smart-nodes 0.5.0 → 0.5.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.
- package/CHANGELOG.md +9 -1
- package/forwarder/forwarder.js +7 -4
- package/forwarder/locales/de-DE/forwarder.html +2 -2
- package/forwarder/locales/en-US/forwarder.html +2 -2
- package/light/light.js +0 -1
- package/mixing-valve/locales/de-DE/mixing-valve.html +8 -2
- package/mixing-valve/locales/de-DE/mixing-valve.json +2 -0
- package/mixing-valve/locales/en-US/mixing-valve.html +8 -2
- package/mixing-valve/locales/en-US/mixing-valve.json +2 -0
- package/mixing-valve/mixing-valve.html +56 -38
- package/mixing-valve/mixing-valve.js +119 -33
- package/mode-selector/locales/de-DE/mode-selector.json +1 -1
- package/mode-selector/locales/en-US/mode-selector.json +1 -1
- package/mode-selector/mode-selector.html +7 -7
- package/package.json +1 -1
- package/scheduler/locales/de-DE/scheduler.html +2 -2
- package/scheduler/locales/en-US/scheduler.html +2 -2
- package/scheduler/scheduler.js +6 -3
- package/statistic/locales/de-DE/statistic.html +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -209,4 +209,12 @@
|
|
|
209
209
|
- Mixing valve can now output as percentage directly or with open/close impulses.
|
|
210
210
|
- Mixing valve tries to guess the best position, when getting enabled.
|
|
211
211
|
- Added debug topic to all nodes, to see current values.
|
|
212
|
-
- Added new node "mode-selector".
|
|
212
|
+
- Added new node "mode-selector".
|
|
213
|
+
|
|
214
|
+
## Version 0.5.1:
|
|
215
|
+
|
|
216
|
+
- To be more compatible, topic "set" is also possible instead of "set_state". Same for "set_inverted" instead of "set_state_inverted".
|
|
217
|
+
|
|
218
|
+
## Version 0.5.2:
|
|
219
|
+
|
|
220
|
+
- Added alarm mode to mixing-valve.
|
package/forwarder/forwarder.js
CHANGED
|
@@ -81,16 +81,19 @@ module.exports = function (RED)
|
|
|
81
81
|
{
|
|
82
82
|
let real_topic = helper.getTopicName(msg.topic);
|
|
83
83
|
|
|
84
|
-
if (real_topic
|
|
84
|
+
if (real_topic.startsWith("set_state"))
|
|
85
|
+
real_topic = real_topic.replace("set_state", "set");
|
|
86
|
+
|
|
87
|
+
if (real_topic == "set_inverted")
|
|
85
88
|
{
|
|
86
|
-
real_topic = "
|
|
89
|
+
real_topic = "set";
|
|
87
90
|
msg.payload = !msg.payload;
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
let new_state = null;
|
|
91
|
-
if (real_topic == "enable" || (real_topic == "
|
|
94
|
+
if (real_topic == "enable" || (real_topic == "set" && msg.payload))
|
|
92
95
|
new_state = true;
|
|
93
|
-
else if (real_topic == "disable" || (real_topic == "
|
|
96
|
+
else if (real_topic == "disable" || (real_topic == "set" && !msg.payload))
|
|
94
97
|
new_state = false;
|
|
95
98
|
|
|
96
99
|
switch (real_topic)
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
<td>Deaktiviert das Weiterleiten.</td>
|
|
28
28
|
</tr>
|
|
29
29
|
<tr>
|
|
30
|
-
<td><code>set_state</code></td>
|
|
30
|
+
<td><code>set</code> oder <code>set_state</code></td>
|
|
31
31
|
<td>Aktiviert das Weiterleiten, wenn <code>msg.payload = true</code> oder deaktiviert das Weiterleiten, wenn <code>msg.payload = false</code>.</td>
|
|
32
32
|
</tr>
|
|
33
33
|
<tr>
|
|
34
|
-
<td><code>set_state_inverted</code></td>
|
|
34
|
+
<td><code>set_inverted</code> oder <code>set_state_inverted</code></td>
|
|
35
35
|
<td>Aktiviert das Weiterleiten, wenn <code>msg.payload = false</code> oder deaktiviert das Weiterleiten, wenn <code>msg.payload = true</code>.</td>
|
|
36
36
|
</tr>
|
|
37
37
|
</tbody>
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
<td>Disables forwarding.</td>
|
|
28
28
|
</tr>
|
|
29
29
|
<tr>
|
|
30
|
-
<td><code>set_state</code></td>
|
|
30
|
+
<td><code>set</code> or <code>set_state</code></td>
|
|
31
31
|
<td>Enables forwarding if <code>msg.payload = true</code> or disables forwarding if <code>msg.payload = false</code>.</td>
|
|
32
32
|
</tr>
|
|
33
33
|
<tr>
|
|
34
|
-
<td><code>set_state_inverted</code></td>
|
|
34
|
+
<td><code>set_inverted</code> or <code>set_state_inverted</code></td>
|
|
35
35
|
<td>Enables forwarding if <code>msg.payload = false</code> or disables forwarding if <code>msg.payload = true</code>.</td>
|
|
36
36
|
</tr>
|
|
37
37
|
</tbody>
|
package/light/light.js
CHANGED
|
@@ -34,11 +34,11 @@
|
|
|
34
34
|
<td>Deaktiviert die Mischeransteuerung und fährt die Position an, die als Aus-Modus festgelegt ist.</td>
|
|
35
35
|
</tr>
|
|
36
36
|
<tr>
|
|
37
|
-
<td><code>set_state</code></td>
|
|
37
|
+
<td><code>set</code> oder <code>set_state</code></td>
|
|
38
38
|
<td>Aktiviert den Mischer, wenn <code>msg.payload = true</code> oder deaktiviert ihn, wenn <code>msg.payload = false</code>.</td>
|
|
39
39
|
</tr>
|
|
40
40
|
<tr>
|
|
41
|
-
<td><code>set_state_inverted</code></td>
|
|
41
|
+
<td><code>set_inverted</code> oder <code>set_state_inverted</code></td>
|
|
42
42
|
<td>Aktiviert den Mischer, wenn <code>msg.payload = false</code> oder deaktiviert ihn, wenn <code>msg.payload = true</code>.</td>
|
|
43
43
|
</tr>
|
|
44
44
|
<tr>
|
|
@@ -63,6 +63,12 @@
|
|
|
63
63
|
<td><code>current_temperature</code></td>
|
|
64
64
|
<td>Setzt die aktuelle Temperatur auf <code>msg.payload</code> °C.</td>
|
|
65
65
|
</tr>
|
|
66
|
+
<tr>
|
|
67
|
+
<td><code>alarm</code></td>
|
|
68
|
+
<td>
|
|
69
|
+
Setzt den aktuellen Alarmzustand auf den Wert von <code>msg.payload</code> und löst die konfigurierte Alarmaktion aus (Öffnen / Schließen).
|
|
70
|
+
</td>
|
|
71
|
+
</tr>
|
|
66
72
|
<tr>
|
|
67
73
|
<td><code>calibrate</code></td>
|
|
68
74
|
<td>Erzwingt eine Kalibrierungsfahrt.</td>
|
|
@@ -34,11 +34,11 @@
|
|
|
34
34
|
<td>Deactivates the mixer control and moves to the position that is set as the off mode.</td>
|
|
35
35
|
</tr>
|
|
36
36
|
<tr>
|
|
37
|
-
<td><code>set_state</code></td>
|
|
37
|
+
<td><code>set</code> or <code>set_state</code></td>
|
|
38
38
|
<td>Activates the mixing valve when <code>msg.payload = true</code> or deactivates it if <code>msg.payload = false</code>.</td>
|
|
39
39
|
</tr>
|
|
40
40
|
<tr>
|
|
41
|
-
<td><code>set_state_inverted</code></td>
|
|
41
|
+
<td><code>set_inverted</code> or <code>set_state_inverted</code></td>
|
|
42
42
|
<td>Activates the mixing valve when <code>msg.payload = false</code> or deactivates it if <code>msg.payload = true</code>.</td>
|
|
43
43
|
</tr>
|
|
44
44
|
<tr>
|
|
@@ -63,6 +63,12 @@
|
|
|
63
63
|
<td><code>current_temperature</code></td>
|
|
64
64
|
<td>Sets the current temperature to <code>msg.payload</code> °C.</td>
|
|
65
65
|
</tr>
|
|
66
|
+
<tr>
|
|
67
|
+
<td><code>alarm</code></td>
|
|
68
|
+
<td>
|
|
69
|
+
Sets the current alarm state to the value of <code>msg.payload</code> and triggers the configured alarm action (Open / Close).
|
|
70
|
+
</td>
|
|
71
|
+
</tr>
|
|
66
72
|
<tr>
|
|
67
73
|
<td><code>calibrate</code></td>
|
|
68
74
|
<td>Forces a calibration run.</td>
|
|
@@ -150,7 +150,8 @@
|
|
|
150
150
|
max_change_percent: { value: 1 },
|
|
151
151
|
max_change_temp_difference: { value: 20 },
|
|
152
152
|
min_change_time: { value: 100 },
|
|
153
|
-
links: { value: [], type: "smart_central-control[]" }
|
|
153
|
+
links: { value: [], type: "smart_central-control[]" },
|
|
154
|
+
alarm_action: { value: "NOTHING" }, // NOTHING | OPEN | CLOSE
|
|
154
155
|
},
|
|
155
156
|
inputs: 1,
|
|
156
157
|
outputs: 1,
|
|
@@ -170,6 +171,33 @@
|
|
|
170
171
|
onEditPrepare(this, ["smart_central-control"]);
|
|
171
172
|
initTreeList(node, ["smart_central-control"]);
|
|
172
173
|
|
|
174
|
+
$("#node-input-output_mode")
|
|
175
|
+
.css("max-width", "70%")
|
|
176
|
+
.typedInput({
|
|
177
|
+
type: "num",
|
|
178
|
+
types: [{
|
|
179
|
+
value: "output_mode",
|
|
180
|
+
options: [
|
|
181
|
+
{ value: "OPEN_CLOSE", label: node._("mixing-valve.ui.open_close") },
|
|
182
|
+
{ value: "PERCENTAGE", label: node._("mixing-valve.ui.percentage") },
|
|
183
|
+
]
|
|
184
|
+
}]
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
$("#node-input-output_mode").on("change", function ()
|
|
188
|
+
{
|
|
189
|
+
if (this.value == "OPEN_CLOSE")
|
|
190
|
+
{
|
|
191
|
+
$(".only-open-close").show();
|
|
192
|
+
node.outputs = 3;
|
|
193
|
+
}
|
|
194
|
+
else
|
|
195
|
+
{
|
|
196
|
+
$(".only-open-close").hide();
|
|
197
|
+
node.outputs = 1;
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
173
201
|
$("#node-input-setpoint")
|
|
174
202
|
.css("max-width", "4rem")
|
|
175
203
|
.spinner({
|
|
@@ -239,50 +267,36 @@
|
|
|
239
267
|
],
|
|
240
268
|
}],
|
|
241
269
|
});
|
|
242
|
-
|
|
243
|
-
$("#node-input-
|
|
270
|
+
|
|
271
|
+
$("#node-input-alarm_action")
|
|
244
272
|
.css("max-width", "70%")
|
|
245
273
|
.typedInput({
|
|
246
|
-
type: "num",
|
|
247
274
|
types: [{
|
|
248
|
-
value: "
|
|
275
|
+
value: "alarm_action",
|
|
276
|
+
default: "NOTHING",
|
|
249
277
|
options: [
|
|
250
|
-
{ value: "
|
|
251
|
-
{ value: "
|
|
252
|
-
|
|
253
|
-
|
|
278
|
+
{ value: "NOTHING", label: node._("mixing-valve.ui.no_action") },
|
|
279
|
+
{ value: "OPEN", label: node._("mixing-valve.ui.open") },
|
|
280
|
+
{ value: "CLOSE", label: node._("mixing-valve.ui.close") }
|
|
281
|
+
],
|
|
282
|
+
}],
|
|
254
283
|
});
|
|
255
284
|
|
|
256
|
-
$("#node-input-output_mode").on("change", function ()
|
|
257
|
-
{
|
|
258
|
-
if (this.value == "OPEN_CLOSE")
|
|
259
|
-
{
|
|
260
|
-
$(".only-open-close").show();
|
|
261
|
-
node.outputs = 3;
|
|
262
|
-
}
|
|
263
|
-
else
|
|
264
|
-
{
|
|
265
|
-
$(".only-open-close").hide();
|
|
266
|
-
node.outputs = 1;
|
|
267
|
-
}
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
|
|
271
285
|
$("#node-input-precision")
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
+
.css("max-width", "4rem")
|
|
287
|
+
.spinner({
|
|
288
|
+
min: 0.1,
|
|
289
|
+
max: 1.0,
|
|
290
|
+
step: 0.1,
|
|
291
|
+
change: function (event, ui)
|
|
292
|
+
{
|
|
293
|
+
var value = parseFloat(this.value);
|
|
294
|
+
value = isNaN(value) ? 0.0 : value;
|
|
295
|
+
value = Math.max(value, parseFloat($(this).attr("aria-valuemin")));
|
|
296
|
+
value = Math.min(value, parseFloat($(this).attr("aria-valuemax")));
|
|
297
|
+
if (value !== this.value) $(this).spinner("value", value);
|
|
298
|
+
},
|
|
299
|
+
});
|
|
286
300
|
|
|
287
301
|
$("#node-input-max_change_percent")
|
|
288
302
|
.css("max-width", "4rem")
|
|
@@ -372,6 +386,10 @@
|
|
|
372
386
|
<label for="node-input-valve_mode"><i class="fa fa-fire"></i> <span data-i18n="mixing-valve.ui.mode"></span></label>
|
|
373
387
|
<input id="node-input-valve_mode" />
|
|
374
388
|
</div>
|
|
389
|
+
<div class="form-row">
|
|
390
|
+
<label for="node-input-alarm_action"><i class="fa fa-exclamation-triangle"></i> <span data-i18n="mixing-valve.ui.alarm_on"></span></label>
|
|
391
|
+
<input id="node-input-alarm_action"/>
|
|
392
|
+
</div>
|
|
375
393
|
<div class="form-row">
|
|
376
394
|
<label for="node-input-precision" style="width: 250px;"><i class="fa fa-sliders"></i> <span data-i18n="mixing-valve.ui.precision"></span></label>
|
|
377
395
|
<input id="node-input-precision" value="1.0" /> °C
|
|
@@ -33,11 +33,9 @@ module.exports = function (RED)
|
|
|
33
33
|
setpoint: config.setpoint,
|
|
34
34
|
off_mode: config.off_mode,
|
|
35
35
|
valve_mode: config.valve_mode,
|
|
36
|
-
precision: config.precision || 1,
|
|
37
|
-
max_change_percent: config.max_change_percent || 2,
|
|
38
|
-
max_change_temp_difference: config.max_change_temp_difference || 20,
|
|
39
|
-
min_change_time: config.min_change_time || 0,
|
|
40
36
|
last_position: null,
|
|
37
|
+
last_enabled_sended: null,
|
|
38
|
+
alarm_active: false
|
|
41
39
|
}, smart_context.get(node.id));
|
|
42
40
|
|
|
43
41
|
// Ensure correct types
|
|
@@ -45,10 +43,13 @@ module.exports = function (RED)
|
|
|
45
43
|
node_settings.setpoint = parseFloat(node_settings.setpoint);
|
|
46
44
|
if (isNaN(node_settings.setpoint) || !isFinite(node_settings.setpoint))
|
|
47
45
|
node_settings.setpoint = 20;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
|
|
47
|
+
// Remove old settings
|
|
48
|
+
delete node_settings.precision;
|
|
49
|
+
delete node_settings.max_change_percent;
|
|
50
|
+
delete node_settings.max_change_temp_difference;
|
|
51
|
+
delete node_settings.min_change_time;
|
|
52
|
+
|
|
52
53
|
|
|
53
54
|
// ##################
|
|
54
55
|
// # Dynamic config #
|
|
@@ -57,11 +58,17 @@ module.exports = function (RED)
|
|
|
57
58
|
let time_sampling_s = config.time_sampling;
|
|
58
59
|
let output_mode = config.output_mode || OUTPUT_MODE_OPEN_CLOSE;
|
|
59
60
|
let force_position = null;
|
|
61
|
+
let alarm_action = config.alarm_action || "NOTHING"; // NOTHING | OPEN | CLOSE
|
|
60
62
|
let min_temperature = null;
|
|
61
63
|
let min_temperature_position = null;
|
|
62
64
|
let max_temperature = null;
|
|
63
65
|
let max_temperature_position = null;
|
|
64
66
|
let temp_save_date = Date.now();
|
|
67
|
+
let precision = parseInt(config.precision || 1, 10);
|
|
68
|
+
let max_change_percent = parseInt(config.max_change_percent || 2, 10);
|
|
69
|
+
let max_change_temp_difference = parseInt(config.max_change_temp_difference || 20, 10);
|
|
70
|
+
let min_change_time = parseInt(config.min_change_time || 0, 10);
|
|
71
|
+
|
|
65
72
|
|
|
66
73
|
// ##################
|
|
67
74
|
// # Runtime values #
|
|
@@ -148,13 +155,16 @@ module.exports = function (RED)
|
|
|
148
155
|
|
|
149
156
|
let real_topic = helper.getTopicName(msg.topic);
|
|
150
157
|
|
|
151
|
-
if (real_topic
|
|
158
|
+
if (real_topic.startsWith("set_state"))
|
|
159
|
+
real_topic = real_topic.replace("set_state", "set");
|
|
160
|
+
|
|
161
|
+
if (real_topic == "set_inverted")
|
|
152
162
|
{
|
|
153
|
-
real_topic = "
|
|
163
|
+
real_topic = "set";
|
|
154
164
|
msg.payload = !msg.payload;
|
|
155
165
|
}
|
|
156
166
|
|
|
157
|
-
if (real_topic == "
|
|
167
|
+
if (real_topic == "set")
|
|
158
168
|
real_topic = (!!msg.payload) ? "enable" : "disable";
|
|
159
169
|
|
|
160
170
|
switch (real_topic)
|
|
@@ -168,6 +178,10 @@ module.exports = function (RED)
|
|
|
168
178
|
heating_mode: node_settings.valve_mode,
|
|
169
179
|
current_temperature,
|
|
170
180
|
output_mode,
|
|
181
|
+
precision,
|
|
182
|
+
max_change_percent,
|
|
183
|
+
max_change_temp_difference,
|
|
184
|
+
min_change_time,
|
|
171
185
|
min_temperature,
|
|
172
186
|
min_temperature_position,
|
|
173
187
|
max_temperature,
|
|
@@ -179,11 +193,16 @@ module.exports = function (RED)
|
|
|
179
193
|
case "enable":
|
|
180
194
|
if (node_settings.enabled)
|
|
181
195
|
{
|
|
182
|
-
|
|
196
|
+
helper.log(node, "Already enabled");
|
|
183
197
|
break;
|
|
184
198
|
}
|
|
185
199
|
|
|
186
200
|
node_settings.enabled = true;
|
|
201
|
+
|
|
202
|
+
// If in alarm mode, just save enabled state, but don't start changing
|
|
203
|
+
if (node_settings.alarm_active)
|
|
204
|
+
return;
|
|
205
|
+
|
|
187
206
|
stopChanging();
|
|
188
207
|
|
|
189
208
|
// Set the most probable position
|
|
@@ -230,6 +249,10 @@ module.exports = function (RED)
|
|
|
230
249
|
|
|
231
250
|
node_settings.enabled = false;
|
|
232
251
|
|
|
252
|
+
// If in alarm mode, just save enabled state, but don't start changing
|
|
253
|
+
if (node_settings.alarm_active)
|
|
254
|
+
return;
|
|
255
|
+
|
|
233
256
|
stopSampling();
|
|
234
257
|
doOffMode();
|
|
235
258
|
break;
|
|
@@ -304,6 +327,45 @@ module.exports = function (RED)
|
|
|
304
327
|
calibrate();
|
|
305
328
|
break;
|
|
306
329
|
|
|
330
|
+
case "alarm":
|
|
331
|
+
// Make sure it is bool
|
|
332
|
+
msg.payload = !!msg.payload;
|
|
333
|
+
|
|
334
|
+
// No alarm change -> nothing to do
|
|
335
|
+
if (node_settings.alarm_active == msg.payload)
|
|
336
|
+
break;
|
|
337
|
+
|
|
338
|
+
node_settings.alarm_active = msg.payload;
|
|
339
|
+
if (node_settings.alarm_active)
|
|
340
|
+
{
|
|
341
|
+
switch (alarm_action)
|
|
342
|
+
{
|
|
343
|
+
case "OPEN":
|
|
344
|
+
force_position = 100;;
|
|
345
|
+
break;
|
|
346
|
+
|
|
347
|
+
case "CLOSE":
|
|
348
|
+
force_position = 0;
|
|
349
|
+
break;
|
|
350
|
+
|
|
351
|
+
default:
|
|
352
|
+
case "NOTHING":
|
|
353
|
+
// Don't use alarm mode
|
|
354
|
+
node_settings.alarm_active = false;
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
else
|
|
359
|
+
{
|
|
360
|
+
force_position = null;
|
|
361
|
+
if (!node_settings.enabled)
|
|
362
|
+
doOffMode();
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
stopSampling();
|
|
366
|
+
startSampling();
|
|
367
|
+
break;
|
|
368
|
+
|
|
307
369
|
default:
|
|
308
370
|
helper.warn(this, "Invalid topic: " + real_topic);
|
|
309
371
|
return;
|
|
@@ -314,7 +376,7 @@ module.exports = function (RED)
|
|
|
314
376
|
|
|
315
377
|
let startSampling = () =>
|
|
316
378
|
{
|
|
317
|
-
if (!node_settings.enabled)
|
|
379
|
+
if (!node_settings.enabled && !node_settings.alarm_active)
|
|
318
380
|
{
|
|
319
381
|
helper.log(node, "Node is disabled, cannot start sampling");
|
|
320
382
|
return;
|
|
@@ -346,7 +408,7 @@ module.exports = function (RED)
|
|
|
346
408
|
helper.log(node, "Force position to " + force_position.toFixed(1) + "%");
|
|
347
409
|
|
|
348
410
|
// Under precision % difference => do nothing
|
|
349
|
-
if (Math.abs(node_settings.last_position - force_position) <=
|
|
411
|
+
if (Math.abs(node_settings.last_position - force_position) <= precision)
|
|
350
412
|
{
|
|
351
413
|
helper.log(node, "No Force position needed");
|
|
352
414
|
force_position = null;
|
|
@@ -365,6 +427,7 @@ module.exports = function (RED)
|
|
|
365
427
|
case OUTPUT_MODE_PERCENTAGE:
|
|
366
428
|
// Output mode percentage
|
|
367
429
|
node_settings.last_position = force_position;
|
|
430
|
+
force_position = null;
|
|
368
431
|
node.send({ payload: node_settings.last_position });
|
|
369
432
|
smart_context.set(node.id, node_settings);
|
|
370
433
|
setStatus();
|
|
@@ -408,20 +471,23 @@ module.exports = function (RED)
|
|
|
408
471
|
|
|
409
472
|
// +/- 1°C => already good enough, do nothing
|
|
410
473
|
let temp_diff = Math.abs(current_temperature - node_settings.setpoint);
|
|
411
|
-
if (temp_diff <
|
|
474
|
+
if (temp_diff < precision)
|
|
412
475
|
{
|
|
413
|
-
|
|
414
|
-
// Update min/max temperature
|
|
415
|
-
if (min_temperature === null || current_temperature < min_temperature)
|
|
476
|
+
if (!node_settings.alarm_active)
|
|
416
477
|
{
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
478
|
+
// Found a good position for the current setpoint
|
|
479
|
+
// Update min/max temperature
|
|
480
|
+
if (min_temperature === null || current_temperature < min_temperature)
|
|
481
|
+
{
|
|
482
|
+
min_temperature = current_temperature;
|
|
483
|
+
min_temperature_position = node_settings.last_position;
|
|
484
|
+
}
|
|
420
485
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
486
|
+
if (max_temperature === null || current_temperature > max_temperature)
|
|
487
|
+
{
|
|
488
|
+
max_temperature = current_temperature;
|
|
489
|
+
max_temperature_position = node_settings.last_position;
|
|
490
|
+
}
|
|
425
491
|
}
|
|
426
492
|
return;
|
|
427
493
|
}
|
|
@@ -429,11 +495,11 @@ module.exports = function (RED)
|
|
|
429
495
|
// 0 °C diff => 0% change
|
|
430
496
|
// for max_change_temp_difference (default: 20 °C) diff => max_change_percent (default: 2%) change
|
|
431
497
|
let change_percentage = helper.scale(
|
|
432
|
-
Math.min(temp_diff,
|
|
498
|
+
Math.min(temp_diff, max_change_temp_difference),
|
|
433
499
|
0,
|
|
434
|
-
|
|
500
|
+
max_change_temp_difference,
|
|
435
501
|
0,
|
|
436
|
-
|
|
502
|
+
max_change_percent
|
|
437
503
|
);
|
|
438
504
|
|
|
439
505
|
// calculate direction
|
|
@@ -473,8 +539,8 @@ module.exports = function (RED)
|
|
|
473
539
|
// Change time in ms
|
|
474
540
|
let moving_time = (time_total_s * 1000 / 100) * change_percentage;
|
|
475
541
|
|
|
476
|
-
if (moving_time <
|
|
477
|
-
moving_time =
|
|
542
|
+
if (moving_time < min_change_time)
|
|
543
|
+
moving_time = min_change_time;
|
|
478
544
|
|
|
479
545
|
// start moving
|
|
480
546
|
startChanging(adjustAction, moving_time);
|
|
@@ -651,9 +717,9 @@ module.exports = function (RED)
|
|
|
651
717
|
if (calibration_timeout !== null)
|
|
652
718
|
node.status({ fill: "yellow", shape: "ring", text: helper.getCurrentTimeForStatus() + ": In calibration" });
|
|
653
719
|
else if (changing_timeout != null)
|
|
654
|
-
node.status({ fill: node_settings.enabled ? "green" : "red", shape: "ring", text: helper.getCurrentTimeForStatus() + ": " + (node_settings.valve_mode == "HEATING" ? "🔥" : "❄️") + " " + (adjusting == ADJUST_OPEN ? "Opening" : "Closing") + ", Set: " + helper.toFixed(node_settings.setpoint, 1) + "°C, Cur: " + helper.toFixed(current_temperature, 1) + "°C, Pos: " + helper.toFixed(node_settings.last_position, 1) + "%" });
|
|
720
|
+
node.status({ fill: node_settings.enabled ? "green" : "red", shape: "ring", text: helper.getCurrentTimeForStatus() + ": " + (node_settings.alarm_active ? "ALARM - " : "") + (node_settings.valve_mode == "HEATING" ? "🔥" : "❄️") + " " + (adjusting == ADJUST_OPEN ? "Opening" : "Closing") + ", Set: " + helper.toFixed(node_settings.setpoint, 1) + "°C, Cur: " + helper.toFixed(current_temperature, 1) + "°C, Pos: " + helper.toFixed(node_settings.last_position, 1) + "%" });
|
|
655
721
|
else
|
|
656
|
-
node.status({ fill: node_settings.enabled ? "green" : "red", shape: "dot", text: helper.getCurrentTimeForStatus() + ": " + (node_settings.valve_mode == "HEATING" ? "🔥" : "❄️") + " Set: " + helper.toFixed(node_settings.setpoint, 1) + "°C, Cur: " + helper.toFixed(current_temperature, 1) + "°C, Pos: " + helper.toFixed(node_settings.last_position, 1) + "%" });
|
|
722
|
+
node.status({ fill: node_settings.enabled ? "green" : "red", shape: "dot", text: helper.getCurrentTimeForStatus() + ": " + (node_settings.alarm_active ? "ALARM - " : "") + (node_settings.valve_mode == "HEATING" ? "🔥" : "❄️") + " Set: " + helper.toFixed(node_settings.setpoint, 1) + "°C, Cur: " + helper.toFixed(current_temperature, 1) + "°C, Pos: " + helper.toFixed(node_settings.last_position, 1) + "%" });
|
|
657
723
|
}
|
|
658
724
|
|
|
659
725
|
/**
|
|
@@ -685,10 +751,30 @@ module.exports = function (RED)
|
|
|
685
751
|
node_settings.last_position = 0;
|
|
686
752
|
}
|
|
687
753
|
}
|
|
754
|
+
else if (node_settings.alarm_active)
|
|
755
|
+
{
|
|
756
|
+
switch (alarm_action)
|
|
757
|
+
{
|
|
758
|
+
case "OPEN":
|
|
759
|
+
force_position = 100;;
|
|
760
|
+
break;
|
|
761
|
+
|
|
762
|
+
case "CLOSE":
|
|
763
|
+
force_position = 0;
|
|
764
|
+
break;
|
|
765
|
+
|
|
766
|
+
default:
|
|
767
|
+
case "NOTHING":
|
|
768
|
+
// Don't use alarm mode
|
|
769
|
+
node_settings.alarm_active = false;
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
startSampling();
|
|
774
|
+
}
|
|
688
775
|
else if (node_settings.enabled)
|
|
689
776
|
{
|
|
690
777
|
startSampling();
|
|
691
|
-
node.send([null, null, { payload: helper.toFixed(node_settings.last_position, 1) }]);
|
|
692
778
|
}
|
|
693
779
|
|
|
694
780
|
setStatus();
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
.appendTo(row);
|
|
82
82
|
|
|
83
83
|
// Output name
|
|
84
|
-
var modeName = $("<input/>", { class: "node-input-prop-name", placeholder: node._("
|
|
84
|
+
var modeName = $("<input/>", { class: "node-input-prop-name", placeholder: node._("mode-selector.ui.name"), type: "text" })
|
|
85
85
|
.css("width", "90%")
|
|
86
86
|
.appendTo(row);
|
|
87
87
|
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
tabs.addTab({
|
|
116
116
|
id: "modes-tab-outputs",
|
|
117
117
|
iconClass: "fa fa-list-ol",
|
|
118
|
-
label: node._("
|
|
118
|
+
label: node._("mode-selector.ui.modes")
|
|
119
119
|
});
|
|
120
120
|
},
|
|
121
121
|
oneditsave: function ()
|
|
@@ -135,12 +135,12 @@
|
|
|
135
135
|
|
|
136
136
|
<script type="text/html" data-template-name="smart_mode-selector">
|
|
137
137
|
<div class="form-row">
|
|
138
|
-
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="
|
|
139
|
-
<input type="text" id="node-input-name" data-i18n="[placeholder]
|
|
138
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="mode-selector.ui.name"></span></label>
|
|
139
|
+
<input type="text" id="node-input-name" data-i18n="[placeholder]mode-selector.ui.name" />
|
|
140
140
|
</div>
|
|
141
141
|
<div class="form-row">
|
|
142
142
|
<input type="checkbox" id="node-input-update_only_changed_outputs" style="width: 20px;" />
|
|
143
|
-
<label for="node-input-update_only_changed_outputs" style="width: calc(100% - 30px);" data-i18n="
|
|
143
|
+
<label for="node-input-update_only_changed_outputs" style="width: calc(100% - 30px);" data-i18n="mode-selector.ui.update_only_changed_outputs"></label>
|
|
144
144
|
</div>
|
|
145
145
|
|
|
146
146
|
<div id="mode-selector-modes">
|
|
@@ -154,10 +154,10 @@
|
|
|
154
154
|
</div>
|
|
155
155
|
</div>
|
|
156
156
|
<hr/>
|
|
157
|
-
<h4 style="margin: 0.5rem 0;" data-i18n="
|
|
157
|
+
<h4 style="margin: 0.5rem 0;" data-i18n="mode-selector.ui.system_start"></h4>
|
|
158
158
|
<div class="form-row">
|
|
159
159
|
<input type="checkbox" id="node-input-resend_on_start" style="width: 20px;" />
|
|
160
|
-
<label for="node-input-resend_on_start" style="width: calc(100% - 30px);" data-i18n="
|
|
160
|
+
<label for="node-input-resend_on_start" style="width: calc(100% - 30px);" data-i18n="mode-selector.ui.send_after_start"></label>
|
|
161
161
|
</div>
|
|
162
162
|
|
|
163
163
|
</script>
|
package/package.json
CHANGED
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
<td>Deaktiviert den Scheduler.</td>
|
|
26
26
|
</tr>
|
|
27
27
|
<tr>
|
|
28
|
-
<td><code>set_state</code></td>
|
|
28
|
+
<td><code>set</code> oder <code>set_state</code></td>
|
|
29
29
|
<td>Aktiviert den Scheduler, wenn <code>msg.payload = true</code> oder deaktiviert den Scheduler, wenn <code>msg.payload = false</code>.</td>
|
|
30
30
|
</tr>
|
|
31
31
|
<tr>
|
|
32
|
-
<td><code>set_state_inverted</code></td>
|
|
32
|
+
<td><code>set_inverted</code> oder <code>set_state_inverted</code></td>
|
|
33
33
|
<td>Aktiviert den Scheduler, wenn <code>msg.payload = false</code> oder deaktiviert den Scheduler, wenn <code>msg.payload = true</code>.</td>
|
|
34
34
|
</tr>
|
|
35
35
|
</tbody>
|
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
<td>Disables the scheduler.</td>
|
|
26
26
|
</tr>
|
|
27
27
|
<tr>
|
|
28
|
-
<td><code>set_state</code></td>
|
|
28
|
+
<td><code>set</code> or <code>set_state</code></td>
|
|
29
29
|
<td>Enables the scheduler if <code>msg.payload = true</code> or disables the scheduler if <code>msg.payload = false</code>.</td>
|
|
30
30
|
</tr>
|
|
31
31
|
<tr>
|
|
32
|
-
<td><code>set_state_inverted</code></td>
|
|
32
|
+
<td><code>set_inverted</code> oder <code>set_state_inverted</code></td>
|
|
33
33
|
<td>Enables the scheduler if <code>msg.payload = false</code> or disables the scheduler if <code>msg.payload = true</code>.</td>
|
|
34
34
|
</tr>
|
|
35
35
|
</tbody>
|
package/scheduler/scheduler.js
CHANGED
|
@@ -102,13 +102,16 @@ module.exports = function (RED)
|
|
|
102
102
|
{
|
|
103
103
|
let real_topic = helper.getTopicName(msg.topic);
|
|
104
104
|
|
|
105
|
-
if (real_topic
|
|
105
|
+
if (real_topic.startsWith("set_state"))
|
|
106
|
+
real_topic = real_topic.replace("set_state", "set");
|
|
107
|
+
|
|
108
|
+
if (real_topic == "set_inverted")
|
|
106
109
|
{
|
|
107
|
-
real_topic = "
|
|
110
|
+
real_topic = "set";
|
|
108
111
|
msg.payload = !msg.payload;
|
|
109
112
|
}
|
|
110
113
|
|
|
111
|
-
if (real_topic == "
|
|
114
|
+
if (real_topic == "set")
|
|
112
115
|
real_topic = (!!msg.payload) ? "enable" : "disable";
|
|
113
116
|
|
|
114
117
|
switch (real_topic)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script type="text/html" data-help-name="smart_statistic">
|
|
2
2
|
<p>Dieser Knoten berechnet das Minimum, Maximum, die Summe, die Differenz, den absoluten Wert, die absolute Differenz, den Durchschnitt sowie den gleitenden Mittelwert verschiedener Werte.</p>
|
|
3
3
|
<p>Jeder Wert muss mit einem eigenen Topic gesendet werden. Kommt ein zweiter Wert mit dem gleichen Topic, wird der entsprechende Wert überschrieben.</p>
|
|
4
|
-
<p>Im Falle von MIN, MAX und ABS wird das entsprechende Topic, welches mit den Werten kam, mit ausgegeben. Bei SUM, DIFF, AVG und MOV_AVG handelt es sich um kombinierte Ergebnisse, weshalb Topic hier immer nicht gesetzt ist.</p>
|
|
4
|
+
<p>Im Falle von MIN, MAX und ABS wird das entsprechende Topic, welches mit den Werten kam, mit ausgegeben. Bei SUM, DIFF, AVG und MOV_AVG handelt es sich um kombinierte Ergebnisse, weshalb das Topic hier immer nicht gesetzt ist.</p>
|
|
5
5
|
<p>Für den absoluten Wert sowie den gleitenden Mittelwert spielt das Topic keine Rolle, jeder Wert der empfangen wird, wird für die Berechnung verwendet.</p>
|
|
6
6
|
<p>
|
|
7
7
|
<b>Hinweis:</b> Smart Nodes verwenden Topics im Format <code>name#nummer</code>, damit können verschiedene Smart Nodes mit dem gleichen Topic angesteuert werden.<br/>
|