Migrate to new Gitea instance (gitea.wbd-rd.nl)

- Update all submodule URLs from gitea.centraal.wbd-rd.nl to gitea.wbd-rd.nl
- Add settler as proper submodule in .gitmodules
- Add agent skills, function anchors, decisions, and improvements
- Add Docker configuration and scripts
- Add manuals and third_party docs
- Update .gitignore with secrets and build artifacts
- Remove stale .tgz build artifact

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
znetsixe
2026-03-04 21:07:04 +01:00
parent fbd9e6ec11
commit 6a6c04d34b
169 changed files with 21332 additions and 1512 deletions

30
manuals/node-red/INDEX.md Normal file
View File

@@ -0,0 +1,30 @@
# Node-RED Manual Index
This folder summarizes official Node-RED docs that are relevant to EVOLV node development.
## Official Sources
- Creating Nodes: JavaScript file and message handling
https://nodered.org/docs/creating-nodes/node-js
- Creating Nodes: Edit dialog and node definition in `.html`
https://nodered.org/docs/creating-nodes/edit-dialog
- Working with messages
https://nodered.org/docs/user-guide/messages
- Writing Functions (return arrays, multiple outputs, async send/done)
https://nodered.org/docs/user-guide/writing-functions
## What To Check First (EVOLV)
1. Runtime routing in `src/nodeClass.js`: use explicit output arrays for multi-output nodes.
2. Input handlers: use `send` + `done` pattern from Node-RED runtime docs.
3. Function nodes in example flows: return arrays with output-position alignment.
4. Editor/runtime parity: properties in `RED.nodes.registerType(...defaults...)` must map to runtime config parsing.
5. For FlowFuse dashboard reference, see:
- `flowfuse-widgets-catalog.md` — master index of all 22 widget types
- `flowfuse-ui-chart-manual.md` — chart widget (line, bar, scatter, pie, histogram)
- `flowfuse-ui-gauge-manual.md` — gauge widget (tile, battery, tank, half, 3/4 arc)
- `flowfuse-ui-text-manual.md` — text display widget
- `flowfuse-ui-template-manual.md` — custom Vue/Vuetify template widget
- `flowfuse-ui-button-manual.md` — button widget
- `flowfuse-ui-config-manual.md` — config nodes (ui-base, ui-page, ui-group, ui-theme)
- `flowfuse-dashboard-layout-manual.md` — layout patterns and sizing rules

View File

@@ -0,0 +1,47 @@
# FlowFuse Dashboard Layout Notes (EVOLV Reference)
Primary sources:
- https://dashboard.flowfuse.com/
- https://dashboard.flowfuse.com/nodes/widgets/ui-chart.html
## Compact Screen Guidelines
- Use a 12-column page grid and place charts in 4-column blocks for 3-up rows.
- Disable legends for single-series charts to reduce visual noise.
- Prefer concise text widgets under charts for state/timing/snapshot summaries.
- Use compact theme spacing:
- lower page padding
- lower group gap
- lower widget gap
## Time Window Guidelines
- For live demos, default chart history to 10-15 minutes.
- Keep axis labels short and unit-specific.
- Use one chart per KPI unless comparison is intentionally needed.
## Message Hygiene For Widgets
- Chart widgets: send minimal `{ topic, payload, timestamp }`.
- Text widgets: send plain string in `msg.payload`.
- Separate chart and text outputs by Function-node output index.
## Gauge Sizing in Groups
- **Tank gauge**: `width: 2, height: 4` — tall vertical fill indicator.
- **3/4 arc gauge**: `width: 2, height: 3` — fits beside tank in same group row.
- **Status text**: `width: 4, height: 1` — full group width, below gauges.
- In a 4-column group, two `width: 2` gauges sit side by side, text below spans full width.
- Set `order` on widgets: tank=2, arc=3, text=1 (text first = top, gauges below; or tank=1, arc=2, text=3 for gauges on top).
## Group Height Auto-sizing
- Set `height: "1"` on groups to auto-grow with content.
- A group with a `height: 4` tank + `height: 1` text will auto-expand to ~5 rows.
## References
- FlowFuse Dashboard docs root: https://dashboard.flowfuse.com/
- FlowFuse `ui-chart` docs: https://dashboard.flowfuse.com/nodes/widgets/ui-chart.html
- FlowFuse Widget catalog: [flowfuse-widgets-catalog.md](flowfuse-widgets-catalog.md)
- FlowFuse Config nodes: [flowfuse-ui-config-manual.md](flowfuse-ui-config-manual.md)

