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> |
||
|---|---|---|
| .. | ||
| icons | ||
| src | ||
| package.json | ||
| README.md | ||
@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
./noderedinto/data/node_modules/@betterframe/nodered-nodesand restart the container.