diff --git a/accessories/Alarm.js b/accessories/Alarm.js index 714b96e..f4befe4 100644 --- a/accessories/Alarm.js +++ b/accessories/Alarm.js @@ -1,5 +1,7 @@ var Service, Characteristic, Command, ExecutionState, State, AbstractAccessory; +var sharedCurrentState, sharedTargetState, initCurrentState, initTargetState; + module.exports = function(homebridge, abstractAccessory, api) { AbstractAccessory = abstractAccessory; Service = homebridge.hap.Service; @@ -21,13 +23,17 @@ Alarm = function(log, api, device, config) { this.stayZones = config.STAY_ARM || 'A'; this.nightZones = config.NIGHT_ARM || 'B'; - var service = new Service.SecuritySystem(device.label); - - this.currentState = service.getCharacteristic(Characteristic.SecuritySystemCurrentState); - this.targetState = service.getCharacteristic(Characteristic.SecuritySystemTargetState) - this.targetState.on('set', this.setState.bind(this)); - - this.services.push(service); + var service = new Service.SecuritySystem(device.label); + this.currentState = service.getCharacteristic(Characteristic.SecuritySystemCurrentState); + this.targetState = service.getCharacteristic(Characteristic.SecuritySystemTargetState); + this.targetState.on('set', this.setState.bind(this)); + + // Store a static shared state for splited alarm component + if(this.device.widget == 'StatelessAlarmController') { + this.currentState.updateValue(Characteristic.SecuritySystemCurrentState.DISARMED); + this.targetState.updateValue(Characteristic.SecuritySystemTargetState.DISARM); + } + this.services.push(service); }; Alarm.UUID = 'Alarm'; @@ -40,32 +46,32 @@ Alarm.prototype = { setState: function(value, callback) { var that = this; var command = null; - switch(value) { - default: - case Characteristic.SecuritySystemTargetState.STAY_ARM: - command = new Command('alarmZoneOn'); - command.parameters = [this.stayZones]; - break; - case Characteristic.SecuritySystemTargetState.NIGHT_ARM: - command = new Command('alarmZoneOn'); - command.parameters = [this.nightZones]; - break; - case Characteristic.SecuritySystemTargetState.AWAY_ARM: - command = new Command('alarmOn'); - break; - case Characteristic.SecuritySystemTargetState.DISARM: - command = new Command('alarmOff'); - break; - } + switch(value) { + default: + case Characteristic.SecuritySystemTargetState.STAY_ARM: + command = new Command('alarmZoneOn'); + command.parameters = [this.stayZones]; + break; + case Characteristic.SecuritySystemTargetState.NIGHT_ARM: + command = new Command('alarmZoneOn'); + command.parameters = [this.nightZones]; + break; + case Characteristic.SecuritySystemTargetState.AWAY_ARM: + command = new Command('alarmOn'); + break; + case Characteristic.SecuritySystemTargetState.DISARM: + command = new Command('alarmOff'); + break; + } this.executeCommand(command, function(status, error, data) { switch (status) { case ExecutionState.INITIALIZED: callback(error); break; case ExecutionState.COMPLETED: - if(that.device.widget == 'StatelessAlarmController') { // If stateless alarm, update target immediately - that.currentState.updateValue(value); - } + if(that.device.widget == 'StatelessAlarmController') { + this.currentState.updateValue(value); + } break; case ExecutionState.FAILED: // Restore current state as target @@ -78,22 +84,42 @@ Alarm.prototype = { }, onStateUpdate: function(name, value) { - //this.log.debug('['+this.name+'] ' + name + '=' + value); // For analysis + //this.log('['+this.name+'] ' + name + '=' + value); // For analysis if (name == State.STATE_ACTIVE_ZONES) { var converted = null; var target = null; switch(value) { - default: - case '': target = converted = Characteristic.SecuritySystemCurrentState.DISARMED; break; - case 'A': target = converted = Characteristic.SecuritySystemCurrentState.STAY_ARM; break; - case 'A,B,C': target = converted = Characteristic.SecuritySystemCurrentState.AWAY_ARM; break; - case 'B': target = converted = Characteristic.SecuritySystemCurrentState.NIGHT_ARM; break; - case 'triggered': converted = Characteristic.SecuritySystemCurrentState.ALARM_TRIGGERED; break; - } + default: + case '': target = converted = Characteristic.SecuritySystemCurrentState.DISARMED; break; + case this.stayZones: target = converted = Characteristic.SecuritySystemCurrentState.STAY_ARM; break; + case 'A,B,C': target = converted = Characteristic.SecuritySystemCurrentState.AWAY_ARM; break; + case this.nightZones: target = converted = Characteristic.SecuritySystemCurrentState.NIGHT_ARM; break; + case 'triggered': converted = Characteristic.SecuritySystemCurrentState.ALARM_TRIGGERED; break; + } this.currentState.updateValue(converted); if (!this.isCommandInProgress()) // if no command running, update target this.targetState.updateValue(target); + } else if (name == 'internal:CurrentAlarmModeState') { + var converted = null; + switch(value) { + default: + case 'off': converted = Characteristic.SecuritySystemCurrentState.DISARMED; break; + case 'partial1': converted = Characteristic.SecuritySystemCurrentState.STAY_ARM; break; + case 'total': converted = Characteristic.SecuritySystemCurrentState.AWAY_ARM; break; + case 'partial2': converted = Characteristic.SecuritySystemCurrentState.NIGHT_ARM; break; + } + this.currentState.updateValue(converted); + } else if (name == 'internal:TargetAlarmModeState') { + var converted = null; + switch(value) { + default: + case 'off': converted = Characteristic.SecuritySystemTargetState.DISARM; break; + case 'partial1': converted = Characteristic.SecuritySystemTargetState.STAY_ARM; break; + case 'total': converted = Characteristic.SecuritySystemTargetState.AWAY_ARM; break; + case 'partial2': converted = Characteristic.SecuritySystemTargetState.NIGHT_ARM; break; + } + this.targetState.updateValue(converted); } } } \ No newline at end of file diff --git a/accessories/ElectricitySensor.js b/accessories/ElectricitySensor.js new file mode 100644 index 0000000..f0b4021 --- /dev/null +++ b/accessories/ElectricitySensor.js @@ -0,0 +1,93 @@ +var Service, Characteristic, Command, ExecutionState, State, AbstractAccessory; + +var PowerConsumption, EnergyConsumption; + +var inherits = function (ctor, superCtor) { + + if (ctor === undefined || ctor === null) + throw new TypeError('The constructor to "inherits" must not be ' + + 'null or undefined'); + + if (superCtor === undefined || superCtor === null) + throw new TypeError('The super constructor to "inherits" must not ' + + 'be null or undefined'); + + if (superCtor.prototype === undefined) + throw new TypeError('The super constructor to "inherits" must ' + + 'have a prototype'); + + ctor.super_ = superCtor; + Object.setPrototypeOf(ctor.prototype, superCtor.prototype); +} + +module.exports = function(homebridge, abstractAccessory, api) { + AbstractAccessory = abstractAccessory; + Service = homebridge.hap.Service; + Characteristic = homebridge.hap.Characteristic; + Command = api.Command; + ExecutionState = api.ExecutionState; + State = api.State; + + makeCharacteristics(); + + return ElectricitySensor; +} + +/** + * Accessory "ElectricitySensor" + */ + +ElectricitySensor = function(log, api, device) { + AbstractAccessory.call(this, log, api, device); + var service = new Service.Outlet(device.label); + service.addCharacteristic(EnergyConsumption); + service.addCharacteristic(PowerConsumption); + + this.energyState = service.getCharacteristic(EnergyConsumption); + this.powerState = service.getCharacteristic(PowerConsumption); + + this.services.push(service); +}; + +ElectricitySensor.UUID = 'ElectricitySensor'; + +ElectricitySensor.prototype = { + + onStateUpdate: function(name, value) { + if (name == 'core:ElectricEnergyConsumptionState') { + this.energyState.updateValue(value); + } else if (name == 'core:ElectricPowerConsumptionState') { + this.powerState.updateValue(value); + } + } +} + +function makeCharacteristics() { + PowerConsumption = function() { + Characteristic.call(this, 'Current Consumption', 'E863F10D-079E-48FF-8F27-9C2605A29F52'); + this.setProps({ + format: Characteristic.Formats.INT, + maxValue: 65535, + minValue: 0, + minStep: 1, + unit: "W", + perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY] + }); + this.value = this.getDefaultValue(); + }; + inherits(PowerConsumption, Characteristic); + + EnergyConsumption = function() { + Characteristic.call(this, 'Total Consumption', 'E863F10C-079E-48FF-8F27-9C2605A29F52'); + this.setProps({ + format: Characteristic.Formats.FLOAT, + maxValue: 4294967295, + minValue: 0, + minStep: 0.01, + unit: "kWh", + perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY] + }); + this.value = this.getDefaultValue(); + }; + inherits(EnergyConsumption, Characteristic); +} \ No newline at end of file diff --git a/accessories/GarageDoor.js b/accessories/GarageDoor.js index 00a3f73..0bdf3e3 100644 --- a/accessories/GarageDoor.js +++ b/accessories/GarageDoor.js @@ -22,6 +22,8 @@ GarageDoor = function(log, api, device) { this.currentState = service.getCharacteristic(Characteristic.CurrentDoorState); this.targetState = service.getCharacteristic(Characteristic.TargetDoorState) if(this.device.widget == 'UpDownGarageDoor4T') { + this.currentState.updateValue(Characteristic.CurrentDoorState.CLOSED); + this.targetState.updateValue(Characteristic.TargetDoorState.CLOSED); this.targetState.on('set', this.cycle.bind(this)); } else { this.targetState.on('set', this.setState.bind(this)); @@ -94,16 +96,16 @@ GarageDoor.prototype = { var converted = null; var target = null; switch(value) { - case 'unknown': - case 'open' : - converted = Characteristic.CurrentDoorState.OPEN; - target = Characteristic.TargetDoorState.OPEN; - break; - case 'closed' : - converted = Characteristic.CurrentDoorState.CLOSED; - target = Characteristic.TargetDoorState.CLOSED; - break; - } + case 'unknown': + case 'open' : + converted = Characteristic.CurrentDoorState.OPEN; + target = Characteristic.TargetDoorState.OPEN; + break; + case 'closed' : + converted = Characteristic.CurrentDoorState.CLOSED; + target = Characteristic.TargetDoorState.CLOSED; + break; + } this.currentState.updateValue(converted); if (!this.isCommandInProgress()) // if no command running, update target diff --git a/accessories/Gate.js b/accessories/Gate.js index eb12916..497ea52 100644 --- a/accessories/Gate.js +++ b/accessories/Gate.js @@ -22,6 +22,8 @@ Gate = function(log, api, device) { this.currentState = service.getCharacteristic(Characteristic.CurrentDoorState); this.targetState = service.getCharacteristic(Characteristic.TargetDoorState) if(this.device.widget == 'OpenCloseGate4T') { + this.currentState.updateValue(Characteristic.CurrentDoorState.CLOSED); + this.targetState.updateValue(Characteristic.TargetDoorState.CLOSED); this.targetState.on('set', this.cycle.bind(this)); } else { this.targetState.on('set', this.setState.bind(this)); diff --git a/accessories/RollerShutter.js b/accessories/RollerShutter.js index de074d9..0b4fc1a 100644 --- a/accessories/RollerShutter.js +++ b/accessories/RollerShutter.js @@ -19,9 +19,6 @@ RollerShutter = function(log, api, device, config) { AbstractAccessory.call(this, log, api, device); var service = new Service.WindowCovering(device.label); - this.stayZones = config.STAY_ARM || 'A'; - this.nightZones = config.NIGHT_ARM || 'B'; - this.currentPosition = service.getCharacteristic(Characteristic.CurrentPosition); this.targetPosition = service.getCharacteristic(Characteristic.TargetPosition); if(this.device.widget == 'UpDownRollerShutter') { @@ -59,7 +56,7 @@ RollerShutter.prototype = { case ExecutionState.IN_PROGRESS: var newValue = (value == 100 || value > that.currentPosition.value) ? Characteristic.PositionState.INCREASING : Characteristic.PositionState.DECREASING; that.positionState.updateValue(newValue); - that.log('['+that.name+'] Command in progress, state='+newValue); + that.log.debug('['+that.name+'] Command in progress, state='+newValue); break; case ExecutionState.COMPLETED: case ExecutionState.FAILED: @@ -112,7 +109,7 @@ RollerShutter.prototype = { onStateUpdate: function(name, value) { if (name == State.STATE_CLOSURE) { - this.log('['+this.name+'] ' + name + '=' + value); // For analysis + this.log.debug('['+this.name+'] ' + name + '=' + value); // For analysis var converted = 100 - value; this.currentPosition.updateValue(converted); if (!this.isCommandInProgress()) // if no command running, update target diff --git a/index.js b/index.js index 8574b44..beda431 100644 --- a/index.js +++ b/index.js @@ -27,7 +27,7 @@ function TahomaPlatform(log, config, api) { this.exclusions = config.exclude || []; this.exclusions.push('internal'); // Exclude internal devices - this.api = new OverkizService.Api(log, config); + this.api = new OverkizService.Api(log, config); this.platformAccessories = []; @@ -48,27 +48,27 @@ TahomaPlatform.prototype = { this.log.info("Fetching accessories..."); if (that.platformAccessories.length == 0) { this.api.getDevices(function(error, data) { - if (!error) { - for (device of data) { - var accessory = null; - var protocol = device.controllableName.split(':').shift(); // Get device protocol name - var accessoryConfig = that.config[device.uiClass] || {}; - if(DeviceAccessory[device.uiClass] != null && that.exclusions.indexOf(protocol) == -1 && that.exclusions.indexOf(device.label) == -1) { - accessory = new DeviceAccessory[device.uiClass](that.log, that.api, device, accessoryConfig); - } else { - that.log.info('Device ' + device.uiClass + ' ignored'); - } - if(accessory != null) { - if(device.states != null) { - for (state of device.states) { - accessory.onStateUpdate(state.name, state.value); - } - } - that.platformAccessories.push(accessory); - } - } + if (!error) { + for (device of data) { + var accessory = null; + var protocol = device.controllableName.split(':').shift(); // Get device protocol name + var accessoryConfig = that.config[device.uiClass] || {}; + if(DeviceAccessory[device.uiClass] != null && that.exclusions.indexOf(protocol) == -1 && that.exclusions.indexOf(device.label) == -1) { + accessory = new DeviceAccessory[device.uiClass](that.log, that.api, device, accessoryConfig); + } else { + that.log.info('Device ' + device.uiClass + ' ignored'); + } + if(accessory != null) { + if(device.states != null) { + for (state of device.states) { + accessory.onStateUpdate(state.name, state.value); + } + } + that.platformAccessories.push(accessory); + } } - callback(that.platformAccessories); + } + callback(that.platformAccessories); }); } else { callback(this.platformAccessories); diff --git a/package.json b/package.json index e1c2d17..8adc56e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,8 @@ { "name": "homebridge-tahoma", - "version": "0.0.8", + "version": "0.1.0", "description": "Sample Platform plugin for TaHoma and Cozytouch services (Somfy,Atlantic,Thermor,Sauter): https://github.com/dubocr/homebridge-tahoma", + "author": "Romain DUBOC", "license": "ISC", "keywords": [ "homebridge-plugin" @@ -17,6 +18,7 @@ "node": ">=0.12.0", "homebridge": ">=0.2.0" }, + "homepage": "https://github.com/dubocr/homebridge-tahoma#readme", "dependencies": { "request": "^2.65.0", "polling-to-event": ">=2.0.2",