diff --git a/accessories/AD2USB.js b/accessories/AD2USB.js new file mode 100644 index 0000000..c142c8f --- /dev/null +++ b/accessories/AD2USB.js @@ -0,0 +1,245 @@ +var types = require("../lib/HAP-NodeJS/accessories/types.js"); +var AD2USB = require('ad2usb'); +var CUSTOM_PANEL_LCD_TEXT_CTYPE = "A3E7B8F9-216E-42C1-A21C-97D4E3BE52C8"; + +function AD2USBAccessory(log, config) { + + this.log = log; + this.name = config["name"]; + this.host = config["host"]; + this.port = config["port"]; + this.pin = config["pin"]; + var that = this; + this.currentArmState = 2; + this.currentStateCharacteristic = undefined; + this.targetStateCharacteristic = undefined; + this.lcdCharacteristic = undefined; + + var alarm = AD2USB.connect(this.host, this.port, function() { + + // Send an initial empty character to get status + alarm.send(''); + + // Armed Away + alarm.on('armedAway', function() { + + that.log("Armed to AWAY"); + if (that.currentStateCharacteristic) { + that.currentStateCharacteristic.updateValue(0, null); + } + if (that.targetStateCharacteristic) { + that.targetStateCharacteristic.updateValue(1, null); + } + + }); + + // Armed Stay + alarm.on('armedStay', function() { + + that.log("Armed to STAY"); + if (that.currentStateCharacteristic) { + that.currentStateCharacteristic.updateValue(0, null); + } + if (that.targetStateCharacteristic) { + that.targetStateCharacteristic.updateValue(0, null); + } + + }); + + // Armed Night + alarm.on('armedNight', function() { + + that.log("Armed to NIGHT"); + if (that.currentStateCharacteristic) { + that.currentStateCharacteristic.updateValue(0, null); + } + if (that.targetStateCharacteristic) { + that.targetStateCharacteristic.updateValue(2, null); + } + + }); + + // Disarmed + alarm.on('disarmed', function() { + + that.log("Disarmed"); + if (that.currentStateCharacteristic) { + that.currentStateCharacteristic.updateValue(1, null); + } + if (that.targetStateCharacteristic) { + that.targetStateCharacteristic.updateValue(3, null); + } + + }); + + // Text Change + alarm.on('lcdtext', function(newText) { + + that.log("LCD: " + newText); + if (that.lcdCharacteristic) { + that.lcdCharacteristic.updateValue(newText, null); + } + + }); + + + }); + this.alarm = alarm; + +} + +AD2USBAccessory.prototype = { + + setArmState: function(targetArmState) { + + var that = this; + that.log("Desired target arm state: " + targetArmState); + + // TARGET + // 0 - Stay + // 1 - Away + // 2 - Night + // 3 - Disarm + if (targetArmState == 0) { + that.alarm.armStay(that.pin); + } + else if (targetArmState == 1) { + that.alarm.armAway(that.pin); + } + else if (targetArmState == 2) { + that.alarm.armNight(that.pin); + } + else if (targetArmState == 3) { + that.alarm.disarm(that.pin); + } + + + // CURRENT + // 0 - Armed + // 1 - Disarmed + // 2 - Hold + + }, + + getServices: function() { + var that = this; + return [{ + sType: types.ACCESSORY_INFORMATION_STYPE, + characteristics: [{ + cType: types.NAME_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: this.name, + supportEvents: false, + supportBonjour: false, + manfDescription: "Name of the accessory", + designedMaxLength: 255 + },{ + cType: types.MANUFACTURER_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: "Nutech", + supportEvents: false, + supportBonjour: false, + manfDescription: "Manufacturer", + designedMaxLength: 255 + },{ + cType: types.MODEL_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: "AD2USB", + supportEvents: false, + supportBonjour: false, + manfDescription: "Model", + designedMaxLength: 255 + },{ + cType: types.SERIAL_NUMBER_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: "AD2USBIF", + supportEvents: false, + supportBonjour: false, + manfDescription: "SN", + designedMaxLength: 255 + },{ + cType: types.IDENTIFY_CTYPE, + onUpdate: null, + perms: ["pw"], + format: "bool", + initialValue: false, + supportEvents: false, + supportBonjour: false, + manfDescription: "Identify Accessory", + designedMaxLength: 1 + }] + },{ + sType: types.ALARM_STYPE, + characteristics: [{ + cType: types.NAME_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: this.name, + supportEvents: false, + supportBonjour: false, + manfDescription: "Name of service", + designedMaxLength: 255 + },{ + cType: types.ALARM_CURRENT_STATE_CTYPE, + onUpdate: null, + onRegister: function(characteristic) { + + that.currentStateCharacteristic = characteristic; + characteristic.eventEnabled = true; + + }, + perms: ["pr","ev"], + format: "int", + initialValue: 2, + supportEvents: true, + supportBonjour: false, + manfDescription: "Alarm current arm state", + designedMaxLength: 1 + },{ + cType: types.ALARM_TARGET_STATE_CTYPE, + onUpdate: function(value) { that.setArmState(value); }, + onRegister: function(characteristic) { + + that.targetStateCharacteristic = characteristic; + characteristic.eventEnabled = true; + + }, + perms: ["pw","pr","ev"], + format: "int", + initialValue: 1, + supportEvents: true, + supportBonjour: false, + manfDescription: "Alarm target arm state", + designedMaxLength: 1 + }, + { + cType: CUSTOM_PANEL_LCD_TEXT_CTYPE, + onUpdate: null, + onRegister: function(characteristic) { + + that.lcdCharacteristic = characteristic; + characteristic.eventEnabled = true; + + }, + perms: ["pr","ev"], + format: "string", + initialValue: "Unknown", + supportEvents: false, + supportBonjour: false, + manfDescription: "Keypad Text", + designedMaxLength: 64 + }] + }]; + } +}; + +module.exports.accessory = AD2USBAccessory; diff --git a/accessories/ELKM1.js b/accessories/ELKM1.js new file mode 100644 index 0000000..7ac2ba3 --- /dev/null +++ b/accessories/ELKM1.js @@ -0,0 +1,126 @@ +var types = require("../lib/HAP-NodeJS/accessories/types.js"); +var elkington = require("elkington"); + +function ElkM1Accessory(log, config) { + this.log = log; + this.name = config["name"]; + this.zone = config["zone"]; + this.host = config["host"]; + this.port = config["port"]; + this.pin = config["pin"]; + this.arm = config["arm"]; +} + +ElkM1Accessory.prototype = { + setPowerState: function(alarmOn) { + var that = this; + + if (!alarmOn) + { + return; + } + + var elk = elkington.createConnection({ + port: that.port, + host: that.host, + }); + + switch (that.arm) + { + case 'Away': + elk.armAway({area: that.zone, code: that.pin}); + break; + case 'Stay': + elk.armStay({area: that.zone, code: that.pin}); + break; + case 'Night': + elk.armNightInstant({area: that.zone, code: that.pin}); + break; + default: + break; + } + }, + + getServices: function() { + var that = this; + return [{ + sType: types.ACCESSORY_INFORMATION_STYPE, + characteristics: [{ + cType: types.NAME_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: this.name, + supportEvents: false, + supportBonjour: false, + manfDescription: "Name of the accessory", + designedMaxLength: 255 + },{ + cType: types.MANUFACTURER_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: "Elk", + supportEvents: false, + supportBonjour: false, + manfDescription: "Manufacturer", + designedMaxLength: 255 + },{ + cType: types.MODEL_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: "M1", + supportEvents: false, + supportBonjour: false, + manfDescription: "Model", + designedMaxLength: 255 + },{ + cType: types.SERIAL_NUMBER_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: "A1S2NASF88EW", + supportEvents: false, + supportBonjour: false, + manfDescription: "SN", + designedMaxLength: 255 + },{ + cType: types.IDENTIFY_CTYPE, + onUpdate: null, + perms: ["pw"], + format: "bool", + initialValue: false, + supportEvents: false, + supportBonjour: false, + manfDescription: "Identify Accessory", + designedMaxLength: 1 + }] + },{ + sType: types.SWITCH_STYPE, + characteristics: [{ + cType: types.NAME_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: this.name, + supportEvents: false, + supportBonjour: false, + manfDescription: "Name of service", + designedMaxLength: 255 + },{ + cType: types.POWER_STATE_CTYPE, + onUpdate: function(value) { that.setPowerState(value); }, + perms: ["pw","pr","ev"], + format: "bool", + initialValue: false, + supportEvents: false, + supportBonjour: false, + manfDescription: "Alarm the Zone", + designedMaxLength: 1 + }] + }]; + } +}; + +module.exports.accessory = ElkM1Accessory; diff --git a/app.js b/app.js index 4f94b23..6b2bd77 100644 --- a/app.js +++ b/app.js @@ -124,6 +124,7 @@ function createHAPServer(name, services) { for (var k = 0; k < services[j].characteristics.length; k++) { var options = { onRead: services[j].characteristics[k].onRead, + onRegister: services[j].characteristics[k].onRegister, type: services[j].characteristics[k].cType, perms: services[j].characteristics[k].perms, format: services[j].characteristics[k].format, diff --git a/config-sample.json b/config-sample.json index 768a1d1..3cfe0d7 100644 --- a/config-sample.json +++ b/config-sample.json @@ -103,6 +103,23 @@ "off_url": "https://192.168.1.22:3030/devices/23222/off", "brightness_url": "https://192.168.1.22:3030/devices/23222/brightness/%b", "http_method": "POST" + },{ + "accessory": "ELKM1", + "name": "Security System", + "description": "Allows basic control of Elk M1 security system. You can use 1 of 3 arm modes: Away, Stay, Night. If you need to access all 3, create 3 accessories with different names.", + "zone": "1", + "host": "192.168.1.10", + "port": "2101", + "pin": "1234", + "arm": "Away" + }, + { + "accessory": "AD2USB", + "name": "Alarm", + "description": "Arm, disarm, and status monitoring of the default partition for Honeywell/Ademco alarm systems. Requires network configured AD2USB interface", + "host": "192.168.1.200", // IP address of the SER2SOCK service + "port" : 4999, // Port the SER2SOCK process is running on + "pin": "1234" // PIN used for arming / disarming } ] } diff --git a/lib/HAP-NodeJS b/lib/HAP-NodeJS index b130842..bb7f620 160000 --- a/lib/HAP-NodeJS +++ b/lib/HAP-NodeJS @@ -1 +1 @@ -Subproject commit b130842359214062eb61a220577ebd7de98d0dd9 +Subproject commit bb7f620d9a32426479829b344356716a16dd374a diff --git a/package.json b/package.json index c12ab9f..f19c60b 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "url": "git://github.com/nfarina/homebridge.git" }, "dependencies": { + "ad2usb": "git+https://github.com/alistairg/node-ad2usb.git#local", "request": "2.49.x", "node-persist": "0.0.x", "xmldoc": "0.1.x", @@ -18,6 +19,7 @@ "carwingsjs": "0.0.x", "sonos": "0.8.x", "wemo": "0.2.x", - "wink-js": "0.0.5" + "wink-js": "0.0.5", + "elkington": "kevinohara80/elkington" } }