View File

@@ -0,0 +1,62 @@
# FlowFuse `ui-button` Manual (EVOLV Reference)
Source: https://dashboard.flowfuse.com/nodes/widgets/ui-button.html
## Purpose
Clickable button that sends a message on user interaction.
## Properties
| Property | Type | Dynamic | Notes |
|----------|------|---------|-------|
| `group` | ref | No | Parent ui-group |
| `width` | int | No | Columns |
| `height` | int | No | Row units |
| `label` | string | Yes | Button text |
| `icon` | string | Yes | Material Design icon name (no `mdi-` prefix) |
| `iconPosition` | string | Yes | `"left"` or `"right"` |
| `buttonColor` | string | Yes | Background color |
| `textColor` | string | Yes | Label color (auto-calculated if omitted) |
| `iconColor` | string | Yes | Icon color (matches text if omitted) |
| `tooltip` | string | No | Hover tooltip |
| `order` | int | No | Position in group |
| `emulateClick` | bool | No | Trigger click on any received msg |
## Events
| Event | Enabled By | Output |
|-------|-----------|--------|
| Click | `onclick: true` | `msg.payload` = configured value |
| Pointer Down | `onpointerdown: true` | `msg.payload` + `msg._event` with timestamp |
| Pointer Up | `onpointerup: true` | `msg.payload` + `msg._event` with timestamp |
Hold duration = `pointerup._event.timestamp - pointerdown._event.timestamp`.
## Input
`msg.payload` — sets button payload value. With `emulateClick: true`, any input msg triggers a click.
`msg.enabled``true` / `false` to enable/disable the button.
## Dynamic Properties (`msg.ui_update`)
```js
msg.ui_update = {
label: "Stop",
icon: "stop",
iconPosition: "left",
buttonColor: "#f44336",
textColor: "#ffffff",
iconColor: "#ffffff",
class: "my-btn-class"
};
```
## EVOLV Key Rules
1. Use buttons for operator actions (start/stop pump, acknowledge alarm).
2. Set `emulateClick: false` (default) — don't auto-trigger on incoming messages.
3. For toggle buttons, update label/color dynamically via `msg.ui_update` from downstream logic.
4. Pair with confirmation dialog (ui-notification or ui-template) for destructive actions.
5. Standard sizing: `width: 2, height: 1` for inline; `width: 4, height: 1` for full-width in 4-col group.

View File

