@bldgblocks/node-red-contrib-control 0.1.34 → 0.1.37
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 +464 -0
- package/nodes/history-collector.html +29 -1
- package/nodes/history-collector.js +46 -16
- package/nodes/history-config.html +13 -1
- package/nodes/history-service.html +84 -0
- package/nodes/history-service.js +66 -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
|
@@ -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
|
<div class="form-row">
|
|
8
12
|
<label for="node-input-nullToZero"><i class="fa fa-cog"></i> Null Mapping</label>
|
|
9
13
|
<input type="checkbox" id="node-input-nullToZero" style="width: auto; margin-left: 10px;"> Map null to 0
|
|
@@ -17,6 +21,7 @@
|
|
|
17
21
|
color: "#301934",
|
|
18
22
|
defaults: {
|
|
19
23
|
name: { value: "" },
|
|
24
|
+
inputProperty: { value: "payload" },
|
|
20
25
|
nullToZero: { value: false }
|
|
21
26
|
},
|
|
22
27
|
inputs: 1,
|
|
@@ -33,18 +38,26 @@
|
|
|
33
38
|
|
|
34
39
|
<!-- Help Section -->
|
|
35
40
|
<script type="text/markdown" data-help-name="boolean-to-number-block">
|
|
36
|
-
Converts a boolean or null input
|
|
41
|
+
Converts a boolean or null input from a configured property to a numeric output.
|
|
37
42
|
|
|
38
43
|
### Inputs
|
|
39
|
-
:
|
|
44
|
+
: input-property (boolean | null) : Value to convert, read from the configured Input Property.
|
|
40
45
|
|
|
41
46
|
### Outputs
|
|
42
|
-
: payload (number) : Converted value `null` to `0` (if `nullToZero`) or `-1`, `false` to `0`, `true` to `1`.
|
|
47
|
+
: payload (number) : Converted value: `null` to `0` (if `nullToZero` is true) or `-1`, `false` to `0`, `true` to `1`.
|
|
48
|
+
|
|
49
|
+
### Properties
|
|
50
|
+
: name (string) : Display name in editor.
|
|
51
|
+
: inputProperty (string) : Message property to read input from (default: `payload`). Supports nested properties (e.g., `data.value`).
|
|
52
|
+
: nullToZero (boolean) : When checked, `null` maps to `0`; when unchecked, `null` maps to `-1`.
|
|
43
53
|
|
|
44
54
|
### Details
|
|
45
|
-
Converts
|
|
55
|
+
Converts boolean or null input (read from the configured **Input Property**, default: `msg.payload`) to a number:
|
|
56
|
+
- `true` → `1`
|
|
57
|
+
- `false` → `0`
|
|
58
|
+
- `null` → `0` (if **Null Mapping** is checked) or `-1` (if unchecked)
|
|
46
59
|
|
|
47
|
-
|
|
60
|
+
Output is always written to `msg.payload`. All other input message properties are passed through unchanged.
|
|
48
61
|
|
|
49
62
|
### Status
|
|
50
63
|
- Green (dot): Configuration update
|
|
@@ -1,36 +1,44 @@
|
|
|
1
1
|
module.exports = function(RED) {
|
|
2
|
+
const utils = require('./utils')(RED);
|
|
3
|
+
|
|
2
4
|
function BooleanToNumberBlockNode(config) {
|
|
3
5
|
RED.nodes.createNode(this, config);
|
|
4
6
|
const node = this;
|
|
5
7
|
|
|
6
8
|
// Initialize runtime for editor display
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
// Initialize state
|
|
10
|
+
node.name = config.name;
|
|
11
|
+
node.inputProperty = config.inputProperty || "payload";
|
|
12
|
+
node.nullToZero = Boolean(config.nullToZero);
|
|
11
13
|
|
|
12
14
|
node.on("input", function(msg, send, done) {
|
|
13
15
|
send = send || function() { node.send.apply(node, arguments); };
|
|
14
16
|
|
|
15
|
-
// Check for missing
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
// Check for missing input property
|
|
18
|
+
let inputValue;
|
|
19
|
+
try {
|
|
20
|
+
inputValue = RED.util.getMessageProperty(msg, node.inputProperty);
|
|
21
|
+
} catch (err) {
|
|
22
|
+
inputValue = undefined;
|
|
23
|
+
}
|
|
24
|
+
if (inputValue === undefined) {
|
|
25
|
+
utils.setStatusError(node, "missing or invalid input property");
|
|
18
26
|
if (done) done();
|
|
19
27
|
return;
|
|
20
28
|
}
|
|
21
29
|
|
|
22
|
-
// Validate and convert
|
|
23
|
-
const inputDisplay =
|
|
24
|
-
if (
|
|
25
|
-
msg.payload = node.
|
|
26
|
-
|
|
30
|
+
// Validate and convert input
|
|
31
|
+
const inputDisplay = inputValue === null ? "null" : String(inputValue);
|
|
32
|
+
if (inputValue === null) {
|
|
33
|
+
msg.payload = node.nullToZero ? 0 : -1;
|
|
34
|
+
utils.setStatusChanged(node, `in: ${inputDisplay}, out: ${msg.payload}`);
|
|
27
35
|
send(msg);
|
|
28
|
-
} else if (typeof
|
|
29
|
-
msg.payload =
|
|
30
|
-
|
|
36
|
+
} else if (typeof inputValue === "boolean") {
|
|
37
|
+
msg.payload = inputValue ? 1 : 0;
|
|
38
|
+
utils.setStatusChanged(node, `in: ${inputDisplay}, out: ${msg.payload}`);
|
|
31
39
|
send(msg);
|
|
32
40
|
} else {
|
|
33
|
-
|
|
41
|
+
utils.setStatusError(node, "invalid input type");
|
|
34
42
|
}
|
|
35
43
|
|
|
36
44
|
if (done) done();
|
package/nodes/cache-block.js
CHANGED
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
module.exports = function(RED) {
|
|
2
|
+
const utils = require('./utils')(RED);
|
|
3
|
+
|
|
2
4
|
function CacheBlockNode(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.operationMode = config.operationMode;
|
|
12
|
+
node.cachedMessage = null;
|
|
12
13
|
|
|
13
14
|
node.on("input", function(msg, send, done) {
|
|
14
15
|
send = send || function() { node.send.apply(node, arguments); };
|
|
15
16
|
|
|
16
17
|
// Guard against invalid message
|
|
17
18
|
if (!msg) {
|
|
18
|
-
|
|
19
|
+
utils.setStatusError(node, "invalid message");
|
|
19
20
|
if (done) done();
|
|
20
21
|
return;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
// Validate context
|
|
24
25
|
if (!msg.hasOwnProperty("context") || typeof msg.context !== "string") {
|
|
25
|
-
|
|
26
|
+
utils.setStatusError(node, "missing context");
|
|
26
27
|
if (done) done();
|
|
27
28
|
return;
|
|
28
29
|
}
|
|
@@ -31,39 +32,29 @@ module.exports = function(RED) {
|
|
|
31
32
|
case "update":
|
|
32
33
|
// Validate payload
|
|
33
34
|
if (!msg.hasOwnProperty("payload")) {
|
|
34
|
-
|
|
35
|
+
utils.setStatusError(node, "missing payload");
|
|
35
36
|
if (done) done();
|
|
36
37
|
return;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
node.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
shape: "dot",
|
|
43
|
-
text: `update: ${typeof msg.payload === "number" ? msg.payload.toFixed(2) : JSON.stringify(msg.payload).slice(0, 20)}`
|
|
44
|
-
});
|
|
40
|
+
node.cachedMessage = RED.util.cloneMessage(msg);
|
|
41
|
+
const updateText = `update: ${typeof msg.payload === "number" ? msg.payload.toFixed(2) : JSON.stringify(msg.payload).slice(0, 20)}`;
|
|
42
|
+
utils.setStatusOK(node, updateText);
|
|
45
43
|
if (done) done();
|
|
46
44
|
return;
|
|
47
45
|
case "execute":
|
|
48
|
-
if (node.
|
|
49
|
-
|
|
50
|
-
fill: "blue",
|
|
51
|
-
shape: "dot",
|
|
52
|
-
text: "execute: null"
|
|
53
|
-
});
|
|
46
|
+
if (node.cachedMessage === null) {
|
|
47
|
+
utils.setStatusChanged(node, "execute: null");
|
|
54
48
|
send({ payload: null });
|
|
55
49
|
} else {
|
|
56
50
|
let outputMsg;
|
|
57
|
-
if (node.
|
|
58
|
-
outputMsg = RED.util.cloneMessage(node.
|
|
51
|
+
if (node.operationMode === "clone") {
|
|
52
|
+
outputMsg = RED.util.cloneMessage(node.cachedMessage);
|
|
59
53
|
} else {
|
|
60
|
-
outputMsg = { payload: node.
|
|
54
|
+
outputMsg = { payload: node.cachedMessage.payload };
|
|
61
55
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
shape: "dot",
|
|
65
|
-
text: `execute: ${typeof outputMsg.payload === "number" ? outputMsg.payload.toFixed(2) : JSON.stringify(outputMsg.payload).slice(0, 20)}`
|
|
66
|
-
});
|
|
56
|
+
const executeText = `execute: ${typeof outputMsg.payload === "number" ? outputMsg.payload.toFixed(2) : JSON.stringify(outputMsg.payload).slice(0, 20)}`;
|
|
57
|
+
utils.setStatusChanged(node, executeText);
|
|
67
58
|
send(outputMsg);
|
|
68
59
|
}
|
|
69
60
|
if (done) done();
|
|
@@ -71,27 +62,23 @@ module.exports = function(RED) {
|
|
|
71
62
|
case "reset":
|
|
72
63
|
// Validate payload
|
|
73
64
|
if (!msg.hasOwnProperty("payload")) {
|
|
74
|
-
|
|
65
|
+
utils.setStatusError(node, "missing payload");
|
|
75
66
|
if (done) done();
|
|
76
67
|
return;
|
|
77
68
|
}
|
|
78
69
|
|
|
79
70
|
if (typeof msg.payload !== "boolean" || !msg.payload) {
|
|
80
|
-
|
|
71
|
+
utils.setStatusError(node, "invalid reset");
|
|
81
72
|
if (done) done();
|
|
82
73
|
return;
|
|
83
74
|
}
|
|
84
75
|
|
|
85
|
-
node.
|
|
86
|
-
|
|
87
|
-
fill: "green",
|
|
88
|
-
shape: "dot",
|
|
89
|
-
text: "reset"
|
|
90
|
-
});
|
|
76
|
+
node.cachedMessage = null;
|
|
77
|
+
utils.setStatusOK(node, "reset");
|
|
91
78
|
if (done) done();
|
|
92
79
|
return;
|
|
93
80
|
default:
|
|
94
|
-
|
|
81
|
+
utils.setStatusWarn(node, "unknown context");
|
|
95
82
|
if (done) done("Unknown context");
|
|
96
83
|
return;
|
|
97
84
|
}
|
|
@@ -4,13 +4,29 @@
|
|
|
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 for call (request) signal"><i class="fa fa-sign-in"></i> Call Property</label>
|
|
9
|
+
<input type="text" id="node-input-inputProperty" placeholder="payload">
|
|
10
|
+
</div>
|
|
11
|
+
<div class="form-row">
|
|
12
|
+
<label for="node-input-statusInputProperty" title="Message property for status (response) signal"><i class="fa fa-sign-out"></i> Status Property</label>
|
|
13
|
+
<input type="text" id="node-input-statusInputProperty" placeholder="status">
|
|
14
|
+
</div>
|
|
7
15
|
<div class="form-row">
|
|
8
16
|
<label for="node-input-statusTimeout" title="Time to wait for status response (seconds, positive number)"><i class="fa fa-clock-o"></i> Status Timeout</label>
|
|
9
17
|
<input type="number" id="node-input-statusTimeout" placeholder="30" min="0.01" step="any">
|
|
10
18
|
</div>
|
|
11
19
|
<div class="form-row">
|
|
12
|
-
<label for="node-input-clearDelay" title="Delay before clearing status and alarm after call ends (seconds,
|
|
13
|
-
<input type="number" id="node-input-clearDelay" placeholder="10" min="0
|
|
20
|
+
<label for="node-input-clearDelay" title="Delay before clearing status and alarm after call ends (seconds, 0=immediate)"><i class="fa fa-hourglass"></i> Clear Delay</label>
|
|
21
|
+
<input type="number" id="node-input-clearDelay" placeholder="10" min="0" step="any">
|
|
22
|
+
</div>
|
|
23
|
+
<div class="form-row">
|
|
24
|
+
<label for="node-input-debounce" title="Debounce status flicker (milliseconds, 0=disabled)"><i class="fa fa-filter"></i> Debounce</label>
|
|
25
|
+
<input type="number" id="node-input-debounce" placeholder="100" min="0" step="any">
|
|
26
|
+
</div>
|
|
27
|
+
<div class="form-row">
|
|
28
|
+
<label for="node-input-heartbeatTimeout" title="Heartbeat window for continuous status monitoring (seconds, 0=disabled)"><i class="fa fa-pulse"></i> Heartbeat Timeout</label>
|
|
29
|
+
<input type="number" id="node-input-heartbeatTimeout" placeholder="30" min="0" step="any">
|
|
14
30
|
</div>
|
|
15
31
|
<div class="form-row">
|
|
16
32
|
<label for="node-input-runLostStatus" title="Alarm if status is lost during call (boolean)"><i class="fa fa-exclamation-triangle"></i> Run Lost Status</label>
|
|
@@ -37,8 +53,12 @@
|
|
|
37
53
|
color: "#301934",
|
|
38
54
|
defaults: {
|
|
39
55
|
name: { value: "" },
|
|
56
|
+
inputProperty: { value: "payload" },
|
|
57
|
+
statusInputProperty: { value: "status" },
|
|
40
58
|
statusTimeout: { value: 30, required: true, validate: function(v) { return !isNaN(parseFloat(v)) && parseFloat(v) > 0; } },
|
|
41
|
-
clearDelay: { value: 10, required: true, validate: function(v) { return !isNaN(parseFloat(v)) && parseFloat(v)
|
|
59
|
+
clearDelay: { value: 10, required: true, validate: function(v) { return !isNaN(parseFloat(v)) && parseFloat(v) >= 0; } },
|
|
60
|
+
debounce: { value: 100, required: true, validate: function(v) { return !isNaN(parseFloat(v)) && parseFloat(v) >= 0; } },
|
|
61
|
+
heartbeatTimeout: { value: 30, required: true, validate: function(v) { return !isNaN(parseFloat(v)) && parseFloat(v) >= 0; } },
|
|
42
62
|
runLostStatus: { value: false },
|
|
43
63
|
noStatusOnRun: { value: true },
|
|
44
64
|
runLostStatusMessage: { value: "Status lost during run" },
|
|
@@ -58,39 +78,78 @@
|
|
|
58
78
|
|
|
59
79
|
<!-- Help Section -->
|
|
60
80
|
<script type="text/markdown" data-help-name="call-status-block">
|
|
61
|
-
Monitors call and status signals
|
|
81
|
+
Monitors call and status signals to detect equipment faults, communication losses, and synchronization errors.
|
|
62
82
|
|
|
63
83
|
### Inputs
|
|
64
|
-
|
|
65
|
-
|
|
84
|
+
|
|
85
|
+
: payload (boolean) : Requested equipment state (call signal). Default property name for sending call commands. When true, block expects equipment to respond with status=true within timeout period. Property name configurable via inputProperty setting.
|
|
86
|
+
: status (boolean) : Equipment response signal. Updates actual equipment state and triggers state transitions. Default property name `msg.status`. Accepts `msg.context="status"` with `msg.payload` as fallback routing.
|
|
66
87
|
|
|
67
88
|
### Outputs
|
|
68
|
-
|
|
69
|
-
|
|
89
|
+
|
|
90
|
+
: payload (boolean) : Current call/requested state (true=on, false=off).
|
|
91
|
+
: status (object) : State information object with properties: call (boolean), status (boolean), alarm (boolean), alarmMessage (string).
|
|
92
|
+
: diagnostics (object) : Internal state for monitoring with properties: state (state machine state), initialTimeout (boolean), heartbeatActive (boolean), neverReceivedStatus (boolean), lastStatusTime (milliseconds or null), timeSinceLastStatus (milliseconds or null).
|
|
70
93
|
|
|
71
94
|
### Properties
|
|
72
|
-
|
|
73
|
-
-
|
|
74
|
-
-
|
|
75
|
-
-
|
|
76
|
-
-
|
|
77
|
-
-
|
|
78
|
-
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
###
|
|
86
|
-
|
|
87
|
-
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
95
|
+
|
|
96
|
+
- name (string) - Display name in editor. Default: `"call status"`.
|
|
97
|
+
- inputProperty (string) - Input message property for call signal. Default: `"payload"`.
|
|
98
|
+
- statusInputProperty (string) - Input message property for status signal. Default: `"status"`.
|
|
99
|
+
- statusTimeout (number) - Seconds to wait for initial status response (0.01–3600s). Default: `30`. If heartbeat monitoring is disabled, this is the only timeout check.
|
|
100
|
+
- heartbeatTimeout (number) - Seconds for continuous status heartbeat monitoring while call=true (0–3600s). Default: `30`. Set to 0 to disable heartbeat monitoring.
|
|
101
|
+
- clearDelay (number) - Seconds to hold actual state after call deactivated (0–3600s). Default: `10`.
|
|
102
|
+
- debounce (number) - Milliseconds to filter status flicker (0–10000ms). Default: `100`.
|
|
103
|
+
- runLostStatus (checkbox) - Alarm if equipment status becomes false while call is true. Default: checked.
|
|
104
|
+
- noStatusOnRun (checkbox) - Alarm if no status response within initial timeout. Default: checked.
|
|
105
|
+
- runLostStatusMessage (string) - Alarm text for status lost. Default: `"Status lost during run"`.
|
|
106
|
+
- noStatusOnRunMessage (string) - Alarm text for no status. Default: `"No status received during run"`.
|
|
107
|
+
|
|
108
|
+
### Details
|
|
109
|
+
|
|
110
|
+
The block implements a 4-state controller: IDLE (call=false), WAITING_FOR_STATUS (call=true, status pending), RUNNING (call=true, status=true), and STATUS_LOST (call=true, status=false).
|
|
111
|
+
|
|
112
|
+
When call=true, the block expects two things: (1) Initial status arrival within statusTimeout seconds, and (2) if heartbeatTimeout is enabled, continuous status updates at least once every heartbeatTimeout seconds. If either requirement is violated, an alarm is triggered.
|
|
113
|
+
|
|
114
|
+
When call=false but status=true, the block monitors that status goes false within clearDelay + 1 second. If status remains true beyond this window, an alarm is triggered indicating the equipment did not respond to the deactivation.
|
|
115
|
+
|
|
116
|
+
Input routing processes messages in priority order: Status Update (from `msg.status` or `msg.context="status"`), Call Request (from configured input property).
|
|
117
|
+
|
|
118
|
+
Debounce collapses rapid status changes within the configured window into a single state transition, preventing false alarms from sensor noise. All alarm conditions include 100ms hysteresis to prevent false triggers.
|
|
119
|
+
|
|
120
|
+
#### Heartbeat Monitoring
|
|
121
|
+
|
|
122
|
+
When call=true and heartbeatTimeout > 0, the block continuously verifies that status updates arrive within the heartbeat window. This detects communication loss or equipment failures mid-cycle. Set heartbeatTimeout=0 to disable this feature.
|
|
123
|
+
|
|
124
|
+
#### Alarm Conditions
|
|
125
|
+
|
|
126
|
+
Status Active Without Call (Hysteresis 100ms): Triggered when status=true AND call=false, indicating potential equipment fault, check valve failure, or external signal interference.
|
|
127
|
+
|
|
128
|
+
No Status Response on Run (Hysteresis: statusTimeout seconds): Triggered when call=true but no status arrives within initial timeout, indicating equipment not responding or wiring failure at call initiation.
|
|
129
|
+
|
|
130
|
+
Status Lost During Run (Hysteresis 100ms): Triggered when call=true, status=true initially, but either: (1) status goes false mid-cycle, or (2) heartbeat monitoring detects no status update within heartbeatTimeout window. Indicates mid-cycle shutdown or communication loss.
|
|
131
|
+
|
|
132
|
+
Status Not Clearing (Hysteresis 100ms): Triggered when call=false, status=true initially, but status does not go false within clearDelay + 1 second window. Indicates equipment failed to deactivate.
|
|
133
|
+
|
|
134
|
+
#### HVAC Scenarios
|
|
135
|
+
|
|
136
|
+
Scenario 1: Chiller with unloader valve feedback. Send `{payload: true}` to request call. Chiller responds with `{context: "status", payload: true}` within 30s. As long as call=true and status updates arrive every 30 seconds, no alarm. If 31 seconds elapse without update, heartbeat alarm triggers. Configuration: Status Timeout=30s, Heartbeat Timeout=30s, Run Lost Status=true.
|
|
137
|
+
|
|
138
|
+
Scenario 2: VAV box with wireless controller. Send `{payload: true}` to activate damper. If wireless module not responding, No Status alarm triggers after 10s. Configuration: Status Timeout=10s, Heartbeat Timeout=0 (heartbeat disabled), No Status On Run=true.
|
|
139
|
+
|
|
140
|
+
Scenario 3: Pump with 5-second startup lag. Send `{payload: true}` at t=0. Pump motor spins up for 5 seconds, status arrives at t=5s. Send `{payload: false}` at t=10s. Clear Delay holds actual state for 3 seconds. At t=13s, state fully cleared. Configuration: Status Timeout=8s, Heartbeat Timeout=15s, Clear Delay=3s, Debounce=200ms.
|
|
141
|
+
|
|
142
|
+
Scenario 4: Equipment with 60-second status heartbeat. Enable heartbeat monitoring with 60-second window. Configuration: Status Timeout=30s, Heartbeat Timeout=60s. Status must arrive at least once every 60 seconds to avoid "Status Lost" alarm.
|
|
143
|
+
|
|
144
|
+
### Status
|
|
145
|
+
- Green (dot): Configuration update
|
|
146
|
+
- Blue (dot): State changed
|
|
147
|
+
- Blue (ring): State unchanged
|
|
148
|
+
- Red (ring): Error
|
|
149
|
+
- Yellow (ring): Warning
|
|
150
|
+
|
|
151
|
+
### References
|
|
152
|
+
|
|
153
|
+
- [Node-RED Documentation](https://nodered.org/docs/)
|
|
154
|
+
- [GitHub Repository](https://github.com/BldgBlocks/node-red-contrib-buildingblocks-control.git)
|
|
96
155
|
</script>
|