Merge branch 'nfarina/master'

This commit is contained in:
S'pht'Kr
2015-10-07 05:38:46 +02:00
8 changed files with 455 additions and 325 deletions

View File

@@ -11,6 +11,17 @@ function LockitronAccessory(log, config) {
this.name = config["name"]; this.name = config["name"];
this.accessToken = config["api_token"]; this.accessToken = config["api_token"];
this.lockID = config["lock_id"]; this.lockID = config["lock_id"];
this.service = new Service.LockMechanism(this.name);
this.service
.getCharacteristic(Characteristic.LockCurrentState)
.on('get', this.getState.bind(this));
this.service
.getCharacteristic(Characteristic.LockTargetState)
.on('get', this.getState.bind(this))
.on('set', this.setState.bind(this));
} }
LockitronAccessory.prototype.getState = function(callback) { LockitronAccessory.prototype.getState = function(callback) {
@@ -36,7 +47,7 @@ LockitronAccessory.prototype.getState = function(callback) {
} }
LockitronAccessory.prototype.setState = function(state, callback) { LockitronAccessory.prototype.setState = function(state, callback) {
var lockitronState = (state == 1) ? "lock" : "unlock"; var lockitronState = (state == Characteristic.LockTargetState.SECURED) ? "lock" : "unlock";
this.log("Set state to %s", lockitronState); this.log("Set state to %s", lockitronState);
@@ -47,6 +58,14 @@ LockitronAccessory.prototype.setState = function(state, callback) {
if (!err && response.statusCode == 200) { if (!err && response.statusCode == 200) {
this.log("State change complete."); this.log("State change complete.");
// we succeeded, so update the "current" state as well
var currentState = (state == Characteristic.LockTargetState.SECURED) ?
Characteristic.LockCurrentState.SECURED : Characteristic.LockCurrentState.UNSECURED;
this.service
.setCharacteristic(Characteristic.LockCurrentState, currentState);
callback(null); // success callback(null); // success
} }
else { else {
@@ -57,17 +76,5 @@ LockitronAccessory.prototype.setState = function(state, callback) {
}, },
LockitronAccessory.prototype.getServices = function() { LockitronAccessory.prototype.getServices = function() {
return [this.service];
var service = new Service.LockMechanism(this.name);
service
.getCharacteristic(Characteristic.LockCurrentState)
.on('get', this.getState.bind(this));
service
.getCharacteristic(Characteristic.LockTargetState)
.on('get', this.getState.bind(this))
.on('set', this.setState.bind(this));
return [service];
} }

View File

@@ -144,6 +144,16 @@ WeMoAccessory.prototype.getServices = function() {
return [garageDoorService]; return [garageDoorService];
} }
else if (this.service == "Light") {
var lightbulbService = new Service.Lightbulb(this.name);
lightbulbService
.getCharacteristic(Characteristic.On)
.on('get', this.getPowerOn.bind(this))
.on('set', this.setPowerOn.bind(this));
return [lightbulbService];
}
else if (this.service == "MotionSensor") { else if (this.service == "MotionSensor") {
var motionSensorService = new Service.MotionSensor(this.name); var motionSensorService = new Service.MotionSensor(this.name);

View File

@@ -14,6 +14,9 @@ New 2015-09-18:
New 2015-09-19: New 2015-09-19:
- GarageDoorOpener Service - GarageDoorOpener Service
- MotionSensor Service - MotionSensor Service
New 2015-10-02:
- Check for valid group addresses
- new "R" flag allowed for Boolean addresses: 1/2/3R is the boolean not(1/2/3), i.e. 0 and 1 switched on read and write
* *
*/ */
var Service = require("HAP-NodeJS").Service; var Service = require("HAP-NodeJS").Service;
@@ -24,6 +27,8 @@ var knxd_startMonitor = require('../platforms/KNX.js').startMonitor;
var milliTimeout = 300; // used to block responses while swiping var milliTimeout = 300; // used to block responses while swiping
var colorOn = "\x1b[30;47m";
var colorOff = "\x1b[0m";
function KNXDevice(log, config) { function KNXDevice(log, config) {
this.log = log; this.log = log;
@@ -115,6 +120,7 @@ KNXDevice.prototype = {
if (!groupAddress) { if (!groupAddress) {
return null; return null;
} }
this.log("[knxdevice:knxread] preparing knx request for "+groupAddress);
var knxdConnection = new knxd.Connection(); var knxdConnection = new knxd.Connection();
// this.log("DEBUG in knxread: created empty connection, trying to connect socket to "+this.knxd_ip+":"+this.knxd_port); // this.log("DEBUG in knxread: created empty connection, trying to connect socket to "+this.knxd_ip+":"+this.knxd_port);
knxdConnection.socketRemote({ host: this.knxd_ip, port: this.knxd_port }, function() { knxdConnection.socketRemote({ host: this.knxd_ip, port: this.knxd_port }, function() {
@@ -130,7 +136,7 @@ KNXDevice.prototype = {
if (err) { if (err) {
this.log("[ERROR] knxread:sendAPDU: " + err); this.log("[ERROR] knxread:sendAPDU: " + err);
} else { } else {
this.log("knx request sent for "+groupAddress); this.log("[knxdevice:knxread] knx request sent for "+groupAddress);
} }
}.bind(this)); }.bind(this));
} }
@@ -143,12 +149,12 @@ KNXDevice.prototype = {
// handle multiple addresses // handle multiple addresses
for (var i = 0; i < groupAddresses.length; i++) { for (var i = 0; i < groupAddresses.length; i++) {
if (groupAddresses[i]) { // do not bind empty addresses if (groupAddresses[i]) { // do not bind empty addresses
this.knxread (groupAddresses[i]); this.knxread (groupAddresses[i].match(/(\d*\/\d*\/\d*)/)[0]); // clean address
} }
} }
} else { } else {
// it's only one // it's only one
this.knxread (groupAddresses); this.knxread (groupAddresses.match(/(\d*\/\d*\/\d*)/)[0]); // regex for cleaning address
} }
}, },
@@ -158,70 +164,80 @@ KNXDevice.prototype = {
// boolean: get 0 or 1 from the bus, write boolean // boolean: get 0 or 1 from the bus, write boolean
knxregister_bool: function(addresses, characteristic) { knxregister_bool: function(addresses, characteristic) {
this.log("knx registering BOOLEAN " + addresses); this.log("knx registering BOOLEAN " + addresses);
knxd_registerGA(addresses, function(val, src, dest, type){ knxd_registerGA(addresses, function(val, src, dest, type, reverse){
this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type + " for " + characteristic.displayName); this.log("[" +this.name + "]: Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type + " for " + characteristic.displayName);
// iterate(characteristic); // iterate(characteristic);
characteristic.setValue(val ? 1 : 0, undefined, 'fromKNXBus');
}.bind(this)); characteristic.setValue(val ? (reverse ? 0:1) : (reverse ? 1:0), undefined, 'fromKNXBus');
},
knxregister_boolReverse: function(addresses, characteristic) {
this.log("knx registering BOOLEAN " + addresses);
knxd_registerGA(addresses, function(val, src, dest, type){
this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type + " for " + characteristic.displayName);
// iterate(characteristic);
characteristic.setValue(val ? 0 : 1, undefined, 'fromKNXBus');
}.bind(this)); }.bind(this));
}, },
// knxregister_boolReverse: function(addresses, characteristic) {
// this.log("knx registering BOOLEAN REVERSE " + addresses);
// knxd_registerGA(addresses, function(val, src, dest, type, reverse){
// this.log("[" +this.name + "]: Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type + " for " + characteristic.displayName);
//// iterate(characteristic);
// characteristic.setValue(val ? 0 : 1, undefined, 'fromKNXBus');
// }.bind(this));
// },
// percentage: get 0..255 from the bus, write 0..100 to characteristic // percentage: get 0..255 from the bus, write 0..100 to characteristic
knxregister_percent: function(addresses, characteristic) { knxregister_percent: function(addresses, characteristic) {
this.log("knx registering PERCENT " + addresses); this.log("knx registering PERCENT " + addresses);
knxd_registerGA(addresses, function(val, src, dest, type){ knxd_registerGA(addresses, function(val, src, dest, type, reverse){
this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName); this.log("[" +this.name + "]: Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName);
if (type !== "DPT5") { if (type !== "DPT5") {
this.log("[ERROR] Received value cannot be a percentage value"); this.log("[ERROR] Received value cannot be a percentage value");
} else { } else {
if (!characteristic.timeout) { characteristic.setValue(Math.round(( reverse ? (255-val):val)/255*100), undefined, 'fromKNXBus');
if (characteristic.timeout < Date.now()) {
characteristic.setValue(Math.round(val/255*100), undefined, 'fromKNXBus');
} else {
this.log("Blackout time");
}
} else {
characteristic.setValue(Math.round(val/255*100), undefined, 'fromKNXBus');
} // todo get the boolean logic right into one OR expresssion
} }
}.bind(this)); }.bind(this));
}, },
// float // float
knxregister_float: function(addresses, characteristic) { knxregister_float: function(addresses, characteristic) {
this.log("knx registering FLOAT " + addresses); // update for props refactor https://github.com/KhaosT/HAP-NodeJS/commit/1d84d128d1513beedcafc24d2c07d98185563243#diff-cb84de3a1478a38b2cf8388d709f1c1cR50
knxd_registerGA(addresses, function(val, src, dest, type){
this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName); var validValue = true;
var hk_value = Math.round(val*10)/10; var hk_value = 0.0;
if (hk_value>=characteristic.minimumValue && hk_value<=characteristic.maximumValue) { this.log("["+ this.name +"]:[" + characteristic.displayName+ "]:knx registering FLOAT " + addresses);
knxd_registerGA(addresses, function(val, src, dest, type, reverse){
this.log("["+ this.name +"]:[" + characteristic.displayName+ "]: Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName);
// make hk_value compliant to properties
if (characteristic.props.minStep) {
// quantize
hk_value = Math.round(val/characteristic.props.minStep)/(1/characteristic.props.minStep);
} else {
hk_value = val;
}
// range check
validValue = true; // assume validity at beginning
if (characteristic.props.minValue) {
validValue = validValue && (hk_value>=characteristic.props.minValue);
}
if (characteristic.props.maxValue) {
validValue = validValue && (hk_value<=characteristic.props.maxValue);
}
if (validValue) {
characteristic.setValue(hk_value, undefined, 'fromKNXBus'); // 1 decimal for HomeKit characteristic.setValue(hk_value, undefined, 'fromKNXBus'); // 1 decimal for HomeKit
} else { } else {
this.log("Value %s out of bounds %s...%s ",hk_value, characteristic.minimumValue, characteristic.maximumValue); this.log("["+ this.name +"]:[" + characteristic.displayName+ "]: Value %s out of bounds %s...%s ",hk_value, characteristic.props.minValue, characteristic.props.maxValue);
} }
}.bind(this)); }.bind(this));
}, },
//integer //integer
knxregister_int: function(addresses, characteristic) { knxregister_int: function(addresses, characteristic) {
this.log("knx registering FLOAT " + addresses); this.log("["+ this.name +"]:[" + characteristic.displayName+ "]:knx registering INT " + addresses);
knxd_registerGA(addresses, function(val, src, dest, type){ knxd_registerGA(addresses, function(val, src, dest, type, reverse){
this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName); this.log("["+ this.name +"]:[" + characteristic.displayName+ "]: Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName);
if (val>=(characteristic.minimumValue || 0) && val<=(characteristic.maximumValue || 255)) { if (val>=(characteristic.props.minValue || 0) && val<=(characteristic.props.maxValue || 255)) {
characteristic.setValue(val, undefined, 'fromKNXBus'); characteristic.setValue(reverse ? (255-val):val, undefined, 'fromKNXBus');
} else { } else {
this.log("Value %s out of bounds %s...%s ",hk_value, (characteristic.minimumValue || 0), (characteristic.maximumValue || 255)); this.log("["+ this.name +"]:[" + characteristic.displayName+ "]: Value %s out of bounds %s...%s ",hk_value, (characteristic.props.minValue || 0), (characteristic.props.maxValue || 255));
} }
}.bind(this)); }.bind(this));
}, },
knxregister_HVAC: function(addresses, characteristic) { knxregister_HVAC: function(addresses, characteristic) {
this.log("knx registering HVAC " + addresses); this.log("["+ this.name +"]:[" + characteristic.displayName+ "]:knx registering HVAC " + addresses);
knxd_registerGA(addresses, function(val, src, dest, type){ knxd_registerGA(addresses, function(val, src, dest, type){
this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName); this.log("["+ this.name +"]:[" + characteristic.displayName+ "]:Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName);
var HAPvalue = 0; var HAPvalue = 0;
switch (val){ switch (val){
case 0: case 0:
@@ -261,9 +277,9 @@ KNXDevice.prototype = {
*/ */
// undefined, has to match! // undefined, has to match!
knxregister: function(addresses, characteristic) { knxregister: function(addresses, characteristic) {
this.log("knx registering " + addresses); this.log("["+ this.name +"]:[" + characteristic.displayName+ "]:knx registering " + addresses);
knxd_registerGA(addresses, function(val, src, dest, type){ knxd_registerGA(addresses, function(val, src, dest, type){
this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName); this.log("["+ this.name +"]:[" + characteristic.displayName+ "]:Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName);
characteristic.setValue(val, undefined, 'fromKNXBus'); characteristic.setValue(val, undefined, 'fromKNXBus');
}.bind(this)); }.bind(this));
}, },
@@ -276,71 +292,74 @@ KNXDevice.prototype = {
* }.bind(this)); * }.bind(this));
* *
*/ */
setBooleanState: function(value, callback, context, gaddress) { setBooleanState: function(value, callback, context, gaddress, reverseflag) {
if (context === 'fromKNXBus') { if (context === 'fromKNXBus') {
this.log(gaddress + " event ping pong, exit!"); // this.log(gaddress + " event ping pong, exit!");
if (callback) { if (callback) {
callback(); callback();
} }
} else { } else {
var numericValue = 0; var numericValue = reverseflag ? 1:0;
if (value) { if (value) {
numericValue = 1; // need 0 or 1, not true or something numericValue = reverseflag ? 0:1; // need 0 or 1, not true or something
} }
this.log("Setting "+gaddress+" Boolean to %s", numericValue); this.log("["+ this.name +"]:Setting "+gaddress+" " + reverseflag ? " (reverse)":""+ " Boolean to %s", numericValue);
this.knxwrite(callback, gaddress,'DPT1',numericValue); this.knxwrite(callback, gaddress,'DPT1',numericValue);
} }
}, },
setBooleanReverseState: function(value, callback, context, gaddress) { // setBooleanReverseState: function(value, callback, context, gaddress) {
// if (context === 'fromKNXBus') {
//// this.log(gaddress + " event ping pong, exit!");
// if (callback) {
// callback();
// }
// } else {
// var numericValue = 0;
// if (!value) {
// numericValue = 1; // need 0 or 1, not true or something
// }
// this.log("["+ this.name +"]:Setting "+gaddress+" Boolean to %s", numericValue);
// this.knxwrite(callback, gaddress,'DPT1',numericValue);
// }
//
// },
setPercentage: function(value, callback, context, gaddress, reverseflag) {
if (context === 'fromKNXBus') { if (context === 'fromKNXBus') {
this.log(gaddress + " event ping pong, exit!"); // this.log(gaddress + "event ping pong, exit!");
if (callback) {
callback();
}
} else {
var numericValue = 0;
if (!value) {
numericValue = 1; // need 0 or 1, not true or something
}
this.log("Setting "+gaddress+" Boolean to %s", numericValue);
this.knxwrite(callback, gaddress,'DPT1',numericValue);
}
},
setPercentage: function(value, callback, context, gaddress) {
if (context === 'fromKNXBus') {
this.log("event ping pong, exit!");
if (callback) { if (callback) {
callback(); callback();
} }
} else { } else {
var numericValue = 0; var numericValue = 0;
if (value) { value = ( value>=0 ? (value<=100 ? value:100):0 ); //ensure range 0..100
numericValue = Math.round(255*value/100); // convert 1..100 to 1..255 for KNX bus if (reverseflag) {
numericValue = 255 - Math.round(255*value/100); // convert 0..100 to 255..0 for KNX bus
} else {
numericValue = Math.round(255*value/100); // convert 0..100 to 0..255 for KNX bus
} }
this.log("Setting "+gaddress+" percentage to %s (%s)", value, numericValue); this.log("["+ this.name +"]:Setting "+gaddress+" percentage to %s (%s)", value, numericValue);
this.knxwrite(callback, gaddress,'DPT5',numericValue); this.knxwrite(callback, gaddress,'DPT5',numericValue);
} }
}, },
setInt: function(value, callback, context, gaddress) { setInt: function(value, callback, context, gaddress) {
if (context === 'fromKNXBus') { if (context === 'fromKNXBus') {
this.log("event ping pong, exit!"); // this.log(gaddress + "event ping pong, exit!");
if (callback) { if (callback) {
callback(); callback();
} }
} else { } else {
var numericValue = 0; var numericValue = 0;
if (value && value>=0 && value<=255) { if (value && value>=0 && value<=255) {
numericValue = value; // assure 1..255 for KNX bus numericValue = value; // assure 0..255 for KNX bus
} }
this.log("Setting "+gaddress+" int to %s (%s)", value, numericValue); this.log("["+ this.name +"]:Setting "+gaddress+" int to %s (%s)", value, numericValue);
this.knxwrite(callback, gaddress,'DPT5',numericValue); this.knxwrite(callback, gaddress,'DPT5',numericValue);
} }
}, },
setFloat: function(value, callback, context, gaddress) { setFloat: function(value, callback, context, gaddress) {
if (context === 'fromKNXBus') { if (context === 'fromKNXBus') {
this.log(gaddress + " event ping pong, exit!"); // this.log(gaddress + " event ping pong, exit!");
if (callback) { if (callback) {
callback(); callback();
} }
@@ -349,13 +368,13 @@ KNXDevice.prototype = {
if (value) { if (value) {
numericValue = value; // homekit expects precision of 1 decimal numericValue = value; // homekit expects precision of 1 decimal
} }
this.log("Setting "+gaddress+" Float to %s", numericValue); this.log("["+ this.name +"]:Setting "+gaddress+" Float to %s", numericValue);
this.knxwrite(callback, gaddress,'DPT9',numericValue); this.knxwrite(callback, gaddress,'DPT9',numericValue);
} }
}, },
setHVACState: function(value, callback, context, gaddress) { setHVACState: function(value, callback, context, gaddress) {
if (context === 'fromKNXBus') { if (context === 'fromKNXBus') {
this.log(gaddress + " event ping pong, exit!"); // this.log(gaddress + " event ping pong, exit!");
if (callback) { if (callback) {
callback(); callback();
} }
@@ -378,7 +397,7 @@ KNXDevice.prototype = {
KNXvalue = 1; KNXvalue = 1;
} }
this.log("Setting "+gaddress+" HVAC to %s", KNXvalue); this.log("["+ this.name +"]:Setting "+gaddress+" HVAC to %s", KNXvalue);
this.knxwrite(callback, gaddress,'DPT5',KNXvalue); this.knxwrite(callback, gaddress,'DPT5',KNXvalue);
} }
@@ -387,33 +406,50 @@ KNXDevice.prototype = {
* *
*/ */
identify: function(callback) { identify: function(callback) {
this.log("Identify requested!"); this.log("["+ this.name +"]:Identify requested!");
callback(); // success callback(); // success
}, },
/** bindCharacteristic /** bindCharacteristic
* initializes callbacks for 'set' events (from HK) and for KNX bus reads (to HK) * initializes callbacks for 'set' events (from HK) and for KNX bus reads (to HK)
*/ */
bindCharacteristic: function(myService, characteristicType, valueType, config) { bindCharacteristic: function(myService, characteristicType, valueType, config, defaultValue) {
var myCharacteristic = myService.getCharacteristic(characteristicType); var myCharacteristic = myService.getCharacteristic(characteristicType);
var setGA = "";
var setReverse = false;
if (myCharacteristic === undefined) { if (myCharacteristic === undefined) {
throw new Error("unknown characteristics cannot be bound"); throw new Error("unknown characteristics cannot be bound");
} }
if (defaultValue) {
myCharacteristic.setValue(defaultValue);
}
if (config.Set) { if (config.Set) {
// can write // can write
// extract address and Reverse flag
setGA = config.Set.match(/\d*\/\d*\/\d*/);
if (setGA===null) {
this.log(colorOn + "["+ this.name +"]:["+myCharacteristic.displayName+"] Error in group adress: ["+ config.Set +"] "+colorOff);
throw new Error("EINVGROUPADRESS - Invalid group address given");
} else {
setGA=setGA[0]; // first element of returned array is the group address
}
setReverse = config.Set.match(/\d*\/\d*\/\d*(R)/) ? true:false;
switch (valueType) { switch (valueType) {
case "Bool": case "Bool":
myCharacteristic.on('set', function(value, callback, context) { myCharacteristic.on('set', function(value, callback, context) {
this.setBooleanState(value, callback, context, config.Set); this.setBooleanState(value, callback, context, setGA, setReverse); //NEW
}.bind(this));
break;
case "BoolReverse":
myCharacteristic.on('set', function(value, callback, context) {
this.setBooleanReverseState(value, callback, context, config.Set);
}.bind(this)); }.bind(this));
break; break;
// case "BoolReverse":
// this.log("["+ this.name +"]:["+myCharacteristic.displayName+"] \x1b[30;47m%s\x1b[0mWARNING in group adress: "+ config.Set +": Legacy BoolReverse used. Use " + config.Set +"R instead");
// myCharacteristic.on('set', function(value, callback, context) {
// this.setBooleanReverseState(value, callback, context, config.Set);
// }.bind(this));
// break;
case "Percent": case "Percent":
myCharacteristic.on('set', function(value, callback, context) { myCharacteristic.on('set', function(value, callback, context) {
this.setPercentage(value, callback, context, config.Set); this.setPercentage(value, callback, context, setGA, setReverse);
myCharacteristic.timeout = Date.now()+milliTimeout; myCharacteristic.timeout = Date.now()+milliTimeout;
}.bind(this)); }.bind(this));
break; break;
@@ -433,7 +469,7 @@ KNXDevice.prototype = {
}.bind(this)); }.bind(this));
break; break;
default: { default: {
this.log("[ERROR] unknown type passed"); this.log(colorOn + "[ERROR] unknown type passed: [" + valueType+"]"+ colorOff);
throw new Error("[ERROR] unknown type passed"); throw new Error("[ERROR] unknown type passed");
} }
} }
@@ -445,9 +481,9 @@ KNXDevice.prototype = {
case "Bool": case "Bool":
this.knxregister_bool([config.Set].concat(config.Listen || []), myCharacteristic); this.knxregister_bool([config.Set].concat(config.Listen || []), myCharacteristic);
break; break;
case "BoolReverse": // case "BoolReverse":
this.knxregister_boolReverse([config.Set].concat(config.Listen || []), myCharacteristic); // this.knxregister_boolReverse([config.Set].concat(config.Listen || []), myCharacteristic);
break; // break;
case "Percent": case "Percent":
this.knxregister_percent([config.Set].concat(config.Listen || []), myCharacteristic); this.knxregister_percent([config.Set].concat(config.Listen || []), myCharacteristic);
break; break;
@@ -461,10 +497,10 @@ KNXDevice.prototype = {
this.knxregister_HVAC([config.Set].concat(config.Listen || []), myCharacteristic); this.knxregister_HVAC([config.Set].concat(config.Listen || []), myCharacteristic);
break; break;
default: default:
this.log("[ERROR] unknown type passed"); this.log(colorOn+ "[ERROR] unknown type passed: ["+valueType+"]"+colorOff);
throw new Error("[ERROR] unknown type passed"); throw new Error("[ERROR] unknown type passed");
} }
this.log("Issuing read requests on the KNX bus..."); this.log("["+ this.name +"]:["+myCharacteristic.displayName+"]: Issuing read requests on the KNX bus...");
this.knxreadarray([config.Set].concat(config.Listen || [])); this.knxreadarray([config.Set].concat(config.Listen || []));
} }
return myCharacteristic; // for chaining or whatsoever return myCharacteristic; // for chaining or whatsoever
@@ -494,30 +530,30 @@ KNXDevice.prototype = {
var myService = new Service.ContactSensor(config.name,config.name); var myService = new Service.ContactSensor(config.name,config.name);
if (config.ContactSensorState) { if (config.ContactSensorState) {
this.log("ContactSensor ContactSensorState characteristic enabled"); this.log("["+ this.name +"]:ContactSensor ContactSensorState characteristic enabled");
this.bindCharacteristic(myService, Characteristic.ContactSensorState, "Bool", config.ContactSensorState); this.bindCharacteristic(myService, Characteristic.ContactSensorState, "Bool", config.ContactSensorState);
} else if (config.ContactSensorStateContact1) { } else if (config.ContactSensorStateContact1) {
this.log("ContactSensor ContactSensorStateContact1 characteristic enabled"); this.log(colorOn+ "[ERROR] outdated type passed: [ContactSensorStateContact1]"+colorOff);
this.bindCharacteristic(myService, Characteristic.ContactSensorState, "BoolReverse", config.ContactSensorStateContact1); throw new Error("[ERROR] outdated type passed");
} }
//optionals //optionals
if (config.StatusActive) { if (config.StatusActive) {
this.log("ContactSensor StatusActive characteristic enabled"); this.log("["+ this.name +"]:ContactSensor StatusActive characteristic enabled");
myService.addCharacteristic(Characteristic.StatusActive); myService.addCharacteristic(Characteristic.StatusActive);
this.bindCharacteristic(myService, Characteristic.StatusActive, "Bool", config.StatusActive); this.bindCharacteristic(myService, Characteristic.StatusActive, "Bool", config.StatusActive);
} }
if (config.StatusFault) { if (config.StatusFault) {
this.log("ContactSensor StatusFault characteristic enabled"); this.log("["+ this.name +"]:ContactSensor StatusFault characteristic enabled");
myService.addCharacteristic(Characteristic.StatusFault); myService.addCharacteristic(Characteristic.StatusFault);
this.bindCharacteristic(myService, Characteristic.StatusFault, "Bool", config.StatusFault); this.bindCharacteristic(myService, Characteristic.StatusFault, "Bool", config.StatusFault);
} }
if (config.StatusTampered) { if (config.StatusTampered) {
this.log("ContactSensor StatusTampered characteristic enabled"); this.log("["+ this.name +"]:ContactSensor StatusTampered characteristic enabled");
myService.addCharacteristic(Characteristic.StatusTampered); myService.addCharacteristic(Characteristic.StatusTampered);
this.bindCharacteristic(myService, Characteristic.StatusTampered, "Bool", config.StatusTampered); this.bindCharacteristic(myService, Characteristic.StatusTampered, "Bool", config.StatusTampered);
} }
if (config.StatusLowBattery) { if (config.StatusLowBattery) {
this.log("ContactSensor StatusLowBattery characteristic enabled"); this.log("["+ this.name +"]:ContactSensor StatusLowBattery characteristic enabled");
myService.addCharacteristic(Characteristic.StatusLowBattery); myService.addCharacteristic(Characteristic.StatusLowBattery);
this.bindCharacteristic(myService, Characteristic.StatusLowBattery, "Bool", config.StatusLowBattery); this.bindCharacteristic(myService, Characteristic.StatusLowBattery, "Bool", config.StatusLowBattery);
} }
@@ -555,27 +591,27 @@ KNXDevice.prototype = {
var myService = new Service.GarageDoorOpener(config.name,config.name); var myService = new Service.GarageDoorOpener(config.name,config.name);
if (config.CurrentDoorState) { if (config.CurrentDoorState) {
this.log("GarageDoorOpener CurrentDoorState characteristic enabled"); this.log("["+ this.name +"]:GarageDoorOpener CurrentDoorState characteristic enabled");
this.bindCharacteristic(myService, Characteristic.CurrentDoorState, "Int", config.CurrentDoorState); this.bindCharacteristic(myService, Characteristic.CurrentDoorState, "Int", config.CurrentDoorState);
} }
if (config.TargetDoorState) { if (config.TargetDoorState) {
this.log("GarageDoorOpener TargetDoorState characteristic enabled"); this.log("["+ this.name +"]:GarageDoorOpener TargetDoorState characteristic enabled");
//myService.getCharacteristic(Characteristic.TargetDoorState).minimumValue=0; // //myService.getCharacteristic(Characteristic.TargetDoorState).minimumValue=0; //
//myService.getCharacteristic(Characteristic.TargetDoorState).maximumValue=4; // //myService.getCharacteristic(Characteristic.TargetDoorState).maximumValue=4; //
this.bindCharacteristic(myService, Characteristic.TargetDoorState, "Int", config.TargetDoorState); this.bindCharacteristic(myService, Characteristic.TargetDoorState, "Int", config.TargetDoorState);
} }
if (config.ObstructionDetected) { if (config.ObstructionDetected) {
this.log("GarageDoorOpener ObstructionDetected characteristic enabled"); this.log("["+ this.name +"]:GarageDoorOpener ObstructionDetected characteristic enabled");
this.bindCharacteristic(myService, Characteristic.ObstructionDetected, "Bool", config.ObstructionDetected); this.bindCharacteristic(myService, Characteristic.ObstructionDetected, "Bool", config.ObstructionDetected);
} }
//optionals //optionals
if (config.LockCurrentState) { if (config.LockCurrentState) {
this.log("GarageDoorOpener LockCurrentState characteristic enabled"); this.log("["+ this.name +"]:GarageDoorOpener LockCurrentState characteristic enabled");
myService.addCharacteristic(Characteristic.LockCurrentState); myService.addCharacteristic(Characteristic.LockCurrentState);
this.bindCharacteristic(myService, Characteristic.LockCurrentState, "Int", config.LockCurrentState); this.bindCharacteristic(myService, Characteristic.LockCurrentState, "Int", config.LockCurrentState);
} }
if (config.LockTargetState) { if (config.LockTargetState) {
this.log("GarageDoorOpener LockTargetState characteristic enabled"); this.log("["+ this.name +"]:GarageDoorOpener LockTargetState characteristic enabled");
myService.addCharacteristic(Characteristic.LockTargetState); myService.addCharacteristic(Characteristic.LockTargetState);
this.bindCharacteristic(myService, Characteristic.LockTargetState, "Bool", config.LockTargetState); this.bindCharacteristic(myService, Characteristic.LockTargetState, "Bool", config.LockTargetState);
} }
@@ -596,12 +632,12 @@ KNXDevice.prototype = {
var myService = new Service.Lightbulb(config.name,config.name); var myService = new Service.Lightbulb(config.name,config.name);
// On (and Off) // On (and Off)
if (config.On) { if (config.On) {
this.log("Lightbulb on/off characteristic enabled"); this.log("["+ this.name +"]:Lightbulb on/off characteristic enabled");
this.bindCharacteristic(myService, Characteristic.On, "Bool", config.On); this.bindCharacteristic(myService, Characteristic.On, "Bool", config.On);
} // On characteristic } // On characteristic
// Brightness if available // Brightness if available
if (config.Brightness) { if (config.Brightness) {
this.log("Lightbulb Brightness characteristic enabled"); this.log("["+ this.name +"]:Lightbulb Brightness characteristic enabled");
myService.addCharacteristic(Characteristic.Brightness); // it's an optional myService.addCharacteristic(Characteristic.Brightness); // it's an optional
this.bindCharacteristic(myService, Characteristic.Brightness, "Percent", config.Brightness); this.bindCharacteristic(myService, Characteristic.Brightness, "Percent", config.Brightness);
} }
@@ -623,7 +659,7 @@ KNXDevice.prototype = {
var myService = new Service.LightSensor(config.name,config.name); var myService = new Service.LightSensor(config.name,config.name);
// CurrentTemperature) // CurrentTemperature)
if (config.CurrentAmbientLightLevel) { if (config.CurrentAmbientLightLevel) {
this.log("LightSensor CurrentAmbientLightLevel characteristic enabled"); this.log("["+ this.name +"]:LightSensor CurrentAmbientLightLevel characteristic enabled");
this.bindCharacteristic(myService, Characteristic.CurrentAmbientLightLevel, "Float", config.CurrentAmbientLightLevel); this.bindCharacteristic(myService, Characteristic.CurrentAmbientLightLevel, "Float", config.CurrentAmbientLightLevel);
} }
return myService; return myService;
@@ -648,20 +684,20 @@ KNXDevice.prototype = {
// LockCurrentState // LockCurrentState
if (config.LockCurrentState) { if (config.LockCurrentState) {
// for normal contacts: Secured = 1 // for normal contacts: Secured = 1
this.log("LockMechanism LockCurrentState characteristic enabled"); this.log("["+ this.name +"]:LockMechanism LockCurrentState characteristic enabled");
this.bindCharacteristic(myService, Characteristic.LockCurrentState, "Bool", config.LockCurrentState); this.bindCharacteristic(myService, Characteristic.LockCurrentState, "Bool", config.LockCurrentState);
} else if (config.LockCurrentStateSecured0) { } else if (config.LockCurrentStateSecured0) {
// for reverse contacts Secured = 0 // for reverse contacts Secured = 0
this.log("LockMechanism LockCurrentState characteristic enabled"); this.log(colorOn+ "[ERROR] outdated type passed: [LockCurrentStateSecured0]"+colorOff);
this.bindCharacteristic(myService, Characteristic.LockCurrentState, "BoolReverse", config.LockCurrentStateSecured0); throw new Error("[ERROR] outdated type passed");
} }
// LockTargetState // LockTargetState
if (config.LockTargetState) { if (config.LockTargetState) {
this.log("LockMechanism LockTargetState characteristic enabled"); this.log("["+ this.name +"]:LockMechanism LockTargetState characteristic enabled");
this.bindCharacteristic(myService, Characteristic.LockTargetState, "Bool", config.LockTargetState); this.bindCharacteristic(myService, Characteristic.LockTargetState, "Bool", config.LockTargetState);
} else if (config.LockTargetStateSecured0) { } else if (config.LockTargetStateSecured0) {
this.log("LockMechanism LockTargetState characteristic enabled"); this.log(colorOn+ "[ERROR] outdated type passed: [LockTargetStateSecured0]"+colorOff);
this.bindCharacteristic(myService, Characteristic.LockTargetState, "BoolReverse", config.LockTargetStateSecured0); throw new Error("[ERROR] outdated type passed");
} }
//iterate(myService); //iterate(myService);
@@ -683,27 +719,27 @@ KNXDevice.prototype = {
var myService = new Service.MotionSensor(config.name,config.name); var myService = new Service.MotionSensor(config.name,config.name);
if (config.MotionDetected) { if (config.MotionDetected) {
this.log("MotionSensor MotionDetected characteristic enabled"); this.log("["+ this.name +"]:MotionSensor MotionDetected characteristic enabled");
this.bindCharacteristic(myService, Characteristic.MotionDetected, "Bool", config.MotionDetected); this.bindCharacteristic(myService, Characteristic.MotionDetected, "Bool", config.MotionDetected);
} }
//optionals //optionals
if (config.StatusActive) { if (config.StatusActive) {
this.log("MotionSensor StatusActive characteristic enabled"); this.log("["+ this.name +"]:MotionSensor StatusActive characteristic enabled");
myService.addCharacteristic(Characteristic.StatusActive); myService.addCharacteristic(Characteristic.StatusActive);
this.bindCharacteristic(myService, Characteristic.StatusActive, "Bool", config.StatusActive); this.bindCharacteristic(myService, Characteristic.StatusActive, "Bool", config.StatusActive);
} }
if (config.StatusFault) { if (config.StatusFault) {
this.log("MotionSensor StatusFault characteristic enabled"); this.log("["+ this.name +"]:MotionSensor StatusFault characteristic enabled");
myService.addCharacteristic(Characteristic.StatusFault); myService.addCharacteristic(Characteristic.StatusFault);
this.bindCharacteristic(myService, Characteristic.StatusFault, "Bool", config.StatusFault); this.bindCharacteristic(myService, Characteristic.StatusFault, "Bool", config.StatusFault);
} }
if (config.StatusTampered) { if (config.StatusTampered) {
this.log("MotionSensor StatusTampered characteristic enabled"); this.log("["+ this.name +"]:MotionSensor StatusTampered characteristic enabled");
myService.addCharacteristic(Characteristic.StatusTampered); myService.addCharacteristic(Characteristic.StatusTampered);
this.bindCharacteristic(myService, Characteristic.StatusTampered, "Bool", config.StatusTampered); this.bindCharacteristic(myService, Characteristic.StatusTampered, "Bool", config.StatusTampered);
} }
if (config.StatusLowBattery) { if (config.StatusLowBattery) {
this.log("MotionSensor StatusLowBattery characteristic enabled"); this.log("["+ this.name +"]:MotionSensor StatusLowBattery characteristic enabled");
myService.addCharacteristic(Characteristic.StatusLowBattery); myService.addCharacteristic(Characteristic.StatusLowBattery);
this.bindCharacteristic(myService, Characteristic.StatusLowBattery, "Bool", config.StatusLowBattery); this.bindCharacteristic(myService, Characteristic.StatusLowBattery, "Bool", config.StatusLowBattery);
} }
@@ -726,11 +762,11 @@ KNXDevice.prototype = {
var myService = new Service.Outlet(config.name,config.name); var myService = new Service.Outlet(config.name,config.name);
// On (and Off) // On (and Off)
if (config.On) { if (config.On) {
this.log("Outlet on/off characteristic enabled"); this.log("["+ this.name +"]:Outlet on/off characteristic enabled");
this.bindCharacteristic(myService, Characteristic.On, "Bool", config.On); this.bindCharacteristic(myService, Characteristic.On, "Bool", config.On);
} // OutletInUse characteristic } // OutletInUse characteristic
if (config.OutletInUse) { if (config.OutletInUse) {
this.log("Outlet on/off characteristic enabled"); this.log("["+ this.name +"]:Outlet on/off characteristic enabled");
this.bindCharacteristic(myService, Characteristic.OutletInUse, "Bool", config.OutletInUse); this.bindCharacteristic(myService, Characteristic.OutletInUse, "Bool", config.OutletInUse);
} }
return myService; return myService;
@@ -748,7 +784,7 @@ KNXDevice.prototype = {
var myService = new Service.Switch(config.name,config.name); var myService = new Service.Switch(config.name,config.name);
// On (and Off) // On (and Off)
if (config.On) { if (config.On) {
this.log("Switch on/off characteristic enabled"); this.log("["+ this.name +"]:Switch on/off characteristic enabled");
this.bindCharacteristic(myService, Characteristic.On, "Bool", config.On); this.bindCharacteristic(myService, Characteristic.On, "Bool", config.On);
} // On characteristic } // On characteristic
@@ -775,26 +811,35 @@ KNXDevice.prototype = {
var myService = new Service.Thermostat(config.name,config.name); var myService = new Service.Thermostat(config.name,config.name);
// CurrentTemperature) // CurrentTemperature)
// props update for https://github.com/KhaosT/HAP-NodeJS/commit/1d84d128d1513beedcafc24d2c07d98185563243#diff-cb84de3a1478a38b2cf8388d709f1c1cR108
if (config.CurrentTemperature) { if (config.CurrentTemperature) {
this.log("Thermostat CurrentTemperature characteristic enabled"); this.log("["+ this.name +"]:Thermostat CurrentTemperature characteristic enabled");
myService.getCharacteristic(Characteristic.CurrentTemperature).setProps({
minValue: config.CurrentTemperature.minValue || -40,
maxValue: config.CurrentTemperature.maxValue || 60
}); // °C by default
this.bindCharacteristic(myService, Characteristic.CurrentTemperature, "Float", config.CurrentTemperature); this.bindCharacteristic(myService, Characteristic.CurrentTemperature, "Float", config.CurrentTemperature);
} }
// TargetTemperature if available // TargetTemperature if available
if (config.TargetTemperature) { if (config.TargetTemperature) {
this.log("Thermostat TargetTemperature characteristic enabled"); this.log("["+ this.name +"]:Thermostat TargetTemperature characteristic enabled");
// default boundary too narrow for thermostats // default boundary too narrow for thermostats
myService.getCharacteristic(Characteristic.TargetTemperature).minimumValue=0; // °C // props update for https://github.com/KhaosT/HAP-NodeJS/commit/1d84d128d1513beedcafc24d2c07d98185563243#diff-cb84de3a1478a38b2cf8388d709f1c1cR108
myService.getCharacteristic(Characteristic.TargetTemperature).maximumValue=40; // °C myService.getCharacteristic(Characteristic.TargetTemperature).setProps({
minValue: config.TargetTemperature.minValue || 0,
maxValue: config.TargetTemperature.maxValue || 40
});
this.bindCharacteristic(myService, Characteristic.TargetTemperature, "Float", config.TargetTemperature); this.bindCharacteristic(myService, Characteristic.TargetTemperature, "Float", config.TargetTemperature);
} }
// HVAC // HVAC
if (config.CurrentHeatingCoolingState) { if (config.CurrentHeatingCoolingState) {
this.log("Thermostat CurrentHeatingCoolingState characteristic enabled"); this.log("["+ this.name +"]:Thermostat CurrentHeatingCoolingState characteristic enabled");
this.bindCharacteristic(myService, Characteristic.CurrentHeatingCoolingState, "HVAC", config.CurrentHeatingCoolingState); this.bindCharacteristic(myService, Characteristic.CurrentHeatingCoolingState, "HVAC", config.CurrentHeatingCoolingState);
} }
// HVAC // HVAC
if (config.TargetHeatingCoolingState) { if (config.TargetHeatingCoolingState) {
this.log("Thermostat TargetHeatingCoolingState characteristic enabled"); this.log("["+ this.name +"]:Thermostat TargetHeatingCoolingState characteristic enabled");
this.bindCharacteristic(myService, Characteristic.TargetHeatingCoolingState, "HVAC", config.TargetHeatingCoolingState); this.bindCharacteristic(myService, Characteristic.TargetHeatingCoolingState, "HVAC", config.TargetHeatingCoolingState);
} }
return myService; return myService;
@@ -812,10 +857,16 @@ KNXDevice.prototype = {
} }
var myService = new Service.TemperatureSensor(config.name,config.name); var myService = new Service.TemperatureSensor(config.name,config.name);
// CurrentTemperature) // CurrentTemperature)
// props update for https://github.com/KhaosT/HAP-NodeJS/commit/1d84d128d1513beedcafc24d2c07d98185563243#diff-cb84de3a1478a38b2cf8388d709f1c1cR108
if (config.CurrentTemperature) { if (config.CurrentTemperature) {
this.log("TemperatureSensor CurrentTemperature characteristic enabled"); this.log("["+ this.name +"]:TemperatureSensor CurrentTemperature characteristic enabled");
myService.getCharacteristic(Characteristic.CurrentTemperature).setProps({
minValue: config.CurrentTemperature.minValue || -40,
maxValue: config.CurrentTemperature.maxValue || 60
}); // °C by default
this.bindCharacteristic(myService, Characteristic.CurrentTemperature, "Float", config.CurrentTemperature); this.bindCharacteristic(myService, Characteristic.CurrentTemperature, "Float", config.CurrentTemperature);
} }
return myService; return myService;
}, },
getWindowService: function(config) { getWindowService: function(config) {
@@ -845,16 +896,16 @@ KNXDevice.prototype = {
var myService = new Service.Window(config.name,config.name); var myService = new Service.Window(config.name,config.name);
if (config.CurrentPosition) { if (config.CurrentPosition) {
this.log("Window CurrentPosition characteristic enabled"); this.log("["+ this.name +"]:Window CurrentPosition characteristic enabled");
this.bindCharacteristic(myService, Characteristic.CurrentPosition, "Percent", config.CurrentPosition); this.bindCharacteristic(myService, Characteristic.CurrentPosition, "Percent", config.CurrentPosition);
} }
if (config.TargetPosition) { if (config.TargetPosition) {
this.log("Window TargetPosition characteristic enabled"); this.log("["+ this.name +"]:Window TargetPosition characteristic enabled");
this.bindCharacteristic(myService, Characteristic.TargetPosition, "Percent", config.TargetPosition); this.bindCharacteristic(myService, Characteristic.TargetPosition, "Percent", config.TargetPosition);
} }
if (config.PositionState) { if (config.PositionState) {
this.log("Window PositionState characteristic enabled"); this.log("["+ this.name +"]:Window PositionState characteristic enabled");
this.bindCharacteristic(myService, Characteristic.PositionState, "Float", config.PositionState); this.bindCharacteristic(myService, Characteristic.PositionState, "Int", config.PositionState);
} }
return myService; return myService;
}, },
@@ -880,15 +931,15 @@ KNXDevice.prototype = {
var myService = new Service.WindowCovering(config.name,config.name); var myService = new Service.WindowCovering(config.name,config.name);
if (config.CurrentPosition) { if (config.CurrentPosition) {
this.log("WindowCovering CurrentPosition characteristic enabled"); this.log("["+ this.name +"]:WindowCovering CurrentPosition characteristic enabled");
this.bindCharacteristic(myService, Characteristic.CurrentPosition, "Percent", config.CurrentPosition); this.bindCharacteristic(myService, Characteristic.CurrentPosition, "Percent", config.CurrentPosition);
} }
if (config.TargetPosition) { if (config.TargetPosition) {
this.log("WindowCovering TargetPosition characteristic enabled"); this.log("["+ this.name +"]:WindowCovering TargetPosition characteristic enabled");
this.bindCharacteristic(myService, Characteristic.TargetPosition, "Percent", config.TargetPosition); this.bindCharacteristic(myService, Characteristic.TargetPosition, "Percent", config.TargetPosition);
} }
if (config.PositionState) { if (config.PositionState) {
this.log("WindowCovering PositionState characteristic enabled"); this.log("["+ this.name +"]:WindowCovering PositionState characteristic enabled");
this.bindCharacteristic(myService, Characteristic.PositionState, "Float", config.PositionState); this.bindCharacteristic(myService, Characteristic.PositionState, "Float", config.PositionState);
} }
return myService; return myService;
@@ -910,7 +961,7 @@ KNXDevice.prototype = {
informationService informationService
.setCharacteristic(Characteristic.Manufacturer, "Opensource Community") .setCharacteristic(Characteristic.Manufacturer, "Opensource Community")
.setCharacteristic(Characteristic.Model, "KNX Universal Device") .setCharacteristic(Characteristic.Model, "KNX Universal Device")
.setCharacteristic(Characteristic.SerialNumber, "Version 1.1.2"); .setCharacteristic(Characteristic.SerialNumber, "Version 1.1.4");
accessoryServices.push(informationService); accessoryServices.push(informationService);
@@ -965,8 +1016,8 @@ KNXDevice.prototype = {
accessoryServices.push(this.getWindowCoveringService(configService)); accessoryServices.push(this.getWindowCoveringService(configService));
break; break;
default: default:
this.log("[ERROR] unknown 'type' property of '"+configService.type+"' for service "+ configService.name + " in config.json. KNX platform section fault "); this.log("[ERROR] unknown 'type' property of ["+configService.type+"] for service ["+ configService.name + "] in config.json. KNX platform section fault ");
//throw new Error("[ERROR] unknown 'type' property for service "+ configService.name + " in config.json. KNX platform section fault "); throw new Error("[ERROR] unknown 'type' property of ["+configService.type+"] for service '"+ configService.name + "' in config.json. KNX platform section fault ");
} }
} }
// start listening for events on the bus (if not started yet - will prevent itself) // start listening for events on the bus (if not started yet - will prevent itself)

View File

@@ -89,14 +89,19 @@
"delay": 30, "delay": 30,
"repeat": 3, "repeat": 3,
"zones":["Kitchen Lamp","Bedroom Lamp","Living Room Lamp","Hallway Lamp"] "zones":["Kitchen Lamp","Bedroom Lamp","Living Room Lamp","Hallway Lamp"]
}, },
{ {
"platform": "HomeAssistant", "platform": "HomeAssistant",
"name": "HomeAssistant", "name": "HomeAssistant",
"host": "http://192.168.1.10:8123", "host": "http://192.168.1.10:8123",
"password": "XXXXX", "password": "XXXXX",
"supported_types": ["light", "switch", "media_player", "scene"] "supported_types": ["light", "switch", "media_player", "scene"]
} },
{
"platform": "LIFx",
"name": "LIFx",
"access_token": "XXXXXXXX generate at https://cloud.lifx.com/settings"
}
], ],
"accessories": [ "accessories": [

View File

@@ -152,15 +152,24 @@ HomeAssistantPlatform.prototype = {
entity = data[i] entity = data[i]
entity_type = entity.entity_id.split('.')[0] entity_type = entity.entity_id.split('.')[0]
// ignore devices that are not in the list of supported types
if (that.supportedTypes.indexOf(entity_type) == -1) { if (that.supportedTypes.indexOf(entity_type) == -1) {
continue; continue;
} }
// ignore hidden devices
if (entity.attributes && entity.attributes.hidden) {
continue;
}
var accessory = null var accessory = null
if (entity_type == 'light') { if (entity_type == 'light') {
accessory = new HomeAssistantLight(that.log, entity, that) accessory = new HomeAssistantLight(that.log, entity, that)
}else if (entity_type == 'switch'){ }else if (entity_type == 'switch'){
console.log(JSON.stringify(entity))
console.log("");
console.log("");
accessory = new HomeAssistantSwitch(that.log, entity, that) accessory = new HomeAssistantSwitch(that.log, entity, that)
}else if (entity_type == 'scene'){ }else if (entity_type == 'scene'){
accessory = new HomeAssistantSwitch(that.log, entity, that, 'scene') accessory = new HomeAssistantSwitch(that.log, entity, that, 'scene')

View File

@@ -1,154 +1,156 @@
{ {
"bridge": { "bridge": {
"name": "Homebridge", "name": "Homebridge",
"username": "CC:22:3D:E3:CE:30", "username": "CC:22:3D:E3:CE:30",
"port": 51826, "port": 51826,
"pin": "031-45-154" "pin": "031-45-154"
}, },
"description": "This is an example configuration file for KNX platform shim", "description": "This is an example configuration file for KNX platform shim",
"hint": "Always paste into jsonlint.com validation page before starting your homebridge, saves a lot of frustration", "hint": "Always paste into jsonlint.com validation page before starting your homebridge, saves a lot of frustration",
"hint2": "Replace all group addresses by current addresses of your installation, these are arbitrary examples!", "hint2": "Replace all group addresses by current addresses of your installation, these are arbitrary examples!",
"hint3": "For valid services and their characteristics have a look at the knxdevice.md file in folder accessories!", "hint3": "For valid services and their characteristics have a look at the KNX.md file in folder platforms!",
"platforms": [ "platforms": [
{ {
"platform": "KNX", "platform": "KNX",
"name": "KNX", "name": "KNX",
"knxd_ip": "192.168.178.205", "knxd_ip": "192.168.178.205",
"knxd_port": 6720, "knxd_port": 6720,
"accessories": [ "accessories": [
{ {
"accessory_type": "knxdevice", "accessory_type": "knxdevice",
"description": "Only generic type knxdevice is supported, all previous knx types have been merged into that.", "description": "Only generic type knxdevice is supported, all previous knx types have been merged into that.",
"name": "Living Room North Lamp", "name": "Living Room North Lamp",
"services": [ "services": [
{ {
"type": "Lightbulb", "type": "Lightbulb",
"description": "iOS8 Lightbulb type, supports On (Switch) and Brightness", "description": "iOS8 Lightbulb type, supports On (Switch) and Brightness",
"name": "Living Room North Lamp", "name": "Living Room North Lamp",
"On": { "On": {
"Set": "1/1/6", "Set": "1/1/6",
"Listen": [ "Listen": [
"1/1/63" "1/1/63"
] ]
}, },
"Brightness": { "Brightness": {
"Set": "1/1/62", "Set": "1/1/62",
"Listen": [ "Listen": [
"1/1/64" "1/1/64"
] ]
} }
} }
], ],
"services-description": "Services is an array, you CAN have multiple service types in one accessory, though it is not fully supported in many iOS HK apps, such as EVE and myTouchHome" "services-description": "Services is an array, you CAN have multiple service types in one accessory, though it is not fully supported in many iOS HK apps, such as EVE and myTouchHome"
}, },
{ {
"accessory_type": "knxdevice", "accessory_type": "knxdevice",
"name": "Office Temperature", "name": "Office Temperature",
"description": "iOS8.4.1 TemperatureSensor type, supports CurrentTemperature", "description": "iOS8.4.1 TemperatureSensor type, supports CurrentTemperature",
"services": [ "services": [
{ {
"type": "TemperatureSensor", "type": "TemperatureSensor",
"name": "Raumtemperatur", "name": "Raumtemperatur",
"CurrentTemperature": { "CurrentTemperature": {
"Listen": "3/3/44" "Listen": "3/3/44"
} }
} }
] ]
}, },
{ {
"accessory_type": "knxdevice", "accessory_type": "knxdevice",
"name": "Office Window Lock", "name": "Office Window Lock",
"services": [ "services": [
{ {
"type": "LockMechanism", "type": "LockMechanism",
"description": "iOS8 Lock mechanism, Supports LockCurrentStateSecured0 OR LockCurrentState, LockTargetStateSecured0 OR LockTargetState, use depending if LOCKED is 0 or 1", "description": "iOS8 Lock mechanism, Supports LockCurrentState, LockTargetState, append R to the addresses if LOCKED is 1",
"name": "Office Window Lock", "name": "Office Window Lock",
"LockCurrentStateSecured0": { "LockCurrentState": {
"Listen": "5/3/15" "Listen": "5/3/15R"
}, },
"LockTargetStateSecured0": { "LockTargetState": {
"Listen": "5/3/15" "Listen": "5/3/16R"
} }
} }
] ]
}, },
{ {
"accessory_type": "knxdevice", "accessory_type": "knxdevice",
"description": "sample device with multiple services. Multiple services of different types are widely supported", "description": "sample device with multiple services. Multiple services of different types are widely supported",
"name": "Office", "name": "Office",
"services": [ "services": [
{ {
"type": "Lightbulb", "type": "Lightbulb",
"name": "Office Lamp", "name": "Office Lamp",
"On": { "On": {
"Set": "1/3/5" "Set": "1/3/5"
} }
}, },
{ {
"type": "Thermostat", "type": "Thermostat",
"description": "iOS8 Thermostat type, supports CurrentTemperature, TargetTemperature, CurrentHeatingCoolingState ", "description": "iOS8 Thermostat type, supports CurrentTemperature, TargetTemperature, CurrentHeatingCoolingState ",
"name": "Raumtemperatur", "name": "Raumtemperatur",
"CurrentTemperature": { "CurrentTemperature": {
"Listen": "3/3/44" "Listen": "3/3/44"
}, },
"TargetTemperature": { "TargetTemperature": {
"Set": "3/3/94" "Set": "3/3/94"
}, },
"CurrentHeatingCoolingState": { "CurrentHeatingCoolingState": {
"Listen": "3/3/64" "Listen": "3/3/64"
} }
}, },
{ {
"type": "WindowCovering", "type": "WindowCovering",
"description": "iOS9 Window covering (blinds etc) type, still WIP", "description": "iOS9 Window covering (blinds etc) type, still WIP",
"name": "Blinds", "name": "Blinds",
"TargetPosition": { "TargetPosition": {
"Set": "1/2/3", "Set": "1/2/3",
"Listen": "1/2/4" "Listen": "1/2/4"
}, },
"CurrentPosition": { "CurrentPosition": {
"Set": "1/3/1", "Set": "1/3/1",
"Listen": "1/3/2" "Listen": "1/3/2"
}, },
"PositionState": { "PositionState": {
"Listen": "2/7/1" "Listen": "2/7/1"
} }
} }
] ]
}, },
{ {
"accessory_type": "knxdevice", "accessory_type": "knxdevice",
"description": "sample contact sensor device", "description": "sample contact sensor device",
"name": "Office Contact", "name": "Office Contact",
"services": [ "services": [
{ {
"type": "ContactSensor", "type": "ContactSensor",
"name": "Office Door", "name": "Office Door",
"ContactSensorState": { "ContactSensorState": {
"Listen": "5/3/5" "Listen": "5/3/5"
} }
} }
] ]
}, },
{ {
"accessory_type": "knxdevice", "accessory_type": "knxdevice",
"description": "sample garage door opener", "description": "sample garage door opener",
"name": "Office Garage", "name": "Office Garage",
"services": [ "services": [
{ {
"type": "GarageDoorOpener", "type": "GarageDoorOpener",
"name": "Office Garage Opener", "name": "Office Garage Opener",
"CurrentDoorState": { "CurrentDoorState": {
"Listen": "5/4/5" "Listen": "5/4/5"
}, },
"TargetDoorState": { "TargetDoorState": {
"Listen": "5/4/6" "Listen": "5/4/6"
} }
} }
] ]
} }
] ]
} }
], ],
"accessories": [] "accessories": [
]
} }

View File

@@ -116,8 +116,8 @@ function groupsocketlisten(opts, callback) {
} }
var registerSingleGA = function registerSingleGA (groupAddress, callback) { var registerSingleGA = function registerSingleGA (groupAddress, callback, reverse) {
subscriptions.push({address: groupAddress, callback: callback }); subscriptions.push({address: groupAddress, callback: callback, reverse:reverse });
} }
/* /*
@@ -143,7 +143,7 @@ var startMonitor = function startMonitor(opts) { // using { host: name-ip, port
if (subscriptions[i].address === dest) { if (subscriptions[i].address === dest) {
// found one, notify // found one, notify
console.log('HIT: Write from '+src+' to '+dest+': '+val+' ['+type+']'); console.log('HIT: Write from '+src+' to '+dest+': '+val+' ['+type+']');
subscriptions[i].callback(val, src, dest, type); subscriptions[i].callback(val, src, dest, type, subscriptions[i].reverse);
} }
} }
}); });
@@ -156,7 +156,7 @@ var startMonitor = function startMonitor(opts) { // using { host: name-ip, port
if (subscriptions[i].address === dest) { if (subscriptions[i].address === dest) {
// found one, notify // found one, notify
// console.log('HIT: Response from '+src+' to '+dest+': '+val+' ['+type+']'); // console.log('HIT: Response from '+src+' to '+dest+': '+val+' ['+type+']');
subscriptions[i].callback(val, src, dest, type); subscriptions[i].callback(val, src, dest, type, subscriptions[i].reverse);
} }
} }
@@ -185,13 +185,16 @@ var registerGA = function (groupAddresses, callback) {
if (groupAddresses.constructor.toString().indexOf("Array") > -1) { if (groupAddresses.constructor.toString().indexOf("Array") > -1) {
// handle multiple addresses // handle multiple addresses
for (var i = 0; i < groupAddresses.length; i++) { for (var i = 0; i < groupAddresses.length; i++) {
if (groupAddresses[i]) { // do not bind empty addresses if (groupAddresses[i] && groupAddresses[i].match(/(\d*\/\d*\/\d*)/)) { // do not bind empty addresses or invalid addresses
registerSingleGA (groupAddresses[i], callback); // clean the addresses
registerSingleGA (groupAddresses[i].match(/(\d*\/\d*\/\d*)/)[0], callback,groupAddresses[i].match(/\d*\/\d*\/\d*(R)/) ? true:false );
} }
} }
} else { } else {
// it's only one // it's only one
registerSingleGA (groupAddresses, callback); if (groupAddresses.match(/(\d*\/\d*\/\d*)/)) {
registerSingleGA (groupAddresses.match(/(\d*\/\d*\/\d*)/)[0], callback, groupAddresses[i].match(/\d*\/\d*\/\d*(R)/) ? true:false);
}
} }
// console.log("listeners now: " + subscriptions.length); // console.log("listeners now: " + subscriptions.length);
}; };

View File

@@ -47,7 +47,7 @@ You have to add services in the following syntax:
{ {
"type": "SERVICENAME", "type": "SERVICENAME",
"description": "This is just for you to remember things", "description": "This is just for you to remember things",
"name": "We need a name for each service, though it usually shows only if multiple services are present in one accessory", "name": "beer tap thermostat",
"CHARACTERISTIC1": { "CHARACTERISTIC1": {
"Set": "1/1/6", "Set": "1/1/6",
"Listen": [ "Listen": [
@@ -68,11 +68,54 @@ Two kinds of addresses are supported: `"Set":"1/2/3"` is a writable group addres
`"Listen":["1/2/3","1/2/4","1/2/5"]` is an array of addresses that are listened to additionally. To these addresses never values get written, but the on startup the service will issue *KNX read requests* to ALL addresses listed in `Set:` and in `Listen:` `"Listen":["1/2/3","1/2/4","1/2/5"]` is an array of addresses that are listened to additionally. To these addresses never values get written, but the on startup the service will issue *KNX read requests* to ALL addresses listed in `Set:` and in `Listen:`
# Supported Services and their characteristics For two characteristics there are additional minValue and maxValue attributes. These are CurrentTemperature and TargetTemperature, and are used in TemperatureSensor and Thermostat.
So the charcteristic section may look like:
````json
{
"type": "Thermostat",
"description": "Sample thermostat",
"name": "We need a name for each service, though it usually shows only if multiple services are present in one accessory",
"CurrentTemperature": {
"Set": "1/1/6",
"Listen": [
"1/1/63"
],
"minValue": -18,
"maxValue": 30
},
"TargetTemperature": {
"Set": "1/1/62",
"Listen": [
"1/1/64"
],
"minValue": -4,
"maxValue": 12
}
}
````
## reversal of values for characteristics
In general, all DPT1 types can be reversed. If you need a 1 for "contact" of a contact senser, you can append an "R" to the group address.
Likewise, all percentages of DPT5 can be reversed, if you need a 100% (=255) for window closed, append an "R" to the group address. Do not forget the listening addresses!
````json
{
"type": "ContactSensor",
"description": "Sample ContactSensor with 1 as contact (0 is Apple's default)",
"name": "WindowContact1",
"ContactSensorState": {
"Listen": [
"1/1/100R"
]
}
}
````
# Supported Services and their characteristics
## ContactSensor ## ContactSensor
- ContactSensorState: DPT 1.002, 0 as contact **OR** - ContactSensorState: DPT 1.002, 0 as contact
- ContactSensorStateContact1: DPT 1.002, 1 as contact - ~~ContactSensorStateContact1: DPT 1.002, 1 as contact~~
- StatusActive: DPT 1.011, 1 as true - StatusActive: DPT 1.011, 1 as true
- StatusFault: DPT 1.011, 1 as true - StatusFault: DPT 1.011, 1 as true
@@ -113,10 +156,10 @@ Two kinds of addresses are supported: `"Set":"1/2/3"` is a writable group addres
- CurrentAmbientLightLevel: DPT 9.004, 0 to 100000 Lux - CurrentAmbientLightLevel: DPT 9.004, 0 to 100000 Lux
## LockMechanism (This is poorly mapped!) ## LockMechanism (This is poorly mapped!)
- LockCurrentState: DPT 1, 1 as secured **OR (but not both:)** - LockCurrentState: DPT 1, 1 as secured
- LockCurrentStateSecured0: DPT 1, 0 as secured - ~~LockCurrentStateSecured0: DPT 1, 0 as secured~~
- LockTargetState: DPT 1, 1 as secured **OR** - LockTargetState: DPT 1, 1 as secured
- LockTargetStateSecured0: DPT 1, 0 as secured - ~~LockTargetStateSecured0: DPT 1, 0 as secured~~
*ToDo here: correction of mappings, HomeKit reqires lock states UNSECURED=0, SECURED=1, JAMMED = 2, UNKNOWN=3* *ToDo here: correction of mappings, HomeKit reqires lock states UNSECURED=0, SECURED=1, JAMMED = 2, UNKNOWN=3*
@@ -136,11 +179,11 @@ Two kinds of addresses are supported: `"Set":"1/2/3"` is a writable group addres
- On: DPT 1.001, 1 as on, 0 as off - On: DPT 1.001, 1 as on, 0 as off
## TemperatureSensor ## TemperatureSensor
- CurrentTemperature: DPT9.001 in °C [listen only] - CurrentTemperature: DPT9.001 in °C [listen only]
## Thermostat ## Thermostat
- CurrentTemperature: DPT9.001 in °C [listen only] - CurrentTemperature: DPT9.001 in °C [listen only], -40 to 80°C if not overriden as shown above
- TargetTemperature: DPT9.001, values 0..40°C only, all others are ignored - TargetTemperature: DPT9.001, values 0..40°C only, all others are ignored
- CurrentHeatingCoolingState: DPT20.102 HVAC, because of the incompatible mapping only off and heating (=auto) are shown, [listen only] - CurrentHeatingCoolingState: DPT20.102 HVAC, because of the incompatible mapping only off and heating (=auto) are shown, [listen only]
- TargetHeatingCoolingState: DPT20.102 HVAC, as above - TargetHeatingCoolingState: DPT20.102 HVAC, as above
@@ -152,7 +195,7 @@ Two kinds of addresses are supported: `"Set":"1/2/3"` is a writable group addres
## WindowCovering ## WindowCovering
- CurrentPosition: DPT5 percentage - CurrentPosition: DPT5 percentage
- TargetPosition: DPT5 percentage - TargetPosition: DPT5 percentage
- PositionState: DPT5 value [listen only] - PositionState: DPT5 value [listen only: 0 Closing, 1 Opening, 2 Stopped]
### not yet supported ### not yet supported
- HoldPosition - HoldPosition