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.
Files changed (109) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +35 -17
  3. package/central/central.html +26 -12
  4. package/central/central.js +84 -26
  5. package/central/locales/de-DE/central.json +1 -4
  6. package/central/locales/en-US/central.json +0 -3
  7. package/compare/compare.html +40 -24
  8. package/compare/compare.js +69 -29
  9. package/compare/locales/de-DE/compare.json +5 -7
  10. package/compare/locales/en-US/compare.json +18 -8
  11. package/counter/counter.html +64 -17
  12. package/counter/counter.js +43 -20
  13. package/counter/locales/de-DE/counter.json +6 -9
  14. package/counter/locales/en-US/counter.json +12 -16
  15. package/delay/delay.html +18 -88
  16. package/delay/delay.js +63 -20
  17. package/delay/locales/de-DE/delay.html +71 -0
  18. package/delay/locales/de-DE/delay.json +19 -0
  19. package/delay/locales/en-US/delay.html +76 -0
  20. package/delay/locales/en-US/delay.json +19 -0
  21. package/forwarder/forwarder.html +11 -42
  22. package/forwarder/forwarder.js +59 -18
  23. package/forwarder/locales/de-DE/forwarder.html +32 -0
  24. package/forwarder/locales/de-DE/forwarder.json +15 -0
  25. package/forwarder/locales/en-US/forwarder.html +32 -0
  26. package/forwarder/locales/en-US/forwarder.json +15 -0
  27. package/heating-curve/heating-curve.html +10 -51
  28. package/heating-curve/heating-curve.js +39 -14
  29. package/heating-curve/locales/de-DE/heating-curve.html +38 -0
  30. package/heating-curve/locales/de-DE/heating-curve.json +12 -0
  31. package/heating-curve/locales/en-US/heating-curve.html +38 -0
  32. package/heating-curve/locales/en-US/heating-curve.json +12 -0
  33. package/hysteresis/hysteresis.html +43 -65
  34. package/hysteresis/hysteresis.js +94 -69
  35. package/hysteresis/locales/de-DE/hysteresis.html +36 -0
  36. package/hysteresis/locales/de-DE/hysteresis.json +27 -0
  37. package/hysteresis/locales/en-US/hysteresis.html +36 -0
  38. package/hysteresis/locales/en-US/hysteresis.json +27 -0
  39. package/{light-control/light-control.html → light/light.html} +34 -144
  40. package/{light-control/light-control.js → light/light.js} +151 -32
  41. package/light/locales/de-DE/light.html +149 -0
  42. package/light/locales/de-DE/light.json +24 -0
  43. package/light/locales/en-US/light.html +148 -0
  44. package/light/locales/en-US/light.json +24 -0
  45. package/logic/locales/de-DE/logic.html +12 -0
  46. package/logic/locales/de-DE/logic.json +26 -0
  47. package/logic/locales/en-US/logic.html +12 -0
  48. package/logic/locales/en-US/logic.json +26 -0
  49. package/logic/logic.html +27 -40
  50. package/logic/logic.js +63 -29
  51. package/long-press/locales/de-DE/long-press.html +5 -0
  52. package/long-press/locales/de-DE/long-press.json +13 -0
  53. package/long-press/locales/en-US/long-press.html +5 -0
  54. package/long-press/locales/en-US/long-press.json +13 -0
  55. package/{long-press-control/long-press-control.html → long-press/long-press.html} +10 -14
  56. package/long-press/long-press.js +163 -0
  57. package/mixing-valve/locales/de-DE/mixing-valve.html +65 -0
  58. package/mixing-valve/locales/de-DE/mixing-valve.json +19 -0
  59. package/mixing-valve/locales/en-US/mixing-valve.html +66 -0
  60. package/mixing-valve/locales/en-US/mixing-valve.json +19 -0
  61. package/mixing-valve/mixing-valve.html +15 -79
  62. package/mixing-valve/mixing-valve.js +87 -61
  63. package/multi-press/locales/de-DE/multi-press.html +5 -0
  64. package/multi-press/locales/de-DE/multi-press.json +12 -0
  65. package/multi-press/locales/en-US/multi-press.html +5 -0
  66. package/multi-press/locales/en-US/multi-press.json +12 -0
  67. package/{multi-press-control/multi-press-control.html → multi-press/multi-press.html} +9 -13
  68. package/{multi-press-control/multi-press-control.js → multi-press/multi-press.js} +53 -5
  69. package/package.json +7 -7
  70. package/persistence.js +1 -0
  71. package/scene/locales/de-DE/scene.html +105 -0
  72. package/scene/locales/de-DE/scene.json +21 -0
  73. package/scene/locales/en-US/scene.html +107 -0
  74. package/scene/locales/en-US/scene.json +20 -0
  75. package/{scene-control/scene-control.html → scene/scene.html} +18 -121
  76. package/{scene-control/scene-control.js → scene/scene.js} +76 -26
  77. package/scheduler/locales/de-DE/scheduler.html +30 -0
  78. package/scheduler/locales/de-DE/scheduler.json +21 -0
  79. package/scheduler/locales/en-US/scheduler.html +30 -0
  80. package/scheduler/locales/en-US/scheduler.json +21 -0
  81. package/scheduler/scheduler.html +34 -64
  82. package/scheduler/scheduler.js +85 -53
  83. package/shutter/locales/de-DE/shutter.html +127 -0
  84. package/shutter/locales/de-DE/shutter.json +11 -0
  85. package/shutter/locales/en-US/shutter.html +133 -0
  86. package/shutter/locales/en-US/shutter.json +11 -0
  87. package/{shutter-control/shutter-control.html → shutter/shutter.html} +7 -133
  88. package/{shutter-control/shutter-control.js → shutter/shutter.js} +116 -56
  89. package/shutter-complex/locales/de-DE/shutter-complex.html +120 -0
  90. package/shutter-complex/locales/de-DE/shutter-complex.json +20 -0
  91. package/shutter-complex/locales/en-US/shutter-complex.html +120 -0
  92. package/shutter-complex/locales/en-US/shutter-complex.json +20 -0
  93. package/{shutter-complex-control/shutter-complex-control.html → shutter-complex/shutter-complex.html} +30 -133
  94. package/shutter-complex/shutter-complex.js +578 -0
  95. package/smart_helper.js +61 -9
  96. package/statistic/locales/de-DE/statistic.html +10 -0
  97. package/statistic/locales/de-DE/statistic.json +29 -0
  98. package/statistic/locales/en-US/statistic.html +10 -0
  99. package/statistic/locales/en-US/statistic.json +29 -0
  100. package/statistic/statistic.html +32 -36
  101. package/statistic/statistic.js +58 -29
  102. package/text-exec/locales/de-DE/text-exec.html +18 -0
  103. package/text-exec/locales/de-DE/text-exec.json +7 -0
  104. package/text-exec/locales/en-US/text-exec.html +18 -0
  105. package/text-exec/locales/en-US/text-exec.json +7 -0
  106. package/text-exec/text-exec.html +9 -25
  107. package/text-exec/text-exec.js +43 -2
  108. package/long-press-control/long-press-control.js +0 -76
  109. 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
