smart-nodes
Version:
Controls light, shutters and more. Includes common used logic and statistic nodes to control your home.
272 lines (220 loc) • 9.57 kB
JavaScript
module.exports = function (RED)
{
"use strict";
function ModeSelectorNode(config)
{
const node = this;
RED.nodes.createNode(node, config);
// ###################
// # Class constants #
// ###################
// #######################
// # Global help objects #
// #######################
const smart_context = require("../persistence.js")(RED);
const helper = require("../smart_helper.js");
// #####################
// # persistent values #
// #####################
var node_settings = helper.cloneObject({
last_mode: null,
config_change_date: config.config_change_date,
}, smart_context.get(node.id, config.config_change_date));
// ##################
// # Dynamic config #
// ##################
let mode_items = (config.mode_items || [{ name: "NO_MODES_DEFINED" }]).map(m => m.name);
let previous_mode = null;
let update_only_changed_outputs = config.update_only_changed_outputs ?? true;
// ##################
// # Runtime values #
// ##################
// ###############
// # Node events #
// ###############
node.on("input", function (msg)
{
handleTopic(msg);
setStatus();
smart_context.set(node.id, node_settings);
});
node.on("close", function ()
{
});
// #####################
// # Private functions #
// #####################
// This is the main function which handles all topics that was received.
let handleTopic = msg =>
{
let [real_topic, new_mode] = msg.topic.split("#");
switch (real_topic)
{
case "debug":
helper.nodeDebug(node, {
node_settings,
mode_items,
previous_mode,
update_only_changed_outputs
});
break;
case "set_mode":
{
if (msg.payload == null)
return;
if (typeof msg.payload === "boolean" || msg.payload === "on" || msg.payload === "off")
{
msg.payload = helper.toBool(msg.payload);
// Syntax: set_mode#MODE_NAME and payload is boolean
// If payload is true, set to MODE_NAME
// if payload is false, set to default mode
let mode_from_topic = new_mode;
if (msg.payload === false)
new_mode = mode_items[0];
// make sure the sended topics exists
let selectedIndex = mode_items.indexOf(mode_from_topic);
if (selectedIndex === -1)
{
node.warn("set_mode: invalid mode: '" + mode_from_topic + "' not defined");
return;
}
selectedIndex = mode_items.indexOf(new_mode);
if (selectedIndex === -1)
{
node.warn("set_mode: invalid mode: '" + new_mode + "' not defined");
return;
}
// If a mode is turned off that wasn't active before, do nothing
if (msg.payload === false)
{
// if the given mode_from_topic is turned off but it never was active, now
// ignore that
if (node_settings.last_mode != mode_from_topic)
return;
// don't send turn off again
previous_mode = null;
}
// Don't allow turning off the default mode
if (msg.payload === false && mode_from_topic == mode_items[0])
{
// turn default on again
sendCurrentMode();
return;
}
// No change, don't send anything
if (node_settings.last_mode == new_mode)
return;
// if a mode is activated, now
if (msg.payload === true)
{
// don't send turn on again
node_settings.last_mode = null;
}
else
{
// set as current mode
node_settings.last_mode = new_mode;
}
sendCurrentMode();
node_settings.last_mode = new_mode;
previous_mode = new_mode;
smart_context.set(node.id, node_settings);
}
else if (typeof msg.payload === "string")
{
if (new_mode != null)
{
node.warn("set_mode: when using mode in topic, payload has to be boolean");
return;
}
let selectedIndex = mode_items.indexOf(msg.payload);
if (selectedIndex === -1)
{
node.warn("set_mode: invalid mode: '" + msg.payload + "' not defined");
return;
}
// No change, don't send anything
if (node_settings.last_mode == msg.payload)
return;
node_settings.last_mode = msg.payload;
smart_context.set(node.id, node_settings);
sendCurrentMode();
}
else
{
node.warn("set_mode: invalid payload, has to be string or boolean");
break;
}
}
break;
case "toggle_mode":
{
if (msg.payload == null)
return;
let new_mode = msg.payload;
let selectedIndex = mode_items.indexOf(new_mode);
if (selectedIndex === -1)
{
node.warn("set_mode: invalid payload, mode '" + new_mode + "' not found");
break;
}
// No change, go to default mode
if (node_settings.last_mode == new_mode)
new_mode = mode_items[0];
// No change, don't send anything
if (node_settings.last_mode == new_mode)
break;
node_settings.last_mode = new_mode;
smart_context.set(node.id, node_settings);
sendCurrentMode();
}
break;
case "refresh":
sendCurrentMode(true);
break;
default:
break;
}
}
let sendCurrentMode = (force_send_all = false) =>
{
let result;
if (!force_send_all && update_only_changed_outputs)
{
result = mode_items.map((m, idx) =>
{
if (m != node_settings.last_mode && m != previous_mode)
return null;
return { payload: (m === node_settings.last_mode) };
});
}
else
{
result = mode_items.map((m, idx) =>
{
return { payload: (m === node_settings.last_mode) };
});
}
previous_mode = node_settings.last_mode;
node.send(result);
}
let setStatus = () =>
{
node.status({ fill: "green", shape: "dot", text: helper.getCurrentTimeForStatus() + ": " + node_settings.last_mode });
}
if (node_settings.last_mode == null && mode_items.length > 0)
{
node_settings.last_mode = mode_items[0];
smart_context.set(node.id, node_settings);
}
if (config.resend_on_start)
{
setTimeout(() =>
{
sendCurrentMode();
}, 10000);
}
setStatus();
}
RED.nodes.registerType("smart_mode-selector", ModeSelectorNode);
}