@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.
Files changed (110) hide show
  1. package/nodes/accumulate-block.html +18 -8
  2. package/nodes/accumulate-block.js +39 -44
  3. package/nodes/add-block.html +1 -1
  4. package/nodes/add-block.js +18 -11
  5. package/nodes/alarm-collector.html +260 -0
  6. package/nodes/alarm-collector.js +292 -0
  7. package/nodes/alarm-config.html +129 -0
  8. package/nodes/alarm-config.js +126 -0
  9. package/nodes/alarm-service.html +96 -0
  10. package/nodes/alarm-service.js +142 -0
  11. package/nodes/analog-switch-block.js +25 -36
  12. package/nodes/and-block.js +44 -15
  13. package/nodes/average-block.js +46 -41
  14. package/nodes/boolean-switch-block.js +10 -28
  15. package/nodes/boolean-to-number-block.html +18 -5
  16. package/nodes/boolean-to-number-block.js +24 -16
  17. package/nodes/cache-block.js +24 -37
  18. package/nodes/call-status-block.html +91 -32
  19. package/nodes/call-status-block.js +398 -115
  20. package/nodes/changeover-block.html +5 -0
  21. package/nodes/changeover-block.js +167 -162
  22. package/nodes/comment-block.html +1 -1
  23. package/nodes/comment-block.js +14 -9
  24. package/nodes/compare-block.html +14 -4
  25. package/nodes/compare-block.js +23 -18
  26. package/nodes/contextual-label-block.html +5 -0
  27. package/nodes/contextual-label-block.js +6 -16
  28. package/nodes/convert-block.html +25 -39
  29. package/nodes/convert-block.js +31 -16
  30. package/nodes/count-block.html +11 -5
  31. package/nodes/count-block.js +34 -32
  32. package/nodes/delay-block.js +58 -53
  33. package/nodes/divide-block.js +43 -45
  34. package/nodes/edge-block.html +17 -10
  35. package/nodes/edge-block.js +43 -41
  36. package/nodes/enum-switch-block.js +6 -6
  37. package/nodes/frequency-block.html +6 -1
  38. package/nodes/frequency-block.js +64 -74
  39. package/nodes/global-getter.html +51 -15
  40. package/nodes/global-getter.js +43 -13
  41. package/nodes/global-setter.html +1 -1
  42. package/nodes/global-setter.js +40 -12
  43. package/nodes/history-buffer.html +96 -0
  44. package/nodes/history-buffer.js +461 -0
  45. package/nodes/history-collector.html +29 -1
  46. package/nodes/history-collector.js +37 -16
  47. package/nodes/history-config.html +13 -1
  48. package/nodes/history-service.html +84 -0
  49. package/nodes/history-service.js +52 -0
  50. package/nodes/hysteresis-block.html +5 -0
  51. package/nodes/hysteresis-block.js +13 -16
  52. package/nodes/interpolate-block.html +20 -2
  53. package/nodes/interpolate-block.js +39 -50
  54. package/nodes/join.html +78 -0
  55. package/nodes/join.js +78 -0
  56. package/nodes/latch-block.js +12 -14
  57. package/nodes/load-sequence-block.js +102 -110
  58. package/nodes/max-block.js +26 -26
  59. package/nodes/memory-block.js +57 -58
  60. package/nodes/min-block.js +26 -25
  61. package/nodes/minmax-block.js +35 -34
  62. package/nodes/modulo-block.js +45 -43
  63. package/nodes/multiply-block.js +43 -41
  64. package/nodes/negate-block.html +17 -7
  65. package/nodes/negate-block.js +25 -19
  66. package/nodes/network-point-read.html +128 -0
  67. package/nodes/network-point-read.js +230 -0
  68. package/nodes/{network-register.html → network-point-register.html} +94 -7
  69. package/nodes/{network-register.js → network-point-register.js} +18 -4
  70. package/nodes/network-point-write.html +149 -0
  71. package/nodes/network-point-write.js +222 -0
  72. package/nodes/network-service-bridge.html +131 -0
  73. package/nodes/network-service-bridge.js +376 -0
  74. package/nodes/network-service-read.html +81 -0
  75. package/nodes/{network-read.js → network-service-read.js} +4 -3
  76. package/nodes/{network-point-registry.html → network-service-registry.html} +19 -4
  77. package/nodes/{network-point-registry.js → network-service-registry.js} +7 -2
  78. package/nodes/network-service-write.html +89 -0
  79. package/nodes/{network-write.js → network-service-write.js} +3 -3
  80. package/nodes/nullify-block.js +13 -15
  81. package/nodes/on-change-block.html +17 -9
  82. package/nodes/on-change-block.js +49 -46
  83. package/nodes/oneshot-block.html +13 -10
  84. package/nodes/oneshot-block.js +57 -75
  85. package/nodes/or-block.js +44 -15
  86. package/nodes/pid-block.html +54 -4
  87. package/nodes/pid-block.js +459 -248
  88. package/nodes/priority-block.js +24 -35
  89. package/nodes/rate-limit-block.js +70 -72
  90. package/nodes/rate-of-change-block.html +33 -14
  91. package/nodes/rate-of-change-block.js +74 -62
  92. package/nodes/round-block.html +14 -9
  93. package/nodes/round-block.js +32 -25
  94. package/nodes/saw-tooth-wave-block.js +49 -76
  95. package/nodes/scale-range-block.html +12 -6
  96. package/nodes/scale-range-block.js +46 -39
  97. package/nodes/sine-wave-block.js +49 -57
  98. package/nodes/string-builder-block.js +6 -6
  99. package/nodes/subtract-block.js +38 -34
  100. package/nodes/thermistor-block.js +44 -44
  101. package/nodes/tick-tock-block.js +32 -32
  102. package/nodes/time-sequence-block.js +30 -42
  103. package/nodes/triangle-wave-block.js +49 -69
  104. package/nodes/tstat-block.js +34 -44
  105. package/nodes/units-block.html +90 -69
  106. package/nodes/units-block.js +22 -30
  107. package/nodes/utils.js +206 -3
  108. package/package.json +14 -6
  109. package/nodes/network-read.html +0 -56
  110. package/nodes/network-write.html +0 -65
