From ba32781a11d2294c6a7222ea6aff2bdbb32475ad Mon Sep 17 00:00:00 2001 From: jangxx Date: Tue, 29 Jan 2019 23:24:32 +0100 Subject: [PATCH] version 1.0.0 --- .gitignore | 1 + HCPhilipsHue.js | 184 ++++++++++++++++++++++++++++++++++++++++++++++ package-lock.json | 109 +++++++++++++++++++++++++++ package.json | 19 +++++ 4 files changed, 313 insertions(+) create mode 100644 .gitignore create mode 100644 HCPhilipsHue.js create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/HCPhilipsHue.js b/HCPhilipsHue.js new file mode 100644 index 0000000..50339bb --- /dev/null +++ b/HCPhilipsHue.js @@ -0,0 +1,184 @@ +const { + HCColorLamp, + StateUpdateManager +} = require('homecontrol-control-base'); + +const PhilipsHueApi = require('node-hue-api'); + +class HCMagicHome extends HCColorLamp { + constructor(config) { + super(config); + + if (!("address" in this._configuration)) { + throw new Error(`Required configuration field "address" is missing"`); + } + + if (!("username" in this._configuration)) { + throw new Error(`Required configuration field "username" is missing"`); + } + + if (!("uniqueid" in this._configuration)) { + throw new Error(`Required configuration field "uniqueid" is missing"`); + } + + this._api = new PhilipsHueApi.HueApi(this._configuration.address, this._configuration.username); + this._lightid = null; + + this._sumanager = new StateUpdateManager(this._state); + } + + // overwrite to make use of the SUManager + get state() { + return this._sumanager.state.clone(); + } + + get effects() { + return [ + {name: "Color Loop", id: "colorloop"} + ]; + } + + /** + * Fetch idmap and resolve uniqueId into a local id + */ + init() { + return this._api.lights().then((resp) => { + let lights = resp.lights; + + for(let l of lights) { + if (l.uniqueid == this._configuration.uniqueid) { + this._lightid = l.id; + break; + } + } + + if (this._lightid == null) throw new Error(`Light with unique id "${this._configuration.uniqueid}" was not found on the bridge.`); + }); + } + + turnOn() { + let futureState = this.state; + futureState.on = true; + let suid = this._sumanager.registerUpdate(futureState); + + let lightState = PhilipsHueApi.lightState.create(); + lightState.on(); + + return this._api.setLightState(this._lightid, lightState).then(resolveHelper(this, suid), rejectHelper(this, suid)); + } + + turnOff() { + let futureState = this.state; + futureState.on = false; + let suid = this._sumanager.registerUpdate(futureState); + + let lightState = PhilipsHueApi.lightState.create(); + lightState.off(); + + return this._api.setLightState(this._lightid, lightState).then(resolveHelper(this, suid), rejectHelper(this, suid)); + } + + setBrightness(brightness) { + let futureState = this.state; + futureState.brightness = brightness; + let suid = this._sumanager.registerUpdate(futureState); + + let lightState = PhilipsHueApi.lightState.create(); + lightState.bri(brightness * (255 / 100)); + + return this._api.setLightState(this._lightid, lightState).then(resolveHelper(this, suid), rejectHelper(this, suid)); + } + + setColor(color) { + let futureState = this.state; + futureState.color = fillPartialHSL(color, futureState.color); + let suid = this._sumanager.registerUpdate(futureState); + + let lightState = PhilipsHueApi.lightState.create(); + let { hue, sat } = convertHSLtoHue(futureState.color.hue, futureState.color.sat, futureState.color.l); + lightState.hue(hue).sat(sat); + + return this._api.setLightState(this._lightid, lightState).then(resolveHelper(this, suid), rejectHelper(this, suid)); + } + + setEffect(id) { + let futureState = this.state; + futureState.effect = id; + let suid = this._sumanager.registerUpdate(futureState); + + let lightState = PhilipsHueApi.lightState.create(); + lightState.effect(id); + + return this._api.setLightState(this._lightid, lightState).then(resolveHelper(this, suid), rejectHelper(this, suid)); + } + + pullState() { + return this._api.lightStatus(this._lightid).then((resp) => { + let lampstate = resp.state; + + let currentState = this.state; + let futureState = currentState.clone(); + + futureState.on = lampstate.on; + futureState.color = convertHuetoHSL(lampstate.hue, lampstate.sat); + futureState.brightness = lampstate.bri * (100/255); + futureState.effect = lampstate.effect; + + if (currentState.hash != futureState.hash) { + // the state of the controller has changed externally + this._sumanager.insertConfirmedState(futureState); + this.emit("state change", this.state); + } + }); + } +} + +module.exports = HCMagicHome; + +function rejectHelper(self, suid) { + return (err) => { + self._sumanager.rejectUpdate(suid); + throw err; // rethrow so we can catch it at a higher level + }; +} + +function resolveHelper(self, suid) { + return () => { + self._sumanager.confirmUpdate(suid); + + if (self._sumanager.highestConfirmedId == suid) { + self.emit("state change", self.state); + } + }; +} + +function fillPartialHSL(newcolor, oldcolor) { + //console.log(newcolor, oldcolor); + var color = {}; + color["hue"] = (newcolor.hue != null) ? newcolor.hue : oldcolor.hue; + color["sat"] = (newcolor.sat != null) ? newcolor.sat : oldcolor.sat; + color["l"] = (newcolor.l != null) ? newcolor.l : oldcolor.l; + return color; +} + +function convertHSLtoHue(h, s, l) { + let hue = clamp(h, 0, 360) * (65539 / 360); + let sat = clamp(s, 0, 100) * (255 / 100); + + return {hue, sat}; +} + +function convertHuetoHSL(hue, sat) { + hue = clamp(hue, 0, 65539); + sat = clamp(sat, 0, 255); + + var h = hue * (360 / 65539); + var s = sat * (100 / 255); + var l = 100 - sat * ((100-50) / 255) + + return {hue: Math.round(h), sat: Math.round(s), l: Math.round(l)}; +} + +function clamp(value, min, max) { + return Math.min(max, Math.max(min, value)); +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..3949a81 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,109 @@ +{ + "name": "hc-philipshue", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "axios": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.15.3.tgz", + "integrity": "sha1-LJ1jiy4ZGgjqHWzJiOrda6W9wFM=", + "requires": { + "follow-redirects": "1.0.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "follow-redirects": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.0.0.tgz", + "integrity": "sha1-jjQpjL0uF28lTv/sdaHHjMhJ/Tc=", + "requires": { + "debug": "^2.2.0" + } + }, + "homecontrol-control-base": { + "version": "git+https://git.literalchaos.de/jan/homecontrol-control-base.git#809acfad8245dad9d974cba5b1eff2dfc5419cc9", + "from": "git+https://git.literalchaos.de/jan/homecontrol-control-base.git", + "requires": { + "merge-options": "^1.0.1", + "node-object-hash": "^1.4.1" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + }, + "merge-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-1.0.1.tgz", + "integrity": "sha512-iuPV41VWKWBIOpBsjoxjDZw8/GbSfZ2mk7N1453bwMrfzdrIk7EzBd+8UVR6rkw67th7xnk9Dytl3J+lHPdxvg==", + "requires": { + "is-plain-obj": "^1.1" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node-hue-api": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/node-hue-api/-/node-hue-api-2.4.4.tgz", + "integrity": "sha512-526lV8pK2x4HW2JufXHVcFHj8Z9mOuPR37Gu+/w9Vw/CAPvzKXY5BkG8KmOLbpzjiWljN3s+JMO3n4xDra3l9Q==", + "requires": { + "axios": "~0.15.3", + "deep-extend": "^0.6.0", + "q": "~1.4", + "traits": "~0.4.0", + "xml2js": "~0.4" + } + }, + "node-object-hash": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/node-object-hash/-/node-object-hash-1.4.1.tgz", + "integrity": "sha512-JQVqSM5/mOaUoUhCYR0t1vgm8RFo7qpJtPvnoFCLeqQh1xrfmr3BCD3nGBnACzpIEF7F7EVgqGD3O4lao/BY/A==" + }, + "q": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", + "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "traits": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/traits/-/traits-0.4.0.tgz", + "integrity": "sha1-QW7cq9yL9kvSkQKnRT3N3YVzcaE=" + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..a02e200 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "hc-philipshue", + "version": "1.0.0", + "description": "Philips Hue Plugin for Homecontrol", + "main": "HCPhilipsHue.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git@git.literalchaos.de:jan/hc-philipshue.git" + }, + "author": "Jan Scheiper", + "license": "UNLICENSED", + "dependencies": { + "homecontrol-control-base": "git+https://git.literalchaos.de/jan/homecontrol-control-base.git", + "node-hue-api": "^2.4.4" + } +}