369 lines
18 KiB
Markdown
369 lines
18 KiB
Markdown
# Dashboards and panels
|
||
|
||
A **dashboard** is a grid of widgets bound to message names. It can show values
|
||
and send controls across several flows. A **panel** assigns published dashboards
|
||
to a paired screen, such as a wall tablet.
|
||
|
||
## Building one
|
||
|
||
1. Open **Dashboards → New dashboard**.
|
||
2. Enter edit mode, choose **Add widget** and select a type.
|
||
3. Bind the widget to a message in its settings. On desktop, drag and resize
|
||
tiles on the grid.
|
||
4. **Publish** when the dashboard is ready.
|
||
|
||
Edits stay in a draft; paired screens show the published version. On phones,
|
||
tiles stack in one column and dragging is disabled, but settings remain editable.
|
||
|
||
Controls can publish real values while you edit, so choose their targets with
|
||
care. Your new Switch may already be flipping something!
|
||
|
||
## The widgets
|
||
|
||
### Display
|
||
|
||
| Widget | Binds to | Shows |
|
||
|---|---|---|
|
||
| **Value** | anything | formatted reading, unit and precision |
|
||
| **Gauge** | `float`, `int` | reading within a min/max range |
|
||
| **Chart** | numeric messages, a query response or run metrics | up to five lines |
|
||
| **Bar** | `float`, `int` | up to eight readings with separate scales |
|
||
| **Icon** | numbers, booleans or strings | a glyph selected from the value |
|
||
| **Text** | — | Markdown |
|
||
| **Agenda** | `list` | upcoming items |
|
||
| **Forecast** | `list` | an outlook strip |
|
||
| **Notification** | `record` | title, body and severity |
|
||
| **Media** | `image`, `audio`, `video`, `artifact` | image, audio or video |
|
||
| **Player** | `record` | playback status and transport controls |
|
||
| **Clock** | — | current time |
|
||
| **Website** | — | embedded page or image |
|
||
|
||
Every widget has a title. **Show title** controls its visibility; the title
|
||
still names the widget for accessibility and published values.
|
||
|
||
### Input
|
||
|
||
| Widget | Publishes | Settings |
|
||
|---|---|---|
|
||
| **Button** | fixed value | one-shot action |
|
||
| **Buttons** | a value per button | grid of actions |
|
||
| **Switch** | `bool` | on/off |
|
||
| **Slider** | `float`, `int` | min, max, step; horizontal or vertical; a reading beside it |
|
||
| **Input** | text or number | free entry |
|
||
| **Selector** | selected value | menu or row of choices |
|
||
| **Colour** | list or string | HSV, RGB or hex |
|
||
|
||
Colour defaults to `[h, s, v]`: hue 0–360°, saturation and brightness 0–100%.
|
||
**Sends** can select RGB (`[r, g, b]`, 0–255) or hex (`"#rrggbb"`).
|
||
HSV and RGB bind to `list`; hex binds to `str`.
|
||
|
||
Controls publish to their bound messages. The flow canvas shows them as
|
||
endpoints feeding the nodes that consume those messages.
|
||
|
||
Bindings are checked in the editor and on the server. See
|
||
[Payload types](../reference/payload-types.md).
|
||
|
||
### A slider's reading
|
||
|
||
A Slider can also show what it sets against, such as the room's temperature
|
||
beside a setpoint: **Beside it** binds `reading: {message, dtype, label}` to a
|
||
`float` or `int`. The reading is only shown, never published. It is written in
|
||
the slider's unit and to its step's decimals (a step of 0.5 writes 21 as
|
||
`21.0`), as the slider writes its own value, so the two always match; `—`
|
||
stands in for the value until one arrives. On a round face it takes the middle
|
||
(see [Round dashboards](#round-dashboards)); in a tile it is a line under the
|
||
slider's value.
|
||
|
||
It is also marked where it sits on the slider's scale, as a thermostat marks
|
||
the room's temperature: the handle is what is set, the mark what is. The mark
|
||
is a bar across the track or the ring, in the text colour, at the reading's
|
||
fraction of the scale, (reading − min) / (max − min) held to 0–1, so a reading
|
||
outside the range sits at that end. It is drawn over the track and its fill
|
||
and under the handle, and is longer than the handle, so it still shows where
|
||
the two meet. It takes no pointer, and there is none until a value arrives.
|
||
|
||
Sizes are CSS pixels of the canvas, as in the round geometry below. The
|
||
slider's own value is at the tile's text size (14; 16 on a touch-friendly
|
||
panel), centred on a line as tall as the control (32; 48 touch-friendly).
|
||
|
||
| Part | Value |
|
||
|---|---|
|
||
| Line | 16 high, text 12: the label (muted, at most 96 wide, cut with an ellipsis), 6, the value (text colour), 6, the unit (muted). Without a label, the value and unit alone |
|
||
| Row | directly under the slider's value, right-aligned with it, in the column right of the track (12 apart). With ticks the line shares the ticks' row, so the tile grows no taller; without them it adds 16 |
|
||
| Row width | the value column is as wide as its wider line, but at most half the row; the track takes the rest. What does not fit is cut from the label; the value and unit never are |
|
||
| Fader | directly under the slider's value, both centred under the track, 8 below it; the line no wider than the tile inside its padding, cut from the label first |
|
||
| Mark (row) | a bar 2 wide and the handle's diameter + 8 long (16 + 8 = 24; 24 + 8 = 32 touch-friendly), centred on the track's midline (the track is 8 thick). Its centre sits on the handle's travel: handle ÷ 2 + fraction × (track length − handle) from the track's left end, so mark and handle line up at the same value |
|
||
| Mark (fader) | the same turned: 2 high, handle + 8 wide, centred on the track, measured up from its bottom end |
|
||
| Locked | the mark at 0.45 opacity, as the handle |
|
||
|
||
## Charts
|
||
|
||
Choose the chart's data source:
|
||
|
||
- **Live:** recent numeric message history, updated as values arrive.
|
||
- **Query:** publishes a `record` request containing `range_s` and
|
||
`interval_s`, then draws a flow's `series` response. Include those fields in
|
||
the answer so the chart can reject responses for a different window.
|
||
- **Runs:** compares a numeric metric from named runs, a sweep or the latest
|
||
runs of a flow. See [Runs](../concepts/runs.md).
|
||
|
||
Multi-line charts show a legend as well as different colours.
|
||
|
||
!!! tip "Skip the unfinished bucket"
|
||
|
||
If your query aggregates into time buckets, leave out the bucket still
|
||
filling. Otherwise an incomplete total can look like a sudden drop.
|
||
|
||
## Media tiles
|
||
|
||
Media messages contain artifact references; the widget loads the bytes they
|
||
name. **Crop or fit** controls image sizing. **Play as it arrives** enables
|
||
autoplay, subject to the browser's rules for sound.
|
||
|
||
For local streams, `volatile=True` artifacts can be pushed over the websocket
|
||
with their message. Stored artifacts are fetched individually, as are frames
|
||
viewed through a portal. Use occasional snapshots for remote screens.
|
||
|
||
For continuous video, set **Live stream** to a browser-compatible camera URL.
|
||
The browser must be able to reach that URL; enrollment does not expose the
|
||
camera itself. See [Media payloads](../reference/payload-types.md#image-audio-video).
|
||
|
||
## Player tiles
|
||
|
||
A Player reads a status `record` and publishes commands to a separate `str`
|
||
message.
|
||
|
||
| Status field | Meaning |
|
||
|---|---|
|
||
| `title`, `artist`, `album` | track details |
|
||
| `status` | `play`, `pause`, `stop`, `load` or `off` |
|
||
| `position`, `duration` | seconds |
|
||
|
||
Controls send `toggle`, `next`, `prev` and `seek:<seconds>`. Your receiving
|
||
node translates those into device actions. The position advances in the
|
||
browser between readings and resets to each fresh reading.
|
||
|
||
## Website tiles
|
||
|
||
Set **Address** to a page to embed, or choose **Image** for a snapshot or MJPEG
|
||
URL. A website may refuse embedding.
|
||
|
||
Embedded pages run in a sandbox without cookies or storage by default.
|
||
**Trusted site** permits the site's own origin, cookies and storage.
|
||
Use it only for a trusted destination. The app blocks trusted embeds of its
|
||
own origin, including the portal origin, to protect your session.
|
||
|
||
## Dashboard settings
|
||
|
||
Open **Dashboard settings** from the dock. Each setting can hold a fixed value
|
||
or follow a message chosen in **Driven by**. The fixed value is the fallback
|
||
when no usable message value is available.
|
||
|
||
| Setting | Choices | Message type |
|
||
|---|---|---|
|
||
| **Look** | `fluksio`, `material`, `glass` | `str` |
|
||
| **Theme** | `system`, `light`, `dark` | `str` |
|
||
| **Palette** | ordered hex colours | `list` |
|
||
| **Background** | image URL | `str` |
|
||
| **Lock** | read-only on/off | `bool` |
|
||
|
||
To change one on a schedule, have a scheduled node publish its bound message.
|
||
|
||
### Look and theme
|
||
|
||
**Fluksio** is the default look. **Material** uses flat tonal cards; **Glass**
|
||
uses translucent tiles. Widgets work the same in all three.
|
||
|
||
Theme follows the chosen preference unless a palette sets the background
|
||
colour.
|
||
|
||
### Palette
|
||
|
||
Paste a colour list or a [Coolors](https://coolors.co) palette link. Colours
|
||
have roles by position:
|
||
|
||
| Position | Role |
|
||
|---|---|
|
||
| 1 | background |
|
||
| 2 | widget surface |
|
||
| 3 | primary controls and first chart line |
|
||
| 4 | accent |
|
||
| 5 | text |
|
||
| 6+ | additional chart lines |
|
||
|
||
Missing roles are derived, so three colours are enough to get started.
|
||
**Rotate** shifts their order. Text contrast is corrected automatically;
|
||
failure and success indicators keep their status colours.
|
||
|
||
### Background and lock
|
||
|
||
Background draws an image beneath the widgets. Lock disables publishing from
|
||
controls and shows **Read-only** when viewing the dashboard. It does not
|
||
restrict API permissions and does not disable controls in edit mode.
|
||
|
||
## Round dashboards
|
||
|
||
For a round screen such as a rotary knob, set the canvas to **Round** in the
|
||
dashboard settings, or pick the **Knob** preset (360×360). A round canvas is
|
||
square (`shape: "round"` forces `canvas_height` to `canvas_width`), and nothing
|
||
outside its circle is drawn.
|
||
|
||
A round dashboard has no grid. It shows one widget at a time, each drawn as the
|
||
whole face, in the order of `widgets`; `layout` is not read. In the editor, the
|
||
faces are a row above the dock: press one to show it and open its settings,
|
||
drag it along the row (or Alt+←/→) to move it, and add more with **Add widget**.
|
||
|
||
A Slider, Selector, Colour or Gauge draws on the **ring** along the edge, which
|
||
a knob's own ring turns. In a browser, drag along the ring or use the mouse
|
||
wheel (or the arrow keys once it has focus): one notch is one step, one option
|
||
(no wrap) or one hue step (`hue_step`, default 5°, wrapping). The value moves
|
||
at once and is published once, 300 ms after the last notch or when the pointer
|
||
is released. A Gauge only shows. On any other face, turning a knob only wakes
|
||
the screen.
|
||
|
||
A Slider's reading (**Beside it**) is written large in the middle, under its
|
||
label; the slider's own title and value move to the line in the ring's gap.
|
||
|
||
Value, Switch, Button, Clock, Icon, Notification and Text have faces of their
|
||
own. Every other type is drawn as its rectangular body in the middle of the
|
||
face, and the editor marks it **Poor fit for round**.
|
||
|
||
### Moving between faces
|
||
|
||
| Gesture | Effect |
|
||
|---|---|
|
||
| Swipe left / right | next / previous face of this dashboard; stops at either end |
|
||
| Swipe up / down | next / previous dashboard of the panel (`/panel/{id}` only); stops at either end |
|
||
| Page dot | that face |
|
||
| PageDown / PageUp | next / previous face |
|
||
| Shift+PageDown / Shift+PageUp | next / previous dashboard of the panel |
|
||
| Arrow keys | turn the ring, not the page |
|
||
|
||
A swipe is a drag of at least 40 canvas pixels, decided by its longer axis; a
|
||
drag that starts on the ring turns the ring instead. A dashboard reopens on the
|
||
face it was last left on in this session, otherwise on its first. Landing on
|
||
another dashboard shows its title and icon in the middle of the face for one
|
||
second; a round panel has no rail.
|
||
|
||
### Geometry
|
||
|
||
Devices that draw dashboards natively use these numbers. They are canvas
|
||
pixels, scaled with the canvas; S is the canvas width and c = S/2. Angles are
|
||
degrees clockwise from three o'clock. Text sizes are font sizes; muted is the
|
||
look's muted text colour.
|
||
|
||
| Part | Value |
|
||
|---|---|
|
||
| Face | circle of diameter S at (c, c) |
|
||
| Title | one line centred at y = c − 80, 15, muted, at most 200 wide; the widget's title, or the label of a slider's reading |
|
||
| Middle | a 240 × 128 box centred at (c, c); what a face draws in its middle is centred in it and clipped to it |
|
||
| Centre reading | centred at (c, c), line height 1, its unit after it at 0.4 em, muted. Size: 72, or 240 ÷ (0.6 × the characters written) rounded down where that is smaller, never below 24 |
|
||
| Ring band | 40 along the edge |
|
||
| Ring arc | start 120°, sweep 300° (ends at 60°), centre-line radius c − 20, stroke 16 with round caps |
|
||
| Slider | track over the sweep, value arc from the start to the value (none at the minimum), handle 28 across; the value as the centre reading |
|
||
| Slider with a reading | the reading as the centre reading; the gap line holds the title (muted) and the slider's value. The reading's mark: a bar 36 long and 3 wide, square-ended, in the text colour, along the radius at the reading's fraction of the sweep (the same angle the handle has at that value), centred on the ring's centre line, so from radius c − 38 to c − 2; over the track and value arc, under the handle |
|
||
| Selector | a 6 mark per option at i/(n−1) of the sweep (one option: the middle), the selected one under the handle; its label as the centre reading, at most 36 |
|
||
| Colour | hue h at h/360 of the sweep in 150 strokes of 2°, red at both ends; the handle filled with the colour; a swatch 96 across in the middle, with a hairline border |
|
||
| Gauge | track and value arc, no handle; the value as the centre reading |
|
||
| Gap line | centred at y = S − 28, 13, at most 160 wide |
|
||
| Value | the reading as the centre reading, to its precision; a word shrinks like any other |
|
||
| Switch | a disc 128 across centred at (c, c): the look's button, filled when on and tonal when off, with a power glyph 40 over the word On or Off at 15; a press toggles |
|
||
| Button | a disc 128 across centred at (c, c), tonal, its label at 15 over at most three lines; a press sends its value |
|
||
| Clock | the time (hours and minutes, the device's locale) sized as a centre reading, over the date (weekday, day, month) at 15, muted, 4 apart; the pair centred at (c, c) |
|
||
| Icon | the glyph 96 across over its label at 15, muted, 4 apart; the pair centred at (c, c). Nothing matched: a dash at 72, muted |
|
||
| Notification | the title at 15, medium (an error in the destructive colour, led by ⚠), over the body at 15, muted; centred lines in the middle |
|
||
| Text | centred lines at 15: `#` at 1.35 em and `##` medium, `###` muted, list items led by a bullet; in the middle |
|
||
| Not wired | the problem, at 15, muted, in the middle; a ring type draws no ring |
|
||
| Any other type | its rectangular body, without a card, from y = c − 64 to c + S/(2√2), x = c ± S/(2√2) (Knob: 53–307 × 116–307) |
|
||
| Page dots | one per face, from two faces: dots 6 across on targets 16 apart (closer when more than 128 wide would be needed), centred at y = S − 12; the current one in the text colour, the others muted at half opacity |
|
||
| Dashboard name | a pill centred at (c, c), 36 high: the icon 16 and the title at 15, medium, at most 220 wide, on the frosted surface; for 1000 ms |
|
||
| Paging | a new face slides in 48 from the side it was paged towards and fades in; the old one leaves the other way |
|
||
| Locked | controls do nothing and read as disabled; the ring at 0.45 opacity |
|
||
|
||
Paint follows the look: Fluksio draws a muted track, a primary value and a
|
||
handle of the ground colour ringed in primary; Material a tonal track and a
|
||
primary handle; Glass a translucent track, a glowing value and a white handle.
|
||
A Switch or Button face wears the look's own button.
|
||
|
||
## Showing one
|
||
|
||
Open `/view/{name}` for a dashboard-only view with your normal session.
|
||
For a dedicated screen, pair a panel.
|
||
|
||
## Panels: hanging a screen on a wall
|
||
|
||
As a superuser:
|
||
|
||
1. Open **Dashboards → Panels**, add a panel and select its dashboards.
|
||
2. Enable **Touch friendly** for larger controls.
|
||
3. Open the displayed pairing link on the device. It shows a six-character code.
|
||
4. Enter that code for the panel, check the device description, then press
|
||
**Pair device**.
|
||
|
||
The screen collects its credential and shows the assigned published dashboards.
|
||
Several dashboards get a navigation rail.
|
||
|
||
A panel credential permits its assigned dashboards, their bound messages and
|
||
the artifacts they show. It also permits publishing from its controls and query
|
||
charts. On a locked dashboard only the query charts may still publish.
|
||
|
||
**Unpair screen** revokes a locally paired credential and keeps the panel's
|
||
dashboard assignments. Deleting the panel removes the assignment and its
|
||
access.
|
||
|
||
### A screen without a browser
|
||
|
||
The openpanel firmware, kept in its own repository, draws dashboards as native
|
||
widgets on an ESP32-P4 wall panel (Waveshare ESP32-P4-WIFI6-Touch-LCD-7B,
|
||
1024×600). Once its config page points it at this instance, it shows the
|
||
pairing code itself; enter it as in step 4. It reads the same published
|
||
dashboards and live values and draws every widget type with two exceptions,
|
||
both shown as a placeholder: a website tile that embeds a page (image tiles
|
||
work), and a chart of runs, which a panel 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 the panel's [I/O](#the-devices-own-io) port 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 device's own I/O
|
||
|
||
A panel can also act as a node. Under **I/O**, wire a port on the device to a
|
||
message:
|
||
|
||
| 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 flow must declare it, as for a control |
|
||
| `config` | A JSON object passed to the device unread, such as `{"pull": "up", "debounce_ms": 50}` |
|
||
|
||
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.
|
||
|
||
!!! note "If the local link is wrong"
|
||
|
||
Pairing links use `FRONTEND_HOST`. Set it to the app address reachable
|
||
from the screen's network.
|
||
|
||
### A screen somewhere you cannot reach
|
||
|
||
An [enrolled instance](portal.md) also shows a portal pairing link. Use that
|
||
link on the remote screen, then approve its code in the same dialog. The
|
||
device description says *via portal*.
|
||
|
||
The instance enforces the same dashboard and message limits. Remote
|
||
credentials are issued by the portal, so **Unpair screen** does not revoke
|
||
them; revoke them at the portal. Deleting the panel removes access on the
|
||
instance.
|
||
|
||
## See also
|
||
|
||
- [Payload types](../reference/payload-types.md)
|
||
- [Flows, nodes and messages](../concepts/flows.md)
|
||
- [Accounts and the portal](portal.md)
|