node-red-contrib-boolean-logic-ultimate 1.2.15 → 1.2.17

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,21 @@
4
4
 
5
5
  # CHANGELOG
6
6
 
7
+ <p>
8
+ <b>Version 1.2.17</b> July 2026<br/>
9
+
10
+ - HysteresisUltimate: added permanent output control through <code>msg.permanentLockState</code>, supporting <code>lock</code>, <code>lockOn</code>, <code>lockOff</code> and <code>unlock</code>.<br/>
11
+ - HysteresisUltimate: control-topic configuration and diagnostics remain available while state output is locked.<br/>
12
+ - Updated the Hysteresis editor help, README documentation, automated tests and importable example flow.<br/>
13
+ - Reorganized the README with a clearer heading structure, installation instructions and a categorized node index.<br/>
14
+ </p>
15
+
16
+ <p>
17
+ <b>Version 1.2.16</b> July 2026<br/>
18
+
19
+ - DOCS: new Max Supervibe README banner and app-icon logo.<br/>
20
+ </p>
21
+
7
22
  <p>
8
23
  <b>Version 1.2.15</b> Juli 2026<br/>
9
24
 
package/README.md CHANGED
@@ -1,4 +1,10 @@
1
- ![Logo](img/logo.png)
1
+ <p align="center">
2
+ <img src="img/logo-supervibe.png" alt="Boolean Logic Ultimate - Max Supervibe" width="380">
3
+ </p>
4
+
5
+ # Boolean Logic Ultimate
6
+
7
+ A collection of enhanced logic and utility nodes for Node-RED. It supports persistent values after reboot and conversion of common Home Assistant values.
2
8
 
3
9
  [![NPM version][npm-version-image]][npm-url]
4
10
  [![NPM downloads per month][npm-downloads-month-image]][npm-url]
