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)); }