node-red-contrib-boolean-logic-ultimate 1.2.11 → 1.2.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,27 @@
4
4
 
5
5
  # CHANGELOG
6
6
 
7
+ <p>
8
+ <b>Version 1.2.14</b> June 2026<br/>
9
+
10
+ - NEW: MinMaxLimiterUltimate node, constrains a numeric value between a min and a max (clamp/saturation), e.g. to keep a dimmable lamp away from its extreme values.<br/>
11
+ - Replaced all node icons with native Node-RED icons for consistent, properly centered rendering.<br/>
12
+ </p>
13
+
14
+ <p>
15
+ <b>Version 1.2.13</b> May 2026<br/>
16
+
17
+ - BooleanLogicUltimate: renamed the editor field label from "Evaluate" to "Input".<br/>
18
+ - Updated BooleanLogic help and README terminology to match the new label.<br/>
19
+ </p>
20
+
21
+ <p>
22
+ <b>Version 1.2.12</b> May 2026<br/>
23
+
24
+ - Renamed editor label from "With Input" to "Input" for clearer wording across affected nodes.<br/>
25
+ - Updated the related node help sections and README terminology accordingly.<br/>
26
+ </p>
27
+
7
28
  <p>
8
29
  <b>Version 1.2.11</b> April 2026<br/>
9
30
 
package/README.md CHANGED
@@ -99,7 +99,7 @@ The node can convert arbitrary input values to true/false. It supports Homeassis
99
99
  | Property | Description |
100
100
  | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
101
101
  | Inputs count | Set the number of different topics to be evaluated. The node will output a message to the flow, after this number of _different_ topics arrives. _Remember: each input topic must be different. For example, if you set this field to 3, the node expects 3 different topics._ |
102
- | Evaluate | It's the msg property to be evaluated. _By default, it is "payload", but you can also specify other properties, for example "payload.value"_ |
102
+ | Input | It's the msg property to be evaluated. _By default, it is "payload", but you can also specify other properties, for example "payload.value"_ |
103
103
  | Filter output | **Output both 'true' and 'false'** results: Standard behaviour, the node will output <b>true</b> and <b>false</b> whenever it receives an input and calculate the boolean logics as output. **Output only 'true'** results: whenever the node receives an input, it outputs a payload <b>true</b> only if the result of the logic is true. <b>False</b> results are filtered out. |
104
104
  | Trigger mode | **All topics**: standard behaviour, the node will evaluate each input topic and ouputs the values. At each input change, it will output a msg on the flow. **Single topic + eval other inputs**: the node evaluates all the input topics, but only whenever it receives a msg input with the **specified topic**, it outputs a msg to the flow. |
