From ead491fb4c4bf8f18822e0341837264e689146d5 Mon Sep 17 00:00:00 2001 From: Thomas Kluge Date: Fri, 30 Oct 2015 16:50:31 +0100 Subject: [PATCH 1/6] switch back from xmlrpc branch to master --- platforms/HomeMatic.js | 16 + platforms/HomematicChannel.js | 949 +++++++++++++++++----------------- 2 files changed, 498 insertions(+), 467 deletions(-) diff --git a/platforms/HomeMatic.js b/platforms/HomeMatic.js index a451451..219ebdf 100644 --- a/platforms/HomeMatic.js +++ b/platforms/HomeMatic.js @@ -2,9 +2,25 @@ // // Homematic Platform Shim for HomeBridge // +// to add the homematic platform add this to config.json. Example: +// "platforms": [ +// { +// "platform": "HomeMatic", +// "name": "HomeMatic", +// "filter_device":[], +// "filter_channel":["BidCos-RF.KEQXXXXXXX:4", "BidCos-RF.LEQXXXXXXX:2"], +// "outlets":[ "BidCos-RF.KEQXXXXXXX:4","BidCos-RF.IEQXXXXXXX:1"] +// +// } +// // V0.1 - 2015/10/29 // - initial version // - reintegrated Homematic Platform fork from https://github.com/thkl/homebridge/tree/xmlrpc +// 2015/10/30 thkl +// - added Rotary Sensors ; fixed thermostat + +// ], + var types = require("hap-nodejs/accessories/types.js"); diff --git a/platforms/HomematicChannel.js b/platforms/HomematicChannel.js index 5b6cff2..e2818a5 100644 --- a/platforms/HomematicChannel.js +++ b/platforms/HomematicChannel.js @@ -1,180 +1,185 @@ -"use strict"; 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 = []; + this.datapointMappings = []; } + HomeMaticGenericChannel.prototype = { - // Return current States - query: function(dp, callback) { - if (this.state[dp] !== undefined) { + addValueMapping: function(dp,value,mappedvalue) { + if (this.datapointMappings[dp]==undefined) { + this.datapointMappings[dp] = []; + } + this.datapointMappings[dp][value] = mappedvalue; + } , + + // 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) { - if (this.state[dp] !== undefined) { - return (this.state[dp]); + 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; - }); + 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"; - 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); + // Check custom Mapping from HM to HomeKit + var map = this.datapointMappings[dp]; + if (map != undefined) { + this.log("Mapping found for " + dp); + if (map[value]!=undefined) { + this.log("Mapping found for " + dp + " " + value); + value = map[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; - } - + delayed: function(mode, dp,value,delay) { + + if (this.eventupdate==true) { + return; + } + var timer = this.delayed[delay]; - if (timer) { - clearTimeout(timer); + if( timer ) { + clearTimeout( timer ); } - this.log(this.name + " delaying command " + mode + " " + dp + " with value " + value); + 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) { + command: function(mode,dp,value,callback) { + + if (this.eventupdate==true) { + return; + } + var that = this; - 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); - } + 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) { - - var cTypes = [{ + + cTypes = [{ cType: types.NAME_CTYPE, onUpdate: null, perms: ["pr"], @@ -184,174 +189,183 @@ HomeMaticGenericChannel.prototype = { supportBonjour: false, 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({ + + + if (this.type=="KEYMATIC") { + cTypes.push( + { cType: types.CURRENT_LOCK_MECHANISM_STATE_CTYPE, - + onRead: function(callback) { - that.query("STATE", callback); - }, - - onRegister: function(characteristic) { + that.query("STATE",callback); + }, + + onRegister: function(characteristic) { that.currentStateCharacteristic["STATE"] = characteristic; characteristic.eventEnabled = true; that.remoteGetValue("STATE"); - }, + }, - perms: ["pr", "ev"], + perms: ["pr","ev"], format: "bool", - initialValue: that.dpvalue("STATE", 0), + 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; + 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.addValueMapping("STATE","1",0); + that.addValueMapping("STATE","0",1); 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"); + + 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) { - that.query("LEVEL", callback); + callback(1); }, - - onRegister: function(characteristic) { - that.currentStateCharacteristic["LEVEL"] = characteristic; - characteristic.eventEnabled = true; - that.remoteGetValue("LEVEL"); + + onRegister: function(characteristic) { + that.currentStateCharacteristic["OPEN"] = characteristic; + characteristic.eventEnabled = true; }, - - perms: ["pw", "pr", "ev"], + + perms: ["pw","pr","ev"], format: "bool", - initialValue: (that.dpvalue("LEVEL") > 0, 0), + 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); + that.delayed("set","LEVEL" , String(value/100),100); }, - + 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: "int", - initialValue: that.dpvalue("LEVEL", 0), + initialValue: that.dpvalue("LEVEL",0), supportEvents: false, supportBonjour: false, manfDescription: "Adjust Brightness of Light", @@ -362,23 +376,26 @@ HomeMaticGenericChannel.prototype = { }); } - if (this.type == "BLIND") { - cTypes.push({ + + + if (this.type=="BLIND") { + cTypes.push( + { cType: types.WINDOW_COVERING_CURRENT_POSITION_CTYPE, - + onRead: function(callback) { - that.query("LEVEL", callback); - }, - - onRegister: function(characteristic) { + that.query("LEVEL",callback); + }, + + onRegister: function(characteristic) { that.currentStateCharacteristic["LEVEL"] = characteristic; characteristic.eventEnabled = true; that.remoteGetValue("LEVEL"); - }, + }, - perms: ["pr", "ev"], + perms: ["pr","ev"], format: "int", - initialValue: that.dpvalue("LEVEL", 0), + initialValue: that.dpvalue("LEVEL",0), supportEvents: false, supportBonjour: false, manfDescription: "Current Blind Position", @@ -387,271 +404,269 @@ HomeMaticGenericChannel.prototype = { 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, - - onUpdate: function(value) { - that.delayed("set", "LEVEL", String(value / 100), 100); - }, - - - onRead: function(callback) { - that.query("LEVEL", callback); - }, - - onRegister: function(characteristic) { + 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, - + + // Simple Contact (Magnet) + + 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" + }); + } + + // Rotary Handle + if (this.type=="ROTARY_HANDLE_SENSOR") { + cTypes.push( + { + cType: types.CONTACT_SENSOR_STATE_CTYPE, + onRead: function(callback) { - that.query("MOTION", callback); + that.query("STATE",callback); }, - - onRegister: function(characteristic) { - that.currentStateCharacteristic["MOTION"] = characteristic; - characteristic.eventEnabled = true; - that.remoteGetValue("MOTION"); + + onRegister: function(characteristic) { + that.addValueMapping("STATE","2",1); + that.currentStateCharacteristic["STATE"] = characteristic; + characteristic.eventEnabled = true; + that.remoteGetValue("STATE"); }, - - 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 + + perms: ["pr","ev"], + format: "bool", + initialValue: that.dpvalue("STATE",0), + supportEvents: false, + supportBonjour: false, + manfDescription: "Current State" + }); + } + + + // Motion Detector + + 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" + }); + } + + // Heating Device + + if ((this.type=="CLIMATECONTROL_RT_TRANSCEIVER") || (this.type=="THERMALCONTROL_TRANSMIT")) { + + 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); }, - - { - 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) { + + onRegister: function(characteristic) { that.currentStateCharacteristic["ACTUAL_TEMPERATURE"] = characteristic; characteristic.eventEnabled = true; that.remoteGetValue("ACTUAL_TEMPERATURE"); - }, - perms: ["pr", "ev"], - 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); + that.delayed("set", "MANU_MODE", 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; + ); + } + + + 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") { + + 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=="BLIND") { return types.WINDOW_COVERING_STYPE; - } + } - if (this.type == "CLIMATECONTROL_RT_TRANSCEIVER") { + if ((this.type=="CLIMATECONTROL_RT_TRANSCEIVER") || (this.type=="THERMALCONTROL_TRANSMIT")) { return types.THERMOSTAT_STYPE; - } - - if (this.type == "SHUTTER_CONTACT") { + } + + if ((this.type=="SHUTTER_CONTACT") ||(this.type=="ROTARY_HANDLE_SENSOR")) { return types.CONTACT_SENSOR_STYPE; - } - - if (this.type == "MOTION_DETECTOR") { - return types.MOTION_SENSOR_STYPE; - } - - - if (this.type == "KEYMATIC") { - return types.LOCK_MECHANISM_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() - }, { + 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; } }; -module.exports = HomeMaticGenericChannel; \ No newline at end of file +module.exports = HomeMaticGenericChannel; From ea1f75abb0da4c17e0a7ca27180cde5754a8a319 Mon Sep 17 00:00:00 2001 From: Thomas Kluge Date: Fri, 30 Oct 2015 18:13:31 +0100 Subject: [PATCH 2/6] setup.sh complete installation on a pi2 --- setup.sh | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 setup.sh diff --git a/setup.sh b/setup.sh new file mode 100644 index 0000000..8bb9164 --- /dev/null +++ b/setup.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env + +# Check if we can use colours in our output +use_colour=0 +[ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null && use_colour=1 + +# Some useful functions +progress() { + [ $use_colour -eq 1 ] && echo -ne "\033[01;32m" + echo "$@" >&2 + [ $use_colour -eq 1 ] && echo -ne "\033[00m" +} + +info() { + [ $use_colour -eq 1 ] && echo -ne "\033[01;34m" + echo "$@" >&2 + [ $use_colour -eq 1 ] && echo -ne "\033[00m" +} + +die () { + [ $use_colour -eq 1 ] && echo -ne "\033[01;31m" + echo "$@" >&2 + [ $use_colour -eq 1 ] && echo -ne "\033[00m" + exit 1 +} + +install_package() { + package=$1 + info "install ${package}" + sudo apt-get -y --force-yes install $package 2>&1 > /dev/null + return $? +} + +# check architecture +sudo test "`dpkg --print-architecture`" == "armhf" || die "This Repos is only for armhf." + +# set timezone and update system +info "Setting up locale and keyboard" +sudo dpkg-reconfigure locales + +TIMEZONE="Europe/Berlin" +echo $TIMEZONE | sudo tee /etc/timezone +sudo cp /usr/share/zoneinfo/${TIMEZONE} /etc/localtime +sudo dpkg-reconfigure -f noninteractive tzdata + +info "Setting up Hostname" +echo 'Homebridge' | sudo tee /etc/hostname + +info "Cleaning up" +sudo dpkg --configure -a + +info "Update Package Lists this may take some time (10-20 min) depending on your internet connection" +sudo apt-get update -y +sudo apt-get dist-upgrade -y +info "Done" + +info "Installing Zeroconf" + +install_package "libavahi-compat-libdnssd-dev" +install_package "gcc-4.8 g++-4.8" +install_package "libkrb5-dev" + +info "Installing node" +wget https://s3-eu-west-1.amazonaws.com/conoroneill.net/wp-content/uploads/2015/03/node-v0.12.1-linux-arm-pi.tar.gz +tar -zxvf node-v0.12.1-linux-arm-pi.tar.gz +cd node-v0.12.1-linux-arm-pi +sudo cp -R * /usr/local/ + + +info "Cloning Repository" +cd /home/pi +git clone -b master --single-branch https://github.com/thkl/homebridge.git +cd homebridge + +info "Installing Node Modules" +npm install + +info "Setup" + +hazconfig="$(cat /home/pi/homebridge/config.json| grep 'bridge' | wc -l)" +if [ "$hazconfig" = "0" ]; then + + CCUIP=$(whiptail --inputbox "Please enter your CCU IP" 20 60 "000.000.000.000" 3>&1 1>&2 2>&3) + if [ $? -eq 0 ]; then + echo "{\"bridge\": {\"name\": \"Homebridge\", \"username\": \"CC:22:3D:E3:CE:30\",\"port\": 51826,\"pin\": \"031-45-154\"}," >> /home/pi/homebridge/config.json; + echo "\"description\": \"This is an autogenerated config. only the homematic platform is enabled. see the sample for more\"," >> /home/pi/homebridge/config.json; + echo "\"platforms\": [" >> /home/pi/homebridge/config.json; + echo "{\"platform\": \"HomeMaticPlatform\",\"name\": \"HomeMatic CCU\",\"ccu_ip\": \"$CCUIP\"," >> /home/pi/homebridge/config.json; + echo "\"filter_device\":[],\"filter_channel\":[],\"outlets\":[]}" >> /home/pi/homebridge/config.json; + echo "],\"accessories\": []}" >> /home/pi/homebridge/config.json; + fi +fi + +whiptail --yesno "Would you like to start homebridge at boot by default?" $DEFAULT 20 60 2 +RET=$? +if [ $RET -eq 0 ]; then + sudo cp /home/pi/homebridge/homebridge.txt /etc/init.d/homebridge + sudo chmod 755 /etc/init.d/homebridge + sudo update-rc.d homebridge defaults +fi + +info "Done. If there are no error messages you are done." +info "Your config is ready to use" +info "to start the homebridge goto /home/pi/homebridge and call npm run start." From 727809e9b4ff188cf8325a0ae18990fe2234f676 Mon Sep 17 00:00:00 2001 From: Thomas Kluge Date: Fri, 30 Oct 2015 18:30:07 +0100 Subject: [PATCH 3/6] removed some logs changed to autodetection if hm channel is supported yet --- platforms/HomeMatic.js | 9 +++++---- platforms/HomematicChannel.js | 4 +--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/platforms/HomeMatic.js b/platforms/HomeMatic.js index 219ebdf..30244e5 100644 --- a/platforms/HomeMatic.js +++ b/platforms/HomeMatic.js @@ -308,14 +308,15 @@ HomeMaticPlatform.prototype = { // 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; var accessory = new HomeMaticGenericChannel(that.log, that, ch.id, ch.name, ch.type, ch.address, special); - that.foundAccessories.push(accessory); - } - + if (accessory.sType()!=undefined) { + // support exists for this channel + that.foundAccessories.push(accessory); + } } else { that.log(device.name + " has no address"); diff --git a/platforms/HomematicChannel.js b/platforms/HomematicChannel.js index e2818a5..2322fed 100644 --- a/platforms/HomematicChannel.js +++ b/platforms/HomematicChannel.js @@ -78,9 +78,7 @@ HomeMaticGenericChannel.prototype = { // Check custom Mapping from HM to HomeKit var map = this.datapointMappings[dp]; if (map != undefined) { - this.log("Mapping found for " + dp); if (map[value]!=undefined) { - this.log("Mapping found for " + dp + " " + value); value = map[value]; } } @@ -636,7 +634,7 @@ HomeMaticGenericChannel.prototype = { return types.THERMOSTAT_STYPE; } - if ((this.type=="SHUTTER_CONTACT") ||(this.type=="ROTARY_HANDLE_SENSOR")) { + if ((this.type=="SHUTTER_CONTACT") || (this.type=="ROTARY_HANDLE_SENSOR")) { return types.CONTACT_SENSOR_STYPE; } From 04f48ecbaba6d40f0c76ae899f5bf45dc683bd26 Mon Sep 17 00:00:00 2001 From: Thomas Kluge Date: Sat, 31 Oct 2015 12:02:06 +0100 Subject: [PATCH 4/6] fixed thermostats --- platforms/HomeMatic.js | 6 ++++++ platforms/HomematicChannel.js | 22 ++++++++++++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/platforms/HomeMatic.js b/platforms/HomeMatic.js index 30244e5..f140f3d 100644 --- a/platforms/HomeMatic.js +++ b/platforms/HomeMatic.js @@ -360,6 +360,12 @@ HomeMaticPlatform.prototype = { }, + setRegaValue: function(channel, datapoint, value) { + 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)  { diff --git a/platforms/HomematicChannel.js b/platforms/HomematicChannel.js index 2322fed..786b556 100644 --- a/platforms/HomematicChannel.js +++ b/platforms/HomematicChannel.js @@ -32,11 +32,11 @@ HomeMaticGenericChannel.prototype = { var that = this; if (this.state[dp] != undefined) { - callback(this.state[dp]); + if (callback!=undefined){callback(this.state[dp]);} } else { // that.log("No cached Value found start fetching and send temp 0 back"); this.remoteGetValue(dp); - callback(0); + if (callback!=undefined){callback(0);} } }, @@ -107,16 +107,22 @@ HomeMaticGenericChannel.prototype = { }, 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); + this.log("Send " + value + " to Datapoint " + dp + " at " + that.adress); that.platform.setValue(that.adress,dp,value); } + + if (mode == "setrega") { + this.log("Send " + value + " to Datapoint " + dp + " at " + that.adress); + that.platform.setRegaValue(that.adress,dp,value); + } + }, informationCharacteristics: function() { @@ -580,11 +586,15 @@ HomeMaticGenericChannel.prototype = { { cType: types.TARGET_TEMPERATURE_CTYPE, onUpdate: function(value) { - //that.delayed("set", "SET_TEMPERATURE", value,500); - that.delayed("set", "MANU_MODE", value,500); + if (that.state["CONTROL_MODE"]!=1) { + that.delayed("setrega", "MANU_MODE",value,500); + } else { + that.delayed("set", "SET_TEMPERATURE", value,500); + } }, onRead: function(callback) { that.query("SET_TEMPERATURE",callback); + that.query("CONTROL_MODE",undefined); }, onRegister: function(characteristic) { From 5c921570097d44f251126b4683fa945c1312e133 Mon Sep 17 00:00:00 2001 From: Thomas Kluge Date: Sat, 31 Oct 2015 16:28:51 +0100 Subject: [PATCH 5/6] removed setup --- setup.sh | 104 ------------------------------------------------------- 1 file changed, 104 deletions(-) delete mode 100644 setup.sh diff --git a/setup.sh b/setup.sh deleted file mode 100644 index 8bb9164..0000000 --- a/setup.sh +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/env - -# Check if we can use colours in our output -use_colour=0 -[ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null && use_colour=1 - -# Some useful functions -progress() { - [ $use_colour -eq 1 ] && echo -ne "\033[01;32m" - echo "$@" >&2 - [ $use_colour -eq 1 ] && echo -ne "\033[00m" -} - -info() { - [ $use_colour -eq 1 ] && echo -ne "\033[01;34m" - echo "$@" >&2 - [ $use_colour -eq 1 ] && echo -ne "\033[00m" -} - -die () { - [ $use_colour -eq 1 ] && echo -ne "\033[01;31m" - echo "$@" >&2 - [ $use_colour -eq 1 ] && echo -ne "\033[00m" - exit 1 -} - -install_package() { - package=$1 - info "install ${package}" - sudo apt-get -y --force-yes install $package 2>&1 > /dev/null - return $? -} - -# check architecture -sudo test "`dpkg --print-architecture`" == "armhf" || die "This Repos is only for armhf." - -# set timezone and update system -info "Setting up locale and keyboard" -sudo dpkg-reconfigure locales - -TIMEZONE="Europe/Berlin" -echo $TIMEZONE | sudo tee /etc/timezone -sudo cp /usr/share/zoneinfo/${TIMEZONE} /etc/localtime -sudo dpkg-reconfigure -f noninteractive tzdata - -info "Setting up Hostname" -echo 'Homebridge' | sudo tee /etc/hostname - -info "Cleaning up" -sudo dpkg --configure -a - -info "Update Package Lists this may take some time (10-20 min) depending on your internet connection" -sudo apt-get update -y -sudo apt-get dist-upgrade -y -info "Done" - -info "Installing Zeroconf" - -install_package "libavahi-compat-libdnssd-dev" -install_package "gcc-4.8 g++-4.8" -install_package "libkrb5-dev" - -info "Installing node" -wget https://s3-eu-west-1.amazonaws.com/conoroneill.net/wp-content/uploads/2015/03/node-v0.12.1-linux-arm-pi.tar.gz -tar -zxvf node-v0.12.1-linux-arm-pi.tar.gz -cd node-v0.12.1-linux-arm-pi -sudo cp -R * /usr/local/ - - -info "Cloning Repository" -cd /home/pi -git clone -b master --single-branch https://github.com/thkl/homebridge.git -cd homebridge - -info "Installing Node Modules" -npm install - -info "Setup" - -hazconfig="$(cat /home/pi/homebridge/config.json| grep 'bridge' | wc -l)" -if [ "$hazconfig" = "0" ]; then - - CCUIP=$(whiptail --inputbox "Please enter your CCU IP" 20 60 "000.000.000.000" 3>&1 1>&2 2>&3) - if [ $? -eq 0 ]; then - echo "{\"bridge\": {\"name\": \"Homebridge\", \"username\": \"CC:22:3D:E3:CE:30\",\"port\": 51826,\"pin\": \"031-45-154\"}," >> /home/pi/homebridge/config.json; - echo "\"description\": \"This is an autogenerated config. only the homematic platform is enabled. see the sample for more\"," >> /home/pi/homebridge/config.json; - echo "\"platforms\": [" >> /home/pi/homebridge/config.json; - echo "{\"platform\": \"HomeMaticPlatform\",\"name\": \"HomeMatic CCU\",\"ccu_ip\": \"$CCUIP\"," >> /home/pi/homebridge/config.json; - echo "\"filter_device\":[],\"filter_channel\":[],\"outlets\":[]}" >> /home/pi/homebridge/config.json; - echo "],\"accessories\": []}" >> /home/pi/homebridge/config.json; - fi -fi - -whiptail --yesno "Would you like to start homebridge at boot by default?" $DEFAULT 20 60 2 -RET=$? -if [ $RET -eq 0 ]; then - sudo cp /home/pi/homebridge/homebridge.txt /etc/init.d/homebridge - sudo chmod 755 /etc/init.d/homebridge - sudo update-rc.d homebridge defaults -fi - -info "Done. If there are no error messages you are done." -info "Your config is ready to use" -info "to start the homebridge goto /home/pi/homebridge and call npm run start." From c1cc8be8fabc13baa398c515f31f35b3d0001b9d Mon Sep 17 00:00:00 2001 From: Thomas Kluge Date: Sun, 1 Nov 2015 12:57:29 +0100 Subject: [PATCH 6/6] new Devices : Smokedetector --- platforms/HomeMatic.js | 2 +- platforms/HomematicChannel.js | 32 +++++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/platforms/HomeMatic.js b/platforms/HomeMatic.js index f140f3d..6f3a6aa 100644 --- a/platforms/HomeMatic.js +++ b/platforms/HomeMatic.js @@ -330,7 +330,7 @@ HomeMaticPlatform.prototype = { }); /* - accessory = new HomeMaticGenericChannel(that.log, that, "1234" , "DummyKM" , "KEYMATIC" , "1234"); + var accessory = new HomeMaticGenericChannel(that.log, that, "1234" , "DummyKM" , "SMOKE_DETECTOR" , "1234"); that.foundAccessories.push(accessory); accessory = new HomeMaticGenericChannel(that.log, that, "5678" , "DummyBLIND" , "BLIND" , "5678"); diff --git a/platforms/HomematicChannel.js b/platforms/HomematicChannel.js index 786b556..f15eb58 100644 --- a/platforms/HomematicChannel.js +++ b/platforms/HomematicChannel.js @@ -544,6 +544,31 @@ HomeMaticGenericChannel.prototype = { }); } + // Smoke Detector + if (this.type=="SMOKE_DETECTOR") { + cTypes.push( + { + cType: "00000076-0000-1000-8000-0026BB765291", + + 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: "Smoke detected" + }); + } + // Heating Device if ((this.type=="CLIMATECONTROL_RT_TRANSCEIVER") || (this.type=="THERMALCONTROL_TRANSMIT")) { @@ -588,8 +613,9 @@ HomeMaticGenericChannel.prototype = { onUpdate: function(value) { if (that.state["CONTROL_MODE"]!=1) { that.delayed("setrega", "MANU_MODE",value,500); + that.state["CONTROL_MODE"]=1; // set to Manual Mode } else { - that.delayed("set", "SET_TEMPERATURE", value,500); + that.delayed("setrega", "SET_TEMPERATURE", value,500); } }, onRead: function(callback) { @@ -601,6 +627,7 @@ HomeMaticGenericChannel.prototype = { that.currentStateCharacteristic["SET_TEMPERATURE"] = characteristic; characteristic.eventEnabled = true; that.remoteGetValue("SET_TEMPERATURE"); + that.remoteGetValue("CONTROL_MODE"); }, perms: ["pw","pr","ev"],format: "double", initialValue: that.dpvalue("SET_TEMPERATURE",16), @@ -657,6 +684,9 @@ HomeMaticGenericChannel.prototype = { return types.LOCK_MECHANISM_STYPE } + if (this.type=="SMOKE_DETECTOR") { + return "00000087-0000-1000-8000-0026BB765291"; + } },