powerpods/goTool/docs/API_WEBSOCKET.md

253 lines
7.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# WebSocket API
External API: `ws://localhost:8081/ws` (default `-api-addr`, disable with empty string).
Start with `go run . -port /dev/ttyUSB0 serve`.
---
## Connection flow
1. Connect → server sends `hello` (push off; defaults and command list).
2. Send JSON commands → one reply per message (`*_status` or `client_list`).
3. After `set_stream` with `enable: true`, server may push `input` messages without a prior command.
Commands and pushes share one socket — always branch on `type`.
On disconnect, `set_stream` state for that socket is dropped. Firmware settings (`set_input_stream`, `set_tap_notify`) stay on the master until changed.
---
## Two layers (firmware vs host)
| Layer | Commands | Effect |
| ------------------ | ------------------------------------ | ------------------------------------------------------------------ |
| Firmware (ESP-NOW) | `set_input_stream`, `set_tap_notify` | Per `client_id`: slave sends accel and/or tap events to the master |
| This connection | `set_stream` | Whether you receive push JSON on this socket |
UART polling runs only when at least one connection has `receive_input: true` **and** at least one slave streams input or has tap notify enabled. `set_tap_notify` alone does not enable push — you still need `set_stream`.
### Push timing (per connection)
| Field | Where | Meaning |
| ------------- | -------------------------------------- | ------------------------------------------------------------ |
| `interval_ms` | `hello`, `set_stream`, `stream_status` | Minimum ms between `input` pushes on this socket (1 … 10000) |
| `pre_fetch` | `set_stream`, `stream_status` | Ms before each push when the host starts the UART cache read |
Global UART poll interval = minimum `interval_ms` among all connections with push enabled.
Typical sequence:
1. `list_clients` → slave IDs
2. Per slave: `set_input_stream` and/or `set_tap_notify`
3. `set_stream` with `"enable": true`
4. Read `input` messages; filter by `client_id` in your app (no per-slave filter on the wire)
---
## Push: `input`
Combines latest accel cache and visible tap state for every slave slot on the master.
**Success:**
```json
{
"type": "input",
"t": 1716900123456789012,
"success": true,
"clients": [
{
"client_id": 16,
"valid": true,
"x": 12,
"y": -34,
"z": 16384,
"accel_age_ms": 8,
"tap_kind": "single",
"tap_age_ms": 3
},
{
"client_id": 42,
"valid": false
}
]
}
```
| Field | Meaning |
| -------------- | -------------------------------------------------------------------- |
| `t` | Unix timestamp in nanoseconds when the host read the cache |
| `success` | `true` if `CACHE_STATUS` succeeded |
| `clients[]` | One entry per slave slot (includes invalid/stale entries) |
| `client_id` | Same id as in `list_clients` |
| `valid` | `false` if no accel sample yet or stale; omit `x`/`y`/`z` when false |
| `x`, `y`, `z` | Raw accelerometer LSB (BMA456, ±2 g) |
| `accel_age_ms` | Ms since the master received this accel sample |
| `tap_kind` | `"single"`, `"double"`, or `"triple"`; omit when no recent tap |
| `tap_age_ms` | Ms since tap in master cache; omit with `tap_kind` |
Tap events stay visible for `tap_display_min_ms` (2000, in `hello`) after the API first saw them.
**Failure** (no `clients` array):
```json
{"type":"input","t":1716900123456789012,"success":false,"error":"uart busy"}
```
---
## Commands
One JSON object per message; field `type` selects the command.
**Errors:** Replies use the matching response `type`. On failure: `success: false` (or omitted) and `"error": "…"`. Malformed JSON or unknown `type``stream_status` with `error`.
### `hello` (server → client)
```json
{
"type": "hello",
"serial_port": "/dev/ttyUSB0",
"interval_ms": 16,
"pre_fetch_ms": 2,
"tap_display_min_ms": 2000,
"commands": [
"list_clients",
"set_stream", "get_stream",
"set_input_stream", "get_input_stream",
"set_tap_notify", "get_tap_notify",
"set_led_ring", "get_battery"
]
}
```
### `list_clients`
Request: `{"type":"list_clients"}`
Response `client_list`:
```json
{
"type": "client_list",
"success": true,
"clients": [
{
"id": 16,
"mac": "aa:bb:cc:dd:ee:10",
"available": true,
"input_stream": false,
"tap_notify_single": false,
"tap_notify_double": false,
"tap_notify_triple": false
}
]
}
```
Also per client: `version`, `used`, `last_ping`, `last_success_ping`.
### `set_stream` / `get_stream`
```json
{"type":"set_stream","enable":true,"interval_ms":32,"pre_fetch":2}
{"type":"get_stream"}
```
Response `stream_status`:
```json
{"type":"stream_status","receive_input":true,"interval_ms":32,"pre_fetch":2,"success":true}
```
### `set_input_stream` / `get_input_stream` (firmware)
`client_id` required (> 0).
```json
{"type":"set_input_stream","client_id":16,"enable":true}
{"type":"get_input_stream","client_id":16}
```
Response `input_stream_status`:
```json
{"type":"input_stream_status","client_id":16,"enabled":true,"success":true}
```
### `set_tap_notify` / `get_tap_notify` (firmware)
Set requires `single`, `double_tap`, `triple` per client, or `"all_clients": true` for broadcast.
```json
{"type":"set_tap_notify","client_id":16,"single":true,"double_tap":false,"triple":false}
{"type":"get_tap_notify","client_id":16}
```
Response `tap_notify_status`:
```json
{"type":"tap_notify_status","client_id":16,"success":true,"single":true,"double_tap":false,"triple":false}
```
### `set_led_ring`
```json
{"type":"set_led_ring","mode":"color","client_id":16,"r":255,"g":0,"b":0,"intensity":128}
{"type":"set_led_ring","mode":"digit","client_id":0,"digit":3,"r":0,"g":255,"b":0}
{"type":"set_led_ring","mode":"find-me","all_clients":true,"slaves_only":true}
```
| Request `mode` | Notes |
| -------------- | --------------------------- |
| `clear` | Turn off |
| `color` | Full ring RGB + `intensity` |
| `progress` | `progress` 0100 |
| `digit` | `digit` 010 |
| `blink` | `blink_ms`, `blink_count` |
| `find-me` | Locate pod |
Target: `client_id` (`0` = master) or `all_clients` (+ optional `slaves_only`).
Response `led_ring_status``mode` is numeric: 0=clear, 1=progress, 2=digit, 3=blink, 4=find-me, 5=color.
```json
{"type":"led_ring_status","success":true,"mode":5,"client_id":16,"slaves_updated":1}
```
### `get_battery`
Slaves push battery every 30 s; this reads the master cache. Default: all clients.
```json
{"type":"get_battery","all_clients":true}
{"type":"get_battery","client_id":16}
```
Response `battery_status`:
```json
{
"type": "battery_status",
"success": true,
"samples": [
{
"client_id": 16,
"lipo1": {"valid": true, "voltage_mv": 3850, "percent": 71},
"lipo2": {"valid": false},
"age_ms": 1200
}
]
}
```