From 50936ee4714bbc3672fcdf5b3cada77518ff5bb7 Mon Sep 17 00:00:00 2001 From: Russell Cloran Date: Wed, 12 Oct 2016 11:50:44 -0700 Subject: [PATCH 1/6] Add support for thermometers --- README.md | 3 +- accessories/temperature.js | 70 ++++++++++++++++++++++++++++++++++++++ index.js | 8 +++++ 3 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 accessories/temperature.js diff --git a/README.md b/README.md index e6f2264..1e99add 100755 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ Here's a list of the devices that are currently exposed: * **Rollershutter** - exposed as a garage door * **Fan** - on/off/speed * **Input boolean** - on/off +* **Temperature** - temperature from sensors and climate devices is exposed ### Scene Support @@ -68,7 +69,7 @@ adding it to your `config.json`. "name": "HomeAssistant", "host": "http://192.168.1.16:8123", "password": "yourapipassword", - "supported_types": ["fan", "garage_door", "input_boolean", "light", "lock", "media_player", "rollershutter", "scene", "switch"] + "supported_types": ["climate", "fan", "garage_door", "input_boolean", "light", "lock", "media_player", "rollershutter", "scene", "sensor", "switch"] } ] ``` diff --git a/accessories/temperature.js b/accessories/temperature.js new file mode 100644 index 0000000..547c1f1 --- /dev/null +++ b/accessories/temperature.js @@ -0,0 +1,70 @@ +var Service, Characteristic, communicationError; + +module.exports = function (oService, oCharacteristic, oCommunicationError) { + Service = oService; + Characteristic = oCharacteristic; + communicationError = oCommunicationError; + + return HomeAssistantTemperature; +}; +module.exports.HomeAssistantTemperature = HomeAssistantTemperature; + +function HomeAssistantTemperature(log, data, client) { + // device info + this.data = data + this.entity_id = data.entity_id + if (data.attributes && data.attributes.friendly_name) { + this.name = data.attributes.friendly_name + }else{ + this.name = data.entity_id.split('.').pop().replace(/_/g, ' ') + } + + this.entity_type = data.entity_id.split('.')[0] + + this.client = client + this.log = log; +} + +HomeAssistantTemperature.prototype = { + onEvent: function(old_state, new_state) { + }, + identify: function(callback){ + this.log("identifying: " + this.name); + callback(); + }, + getTemperature: function(callback){ + this.log("fetching temperature for: " + this.name); + this.client.fetchState(this.entity_id, function(data){ + if (data) { + if (this.entity_type == 'sensor'){ + value = parseFloat(data.state) + }else{ + value = parseFloat(data.attributes.temperature) + } + // HomeKit only works with Celsius internally + if (data.attributes.unit_of_measurement == '\u00B0F') { + value = (value - 32) / 1.8 + } + callback(null, value) + }else{ + callback(communicationError) + } + }.bind(this)) + }, + getServices: function() { + this.temperatureService = new Service.TemperatureSensor(); + var informationService = new Service.AccessoryInformation(); + + informationService + .setCharacteristic(Characteristic.Manufacturer, "Home Assistant") + .setCharacteristic(Characteristic.Model, "Thermometer") + .setCharacteristic(Characteristic.SerialNumber, "xxx"); + + this.temperatureService + .getCharacteristic(Characteristic.CurrentTemperature) + .on('get', this.getTemperature.bind(this)) + + return [informationService, this.temperatureService]; + } + +} diff --git a/index.js b/index.js index 3acda54..aaccc84 100644 --- a/index.js +++ b/index.js @@ -12,6 +12,7 @@ var HomeAssistantGarageDoor; var HomeAssistantMediaPlayer; var HomeAssistantRollershutter; var HomeAssistantFan; +var HomeAssistantTemperature; module.exports = function(homebridge) { console.log("homebridge API version: " + homebridge.version); @@ -27,6 +28,7 @@ module.exports = function(homebridge) { HomeAssistantRollershutter = require('./accessories/rollershutter')(Service, Characteristic, communicationError); HomeAssistantMediaPlayer = require('./accessories/media_player')(Service, Characteristic, communicationError); HomeAssistantFan = require('./accessories/fan')(Service, Characteristic, communicationError); + HomeAssistantTemperature = require('./accessories/temperature')(Service, Characteristic, communicationError); homebridge.registerPlatform("homebridge-homeassistant", "HomeAssistant", HomeAssistantPlatform, false); } @@ -186,6 +188,12 @@ HomeAssistantPlatform.prototype = { accessory = new HomeAssistantSwitch(that.log, entity, that, 'input_boolean') }else if (entity_type == 'fan'){ accessory = new HomeAssistantFan(that.log, entity, that) + }else if (entity_type == 'sensor'){ + if (entity.attributes && (entity.attributes.unit_of_measurement == '\u00B0C' || entity.attributes.unit_of_measurement == '\u00B0F') && entity.entity_id != 'sensor.vision_zp3111_multisensor_4in1_dew_point_5'){ + accessory = new HomeAssistantTemperature(that.log, entity, that); + } + }else if (entity_type == 'climate'){ + accessory = new HomeAssistantTemperature(that.log, entity, that); } if (accessory) { From 665abcb720cf8849d6263b0a2840ad64cd9bf66a Mon Sep 17 00:00:00 2001 From: Russell Cloran Date: Wed, 12 Oct 2016 12:31:42 -0700 Subject: [PATCH 2/6] Handle onEvent --- accessories/temperature.js | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/accessories/temperature.js b/accessories/temperature.js index 547c1f1..cc5b2e5 100644 --- a/accessories/temperature.js +++ b/accessories/temperature.js @@ -26,7 +26,21 @@ function HomeAssistantTemperature(log, data, client) { } HomeAssistantTemperature.prototype = { + temperatureFromData: function(data) { + if (this.entity_type == 'sensor'){ + value = parseFloat(data.state) + }else{ + value = parseFloat(data.attributes.temperature) + } + // HomeKit only works with Celsius internally + if (data.attributes.unit_of_measurement == '\u00B0F') { + value = (value - 32) / 1.8 + } + return value + }, onEvent: function(old_state, new_state) { + this.temperatureService.getCharacteristic(Characteristic.CurrentTemperature) + .setValue(this.temperatureFromData(new_state), null, 'internal') }, identify: function(callback){ this.log("identifying: " + this.name); @@ -36,16 +50,7 @@ HomeAssistantTemperature.prototype = { this.log("fetching temperature for: " + this.name); this.client.fetchState(this.entity_id, function(data){ if (data) { - if (this.entity_type == 'sensor'){ - value = parseFloat(data.state) - }else{ - value = parseFloat(data.attributes.temperature) - } - // HomeKit only works with Celsius internally - if (data.attributes.unit_of_measurement == '\u00B0F') { - value = (value - 32) / 1.8 - } - callback(null, value) + callback(null, this.temperatureFromData(data)) }else{ callback(communicationError) } From 872d40f8023f0b962c98a1eb8035950ae3ad81e9 Mon Sep 17 00:00:00 2001 From: Russell Cloran Date: Wed, 12 Oct 2016 13:54:16 -0700 Subject: [PATCH 3/6] Address review comments - Use UTF-8 encoded degree symbol instead of escape - Remove climate support - Remove testing stuff --- README.md | 4 ++-- accessories/temperature.js | 8 ++------ index.js | 4 +--- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1e99add..4e63b6b 100755 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Here's a list of the devices that are currently exposed: * **Rollershutter** - exposed as a garage door * **Fan** - on/off/speed * **Input boolean** - on/off -* **Temperature** - temperature from sensors and climate devices is exposed +* **Temperature** - temperature from sensors is exposed ### Scene Support @@ -69,7 +69,7 @@ adding it to your `config.json`. "name": "HomeAssistant", "host": "http://192.168.1.16:8123", "password": "yourapipassword", - "supported_types": ["climate", "fan", "garage_door", "input_boolean", "light", "lock", "media_player", "rollershutter", "scene", "sensor", "switch"] + "supported_types": ["fan", "garage_door", "input_boolean", "light", "lock", "media_player", "rollershutter", "scene", "sensor", "switch"] } ] ``` diff --git a/accessories/temperature.js b/accessories/temperature.js index cc5b2e5..f5db51f 100644 --- a/accessories/temperature.js +++ b/accessories/temperature.js @@ -27,13 +27,9 @@ function HomeAssistantTemperature(log, data, client) { HomeAssistantTemperature.prototype = { temperatureFromData: function(data) { - if (this.entity_type == 'sensor'){ - value = parseFloat(data.state) - }else{ - value = parseFloat(data.attributes.temperature) - } + value = parseFloat(data.state) // HomeKit only works with Celsius internally - if (data.attributes.unit_of_measurement == '\u00B0F') { + if (data.attributes.unit_of_measurement == '°F') { value = (value - 32) / 1.8 } return value diff --git a/index.js b/index.js index aaccc84..f00f5a7 100644 --- a/index.js +++ b/index.js @@ -189,11 +189,9 @@ HomeAssistantPlatform.prototype = { }else if (entity_type == 'fan'){ accessory = new HomeAssistantFan(that.log, entity, that) }else if (entity_type == 'sensor'){ - if (entity.attributes && (entity.attributes.unit_of_measurement == '\u00B0C' || entity.attributes.unit_of_measurement == '\u00B0F') && entity.entity_id != 'sensor.vision_zp3111_multisensor_4in1_dew_point_5'){ + if (entity.attributes && (entity.attributes.unit_of_measurement == '°C' || entity.attributes.unit_of_measurement == '°F')){ accessory = new HomeAssistantTemperature(that.log, entity, that); } - }else if (entity_type == 'climate'){ - accessory = new HomeAssistantTemperature(that.log, entity, that); } if (accessory) { From 968015f20c76a5462073c940ca71325a9d0217f1 Mon Sep 17 00:00:00 2001 From: Russell Cloran Date: Sat, 15 Oct 2016 14:19:11 -0700 Subject: [PATCH 4/6] Change to a more generic sensor class --- accessories/sensor.js | 108 +++++++++++++++++++++++++++++++++++++ accessories/temperature.js | 71 ------------------------ index.js | 10 ++-- package.json | 2 +- 4 files changed, 112 insertions(+), 79 deletions(-) create mode 100644 accessories/sensor.js delete mode 100644 accessories/temperature.js diff --git a/accessories/sensor.js b/accessories/sensor.js new file mode 100644 index 0000000..f9a17db --- /dev/null +++ b/accessories/sensor.js @@ -0,0 +1,108 @@ +var Service, Characteristic, communicationError; + +module.exports = function (oService, oCharacteristic, oCommunicationError) { + Service = oService; + Characteristic = oCharacteristic; + communicationError = oCommunicationError; + + return HomeAssistantSensorFactory; +}; + +function HomeAssistantSensorFactory(log, data, client) { + if (!entity.attributes) { + return null + } + var service, characteristic, transformData + if (entity.attributes.unit_of_measurement == '°C' || entity.attributes.unit_of_measurement == '°F') { + service = Service.TemperatureSensor + characteristic = Characteristic.CurrentTemperature + transformData = function(data) { + value = parseFloat(data.state) + // HomeKit only works with Celsius internally + if (data.attributes.unit_of_measurement == '°F') { + value = (value - 32) / 1.8 + } + return value + } + } else if (entity.attributes.unit_of_measurement == "%" && data.entity_id.includes("humidity")) { + service = Service.HumiditySensor + characteristic = Characteristic.CurrentRelativeHumidity + } else if (entity.attributes.unit_of_measurement == "lux") { + service = Service.LightSensor + characteristic = Characteristic.CurrentAmbientLightLevel + transformData = function(data) { + return Math.max(0.0001, parseFloat(data.state)) + } + } else { + return null + } + + return new HomeAssistantSensor(log, data, client, service, characteristic, transformData) +} + +class HomeAssistantSensor { + constructor(log, data, client, service, characteristic, transformData) { + // device info + this.data = data + this.entity_id = data.entity_id + if (data.attributes && data.attributes.friendly_name) { + this.name = data.attributes.friendly_name + }else{ + this.name = data.entity_id.split('.').pop().replace(/_/g, ' ') + } + + this.entity_type = data.entity_id.split('.')[0] + + this.client = client + this.log = log + + this.service = service + this.characteristic = characteristic + if (transformData) { + this.transformData = transformData + } + } + + transformData(data) { + return parseFloat(data.state) + } + + onEvent(old_state, new_state) { + this.sensorService.getCharacteristic(this.characteristic) + .setValue(this.transformData(new_state), null, 'internal') + } + + identify(callback){ + this.log("identifying: " + this.name); + callback(); + } + + getState(callback){ + this.log("fetching state for: " + this.name); + this.client.fetchState(this.entity_id, function(data){ + if (data) { + callback(null, this.transformData(data)) + }else{ + callback(communicationError) + } + }.bind(this)) + } + + getServices() { + this.sensorService = new this.service() + var informationService = new Service.AccessoryInformation() + + informationService + .setCharacteristic(Characteristic.Manufacturer, "Home Assistant") + .setCharacteristic(Characteristic.Model, "Sensor") + .setCharacteristic(Characteristic.SerialNumber, "xxx") + + this.sensorService + .getCharacteristic(this.characteristic) + .on('get', this.getState.bind(this)) + + return [informationService, this.sensorService]; + } +} + +module.exports.HomeAssistantSensorFactory = HomeAssistantSensorFactory; diff --git a/accessories/temperature.js b/accessories/temperature.js deleted file mode 100644 index f5db51f..0000000 --- a/accessories/temperature.js +++ /dev/null @@ -1,71 +0,0 @@ -var Service, Characteristic, communicationError; - -module.exports = function (oService, oCharacteristic, oCommunicationError) { - Service = oService; - Characteristic = oCharacteristic; - communicationError = oCommunicationError; - - return HomeAssistantTemperature; -}; -module.exports.HomeAssistantTemperature = HomeAssistantTemperature; - -function HomeAssistantTemperature(log, data, client) { - // device info - this.data = data - this.entity_id = data.entity_id - if (data.attributes && data.attributes.friendly_name) { - this.name = data.attributes.friendly_name - }else{ - this.name = data.entity_id.split('.').pop().replace(/_/g, ' ') - } - - this.entity_type = data.entity_id.split('.')[0] - - this.client = client - this.log = log; -} - -HomeAssistantTemperature.prototype = { - temperatureFromData: function(data) { - value = parseFloat(data.state) - // HomeKit only works with Celsius internally - if (data.attributes.unit_of_measurement == '°F') { - value = (value - 32) / 1.8 - } - return value - }, - onEvent: function(old_state, new_state) { - this.temperatureService.getCharacteristic(Characteristic.CurrentTemperature) - .setValue(this.temperatureFromData(new_state), null, 'internal') - }, - identify: function(callback){ - this.log("identifying: " + this.name); - callback(); - }, - getTemperature: function(callback){ - this.log("fetching temperature for: " + this.name); - this.client.fetchState(this.entity_id, function(data){ - if (data) { - callback(null, this.temperatureFromData(data)) - }else{ - callback(communicationError) - } - }.bind(this)) - }, - getServices: function() { - this.temperatureService = new Service.TemperatureSensor(); - var informationService = new Service.AccessoryInformation(); - - informationService - .setCharacteristic(Characteristic.Manufacturer, "Home Assistant") - .setCharacteristic(Characteristic.Model, "Thermometer") - .setCharacteristic(Characteristic.SerialNumber, "xxx"); - - this.temperatureService - .getCharacteristic(Characteristic.CurrentTemperature) - .on('get', this.getTemperature.bind(this)) - - return [informationService, this.temperatureService]; - } - -} diff --git a/index.js b/index.js index f00f5a7..0959aa8 100644 --- a/index.js +++ b/index.js @@ -12,7 +12,7 @@ var HomeAssistantGarageDoor; var HomeAssistantMediaPlayer; var HomeAssistantRollershutter; var HomeAssistantFan; -var HomeAssistantTemperature; +var HomeAssistantSensorFactory; module.exports = function(homebridge) { console.log("homebridge API version: " + homebridge.version); @@ -28,7 +28,7 @@ module.exports = function(homebridge) { HomeAssistantRollershutter = require('./accessories/rollershutter')(Service, Characteristic, communicationError); HomeAssistantMediaPlayer = require('./accessories/media_player')(Service, Characteristic, communicationError); HomeAssistantFan = require('./accessories/fan')(Service, Characteristic, communicationError); - HomeAssistantTemperature = require('./accessories/temperature')(Service, Characteristic, communicationError); + HomeAssistantSensorFactory = require('./accessories/sensor')(Service, Characteristic, communicationError); homebridge.registerPlatform("homebridge-homeassistant", "HomeAssistant", HomeAssistantPlatform, false); } @@ -141,8 +141,6 @@ HomeAssistantPlatform.prototype = { setTimeout(function() { that.accessories(callback); }, 5000); return; } - // that.log(response) - // that.log(data) for (var i = 0; i < data.length; i++) { entity = data[i] @@ -189,9 +187,7 @@ HomeAssistantPlatform.prototype = { }else if (entity_type == 'fan'){ accessory = new HomeAssistantFan(that.log, entity, that) }else if (entity_type == 'sensor'){ - if (entity.attributes && (entity.attributes.unit_of_measurement == '°C' || entity.attributes.unit_of_measurement == '°F')){ - accessory = new HomeAssistantTemperature(that.log, entity, that); - } + accessory = HomeAssistantSensorFactory(that.log, entity, that) } if (accessory) { diff --git a/package.json b/package.json index 6392c85..5abe938 100755 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "url": "http://github.com/home-assistant/homebridge-homeassistant/issues" }, "engines": { - "node": ">=0.12.0", + "node": ">=4.3.2", "homebridge": ">=0.3.0" }, "dependencies": { From 449ec07bf0631916d8df883855d93e5ce4c6d326 Mon Sep 17 00:00:00 2001 From: Russell Cloran Date: Tue, 25 Oct 2016 09:38:43 -0700 Subject: [PATCH 5/6] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4e63b6b..acaa22c 100755 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Here's a list of the devices that are currently exposed: * **Rollershutter** - exposed as a garage door * **Fan** - on/off/speed * **Input boolean** - on/off -* **Temperature** - temperature from sensors is exposed +* **Sensors** - temperature, light and humidity sensors ### Scene Support From 851a12be4f6c916c1bc9a8fa587a13fadda7952a Mon Sep 17 00:00:00 2001 From: Russell Cloran Date: Tue, 25 Oct 2016 10:52:58 -0700 Subject: [PATCH 6/6] jshint suggested changes --- accessories/sensor.js | 82 +++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/accessories/sensor.js b/accessories/sensor.js index f9a17db..8fa8b5f 100644 --- a/accessories/sensor.js +++ b/accessories/sensor.js @@ -9,67 +9,67 @@ module.exports = function (oService, oCharacteristic, oCommunicationError) { }; function HomeAssistantSensorFactory(log, data, client) { - if (!entity.attributes) { - return null + if (!data.attributes) { + return null; } - var service, characteristic, transformData - if (entity.attributes.unit_of_measurement == '°C' || entity.attributes.unit_of_measurement == '°F') { - service = Service.TemperatureSensor - characteristic = Characteristic.CurrentTemperature + var service, characteristic, transformData; + if (data.attributes.unit_of_measurement === '°C' || data.attributes.unit_of_measurement === '°F') { + service = Service.TemperatureSensor; + characteristic = Characteristic.CurrentTemperature; transformData = function(data) { - value = parseFloat(data.state) + var value = parseFloat(data.state); // HomeKit only works with Celsius internally - if (data.attributes.unit_of_measurement == '°F') { - value = (value - 32) / 1.8 + if (data.attributes.unit_of_measurement === '°F') { + value = (value - 32) / 1.8; } - return value - } - } else if (entity.attributes.unit_of_measurement == "%" && data.entity_id.includes("humidity")) { - service = Service.HumiditySensor - characteristic = Characteristic.CurrentRelativeHumidity - } else if (entity.attributes.unit_of_measurement == "lux") { - service = Service.LightSensor - characteristic = Characteristic.CurrentAmbientLightLevel + return value; + }; + } else if (data.attributes.unit_of_measurement === "%" && data.entity_id.includes("humidity")) { + service = Service.HumiditySensor; + characteristic = Characteristic.CurrentRelativeHumidity; + } else if (data.attributes.unit_of_measurement === "lux") { + service = Service.LightSensor; + characteristic = Characteristic.CurrentAmbientLightLevel; transformData = function(data) { - return Math.max(0.0001, parseFloat(data.state)) - } + return Math.max(0.0001, parseFloat(data.state)); + }; } else { - return null + return null; } - return new HomeAssistantSensor(log, data, client, service, characteristic, transformData) + return new HomeAssistantSensor(log, data, client, service, characteristic, transformData); } class HomeAssistantSensor { constructor(log, data, client, service, characteristic, transformData) { // device info - this.data = data - this.entity_id = data.entity_id + this.data = data; + this.entity_id = data.entity_id; if (data.attributes && data.attributes.friendly_name) { - this.name = data.attributes.friendly_name + this.name = data.attributes.friendly_name; }else{ - this.name = data.entity_id.split('.').pop().replace(/_/g, ' ') + this.name = data.entity_id.split('.').pop().replace(/_/g, ' '); } - this.entity_type = data.entity_id.split('.')[0] + this.entity_type = data.entity_id.split('.')[0]; - this.client = client - this.log = log + this.client = client; + this.log = log; - this.service = service - this.characteristic = characteristic + this.service = service; + this.characteristic = characteristic; if (transformData) { - this.transformData = transformData + this.transformData = transformData; } } transformData(data) { - return parseFloat(data.state) + return parseFloat(data.state); } onEvent(old_state, new_state) { this.sensorService.getCharacteristic(this.characteristic) - .setValue(this.transformData(new_state), null, 'internal') + .setValue(this.transformData(new_state), null, 'internal'); } identify(callback){ @@ -79,27 +79,27 @@ class HomeAssistantSensor { getState(callback){ this.log("fetching state for: " + this.name); - this.client.fetchState(this.entity_id, function(data){ + this.client.fetchState(this.entity_id, function(data) { if (data) { - callback(null, this.transformData(data)) + callback(null, this.transformData(data)); }else{ - callback(communicationError) + callback(communicationError); } - }.bind(this)) + }.bind(this)); } getServices() { - this.sensorService = new this.service() - var informationService = new Service.AccessoryInformation() + this.sensorService = new this.service(); + var informationService = new Service.AccessoryInformation(); informationService .setCharacteristic(Characteristic.Manufacturer, "Home Assistant") .setCharacteristic(Characteristic.Model, "Sensor") - .setCharacteristic(Characteristic.SerialNumber, "xxx") + .setCharacteristic(Characteristic.SerialNumber, "xxx"); this.sensorService .getCharacteristic(this.characteristic) - .on('get', this.getState.bind(this)) + .on('get', this.getState.bind(this)); return [informationService, this.sensorService]; }