Hikvision Motion Detection in OpenHAB using node.js

Using a node.js module (node-hikvision-api) for accessing a hikvision camera’s built-in motion detection for use in OpenHAB

It’s a little annoying how Hikvision exposes the motion events on their cameras. It’s a multipart http stream containing XML.

See Hikvision manual Page 176
Section 8.11.30 /ISAPI/Event/notification/alertStream
http://oversea-download.hikvision.com/uploadfile/Leaflet/ISAPI/HIKVISION%20ISAPI_2.0-IPMD%20Service.pdf

node-hikvision-api abstracts that away a bit so hikmotion.js can focus on what to do with those events, like update motion contacts in OpenHAB.

Requirements:
node.js
hikvision camera with motion events configured
(tested with DS-2CD2332-I & XC-2142FWDI)

linedetect vmd

Customization:
This is just an example of what I’m doing with a couple of my cameras. Modify hikmotion.js to fit your needs.

Installation:
[text]sudo mkdir /opt/cameramotion
sudo cp hikmotion.js /opt/cameramotion/hikmotion.js
sudo cp hikmotion.conf /etc/init/hikmotion.conf
cd /opt/cameramotion
sudo npm install git+https://github.com/ragingcomputer/node-hikvision-api.git
sudo npm install http
sudo start hikmotion[/text]

OpenHAB Items
[text]Group Outdoor
Group Motion
Contact Motion_VT_Garage "Garage Video Motion [MAP(motion.map):%s]" (Motion,Outdoor)
Contact Motion_VT_Garage_Line "Garage Video Line [MAP(motion.map):%s]" (Motion,Outdoor)
Contact Motion_VT_Front "Front Video Motion [MAP(motion.map):%s]" (Motion,Outdoor)
Contact Motion_VT_Front_Line "Front Video Line [MAP(motion.map):%s]" (Motion,Outdoor)
[/text]

motion.map
[text]CLOSED=No Motion
OPEN=Motion
-=No Motion
[/text]

/opt/cameramotion/hikmotion.js
[javascript]#!/usr/bin/nodejs
var ipcamera = require(‘node-hikvision-api’);
var http = require(‘http’);

// Options:
var openhabOptions = {
host : ‘localhost’,
port : 8080,
username : ‘user’,
password : ‘pass’
}

// options for hikvision camera
// required per camera
var optionsGarage = {
host : ‘192.168.1.111’,
port : ’80’,
user : ‘admin’,
pass : ‘password123’,
log : false,
};

var optionsFront = {
host : ‘192.168.1.112’,
port : ’80’,
user : ‘admin’,
pass : ‘password123’,
log : false,
};

// one required per camera
var hikvisionGarage = new ipcamera.hikvision(optionsGarage);
var hikvisionFront = new ipcamera.hikvision(optionsFront);

// Set initial state to no motion
setState(‘Motion_VT_Garage’, ‘CLOSED’);
setState(‘Motion_VT_Garage_Line’, ‘CLOSED’);
setState(‘Motion_VT_Front’, ‘CLOSED’);
setState(‘Motion_VT_Front_Line’, ‘CLOSED’);

// Monitor Camera Alarms
// requires one function per camera
hikvisionGarage.on(‘alarm’, function(code,action,index) {
if (code === ‘VideoMotion’ && action === ‘Start’) {
console.log(getDateTime() + ‘ Garage Channel ‘ + index + ‘: Video Motion Detected’)
setState(‘Motion_VT_Garage’, ‘OPEN’);
}
if (code === ‘VideoMotion’ && action === ‘Stop’) {
console.log(getDateTime() + ‘ Garage Channel ‘ + index + ‘: Video Motion Ended’)
setState(‘Motion_VT_Garage’, ‘CLOSED’);
}
if (code === ‘LineDetection’ && action === ‘Start’) {
console.log(getDateTime() + ‘ Garage Channel ‘ + index + ‘: Line Cross Detected’)
setState(‘Motion_VT_Garage_Line’, ‘OPEN’);
}
if (code === ‘LineDetection’ && action === ‘Stop’) {
console.log(getDateTime() + ‘ Garage Channel ‘ + index + ‘: Line Cross Ended’)
setState(‘Motion_VT_Garage_Line’, ‘CLOSED’);
}

});

// code = event reported by camera
// expected code values AlarmLocal VideoMotion LineDetection VideoLoss VideoBlind
// action = Start or Stop
// index = camera index number in the camera device. almost always 1
hikvisionFront.on(‘alarm’, function(code,action,index) {
if (code === ‘VideoMotion’ && action === ‘Start’) {
console.log(getDateTime() + ‘ Front Channel ‘ + index + ‘: Video Motion Detected’)
setState(‘Motion_VT_Front’, ‘OPEN’);
}
if (code === ‘VideoMotion’ && action === ‘Stop’) {
console.log(getDateTime() + ‘ Front Channel ‘ + index + ‘: Video Motion Ended’)
setState(‘Motion_VT_Front’, ‘CLOSED’);
}
if (code === ‘LineDetection’ && action === ‘Start’) {
console.log(getDateTime() + ‘ Front Channel ‘ + index + ‘: Line Cross Detected’)
setState(‘Motion_VT_Front_Line’, ‘OPEN’);
}
if (code === ‘LineDetection’ && action === ‘Stop’) {
console.log(getDateTime() + ‘ Front Channel ‘ + index + ‘: Line Cross Ended’)
setState(‘Motion_VT_Front_Line’, ‘CLOSED’);
}
});

function getDateTime() {
var date = new Date();
var hour = date.getHours();
hour = (hour < 10 ? "0" : "") + hour;
var min = date.getMinutes();
min = (min < 10 ? "0" : "") + min;
var sec = date.getSeconds();
sec = (sec < 10 ? "0" : "") + sec;
var year = date.getFullYear();
var month = date.getMonth() + 1;
month = (month < 10 ? "0" : "") + month;
var day = date.getDate();
day = (day < 10 ? "0" : "") + day;
return year + "-" + month + "-" + day + " " + hour + ":" + min + ":" + sec;
}

// takes 2 variables, item, which is the item name in openhab
// and the state you intend to set
function setState( item, newState ) {

var username = openhabOptions[‘username’];
var password = openhabOptions[‘password’];
var auth = ‘Basic ‘ + new Buffer(username + ‘:’ + password).toString(‘base64’);

var headers = {
‘Content-Type’: ‘text/plain’,
‘Authorization’: auth
};

var options = {
host: openhabOptions[‘host’],
port: openhabOptions[‘port’],
path: ‘/rest/items/’ + item + ‘/state’,
method: ‘PUT’,
headers: headers
};

var req = http.request(options, function(res) {
// console.log(‘STATUS: ‘ + res.statusCode);
// console.log(‘HEADERS: ‘ + JSON.stringify(res.headers));
res.setEncoding(‘utf8’);
res.on(‘data’, function (chunk) {
// console.log(‘BODY: ‘ + chunk);
});
});

req.on(‘error’, function(e) {
// console.log(‘problem with request: ‘ + e.message);
});

// write data to request body
req.write(newState);
req.end();

}
[/javascript]

And the Upstart script

/etc/init/hikmotion.conf
[text]
description "hikmotion.js server"
author "Raging Computer"

start on started mountall
stop on shutdown

# automatically respawn

respawn
respawn limit 99 5

script

export HOME="/opt/cameramotion"
exec sudo -u openhab /usr/bin/nodejs /opt/cameramotion/hikmotion.js >> /var/log/hikmotion.log 2>&1

end script

post-start script

# optionally put a script here that will notifiy you node has (re)started
# /root/bin/hoptoad.sh "node.js has started!"

end script
[/text]

8 thoughts on “Hikvision Motion Detection in OpenHAB using node.js

  1. Full Error:
    pi@openhab:/opt/cameramotion $ sudo node hikmotion.js
    events.js:160
    throw er; // Unhandled ‘error’ event
    ^

    TypeError: Cannot read property ‘eventType’ of undefined
    at /opt/cameramotion/node_modules/node-hikvision-api/hikvision.js:48:48
    at Parser. (/opt/cameramotion/node_modules/xml2js/lib/xml2js.js:489:18)
    at emitOne (events.js:96:13)
    at Parser.emit (events.js:188:7)
    at Object.onclosetag (/opt/cameramotion/node_modules/xml2js/lib/xml2js.js:447:26)
    at emit (/opt/cameramotion/node_modules/sax/lib/sax.js:640:35)
    at emitNode (/opt/cameramotion/node_modules/sax/lib/sax.js:645:5)
    at closeTag (/opt/cameramotion/node_modules/sax/lib/sax.js:905:7)
    at Object.write (/opt/cameramotion/node_modules/sax/lib/sax.js:1452:13)
    at Parser.exports.Parser.Parser.parseString (/opt/cameramotion/node_modules/xml2js/lib/xml2js.js:508:31)

    Like

  2. Can not get this to work on Raspbian with Jessie.
    sudo npm install git+https://github.com/ragingcomputer/node-hikvision-api.git Throws errors.

    Like

  3. Hi Guys
    does anybody have a solution for the issues onder openhabian? i followed the instructions and when i try to start hikmotion i receive the following error:

    openhabian@openHAB:/usr/bin$ sudo start hikmotion
    sudo: start: command not found

    thynk for any solutions

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: