From 14c2019cdb0140237e1e97230ef11b3b8c67e63f Mon Sep 17 00:00:00 2001 From: Erik Eng Date: Mon, 23 Jan 2017 08:46:59 +0100 Subject: [PATCH] First version --- .gitignore | 1 + Makefile | 9 +++ README.md | 33 +++++++++ index.js | 185 +++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 23 +++++++ 5 files changed, 251 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 index.js create mode 100644 package.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..642601a --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +export PATH := $(shell npm bin):$(PATH) +SHELL := /bin/bash + +.PHONY: release + +release: + npm version minor || exit 1 + npm publish || exit 1 + git push origin master --tags diff --git a/README.md b/README.md new file mode 100644 index 0000000..074bcec --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# homebridge-verisure + +This is a plugin for [homebridge](https://github.com/nfarina/homebridge). It's a +working implementation for several Verisure devices: + +- [x] __VOICEBOX1__ - Temperature +- [x] __SMOKE2__ - Temperature +- [x] __SMARTPLUG__ - State, on, off + +## 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" + } +] +``` diff --git a/index.js b/index.js new file mode 100644 index 0000000..297d846 --- /dev/null +++ b/index.js @@ -0,0 +1,185 @@ +const verisure = require('verisure') + + +var Accessory, Service, Characteristic, UUIDGen; + +const PLUGIN_NAME = 'homebridge-verisure'; +const PLATFORM_NAME = 'verisure'; +const MANUFACTURER = 'Verisure'; + +const DEVICE_TYPES = { + 'VOICEBOX1': 'Directenhet', + 'SMOKE2': 'Rökdetektor', + 'SMARTPLUG': 'Smart plug' +} + +let VERISURE_TOKEN = null; +let VERISURE_INSTALLATION = null; +let VERISURE_CALLS = {}; + + +const getVerisureInstallation = function(config, callback) { + verisure.auth(config.email, config.password, function(err, token) { + if(err) return callback(err); + VERISURE_TOKEN = token; + + verisure.installations(token, config.email, function(err, installations) { + if(err) return callback(err); + VERISURE_INSTALLATION = installations[0]; + callback(); + }); + }); +} + +const getOverview = function(callback) { + if(typeof VERISURE_CALLS.overview == 'undefined') { + VERISURE_CALLS.overview = []; + } + VERISURE_CALLS.overview.push(callback); + if(VERISURE_CALLS.overview.length > 1) { + return; + } + verisure.overview(VERISURE_TOKEN, VERISURE_INSTALLATION, function(err, overview) { + VERISURE_CALLS.overview.map(function(callback) { + callback(err, overview); + }); + VERISURE_CALLS.overview = []; + }); +} + + +module.exports = function(homebridge) { + Accessory = homebridge.platformAccessory; + + Service = homebridge.hap.Service; + Characteristic = homebridge.hap.Characteristic; + UUIDGen = homebridge.hap.uuid; + + homebridge.registerPlatform(PLUGIN_NAME, PLATFORM_NAME, VerisurePlatform, true); +} + + +const VerisurePlatform = function(log, config, api) { + var platform = this; + this.log = log; + this.config = config; + + this.accessories = function(callback) { + getVerisureInstallation(config, function(err) { + if(err) return log(err); + + verisure.overview(VERISURE_TOKEN, VERISURE_INSTALLATION, function(err, overview) { + if(err) return log(err); + var devices = overview.climateValues.map(function(device) { + return new VerisureAccessory(log, { + name: `${DEVICE_TYPES[device.deviceType]} (${device.deviceArea})`, + model: device.deviceType, + serialNumber: device.deviceLabel, + value: 0 + }); + }); + + devices = devices.concat(overview.smartPlugs.map(function(device) { + return new VerisureAccessory(log, { + name: `${DEVICE_TYPES.SMARTPLUG} (${device.area})`, + model: 'SMARTPLUG', + serialNumber: device.deviceLabel, + value: device.currentState == 'ON' ? 1 : 0 + }); + })); + + callback(devices); + }); + }) + } +} + + +const VerisureAccessory = function(log, config) { + this.log = log; + this.config = config; + + this.name = config.name; + this.model = config.model; + this.serialNumber = config.serialNumber; + this.value = config.value; +} + + +VerisureAccessory.prototype = { + _getCurrentTemperature: function(callback) { + this.log(`${this.name} (${this.serialNumber}): Getting current temperature...`); + var that = this; + + getOverview(function(err, overview) { + if(err) return callback(err); + overview.climateValues.map(function(device) { + if(device.deviceLabel != that.serialNumber) return; + that.value = device.temperature; + callback(err, that.value); + }); + }); + }, + + _getSwitchValue: function(callback) { + this.log(`${this.name} (${this.serialNumber}): Getting current value...`); + var that = this; + + getOverview(function(err, overview) { + if(err) return callback(err); + overview.smartPlugs.map(function(device) { + if(device.deviceLabel != that.serialNumber) return; + that.value = device.currentState == 'ON' ? 1 : 0; + callback(err, that.value); + }); + }); + }, + + _setSwitchValue: function(value, callback) { + this.log(`${this.name} (${this.serialNumber}): Setting current value to "${value}"...`); + this.value = value; + + verisure._apiClient({ + method: 'POST', + uri: `/installation/${VERISURE_INSTALLATION.giid}/smartplug/state`, + headers: { + 'Cookie': `vid=${VERISURE_TOKEN}`, + 'Accept': 'application/json, text/javascript, */*; q=0.01' + }, + json: [ + { + deviceLabel: this.serialNumber, + state: value == 1 ? true : false + } + ] + }, callback); + }, + + getServices: function() { + var accessoryInformation = new Service.AccessoryInformation(); + accessoryInformation + .setCharacteristic(Characteristic.Manufacturer, MANUFACTURER) + .setCharacteristic(Characteristic.Model, this.model) + .setCharacteristic(Characteristic.SerialNumber, this.serialNumber) + + var service = null; + + if(this.model == 'SMARTPLUG') { + service = new Service.Switch(this.name); + service + .getCharacteristic(Characteristic.On) + .on('get', this._getSwitchValue.bind(this)) + .on('set', this._setSwitchValue.bind(this)) + .value = this.value; + } + + if(['VOICEBOX1', 'SMOKE2'].includes(this.model)) { + service = new Service.TemperatureSensor(this.name); + service + .getCharacteristic(Characteristic.CurrentTemperature) + .on('get', this._getCurrentTemperature.bind(this)); + } + + return [accessoryInformation, service] + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..d377c25 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "homebridge-verisure", + "version": "1.0.0", + "description": "Verisure plugin for homebridge: https://github.com/nfarina/homebridge", + "license": "MIT", + "keywords": [ + "homebridge-plugin" + ], + "repository": { + "type": "git", + "url": "git://github.com/ptz0n/homebridge-verisure.git" + }, + "bugs": { + "url": "http://github.com/ptz0n/homebridge-verisure/issues" + }, + "engines": { + "node": ">=0.12.0", + "homebridge": ">=0.2.0" + }, + "dependencies": { + "verisure": "^1.1.0" + } +}