Replaces the prior stub/partial wiki with a Home + Reference-{Architecture,
Contracts,Examples,Limitations} + _Sidebar structure. Topic-contract and
data-model sections wrapped in AUTOGEN markers for the future wiki-gen tool.
Source-vs-spec contradictions surfaced and flagged inline (not silently
fixed). Pending-review notes mark sections that need a full node review.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8.3 KiB
dashboardAPI
A dashboardAPI node converts EVOLV node topology into Grafana dashboards. On each inbound child.register event it resolves the child source, walks its direct children, loads per-softwareType Grafana JSON templates from config/, and emits one HTTP upsert request per dashboard on Port 0 to a downstream http request node. Sits adjacent to the S88 hierarchy as a passive HTTP emitter — no measurements, no tick loop, no parent registration.
Note
Pending full node review (2026-05). Content reflects
CONTRACT.mdand current source only.
At a glance
| Thing | Value |
|---|---|
| What it represents | Utility bridge between EVOLV topology and Grafana — auto-generates dashboards from child.register events |
| S88 level | Utility — not in the S88 hierarchy; sits adjacent to it |
| Use it when | You want Grafana dashboards to materialise automatically when an EVOLV node graph is deployed |
| Don't use it for | Maintaining hand-curated Grafana dashboards (will overwrite); arbitrary Grafana API calls; tick / measurement data plumbing |
| Children it accepts | Any EVOLV node whose nodeSource.config carries functionality.softwareType |
| Parents it talks to | None — dashboardAPI is a passive sink; it does not register with a parent |
How it fits
flowchart LR
ps[pumpingStation<br/>Process Cell]:::pc -.child.register.-> dash
mgc[machineGroupControl<br/>Unit]:::unit -.child.register.-> dash
rm[rotatingMachine<br/>Equipment]:::equip -.child.register.-> dash
meas[measurement<br/>Control Module]:::ctrl -.child.register.-> dash
dash[dashboardAPI<br/>Utility]:::neutral -->|"POST /api/dashboards/db"| http[http request<br/>node-red core]:::neutral
http --> grafana[(Grafana<br/>HTTP API)]
grafana -.renders dashboards for.-> ff[FlowFuse / Browser]
classDef pc fill:#0c99d9,color:#fff
classDef unit fill:#50a8d9,color:#000
classDef equip fill:#86bbdd,color:#000
classDef ctrl fill:#a9daee,color:#000
classDef neutral fill:#dddddd,color:#000
Dashed arrows = inbound child.register events from any EVOLV process node. The solid arrow is the outbound HTTP upsert envelope on Port 0 — emitted once per generated dashboard in the walked graph. S88 colours and the utility-neutral #dddddd are anchored in .claude/rules/node-red-flow-layout.md.
Try it — 3-minute demo
Import the basic example flow, deploy, and watch a child.register payload turn into a Grafana dashboard upsert request.
curl -X POST -H 'Content-Type: application/json' \
--data @nodes/dashboardAPI/examples/basic.flow.json \
http://localhost:1880/flow
What to click after deploy:
- Open the inject node (
basic trigger) and edit the payload to a{source: {config: {...}}}shape — see Reference — Examples for the minimal inline-payload shape. - Fire the inject. Watch the debug pane: one
topic: 'create'HTTP envelope appears per dashboard in the walked graph (root + direct children). - Wire a downstream
http requestnode (methodPOST) to the dashboardAPI output to actually POST the envelope to Grafana.
Important
GIF needed. Demo recording of the inject → Port-0 envelope → Grafana dashboard upsert path. Save as
wiki/_partial-gifs/dashboardAPI/01-basic-demo.gif, target ≤ 1 MB aftergifsicle -O3 --lossy=80.
Warning
The shipped
basic.flow.json/integration.flow.json/edge.flow.jsonare stubs — the inject payloads do not yet conform to thechild.registerresolver's expected shape. They will triggerMissing or invalid child nodeerrors until updated. Tracked in Limitations — Example flow stubs.
The one thing you'll send
| Topic | Aliases | Payload | What it does |
|---|---|---|---|
child.register |
registerChild |
string (child node id) or {source: {...}} or {config: {...}} (optionally msg.includeChildren: boolean, default true) |
Resolves the child source (RED.nodes.getNode → node._flow.getNode → inline payload), calls source.generateDashboardsForGraph(child, {includeChildren}), then emits one topic: 'create' HTTP-upsert message on Port 0 per generated dashboard. |
That's it. There is no set.*, no cmd.*, no query.* — the registry has a single canonical topic (alias-with-deprecation). The legacy registerChild alias logs a one-time deprecation warning on first use.
What you'll see come out
Sample Port 0 message after a child.register for a pumpingStation node with two direct children:
{
"topic": "create",
"url": "http://grafana:3000/api/dashboards/db",
"method": "POST",
"headers": {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": "Bearer eyJ..."
},
"payload": {
"dashboard": { "uid": "a1b2c3d4e5f6", "title": "Pumping Station Demo", "templating": {...} },
"folderId": 0,
"overwrite": true
},
"meta": {
"nodeId": "ps_demo",
"softwareType": "pumpingStation",
"uid": "a1b2c3d4e5f6",
"title": "Pumping Station Demo"
}
}
| Field | Meaning |
|---|---|
topic |
Always 'create' — signals a dashboard-upsert HTTP envelope. |
url |
grafanaUpsertUrl() = <protocol>://<host>:<port>/api/dashboards/db. |
method |
Always POST. |
headers.Authorization |
Present only when bearerToken is configured; omitted otherwise. |
payload.dashboard |
The composed Grafana dashboard JSON (template + templating vars filled in). |
payload.dashboard.uid |
sha1(softwareType:nodeId).slice(0, 12) — stable across re-deploys. |
meta.* |
Correlation fields for the downstream consumer (nodeId, softwareType, uid, title). |
Inbound msg fields propagate via spread ({...msg, ...envelope}) so any caller-supplied correlation / trace fields survive.
Port 1 (InfluxDB telemetry) and Port 2 (registration / control plumbing) are unused — dashboardAPI has no measurements and does not register with a parent. See Reference — Architecture.
The new bit — no BaseNodeAdapter / BaseDomain
Most EVOLV nodes extend BaseNodeAdapter + BaseDomain from generalFunctions/. dashboardAPI does not — per OPEN_QUESTIONS.md (2026-05-10) the decision is to keep a bespoke adapter until BaseNodeAdapter grows passive / HTTP-only flags.
Reasons:
- No
generalFunctions/src/configs/dashboardapi.json—BaseDomain's constructor unconditionally callsconfigManager.getConfig(ctor.name)and would throw. The localdependencies/dashboardapi/dashboardapiConfig.jsonis for the editor menu endpoint, not the runtime config pipeline. - No periodic output —
BaseNodeAdapter._emitOutputs()/outputUtils.formatMsgassumes a delta-compressed Port 0 / 1 stream; dashboardAPI emits HTTP-shaped messages instead. - No registration to a parent —
BaseNodeAdapter._scheduleRegistrationwould emit a spuriouschild.registerof its own. - No status badge / tick / measurements / children of its own.
dashboardAPI uses the shared commandRegistry (canonical-topic naming + alias-with-deprecation) and stops there. See Reference — Architecture for the full rationale.
Need more?
| Page | What you'll find |
|---|---|
| Reference — Contracts | Full topic contract, config schema, child resolution rules, template alias table |
| Reference — Architecture | Code map, HTTP-endpoint lifecycle, template loader, UID stability, graph walk |
| Reference — Examples | Shipped example flows + debug recipes |
| Reference — Limitations | Legacy filename drift, stub flows, missing template handling, open questions |