@bldgblocks/node-red-contrib-control 0.1.33 → 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 +74 -67
- package/nodes/global-setter.html +1 -1
- package/nodes/global-setter.js +168 -188
- 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-point-register.js +126 -0
- 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-service-read.js +58 -0
- 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-service-write.js +83 -0
- 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 +275 -3
- package/package.json +14 -6
- package/nodes/network-read.html +0 -56
- package/nodes/network-read.js +0 -59
- package/nodes/network-register.js +0 -161
- package/nodes/network-write.html +0 -64
- package/nodes/network-write.js +0 -126
package/nodes/global-getter.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
module.exports = function(RED) {
|
|
2
|
+
const utils = require('./utils')(RED);
|
|
2
3
|
function GlobalGetterNode(config) {
|
|
3
4
|
RED.nodes.createNode(this, config);
|
|
4
5
|
const node = this;
|
|
5
6
|
node.targetNodeId = config.targetNode;
|
|
6
7
|
node.outputProperty = config.outputProperty || "payload";
|
|
8
|
+
node.dropdownPath = config.dropdownPath || "";
|
|
7
9
|
node.updates = config.updates;
|
|
8
10
|
node.detail = config.detail;
|
|
9
11
|
|
|
@@ -15,148 +17,153 @@ module.exports = function(RED) {
|
|
|
15
17
|
const retryDelays = [0, 100, 500, 1000, 2000, 4000, 8000, 16000];
|
|
16
18
|
const maxRetries = retryDelays.length - 1;
|
|
17
19
|
|
|
18
|
-
// ---
|
|
19
|
-
function sendValue(storedObject, msgToReuse) {
|
|
20
|
-
const msg = msgToReuse || {};
|
|
20
|
+
// --- Output Helper ---
|
|
21
|
+
function sendValue(storedObject, msgToReuse, done) {
|
|
22
|
+
const msg = RED.util.cloneMessage(msgToReuse) || {};
|
|
21
23
|
|
|
22
24
|
if (storedObject !== undefined && storedObject !== null) {
|
|
23
|
-
|
|
24
|
-
// CHECK: Is this our Wrapper Format? (Created by Global Setter)
|
|
25
|
+
// Check if this is our custom wrapper object
|
|
25
26
|
if (storedObject && typeof storedObject === 'object' && storedObject.hasOwnProperty('value')) {
|
|
26
|
-
|
|
27
|
-
// Merge all attributes onto the msg root
|
|
28
|
-
// This automatically handles priority, units, metadata, and any future fields
|
|
29
27
|
if (node.detail === "getObject") {
|
|
30
28
|
Object.assign(msg, storedObject);
|
|
31
29
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
if (config.outputPropertyType === "flow" || config.outputPropertyType === "dropdown") {
|
|
31
|
+
if (config.outputProperty === "sourceToFlow") {
|
|
32
|
+
node.context().flow.set(node.dropdownPath, storedObject.value);
|
|
33
|
+
} else {
|
|
34
|
+
node.context().flow.set(node.outputProperty, storedObject.value);
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
RED.util.setMessageProperty(msg, node.outputProperty, storedObject.value);
|
|
38
|
+
}
|
|
36
39
|
} else {
|
|
37
|
-
//
|
|
38
|
-
|
|
40
|
+
// Legacy/Raw values
|
|
41
|
+
if (config.outputPropertyType === "flow" || config.outputPropertyType === "dropdown") {
|
|
42
|
+
if (config.outputProperty === "sourceToFlow") {
|
|
43
|
+
node.context().flow.set(node.dropdownPath, storedObject);
|
|
44
|
+
} else {
|
|
45
|
+
node.context().flow.set(node.outputProperty, storedObject);
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
RED.util.setMessageProperty(msg, node.outputProperty, storedObject);
|
|
49
|
+
}
|
|
39
50
|
msg.metadata = { path: setterNode ? setterNode.varName : "unknown", legacy: true };
|
|
40
51
|
}
|
|
41
52
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
53
|
+
let valDisplay = storedObject.value;
|
|
54
|
+
if (valDisplay === null) valDisplay = "null";
|
|
55
|
+
else if (valDisplay === undefined) valDisplay = "undefined";
|
|
56
|
+
else if (typeof valDisplay === "object") valDisplay = JSON.stringify(valDisplay);
|
|
57
|
+
else valDisplay = typeof valDisplay === "number" ? valDisplay : valDisplay;
|
|
46
58
|
|
|
47
|
-
|
|
48
|
-
|
|
59
|
+
// Trim to 64 characters with ellipsis
|
|
60
|
+
if (valDisplay.length > 64) {
|
|
61
|
+
valDisplay = valDisplay.substring(0, 64) + "...";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
utils.sendSuccess(node, msg, done, `get: ${valDisplay}`, null, "dot");
|
|
49
65
|
} else {
|
|
50
|
-
|
|
66
|
+
utils.sendError(node, msg, done, "global variable undefined");
|
|
51
67
|
}
|
|
52
68
|
}
|
|
53
69
|
|
|
54
|
-
// ---
|
|
70
|
+
// --- Connection Logic ---
|
|
55
71
|
function establishListener() {
|
|
56
|
-
// Look for source node
|
|
57
72
|
setterNode = RED.nodes.getNode(node.targetNodeId);
|
|
58
73
|
|
|
59
|
-
// If found, subscribe
|
|
60
74
|
if (setterNode && setterNode.varName && node.updates === 'always') {
|
|
61
75
|
if (updateListener) {
|
|
62
|
-
|
|
63
|
-
RED.events.removeListener("bldgblocks-global-update", updateListener);
|
|
76
|
+
RED.events.removeListener("bldgblocks:global:value-changed", updateListener);
|
|
64
77
|
}
|
|
65
78
|
|
|
66
79
|
updateListener = function(evt) {
|
|
67
80
|
if (evt.key === setterNode.varName && evt.store === setterNode.storeName) {
|
|
68
|
-
|
|
81
|
+
// Event Trigger: Pass null for done, as it's not a node input
|
|
82
|
+
sendValue(evt.data, {}, null);
|
|
69
83
|
}
|
|
70
84
|
};
|
|
71
85
|
|
|
72
|
-
RED.events.on("bldgblocks
|
|
86
|
+
RED.events.on("bldgblocks:global:value-changed", updateListener);
|
|
73
87
|
|
|
74
|
-
// Clear retry interval once successful
|
|
75
88
|
if (retryAction) {
|
|
76
89
|
clearInterval(retryAction);
|
|
77
90
|
retryAction = null;
|
|
78
91
|
}
|
|
79
92
|
|
|
80
|
-
|
|
93
|
+
utils.setStatusOK(node, "Connected");
|
|
81
94
|
return true;
|
|
82
95
|
}
|
|
83
96
|
return false;
|
|
84
97
|
}
|
|
85
98
|
|
|
86
|
-
// --- Maintain event subscription ---
|
|
87
99
|
function startHealthCheck() {
|
|
88
|
-
const
|
|
89
|
-
const listeners = RED.events.listeners("bldgblocks
|
|
100
|
+
const check = () => {
|
|
101
|
+
const listeners = RED.events.listeners("bldgblocks:global:value-changed");
|
|
90
102
|
const hasOurListener = listeners.includes(updateListener);
|
|
91
|
-
|
|
92
103
|
if (!hasOurListener) {
|
|
93
104
|
node.warn("Event listener lost, reconnecting...");
|
|
94
105
|
if (establishListener()) {
|
|
95
|
-
|
|
106
|
+
utils.setStatusOK(node, "Reconnected");
|
|
96
107
|
}
|
|
97
108
|
}
|
|
98
|
-
|
|
99
|
-
// Schedule next health check regardless of outcome
|
|
100
|
-
setTimeout(healthCheckAction, 30000);
|
|
109
|
+
setTimeout(check, 30000);
|
|
101
110
|
};
|
|
102
|
-
|
|
103
|
-
setTimeout(healthCheckAction, 30000);
|
|
111
|
+
setTimeout(check, 30000);
|
|
104
112
|
}
|
|
105
113
|
|
|
106
114
|
function subscribeWithRetry() {
|
|
107
|
-
// Recursive retry
|
|
108
115
|
retryAction = () => {
|
|
109
116
|
if (retryCount >= maxRetries) {
|
|
110
|
-
|
|
111
|
-
node.status({ fill: "red", shape: "ring", text: "Connection failed" });
|
|
117
|
+
utils.sendError(node, null, null, "Connection failed");
|
|
112
118
|
return;
|
|
113
119
|
}
|
|
114
|
-
|
|
115
120
|
if (establishListener()) {
|
|
116
121
|
retryCount = 0;
|
|
117
|
-
return;
|
|
122
|
+
return;
|
|
118
123
|
}
|
|
119
|
-
|
|
120
124
|
retryCount++;
|
|
121
125
|
setTimeout(retryAction, retryDelays[Math.min(retryCount, maxRetries - 1)]);
|
|
122
126
|
};
|
|
123
|
-
|
|
124
127
|
setTimeout(retryAction, retryDelays[0]);
|
|
125
128
|
}
|
|
126
129
|
|
|
127
|
-
// ---
|
|
128
|
-
node.on('input', function(msg, send, done) {
|
|
130
|
+
// --- INPUT HANDLER ---
|
|
131
|
+
node.on('input', async function(msg, send, done) {
|
|
129
132
|
send = send || function() { node.send.apply(node, arguments); };
|
|
130
133
|
|
|
131
|
-
|
|
134
|
+
try {
|
|
135
|
+
setterNode ??= RED.nodes.getNode(node.targetNodeId);
|
|
132
136
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
if (setterNode && setterNode.varName) {
|
|
138
|
+
// Async Get - required default store to keep values in memory for polled getter nodes.
|
|
139
|
+
// 'persistent' for cross reboot storage.
|
|
140
|
+
let storedObject = await utils.getGlobalState(node, setterNode.varName, 'default');
|
|
141
|
+
if (!storedObject) {
|
|
142
|
+
// Fallback to persistent store if not found in default. Should not happen normally.
|
|
143
|
+
storedObject = await utils.getGlobalState(node, setterNode.varName, setterNode.storeName);
|
|
144
|
+
}
|
|
145
|
+
sendValue(storedObject, msg, done);
|
|
146
|
+
} else {
|
|
147
|
+
node.warn("Source node not found or not configured.");
|
|
148
|
+
utils.sendError(node, msg, done, "Source node not found");
|
|
149
|
+
}
|
|
150
|
+
} catch (err) {
|
|
151
|
+
node.error(err);
|
|
152
|
+
utils.sendError(node, msg, done, `Internal Error: ${err.message}`);
|
|
139
153
|
}
|
|
140
|
-
|
|
141
|
-
if (done) done();
|
|
142
154
|
});
|
|
143
155
|
|
|
144
|
-
// ---
|
|
156
|
+
// --- INIT ---
|
|
145
157
|
if (node.updates === 'always') {
|
|
146
158
|
subscribeWithRetry();
|
|
147
159
|
startHealthCheck();
|
|
148
160
|
}
|
|
149
161
|
|
|
150
|
-
// --- CLEANUP ---
|
|
151
162
|
node.on('close', function(removed, done) {
|
|
152
|
-
if (healthCheckAction)
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
if (retryAction) {
|
|
156
|
-
clearInterval(retryAction);
|
|
157
|
-
}
|
|
163
|
+
if (healthCheckAction) clearInterval(healthCheckAction);
|
|
164
|
+
if (retryAction) clearInterval(retryAction);
|
|
158
165
|
if (removed && updateListener) {
|
|
159
|
-
RED.events.removeListener("bldgblocks
|
|
166
|
+
RED.events.removeListener("bldgblocks:global:value-changed", updateListener);
|
|
160
167
|
}
|
|
161
168
|
done();
|
|
162
169
|
});
|
package/nodes/global-setter.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
</div>
|
|
6
6
|
<div class="form-row">
|
|
7
7
|
<label for="node-input-path"><i class="fa fa-sitemap"></i> Global Path</label>
|
|
8
|
-
<input type="text" id="node-input-path"
|
|
8
|
+
<input type="text" id="node-input-path" placeholder="furnace/outputs/heat">
|
|
9
9
|
</div>
|
|
10
10
|
<div class="form-row">
|
|
11
11
|
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> Input</label>
|