@@ -8,19 +14,39 @@
8
14
  [![Donate via PayPal](https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square)](https://www.paypal.me/techtoday)
9
15
  [![youtube][youtube-image]][youtube-url]
10
16
 
11
- A set of Node-RED enhanced boolean logic and utility nodes, with persistent values after reboot. Compatible also with Homeassistant values.
17
+ <p align="center">
18
+ <img src="img/readmemain.png" alt="Boolean Logic Ultimate for Node-RED — Max Supervibe" width="70%">
19
+ </p>
12
20
 
13
- <br/>
14
- <br/>
21
+ ## Getting started
15
22
 
16
- ## CHANGELOG
23
+ Install the package from the Node-RED palette manager or run this command in your Node-RED user directory:
17
24
 
18
- - See <a href="https://github.com/Supergiovane/node-red-contrib-boolean-logic-ultimate/blob/master/CHANGELOG.md">here the changelog</a>
25
+ ```shell
26
+ npm install node-red-contrib-boolean-logic-ultimate
27
+ ```
19
28
 
20
- <br/>
21
- <br/>
29
+ Importable flows are available from the Node-RED editor under **Menu (☰) → Import → Examples**, or directly in the [`examples/`](examples/) folder. See the [examples overview](examples/README.md) for the complete list.
22
30
 
23
- # EXAMPLES
31
+ ## Documentation
32
+
33
+ | Logic and routing | Timing and flow | Values and simulation |
34
+ | ----------------- | --------------- | --------------------- |
35
+ | [Boolean Logic](#boolean-logic) | [Interrupt Flows Ultimate](#interrupt-flows-ultimate) | [Translator node](#translator-node) |
36
+ | [Invert Ultimate](#invert-ultimate) | [Blinker Ultimate](#blinker-ultimate) | [Simple Output Ultimate](#simple-output-ultimate) |
37
+ | [Filter Ultimate](#filter-ultimate) | [Impulse Ultimate](#impulse-ultimate) | [Inject Ultimate](#inject-ultimate) |
38
+ | [Hysteresis Ultimate](#hysteresis-ultimate) | [Rate Limiter Ultimate](#rate-limiter-ultimate) | [Status Ultimate](#status-ultimate) |
39
+ | [Railway Switch Ultimate](#railway-switch-ultimate) | [Debouncer Ultimate](#debouncer-ultimate) | [Math Ultimate](#math-ultimate) |
40
+ | [Toggle Ultimate](#toggle-ultimate) | [Staircase Light Ultimate](#staircase-light-ultimate) | [Kalman Filter Ultimate](#kalman-filter-ultimate) |
41
+ | | | [Presence Simulator Ultimate](#presence-simulator-ultimate) |
42
+
43
+ Each node section contains its configuration, accepted input messages, outputs and runtime commands where applicable.
44
+
45
+ ## Changelog
46
+
47
+ - See the project [changelog](CHANGELOG.md) for release details.
48
+
49
+ ## Examples
24
50
 
25
51
  Importable Node-RED example flows are available in the [`examples/`](examples/) folder (one JSON per node). See [`examples/README.md`](examples/README.md) for a quick overview.
26
52
 
@@ -28,12 +54,9 @@ Importable Node-RED example flows are available in the [`examples/`](examples/)
28
54
 
29
55
  - Node-RED editor → Menu (☰) → **Import** → **Examples** and select an example from `node-red-contrib-boolean-logic-ultimate`
30
56
 
31
- <br/>
32
- <br/>
57
+ ## Translator node
33
58
 
34
- # TRANSLATOR NODE
35
-
36
- Other than true/false, all nodes accepts [Homeassistant](https://www.home-assistant.io) output strings.
59
+ In addition to true/false, the nodes accept common [Home Assistant](https://www.home-assistant.io) state strings.
37
60
 
38
61
  You can **even add your own input translation word list**, thanks to the translator-config node.
39
62
 
@@ -41,20 +64,17 @@ The translator node can translate an input payload, to a true/false boolean valu
41
64
 
42
65
  Each row in the text box, represents a translation command. <br/>
43
66
 
44
- There are some default translation's rows, to make the _boolean-logic-ultimate_ nodes compatible with Homeassistant as default. <br/>
67
+ Default translation rows make the _boolean-logic-ultimate_ nodes compatible with common Home Assistant states. <br/>
45
68
 
46
69
  You can add your own translation row.<br/>
47
70
 
48
71
  | | Description |
49
72
  | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
50
- | Translate | Add, delete or edit your own translation command. The row's translation command must be **input string:true(or false)**. For example: <code>open:true</code> <code>closed:false</code>. You can also use an expressions to be evaluated, like this <code>{{value>=50}}:true</code> and <code>{{value<50}}:false</code>. In this case, the tranlsator will evaluate (javascript eval) the expression and, if true, returns the choosen value. |
73
+ | Translate | Add, delete or edit a translation command. Each row must use the format **input string:true(or false)**, for example <code>open:true</code> or <code>closed:false</code>. Expressions such as <code>{{value>=50}}:true</code> are also supported; when the expression evaluates to true, the translator returns the selected value. |
51
74
 
52
75
  ![alt text](image.png)
53
76
 
54
- <br/>
55
- <br/>
56
-
57
- # BOOLEAN LOGIC
77
+ ## Boolean Logic
58
78
 
59
79
  <img src='https://raw.githubusercontent.com/Supergiovane/node-red-contrib-boolean-logic-ultimate/master/img/bl1.png' width='60%'>
60
80
 
@@ -94,7 +114,7 @@ You can also set the default values of the topic inputs.<br/>
94
114
 
95
115
  The node can convert arbitrary input values to true/false. It supports Homeassistant string to boolean conversion as well. For enabling auto conversion, please be sure to disable **Reject non boolean (true/false) input values** <br/>
96
116
 
97
- ### NODE CONFIGURATION
117
+ ### Configuration
98
118
 
99
119
  | Property | Description |
100
120
  | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
@@ -116,35 +136,29 @@ The node can convert arbitrary input values to true/false. It supports Homeassis
116
136
  | msg.reset = true | Resets all saved input values to undefined |
117
137
  | msg.inputcount | Changes the inputs count property. For example, <b>msg.inputcount = 3</b> Whenever you lower the inputcount from a higher number to a lower one, for example from 3 to 2, it's suggested to do a <b>msg.reset=true</b> to reset all stored input values. |
118
138
 
119
- <br/>
120
- <br/>
121
- <br/>
122
- <br/>
123
- <br/>
124
-
125
- # INTERRUPT FLOWS ULTIMATE
139
+ ## Interrupt Flows Ultimate
126
140
 
127
141
  The interrupt flows is able to stop the input messages to exiting the node.
128
142
 
129
- ### NODE CONFIGURATION
143
+ ### Configuration
130
144
 
131
145
  | Property | Description |
132
146
  | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
133
147
  | 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
- | 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"_ |
148
+ | 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
149
  | 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
150
 
137
151
  <br/>
138
152
 
139
153
  **INPUT MSG WITH "TRIGGER" TOPIC**
140
154
 
141
- Pass <code>msg.payload = true</code> to allow messages to pass through</br>
155
+ Pass <code>msg.payload = true</code> to allow messages to pass through<br/>
142
156
 
143
- Pass <code>msg.payload = false</code> to prevent messages from passing through</br>
157
+ Pass <code>msg.payload = false</code> to prevent messages from passing through<br/>
144
158
 
145
- Pass <code>msg.play = true</code> from a message having the "trigger" topic, to replay the last stored message</br>
159
+ Pass <code>msg.play = true</code> from a message having the "trigger" topic, to replay the last stored message<br/>
146
160
 
147
- Pass <code>msg.reset = true</code> from a message having the "trigger" topic, to clear the last stored message</br>
161
+ Pass <code>msg.reset = true</code> from a message having the "trigger" topic, to clear the last stored message<br/>
148
162
 
149
163
  <code>
150
164
  // Assume you set the "trigger by topic" field to "trigger"
@@ -160,7 +174,7 @@ msg.topic = "trigger"
160
174
  msg.reset = true;
161
175
  </code>
162
176
 
163
- </br>
177
+ <br/>
164
178
 
165
179
  See the example below.<br/>
166
180
 
@@ -186,19 +200,13 @@ This allow to save the state of a node and then replay it back whenever you want
186
200
  </code>
187
201
  </details>
188
202
 
189
- <br/>
190
- <br/>
191
- <br/>
192
- <br/>
193
- <br/>
194
-
195
- # INVERT ULTIMATE
203
+ ## Invert Ultimate
196
204
 
197
205
  Outputs the inverted input. For example true -> false<br />
198
206
 
199
207
  The input message is preserved and passed to the output, changing only the topic and the payload. If the input message has it's own topic, it'll be preserved as well.<br/>
200
208
 
201
- ### NODE CONFIGURATION
209
+ ### Configuration
202
210
 
203
211
  | Property | Description |
204
212
  | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -206,13 +214,7 @@ The input message is preserved and passed to the output, changing only the topic
206
214
 
207
215
  <br/>
208
216
 
209
- <br/>
210
- <br/>
211
- <br/>
212
- <br/>
213
- <br/>
214
-
215
- # FILTER ULTIMATE
217
+ ## Filter Ultimate
216
218
 
217
219
  This node has 2 outputs.<br />
218
220
 
@@ -222,7 +224,7 @@ If the input payload is false, the node will send nothing on output 1 and <code>
222
224
 
223
225
  The input message is preserved and passed to the output, changing only the topic and the payload. If the input message has it's own topic, it'll be preserved as well.<br/>
224
226
 
225
- ### NODE CONFIGURATION
227
+ ### Configuration
226
228
 
227
229
  | Property | Description |
228
230
  | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -230,15 +232,9 @@ The input message is preserved and passed to the output, changing only the topic
230
232
 
231
233
  <br/>
232
234
 
233
- <br/>
234
- <br/>
235
- <br/>
236
- <br/>
237
- <br/>
238
-
239
- # BLINKER ULTIMATE
235
+ ## Blinker Ultimate
240
236
 
241
- The pourpose of this node is to blink a led or something.<br />
237
+ This node blinks an LED or another device.<br />
242
238
 
243
239
  output1 : outputs the value true/false<br/>
244
240
 
@@ -246,7 +242,7 @@ output2 : outputs the inverted value false/true<br/>
246
242
 
247
243
  <br/>
248
244
 
249
- ### NODE CONFIGURATION
245
+ ### Configuration
250
246
 
251
247
  | Property | Description |
252
248
  | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -254,11 +250,11 @@ output2 : outputs the inverted value false/true<br/>
254
250
 
255
251
  <br/>
256
252
 
257
- Pass <code>msg.payload = true</code> to start blinking</br>
253
+ Pass <code>msg.payload = true</code> to start blinking<br/>
258
254
 
259
- Pass <code>msg.payload = false</code> to stop blinking</br>
255
+ Pass <code>msg.payload = false</code> to stop blinking<br/>
260
256
 
261
- Pass <code>msg.interval = 2000</code> to change the blinking interval</br>
257
+ Pass <code>msg.interval = 2000</code> to change the blinking interval<br/>
262
258
 
263
259
  - output1 stop behavior : when the blinker receives the stop message, you can select the behavior of the output1<br/>
264
260
  - output2 stop behavior : when the blinker receives the stop message, you can select the behavior of the output2<br/>
@@ -271,19 +267,13 @@ Pass <code>msg.interval = 2000</code> to change the blinking interval</br>
271
267
  </code>
272
268
  </details>
273
269
 
274
- <br/>
275
- <br/>
276
- <br/>
277
- <br/>
278
- <br/>
270
+ ## Simple Output Ultimate
279
271
 
280
- # SIMPLE OUTPUT ULTIMATE
281
-
282
- The pourpose of this node is to send a message with payload TRUE on the first output and FALSE on second output, independently from the msg input.<br />
272
+ This node sends a message with payload TRUE on the first output and FALSE on the second output, independently of the input message.<br />
283
273
 
284
274
  This is useful if you need to simply send a true or false payload.
285
275
 
286
- ### NODE CONFIGURATION
276
+ ### Configuration
287
277
 
288
278
  | Property | Description |
289
279
  | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -299,15 +289,9 @@ This is useful if you need to simply send a true or false payload.
299
289
  </code>
300
290
  </details>
301
291
 
302
- <br/>
303
- <br/>
304
- <br/>
305
- <br/>
306
- <br/>
307
-
308
- # INJECT ULTIMATE
292
+ ## Inject Ultimate
309
293
 
310
- The pourpose of this node is to send a message with payload TRUE on the first output, FALSE on second output and a TOGGLE (true/false) on the third output, by pressing the pushbutton.<br />
294
+ This node sends TRUE on the first output, FALSE on the second output and a toggled true/false value on the third output when its button is pressed.<br />
311
295
 
312
296
  This is useful if you need to simply test your flow.
313
297
 
@@ -319,15 +303,9 @@ This is useful if you need to simply test your flow.
319
303
  </code>
320
304
  </details>
321
305
 
322
- <br/>
323
- <br/>
324
- <br/>
325
- <br/>
326
- <br/>
327
-
328
- # STATUS ULTIMATE
306
+ ## Status Ultimate
329
307
 
330
- The pourpose of this node is to show a status of the passingthrough message.<br />
308
+ This node displays the value of a passing message as its Node-RED status.<br />
331
309
 
332
310
  **Show msg.**
333
311
 
@@ -340,15 +318,9 @@ The pourpose of this node is to show a status of the passingthrough message.<br
340
318
  [{"id":"8c1648bf.58e6","type":"StatusUltimate","z":"5c2de561.6a0de4","name":"Status","property":"testobject.color","x":90,"y":180,"wires":[["b96cd259.3f8398"]]},{"id":"3beb9c6.90d1e64","type":"function","z":"5c2de561.6a0de4","name":"Dummy msg","func":"msg.payload = \"The payload is \" + msg.payload;\nmsg.myproperty = \"This is my custom property\";\nmsg.testobject = {len : 100, color : \"blue\"};\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":230,"y":100,"wires":[["8c1648bf.58e6"]]},{"id":"84080b79.df3f38","type":"debug","z":"5c2de561.6a0de4","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":710,"y":180,"wires":[]},{"id":"b534d2ec.635398","type":"InjectUltimate","z":"5c2de561.6a0de4","name":"Inject","topic":"1","curVal":true,"x":90,"y":100,"wires":[[],[],["3beb9c6.90d1e64"]]},{"id":"9745c77a.0361b","type":"comment","z":"5c2de561.6a0de4","name":"View the status of a message passing through the StatusUltimate node","info":"","x":270,"y":40,"wires":[]},{"id":"b96cd259.3f8398","type":"StatusUltimate","z":"5c2de561.6a0de4","name":"Status","property":"payload","x":230,"y":180,"wires":[["ac2b784b.b44a48"]]},{"id":"ac2b784b.b44a48","type":"StatusUltimate","z":"5c2de561.6a0de4","name":"Status","property":"myproperty","x":450,"y":180,"wires":[["84080b79.df3f38"]]}]
341
319
  </code>
342
320
  </details>
343
- <br/>
344
- <br/>
345
- <br/>
346
- <br/>
347
- <br/>
348
-
349
- # IMPULSE ULTIMATE
321
+ ## Impulse Ultimate
350
322
 
351
- The pourpose of this node is to send a sequence of pulsed commands to for example, open a garage door or to command an appliance requiring a set of timed commands.<br />
323
+ This node sends a timed sequence of pulsed commands, for example to open a garage door or control an appliance.<br />
352
324
 
353
325
  <img src='https://raw.githubusercontent.com/Supergiovane/node-red-contrib-boolean-logic-ultimate/master/img/Impulse.png' width='60%'>
354
326
  <details><summary>CLICK HERE, copy and paste it into your flow</summary>
@@ -357,7 +329,7 @@ The pourpose of this node is to send a sequence of pulsed commands to for exampl
357
329
  </code>
358
330
  </details>
359
331
 
360
- ### NODE CONFIGURATION
332
+ ### Configuration
361
333
 
362
334
  | Property | Description |
363
335
  | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -379,23 +351,17 @@ Restart the sequence from the beginning. Use **_restart_** alone, without **:**
379
351
  comment. For example: **_// This opens the garage_**. The comment are ignored, so you can write what you want.<br />
380
352
  <br />
381
353
 
382
- Pass <code>msg.payload = true</code> to the node to start the sequence</br>
354
+ Pass <code>msg.payload = true</code> to the node to start the sequence<br/>
383
355
 
384
- Pass <code>msg.payload = false</code> to the node to stop the running sequence</br>
356
+ Pass <code>msg.payload = false</code> to the node to stop the running sequence<br/>
385
357
 
386
358
  <br />
387
359
 
388
360
  - Output: the node outputs a message you specified in the command textbox<br/>
389
361
 
390
- <br/>
391
- <br/>
392
- <br/>
393
- <br/>
394
- <br/>
362
+ ## Math Ultimate
395
363
 
396
- # MATH ULTIMATE
397
-
398
- The pourpose of this node is to do maths on the incoming values. Each incoming message MUST HAVE OWN TOPIC.<br />
364
+ This node performs calculations on incoming values. Each incoming message must have its own topic.<br />
399
365
 
400
366
  <img src='https://raw.githubusercontent.com/Supergiovane/node-red-contrib-boolean-logic-ultimate/master/img/sum.png' width='60%'>
401
367
  <details><summary>CLICK HERE, copy and paste it into your flow</summary>
@@ -404,7 +370,7 @@ The pourpose of this node is to do maths on the incoming values. Each incoming m
404
370
  </code>
405
371
  </details>
406
372
 
407
- ### NODE CONFIGURATION
373
+ ### Configuration
408
374
 
409
375
  | Property | Description |
410
376
  | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -430,18 +396,11 @@ resets the values to zero.
430
396
  }
431
397
  </pre>
432
398
 
433
- br/>
399
+ ## Toggle Ultimate
434
400
 
435
- <br/>
436
- <br/>
437
- <br/>
438
- <br/>
439
-
440
- # TOGGLE ULTIMATE
401
+ This node toggles between true and false whenever an inbound message arrives.<br />
441
402
 
442
- The pourpose of this node is to toggle between true/false, everytime an inboud message arrives.<br />
443
-
444
- ### NODE CONFIGURATION
403
+ ### Configuration
445
404
 
446
405
  | Property | Description |
447
406
  | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -451,27 +410,21 @@ The pourpose of this node is to toggle between true/false, everytime an inboud m
451
410
 
452
411
  **INPUT**<br />
453
412
 
454
- Any message that arrives on input, will be passwd through to the output with the payload toggled between true and false.
455
-
456
- <br/>
457
- <br/>
458
- <br/>
459
- <br/>
460
- <br/>
413
+ Every input message is passed to the output with its payload toggled between true and false.
461
414
 
462
- # RAILWAY SWITCH ULTIMATE
415
+ ## Railway Switch Ultimate
463
416
 
464
- The railway switcher, redirect the incoming messages to one ot the avaiable output pins, just like a railway jinction switch.
417
+ The railway switch redirects incoming messages to one of the available outputs, like a railway junction.
465
418
 
466
419
  <img src='https://raw.githubusercontent.com/Supergiovane/node-red-contrib-boolean-logic-ultimate/master/img/railroadSwitchScambio.png' width='80%'>
467
420
 
468
- ### NODE CONFIGURATION
421
+ ### Configuration
469
422
 
470
423
  | Property | Description |
471
424
  | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
472
425
  | Switcher topic | Whenever the node receives a payload from this **topic**, it redirects the input messages to a choosen output PIN. |
473
426
  | Output pins | Number of output pins (outputs) to show. Default is 5, range is 1..10. |
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"_ |
427
+ | 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
428
  | Translator | Translates the incoming <code>payload</code> value. This allows the compatibility with, for example, **HomeAssistant** nodes. |
476
429
 
477
430
  ### Inputs
@@ -519,7 +472,7 @@ this JSON input message redirects all input messages to the third PIN, and so on
519
472
  }
520
473
  ```
521
474
 
522
- </br>
475
+ <br/>
523
476
 
524
477
  See the example below.<br/>
525
478
 
@@ -616,14 +569,9 @@ Copy and paste it into your flow
616
569
  ];
617
570
  ```
618
571
 
619
- <br/>
620
- <br/>
621
- <br/>
622
- <br/>
623
-
624
- # KALMAN FILTER ULTIMATE
572
+ ## Kalman Filter Ultimate
625
573
 
626
- ## Outputs the Kalman filtered input.
574
+ Outputs the Kalman-filtered input.
627
575
 
628
576
  Please refer to [this](https://github.com/wouterbulten/kalmanjs) link, on how it works.
629
577
 
@@ -644,13 +592,11 @@ Please refer to [this](https://github.com/wouterbulten/kalmanjs) link, on how it
644
592
 
645
593
  : payload (number) : the payload containing the number. If you've changed the incoming evaluation property in the **_Input_** field, the number to be evaluated must be put in such message's property, instead of the _payload_ property.
646
594
 
647
- <br/>
648
-
649
- # RATE LIMITER ULTIMATE
595
+ ## Rate Limiter Ultimate
650
596
 
651
597
  Gateway per sensori e dispositivi troppo “chiacchieroni”: limita burst e rimbalzi con modalità **Debounce**, **Throttle** e **Window**. Lo stato del nodo riporta sempre modalità corrente, contatori di messaggi inoltrati e bloccati.
652
598
 
653
- ### NODE CONFIGURATION
599
+ ### Configuration
654
600
 
655
601
  | Property | Description |
656
602
  | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -663,29 +609,27 @@ Gateway per sensori e dispositivi troppo “chiacchieroni”: limita burst e rim
663
609
  | Max messages | Numero di messaggi ammessi nella finestra. |
664
610
  | On limit | _Drop_ scarta i messaggi extra, _Queue last_ accoda l’ultimo e lo riproduce appena possibile. |
665
611
  | Control topic | Topic dei messaggi di controllo (default `rate`). |
666
- | Input | Proprietà del messaggio da monitorare (default `msg.payload`). |
612
+ | Input | Proprietà del messaggio da monitorare (default `msg.payload`). |
667
613
  | Stats every (s) | Ogni quanti secondi emettere un riepilogo statistico (0 = disattivato). |
668
614
  | Translator | Nodo translator-config opzionale per adattare le stringhe d’ingresso a true/false. |
669
615
 
670
616
  <br/>
671
617
 
672
- ### OUTPUTS
618
+ ### Outputs
673
619
 
674
620
  - **Output 1**: messaggi inoltrati, invariati.
675
621
  - **Output 2**: diagnostica (`{mode, reason, passed, dropped, msg, propertyValue}`) e statistiche periodiche su `controlTopic/stats`.
676
622
 
677
623
  <br/>
678
624
 
679
- ### CONTROL MESSAGES (`msg.topic === controlTopic`)
625
+ ### Control messages (`msg.topic === controlTopic`)
680
626
 
681
627
  - `msg.reset = true` &rarr; azzera contatori, accoda azzerata e timer cancellati.
682
628
  - `msg.flush = true` &rarr; forza l’emissione immediata del messaggio in attesa.
683
629
  - `msg.mode = 'debounce'|'throttle'|'window'` &rarr; cambia modalità runtime e resetta lo stato.
684
630
  - `msg.interval`, `msg.wait`, `msg.windowSize`, `msg.maxInWindow` &rarr; aggiorna i parametri corrispondenti.
685
631
 
686
- <br/>
687
-
688
- # DEBOUNCER ULTIMATE
632
+ ## Debouncer Ultimate
689
633
 
690
634
  Nodo dedicato al solo debounce: utile quando vuoi filtrare rimbalzi o burst rapidi senza portarti dietro le altre modalità del `RateLimiterUltimate`. Può inoltrare il **primo** messaggio, l’**ultimo** oppure **entrambi**, dopo un intervallo di quiete configurabile.
691
635
 
@@ -695,32 +639,30 @@ Nell'immagine si vede il comportamento tipico: a sinistra arrivano molti messagg
695
639
 
696
640
  Example flow: [`examples/DebouncerUltimate.json`](examples/DebouncerUltimate.json)
697
641
 
698
- ### NODE CONFIGURATION
642
+ ### Configuration
699
643
 
700
- | Property | Description |
701
- | ------------- | --------------------------------------------------------------------------------------------- |
702
- | Wait (ms) | Tempo di quiete richiesto prima di chiudere la finestra di debounce. |
703
- | Emit | Scegli se inviare subito il primo messaggio, solo l’ultimo dopo la pausa, oppure entrambi. |
704
- | Control topic | Topic dei messaggi di controllo (default `debouncer`). |
644
+ | Property | Description |
645
+ | ------------- | ------------------------------------------------------------------------------------------ |
646
+ | Wait (ms) | Tempo di quiete richiesto prima di chiudere la finestra di debounce. |
647
+ | Emit | Scegli se inviare subito il primo messaggio, solo l’ultimo dopo la pausa, oppure entrambi. |
648
+ | Control topic | Topic dei messaggi di controllo (default `debouncer`). |
705
649
 
706
- ### OUTPUT
650
+ ### Outputs
707
651
 
708
652
  - **Output 1**: messaggi inoltrati dopo il debounce.
709
653
 
710
- ### CONTROL MESSAGES (`msg.topic === controlTopic`)
654
+ ### Control messages (`msg.topic === controlTopic`)
711
655
 
712
656
  - `msg.reset = true` &rarr; cancella timer e messaggio pendente.
713
657
  - `msg.flush = true` &rarr; inoltra subito l’ultimo messaggio in attesa.
714
658
  - `msg.wait` &rarr; aggiorna il ritardo di debounce a runtime.
715
659
  - `msg.emitOn = 'leading'|'trailing'|'both'` &rarr; cambia la modalità di emissione.
716
660
 
717
- <br/>
718
-
719
- # PRESENCE SIMULATOR ULTIMATE
661
+ ## Presence Simulator Ultimate
720
662
 
721
663
  The purpose of this node is to replay a programmable sequence of messages in order to simulate occupancy when you are away.
722
664
 
723
- ### NODE CONFIGURATION
665
+ ### Configuration
724
666
 
725
667
  | Property | Description |
726
668
  | ------------- | ---------------------------------------------------------------------------------------------- |
@@ -729,11 +671,11 @@ The purpose of this node is to replay a programmable sequence of messages in ord
729
671
  | Loop sequence | Repeats the sequence when it reaches the end. |
730
672
  | Random delays | Enables a random variation of the programmed delays. |
731
673
  | Jitter (%) | Maximum percentage of variation applied when random delays are enabled. |
732
- | Input | Message property to inspect for inline events (default `payload`). |
674
+ | Input | Message property to inspect for inline events (default `payload`). |
733
675
  | Translator | Optional translator-config to convert incoming values. |
734
676
  | Sequence | One JSON object per line, each containing at least `delay` (ms) plus the properties to output. |
735
677
 
736
- ### CONTROL MESSAGES (`msg.topic === controlTopic`)
678
+ ### Control messages (`msg.topic === controlTopic`)
737
679
 
738
680
  - `msg.command = 'start'` / `msg.start = true` &rarr; begin playback.
739
681
  - `msg.command = 'stop'` / `msg.stop = true` &rarr; halt playback.
@@ -743,13 +685,11 @@ The purpose of this node is to replay a programmable sequence of messages in ord
743
685
 
744
686
  Each event in the sequence outputs a message configured in the JSON line. When random delays are enabled, the effective delay is varied within the configured jitter.
745
687
 
746
- <br/>
747
-
748
- # STAIRCASE LIGHT ULTIMATE
688
+ ## Staircase Light Ultimate
749
689
 
750
690
  The purpose of this node is to control staircase lighting with a timer, pre-off warning and optional extension on every trigger.
751
691
 
752
- ### NODE CONFIGURATION
692
+ ### Configuration
753
693
 
754
694
  | Property | Description |
755
695
  | ------------------ | ------------------------------------------------------------------ |
@@ -759,12 +699,12 @@ The purpose of this node is to control staircase lighting with a timer, pre-off
759
699
  | Warning offset (s) | Seconds before switch-off when the warning is sent. |
760
700
  | Restart on trigger | Restarts the timer when a new trigger arrives while active. |
761
701
  | Allow off input | Allows a `false` from the main input to switch off immediately. |
762
- | Input | Message property evaluated as trigger (default `payload`). |
702
+ | Input | Message property evaluated as trigger (default `payload`). |
763
703
  | Translator | Optional translator-config for true/false conversion. |
764
704
  | On/Off payload | Values emitted on output 1 to turn the light on/off. |
765
705
  | Warning payload | Value emitted on output 2 when the warning fires. |
766
706
 
767
- ### CONTROL MESSAGES (`msg.topic === controlTopic`)
707
+ ### Control messages (`msg.topic === controlTopic`)
768
708
 
769
709
  - `msg.command = 'on'` / `msg.start = true` &rarr; start the timer and turn on the light.
770
710
  - `msg.command = 'off'` / `msg.stop = true` &rarr; switch off immediately.
@@ -773,35 +713,57 @@ The purpose of this node is to control staircase lighting with a timer, pre-off
773
713
 
774
714
  Output 1 delivers the ON/OFF command. Output 2 delivers the warning and includes `msg.remaining` with the seconds left.
775
715
 
776
- <br/>
777
-
778
- # HYSTERESIS ULTIMATE
716
+ ## Hysteresis Ultimate
779
717
 
780
718
  Adds hysteresis to numeric sensor values to avoid rapid ON/OFF bouncing around thresholds.
781
719
 
782
720
  Example flow: [`examples/HysteresisUltimate.json`](examples/HysteresisUltimate.json)
783
721
 
784
- ### NODE CONFIGURATION
722
+ ### Configuration
723
+
724
+ | Property | Description |
725
+ | ------------------- | ----------- |
726
+ | Name | Optional label shown in the flow editor. |
727
+ | Control topic | Topic used for runtime commands (default `hysteresis`). It is not required for `permanentLockState`. |
728
+ | Mode | `high`: ON at or above the ON threshold and OFF at or below the OFF threshold. `low`: ON at or below the ON threshold and OFF at or above the OFF threshold. Values between the thresholds retain the current state. |
729
+ | ON threshold | Numeric limit that changes the internal state to ON according to the selected mode. |
730
+ | OFF threshold | Numeric limit that changes the internal state to OFF according to the selected mode. |
731
+ | Initial state ON | Initial internal state after deployment or restart. It does not emit at startup and is restored by `msg.reset = true`. |
732
+ | Emit only on change | When enabled, output 1 emits only on transitions. When disabled, every valid numeric input emits the current state. |
733
+ | Input | Message property evaluated as a numeric value (default `msg.payload`). Numbers, booleans and numeric strings are accepted. |
734
+ | Translator | Optional shared translator configuration associated with the input. Hysteresis evaluation uses the numeric value of the selected input property. |
735
+ | On payload | Typed value assigned to `msg.payload` on output 1 while the state is ON. |
736
+ | Off payload | Typed value assigned to `msg.payload` on output 1 while the state is OFF. |
785
737
 
786
- | Property | Description |
787
- | ------------------- | ------------------------------------------------------------------------------ |
788
- | Control topic | Topic that receives runtime commands such as threshold updates and reset. |
789
- | Mode | `high` = ON above threshold, OFF below. `low` = ON below threshold, OFF above. |
790
- | ON/OFF threshold | Hysteresis limits. |
791
- | Initial state | Startup output state. |
792
- | Emit only on change | If enabled, output 1 emits only on state transitions. |
793
- | Input | Message property evaluated as numeric value (default `payload`). |
794
- | Translator | Optional translator-config. |
795
- | On/Off payload | Typed payloads sent on output 1. |
738
+ ### Input messages
796
739
 
797
- ### CONTROL MESSAGES (`msg.topic === controlTopic`)
740
+ Normal messages are evaluated using the configured **Input** property. Missing or non-numeric values generate an `invalid_input` diagnostic on output 2.
741
+
742
+ ### Control messages (`msg.topic === controlTopic`)
798
743
 
799
744
  - `msg.onThreshold`, `msg.offThreshold` &rarr; update thresholds at runtime.
800
- - `msg.state = true|false` &rarr; force state.
801
- - `msg.reset = true` &rarr; restore initial state.
745
+ - `msg.state = true|false` &rarr; force the state and emit it on output 1. While locked, it updates the internal state without emitting on output 1.
746
+ - `msg.reset = true` &rarr; restore the initial state and emit a `reset` diagnostic.
802
747
  - `msg.status = true` &rarr; emit current state/thresholds on output 2.
803
748
 
804
- Output 1 emits the command payload (`onPayload`/`offPayload`). Output 2 emits diagnostic events.
749
+ ### Permanent output lock
750
+
751
+ Regardless of `msg.topic`, `msg.permanentLockState` accepts:
752
+
753
+ - `lock` &rarr; suppress state output until another permanent-lock command arrives.
754
+ - `lockOn` &rarr; force ON, emit the configured ON payload once, then suppress state output.
755
+ - `lockOff` &rarr; force OFF, emit the configured OFF payload once, then suppress state output.
756
+ - `unlock` &rarr; resume normal processing (the unlock message itself is consumed).
757
+
758
+ While locked, normal input messages are ignored and output 1 remains suppressed. Control-topic commands remain active: thresholds can be updated, `state` and `reset` can change the internal state, and `status`/`reset` diagnostics can still be emitted on output 2.
759
+
760
+ ### Outputs
761
+
762
+ 1. **State output** emits a clone of the input message with:
763
+ - the configured `onPayload` or `offPayload` in `msg.payload`;
764
+ - `msg.event` set to `state_changed` or `state_confirmed`;
765
+ - `msg.hysteresis` containing `state`, `changed`, `mode`, input `value`, `onThreshold` and `offThreshold`.
766
+ 2. **Diagnostics** emits state-change, invalid-input, reset and status events. It sets `msg.topic` to `<controlTopic>/event` and places the event details in `msg.payload`.
805
767
 
806
768
  <br/>
807
769
 
@@ -121,17 +121,40 @@
121
121
 
122
122
  |Property|Description|
123
123
  |--|--|
124
- | Mode | `high`: ON above threshold, OFF below. `low`: ON below threshold, OFF above. |
125
- | ON/OFF threshold | The two hysteresis limits. |
126
- | Emit only on change | Emits output only when state changes. |
127
- | Input | Message property to evaluate (default `payload`). |
128
- | Translator | Optional translator-config. |
129
- | On/Off payload | Values sent on output 1. |
124
+ | Name | Optional label shown in the flow editor. |
125
+ | Control topic | Topic used for runtime commands. The default is `hysteresis`. It is not required for `permanentLockState`. |
126
+ | Mode | `high`: switch ON when the value is greater than or equal to the ON threshold and OFF when it is less than or equal to the OFF threshold. `low`: switch ON when the value is less than or equal to the ON threshold and OFF when it is greater than or equal to the OFF threshold. Values between the thresholds keep the current state. |
127
+ | ON threshold | Numeric limit that changes the internal state to ON, according to the selected mode. |
128
+ | OFF threshold | Numeric limit that changes the internal state to OFF, according to the selected mode. |
129
+ | Initial state ON | Initial internal state after deployment or restart. It does not emit a message by itself. It is also restored by `msg.reset = true`. |
130
+ | Emit only on change | When enabled, output 1 emits only when the state changes. When disabled, every valid numeric input emits the current ON/OFF state. |
131
+ | Input | Numeric message property to evaluate. The default is `msg.payload`; numbers, booleans and numeric strings are accepted. |
132
+ | Translator | Optional shared translator configuration associated with the input. Hysteresis evaluation uses the numeric value of the selected input property. |
133
+ | On payload | Typed value written to `msg.payload` on output 1 when the state is ON. |
134
+ | Off payload | Typed value written to `msg.payload` on output 1 when the state is OFF. |
130
135
 
131
- Control topic messages:
136
+ ### Inputs
137
+
138
+ Normal input messages are evaluated using the configured **Input** property. An absent or non-numeric value produces an `invalid_input` diagnostic on output 2.
139
+
140
+ The following commands are consumed only when `msg.topic` equals **Control topic**:
132
141
 
133
142
  - `msg.onThreshold`, `msg.offThreshold` to update thresholds at runtime.
134
- - `msg.state = true|false` to force state.
135
- - `msg.reset = true` to restore initial state.
143
+ - `msg.state = true|false` to force the state and emit it on output 1. While locked, it updates the internal state without emitting on output 1.
144
+ - `msg.reset = true` to restore the configured initial state and emit a `reset` diagnostic.
136
145
  - `msg.status = true` to emit current status on output 2.
137
- </script>
146
+
147
+ `msg.permanentLockState` is handled regardless of `msg.topic`:
148
+
149
+ - `"lock"` suppresses state output until another valid permanent-lock command arrives.
150
+ - `"lockOn"` forces ON and emits the configured **On payload** once, then suppresses state output.
151
+ - `"lockOff"` forces OFF and emits the configured **Off payload** once, then suppresses state output.
152
+ - `"unlock"` resumes normal processing. The unlock message itself is consumed.
153
+
154
+ While locked, normal input messages are ignored and output 1 remains suppressed. Control-topic commands remain active: thresholds can be updated, `state` and `reset` can change the internal state, and `status`/`reset` diagnostics can still be emitted on output 2.
155
+
156
+ ### Outputs
157
+
158
+ 1. **State output**: a clone of the input message with the configured ON/OFF `msg.payload`, `msg.event` (`state_changed` or `state_confirmed`) and `msg.hysteresis`, containing `state`, `changed`, `mode`, input `value`, `onThreshold` and `offThreshold`.
159
+ 2. **Diagnostics**: events for state changes, invalid inputs, reset and status. The topic is `<controlTopic>/event`, and `msg.payload` contains the event details.
160
+ </script>
@@ -18,6 +18,7 @@ module.exports = function (RED) {
18
18
  let onThreshold = Number(config.onThreshold);
19
19
  let offThreshold = Number(config.offThreshold);
20
20
  let state = Boolean(config.initialState);
21
+ let permanentLockState = 'unlock';
21
22
 
22
23
  if (!Number.isFinite(onThreshold)) onThreshold = 70;
23
24
  if (!Number.isFinite(offThreshold)) offThreshold = 65;
@@ -47,10 +48,11 @@ module.exports = function (RED) {
47
48
  function updateStatus(lastValue) {
48
49
  const direction = mode === 'low' ? 'LOW' : 'HIGH';
49
50
  const valueText = lastValue === undefined ? '-' : Number(lastValue).toFixed(2);
51
+ const lockText = permanentLockState === 'unlock' ? '' : ` ${permanentLockState}`;
50
52
  setNodeStatus({
51
53
  fill: state ? 'green' : 'grey',
52
54
  shape: state ? 'dot' : 'ring',
53
- text: `${direction} ${valueText} on:${onThreshold} off:${offThreshold}`,
55
+ text: `${direction} ${valueText} on:${onThreshold} off:${offThreshold}${lockText}`,
54
56
  });
55
57
  }
56
58
 
@@ -101,7 +103,7 @@ module.exports = function (RED) {
101
103
  updateStatus(inputValue);
102
104
  }
103
105
 
104
- function handleControl(msg) {
106
+ function handleControl(msg, suppressStateOutput) {
105
107
  let consumed = false;
106
108
 
107
109
  if (msg.reset === true) {
@@ -134,7 +136,9 @@ module.exports = function (RED) {
134
136
 
135
137
  if (Object.prototype.hasOwnProperty.call(msg, 'state')) {
136
138
  state = Boolean(msg.state);
137
- emitState(msg, true, undefined);
139
+ if (!suppressStateOutput) {
140
+ emitState(msg, true, undefined);
141
+ }
138
142
  consumed = true;
139
143
  }
140
144
 
@@ -156,8 +160,46 @@ module.exports = function (RED) {
156
160
  return consumed;
157
161
  }
158
162
 
163
+ function handlePermanentLock(msg) {
164
+ if (!Object.prototype.hasOwnProperty.call(msg, 'permanentLockState')) {
165
+ return false;
166
+ }
167
+
168
+ const command = msg.permanentLockState;
169
+ if (!['unlock', 'lock', 'lockOff', 'lockOn'].includes(command)) {
170
+ return false;
171
+ }
172
+
173
+ if (command === 'unlock') {
174
+ permanentLockState = 'unlock';
175
+ updateStatus();
176
+ return true;
177
+ }
178
+
179
+ permanentLockState = command;
180
+ if (command === 'lockOn' || command === 'lockOff') {
181
+ state = command === 'lockOn';
182
+ emitState(msg, true, undefined);
183
+ } else {
184
+ updateStatus();
185
+ }
186
+ return true;
187
+ }
188
+
159
189
  node.on('input', (msg) => {
160
- if (msg.topic === controlTopic && handleControl(msg)) {
190
+ if (handlePermanentLock(msg)) {
191
+ return;
192
+ }
193
+
194
+ const isLocked = permanentLockState !== 'unlock';
195
+
196
+ if (msg.topic === controlTopic) {
197
+ if (handleControl(msg, isLocked) || isLocked) {
198
+ return;
199
+ }
200
+ }
201
+
202
+ if (isLocked) {
161
203
  return;
162
204
  }
163
205
 
@@ -76,6 +76,80 @@
76
76
  "y": 240,
77
77
  "wires": [["hys-node"]]
78
78
  },
79
+ {
80
+ "id": "hys-lock-comment",
81
+ "type": "comment",
82
+ "z": "hys-flow",
83
+ "name": "Permanent output lock commands (independent of msg.topic)",
84
+ "info": "lock: suppress state output\nlockOn: emit ON once, then suppress state output\nlockOff: emit OFF once, then suppress state output\nunlock: resume normal processing\n\nControl-topic commands and diagnostics remain active while locked.",
85
+ "x": 310,
86
+ "y": 320,
87
+ "wires": []
88
+ },
89
+ {
90
+ "id": "hys-lock",
91
+ "type": "inject",
92
+ "z": "hys-flow",
93
+ "name": "Lock without output",
94
+ "props": [
95
+ { "p": "permanentLockState", "v": "lock", "vt": "str" }
96
+ ],
97
+ "repeat": "",
98
+ "crontab": "",
99
+ "once": false,
100
+ "onceDelay": 0.1,
101
+ "x": 230,
102
+ "y": 360,
103
+ "wires": [["hys-node"]]
104
+ },
105
+ {
106
+ "id": "hys-lock-on",
107
+ "type": "inject",
108
+ "z": "hys-flow",
109
+ "name": "Force ON and lock",
110
+ "props": [
111
+ { "p": "permanentLockState", "v": "lockOn", "vt": "str" }
112
+ ],
113
+ "repeat": "",
114
+ "crontab": "",
115
+ "once": false,
116
+ "onceDelay": 0.1,
117
+ "x": 230,
118
+ "y": 400,
119
+ "wires": [["hys-node"]]
120
+ },
121
+ {
122
+ "id": "hys-lock-off",
123
+ "type": "inject",
124
+ "z": "hys-flow",
125
+ "name": "Force OFF and lock",
126
+ "props": [
127
+ { "p": "permanentLockState", "v": "lockOff", "vt": "str" }
128
+ ],
129
+ "repeat": "",
130
+ "crontab": "",
131
+ "once": false,
132
+ "onceDelay": 0.1,
133
+ "x": 230,
134
+ "y": 440,
135
+ "wires": [["hys-node"]]
136
+ },
137
+ {
138
+ "id": "hys-unlock",
139
+ "type": "inject",
140
+ "z": "hys-flow",
141
+ "name": "Unlock",
142
+ "props": [
143
+ { "p": "permanentLockState", "v": "unlock", "vt": "str" }
144
+ ],
145
+ "repeat": "",
146
+ "crontab": "",
147
+ "once": false,
148
+ "onceDelay": 0.1,
149
+ "x": 200,
150
+ "y": 480,
151
+ "wires": [["hys-node"]]
152
+ },
79
153
  {
80
154
  "id": "hys-out",
81
155
  "type": "debug",
Binary file
Binary file
package/package.json CHANGED
@@ -1,25 +1,40 @@
1
1
  {
2
2
  "name": "node-red-contrib-boolean-logic-ultimate",
3
- "version": "1.2.15",
3
+ "version": "1.2.17",
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
- "author": "Supergiovane (https://github.com/Supergiovane)",
5
+ "author": {
6
+ "name": "Massimo Saccani",
7
+ "email": "maxsupergiovane@icloud.com",
8
+ "url": "https://youtube.com/@maxsupervibe"
9
+ },
6
10
  "dependencies": {
7
- "fs": "0.0.1-security",
8
- "path": ">=0.12.7",
9
- "kalmanjs": "1.1.0"
11
+ "fs": "^0.0.1-security",
12
+ "kalmanjs": "^1.1.0",
13
+ "path": "^0.12.7"
10
14
  },
11
15
  "devDependencies": {
12
- "chai": "^4.3.10",
13
- "mocha": "^10.4.0",
14
- "node-red": "^3.1.0",
15
- "node-red-node-test-helper": "^0.3.5"
16
+ "chai": "^6.2.2",
17
+ "mocha": "^11.7.6",
18
+ "node-red": "^5.0.1",
19
+ "node-red-node-test-helper": "^0.3.6"
16
20
  },
17
21
  "keywords": [
18
22
  "node-red",
23
+ "node-red-contrib",
19
24
  "boolean",
25
+ "logic",
20
26
  "gate",
21
27
  "filter",
22
- "logic"
28
+ "debounce",
29
+ "toggle",
30
+ "blinker",
31
+ "comparator",
32
+ "hysteresis",
33
+ "rate-limiter",
34
+ "kalman",
35
+ "utility",
36
+ "home-assistant",
37
+ "home-automation"
23
38
  ],
24
39
  "license": "MIT",
25
40
  "repository": {
@@ -54,4 +69,4 @@
54
69
  "scripts": {
55
70
  "test": "mocha test/**/*.spec.js"
56
71
  }
57
- }
72
+ }
@@ -84,4 +84,61 @@ describe('HysteresisUltimate node', function () {
84
84
  hys.receive({ topic: 'sensor', payload: 58 });
85
85
  }).catch(done);
86
86
  });
87
+
88
+ it('supports permanent lock, lockOn, lockOff and unlock commands', function (done) {
89
+ const flowId = 'hys-lock';
90
+ const flow = [
91
+ { id: flowId, type: 'tab', label: 'hys-lock' },
92
+ {
93
+ id: 'hys',
94
+ type: 'HysteresisUltimate',
95
+ z: flowId,
96
+ mode: 'high',
97
+ onThreshold: 70,
98
+ offThreshold: 60,
99
+ emitOnlyOnChange: true,
100
+ onPayload: 'on',
101
+ onPayloadType: 'str',
102
+ offPayload: 'off',
103
+ offPayloadType: 'str',
104
+ wires: [['out'], ['diag']],
105
+ },
106
+ { id: 'out', type: 'helper', z: flowId },
107
+ { id: 'diag', type: 'helper', z: flowId },
108
+ ];
109
+
110
+ loadHysteresis(flow).then(() => {
111
+ const hys = helper.getNode('hys');
112
+ const out = helper.getNode('out');
113
+ const diag = helper.getNode('diag');
114
+ const outputPayloads = [];
115
+ const diagnostics = [];
116
+
117
+ out.on('input', (msg) => outputPayloads.push(msg.payload));
118
+ diag.on('input', (msg) => diagnostics.push(msg.payload));
119
+
120
+ hys.receive({ permanentLockState: 'lockOn' });
121
+ hys.receive({ payload: 55 });
122
+ hys.receive({ topic: 'hysteresis', onThreshold: 74, status: true });
123
+ hys.receive({ topic: 'hysteresis', state: false });
124
+ hys.receive({ permanentLockState: 'lockOff' });
125
+ hys.receive({ payload: 75 });
126
+ hys.receive({ permanentLockState: 'lock' });
127
+ hys.receive({ payload: 75 });
128
+ hys.receive({ permanentLockState: 'unlock' });
129
+ hys.receive({ payload: 75 });
130
+
131
+ setTimeout(() => {
132
+ try {
133
+ expect(outputPayloads).to.deep.equal(['on', 'off', 'on']);
134
+ expect(diagnostics).to.have.length(2);
135
+ expect(diagnostics[0]).to.include({ event: 'status', state: true, onThreshold: 74 });
136
+ expect(diagnostics[1]).to.include({ event: 'state_changed', state: true, onThreshold: 74 });
137
+ done();
138
+ } catch (error) {
139
+ done(error);
140
+ }
141
+ }, 50);
142
+ }).catch(done);
143
+ });
87
144
  });