105
105
  | If input states are undefined | Every time you create a node or modify the node, all inputs are set to undefined. This means that the node will wait the arrive of all topics (for example 3 topics, if you've selected 3 topics in the option), before it can output a payload. This can be a problem if your logic must be operative as soon as you deploy the flow. To overcome this problem, you can "initialize" all the undefined inputs with True or False. **Leave undefined**: Standard behaviour, the node will wait all the "undefined" topics to arrive, then starts a flow with the result. **True or False**: The node is immediately operative, by force the initialization of the "undefined" inputs with "true" or "false". |
@@ -131,7 +131,7 @@ The interrupt flows is able to stop the input messages to exiting the node.
131
131
  | Property | Description |
132
132
  | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
133
133
  | Trigger by topic | Whenever the node receives a payload = false from this topic,it stops output messages to the flow. As soon it receives payload = true from this topic, the output messages start to flow out again. The node will output the current stored message plus an added property "isReplay = true", as soon as it receives a **_msg.play = true_** from this topic. The node will clear the current stored message, as soon as it receives a **_msg.reset = true_** from this topic. |
134
- | With Input | It's the msg property to be evaluated. _By default, it is "payload", but you can also specify other properties, for example "payload.value"_ |
134
+ | Input | It's the msg property to be evaluated. _By default, it is "payload", but you can also specify other properties, for example "payload.value"_ |
135
135
  | Then | This property, allow you to auto toggle the selected start state (pass or block) after a timer has elapsed. You can choose from some pre-defined delays. If you have, for example, an Homekit-Bridged nodeset with a thermostat node or security system node in your flow, once node-red restarts, these homekit nodes output a default message to the flow. Just put an InterruptFlow node with a "block at start" behaviour and a toggle delay enabled behind homekit nodes, to temporary stop the chained nodes to receive the unwanted startup message. |
136
136
 
137
137
  <br/>
@@ -471,7 +471,7 @@ The railway switcher, redirect the incoming messages to one ot the avaiable outp
471
471
  | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
472
472
  | Switcher topic | Whenever the node receives a payload from this **topic**, it redirects the input messages to a choosen output PIN. |
473
473
  | Output pins | Number of output pins (outputs) to show. Default is 5, range is 1..10. |
474
- | With Input | It's the msg property to be evaluated. _By default, it is "payload", but you can also specify other properties, for example "payload.value"_ |
474
+ | Input | It's the msg property to be evaluated. _By default, it is "payload", but you can also specify other properties, for example "payload.value"_ |
475
475
  | Translator | Translates the incoming <code>payload</code> value. This allows the compatibility with, for example, **HomeAssistant** nodes. |
476
476
 
477
477
  ### Inputs
@@ -490,7 +490,7 @@ Take the example where you choosen such properties:
490
490
 
491
491
  **Switcher topic**: "switcher"
492
492
 
493
- **With Input**: "payload"
493
+ **Input**: "payload"
494
494
 
495
495
  this JSON input message redirects all input messages to the first PIN
496
496
 
@@ -663,7 +663,7 @@ Gateway per sensori e dispositivi troppo “chiacchieroni”: limita burst e rim
663
663
  | Max messages | Numero di messaggi ammessi nella finestra. |
664
664
  | On limit | _Drop_ scarta i messaggi extra, _Queue last_ accoda l’ultimo e lo riproduce appena possibile. |
665
665
  | Control topic | Topic dei messaggi di controllo (default `rate`). |
666
- | With Input | Proprietà del messaggio da monitorare (default `msg.payload`). |
666
+ | Input | Proprietà del messaggio da monitorare (default `msg.payload`). |
667
667
  | Stats every (s) | Ogni quanti secondi emettere un riepilogo statistico (0 = disattivato). |
668
668
  | Translator | Nodo translator-config opzionale per adattare le stringhe d’ingresso a true/false. |
669
669
 
@@ -729,7 +729,7 @@ The purpose of this node is to replay a programmable sequence of messages in ord
729
729
  | Loop sequence | Repeats the sequence when it reaches the end. |
730
730
  | Random delays | Enables a random variation of the programmed delays. |
731
731
  | Jitter (%) | Maximum percentage of variation applied when random delays are enabled. |
732
- | With Input | Message property to inspect for inline events (default `payload`). |
732
+ | Input | Message property to inspect for inline events (default `payload`). |
733
733
  | Translator | Optional translator-config to convert incoming values. |
734
734
  | Sequence | One JSON object per line, each containing at least `delay` (ms) plus the properties to output. |
735
735
 
@@ -759,7 +759,7 @@ The purpose of this node is to control staircase lighting with a timer, pre-off
759
759
  | Warning offset (s) | Seconds before switch-off when the warning is sent. |
760
760
  | Restart on trigger | Restarts the timer when a new trigger arrives while active. |
761
761
  | Allow off input | Allows a `false` from the main input to switch off immediately. |
762
- | With Input | Message property evaluated as trigger (default `payload`). |
762
+ | Input | Message property evaluated as trigger (default `payload`). |
763
763
  | Translator | Optional translator-config for true/false conversion. |
764
764
  | On/Off payload | Values emitted on output 1 to turn the light on/off. |
765
765
  | Warning payload | Value emitted on output 2 when the warning fires. |
@@ -790,7 +790,7 @@ Example flow: [`examples/HysteresisUltimate.json`](examples/HysteresisUltimate.j
790
790
  | ON/OFF threshold | Hysteresis limits. |
791
791
  | Initial state | Startup output state. |
792
792
  | Emit only on change | If enabled, output 1 emits only on state transitions. |
793
- | With Input | Message property evaluated as numeric value (default `payload`). |
793
+ | Input | Message property evaluated as numeric value (default `payload`). |
794
794
  | Translator | Optional translator-config. |
795
795
  | On/Off payload | Typed payloads sent on output 1. |
796
796
 
@@ -24,7 +24,7 @@
24
24
  break;
25
25
  }
26
26
  },
27
- icon: "light.png",
27
+ icon: "light.svg",
28
28
  label:
29
29
  function () {
30
30
  return (this.name || "Blinker") + " (" + this.blinkfrequency + "ms)";
@@ -56,7 +56,7 @@
56
56
  break;
57
57
  }
58
58
  },
59
- icon: "serial.png",
59
+ icon: "serial.svg",
60
60
  label:
