BetterFrame/nodered
Mitchell R 55b11f2ffa
Some checks are pending
release / meta (push) Waiting to run
release / build (push) Blocked by required conditions
fix: Node-RED event forwarding + parse ONVIF Key section (PlateNumber)
Server bridge was forwarding to raw topic paths that no Node-RED node
listens on. Now forwards to fixed routes: camera.event, onvif.event,
onvif.motion, onvif.anpr — matching what trigger nodes register.

ONVIF XML parser now extracts Key section SimpleItems (PlateNumber,
etc.) into the data map alongside Data section items. Previously only
parsed Source and Data, missing Key-section fields like plate numbers.

Node-RED trigger nodes: camera_id filter changed from Number() to
String() comparison for UUIDv7 compatibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-26 15:38:30 +02:00
..
icons feat: Node-RED custom nodes + dashboard entity type 2026-05-13 01:47:53 +02:00
src fix: Node-RED event forwarding + parse ONVIF Key section (PlateNumber) 2026-05-26 15:38:30 +02:00
package.json feat(nodered): motion + ANPR + generic ONVIF event trigger nodes 2026-05-23 02:17:05 +02:00
README.md fix: trigger nodes self-register + move to angie-blocked path 2026-05-13 02:42:37 +02:00

@betterframe/nodered-nodes

BetterFrame integration nodes for Node-RED. Drag-and-droppable nodes for the BetterFrame admin REST API and kiosk event ingest.

Nodes

Node Category Purpose
bf-server-config config Shared server URL + admin API key
bf-kiosk-camera-event Triggers Filter incoming kiosk camera events (default camera.*)
bf-trigger-display-power Triggers Fires on display.power.changed
bf-trigger-layout-changed Triggers Fires on layout.changed
bf-trigger-kiosk-changed Triggers Fires on kiosk.changed (connect/disconnect/heartbeat)
bf-trigger-camera-changed Triggers Fires on camera.changed (created/updated/deleted)
bf-trigger-status Triggers Fires on kiosk.status (heartbeat-only telemetry; optional kiosk_id filter)
bf-layout-switch BetterFrame Switch a display's active layout
bf-power BetterFrame Wake / standby a kiosk display
bf-fan BetterFrame Set fan mode (auto/pwm) on a kiosk
bf-cameras BetterFrame Fetch the camera list
bf-config-get BetterFrame Fetch BF state (displays/kiosks/cameras/layouts/entities, by id or full list)
bf-config-set BetterFrame Mutate BF state (default layout, enabled, priority, name)
bf-status BetterFrame Fetch current kiosk state by ID (telemetry, last_seen_at, etc.)
bf-snapshot BetterFrame Fetch a JPEG snapshot for a camera entity (binary Buffer payload)

Authentication

All action/query nodes use an admin-scoped API key created in the BetterFrame admin UI. The key is sent as Authorization: Bearer bf-.... Configure once on a bf-server-config node and reference it from the others.

Event ingest path

Trigger nodes are self-contained — each one registers its own POST /in/<topic> handler on Node-RED's user-facing HTTP server (via RED.httpNode.post) when the flow is deployed. You do not need to wire an upstream http in node anymore.

The BetterFrame server's nodered-bridge.forward(topic, payload) posts events directly to http://<nodered-host>:1880/in/<topic>. Each trigger node listens on its own fixed topic:

Node Internal route
bf-trigger-display-power POST /in/display.power.changed
bf-trigger-layout-changed POST /in/layout.changed
bf-trigger-kiosk-changed POST /in/kiosk.changed
bf-trigger-camera-changed POST /in/camera.changed
bf-trigger-status POST /in/kiosk.status
bf-kiosk-camera-event POST /in/camera.event

The server emits these topics from coordinator-ws (kiosk WS lifecycle) and the admin routes (layout/power/camera mutations). Multiple instances of the same trigger node on the same canvas are fine — Express runs all matching handlers in registration order.

If the Angie proxy fronts Node-RED, the otherwise-unmatched root paths (public HTTP-in URLs) and the /in/kiosk/<topic> (kiosk-key gated) / /in/public/<topic> (public, rate-limited) routes are still available for user-authored flows that use stock http in nodes — those layers strip the prefix before proxying. The trigger nodes' fixed /in/<topic> routes are reserved for internal server-to-Node-RED delivery and are not exposed through the proxy's gated surfaces by default.

Each trigger node also offers an optional ID filter (display_id / kiosk_id / camera_id) so you can drop one node per entity without a downstream switch.

Installation

Dev (single-host BetterFrame install)

# Symlink the package into Node-RED's user dir so edits hot-reload.
ln -s "$(pwd)/nodered" ~/.node-red/node_modules/@betterframe/nodered-nodes
# Restart Node-RED.

Docker compose

The compose stack mounts nodered-data as /data. Either:

  • bake the package into the Node-RED image by extending the Dockerfile with npm install /repo/nodered, or
  • mount ./nodered into /data/node_modules/@betterframe/nodered-nodes and restart the container.