Device node: a device's ports are wired in flows
- flow/nodes/device.py: `device` node with params `device` and `config`;
provides the device's `in` ports, requires its `out` ports without
waking anything, never run (Node.passive), down "Offline" from presence
- DeviceDef.io and DevicePort go; GET /devices/{id} computes `io` and
`bindings` from the device nodes of published flows, in a fixed order,
cached by FlowStore.revision; the credential's scope comes from them
- issues `unknown_device` and `device_port` (wired twice; against a report
the wrong direction or dtype, or unlisted as advisory), rechecked when a
device is created or deleted; ValidationIssue.advisory is a field
- a device publishing on a bound port is credited to its node by the
server; the panel endpoint kind is gone from `_endpoints` and the canvas
- a rebuild that moves a device's bindings sends `dashboard_changed` with
an empty name, which the firmware refetches on
- startup moves each device's `io` rows into device nodes in the flows
declaring their messages, draft and published, one commit; rows no
flow declares are logged and left out
- frontend: Device node settings (device, Publishes, Follows, a port
picker from a report), palette shortcuts per reported port, DeviceDetail
lists bindings with "Use in flow" in place of the I/O editor
- docs: devices, flow editor, node types, issues, API
This commit is contained in:
+1
-1
@@ -191,7 +191,7 @@ on its own, so this is for reclaiming space sooner.
|
||||
| `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` |
|
||||
| `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`, `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 |
|
||||
| `GET` | `/devices/pending` | devices waiting for a code, as they describe themselves |
|
||||
|
||||
+2
-2
@@ -256,8 +256,8 @@ To wire the loop in a flow `home`:
|
||||
|
||||
1. Declare a [flow input](../concepts/flows.md#flow-inputs) `heard` of type
|
||||
`audio` with origin `panel`.
|
||||
2. Under the panel's [I/O](../interface/dashboards.md#the-devices-own-io), wire
|
||||
`mic` as `in` to `home.heard` and `speaker` as `out` to `home.reply`.
|
||||
2. Add a [Device node](../interface/dashboards.md#the-devices-own-ports) for
|
||||
the panel, publishing `mic` to `heard` and following `reply` on `speaker`.
|
||||
3. Bind the listening node's `audio` to `heard` and its `text` to `said`.
|
||||
4. Have your own nodes read `said` and publish an answer as a `str`, such as
|
||||
`answer`.
|
||||
|
||||
@@ -138,7 +138,9 @@ The canvas reports issues on the affected nodes:
|
||||
| `node_error` | code or configuration could not load |
|
||||
| `unauthenticated_hook` | a webhook or served websocket has no shared secret; advisory |
|
||||
| `ignored_device` | a node other than a Function node names a device, which nothing reads; advisory |
|
||||
| `node_unhealthy` | a loaded node reports a runtime problem, such as a lost connection |
|
||||
| `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 |
|
||||
|
||||
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
|
||||
|
||||
@@ -468,9 +468,9 @@ device first, the save is refused and the page shows their version.
|
||||
|
||||
A device credential reads its assigned dashboards, their bound messages and the
|
||||
artifacts they show. It writes only the targets of their controls and query
|
||||
charts, and the `in` ports of its own [I/O](#the-devices-own-io). A message its
|
||||
tiles only draw, or a dashboard setting is bound to, stays read only. On a
|
||||
locked dashboard only the query charts may still publish.
|
||||
charts, and the messages of its own `in` [ports](#the-devices-own-ports). A
|
||||
message its tiles only draw, or a dashboard setting is bound to, stays read
|
||||
only. On a locked dashboard only the query charts may still publish.
|
||||
|
||||
**Unpair screen** revokes a locally paired credential and keeps the device and
|
||||
its dashboards. Deleting the device removes the assignment and its access, and
|
||||
@@ -495,30 +495,47 @@ work), and a chart of runs, which a device credential may not read. Media tiles
|
||||
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 [I/O](#the-devices-own-io) port names
|
||||
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)
|
||||
turns `mic` into text and text into `speaker`.
|
||||
|
||||
### The device's own I/O
|
||||
### The device's own ports
|
||||
|
||||
A device can also act as a node. Under **I/O**, wire a port on the device to a
|
||||
message. A new port saves once it has a port name and a message:
|
||||
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.
|
||||
|
||||
| Field | Meaning |
|
||||
|---|---|
|
||||
| `port` | The device's name for it, such as `gpio.4`, `backlight`, `touch`, `mic` or `speaker`. Lowercase letters, digits, `_` and `.` |
|
||||
| `direction` | `in`: the device publishes to the message. `out`: the device follows it |
|
||||
| `message` | A qualified `flow.name`, a lowercase flow name, a dot, then letters, digits and underscores. A flow must declare it, as for a control |
|
||||
| `config` | A JSON object passed to the device unread, such as `{"pull": "up", "debounce_ms": 50}` |
|
||||
| port | The device's name for it, such as `gpio.4`, `backlight`, `touch`, `mic` or `speaker`. Lowercase letters, digits, `_` and `.` |
|
||||
| message | The message the port publishes or follows, named like any port's |
|
||||
| type | The payload type, checked like any message's |
|
||||
| config | A JSON object passed to the device unread, such as `{"pull": "up", "debounce_ms": 50}` |
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
`out` messages are read only. The flow canvas draws each port as an endpoint,
|
||||
an `in` port feeding the nodes that consume its message and an `out` port fed
|
||||
by the nodes that produce it.
|
||||
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.
|
||||
|
||||
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
|
||||
node per device and flow, in the published flow and its draft. A port whose
|
||||
message no flow declares is left out and named in the startup log, so wire it
|
||||
again by hand.
|
||||
|
||||
!!! note "If the local link is wrong"
|
||||
|
||||
|
||||
@@ -13,14 +13,14 @@ below, so a large flow grows downwards in one column.
|
||||
- **Edges** are routed around the nodes, a loop back to an earlier node
|
||||
included, and pulse when values pass through. Click one to inspect its
|
||||
payload.
|
||||
- **Endpoints** show flow inputs, dashboard widgets, device I/O ports and nodes
|
||||
in other flows that exchange messages with this flow. A value passing lights
|
||||
- **Endpoints** show flow inputs, dashboard widgets and nodes in other flows
|
||||
that exchange messages with this flow. A value passing lights
|
||||
one up in a sweep: left to right when it sends into the flow, right to left
|
||||
when it reads out of it.
|
||||
- **Clicking an endpoint** shows a flow input's type, starting value and
|
||||
current value, with a pencil that opens the flow settings to edit it. A device
|
||||
port opens the device in the Devices tab. A node in another flow opens that
|
||||
flow on the label leading back here, with its edges highlighted.
|
||||
current value, with a pencil that opens the flow settings to edit it. A node
|
||||
in another flow opens that flow on the label leading back here, with its
|
||||
edges highlighted.
|
||||
|
||||
Values update over a websocket. A connection banner tells you when the editor
|
||||
cannot reach the engine.
|
||||
@@ -37,6 +37,12 @@ and open its settings.
|
||||
payload type, bound message and delivery interval. Outputs also have a
|
||||
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*.
|
||||
|
||||
Message fields suggest existing names. Matching an output's message is how
|
||||
you connect an input.
|
||||
|
||||
|
||||
@@ -386,6 +386,24 @@ They are reported but cannot receive writes unless wired.
|
||||
|
||||
The chat's [Control tab](../code/agents.md#control) sends commands by hand.
|
||||
|
||||
## Devices
|
||||
|
||||
### Device
|
||||
|
||||
**`device`** — a paired device's own ports: what it publishes, what it follows.
|
||||
|
||||
The engine never runs it. Its outputs are the device's `in` ports, published by
|
||||
the device itself, and its inputs the `out` ports the device follows, which
|
||||
never wake anything. Each port is named by the device, such as `gpio.4` or
|
||||
`backlight`. See [the device's own ports](../interface/dashboards.md#the-devices-own-ports).
|
||||
|
||||
| Setting | Default | Notes |
|
||||
|---|---|---|
|
||||
| `device` | | the device's id |
|
||||
| `config` | `{}` | per port, a JSON object handed to the device unread |
|
||||
|
||||
The node is down, *Offline*, while the device is not connected.
|
||||
|
||||
## Connectors
|
||||
|
||||
Installed connector packages add types through the `fluksio.node_types`
|
||||
|
||||
Reference in New Issue
Block a user