61
61
  function () {
62
62
  let label = "";
@@ -142,7 +142,7 @@
142
142
  <input style="width:100px" type="text" id="node-input-inputCount" placeholder="Inputs count, for example: 2">
143
143
  </div>
144
144
  <div class="form-row">
145
- <label for="node-input-payloadPropName"><i class="fa fa-ellipsis-h"></i> Evaluate</label>
145
+ <label for="node-input-payloadPropName"><i class="fa fa-ellipsis-h"></i> Input</label>
146
146
  <input type="text" id="node-input-payloadPropName">
147
147
  </div>
148
148
  <div class="form-row">
@@ -228,7 +228,7 @@
228
228
  |Property|Description|
229
229
  |--|--|
230
230
  | Inputs count | Set the number of different topics to be evaluated. The node will output a message to the flow, after this number of *different* topics arrives. *Remember: each input topic must be different. For example, if you set this field to 3, the node expects 3 different topics.* |
231
- | Evaluate | It's the msg property to be evaluated. *By default, it is "payload", but you can also specify other properties, for example "payload.value"* |
231
+ | Input | It's the msg property to be evaluated. *By default, it is "payload", but you can also specify other properties, for example "payload.value"* |
232
232
 
233
233
  <br/>
234
234
 
@@ -259,4 +259,4 @@
259
259
  [Find it useful?](https://www.paypal.me/techtoday)
260
260
  <br/>
261
261
 
262
- </script>
262
+ </script>
@@ -21,7 +21,7 @@
21
21
  },
22
22
  inputs: 1,
23
23
  outputs: 1,
24
- icon: "font-awesome/fa-arrows-v",
24
+ icon: "sort.svg",
25
25
  label:
26
26
  function () {
27
27
  return this.name || "Comparator";
@@ -11,7 +11,7 @@
11
11
  inputs: 1,
12
12
  outputs: 1,
13
13
  outputLabels: ['Forward'],
14
- icon: 'file-in.png',
14
+ icon: 'file-in.svg',
15
15
  label: function () {
16
16
  return this.name || 'Debouncer';
17
17
  },
@@ -24,7 +24,7 @@
24
24
  break;
25
25
  }
26
26
  },
27
- icon: "switch.png",
27
+ icon: "switch.svg",
28
28
  label:
29
29
  function () {
30
30
  return this.name || "Filter";
@@ -20,7 +20,7 @@
20
20
  inputs: 1,
21
21
  outputs: 2,
22
22
  outputLabels: ['State output', 'Diagnostics'],
23
- icon: 'font-awesome/fa-sliders',
23
+ icon: 'rbe.svg',
24
24
  label: function () {
25
25
  return this.name || 'Hysteresis';
26
26
  },
@@ -96,7 +96,7 @@
96
96
  </div>
97
97
 
98
98
  <div class="form-row">
99
- <label for="node-input-payloadPropName"><i class="fa fa-ellipsis-h"></i> With Input</label>
99
+ <label for="node-input-payloadPropName"><i class="fa fa-ellipsis-h"></i> Input</label>
100
100
  <input type="text" id="node-input-payloadPropName">
101
101
  </div>
102
102
 
@@ -124,7 +124,7 @@
124
124
  | Mode | `high`: ON above threshold, OFF below. `low`: ON below threshold, OFF above. |
125
125
  | ON/OFF threshold | The two hysteresis limits. |
126
126
  | Emit only on change | Emits output only when state changes. |
127
- | With Input | Message property to evaluate (default `payload`). |
127
+ | Input | Message property to evaluate (default `payload`). |
128
128
  | Translator | Optional translator-config. |
129
129
  | On/Off payload | Values sent on output 1. |
130
130
 
@@ -134,4 +134,4 @@ Control topic messages:
134
134
  - `msg.state = true|false` to force state.
135
135
  - `msg.reset = true` to restore initial state.
136
136
  - `msg.status = true` to emit current status on output 2.
137
- </script>
137
+ </script>
@@ -25,7 +25,7 @@
25
25
  break;
26
26
  }
27
27
  },
28
- icon: "file-in.png",
28
+ icon: "file-in.svg",
29
29
  label:
30
30
  function () {
31
31
  return (this.name || "Interrupt") + " (" + this.triggertopic + ")";
@@ -56,7 +56,7 @@
56
56
  <input type="text" id="node-input-triggertopic" placeholder="Name">
57
57
  </div>
58
58
  <div class="form-row">
59
- <label for="node-input-payloadPropName"><i class="fa fa-ellipsis-h"></i> With Input</label>
59
+ <label for="node-input-payloadPropName"><i class="fa fa-ellipsis-h"></i> Input</label>
60
60
  <input type="text" id="node-input-payloadPropName">
61
61
  </div>
62
62
  <div><i>Whenever the node receives a msg with false from this topic,<br/> it stops output messages to the flow.<br/>As soon it receives a message with true from this topic,<br/>the output messages start to flow out again.
@@ -105,7 +105,7 @@
105
105
  |Property|Description|
106
106
  |--|--|
107
107
  | Trigger by topic | Whenever the node receives a payload = false from this topic,it stops output messages to the flow. As soon it receives payload = true from this topic, the output messages start to flow out again. The node will output the current stored message plus an added property "isReplay = true", as soon as it receives a ***msg.play = true*** from this topic. The node will clear the current stored message, as soon as it receives a ***msg.reset = true*** from this topic. |
108
- | With Input | It's the msg property to be evaluated. *By default, it is "payload", but you can also specify other properties, for example "payload.value"* |
108
+ | Input | It's the msg property to be evaluated. *By default, it is "payload", but you can also specify other properties, for example "payload.value"* |
109
109
  | Then | This property, allow you to auto toggle the selected start state (pass or block) after a timer has elapsed. You can choose from some pre-defined delays. If you have, for example, an Homekit-Bridged nodeset with a thermostat node or security system node in your flow, once node-red restarts, these homekit nodes output a default message to the flow. Just put an InterruptFlow node with a "block at start" behaviour and a toggle delay enabled behind homekit nodes, to temporary stop the chained nodes to receive the unwanted startup message.|
110
110
  | Translator Input | Translates the incoming <code>payload</code> value, to true/false. This allows the compatibility with, for example, **HomeAssistant** nodes. |
111
111
 
@@ -11,7 +11,7 @@
11
11
  },