- // used from text-exec node
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
- // persistent values
20
- var node_settings = Object.assign({}, {
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
- // dynamic config
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
- // runtime values
28
- let max_time_on_timeout = null;
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
- // central handling
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
- status();
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
- current_timeout_ms = timeMs;
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
- max_time_on_timeout = setTimeout(() =>
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
- status();
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 (max_time_on_timeout != null)
285
+ if (timeout != null)
237
286
  {
238
- clearTimeout(max_time_on_timeout);
239
- max_time_on_timeout = null;
287
+ clearTimeout(timeout);
288
+ timeout = null;
240
289
  }
241
290
  }
242
291
 
243
- let status = () =>
292
+ let setStatus = () =>
244
293
  {
245
294
  let scene = getCurrentScene();
246
295
  if (scene != 0)
247
296
  {
248
- if (isPermanent || current_timeout_ms <= 0)
297
+ if (isPermanent || timeout_end_date == null)
249
298
  node.status({ fill: "green", shape: "dot", text: helper.getCurrentTimeForStatus() + ": Scene " + scene + " active" });
250
- else if (max_time_on_timeout)
251
- node.status({ fill: "yellow", shape: "ring", text: helper.getCurrentTimeForStatus() + ": Scene " + scene + " active, wait " + helper.formatMsToStatus(current_timeout_ms, "until") + " for auto off" });
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
- // console.log(node.id + " -> " + link);
269
- // console.log({ source: node.id, state: state });
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
- status();
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
+ }
@@ -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>&nbsp;<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: "Montag" },
228
- { value: "2", label: "Dienstag" },
229
- { value: "3", label: "Mittwoch" },
230
- { value: "4", label: "Donnerstag" },
231
- { value: "5", label: "Freitag" },
232
- { value: "6", label: "Samstag" },
233
- { value: "0", label: "Sonntag" }
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-help-name="smart_scheduler">
331
- <p>
332
- Diese Node sendet zu den angegebenen Zeitpunkten die entsprechenden Nachrichten an den Ausgang.
333
- <p>
334
- Folgende topics werden akzeptiert:
335
- <table>
336
- <thead>
337
- <tr>
338
- <th>Topic</th>
339
- <th>Beschreibung</th>
340
- </tr>
341
- </thead>
342
- <tbody>
343
- <tr>
344
- <td><code>enable</code></td>
345
- <td>Aktiviert den Scheduler.</td>
346
- </tr>
347
- <tr>
348
- <td><code>disable</code></td>
349
- <td>Deaktiviert den Scheduler.</td>
350
- </tr>
351
- <tr>
352
- <td><code>set_state</code></td>
353
- <td>Aktiviert den Scheduler, wenn <code>msg.payload = true</code> oder deaktiviert den Scheduler, wenn <code>msg.payload = false</code>.</td>
354
- </tr>
355
- </tbody>
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>
@@ -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
- lastMessage: null,
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
- // prepare schedules
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
- switch (helper.getTopicName(msg.topic))
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
- smart_context.set(node.id, node_settings);
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(raiseEvent, waitTime, minIndex);
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 then we cannot it is never raised
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
- // console.log({
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.lastMessage = schedule.message;
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.lastMessage != null)
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.lastMessage);
272
+ node.send(helper.cloneObject(node_settings.last_message));
241
273
  }, 10000);
242
274
  }
243
275
  }