From 48bec8a1bc805cceafd589e277d74283e5817840 Mon Sep 17 00:00:00 2001 From: Stefan Kuper Date: Sat, 31 Oct 2015 14:56:24 +0100 Subject: [PATCH 1/7] new API and added noise and atmospheric pressure --- platforms/Netatmo.js | 455 +++++++++++++++++++++---------------------- 1 file changed, 225 insertions(+), 230 deletions(-) diff --git a/platforms/Netatmo.js b/platforms/Netatmo.js index fe532bd..ca10f10 100644 --- a/platforms/Netatmo.js +++ b/platforms/Netatmo.js @@ -8,6 +8,7 @@ // { // "platform": "Netatmo", // "name": "Netatmo Weather", +// "ttl": 10, // "auth": { // "client_id": "", // "client_secret": "", @@ -19,32 +20,99 @@ // // The default code for all HomeBridge accessories is 031-45-154. -var types = require("hap-nodejs/accessories/types.js"); +var DEFAULT_CACHE_TTL = 10; // 10 seconds caching - use config["ttl"] to override -////////////////////////////////////////////////////////////////////////////// -// DECLARE SOME UUIDS WHICH SHOUL BE IN HAP-NODEJS TYPES LIB, BUT ARE NOT YET -// REMOVE WHEN HAP LIB IS UPDATED!! -////////////////////////////////////////////////////////////////////////////// -var stPre = "000000"; -var stPost = "-0000-1000-8000-0026BB765291"; +// CUSTOM SERVICE AND CHARACTERISTIC IDS +var ATMOSPHERIC_PRESSURE_STYPE_ID = "B77831FD-D66A-46A4-B66D-FD7EE8DFE3CE"; +var ATMOSPHERIC_PRESSURE_CTYPE_ID = "28FDA6BC-9C2A-4DEA-AAFD-B49DB6D155AB"; +var NOISE_LEVEL_STYPE_ID = "8C85FD40-EB20-45EE-86C5-BCADC773E580"; +var NOISE_LEVEL_CTYPE_ID = "2CD7B6FD-419A-4740-8995-E3BFE43735AB"; -types.BATTERY_SERVICE_STYPE = stPre + "96" + stPost; -types.AIR_QUALITY_SENSOR_STYPE = stPre + "8D" + stPost; -types.CARBON_DIOXIDE_SENSOR_STYPE = stPre + "97" + stPost; +var Service; +try { + Service = require("hap-nodejs").Service; +} catch(err) { + Service = require("HAP-NodeJS").Service; +} -types.AIR_PARTICULATE_DENISITY_CTYPE = stPre + "64" + stPost; -types.CARBON_DIOXIDE_DETECTED_CTYPE = stPre + "92" + stPost; -types.CARBON_DIOXIDE_LEVEL_CTYPE = stPre + "93" + stPost; -types.AIR_QUALITY_CTYPE = stPre + "95" + stPost; -////////////////////////////////////////////////////////////////////////////// +var Characteristic; +try { + Characteristic = require("hap-nodejs").Characteristic; +} catch(err) { + Characteristic = require("HAP-NodeJS").Characteristic; +} var netatmo = require("netatmo"); var NodeCache = require("node-cache"); +var inherits = require('util').inherits; -function NetAtmoRepository(log, api) { +Characteristic.AtmosphericPressureLevel = function() { + Characteristic.call(this, 'Atmospheric Pressure', ATMOSPHERIC_PRESSURE_CTYPE_ID); + this.setProps({ + format: Characteristic.Formats.UINT8, + unit: "mbar", + minValue: 800, + maxValue: 1200, + minStep: 1, + perms: [ + Characteristic.Perms.READ, + Characteristic.Perms.NOTIFY + ] + }); + this.value = this.getDefaultValue(); +}; +inherits(Characteristic.AtmosphericPressureLevel, Characteristic); +Characteristic.NoiseLevel = function() { + Characteristic.call(this, 'Noise Level', NOISE_LEVEL_CTYPE_ID); + this.setProps({ + format: Characteristic.Formats.UINT8, + unit: "dB", + minValue: 0, + maxValue: 200, + minStep: 1, + perms: [ + Characteristic.Perms.READ, + Characteristic.Perms.NOTIFY + ] + }); + this.value = this.getDefaultValue(); +}; +inherits(Characteristic.NoiseLevel, Characteristic); + +Service.AtmosphericPressureSensor = function(displayName, subtype) { + Service.call(this, displayName, ATMOSPHERIC_PRESSURE_STYPE_ID, subtype); + + // Required Characteristics + this.addCharacteristic(Characteristic.AtmosphericPressureLevel); + + // Optional Characteristics + this.addOptionalCharacteristic(Characteristic.StatusActive); + this.addOptionalCharacteristic(Characteristic.StatusFault); + this.addOptionalCharacteristic(Characteristic.StatusLowBattery); + this.addOptionalCharacteristic(Characteristic.StatusTampered); + this.addOptionalCharacteristic(Characteristic.Name); +}; +inherits(Service.AtmosphericPressureSensor, Service); + +Service.NoiseLevelSensor = function(displayName, subtype) { + Service.call(this, displayName, NOISE_LEVEL_STYPE_ID, subtype); + + // Required Characteristics + this.addCharacteristic(Characteristic.NoiseLevel); + + // Optional Characteristics + this.addOptionalCharacteristic(Characteristic.StatusActive); + this.addOptionalCharacteristic(Characteristic.StatusFault); + this.addOptionalCharacteristic(Characteristic.StatusLowBattery); + this.addOptionalCharacteristic(Characteristic.StatusTampered); + this.addOptionalCharacteristic(Characteristic.Name); +}; +inherits(Service.NoiseLevelSensor, Service); + +function NetAtmoRepository(log, api, ttl) { this.api = api; this.log = log; - this.cache = new NodeCache(); + this.cache = new NodeCache( { stdTTL: ttl } ); } NetAtmoRepository.prototype = { @@ -71,7 +139,7 @@ NetAtmoRepository.prototype = { } } - that.cache.set( "datasource", datasource, 20 ); + that.cache.set( "datasource", datasource ); callback(datasource); }); }, @@ -92,7 +160,8 @@ NetAtmoRepository.prototype = { function NetatmoPlatform(log, config) { this.log = log; var api = new netatmo(config["auth"]); - this.repository = new NetAtmoRepository(this.log, api); + var ttl = typeof config["ttl"] !== 'undefined' ? config["ttl"] : DEFAULT_CACHE_TTL; + this.repository = new NetAtmoRepository(this.log, api, ttl); api.on("error", function(error) { this.log('ERROR - Netatmo: ' + error); }); @@ -124,7 +193,7 @@ function NetatmoAccessory(log, repository, device) { this.deviceId = device._id; this.name = device.module_name this.serial = device._id; - + this.firmware = device.firmware; this.model = device.type; this.serviceTypes = device.data_type; if (device.battery_vp) { @@ -141,243 +210,169 @@ NetatmoAccessory.prototype = { }); }, - getCurrentTemperature: function(callback) { + identify: function(callback) { + this.log("Identify requested!"); + callback(); // success + }, + + currentTemperature: function (callback) { this.getData(function(deviceData) { - callback(deviceData.dashboard_data.Temperature); - }); - }, +/* + if (error) { + callback(error); + } else { +*/ + callback(null, deviceData.dashboard_data.Temperature); + }.bind(this)); + }, - getCurrentHumidity: function(callback) { + currentRelativeHumidity: function(callback) { this.getData(function(deviceData) { - callback(deviceData.dashboard_data.Humidity); - }); + callback(null, deviceData.dashboard_data.Humidity); + }.bind(this)); }, - getAirQuality: function(callback) { + carbonDioxideDetected: function(callback) { + var that = this; + that.log ("getting CO2" + Characteristic.CarbonDioxideDetected.CO2_LEVELS_ABNORMAL); + this.getData(function(deviceData) { - var level = deviceData.dashboard_data.CO2; - var quality = 0; - if (level > 2000) quality = 5; - else if (level > 1500) quality = 4; - else if (level > 1000) quality = 3; - else if (level > 500) quality = 2; - else if (level > 250) quality = 1; - callback(quality); - }); + var result = (deviceData.dashboard_data.CO2 > 1000 ? Characteristic.CarbonDioxideDetected.CO2_LEVELS_ABNORMAL : Characteristic.CarbonDioxideDetected.CO2_LEVELS_NORMAL); + callback(null, result); + }.bind(this)); }, - getCurrentCO2Level: function(callback) { - this.log("fetching co2"); + + carbonDioxideLevel: function(callback) { this.getData(function(deviceData) { - callback(deviceData.dashboard_data.CO2); - }); + callback(null, deviceData.dashboard_data.CO2); + }.bind(this)); }, - informationCharacteristics: function() { - return [ - { - 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: "Netatmo", - 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 - } - ] + airQuality: function(callback) { + this.getData(function(deviceData) { + var level = deviceData.dashboard_data.CO2; + var quality = Characteristic.AirQuality.UNKNOWN; + if (level > 2000) quality = Characteristic.AirQuality.POOR; + else if (level > 1500) quality = Characteristic.AirQuality.INFERIOR; + else if (level > 1000) quality = Characteristic.AirQuality.FAIR; + else if (level > 500) quality = Characteristic.AirQuality.GOOD; + else if (level > 250) quality = Characteristic.AirQuality.EXCELLENT; + callback(null, quality); + }.bind(this)); }, - humidityCharacteristics: function(that) { - var cTypes = [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name +" Humidity", - supportEvents: true, - supportBonjour: false, - manfDescription: "Name of service", - designedMaxLength: 255 - },{ - cType: types.CURRENT_RELATIVE_HUMIDITY_CTYPE, - onRead: function(callback) { that.getCurrentHumidity(callback); }, - onUpdate: null, - perms: ["pr","ev"], - format: "int", - initialValue: 0, - supportEvents: false, - supportBonjour: false, - manfDescription: "Humidity" - }]; - return cTypes; + batteryLevel: function(callback) { + this.getData(function(deviceData) { + var charge = deviceData.battery_vp; + var level = charge < 3000 ? 0 : (charge - 3000)/30; + callback(null, level); + }.bind(this)); }, - temperatureCharacteristics: function(that) { - var cTypes = [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name + " Temperature", - supportEvents: true, - supportBonjour: false, - manfDescription: "Name of service", - designedMaxLength: 255 - },{ - cType: types.CURRENT_TEMPERATURE_CTYPE, - onRead: function(callback) { that.getCurrentTemperature(callback); }, - onUpdate: null, - perms: ["pr","ev"], - format: "float", - initialValue: 0.0, - supportEvents: false, - supportBonjour: false, - manfDescription: "Current Temperature", - unit: "celsius" - }]; - return cTypes; + statusLowBattery: function(callback) { + this.getData(function(deviceData) { + var charge = deviceData.battery_vp; + var level = charge < 4600 ? Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW : Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + callback(null, level); + }.bind(this)); }, - co2Characteristics: function(that) { - var cTypes = [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name + "Carbon Dioxide Level", - supportEvents: true, - supportBonjour: false, - manfDescription: "Name of service", - designedMaxLength: 255 - },{ - cType: types.CARBON_DIOXIDE_DETECTED_CTYPE, - //onRead: function(callback) { that.getCurrentTemperature(callback); }, - onRead: function(callback) { callback(0); }, - onUpdate: null, - perms: ["pr","ev"], - format: "uint8", - initialValue: 0, - supportEvents: false, - supportBonjour: false, - manfDescription: "CO2 detected" - },{ - cType: types.CARBON_DIOXIDE_LEVEL_CTYPE, - onRead: function(callback) { that.getCurrentCO2Level(callback); }, - onUpdate: null, - perms: ["pr","ev"], - format: "float", - initialValue: 0, - supportEvents: false, - supportBonjour: false, - manfDescription: "CO2 level " - }]; - return cTypes; + atmosphericPressure: function(callback) { + this.getData(function(deviceData) { + callback(null, deviceData.dashboard_data.Pressure); + }.bind(this)); }, - airQualityCharacteristics: function(that) { - var cTypes = [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name + " Air Quality", - supportEvents: true, - supportBonjour: false, - manfDescription: "Name of service", - designedMaxLength: 255 - },{ - cType: types.AIR_QUALITY_CTYPE, - onRead: function(callback) { that.getAirQuality(callback); }, - onUpdate: null, - perms: ["pr","ev"], - format: "float", - initialValue: 0, - supportEvents: false, - supportBonjour: false, - manfDescription: "Current Air Quality", - }]; - return cTypes; + noiseLevel: function(callback) { + this.getData(function(deviceData) { + callback(null, deviceData.dashboard_data.Noise); + }.bind(this)); }, - getServices: function() { var that = this; - var services = [{ - sType: types.ACCESSORY_INFORMATION_STYPE, - characteristics: this.informationCharacteristics(), - }]; + var services = []; + + this.log("creating services for " + this.name) + + // INFORMATION /////////////////////////////////////////////////// + + var informationService = new Service.AccessoryInformation(); + var firmwareCharacteristic = informationService.getCharacteristic(Characteristic.FirmwareRevision) + || informationService.addCharacteristic(Characteristic.FirmwareRevision); + services.push( informationService ); + + informationService + .setCharacteristic(Characteristic.Manufacturer, "Netatmo") + .setCharacteristic(Characteristic.Model, this.model) + .setCharacteristic(Characteristic.Name, this.name) + .setCharacteristic(Characteristic.SerialNumber, this.serial) + .setCharacteristic(Characteristic.FirmwareRevision, this.firmware); // TEMPERATURE ////////////////////////////////////////////////// if (this.serviceTypes.indexOf("Temperature") > -1) { - var tempSensorSvc = { - sType: types.TEMPERATURE_SENSOR_STYPE, - characteristics: this.temperatureCharacteristics(that) - } - services.push(tempSensorSvc); - } - // HUMIDITY //////////////////////////////////////////////////// - if (this.serviceTypes.indexOf("Humidity") > -1) { - services.push({ - sType: types.HUMIDITY_SENSOR_STYPE, - characteristics: this.humidityCharacteristics(that) - }); - } - // CO2 SENSOR ///////////////////////////////////////////////// - if (this.serviceTypes.indexOf("CO2") > -1) { - services.push({ - sType: types.CARBON_DIOXIDE_SENSOR_STYPE, - characteristics: this.co2Characteristics(that) - }); - services.push({ - sType: types.AIR_QUALITY_SENSOR_STYPE, - characteristics: this.airQualityCharacteristics(that) - }); + var temperatureSensor = new Service.TemperatureSensor(this.name + " Temperature"); + services.push( temperatureSensor ); + temperatureSensor.getCharacteristic(Characteristic.CurrentTemperature) + .on('get', this.currentTemperature.bind(this)); } - // TODO: Pressure - // TODO: Noise - // TODO: Battery + // HUMIDITY //////////////////////////////////////////////////// + if (this.serviceTypes.indexOf("Humidity") > -1) { + var humiditySensor = new Service.HumiditySensor(this.name + " Humidity"); + services.push( humiditySensor ); + humiditySensor.getCharacteristic(Characteristic.CurrentRelativeHumidity) + .on('get', this.currentRelativeHumidity.bind(this)); + } + + + // CO2 SENSOR ///////////////////////////////////////////////// + if (this.serviceTypes.indexOf("CO2") > -1) { + var carbonDioxideSensor = new Service.CarbonDioxideSensor(this.name + " Carbon Dioxide"); + var carbonDioxideLevelCharacteristic = carbonDioxideSensor.getCharacteristic(Characteristic.CarbonDioxideLevel) + || carbonDioxideSensor.addCharacteristic(Characteristic.CarbonDioxideLevel); + + services.push( carbonDioxideSensor ); + carbonDioxideSensor.getCharacteristic(Characteristic.CarbonDioxideDetected) + .on('get', this.carbonDioxideDetected.bind(this)); + carbonDioxideLevelCharacteristic + .on('get', this.carbonDioxideLevel.bind(this)); + + var airQualitySensor = new Service.AirQualitySensor(this.name + " Air Quality"); + services.push( airQualitySensor ); + airQualitySensor.getCharacteristic(Characteristic.AirQuality) + .on('get', this.airQuality.bind(this)); + } + + // BATTERY SERVICE //////////////////////////////////////////// + + if (this.serviceTypes.indexOf("Battery") > -1) { + var batteryService = new Service.BatteryService(this.name + " Battery Level"); + services.push( batteryService ); + batteryService.getCharacteristic(Characteristic.BatteryLevel) + .on('get', this.batteryLevel.bind(this)); + batteryService.getCharacteristic(Characteristic.StatusLowBattery) + .on('get', this.statusLowBattery.bind(this)); + } + + // ATMOSPHERIC PRESSURE ///////////////////////////////////////////////////// + + if (this.serviceTypes.indexOf("Pressure") > -1) { + var atmosphericPressureSensor = new Service.AtmosphericPressureSensor(this.name + " Atmospheric Pressure"); + services.push( atmosphericPressureSensor ); + atmosphericPressureSensor.getCharacteristic(Characteristic.AtmosphericPressureLevel) + .on('get', this.atmosphericPressure.bind(this)); + } + + // NOISE LEVEL ////////////////////////////////////////////////////////////// + + if (this.serviceTypes.indexOf("Noise") > -1) { + var noiseLevelSensor = new Service.NoiseLevelSensor(this.name + " Noise Level"); + services.push( noiseLevelSensor ); + noiseLevelSensor.getCharacteristic(Characteristic.NoiseLevel) + .on('get', this.noiseLevel.bind(this)); + } + // TODO: Check Elgato Eve Characteristics (map min, max, time series, etc.)! return services; From 3b19613528b249e2fa2c1d18986883da844e5453 Mon Sep 17 00:00:00 2001 From: stipus Date: Thu, 29 Oct 2015 12:16:05 +0100 Subject: [PATCH 2/7] SecuritySystem,Window, WindowCovering, obstruction --- platforms/HomeSeer.js | 151 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 147 insertions(+), 4 deletions(-) diff --git a/platforms/HomeSeer.js b/platforms/HomeSeer.js index 7c899f8..b4fa36d 100644 --- a/platforms/HomeSeer.js +++ b/platforms/HomeSeer.js @@ -29,12 +29,22 @@ // - Added offEventGroup && offEventName to events (turn on launches one HS event. turn off can launch another HS event) // - Added GarageDoorOpener support // - Added Lock support +// V0.10 - 2015/10/29 +// - Added Security System support +// - Added Window support +// - Added Window Covering support +// - Added obstruction support to doors, windows, and windowCoverings // // // Remember to add platform to config.json. // // You can get HomeSeer Device References by clicking a HomeSeer device name, then -// choosing the Advanced Tab. +// choosing the Advanced Tab. +// +// The uuid_base parameter is valid for all events and accessories. +// If you set this parameter to some unique identifier, the HomeKit accessory ID will be based on uuid_base instead of the accessory name. +// It is then easier to change the accessory name without messing the HomeKit database. +// // // Example: // "platforms": [ @@ -148,10 +158,31 @@ // "lockValue":1 // Required - HomeSeer device control value to lock // }, // { +// "ref":230, // Required - HomeSeer Device Reference of a Security System +// "type":"SecuritySystem", // Required for a security system +// "name":"Home alarm", // Optional - HomeSeer device name is the default +// "armedStayValues":[0], // Optional - List of the HomeSeer device values for a HomeKit security state=ARMED-STAY +// "armedAwayValues":[1], // Optional - List of the HomeSeer device values for a HomeKit security state=ARMED-AWAY +// "armedNightValues":[2], // Optional - List of the HomeSeer device values for a HomeKit security state=ARMED-NIGHT +// "disarmedValues":[3], // Optional - List of the HomeSeer device values for a HomeKit security state=DISARMED +// "alarmValues":[4], // Optional - List of the HomeSeer device values for a HomeKit security state=ALARM +// "armStayValue":0, // Required - HomeSeer device control value to arm in stay mode. If you don't have this mode, select any value that arms your system +// "armAwayValue":1, // Required - HomeSeer device control value to arm in away mode. If you don't have this mode, select any value that arms your system +// "armNightValue":2, // Required - HomeSeer device control value to arm in night mode. If you don't have this mode, select any value that arms your system +// "disarmValue":3 // Required - HomeSeer device control value to disarm security system +// }, +// { // "ref":115, // Required - HomeSeer Device Reference for a device holding battery level (0-100) // "type":"Battery", // Required for a Battery // "name":"Roomba battery", // Optional - HomeSeer device name is the default // "batteryThreshold":15 // Optional - If the level is below this value, the HomeKit LowBattery characteristic is set to 1. Default is 10 +// }, +// { +// "ref":240, // Required - HomeSeer Device Reference for a door - HomeSeer values must go from 0 (closed) to 100 (open) +// "type":"Door", // Required for a Door +// "name":"Main door", // Optional - HomeSeer device name is the default +// "obstructionRef":241, // Optional - HomeSeer device reference for your door obstruction state (can be the same as ref) +// "obstructionValues":[1] // Optional - List of the HomeSeer device values for a HomeKit obstruction state=OBSTRUCTION // } // ] // } @@ -177,8 +208,10 @@ // - Battery (batteryThreshold option) // - GarageDoorOpener (state, control, obstruction, lock options) // - Lock (unsecured, secured, jammed options) -// - Door - +// - SecuritySystem (arm, disarm options) +// - Door (obstruction option) +// - Window (obstruction option) +// - WindowCovering (obstruction option) var Service = require("hap-nodejs").Service; var Characteristic = require("hap-nodejs").Characteristic; @@ -672,7 +705,7 @@ HomeSeerAccessory.prototype = { }, setLockTargetState: function(state, callback) { - this.log("Setting target lock state state to %s", state); + this.log("Setting target lock state to %s", state); var ref = this.config.lockRef; var value = 0; @@ -694,6 +727,62 @@ HomeSeerAccessory.prototype = { }.bind(this)); }, + getSecuritySystemCurrentState: function(callback) { + var url = this.access_url + "request=getstatus&ref=" + this.ref; + + httpRequest(url, 'GET', function(error, response, body) { + if (error) { + this.log('HomeSeer get security system current state function failed: %s', error.message); + callback( error, 3 ); + } + else { + var status = JSON.parse( body ); + var value = status.Devices[0].value; + + this.log('HomeSeer get security system current state function succeeded: value=' + value ); + if( this.config.armedStayValues && this.config.armedStayValues.indexOf(value) != -1 ) + callback( null, 0 ); + else if( this.config.armedAwayValues && this.config.armedAwayValues.indexOf(value) != -1 ) + callback( null, 1 ); + else if( this.config.armedNightValues && this.config.armedNightValues.indexOf(value) != -1 ) + callback( null, 2 ); + else if( this.config.disarmedValues && this.config.disarmedValues.indexOf(value) != -1 ) + callback( null, 3 ); + else if( this.config.alarmValues && this.config.alarmValues.indexOf(value) != -1 ) + callback( null, 4 ); + else + callback( null, 0 ); + } + }.bind(this)); + }, + + setSecuritySystemTargetState: function(state, callback) { + this.log("Setting security system state to %s", state); + + var value = 0; + if( state == 0 && this.config.armStayValue ) + value = this.config.armStayValue; + else if( state == 1 && this.config.armAwayValue ) + value = this.config.armAwayValue; + else if( state == 2 && this.config.armNightValue ) + value = this.config.armNightValue; + else if( state == 3 && this.config.disarmValue ) + value = this.config.disarmValue; + + var url = this.access_url + "request=controldevicebyvalue&ref=" + this.ref + "&value=" + value; + httpRequest(url, 'GET', function(error, response, body) { + if (error) { + this.log('HomeSeer set target security system state function failed: %s', error.message); + callback(error); + } + else { + this.log('HomeSeer set target security system state function succeeded!'); + callback(); + } + }.bind(this)); + }, + + getPositionState: function(callback) { callback( null, 2 ); // Temporarily return STOPPED. TODO: full door support }, @@ -897,9 +986,52 @@ HomeSeerAccessory.prototype = { doorService .getCharacteristic(Characteristic.PositionState) .on('get', this.getPositionState.bind(this)); + if( this.config.obstructionRef ) { + doorService + .addCharacteristic(new Characteristic.ObstructionDetected()) + .on('get', this.getObstructionDetected.bind(this)); + } services.push( doorService ); break; } + case "Window": { + var windowService = new Service.Window(); + windowService + .getCharacteristic(Characteristic.CurrentPosition) + .on('get', this.getValue.bind(this)); + windowService + .getCharacteristic(Characteristic.TargetPosition) + .on('set', this.setValue.bind(this)); + windowService + .getCharacteristic(Characteristic.PositionState) + .on('get', this.getPositionState.bind(this)); + if( this.config.obstructionRef ) { + windowService + .addCharacteristic(new Characteristic.ObstructionDetected()) + .on('get', this.getObstructionDetected.bind(this)); + } + services.push( windowService ); + break; + } + case "WindowCovering": { + var windowCoveringService = new Service.WindowCovering(); + windowCoveringService + .getCharacteristic(Characteristic.CurrentPosition) + .on('get', this.getValue.bind(this)); + windowCoveringService + .getCharacteristic(Characteristic.TargetPosition) + .on('set', this.setValue.bind(this)); + windowCoveringService + .getCharacteristic(Characteristic.PositionState) + .on('get', this.getPositionState.bind(this)); + services.push( windowCoveringService ); + if( this.config.obstructionRef ) { + windowCoveringService + .addCharacteristic(new Characteristic.ObstructionDetected()) + .on('get', this.getObstructionDetected.bind(this)); + } + break; + } case "Battery": { this.config.batteryRef = this.ref; var batteryService = new Service.BatteryService(); @@ -974,6 +1106,17 @@ HomeSeerAccessory.prototype = { services.push( lockService ); break; } + case "SecuritySystem": { + var securitySystemService = new Service.SecuritySystem(); + securitySystemService + .getCharacteristic(Characteristic.SecuritySystemCurrentState) + .on('get', this.getSecuritySystemCurrentState.bind(this)); + securitySystemService + .getCharacteristic(Characteristic.SecuritySystemTargetState) + .on('set', this.setSecuritySystemTargetState.bind(this)); + services.push( securitySystemService ); + break; + } default:{ var lightbulbService = new Service.Lightbulb(); From b277b76c39b702e56ed4682e8f1be9bf35699bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6nig?= Date: Thu, 29 Oct 2015 19:06:47 +0100 Subject: [PATCH 3/7] reintegrated Homematic Platform fork from https://github.com/thkl/homebridge/tree/xmlrpc --- package.json | 1 + platforms/HomeMatic.js | 406 ++++++++++++++++++++++ platforms/HomematicChannel.js | 631 ++++++++++++++++++++++++++++++++++ 3 files changed, 1038 insertions(+) create mode 100644 platforms/HomeMatic.js create mode 100644 platforms/HomematicChannel.js diff --git a/package.json b/package.json index eb9e811..e1de444 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "hap-nodejs": "^0.0.3", "harmonyhubjs-client": "^1.1.6", "harmonyhubjs-discover": "git+https://github.com/swissmanu/harmonyhubjs-discover.git", + "homematic-xmlrpc": "git+https://github.com/hobbyquaker/homematic-xmlrpc", "isy-js": "", "komponist": "0.1.0", "lifx": "git+https://github.com/magicmonkey/lifxjs.git", diff --git a/platforms/HomeMatic.js b/platforms/HomeMatic.js new file mode 100644 index 0000000..b22a0f4 --- /dev/null +++ b/platforms/HomeMatic.js @@ -0,0 +1,406 @@ +// +// Homematic Platform Shim for HomeBridge +// +// V0.1 - 2015/10/29 +// - initial version +// - reintegrated Homematic Platform fork from https://github.com/thkl/homebridge/tree/xmlrpc + + +var types = require("hap-nodejs/accessories/types.js"); +var xmlrpc = require('homematic-xmlrpc') + +var request = require("request"); +var http = require("http"); +var path = require("path"); + +var HomeMaticGenericChannel = require(path.resolve(__dirname, 'HomematicChannel.js')); + + + +function RegaRequest(log,ccuip) { + this.log = log; + this.ccuIP = ccuip; +} + +RegaRequest.prototype = { + + script: function (script, callback) { + + var post_options = { + host: this.ccuIP, + port: '80', + path: '/tclrega.exe', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': script.length + } + }; + + var post_req = http.request(post_options, function(res) { + var data = ""; + res.setEncoding('binary'); + res.on('data', function (chunk) { + data += chunk.toString(); + }); + res.on('end', function () { + var pos = data.lastIndexOf(""); + var response = (data.substring(0, pos)); + callback(response); + }); + }); + + post_req.on('error', function(e) { + callback("{}"); + }); + + post_req.write(script); + post_req.end(); + + + }, + + getValue: function(channel,datapoint,callback) { + var that = this; + + var script = "var d = dom.GetObject(\""+channel+"."+datapoint+"\");if (d){Write(d.State());}"; + //that.log("Rega Request " + script); + var regarequest = this.script(script, function(data) { + that.log("Rega Response" + data); + if (data!=undefined) { + callback(parseFloat(data)); + } + } + ); + }, + + setValue: function(channel,datapoint,value) { + var that = this; + + var script = "var d = dom.GetObject(\""+channel+"."+datapoint+"\");if (d){d.State(\""+value+"\");}"; + //that.log("Rega Request " + script); + var regarequest = this.script(script, function(data) { + }); + } + +} + +function HomematicRPC(log,ccuip,platform) { + this.log = log; + this.ccuip = ccuip; + this.platform = platform; + this.server; + this.client; + this.stopping = false; + this.localIP; +} + +HomematicRPC.prototype= { + + + init:function() { + var that = this; + + var ip = this.getIPAddress(); + if (ip=="0.0.0.0") { + that.log("Can not fetch IP"); + return; + } + + this.localIP = ip; + this.log("Local IP: "+this.localIP) + + this.server = xmlrpc.createServer({ host: this.localIP , port: 9090 }) + + this.server.on('NotFound', function(method, params) { + that.log('Method ' + method + ' does not exist'); + }); + + this.server.on('system.listMethods', function (err, params, callback) { + that.log('Method call params for \'system.listMethods\': ' + params) + callback(null,['system.listMethods', 'system.multicall']); + }); + + + this.server.on('system.multicall', function (err, params, callback) { + params.map(function(events) { + try { + events.map(function(event){ + if ((event["methodName"]=="event") && (event['params'] != undefined)) { + var params = event['params']; + var channel = "BidCos-RF." + params[1]; + var datapoint = params[2]; + var value = params[3]; + that.platform.foundAccessories.map(function(accessory){ + if (accessory.adress == channel) { + accessory.event(datapoint,value); + } + }); + } + }); + } catch(err) {} + }); + callback(null); + }); + + this.log('XML-RPC server listening on port 9090') + this.connect(); + + + process.on('SIGINT', function () { + if (that.stopping) { + return; + } + that.stopping = true; + that.stop(); + }); + + process.on('SIGTERM', function () { + if (that.stopping) { + return; + } + that.stopping = true; + that.stop(); + }); + + }, + + getIPAddress: function() { + var interfaces = require('os').networkInterfaces(); + for (var devName in interfaces) { + var iface = interfaces[devName]; + for (var i = 0; i < iface.length; i++) { + var alias = iface[i]; + if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) + return alias.address; + } + } + return '0.0.0.0'; + }, + + getValue:function(channel,datapoint,callback) { + + var that = this; + if (this.client == undefined) { + that.log("Returning cause client is invalid"); + return; + } + if (channel.indexOf("BidCos-RF.")>-1) { + channel = channel.substr(10); + this.log("Calling rpc getValue"); + this.client.methodCall('getValue', [channel,datapoint], function (error, value) { + callback(value); + }); + return; + } + }, + + setValue:function(channel,datapoint,value) { + + var that = this; + + if (this.client == undefined) return; + + if (channel.indexOf("BidCos-RF.")>-1) { + channel = channel.substr(10); + } + + this.client.methodCall('setValue', [channel,datapoint,value], function (error, value) { + + }); + }, + + connect:function(){ + var that = this; + this.log('Creating Local HTTP Client for CCU RPC Events'); + this.client = xmlrpc.createClient({ host: this.ccuip, port: 2001, path: '/'}); + this.log('CCU RPC Init Call on port 2001'); + this.client.methodCall('init', ['http://'+this.localIP+':9090','homebridge'], function (error, value) { + that.log('CCU Response ....') + }); + }, + + + stop:function() { + this.log("Removing Event Server"); + this.client.methodCall('init', ['http://'+this.localIP+':9090'], function (error, value) { + + }); + setTimeout(process.exit(0), 1000); + } + +} + + +function HomeMaticPlatform(log, config) { + this.log = log; + this.ccuIP = config["ccu_ip"]; + this.filter_device = config["filter_device"]; + this.filter_channel = config["filter_channel"]; + this.outlets = config["outlets"]; + + this.sendQueue = []; + this.timer = 0; + + this.foundAccessories = []; + this.adressesToQuery = []; + + this.xmlrpc = new HomematicRPC(this.log,this.ccuIP,this); + this.xmlrpc.init(); +} + +HomeMaticPlatform.prototype = { + + + + accessories: function(callback) { + this.log("Fetching Homematic devices..."); + var that = this; + that.foundAccessories = []; + + var script = "string sDeviceId;string sChannelId;boolean df = true;Write(\'{\"devices\":[\');foreach(sDeviceId, root.Devices().EnumIDs()){object oDevice = dom.GetObject(sDeviceId);if(oDevice){var oInterface = dom.GetObject(oDevice.Interface());if(df) {df = false;} else { Write(\',\');}Write(\'{\');Write(\'\"id\": \"\' # sDeviceId # \'\",\');Write(\'\"name\": \"\' # oDevice.Name() # \'\",\');Write(\'\"address\": \"\' # oDevice.Address() # \'\",\');Write(\'\"channels\": [\');boolean bcf = true;foreach(sChannelId, oDevice.Channels().EnumIDs()){object oChannel = dom.GetObject(sChannelId);if(bcf) {bcf = false;} else {Write(\',\');}Write(\'{\');Write(\'\"cId\": \' # sChannelId # \',\');Write(\'\"name\": \"\' # oChannel.Name() # \'\",\');if(oInterface){Write(\'\"address\": \"\' # oInterface.Name() #\'.'\ # oChannel.Address() # \'\",\');}Write(\'\"type\": \"\' # oChannel.HssType() # \'\"\');Write(\'}\');}Write(\']}\');}}Write(\']}\');"; + + var regarequest = new RegaRequest(this.log,this.ccuIP).script(script, function(data) { + var json = JSON.parse(data); + if (json['devices'] != undefined) { + json['devices'].map(function(device) { + var isFiltered = false; + + if ((that.filter_device != undefined) && (that.filter_device.indexOf(device.address) > -1)) { + isFiltered = true; + } else { + isFiltered = false; + } + // that.log('device address:', device.address); + + if ((device['channels'] != undefined) && (!isFiltered)) { + + device['channels'].map(function(ch) { + var isChannelFiltered = false; + + if ((that.filter_channel != undefined) && (that.filter_channel.indexOf(ch.address) > -1)) { + isChannelFiltered = true; + } else { + isChannelFiltered = false; + } + // that.log('name', ch.name, ' -> address:', ch.address); + if ((ch.address != undefined) && (!isChannelFiltered)) { + + if ((ch.type=="SWITCH") || (ch.type=="BLIND") || (ch.type=="SHUTTER_CONTACT") + || (ch.type=="DIMMER") || (ch.type=="CLIMATECONTROL_RT_TRANSCEIVER") + || (ch.type=="MOTION_DETECTOR") || (ch.type=="KEYMATIC") + ) { + // Switch found + // Check if marked as Outlet + var special = (that.outlets.indexOf(ch.address) > -1) ? 'OUTLET' : undefined; + accessory = new HomeMaticGenericChannel(that.log, that, ch.id , ch.name , ch.type , ch.address, special); + that.foundAccessories.push(accessory); + } + + + } else { + that.log(device.name + " has no address"); + } + + }); + } else { + that.log(device.name + " has no channels or is filtered"); + } + + }); + +/* + accessory = new HomeMaticGenericChannel(that.log, that, "1234" , "DummyKM" , "KEYMATIC" , "1234"); + that.foundAccessories.push(accessory); + + accessory = new HomeMaticGenericChannel(that.log, that, "5678" , "DummyBLIND" , "BLIND" , "5678"); + that.foundAccessories.push(accessory); + + */ + callback(that.foundAccessories); + } else { + callback(that.foundAccessories); + } + }); + + }, + + setValue:function(channel,datapoint,value) { + if (channel.indexOf("BidCos-RF.")>-1) { + this.xmlrpc.setValue(channel,datapoint,value); + return; + } + + if (channel.indexOf("VirtualDevices.")>-1) { + var rega = new RegaRequest(this.log,this.ccuIP); + rega.setValue(channel,datapoint,value); + return; + } + + }, + + + getValue:function(channel,datapoint,callback) { + + if (channel.indexOf("BidCos-RF.")>-1) { + this.xmlrpc.getValue(channel,datapoint,callback); + return; + } + + if (channel.indexOf("VirtualDevices.")>-1) { + var rega = new RegaRequest(this.log,this.ccuIP); + rega.getValue(channel,datapoint,callback); + return; + } + + }, + + prepareRequest: function(accessory,script) { + var that = this; + this.sendQueue.push(script); + that.delayed(100); + }, + + sendPreparedRequests: function() { + var that = this; + var script = "var d;"; + this.sendQueue.map(function(command) { + script = script + command; + }); + this.sendQueue = []; + //this.log("RegaSend: " + script); + var regarequest = new RegaRequest(this.log,this.ccuIP).script(script, function(data) { + }); + }, + + sendRequest: function(accessory,script,callback) { + var that = this; + var regarequest = new RegaRequest(this.log,this.ccuIP).script(script, function(data) { + if (data != undefined) { + try { + var json = JSON.parse(data); + callback(json); + } catch (err) { + callback(undefined); + } + return; + } + }); + }, + + delayed: function(delay) { + var timer = this.delayed[delay]; + if( timer ) { + this.log("removing old command"); + clearTimeout( timer ); + } + + var that = this; + this.delayed[delay] = setTimeout( function(){clearTimeout(that.delayed[delay]);that.sendPreparedRequests()}, delay?delay:100); + this.log("New Timer was set"); + } +} + + + +module.exports.platform = HomeMaticPlatform; \ No newline at end of file diff --git a/platforms/HomematicChannel.js b/platforms/HomematicChannel.js new file mode 100644 index 0000000..1ef22a5 --- /dev/null +++ b/platforms/HomematicChannel.js @@ -0,0 +1,631 @@ +var types = require("hap-nodejs/accessories/types.js"); + + +function HomeMaticGenericChannel(log,platform, id ,name, type ,adress,special) { + this.name = name; + this.type = type; + this.adress = adress; + this.log = log; + this.platform = platform; + this.state = []; + this.eventupdate = false; + this.special = special; + this.currentStateCharacteristic = []; + this.reverseDP = []; +} + + + + +HomeMaticGenericChannel.prototype = { + + + // Return current States + query: function(dp,callback) { + var that = this; + + if (this.state[dp] != undefined) { + callback(this.state[dp]); + } else { +// that.log("No cached Value found start fetching and send temp 0 back"); + this.remoteGetValue(dp); + callback(0); + } + + }, + + dpvalue:function(dp,fallback) { + if (this.state[dp] != undefined) { + return(this.state[dp]); + } else { + return fallback; + } + }, + + remoteGetValue:function(dp) { + var that = this; + that.platform.getValue(that.adress,dp,function(newValue) { + that.log("Remote Value Response for " + that.adress + "." + dp + "->" + newValue); + that.eventupdate = true; + that.cache(dp,newValue); + that.eventupdate = false; + }); + }, + + + event:function(dp,newValue) { + + if (dp=="LEVEL") { + newValue = newValue*100; + } + + this.eventupdate = true; + this.cache(dp,newValue); + this.eventupdate = false; + }, + + reverse:function(value) { + if (value=="true") return "false"; + if (value=="false") return "true"; + if (value==0) return 1; + if (value==1) return 0; + if (value=="0") return "1"; + if (value=="1") return "0"; + return value; + }, + + cache:function(dp,value) { + var that = this; + + if ((that.reverseDP[dp]!=undefined) && (that.reverseDP[dp]==true)) { + value = that.reverse(value); + } + + if (that.currentStateCharacteristic[dp]!=undefined) { + that.currentStateCharacteristic[dp].updateValue(value, null); + } + this.state[dp] = value; + }, + + + delayed: function(mode, dp,value,delay) { + + if (this.eventupdate==true) { + return; + } + + var timer = this.delayed[delay]; + if( timer ) { + clearTimeout( timer ); + } + + this.log(this.name + " delaying command "+mode + " " + dp +" with value " + value); + var that = this; + this.delayed[delay] = setTimeout( function(){clearTimeout(that.delayed[delay]);that.command(mode,dp,value)}, delay?delay:100 ); + }, + + command: function(mode,dp,value,callback) { + + if (this.eventupdate==true) { + return; + } + var that = this; + + if (mode == "set") { + //this.log("Send " + value + " to Datapoint " + dp + " at " + that.adress); + that.platform.setValue(that.adress,dp,value); + } + }, + + informationCharacteristics: function() { + return [ + { + 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: "EQ-3", + 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: this.adress , + supportEvents: false, + supportBonjour: false, + manfDescription: "SN", + designedMaxLength: 255 + },{ + cType: types.IDENTIFY_CTYPE, + onUpdate: null, + perms: ["pw"], + format: "bool", + initialValue: false, + supportEvents: false, + supportBonjour: false, + manfDescription: "Identify Accessory", + designedMaxLength: 1 + } + ] + }, + + controlCharacteristics: function(that) { + + cTypes = [{ + cType: types.NAME_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: this.name, + supportEvents: true, + supportBonjour: false, + manfDescription: "Name of service", + designedMaxLength: 255 + }] + + + if (this.type=="SWITCH") { + cTypes.push({ + cType: types.POWER_STATE_CTYPE, + onUpdate: function(value) { + that.command("set","STATE" , (value==1)?true:false) + }, + + onRead: function(callback) { + that.query("STATE",callback); + }, + + onRegister: function(characteristic) { + that.currentStateCharacteristic["STATE"] = characteristic; + characteristic.eventEnabled = true; + that.remoteGetValue("STATE"); + }, + + perms: ["pw","pr","ev"], + format: "bool", + initialValue: that.dpvalue("STATE",0), + supportEvents: false, + supportBonjour: false, + manfDescription: "Change the power state", + designedMaxLength: 1 + }); + + if (this.special=="OUTLET") { + cTypes.push({ + cType: types.OUTLET_IN_USE_CTYPE, + + onRead: function(callback) { + callback(true); + }, + perms: ["pr","ev"], + format: "bool", + initialValue: true, + supportEvents: false, + supportBonjour: false, + manfDescription: "Is Outlet in Use", + designedMaxLength: 1 + }) + } + } + + + if (this.type=="KEYMATIC") { + cTypes.push( + { + cType: types.CURRENT_LOCK_MECHANISM_STATE_CTYPE, + + onRead: function(callback) { + that.query("STATE",callback); + }, + + onRegister: function(characteristic) { + that.currentStateCharacteristic["STATE"] = characteristic; + characteristic.eventEnabled = true; + that.remoteGetValue("STATE"); + }, + + perms: ["pr","ev"], + format: "bool", + initialValue: that.dpvalue("STATE",0), + supportEvents: false, + supportBonjour: false, + manfDescription: "Current State of your Lock", + designedMaxLength: 1 + }, + { + cType: types.TARGET_LOCK_MECHANISM_STATE_CTYPE, + + onUpdate: function(value) { + that.command("set","STATE",(value==1)?"true":"false") + }, + + onRead: function(callback) { + that.query("STATE",callback); + }, + + onRegister: function(characteristic) { + that.reverseDP["STATE"] = true; + that.currentStateCharacteristic["STATE"] = characteristic; + characteristic.eventEnabled = true; + that.remoteGetValue("STATE"); + }, + + + perms: ["pw","pr","ev"], + format: "bool", + initialValue: that.dpvalue("STATE",0), + supportEvents: false, + supportBonjour: false, + manfDescription: "Target State of your Lock", + designedMaxLength: 1 + } + + , + { + cType: types.TARGET_DOORSTATE_CTYPE, + + onUpdate: function(value) { + that.command("set","OPEN" , "true") + }, + + onRead: function(callback) { + callback(1); + }, + + onRegister: function(characteristic) { + that.currentStateCharacteristic["OPEN"] = characteristic; + characteristic.eventEnabled = true; + }, + + perms: ["pw","pr","ev"], + format: "bool", + initialValue: 1, + supportEvents: false, + supportBonjour: false, + manfDescription: "Open the Lock", + designedMaxLength: 1 + } + ); + + + } + + + + if (this.type=="DIMMER") { + cTypes.push({ + cType: types.POWER_STATE_CTYPE, + onUpdate: function(value) { + that.command("set","LEVEL" , (value==true) ? "1" : "0") + }, + + onRead: function(callback) { + that.query("LEVEL",callback); + }, + + onRegister: function(characteristic) { + that.currentStateCharacteristic["LEVEL"] = characteristic; + characteristic.eventEnabled = true; + that.remoteGetValue("LEVEL"); + }, + + perms: ["pw","pr","ev"], + format: "bool", + initialValue: (that.dpvalue("LEVEL")>0,0), + supportEvents: false, + supportBonjour: false, + manfDescription: "Change the power state", + designedMaxLength: 1 + }, + { + cType: types.BRIGHTNESS_CTYPE, + onUpdate: function(value) { + that.delayed("set","LEVEL" , String(value/100),100); + }, + + onRead: function(callback) { + that.query("LEVEL",callback); + }, + + onRegister: function(characteristic) { + that.currentStateCharacteristic["LEVEL"] = characteristic; + characteristic.eventEnabled = true; + that.remoteGetValue("LEVEL"); + }, + + + perms: ["pw","pr","ev"], + format: "int", + initialValue: that.dpvalue("LEVEL",0), + supportEvents: false, + supportBonjour: false, + manfDescription: "Adjust Brightness of Light", + designedMinValue: 0, + designedMaxValue: 100, + designedMinStep: 1, + unit: "%" + }); + } + + if (this.type=="BLIND") { + cTypes.push( + { + cType: types.WINDOW_COVERING_CURRENT_POSITION_CTYPE, + + onRead: function(callback) { + that.query("LEVEL",callback); + }, + + onRegister: function(characteristic) { + that.currentStateCharacteristic["LEVEL"] = characteristic; + characteristic.eventEnabled = true; + that.remoteGetValue("LEVEL"); + }, + + perms: ["pr","ev"], + format: "int", + initialValue: that.dpvalue("LEVEL",0), + supportEvents: false, + supportBonjour: false, + manfDescription: "Current Blind Position", + designedMinValue: 0, + designedMaxValue: 100, + designedMinStep: 1, + unit: "%" + }, + + { + cType: types.WINDOW_COVERING_TARGET_POSITION_CTYPE, + + onUpdate: function(value) { + that.delayed("set","LEVEL" , String(value/100),100); + }, + + + onRead: function(callback) { + that.query("LEVEL",callback); + }, + + onRegister: function(characteristic) { + that.currentStateCharacteristic["LEVEL"] = characteristic; + characteristic.eventEnabled = true; + that.remoteGetValue("LEVEL"); + }, + + perms: ["pw","pr","ev"], + format: "int", + initialValue: that.dpvalue("LEVEL",0), + supportEvents: false, + supportBonjour: false, + manfDescription: "Target Blind Position", + designedMinValue: 0, + designedMaxValue: 100, + designedMinStep: 1, + unit: "%" + }, + { + cType: types.WINDOW_COVERING_OPERATION_STATE_CTYPE, + + onRead: function(callback) { + that.query("DIRECTION",callback); + }, + + onRegister: function(characteristic) { + that.currentStateCharacteristic["DIRECTION"] = characteristic; + characteristic.eventEnabled = true; + that.remoteGetValue("DIRECTION"); + }, + + perms: ["pr","ev"], + format: "int", + initialValue: that.dpvalue("DIRECTION",0), + supportEvents: false, + supportBonjour: false, + manfDescription: "Operating State ", + designedMinValue: 0, + designedMaxValue: 2, + designedMinStep: 1 + } + + ); + } + + if (this.type=="SHUTTER_CONTACT") { + cTypes.push( + { + cType: types.CONTACT_SENSOR_STATE_CTYPE, + + onRead: function(callback) { + that.query("STATE",callback); + }, + + onRegister: function(characteristic) { + that.currentStateCharacteristic["STATE"] = characteristic; + characteristic.eventEnabled = true; + that.remoteGetValue("STATE"); + }, + + perms: ["pr","ev"], + format: "bool", + initialValue: that.dpvalue("STATE",0), + supportEvents: false, + supportBonjour: false, + manfDescription: "Current State" + }); + } + + if (this.type=="MOTION_DETECTOR") { + cTypes.push( + { + cType: types.MOTION_DETECTED_CTYPE, + + onRead: function(callback) { + that.query("MOTION",callback); + }, + + onRegister: function(characteristic) { + that.currentStateCharacteristic["MOTION"] = characteristic; + characteristic.eventEnabled = true; + that.remoteGetValue("MOTION"); + }, + + perms: ["pr","ev"], + format: "bool", + initialValue: that.dpvalue("MOTION",0), + supportEvents: false, + supportBonjour: false, + manfDescription: "Current Motion State" + }); + } + + if (this.type=="CLIMATECONTROL_RT_TRANSCEIVER") { + + cTypes.push({ + cType: types.NAME_CTYPE,onUpdate: null,perms: ["pr"],format: "string", + initialValue: this.name,supportEvents: true,supportBonjour: false,manfDescription: "Name of service",designedMaxLength: 255 + }, + + { + cType: types.CURRENTHEATINGCOOLING_CTYPE,onUpdate: null, + perms: ["pr"],format: "int",initialValue: 1,supportEvents: false, + supportBonjour: false,manfDescription: "Current Mode",designedMaxLength: 1,designedMinValue: 1,designedMaxValue: 1,designedMinStep: 1 + }, + + { + cType: types.TARGETHEATINGCOOLING_CTYPE,onUpdate: null,perms: ["pw","pr"], + format: "int",initialValue: 1,supportEvents: false,supportBonjour: false,manfDescription: "Target Mode", + designedMinValue: 1,designedMaxValue: 1,designedMinStep: 1 + }, + + { + cType: types.CURRENT_TEMPERATURE_CTYPE, + onUpdate: null, + + onRead: function(callback) { + that.query("ACTUAL_TEMPERATURE",callback); + }, + + onRegister: function(characteristic) { + that.currentStateCharacteristic["ACTUAL_TEMPERATURE"] = characteristic; + characteristic.eventEnabled = true; + that.remoteGetValue("ACTUAL_TEMPERATURE"); + }, + perms: ["pw","pr","ev"], perms: ["pr"],format: "double", + initialValue: that.dpvalue("ACTUAL_TEMPERATURE",20), + supportEvents: false,supportBonjour: false,manfDescription: "Current Temperature",unit: "celsius" + }, + + { + cType: types.TARGET_TEMPERATURE_CTYPE, + onUpdate: function(value) { + that.delayed("set", "SET_TEMPERATURE", value,500); + }, + onRead: function(callback) { + that.query("SET_TEMPERATURE",callback); + + }, + onRegister: function(characteristic) { + that.currentStateCharacteristic["SET_TEMPERATURE"] = characteristic; + characteristic.eventEnabled = true; + that.remoteGetValue("SET_TEMPERATURE"); + }, + perms: ["pw","pr","ev"],format: "double", + initialValue: that.dpvalue("SET_TEMPERATURE",16), + supportEvents: false,supportBonjour: false, manfDescription: "Target Temperature", + designedMinValue: 16,designedMaxValue: 38,designedMinStep: 1,unit: "celsius" + }, + + { + cType: types.TEMPERATURE_UNITS_CTYPE,onRead: null, + perms: ["pr"],format: "int",initialValue: 0,supportEvents: false, + supportBonjour: false,manfDescription: "Current Temperature Unit",unit: "celsius" + } + + ); + } + + + return cTypes + }, + + sType: function() { + + if (this.type=="SWITCH") { + + if (this.special=="OUTLET") { + return types.OUTLET_STYPE; + } else { + return types.LIGHTBULB_STYPE; + } + } + + if (this.type=="DIMMER") { + return types.LIGHTBULB_STYPE; + } + + if (this.type=="BLIND") { + return types.WINDOW_COVERING_STYPE; + } + + if (this.type=="CLIMATECONTROL_RT_TRANSCEIVER") { + return types.THERMOSTAT_STYPE; + } + + if (this.type=="SHUTTER_CONTACT") { + return types.CONTACT_SENSOR_STYPE; + } + + if (this.type=="MOTION_DETECTOR") { + return types.MOTION_SENSOR_STYPE + } + + + if (this.type=="KEYMATIC") { + return types.LOCK_MECHANISM_STYPE + } + + + + }, + + getServices: function() { + var that = this; + var services = [{ + sType: types.ACCESSORY_INFORMATION_STYPE, + characteristics: this.informationCharacteristics(), + }, + { + sType: this.sType(), + characteristics: this.controlCharacteristics(that) + }]; + this.log("Loaded services for " + this.name) + return services; + } +}; + + +module.exports = HomeMaticGenericChannel; \ No newline at end of file From 6b36ec9dc417c2138e2e643b62a6c9a9f6c2df1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6nig?= Date: Thu, 29 Oct 2015 19:15:35 +0100 Subject: [PATCH 4/7] cleanup formatting --- platforms/HomeMatic.js | 584 +++++++++++----------- platforms/HomematicChannel.js | 912 ++++++++++++++++++---------------- 2 files changed, 766 insertions(+), 730 deletions(-) diff --git a/platforms/HomeMatic.js b/platforms/HomeMatic.js index b22a0f4..1b99729 100644 --- a/platforms/HomeMatic.js +++ b/platforms/HomeMatic.js @@ -17,345 +17,347 @@ var HomeMaticGenericChannel = require(path.resolve(__dirname, 'HomematicChannel. -function RegaRequest(log,ccuip) { - this.log = log; - this.ccuIP = ccuip; +function RegaRequest(log, ccuip) { + this.log = log; + this.ccuIP = ccuip; } RegaRequest.prototype = { - script: function (script, callback) { + script: function(script, callback) { - var post_options = { - host: this.ccuIP, - port: '80', - path: '/tclrega.exe', - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': script.length - } - }; + var post_options = { + host: this.ccuIP, + port: '80', + path: '/tclrega.exe', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': script.length + } + }; - var post_req = http.request(post_options, function(res) { - var data = ""; - res.setEncoding('binary'); - res.on('data', function (chunk) { - data += chunk.toString(); - }); - res.on('end', function () { - var pos = data.lastIndexOf(""); - var response = (data.substring(0, pos)); - callback(response); - }); - }); + var post_req = http.request(post_options, function(res) { + var data = ""; + res.setEncoding('binary'); + res.on('data', function(chunk) { + data += chunk.toString(); + }); + res.on('end', function() { + var pos = data.lastIndexOf(""); + var response = (data.substring(0, pos)); + callback(response); + }); + }); - post_req.on('error', function(e) { - callback("{}"); - }); + post_req.on('error', function(e) { + callback("{}"); + }); - post_req.write(script); - post_req.end(); + post_req.write(script); + post_req.end(); - }, - - getValue: function(channel,datapoint,callback) { - var that = this; - - var script = "var d = dom.GetObject(\""+channel+"."+datapoint+"\");if (d){Write(d.State());}"; - //that.log("Rega Request " + script); - var regarequest = this.script(script, function(data) { - that.log("Rega Response" + data); - if (data!=undefined) { - callback(parseFloat(data)); - } - } - ); }, - - setValue: function(channel,datapoint,value) { - var that = this; - - var script = "var d = dom.GetObject(\""+channel+"."+datapoint+"\");if (d){d.State(\""+value+"\");}"; - //that.log("Rega Request " + script); - var regarequest = this.script(script, function(data) { - }); + + getValue: function(channel, datapoint, callback) { + var that = this; + + var script = "var d = dom.GetObject(\"" + channel + "." + datapoint + "\");if (d){Write(d.State());}"; + //that.log("Rega Request " + script); + var regarequest = this.script(script, function(data) { + that.log("Rega Response" + data); + if (data != undefined) { + callback(parseFloat(data)); + } + }); + }, + + setValue: function(channel, datapoint, value) { + var that = this; + + var script = "var d = dom.GetObject(\"" + channel + "." + datapoint + "\");if (d){d.State(\"" + value + "\");}"; + //that.log("Rega Request " + script); + var regarequest = this.script(script, function(data) {}); } } -function HomematicRPC(log,ccuip,platform) { - this.log = log; - this.ccuip = ccuip; - this.platform = platform; - this.server; - this.client; - this.stopping = false; - this.localIP; +function HomematicRPC(log, ccuip, platform) { + this.log = log; + this.ccuip = ccuip; + this.platform = platform; + this.server; + this.client; + this.stopping = false; + this.localIP; } -HomematicRPC.prototype= { +HomematicRPC.prototype = { - init:function() { - var that = this; - - var ip = this.getIPAddress(); - if (ip=="0.0.0.0") { - that.log("Can not fetch IP"); - return; - } - - this.localIP = ip; - this.log("Local IP: "+this.localIP) - - this.server = xmlrpc.createServer({ host: this.localIP , port: 9090 }) + init: function() { + var that = this; - this.server.on('NotFound', function(method, params) { - that.log('Method ' + method + ' does not exist'); - }); - - this.server.on('system.listMethods', function (err, params, callback) { - that.log('Method call params for \'system.listMethods\': ' + params) - callback(null,['system.listMethods', 'system.multicall']); - }); + var ip = this.getIPAddress(); + if (ip == "0.0.0.0") { + that.log("Can not fetch IP"); + return; + } - - this.server.on('system.multicall', function (err, params, callback) { - params.map(function(events) { - try { - events.map(function(event){ - if ((event["methodName"]=="event") && (event['params'] != undefined)) { - var params = event['params']; - var channel = "BidCos-RF." + params[1]; - var datapoint = params[2]; - var value = params[3]; - that.platform.foundAccessories.map(function(accessory){ - if (accessory.adress == channel) { - accessory.event(datapoint,value); - } - }); - } - }); - } catch(err) {} - }); - callback(null); - }); - - this.log('XML-RPC server listening on port 9090') + this.localIP = ip; + this.log("Local IP: " + this.localIP) + + this.server = xmlrpc.createServer({ + host: this.localIP, + port: 9090 + }) + + this.server.on('NotFound', function(method, params) { + that.log('Method ' + method + ' does not exist'); + }); + + this.server.on('system.listMethods', function(err, params, callback) { + that.log('Method call params for \'system.listMethods\': ' + params) + callback(null, ['system.listMethods', 'system.multicall']); + }); + + + this.server.on('system.multicall', function(err, params, callback) { + params.map(function(events) { + try { + events.map(function(event) { + if ((event["methodName"] == "event") && (event['params'] != undefined)) { + var params = event['params']; + var channel = "BidCos-RF." + params[1]; + var datapoint = params[2]; + var value = params[3]; + that.platform.foundAccessories.map(function(accessory) { + if (accessory.adress == channel) { + accessory.event(datapoint, value); + } + }); + } + }); + } catch (err) {} + }); + callback(null); + }); + + this.log('XML-RPC server listening on port 9090') this.connect(); - - - process.on('SIGINT', function () { - if (that.stopping) { - return; - } - that.stopping = true; - that.stop(); - }); - process.on('SIGTERM', function () { - if (that.stopping) { - return; - } - that.stopping = true; - that.stop(); - }); - }, - - getIPAddress: function() { - var interfaces = require('os').networkInterfaces(); - for (var devName in interfaces) { + process.on('SIGINT', function() { + if (that.stopping) { + return; + } + that.stopping = true; + that.stop(); + }); + + process.on('SIGTERM', function() { + if (that.stopping) { + return; + } + that.stopping = true; + that.stop(); + }); + + }, + + getIPAddress: function() { + var interfaces = require('os').networkInterfaces(); + for (var devName in interfaces) { var iface = interfaces[devName]; for (var i = 0; i < iface.length; i++) { - var alias = iface[i]; - if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) - return alias.address; - } + var alias = iface[i]; + if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) + return alias.address; } - return '0.0.0.0'; - }, + } + return '0.0.0.0'; + }, - getValue:function(channel,datapoint,callback) { - - var that = this; - if (this.client == undefined) { - that.log("Returning cause client is invalid"); - return; - } - if (channel.indexOf("BidCos-RF.")>-1) { - channel = channel.substr(10); - this.log("Calling rpc getValue"); - this.client.methodCall('getValue', [channel,datapoint], function (error, value) { - callback(value); - }); - return; - } - }, + getValue: function(channel, datapoint, callback) { - setValue:function(channel,datapoint,value) { - - var that = this; - - if (this.client == undefined) return; + var that = this; + if (this.client == undefined) { + that.log("Returning cause client is invalid"); + return; + } + if (channel.indexOf("BidCos-RF.") > -1)  { + channel = channel.substr(10); + this.log("Calling rpc getValue"); + this.client.methodCall('getValue', [channel, datapoint], function(error, value) { + callback(value); + }); + return; + } + }, - if (channel.indexOf("BidCos-RF.")>-1) { - channel = channel.substr(10); - } - - this.client.methodCall('setValue', [channel,datapoint,value], function (error, value) { + setValue: function(channel, datapoint, value) { - }); - }, + var that = this; - connect:function(){ - var that = this; - this.log('Creating Local HTTP Client for CCU RPC Events'); - this.client = xmlrpc.createClient({ host: this.ccuip, port: 2001, path: '/'}); - this.log('CCU RPC Init Call on port 2001'); - this.client.methodCall('init', ['http://'+this.localIP+':9090','homebridge'], function (error, value) { - that.log('CCU Response ....') - }); - }, - - - stop:function() { - this.log("Removing Event Server"); - this.client.methodCall('init', ['http://'+this.localIP+':9090'], function (error, value) { + if (this.client == undefined) return; + + if (channel.indexOf("BidCos-RF.") > -1)  { + channel = channel.substr(10); + } + + this.client.methodCall('setValue', [channel, datapoint, value], function(error, value) { }); - setTimeout(process.exit(0), 1000); + }, + + connect: function() { + var that = this; + this.log('Creating Local HTTP Client for CCU RPC Events'); + this.client = xmlrpc.createClient({ + host: this.ccuip, + port: 2001, + path: '/' + }); + this.log('CCU RPC Init Call on port 2001'); + this.client.methodCall('init', ['http://' + this.localIP + ':9090', 'homebridge'], function(error, value) { + that.log('CCU Response ....') + }); + }, + + + stop: function() { + this.log("Removing Event Server"); + this.client.methodCall('init', ['http://' + this.localIP + ':9090'], function(error, value) { + + }); + setTimeout(process.exit(0), 1000); } } function HomeMaticPlatform(log, config) { - this.log = log; - this.ccuIP = config["ccu_ip"]; - this.filter_device = config["filter_device"]; - this.filter_channel = config["filter_channel"]; - this.outlets = config["outlets"]; + this.log = log; + this.ccuIP = config["ccu_ip"]; + this.filter_device = config["filter_device"]; + this.filter_channel = config["filter_channel"]; + this.outlets = config["outlets"]; - this.sendQueue = []; - this.timer = 0; - - this.foundAccessories = []; - this.adressesToQuery = []; - - this.xmlrpc = new HomematicRPC(this.log,this.ccuIP,this); - this.xmlrpc.init(); + this.sendQueue = []; + this.timer = 0; + + this.foundAccessories = []; + this.adressesToQuery = []; + + this.xmlrpc = new HomematicRPC(this.log, this.ccuIP, this); + this.xmlrpc.init(); } HomeMaticPlatform.prototype = { - - + + accessories: function(callback) { this.log("Fetching Homematic devices..."); - var that = this; + var that = this; that.foundAccessories = []; - + var script = "string sDeviceId;string sChannelId;boolean df = true;Write(\'{\"devices\":[\');foreach(sDeviceId, root.Devices().EnumIDs()){object oDevice = dom.GetObject(sDeviceId);if(oDevice){var oInterface = dom.GetObject(oDevice.Interface());if(df) {df = false;} else { Write(\',\');}Write(\'{\');Write(\'\"id\": \"\' # sDeviceId # \'\",\');Write(\'\"name\": \"\' # oDevice.Name() # \'\",\');Write(\'\"address\": \"\' # oDevice.Address() # \'\",\');Write(\'\"channels\": [\');boolean bcf = true;foreach(sChannelId, oDevice.Channels().EnumIDs()){object oChannel = dom.GetObject(sChannelId);if(bcf) {bcf = false;} else {Write(\',\');}Write(\'{\');Write(\'\"cId\": \' # sChannelId # \',\');Write(\'\"name\": \"\' # oChannel.Name() # \'\",\');if(oInterface){Write(\'\"address\": \"\' # oInterface.Name() #\'.'\ # oChannel.Address() # \'\",\');}Write(\'\"type\": \"\' # oChannel.HssType() # \'\"\');Write(\'}\');}Write(\']}\');}}Write(\']}\');"; - var regarequest = new RegaRequest(this.log,this.ccuIP).script(script, function(data) { - var json = JSON.parse(data); - if (json['devices'] != undefined) { - json['devices'].map(function(device) { - var isFiltered = false; + var regarequest = new RegaRequest(this.log, this.ccuIP).script(script, function(data) { + var json = JSON.parse(data); + if (json['devices'] != undefined) { + json['devices'].map(function(device) { + var isFiltered = false; - if ((that.filter_device != undefined) && (that.filter_device.indexOf(device.address) > -1)) { - isFiltered = true; - } else { - isFiltered = false; - } - // that.log('device address:', device.address); + if ((that.filter_device != undefined) && (that.filter_device.indexOf(device.address) > -1)) { + isFiltered = true; + } else { + isFiltered = false; + } + // that.log('device address:', device.address); - if ((device['channels'] != undefined) && (!isFiltered)) { + if ((device['channels'] != undefined) && (!isFiltered)) { - device['channels'].map(function(ch) { - var isChannelFiltered = false; + device['channels'].map(function(ch) { + var isChannelFiltered = false; - if ((that.filter_channel != undefined) && (that.filter_channel.indexOf(ch.address) > -1)) { - isChannelFiltered = true; - } else { - isChannelFiltered = false; - } - // that.log('name', ch.name, ' -> address:', ch.address); - if ((ch.address != undefined) && (!isChannelFiltered)) { - - if ((ch.type=="SWITCH") || (ch.type=="BLIND") || (ch.type=="SHUTTER_CONTACT") - || (ch.type=="DIMMER") || (ch.type=="CLIMATECONTROL_RT_TRANSCEIVER") - || (ch.type=="MOTION_DETECTOR") || (ch.type=="KEYMATIC") - ) { - // Switch found - // Check if marked as Outlet - var special = (that.outlets.indexOf(ch.address) > -1) ? 'OUTLET' : undefined; - accessory = new HomeMaticGenericChannel(that.log, that, ch.id , ch.name , ch.type , ch.address, special); - that.foundAccessories.push(accessory); - } - + if ((that.filter_channel != undefined) && (that.filter_channel.indexOf(ch.address) > -1)) { + isChannelFiltered = true; + } else { + isChannelFiltered = false; + } + // that.log('name', ch.name, ' -> address:', ch.address); + if ((ch.address != undefined) && (!isChannelFiltered)) { - } else { - that.log(device.name + " has no address"); - } + if ((ch.type == "SWITCH") || (ch.type == "BLIND") || (ch.type == "SHUTTER_CONTACT") || (ch.type == "DIMMER") || (ch.type == "CLIMATECONTROL_RT_TRANSCEIVER") ||  (ch.type == "MOTION_DETECTOR") ||  (ch.type == "KEYMATIC")) { + // Switch found + // Check if marked as Outlet + var special = (that.outlets.indexOf(ch.address) > -1) ? 'OUTLET' : undefined; + accessory = new HomeMaticGenericChannel(that.log, that, ch.id, ch.name, ch.type, ch.address, special); + that.foundAccessories.push(accessory); + } - }); - } else { - that.log(device.name + " has no channels or is filtered"); - } - }); + } else { + that.log(device.name + " has no address"); + } -/* - accessory = new HomeMaticGenericChannel(that.log, that, "1234" , "DummyKM" , "KEYMATIC" , "1234"); - that.foundAccessories.push(accessory); + }); + } else { + that.log(device.name + " has no channels or is filtered"); + } - accessory = new HomeMaticGenericChannel(that.log, that, "5678" , "DummyBLIND" , "BLIND" , "5678"); - that.foundAccessories.push(accessory); - - */ - callback(that.foundAccessories); - } else { - callback(that.foundAccessories); - } + }); + + /* + accessory = new HomeMaticGenericChannel(that.log, that, "1234" , "DummyKM" , "KEYMATIC" , "1234"); + that.foundAccessories.push(accessory); + + accessory = new HomeMaticGenericChannel(that.log, that, "5678" , "DummyBLIND" , "BLIND" , "5678"); + that.foundAccessories.push(accessory); + + */ + callback(that.foundAccessories); + } else { + callback(that.foundAccessories); + } }); - + }, - - setValue:function(channel,datapoint,value) { - if (channel.indexOf("BidCos-RF.")>-1) { - this.xmlrpc.setValue(channel,datapoint,value); - return; + + setValue: function(channel, datapoint, value) { + if (channel.indexOf("BidCos-RF.") > -1)  { + this.xmlrpc.setValue(channel, datapoint, value); + return; + } + + if (channel.indexOf("VirtualDevices.") > -1)  { + var rega = new RegaRequest(this.log, this.ccuIP); + rega.setValue(channel, datapoint, value); + return; } - if (channel.indexOf("VirtualDevices.")>-1) { - var rega = new RegaRequest(this.log,this.ccuIP); - rega.setValue(channel,datapoint,value); - return; - } - }, - - - getValue:function(channel,datapoint,callback) { - - if (channel.indexOf("BidCos-RF.")>-1) { - this.xmlrpc.getValue(channel,datapoint,callback); - return; - } - - if (channel.indexOf("VirtualDevices.")>-1) { - var rega = new RegaRequest(this.log,this.ccuIP); - rega.getValue(channel,datapoint,callback); - return; - } - + + + getValue: function(channel, datapoint, callback) { + + if (channel.indexOf("BidCos-RF.") > -1)  { + this.xmlrpc.getValue(channel, datapoint, callback); + return; + } + + if (channel.indexOf("VirtualDevices.") > -1)  { + var rega = new RegaRequest(this.log, this.ccuIP); + rega.getValue(channel, datapoint, callback); + return; + } + }, - - prepareRequest: function(accessory,script) { + + prepareRequest: function(accessory, script) { var that = this; this.sendQueue.push(script); that.delayed(100); @@ -369,34 +371,36 @@ HomeMaticPlatform.prototype = { }); this.sendQueue = []; //this.log("RegaSend: " + script); - var regarequest = new RegaRequest(this.log,this.ccuIP).script(script, function(data) { - }); + var regarequest = new RegaRequest(this.log, this.ccuIP).script(script, function(data) {}); }, - sendRequest: function(accessory,script,callback) { + sendRequest: function(accessory, script, callback) { var that = this; - var regarequest = new RegaRequest(this.log,this.ccuIP).script(script, function(data) { - if (data != undefined) { - try { - var json = JSON.parse(data); - callback(json); - } catch (err) { - callback(undefined); - } - return; - } + var regarequest = new RegaRequest(this.log, this.ccuIP).script(script, function(data) { + if (data != undefined) { + try { + var json = JSON.parse(data); + callback(json); + } catch (err) { + callback(undefined); + } + return; + } }); }, delayed: function(delay) { var timer = this.delayed[delay]; - if( timer ) { + if (timer) { this.log("removing old command"); - clearTimeout( timer ); + clearTimeout(timer); } var that = this; - this.delayed[delay] = setTimeout( function(){clearTimeout(that.delayed[delay]);that.sendPreparedRequests()}, delay?delay:100); + this.delayed[delay] = setTimeout(function() { + clearTimeout(that.delayed[delay]); + that.sendPreparedRequests() + }, delay ? delay : 100); this.log("New Timer was set"); } } diff --git a/platforms/HomematicChannel.js b/platforms/HomematicChannel.js index 1ef22a5..4e5a34b 100644 --- a/platforms/HomematicChannel.js +++ b/platforms/HomematicChannel.js @@ -1,180 +1,180 @@ var types = require("hap-nodejs/accessories/types.js"); -function HomeMaticGenericChannel(log,platform, id ,name, type ,adress,special) { - this.name = name; - this.type = type; - this.adress = adress; - this.log = log; +function HomeMaticGenericChannel(log, platform, id, name, type, adress, special) { + this.name = name; + this.type = type; + this.adress = adress; + this.log = log; this.platform = platform; - this.state = []; + this.state = []; this.eventupdate = false; - this.special = special; + this.special = special; this.currentStateCharacteristic = []; this.reverseDP = []; } - HomeMaticGenericChannel.prototype = { - // Return current States - query: function(dp,callback) { + // Return current States + query: function(dp, callback) { var that = this; - + if (this.state[dp] != undefined) { callback(this.state[dp]); } else { -// that.log("No cached Value found start fetching and send temp 0 back"); + // that.log("No cached Value found start fetching and send temp 0 back"); this.remoteGetValue(dp); callback(0); } }, - dpvalue:function(dp,fallback) { + dpvalue: function(dp, fallback) { if (this.state[dp] != undefined) { - return(this.state[dp]); + return (this.state[dp]); } else { return fallback; } }, - remoteGetValue:function(dp) { - var that = this; - that.platform.getValue(that.adress,dp,function(newValue) { - that.log("Remote Value Response for " + that.adress + "." + dp + "->" + newValue); - that.eventupdate = true; - that.cache(dp,newValue); - that.eventupdate = false; - }); + remoteGetValue: function(dp) { + var that = this; + that.platform.getValue(that.adress, dp, function(newValue) { + that.log("Remote Value Response for " + that.adress + "." + dp + "->" + newValue); + that.eventupdate = true; + that.cache(dp, newValue); + that.eventupdate = false; + }); }, - - event:function(dp,newValue) { - - if (dp=="LEVEL") { - newValue = newValue*100; + + event: function(dp, newValue) { + + if (dp == "LEVEL") { + newValue = newValue * 100; } this.eventupdate = true; - this.cache(dp,newValue); + this.cache(dp, newValue); this.eventupdate = false; }, - reverse:function(value) { - if (value=="true") return "false"; - if (value=="false") return "true"; - if (value==0) return 1; - if (value==1) return 0; - if (value=="0") return "1"; - if (value=="1") return "0"; + reverse: function(value) { + if (value == "true") return "false"; + if (value == "false") return "true"; + if (value == 0) return 1; + if (value == 1) return 0; + if (value == "0") return "1"; + if (value == "1") return "0"; return value; }, - cache:function(dp,value) { + cache: function(dp, value) { var that = this; - if ((that.reverseDP[dp]!=undefined) && (that.reverseDP[dp]==true)) { - value = that.reverse(value); - } - - if (that.currentStateCharacteristic[dp]!=undefined) { - that.currentStateCharacteristic[dp].updateValue(value, null); + if ((that.reverseDP[dp] != undefined) && (that.reverseDP[dp] == true)) { + value = that.reverse(value); + } + + if (that.currentStateCharacteristic[dp] != undefined) { + that.currentStateCharacteristic[dp].updateValue(value, null); } this.state[dp] = value; }, - delayed: function(mode, dp,value,delay) { - - if (this.eventupdate==true) { - return; - } - - var timer = this.delayed[delay]; - if( timer ) { - clearTimeout( timer ); + delayed: function(mode, dp, value, delay) { + + if (this.eventupdate == true) { + return; } - this.log(this.name + " delaying command "+mode + " " + dp +" with value " + value); + var timer = this.delayed[delay]; + if (timer) { + clearTimeout(timer); + } + + this.log(this.name + " delaying command " + mode + " " + dp + " with value " + value); var that = this; - this.delayed[delay] = setTimeout( function(){clearTimeout(that.delayed[delay]);that.command(mode,dp,value)}, delay?delay:100 ); + this.delayed[delay] = setTimeout(function() { + clearTimeout(that.delayed[delay]); + that.command(mode, dp, value) + }, delay ? delay : 100); }, - command: function(mode,dp,value,callback) { - - if (this.eventupdate==true) { - return; - } - var that = this; + command: function(mode, dp, value, callback) { - if (mode == "set") { - //this.log("Send " + value + " to Datapoint " + dp + " at " + that.adress); - that.platform.setValue(that.adress,dp,value); - } + if (this.eventupdate == true) { + return; + } + var that = this; + + if (mode == "set") { + //this.log("Send " + value + " to Datapoint " + dp + " at " + that.adress); + that.platform.setValue(that.adress, dp, value); + } }, informationCharacteristics: function() { - return [ - { - 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: "EQ-3", - 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: this.adress , - 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 - } - ] + return [{ + 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: "EQ-3", + 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: this.adress, + supportEvents: false, + supportBonjour: false, + manfDescription: "SN", + designedMaxLength: 255 + }, { + cType: types.IDENTIFY_CTYPE, + onUpdate: null, + perms: ["pw"], + format: "bool", + initialValue: false, + supportEvents: false, + supportBonjour: false, + manfDescription: "Identify Accessory", + designedMaxLength: 1 + }] }, controlCharacteristics: function(that) { - + cTypes = [{ cType: types.NAME_CTYPE, onUpdate: null, @@ -186,181 +186,177 @@ HomeMaticGenericChannel.prototype = { manfDescription: "Name of service", designedMaxLength: 255 }] - - - if (this.type=="SWITCH") { - cTypes.push({ + + + if (this.type == "SWITCH") { + cTypes.push({ cType: types.POWER_STATE_CTYPE, onUpdate: function(value) { - that.command("set","STATE" , (value==1)?true:false) + that.command("set", "STATE", (value == 1) ? true : false) }, onRead: function(callback) { - that.query("STATE",callback); + that.query("STATE", callback); }, - - onRegister: function(characteristic) { - that.currentStateCharacteristic["STATE"] = characteristic; - characteristic.eventEnabled = true; - that.remoteGetValue("STATE"); + + onRegister: function(characteristic) { + that.currentStateCharacteristic["STATE"] = characteristic; + characteristic.eventEnabled = true; + that.remoteGetValue("STATE"); }, - - perms: ["pw","pr","ev"], + + perms: ["pw", "pr", "ev"], format: "bool", - initialValue: that.dpvalue("STATE",0), + initialValue: that.dpvalue("STATE", 0), supportEvents: false, supportBonjour: false, manfDescription: "Change the power state", designedMaxLength: 1 }); - - if (this.special=="OUTLET") { + + if (this.special == "OUTLET") { cTypes.push({ cType: types.OUTLET_IN_USE_CTYPE, - + onRead: function(callback) { - callback(true); + callback(true); }, - perms: ["pr","ev"], - format: "bool", - initialValue: true, - supportEvents: false, - supportBonjour: false, - manfDescription: "Is Outlet in Use", - designedMaxLength: 1 - }) - } + perms: ["pr", "ev"], + format: "bool", + initialValue: true, + supportEvents: false, + supportBonjour: false, + manfDescription: "Is Outlet in Use", + designedMaxLength: 1 + }) + } } - - - if (this.type=="KEYMATIC") { - cTypes.push( - { - cType: types.CURRENT_LOCK_MECHANISM_STATE_CTYPE, - - onRead: function(callback) { - that.query("STATE",callback); - }, - - onRegister: function(characteristic) { + + + if (this.type == "KEYMATIC") { + cTypes.push({ + cType: types.CURRENT_LOCK_MECHANISM_STATE_CTYPE, + + onRead: function(callback) { + that.query("STATE", callback); + }, + + onRegister: function(characteristic) { that.currentStateCharacteristic["STATE"] = characteristic; characteristic.eventEnabled = true; that.remoteGetValue("STATE"); - }, + }, - perms: ["pr","ev"], - format: "bool", - initialValue: that.dpvalue("STATE",0), - supportEvents: false, - supportBonjour: false, - manfDescription: "Current State of your Lock", - designedMaxLength: 1 - }, - { - cType: types.TARGET_LOCK_MECHANISM_STATE_CTYPE, - - onUpdate: function(value) { - that.command("set","STATE",(value==1)?"true":"false") - }, - - onRead: function(callback) { - that.query("STATE",callback); - }, - - onRegister: function(characteristic) { + perms: ["pr", "ev"], + format: "bool", + initialValue: that.dpvalue("STATE", 0), + supportEvents: false, + supportBonjour: false, + manfDescription: "Current State of your Lock", + designedMaxLength: 1 + }, { + cType: types.TARGET_LOCK_MECHANISM_STATE_CTYPE, + + onUpdate: function(value) { + that.command("set", "STATE", (value == 1) ? "true" : "false") + }, + + onRead: function(callback) { + that.query("STATE", callback); + }, + + onRegister: function(characteristic) { that.reverseDP["STATE"] = true; that.currentStateCharacteristic["STATE"] = characteristic; characteristic.eventEnabled = true; that.remoteGetValue("STATE"); - }, + }, - - perms: ["pw","pr","ev"], - format: "bool", - initialValue: that.dpvalue("STATE",0), - supportEvents: false, - supportBonjour: false, - manfDescription: "Target State of your Lock", - designedMaxLength: 1 - } - - , - { - cType: types.TARGET_DOORSTATE_CTYPE, - - onUpdate: function(value) { - that.command("set","OPEN" , "true") - }, - onRead: function(callback) { - callback(1); - }, - - onRegister: function(characteristic) { + perms: ["pw", "pr", "ev"], + format: "bool", + initialValue: that.dpvalue("STATE", 0), + supportEvents: false, + supportBonjour: false, + manfDescription: "Target State of your Lock", + designedMaxLength: 1 + } + + , { + cType: types.TARGET_DOORSTATE_CTYPE, + + onUpdate: function(value) { + that.command("set", "OPEN", "true") + }, + + onRead: function(callback) { + callback(1); + }, + + onRegister: function(characteristic) { that.currentStateCharacteristic["OPEN"] = characteristic; characteristic.eventEnabled = true; - }, - - perms: ["pw","pr","ev"], - format: "bool", - initialValue: 1, - supportEvents: false, - supportBonjour: false, - manfDescription: "Open the Lock", - designedMaxLength: 1 - } + }, + + perms: ["pw", "pr", "ev"], + format: "bool", + initialValue: 1, + supportEvents: false, + supportBonjour: false, + manfDescription: "Open the Lock", + designedMaxLength: 1 + } ); - - + + } - if (this.type=="DIMMER") { - cTypes.push({ + if (this.type == "DIMMER") { + cTypes.push({ cType: types.POWER_STATE_CTYPE, onUpdate: function(value) { - that.command("set","LEVEL" , (value==true) ? "1" : "0") + that.command("set", "LEVEL", (value == true) ? "1" : "0") }, onRead: function(callback) { - that.query("LEVEL",callback); + that.query("LEVEL", callback); }, - - onRegister: function(characteristic) { - that.currentStateCharacteristic["LEVEL"] = characteristic; - characteristic.eventEnabled = true; - that.remoteGetValue("LEVEL"); + + onRegister: function(characteristic) { + that.currentStateCharacteristic["LEVEL"] = characteristic; + characteristic.eventEnabled = true; + that.remoteGetValue("LEVEL"); }, - - perms: ["pw","pr","ev"], + + perms: ["pw", "pr", "ev"], format: "bool", - initialValue: (that.dpvalue("LEVEL")>0,0), + initialValue: (that.dpvalue("LEVEL") > 0, 0), supportEvents: false, supportBonjour: false, manfDescription: "Change the power state", designedMaxLength: 1 - }, - { + }, { cType: types.BRIGHTNESS_CTYPE, onUpdate: function(value) { - that.delayed("set","LEVEL" , String(value/100),100); - }, - - onRead: function(callback) { - that.query("LEVEL",callback); - }, - - onRegister: function(characteristic) { - that.currentStateCharacteristic["LEVEL"] = characteristic; - characteristic.eventEnabled = true; - that.remoteGetValue("LEVEL"); + that.delayed("set", "LEVEL", String(value / 100), 100); }, - - perms: ["pw","pr","ev"], + onRead: function(callback) { + that.query("LEVEL", callback); + }, + + onRegister: function(characteristic) { + that.currentStateCharacteristic["LEVEL"] = characteristic; + characteristic.eventEnabled = true; + that.remoteGetValue("LEVEL"); + }, + + + perms: ["pw", "pr", "ev"], format: "int", - initialValue: that.dpvalue("LEVEL",0), + initialValue: that.dpvalue("LEVEL", 0), supportEvents: false, supportBonjour: false, manfDescription: "Adjust Brightness of Light", @@ -371,245 +367,282 @@ HomeMaticGenericChannel.prototype = { }); } - if (this.type=="BLIND") { - cTypes.push( - { - cType: types.WINDOW_COVERING_CURRENT_POSITION_CTYPE, - - onRead: function(callback) { - that.query("LEVEL",callback); - }, - - onRegister: function(characteristic) { + if (this.type == "BLIND") { + cTypes.push({ + cType: types.WINDOW_COVERING_CURRENT_POSITION_CTYPE, + + onRead: function(callback) { + that.query("LEVEL", callback); + }, + + onRegister: function(characteristic) { that.currentStateCharacteristic["LEVEL"] = characteristic; characteristic.eventEnabled = true; that.remoteGetValue("LEVEL"); + }, + + perms: ["pr", "ev"], + format: "int", + initialValue: that.dpvalue("LEVEL", 0), + supportEvents: false, + supportBonjour: false, + manfDescription: "Current Blind Position", + designedMinValue: 0, + designedMaxValue: 100, + designedMinStep: 1, + unit: "%" }, - perms: ["pr","ev"], - format: "int", - initialValue: that.dpvalue("LEVEL",0), - supportEvents: false, - supportBonjour: false, - manfDescription: "Current Blind Position", - designedMinValue: 0, - designedMaxValue: 100, - designedMinStep: 1, - unit: "%" - }, - - { - cType: types.WINDOW_COVERING_TARGET_POSITION_CTYPE, - - onUpdate: function(value) { - that.delayed("set","LEVEL" , String(value/100),100); - }, - + { + cType: types.WINDOW_COVERING_TARGET_POSITION_CTYPE, - onRead: function(callback) { - that.query("LEVEL",callback); - }, - - onRegister: function(characteristic) { + onUpdate: function(value) { + that.delayed("set", "LEVEL", String(value / 100), 100); + }, + + + onRead: function(callback) { + that.query("LEVEL", callback); + }, + + onRegister: function(characteristic) { that.currentStateCharacteristic["LEVEL"] = characteristic; characteristic.eventEnabled = true; that.remoteGetValue("LEVEL"); - }, + }, - perms: ["pw","pr","ev"], - format: "int", - initialValue: that.dpvalue("LEVEL",0), - supportEvents: false, - supportBonjour: false, - manfDescription: "Target Blind Position", - designedMinValue: 0, - designedMaxValue: 100, - designedMinStep: 1, - unit: "%" - }, - { - cType: types.WINDOW_COVERING_OPERATION_STATE_CTYPE, - - onRead: function(callback) { - that.query("DIRECTION",callback); - }, - - onRegister: function(characteristic) { + perms: ["pw", "pr", "ev"], + format: "int", + initialValue: that.dpvalue("LEVEL", 0), + supportEvents: false, + supportBonjour: false, + manfDescription: "Target Blind Position", + designedMinValue: 0, + designedMaxValue: 100, + designedMinStep: 1, + unit: "%" + }, { + cType: types.WINDOW_COVERING_OPERATION_STATE_CTYPE, + + onRead: function(callback) { + that.query("DIRECTION", callback); + }, + + onRegister: function(characteristic) { that.currentStateCharacteristic["DIRECTION"] = characteristic; characteristic.eventEnabled = true; that.remoteGetValue("DIRECTION"); - }, + }, + + perms: ["pr", "ev"], + format: "int", + initialValue: that.dpvalue("DIRECTION", 0), + supportEvents: false, + supportBonjour: false, + manfDescription: "Operating State ", + designedMinValue: 0, + designedMaxValue: 2, + designedMinStep: 1 + } - perms: ["pr","ev"], - format: "int", - initialValue: that.dpvalue("DIRECTION",0), - supportEvents: false, - supportBonjour: false, - manfDescription: "Operating State ", - designedMinValue: 0, - designedMaxValue: 2, - designedMinStep: 1 - } - ); } - - if (this.type=="SHUTTER_CONTACT") { - cTypes.push( - { - cType: types.CONTACT_SENSOR_STATE_CTYPE, - + + if (this.type == "SHUTTER_CONTACT") { + cTypes.push({ + cType: types.CONTACT_SENSOR_STATE_CTYPE, + onRead: function(callback) { - that.query("STATE",callback); + that.query("STATE", callback); }, - - onRegister: function(characteristic) { - that.currentStateCharacteristic["STATE"] = characteristic; - characteristic.eventEnabled = true; - that.remoteGetValue("STATE"); + + onRegister: function(characteristic) { + that.currentStateCharacteristic["STATE"] = characteristic; + characteristic.eventEnabled = true; + that.remoteGetValue("STATE"); }, - - perms: ["pr","ev"], - format: "bool", - initialValue: that.dpvalue("STATE",0), - supportEvents: false, - supportBonjour: false, - manfDescription: "Current State" - }); - } - - if (this.type=="MOTION_DETECTOR") { - cTypes.push( - { - cType: types.MOTION_DETECTED_CTYPE, - + + perms: ["pr", "ev"], + format: "bool", + initialValue: that.dpvalue("STATE", 0), + supportEvents: false, + supportBonjour: false, + manfDescription: "Current State" + }); + } + + if (this.type == "MOTION_DETECTOR") { + cTypes.push({ + cType: types.MOTION_DETECTED_CTYPE, + onRead: function(callback) { - that.query("MOTION",callback); + that.query("MOTION", callback); }, - - onRegister: function(characteristic) { - that.currentStateCharacteristic["MOTION"] = characteristic; - characteristic.eventEnabled = true; - that.remoteGetValue("MOTION"); + + onRegister: function(characteristic) { + that.currentStateCharacteristic["MOTION"] = characteristic; + characteristic.eventEnabled = true; + that.remoteGetValue("MOTION"); }, - - perms: ["pr","ev"], - format: "bool", - initialValue: that.dpvalue("MOTION",0), - supportEvents: false, - supportBonjour: false, - manfDescription: "Current Motion State" - }); - } - - if (this.type=="CLIMATECONTROL_RT_TRANSCEIVER") { - - cTypes.push({ - cType: types.NAME_CTYPE,onUpdate: null,perms: ["pr"],format: "string", - initialValue: this.name,supportEvents: true,supportBonjour: false,manfDescription: "Name of service",designedMaxLength: 255 - }, - - { - cType: types.CURRENTHEATINGCOOLING_CTYPE,onUpdate: null, - perms: ["pr"],format: "int",initialValue: 1,supportEvents: false, - supportBonjour: false,manfDescription: "Current Mode",designedMaxLength: 1,designedMinValue: 1,designedMaxValue: 1,designedMinStep: 1 - }, - - { - cType: types.TARGETHEATINGCOOLING_CTYPE,onUpdate: null,perms: ["pw","pr"], - format: "int",initialValue: 1,supportEvents: false,supportBonjour: false,manfDescription: "Target Mode", - designedMinValue: 1,designedMaxValue: 1,designedMinStep: 1 - }, - - { - cType: types.CURRENT_TEMPERATURE_CTYPE, - onUpdate: null, - - onRead: function(callback) { - that.query("ACTUAL_TEMPERATURE",callback); - }, - - onRegister: function(characteristic) { + + perms: ["pr", "ev"], + format: "bool", + initialValue: that.dpvalue("MOTION", 0), + supportEvents: false, + supportBonjour: false, + manfDescription: "Current Motion State" + }); + } + + if (this.type == "CLIMATECONTROL_RT_TRANSCEIVER") { + + cTypes.push({ + cType: types.NAME_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: this.name, + supportEvents: true, + supportBonjour: false, + manfDescription: "Name of service", + designedMaxLength: 255 + }, + + { + cType: types.CURRENTHEATINGCOOLING_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "int", + initialValue: 1, + supportEvents: false, + supportBonjour: false, + manfDescription: "Current Mode", + designedMaxLength: 1, + designedMinValue: 1, + designedMaxValue: 1, + designedMinStep: 1 + }, + + { + cType: types.TARGETHEATINGCOOLING_CTYPE, + onUpdate: null, + perms: ["pw", "pr"], + format: "int", + initialValue: 1, + supportEvents: false, + supportBonjour: false, + manfDescription: "Target Mode", + designedMinValue: 1, + designedMaxValue: 1, + designedMinStep: 1 + }, + + { + cType: types.CURRENT_TEMPERATURE_CTYPE, + onUpdate: null, + + onRead: function(callback) { + that.query("ACTUAL_TEMPERATURE", callback); + }, + + onRegister: function(characteristic) { that.currentStateCharacteristic["ACTUAL_TEMPERATURE"] = characteristic; characteristic.eventEnabled = true; that.remoteGetValue("ACTUAL_TEMPERATURE"); - }, - perms: ["pw","pr","ev"], perms: ["pr"],format: "double", - initialValue: that.dpvalue("ACTUAL_TEMPERATURE",20), - supportEvents: false,supportBonjour: false,manfDescription: "Current Temperature",unit: "celsius" - }, - - { - cType: types.TARGET_TEMPERATURE_CTYPE, - onUpdate: function(value) { - that.delayed("set", "SET_TEMPERATURE", value,500); - }, - onRead: function(callback) { - that.query("SET_TEMPERATURE",callback); - - }, - onRegister: function(characteristic) { + }, + perms: ["pw", "pr", "ev"], + perms: ["pr"], + format: "double", + initialValue: that.dpvalue("ACTUAL_TEMPERATURE", 20), + supportEvents: false, + supportBonjour: false, + manfDescription: "Current Temperature", + unit: "celsius" + }, + + { + cType: types.TARGET_TEMPERATURE_CTYPE, + onUpdate: function(value) { + that.delayed("set", "SET_TEMPERATURE", value, 500); + }, + onRead: function(callback) { + that.query("SET_TEMPERATURE", callback); + + }, + onRegister: function(characteristic) { that.currentStateCharacteristic["SET_TEMPERATURE"] = characteristic; characteristic.eventEnabled = true; that.remoteGetValue("SET_TEMPERATURE"); - }, - perms: ["pw","pr","ev"],format: "double", - initialValue: that.dpvalue("SET_TEMPERATURE",16), - supportEvents: false,supportBonjour: false, manfDescription: "Target Temperature", - designedMinValue: 16,designedMaxValue: 38,designedMinStep: 1,unit: "celsius" - }, - - { - cType: types.TEMPERATURE_UNITS_CTYPE,onRead: null, - perms: ["pr"],format: "int",initialValue: 0,supportEvents: false, - supportBonjour: false,manfDescription: "Current Temperature Unit",unit: "celsius" + }, + perms: ["pw", "pr", "ev"], + format: "double", + initialValue: that.dpvalue("SET_TEMPERATURE", 16), + supportEvents: false, + supportBonjour: false, + manfDescription: "Target Temperature", + designedMinValue: 16, + designedMaxValue: 38, + designedMinStep: 1, + unit: "celsius" + }, + + { + cType: types.TEMPERATURE_UNITS_CTYPE, + onRead: null, + perms: ["pr"], + format: "int", + initialValue: 0, + supportEvents: false, + supportBonjour: false, + manfDescription: "Current Temperature Unit", + unit: "celsius" + } + + ); } - ); - } - - + return cTypes }, sType: function() { - - if (this.type=="SWITCH") { - - if (this.special=="OUTLET") { - return types.OUTLET_STYPE; - } else { - return types.LIGHTBULB_STYPE; - } - } - - if (this.type=="DIMMER") { - return types.LIGHTBULB_STYPE; - } - if (this.type=="BLIND") { + if (this.type == "SWITCH") { + + if (this.special == "OUTLET") { + return types.OUTLET_STYPE; + } else { + return types.LIGHTBULB_STYPE; + } + } + + if (this.type == "DIMMER") { + return types.LIGHTBULB_STYPE; + } + + if (this.type == "BLIND") { return types.WINDOW_COVERING_STYPE; - } + } - if (this.type=="CLIMATECONTROL_RT_TRANSCEIVER") { + if (this.type == "CLIMATECONTROL_RT_TRANSCEIVER") { return types.THERMOSTAT_STYPE; - } - - if (this.type=="SHUTTER_CONTACT") { + } + + if (this.type == "SHUTTER_CONTACT") { return types.CONTACT_SENSOR_STYPE; - } - - if (this.type=="MOTION_DETECTOR") { - return types.MOTION_SENSOR_STYPE - } + } + + if (this.type == "MOTION_DETECTOR") { + return types.MOTION_SENSOR_STYPE + } + + + if (this.type == "KEYMATIC") { + return types.LOCK_MECHANISM_STYPE + } + - if (this.type=="KEYMATIC") { - return types.LOCK_MECHANISM_STYPE - } - - - }, getServices: function() { @@ -617,8 +650,7 @@ HomeMaticGenericChannel.prototype = { var services = [{ sType: types.ACCESSORY_INFORMATION_STYPE, characteristics: this.informationCharacteristics(), - }, - { + }, { sType: this.sType(), characteristics: this.controlCharacteristics(that) }]; From 7623bf4204fdc672a5a18398ad3374dced027844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Ko=CC=88nig?= Date: Thu, 29 Oct 2015 20:28:00 +0100 Subject: [PATCH 5/7] cleanup formatting --- platforms/HomeMatic.js | 121 ++++++++++++++++---------------- platforms/HomematicChannel.js | 128 ++++++++++++++++------------------ 2 files changed, 122 insertions(+), 127 deletions(-) diff --git a/platforms/HomeMatic.js b/platforms/HomeMatic.js index 1b99729..a451451 100644 --- a/platforms/HomeMatic.js +++ b/platforms/HomeMatic.js @@ -1,3 +1,4 @@ +"use strict"; // // Homematic Platform Shim for HomeBridge // @@ -7,13 +8,13 @@ var types = require("hap-nodejs/accessories/types.js"); -var xmlrpc = require('homematic-xmlrpc') +var xmlrpc = require("homematic-xmlrpc"); var request = require("request"); var http = require("http"); var path = require("path"); -var HomeMaticGenericChannel = require(path.resolve(__dirname, 'HomematicChannel.js')); +var HomeMaticGenericChannel = require(path.resolve(__dirname, "HomematicChannel.js")); @@ -28,29 +29,29 @@ RegaRequest.prototype = { var post_options = { host: this.ccuIP, - port: '80', - path: '/tclrega.exe', - method: 'POST', + port: "80", + path: "/tclrega.exe", + method: "POST", headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': script.length + "Content-Type": "application/x-www-form-urlencoded", + "Content-Length": script.length } }; var post_req = http.request(post_options, function(res) { var data = ""; - res.setEncoding('binary'); - res.on('data', function(chunk) { + res.setEncoding("binary"); + res.on("data", function(chunk) { data += chunk.toString(); }); - res.on('end', function() { + res.on("end", function() { var pos = data.lastIndexOf(""); var response = (data.substring(0, pos)); callback(response); }); }); - post_req.on('error', function(e) { + post_req.on("error", function(e) { callback("{}"); }); @@ -65,23 +66,24 @@ RegaRequest.prototype = { var script = "var d = dom.GetObject(\"" + channel + "." + datapoint + "\");if (d){Write(d.State());}"; //that.log("Rega Request " + script); - var regarequest = this.script(script, function(data) { + this.script(script, function(data) { that.log("Rega Response" + data); - if (data != undefined) { + if (data !== undefined) { callback(parseFloat(data)); } }); }, setValue: function(channel, datapoint, value) { - var that = this; var script = "var d = dom.GetObject(\"" + channel + "." + datapoint + "\");if (d){d.State(\"" + value + "\");}"; - //that.log("Rega Request " + script); - var regarequest = this.script(script, function(data) {}); + //this.log("Rega Request " + script); + this.script(script, function(data) { + + }); } -} +}; function HomematicRPC(log, ccuip, platform) { this.log = log; @@ -106,29 +108,29 @@ HomematicRPC.prototype = { } this.localIP = ip; - this.log("Local IP: " + this.localIP) + this.log("Local IP: " + this.localIP); this.server = xmlrpc.createServer({ host: this.localIP, port: 9090 - }) - - this.server.on('NotFound', function(method, params) { - that.log('Method ' + method + ' does not exist'); }); - this.server.on('system.listMethods', function(err, params, callback) { - that.log('Method call params for \'system.listMethods\': ' + params) - callback(null, ['system.listMethods', 'system.multicall']); + this.server.on("NotFound", function(method, params) { + that.log("Method " + method + " does not exist"); + }); + + this.server.on("system.listMethods", function(err, params, callback) { + that.log("Method call params for 'system.listMethods': " + params); + callback(null, ["system.listMethods", "system.multicall"]); }); - this.server.on('system.multicall', function(err, params, callback) { + this.server.on("system.multicall", function(err, params, callback) { params.map(function(events) { try { events.map(function(event) { - if ((event["methodName"] == "event") && (event['params'] != undefined)) { - var params = event['params']; + if ((event["methodName"] == "event") && (event["params"] !== undefined)) { + var params = event["params"]; var channel = "BidCos-RF." + params[1]; var datapoint = params[2]; var value = params[3]; @@ -144,11 +146,11 @@ HomematicRPC.prototype = { callback(null); }); - this.log('XML-RPC server listening on port 9090') + this.log("XML-RPC server listening on port 9090"); this.connect(); - process.on('SIGINT', function() { + process.on("SIGINT", function() { if (that.stopping) { return; } @@ -156,7 +158,7 @@ HomematicRPC.prototype = { that.stop(); }); - process.on('SIGTERM', function() { + process.on("SIGTERM", function() { if (that.stopping) { return; } @@ -167,29 +169,28 @@ HomematicRPC.prototype = { }, getIPAddress: function() { - var interfaces = require('os').networkInterfaces(); + var interfaces = require("os").networkInterfaces(); for (var devName in interfaces) { var iface = interfaces[devName]; for (var i = 0; i < iface.length; i++) { var alias = iface[i]; - if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) + if (alias.family === "IPv4" && alias.address !== "127.0.0.1" && !alias.internal) return alias.address; } } - return '0.0.0.0'; + return "0.0.0.0"; }, getValue: function(channel, datapoint, callback) { var that = this; - if (this.client == undefined) { + if (this.client === undefined) { that.log("Returning cause client is invalid"); return; } if (channel.indexOf("BidCos-RF.") > -1)  { channel = channel.substr(10); - this.log("Calling rpc getValue"); - this.client.methodCall('getValue', [channel, datapoint], function(error, value) { + this.client.methodCall("getValue", [channel, datapoint], function(error, value) { callback(value); }); return; @@ -200,41 +201,41 @@ HomematicRPC.prototype = { var that = this; - if (this.client == undefined) return; + if (this.client === undefined) return; if (channel.indexOf("BidCos-RF.") > -1)  { channel = channel.substr(10); } - this.client.methodCall('setValue', [channel, datapoint, value], function(error, value) { + this.client.methodCall("setValue", [channel, datapoint, value], function(error, value) { }); }, connect: function() { var that = this; - this.log('Creating Local HTTP Client for CCU RPC Events'); + this.log("Creating Local HTTP Client for CCU RPC Events"); this.client = xmlrpc.createClient({ host: this.ccuip, port: 2001, - path: '/' + path: "/" }); - this.log('CCU RPC Init Call on port 2001'); - this.client.methodCall('init', ['http://' + this.localIP + ':9090', 'homebridge'], function(error, value) { - that.log('CCU Response ....') + this.log("CCU RPC Init Call on port 2001"); + this.client.methodCall("init", ["http://" + this.localIP + ":9090", "homebridge"], function(error, value) { + that.log("CCU Response ...."); }); }, stop: function() { this.log("Removing Event Server"); - this.client.methodCall('init', ['http://' + this.localIP + ':9090'], function(error, value) { + this.client.methodCall("init", ["http://" + this.localIP + ":9090"], function(error, value) { }); setTimeout(process.exit(0), 1000); } -} +}; function HomeMaticPlatform(log, config) { @@ -267,35 +268,35 @@ HomeMaticPlatform.prototype = { var regarequest = new RegaRequest(this.log, this.ccuIP).script(script, function(data) { var json = JSON.parse(data); - if (json['devices'] != undefined) { - json['devices'].map(function(device) { + if (json["devices"] !== undefined) { + json["devices"].map(function(device) { var isFiltered = false; - if ((that.filter_device != undefined) && (that.filter_device.indexOf(device.address) > -1)) { + if ((that.filter_device !== undefined) && (that.filter_device.indexOf(device.address) > -1)) { isFiltered = true; } else { isFiltered = false; } // that.log('device address:', device.address); - if ((device['channels'] != undefined) && (!isFiltered)) { + if ((device["channels"] !== undefined) && (!isFiltered)) { - device['channels'].map(function(ch) { + device["channels"].map(function(ch) { var isChannelFiltered = false; - if ((that.filter_channel != undefined) && (that.filter_channel.indexOf(ch.address) > -1)) { + if ((that.filter_channel !== undefined) && (that.filter_channel.indexOf(ch.address) > -1)) { isChannelFiltered = true; } else { isChannelFiltered = false; } // that.log('name', ch.name, ' -> address:', ch.address); - if ((ch.address != undefined) && (!isChannelFiltered)) { + if ((ch.address !== undefined) && (!isChannelFiltered)) { if ((ch.type == "SWITCH") || (ch.type == "BLIND") || (ch.type == "SHUTTER_CONTACT") || (ch.type == "DIMMER") || (ch.type == "CLIMATECONTROL_RT_TRANSCEIVER") ||  (ch.type == "MOTION_DETECTOR") ||  (ch.type == "KEYMATIC")) { // Switch found // Check if marked as Outlet - var special = (that.outlets.indexOf(ch.address) > -1) ? 'OUTLET' : undefined; - accessory = new HomeMaticGenericChannel(that.log, that, ch.id, ch.name, ch.type, ch.address, special); + var special = (that.outlets.indexOf(ch.address) > -1) ? "OUTLET" : undefined; + var accessory = new HomeMaticGenericChannel(that.log, that, ch.id, ch.name, ch.type, ch.address, special); that.foundAccessories.push(accessory); } @@ -370,14 +371,14 @@ HomeMaticPlatform.prototype = { script = script + command; }); this.sendQueue = []; - //this.log("RegaSend: " + script); + //this.log('RegaSend: ' + script); var regarequest = new RegaRequest(this.log, this.ccuIP).script(script, function(data) {}); }, sendRequest: function(accessory, script, callback) { - var that = this; + var regarequest = new RegaRequest(this.log, this.ccuIP).script(script, function(data) { - if (data != undefined) { + if (data !== undefined) { try { var json = JSON.parse(data); callback(json); @@ -399,11 +400,11 @@ HomeMaticPlatform.prototype = { var that = this; this.delayed[delay] = setTimeout(function() { clearTimeout(that.delayed[delay]); - that.sendPreparedRequests() + that.sendPreparedRequests(); }, delay ? delay : 100); this.log("New Timer was set"); } -} +}; diff --git a/platforms/HomematicChannel.js b/platforms/HomematicChannel.js index 4e5a34b..5b6cff2 100644 --- a/platforms/HomematicChannel.js +++ b/platforms/HomematicChannel.js @@ -1,3 +1,4 @@ +"use strict"; var types = require("hap-nodejs/accessories/types.js"); @@ -21,9 +22,7 @@ HomeMaticGenericChannel.prototype = { // Return current States query: function(dp, callback) { - var that = this; - - if (this.state[dp] != undefined) { + if (this.state[dp] !== undefined) { callback(this.state[dp]); } else { // that.log("No cached Value found start fetching and send temp 0 back"); @@ -34,7 +33,7 @@ HomeMaticGenericChannel.prototype = { }, dpvalue: function(dp, fallback) { - if (this.state[dp] != undefined) { + if (this.state[dp] !== undefined) { return (this.state[dp]); } else { return fallback; @@ -66,8 +65,8 @@ HomeMaticGenericChannel.prototype = { reverse: function(value) { if (value == "true") return "false"; if (value == "false") return "true"; - if (value == 0) return 1; - if (value == 1) return 0; + if (value === 0) return 1; + if (value === 1) return 0; if (value == "0") return "1"; if (value == "1") return "0"; return value; @@ -76,11 +75,11 @@ HomeMaticGenericChannel.prototype = { cache: function(dp, value) { var that = this; - if ((that.reverseDP[dp] != undefined) && (that.reverseDP[dp] == true)) { + if ((that.reverseDP[dp] !== undefined) && (that.reverseDP[dp] === true)) { value = that.reverse(value); } - if (that.currentStateCharacteristic[dp] != undefined) { + if (that.currentStateCharacteristic[dp] !== undefined) { that.currentStateCharacteristic[dp].updateValue(value, null); } this.state[dp] = value; @@ -89,7 +88,7 @@ HomeMaticGenericChannel.prototype = { delayed: function(mode, dp, value, delay) { - if (this.eventupdate == true) { + if (this.eventupdate === true) { return; } @@ -102,13 +101,13 @@ HomeMaticGenericChannel.prototype = { var that = this; this.delayed[delay] = setTimeout(function() { clearTimeout(that.delayed[delay]); - that.command(mode, dp, value) + that.command(mode, dp, value); }, delay ? delay : 100); }, command: function(mode, dp, value, callback) { - if (this.eventupdate == true) { + if (this.eventupdate === true) { return; } var that = this; @@ -170,12 +169,12 @@ HomeMaticGenericChannel.prototype = { supportBonjour: false, manfDescription: "Identify Accessory", designedMaxLength: 1 - }] + }]; }, controlCharacteristics: function(that) { - cTypes = [{ + var cTypes = [{ cType: types.NAME_CTYPE, onUpdate: null, perms: ["pr"], @@ -185,14 +184,14 @@ HomeMaticGenericChannel.prototype = { supportBonjour: false, manfDescription: "Name of service", designedMaxLength: 255 - }] + }]; if (this.type == "SWITCH") { cTypes.push({ cType: types.POWER_STATE_CTYPE, onUpdate: function(value) { - that.command("set", "STATE", (value == 1) ? true : false) + that.command("set", "STATE", (value == 1) ? true : false); }, onRead: function(callback) { @@ -228,37 +227,37 @@ HomeMaticGenericChannel.prototype = { supportBonjour: false, manfDescription: "Is Outlet in Use", designedMaxLength: 1 - }) + }); } } if (this.type == "KEYMATIC") { cTypes.push({ - cType: types.CURRENT_LOCK_MECHANISM_STATE_CTYPE, + cType: types.CURRENT_LOCK_MECHANISM_STATE_CTYPE, - onRead: function(callback) { + onRead: function(callback) { that.query("STATE", callback); }, - onRegister: function(characteristic) { + onRegister: function(characteristic) { that.currentStateCharacteristic["STATE"] = characteristic; characteristic.eventEnabled = true; that.remoteGetValue("STATE"); }, - perms: ["pr", "ev"], - format: "bool", - initialValue: that.dpvalue("STATE", 0), - supportEvents: false, - supportBonjour: false, - manfDescription: "Current State of your Lock", - designedMaxLength: 1 - }, { + perms: ["pr", "ev"], + format: "bool", + initialValue: that.dpvalue("STATE", 0), + supportEvents: false, + supportBonjour: false, + manfDescription: "Current State of your Lock", + designedMaxLength: 1 + }, { cType: types.TARGET_LOCK_MECHANISM_STATE_CTYPE, onUpdate: function(value) { - that.command("set", "STATE", (value == 1) ? "true" : "false") + that.command("set", "STATE", (value == 1) ? "true" : "false"); }, onRead: function(callback) { @@ -280,13 +279,13 @@ HomeMaticGenericChannel.prototype = { supportBonjour: false, manfDescription: "Target State of your Lock", designedMaxLength: 1 - } + }, - , { + { cType: types.TARGET_DOORSTATE_CTYPE, onUpdate: function(value) { - that.command("set", "OPEN", "true") + that.command("set", "OPEN", "true"); }, onRead: function(callback) { @@ -307,17 +306,13 @@ HomeMaticGenericChannel.prototype = { designedMaxLength: 1 } ); - - } - - if (this.type == "DIMMER") { cTypes.push({ cType: types.POWER_STATE_CTYPE, onUpdate: function(value) { - that.command("set", "LEVEL", (value == true) ? "1" : "0") + that.command("set", "LEVEL", (value == true) ? "1" : "0"); }, onRead: function(callback) { @@ -369,29 +364,29 @@ HomeMaticGenericChannel.prototype = { if (this.type == "BLIND") { cTypes.push({ - cType: types.WINDOW_COVERING_CURRENT_POSITION_CTYPE, + cType: types.WINDOW_COVERING_CURRENT_POSITION_CTYPE, - onRead: function(callback) { + onRead: function(callback) { that.query("LEVEL", callback); }, - onRegister: function(characteristic) { + onRegister: function(characteristic) { that.currentStateCharacteristic["LEVEL"] = characteristic; characteristic.eventEnabled = true; that.remoteGetValue("LEVEL"); }, - perms: ["pr", "ev"], - format: "int", - initialValue: that.dpvalue("LEVEL", 0), - supportEvents: false, - supportBonjour: false, - manfDescription: "Current Blind Position", - designedMinValue: 0, - designedMaxValue: 100, - designedMinStep: 1, - unit: "%" - }, + perms: ["pr", "ev"], + format: "int", + initialValue: that.dpvalue("LEVEL", 0), + supportEvents: false, + supportBonjour: false, + manfDescription: "Current Blind Position", + designedMinValue: 0, + designedMaxValue: 100, + designedMinStep: 1, + unit: "%" + }, { cType: types.WINDOW_COVERING_TARGET_POSITION_CTYPE, @@ -497,16 +492,16 @@ HomeMaticGenericChannel.prototype = { if (this.type == "CLIMATECONTROL_RT_TRANSCEIVER") { cTypes.push({ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: true, - supportBonjour: false, - manfDescription: "Name of service", - designedMaxLength: 255 - }, + cType: types.NAME_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: this.name, + supportEvents: true, + supportBonjour: false, + manfDescription: "Name of service", + designedMaxLength: 255 + }, { cType: types.CURRENTHEATINGCOOLING_CTYPE, @@ -550,8 +545,7 @@ HomeMaticGenericChannel.prototype = { characteristic.eventEnabled = true; that.remoteGetValue("ACTUAL_TEMPERATURE"); }, - perms: ["pw", "pr", "ev"], - perms: ["pr"], + perms: ["pr", "ev"], format: "double", initialValue: that.dpvalue("ACTUAL_TEMPERATURE", 20), supportEvents: false, @@ -602,7 +596,7 @@ HomeMaticGenericChannel.prototype = { } - return cTypes + return cTypes; }, sType: function() { @@ -633,12 +627,12 @@ HomeMaticGenericChannel.prototype = { } if (this.type == "MOTION_DETECTOR") { - return types.MOTION_SENSOR_STYPE + return types.MOTION_SENSOR_STYPE; } if (this.type == "KEYMATIC") { - return types.LOCK_MECHANISM_STYPE + return types.LOCK_MECHANISM_STYPE; } @@ -649,12 +643,12 @@ HomeMaticGenericChannel.prototype = { var that = this; var services = [{ sType: types.ACCESSORY_INFORMATION_STYPE, - characteristics: this.informationCharacteristics(), + characteristics: this.informationCharacteristics() }, { sType: this.sType(), characteristics: this.controlCharacteristics(that) }]; - this.log("Loaded services for " + this.name) + this.log("Loaded services for " + this.name); return services; } }; From 1dfa190b18815dbf3ba8d5846763da3b728aa1f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6nig?= Date: Thu, 29 Oct 2015 21:04:32 +0100 Subject: [PATCH 6/7] Add HomeMatic Platform sample --- config-sample.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/config-sample.json b/config-sample.json index 0e04cfe..a199ea6 100644 --- a/config-sample.json +++ b/config-sample.json @@ -115,7 +115,15 @@ "username": "your netatmo username", "password": "your netatmo password" } - } + }, + { + "platform": "HomeMatic", + "name": "HomeMatic CCU", + "ccu_ip": "192.168.0.100", + "filter_device":[], + "filter_channel":["BidCos-RF.KEQXXXXXXX:4", "BidCos-RF.LEQXXXXXXX:2"], + "outlets":[ "BidCos-RF.KEQXXXXXXX:4","BidCos-RF.IEQXXXXXXX:1"] + }, ], "accessories": [ From 70f2dc6230da7e5e16c199fd8352dad537882164 Mon Sep 17 00:00:00 2001 From: bwilliot Date: Sat, 31 Oct 2015 11:30:01 +0100 Subject: [PATCH 7/7] Update PhilipsHue.js fix issue --- platforms/PhilipsHue.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/PhilipsHue.js b/platforms/PhilipsHue.js index 9d3394c..81f36b8 100644 --- a/platforms/PhilipsHue.js +++ b/platforms/PhilipsHue.js @@ -239,7 +239,7 @@ PhilipsHueAccessory.prototype = { if (err.code == "ECONNRESET") { setTimeout(function() { this.executeChange(characteristic, value, callback); - }, 300); + }.bind(this), 300); } else { this.log(err); callback(new Error(err));