@@ -0,0 +1,121 @@
# FlowFuse `ui-chart` Manual (EVOLV Reference)
Source: https://dashboard.flowfuse.com/nodes/widgets/ui-chart.html
## Chart Types
| Type | X-Axis Options | Notes |
|------|---------------|-------|
| Line | timescale, linear, categorical | Interpolation: linear, step, bezier, cubic, cubic-mono |
| Bar | categorical | Grouped (side-by-side) or stacked |
| Scatter | timescale, linear, categorical | Configurable point shape & radius |
| Pie / Doughnut | radial | Nested series for multi-layer |
| Histogram | auto-binned | Numerical bins or categorical counting |
## Properties
| Property | Type | Dynamic | Notes |
|----------|------|---------|-------|
| `group` | ref | No | Parent ui-group |
| `width` | int | No | Columns |
| `height` | int | No | Row units |
| `label` | string | No | Chart title |
| `chartType` | string | No | `"line"`, `"bar"`, `"scatter"`, `"pie"`, `"histogram"` |
| `showLegend` | bool | No | Show series legend |
| `action` | string | Yes | `"append"` or `"replace"` |
| `pointShape` | string | No | Point marker shape (scatter/line) |
| `xAxisLimit` | int | No | Max data points to keep |
| `textColor` | color | No | Override text color |
| `gridColor` | color | No | Override grid lines |
| `xAxisType` | string | No | `"time"`, `"linear"`, `"category"` |
## Series Configuration
| Source | How It Works |
|--------|-------------|
| `msg.topic` | Default — each unique topic creates a series |
| `key` | Uses a named property from data object |
| `JSON` | Array of keys for multi-series from single msg |
| (blank) | Auto-generates timestamp for x |
## Input Format
### Simple (recommended for EVOLV)
```js
{ topic: "PS West Level", payload: 2.34, timestamp: Date.now() }
```
### Object with x/y mapping
```js
{ payload: { time: 1700000000000, value: 2.34 } }
// Configure x → "time", y → "value"
```
### Nested access
X/Y keys support dot notation: `"nested.value"``payload.nested.value`
## Dynamic Properties
| Property | Path | Notes |
|----------|------|-------|
| Action | `msg.action` | `"append"` or `"replace"` |
| Chart Options | `msg.ui_update.chartOptions` | eCharts config merge |
| CSS Class | `msg.class` | Add custom class |
## eCharts Customization (`msg.ui_update.chartOptions`)
Deep-merges with existing config. Accumulates across messages.
```js
msg.ui_update = {
chartOptions: {
yAxis: { position: "right", name: "Level (m)" },
grid: { top: 60, right: 40 },
title: { text: "Basin Levels", textStyle: { fontSize: 14 } }
}
};
```
**Series colors** — must provide all series configs together:
```js
msg.ui_update = {
chartOptions: {
series: [
{ name: "PS West", type: "line", lineStyle: { color: "#2196f3" }, itemStyle: { color: "#2196f3" } },
{ name: "PS North", type: "line", lineStyle: { color: "#ff9800" }, itemStyle: { color: "#ff9800" } }
]
}
};
```
## Data Actions
| Action | How | Effect |
|--------|-----|--------|
| Append | `msg.action = "append"` (default) | Add points to existing data |
| Replace | `msg.action = "replace"` | Clear then add |
| Clear | `msg.payload = []` | Remove all data |
## Time Formatting Tokens
`{HH}:{mm}:{ss}``12:00:00`
`{yyyy}-{M}-{d}``2020-1-1`
Tokens: `{yyyy}`, `{MM}`, `{dd}`, `{HH}`, `{H}`, `{hh}`, `{h}`, `{mm}`, `{m}`, `{ss}`, `{s}`, `{eeee}` (day name)
## EVOLV Key Rules
1. Send minimal `{ topic, payload, timestamp }` — chart auto-uses topic for series.
2. Set `category: "topic"`, `categoryType: "msg"` in node config — blank category causes editor validation errors.
3. For timeseries: leave x-key blank → auto-timestamp. Supply ms timestamps if custom.
4. For multi-station overlay charts: each station sends with a different `msg.topic`.
5. Use `msg.action = "append"` (default) for streaming data; `"replace"` for snapshot updates.
6. Keep one topic per chart for simple KPIs; use multi-topic for comparison views.
7. Prefer explicit output-slot routing: `node.send([msgForChart, msgForText, ...]); return null;`
## Common Failure Modes
- Payload is not numeric (string/object without y-key config)
- Function node returns to wrong output index
- Topic churn creates unexpected series fragmentation
- Chart has stale data policy mismatch (`append` vs `replace`)
- Blank `category` field causes red node on deploy

View File

