diff --git a/accessories/HomeMatic.js b/accessories/HomeMatic.js index ce575b1..89e43b0 100644 --- a/accessories/HomeMatic.js +++ b/accessories/HomeMatic.js @@ -30,7 +30,31 @@ HomeMatic.prototype = { } }); }, + getPowerState: function(callback) { + var that = this; + + this.log("Getting Power State of CCU"); + request.get({ + url: "http://"+this.ccuIP+"/config/xmlapi/state.cgi?datapoint_id="+this.ccuID, + }, function(err, response, body) { + if (!err && response.statusCode == 200) { + + //that.log("Response:"+response.body); + var responseString = response.body.substring(83,87); + //that.log(responseString); + switch(responseString){ + case "true": {modvalue = "1";break;} + case "fals": {modvalue = "0";break;} + } + callback(parseInt(modvalue)); + that.log("Getting Power State complete."); + } + else { + that.log("Error '"+err+"' getting Power State: " + body); + } + }); + }, getServices: function() { var that = this; return [{ @@ -101,6 +125,7 @@ HomeMatic.prototype = { },{ cType: types.POWER_STATE_CTYPE, onUpdate: function(value) { that.setPowerState(value); }, + onRead: function(callback) { that.getPowerState(callback); }, perms: ["pw","pr","ev"], format: "bool", initialValue: false, diff --git a/accessories/HomeMaticThermo.js b/accessories/HomeMaticThermo.js new file mode 100644 index 0000000..f3d300f --- /dev/null +++ b/accessories/HomeMaticThermo.js @@ -0,0 +1,264 @@ +var types = require("HAP-NodeJS/accessories/types.js"); +var request = require("request"); + +function HomeMaticThermo(log, config) { + this.log = log; + this.name = config["name"]; + this.ccuIDTargetTemp = config["ccu_id_TargetTemp"]; + this.ccuIDCurrentTemp = config["ccu_id_CurrentTemp"]; + this.ccuIDControlMode = config["ccu_id_ControlMode"]; + this.ccuIDManuMode = config["ccu_id_ManuMode"]; + this.ccuIDAutoMode = config["ccu_id_AutoMode"]; + this.ccuIP = config["ccu_ip"]; +} + +HomeMaticThermo.prototype = { + + setTargetTemperature: function(value) { + + var that = this; + + this.log("Setting target Temperature of CCU to " + value); + this.log(this.ccuIDTargetTemp + " " + value); + + request.put({ + url: "http://"+this.ccuIP+"/config/xmlapi/statechange.cgi?ise_id="+this.ccuIDTargetTemp+"&new_value="+ value, + }, function(err, response, body) { + + if (!err && response.statusCode == 200) { + that.log("State change complete."); + } + else { + that.log("Error '"+err+"' setting Temperature: " + body); + } + }); + }, + getCurrentTemperature: function(callback) { + + var that = this; + + this.log("Getting current Temperature of CCU"); + request.get({ + url: "http://"+this.ccuIP+"/config/xmlapi/state.cgi?datapoint_id="+this.ccuIDCurrentTemp, + }, function(err, response, body) { + + if (!err && response.statusCode == 200) { + + //that.log("Response:"+response.body); + var responseString = response.body.substring(83,87); + //that.log(responseString); + callback(parseFloat(responseString)); + //that.log("Getting current temperature complete."); + } + else { + that.log("Error '"+err+"' getting Temperature: " + body); + } + }); + }, + getTargetTemperature: function(callback) { + + var that = this; + +this.log("Getting target Temperature of CCU"); + request.get({ + url: "http://"+this.ccuIP+"/config/xmlapi/state.cgi?datapoint_id="+this.ccuIDTargetTemp, + }, function(err, response, body) { + + if (!err && response.statusCode == 200) { + + //that.log("Response:"+response.body); + var responseString = response.body.substring(83,87); + //that.log(responseString); + callback(parseFloat(responseString)); + //that.log("Getting target temperature complete."); + } + else { + that.log("Error '"+err+"' getting Temperature: " + body); + } + }); + }, + getMode: function(callback) { + + var that = this; + + //this.log("Getting target Mode of CCU"); + //this.log(this.ccuID+ value); + + request.get({ + url: "http://"+this.ccuIP+"/config/xmlapi/state.cgi?datapoint_id="+this.ccuIDControlMode, + }, function(err, response, body) { + + if (!err && response.statusCode == 200) { + + //that.log("Response:"+response.body); + var responseInt = response.body.substring(83,84); + //that.log(responseString); + if (responseInt == 1) + { callback(parseInt("0")); } + if (responseInt == 0) + { callback(parseInt("1")); } + //that.log("Getting mode complete."); + } + else { + that.log("Error '"+err+"' getting Mode: " + body); + } + }); + }, + setMode: function(value) { + + var that = this; + + //this.log("Seting target Mode of CCU:" + value); + var modvalue; + var dpID; + switch(value) { + case 3: {modvalue = "true";dpID=this.ccuIDAutoMode;break;} //auto + case 1: {modvalue = "true";dpID=this.ccuIDAutoMode;break;} //heating => auto + default: {modvalue = "1";dpID=this.ccuIDManuMode;} //default => off (manual) + } + + request.put({ + url: "http://"+this.ccuIP+"/config/xmlapi/statechange.cgi?ise_id="+dpID+"&new_value="+ modvalue, + }, function(err, response, body) { + + if (!err && response.statusCode == 200) { + //that.log("Setting Mode complete."); + } + else { + that.log("Error '"+err+"' setting Mode: " + body); + } + }); + }, + getServices: function() { + var that = this; + return [{ + sType: types.ACCESSORY_INFORMATION_STYPE, + characteristics: [{ + cType: types.NAME_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: this.name, + supportEvents: false, + supportBonjour: false, + manfDescription: "Name of the accessory", + designedMaxLength: 255 + },{ + cType: types.MANUFACTURER_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: "test", + supportEvents: false, + supportBonjour: false, + manfDescription: "Manufacturer", + designedMaxLength: 255 + },{ + cType: types.MODEL_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: "test", + supportEvents: false, + supportBonjour: false, + manfDescription: "Model", + designedMaxLength: 255 + },{ + cType: types.SERIAL_NUMBER_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: "A1S2NREF88EW", + supportEvents: false, + supportBonjour: false, + manfDescription: "SN", + designedMaxLength: 255 + },{ + cType: types.IDENTIFY_CTYPE, + onUpdate: null, + perms: ["pw"], + format: "bool", + initialValue: false, + supportEvents: false, + supportBonjour: false, + manfDescription: "Identify Accessory", + designedMaxLength: 1 + }] + },{ + sType: types.THERMOSTAT_STYPE, + characteristics: [{ + cType: types.NAME_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: this.name, + supportEvents: false, + supportBonjour: false, + manfDescription: "Name of service", + designedMaxLength: 255 + },{ + cType: types.CURRENTHEATINGCOOLING_CTYPE, + onRead: function(callback) { that.getMode(callback); }, + perms: ["pr","ev"], + format: "int", + initialValue: 0, + supportEvents: false, + supportBonjour: false, + manfDescription: "Current Mode", + designedMaxLength: 1, + designedMinValue: 0, + designedMaxValue: 2, + designedMinStep: 1, + },{ + cType: types.TARGETHEATINGCOOLING_CTYPE, + onRead: function(callback) { that.getMode(callback); }, + onUpdate: function(value) { that.setMode(value);}, + perms: ["pw","pr","ev"], + format: "int", + initialValue: 0, + supportEvents: false, + supportBonjour: false, + manfDescription: "Target Mode", + designedMinValue: 0, + designedMaxValue: 3, + designedMinStep: 1, + },{ + cType: types.CURRENT_TEMPERATURE_CTYPE, + onRead: function(callback) { that.getCurrentTemperature(callback); }, + onUpdate: null, + perms: ["pr","ev"], + format: "float", + initialValue: 13.0, + supportEvents: false, + supportBonjour: false, + manfDescription: "Current Temperature", + unit: "celsius" + },{ + cType: types.TARGET_TEMPERATURE_CTYPE, + onUpdate: function(value) { that.setTargetTemperature(value); }, + onRead: function(callback) { that.getTargetTemperature(callback); }, + perms: ["pw","pr","ev"], + format: "float", + initialValue: 19.0, + supportEvents: false, + supportBonjour: false, + manfDescription: "Target Temperature", + designedMinValue: 4, + designedMaxValue: 25, + designedMinStep: 0.1, + unit: "celsius" + },{ + cType: types.TEMPERATURE_UNITS_CTYPE, + onUpdate: null, + perms: ["pr","ev"], + format: "int", + initialValue: 0, + supportEvents: false, + supportBonjour: false, + manfDescription: "Unit" + }] + }]; + } +}; + +module.exports.accessory = HomeMaticThermo; diff --git a/accessories/HomeMaticWindow.js b/accessories/HomeMaticWindow.js new file mode 100644 index 0000000..b7e585d --- /dev/null +++ b/accessories/HomeMaticWindow.js @@ -0,0 +1,123 @@ +var types = require("HAP-NodeJS/accessories/types.js"); +var Characteristic = require("HAP-NodeJS").Characteristic; +var request = require("request"); + +function HomeMaticWindow(log, config) { + this.log = log; + this.name = config["name"]; + this.ccuID = config["ccu_id"]; + this.ccuIP = config["ccu_ip"]; +} + +HomeMaticWindow.prototype = { + + + getPowerState: function(callback) { + var that = this; + + this.log("Getting Window State of CCU"); + request.get({ + url: "http://"+this.ccuIP+"/config/xmlapi/state.cgi?datapoint_id="+this.ccuID, + }, function(err, response, body) { + + if (!err && response.statusCode == 200) { + + //that.log("Response:"+response.body); + var responseString = response.body.substring(83,84); + //that.log(responseString); + switch(responseString){ + case "0": {callback(Characteristic.ContactSensorState.CONTACT_DETECTED);break;} + case "1": {callback(Characteristic.ContactSensorState.CONTACT_NOT_DETECTED);break;} + case "2": {callback(Characteristic.ContactSensorState.CONTACT_NOT_DETECTED);break;} + } + that.log("Getting Window State complete."); + } + else { + that.log("Error '"+err+"' getting Window State: " + body); + } + }); + }, + + getServices: function() { + var that = this; + return [{ + sType: types.ACCESSORY_INFORMATION_STYPE, + characteristics: [{ + cType: types.NAME_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: this.name, + supportEvents: false, + supportBonjour: false, + manfDescription: "Name of the accessory", + designedMaxLength: 255 + },{ + cType: types.MANUFACTURER_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: "Homematic", + supportEvents: false, + supportBonjour: false, + manfDescription: "Manufacturer", + designedMaxLength: 255 + },{ + cType: types.MODEL_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: "HM-Sec-RHS", + supportEvents: false, + supportBonjour: false, + manfDescription: "Model", + designedMaxLength: 255 + },{ + cType: types.SERIAL_NUMBER_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: "A1S2NASF88EW", + supportEvents: false, + supportBonjour: false, + manfDescription: "SN", + designedMaxLength: 255 + },{ + cType: types.IDENTIFY_CTYPE, + onUpdate: null, + perms: ["pw"], + format: "bool", + initialValue: false, + supportEvents: false, + supportBonjour: false, + manfDescription: "Identify Accessory", + designedMaxLength: 1 + }] + },{ + sType: types.CONTACT_SENSOR_STYPE, + characteristics: [{ + cType: types.NAME_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: this.name, + supportEvents: false, + supportBonjour: false, + manfDescription: "Name of service", + designedMaxLength: 255 + },{ + cType: types.CONTACT_SENSOR_STATE_CTYPE, + onRead: function(callback) { that.getPowerState(callback); }, + perms: ["pr","ev"], + format: "bool", + initialValue: false, + supportEvents: false, + supportBonjour: false, + manfDescription: "Get Window state of a Variable", + designedMaxLength: 1 + }] + }]; + } +}; + +module.exports.accessory = HomeMaticWindow; diff --git a/config-sample.json b/config-sample.json index 8fc7774..05b14ca 100644 --- a/config-sample.json +++ b/config-sample.json @@ -153,6 +153,24 @@ "ccu_id": "The XMP-API id of your HomeMatic device", "ccu_ip": "The IP-Adress of your HomeMatic CCU device" }, + { + "accessory": "HomeMaticWindow", + "name": "Contact", + "description": "Control HomeMatic devices (The XMP-API addon for the CCU is required)", + "ccu_id": "The XMP-API id of your HomeMatic device (type HM-Sec-RHS)", + "ccu_ip": "The IP-Adress of your HomeMatic CCU device" + }, + { + "accessory": "HomeMaticThermo", + "name": "Contact", + "description": "Control HomeMatic devices (The XMP-API addon for the CCU is required)", + "ccu_id_TargetTemp": "The XMP-API id of your HomeMatic device (type HM-CC-RT-DN )", + "ccu_id_CurrentTemp": "The XMP-API id of your HomeMatic device (type HM-CC-RT-DN )", + "ccu_id_ControlMode": "The XMP-API id of your HomeMatic device (type HM-CC-RT-DN )", + "ccu_id_ManuMode": "The XMP-API id of your HomeMatic device (type HM-CC-RT-DN )", + "ccu_id_AutoMode": "The XMP-API id of your HomeMatic device (type HM-CC-RT-DN )", + "ccu_ip": "The IP-Adress of your HomeMatic CCU device" + }, { "accessory": "X10", "name": "Lamp", diff --git a/platforms/HomeSeer.js b/platforms/HomeSeer.js index e0c5c79..c4ccde7 100644 --- a/platforms/HomeSeer.js +++ b/platforms/HomeSeer.js @@ -17,6 +17,13 @@ // - Added Battery support // - Added low battery support for all sensors // - Added HomeSeer event support (using HomeKit switches...) +// V0.7 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/13 +// - You can add multiple HomeKit devices for the same HomeSeer device reference +// - Added CarbonMonoxide sensor +// - Added CarbonDioxide sensor +// - Added onValues option to all binary sensors +// V0.8 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/14 +// - Added uuid_base parameter to all accessories // // // Remember to add platform to config.json. @@ -35,7 +42,8 @@ // { // "eventGroup":"My Group", // Required - The HomeSeer event group // "eventName":"My Event", // Required - The HomeSeer event name -// "name":"Test" // Optional - HomeSeer event name is the default +// "name":"Test", // Optional - HomeSeer event name is the default +// "uuid_base":"SomeUniqueId" // Optional - HomeKit identifier will be derived from this parameter instead of the name // } // ], // @@ -46,7 +54,8 @@ // "name":"My Light", // Optional - HomeSeer device name is the default // "offValue":"0", // Optional - 0 is the default // "onValue":"100", // Optional - 100 is the default -// "can_dim":true // Optional - true is the default - false for a non dimmable lightbulb +// "can_dim":true, // Optional - true is the default - false for a non dimmable lightbulb +// "uuid_base":"SomeUniqueId2" // Optional - HomeKit identifier will be derived from this parameter instead of the name // }, // { // "ref":9 // This is a dimmable Lightbulb by default @@ -64,6 +73,22 @@ // "batteryThreshold":15 // Optional - If sensor battery level is below this value, the HomeKit LowBattery characteristic is set to 1. Default is 10 // }, // { +// "ref":34, // Required - HomeSeer Device Reference for your sensor +// "type":"SmokeSensor", // Required for a smoke sensor +// "name":"Kichen smoke detector", // Optional - HomeSeer device name is the default +// "batteryRef":35, // Optional - HomeSeer device reference for the sensor battery level +// "batteryThreshold":15, // Optional - If sensor battery level is below this value, the HomeKit LowBattery characteristic is set to 1. Default is 10 +// "onValues":[1,1.255] // Optional - List of all HomeSeer values triggering a "ON" sensor state - Default is any value different than 0 +// }, +// { +// "ref":34, // Required - HomeSeer Device Reference for your sensor (Here it's the same device as the SmokeSensor above) +// "type":"CarbonMonoxideSensor", // Required for a carbon monoxide sensor +// "name":"Kichen CO detector", // Optional - HomeSeer device name is the default +// "batteryRef":35, // Optional - HomeSeer device reference for the sensor battery level +// "batteryThreshold":15, // Optional - If sensor battery level is below this value, the HomeKit LowBattery characteristic is set to 1. Default is 10 +// "onValues":[2,2.255] // Optional - List of all HomeSeer values triggering a "ON" sensor state - Default is any value different than 0 +// }, +// { // "ref":113, // Required - HomeSeer Device Reference of the Current Temperature Device // "type":"Thermostat", // Required for a Thermostat // "name":"Température Salon", // Optional - HomeSeer device name is the default @@ -76,10 +101,10 @@ // "stateCoolValues":[2], // Required - List of the HomeSeer device values for a HomeKit state=COOL // "stateAutoValues":[3], // Required - List of the HomeSeer device values for a HomeKit state=AUTO // "controlRef":168, // Required - HomeSeer device reference for your thermostat mode control (It can be the same as stateRef for some thermostats) -// "controlOffValue":0, // Required - Value for OFF -// "controlHeatValue":1, // Required - Value for HEAT -// "controlCoolValue":2, // Required - Value for COOL -// "controlAutoValue":3, // Required - Value for AUTO +// "controlOffValue":0, // Required - HomeSeer device control value for OFF +// "controlHeatValue":1, // Required - HomeSeer device control value for HEAT +// "controlCoolValue":2, // Required - HomeSeer device control value for COOL +// "controlAutoValue":3, // Required - HomeSeer device control value for AUTO // "coolingThresholdRef":169, // Optional - Not-implemented-yet - HomeSeer device reference for your thermostat cooling threshold // "heatingThresholdRef":170 // Optional - Not-implemented-yet - HomeSeer device reference for your thermostat heating threshold // }, @@ -95,20 +120,22 @@ // // // SUPORTED TYPES: -// - Lightbulb (can_dim, onValue, offValue options) -// - Fan (onValue, offValue options) -// - Switch (onValue, offValue options) -// - Outlet (onValue, offValue options) -// - Thermostat (temperatureUnit, setPoint, state, control options) -// - TemperatureSensor (temperatureUnit=C|F) -// - ContactSensor (0=no contact, 1=contact - batteryRef, batteryThreshold option) -// - MotionSensor (0=no motion, 1=motion - batteryRef, batteryThreshold option) -// - LeakSensor (0=no leak, 1=leak - batteryRef, batteryThreshold option) -// - LightSensor (HomeSeer device value in Lux - batteryRef, batteryThreshold option) -// - HumiditySensor (HomeSeer device value in % - batteryRef, batteryThreshold option) -// - OccupancySensor (0=no occupancy, 1=occupancy - batteryRef, batteryThreshold option) -// - SmokeSensor (0=no smoke, 1=smoke - batteryRef, batteryThreshold option) -// - Battery (batteryThreshold option) +// - Lightbulb (can_dim, onValue, offValue options) +// - Fan (onValue, offValue options) +// - Switch (onValue, offValue options) +// - Outlet (onValue, offValue options) +// - Thermostat (temperatureUnit, setPoint, state, control options) +// - TemperatureSensor (temperatureUnit=C|F) +// - HumiditySensor (HomeSeer device value in % - batteryRef, batteryThreshold options) +// - LightSensor (HomeSeer device value in Lux - batteryRef, batteryThreshold options) +// - ContactSensor (onValues, batteryRef, batteryThreshold options) +// - MotionSensor (onValues, batteryRef, batteryThreshold options) +// - LeakSensor (onValues, batteryRef, batteryThreshold options) +// - OccupancySensor (onValues, batteryRef, batteryThreshold options) +// - SmokeSensor (onValues, batteryRef, batteryThreshold options) +// - CarbonMonoxideSensor (onValues, batteryRef, batteryThreshold options) +// - CarbonDioxideSensor (onValues, batteryRef, batteryThreshold options) +// - Battery (batteryThreshold option) // - Door @@ -163,9 +190,14 @@ HomeSeerPlatform.prototype = { else { this.log('HomeSeer status function succeeded!'); var response = JSON.parse( body ); - for( var i=0; i cx.maximumValue) return cx.maximumValue; + // Bounds checking now done upstream! + //if(lux < cx.minimumValue) return cx.minimumValue; if(lux > cx.maximumValue) return cx.maximumValue; return lux; } else { return vdev.metrics.level; @@ -768,6 +778,43 @@ ZWayServerAccessory.prototype = { }); return cx; } + + if(cx instanceof Characteristic.MotionDetected){ + cx.zway_getValueFromVDev = function(vdev){ + return vdev.metrics.level === "off" ? false : true; + }; + cx.value = cx.zway_getValueFromVDev(vdev); + cx.on('get', function(callback, context){ + debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"..."); + this.getVDev(vdev).then(function(result){ + debug("Got value: " + cx.zway_getValueFromVDev(result.data) + ", for " + vdev.metrics.title + "."); + callback(false, cx.zway_getValueFromVDev(result.data)); + }); + }.bind(this)); + cx.on('change', function(ev){ + debug("Device " + vdev.metrics.title + ", characteristic " + cx.displayName + " changed from " + ev.oldValue + " to " + ev.newValue); + }); + return cx; + } + + if(cx instanceof Characteristic.StatusTampered){ + cx.zway_getValueFromVDev = function(vdev){ + return vdev.metrics.level === "off" ? Characteristic.StatusTampered.NOT_TAMPERED : Characteristic.StatusTampered.TAMPERED; + }; + cx.value = cx.zway_getValueFromVDev(vdev); + cx.on('get', function(callback, context){ + debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"..."); + this.getVDev(vdev).then(function(result){ + debug("Got value: " + cx.zway_getValueFromVDev(result.data) + ", for " + vdev.metrics.title + "."); + callback(false, cx.zway_getValueFromVDev(result.data)); + }); + }.bind(this)); + cx.on('change', function(ev){ + debug("Device " + vdev.metrics.title + ", characteristic " + cx.displayName + " changed from " + ev.oldValue + " to " + ev.newValue); + }); + return cx; + } + } , configureService: function(service, vdev){