fix: Node-RED event forwarding + parse ONVIF Key section (PlateNumber)
Some checks are pending
release / meta (push) Waiting to run
release / build (push) Blocked by required conditions

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>
This commit is contained in:
Mitchell R 2026-05-26 15:38:30 +02:00
parent eb8abbdff9
commit 55b11f2ffa
No known key found for this signature in database
6 changed files with 16 additions and 12 deletions

View file

@ -448,6 +448,11 @@ fn parse_notification_messages(xml: &str) -> Vec<OnvifEvent> {
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);

View file

@ -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 = {

View file

@ -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();
}

View file

@ -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();
}

View file

@ -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();
}

View file

@ -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);
}
}