185 lines
4.9 KiB
JavaScript
185 lines
4.9 KiB
JavaScript
const {
|
|
HCColorLamp,
|
|
StateUpdateManager
|
|
} = require('homecontrol-control-base');
|
|
|
|
const PhilipsHueApi = require('node-hue-api');
|
|
|
|
class HCPhilipsHue 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 = HCPhilipsHue;
|
|
|
|
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));
|
|
}
|