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.
201 lines (173 loc) • 4.79 kB
JavaScript
module.exports = function(RED)
{
"use strict";
function HueMotion(config)
{
RED.nodes.createNode(this, config);
const scope = this;
const bridge = RED.nodes.getNode(config.bridge);
const async = require('async');
// SAVE LAST COMMAND
this.lastCommand = null;
//
// CHECK CONFIG
if(bridge == null)
{
this.status({fill: "red", shape: "ring", text: "hue-motion.node.not-configured"});
return false;
}
//
// UNIVERSAL MODE?
if(!config.sensorid)
{
this.status({fill: "grey", shape: "dot", text: "hue-motion.node.universal"});
}
//
// UPDATE STATE
if(config.sensorid)
{
this.status({fill: "grey", shape: "dot", text: "hue-motion.node.no-motion"});
}
//
// SUBSCRIBE TO UPDATES FROM THE BRIDGE
bridge.subscribe("motion", config.sensorid, function(info)
{
let currentState = bridge.get("motion", info.id);
// 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.sensorid)
{
if(currentState.payload.reachable == false)
{
scope.status({fill: "red", shape: "ring", text: "hue-motion.node.not-reachable"});
}
else if(currentState.payload.active == true)
{
if(currentState.payload.motion)
{
scope.status({fill: "green", shape: "dot", text: "hue-motion.node.motion"});
}
else
{
scope.status({fill: "grey", shape: "dot", text: "hue-motion.node.activated"});
}
}
else if(currentState.payload.active == false)
{
scope.status({fill: "red", shape: "ring", text: "hue-motion.node.deactivated"});
}
}
}
});
//
// CONTROL SENSOR
this.on('input', function(msg, send, done)
{
// 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 tempSensorID = (!config.sensorid && typeof msg.topic != 'undefined' && bridge.validResourceID.test(msg.topic) === true) ? msg.topic : config.sensorid;
if(!tempSensorID)
{
scope.error("Please submit a valid sensor ID.");
return false;
}
let currentState = bridge.get("motion", tempSensorID);
if(!currentState)
{
scope.error("The sensor 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;
}
// 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;
}
// TURN ON / OFF
if((msg.payload === true || msg.payload === false) && (msg.payload != currentState.payload.active))
{
// PREPARE PATCH
patchObject["enabled"] = (msg.payload == true);
}
//
// SHOULD PATCH?
if(Object.values(patchObject).length > 0)
{
// CHANGE NODE UI STATE
if(config.sensorid)
{
scope.status({fill: "grey", shape: "ring", text: "hue-motion.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("motion", tempSensorID, patchObject)
.then(function() { if(done) { callback(null, true); }})
.catch(function(errors) { callback(errors, null); });
},
function(errors, success)
{
if(errors)
{
scope.error(errors);
}
else if(done)
{
done();
}
});
}
else
{
// 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(); }
}
});
}
RED.nodes.registerType("hue-motion", HueMotion);
}