Add additional configuration and utility files
- Add install script and message configuration - Add settings controller and templates - Add ESP-NOW message utility - Update API documentation
This commit is contained in:
80
src/util/README.md
Normal file
80
src/util/README.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# ESPNow Message Builder
|
||||
|
||||
This utility module provides functions to build ESPNow messages according to the LED Driver API specification.
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Message Building
|
||||
|
||||
```python
|
||||
from util.espnow_message import build_message, build_preset_dict, build_select_dict
|
||||
|
||||
# Build a message with presets and select
|
||||
presets = {
|
||||
"red_blink": build_preset_dict({
|
||||
"pattern": "blink",
|
||||
"colors": ["#FF0000"],
|
||||
"delay": 200,
|
||||
"brightness": 255,
|
||||
"auto": True
|
||||
})
|
||||
}
|
||||
|
||||
select = build_select_dict({
|
||||
"device1": "red_blink"
|
||||
})
|
||||
|
||||
message = build_message(presets=presets, select=select)
|
||||
# Result: {"v": "1", "presets": {...}, "select": {...}}
|
||||
```
|
||||
|
||||
### Building Select Messages with Step Synchronization
|
||||
|
||||
```python
|
||||
from util.espnow_message import build_message, build_select_dict
|
||||
|
||||
# Select with step for synchronization
|
||||
select = build_select_dict(
|
||||
{"device1": "rainbow_preset", "device2": "rainbow_preset"},
|
||||
step_mapping={"device1": 10, "device2": 10}
|
||||
)
|
||||
|
||||
message = build_message(select=select)
|
||||
# Result: {"v": "1", "select": {"device1": ["rainbow_preset", 10], "device2": ["rainbow_preset", 10]}}
|
||||
```
|
||||
|
||||
### Converting Presets
|
||||
|
||||
```python
|
||||
from util.espnow_message import build_preset_dict, build_presets_dict
|
||||
|
||||
# Single preset
|
||||
preset = build_preset_dict({
|
||||
"name": "my_preset",
|
||||
"pattern": "rainbow",
|
||||
"colors": ["#FF0000", "#00FF00"], # Can be hex strings or RGB tuples
|
||||
"delay": 100,
|
||||
"brightness": 127,
|
||||
"auto": False,
|
||||
"n1": 2
|
||||
})
|
||||
|
||||
# Multiple presets
|
||||
presets_data = {
|
||||
"preset1": {"pattern": "on", "colors": ["#FF0000"]},
|
||||
"preset2": {"pattern": "blink", "colors": ["#00FF00"]}
|
||||
}
|
||||
presets = build_presets_dict(presets_data)
|
||||
```
|
||||
|
||||
## API Specification
|
||||
|
||||
See `docs/API.md` for the complete ESPNow API specification.
|
||||
|
||||
## Key Features
|
||||
|
||||
- **Version Field**: All messages include `"v": "1"` for version tracking
|
||||
- **Preset Format**: Presets use hex color strings (`#RRGGBB`), not RGB tuples
|
||||
- **Select Format**: Select values are always lists: `["preset_name"]` or `["preset_name", step]`
|
||||
- **Color Conversion**: Automatically converts RGB tuples to hex strings
|
||||
- **Default Values**: Provides sensible defaults for missing fields
|
||||
185
src/util/espnow_message.py
Normal file
185
src/util/espnow_message.py
Normal file
@@ -0,0 +1,185 @@
|
||||
"""
|
||||
ESPNow message builder utility for LED driver communication.
|
||||
|
||||
This module provides utilities to build ESPNow messages according to the API specification.
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
|
||||
def build_message(presets=None, select=None):
|
||||
"""
|
||||
Build an ESPNow message according to the API specification.
|
||||
|
||||
Args:
|
||||
presets: Dictionary mapping preset names to preset objects, or None
|
||||
select: Dictionary mapping device names to select lists, or None
|
||||
|
||||
Returns:
|
||||
JSON string ready to send via ESPNow
|
||||
|
||||
Example:
|
||||
message = build_message(
|
||||
presets={
|
||||
"red_blink": {
|
||||
"pattern": "blink",
|
||||
"colors": ["#FF0000"],
|
||||
"delay": 200,
|
||||
"brightness": 255,
|
||||
"auto": True
|
||||
}
|
||||
},
|
||||
select={
|
||||
"device1": ["red_blink"]
|
||||
}
|
||||
)
|
||||
"""
|
||||
message = {
|
||||
"v": "1"
|
||||
}
|
||||
|
||||
if presets:
|
||||
message["presets"] = presets
|
||||
|
||||
if select:
|
||||
message["select"] = select
|
||||
|
||||
return json.dumps(message)
|
||||
|
||||
|
||||
def build_select_message(device_name, preset_name, step=None):
|
||||
"""
|
||||
Build a select message for a single device.
|
||||
|
||||
Args:
|
||||
device_name: Name of the device
|
||||
preset_name: Name of the preset to select
|
||||
step: Optional step value for synchronization
|
||||
|
||||
Returns:
|
||||
Dictionary with select field ready to use in build_message
|
||||
|
||||
Example:
|
||||
select = build_select_message("device1", "rainbow_preset", step=10)
|
||||
message = build_message(select=select)
|
||||
"""
|
||||
select_list = [preset_name]
|
||||
if step is not None:
|
||||
select_list.append(step)
|
||||
|
||||
return {device_name: select_list}
|
||||
|
||||
|
||||
def build_preset_dict(preset_data):
|
||||
"""
|
||||
Convert preset data to API-compliant format.
|
||||
|
||||
Args:
|
||||
preset_data: Dictionary with preset fields (may include name, pattern, colors, etc.)
|
||||
|
||||
Returns:
|
||||
Dictionary with preset in API-compliant format (without name field)
|
||||
|
||||
Example:
|
||||
preset = build_preset_dict({
|
||||
"name": "red_blink",
|
||||
"pattern": "blink",
|
||||
"colors": ["#FF0000"],
|
||||
"delay": 200,
|
||||
"brightness": 255,
|
||||
"auto": True,
|
||||
"n1": 0,
|
||||
"n2": 0,
|
||||
"n3": 0,
|
||||
"n4": 0,
|
||||
"n5": 0,
|
||||
"n6": 0
|
||||
})
|
||||
"""
|
||||
# Ensure colors are in hex format
|
||||
colors = preset_data.get("colors", ["#FFFFFF"])
|
||||
if colors:
|
||||
# Convert RGB tuples to hex strings if needed
|
||||
if isinstance(colors[0], list) and len(colors[0]) == 3:
|
||||
# RGB tuple format [r, g, b]
|
||||
colors = [f"#{r:02x}{g:02x}{b:02x}" for r, g, b in colors]
|
||||
elif not isinstance(colors[0], str):
|
||||
# Handle other formats - convert to hex
|
||||
colors = ["#FFFFFF"]
|
||||
# Ensure all colors start with #
|
||||
colors = [c if c.startswith("#") else f"#{c}" for c in colors]
|
||||
else:
|
||||
colors = ["#FFFFFF"]
|
||||
|
||||
preset = {
|
||||
"pattern": preset_data.get("pattern", "off"),
|
||||
"colors": colors,
|
||||
"delay": preset_data.get("delay", 100),
|
||||
"brightness": preset_data.get("brightness", preset_data.get("br", 127)),
|
||||
"auto": preset_data.get("auto", True),
|
||||
"n1": preset_data.get("n1", 0),
|
||||
"n2": preset_data.get("n2", 0),
|
||||
"n3": preset_data.get("n3", 0),
|
||||
"n4": preset_data.get("n4", 0),
|
||||
"n5": preset_data.get("n5", 0),
|
||||
"n6": preset_data.get("n6", 0)
|
||||
}
|
||||
|
||||
return preset
|
||||
|
||||
|
||||
def build_presets_dict(presets_data):
|
||||
"""
|
||||
Convert multiple presets to API-compliant format.
|
||||
|
||||
Args:
|
||||
presets_data: Dictionary mapping preset names to preset data
|
||||
|
||||
Returns:
|
||||
Dictionary mapping preset names to API-compliant preset objects
|
||||
|
||||
Example:
|
||||
presets = build_presets_dict({
|
||||
"red_blink": {
|
||||
"pattern": "blink",
|
||||
"colors": ["#FF0000"],
|
||||
"delay": 200
|
||||
},
|
||||
"blue_pulse": {
|
||||
"pattern": "pulse",
|
||||
"colors": ["#0000FF"],
|
||||
"delay": 100
|
||||
}
|
||||
})
|
||||
"""
|
||||
result = {}
|
||||
for preset_name, preset_data in presets_data.items():
|
||||
result[preset_name] = build_preset_dict(preset_data)
|
||||
return result
|
||||
|
||||
|
||||
def build_select_dict(device_preset_mapping, step_mapping=None):
|
||||
"""
|
||||
Build a select dictionary mapping device names to select lists.
|
||||
|
||||
Args:
|
||||
device_preset_mapping: Dictionary mapping device names to preset names
|
||||
step_mapping: Optional dictionary mapping device names to step values
|
||||
|
||||
Returns:
|
||||
Dictionary with select field ready to use in build_message
|
||||
|
||||
Example:
|
||||
select = build_select_dict(
|
||||
{"device1": "rainbow_preset", "device2": "pulse_preset"},
|
||||
step_mapping={"device1": 10}
|
||||
)
|
||||
message = build_message(select=select)
|
||||
"""
|
||||
select = {}
|
||||
for device_name, preset_name in device_preset_mapping.items():
|
||||
select_list = [preset_name]
|
||||
if step_mapping and device_name in step_mapping:
|
||||
select_list.append(step_mapping[device_name])
|
||||
select[device_name] = select_list
|
||||
return select
|
||||
Reference in New Issue
Block a user