Skip to content

Commit

Permalink
Merge pull request #13 from kodsnutten/master
Browse files Browse the repository at this point in the history
Initial support for Yale Doorman
  • Loading branch information
ptz0n authored Jul 20, 2017
2 parents 86748b9 + 9ed661d commit 79bc07a
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 37 deletions.
72 changes: 37 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
@@ -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": "[email protected]",
"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": "[email protected]",
"password": "yourT0p5ecre7Passw0rd",
"doorcode": "000000"
}
]
```
148 changes: 146 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const PLATFORM_NAME = 'verisure';
const MANUFACTURER = 'Verisure';

const DEVICE_TYPES = {
'DOORLOCK': 'Yale Doorman',
'HUMIDITY1': 'Klimatdetektor',
'SIREN1': 'Siren',
'SMARTPLUG': 'Smart plug',
Expand All @@ -27,6 +28,7 @@ let VERISURE_DEVICE_NAMES = []


const getVerisureInstallation = function(config, callback) {

verisure.auth(config.email, config.password, function(err, token) {
if(err) return callback(err);
VERISURE_TOKEN = token;
Expand Down Expand Up @@ -82,7 +84,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);
Expand All @@ -108,6 +109,20 @@ 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,
doorcode: config.doorcode,
category: 6 // Hardcoded from Accessory.Categories in Accessory.js of hap-nodejs
});
});
devices = devices.concat(locks);
}

callback(devices);
});
})
Expand All @@ -123,6 +138,7 @@ const VerisureAccessory = function(log, config) {
this.model = config.model;
this.serialNumber = config.serialNumber;
this.value = config.value;
this.service = null;
}


Expand Down Expand Up @@ -175,11 +191,125 @@ 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": this.config.doorcode
}
}, 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) {
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);
}
}.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;
Expand All @@ -193,6 +323,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
Expand Down

0 comments on commit 79bc07a

Please sign in to comment.