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:
2026-01-27 13:05:09 +13:00
parent e74ef6d64f
commit 7e33f7db6a
9 changed files with 1064 additions and 450 deletions

View File

@@ -1,504 +1,263 @@
# LED Controller API Specification
# LED Driver ESPNow API Documentation
**Base URL:** `http://device-ip/` or `http://192.168.4.1/` (when in AP mode)
**Protocol:** HTTP/1.1
**Content-Type:** `application/json`
This document describes the ESPNow message format for controlling LED driver devices.
## Presets API
## Message Format
### GET /presets
All messages are JSON objects sent via ESPNow with the following structure:
List all presets.
**Response:** `200 OK`
```json
{
"preset1": {
"name": "preset1",
"pattern": "on",
"colors": [[255, 0, 0]],
"delay": 100,
"n1": 0,
"n2": 0,
"n3": 0,
"n4": 0,
"n5": 0,
"n6": 0,
"n7": 0,
"n8": 0
"v": "1",
"presets": { ... },
"select": { ... }
}
```
### Version Field
- **`v`** (required): Message version, must be `"1"`. Messages with other versions are ignored.
## Presets
Presets define LED patterns with their configuration. Each preset has a name and contains pattern-specific settings.
### Preset Structure
```json
{
"presets": {
"preset_name": {
"pattern": "pattern_type",
"colors": ["#RRGGBB", ...],
"delay": 100,
"brightness": 127,
"auto": true,
"n1": 0,
"n2": 0,
"n3": 0,
"n4": 0,
"n5": 0,
"n6": 0
}
}
}
```
### GET /presets/{name}
### Preset Fields
Get a specific preset by name.
- **`pattern`** (required): Pattern type. Options:
- `"off"` - Turn off all LEDs
- `"on"` - Solid color
- `"blink"` - Blinking pattern
- `"rainbow"` - Rainbow color cycle
- `"pulse"` - Pulse/fade pattern
- `"transition"` - Color transition
- `"chase"` - Chasing pattern
- `"circle"` - Circle loading pattern
- **`colors`** (optional): Array of hex color strings (e.g., `"#FF0000"` for red). Default: `["#FFFFFF"]`
- Colors are automatically converted from hex to RGB and reordered based on device color order setting
- Supports multiple colors for patterns that use them
- **`delay`** (optional): Delay in milliseconds between pattern updates. Default: `100`
- **`brightness`** (optional): Brightness level (0-255). Default: `127`
- **`auto`** (optional): Auto mode flag. Default: `true`
- `true`: Pattern runs continuously
- `false`: Pattern advances one step per beat (manual mode)
- **`n1` through `n6`** (optional): Pattern-specific numeric parameters. Default: `0`
- See pattern-specific documentation below
### Pattern-Specific Parameters
#### Rainbow
- **`n1`**: Step increment (how many color wheel positions to advance per update). Default: `1`
#### Pulse
- **`n1`**: Attack time in milliseconds (fade in)
- **`n2`**: Hold time in milliseconds (full brightness)
- **`n3`**: Decay time in milliseconds (fade out)
- **`delay`**: Delay time in milliseconds (off between pulses)
#### Transition
- **`delay`**: Transition duration in milliseconds
#### Chase
- **`n1`**: Number of LEDs with first color
- **`n2`**: Number of LEDs with second color
- **`n3`**: Movement amount on even steps (can be negative)
- **`n4`**: Movement amount on odd steps (can be negative)
#### Circle
- **`n1`**: Head movement rate (LEDs per second)
- **`n2`**: Maximum length
- **`n3`**: Tail movement rate (LEDs per second)
- **`n4`**: Minimum length
## Select Messages
Select messages control which preset is active on which device. The format uses a list to support step synchronization.
### Select Format
**Response:** `200 OK`
```json
{
"name": "preset1",
"pattern": "on",
"colors": [[255, 0, 0]],
"delay": 100,
"n1": 0,
"n2": 0,
"n3": 0,
"n4": 0,
"n5": 0,
"n6": 0,
"n7": 0,
"n8": 0
}
```
**Response:** `404 Not Found`
```json
{
"error": "Preset not found"
}
```
### POST /presets
Create a new preset.
**Request Body:**
```json
{
"name": "preset1",
"pattern": "on",
"colors": [[255, 0, 0]],
"delay": 100,
"n1": 0,
"n2": 0,
"n3": 0,
"n4": 0,
"n5": 0,
"n6": 0,
"n7": 0,
"n8": 0
}
```
**Response:** `201 Created` - Returns the created preset
**Response:** `400 Bad Request`
```json
{
"error": "Name is required"
}
```
**Response:** `409 Conflict`
```json
{
"error": "Preset already exists"
}
```
### PUT /presets/{name}
Update an existing preset.
**Request Body:**
```json
{
"delay": 200,
"colors": [[0, 255, 0]]
}
```
**Response:** `200 OK` - Returns the updated preset
**Response:** `404 Not Found`
```json
{
"error": "Preset not found"
}
```
### DELETE /presets/{name}
Delete a preset.
**Response:** `200 OK`
```json
{
"message": "Preset deleted successfully"
}
```
**Response:** `404 Not Found`
```json
{
"error": "Preset not found"
}
```
## Profiles API
### GET /profiles
List all profiles.
**Response:** `200 OK`
```json
{
"profile1": {
"name": "profile1",
"description": "Profile description",
"scenes": []
"select": {
"device_name": ["preset_name"],
"device_name2": ["preset_name2", step_value]
}
}
```
### GET /profiles/{name}
### Select Fields
Get a specific profile by name.
- **`select`**: Object mapping device names to selection lists
- **Key**: Device name (as configured in device settings)
- **Value**: List with one or two elements:
- `["preset_name"]` - Select preset (uses default step behavior)
- `["preset_name", step]` - Select preset with explicit step value (for synchronization)
**Response:** `200 OK`
### Step Synchronization
The step value allows precise synchronization across multiple devices:
- **Without step**: `["preset_name"]`
- If switching to different preset: step resets to 0
- If selecting "off" pattern: step resets to 0
- If selecting same preset (beat): step is preserved, pattern restarts
- **With step**: `["preset_name", 10]`
- Explicitly sets step to the specified value
- Useful for synchronizing multiple devices to the same step
### Beat Functionality
Calling `select()` again with the same preset name acts as a "beat" - it restarts the pattern generator:
- **Single-tick patterns** (rainbow, chase in manual mode): Advance one step per beat
- **Multi-tick patterns** (pulse in manual mode): Run through full cycle per beat
Example beat sequence:
```json
{
"name": "profile1",
"description": "Profile description",
"scenes": []
}
// Beat 1
{"select": {"device1": ["rainbow_preset"]}}
// Beat 2 (same preset = beat)
{"select": {"device1": ["rainbow_preset"]}}
// Beat 3
{"select": {"device1": ["rainbow_preset"]}}
```
**Response:** `404 Not Found`
## Synchronization
### Using "off" Pattern
Selecting the "off" pattern resets the step counter to 0, providing a synchronization point:
```json
{
"error": "Profile not found"
}
```
### POST /profiles
Create a new profile.
**Request Body:**
```json
{
"name": "profile1",
"description": "Profile description",
"scenes": []
}
```
**Response:** `201 Created` - Returns the created profile
**Response:** `400 Bad Request`
```json
{
"error": "Name is required"
}
```
**Response:** `409 Conflict`
```json
{
"error": "Profile already exists"
}
```
### PUT /profiles/{name}
Update an existing profile.
**Request Body:**
```json
{
"description": "Updated description"
}
```
**Response:** `200 OK` - Returns the updated profile
**Response:** `404 Not Found`
```json
{
"error": "Profile not found"
}
```
### DELETE /profiles/{name}
Delete a profile.
**Response:** `200 OK`
```json
{
"message": "Profile deleted successfully"
}
```
**Response:** `404 Not Found`
```json
{
"error": "Profile not found"
}
```
## Scenes API
### GET /scenes
List all scenes. Optionally filter by profile using query parameter.
**Query Parameters:**
- `profile` (optional): Filter scenes by profile name
**Example:** `GET /scenes?profile=profile1`
**Response:** `200 OK`
```json
{
"profile1:scene1": {
"name": "scene1",
"profile_name": "profile1",
"description": "Scene description",
"transition_time": 0,
"devices": [
{"device_name": "device1", "preset_name": "preset1"},
{"device_name": "device2", "preset_name": "preset2"}
]
"select": {
"device1": ["off"],
"device2": ["off"]
}
}
```
### GET /scenes/{profile_name}/{scene_name}
After all devices are "off", switching to a pattern ensures they all start from step 0:
Get a specific scene.
**Response:** `200 OK`
```json
{
"name": "scene1",
"profile_name": "profile1",
"description": "Scene description",
"transition_time": 0,
"devices": [
{"device_name": "device1", "preset_name": "preset1"},
{"device_name": "device2", "preset_name": "preset2"}
]
"select": {
"device1": ["rainbow_preset"],
"device2": ["rainbow_preset"]
}
}
```
**Response:** `404 Not Found`
### Using Step Parameter
For precise synchronization, use the step parameter:
```json
{
"error": "Scene not found"
"select": {
"device1": ["rainbow_preset", 10],
"device2": ["rainbow_preset", 10],
"device3": ["rainbow_preset", 10]
}
}
```
### POST /scenes
All devices will start at step 10 and advance together on subsequent beats.
Create a new scene.
## Complete Example
**Request Body:**
```json
{
"name": "scene1",
"profile_name": "profile1",
"description": "Scene description",
"transition_time": 0,
"devices": [
{"device_name": "device1", "preset_name": "preset1"},
{"device_name": "device2", "preset_name": "preset2"}
]
"v": "1",
"presets": {
"red_blink": {
"pattern": "blink",
"colors": ["#FF0000"],
"delay": 200,
"brightness": 255,
"auto": true
},
"rainbow_manual": {
"pattern": "rainbow",
"delay": 100,
"n1": 2,
"auto": false
},
"pulse_slow": {
"pattern": "pulse",
"colors": ["#00FF00"],
"delay": 500,
"n1": 1000,
"n2": 500,
"n3": 1000,
"auto": false
}
},
"select": {
"device1": ["red_blink"],
"device2": ["rainbow_manual", 0],
"device3": ["pulse_slow"]
}
}
```
**Response:** `201 Created` - Returns the created scene
## Message Processing
**Response:** `400 Bad Request`
```json
{
"error": "Name is required"
}
```
or
```json
{
"error": "Profile name is required"
}
```
1. **Version Check**: Messages with `v != "1"` are rejected
2. **Preset Processing**: Presets are created or updated (upsert behavior)
3. **Color Conversion**: Hex colors are converted to RGB tuples and reordered based on device color order
4. **Selection**: Devices select their assigned preset, optionally with step value
**Response:** `409 Conflict`
```json
{
"error": "Scene already exists"
}
```
## Best Practices
### PUT /scenes/{profile_name}/{scene_name}
1. **Always include version**: Set `"v": "1"` in all messages
2. **Use "off" for sync**: Select "off" pattern to synchronize devices before starting patterns
3. **Beats for manual mode**: Send select messages repeatedly with same preset name to advance manual patterns
4. **Step for precision**: Use step parameter when exact synchronization is required
5. **Color format**: Always use hex strings (`"#RRGGBB"`), conversion is automatic
Update an existing scene.
## Error Handling
**Request Body:**
```json
{
"transition_time": 500,
"description": "Updated description"
}
```
- Invalid version: Message is ignored
- Missing preset: Selection fails, device keeps current preset
- Invalid pattern: Selection fails, device keeps current preset
- Missing colors: Pattern uses default white color
- Invalid step: Step value is used as-is (may cause unexpected behavior)
**Response:** `200 OK` - Returns the updated scene
## Notes
**Response:** `404 Not Found`
```json
{
"error": "Scene not found"
}
```
### DELETE /scenes/{profile_name}/{scene_name}
Delete a scene.
**Response:** `200 OK`
```json
{
"message": "Scene deleted successfully"
}
```
**Response:** `404 Not Found`
```json
{
"error": "Scene not found"
}
```
### POST /scenes/{profile_name}/{scene_name}/devices
Add a device assignment to a scene.
**Request Body:**
```json
{
"device_name": "device1",
"preset_name": "preset1"
}
```
**Response:** `200 OK` - Returns the updated scene
**Response:** `400 Bad Request`
```json
{
"error": "Device name and preset name are required"
}
```
**Response:** `404 Not Found`
```json
{
"error": "Scene not found"
}
```
### DELETE /scenes/{profile_name}/{scene_name}/devices/{device_name}
Remove a device assignment from a scene.
**Response:** `200 OK` - Returns the updated scene
**Response:** `404 Not Found`
```json
{
"error": "Scene not found"
}
```
## Patterns API
### GET /patterns
Get the list of available pattern names.
**Response:** `200 OK`
```json
["on", "bl", "cl", "rb", "sb", "o"]
```
### POST /patterns
Add a new pattern name to the list.
**Request Body:**
```json
{
"name": "new_pattern"
}
```
**Response:** `201 Created` - Returns the updated list of patterns
```json
["on", "bl", "cl", "rb", "sb", "o", "new_pattern"]
```
**Response:** `400 Bad Request`
```json
{
"error": "Name is required"
}
```
**Response:** `409 Conflict`
```json
{
"error": "Pattern already exists"
}
```
### DELETE /patterns/{name}
Remove a pattern name from the list.
**Response:** `200 OK`
```json
{
"message": "Pattern deleted successfully"
}
```
**Response:** `404 Not Found`
```json
{
"error": "Pattern not found"
}
```
## Error Responses
All endpoints may return the following error responses:
**400 Bad Request** - Invalid request data
```json
{
"error": "Error message"
}
```
**404 Not Found** - Resource not found
```json
{
"error": "Resource not found"
}
```
**409 Conflict** - Resource already exists
```json
{
"error": "Resource already exists"
}
```
**500 Internal Server Error** - Server error
```json
{
"error": "Error message"
}
```
- Colors are automatically converted from hex strings to RGB tuples
- Color order reordering happens automatically based on device settings
- Step counter wraps around (0-255 for rainbow, unbounded for others)
- Manual mode patterns stop after one step/cycle, waiting for next beat
- Auto mode patterns run continuously until changed