smart-nodes 0.5.1 → 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 +5 -1
- package/mixing-valve/locales/de-DE/mixing-valve.html +6 -0
- package/mixing-valve/locales/de-DE/mixing-valve.json +2 -0
- package/mixing-valve/locales/en-US/mixing-valve.html +6 -0
- package/mixing-valve/locales/en-US/mixing-valve.json +2 -0
- package/mixing-valve/mixing-valve.html +20 -2
- package/mixing-valve/mixing-valve.js +112 -29
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -213,4 +213,8 @@
|
|
|
213
213
|
|
|
214
214
|
## Version 0.5.1:
|
|
215
215
|
|
|
216
|
-
- To be more compatible, topic "set" is also possible instead of "set_state". Same for "set_inverted" instead of "set_state_inverted".
|
|
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.
|
|
@@ -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>
|
|
@@ -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,
|
|
@@ -266,7 +267,20 @@
|
|
|
266
267
|
],
|
|
267
268
|
}],
|
|
268
269
|
});
|
|
269
|
-
|
|
270
|
+
|
|
271
|
+
$("#node-input-alarm_action")
|
|
272
|
+
.css("max-width", "70%")
|
|
273
|
+
.typedInput({
|
|
274
|
+
types: [{
|
|
275
|
+
value: "alarm_action",
|
|
276
|
+
default: "NOTHING",
|
|
277
|
+
options: [
|
|
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
|
+
}],
|
|
283
|
+
});
|
|
270
284
|
|
|
271
285
|
$("#node-input-precision")
|
|
272
286
|
.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 #
|
|
@@ -171,6 +178,10 @@ module.exports = function (RED)
|
|
|
171
178
|
heating_mode: node_settings.valve_mode,
|
|
172
179
|
current_temperature,
|
|
173
180
|
output_mode,
|
|
181
|
+
precision,
|
|
182
|
+
max_change_percent,
|
|
183
|
+
max_change_temp_difference,
|
|
184
|
+
min_change_time,
|
|
174
185
|
min_temperature,
|
|
175
186
|
min_temperature_position,
|
|
176
187
|
max_temperature,
|
|
@@ -187,6 +198,11 @@ module.exports = function (RED)
|
|
|
187
198
|
}
|
|
188
199
|
|
|
189
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
|
+
|
|
190
206
|
stopChanging();
|
|
191
207
|
|
|
192
208
|
// Set the most probable position
|
|
@@ -233,6 +249,10 @@ module.exports = function (RED)
|
|
|
233
249
|
|
|
234
250
|
node_settings.enabled = false;
|
|
235
251
|
|
|
252
|
+
// If in alarm mode, just save enabled state, but don't start changing
|
|
253
|
+
if (node_settings.alarm_active)
|
|
254
|
+
return;
|
|
255
|
+
|
|
236
256
|
stopSampling();
|
|
237
257
|
doOffMode();
|
|
238
258
|
break;
|
|
@@ -307,6 +327,45 @@ module.exports = function (RED)
|
|
|
307
327
|
calibrate();
|
|
308
328
|
break;
|
|
309
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
|
+
|
|
310
369
|
default:
|
|
311
370
|
helper.warn(this, "Invalid topic: " + real_topic);
|
|
312
371
|
return;
|
|
@@ -317,7 +376,7 @@ module.exports = function (RED)
|
|
|
317
376
|
|
|
318
377
|
let startSampling = () =>
|
|
319
378
|
{
|
|
320
|
-
if (!node_settings.enabled)
|
|
379
|
+
if (!node_settings.enabled && !node_settings.alarm_active)
|
|
321
380
|
{
|
|
322
381
|
helper.log(node, "Node is disabled, cannot start sampling");
|
|
323
382
|
return;
|
|
@@ -349,7 +408,7 @@ module.exports = function (RED)
|
|
|
349
408
|
helper.log(node, "Force position to " + force_position.toFixed(1) + "%");
|
|
350
409
|
|
|
351
410
|
// Under precision % difference => do nothing
|
|
352
|
-
if (Math.abs(node_settings.last_position - force_position) <=
|
|
411
|
+
if (Math.abs(node_settings.last_position - force_position) <= precision)
|
|
353
412
|
{
|
|
354
413
|
helper.log(node, "No Force position needed");
|
|
355
414
|
force_position = null;
|
|
@@ -368,6 +427,7 @@ module.exports = function (RED)
|
|
|
368
427
|
case OUTPUT_MODE_PERCENTAGE:
|
|
369
428
|
// Output mode percentage
|
|
370
429
|
node_settings.last_position = force_position;
|
|
430
|
+
force_position = null;
|
|
371
431
|
node.send({ payload: node_settings.last_position });
|
|
372
432
|
smart_context.set(node.id, node_settings);
|
|
373
433
|
setStatus();
|
|
@@ -411,20 +471,23 @@ module.exports = function (RED)
|
|
|
411
471
|
|
|
412
472
|
// +/- 1°C => already good enough, do nothing
|
|
413
473
|
let temp_diff = Math.abs(current_temperature - node_settings.setpoint);
|
|
414
|
-
if (temp_diff <
|
|
474
|
+
if (temp_diff < precision)
|
|
415
475
|
{
|
|
416
|
-
|
|
417
|
-
// Update min/max temperature
|
|
418
|
-
if (min_temperature === null || current_temperature < min_temperature)
|
|
476
|
+
if (!node_settings.alarm_active)
|
|
419
477
|
{
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
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
|
+
}
|
|
423
485
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
486
|
+
if (max_temperature === null || current_temperature > max_temperature)
|
|
487
|
+
{
|
|
488
|
+
max_temperature = current_temperature;
|
|
489
|
+
max_temperature_position = node_settings.last_position;
|
|
490
|
+
}
|
|
428
491
|
}
|
|
429
492
|
return;
|
|
430
493
|
}
|
|
@@ -432,11 +495,11 @@ module.exports = function (RED)
|
|
|
432
495
|
// 0 °C diff => 0% change
|
|
433
496
|
// for max_change_temp_difference (default: 20 °C) diff => max_change_percent (default: 2%) change
|
|
434
497
|
let change_percentage = helper.scale(
|
|
435
|
-
Math.min(temp_diff,
|
|
498
|
+
Math.min(temp_diff, max_change_temp_difference),
|
|
436
499
|
0,
|
|
437
|
-
|
|
500
|
+
max_change_temp_difference,
|
|
438
501
|
0,
|
|
439
|
-
|
|
502
|
+
max_change_percent
|
|
440
503
|
);
|
|
441
504
|
|
|
442
505
|
// calculate direction
|
|
@@ -476,8 +539,8 @@ module.exports = function (RED)
|
|
|
476
539
|
// Change time in ms
|
|
477
540
|
let moving_time = (time_total_s * 1000 / 100) * change_percentage;
|
|
478
541
|
|
|
479
|
-
if (moving_time <
|
|
480
|
-
moving_time =
|
|
542
|
+
if (moving_time < min_change_time)
|
|
543
|
+
moving_time = min_change_time;
|
|
481
544
|
|
|
482
545
|
// start moving
|
|
483
546
|
startChanging(adjustAction, moving_time);
|
|
@@ -654,9 +717,9 @@ module.exports = function (RED)
|
|
|
654
717
|
if (calibration_timeout !== null)
|
|
655
718
|
node.status({ fill: "yellow", shape: "ring", text: helper.getCurrentTimeForStatus() + ": In calibration" });
|
|
656
719
|
else if (changing_timeout != null)
|
|
657
|
-
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) + "%" });
|
|
658
721
|
else
|
|
659
|
-
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) + "%" });
|
|
660
723
|
}
|
|
661
724
|
|
|
662
725
|
/**
|
|
@@ -688,10 +751,30 @@ module.exports = function (RED)
|
|
|
688
751
|
node_settings.last_position = 0;
|
|
689
752
|
}
|
|
690
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
|
+
}
|
|
691
775
|
else if (node_settings.enabled)
|
|
692
776
|
{
|
|
693
777
|
startSampling();
|
|
694
|
-
node.send([null, null, { payload: helper.toFixed(node_settings.last_position, 1) }]);
|
|
695
778
|
}
|
|
696
779
|
|
|
697
780
|
setStatus();
|