diff --git a/README.md b/README.md index 33bd35f..9d98677 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,10 @@ Dynamic thresholds expect following settings: * `Topic Current` specifies a message topic under which the current values are send. This values are then matched against the respective thresholds. * `Hysteresis+` is the upper delta for the triggering point. The `msg.payload` from `Threshold Topic` plus the `Hysteresis+` value equals the `Upper Threshold`. * `Hysteresis-` is the lower delta for the triggering point. The `msg.payload` from `Threshold Topic` minus the `Hysteresis-` value equals the `Lower Threshold`. +* `Raise error on missing threshold` will create an error object in case the threshold is missing and `Topic Current` value arrives. This can be handled via a `Catch` node. Also see the included examples -**Note:** The values set in dynamic mode will not survive a node-red deploy or restart. +**Note:** +The values set in dynamic mode will typically not survive a node-red deploy or restart. With version **0.3.0** the node will save all relevant settings as context. Using a persistent context store, e.g. file, will allow the node to recover these values and continue from there. ## Send initial message @@ -40,6 +42,9 @@ After starting node-red or deploying the flow, the hysteresis node does not know In the node Output settings either `Original Payload / Topic` or custom values can be specified. +## Node Status +The node makes extensive use of status information. These can be used to react on status changes with the `Status` node. Also see the included examples for a how-to. + ## Use cases In control systems, hysteresis can be used to filter signals so that the output reacts less rapidly than it otherwise would, by taking recent history into account. For example, a thermostat controlling a heater may switch the heater on when the temperature drops below A, but not turn it off until the temperature rises above B. For instance, if one wishes to maintain a temperature of 20 °C then one might set the thermostat to turn the heater on when the temperature drops to below 18 °C and off when the temperature exceeds 22 °C. diff --git a/examples/Dynamic_Threshold_Error.json b/examples/Dynamic_Threshold_Error.json new file mode 100644 index 0000000..49a3f9d --- /dev/null +++ b/examples/Dynamic_Threshold_Error.json @@ -0,0 +1,387 @@ +[ + { + "id": "a40f8b0e.e67288", + "type": "hysteresis", + "z": "af01642a.58d1f", + "name": "", + "ThresholdType": "dynamic", + "ThresholdRising": "", + "ThresholdFalling": "", + "TopicThreshold": "my_dyn_threshold", + "TopicCurrent": "my_current_value", + "ThresholdDeltaRising": "5", + "ThresholdDeltaFalling": "5", + "InitialMessage": true, + "OutRisingType": "str", + "OutRisingValue": "upper exceeded", + "OutFallingType": "str", + "OutFallingValue": "lower underran", + "OutTopicType": "str", + "OutTopicValue": "fgfg", + "DynRaiseError": true, + "x": 1020, + "y": 420, + "wires": [ + [ + "1da1a3c2.a1807c" + ] + ] + }, + { + "id": "1da1a3c2.a1807c", + "type": "debug", + "z": "af01642a.58d1f", + "name": "", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "x": 1210, + "y": 420, + "wires": [] + }, + { + "id": "3431c2f0.8275ee", + "type": "inject", + "z": "af01642a.58d1f", + "name": "", + "props": [ + { + "p": "payload" + }, + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "my_dyn_threshold", + "payload": "20", + "payloadType": "num", + "x": 760, + "y": 140, + "wires": [ + [ + "a40f8b0e.e67288" + ] + ] + }, + { + "id": "f2d9f28f.1f254", + "type": "inject", + "z": "af01642a.58d1f", + "name": "", + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "my_current_value", + "payload": "20", + "payloadType": "num", + "x": 760, + "y": 380, + "wires": [ + [ + "a40f8b0e.e67288" + ] + ] + }, + { + "id": "3fb90d36.745602", + "type": "inject", + "z": "af01642a.58d1f", + "name": "", + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "my_current_value", + "payload": "26", + "payloadType": "num", + "x": 760, + "y": 540, + "wires": [ + [ + "a40f8b0e.e67288" + ] + ] + }, + { + "id": "9d76d540.8d13d", + "type": "inject", + "z": "af01642a.58d1f", + "name": "", + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "my_current_value", + "payload": "14", + "payloadType": "num", + "x": 760, + "y": 300, + "wires": [ + [ + "a40f8b0e.e67288" + ] + ] + }, + { + "id": "9ec83592.e4e848", + "type": "inject", + "z": "af01642a.58d1f", + "name": "", + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "my_current_value", + "payload": "13", + "payloadType": "num", + "x": 760, + "y": 260, + "wires": [ + [ + "a40f8b0e.e67288" + ] + ] + }, + { + "id": "d40c42cf.20592", + "type": "inject", + "z": "af01642a.58d1f", + "name": "", + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "my_current_value", + "payload": "12", + "payloadType": "num", + "x": 760, + "y": 220, + "wires": [ + [ + "a40f8b0e.e67288" + ] + ] + }, + { + "id": "7ef42e7c.a6ecb8", + "type": "inject", + "z": "af01642a.58d1f", + "name": "", + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "my_current_value", + "payload": "27", + "payloadType": "num", + "x": 760, + "y": 580, + "wires": [ + [ + "a40f8b0e.e67288" + ] + ] + }, + { + "id": "4234989d.33142", + "type": "inject", + "z": "af01642a.58d1f", + "name": "", + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "my_current_value", + "payload": "28", + "payloadType": "num", + "x": 760, + "y": 620, + "wires": [ + [ + "a40f8b0e.e67288" + ] + ] + }, + { + "id": "7eae1ec9.145348", + "type": "inject", + "z": "af01642a.58d1f", + "name": "", + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "my_current_value", + "payload": "21", + "payloadType": "num", + "x": 760, + "y": 420, + "wires": [ + [ + "a40f8b0e.e67288" + ] + ] + }, + { + "id": "f728e075.542f9", + "type": "inject", + "z": "af01642a.58d1f", + "name": "", + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "my_current_value", + "payload": "22", + "payloadType": "num", + "x": 760, + "y": 460, + "wires": [ + [ + "a40f8b0e.e67288" + ] + ] + }, + { + "id": "e608dc44.6d38f8", + "type": "comment", + "z": "af01642a.58d1f", + "name": "Dynamic Threshold", + "info": "Set the dynamic threshold.\nUntil this is injected, the hysteresis node will \nshow the warning \"Thresholds missing\" ", + "x": 730, + "y": 100, + "wires": [] + }, + { + "id": "384db5a.994a84a", + "type": "comment", + "z": "af01642a.58d1f", + "name": "Low Band Values", + "info": "Output will only be generated when the lower trigger \nthreshold is reached for the first time", + "x": 720, + "y": 180, + "wires": [] + }, + { + "id": "eb4a514d.09be18", + "type": "comment", + "z": "af01642a.58d1f", + "name": "Dead Band Values", + "info": "In the \"dead band\" no output will be generated until\nthe opposite trigger level is reached", + "x": 730, + "y": 340, + "wires": [] + }, + { + "id": "6befbefb.0b554", + "type": "comment", + "z": "af01642a.58d1f", + "name": "High Band Values", + "info": "Output will only be generated when the upper trigger \nthreshold is reached for the first time", + "x": 730, + "y": 500, + "wires": [] + }, + { + "id": "f0041ca.0231a6", + "type": "debug", + "z": "af01642a.58d1f", + "name": "", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "true", + "targetType": "full", + "statusVal": "", + "statusType": "auto", + "x": 1190, + "y": 580, + "wires": [] + }, + { + "id": "eeadb5c9.884858", + "type": "catch", + "z": "af01642a.58d1f", + "name": "", + "scope": [ + "a40f8b0e.e67288" + ], + "uncaught": false, + "x": 1020, + "y": 540, + "wires": [ + [ + "f0041ca.0231a6", + "2f92c76e.42dca" + ] + ] + }, + { + "id": "85e96cbf.e608d8", + "type": "change", + "z": "af01642a.58d1f", + "name": "Set Threshold", + "rules": [ + { + "t": "set", + "p": "topic", + "pt": "msg", + "to": "my_dyn_threshold", + "tot": "str" + }, + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "20", + "tot": "num" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 1380, + "y": 540, + "wires": [ + [ + "a40f8b0e.e67288" + ] + ] + }, + { + "id": "2f92c76e.42dca", + "type": "switch", + "z": "af01642a.58d1f", + "name": "Test on Error", + "property": "error.message", + "propertyType": "msg", + "rules": [ + { + "t": "eq", + "v": "Error: Thresholds missing", + "vt": "str" + } + ], + "checkall": "true", + "repair": false, + "outputs": 1, + "x": 1210, + "y": 540, + "wires": [ + [ + "85e96cbf.e608d8" + ] + ] + } +] \ No newline at end of file diff --git a/examples/Test_Status.json b/examples/Test_Status.json new file mode 100644 index 0000000..9d14acf --- /dev/null +++ b/examples/Test_Status.json @@ -0,0 +1,386 @@ +[ + { + "id": "a40f8b0e.e67288", + "type": "hysteresis", + "z": "af01642a.58d1f", + "name": "", + "ThresholdType": "dynamic", + "ThresholdRising": "", + "ThresholdFalling": "", + "TopicThreshold": "my_dyn_threshold", + "TopicCurrent": "my_current_value", + "ThresholdDeltaRising": "5", + "ThresholdDeltaFalling": "5", + "InitialMessage": true, + "OutRisingType": "str", + "OutRisingValue": "upper exceeded", + "OutFallingType": "str", + "OutFallingValue": "lower underran", + "OutTopicType": "str", + "OutTopicValue": "test topic", + "DynRaiseError": true, + "x": 1020, + "y": 420, + "wires": [ + [ + "1da1a3c2.a1807c" + ] + ] + }, + { + "id": "1da1a3c2.a1807c", + "type": "debug", + "z": "af01642a.58d1f", + "name": "", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "x": 1210, + "y": 420, + "wires": [] + }, + { + "id": "3431c2f0.8275ee", + "type": "inject", + "z": "af01642a.58d1f", + "name": "", + "props": [ + { + "p": "payload" + }, + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "my_dyn_threshold", + "payload": "20", + "payloadType": "num", + "x": 760, + "y": 140, + "wires": [ + [ + "a40f8b0e.e67288" + ] + ] + }, + { + "id": "f2d9f28f.1f254", + "type": "inject", + "z": "af01642a.58d1f", + "name": "", + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "my_current_value", + "payload": "20", + "payloadType": "num", + "x": 760, + "y": 380, + "wires": [ + [ + "a40f8b0e.e67288" + ] + ] + }, + { + "id": "3fb90d36.745602", + "type": "inject", + "z": "af01642a.58d1f", + "name": "", + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "my_current_value", + "payload": "26", + "payloadType": "num", + "x": 760, + "y": 540, + "wires": [ + [ + "a40f8b0e.e67288" + ] + ] + }, + { + "id": "9d76d540.8d13d", + "type": "inject", + "z": "af01642a.58d1f", + "name": "", + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "my_current_value", + "payload": "14", + "payloadType": "num", + "x": 760, + "y": 300, + "wires": [ + [ + "a40f8b0e.e67288" + ] + ] + }, + { + "id": "9ec83592.e4e848", + "type": "inject", + "z": "af01642a.58d1f", + "name": "", + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "my_current_value", + "payload": "13", + "payloadType": "num", + "x": 760, + "y": 260, + "wires": [ + [ + "a40f8b0e.e67288" + ] + ] + }, + { + "id": "d40c42cf.20592", + "type": "inject", + "z": "af01642a.58d1f", + "name": "", + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "my_current_value", + "payload": "12", + "payloadType": "num", + "x": 760, + "y": 220, + "wires": [ + [ + "a40f8b0e.e67288" + ] + ] + }, + { + "id": "7ef42e7c.a6ecb8", + "type": "inject", + "z": "af01642a.58d1f", + "name": "", + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "my_current_value", + "payload": "27", + "payloadType": "num", + "x": 760, + "y": 580, + "wires": [ + [ + "a40f8b0e.e67288" + ] + ] + }, + { + "id": "4234989d.33142", + "type": "inject", + "z": "af01642a.58d1f", + "name": "", + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "my_current_value", + "payload": "28", + "payloadType": "num", + "x": 760, + "y": 620, + "wires": [ + [ + "a40f8b0e.e67288" + ] + ] + }, + { + "id": "7eae1ec9.145348", + "type": "inject", + "z": "af01642a.58d1f", + "name": "", + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "my_current_value", + "payload": "21", + "payloadType": "num", + "x": 760, + "y": 420, + "wires": [ + [ + "a40f8b0e.e67288" + ] + ] + }, + { + "id": "f728e075.542f9", + "type": "inject", + "z": "af01642a.58d1f", + "name": "", + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "my_current_value", + "payload": "22", + "payloadType": "num", + "x": 760, + "y": 460, + "wires": [ + [ + "a40f8b0e.e67288" + ] + ] + }, + { + "id": "e608dc44.6d38f8", + "type": "comment", + "z": "af01642a.58d1f", + "name": "Dynamic Threshold", + "info": "Set the dynamic threshold.\nUntil this is injected, the hysteresis node will \nshow the warning \"Thresholds missing\" ", + "x": 730, + "y": 100, + "wires": [] + }, + { + "id": "384db5a.994a84a", + "type": "comment", + "z": "af01642a.58d1f", + "name": "Low Band Values", + "info": "Output will only be generated when the lower trigger \nthreshold is reached for the first time", + "x": 720, + "y": 180, + "wires": [] + }, + { + "id": "eb4a514d.09be18", + "type": "comment", + "z": "af01642a.58d1f", + "name": "Dead Band Values", + "info": "In the \"dead band\" no output will be generated until\nthe opposite trigger level is reached", + "x": 730, + "y": 340, + "wires": [] + }, + { + "id": "6befbefb.0b554", + "type": "comment", + "z": "af01642a.58d1f", + "name": "High Band Values", + "info": "Output will only be generated when the upper trigger \nthreshold is reached for the first time", + "x": 730, + "y": 500, + "wires": [] + }, + { + "id": "f0041ca.0231a6", + "type": "debug", + "z": "af01642a.58d1f", + "name": "", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "true", + "targetType": "full", + "statusVal": "", + "statusType": "auto", + "x": 1190, + "y": 580, + "wires": [] + }, + { + "id": "85e96cbf.e608d8", + "type": "change", + "z": "af01642a.58d1f", + "name": "Set Value for Initial Dead Band", + "rules": [ + { + "t": "set", + "p": "topic", + "pt": "msg", + "to": "test topic", + "tot": "str" + }, + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "20", + "tot": "num" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 1530, + "y": 540, + "wires": [ + [ + "1da1a3c2.a1807c" + ] + ] + }, + { + "id": "2f92c76e.42dca", + "type": "switch", + "z": "af01642a.58d1f", + "name": "Test on initial dead band", + "property": "status.text", + "propertyType": "msg", + "rules": [ + { + "t": "cont", + "v": "(initial dead band)", + "vt": "str" + } + ], + "checkall": "true", + "repair": false, + "outputs": 1, + "x": 1250, + "y": 540, + "wires": [ + [ + "85e96cbf.e608d8" + ] + ] + }, + { + "id": "a10356b0.86eb5", + "type": "status", + "z": "af01642a.58d1f", + "name": "", + "scope": [ + "a40f8b0e.e67288" + ], + "x": 1020, + "y": 540, + "wires": [ + [ + "f0041ca.0231a6", + "2f92c76e.42dca" + ] + ] + } +] \ No newline at end of file diff --git a/hysteresis.html b/hysteresis.html index 63da65f..4e4d336 100644 --- a/hysteresis.html +++ b/hysteresis.html @@ -21,7 +21,8 @@ OutFallingType: {value: ''}, OutFallingValue: {value: ''}, OutTopicType: {value: ''}, - OutTopicValue: {value: ''} + OutTopicValue: {value: ''}, + DynRaiseError: {checked: 'unchecked'} }, inputs: 1, outputs: 1, @@ -97,13 +98,17 @@
- +
+
+ + +
diff --git a/hysteresis.js b/hysteresis.js index f619adb..a460264 100644 --- a/hysteresis.js +++ b/hysteresis.js @@ -1,5 +1,5 @@ module.exports = function(RED) { - 'use strict'; + function HysteresisNode(config) { RED.nodes.createNode(this, config); @@ -10,6 +10,7 @@ module.exports = function(RED) { this.TopicCurrent = config.TopicCurrent; this.ThresholdDeltaRising = config.ThresholdDeltaRising; this.ThresholdDeltaFalling = config.ThresholdDeltaFalling; + this.DynRaiseError = config.DynRaiseError; this.InitialMessage = config.InitialMessage; this.OutRisingType = config.OutRisingType; this.OutRisingValue = config.OutRisingValue; @@ -44,16 +45,19 @@ module.exports = function(RED) { } } + // Define a node context + var nodeContext = this.context(); + // eslint-disable-next-line prefer-const let node = this; - let TriggerValueRising = ''; - let TriggerValueFalling = ''; + let TriggerValueRising = nodeContext.get('TriggerValueRising') || ''; + let TriggerValueFalling = nodeContext.get('TriggerValueFalling') || ''; if (this.ThresholdType === 'fixed') { TriggerValueRising = Number.parseFloat(this.ThresholdRising); TriggerValueFalling = Number.parseFloat(this.ThresholdFalling); } - // clear direction flag - node.direction = ''; + // get from context or clear direction flag + node.direction = nodeContext.get('Direction') || ''; // Set initial status if (!TriggerValueRising) { @@ -62,14 +66,38 @@ module.exports = function(RED) { node.status({fill:'yellow', shape:'ring', text: TriggerValueFalling + '/--/' + TriggerValueRising}); } - this.on('input', function(msg) { + this.on('input', function(msg, send, done) { + // For maximum backwards compatibility, check that send exists. + // If this node is installed in Node-RED 0.x, it will need to + // fallback to using `node.send` + send = send || function() { + node.send.apply(node, arguments); + }; + // Check for proper topic when using dynamic threshold if (this.ThresholdType === 'dynamic' && msg.topic === this.TopicThreshold && !Number.isNaN(msg.payload)) { TriggerValueRising = Number.parseFloat(msg.payload) + Number.parseFloat(this.ThresholdDeltaRising); TriggerValueFalling = Number.parseFloat(msg.payload) - Number.parseFloat(this.ThresholdDeltaFalling); + nodeContext.set('TriggerValueRising', TriggerValueRising); + nodeContext.set('TriggerValueFalling', TriggerValueFalling); node.status({fill:'yellow', shape:'ring', text: TriggerValueFalling + '/--/' + TriggerValueRising}); } + // Raise error when receiving a 'TopicCurrent' payload but no dynamic threshold set + if (Object.prototype.hasOwnProperty.call(msg, 'payload') && this.ThresholdType === 'dynamic' && + !TriggerValueRising && msg.topic === this.TopicCurrent && this.DynRaiseError) { + const err = new Error('Thresholds missing'); + if (err) { + if (done) { + // Node-RED 1.0 compatible + done(err); + } else { + // Node-RED 0.x compatible + node.error(err, msg); + } + } + } + // original msg object const msgNew = RED.util.cloneMessage(msg); // set topic @@ -78,22 +106,26 @@ module.exports = function(RED) { } 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))) { + (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 >= TriggerValueRising) { msgNew.payload = (this.OutRisingType === 'pay' ? msgNew.payload : this.OutRisingValue); msgNew.hystdirection = 'initial high'; - node.send(msgNew); + send(msgNew); node.direction = 'high'; + nodeContext.set('Direction', node.direction); node.status({fill:'green', shape:'dot', text: TriggerValueFalling + '/' + CurrentValue + '/' + TriggerValueRising + ' (initial high band)'}); + } else if ((CurrentValue > TriggerValueFalling) && (CurrentValue < TriggerValueRising)) { + node.status({fill:'green', shape:'dot', text: TriggerValueFalling + '/' + CurrentValue + '/' + TriggerValueRising + ' (initial dead band)'}); } else if (CurrentValue <= TriggerValueFalling) { msgNew.payload = (this.OutFallingType === 'pay' ? msgNew.payload : this.OutFallingValue); msgNew.hystdirection = 'initial low'; - node.send(msgNew); + send(msgNew); node.direction = 'low'; + nodeContext.set('Direction', node.direction); node.status({fill:'blue', shape:'dot', text: TriggerValueFalling + '/' + CurrentValue + '/' + TriggerValueRising + ' (initial low band)'}); } // Last value known. Work as hysteresis @@ -102,15 +134,17 @@ module.exports = function(RED) { if (CurrentValue > node.LastValue && CurrentValue >= TriggerValueRising && node.direction !== 'high') { msgNew.payload = (this.OutRisingType === 'pay' ? msgNew.payload : this.OutRisingValue); msgNew.hystdirection = 'high'; - node.send(msgNew); + send(msgNew); node.direction = 'high'; + nodeContext.set('Direction', node.direction); 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); + send(msgNew); node.direction = 'low'; + nodeContext.set('Direction', node.direction); 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)'}); @@ -131,6 +165,11 @@ module.exports = function(RED) { } } node.LastValue = CurrentValue; + nodeContext.set('LastValue', node.LastValue); + + if (done) { + done(); + } } }); } diff --git a/package.json b/package.json index e936278..b97503e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red-contrib-hysteresis", - "version": "0.2.0", + "version": "0.3.0", "description": "Adds hysteresis function to node-red", "main": "hysteresis.js", "files": [