feat(streaming): introduce WebSocket Streaming API bridging event bus

ApiServer: add AsyncWebSocket at /ws; accept JSON {event, payload} (string or object) and dispatch via ctx.fire; mirror all local events to clients using NodeContext::onAny.\nNodeContext: add onAny subscriber API.\nNeoPatternService: add api/neopattern/color event to set solid color.\nCluster: centralize cluster/broadcast sending in core; services delegate.\nAPI: add generic /api/node/event and /api/cluster/event endpoints in respective services.\nTests: add ws-color-client, ws-cluster-broadcast-color, http-cluster-broadcast-color.\nDocs: add StreamingAPI.md; update README and test/README.\nFixes: robust WS JSON parsing on ESP8266 and payload handling.
This commit is contained in:
2025-09-28 21:08:42 +02:00
parent f0df84dc87
commit 3cc5405292
33 changed files with 5976 additions and 8 deletions

71
docs/StreamingAPI.md Normal file
View File

@@ -0,0 +1,71 @@
## Streaming API (WebSocket)
### Overview
The streaming API exposes an event-driven WebSocket at `/ws`. It bridges between external clients and the internal event bus:
- Incoming WebSocket JSON `{ event, payload }``ctx.fire(event, payload)`
- Local events → broadcasted to all connected WebSocket clients as `{ event, payload }`
This allows real-time control and observation of the system without polling.
### URL
- `ws://<device-ip>/ws`
### Message Format
- Client → Device
```json
{
"event": "<event-name>",
"payload": "<json-string>" | { /* inline JSON */ }
}
```
- Device → Client
```json
{
"event": "<event-name>",
"payload": "<json-string>"
}
```
Notes:
- The device accepts `payload` as a string or a JSON object/array. Objects are serialized into a string before dispatching to local subscribers to keep a consistent downstream contract.
- A minimal ack `{ "ok": true }` is sent after a valid inbound message.
### Event Bus Integration
- The WebSocket registers an `onAny` subscriber to `NodeContext` so that all local events are mirrored to clients.
- Services should subscribe to specific events via `ctx.on("<name>", ...)`.
### Examples
1) Set a solid color on NeoPattern:
```json
{
"event": "api/neopattern/color",
"payload": { "color": "#FF0000", "brightness": 128 }
}
```
2) Broadcast a cluster event (delegated to core):
```json
{
"event": "cluster/broadcast",
"payload": {
"event": "api/neopattern/color",
"data": { "color": "#00FF00", "brightness": 128 }
}
}
```
### Reference Implementation
- WebSocket setup and bridging are implemented in `ApiServer`.
- Global event subscription uses `NodeContext::onAny`.
Related docs:
- [`ClusterBroadcast.md`](./ClusterBroadcast.md) — centralized UDP broadcasting and CLUSTER_EVENT format