From 98c61bc72a8191f0fb850f282d03c9c1aa45f06c 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 1/4] 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 cbc34897f947037aa4247a0e42ad34dbd9d08ae0 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 2/4] 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 fd49b96d786a0491ab8596817d93d013c79681ad 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 3/4] 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 5f82fa6fb6d7f0e1d54959f4443c4fd430b66025 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 4/4] 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": [