12
12
  inputs: 1,
13
13
  outputs: 1,
14
- icon: "swap.png",
14
+ icon: "swap.svg",
15
15
  label:
16
16
  function () {
17
17
  return this.name || "Invert";
@@ -13,7 +13,7 @@
13
13
  },
14
14
  inputs: 1,
15
15
  outputs: 1,
16
- icon: "swap.png",
16
+ icon: "swap.svg",
17
17
  label:
18
18
  function () {
19
19
  return this.name || "KalmanFilter";
@@ -0,0 +1,100 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType('MinMaxLimiterUltimate', {
3
+ category: 'Boolean Logic Ultimate',
4
+ color: '#ff8080',
5
+ defaults: {
6
+ name: { value: '' },
7
+ controlTopic: { value: 'clamp' },
8
+ payloadPropName: { value: 'payload', required: false },
9
+ translatorConfig: { type: 'translator-config', required: false },
10
+ min: { value: 10, validate: RED.validators.number() },
11
+ max: { value: 90, validate: RED.validators.number() },
12
+ passInvalid: { value: false }
13
+ },
14
+ inputs: 1,
15
+ outputs: 1,
16
+ outputLabels: ['Clamped value'],
17
+ icon: 'range.svg',
18
+ label: function () {
19
+ return this.name || 'Min-Max Limiter';
20
+ },
21
+ paletteLabel: function () {
22
+ return 'Min-Max Limiter';
23
+ },
24
+ oneditprepare: function () {
25
+ const payloadField = $('#node-input-payloadPropName');
26
+ if (payloadField.val() === '') payloadField.val('payload');
27
+ payloadField.typedInput({ default: 'msg', types: ['msg'] });
28
+ }
29
+ });
30
+ </script>
31
+
32
+ <script type="text/html" data-template-name="MinMaxLimiterUltimate">
33
+ <div class="form-row">
34
+ <b>Min-Max Limiter</b>
35
+ &nbsp;&nbsp;<span style="color:red"><i class="fa fa-question-circle"></i>&nbsp;<a target="_blank" href="https://github.com/Supergiovane/node-red-contrib-boolean-logic-ultimate"><u>Help online</u></a></span>
36
+ </div>
37
+
38
+ <div class="form-row">
39
+ <label for="node-input-name"><i class="icon-tag"></i> Name</label>
40
+ <input type="text" id="node-input-name" placeholder="Name">
41
+ </div>
42
+
43
+ <div class="form-row">
44
+ <label for="node-input-controlTopic"><i class="fa fa-tag"></i> Control topic</label>
45
+ <input type="text" id="node-input-controlTopic">
46
+ </div>
47
+
48
+ <div class="form-row">
49
+ <label for="node-input-min"><i class="fa fa-long-arrow-down"></i> Min (lower limit)</label>
50
+ <input type="number" id="node-input-min" step="any">
51
+ </div>
52
+
53
+ <div class="form-row">
54
+ <label for="node-input-max"><i class="fa fa-long-arrow-up"></i> Max (upper limit)</label>
55
+ <input type="number" id="node-input-max" step="any">
56
+ </div>
57
+
58
+ <div class="form-row">
59
+ <label for="node-input-passInvalid"><i class="fa fa-share"></i> Pass non-numbers</label>
60
+ <input type="checkbox" id="node-input-passInvalid" style="width:auto; margin-top:7px;">
61
+ </div>
62
+
63
+ <div class="form-row">
64
+ <label for="node-input-payloadPropName"><i class="fa fa-ellipsis-h"></i> Input</label>
65
+ <input type="text" id="node-input-payloadPropName">
66
+ </div>
67
+
68
+ <div class="form-row">
69
+ <label for="node-input-translatorConfig"><i class="fa fa-language"></i> Translator</label>
70
+ <input type="text" id="node-input-translatorConfig">
71
+ </div>
72
+ </script>
73
+
74
+ <script type="text/markdown" data-help-name="MinMaxLimiterUltimate">
75
+ <p>Constrains a numeric value between a lower and an upper limit (clamp / saturation).
76
+ Useful, for example, to keep a dimmable lamp away from its extreme values.</p>
77
+
78
+ <p>If the input is below <code>Min</code> it outputs <code>Min</code>; if it is above
79
+ <code>Max</code> it outputs <code>Max</code>; otherwise the value passes through unchanged.</p>
80
+
81
+ |Property|Description|
82
+ |--|--|
83
+ | Control topic | Topic used to update limits at runtime. |
84
+ | Min | Lower limit. Values below it are raised to this value. |
85
+ | Max | Upper limit. Values above it are lowered to this value. |
86
+ | Pass non-numbers | If enabled, non-numeric inputs are forwarded unchanged instead of being dropped. |
87
+ | Input | Message property to read and write (default `payload`). |
88
+ | Translator | Optional translator-config. |
89
+
90
+ <p>The output message also carries <code>msg.clamp</code> with
91
+ <code>{ clamped, input, output, min, max }</code>.</p>
92
+
93
+ <p>Example (Min=10, Max=90): input <code>0</code> &rarr; <code>10</code>,
94
+ input <code>50</code> &rarr; <code>50</code>, input <code>100</code> &rarr; <code>90</code>.</p>
95
+
96
+ Control topic messages:
97
+
98
+ - `msg.min` to update the lower limit at runtime.
99
+ - `msg.max` to update the upper limit at runtime.
100
+ </script>
@@ -0,0 +1,128 @@
1
+ 'use strict';
2
+
3
+ module.exports = function (RED) {
4
+ const helpers = require('./lib/node-helpers.js');
5
+
6
+ function MinMaxLimiterUltimate(config) {
7
+ RED.nodes.createNode(this, config);
8
+ const node = this;
9
+ const REDUtil = RED.util;
10
+
11
+ const setNodeStatus = helpers.createStatus(node);
12
+
13
+ const controlTopic = config.controlTopic || 'clamp';
14
+ const payloadPropName = config.payloadPropName || 'payload';
15
+ const passInvalid = config.passInvalid === true;
16
+
17
+ let min = Number(config.min);
18
+ let max = Number(config.max);
19
+
20
+ if (!Number.isFinite(min)) min = 10;
21
+ if (!Number.isFinite(max)) max = 90;
22
+
23
+ function normalizeLimits() {
24
+ // Keep min <= max even if the user (or a control message) swaps them.
25
+ if (Number.isFinite(min) && Number.isFinite(max) && min > max) {
26
+ const tmp = min;
27
+ min = max;
28
+ max = tmp;
29
+ }
30
+ }
31
+
32
+ function toNumber(value) {
33
+ if (typeof value === 'number' && Number.isFinite(value)) {
34
+ return value;
35
+ }
36
+ if (typeof value === 'boolean') {
37
+ return value ? 1 : 0;
38
+ }
39
+ if (typeof value === 'string') {
40
+ const v = Number(value.trim());
41
+ return Number.isFinite(v) ? v : undefined;
42
+ }
43
+ return undefined;
44
+ }
45
+
46
+ function updateStatus(lastValue, clamped) {
47
+ let text;
48
+ if (lastValue === undefined) {
49
+ text = `min:${min} max:${max}`;
50
+ } else {
51
+ const valueText = Number(lastValue).toFixed(2).replace(/\.00$/, '');
52
+ text = `${valueText} [${min}..${max}]`;
53
+ }
54
+ setNodeStatus({
55
+ fill: clamped ? 'yellow' : 'green',
56
+ shape: clamped ? 'ring' : 'dot',
57
+ text,
58
+ });
59
+ }
60
+
61
+ function handleControl(msg) {
62
+ let consumed = false;
63
+
64
+ if (Object.prototype.hasOwnProperty.call(msg, 'min')) {
65
+ const next = Number(msg.min);
66
+ if (Number.isFinite(next)) {
67
+ min = next;
68
+ consumed = true;
69
+ }
70
+ }
71
+
72
+ if (Object.prototype.hasOwnProperty.call(msg, 'max')) {
73
+ const next = Number(msg.max);
74
+ if (Number.isFinite(next)) {
75
+ max = next;
76
+ consumed = true;
77
+ }
78
+ }
79
+
80
+ if (consumed) {
81
+ normalizeLimits();
82
+ updateStatus();
83
+ }
84
+
85
+ return consumed;
86
+ }
87
+
88
+ node.on('input', (msg) => {
89
+ if (msg.topic === controlTopic && handleControl(msg)) {
90
+ return;
91
+ }
92
+
93
+ const resolved = helpers.resolveInput(msg, payloadPropName, config.translatorConfig, RED);
94
+ const inputValue = toNumber(resolved.value);
95
+
96
+ if (inputValue === undefined) {
97
+ node.warn(`MinMaxLimiterUltimate: '${payloadPropName}' is not a number (${resolved.value})`);
98
+ updateStatus();
99
+ if (passInvalid) {
100
+ node.send(msg);
101
+ }
102
+ return;
103
+ }
104
+
105
+ let outputValue = inputValue;
106
+ if (outputValue < min) outputValue = min;
107
+ if (outputValue > max) outputValue = max;
108
+ const clamped = outputValue !== inputValue;
109
+
110
+ REDUtil.setMessageProperty(msg, payloadPropName, outputValue, true);
111
+ msg.clamp = {
112
+ clamped,
113
+ input: inputValue,
114
+ output: outputValue,
115
+ min,
116
+ max,
117
+ };
118
+
119
+ node.send(msg);
120
+ updateStatus(inputValue, clamped);
121
+ });
122
+
123
+ normalizeLimits();
124
+ updateStatus();
125
+ }
126
+
127
+ RED.nodes.registerType('MinMaxLimiterUltimate', MinMaxLimiterUltimate);
128
+ };
@@ -15,7 +15,7 @@
15
15
  },
