From 387e7ec9cebd750aa9dcbd4a06178d5edb64b85d Mon Sep 17 00:00:00 2001 From: Snowdd1 Date: Sat, 19 Sep 2015 15:19:11 +0200 Subject: [PATCH] KNX support for Garage Door Opener The garage door opener device MUST adhere to HomeKit numeric conventions, see KNX.md documentation --- accessories/knxdevice.js | 90 +++++++++++++++++++++++++++++++++++++--- platforms/KNX.md | 71 +++++++++++++++++++++---------- 2 files changed, 134 insertions(+), 27 deletions(-) diff --git a/accessories/knxdevice.js b/accessories/knxdevice.js index db93fd4..f94f5b2 100644 --- a/accessories/knxdevice.js +++ b/accessories/knxdevice.js @@ -322,6 +322,21 @@ KNXDevice.prototype = { this.knxwrite(callback, gaddress,'DPT5',numericValue); } }, + setInt: function(value, callback, context, gaddress) { + if (context === 'fromKNXBus') { + this.log("event ping pong, exit!"); + if (callback) { + callback(); + } + } else { + var numericValue = 0; + if (value && value>=0 && value<=255) { + numericValue = value; // assure 1..255 for KNX bus + } + this.log("Setting "+gaddress+" int to %s (%s)", value, numericValue); + this.knxwrite(callback, gaddress,'DPT5',numericValue); + } + }, setFloat: function(value, callback, context, gaddress) { if (context === 'fromKNXBus') { this.log(gaddress + " event ping pong, exit!"); @@ -406,6 +421,11 @@ KNXDevice.prototype = { this.setFloat(value, callback, context, config.Set); }.bind(this)); break; + case "Int": + myCharacteristic.on('set', function(value, callback, context) { + this.setInt(value, callback, context, config.Set); + }.bind(this)); + break; case "HVAC": myCharacteristic.on('set', function(value, callback, context) { this.setHVACState(value, callback, context, config.Set); @@ -432,6 +452,10 @@ KNXDevice.prototype = { case "Float": this.knxregister_float([config.Set].concat(config.Listen || []), myCharacteristic); break; + case "Int": + // use float as return type for ints, for we don't care + this.knxregister_float([config.Set].concat(config.Listen || []), myCharacteristic); + break; case "HVAC": this.knxregister_HVAC([config.Set].concat(config.Listen || []), myCharacteristic); break; @@ -498,6 +522,62 @@ KNXDevice.prototype = { } return myService; }, + getGarageDoorOpenerService: function(config) { +// // Required Characteristics +// this.addCharacteristic(Characteristic.CurrentDoorState); +// this.addCharacteristic(Characteristic.TargetDoorState); +// this.addCharacteristic(Characteristic.ObstructionDetected); +// Characteristic.CurrentDoorState.OPEN = 0; +// Characteristic.CurrentDoorState.CLOSED = 1; +// Characteristic.CurrentDoorState.OPENING = 2; +// Characteristic.CurrentDoorState.CLOSING = 3; +// Characteristic.CurrentDoorState.STOPPED = 4; +// // +// // Optional Characteristics +// this.addOptionalCharacteristic(Characteristic.LockCurrentState); +// this.addOptionalCharacteristic(Characteristic.LockTargetState); + // The value property of LockCurrentState must be one of the following: +// Characteristic.LockCurrentState.UNSECURED = 0; +// Characteristic.LockCurrentState.SECURED = 1; +// Characteristic.LockCurrentState.JAMMED = 2; +// Characteristic.LockCurrentState.UNKNOWN = 3; + + // some sanity checks + if (config.type !== "GarageDoorOpener") { + this.log("[ERROR] GarageDoorOpener Service for non 'GarageDoorOpener' service called"); + return undefined; + } + if (!config.name) { + this.log("[ERROR] GarageDoorOpener Service without 'name' property called"); + return undefined; + } + + var myService = new Service.GarageDoorOpener(config.name,config.name); + if (config.CurrentDoorState) { + this.log("GarageDoorOpener CurrentDoorState characteristic enabled"); + this.bindCharacteristic(myService, Characteristic.CurrentDoorState, "Int", config.CurrentDoorState); + } + if (config.TargetDoorState) { + this.log("GarageDoorOpener TargetDoorState characteristic enabled"); + this.bindCharacteristic(myService, Characteristic.TargetDoorState, "Int", config.TargetDoorState); + } + if (config.ObstructionDetected) { + this.log("GarageDoorOpener ObstructionDetected characteristic enabled"); + this.bindCharacteristic(myService, Characteristic.ObstructionDetected, "Bool", config.ObstructionDetected); + } + //optionals + if (config.LockCurrentState) { + this.log("GarageDoorOpener LockCurrentState characteristic enabled"); + myService.addCharacteristic(Characteristic.LockCurrentState); + this.bindCharacteristic(myService, Characteristic.LockCurrentState, "Int", config.LockCurrentState); + } + if (config.LockTargetState) { + this.log("GarageDoorOpener LockTargetState characteristic enabled"); + myService.addCharacteristic(Characteristic.LockTargetState); + this.bindCharacteristic(myService, Characteristic.LockTargetState, "Bool", config.LockTargetState); + } + return myService; + }, getLightbulbService: function(config) { // some sanity checks //this.config = config; @@ -657,11 +737,6 @@ KNXDevice.prototype = { // TargetTemperature if available if (config.TargetTemperature) { this.log("Thermostat TargetTemperature characteristic enabled"); - - // DEBUG - console.log("default value: " + myService.getCharacteristic(Characteristic.TargetTemperature).value); - // DEBUG - // default boundary too narrow for thermostats myService.getCharacteristic(Characteristic.TargetTemperature).minimumValue=0; // °C myService.getCharacteristic(Characteristic.TargetTemperature).maximumValue=40; // °C @@ -790,7 +865,7 @@ KNXDevice.prototype = { informationService .setCharacteristic(Characteristic.Manufacturer, "Opensource Community") .setCharacteristic(Characteristic.Model, "KNX Universal Device") - .setCharacteristic(Characteristic.SerialNumber, "Version 1.1"); + .setCharacteristic(Characteristic.SerialNumber, "Version 1.1.2"); accessoryServices.push(informationService); @@ -814,6 +889,9 @@ KNXDevice.prototype = { case "ContactSensor": accessoryServices.push(this.getContactSenserService(configService)); break; + case "GarageDoorOpener": + accessoryServices.push(this.getGarageDoorOpenerService(configService)); + break; case "Lightbulb": accessoryServices.push(this.getLightbulbService(configService)); break; diff --git a/platforms/KNX.md b/platforms/KNX.md index 937af45..2eba871 100644 --- a/platforms/KNX.md +++ b/platforms/KNX.md @@ -71,47 +71,76 @@ Two kinds of addresses are supported: `"Set":"1/2/3"` is a writable group addres # Supported Services and their characteristics ## ContactSensor -- ContactSensorState: DPT 1, 0 as contact **OR** -- ContactSensorStateContact1: DPT 1, 1 as contact +- ContactSensorState: DPT 1.002, 0 as contact **OR** +- ContactSensorStateContact1: DPT 1.002, 1 as contact + +- StatusActive: DPT 1.011, 1 as true +- StatusFault: DPT 1.011, 1 as true +- StatusTampered: DPT 1.011, 1 as true +- StatusLowBattery: DPT 1.011, 1 as true + +## GarageDoorOpener +- CurrentDoorState: DPT5 integer value in range 0..4 + // Characteristic.CurrentDoorState.OPEN = 0; + // Characteristic.CurrentDoorState.CLOSED = 1; + // Characteristic.CurrentDoorState.OPENING = 2; + // Characteristic.CurrentDoorState.CLOSING = 3; + // Characteristic.CurrentDoorState.STOPPED = 4; + +- TargetDoorState: DPT5 integer value in range 0..1 + // Characteristic.TargetDoorState.OPEN = 0; + // Characteristic.TargetDoorState.CLOSED = 1; + +- ObstructionDetected: DPT1, 1 as true + +- LockCurrentState: DPT5 integer value in range 0..3 + // Characteristic.LockCurrentState.UNSECURED = 0; + // Characteristic.LockCurrentState.SECURED = 1; + // Characteristic.LockCurrentState.JAMMED = 2; + // Characteristic.LockCurrentState.UNKNOWN = 3; + +- LockTargetState: DPT5 integer value in range 0..1 + // Characteristic.LockTargetState.UNSECURED = 0; + // Characteristic.LockTargetState.SECURED = 1; + -- StatusActive: DPT 1, 1 as true -- StatusFault: DPT 1, 1 as true -- StatusTampered: DPT 1, 1 as true -- StatusLowBattery: DPT 1, 1 as true ## Lightbulb - - On: DPT 1, 1 as on, 0 as off - - Brightness: DPT5 percentage, 100% (=255) the brightest + - On: DPT 1.001, 1 as on, 0 as off + - Brightness: DPT5.001 percentage, 100% (=255) the brightest ## LightSensor -- CurrentAmbientLightLevel: DPT 9, 0 to 100000 Lux +- CurrentAmbientLightLevel: DPT 9.004, 0 to 100000 Lux -## LockMechanism +## LockMechanism (This is poorly mapped!) - LockCurrentState: DPT 1, 1 as secured **OR (but not both:)** - LockCurrentStateSecured0: DPT 1, 0 as secured - LockTargetState: DPT 1, 1 as secured **OR** - LockTargetStateSecured0: DPT 1, 0 as secured +*ToDo here: correction of mappings, HomeKit reqires lock states UNSECURED=0, SECURED=1, JAMMED = 2, UNKNOWN=3* + + ## Outlet - - On: DPT 1, 1 as on, 0 as off - - OutletInUse: DPT 1, 1 as on, 0 as off + - On: DPT 1.001, 1 as on, 0 as off + - OutletInUse: DPT 1.011, 1 as on, 0 as off ## Switch - - On: DPT 1, 1 as on, 0 as off + - On: DPT 1.001, 1 as on, 0 as off ## TemperatureSensor -- CurrentTemperature: DPT9 in °C [listen only] +- CurrentTemperature: DPT9.001 in °C [listen only] ## Thermostat -- CurrentTemperature: DPT9 in °C [listen only] -- TargetTemperature: DPT9, values 0..40°C only, all others are ignored -- CurrentHeatingCoolingState: DPT5 HVAC, because of the incompatible mapping only off and heating (=auto) are shown, [listen only] -- TargetHeatingCoolingState: as above +- CurrentTemperature: DPT9.001 in °C [listen only] +- 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] +- TargetHeatingCoolingState: DPT20.102 HVAC, as above ## Window -- CurrentPosition: DPT5 percentage -- TargetPosition: DPT5 percentage -- PositionState: DPT5 value [listen only] +- CurrentPosition: DPT5.001 percentage +- TargetPosition: DPT5.001 percentage +- PositionState: DPT5.005 value [listen only: 0 Increasing, 1 Decreasing, 2 Stopped] ## WindowCovering - CurrentPosition: DPT5 percentage