docs: rewrite API reference for current HTTP and driver flows
Made-with: Cursor
This commit is contained in:
446
docs/API.md
446
docs/API.md
@@ -1,263 +1,297 @@
|
|||||||
# LED Driver ESPNow API Documentation
|
# LED Controller API
|
||||||
|
|
||||||
This document describes the ESPNow message format for controlling LED driver devices.
|
This document covers:
|
||||||
|
|
||||||
## Message Format
|
1. **HTTP and WebSocket** exposed by the Raspberry Pi app (`src/main.py`) — profiles, presets, transport send, and related resources.
|
||||||
|
2. **LED driver JSON** — the compact message format sent over the serial→ESP-NOW bridge to devices (same logical API as ESP-NOW payloads).
|
||||||
|
|
||||||
All messages are JSON objects sent via ESPNow with the following structure:
|
Default listen address: `0.0.0.0`. Port defaults to **80**; override with the `PORT` environment variable (see `pipenv run run`).
|
||||||
|
|
||||||
|
All JSON APIs use `Content-Type: application/json` for bodies and responses unless noted.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Session and scoping
|
||||||
|
|
||||||
|
Several routes use **`@with_session`**: the server stores a **current profile** in the session (cookie). Endpoints that scope data to “the current profile” (notably **`/presets`**) only return or mutate presets whose `profile_id` matches that session value.
|
||||||
|
|
||||||
|
Profiles are selected with **`POST /profiles/<id>/apply`**, which sets `current_profile` in the session.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Static pages and assets
|
||||||
|
|
||||||
|
| Method | Path | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| GET | `/` | Main UI (`templates/index.html`) |
|
||||||
|
| GET | `/settings` | Settings page (`templates/settings.html`) |
|
||||||
|
| GET | `/favicon.ico` | Empty response (204) |
|
||||||
|
| GET | `/static/<path>` | Static files under `src/static/` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## WebSocket: `/ws`
|
||||||
|
|
||||||
|
Connect to **`ws://<host>:<port>/ws`**.
|
||||||
|
|
||||||
|
- Send **JSON**: the object is forwarded to the transport (serial bridge → ESP-NOW) as JSON. Optional key **`to`**: 12-character hex MAC address; if present it is removed from the object and the payload is sent to that peer; otherwise the default destination is used.
|
||||||
|
- Send **non-JSON text**: forwarded as raw bytes with the default address.
|
||||||
|
- On send failure, the server may reply with `{"error": "Send failed"}`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## HTTP API by resource
|
||||||
|
|
||||||
|
Below, `<id>` values are string identifiers used by the JSON stores (numeric strings in practice).
|
||||||
|
|
||||||
|
### Settings — `/settings`
|
||||||
|
|
||||||
|
| Method | Path | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| GET | `/settings` | Full settings object (from `settings.json` / `Settings` model). |
|
||||||
|
| PUT | `/settings/settings` | Merge keys into settings and save. Returns `{"message": "Settings updated successfully"}`. |
|
||||||
|
| GET | `/settings/wifi/ap` | Saved Wi‑Fi AP fields: `saved_ssid`, `saved_password`, `saved_channel`, `active` (Pi: `active` is always false). |
|
||||||
|
| POST | `/settings/wifi/ap` | Body: `ssid` (required), `password`, `channel` (1–11). Persists AP-related settings. |
|
||||||
|
| GET | `/settings/page` | Serves `templates/settings.html` (same page as `GET /settings` from the root app, for convenience). |
|
||||||
|
|
||||||
|
### Profiles — `/profiles`
|
||||||
|
|
||||||
|
| Method | Path | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| GET | `/profiles` | `{"profiles": {...}, "current_profile_id": "<id>"}`. Ensures a default current profile when possible. |
|
||||||
|
| GET | `/profiles/current` | `{"id": "...", "profile": {...}}` |
|
||||||
|
| GET | `/profiles/<id>` | Single profile. If `<id>` is `current`, same as `/profiles/current`. |
|
||||||
|
| POST | `/profiles` | Create profile. Body may include `name` and other fields. Returns `{ "<id>": { ... } }` with status 201. |
|
||||||
|
| POST | `/profiles/<id>/apply` | Sets session current profile to `<id>`. |
|
||||||
|
| POST | `/profiles/<id>/clone` | Clone profile (tabs, palettes, presets). Body may include `name`. |
|
||||||
|
| PUT | `/profiles/current` | Update the current profile (from session). |
|
||||||
|
| PUT | `/profiles/<id>` | Update profile by id. |
|
||||||
|
| DELETE | `/profiles/<id>` | Delete profile. |
|
||||||
|
|
||||||
|
### Presets — `/presets`
|
||||||
|
|
||||||
|
Scoped to **current profile** in session (see above).
|
||||||
|
|
||||||
|
| Method | Path | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| GET | `/presets` | Map of preset id → preset object for the current profile only. |
|
||||||
|
| GET | `/presets/<id>` | One preset, 404 if missing or wrong profile. |
|
||||||
|
| POST | `/presets` | Create preset; server assigns id and sets `profile_id`. Body fields stored on the preset. Returns `{ "<id>": { ... } }`, 201. |
|
||||||
|
| PUT | `/presets/<id>` | Update preset (must belong to current profile). |
|
||||||
|
| DELETE | `/presets/<id>` | Delete preset. |
|
||||||
|
| POST | `/presets/send` | Push presets to the LED driver over the configured transport (see below). |
|
||||||
|
|
||||||
|
**`POST /presets/send` body:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"preset_ids": ["1", "2"],
|
||||||
|
"save": true,
|
||||||
|
"default": "1",
|
||||||
|
"destination_mac": "aabbccddeeff"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- **`preset_ids`** (or **`ids`**): non-empty list of preset ids to include.
|
||||||
|
- **`save`**: if true, the outgoing message includes `"save": true` so the driver may persist presets (default true).
|
||||||
|
- **`default`**: optional preset id string; forwarded as top-level `"default"` in the driver message (startup selection on device).
|
||||||
|
- **`destination_mac`** (or **`to`**): optional 12-character hex MAC for unicast; omitted uses the transport default (e.g. broadcast).
|
||||||
|
|
||||||
|
Response on success includes `presets_sent`, `messages_sent` (chunking splits payloads so each JSON string stays ≤ 240 bytes).
|
||||||
|
|
||||||
|
### Tabs — `/tabs`
|
||||||
|
|
||||||
|
| Method | Path | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| GET | `/tabs` | `tabs`, `tab_order`, `current_tab_id`, `profile_id` for the session-backed profile. |
|
||||||
|
| GET | `/tabs/current` | Current tab from cookie/session. |
|
||||||
|
| POST | `/tabs` | Create tab; optional JSON `name`, `names`, `presets`; can append to current profile’s tab list. |
|
||||||
|
| GET | `/tabs/<id>` | Tab JSON. |
|
||||||
|
| PUT | `/tabs/<id>` | Update tab. |
|
||||||
|
| DELETE | `/tabs/<id>` | Delete tab; can delete `current` to remove the active tab; updates profile tab list. |
|
||||||
|
| POST | `/tabs/<id>/set-current` | Sets `current_tab` cookie. |
|
||||||
|
| POST | `/tabs/<id>/clone` | Clone tab into current profile. |
|
||||||
|
|
||||||
|
### Palettes — `/palettes`
|
||||||
|
|
||||||
|
| Method | Path | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| GET | `/palettes` | Map of id → color list. |
|
||||||
|
| GET | `/palettes/<id>` | `{"colors": [...], "id": "<id>"}` |
|
||||||
|
| POST | `/palettes` | Body may include `colors`. Returns palette object with `id`, 201. |
|
||||||
|
| PUT | `/palettes/<id>` | Update colors (`name` ignored). |
|
||||||
|
| DELETE | `/palettes/<id>` | Delete palette. |
|
||||||
|
|
||||||
|
### Groups — `/groups`
|
||||||
|
|
||||||
|
| Method | Path | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| GET | `/groups` | All groups. |
|
||||||
|
| GET | `/groups/<id>` | One group. |
|
||||||
|
| POST | `/groups` | Create; optional `name` and fields. |
|
||||||
|
| PUT | `/groups/<id>` | Update. |
|
||||||
|
| DELETE | `/groups/<id>` | Delete. |
|
||||||
|
|
||||||
|
### Scenes — `/scenes`
|
||||||
|
|
||||||
|
| Method | Path | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| GET | `/scenes` | All scenes. |
|
||||||
|
| GET | `/scenes/<id>` | One scene. |
|
||||||
|
| POST | `/scenes` | Create (body JSON stored on scene). |
|
||||||
|
| PUT | `/scenes/<id>` | Update. |
|
||||||
|
| DELETE | `/scenes/<id>` | Delete. |
|
||||||
|
|
||||||
|
### Sequences — `/sequences`
|
||||||
|
|
||||||
|
| Method | Path | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| GET | `/sequences` | All sequences. |
|
||||||
|
| GET | `/sequences/<id>` | One sequence. |
|
||||||
|
| POST | `/sequences` | Create; may use `group_name`, `presets` in body. |
|
||||||
|
| PUT | `/sequences/<id>` | Update. |
|
||||||
|
| DELETE | `/sequences/<id>` | Delete. |
|
||||||
|
|
||||||
|
### Patterns — `/patterns`
|
||||||
|
|
||||||
|
| Method | Path | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| GET | `/patterns/definitions` | Contents of `pattern.json` (pattern metadata for the UI). |
|
||||||
|
| GET | `/patterns` | All pattern records. |
|
||||||
|
| GET | `/patterns/<id>` | One pattern. |
|
||||||
|
| POST | `/patterns` | Create (`name`, optional `data`). |
|
||||||
|
| PUT | `/patterns/<id>` | Update. |
|
||||||
|
| DELETE | `/patterns/<id>` | Delete. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## LED driver message format (transport / ESP-NOW)
|
||||||
|
|
||||||
|
Messages are JSON objects. The Pi **`build_message()`** helper (`src/util/espnow_message.py`) produces the same shape sent over serial and forwarded by the ESP32 bridge.
|
||||||
|
|
||||||
|
### Top-level fields
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"v": "1",
|
"v": "1",
|
||||||
"presets": { ... },
|
"presets": { },
|
||||||
"select": { ... }
|
"select": { },
|
||||||
|
"save": true,
|
||||||
|
"default": "preset_id",
|
||||||
|
"b": 255
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Version Field
|
- **`v`** (required): Must be `"1"` or the driver ignores the message.
|
||||||
|
- **`presets`**: Map of **preset id** (string) → preset object (see below). Optional **`name`** field on each value is accepted for display; the driver keys presets by map key.
|
||||||
|
- **`select`**: Map of **device name** (as in device settings) → `[ "preset_id" ]` or `[ "preset_id", step ]`.
|
||||||
|
- **`save`**: If present (e.g. true), the driver may persist presets to flash after applying.
|
||||||
|
- **`default`**: Preset id string to use as startup default on the device.
|
||||||
|
- **`b`**: Optional **global** brightness 0–255 (driver applies this in addition to per-preset brightness).
|
||||||
|
|
||||||
- **`v`** (required): Message version, must be `"1"`. Messages with other versions are ignored.
|
### Preset object (wire / driver keys)
|
||||||
|
|
||||||
## Presets
|
On the wire, presets use **short keys** (saves space in the ≤240-byte chunks):
|
||||||
|
|
||||||
Presets define LED patterns with their configuration. Each preset has a name and contains pattern-specific settings.
|
| Key | Meaning | Notes |
|
||||||
|
|-----|---------|--------|
|
||||||
|
| `p` | Pattern id | `off`, `on`, `blink`, `rainbow`, `pulse`, `transition`, `chase`, `circle` |
|
||||||
|
| `c` | Colors | Array of `"#RRGGBB"` hex strings; converted to RGB on device |
|
||||||
|
| `d` | Delay ms | Default 100 |
|
||||||
|
| `b` | Preset brightness | 0–255; combined with global `b` on the device |
|
||||||
|
| `a` | Auto | `true`: run continuously; `false`: one step/cycle per “beat” |
|
||||||
|
| `n1`–`n6` | Pattern parameters | See below |
|
||||||
|
|
||||||
### Preset Structure
|
The HTTP app’s **`POST /presets/send`** path builds this from stored presets via **`build_preset_dict()`** (long names like `pattern` / `colors` in the DB are translated to `p` / `c` / …).
|
||||||
|
|
||||||
```json
|
### Pattern-specific parameters (`n1`–`n6`)
|
||||||
{
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Preset Fields
|
|
||||||
|
|
||||||
- **`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
|
#### Rainbow
|
||||||
- **`n1`**: Step increment (how many color wheel positions to advance per update). Default: `1`
|
- **`n1`**: Step increment on the color wheel per update (default 1).
|
||||||
|
|
||||||
#### Pulse
|
#### Pulse
|
||||||
- **`n1`**: Attack time in milliseconds (fade in)
|
- **`n1`**: Attack (fade in) ms
|
||||||
- **`n2`**: Hold time in milliseconds (full brightness)
|
- **`n2`**: Hold ms
|
||||||
- **`n3`**: Decay time in milliseconds (fade out)
|
- **`n3`**: Decay (fade out) ms
|
||||||
- **`delay`**: Delay time in milliseconds (off between pulses)
|
- **`d`**: Off time between pulses ms
|
||||||
|
|
||||||
#### Transition
|
#### Transition
|
||||||
- **`delay`**: Transition duration in milliseconds
|
- **`d`**: Transition duration ms
|
||||||
|
|
||||||
#### Chase
|
#### Chase
|
||||||
- **`n1`**: Number of LEDs with first color
|
- **`n1`**: LEDs with first color
|
||||||
- **`n2`**: Number of LEDs with second color
|
- **`n2`**: LEDs with second color
|
||||||
- **`n3`**: Movement amount on even steps (can be negative)
|
- **`n3`**: Movement on even steps (may be negative)
|
||||||
- **`n4`**: Movement amount on odd steps (can be negative)
|
- **`n4`**: Movement on odd steps (may be negative)
|
||||||
|
|
||||||
#### Circle
|
#### Circle
|
||||||
- **`n1`**: Head movement rate (LEDs per second)
|
- **`n1`**: Head speed (LEDs/s)
|
||||||
- **`n2`**: Maximum length
|
- **`n2`**: Max length
|
||||||
- **`n3`**: Tail movement rate (LEDs per second)
|
- **`n3`**: Tail speed (LEDs/s)
|
||||||
- **`n4`**: Minimum length
|
- **`n4`**: Min length
|
||||||
|
|
||||||
## Select Messages
|
### Select messages
|
||||||
|
|
||||||
Select messages control which preset is active on which device. The format uses a list to support step synchronization.
|
|
||||||
|
|
||||||
### Select Format
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"select": {
|
"select": {
|
||||||
"device_name": ["preset_name"],
|
"device_name": ["preset_id"],
|
||||||
"device_name2": ["preset_name2", step_value]
|
"other_device": ["preset_id", 10]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Select Fields
|
- One element: select preset; step behavior follows driver rules (reset on `off`, etc.).
|
||||||
|
- Two elements: explicit **step** for sync.
|
||||||
|
|
||||||
- **`select`**: Object mapping device names to selection lists
|
### Beat and sync behavior
|
||||||
- **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)
|
|
||||||
|
|
||||||
### Step Synchronization
|
- Sending **`select`** again with the **same** preset name acts as a **beat** (advances manual patterns / restarts generators per driver logic).
|
||||||
|
- Choosing **`off`** resets step as a sync point; then selecting a pattern aligns step 0 across devices unless a step is passed explicitly.
|
||||||
|
|
||||||
The step value allows precise synchronization across multiple devices:
|
### Example (compact preset map)
|
||||||
|
|
||||||
- **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
|
|
||||||
// Beat 1
|
|
||||||
{"select": {"device1": ["rainbow_preset"]}}
|
|
||||||
|
|
||||||
// Beat 2 (same preset = beat)
|
|
||||||
{"select": {"device1": ["rainbow_preset"]}}
|
|
||||||
|
|
||||||
// Beat 3
|
|
||||||
{"select": {"device1": ["rainbow_preset"]}}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Synchronization
|
|
||||||
|
|
||||||
### Using "off" Pattern
|
|
||||||
|
|
||||||
Selecting the "off" pattern resets the step counter to 0, providing a synchronization point:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"select": {
|
|
||||||
"device1": ["off"],
|
|
||||||
"device2": ["off"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
After all devices are "off", switching to a pattern ensures they all start from step 0:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"select": {
|
|
||||||
"device1": ["rainbow_preset"],
|
|
||||||
"device2": ["rainbow_preset"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using Step Parameter
|
|
||||||
|
|
||||||
For precise synchronization, use the step parameter:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"select": {
|
|
||||||
"device1": ["rainbow_preset", 10],
|
|
||||||
"device2": ["rainbow_preset", 10],
|
|
||||||
"device3": ["rainbow_preset", 10]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
All devices will start at step 10 and advance together on subsequent beats.
|
|
||||||
|
|
||||||
## Complete Example
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"v": "1",
|
"v": "1",
|
||||||
|
"save": true,
|
||||||
"presets": {
|
"presets": {
|
||||||
"red_blink": {
|
"1": {
|
||||||
"pattern": "blink",
|
"name": "Red blink",
|
||||||
"colors": ["#FF0000"],
|
"p": "blink",
|
||||||
"delay": 200,
|
"c": ["#FF0000"],
|
||||||
"brightness": 255,
|
"d": 200,
|
||||||
"auto": true
|
"b": 255,
|
||||||
},
|
"a": true,
|
||||||
"rainbow_manual": {
|
"n1": 0, "n2": 0, "n3": 0, "n4": 0, "n5": 0, "n6": 0
|
||||||
"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": {
|
"select": {
|
||||||
"device1": ["red_blink"],
|
"living-room": ["1"]
|
||||||
"device2": ["rainbow_manual", 0],
|
|
||||||
"device3": ["pulse_slow"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Message Processing
|
---
|
||||||
|
|
||||||
1. **Version Check**: Messages with `v != "1"` are rejected
|
## Processing summary (driver)
|
||||||
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
|
|
||||||
|
|
||||||
## Best Practices
|
1. Reject if `v != "1"`.
|
||||||
|
2. Apply optional top-level **`b`** (global brightness).
|
||||||
|
3. For each entry in **`presets`**, normalize colors and upsert preset by id.
|
||||||
|
4. If this device’s **`name`** appears in **`select`**, run selection (optional step).
|
||||||
|
5. If **`default`** is set, store startup preset id.
|
||||||
|
6. If **`save`** is set, persist presets.
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
## Error Handling
|
## Error handling (HTTP)
|
||||||
|
|
||||||
- Invalid version: Message is ignored
|
Controllers typically return JSON with an **`error`** string and 4xx/5xx status codes. Invalid JSON bodies often yield `{"error": "Invalid JSON"}`.
|
||||||
- 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)
|
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
- Colors are automatically converted from hex strings to RGB tuples
|
- **Human-readable preset fields** (`pattern`, `colors`, `delay`, …) are fine in the **web app / database**; the **send path** converts them to **`p` / `c` / `d`** for the driver.
|
||||||
- Color order reordering happens automatically based on device settings
|
- For a copy of the older long-key reference, see **`led-driver/docs/API.md`** in this repo (conceptually the same behavior; wire format prefers short keys).
|
||||||
- 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
|
|
||||||
|
|||||||
Reference in New Issue
Block a user