From e40f10b82e78dad18516e55ac4d357f4b4468f43 Mon Sep 17 00:00:00 2001 From: Mike Riccio Date: Thu, 15 Oct 2015 14:25:20 -0700 Subject: [PATCH 1/5] 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 2/5] 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 3/5] 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 aad811fe6e794b37e253c4bc39e94bef77a559a6 Mon Sep 17 00:00:00 2001 From: Mike Riccio Date: Thu, 15 Oct 2015 21:31:08 -0700 Subject: [PATCH 4/5] 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 c22c14584dab3b8d592184aa4852a540885f6121 Mon Sep 17 00:00:00 2001 From: Mike Riccio Date: Fri, 16 Oct 2015 09:36:18 -0700 Subject: [PATCH 5/5] 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