Publish the documentation site: docs.fluksio.com
A zensical site under docs/, served by a new `docs` compose service behind Traefik, built with --strict in CI. Same pattern the sibling n3xd workspace uses. Getting started splits the way the landing page does — one path is `pip install fluksio` and a training script, the other is a Docker stack and an afternoon in the browser — because the two audiences will not spend the same amount of time. Everything after that is shared: the concepts, the web interface (app and portal), the CLI and the API, and a reference for node types, payload types and configuration. The three flow guides move here from the docs submodule rather than being copied, so there is one version of them. Styling mirrors DESIGN-GUIDELINES.md: the app's token palette remapped onto Material's variables in both schemes, Inter, the 16px panel radius, and the one terracotta accent spent on the facility lane of the audience split. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M7Xv3cJEW5c8AXxn2hoojV
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
# Dashboards and panels
|
||||
|
||||
A dashboard is a grid of widgets bound to message names — the same names that
|
||||
wire the graph. It is its own document rather than a set of nodes, so it reads
|
||||
across flows without belonging to any of them, and a flow stays the logic it
|
||||
was.
|
||||
|
||||
There is no second tool and no separate deployment. You build the panel next to
|
||||
the thing feeding it.
|
||||
|
||||
## Building one
|
||||
|
||||
**Dashboards → New dashboard**, then drag widgets onto the grid from the widget
|
||||
picker and bind each to a message.
|
||||
|
||||
Like flows, dashboards are drafts until you publish. A wall panel reads only
|
||||
the published version, so a half-arranged page never reaches the wall.
|
||||
|
||||
On a phone the dashboard is read, not arranged: the grid stacks to one column
|
||||
and dragging is off. Picking a widget and editing its settings still works.
|
||||
|
||||
## The widgets
|
||||
|
||||
### Display
|
||||
|
||||
| Widget | Binds to | Notes |
|
||||
|---|---|---|
|
||||
| **Value** | anything | a formatted reading with a unit and a precision |
|
||||
| **Gauge** | `float`, `int` | min, max, unit |
|
||||
| **Chart** | `float`, `int` | up to five series; see *Two kinds of chart* below |
|
||||
| **Bar** | `float`, `int` | a reading, optionally nesting up to three inside it |
|
||||
| **Icon** | numbers, booleans, weather strings | maps a value onto a glyph |
|
||||
| **Text** | — | markdown you write; a label, a note, an instruction |
|
||||
| **Agenda** | `list` | upcoming items, e.g. from a calendar connector |
|
||||
| **Forecast** | `list` | a short outlook strip |
|
||||
| **Notification** | `record` | title, body and severity — what an alert channel writes |
|
||||
| **Clock** | — | the time, in a size a wall can read |
|
||||
|
||||
### Input
|
||||
|
||||
| Widget | Publishes | Notes |
|
||||
|---|---|---|
|
||||
| **Button** | a fixed value | one-shot: run it, open it, reset it |
|
||||
| **Switch** | `bool` | on/off |
|
||||
| **Slider** | `float`, `int` | min, max, step |
|
||||
| **Input** | text or a number | free entry |
|
||||
| **Dropdown** | one of a list | a mode, a scene, a preset |
|
||||
|
||||
A control publishes the message it is bound to, exactly as a node would. On the
|
||||
flow canvas it is drawn as a labelled endpoint feeding the nodes that read it,
|
||||
so nobody has to guess where the value came from.
|
||||
|
||||
### Typed bindings
|
||||
|
||||
Widgets are type-checked against the message the same way ports are: a switch
|
||||
takes a `bool`, a gauge takes a number, an agenda takes a `list`, a
|
||||
notification takes a `record`. Bind one wrong and the editor says so rather
|
||||
than drawing nothing. The same table is enforced on the server.
|
||||
|
||||
Charts are the exception worth knowing: because identity across five series is
|
||||
carried by lightness alone, a chart with more than one series always draws a
|
||||
legend.
|
||||
|
||||
## Two kinds of chart
|
||||
|
||||
**Live.** Bind up to five messages and the chart draws the engine's own ring
|
||||
buffer of recent values, extended over the socket as new ones land. Nothing
|
||||
else is involved. This is what you want for "the last few hours of the living
|
||||
room".
|
||||
|
||||
**Querying.** The chart publishes a *request* — the window and resolution it
|
||||
wants — exactly as a slider publishes a value, and draws the `series` some flow
|
||||
answers with. What serves that request is the flow's business: typically a
|
||||
small Python node that builds a Flux query, an InfluxDB node that runs it, and
|
||||
another Python node that shapes the rows.
|
||||
|
||||
That indirection is the point. The widget never learns which database answered
|
||||
it, so swapping the store is a change to one flow and nothing else. The answer
|
||||
also states what it was computed for, so an answer to a different question is
|
||||
ignored rather than two charts overwriting each other's picture.
|
||||
|
||||
## Showing one
|
||||
|
||||
- `/view/{name}` — a browser tab pointed at one dashboard. Needs an ordinary
|
||||
session.
|
||||
- **Panels** — a named device that pairs instead of logging in. Below.
|
||||
|
||||
## Panels: hanging a screen on a wall
|
||||
|
||||
A wall tablet has no keyboard, so it pairs.
|
||||
|
||||
1. **Dashboards → Panels**, add a panel named after where it hangs, and tick
|
||||
the dashboards it shows. More than one and the screen draws a rail to switch
|
||||
between them.
|
||||
2. Point the device's browser at the link the dialog shows. The device then
|
||||
displays a six-character code.
|
||||
3. Type that code into the same panel's **Pair device** field. The line under
|
||||
it names what is holding the code — check it is the screen you just hung,
|
||||
because approving adopts whatever answered. The screen picks the credential
|
||||
up within a few seconds and never asks again.
|
||||
|
||||
What the screen holds is not a login. It reaches that panel's published
|
||||
dashboards and the message endpoints its widgets speak, and nothing else.
|
||||
|
||||
It cannot be made strictly read-only, and that is honest rather than an
|
||||
oversight: a querying chart publishes its request, and a control on a panel is
|
||||
the reason you put one there. Deleting the panel revokes the credential, which
|
||||
is also how you retire a device — the screen falls back to asking for a new
|
||||
code.
|
||||
|
||||
!!! note "If the link is wrong"
|
||||
|
||||
The pairing link is built from the installation's `FRONTEND_HOST`. If that
|
||||
is not the address devices on your network actually reach, fix the setting
|
||||
rather than the link: it is the same one password-reset mails and the OAuth
|
||||
metadata are built from.
|
||||
|
||||
### A screen somewhere you cannot reach
|
||||
|
||||
Another building, someone else's network, no route in. An installation
|
||||
[enrolled with a portal](portal.md) shows a second link,
|
||||
`https://hub.${DOMAIN}/i/{installation-id}/panel`, and the same three steps
|
||||
work through it: the portal serves that one page without a session, forwards
|
||||
the pairing calls down the tunnel, and mints the credential when you approve
|
||||
the code. The pairing line then reads *via portal*.
|
||||
|
||||
The portal names the panel and nothing else. What the panel may read is decided
|
||||
on the installation, on every call, by the same check a locally paired screen
|
||||
passes. Two differences: it acts as the account the installation was enrolled
|
||||
with rather than as whoever approved it, and deleting the panel stops it here
|
||||
immediately while the portal's copy of the token expires on its own.
|
||||
|
||||
## See also
|
||||
|
||||
- [Payload types](../reference/payload-types.md) — what a widget can bind to
|
||||
- [Flows, nodes and messages](../concepts/flows.md) — where the names come from
|
||||
- [Accounts and the portal](portal.md) — reaching all of this from outside
|
||||
Reference in New Issue
Block a user