Each client panel gets mode-specific LED inputs (color, intensity, progress, digit, blink) and the Makefile now copies API_REST.md alongside the WebSocket docs. Co-authored-by: Cursor <cursoragent@cursor.com>
6.2 KiB
REST API
go run . -port /dev/ttyUSB0 serve starts two HTTP servers on the same UART link:
| Base URL | Flag | Used by |
|---|---|---|
http://localhost:8080 |
-addr (default :8080) |
Web dashboard + automation on the UI routes |
http://localhost:8081 |
-api-addr (default :8081, "" disables) |
External programs; subset of routes + service info |
WebSocket streaming (accel/tap push): API_WEBSOCKET.md.
All JSON responses use Content-Type: application/json. On UART errors many routes return 503 with "error" in the body.
External API (:8081)
Service info
GET /
GET /api/v1/
{
"name": "powerpod-external-api",
"version": "1",
"serial_port": "/dev/ttyUSB0",
"websocket": "/ws",
"default_interval_ms": 16,
"min_interval_ms": 1,
"max_interval_ms": 10000,
"tap_display_min_ms": 2000,
"description": "..."
}
Battery
GET /api/battery?all_clients=true
GET /api/battery?client_id=16
POST /api/battery
Content-Type: application/json
POST body:
{"all_clients": true}
{"client_id": 0}
{"client_id": 16}
Response:
{
"success": true,
"samples": [
{
"client_id": 16,
"lipo1": {"valid": true, "voltage_mv": 3850, "percent": 71},
"lipo2": {"valid": false},
"age_ms": 1200
}
]
}
Slaves push battery to the master every 30 s; these routes read the master cache.
WebSocket equivalent: get_battery on ws://localhost:8081/ws (reply type battery_status).
LED ring
POST /api/led-ring
Content-Type: application/json
Body:
{"mode":"color","client_id":16,"r":255,"g":0,"b":0,"intensity":128}
{"mode":"digit","client_id":0,"digit":3,"r":0,"g":255,"b":0}
{"mode":"find-me","all_clients":true,"slaves_only":true}
mode |
Notes |
|---|---|
clear |
Turn off |
color |
Full ring RGB + intensity |
progress |
progress 0–100 |
digit |
digit 0–10 |
blink |
blink_ms, blink_count |
find-me |
Locate pod |
Use client_id (0 = master) or all_clients (+ optional slaves_only) for broadcast.
Response: success, slaves_updated, optional error.
WebSocket: set_led_ring with the same fields plus "type":"set_led_ring" → led_ring_status.
Dashboard API (:8080)
Used by the web UI; safe for scripts that drive the same features.
Live stream (host CACHE_STATUS poll ~16 ms)
GET /api/live-stream
PUT /api/live-stream
Content-Type: application/json
{"enable": true}
{"enabled": true, "success": true}
Enables fast UART polling for dashboard accel/tap display. Per-slave accel still requires accel-stream (below).
Accel stream (firmware ESP-NOW, per slave)
GET /api/clients/16/accel-stream
PUT /api/clients/16/accel-stream
Content-Type: application/json
{"enable": true}
{"enabled": true, "client_id": 16, "success": true}
All slaves:
POST /api/accel-stream
Content-Type: application/json
{"write": true, "enable": true, "all_clients": true}
Polling on the host runs only while at least one slave has streaming enabled (here or via external WebSocket / dashboard).
Tap notify (firmware; does not start host tap polling)
GET /api/clients/16/tap-notify
PUT /api/clients/16/tap-notify
Content-Type: application/json
{"single": true, "double_tap": false, "triple": false}
{
"client_id": 16,
"success": true,
"slaves_updated": 1,
"single": true,
"double_tap": false,
"triple": false
}
All slaves:
POST /api/tap-notify
Content-Type: application/json
{"single": true, "double_tap": false, "triple": false, "all_clients": true}
Host tap display / external set_tap_stream is separate.
Tap snapshot (one-shot, via CACHE_STATUS)
GET /api/tap-snapshot?client_id=16
Reads the combined cache (CACHE_STATUS); optional client_id filters pending tap events. Pending taps are consumed on read.
{
"events": [
{"client_id": 16, "kind": "single", "age_ms": 4}
]
}
Deadzone
GET /api/deadzone?client_id=0
POST /api/deadzone
Content-Type: application/json
{"write": true, "deadzone": 128, "client_id": 0}
With all_clients + slaves_only: push to ESP-NOW slaves only (master BMA456 unchanged).
{"deadzone": 128, "client_id": 0, "success": true, "slaves_updated": 2}
Unicast test
POST /api/unicast-test
Content-Type: application/json
{"client_id": 16, "seq": 42}
Find me
POST /api/find-me
Content-Type: application/json
{"client_id": 16}
client_id 0 = master LED ring.
Restart
POST /api/restart
Content-Type: application/json
{"client_id": 16}
OTA (master UART upload)
POST /api/ota
Content-Type: multipart/form-data
Form field firmware: binary image, max 2 MiB.
{"success": true, "bytes_written": 123456, "target_slot": 1}
Firmware distributes to slaves over ESP-NOW after OTA_END. Progress also appears on dashboard WebSocket as ota_progress messages.
CLI equivalent: go run . -port /dev/ttyUSB0 ota build/powerpod.bin
LED ring and battery
Same as external API:
POST /api/led-ringGET/POST/api/battery
Dashboard vs external
| Feature | Dashboard :8080 |
External :8081 |
|---|---|---|
| Client list | Via dashboard WebSocket state / CLI clients |
WebSocket list_clients |
| Accel/tap push stream | WebSocket state when live-stream on | WebSocket set_stream / set_tap_stream |
| Accel stream enable | REST PUT .../accel-stream |
WebSocket set_accel_stream |
| Tap notify | REST PUT .../tap-notify |
WebSocket set_tap_notify |
| LED / battery | REST | REST + WebSocket on :8081 |
UI mapping
| UI action | REST / CLI |
|---|---|
| Nur Master deadzone | POST /api/deadzone client_id: 0 or CLI deadzone -set -client 0 |
| Einzelner Slave | client_id: <id> |
| Alle Slaves deadzone | all_clients + slaves_only on POST |
| Unicast test | POST /api/unicast-test |
| Tap notify S/D/T | PUT /api/clients/{id}/tap-notify |
| Tap receive (UI) | Live stream + tap notify; see WebSocket doc for external API |