Panels become devices: per-device documents in the flow repo, a Devices tab

- flow/devices.py: DeviceDef in FLOWS_DIR/_devices/<id>.json, committed under
  the flow store's write lock, 409 on a stale version; nonces in DEVICES_FILE
  outside git; in-memory presence announced as device_status
- api/routes/devices.py: CRUD, pairing with an optional {name, board,
  firmware}, a pending list adopted without a code from the LAN only, unpair;
  /panels/pair, /panels/pair/{code} and GET /panels/{id} kept as aliases,
  PUT /panels/ gone
- _device_may replaces _panel_may, JWT claims unchanged
- panels.json moves into devices once at startup, then .migrated
- MCP list_devices, get_device; search category device
- Devices tab (list, detail with presence, pairing, ordered dashboards,
  touch, I/O), PanelsDialog removed, serialised optimistic saves

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Melvin Strobl
2026-10-16 15:48:36 +02:00
co-authored by Claude Opus 5.5
parent 01495aed25
commit ed06bdec6b
56 changed files with 3539 additions and 2404 deletions
+11 -6
View File
@@ -179,7 +179,7 @@ a run, a current message or a flow input's initial value still references the
digest, the call returns `409`. The hourly sweep removes unreferenced bytes
on its own, so this is for reclaiming space sooner.
## Dashboards and panels
## Dashboards and devices
| Method | Path | Action |
|---|---|---|
@@ -190,11 +190,16 @@ on its own, so this is for reclaiming space sooner.
| `PUT` | `/dashboards/{name}` | save a draft |
| `POST` | `/dashboards/{name}/publish`, `/dashboards/{name}/discard`, `/dashboards/{name}/rename` | publish, discard, or rename |
| `POST` | `/dashboards/{name}/duplicate` | copy into an unpublished `{name}_copy` |
| `GET`, `PUT` | `/panels/` | read or replace panel assignments; send the current `version` |
| `POST` | `/panels/pair` | start device pairing |
| `GET` | `/panels/pair/{code}` | poll pairing with its `secret` |
| `POST` | `/panels/{id}/pair` | approve the pairing |
| `POST` | `/panels/{id}/unpair` | revoke the panel credential |
| `GET` | `/devices/` | list devices, with `online` and `last_seen` |
| `POST`, `GET`, `PUT`, `DELETE` | `/devices/{id}` | create, read, save or delete one; a save sends the `version` it read and gets 409 when someone saved first |
| `POST` | `/devices/pair` | start device pairing, optionally with `{name, board, firmware}`; unauthenticated |
| `GET` | `/devices/pair/{code}` | poll pairing with its `secret`; answers `device` (and `panel` for older firmware); unauthenticated |
| `GET` | `/devices/pending` | devices waiting for a code, as they describe themselves |
| `POST` | `/devices/{id}/pair` | adopt by `{code}`, or by `{request}` from the pending list for a request from your network |
| `POST` | `/devices/{id}/unpair` | revoke the device credential |
`/panels/pair`, `/panels/pair/{code}` and `GET /panels/{id}` answer as their
`/devices/` counterparts, for firmware that has not updated yet.
## Secrets, modules, and alerts