UNPKG

node-red-smithtek-hysteresis

Version:

Adds hysteresis function to node-red

398 lines (384 loc) 13.7 kB
module.exports = function (RED) { "use strict"; function HysteresisNode(config) { RED.nodes.createNode(this, config); this.ThresholdType = config.ThresholdType || "fixed"; this.ThresholdRising = config.ThresholdRising; this.ThresholdFalling = config.ThresholdFalling; this.TopicThreshold = config.TopicThreshold; this.TopicCurrent = config.TopicCurrent; this.ThresholdDeltaRising = config.ThresholdDeltaRising; this.ThresholdDeltaFalling = config.ThresholdDeltaFalling; this.InitialMessage = config.InitialMessage; this.OutRisingType = config.OutRisingType; this.OutRisingValue = config.OutRisingValue; this.OutFallingType = config.OutFallingType; this.OutFallingValue = config.OutFallingValue; this.OutTopicType = config.OutTopicType; this.OutTopicValue = config.OutTopicValue || ""; var nodeContext = this.context(); var statusTimeout = null; if (this.OutRisingType !== "pay") { if (this.OutRisingType === "num" && !Number.isNaN(this.OutRisingValue)) { this.OutRisingValue = Number.parseFloat(this.OutRisingValue); } else if ( this.OutRisingType === "bool" && (this.OutRisingValue === "true" || this.OutRisingValue === "false") ) { this.OutRisingValue === "true" ? (this.OutRisingValue = true) : (this.OutRisingValue = false); } else if (this.OutRisingValue === "null") { this.OutRisingType = "null"; this.OutRisingValue = null; } else { this.OutRisingValue = String(this.OutRisingValue); } } if (this.OutFallingType !== "pay") { if ( this.OutFallingType === "num" && !Number.isNaN(this.OutFallingValue) ) { this.OutFallingValue = Number.parseFloat(this.OutFallingValue); } else if ( this.OutFallingType === "bool" && (this.OutFallingValue === "true" || this.OutFallingValue === "false") ) { this.OutFallingValue === "true" ? (this.OutFallingValue = true) : (this.OutFallingValue = false); } else if (this.OutFallingValue === "null") { this.OutFallingType = "null"; this.OutFallingValue = null; } else { this.OutFallingValue = String(this.OutFallingValue); } } // eslint-disable-next-line prefer-const let node = this; let TriggerValueRising = ""; let TriggerValueFalling = ""; // if (this.ThresholdType === "fixed") { // TriggerValueRising = Number.parseFloat(this.ThresholdRising); // TriggerValueFalling = Number.parseFloat(this.ThresholdFalling); // } TriggerValueRising = nodeContext.get("TriggerValueRising") || ""; TriggerValueFalling = nodeContext.get("TriggerValueFalling") || ""; // clear direction flag node.direction = ""; // Set initial status setInitialStatus(); function setInitialStatus() { if ("" === TriggerValueRising && "" === TriggerValueFalling) { node.status({ fill: "red", shape: "ring", text: "Thresholds missing", }); } else if ("" === TriggerValueRising) { node.status({ fill: "red", shape: "ring", text: "Upper Thresholds missing", }); } else if ("" === TriggerValueFalling) { node.status({ fill: "red", shape: "ring", text: "Lower Thresholds missing", }); } else { node.status({ fill: "yellow", shape: "ring", text: TriggerValueFalling + "/--/" + TriggerValueRising, }); } } function setWarningStatus(status) { if ("upper" === status) { node.status({ fill: "yellow", shape: "ring", text: "Upper Thresholds must equal or greater than Lower Thresholds", }); } else if ("lower" === status) { node.status({ fill: "yellow", shape: "ring", text: "Lower Thresholds must equal or less than Upper Thresholds", }); } statusTimeout = setTimeout(function () { setInitialStatus(); }, 2000); } this.on("input", function (msg) { if ( Object.prototype.hasOwnProperty.call(msg, "payload") && Object.prototype.hasOwnProperty.call(msg.payload, "high") ) { if ( "" != TriggerValueFalling && msg.payload.high < TriggerValueFalling ) { setWarningStatus("upper"); } else { TriggerValueRising = msg.payload.high; nodeContext.set("TriggerValueRising", msg.payload.high); setInitialStatus(); } } else if ( Object.prototype.hasOwnProperty.call(msg, "payload") && Object.prototype.hasOwnProperty.call(msg.payload, "low") ) { if ("" != TriggerValueRising && msg.payload.low > TriggerValueRising) { setWarningStatus("lower"); } else { TriggerValueFalling = msg.payload.low; nodeContext.set("TriggerValueFalling", msg.payload.low); setInitialStatus(); } } else { // original msg object const msgNew = RED.util.cloneMessage(msg); // set topic if (this.OutTopicType === "str") { msgNew.topic = this.OutTopicValue; } if ( (Object.prototype.hasOwnProperty.call(msg, "payload") && this.ThresholdType === "fixed" && !Number.isNaN(msg.payload)) || (Object.prototype.hasOwnProperty.call(msg, "payload") && this.ThresholdType === "dynamic" && msg.topic === this.TopicCurrent && TriggerValueRising && !Number.isNaN(msg.payload)) ) { const CurrentValue = Number.parseFloat(msg.payload); // Cover the case where no initial values are known if ( this.InitialMessage && node.direction === "" && !Number.isNaN(CurrentValue) ) { if (CurrentValue >= Number.parseFloat(TriggerValueRising)) { msgNew.payload = this.OutRisingType === "pay" ? msgNew.payload : this.OutRisingValue; msgNew.hystdirection = "initial high"; node.send(msgNew); node.direction = "high"; node.status({ fill: "green", shape: "dot", text: TriggerValueFalling + "/" + CurrentValue + "/" + TriggerValueRising + " (initial high band)", }); } else if (CurrentValue <= TriggerValueFalling) { msgNew.payload = this.OutFallingType === "pay" ? msgNew.payload : this.OutFallingValue; msgNew.hystdirection = "initial low"; node.send(msgNew); node.direction = "low"; node.status({ fill: "blue", shape: "dot", text: TriggerValueFalling + "/" + CurrentValue + "/" + TriggerValueRising + " (initial low band)", }); } // Last value known. Work as hysteresis } else if (!Number.isNaN(CurrentValue) && node.LastValue) { // rising if ( CurrentValue > node.LastValue && CurrentValue >= TriggerValueRising && node.direction !== "high" ) { msgNew.payload = this.OutRisingType === "pay" ? msgNew.payload : this.OutRisingValue; msgNew.hystdirection = "high"; node.send(msgNew); node.direction = "high"; node.status({ fill: "green", shape: "dot", text: TriggerValueFalling + "/" + CurrentValue + "/" + TriggerValueRising + " (high band)", }); // falling } else if ( CurrentValue < node.LastValue && CurrentValue <= TriggerValueFalling && node.direction !== "low" ) { msgNew.payload = this.OutFallingType === "pay" ? msgNew.payload : this.OutFallingValue; msgNew.hystdirection = "low"; node.send(msgNew); node.direction = "low"; node.status({ fill: "blue", shape: "dot", text: TriggerValueFalling + "/" + CurrentValue + "/" + TriggerValueRising + " (low band)", }); } else if ( CurrentValue > node.LastValue && CurrentValue >= TriggerValueRising && node.direction === "high" ) { node.status({ fill: "green", shape: "dot", text: TriggerValueFalling + "/" + CurrentValue + "/" + TriggerValueRising + " (high band rising)", }); } else if ( CurrentValue < node.LastValue && CurrentValue >= TriggerValueRising && node.direction === "high" ) { node.status({ fill: "green", shape: "dot", text: TriggerValueFalling + "/" + CurrentValue + "/" + TriggerValueRising + " (high band falling)", }); } else if ( CurrentValue > node.LastValue && CurrentValue > TriggerValueFalling && CurrentValue < TriggerValueRising && node.direction === "high" ) { node.status({ fill: "green", shape: "ring", text: TriggerValueFalling + "/" + CurrentValue + "/" + TriggerValueRising + " (high dead band rising)", }); } else if ( CurrentValue < node.LastValue && CurrentValue > TriggerValueFalling && CurrentValue < TriggerValueRising && node.direction === "high" ) { node.status({ fill: "green", shape: "ring", text: TriggerValueFalling + "/" + CurrentValue + "/" + TriggerValueRising + " (high dead band falling)", }); } else if ( CurrentValue > node.LastValue && CurrentValue > TriggerValueFalling && CurrentValue < TriggerValueRising && node.direction === "low" ) { node.status({ fill: "blue", shape: "ring", text: TriggerValueFalling + "/" + CurrentValue + "/" + TriggerValueRising + " (low dead band rising)", }); } else if ( CurrentValue < node.LastValue && CurrentValue > TriggerValueFalling && CurrentValue < TriggerValueRising && node.direction === "low" ) { node.status({ fill: "blue", shape: "ring", text: TriggerValueFalling + "/" + CurrentValue + "/" + TriggerValueRising + " (low dead band falling)", }); } else if ( CurrentValue > node.LastValue && CurrentValue <= TriggerValueFalling && node.direction === "low" ) { node.status({ fill: "blue", shape: "dot", text: TriggerValueFalling + "/" + CurrentValue + "/" + TriggerValueRising + " (low band rising)", }); } else if ( CurrentValue < node.LastValue && CurrentValue <= TriggerValueFalling && node.direction === "low" ) { node.status({ fill: "blue", shape: "dot", text: TriggerValueFalling + "/" + CurrentValue + "/" + TriggerValueFalling + " (low band falling)", }); } } else {} node.LastValue = CurrentValue; } } }); } RED.nodes.registerType("smithtek_node_red_hysteresis", HysteresisNode); };