@@ -0,0 +1,103 @@
# FlowFuse Config Nodes Manual (EVOLV Reference)
Sources:
- https://dashboard.flowfuse.com/nodes/config/ui-base.html
- https://dashboard.flowfuse.com/nodes/config/ui-page.html
- https://dashboard.flowfuse.com/nodes/config/ui-group.html
- https://dashboard.flowfuse.com/nodes/config/ui-theme.html
---
## `ui-base` — Dashboard Root
| Property | Type | Notes |
|----------|------|-------|
| `path` | string | URL path after host, e.g. `/dashboard` |
| `appIcon` | string | URL to square icon (192512px) for PWA |
| `includePagePath` | bool | Show page paths in side nav |
| `navigationStyle` | string | `"default"`, `"fixed"`, `"icon"`, `"temporary"`, `"none"` |
| `headerStyle` | string | `"default"` (scrolls), `"fixed"` (sticky), `"hidden"` |
| `headerContent` | string | `"page"`, `"dashboard"`, `"both"`, `"none"` |
---
## `ui-page` — Dashboard Page
| Property | Type | Notes |
|----------|------|-------|
| `name` | string | Displayed in nav and header |
| `ui` | ref | Parent ui-base |
| `path` | string | URL path segment, e.g. `/influent` |
| `icon` | string | Material Design icon (no `mdi-` prefix) |
| `theme` | ref | ui-theme reference |
| `layout` | string | `"grid"`, `"fixed"`, `"notebook"`, `"tabs"` |
| `order` | int | Position in navigation |
| `breakpoints` | array | See breakpoints table |
### Layout Types
| Layout | Description |
|--------|-------------|
| `grid` | Responsive grid, widgets flow into columns |
| `fixed` | Absolute positioned, no responsive reflow |
| `notebook` | Single-column stacked groups |
| `tabs` | Each group becomes a tab |
### Default Breakpoints (grid/notebook/tabs)
| Name | Min Width | Columns |
|------|-----------|---------|
| Mobile | 0 px | 3 |
| Medium | 576 px | 6 |
| Tablet | 768 px | 9 |
| Desktop | 1024 px | 12 |
---
## `ui-group` — Widget Container
| Property | Type | Notes |
|----------|------|-------|
| `name` | string | Group title (shown if `showTitle: true`) |
| `page` | ref | Parent ui-page |
| `width` | string/int | Column span (e.g. `"4"` = 4 of 12 columns) |
| `height` | string/int | Minimum row height (`"1"` = auto-grow) |
| `order` | int | Render order on page |
| `showTitle` | bool | Show name as header |
| `className` | string | Custom CSS class |
| `groupType` | string | `"default"` (visible) or `"dialog"` (triggered) |
### Sizing Rules
- Group `width` sets column span out of page's total columns (default 12).
- Group `height` is a **minimum** — group grows to fit content.
- Widget `width` within a group is relative to the group's width.
- Widget `height` is in row units (1 unit = theme's Row Height setting).
---
## `ui-theme` — Appearance
| Property | Type | Default | Notes |
|----------|------|---------|-------|
| `colors.surface` | color | — | Header & nav background |
| `colors.primary` | color | — | Buttons, sliders, focus rings |
| `colors.bgPage` | color | — | Page background |
| `colors.groupBg` | color | — | Group background |
| `colors.groupOutline` | color | — | Group border color |
| `sizes.rowHeight` | string | `"default"` | `"default"` (48px), `"comfortable"` (36px), `"compact"` (32px) |
| `sizes.pagePadding` | string | `"12px"` | CSS shorthand (e.g. `"12px 6px"`) |
| `sizes.groupGap` | string | `"12px"` | Space between groups |
| `sizes.groupBorderRadius` | string | `"4px"` | Group corner rounding |
| `sizes.widgetGap` | string | `"12px"` | Space between widgets in group |
---
## EVOLV Key Rules
1. **12-column grid** with 4-col groups gives a clean 3-column layout at desktop.
2. Set `height: "1"` on groups to let them auto-size (content determines height).
3. Use `order` on groups to control left-to-right placement within a row.
4. For compact dashboards: theme `rowHeight: "compact"`, `pagePadding: "6px"`, `groupGap: "6px"`, `widgetGap: "6px"`.
5. Widget `order` within a group determines top-to-bottom flow (lower = higher).
6. Gauge sizing guide: tank gauge `width:2, height:4` + 3/4 gauge `width:2, height:3` fits well in a 4-col group alongside a status text `width:4, height:1`.

View File

@@ -0,0 +1,108 @@
# FlowFuse `ui-gauge` Manual (EVOLV Reference)
Source: https://dashboard.flowfuse.com/nodes/widgets/ui-gauge.html
## Gauge Types
| `gtype` value | Visual | Best For |
|---------------|--------|----------|
| `gauge-tile` | Compact square with value | KPI tiles |
| `gauge-battery` | Horizontal battery bar | Charge / capacity |
| `gauge-tank` | Vertical tank with fill gradient | Liquid levels |
| `gauge-half` | 180° arc | Setpoint / range display |
| `gauge-34` | 270° arc | Primary process gauge |
## Properties
| Property | Type | Dynamic | Notes |
|----------|------|---------|-------|
| `group` | ref | No | Parent ui-group |
| `width` | int | No | Columns (max = group width) |
| `height` | int | No | Row units |
| `gtype` | string | Yes | See table above |
| `gstyle` | string | Yes | `"Needle"` or `"Rounded"` (half/3-4 only) |
| `min` | number | Yes | Range minimum |
| `max` | number | Yes | Range maximum |
| `segments` | array | Yes | `[{color: "#hex", from: number}, …]` |
| `title` | string | Yes | Label above gauge |
| `prefix` | string | Yes | Before value (half/3-4 only) |
| `suffix` | string | Yes | After value (half/3-4 only) |
| `units` | string | Yes | Small text below value (half/3-4 only) |
| `icon` | string | Yes | Material Design icon (half/3-4 only) |
| `sizeGauge` | int | No | Arc thickness px |
| `sizeGap` | int | No | Gap between arc & segments px |
| `sizeSegments` | int | No | Segment ring thickness px |
## Input
`msg.payload` — numeric value to display.
## Dynamic Properties (`msg.ui_update`)
```js
msg.ui_update = {
label: "Tank A",
gtype: "gauge-tank",
gstyle: "Rounded",
min: 0, max: 100,
segments: [{color:"#f44336", from:0}, {color:"#4caf50", from:25}, {color:"#f44336", from:90}],
prefix: "", suffix: "%", units: "fill"
};
```
## Segments
Array of `{color, from}` objects sorted ascending by `from`. Each segment colors the range from its `from` up to the next segment's `from` (or `max`).
**Tank default segments** (auto-applied when switching to tank type):
```json
[{"color":"#A8F5FF","from":0},{"color":"#55DBEC","from":15},
{"color":"#53B4FD","from":35},{"color":"#2397D1","from":50}]
```
## CSS Selectors
| Selector | Target |
|----------|--------|
| `.nrdb-ui-gauge-value span` | Central value text |
| `.nrdb-ui-gauge-value label` | Unit label |
| `.nrdb-ui-gauge-value i` | Icon |
| `.nrdb-ui-gauge #limits` | Min/max labels |
## EVOLV Key Rules
1. **Tank gauge** for basin level: set `gtype:"gauge-tank"`, `min:0`, `max:<basin height>`, custom segments by safety thresholds.
2. **3/4 gauge** for fill %: set `gtype:"gauge-34"`, `gstyle:"Rounded"`, `min:0`, `max:100`, segments for low/normal/high.
3. Recommended sizing: tank `width:2, height:4`; 3/4 arc `width:2, height:3`.
4. Send plain numeric `msg.payload` — no topic needed (gauge has no series concept).
5. Segments must be provided as array even for a single color range.
## Node JSON Example (tank)
```json
{
"id": "demo_gauge_tank_west",
"type": "ui-gauge",
"z": "demo_tab_dashboard",
"group": "demo_ui_grp_ps_west",
"name": "West Tank Level",
"gtype": "gauge-tank",
"gstyle": "Rounded",
"title": "Level",
"units": "m",
"prefix": "",
"suffix": "m",
"min": 0, "max": 4,
"segments": [
{"color":"#f44336","from":0},
{"color":"#ff9800","from":0.3},
{"color":"#2196f3","from":1.0},
{"color":"#ff9800","from":2.5},
{"color":"#f44336","from":3.2}
],
"width": 2, "height": 4,
"order": 2,
"x": 700, "y": 400,
"wires": []
}
```

View File

@@ -0,0 +1,112 @@
# FlowFuse `ui-template` Manual (EVOLV Reference)
Source: https://dashboard.flowfuse.com/nodes/widgets/ui-template.html
## Purpose
Custom Vue 3 / Vuetify / HTML widget. Full scripting, scoped CSS, send/receive messages.
## Scopes
| Scope | Renders | Use Case |
|-------|---------|----------|
| Widget (Group) | Inside a ui-group | Custom gauges, tables, controls |
| Widget (Page) | On page, outside groups | Floating overlays, full-width banners |
| Widget (UI) | On every page | Global headers, footers |
| CSS (All Pages) | N/A — injects `<style>` | Global CSS overrides |
| CSS (Single Page) | N/A — injects `<style>` | Page-specific CSS |
## Properties
| Property | Type | Dynamic | Notes |
|----------|------|---------|-------|
| `group` | ref | No | Parent ui-group (Group scope) |
| `page` | ref | No | Parent ui-page (Page scope) |
| `width` | int | No | Columns |
| `height` | int | No | Row units |
| `format` | string | Yes | Vue template string |
| `storeOutMessages` | bool | No | Persist last output for late joiners |
| `passthru` | bool | No | Forward input to output |
| `templateScope` | string | No | `"widget:group"`, `"widget:page"`, `"widget:ui"`, `"page:style"`, `"site:style"` |
## Template Structure
```html
<template>
<v-btn @click="send({payload: 'clicked'})">Click</v-btn>
<p>Value: {{ msg.payload }}</p>
</template>
<script>
export default {
data() { return { local: 0 }; },
watch: {
msg(val) { this.local = val.payload; }
},
methods: {
doSend() { this.send({payload: this.local}); }
},
mounted() { /* one-time setup */ },
unmounted() { /* cleanup */ }
};
</script>
<style scoped>
p { color: #2196f3; }
</style>
```
## Built-in Variables
| Variable | Description |
|----------|-------------|
| `id` | This node's unique ID |
| `msg` | Latest received message (reactive) |
| `$socket` | Socket.io client for custom events |
## Sending Messages
```js
this.send({ payload: 42, topic: "my-topic" });
this.submit(); // sends FormData from <form>
```
## Receiving Messages
**Option A** — Vue `watch`:
```js
watch: { msg(newMsg) { /* react */ } }
```
**Option B** — Socket listener:
```js
mounted() {
this.$socket.on('msg-input:' + this.id, (msg) => { /* handle */ });
}
```
## Teleports (inject into dashboard chrome)
```html
<Teleport to="#app-bar-actions">
<v-btn>Custom Button</v-btn>
</Teleport>
```
Targets: `#app-bar-title`, `#app-bar-actions`.
## Vuetify Components
All Vuetify 3 components are available without import: `<v-btn>`, `<v-card>`, `<v-slider>`, `<v-data-table>`, etc.
## Dynamic Properties (`msg.ui_update`)
```js
msg.ui_update = { format: "<p>New template content</p>" };
```
## EVOLV Key Rules
1. Use templates sparingly — prefer built-in widgets when they fit.
2. For complex custom visualizations (SVG P&ID, animated schematics), template is the right choice.
3. Always use `<style scoped>` to avoid leaking CSS to other widgets.
4. Prefer `watch: { msg }` over socket listeners for simpler code.
5. Keep templates under 100 lines — split complex UIs into multiple template nodes.

View File

@@ -0,0 +1,72 @@
# FlowFuse `ui-text` Manual (EVOLV Reference)
Source: https://dashboard.flowfuse.com/nodes/widgets/ui-text.html
## Purpose
Read-only text display widget. Shows a label + value. Supports HTML in payload.
## Properties
| Property | Type | Dynamic | Notes |
|----------|------|---------|-------|
| `group` | ref | No | Parent ui-group |
| `width` | int | No | Columns |
| `height` | int | No | Row units |
| `label` | string | Yes | Left-side label text |
| `format` | string | No | Template: `{{msg.payload}}` or `{{msg.myProp}}` |
| `layout` | string | Yes | See layout options below |
| `style` | bool | No | Enable custom font/size/color |
| `font` | string | Yes | Font family (when style enabled) |
| `fontSize` | string | Yes | e.g. `"16px"`, `"1.2rem"` |
| `color` | string | Yes | Text color (when style enabled) |
## Layout Options
| Value | Description |
|-------|-------------|
| `row-left` | Label and value left-aligned |
| `row-center` | Centered on row |
| `row-right` | Right-aligned |
| `row-spread` | Label left, value right |
| `col-center` | Stacked vertically, centered |
## Input
`msg.payload` — string or number. HTML is rendered if present.
## Dynamic Properties (`msg.ui_update`)
```js
msg.ui_update = {
label: "New Label",
layout: "row-spread",
font: "monospace",
fontSize: "14px",
color: "#ff5722"
};
```
Also: `msg.class` — add CSS class to the widget container.
## Value Formatting
The `format` field supports template syntax and JSONata:
- `{{msg.payload}}` — raw value
- `{{msg.myProp}}` — any msg property
- JSONata in Value property: `$round(payload, 1)`
## HTML Support
`msg.payload` can contain HTML tags. Useful for inline formatting:
```js
msg.payload = '<b>Active</b> — 3 pumps online';
```
## EVOLV Key Rules
1. Use `layout: "row-spread"` for status lines (label left, value right).
2. For multi-field status, build a formatted string in the Function node and send as single payload.
3. Keep text widgets `height: 1` for compact status rows.
4. Use `format: "{{msg.payload}}"` (the default) — keeps it simple.
5. For direction arrows and styled status, use HTML in payload: `↑ filling | 142 m³/h | 35 min`.

View File

@@ -0,0 +1,49 @@
# FlowFuse Dashboard 2.0 — Widget Catalog
Source: https://dashboard.flowfuse.com/
## Widgets (22 types)
| Widget | Type String | Description |
|--------|-------------|-------------|
| Audio | `ui-audio` | Play audio files or TTS in browser |
| Button | `ui-button` | Clickable button, sends msg on click/pointerdown/pointerup |
| Button Group | `ui-button-group` | Multiple buttons rendered together as toggle/selection |
| Chart | `ui-chart` | Line, bar, scatter, pie, histogram — backed by eCharts |
| Control | `ui-control` | Programmatic page navigation and group visibility control |
| Dropdown | `ui-dropdown` | Select one or many options from a dropdown list |
| Event | `ui-event` | Fires msg on browser-side events (page load, resize, etc.) |
| File Input | `ui-file-input` | File upload from browser to Node-RED |
| Form | `ui-form` | Multi-field input form with submit action |
| Gauge | `ui-gauge` | Numeric display as tile, battery, tank, half-arc, or 3/4-arc |
| Markdown | `ui-markdown` | Render markdown/HTML content |
| Notification | `ui-notification` | Toast / snackbar / alert popups |
| Number Input | `ui-number-input` | Numeric input field with optional range/step |
| Progress | `ui-progress` | Linear or circular progress bar |
| Radio Group | `ui-radio-group` | Select one option from radio buttons |
| Slider | `ui-slider` | Horizontal or vertical slider control |
| Spacer | `ui-spacer` | Empty cell for layout positioning |
| Switch | `ui-switch` | On/off toggle switch |
| Table | `ui-table` | Paginated data table with sorting/selection |
| Template | `ui-template` | Custom Vue/Vuetify/HTML with full scripting |
| Text | `ui-text` | Read-only text display, supports HTML |
| Text Input | `ui-text-input` | Editable text field (text, password, email, etc.) |
## Config Nodes (4 types)
| Node | Type String | Purpose |
|------|-------------|---------|
| Base | `ui-base` | Root dashboard instance — path, nav style, header |
| Theme | `ui-theme` | Colors, sizing, spacing, group styling |
| Page | `ui-page` | Dashboard page — path, layout (grid/fixed/notebook/tabs), breakpoints |
| Group | `ui-group` | Widget container — width, height, order within a page |
## See Also
- [ui-gauge manual](flowfuse-ui-gauge-manual.md)
- [ui-text manual](flowfuse-ui-text-manual.md)
- [ui-template manual](flowfuse-ui-template-manual.md)
- [ui-button manual](flowfuse-ui-button-manual.md)
- [ui-chart manual](flowfuse-ui-chart-manual.md)
- [ui-config manual](flowfuse-ui-config-manual.md)
- [Dashboard layout notes](flowfuse-dashboard-layout-manual.md)

View File

@@ -0,0 +1,29 @@
# Node-RED Function Node Patterns (EVOLV Summary)
Based on: https://nodered.org/docs/user-guide/writing-functions
## Return Semantics
- `return msg;` sends to output 1.
- `return [msg1, msg2, ...];` sends one message per output position.
- Use `null` for outputs that should not emit.
Examples:
- `return [msg, null, null];` -> output 1 only
- `return [null, msg, null];` -> output 2 only
## Message Mutation Pattern
- Start with the incoming `msg`, set fields (`msg.topic`, `msg.payload`), then return/send.
- This preserves message context unless there is a deliberate reason to create a new object.
## Async Function Pattern
- For asynchronous work, call `node.send(...)` and then `node.done()`.
- Avoid returning a message when output is sent asynchronously.
## EVOLV Practical Rule
- In example flows (including parsers), always align return-array position with downstream wiring.
- For dashboard parser functions, keep one stable output mapping per metric to avoid wire-level regressions.

View File

@@ -0,0 +1,27 @@
# Node-RED Messages and Editor/Runtime Structure (EVOLV Summary)
Sources:
- Messages: https://nodered.org/docs/user-guide/messages
- Edit dialog and node definition: https://nodered.org/docs/creating-nodes/edit-dialog
## Message Shape
- Standard message object is usually `msg`.
- `msg.payload` is the default value channel.
- `msg.topic` is used for routing/intent labeling.
## EVOLV Topic Routing
- EVOLV runtime wrappers route commands by `msg.topic` in `src/nodeClass.js`.
- Keep topic names stable unless a migration is defined (flow compatibility).
## Editor/Runtime Parity
- In `.html`, `RED.nodes.registerType(... defaults ...)` defines persisted config fields.
- Runtime parser in `src/nodeClass.js` must read and normalize the same fields.
- If a field is added/renamed in editor defaults, update runtime mapping and tests in the same change.
## Multi-Output Node Rule
- Runtime `node.send([out1, out2, out3])` must align with declared `outputs` in `.html`.
- If output meaning is `process`, `dbase`, `parent`, maintain stable index mapping across all message paths.

View File

@@ -0,0 +1,31 @@
# Node-RED Runtime Node JS Manual (EVOLV Summary)
Based on: https://nodered.org/docs/creating-nodes/node-js
## Input Handler Contract
- Node-RED calls runtime handlers as `function(msg, send, done)`.
- For compatibility, custom nodes should support fallback behavior when `send` is not provided.
- Use `done()` on successful completion and `done(err)` on failure.
## Output Routing Rules
- For single-output nodes: `send(msg)` sends on output 1.
- For multi-output nodes: pass an array with one slot per output, for example:
- `[msg, null, null]` => output 1 only
- `[null, msg, null]` => output 2 only
- Always construct the outbound message first, then send it.
## EVOLV Runtime Pattern
Recommended runtime input structure for `src/nodeClass.js`:
1. Normalize `send` fallback.
2. Wrap `switch(msg.topic)` in `try/catch`.
3. Route response messages to explicit output index with array format.
4. Call `done()`/`done(err)` consistently.
## Common Failure Mode
- Sending a bare object in a multi-output context can hide intended port routing.
- In EVOLV nodes with 3 outputs (`process`, `dbase`, `parent`), use explicit arrays for deterministic wiring behavior.