From 983f1362715fc3064c3b3fe5882f2d085449c227 Mon Sep 17 00:00:00 2001 From: ilcato Date: Tue, 15 Sep 2015 13:57:21 +0200 Subject: [PATCH 01/69] Create FibaroHC2.js --- platforms/FibaroHC2.js | 445 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 445 insertions(+) create mode 100644 platforms/FibaroHC2.js diff --git a/platforms/FibaroHC2.js b/platforms/FibaroHC2.js new file mode 100644 index 0000000..25e093f --- /dev/null +++ b/platforms/FibaroHC2.js @@ -0,0 +1,445 @@ +// Fibaro Home Center 2 Platform Shim for HomeBridge +// +// Remember to add platform to config.json. Example: +// "platforms": [ +// { + "platform": "FibaroHC2", + "name": "FibaroHC2", + "host": "PUT IP ADDRESS OF YOUR HC2 HERE", + "username": "PUT USERNAME OF YOUR HC2 HERE", + "password": "PUT PASSWORD OF YOUR HC2 HERE" +// } +// ], +// +// When you attempt to add a device, it will ask for a "PIN code". +// The default code for all HomeBridge accessories is 031-45-154. + +var types = require("HAP-NodeJS/accessories/types.js"); +var request = require("request"); + +function FibaroHC2Platform(log, config){ + this.log = log; + this.host = config["host"]; + this.username = config["username"]; + this.password = config["password"]; + this.auth = "Basic " + new Buffer(this.username + ":" + this.password).toString("base64"); + this.url = "http://"+this.host+"/api/devices"; +} + +FibaroHC2Platform.prototype = { + accessories: function(callback) { + this.log("Fetching Fibaro Home Center devices..."); + + var that = this; + var foundAccessories = []; + + request.get({ + url: this.url, + headers : { + "Authorization" : this.auth + }, + json: true + }, function(err, response, json) { + if (!err && response.statusCode == 200) { + if (json != undefined) { + json.map(function(s) { + that.log("Found: " + s.type); + if (s.visible == true) { + if (s.type == "com.fibaro.multilevelSwitch") { + accessory = new FibaroDimmerAccessory(that, s.name, s.id); + foundAccessories.push(accessory); + } else if (s.type == "com.fibaro.FGRM222") + { + accessory = new FibaroRollerShutterAccessory(that, s.name, s.id); + foundAccessories.push(accessory); + } else if (s.type == "com.fibaro.binarySwitch" || s.type == "com.fibaro.developer.bxs.virtualBinarySwitch") + { + accessory = new FibaroBinarySwitchAccessory(that, s.name, s.id); + foundAccessories.push(accessory); + } else if (s.type == "com.fibaro.FGMS001") + { + accessory = new FibaroMotionSensorAccessory(that, s.name, s.id); + foundAccessories.push(accessory); + } else if (s.type == "com.fibaro.temperatureSensor") + { + accessory = new FibaroTemperatureSensorAccessory(that, s.name, s.id); + foundAccessories.push(accessory); + } else if (s.type == "com.fibaro.doorSensor") + { + accessory = new FibaroDoorSensorAccessory(that, s.name, s.id); + foundAccessories.push(accessory); + } + + } + }) + } + callback(foundAccessories); + } else { + that.log("There was a problem authenticating with FibaroHC2."); + } + }); + + }, + getAccessoryValue: function(callback, returnBoolean, that) { + var url = "http://"+that.platform.host+"/api/devices/"+that.id+"/properties/value"; + request.get({ + headers : { + "Authorization" : that.platform.auth + }, + json: true, + url: url + }, function(err, response, json) { + that.platform.log(url); + if (!err && response.statusCode == 200) { + if (returnBoolean) + callback(json.value == 0 ? 0 : 1); + else + callback(json.value); + } else { + that.platform.log("There was a problem getting value from" + that.id); + } + }) + }, + getAccessoryServices: function(that) { + var services = [{ + sType: types.ACCESSORY_INFORMATION_STYPE, + characteristics: this.informationCharacteristics(that), + }, + { + sType: that.SERVICE_TYPE, + characteristics: this.controlCharacteristics(that) + }]; + this.log("Loaded services for " + that.name) + return services; + }, + command: function(c,value, that) { + var url = "http://"+this.host+"/api/devices/"+that.id+"/action/"+c; + var body = value != undefined ? JSON.stringify({ + "args": [ + value + ] + }) : null; + var method = "post"; + request({ + url: url, + body: body, + method: method, + headers: { + "Authorization" : this.auth + }, + }, function(err, response) { + if (err) { + that.platform.log("There was a problem sending command " + c + " to" + that.name); + that.platform.log(url); + } else { + that.platform.log(that.name + " sent command " + c); + that.platform.log(url); + } + }); + }, + informationCharacteristics: function(that) + { + return [ + { + cType: types.NAME_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: that.name, + supportEvents: false, + supportBonjour: false, + manfDescription: "Name of the accessory", + designedMaxLength: 255 + },{ + cType: types.MANUFACTURER_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: "Fibaro", + supportEvents: false, + supportBonjour: false, + manfDescription: "Manufacturer", + designedMaxLength: 255 + },{ + cType: types.MODEL_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: that.MODEL_TYPE, + 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 + } + ] + }, + controlCharacteristics: function(that) { + var cTypes = []; + var l = that.CONTROL_CHARACTERISTICS.length; + for (var i = 0; i < l; i++) { + if (that.CONTROL_CHARACTERISTICS[i] == types.NAME_CTYPE) { + cTypes.push({ + cType: types.NAME_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: that.name, + supportEvents: true, + supportBonjour: false, + manfDescription: "Name of service", + designedMaxLength: 255 + }); + } else if (that.CONTROL_CHARACTERISTICS[i] == types.POWER_STATE_CTYPE) { + cTypes.push({ + cType: types.POWER_STATE_CTYPE, + onUpdate: function(value) { + if (value == 0) { + that.platform.command("turnOff", null, that) + } else { + that.platform.command("turnOn", null, that) + } + }, + onRead: function(callback) { + that.platform.getAccessoryValue(callback, true, that); + }, + perms: ["pw","pr","ev"], + format: "bool", + initialValue: 0, + supportEvents: true, + supportBonjour: false, + manfDescription: "Change the power state", + designedMaxLength: 1 + }); + } else if (that.CONTROL_CHARACTERISTICS[i] == types.BRIGHTNESS_CTYPE) { + cTypes.push({ + cType: types.BRIGHTNESS_CTYPE, + onUpdate: function(value) { that.platform.command("setValue", value, that); }, + onRead: function(callback) { + that.platform.getAccessoryValue(callback, false, that); + }, + perms: ["pw","pr","ev"], + format: "int", + initialValue: 0, + supportEvents: true, + supportBonjour: false, + manfDescription: "Adjust Brightness of Light", + designedMinValue: 0, + designedMaxValue: 100, + designedMinStep: 10, + unit: "%" + }); + } else if (that.CONTROL_CHARACTERISTICS[i] == types.WINDOW_COVERING_CURRENT_POSITION_CTYPE) { + cTypes.push({ + cType: types.WINDOW_COVERING_CURRENT_POSITION_CTYPE, + onRead: function(callback) { + that.platform.getAccessoryValue(callback, false, that); + }, + perms: ["pr","ev"], + format: "int", + initialValue: 0, + supportEvents: false, + supportBonjour: false, + manfDescription: "Current Blind Position", + designedMinValue: 0, + designedMaxValue: 100, + designedMinStep: 1, + unit: "%" + }); + } else if (that.CONTROL_CHARACTERISTICS[i] == types.WINDOW_COVERING_TARGET_POSITION_CTYPE) { + cTypes.push({ + cType: types.WINDOW_COVERING_TARGET_POSITION_CTYPE, + onUpdate: function(value) { that.platform.command("setValue", value, that); }, + onRead: function(callback) { + that.platform.getAccessoryValue(callback, false, that); + }, + perms: ["pw","pr","ev"], + format: "int", + initialValue: 0, + supportEvents: false, + supportBonjour: false, + manfDescription: "Target Blind Position", + designedMinValue: 0, + designedMaxValue: 100, + designedMinStep: 1, + unit: "%" + }); + } else if (that.CONTROL_CHARACTERISTICS[i] == types.WINDOW_COVERING_OPERATION_STATE_CTYPE) { + cTypes.push({ + cType: types.WINDOW_COVERING_OPERATION_STATE_CTYPE, + perms: ["pr","ev"], + format: "int", + initialValue: 0, + supportEvents: false, + supportBonjour: false, + manfDescription: "Position State", + designedMinValue: 0, + designedMaxValue: 2, + designedMinStep: 1, + }); + } else if (that.CONTROL_CHARACTERISTICS[i] == types.CURRENT_TEMPERATURE_CTYPE) { + cTypes.push({ + cType: types.CURRENT_TEMPERATURE_CTYPE, + onRead: function(callback) { + that.platform.getAccessoryValue(callback, false, that); + }, + perms: ["pr","ev"], + format: "float", + unit: "celsius", + stepValue: 0.1, + initialValue: 0, + supportEvents: true, + supportBonjour: false, + manfDescription: "Get current temperature" + }); + } else if (that.CONTROL_CHARACTERISTICS[i] == types.MOTION_DETECTED_CTYPE) { + cTypes.push({ + cType: types.MOTION_DETECTED_CTYPE, + onRead: function(callback) { + that.platform.getAccessoryValue(callback, true, that); + }, + perms: ["pr","ev"], + format: "bool", + initialValue: 0, + supportEvents: true, + supportBonjour: false, + manfDescription: "Detect motion", + designedMaxLength: 1 + }); + } else if (that.CONTROL_CHARACTERISTICS[i] == types.CONTACT_SENSOR_STATE_CTYPE) { + cTypes.push({ + cType: types.CONTACT_SENSOR_STATE_CTYPE, + onRead: function(callback) { + that.platform.getAccessoryValue(callback, true, that); + }, + perms: ["pr","ev"], + format: "bool", + initialValue: 0, + supportEvents: true, + supportBonjour: false, + manfDescription: "Detect door contact", + designedMaxLength: 1 + }); + } + } + return cTypes + } +} + +function FibaroDimmerAccessory(platform, name, id) { + // device info + this.platform = platform; + this.name = name; + this.id = id; + this.MODEL_TYPE = "Dimmer"; + this.SERVICE_TYPE = types.LIGHTBULB_STYPE; + this.CONTROL_CHARACTERISTICS = [types.NAME_CTYPE, types.POWER_STATE_CTYPE, types.BRIGHTNESS_CTYPE]; +} + +FibaroDimmerAccessory.prototype = { + getServices: function() { + return this.platform.getAccessoryServices(this); + } +}; + +function FibaroRollerShutterAccessory(platform, name, id) { + // device info + this.platform = platform; + this.name = name; + this.id = id; + this.MODEL_TYPE = "Roller Shutter 2"; + this.SERVICE_TYPE = types.WINDOW_COVERING_STYPE; + this.CONTROL_CHARACTERISTICS = [types.NAME_CTYPE, types.WINDOW_COVERING_CURRENT_POSITION_CTYPE, types.WINDOW_COVERING_TARGET_POSITION_CTYPE, types.WINDOW_COVERING_OPERATION_STATE_CTYPE]; + +} + +FibaroRollerShutterAccessory.prototype = { + getServices: function() { + return this.platform.getAccessoryServices(this); + } +}; + +function FibaroBinarySwitchAccessory(platform, name, id) { + // device info + this.platform = platform; + this.name = name; + this.id = id; + this.MODEL_TYPE = "Binary Switch"; + this.SERVICE_TYPE = types.SWITCH_STYPE; + this.CONTROL_CHARACTERISTICS = [types.NAME_CTYPE, types.POWER_STATE_CTYPE]; +} + +FibaroBinarySwitchAccessory.prototype = { + getServices: function() { + return this.platform.getAccessoryServices(this); + } +}; + +function FibaroTemperatureSensorAccessory(platform, name, id) { + // device info + this.platform = platform; + this.name = name; + this.id = id; + this.MODEL_TYPE = "Temperature Sensor"; + this.SERVICE_TYPE = types.TEMPERATURE_SENSOR_STYPE; + this.CONTROL_CHARACTERISTICS = [types.NAME_CTYPE, types.CURRENT_TEMPERATURE_CTYPE]; +} + +FibaroTemperatureSensorAccessory.prototype = { + getServices: function() { + return this.platform.getAccessoryServices(this); + } +}; + +function FibaroMotionSensorAccessory(platform, name, id) { + // device info + this.platform = platform; + this.name = name; + this.id = id; + this.MODEL_TYPE = "Motion Sensor"; + this.SERVICE_TYPE = types.MOTION_SENSOR_STYPE; + this.CONTROL_CHARACTERISTICS = [types.NAME_CTYPE, types.MOTION_DETECTED_CTYPE]; +} + +FibaroMotionSensorAccessory.prototype = { + getServices: function() { + return this.platform.getAccessoryServices(this); + } +}; + +function FibaroDoorSensorAccessory(platform, name, id) { + // device info + this.platform = platform; + this.name = name; + this.id = id; + this.MODEL_TYPE = "Door Sensor"; + this.SERVICE_TYPE = types.CONTACT_SENSOR_STYPE; + this.CONTROL_CHARACTERISTICS = [types.NAME_CTYPE, types.CONTACT_SENSOR_STATE_CTYPE]; +} + +FibaroDoorSensorAccessory.prototype = { + getServices: function() { + return this.platform.getAccessoryServices(this); + } +}; + +module.exports.platform = FibaroHC2Platform; From d36827f92de51ab86b6b5656730a9a0f4c310732 Mon Sep 17 00:00:00 2001 From: ilcato Date: Tue, 15 Sep 2015 14:26:42 +0200 Subject: [PATCH 02/69] Fixed comments --- platforms/FibaroHC2.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/platforms/FibaroHC2.js b/platforms/FibaroHC2.js index 25e093f..14dbdd2 100644 --- a/platforms/FibaroHC2.js +++ b/platforms/FibaroHC2.js @@ -3,11 +3,11 @@ // Remember to add platform to config.json. Example: // "platforms": [ // { - "platform": "FibaroHC2", - "name": "FibaroHC2", - "host": "PUT IP ADDRESS OF YOUR HC2 HERE", - "username": "PUT USERNAME OF YOUR HC2 HERE", - "password": "PUT PASSWORD OF YOUR HC2 HERE" +// "platform": "FibaroHC2", +// "name": "FibaroHC2", +// "host": "PUT IP ADDRESS OF YOUR HC2 HERE", +// "username": "PUT USERNAME OF YOUR HC2 HERE", +// "password": "PUT PASSWORD OF YOUR HC2 HERE" // } // ], // From a041407104f3670a9081d8649014fd7132bd04a2 Mon Sep 17 00:00:00 2001 From: ilcato Date: Tue, 15 Sep 2015 19:03:08 +0200 Subject: [PATCH 03/69] Added automatic status update through event mechanism --- platforms/FibaroHC2.js | 99 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 7 deletions(-) diff --git a/platforms/FibaroHC2.js b/platforms/FibaroHC2.js index 14dbdd2..6eef466 100644 --- a/platforms/FibaroHC2.js +++ b/platforms/FibaroHC2.js @@ -18,12 +18,14 @@ var types = require("HAP-NodeJS/accessories/types.js"); var request = require("request"); function FibaroHC2Platform(log, config){ - this.log = log; - this.host = config["host"]; - this.username = config["username"]; - this.password = config["password"]; - this.auth = "Basic " + new Buffer(this.username + ":" + this.password).toString("base64"); - this.url = "http://"+this.host+"/api/devices"; + this.log = log; + this.host = config["host"]; + this.username = config["username"]; + this.password = config["password"]; + this.auth = "Basic " + new Buffer(this.username + ":" + this.password).toString("base64"); + this.url = "http://"+this.host+"/api/devices"; + + startPollingUpdate( this ); } FibaroHC2Platform.prototype = { @@ -75,7 +77,7 @@ FibaroHC2Platform.prototype = { } callback(foundAccessories); } else { - that.log("There was a problem authenticating with FibaroHC2."); + that.log("There was a problem connecting with FibaroHC2."); } }); @@ -212,6 +214,10 @@ FibaroHC2Platform.prototype = { } else if (that.CONTROL_CHARACTERISTICS[i] == types.POWER_STATE_CTYPE) { cTypes.push({ cType: types.POWER_STATE_CTYPE, + onRegister: function(characteristic) { + characteristic.eventEnabled = true; + subscribeUpdate(characteristic, that, true); + }, onUpdate: function(value) { if (value == 0) { that.platform.command("turnOff", null, that) @@ -233,6 +239,10 @@ FibaroHC2Platform.prototype = { } else if (that.CONTROL_CHARACTERISTICS[i] == types.BRIGHTNESS_CTYPE) { cTypes.push({ cType: types.BRIGHTNESS_CTYPE, + onRegister: function(characteristic) { + characteristic.eventEnabled = true; + subscribeUpdate(characteristic, that, false); + }, onUpdate: function(value) { that.platform.command("setValue", value, that); }, onRead: function(callback) { that.platform.getAccessoryValue(callback, false, that); @@ -251,6 +261,10 @@ FibaroHC2Platform.prototype = { } else if (that.CONTROL_CHARACTERISTICS[i] == types.WINDOW_COVERING_CURRENT_POSITION_CTYPE) { cTypes.push({ cType: types.WINDOW_COVERING_CURRENT_POSITION_CTYPE, + onRegister: function(characteristic) { + characteristic.eventEnabled = true; + subscribeUpdate(characteristic, that, false); + }, onRead: function(callback) { that.platform.getAccessoryValue(callback, false, that); }, @@ -268,6 +282,10 @@ FibaroHC2Platform.prototype = { } else if (that.CONTROL_CHARACTERISTICS[i] == types.WINDOW_COVERING_TARGET_POSITION_CTYPE) { cTypes.push({ cType: types.WINDOW_COVERING_TARGET_POSITION_CTYPE, + onRegister: function(characteristic) { + characteristic.eventEnabled = true; + subscribeUpdate(characteristic, that, false); + }, onUpdate: function(value) { that.platform.command("setValue", value, that); }, onRead: function(callback) { that.platform.getAccessoryValue(callback, false, that); @@ -299,6 +317,10 @@ FibaroHC2Platform.prototype = { } else if (that.CONTROL_CHARACTERISTICS[i] == types.CURRENT_TEMPERATURE_CTYPE) { cTypes.push({ cType: types.CURRENT_TEMPERATURE_CTYPE, + onRegister: function(characteristic) { + characteristic.eventEnabled = true; + subscribeUpdate(characteristic, that, false); + }, onRead: function(callback) { that.platform.getAccessoryValue(callback, false, that); }, @@ -314,6 +336,10 @@ FibaroHC2Platform.prototype = { } else if (that.CONTROL_CHARACTERISTICS[i] == types.MOTION_DETECTED_CTYPE) { cTypes.push({ cType: types.MOTION_DETECTED_CTYPE, + onRegister: function(characteristic) { + characteristic.eventEnabled = true; + subscribeUpdate(characteristic, that, true); + }, onRead: function(callback) { that.platform.getAccessoryValue(callback, true, that); }, @@ -328,6 +354,10 @@ FibaroHC2Platform.prototype = { } else if (that.CONTROL_CHARACTERISTICS[i] == types.CONTACT_SENSOR_STATE_CTYPE) { cTypes.push({ cType: types.CONTACT_SENSOR_STATE_CTYPE, + onRegister: function(characteristic) { + characteristic.eventEnabled = true; + subscribeUpdate(characteristic, that, true); + }, onRead: function(callback) { that.platform.getAccessoryValue(callback, true, that); }, @@ -441,5 +471,60 @@ FibaroDoorSensorAccessory.prototype = { return this.platform.getAccessoryServices(this); } }; +var lastPoll=0; +var pollingUpdateRunning = false; + +function startPollingUpdate( platform ) +{ + if( pollingUpdateRunning ) + return; + pollingUpdateRunning = true; + + var updateUrl = "http://"+platform.host+"/api/refreshStates?last="+lastPoll; + + request.get({ + url: updateUrl, + headers : { + "Authorization" : platform.auth + }, + json: true + }, function(err, response, json) { + if (!err && response.statusCode == 200) { + if (json != undefined) { + lastPoll = json.last; + if (json.changes != undefined) { + json.changes.map(function(s) { + if (s.value != undefined) { + + var value=parseInt(s.value); + if (isNaN(value)) + value=(s.value === "true"); + for (i=0;i Date: Wed, 16 Sep 2015 21:56:09 +0200 Subject: [PATCH 04/69] Fixed status update --- platforms/FibaroHC2.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/platforms/FibaroHC2.js b/platforms/FibaroHC2.js index 6eef466..a1336d5 100644 --- a/platforms/FibaroHC2.js +++ b/platforms/FibaroHC2.js @@ -115,6 +115,11 @@ FibaroHC2Platform.prototype = { return services; }, command: function(c,value, that) { + if (that.doNotSet != undefined && that.doNotSet == true) { + that.doNotSet = false; + return; + } + var url = "http://"+this.host+"/api/devices/"+that.id+"/action/"+c; var body = value != undefined ? JSON.stringify({ "args": [ @@ -502,6 +507,7 @@ function startPollingUpdate( platform ) for (i=0;i Date: Fri, 18 Sep 2015 17:33:36 +0200 Subject: [PATCH 05/69] Complete refactoring and adoption of new API Added support for LightSensor --- platforms/FibaroHC2.js | 540 ++++++++++++----------------------------- 1 file changed, 157 insertions(+), 383 deletions(-) diff --git a/platforms/FibaroHC2.js b/platforms/FibaroHC2.js index a1336d5..c3e92e8 100644 --- a/platforms/FibaroHC2.js +++ b/platforms/FibaroHC2.js @@ -15,6 +15,8 @@ // The default code for all HomeBridge accessories is 031-45-154. var types = require("HAP-NodeJS/accessories/types.js"); +var Service = require("HAP-NodeJS").Service; +var Characteristic = require("HAP-NodeJS").Characteristic; var request = require("request"); function FibaroHC2Platform(log, config){ @@ -47,31 +49,27 @@ FibaroHC2Platform.prototype = { json.map(function(s) { that.log("Found: " + s.type); if (s.visible == true) { - if (s.type == "com.fibaro.multilevelSwitch") { - accessory = new FibaroDimmerAccessory(that, s.name, s.id); + var accessory = null; + if (s.type == "com.fibaro.multilevelSwitch") + accessory = new FibaroDimmerAccessory(that, s); + else if (s.type == "com.fibaro.FGRM222" || s.type == "com.fibaro.FGR221") + accessory = new FibaroRollerShutterAccessory(that, s); + else if (s.type == "com.fibaro.binarySwitch" || s.type == "com.fibaro.developer.bxs.virtualBinarySwitch") + accessory = new FibaroBinarySwitchAccessory(that, s); + else if (s.type == "com.fibaro.FGMS001" || s.type == "com.fibaro.motionSensor") + accessory = new FibaroMotionSensorAccessory(that, s); + else if (s.type == "com.fibaro.temperatureSensor") + accessory = new FibaroTemperatureSensorAccessory(that, s); + else if (s.type == "com.fibaro.doorSensor") + accessory = new FibaroDoorSensorAccessory(that, s); + else if (s.type == "com.fibaro.lightSensor") + accessory = new FibaroLightSensorAccessory(that, s); + if (accessory != null) { + accessory.getServices = function() { + return that.getServices(accessory); + }; foundAccessories.push(accessory); - } else if (s.type == "com.fibaro.FGRM222") - { - accessory = new FibaroRollerShutterAccessory(that, s.name, s.id); - foundAccessories.push(accessory); - } else if (s.type == "com.fibaro.binarySwitch" || s.type == "com.fibaro.developer.bxs.virtualBinarySwitch") - { - accessory = new FibaroBinarySwitchAccessory(that, s.name, s.id); - foundAccessories.push(accessory); - } else if (s.type == "com.fibaro.FGMS001") - { - accessory = new FibaroMotionSensorAccessory(that, s.name, s.id); - foundAccessories.push(accessory); - } else if (s.type == "com.fibaro.temperatureSensor") - { - accessory = new FibaroTemperatureSensorAccessory(that, s.name, s.id); - foundAccessories.push(accessory); - } else if (s.type == "com.fibaro.doorSensor") - { - accessory = new FibaroDoorSensorAccessory(that, s.name, s.id); - foundAccessories.push(accessory); - } - + } } }) } @@ -82,49 +80,10 @@ FibaroHC2Platform.prototype = { }); }, - getAccessoryValue: function(callback, returnBoolean, that) { - var url = "http://"+that.platform.host+"/api/devices/"+that.id+"/properties/value"; - request.get({ - headers : { - "Authorization" : that.platform.auth - }, - json: true, - url: url - }, function(err, response, json) { - that.platform.log(url); - if (!err && response.statusCode == 200) { - if (returnBoolean) - callback(json.value == 0 ? 0 : 1); - else - callback(json.value); - } else { - that.platform.log("There was a problem getting value from" + that.id); - } - }) - }, - getAccessoryServices: function(that) { - var services = [{ - sType: types.ACCESSORY_INFORMATION_STYPE, - characteristics: this.informationCharacteristics(that), - }, - { - sType: that.SERVICE_TYPE, - characteristics: this.controlCharacteristics(that) - }]; - this.log("Loaded services for " + that.name) - return services; - }, command: function(c,value, that) { - if (that.doNotSet != undefined && that.doNotSet == true) { - that.doNotSet = false; - return; - } - var url = "http://"+this.host+"/api/devices/"+that.id+"/action/"+c; var body = value != undefined ? JSON.stringify({ - "args": [ - value - ] + "args": [ value ] }) : null; var method = "post"; request({ @@ -144,338 +103,153 @@ FibaroHC2Platform.prototype = { } }); }, - informationCharacteristics: function(that) - { - return [ - { - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: that.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of the accessory", - designedMaxLength: 255 - },{ - cType: types.MANUFACTURER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Fibaro", - supportEvents: false, - supportBonjour: false, - manfDescription: "Manufacturer", - designedMaxLength: 255 - },{ - cType: types.MODEL_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: that.MODEL_TYPE, - 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 + getAccessoryValue: function(callback, returnBoolean, that) { + var url = "http://"+that.platform.host+"/api/devices/"+that.id+"/properties/value"; + request.get({ + headers : { + "Authorization" : that.platform.auth + }, + json: true, + url: url + }, function(err, response, json) { + that.platform.log(url); + if (!err && response.statusCode == 200) { + if (returnBoolean) + callback(undefined, json.value == 0 ? 0 : 1); + else + callback(undefined, json.value); + } else { + that.platform.log("There was a problem getting value from" + that.id); } - ] + }) }, - controlCharacteristics: function(that) { - var cTypes = []; - var l = that.CONTROL_CHARACTERISTICS.length; - for (var i = 0; i < l; i++) { - if (that.CONTROL_CHARACTERISTICS[i] == types.NAME_CTYPE) { - cTypes.push({ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: that.name, - supportEvents: true, - supportBonjour: false, - manfDescription: "Name of service", - designedMaxLength: 255 - }); - } else if (that.CONTROL_CHARACTERISTICS[i] == types.POWER_STATE_CTYPE) { - cTypes.push({ - cType: types.POWER_STATE_CTYPE, - onRegister: function(characteristic) { - characteristic.eventEnabled = true; - subscribeUpdate(characteristic, that, true); - }, - onUpdate: function(value) { - if (value == 0) { - that.platform.command("turnOff", null, that) - } else { - that.platform.command("turnOn", null, that) - } - }, - onRead: function(callback) { - that.platform.getAccessoryValue(callback, true, that); - }, - perms: ["pw","pr","ev"], - format: "bool", - initialValue: 0, - supportEvents: true, - supportBonjour: false, - manfDescription: "Change the power state", - designedMaxLength: 1 - }); - } else if (that.CONTROL_CHARACTERISTICS[i] == types.BRIGHTNESS_CTYPE) { - cTypes.push({ - cType: types.BRIGHTNESS_CTYPE, - onRegister: function(characteristic) { - characteristic.eventEnabled = true; - subscribeUpdate(characteristic, that, false); - }, - onUpdate: function(value) { that.platform.command("setValue", value, that); }, - onRead: function(callback) { - that.platform.getAccessoryValue(callback, false, that); - }, - perms: ["pw","pr","ev"], - format: "int", - initialValue: 0, - supportEvents: true, - supportBonjour: false, - manfDescription: "Adjust Brightness of Light", - designedMinValue: 0, - designedMaxValue: 100, - designedMinStep: 10, - unit: "%" - }); - } else if (that.CONTROL_CHARACTERISTICS[i] == types.WINDOW_COVERING_CURRENT_POSITION_CTYPE) { - cTypes.push({ - cType: types.WINDOW_COVERING_CURRENT_POSITION_CTYPE, - onRegister: function(characteristic) { - characteristic.eventEnabled = true; - subscribeUpdate(characteristic, that, false); - }, - onRead: function(callback) { - that.platform.getAccessoryValue(callback, false, that); - }, - perms: ["pr","ev"], - format: "int", - initialValue: 0, - supportEvents: false, - supportBonjour: false, - manfDescription: "Current Blind Position", - designedMinValue: 0, - designedMaxValue: 100, - designedMinStep: 1, - unit: "%" - }); - } else if (that.CONTROL_CHARACTERISTICS[i] == types.WINDOW_COVERING_TARGET_POSITION_CTYPE) { - cTypes.push({ - cType: types.WINDOW_COVERING_TARGET_POSITION_CTYPE, - onRegister: function(characteristic) { - characteristic.eventEnabled = true; - subscribeUpdate(characteristic, that, false); - }, - onUpdate: function(value) { that.platform.command("setValue", value, that); }, - onRead: function(callback) { - that.platform.getAccessoryValue(callback, false, that); - }, - perms: ["pw","pr","ev"], - format: "int", - initialValue: 0, - supportEvents: false, - supportBonjour: false, - manfDescription: "Target Blind Position", - designedMinValue: 0, - designedMaxValue: 100, - designedMinStep: 1, - unit: "%" - }); - } else if (that.CONTROL_CHARACTERISTICS[i] == types.WINDOW_COVERING_OPERATION_STATE_CTYPE) { - cTypes.push({ - cType: types.WINDOW_COVERING_OPERATION_STATE_CTYPE, - perms: ["pr","ev"], - format: "int", - initialValue: 0, - supportEvents: false, - supportBonjour: false, - manfDescription: "Position State", - designedMinValue: 0, - designedMaxValue: 2, - designedMinStep: 1, - }); - } else if (that.CONTROL_CHARACTERISTICS[i] == types.CURRENT_TEMPERATURE_CTYPE) { - cTypes.push({ - cType: types.CURRENT_TEMPERATURE_CTYPE, - onRegister: function(characteristic) { - characteristic.eventEnabled = true; - subscribeUpdate(characteristic, that, false); - }, - onRead: function(callback) { - that.platform.getAccessoryValue(callback, false, that); - }, - perms: ["pr","ev"], - format: "float", - unit: "celsius", - stepValue: 0.1, - initialValue: 0, - supportEvents: true, - supportBonjour: false, - manfDescription: "Get current temperature" - }); - } else if (that.CONTROL_CHARACTERISTICS[i] == types.MOTION_DETECTED_CTYPE) { - cTypes.push({ - cType: types.MOTION_DETECTED_CTYPE, - onRegister: function(characteristic) { - characteristic.eventEnabled = true; - subscribeUpdate(characteristic, that, true); - }, - onRead: function(callback) { - that.platform.getAccessoryValue(callback, true, that); - }, - perms: ["pr","ev"], - format: "bool", - initialValue: 0, - supportEvents: true, - supportBonjour: false, - manfDescription: "Detect motion", - designedMaxLength: 1 - }); - } else if (that.CONTROL_CHARACTERISTICS[i] == types.CONTACT_SENSOR_STATE_CTYPE) { - cTypes.push({ - cType: types.CONTACT_SENSOR_STATE_CTYPE, - onRegister: function(characteristic) { - characteristic.eventEnabled = true; - subscribeUpdate(characteristic, that, true); - }, - onRead: function(callback) { - that.platform.getAccessoryValue(callback, true, that); - }, - perms: ["pr","ev"], - format: "bool", - initialValue: 0, - supportEvents: true, - supportBonjour: false, - manfDescription: "Detect door contact", - designedMaxLength: 1 - }); - } - } - return cTypes - } + getInformationService: function(homebridgeAccessory) { + var informationService = new Service.AccessoryInformation(); + informationService + .setCharacteristic(Characteristic.Name, homebridgeAccessory.name) + .setCharacteristic(Characteristic.Manufacturer, homebridgeAccessory.manufacturer) + .setCharacteristic(Characteristic.Model, homebridgeAccessory.model) + .setCharacteristic(Characteristic.SerialNumber, homebridgeAccessory.serialNumber); + return informationService; + }, + bindCharacteristicEvents: function(characteristic, homebridgeAccessory) { + var onOff = characteristic.format == "bool" ? true : false; + var readOnly = characteristic.writable == undefined || characteristic.writable == false ? true : false; + subscribeUpdate(characteristic, homebridgeAccessory, onOff); + if (!readOnly) { + characteristic + .on('set', function(value, callback, context) { + if( context !== 'fromFibaro' ) { + if (onOff) + homebridgeAccessory.platform.command(value == 0 ? "turnOff": "turnOn", null, homebridgeAccessory); + else + homebridgeAccessory.platform.command("setValue", value, homebridgeAccessory); + } + callback(); + }.bind(this) ); + } + characteristic + .on('get', function(callback) { + homebridgeAccessory.platform.getAccessoryValue(callback, onOff, homebridgeAccessory); + }.bind(this) ); + }, + getServices: function(homebridgeAccessory) { + var informationService = homebridgeAccessory.platform.getInformationService(homebridgeAccessory); + for (var i=0; i < homebridgeAccessory.characteristics.length; i++) { + var characteristic = homebridgeAccessory.controlService.getCharacteristic(homebridgeAccessory.characteristics[i]); + if (characteristic == undefined) + characteristic = homebridgeAccessory.controlService.addCharacteristic(homebridgeAccessory.characteristics[i]); + homebridgeAccessory.platform.bindCharacteristicEvents(characteristic, homebridgeAccessory); + } + + return [informationService, homebridgeAccessory.controlService]; + } } -function FibaroDimmerAccessory(platform, name, id) { - // device info - this.platform = platform; - this.name = name; - this.id = id; - this.MODEL_TYPE = "Dimmer"; - this.SERVICE_TYPE = types.LIGHTBULB_STYPE; - this.CONTROL_CHARACTERISTICS = [types.NAME_CTYPE, types.POWER_STATE_CTYPE, types.BRIGHTNESS_CTYPE]; +function FibaroDimmerAccessory(platform, s) { + this.platform = platform; + this.remoteAccessory = s; + this.id = s.id; + this.name = s.name; + this.model = s.type; + this.manufacturer = "Fibaro"; + this.serialNumber = ""; + this.controlService = new Service.Lightbulb(this.name); + this.characteristics = [Characteristic.On, Characteristic.Brightness]; } -FibaroDimmerAccessory.prototype = { - getServices: function() { - return this.platform.getAccessoryServices(this); - } -}; - -function FibaroRollerShutterAccessory(platform, name, id) { - // device info - this.platform = platform; - this.name = name; - this.id = id; - this.MODEL_TYPE = "Roller Shutter 2"; - this.SERVICE_TYPE = types.WINDOW_COVERING_STYPE; - this.CONTROL_CHARACTERISTICS = [types.NAME_CTYPE, types.WINDOW_COVERING_CURRENT_POSITION_CTYPE, types.WINDOW_COVERING_TARGET_POSITION_CTYPE, types.WINDOW_COVERING_OPERATION_STATE_CTYPE]; - +function FibaroRollerShutterAccessory(platform, s) { + this.platform = platform; + this.remoteAccessory = s; + this.id = s.id; + this.name = s.name; + this.model = s.type; + this.manufacturer = "Fibaro"; + this.serialNumber = ""; + this.controlService = new Service.WindowCovering(this.name); + this.characteristics = [Characteristic.CurrentPosition, Characteristic.TargetPosition, Characteristic.PositionState]; } -FibaroRollerShutterAccessory.prototype = { - getServices: function() { - return this.platform.getAccessoryServices(this); - } -}; - -function FibaroBinarySwitchAccessory(platform, name, id) { - // device info - this.platform = platform; - this.name = name; - this.id = id; - this.MODEL_TYPE = "Binary Switch"; - this.SERVICE_TYPE = types.SWITCH_STYPE; - this.CONTROL_CHARACTERISTICS = [types.NAME_CTYPE, types.POWER_STATE_CTYPE]; +function FibaroBinarySwitchAccessory(platform, s) { + this.platform = platform; + this.remoteAccessory = s; + this.id = s.id; + this.name = s.name; + this.model = s.type; + this.manufacturer = "Fibaro"; + this.serialNumber = ""; + this.controlService = new Service.Switch(this.name); + this.characteristics = [Characteristic.On]; } -FibaroBinarySwitchAccessory.prototype = { - getServices: function() { - return this.platform.getAccessoryServices(this); - } -}; - -function FibaroTemperatureSensorAccessory(platform, name, id) { - // device info - this.platform = platform; - this.name = name; - this.id = id; - this.MODEL_TYPE = "Temperature Sensor"; - this.SERVICE_TYPE = types.TEMPERATURE_SENSOR_STYPE; - this.CONTROL_CHARACTERISTICS = [types.NAME_CTYPE, types.CURRENT_TEMPERATURE_CTYPE]; +function FibaroMotionSensorAccessory(platform, s) { + this.platform = platform; + this.remoteAccessory = s; + this.id = s.id; + this.name = s.name; + this.model = "Motion Sensor"; + this.manufacturer = "Fibaro"; + this.serialNumber = ""; + this.controlService = new Service.MotionSensor(this.name); + this.characteristics = [Characteristic.MotionDetected]; } -FibaroTemperatureSensorAccessory.prototype = { - getServices: function() { - return this.platform.getAccessoryServices(this); - } -}; - -function FibaroMotionSensorAccessory(platform, name, id) { - // device info - this.platform = platform; - this.name = name; - this.id = id; - this.MODEL_TYPE = "Motion Sensor"; - this.SERVICE_TYPE = types.MOTION_SENSOR_STYPE; - this.CONTROL_CHARACTERISTICS = [types.NAME_CTYPE, types.MOTION_DETECTED_CTYPE]; +function FibaroTemperatureSensorAccessory(platform, s) { + this.platform = platform; + this.remoteAccessory = s; + this.id = s.id; + this.name = s.name; + this.model = s.type; + this.manufacturer = "Fibaro"; + this.serialNumber = ""; + this.controlService = new Service.TemperatureSensor(this.name); + this.characteristics = [Characteristic.CurrentTemperature]; } -FibaroMotionSensorAccessory.prototype = { - getServices: function() { - return this.platform.getAccessoryServices(this); - } -}; - -function FibaroDoorSensorAccessory(platform, name, id) { - // device info - this.platform = platform; - this.name = name; - this.id = id; - this.MODEL_TYPE = "Door Sensor"; - this.SERVICE_TYPE = types.CONTACT_SENSOR_STYPE; - this.CONTROL_CHARACTERISTICS = [types.NAME_CTYPE, types.CONTACT_SENSOR_STATE_CTYPE]; +function FibaroDoorSensorAccessory(platform, s) { + this.platform = platform; + this.remoteAccessory = s; + this.id = s.id; + this.name = s.name; + this.model = s.type; + this.manufacturer = "Fibaro"; + this.serialNumber = ""; + this.controlService = new Service.ContactSensor(this.name); + this.characteristics = [Characteristic.ContactSensorState]; +} + +function FibaroLightSensorAccessory(platform, s) { + this.platform = platform; + this.remoteAccessory = s; + this.id = s.id; + this.name = s.name; + this.model = s.type; + this.manufacturer = "Fibaro"; + this.serialNumber = ""; + this.controlService = new Service.LightSensor(this.name); + this.characteristics = [Characteristic.CurrentAmbientLightLevel]; } -FibaroDoorSensorAccessory.prototype = { - getServices: function() { - return this.platform.getAccessoryServices(this); - } -}; var lastPoll=0; var pollingUpdateRunning = false; @@ -507,11 +281,10 @@ function startPollingUpdate( platform ) for (i=0;i Date: Sat, 19 Sep 2015 10:12:35 +0200 Subject: [PATCH 06/69] Added support for Fibaro Outlet --- platforms/FibaroHC2.js | 94 ++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 59 deletions(-) diff --git a/platforms/FibaroHC2.js b/platforms/FibaroHC2.js index c3e92e8..c18d0dd 100644 --- a/platforms/FibaroHC2.js +++ b/platforms/FibaroHC2.js @@ -64,10 +64,20 @@ FibaroHC2Platform.prototype = { accessory = new FibaroDoorSensorAccessory(that, s); else if (s.type == "com.fibaro.lightSensor") accessory = new FibaroLightSensorAccessory(that, s); + else if (s.type == "com.fibaro.FGWP101") + accessory = new FibaroOutletAccessory(that, s); + if (accessory != null) { accessory.getServices = function() { return that.getServices(accessory); }; + accessory.platform = that; + accessory.remoteAccessory = s; + accessory.id = s.id; + accessory.name = s.name; + accessory.model = s.type; + accessory.manufacturer = "Fibaro"; + accessory.serialNumber = ""; foundAccessories.push(accessory); } } @@ -103,23 +113,30 @@ FibaroHC2Platform.prototype = { } }); }, - getAccessoryValue: function(callback, returnBoolean, that) { - var url = "http://"+that.platform.host+"/api/devices/"+that.id+"/properties/value"; + getAccessoryValue: function(callback, returnBoolean, homebridgeAccessory, powerValue) { + var url = "http://"+homebridgeAccessory.platform.host+"/api/devices/"+homebridgeAccessory.id+"/properties/"; + if (powerValue) + url = url + "power"; + else + url = url + "value"; + request.get({ headers : { - "Authorization" : that.platform.auth + "Authorization" : homebridgeAccessory.platform.auth }, json: true, url: url }, function(err, response, json) { - that.platform.log(url); + homebridgeAccessory.platform.log(url); if (!err && response.statusCode == 200) { - if (returnBoolean) + if (powerValue) { + callback(undefined, parseFloat(json.value) > 1.0 ? true : false); + } else if (returnBoolean) callback(undefined, json.value == 0 ? 0 : 1); else callback(undefined, json.value); } else { - that.platform.log("There was a problem getting value from" + that.id); + homebridgeAccessory.platform.log("There was a problem getting value from" + homebridgeAccessory.id); } }) }, @@ -135,6 +152,7 @@ FibaroHC2Platform.prototype = { bindCharacteristicEvents: function(characteristic, homebridgeAccessory) { var onOff = characteristic.format == "bool" ? true : false; var readOnly = characteristic.writable == undefined || characteristic.writable == false ? true : false; + var powerValue = (characteristic.UUID == "00000026-0000-1000-8000-0026BB765291") ? true : false; subscribeUpdate(characteristic, homebridgeAccessory, onOff); if (!readOnly) { characteristic @@ -150,7 +168,7 @@ FibaroHC2Platform.prototype = { } characteristic .on('get', function(callback) { - homebridgeAccessory.platform.getAccessoryValue(callback, onOff, homebridgeAccessory); + homebridgeAccessory.platform.getAccessoryValue(callback, onOff, homebridgeAccessory, powerValue); }.bind(this) ); }, getServices: function(homebridgeAccessory) { @@ -167,89 +185,45 @@ FibaroHC2Platform.prototype = { } function FibaroDimmerAccessory(platform, s) { - this.platform = platform; - this.remoteAccessory = s; - this.id = s.id; - this.name = s.name; - this.model = s.type; - this.manufacturer = "Fibaro"; - this.serialNumber = ""; this.controlService = new Service.Lightbulb(this.name); this.characteristics = [Characteristic.On, Characteristic.Brightness]; } function FibaroRollerShutterAccessory(platform, s) { - this.platform = platform; - this.remoteAccessory = s; - this.id = s.id; - this.name = s.name; - this.model = s.type; - this.manufacturer = "Fibaro"; - this.serialNumber = ""; this.controlService = new Service.WindowCovering(this.name); this.characteristics = [Characteristic.CurrentPosition, Characteristic.TargetPosition, Characteristic.PositionState]; } function FibaroBinarySwitchAccessory(platform, s) { - this.platform = platform; - this.remoteAccessory = s; - this.id = s.id; - this.name = s.name; - this.model = s.type; - this.manufacturer = "Fibaro"; - this.serialNumber = ""; this.controlService = new Service.Switch(this.name); this.characteristics = [Characteristic.On]; } function FibaroMotionSensorAccessory(platform, s) { - this.platform = platform; - this.remoteAccessory = s; - this.id = s.id; - this.name = s.name; - this.model = "Motion Sensor"; - this.manufacturer = "Fibaro"; - this.serialNumber = ""; this.controlService = new Service.MotionSensor(this.name); this.characteristics = [Characteristic.MotionDetected]; } function FibaroTemperatureSensorAccessory(platform, s) { - this.platform = platform; - this.remoteAccessory = s; - this.id = s.id; - this.name = s.name; - this.model = s.type; - this.manufacturer = "Fibaro"; - this.serialNumber = ""; this.controlService = new Service.TemperatureSensor(this.name); this.characteristics = [Characteristic.CurrentTemperature]; } function FibaroDoorSensorAccessory(platform, s) { - this.platform = platform; - this.remoteAccessory = s; - this.id = s.id; - this.name = s.name; - this.model = s.type; - this.manufacturer = "Fibaro"; - this.serialNumber = ""; this.controlService = new Service.ContactSensor(this.name); this.characteristics = [Characteristic.ContactSensorState]; } function FibaroLightSensorAccessory(platform, s) { - this.platform = platform; - this.remoteAccessory = s; - this.id = s.id; - this.name = s.name; - this.model = s.type; - this.manufacturer = "Fibaro"; - this.serialNumber = ""; this.controlService = new Service.LightSensor(this.name); this.characteristics = [Characteristic.CurrentAmbientLightLevel]; } +function FibaroOutletAccessory(platform, s) { + this.controlService = new Service.Outlet(this.name); + this.characteristics = [Characteristic.On, Characteristic.OutletInUse]; +} + var lastPoll=0; var pollingUpdateRunning = false; @@ -281,10 +255,12 @@ function startPollingUpdate( platform ) for (i=0;i 1.0 ? true : false, undefined, 'fromFibaro'); + } else if ((subscription.onOff && typeof(value) == "boolean") || !subscription.onOff) + subscription.characteristic.setValue(value, undefined, 'fromFibaro'); else - subscription.characteristic.setValue(value == 0 ? false : true, undefined, 'fromFibaro'); + subscription.characteristic.setValue(value == 0 ? false : true, undefined, 'fromFibaro'); } } } From 656a8057acbd177995f5a3dd9b63262082387d2b Mon Sep 17 00:00:00 2001 From: S'pht'Kr Date: Sat, 19 Sep 2015 13:00:01 +0200 Subject: [PATCH 07/69] Initial work on reading, seems to work okay. --- platforms/ZWayServer.js | 71 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/platforms/ZWayServer.js b/platforms/ZWayServer.js index 5b79c8e..7c8b925 100644 --- a/platforms/ZWayServer.js +++ b/platforms/ZWayServer.js @@ -11,6 +11,7 @@ function ZWayServerPlatform(log, config){ this.url = config["url"]; this.login = config["login"]; this.password = config["password"]; + this.opt_in = config["opt_in"]; this.name_overrides = config["name_overrides"]; this.batteryLow = config["battery_low_level"] || 15; this.pollInterval = config["poll_interval"] || 2; @@ -110,6 +111,7 @@ ZWayServerPlatform.prototype = { for(var i = 0; i < devices.length; i++){ var vdev = devices[i]; if(vdev.tags.indexOf("Homebridge:Skip") >= 0) { debug("Tag says skip!"); continue; } + if(this.opt_in && vdev.tags.indexOf("Homebridge:Include") < 0) continue; var gdid = vdev.id.replace(/^(.*?)_zway_(\d+-\d+)-\d.*/, '$1_$2'); var gd = groupedDevices[gdid] || (groupedDevices[gdid] = {devices: [], types: {}, primary: undefined}); gd.devices.push(vdev); @@ -222,6 +224,27 @@ ZWayServerAccessory.prototype = { }); }, + rgb2hsv: function(obj) { + var r = obj.r/255, g = obj.g/255, b = obj.b/255; + var max, min, d, h, s, v; + + if (min === max) { + // shade of gray + return [0, 0, r]; + } + + min = Math.min(r, Math.min(g, b)); + max = Math.max(r, Math.max(g, b)); + + var d = (r === min) ? g - b : ((b === min) ? r - g : b - r); + h = (r === min) ? 3 : ((b === min) ? 1 : 5); + h = 60 * (h - d/(max - min)); + s = (max - min) / max; + v = max; + return {"h": h, "s": s * 100, "v": v}; + } + , + getVDevServices: function(vdev){ var typeKey = ZWayServerPlatform.getVDevTypeKey(vdev); var services = [], service; @@ -272,6 +295,8 @@ ZWayServerAccessory.prototype = { this.uuidToTypeKeyMap = map = {}; map[(new Characteristic.On).UUID] = ["switchBinary","switchMultilevel"]; map[(new Characteristic.Brightness).UUID] = ["switchMultilevel"]; + map[(new Characteristic.Hue).UUID] = ["switchRGBW"]; + map[(new Characteristic.Saturation).UUID] = ["switchRGBW"]; map[(new Characteristic.CurrentTemperature).UUID] = ["sensorMultilevel.Temperature","thermostat"]; map[(new Characteristic.TargetTemperature).UUID] = ["thermostat"]; map[(new Characteristic.TemperatureDisplayUnits).UUID] = ["sensorMultilevel.Temperature","thermostat"]; //TODO: Always a fixed result @@ -310,7 +335,7 @@ ZWayServerAccessory.prototype = { } , configureCharacteristic: function(cx, vdev){ - var that = this; + var that, accessory = this; // Add this combination to the maps... if(!this.platform.cxVDevMap[vdev.id]) this.platform.cxVDevMap[vdev.id] = []; @@ -381,6 +406,50 @@ ZWayServerAccessory.prototype = { return cx; } + if(cx instanceof Characteristic.Hue){ + cx.zway_getValueFromVDev = function(vdev){ + return accessory.rgb2hsv(vdev.metrics.color).h; + }; + cx.value = cx.zway_getValueFromVDev(vdev); + cx.on('get', function(callback, context){ + debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"..."); + this.getVDev(vdev).then(function(result){ + debug("Got value: " + cx.zway_getValueFromVDev(result.data) + ", for " + vdev.metrics.title + "."); + callback(false, cx.zway_getValueFromVDev(result.data)); + }); + }.bind(this)); + + cx.writeable = false; + //cx.on('set', function(level, callback){ + // this.command(vdev, "exact", {level: "on", "color.r": 255, "color.g": 0, "color.b": 0}).then(function(result){ + // callback(); + // }); + //}.bind(this)); + return cx; + } + + if(cx instanceof Characteristic.Saturation){ + cx.zway_getValueFromVDev = function(vdev){ + return accessory.rgb2hsv(vdev.metrics.color).s; + }; + cx.value = cx.zway_getValueFromVDev(vdev); + cx.on('get', function(callback, context){ + debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"..."); + this.getVDev(vdev).then(function(result){ + debug("Got value: " + cx.zway_getValueFromVDev(result.data) + ", for " + vdev.metrics.title + "."); + callback(false, cx.zway_getValueFromVDev(result.data)); + }); + }.bind(this)); + + cx.writeable = false; + //cx.on('set', function(level, callback){ + // this.command(vdev, "exact", {level: "on", "color.r": 255, "color.g": 0, "color.b": 0}).then(function(result){ + // callback(); + // }); + //}.bind(this)); + return cx; + } + if(cx instanceof Characteristic.CurrentTemperature){ cx.zway_getValueFromVDev = function(vdev){ return vdev.metrics.level; From ca232f3111305803b2d418764312c72762c534cf Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Fri, 25 Sep 2015 00:26:07 -0400 Subject: [PATCH 08/69] Noted updated for requirement to successfully get HAP-NodeJS installed. --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 641e32f..2748494 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,13 @@ You'll also need some patience, as Siri can be very strict about sentence struct # Getting Started -OK, if you're still excited enough about ordering Siri to make your coffee (which, who wouldn't be!) then here's how to set things up. First, clone this repo: +OK, if you're still excited enough about ordering Siri to make your coffee (which, who wouldn't be!) then here's how to set things up. + +**Note:** You'll need to make sure you have the following package installed for your platform. + + * libavahi-compat-libdnssd-dev + +First, clone this repo: $ git clone https://github.com/nfarina/homebridge.git $ cd homebridge @@ -60,6 +66,7 @@ OK, if you're still excited enough about ordering Siri to make your coffee (whic **Node**: You'll need to have NodeJS version 0.12.x or better installed for required submodule `HAP-NodeJS` to load. + Now you should be able to run the homebridge server: $ cd homebridge From bb3381e10af1c472f81422608b8b91e240a8cc8c Mon Sep 17 00:00:00 2001 From: Nick Farina Date: Thu, 24 Sep 2015 22:15:58 -0700 Subject: [PATCH 09/69] Update README.md --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2748494..ab3b98a 100644 --- a/README.md +++ b/README.md @@ -54,9 +54,7 @@ You'll also need some patience, as Siri can be very strict about sentence struct OK, if you're still excited enough about ordering Siri to make your coffee (which, who wouldn't be!) then here's how to set things up. -**Note:** You'll need to make sure you have the following package installed for your platform. - - * libavahi-compat-libdnssd-dev +**Note:** If you're running on Linux, you'll need to make sure you have the `libavahi-compat-libdnssd-dev` package installed. First, clone this repo: @@ -64,7 +62,7 @@ First, clone this repo: $ cd homebridge $ npm install -**Node**: You'll need to have NodeJS version 0.12.x or better installed for required submodule `HAP-NodeJS` to load. +**Note**: You'll need to have NodeJS version 0.12.x or better installed for required submodule `HAP-NodeJS` to load. Now you should be able to run the homebridge server: From c6c45d9e3db184b94b1fd9a32e2b38264dc2e9df Mon Sep 17 00:00:00 2001 From: ilcato Date: Sun, 27 Sep 2015 13:00:57 +0200 Subject: [PATCH 10/69] Fixed incompatibility with new version of HAP-Node.js --- platforms/FibaroHC2.js | 65 +++++++++++------------------------------- 1 file changed, 16 insertions(+), 49 deletions(-) diff --git a/platforms/FibaroHC2.js b/platforms/FibaroHC2.js index c18d0dd..4567b40 100644 --- a/platforms/FibaroHC2.js +++ b/platforms/FibaroHC2.js @@ -51,22 +51,21 @@ FibaroHC2Platform.prototype = { if (s.visible == true) { var accessory = null; if (s.type == "com.fibaro.multilevelSwitch") - accessory = new FibaroDimmerAccessory(that, s); + accessory = new FibaroAccessory(new Service.Lightbulb(s.name), [Characteristic.On, Characteristic.Brightness]); else if (s.type == "com.fibaro.FGRM222" || s.type == "com.fibaro.FGR221") - accessory = new FibaroRollerShutterAccessory(that, s); + accessory = new FibaroAccessory(new Service.WindowCovering(s.name), [Characteristic.CurrentPosition, Characteristic.TargetPosition, Characteristic.PositionState]); else if (s.type == "com.fibaro.binarySwitch" || s.type == "com.fibaro.developer.bxs.virtualBinarySwitch") - accessory = new FibaroBinarySwitchAccessory(that, s); + accessory = new FibaroAccessory(new Service.Switch(s.name), [Characteristic.On]); else if (s.type == "com.fibaro.FGMS001" || s.type == "com.fibaro.motionSensor") - accessory = new FibaroMotionSensorAccessory(that, s); + accessory = new FibaroAccessory(new Service.MotionSensor(s.name), [Characteristic.MotionDetected]); else if (s.type == "com.fibaro.temperatureSensor") - accessory = new FibaroTemperatureSensorAccessory(that, s); + accessory = new FibaroAccessory(new Service.TemperatureSensor(s.name), [Characteristic.CurrentTemperature]); else if (s.type == "com.fibaro.doorSensor") - accessory = new FibaroDoorSensorAccessory(that, s); + accessory = new FibaroAccessory(new Service.ContactSensor(s.name), [Characteristic.ContactSensorState]); else if (s.type == "com.fibaro.lightSensor") - accessory = new FibaroLightSensorAccessory(that, s); + accessory = new FibaroAccessory(new Service.LightSensor(s.name), [Characteristic.CurrentAmbientLightLevel]); else if (s.type == "com.fibaro.FGWP101") - accessory = new FibaroOutletAccessory(that, s); - + accessory = new FibaroAccessory(new Service.Outlet(s.name), [Characteristic.On, Characteristic.OutletInUse]); if (accessory != null) { accessory.getServices = function() { return that.getServices(accessory); @@ -150,8 +149,11 @@ FibaroHC2Platform.prototype = { return informationService; }, bindCharacteristicEvents: function(characteristic, homebridgeAccessory) { - var onOff = characteristic.format == "bool" ? true : false; - var readOnly = characteristic.writable == undefined || characteristic.writable == false ? true : false; + var onOff = characteristic.props.format == "bool" ? true : false; + var readOnly = true; + for (var i = 0; i < characteristic.props.perms.length; i++) + if (characteristic.props.perms[i] == "pw") + readOnly = false; var powerValue = (characteristic.UUID == "00000026-0000-1000-8000-0026BB765291") ? true : false; subscribeUpdate(characteristic, homebridgeAccessory, onOff); if (!readOnly) { @@ -184,44 +186,9 @@ FibaroHC2Platform.prototype = { } } -function FibaroDimmerAccessory(platform, s) { - this.controlService = new Service.Lightbulb(this.name); - this.characteristics = [Characteristic.On, Characteristic.Brightness]; -} - -function FibaroRollerShutterAccessory(platform, s) { - this.controlService = new Service.WindowCovering(this.name); - this.characteristics = [Characteristic.CurrentPosition, Characteristic.TargetPosition, Characteristic.PositionState]; -} - -function FibaroBinarySwitchAccessory(platform, s) { - this.controlService = new Service.Switch(this.name); - this.characteristics = [Characteristic.On]; -} - -function FibaroMotionSensorAccessory(platform, s) { - this.controlService = new Service.MotionSensor(this.name); - this.characteristics = [Characteristic.MotionDetected]; -} - -function FibaroTemperatureSensorAccessory(platform, s) { - this.controlService = new Service.TemperatureSensor(this.name); - this.characteristics = [Characteristic.CurrentTemperature]; -} - -function FibaroDoorSensorAccessory(platform, s) { - this.controlService = new Service.ContactSensor(this.name); - this.characteristics = [Characteristic.ContactSensorState]; -} - -function FibaroLightSensorAccessory(platform, s) { - this.controlService = new Service.LightSensor(this.name); - this.characteristics = [Characteristic.CurrentAmbientLightLevel]; -} - -function FibaroOutletAccessory(platform, s) { - this.controlService = new Service.Outlet(this.name); - this.characteristics = [Characteristic.On, Characteristic.OutletInUse]; +function FibaroAccessory(controlService, characteristics) { + this.controlService = controlService; + this.characteristics = characteristics; } var lastPoll=0; From 2a66eac0584c9d38d97bcf987874d1e67bc1c2a0 Mon Sep 17 00:00:00 2001 From: S'pht'Kr Date: Mon, 28 Sep 2015 06:12:25 +0200 Subject: [PATCH 11/69] Fix for multiple devices, refactor, and custom Service+Characteristic --- platforms/YamahaAVR.js | 219 +++++++++++++++++++++++++---------------- 1 file changed, 133 insertions(+), 86 deletions(-) diff --git a/platforms/YamahaAVR.js b/platforms/YamahaAVR.js index f554fa0..1659c0f 100644 --- a/platforms/YamahaAVR.js +++ b/platforms/YamahaAVR.js @@ -1,5 +1,10 @@ var types = require("HAP-NodeJS/accessories/types.js"); +var inherits = require('util').inherits; +var debug = require('debug')('YamahaAVR'); +var Service = require("HAP-NodeJS").Service; +var Characteristic = require("HAP-NodeJS").Characteristic; var Yamaha = require('yamaha-nodejs'); +var Q = require('q'); var mdns = require('mdns'); //workaround for raspberry pi var sequence = [ @@ -12,10 +17,53 @@ function YamahaAVRPlatform(log, config){ this.log = log; this.config = config; this.playVolume = config["play_volume"]; + this.minVolume = config["min_volume"] || -50.0; + this.maxVolume = config["max_volume"] || -20.0; + this.gapVolume = this.maxVolume - this.minVolume; this.setMainInputTo = config["setMainInputTo"]; + this.expectedDevices = config["expected_devices"] || 100; + this.discoveryTimeout = config["discovery_timeout"] || 30; this.browser = mdns.createBrowser(mdns.tcp('http'), {resolverSequence: sequence}); } +// Custom Characteristics and service... + +YamahaAVRPlatform.AudioVolume = function() { + Characteristic.call(this, 'Audio Volume', '00001001-0000-1000-8000-135D67EC4377'); + this.format = 'uint8'; + this.unit = 'percentage'; + this.maximumValue = 100; + this.minimumValue = 0; + this.stepValue = 1; + this.readable = true; + this.writable = true; + this.supportsEventNotification = true; + this.value = this.getDefaultValue(); +}; +inherits(YamahaAVRPlatform.AudioVolume, Characteristic); + +YamahaAVRPlatform.Muting = function() { + Characteristic.call(this, 'Muting', '00001002-0000-1000-8000-135D67EC4377'); + this.format = 'bool'; + this.readable = true; + this.writable = true; + this.supportsEventNotification = true; + this.value = this.getDefaultValue(); +}; +inherits(YamahaAVRPlatform.Muting, Characteristic); + +YamahaAVRPlatform.AudioDeviceService = function(displayName, subtype) { + Service.call(this, displayName, '00000001-0000-1000-8000-135D67EC4377', subtype); + + // Required Characteristics + this.addCharacteristic(YamahaAVRPlatform.AudioVolume); + + // Optional Characteristics + this.addOptionalCharacteristic(YamahaAVRPlatform.Muting); +}; +inherits(YamahaAVRPlatform.AudioDeviceService, Service); + + YamahaAVRPlatform.prototype = { accessories: function(callback) { this.log("Getting Yamaha AVR devices."); @@ -24,7 +72,9 @@ YamahaAVRPlatform.prototype = { var browser = this.browser; browser.stop(); browser.removeAllListeners('serviceUp'); // cleanup listeners - + var accessories = []; + var timer, timeElapsed = 0, checkCyclePeriod = 5000; + browser.on('serviceUp', function(service){ var name = service.name; //console.log('Found HTTP service "' + name + '"'); @@ -36,12 +86,36 @@ YamahaAVRPlatform.prototype = { var sysId = sysConfig.YAMAHA_AV.System[0].Config[0].System_ID[0]; that.log("Found Yamaha " + sysModel + " - " + sysId + ", \"" + name + "\""); var accessory = new YamahaAVRAccessory(that.log, that.config, service, yamaha, sysConfig); - callback([accessory]); + accessories.push(accessory); + if(accessories.length >= this.expectedDevices) + timeoutFunction(); // We're done, call the timeout function now. + //callback([accessory]); }, function(err){ return; - }) + }); }); browser.start(); + + // The callback can only be called once...so we'll have to find as many as we can + // in a fixed time and then call them in. + var timeoutFunction = function(){ + if(accessories.length >= that.expectedDevices){ + clearTimeout(timer); + } else { + timeElapsed += checkCyclePeriod; + if(timeElapsed > that.discoveryTimeout * 1000){ + that.log("Waited " + that.discoveryTimeout + " seconds, stopping discovery."); + } else { + timer = setTimeout(timeoutFunction, checkCyclePeriod); + return; + } + } + browser.stop(); + browser.removeAllListeners('serviceUp'); + that.log("Discovery finished, found " + accessories.length + " Yamaha AVR devices."); + callback(accessories); + }; + timer = setTimeout(timeoutFunction, checkCyclePeriod); } }; @@ -56,6 +130,9 @@ function YamahaAVRAccessory(log, config, mdnsService, yamaha, sysConfig) { this.serviceName = mdnsService.name + " Speakers"; this.setMainInputTo = config["setMainInputTo"]; this.playVolume = this.config["play_volume"]; + this.minVolume = config["min_volume"] || -50.0; + this.maxVolume = config["max_volume"] || -20.0; + this.gapVolume = this.maxVolume - this.minVolume; } YamahaAVRAccessory.prototype = { @@ -66,104 +143,74 @@ YamahaAVRAccessory.prototype = { if (playing) { - yamaha.powerOn().then(function(){ + return yamaha.powerOn().then(function(){ if (that.playVolume) return yamaha.setVolumeTo(that.playVolume*10); - else return { then: function(f, r){ f(); } }; + else return Q(); }).then(function(){ if (that.setMainInputTo) return yamaha.setMainInputTo(that.setMainInputTo); - else return { then: function(f, r){ f(); } }; + else return Q(); }).then(function(){ if (that.setMainInputTo == "AirPlay") return yamaha.SendXMLToReceiver( 'Play' ); - else return { then: function(f, r){ f(); } }; - //else return Promise.fulfilled(undefined); + else return Q(); }); } else { - yamaha.powerOff(); + return yamaha.powerOff(); } }, 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: "Yamaha", - supportEvents: false, - supportBonjour: false, - manfDescription: "Manufacturer", - designedMaxLength: 255 - },{ - cType: types.MODEL_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.sysConfig.YAMAHA_AV.System[0].Config[0].Model_Name[0], - supportEvents: false, - supportBonjour: false, - manfDescription: "Model", - designedMaxLength: 255 - },{ - cType: types.SERIAL_NUMBER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.sysConfig.YAMAHA_AV.System[0].Config[0].System_ID[0], - 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.serviceName, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of service", - designedMaxLength: 255 - },{ - cType: types.POWER_STATE_CTYPE, - onUpdate: function(value) { that.setPlaying(value); }, - perms: ["pw","pr","ev"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Change the playback state of the Yamaha AV Receiver", - designedMaxLength: 1 - }] - }]; + var informationService = new Service.AccessoryInformation(); + var yamaha = this.yamaha; + + informationService + .setCharacteristic(Characteristic.Name, this.name) + .setCharacteristic(Characteristic.Manufacturer, "Yamaha") + .setCharacteristic(Characteristic.Model, this.sysConfig.YAMAHA_AV.System[0].Config[0].Model_Name[0]) + .setCharacteristic(Characteristic.SerialNumber, this.sysConfig.YAMAHA_AV.System[0].Config[0].System_ID[0]); + + var switchService = new Service.Switch("Power State"); + switchService.getCharacteristic(Characteristic.On) + .on('get', function(callback, context){ + yamaha.isOn().then(function(result){ + callback(false, result); + }.bind(this)); + }.bind(this)) + .on('set', function(powerOn, callback){ + this.setPlaying(powerOn).then(function(){ + callback(false, powerOn); + }, function(error){ + callback(error, !powerOn); //TODO: Actually determine and send real new status. + }); + }.bind(this)); + + var audioDeviceService = new YamahaAVRPlatform.AudioDeviceService("Audio Functions"); + audioDeviceService.getCharacteristic(YamahaAVRPlatform.AudioVolume) + .on('get', function(callback, context){ + yamaha.getBasicInfo().done(function(basicInfo){ + var v = basicInfo.getVolume()/10.0; + var p = 100 * ((v - that.minVolume) / that.gapVolume); + p = p < 0 ? 0 : p > 100 ? 100 : Math.round(p); + debug("Got volume percent of " + p + "%"); + callback(false, p); + }); + }) + .on('set', function(p, callback){ + var v = ((p / 100) * that.gapVolume) + that.minVolume; + v = Math.round(v*10.0); + debug("Setting volume to " + v); + yamaha.setVolumeTo(v).then(function(){ + callback(false, p); + }); + }) + .getValue(null, null); // force an asynchronous get + + + return [informationService, switchService, audioDeviceService]; + } }; From 7aa758cb042bac0de651107a4e64eb28a82313cb Mon Sep 17 00:00:00 2001 From: S'pht'Kr Date: Tue, 29 Sep 2015 06:53:22 +0200 Subject: [PATCH 12/69] Working towards getting the extra dimmers in one accessory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Having trouble with service subtypes…hmm… --- platforms/ZWayServer.js | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/platforms/ZWayServer.js b/platforms/ZWayServer.js index 7c8b925..4915f89 100644 --- a/platforms/ZWayServer.js +++ b/platforms/ZWayServer.js @@ -113,10 +113,16 @@ ZWayServerPlatform.prototype = { if(vdev.tags.indexOf("Homebridge:Skip") >= 0) { debug("Tag says skip!"); continue; } if(this.opt_in && vdev.tags.indexOf("Homebridge:Include") < 0) continue; var gdid = vdev.id.replace(/^(.*?)_zway_(\d+-\d+)-\d.*/, '$1_$2'); - var gd = groupedDevices[gdid] || (groupedDevices[gdid] = {devices: [], types: {}, primary: undefined}); + var gd = groupedDevices[gdid] || (groupedDevices[gdid] = {devices: [], types: {}, extras: {}, primary: undefined}); gd.devices.push(vdev); - gd.types[ZWayServerPlatform.getVDevTypeKey(vdev)] = gd.devices.length - 1; - gd.types[vdev.deviceType] = gd.devices.length - 1; // also include the deviceType only as a possibility + var tk = ZWayServerPlatform.getVDevTypeKey(vdev); + if(gd.types[tk] === undefined){ + gd.types[tk] = gd.devices.length - 1; + } else { + gd.extras[tk] = gd.extras[tk] || []; + gd.extras[tk].push(gd.devices.length - 1); + } + if(tk !== vdev.deviceType) gd.types[vdev.deviceType] = gd.devices.length - 1; // also include the deviceType only as a possibility } //TODO: Make a second pass, re-splitting any devices that don't make sense together for(var gdid in groupedDevices) { @@ -250,25 +256,25 @@ ZWayServerAccessory.prototype = { var services = [], service; switch (typeKey) { case "switchBinary": - services.push(new Service.Switch(vdev.metrics.title)); + services.push(new Service.Switch(vdev.metrics.title, vdev.id)); break; case "switchMultilevel": - services.push(new Service.Lightbulb(vdev.metrics.title)); + services.push(new Service.Lightbulb(vdev.metrics.title, vdev.id)); break; case "thermostat": - services.push(new Service.Thermostat(vdev.metrics.title)); + services.push(new Service.Thermostat(vdev.metrics.title, vdev.id)); break; case "sensorMultilevel.Temperature": - services.push(new Service.TemperatureSensor(vdev.metrics.title)); + services.push(new Service.TemperatureSensor(vdev.metrics.title, vdev.id)); break; case "sensorBinary.Door/Window": - services.push(new Service.GarageDoorOpener(vdev.metrics.title)); + services.push(new Service.GarageDoorOpener(vdev.metrics.title, vdev.id)); break; case "battery.Battery": - services.push(new Service.BatteryService(vdev.metrics.title)); + services.push(new Service.BatteryService(vdev.metrics.title, vdev.id)); break; case "sensorMultilevel.Luminiscence": - services.push(new Service.LightSensor(vdev.metrics.title)); + services.push(new Service.LightSensor(vdev.metrics.title, vdev.id)); break; } @@ -335,7 +341,7 @@ ZWayServerAccessory.prototype = { } , configureCharacteristic: function(cx, vdev){ - var that, accessory = this; + var accessory = this; // Add this combination to the maps... if(!this.platform.cxVDevMap[vdev.id]) this.platform.cxVDevMap[vdev.id] = []; @@ -349,7 +355,7 @@ ZWayServerAccessory.prototype = { cx.value = cx.zway_getValueFromVDev(vdev); cx.on('get', function(callback, context){ debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"..."); - callback(false, that.name); + callback(false, accessory.name); }); cx.writable = false; return cx; @@ -597,7 +603,7 @@ ZWayServerAccessory.prototype = { if(cx instanceof Characteristic.StatusLowBattery){ cx.zway_getValueFromVDev = function(vdev){ - return vdev.metrics.level <= that.platform.batteryLow ? Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW : Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + return vdev.metrics.level <= accessory.platform.batteryLow ? Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW : Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; }; cx.value = cx.zway_getValueFromVDev(vdev); cx.on('get', function(callback, context){ @@ -686,6 +692,12 @@ ZWayServerAccessory.prototype = { var services = [informationService]; services = services.concat(this.getVDevServices(this.devDesc.devices[this.devDesc.primary])); + + // Any extra switchMultilevels? Could be a RGBW+W bulb, add them as additional services... + if(this.devDesc.extras["switchMultilevel"]) for(var i = 0; i < this.devDesc.extras["switchMultilevel"].length; i++){ + var xvdev = this.devDesc.devices[this.devDesc.extras["switchMultilevel"][i]]; + services = services.concat(this.getVDevServices(xvdev)); + } if(this.platform.splitServices){ if(this.devDesc.types["battery.Battery"]){ From 0b3930e458054fbbd93144dc7c8420b4120a39bd Mon Sep 17 00:00:00 2001 From: S'pht'Kr Date: Tue, 29 Sep 2015 06:59:40 +0200 Subject: [PATCH 13/69] Erm...oops. --- platforms/ZWayServer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platforms/ZWayServer.js b/platforms/ZWayServer.js index 33c5ac7..fcab891 100644 --- a/platforms/ZWayServer.js +++ b/platforms/ZWayServer.js @@ -93,8 +93,8 @@ ZWayServerPlatform.prototype = { "thermostat", "switchMultilevel", "switchBinary", - "sensorBinary.Door/Window" - "sensorMultilevel.Temperature", + "sensorBinary.Door/Window", + "sensorMultilevel.Temperature" ]; var that = this; From 99da61d30a1295b17e24bd8f1f8566d01eb29226 Mon Sep 17 00:00:00 2001 From: S'pht'Kr Date: Tue, 29 Sep 2015 07:06:05 +0200 Subject: [PATCH 14/69] Lost some subtype parameters in deconfliction --- platforms/ZWayServer.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/platforms/ZWayServer.js b/platforms/ZWayServer.js index fcab891..c86d4ac 100644 --- a/platforms/ZWayServer.js +++ b/platforms/ZWayServer.js @@ -256,16 +256,16 @@ ZWayServerAccessory.prototype = { var services = [], service; switch (typeKey) { case "thermostat": - services.push(new Service.Thermostat(vdev.metrics.title)); + services.push(new Service.Thermostat(vdev.metrics.title, vdev.id)); break; case "switchBinary": - services.push(new Service.Switch(vdev.metrics.title)); + services.push(new Service.Switch(vdev.metrics.title, vdev.id)); break; case "switchMultilevel": - services.push(new Service.Lightbulb(vdev.metrics.title)); + services.push(new Service.Lightbulb(vdev.metrics.title, vdev.id)); break; case "sensorBinary.Door/Window": - services.push(new Service.GarageDoorOpener(vdev.metrics.title)); + services.push(new Service.GarageDoorOpener(vdev.metrics.title, vdev.id)); break; case "sensorMultilevel.Temperature": services.push(new Service.TemperatureSensor(vdev.metrics.title, vdev.id)); From ba5722c5174ec2fba0fa0b6670aaf6ebdaa2e936 Mon Sep 17 00:00:00 2001 From: Raoul Date: Wed, 30 Sep 2015 09:21:59 +0200 Subject: [PATCH 15/69] props update minValue, maxValue used in Temperature devices (Apple's default values too narrow for beer coolers I've heard) adapted call syntax for "Simplify Characteristic properties" https://github.com/KhaosT/HAP-NodeJS/commit/1d84d128d1513beedcafc24d2c07d98185563243#diff-cb84de3a1478a38b2cf8388d709f1c1cR108 --- accessories/knxdevice.js | 51 +++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/accessories/knxdevice.js b/accessories/knxdevice.js index 5801249..6efbbe3 100644 --- a/accessories/knxdevice.js +++ b/accessories/knxdevice.js @@ -195,26 +195,44 @@ KNXDevice.prototype = { }, // float knxregister_float: function(addresses, characteristic) { + // update for props refactor https://github.com/KhaosT/HAP-NodeJS/commit/1d84d128d1513beedcafc24d2c07d98185563243#diff-cb84de3a1478a38b2cf8388d709f1c1cR50 + + var validValue = true; + var hk_value = 0.0; this.log("knx registering FLOAT " + addresses); knxd_registerGA(addresses, function(val, src, dest, type){ this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName); - var hk_value = Math.round(val*10)/10; - if (hk_value>=characteristic.minimumValue && hk_value<=characteristic.maximumValue) { + // make hk_value compliant to properties + if (characteristic.props.minStep) { + // quantize + hk_value = Math.round(val/characteristic.props.minStep)*characteristic.props.minStep; + } else { + hk_value = val; + } + // range check + validValue = true; // assume validity at beginning + if (characteristic.props.minValue) { + validValue = validValue && (hk_value>=characteristic.props.minValue); + } + if (characteristic.props.maxValue) { + validValue = validValue && (hk_value<=characteristic.props.maxValue); + } + if (validValue) { characteristic.setValue(hk_value, undefined, 'fromKNXBus'); // 1 decimal for HomeKit } else { - this.log("Value %s out of bounds %s...%s ",hk_value, characteristic.minimumValue, characteristic.maximumValue); + this.log("Accessory "+ this.name +":" + characteristic.displayName+ ": Value %s out of bounds %s...%s ",hk_value, characteristic.props.minValue, characteristic.props.maxValue); } }.bind(this)); }, //integer knxregister_int: function(addresses, characteristic) { - this.log("knx registering FLOAT " + addresses); + this.log("knx registering INT " + addresses); knxd_registerGA(addresses, function(val, src, dest, type){ this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName); - if (val>=(characteristic.minimumValue || 0) && val<=(characteristic.maximumValue || 255)) { + if (val>=(characteristic.props.minValue || 0) && val<=(characteristic.props.maxValue || 255)) { characteristic.setValue(val, undefined, 'fromKNXBus'); } else { - this.log("Value %s out of bounds %s...%s ",hk_value, (characteristic.minimumValue || 0), (characteristic.maximumValue || 255)); + this.log("Accessory "+ this.name +":" + characteristic.displayName+ ": Value %s out of bounds %s...%s ",hk_value, (characteristic.props.minValue || 0), (characteristic.props.maxValue || 255)); } }.bind(this)); }, @@ -775,16 +793,25 @@ KNXDevice.prototype = { var myService = new Service.Thermostat(config.name,config.name); // CurrentTemperature) + // props update for https://github.com/KhaosT/HAP-NodeJS/commit/1d84d128d1513beedcafc24d2c07d98185563243#diff-cb84de3a1478a38b2cf8388d709f1c1cR108 if (config.CurrentTemperature) { this.log("Thermostat CurrentTemperature characteristic enabled"); + myService.getCharacteristic(Characteristic.CurrentTemperature).setProps({ + minValue: config.CurrentTemperature.minValue || -40, + maxValue: config.CurrentTemperature.maxValue || 60 + }); // °C by default this.bindCharacteristic(myService, Characteristic.CurrentTemperature, "Float", config.CurrentTemperature); } // TargetTemperature if available if (config.TargetTemperature) { this.log("Thermostat TargetTemperature characteristic enabled"); // default boundary too narrow for thermostats - myService.getCharacteristic(Characteristic.TargetTemperature).minimumValue=0; // °C - myService.getCharacteristic(Characteristic.TargetTemperature).maximumValue=40; // °C + // props update for https://github.com/KhaosT/HAP-NodeJS/commit/1d84d128d1513beedcafc24d2c07d98185563243#diff-cb84de3a1478a38b2cf8388d709f1c1cR108 + myService.getCharacteristic(Characteristic.TargetTemperature).setProps({ + minValue: config.TargetTemperature.minValue || 0, + maxValue: config.TargetTemperature.maxValue || 40 + }); + this.bindCharacteristic(myService, Characteristic.TargetTemperature, "Float", config.TargetTemperature); } // HVAC @@ -812,10 +839,16 @@ KNXDevice.prototype = { } var myService = new Service.TemperatureSensor(config.name,config.name); // CurrentTemperature) + // props update for https://github.com/KhaosT/HAP-NodeJS/commit/1d84d128d1513beedcafc24d2c07d98185563243#diff-cb84de3a1478a38b2cf8388d709f1c1cR108 if (config.CurrentTemperature) { - this.log("TemperatureSensor CurrentTemperature characteristic enabled"); + this.log("Thermostat CurrentTemperature characteristic enabled"); + myService.getCharacteristic(Characteristic.CurrentTemperature).setProps({ + minValue: config.CurrentTemperature.minValue || -40, + maxValue: config.CurrentTemperature.maxValue || 60 + }); // °C by default this.bindCharacteristic(myService, Characteristic.CurrentTemperature, "Float", config.CurrentTemperature); } + return myService; }, getWindowService: function(config) { From 54f0c2f0cb894b77e78032311e3bf54252646aa0 Mon Sep 17 00:00:00 2001 From: Snowdd1 Date: Wed, 30 Sep 2015 20:03:20 +0200 Subject: [PATCH 16/69] Doc updates --- platforms/KNX-sample-config.json | 306 ++++++++++++++++--------------- platforms/KNX.md | 2 +- 2 files changed, 155 insertions(+), 153 deletions(-) diff --git a/platforms/KNX-sample-config.json b/platforms/KNX-sample-config.json index 2c29736..86e070b 100644 --- a/platforms/KNX-sample-config.json +++ b/platforms/KNX-sample-config.json @@ -1,154 +1,156 @@ { - "bridge": { - "name": "Homebridge", - "username": "CC:22:3D:E3:CE:30", - "port": 51826, - "pin": "031-45-154" - }, - "description": "This is an example configuration file for KNX platform shim", - "hint": "Always paste into jsonlint.com validation page before starting your homebridge, saves a lot of frustration", - "hint2": "Replace all group addresses by current addresses of your installation, these are arbitrary examples!", - "hint3": "For valid services and their characteristics have a look at the knxdevice.md file in folder accessories!", - "platforms": [ - { - "platform": "KNX", - "name": "KNX", - "knxd_ip": "192.168.178.205", - "knxd_port": 6720, - "accessories": [ - { - "accessory_type": "knxdevice", - "description": "Only generic type knxdevice is supported, all previous knx types have been merged into that.", - "name": "Living Room North Lamp", - "services": [ - { - "type": "Lightbulb", - "description": "iOS8 Lightbulb type, supports On (Switch) and Brightness", - "name": "Living Room North Lamp", - "On": { - "Set": "1/1/6", - "Listen": [ - "1/1/63" - ] - }, - "Brightness": { - "Set": "1/1/62", - "Listen": [ - "1/1/64" - ] - } - } - ], - "services-description": "Services is an array, you CAN have multiple service types in one accessory, though it is not fully supported in many iOS HK apps, such as EVE and myTouchHome" - }, - { - "accessory_type": "knxdevice", - "name": "Office Temperature", - "description": "iOS8.4.1 TemperatureSensor type, supports CurrentTemperature", - "services": [ - { - "type": "TemperatureSensor", - "name": "Raumtemperatur", - "CurrentTemperature": { - "Listen": "3/3/44" - } - } - ] - }, - { - "accessory_type": "knxdevice", - "name": "Office Window Lock", - "services": [ - { - "type": "LockMechanism", - "description": "iOS8 Lock mechanism, Supports LockCurrentStateSecured0 OR LockCurrentState, LockTargetStateSecured0 OR LockTargetState, use depending if LOCKED is 0 or 1", - "name": "Office Window Lock", - "LockCurrentStateSecured0": { - "Listen": "5/3/15" - }, - "LockTargetStateSecured0": { - "Listen": "5/3/15" - } - } - ] - }, - { - "accessory_type": "knxdevice", - "description": "sample device with multiple services. Multiple services of different types are widely supported", - "name": "Office", - "services": [ - { - "type": "Lightbulb", - "name": "Office Lamp", - "On": { - "Set": "1/3/5" - } - }, - { - "type": "Thermostat", - "description": "iOS8 Thermostat type, supports CurrentTemperature, TargetTemperature, CurrentHeatingCoolingState ", - "name": "Raumtemperatur", - "CurrentTemperature": { - "Listen": "3/3/44" - }, - "TargetTemperature": { - "Set": "3/3/94" - }, - "CurrentHeatingCoolingState": { - "Listen": "3/3/64" - } - }, - { - "type": "WindowCovering", - "description": "iOS9 Window covering (blinds etc) type, still WIP", - "name": "Blinds", - "TargetPosition": { - "Set": "1/2/3", - "Listen": "1/2/4" - }, - "CurrentPosition": { - "Set": "1/3/1", - "Listen": "1/3/2" - }, - "PositionState": { - "Listen": "2/7/1" - } - } - ] - }, - { - "accessory_type": "knxdevice", - "description": "sample contact sensor device", - "name": "Office Contact", - "services": [ - { - "type": "ContactSensor", - "name": "Office Door", - "ContactSensorState": { - "Listen": "5/3/5" - } - } - ] - }, - { - "accessory_type": "knxdevice", - "description": "sample garage door opener", - "name": "Office Garage", - "services": [ - { - "type": "GarageDoorOpener", - "name": "Office Garage Opener", - "CurrentDoorState": { - "Listen": "5/4/5" - }, - "TargetDoorState": { - "Listen": "5/4/6" - } - } - ] - } - ] - } - ], - "accessories": [] + "bridge": { + "name": "Homebridge", + "username": "CC:22:3D:E3:CE:30", + "port": 51826, + "pin": "031-45-154" + }, + "description": "This is an example configuration file for KNX platform shim", + "hint": "Always paste into jsonlint.com validation page before starting your homebridge, saves a lot of frustration", + "hint2": "Replace all group addresses by current addresses of your installation, these are arbitrary examples!", + "hint3": "For valid services and their characteristics have a look at the knxdevice.md file in folder accessories!", + "platforms": [ + { + "platform": "KNX", + "name": "KNX", + "knxd_ip": "192.168.178.205", + "knxd_port": 6720, + "accessories": [ + { + "accessory_type": "knxdevice", + "description": "Only generic type knxdevice is supported, all previous knx types have been merged into that.", + "name": "Living Room North Lamp", + "services": [ + { + "type": "Lightbulb", + "description": "iOS8 Lightbulb type, supports On (Switch) and Brightness", + "name": "Living Room North Lamp", + "On": { + "Set": "1/1/6", + "Listen": [ + "1/1/63" + ] + }, + "Brightness": { + "Set": "1/1/62", + "Listen": [ + "1/1/64" + ] + } + } + ], + "services-description": "Services is an array, you CAN have multiple service types in one accessory, though it is not fully supported in many iOS HK apps, such as EVE and myTouchHome" + }, + { + "accessory_type": "knxdevice", + "name": "Office Temperature", + "description": "iOS8.4.1 TemperatureSensor type, supports CurrentTemperature", + "services": [ + { + "type": "TemperatureSensor", + "name": "Raumtemperatur", + "CurrentTemperature": { + "Listen": "3/3/44" + } + } + ] + }, + { + "accessory_type": "knxdevice", + "name": "Office Window Lock", + "services": [ + { + "type": "LockMechanism", + "description": "iOS8 Lock mechanism, Supports LockCurrentStateSecured0 OR LockCurrentState, LockTargetStateSecured0 OR LockTargetState, use depending if LOCKED is 0 or 1", + "name": "Office Window Lock", + "LockCurrentStateSecured0": { + "Listen": "5/3/15" + }, + "LockTargetStateSecured0": { + "Listen": "5/3/15" + } + } + ] + }, + { + "accessory_type": "knxdevice", + "description": "sample device with multiple services. Multiple services of different types are widely supported", + "name": "Office", + "services": [ + { + "type": "Lightbulb", + "name": "Office Lamp", + "On": { + "Set": "1/3/5" + } + }, + { + "type": "Thermostat", + "description": "iOS8 Thermostat type, supports CurrentTemperature, TargetTemperature, CurrentHeatingCoolingState ", + "name": "Raumtemperatur", + "CurrentTemperature": { + "Listen": "3/3/44" + }, + "TargetTemperature": { + "Set": "3/3/94" + }, + "CurrentHeatingCoolingState": { + "Listen": "3/3/64" + } + }, + { + "type": "WindowCovering", + "description": "iOS9 Window covering (blinds etc) type, still WIP", + "name": "Blinds", + "TargetPosition": { + "Set": "1/2/3", + "Listen": "1/2/4" + }, + "CurrentPosition": { + "Set": "1/3/1", + "Listen": "1/3/2" + }, + "PositionState": { + "Listen": "2/7/1" + } + } + ] + }, + { + "accessory_type": "knxdevice", + "description": "sample contact sensor device", + "name": "Office Contact", + "services": [ + { + "type": "ContactSensor", + "name": "Office Door", + "ContactSensorState": { + "Listen": "5/3/5" + } + } + ] + }, + { + "accessory_type": "knxdevice", + "description": "sample garage door opener", + "name": "Office Garage", + "services": [ + { + "type": "GarageDoorOpener", + "name": "Office Garage Opener", + "CurrentDoorState": { + "Listen": "5/4/5" + }, + "TargetDoorState": { + "Listen": "5/4/6" + } + } + ] + } + ] + } + ], + "accessories": [ + + ] } \ No newline at end of file diff --git a/platforms/KNX.md b/platforms/KNX.md index c3b4fe3..bab706d 100644 --- a/platforms/KNX.md +++ b/platforms/KNX.md @@ -152,7 +152,7 @@ Two kinds of addresses are supported: `"Set":"1/2/3"` is a writable group addres ## WindowCovering - CurrentPosition: DPT5 percentage - TargetPosition: DPT5 percentage -- PositionState: DPT5 value [listen only] +- PositionState: DPT5 value [listen only: 0 Closing, 1 Opening, 2 STopped] ### not yet supported - HoldPosition From 98ee9f8f95d7f2debada459b96b4cdba4a8b39ff Mon Sep 17 00:00:00 2001 From: Snowdd1 Date: Wed, 30 Sep 2015 22:07:33 +0200 Subject: [PATCH 17/69] Return value guarding; Cleaner logs --- accessories/knxdevice.js | 154 +++++++++++++++++++-------------------- platforms/KNX.md | 33 ++++++++- 2 files changed, 108 insertions(+), 79 deletions(-) diff --git a/accessories/knxdevice.js b/accessories/knxdevice.js index 6efbbe3..b2967c2 100644 --- a/accessories/knxdevice.js +++ b/accessories/knxdevice.js @@ -159,7 +159,7 @@ KNXDevice.prototype = { knxregister_bool: function(addresses, characteristic) { this.log("knx registering BOOLEAN " + addresses); knxd_registerGA(addresses, function(val, src, dest, type){ - this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type + " for " + characteristic.displayName); + this.log("[" +this.name + "]: Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type + " for " + characteristic.displayName); // iterate(characteristic); characteristic.setValue(val ? 1 : 0, undefined, 'fromKNXBus'); }.bind(this)); @@ -167,7 +167,7 @@ KNXDevice.prototype = { knxregister_boolReverse: function(addresses, characteristic) { this.log("knx registering BOOLEAN " + addresses); knxd_registerGA(addresses, function(val, src, dest, type){ - this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type + " for " + characteristic.displayName); + this.log("[" +this.name + "]: Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type + " for " + characteristic.displayName); // iterate(characteristic); characteristic.setValue(val ? 0 : 1, undefined, 'fromKNXBus'); }.bind(this)); @@ -176,19 +176,19 @@ KNXDevice.prototype = { knxregister_percent: function(addresses, characteristic) { this.log("knx registering PERCENT " + addresses); knxd_registerGA(addresses, function(val, src, dest, type){ - this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName); + this.log("[" +this.name + "]: Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName); if (type !== "DPT5") { this.log("[ERROR] Received value cannot be a percentage value"); } else { - if (!characteristic.timeout) { - if (characteristic.timeout < Date.now()) { +// if (!characteristic.timeout) { +// if (characteristic.timeout < Date.now()) { characteristic.setValue(Math.round(val/255*100), undefined, 'fromKNXBus'); - } else { - this.log("Blackout time"); - } - } else { - characteristic.setValue(Math.round(val/255*100), undefined, 'fromKNXBus'); - } // todo get the boolean logic right into one OR expresssion +// } else { +// this.log("Blackout time"); +// } +// } else { +// characteristic.setValue(Math.round(val/255*100), undefined, 'fromKNXBus'); +// } // todo get the boolean logic right into one OR expresssion } }.bind(this)); @@ -199,13 +199,13 @@ KNXDevice.prototype = { var validValue = true; var hk_value = 0.0; - this.log("knx registering FLOAT " + addresses); + this.log("["+ this.name +"]:[" + characteristic.displayName+ "]:knx registering FLOAT " + addresses); knxd_registerGA(addresses, function(val, src, dest, type){ - this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName); + this.log("["+ this.name +"]:[" + characteristic.displayName+ "]: Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName); // make hk_value compliant to properties if (characteristic.props.minStep) { // quantize - hk_value = Math.round(val/characteristic.props.minStep)*characteristic.props.minStep; + hk_value = Math.round(val/characteristic.props.minStep)/(1/characteristic.props.minStep); } else { hk_value = val; } @@ -220,26 +220,26 @@ KNXDevice.prototype = { if (validValue) { characteristic.setValue(hk_value, undefined, 'fromKNXBus'); // 1 decimal for HomeKit } else { - this.log("Accessory "+ this.name +":" + characteristic.displayName+ ": Value %s out of bounds %s...%s ",hk_value, characteristic.props.minValue, characteristic.props.maxValue); + this.log("["+ this.name +"]:[" + characteristic.displayName+ "]: Value %s out of bounds %s...%s ",hk_value, characteristic.props.minValue, characteristic.props.maxValue); } }.bind(this)); }, //integer knxregister_int: function(addresses, characteristic) { - this.log("knx registering INT " + addresses); + this.log("["+ this.name +"]:[" + characteristic.displayName+ "]:knx registering INT " + addresses); knxd_registerGA(addresses, function(val, src, dest, type){ - this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName); + this.log("["+ this.name +"]:[" + characteristic.displayName+ "]: Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName); if (val>=(characteristic.props.minValue || 0) && val<=(characteristic.props.maxValue || 255)) { characteristic.setValue(val, undefined, 'fromKNXBus'); } else { - this.log("Accessory "+ this.name +":" + characteristic.displayName+ ": Value %s out of bounds %s...%s ",hk_value, (characteristic.props.minValue || 0), (characteristic.props.maxValue || 255)); + this.log("["+ this.name +"]:[" + characteristic.displayName+ "]: Value %s out of bounds %s...%s ",hk_value, (characteristic.props.minValue || 0), (characteristic.props.maxValue || 255)); } }.bind(this)); }, knxregister_HVAC: function(addresses, characteristic) { - this.log("knx registering HVAC " + addresses); + this.log("["+ this.name +"]:[" + characteristic.displayName+ "]:knx registering HVAC " + addresses); knxd_registerGA(addresses, function(val, src, dest, type){ - this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName); + this.log("["+ this.name +"]:[" + characteristic.displayName+ "]:Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName); var HAPvalue = 0; switch (val){ case 0: @@ -279,9 +279,9 @@ KNXDevice.prototype = { */ // undefined, has to match! knxregister: function(addresses, characteristic) { - this.log("knx registering " + addresses); + this.log("["+ this.name +"]:[" + characteristic.displayName+ "]:knx registering " + addresses); knxd_registerGA(addresses, function(val, src, dest, type){ - this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName); + this.log("["+ this.name +"]:[" + characteristic.displayName+ "]:Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName); characteristic.setValue(val, undefined, 'fromKNXBus'); }.bind(this)); }, @@ -296,7 +296,7 @@ KNXDevice.prototype = { */ setBooleanState: function(value, callback, context, gaddress) { if (context === 'fromKNXBus') { - this.log(gaddress + " event ping pong, exit!"); +// this.log(gaddress + " event ping pong, exit!"); if (callback) { callback(); } @@ -305,14 +305,14 @@ KNXDevice.prototype = { if (value) { numericValue = 1; // need 0 or 1, not true or something } - this.log("Setting "+gaddress+" Boolean to %s", numericValue); + this.log("["+ this.name +"]:Setting "+gaddress+" Boolean to %s", numericValue); this.knxwrite(callback, gaddress,'DPT1',numericValue); } }, setBooleanReverseState: function(value, callback, context, gaddress) { if (context === 'fromKNXBus') { - this.log(gaddress + " event ping pong, exit!"); +// this.log(gaddress + " event ping pong, exit!"); if (callback) { callback(); } @@ -321,14 +321,14 @@ KNXDevice.prototype = { if (!value) { numericValue = 1; // need 0 or 1, not true or something } - this.log("Setting "+gaddress+" Boolean to %s", numericValue); + this.log("["+ this.name +"]:Setting "+gaddress+" Boolean to %s", numericValue); this.knxwrite(callback, gaddress,'DPT1',numericValue); } }, setPercentage: function(value, callback, context, gaddress) { if (context === 'fromKNXBus') { - this.log("event ping pong, exit!"); +// this.log(gaddress + "event ping pong, exit!"); if (callback) { callback(); } @@ -337,13 +337,13 @@ KNXDevice.prototype = { if (value) { numericValue = Math.round(255*value/100); // convert 1..100 to 1..255 for KNX bus } - this.log("Setting "+gaddress+" percentage to %s (%s)", value, numericValue); + this.log("["+ this.name +"]:Setting "+gaddress+" percentage to %s (%s)", value, numericValue); this.knxwrite(callback, gaddress,'DPT5',numericValue); } }, setInt: function(value, callback, context, gaddress) { if (context === 'fromKNXBus') { - this.log("event ping pong, exit!"); +// this.log(gaddress + "event ping pong, exit!"); if (callback) { callback(); } @@ -352,13 +352,13 @@ KNXDevice.prototype = { if (value && value>=0 && value<=255) { numericValue = value; // assure 1..255 for KNX bus } - this.log("Setting "+gaddress+" int to %s (%s)", value, numericValue); + this.log("["+ this.name +"]:Setting "+gaddress+" int to %s (%s)", value, numericValue); this.knxwrite(callback, gaddress,'DPT5',numericValue); } }, setFloat: function(value, callback, context, gaddress) { if (context === 'fromKNXBus') { - this.log(gaddress + " event ping pong, exit!"); +// this.log(gaddress + " event ping pong, exit!"); if (callback) { callback(); } @@ -367,13 +367,13 @@ KNXDevice.prototype = { if (value) { numericValue = value; // homekit expects precision of 1 decimal } - this.log("Setting "+gaddress+" Float to %s", numericValue); + this.log("["+ this.name +"]:Setting "+gaddress+" Float to %s", numericValue); this.knxwrite(callback, gaddress,'DPT9',numericValue); } }, setHVACState: function(value, callback, context, gaddress) { if (context === 'fromKNXBus') { - this.log(gaddress + " event ping pong, exit!"); +// this.log(gaddress + " event ping pong, exit!"); if (callback) { callback(); } @@ -396,7 +396,7 @@ KNXDevice.prototype = { KNXvalue = 1; } - this.log("Setting "+gaddress+" HVAC to %s", KNXvalue); + this.log("["+ this.name +"]:Setting "+gaddress+" HVAC to %s", KNXvalue); this.knxwrite(callback, gaddress,'DPT5',KNXvalue); } @@ -405,7 +405,7 @@ KNXDevice.prototype = { * */ identify: function(callback) { - this.log("Identify requested!"); + this.log("["+ this.name +"]:Identify requested!"); callback(); // success }, /** bindCharacteristic @@ -479,10 +479,10 @@ KNXDevice.prototype = { this.knxregister_HVAC([config.Set].concat(config.Listen || []), myCharacteristic); break; default: - this.log("[ERROR] unknown type passed"); + this.log("[ERROR] unknown type passed: ["+valueType+"]"); throw new Error("[ERROR] unknown type passed"); } - this.log("Issuing read requests on the KNX bus..."); + this.log("["+ this.name +"]:["+myCharacteristic.displayName+"]: Issuing read requests on the KNX bus..."); this.knxreadarray([config.Set].concat(config.Listen || [])); } return myCharacteristic; // for chaining or whatsoever @@ -512,30 +512,30 @@ KNXDevice.prototype = { var myService = new Service.ContactSensor(config.name,config.name); if (config.ContactSensorState) { - this.log("ContactSensor ContactSensorState characteristic enabled"); + this.log("["+ this.name +"]:ContactSensor ContactSensorState characteristic enabled"); this.bindCharacteristic(myService, Characteristic.ContactSensorState, "Bool", config.ContactSensorState); } else if (config.ContactSensorStateContact1) { - this.log("ContactSensor ContactSensorStateContact1 characteristic enabled"); + this.log("["+ this.name +"]:ContactSensor ContactSensorStateContact1 characteristic enabled"); this.bindCharacteristic(myService, Characteristic.ContactSensorState, "BoolReverse", config.ContactSensorStateContact1); } //optionals if (config.StatusActive) { - this.log("ContactSensor StatusActive characteristic enabled"); + this.log("["+ this.name +"]:ContactSensor StatusActive characteristic enabled"); myService.addCharacteristic(Characteristic.StatusActive); this.bindCharacteristic(myService, Characteristic.StatusActive, "Bool", config.StatusActive); } if (config.StatusFault) { - this.log("ContactSensor StatusFault characteristic enabled"); + this.log("["+ this.name +"]:ContactSensor StatusFault characteristic enabled"); myService.addCharacteristic(Characteristic.StatusFault); this.bindCharacteristic(myService, Characteristic.StatusFault, "Bool", config.StatusFault); } if (config.StatusTampered) { - this.log("ContactSensor StatusTampered characteristic enabled"); + this.log("["+ this.name +"]:ContactSensor StatusTampered characteristic enabled"); myService.addCharacteristic(Characteristic.StatusTampered); this.bindCharacteristic(myService, Characteristic.StatusTampered, "Bool", config.StatusTampered); } if (config.StatusLowBattery) { - this.log("ContactSensor StatusLowBattery characteristic enabled"); + this.log("["+ this.name +"]:ContactSensor StatusLowBattery characteristic enabled"); myService.addCharacteristic(Characteristic.StatusLowBattery); this.bindCharacteristic(myService, Characteristic.StatusLowBattery, "Bool", config.StatusLowBattery); } @@ -573,27 +573,27 @@ KNXDevice.prototype = { var myService = new Service.GarageDoorOpener(config.name,config.name); if (config.CurrentDoorState) { - this.log("GarageDoorOpener CurrentDoorState characteristic enabled"); + this.log("["+ this.name +"]:GarageDoorOpener CurrentDoorState characteristic enabled"); this.bindCharacteristic(myService, Characteristic.CurrentDoorState, "Int", config.CurrentDoorState); } if (config.TargetDoorState) { - this.log("GarageDoorOpener TargetDoorState characteristic enabled"); + this.log("["+ this.name +"]:GarageDoorOpener TargetDoorState characteristic enabled"); //myService.getCharacteristic(Characteristic.TargetDoorState).minimumValue=0; // //myService.getCharacteristic(Characteristic.TargetDoorState).maximumValue=4; // this.bindCharacteristic(myService, Characteristic.TargetDoorState, "Int", config.TargetDoorState); } if (config.ObstructionDetected) { - this.log("GarageDoorOpener ObstructionDetected characteristic enabled"); + this.log("["+ this.name +"]:GarageDoorOpener ObstructionDetected characteristic enabled"); this.bindCharacteristic(myService, Characteristic.ObstructionDetected, "Bool", config.ObstructionDetected); } //optionals if (config.LockCurrentState) { - this.log("GarageDoorOpener LockCurrentState characteristic enabled"); + this.log("["+ this.name +"]:GarageDoorOpener LockCurrentState characteristic enabled"); myService.addCharacteristic(Characteristic.LockCurrentState); this.bindCharacteristic(myService, Characteristic.LockCurrentState, "Int", config.LockCurrentState); } if (config.LockTargetState) { - this.log("GarageDoorOpener LockTargetState characteristic enabled"); + this.log("["+ this.name +"]:GarageDoorOpener LockTargetState characteristic enabled"); myService.addCharacteristic(Characteristic.LockTargetState); this.bindCharacteristic(myService, Characteristic.LockTargetState, "Bool", config.LockTargetState); } @@ -614,12 +614,12 @@ KNXDevice.prototype = { var myService = new Service.Lightbulb(config.name,config.name); // On (and Off) if (config.On) { - this.log("Lightbulb on/off characteristic enabled"); + this.log("["+ this.name +"]:Lightbulb on/off characteristic enabled"); this.bindCharacteristic(myService, Characteristic.On, "Bool", config.On); } // On characteristic // Brightness if available if (config.Brightness) { - this.log("Lightbulb Brightness characteristic enabled"); + this.log("["+ this.name +"]:Lightbulb Brightness characteristic enabled"); myService.addCharacteristic(Characteristic.Brightness); // it's an optional this.bindCharacteristic(myService, Characteristic.Brightness, "Percent", config.Brightness); } @@ -641,7 +641,7 @@ KNXDevice.prototype = { var myService = new Service.LightSensor(config.name,config.name); // CurrentTemperature) if (config.CurrentAmbientLightLevel) { - this.log("LightSensor CurrentAmbientLightLevel characteristic enabled"); + this.log("["+ this.name +"]:LightSensor CurrentAmbientLightLevel characteristic enabled"); this.bindCharacteristic(myService, Characteristic.CurrentAmbientLightLevel, "Float", config.CurrentAmbientLightLevel); } return myService; @@ -666,19 +666,19 @@ KNXDevice.prototype = { // LockCurrentState if (config.LockCurrentState) { // for normal contacts: Secured = 1 - this.log("LockMechanism LockCurrentState characteristic enabled"); + this.log("["+ this.name +"]:LockMechanism LockCurrentState characteristic enabled"); this.bindCharacteristic(myService, Characteristic.LockCurrentState, "Bool", config.LockCurrentState); } else if (config.LockCurrentStateSecured0) { // for reverse contacts Secured = 0 - this.log("LockMechanism LockCurrentState characteristic enabled"); + this.log("["+ this.name +"]:LockMechanism LockCurrentState characteristic enabled"); this.bindCharacteristic(myService, Characteristic.LockCurrentState, "BoolReverse", config.LockCurrentStateSecured0); } // LockTargetState if (config.LockTargetState) { - this.log("LockMechanism LockTargetState characteristic enabled"); + this.log("["+ this.name +"]:LockMechanism LockTargetState characteristic enabled"); this.bindCharacteristic(myService, Characteristic.LockTargetState, "Bool", config.LockTargetState); } else if (config.LockTargetStateSecured0) { - this.log("LockMechanism LockTargetState characteristic enabled"); + this.log("["+ this.name +"]:LockMechanism LockTargetState characteristic enabled"); this.bindCharacteristic(myService, Characteristic.LockTargetState, "BoolReverse", config.LockTargetStateSecured0); } @@ -701,27 +701,27 @@ KNXDevice.prototype = { var myService = new Service.MotionSensor(config.name,config.name); if (config.MotionDetected) { - this.log("MotionSensor MotionDetected characteristic enabled"); + this.log("["+ this.name +"]:MotionSensor MotionDetected characteristic enabled"); this.bindCharacteristic(myService, Characteristic.MotionDetected, "Bool", config.MotionDetected); } //optionals if (config.StatusActive) { - this.log("MotionSensor StatusActive characteristic enabled"); + this.log("["+ this.name +"]:MotionSensor StatusActive characteristic enabled"); myService.addCharacteristic(Characteristic.StatusActive); this.bindCharacteristic(myService, Characteristic.StatusActive, "Bool", config.StatusActive); } if (config.StatusFault) { - this.log("MotionSensor StatusFault characteristic enabled"); + this.log("["+ this.name +"]:MotionSensor StatusFault characteristic enabled"); myService.addCharacteristic(Characteristic.StatusFault); this.bindCharacteristic(myService, Characteristic.StatusFault, "Bool", config.StatusFault); } if (config.StatusTampered) { - this.log("MotionSensor StatusTampered characteristic enabled"); + this.log("["+ this.name +"]:MotionSensor StatusTampered characteristic enabled"); myService.addCharacteristic(Characteristic.StatusTampered); this.bindCharacteristic(myService, Characteristic.StatusTampered, "Bool", config.StatusTampered); } if (config.StatusLowBattery) { - this.log("MotionSensor StatusLowBattery characteristic enabled"); + this.log("["+ this.name +"]:MotionSensor StatusLowBattery characteristic enabled"); myService.addCharacteristic(Characteristic.StatusLowBattery); this.bindCharacteristic(myService, Characteristic.StatusLowBattery, "Bool", config.StatusLowBattery); } @@ -744,11 +744,11 @@ KNXDevice.prototype = { var myService = new Service.Outlet(config.name,config.name); // On (and Off) if (config.On) { - this.log("Outlet on/off characteristic enabled"); + this.log("["+ this.name +"]:Outlet on/off characteristic enabled"); this.bindCharacteristic(myService, Characteristic.On, "Bool", config.On); } // OutletInUse characteristic if (config.OutletInUse) { - this.log("Outlet on/off characteristic enabled"); + this.log("["+ this.name +"]:Outlet on/off characteristic enabled"); this.bindCharacteristic(myService, Characteristic.OutletInUse, "Bool", config.OutletInUse); } return myService; @@ -766,7 +766,7 @@ KNXDevice.prototype = { var myService = new Service.Switch(config.name,config.name); // On (and Off) if (config.On) { - this.log("Switch on/off characteristic enabled"); + this.log("["+ this.name +"]:Switch on/off characteristic enabled"); this.bindCharacteristic(myService, Characteristic.On, "Bool", config.On); } // On characteristic @@ -795,7 +795,7 @@ KNXDevice.prototype = { // CurrentTemperature) // props update for https://github.com/KhaosT/HAP-NodeJS/commit/1d84d128d1513beedcafc24d2c07d98185563243#diff-cb84de3a1478a38b2cf8388d709f1c1cR108 if (config.CurrentTemperature) { - this.log("Thermostat CurrentTemperature characteristic enabled"); + this.log("["+ this.name +"]:Thermostat CurrentTemperature characteristic enabled"); myService.getCharacteristic(Characteristic.CurrentTemperature).setProps({ minValue: config.CurrentTemperature.minValue || -40, maxValue: config.CurrentTemperature.maxValue || 60 @@ -804,7 +804,7 @@ KNXDevice.prototype = { } // TargetTemperature if available if (config.TargetTemperature) { - this.log("Thermostat TargetTemperature characteristic enabled"); + this.log("["+ this.name +"]:Thermostat TargetTemperature characteristic enabled"); // default boundary too narrow for thermostats // props update for https://github.com/KhaosT/HAP-NodeJS/commit/1d84d128d1513beedcafc24d2c07d98185563243#diff-cb84de3a1478a38b2cf8388d709f1c1cR108 myService.getCharacteristic(Characteristic.TargetTemperature).setProps({ @@ -816,12 +816,12 @@ KNXDevice.prototype = { } // HVAC if (config.CurrentHeatingCoolingState) { - this.log("Thermostat CurrentHeatingCoolingState characteristic enabled"); + this.log("["+ this.name +"]:Thermostat CurrentHeatingCoolingState characteristic enabled"); this.bindCharacteristic(myService, Characteristic.CurrentHeatingCoolingState, "HVAC", config.CurrentHeatingCoolingState); } // HVAC if (config.TargetHeatingCoolingState) { - this.log("Thermostat TargetHeatingCoolingState characteristic enabled"); + this.log("["+ this.name +"]:Thermostat TargetHeatingCoolingState characteristic enabled"); this.bindCharacteristic(myService, Characteristic.TargetHeatingCoolingState, "HVAC", config.TargetHeatingCoolingState); } return myService; @@ -841,7 +841,7 @@ KNXDevice.prototype = { // CurrentTemperature) // props update for https://github.com/KhaosT/HAP-NodeJS/commit/1d84d128d1513beedcafc24d2c07d98185563243#diff-cb84de3a1478a38b2cf8388d709f1c1cR108 if (config.CurrentTemperature) { - this.log("Thermostat CurrentTemperature characteristic enabled"); + this.log("["+ this.name +"]:TemperatureSensor CurrentTemperature characteristic enabled"); myService.getCharacteristic(Characteristic.CurrentTemperature).setProps({ minValue: config.CurrentTemperature.minValue || -40, maxValue: config.CurrentTemperature.maxValue || 60 @@ -878,15 +878,15 @@ KNXDevice.prototype = { var myService = new Service.Window(config.name,config.name); if (config.CurrentPosition) { - this.log("Window CurrentPosition characteristic enabled"); + this.log("["+ this.name +"]:Window CurrentPosition characteristic enabled"); this.bindCharacteristic(myService, Characteristic.CurrentPosition, "Percent", config.CurrentPosition); } if (config.TargetPosition) { - this.log("Window TargetPosition characteristic enabled"); + this.log("["+ this.name +"]:Window TargetPosition characteristic enabled"); this.bindCharacteristic(myService, Characteristic.TargetPosition, "Percent", config.TargetPosition); } if (config.PositionState) { - this.log("Window PositionState characteristic enabled"); + this.log("["+ this.name +"]:Window PositionState characteristic enabled"); this.bindCharacteristic(myService, Characteristic.PositionState, "Float", config.PositionState); } return myService; @@ -913,15 +913,15 @@ KNXDevice.prototype = { var myService = new Service.WindowCovering(config.name,config.name); if (config.CurrentPosition) { - this.log("WindowCovering CurrentPosition characteristic enabled"); + this.log("["+ this.name +"]:WindowCovering CurrentPosition characteristic enabled"); this.bindCharacteristic(myService, Characteristic.CurrentPosition, "Percent", config.CurrentPosition); } if (config.TargetPosition) { - this.log("WindowCovering TargetPosition characteristic enabled"); + this.log("["+ this.name +"]:WindowCovering TargetPosition characteristic enabled"); this.bindCharacteristic(myService, Characteristic.TargetPosition, "Percent", config.TargetPosition); } if (config.PositionState) { - this.log("WindowCovering PositionState characteristic enabled"); + this.log("["+ this.name +"]:WindowCovering PositionState characteristic enabled"); this.bindCharacteristic(myService, Characteristic.PositionState, "Float", config.PositionState); } return myService; @@ -943,7 +943,7 @@ KNXDevice.prototype = { informationService .setCharacteristic(Characteristic.Manufacturer, "Opensource Community") .setCharacteristic(Characteristic.Model, "KNX Universal Device") - .setCharacteristic(Characteristic.SerialNumber, "Version 1.1.2"); + .setCharacteristic(Characteristic.SerialNumber, "Version 1.1.4"); accessoryServices.push(informationService); @@ -998,8 +998,8 @@ KNXDevice.prototype = { accessoryServices.push(this.getWindowCoveringService(configService)); break; default: - this.log("[ERROR] unknown 'type' property of '"+configService.type+"' for service "+ configService.name + " in config.json. KNX platform section fault "); - //throw new Error("[ERROR] unknown 'type' property for service "+ configService.name + " in config.json. KNX platform section fault "); + this.log("[ERROR] unknown 'type' property of ["+configService.type+"] for service ["+ configService.name + "] in config.json. KNX platform section fault "); + throw new Error("[ERROR] unknown 'type' property of ["+configService.type+"] for service '"+ configService.name + "' in config.json. KNX platform section fault "); } } // start listening for events on the bus (if not started yet - will prevent itself) diff --git a/platforms/KNX.md b/platforms/KNX.md index bab706d..0b4d4e9 100644 --- a/platforms/KNX.md +++ b/platforms/KNX.md @@ -47,7 +47,7 @@ You have to add services in the following syntax: { "type": "SERVICENAME", "description": "This is just for you to remember things", - "name": "We need a name for each service, though it usually shows only if multiple services are present in one accessory", + "name": "beer tap thermostat", "CHARACTERISTIC1": { "Set": "1/1/6", "Listen": [ @@ -68,6 +68,35 @@ Two kinds of addresses are supported: `"Set":"1/2/3"` is a writable group addres `"Listen":["1/2/3","1/2/4","1/2/5"]` is an array of addresses that are listened to additionally. To these addresses never values get written, but the on startup the service will issue *KNX read requests* to ALL addresses listed in `Set:` and in `Listen:` +For two characteristics there are additional minValue and maxValue attributes. These are CurrentTemperature and TargetTemperature, and are used in TemperatureSensor and Thermostat. + +So the charcteristic section may look like: + + ````json + { + "type": "Thermostat", + "description": "Sample thermostat", + "name": "We need a name for each service, though it usually shows only if multiple services are present in one accessory", + "CurrentTemperature": { + "Set": "1/1/6", + "Listen": [ + "1/1/63" + ], + minValue: -18, + maxValue: 30 + }, + "TargetTemperature": { + "Set": "1/1/62", + "Listen": [ + "1/1/64" + ], + minValue: -4, + maxValue: 12 + } + } +```` + + # Supported Services and their characteristics ## ContactSensor @@ -139,7 +168,7 @@ Two kinds of addresses are supported: `"Set":"1/2/3"` is a writable group addres - CurrentTemperature: DPT9.001 in °C [listen only] ## Thermostat -- CurrentTemperature: DPT9.001 in °C [listen only] +- CurrentTemperature: DPT9.001 in °C [listen only], -40 to 80°C if not overriden as shown above - TargetTemperature: DPT9.001, values 0..40°C only, all others are ignored - CurrentHeatingCoolingState: DPT20.102 HVAC, because of the incompatible mapping only off and heating (=auto) are shown, [listen only] - TargetHeatingCoolingState: DPT20.102 HVAC, as above From 1fce5c875439a9a61a9f1ff4f7c483aedba440e0 Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Thu, 1 Oct 2015 01:29:42 -0400 Subject: [PATCH 18/69] ignore hiddent Home Assistant devices --- platforms/HomeAssistant.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/platforms/HomeAssistant.js b/platforms/HomeAssistant.js index 5a23ab5..61d3bc2 100644 --- a/platforms/HomeAssistant.js +++ b/platforms/HomeAssistant.js @@ -152,15 +152,24 @@ HomeAssistantPlatform.prototype = { entity = data[i] entity_type = entity.entity_id.split('.')[0] + // ignore devices that are not in the list of supported types if (that.supportedTypes.indexOf(entity_type) == -1) { continue; } + // ignore hidden devices + if (entity.attributes && entity.attributes.hidden) { + continue; + } + var accessory = null if (entity_type == 'light') { accessory = new HomeAssistantLight(that.log, entity, that) }else if (entity_type == 'switch'){ + console.log(JSON.stringify(entity)) + console.log(""); + console.log(""); accessory = new HomeAssistantSwitch(that.log, entity, that) }else if (entity_type == 'scene'){ accessory = new HomeAssistantSwitch(that.log, entity, that, 'scene') From 45f40874efd315c0aa7ecb145c521f3a6ac5ef92 Mon Sep 17 00:00:00 2001 From: Mikulas Date: Thu, 1 Oct 2015 23:22:17 +0200 Subject: [PATCH 19/69] add lifx to sample config --- config-sample.json | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/config-sample.json b/config-sample.json index 6f24554..7af74db 100644 --- a/config-sample.json +++ b/config-sample.json @@ -89,14 +89,19 @@ "delay": 30, "repeat": 3, "zones":["Kitchen Lamp","Bedroom Lamp","Living Room Lamp","Hallway Lamp"] - }, - { + }, + { "platform": "HomeAssistant", "name": "HomeAssistant", "host": "http://192.168.1.10:8123", "password": "XXXXX", "supported_types": ["light", "switch", "media_player", "scene"] - } + }, + { + "platform": "LIFx", + "name": "LIFx", + "access_token": "XXXXXXXX generate at https://cloud.lifx.com/settings" + } ], "accessories": [ From d04d41734477256875717ab4d7a0c2a4457e5ca7 Mon Sep 17 00:00:00 2001 From: S'pht'Kr Date: Fri, 2 Oct 2015 06:19:59 +0200 Subject: [PATCH 20/69] Initial read/write support for RGB bulbs complete MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Needs testing, but seems to work with my Aeon bulb…taking into account the wonkiness of that bulb with Z-Way, at least. --- platforms/ZWayServer.js | 108 +++++++++++++++++++++++++++++++++++----- 1 file changed, 95 insertions(+), 13 deletions(-) diff --git a/platforms/ZWayServer.js b/platforms/ZWayServer.js index c86d4ac..730eb9b 100644 --- a/platforms/ZWayServer.js +++ b/platforms/ZWayServer.js @@ -180,6 +180,11 @@ ZWayServerPlatform.prototype = { if(this.cxVDevMap[upd.id]){ var vdev = this.vDevStore[upd.id]; vdev.metrics.level = upd.metrics.level; + if(upd.metrics.color){ + vdev.metrics.r = upd.metrics.r; + vdev.metrics.g = upd.metrics.g; + vdev.metrics.b = upd.metrics.b; + } vdev.updateTime = upd.updateTime; var cxs = this.cxVDevMap[upd.id]; for(var j = 0; j < cxs.length; j++){ @@ -231,26 +236,62 @@ ZWayServerAccessory.prototype = { }, rgb2hsv: function(obj) { + // RGB: 0-255; H: 0-360, S,V: 0-100 var r = obj.r/255, g = obj.g/255, b = obj.b/255; var max, min, d, h, s, v; - if (min === max) { - // shade of gray - return [0, 0, r]; - } - min = Math.min(r, Math.min(g, b)); max = Math.max(r, Math.max(g, b)); + if (min === max) { + // shade of gray + return {h: 0, s: 0, v: r * 100}; + } + var d = (r === min) ? g - b : ((b === min) ? r - g : b - r); h = (r === min) ? 3 : ((b === min) ? 1 : 5); h = 60 * (h - d/(max - min)); s = (max - min) / max; v = max; - return {"h": h, "s": s * 100, "v": v}; + return {"h": h, "s": s * 100, "v": v * 100}; + } + , + hsv2rgb: function(obj) { + // H: 0-360; S,V: 0-100; RGB: 0-255 + var r, g, b; + var sfrac = obj.s / 100; + var vfrac = obj.v / 100; + + if(sfrac === 0){ + var vbyte = Math.round(vfrac*255); + return { r: vbyte, g: vbyte, b: vbyte }; + } + + var hdb60 = (obj.h % 360) / 60; + var sector = Math.floor(hdb60); + var fpart = hdb60 - sector; + var c = vfrac * (1 - sfrac); + var x1 = vfrac * (1 - sfrac * fpart); + var x2 = vfrac * (1 - sfrac * (1 - fpart)); + switch(sector){ + case 0: + r = vfrac; g = x2; b = c; break; + case 1: + r = x1; g = vfrac; b = c; break; + case 2: + r = c; g = vfrac; b = x2; break; + case 3: + r = c; g = x1; b = vfrac; break; + case 4: + r = x2; g = c; b = vfrac; break; + case 5: + default: + r = vfrac; g = c; b = x1; break; + } + + return { "r": Math.round(255 * r), "g": Math.round(255 * g), "b": Math.round(255 * b) }; } , - getVDevServices: function(vdev){ var typeKey = ZWayServerPlatform.getVDevTypeKey(vdev); var services = [], service; @@ -340,7 +381,7 @@ ZWayServerAccessory.prototype = { return null; } , - configureCharacteristic: function(cx, vdev){ + configureCharacteristic: function(cx, vdev, service){ var accessory = this; // Add this combination to the maps... @@ -414,6 +455,7 @@ ZWayServerAccessory.prototype = { if(cx instanceof Characteristic.Hue){ cx.zway_getValueFromVDev = function(vdev){ + debug("Derived value " + accessory.rgb2hsv(vdev.metrics.color).h + " for hue."); return accessory.rgb2hsv(vdev.metrics.color).h; }; cx.value = cx.zway_getValueFromVDev(vdev); @@ -424,6 +466,18 @@ ZWayServerAccessory.prototype = { callback(false, cx.zway_getValueFromVDev(result.data)); }); }.bind(this)); + cx.on('set', function(hue, callback){ + var scx = service.getCharacteristic(Characteristic.Saturation); + var vcx = service.getCharacteristic(Characteristic.Brightness); + if(!scx || !vcx){ + debug("Hue without Saturation and Brightness is not supported! Cannot set value!") + callback(true, cx.value); + } + var rgb = this.hsv2rgb({ h: hue, s: scx.value, v: vcx.value }); + this.command(vdev, "exact", { red: rgb.r, green: rgb.g, blue: rgb.b }).then(function(result){ + callback(); + }); + }.bind(this)); cx.writeable = false; //cx.on('set', function(level, callback){ @@ -436,6 +490,7 @@ ZWayServerAccessory.prototype = { if(cx instanceof Characteristic.Saturation){ cx.zway_getValueFromVDev = function(vdev){ + debug("Derived value " + accessory.rgb2hsv(vdev.metrics.color).s + " for saturation."); return accessory.rgb2hsv(vdev.metrics.color).s; }; cx.value = cx.zway_getValueFromVDev(vdev); @@ -446,6 +501,18 @@ ZWayServerAccessory.prototype = { callback(false, cx.zway_getValueFromVDev(result.data)); }); }.bind(this)); + cx.on('set', function(saturation, callback){ + var hcx = service.getCharacteristic(Characteristic.Hue); + var vcx = service.getCharacteristic(Characteristic.Brightness); + if(!hcx || !vcx){ + debug("Saturation without Hue and Brightness is not supported! Cannot set value!") + callback(true, cx.value); + } + var rgb = this.hsv2rgb({ h: hcx.value, s: saturation, v: vcx.value }); + this.command(vdev, "exact", { red: rgb.r, green: rgb.g, blue: rgb.b }).then(function(result){ + callback(); + }); + }.bind(this)); cx.writeable = false; //cx.on('set', function(level, callback){ @@ -666,14 +733,29 @@ ZWayServerAccessory.prototype = { success = false; debug("ERROR! Failed to configure required characteristic \"" + service.characteristics[i].displayName + "\"!"); } - cx = this.configureCharacteristic(cx, vdev); + cx = this.configureCharacteristic(cx, vdev, service); } for(var i = 0; i < service.optionalCharacteristics.length; i++){ var cx = service.optionalCharacteristics[i]; - var vdev = this.getVDevForCharacteristic(cx); + var vdev = this.getVDevForCharacteristic(cx, vdev); if(!vdev) continue; - cx = this.configureCharacteristic(cx, vdev); - if(cx) service.addCharacteristic(cx); + + //NOTE: Questionable logic, but if the vdev has already been used for the same + // characteristic type elsewhere, lets not duplicate it just for the sake of an + // optional characteristic. This eliminates the problem with RGB+W+W bulbs + // having the HSV controls shown again, but might have unintended consequences... + var othercx, othercxs = this.platform.cxVDevMap[vdev.id]; + if(othercxs) for(var j = 0; j < othercxs.length; j++) if(othercxs[j].UUID === cx.UUID) othercx = othercxs[j]; + if(othercx) + continue; + + cx = this.configureCharacteristic(cx, vdev, service); + try { + if(cx) service.addCharacteristic(cx); + } + catch (ex) { + debug('Adding Characteristic "' + cx.displayName + '" failed with message "' + ex.message + '". This may be expected.'); + } } return success; } @@ -736,7 +818,7 @@ ZWayServerAccessory.prototype = { extraCxs = []; // to wipe out any already setup cxs. break; } - this.configureCharacteristic(cx, vdev2); + this.configureCharacteristic(cx, vdev2, service); extraCxs.push(cx); } for(var j = 0; j < extraCxs.length; j++) From fd72307384a432ac84123b4267cc12b1b64abef5 Mon Sep 17 00:00:00 2001 From: Snowdd1 Date: Fri, 2 Oct 2015 08:17:59 +0200 Subject: [PATCH 21/69] Fix hint in KNX-sample-config.json --- platforms/KNX-sample-config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/KNX-sample-config.json b/platforms/KNX-sample-config.json index 86e070b..05b7e15 100644 --- a/platforms/KNX-sample-config.json +++ b/platforms/KNX-sample-config.json @@ -8,7 +8,7 @@ "description": "This is an example configuration file for KNX platform shim", "hint": "Always paste into jsonlint.com validation page before starting your homebridge, saves a lot of frustration", "hint2": "Replace all group addresses by current addresses of your installation, these are arbitrary examples!", - "hint3": "For valid services and their characteristics have a look at the knxdevice.md file in folder accessories!", + "hint3": "For valid services and their characteristics have a look at the KNX.md file in folder platforms!", "platforms": [ { "platform": "KNX", From 3031ba7e0fdcf78251aa0691219b2b71d01f8434 Mon Sep 17 00:00:00 2001 From: Snowdd1 Date: Fri, 2 Oct 2015 18:05:40 +0200 Subject: [PATCH 22/69] Refactoring of reversely used group addresses The use of 0/1 for binary objects depends on installation, a contact sensor can have contact=1 or open=1 The same applies to percentages: a window can be 100% closed or 100% open. For a more general usage of reversed values, a suffix ("R") can now be attached to all DPT1 and DPT5 group addresses. Please see updated KNX.md for examples and details. Note: outdated characteristic-workaround throw an exception at boot-up: - ContactSensorStateContact1 - LockCurrentStateSecured0 - LockTargetStateSecured0 Please see error message and KNX.md for reference. --- accessories/knxdevice.js | 162 +++++++++++++++++-------------- platforms/KNX-sample-config.json | 10 +- platforms/KNX.js | 17 ++-- platforms/KNX.md | 30 ++++-- 4 files changed, 127 insertions(+), 92 deletions(-) diff --git a/accessories/knxdevice.js b/accessories/knxdevice.js index b2967c2..4656522 100644 --- a/accessories/knxdevice.js +++ b/accessories/knxdevice.js @@ -14,6 +14,9 @@ New 2015-09-18: New 2015-09-19: - GarageDoorOpener Service - MotionSensor Service +New 2015-10-02: +- Check for valid group addresses +- new "R" flag allowed for Boolean addresses: 1/2/3R is the boolean not(1/2/3), i.e. 0 and 1 switched on read and write * */ var Service = require("HAP-NodeJS").Service; @@ -24,6 +27,8 @@ var knxd_startMonitor = require('../platforms/KNX.js').startMonitor; var milliTimeout = 300; // used to block responses while swiping +var colorOn = "\x1b[30;47m"; +var colorOff = "\x1b[0m"; function KNXDevice(log, config) { this.log = log; @@ -115,6 +120,7 @@ KNXDevice.prototype = { if (!groupAddress) { return null; } + this.log("[knxdevice:knxread] preparing knx request for "+groupAddress); var knxdConnection = new knxd.Connection(); // this.log("DEBUG in knxread: created empty connection, trying to connect socket to "+this.knxd_ip+":"+this.knxd_port); knxdConnection.socketRemote({ host: this.knxd_ip, port: this.knxd_port }, function() { @@ -130,7 +136,7 @@ KNXDevice.prototype = { if (err) { this.log("[ERROR] knxread:sendAPDU: " + err); } else { - this.log("knx request sent for "+groupAddress); + this.log("[knxdevice:knxread] knx request sent for "+groupAddress); } }.bind(this)); } @@ -143,12 +149,12 @@ KNXDevice.prototype = { // handle multiple addresses for (var i = 0; i < groupAddresses.length; i++) { if (groupAddresses[i]) { // do not bind empty addresses - this.knxread (groupAddresses[i]); + this.knxread (groupAddresses[i].match(/(\d*\/\d*\/\d*)/)[0]); // clean address } } } else { // it's only one - this.knxread (groupAddresses); + this.knxread (groupAddresses.match(/(\d*\/\d*\/\d*)/)[0]); // regex for cleaning address } }, @@ -158,38 +164,30 @@ KNXDevice.prototype = { // boolean: get 0 or 1 from the bus, write boolean knxregister_bool: function(addresses, characteristic) { this.log("knx registering BOOLEAN " + addresses); - knxd_registerGA(addresses, function(val, src, dest, type){ + knxd_registerGA(addresses, function(val, src, dest, type, reverse){ this.log("[" +this.name + "]: Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type + " for " + characteristic.displayName); // iterate(characteristic); - characteristic.setValue(val ? 1 : 0, undefined, 'fromKNXBus'); - }.bind(this)); - }, - knxregister_boolReverse: function(addresses, characteristic) { - this.log("knx registering BOOLEAN " + addresses); - knxd_registerGA(addresses, function(val, src, dest, type){ - this.log("[" +this.name + "]: Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type + " for " + characteristic.displayName); -// iterate(characteristic); - characteristic.setValue(val ? 0 : 1, undefined, 'fromKNXBus'); + + characteristic.setValue(val ? (reverse ? 0:1) : (reverse ? 1:0), undefined, 'fromKNXBus'); }.bind(this)); }, +// knxregister_boolReverse: function(addresses, characteristic) { +// this.log("knx registering BOOLEAN REVERSE " + addresses); +// knxd_registerGA(addresses, function(val, src, dest, type, reverse){ +// this.log("[" +this.name + "]: Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type + " for " + characteristic.displayName); +//// iterate(characteristic); +// characteristic.setValue(val ? 0 : 1, undefined, 'fromKNXBus'); +// }.bind(this)); +// }, // percentage: get 0..255 from the bus, write 0..100 to characteristic knxregister_percent: function(addresses, characteristic) { this.log("knx registering PERCENT " + addresses); - knxd_registerGA(addresses, function(val, src, dest, type){ + knxd_registerGA(addresses, function(val, src, dest, type, reverse){ this.log("[" +this.name + "]: Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName); if (type !== "DPT5") { this.log("[ERROR] Received value cannot be a percentage value"); } else { -// if (!characteristic.timeout) { -// if (characteristic.timeout < Date.now()) { - characteristic.setValue(Math.round(val/255*100), undefined, 'fromKNXBus'); -// } else { -// this.log("Blackout time"); -// } -// } else { -// characteristic.setValue(Math.round(val/255*100), undefined, 'fromKNXBus'); -// } // todo get the boolean logic right into one OR expresssion - + characteristic.setValue(Math.round(( reverse ? (255-val):val)/255*100), undefined, 'fromKNXBus'); } }.bind(this)); }, @@ -200,7 +198,7 @@ KNXDevice.prototype = { var validValue = true; var hk_value = 0.0; this.log("["+ this.name +"]:[" + characteristic.displayName+ "]:knx registering FLOAT " + addresses); - knxd_registerGA(addresses, function(val, src, dest, type){ + knxd_registerGA(addresses, function(val, src, dest, type, reverse){ this.log("["+ this.name +"]:[" + characteristic.displayName+ "]: Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName); // make hk_value compliant to properties if (characteristic.props.minStep) { @@ -227,10 +225,10 @@ KNXDevice.prototype = { //integer knxregister_int: function(addresses, characteristic) { this.log("["+ this.name +"]:[" + characteristic.displayName+ "]:knx registering INT " + addresses); - knxd_registerGA(addresses, function(val, src, dest, type){ + knxd_registerGA(addresses, function(val, src, dest, type, reverse){ this.log("["+ this.name +"]:[" + characteristic.displayName+ "]: Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName); if (val>=(characteristic.props.minValue || 0) && val<=(characteristic.props.maxValue || 255)) { - characteristic.setValue(val, undefined, 'fromKNXBus'); + characteristic.setValue(reverse ? (255-val):val, undefined, 'fromKNXBus'); } else { this.log("["+ this.name +"]:[" + characteristic.displayName+ "]: Value %s out of bounds %s...%s ",hk_value, (characteristic.props.minValue || 0), (characteristic.props.maxValue || 255)); } @@ -294,39 +292,39 @@ KNXDevice.prototype = { * }.bind(this)); * */ - setBooleanState: function(value, callback, context, gaddress) { + setBooleanState: function(value, callback, context, gaddress, reverseflag) { if (context === 'fromKNXBus') { // this.log(gaddress + " event ping pong, exit!"); if (callback) { callback(); } } else { - var numericValue = 0; + var numericValue = reverseflag ? 1:0; if (value) { - numericValue = 1; // need 0 or 1, not true or something + numericValue = reverseflag ? 0:1; // need 0 or 1, not true or something } - this.log("["+ this.name +"]:Setting "+gaddress+" Boolean to %s", numericValue); + this.log("["+ this.name +"]:Setting "+gaddress+" " + reverseflag ? " (reverse)":""+ " Boolean to %s", numericValue); this.knxwrite(callback, gaddress,'DPT1',numericValue); } }, - setBooleanReverseState: function(value, callback, context, gaddress) { - if (context === 'fromKNXBus') { -// this.log(gaddress + " event ping pong, exit!"); - if (callback) { - callback(); - } - } else { - var numericValue = 0; - if (!value) { - numericValue = 1; // need 0 or 1, not true or something - } - this.log("["+ this.name +"]:Setting "+gaddress+" Boolean to %s", numericValue); - this.knxwrite(callback, gaddress,'DPT1',numericValue); - } - - }, - setPercentage: function(value, callback, context, gaddress) { +// setBooleanReverseState: function(value, callback, context, gaddress) { +// if (context === 'fromKNXBus') { +//// this.log(gaddress + " event ping pong, exit!"); +// if (callback) { +// callback(); +// } +// } else { +// var numericValue = 0; +// if (!value) { +// numericValue = 1; // need 0 or 1, not true or something +// } +// this.log("["+ this.name +"]:Setting "+gaddress+" Boolean to %s", numericValue); +// this.knxwrite(callback, gaddress,'DPT1',numericValue); +// } +// +// }, + setPercentage: function(value, callback, context, gaddress, reverseflag) { if (context === 'fromKNXBus') { // this.log(gaddress + "event ping pong, exit!"); if (callback) { @@ -334,8 +332,11 @@ KNXDevice.prototype = { } } else { var numericValue = 0; - if (value) { - numericValue = Math.round(255*value/100); // convert 1..100 to 1..255 for KNX bus + value = ( value>=0 ? (value<=100 ? value:100):0 ); //ensure range 0..100 + if (reverseflag) { + numericValue = 255 - Math.round(255*value/100); // convert 0..100 to 255..0 for KNX bus + } else { + numericValue = Math.round(255*value/100); // convert 0..100 to 0..255 for KNX bus } this.log("["+ this.name +"]:Setting "+gaddress+" percentage to %s (%s)", value, numericValue); this.knxwrite(callback, gaddress,'DPT5',numericValue); @@ -350,7 +351,7 @@ KNXDevice.prototype = { } else { var numericValue = 0; if (value && value>=0 && value<=255) { - numericValue = value; // assure 1..255 for KNX bus + numericValue = value; // assure 0..255 for KNX bus } this.log("["+ this.name +"]:Setting "+gaddress+" int to %s (%s)", value, numericValue); this.knxwrite(callback, gaddress,'DPT5',numericValue); @@ -411,27 +412,44 @@ KNXDevice.prototype = { /** bindCharacteristic * initializes callbacks for 'set' events (from HK) and for KNX bus reads (to HK) */ - bindCharacteristic: function(myService, characteristicType, valueType, config) { + bindCharacteristic: function(myService, characteristicType, valueType, config, defaultValue) { var myCharacteristic = myService.getCharacteristic(characteristicType); + var setGA = ""; + var setReverse = false; if (myCharacteristic === undefined) { throw new Error("unknown characteristics cannot be bound"); } + if (defaultValue) { + myCharacteristic.setValue(defaultValue); + } if (config.Set) { // can write + // extract address and Reverse flag + setGA = config.Set.match(/\d*\/\d*\/\d*/); + if (setGA===null) { + this.log(colorOn + "["+ this.name +"]:["+myCharacteristic.displayName+"] Error in group adress: ["+ config.Set +"] "+colorOff); + throw new Error("EINVGROUPADRESS - Invalid group address given"); + } else { + setGA=setGA[0]; // first element of returned array is the group address + } + + setReverse = config.Set.match(/\d*\/\d*\/\d*(R)/) ? true:false; + switch (valueType) { case "Bool": myCharacteristic.on('set', function(value, callback, context) { - this.setBooleanState(value, callback, context, config.Set); - }.bind(this)); - break; - case "BoolReverse": - myCharacteristic.on('set', function(value, callback, context) { - this.setBooleanReverseState(value, callback, context, config.Set); + this.setBooleanState(value, callback, context, setGA, setReverse); //NEW }.bind(this)); break; +// case "BoolReverse": +// this.log("["+ this.name +"]:["+myCharacteristic.displayName+"] \x1b[30;47m%s\x1b[0mWARNING in group adress: "+ config.Set +": Legacy BoolReverse used. Use " + config.Set +"R instead"); +// myCharacteristic.on('set', function(value, callback, context) { +// this.setBooleanReverseState(value, callback, context, config.Set); +// }.bind(this)); +// break; case "Percent": myCharacteristic.on('set', function(value, callback, context) { - this.setPercentage(value, callback, context, config.Set); + this.setPercentage(value, callback, context, setGA, setReverse); myCharacteristic.timeout = Date.now()+milliTimeout; }.bind(this)); break; @@ -451,7 +469,7 @@ KNXDevice.prototype = { }.bind(this)); break; default: { - this.log("[ERROR] unknown type passed"); + this.log(colorOn + "[ERROR] unknown type passed: [" + valueType+"]"+ colorOff); throw new Error("[ERROR] unknown type passed"); } } @@ -463,9 +481,9 @@ KNXDevice.prototype = { case "Bool": this.knxregister_bool([config.Set].concat(config.Listen || []), myCharacteristic); break; - case "BoolReverse": - this.knxregister_boolReverse([config.Set].concat(config.Listen || []), myCharacteristic); - break; +// case "BoolReverse": +// this.knxregister_boolReverse([config.Set].concat(config.Listen || []), myCharacteristic); +// break; case "Percent": this.knxregister_percent([config.Set].concat(config.Listen || []), myCharacteristic); break; @@ -479,8 +497,8 @@ KNXDevice.prototype = { this.knxregister_HVAC([config.Set].concat(config.Listen || []), myCharacteristic); break; default: - this.log("[ERROR] unknown type passed: ["+valueType+"]"); - throw new Error("[ERROR] unknown type passed"); + this.log(colorOn+ "[ERROR] unknown type passed: ["+valueType+"]"+colorOff); + throw new Error("[ERROR] unknown type passed"); } this.log("["+ this.name +"]:["+myCharacteristic.displayName+"]: Issuing read requests on the KNX bus..."); this.knxreadarray([config.Set].concat(config.Listen || [])); @@ -515,8 +533,8 @@ KNXDevice.prototype = { this.log("["+ this.name +"]:ContactSensor ContactSensorState characteristic enabled"); this.bindCharacteristic(myService, Characteristic.ContactSensorState, "Bool", config.ContactSensorState); } else if (config.ContactSensorStateContact1) { - this.log("["+ this.name +"]:ContactSensor ContactSensorStateContact1 characteristic enabled"); - this.bindCharacteristic(myService, Characteristic.ContactSensorState, "BoolReverse", config.ContactSensorStateContact1); + this.log(colorOn+ "[ERROR] outdated type passed: [ContactSensorStateContact1]"+colorOff); + throw new Error("[ERROR] outdated type passed"); } //optionals if (config.StatusActive) { @@ -670,16 +688,16 @@ KNXDevice.prototype = { this.bindCharacteristic(myService, Characteristic.LockCurrentState, "Bool", config.LockCurrentState); } else if (config.LockCurrentStateSecured0) { // for reverse contacts Secured = 0 - this.log("["+ this.name +"]:LockMechanism LockCurrentState characteristic enabled"); - this.bindCharacteristic(myService, Characteristic.LockCurrentState, "BoolReverse", config.LockCurrentStateSecured0); + this.log(colorOn+ "[ERROR] outdated type passed: [LockCurrentStateSecured0]"+colorOff); + throw new Error("[ERROR] outdated type passed"); } // LockTargetState if (config.LockTargetState) { this.log("["+ this.name +"]:LockMechanism LockTargetState characteristic enabled"); this.bindCharacteristic(myService, Characteristic.LockTargetState, "Bool", config.LockTargetState); } else if (config.LockTargetStateSecured0) { - this.log("["+ this.name +"]:LockMechanism LockTargetState characteristic enabled"); - this.bindCharacteristic(myService, Characteristic.LockTargetState, "BoolReverse", config.LockTargetStateSecured0); + this.log(colorOn+ "[ERROR] outdated type passed: [LockTargetStateSecured0]"+colorOff); + throw new Error("[ERROR] outdated type passed"); } //iterate(myService); @@ -887,7 +905,7 @@ KNXDevice.prototype = { } if (config.PositionState) { this.log("["+ this.name +"]:Window PositionState characteristic enabled"); - this.bindCharacteristic(myService, Characteristic.PositionState, "Float", config.PositionState); + this.bindCharacteristic(myService, Characteristic.PositionState, "Int", config.PositionState); } return myService; }, diff --git a/platforms/KNX-sample-config.json b/platforms/KNX-sample-config.json index 05b7e15..eda2fe3 100644 --- a/platforms/KNX-sample-config.json +++ b/platforms/KNX-sample-config.json @@ -61,13 +61,13 @@ "services": [ { "type": "LockMechanism", - "description": "iOS8 Lock mechanism, Supports LockCurrentStateSecured0 OR LockCurrentState, LockTargetStateSecured0 OR LockTargetState, use depending if LOCKED is 0 or 1", + "description": "iOS8 Lock mechanism, Supports LockCurrentState, LockTargetState, append R to the addresses if LOCKED is 1", "name": "Office Window Lock", - "LockCurrentStateSecured0": { - "Listen": "5/3/15" + "LockCurrentState": { + "Listen": "5/3/15R" }, - "LockTargetStateSecured0": { - "Listen": "5/3/15" + "LockTargetState": { + "Listen": "5/3/16R" } } ] diff --git a/platforms/KNX.js b/platforms/KNX.js index 573b3b9..65f7a13 100644 --- a/platforms/KNX.js +++ b/platforms/KNX.js @@ -116,8 +116,8 @@ function groupsocketlisten(opts, callback) { } -var registerSingleGA = function registerSingleGA (groupAddress, callback) { - subscriptions.push({address: groupAddress, callback: callback }); +var registerSingleGA = function registerSingleGA (groupAddress, callback, reverse) { + subscriptions.push({address: groupAddress, callback: callback, reverse:reverse }); } /* @@ -143,7 +143,7 @@ var startMonitor = function startMonitor(opts) { // using { host: name-ip, port if (subscriptions[i].address === dest) { // found one, notify console.log('HIT: Write from '+src+' to '+dest+': '+val+' ['+type+']'); - subscriptions[i].callback(val, src, dest, type); + subscriptions[i].callback(val, src, dest, type, subscriptions[i].reverse); } } }); @@ -156,7 +156,7 @@ var startMonitor = function startMonitor(opts) { // using { host: name-ip, port if (subscriptions[i].address === dest) { // found one, notify // console.log('HIT: Response from '+src+' to '+dest+': '+val+' ['+type+']'); - subscriptions[i].callback(val, src, dest, type); + subscriptions[i].callback(val, src, dest, type, subscriptions[i].reverse); } } @@ -185,13 +185,16 @@ var registerGA = function (groupAddresses, callback) { if (groupAddresses.constructor.toString().indexOf("Array") > -1) { // handle multiple addresses for (var i = 0; i < groupAddresses.length; i++) { - if (groupAddresses[i]) { // do not bind empty addresses - registerSingleGA (groupAddresses[i], callback); + if (groupAddresses[i] && groupAddresses[i].match(/(\d*\/\d*\/\d*)/)) { // do not bind empty addresses or invalid addresses + // clean the addresses + registerSingleGA (groupAddresses[i].match(/(\d*\/\d*\/\d*)/)[0], callback,groupAddresses[i].match(/\d*\/\d*\/\d*(R)/) ? true:false ); } } } else { // it's only one - registerSingleGA (groupAddresses, callback); + if (groupAddresses.match(/(\d*\/\d*\/\d*)/)) { + registerSingleGA (groupAddresses.match(/(\d*\/\d*\/\d*)/)[0], callback, groupAddresses[i].match(/\d*\/\d*\/\d*(R)/) ? true:false); + } } // console.log("listeners now: " + subscriptions.length); }; diff --git a/platforms/KNX.md b/platforms/KNX.md index 0b4d4e9..3e649b9 100644 --- a/platforms/KNX.md +++ b/platforms/KNX.md @@ -97,11 +97,25 @@ So the charcteristic section may look like: ```` +## reversal of values for characteristics +In general, all DPT1 types can be reversed. If you need a 1 for "contact" of a contact senser, you can append an "R" to the group address. +Likewise, all percentages of DPT5 can be reversed, if you need a 100% (=255) for window closed, append an "R" to the group address. Do not forget the listening addresses! + ````json + { + "type": "ContactSensor", + "description": "Sample ContactSensor with 1 as contact (0 is Apple's default)", + "name": "WindowContact1", + "ContactSensorState": { + "Listen": [ + "1/1/100R" + ] + } + } +```` # Supported Services and their characteristics - ## ContactSensor -- ContactSensorState: DPT 1.002, 0 as contact **OR** -- ContactSensorStateContact1: DPT 1.002, 1 as contact +- ContactSensorState: DPT 1.002, 0 as contact +- ~~ContactSensorStateContact1: DPT 1.002, 1 as contact~~ - StatusActive: DPT 1.011, 1 as true - StatusFault: DPT 1.011, 1 as true @@ -142,10 +156,10 @@ So the charcteristic section may look like: - CurrentAmbientLightLevel: DPT 9.004, 0 to 100000 Lux ## LockMechanism (This is poorly mapped!) -- LockCurrentState: DPT 1, 1 as secured **OR (but not both:)** -- LockCurrentStateSecured0: DPT 1, 0 as secured -- LockTargetState: DPT 1, 1 as secured **OR** -- LockTargetStateSecured0: DPT 1, 0 as secured +- LockCurrentState: DPT 1, 1 as secured +- ~~LockCurrentStateSecured0: DPT 1, 0 as secured~~ +- LockTargetState: DPT 1, 1 as secured +- ~~LockTargetStateSecured0: DPT 1, 0 as secured~~ *ToDo here: correction of mappings, HomeKit reqires lock states UNSECURED=0, SECURED=1, JAMMED = 2, UNKNOWN=3* @@ -181,7 +195,7 @@ So the charcteristic section may look like: ## WindowCovering - CurrentPosition: DPT5 percentage - TargetPosition: DPT5 percentage -- PositionState: DPT5 value [listen only: 0 Closing, 1 Opening, 2 STopped] +- PositionState: DPT5 value [listen only: 0 Closing, 1 Opening, 2 Stopped] ### not yet supported - HoldPosition From ac62a44ce27ae30468dc65f59e3d0bdb9c3429c8 Mon Sep 17 00:00:00 2001 From: Raoul Date: Sat, 3 Oct 2015 14:48:06 +0200 Subject: [PATCH 23/69] Updates to wrong json Examples in KNX.md doc file Missing quotes around new key names. --- platforms/KNX.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/platforms/KNX.md b/platforms/KNX.md index 3e649b9..ffb7301 100644 --- a/platforms/KNX.md +++ b/platforms/KNX.md @@ -82,16 +82,16 @@ So the charcteristic section may look like: "Listen": [ "1/1/63" ], - minValue: -18, - maxValue: 30 + "minValue": -18, + "maxValue": 30 }, "TargetTemperature": { "Set": "1/1/62", "Listen": [ "1/1/64" ], - minValue: -4, - maxValue: 12 + "minValue": -4, + "maxValue": 12 } } ```` @@ -179,11 +179,11 @@ Likewise, all percentages of DPT5 can be reversed, if you need a 100% (=255) for - On: DPT 1.001, 1 as on, 0 as off ## TemperatureSensor -- CurrentTemperature: DPT9.001 in °C [listen only] +- CurrentTemperature: DPT9.001 in °C [listen only] ## Thermostat -- CurrentTemperature: DPT9.001 in °C [listen only], -40 to 80°C if not overriden as shown above -- TargetTemperature: DPT9.001, values 0..40°C only, all others are ignored +- CurrentTemperature: DPT9.001 in °C [listen only], -40 to 80°C if not overriden as shown above +- TargetTemperature: DPT9.001, values 0..40°C only, all others are ignored - CurrentHeatingCoolingState: DPT20.102 HVAC, because of the incompatible mapping only off and heating (=auto) are shown, [listen only] - TargetHeatingCoolingState: DPT20.102 HVAC, as above From b66610ce935e5d0660ba76e925a8eca282f338eb Mon Sep 17 00:00:00 2001 From: Nick Farina Date: Mon, 5 Oct 2015 17:12:06 -0700 Subject: [PATCH 24/69] Update Lockitron state on successful change --- accessories/Lockitron.js | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/accessories/Lockitron.js b/accessories/Lockitron.js index 04414af..8088d14 100644 --- a/accessories/Lockitron.js +++ b/accessories/Lockitron.js @@ -11,6 +11,17 @@ function LockitronAccessory(log, config) { this.name = config["name"]; this.accessToken = config["api_token"]; this.lockID = config["lock_id"]; + + this.service = new Service.LockMechanism(this.name); + + this.service + .getCharacteristic(Characteristic.LockCurrentState) + .on('get', this.getState.bind(this)); + + this.service + .getCharacteristic(Characteristic.LockTargetState) + .on('get', this.getState.bind(this)) + .on('set', this.setState.bind(this)); } LockitronAccessory.prototype.getState = function(callback) { @@ -36,7 +47,7 @@ LockitronAccessory.prototype.getState = function(callback) { } LockitronAccessory.prototype.setState = function(state, callback) { - var lockitronState = (state == 1) ? "lock" : "unlock"; + var lockitronState = (state == Characteristic.LockTargetState.SECURED) ? "lock" : "unlock"; this.log("Set state to %s", lockitronState); @@ -47,6 +58,14 @@ LockitronAccessory.prototype.setState = function(state, callback) { if (!err && response.statusCode == 200) { this.log("State change complete."); + + // we succeeded, so update the "current" state as well + var currentState = (state == Characteristic.LockTargetState.SECURED) ? + Characteristic.LockCurrentState.SECURED : Characteristic.LockCurrentState.UNSECURED; + + this.service + .setCharacteristic(Characteristic.LockCurrentState, currentState); + callback(null); // success } else { @@ -57,17 +76,5 @@ LockitronAccessory.prototype.setState = function(state, callback) { }, LockitronAccessory.prototype.getServices = function() { - - var service = new Service.LockMechanism(this.name); - - service - .getCharacteristic(Characteristic.LockCurrentState) - .on('get', this.getState.bind(this)); - - service - .getCharacteristic(Characteristic.LockTargetState) - .on('get', this.getState.bind(this)) - .on('set', this.setState.bind(this)); - - return [service]; + return [this.service]; } From feca9fbf0d9418ea742bdf58aa9a71ef1b79776e Mon Sep 17 00:00:00 2001 From: Nick Farina Date: Mon, 5 Oct 2015 17:29:53 -0700 Subject: [PATCH 25/69] Support WeMo "Light" services --- accessories/WeMo.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/accessories/WeMo.js b/accessories/WeMo.js index c19e19f..b97b041 100644 --- a/accessories/WeMo.js +++ b/accessories/WeMo.js @@ -144,6 +144,16 @@ WeMoAccessory.prototype.getServices = function() { return [garageDoorService]; } + else if (this.service == "Light") { + var lightbulbService = new Service.Lightbulb(this.name); + + lightbulbService + .getCharacteristic(Characteristic.On) + .on('get', this.getPowerOn.bind(this)) + .on('set', this.setPowerOn.bind(this)); + + return [lightbulbService]; + } else if (this.service == "MotionSensor") { var motionSensorService = new Service.MotionSensor(this.name); From e6afda55d645caeb960f65e4c094ba0cc4c37a61 Mon Sep 17 00:00:00 2001 From: Snowdd1 Date: Tue, 6 Oct 2015 20:47:07 +0200 Subject: [PATCH 26/69] New UUID generation addresses #203 # This is a breaking change for all configured homekit databases, as it will remove room/area assignments and other things configured in iOS apps ... unless your accessory type was called `Object` before Changes: - Without any changes to the configuration file all accessories UUIDs will be generated by `uuid.generate(accessoryType + ":"` instead of the constructor name which turned out to be unreliable in that context. So about all UUIDs will change. - new parameter uuid_base can be used for accessories in config.json to use a different base for UUID generation than the displayName, which might not be unique - platforms can add the same property to their accessories before returned, just as they have a name property right now. --- app.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app.js b/app.js index a8c2006..126b7e3 100644 --- a/app.js +++ b/app.js @@ -86,7 +86,7 @@ function loadAccessories() { log("Initializing %s accessory...", accessoryType); var accessoryInstance = new accessoryConstructor(log, accessoryConfig); - var accessory = createAccessory(accessoryInstance, accessoryName); + var accessory = createAccessory(accessoryInstance, accessoryName, accessoryType, accessoryConfig.uuid_base); //pass accessoryType for UUID generation, and optional parameter uuid_base which can be used instead of displayName for UUID generation // add it to the bridge bridge.addBridgedAccessory(accessory); @@ -113,11 +113,11 @@ function loadPlatforms() { log("Initializing %s platform...", platformType); var platformInstance = new platformConstructor(log, platformConfig); - loadPlatformAccessories(platformInstance, log); + loadPlatformAccessories(platformInstance, log, platformType); } } -function loadPlatformAccessories(platformInstance, log) { +function loadPlatformAccessories(platformInstance, log, platformType) { asyncCalls++; platformInstance.accessories(once(function(foundAccessories){ asyncCalls--; @@ -129,7 +129,7 @@ function loadPlatformAccessories(platformInstance, log) { log("Initializing platform accessory '%s'...", accessoryName); - var accessory = createAccessory(accessoryInstance, accessoryName); + var accessory = createAccessory(accessoryInstance, accessoryName, platformType, accessoryInstance.uuid_base); // add it to the bridge bridge.addBridgedAccessory(accessory); @@ -141,7 +141,7 @@ function loadPlatformAccessories(platformInstance, log) { })); } -function createAccessory(accessoryInstance, displayName) { +function createAccessory(accessoryInstance, displayName, accessoryType, uuid_base) { var services = accessoryInstance.getServices(); @@ -159,7 +159,7 @@ function createAccessory(accessoryInstance, displayName) { // The returned "services" for this accessory are simply an array of new-API-style // Service instances which we can add to a created HAP-NodeJS Accessory directly. - var accessoryUUID = uuid.generate(accessoryInstance.constructor.name + ":" + displayName); + var accessoryUUID = uuid.generate(accessoryType + ":" + (uuid_base || displayName)); var accessory = new Accessory(displayName, accessoryUUID); From af79ea4fbf5b172cdf0f076307da91e0b37fdd55 Mon Sep 17 00:00:00 2001 From: S'pht'Kr Date: Wed, 7 Oct 2015 05:38:24 +0200 Subject: [PATCH 27/69] Beginning tag support enhancements Generalized tag recognition, tags are now `Homebridge.*` instead of `Homebridge:*`, initial attempt at `IsPrimary` but probably not working right yet. --- platforms/ZWayServer.js | 49 +++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/platforms/ZWayServer.js b/platforms/ZWayServer.js index 730eb9b..f21f39b 100644 --- a/platforms/ZWayServer.js +++ b/platforms/ZWayServer.js @@ -83,7 +83,20 @@ ZWayServerPlatform.prototype = { return deferred.promise; } , - + getTagValue: function(vdev, tagStem){ + if(!(vdev.tags && vdev.tags.length > 0)) return false; + var tagStem = "Homebridge." + tagStem; + if(vdev.tags.indexOf(tagStem) >= 0) return true; + var tags = vdev.tags, l = tags.length, tag; + for(var i = 0; i < l; i++){ + tag = tags[i]; + if(tag.indexOf(tagStem + ":") === 0){ + return tag.substr(tagStem.length + 1); + } + } + return false; + } + , accessories: function(callback) { debug("Fetching Z-Way devices..."); @@ -110,12 +123,24 @@ ZWayServerPlatform.prototype = { var groupedDevices = {}; for(var i = 0; i < devices.length; i++){ var vdev = devices[i]; - if(vdev.tags.indexOf("Homebridge:Skip") >= 0) { debug("Tag says skip!"); continue; } - if(this.opt_in && vdev.tags.indexOf("Homebridge:Include") < 0) continue; + if(this.getTagValue("Skip")) { debug("Tag says skip!"); continue; } + if(this.opt_in && !this.getTagValue(vdev, "Include")) continue; var gdid = vdev.id.replace(/^(.*?)_zway_(\d+-\d+)-\d.*/, '$1_$2'); var gd = groupedDevices[gdid] || (groupedDevices[gdid] = {devices: [], types: {}, extras: {}, primary: undefined}); gd.devices.push(vdev); var tk = ZWayServerPlatform.getVDevTypeKey(vdev); + + // If this is explicitly set as primary, set it now... + if(this.getTagValue("IsPrimary")){ + gd.primary = gd.devices.length - 1; + if(gd.types[tk] !== undefined){ + // everybody out of the way! + gd.extras[tk] = gd.extras[tk] || []; + gd.extras[tk].push(gd.types[tk]); + } + gd.types[tk] = gd.primary; + } + if(gd.types[tk] === undefined){ gd.types[tk] = gd.devices.length - 1; } else { @@ -136,12 +161,17 @@ ZWayServerPlatform.prototype = { } var accessory = null; - for(var ti = 0; ti < primaryDeviceClasses.length; ti++){ + if(gd.primary !== undefined){ + var pd = gd.devices[gd.primary]; + var name = pd.metrics && pd.metrics.title ? pd.metrics.title : pd.id; + accessory = new ZWayServerAccessory(name, gd, that); + } + else for(var ti = 0; ti < primaryDeviceClasses.length; ti++){ if(gd.types[primaryDeviceClasses[ti]] !== undefined){ gd.primary = gd.types[primaryDeviceClasses[ti]]; var pd = gd.devices[gd.primary]; var name = pd.metrics && pd.metrics.title ? pd.metrics.title : pd.id; - debug("Using primary device with type " + primaryDeviceClasses[ti] + ", " + name + " (" + pd.id + ") as primary."); + //debug("Using primary device with type " + primaryDeviceClasses[ti] + ", " + name + " (" + pd.id + ") as primary."); accessory = new ZWayServerAccessory(name, gd, that); break; } @@ -303,7 +333,11 @@ ZWayServerAccessory.prototype = { services.push(new Service.Switch(vdev.metrics.title, vdev.id)); break; case "switchMultilevel": - services.push(new Service.Lightbulb(vdev.metrics.title, vdev.id)); + if(this.platform.getTagValue(vdev, "ServiceType") === "Switch"){ + services.push(new Service.Switch(vdev.metrics.title, vdev.id)); + } else { + services.push(new Service.Lightbulb(vdev.metrics.title, vdev.id)); + } break; case "sensorBinary.Door/Window": services.push(new Service.GarageDoorOpener(vdev.metrics.title, vdev.id)); @@ -778,7 +812,8 @@ ZWayServerAccessory.prototype = { // Any extra switchMultilevels? Could be a RGBW+W bulb, add them as additional services... if(this.devDesc.extras["switchMultilevel"]) for(var i = 0; i < this.devDesc.extras["switchMultilevel"].length; i++){ var xvdev = this.devDesc.devices[this.devDesc.extras["switchMultilevel"][i]]; - services = services.concat(this.getVDevServices(xvdev)); + var xservice = this.getVDevServices(xvdev); + services = services.concat(xservice); } if(this.platform.splitServices){ From a15c026f2f5503ca80ed47881c5bf102d5bf28b0 Mon Sep 17 00:00:00 2001 From: S'pht'Kr Date: Wed, 7 Oct 2015 06:59:04 +0200 Subject: [PATCH 28/69] Manual address specification working now --- platforms/YamahaAVR.js | 54 +++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/platforms/YamahaAVR.js b/platforms/YamahaAVR.js index 1659c0f..f08fa96 100644 --- a/platforms/YamahaAVR.js +++ b/platforms/YamahaAVR.js @@ -23,6 +23,7 @@ function YamahaAVRPlatform(log, config){ this.setMainInputTo = config["setMainInputTo"]; this.expectedDevices = config["expected_devices"] || 100; this.discoveryTimeout = config["discovery_timeout"] || 30; + this.manualAddresses = config["manual_addresses"] || {}; this.browser = mdns.createBrowser(mdns.tcp('http'), {resolverSequence: sequence}); } @@ -75,25 +76,44 @@ YamahaAVRPlatform.prototype = { var accessories = []; var timer, timeElapsed = 0, checkCyclePeriod = 5000; - browser.on('serviceUp', function(service){ + // Hmm... seems we need to prevent double-listing via manual and Bonjour... + var sysIds = {}; + + var setupFromService = function(service){ var name = service.name; //console.log('Found HTTP service "' + name + '"'); // We can't tell just from mdns if this is an AVR... if (service.port != 80) return; // yamaha-nodejs assumes this, so finding one on another port wouldn't do any good anyway. var yamaha = new Yamaha(service.host); - yamaha.getSystemConfig().then(function(sysConfig){ - var sysModel = sysConfig.YAMAHA_AV.System[0].Config[0].Model_Name[0]; - var sysId = sysConfig.YAMAHA_AV.System[0].Config[0].System_ID[0]; - that.log("Found Yamaha " + sysModel + " - " + sysId + ", \"" + name + "\""); - var accessory = new YamahaAVRAccessory(that.log, that.config, service, yamaha, sysConfig); - accessories.push(accessory); - if(accessories.length >= this.expectedDevices) - timeoutFunction(); // We're done, call the timeout function now. - //callback([accessory]); - }, function(err){ - return; + yamaha.getSystemConfig().then( + function(sysConfig){ + var sysModel = sysConfig.YAMAHA_AV.System[0].Config[0].Model_Name[0]; + var sysId = sysConfig.YAMAHA_AV.System[0].Config[0].System_ID[0]; + if(sysIds[sysId]){ + this.log("WARN: Got multiple systems with ID " + sysId + "! Omitting duplicate!"); + return; + } + sysIds[sysId] = true; + this.log("Found Yamaha " + sysModel + " - " + sysId + ", \"" + name + "\""); + var accessory = new YamahaAVRAccessory(this.log, this.config, name, yamaha, sysConfig); + accessories.push(accessory); + if(accessories.length >= this.expectedDevices) + timeoutFunction(); // We're done, call the timeout function now. + }.bind(this) + ); + }.bind(this); + + // process manually specified devices... + for(var key in this.manualAddresses){ + if(!this.manualAddresses.hasOwnProperty(key)) continue; + setupFromService({ + name: key, + host: this.manualAddresses[key], + port: 80 }); - }); + } + + browser.on('serviceUp', setupFromService); browser.start(); // The callback can only be called once...so we'll have to find as many as we can @@ -119,15 +139,15 @@ YamahaAVRPlatform.prototype = { } }; -function YamahaAVRAccessory(log, config, mdnsService, yamaha, sysConfig) { +function YamahaAVRAccessory(log, config, name, yamaha, sysConfig) { this.log = log; this.config = config; - this.mdnsService = mdnsService; this.yamaha = yamaha; this.sysConfig = sysConfig; - this.name = mdnsService.name; - this.serviceName = mdnsService.name + " Speakers"; + this.nameSuffix = config["name_suffix"] || " Speakers"; + this.name = name; + this.serviceName = name + this.nameSuffix; this.setMainInputTo = config["setMainInputTo"]; this.playVolume = this.config["play_volume"]; this.minVolume = config["min_volume"] || -50.0; From ed8a3c806231dc51fecbea64812c93a6cc4e42d0 Mon Sep 17 00:00:00 2001 From: Snowdd1 Date: Wed, 7 Oct 2015 10:39:00 +0200 Subject: [PATCH 29/69] uuid_base in constructor --- accessories/knxdevice.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/accessories/knxdevice.js b/accessories/knxdevice.js index 4656522..6aa4ffd 100644 --- a/accessories/knxdevice.js +++ b/accessories/knxdevice.js @@ -17,6 +17,8 @@ New 2015-09-19: New 2015-10-02: - Check for valid group addresses - new "R" flag allowed for Boolean addresses: 1/2/3R is the boolean not(1/2/3), i.e. 0 and 1 switched on read and write +New 2015-10-07: +- Accept uuid_base parameter from config.json to use as unique identifier in UUIDs instead of name (optional) * */ var Service = require("HAP-NodeJS").Service; @@ -38,6 +40,9 @@ function KNXDevice(log, config) { if (config.name) { this.name = config.name; } + if (config.uuid_base) { + this.uuid_base = config.uuid_base; + } if (config.knxd_ip){ this.knxd_ip = config.knxd_ip; } else { From 772f35efac270edde65cb6a4fe8af2cc02573bb2 Mon Sep 17 00:00:00 2001 From: stipus Date: Thu, 8 Oct 2015 09:07:33 +0200 Subject: [PATCH 30/69] Create HomeSeer.js --- platforms/HomeSeer.js | 370 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 370 insertions(+) create mode 100644 platforms/HomeSeer.js diff --git a/platforms/HomeSeer.js b/platforms/HomeSeer.js new file mode 100644 index 0000000..4618efe --- /dev/null +++ b/platforms/HomeSeer.js @@ -0,0 +1,370 @@ +'use strict'; + +// +// HomeSeer Platform Shim for HomeBridge +// V0.1 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/07 - Initial version +// +// +// Remember to add platform to config.json. +// +// You can get HomeSeer Device References by clicking a HomeSeer device name, then +// choosing the Advanced Tab. +// +// Example: +// "platforms": [ +// { +// "platform": "HomeSeer", // required +// "name": "HomeSeer", // required +// "url": "http://192.168.3.4:81", // required +// "accessories":[ +// { +// "ref":8, // required - HomeSeer Device Reference (To get it, select the HS Device - then Advanced Tab) +// "type":"Lightbulb", // Optional - Lightbulb is the default +// "name":"My Light", // Optional - HomeSeer device name is the default +// "offValue":"0", // Optional - 0 is the default +// "onValue":"100", // Optional - 100 is the default +// "can_dim":true // Optional - true is the default - false for a non dimmable lightbulb +// }, +// { +// "ref":9 // This is a dimmable Lightbulb by default +// }, +// { +// "ref":58, // This is an controllable outlet +// "type":"Outlet" +// } +// ] +// } +// ], +// +// +// SUPORTED TYPES: +// - Lightbulb (can_dim, onValue, offValue options) +// - Fan (onValue, offValue options) +// - Switch (onValue, offValue options) +// - Outlet (onValue, offValue options) +// - TemperatureSensor +// - ContactSensor +// - MotionSensor +// - LeakSensor +// - LightSensor +// - OccupancySensor +// - SmokeSensor +// - Door + + +var Service = require("HAP-NodeJS").Service; +var Characteristic = require("HAP-NodeJS").Characteristic; +var request = require("request"); + + +function httpRequest(url, method, callback) { + request({ + url: url, + method: method + }, + function (error, response, body) { + callback(error, response, body) + }) +} + + + +function HomeSeerPlatform(log, config){ + this.log = log; + this.config = config; +} + +HomeSeerPlatform.prototype = { + accessories: function(callback) { + this.log("Fetching HomeSeer devices."); + + var refList = ""; + for( var i=0; i Date: Thu, 8 Oct 2015 13:50:34 -0400 Subject: [PATCH 31/69] Simple fix for undefined Nest device names --- platforms/Nest.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/platforms/Nest.js b/platforms/Nest.js index 414fcef..c1ef3bd 100644 --- a/platforms/Nest.js +++ b/platforms/Nest.js @@ -44,7 +44,11 @@ NestPlatform.prototype = { function NestThermostatAccessory(log, name, device, deviceId) { // device info - this.name = name; + if (name) { + this.name = name; + } else { + this.name = "Nest"; + } this.model = device.model_version; this.serial = device.serial_number; this.deviceId = deviceId; @@ -390,4 +394,4 @@ NestThermostatAccessory.prototype = { } module.exports.accessory = NestThermostatAccessory; -module.exports.platform = NestPlatform; \ No newline at end of file +module.exports.platform = NestPlatform; From c0dfc9a8cdc95badc21c1956feadf1204786c2ad Mon Sep 17 00:00:00 2001 From: Theodor Tonum Date: Sat, 10 Oct 2015 01:14:10 +0200 Subject: [PATCH 32/69] Add support for local Telldus control --- config-sample.json | 4 + package.json | 1 + platforms/Telldus.js | 265 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 270 insertions(+) create mode 100644 platforms/Telldus.js diff --git a/config-sample.json b/config-sample.json index 7af74db..b837f61 100644 --- a/config-sample.json +++ b/config-sample.json @@ -23,6 +23,10 @@ "token" : "telldus token", "token_secret" : "telldus token secret" }, + { + "platform" : "Telldus", + "name" : "Telldus" + }, { "platform": "Wink", "name": "Wink", diff --git a/package.json b/package.json index 3a3ca74..8f88a35 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "tough-cookie": "^2.0.0", "request": "2.49.x", "sonos": "0.8.x", + "telldus": "0.0.9", "telldus-live": "0.2.x", "teslams": "1.0.1", "unofficial-nest-api": "git+https://github.com/hachidorii/unofficial_nodejs_nest.git#d8d48edc952b049ff6320ef99afa7b2f04cdee98", diff --git a/platforms/Telldus.js b/platforms/Telldus.js new file mode 100644 index 0000000..87d37f1 --- /dev/null +++ b/platforms/Telldus.js @@ -0,0 +1,265 @@ +var types = require("HAP-NodeJS/accessories/types.js"); +var telldus = require('telldus'); + +function TelldusPlatform(log, config) { + var that = this; + that.log = log; +} + +TelldusPlatform.prototype = { + + accessories: function(callback) { + var that = this; + + that.log("Fetching devices..."); + + var devices = telldus.getDevicesSync(); + + that.log("Found " + devices.length + " devices..."); + + var foundAccessories = []; + + // Clean non device + for (var i = 0; i < devices.length; i++) { + if (devices[i].type != 'DEVICE') { + devices.splice(i, 1); + } + } + + for (var i = 0; i < devices.length; i++) { + if (devices[i].type === 'DEVICE') { + TelldusAccessory.create(that.log, devices[i], function(err, accessory) { + if (!!err) that.log("Couldn't load device info"); + foundAccessories.push(accessory); + if (foundAccessories.length >= devices.length) { + callback(foundAccessories); + } + }); + } + } + } +}; + +var TelldusAccessory = function TelldusAccessory(log, device) { + + this.log = log; + + var m = device.model.split(':'); + + this.dimTimeout = false; + + // Set accessory info + this.device = device; + this.id = device.id; + this.name = device.name; + this.manufacturer = "Telldus"; // NOTE: Change this later + this.model = device.model; + this.status = device.status; + switch (device.status.name) { + case 'OFF': + this.state = 0; + this.stateValue = 0; + break; + case 'ON': + this.state = 2; + this.stateValue = 1; + break; + case 'DIM': + this.state = 16; + this.stateValue = device.status.level; + break; + } +}; + +TelldusAccessory.create = function (log, device, callback) { + + callback(null, new TelldusAccessory(log, device)); + +}; + +TelldusAccessory.prototype = { + + dimmerValue: function() { + + if (this.state === 1) { + return 100; + } + + if (this.state === 16 && this.stateValue != "unde") { + return parseInt(this.stateValue * 100 / 255); + } + + return 0; + }, + + informationCharacteristics: function() { + var that = this; + + informationCharacteristics = [ + { + cType: types.NAME_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: that.name, + supportEvents: false, + supportBonjour: false, + manfDescription: "Name of the accessory", + designedMaxLength: 255 + },{ + cType: types.MANUFACTURER_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: that.manufacturer, + supportEvents: false, + supportBonjour: false, + manfDescription: "Manufacturer", + designedMaxLength: 255 + },{ + cType: types.MODEL_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: that.model, + 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: function () { + telldus.turnOff(that.id, function(err){ + if (!!err) that.log("Error: " + err.message); + telldus.turnOn(that.id, function(err){ + if (!!err) that.log("Error: " + err.message); + telldus.turnOff(that.id, function(err){ + if (!!err) that.log("Error: " + err.message); + telldus.turnOn(that.id, function(err){ + if (!!err) that.log("Error: " + err.message); + }); + }); + }); + }); + }, + perms: ["pw"], + format: "bool", + initialValue: false, + supportEvents: false, + supportBonjour: false, + manfDescription: "Identify Accessory", + designedMaxLength: 1 + } + ]; + return informationCharacteristics; + }, + + controlCharacteristics: function() { + var that = this; + + cTypes = [{ + cType: types.NAME_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: that.name, + supportEvents: true, + supportBonjour: false, + manfDescription: "Name of service", + designedMaxLength: 255 + }] + + cTypes.push({ + cType: types.POWER_STATE_CTYPE, + onUpdate: function(value) { + if (value) { + telldus.turnOn(that.id, function(err){ + if (!!err) { + that.log("Error: " + err.message) + } else { + that.log(that.name + " - Updated power state: ON"); + } + }); + } else { + telldus.turnOff(that.id, function(err){ + if (!!err) { + that.log("Error: " + err.message) + } else { + that.log(that.name + " - Updated power state: OFF"); + } + }); + } + }, + perms: ["pw","pr","ev"], + format: "bool", + initialValue: (that.state != 2 && (that.state === 16 && that.stateValue != 0)) ? 1 : 0, + supportEvents: true, + supportBonjour: false, + manfDescription: "Change the power state", + designedMaxLength: 1 + }) + + if (that.model === "selflearning-dimmer") { + cTypes.push({ + cType: types.BRIGHTNESS_CTYPE, + onUpdate: function (value) { + if (that.dimTimeout) { + clearTimeout(that.dimTimeout); + } + + that.dimTimeout = setTimeout(function(){ + telldus.dim(that.id, (255 * (value / 100)), function(err, result){ + if (!!err) { + that.log("Error: " + err.message); + } else { + that.log(that.name + " - Updated brightness: " + value); + } + }); + that.dimTimeout = false; + }, 250); + }, + perms: ["pw", "pr", "ev"], + format: "int", + initialValue: that.dimmerValue(), + supportEvents: true, + supportBonjour: false, + manfDescription: "Adjust Brightness of Light", + designedMinValue: 0, + designedMaxValue: 100, + designedMinStep: 1, + unit: "%" + }) + } + + return cTypes + }, + + getServices: function() { + + var services = [ + { + sType: types.ACCESSORY_INFORMATION_STYPE, + characteristics: this.informationCharacteristics() + }, + { + sType: types.LIGHTBULB_STYPE, + characteristics: this.controlCharacteristics() + } + ]; + + return services; + } +}; + +module.exports.platform = TelldusPlatform; +module.exports.accessory = TelldusAccessory; From 4fbd7eb775a1137b8b88dfd674d4c6769c8ad54e Mon Sep 17 00:00:00 2001 From: stipus Date: Sat, 10 Oct 2015 12:38:46 +0200 Subject: [PATCH 33/69] Fix for HomeSeer occupancy sensor --- platforms/HomeSeer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platforms/HomeSeer.js b/platforms/HomeSeer.js index 4618efe..1ecbd43 100644 --- a/platforms/HomeSeer.js +++ b/platforms/HomeSeer.js @@ -3,6 +3,7 @@ // // HomeSeer Platform Shim for HomeBridge // V0.1 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/07 - Initial version +// V0.2 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/10 - occupancy sensor fix // // // Remember to add platform to config.json. @@ -320,7 +321,7 @@ HomeSeerAccessory.prototype = { } case "OccupancySensor": { var occupancySensorService = new Service.OccupancySensor(); - motionSensorService + occupancySensorService .getCharacteristic(Characteristic.OccupancyDetected) .on('get', this.getPowerState.bind(this)); services.push( occupancySensorService ); From b4f4f58519b0f428218d094fc9591caf727a78cc Mon Sep 17 00:00:00 2001 From: iRaven Date: Sat, 10 Oct 2015 14:05:16 +0200 Subject: [PATCH 34/69] Added Get-State Function --- accessories/HomeMatic.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/accessories/HomeMatic.js b/accessories/HomeMatic.js index ce575b1..89e43b0 100644 --- a/accessories/HomeMatic.js +++ b/accessories/HomeMatic.js @@ -30,7 +30,31 @@ HomeMatic.prototype = { } }); }, + getPowerState: function(callback) { + var that = this; + + this.log("Getting Power State of CCU"); + request.get({ + url: "http://"+this.ccuIP+"/config/xmlapi/state.cgi?datapoint_id="+this.ccuID, + }, function(err, response, body) { + if (!err && response.statusCode == 200) { + + //that.log("Response:"+response.body); + var responseString = response.body.substring(83,87); + //that.log(responseString); + switch(responseString){ + case "true": {modvalue = "1";break;} + case "fals": {modvalue = "0";break;} + } + callback(parseInt(modvalue)); + that.log("Getting Power State complete."); + } + else { + that.log("Error '"+err+"' getting Power State: " + body); + } + }); + }, getServices: function() { var that = this; return [{ @@ -101,6 +125,7 @@ HomeMatic.prototype = { },{ cType: types.POWER_STATE_CTYPE, onUpdate: function(value) { that.setPowerState(value); }, + onRead: function(callback) { that.getPowerState(callback); }, perms: ["pw","pr","ev"], format: "bool", initialValue: false, From 4a831422eb3c565bb33c965992196f982f88a1fa Mon Sep 17 00:00:00 2001 From: "stevetrease@gmail.com" Date: Sat, 10 Oct 2015 14:41:00 +0100 Subject: [PATCH 35/69] Added two new accessories for a readonly thermometer and hygrometer (humidity meter) based on HttpAccessory. --- accessories/HttpHygrometer.js | 71 ++++++++++++++++++++++++++++++ accessories/HttpThermometer.js | 79 ++++++++++++++++++++++++++++++++++ config-sample.json | 15 ++++++- 3 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 accessories/HttpHygrometer.js create mode 100644 accessories/HttpThermometer.js diff --git a/accessories/HttpHygrometer.js b/accessories/HttpHygrometer.js new file mode 100644 index 0000000..61ad3b9 --- /dev/null +++ b/accessories/HttpHygrometer.js @@ -0,0 +1,71 @@ +var Service = require("HAP-NodeJS").Service; +var Characteristic = require("HAP-NodeJS").Characteristic; +var request = require("request"); + +module.exports = { + accessory: HygrometerAccessory +} + +function HygrometerAccessory(log, config) { + this.log = log; + + // url info + this.url = config["url"]; + this.http_method = config["http_method"]; +} + + +HygrometerAccessory.prototype = { + + httpRequest: function(url, method, callback) { + request({ + url: url, + method: method + }, + function (error, response, body) { + callback(error, response, body) + }) + }, + + + identify: function(callback) { + this.log("Identify requested!"); + callback(); // success + }, + + getCurrentRelativeHumidity: function (callback) { + var that = this; + that.log ("getting CurrentCurrentRelativeHumidity"); + + this.httpRequest(this.url, this.http_method, function(error, response, body) { + if (error) { + this.log('HTTP function failed: %s', error); + callback(error); + } + else { + this.log('HTTP function succeeded - %s', body); + callback(null, Number(body)); + } + }.bind(this)); + }, + + getServices: function() { + + // you can OPTIONALLY create an information service if you wish to override + // the default values for things like serial number, model, etc. + var informationService = new Service.AccessoryInformation(); + + informationService + .setCharacteristic(Characteristic.Manufacturer, "HTTP Manufacturer") + .setCharacteristic(Characteristic.Model, "HTTP Hygrometer") + .setCharacteristic(Characteristic.SerialNumber, "HTTP Serial Number"); + + var humidityService = new Service.HumiditySensor(); + + humidityService + .getCharacteristic(Characteristic.CurrentRelativeHumidity) + .on('get', this.getCurrentRelativeHumidity.bind(this)); + + return [informationService, humidityService]; + } +}; diff --git a/accessories/HttpThermometer.js b/accessories/HttpThermometer.js new file mode 100644 index 0000000..ac9bdc2 --- /dev/null +++ b/accessories/HttpThermometer.js @@ -0,0 +1,79 @@ +var Service = require("HAP-NodeJS").Service; +var Characteristic = require("HAP-NodeJS").Characteristic; +var request = require("request"); + +module.exports = { + accessory: ThermometerAccessory +} + +function ThermometerAccessory(log, config) { + this.log = log; + + // url info + this.url = config["url"]; + this.http_method = config["http_method"]; +} + + +ThermometerAccessory.prototype = { + + httpRequest: function(url, method, callback) { + request({ + url: url, + method: method + }, + function (error, response, body) { + callback(error, response, body) + }) + }, + + + identify: function(callback) { + this.log("Identify requested!"); + callback(); // success + }, + + getCurrentTemperature: function (callback) { + var that = this; + that.log ("getting CurrentTemperature"); + + + this.httpRequest(this.url, this.http_method, function(error, response, body) { + if (error) { + this.log('HTTP function failed: %s', error); + callback(error); + } + else { + this.log('HTTP function succeeded - %s', body); + callback(null, Number(body)); + } + }.bind(this)); + }, + + getTemperatureUnits: function (callback) { + var that = this; + that.log ("getTemperature Units"); + // 1 = F and 0 = C + callback (null, 0); + }, + + getServices: function() { + + // you can OPTIONALLY create an information service if you wish to override + // the default values for things like serial number, model, etc. + var informationService = new Service.AccessoryInformation(); + + informationService + .setCharacteristic(Characteristic.Manufacturer, "HTTP Manufacturer") + .setCharacteristic(Characteristic.Model, "HTTP Thermometer") + .setCharacteristic(Characteristic.SerialNumber, "HTTP Serial Number"); + + var temperatureService = new Service.TemperatureSensor(); + + temperatureService + .getCharacteristic(Characteristic.CurrentTemperature) + .on('get', this.getCurrentTemperature.bind(this)); + + return [informationService, temperatureService]; + } +}; diff --git a/config-sample.json b/config-sample.json index 7af74db..85fd6e3 100644 --- a/config-sample.json +++ b/config-sample.json @@ -164,7 +164,20 @@ "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": "HttpHygrometer", + "name": "Kitchen", + "url": "http://host/URL", + "http_method": "GET" + }, + { + "accessory": "HttpThermometer", + "name": "Garage", + "url": "http://home/URL", + "http_method": "GET" + }, + { "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.", From 25299a7863455ed83b653e3a95419e609f48314a Mon Sep 17 00:00:00 2001 From: S'pht'Kr Date: Sat, 10 Oct 2015 15:45:27 +0200 Subject: [PATCH 36/69] =?UTF-8?q?Last=20bits=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- platforms/ZWayServer.js | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/platforms/ZWayServer.js b/platforms/ZWayServer.js index f21f39b..0b81e88 100644 --- a/platforms/ZWayServer.js +++ b/platforms/ZWayServer.js @@ -125,20 +125,26 @@ ZWayServerPlatform.prototype = { var vdev = devices[i]; if(this.getTagValue("Skip")) { debug("Tag says skip!"); continue; } if(this.opt_in && !this.getTagValue(vdev, "Include")) continue; - var gdid = vdev.id.replace(/^(.*?)_zway_(\d+-\d+)-\d.*/, '$1_$2'); + + var gdid = this.getTagValue(vdev, "Accessory.Id"); + if(!gdid){ + gdid = vdev.id.replace(/^(.*?)_zway_(\d+-\d+)-\d.*/, '$1_$2'); + } + var gd = groupedDevices[gdid] || (groupedDevices[gdid] = {devices: [], types: {}, extras: {}, primary: undefined}); gd.devices.push(vdev); var tk = ZWayServerPlatform.getVDevTypeKey(vdev); // If this is explicitly set as primary, set it now... - if(this.getTagValue("IsPrimary")){ - gd.primary = gd.devices.length - 1; + if(this.getTagValue(vdev, "IsPrimary")){ + // everybody out of the way! Can't be in "extras" if you're the primary... if(gd.types[tk] !== undefined){ - // everybody out of the way! gd.extras[tk] = gd.extras[tk] || []; gd.extras[tk].push(gd.types[tk]); + delete gd.types[tk]; // clear the way for this one to be set here below... } - gd.types[tk] = gd.primary; + gd.primary = gd.devices.length - 1; + //gd.types[tk] = gd.primary; } if(gd.types[tk] === undefined){ @@ -149,7 +155,7 @@ ZWayServerPlatform.prototype = { } if(tk !== vdev.deviceType) gd.types[vdev.deviceType] = gd.devices.length - 1; // also include the deviceType only as a possibility } - //TODO: Make a second pass, re-splitting any devices that don't make sense together + for(var gdid in groupedDevices) { if(!groupedDevices.hasOwnProperty(gdid)) continue; @@ -183,7 +189,6 @@ ZWayServerPlatform.prototype = { foundAccessories.push(accessory); } -//foundAccessories = foundAccessories.slice(0, 10); // Limit to a few devices for testing... callback(foundAccessories); // Start the polling process... @@ -332,8 +337,9 @@ ZWayServerAccessory.prototype = { case "switchBinary": services.push(new Service.Switch(vdev.metrics.title, vdev.id)); break; + case "switchRGBW": case "switchMultilevel": - if(this.platform.getTagValue(vdev, "ServiceType") === "Switch"){ + if(this.platform.getTagValue(vdev, "Service.Type") === "Switch"){ services.push(new Service.Switch(vdev.metrics.title, vdev.id)); } else { services.push(new Service.Lightbulb(vdev.metrics.title, vdev.id)); @@ -436,6 +442,12 @@ ZWayServerAccessory.prototype = { return cx; } + // We don't want to override "Name"'s name...so we just move this below that block. + var descOverride = this.platform.getTagValue(vdev, "Characteristic.Description"); + if(descOverride){ + cx.displayName = descOverride; + } + if(cx instanceof Characteristic.On){ cx.zway_getValueFromVDev = function(vdev){ var val = false; @@ -797,17 +809,23 @@ ZWayServerAccessory.prototype = { getServices: function() { var that = this; + var vdevPrimary = this.devDesc.devices[this.devDesc.primary]; + var accId = this.platform.getTagValue(vdevPrimary, "Accessory.Id"); + if(!accId){ + accId = "VDev-" + vdevPrimary.h; //FIXME: Is this valid? + } + var informationService = new Service.AccessoryInformation(); informationService .setCharacteristic(Characteristic.Name, this.name) .setCharacteristic(Characteristic.Manufacturer, "Z-Wave.me") .setCharacteristic(Characteristic.Model, "Virtual Device (VDev version 1)") - .setCharacteristic(Characteristic.SerialNumber, "VDev-" + this.devDesc.devices[this.devDesc.primary].h) //FIXME: Is this valid?); + .setCharacteristic(Characteristic.SerialNumber, accId); var services = [informationService]; - services = services.concat(this.getVDevServices(this.devDesc.devices[this.devDesc.primary])); + services = services.concat(this.getVDevServices(vdevPrimary)); // Any extra switchMultilevels? Could be a RGBW+W bulb, add them as additional services... if(this.devDesc.extras["switchMultilevel"]) for(var i = 0; i < this.devDesc.extras["switchMultilevel"].length; i++){ From 05e811cdd66c25c12d60dad03236613750834cce Mon Sep 17 00:00:00 2001 From: iRaven Date: Sat, 10 Oct 2015 15:56:51 +0200 Subject: [PATCH 37/69] Added HM-Sec-RHS Support as contact --- accessories/HomeMaticWindow.js | 123 +++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 accessories/HomeMaticWindow.js diff --git a/accessories/HomeMaticWindow.js b/accessories/HomeMaticWindow.js new file mode 100644 index 0000000..b7e585d --- /dev/null +++ b/accessories/HomeMaticWindow.js @@ -0,0 +1,123 @@ +var types = require("HAP-NodeJS/accessories/types.js"); +var Characteristic = require("HAP-NodeJS").Characteristic; +var request = require("request"); + +function HomeMaticWindow(log, config) { + this.log = log; + this.name = config["name"]; + this.ccuID = config["ccu_id"]; + this.ccuIP = config["ccu_ip"]; +} + +HomeMaticWindow.prototype = { + + + getPowerState: function(callback) { + var that = this; + + this.log("Getting Window State of CCU"); + request.get({ + url: "http://"+this.ccuIP+"/config/xmlapi/state.cgi?datapoint_id="+this.ccuID, + }, function(err, response, body) { + + if (!err && response.statusCode == 200) { + + //that.log("Response:"+response.body); + var responseString = response.body.substring(83,84); + //that.log(responseString); + switch(responseString){ + case "0": {callback(Characteristic.ContactSensorState.CONTACT_DETECTED);break;} + case "1": {callback(Characteristic.ContactSensorState.CONTACT_NOT_DETECTED);break;} + case "2": {callback(Characteristic.ContactSensorState.CONTACT_NOT_DETECTED);break;} + } + that.log("Getting Window State complete."); + } + else { + that.log("Error '"+err+"' getting Window State: " + body); + } + }); + }, + + 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: "Homematic", + supportEvents: false, + supportBonjour: false, + manfDescription: "Manufacturer", + designedMaxLength: 255 + },{ + cType: types.MODEL_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: "HM-Sec-RHS", + 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.CONTACT_SENSOR_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.CONTACT_SENSOR_STATE_CTYPE, + onRead: function(callback) { that.getPowerState(callback); }, + perms: ["pr","ev"], + format: "bool", + initialValue: false, + supportEvents: false, + supportBonjour: false, + manfDescription: "Get Window state of a Variable", + designedMaxLength: 1 + }] + }]; + } +}; + +module.exports.accessory = HomeMaticWindow; From a274ae4edab182a22e08b1bab875a1a15fe37646 Mon Sep 17 00:00:00 2001 From: S'pht'Kr Date: Sun, 11 Oct 2015 05:44:44 +0200 Subject: [PATCH 38/69] Switches to the *new* new Characteristics API format for the two custom Characteristics. Fixes #247 --- platforms/YamahaAVR.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/platforms/YamahaAVR.js b/platforms/YamahaAVR.js index f08fa96..0dde24f 100644 --- a/platforms/YamahaAVR.js +++ b/platforms/YamahaAVR.js @@ -31,24 +31,24 @@ function YamahaAVRPlatform(log, config){ YamahaAVRPlatform.AudioVolume = function() { Characteristic.call(this, 'Audio Volume', '00001001-0000-1000-8000-135D67EC4377'); - this.format = 'uint8'; - this.unit = 'percentage'; - this.maximumValue = 100; - this.minimumValue = 0; - this.stepValue = 1; - this.readable = true; - this.writable = true; - this.supportsEventNotification = true; + this.setProps({ + format: Characteristic.Formats.UINT8, + unit: Characteristic.Units.PERCENTAGE, + maxValue: 100, + minValue: 0, + minStep: 1, + perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY] + }); this.value = this.getDefaultValue(); }; inherits(YamahaAVRPlatform.AudioVolume, Characteristic); YamahaAVRPlatform.Muting = function() { Characteristic.call(this, 'Muting', '00001002-0000-1000-8000-135D67EC4377'); - this.format = 'bool'; - this.readable = true; - this.writable = true; - this.supportsEventNotification = true; + this.setProps({ + format: Characteristic.Formats.UINT8, + perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY] + }); this.value = this.getDefaultValue(); }; inherits(YamahaAVRPlatform.Muting, Characteristic); From c506c44d6091b05b550b6fc901f4a1c69a7b2605 Mon Sep 17 00:00:00 2001 From: stipus Date: Mon, 12 Oct 2015 02:02:37 +0200 Subject: [PATCH 39/69] Update HomeSeer.js --- platforms/HomeSeer.js | 248 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 230 insertions(+), 18 deletions(-) diff --git a/platforms/HomeSeer.js b/platforms/HomeSeer.js index 1ecbd43..8cb371a 100644 --- a/platforms/HomeSeer.js +++ b/platforms/HomeSeer.js @@ -2,8 +2,15 @@ // // HomeSeer Platform Shim for HomeBridge -// V0.1 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/07 - Initial version -// V0.2 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/10 - occupancy sensor fix +// V0.1 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/07 +// - Initial version +// V0.2 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/10 +// - Occupancy sensor fix +// V0.3 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/11 +// - Added TemperatureUnit=F|C option to temperature sensors +// - Added negative temperature support to temperature sensors +// V0.4 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/11 +// - Added thermostat support // // // Remember to add platform to config.json. @@ -14,24 +21,50 @@ // Example: // "platforms": [ // { -// "platform": "HomeSeer", // required -// "name": "HomeSeer", // required -// "url": "http://192.168.3.4:81", // required +// "platform": "HomeSeer", // Required +// "name": "HomeSeer", // Required +// "host": "http://192.168.3.4:81", // Required - If you did setup HomeSeer authentication, use "http://user:password@ip_address:port" // "accessories":[ // { -// "ref":8, // required - HomeSeer Device Reference (To get it, select the HS Device - then Advanced Tab) -// "type":"Lightbulb", // Optional - Lightbulb is the default -// "name":"My Light", // Optional - HomeSeer device name is the default -// "offValue":"0", // Optional - 0 is the default -// "onValue":"100", // Optional - 100 is the default -// "can_dim":true // Optional - true is the default - false for a non dimmable lightbulb +// "ref":8, // Required - HomeSeer Device Reference (To get it, select the HS Device - then Advanced Tab) +// "type":"Lightbulb", // Optional - Lightbulb is the default +// "name":"My Light", // Optional - HomeSeer device name is the default +// "offValue":"0", // Optional - 0 is the default +// "onValue":"100", // Optional - 100 is the default +// "can_dim":true // Optional - true is the default - false for a non dimmable lightbulb // }, // { -// "ref":9 // This is a dimmable Lightbulb by default +// "ref":9 // This is a dimmable Lightbulb by default // }, // { -// "ref":58, // This is an controllable outlet +// "ref":58, // This is an controllable outlet // "type":"Outlet" +// }, +// { +// "ref":111, +// "type":"TemperatureSensor", // Required for a temperature sensor +// "temperatureUnit":"F", // Optional - C is the default +// "name":"Bedroom temp" // Optional - HomeSeer device name is the default +// }, +// { +// "ref":113, // Required - HomeSeer Device Reference of the Current Temperature Device +// "type":"Thermostat", // Required for a Thermostat +// "name":"Température Salon", // Optional - HomeSeer device name is the default +// "temperatureUnit":"C", // Optional - F for Fahrenheit, C for Celsius, C is the default +// "setPointRef":167, // Required - HomeSeer device reference for your thermostat Set Point. +// "setPointReadOnly":true, // Optional - Set to false if your SetPoint is read/write. true is the default +// "stateRef":166, // Required - HomeSeer device reference for your thermostat current state +// "stateOffValues":[0,4,5], // Required - List of the HomeSeer device values for a HomeKit state=OFF +// "stateHeatValues":[1], // Required - List of the HomeSeer device values for a HomeKit state=HEAT +// "stateCoolValues":[2], // Required - List of the HomeSeer device values for a HomeKit state=COOL +// "stateAutoValues":[3], // Required - List of the HomeSeer device values for a HomeKit state=AUTO +// "controlRef":168, // Required - HomeSeer device reference for your thermostat mode control (It can be the same as stateRef for some thermostats) +// "controlOffValue":0, // Required - Value for OFF +// "controlHeatValue":1, // Required - Value for HEAT +// "controlCoolValue":2, // Required - Value for COOL +// "controlAutoValue":3, // Required - Value for AUTO +// "coolingThresholdRef":169, // Optional - Not-implemented-yet - HomeSeer device reference for your thermostat cooling threshold +// "heatingThresholdRef":170 // Optional - Not-implemented-yet - HomeSeer device reference for your thermostat heating threshold // } // ] // } @@ -43,7 +76,8 @@ // - Fan (onValue, offValue options) // - Switch (onValue, offValue options) // - Outlet (onValue, offValue options) -// - TemperatureSensor +// - TemperatureSensor (temperatureUnit=C|F) +// - Thermostat (temperatureUnit, setPoint, state, control options) // - ContactSensor // - MotionSensor // - LeakSensor @@ -89,6 +123,7 @@ HomeSeerPlatform.prototype = { var that = this; var foundAccessories = []; var url = this.config["host"] + "/JSON?request=getstatus&ref=" + refList; + httpRequest( url, "GET", function(error, response, body) { if (error) { this.log('HomeSeer status function failed: %s', error.message); @@ -115,8 +150,9 @@ function HomeSeerAccessory(log, platformConfig, status ) { this.onValue = "100"; this.offValue = "0"; - this.control_url = platformConfig["host"] + "/JSON?request=controldevicebyvalue&ref=" + this.ref + "&value="; - this.status_url = platformConfig["host"] + "/JSON?request=getstatus&ref=" + this.ref; + this.access_url = platformConfig["host"] + "/JSON?"; + this.control_url = this.access_url + "request=controldevicebyvalue&ref=" + this.ref + "&value="; + this.status_url = this.access_url + "request=getstatus&ref=" + this.ref; for( var i=0; i Date: Mon, 12 Oct 2015 02:11:24 +0200 Subject: [PATCH 40/69] Update HomeSeer.js --- platforms/HomeSeer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/HomeSeer.js b/platforms/HomeSeer.js index 8cb371a..e691b68 100644 --- a/platforms/HomeSeer.js +++ b/platforms/HomeSeer.js @@ -505,7 +505,7 @@ HomeSeerAccessory.prototype = { } case "OccupancySensor": { var occupancySensorService = new Service.OccupancySensor(); - motionSensorService + occupancySensorService .getCharacteristic(Characteristic.OccupancyDetected) .on('get', this.getPowerState.bind(this)); services.push( occupancySensorService ); From c59b87d17d3fb032ce087ca7cb7119e46c4b5ce0 Mon Sep 17 00:00:00 2001 From: stipus Date: Mon, 12 Oct 2015 02:15:41 +0200 Subject: [PATCH 41/69] Update HomeSeer.js --- platforms/HomeSeer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/HomeSeer.js b/platforms/HomeSeer.js index e691b68..e3c289b 100644 --- a/platforms/HomeSeer.js +++ b/platforms/HomeSeer.js @@ -9,7 +9,7 @@ // V0.3 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/11 // - Added TemperatureUnit=F|C option to temperature sensors // - Added negative temperature support to temperature sensors -// V0.4 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/11 +// V0.4 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/12 // - Added thermostat support // // From d4279c5ef5be6880c093dee570ab51e999ae35f7 Mon Sep 17 00:00:00 2001 From: iRaven Date: Mon, 12 Oct 2015 18:12:14 +0200 Subject: [PATCH 42/69] Added Support for Thermostat HM-CC-RT-DN --- accessories/HomeMaticThermo | 264 ++++++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 accessories/HomeMaticThermo diff --git a/accessories/HomeMaticThermo b/accessories/HomeMaticThermo new file mode 100644 index 0000000..f3d300f --- /dev/null +++ b/accessories/HomeMaticThermo @@ -0,0 +1,264 @@ +var types = require("HAP-NodeJS/accessories/types.js"); +var request = require("request"); + +function HomeMaticThermo(log, config) { + this.log = log; + this.name = config["name"]; + this.ccuIDTargetTemp = config["ccu_id_TargetTemp"]; + this.ccuIDCurrentTemp = config["ccu_id_CurrentTemp"]; + this.ccuIDControlMode = config["ccu_id_ControlMode"]; + this.ccuIDManuMode = config["ccu_id_ManuMode"]; + this.ccuIDAutoMode = config["ccu_id_AutoMode"]; + this.ccuIP = config["ccu_ip"]; +} + +HomeMaticThermo.prototype = { + + setTargetTemperature: function(value) { + + var that = this; + + this.log("Setting target Temperature of CCU to " + value); + this.log(this.ccuIDTargetTemp + " " + value); + + request.put({ + url: "http://"+this.ccuIP+"/config/xmlapi/statechange.cgi?ise_id="+this.ccuIDTargetTemp+"&new_value="+ value, + }, function(err, response, body) { + + if (!err && response.statusCode == 200) { + that.log("State change complete."); + } + else { + that.log("Error '"+err+"' setting Temperature: " + body); + } + }); + }, + getCurrentTemperature: function(callback) { + + var that = this; + + this.log("Getting current Temperature of CCU"); + request.get({ + url: "http://"+this.ccuIP+"/config/xmlapi/state.cgi?datapoint_id="+this.ccuIDCurrentTemp, + }, function(err, response, body) { + + if (!err && response.statusCode == 200) { + + //that.log("Response:"+response.body); + var responseString = response.body.substring(83,87); + //that.log(responseString); + callback(parseFloat(responseString)); + //that.log("Getting current temperature complete."); + } + else { + that.log("Error '"+err+"' getting Temperature: " + body); + } + }); + }, + getTargetTemperature: function(callback) { + + var that = this; + +this.log("Getting target Temperature of CCU"); + request.get({ + url: "http://"+this.ccuIP+"/config/xmlapi/state.cgi?datapoint_id="+this.ccuIDTargetTemp, + }, function(err, response, body) { + + if (!err && response.statusCode == 200) { + + //that.log("Response:"+response.body); + var responseString = response.body.substring(83,87); + //that.log(responseString); + callback(parseFloat(responseString)); + //that.log("Getting target temperature complete."); + } + else { + that.log("Error '"+err+"' getting Temperature: " + body); + } + }); + }, + getMode: function(callback) { + + var that = this; + + //this.log("Getting target Mode of CCU"); + //this.log(this.ccuID+ value); + + request.get({ + url: "http://"+this.ccuIP+"/config/xmlapi/state.cgi?datapoint_id="+this.ccuIDControlMode, + }, function(err, response, body) { + + if (!err && response.statusCode == 200) { + + //that.log("Response:"+response.body); + var responseInt = response.body.substring(83,84); + //that.log(responseString); + if (responseInt == 1) + { callback(parseInt("0")); } + if (responseInt == 0) + { callback(parseInt("1")); } + //that.log("Getting mode complete."); + } + else { + that.log("Error '"+err+"' getting Mode: " + body); + } + }); + }, + setMode: function(value) { + + var that = this; + + //this.log("Seting target Mode of CCU:" + value); + var modvalue; + var dpID; + switch(value) { + case 3: {modvalue = "true";dpID=this.ccuIDAutoMode;break;} //auto + case 1: {modvalue = "true";dpID=this.ccuIDAutoMode;break;} //heating => auto + default: {modvalue = "1";dpID=this.ccuIDManuMode;} //default => off (manual) + } + + request.put({ + url: "http://"+this.ccuIP+"/config/xmlapi/statechange.cgi?ise_id="+dpID+"&new_value="+ modvalue, + }, function(err, response, body) { + + if (!err && response.statusCode == 200) { + //that.log("Setting Mode complete."); + } + else { + that.log("Error '"+err+"' setting Mode: " + body); + } + }); + }, + 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: "test", + supportEvents: false, + supportBonjour: false, + manfDescription: "Manufacturer", + designedMaxLength: 255 + },{ + cType: types.MODEL_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: "test", + supportEvents: false, + supportBonjour: false, + manfDescription: "Model", + designedMaxLength: 255 + },{ + cType: types.SERIAL_NUMBER_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: "A1S2NREF88EW", + 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.THERMOSTAT_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.CURRENTHEATINGCOOLING_CTYPE, + onRead: function(callback) { that.getMode(callback); }, + perms: ["pr","ev"], + format: "int", + initialValue: 0, + supportEvents: false, + supportBonjour: false, + manfDescription: "Current Mode", + designedMaxLength: 1, + designedMinValue: 0, + designedMaxValue: 2, + designedMinStep: 1, + },{ + cType: types.TARGETHEATINGCOOLING_CTYPE, + onRead: function(callback) { that.getMode(callback); }, + onUpdate: function(value) { that.setMode(value);}, + perms: ["pw","pr","ev"], + format: "int", + initialValue: 0, + supportEvents: false, + supportBonjour: false, + manfDescription: "Target Mode", + designedMinValue: 0, + designedMaxValue: 3, + designedMinStep: 1, + },{ + cType: types.CURRENT_TEMPERATURE_CTYPE, + onRead: function(callback) { that.getCurrentTemperature(callback); }, + onUpdate: null, + perms: ["pr","ev"], + format: "float", + initialValue: 13.0, + supportEvents: false, + supportBonjour: false, + manfDescription: "Current Temperature", + unit: "celsius" + },{ + cType: types.TARGET_TEMPERATURE_CTYPE, + onUpdate: function(value) { that.setTargetTemperature(value); }, + onRead: function(callback) { that.getTargetTemperature(callback); }, + perms: ["pw","pr","ev"], + format: "float", + initialValue: 19.0, + supportEvents: false, + supportBonjour: false, + manfDescription: "Target Temperature", + designedMinValue: 4, + designedMaxValue: 25, + designedMinStep: 0.1, + unit: "celsius" + },{ + cType: types.TEMPERATURE_UNITS_CTYPE, + onUpdate: null, + perms: ["pr","ev"], + format: "int", + initialValue: 0, + supportEvents: false, + supportBonjour: false, + manfDescription: "Unit" + }] + }]; + } +}; + +module.exports.accessory = HomeMaticThermo; From 1f8db79324ad2935d143a43b9d4f23a786c89a9a Mon Sep 17 00:00:00 2001 From: stipus Date: Tue, 13 Oct 2015 00:53:19 +0200 Subject: [PATCH 43/69] Added support for humidity sensors, battery level sensors, low battery flag for all sensors, and ability to run HomeSeer events - Added Humidity sensor support - Added Battery support - Added low battery support for all sensors - Added HomeSeer event support (using HomeKit switches...) --- platforms/HomeSeer.js | 214 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 193 insertions(+), 21 deletions(-) diff --git a/platforms/HomeSeer.js b/platforms/HomeSeer.js index e3c289b..e0c5c79 100644 --- a/platforms/HomeSeer.js +++ b/platforms/HomeSeer.js @@ -11,6 +11,12 @@ // - Added negative temperature support to temperature sensors // V0.4 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/12 // - Added thermostat support +// V0.5 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/12 +// - Added Humidity sensor support +// V0.6 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/12 +// - Added Battery support +// - Added low battery support for all sensors +// - Added HomeSeer event support (using HomeKit switches...) // // // Remember to add platform to config.json. @@ -24,7 +30,16 @@ // "platform": "HomeSeer", // Required // "name": "HomeSeer", // Required // "host": "http://192.168.3.4:81", // Required - If you did setup HomeSeer authentication, use "http://user:password@ip_address:port" -// "accessories":[ +// +// "events":[ // Optional - List of Events - Currently they are imported into HomeKit as switches +// { +// "eventGroup":"My Group", // Required - The HomeSeer event group +// "eventName":"My Event", // Required - The HomeSeer event name +// "name":"Test" // Optional - HomeSeer event name is the default +// } +// ], +// +// "accessories":[ // Required - List of Accessories // { // "ref":8, // Required - HomeSeer Device Reference (To get it, select the HS Device - then Advanced Tab) // "type":"Lightbulb", // Optional - Lightbulb is the default @@ -37,14 +52,16 @@ // "ref":9 // This is a dimmable Lightbulb by default // }, // { -// "ref":58, // This is an controllable outlet +// "ref":58, // This is a controllable outlet // "type":"Outlet" // }, // { -// "ref":111, +// "ref":111, // Required - HomeSeer Device Reference for your sensor // "type":"TemperatureSensor", // Required for a temperature sensor // "temperatureUnit":"F", // Optional - C is the default -// "name":"Bedroom temp" // Optional - HomeSeer device name is the default +// "name":"Bedroom temp", // Optional - HomeSeer device name is the default +// "batteryRef":112, // Optional - HomeSeer device reference for the sensor battery level +// "batteryThreshold":15 // Optional - If sensor battery level is below this value, the HomeKit LowBattery characteristic is set to 1. Default is 10 // }, // { // "ref":113, // Required - HomeSeer Device Reference of the Current Temperature Device @@ -65,6 +82,12 @@ // "controlAutoValue":3, // Required - Value for AUTO // "coolingThresholdRef":169, // Optional - Not-implemented-yet - HomeSeer device reference for your thermostat cooling threshold // "heatingThresholdRef":170 // Optional - Not-implemented-yet - HomeSeer device reference for your thermostat heating threshold +// }, +// { +// "ref":115, // Required - HomeSeer Device Reference for a device holding battery level (0-100) +// "type":"Battery", // Required for a Battery +// "name":"Roomba battery", // Optional - HomeSeer device name is the default +// "batteryThreshold":15 // Optional - If the level is below this value, the HomeKit LowBattery characteristic is set to 1. Default is 10 // } // ] // } @@ -72,18 +95,20 @@ // // // SUPORTED TYPES: -// - Lightbulb (can_dim, onValue, offValue options) -// - Fan (onValue, offValue options) -// - Switch (onValue, offValue options) -// - Outlet (onValue, offValue options) +// - Lightbulb (can_dim, onValue, offValue options) +// - Fan (onValue, offValue options) +// - Switch (onValue, offValue options) +// - Outlet (onValue, offValue options) +// - Thermostat (temperatureUnit, setPoint, state, control options) // - TemperatureSensor (temperatureUnit=C|F) -// - Thermostat (temperatureUnit, setPoint, state, control options) -// - ContactSensor -// - MotionSensor -// - LeakSensor -// - LightSensor -// - OccupancySensor -// - SmokeSensor +// - ContactSensor (0=no contact, 1=contact - batteryRef, batteryThreshold option) +// - MotionSensor (0=no motion, 1=motion - batteryRef, batteryThreshold option) +// - LeakSensor (0=no leak, 1=leak - batteryRef, batteryThreshold option) +// - LightSensor (HomeSeer device value in Lux - batteryRef, batteryThreshold option) +// - HumiditySensor (HomeSeer device value in % - batteryRef, batteryThreshold option) +// - OccupancySensor (0=no occupancy, 1=occupancy - batteryRef, batteryThreshold option) +// - SmokeSensor (0=no smoke, 1=smoke - batteryRef, batteryThreshold option) +// - Battery (batteryThreshold option) // - Door @@ -111,19 +136,25 @@ function HomeSeerPlatform(log, config){ HomeSeerPlatform.prototype = { accessories: function(callback) { - this.log("Fetching HomeSeer devices."); + var that = this; + var foundAccessories = []; + if( this.config.events ) { + this.log("Creating HomeSeer events."); + for( var i=0; i Date: Tue, 13 Oct 2015 05:37:44 +0200 Subject: [PATCH 44/69] MotionDetector added, with new needed configuration tags. --- platforms/ZWayServer.js | 70 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 6 deletions(-) diff --git a/platforms/ZWayServer.js b/platforms/ZWayServer.js index 0b81e88..92938a3 100644 --- a/platforms/ZWayServer.js +++ b/platforms/ZWayServer.js @@ -131,8 +131,11 @@ ZWayServerPlatform.prototype = { gdid = vdev.id.replace(/^(.*?)_zway_(\d+-\d+)-\d.*/, '$1_$2'); } - var gd = groupedDevices[gdid] || (groupedDevices[gdid] = {devices: [], types: {}, extras: {}, primary: undefined}); + var gd = groupedDevices[gdid] || (groupedDevices[gdid] = { devices: [], types: {}, extras: {}, primary: undefined, cxmap: {} }); + gd.devices.push(vdev); + var vdevIndex = gd.devices.length - 1; + var tk = ZWayServerPlatform.getVDevTypeKey(vdev); // If this is explicitly set as primary, set it now... @@ -143,17 +146,24 @@ ZWayServerPlatform.prototype = { gd.extras[tk].push(gd.types[tk]); delete gd.types[tk]; // clear the way for this one to be set here below... } - gd.primary = gd.devices.length - 1; + gd.primary = vdevIndex; //gd.types[tk] = gd.primary; } if(gd.types[tk] === undefined){ - gd.types[tk] = gd.devices.length - 1; + gd.types[tk] = vdevIndex; } else { gd.extras[tk] = gd.extras[tk] || []; - gd.extras[tk].push(gd.devices.length - 1); + gd.extras[tk].push(vdevIndex); + } + if(tk !== vdev.deviceType) gd.types[vdev.deviceType] = vdevIndex; // also include the deviceType only as a possibility + + // Create a map entry when Homebridge.Characteristic.Type is set... + var ctype = this.getTagValue(vdev, "Characteristic.Type"); + if(ctype && Characteristic[ctype]){ + var cx = new Characteristic[ctype](); + gd.cxmap[cx.UUID] = vdevIndex; } - if(tk !== vdev.deviceType) gd.types[vdev.deviceType] = gd.devices.length - 1; // also include the deviceType only as a possibility } for(var gdid in groupedDevices) { @@ -357,6 +367,11 @@ ZWayServerAccessory.prototype = { case "sensorMultilevel.Luminiscence": services.push(new Service.LightSensor(vdev.metrics.title, vdev.id)); break; + case "sensorBinary": + var stype = this.platform.getTagValue(vdev, "Service.Type"); + if(stype === "MotionSensor"){ + services.push(new Service.MotionSensor(vdev.metrics.title, vdev.id)); + } } var validServices =[]; @@ -377,6 +392,12 @@ ZWayServerAccessory.prototype = { } , getVDevForCharacteristic: function(cx, vdevPreferred){ + + // If we know which vdev should be used for this Characteristic, we're done! + if(this.devDesc.cxmap[cx.UUID] !== undefined){ + return this.devDesc.devices[this.devDesc.cxmap[cx.UUID]]; + } + var map = this.uuidToTypeKeyMap; if(!map){ this.uuidToTypeKeyMap = map = {}; @@ -399,7 +420,7 @@ ZWayServerAccessory.prototype = { } if(cx instanceof Characteristic.Name) return vdevPreferred; - + // Special case!: If cx is a CurrentTemperature, ignore the preferred device...we want the sensor if available! if(cx instanceof Characteristic.CurrentTemperature) vdevPreferred = null; // @@ -768,6 +789,43 @@ ZWayServerAccessory.prototype = { }); return cx; } + + if(cx instanceof Characteristic.MotionDetected){ + cx.zway_getValueFromVDev = function(vdev){ + return vdev.metrics.level === "off" ? false : true; + }; + cx.value = cx.zway_getValueFromVDev(vdev); + cx.on('get', function(callback, context){ + debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"..."); + this.getVDev(vdev).then(function(result){ + debug("Got value: " + cx.zway_getValueFromVDev(result.data) + ", for " + vdev.metrics.title + "."); + callback(false, cx.zway_getValueFromVDev(result.data)); + }); + }.bind(this)); + cx.on('change', function(ev){ + debug("Device " + vdev.metrics.title + ", characteristic " + cx.displayName + " changed from " + ev.oldValue + " to " + ev.newValue); + }); + return cx; + } + + if(cx instanceof Characteristic.StatusTampered){ + cx.zway_getValueFromVDev = function(vdev){ + return vdev.metrics.level === "off" ? Characteristic.StatusTampered.NOT_TAMPERED : Characteristic.StatusTampered.TAMPERED; + }; + cx.value = cx.zway_getValueFromVDev(vdev); + cx.on('get', function(callback, context){ + debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"..."); + this.getVDev(vdev).then(function(result){ + debug("Got value: " + cx.zway_getValueFromVDev(result.data) + ", for " + vdev.metrics.title + "."); + callback(false, cx.zway_getValueFromVDev(result.data)); + }); + }.bind(this)); + cx.on('change', function(ev){ + debug("Device " + vdev.metrics.title + ", characteristic " + cx.displayName + " changed from " + ev.oldValue + " to " + ev.newValue); + }); + return cx; + } + } , configureService: function(service, vdev){ From fdbd33f29dc97476745770a36af2d6481c084e1e Mon Sep 17 00:00:00 2001 From: S'pht'Kr Date: Tue, 13 Oct 2015 06:50:23 +0200 Subject: [PATCH 45/69] Updated for new tweaked "setProps" API, removed cruft. --- platforms/ZWayServer.js | 43 +++++++++++++++-------------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/platforms/ZWayServer.js b/platforms/ZWayServer.js index 92938a3..e50336e 100644 --- a/platforms/ZWayServer.js +++ b/platforms/ZWayServer.js @@ -459,7 +459,6 @@ ZWayServerAccessory.prototype = { debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"..."); callback(false, accessory.name); }); - cx.writable = false; return cx; } @@ -546,12 +545,6 @@ ZWayServerAccessory.prototype = { }); }.bind(this)); - cx.writeable = false; - //cx.on('set', function(level, callback){ - // this.command(vdev, "exact", {level: "on", "color.r": 255, "color.g": 0, "color.b": 0}).then(function(result){ - // callback(); - // }); - //}.bind(this)); return cx; } @@ -581,12 +574,6 @@ ZWayServerAccessory.prototype = { }); }.bind(this)); - cx.writeable = false; - //cx.on('set', function(level, callback){ - // this.command(vdev, "exact", {level: "on", "color.r": 255, "color.g": 0, "color.b": 0}).then(function(result){ - // callback(); - // }); - //}.bind(this)); return cx; } @@ -602,8 +589,10 @@ ZWayServerAccessory.prototype = { callback(false, cx.zway_getValueFromVDev(result.data)); }); }.bind(this)); - cx.minimumValue = vdev.metrics && vdev.metrics.min !== undefined ? vdev.metrics.min : -40; - cx.maximumValue = vdev.metrics && vdev.metrics.max !== undefined ? vdev.metrics.max : 999; + cx.setProps({ + minValue: vdev.metrics && vdev.metrics.min !== undefined ? vdev.metrics.min : -40, + maxValue: vdev.metrics && vdev.metrics.max !== undefined ? vdev.metrics.max : 999 + }); return cx; } @@ -625,8 +614,10 @@ ZWayServerAccessory.prototype = { callback(); }); }.bind(this)); - cx.minimumValue = vdev.metrics && vdev.metrics.min !== undefined ? vdev.metrics.min : 5; - cx.maximumValue = vdev.metrics && vdev.metrics.max !== undefined ? vdev.metrics.max : 40; + cx.setProps({ + minValue: vdev.metrics && vdev.metrics.min !== undefined ? vdev.metrics.min : 5, + maxValue: vdev.metrics && vdev.metrics.max !== undefined ? vdev.metrics.max : 40 + }); return cx; } @@ -640,7 +631,9 @@ ZWayServerAccessory.prototype = { debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"..."); callback(false, Characteristic.TemperatureDisplayUnits.CELSIUS); }); - cx.writable = false; + cx.setProps({ + perms: [Characteristic.Perms.READ] + }); return cx; } @@ -668,7 +661,6 @@ ZWayServerAccessory.prototype = { callback(false, Characteristic.TargetHeatingCoolingState.HEAT); }); // Hmm... apparently if this is not setable, we can't add a thermostat change to a scene. So, make it writable but a no-op. - cx.writable = true; cx.on('set', function(newValue, callback){ debug("WARN: Set of TargetHeatingCoolingState not yet implemented, resetting to HEAT!") callback(undefined, Characteristic.TargetHeatingCoolingState.HEAT); @@ -703,8 +695,9 @@ ZWayServerAccessory.prototype = { debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"..."); callback(false, Characteristic.TargetDoorState.CLOSED); }); - //cx.readable = false; - cx.writable = false; + cx.setProps({ + perms: [Characteristic.Perms.READ] + }); } if(cx instanceof Characteristic.ObstructionDetected){ @@ -717,8 +710,6 @@ ZWayServerAccessory.prototype = { debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"..."); callback(false, false); }); - //cx.readable = false; - cx.writable = false; } if(cx instanceof Characteristic.BatteryLevel){ @@ -759,8 +750,6 @@ ZWayServerAccessory.prototype = { debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"..."); callback(false, Characteristic.ChargingState.NOT_CHARGING); }); - //cx.readable = false; - cx.writable = false; } if(cx instanceof Characteristic.CurrentAmbientLightLevel){ @@ -769,8 +758,8 @@ ZWayServerAccessory.prototype = { // Completely unscientific guess, based on test-fit data and Wikipedia real-world lux values. // This will probably change! var lux = 0.0005 * (vdev.metrics.level^3.6); - if(lux < cx.minimumValue) return cx.minimumValue; - if(lux > cx.maximumValue) return cx.maximumValue; + // Bounds checking now done upstream! + //if(lux < cx.minimumValue) return cx.minimumValue; if(lux > cx.maximumValue) return cx.maximumValue; return lux; } else { return vdev.metrics.level; From 7c7ceb64534eeaf875ddaf18b685aa2053e19e21 Mon Sep 17 00:00:00 2001 From: iRaven Date: Tue, 13 Oct 2015 13:06:11 +0200 Subject: [PATCH 46/69] Rename HomeMaticThermo to HomeMaticThermo.js --- accessories/{HomeMaticThermo => HomeMaticThermo.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename accessories/{HomeMaticThermo => HomeMaticThermo.js} (100%) diff --git a/accessories/HomeMaticThermo b/accessories/HomeMaticThermo.js similarity index 100% rename from accessories/HomeMaticThermo rename to accessories/HomeMaticThermo.js From d8a35963266e6a2db4ff271cd57c8caa22da098b Mon Sep 17 00:00:00 2001 From: iRaven Date: Tue, 13 Oct 2015 17:39:28 +0200 Subject: [PATCH 47/69] Added HomematicThermo, HomematicWindow --- config-sample.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/config-sample.json b/config-sample.json index 7af74db..1e9d872 100644 --- a/config-sample.json +++ b/config-sample.json @@ -149,6 +149,24 @@ "ccu_id": "The XMP-API id of your HomeMatic device", "ccu_ip": "The IP-Adress of your HomeMatic CCU device" }, + { + "accessory": "HomeMaticWindow", + "name": "Contact", + "description": "Control HomeMatic devices (The XMP-API addon for the CCU is required)", + "ccu_id": "The XMP-API id of your HomeMatic device (type HM-Sec-RHS)", + "ccu_ip": "The IP-Adress of your HomeMatic CCU device" + }, + { + "accessory": "HomeMaticThermo", + "name": "Contact", + "description": "Control HomeMatic devices (The XMP-API addon for the CCU is required)", + "ccu_id_TargetTemp": "The XMP-API id of your HomeMatic device (type HM-CC-RT-DN )", + "ccu_id_CurrentTemp": "The XMP-API id of your HomeMatic device (type HM-CC-RT-DN )", + "ccu_id_ControlMode": "The XMP-API id of your HomeMatic device (type HM-CC-RT-DN )", + "ccu_id_ManuMode": "The XMP-API id of your HomeMatic device (type HM-CC-RT-DN )", + "ccu_id_AutoMode": "The XMP-API id of your HomeMatic device (type HM-CC-RT-DN )", + "ccu_ip": "The IP-Adress of your HomeMatic CCU device" + }, { "accessory": "X10", "name": "Lamp", From a056b16c35e0e6c6dfc91bbd73fca2ae01f7009a Mon Sep 17 00:00:00 2001 From: stipus Date: Wed, 14 Oct 2015 22:06:11 +0200 Subject: [PATCH 48/69] Added CarbonMonoxide and CarbonDioxide support - Added CarbonMonoxide and CarbonDioxide sensor support - Added onValues option to all binary sensors - Added uuid_base parameter to all HomeSeer accessories --- platforms/HomeSeer.js | 212 +++++++++++++++++++++++++++++------------- 1 file changed, 149 insertions(+), 63 deletions(-) diff --git a/platforms/HomeSeer.js b/platforms/HomeSeer.js index e0c5c79..c4ccde7 100644 --- a/platforms/HomeSeer.js +++ b/platforms/HomeSeer.js @@ -17,6 +17,13 @@ // - Added Battery support // - Added low battery support for all sensors // - Added HomeSeer event support (using HomeKit switches...) +// V0.7 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/13 +// - You can add multiple HomeKit devices for the same HomeSeer device reference +// - Added CarbonMonoxide sensor +// - Added CarbonDioxide sensor +// - Added onValues option to all binary sensors +// V0.8 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/14 +// - Added uuid_base parameter to all accessories // // // Remember to add platform to config.json. @@ -35,7 +42,8 @@ // { // "eventGroup":"My Group", // Required - The HomeSeer event group // "eventName":"My Event", // Required - The HomeSeer event name -// "name":"Test" // Optional - HomeSeer event name is the default +// "name":"Test", // Optional - HomeSeer event name is the default +// "uuid_base":"SomeUniqueId" // Optional - HomeKit identifier will be derived from this parameter instead of the name // } // ], // @@ -46,7 +54,8 @@ // "name":"My Light", // Optional - HomeSeer device name is the default // "offValue":"0", // Optional - 0 is the default // "onValue":"100", // Optional - 100 is the default -// "can_dim":true // Optional - true is the default - false for a non dimmable lightbulb +// "can_dim":true, // Optional - true is the default - false for a non dimmable lightbulb +// "uuid_base":"SomeUniqueId2" // Optional - HomeKit identifier will be derived from this parameter instead of the name // }, // { // "ref":9 // This is a dimmable Lightbulb by default @@ -64,6 +73,22 @@ // "batteryThreshold":15 // Optional - If sensor battery level is below this value, the HomeKit LowBattery characteristic is set to 1. Default is 10 // }, // { +// "ref":34, // Required - HomeSeer Device Reference for your sensor +// "type":"SmokeSensor", // Required for a smoke sensor +// "name":"Kichen smoke detector", // Optional - HomeSeer device name is the default +// "batteryRef":35, // Optional - HomeSeer device reference for the sensor battery level +// "batteryThreshold":15, // Optional - If sensor battery level is below this value, the HomeKit LowBattery characteristic is set to 1. Default is 10 +// "onValues":[1,1.255] // Optional - List of all HomeSeer values triggering a "ON" sensor state - Default is any value different than 0 +// }, +// { +// "ref":34, // Required - HomeSeer Device Reference for your sensor (Here it's the same device as the SmokeSensor above) +// "type":"CarbonMonoxideSensor", // Required for a carbon monoxide sensor +// "name":"Kichen CO detector", // Optional - HomeSeer device name is the default +// "batteryRef":35, // Optional - HomeSeer device reference for the sensor battery level +// "batteryThreshold":15, // Optional - If sensor battery level is below this value, the HomeKit LowBattery characteristic is set to 1. Default is 10 +// "onValues":[2,2.255] // Optional - List of all HomeSeer values triggering a "ON" sensor state - Default is any value different than 0 +// }, +// { // "ref":113, // Required - HomeSeer Device Reference of the Current Temperature Device // "type":"Thermostat", // Required for a Thermostat // "name":"Température Salon", // Optional - HomeSeer device name is the default @@ -76,10 +101,10 @@ // "stateCoolValues":[2], // Required - List of the HomeSeer device values for a HomeKit state=COOL // "stateAutoValues":[3], // Required - List of the HomeSeer device values for a HomeKit state=AUTO // "controlRef":168, // Required - HomeSeer device reference for your thermostat mode control (It can be the same as stateRef for some thermostats) -// "controlOffValue":0, // Required - Value for OFF -// "controlHeatValue":1, // Required - Value for HEAT -// "controlCoolValue":2, // Required - Value for COOL -// "controlAutoValue":3, // Required - Value for AUTO +// "controlOffValue":0, // Required - HomeSeer device control value for OFF +// "controlHeatValue":1, // Required - HomeSeer device control value for HEAT +// "controlCoolValue":2, // Required - HomeSeer device control value for COOL +// "controlAutoValue":3, // Required - HomeSeer device control value for AUTO // "coolingThresholdRef":169, // Optional - Not-implemented-yet - HomeSeer device reference for your thermostat cooling threshold // "heatingThresholdRef":170 // Optional - Not-implemented-yet - HomeSeer device reference for your thermostat heating threshold // }, @@ -95,20 +120,22 @@ // // // SUPORTED TYPES: -// - Lightbulb (can_dim, onValue, offValue options) -// - Fan (onValue, offValue options) -// - Switch (onValue, offValue options) -// - Outlet (onValue, offValue options) -// - Thermostat (temperatureUnit, setPoint, state, control options) -// - TemperatureSensor (temperatureUnit=C|F) -// - ContactSensor (0=no contact, 1=contact - batteryRef, batteryThreshold option) -// - MotionSensor (0=no motion, 1=motion - batteryRef, batteryThreshold option) -// - LeakSensor (0=no leak, 1=leak - batteryRef, batteryThreshold option) -// - LightSensor (HomeSeer device value in Lux - batteryRef, batteryThreshold option) -// - HumiditySensor (HomeSeer device value in % - batteryRef, batteryThreshold option) -// - OccupancySensor (0=no occupancy, 1=occupancy - batteryRef, batteryThreshold option) -// - SmokeSensor (0=no smoke, 1=smoke - batteryRef, batteryThreshold option) -// - Battery (batteryThreshold option) +// - Lightbulb (can_dim, onValue, offValue options) +// - Fan (onValue, offValue options) +// - Switch (onValue, offValue options) +// - Outlet (onValue, offValue options) +// - Thermostat (temperatureUnit, setPoint, state, control options) +// - TemperatureSensor (temperatureUnit=C|F) +// - HumiditySensor (HomeSeer device value in % - batteryRef, batteryThreshold options) +// - LightSensor (HomeSeer device value in Lux - batteryRef, batteryThreshold options) +// - ContactSensor (onValues, batteryRef, batteryThreshold options) +// - MotionSensor (onValues, batteryRef, batteryThreshold options) +// - LeakSensor (onValues, batteryRef, batteryThreshold options) +// - OccupancySensor (onValues, batteryRef, batteryThreshold options) +// - SmokeSensor (onValues, batteryRef, batteryThreshold options) +// - CarbonMonoxideSensor (onValues, batteryRef, batteryThreshold options) +// - CarbonDioxideSensor (onValues, batteryRef, batteryThreshold options) +// - Battery (batteryThreshold option) // - Door @@ -163,9 +190,14 @@ HomeSeerPlatform.prototype = { else { this.log('HomeSeer status function succeeded!'); var response = JSON.parse( body ); - for( var i=0; i Date: Thu, 15 Oct 2015 14:25:20 -0700 Subject: [PATCH 49/69] Initial support for Indigo server (http://www.indigodomo.com) --- platforms/Indigo.js | 502 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 502 insertions(+) create mode 100644 platforms/Indigo.js diff --git a/platforms/Indigo.js b/platforms/Indigo.js new file mode 100644 index 0000000..e42849a --- /dev/null +++ b/platforms/Indigo.js @@ -0,0 +1,502 @@ +var types = require("HAP-NodeJS/accessories/types.js"); +var Characteristic = require("HAP-NodeJS").Characteristic; +var request = require('request'); +var async = require('async'); + + +function IndigoPlatform(log, config) { + this.log = log; + + this.baseURL = "http://" + config["host"] + ":" + config["port"]; + + if (config["username"] && config["password"]) { + this.auth = { + 'user': config["username"], + 'pass': config["password"], + 'sendImmediately': false + }; + } +} + +IndigoPlatform.prototype = { + accessories: function(callback) { + var that = this; + this.log("Discovering Indigo Devices."); + + var options = { + url: this.baseURL + "/devices.json/", + method: 'GET' + }; + if (this.auth) { + options['auth'] = this.auth; + } + this.foundAccessories = []; + this.callback = callback; + + request(options, function(error, response, body) { + if (error) { + console.trace("Requesting Indigo devices."); + that.log(error); + return error; + } + + // Cheesy hack because response may have an extra comma at the start of the array, which is invalid + var firstComma = body.indexOf(","); + if (firstComma < 10) { + body = "[" + body.substr(firstComma + 1); + } + + var json = JSON.parse(body); + async.each(json, function(item, asyncCallback) { + var deviceURL = that.baseURL + item.restURL; + var deviceOptions = { + url: deviceURL, + method: 'GET' + }; + if (that.auth) { + deviceOptions['auth'] = that.auth; + } + + request(deviceOptions, function(deviceError, deviceResponse, deviceBody) { + if (deviceError) { + asyncCallback(deviceError); + } + + var deviceJson = JSON.parse(deviceBody); + that.log("Discovered " + deviceJson.type + ": " + deviceJson.name); + that.foundAccessories.push( + new IndigoAccessory(that.log, that.auth, deviceURL, deviceJson)); + asyncCallback(); + }); + }, function(asyncError) { + // This will be called after all the requests complete + if (asyncError) { + console.trace("Requesting Indigo device info."); + that.log(asyncError); + } else { + that.callback(that.foundAccessories.sort(function (a,b) { + return (a.name > b.name) - (a.name < b.name); + })); + } + }); + }); + } +} + + +function IndigoAccessory(log, auth, deviceURL, json) { + this.log = log; + this.auth = auth; + this.deviceURL = deviceURL; + + for (var prop in json) { + if (json.hasOwnProperty(prop)) { + this[prop] = json[prop]; + } + } +} + +IndigoAccessory.prototype = { + getStatus: function(callback) { + var that = this; + + var options = { + url: this.deviceURL, + method: 'GET' + }; + if (this.auth) { + options['auth'] = this.auth; + } + + request(options, function(error, response, body) { + if (error) { + console.trace("Requesting Device Status."); + that.log(error); + return error; + } + + that.log("getStatus of " + that.name + ": " + body); + callback(JSON.parse(body)); + }); + }, + + updateStatus: function(params) { + var that = this; + var options = { + url: this.deviceURL + "?" + params, + method: 'PUT' + }; + if (this.auth) { + options['auth'] = this.auth; + } + + this.log("updateStatus of " + that.name + ": " + params); + request(options, function(error, response, body) { + if (error) { + console.trace("Updating Device Status."); + that.log(error); + return error; + } + }); + }, + + query: function(prop, callback) { + this.getStatus(function(json) { + callback(json[prop]); + }); + }, + + turnOn: function() { + if (this.typeSupportsOnOff) { + this.updateStatus("isOn=1"); + } + }, + + turnOff: function() { + if (this.typeSupportsOnOff) { + this.updateStatus("isOn=0"); + } + }, + + setBrightness: function(brightness) { + if (this.typeSupportsDim && brightness >= 0 && brightness <= 100) { + this.updateStatus("brightness=" + brightness); + } + }, + + setSpeedIndex: function(speedIndex) { + if (this.typeSupportsSpeedControl && speedIndex >= 0 && speedIndex <= 3) { + this.updateStatus("speedIndex=" + speedIndex); + } + }, + + getCurrentHeatingCooling: function(callback) { + this.getStatus(function(json) { + var mode = 0; + if (json["hvacOperatonModeIsHeat"]) { + mode = 1; + } + else if (json["hvacOperationModeIsCool"]) { + mode = 2; + } + else if (json["hvacOperationModeIsAuto"]) { + mode = 3; + } + callback(mode); + }); + }, + + setTargetHeatingCooling: function(mode) { + if (mode == 0) { + param = "Off"; + } + else if (mode == 1) { + param = "Heat"; + } + else if (mode == 2) { + param = "Cool"; + } + else if (mode == 3) { + param = "Auto"; + } + + if (param) { + this.updateStatus("hvacOperationModeIs" + param + "=true"); + } + }, + + getTargetTemperature: function(callback) { + this.getStatus(function(json) { + var result; + if (json["hvacOperatonModeIsHeat"]) { + result = json["setpointHeat"]; + } + else if (json["hvacOperationModeIsCool"]) { + result = json["setpointCool"]; + } + else { + result = (json["setpointHeat"] + json["setpointCool"]) / 2; + } + callback(result); + }); + }, + + setTargetTemperature: function(temperature) { + var that = this; + this.getStatus(function(json) { + if (json["hvacOperatonModeIsHeat"]) { + that.updateStatus("setpointHeat=" + temperature); + } + else if (json["hvacOperationModeIsCool"]) { + that.updateStatus("setpointCool=" + temperature); + } + else { + var cool = temperature + 5; + var heat = temperature - 5; + that.updateStatus("setpointCool=" + cool + "&setpointHeat=" + heat); + } + }); + }, + + informationCharacteristics: function() { + return [ + { + cType: types.NAME_CTYPE, + onUpdate: null, + perms: [Characteristic.Perms.READ], + format: Characteristic.Formats.STRING, + initialValue: this.name, + supportEvents: false, + supportBonjour: false, + manfDescription: "Name of the accessory", + designedMaxLength: 255 + },{ + cType: types.MANUFACTURER_CTYPE, + onUpdate: null, + perms: [Characteristic.Perms.READ], + format: Characteristic.Formats.STRING, + initialValue: "Indigo", + supportEvents: false, + supportBonjour: false, + manfDescription: "Manufacturer", + designedMaxLength: 255 + },{ + cType: types.MODEL_CTYPE, + onUpdate: null, + perms: [Characteristic.Perms.READ], + format: Characteristic.Formats.STRING, + initialValue: this.type, + supportEvents: false, + supportBonjour: false, + manfDescription: "Model", + designedMaxLength: 255 + },{ + cType: types.SERIAL_NUMBER_CTYPE, + onUpdate: null, + perms: [Characteristic.Perms.READ], + format: Characteristic.Formats.STRING, + initialValue: this.addressStr, + supportEvents: false, + supportBonjour: false, + manfDescription: "SN", + designedMaxLength: 255 + },{ + cType: types.IDENTIFY_CTYPE, + onUpdate: null, + perms: [Characteristic.Perms.WRITE], + format: Characteristic.Formats.BOOL, + initialValue: false, + supportEvents: false, + supportBonjour: false, + manfDescription: "Identify Accessory", + designedMaxLength: 1 + } + ] + }, + + controlCharacteristics: function(that) { + var cTypes = [{ + cType: types.NAME_CTYPE, + onUpdate: null, + perms: [Characteristic.Perms.READ], + format: Characteristic.Formats.STRING, + initialValue: that.name, + supportEvents: false, + supportBonjour: false, + manfDescription: "Name of the accessory", + designedMaxLength: 255 + }]; + +/* if (that.typeSupportsOnOff) + { */ + cTypes.push({ + cType: types.POWER_STATE_CTYPE, + perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], + format: Characteristic.Formats.BOOL, + initialValue: (that.isOn) ? 1 : 0, + supportEvents: true, + supportBonjour: false, + manfDescription: "Change the power state", + designedMaxLength: 1, + onUpdate: function(value) { + if (value == 0) { + that.turnOff(); + } else { + that.turnOn(); + } + }, + onRead: function(callback) { + that.query("isOn", function(isOn) { + callback((isOn) ? 1 : 0); + }); + } + }); +// } + if (that.typeSupportsDim) + { + cTypes.push({ + cType: types.BRIGHTNESS_CTYPE, + perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], + format: Characteristic.Formats.INT, + initialValue: that.brightness, + supportEvents: true, + supportBonjour: false, + manfDescription: "Adjust Brightness of Light", + designedMinValue: 0, + designedMaxValue: 100, + designedMinStep: 1, + unit: Characteristic.Units.PERCENTAGE, + onUpdate: function(value) { + that.setBrightness(value); + }, + onRead: function(callback) { + that.query("brightness", callback); + } + }); + } + if (that.typeSupportsSpeedControl) + { + cTypes.push({ + cType: types.ROTATION_SPEED_CTYPE, + perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], + format: Characteristic.Formats.INT, + initialValue: 0, + supportEvents: true, + supportBonjour: false, + manfDescription: "Change the speed of the fan", + designedMaxLength: 1, + designedMinValue: 0, + designedMaxValue: 3, + designedMinStep: 1, + onUpdate: function(value) { + that.setSpeedIndex(value); + }, + onRead: function(callback) { + that.query("speedIndex", callback); + } + }); + } + if (that.typeSupportsHVAC) + { + cTypes.push({ + cType: types.CURRENTHEATINGCOOLING_CTYPE, + perms: [Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], + format: Characteristic.Formats.INT, + initialValue: 0, + supportEvents: true, + supportBonjour: false, + manfDescription: "Current Mode", + designedMaxLength: 1, + designedMinValue: 0, + designedMaxValue: 3, + designedMinStep: 1, + onUpdate: null, + onRead: function(callback) { + that.getCurrentHeatingCooling(callback); + } + }); + cTypes.push({ + cType: types.TARGETHEATINGCOOLING_CTYPE, + perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], + format: Characteristic.Formats.INT, + initialValue: 0, + supportEvents: true, + supportBonjour: false, + manfDescription: "Target Mode", + designedMaxLength: 1, + designedMinValue: 0, + designedMaxValue: 3, + designedMinStep: 1, + onUpdate: function(value) { + that.setTargetHeatingCooling(value); + }, + onRead: function(callback) { + that.getCurrentHeatingCooling(callback); + } + }); + cTypes.push({ + cType: types.CURRENT_TEMPERATURE_CTYPE, + perms: [Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], + format: Characteristic.Formats.INT, + designedMinValue: 0, + designedMaxValue: 110, + designedMinStep: 1, + initialValue: 0, + supportEvents: true, + supportBonjour: false, + manfDescription: "Current Temperature", + unit: Characteristic.Units.FAHRENHEIT, + onUpdate: null, + onRead: function(callback) { + that.query("displayRawState", callback); + } + }); + cTypes.push({ + cType: types.TARGET_TEMPERATURE_CTYPE, + perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], + format: Characteristic.Formats.INT, + designedMinValue: 0, + designedMaxValue: 110, + designedMinStep: 1, + initialValue: 0, + supportEvents: true, + supportBonjour: false, + manfDescription: "Target Temperature", + unit: Characteristic.Units.FAHRENHEIT, + onUpdate: function(value) { + that.setTargetTemperature(value); + }, + onRead: function(callback) { + that.getTargetTemperature(callback); + } + }); + cTypes.push({ + cType: types.TEMPERATURE_UNITS_CTYPE, + perms: [Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], + format: Characteristic.Formats.INT, + initialValue: 1, + supportEvents: false, + supportBonjour: false, + manfDescription: "Unit", + onUpdate: null, + onRead: function(callback) { + callback(Characteristic.Units.FAHRENHEIT); + } + }); + } + + return cTypes; + }, + + sType: function() { + if (this.typeSupportsHVAC) { + return types.THERMOSTAT_STYPE; + } else if (this.typeSupportsDim) { + return types.LIGHTBULB_STYPE; + } else if (this.typeSupportsSpeedControl) { + return types.FAN_STYPE; + } else if (this.typeSupportsOnOff) { + return types.SWITCH_STYPE; + } + + return types.SWITCH_STYPE; + }, + + getServices: function() { + var that = this; + var services = [{ + sType: types.ACCESSORY_INFORMATION_STYPE, + characteristics: that.informationCharacteristics(), + }, + { + sType: that.sType(), + characteristics: that.controlCharacteristics(that) + }]; + + that.log("Loaded services for " + that.name); + return services; + } +}; + +module.exports.accessory = IndigoAccessory; +module.exports.platform = IndigoPlatform; From 9ad8a111c68b342069cfbcd1e0ff1bf1090022c4 Mon Sep 17 00:00:00 2001 From: Mike Riccio Date: Thu, 15 Oct 2015 15:07:14 -0700 Subject: [PATCH 50/69] Added intro comment. --- platforms/Indigo.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/platforms/Indigo.js b/platforms/Indigo.js index e42849a..f77813a 100644 --- a/platforms/Indigo.js +++ b/platforms/Indigo.js @@ -1,3 +1,24 @@ +// Indigo Platform Shim for HomeBridge +// Written by Mike Riccio (https://github.com/webdeck) +// Based on many of the other HomeBridge plartform modules +// See http://www.indigodomo.com/ for more info on Indigo +// +// Remember to add platform to config.json. Example: +// "platforms": [ +// { +// "platform": "Indigo", // required +// "name": "Indigo", // required +// "host": "127.0.0.1", // required +// "port": "8176", // required +// "username": "username", // optional +// "password": "password" // optional +// } +// ], +// +// When you attempt to add a device, it will ask for a "PIN code". +// The default code for all HomeBridge accessories is 031-45-154. +// + var types = require("HAP-NodeJS/accessories/types.js"); var Characteristic = require("HAP-NodeJS").Characteristic; var request = require('request'); From 6fa69c5c4bb17cfbae3607cc8ed7eaa92d4f43e0 Mon Sep 17 00:00:00 2001 From: Mike Riccio Date: Thu, 15 Oct 2015 16:47:59 -0700 Subject: [PATCH 51/69] Removed tabs --- platforms/Indigo.js | 436 ++++++++++++++++++++++---------------------- 1 file changed, 218 insertions(+), 218 deletions(-) diff --git a/platforms/Indigo.js b/platforms/Indigo.js index f77813a..1332004 100644 --- a/platforms/Indigo.js +++ b/platforms/Indigo.js @@ -8,10 +8,10 @@ // { // "platform": "Indigo", // required // "name": "Indigo", // required -// "host": "127.0.0.1", // required -// "port": "8176", // required -// "username": "username", // optional -// "password": "password" // optional +// "host": "127.0.0.1", // required +// "port": "8176", // required +// "username": "username", // optional +// "password": "password" // optional // } // ], // @@ -28,19 +28,19 @@ var async = require('async'); function IndigoPlatform(log, config) { this.log = log; - this.baseURL = "http://" + config["host"] + ":" + config["port"]; + this.baseURL = "http://" + config["host"] + ":" + config["port"]; - if (config["username"] && config["password"]) { - this.auth = { - 'user': config["username"], - 'pass': config["password"], - 'sendImmediately': false - }; - } + if (config["username"] && config["password"]) { + this.auth = { + 'user': config["username"], + 'pass': config["password"], + 'sendImmediately': false + }; + } } IndigoPlatform.prototype = { - accessories: function(callback) { + accessories: function(callback) { var that = this; this.log("Discovering Indigo Devices."); @@ -48,11 +48,11 @@ IndigoPlatform.prototype = { url: this.baseURL + "/devices.json/", method: 'GET' }; - if (this.auth) { - options['auth'] = this.auth; - } + if (this.auth) { + options['auth'] = this.auth; + } this.foundAccessories = []; - this.callback = callback; + this.callback = callback; request(options, function(error, response, body) { if (error) { @@ -61,73 +61,73 @@ IndigoPlatform.prototype = { return error; } - // Cheesy hack because response may have an extra comma at the start of the array, which is invalid - var firstComma = body.indexOf(","); - if (firstComma < 10) { - body = "[" + body.substr(firstComma + 1); - } + // Cheesy hack because response may have an extra comma at the start of the array, which is invalid + var firstComma = body.indexOf(","); + if (firstComma < 10) { + body = "[" + body.substr(firstComma + 1); + } - var json = JSON.parse(body); - async.each(json, function(item, asyncCallback) { - var deviceURL = that.baseURL + item.restURL; - var deviceOptions = { - url: deviceURL, - method: 'GET' - }; - if (that.auth) { - deviceOptions['auth'] = that.auth; - } + var json = JSON.parse(body); + async.each(json, function(item, asyncCallback) { + var deviceURL = that.baseURL + item.restURL; + var deviceOptions = { + url: deviceURL, + method: 'GET' + }; + if (that.auth) { + deviceOptions['auth'] = that.auth; + } - request(deviceOptions, function(deviceError, deviceResponse, deviceBody) { - if (deviceError) { - asyncCallback(deviceError); - } + request(deviceOptions, function(deviceError, deviceResponse, deviceBody) { + if (deviceError) { + asyncCallback(deviceError); + } - var deviceJson = JSON.parse(deviceBody); - that.log("Discovered " + deviceJson.type + ": " + deviceJson.name); - that.foundAccessories.push( - new IndigoAccessory(that.log, that.auth, deviceURL, deviceJson)); - asyncCallback(); - }); - }, function(asyncError) { - // This will be called after all the requests complete - if (asyncError) { - console.trace("Requesting Indigo device info."); - that.log(asyncError); - } else { - that.callback(that.foundAccessories.sort(function (a,b) { - return (a.name > b.name) - (a.name < b.name); - })); - } - }); - }); - } + var deviceJson = JSON.parse(deviceBody); + that.log("Discovered " + deviceJson.type + ": " + deviceJson.name); + that.foundAccessories.push( + new IndigoAccessory(that.log, that.auth, deviceURL, deviceJson)); + asyncCallback(); + }); + }, function(asyncError) { + // This will be called after all the requests complete + if (asyncError) { + console.trace("Requesting Indigo device info."); + that.log(asyncError); + } else { + that.callback(that.foundAccessories.sort(function (a,b) { + return (a.name > b.name) - (a.name < b.name); + })); + } + }); + }); + } } function IndigoAccessory(log, auth, deviceURL, json) { this.log = log; - this.auth = auth; + this.auth = auth; this.deviceURL = deviceURL; - for (var prop in json) { - if (json.hasOwnProperty(prop)) { - this[prop] = json[prop]; - } - } + for (var prop in json) { + if (json.hasOwnProperty(prop)) { + this[prop] = json[prop]; + } + } } IndigoAccessory.prototype = { getStatus: function(callback) { - var that = this; + var that = this; var options = { - url: this.deviceURL, + url: this.deviceURL, method: 'GET' }; - if (this.auth) { - options['auth'] = this.auth; - } + if (this.auth) { + options['auth'] = this.auth; + } request(options, function(error, response, body) { if (error) { @@ -136,22 +136,22 @@ IndigoAccessory.prototype = { return error; } - that.log("getStatus of " + that.name + ": " + body); - callback(JSON.parse(body)); + that.log("getStatus of " + that.name + ": " + body); + callback(JSON.parse(body)); }); }, updateStatus: function(params) { - var that = this; + var that = this; var options = { - url: this.deviceURL + "?" + params, + url: this.deviceURL + "?" + params, method: 'PUT' }; - if (this.auth) { - options['auth'] = this.auth; - } + if (this.auth) { + options['auth'] = this.auth; + } - this.log("updateStatus of " + that.name + ": " + params); + this.log("updateStatus of " + that.name + ": " + params); request(options, function(error, response, body) { if (error) { console.trace("Updating Device Status."); @@ -162,101 +162,101 @@ IndigoAccessory.prototype = { }, query: function(prop, callback) { - this.getStatus(function(json) { - callback(json[prop]); - }); + this.getStatus(function(json) { + callback(json[prop]); + }); }, - turnOn: function() { - if (this.typeSupportsOnOff) { - this.updateStatus("isOn=1"); - } - }, - - turnOff: function() { - if (this.typeSupportsOnOff) { - this.updateStatus("isOn=0"); - } - }, + turnOn: function() { + if (this.typeSupportsOnOff) { + this.updateStatus("isOn=1"); + } + }, + + turnOff: function() { + if (this.typeSupportsOnOff) { + this.updateStatus("isOn=0"); + } + }, - setBrightness: function(brightness) { - if (this.typeSupportsDim && brightness >= 0 && brightness <= 100) { - this.updateStatus("brightness=" + brightness); - } - }, - - setSpeedIndex: function(speedIndex) { - if (this.typeSupportsSpeedControl && speedIndex >= 0 && speedIndex <= 3) { - this.updateStatus("speedIndex=" + speedIndex); - } - }, - + setBrightness: function(brightness) { + if (this.typeSupportsDim && brightness >= 0 && brightness <= 100) { + this.updateStatus("brightness=" + brightness); + } + }, + + setSpeedIndex: function(speedIndex) { + if (this.typeSupportsSpeedControl && speedIndex >= 0 && speedIndex <= 3) { + this.updateStatus("speedIndex=" + speedIndex); + } + }, + getCurrentHeatingCooling: function(callback) { - this.getStatus(function(json) { - var mode = 0; - if (json["hvacOperatonModeIsHeat"]) { - mode = 1; - } - else if (json["hvacOperationModeIsCool"]) { - mode = 2; - } - else if (json["hvacOperationModeIsAuto"]) { - mode = 3; - } - callback(mode); - }); + this.getStatus(function(json) { + var mode = 0; + if (json["hvacOperatonModeIsHeat"]) { + mode = 1; + } + else if (json["hvacOperationModeIsCool"]) { + mode = 2; + } + else if (json["hvacOperationModeIsAuto"]) { + mode = 3; + } + callback(mode); + }); }, setTargetHeatingCooling: function(mode) { - if (mode == 0) { - param = "Off"; - } - else if (mode == 1) { - param = "Heat"; - } - else if (mode == 2) { - param = "Cool"; - } - else if (mode == 3) { - param = "Auto"; - } + if (mode == 0) { + param = "Off"; + } + else if (mode == 1) { + param = "Heat"; + } + else if (mode == 2) { + param = "Cool"; + } + else if (mode == 3) { + param = "Auto"; + } - if (param) { - this.updateStatus("hvacOperationModeIs" + param + "=true"); - } + if (param) { + this.updateStatus("hvacOperationModeIs" + param + "=true"); + } }, getTargetTemperature: function(callback) { - this.getStatus(function(json) { - var result; - if (json["hvacOperatonModeIsHeat"]) { - result = json["setpointHeat"]; - } - else if (json["hvacOperationModeIsCool"]) { - result = json["setpointCool"]; - } - else { - result = (json["setpointHeat"] + json["setpointCool"]) / 2; - } - callback(result); - }); + this.getStatus(function(json) { + var result; + if (json["hvacOperatonModeIsHeat"]) { + result = json["setpointHeat"]; + } + else if (json["hvacOperationModeIsCool"]) { + result = json["setpointCool"]; + } + else { + result = (json["setpointHeat"] + json["setpointCool"]) / 2; + } + callback(result); + }); }, setTargetTemperature: function(temperature) { - var that = this; - this.getStatus(function(json) { - if (json["hvacOperatonModeIsHeat"]) { - that.updateStatus("setpointHeat=" + temperature); - } - else if (json["hvacOperationModeIsCool"]) { - that.updateStatus("setpointCool=" + temperature); - } - else { - var cool = temperature + 5; - var heat = temperature - 5; - that.updateStatus("setpointCool=" + cool + "&setpointHeat=" + heat); - } - }); + var that = this; + this.getStatus(function(json) { + if (json["hvacOperatonModeIsHeat"]) { + that.updateStatus("setpointHeat=" + temperature); + } + else if (json["hvacOperationModeIsCool"]) { + that.updateStatus("setpointCool=" + temperature); + } + else { + var cool = temperature + 5; + var heat = temperature - 5; + that.updateStatus("setpointCool=" + cool + "&setpointHeat=" + heat); + } + }); }, informationCharacteristics: function() { @@ -316,7 +316,7 @@ IndigoAccessory.prototype = { }, controlCharacteristics: function(that) { - var cTypes = [{ + var cTypes = [{ cType: types.NAME_CTYPE, onUpdate: null, perms: [Characteristic.Perms.READ], @@ -326,15 +326,15 @@ IndigoAccessory.prototype = { supportBonjour: false, manfDescription: "Name of the accessory", designedMaxLength: 255 - }]; + }]; /* if (that.typeSupportsOnOff) - { */ + { */ cTypes.push({ cType: types.POWER_STATE_CTYPE, perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], format: Characteristic.Formats.BOOL, - initialValue: (that.isOn) ? 1 : 0, + initialValue: (that.isOn) ? 1 : 0, supportEvents: true, supportBonjour: false, manfDescription: "Change the power state", @@ -347,14 +347,14 @@ IndigoAccessory.prototype = { } }, onRead: function(callback) { - that.query("isOn", function(isOn) { - callback((isOn) ? 1 : 0); - }); + that.query("isOn", function(isOn) { + callback((isOn) ? 1 : 0); + }); } }); -// } - if (that.typeSupportsDim) - { +// } + if (that.typeSupportsDim) + { cTypes.push({ cType: types.BRIGHTNESS_CTYPE, perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], @@ -368,10 +368,10 @@ IndigoAccessory.prototype = { designedMinStep: 1, unit: Characteristic.Units.PERCENTAGE, onUpdate: function(value) { - that.setBrightness(value); + that.setBrightness(value); }, onRead: function(callback) { - that.query("brightness", callback); + that.query("brightness", callback); } }); } @@ -386,11 +386,11 @@ IndigoAccessory.prototype = { supportBonjour: false, manfDescription: "Change the speed of the fan", designedMaxLength: 1, - designedMinValue: 0, - designedMaxValue: 3, - designedMinStep: 1, + designedMinValue: 0, + designedMaxValue: 3, + designedMinStep: 1, onUpdate: function(value) { - that.setSpeedIndex(value); + that.setSpeedIndex(value); }, onRead: function(callback) { that.query("speedIndex", callback); @@ -400,44 +400,44 @@ IndigoAccessory.prototype = { if (that.typeSupportsHVAC) { cTypes.push({ - cType: types.CURRENTHEATINGCOOLING_CTYPE, - perms: [Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], - format: Characteristic.Formats.INT, - initialValue: 0, - supportEvents: true, - supportBonjour: false, - manfDescription: "Current Mode", - designedMaxLength: 1, - designedMinValue: 0, - designedMaxValue: 3, - designedMinStep: 1, - onUpdate: null, - onRead: function(callback) { - that.getCurrentHeatingCooling(callback); - } + cType: types.CURRENTHEATINGCOOLING_CTYPE, + perms: [Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], + format: Characteristic.Formats.INT, + initialValue: 0, + supportEvents: true, + supportBonjour: false, + manfDescription: "Current Mode", + designedMaxLength: 1, + designedMinValue: 0, + designedMaxValue: 3, + designedMinStep: 1, + onUpdate: null, + onRead: function(callback) { + that.getCurrentHeatingCooling(callback); + } }); cTypes.push({ - cType: types.TARGETHEATINGCOOLING_CTYPE, - perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], - format: Characteristic.Formats.INT, - initialValue: 0, - supportEvents: true, - supportBonjour: false, - manfDescription: "Target Mode", - designedMaxLength: 1, - designedMinValue: 0, - designedMaxValue: 3, - designedMinStep: 1, - onUpdate: function(value) { - that.setTargetHeatingCooling(value); - }, - onRead: function(callback) { - that.getCurrentHeatingCooling(callback); - } + cType: types.TARGETHEATINGCOOLING_CTYPE, + perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], + format: Characteristic.Formats.INT, + initialValue: 0, + supportEvents: true, + supportBonjour: false, + manfDescription: "Target Mode", + designedMaxLength: 1, + designedMinValue: 0, + designedMaxValue: 3, + designedMinStep: 1, + onUpdate: function(value) { + that.setTargetHeatingCooling(value); + }, + onRead: function(callback) { + that.getCurrentHeatingCooling(callback); + } }); cTypes.push({ - cType: types.CURRENT_TEMPERATURE_CTYPE, - perms: [Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], + cType: types.CURRENT_TEMPERATURE_CTYPE, + perms: [Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], format: Characteristic.Formats.INT, designedMinValue: 0, designedMaxValue: 110, @@ -446,11 +446,11 @@ IndigoAccessory.prototype = { supportEvents: true, supportBonjour: false, manfDescription: "Current Temperature", - unit: Characteristic.Units.FAHRENHEIT, - onUpdate: null, + unit: Characteristic.Units.FAHRENHEIT, + onUpdate: null, onRead: function(callback) { that.query("displayRawState", callback); - } + } }); cTypes.push({ cType: types.TARGET_TEMPERATURE_CTYPE, @@ -463,12 +463,12 @@ IndigoAccessory.prototype = { supportEvents: true, supportBonjour: false, manfDescription: "Target Temperature", - unit: Characteristic.Units.FAHRENHEIT, - onUpdate: function(value) { + unit: Characteristic.Units.FAHRENHEIT, + onUpdate: function(value) { that.setTargetTemperature(value); }, onRead: function(callback) { - that.getTargetTemperature(callback); + that.getTargetTemperature(callback); } }); cTypes.push({ @@ -479,9 +479,9 @@ IndigoAccessory.prototype = { supportEvents: false, supportBonjour: false, manfDescription: "Unit", - onUpdate: null, + onUpdate: null, onRead: function(callback) { - callback(Characteristic.Units.FAHRENHEIT); + callback(Characteristic.Units.FAHRENHEIT); } }); } @@ -490,13 +490,13 @@ IndigoAccessory.prototype = { }, sType: function() { - if (this.typeSupportsHVAC) { - return types.THERMOSTAT_STYPE; - } else if (this.typeSupportsDim) { + if (this.typeSupportsHVAC) { + return types.THERMOSTAT_STYPE; + } else if (this.typeSupportsDim) { return types.LIGHTBULB_STYPE; } else if (this.typeSupportsSpeedControl) { return types.FAN_STYPE; - } else if (this.typeSupportsOnOff) { + } else if (this.typeSupportsOnOff) { return types.SWITCH_STYPE; } @@ -504,7 +504,7 @@ IndigoAccessory.prototype = { }, getServices: function() { - var that = this; + var that = this; var services = [{ sType: types.ACCESSORY_INFORMATION_STYPE, characteristics: that.informationCharacteristics(), From bab1eb730e332b1e946ccc70709554d74a106447 Mon Sep 17 00:00:00 2001 From: Nick Farina Date: Thu, 15 Oct 2015 20:08:25 -0700 Subject: [PATCH 52/69] Version bump to node-icontrol --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8f88a35..feb0370 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "lifx": "git+https://github.com/magicmonkey/lifxjs.git", "mdns": "^2.2.4", "node-hue-api": "^1.0.5", - "node-icontrol": "^0.1.4", + "node-icontrol": "^0.1.5", "node-milight-promise": "0.0.x", "node-persist": "0.0.x", "q": "1.4.x", From aad811fe6e794b37e253c4bc39e94bef77a559a6 Mon Sep 17 00:00:00 2001 From: Mike Riccio Date: Thu, 15 Oct 2015 21:31:08 -0700 Subject: [PATCH 53/69] Fixed thermostats to report celsius. --- platforms/Indigo.js | 101 ++++++++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 46 deletions(-) diff --git a/platforms/Indigo.js b/platforms/Indigo.js index 1332004..98f259c 100644 --- a/platforms/Indigo.js +++ b/platforms/Indigo.js @@ -226,34 +226,42 @@ IndigoAccessory.prototype = { } }, + // Note: HomeKit wants all temperature values to be in celsius + getCurrentTemperature: function(callback) { + this.query("displayRawState", function(temperature) { + callback((temperature - 32.0) * 5.0 / 9.0); + }); + }, + getTargetTemperature: function(callback) { this.getStatus(function(json) { - var result; + var temperature; if (json["hvacOperatonModeIsHeat"]) { - result = json["setpointHeat"]; + temperature = json["setpointHeat"]; } else if (json["hvacOperationModeIsCool"]) { - result = json["setpointCool"]; + temperature = json["setpointCool"]; } else { - result = (json["setpointHeat"] + json["setpointCool"]) / 2; + temperature = (json["setpointHeat"] + json["setpointCool"]) / 2.0; } - callback(result); + callback((temperature - 32.0) * 5.0 / 9.0); }); }, setTargetTemperature: function(temperature) { var that = this; + var t = (temperature * 9.0 / 5.0) + 32.0; this.getStatus(function(json) { if (json["hvacOperatonModeIsHeat"]) { - that.updateStatus("setpointHeat=" + temperature); + that.updateStatus("setpointHeat=" + t); } else if (json["hvacOperationModeIsCool"]) { - that.updateStatus("setpointCool=" + temperature); + that.updateStatus("setpointCool=" + t); } else { - var cool = temperature + 5; - var heat = temperature - 5; + var cool = t + 5; + var heat = t - 5; that.updateStatus("setpointCool=" + cool + "&setpointHeat=" + heat); } }); @@ -328,39 +336,36 @@ IndigoAccessory.prototype = { designedMaxLength: 255 }]; -/* if (that.typeSupportsOnOff) - { */ - cTypes.push({ - cType: types.POWER_STATE_CTYPE, - perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], - format: Characteristic.Formats.BOOL, - initialValue: (that.isOn) ? 1 : 0, - supportEvents: true, - supportBonjour: false, - manfDescription: "Change the power state", - designedMaxLength: 1, - onUpdate: function(value) { - if (value == 0) { - that.turnOff(); - } else { - that.turnOn(); - } - }, - onRead: function(callback) { - that.query("isOn", function(isOn) { - callback((isOn) ? 1 : 0); - }); + cTypes.push({ + cType: types.POWER_STATE_CTYPE, + perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], + format: Characteristic.Formats.BOOL, + initialValue: (that.isOn) ? 1 : 0, + supportEvents: false, + supportBonjour: false, + manfDescription: "Change the power state", + designedMaxLength: 1, + onUpdate: function(value) { + if (value == 0) { + that.turnOff(); + } else { + that.turnOn(); } - }); -// } - if (that.typeSupportsDim) - { + }, + onRead: function(callback) { + that.query("isOn", function(isOn) { + callback((isOn) ? 1 : 0); + }); + } + }); + + if (that.typeSupportsDim) { cTypes.push({ cType: types.BRIGHTNESS_CTYPE, perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], format: Characteristic.Formats.INT, initialValue: that.brightness, - supportEvents: true, + supportEvents: false, supportBonjour: false, manfDescription: "Adjust Brightness of Light", designedMinValue: 0, @@ -375,14 +380,14 @@ IndigoAccessory.prototype = { } }); } - if (that.typeSupportsSpeedControl) - { + + if (that.typeSupportsSpeedControl) { cTypes.push({ cType: types.ROTATION_SPEED_CTYPE, perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], format: Characteristic.Formats.INT, initialValue: 0, - supportEvents: true, + supportEvents: false, supportBonjour: false, manfDescription: "Change the speed of the fan", designedMaxLength: 1, @@ -397,14 +402,14 @@ IndigoAccessory.prototype = { } }); } - if (that.typeSupportsHVAC) - { + + if (that.typeSupportsHVAC) { cTypes.push({ cType: types.CURRENTHEATINGCOOLING_CTYPE, perms: [Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], format: Characteristic.Formats.INT, initialValue: 0, - supportEvents: true, + supportEvents: false, supportBonjour: false, manfDescription: "Current Mode", designedMaxLength: 1, @@ -416,12 +421,13 @@ IndigoAccessory.prototype = { that.getCurrentHeatingCooling(callback); } }); + cTypes.push({ cType: types.TARGETHEATINGCOOLING_CTYPE, perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], format: Characteristic.Formats.INT, initialValue: 0, - supportEvents: true, + supportEvents: false, supportBonjour: false, manfDescription: "Target Mode", designedMaxLength: 1, @@ -435,6 +441,7 @@ IndigoAccessory.prototype = { that.getCurrentHeatingCooling(callback); } }); + cTypes.push({ cType: types.CURRENT_TEMPERATURE_CTYPE, perms: [Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], @@ -443,15 +450,16 @@ IndigoAccessory.prototype = { designedMaxValue: 110, designedMinStep: 1, initialValue: 0, - supportEvents: true, + supportEvents: false, supportBonjour: false, manfDescription: "Current Temperature", unit: Characteristic.Units.FAHRENHEIT, onUpdate: null, onRead: function(callback) { - that.query("displayRawState", callback); + that.getCurrentTemperature(callback); } }); + cTypes.push({ cType: types.TARGET_TEMPERATURE_CTYPE, perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], @@ -460,7 +468,7 @@ IndigoAccessory.prototype = { designedMaxValue: 110, designedMinStep: 1, initialValue: 0, - supportEvents: true, + supportEvents: false, supportBonjour: false, manfDescription: "Target Temperature", unit: Characteristic.Units.FAHRENHEIT, @@ -471,6 +479,7 @@ IndigoAccessory.prototype = { that.getTargetTemperature(callback); } }); + cTypes.push({ cType: types.TEMPERATURE_UNITS_CTYPE, perms: [Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], From 7233c5bf7454008a45fd4c29219e09fec2f4b5cb Mon Sep 17 00:00:00 2001 From: Matthias Schmidt Date: Fri, 16 Oct 2015 10:50:55 +0200 Subject: [PATCH 54/69] Support for case-sensitive file systems and current HAP-NodeJS with nodejs 4 support --- accessories/FileSensor.js | 4 ++-- accessories/GenericRS232Device.js | 4 ++-- accessories/HomeMaticWindow.js | 2 +- accessories/Http.js | 4 ++-- accessories/HttpHygrometer.js | 4 ++-- accessories/HttpThermometer.js | 4 ++-- accessories/Lockitron.js | 4 ++-- accessories/WeMo.js | 4 ++-- accessories/iControl.js | 4 ++-- accessories/knxdevice.js | 4 ++-- accessories/mpdclient.js | 4 ++-- app.js | 14 +++++++------- package.json | 2 +- platforms/FHEM.js | 4 ++-- platforms/FibaroHC2.js | 4 ++-- platforms/HomeAssistant.js | 4 ++-- platforms/HomeSeer.js | 4 ++-- platforms/LIFx.js | 4 ++-- platforms/MiLight.js | 4 ++-- platforms/YamahaAVR.js | 4 ++-- platforms/ZWayServer.js | 4 ++-- 21 files changed, 45 insertions(+), 45 deletions(-) diff --git a/accessories/FileSensor.js b/accessories/FileSensor.js index e377dc6..112089e 100644 --- a/accessories/FileSensor.js +++ b/accessories/FileSensor.js @@ -1,5 +1,5 @@ -var Service = require("HAP-NodeJS").Service; -var Characteristic = require("HAP-NodeJS").Characteristic; +var Service = require("hap-nodejs").Service; +var Characteristic = require("hap-nodejs").Characteristic; var chokidar = require("chokidar"); var debug = require("debug")("FileSensorAccessory"); var crypto = require("crypto"); diff --git a/accessories/GenericRS232Device.js b/accessories/GenericRS232Device.js index b84e4cc..01fe80c 100644 --- a/accessories/GenericRS232Device.js +++ b/accessories/GenericRS232Device.js @@ -1,5 +1,5 @@ -var Service = require("HAP-NodeJS").Service; -var Characteristic = require("HAP-NodeJS").Characteristic; +var Service = require("hap-nodejs").Service; +var Characteristic = require("hap-nodejs").Characteristic; var SerialPort = require("serialport").SerialPort; module.exports = { diff --git a/accessories/HomeMaticWindow.js b/accessories/HomeMaticWindow.js index b7e585d..865b24a 100644 --- a/accessories/HomeMaticWindow.js +++ b/accessories/HomeMaticWindow.js @@ -1,5 +1,5 @@ var types = require("HAP-NodeJS/accessories/types.js"); -var Characteristic = require("HAP-NodeJS").Characteristic; +var Characteristic = require("hap-nodejs").Characteristic; var request = require("request"); function HomeMaticWindow(log, config) { diff --git a/accessories/Http.js b/accessories/Http.js index e1859cf..fb708e3 100644 --- a/accessories/Http.js +++ b/accessories/Http.js @@ -1,5 +1,5 @@ -var Service = require("HAP-NodeJS").Service; -var Characteristic = require("HAP-NodeJS").Characteristic; +var Service = require("hap-nodejs").Service; +var Characteristic = require("hap-nodejs").Characteristic; var request = require("request"); module.exports = { diff --git a/accessories/HttpHygrometer.js b/accessories/HttpHygrometer.js index 61ad3b9..f722fe8 100644 --- a/accessories/HttpHygrometer.js +++ b/accessories/HttpHygrometer.js @@ -1,5 +1,5 @@ -var Service = require("HAP-NodeJS").Service; -var Characteristic = require("HAP-NodeJS").Characteristic; +var Service = require("hap-nodejs").Service; +var Characteristic = require("hap-nodejs").Characteristic; var request = require("request"); module.exports = { diff --git a/accessories/HttpThermometer.js b/accessories/HttpThermometer.js index ac9bdc2..9235a57 100644 --- a/accessories/HttpThermometer.js +++ b/accessories/HttpThermometer.js @@ -1,5 +1,5 @@ -var Service = require("HAP-NodeJS").Service; -var Characteristic = require("HAP-NodeJS").Characteristic; +var Service = require("hap-nodejs").Service; +var Characteristic = require("hap-nodejs").Characteristic; var request = require("request"); module.exports = { diff --git a/accessories/Lockitron.js b/accessories/Lockitron.js index 8088d14..ed95b7e 100644 --- a/accessories/Lockitron.js +++ b/accessories/Lockitron.js @@ -1,5 +1,5 @@ -var Service = require('HAP-NodeJS').Service; -var Characteristic = require('HAP-NodeJS').Characteristic; +var Service = require("hap-nodejs").Service; +var Characteristic = require("hap-nodejs").Characteristic; var request = require("request"); module.exports = { diff --git a/accessories/WeMo.js b/accessories/WeMo.js index b97b041..5cbc3f4 100644 --- a/accessories/WeMo.js +++ b/accessories/WeMo.js @@ -1,5 +1,5 @@ -var Service = require("HAP-NodeJS").Service; -var Characteristic = require("HAP-NodeJS").Characteristic; +var Service = require("hap-nodejs").Service; +var Characteristic = require("hap-nodejs").Characteristic; var wemo = require('wemo'); module.exports = { diff --git a/accessories/iControl.js b/accessories/iControl.js index d948867..a4299b5 100644 --- a/accessories/iControl.js +++ b/accessories/iControl.js @@ -1,6 +1,6 @@ var iControl = require('node-icontrol').iControl; -var Service = require('HAP-NodeJS').Service; -var Characteristic = require('HAP-NodeJS').Characteristic; +var Service = require("hap-nodejs").Service; +var Characteristic = require("hap-nodejs").Characteristic; module.exports = { accessory: iControlAccessory diff --git a/accessories/knxdevice.js b/accessories/knxdevice.js index 6aa4ffd..92bb88c 100644 --- a/accessories/knxdevice.js +++ b/accessories/knxdevice.js @@ -21,8 +21,8 @@ New 2015-10-07: - Accept uuid_base parameter from config.json to use as unique identifier in UUIDs instead of name (optional) * */ -var Service = require("HAP-NodeJS").Service; -var Characteristic = require("HAP-NodeJS").Characteristic; +var Service = require("hap-nodejs").Service; +var Characteristic = require("hap-nodejs").Characteristic; var knxd = require("eibd"); var knxd_registerGA = require('../platforms/KNX.js').registerGA; var knxd_startMonitor = require('../platforms/KNX.js').startMonitor; diff --git a/accessories/mpdclient.js b/accessories/mpdclient.js index 8fee5eb..ac6bf5d 100644 --- a/accessories/mpdclient.js +++ b/accessories/mpdclient.js @@ -1,5 +1,5 @@ -var Service = require("HAP-NodeJS").Service; -var Characteristic = require("HAP-NodeJS").Characteristic; +var Service = require("hap-nodejs").Service; +var Characteristic = require("hap-nodejs").Characteristic; var request = require("request"); var komponist = require('komponist') diff --git a/app.js b/app.js index 126b7e3..ffc203e 100644 --- a/app.js +++ b/app.js @@ -1,13 +1,13 @@ var fs = require('fs'); var path = require('path'); var storage = require('node-persist'); -var hap = require('HAP-NodeJS'); -var uuid = require('HAP-NodeJS').uuid; -var Bridge = require('HAP-NodeJS').Bridge; -var Accessory = require('HAP-NodeJS').Accessory; -var Service = require('HAP-NodeJS').Service; -var Characteristic = require('HAP-NodeJS').Characteristic; -var accessoryLoader = require('HAP-NodeJS').AccessoryLoader; +var hap = require("hap-nodejs"); +var uuid = require("hap-nodejs").uuid; +var Bridge = require("hap-nodejs").Bridge; +var Accessory = require("hap-nodejs").Accessory; +var Service = require("hap-nodejs").Service; +var Characteristic = require("hap-nodejs").Characteristic; +var accessoryLoader = require("hap-nodejs").AccessoryLoader; var once = require('HAP-NodeJS/lib/util/once').once; console.log("Starting HomeBridge server..."); diff --git a/package.json b/package.json index feb0370..95db873 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "color": "0.10.x", "eibd": "^0.3.1", "elkington": "kevinohara80/elkington", - "hap-nodejs": "git+https://github.com/KhaosT/HAP-NodeJS#4650e771f356a220868d873d16564a6be6603ff7", + "hap-nodejs": "git+https://github.com/KhaosT/HAP-NodeJS#215a3bb1d603097d63ba73d4f7d731813c6b87e5", "harmonyhubjs-client": "^1.1.4", "harmonyhubjs-discover": "git+https://github.com/swissmanu/harmonyhubjs-discover.git", "lifx-api": "^1.0.1", diff --git a/platforms/FHEM.js b/platforms/FHEM.js index 03f3ee1..7ef5e37 100644 --- a/platforms/FHEM.js +++ b/platforms/FHEM.js @@ -16,8 +16,8 @@ // When you attempt to add a device, it will ask for a "PIN code". // The default code for all HomeBridge accessories is 031-45-154. -var Service = require("HAP-NodeJS").Service; -var Characteristic = require("HAP-NodeJS").Characteristic; +var Service = require("hap-nodejs").Service; +var Characteristic = require("hap-nodejs").Characteristic; var types = require('HAP-NodeJS/accessories/types.js'); diff --git a/platforms/FibaroHC2.js b/platforms/FibaroHC2.js index 4567b40..8293645 100644 --- a/platforms/FibaroHC2.js +++ b/platforms/FibaroHC2.js @@ -15,8 +15,8 @@ // The default code for all HomeBridge accessories is 031-45-154. var types = require("HAP-NodeJS/accessories/types.js"); -var Service = require("HAP-NodeJS").Service; -var Characteristic = require("HAP-NodeJS").Characteristic; +var Service = require("hap-nodejs").Service; +var Characteristic = require("hap-nodejs").Characteristic; var request = require("request"); function FibaroHC2Platform(log, config){ diff --git a/platforms/HomeAssistant.js b/platforms/HomeAssistant.js index 61d3bc2..1bfdc6c 100644 --- a/platforms/HomeAssistant.js +++ b/platforms/HomeAssistant.js @@ -67,8 +67,8 @@ // When you attempt to add a device, it will ask for a "PIN code". // The default code for all HomeBridge accessories is 031-45-154. -var Service = require("HAP-NodeJS").Service; -var Characteristic = require("HAP-NodeJS").Characteristic; +var Service = require("hap-nodejs").Service; +var Characteristic = require("hap-nodejs").Characteristic; var url = require('url') var request = require("request"); diff --git a/platforms/HomeSeer.js b/platforms/HomeSeer.js index c4ccde7..ac0bfa0 100644 --- a/platforms/HomeSeer.js +++ b/platforms/HomeSeer.js @@ -139,8 +139,8 @@ // - Door -var Service = require("HAP-NodeJS").Service; -var Characteristic = require("HAP-NodeJS").Characteristic; +var Service = require("hap-nodejs").Service; +var Characteristic = require("hap-nodejs").Characteristic; var request = require("request"); diff --git a/platforms/LIFx.js b/platforms/LIFx.js index 79988eb..89de156 100644 --- a/platforms/LIFx.js +++ b/platforms/LIFx.js @@ -16,8 +16,8 @@ // The default code for all HomeBridge accessories is 031-45-154. // -var Service = require("HAP-NodeJS").Service; -var Characteristic = require("HAP-NodeJS").Characteristic; +var Service = require("hap-nodejs").Service; +var Characteristic = require("hap-nodejs").Characteristic; var lifxRemoteObj = require('lifx-api'); var lifx_remote; diff --git a/platforms/MiLight.js b/platforms/MiLight.js index 3869e74..0325352 100644 --- a/platforms/MiLight.js +++ b/platforms/MiLight.js @@ -47,8 +47,8 @@ TODO: */ -var Service = require("HAP-NodeJS").Service; -var Characteristic = require("HAP-NodeJS").Characteristic; +var Service = require("hap-nodejs").Service; +var Characteristic = require("hap-nodejs").Characteristic; var Milight = require('node-milight-promise').MilightController; var commands = require('node-milight-promise').commands; diff --git a/platforms/YamahaAVR.js b/platforms/YamahaAVR.js index 0dde24f..be73b77 100644 --- a/platforms/YamahaAVR.js +++ b/platforms/YamahaAVR.js @@ -1,8 +1,8 @@ var types = require("HAP-NodeJS/accessories/types.js"); var inherits = require('util').inherits; var debug = require('debug')('YamahaAVR'); -var Service = require("HAP-NodeJS").Service; -var Characteristic = require("HAP-NodeJS").Characteristic; +var Service = require("hap-nodejs").Service; +var Characteristic = require("hap-nodejs").Characteristic; var Yamaha = require('yamaha-nodejs'); var Q = require('q'); var mdns = require('mdns'); diff --git a/platforms/ZWayServer.js b/platforms/ZWayServer.js index e50336e..9800ab6 100644 --- a/platforms/ZWayServer.js +++ b/platforms/ZWayServer.js @@ -1,6 +1,6 @@ var debug = require('debug')('ZWayServer'); -var Service = require("HAP-NodeJS").Service; -var Characteristic = require("HAP-NodeJS").Characteristic; +var Service = require("hap-nodejs").Service; +var Characteristic = require("hap-nodejs").Characteristic; var types = require("HAP-NodeJS/accessories/types.js"); var request = require("request"); var tough = require('tough-cookie'); From 23f190e3d8d82d0663a996d30cca1ec655800ea7 Mon Sep 17 00:00:00 2001 From: stipus Date: Fri, 16 Oct 2015 13:00:18 +0200 Subject: [PATCH 55/69] GarageDoorOpener and Lock support - Smoke sensor battery fix - Added offEventGroup && offEventName to events (turn on launches one HS event. turn off can launch another HS event) - Added GarageDoorOpener support - Added Lock support --- platforms/HomeSeer.js | 253 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 239 insertions(+), 14 deletions(-) diff --git a/platforms/HomeSeer.js b/platforms/HomeSeer.js index c4ccde7..1ebf307 100644 --- a/platforms/HomeSeer.js +++ b/platforms/HomeSeer.js @@ -1,29 +1,34 @@ 'use strict'; // -// HomeSeer Platform Shim for HomeBridge -// V0.1 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/07 +// HomeSeer Platform Shim for HomeBridge by Jean-Michel Joudrier - (stipus at stipus dot com) +// V0.1 - 2015/10/07 // - Initial version -// V0.2 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/10 +// V0.2 - 2015/10/10 // - Occupancy sensor fix -// V0.3 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/11 +// V0.3 - 2015/10/11 // - Added TemperatureUnit=F|C option to temperature sensors // - Added negative temperature support to temperature sensors -// V0.4 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/12 +// V0.4 - 2015/10/12 // - Added thermostat support -// V0.5 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/12 +// V0.5 - 2015/10/12 // - Added Humidity sensor support -// V0.6 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/12 +// V0.6 - 2015/10/12 // - Added Battery support // - Added low battery support for all sensors // - Added HomeSeer event support (using HomeKit switches...) -// V0.7 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/13 +// V0.7 - 2015/10/13 // - You can add multiple HomeKit devices for the same HomeSeer device reference // - Added CarbonMonoxide sensor // - Added CarbonDioxide sensor // - Added onValues option to all binary sensors -// V0.8 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/14 +// V0.8 - 2015/10/14 // - Added uuid_base parameter to all accessories +// V0.9 - 2015/10/16 +// - Smoke sensor battery fix +// - Added offEventGroup && offEventName to events (turn on launches one HS event. turn off can launch another HS event) +// - Added GarageDoorOpener support +// - Added Lock support // // // Remember to add platform to config.json. @@ -41,7 +46,9 @@ // "events":[ // Optional - List of Events - Currently they are imported into HomeKit as switches // { // "eventGroup":"My Group", // Required - The HomeSeer event group -// "eventName":"My Event", // Required - The HomeSeer event name +// "eventName":"My On Event", // Required - The HomeSeer event name +// "offEventGroup":"My Group", // Optional - The HomeSeer event group for turn-off +// "offEventName":"My Off Event", // Optional - The HomeSeer event name for turn-off // "name":"Test", // Optional - HomeSeer event name is the default // "uuid_base":"SomeUniqueId" // Optional - HomeKit identifier will be derived from this parameter instead of the name // } @@ -55,7 +62,7 @@ // "offValue":"0", // Optional - 0 is the default // "onValue":"100", // Optional - 100 is the default // "can_dim":true, // Optional - true is the default - false for a non dimmable lightbulb -// "uuid_base":"SomeUniqueId2" // Optional - HomeKit identifier will be derived from this parameter instead of the name +// "uuid_base":"SomeUniqueId2" // Optional - HomeKit identifier will be derived from this parameter instead of the name. You SHOULD add this parameter to all accessories ! // }, // { // "ref":9 // This is a dimmable Lightbulb by default @@ -109,6 +116,38 @@ // "heatingThresholdRef":170 // Optional - Not-implemented-yet - HomeSeer device reference for your thermostat heating threshold // }, // { +// "ref":200, // Required - HomeSeer Device Reference of a garage door opener +// "type":"GarageDoorOpener", // Required for a Garage Door Opener +// "name":"Garage Door", // Optional - HomeSeer device name is the default +// "stateRef":201, // Required - HomeSeer device reference for your garage door opener current state (can be the same as ref) +// "stateOpenValues":[0], // Required - List of the HomeSeer device values for a HomeKit state=OPEN +// "stateClosedValues":[1], // Required - List of the HomeSeer device values for a HomeKit state=CLOSED +// "stateOpeningValues":[2], // Optional - List of the HomeSeer device values for a HomeKit state=OPENING +// "stateClosingValues":[3], // Optional - List of the HomeSeer device values for a HomeKit state=CLOSING +// "stateStoppedValues":[4], // Optional - List of the HomeSeer device values for a HomeKit state=STOPPED +// "controlRef":201, // Required - HomeSeer device reference for your garage door opener control (can be the same as ref and stateRef) +// "controlOpenValue":0, // Required - HomeSeer device control value for OPEN +// "controlCloseValue":1, // Required - HomeSeer device control value for CLOSE +// "obstructionRef":201, // Optional - HomeSeer device reference for your garage door opener obstruction state (can be the same as ref) +// "obstructionValues":[5], // Optional - List of the HomeSeer device values for a HomeKit obstruction state=OBSTRUCTION +// "lockRef":202, // Optional - HomeSeer device reference for your garage door lock (can be the same as ref) +// "lockUnsecuredValues":[0], // Optional - List of the HomeSeer device values for a HomeKit lock state=UNSECURED +// "lockSecuredValues":[1], // Optional - List of the HomeSeer device values for a HomeKit lock state=SECURED +// "lockJammedValues":[2], // Optional - List of the HomeSeer device values for a HomeKit lock state=JAMMED +// "unlockValue":0, // Optional - HomeSeer device control value to unlock the garage door opener +// "lockValue":1 // Optional - HomeSeer device control value to lock the garage door opener +// }, +// { +// "ref":210, // Required - HomeSeer Device Reference of a Lock +// "type":"Lock", // Required for a Lock +// "name":"Main Door Lock", // Optional - HomeSeer device name is the default +// "lockUnsecuredValues":[0], // Required - List of the HomeSeer device values for a HomeKit lock state=UNSECURED +// "lockSecuredValues":[1], // Required - List of the HomeSeer device values for a HomeKit lock state=SECURED +// "lockJammedValues":[2], // Optional - List of the HomeSeer device values for a HomeKit lock state=JAMMED +// "unlockValue":0, // Required - HomeSeer device control value to unlock +// "lockValue":1 // Required - HomeSeer device control value to lock +// }, +// { // "ref":115, // Required - HomeSeer Device Reference for a device holding battery level (0-100) // "type":"Battery", // Required for a Battery // "name":"Roomba battery", // Optional - HomeSeer device name is the default @@ -136,6 +175,8 @@ // - CarbonMonoxideSensor (onValues, batteryRef, batteryThreshold options) // - CarbonDioxideSensor (onValues, batteryRef, batteryThreshold options) // - Battery (batteryThreshold option) +// - GarageDoorOpener (state, control, obstruction, lock options) +// - Lock (unsecured, secured, jammed options) // - Door @@ -520,6 +561,143 @@ HomeSeerAccessory.prototype = { }.bind(this)); }, + getCurrentDoorState: function(callback) { + var ref = this.config.stateRef; + var url = this.access_url + "request=getstatus&ref=" + ref; + + httpRequest(url, 'GET', function(error, response, body) { + if (error) { + this.log('HomeSeer get current door state function failed: %s', error.message); + callback( error, 0 ); + } + else { + var status = JSON.parse( body ); + var value = status.Devices[0].value; + + this.log('HomeSeer get target door state function succeeded: value=' + value ); + if( this.config.stateOpenValues.indexOf(value) != -1 ) + callback( null, 0 ); + else if( this.config.stateClosedValues.indexOf(value) != -1 ) + callback( null, 1 ); + else if( this.config.stateOpeningValues && this.config.stateOpeningValues.indexOf(value) != -1 ) + callback( null, 2 ); + else if( this.config.stateClosingValues && this.config.stateClosingValues.indexOf(value) != -1 ) + callback( null, 3 ); + else if( this.config.stateStoppedValues && this.config.stateStoppedValues.indexOf(value) != -1 ) + callback( null, 4 ); + else { + this.log( "Error: value for current door state not in stateO0penValues, stateClosedValues, stateOpeningValues, stateClosingValues, stateStoppedValues" ); + callback( null, 0 ); + } + } + }.bind(this)); + }, + + setTargetDoorState: function(state, callback) { + this.log("Setting target door state state to %s", state); + + var ref = this.config.controlRef; + var value = 0; + if( state == 0 ) + value = this.config.controlOpenValue; + else if( state == 1 ) + value = this.config.controlCloseValue; + + var url = this.access_url + "request=controldevicebyvalue&ref=" + ref + "&value=" + value; + httpRequest(url, 'GET', function(error, response, body) { + if (error) { + this.log('HomeSeer set target door state function failed: %s', error.message); + callback(error); + } + else { + this.log('HomeSeer set target door state function succeeded!'); + callback(); + } + }.bind(this)); + }, + + getObstructionDetected: function(callback) { + if( this.config.obstructionRef ) { + var ref = this.config.obstructionRef; + var url = this.access_url + "request=getstatus&ref=" + ref; + + httpRequest(url, 'GET', function(error, response, body) { + if (error) { + this.log('HomeSeer get obstruction detected function failed: %s', error.message); + callback( error, 0 ); + } + else { + var status = JSON.parse( body ); + var value = status.Devices[0].value; + + this.log('HomeSeer get obstruction detected function succeeded: value=' + value ); + if( this.config.obstructionValues && this.config.obstructionValues.indexOf(value) != -1 ) + callback( null, 1 ); + else { + callback( null, 0 ); + } + } + }.bind(this)); + } + else { + callback( null, 0 ); + } + }, + + getLockCurrentState: function(callback) { + var ref = this.config.lockRef; + var url = this.access_url + "request=getstatus&ref=" + ref; + + httpRequest(url, 'GET', function(error, response, body) { + if (error) { + this.log('HomeSeer get lock current state function failed: %s', error.message); + callback( error, 3 ); + } + else { + var status = JSON.parse( body ); + var value = status.Devices[0].value; + + this.log('HomeSeer get lock current state function succeeded: value=' + value ); + if( this.config.lockUnsecuredValues && this.config.lockUnsecuredValues.indexOf(value) != -1 ) + callback( null, 0 ); + else if( this.config.lockSecuredValues && this.config.lockSecuredValues.indexOf(value) != -1 ) + callback( null, 1 ); + else if( this.config.lockJammedValues && this.config.lockJammedValues.indexOf(value) != -1 ) + callback( null, 2 ); + else { + callback( null, 3 ); + } + } + }.bind(this)); + }, + + setLockTargetState: function(state, callback) { + this.log("Setting target lock state state to %s", state); + + var ref = this.config.lockRef; + var value = 0; + if( state == 0 && this.config.unlockValue ) + value = this.config.unlockValue; + else if( state == 1 && this.config.lockValue ) + value = this.config.lockValue; + + var url = this.access_url + "request=controldevicebyvalue&ref=" + ref + "&value=" + value; + httpRequest(url, 'GET', function(error, response, body) { + if (error) { + this.log('HomeSeer set target lock state function failed: %s', error.message); + callback(error); + } + else { + this.log('HomeSeer set target lock state function succeeded!'); + callback(); + } + }.bind(this)); + }, + + getPositionState: function(callback) { + callback( null, 2 ); // Temporarily return STOPPED. TODO: full door support + }, + getServices: function() { var services = [] @@ -675,7 +853,7 @@ HomeSeerAccessory.prototype = { .getCharacteristic(Characteristic.SmokeDetected) .on('get', this.getBinarySensorState.bind(this)); if( this.config.batteryRef ) { - temperatureSensorService + smokeSensorService .addCharacteristic(new Characteristic.StatusLowBattery()) .on('get', this.getLowBatteryStatus.bind(this)); } @@ -716,6 +894,9 @@ HomeSeerAccessory.prototype = { doorService .getCharacteristic(Characteristic.TargetPosition) .on('set', this.setValue.bind(this)); + doorService + .getCharacteristic(Characteristic.PositionState) + .on('get', this.getPositionState.bind(this)); services.push( doorService ); break; } @@ -759,6 +940,41 @@ HomeSeerAccessory.prototype = { services.push( thermostatService ); break; } + case "GarageDoorOpener": { + var garageDoorOpenerService = new Service.GarageDoorOpener(); + garageDoorOpenerService + .getCharacteristic(Characteristic.CurrentDoorState) + .on('get', this.getCurrentDoorState.bind(this)); + garageDoorOpenerService + .getCharacteristic(Characteristic.TargetDoorState) + .on('set', this.setTargetDoorState.bind(this)); + garageDoorOpenerService + .getCharacteristic(Characteristic.ObstructionDetected) + .on('get', this.getObstructionDetected.bind(this)); + if( this.config.lockRef ) { + garageDoorOpenerService + .addCharacteristic(new Characteristic.LockCurrentState()) + .on('get', this.getLockCurrentState.bind(this)); + garageDoorOpenerService + .addCharacteristic(new Characteristic.LockTargetState()) + .on('set', this.setLockTargetState.bind(this)); + } + services.push( garageDoorOpenerService ); + break; + } + case "Lock": { + this.config.lockRef = this.ref; + var lockService = new Service.LockMechanism(); + lockService + .getCharacteristic(Characteristic.LockCurrentState) + .on('get', this.getLockCurrentState.bind(this)); + lockService + .getCharacteristic(Characteristic.LockTargetState) + .on('set', this.setLockTargetState.bind(this)); + services.push( lockService ); + break; + } + default:{ var lightbulbService = new Service.Lightbulb(); lightbulbService @@ -787,7 +1003,11 @@ function HomeSeerEvent(log, platformConfig, eventConfig ) { this.model = "HomeSeer Event"; this.access_url = platformConfig["host"] + "/JSON?"; - this.launch_url = this.access_url + "request=runevent&group=" + encodeURIComponent(this.config.eventGroup) + "&name=" + encodeURIComponent(this.config.eventName); + this.on_url = this.access_url + "request=runevent&group=" + encodeURIComponent(this.config.eventGroup) + "&name=" + encodeURIComponent(this.config.eventName); + + if( this.config.offEventGroup && this.config.offEventName ) { + this.off_url = this.access_url + "request=runevent&group=" + encodeURIComponent(this.config.offEventGroup) + "&name=" + encodeURIComponent(this.config.offEventName); + } if( this.config.name ) this.name = this.config.name; @@ -805,7 +1025,12 @@ HomeSeerEvent.prototype = { launchEvent: function(value, callback) { this.log("Setting event value to %s", value); - httpRequest(this.launch_url, 'GET', function(error, response, body) { + var url = this.on_url; + if( value == 0 && this.off_url ) { + url = this.off_url; + } + + httpRequest(url, 'GET', function(error, response, body) { if (error) { this.log('HomeSeer run event function failed: %s', error.message); callback(error); From c22c14584dab3b8d592184aa4852a540885f6121 Mon Sep 17 00:00:00 2001 From: Mike Riccio Date: Fri, 16 Oct 2015 09:36:18 -0700 Subject: [PATCH 56/69] Added more logging when there's an error getting device details. --- platforms/Indigo.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/platforms/Indigo.js b/platforms/Indigo.js index 98f259c..cd20c84 100644 --- a/platforms/Indigo.js +++ b/platforms/Indigo.js @@ -80,14 +80,16 @@ IndigoPlatform.prototype = { request(deviceOptions, function(deviceError, deviceResponse, deviceBody) { if (deviceError) { - asyncCallback(deviceError); + console.trace("Requesting Indigo device info: " + deviceURL + "\nError: " + deviceError + "\nResponse: " + deviceBody); + asyncCallback(deviceError) + } + else { + var deviceJson = JSON.parse(deviceBody); + that.log("Discovered " + deviceJson.type + ": " + deviceJson.name); + that.foundAccessories.push( + new IndigoAccessory(that.log, that.auth, deviceURL, deviceJson)); + asyncCallback(); } - - var deviceJson = JSON.parse(deviceBody); - that.log("Discovered " + deviceJson.type + ": " + deviceJson.name); - that.foundAccessories.push( - new IndigoAccessory(that.log, that.auth, deviceURL, deviceJson)); - asyncCallback(); }); }, function(asyncError) { // This will be called after all the requests complete From bb1c193bc030921ee620ba5d9c75e9e41ac3f90c Mon Sep 17 00:00:00 2001 From: Matthias Schmidt Date: Fri, 16 Oct 2015 19:10:14 +0200 Subject: [PATCH 57/69] Use npm release of hap-nodejs --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 95db873..bbc5669 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "color": "0.10.x", "eibd": "^0.3.1", "elkington": "kevinohara80/elkington", - "hap-nodejs": "git+https://github.com/KhaosT/HAP-NodeJS#215a3bb1d603097d63ba73d4f7d731813c6b87e5", + "hap-nodejs": "^0.0.2", "harmonyhubjs-client": "^1.1.4", "harmonyhubjs-discover": "git+https://github.com/swissmanu/harmonyhubjs-discover.git", "lifx-api": "^1.0.1", From 831480d035660ba0a2532af4ebfc7a1cefa915c9 Mon Sep 17 00:00:00 2001 From: Nick Farina Date: Fri, 16 Oct 2015 10:40:01 -0700 Subject: [PATCH 58/69] Remove telldus for now --- package.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/package.json b/package.json index bbc5669..ff5563b 100644 --- a/package.json +++ b/package.json @@ -31,8 +31,6 @@ "tough-cookie": "^2.0.0", "request": "2.49.x", "sonos": "0.8.x", - "telldus": "0.0.9", - "telldus-live": "0.2.x", "teslams": "1.0.1", "unofficial-nest-api": "git+https://github.com/hachidorii/unofficial_nodejs_nest.git#d8d48edc952b049ff6320ef99afa7b2f04cdee98", "wemo": "0.2.x", From c5499c122e11bfb3ee130f0670d80f0c38bc7aa1 Mon Sep 17 00:00:00 2001 From: Mike Riccio Date: Fri, 16 Oct 2015 20:21:30 -0700 Subject: [PATCH 59/69] Add devices even if discovery errors. Don't assign everything POWER_STATE_CTYPE. --- platforms/Indigo.js | 67 +++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/platforms/Indigo.js b/platforms/Indigo.js index cd20c84..1b7f722 100644 --- a/platforms/Indigo.js +++ b/platforms/Indigo.js @@ -19,8 +19,8 @@ // The default code for all HomeBridge accessories is 031-45-154. // -var types = require("HAP-NodeJS/accessories/types.js"); -var Characteristic = require("HAP-NodeJS").Characteristic; +var types = require("hap-nodejs/accessories/types.js"); +var Characteristic = require("hap-nodejs").Characteristic; var request = require('request'); var async = require('async'); @@ -96,11 +96,11 @@ IndigoPlatform.prototype = { if (asyncError) { console.trace("Requesting Indigo device info."); that.log(asyncError); - } else { - that.callback(that.foundAccessories.sort(function (a,b) { - return (a.name > b.name) - (a.name < b.name); - })); } + + that.callback(that.foundAccessories.sort(function (a,b) { + return (a.name > b.name) - (a.name < b.name); + })); }); }); } @@ -326,6 +326,8 @@ IndigoAccessory.prototype = { }, controlCharacteristics: function(that) { + var hasAType = false; + var cTypes = [{ cType: types.NAME_CTYPE, onUpdate: null, @@ -338,30 +340,8 @@ IndigoAccessory.prototype = { designedMaxLength: 255 }]; - cTypes.push({ - cType: types.POWER_STATE_CTYPE, - perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], - format: Characteristic.Formats.BOOL, - initialValue: (that.isOn) ? 1 : 0, - supportEvents: false, - supportBonjour: false, - manfDescription: "Change the power state", - designedMaxLength: 1, - onUpdate: function(value) { - if (value == 0) { - that.turnOff(); - } else { - that.turnOn(); - } - }, - onRead: function(callback) { - that.query("isOn", function(isOn) { - callback((isOn) ? 1 : 0); - }); - } - }); - if (that.typeSupportsDim) { + hasAType = true; cTypes.push({ cType: types.BRIGHTNESS_CTYPE, perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], @@ -384,6 +364,7 @@ IndigoAccessory.prototype = { } if (that.typeSupportsSpeedControl) { + hasAType = true; cTypes.push({ cType: types.ROTATION_SPEED_CTYPE, perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], @@ -406,6 +387,7 @@ IndigoAccessory.prototype = { } if (that.typeSupportsHVAC) { + hasAType = true; cTypes.push({ cType: types.CURRENTHEATINGCOOLING_CTYPE, perms: [Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], @@ -486,7 +468,7 @@ IndigoAccessory.prototype = { cType: types.TEMPERATURE_UNITS_CTYPE, perms: [Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], format: Characteristic.Formats.INT, - initialValue: 1, + initialValue: Characteristic.Units.FAHRENHEIT, supportEvents: false, supportBonjour: false, manfDescription: "Unit", @@ -497,6 +479,31 @@ IndigoAccessory.prototype = { }); } + if (that.typeSupportsOnOff || !hasAType) { + cTypes.push({ + cType: types.POWER_STATE_CTYPE, + perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], + format: Characteristic.Formats.BOOL, + initialValue: (that.isOn) ? 1 : 0, + supportEvents: false, + supportBonjour: false, + manfDescription: "Change the power state", + designedMaxLength: 1, + onUpdate: function(value) { + if (value == 0) { + that.turnOff(); + } else { + that.turnOn(); + } + }, + onRead: function(callback) { + that.query("isOn", function(isOn) { + callback((isOn) ? 1 : 0); + }); + } + }); + } + return cTypes; }, From 896d6b40d958a3e93e12ef807b9c1958d08410cc Mon Sep 17 00:00:00 2001 From: Mike Riccio Date: Fri, 16 Oct 2015 20:36:32 -0700 Subject: [PATCH 60/69] Fixed fahrenheit units, was giving error. --- platforms/Indigo.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/platforms/Indigo.js b/platforms/Indigo.js index 1b7f722..cc77e21 100644 --- a/platforms/Indigo.js +++ b/platforms/Indigo.js @@ -430,10 +430,10 @@ IndigoAccessory.prototype = { cType: types.CURRENT_TEMPERATURE_CTYPE, perms: [Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], format: Characteristic.Formats.INT, - designedMinValue: 0, - designedMaxValue: 110, + designedMinValue: 16, + designedMaxValue: 38, designedMinStep: 1, - initialValue: 0, + initialValue: 20, supportEvents: false, supportBonjour: false, manfDescription: "Current Temperature", @@ -448,10 +448,10 @@ IndigoAccessory.prototype = { cType: types.TARGET_TEMPERATURE_CTYPE, perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], format: Characteristic.Formats.INT, - designedMinValue: 0, - designedMaxValue: 110, + designedMinValue: 16, + designedMaxValue: 38, designedMinStep: 1, - initialValue: 0, + initialValue: 20, supportEvents: false, supportBonjour: false, manfDescription: "Target Temperature", @@ -468,13 +468,13 @@ IndigoAccessory.prototype = { cType: types.TEMPERATURE_UNITS_CTYPE, perms: [Characteristic.Perms.READ,Characteristic.Perms.NOTIFY], format: Characteristic.Formats.INT, - initialValue: Characteristic.Units.FAHRENHEIT, + initialValue: 1, supportEvents: false, supportBonjour: false, manfDescription: "Unit", onUpdate: null, onRead: function(callback) { - callback(Characteristic.Units.FAHRENHEIT); + callback(1); } }); } From f2d22584fcc79a6c7b59222687ee26d9b844ec3c Mon Sep 17 00:00:00 2001 From: tommasomarchionni Date: Sat, 17 Oct 2015 12:34:40 +0200 Subject: [PATCH 61/69] Update app.js --- app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.js b/app.js index ffc203e..a17f8ed 100644 --- a/app.js +++ b/app.js @@ -8,7 +8,7 @@ var Accessory = require("hap-nodejs").Accessory; var Service = require("hap-nodejs").Service; var Characteristic = require("hap-nodejs").Characteristic; var accessoryLoader = require("hap-nodejs").AccessoryLoader; -var once = require('HAP-NodeJS/lib/util/once').once; +var once = require("hap-nodejs").once; console.log("Starting HomeBridge server..."); From 385908fa8910f8f5a062e5a4042cef2116725286 Mon Sep 17 00:00:00 2001 From: tommasomarchionni Date: Sat, 17 Oct 2015 13:23:30 +0200 Subject: [PATCH 62/69] Update app.js --- app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.js b/app.js index a17f8ed..20279fa 100644 --- a/app.js +++ b/app.js @@ -8,7 +8,7 @@ var Accessory = require("hap-nodejs").Accessory; var Service = require("hap-nodejs").Service; var Characteristic = require("hap-nodejs").Characteristic; var accessoryLoader = require("hap-nodejs").AccessoryLoader; -var once = require("hap-nodejs").once; +var once = require("hap-nodejs/lib/util/once").once; console.log("Starting HomeBridge server..."); From 196cda80634d59582f22ae7d08c28c7050edfdc4 Mon Sep 17 00:00:00 2001 From: William Bout Date: Sat, 17 Oct 2015 18:12:11 +0200 Subject: [PATCH 63/69] Update Hyperion.js Fix require error --- accessories/Hyperion.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accessories/Hyperion.js b/accessories/Hyperion.js index b01db55..8767b19 100644 --- a/accessories/Hyperion.js +++ b/accessories/Hyperion.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var net = require('net'); var Color = require('color'); From 38cb94c012eaf249ca8187e87124aa2633f98e00 Mon Sep 17 00:00:00 2001 From: Mason James Date: Sat, 17 Oct 2015 18:41:57 -0400 Subject: [PATCH 64/69] Update hap-nodejs requires Update remaining platforms and accessories --- accessories/AD2USB.js | 2 +- accessories/Carwings.js | 2 +- accessories/ELKM1.js | 2 +- accessories/HomeMatic.js | 2 +- accessories/HomeMaticThermo.js | 2 +- accessories/HomeMaticWindow.js | 2 +- accessories/LiftMaster.js | 2 +- accessories/Tesla.js | 2 +- accessories/X10.js | 2 +- platforms/Domoticz.js | 2 +- platforms/FHEM.js | 2 +- platforms/FibaroHC2.js | 2 +- platforms/ISY.js | 2 +- platforms/KNX.js | 2 +- platforms/LogitechHarmony.js | 2 +- platforms/Nest.js | 2 +- platforms/PhilipsHue.js | 2 +- platforms/SmartThings.js | 2 +- platforms/Sonos.js | 2 +- platforms/Telldus.js | 2 +- platforms/TelldusLive.js | 2 +- platforms/Wink.js | 2 +- platforms/YamahaAVR.js | 2 +- platforms/ZWayServer.js | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/accessories/AD2USB.js b/accessories/AD2USB.js index ea05377..bcef82d 100644 --- a/accessories/AD2USB.js +++ b/accessories/AD2USB.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var AD2USB = require('ad2usb'); var CUSTOM_PANEL_LCD_TEXT_CTYPE = "A3E7B8F9-216E-42C1-A21C-97D4E3BE52C8"; diff --git a/accessories/Carwings.js b/accessories/Carwings.js index 150c936..7d0cd82 100644 --- a/accessories/Carwings.js +++ b/accessories/Carwings.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var carwings = require("carwingsjs"); function CarwingsAccessory(log, config) { diff --git a/accessories/ELKM1.js b/accessories/ELKM1.js index d32cc1b..ead0f17 100644 --- a/accessories/ELKM1.js +++ b/accessories/ELKM1.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var elkington = require("elkington"); function ElkM1Accessory(log, config) { diff --git a/accessories/HomeMatic.js b/accessories/HomeMatic.js index 89e43b0..23a7ecb 100644 --- a/accessories/HomeMatic.js +++ b/accessories/HomeMatic.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var request = require("request"); function HomeMatic(log, config) { diff --git a/accessories/HomeMaticThermo.js b/accessories/HomeMaticThermo.js index f3d300f..86db229 100644 --- a/accessories/HomeMaticThermo.js +++ b/accessories/HomeMaticThermo.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var request = require("request"); function HomeMaticThermo(log, config) { diff --git a/accessories/HomeMaticWindow.js b/accessories/HomeMaticWindow.js index 865b24a..6dcaf78 100644 --- a/accessories/HomeMaticWindow.js +++ b/accessories/HomeMaticWindow.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var Characteristic = require("hap-nodejs").Characteristic; var request = require("request"); diff --git a/accessories/LiftMaster.js b/accessories/LiftMaster.js index baf3be8..c239f24 100644 --- a/accessories/LiftMaster.js +++ b/accessories/LiftMaster.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var request = require("request"); // This seems to be the "id" of the official LiftMaster iOS app diff --git a/accessories/Tesla.js b/accessories/Tesla.js index 016a465..8d96e57 100644 --- a/accessories/Tesla.js +++ b/accessories/Tesla.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var tesla = require("teslams"); function TeslaAccessory(log, config) { diff --git a/accessories/X10.js b/accessories/X10.js index 6668a44..0cf781c 100644 --- a/accessories/X10.js +++ b/accessories/X10.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var request = require("request"); function X10(log, config) { diff --git a/platforms/Domoticz.js b/platforms/Domoticz.js index 8930011..948167d 100644 --- a/platforms/Domoticz.js +++ b/platforms/Domoticz.js @@ -50,7 +50,7 @@ // When you attempt to add a device, it will ask for a "PIN code". // The default code for all HomeBridge accessories is 031-45-154. // -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var request = require("request"); function DomoticzPlatform(log, config){ diff --git a/platforms/FHEM.js b/platforms/FHEM.js index 7ef5e37..b892a0a 100644 --- a/platforms/FHEM.js +++ b/platforms/FHEM.js @@ -19,7 +19,7 @@ var Service = require("hap-nodejs").Service; var Characteristic = require("hap-nodejs").Characteristic; -var types = require('HAP-NodeJS/accessories/types.js'); +var types = require('hap-nodejs/accessories/types.js'); var util = require('util'); diff --git a/platforms/FibaroHC2.js b/platforms/FibaroHC2.js index 8293645..57c880c 100644 --- a/platforms/FibaroHC2.js +++ b/platforms/FibaroHC2.js @@ -14,7 +14,7 @@ // When you attempt to add a device, it will ask for a "PIN code". // The default code for all HomeBridge accessories is 031-45-154. -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var Service = require("hap-nodejs").Service; var Characteristic = require("hap-nodejs").Characteristic; var request = require("request"); diff --git a/platforms/ISY.js b/platforms/ISY.js index b4b20a8..53c4980 100644 --- a/platforms/ISY.js +++ b/platforms/ISY.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var xml2js = require('xml2js'); var request = require('request'); var util = require('util'); diff --git a/platforms/KNX.js b/platforms/KNX.js index 65f7a13..0ca4309 100644 --- a/platforms/KNX.js +++ b/platforms/KNX.js @@ -2,7 +2,7 @@ * based on Sonos platform */ 'use strict'; -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); //var hardware = require('myHardwareSupport'); //require any additional hardware packages var knxd = require('eibd'); diff --git a/platforms/LogitechHarmony.js b/platforms/LogitechHarmony.js index a5c5e22..0521a65 100644 --- a/platforms/LogitechHarmony.js +++ b/platforms/LogitechHarmony.js @@ -17,7 +17,7 @@ // -var types = require('HAP-NodeJS/accessories/types.js'); +var types = require('hap-nodejs/accessories/types.js'); var harmonyDiscover = require('harmonyhubjs-discover'); var harmony = require('harmonyhubjs-client'); diff --git a/platforms/Nest.js b/platforms/Nest.js index c1ef3bd..9d225c0 100644 --- a/platforms/Nest.js +++ b/platforms/Nest.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var nest = require('unofficial-nest-api'); function NestPlatform(log, config){ diff --git a/platforms/PhilipsHue.js b/platforms/PhilipsHue.js index 9bceaf0..40dcc33 100644 --- a/platforms/PhilipsHue.js +++ b/platforms/PhilipsHue.js @@ -33,7 +33,7 @@ var hue = require("node-hue-api"), HueApi = hue.HueApi, lightState = hue.lightState; -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); function PhilipsHuePlatform(log, config) { this.log = log; diff --git a/platforms/SmartThings.js b/platforms/SmartThings.js index 580e70b..470ee55 100644 --- a/platforms/SmartThings.js +++ b/platforms/SmartThings.js @@ -1,7 +1,7 @@ // SmartThings JSON API SmartApp required // https://github.com/jnewland/SmartThings/blob/master/JSON.groovy // -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var request = require("request"); function SmartThingsPlatform(log, config){ diff --git a/platforms/Sonos.js b/platforms/Sonos.js index 1d19c2f..812a803 100644 --- a/platforms/Sonos.js +++ b/platforms/Sonos.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var sonos = require('sonos'); function SonosPlatform(log, config){ diff --git a/platforms/Telldus.js b/platforms/Telldus.js index 87d37f1..c192a31 100644 --- a/platforms/Telldus.js +++ b/platforms/Telldus.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var telldus = require('telldus'); function TelldusPlatform(log, config) { diff --git a/platforms/TelldusLive.js b/platforms/TelldusLive.js index b6a88f1..0e861b5 100644 --- a/platforms/TelldusLive.js +++ b/platforms/TelldusLive.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var TellduAPI = require("telldus-live"); function TelldusLivePlatform(log, config) { diff --git a/platforms/Wink.js b/platforms/Wink.js index e2ec455..d9de07f 100644 --- a/platforms/Wink.js +++ b/platforms/Wink.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var wink = require('wink-js'); var model = { diff --git a/platforms/YamahaAVR.js b/platforms/YamahaAVR.js index be73b77..8d79058 100644 --- a/platforms/YamahaAVR.js +++ b/platforms/YamahaAVR.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var inherits = require('util').inherits; var debug = require('debug')('YamahaAVR'); var Service = require("hap-nodejs").Service; diff --git a/platforms/ZWayServer.js b/platforms/ZWayServer.js index 9800ab6..f456cff 100644 --- a/platforms/ZWayServer.js +++ b/platforms/ZWayServer.js @@ -1,7 +1,7 @@ var debug = require('debug')('ZWayServer'); var Service = require("hap-nodejs").Service; var Characteristic = require("hap-nodejs").Characteristic; -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var request = require("request"); var tough = require('tough-cookie'); var Q = require("q"); From 15e9219c80aea5c106af3f775fdec1d42084127b Mon Sep 17 00:00:00 2001 From: David Garozzo Date: Sat, 17 Oct 2015 21:43:43 -0400 Subject: [PATCH 65/69] add async dependency for Indigo --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index ff5563b..f8bf8e4 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "license": "ISC", "dependencies": { "ad2usb": "git+https://github.com/alistairg/node-ad2usb.git#local", + "async": "^1.4.2", "carwingsjs": "0.0.x", "chokidar": "^1.0.5", "color": "0.10.x", From ad62bd4b15a954defbd3a211de588f5058db6847 Mon Sep 17 00:00:00 2001 From: Mike Riccio Date: Sat, 17 Oct 2015 20:27:04 -0700 Subject: [PATCH 66/69] Graceful handling of JSON parse errors from Indigo responses. Make initial discover queries serialized to not overwhelm slow Indigo servers. --- platforms/Indigo.js | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/platforms/Indigo.js b/platforms/Indigo.js index cc77e21..4886f29 100644 --- a/platforms/Indigo.js +++ b/platforms/Indigo.js @@ -68,7 +68,7 @@ IndigoPlatform.prototype = { } var json = JSON.parse(body); - async.each(json, function(item, asyncCallback) { + async.eachSeries(json, function(item, asyncCallback) { var deviceURL = that.baseURL + item.restURL; var deviceOptions = { url: deviceURL, @@ -81,15 +81,19 @@ IndigoPlatform.prototype = { request(deviceOptions, function(deviceError, deviceResponse, deviceBody) { if (deviceError) { console.trace("Requesting Indigo device info: " + deviceURL + "\nError: " + deviceError + "\nResponse: " + deviceBody); - asyncCallback(deviceError) } else { - var deviceJson = JSON.parse(deviceBody); - that.log("Discovered " + deviceJson.type + ": " + deviceJson.name); - that.foundAccessories.push( - new IndigoAccessory(that.log, that.auth, deviceURL, deviceJson)); - asyncCallback(); + try { + var deviceJson = JSON.parse(deviceBody); + that.log("Discovered " + deviceJson.type + ": " + deviceJson.name); + that.foundAccessories.push( + new IndigoAccessory(that.log, that.auth, deviceURL, deviceJson)); + } + catch (e) { + that.log("Error parsing Indigo device info: " + deviceURL + "\nException: " + e + "\nResponse: " + deviceBody); + } } + asyncCallback(); }); }, function(asyncError) { // This will be called after all the requests complete @@ -135,11 +139,18 @@ IndigoAccessory.prototype = { if (error) { console.trace("Requesting Device Status."); that.log(error); - return error; } - - that.log("getStatus of " + that.name + ": " + body); - callback(JSON.parse(body)); + else { + that.log("getStatus of " + that.name + ": " + body); + try { + var json = JSON.parse(body); + callback(json); + } + catch (e) { + console.trace("Requesting Device Status."); + that.log("Exception: " + e + "\nResponse: " + body); + } + } }); }, From 915e67458354bc130bf47261827cab228cb6db94 Mon Sep 17 00:00:00 2001 From: Matthias Schmidt Date: Sun, 18 Oct 2015 13:29:50 +0200 Subject: [PATCH 67/69] Change all HAP-NodeJS require calls to lowercase --- accessories/AD2USB.js | 2 +- accessories/Carwings.js | 2 +- accessories/ELKM1.js | 2 +- accessories/HomeMatic.js | 2 +- accessories/HomeMaticThermo.js | 2 +- accessories/HomeMaticWindow.js | 2 +- accessories/LiftMaster.js | 2 +- accessories/Tesla.js | 2 +- accessories/X10.js | 2 +- platforms/Domoticz.js | 2 +- platforms/FHEM.js | 2 +- platforms/FibaroHC2.js | 2 +- platforms/ISY.js | 2 +- platforms/KNX.js | 2 +- platforms/LogitechHarmony.js | 2 +- platforms/Nest.js | 2 +- platforms/PhilipsHue.js | 2 +- platforms/SmartThings.js | 2 +- platforms/Sonos.js | 2 +- platforms/Telldus.js | 2 +- platforms/TelldusLive.js | 2 +- platforms/Wink.js | 2 +- platforms/YamahaAVR.js | 2 +- platforms/ZWayServer.js | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/accessories/AD2USB.js b/accessories/AD2USB.js index ea05377..bcef82d 100644 --- a/accessories/AD2USB.js +++ b/accessories/AD2USB.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var AD2USB = require('ad2usb'); var CUSTOM_PANEL_LCD_TEXT_CTYPE = "A3E7B8F9-216E-42C1-A21C-97D4E3BE52C8"; diff --git a/accessories/Carwings.js b/accessories/Carwings.js index 150c936..7d0cd82 100644 --- a/accessories/Carwings.js +++ b/accessories/Carwings.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var carwings = require("carwingsjs"); function CarwingsAccessory(log, config) { diff --git a/accessories/ELKM1.js b/accessories/ELKM1.js index d32cc1b..ead0f17 100644 --- a/accessories/ELKM1.js +++ b/accessories/ELKM1.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var elkington = require("elkington"); function ElkM1Accessory(log, config) { diff --git a/accessories/HomeMatic.js b/accessories/HomeMatic.js index 89e43b0..23a7ecb 100644 --- a/accessories/HomeMatic.js +++ b/accessories/HomeMatic.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var request = require("request"); function HomeMatic(log, config) { diff --git a/accessories/HomeMaticThermo.js b/accessories/HomeMaticThermo.js index f3d300f..86db229 100644 --- a/accessories/HomeMaticThermo.js +++ b/accessories/HomeMaticThermo.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var request = require("request"); function HomeMaticThermo(log, config) { diff --git a/accessories/HomeMaticWindow.js b/accessories/HomeMaticWindow.js index 865b24a..6dcaf78 100644 --- a/accessories/HomeMaticWindow.js +++ b/accessories/HomeMaticWindow.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var Characteristic = require("hap-nodejs").Characteristic; var request = require("request"); diff --git a/accessories/LiftMaster.js b/accessories/LiftMaster.js index baf3be8..c239f24 100644 --- a/accessories/LiftMaster.js +++ b/accessories/LiftMaster.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var request = require("request"); // This seems to be the "id" of the official LiftMaster iOS app diff --git a/accessories/Tesla.js b/accessories/Tesla.js index 016a465..8d96e57 100644 --- a/accessories/Tesla.js +++ b/accessories/Tesla.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var tesla = require("teslams"); function TeslaAccessory(log, config) { diff --git a/accessories/X10.js b/accessories/X10.js index 6668a44..0cf781c 100644 --- a/accessories/X10.js +++ b/accessories/X10.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var request = require("request"); function X10(log, config) { diff --git a/platforms/Domoticz.js b/platforms/Domoticz.js index 8930011..948167d 100644 --- a/platforms/Domoticz.js +++ b/platforms/Domoticz.js @@ -50,7 +50,7 @@ // When you attempt to add a device, it will ask for a "PIN code". // The default code for all HomeBridge accessories is 031-45-154. // -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var request = require("request"); function DomoticzPlatform(log, config){ diff --git a/platforms/FHEM.js b/platforms/FHEM.js index 7ef5e37..b892a0a 100644 --- a/platforms/FHEM.js +++ b/platforms/FHEM.js @@ -19,7 +19,7 @@ var Service = require("hap-nodejs").Service; var Characteristic = require("hap-nodejs").Characteristic; -var types = require('HAP-NodeJS/accessories/types.js'); +var types = require('hap-nodejs/accessories/types.js'); var util = require('util'); diff --git a/platforms/FibaroHC2.js b/platforms/FibaroHC2.js index 8293645..57c880c 100644 --- a/platforms/FibaroHC2.js +++ b/platforms/FibaroHC2.js @@ -14,7 +14,7 @@ // When you attempt to add a device, it will ask for a "PIN code". // The default code for all HomeBridge accessories is 031-45-154. -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var Service = require("hap-nodejs").Service; var Characteristic = require("hap-nodejs").Characteristic; var request = require("request"); diff --git a/platforms/ISY.js b/platforms/ISY.js index b4b20a8..53c4980 100644 --- a/platforms/ISY.js +++ b/platforms/ISY.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var xml2js = require('xml2js'); var request = require('request'); var util = require('util'); diff --git a/platforms/KNX.js b/platforms/KNX.js index 65f7a13..0ca4309 100644 --- a/platforms/KNX.js +++ b/platforms/KNX.js @@ -2,7 +2,7 @@ * based on Sonos platform */ 'use strict'; -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); //var hardware = require('myHardwareSupport'); //require any additional hardware packages var knxd = require('eibd'); diff --git a/platforms/LogitechHarmony.js b/platforms/LogitechHarmony.js index a5c5e22..0521a65 100644 --- a/platforms/LogitechHarmony.js +++ b/platforms/LogitechHarmony.js @@ -17,7 +17,7 @@ // -var types = require('HAP-NodeJS/accessories/types.js'); +var types = require('hap-nodejs/accessories/types.js'); var harmonyDiscover = require('harmonyhubjs-discover'); var harmony = require('harmonyhubjs-client'); diff --git a/platforms/Nest.js b/platforms/Nest.js index c1ef3bd..9d225c0 100644 --- a/platforms/Nest.js +++ b/platforms/Nest.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var nest = require('unofficial-nest-api'); function NestPlatform(log, config){ diff --git a/platforms/PhilipsHue.js b/platforms/PhilipsHue.js index 9bceaf0..40dcc33 100644 --- a/platforms/PhilipsHue.js +++ b/platforms/PhilipsHue.js @@ -33,7 +33,7 @@ var hue = require("node-hue-api"), HueApi = hue.HueApi, lightState = hue.lightState; -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); function PhilipsHuePlatform(log, config) { this.log = log; diff --git a/platforms/SmartThings.js b/platforms/SmartThings.js index 580e70b..470ee55 100644 --- a/platforms/SmartThings.js +++ b/platforms/SmartThings.js @@ -1,7 +1,7 @@ // SmartThings JSON API SmartApp required // https://github.com/jnewland/SmartThings/blob/master/JSON.groovy // -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var request = require("request"); function SmartThingsPlatform(log, config){ diff --git a/platforms/Sonos.js b/platforms/Sonos.js index 1d19c2f..812a803 100644 --- a/platforms/Sonos.js +++ b/platforms/Sonos.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var sonos = require('sonos'); function SonosPlatform(log, config){ diff --git a/platforms/Telldus.js b/platforms/Telldus.js index 87d37f1..c192a31 100644 --- a/platforms/Telldus.js +++ b/platforms/Telldus.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var telldus = require('telldus'); function TelldusPlatform(log, config) { diff --git a/platforms/TelldusLive.js b/platforms/TelldusLive.js index b6a88f1..0e861b5 100644 --- a/platforms/TelldusLive.js +++ b/platforms/TelldusLive.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var TellduAPI = require("telldus-live"); function TelldusLivePlatform(log, config) { diff --git a/platforms/Wink.js b/platforms/Wink.js index e2ec455..d9de07f 100644 --- a/platforms/Wink.js +++ b/platforms/Wink.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var wink = require('wink-js'); var model = { diff --git a/platforms/YamahaAVR.js b/platforms/YamahaAVR.js index be73b77..8d79058 100644 --- a/platforms/YamahaAVR.js +++ b/platforms/YamahaAVR.js @@ -1,4 +1,4 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var inherits = require('util').inherits; var debug = require('debug')('YamahaAVR'); var Service = require("hap-nodejs").Service; diff --git a/platforms/ZWayServer.js b/platforms/ZWayServer.js index 9800ab6..f456cff 100644 --- a/platforms/ZWayServer.js +++ b/platforms/ZWayServer.js @@ -1,7 +1,7 @@ var debug = require('debug')('ZWayServer'); var Service = require("hap-nodejs").Service; var Characteristic = require("hap-nodejs").Characteristic; -var types = require("HAP-NodeJS/accessories/types.js"); +var types = require("hap-nodejs/accessories/types.js"); var request = require("request"); var tough = require('tough-cookie'); var Q = require("q"); From 6e5c35ec8820e47cf1f890189c8506543e7b06fc Mon Sep 17 00:00:00 2001 From: Nick Farina Date: Sun, 18 Oct 2015 14:23:00 -0700 Subject: [PATCH 68/69] Restore telldus-live See #277 for discussion. --- package.json | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index f8bf8e4..6bea480 100644 --- a/package.json +++ b/package.json @@ -16,30 +16,31 @@ "carwingsjs": "0.0.x", "chokidar": "^1.0.5", "color": "0.10.x", + "debug": "^2.2.0", "eibd": "^0.3.1", "elkington": "kevinohara80/elkington", "hap-nodejs": "^0.0.2", "harmonyhubjs-client": "^1.1.4", "harmonyhubjs-discover": "git+https://github.com/swissmanu/harmonyhubjs-discover.git", - "lifx-api": "^1.0.1", + "komponist": "0.1.0", "lifx": "git+https://github.com/magicmonkey/lifxjs.git", + "lifx-api": "^1.0.1", "mdns": "^2.2.4", "node-hue-api": "^1.0.5", "node-icontrol": "^0.1.5", "node-milight-promise": "0.0.x", "node-persist": "0.0.x", "q": "1.4.x", - "tough-cookie": "^2.0.0", "request": "2.49.x", "sonos": "0.8.x", + "telldus-live": "^0.2.1", "teslams": "1.0.1", + "tough-cookie": "^2.0.0", "unofficial-nest-api": "git+https://github.com/hachidorii/unofficial_nodejs_nest.git#d8d48edc952b049ff6320ef99afa7b2f04cdee98", "wemo": "0.2.x", "wink-js": "0.0.5", "xml2js": "0.4.x", "xmldoc": "0.1.x", - "komponist" : "0.1.0", - "yamaha-nodejs": "0.4.x", - "debug": "^2.2.0" + "yamaha-nodejs": "0.4.x" } } From 7b24d142b4156fe69619f0560ca73ad7e66d4e7c Mon Sep 17 00:00:00 2001 From: tommasomarchionni Date: Mon, 19 Oct 2015 14:52:08 +0200 Subject: [PATCH 69/69] Create Openhab.js --- platforms/Openhab.js | 347 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 347 insertions(+) create mode 100644 platforms/Openhab.js diff --git a/platforms/Openhab.js b/platforms/Openhab.js new file mode 100644 index 0000000..a148240 --- /dev/null +++ b/platforms/Openhab.js @@ -0,0 +1,347 @@ +// OpenHAB Platform Shim for HomeBridge +// Written by Tommaso Marchionni +// Based on many of the other HomeBridge platform modules +// +// Revisions: +// +// 17 October 2015 [tommasomarchionni] +// - Initial release +// +// Remember to add platform to config.json. Example: +// "platforms": [ +// { +// "platform": "Openhab", +// "name": "Openhab", +// "server": "127.0.0.1", +// "port": "8080", +// "sitemap": "demo" +// } +// ], +// +// Example of sitemap in OpenHAB: +// sitemap homekit label="HomeKit" { +// Switch item=Light_1 label="Light 1" +// } +// +// When you attempt to add a device, it will ask for a "PIN code". +// The default code for all HomeBridge accessories is 031-45-154. +// + +var types = require("hap-nodejs/accessories/types.js"); +var request = require("request"); +var Service = require("hap-nodejs/lib/Service.js").Service; +var Characteristic = require("hap-nodejs").Characteristic; + +function OpenhabPlatform(log, config){ + this.log = log; + this.user = config["user"]; + this.password = config["password"]; + this.server = config["server"]; + this.port = config["port"]; + + this.protocol = "http"; + + this.sitemap = "demo"; + if (typeof config["sitemap"] != 'undefined') { + this.sitemap = config["sitemap"]; + } + +} + +OpenhabPlatform.prototype = { + + sitemapUrl: function() { + var serverString = this.server; + //TODO da verificare + if (this.user && this.password) { + serverString = this.user + ":" + this.password + "@" + serverString; + } + + return this.protocol + "://" + serverString + ":" + this.port + "/rest/sitemaps/" + this.sitemap + "?type=json"; + }, + + parseSitemap: function(sitemap) { + var widgets = [].concat(sitemap.homepage.widget); + var result = []; + for (var i = 0; i < widgets.length; i++) { + var widget = widgets[i]; + if (!widget.item) { + //TODO to handle frame + this.log("WARN: The widget '" + widget.label + "' does not reference an item."); + continue; + } + + if (widget.item.type=="SwitchItem" || widget.item.type=="DimmerItem" || widget.item.type == "RollershutterItem"){ + accessory = new OpenhabAccessory(this.log,this,widget.widgetId,widget.label,widget.item) + this.log("Accessory Found: " + widget.label); + result.push(accessory); + } + + + + } + return result; + }, + + accessories: function(callback) { + this.log("Fetching OpenHAB devices."); + var that = this; + + url = that.sitemapUrl(); + this.log("Connecting to " + url); + request.get({ + url: url, + json: true + }, function(err, response, json) { + if (!err && response.statusCode == 200) { + callback(that.parseSitemap(json)); + } else { + that.log("There was a problem connecting to OpenHAB."); + } + }); + } +}; + +function OpenhabAccessory(log, platform, widgetId, label, detail) { + this.log = log; + this.platform = platform; + this.idx = widgetId; + this.name = label; + this.label = label; + this.type = detail.type; + this.deviceURL = detail.link; + this.addressStr = "n/a"; + this.state = detail.state; + + if (this.type == "DimmerItem") { + this.typeSupportsOnOff = true; + this.typeSupportsDim = true; + } + + if (this.type == "SwitchItem") { + this.typeSupportsOnOff = true; + } + + if (this.type == "RollershutterItem") { + this.typeSupportsWindowCovering = true; + } + +} + +OpenhabAccessory.prototype = { + + updateStatus: function(command) { + var that = this; + + var options = { + url: this.deviceURL, + method: 'POST', + body: "" + command + }; + if (this.auth) { + options['auth'] = this.auth; + } + + that.log("eseguo post"); + + request(options, function(error, response, body) { + if (error) { + console.trace("Updating Device Status."); + that.log(error); + return error; + } + + that.log("updateStatus of " + that.name + ": " + command); + + }); + }, + + getServiceType: function() { + if (this.typeSupportsWindowCovering){ + return new Service.WindowCovering; + } else if (this.typeSupportsDim) { + return new Service.Lightbulb; + } else if (this.typeSupportsOnOff) { + return new Service.Switch; + } + }, + + updateStatus: function(command, callback) { + var that = this; + + var options = { + url: this.deviceURL, + method: 'POST', + body: "" + command + }; + if (this.auth) { + options['auth'] = this.auth; + } + + request(options, function(error, response, body) { + if (error) { + //console.trace("Updating Device Status."); + //that.log(error); + //return error; + callback(new Error(error)); + } else { + that.log("updateStatus of " + that.name + ": " + command); + callback(true); + } + }.bind(this)); + }, + + setPowerState: function(powerOn, callback) { + var that = this; + + if (this.typeSupportsOnOff) { + if (powerOn) { + var command = "ON"; + } else { + var command = "OFF"; + } + + this.log("Setting power state on the '"+this.name+"' to " + command); + this.updateStatus(command, function(noError){ + if (noError) { + that.log("Successfully set '"+that.name+"' to " + command); + callback(); + } else { + callback(new Error('Can not communicate with OpenHAB.')); + } + }.bind(this)); + + }else{ + callback(new Error(this.name + " not supports ONOFF")); + } + }, + + getStatus: function(callback){ + var that = this; + this.log("Fetching status brightness for: " + this.name); + + var options = { + url: this.deviceURL + '/state?type=json', + method: 'GET' + }; + + if (this.auth) { + options['auth'] = this.auth; + } + + request(options, function(error, response, body) { + if (error) { + //console.trace("Requesting Device Status."); + //that.log(error); + //return error; + callback(new Error('Can not communicate with Home Assistant.')); + } else { + that.log("getStatus of " + that.name + ": " + body); + callback(null,body); + } + + + + }.bind(this)); + + }, + + getCurrentPosition: function(callback){ + callback(100); + }, + + getPositionState: function(callback){ + this.log("Fetching position state for: " + this.name); + callback(Characteristic.PositionState.STOPPED); + }, + + setTargetPosition: function(level, callback) { + var that = this; + + this.log("Setting target position on the '"+this.name+"' to " + level); + + this.updateStatus(level, function(noError){ + if (noError) { + that.log("Successfully set position on the '"+that.name+"' to " + level); + callback(); + } else { + callback(new Error('Can not communicate with OpenHAB.')); + } + }.bind(this)); + + }, + + setBrightness: function(level, callback) { + var that = this; + + if (this.typeSupportsDim && level >= 0 && level <= 100) { + + this.log("Setting brightness on the '"+this.name+"' to " + level); + + this.updateStatus(level, function(noError){ + if (noError) { + that.log("Successfully set brightness on the '"+that.name+"' to " + level); + callback(); + } else { + callback(new Error('Can not communicate with OpenHAB.')); + } + }.bind(this)); + } + }, + + getServices: function() { + + var informationService = new Service.AccessoryInformation(); + + informationService + .setCharacteristic(Characteristic.Manufacturer, "OpenHAB") + .setCharacteristic(Characteristic.Model, this.type) + .setCharacteristic(Characteristic.SerialNumber, "1234567890") + .setCharacteristic(Characteristic.Name, this.label); + + var otherService = this.getServiceType(); + + if (this.typeSupportsOnOff) { + otherService + .getCharacteristic(Characteristic.On) + .on('get', this.getStatus.bind(this)) + .on('set', this.setPowerState.bind(this)); + + } + + if (this.typeSupportsDim) { + otherService + .addCharacteristic(Characteristic.Brightness) + .on('get', this.getStatus.bind(this)) + .on('set', this.setBrightness.bind(this)); + } + + if (this.typeSupportsWindowCovering) { + var currentPosition = 100; + + otherService + .getCharacteristic(Characteristic.CurrentPosition) + .on('get', this.getCurrentPosition.bind(this)) + .setValue(currentPosition); + + otherService + .getCharacteristic(Characteristic.PositionState) + .on('get', this.getPositionState.bind(this)) + .setValue(Characteristic.PositionState.STOPPED); + + otherService + .getCharacteristic(Characteristic.TargetPosition) + .on('get', this.getCurrentPosition.bind(this)) + .on('set', this.setTargetPosition.bind(this)); + + } + + console.log(informationService); + + return [informationService, otherService]; + } + +} + +module.exports.accessory = OpenhabAccessory; +module.exports.platform = OpenhabPlatform;