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