@bldgblocks/node-red-contrib-control 0.1.34 → 0.1.36
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/nodes/accumulate-block.html +18 -8
- package/nodes/accumulate-block.js +39 -44
- package/nodes/add-block.html +1 -1
- package/nodes/add-block.js +18 -11
- package/nodes/alarm-collector.html +260 -0
- package/nodes/alarm-collector.js +292 -0
- package/nodes/alarm-config.html +129 -0
- package/nodes/alarm-config.js +126 -0
- package/nodes/alarm-service.html +96 -0
- package/nodes/alarm-service.js +142 -0
- package/nodes/analog-switch-block.js +25 -36
- package/nodes/and-block.js +44 -15
- package/nodes/average-block.js +46 -41
- package/nodes/boolean-switch-block.js +10 -28
- package/nodes/boolean-to-number-block.html +18 -5
- package/nodes/boolean-to-number-block.js +24 -16
- package/nodes/cache-block.js +24 -37
- package/nodes/call-status-block.html +91 -32
- package/nodes/call-status-block.js +398 -115
- package/nodes/changeover-block.html +5 -0
- package/nodes/changeover-block.js +167 -162
- package/nodes/comment-block.html +1 -1
- package/nodes/comment-block.js +14 -9
- package/nodes/compare-block.html +14 -4
- package/nodes/compare-block.js +23 -18
- package/nodes/contextual-label-block.html +5 -0
- package/nodes/contextual-label-block.js +6 -16
- package/nodes/convert-block.html +25 -39
- package/nodes/convert-block.js +31 -16
- package/nodes/count-block.html +11 -5
- package/nodes/count-block.js +34 -32
- package/nodes/delay-block.js +58 -53
- package/nodes/divide-block.js +43 -45
- package/nodes/edge-block.html +17 -10
- package/nodes/edge-block.js +43 -41
- package/nodes/enum-switch-block.js +6 -6
- package/nodes/frequency-block.html +6 -1
- package/nodes/frequency-block.js +64 -74
- package/nodes/global-getter.html +51 -15
- package/nodes/global-getter.js +43 -13
- package/nodes/global-setter.html +1 -1
- package/nodes/global-setter.js +40 -12
- package/nodes/history-buffer.html +96 -0
- package/nodes/history-buffer.js +461 -0
- package/nodes/history-collector.html +29 -1
- package/nodes/history-collector.js +37 -16
- package/nodes/history-config.html +13 -1
- package/nodes/history-service.html +84 -0
- package/nodes/history-service.js +52 -0
- package/nodes/hysteresis-block.html +5 -0
- package/nodes/hysteresis-block.js +13 -16
- package/nodes/interpolate-block.html +20 -2
- package/nodes/interpolate-block.js +39 -50
- package/nodes/join.html +78 -0
- package/nodes/join.js +78 -0
- package/nodes/latch-block.js +12 -14
- package/nodes/load-sequence-block.js +102 -110
- package/nodes/max-block.js +26 -26
- package/nodes/memory-block.js +57 -58
- package/nodes/min-block.js +26 -25
- package/nodes/minmax-block.js +35 -34
- package/nodes/modulo-block.js +45 -43
- package/nodes/multiply-block.js +43 -41
- package/nodes/negate-block.html +17 -7
- package/nodes/negate-block.js +25 -19
- package/nodes/network-point-read.html +128 -0
- package/nodes/network-point-read.js +230 -0
- package/nodes/{network-register.html → network-point-register.html} +94 -7
- package/nodes/{network-register.js → network-point-register.js} +18 -4
- package/nodes/network-point-write.html +149 -0
- package/nodes/network-point-write.js +222 -0
- package/nodes/network-service-bridge.html +131 -0
- package/nodes/network-service-bridge.js +376 -0
- package/nodes/network-service-read.html +81 -0
- package/nodes/{network-read.js → network-service-read.js} +4 -3
- package/nodes/{network-point-registry.html → network-service-registry.html} +19 -4
- package/nodes/{network-point-registry.js → network-service-registry.js} +7 -2
- package/nodes/network-service-write.html +89 -0
- package/nodes/{network-write.js → network-service-write.js} +3 -3
- package/nodes/nullify-block.js +13 -15
- package/nodes/on-change-block.html +17 -9
- package/nodes/on-change-block.js +49 -46
- package/nodes/oneshot-block.html +13 -10
- package/nodes/oneshot-block.js +57 -75
- package/nodes/or-block.js +44 -15
- package/nodes/pid-block.html +54 -4
- package/nodes/pid-block.js +459 -248
- package/nodes/priority-block.js +24 -35
- package/nodes/rate-limit-block.js +70 -72
- package/nodes/rate-of-change-block.html +33 -14
- package/nodes/rate-of-change-block.js +74 -62
- package/nodes/round-block.html +14 -9
- package/nodes/round-block.js +32 -25
- package/nodes/saw-tooth-wave-block.js +49 -76
- package/nodes/scale-range-block.html +12 -6
- package/nodes/scale-range-block.js +46 -39
- package/nodes/sine-wave-block.js +49 -57
- package/nodes/string-builder-block.js +6 -6
- package/nodes/subtract-block.js +38 -34
- package/nodes/thermistor-block.js +44 -44
- package/nodes/tick-tock-block.js +32 -32
- package/nodes/time-sequence-block.js +30 -42
- package/nodes/triangle-wave-block.js +49 -69
- package/nodes/tstat-block.js +34 -44
- package/nodes/units-block.html +90 -69
- package/nodes/units-block.js +22 -30
- package/nodes/utils.js +206 -3
- package/package.json +14 -6
- package/nodes/network-read.html +0 -56
- package/nodes/network-write.html +0 -65
package/nodes/comment-block.js
CHANGED
|
@@ -1,44 +1,49 @@
|
|
|
1
1
|
module.exports = function(RED) {
|
|
2
|
+
const utils = require('./utils')(RED);
|
|
3
|
+
|
|
2
4
|
function CommentBlockNode(config) {
|
|
3
5
|
RED.nodes.createNode(this, config);
|
|
4
6
|
const node = this;
|
|
5
7
|
|
|
6
8
|
// Initialize properties
|
|
7
9
|
node.name = config.name;
|
|
8
|
-
node.comment = config.comment;
|
|
10
|
+
node.comment = config.comment || "";
|
|
9
11
|
node.statusDisplay = config.statusDisplay;
|
|
10
12
|
|
|
11
13
|
// Ensure comment is within 100 characters
|
|
12
|
-
if (node.comment.length > 100) {
|
|
14
|
+
if (node.comment && node.comment.length > 100) {
|
|
13
15
|
node.comment = node.comment.substring(0, 100);
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
//
|
|
18
|
+
// Update status based on configuration
|
|
17
19
|
const updateStatus = function() {
|
|
18
20
|
switch (node.statusDisplay) {
|
|
19
21
|
case "default":
|
|
20
|
-
|
|
22
|
+
utils.setStatusOK(node, node.comment || "No comment set");
|
|
23
|
+
break;
|
|
21
24
|
case "name":
|
|
22
|
-
|
|
25
|
+
utils.setStatusOK(node, node.name || "comment");
|
|
26
|
+
break;
|
|
23
27
|
case "none":
|
|
24
28
|
default:
|
|
25
|
-
|
|
29
|
+
// No status for "none"
|
|
30
|
+
break;
|
|
26
31
|
}
|
|
27
32
|
};
|
|
28
33
|
|
|
29
|
-
|
|
34
|
+
updateStatus();
|
|
30
35
|
|
|
31
36
|
node.on("input", function(msg, send, done) {
|
|
32
37
|
send = send || function() { node.send.apply(node, arguments); };
|
|
33
38
|
|
|
34
39
|
// Guard against invalid msg
|
|
35
40
|
if (!msg) {
|
|
36
|
-
|
|
41
|
+
utils.setStatusError(node, "invalid message");
|
|
37
42
|
if (done) done();
|
|
38
43
|
return;
|
|
39
44
|
}
|
|
40
45
|
|
|
41
|
-
|
|
46
|
+
updateStatus();
|
|
42
47
|
|
|
43
48
|
send(msg);
|
|
44
49
|
if (done) done();
|
package/nodes/compare-block.html
CHANGED
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
<label for="node-input-name" title="Display name shown on the canvas"><i class="fa fa-tag"></i> Name</label>
|
|
5
5
|
<input type="text" id="node-input-name" placeholder="Name">
|
|
6
6
|
</div>
|
|
7
|
-
<div class="form-row">
|
|
7
|
+
<div class="form-row">
|
|
8
|
+
<label for="node-input-inputProperty" title="Message property to read input from"><i class="fa fa-folder-open"></i> Input Property</label>
|
|
9
|
+
<input type="text" id="node-input-inputProperty" placeholder="payload">
|
|
10
|
+
</div>
|
|
11
|
+
<div class="form-row">
|
|
8
12
|
<label for="node-input-setpoint" title="Default setpoint value for comparison (number)"><i class="fa fa-crosshairs"></i> Setpoint</label>
|
|
9
13
|
<input type="number" id="node-input-setpoint" placeholder="50" step="any">
|
|
10
14
|
</div>
|
|
@@ -17,6 +21,7 @@
|
|
|
17
21
|
color: "#301934",
|
|
18
22
|
defaults: {
|
|
19
23
|
name: { value: "" },
|
|
24
|
+
inputProperty: { value: "payload" },
|
|
20
25
|
setpoint: { value: 50, required: true, validate: function(v) { return !isNaN(+v) && isFinite(+v); } }
|
|
21
26
|
},
|
|
22
27
|
inputs: 1,
|
|
@@ -36,21 +41,26 @@
|
|
|
36
41
|
Compares a numeric input to a setpoint, outputting three boolean signals.
|
|
37
42
|
|
|
38
43
|
### Inputs
|
|
44
|
+
: input-property (number) : Input number to compare, read from the configured Input Property.
|
|
39
45
|
: context (string) : Configuration commands - setpoint (`"setpoint"`). Unmatched values trigger error.
|
|
40
|
-
: payload (number) : Input number to compare, or setpoint value for config.
|
|
41
|
-
|
|
42
46
|
### Outputs
|
|
43
47
|
: greater than (boolean) : `true` if input > setpoint, else `false`.
|
|
44
48
|
: equal to (boolean) : `true` if input = setpoint, else `false`.
|
|
45
49
|
: less than (boolean) : `true` if input < setpoint, else `false`.
|
|
46
50
|
|
|
47
51
|
### Properties
|
|
52
|
+
: name (string) : Display name in editor.
|
|
53
|
+
: inputProperty (string) : Message property to read input from (default: `payload`). Supports nested properties (e.g., `data.value`).
|
|
48
54
|
: setpoint (number) : Value for comparison.
|
|
49
55
|
|
|
50
56
|
### Details
|
|
51
|
-
Compares `msg.payload`
|
|
57
|
+
Compares a numeric input (read from the configured **Input Property**, default: `msg.payload`) to a `setpoint` (default: 50), outputting three messages to separate ports:
|
|
52
58
|
`true` for greater than, equal to, or less than the setpoint, respectively; `false` otherwise.
|
|
53
59
|
|
|
60
|
+
Output is always written to `msg.payload` in each output message.
|
|
61
|
+
|
|
62
|
+
Setpoint configurable via editor or `msg.context = "setpoint"` with numeric `msg.payload`.
|
|
63
|
+
|
|
54
64
|
### Status
|
|
55
65
|
- Green (dot): Configuration update
|
|
56
66
|
- Blue (dot): State changed
|
package/nodes/compare-block.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
module.exports = function(RED) {
|
|
2
|
+
const utils = require('./utils')(RED);
|
|
3
|
+
|
|
2
4
|
function CompareBlockNode(config) {
|
|
3
5
|
RED.nodes.createNode(this, config);
|
|
4
6
|
const node = this;
|
|
5
7
|
|
|
8
|
+
node.inputProperty = config.inputProperty || "payload";
|
|
6
9
|
node.setpoint = Number(config.setpoint);
|
|
7
10
|
|
|
8
11
|
node.on("input", function(msg, send, done) {
|
|
@@ -10,7 +13,7 @@ module.exports = function(RED) {
|
|
|
10
13
|
|
|
11
14
|
// Guard against invalid message
|
|
12
15
|
if (!msg) {
|
|
13
|
-
|
|
16
|
+
utils.setStatusError(node, "invalid message");
|
|
14
17
|
if (done) done();
|
|
15
18
|
return;
|
|
16
19
|
}
|
|
@@ -18,38 +21,44 @@ module.exports = function(RED) {
|
|
|
18
21
|
// Handle context updates
|
|
19
22
|
if (msg.hasOwnProperty("context")) {
|
|
20
23
|
if (!msg.hasOwnProperty("payload")) {
|
|
21
|
-
|
|
24
|
+
utils.setStatusError(node, "missing payload for setpoint");
|
|
22
25
|
if (done) done();
|
|
23
26
|
return;
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
if (msg.context === "setpoint") {
|
|
27
|
-
const
|
|
28
|
-
if (
|
|
29
|
-
node.setpoint =
|
|
30
|
-
|
|
30
|
+
const numVal = utils.validateNumericPayload(msg.payload);
|
|
31
|
+
if (numVal.valid) {
|
|
32
|
+
node.setpoint = numVal.value;
|
|
33
|
+
utils.setStatusOK(node, `setpoint: ${numVal.value.toFixed(2)}`);
|
|
31
34
|
} else {
|
|
32
|
-
|
|
35
|
+
utils.setStatusError(node, "invalid setpoint");
|
|
33
36
|
}
|
|
34
37
|
if (done) done();
|
|
35
38
|
return;
|
|
36
39
|
} else {
|
|
37
|
-
|
|
40
|
+
utils.setStatusWarn(node, "unknown context");
|
|
38
41
|
if (done) done("Unknown context");
|
|
39
42
|
return;
|
|
40
43
|
}
|
|
41
44
|
}
|
|
42
45
|
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
// Get input from configured property
|
|
47
|
+
let input;
|
|
48
|
+
try {
|
|
49
|
+
input = RED.util.getMessageProperty(msg, node.inputProperty);
|
|
50
|
+
} catch (err) {
|
|
51
|
+
input = undefined;
|
|
52
|
+
}
|
|
53
|
+
if (input === undefined) {
|
|
54
|
+
utils.setStatusError(node, "missing or invalid input property");
|
|
46
55
|
if (done) done();
|
|
47
56
|
return;
|
|
48
57
|
}
|
|
49
58
|
|
|
50
|
-
const inputValue = parseFloat(
|
|
59
|
+
const inputValue = parseFloat(input);
|
|
51
60
|
if (isNaN(inputValue) || !isFinite(inputValue)) {
|
|
52
|
-
|
|
61
|
+
utils.setStatusError(node, "invalid input");
|
|
53
62
|
if (done) done();
|
|
54
63
|
return;
|
|
55
64
|
}
|
|
@@ -64,11 +73,7 @@ module.exports = function(RED) {
|
|
|
64
73
|
{ payload: less }
|
|
65
74
|
];
|
|
66
75
|
|
|
67
|
-
node.
|
|
68
|
-
fill: "blue",
|
|
69
|
-
shape: "dot",
|
|
70
|
-
text: `in: ${inputValue.toFixed(2)}, sp: ${node.setpoint.toFixed(2)}, out: [${greater}, ${equal}, ${less}]`
|
|
71
|
-
});
|
|
76
|
+
utils.setStatusOK(node, `in: ${inputValue.toFixed(2)}, sp: ${node.setpoint.toFixed(2)}, out: [${greater}, ${equal}, ${less}]`);
|
|
72
77
|
|
|
73
78
|
send(outputs);
|
|
74
79
|
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
<label for="node-input-name" title="Display name shown on the canvas"><i class="fa fa-tag"></i> Name</label>
|
|
4
4
|
<input type="text" id="node-input-name" placeholder="Name">
|
|
5
5
|
</div>
|
|
6
|
+
<div class="form-row">
|
|
7
|
+
<label for="node-input-inputProperty" title="Message property to read label from"><i class="fa fa-folder-open"></i> Input Property</label>
|
|
8
|
+
<input type="text" id="node-input-inputProperty" placeholder="payload">
|
|
9
|
+
</div>
|
|
6
10
|
<div class="form-row">
|
|
7
11
|
<label for="node-input-contextPropertyName" title="Context property to set (e.g., priority, clear, in1)"><i class="fa fa-key"></i> Context Property</label>
|
|
8
12
|
<input type="text" id="node-input-contextPropertyName" placeholder="e.g., priority">
|
|
@@ -19,6 +23,7 @@ RED.nodes.registerType("contextual-label-block", {
|
|
|
19
23
|
color: "#301934",
|
|
20
24
|
defaults: {
|
|
21
25
|
name: { value: "" },
|
|
26
|
+
inputProperty: { value: "payload" },
|
|
22
27
|
contextPropertyName: {
|
|
23
28
|
value: "in1",
|
|
24
29
|
required: true,
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
module.exports = function(RED) {
|
|
2
|
+
const utils = require('./utils')(RED);
|
|
3
|
+
|
|
2
4
|
function ContextualLabelBlockNode(config) {
|
|
3
5
|
RED.nodes.createNode(this, config);
|
|
4
6
|
const node = this;
|
|
@@ -6,17 +8,13 @@ module.exports = function(RED) {
|
|
|
6
8
|
node.contextPropertyName = config.contextPropertyName || "in1";
|
|
7
9
|
node.removeLabel = config.removeLabel || false;
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
fill: "green",
|
|
11
|
-
shape: "dot",
|
|
12
|
-
text: `mode: ${node.removeLabel ? "remove" : "set"}, property: ${node.contextPropertyName}`
|
|
13
|
-
});
|
|
11
|
+
utils.setStatusOK(node, `mode: ${node.removeLabel ? "remove" : "set"}, property: ${node.contextPropertyName}`);
|
|
14
12
|
|
|
15
13
|
node.on("input", function(msg, send, done) {
|
|
16
14
|
send = send || function() { node.send.apply(node, arguments); };
|
|
17
15
|
|
|
18
16
|
if (!msg) {
|
|
19
|
-
|
|
17
|
+
utils.setStatusError(node, "missing message");
|
|
20
18
|
node.warn("Missing message");
|
|
21
19
|
if (done) done();
|
|
22
20
|
return;
|
|
@@ -25,18 +23,10 @@ module.exports = function(RED) {
|
|
|
25
23
|
// Set or remove context property
|
|
26
24
|
if (node.removeLabel) {
|
|
27
25
|
delete msg.context;
|
|
28
|
-
|
|
29
|
-
fill: "blue",
|
|
30
|
-
shape: "dot",
|
|
31
|
-
text: `in: ${msg.payload}, out: removed context`
|
|
32
|
-
});
|
|
26
|
+
utils.setStatusChanged(node, `in: ${msg.payload}, out: removed context`);
|
|
33
27
|
} else {
|
|
34
28
|
msg.context = node.contextPropertyName;
|
|
35
|
-
|
|
36
|
-
fill: "blue",
|
|
37
|
-
shape: "dot",
|
|
38
|
-
text: `in: ${msg.payload}, out: ${node.contextPropertyName}`
|
|
39
|
-
});
|
|
29
|
+
utils.setStatusChanged(node, `in: ${msg.payload}, out: ${node.contextPropertyName}`);
|
|
40
30
|
}
|
|
41
31
|
|
|
42
32
|
send(msg);
|
package/nodes/convert-block.html
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
<label for="node-input-name" title="Display name shown on the canvas"><i class="fa fa-tag"></i> Name</label>
|
|
4
4
|
<input type="text" id="node-input-name" placeholder="Name">
|
|
5
5
|
</div>
|
|
6
|
+
<div class="form-row">
|
|
7
|
+
<label for="node-input-inputProperty" title="Message property to read input from"><i class="fa fa-folder-open"></i> Input Property</label>
|
|
8
|
+
<input type="text" id="node-input-inputProperty" placeholder="payload">
|
|
9
|
+
</div>
|
|
6
10
|
<div class="form-row">
|
|
7
11
|
<label for="node-input-conversion" title="Select the unit conversion to perform"><i class="fa fa-exchange"></i> Conversion</label>
|
|
8
12
|
<select id="node-input-conversion">
|
|
@@ -62,6 +66,7 @@
|
|
|
62
66
|
color: "#301934",
|
|
63
67
|
defaults: {
|
|
64
68
|
name: { value: "" },
|
|
69
|
+
inputProperty: { value: "payload" },
|
|
65
70
|
conversion: { value: "C to F", required: true }
|
|
66
71
|
},
|
|
67
72
|
inputs: 1,
|
|
@@ -117,54 +122,35 @@
|
|
|
117
122
|
</script>
|
|
118
123
|
|
|
119
124
|
<script type="text/markdown" data-help-name="convert-block">
|
|
120
|
-
Converts numeric input
|
|
125
|
+
Converts numeric input from a configured property between various units.
|
|
121
126
|
|
|
122
127
|
### Inputs
|
|
128
|
+
: input-property (number) : Numeric value to convert, read from the configured Input Property.
|
|
123
129
|
: context (string) : Configures the conversion (`"conversion"`). Unmatched values ignored.
|
|
124
|
-
: payload (number) : Numeric value to convert
|
|
125
130
|
|
|
126
131
|
### Outputs
|
|
127
132
|
: payload (number) : Converted value (e.g., °C to °F, Pa to inH₂O).
|
|
128
133
|
|
|
134
|
+
### Properties
|
|
135
|
+
: name (string) : Display name in editor.
|
|
136
|
+
: inputProperty (string) : Message property to read input from (default: `payload`). Supports nested properties (e.g., `data.value`).
|
|
137
|
+
: conversion (string) : Conversion type to apply (e.g., "F to C", "Pa to inH₂O").
|
|
138
|
+
|
|
129
139
|
### Details
|
|
130
|
-
Converts `msg.payload` between units based on the selected conversion.
|
|
140
|
+
Converts numeric input (read from the configured **Input Property**, default: `msg.payload`) between units based on the selected conversion.
|
|
131
141
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
"inH₂O to Pa",
|
|
145
|
-
"Pa to inHg",
|
|
146
|
-
"inHg to Pa",
|
|
147
|
-
"Pa to bar",
|
|
148
|
-
"bar to Pa",
|
|
149
|
-
"Pa to psi",
|
|
150
|
-
"psi to Pa",
|
|
151
|
-
"m to ft",
|
|
152
|
-
"ft to m",
|
|
153
|
-
"m to in",
|
|
154
|
-
"in to m",
|
|
155
|
-
"mm to in",
|
|
156
|
-
"in to mm",
|
|
157
|
-
"kg to lb",
|
|
158
|
-
"lb to kg",
|
|
159
|
-
"L to gal",
|
|
160
|
-
"gal to L",
|
|
161
|
-
"kW to hp",
|
|
162
|
-
"hp to kW",
|
|
163
|
-
"rad to deg",
|
|
164
|
-
"deg to rad",
|
|
165
|
-
"s to min",
|
|
166
|
-
"min to s"
|
|
167
|
-
];
|
|
142
|
+
Supported conversions include:
|
|
143
|
+
- Temperature: F to C, C to F, K to C, C to K, K to F, F to K, R to F, F to R
|
|
144
|
+
- Percentage: decimal to %, % to decimal
|
|
145
|
+
- Pressure: Pa to inH₂O, inH₂O to Pa, Pa to inHg, inHg to Pa, Pa to bar, bar to Pa, Pa to psi, psi to Pa
|
|
146
|
+
- Distance: m to ft, ft to m, m to in, in to m, mm to in, in to mm
|
|
147
|
+
- Weight: kg to lb, lb to kg
|
|
148
|
+
- Volume: L to gal, gal to L
|
|
149
|
+
- Power: kW to hp, hp to kW
|
|
150
|
+
- Angle: rad to deg, deg to rad
|
|
151
|
+
- Time: s to min, min to s
|
|
152
|
+
|
|
153
|
+
Output is always written to `msg.payload`.
|
|
168
154
|
|
|
169
155
|
### Status
|
|
170
156
|
- Green (dot): Configuration update
|
package/nodes/convert-block.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
module.exports = function(RED) {
|
|
2
|
+
const utils = require('./utils')(RED);
|
|
3
|
+
|
|
2
4
|
function ConvertBlockNode(config) {
|
|
3
5
|
RED.nodes.createNode(this, config);
|
|
4
6
|
const node = this;
|
|
@@ -41,16 +43,16 @@ module.exports = function(RED) {
|
|
|
41
43
|
];
|
|
42
44
|
|
|
43
45
|
// Initialize runtime state
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
// Initialize state
|
|
47
|
+
node.inputProperty = config.inputProperty || "payload";
|
|
48
|
+
node.conversion = validConversions.includes(config.conversion) ? config.conversion : "C to F";
|
|
47
49
|
|
|
48
50
|
node.on("input", function(msg, send, done) {
|
|
49
51
|
send = send || function() { node.send.apply(node, arguments); };
|
|
50
52
|
|
|
51
53
|
// Guard against invalid message
|
|
52
54
|
if (!msg) {
|
|
53
|
-
|
|
55
|
+
utils.setStatusError(node, "missing message");
|
|
54
56
|
if (done) done();
|
|
55
57
|
return;
|
|
56
58
|
}
|
|
@@ -58,18 +60,18 @@ module.exports = function(RED) {
|
|
|
58
60
|
// Handle configuration messages
|
|
59
61
|
if (msg.hasOwnProperty("context")) {
|
|
60
62
|
if (typeof msg.context !== "string") {
|
|
61
|
-
|
|
63
|
+
utils.setStatusWarn(node, "unknown context");
|
|
62
64
|
if (done) done();
|
|
63
65
|
return;
|
|
64
66
|
}
|
|
65
67
|
if (msg.context === "conversion") {
|
|
66
68
|
if (!msg.hasOwnProperty("payload") || !validConversions.includes(msg.payload)) {
|
|
67
|
-
|
|
69
|
+
utils.setStatusError(node, "invalid conversion");
|
|
68
70
|
if (done) done();
|
|
69
71
|
return;
|
|
70
72
|
}
|
|
71
|
-
node.
|
|
72
|
-
|
|
73
|
+
node.conversion = msg.payload;
|
|
74
|
+
utils.setStatusOK(node, `conversion: ${node.conversion}`);
|
|
73
75
|
if (done) done();
|
|
74
76
|
return;
|
|
75
77
|
}
|
|
@@ -78,23 +80,29 @@ module.exports = function(RED) {
|
|
|
78
80
|
return;
|
|
79
81
|
}
|
|
80
82
|
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
// Get input from configured property
|
|
84
|
+
let input;
|
|
85
|
+
try {
|
|
86
|
+
input = RED.util.getMessageProperty(msg, node.inputProperty);
|
|
87
|
+
} catch (err) {
|
|
88
|
+
input = undefined;
|
|
89
|
+
}
|
|
90
|
+
if (input === undefined) {
|
|
91
|
+
utils.setStatusError(node, "missing or invalid input property");
|
|
84
92
|
if (done) done();
|
|
85
93
|
return;
|
|
86
94
|
}
|
|
87
95
|
|
|
88
|
-
const inputValue = parseFloat(
|
|
96
|
+
const inputValue = parseFloat(input);
|
|
89
97
|
if (isNaN(inputValue) || !isFinite(inputValue)) {
|
|
90
|
-
|
|
98
|
+
utils.setStatusError(node, "invalid input");
|
|
91
99
|
if (done) done();
|
|
92
100
|
return;
|
|
93
101
|
}
|
|
94
102
|
|
|
95
103
|
// Perform conversion
|
|
96
104
|
let output, inUnit, outUnit;
|
|
97
|
-
switch (node.
|
|
105
|
+
switch (node.conversion) {
|
|
98
106
|
case "F to C":
|
|
99
107
|
output = (inputValue - 32) * 5 / 9;
|
|
100
108
|
inUnit = "°F";
|
|
@@ -268,11 +276,18 @@ module.exports = function(RED) {
|
|
|
268
276
|
}
|
|
269
277
|
|
|
270
278
|
// Format status numbers
|
|
271
|
-
|
|
279
|
+
let num = Number(msg.payload);
|
|
280
|
+
let inDisplay = 0
|
|
281
|
+
if (isNaN(num)) {
|
|
282
|
+
inDisplay = 0;
|
|
283
|
+
} else {
|
|
284
|
+
inDisplay = num % 1 === 0 ? num : num.toFixed(2);
|
|
285
|
+
msg.payload = inDisplay;
|
|
286
|
+
}
|
|
272
287
|
const outDisplay = output % 1 === 0 ? output : output.toFixed(2);
|
|
273
288
|
|
|
274
289
|
// Update status and send output
|
|
275
|
-
|
|
290
|
+
utils.setStatusOK(node, `${inDisplay} ${inUnit} → ${outDisplay} ${outUnit}`);
|
|
276
291
|
|
|
277
292
|
msg.payload = output;
|
|
278
293
|
send(msg);
|
package/nodes/count-block.html
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
<label for="node-input-name" title="Display name shown on the canvas"><i class="fa fa-tag"></i> Name</label>
|
|
5
5
|
<input type="text" id="node-input-name" placeholder="Name">
|
|
6
6
|
</div>
|
|
7
|
+
<div class="form-row">
|
|
8
|
+
<label for="node-input-inputProperty" title="Message property to read input from"><i class="fa fa-folder-open"></i> Input Property</label>
|
|
9
|
+
<input type="text" id="node-input-inputProperty" placeholder="payload">
|
|
10
|
+
</div>
|
|
7
11
|
</script>
|
|
8
12
|
|
|
9
13
|
<!-- JavaScript Section: Registers the node and handles editor logic -->
|
|
@@ -12,7 +16,8 @@
|
|
|
12
16
|
category: "bldgblocks control",
|
|
13
17
|
color: "#301934",
|
|
14
18
|
defaults: {
|
|
15
|
-
name: { value: "" }
|
|
19
|
+
name: { value: "" },
|
|
20
|
+
inputProperty: { value: "payload" }
|
|
16
21
|
},
|
|
17
22
|
inputs: 1,
|
|
18
23
|
outputs: 1,
|
|
@@ -28,21 +33,22 @@
|
|
|
28
33
|
|
|
29
34
|
<!-- Help Section -->
|
|
30
35
|
<script type="text/markdown" data-help-name="count-block">
|
|
31
|
-
Counts boolean rising edges
|
|
36
|
+
Counts boolean rising edges from a configured property.
|
|
32
37
|
|
|
33
38
|
### Inputs
|
|
39
|
+
: input-property (boolean) : Boolean value to detect rising edges, read from the configured Input Property.
|
|
34
40
|
: context (string) : Resets count (`"reset"`). Unmatched values trigger error.
|
|
35
|
-
: payload (boolean) : Input boolean to detect rising edges.
|
|
36
41
|
|
|
37
42
|
### Outputs
|
|
38
43
|
: payload (number) : Current count of rising edges.
|
|
39
44
|
|
|
40
45
|
### Properties
|
|
41
46
|
: name (string) : Display name in editor.
|
|
47
|
+
: inputProperty (string) : Message property to read input from (default: `payload`). Supports nested properties (e.g., `data.value`).
|
|
42
48
|
|
|
43
49
|
### Details
|
|
44
|
-
Counts rising edges (false-to-true transitions) in `msg.payload`
|
|
45
|
-
Resets count to 0 via `msg.context = "reset"` with `msg.payload = true`.
|
|
50
|
+
Counts rising edges (false-to-true transitions) in the configured **Input Property** (default: `msg.payload`), incrementing the count on each transition.
|
|
51
|
+
Resets count to 0 via `msg.context = "reset"` with `msg.payload = true`.
|
|
46
52
|
|
|
47
53
|
### Status
|
|
48
54
|
- Green (dot): Configuration update
|
package/nodes/count-block.js
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
module.exports = function(RED) {
|
|
2
|
+
const utils = require('./utils')(RED);
|
|
3
|
+
|
|
2
4
|
function CountBlockNode(config) {
|
|
3
5
|
RED.nodes.createNode(this, config);
|
|
4
6
|
const node = this;
|
|
5
7
|
|
|
6
8
|
// Initialize runtime state
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
// Initialize state
|
|
10
|
+
node.name = config.name;
|
|
11
|
+
node.inputProperty = config.inputProperty || "payload";
|
|
12
|
+
node.count = 0;
|
|
13
|
+
node.prevState = false;
|
|
12
14
|
|
|
13
15
|
node.on("input", function(msg, send, done) {
|
|
14
16
|
send = send || function() { node.send.apply(node, arguments); };
|
|
15
17
|
|
|
16
18
|
// Guard against invalid message
|
|
17
19
|
if (!msg) {
|
|
18
|
-
|
|
20
|
+
utils.setStatusError(node, "invalid message");
|
|
19
21
|
if (done) done();
|
|
20
22
|
return;
|
|
21
23
|
}
|
|
@@ -23,62 +25,62 @@ module.exports = function(RED) {
|
|
|
23
25
|
// Handle context updates
|
|
24
26
|
if (msg.hasOwnProperty("context")) {
|
|
25
27
|
if (!msg.hasOwnProperty("payload")) {
|
|
26
|
-
|
|
28
|
+
utils.setStatusError(node, "missing payload for reset");
|
|
27
29
|
if (done) done();
|
|
28
30
|
return;
|
|
29
31
|
}
|
|
30
32
|
if (msg.context === "reset") {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
const boolVal = utils.validateBoolean(msg.payload);
|
|
34
|
+
if (!boolVal.valid) {
|
|
35
|
+
utils.setStatusError(node, boolVal.error);
|
|
33
36
|
if (done) done();
|
|
34
37
|
return;
|
|
35
38
|
}
|
|
36
|
-
if (
|
|
37
|
-
node.
|
|
38
|
-
node.
|
|
39
|
-
|
|
40
|
-
send({ payload: node.
|
|
39
|
+
if (boolVal.value === true) {
|
|
40
|
+
node.count = 0;
|
|
41
|
+
node.prevState = false;
|
|
42
|
+
utils.setStatusOK(node, "state reset");
|
|
43
|
+
send({ payload: node.count });
|
|
41
44
|
}
|
|
42
45
|
if (done) done();
|
|
43
46
|
return;
|
|
44
47
|
} else {
|
|
45
|
-
|
|
48
|
+
utils.setStatusWarn(node, "unknown context");
|
|
46
49
|
if (done) done("Unknown context");
|
|
47
50
|
return;
|
|
48
51
|
}
|
|
49
52
|
}
|
|
50
53
|
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
// Get input value from configured property
|
|
55
|
+
let inputValue;
|
|
56
|
+
try {
|
|
57
|
+
inputValue = RED.util.getMessageProperty(msg, node.inputProperty);
|
|
58
|
+
} catch (err) {
|
|
59
|
+
inputValue = undefined;
|
|
56
60
|
}
|
|
57
|
-
|
|
58
|
-
const inputValue = msg.payload;
|
|
59
61
|
if (typeof inputValue !== "boolean") {
|
|
60
|
-
|
|
62
|
+
utils.setStatusError(node, "missing or invalid input property");
|
|
61
63
|
if (done) done();
|
|
62
64
|
return;
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
// Prevent integer overflow
|
|
66
|
-
if (node.
|
|
67
|
-
node.
|
|
68
|
-
|
|
68
|
+
if (node.count > Number.MAX_SAFE_INTEGER - 100000) {
|
|
69
|
+
node.count = 0;
|
|
70
|
+
utils.setStatusWarn(node, "count overflow reset");
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
// Increment on false → true transition
|
|
72
|
-
if (!node.
|
|
73
|
-
node.
|
|
74
|
-
|
|
75
|
-
send({ payload: node.
|
|
74
|
+
if (!node.prevState && inputValue === true) {
|
|
75
|
+
node.count++;
|
|
76
|
+
utils.setStatusChanged(node, `count: ${node.count}`);
|
|
77
|
+
send({ payload: node.count });
|
|
76
78
|
} else {
|
|
77
|
-
|
|
79
|
+
utils.setStatusUnchanged(node, `count: ${node.count}`);
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
// Update prevState
|
|
81
|
-
node.
|
|
83
|
+
node.prevState = inputValue;
|
|
82
84
|
|
|
83
85
|
if (done) done();
|
|
84
86
|
});
|