node-red-contrib-freya-nodes 0.2.1 → 0.2.2

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.
@@ -29,7 +29,7 @@
29
29
  },
30
30
  inputs:1,
31
31
  outputs:2,
32
- outputLabels:["actuator commands", "status"],
32
+ outputLabels:["control (actuators)", "status"],
33
33
  icon: "font-awesome/fa-tint",
34
34
  label: function() {
35
35
  return this.name || "Humidity Controller";
@@ -125,11 +125,13 @@
125
125
 
126
126
  <h3>Outputs</h3>
127
127
  <dl class="message-properties">
128
- <dt>Output 1: Actuator Commands <span class="property-type">object</span></dt>
129
- <dd>Commands for actuators: <code>{humidifier: "on/off", dehumidifier: "on/off"}</code></dd>
130
-
128
+ <dt>Output 1: Control <span class="property-type">object</span></dt>
129
+ <dd><code>msg.payload</code> is <code>{ humidifier: "on"/"off", dehumidifier: "on"/"off" }</code>
130
+ with <code>msg.topic = "actuators"</code>.</dd>
131
+
131
132
  <dt>Output 2: Status <span class="property-type">object</span></dt>
132
- <dd>Controller status including state, readings, safety overrides, and deadband</dd>
133
+ <dd><code>msg.payload</code> contains <code>state</code>, <code>timestamp</code>, and optional
134
+ context fields. <code>msg.topic = "status"</code>. Emitted only on state changes.</dd>
133
135
  </dl>
134
136
 
135
137
  <h3>Control Logic</h3>
@@ -156,4 +158,13 @@
156
158
  <li><strong>Auto Recovery:</strong> Resumes normal control immediately when sensor returns</li>
157
159
  <li><strong>Status Alerts:</strong> Emits configurable severity alerts on watchdog trigger</li>
158
160
  </ul>
161
+
162
+ <h3>Status Messages</h3>
163
+ <p>Status messages are emitted on output 2 only when the state changes:</p>
164
+ <ul>
165
+ <li><strong>idle</strong> — at target or awaiting inputs</li>
166
+ <li><strong>humidifying</strong> — humidifier is active (includes <code>target</code>, <code>reading</code>, <code>deadband</code>, <code>safetyOverride</code>)</li>
167
+ <li><strong>dehumidifying</strong> — dehumidifier is active (includes <code>target</code>, <code>reading</code>, <code>deadband</code>, <code>safetyOverride</code>)</li>
168
+ <li><strong>error</strong> — open-loop mode, watchdog triggered (includes <code>severity</code>, <code>message</code>)</li>
169
+ </ul>
159
170
  </script>
@@ -11,7 +11,22 @@ const humidityController = (RED) => {
11
11
  const state = {
12
12
  currentState: 'idle',
13
13
  safetyOverride: null,
14
- openLoopMode: false
14
+ openLoopMode: false,
15
+ previousStatusState: null
16
+ };
17
+ const emitStatus = (newState, optionalFields) => {
18
+ if (newState === state.previousStatusState) {
19
+ return null;
20
+ }
21
+ state.previousStatusState = newState;
22
+ const statusPayload = {
23
+ state: newState,
24
+ timestamp: Date.now()
25
+ };
26
+ if (optionalFields) {
27
+ Object.assign(statusPayload, optionalFields);
28
+ }
29
+ return { payload: statusPayload, topic: 'status' };
15
30
  };
16
31
  const startWatchdog = () => {
17
32
  if (state.watchdogTimer) {
@@ -19,6 +34,7 @@ const humidityController = (RED) => {
19
34
  }
20
35
  state.watchdogTimer = setTimeout(() => {
21
36
  state.openLoopMode = true;
37
+ state.previousStatusState = 'error';
22
38
  node.send([
23
39
  {
24
40
  payload: {
@@ -29,10 +45,10 @@ const humidityController = (RED) => {
29
45
  },
30
46
  {
31
47
  payload: {
48
+ state: 'error',
49
+ timestamp: Date.now(),
32
50
  severity: node.watchdogSeverity,
33
- message: `No sensor feedback for ${node.watchdogTimeout}s`,
34
- state: 'open-loop',
35
- timestamp: new Date().toISOString()
51
+ message: `No sensor feedback for ${node.watchdogTimeout}s`
36
52
  },
37
53
  topic: 'status'
38
54
  }
@@ -114,6 +130,12 @@ const humidityController = (RED) => {
114
130
  };
115
131
  const _executeControlLoop = () => {
116
132
  const control = calculateControl();
133
+ const statusMsg = emitStatus(control.state, {
134
+ target: state.targetHumidity,
135
+ reading: state.currentHumidity,
136
+ deadband: this.deadband,
137
+ safetyOverride: control.override || null
138
+ });
117
139
  node.send([
118
140
  {
119
141
  payload: {
@@ -122,17 +144,7 @@ const humidityController = (RED) => {
122
144
  },
123
145
  topic: 'actuators'
124
146
  },
125
- {
126
- payload: {
127
- state: control.state,
128
- target: state.targetHumidity,
129
- reading: state.currentHumidity,
130
- safetyOverride: control.override || null,
131
- deadband: this.deadband,
132
- openLoopMode: state.openLoopMode
133
- },
134
- topic: 'status'
135
- }
147
+ statusMsg
136
148
  ]);
137
149
  updateStatus(control);
138
150
  };
@@ -168,6 +180,7 @@ const humidityController = (RED) => {
168
180
  stopWatchdog();
169
181
  });
170
182
  node.status({ fill: 'yellow', shape: 'dot', text: 'awaiting inputs' });
183
+ emitStatus('idle');
171
184
  }
172
185
  RED.nodes.registerType('humidity controller', HumidityControllerNode);
173
186
  };
@@ -30,7 +30,7 @@
30
30
  },
31
31
  inputs:1,
32
32
  outputs:2,
33
- outputLabels:["control", "status"],
33
+ outputLabels:["control (actuators)", "status"],
34
34
  icon: "font-awesome/fa-lightbulb-o",
35
35
  label: function() {
36
36
  return this.name || "Lighting Controller";
@@ -139,8 +139,20 @@
139
139
  </ul>
140
140
 
141
141
  <h3>Outputs</h3>
142
+ <dl class="message-properties">
143
+ <dt>Output 1: Control <span class="property-type">object</span></dt>
144
+ <dd><code>msg.payload</code> contains the lighting command (analog: <code>{ lighting: 0.0–100.0 }</code>,
145
+ digital: <code>{ lighting: "on"/"off" }</code>). <code>msg.topic = "actuators"</code>.</dd>
146
+
147
+ <dt>Output 2: Status <span class="property-type">object</span></dt>
148
+ <dd><code>msg.payload</code> contains <code>state</code>, <code>timestamp</code>, and optional
149
+ context fields. <code>msg.topic = "status"</code>. Emitted on every input.</dd>
150
+ </dl>
151
+
152
+ <h3>Status Messages</h3>
153
+ <p>Status messages are emitted on output 2 on every input, reflecting the current brightness and state:</p>
142
154
  <ul>
143
- <li><strong>Output 1:</strong> Actuator command with formatted lighting value</li>
144
- <li><strong>Output 2:</strong> Status message for the status aggregator</li>
155
+ <li><strong>idle</strong> lighting is off (brightness = 0)</li>
156
+ <li><strong>active</strong> lighting is on (includes <code>brightness</code> %, <code>mode</code>, <code>scheduleActive</code>)</li>
145
157
  </ul>
146
158
  </script>
@@ -9,6 +9,16 @@ const lightingController = (RED) => {
9
9
  this.scheduleTime1 = config.scheduleTime1 || '';
10
10
  this.scheduleTime2 = config.scheduleTime2 || '';
11
11
  this.scheduleMode = config.scheduleMode || 'allowed';
12
+ const buildStatus = (currentState, optionalFields) => {
13
+ const statusPayload = {
14
+ state: currentState,
15
+ timestamp: Date.now()
16
+ };
17
+ if (optionalFields) {
18
+ Object.assign(statusPayload, optionalFields);
19
+ }
20
+ return { payload: statusPayload, topic: 'status' };
21
+ };
12
22
  const isWithinSchedule = () => {
13
23
  if (!this.scheduleTime1 || !this.scheduleTime2) {
14
24
  return true;
@@ -96,9 +106,15 @@ const lightingController = (RED) => {
96
106
  node.status({ fill: 'grey', shape: 'dot', text: statusText });
97
107
  }
98
108
  }
109
+ const currentState = finalValue > 0 ? 'active' : 'idle';
110
+ const statusMsg = buildStatus(currentState, {
111
+ brightness: finalValue,
112
+ mode: this.mode,
113
+ scheduleActive
114
+ });
99
115
  send([
100
116
  { payload: outputPayload, topic: 'actuators' },
101
- { payload: Object.assign(Object.assign({}, outputPayload), { scheduleActive, gain: this.gain, bottomCutOff: this.bottomCutOff, mode: this.mode }), topic: 'status' }
117
+ statusMsg
102
118
  ]);
103
119
  done === null || done === void 0 ? void 0 : done();
104
120
  });
@@ -23,12 +23,13 @@
23
23
  name: {value:""},
24
24
  interval: {value: 60, required: true, validate: RED.validators.number()},
25
25
  duration: {value: 30, required: true, validate: RED.validators.number()},
26
- tickInterval: {value: 60, required: true, validate: function(v) { return RED.validators.number()(v) && parseInt(v) >= 1; }}
26
+ tickInterval: {value: 60, required: true, validate: function(v) { return RED.validators.number()(v) && parseInt(v) >= 1; }},
27
+ nightDisable: {value: false}
27
28
  },
28
29
  inputs:1,
29
- outputs:1,
30
+ outputs:2,
30
31
  inputLabels:["parameter update"],
31
- outputLabels:["actuator command"],
32
+ outputLabels:["control (actuators)", "status"],
32
33
  icon: "font-awesome/fa-tint",
33
34
  label: function() {
34
35
  return this.name || "Precipitation Controller";
@@ -55,6 +56,11 @@
55
56
  <label for="node-input-tickInterval"><i class="fa fa-clock-o"></i> Tick Interval (seconds)</label>
56
57
  <input type="number" id="node-input-tickInterval" placeholder="60" step="1" min="1">
57
58
  </div>
59
+ <hr/>
60
+ <div class="form-row">
61
+ <input type="checkbox" id="node-input-nightDisable" style="display:inline-block; width:auto; vertical-align:top;">
62
+ <label for="node-input-nightDisable" style="width:auto;"><i class="fa fa-moon-o"></i> Disable precipitation at night</label>
63
+ </div>
58
64
  </script>
59
65
 
60
66
  <script type="text/html" data-help-name="precipitation controller">
@@ -64,24 +70,28 @@
64
70
  <h3>Input</h3>
65
71
  <dl class="message-properties">
66
72
  <dt>msg.topic = "interval" <span class="property-type">number</span></dt>
67
- <dd><code>msg.payload</code> sets the precipitation interval in minutes, overriding the configured default</dd>
73
+ <dd><code>msg.payload</code> sets the precipitation interval in milliseconds, overriding the configured default</dd>
68
74
 
69
75
  <dt>msg.topic = "duration" <span class="property-type">number</span></dt>
70
- <dd><code>msg.payload</code> sets the pump duration in seconds, overriding the configured default</dd>
76
+ <dd><code>msg.payload</code> sets the pump duration in milliseconds, overriding the configured default</dd>
71
77
 
72
78
  <dt>msg.topic = "night" <span class="property-type">boolean</span></dt>
73
79
  <dd><code>msg.payload</code> signals nighttime (<code>true</code>) or daytime (<code>false</code>).
80
+ Only effective when the nighttime disable toggle is enabled in the editor.
74
81
  During nighttime no new precipitation events are started. If the pump is already active
75
82
  when night begins, the current cycle finishes normally.</dd>
76
83
  </dl>
77
84
  <p>Messages with unrecognised topics are ignored.</p>
78
85
 
79
- <h3>Output</h3>
86
+ <h3>Outputs</h3>
80
87
  <dl class="message-properties">
81
- <dt>msg.payload <span class="property-type">object</span></dt>
82
- <dd><code>{ pump: "on" }</code> when the pump turns on, <code>{ pump: "off" }</code> when it turns off.
83
- <code>msg.topic</code> is set to <code>"actuators"</code>.
84
- Two messages are sent per precipitation event.</dd>
88
+ <dt>Output 1: Control <span class="property-type">object</span></dt>
89
+ <dd><code>msg.payload</code> is <code>{ pump: "on" }</code> or <code>{ pump: "off" }</code>
90
+ with <code>msg.topic = "actuators"</code>. Two messages are sent per precipitation event.</dd>
91
+
92
+ <dt>Output 2: Status <span class="property-type">object</span></dt>
93
+ <dd><code>msg.payload</code> contains <code>state</code>, <code>timestamp</code>, and optional
94
+ context fields. <code>msg.topic = "status"</code>. Emitted only on state changes.</dd>
85
95
  </dl>
86
96
 
87
97
  <h3>Details</h3>
@@ -89,7 +99,7 @@
89
99
  precipitation is due:</p>
90
100
  <ul>
91
101
  <li>If the pump is already running the tick is skipped (no overlapping cycles).</li>
92
- <li>If nighttime is signalled, the tick is skipped.</li>
102
+ <li>If nighttime disable is enabled and night is signalled, the tick is skipped.</li>
93
103
  <li>If this is the first tick after deploy, or the configured interval has elapsed
94
104
  since the last event, the pump is turned on for the configured duration and then
95
105
  automatically turned off.</li>
@@ -99,14 +109,31 @@
99
109
  <p>On redeploy or shutdown the node clears all timers and forces the pump off
100
110
  to prevent it from getting stuck on.</p>
101
111
 
112
+ <h3>Nighttime Disable</h3>
113
+ <p>When the nighttime disable toggle is enabled, the node tracks a night/day state
114
+ updated via <code>msg.topic = "night"</code>. During nighttime no new precipitation events
115
+ are started. If the pump is already active when night begins, the current cycle is
116
+ allowed to finish normally. When the toggle is off, night messages are ignored and
117
+ precipitation runs 24/7.</p>
118
+
119
+ <h3>Status Messages</h3>
120
+ <p>Status messages are emitted on output 2 only when the state changes:</p>
121
+ <ul>
122
+ <li><strong>idle</strong> — initial state after deploy, no evaluation yet</li>
123
+ <li><strong>active</strong> — pump is on (includes <code>duration</code> in ms)</li>
124
+ <li><strong>waiting</strong> — pump off, counting down (includes <code>remaining</code> in ms)</li>
125
+ <li><strong>paused</strong> — nighttime, precipitation suppressed</li>
126
+ </ul>
127
+
102
128
  <h3>Configuration</h3>
103
129
  <ul>
104
130
  <li><strong>Interval:</strong> Time between precipitation events in minutes (default: 60)</li>
105
131
  <li><strong>Duration:</strong> How long the pump runs per event in seconds (default: 30)</li>
106
132
  <li><strong>Tick Interval:</strong> How often to evaluate whether precipitation is due in seconds (default: 60)</li>
133
+ <li><strong>Disable precipitation at night:</strong> When enabled, precipitation is paused during nighttime</li>
107
134
  </ul>
108
135
 
109
- <h3>Status</h3>
136
+ <h3>Node Status</h3>
110
137
  <ul>
111
138
  <li><strong>idle</strong> (grey/ring) — initial state before first tick</li>
112
139
  <li><strong>pump on</strong> (blue/dot) — pump is running, shows duration</li>
@@ -17,39 +17,67 @@ const precipitationController = (RED) => {
17
17
  this.interval = parseFloat(config.interval) || 60;
18
18
  this.duration = parseFloat(config.duration) || 30;
19
19
  this.tickInterval = parseInt(config.tickInterval) || 60;
20
+ this.nightDisable = config.nightDisable || false;
20
21
  const state = {
21
22
  lastPrecipitation: null,
22
23
  pumpActive: false,
23
24
  intervalMs: (node.interval || 60) * 60000,
24
25
  durationMs: (node.duration || 30) * 1000,
25
- night: false
26
+ night: false,
27
+ previousState: null
28
+ };
29
+ const emitStatus = (newState, optionalFields) => {
30
+ if (newState === state.previousState) {
31
+ return;
32
+ }
33
+ state.previousState = newState;
34
+ const statusPayload = {
35
+ state: newState,
36
+ timestamp: Date.now()
37
+ };
38
+ if (optionalFields) {
39
+ Object.assign(statusPayload, optionalFields);
40
+ }
41
+ node.send([null, { payload: statusPayload, topic: 'status' }]);
26
42
  };
27
43
  const evaluatePrecipitation = () => {
28
44
  if (state.pumpActive) {
29
45
  return;
30
46
  }
31
- if (state.night) {
47
+ if (node.nightDisable && state.night) {
32
48
  node.status({ fill: 'grey', shape: 'ring', text: 'paused (night)' });
49
+ emitStatus('paused');
33
50
  return;
34
51
  }
35
52
  const now = Date.now();
36
53
  if (state.lastPrecipitation === null || (now - state.lastPrecipitation) >= state.intervalMs) {
37
54
  state.pumpActive = true;
38
55
  state.lastPrecipitation = now;
39
- node.send({ payload: { pump: 'on' }, topic: 'actuators' });
40
56
  node.status({ fill: 'blue', shape: 'dot', text: `pump on (${formatDuration(state.durationMs)})` });
57
+ state.previousState = 'active';
58
+ node.send([
59
+ { payload: { pump: 'on' }, topic: 'actuators' },
60
+ { payload: { state: 'active', timestamp: now, duration: state.durationMs }, topic: 'status' }
61
+ ]);
41
62
  state.offTimer = setTimeout(() => {
42
63
  state.pumpActive = false;
43
64
  state.offTimer = undefined;
44
- node.send({ payload: { pump: 'off' }, topic: 'actuators' });
45
- const timeStr = new Date().toLocaleTimeString('en-GB', { hour12: false });
65
+ const offNow = Date.now();
66
+ const timeStr = new Date(offNow).toLocaleTimeString('en-GB', { hour12: false });
46
67
  node.status({ fill: 'grey', shape: 'ring', text: `last: ${timeStr}` });
68
+ const remaining = state.intervalMs - (offNow - (state.lastPrecipitation || offNow));
69
+ state.previousState = 'waiting';
70
+ node.send([
71
+ { payload: { pump: 'off' }, topic: 'actuators' },
72
+ { payload: { state: 'waiting', timestamp: offNow, remaining: Math.max(0, remaining) }, topic: 'status' }
73
+ ]);
47
74
  }, state.durationMs);
48
75
  }
49
76
  else {
50
77
  const elapsed = now - state.lastPrecipitation;
51
78
  const remaining = state.intervalMs - elapsed;
52
79
  node.status({ fill: 'green', shape: 'ring', text: `next in ${formatDuration(remaining)}` });
80
+ emitStatus('waiting', { remaining });
53
81
  }
54
82
  };
55
83
  const startTickInterval = () => {
@@ -63,16 +91,18 @@ const precipitationController = (RED) => {
63
91
  };
64
92
  node.on('input', (msg, send, done) => {
65
93
  if (msg.topic === 'interval' && typeof msg.payload === 'number' && isFinite(msg.payload)) {
66
- state.intervalMs = msg.payload * 60000;
67
- node.log(`Interval updated: ${msg.payload} minutes`);
94
+ state.intervalMs = msg.payload;
95
+ node.log(`Interval updated: ${msg.payload}ms`);
68
96
  }
69
97
  else if (msg.topic === 'duration' && typeof msg.payload === 'number' && isFinite(msg.payload)) {
70
- state.durationMs = msg.payload * 1000;
71
- node.log(`Duration updated: ${msg.payload} seconds`);
98
+ state.durationMs = msg.payload;
99
+ node.log(`Duration updated: ${msg.payload}ms`);
72
100
  }
73
101
  else if (msg.topic === 'night' && typeof msg.payload === 'boolean') {
74
- state.night = msg.payload;
75
- node.log(`Night state updated: ${msg.payload}`);
102
+ if (node.nightDisable) {
103
+ state.night = msg.payload;
104
+ node.log(`Night state updated: ${msg.payload}`);
105
+ }
76
106
  }
77
107
  done === null || done === void 0 ? void 0 : done();
78
108
  });
@@ -87,11 +117,12 @@ const precipitationController = (RED) => {
87
117
  }
88
118
  if (state.pumpActive) {
89
119
  state.pumpActive = false;
90
- node.send({ payload: { pump: 'off' }, topic: 'actuators' });
120
+ node.send([{ payload: { pump: 'off' }, topic: 'actuators' }, null]);
91
121
  }
92
122
  done();
93
123
  });
94
124
  node.status({ fill: 'grey', shape: 'ring', text: 'idle' });
125
+ emitStatus('idle');
95
126
  startTickInterval();
96
127
  }
97
128
  RED.nodes.registerType('precipitation controller', PrecipitationControllerNode);
@@ -29,7 +29,7 @@
29
29
  },
30
30
  inputs:1,
31
31
  outputs:2,
32
- outputLabels:["actuator commands", "status"],
32
+ outputLabels:["control (actuators)", "status"],
33
33
  icon: "font-awesome/fa-thermometer-half",
34
34
  label: function() {
35
35
  return this.name||"Temperature Controller";
@@ -97,10 +97,15 @@
97
97
  </dl>
98
98
 
99
99
  <h3>Outputs</h3>
100
- <ul>
101
- <li><strong>Output 1 (Actuators):</strong> <code>msg.payload</code> (object) - actuator commands: <code>{ heater: "on/off", cooler: "on/off" }</code></li>
102
- <li><strong>Output 2 (Status):</strong> Status information for aggregation</li>
103
- </ul>
100
+ <dl class="message-properties">
101
+ <dt>Output 1: Control <span class="property-type">object</span></dt>
102
+ <dd><code>msg.payload</code> is <code>{ heater: "on"/"off", cooler: "on"/"off" }</code>
103
+ with <code>msg.topic = "actuators"</code>.</dd>
104
+
105
+ <dt>Output 2: Status <span class="property-type">object</span></dt>
106
+ <dd><code>msg.payload</code> contains <code>state</code>, <code>timestamp</code>, and optional
107
+ context fields. <code>msg.topic = "status"</code>. Emitted only on state changes.</dd>
108
+ </dl>
104
109
 
105
110
  <h3>Control Logic</h3>
106
111
  <ul>
@@ -122,4 +127,13 @@
122
127
  <li><strong>Auto Recovery:</strong> Resumes normal control immediately when sensor returns</li>
123
128
  <li><strong>Status Alerts:</strong> Emits configurable severity alerts on watchdog trigger</li>
124
129
  </ul>
130
+
131
+ <h3>Status Messages</h3>
132
+ <p>Status messages are emitted on output 2 only when the state changes:</p>
133
+ <ul>
134
+ <li><strong>idle</strong> — at target or awaiting inputs</li>
135
+ <li><strong>heating</strong> — heater is active (includes <code>target</code>, <code>reading</code>, <code>deadband</code>, <code>safetyOverride</code>)</li>
136
+ <li><strong>cooling</strong> — cooler is active (includes <code>target</code>, <code>reading</code>, <code>deadband</code>, <code>safetyOverride</code>)</li>
137
+ <li><strong>error</strong> — open-loop mode, watchdog triggered (includes <code>severity</code>, <code>message</code>)</li>
138
+ </ul>
125
139
  </script>
@@ -11,7 +11,22 @@ const temperatureController = (RED) => {
11
11
  const state = {
12
12
  currentState: 'idle',
13
13
  safetyOverride: null,
14
- openLoopMode: false
14
+ openLoopMode: false,
15
+ previousStatusState: null
16
+ };
17
+ const emitStatus = (newState, optionalFields) => {
18
+ if (newState === state.previousStatusState) {
19
+ return null;
20
+ }
21
+ state.previousStatusState = newState;
22
+ const statusPayload = {
23
+ state: newState,
24
+ timestamp: Date.now()
25
+ };
26
+ if (optionalFields) {
27
+ Object.assign(statusPayload, optionalFields);
28
+ }
29
+ return { payload: statusPayload, topic: 'status' };
15
30
  };
16
31
  const startWatchdog = () => {
17
32
  if (state.watchdogTimer) {
@@ -19,6 +34,7 @@ const temperatureController = (RED) => {
19
34
  }
20
35
  state.watchdogTimer = setTimeout(() => {
21
36
  state.openLoopMode = true;
37
+ state.previousStatusState = 'error';
22
38
  node.send([
23
39
  {
24
40
  payload: {
@@ -29,10 +45,10 @@ const temperatureController = (RED) => {
29
45
  },
30
46
  {
31
47
  payload: {
48
+ state: 'error',
49
+ timestamp: Date.now(),
32
50
  severity: node.watchdogSeverity,
33
- message: `No sensor feedback for ${node.watchdogTimeout}s`,
34
- state: 'open-loop',
35
- timestamp: new Date().toISOString()
51
+ message: `No sensor feedback for ${node.watchdogTimeout}s`
36
52
  },
37
53
  topic: 'status'
38
54
  }
@@ -114,6 +130,12 @@ const temperatureController = (RED) => {
114
130
  };
115
131
  const _executeControlLoop = () => {
116
132
  const control = calculateControl();
133
+ const statusMsg = emitStatus(control.state, {
134
+ target: state.targetTemperature,
135
+ reading: state.currentTemperature,
136
+ deadband: this.deadband,
137
+ safetyOverride: control.override || null
138
+ });
117
139
  node.send([
118
140
  {
119
141
  payload: {
@@ -122,17 +144,7 @@ const temperatureController = (RED) => {
122
144
  },
123
145
  topic: 'actuators'
124
146
  },
125
- {
126
- payload: {
127
- state: control.state,
128
- target: state.targetTemperature,
129
- reading: state.currentTemperature,
130
- safetyOverride: control.override || null,
131
- deadband: this.deadband,
132
- openLoopMode: state.openLoopMode
133
- },
134
- topic: 'status'
135
- }
147
+ statusMsg
136
148
  ]);
137
149
  updateStatus(control);
138
150
  };
@@ -168,6 +180,7 @@ const temperatureController = (RED) => {
168
180
  stopWatchdog();
169
181
  });
170
182
  node.status({ fill: 'yellow', shape: 'dot', text: 'awaiting inputs' });
183
+ emitStatus('idle');
171
184
  }
172
185
  RED.nodes.registerType('temperature controller', TemperatureControllerNode);
173
186
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-freya-nodes",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Custom nodes for Freya Vivarium Control System",
5
5
  "author": "Sanne 'SpuQ' Santens",
6
6
  "license": "GPL-3.0",