16
16
  inputs: 1,
17
17
  outputs: 1,
18
- icon: 'file-in.png',
18
+ icon: 'file-in.svg',
19
19
  label: function () {
20
20
  return this.name || 'Presence Simulator';
21
21
  },
@@ -68,7 +68,7 @@
68
68
  </div>
69
69
 
70
70
  <div class="form-row">
71
- <label for="node-input-payloadPropName"><i class="fa fa-ellipsis-h"></i> With Input</label>
71
+ <label for="node-input-payloadPropName"><i class="fa fa-ellipsis-h"></i> Input</label>
72
72
  <input type="text" id="node-input-payloadPropName">
73
73
  </div>
74
74
 
@@ -93,7 +93,7 @@
93
93
  | Loop sequence | Repeats the sequence when the end is reached. |
94
94
  | Random delays | Enables a random variation of the programmed delays. |
95
95
  | Jitter (%) | Maximum percentage of variation applied when random delays are enabled. |
96
- | With Input | Message property to inspect for inline events (default `payload`). |
96
+ | Input | Message property to inspect for inline events (default `payload`). |
97
97
  | Translator | Optional translator-config to convert incoming values. |
98
98
  | Sequence | One JSON object per line, each composed at least of `delay` (in ms) and the properties to output. |
99
99
 
