smart-nodes 0.3.37 → 0.4.0
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 +62 -0
- package/README.md +35 -17
- package/central/central.html +26 -12
- package/central/central.js +84 -26
- package/central/locales/de-DE/central.json +1 -4
- package/central/locales/en-US/central.json +0 -3
- package/compare/compare.html +40 -24
- package/compare/compare.js +69 -29
- package/compare/locales/de-DE/compare.json +5 -7
- package/compare/locales/en-US/compare.json +18 -8
- package/counter/counter.html +64 -17
- package/counter/counter.js +43 -20
- package/counter/locales/de-DE/counter.json +6 -9
- package/counter/locales/en-US/counter.json +12 -16
- package/delay/delay.html +18 -88
- package/delay/delay.js +63 -20
- package/delay/locales/de-DE/delay.html +71 -0
- package/delay/locales/de-DE/delay.json +19 -0
- package/delay/locales/en-US/delay.html +76 -0
- package/delay/locales/en-US/delay.json +19 -0
- package/forwarder/forwarder.html +11 -42
- package/forwarder/forwarder.js +59 -18
- package/forwarder/locales/de-DE/forwarder.html +32 -0
- package/forwarder/locales/de-DE/forwarder.json +15 -0
- package/forwarder/locales/en-US/forwarder.html +32 -0
- package/forwarder/locales/en-US/forwarder.json +15 -0
- package/heating-curve/heating-curve.html +10 -51
- package/heating-curve/heating-curve.js +38 -13
- package/heating-curve/locales/de-DE/heating-curve.html +38 -0
- package/heating-curve/locales/de-DE/heating-curve.json +12 -0
- package/heating-curve/locales/en-US/heating-curve.html +38 -0
- package/heating-curve/locales/en-US/heating-curve.json +12 -0
- package/hysteresis/hysteresis.html +43 -65
- package/hysteresis/hysteresis.js +94 -68
- package/hysteresis/locales/de-DE/hysteresis.html +36 -0
- package/hysteresis/locales/de-DE/hysteresis.json +27 -0
- package/hysteresis/locales/en-US/hysteresis.html +36 -0
- package/hysteresis/locales/en-US/hysteresis.json +27 -0
- package/{light-control/light-control.html → light/light.html} +34 -144
- package/{light-control/light-control.js → light/light.js} +151 -32
- package/light/locales/de-DE/light.html +149 -0
- package/light/locales/de-DE/light.json +24 -0
- package/light/locales/en-US/light.html +148 -0
- package/light/locales/en-US/light.json +24 -0
- package/logic/locales/de-DE/logic.html +12 -0
- package/logic/locales/de-DE/logic.json +26 -0
- package/logic/locales/en-US/logic.html +12 -0
- package/logic/locales/en-US/logic.json +26 -0
- package/logic/logic.html +27 -40
- package/logic/logic.js +63 -29
- package/long-press/locales/de-DE/long-press.html +5 -0
- package/long-press/locales/de-DE/long-press.json +13 -0
- package/long-press/locales/en-US/long-press.html +5 -0
- package/long-press/locales/en-US/long-press.json +13 -0
- package/{long-press-control/long-press-control.html → long-press/long-press.html} +10 -14
- package/long-press/long-press.js +163 -0
- package/mixing-valve/locales/de-DE/mixing-valve.html +65 -0
- package/mixing-valve/locales/de-DE/mixing-valve.json +19 -0
- package/mixing-valve/locales/en-US/mixing-valve.html +66 -0
- package/mixing-valve/locales/en-US/mixing-valve.json +19 -0
- package/mixing-valve/mixing-valve.html +15 -79
- package/mixing-valve/mixing-valve.js +87 -61
- package/multi-press/locales/de-DE/multi-press.html +5 -0
- package/multi-press/locales/de-DE/multi-press.json +12 -0
- package/multi-press/locales/en-US/multi-press.html +5 -0
- package/multi-press/locales/en-US/multi-press.json +12 -0
- package/{multi-press-control/multi-press-control.html → multi-press/multi-press.html} +9 -13
- package/{multi-press-control/multi-press-control.js → multi-press/multi-press.js} +53 -5
- package/package.json +7 -7
- package/persistence.js +1 -0
- package/scene/locales/de-DE/scene.html +105 -0
- package/scene/locales/de-DE/scene.json +21 -0
- package/scene/locales/en-US/scene.html +107 -0
- package/scene/locales/en-US/scene.json +20 -0
- package/{scene-control/scene-control.html → scene/scene.html} +18 -121
- package/{scene-control/scene-control.js → scene/scene.js} +76 -26
- package/scheduler/locales/de-DE/scheduler.html +30 -0
- package/scheduler/locales/de-DE/scheduler.json +21 -0
- package/scheduler/locales/en-US/scheduler.html +30 -0
- package/scheduler/locales/en-US/scheduler.json +21 -0
- package/scheduler/scheduler.html +34 -64
- package/scheduler/scheduler.js +85 -53
- package/shutter/locales/de-DE/shutter.html +127 -0
- package/shutter/locales/de-DE/shutter.json +11 -0
- package/shutter/locales/en-US/shutter.html +133 -0
- package/shutter/locales/en-US/shutter.json +11 -0
- package/{shutter-control/shutter-control.html → shutter/shutter.html} +7 -133
- package/{shutter-control/shutter-control.js → shutter/shutter.js} +116 -56
- package/shutter-complex/locales/de-DE/shutter-complex.html +120 -0
- package/shutter-complex/locales/de-DE/shutter-complex.json +20 -0
- package/shutter-complex/locales/en-US/shutter-complex.html +120 -0
- package/shutter-complex/locales/en-US/shutter-complex.json +20 -0
- package/{shutter-complex-control/shutter-complex-control.html → shutter-complex/shutter-complex.html} +30 -133
- package/shutter-complex/shutter-complex.js +578 -0
- package/smart_helper.js +52 -9
- package/statistic/locales/de-DE/statistic.html +10 -0
- package/statistic/locales/de-DE/statistic.json +29 -0
- package/statistic/locales/en-US/statistic.html +10 -0
- package/statistic/locales/en-US/statistic.json +29 -0
- package/statistic/statistic.html +32 -36
- package/statistic/statistic.js +57 -28
- package/text-exec/locales/de-DE/text-exec.html +18 -0
- package/text-exec/locales/de-DE/text-exec.json +7 -0
- package/text-exec/locales/en-US/text-exec.html +18 -0
- package/text-exec/locales/en-US/text-exec.json +7 -0
- package/text-exec/text-exec.html +9 -25
- package/text-exec/text-exec.js +43 -2
- package/long-press-control/long-press-control.js +0 -76
- package/shutter-complex-control/shutter-complex-control.js +0 -442
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
},
|
|
23
23
|
oneditprepare: function ()
|
|
24
24
|
{
|
|
25
|
+
let node = this;
|
|
26
|
+
|
|
25
27
|
$("#node-input-setpoint")
|
|
26
28
|
.spinner({
|
|
27
29
|
min: -30,
|
|
@@ -67,9 +69,9 @@
|
|
|
67
69
|
default: "NOTHING",
|
|
68
70
|
value: "off_mode",
|
|
69
71
|
options: [
|
|
70
|
-
{ value: "NOTHING", label: "
|
|
71
|
-
{ value: "OPEN", label: "
|
|
72
|
-
{ value: "CLOSE", label: "
|
|
72
|
+
{ value: "NOTHING", label: node._("mixing-valve.ui.nothing") },
|
|
73
|
+
{ value: "OPEN", label: node._("mixing-valve.ui.open") },
|
|
74
|
+
{ value: "CLOSE", label: node._("mixing-valve.ui.close") }
|
|
73
75
|
],
|
|
74
76
|
}],
|
|
75
77
|
});
|
|
@@ -79,8 +81,8 @@
|
|
|
79
81
|
default: "HEATING",
|
|
80
82
|
value: "valve_mode",
|
|
81
83
|
options: [
|
|
82
|
-
{ value: "HEATING", label: "
|
|
83
|
-
{ value: "COOLING", label: "
|
|
84
|
+
{ value: "HEATING", label: node._("mixing-valve.ui.heating") },
|
|
85
|
+
{ value: "COOLING", label: node._("mixing-valve.ui.cooling") }
|
|
84
86
|
],
|
|
85
87
|
}],
|
|
86
88
|
});
|
|
@@ -90,97 +92,31 @@
|
|
|
90
92
|
|
|
91
93
|
<script type="text/html" data-template-name="smart_mixing-valve">
|
|
92
94
|
<div class="form-row">
|
|
93
|
-
<label for="node-input-name"><i class="fa fa-tag"></i>
|
|
94
|
-
<input type="text" id="node-input-name"
|
|
95
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="mixing-valve.ui.name"></span></label>
|
|
96
|
+
<input type="text" id="node-input-name" data-i18n="[placeholder]mixing-valve.ui.name" />
|
|
95
97
|
</div>
|
|
96
98
|
<div class="form-row">
|
|
97
99
|
<input type="checkbox" id="node-input-enabled" style="width: 20px;" />
|
|
98
|
-
<span for="node-input-enabled" style="width: 200px;">
|
|
100
|
+
<span for="node-input-enabled" style="width: 200px;"> <span data-i18n="mixing-valve.ui.enabled"></span></span>
|
|
99
101
|
</div>
|
|
100
102
|
<div class="form-row">
|
|
101
|
-
<label for="node-input-setpoint"><i class="fa fa-sliders"></i>
|
|
103
|
+
<label for="node-input-setpoint"><i class="fa fa-sliders"></i> <span data-i18n="mixing-valve.ui.setpoint"></span></label>
|
|
102
104
|
<input id="node-input-setpoint" value="0" /> °C
|
|
103
105
|
</div>
|
|
104
106
|
<div class="form-row">
|
|
105
|
-
<label for="node-input-time_total"><i class="fa fa-clock-o"></i>
|
|
107
|
+
<label for="node-input-time_total"><i class="fa fa-clock-o"></i> <span data-i18n="mixing-valve.ui.runtime"></span></label>
|
|
106
108
|
<input id="node-input-time_total" value="0" /> s
|
|
107
109
|
</div>
|
|
108
110
|
<div class="form-row">
|
|
109
|
-
<label for="node-input-time_sampling"><i class="fa fa-clock-o"></i>
|
|
111
|
+
<label for="node-input-time_sampling"><i class="fa fa-clock-o"></i> <span data-i18n="mixing-valve.ui.sampling"></span></label>
|
|
110
112
|
<input id="node-input-time_sampling" value="0" /> s
|
|
111
113
|
</div>
|
|
112
114
|
<div class="form-row">
|
|
113
|
-
<label for="node-input-off_mode"><i class="fa fa-power-off"></i>
|
|
115
|
+
<label for="node-input-off_mode"><i class="fa fa-power-off"></i> <span data-i18n="mixing-valve.ui.power_off"></span></label>
|
|
114
116
|
<input id="node-input-off_mode" />
|
|
115
117
|
</div>
|
|
116
118
|
<div class="form-row">
|
|
117
|
-
<label for="node-input-valve_mode"><i class="fa fa-fire"></i>
|
|
119
|
+
<label for="node-input-valve_mode"><i class="fa fa-fire"></i> <span data-i18n="mixing-valve.ui.mode"></span></label>
|
|
118
120
|
<input id="node-input-valve_mode" />
|
|
119
121
|
</div>
|
|
120
|
-
</script>
|
|
121
|
-
|
|
122
|
-
<script type="text/html" data-help-name="smart_mixing-valve">
|
|
123
|
-
<p>
|
|
124
|
-
Diese Node steuert einen Heizungsmischer. Dieser kann sowohl fürs Heizen als auch fürs Kühlen verwendet werden.
|
|
125
|
-
Nach der eingestellten Abtastzeit wird geprüft, ob die Position des Mischers korrigiert werden muss.
|
|
126
|
-
</p>
|
|
127
|
-
<p>
|
|
128
|
-
Beim ersten Verwenden wird eine Kalibrierungsfahrt gestartet. D.h. der Mischer schließt für die eingestellte Laufzeit und befindet sich dann auf 0%.
|
|
129
|
-
Danach kann der Mischvorgang starten. Die zuletzt angefahrene Position wird persistent gespeichert, wodurch weitere Kalibrierungsfahrten in der Regel nicht mehr notwending sind.
|
|
130
|
-
</p>
|
|
131
|
-
<p>
|
|
132
|
-
<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/>
|
|
133
|
-
Diese Node verwendet nur den Teil <code>name</code>. <code>#</code> und <code>nummer</code> sind dabei optional.
|
|
134
|
-
</p>
|
|
135
|
-
<p>
|
|
136
|
-
Folgende topics werden akzeptiert:
|
|
137
|
-
<table>
|
|
138
|
-
<thead>
|
|
139
|
-
<tr>
|
|
140
|
-
<th>Topic</th>
|
|
141
|
-
<th>Beschreibung</th>
|
|
142
|
-
</tr>
|
|
143
|
-
</thead>
|
|
144
|
-
<tbody>
|
|
145
|
-
<tr>
|
|
146
|
-
<td><code>enable</code></td>
|
|
147
|
-
<td>Aktiviert die Mischeransteuerung.</td>
|
|
148
|
-
</tr>
|
|
149
|
-
<tr>
|
|
150
|
-
<td><code>disable</code></td>
|
|
151
|
-
<td>Deaktiviert die Mischeransteuerung und fährt die Position an, die als Aus-Modus festgelegt ist.</td>
|
|
152
|
-
</tr>
|
|
153
|
-
<tr>
|
|
154
|
-
<td><code>set_state</code></td>
|
|
155
|
-
<td>Aktiviert das Weiterleiten, wenn <code>msg.payload = true</code> oder deaktiviert das Weiterleiten, wenn <code>msg.payload = false</code>.</td>
|
|
156
|
-
</tr>
|
|
157
|
-
<tr>
|
|
158
|
-
<td><code>setpoint</code></td>
|
|
159
|
-
<td>Überschreibt den Sollwert mit <code>msg.payload</code> °C.</td>
|
|
160
|
-
</tr>
|
|
161
|
-
<tr>
|
|
162
|
-
<td><code>off_mode</code></td>
|
|
163
|
-
<td>
|
|
164
|
-
Überschreibt den Aus-Modus mit <code>msg.payload</code>.
|
|
165
|
-
Gültige Werte für <code>msg.payload</code> sind <code>"NOTHING"</code>, <code>"OPEN"</code> und <code>"CLOSE"</code>.
|
|
166
|
-
</td>
|
|
167
|
-
</tr>
|
|
168
|
-
<tr>
|
|
169
|
-
<td><code>valve_mode</code></td>
|
|
170
|
-
<td>
|
|
171
|
-
Überschreibt den Modus mit <code>msg.payload</code>.
|
|
172
|
-
Gültige Werte für <code>msg.payload</code> sind <code>"HEATING"</code> und <code>"COOLING"</code>.
|
|
173
|
-
</td>
|
|
174
|
-
</tr>
|
|
175
|
-
<tr>
|
|
176
|
-
<td><code>current_temperature</code></td>
|
|
177
|
-
<td>Setzt die aktuelle Temperatur auf <code>msg.payload</code> °C.</td>
|
|
178
|
-
</tr>
|
|
179
|
-
<tr>
|
|
180
|
-
<td><code>calibrate</code></td>
|
|
181
|
-
<td>Erzwingt eine Kalibrierungsfahrt.</td>
|
|
182
|
-
</tr>
|
|
183
|
-
</tbody>
|
|
184
|
-
</table>
|
|
185
|
-
</p>
|
|
186
122
|
</script>
|
|
@@ -7,11 +7,25 @@ module.exports = function (RED)
|
|
|
7
7
|
const node = this;
|
|
8
8
|
RED.nodes.createNode(node, config);
|
|
9
9
|
|
|
10
|
+
|
|
11
|
+
// ###################
|
|
12
|
+
// # Class constants #
|
|
13
|
+
// ###################
|
|
14
|
+
const ADJUST_OPEN = 0;
|
|
15
|
+
const ADJUST_CLOSE = 1;
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
// #######################
|
|
19
|
+
// # Global help objects #
|
|
20
|
+
// #######################
|
|
10
21
|
const smart_context = require("../persistence.js")(RED);
|
|
11
22
|
const helper = require("../smart_helper.js");
|
|
12
23
|
|
|
13
|
-
|
|
14
|
-
|
|
24
|
+
|
|
25
|
+
// #####################
|
|
26
|
+
// # persistent values #
|
|
27
|
+
// #####################
|
|
28
|
+
var node_settings = helper.cloneObject({
|
|
15
29
|
enabled: config.enabled,
|
|
16
30
|
setpoint: config.setpoint,
|
|
17
31
|
off_mode: config.off_mode,
|
|
@@ -19,21 +33,46 @@ module.exports = function (RED)
|
|
|
19
33
|
last_position: null
|
|
20
34
|
}, smart_context.get(node.id));
|
|
21
35
|
|
|
22
|
-
|
|
36
|
+
|
|
37
|
+
// ##################
|
|
38
|
+
// # Dynamic config #
|
|
39
|
+
// ##################
|
|
23
40
|
let time_total = config.time_total;
|
|
24
41
|
let time_sampling = config.time_sampling;
|
|
25
42
|
|
|
26
|
-
|
|
43
|
+
|
|
44
|
+
// ##################
|
|
45
|
+
// # Runtime values #
|
|
46
|
+
// ##################
|
|
47
|
+
|
|
48
|
+
// Here the setInterval return value is stored which is used to recheck the valve position
|
|
27
49
|
let sampling_interval = null;
|
|
50
|
+
|
|
51
|
+
// Here the setTimeout return value is stored to stop the calibration time.
|
|
28
52
|
let calibration_timeout = null;
|
|
53
|
+
|
|
54
|
+
// Here the setTimeout return value is stored to stop the valve adjustment.
|
|
29
55
|
let changing_timeout = null;
|
|
30
|
-
|
|
56
|
+
|
|
57
|
+
// Store the direction of the adjustment
|
|
58
|
+
let adjusting = null;
|
|
59
|
+
|
|
60
|
+
// Stores the current temperature which is used to determine the adjusting direction
|
|
31
61
|
let current_temperature = null;
|
|
32
|
-
let changing_start_time = null;
|
|
33
62
|
|
|
63
|
+
// The start time of the adjustment to count the change
|
|
64
|
+
let adjusting_start_time = null;
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
// ###############
|
|
68
|
+
// # Node events #
|
|
69
|
+
// ###############
|
|
34
70
|
node.on("input", function (msg)
|
|
35
71
|
{
|
|
36
72
|
handleTopic(msg);
|
|
73
|
+
|
|
74
|
+
setStatus();
|
|
75
|
+
smart_context.set(node.id, node_settings);
|
|
37
76
|
});
|
|
38
77
|
|
|
39
78
|
node.on("close", function ()
|
|
@@ -53,14 +92,22 @@ module.exports = function (RED)
|
|
|
53
92
|
});
|
|
54
93
|
|
|
55
94
|
|
|
95
|
+
// #####################
|
|
96
|
+
// # Private functions #
|
|
97
|
+
// #####################
|
|
98
|
+
|
|
99
|
+
// This is the main function which handles all topics that was received.
|
|
56
100
|
let handleTopic = msg =>
|
|
57
101
|
{
|
|
58
102
|
let real_topic = helper.getTopicName(msg.topic);
|
|
103
|
+
|
|
104
|
+
if (real_topic == "set_state")
|
|
105
|
+
real_topic = (!!msg.payload) ? "enable" : "disable";
|
|
106
|
+
|
|
59
107
|
switch (real_topic)
|
|
60
108
|
{
|
|
61
109
|
case "enable":
|
|
62
110
|
node_settings.enabled = true;
|
|
63
|
-
smart_context.set(node.id, node_settings);
|
|
64
111
|
|
|
65
112
|
stopChanging();
|
|
66
113
|
startSampling();
|
|
@@ -68,37 +115,20 @@ module.exports = function (RED)
|
|
|
68
115
|
|
|
69
116
|
case "disable":
|
|
70
117
|
node_settings.enabled = false;
|
|
71
|
-
smart_context.set(node.id, node_settings);
|
|
72
118
|
|
|
73
119
|
stopSampling();
|
|
74
120
|
doOffMode();
|
|
75
121
|
break;
|
|
76
122
|
|
|
77
|
-
case "set_state":
|
|
78
|
-
node_settings.enabled = !!msg.payload; // force boolean
|
|
79
|
-
smart_context.set(node.id, node_settings);
|
|
80
|
-
|
|
81
|
-
if (node_settings.enabled)
|
|
82
|
-
{
|
|
83
|
-
startSampling();
|
|
84
|
-
}
|
|
85
|
-
else
|
|
86
|
-
{
|
|
87
|
-
stopSampling();
|
|
88
|
-
doOffMode();
|
|
89
|
-
}
|
|
90
|
-
break;
|
|
91
|
-
|
|
92
123
|
case "setpoint":
|
|
93
124
|
let new_setpoint = parseFloat(msg.payload);
|
|
94
125
|
if (isNaN(new_setpoint) && !isFinite(new_setpoint))
|
|
95
126
|
{
|
|
96
|
-
|
|
127
|
+
console.warn("Invalid payload: " + msg.payload);
|
|
97
128
|
return;
|
|
98
129
|
}
|
|
130
|
+
|
|
99
131
|
node_settings.setpoint = new_setpoint;
|
|
100
|
-
smart_context.set(node.id, node_settings);
|
|
101
|
-
node.status({ fill: "yellow", shape: "ring", text: helper.getCurrentTimeForStatus() + ": New setpoint " + new_setpoint });
|
|
102
132
|
break;
|
|
103
133
|
|
|
104
134
|
case "off_mode":
|
|
@@ -108,11 +138,14 @@ module.exports = function (RED)
|
|
|
108
138
|
case "OPEN":
|
|
109
139
|
case "CLOSE":
|
|
110
140
|
node_settings.off_mode = msg.payload;
|
|
111
|
-
smart_context.set(node.id, node_settings);
|
|
112
141
|
|
|
113
142
|
if (!node_settings.enabled)
|
|
114
143
|
doOffMode();
|
|
115
144
|
break;
|
|
145
|
+
|
|
146
|
+
default:
|
|
147
|
+
console.warn("Invalid off_mode: " + msg.payload);
|
|
148
|
+
return;
|
|
116
149
|
}
|
|
117
150
|
break;
|
|
118
151
|
|
|
@@ -122,12 +155,13 @@ module.exports = function (RED)
|
|
|
122
155
|
case "HEATING":
|
|
123
156
|
case "COOLING":
|
|
124
157
|
node_settings.valve_mode = msg.payload;
|
|
125
|
-
smart_context.set(node.id, node_settings);
|
|
126
158
|
setStatus();
|
|
127
159
|
break;
|
|
160
|
+
|
|
161
|
+
default:
|
|
162
|
+
console.warn("Invalid valve_mode: " + msg.payload);
|
|
163
|
+
return;
|
|
128
164
|
}
|
|
129
|
-
node_settings.enabled = true;
|
|
130
|
-
smart_context.set(node.id, node_settings);
|
|
131
165
|
|
|
132
166
|
startSampling();
|
|
133
167
|
break;
|
|
@@ -136,11 +170,10 @@ module.exports = function (RED)
|
|
|
136
170
|
let new_temp = parseFloat(msg.payload);
|
|
137
171
|
if (isNaN(new_temp) && !isFinite(new_temp))
|
|
138
172
|
{
|
|
139
|
-
|
|
173
|
+
console.warn("Invalid payload for current_temperature: " + msg.payload);
|
|
140
174
|
return;
|
|
141
175
|
}
|
|
142
176
|
current_temperature = new_temp;
|
|
143
|
-
setStatus();
|
|
144
177
|
break;
|
|
145
178
|
|
|
146
179
|
case "calibrate":
|
|
@@ -148,7 +181,7 @@ module.exports = function (RED)
|
|
|
148
181
|
break;
|
|
149
182
|
|
|
150
183
|
default:
|
|
151
|
-
|
|
184
|
+
console.warn("Invalid topic: " + real_topic);
|
|
152
185
|
return;
|
|
153
186
|
}
|
|
154
187
|
}
|
|
@@ -208,55 +241,50 @@ module.exports = function (RED)
|
|
|
208
241
|
}
|
|
209
242
|
|
|
210
243
|
// calculate direction
|
|
211
|
-
let
|
|
244
|
+
let adjustAction = ADJUST_CLOSE;
|
|
212
245
|
if (current_temperature < node_settings.setpoint)
|
|
213
246
|
{
|
|
214
247
|
if (node_settings.valve_mode == "HEATING")
|
|
215
|
-
|
|
248
|
+
adjustAction = ADJUST_OPEN;
|
|
216
249
|
}
|
|
217
250
|
else
|
|
218
251
|
{
|
|
219
252
|
if (node_settings.valve_mode == "COOLING")
|
|
220
|
-
|
|
253
|
+
adjustAction = ADJUST_OPEN;
|
|
221
254
|
}
|
|
222
255
|
|
|
223
256
|
// start moving
|
|
224
|
-
startChanging(
|
|
225
|
-
|
|
226
|
-
setStatus();
|
|
257
|
+
startChanging(adjustAction, moving_time);
|
|
227
258
|
}
|
|
228
259
|
|
|
229
|
-
let startChanging = (
|
|
260
|
+
let startChanging = (adjustAction, time_ms) =>
|
|
230
261
|
{
|
|
231
262
|
stopChanging();
|
|
232
263
|
|
|
233
264
|
// Already oppened/closed
|
|
234
|
-
if (
|
|
265
|
+
if (adjustAction == ADJUST_OPEN && node_settings.last_position == 100)
|
|
235
266
|
return;
|
|
236
|
-
if (
|
|
267
|
+
if (adjustAction == ADJUST_CLOSE && node_settings.last_position == 0)
|
|
237
268
|
return;
|
|
238
269
|
|
|
239
|
-
|
|
240
|
-
if (
|
|
270
|
+
adjusting_start_time = Date.now();
|
|
271
|
+
if (adjustAction == ADJUST_OPEN)
|
|
241
272
|
node.send([{ payload: true }, { payload: false }, null]);
|
|
242
273
|
else
|
|
243
274
|
node.send([{ payload: false }, { payload: true }, null]);
|
|
244
275
|
|
|
245
|
-
|
|
246
|
-
fill: "yellow", shape: "ring", text: helper.getCurrentTimeForStatus() + ": Start " + (do_open ? "opening" : "closing") + " for " + helper.formatMsToStatus(time_ms)
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
changing_open = do_open;
|
|
276
|
+
adjusting = adjustAction;
|
|
250
277
|
changing_timeout = setTimeout(() =>
|
|
251
278
|
{
|
|
252
279
|
changing_timeout = null;
|
|
253
280
|
stopChanging();
|
|
281
|
+
setStatus();
|
|
254
282
|
}, time_ms);
|
|
255
|
-
|
|
256
283
|
}
|
|
257
284
|
|
|
258
285
|
let stopChanging = () =>
|
|
259
286
|
{
|
|
287
|
+
// Stop any runnting timeout
|
|
260
288
|
if (changing_timeout !== null)
|
|
261
289
|
{
|
|
262
290
|
clearTimeout(changing_timeout);
|
|
@@ -264,15 +292,15 @@ module.exports = function (RED)
|
|
|
264
292
|
}
|
|
265
293
|
|
|
266
294
|
// No changing in progress
|
|
267
|
-
if (
|
|
295
|
+
if (adjusting_start_time == null)
|
|
268
296
|
return;
|
|
269
297
|
|
|
270
298
|
// Calculate moved percentage
|
|
271
|
-
let time_passed = (Date.now() -
|
|
272
|
-
|
|
299
|
+
let time_passed = (Date.now() - adjusting_start_time) / 1000;
|
|
300
|
+
adjusting_start_time = null;
|
|
273
301
|
|
|
274
302
|
let changed_value = (time_passed / time_total) * 100; // calculate in % value (0-100)
|
|
275
|
-
if (
|
|
303
|
+
if (adjusting == ADJUST_OPEN)
|
|
276
304
|
node_settings.last_position += changed_value;
|
|
277
305
|
else
|
|
278
306
|
node_settings.last_position -= changed_value;
|
|
@@ -280,9 +308,6 @@ module.exports = function (RED)
|
|
|
280
308
|
// Only values from 0 to 100 are allowed
|
|
281
309
|
node_settings.last_position = Math.min(Math.max(node_settings.last_position, 0), 100);
|
|
282
310
|
|
|
283
|
-
// Save state
|
|
284
|
-
smart_context.set(node.id, node_settings);
|
|
285
|
-
|
|
286
311
|
node.send([{ payload: false }, { payload: false }, { payload: node_settings.last_position.toFixed(1) }]);
|
|
287
312
|
|
|
288
313
|
setStatus();
|
|
@@ -302,6 +327,7 @@ module.exports = function (RED)
|
|
|
302
327
|
|
|
303
328
|
// Calibration finished, start sampling if enabled
|
|
304
329
|
calibration_timeout = null;
|
|
330
|
+
|
|
305
331
|
if (node_settings.enabled)
|
|
306
332
|
startSampling();
|
|
307
333
|
else
|
|
@@ -309,8 +335,6 @@ module.exports = function (RED)
|
|
|
309
335
|
|
|
310
336
|
setStatus();
|
|
311
337
|
}, time_total * 1000);
|
|
312
|
-
|
|
313
|
-
setStatus();
|
|
314
338
|
}
|
|
315
339
|
|
|
316
340
|
let doOffMode = () =>
|
|
@@ -318,11 +342,11 @@ module.exports = function (RED)
|
|
|
318
342
|
switch (node_settings.off_mode)
|
|
319
343
|
{
|
|
320
344
|
case "OPEN":
|
|
321
|
-
startChanging(
|
|
345
|
+
startChanging(ADJUST_OPEN, time_total * 1000);
|
|
322
346
|
break;
|
|
323
347
|
|
|
324
348
|
case "CLOSE":
|
|
325
|
-
startChanging(
|
|
349
|
+
startChanging(ADJUST_CLOSE, time_total * 1000);
|
|
326
350
|
break;
|
|
327
351
|
|
|
328
352
|
case "NOTHING":
|
|
@@ -335,6 +359,8 @@ module.exports = function (RED)
|
|
|
335
359
|
{
|
|
336
360
|
if (calibration_timeout !== null)
|
|
337
361
|
node.status({ fill: "yellow", shape: "ring", text: helper.getCurrentTimeForStatus() + ": In calibration" });
|
|
362
|
+
else if (changing_timeout != null)
|
|
363
|
+
node.status({ fill: node_settings.enabled ? "green" : "red", shape: "ring", text: helper.getCurrentTimeForStatus() + ": " + (node_settings.valve_mode == "HEATING" ? "🔥" : "❄️") + " " + (adjusting == ADJUST_OPEN ? "Opening" : "Closing") + ", Set: " + node_settings.setpoint?.toFixed(1) + "°C, Cur: " + current_temperature?.toFixed(1) + "°C, Pos: " + node_settings.last_position?.toFixed(1) + "%" });
|
|
338
364
|
else
|
|
339
365
|
node.status({ fill: node_settings.enabled ? "green" : "red", shape: "dot", text: helper.getCurrentTimeForStatus() + ": " + (node_settings.valve_mode == "HEATING" ? "🔥" : "❄️") + " Set: " + node_settings.setpoint?.toFixed(1) + "°C, Cur: " + current_temperature?.toFixed(1) + "°C, Pos: " + node_settings.last_position?.toFixed(1) + "%" });
|
|
340
366
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<script type="text/html" data-help-name="smart_multi-press-control">
|
|
2
|
+
<p>Diese Node zählt mit, wie oft eine bestimmte Taste gedrückt wurde. Ein weiterer Tastendruck muss innerhalb der eingestellten Zeit erfolgen.</p>
|
|
3
|
+
<p>Wenn nach der eingestellten Zeit kein weiterer Tastendruck vorgenommen wird, so wird eine Nachricht mit dem eingestellen Inhalt an den entsprechenden Ausgang geschickt.</p>
|
|
4
|
+
<p>Ist die maximale Anzahl an Tastendrücken erreicht, wird sofort eine Nachricht rausgeschickt. Weitere Tastendrücke werden wieder von vorne gezählt und ausgewertet.</p>
|
|
5
|
+
</script>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"multi-press": {
|
|
3
|
+
"ui": {
|
|
4
|
+
"name": "Name",
|
|
5
|
+
"count_presses": "Anzahl Tastendrücke",
|
|
6
|
+
"count_presses_placeholder": "Max Anzahl an Tastendrücke",
|
|
7
|
+
"time_interval": "Zeitabstand",
|
|
8
|
+
"time_interval_placeholder": "Max Zeit in ms zwischen Tastendruck",
|
|
9
|
+
"output_messages": "Ausgangsnachrichten"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<script type="text/html" data-help-name="smart_multi-press-control">
|
|
2
|
+
<p>This node counts how often a certain key has been pressed. A further key press must be made within the set time.</p>
|
|
3
|
+
<p>If no further key press is made after the set time, a message with the set content is sent to the corresponding output.</p>
|
|
4
|
+
<p>If the maximum number of key presses is reached, a message is sent out immediately. Further key presses are counted and evaluated from the beginning again.</p>
|
|
5
|
+
</script>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"multi-press": {
|
|
3
|
+
"ui": {
|
|
4
|
+
"name": "Name",
|
|
5
|
+
"count_presses": "Number of keypresses",
|
|
6
|
+
"count_presses_placeholder": "Max number of keypresses",
|
|
7
|
+
"time_interval": "Time interval",
|
|
8
|
+
"time_interval_placeholder": "Max time in ms between keypresses",
|
|
9
|
+
"output_messages": "Output messages"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
},
|
|
26
26
|
oneditprepare: function ()
|
|
27
27
|
{
|
|
28
|
+
let node = this;
|
|
29
|
+
|
|
28
30
|
$("#node-input-outputs").spinner({
|
|
29
31
|
min: 2,
|
|
30
32
|
max: 4,
|
|
@@ -96,20 +98,20 @@
|
|
|
96
98
|
|
|
97
99
|
<script type="text/html" data-template-name="smart_multi-press-control">
|
|
98
100
|
<div class="form-row">
|
|
99
|
-
<label for="node-input-name"><i class="fa fa-tag"></i>
|
|
100
|
-
<input type="text" id="node-input-name"
|
|
101
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="multi-press.ui.name"></span></label>
|
|
102
|
+
<input type="text" id="node-input-name" data-i18n="[placeholder]multi-press.ui.name" />
|
|
101
103
|
</div>
|
|
102
104
|
<div class="form-row">
|
|
103
|
-
<label for="node-input-outputs" style="vertical-align: middle"><i class="fa fa-hand-o-up"></i>
|
|
104
|
-
<input id="node-input-outputs"
|
|
105
|
+
<label for="node-input-outputs" style="vertical-align: middle"><i class="fa fa-hand-o-up"></i> <span data-i18n="multi-press.ui.count_presses"></span></label>
|
|
106
|
+
<input id="node-input-outputs" data-i18n="[placeholder]multi-press.ui.count_presses_placeholder" placeholder="" />
|
|
105
107
|
</div>
|
|
106
108
|
<div class="form-row">
|
|
107
|
-
<label for="node-input-press_delay_ms"><i class="fa fa-clock-o"></i>
|
|
108
|
-
<input id="node-input-press_delay_ms"
|
|
109
|
+
<label for="node-input-press_delay_ms"><i class="fa fa-clock-o"></i> <span data-i18n="multi-press.ui.time_interval"></span></label>
|
|
110
|
+
<input id="node-input-press_delay_ms" data-i18n="[placeholder]multi-press.ui.time_interval_placeholder" placeholder="" />
|
|
109
111
|
<span> ms</span>
|
|
110
112
|
</div>
|
|
111
113
|
<hr/>
|
|
112
|
-
<h4 style="margin: 0.5rem 0;"
|
|
114
|
+
<h4 style="margin: 0.5rem 0;" data-i18n="multi-press.ui.output_messages"></h4>
|
|
113
115
|
<div class="form-row">
|
|
114
116
|
<label for="node-input-out1"><i class="fa fa-hand-o-up"></i> 1x</label>
|
|
115
117
|
<input type="text" id="node-input-out1" />
|
|
@@ -126,10 +128,4 @@
|
|
|
126
128
|
<label for="node-input-out4"><i class="fa fa-hand-o-up"></i> 4x</label>
|
|
127
129
|
<input type="text" id="node-input-out4" />
|
|
128
130
|
</div>
|
|
129
|
-
</script>
|
|
130
|
-
|
|
131
|
-
<script type="text/html" data-help-name="smart_multi-press-control">
|
|
132
|
-
<p>Diese Node zählt mit, wie oft eine bestimmte Taste gedrückt wurde. Ein weiterer Tastendruck muss innerhalb der eingestellten Zeit erfolgen.</p>
|
|
133
|
-
<p>Wenn nach der eingestellten Zeit kein weiterer Tastendruck vorgenommen wird, so wird eine Nachricht mit dem eingestellen Inhalt an den entsprechenden Ausgang geschickt.</p>
|
|
134
|
-
<p>Ist die maximale Anzahl an Tastendrücken erreicht, wird sofort eine Nachricht rausgeschickt. Weitere Tastendrücke werden wieder von vorne gezählt und ausgewertet.</p>
|
|
135
131
|
</script>
|
|
@@ -7,9 +7,27 @@ module.exports = function (RED)
|
|
|
7
7
|
const node = this;
|
|
8
8
|
RED.nodes.createNode(node, config);
|
|
9
9
|
|
|
10
|
+
|
|
11
|
+
// ###################
|
|
12
|
+
// # Class constants #
|
|
13
|
+
// ###################
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
// #######################
|
|
17
|
+
// # Global help objects #
|
|
18
|
+
// #######################
|
|
19
|
+
const smart_context = require("../persistence.js")(RED);
|
|
10
20
|
const helper = require("../smart_helper.js");
|
|
11
21
|
|
|
12
|
-
|
|
22
|
+
|
|
23
|
+
// #####################
|
|
24
|
+
// # persistent values #
|
|
25
|
+
// #####################
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
// ##################
|
|
29
|
+
// # Dynamic config #
|
|
30
|
+
// ##################
|
|
13
31
|
let press_delay_ms = parseInt(config.press_delay_ms || 200, 10);
|
|
14
32
|
let outs = [
|
|
15
33
|
helper.evaluateNodeProperty(RED, config.out1, "json"),
|
|
@@ -18,12 +36,21 @@ module.exports = function (RED)
|
|
|
18
36
|
helper.evaluateNodeProperty(RED, config.out4, "json")
|
|
19
37
|
];
|
|
20
38
|
|
|
21
|
-
|
|
39
|
+
|
|
40
|
+
// ##################
|
|
41
|
+
// # Runtime values #
|
|
42
|
+
// ##################
|
|
43
|
+
|
|
44
|
+
// Count the number of current presses
|
|
22
45
|
let count = 0;
|
|
46
|
+
|
|
47
|
+
// Here the setTimeout return value is stored to detect end of multipress.
|
|
23
48
|
let timeout = null;
|
|
24
49
|
|
|
25
|
-
node.status({});
|
|
26
50
|
|
|
51
|
+
// ###############
|
|
52
|
+
// # Node events #
|
|
53
|
+
// ###############
|
|
27
54
|
node.on("input", function (msg)
|
|
28
55
|
{
|
|
29
56
|
if (msg.payload)
|
|
@@ -39,6 +66,13 @@ module.exports = function (RED)
|
|
|
39
66
|
}
|
|
40
67
|
});
|
|
41
68
|
|
|
69
|
+
|
|
70
|
+
// #####################
|
|
71
|
+
// # Private functions #
|
|
72
|
+
// #####################
|
|
73
|
+
/**
|
|
74
|
+
* This function start/restarts the timer and increase the counter by one.
|
|
75
|
+
*/
|
|
42
76
|
let startTimeout = () =>
|
|
43
77
|
{
|
|
44
78
|
count++;
|
|
@@ -49,18 +83,32 @@ module.exports = function (RED)
|
|
|
49
83
|
timeout = null;
|
|
50
84
|
}
|
|
51
85
|
|
|
86
|
+
// If maximum presses are detected, send result immediately
|
|
87
|
+
// Otherwise start timeout
|
|
52
88
|
if (count == config.outputs)
|
|
53
89
|
sendResult();
|
|
54
90
|
else
|
|
55
|
-
timeout = setTimeout(
|
|
91
|
+
timeout = setTimeout(() =>
|
|
92
|
+
{
|
|
93
|
+
timeout = null;
|
|
94
|
+
sendResult();
|
|
95
|
+
}, press_delay_ms);
|
|
56
96
|
}
|
|
57
97
|
|
|
98
|
+
/**
|
|
99
|
+
* This function is called if the number of presses should be send to the output.
|
|
100
|
+
*/
|
|
58
101
|
let sendResult = () =>
|
|
59
102
|
{
|
|
60
103
|
node.status({ fill: "green", shape: "dot", text: helper.getCurrentTimeForStatus() + ": Last was press " + count + " time" + (count == 1 ? "" : "s") });
|
|
104
|
+
|
|
105
|
+
// Prepare data
|
|
61
106
|
let data = Array.from({ length: config.outputs }).fill(null);
|
|
62
|
-
|
|
107
|
+
// Send a copy of the defined json
|
|
108
|
+
data[count - 1] = helper.cloneObject(outs[count - 1]);
|
|
63
109
|
node.send(data);
|
|
110
|
+
|
|
111
|
+
// Reset values
|
|
64
112
|
count = 0;
|
|
65
113
|
timeout = null;
|
|
66
114
|
}
|