@gmag11/nodered-mcp-server 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +162 -0
- package/index.js +133 -0
- package/package.json +58 -0
- package/resources/skills/nodered-flow-builder/SKILL.md +659 -0
- package/resources/skills/nodered-flow-layout/SKILL.md +395 -0
- package/resources/skills/nodered-flowfuse-dashboard/SKILL.md +941 -0
- package/resources/skills/nodered-fundamentals/SKILL.md +323 -0
- package/resources/skills/nodered-jsonata/SKILL.md +1039 -0
- package/resources/skills/nodered-mustache/SKILL.md +588 -0
- package/resources/skills/nodered-node-reference/SKILL.md +1020 -0
- package/resources/skills/nodered-node-reference/examples/common.json +113 -0
- package/resources/skills/nodered-node-reference/examples/network.json +107 -0
- package/resources/skills/nodered-node-reference/examples/parser.json +147 -0
- package/resources/skills/nodered-node-reference/examples/sequence.json +141 -0
- package/resources/skills/nodered-node-reference/examples/storage.json +104 -0
- package/resources/skills/nodered-patterns/SKILL.md +414 -0
- package/resources/skills/nodered-patterns/examples/error-handler.json +72 -0
- package/resources/skills/nodered-patterns/examples/http-endpoint.json +42 -0
- package/resources/skills/nodered-patterns/examples/mqtt-subscriber.json +47 -0
- package/resources/skills/nodered-patterns/examples/timer-flow.json +50 -0
- package/resources/skills/nodered-subflows/SKILL.md +261 -0
- package/resources/skills/nodered-uibuilder/SKILL.md +500 -0
- package/src/auth/api-key-verifier.js +36 -0
- package/src/auth/composite-verifier.js +59 -0
- package/src/auth/config.js +106 -0
- package/src/auth/oauth-clients-store.js +107 -0
- package/src/auth/oauth-provider.js +149 -0
- package/src/auth/oauth-token-store.js +312 -0
- package/src/nodered/auth.js +158 -0
- package/src/nodered/client.js +199 -0
- package/src/nodered/comms-client.js +500 -0
- package/src/renderer/colors.js +161 -0
- package/src/renderer/geometry.js +115 -0
- package/src/renderer/html-builder.js +571 -0
- package/src/renderer/index.js +51 -0
- package/src/renderer/ir-builder.js +161 -0
- package/src/renderer/layout.js +126 -0
- package/src/renderer/mermaid-builder.js +109 -0
- package/src/renderer/svg-builder.js +228 -0
- package/src/schemas/responses.js +283 -0
- package/src/server.js +844 -0
- package/src/skills/loader.js +84 -0
- package/src/staging-store.js +258 -0
- package/src/tools/add-nodes-to-group.js +216 -0
- package/src/tools/connect-nodes.js +115 -0
- package/src/tools/constants.js +45 -0
- package/src/tools/create-flow.js +87 -0
- package/src/tools/create-node.js +126 -0
- package/src/tools/create-subflow-instance.js +123 -0
- package/src/tools/create-subflow.js +101 -0
- package/src/tools/delete-context.js +60 -0
- package/src/tools/delete-flow.js +81 -0
- package/src/tools/delete-group.js +116 -0
- package/src/tools/delete-node.js +73 -0
- package/src/tools/delete-subflow.js +103 -0
- package/src/tools/deploy.js +94 -0
- package/src/tools/disconnect-nodes.js +158 -0
- package/src/tools/export-flow.js +161 -0
- package/src/tools/export-subflow.js +78 -0
- package/src/tools/flow-utils.js +376 -0
- package/src/tools/get-config-nodes.js +86 -0
- package/src/tools/get-context.js +76 -0
- package/src/tools/get-flow-diagram.js +99 -0
- package/src/tools/get-flow-nodes.js +116 -0
- package/src/tools/get-flows.js +74 -0
- package/src/tools/get-node-detail.js +77 -0
- package/src/tools/get-node-type-detail.js +92 -0
- package/src/tools/get-palette-nodes.js +63 -0
- package/src/tools/get-staging-status.js +34 -0
- package/src/tools/get-subflow-detail.js +110 -0
- package/src/tools/get-subflows.js +105 -0
- package/src/tools/import-flow.js +310 -0
- package/src/tools/inject-message.js +117 -0
- package/src/tools/install-node.js +31 -0
- package/src/tools/read-debug-messages.js +155 -0
- package/src/tools/refresh-staging.js +62 -0
- package/src/tools/remove-nodes-from-group.js +162 -0
- package/src/tools/render-staging.js +69 -0
- package/src/tools/response-utils.js +42 -0
- package/src/tools/search-nodes.js +134 -0
- package/src/tools/uninstall-node.js +31 -0
- package/src/tools/update-flow.js +95 -0
- package/src/tools/update-group.js +77 -0
- package/src/tools/update-node.js +132 -0
- package/src/tools/update-subflow.js +84 -0
- package/src/transport/http.js +252 -0
- package/src/transport/stdio.js +16 -0
- package/src/transport/ws-server.js +223 -0
|
@@ -0,0 +1,941 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nodered-flowfuse-dashboard
|
|
3
|
+
version: "1.30.2"
|
|
4
|
+
category: dashboard
|
|
5
|
+
description: >-
|
|
6
|
+
Reference for @flowfuse/node-red-dashboard (Dashboard 2.0) v1.30.2, the officially
|
|
7
|
+
recommended Node-RED dashboard. Covers widget catalog with properties,
|
|
8
|
+
config nodes (ui-base, ui-page, ui-group, ui-theme), wiring patterns, and common
|
|
9
|
+
dashboard recipes. Docs: https://dashboard.flowfuse.com/
|
|
10
|
+
tools:
|
|
11
|
+
- install-node
|
|
12
|
+
- create-node
|
|
13
|
+
- update-node
|
|
14
|
+
- get-node-type-detail
|
|
15
|
+
- get-palette-nodes
|
|
16
|
+
- get-node-detail
|
|
17
|
+
- connect-nodes
|
|
18
|
+
- inject-message
|
|
19
|
+
- get-config-nodes
|
|
20
|
+
- refresh-staging
|
|
21
|
+
- deploy
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# FlowFuse Dashboard 2.0 Reference
|
|
25
|
+
|
|
26
|
+
Comprehensive reference for building dashboards with **@flowfuse/node-red-dashboard** (Dashboard 2.0), the officially recommended successor to the obsolete `node-red-dashboard` v1. **Documented version: v1.30.2**.
|
|
27
|
+
|
|
28
|
+
> **⚠️ Staging reminder:** All create-node/connect-nodes calls stage changes locally. After building any dashboard, call `deploy()` to push to Node-RED. **NEVER skip deploy** — undeployed edits are not active.
|
|
29
|
+
|
|
30
|
+
> **⚠️ MANDATORY RULE:** Every Dashboard 2.0 flow MUST include a `ui-theme` config node. Every `ui-page` MUST reference that theme via its `theme` property. Dashboard 2.0 will not render correctly without a theme.
|
|
31
|
+
>
|
|
32
|
+
> **Prerequisites:** Read `nodered-fundamentals` first for core vocabulary. Use `nodered-flow-builder` for the step-by-step build workflow. See `nodered-flow-layout` for node positioning rules.
|
|
33
|
+
|
|
34
|
+
> **Widget discovery:** For any widget NOT listed in the deep-reference section below, or to verify current property values, use `get-palette-nodes` to list available Dashboard 2.0 types and `get-node-type-detail` to retrieve the full property schema.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Concepts & Architecture
|
|
39
|
+
|
|
40
|
+
Dashboard 2.0 is a **widget-based** dashboard system. Each UI element (button, chart, gauge, form, etc.) is a standard Node-RED node that you create with `create-node` and wire with `connect-nodes` on the flow canvas. There is no separate UI editor — the dashboard layout is defined entirely by the node hierarchy.
|
|
41
|
+
|
|
42
|
+
**Key differences from obsolete v1:**
|
|
43
|
+
- Package name is `@flowfuse/node-red-dashboard` (NOT `node-red-dashboard`)
|
|
44
|
+
- Uses Vue 3 / Vuetify 3 under the hood (v1 used AngularJS)
|
|
45
|
+
- Config nodes are regular Node-RED config nodes (not a separate UI setup tab)
|
|
46
|
+
- Supports multi-tenancy via `msg._client` for per-user data (see Multi-Tenancy section in the official docs)
|
|
47
|
+
- The Dashboard 2.0 sidebar in the Node-RED editor lets you manage pages, groups, themes, and widget ordering visually
|
|
48
|
+
|
|
49
|
+
**Design patterns:**
|
|
50
|
+
- **Single Source of Truth:** All users see the same data (default). Wire a sensor to a gauge — everyone sees the same reading.
|
|
51
|
+
- **Multi-Tenancy:** Each user sees their own data. Toggle "Accept Client Data" in the Dashboard 2.0 sidebar for specific widget types. Use `msg._client` to target specific connections.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Config Nodes
|
|
56
|
+
|
|
57
|
+
Dashboard 2.0 uses four config nodes that define the dashboard structure. **Creation order matters** because each references the previous one. **⚠️ `ui-theme` is MANDATORY — always create one before creating pages.**
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
ui-base (app settings, URL path)
|
|
61
|
+
├── ui-theme (colors, sizes) — ⚠️ MANDATORY, always create one
|
|
62
|
+
└── ui-page (navigation entry, layout type) — must reference ui-theme
|
|
63
|
+
└── ui-group (widget container, width)
|
|
64
|
+
└── widgets (ui-button, ui-chart, etc.)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### ui-base (`type: "ui-base"`)
|
|
68
|
+
|
|
69
|
+
Defines global dashboard settings: base URL path, app icon, navigation style, theme mode.
|
|
70
|
+
|
|
71
|
+
| Property | Type | Description |
|
|
72
|
+
|----------|------|-------------|
|
|
73
|
+
| `path` | string | Base URL endpoint (default: `"/dashboard"`) |
|
|
74
|
+
| `name` | string | Dashboard site name shown in browser tab |
|
|
75
|
+
| `appIcon` | string | URL to square icon image (192-512px), shown in browser tab and PWA |
|
|
76
|
+
| `sideNavigationStyle` | string | `"default"` (collapsing), `"fixed"`, `"icons"` (collapse to icons), `"over"` (appear over content), `"none"` (always hide) |
|
|
77
|
+
| `headerStyle` | string | `"default"` (scrolls away), `"fixed"` (always visible), `"hidden"` (no title bar) |
|
|
78
|
+
| `headerContent` | string | `"pageName"`, `"dashboardName"`, `"dashboardNamePageName"`, `"none"` |
|
|
79
|
+
| `includePagePathInLabel` | boolean | Show page path alongside name in sidebar |
|
|
80
|
+
| `allowInstall` | boolean | Enable PWA install prompt |
|
|
81
|
+
| `theme` | string | Theme config node ID for default theme |
|
|
82
|
+
|
|
83
|
+
**Creation:**
|
|
84
|
+
```
|
|
85
|
+
create-node(type: "ui-base", name: "My Dashboard", properties: { path: "/dashboard", sideNavigationStyle: "default" })
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### ui-page (`type: "ui-page"`)
|
|
89
|
+
|
|
90
|
+
Represents a navigable page in the dashboard sidebar. Must reference a `ui-base` node. **⚠️ The `theme` property is REQUIRED — every `ui-page` must reference a `ui-theme` config node.** Always create a `ui-theme` before creating pages.
|
|
91
|
+
|
|
92
|
+
| Property | Type | Required | Description |
|
|
93
|
+
|----------|------|----------|-------------|
|
|
94
|
+
| `ui` | string | ✅ | Config node ID of the parent `ui-base` |
|
|
95
|
+
| `path` | string | ✅ | URL path extending the base (e.g., `"/page1"`) |
|
|
96
|
+
| `name` | string | | Page display name in sidebar |
|
|
97
|
+
| `theme` | string | ✅ | Theme config node ID — **MANDATORY**, always create a `ui-theme` first |
|
|
98
|
+
| `icon` | string | | Material Design icon name (without `mdi-` prefix) |
|
|
99
|
+
| `layout` | string | | `"grid"` (default), `"fixed"`, `"notebook"`, `"tabs"` |
|
|
100
|
+
| `className` | string | | Custom CSS class(es) for advanced styling |
|
|
101
|
+
| `visible` | string | | `"true"` (default) or `"false"` — controls page visibility |
|
|
102
|
+
| `disabled` | string | | `"false"` (default) or `"true"` — controls page interactivity |
|
|
103
|
+
| `breakpoints` | array | | Responsive layout breakpoints: `[{ name, px, cols }]` — not available for fixed layout |
|
|
104
|
+
|
|
105
|
+
**Creation:**
|
|
106
|
+
```
|
|
107
|
+
// Step 1: Create ui-theme first (REQUIRED)
|
|
108
|
+
create-node(type: "ui-theme", name: "Dashboard Theme", properties: {
|
|
109
|
+
colors: { surface: "#ffffff", primary: "#0094ce", bgPage: "#eeeeee", groupBg: "#ffffff", groupOutline: "#cccccc" },
|
|
110
|
+
sizes: { density: "default", pagePadding: "12px", groupGap: "12px", groupBorderRadius: "4px", widgetGap: "12px" }
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
// Step 2: Create ui-page with theme reference
|
|
114
|
+
create-node(type: "ui-page", name: "Home", properties: {
|
|
115
|
+
ui: "<uiBaseId>",
|
|
116
|
+
path: "/home",
|
|
117
|
+
icon: "home",
|
|
118
|
+
layout: "grid",
|
|
119
|
+
theme: "<uiThemeId>" // ⚠️ MANDATORY
|
|
120
|
+
})
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### ui-group (`type: "ui-group"`)
|
|
124
|
+
|
|
125
|
+
A container for widgets on a page. Must reference a `ui-page` node. Widgets reference their parent `ui-group`.
|
|
126
|
+
|
|
127
|
+
| Property | Type | Description |
|
|
128
|
+
|----------|------|-------------|
|
|
129
|
+
| `page` | string | Config node ID of the parent `ui-page` |
|
|
130
|
+
| `name` | string | Group label shown in the dashboard |
|
|
131
|
+
| `width` | number | Width in grid columns (1-12, grid layout) or fixed px units |
|
|
132
|
+
| `height` | number | Minimum height in px |
|
|
133
|
+
| `groupType` | string | `"default"` (always visible) or `"dialog"` (triggered by `ui-control`) |
|
|
134
|
+
| `className` | string | Custom CSS class(es) for advanced styling |
|
|
135
|
+
| `showTitle` | boolean | Show the group name as a title bar above the widgets |
|
|
136
|
+
| `visible` | string | `"true"` (default) or `"false"` — controls group visibility |
|
|
137
|
+
| `disabled` | string | `"false"` (default) or `"true"` — controls group interactivity |
|
|
138
|
+
|
|
139
|
+
**Creation:**
|
|
140
|
+
```
|
|
141
|
+
create-node(type: "ui-group", name: "Controls", properties: { page: "<uiPageId>", width: 6, height: 200 })
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Note:** If you create a widget without specifying a `group`, Dashboard 2.0 auto-creates a default base, page, and group for you. However, for predictable layouts, always create config nodes explicitly.
|
|
145
|
+
|
|
146
|
+
### ui-theme (`type: "ui-theme"`)
|
|
147
|
+
|
|
148
|
+
Defines the visual theme (colors and spacing) applied to dashboard pages. **⚠️ MANDATORY — every `ui-page` must reference a `ui-theme`. Always create one before creating any pages.**
|
|
149
|
+
|
|
150
|
+
| Property | Type | Description |
|
|
151
|
+
|----------|------|-------------|
|
|
152
|
+
| `name` | string | Theme display name in the Dashboard sidebar |
|
|
153
|
+
| `colors` | object | Color definitions: `{ surface, primary, bgPage, groupBg, groupOutline }` — all optional, hex colors |
|
|
154
|
+
| `sizes` | object | Sizing definitions: `{ density, pagePadding, groupGap, groupBorderRadius, widgetGap }` — all optional |
|
|
155
|
+
|
|
156
|
+
**Colors object:**
|
|
157
|
+
| Key | Description |
|
|
158
|
+
|-----|-------------|
|
|
159
|
+
| `surface` | Card/widget surface color (default: `"#ffffff"`) |
|
|
160
|
+
| `primary` | Primary accent color for headers, active elements (default: `"#0094ce"`) |
|
|
161
|
+
| `bgPage` | Page background color (default: `"#eeeeee"`) |
|
|
162
|
+
| `groupBg` | Group container background color (default: `"#ffffff"`) |
|
|
163
|
+
| `groupOutline` | Group container border color (default: `"#cccccc"`) |
|
|
164
|
+
|
|
165
|
+
**Sizes object:**
|
|
166
|
+
| Key | Description |
|
|
167
|
+
|-----|-------------|
|
|
168
|
+
| `density` | UI density: `"default"`, `"comfortable"`, `"compact"` |
|
|
169
|
+
| `pagePadding` | CSS value for page outer padding (default: `"12px"`) |
|
|
170
|
+
| `groupGap` | CSS value for gap between groups (default: `"12px"`) |
|
|
171
|
+
| `groupBorderRadius` | CSS value for group border radius (default: `"4px"`) |
|
|
172
|
+
| `widgetGap` | CSS value for gap between widgets within a group (default: `"12px"`) |
|
|
173
|
+
|
|
174
|
+
**Creation:**
|
|
175
|
+
```
|
|
176
|
+
create-node(type: "ui-theme", name: "Dashboard Theme", properties: {
|
|
177
|
+
colors: {
|
|
178
|
+
surface: "#ffffff",
|
|
179
|
+
primary: "#0094ce",
|
|
180
|
+
bgPage: "#eeeeee",
|
|
181
|
+
groupBg: "#ffffff",
|
|
182
|
+
groupOutline: "#cccccc"
|
|
183
|
+
},
|
|
184
|
+
sizes: {
|
|
185
|
+
density: "default",
|
|
186
|
+
pagePadding: "12px",
|
|
187
|
+
groupGap: "12px",
|
|
188
|
+
groupBorderRadius: "4px",
|
|
189
|
+
widgetGap: "12px"
|
|
190
|
+
}
|
|
191
|
+
})
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Widget Catalog — Complete List
|
|
197
|
+
|
|
198
|
+
Every available Dashboard 2.0 widget with its `type` string for `create-node`. The list is ordered by category.
|
|
199
|
+
|
|
200
|
+
### Display Widgets
|
|
201
|
+
|
|
202
|
+
| Type | Description |
|
|
203
|
+
|------|-------------|
|
|
204
|
+
| `ui-text` | Displays text/HTML updated via `msg.payload`. Supports HTML formatting, JSONata value formatting |
|
|
205
|
+
| `ui-markdown` | Renders Markdown content sent via `msg.payload` |
|
|
206
|
+
| `ui-template` | Full Vue 3 component — custom HTML/CSS/JS, access to `msg` via `this.msg`. Use for custom charts, layouts, embedded content |
|
|
207
|
+
| `ui-table` | Tabular data display with sorting, pagination, search, cell types (text, link, button, color, progress, sparkline, image) |
|
|
208
|
+
| `ui-notification` | Toast notification popup with configurable position, timeout, color, dismiss/confirm buttons |
|
|
209
|
+
| `ui-audio` | Audio playback widget. Send URL in `msg.payload` to play |
|
|
210
|
+
| `ui-iframe` | Embeds an external URL in an iframe |
|
|
211
|
+
| `ui-led` | LED indicator light — sends boolean state from `msg.payload` |
|
|
212
|
+
| `ui-map` | Map display widget |
|
|
213
|
+
|
|
214
|
+
### Input / Control Widgets
|
|
215
|
+
|
|
216
|
+
| Type | Description |
|
|
217
|
+
|------|-------------|
|
|
218
|
+
| `ui-button` | Clickable button. Emits `msg.payload` on click. Supports icon, color, pointer events |
|
|
219
|
+
| `ui-button-group` | Group of buttons that emit the clicked button's value |
|
|
220
|
+
| `ui-switch` | Toggle switch — emits on/off payload values |
|
|
221
|
+
| `ui-slider` | Numeric slider with min/max/step. Emits value as user slides |
|
|
222
|
+
| `ui-number-input` | Numeric text input field |
|
|
223
|
+
| `ui-text-input` | Free-form text input field |
|
|
224
|
+
| `ui-dropdown` | Dropdown select (single or multi-select). Options as array of `{label, value}` |
|
|
225
|
+
| `ui-radio-group` | Radio button group |
|
|
226
|
+
| `ui-date-picker` | Date selection widget |
|
|
227
|
+
| `ui-file-input` | File upload widget |
|
|
228
|
+
| `ui-form` | Multi-field form with submit/cancel buttons. Emits form data as object |
|
|
229
|
+
| `ui-control` | Page/group visibility control — show/hide/enable/disable groups or navigate pages |
|
|
230
|
+
| `ui-event` | Emits events on page load/view. Used for multi-tenancy patterns |
|
|
231
|
+
|
|
232
|
+
### Data Visualization Widgets
|
|
233
|
+
|
|
234
|
+
| Type | Description |
|
|
235
|
+
|------|-------------|
|
|
236
|
+
| `ui-chart` | Multi-type chart (line, bar, scatter, pie/doughnut, histogram). Based on eCharts |
|
|
237
|
+
| `ui-gauge` | Gauge display (half, 3/4, tile, battery, water tank types with segments) |
|
|
238
|
+
| `ui-progress` | Progress bar — value 0-100 via `msg.payload` |
|
|
239
|
+
|
|
240
|
+
### Layout Widgets
|
|
241
|
+
|
|
242
|
+
| Type | Description |
|
|
243
|
+
|------|-------------|
|
|
244
|
+
| `ui-spacer` | Adds vertical space between widgets in a group |
|
|
245
|
+
|
|
246
|
+
> **Discovery tip:** Widget catalogs grow over releases. Use `get-palette-nodes` filtered for `@flowfuse/node-red-dashboard` to get the current list. Use `get-node-type-detail` on any type to get its full property schema.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Deep Reference — Top 10 Widgets
|
|
251
|
+
|
|
252
|
+
Full property tables and wiring examples for the most commonly used widgets.
|
|
253
|
+
|
|
254
|
+
### ui-button
|
|
255
|
+
|
|
256
|
+
`type: "ui-button"` — Clickable button that emits `msg.payload` when clicked.
|
|
257
|
+
|
|
258
|
+
| Property | Type | Description |
|
|
259
|
+
|----------|------|-------------|
|
|
260
|
+
| `group` | string | Parent `ui-group` config node ID |
|
|
261
|
+
| `label` | string | Button text |
|
|
262
|
+
| `icon` | string | Material Design icon name (without `mdi-` prefix) |
|
|
263
|
+
| `iconPosition` | string | `"left"` or `"right"` |
|
|
264
|
+
| `color` | string | Button background color (CSS color or hex) |
|
|
265
|
+
| `textColor` | string | Button text color |
|
|
266
|
+
| `iconColor` | string | Icon color |
|
|
267
|
+
| `payload` | string | Value emitted as `msg.payload` when clicked |
|
|
268
|
+
| `payloadType` | string | `"str"`, `"num"`, `"json"`, `"bool"` |
|
|
269
|
+
| `topic` | string | `msg.topic` value on click |
|
|
270
|
+
| `emulateClick` | boolean | If true, incoming `msg` triggers a click emit |
|
|
271
|
+
|
|
272
|
+
**Wiring example — button triggers a flow:**
|
|
273
|
+
```
|
|
274
|
+
// Create
|
|
275
|
+
create-node(type: "ui-button", name: "Start Process", properties: {
|
|
276
|
+
group: "<uiGroupId>",
|
|
277
|
+
label: "Start",
|
|
278
|
+
icon: "play",
|
|
279
|
+
color: "green",
|
|
280
|
+
payload: "start",
|
|
281
|
+
payloadType: "str"
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
// Wire: button → function (process)
|
|
285
|
+
connect-nodes(fromNodeId: "<buttonId>", outputPort: 0, toNodeId: "<functionId>")
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**Data OUT (button click):** The button outputs `msg.payload` = the configured payload value. Wire the button's output to a function/switch node.
|
|
289
|
+
|
|
290
|
+
**Data IN (emulate click):** Send any `msg` to the button's input to programmatically trigger a click (if `emulateClick: true`). The button will emit its configured payload on its output.
|
|
291
|
+
|
|
292
|
+
**Dynamic updates via msg.ui_update:**
|
|
293
|
+
```javascript
|
|
294
|
+
msg.ui_update = {
|
|
295
|
+
label: "Stop",
|
|
296
|
+
icon: "stop",
|
|
297
|
+
color: "red"
|
|
298
|
+
}
|
|
299
|
+
// Send to button's input to change appearance at runtime
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### ui-chart
|
|
303
|
+
|
|
304
|
+
`type: "ui-chart"` — Multi-type chart supporting line, bar, scatter, pie/doughnut, and histogram.
|
|
305
|
+
|
|
306
|
+
| Property | Type | Description |
|
|
307
|
+
|----------|------|-------------|
|
|
308
|
+
| `group` | string | Parent `ui-group` config node ID |
|
|
309
|
+
| `label` | string | Chart title |
|
|
310
|
+
| `chartType` | string | `"line"`, `"bar"`, `"scatter"`, `"pie"`, `"doughnut"` |
|
|
311
|
+
| `xAxisType` | string | `"timescale"`, `"linear"`, `"categorical"`, `"bins"` |
|
|
312
|
+
| `series` | string/array | Series grouping: `"msg.topic"`, `"key"` with key name, or JSON array of keys |
|
|
313
|
+
| `x` | string | Key in data for x-axis value. Leave blank for auto-timestamp on timescale |
|
|
314
|
+
| `y` | string | Key in data for y-axis value. Leave blank for `msg.payload` |
|
|
315
|
+
| `action` | string | `"append"` (add to existing) or `"replace"` (clear then add) |
|
|
316
|
+
| `showLegend` | boolean | Show chart legend |
|
|
317
|
+
| `pointShape` | string | `"circle"`, `"triangle"`, `"rect"`, `"diamond"`, etc. (line/scatter) |
|
|
318
|
+
| `pointRadius` | number | Point radius in px |
|
|
319
|
+
| `xAxisFormat` | string | Time format string (e.g., `"{HH}:{mm}:{ss}"`, `"{yyyy}-{M}-{d}"`) |
|
|
320
|
+
| `xAxisLimit` | number | Max data points or time limit before pruning old data (0 = unlimited) |
|
|
321
|
+
| `interpolation` | string | Line interpolation: `"linear"`, `"step"`, `"bezier"`, `"cubic"`, `"cubic-mono"` |
|
|
322
|
+
|
|
323
|
+
**Wiring example — live sensor chart:**
|
|
324
|
+
```
|
|
325
|
+
// Create chart
|
|
326
|
+
create-node(type: "ui-chart", name: "Temperature", properties: {
|
|
327
|
+
group: "<uiGroupId>",
|
|
328
|
+
label: "Temperature Over Time",
|
|
329
|
+
chartType: "line",
|
|
330
|
+
xAxisType: "timescale",
|
|
331
|
+
series: "msg.topic",
|
|
332
|
+
action: "append",
|
|
333
|
+
xAxisLimit: 300 // keep last 300 data points
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
// Wire: inject (sensor) → chart
|
|
337
|
+
connect-nodes(fromNodeId: "<injectId>", outputPort: 0, toNodeId: "<chartId>")
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
**Data format for line chart:**
|
|
341
|
+
```javascript
|
|
342
|
+
// Simple value — auto-timestamp
|
|
343
|
+
msg.payload = 23.5
|
|
344
|
+
msg.topic = "Sensor A"
|
|
345
|
+
|
|
346
|
+
// Object with explicit timestamp and value
|
|
347
|
+
msg.payload = { time: Date.now(), value: 23.5 }
|
|
348
|
+
// With chart config: x → key "time", y → key "value"
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
**Data format for bar chart:**
|
|
352
|
+
```javascript
|
|
353
|
+
// Array of objects
|
|
354
|
+
msg.payload = [
|
|
355
|
+
{ category: "A", value: 10 },
|
|
356
|
+
{ category: "B", value: 25 },
|
|
357
|
+
{ category: "C", value: 15 }
|
|
358
|
+
]
|
|
359
|
+
// Config: x → "category", y → "value", xAxisType: "categorical"
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
**Clearing chart data:** Send `msg.payload = []` to clear all data.
|
|
363
|
+
|
|
364
|
+
**Dynamic chart options via msg.ui_update:**
|
|
365
|
+
```javascript
|
|
366
|
+
msg.ui_update = {
|
|
367
|
+
chartOptions: {
|
|
368
|
+
title: { textStyle: { fontSize: 20 } },
|
|
369
|
+
yAxis: { position: "right" }
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
// Full eCharts option object supported — changes are additive
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### ui-gauge
|
|
376
|
+
|
|
377
|
+
`type: "ui-gauge"` — Gauge display with multiple visual types and color-coded segments.
|
|
378
|
+
|
|
379
|
+
| Property | Type | Description |
|
|
380
|
+
|----------|------|-------------|
|
|
381
|
+
| `group` | string | Parent `ui-group` config node ID |
|
|
382
|
+
| `label` | string | Label above the gauge |
|
|
383
|
+
| `gtype` | string | `"gauge-tile"`, `"gauge-battery"`, `"gauge-tank"`, `"gauge-half"`, `"gauge-34"` |
|
|
384
|
+
| `gstyle` | string | `"needle"` or `"rounded"` (for half/34 gauges) |
|
|
385
|
+
| `value` | string/expression | Value source: `msg.payload`, `msg.<property>`, JSONata expression, or static |
|
|
386
|
+
| `min` | number | Minimum value |
|
|
387
|
+
| `max` | number | Maximum value |
|
|
388
|
+
| `segments` | array | `[{ color: "#hex", from: number }]` — color bands on the arc |
|
|
389
|
+
| `prefix` | string | Text before the value (half/34 only) |
|
|
390
|
+
| `suffix` | string | Text after the value (half/34 only) |
|
|
391
|
+
| `units` | string | Small units text below the value (half/34 only) |
|
|
392
|
+
| `icon` | string | Material Design icon below the value (half/34 only) |
|
|
393
|
+
| `alwaysShowLabel` | boolean | Always show label on tile gauges |
|
|
394
|
+
| `floatingLabelPosition` | string | `"top-left"`, `"top-right"`, `"bottom-left"`, `"bottom-right"` (tile only) |
|
|
395
|
+
|
|
396
|
+
**Wiring example — real-time value display:**
|
|
397
|
+
```
|
|
398
|
+
// Create gauge
|
|
399
|
+
create-node(type: "ui-gauge", name: "CPU Usage", properties: {
|
|
400
|
+
group: "<uiGroupId>",
|
|
401
|
+
label: "CPU",
|
|
402
|
+
gtype: "gauge-half",
|
|
403
|
+
gstyle: "rounded",
|
|
404
|
+
min: 0,
|
|
405
|
+
max: 100,
|
|
406
|
+
units: "%",
|
|
407
|
+
segments: [
|
|
408
|
+
{ color: "#4caf50", from: 0 },
|
|
409
|
+
{ color: "#ff9800", from: 60 },
|
|
410
|
+
{ color: "#f44336", from: 80 }
|
|
411
|
+
]
|
|
412
|
+
})
|
|
413
|
+
|
|
414
|
+
// Wire: inject (sensor) → gauge
|
|
415
|
+
connect-nodes(fromNodeId: "<injectId>", outputPort: 0, toNodeId: "<gaugeId>")
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
**Data IN:** Send numeric `msg.payload` to update the gauge value. Use JSONata on the `value` property to format (e.g., `$round(payload, 1)` for 1 decimal).
|
|
419
|
+
|
|
420
|
+
**Dynamic updates via msg.ui_update:**
|
|
421
|
+
```javascript
|
|
422
|
+
msg.ui_update = {
|
|
423
|
+
label: "Memory",
|
|
424
|
+
min: 0,
|
|
425
|
+
max: 64,
|
|
426
|
+
units: "GB",
|
|
427
|
+
segments: [{ color: "#2196f3", from: 0 }]
|
|
428
|
+
}
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### ui-slider
|
|
432
|
+
|
|
433
|
+
`type: "ui-slider"` — Numeric slider with configurable range, ticks, and icons.
|
|
434
|
+
|
|
435
|
+
| Property | Type | Description |
|
|
436
|
+
|----------|------|-------------|
|
|
437
|
+
| `group` | string | Parent `ui-group` config node ID |
|
|
438
|
+
| `label` | string | Label to the left of the slider (HTML allowed) |
|
|
439
|
+
| `min` | number | Minimum value |
|
|
440
|
+
| `max` | number | Maximum value |
|
|
441
|
+
| `step` | number | Step increment |
|
|
442
|
+
| `thumbLabel` | string | `"true"`, `"false"`, `"always"` — when to show the thumb value |
|
|
443
|
+
| `showTicks` | string | `"true"`, `"false"`, `"always"` — when to show ticks |
|
|
444
|
+
| `color` | string | Main slider/thumb color |
|
|
445
|
+
| `colorTrack` | string | Track color |
|
|
446
|
+
| `colorThumb` | string | Thumb/handle color |
|
|
447
|
+
| `iconPrepend` | string | Material icon before the slider |
|
|
448
|
+
| `iconAppend` | string | Material icon after the slider |
|
|
449
|
+
| `output` | string | `"onChange"` (emit while sliding) or `"onRelease"` (emit on release) |
|
|
450
|
+
| `showTextField` | boolean | Show a text input alongside for direct value entry |
|
|
451
|
+
|
|
452
|
+
**Wiring example — slider controls a chart:**
|
|
453
|
+
```
|
|
454
|
+
// Wire: slider → chart
|
|
455
|
+
connect-nodes(fromNodeId: "<sliderId>", outputPort: 0, toNodeId: "<chartId>")
|
|
456
|
+
// Moving the slider sends its value as msg.payload to the chart
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
**Data IN (set value):** Send numeric `msg.payload` to programmatically set the slider position.
|
|
460
|
+
|
|
461
|
+
**Data OUT (user interaction):** Slider emits `msg.payload` = current numeric value whenever the user moves it.
|
|
462
|
+
|
|
463
|
+
### ui-table
|
|
464
|
+
|
|
465
|
+
`type: "ui-table"` — Data table with sorting, pagination, search, and rich cell types.
|
|
466
|
+
|
|
467
|
+
| Property | Type | Description |
|
|
468
|
+
|----------|------|-------------|
|
|
469
|
+
| `group` | string | Parent `ui-group` config node ID |
|
|
470
|
+
| `label` | string | Table title |
|
|
471
|
+
| `maxRows` | number | Max rows per page (0 = no pagination) |
|
|
472
|
+
| `selection` | string | `"none"`, `"click"` (select row), `"checkbox"` (multi-select) |
|
|
473
|
+
| `showSearch` | boolean | Show search/filter bar |
|
|
474
|
+
| `autoColumns` | boolean | Auto-detect columns from data keys |
|
|
475
|
+
| `columns` | array | Manual column definitions with `{ value, label, width, align, type }` |
|
|
476
|
+
| `breakpoint` | string/number | `"xs"`, `"sm"`, `"md"`, `"lg"`, px value, or `"none"` for responsive card mode |
|
|
477
|
+
| `deselect` | boolean | Auto-deselect when data is replaced |
|
|
478
|
+
|
|
479
|
+
**Cell types:** `text`, `html`, `link`, `color`, `tick-cross`, `progress`, `sparkline-trend`, `sparkline-bar`, `button`, `row-number`, `image`.
|
|
480
|
+
|
|
481
|
+
**Wiring example — display API data:**
|
|
482
|
+
```
|
|
483
|
+
// Create table
|
|
484
|
+
create-node(type: "ui-table", name: "Device List", properties: {
|
|
485
|
+
group: "<uiGroupId>",
|
|
486
|
+
label: "Devices",
|
|
487
|
+
maxRows: 10,
|
|
488
|
+
showSearch: true,
|
|
489
|
+
autoColumns: true
|
|
490
|
+
})
|
|
491
|
+
|
|
492
|
+
// Wire: http request → table
|
|
493
|
+
connect-nodes(fromNodeId: "<httpReqId>", outputPort: 0, toNodeId: "<tableId>")
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
**Data IN format:**
|
|
497
|
+
```javascript
|
|
498
|
+
// Array of objects — keys become columns
|
|
499
|
+
msg.payload = [
|
|
500
|
+
{ id: 1, name: "Sensor A", status: "active", temp: 23.5 },
|
|
501
|
+
{ id: 2, name: "Sensor B", status: "inactive", temp: 0 }
|
|
502
|
+
]
|
|
503
|
+
|
|
504
|
+
// Single object — appended to existing data
|
|
505
|
+
msg.payload = { id: 3, name: "Sensor C", status: "active", temp: 19.2 }
|
|
506
|
+
|
|
507
|
+
// Empty array — clear all data
|
|
508
|
+
msg.payload = []
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
**Data OUT (row interaction):**
|
|
512
|
+
```javascript
|
|
513
|
+
// On row click (selection: "click")
|
|
514
|
+
msg.payload = { id: 1, name: "Sensor A", ... } // full row object
|
|
515
|
+
msg.action = "row_click"
|
|
516
|
+
|
|
517
|
+
// On checkbox selection (selection: "checkbox")
|
|
518
|
+
msg.payload = [{ id: 1, ... }, { id: 2, ... }] // array of selected rows
|
|
519
|
+
msg.action = "multiselect"
|
|
520
|
+
|
|
521
|
+
// On button cell click
|
|
522
|
+
msg.payload = { id: 1, ... } // full row object
|
|
523
|
+
msg.column = "columnKey"
|
|
524
|
+
msg.action = "button_click"
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
### ui-form
|
|
528
|
+
|
|
529
|
+
`type: "ui-form"` — Multi-field form that collects user input and emits it as an object on submit.
|
|
530
|
+
|
|
531
|
+
| Property | Type | Description |
|
|
532
|
+
|----------|------|-------------|
|
|
533
|
+
| `group` | string | Parent `ui-group` config node ID |
|
|
534
|
+
| `label` | string | Form title |
|
|
535
|
+
| `options` | array | Form field definitions: `[{ label, name, type, required }]` |
|
|
536
|
+
| `buttons` | object | `{ submit: "Submit", cancel: "Cancel" }` — omit cancel text to hide cancel button |
|
|
537
|
+
| `twoColumns` | boolean | Render form in two-column layout |
|
|
538
|
+
| `resetOnSubmit` | boolean | Clear form after submission |
|
|
539
|
+
| `topic` | string | `msg.topic` value on submit |
|
|
540
|
+
|
|
541
|
+
**Field types:** `text`, `multiline`, `password`, `email`, `number`, `checkbox`, `switch`, `date`, `time`, `dropdown`.
|
|
542
|
+
|
|
543
|
+
**Wiring example — form submission processing:**
|
|
544
|
+
```
|
|
545
|
+
// Create form
|
|
546
|
+
create-node(type: "ui-form", name: "User Registration", properties: {
|
|
547
|
+
group: "<uiGroupId>",
|
|
548
|
+
label: "Register",
|
|
549
|
+
options: [
|
|
550
|
+
{ label: "Name", name: "name", type: "text", required: true },
|
|
551
|
+
{ label: "Email", name: "email", type: "email", required: true },
|
|
552
|
+
{ label: "Age", name: "age", type: "number" },
|
|
553
|
+
{ label: "Subscribe", name: "newsletter", type: "checkbox" }
|
|
554
|
+
],
|
|
555
|
+
buttons: { submit: "Register", cancel: "Cancel" },
|
|
556
|
+
resetOnSubmit: true
|
|
557
|
+
})
|
|
558
|
+
|
|
559
|
+
// Wire: form → function (process) → debug
|
|
560
|
+
connect-nodes(fromNodeId: "<formId>", outputPort: 0, toNodeId: "<functionId>")
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
**Data OUT:** On submit, `msg.payload` = `{ name: "John", email: "john@example.com", age: 30, newsletter: true }`.
|
|
564
|
+
|
|
565
|
+
**Data IN (pre-fill):** Send an object to the form's input to pre-fill fields:
|
|
566
|
+
```javascript
|
|
567
|
+
msg.payload = { name: "John", email: "john@example.com" }
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
### ui-dropdown
|
|
571
|
+
|
|
572
|
+
`type: "ui-dropdown"` — Dropdown select with single or multi-select support.
|
|
573
|
+
|
|
574
|
+
| Property | Type | Description |
|
|
575
|
+
|----------|------|-------------|
|
|
576
|
+
| `group` | string | Parent `ui-group` config node ID |
|
|
577
|
+
| `label` | string | Label text (HTML allowed) |
|
|
578
|
+
| `options` | array | `[{ label: "Display", value: "val" }]` — can also be array of strings |
|
|
579
|
+
| `multiple` | boolean | Allow multi-selection |
|
|
580
|
+
| `chips` | boolean | Show selected items as chips (multi-select) |
|
|
581
|
+
| `clearable` | boolean | Show clear button |
|
|
582
|
+
| `allowSearch` | boolean | Enable text search/filter |
|
|
583
|
+
| `msgTrigger` | string | `"onChange"` or `"onClose"` — when to emit the value |
|
|
584
|
+
|
|
585
|
+
**Wiring example:**
|
|
586
|
+
```
|
|
587
|
+
create-node(type: "ui-dropdown", name: "Device Selector", properties: {
|
|
588
|
+
group: "<uiGroupId>",
|
|
589
|
+
label: "Select Device",
|
|
590
|
+
options: [
|
|
591
|
+
{ label: "Sensor A", value: "sensor_a" },
|
|
592
|
+
{ label: "Sensor B", value: "sensor_b" },
|
|
593
|
+
{ label: "Sensor C", value: "sensor_c" }
|
|
594
|
+
]
|
|
595
|
+
})
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
**Data OUT:** On selection, `msg.payload` = the selected option's `value` (or array of values for multi-select).
|
|
599
|
+
|
|
600
|
+
**Data IN (programmatic selection):**
|
|
601
|
+
```javascript
|
|
602
|
+
// Single select
|
|
603
|
+
msg.payload = "sensor_a"
|
|
604
|
+
|
|
605
|
+
// Multi-select
|
|
606
|
+
msg.payload = ["sensor_a", "sensor_b"]
|
|
607
|
+
|
|
608
|
+
// Clear selection
|
|
609
|
+
msg.payload = []
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
### ui-text
|
|
613
|
+
|
|
614
|
+
`type: "ui-text"` — Displays a text value that updates with each received `msg.payload`. Supports HTML and JSONata formatting.
|
|
615
|
+
|
|
616
|
+
| Property | Type | Description |
|
|
617
|
+
|----------|------|-------------|
|
|
618
|
+
| `group` | string | Parent `ui-group` config node ID |
|
|
619
|
+
| `label` | string | Optional label above/beside the text |
|
|
620
|
+
| `layout` | string | `"row-left"`, `"row-center"`, `"row-right"`, `"row-spread"`, `"col-center"` |
|
|
621
|
+
| `value` | string/expression | Value source: `msg.payload`, `msg.<property>`, JSONata expression, or static |
|
|
622
|
+
| `font` | string | Font family (if custom style enabled) |
|
|
623
|
+
| `fontSize` | string | Font size (if custom style enabled) |
|
|
624
|
+
| `color` | string | Text color (if custom style enabled) |
|
|
625
|
+
|
|
626
|
+
**Wiring example:**
|
|
627
|
+
```
|
|
628
|
+
create-node(type: "ui-text", name: "Status Display", properties: {
|
|
629
|
+
group: "<uiGroupId>",
|
|
630
|
+
label: "System Status",
|
|
631
|
+
layout: "row-left"
|
|
632
|
+
})
|
|
633
|
+
|
|
634
|
+
// Wire any source → ui-text
|
|
635
|
+
connect-nodes(fromNodeId: "<sourceId>", outputPort: 0, toNodeId: "<textId>")
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
**Data IN:** Send `msg.payload` to update the displayed text. HTML is rendered if the payload contains HTML tags. Use JSONata on the `value` property to format (e.g., `$round(payload, 1)`).
|
|
639
|
+
|
|
640
|
+
**Dynamic update:**
|
|
641
|
+
```javascript
|
|
642
|
+
msg.ui_update = { label: "Temperature", color: "#e53935", fontSize: "24px" }
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
### ui-switch
|
|
646
|
+
|
|
647
|
+
`type: "ui-switch"` — Toggle switch that emits configurable on/off payload values.
|
|
648
|
+
|
|
649
|
+
| Property | Type | Description |
|
|
650
|
+
|----------|------|-------------|
|
|
651
|
+
| `group` | string | Parent `ui-group` config node ID |
|
|
652
|
+
| `label` | string | Label text |
|
|
653
|
+
| `layout` | string | `"row-left"`, `"row-left-reverse"`, `"row-spread"`, `"row-spread-reverse"` |
|
|
654
|
+
| `clickableArea` | string | `"switch"` (only the switch) or `"full"` (label + switch) |
|
|
655
|
+
| `onIcon` | string | Material icon when on |
|
|
656
|
+
| `offIcon` | string | Material icon when off |
|
|
657
|
+
| `onColor` | string | Icon color when on |
|
|
658
|
+
| `offColor` | string | Icon color when off |
|
|
659
|
+
| `onPayload` | string/number/boolean | Value emitted when switched on |
|
|
660
|
+
| `offPayload` | string/number/boolean | Value emitted when switched off |
|
|
661
|
+
| `onPayloadType` | string | `"str"`, `"num"`, `"json"`, `"bool"` |
|
|
662
|
+
| `offPayloadType` | string | `"str"`, `"num"`, `"json"`, `"bool"` |
|
|
663
|
+
| `passthru` | boolean | If true, passes input msg through to output |
|
|
664
|
+
| `decouple` | boolean | If true (non-passthrough), switch shows output state, not input state |
|
|
665
|
+
|
|
666
|
+
**Wiring example — switch controls an output:**
|
|
667
|
+
```
|
|
668
|
+
create-node(type: "ui-switch", name: "Enable Pump", properties: {
|
|
669
|
+
group: "<uiGroupId>",
|
|
670
|
+
label: "Pump",
|
|
671
|
+
onPayload: true,
|
|
672
|
+
onPayloadType: "bool",
|
|
673
|
+
offPayload: false,
|
|
674
|
+
offPayloadType: "bool"
|
|
675
|
+
})
|
|
676
|
+
|
|
677
|
+
// Wire: switch → function (control logic)
|
|
678
|
+
connect-nodes(fromNodeId: "<switchId>", outputPort: 0, toNodeId: "<functionId>")
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
**Data OUT:** On toggle, `msg.payload` = onPayload or offPayload.
|
|
682
|
+
|
|
683
|
+
**Data IN (set state):** Send the matching payload value to programmatically toggle:
|
|
684
|
+
```javascript
|
|
685
|
+
msg.payload = true // switch turns on
|
|
686
|
+
msg.payload = false // switch turns off
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
### ui-notification
|
|
690
|
+
|
|
691
|
+
`type: "ui-notification"` — Toast notification popup. **Unique:** attached to `ui-base` (via `ui` property), not `ui-group`.
|
|
692
|
+
|
|
693
|
+
| Property | Type | Description |
|
|
694
|
+
|----------|------|-------------|
|
|
695
|
+
| `ui` | string | Parent `ui-base` config node ID (NOT a group) |
|
|
696
|
+
| `position` | string | `"top right"`, `"top center"`, `"top left"`, `"bottom right"`, `"bottom center"`, `"bottom left"`, `"center center"` |
|
|
697
|
+
| `color` | string | Notification border/header color |
|
|
698
|
+
| `displayTime` | number | Auto-close timeout in seconds (0 = indefinite) |
|
|
699
|
+
| `showCountdown` | boolean | Show countdown progress bar |
|
|
700
|
+
| `allowDismiss` | boolean | Show dismiss button |
|
|
701
|
+
| `dismissText` | string | Dismiss button label |
|
|
702
|
+
| `allowConfirm` | boolean | Show confirm button |
|
|
703
|
+
| `confirmText` | string | Confirm button label |
|
|
704
|
+
| `raw` | boolean | If true, `msg.payload` is treated as raw HTML |
|
|
705
|
+
|
|
706
|
+
**Wiring example — alert on threshold:**
|
|
707
|
+
```
|
|
708
|
+
create-node(type: "ui-notification", name: "Alerts", properties: {
|
|
709
|
+
ui: "<uiBaseId>",
|
|
710
|
+
position: "top right",
|
|
711
|
+
color: "#f44336",
|
|
712
|
+
displayTime: 5,
|
|
713
|
+
showCountdown: true,
|
|
714
|
+
allowDismiss: true
|
|
715
|
+
})
|
|
716
|
+
|
|
717
|
+
// Wire: function (threshold check) → notification
|
|
718
|
+
connect-nodes(fromNodeId: "<functionId>", outputPort: 0, toNodeId: "<notifId>")
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
**Data IN:** Send `msg.payload` as the notification message text (or HTML if `raw: true`).
|
|
722
|
+
|
|
723
|
+
**Sending to all clients:** By default, notifications target specific clients via `msg._client`. To broadcast to all connected clients, delete `msg._client` before sending:
|
|
724
|
+
```javascript
|
|
725
|
+
delete msg._client
|
|
726
|
+
msg.payload = "⚠️ System Alert: Temperature exceeds threshold!"
|
|
727
|
+
return msg
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
**Data OUT:** When user clicks confirm or dismiss, the notification emits `msg.payload` with the action taken.
|
|
731
|
+
|
|
732
|
+
---
|
|
733
|
+
|
|
734
|
+
## Wiring Patterns
|
|
735
|
+
|
|
736
|
+
### Data INTO Widgets (Display/Update)
|
|
737
|
+
|
|
738
|
+
Most display widgets accept `msg.payload` on their input to update their content:
|
|
739
|
+
|
|
740
|
+
```
|
|
741
|
+
sensor/inject → [function/transform] → widget
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
- **Gauge/Text/Progress:** Wire any source directly. `msg.payload` numeric or string updates the display.
|
|
745
|
+
- **Chart:** Wire data source. `msg.payload` can be a single value (auto-timestamped) or an object/array with x/y keys.
|
|
746
|
+
- **Table:** Wire data source. `msg.payload` as array of objects or single object to append.
|
|
747
|
+
- **Notification:** Wire event source. `msg.payload` is the message text/HTML.
|
|
748
|
+
|
|
749
|
+
**msg.topic for targeted updates:** When multiple widgets share a data bus, use `msg.topic` to differentiate. For charts, `msg.topic` controls series grouping. For text/gauge, `msg.topic` can be used in switch nodes upstream to route data.
|
|
750
|
+
|
|
751
|
+
### Data OUT of Widgets (User Interaction)
|
|
752
|
+
|
|
753
|
+
Interactive widgets emit `msg.payload` from their output when the user interacts:
|
|
754
|
+
|
|
755
|
+
```
|
|
756
|
+
widget → [function/switch/process] → ...
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
- **Button:** Emits configured payload on click.
|
|
760
|
+
- **Switch:** Emits `onPayload`/`offPayload` on toggle.
|
|
761
|
+
- **Slider:** Emits numeric value as user slides.
|
|
762
|
+
- **Dropdown:** Emits selected `value` (or array for multi-select).
|
|
763
|
+
- **Form:** Emits `{ fieldName: value, ... }` on submit.
|
|
764
|
+
- **Table:** Emits row object on click/selection, with `msg.action` and `msg.column`.
|
|
765
|
+
|
|
766
|
+
### Chaining Widgets
|
|
767
|
+
|
|
768
|
+
Widgets can be chained together to create interactive dashboards:
|
|
769
|
+
|
|
770
|
+
```
|
|
771
|
+
slider → chart // slider value drives chart
|
|
772
|
+
button → control // button click shows/hides groups
|
|
773
|
+
dropdown → function // selection filters data
|
|
774
|
+
form → http request // form data posted to API
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
### Multi-Tenancy (Per-User Data)
|
|
778
|
+
|
|
779
|
+
Dashboard 2.0 supports per-user data via `msg._client`. Use the Dashboard 2.0 sidebar in Node-RED to enable "Accept Client Data" on specific widget types. When enabled:
|
|
780
|
+
- Every `msg` from that widget contains `msg._client` with the user's `socketId`
|
|
781
|
+
- Sending a `msg` with `_client` back targets only that user
|
|
782
|
+
- Delete `msg._client` to broadcast to all users
|
|
783
|
+
|
|
784
|
+
---
|
|
785
|
+
|
|
786
|
+
## Recipes
|
|
787
|
+
|
|
788
|
+
### Recipe: Live Chart from a Data Source
|
|
789
|
+
|
|
790
|
+
**Goal:** Display a real-time line chart from a sensor or periodic data source.
|
|
791
|
+
|
|
792
|
+
**Nodes:** `inject` (sensor simulator) → `ui-chart`
|
|
793
|
+
|
|
794
|
+
**Steps:**
|
|
795
|
+
1. Create config hierarchy (theme is REQUIRED):
|
|
796
|
+
```
|
|
797
|
+
baseId = create-node(type: "ui-base", name: "Dashboard", properties: { path: "/dashboard" })
|
|
798
|
+
themeId = create-node(type: "ui-theme", name: "Dashboard Theme", properties: {
|
|
799
|
+
colors: { surface: "#ffffff", primary: "#0094ce", bgPage: "#eeeeee", groupBg: "#ffffff", groupOutline: "#cccccc" },
|
|
800
|
+
sizes: { density: "default", pagePadding: "12px", groupGap: "12px", groupBorderRadius: "4px", widgetGap: "12px" }
|
|
801
|
+
})
|
|
802
|
+
pageId = create-node(type: "ui-page", name: "Monitoring", properties: {
|
|
803
|
+
ui: baseId, path: "/monitor", layout: "grid", theme: themeId
|
|
804
|
+
})
|
|
805
|
+
groupId = create-node(type: "ui-group", name: "Charts", properties: { page: pageId, width: 12 })
|
|
806
|
+
```
|
|
807
|
+
2. Create chart:
|
|
808
|
+
```
|
|
809
|
+
chartId = create-node(type: "ui-chart", name: "Live Readings", properties: {
|
|
810
|
+
group: groupId,
|
|
811
|
+
label: "Sensor Values",
|
|
812
|
+
chartType: "line",
|
|
813
|
+
xAxisType: "timescale",
|
|
814
|
+
series: "msg.topic",
|
|
815
|
+
action: "append",
|
|
816
|
+
xAxisLimit: 300
|
|
817
|
+
})
|
|
818
|
+
```
|
|
819
|
+
3. Create inject (or wire existing sensor):
|
|
820
|
+
```
|
|
821
|
+
sourceId = create-node(type: "inject", name: "Sensor Sim", properties: {
|
|
822
|
+
payload: "23.5",
|
|
823
|
+
payloadType: "num",
|
|
824
|
+
topic: "Temperature",
|
|
825
|
+
repeat: "5"
|
|
826
|
+
})
|
|
827
|
+
```
|
|
828
|
+
4. Wire and deploy:
|
|
829
|
+
```
|
|
830
|
+
connect-nodes(fromNodeId: sourceId, outputPort: 0, toNodeId: chartId)
|
|
831
|
+
deploy()
|
|
832
|
+
```
|
|
833
|
+
5. Open `http://<nodered-host>:1880/dashboard/monitor` to view.
|
|
834
|
+
|
|
835
|
+
### Recipe: Button-Triggered Action
|
|
836
|
+
|
|
837
|
+
**Goal:** A button that triggers a function node to perform an action (API call, MQTT publish, etc.).
|
|
838
|
+
|
|
839
|
+
**Nodes:** `ui-button` → `function` → `debug` (or `http request`)
|
|
840
|
+
|
|
841
|
+
**Steps:**
|
|
842
|
+
1. Ensure config hierarchy exists (base → page → group). Create if needed.
|
|
843
|
+
2. Create button:
|
|
844
|
+
```
|
|
845
|
+
buttonId = create-node(type: "ui-button", name: "Refresh Data", properties: {
|
|
846
|
+
group: "<groupId>",
|
|
847
|
+
label: "Refresh",
|
|
848
|
+
icon: "refresh",
|
|
849
|
+
color: "blue",
|
|
850
|
+
payload: "refresh",
|
|
851
|
+
payloadType: "str"
|
|
852
|
+
})
|
|
853
|
+
```
|
|
854
|
+
3. Create processing function:
|
|
855
|
+
```
|
|
856
|
+
fnId = create-node(type: "function", name: "Fetch Data", properties: {
|
|
857
|
+
func: "// Fetch data from API\n// msg.payload already = 'refresh' from button\nreturn msg;",
|
|
858
|
+
outputs: 1
|
|
859
|
+
})
|
|
860
|
+
```
|
|
861
|
+
4. Wire:
|
|
862
|
+
```
|
|
863
|
+
connect-nodes(fromNodeId: buttonId, outputPort: 0, toNodeId: fnId)
|
|
864
|
+
```
|
|
865
|
+
5. Deploy and test — clicking the button triggers the function.
|
|
866
|
+
|
|
867
|
+
### Recipe: Form Submission Processing
|
|
868
|
+
|
|
869
|
+
**Goal:** Collect user input via a form and process it in Node-RED.
|
|
870
|
+
|
|
871
|
+
**Nodes:** `ui-form` → `function` (validate/process) → `debug` + `http request`
|
|
872
|
+
|
|
873
|
+
**Steps:**
|
|
874
|
+
1. Create form:
|
|
875
|
+
```
|
|
876
|
+
formId = create-node(type: "ui-form", name: "Contact Form", properties: {
|
|
877
|
+
group: "<groupId>",
|
|
878
|
+
label: "Contact Us",
|
|
879
|
+
options: [
|
|
880
|
+
{ label: "Name", name: "name", type: "text", required: true },
|
|
881
|
+
{ label: "Message", name: "message", type: "multiline", required: true }
|
|
882
|
+
],
|
|
883
|
+
buttons: { submit: "Send", cancel: "Clear" },
|
|
884
|
+
resetOnSubmit: true
|
|
885
|
+
})
|
|
886
|
+
```
|
|
887
|
+
2. Create processing function:
|
|
888
|
+
```
|
|
889
|
+
fnId = create-node(type: "function", name: "Process Form", properties: {
|
|
890
|
+
func: "// msg.payload = { name: '...', message: '...' }\nif (!msg.payload.name || !msg.payload.message) {\n return null; // drop invalid\n}\nmsg.topic = 'contact-form';\nreturn msg;",
|
|
891
|
+
outputs: 1
|
|
892
|
+
})
|
|
893
|
+
```
|
|
894
|
+
3. Wire:
|
|
895
|
+
```
|
|
896
|
+
connect-nodes(fromNodeId: formId, outputPort: 0, toNodeId: fnId)
|
|
897
|
+
```
|
|
898
|
+
4. Deploy. On form submit, `msg.payload` contains `{ name: "User", message: "Hello" }`.
|
|
899
|
+
|
|
900
|
+
### Recipe: Gauge Monitoring a Real-Time Value
|
|
901
|
+
|
|
902
|
+
**Goal:** Display a live gauge that updates from a sensor or data source.
|
|
903
|
+
|
|
904
|
+
**Nodes:** `inject` (or data source) → `ui-gauge`
|
|
905
|
+
|
|
906
|
+
**Steps:**
|
|
907
|
+
1. Create gauge:
|
|
908
|
+
```
|
|
909
|
+
gaugeId = create-node(type: "ui-gauge", name: "CPU Load", properties: {
|
|
910
|
+
group: "<groupId>",
|
|
911
|
+
label: "CPU",
|
|
912
|
+
gtype: "gauge-half",
|
|
913
|
+
gstyle: "needle",
|
|
914
|
+
min: 0,
|
|
915
|
+
max: 100,
|
|
916
|
+
units: "%",
|
|
917
|
+
segments: [
|
|
918
|
+
{ color: "#4caf50", from: 0 },
|
|
919
|
+
{ color: "#ff9800", from: 60 },
|
|
920
|
+
{ color: "#f44336", from: 80 }
|
|
921
|
+
]
|
|
922
|
+
})
|
|
923
|
+
```
|
|
924
|
+
2. Wire data source to gauge:
|
|
925
|
+
```
|
|
926
|
+
connect-nodes(fromNodeId: "<sourceId>", outputPort: 0, toNodeId: gaugeId)
|
|
927
|
+
```
|
|
928
|
+
3. Deploy. Send numeric `msg.payload` values to the gauge to update the display.
|
|
929
|
+
|
|
930
|
+
---
|
|
931
|
+
|
|
932
|
+
## References
|
|
933
|
+
|
|
934
|
+
- **Official Documentation:** https://dashboard.flowfuse.com/
|
|
935
|
+
- **Getting Started Guide:** https://dashboard.flowfuse.com/getting-started.html
|
|
936
|
+
- **Widget Catalog:** https://dashboard.flowfuse.com/nodes/widgets.html
|
|
937
|
+
- **Config Nodes:** https://dashboard.flowfuse.com/nodes/config/ui-base.html
|
|
938
|
+
- **GitHub Repository:** https://github.com/FlowFuse/node-red-dashboard
|
|
939
|
+
- **npm Package:** `@flowfuse/node-red-dashboard` (documented at v1.30.2)
|
|
940
|
+
|
|
941
|
+
> **Version note:** This skill documents `@flowfuse/node-red-dashboard` v1.30.2. If the installed version differs significantly, consult the official docs for the latest widget properties and features. Use `get-node-type-detail` for runtime property discovery on any widget type.
|