From 983f1362715fc3064c3b3fe5882f2d085449c227 Mon Sep 17 00:00:00 2001 From: ilcato Date: Tue, 15 Sep 2015 13:57:21 +0200 Subject: [PATCH 01/12] 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/12] 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/12] 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/12] 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/12] 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/12] 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 67fb6e8b4decde8b42a80fe54e1df3661696a664 Mon Sep 17 00:00:00 2001 From: Kai Date: Mon, 21 Sep 2015 00:59:26 +0200 Subject: [PATCH 07/12] fixing thermostat. switch the order of the characteristics now my stellaZ thermostat is shown correct in apps like eve and insteon+ --- platforms/ZWayServer.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/platforms/ZWayServer.js b/platforms/ZWayServer.js index 5b79c8e..70c2d6e 100644 --- a/platforms/ZWayServer.js +++ b/platforms/ZWayServer.js @@ -89,11 +89,11 @@ ZWayServerPlatform.prototype = { //TODO: Unify this with getVDevServices, so there's only one place with mapping between service and vDev type. //Note: Order matters! var primaryDeviceClasses = [ - "switchBinary", "thermostat", - "sensorBinary.Door/Window", "sensorMultilevel.Temperature", - "switchMultilevel" + "switchMultilevel", + "switchBinary", + "sensorBinary.Door/Window" ]; var that = this; @@ -226,24 +226,24 @@ ZWayServerAccessory.prototype = { var typeKey = ZWayServerPlatform.getVDevTypeKey(vdev); var services = [], service; switch (typeKey) { - case "switchBinary": - services.push(new Service.Switch(vdev.metrics.title)); - break; - case "switchMultilevel": - services.push(new Service.Lightbulb(vdev.metrics.title)); - break; - case "thermostat": + case "thermostat": services.push(new Service.Thermostat(vdev.metrics.title)); break; case "sensorMultilevel.Temperature": services.push(new Service.TemperatureSensor(vdev.metrics.title)); break; - case "sensorBinary.Door/Window": - services.push(new Service.GarageDoorOpener(vdev.metrics.title)); + case "switchMultilevel": + services.push(new Service.Lightbulb(vdev.metrics.title)); break; case "battery.Battery": services.push(new Service.BatteryService(vdev.metrics.title)); break; + case "switchBinary": + services.push(new Service.Switch(vdev.metrics.title)); + break; + case "sensorBinary.Door/Window": + services.push(new Service.GarageDoorOpener(vdev.metrics.title)); + break; case "sensorMultilevel.Luminiscence": services.push(new Service.LightSensor(vdev.metrics.title)); break; From 625b9f47df2061f11099753108bc2d38364ebd6e Mon Sep 17 00:00:00 2001 From: Nick Farina Date: Mon, 21 Sep 2015 08:17:46 -0700 Subject: [PATCH 08/12] Bump HAP-NodeJS Closes #181 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d0eb41c..3a3ca74 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#0030b35856e04ee2b42f0d05839feaa5c44cbd1f", + "hap-nodejs": "git+https://github.com/KhaosT/HAP-NodeJS#4650e771f356a220868d873d16564a6be6603ff7", "harmonyhubjs-client": "^1.1.4", "harmonyhubjs-discover": "git+https://github.com/swissmanu/harmonyhubjs-discover.git", "lifx-api": "^1.0.1", From ca232f3111305803b2d418764312c72762c534cf Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Fri, 25 Sep 2015 00:26:07 -0400 Subject: [PATCH 09/12] 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 10/12] 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 11/12] 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 12/12] 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]; + } };