@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
@@ -16,7 +16,7 @@ module.exports = function(RED) {
16
16
  send = send || function() { node.send.apply(node, arguments); };
17
17
 
18
18
  if (!msg) {
19
- node.status({ fill: "red", shape: "ring", text: "invalid message" });
19
+ utils.setStatusError(node, "invalid message");
20
20
  if (done) done();
21
21
  return;
22
22
  }
@@ -27,7 +27,7 @@ module.exports = function(RED) {
27
27
  // Check busy lock
28
28
  if (node.isBusy) {
29
29
  // Update status to let user know they are pushing too fast
30
- node.status({ fill: "yellow", shape: "ring", text: "busy - dropped msg" });
30
+ utils.setStatusBusy(node, "busy - dropped msg");
31
31
  if (done) done();
32
32
  return;
33
33
  }
@@ -82,7 +82,7 @@ module.exports = function(RED) {
82
82
  if (msg.hasOwnProperty("context")) {
83
83
 
84
84
  if (!msg.hasOwnProperty("payload")) {
85
- node.status({ fill: "red", shape: "ring", text: "missing payload" });
85
+ utils.setStatusError(node, "missing payload");
86
86
  if (done) done();
87
87
  return;
88
88
  }
@@ -94,12 +94,12 @@ module.exports = function(RED) {
94
94
  if (config[`in${index}Type`] === "str") {
95
95
  node[`in${index}`] = msg.payload;
96
96
  } else {
97
- node.status({ fill: "red", shape: "ring", text: `Field type is ${config[`in${index}Type`]}` });
97
+ utils.setStatusError(node, `Field type is ${config[`in${index}Type`]}`);
98
98
  if (done) done();
99
99
  return;
100
100
  }
101
101
  } else {
102
- node.status({ fill: "red", shape: "ring", text: `invalid input index ${index || "NaN"}` });
102
+ utils.setStatusError(node, `invalid input index ${index || "NaN"}`);
103
103
  if (done) done();
104
104
  return;
105
105
  }
@@ -107,7 +107,7 @@ module.exports = function(RED) {
107
107
  }
108
108
 
109
109
  const output = { payload: `${node.in1}${node.in2}${node.in3}${node.in4}` };
110
- node.status({ fill: "blue", shape: "dot", text: `${ output.payload }` });
110
+ utils.setStatusOK(node, `${ output.payload }`);
111
111
  send(output);
112
112
 
113
113
  if (done) done();
@@ -1,28 +1,25 @@
1
1
  module.exports = function(RED) {
2
+ const utils = require('./utils')(RED);
3
+
2
4
  function SubtractBlockNode(config) {
3
5
  RED.nodes.createNode(this, config);
4
6
 
5
7
  const node = this;
6
8
 
7
9
  // Initialize runtime state
8
- node.runtime = {
9
- name: config.name,
10
- slots: parseInt(config.slots),
11
- inputs: Array(parseInt(config.slots)).fill(0),
12
- lastResult: null
13
- };
10
+ // Initialize state
11
+ node.name = config.name;
12
+ node.slots = parseInt(config.slots);
13
+ node.inputs = Array(parseInt(config.slots)).fill(0);
14
+ node.lastResult = null;
14
15
 
15
16
  // Validate initial config
16
- if (isNaN(node.runtime.slots) || node.runtime.slots < 1) {
17
- node.runtime.slots = 2;
18
- node.runtime.inputs = Array(2).fill(0);
19
- node.status({ fill: "red", shape: "ring", text: "invalid slots, using 2" });
17
+ if (isNaN(node.slots) || node.slots < 1) {
18
+ node.slots = 2;
19
+ node.inputs = Array(2).fill(0);
20
+ utils.setStatusError(node, "invalid slots, using 2");
20
21
  } else {
21
- node.status({
22
- fill: "green",
23
- shape: "dot",
24
- text: `name: ${node.runtime.name}, slots: ${node.runtime.slots}`
25
- });
22
+ utils.setStatusOK(node, `name: ${node.name}, slots: ${node.slots}`);
26
23
  }
27
24
 
28
25
  node.on("input", function(msg, send, done) {
@@ -30,65 +27,72 @@ module.exports = function(RED) {
30
27
 
31
28
  // Guard against invalid msg
32
29
  if (!msg) {
33
- node.status({ fill: "red", shape: "ring", text: "invalid message" });
30
+ utils.setStatusError(node, "invalid message");
34
31
  if (done) done();
35
32
  return;
36
33
  }
37
34
 
38
35
  // Check for missing context or payload
39
36
  if (!msg.hasOwnProperty("context")) {
40
- node.status({ fill: "red", shape: "ring", text: "missing context" });
37
+ utils.setStatusError(node, "missing context");
41
38
  if (done) done();
42
39
  return;
43
40
  }
44
41
 
45
42
  if (!msg.hasOwnProperty("payload")) {
46
- node.status({ fill: "red", shape: "ring", text: "missing payload" });
43
+ utils.setStatusError(node, "missing payload");
47
44
  if (done) done();
48
45
  return;
49
46
  }
50
47
 
51
48
  // Handle configuration messages
52
49
  if (msg.context === "reset") {
53
- if (typeof msg.payload !== "boolean") {
54
- node.status({ fill: "red", shape: "ring", text: "invalid reset" });
50
+ const boolVal = utils.validateBoolean(msg.payload);
51
+ if (!boolVal.valid) {
52
+ utils.setStatusError(node, boolVal.error);
55
53
  if (done) done();
56
54
  return;
57
55
  }
58
- if (msg.payload === true) {
59
- node.runtime.inputs = Array(node.runtime.slots).fill(0);
60
- node.runtime.lastResult = null;
61
- node.status({ fill: "green", shape: "dot", text: "state reset" });
56
+ if (boolVal.value === true) {
57
+ node.inputs = Array(node.slots).fill(0);
58
+ node.lastResult = null;
59
+ utils.setStatusOK(node, "state reset");
62
60
  if (done) done();
63
61
  return;
64
62
  }
65
63
  } else if (msg.context.startsWith("in")) {
66
- let slotIndex = parseInt(msg.context.slice(2)) - 1;
67
- if (isNaN(slotIndex) || slotIndex < 0 || slotIndex >= node.runtime.slots) {
68
- node.status({ fill: "red", shape: "ring", text: `invalid input slot ${msg.context}` });
64
+ const slotVal = utils.validateSlotIndex(msg.context, node.slots);
65
+ if (!slotVal.valid) {
66
+ utils.setStatusError(node, slotVal.error);
69
67
  if (done) done();
70
68
  return;
71
69
  }
70
+ const slotIndex = slotVal.index - 1;
72
71
  let newValue = parseFloat(msg.payload);
73
72
  if (isNaN(newValue)) {
74
- node.status({ fill: "red", shape: "ring", text: "invalid input" });
73
+ utils.setStatusError(node, "invalid input");
75
74
  if (done) done();
76
75
  return;
77
76
  }
78
- node.runtime.inputs[slotIndex] = newValue;
77
+ node.inputs[slotIndex] = newValue;
79
78
 
80
79
  // Calculate subtraction
81
- const result = node.runtime.inputs.reduce((acc, val, idx) => idx === 0 ? val : acc - val, 0);
82
- const isUnchanged = result === node.runtime.lastResult;
83
- node.status({ fill: "blue", shape: isUnchanged ? "ring" : "dot", text: `${msg.context}: ${newValue.toFixed(2)}, diff: ${result.toFixed(2)}` });
80
+ const result = node.inputs.reduce((acc, val, idx) => idx === 0 ? val : acc - val, 0);
81
+ const isUnchanged = result === node.lastResult;
82
+ const statusText = `${msg.context}: ${newValue.toFixed(2)}, diff: ${result.toFixed(2)}`;
83
+ if (isUnchanged) {
84
+ utils.setStatusUnchanged(node, statusText);
85
+ } else {
86
+ utils.setStatusChanged(node, statusText);
87
+ }
84
88
 
85
- node.runtime.lastResult = result;
89
+ node.lastResult = result;
86
90
  send({ payload: result });
87
91
 
88
92
  if (done) done();
89
93
  return;
90
94
  } else {
91
- node.status({ fill: "yellow", shape: "ring", text: "unknown context" });
95
+ utils.setStatusWarn(node, "unknown context");
92
96
  if (done) done();
93
97
  return;
94
98
  }
@@ -1,51 +1,51 @@
1
1
  module.exports = function(RED) {
2
+ const utils = require('./utils')(RED);
2
3
  function ThermistorBlockNode(config) {
3
4
  RED.nodes.createNode(this, config);
4
5
  const node = this;
5
6
  const context = this.context();
6
7
 
7
8
  // Initialize runtime state
8
- node.runtime = {
9
- name: config.name,
10
- R_fixed: parseFloat(config.R_fixed),
11
- Vsupply: parseFloat(config.Vsupply),
12
- Vref: parseFloat(config.Vref),
13
- ADC_max: parseFloat(config.ADC_max),
14
- lastVoltage: context.get("lastVoltage"),
15
- lastResistance: context.get("lastResistance")
16
- };
9
+ // Initialize state
10
+ node.name = config.name;
11
+ node.R_fixed = parseFloat(config.R_fixed);
12
+ node.Vsupply = parseFloat(config.Vsupply);
13
+ node.Vref = parseFloat(config.Vref);
14
+ node.ADC_max = parseFloat(config.ADC_max);
15
+ node.lastVoltage = context.get("lastVoltage");
16
+ node.lastResistance = context.get("lastResistance");
17
17
 
18
18
  // Validate configuration
19
- if (isNaN(node.runtime.R_fixed) || node.runtime.R_fixed <= 0) {
20
- node.status({ fill: "red", shape: "ring", text: "invalid r_fixed" });
21
- node.warn(`Invalid configuration: r_fixed=${node.runtime.R_fixed}`);
19
+ if (isNaN(node.R_fixed) || node.R_fixed <= 0) {
20
+ utils.setStatusError(node, "invalid r_fixed");
21
+ node.warn(`Invalid configuration: r_fixed=${node.R_fixed}`);
22
22
  return;
23
23
  }
24
- if (isNaN(node.runtime.Vsupply) || node.runtime.Vsupply <= 0) {
25
- node.status({ fill: "red", shape: "ring", text: "invalid vsupply" });
26
- node.warn(`Invalid configuration: vsupply=${node.runtime.Vsupply}`);
24
+ if (isNaN(node.Vsupply) || node.Vsupply <= 0) {
25
+ utils.setStatusError(node, "invalid vsupply");
26
+ node.warn(`Invalid configuration: vsupply=${node.Vsupply}`);
27
27
  return;
28
28
  }
29
- if (isNaN(node.runtime.Vref) || node.runtime.Vref <= 0) {
30
- node.status({ fill: "red", shape: "ring", text: "invalid vref" });
31
- node.warn(`Invalid configuration: vref=${node.runtime.Vref}`);
29
+ if (isNaN(node.Vref) || node.Vref <= 0) {
30
+ utils.setStatusError(node, "invalid vref");
31
+ node.warn(`Invalid configuration: vref=${node.Vref}`);
32
32
  return;
33
33
  }
34
- if (isNaN(node.runtime.ADC_max) || node.runtime.ADC_max <= 0) {
35
- node.status({ fill: "red", shape: "ring", text: "invalid adc_max" });
36
- node.warn(`Invalid configuration: adc_max=${node.runtime.ADC_max}`);
34
+ if (isNaN(node.ADC_max) || node.ADC_max <= 0) {
35
+ utils.setStatusError(node, "invalid adc_max");
36
+ node.warn(`Invalid configuration: adc_max=${node.ADC_max}`);
37
37
  return;
38
38
  }
39
39
 
40
40
  // Set initial status
41
- node.status({ fill: "green", shape: "dot", text: `r_fixed: ${node.runtime.R_fixed}, vsupply: ${node.runtime.Vsupply}` });
41
+ utils.setStatusOK(node, `r_fixed: ${node.R_fixed}, vsupply: ${node.Vsupply}`);
42
42
 
43
43
  node.on("input", function(msg, send, done) {
44
44
  send = send || function() { node.send.apply(node, arguments); };
45
45
 
46
46
  // Validate input
47
47
  if (!msg || typeof msg !== "object") {
48
- node.status({ fill: "red", shape: "ring", text: "missing message" });
48
+ utils.setStatusError(node, "missing message");
49
49
  node.warn(`Missing message`);
50
50
  if (done) done();
51
51
  return;
@@ -54,7 +54,7 @@ module.exports = function(RED) {
54
54
  let inputArray;
55
55
  if (Buffer.isBuffer(msg.payload)) {
56
56
  if (msg.payload.length !== 2) {
57
- node.status({ fill: "red", shape: "ring", text: "invalid input: expected 2-byte buffer" });
57
+ utils.setStatusError(node, "invalid input: expected 2-byte buffer");
58
58
  node.warn(`Invalid input: expected 2-byte buffer, got ${JSON.stringify(msg.payload)}`);
59
59
  if (done) done();
60
60
  return;
@@ -63,7 +63,7 @@ module.exports = function(RED) {
63
63
  } else if (typeof msg.payload === "object" && msg.payload.type === "Buffer" && Array.isArray(msg.payload.data) && msg.payload.data.length === 2) {
64
64
  inputArray = msg.payload.data;
65
65
  if (typeof inputArray[0] !== "number" || typeof inputArray[1] !== "number") {
66
- node.status({ fill: "red", shape: "ring", text: "invalid input: expected numeric [highByte, lowByte]" });
66
+ utils.setStatusError(node, "invalid input: expected numeric [highByte, lowByte]");
67
67
  node.warn(`Invalid input: expected numeric [highByte, lowByte], got ${JSON.stringify(msg.payload)}`);
68
68
  if (done) done();
69
69
  return;
@@ -71,7 +71,7 @@ module.exports = function(RED) {
71
71
  } else if (Array.isArray(msg.payload) && msg.payload.length === 2 && typeof msg.payload[0] === "number" && typeof msg.payload[1] === "number") {
72
72
  inputArray = msg.payload;
73
73
  } else {
74
- node.status({ fill: "red", shape: "ring", text: "invalid input: expected [highByte, lowByte] or 2-byte buffer" });
74
+ utils.setStatusError(node, "invalid input: expected [highByte, lowByte] or 2-byte buffer");
75
75
  node.warn(`Invalid input: expected [highByte, lowByte] or 2-byte buffer, got ${JSON.stringify(msg.payload)}`);
76
76
  if (done) done();
77
77
  return;
@@ -80,43 +80,43 @@ module.exports = function(RED) {
80
80
  try {
81
81
  // Calculate raw 16-bit value
82
82
  const raw = (inputArray[0] << 8) | inputArray[1];
83
- if (raw < 0 || raw > node.runtime.ADC_max) {
84
- node.status({ fill: "red", shape: "ring", text: "raw value out of range" });
85
- node.warn(`Raw value ${raw} out of range [0, ${node.runtime.ADC_max}]`);
83
+ if (raw < 0 || raw > node.ADC_max) {
84
+ utils.setStatusError(node, "raw value out of range");
85
+ node.warn(`Raw value ${raw} out of range [0, ${node.ADC_max}]`);
86
86
  if (done) done();
87
87
  return;
88
88
  }
89
89
 
90
90
  // Calculate voltage
91
- const voltage = (raw * node.runtime.Vref) / node.runtime.ADC_max;
92
- if (voltage >= node.runtime.Vsupply || voltage <= 0) {
93
- node.status({ fill: "red", shape: "ring", text: "voltage out of range" });
94
- node.warn(`Voltage ${voltage} out of range (0, ${node.runtime.Vsupply})`);
91
+ const voltage = (raw * node.Vref) / node.ADC_max;
92
+ if (voltage >= node.Vsupply || voltage <= 0) {
93
+ utils.setStatusError(node, "voltage out of range");
94
+ node.warn(`Voltage ${voltage} out of range (0, ${node.Vsupply})`);
95
95
  if (done) done();
96
96
  return;
97
97
  }
98
98
 
99
99
  // Calculate thermistor resistance
100
- const R_thermistor = node.runtime.R_fixed * (voltage / (node.runtime.Vsupply - voltage));
100
+ const R_thermistor = node.R_fixed * (voltage / (node.Vsupply - voltage));
101
101
  if (isNaN(R_thermistor) || R_thermistor < 0) {
102
- node.status({ fill: "red", shape: "ring", text: "invalid resistance" });
102
+ utils.setStatusError(node, "invalid resistance");
103
103
  node.warn(`Invalid resistance ${R_thermistor}`);
104
104
  if (done) done();
105
105
  return;
106
106
  }
107
107
 
108
108
  // Check if outputs have changed
109
- const isUnchanged = voltage === node.runtime.lastVoltage && R_thermistor === node.runtime.lastResistance;
110
- node.status({
111
- fill: "blue",
112
- shape: isUnchanged ? "ring" : "dot",
113
- text: `in: ${raw}, out: ${voltage.toFixed(2)}, ${R_thermistor.toFixed(2)}`
114
- });
109
+ const isUnchanged = voltage === node.lastVoltage && R_thermistor === node.lastResistance;
110
+ if (isUnchanged) {
111
+ utils.setStatusUnchanged(node, `in: ${raw}, out: ${voltage.toFixed(2)}, ${R_thermistor.toFixed(2)}`);
112
+ } else {
113
+ utils.setStatusChanged(node, `in: ${raw}, out: ${voltage.toFixed(2)}, ${R_thermistor.toFixed(2)}`);
114
+ }
115
115
 
116
116
  if (!isUnchanged) {
117
117
  // Update context and runtime
118
- node.runtime.lastVoltage = voltage;
119
- node.runtime.lastResistance = R_thermistor;
118
+ node.lastVoltage = voltage;
119
+ node.lastResistance = R_thermistor;
120
120
  context.set("lastVoltage", voltage);
121
121
  context.set("lastResistance", R_thermistor);
122
122
 
@@ -128,7 +128,7 @@ module.exports = function(RED) {
128
128
  }
129
129
 
130
130
  } catch (error) {
131
- node.status({ fill: "red", shape: "ring", text: "calculation error" });
131
+ utils.setStatusError(node, "calculation error");
132
132
  node.warn(`Calculation error: ${error.message}`);
133
133
  if (done) done(error);
134
134
  return;
@@ -1,19 +1,19 @@
1
1
  module.exports = function(RED) {
2
+ const utils = require('./utils')(RED);
2
3
  function TickTockBlockNode(config) {
3
4
  RED.nodes.createNode(this, config);
4
5
  const node = this;
5
6
 
6
7
  // Initialize runtime state
7
- node.runtime = {
8
- name: config.name,
9
- period: parseFloat(config.period),
10
- state: true
11
- };
8
+ // Initialize state
9
+ node.name = config.name;
10
+ node.period = parseFloat(config.period);
11
+ node.state = true;
12
12
 
13
13
  // Validate initial config
14
- if (isNaN(node.runtime.period) || node.runtime.period <= 0 || !isFinite(node.runtime.period)) {
15
- node.runtime.period = 10;
16
- node.status({ fill: "red", shape: "ring", text: "invalid period" });
14
+ if (isNaN(node.period) || node.period <= 0 || !isFinite(node.period)) {
15
+ node.period = 10;
16
+ utils.setStatusError(node, "invalid period");
17
17
  }
18
18
 
19
19
  let intervalId = null;
@@ -23,7 +23,7 @@ module.exports = function(RED) {
23
23
 
24
24
  // Guard against invalid message
25
25
  if (!msg) {
26
- node.status({ fill: "red", shape: "ring", text: "invalid message" });
26
+ utils.setStatusError(node, "invalid message");
27
27
  if (done) done();
28
28
  return;
29
29
  }
@@ -31,12 +31,12 @@ module.exports = function(RED) {
31
31
  // Handle context updates
32
32
  if (msg.hasOwnProperty("context")) {
33
33
  if (!msg.hasOwnProperty("payload")) {
34
- node.status({ fill: "red", shape: "ring", text: `missing payload for ${msg.context}` });
34
+ utils.setStatusError(node, `missing payload for ${msg.context}`);
35
35
  if (done) done();
36
36
  return;
37
37
  }
38
38
  if (typeof msg.context !== "string") {
39
- node.status({ fill: "red", shape: "ring", text: "invalid context" });
39
+ utils.setStatusError(node, "invalid context");
40
40
  if (done) done();
41
41
  return;
42
42
  }
@@ -44,50 +44,50 @@ module.exports = function(RED) {
44
44
  case "period":
45
45
  const value = parseFloat(msg.payload);
46
46
  if (isNaN(value) || value <= 0 || !isFinite(value)) {
47
- node.status({ fill: "red", shape: "ring", text: "invalid period" });
47
+ utils.setStatusError(node, "invalid period");
48
48
  if (done) done();
49
49
  return;
50
50
  }
51
- node.runtime.period = value;
52
- node.status({ fill: "green", shape: "dot", text: `period: ${node.runtime.period.toFixed(2)}` });
51
+ node.period = value;
52
+ utils.setStatusOK(node, `period: ${node.period.toFixed(2)}`);
53
53
  if (intervalId) {
54
54
  clearInterval(intervalId);
55
- node.runtime.state = true;
56
- const halfPeriodMs = (node.runtime.period * 1000) / 2;
57
- send({ payload: node.runtime.state });
58
- node.status({ fill: "blue", shape: "dot", text: `out: ${node.runtime.state}` });
55
+ node.state = true;
56
+ const halfPeriodMs = (node.period * 1000) / 2;
57
+ send({ payload: node.state });
58
+ utils.setStatusChanged(node, `out: ${node.state}`);
59
59
  intervalId = setInterval(() => {
60
- node.runtime.state = !node.runtime.state;
61
- send({ payload: node.runtime.state });
62
- node.status({ fill: "blue", shape: "dot", text: `out: ${node.runtime.state}` });
60
+ node.state = !node.state;
61
+ send({ payload: node.state });
62
+ utils.setStatusChanged(node, `out: ${node.state}`);
63
63
  }, halfPeriodMs);
64
64
  }
65
65
  break;
66
66
  case "command":
67
67
  if (typeof msg.payload !== "string" || !["start", "stop"].includes(msg.payload)) {
68
- node.status({ fill: "red", shape: "ring", text: "invalid command" });
68
+ utils.setStatusError(node, "invalid command");
69
69
  if (done) done();
70
70
  return;
71
71
  }
72
72
  if (msg.payload === "start" && !intervalId) {
73
- node.runtime.state = true;
74
- const halfPeriodMs = (node.runtime.period * 1000) / 2;
75
- send({ payload: node.runtime.state });
76
- node.status({ fill: "blue", shape: "dot", text: `out: ${node.runtime.state}` });
73
+ node.state = true;
74
+ const halfPeriodMs = (node.period * 1000) / 2;
75
+ send({ payload: node.state });
76
+ utils.setStatusChanged(node, `out: ${node.state}`);
77
77
  intervalId = setInterval(() => {
78
- node.runtime.state = !node.runtime.state;
79
- send({ payload: node.runtime.state });
80
- node.status({ fill: "blue", shape: "dot", text: `out: ${node.runtime.state}` });
78
+ node.state = !node.state;
79
+ send({ payload: node.state });
80
+ utils.setStatusChanged(node, `out: ${node.state}`);
81
81
  }, halfPeriodMs);
82
- node.status({ fill: "green", shape: "dot", text: `started, period: ${node.runtime.period.toFixed(2)}` });
82
+ utils.setStatusOK(node, `started, period: ${node.period.toFixed(2)}`);
83
83
  } else if (msg.payload === "stop" && intervalId) {
84
84
  clearInterval(intervalId);
85
85
  intervalId = null;
86
- node.status({ fill: "yellow", shape: "dot", text: "stopped" });
86
+ utils.setStatusWarn(node, "stopped");
87
87
  }
88
88
  break;
89
89
  default:
90
- node.status({ fill: "yellow", shape: "ring", text: "unknown context" });
90
+ utils.setStatusWarn(node, "unknown context");
91
91
  if (done) done("Unknown context");
92
92
  return;
93
93
  }
@@ -1,19 +1,19 @@
1
1
  module.exports = function(RED) {
2
+ const utils = require('./utils')(RED);
2
3
  function TimeSequenceBlockNode(config) {
3
4
  RED.nodes.createNode(this, config);
4
5
  const node = this;
5
6
 
6
7
  // Initialize runtime state
7
- node.runtime = {
8
- name: config.name,
9
- delay: parseFloat(config.delay),
10
- stage: 0
11
- };
8
+ // Initialize state
9
+ node.name = config.name;
10
+ node.delay = parseFloat(config.delay);
11
+ node.stage = 0;
12
12
 
13
13
  // Validate initial config
14
- if (isNaN(node.runtime.delay) || node.runtime.delay < 0 || !isFinite(node.runtime.delay)) {
15
- node.runtime.delay = 5000;
16
- node.status({ fill: "red", shape: "ring", text: "invalid delay" });
14
+ if (isNaN(node.delay) || node.delay < 0 || !isFinite(node.delay)) {
15
+ node.delay = 5000;
16
+ utils.setStatusError(node, "invalid delay");
17
17
  }
18
18
 
19
19
  let timer = null;
@@ -23,7 +23,7 @@ module.exports = function(RED) {
23
23
 
24
24
  // Guard against invalid message
25
25
  if (!msg) {
26
- node.status({ fill: "red", shape: "ring", text: "invalid message" });
26
+ utils.setStatusError(node, "invalid message");
27
27
  if (done) done();
28
28
  return;
29
29
  }
@@ -31,39 +31,35 @@ module.exports = function(RED) {
31
31
  // Handle context updates
32
32
  if (msg.hasOwnProperty("context")) {
33
33
  if (typeof msg.context !== "string") {
34
- node.status({ fill: "red", shape: "ring", text: "invalid context" });
34
+ utils.setStatusError(node, "invalid context");
35
35
  if (done) done();
36
36
  return;
37
37
  }
38
38
  switch (msg.context) {
39
39
  case "delay":
40
40
  if (!msg.hasOwnProperty("payload")) {
41
- node.status({ fill: "red", shape: "ring", text: `missing payload for ${msg.context}` });
41
+ utils.setStatusError(node, `missing payload for ${msg.context}`);
42
42
  if (done) done();
43
43
  return;
44
44
  }
45
45
  const delayValue = parseFloat(msg.payload);
46
46
  if (isNaN(delayValue) || delayValue < 0 || !isFinite(delayValue)) {
47
- node.status({ fill: "red", shape: "ring", text: "invalid delay" });
47
+ utils.setStatusError(node, "invalid delay");
48
48
  if (done) done();
49
49
  return;
50
50
  }
51
- node.runtime.delay = delayValue;
52
- node.status({
53
- fill: "green",
54
- shape: "dot",
55
- text: `delay: ${node.runtime.delay.toFixed(2)} ms`
56
- });
51
+ node.delay = delayValue;
52
+ utils.setStatusOK(node, `delay: ${node.delay.toFixed(2)} ms`);
57
53
  if (done) done();
58
54
  return;
59
55
  case "reset":
60
56
  if (!msg.hasOwnProperty("payload")) {
61
- node.status({ fill: "red", shape: "ring", text: `missing payload for ${msg.context}` });
57
+ utils.setStatusError(node, `missing payload for ${msg.context}`);
62
58
  if (done) done();
63
59
  return;
64
60
  }
65
61
  if (typeof msg.payload !== "boolean" || !msg.payload) {
66
- node.status({ fill: "red", shape: "ring", text: "invalid reset" });
62
+ utils.setStatusError(node, "invalid reset");
67
63
  if (done) done();
68
64
  return;
69
65
  }
@@ -71,13 +67,9 @@ module.exports = function(RED) {
71
67
  clearTimeout(timer);
72
68
  timer = null;
73
69
  }
74
- node.runtime.stage = 0;
70
+ node.stage = 0;
75
71
  const resetMsg = { payload: false };
76
- node.status({
77
- fill: "green",
78
- shape: "dot",
79
- text: "state reset"
80
- });
72
+ utils.setStatusOK(node, "state reset");
81
73
  send([resetMsg, resetMsg, resetMsg, resetMsg]);
82
74
  if (done) done();
83
75
  return;
@@ -88,41 +80,37 @@ module.exports = function(RED) {
88
80
 
89
81
  // Validate input
90
82
  if (!msg.hasOwnProperty("payload")) {
91
- node.status({ fill: "red", shape: "ring", text: "missing input" });
83
+ utils.setStatusError(node, "missing input");
92
84
  if (done) done();
93
85
  return;
94
86
  }
95
87
 
96
88
  // Process input
97
- if (node.runtime.stage !== 0) {
98
- node.status({ fill: "yellow", shape: "ring", text: "sequence already running" });
89
+ if (node.stage !== 0) {
90
+ utils.setStatusWarn(node, "sequence already running");
99
91
  if (done) done();
100
92
  return;
101
93
  }
102
94
 
103
95
  // Start new sequence
104
- node.runtime.stage = 1;
96
+ node.stage = 1;
105
97
  const cloneMsg = RED.util.cloneMessage(msg);
106
98
 
107
99
  // Output sequence
108
100
  const sendNextOutput = () => {
109
- if (node.runtime.stage === 0) return;
101
+ if (node.stage === 0) return;
110
102
  const stageLabels = ["stage 1", "stage 2", "stage 3", "stage 4"];
111
103
  const outputs = [null, null, null, null];
112
- cloneMsg.stage = node.runtime.stage;
113
- outputs[node.runtime.stage - 1] = cloneMsg;
114
- node.status({
115
- fill: "blue",
116
- shape: "dot",
117
- text: `stage: ${stageLabels[node.runtime.stage - 1]}, in: ${JSON.stringify(cloneMsg.payload).slice(0, 20)}`
118
- });
104
+ cloneMsg.stage = node.stage;
105
+ outputs[node.stage - 1] = cloneMsg;
106
+ utils.setStatusOK(node, `stage: ${stageLabels[node.stage - 1]}, in: ${JSON.stringify(cloneMsg.payload).slice(0, 20)}`);
119
107
  send(outputs);
120
108
 
121
- node.runtime.stage++;
122
- if (node.runtime.stage <= 4) {
123
- timer = setTimeout(sendNextOutput, node.runtime.delay);
109
+ node.stage++;
110
+ if (node.stage <= 4) {
111
+ timer = setTimeout(sendNextOutput, node.delay);
124
112
  } else {
125
- node.runtime.stage = 0;
113
+ node.stage = 0;
126
114
  timer = null;
127
115
  }
128
116
  };