@@ -16,7 +16,7 @@
16
16
  outputLabels: function (i) {
17
17
  return "PIN " + i;
18
18
  },
19
- icon: "font-awesome/fa-train",
19
+ icon: "switch.svg",
20
20
  label:
21
21
  function () {
22
22
  return (this.name || "Switch") + " (" + this.triggertopic + ")";
@@ -141,7 +141,7 @@
141
141
  </select>
142
142
  </div>
143
143
  <div class="form-row">
144
- <label for="node-input-payloadPropName"><i class="fa fa-ellipsis-h"></i> With Input</label>
144
+ <label for="node-input-payloadPropName"><i class="fa fa-ellipsis-h"></i> Input</label>
145
145
  <input type="text" id="node-input-payloadPropName">
146
146
  </div>
147
147
  <div class="form-row">
@@ -163,7 +163,7 @@
163
163
  |--|--|
164
164
  | Switcher topic | Whenever the node receives a payload from this **topic**, it redirects the input messages to a choosen output PIN. |
165
165
  | Output pins | Number of output pins to display (1..10). |
166
- | With Input | It's the msg property to be evaluated. *By default, it is "payload", but you can also specify other properties, for example "payload.value"* |
166
+ | Input | It's the msg property to be evaluated. *By default, it is "payload", but you can also specify other properties, for example "payload.value"* |
167
167
  | Translator | Translates the incoming <code>payload</code> value. This allows the compatibility with, for example, **HomeAssistant** nodes. |
168
168
 
169
169
  ### Inputs
@@ -178,7 +178,7 @@ Once an output PIN has been choosen, all messages passing through the node will
178
178
 
179
179
  Take the example where you choosen such properties:
180
180
  **Switcher topic**: "switcher"
181
- **With Input**: "payload"
181
+ **Input**: "payload"
182
182
  this JSON input message redirects all input messages to the first PIN
183
183
 
184
184
  ```json
@@ -22,7 +22,7 @@
22
22
  outputLabels: function (index) {
23
23
  return index === 0 ? 'Forward' : 'Diagnostics';
24
24
  },
25
- icon: 'file-in.png',
25
+ icon: 'file-in.svg',
26
26
  label: function () {
27
27
  const mode = (this.mode || 'debounce').charAt(0).toUpperCase() + (this.mode || 'debounce').slice(1);
28
28
  return (this.name || 'RateLimiter') + ' (' + mode + ')';
@@ -87,7 +87,7 @@
87
87
  </div>
88
88
 
89
89
  <div class="form-row rate-config-common">
90
- <label for="node-input-payloadPropName"><i class="fa fa-ellipsis-h"></i> With Input</label>
90
+ <label for="node-input-payloadPropName"><i class="fa fa-ellipsis-h"></i> Input</label>
91
91
  <input type="text" id="node-input-payloadPropName">
92
92
  </div>
93
93
 
@@ -160,5 +160,5 @@ The purpose of this node is to moderate the frequency of incoming messages.
160
160
  - `msg.mode = 'debounce'|'throttle'|'window'` – changes mode at runtime.
161
161
  - `msg.interval`, `msg.wait`, `msg.windowSize`, `msg.maxInWindow` – adjust the related thresholds.
162
162
 
163
- The **With Input** field selects which message property to evaluate (default `msg.payload`). When configured, it can be translated through the **translator-config** node for Home Assistant compatibility.
163
+ The **Input** field selects which message property to evaluate (default `msg.payload`). When configured, it can be translated through the **translator-config** node for Home Assistant compatibility.
164
164
  </script>
@@ -22,7 +22,7 @@
22
22
  break;
23
23
  }
24
24
  },
25
- icon: "font-awesome/fa-arrow-circle-o-right",
25
+ icon: "arrow-in.svg",
26
26
  label:
27
27
  function () {
28
28
  return this.name || "Simple Output";
@@ -22,7 +22,7 @@
22
22
  inputs: 1,
23
23
  outputs: 2,
24
24
  outputLabels: ['Light command', 'Warning'],
25
- icon: 'timer.png',
25
+ icon: 'timer.svg',
26
26
  label: function () {
27
27
  return this.name || 'Staircase Light';
28
28
  },
@@ -103,7 +103,7 @@
103
103
  </div>
104
104
 
105
105
  <div class="form-row">
106
- <label for="node-input-payloadPropName"><i class="fa fa-ellipsis-h"></i> With Input</label>
106
+ <label for="node-input-payloadPropName"><i class="fa fa-ellipsis-h"></i> Input</label>
107
107
  <input type="text" id="node-input-payloadPropName">
108
108
  </div>
109
109
 
@@ -139,7 +139,7 @@
139
139
  | Warning offset (s) | Seconds before switch-off when the warning is sent. |
140
140
  | Restart on trigger | Restarts the timer every time a new trigger arrives while active. |
141
141
  | Allow off input | Allows a `false` from the main input to switch off immediately. |
142
- | With Input | Message property to evaluate as trigger (default `payload`). |
142
+ | Input | Message property to evaluate as trigger (default `payload`). |
143
143
  | Translator | Optional translator-config for true/false conversion. |
144
144
  | On/Off payload | Values emitted on output 1 to switch the light. |
145
145
  | Warning payload | Value emitted on output 2 when the warning fires. |
@@ -13,7 +13,7 @@
13
13
  },
14
14
  inputs: 1,
15
15
  outputs: 1,
16
- icon: "font-awesome/fa-eye",
16
+ icon: "white-globe.svg",
17
17
  label:
18
18
  function () {
19
19
  return this.name || "Status";
@@ -16,7 +16,7 @@
16
16
  },
17
17
  inputs: 1,
18
18
  outputs: 1,
19
- icon: "font-awesome/fa-plus",
19
+ icon: "hash.svg",
20
20
  label:
21
21
  function () {
22
22
  return this.name || "Math";
@@ -12,7 +12,7 @@
12
12
  },
13
13
  inputs: 1,
14
14
  outputs: 1,
15
- icon: "serial.png",
15
+ icon: "serial.svg",
16
16
  label:
17
17
  function () {
18
18
  return this.name || "Toggle";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-boolean-logic-ultimate",
3
- "version": "1.2.11",
3
+ "version": "1.2.14",
4
4
  "description": "A set of Node-RED enhanced boolean logic and utility nodes, flow interruption, blinker, debouncer, invert, filter, toggle etc.., with persistent values after reboot. Compatible also with Homeassistant values.",
5
5
  "author": "Supergiovane (https://github.com/Supergiovane)",
6
6
  "dependencies": {
@@ -47,6 +47,7 @@
47
47
  "PresenceSimulatorUltimate": "boolean-logic-ultimate/PresenceSimulatorUltimate.js",
48
48
  "StaircaseLightUltimate": "boolean-logic-ultimate/StaircaseLightUltimate.js",
49
49
  "HysteresisUltimate": "boolean-logic-ultimate/HysteresisUltimate.js",
50
+ "MinMaxLimiterUltimate": "boolean-logic-ultimate/MinMaxLimiterUltimate.js",
50
51
  "translator-config": "boolean-logic-ultimate/translator-config.js"
51
52
  }
52
53
  },
@@ -0,0 +1,128 @@
1
+ 'use strict';
2
+
3
+ const { expect } = require('chai');
4
+ const { helper } = require('./helpers');
5
+
6
+ const clampNode = require('../boolean-logic-ultimate/MinMaxLimiterUltimate.js');
7
+
8
+ function loadClamp(flow, credentials) {
9
+ const normalizedFlow = flow.map((node, index) => {
10
+ if (
11
+ node &&
12
+ node.type &&
13
+ node.type !== 'tab' &&
14
+ node.type !== 'subflow' &&
15
+ node.type !== 'group' &&
16
+ node.z &&
17
+ !(Object.prototype.hasOwnProperty.call(node, 'x') && Object.prototype.hasOwnProperty.call(node, 'y'))
18
+ ) {
19
+ return { ...node, x: 100 + index * 10, y: 100 + index * 10 };
20
+ }
21
+ return node;
22
+ });
23
+ return helper.load(clampNode, normalizedFlow, credentials || {});
24
+ }
25
+
26
+ describe('MinMaxLimiterUltimate node', function () {
27
+ this.timeout(5000);
28
+
29
+ before(function (done) {
30
+ helper.startServer(done);
31
+ });
32
+
33
+ after(function (done) {
34
+ helper.stopServer(done);
35
+ });
36
+
37
+ afterEach(function () {
38
+ return helper.unload();
39
+ });
40
+
41
+ function baseFlow(extra) {
42
+ const flowId = 'clamp1';
43
+ return [
44
+ { id: flowId, type: 'tab', label: 'clamp1' },
45
+ Object.assign(
46
+ {
47
+ id: 'clamp',
48
+ type: 'MinMaxLimiterUltimate',
49
+ z: flowId,
50
+ min: 10,
51
+ max: 90,
52
+ payloadPropName: 'payload',
53
+ wires: [['out']],
54
+ },
55
+ extra || {}
56
+ ),
57
+ { id: 'in', type: 'helper', z: flowId, wires: [['clamp']] },
58
+ { id: 'out', type: 'helper', z: flowId },
59
+ ];
60
+ }
61
+
62
+ it('clamps below min, above max and passes through in range', function (done) {
63
+ loadClamp(baseFlow()).then(() => {
64
+ const clamp = helper.getNode('clamp');
65
+ const out = helper.getNode('out');
66
+ const results = [];
67
+
68
+ out.on('input', (msg) => {
69
+ results.push(msg.payload);
70
+ if (results.length === 3) {
71
+ try {
72
+ expect(results).to.deep.equal([10, 50, 90]);
73
+ done();
74
+ } catch (error) {
75
+ done(error);
76
+ }
77
+ }
78
+ });
79
+
80
+ clamp.receive({ payload: 0 });
81
+ clamp.receive({ payload: 50 });
82
+ clamp.receive({ payload: 100 });
83
+ }).catch(done);
84
+ });
85
+
86
+ it('exposes msg.clamp diagnostics', function (done) {
87
+ loadClamp(baseFlow()).then(() => {
88
+ const clamp = helper.getNode('clamp');
89
+ const out = helper.getNode('out');
90
+
91
+ out.on('input', (msg) => {
92
+ try {
93
+ expect(msg.clamp).to.deep.equal({
94
+ clamped: true,
95
+ input: 5,
96
+ output: 10,
97
+ min: 10,
98
+ max: 90,
99
+ });
100
+ done();
101
+ } catch (error) {
102
+ done(error);
103
+ }
104
+ });
105
+
106
+ clamp.receive({ payload: 5 });
107
+ }).catch(done);
108
+ });
109
+
110
+ it('updates limits at runtime via control topic', function (done) {
111
+ loadClamp(baseFlow({ controlTopic: 'clamp' })).then(() => {
112
+ const clamp = helper.getNode('clamp');
113
+ const out = helper.getNode('out');
114
+
115
+ out.on('input', (msg) => {
116
+ try {
117
+ expect(msg.payload).to.equal(20);
118
+ done();
119
+ } catch (error) {
120
+ done(error);
121
+ }
122
+ });
123
+
124
+ clamp.receive({ topic: 'clamp', min: 20, max: 80 });
125
+ clamp.receive({ payload: 5 });
126
+ }).catch(done);
127
+ });
128
+ });