@@ -10,16 +10,15 @@ module.exports = function(RED) {
10
10
  const node = this;
11
11
 
12
12
  // Initialize runtime state
13
- node.runtime = {
14
- name: config.name,
15
- writePeriod: config.writePeriod,
16
- transferProperty: config.transferProperty,
17
- writeOnUpdate: config.writeOnUpdate === true,
18
- storedMsg: null
19
- };
13
+ // Initialize state
14
+ node.name = config.name;
15
+ node.writePeriod = config.writePeriod;
16
+ node.transferProperty = config.transferProperty;
17
+ node.writeOnUpdate = config.writeOnUpdate === true;
18
+ node.storedMsg = null;
20
19
 
21
20
  // Resolve typed inputs
22
- node.runtime.writePeriod = parseFloat(RED.util.evaluateNodeProperty( config.writePeriod, config.writePeriodType, node ));
21
+ node.writePeriod = parseFloat(RED.util.evaluateNodeProperty( config.writePeriod, config.writePeriodType, node ));
23
22
 
24
23
  // File path for persistent storage
25
24
  const filePath = path.join(RED.settings.userDir, `memory-${node.id}.json`);
@@ -31,12 +30,12 @@ module.exports = function(RED) {
31
30
  async function loadStoredMessage() {
32
31
  try {
33
32
  const data = await fs.readFile(filePath, "utf8");
34
- node.runtime.storedMsg = JSON.parse(data);
35
- const payloadStr = node.runtime.storedMsg[node.runtime.transferProperty] != null ? String(node.runtime.storedMsg[node.runtime.transferProperty]).substring(0, 20) : "null";
36
- node.status({ fill: "green", shape: "dot", text: `loaded: ${payloadStr}` });
33
+ node.storedMsg = JSON.parse(data);
34
+ const payloadStr = node.storedMsg[node.transferProperty] != null ? String(node.storedMsg[node.transferProperty]).substring(0, 20) : "null";
35
+ utils.setStatusOK(node, `loaded: ${payloadStr}`);
37
36
  } catch (err) {
38
37
  if (err.code !== "ENOENT") {
39
- node.status({ fill: "red", shape: "ring", text: "file error" });
38
+ utils.setStatusError(node, "file error");
40
39
  }
41
40
  }
42
41
  }
@@ -50,7 +49,7 @@ module.exports = function(RED) {
50
49
  }
51
50
  return null;
52
51
  } catch (err) {
53
- node.status({ fill: "red", shape: "ring", text: "file read error" });
52
+ utils.setStatusError(node, "file read error");
54
53
  node.error("Failed to read stored message: " + err.message);
55
54
  return null;
56
55
  }
@@ -63,13 +62,13 @@ module.exports = function(RED) {
63
62
  await fs.writeFile(filePath, JSON.stringify(lastUpdateMsg));
64
63
  lastUpdateMsg = null;
65
64
  } catch (err) {
66
- node.status({ fill: "red", shape: "ring", text: "file error" });
65
+ utils.setStatusError(node, "file error");
67
66
  node.error("Failed to save message: " + err.message);
68
67
  }
69
68
  }
70
69
 
71
70
  // Initialize (load only if writeOnUpdate is false)
72
- if (!node.runtime.writeOnUpdate) {
71
+ if (!node.writeOnUpdate) {
73
72
  loadStoredMessage().catch(err => {
74
73
  node.error("Failed to load stored message: " + err.message);
75
74
  });
@@ -80,14 +79,14 @@ module.exports = function(RED) {
80
79
 
81
80
  // Guard against invalid message
82
81
  if (!msg) {
83
- node.status({ fill: "red", shape: "ring", text: "invalid message" });
82
+ utils.setStatusError(node, "invalid message");
84
83
  if (done) done();
85
84
  return;
86
85
  }
87
86
 
88
87
  // Evaluate typed-inputs if needed
89
88
  if (utils.requiresEvaluation(config.writePeriodType)) {
90
- node.runtime.writePeriod = parseFloat(RED.util.evaluateNodeProperty( config.writePeriod, config.writePeriodType, node, msg ));
89
+ node.writePeriod = parseFloat(RED.util.evaluateNodeProperty( config.writePeriod, config.writePeriodType, node, msg ));
91
90
  }
92
91
 
93
92
  // Initialize output array: [Output 1, Output 2]
@@ -96,8 +95,8 @@ module.exports = function(RED) {
96
95
  // Handle context
97
96
  if (!msg.hasOwnProperty("context") || !msg.context || typeof msg.context !== "string") {
98
97
  // Pass-through message to Output 2
99
- const payloadStr = msg[node.runtime.transferProperty] != null ? String(msg[node.runtime.transferProperty]).substring(0, 20) : "null";
100
- node.status({ fill: "blue", shape: "dot", text: `in: ${payloadStr}, out2: ${payloadStr}` });
98
+ const payloadStr = msg[node.transferProperty] != null ? String(msg[node.transferProperty]).substring(0, 20) : "null";
99
+ utils.setStatusChanged(node, `in: ${payloadStr}, out2: ${payloadStr}`);
101
100
  output[1] = msg;
102
101
  send(output);
103
102
  if (done) done();
@@ -105,30 +104,30 @@ module.exports = function(RED) {
105
104
  }
106
105
 
107
106
  if (msg.context === "update") {
108
- if (!msg.hasOwnProperty(node.runtime.transferProperty)) {
109
- node.status({ fill: "red", shape: "ring", text: `missing ${node.runtime.transferProperty}` });
107
+ if (!msg.hasOwnProperty(node.transferProperty)) {
108
+ utils.setStatusError(node, `missing ${node.transferProperty}`);
110
109
  if (done) done();
111
110
  return;
112
111
  }
113
- const payloadStr = msg[node.runtime.transferProperty] != null ? String(msg[node.runtime.transferProperty]).substring(0, 20) : "null";
114
- if (node.runtime.writeOnUpdate) {
112
+ const payloadStr = msg[node.transferProperty] != null ? String(msg[node.transferProperty]).substring(0, 20) : "null";
113
+ if (node.writeOnUpdate) {
115
114
  // Write directly to file, do not store in memory
116
115
  try {
117
116
  fs.writeFile(filePath, JSON.stringify(msg)).catch(err => {
118
- node.status({ fill: "red", shape: "ring", text: "file error" });
117
+ utils.setStatusError(node, "file error");
119
118
  node.error("Failed to save message: " + err.message);
120
119
  });
121
- node.status({ fill: "green", shape: "dot", text: `updated: ${payloadStr}` });
120
+ utils.setStatusOK(node, `updated: ${payloadStr}`);
122
121
  } catch (err) {
123
- node.status({ fill: "red", shape: "ring", text: "file error" });
122
+ utils.setStatusError(node, "file error");
124
123
  node.error("Failed to save message: " + err.message);
125
124
  }
126
125
  } else {
127
126
  // Original behavior: store in memory and context, delay write
128
- node.runtime.storedMsg = RED.util.cloneMessage(msg);
129
- node.context().set("storedMsg", node.runtime.storedMsg);
130
- lastUpdateMsg = node.runtime.storedMsg;
131
- node.status({ fill: "green", shape: "dot", text: `updated: ${payloadStr}` });
127
+ node.storedMsg = RED.util.cloneMessage(msg);
128
+ node.context().set("storedMsg", node.storedMsg);
129
+ lastUpdateMsg = node.storedMsg;
130
+ utils.setStatusOK(node, `updated: ${payloadStr}`);
132
131
  if (writeTimeout) clearTimeout(writeTimeout);
133
132
  writeTimeout = setTimeout(() => {
134
133
  saveMessage();
@@ -139,15 +138,15 @@ module.exports = function(RED) {
139
138
  }
140
139
 
141
140
  if (msg.context === "execute") {
142
- let storedMsg = node.runtime.writeOnUpdate ? readStoredMessageSync() : node.runtime.storedMsg;
141
+ let storedMsg = node.writeOnUpdate ? readStoredMessageSync() : node.storedMsg;
143
142
  if (storedMsg !== null) {
144
143
  const outMsg = RED.util.cloneMessage(msg);
145
- outMsg[node.runtime.transferProperty] = storedMsg[node.runtime.transferProperty];
146
- const payloadStr = outMsg[node.runtime.transferProperty] != null ? String(outMsg[node.runtime.transferProperty]).substring(0, 20) : "null";
147
- node.status({ fill: "blue", shape: "dot", text: `in: execute, out2: ${payloadStr}` });
144
+ outMsg[node.transferProperty] = storedMsg[node.transferProperty];
145
+ const payloadStr = outMsg[node.transferProperty] != null ? String(outMsg[node.transferProperty]).substring(0, 20) : "null";
146
+ utils.setStatusChanged(node, `in: execute, out2: ${payloadStr}`);
148
147
  output[1] = outMsg;
149
148
  } else {
150
- node.status({ fill: "blue", shape: "ring", text: `in: execute, out2: null` });
149
+ utils.setStatusUnchanged(node, `in: execute, out2: null`);
151
150
  output[1] = { payload: null };
152
151
  }
153
152
  send(output);
@@ -156,51 +155,51 @@ module.exports = function(RED) {
156
155
  }
157
156
 
158
157
  if (msg.context === "executeWithFallback") {
159
- let storedMsg = node.runtime.writeOnUpdate ? readStoredMessageSync() : node.runtime.storedMsg;
158
+ let storedMsg = node.writeOnUpdate ? readStoredMessageSync() : node.storedMsg;
160
159
  if (storedMsg !== null) {
161
160
  const outMsg = RED.util.cloneMessage(msg);
162
- outMsg[node.runtime.transferProperty] = storedMsg[node.runtime.transferProperty];
163
- const payloadStr = outMsg[node.runtime.transferProperty] != null ? String(outMsg[node.runtime.transferProperty]).substring(0, 20) : "null";
164
- node.status({ fill: "blue", shape: "dot", text: `in: executeWithFallback, out2: ${payloadStr}` });
161
+ outMsg[node.transferProperty] = storedMsg[node.transferProperty];
162
+ const payloadStr = outMsg[node.transferProperty] != null ? String(outMsg[node.transferProperty]).substring(0, 20) : "null";
163
+ utils.setStatusChanged(node, `in: executeWithFallback, out2: ${payloadStr}`);
165
164
  output[1] = outMsg;
166
165
  } else {
167
166
  let value;
168
- if (msg.hasOwnProperty(node.runtime.transferProperty)) {
169
- value = msg[node.runtime.transferProperty];
167
+ if (msg.hasOwnProperty(node.transferProperty)) {
168
+ value = msg[node.transferProperty];
170
169
  }
171
170
  else if (msg.hasOwnProperty("fallback")) {
172
171
  value = msg.fallback;
173
172
  } else {
174
- node.status({ fill: "red", shape: "ring", text: `missing ${node.runtime.transferProperty}` });
173
+ utils.setStatusError(node, `missing ${node.transferProperty}`);
175
174
  if (done) done();
176
175
  return;
177
176
  }
178
177
 
179
- if (node.runtime.writeOnUpdate) {
178
+ if (node.writeOnUpdate) {
180
179
  // Write directly to file
181
180
  try {
182
- fs.writeFile(filePath, JSON.stringify({ [node.runtime.transferProperty]: value })).catch(err => {
183
- node.status({ fill: "red", shape: "ring", text: "file error" });
181
+ fs.writeFile(filePath, JSON.stringify({ [node.transferProperty]: value })).catch(err => {
182
+ utils.setStatusError(node, "file error");
184
183
  node.error("Failed to save message: " + err.message);
185
184
  });
186
185
  } catch (err) {
187
- node.status({ fill: "red", shape: "ring", text: "file error" });
186
+ utils.setStatusError(node, "file error");
188
187
  node.error("Failed to save message: " + err.message);
189
188
  }
190
189
  } else {
191
190
  // Store in memory and context
192
- node.runtime.storedMsg = { [node.runtime.transferProperty]: value };
193
- node.context().set("storedMsg", node.runtime.storedMsg);
194
- lastUpdateMsg = node.runtime.storedMsg;
191
+ node.storedMsg = { [node.transferProperty]: value };
192
+ node.context().set("storedMsg", node.storedMsg);
193
+ lastUpdateMsg = node.storedMsg;
195
194
  if (writeTimeout) clearTimeout(writeTimeout);
196
195
  writeTimeout = setTimeout(() => {
197
196
  saveMessage();
198
197
  }, writePeriod);
199
198
  }
200
199
  const outMsg = RED.util.cloneMessage(msg);
201
- outMsg[node.runtime.transferProperty] = value;
202
- const payloadStr = msg[node.runtime.transferProperty] != null ? String(msg[node.runtime.transferProperty]).substring(0, 20) : "null";
203
- node.status({ fill: "blue", shape: "dot", text: `in: executeWithFallback, out2: ${payloadStr}` });
200
+ outMsg[node.transferProperty] = value;
201
+ const payloadStr = msg[node.transferProperty] != null ? String(msg[node.transferProperty]).substring(0, 20) : "null";
202
+ utils.setStatusChanged(node, `in: executeWithFallback, out2: ${payloadStr}`);
204
203
  output[1] = outMsg;
205
204
  }
206
205
  send(output);
@@ -209,29 +208,29 @@ module.exports = function(RED) {
209
208
  }
210
209
 
211
210
  if (msg.context === "query") {
212
- const hasValue = node.runtime.writeOnUpdate ? fsSync.existsSync(filePath) : node.runtime.storedMsg !== null;
213
- node.status({ fill: "blue", shape: "dot", text: `in: query, out1: ${hasValue}` });
211
+ const hasValue = node.writeOnUpdate ? fsSync.existsSync(filePath) : node.storedMsg !== null;
212
+ utils.setStatusChanged(node, `in: query, out1: ${hasValue}`);
214
213
  output[0] = { payload: hasValue };
215
214
  send(output);
216
215
  if (done) done();
217
216
  return;
218
217
  }
219
218
 
220
- node.status({ fill: "yellow", shape: "ring", text: "unknown context" });
219
+ utils.setStatusWarn(node, "unknown context");
221
220
  if (done) done("Unknown context");
222
221
  });
223
222
 
224
223
  node.on("close", function(done) {
225
224
  if (writeTimeout) clearTimeout(writeTimeout);
226
- if (!node.runtime.writeOnUpdate && lastUpdateMsg) {
225
+ if (!node.writeOnUpdate && lastUpdateMsg) {
227
226
  saveMessage()
228
227
  .then(() => {
229
- node.status({});
228
+ utils.setStatusOK(node, "");
230
229
  done();
231
230
  })
232
231
  .catch(err => {
233
232
  node.error("Failed to save message on close: " + err.message);
234
- node.status({});
233
+ utils.setStatusError(node, "save error");
235
234
  done();
236
235
  });
237
236
  } else {
@@ -7,10 +7,9 @@ module.exports = function(RED) {
7
7
  node.isBusy = false;
8
8
 
9
9
  // Initialize runtime state
10
- node.runtime = {
11
- name: config.name,
12
- min: parseFloat(config.min)
13
- };
10
+ // Initialize state
11
+ node.name = config.name;
12
+ node.min = parseFloat(config.min);
14
13
 
15
14
  // Store last output value for status
16
15
  let lastOutput = null;
@@ -20,7 +19,7 @@ module.exports = function(RED) {
20
19
 
21
20
  // Guard against invalid message
22
21
  if (!msg) {
23
- node.status({ fill: "red", shape: "ring", text: "invalid message" });
22
+ utils.setStatusError(node, "invalid message");
24
23
  if (done) done();
25
24
  return;
26
25
  }
@@ -31,7 +30,7 @@ module.exports = function(RED) {
31
30
  // Check busy lock
32
31
  if (node.isBusy) {
33
32
  // Update status to let user know they are pushing too fast
34
- node.status({ fill: "yellow", shape: "ring", text: "busy - dropped msg" });
33
+ utils.setStatusBusy(node, "busy - dropped msg");
35
34
  if (done) done();
36
35
  return;
37
36
  }
@@ -46,13 +45,13 @@ module.exports = function(RED) {
46
45
  utils.requiresEvaluation(config.minType)
47
46
  ? utils.evaluateNodeProperty(config.min, config.minType, node, msg)
48
47
  .then(val => parseFloat(val))
49
- : Promise.resolve(node.runtime.min),
48
+ : Promise.resolve(node.min),
50
49
  );
51
50
 
52
51
  const results = await Promise.all(evaluations);
53
52
 
54
53
  // Update runtime with evaluated values
55
- if (!isNaN(results[0])) node.runtime.min = results[0];
54
+ if (!isNaN(results[0])) node.min = results[0];
56
55
  } catch (err) {
57
56
  node.error(`Error evaluating properties: ${err.message}`);
58
57
  if (done) done();
@@ -63,8 +62,8 @@ module.exports = function(RED) {
63
62
  }
64
63
 
65
64
  // Validate
66
- if (isNaN(node.runtime.min)) {
67
- node.status({ fill: "red", shape: "ring", text: "invalid evaluated values" });
65
+ if (isNaN(node.min)) {
66
+ utils.setStatusError(node, "invalid evaluated values");
68
67
  if (done) done();
69
68
  return;
70
69
  }
@@ -72,22 +71,22 @@ module.exports = function(RED) {
72
71
  // Handle context updates
73
72
  if (msg.hasOwnProperty("context")) {
74
73
  if (!msg.hasOwnProperty("payload")) {
75
- node.status({ fill: "red", shape: "ring", text: "missing payload for min" });
74
+ utils.setStatusError(node, "missing payload for min");
76
75
  if (done) done();
77
76
  return;
78
77
  }
79
78
  if (msg.context === "min" || msg.context === "setpoint") {
80
79
  const minValue = parseFloat(msg.payload);
81
80
  if (!isNaN(minValue) && minValue >= 0) {
82
- node.runtime.min = minValue;
83
- node.status({ fill: "green", shape: "dot", text: `min: ${minValue}` });
81
+ node.min = minValue;
82
+ utils.setStatusOK(node, `min: ${minValue}`);
84
83
  } else {
85
- node.status({ fill: "red", shape: "ring", text: "invalid min" });
84
+ utils.setStatusError(node, "invalid min");
86
85
  }
87
86
  if (done) done();
88
87
  return;
89
88
  } else {
90
- node.status({ fill: "yellow", shape: "ring", text: "unknown context" });
89
+ utils.setStatusWarn(node, "unknown context");
91
90
  if (done) done();
92
91
  return;
93
92
  }
@@ -95,28 +94,30 @@ module.exports = function(RED) {
95
94
 
96
95
  // Validate input payload
97
96
  if (!msg.hasOwnProperty("payload")) {
98
- node.status({ fill: "red", shape: "ring", text: "missing payload" });
97
+ utils.setStatusError(node, "missing payload");
99
98
  if (done) done();
100
99
  return;
101
100
  }
102
101
 
103
- const inputValue = parseFloat(msg.payload);
104
- if (isNaN(inputValue)) {
105
- node.status({ fill: "red", shape: "ring", text: "invalid payload" });
102
+ const numVal = utils.validateNumericPayload(msg.payload);
103
+ if (!numVal.valid) {
104
+ utils.setStatusError(node, numVal.error);
106
105
  if (done) done();
107
106
  return;
108
107
  }
108
+ const inputValue = numVal.value;
109
109
 
110
110
  // Cap input at min
111
- const outputValue = Math.max(inputValue, node.runtime.min);
111
+ const outputValue = Math.max(inputValue, node.min);
112
112
 
113
113
  // Update status and send output
114
114
  msg.payload = outputValue;
115
- node.status({
116
- fill: "blue",
117
- shape: lastOutput === outputValue ? "ring" : "dot",
118
- text: `in: ${inputValue.toFixed(2)}, out: ${outputValue.toFixed(2)}`
119
- });
115
+ const statusText = `in: ${inputValue.toFixed(2)}, out: ${outputValue.toFixed(2)}`;
116
+ if (lastOutput === outputValue) {
117
+ utils.setStatusUnchanged(node, statusText);
118
+ } else {
119
+ utils.setStatusChanged(node, statusText);
120
+ }
120
121
  lastOutput = outputValue;
121
122
  send(msg);
122
123
 
@@ -7,11 +7,10 @@ module.exports = function(RED) {
7
7
  node.isBusy = false;
8
8
 
9
9
  // Initialize runtime state
10
- node.runtime = {
11
- name: config.name,
12
- min: parseFloat(config.min),
13
- max: parseFloat(config.max)
14
- };
10
+ // Initialize state
11
+ node.name = config.name;
12
+ node.min = parseFloat(config.min);
13
+ node.max = parseFloat(config.max);
15
14
 
16
15
  // Store last output value for status
17
16
  let lastOutput = null;
@@ -21,7 +20,7 @@ module.exports = function(RED) {
21
20
 
22
21
  // Guard against invalid message
23
22
  if (!msg) {
24
- node.status({ fill: "red", shape: "ring", text: "invalid message" });
23
+ utils.setStatusError(node, "invalid message");
25
24
  if (done) done();
26
25
  return;
27
26
  }
@@ -32,7 +31,7 @@ module.exports = function(RED) {
32
31
  // Check busy lock
33
32
  if (node.isBusy) {
34
33
  // Update status to let user know they are pushing too fast
35
- node.status({ fill: "yellow", shape: "ring", text: "busy - dropped msg" });
34
+ utils.setStatusBusy(node, "busy - dropped msg");
36
35
  if (done) done();
37
36
  return;
38
37
  }
@@ -47,21 +46,21 @@ module.exports = function(RED) {
47
46
  utils.requiresEvaluation(config.minType)
48
47
  ? utils.evaluateNodeProperty(config.min, config.minType, node, msg)
49
48
  .then(val => parseFloat(val))
50
- : Promise.resolve(node.runtime.min),
49
+ : Promise.resolve(node.min),
51
50
  );
52
51
 
53
52
  evaluations.push(
54
53
  utils.requiresEvaluation(config.maxType)
55
54
  ? utils.evaluateNodeProperty(config.max, config.maxType, node, msg)
56
55
  .then(val => parseFloat(val))
57
- : Promise.resolve(node.runtime.max),
56
+ : Promise.resolve(node.max),
58
57
  );
59
58
 
60
59
  const results = await Promise.all(evaluations);
61
60
 
62
61
  // Update runtime with evaluated values
63
- if (!isNaN(results[0])) node.runtime.min = results[0];
64
- if (!isNaN(results[1])) node.runtime.max = results[1];
62
+ if (!isNaN(results[0])) node.min = results[0];
63
+ if (!isNaN(results[1])) node.max = results[1];
65
64
  } catch (err) {
66
65
  node.error(`Error evaluating properties: ${err.message}`);
67
66
  if (done) done();
@@ -72,8 +71,8 @@ module.exports = function(RED) {
72
71
  }
73
72
 
74
73
  // Validate min and max
75
- if (isNaN(node.runtime.min) || isNaN(node.runtime.max) || node.runtime.min > node.runtime.max) {
76
- node.status({ fill: "red", shape: "dot", text: `invalid min/max` });
74
+ if (isNaN(node.min) || isNaN(node.max) || node.min > node.max) {
75
+ utils.setStatusError(node, `invalid min/max`);
77
76
  if (done) done();
78
77
  return;
79
78
  }
@@ -81,32 +80,32 @@ module.exports = function(RED) {
81
80
  // Handle context updates
82
81
  if (msg.hasOwnProperty("context")) {
83
82
  if (!msg.hasOwnProperty("payload")) {
84
- node.status({ fill: "red", shape: "ring", text: `missing payload for ${msg.context}` });
83
+ utils.setStatusError(node, `missing payload for ${msg.context}`);
85
84
  if (done) done();
86
85
  return;
87
86
  }
88
87
  const value = parseFloat(msg.payload);
89
88
  if (isNaN(value) || value < 0) {
90
- node.status({ fill: "red", shape: "ring", text: `invalid ${msg.context}` });
89
+ utils.setStatusError(node, `invalid ${msg.context}`);
91
90
  if (done) done();
92
91
  return;
93
92
  }
94
93
  if (msg.context === "min") {
95
- if (value < node.runtime.max) {
96
- node.runtime.min = value;
97
- node.status({ fill: "green", shape: "dot", text: `min: ${node.runtime.min}` });
94
+ if (value < node.max) {
95
+ node.min = value;
96
+ utils.setStatusOK(node, `min: ${node.min}`);
98
97
  } else {
99
- node.status({ fill: "yellow", shape: "dot", text: `Context update aborted. Payload more than max` });
98
+ utils.setStatusWarn(node, `Context update aborted. Payload more than max`);
100
99
  }
101
100
  } else if (msg.context === "max") {
102
- if (value > node.runtime.min) {
103
- node.runtime.max = value;
104
- node.status({ fill: "green", shape: "dot", text: `max: ${node.runtime.max}` });
101
+ if (value > node.min) {
102
+ node.max = value;
103
+ utils.setStatusOK(node, `max: ${node.max}`);
105
104
  } else {
106
- node.status({ fill: "yellow", shape: "dot", text: `Context update aborted. Payload less than min` });
105
+ utils.setStatusWarn(node, `Context update aborted. Payload less than min`);
107
106
  }
108
107
  } else {
109
- node.status({ fill: "yellow", shape: "ring", text: "unknown context" });
108
+ utils.setStatusWarn(node, "unknown context");
110
109
  if (done) done();
111
110
  return;
112
111
  }
@@ -116,28 +115,30 @@ module.exports = function(RED) {
116
115
 
117
116
  // Validate input payload
118
117
  if (!msg.hasOwnProperty("payload")) {
119
- node.status({ fill: "red", shape: "ring", text: "missing payload" });
118
+ utils.setStatusError(node, "missing payload");
120
119
  if (done) done();
121
120
  return;
122
121
  }
123
122
 
124
- const inputValue = parseFloat(msg.payload);
125
- if (isNaN(inputValue)) {
126
- node.status({ fill: "red", shape: "ring", text: "invalid payload" });
123
+ const numVal = utils.validateNumericPayload(msg.payload);
124
+ if (!numVal.valid) {
125
+ utils.setStatusError(node, numVal.error);
127
126
  if (done) done();
128
127
  return;
129
128
  }
129
+ const inputValue = numVal.value;
130
130
 
131
131
  // Clamp input to [min, max]
132
- const outputValue = Math.min(Math.max(inputValue, node.runtime.min), node.runtime.max);
132
+ const outputValue = Math.min(Math.max(inputValue, node.min), node.max);
133
133
 
134
134
  // Update status and send output
135
135
  msg.payload = outputValue;
136
- node.status({
137
- fill: "blue",
138
- shape: lastOutput === outputValue ? "ring" : "dot",
139
- text: `in: ${inputValue.toFixed(2)}, out: ${outputValue.toFixed(2)}`
140
- });
136
+ const statusText = `in: ${inputValue.toFixed(2)}, out: ${outputValue.toFixed(2)}`;
137
+ if (lastOutput === outputValue) {
138
+ utils.setStatusUnchanged(node, statusText);
139
+ } else {
140
+ utils.setStatusChanged(node, statusText);
141
+ }
141
142
  lastOutput = outputValue;
142
143
  send(msg);
143
144