Last active
March 15, 2025 20:56
-
-
Save curtishall/a9e66351bfc8213a749daf3876b86e88 to your computer and use it in GitHub Desktop.
nodejs onvif script to return available event types.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* bc-check-onvif.js | |
* | |
* This script reports: | |
* - MyRuleDetector rules (e.g. People Detect, Vehicle Detect, Dog Cat Detect) | |
* - RuleEngine events such as CellMotionDetector/Motion | |
* - VideoSource events such as MotionAlarm | |
* | |
* Usage: | |
* node bc-check-onvif.js <ip> <port> <username> <password> | |
* | |
* Example: | |
* node bc-check-onvif.js 192.168.87.151 80 admin admin | |
*/ | |
if (process.argv.length < 6) { | |
console.log("Usage: node bc-check-onvif.js <ip> <port> <username> <password>"); | |
process.exit(1); | |
} | |
const HOSTNAME = process.argv[2]; | |
const PORT = process.argv[3]; | |
const USERNAME = process.argv[4]; | |
const PASSWORD = process.argv[5]; | |
const EventMethodTypes = { PULL: "pull", SUBSCRIBE: "subscribe" }; | |
// For this example, we use PullPoint events. | |
let EVENT_MODE = EventMethodTypes.PULL; | |
let Cam = require('/usr/share/nodejs/onvif').Cam; | |
let cam_obj = null; | |
let flow = require('nimble'); | |
let ruleSet = new Set(); | |
// Helper: Remove namespace prefixes from a topic string. | |
// E.g., "tns1:VideoSource" becomes "VideoSource" | |
function stripNamespaces(topic) { | |
if (!topic) return ""; | |
return topic.split('/').map(function(part) { | |
return part.split(':').pop(); | |
}).join('/'); | |
} | |
/** | |
* processTopic: Examines the full topic string and aggregates events of interest. | |
* | |
* It does the following: | |
* 1. If the topic includes "MyRuleDetector", it extracts the rule name (the element immediately following) | |
* and normalizes it (inserting a space before "Detect"). FaceDetect events are skipped. | |
* 2. Otherwise, if the topic contains "CellMotionDetector" (under RuleEngine), it extracts that branch | |
* and stores it in the form "CellMotionDetector: <next-part>". | |
* 3. Similarly, if the topic contains "VideoSource" and "MotionAlarm", it stores it as "VideoSource: MotionAlarm". | |
*/ | |
function processTopic(topic) { | |
if (!topic) return; | |
const lower = topic.toLowerCase(); | |
// Case 1: MyRuleDetector events. | |
if (lower.includes('myruledetector')) { | |
let parts = topic.split('/').filter(Boolean); | |
let idx = parts.findIndex(part => part.toLowerCase() === 'myruledetector'); | |
if (idx >= 0 && parts.length > idx + 1) { | |
let rule = parts[idx + 1]; | |
rule = rule.replace(/detect/i, ' Detect'); | |
// Skip FaceDetect events if desired. | |
if (rule.toLowerCase().includes('face')) return; | |
ruleSet.add(rule); | |
} | |
} | |
// Case 2: Other RuleEngine events (e.g. CellMotionDetector) | |
else if (lower.includes('ruleengine') && lower.includes('cellmotiondetector')) { | |
let parts = topic.split('/').filter(Boolean); | |
let idx = parts.findIndex(part => part.toLowerCase() === 'cellmotiondetector'); | |
if (idx >= 0 && parts.length > idx + 1) { | |
let rule = parts[idx] + ': ' + parts[idx + 1]; | |
ruleSet.add(rule); | |
} | |
} | |
// Case 3: VideoSource events (e.g. MotionAlarm) | |
else if (lower.includes('videosource') && lower.includes('motionalarm')) { | |
let parts = topic.split('/').filter(Boolean); | |
let idx = parts.findIndex(part => part.toLowerCase() === 'videosource'); | |
if (idx >= 0 && parts.length > idx + 1) { | |
let rule = parts[idx] + ': ' + parts[idx + 1]; | |
ruleSet.add(rule); | |
} | |
} | |
} | |
// Event handler for incoming events. | |
function ReceivedEvent(camMessage, xml) { | |
if (camMessage.topic && camMessage.topic._) { | |
let topic = camMessage.topic._; | |
console.log('Received Event Topic:', topic); | |
let cleanTopic = stripNamespaces(topic); | |
processTopic(cleanTopic); | |
} else { | |
console.log('Received event with no topic.'); | |
} | |
} | |
console.log("*******************************************************************************"); | |
console.log("** This script subscribes for events and aggregates supported rules"); | |
console.log("** Usage: node bc-check-onvif.js <ip> <port> <username> <password>"); | |
console.log("*******************************************************************************"); | |
new Cam({ | |
hostname: HOSTNAME, | |
username: USERNAME, | |
password: PASSWORD, | |
port: PORT, | |
timeout: 10000, | |
preserveAddress: true | |
}, function CamFunc(err) { | |
if (err) { | |
console.log("Error connecting to device:", err); | |
process.exit(1); | |
} | |
console.log('Connected to ONVIF Device'); | |
cam_obj = this; | |
flow.series([ | |
function(callback) { | |
cam_obj.getDeviceInformation(function(err, info, xml) { | |
if (!err) { | |
console.log('Manufacturer ' + info.manufacturer); | |
console.log('Model ' + info.model); | |
console.log('Firmware ' + info.firmwareVersion); | |
console.log('Serial Number ' + info.serialNumber); | |
} | |
callback(); | |
}); | |
}, | |
function(callback) { | |
cam_obj.getSystemDateAndTime(function(err, date, xml) { | |
if (!err) { console.log('Device Time ' + date); } | |
callback(); | |
}); | |
}, | |
function(callback) { | |
cam_obj.getCapabilities(function(err, data, xml) { | |
if (err) { console.log(err); } | |
callback(); | |
}); | |
}, | |
function(callback) { | |
cam_obj.getEventProperties(function(err, data, xml) { | |
if (err) { | |
console.log(err); | |
} else { | |
console.log("Event properties retrieved."); | |
} | |
callback(); | |
}); | |
} | |
], function() { | |
// Listen for events via PullPoint subscription. | |
cam_obj.on('event', function(camMessage, xml) { | |
ReceivedEvent(camMessage, xml); | |
}); | |
console.log("Listening for events for 20 seconds..."); | |
// Prevent the process from exiting immediately. | |
process.stdin.resume(); | |
setTimeout(function() { | |
console.log("\nPolling complete.\n"); | |
console.log("This camera supports the following rules:\n"); | |
if (ruleSet.size === 0) { | |
console.log("No rules detected."); | |
} else { | |
Array.from(ruleSet).sort().forEach(function(rule) { | |
console.log(rule); | |
}); | |
} | |
process.exit(0); | |
}, 20000); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment