Files
Zos/Skills/@be/node_modules/node-hue-api/hue-api/lightstate.js

734 lines
19 KiB
JavaScript

"use strict";
var util = require("util")
, utils = require("./utils")
, rgb = require("./rgb")
, lightStateTrait = require("./commands/traits/tLightStateBody")
;
var stateDefinitions = lightStateTrait(true).bodyArguments.value
, State = function () {
this.reset();
};
/**
* Creates a new state object to pass to a Philips Hue Light.
*
* @param values An optional object that contains state properties and values that are to be loaded into the created
* state object. Any properties that are not 'valid' properties of the Light State are not loaded.
*
* @return {State}
*/
module.exports.create = function (values) {
var state = new State();
// If values are provided then load the ones that have values to match our functions
if (values) {
Object.keys(values).forEach(function (value) {
var fn;
if (values.hasOwnProperty(value)) {
fn = state[value];
if (utils.isFunction(fn)) {
fn.apply(state, [values[value]]);
}
}
});
}
return state;
};
module.exports.isLightState = function (obj) {
return obj && obj instanceof State;
};
State.prototype.payload = function () {
return JSON.parse(JSON.stringify(this._values));
};
/**
* Resets/Clears the properties that have been set in the light state object.
* @returns {State}
*/
State.prototype.reset = function () {
this._values = {};
return this;
};
State.prototype.clear = State.prototype.reset;
/**
* Creates a copy of the state object
* @returns {State}
*/
State.prototype.copy = function () {
var copy = new State();
copy._addValues(this._values);
return copy;
};
/**
* Sets the strict state for setting parameters for the light state.
* @param strict
* @returns {State}
*/
State.prototype.strict = function (strict) {
this._strict = Boolean(strict);
return this;
};
State.prototype.isStrict = function () {
return this._strict;
};
/**
* Sets the on state
* @param on The state (true for on, false for off). If this parameter is not specified, it is assumed to be true.
* @returns {State}
*/
State.prototype.on = function (on) {
this._addValues(_getOnState(on));
return this;
};
/**
* Adds the bri state
* @param value The hue bri value, 0 to 254.
* @return {State}
*/
State.prototype.bri = function (value) {
this._addValues(_getBriState(value));
return this;
};
/**
* Adds the hue for the color desired.
* @param hue The hue value is a wrapping value between 0 and 65535. Both 0 and 65535 are red, 25500 is green and 46920 is blue.
* @returns {State}
*/
State.prototype.hue = function (hue) {
this._addValues(_getHueState(hue));
return this;
};
/**
* The saturation of the color for the bulb, 0 being the least saturated i.e. white.
* @param saturation The saturation value 0 to 255
* @returns {State}
*/
State.prototype.sat = function (saturation) {
this._addValues(_getSatState(saturation));
return this;
};
/**
* Adds the xy values
* @param x The x value ranged from 0 to 1, or an Array of [x, y] values
* @param y The y value ranged from 0 to 1
* @return {State}
*/
State.prototype.xy = function (x, y) {
// Cater for the first argument being an array
if (Array.isArray(arguments[0])) {
x = arguments[0][0];
y = arguments[0][1];
}
this._addValues(_getXYState(x, y));
return this;
};
/**
* Adds the Mired Color Temperature
* @param colorTemp The Color Temperature between 153 to 500 inclusive.
* @returns {State}
*/
State.prototype.ct = function (colorTemp) {
this._addValues(_getCtState(colorTemp));
return this;
};
/**
* Adds the alert state
* @param value A String value representing the alert state, "none", "select", "lselect".
* @return {State}
*/
State.prototype.alert = function (value) {
this._addValues(_getAlertState(value));
return this;
};
/**
* Adds an effect for the bulb.
* @param value The type of effect, currently supports "none" and "colorloop".
* @returns {State}
*/
State.prototype.effect = function (value) {
this._addValues(_getEffectState(value));
return this;
};
/**
* Adds a transition to the desired state.
* @param value This is given as a multiple of 100ms and defaults to 4 (400ms).
* @return {State}
*/
State.prototype.transitiontime = function (value) {
this._addValues(_getTransitionState(value));
return this;
};
/**
* Increments/Decrements the brightness value for the lights.
* @param value An amount to change the current brightness by, -254 to 254.
* @returns {State}
*/
State.prototype.bri_inc = function (value) {
this._addValues(_getBrightnessIncrementState(value));
return this;
};
/**
* Increments/Decrements the saturation value for the lights.
* @param value An amount to change the current saturation by, -254 to 254.
* @returns {State}
*/
State.prototype.sat_inc = function (value) {
this._addValues(_getSaturationIncrementState(value));
return this;
};
/**
* Increments/Decrements the Hue value for the lights.
* @param value An amount to change the current hue by, -65534 to 65534.
* @returns {State}
*/
State.prototype.hue_inc = function (value) {
this._addValues(_getHueIncrementState(value));
return this;
};
/**
* Increments/Decrements the color temperature value for the lights.
* @param value An amount to change the current color temperature by, -65534 to 65534.
* @returns {State}
*/
State.prototype.ct_inc = function (value) {
this._addValues(_getCtIncrementState(value));
return this;
};
/**
* Increments/Decrements the XY value for the lights.
* @param value An amount to change the current XY by, -0.5 to 0.5.
* @returns {State}
*/
State.prototype.xy_inc = function (value) {
this._addValues(_getXYIncrementState(value));
return this;
};
State.prototype.scene = function (value) {
this._addValues(_getSceneId(value));
return this;
};
///////////////////////////////////////////////////////////////////////
// Convenience functions
State.prototype.turnOn = State.prototype.on;
State.prototype.off = function () {
this._addValues(_getOnState(false));
return this;
};
State.prototype.turnOff = State.prototype.off;
/**
* Set the brightness as a percent value
* @param percentage The brightness percentage value between 0 and 100.
* @returns {State}
*/
State.prototype.brightness = function (percentage) {
return this.bri(_convertBrightPercentToHueValue(percentage));
};
State.prototype.incrementBrightness = State.prototype.bri_inc;
State.prototype.colorTemperature = State.prototype.ct;
State.prototype.colourTemperature = State.prototype.ct;
State.prototype.colorTemp = State.prototype.ct;
State.prototype.colourTemp = State.prototype.ct;
State.prototype.incrementColorTemp = State.prototype.ct_inc;
State.prototype.incrementColorTemperature = State.prototype.ct_inc;
State.prototype.incrementColourTemp = State.prototype.ct_inc;
State.prototype.incrementColourTemperature = State.prototype.ct_inc;
State.prototype.incrementHue = State.prototype.hue_inc;
State.prototype.incrementXY = State.prototype.xy_inc;
State.prototype.saturation = function (percentage) {
return this.sat(_convertSaturationPercentToHueValue(percentage));
};
State.prototype.incrementSaturation = State.prototype.sat_inc;
State.prototype.shortAlert = function () {
return this.alert("select");
};
State.prototype.alertShort = State.prototype.shortAlert;
State.prototype.longAlert = function () {
return this.alert("lselect");
};
State.prototype.alertLong = State.prototype.longAlert;
State.prototype.transitionTime = State.prototype.transitiontime;
/**
* Sets the transition time in milliseconds.
* @param milliseconds The number of milliseconds for the transition
* @returns {State}
*/
State.prototype.transition = function (milliseconds) {
return this.transitionTime(_convertMilliSecondsToTransitionTime(milliseconds));
};
State.prototype.transitiontime_milliseconds = State.prototype.transition;
State.prototype.transitionTime_milliseconds = State.prototype.transition;
State.prototype.transitionSlow = function () {
return this.transitionTime(8);
};
State.prototype.transitionFast = function () {
return this.transitionTime(2);
};
State.prototype.transitionInstant = function () {
return this.transitionTime(0);
};
State.prototype.transitionDefault = function () {
return this.transitionTime(4);//TODO should load this from the definition
};
/**
* Builds the White state for a lamp
* @param colorTemp The temperature, a value of 153-500
* @param brightPercentage The percentage of brightness 0-100
* @return {State}
*/
State.prototype.white = function (colorTemp, brightPercentage) {
this._addValues(_getWhiteState(colorTemp, brightPercentage));
return this;
};
/**
* Adds the HSL values
* @param hue The hue value in degrees 0-360
* @param saturation The saturation percentage 0-100
* @param luminosity The luminosity percentage 0-100
* @return {State}
*/
State.prototype.hsl = function (hue, saturation, luminosity) {
var temp = saturation * (luminosity < 50 ? luminosity : 100 - luminosity) / 100
, satValue = Math.round(200 * temp / (luminosity + temp)) | 0
, luminosityValue = Math.round(temp + luminosity)
;
return this
.brightness(luminosityValue)
.hue(_convertHueToHueValue(hue))
.sat(_convertSaturationPercentToHueValue(satValue))
;
};
/**
* Adds the HSB values
* @param hue The hue value in degrees 0-360
* @param saturation The saturation percentage 0-100
* @param brightness The brightness percentage 0-100
* @return {State}
*/
State.prototype.hsb = function (hue, saturation, brightness) {
return this
.brightness(brightness)
.hue(_convertHueToHueValue(hue))
.sat(_convertSaturationPercentToHueValue(saturation))
;
};
/**
* Adds the rgb color to the state. This requires knowledge of the light type to be able to convert it into
* an actual color that the map can display.
*
* @param r The amount of Red 0-255, or an {Array} or r, g, b values.
* @param g The amount of Green 0-255
* @param b The amount of Blue 0-255
* @return {State}
*/
State.prototype.rgb = function (r, g, b) {
// The conversion to rgb is now done in the xy space, but to do so requires knowledge of the limits of the light's
// color gamut.
// To cater for this, we store the rgb value requested, and convert it to xy when the user applies it.
// We may have an array passed in, cater for that
if (Array.isArray(arguments[0])) {
r = arguments[0][0];
g = arguments[0][1];
b = arguments[0][2];
}
this._addValues({
rgb: [
_getBoundedValue(r, 0, 255),
_getBoundedValue(g, 0, 255),
_getBoundedValue(b, 0, 255)
]
});
return this;
};
State.prototype.hasRGB = function () {
return !!this._values.rgb;
};
State.prototype.colorLoop = function () {
return this.effect("colorloop");
};
State.prototype.colourLoop = State.prototype.colorLoop;
State.prototype.effectColorLoop = State.prototype.colorLoop;
State.prototype.effectColourLoop = State.prototype.colorLoop;
/**
* Creates a copy of the State if there is an RGB value set.
*
* @param modelid The model ID of the light(s) to convert the rgb value for.
*
* @returns {State} If there is an RGB value set, then a copy of the state, with the rgb value applied based on the
* lamp model provided. If there is no RGB value set, then {null} will be returned.
*/
State.prototype.applyRGB = function (modelid) {
var result = null;
if (this.hasRGB()) {
result = this.copy();
result.xy(rgb.convertRGBtoXY(this._values.rgb, modelid));
}
return result;
};
///////////////////////////////////////////////////////////////////////
State.prototype._addValues = function () {
var state = this._values;
Array.prototype.slice.apply(arguments).forEach(function (stateValue) {
utils.combine(state, stateValue);
});
};
State.prototype._removeValues = function () {
var state = this._values;
Array.prototype.slice.apply(arguments).forEach(function (key) {
delete state[key];
});
};
/////////////////////////////////
// State objects
function _getOnState(value) {
if (value == null || value === undefined) {
value = true;
}
return {
on: _getOnValue(value)
};
}
function _getBriState(value) {
return {
bri: _getBrightnessValue(value)
};
}
function _getHueState(value) {
return {
hue: _getHueValue(value)
};
}
function _getSatState(value) {
return {
sat: _getSaturationValue(value)
};
}
function _getXYState(x, y) {
return {
xy: _getXYValue(x, y)
};
}
function _getCtState(value) {
return {
ct: _getCtValue(value)
};
}
function _getAlertState(value) {
return {
alert: _getAlertValue(value)
};
}
function _getEffectState(value) {
return {
effect: _getEffectValue(value)
};
}
function _getTransitionState(value) {
return {
transitiontime: _getTransitionTimeValue(value)
};
}
function _getBrightnessIncrementState(value) {
return {
bri_inc: _getBrightnessIncrementValue(value)
}
}
function _getSaturationIncrementState(value) {
return {
sat_inc: _getSaturationIncrementValue(value)
}
}
function _getHueIncrementState(value) {
return {
hue_inc: _getHueIncrementValue(value)
}
}
function _getCtIncrementState(value) {
return {
ct_inc: _getCtIncrementValue(value)
}
}
function _getXYIncrementState(value) {
return {
xy_inc: _getXYIncrementValue(value)
}
}
function _getSceneId(value) {
var result = {};
if (value) {
result.scene = value;
}
return result;
}
function _getWhiteState(colorTemp, brightness) {
return utils.combine(
_getCtState(colorTemp),
_getBriState(_convertBrightPercentToHueValue(brightness))
);
}
/////////////////////////////////
// Value Functions
function _convertHueToHueValue(hue) {
return _getBoundedValue(hue, 0, 360) * 182.5487
}
function _convertMilliSecondsToTransitionTime(value) {
var result = 0;
// The transition time is in multiples of 100ms, e.g. 100ms = 1
if (value > 0) {
result = Math.round(value / 100);
}
return result;
}
function _getTransitionTimeValue(value) {
var transition = stateDefinitions.transitiontime;
return valueForType(transition, _getRangeValue(transition, value));
}
function _getBrightnessIncrementValue(value) {
var bri_inc = stateDefinitions.bri_inc;
return valueForType(bri_inc, _getRangeValue(bri_inc, value));
}
function _getSaturationIncrementValue(value) {
var sat_inc = stateDefinitions.sat_inc;
return valueForType(sat_inc, _getRangeValue(sat_inc, value));
}
function _getHueIncrementValue(value) {
var hue_inc = stateDefinitions.hue_inc;
return valueForType(hue_inc, _getRangeValue(hue_inc, value));
}
function _getCtIncrementValue(value) {
var ct_inc = stateDefinitions.ct_inc;
return valueForType(ct_inc, _getRangeValue(ct_inc, value));
}
function _getXYIncrementValue(value) {
var xy_inc = stateDefinitions.xy_inc;
return valueForType(xy_inc, _getRangeValue(xy_inc, value));
}
function convertPercentToValue(definition, percent) {
var range = definition.range
, min = range.min
, max = range.max
, normalizedValue = _getBoundedValue(percent, 0, 100)
;
if (normalizedValue === 0) {
return min;
} else if (normalizedValue === 100) {
return max;
} else {
return normalizedValue * (max / 100);
}
}
function _convertSaturationPercentToHueValue(value) {
return _getSaturationValue(convertPercentToValue(stateDefinitions.sat, value));
}
function _convertBrightPercentToHueValue(value) {
return _getBrightnessValue(convertPercentToValue(stateDefinitions.bri, value));
}
/**
* Obtains the XY color values that the Hue Lights can understand
* @param x The X value
* @param y The Y value
* @return {Array} The converted xy values.
*/
function _getXYValue(x, y) {
var xy = stateDefinitions.xy
, validateValueFn = function (value) {
var valueType = xy.valueType
, normalized = _getRangeValue(valueType, value)
;
return valueForType(valueType, normalized);
}
;
return valueForType(xy, [validateValueFn(x), validateValueFn(y)]);
}
function _getSaturationValue(value) {
var sat = stateDefinitions.sat;
return valueForType(sat, _getRangeValue(sat, value));
}
function _getHueValue(value) {
var hue = stateDefinitions.hue;
return valueForType(hue, _getRangeValue(hue, value));
}
/**
* Color Temperature values are limited to the range of 153 - 500, cold to warm.
* @param value The value to set as a {String} or {Integer}
* @return {Number} The color temperature to use.
*/
function _getCtValue(value) {
var ct = stateDefinitions.ct;
return valueForType(ct, _getRangeValue(ct, value));
}
/**
* Brightness values are limited to the range of 0 - 254.
* @param value The value to set as a {String} or {Integer}
* @return {Number} The brightness value to use.
*/
function _getBrightnessValue(value) {
var bri = stateDefinitions.bri;
return valueForType(bri, _getRangeValue(bri, value));
}
function _getAlertValue(value) {
var alert = stateDefinitions.alert;
return valueForType(alert, _getOptionValue(alert, value));
}
function _getOnValue(value) {
var on = stateDefinitions.on;
return valueForType(on, value);
}
function _getEffectValue(value) {
var effect = stateDefinitions.effect;
return valueForType(effect, _getOptionValue(effect, value));
}
function _getOptionValue(definition, value) {
var validValues = definition.validValues
, resolved
;
validValues.forEach(function (validValue) {
if (!resolved && validValue === value) {
resolved = value;
}
});
return resolved || definition.defaultValue || validValues[0];
}
function _getRangeValue(definition, value) {
var range = definition.range;
if (value === undefined || value === null && definition.defaultValue) {
value = definition.defaultValue;
}
return _getBoundedValue(value, range.min, range.max)
}
/**
* Obtains a value that falls within the specified range
* @param value The value to check
* @param min The minimum allowed value
* @param max The maximum allowed value
* @return {*} The value that sits within the specified range
*/
function _getBoundedValue(value, min, max) {
if (isNaN(value)) {
value = min;
}
if (value < min) {
return min;
} else if (value > max) {
return max;
} else {
return value;
}
}
function valueForType(definition, value) {
return utils.valueForType(definition.type, value);
}