node-red-contrib-boolean-logic-ultimate 1.0.39 → 1.0.43
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.
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
// Usare IntelliSense per informazioni sui possibili attributi.
|
|
3
|
+
// Al passaggio del mouse vengono visualizzate le descrizioni degli attributi esistenti.
|
|
4
|
+
// Per altre informazioni, visitare: https://go.microsoft.com/fwlink/?linkid=830387
|
|
5
|
+
"version": "0.2.0",
|
|
6
|
+
"configurations": [
|
|
7
|
+
{
|
|
8
|
+
"type": "pwa-node",
|
|
9
|
+
"request": "launch",
|
|
10
|
+
"name": "Launch Program",
|
|
11
|
+
"skipFiles": [
|
|
12
|
+
"<node_internals>/**"
|
|
13
|
+
],
|
|
14
|
+
"program": "${workspaceFolder}/boolean-logic-ultimate/BooleanLogicUltimate.js"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
# node-red-contrib-boolean-logic-ultimate
|
|
2
2
|
[](https://www.paypal.me/techtoday)
|
|
3
3
|
|
|
4
|
+
<p>
|
|
5
|
+
<b>Version 1.0.44</b> February 2022<br/>
|
|
6
|
+
- Boolean Logic Ultimate: Added the "d" indication, in the node's name, when the delay option is enabled.</br>
|
|
7
|
+
- Boolean Logic Ultimate: Refined the UI.</br>
|
|
8
|
+
</p>
|
|
9
|
+
<p>
|
|
10
|
+
<b>Version 1.0.41</b> January 2022<br/>
|
|
11
|
+
- Boolean Logic Ultimate: UI optimization and remove warning triangle if the delay is not set.</br>
|
|
12
|
+
</p>
|
|
13
|
+
<p>
|
|
14
|
+
<b>Version 1.0.40</b> January 2022<br/>
|
|
15
|
+
- NEW: Boolean Logic Ultimate: Delay option added. See the readme on gitHub.</br>
|
|
16
|
+
</p>
|
|
4
17
|
<p>
|
|
5
18
|
<b>Version 1.0.39</b> November 2021<br/>
|
|
6
19
|
- FIX a possible issue when msg.payload is numeric.</br>
|
package/README.md
CHANGED
|
@@ -15,9 +15,15 @@ A set of Node-RED enhanced boolean logic, with persisten values after reboot and
|
|
|
15
15
|
> Wellcome! First of all thank you for your interest in my nodes. This is a set of logic nodes, to overcome the simplicity of the default node-red boolean logic nodes.
|
|
16
16
|
Hope you enjoy that and if you're in trouble, please ask!
|
|
17
17
|
|
|
18
|
+
<br/>
|
|
19
|
+
<br/>
|
|
20
|
+
|
|
18
21
|
## CHANGELOG
|
|
19
22
|
* See <a href="https://github.com/Supergiovane/node-red-contrib-boolean-logic-ultimate/blob/master/CHANGELOG.md">here the changelog</a>
|
|
20
23
|
|
|
24
|
+
<br/>
|
|
25
|
+
<br/>
|
|
26
|
+
|
|
21
27
|
# BOOLEAN LOGIC
|
|
22
28
|
|
|
23
29
|
<img src='https://raw.githubusercontent.com/Supergiovane/node-red-contrib-boolean-logic-ultimate/master/img/bl1.png' width='60%'>
|
|
@@ -63,7 +69,7 @@ Set the number of different topics to be evaluated. The node will output a messa
|
|
|
63
69
|
*Remember: each input topic must be different. For example, if you set this field to 3, the node expects 3 different topics.*
|
|
64
70
|
|
|
65
71
|
|
|
66
|
-
**Filter output
|
|
72
|
+
**Filter output**
|
|
67
73
|
|
|
68
74
|
- 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.
|
|
69
75
|
- 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.
|
|
@@ -88,6 +94,12 @@ Every time you modify the node's config, <b>the retained values are cleared</b>.
|
|
|
88
94
|
|
|
89
95
|
If checked, the node will accept only boolean true/false values. Otherwise, it will try to convert the payload to a logic value true/false (including "on" and "off" values, sent, for example, from HomeAssistant).<br/>
|
|
90
96
|
|
|
97
|
+
**Delay evaluation (ms)**
|
|
98
|
+
|
|
99
|
+
Delays the evaluation until this time (in milliseconds) is elapsed. Each time a message or "topic trigger message" (see **Trigger mode**) arrives, the delay is restarted.<br/>
|
|
100
|
+
This option is useful for debouncing pourposes or simply for adding some delay.<br/>
|
|
101
|
+
For example, you can turn on a light if the room is occupied for a long time, allowing people to fast transit repeatedly, without the need of turning the light on.<br/>
|
|
102
|
+
Another example, if you have many sensors changing state rapidly, you can wait until these sensor reach a quiet state, then evaluate the inputs.<br/>
|
|
91
103
|
|
|
92
104
|
**INPUT MSG TO THE NODE**
|
|
93
105
|
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
return v !== undefined && v.length > 0;
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
|
-
restrictinputevaluation:{ value: true }
|
|
36
|
+
restrictinputevaluation: { value: true },
|
|
37
|
+
delayEvaluation: { value: 0 }
|
|
37
38
|
},
|
|
38
39
|
inputs: 1,
|
|
39
40
|
outputs: 3,
|
|
@@ -60,8 +61,14 @@
|
|
|
60
61
|
let label = "";
|
|
61
62
|
let filtered = this.filtertrue == "both" ? "" : "f";
|
|
62
63
|
let trigger = "";
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
let delayEvaluation = "";
|
|
65
|
+
if (this.delayEvaluation === 0 || this.delayEvaluation === "0" || this.delayEvaluation === "" || this.delayEvaluation === undefined) {
|
|
66
|
+
delayEvaluation = "";
|
|
67
|
+
} else {
|
|
68
|
+
delayEvaluation = "d(" + this.delayEvaluation + ")";
|
|
69
|
+
}
|
|
70
|
+
if (typeof this.outputtriggeredby !== "undefined") trigger = this.outputtriggeredby === "all" ? "" : "t(" + this.triggertopic + ")";
|
|
71
|
+
label = "Gate " + this.inputCount + filtered + delayEvaluation + trigger;
|
|
65
72
|
if (this.name !== undefined && this.name.length > 0) {
|
|
66
73
|
label += " (" + this.name + ")";
|
|
67
74
|
}
|
|
@@ -117,9 +124,9 @@
|
|
|
117
124
|
|
|
118
125
|
<div class="form-row">
|
|
119
126
|
<label for="node-input-inputCount"><i class="fa fa-step-forward"></i> Inputs count</label>
|
|
120
|
-
<input type="text" id="node-input-inputCount" placeholder="Inputs count, for example: 2">
|
|
127
|
+
<input style="width:100px" type="text" id="node-input-inputCount" placeholder="Inputs count, for example: 2">
|
|
121
128
|
</div>
|
|
122
|
-
<div class="form-tips" style="margin-top: 8px;background-color:
|
|
129
|
+
<div class="form-tips" style="margin-top: 8px;background-color:lightgreen;text-align:center">Inputs count: each incoming msg.topic represents one input.</div>
|
|
123
130
|
<br/>
|
|
124
131
|
<div class="form-row">
|
|
125
132
|
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
|
@@ -130,7 +137,7 @@
|
|
|
130
137
|
<input type="text" id="node-input-topic" placeholder="Node's own topic">
|
|
131
138
|
</div>
|
|
132
139
|
<div class="form-row">
|
|
133
|
-
<label for="node-input-filtertrue"><i class="fa fa-filter"></i> Filter output
|
|
140
|
+
<label for="node-input-filtertrue"><i class="fa fa-filter"></i> Filter output</label>
|
|
134
141
|
<select type="text" id="node-input-filtertrue" placeholder="Filter">
|
|
135
142
|
<option value="both">Output both 'true' and 'false' results</option>
|
|
136
143
|
<option value="onlytrue">Output only 'true' results</option>
|
|
@@ -148,8 +155,8 @@
|
|
|
148
155
|
<input type="text" id="node-input-triggertopic" placeholder="Input topic">
|
|
149
156
|
</div>
|
|
150
157
|
<div class="form-row">
|
|
151
|
-
<label for="node-input-sInitializeWith"><i class="fa fa-home"></i> If input states are undefined at boot</label>
|
|
152
|
-
<select type="text" id="node-input-sInitializeWith" placeholder="">
|
|
158
|
+
<label style="width:250px" for="node-input-sInitializeWith"><i class="fa fa-home"></i> If input states are undefined at boot</label>
|
|
159
|
+
<select style="width:170px" type="text" id="node-input-sInitializeWith" placeholder="">
|
|
153
160
|
<option value="WaitForPayload">Leave undefined</option>
|
|
154
161
|
<option value="false">Initialize all with False</option>
|
|
155
162
|
<option value="true">Initialize all with True</option>
|
|
@@ -165,7 +172,14 @@
|
|
|
165
172
|
<input type="checkbox" id="node-input-restrictinputevaluation" style="display:inline-block; width:auto; vertical-align:top;">
|
|
166
173
|
 <label style="width:auto" for="node-input-restrictinputevaluation"> Reject non boolean (true/false) input values</label>
|
|
167
174
|
</div>
|
|
168
|
-
|
|
175
|
+
<div class="form-row">
|
|
176
|
+
<label style="width:160px" for="node-input-delayEvaluation"><i class="fa fa-hourglass-o"></i> Delay evaluation (ms)</label>
|
|
177
|
+
<input style="width:150px" type="text" id="node-input-delayEvaluation" placeholder="Set 0 for no delay">
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
<br/>
|
|
181
|
+
<br/>
|
|
182
|
+
<br/>
|
|
169
183
|
</script>
|
|
170
184
|
|
|
171
185
|
<script type="text/x-red" data-help-name="BooleanLogicUltimate">
|
|
@@ -10,6 +10,11 @@ module.exports = function (RED) {
|
|
|
10
10
|
node.sInitializeWith = typeof node.config.sInitializeWith === "undefined" ? "WaitForPayload" : node.config.sInitializeWith;
|
|
11
11
|
node.persistPath = path.join(RED.settings.userDir, "booleanlogicultimatepersist"); // 26/10/2020 Contains the path for the states dir.
|
|
12
12
|
node.restrictinputevaluation = config.restrictinputevaluation === undefined ? false : config.restrictinputevaluation;
|
|
13
|
+
node.delayEvaluation = config.delayEvaluation === undefined ? 0 : config.delayEvaluation; // 26/01/2022 Starts evaluating the inputs only after this amount of time is elapsed, after the last msg input or trigger
|
|
14
|
+
if (isNaN(parseInt(node.delayEvaluation)) || parseInt(node.delayEvaluation) < 0) node.delayEvaluation = 0;
|
|
15
|
+
if (typeof node.delayEvaluation === "string") node.delayEvaluation = parseInt(node.delayEvaluation);
|
|
16
|
+
node.timerDelayEvaluation = null;
|
|
17
|
+
node.inputMessage = {}; // 26/01/2022 input message is stored here.
|
|
13
18
|
|
|
14
19
|
function setNodeStatus({ fill, shape, text }) {
|
|
15
20
|
var dDate = new Date();
|
|
@@ -63,6 +68,13 @@ module.exports = function (RED) {
|
|
|
63
68
|
setNodeStatus({ fill: "yellow", shape: "dot", text: "Waiting for input states" });
|
|
64
69
|
}
|
|
65
70
|
|
|
71
|
+
// Starts the evaluation delay timer, if needed
|
|
72
|
+
node.startTimerDelayEvaluation = () => {
|
|
73
|
+
if (node.timerDelayEvaluation !== null) clearTimeout(node.timerDelayEvaluation);
|
|
74
|
+
node.timerDelayEvaluation = setTimeout(() => {
|
|
75
|
+
outputResult();
|
|
76
|
+
}, node.delayEvaluation);
|
|
77
|
+
}
|
|
66
78
|
|
|
67
79
|
// 14/08/2019 If some inputs are to be initialized, create a dummy items in the array
|
|
68
80
|
initUndefinedInputs();
|
|
@@ -128,21 +140,21 @@ module.exports = function (RED) {
|
|
|
128
140
|
RED.log.error("BooleanLogicUltimate: unable to write to the filesystem. Check wether the user running node-red, has write permission to the filesysten. " + error.message);
|
|
129
141
|
}
|
|
130
142
|
}
|
|
143
|
+
node.inputMessage = msg; // 26/01/2022 Store MSG to be used in the outputResult function.
|
|
131
144
|
|
|
132
145
|
// Do we have as many inputs as we expect?
|
|
133
146
|
var keyCount = Object.keys(node.jSonStates).length;
|
|
134
|
-
|
|
135
147
|
if (keyCount == node.config.inputCount) {
|
|
136
148
|
|
|
137
|
-
var resAND = CalculateResult("AND");
|
|
138
|
-
var resOR = CalculateResult("OR");
|
|
139
|
-
var resXOR = CalculateResult("XOR");
|
|
149
|
+
// var resAND = CalculateResult("AND");
|
|
150
|
+
// var resOR = CalculateResult("OR");
|
|
151
|
+
// var resXOR = CalculateResult("XOR");
|
|
140
152
|
|
|
141
|
-
if (node.config.filtertrue == "onlytrue") {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
153
|
+
// if (node.config.filtertrue == "onlytrue") {
|
|
154
|
+
// if (!resAND) { resAND = null };
|
|
155
|
+
// if (!resOR) { resOR = null };
|
|
156
|
+
// if (!resXOR) { resXOR = null };
|
|
157
|
+
// }
|
|
146
158
|
|
|
147
159
|
// Operation mode evaluation
|
|
148
160
|
if (node.config.outputtriggeredby == "onlyonetopic") {
|
|
@@ -150,12 +162,22 @@ module.exports = function (RED) {
|
|
|
150
162
|
&& node.config.triggertopic !== ""
|
|
151
163
|
&& msg.hasOwnProperty("topic") && msg.topic !== ""
|
|
152
164
|
&& node.config.triggertopic === msg.topic) {
|
|
153
|
-
|
|
165
|
+
if (node.delayEvaluation > 0) {
|
|
166
|
+
node.startTimerDelayEvaluation();
|
|
167
|
+
setNodeStatus({ fill: "blue", shape: "ring", text: "Delay Eval " + node.delayEvaluation + "ms" });
|
|
168
|
+
} else {
|
|
169
|
+
outputResult();
|
|
170
|
+
}
|
|
154
171
|
} else {
|
|
155
172
|
setNodeStatus({ fill: "grey", shape: "ring", text: "Saved (" + (msg.hasOwnProperty("topic") ? msg.topic : "empty input topic") + ") " + value });
|
|
156
173
|
}
|
|
157
174
|
} else {
|
|
158
|
-
|
|
175
|
+
if (node.delayEvaluation > 0) {
|
|
176
|
+
node.startTimerDelayEvaluation();
|
|
177
|
+
setNodeStatus({ fill: "blue", shape: "ring", text: "Delay Eval " + node.delayEvaluation + "ms" });
|
|
178
|
+
} else {
|
|
179
|
+
outputResult();
|
|
180
|
+
}
|
|
159
181
|
}
|
|
160
182
|
}
|
|
161
183
|
else if (keyCount > node.config.inputCount) {
|
|
@@ -280,30 +302,41 @@ module.exports = function (RED) {
|
|
|
280
302
|
return res;
|
|
281
303
|
};
|
|
282
304
|
|
|
283
|
-
function
|
|
284
|
-
|
|
305
|
+
function outputResult() {
|
|
306
|
+
let optionalTopic = node.config.topic;
|
|
307
|
+
let calculatedValueAND = CalculateResult("AND");
|
|
308
|
+
let calculatedValueOR = CalculateResult("OR");
|
|
309
|
+
let calculatedValueXOR = CalculateResult("XOR");
|
|
310
|
+
|
|
311
|
+
if (node.config.filtertrue == "onlytrue") {
|
|
312
|
+
if (!calculatedValueAND) { calculatedValueAND = null };
|
|
313
|
+
if (!calculatedValueOR) { calculatedValueOR = null };
|
|
314
|
+
if (!calculatedValueXOR) { calculatedValueXOR = null };
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
setNodeStatus({ fill: "green", shape: "dot", text: "(AND)" + (calculatedValueAND !== null ? calculatedValueAND : "---") + " (OR)" + (calculatedValueOR !== null ? calculatedValueOR : "---") + " (XOR)" + (calculatedValueXOR !== null ? calculatedValueXOR : "---") });
|
|
285
318
|
|
|
286
319
|
var msgAND = null;
|
|
287
|
-
if (
|
|
288
|
-
msgAND = RED.util.cloneMessage(
|
|
320
|
+
if (calculatedValueAND != null) {
|
|
321
|
+
msgAND = RED.util.cloneMessage(node.inputMessage);
|
|
289
322
|
msgAND.topic = optionalTopic === undefined ? "result" : optionalTopic;
|
|
290
323
|
msgAND.operation = "AND";
|
|
291
|
-
msgAND.payload =
|
|
324
|
+
msgAND.payload = calculatedValueAND;
|
|
292
325
|
|
|
293
326
|
}
|
|
294
327
|
var msgOR = null;
|
|
295
|
-
if (
|
|
296
|
-
msgOR = RED.util.cloneMessage(
|
|
328
|
+
if (calculatedValueOR != null) {
|
|
329
|
+
msgOR = RED.util.cloneMessage(node.inputMessage);
|
|
297
330
|
msgOR.topic = optionalTopic === undefined ? "result" : optionalTopic;
|
|
298
331
|
msgOR.operation = "OR";
|
|
299
|
-
msgOR.payload =
|
|
332
|
+
msgOR.payload = calculatedValueOR;
|
|
300
333
|
}
|
|
301
334
|
var msgXOR = null;
|
|
302
|
-
if (
|
|
303
|
-
msgXOR = RED.util.cloneMessage(
|
|
335
|
+
if (calculatedValueXOR != null) {
|
|
336
|
+
msgXOR = RED.util.cloneMessage(node.inputMessage);
|
|
304
337
|
msgXOR.topic = optionalTopic === undefined ? "result" : optionalTopic;
|
|
305
338
|
msgXOR.operation = "XOR";
|
|
306
|
-
msgXOR.payload =
|
|
339
|
+
msgXOR.payload = calculatedValueXOR;
|
|
307
340
|
}
|
|
308
341
|
node.send([msgAND, msgOR, msgXOR]);
|
|
309
342
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-boolean-logic-ultimate",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.43",
|
|
4
4
|
"description": "A set of Node-RED enhanced boolean logic node, flow interruption node, blinker node, invert node, filter node, with persisten values after reboot and more.",
|
|
5
5
|
"author": "Supergiovane (https://github.com/Supergiovane)",
|
|
6
6
|
"dependencies": {
|