smart-nodes 0.3.37 → 0.4.1
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 +67 -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 +39 -14
- 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 -69
- 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 +61 -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 +58 -29
- 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
|
@@ -7,29 +7,62 @@ 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
|
+
// #######################
|
|
10
19
|
const smart_context = require("../persistence.js")(RED);
|
|
11
20
|
const helper = require("../smart_helper.js");
|
|
12
21
|
|
|
13
|
-
|
|
22
|
+
|
|
23
|
+
// ############################
|
|
24
|
+
// # Used from text-exec node #
|
|
25
|
+
// ############################
|
|
14
26
|
if (typeof config.exec_text_names == "string")
|
|
15
27
|
node.exec_text_names = config.exec_text_names.split(",").map(n => n.trim().toLowerCase());
|
|
16
28
|
else
|
|
17
29
|
node.exec_text_names = [];
|
|
18
30
|
|
|
19
|
-
|
|
20
|
-
|
|
31
|
+
|
|
32
|
+
// #####################
|
|
33
|
+
// # persistent values #
|
|
34
|
+
// #####################
|
|
35
|
+
var node_settings = helper.cloneObject({
|
|
21
36
|
last_values: [], // light is on or off for a scene
|
|
22
37
|
}, smart_context.get(node.id));
|
|
23
38
|
|
|
24
|
-
|
|
39
|
+
|
|
40
|
+
// ##################
|
|
41
|
+
// # Dynamic config #
|
|
42
|
+
// ##################
|
|
25
43
|
let max_time_on = helper.getTimeInMs(config.max_time_on, config.max_time_on_unit);
|
|
26
44
|
|
|
27
|
-
//
|
|
28
|
-
|
|
45
|
+
// ##################
|
|
46
|
+
// # Runtime values #
|
|
47
|
+
// ##################
|
|
48
|
+
|
|
49
|
+
// Here the setTimeout return value is stored to turn off the light.
|
|
50
|
+
// That means if it is null, the light will not be turned off automatically.
|
|
51
|
+
let timeout = null;
|
|
52
|
+
|
|
53
|
+
// If isPermanent is true, then a default on time value is ignored
|
|
54
|
+
// Also if the motion sensor turns off, no timeout is started.
|
|
29
55
|
let isPermanent = false;
|
|
30
|
-
let current_timeout_ms = 0;
|
|
31
56
|
|
|
32
|
-
|
|
57
|
+
|
|
58
|
+
// Here the date is stored, when the light should go off.
|
|
59
|
+
// This is used to calculate the node status.
|
|
60
|
+
let timeout_end_date = null;
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
// #########################
|
|
64
|
+
// # Central node handling #
|
|
65
|
+
// #########################
|
|
33
66
|
var event = "node:" + config.id;
|
|
34
67
|
var handler = function (msg)
|
|
35
68
|
{
|
|
@@ -38,14 +71,14 @@ module.exports = function (RED)
|
|
|
38
71
|
RED.events.on(event, handler);
|
|
39
72
|
|
|
40
73
|
|
|
74
|
+
// Per default expect that all outputs are off
|
|
41
75
|
if (node_settings.last_values.length != config.scenes.length)
|
|
42
|
-
{
|
|
43
|
-
// Per default expect that all outputs are off
|
|
44
76
|
node_settings.last_values = new Array(config.outputs).fill(false);
|
|
45
|
-
}
|
|
46
77
|
|
|
47
|
-
node.status({});
|
|
48
78
|
|
|
79
|
+
// ###############
|
|
80
|
+
// # Node events #
|
|
81
|
+
// ###############
|
|
49
82
|
node.on("input", function (msg)
|
|
50
83
|
{
|
|
51
84
|
handleTopic(msg);
|
|
@@ -54,7 +87,7 @@ module.exports = function (RED)
|
|
|
54
87
|
if (getCurrentScene() != 0)
|
|
55
88
|
startAutoOffIfNeeded(helper.getTimeInMsFromString(msg.time_on ?? max_time_on));
|
|
56
89
|
|
|
57
|
-
|
|
90
|
+
setStatus();
|
|
58
91
|
smart_context.set(node.id, node_settings);
|
|
59
92
|
});
|
|
60
93
|
|
|
@@ -64,6 +97,12 @@ module.exports = function (RED)
|
|
|
64
97
|
RED.events.off(event, handler);
|
|
65
98
|
});
|
|
66
99
|
|
|
100
|
+
|
|
101
|
+
// #####################
|
|
102
|
+
// # Private functions #
|
|
103
|
+
// #####################
|
|
104
|
+
|
|
105
|
+
// This is the main function which handles all topics that was received.
|
|
67
106
|
let handleTopic = msg =>
|
|
68
107
|
{
|
|
69
108
|
let currentScene = getCurrentScene();
|
|
@@ -211,7 +250,16 @@ module.exports = function (RED)
|
|
|
211
250
|
timeMs = max_time_on;
|
|
212
251
|
}
|
|
213
252
|
|
|
214
|
-
|
|
253
|
+
// calculate end date for status message
|
|
254
|
+
if (timeMs > 0)
|
|
255
|
+
{
|
|
256
|
+
timeout_end_date = new Date();
|
|
257
|
+
timeout_end_date.setMilliseconds(timeout_end_date.getMilliseconds() + timeMs);
|
|
258
|
+
}
|
|
259
|
+
else
|
|
260
|
+
{
|
|
261
|
+
timeout_end_date = null;
|
|
262
|
+
}
|
|
215
263
|
|
|
216
264
|
// Stop if any timeout is set
|
|
217
265
|
stopAutoOff();
|
|
@@ -220,35 +268,36 @@ module.exports = function (RED)
|
|
|
220
268
|
if (timeMs <= 0 || isPermanent || getCurrentScene() == 0)
|
|
221
269
|
return;
|
|
222
270
|
|
|
223
|
-
|
|
271
|
+
timeout = setTimeout(() =>
|
|
224
272
|
{
|
|
273
|
+
timeout = null;
|
|
225
274
|
node_settings.last_values = new Array(config.outputs).fill(false);
|
|
226
275
|
node.send(node_settings.last_values.map(val => { return { payload: val }; }));
|
|
227
276
|
notifyCentral();
|
|
228
277
|
|
|
229
|
-
|
|
278
|
+
setStatus();
|
|
230
279
|
smart_context.set(node.id, node_settings);
|
|
231
280
|
}, timeMs);
|
|
232
281
|
}
|
|
233
282
|
|
|
234
283
|
let stopAutoOff = () =>
|
|
235
284
|
{
|
|
236
|
-
if (
|
|
285
|
+
if (timeout != null)
|
|
237
286
|
{
|
|
238
|
-
clearTimeout(
|
|
239
|
-
|
|
287
|
+
clearTimeout(timeout);
|
|
288
|
+
timeout = null;
|
|
240
289
|
}
|
|
241
290
|
}
|
|
242
291
|
|
|
243
|
-
let
|
|
292
|
+
let setStatus = () =>
|
|
244
293
|
{
|
|
245
294
|
let scene = getCurrentScene();
|
|
246
295
|
if (scene != 0)
|
|
247
296
|
{
|
|
248
|
-
if (isPermanent ||
|
|
297
|
+
if (isPermanent || timeout_end_date == null)
|
|
249
298
|
node.status({ fill: "green", shape: "dot", text: helper.getCurrentTimeForStatus() + ": Scene " + scene + " active" });
|
|
250
|
-
else if (
|
|
251
|
-
node.status({ fill: "yellow", shape: "ring", text: helper.getCurrentTimeForStatus() + ": Scene " + scene + " active, wait " + helper.
|
|
299
|
+
else if (timeout)
|
|
300
|
+
node.status({ fill: "yellow", shape: "ring", text: helper.getCurrentTimeForStatus() + ": Scene " + scene + " active, wait " + helper.formatDateToStatus(timeout_end_date, "until") + " for auto off" });
|
|
252
301
|
}
|
|
253
302
|
else
|
|
254
303
|
{
|
|
@@ -265,8 +314,8 @@ module.exports = function (RED)
|
|
|
265
314
|
|
|
266
315
|
config.links.forEach(link =>
|
|
267
316
|
{
|
|
268
|
-
|
|
269
|
-
|
|
317
|
+
helper.log(node.id + " -> " + link);
|
|
318
|
+
helper.log({ source: node.id, state: state });
|
|
270
319
|
RED.events.emit("node:" + link, { source: node.id, state: state });
|
|
271
320
|
});
|
|
272
321
|
}
|
|
@@ -275,7 +324,8 @@ module.exports = function (RED)
|
|
|
275
324
|
if (getCurrentScene() != 0)
|
|
276
325
|
startAutoOffIfNeeded(helper.getTimeInMsFromString(max_time_on));
|
|
277
326
|
|
|
278
|
-
|
|
327
|
+
setStatus();
|
|
279
328
|
}
|
|
329
|
+
|
|
280
330
|
RED.nodes.registerType("smart_scene-control", SceneControlNode);
|
|
281
331
|
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script type="text/html" data-help-name="smart_scheduler">
|
|
2
|
+
<p>
|
|
3
|
+
Diese Node sendet zu den angegebenen Zeitpunkten die entsprechenden Nachrichten an den Ausgang.
|
|
4
|
+
</p>
|
|
5
|
+
<p>
|
|
6
|
+
Folgende topics werden akzeptiert:
|
|
7
|
+
<table>
|
|
8
|
+
<thead>
|
|
9
|
+
<tr>
|
|
10
|
+
<th>Topic</th>
|
|
11
|
+
<th>Beschreibung</th>
|
|
12
|
+
</tr>
|
|
13
|
+
</thead>
|
|
14
|
+
<tbody>
|
|
15
|
+
<tr>
|
|
16
|
+
<td><code>enable</code></td>
|
|
17
|
+
<td>Aktiviert den Scheduler.</td>
|
|
18
|
+
</tr>
|
|
19
|
+
<tr>
|
|
20
|
+
<td><code>disable</code></td>
|
|
21
|
+
<td>Deaktiviert den Scheduler.</td>
|
|
22
|
+
</tr>
|
|
23
|
+
<tr>
|
|
24
|
+
<td><code>set_state</code></td>
|
|
25
|
+
<td>Aktiviert den Scheduler, wenn <code>msg.payload = true</code> oder deaktiviert den Scheduler, wenn <code>msg.payload = false</code>.</td>
|
|
26
|
+
</tr>
|
|
27
|
+
</tbody>
|
|
28
|
+
</table>
|
|
29
|
+
</p>
|
|
30
|
+
</script>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"scheduler": {
|
|
3
|
+
"ui": {
|
|
4
|
+
"name": "Name",
|
|
5
|
+
"scheduler_enabled": "Scheduler aktiviert",
|
|
6
|
+
"schedules": "Schedules",
|
|
7
|
+
|
|
8
|
+
"system_start": "Systemstart",
|
|
9
|
+
"save_state": "Zustand speichern",
|
|
10
|
+
"send_after_start": "Letze Nachricht 10 Sekunden nach dem Start senden",
|
|
11
|
+
|
|
12
|
+
"monday": "Montag",
|
|
13
|
+
"tuesday": "Dienstag",
|
|
14
|
+
"wednesday": "Mittwoch",
|
|
15
|
+
"thursday": "Donnerstag",
|
|
16
|
+
"friday": "Freitag",
|
|
17
|
+
"saturday": "Samstag",
|
|
18
|
+
"sunday": "Sonntag"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script type="text/html" data-help-name="smart_scheduler">
|
|
2
|
+
<p>
|
|
3
|
+
This node sends the corresponding messages to the output at the specified times.
|
|
4
|
+
</p>
|
|
5
|
+
<p>
|
|
6
|
+
The following topics are accepted:
|
|
7
|
+
<table>
|
|
8
|
+
<thead>
|
|
9
|
+
<tr>
|
|
10
|
+
<th>Topic</th>
|
|
11
|
+
<th>Description</th>
|
|
12
|
+
</tr>
|
|
13
|
+
</thead>
|
|
14
|
+
<tbody>
|
|
15
|
+
<tr>
|
|
16
|
+
<td><code>enable</code></td>
|
|
17
|
+
<td>Enables the scheduler.</td>
|
|
18
|
+
</tr>
|
|
19
|
+
<tr>
|
|
20
|
+
<td><code>disable</code></td>
|
|
21
|
+
<td>Disables the scheduler.</td>
|
|
22
|
+
</tr>
|
|
23
|
+
<tr>
|
|
24
|
+
<td><code>set_state</code></td>
|
|
25
|
+
<td>Enables the scheduler if <code>msg.payload = true</code> or disables the scheduler if <code>msg.payload = false</code>.</td>
|
|
26
|
+
</tr>
|
|
27
|
+
</tbody>
|
|
28
|
+
</table>
|
|
29
|
+
</p>
|
|
30
|
+
</script>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"scheduler": {
|
|
3
|
+
"ui": {
|
|
4
|
+
"name": "Name",
|
|
5
|
+
"scheduler_enabled": "Scheduler enabled",
|
|
6
|
+
"schedules": "Schedules",
|
|
7
|
+
|
|
8
|
+
"system_start": "System start",
|
|
9
|
+
"save_state": "Save state",
|
|
10
|
+
"send_after_start": "Send last message 10 seconds after start",
|
|
11
|
+
|
|
12
|
+
"monday": "Monday",
|
|
13
|
+
"tuesday": "Tuesday",
|
|
14
|
+
"wednesday": "Wednesday",
|
|
15
|
+
"thursday": "Thursday",
|
|
16
|
+
"friday": "Friday",
|
|
17
|
+
"saturday": "Saturday",
|
|
18
|
+
"sunday": "Sunday"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
package/scheduler/scheduler.html
CHANGED
|
@@ -1,31 +1,3 @@
|
|
|
1
|
-
<script type="text/html" data-template-name="smart_scheduler">
|
|
2
|
-
<div class="form-row">
|
|
3
|
-
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
|
|
4
|
-
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name" />
|
|
5
|
-
</div>
|
|
6
|
-
<div class="form-row">
|
|
7
|
-
<label for="node-input-enabled"></label>
|
|
8
|
-
<input type="checkbox" id="node-input-enabled" style="width: 20px;" />
|
|
9
|
-
<label for="node-input-enabled" style="width: 200px;">Scheduler aktiviert</label>
|
|
10
|
-
</div>
|
|
11
|
-
<div class="form-row" style="margin-bottom: 2px;">
|
|
12
|
-
<p class="text-center"><i class="fa fa-list"></i> <strong>Schedules</strong></p>
|
|
13
|
-
</div>
|
|
14
|
-
<div class="form-row node-scheduler-schedules-row">
|
|
15
|
-
<ol id="node-scheduler-schedules"></ol>
|
|
16
|
-
</div>
|
|
17
|
-
<hr/>
|
|
18
|
-
<h4 style="margin: 0.5rem 0;">Systemstart</h4>
|
|
19
|
-
<div class="form-row">
|
|
20
|
-
<input type="checkbox" id="node-input-save_state" style="width: 20px;" />
|
|
21
|
-
<label for="node-input-save_state" style="width: calc(100% - 30px);">Zustand speichern</label>
|
|
22
|
-
</div>
|
|
23
|
-
<div class="form-row" id="resend_on_start_row">
|
|
24
|
-
<input type="checkbox" id="node-input-resend_on_start" style="width: 20px;" />
|
|
25
|
-
<label for="node-input-resend_on_start" style="width: calc(100% - 30px);">Letze Nachricht 10 Sekunden nach dem Start senden</label>
|
|
26
|
-
</div>
|
|
27
|
-
</script>
|
|
28
|
-
|
|
29
1
|
<script type="text/html" data-template-scheduler-row="">
|
|
30
2
|
<div>
|
|
31
3
|
<div style="display: inline-block; width: 30px; margin-left: 10px;">
|
|
@@ -224,13 +196,13 @@
|
|
|
224
196
|
default: "",
|
|
225
197
|
multiple: true,
|
|
226
198
|
options: [
|
|
227
|
-
{ value: "1", label: "
|
|
228
|
-
{ value: "2", label: "
|
|
229
|
-
{ value: "3", label: "
|
|
230
|
-
{ value: "4", label: "
|
|
231
|
-
{ value: "5", label: "
|
|
232
|
-
{ value: "6", label: "
|
|
233
|
-
{ value: "0", label: "
|
|
199
|
+
{ value: "1", label: node._("scheduler.ui.monday") },
|
|
200
|
+
{ value: "2", label: node._("scheduler.ui.tuesday") },
|
|
201
|
+
{ value: "3", label: node._("scheduler.ui.wednesday") },
|
|
202
|
+
{ value: "4", label: node._("scheduler.ui.thursday") },
|
|
203
|
+
{ value: "5", label: node._("scheduler.ui.friday") },
|
|
204
|
+
{ value: "6", label: node._("scheduler.ui.saturday") },
|
|
205
|
+
{ value: "0", label: node._("scheduler.ui.sunday") }
|
|
234
206
|
],
|
|
235
207
|
},
|
|
236
208
|
],
|
|
@@ -296,6 +268,7 @@
|
|
|
296
268
|
oneditsave: function ()
|
|
297
269
|
{
|
|
298
270
|
let node = this;
|
|
271
|
+
|
|
299
272
|
node.schedules = [];
|
|
300
273
|
|
|
301
274
|
let scheduleList = $("#node-scheduler-schedules");
|
|
@@ -327,33 +300,30 @@
|
|
|
327
300
|
});
|
|
328
301
|
</script>
|
|
329
302
|
|
|
330
|
-
<script type="text/html" data-
|
|
331
|
-
<
|
|
332
|
-
|
|
333
|
-
<
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
</table>
|
|
357
|
-
</p>
|
|
358
|
-
</p>
|
|
303
|
+
<script type="text/html" data-template-name="smart_scheduler">
|
|
304
|
+
<div class="form-row">
|
|
305
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="scheduler.ui.name"></span></label>
|
|
306
|
+
<input type="text" id="node-input-name" data-i18n="[placeholder]scheduler.ui.name" />
|
|
307
|
+
</div>
|
|
308
|
+
<div class="form-row">
|
|
309
|
+
<label for="node-input-enabled"></label>
|
|
310
|
+
<input type="checkbox" id="node-input-enabled" style="width: 20px;" />
|
|
311
|
+
<label for="node-input-enabled" style="width: 200px;" data-i18n="scheduler.ui.scheduler_enabled"></label>
|
|
312
|
+
</div>
|
|
313
|
+
<div class="form-row" style="margin-bottom: 2px;">
|
|
314
|
+
<p class="text-center"><i class="fa fa-list"></i> <strong data-i18n="scheduler.ui.schedules"></strong></p>
|
|
315
|
+
</div>
|
|
316
|
+
<div class="form-row node-scheduler-schedules-row">
|
|
317
|
+
<ol id="node-scheduler-schedules"></ol>
|
|
318
|
+
</div>
|
|
319
|
+
<hr/>
|
|
320
|
+
<h4 style="margin: 0.5rem 0;" data-i18n="scheduler.ui.system_start"></h4>
|
|
321
|
+
<div class="form-row">
|
|
322
|
+
<input type="checkbox" id="node-input-save_state" style="width: 20px;" />
|
|
323
|
+
<label for="node-input-save_state" style="width: calc(100% - 30px);" data-i18n="scheduler.ui.save_state"></label>
|
|
324
|
+
</div>
|
|
325
|
+
<div class="form-row" id="resend_on_start_row">
|
|
326
|
+
<input type="checkbox" id="node-input-resend_on_start" style="width: 20px;" />
|
|
327
|
+
<label for="node-input-resend_on_start" style="width: calc(100% - 30px);" data-i18n="scheduler.ui.send_after_start"></label>
|
|
328
|
+
</div>
|
|
359
329
|
</script>
|
package/scheduler/scheduler.js
CHANGED
|
@@ -7,29 +7,52 @@ 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
|
+
// #######################
|
|
10
19
|
const smart_context = require("../persistence.js")(RED);
|
|
11
20
|
const helper = require("../smart_helper.js");
|
|
12
21
|
|
|
22
|
+
|
|
23
|
+
// #####################
|
|
24
|
+
// # persistent values #
|
|
25
|
+
// #####################
|
|
13
26
|
var node_settings = {
|
|
14
27
|
enabled: config.enabled,
|
|
15
|
-
|
|
28
|
+
last_message: null,
|
|
16
29
|
};
|
|
17
30
|
|
|
31
|
+
|
|
32
|
+
// load or delete saved values
|
|
18
33
|
if (config.save_state)
|
|
19
|
-
{
|
|
20
|
-
// load old saved values
|
|
21
34
|
node_settings = Object.assign(node_settings, smart_context.get(node.id));
|
|
22
|
-
}
|
|
23
35
|
else
|
|
24
|
-
{
|
|
25
|
-
// delete old saved values
|
|
26
36
|
smart_context.del(node.id);
|
|
27
|
-
}
|
|
28
37
|
|
|
38
|
+
|
|
39
|
+
// ##################
|
|
40
|
+
// # Dynamic config #
|
|
41
|
+
// ##################
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
// ##################
|
|
45
|
+
// # Runtime values #
|
|
46
|
+
// ##################
|
|
47
|
+
|
|
48
|
+
// Here the setTimeout return value is stored when the next timeout should happen
|
|
29
49
|
let timeout = null;
|
|
50
|
+
|
|
51
|
+
// This is date when the next event schould be raised
|
|
30
52
|
let nextEvent = null;
|
|
31
53
|
|
|
32
|
-
|
|
54
|
+
|
|
55
|
+
// Initially prepare schedules config object
|
|
33
56
|
setTimeout(() =>
|
|
34
57
|
{
|
|
35
58
|
for (let i = 0; i < config.schedules.length; i++)
|
|
@@ -47,48 +70,17 @@ module.exports = function (RED)
|
|
|
47
70
|
}, 1000);
|
|
48
71
|
|
|
49
72
|
|
|
73
|
+
// ###############
|
|
74
|
+
// # Node events #
|
|
75
|
+
// ###############
|
|
50
76
|
node.on("input", function (msg)
|
|
51
77
|
{
|
|
52
|
-
|
|
53
|
-
{
|
|
54
|
-
case "enable":
|
|
55
|
-
if (node_settings.enabled)
|
|
56
|
-
return;
|
|
57
|
-
|
|
58
|
-
node_settings.enabled = true;
|
|
59
|
-
if (config.save_state)
|
|
60
|
-
smart_context.set(node.id, node_settings);
|
|
61
|
-
break;
|
|
62
|
-
|
|
63
|
-
case "disable":
|
|
64
|
-
if (!node_settings.enabled)
|
|
65
|
-
return;
|
|
66
|
-
|
|
67
|
-
node_settings.enabled = false;
|
|
68
|
-
if (config.save_state)
|
|
69
|
-
smart_context.set(node.id, node_settings);
|
|
70
|
-
break;
|
|
71
|
-
|
|
72
|
-
case "set_state":
|
|
73
|
-
if (node_settings.enabled == !!msg.payload)
|
|
74
|
-
return;
|
|
75
|
-
|
|
76
|
-
node_settings.enabled = !!msg.payload;
|
|
77
|
-
if (config.save_state)
|
|
78
|
-
smart_context.set(node.id, node_settings);
|
|
79
|
-
break;
|
|
80
|
-
|
|
81
|
-
default:
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (node_settings.enabled)
|
|
86
|
-
initTimeouts();
|
|
87
|
-
else
|
|
88
|
-
clearTimeouts();
|
|
78
|
+
handleTopic(msg);
|
|
89
79
|
|
|
90
80
|
setStatus();
|
|
91
|
-
|
|
81
|
+
|
|
82
|
+
if (config.save_state)
|
|
83
|
+
smart_context.set(node.id, node_settings);
|
|
92
84
|
});
|
|
93
85
|
|
|
94
86
|
node.on("close", function ()
|
|
@@ -100,6 +92,33 @@ module.exports = function (RED)
|
|
|
100
92
|
}
|
|
101
93
|
});
|
|
102
94
|
|
|
95
|
+
|
|
96
|
+
// #####################
|
|
97
|
+
// # Private functions #
|
|
98
|
+
// #####################
|
|
99
|
+
|
|
100
|
+
// This is the main function which handles all topics that was received.
|
|
101
|
+
let handleTopic = msg =>
|
|
102
|
+
{
|
|
103
|
+
let real_topic = helper.getTopicName(msg.topic);
|
|
104
|
+
|
|
105
|
+
if (real_topic == "set_state")
|
|
106
|
+
real_topic = (!!msg.payload) ? "enable" : "disable";
|
|
107
|
+
|
|
108
|
+
switch (real_topic)
|
|
109
|
+
{
|
|
110
|
+
case "enable":
|
|
111
|
+
node_settings.enabled = true;
|
|
112
|
+
initNextTimeout();
|
|
113
|
+
break;
|
|
114
|
+
|
|
115
|
+
case "disable":
|
|
116
|
+
node_settings.enabled = false;
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// calculate which event should occur nect
|
|
103
122
|
let initNextTimeout = () =>
|
|
104
123
|
{
|
|
105
124
|
let minIndex = null;
|
|
@@ -120,16 +139,28 @@ module.exports = function (RED)
|
|
|
120
139
|
return;
|
|
121
140
|
}
|
|
122
141
|
|
|
142
|
+
// Stop timeout if any
|
|
143
|
+
if (timeout != null)
|
|
144
|
+
{
|
|
145
|
+
clearTimeout(timeout);
|
|
146
|
+
timeout = null;
|
|
147
|
+
}
|
|
148
|
+
|
|
123
149
|
nextEvent = config.schedules[minIndex].nextEvent;
|
|
124
150
|
let waitTime = nextEvent.getTime() - (new Date()).getTime();
|
|
125
|
-
timeout = setTimeout(
|
|
151
|
+
timeout = setTimeout(() =>
|
|
152
|
+
{
|
|
153
|
+
timeout = null;
|
|
154
|
+
raiseEvent(minIndex);
|
|
155
|
+
}, waitTime);
|
|
126
156
|
}
|
|
127
157
|
|
|
158
|
+
// calculates the next time when the scheduled i-th entry should run.
|
|
128
159
|
let calcNextEvent = i =>
|
|
129
160
|
{
|
|
130
161
|
const schedule = config.schedules[i];
|
|
131
162
|
|
|
132
|
-
// If no day is checked
|
|
163
|
+
// If no day is checked it is never raised
|
|
133
164
|
if (!schedule.days || schedule.days.length == 0)
|
|
134
165
|
return null;
|
|
135
166
|
|
|
@@ -174,7 +205,7 @@ module.exports = function (RED)
|
|
|
174
205
|
schedule.second
|
|
175
206
|
);
|
|
176
207
|
|
|
177
|
-
//
|
|
208
|
+
// helper.log({
|
|
178
209
|
// i,
|
|
179
210
|
// findNextDay,
|
|
180
211
|
// nextEvent
|
|
@@ -183,6 +214,7 @@ module.exports = function (RED)
|
|
|
183
214
|
return nextEvent;
|
|
184
215
|
}
|
|
185
216
|
|
|
217
|
+
// Send the i-th entry to the output
|
|
186
218
|
let raiseEvent = i =>
|
|
187
219
|
{
|
|
188
220
|
const schedule = config.schedules[i];
|
|
@@ -191,8 +223,8 @@ module.exports = function (RED)
|
|
|
191
223
|
return;
|
|
192
224
|
|
|
193
225
|
timeout = null;
|
|
194
|
-
node.send(schedule.message);
|
|
195
|
-
node_settings.
|
|
226
|
+
node.send(helper.cloneObject(schedule.message));
|
|
227
|
+
node_settings.last_message = schedule.message;
|
|
196
228
|
|
|
197
229
|
if (config.save_state)
|
|
198
230
|
smart_context.set(node.id, node_settings);
|
|
@@ -233,11 +265,11 @@ module.exports = function (RED)
|
|
|
233
265
|
}
|
|
234
266
|
}
|
|
235
267
|
|
|
236
|
-
if (config.save_state && config.resend_on_start && node_settings.
|
|
268
|
+
if (config.save_state && config.resend_on_start && node_settings.last_message != null)
|
|
237
269
|
{
|
|
238
270
|
setTimeout(() =>
|
|
239
271
|
{
|
|
240
|
-
node.send(node_settings.
|
|
272
|
+
node.send(helper.cloneObject(node_settings.last_message));
|
|
241
273
|
}, 10000);
|
|
242
274
|
}
|
|
243
275
|
}
|