From 11f71e076cd0438823ab19a6161b4352bb3b503b Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Tue, 25 Aug 2015 10:28:08 -0700 Subject: [PATCH 01/10] Update MiLight module for new API and add improved (or some at all) handling of lamp types other than rgbw. Also remove invalid comments from json file, and support future versions of node-milight-promise --- accessories/MiLight.js | 260 +++++++++++++++-------------------------- config-sample.json | 12 +- package.json | 2 +- 3 files changed, 104 insertions(+), 170 deletions(-) diff --git a/accessories/MiLight.js b/accessories/MiLight.js index 409fcb5..dacd671 100644 --- a/accessories/MiLight.js +++ b/accessories/MiLight.js @@ -1,181 +1,115 @@ -var types = require("HAP-NodeJS/accessories/types.js"); +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; +module.exports = { + accessory: MiLight +} + function MiLight(log, config) { - this.log = log; - this.ip_address = config["ip_address"]; - this.port = config["port"]; - this.name = config["name"]; - this.zone = config["zone"]; - this.type = config["type"]; - this.delay = config["delay"]; - this.repeat = config["repeat"]; + this.log = log; + + // config info + this.ip_address = config["ip_address"]; + this.port = config["port"]; + this.name = config["name"]; + this.zone = config["zone"]; + this.type = config["type"]; + this.delay = config["delay"]; + this.repeat = config["repeat"]; } var light = new Milight({ - ip: this.ip_address, - port: this.port, - delayBetweenCommands: this.delay, - commandRepeat: this.repeat + ip: this.ip_address, + port: this.port, + delayBetweenCommands: this.delay, + commandRepeat: this.repeat }); MiLight.prototype = { - setPowerState: function(powerOn) { + setPowerState: function(powerOn, callback) { + if (powerOn) { + light.sendCommands(commands[this.type].on(this.zone)); + this.log("Setting power state to on"); + } + else { + light.sendCommands(commands[this.type].off(this.zone)); + this.log("Setting power state to off"); + } + callback(); + }, - var binaryState = powerOn ? "on" : "off"; - var that = this; + setBrightness: function(level, callback) { + this.log("Setting brightness to %s", level); - if (binaryState === "on") { - this.log("Setting power state of zone " + this.zone + " to " + powerOn); - light.sendCommands(commands[this.type].on(this.zone)); - } else { - this.log("Setting power state of zone " + this.zone + " to " + powerOn); - light.sendCommands(commands[this.type].off(this.zone)); - } + // If this is an rgbw lamp, set the absolute brightness specified + if (this.type == "rgbw") { + light.sendCommands(commands.rgbw.brightness(level)); + } else { + // If this is an rgb or a white lamp, they only support brightness up and down. + // Set brightness up when value is >50 and down otherwise. Not sure how well this works real-world. + if (level >= 50) { + light.sendCommands(commands[this.type].brightUp()); + } else { + light.sendCommands(commands[this.type].brightDown()); + } + } + callback(); + }, - }, + setHue: function(value, callback) { + this.log("Setting hue to %s", value); - setBrightnessLevel: function(value) { + if (this.type == "rgbw") { + if (value == 0) { + light.sendCommands(commands.rgbw.whiteMode(this.zone)); + } else { + light.sendCommands(commands.rgbw.hue(commands.rgbw.hsvToMilightColor(Array(value, 0, 0)))); + } + } else if (this.type == "rgb") { + light.sendCommands(commands.rgb.hue(commands.rgbw.hsvToMilightColor(Array(value, 0, 0)))); + } else if (this.type == "white") { + // Again, white lamps don't support setting an absolue colour temp, so trying to do warmer/cooler step at a time based on colour + if (value >= 180) { + light.sendCommands(commands.white.warmer()); + } else { + light.sendCommands(commands.white.cooler()); + } + } - var that = this; + }, + + identify: function(callback) { + this.log("Identify requested!"); + callback(); // success + }, + + getServices: function() { + var informationService = new Service.AccessoryInformation(); + + informationService + .setCharacteristic(Characteristic.Manufacturer, "MiLight") + .setCharacteristic(Characteristic.Model, this.type) + .setCharacteristic(Characteristic.SerialNumber, "MILIGHT12345"); + + var lightbulbService = new Service.Lightbulb(); + + lightbulbService + .getCharacteristic(Characteristic.On) + .on('set', this.setPowerState.bind(this)); + + lightbulbService + .addCharacteristic(new Characteristic.Brightness()) + .on('set', this.setBrightness.bind(this)); - this.log("Setting brightness level of zone " + this.zone + " to " + value); - - light.sendCommands(commands[this.type].brightness(value)); - }, - - setHue: function(value) { - - var that = this; - - this.log("Setting hue of zone " + this.zone + " to " + value); - - if (value == "0") { - light.sendCommands(commands.rgbw.whiteMode(this.zone)); - } else { - light.sendCommands(commands.rgbw.hue(commands.rgbw.hsvToMilightColor(Array(value, 0, 0)))); - } - }, - - - getServices: function() { - var that = this; - var services = [{ - 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: "MiLight", - supportEvents: false, - supportBonjour: false, - manfDescription: "Manufacturer", - designedMaxLength: 255 - }, { - cType: types.MODEL_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.type, - supportEvents: false, - supportBonjour: false, - manfDescription: "Model", - designedMaxLength: 255 - }, { - cType: types.SERIAL_NUMBER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "MILIGHT1234", - 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.LIGHTBULB_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of service", - designedMaxLength: 255 - }, { - cType: types.POWER_STATE_CTYPE, - onUpdate: function(value) { - that.setPowerState(value); - }, - perms: ["pw", "pr", "ev"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Turn on the light", - designedMaxLength: 1 - }, { - cType: types.BRIGHTNESS_CTYPE, - onUpdate: function(value) { - that.setBrightnessLevel(value); - }, - perms: ["pw", "pr", "ev"], - format: "bool", - initialValue: 100, - supportEvents: false, - supportBonjour: false, - manfDescription: "Adjust brightness of light", - designedMinValue: 0, - designedMaxValue: 100, - designedMinStep: 1, - unit: "%" - }] - }]; - if (that.type == "rgbw" || that.type == "rgb") { - services[1].characteristics.push({ - cType: types.HUE_CTYPE, - onUpdate: function(value) { - that.setHue(value); - }, - perms: ["pw", "pr", "ev"], - format: "int", - initialValue: 0, - supportEvents: false, - supportBonjour: false, - manfDescription: "Adjust Hue of Light", - designedMinValue: 0, - designedMaxValue: 360, - designedMinStep: 1, - unit: "arcdegrees" - }); - } - return services; - } + if (this.type == "rgbw" || this.type == "rgb") { + lightbulbService + .addCharacteristic(new Characteristic.Hue()) + .on('set', this.setHue.bind(this)); + } + + return [informationService, lightbulbService]; + } }; - -module.exports.accessory = MiLight; diff --git a/config-sample.json b/config-sample.json index adfe40c..ccad7dc 100644 --- a/config-sample.json +++ b/config-sample.json @@ -153,12 +153,12 @@ { "accessory":"MiLight", "name": "Lamp", - "ip_address": "255.255.255.255", // IP Address of the WiFi Bridge, or 255.255.255.255 to broadcast to all - "port": 8899, // Default port 8899 (50000 for v1 or v2 bridge) - "zone": 1, // Zone to address commands to (not used for rgb only bulbs) - "type": "rgbw", // Bulb type (rgbw, rgb, white) - "delay": 35, // Delay between commands sent to the WiFi bridge (default 35) - "repeat": 3 // Number of times each command is repeated for reliability (default 3) + "ip_address": "255.255.255.255", + "port": 8899, + "zone": 1, + "type": "rgbw", + "delay": 35, + "repeat": 3 }, { "accessory": "Tesla", diff --git a/package.json b/package.json index 5604b0f..ac28c5f 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "harmonyhubjs-discover": "git+https://github.com/swissmanu/harmonyhubjs-discover.git", "mdns": "^2.2.4", "node-hue-api": "^1.0.5", - "node-milight-promise": "0.0.2", + "node-milight-promise": "0.0.x", "node-persist": "0.0.x", "request": "2.49.x", "sonos": "0.8.x", From f81e594ff0a8013dd7dd343f27bee73baf7d44f9 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Tue, 25 Aug 2015 10:34:45 -0700 Subject: [PATCH 02/10] Update readme with reference to milight under lights section --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d75b54e..4fae78b 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Since Siri supports devices added through HomeKit, this means that with Homebrid * _Siri, turn off the Speakers._ ([Sonos](http://www.sonos.com)) * _Siri, turn on the Dehumidifier._ ([WeMo](http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/)) * _Siri, turn on Away Mode._ ([Xfinity Home](http://www.comcast.com/home-security.html)) - * _Siri, turn on the living room lights._ ([Wink](http://www.wink.com), [SmartThings](http://www.smartthings.com), [X10](http://github.com/edc1591/rest-mochad), [Philips Hue](http://meethue.com)) + * _Siri, turn on the living room lights._ ([Wink](http://www.wink.com), [SmartThings](http://www.smartthings.com), [X10](http://github.com/edc1591/rest-mochad), [Philips Hue](http://meethue.com), [LimitlessLED/MiLight/Easybulb](http://www.limitlessled.com/)) * _Siri, set the movie scene._ ([Logitech Harmony](http://myharmony.com/)) If you would like to support any other devices, please write a shim and create a pull request and I'd be happy to add it to this official list. From 7d5caae96d13fae4ed244536ffece8f8024791a4 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Tue, 25 Aug 2015 10:46:57 -0700 Subject: [PATCH 03/10] Add the hue characteristics for all lamps, as we're trying to handle this in some way now --- accessories/MiLight.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/accessories/MiLight.js b/accessories/MiLight.js index dacd671..9ab18f6 100644 --- a/accessories/MiLight.js +++ b/accessories/MiLight.js @@ -104,11 +104,9 @@ MiLight.prototype = { .addCharacteristic(new Characteristic.Brightness()) .on('set', this.setBrightness.bind(this)); - if (this.type == "rgbw" || this.type == "rgb") { - lightbulbService - .addCharacteristic(new Characteristic.Hue()) - .on('set', this.setHue.bind(this)); - } + lightbulbService + .addCharacteristic(new Characteristic.Hue()) + .on('set', this.setHue.bind(this)); return [informationService, lightbulbService]; } From 1050b4e550a8c94806e1edf8e1a487e974e4e5fe Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Tue, 25 Aug 2015 12:12:58 -0700 Subject: [PATCH 04/10] Add a name to the accessory as well --- accessories/MiLight.js | 1 + 1 file changed, 1 insertion(+) diff --git a/accessories/MiLight.js b/accessories/MiLight.js index 9ab18f6..ffcf774 100644 --- a/accessories/MiLight.js +++ b/accessories/MiLight.js @@ -93,6 +93,7 @@ MiLight.prototype = { .setCharacteristic(Characteristic.Manufacturer, "MiLight") .setCharacteristic(Characteristic.Model, this.type) .setCharacteristic(Characteristic.SerialNumber, "MILIGHT12345"); + .setCharacteristic(Characteristic.Name, this.name); var lightbulbService = new Service.Lightbulb(); From d7d80b8618193b3264e0c9c0cd5592679120e6a3 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Tue, 25 Aug 2015 17:46:28 -0700 Subject: [PATCH 05/10] Don't actually add a name characteristic here --- accessories/HomeMatic.js | 0 accessories/MiLight.js | 1 - 2 files changed, 1 deletion(-) mode change 100755 => 100644 accessories/HomeMatic.js diff --git a/accessories/HomeMatic.js b/accessories/HomeMatic.js old mode 100755 new mode 100644 diff --git a/accessories/MiLight.js b/accessories/MiLight.js index ffcf774..9ab18f6 100644 --- a/accessories/MiLight.js +++ b/accessories/MiLight.js @@ -93,7 +93,6 @@ MiLight.prototype = { .setCharacteristic(Characteristic.Manufacturer, "MiLight") .setCharacteristic(Characteristic.Model, this.type) .setCharacteristic(Characteristic.SerialNumber, "MILIGHT12345"); - .setCharacteristic(Characteristic.Name, this.name); var lightbulbService = new Service.Lightbulb(); From b170e81059bf17721ee09793c957d753b3517763 Mon Sep 17 00:00:00 2001 From: EddyK69 Date: Wed, 26 Aug 2015 23:20:42 +0200 Subject: [PATCH 06/10] Implemented: config.json parameter loadscenes & fixed dimmer-bug Added parameter in config.json: 'loadscenes' for enabling/disabling loading scenes Fixed issue with dimmer-range; was 0-100, should be 0-16 --- config-sample.json | 4 +++- platforms/Domoticz.js | 51 ++++++++++++++++++++++++++++--------------- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/config-sample.json b/config-sample.json index ccad7dc..c9ba829 100644 --- a/config-sample.json +++ b/config-sample.json @@ -41,7 +41,9 @@ "platform": "Domoticz", "name": "Domoticz", "server": "127.0.0.1", - "port": "8005" + "port": "8080", + "roomid": 0, + "loadscenes": 1 }, { "platform": "PhilipsHue", diff --git a/platforms/Domoticz.js b/platforms/Domoticz.js index d80e6d6..5bb6910 100644 --- a/platforms/Domoticz.js +++ b/platforms/Domoticz.js @@ -8,6 +8,11 @@ // - Added support for Scenes // - Sorting device names // +// 26 August 2015 [EddyK69] +// - Added parameter in config.json: 'loadscenes' for enabling/disabling loading scenes +// - Fixed issue with dimmer-range; was 0-100, should be 0-16 +// +// // Domoticz JSON API required // https://www.domoticz.com/wiki/Domoticz_API/JSON_URL's#Lights_and_switches // @@ -18,7 +23,8 @@ // "name": "Domoticz", // "server": "127.0.0.1", // "port": "8080", -// "roomid": 123 (0=no roomplan) +// "roomid": 123, (0=no roomplan) +// "loadscenes": 1 (0=disable scenes) // } // ], // @@ -47,6 +53,10 @@ function DomoticzPlatform(log, config){ if (typeof config["roomid"] != 'undefined') { this.roomid = config["roomid"]; } + this.loadscenes = 1; + if (typeof config["loadscenes"] != 'undefined') { + this.loadscenes = config["loadscenes"]; + } } function sortByKey(array, key) { @@ -120,24 +130,26 @@ DomoticzPlatform.prototype = { }); } //Get Scenes - asyncCalls++; - request.get({ - url: this.urlForQuery("type=scenes"), - json: true - }, function(err, response, json) { - if (!err && response.statusCode == 200) { - if (json['result'] != undefined) { - var sArray=sortByKey(json['result'],"Name"); - sArray.map(function(s) { - accessory = new DomoticzAccessory(that.log, that, true, s.idx, s.Name, false, 0, false); - foundAccessories.push(accessory); - }) + if (this.loadscenes == 1) { + asyncCalls++; + request.get({ + url: this.urlForQuery("type=scenes"), + json: true + }, function(err, response, json) { + if (!err && response.statusCode == 200) { + if (json['result'] != undefined) { + var sArray=sortByKey(json['result'],"Name"); + sArray.map(function(s) { + accessory = new DomoticzAccessory(that.log, that, true, s.idx, s.Name, false, 0, false); + foundAccessories.push(accessory); + }) + } + callbackLater(); + } else { + that.log("There was a problem connecting to Domoticz."); } - callbackLater(); - } else { - that.log("There was a problem connecting to Domoticz."); - } - }); + }); + } } } @@ -165,6 +177,9 @@ DomoticzAccessory.prototype = { url = this.platform.urlForQuery("type=command¶m=setcolbrightnessvalue&idx=" + this.idx + "&hue=" + value + "&brightness=100" + "&iswhite=false"); } else if (c == "setLevel") { + //Range should be 0-16 instead of 0-100 + //See http://www.domoticz.com/wiki/Domoticz_API/JSON_URL%27s#Set_a_dimmable_light_to_a_certain_level + value = Math.round((value / 100) * 16) url = this.platform.urlForQuery("type=command¶m=switchlight&idx=" + this.idx + "&switchcmd=Set%20Level&level=" + value); } else if (value != undefined) { From 76eaca8f78d33d610322b3d52314343ff497e8f1 Mon Sep 17 00:00:00 2001 From: EddyK69 Date: Thu, 27 Aug 2015 12:49:22 +0200 Subject: [PATCH 07/10] Fixed misc. dimmer issues Fixed issue that 'on/off'-type lights showed as dimmers in HomeKit. Checking now on SwitchType instead of HaveDimmer Fixed issue that 'on-off'-type lights would not react on Siri 'Switch on/off light'; On/Off types are now handled as Lights instead of Switches --- platforms/Domoticz.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/platforms/Domoticz.js b/platforms/Domoticz.js index 5bb6910..de5b466 100644 --- a/platforms/Domoticz.js +++ b/platforms/Domoticz.js @@ -12,6 +12,10 @@ // - Added parameter in config.json: 'loadscenes' for enabling/disabling loading scenes // - Fixed issue with dimmer-range; was 0-100, should be 0-16 // +// 27 August 2015 [EddyK69] +// - Fixed issue that 'on/off'-type lights showed as dimmers in HomeKit. Checking now on SwitchType instead of HaveDimmer +// - Fixed issue that 'on-off'-type lights would not react on Siri 'Switch on/off light'; On/Off types are now handled as Lights instead of Switches +// (Cannot determine if 'on/off'-type device is a Light or a Switch :( ) // // Domoticz JSON API required // https://www.domoticz.com/wiki/Domoticz_API/JSON_URL's#Lights_and_switches @@ -95,7 +99,8 @@ DomoticzPlatform.prototype = { if (json['result'] != undefined) { var sArray=sortByKey(json['result'],"Name"); sArray.map(function(s) { - accessory = new DomoticzAccessory(that.log, that, false, s.idx, s.Name, s.HaveDimmer, s.MaxDimLevel, (s.SubType=="RGB")||(s.SubType=="RGBW")); + var havedimmer = (s.SwitchType == 'Dimmer') + accessory = new DomoticzAccessory(that.log, that, false, s.idx, s.Name, havedimmer, s.MaxDimLevel, (s.SubType=="RGB")||(s.SubType=="RGBW")); foundAccessories.push(accessory); }) } @@ -118,7 +123,8 @@ DomoticzPlatform.prototype = { sArray.map(function(s) { //only accept switches for now if (typeof s.SwitchType != 'undefined') { - accessory = new DomoticzAccessory(that.log, that, false, s.idx, s.Name, s.HaveDimmer, s.MaxDimLevel, (s.SubType=="RGB")||(s.SubType=="RGBW")); + var havedimmer = (s.SwitchType == 'Dimmer') + accessory = new DomoticzAccessory(that.log, that, false, s.idx, s.Name, havedimmer, s.MaxDimLevel, (s.SubType=="RGB")||(s.SubType=="RGBW")); foundAccessories.push(accessory); } }) @@ -333,11 +339,11 @@ DomoticzAccessory.prototype = { }, sType: function() { - if (this.HaveDimmer == true) { + //if (this.HaveDimmer == true) { return types.LIGHTBULB_STYPE - } else { - return types.SWITCH_STYPE - } + //} else { + // return types.SWITCH_STYPE + //} }, getServices: function() { From bf2216209d01a5164778c7c8f38c1b0255cf975b Mon Sep 17 00:00:00 2001 From: Khaos Tian Date: Fri, 28 Aug 2015 21:49:16 -0700 Subject: [PATCH 08/10] [Philips Hue] retry if command failed due to api rate limit. --- platforms/PhilipsHue.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/platforms/PhilipsHue.js b/platforms/PhilipsHue.js index 0988b06..9bceaf0 100644 --- a/platforms/PhilipsHue.js +++ b/platforms/PhilipsHue.js @@ -216,7 +216,13 @@ PhilipsHueAccessory.prototype = { that.log(device.name + ", characteristic: " + characteristic + ", value: " + value + "."); } else { - that.log(err); + if (err.code == "ECONNRESET") { + setTimeout(function() { + that.executeChange(api, device, characteristic, value); + }, 300); + } else { + that.log(err); + } } }); }, From 2c50d76cb2cb2799eb3a5ba83b4754d37d81f4a3 Mon Sep 17 00:00:00 2001 From: Nick Farina Date: Sat, 29 Aug 2015 09:34:56 -0700 Subject: [PATCH 09/10] Bump HAP-NodeJS --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ac28c5f..7ccb806 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "carwingsjs": "0.0.x", "color": "0.10.x", "elkington": "kevinohara80/elkington", - "hap-nodejs": "git+https://github.com/KhaosT/HAP-NodeJS#46ba0597eb339983a14d98c53764a58a5516fcd2", + "hap-nodejs": "git+https://github.com/KhaosT/HAP-NodeJS#367c598b748abf2f6d5c6185fc38a8843bc6de70", "harmonyhubjs-client": "^1.1.4", "harmonyhubjs-discover": "git+https://github.com/swissmanu/harmonyhubjs-discover.git", "mdns": "^2.2.4", From d067711974975d6d342868d886216201df11a9df Mon Sep 17 00:00:00 2001 From: Nick Farina Date: Sat, 29 Aug 2015 13:14:55 -0700 Subject: [PATCH 10/10] HAP-NodeJS bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7ccb806..ab5b117 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "carwingsjs": "0.0.x", "color": "0.10.x", "elkington": "kevinohara80/elkington", - "hap-nodejs": "git+https://github.com/KhaosT/HAP-NodeJS#367c598b748abf2f6d5c6185fc38a8843bc6de70", + "hap-nodejs": "git+https://github.com/KhaosT/HAP-NodeJS#fff863d7a387636fc612cf27cb859e82d9ee3294", "harmonyhubjs-client": "^1.1.4", "harmonyhubjs-discover": "git+https://github.com/swissmanu/harmonyhubjs-discover.git", "mdns": "^2.2.4",