From 55b11f2ffa79e97ff1a7b45fbb606c3b5591ffd9 Mon Sep 17 00:00:00 2001 From: Mitchell R Date: Tue, 26 May 2026 15:38:30 +0200 Subject: [PATCH] fix: Node-RED event forwarding + parse ONVIF Key section (PlateNumber) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- kiosk/src/onvif_events.rs | 5 +++++ nodered/src/bf-kiosk-camera-event.js | 5 ++--- nodered/src/bf-trigger-anpr.js | 4 ++-- nodered/src/bf-trigger-event.js | 4 ++-- nodered/src/bf-trigger-motion.js | 4 ++-- server/src/plugins/service-api-http/index.ts | 6 +++--- 6 files changed, 16 insertions(+), 12 deletions(-) diff --git a/kiosk/src/onvif_events.rs b/kiosk/src/onvif_events.rs index aef9804..64d60d2 100644 --- a/kiosk/src/onvif_events.rs +++ b/kiosk/src/onvif_events.rs @@ -448,6 +448,11 @@ fn parse_notification_messages(xml: &str) -> Vec { source.insert(name, value); } } + if let Some(key_block) = extract_section(block, "Key") { + for (name, value) in parse_simple_items(&key_block) { + data.insert(name, value); + } + } if let Some(data_block) = extract_section(block, "Data") { for (name, value) in parse_simple_items(&data_block) { data.insert(name, value); diff --git a/nodered/src/bf-kiosk-camera-event.js b/nodered/src/bf-kiosk-camera-event.js index 1857c1e..a560f38 100644 --- a/nodered/src/bf-kiosk-camera-event.js +++ b/nodered/src/bf-kiosk-camera-event.js @@ -26,8 +26,7 @@ module.exports = function (RED) { function BfKioskCameraEventNode(config) { RED.nodes.createNode(this, config); const node = this; - const filterIdRaw = (config.camera_id || "").toString().trim(); - const filterId = filterIdRaw && !isNaN(Number(filterIdRaw)) ? Number(filterIdRaw) : null; + const filterId = (config.camera_id || "").toString().trim() || null; async function handler(req, res) { const body = await readJsonBody(req); @@ -37,7 +36,7 @@ module.exports = function (RED) { const cameraId = body.camera_id !== undefined ? body.camera_id : body.source_camera_id !== undefined ? body.source_camera_id : null; - if (filterId !== null && Number(cameraId) !== filterId) { + if (filterId !== null && String(cameraId) !== filterId) { return res.status(200).end(); } const out = { diff --git a/nodered/src/bf-trigger-anpr.js b/nodered/src/bf-trigger-anpr.js index 7ded382..751e78e 100644 --- a/nodered/src/bf-trigger-anpr.js +++ b/nodered/src/bf-trigger-anpr.js @@ -22,7 +22,7 @@ module.exports = function (RED) { function BfTriggerAnprNode(config) { RED.nodes.createNode(this, config); const node = this; - const filterCam = config.camera_id ? Number(config.camera_id) : null; + const filterCam = config.camera_id ? String(config.camera_id).trim() : null; async function handler(req, res) { const body = await readJsonBody(req); @@ -33,7 +33,7 @@ module.exports = function (RED) { } const cameraId = body.camera_id ?? body.source_camera_id ?? null; - if (filterCam !== null && Number(cameraId) !== filterCam) { + if (filterCam !== null && String(cameraId) !== filterCam) { return res.status(200).end(); } diff --git a/nodered/src/bf-trigger-event.js b/nodered/src/bf-trigger-event.js index 2fa462a..5b6d8a6 100644 --- a/nodered/src/bf-trigger-event.js +++ b/nodered/src/bf-trigger-event.js @@ -15,7 +15,7 @@ module.exports = function (RED) { function BfTriggerEventNode(config) { RED.nodes.createNode(this, config); const node = this; - const filterCam = config.camera_id ? Number(config.camera_id) : null; + const filterCam = config.camera_id ? String(config.camera_id).trim() : null; const filterTopic = (config.topic_filter || "").trim(); async function handler(req, res) { @@ -27,7 +27,7 @@ module.exports = function (RED) { } const cameraId = body.camera_id ?? body.source_camera_id ?? null; - if (filterCam !== null && Number(cameraId) !== filterCam) { + if (filterCam !== null && String(cameraId) !== filterCam) { return res.status(200).end(); } diff --git a/nodered/src/bf-trigger-motion.js b/nodered/src/bf-trigger-motion.js index 8df9b26..f91c9a1 100644 --- a/nodered/src/bf-trigger-motion.js +++ b/nodered/src/bf-trigger-motion.js @@ -22,7 +22,7 @@ module.exports = function (RED) { function BfTriggerMotionNode(config) { RED.nodes.createNode(this, config); const node = this; - const filterCam = config.camera_id ? Number(config.camera_id) : null; + const filterCam = config.camera_id ? String(config.camera_id).trim() : null; async function handler(req, res) { const body = await readJsonBody(req); @@ -34,7 +34,7 @@ module.exports = function (RED) { } const cameraId = body.camera_id ?? body.source_camera_id ?? null; - if (filterCam !== null && Number(cameraId) !== filterCam) { + if (filterCam !== null && String(cameraId) !== filterCam) { return res.status(200).end(); } diff --git a/server/src/plugins/service-api-http/index.ts b/server/src/plugins/service-api-http/index.ts index 4fb3a87..13eb5c7 100644 --- a/server/src/plugins/service-api-http/index.ts +++ b/server/src/plugins/service-api-http/index.ts @@ -735,11 +735,11 @@ function registerKioskRoutes( nodered.forward(body.topic, out, markForwarded); mqtt.publishEvent(kiosk.id, body.topic, out); - // ONVIF events: also forward to the fixed onvif.event route so the - // bf-trigger-motion / bf-trigger-anpr / bf-trigger-event nodes - // receive them without needing per-topic route registration. + nodered.forward("camera.event", out); if (body.source_type === "onvif") { nodered.forward("onvif.event", out); + nodered.forward("onvif.motion", out); + nodered.forward("onvif.anpr", out); } }