From c897913005bf3a0f19ae77c72d3918c346c262cd Mon Sep 17 00:00:00 2001 From: Kraig McConaghy Date: Sat, 17 Oct 2015 20:51:57 -0500 Subject: [PATCH] Initial work. Still in progress --- platforms/Nest.js | 488 ++++++++++++++++++++-------------------------- 1 file changed, 211 insertions(+), 277 deletions(-) diff --git a/platforms/Nest.js b/platforms/Nest.js index c1ef3bd..8803b22 100644 --- a/platforms/Nest.js +++ b/platforms/Nest.js @@ -1,5 +1,11 @@ var types = require("HAP-NodeJS/accessories/types.js"); var nest = require('unofficial-nest-api'); +var Service = require("hap-nodejs").Service; +var Characteristic = require("hap-nodejs").Characteristic; +var Accessory = require("hap-nodejs").Accessory; +var uuid = require("hap-nodejs").uuid; +var inherits = require('util').inherits; + function NestPlatform(log, config){ @@ -29,8 +35,9 @@ NestPlatform.prototype = { // it's a thermostat, adjust this to detect other accessories if (data.shared[deviceId].hasOwnProperty('current_temperature')) { - var name = data.shared[deviceId].name - var accessory = new NestThermostatAccessory(that.log, name, device, deviceId); + var initialData = data.shared[deviceId]; + var name = initialData.name + var accessory = new NestThermostatAccessory(that.log, name, device, deviceId, initialData); foundAccessories.push(accessory); } } @@ -42,7 +49,7 @@ NestPlatform.prototype = { } } -function NestThermostatAccessory(log, name, device, deviceId) { +function NestThermostatAccessory(log, name, device, deviceId, initialData) { // device info if (name) { this.name = name; @@ -53,124 +60,226 @@ function NestThermostatAccessory(log, name, device, deviceId) { this.serial = device.serial_number; this.deviceId = deviceId; this.log = log; + Accessory.call(this, name, uuid.generate(deviceId)); + + this.getService(Service.AccessoryInformation) + .setCharacteristic(Characteristic.Manufacturer, "Nest") + .setCharacteristic(Characteristic.Model, this.model) + .setCharacteristic(Characteristic.SerialNumber, this.serial); + + this.addService(Service.Thermostat, name); + + this.getService(Service.Thermostat) + .setCharacteristic(Characteristic.TemperatureDisplayUnits, this.extractTemperatureUnits(device)) + .on('get', this.getTemperatureUnits); + + this.getService(Service.Thermostat) + .setCharacteristic(Characteristic.CurrentTemperature, this.extractCurrentTemperature(device)) + //.getCharacteristic(Characteristic.CurrentTemperature) + .on('get', this.getCurrentTemperature); + + this.getService(Service.Thermostat) + .setCharacteristic(Characteristic.TargetTemperature, this.extractTargetTemperature(device)) + .on('get', this.getTargetTemperature) + .on('set', this.setTargetTemperature); + + this.getService(Service.Thermostat) + .setCharacteristic(Characteristic.CurrentHeatingCoolingState, this.extractCurrentHeatingCooling(device)) + .on('get', this.getCurrentHeatingCooling); + + this.getService(Service.Thermostat) + .setCharacteristic(Characteristic.TargetHeatingCoolingState, this.extractTargetHeatingCooling(device)) + .on('get', this.getTargetHeatingCoooling) + .on('set', this.setTargetHeatingCooling); + + //this.getService(Service.Thermostat) + // .getCharacteristic(Characteristic.CurrentRelativeHumidity) + // .on('get', function(callback) { + // that.getCurrentRelativeHumidity(function(currentRelativeHumidity){ + // callback(currentRelativeHumidity); + // }); + // }); + + + } - -NestThermostatAccessory.prototype = { - getCurrentHeatingCooling: function(callback){ - - var that = this; - - this.log("Checking current heating cooling for: " + this.name); - nest.fetchStatus(function (data) { - var device = data.device[that.deviceId]; - - var currentHeatingCooling = 0; - switch(device.current_schedule_mode) { - case "OFF": - targetHeatingCooling = 0; - break; - case "HEAT": - currentHeatingCooling = 1; - break; - case "COOL": - currentHeatingCooling = 2; - break; - case "RANGE": - currentHeatingCooling = 3; - break; - default: - currentHeatingCooling = 0; +inherits(NestThermostatAccessory, Accessory); +//NestThermostatAccessory.prototype.parent = Accessory.prototype; +Service.prototype.getCharacteristic = function(name) { + // returns a characteristic object from the service + // If Service.prototype.getCharacteristic(Characteristic.Type) does not find the characteristic, + // but the type is in optionalCharacteristics, it adds the characteristic.type to the service and returns it. + var index, characteristic; + for (index in this.characteristics) { + characteristic = this.characteristics[index]; + if (typeof name === 'string' && characteristic.displayName === name) { + return characteristic; + } + else if (typeof name === 'function' && characteristic instanceof name) { + return characteristic; + } + } + if (typeof name === 'function') { + for (index in this.optionalCharacteristics) { + characteristic = this.optionalCharacteristics[index]; + if (characteristic instanceof name) { + return this.addCharacteristic(name); } - that.log("Current heating for " + this.name + "is: " + currentHeatingCooling); - callback(currentHeatingCooling); - }); + } + } +}; +NestThermostatAccessory.prototype.getServices = function() { + return this.services; +}; - }, - - getTargetHeatingCoooling: function(callback){ - +NestThermostatAccessory.prototype.extractCurrentHeatingCooling = function(device){ + var currentHeatingCooling = 0; + switch(device.current_schedule_mode) { + case "OFF": + currentHeatingCooling = 0; + break; + case "HEAT": + currentHeatingCooling = 1; + break; + case "COOL": + currentHeatingCooling = 2; + break; + case "RANGE": + currentHeatingCooling = 3; + break; + default: + currentHeatingCooling = 0; + } + this.log("Current heating for " + this.name + "is: " + currentHeatingCooling); + return currentHeatingCooling; +}; +NestThermostatAccessory.prototype.getCurrentHeatingCooling = function(callback){ + var that = this; + this.log("Checking current heating cooling for: " + this.name); + nest.fetchStatus(function (data) { + var device = data.device[that.deviceId]; + var currentHeatingCooling = that.extractCurrentHeatingCooling(device); + callback(currentHeatingCooling); + }); +}; +NestThermostatAccessory.prototype.extractTargetHeatingCooling = function(device){ + var targetHeatingCooling = 0; + switch(device.target_temperature_type) { + case "off": + targetHeatingCooling = 0; + break; + case "heat": + targetHeatingCooling = 1; + break; + case "cool": + targetHeatingCooling = 2; + break; + case "range": + targetHeatingCooling = 3; + break; + default: + targetHeatingCooling = 0; + } + this.log("Current target heating for " + this.name + " is: " + targetHeatingCooling); + return targetHeatingCooling; +}; +NestThermostatAccessory.prototype.getTargetHeatingCoooling = function(callback){ var that = this; - this.log("Checking target heating cooling for: " + this.name); nest.fetchStatus(function (data) { var device = data.device[that.deviceId]; - - var targetHeatingCooling = 0; - switch(device.target_temperature_type) { - case "off": - targetHeatingCooling = 0; - break; - case "heat": - targetHeatingCooling = 1; - break; - case "cool": - targetHeatingCooling = 2; - break; - case "range": - targetHeatingCooling = 3; - break; - default: - targetHeatingCooling = 0; - } - that.log("Current target heating for " + this.name + " is: " + targetHeatingCooling); + var targetHeatingCooling = that.extractTargetHeatingCooling(device); callback(targetHeatingCooling); }); - }, + }; - getCurrentTemperature: function(callback){ +NestThermostatAccessory.prototype.extractCurrentTemperature = function(device){ + var curTemp = this.extractAsDisplayUnit(device.current_temperature, device); + this.log("Current temperature for " + this.name + " is: " + curTemp); + return curTemp; +}; +NestThermostatAccessory.prototype.getCurrentTemperature = function(callback){ var that = this; - nest.fetchStatus(function (data) { var device = data.shared[that.deviceId]; - that.log("Current temperature for " + this.name + " is: " + device.current_temperature); - callback(device.current_temperature); + var curTemp = this.extractCurrentTemperature(device); + callback(curTemp); }); + }; +NestThermostatAccessory.prototype.extractTargetTemperature = function(device){ + var targetTemp; + if (device.target_temperature != undefined) { + targetTemp = device.target_temperature; + } else if (device.temperature_lock_high_temp != undefined) { + targetTemp = device.temperature_lock_high_temp; + } else { + return null; + } - }, - - getTargetTemperature: function(callback){ - + targetTemp = this.extractAsDisplayUnit(targetTemp, device); + this.log("Target temperature for " + this.name + " is: " + targetTemp); + return targetTemp; +}; +NestThermostatAccessory.prototype.getTargetTemperature = function(callback){ var that = this; - nest.fetchStatus(function (data) { var device = data.shared[that.deviceId]; - that.log("Target temperature for " + this.name + " is: " + device.target_temperature); - callback(device.target_temperature); + var targetTemp = this.extractTargetTemperature(device); + callback(targetTemp); }); + }; +NestThermostatAccessory.prototype.extractTemperatureUnits = function(device) { + var temperatureUnits = 0; + switch(device.temperature_scale) { + case "F": + this.log("Tempature unit for " + this.name + " is: " + "Fahrenheit"); + temperatureUnits = 1; + break; + case "C": + this.log("Tempature unit for " + this.name + " is: " + "Celsius"); + temperatureUnits = 0; + break; + default: + temperatureUnits = 0; + } + return temperatureUnits; +}; - }, +NestThermostatAccessory.prototype.isFahrenheitUnit = function(unit) { + return unit == 1; +}; - getTemperatureUnits: function(callback){ +NestThermostatAccessory.prototype.convertToDisplayUnit = function(value, displayUnit) { + return this.isFahrenheitUnit(displayUnit) ? nest.ctof(value) : value; +}; +NestThermostatAccessory.prototype.convertToValueUnit = function(value, displayUnit) { + return this.isFahrenheitUnit(displayUnit) ? nest.ftoc(value) : value; +}; + +NestThermostatAccessory.prototype.extractAsDisplayUnit = function(value, device) { + var tempUnit = this.extractTemperatureUnits(device); + return this.convertToDisplayUnit(value, tempUnit); +}; + +NestThermostatAccessory.prototype.extractAsValueUnit = function(value, device) { + return this.convertToValueUnit(value, this.extractTemperatureUnits(device)); +}; + +NestThermostatAccessory.prototype.getTemperatureUnits = function(callback){ var that = this; - nest.fetchStatus(function (data) { var device = data.device[that.deviceId]; - var temperatureUnits = 0; - switch(device.temperature_scale) { - case "F": - that.log("Tempature unit for " + this.name + " is: " + "Fahrenheit"); - temperatureUnits = 1; - break; - case "C": - that.log("Tempature unit for " + this.name + " is: " + "Celsius"); - temperatureUnits = 0; - break; - default: - temperatureUnits = 0; - } - + var temperatureUnits = that.extractTemperatureUnits(device); callback(temperatureUnits); }); + }; - - }, - - getCurrentRelativeHumidity: function(callback){ +NestThermostatAccessory.prototype.getCurrentRelativeHumidity = function(callback){ var that = this; @@ -181,12 +290,9 @@ NestThermostatAccessory.prototype = { }) - }, - - setTargetHeatingCooling: function(targetHeatingCooling){ - - var that = this; + }; +NestThermostatAccessory.prototype.setTargetHeatingCooling = function(targetHeatingCooling, callback){ var targetTemperatureType = 'off'; switch(targetHeatingCooling) { case 0: @@ -208,190 +314,18 @@ NestThermostatAccessory.prototype = { this.log("Setting target heating cooling for " + this.name + " to: " + targetTemperatureType); nest.setTargetTemperatureType(this.deviceId, targetTemperatureType); - - }, - - setTargetTemperature: function(targetTemperature){ - - var that = this; - - this.log("Setting target temperature for " + this.name + " to: " + targetTemperature); - nest.setTemperature(this.deviceId, targetTemperature); - - - }, - - 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: "Nest", - supportEvents: false, - supportBonjour: false, - manfDescription: "Manufacturer", - designedMaxLength: 255 - },{ - cType: types.MODEL_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.model, - supportEvents: false, - supportBonjour: false, - manfDescription: "Model", - designedMaxLength: 255 - },{ - cType: types.SERIAL_NUMBER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.serial, - supportEvents: false, - supportBonjour: false, - manfDescription: "SN", - designedMaxLength: 255 - },{ - cType: types.IDENTIFY_CTYPE, - onUpdate: null, - perms: ["pw"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Identify Accessory", - designedMaxLength: 1 - }] - },{ - sType: types.THERMOSTAT_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of thermostat", - designedMaxLength: 255 - },{ - cType: types.CURRENTHEATINGCOOLING_CTYPE, - onUpdate: null, - onRead: function(callback) { - that.getCurrentHeatingCooling(function(currentHeatingCooling){ - callback(currentHeatingCooling); - }); - }, - perms: ["pr","ev"], - format: "int", - initialValue: 0, - supportEvents: false, - supportBonjour: false, - manfDescription: "Current Mode", - designedMaxLength: 1, - designedMinValue: 0, - designedMaxValue: 2, - designedMinStep: 1, - },{ - cType: types.TARGETHEATINGCOOLING_CTYPE, - onUpdate: function(value) { - that.setTargetHeatingCooling(value); - }, - onRead: function(callback) { - that.getTargetHeatingCoooling(function(targetHeatingCooling){ - callback(targetHeatingCooling); - }); - }, - perms: ["pw","pr","ev"], - format: "int", - initialValue: 0, - supportEvents: false, - supportBonjour: false, - manfDescription: "Target Mode", - designedMinValue: 0, - designedMaxValue: 3, - designedMinStep: 1, - },{ - cType: types.CURRENT_TEMPERATURE_CTYPE, - onUpdate: null, - onRead: function(callback) { - that.getCurrentTemperature(function(currentTemperature){ - callback(currentTemperature); - }); - }, - perms: ["pr","ev"], - format: "int", - initialValue: 20, - supportEvents: false, - supportBonjour: false, - manfDescription: "Current Temperature", - unit: "celsius" - },{ - cType: types.TARGET_TEMPERATURE_CTYPE, - onUpdate: function(value) { - that.setTargetTemperature(value); - }, - onRead: function(callback) { - that.getTargetTemperature(function(targetTemperature){ - callback(targetTemperature); - }); - }, - perms: ["pw","pr","ev"], - format: "int", - initialValue: 20, - supportEvents: false, - supportBonjour: false, - manfDescription: "Target Temperature", - designedMinValue: 16, - designedMaxValue: 38, - designedMinStep: 1, - unit: "celsius" - },{ - cType: types.TEMPERATURE_UNITS_CTYPE, - onUpdate: null, - onRead: function(callback) { - that.getTemperatureUnits(function(temperatureUnits){ - callback(temperatureUnits); - }); - }, - perms: ["pr","ev"], - format: "int", - initialValue: 0, - supportEvents: false, - supportBonjour: false, - manfDescription: "Unit", - },{ - cType: types.CURRENT_RELATIVE_HUMIDITY_CTYPE, - onUpdate: null, - onRead: function(callback) { - that.getCurrentRelativeHumidity(function(currentRelativeHumidity){ - callback(currentRelativeHumidity); - }); - }, - perms: ["pr","ev"], - format: "int", - initialValue: 0, - supportEvents: false, - supportBonjour: false, - manfDescription: "Humidity", - }] - }]; + if (callback) { + callback(); } -} +}; + +NestThermostatAccessory.prototype.setTargetTemperature = function(targetTemperature, callback){ + this.log("Setting target temperature for " + this.name + " to: " + targetTemperature); + nest.setTemperature(this.deviceId, targetTemperature); + if (callback) { + callback(); + } +}; module.exports.accessory = NestThermostatAccessory; module.exports.platform = NestPlatform;