diff --git a/accessories/knxdevice.js b/accessories/knxdevice.js index 824b9dd..b554b9a 100644 --- a/accessories/knxdevice.js +++ b/accessories/knxdevice.js @@ -1,13 +1,16 @@ -/* +/** * This is a KNX universal accessory shim. * This is NOT the version for dynamic installation * New 2015-09-16: Welcome iOS9.0 -new features includ: -services: -Window -WindowCovering -ContactSensor +new features include: +- services: +- Window +- WindowCovering +- ContactSensor +New 2015-0918: +- Services Switch and Outlet +- Code cleanup * */ var Service = require("HAP-NodeJS").Service; @@ -102,7 +105,6 @@ KNXDevice.prototype = { }.bind(this)); }.bind(this)); }, - // issues an all purpose read request on the knx bus // DOES NOT WAIT for an answer. Please register the address with a callback using registerGA() function knxread: function(groupAddress){ @@ -132,7 +134,6 @@ KNXDevice.prototype = { }.bind(this)); }.bind(this)); }, - // issuing multiple read requests at once knxreadarray: function (groupAddresses) { if (groupAddresses.constructor.toString().indexOf("Array") > -1) { @@ -147,7 +148,9 @@ KNXDevice.prototype = { this.knxread (groupAddresses); } }, - +/** Write special type routines + * + */ // special types knxwrite_percent: function(callback, groupAddress, value) { var numericValue = 0; @@ -159,10 +162,9 @@ KNXDevice.prototype = { } this.knxwrite(callback, groupAddress,'DPT5',numericValue); }, - - - // need to spit registers into types - +/** Registering routines + * + */ // boolean: get 0 or 1 from the bus, write boolean knxregister_bool: function(addresses, characteristic) { this.log("knx registering BOOLEAN " + addresses); @@ -201,7 +203,6 @@ KNXDevice.prototype = { } }.bind(this)); }, - // float knxregister_float: function(addresses, characteristic) { this.log("knx registering FLOAT " + addresses); @@ -216,8 +217,6 @@ KNXDevice.prototype = { }.bind(this)); }, - - // what about HVAC heating cooling types? knxregister_HVAC: function(addresses, characteristic) { this.log("knx registering HVAC " + addresses); knxd_registerGA(addresses, function(val, src, dest, type){ @@ -245,7 +244,7 @@ KNXDevice.prototype = { characteristic.setValue(HAPvalue, undefined, 'fromKNXBus'); }.bind(this)); }, - // to do! KNX: DPT 20.102 = One Byte like DPT5 + /** KNX HVAC (heating, ventilation, and air conditioning) types do not really match to homekit types: // 0 = Auto // 1 = Comfort // 2 = Standby @@ -257,8 +256,8 @@ KNXDevice.prototype = { // Characteristic.TargetHeatingCoolingState.HEAT = 1; // Characteristic.TargetHeatingCoolingState.COOL = 2; // Characteristic.TargetHeatingCoolingState.AUTO = 3; - - + AUTO (3) is not allowed as return type from devices! +*/ // undefined, has to match! knxregister: function(addresses, characteristic) { this.log("knx registering " + addresses); @@ -268,14 +267,14 @@ KNXDevice.prototype = { }.bind(this)); }, - /* - * set methods used for creating callbacks, such as - * var Characteristic = myService.addCharacteristic(new Characteristic.Brightness()) - * .on('set', function(value, callback, context) { - * this.setPercentage(value, callback, context, this.config[index].Set) - * }.bind(this)); - * - */ +/** set methods used for creating callbacks + * such as + * var Characteristic = myService.addCharacteristic(new Characteristic.Brightness()) + * .on('set', function(value, callback, context) { + * this.setPercentage(value, callback, context, this.config[index].Set) + * }.bind(this)); + * + */ setBooleanState: function(value, callback, context, gaddress) { if (context === 'fromKNXBus') { this.log(gaddress + " event ping pong, exit!"); @@ -308,7 +307,6 @@ KNXDevice.prototype = { } }, - setPercentage: function(value, callback, context, gaddress) { if (context === 'fromKNXBus') { this.log("event ping pong, exit!"); @@ -324,7 +322,6 @@ KNXDevice.prototype = { this.knxwrite(callback, gaddress,'DPT5',numericValue); } }, - setFloat: function(value, callback, context, gaddress) { if (context === 'fromKNXBus') { this.log(gaddress + " event ping pong, exit!"); @@ -340,7 +337,6 @@ KNXDevice.prototype = { this.knxwrite(callback, gaddress,'DPT9',numericValue); } }, - setHVACState: function(value, callback, context, gaddress) { if (context === 'fromKNXBus') { this.log(gaddress + " event ping pong, exit!"); @@ -371,21 +367,16 @@ KNXDevice.prototype = { } }, - - +/** identify dummy + * + */ identify: function(callback) { this.log("Identify requested!"); callback(); // success }, - - - /* - * function getXXXXXXXService(config) - * - * returns a configured service object to the caller (accessory/device) - * - */ - +/** bindCharacteristic + * initializes callbacks for 'set' events (from HK) and for KNX bus reads (to HK) + */ bindCharacteristic: function(myService, characteristicType, valueType, config) { var myCharacteristic = myService.getCharacteristic(characteristicType); if (myCharacteristic === undefined) { @@ -453,7 +444,60 @@ KNXDevice.prototype = { } return myCharacteristic; // for chaining or whatsoever }, - +/** + * function getXXXXXXXService(config) + * returns a configured service object to the caller (accessory/device) + * + * @param config + * pass a configuration array parsed from config.json + * specifically for this service + * + */ + getContactSenserService: function(config) { +// Characteristic.ContactSensorState.CONTACT_DETECTED = 0; +// Characteristic.ContactSensorState.CONTACT_NOT_DETECTED = 1; + + // some sanity checks + if (config.type !== "ContactSensor") { + this.log("[ERROR] ContactSensor Service for non 'ContactSensor' service called"); + return undefined; + } + if (!config.name) { + this.log("[ERROR] ContactSensor Service without 'name' property called"); + return undefined; + } + + var myService = new Service.ContactSensor(config.name,config.name); + if (config.ContactSensorState) { + this.log("ContactSensor ContactSensorState characteristic enabled"); + this.bindCharacteristic(myService, Characteristic.ContactSensorState, "Bool", config.ContactSensorState); + } else if (config.ContactSensorStateContact1) { + this.log("ContactSensor ContactSensorStateContact1 characteristic enabled"); + this.bindCharacteristic(myService, Characteristic.ContactSensorState, "BoolReverse", config.ContactSensorStateContact1); + } + //optionals + if (config.StatusActive) { + this.log("ContactSensor StatusActive characteristic enabled"); + myService.addCharacteristic(Characteristic.StatusActive); + this.bindCharacteristic(myService, Characteristic.StatusActive, "Bool", config.StatusActive); + } + if (config.StatusFault) { + this.log("ContactSensor StatusFault characteristic enabled"); + myService.addCharacteristic(Characteristic.StatusFault); + this.bindCharacteristic(myService, Characteristic.StatusFault, "Bool", config.StatusFault); + } + if (config.StatusTampered) { + this.log("ContactSensor StatusTampered characteristic enabled"); + myService.addCharacteristic(Characteristic.StatusTampered); + this.bindCharacteristic(myService, Characteristic.StatusTampered, "Bool", config.StatusTampered); + } + if (config.StatusLowBattery) { + this.log("ContactSensor StatusLowBattery characteristic enabled"); + myService.addCharacteristic(Characteristic.StatusLowBattery); + this.bindCharacteristic(myService, Characteristic.StatusLowBattery, "Bool", config.StatusLowBattery); + } + return myService; + }, getLightbulbService: function(config) { // some sanity checks //this.config = config; @@ -482,13 +526,13 @@ KNXDevice.prototype = { //iterate(myService); return myService; }, - getLockMechanismService: function(config) { - // some sanity checks - //this.config = config; + +/** //this.config = config; // Characteristic.LockCurrentState.UNSECURED = 0; // Characteristic.LockCurrentState.SECURED = 1; - +*/ + // some sanity checks if (config.type !== "LockMechanism") { this.log("[ERROR] LockMechanism Service for non 'LockMechanism' service called"); return undefined; @@ -497,6 +541,7 @@ KNXDevice.prototype = { this.log("[ERROR] LockMechanism Service without 'name' property called"); return undefined; } + var myService = new Service.LockMechanism(config.name,config.name); // LockCurrentState if (config.LockCurrentState) { @@ -520,28 +565,61 @@ KNXDevice.prototype = { //iterate(myService); return myService; }, - + getOutletService: function(config) { + /** + * this.addCharacteristic(Characteristic.On); + * this.addCharacteristic(Characteristic.OutletInUse); + */ + // some sanity checks + if (config.type !== "Outlet") { + this.log("[ERROR] Outlet Service for non 'Outlet' service called"); + return undefined; + } + if (!config.name) { + this.log("[ERROR] Outlet Service without 'name' property called"); + return undefined; + } + var myService = new Service.Outlet(config.name,config.name); + // On (and Off) + if (config.On) { + this.log("Outlet on/off characteristic enabled"); + this.bindCharacteristic(myService, Characteristic.On, "Bool", config.On); + } // OutletInUse characteristic + if (config.OutletInUse) { + this.log("Outlet on/off characteristic enabled"); + this.bindCharacteristic(myService, Characteristic.OutletInUse, "Bool", config.OutletInUse); + } + return myService; + }, + getSwitchService: function(config) { + // some sanity checks + if (config.type !== "Switch") { + this.log("[ERROR] Switch Service for non 'Switch' service called"); + return undefined; + } + if (!config.name) { + this.log("[ERROR] Switch Service without 'name' property called"); + return undefined; + } + var myService = new Service.Switch(config.name,config.name); + // On (and Off) + if (config.On) { + this.log("Switch on/off characteristic enabled"); + this.bindCharacteristic(myService, Characteristic.On, "Bool", config.On); + } // On characteristic + return myService; + }, getThermostatService: function(config) { - - -// // Required Characteristics -// this.addCharacteristic(Characteristic.CurrentHeatingCoolingState); -// this.addCharacteristic(Characteristic.TargetHeatingCoolingState); -// this.addCharacteristic(Characteristic.CurrentTemperature); //check -// this.addCharacteristic(Characteristic.TargetTemperature); // -// this.addCharacteristic(Characteristic.TemperatureDisplayUnits); - // -// // Optional Characteristics -// this.addOptionalCharacteristic(Characteristic.CurrentRelativeHumidity); -// this.addOptionalCharacteristic(Characteristic.TargetRelativeHumidity); -// this.addOptionalCharacteristic(Characteristic.CoolingThresholdTemperature); -// this.addOptionalCharacteristic(Characteristic.HeatingThresholdTemperature); - +/** + // Optional Characteristics + this.addOptionalCharacteristic(Characteristic.CurrentRelativeHumidity); + this.addOptionalCharacteristic(Characteristic.TargetRelativeHumidity); + this.addOptionalCharacteristic(Characteristic.CoolingThresholdTemperature); + this.addOptionalCharacteristic(Characteristic.HeatingThresholdTemperature); +*/ // some sanity checks - - if (config.type !== "Thermostat") { this.log("[ERROR] Thermostat Service for non 'Thermostat' service called"); return undefined; @@ -550,6 +628,7 @@ KNXDevice.prototype = { this.log("[ERROR] Thermostat Service without 'name' property called"); return undefined; } + var myService = new Service.Thermostat(config.name,config.name); // CurrentTemperature) if (config.CurrentTemperature) { @@ -581,15 +660,9 @@ KNXDevice.prototype = { } return myService; }, - - // temperature sensor type (iOS9 assumed) getTemperatureSensorService: function(config) { - - // some sanity checks - - if (config.type !== "TemperatureSensor") { this.log("[ERROR] TemperatureSensor Service for non 'TemperatureSensor' service called"); return undefined; @@ -606,28 +679,18 @@ KNXDevice.prototype = { } return myService; }, - - - - // window type (iOS9 assumed) getWindowService: function(config) { -// Service.Window = function(displayName, subtype) { -// Service.call(this, displayName, '0000008B-0000-1000-8000-0026BB765291', subtype); -// -// // Required Characteristics -// this.addCharacteristic(Characteristic.CurrentPosition); -// this.addCharacteristic(Characteristic.TargetPosition); -// this.addCharacteristic(Characteristic.PositionState); -// -// // Optional Characteristics -// this.addOptionalCharacteristic(Characteristic.HoldPosition); -// this.addOptionalCharacteristic(Characteristic.ObstructionDetected); -// this.addOptionalCharacteristic(Characteristic.Name); - - // Characteristic.PositionState.DECREASING = 0; -// Characteristic.PositionState.INCREASING = 1; -// Characteristic.PositionState.STOPPED = 2; - +/** + Optional Characteristics + this.addOptionalCharacteristic(Characteristic.HoldPosition); + this.addOptionalCharacteristic(Characteristic.ObstructionDetected); + this.addOptionalCharacteristic(Characteristic.Name); + + PositionState values: The KNX blind actuators I have return only MOVING=1 and STOPPED=0 + Characteristic.PositionState.DECREASING = 0; + Characteristic.PositionState.INCREASING = 1; + Characteristic.PositionState.STOPPED = 2; +*/ // some sanity checks @@ -656,34 +719,17 @@ KNXDevice.prototype = { } return myService; }, - - -// /** -// * Service "Window Covering" -// */ -// -// Service.WindowCovering = function(displayName, subtype) { -// Service.call(this, displayName, '0000008C-0000-1000-8000-0026BB765291', subtype); -// -// // Required Characteristics -// this.addCharacteristic(Characteristic.CurrentPosition); -// this.addCharacteristic(Characteristic.TargetPosition); -// this.addCharacteristic(Characteristic.PositionState); -// -// // Optional Characteristics -// this.addOptionalCharacteristic(Characteristic.HoldPosition); -// this.addOptionalCharacteristic(Characteristic.TargetHorizontalTiltAngle); -// this.addOptionalCharacteristic(Characteristic.TargetVerticalTiltAngle); -// this.addOptionalCharacteristic(Characteristic.CurrentHorizontalTiltAngle); -// this.addOptionalCharacteristic(Characteristic.CurrentVerticalTiltAngle); -// this.addOptionalCharacteristic(Characteristic.ObstructionDetected); -// this.addOptionalCharacteristic(Characteristic.Name); -// }; getWindowCoveringService: function(config) { - + /** + // Optional Characteristics + this.addOptionalCharacteristic(Characteristic.HoldPosition); + this.addOptionalCharacteristic(Characteristic.TargetHorizontalTiltAngle); + this.addOptionalCharacteristic(Characteristic.TargetVerticalTiltAngle); + this.addOptionalCharacteristic(Characteristic.CurrentHorizontalTiltAngle); + this.addOptionalCharacteristic(Characteristic.CurrentVerticalTiltAngle); + this.addOptionalCharacteristic(Characteristic.ObstructionDetected); + */ // some sanity checks - - if (config.type !== "WindowCovering") { this.log("[ERROR] WindowCovering Service for non 'WindowCovering' service called"); return undefined; @@ -692,8 +738,8 @@ KNXDevice.prototype = { this.log("[ERROR] WindowCovering Service without 'name' property called"); return undefined; } - var myService = new Service.WindowCovering(config.name,config.name); + var myService = new Service.WindowCovering(config.name,config.name); if (config.CurrentPosition) { this.log("WindowCovering CurrentPosition characteristic enabled"); this.bindCharacteristic(myService, Characteristic.CurrentPosition, "Percent", config.CurrentPosition); @@ -709,64 +755,6 @@ KNXDevice.prototype = { return myService; }, -// Service.ContactSensor = function(displayName, subtype) { -// Service.call(this, displayName, '00000080-0000-1000-8000-0026BB765291', subtype); -// -// // Required Characteristics -// this.addCharacteristic(Characteristic.ContactSensorState); -// -// // Optional Characteristics -// this.addOptionalCharacteristic(Characteristic.StatusActive); -// this.addOptionalCharacteristic(Characteristic.StatusFault); -// this.addOptionalCharacteristic(Characteristic.StatusTampered); -// this.addOptionalCharacteristic(Characteristic.StatusLowBattery); -// this.addOptionalCharacteristic(Characteristic.Name); -// }; -// Characteristic.ContactSensorState.CONTACT_DETECTED = 0; -// Characteristic.ContactSensorState.CONTACT_NOT_DETECTED = 1; - getContactSenserService: function(config) { - // some sanity checks - if (config.type !== "ContactSensor") { - this.log("[ERROR] ContactSensor Service for non 'ContactSensor' service called"); - return undefined; - } - if (!config.name) { - this.log("[ERROR] ContactSensor Service without 'name' property called"); - return undefined; - } - var myService = new Service.ContactSensor(config.name,config.name); - - if (config.ContactSensorState) { - this.log("ContactSensor ContactSensorState characteristic enabled"); - this.bindCharacteristic(myService, Characteristic.ContactSensorState, "Bool", config.ContactSensorState); - } else if (config.ContactSensorStateContact1) { - this.log("ContactSensor ContactSensorStateContact1 characteristic enabled"); - this.bindCharacteristic(myService, Characteristic.ContactSensorState, "BoolReverse", config.ContactSensorStateContact1); - } - //optionals - if (config.StatusActive) { - this.log("ContactSensor StatusActive characteristic enabled"); - myService.addCharacteristic(Characteristic.StatusActive); - this.bindCharacteristic(myService, Characteristic.StatusActive, "Bool", config.StatusActive); - } - if (config.StatusFault) { - this.log("ContactSensor StatusFault characteristic enabled"); - myService.addCharacteristic(Characteristic.StatusFault); - this.bindCharacteristic(myService, Characteristic.StatusFault, "Bool", config.StatusFault); - } - if (config.StatusTampered) { - this.log("ContactSensor StatusTampered characteristic enabled"); - myService.addCharacteristic(Characteristic.StatusTampered); - this.bindCharacteristic(myService, Characteristic.StatusTampered, "Bool", config.StatusTampered); - } - if (config.StatusLowBattery) { - this.log("ContactSensor StatusLowBattery characteristic enabled"); - myService.addCharacteristic(Characteristic.StatusLowBattery); - this.bindCharacteristic(myService, Characteristic.StatusLowBattery, "Bool", config.StatusLowBattery); - } - return myService; - }, - /* assemble the device ***************************************************************************************************/ @@ -788,8 +776,8 @@ KNXDevice.prototype = { accessoryServices.push(informationService); - iterate(this.config); -// throw new Error("STOP"); + //iterate(this.config); + if (!this.config.services){ this.log("No services found in accessory?!") } @@ -814,6 +802,9 @@ KNXDevice.prototype = { case "LockMechanism": accessoryServices.push(this.getLockMechanismService(configService)); break; + case "Switch": + accessoryServices.push(this.getSwitchService(configService)); + break; case "TemperatureSensor": accessoryServices.push(this.getTemperatureSensorService(configService)); break; diff --git a/platforms/KNX.md b/platforms/KNX.md index 22b3aee..6512835 100644 --- a/platforms/KNX.md +++ b/platforms/KNX.md @@ -19,19 +19,16 @@ You need to configure all devices directly in the config.json. "name": "Living Room North Lamp", "On": { "Set": "1/1/6", - "Listen": [ - "1/1/63" - ] + "Listen": ["1/1/63"] }, "Brightness": { "Set": "1/1/62", - "Listen": [ - "1/1/64" - ] + "Listen": ["1/1/64"] } } ] } + ] } ```` In the accessories section (the array within the brackets [ ]) you can insert as many objects as you like in the following form @@ -73,28 +70,42 @@ 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 + +- 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 ## LockMechanism -- LockCurrentState: DPT 1, 1 as secured **OR (but not both:)** +- LockCurrentState: DPT 1, 1 as secured **OR (but not both:)** - LockCurrentStateSecured0: DPT 1, 0 as secured -- LockTargetState: DPT 1, 1 as secured **OR** +- LockTargetState: DPT 1, 1 as secured **OR** - LockTargetStateSecured0: DPT 1, 0 as secured +## Outlet + - On: DPT 1, 1 as on, 0 as off + - OutletInUse: DPT 1, 1 as on, 0 as off + +## Switch + - On: DPT 1, 1 as on, 0 as off + +## TemperatureSensor +- CurrentTemperature: DPT9 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 - -## TemperatureSensor -- CurrentTemperature: DPT9 in °C [listen only] - ## Window - CurrentPosition: DPT5 percentage - TargetPosition: DPT5 percentage @@ -113,16 +124,9 @@ Two kinds of addresses are supported: `"Set":"1/2/3"` is a writable group addres - CurrentVerticalTiltAngle - ObstructionDetected -## ContactSensor -- ContactSensorState: DPT 1, 0 as contact **OR** -- ContactSensorStateContact1: DPT 1, 1 as contact -- StatusActive: DPT 1, 1 as true -- StatusFault: DPT 1, 1 as true -- StatusTampered: DPT 1, 1 as true -- StatusLowBattery: DPT 1, 1 as true # DISCLAIMER -This is work in progress! +**This is work in progress!**