Capability report: PUT /devices/{id}/report, a Capabilities section, offline devices degraded

A device reports its board, screen, camera and ports from its own
credential (16 KB, schema 1, replacing the last report), and the flows
are validated against it again. The node panel turns a reported port's
config schema into a form, a reported screen is a canvas preset and a
touch screen's first report turns Touch friendly on. An offline device
node is degraded: amber on the canvas, a warning on Home, never a fault.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0138N7k5PT4dcR1iEqXaaQ9k
This commit is contained in:
Melvin Strobl
2026-10-16 17:37:49 +02:00
co-authored by Claude Opus 5.5
parent a9d453c9d9
commit ad76200d3d
28 changed files with 820 additions and 129 deletions
+2 -1
View File
@@ -190,7 +190,7 @@ 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` | `/devices/` | list devices, with `online` and `last_seen` |
| `GET` | `/devices/` | list devices, with `online`, `last_seen`, `report` and `reported_at` |
| `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. A read adds `io` and `bindings`, its ports as the device nodes of published flows wire them |
| `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 |
@@ -198,6 +198,7 @@ on its own, so this is for reclaiming space sooner.
| `GET` | `/devices/pair/{code}/device` | what is waiting on a code, before it is approved |
| `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 |
| `PUT` | `/devices/{id}/report` | what the device has, from its own credential only; at most 16 KB, replaces the last report |
`/panels/pair`, `/panels/pair/{code}` and `GET /panels/{id}` answer as their
`/devices/` counterparts, for firmware that has not updated yet.
+5 -4
View File
@@ -140,11 +140,12 @@ The canvas reports issues on the affected nodes:
| `ignored_worker` | a node other than a Function node names a worker, which nothing reads; advisory |
| `unknown_device` | a Device node names no device, or one that does not exist |
| `device_port` | a device port is wired in two places, or against the device's report the wrong way or type; a port the report does not list is advisory |
| `node_unhealthy` | a loaded node reports a runtime problem, such as a lost connection, or a device being offline |
| `node_unhealthy` | a loaded node reports a runtime problem, such as a lost connection |
| `node_degraded` | a loaded node works but not as it should, such as a device being offline; advisory, amber on the canvas |
Build issues prevent the affected flow from running. The last three are runtime
or advisory issues: other work continues, and node health clears when the
connection recovers.
Build issues prevent the affected flow from running. `node_unhealthy` and
`node_degraded` are runtime issues: other work continues, and they clear when
the node recovers. A degraded node is a warning on Home rather than a fault.
You can **stop** a live flow to remove its subscriptions and schedules, or
**pause** it to hold pending work while values still arrive. **Step** releases
+46 -8
View File
@@ -496,9 +496,9 @@ play MJPEG streams, pushed frames, images, and H.264 Baseline or MJPEG clips up
to 640×360. Re-encode other clips with the [clip recipe](../code/nodes.md#making-clips-playable-on-an-lvgl-panel).
The device decides what its [port](#the-devices-own-ports) names
mean. openpanel serves `gpio.<n>`, `backlight`, `touch`, `mic` and `speaker`,
lists each one's `config` in its README and ignores any other name. A browser
screen serves none of them. The [speech recipe](../code/nodes.md#speech-to-and-from-a-panel)
mean. openpanel [reports](#what-a-device-reports) the ones it serves, such as
`gpio.<n>`, `backlight`, `touch`, `mic` and `speaker`, with the settings each
one reads, and ignores any other name. A browser screen serves none of them. The [speech recipe](../code/nodes.md#speech-to-and-from-a-panel)
turns `mic` into text and text into `speaker`.
### The device's own ports
@@ -507,8 +507,8 @@ A device can also act as a node, with a button on its GPIO, its backlight or
its microphone. Its ports are wired in a flow with a **Device** node: pick the
device, then add each port under **Publishes** (`in`, the device sends to the
message) or **Follows** (`out`, the device reads it). **Use in flow** on the
device's page puts its node in a flow you pick, and **Ports** lists what the
published flows wire, each linked to its node.
device's page puts its node in a flow you pick, and **Capabilities** lists the
ports it reports and what the published flows wire, each linked to its node.
| Field | Meaning |
|---|---|
@@ -520,7 +520,8 @@ published flows wire, each linked to its node.
Only published flows count. A port wired in two of them, or a device node
naming a device that does not exist, is an error on the canvas. Until a device
reports what it has, a port name is free text; once it has, a port it does not
list is a warning and one wired the wrong way round an error.
list is a warning and one wired the wrong way round, or to another type, an
error. `int` and `float` pass for each other.
The device reads its ports as `io` on `GET /devices/{id}`, in a fixed order,
and is told to read them again when publishing or deleting a flow changes them.
@@ -528,8 +529,45 @@ The credential may publish to its `in` messages even when a dashboard is
locked, and may upload an artifact named after one
(`PUT /artifacts?name=<message>`), which is how a microphone sends a recording.
A value it publishes there is credited to its device node, so the node's edge
lights up on the canvas. `out` messages are read only. The node is shown down,
*Offline*, while the device is not connected.
lights up on the canvas. `out` messages are read only. The node is shown
degraded, *Offline*, in amber while the device is not connected: a warning on
Home, not a fault, since a panel switched off or asleep breaks nothing.
### What a device reports
A paired device says what it has with `PUT /devices/{id}/report`, from its own
credential only. openpanel sends it after pairing and once per boot. The report
replaces the one before, is at most 16 KB and keys this version does not know
are dropped:
```json
{
"schema": 1,
"platform": "esp32p4",
"board": "p4",
"firmware": "0.1.2",
"display": {"w": 1024, "h": 600, "shape": "rect", "touch": true},
"ports": [
{"port": "backlight", "direction": "out", "dtype": "int",
"config": {"type": "object", "properties": {"wake_s": {"type": "integer", "default": 30}}}},
{"port": "gpio.4", "direction": "in", "modes": ["in", "out"], "dtype": "bool"}
],
"camera": {"sensor": "IMX415", "w": 1920, "h": 1080, "fps": 15},
"features": {"display": true}
}
```
`config` is a JSON schema of the settings the device reads for that port, which
the Device node turns into a form. `modes` lists the directions a port may be
wired in when it may be either, as a GPIO pin. `on_demand` marks a port that
sends only while somebody wants it.
The device's page shows the report under **Capabilities**, with each port's
binding or **Use in flow** for it. A reported screen becomes a canvas preset in
the dashboard settings, named after the device, and the first report of a
touch screen turns **Touch friendly** on.
### Before device nodes
Before device nodes a device carried its ports itself. On its first start an
instance moves each one into the flow that declares its message, one device
+5 -3
View File
@@ -40,8 +40,9 @@ streaming toggle.
A **Device** node stands for a paired device instead: pick the device, then
its ports under **Publishes** and **Follows** (see
[the device's own ports](dashboards.md#the-devices-own-ports)). A device that
reports its ports offers them in a list with their type filled in, and the
command palette offers each one as a shortcut, such as *Hallway camera*.
reports its ports offers them in a list with their type filled in and a form
for the settings each one reads, and the command palette offers each one as a
shortcut, such as *Hallway camera*.
Message fields suggest existing names. Matching an output's message is how
you connect an input.
@@ -156,7 +157,8 @@ conflict. Reload and review their changes before retrying.
The canvas reports unconnected inputs, missing starting values, cycles and
code-load errors. These prevent the affected flow from running. Unauthenticated
webhooks are advisory; runtime node-health problems clear when the node
recovers. See [Flow issues](../concepts/flows.md#when-a-flow-needs-attention).
recovers. A degraded node, such as a device node whose device is offline, is
marked amber: it is a warning, and the flow keeps running. See [Flow issues](../concepts/flows.md#when-a-flow-needs-attention).
## See also
+2 -1
View File
@@ -57,7 +57,8 @@ a small sketch of each layout; open one for its live readings.
Health shows flow status, node load errors, queue depth and event-loop lag.
After you have used batch runs, it also shows running and queued runs.
A degraded instance lists the problems that need attention.
A degraded instance lists the problems that need attention. Warnings, such as
a device that is offline, are listed in amber and leave the status as it is.
### Execution time per node type
+3 -2
View File
@@ -400,9 +400,10 @@ never wake anything. Each port is named by the device, such as `gpio.4` or
| Setting | Default | Notes |
|---|---|---|
| `device` | | the device's id |
| `config` | `{}` | per port, a JSON object handed to the device unread |
| `config` | `{}` | per port, a JSON object handed to the device unread; a reported port's settings get a form |
The node is down, *Offline*, while the device is not connected.
The node is degraded, *Offline*, while the device is not connected: amber on
the canvas and a warning on Home, never a fault.
## Connectors