microweb 0.1.3__py3-none-any.whl → 0.1.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
microweb/cli.py CHANGED
@@ -46,7 +46,7 @@ def check_micropython(port):
46
46
  print_colored(f"mpremote output:\n{result.stdout.strip()}\n{result.stderr.strip()}", color='yellow')
47
47
  return False
48
48
  except Exception as e:
49
- print_colored(f"Error checking MicroPython via mpremote: {e}", color='red')
49
+ print_colored(f"Error checking MicroPython via mpremote: {e}. The app may be running boot.py.", color='blue')
50
50
  return False
51
51
 
52
52
  def get_remote_file_info(port):
@@ -475,6 +475,7 @@ def run(file, port, check_only, static, force, no_stop, timeout, add_boot, remov
475
475
  if result.returncode != 0:
476
476
  print_colored(f"❌ Error running {file}: return code {result.returncode}", color='red')
477
477
  print_colored(f"stdout:\n{result.stdout.strip()}\nstderr:\n{result.stderr.strip()}", color='red')
478
+ print_colored(f"try microweb flash --port COM10 ",color="cyan")
478
479
  return
479
480
  if upload_count > 0:
480
481
  print_colored(f"📊 Uploaded {upload_count} file(s), skipped {len(files_skipped)} file(s)", color='green')
@@ -489,6 +490,8 @@ def run(file, port, check_only, static, force, no_stop, timeout, add_boot, remov
489
490
  except Exception as e:
490
491
  print_colored(f"❌ Error: {e}", color='red')
491
492
 
493
+
494
+
492
495
  @cli.command()
493
496
  @click.option('--port', default=None, help='Serial port, e.g., COM10')
494
497
  @click.option('--remove', 'remove_everything', is_flag=True, help='Actually remove all files in the ESP32 home directory')
@@ -506,51 +509,239 @@ def remove(port, remove_everything):
506
509
  try:
507
510
  if remove_everything:
508
511
  print_colored("Removing all files in ESP32 home directory...", color='yellow')
509
- cmd_ls = ['mpremote', 'connect', port, 'ls']
510
- result = subprocess.run(cmd_ls, capture_output=True, text=True, timeout=10)
511
- if result.returncode == 0:
512
- files = []
513
- for line in result.stdout.strip().split('\n'):
514
- parts = line.strip().split()
515
- if len(parts) >= 2:
516
- filename = ' '.join(parts[1:])
517
- files.append(filename)
518
- for filename in files:
519
- if filename in ('.', '..'):
520
- continue
521
- print_colored(f"Removing {filename}...", color='cyan')
522
- cmd_rm = [
523
- 'mpremote', 'connect', port, 'exec',
524
- f"import os; import shutil; "
525
- f"shutil.rmtree('{filename}') if hasattr(__import__('shutil'), 'rmtree') and os.path.isdir('{filename}') "
526
- f"else (os.remove('{filename}') if '{filename}' in os.listdir() else None)"
527
- ]
528
- subprocess.run(cmd_rm, capture_output=True, text=True, timeout=10)
529
- print_colored("All files in ESP32 home directory removed.", color='green')
530
- else:
531
- print_colored(f"Error listing files: {result.stderr}", color='red')
512
+ remote_files = get_remote_file_info(port)
513
+ if not remote_files:
514
+ print_colored("No files found or error accessing filesystem.", color='yellow')
515
+ return
516
+ for filename in remote_files.keys():
517
+ if filename in ('.', '..'):
518
+ continue
519
+ print_colored(f"Removing {filename}...", color='cyan')
520
+ cmd_rm = ['mpremote', 'connect', port, 'rm', filename]
521
+ result = subprocess.run(cmd_rm, capture_output=True, text=True, timeout=10)
522
+ if result.returncode != 0:
523
+ print_colored(f"Error removing {filename}: {result.stderr}", color='red')
524
+ print_colored("All files in ESP32 home directory removed.", color='green')
532
525
  else:
533
526
  print_colored("Dry run: No files were removed. Use --remove to actually delete all files in the ESP32 home directory.", color='yellow')
534
527
  except Exception as e:
535
528
  print_colored(f"Error removing files: {e}", color='red')
536
529
 
530
+
531
+
532
+ @cli.command()
533
+ @click.option('--path', default='example_app', show_default=True, help='Directory to create the example app')
534
+ def create(path):
535
+ """Create an example MicroWeb app with app.py, static/index.html, and README.md."""
536
+ try:
537
+ os.makedirs(path, exist_ok=True)
538
+ os.makedirs(os.path.join(path, 'static'), exist_ok=True)
539
+
540
+ app_content = """import wifi
541
+ from microweb import MicroWeb
542
+
543
+ # Define a simple User model to store and retrieve user data
544
+ class User:
545
+ def __init__(self, user_id, name, email):
546
+ self.user_id = user_id
547
+ self.name = name
548
+ self.email = email
549
+
550
+ def to_dict(self):
551
+ return {
552
+ 'user_id': self.user_id,
553
+ 'name': self.name,
554
+ 'email': self.email
555
+ }
556
+
557
+ # Initialize MicroWeb application with debug mode and Wi-Fi access point configuration
558
+ app = MicroWeb(debug=True, ap={"ssid": "MyESP32", "password": "mypassword"})
559
+
560
+ # Define route for the root URL, rendering the index.html template with a welcome message
561
+ @app.route('/')
562
+ def home(req):
563
+ return app.render_template('index.html', message="Welcome to MicroWeb API!")
564
+
565
+ # Define API route to return server status and IP address as JSON
566
+ @app.route('/api/status', methods=['GET'])
567
+ def status(req):
568
+ return app.json_response({"status": "running", "ip": wifi.get_ip()})
569
+
570
+ # Define API route to get user data using the User model
571
+ @app.route('/api/user/<id>')
572
+ def get_user(req, match):
573
+ # Extract ID from URL parameter, default to "Anonymous" if not provided
574
+ id = match.group(1) if match else "Anonymous"
575
+ # Create a sample user instance (in a real app, this could come from a database)
576
+ user = User(user_id=id, name=f"User {id}", email=f"user{id}@example.com")
577
+ return app.json_response(user.to_dict())
578
+
579
+ # Define API route to greet a user by ID
580
+ @app.route('/greet/<name>')
581
+ def greet(req, match):
582
+ name = match.group(1) if match else "Anonymous"
583
+ return {"message": f"Hello, {name}!", "status": "success"}
584
+
585
+ # Define API route to echo back POST data as JSON
586
+ # @app.route('/api/echo', methods=['POST'])
587
+ # def echo(req):
588
+ # data = req.form # Get form data from POST body
589
+ # return app.json_response({"received": data})
590
+
591
+ # Define a route that handles both GET and POST requests, returning method-specific JSON responses
592
+ # @app.route('/api/methods', methods=['GET', 'POST'])
593
+ # def methods(req):
594
+ # if req.method == 'GET':
595
+ # return app.json_response({"method": "GET", "message": "This is a GET request"})
596
+ # elif req.method == 'POST':
597
+ # data = req.json() # Get JSON data from POST body
598
+ # return app.json_response({"method": "POST", "received": data})
599
+
600
+ # Define route for form submission, rendering form.html for GET and result.html for POST
601
+ # @app.route('/submit', methods=['GET', 'POST'])
602
+ # def submit_form(req):
603
+ # if req.method == 'POST':
604
+ # return app.render_template('result.html',
605
+ # data=str(req.form), # Convert form data to string
606
+ # method="POST")
607
+ # else:
608
+ # return app.render_template('form.html')
609
+
610
+ # Register static file style.css to be served at /style.css
611
+ # app.add_static('/style.css', 'style.css')
612
+
613
+ # Start the MicroWeb server
614
+ app.run()
615
+ """
616
+
617
+
618
+ index_content = """<!DOCTYPE html>
619
+ <html>
620
+ <head>
621
+ <title>MicroWeb Example</title>
622
+ </head>
623
+ <body>
624
+ <h1>{% message %}</h1>
625
+ <p>This is an example MicroWeb application.</p>
626
+ </body>
627
+ </html>
628
+ """
629
+
630
+ readme_content = """# MicroWeb Example Application
631
+
632
+ This is a simple MicroWeb application for MicroPython devices (e.g., ESP32). It includes a basic web server with a homepage and a status API endpoint.
633
+
634
+ ## Files
635
+ - `app.py`: The main MicroWeb application script.
636
+ - `static/index.html`: The homepage template.
637
+
638
+ ## Setup and Usage
639
+
640
+ ### Prerequisites
641
+ - A MicroPython-compatible device (e.g., ESP32).
642
+ - The `microweb` package installed (`pip install microweb`).
643
+ - A serial port connection (e.g., COM10).
644
+
645
+ ### Flash MicroPython and MicroWeb
646
+ 1. Connect your device to your computer.
647
+ 2. Flash MicroPython and MicroWeb to your device:
648
+ ```bash
649
+ microweb flash --port COM10
650
+ ```
651
+ Replace `COM10` with your device's serial port.
652
+
653
+ ### Run the Application
654
+ 1. Upload and run the application:
655
+ ```bash
656
+ microweb run app.py --port COM10
657
+ ```
658
+ 2. Connect to the Wi-Fi access point:
659
+ - **SSID**: MyESP32
660
+ - **Password**: MyPassword
661
+ 3. Open a browser and visit `http://192.168.4.1` to see the homepage.
662
+
663
+ ### Additional Commands
664
+ - Set the app to run on boot:
665
+ ```bash
666
+ microweb run app.py --port COM10 --add-boot
667
+ ```
668
+ - Remove all files from the device:
669
+ ```bash
670
+ microweb remove --port COM10 --remove
671
+ ```
672
+
673
+ For more details, run:
674
+ ```bash
675
+ microweb examples
676
+ ```
677
+ """
678
+
679
+ app_path = os.path.join(path, 'app.py')
680
+ index_path = os.path.join(path, 'static', 'index.html')
681
+ readme_path = os.path.join(path, 'README.md')
682
+
683
+ with open(app_path, 'w', encoding='utf-8') as f:
684
+ f.write(app_content)
685
+ with open(index_path, 'w', encoding='utf-8') as f:
686
+ f.write(index_content)
687
+ with open(readme_path, 'w', encoding='utf-8') as f:
688
+ f.write(readme_content)
689
+
690
+ print_colored(f"Example app created at {path}/", color='green')
691
+ print_colored(f" - {app_path}", color='cyan')
692
+ print_colored(f" - {index_path}", color='cyan')
693
+ print_colored(f" - {readme_path}", color='cyan')
694
+ print_colored(f"Run with: microweb run {app_path} --port COM10", color='yellow')
695
+ except Exception as e:
696
+ print_colored(f"Error creating example app: {e}", color='red')
697
+
698
+
699
+ @cli.command()
700
+ @click.option('--port', default=None, help='Serial port, e.g., COM10')
701
+ def ls(port):
702
+ """List files on the MicroPython device's filesystem."""
703
+ if not port:
704
+ ports = [p.device for p in serial.tools.list_ports.comports()]
705
+ port = ports[0] if ports else None
706
+ if not port:
707
+ print_colored("No device found. Specify --port, e.g., --port COM10.", color='red')
708
+ return
709
+ try:
710
+ print_colored(f"Listing files on device at {port}...", color='blue')
711
+ remote_files = get_remote_file_info(port)
712
+ if not remote_files:
713
+ print_colored("No files found or error accessing filesystem.", color='yellow')
714
+ return
715
+ print_colored(f"Found {len(remote_files)} files on device:", color='green')
716
+ for filename, size in sorted(remote_files.items()):
717
+ print_colored(f" 📄 {filename}: {size} bytes", color='cyan')
718
+ except Exception as e:
719
+ print_colored(f"Error listing files: {e}", color='red')
720
+
721
+
722
+
537
723
  @cli.command()
538
724
  def examples():
539
- """Show example commands for using microweb CLI."""
540
- print_colored("Example commands for microweb CLI:", color='blue', style='bold')
541
- print_colored("\n1. Flash MicroPython and MicroWeb to ESP32:", color='cyan')
542
- print_colored(" microweb flash --port COM10", color='green')
543
- print_colored("\n2. Upload and run your app.py on ESP32:", color='cyan')
544
- print_colored(" microweb run app.py --port COM10", color='green')
545
- print_colored("\n3. Check static/template files without uploading:", color='cyan')
546
- print_colored(" microweb run app.py --check-only", color='green')
547
- print_colored("\n4. Remove all files from ESP32 (DANGEROUS):", color='cyan')
548
- print_colored(" microweb remove --port COM10 --remove", color='green')
549
- print_colored("\n5. Upload and set app to run on boot:", color='cyan')
550
- print_colored(" microweb run app.py --port COM10 --add-boot", color='green')
551
- print_colored("\n6. Remove boot.py from ESP32:", color='cyan')
552
- print_colored(" microweb run app.py --port COM10 --remove-boot", color='green')
553
- print_colored("\nReplace COM10 with your actual ESP32 serial port.", color='yellow')
725
+ """Show example commands for using microweb CLI."""
726
+ print_colored("Example commands for microweb CLI:", color='blue', style='bold')
727
+ print_colored("\n1. Create an example MicroWeb app:", color='cyan')
728
+ print_colored(" microweb create --path example_app", color='green')
729
+ print_colored("\n2. Flash MicroPython and MicroWeb to ESP32:", color='cyan')
730
+ print_colored(" microweb flash --port COM10", color='green')
731
+ print_colored("\n3. Upload and run your app.py on ESP32:", color='cyan')
732
+ print_colored(" microweb run app.py --port COM10", color='green')
733
+ print_colored("\n4. Check static/template files without uploading:", color='cyan')
734
+ print_colored(" microweb run app.py --check-only", color='green')
735
+ print_colored("\n5. List files on the ESP32 filesystem:", color='cyan')
736
+ print_colored(" microweb ls --port COM10", color='green')
737
+ print_colored("\n6. Remove all files from ESP32 (DANGEROUS):", color='cyan')
738
+ print_colored(" microweb remove --port COM10 --remove", color='green')
739
+ print_colored("\n7. Upload and set app to run on boot:", color='cyan')
740
+ print_colored(" microweb run app.py --port COM10 --add-boot", color='green')
741
+ print_colored("\n8. Remove boot.py from ESP32:", color='cyan')
742
+ print_colored(" microweb run app.py --port COM10 --remove-boot", color='green')
743
+ print_colored("\nReplace COM10 with your actual ESP32 serial port.", color='yellow')
744
+
554
745
 
555
746
 
556
747
  if __name__ == '__main__':
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: microweb
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: A web server framework for MicroPython . Easily build and deploy web applications using MicroPython.
5
5
  Home-page: https://github.com/ishanoshada/microweb
6
6
  Author: Ishan Oshada
@@ -15,6 +15,8 @@ License-File: LICENSE
15
15
  Requires-Dist: pyserial
16
16
  Requires-Dist: esptool
17
17
  Requires-Dist: click
18
+ Requires-Dist: adafruit-ampy
19
+ Requires-Dist: mpremote
18
20
  Dynamic: author
19
21
  Dynamic: classifier
20
22
  Dynamic: description
@@ -84,6 +86,7 @@ With MicroWeb, you get routing, templates, JSON, static files, and more—making
84
86
  - [Features](#features)
85
87
  - [Installation](#installation)
86
88
  - [Usage](#usage)
89
+ - [Creating an Example Application](#creating-an-example-application)
87
90
  - [Flashing the ESP32](#flashing-the-esp32)
88
91
  - [Running a Custom Application](#running-a-custom-application)
89
92
  - [Example Usage](#example-usage)
@@ -93,6 +96,7 @@ With MicroWeb, you get routing, templates, JSON, static files, and more—making
93
96
  - [Wi-Fi Configuration](#wi-fi-configuration)
94
97
  - [Accessing the Web Server](#accessing-the-web-server)
95
98
  - [CLI Tool Usage Examples](#cli-tool-usage-examples)
99
+ - [How to Code with MicroWeb](#how-to-code-with-microweb)
96
100
  - [ Feature Updates ](#feature-updates)
97
101
  - [Project Structure](#project-structure)
98
102
  - [Troubleshooting](#troubleshooting)
@@ -137,32 +141,32 @@ pip install .
137
141
 
138
142
  ---
139
143
 
144
+
140
145
  ## Usage
141
146
 
142
- Flash MicroPython and MicroWeb:
147
+ ### Creating an Example Application
148
+ Generate a sample MicroWeb application with a basic web server, template, and documentation:
143
149
 
144
150
  ```bash
145
- microweb flash --port COM10
151
+ microweb create --path example_app
146
152
  ```
147
153
 
148
- #### Options:
149
-
150
- * `--erase`
151
- Erase the entire flash memory before flashing firmware.
152
-
153
- * `--esp8266`
154
- Flash ESP8266 firmware instead of the default ESP32.
155
-
156
- * `--firmware firmware.bin`
157
- Use a custom `.bin` firmware file. This overrides the default firmware for ESP32 or ESP8266 ( any ).
158
-
154
+ - Creates a directory (default: `example_app`) containing `app.py`, `static/index.html`, and a `README.md` with usage instructions.
155
+ - Option: `--path <directory>` to specify a custom directory name.
159
156
 
157
+ ### Flashing MicroPython and MicroWeb
158
+ Flash MicroPython firmware and MicroWeb to your device:
160
159
 
160
+ ```bash
161
+ microweb flash --port COM10
162
+ ```
161
163
 
162
- ---
164
+ #### Options:
165
+ - `--erase`: Erase the entire flash memory before flashing firmware.
166
+ - `--esp8266`: Flash ESP8266 firmware instead of the default ESP32.
167
+ - `--firmware firmware.bin`: Use a custom `.bin` firmware file, overriding the default firmware for ESP32 or ESP8266.
163
168
 
164
169
  ### Running a Custom Application
165
-
166
170
  Upload and run a MicroPython script:
167
171
 
168
172
  ```bash
@@ -173,7 +177,17 @@ microweb run app.py --port COM10
173
177
  - Uploads and executes the script.
174
178
  - Checks and uploads only changed files by default.
175
179
  - Prompts to run `microweb flash` if MicroPython is not detected.
176
- - After running `app.run()`, the ESP32 will host a Wi-Fi access point (AP) if it cannot connect to a configured network. You can then connect your device to this AP and access the web server using the ESP32's IP address (typically `http://192.168.4.1` in AP mode).
180
+ - After running `app.run()`, the ESP32 will host a Wi-Fi access point (AP) if it cannot connect to a configured network. Connect to this AP and access the web server at `http://192.168.4.1` (typical IP in AP mode).
181
+
182
+ ### Listing Files on the Device
183
+ List files on the MicroPython device's filesystem:
184
+
185
+ ```bash
186
+ microweb ls --port COM10
187
+ ```
188
+
189
+ - Displays all files and their sizes in the device's home directory.
190
+ - Requires MicroPython to be installed on the device.
177
191
 
178
192
  ---
179
193
 
@@ -319,23 +333,26 @@ If no credentials are provided, loads `config.json`. If connection fails, starts
319
333
  - Use the control panel to update Wi-Fi, test routes, or submit forms.
320
334
 
321
335
  ---
336
+
322
337
  ## CLI Tool Usage Examples
323
338
 
324
339
  The following table summarizes common `microweb` CLI commands. See also: #changes.
325
340
 
326
341
  | Command Example | Description |
327
342
  |----------------------------------------------|-----------------------------------------------------------|
328
- | `microweb exmples ` | Show example commands for using microweb CLI.** |
343
+ | `microweb create --path example_app` | Create an example MicroWeb app with `app.py`, `static/index.html`, and `README.md`. |
344
+ | `microweb examples` | Show example commands for using the MicroWeb CLI. |
329
345
  | `microweb flash --port COM10` | Flash MicroPython firmware and upload MicroWeb files. |
330
346
  | `microweb flash --port COM10 --erase` | Erase ESP32 flash before installing MicroPython. |
331
347
  | `microweb run app.py --port COM10` | Upload and run a custom MicroPython script. |
332
348
  | `microweb run app.py --check-only` | Check static/template dependencies without uploading. |
333
349
  | `microweb run app.py --force` | Force upload all files, even if unchanged. |
334
- | `microweb run app.py --add-boot` | Uploads a `boot.py` to auto-run your app on boot. |
335
- | `microweb run app.py --remove-boot` | Removes `boot.py` from the ESP32. |
350
+ | `microweb run app.py --add-boot` | Upload a `boot.py` to auto-run your app on boot. |
351
+ | `microweb run app.py --remove-boot` | Remove `boot.py` from the ESP32. |
336
352
  | `microweb run app.py --static static/` | Specify a custom static files folder. |
337
353
  | `microweb run app.py --no-stop` | Do not reset ESP32 before running the app. |
338
354
  | `microweb run app.py --timeout 600` | Set a custom timeout (in seconds) for app execution. |
355
+ | `microweb ls --port COM10` | List files and their sizes on the ESP32 filesystem. |
339
356
  | `microweb remove --port COM10` | List files on ESP32 (requires `--remove` to actually delete). |
340
357
  | `microweb remove --port COM10 --remove` | Remove all files in ESP32 home directory. |
341
358
 
@@ -347,6 +364,284 @@ The following table summarizes common `microweb` CLI commands. See also: #change
347
364
  For more details, run `microweb --help`.
348
365
 
349
366
 
367
+
368
+ ---
369
+
370
+ ## How to Code with MicroWeb
371
+
372
+ This section guides you through writing MicroWeb applications for MicroPython on ESP32. MicroWeb simplifies web development with features like dynamic routing, template rendering, static file serving, JSON responses, and Wi-Fi configuration. Below, we explain the key components of coding with MicroWeb, with examples to help you get started.
373
+
374
+ ### **1. Setting Up the MicroWeb Application**
375
+ To start, import the `MicroWeb` class and initialize the app. You can configure debugging and Wi-Fi settings (access point or station mode) in the constructor.
376
+
377
+ ```python
378
+ from microweb import MicroWeb
379
+
380
+ # Initialize MicroWeb with debug mode and access point (AP) settings
381
+ app = MicroWeb(debug=True, ap={'ssid': 'MyESP32', 'password': 'mypassword'})
382
+ ```
383
+
384
+ **Explanation**:
385
+ - `debug=True`: Enables detailed logging for troubleshooting, useful during development.
386
+ - `ap={'ssid': ..., 'password': ...}`: Configures the ESP32 to create a Wi-Fi access point if it cannot connect to a network. Alternatively, use `internet={'ssid': ..., 'password': ...}` for station mode to connect to an existing Wi-Fi network.
387
+ - If no Wi-Fi credentials are provided, MicroWeb loads settings from `config.json` or starts a default AP (SSID: `ESP32-MicroWeb`, password: `12345678`).
388
+
389
+ ### **2. Defining Routes**
390
+ Routes map URLs to handler functions. Use the `@app.route()` decorator to define endpoints and specify HTTP methods (e.g., GET, POST).
391
+
392
+ ```python
393
+ @app.route('/')
394
+ def home(req):
395
+ return app.render_template('index.html', message='Welcome to MicroWeb!')
396
+ ```
397
+
398
+ **Explanation**:
399
+ - The `@app.route('/')` decorator maps the root URL (`/`) to the `home` function.
400
+ - The `req` parameter provides access to request data (e.g., `req.method`, `req.form`, `req.json()`).
401
+ - `app.render_template` renders an HTML template (`index.html`) with dynamic variables (e.g., `message`).
402
+
403
+ For dynamic routing with URL parameters:
404
+ ```python
405
+ @app.route('/greet/<name>')
406
+ def greet(req, match):
407
+ name = match.group(1) if match else 'Anonymous'
408
+ return {'message': f'Hello, {name}!', 'status': 'success'}
409
+ ```
410
+
411
+ **Explanation**:
412
+ - `/greet/<name>` captures a URL parameter (e.g., `/greet/Alice` sets `name` to `Alice`).
413
+ - The `match` parameter contains the parsed URL parameters, accessed via `match.group(1)`.
414
+
415
+ ### **3. Handling HTTP Methods**
416
+ MicroWeb supports multiple HTTP methods (GET, POST, etc.) for a single route using the `methods` parameter.
417
+
418
+ ```python
419
+ @app.route('/api/methods', methods=['GET', 'POST'])
420
+ def methods(req):
421
+ if req.method == 'GET':
422
+ return app.json_response({'method': 'GET', 'message': 'This is a GET request'})
423
+ elif req.method == 'POST':
424
+ data = req.json()
425
+ return app.json_response({'method': 'POST', 'received': data})
426
+ ```
427
+
428
+ **Explanation**:
429
+ - The `methods=['GET', 'POST']` parameter allows the route to handle both GET and POST requests.
430
+ - `req.method` checks the HTTP method to determine the response.
431
+ - `req.json()` parses JSON data from the POST request body.
432
+ - `app.json_response` returns a JSON response with the specified data.
433
+
434
+ ### **4. Rendering Templates**
435
+ MicroWeb supports rendering HTML templates with dynamic data, ideal for web interfaces. Templates use a simple syntax (e.g., `{% variable %}`) for dynamic content.
436
+
437
+ ```python
438
+ @app.route('/submit', methods=['GET', 'POST'])
439
+ def submit_form(req):
440
+ if req.method == 'POST':
441
+ return app.render_template('result.html', data=str(req.form), method='POST')
442
+ else:
443
+ return app.render_template('form.html')
444
+ ```
445
+
446
+ **Explanation**:
447
+ - Templates (e.g., `form.html`, `result.html`) must be in the `static/` directory or a specified folder.
448
+ - `req.form` accesses form data from POST requests.
449
+ - `app.render_template` passes variables (e.g., `data`, `method`) to the template for dynamic rendering.
450
+
451
+ **Example Template (`static/result.html`)**:
452
+ ```html
453
+ <!DOCTYPE html>
454
+ <html>
455
+ <head>
456
+ <title>Form Result</title>
457
+ <link rel="stylesheet" href="/style.css">
458
+ </head>
459
+ <body>
460
+ <p><strong>Method:</strong> {% method %}</p>
461
+ <p><strong>Data:</strong> {% data %}</p>
462
+ </body>
463
+ </html>
464
+ ```
465
+
466
+ **Explanation**:
467
+ - The `{% method %}` and `{% data %}` placeholders are replaced with the `method` and `data` variables passed to `app.render_template`.
468
+ - The `<link rel="stylesheet" href="/style.css">` tag references a static CSS file, served via `app.add_static('/style.css', 'style.css')`.
469
+ - Ensure `result.html` and `style.css` are in the `static/` directory and uploaded to the ESP32.
470
+
471
+ **Example Form Template (`static/form.html`)**:
472
+ ```html
473
+ <!DOCTYPE html>
474
+ <html>
475
+ <head>
476
+ <title>Form</title>
477
+ <link rel="stylesheet" href="/style.css">
478
+ </head>
479
+ <body>
480
+ <form method="POST" action="/submit">
481
+ <input type="text" name="username" placeholder="Enter your name">
482
+ <button type="submit">Submit</button>
483
+ </form>
484
+ </body>
485
+ </html>
486
+ ```
487
+
488
+ **Explanation**:
489
+ - The form submits data to `/submit` via POST, which is processed by the `submit_form` route.
490
+ - The `style.css` file is served as a static file, ensuring consistent styling across templates.
491
+
492
+ ### **5. Serving Static Files**
493
+ MicroWeb allows serving static files like CSS, JavaScript, or images to enhance web interfaces.
494
+
495
+ ```python
496
+ app.add_static('/style.css', 'style.css')
497
+ ```
498
+
499
+ **Explanation**:
500
+ - `app.add_static` maps a URL path (`/style.css`) to a file in the `static/` directory (`style.css`).
501
+ - Static files must be uploaded to the ESP32’s filesystem using:
502
+ ```bash
503
+ microweb run app.py --static static/ --port COM10
504
+ ```
505
+ - In the `result.html` example, `<link rel="stylesheet" href="/style.css">` loads the CSS file for styling.
506
+
507
+ ### **6. JSON Responses**
508
+ MicroWeb simplifies JSON responses for API endpoints.
509
+
510
+ ```python
511
+ @app.route('/api/status', methods=['GET'])
512
+ def status(req):
513
+ return app.json_response({'status': 'running', 'ip': wifi.get_ip()})
514
+ ```
515
+
516
+ **Explanation**:
517
+ - `app.json_response` formats the dictionary as JSON and sets the `Content-Type` to `application/json`.
518
+ - The `wifi` module (if available) retrieves the ESP32’s IP address.
519
+
520
+ ### **7. Custom HTTP Headers**
521
+ You can set custom headers using the `Response` class.
522
+
523
+ ```python
524
+ from microweb import Response
525
+
526
+ @app.route('/headers')
527
+ def headers_example(req):
528
+ resp = Response('Custom header set!', content_type='text/plain')
529
+ resp.headers['X-Custom-Header'] = 'Value'
530
+ return resp
531
+ ```
532
+
533
+ **Explanation**:
534
+ - The `Response` class allows custom content types and headers.
535
+ - `resp.headers` sets additional HTTP headers for the response.
536
+
537
+ ### **8. Wi-Fi Integration**
538
+ MicroWeb handles Wi-Fi configuration, allowing the ESP32 to act as an access point or connect to a network.
539
+
540
+ ```python
541
+ import wifi
542
+ from microweb import MicroWeb
543
+
544
+ app = MicroWeb(debug=True, ap={'ssid': 'MyESP32', 'password': 'mypassword'})
545
+
546
+ @app.route('/api/status', methods=['GET'])
547
+ def status(req):
548
+ return app.json_response({'status': 'running', 'ip': wifi.get_ip()})
549
+ ```
550
+
551
+ **Explanation**:
552
+ - The `wifi` module (part of MicroWeb or MicroPython) provides functions like `wifi.get_ip()` to retrieve the device’s IP address.
553
+ - If Wi-Fi connection fails, the ESP32 starts an access point with the specified `ssid` and `password`.
554
+
555
+ ### **9. Running the Application**
556
+ Start the web server with `app.run()`.
557
+
558
+ ```python
559
+ if __name__ == '__main__':
560
+ app.run()
561
+ ```
562
+
563
+ **Explanation**:
564
+ - `app.run()` starts the MicroWeb server, listening for HTTP requests.
565
+ - Use the CLI to upload and run the script:
566
+ ```bash
567
+ microweb run app.py --port COM10
568
+ ```
569
+
570
+ ### **10. Best Practices**
571
+ - **Error Handling**: Add try-except blocks for `wifi.get_ip()` or `req.form` to handle network or input errors.
572
+ ```python
573
+ try:
574
+ ip = wifi.get_ip()
575
+ except Exception as e:
576
+ ip = 'N/A'
577
+ return app.json_response({'status': 'running', 'ip': ip})
578
+ ```
579
+ - **File Management**: Ensure templates (`index.html`, `form.html`, `result.html`) and static files (`style.css`) exist in the `static/` directory before running.
580
+ - **Security**: Avoid hardcoding Wi-Fi credentials; use `config.json` or the web interface for configuration.
581
+ - **Debugging**: Enable `debug=True` during development to log errors and requests.
582
+ - **Testing**: Test routes with tools like `curl` or a browser (e.g., `http://192.168.4.1/` in AP mode).
583
+
584
+ ### **11. Example: Putting It All Together**
585
+ Below is a complete MicroWeb application combining routes, templates, static files, and JSON responses, including the `result.html` template.
586
+
587
+ ```python
588
+ import wifi
589
+ from microweb import MicroWeb
590
+
591
+ app = MicroWeb(debug=True, ap={'ssid': 'MyESP32', 'password': 'mypassword'})
592
+
593
+ @app.route('/')
594
+ def home(req):
595
+ return app.render_template('index.html', message='Welcome to MicroWeb!')
596
+
597
+ @app.route('/api/status', methods=['GET'])
598
+ def status(req):
599
+ return app.json_response({'status': 'running', 'ip': wifi.get_ip()})
600
+
601
+ @app.route('/api/echo', methods=['POST'])
602
+ def echo(req):
603
+ data = req.form
604
+ return app.json_response({'received': data})
605
+
606
+ @app.route('/submit', methods=['GET', 'POST'])
607
+ def submit_form(req):
608
+ if req.method == 'POST':
609
+ return app.render_template('result.html', data=str(req.form), method='POST')
610
+ else:
611
+ return app.render_template('form.html')
612
+
613
+ app.add_static('/style.css', 'style.css')
614
+
615
+ if __name__ == '__main__':
616
+ app.run()
617
+ ```
618
+
619
+ **Explanation**:
620
+ - Combines template rendering (`/`, `/submit`), JSON responses (`/api/status`, `/api/echo`), and static file serving (`/style.css`).
621
+ - The `/submit` route uses `result.html` (as shown above) to display form data and the HTTP method.
622
+ - Upload and run with:
623
+ ```bash
624
+ microweb run app.py --port COM10 --static static/
625
+ ```
626
+ - Access at `http://192.168.4.1` (or the ESP32’s IP address).
627
+
628
+ ### **12. Testing Your Application**
629
+ - **Browser**: Open `http://<ESP32-IP>/` (e.g., `http://192.168.4.1`) to access the web server.
630
+ - **curl**:
631
+ ```bash
632
+ curl http://192.168.4.1/api/status
633
+ curl -X POST -d 'username=Alice' http://192.168.4.1/api/echo
634
+ curl -X POST -d 'username=Alice' http://192.168.4.1/submit
635
+ ```
636
+ - **CLI Logs**: Monitor logs with `debug=True` to debug issues.
637
+ - **Template Testing**: Submit a form via `http://192.168.4.1/submit` to see the `result.html` output, styled with `style.css`.
638
+
639
+ ### **13. Next Steps**
640
+ - Explore the portfolio demo (`tests/portfolio/`) for a full-featured web app example.
641
+ - Use `microweb create --path my_app` to generate a template project.
642
+ - Add boot script support with `microweb run app.py --add-boot --port COM10` for auto-running on power-up.
643
+ - Check the [CLI Tool Usage Examples](#cli-tool-usage-examples) for advanced commands.
644
+
350
645
  ---
351
646
 
352
647
  ### Feature Updates
@@ -388,6 +683,8 @@ microweb/
388
683
  ```
389
684
 
390
685
 
686
+
687
+
391
688
  ---
392
689
 
393
690
  ## Troubleshooting
@@ -1,5 +1,5 @@
1
1
  microweb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- microweb/cli.py,sha256=MoFSGG1AXvza4pwFxhoT9R65-9Jo7VtMslXN993kfv4,27919
2
+ microweb/cli.py,sha256=Btvegc_SJ76rLkSvItuiWLySRDrNy2b0gdUrg2a-zKo,34338
3
3
  microweb/microweb.py,sha256=WlDV1Fk5M4EsvmmkWKVLWaoZh6Vgl9G0qORFsQEQ3iM,12799
4
4
  microweb/uploader.py,sha256=4aTFBMwIIKzONuvXla70ch7yT1pJ8XDmInYBPfVqcok,3164
5
5
  microweb/wifi.py,sha256=S2gOOmTKp2G1uUHxOH0DHQ_k7QSERKHDCm89qv-WlKs,904
@@ -7,9 +7,9 @@ microweb/firmware/ESP32_GENERIC-20250415-v1.25.0.bin,sha256=HrGohHPjKqak1F3Btbat
7
7
  microweb/firmware/ESP8266_GENERIC-20250415-v1.25.0.bin,sha256=DcEkzHgtDl-BCwap6UbgKhirHjbWepSgd8PonauaVcY,636820
8
8
  microweb/firmware/boot.py,sha256=1522jvxAjGk-y7X4mzMilB4pMQ6KrAgosDXRAMtac8k,22
9
9
  microweb/firmware/main.py,sha256=g_rg-1qpQEzvQoSnhXairx_v7xHNuGDJVrmbdziKt1A,10
10
- microweb-0.1.3.dist-info/licenses/LICENSE,sha256=7tSPQeJFv8168MWaFKTWEqYRvM9Lh5xsBh-US0mxUF0,1070
11
- microweb-0.1.3.dist-info/METADATA,sha256=EsCNvIOp8eJ2c6gM_9lw1dTKaLlP5teu09Eo54bgnQ8,13725
12
- microweb-0.1.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
13
- microweb-0.1.3.dist-info/entry_points.txt,sha256=yBGjIy-e3mx-HHkmkGNLBcSDEALB327du5KuauZHmg4,46
14
- microweb-0.1.3.dist-info/top_level.txt,sha256=CkRcDTo-nv4JTieAZPKFC-EGKYYQmKNq5C8zU2k5zRM,9
15
- microweb-0.1.3.dist-info/RECORD,,
10
+ microweb-0.1.4.dist-info/licenses/LICENSE,sha256=7tSPQeJFv8168MWaFKTWEqYRvM9Lh5xsBh-US0mxUF0,1070
11
+ microweb-0.1.4.dist-info/METADATA,sha256=wHRZhYMs8_sAMmNzz3ewkEpcm8gSqcvibAmSvVGPJDc,25314
12
+ microweb-0.1.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
13
+ microweb-0.1.4.dist-info/entry_points.txt,sha256=yBGjIy-e3mx-HHkmkGNLBcSDEALB327du5KuauZHmg4,46
14
+ microweb-0.1.4.dist-info/top_level.txt,sha256=CkRcDTo-nv4JTieAZPKFC-EGKYYQmKNq5C8zU2k5zRM,9
15
+ microweb-0.1.4.dist-info/RECORD,,