node-red-contrib-huemagic
Version:
Philips Hue node to control bridges, lights, groups, scenes, rules, taps, switches, buttons, motion sensors, temperature sensors and Lux sensors using Node-RED.
866 lines (778 loc) • 27.1 kB
JavaScript
module.exports = function(RED)
{
"use strict";
function HueLight(config)
{
RED.nodes.createNode(this, config);
const scope = this;
const bridge = RED.nodes.getNode(config.bridge);
const async = require('async');
// SAVE FUTURE PATCH
this.futurePatchState = {};
// SAVE LAST COMMAND
this.lastCommand = null;
// HELPER
const colorUtils = require('./utils/color');
const merge = require('./utils/merge');
//
// CHECK CONFIG
if(bridge == null)
{
this.status({fill: "red", shape: "ring", text: "hue-light.node.not-configured"});
return false;
}
//
// UNIVERSAL MODE?
if(!config.lightid)
{
this.status({fill: "grey", shape: "dot", text: "hue-light.node.universal"});
}
//
// UPDATE STATE
if(typeof bridge.disableupdates != 'undefined' || bridge.disableupdates == false)
{
this.status({fill: "grey", shape: "dot", text: "hue-light.node.init"});
}
//
// SUBSCRIBE TO UPDATES FROM THE BRIDGE
bridge.subscribe("light", config.lightid, function(info)
{
let currentState = bridge.get("light", info.id, { colornames: config.colornamer ? true : false });
// RESOURCE FOUND?
if(currentState !== false)
{
// SEND MESSAGE
if(!config.skipevents && (config.initevents || info.suppressMessage == false))
{
// SET LAST COMMAND
if(scope.lastCommand !== null)
{
currentState.command = scope.lastCommand;
}
// SEND STATE
scope.send(currentState);
// RESET LAST COMMAND
scope.lastCommand = null;
}
// NOT IN UNIVERAL MODE? -> CHANGE UI STATES
if(config.lightid)
{
if(currentState.payload.reachable === true)
{
if(currentState.payload.on === true)
{
// APPLY FUTURE STATE COMMANDS
if(Object.values(scope.futurePatchState).length > 0)
{
scope.applyCommands({}, null, null);
}
if(currentState.payload.brightness !== false)
{
scope.status({fill: "yellow", shape: "dot", text: RED._("hue-light.node.turned-on-percent",{ percent: currentState.payload.brightness })});
}
else
{
scope.status({fill: "yellow", shape: "dot", text: "hue-light.node.turned-on"});
}
}
else
{
scope.status({fill: "grey", shape: "dot", text: "hue-light.node.turned-off"});
}
}
else
{
var offNotReachableStatus = RED._("hue-light.node.turned-off") + " (" + RED._("hue-light.node.not-reachable") + ")";
scope.status({fill: "red", shape: "ring", text: offNotReachableStatus});
}
}
}
});
//
// CONTROL LIGHT
this.on('input', function(msg, send, done) { scope.applyCommands(msg, send, done); });
//
// APPLY COMMANDS
this.applyCommands = async function(msg, send = null, done = null)
{
// REDEFINE SEND AND DONE IF NOT AVAILABLE
send = send || function() { scope.send.apply(scope,arguments); }
done = done || function() { scope.done.apply(scope,arguments); }
// SAVE LAST COMMAND
scope.lastCommand = RED.util.cloneMessage(msg);
// CREATE PATCH
let patchObject = {};
// DEFINE SENSOR ID & CURRENT STATE
const tempLightID = (!config.lightid && typeof msg.topic != 'undefined' && bridge.validResourceID.test(msg.topic) === true) ? msg.topic : config.lightid;
let currentState = bridge.get("light", tempLightID, { colornames: config.colornamer ? true : false });
if(!currentState)
{
scope.error("The light in not yet available. Please wait until HueMagic has established a connection with the bridge or check whether the resource ID in the configuration is valid.");
return false;
}
// CHECK IF LIGHT ID IS SET
if(!tempLightID)
{
scope.error(RED._("hue-light.node.error-no-id"));
return false;
}
// GET CURRENT STATE
if( (typeof msg.payload != 'undefined' && typeof msg.payload.status != 'undefined') || (typeof msg.__user_inject_props__ != 'undefined' && msg.__user_inject_props__ == "status") )
{
// SET LAST COMMAND
if(scope.lastCommand !== null)
{
currentState.command = scope.lastCommand;
}
// SEND STATE
scope.send(currentState);
// RESET LAST COMMAND
scope.lastCommand = null;
if(done) { done(); }
return true;
}
// GET FUTURE STATE
if(Object.values(scope.futurePatchState).length > 0)
{
patchObject = Object.assign({}, scope.futurePatchState);
scope.futurePatchState = {};
}
// COLORLOOP EFFECT
if(typeof msg.payload != 'undefined' && typeof msg.payload.colorloop != 'undefined' && msg.payload.colorloop > 0)
{
patchObject = {
"on": true,
"effect": "colorloop",
"bri": msg.payload.brightness ? Math.round((254/100)*msg.payload.brightness) : currentState.brightnessLevel
};
// PATCH!
async.retry({
times: 3,
errorFilter: function(err) {
return (err.status == 503);
},
interval: function(retryCount) { return retryCount*2000; }
},
function(callback, results)
{
bridge.patch("light", currentState.info.idV1 + "/state", patchObject, 1)
.then(function(status)
{
// RESET COLORLOOP ANIMATION AFTER X SECONDS
setTimeout(function()
{
bridge.patch("light", currentState.info.idV1 + "/state", { "effect": "none" }, 1);
}, parseInt(msg.payload.colorloop) * 1000);
callback(null, true);
})
.catch(function(errors)
{
callback(errors, null);
});
},
function(errors, success)
{
if(errors)
{
scope.error(errors);
scope.status({fill: "red", shape: "ring", text: "hue-light.node.error-input"});
}
else if(done)
{
done();
}
});
return false;
}
// ALERT EFFECT
if(typeof msg.payload != 'undefined' && typeof msg.payload.alert != 'undefined' && msg.payload.alert > 0)
{
// SAVE PREVIOUS STATE
scope.context().set('lightPreviousState', currentState);
// TURN ON LIGHT
if(currentState.payload.on === false)
{
patchObject["on"] = { on: true };
}
// SET BRIGHTNESS
if(!msg.payload.brightness && currentState.payload.brightness != 100)
{
patchObject["dimming"] = { brightness: 100 };
}
else if(msg.payload.brightness)
{
patchObject["dimming"] = { brightness: parseInt(msg.payload.brightness) };
}
// SET TRANSITION
patchObject["dynamics"] = { duration: 0 };
// CAN CHANGE COLOR?
if(currentState.payload.xyColor)
{
let XYAlertColor = {};
if(typeof msg.payload.rgb != 'undefined')
{
XYAlertColor = colorUtils.rgbToXy(msg.payload.rgb[0], msg.payload.rgb[1], msg.payload.rgb[2], currentState.info.model.colorGamut);
}
else if(typeof msg.payload.hex != 'undefined')
{
let rgbFromHex = colorUtils.hexRgb((msg.payload.hex).toString());
XYAlertColor = colorUtils.rgbToXy(rgbFromHex[0], rgbFromHex[1], rgbFromHex[2], currentState.info.model.colorGamut);
}
else if(typeof msg.payload.color != 'undefined')
{
if(new RegExp("random|any|whatever").test(msg.payload.color))
{
const randomColor = colorUtils.randomHexColor();
let rgbFromHex = colorUtils.hexRgb(rgbFromHex);
XYAlertColor = colorUtils.rgbToXy(rgbFromHex[0], rgbFromHex[1], rgbFromHex[2], currentState.info.model.colorGamut);
}
else
{
var colorHex = colorUtils.colornames(msg.payload.color);
if(colorHex)
{
let rgbFromHex = colorUtils.hexRgb(colorHex);
XYAlertColor = colorUtils.rgbToXy(rgbFromHex[0], rgbFromHex[1], rgbFromHex[2], currentState.info.model.colorGamut);
}
}
}
else
{
XYAlertColor = colorUtils.rgbToXy(255, 0, 0, currentState.info.model.colorGamut);
}
patchObject["color"] = {
xy: XYAlertColor
};
}
// CHANGE NODE UI STATE
if(config.lightid)
{
scope.status({fill: "grey", shape: "ring", text: "hue-light.node.command"});
}
// APPLY THE EFFECT
async.retry({
times: 3,
errorFilter: function(err) {
return (err.status == 503);
},
interval: function(retryCount) { return retryCount*2000; }
},
function(callback, results)
{
// 1. TURN ON THE LIGHT BULB
bridge.patch("light", tempLightID, patchObject)
.then(function(status) {
// 2. APPLY ALERT EFFECT
const alertEffect = { alert: { action: "breathe" }};
return bridge.patch("light", tempLightID, alertEffect);
})
.then(function(status) {
// 3. RESET PREVIOUS STATE (AFTER X SECONDS)
setTimeout(function()
{
const tempPreviousState = scope.context().get('lightPreviousState');
var tempPreviousStatePatch = {};
tempPreviousStatePatch.dimming = { brightness: tempPreviousState.payload.brightness };
if(tempPreviousState.payload.xyColor)
{
tempPreviousStatePatch.color = { xy: tempPreviousState.payload.xyColor };
}
else if(tempPreviousState.payload.colorTemp)
{
tempPreviousStatePatch.color_temperature = { mirek: tempPreviousState.payload.colorTemp };
}
bridge.patch("light", tempLightID, tempPreviousStatePatch).
then(function(status)
{
return bridge.patch("light", tempLightID, { on: { on: false } })
.then(function() { if(done) { done(); }});
})
.then(function(status) {
if(tempPreviousState.payload.on === true)
{
bridge.patch("light", tempLightID, { on: { on: true } })
.then(function() { if(done) { done(); }});
}
});
}, parseInt(msg.payload.alert) * 1000);
callback(null, true);
})
.catch(function(errors) {
callback(errors, null);
});
},
function(errors, success)
{
if(errors)
{
scope.error(errors);
scope.status({fill: "red", shape: "ring", text: "hue-light.node.error-input"});
}
else if(done)
{
done();
}
});
}
// ANIMATION STARTED?
else if(typeof msg.animation != 'undefined' && msg.animation.status == true && msg.animation.restore == true)
{
// SAVE PREVIOUS STATE
scope.context().set('lightPreviousState', currentState);
}
// ANIMATION STOPPED AND RESTORE ACTIVE?
else if(typeof msg.animation != 'undefined' && msg.animation.status == false && msg.animation.restore == true)
{
const tempPreviousState = scope.context().get('lightPreviousState');
var tempPreviousStatePatch = {};
tempPreviousStatePatch.dimming = { brightness: tempPreviousState.payload.brightness };
if(tempPreviousState.payload.xyColor)
{
tempPreviousStatePatch.color = { xy: tempPreviousState.payload.xyColor };
}
else if(tempPreviousState.payload.colorTemp)
{
tempPreviousStatePatch.color_temperature = { mirek: tempPreviousState.payload.colorTemp };
}
// PATCH!
async.retry({
times: 3,
errorFilter: function(err) {
return (err.status == 503);
},
interval: function(retryCount) { return retryCount*2000; }
},
function(callback, results)
{
bridge.patch("light", tempLightID, tempPreviousStatePatch)
.then(function(status)
{
if(tempPreviousState.payload.on === false)
{
bridge.patch("light", tempLightID, { on: { on: false } })
.then(function() { callback(null, true); });
}
else
{
bridge.patch("light", tempLightID, { on: { on: false } })
.then(function(status) {
callback(null, true);
return bridge.patch("light", tempLightID, { on: { on: true } });
});
}
})
.catch(function(errors) { callback(errors, null); });
},
function(errors, success)
{
if(errors)
{
scope.error(errors);
}
else if(done)
{
done();
}
});
}
// EXTENDED COMMANDS
else
{
// SET LIGHT STATE SIMPLE MODE
if(msg.payload === true||msg.payload === false)
{
if(msg.payload !== currentState.payload.on) { patchObject["on"] = { on: msg.payload }; }
}
// SET LIGHT STATE
if(typeof msg.payload != 'undefined' && typeof msg.payload.on != 'undefined' && (msg.payload.on === true || msg.payload.on === false))
{
if(msg.payload.on !== currentState.payload.on) { patchObject["on"] = { on: msg.payload.on }; }
}
// TOGGLE ON / OFF
if(typeof msg.payload != 'undefined' && typeof msg.payload.toggle != 'undefined')
{
patchObject["on"] = { on: !currentState.payload.on };
}
// SET BRIGHTNESS
if(typeof msg.payload != 'undefined' && typeof msg.payload.brightness != 'undefined')
{
// AUTO BRIGHTNESS BASED ON DAY TIME
if(new RegExp("auto|automatic").test(msg.payload.brightness))
{
let ct = colorUtils.colorTemperature();
let autoBrightness = ((300-ct)/2)+100;
autoBrightness = (autoBrightness > 100) ? 100 : autoBrightness;
autoBrightness = (autoBrightness < 20) ? 20 : autoBrightness;
// SET CALCULATED BRIGHTNESS
patchObject["dimming"] = { brightness: autoBrightness };
}
else
{
if(msg.payload.brightness > 100 || msg.payload.brightness < 0)
{
scope.error("Invalid brightness setting. Only 0 - 100 percent allowed");
return false;
}
else if(msg.payload.brightness == 0)
{
if(currentState.payload.on !== false) { patchObject["on"] = { on: false }; }
}
else
{
patchObject["dimming"] = { brightness: msg.payload.brightness };
}
}
}
else if(typeof msg.payload != 'undefined' && typeof msg.payload.brightnessLevel != 'undefined')
{
if(msg.payload.brightnessLevel > 254 || msg.payload.brightnessLevel < 0)
{
scope.error("Invalid brightness setting. Only 0 - 254 allowed");
return false;
}
else if(msg.payload.brightness == 0)
{
if(currentState.payload.on !== false) { patchObject["on"] = { on: false }; }
}
else
{
patchObject["dimming"] = { brightness: Math.round((100/254)*msg.payload.brightnessLevel) };
}
}
else if(typeof msg.payload != 'undefined' && typeof msg.payload.incrementBrightness != 'undefined')
{
let incrementBy = (isNaN(msg.payload.incrementBrightness)) ? 10 : msg.payload.incrementBrightness;
let targetBrightness = Math.round(currentState.payload.brightness + incrementBy);
targetBrightness = (targetBrightness > 100) ? 100 : targetBrightness;
patchObject["dimming"] = { brightness: targetBrightness };
}
else if(typeof msg.payload != 'undefined' && typeof msg.payload.decrementBrightness != 'undefined')
{
let decrementBy = (isNaN(msg.payload.decrementBrightness)) ? 10 : msg.payload.decrementBrightness;
let targetBrightness = Math.round(currentState.payload.brightness - decrementBy);
targetBrightness = (targetBrightness < 0) ? 0 : targetBrightness;
if(targetBrightness < 1)
{
if(currentState.payload.on !== false) { patchObject["on"] = { on: false }; }
}
patchObject["dimming"] = { brightness: targetBrightness };
}
// SET HUMAN READABLE COLOR OR RANDOM
if(typeof msg.payload != 'undefined' && typeof msg.payload.color != 'undefined' && typeof currentState.payload.xyColor != 'undefined')
{
let XYAlertColor = {};
if(new RegExp("random|any|whatever").test(msg.payload.color))
{
const randomColor = colorUtils.randomHexColor();
let rgbFromHex = colorUtils.hexRgb(randomColor);
XYAlertColor = colorUtils.rgbToXy(rgbFromHex[0], rgbFromHex[1], rgbFromHex[2], currentState.info.model.colorGamut);
}
else
{
var colorHex = colorUtils.colornames(msg.payload.color);
if(colorHex)
{
let rgbFromHex = colorUtils.hexRgb(colorHex);
XYAlertColor = colorUtils.rgbToXy(rgbFromHex[0], rgbFromHex[1], rgbFromHex[2], currentState.info.model.colorGamut);
}
}
patchObject["color"] = {
xy: XYAlertColor
};
}
// SET HEX COLOR
if(typeof msg.payload != 'undefined' && typeof msg.payload.hex != 'undefined' && typeof currentState.payload.xyColor != 'undefined')
{
let rgbFromHex = colorUtils.hexRgb((msg.payload.hex).toString());
patchObject["color"] = {
xy: colorUtils.rgbToXy(rgbFromHex[0], rgbFromHex[1], rgbFromHex[2], currentState.info.model.colorGamut)
};
}
// SET RGB COLOR
if(typeof msg.payload != 'undefined' && typeof msg.payload.rgb != 'undefined' && typeof currentState.payload.xyColor != 'undefined' && msg.payload.rgb.length === 3)
{
patchObject["color"] = {
xy: colorUtils.rgbToXy(msg.payload.rgb[0], msg.payload.rgb[1], msg.payload.rgb[2], currentState.info.model.colorGamut)
};
}
// SET XY COLOR
if(typeof msg.payload != 'undefined' && typeof msg.payload.xyColor != 'undefined' && typeof currentState.payload.xyColor != 'undefined')
{
patchObject["color"] = {
xy: msg.payload.xyColor
};
}
// MIX CURRENT COLOR WITH NEW COLOR
if(typeof msg.payload != 'undefined' && typeof msg.payload.mixColor != 'undefined' && typeof currentState.payload.xyColor != 'undefined')
{
let RGBColor = [];
if(typeof msg.payload.mixColor.color != 'undefined')
{
if(new RegExp("random|any|whatever").test(msg.payload.mixColor.color))
{
RGBColor = colorUtils.hexRgb(colorUtils.randomHexColor());
}
else
{
var colorHex = colorUtils.colornames(msg.payload.mixColor.color);
if(colorHex)
{
RGBColor = colorUtils.hexRgb(colorHex);
}
}
}
else if(typeof msg.payload.mixColor.rgb != 'undefined')
{
RGBColor = msg.payload.mixColor.rgb;
}
else if(typeof msg.payload.mixColor.hex != 'undefined')
{
RGBColor = colorUtils.hexRgb((msg.payload.mixColor.hex).toString());
}
else if(typeof msg.payload.mixColor.xyColor != 'undefined')
{
RGBColor = colorUtils.xyBriToRgb(msg.payload.mixColor.xyColor.x, msg.payload.mixColor.xyColor.y, 100);
}
// GET MIXING AMOUNT
let mixingAmount = 0.5;
if(typeof msg.payload.mixColor.amount != 'undefined' && msg.payload.mixColor.amount > 0 && msg.payload.mixColor.amount <= 100)
{
mixingAmount = msg.payload.mixColor.amount/100;
}
// HAS CURRENT COLOR SETTING?
if(currentState.payload.rgb !== false)
{
let mixedRGBColor = colorUtils.mixColors(currentState.payload.rgb, RGBColor, mixingAmount);
patchObject["color"] = {
xy: colorUtils.rgbToXy(mixedRGBColor[0], mixedRGBColor[1], mixedRGBColor[2], currentState.info.model.colorGamut)
};
}
else
{
patchObject["color"] = {
xy: colorUtils.rgbToXy(RGBColor[0], RGBColor[1], RGBColor[2], currentState.info.model.colorGamut)
};
}
}
// SET COLOR TEMPERATURE
if(typeof msg.payload != 'undefined' && typeof msg.payload.colorTemp != 'undefined' && typeof currentState.payload.colorTemp != 'undefined')
{
// DETERMINE IF AUTOMATIC, WARM, COLD, INT
if(!isNaN(msg.payload.colorTemp))
{
let colorTemp = parseInt(msg.payload.colorTemp);
if(colorTemp >= 153 && colorTemp <= 500)
{
patchObject["color_temperature"] = { mirek: colorTemp };
}
else
{
scope.error("Invalid color temprature. Only 153 - 500 allowed");
return false;
}
}
else if(msg.payload.colorTemp == "cold")
{
patchObject["color_temperature"] = { mirek: 153 };
}
else if(msg.payload.colorTemp == "normal")
{
patchObject["color_temperature"] = { mirek: 240 };
}
else if(msg.payload.colorTemp == "warm")
{
patchObject["color_temperature"] = { mirek: 400 };
}
else if(msg.payload.colorTemp == "hot")
{
patchObject["color_temperature"] = { mirek: 500 };
}
else
{
// SET TEMPERATURE
patchObject["color_temperature"] = { mirek: colorUtils.colorTemperature() };
}
}
else if(typeof msg.payload != 'undefined' && typeof msg.payload.incrementColorTemp != 'undefined' && typeof currentState.payload.colorTemp != 'undefined')
{
let incrementBy = (isNaN(msg.payload.incrementColorTemp)) ? 50 : msg.payload.incrementColorTemp;
let targetColorTemperature = currentState.payload.colorTemp + parseInt(incrementBy);
targetColorTemperature = (targetColorTemperature > 500) ? 500 : targetColorTemperature;
targetColorTemperature = (targetColorTemperature < 153) ? 153 : targetColorTemperature;
// SET TEMPERATURE
patchObject["color_temperature"] = { mirek: targetColorTemperature };
}
else if(typeof msg.payload != 'undefined' && typeof msg.payload.decrementColorTemp != 'undefined' && typeof currentState.payload.colorTemp != 'undefined')
{
let decrementBy = (isNaN(msg.payload.decrementColorTemp)) ? 50 : msg.payload.decrementColorTemp;
let targetColorTemperature = currentState.payload.colorTemp - parseInt(decrementBy);
targetColorTemperature = (targetColorTemperature > 500) ? 500 : targetColorTemperature;
targetColorTemperature = (targetColorTemperature < 153) ? 153 : targetColorTemperature;
// SET TEMPERATURE
patchObject["color_temperature"] = { mirek: targetColorTemperature };
}
// SET TRANSITION TIME
if(typeof msg.payload != 'undefined' && typeof msg.payload.transitionTime != 'undefined')
{
let targetTransitionTime = parseFloat(msg.payload.transitionTime)*1000;
targetTransitionTime = (targetTransitionTime > 6000000) ? 6000000 : targetTransitionTime;
targetTransitionTime = (targetTransitionTime < 0) ? 0 : targetTransitionTime;
patchObject["dynamics"] = { duration: targetTransitionTime };
}
// SET DOMINANT COLORS FROM IMAGE
if(typeof msg.payload != 'undefined' && typeof msg.payload.image != 'undefined' && (typeof currentState.payload.xyColor != 'undefined' || typeof currentState.payload.gradient != 'undefined'))
{
let colors = await colorUtils.getColors(msg.payload.image);
if(colors.length > 0)
{
let colorsHEX = colors.map(color => color.hex());
// SET MULTIPLE COLORS ON SUPPORTED LIGHTS
if(typeof currentState.payload.gradient != 'undefined')
{
let XYColorSet = [];
for (var i = 0; i < currentState.payload.gradient.totalColors; i++)
{
if(typeof colorsHEX[i] != 'undefined')
{
let rgbFromHex = colorUtils.hexRgb(colorsHEX[i]);
XYColorSet.push({
color: { xy: colorUtils.rgbToXy(rgbFromHex[0], rgbFromHex[1], rgbFromHex[2], currentState.info.model.colorGamut) }
});
}
}
if(XYColorSet.length > 0)
{
patchObject["gradient"] = { points: XYColorSet };
}
}
// SET SINGLE COLOR
else
{
let rgbFromHex = colorUtils.hexRgb(colorsHEX[0]);
patchObject["color"] = {
xy: colorUtils.rgbToXy(rgbFromHex[0], rgbFromHex[1], rgbFromHex[2], currentState.info.model.colorGamut)
};
}
}
}
// SET SATURATION
if(typeof msg.payload != 'undefined' && typeof msg.payload.saturation != 'undefined' && typeof currentState.payload.xyColor != 'undefined')
{
if(msg.payload.saturation > 100 || msg.payload.saturation < 0)
{
scope.error("Invalid saturation setting. Only 0 - 100 allowed");
return false;
}
else
{
let currentColor = patchObject["color"] ? colorUtils.xyBriToRgb(patchObject["color"].xy.x, patchObject["color"].xy.y, 100) : currentState.payload.rgb;
let currentColorInHSL = colorUtils.rgbToHsl(currentColor[0], currentColor[1], currentColor[2]);
let saturationFactor = (msg.payload.saturation/100);
// CHANGE SATURATION
currentColorInHSL[1] = currentColorInHSL[1]*saturationFactor;
// CONVERT BACK TO RGB
let saturatedRGBColor = colorUtils.hslToRgb(currentColorInHSL[0], currentColorInHSL[1], currentColorInHSL[2]);
patchObject["color"] = {
xy: colorUtils.rgbToXy(saturatedRGBColor[0], saturatedRGBColor[1], saturatedRGBColor[2], currentState.info.model.colorGamut)
};
}
}
// SET GRADIENT
if(typeof msg.payload != 'undefined' && typeof msg.payload.gradient != 'undefined' && typeof currentState.payload.gradient != 'undefined')
{
let XYColorSet = [];
if(typeof msg.payload.gradient.hex != 'undefined' && Array.isArray(msg.payload.gradient.hex) == true)
{
XYColorSet = [];
for(let oneColor in msg.payload.gradient.hex)
{
let rgbFromHex = colorUtils.hexRgb(oneColor);
XYColorSet.push({
color: { xy: colorUtils.rgbToXy(rgbFromHex[0], rgbFromHex[1], rgbFromHex[2], currentState.info.model.colorGamut) }
});
}
}
if(typeof msg.payload.gradient.rgb != 'undefined' && Array.isArray(msg.payload.gradient.rgb) == true)
{
XYColorSet = [];
for(let oneColor in msg.payload.gradient.rgb)
{
XYColorSet.push({
color: { xy: colorUtils.rgbToXy(oneColor[0], oneColor[1], oneColor[2], currentState.info.model.colorGamut) }
});
}
}
if(typeof msg.payload.gradient.xyColor != 'undefined' && Array.isArray(msg.payload.gradient.xyColor) == true)
{
XYColorSet = [];
for(let oneColor in msg.payload.gradient.xyColor)
{
XYColorSet.push({
color: { xy: oneColor }
});
}
}
patchObject["gradient"] = { points: XYColorSet };
}
//
// SHOULD PATCH?
if(Object.values(patchObject).length > 0)
{
// IS FOR LATER?
if(currentState.payload.on === false || currentState.payload.reachable === false)
{
if(!patchObject["on"] || !patchObject["on"]["on"])
{
scope.futurePatchState = merge.deep(scope.futurePatchState, patchObject);
return false;
}
}
// CHANGE NODE UI STATE
if(config.lightid)
{
scope.status({fill: "grey", shape: "ring", text: "hue-light.node.command"});
}
// PATCH!
async.retry({
times: 3,
errorFilter: function(err) {
return (err.status == 503);
},
interval: function(retryCount) { return retryCount*2000; }
},
function(callback, results)
{
bridge.patch("light", tempLightID, patchObject)
.then(function() { callback(null, true); })
.catch(function(errors) { callback(errors, null); });
},
function(errors, success)
{
if(errors)
{
scope.error(errors);
}
else if(done)
{
done();
}
});
}
else
{
// JUST SEND CURRENT STATE
if(scope.lastCommand !== null)
{
currentState.command = scope.lastCommand;
}
// SEND STATE
scope.send(currentState);
// RESET LAST COMMAND
scope.lastCommand = null;
if(done) { done(); }
}
}
}
}
RED.nodes.registerType("hue-light", HueLight);
}