From 9922e28a3ca752dbaa65ba1a56033bf24535ed74 Mon Sep 17 00:00:00 2001 From: Joakim Arborelius Date: Wed, 14 Jun 2017 19:25:57 +0200 Subject: [PATCH 1/6] Basic documentation changes --- README.md | 72 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index a0cb30a..e13cdf8 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,37 @@ -# homebridge-verisure - -This is a plugin for [homebridge](https://github.com/nfarina/homebridge). It's a -working implementation for several Verisure devices: - -- [x] __HUMIDITY1__ - Temperature -- [x] __SIREN1__ - Temperature -- [x] __SMARTPLUG__ - State, on, off -- [x] __SMOKE2__ - Temperature -- [x] __VOICEBOX1__ - Temperature - -## Installation - -```bash -npm install -g homebridge-verisure -``` - -Now you can update your configuration file to enable the plugin, see sample -snippet below. - -## Configuration - -As part of your configuration, add an object with your Verisure credentials to -your array (list) of enabled platform plugins. - -```json -"platforms": [ - { - "platform" : "verisure", - "name" : "Verisure", - "email": "your@email.com", - "password": "yourT0p5ecre7Passw0rd" - } -] -``` +# homebridge-verisure + +This is a plugin for [homebridge](https://github.com/nfarina/homebridge). It's a +working implementation for several Verisure devices: + +- [x] __DOORLOCK__ - Yale Doorman Lock/Unlock +- [x] __HUMIDITY1__ - Temperature +- [x] __SIREN1__ - Temperature +- [x] __SMARTPLUG__ - State, on, off +- [x] __SMOKE2__ - Temperature +- [x] __VOICEBOX1__ - Temperature + +## Installation + +```bash +npm install -g homebridge-verisure +``` + +Now you can update your configuration file to enable the plugin, see sample +snippet below. + +## Configuration + +As part of your configuration, add an object with your Verisure credentials to +your array (list) of enabled platform plugins. + +```json +"platforms": [ + { + "platform" : "verisure", + "name" : "Verisure", + "email": "your@email.com", + "password": "yourT0p5ecre7Passw0rd", + "doorcode": "000000" + } +] +``` From e864d1891c7aa9470497aaf0312357712ba6827b Mon Sep 17 00:00:00 2001 From: Joakim Arborelius Date: Wed, 14 Jun 2017 19:37:51 +0200 Subject: [PATCH 2/6] Implementation code for Yale Doorman --- index.js | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 137 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 88edc87..9263a7c 100644 --- a/index.js +++ b/index.js @@ -13,6 +13,7 @@ const PLATFORM_NAME = 'verisure'; const MANUFACTURER = 'Verisure'; const DEVICE_TYPES = { + 'DOORLOCK': 'Yale Doorman', 'HUMIDITY1': 'Klimatdetektor', 'SIREN1': 'Siren', 'SMARTPLUG': 'Smart plug', @@ -24,9 +25,11 @@ let VERISURE_TOKEN = null; let VERISURE_INSTALLATION = null; let VERISURE_CALLS = {}; let VERISURE_DEVICE_NAMES = [] +let VERISURE_DOORLOCK_CODE = null; const getVerisureInstallation = function(config, callback) { + if (config && config.doorcode) VERISURE_DOORLOCK_CODE = config.doorcode; verisure.auth(config.email, config.password, function(err, token) { if(err) return callback(err); VERISURE_TOKEN = token; @@ -82,7 +85,6 @@ const VerisurePlatform = function(log, config, api) { const platform = this; this.log = log; this.config = config; - this.accessories = function(callback) { getVerisureInstallation(config, function(err) { if(err) return log.error(err); @@ -108,6 +110,19 @@ const VerisurePlatform = function(log, config, api) { }); })); + if (overview && overview.doorLockStatusList){ + let locks = overview.doorLockStatusList.map(function(device){ + return new VerisureAccessory(log, { + name: getUniqueName(`${device.area}`), + model: 'DOORLOCK', + serialNumber: device.deviceLabel, + value: device.lockedState==='LOCKED' ? 1 : 0, + category: 6 // Hardcoded from Accessory.Categories in Accessory.js of hap-nodejs + }); + }); + devices = devices.concat(locks); + } + callback(devices); }); }) @@ -123,6 +138,7 @@ const VerisureAccessory = function(log, config) { this.model = config.model; this.serialNumber = config.serialNumber; this.value = config.value; + this.service = null; } @@ -175,11 +191,116 @@ VerisureAccessory.prototype = { }, callback); }, + _getCurrentLockState: function(callback){ + this.log(`${this.name} (${this.serialNumber}): GETTING CURRENT LOCK STATE`); + verisure._apiClient({ + method: 'GET', + uri: `/installation/${VERISURE_INSTALLATION.giid}/doorlockstate/search`, + headers: { + 'Cookie': `vid=${VERISURE_TOKEN}`, + 'Accept': 'application/json, text/javascript, */*; q=0.01' + } + }, function (callback, error, response){ + this.log(`**** Response: getCurrentLockState: ${JSON.stringify(response)}`); + if (error) callback(error); + if (response && response.statusCode == 200){ + let body = JSON.parse(response.body); + for (let doorlock of body){ + if (doorlock.deviceLabel == this.serialNumber){ + if (doorlock.motorJam){ + this.value=Characteristic.LockCurrentState.JAMMED; + callback(null, this.value); + break; + } else { + this.value = doorlock.currentLockState == "UNLOCKED" ? Characteristic.LockCurrentState.UNSECURED : Characteristic.LockCurrentState.SECURED ; + callback(null, this.value); + break; + } + } + } + } + }.bind(this, callback)); + }, + + _getTargetLockState: function(callback){ + this.log(`${this.name} (${this.serialNumber}): GETTING TARGET LOCK STATE.`); + + verisure._apiClient({ + method: 'GET', + uri: `/installation/${VERISURE_INSTALLATION.giid}/doorlockstate/search`, + headers: { + 'Cookie': `vid=${VERISURE_TOKEN}`, + 'Accept': 'application/json, text/javascript, */*; q=0.01' + } + }, function(callback, error, response){ + this.log(`**** Response: getTargetLockState: ${JSON.stringify(response)}`); + if (error) callback(error); + if (response && response.statusCode == 200){ + let body = JSON.parse(response.body); + for (let doorlock of body){ + if (doorlock.deviceLabel == this.serialNumber) { + let targetLockState = doorlock.pendingLockState == "NONE" ? doorlock.currentLockState : doorlock.pendingLockState; + callback(error, targetLockState == "UNLOCKED" ? Characteristic.LockTargetState.UNSECURED : Characteristic.LockTargetState.SECURED); + } + } + } + + }.bind(this, callback)); + }, + + _setTargetLockState: function(value, callback){ + this.log(`${this.name} (${this.serialNumber}): Setting TARGET LOCK STATE to "${value}"`); + let actionValue = value ? "lock":"unlock"; + verisure._apiClient({ + method: 'PUT', + uri: `/installation/${VERISURE_INSTALLATION.giid}/device/${this.serialNumber}/${actionValue}`, + headers: { + 'Cookie': `vid=${VERISURE_TOKEN}`, + 'Accept': 'application/json, text/javascript, */*; q=0.01' + }, + json: + { + "code": VERISURE_DOORLOCK_CODE + } + }, function(value, callback, error, response){ + this.log(`***** Response from ${actionValue}-operation: ${JSON.stringify(response)}`); + if (error != null) callback(error, response); + if (response && response.statusCode != 200) callback(response); + this._waitForLockStatusChangeResult(value, callback, response); + }.bind(this,value,callback)) + }, + + _waitForLockStatusChangeResult: function(value, callback, response){ + setTimeout(function(value, callback, response){ + verisure._apiClient({ + method: 'GET', + uri: `/installation/${VERISURE_INSTALLATION.giid}/doorlockstate/change/result/${response.body.doorLockStateChangeTransactionId}`, + headers: { + 'Cookie': `vid=${VERISURE_TOKEN}`, + 'Accept': 'application/json, text/javascript, */*; q=0.01' + } + }, function(value, origResponse, callback,error,response){ + if (error != null) + this.log(`**** ERROR: ${JSON.stringify(error)}`); + + this.log(`**** Response: Doorlockstate: ${JSON.stringify(response)}`); + let body = JSON.parse(response.body); + if (body.result == "NO_DATA"){ + this._waitForLockStatusChangeResult(value, callback, origResponse); + } else { + this.service.setCharacteristic(Characteristic.LockCurrentState, value) + this.value = value; + callback (null); + } + }.bind(this, value, response, callback)); + }.bind(this, value, callback, response) + ,200); + }, getServices: function() { const accessoryInformation = new Service.AccessoryInformation(); accessoryInformation .setCharacteristic(Characteristic.Manufacturer, MANUFACTURER) - .setCharacteristic(Characteristic.Model, this.model) + .setCharacteristic(Characteristic.Model, DEVICE_TYPES[this.model] || this.model) .setCharacteristic(Characteristic.SerialNumber, this.serialNumber) let service = null; @@ -193,6 +314,20 @@ VerisureAccessory.prototype = { .value = this.value; } + if (['DOORLOCK'].includes(this.model)){ + service = new Service.LockMechanism(this.name); + service + .getCharacteristic(Characteristic.LockCurrentState) + .on('get', this._getCurrentLockState.bind(this)); + + service + .getCharacteristic(Characteristic.LockTargetState) + .on('get', this._getTargetLockState.bind(this)) + .on('set', this._setTargetLockState.bind(this)); + + this.service = service; + } + if(['HUMIDITY1', 'SIREN1', 'SMOKE2', 'VOICEBOX1'].includes(this.model)) { service = new Service.TemperatureSensor(this.name); service From b69129095a2cf977753ffc8bd224685e1a123407 Mon Sep 17 00:00:00 2001 From: Joakim Arborelius Date: Sat, 1 Jul 2017 22:41:45 +0200 Subject: [PATCH 3/6] Adjustments according to PR review --- index.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 9263a7c..c770571 100644 --- a/index.js +++ b/index.js @@ -25,11 +25,10 @@ let VERISURE_TOKEN = null; let VERISURE_INSTALLATION = null; let VERISURE_CALLS = {}; let VERISURE_DEVICE_NAMES = [] -let VERISURE_DOORLOCK_CODE = null; const getVerisureInstallation = function(config, callback) { - if (config && config.doorcode) VERISURE_DOORLOCK_CODE = config.doorcode; + verisure.auth(config.email, config.password, function(err, token) { if(err) return callback(err); VERISURE_TOKEN = token; @@ -260,7 +259,7 @@ VerisureAccessory.prototype = { }, json: { - "code": VERISURE_DOORLOCK_CODE + "code": this.config.doorcode } }, function(value, callback, error, response){ this.log(`***** Response from ${actionValue}-operation: ${JSON.stringify(response)}`); From a34adce146fc4b5e7f80b2d618bf925b165a5e72 Mon Sep 17 00:00:00 2001 From: Joakim Arborelius Date: Tue, 4 Jul 2017 09:59:19 +0200 Subject: [PATCH 4/6] Fix config for Yale Doorman MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Forgot to add config-value for ”doorcode” into VerisureAccessory --- index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/index.js b/index.js index c770571..4ff1af9 100644 --- a/index.js +++ b/index.js @@ -116,6 +116,7 @@ const VerisurePlatform = function(log, config, api) { model: 'DOORLOCK', serialNumber: device.deviceLabel, value: device.lockedState==='LOCKED' ? 1 : 0, + doorcode: config.doorcode, category: 6 // Hardcoded from Accessory.Categories in Accessory.js of hap-nodejs }); }); From fbb68e0db78bb43e6aea1461eca6ae6aa05fee17 Mon Sep 17 00:00:00 2001 From: Joakim Arborelius Date: Fri, 14 Jul 2017 12:41:47 +0200 Subject: [PATCH 5/6] Fix homebridge crash as reported in issue #2 This commit fixes the Homebridge crash when you try to lock a already locked door, or unlock a already unlocked door. --- index.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 4ff1af9..f72dfb3 100644 --- a/index.js +++ b/index.js @@ -265,8 +265,11 @@ VerisureAccessory.prototype = { }, function(value, callback, error, response){ this.log(`***** Response from ${actionValue}-operation: ${JSON.stringify(response)}`); if (error != null) callback(error, response); - if (response && response.statusCode != 200) callback(response); - this._waitForLockStatusChangeResult(value, callback, response); + if (response && response.statusCode != 200) { + callback(response); + } else { + this._waitForLockStatusChangeResult(value, callback, response); + } }.bind(this,value,callback)) }, From 9ed661dd67afdc268ac2480101c7caf67c01be4c Mon Sep 17 00:00:00 2001 From: Joakim Arborelius Date: Fri, 14 Jul 2017 13:03:47 +0200 Subject: [PATCH 6/6] Add specific error handling for setting already set state ( #2 ) --- index.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index f72dfb3..cad8798 100644 --- a/index.js +++ b/index.js @@ -266,7 +266,13 @@ VerisureAccessory.prototype = { this.log(`***** Response from ${actionValue}-operation: ${JSON.stringify(response)}`); if (error != null) callback(error, response); if (response && response.statusCode != 200) { - callback(response); + if (response.statusCode == 400 && response.body && response.body.errorCode == "VAL_00819"){ + this.service.setCharacteristic(Characteristic.LockCurrentState, value) + this.value = value; + callback (null); + } else { + callback(response); + } } else { this._waitForLockStatusChangeResult(value, callback, response); }