smart-nodes 0.3.36 → 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.
Files changed (116) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +35 -17
  3. package/central/central.html +27 -24
  4. package/central/central.js +84 -26
  5. package/central/locales/de-DE/central.html +10 -0
  6. package/central/locales/de-DE/central.json +14 -0
  7. package/central/locales/en-US/central.html +10 -0
  8. package/central/locales/en-US/central.json +14 -0
  9. package/compare/compare.html +64 -87
  10. package/compare/compare.js +69 -29
  11. package/compare/locales/de-DE/compare.html +36 -0
  12. package/compare/locales/de-DE/compare.json +35 -0
  13. package/compare/locales/en-US/compare.html +36 -0
  14. package/compare/locales/en-US/compare.json +35 -0
  15. package/counter/counter.html +70 -72
  16. package/counter/counter.js +43 -20
  17. package/counter/locales/de-DE/counter.html +48 -0
  18. package/counter/locales/de-DE/counter.json +21 -0
  19. package/counter/locales/en-US/counter.html +48 -0
  20. package/counter/locales/en-US/counter.json +21 -0
  21. package/delay/delay.html +30 -102
  22. package/delay/delay.js +63 -20
  23. package/delay/locales/de-DE/delay.html +71 -0
  24. package/delay/locales/de-DE/delay.json +19 -0
  25. package/delay/locales/en-US/delay.html +76 -0
  26. package/delay/locales/en-US/delay.json +19 -0
  27. package/forwarder/forwarder.html +11 -42
  28. package/forwarder/forwarder.js +59 -18
  29. package/forwarder/locales/de-DE/forwarder.html +32 -0
  30. package/forwarder/locales/de-DE/forwarder.json +15 -0
  31. package/forwarder/locales/en-US/forwarder.html +32 -0
  32. package/forwarder/locales/en-US/forwarder.json +15 -0
  33. package/heating-curve/heating-curve.html +10 -51
  34. package/heating-curve/heating-curve.js +38 -13
  35. package/heating-curve/locales/de-DE/heating-curve.html +38 -0
  36. package/heating-curve/locales/de-DE/heating-curve.json +12 -0
  37. package/heating-curve/locales/en-US/heating-curve.html +38 -0
  38. package/heating-curve/locales/en-US/heating-curve.json +12 -0
  39. package/hysteresis/hysteresis.html +49 -69
  40. package/hysteresis/hysteresis.js +94 -68
  41. package/hysteresis/locales/de-DE/hysteresis.html +36 -0
  42. package/hysteresis/locales/de-DE/hysteresis.json +27 -0
  43. package/hysteresis/locales/en-US/hysteresis.html +36 -0
  44. package/hysteresis/locales/en-US/hysteresis.json +27 -0
  45. package/light/light.html +250 -0
  46. package/{light-control/light-control.js → light/light.js} +151 -32
  47. package/light/locales/de-DE/light.html +149 -0
  48. package/light/locales/de-DE/light.json +24 -0
  49. package/light/locales/en-US/light.html +148 -0
  50. package/light/locales/en-US/light.json +24 -0
  51. package/logic/locales/de-DE/logic.html +12 -0
  52. package/logic/locales/de-DE/logic.json +26 -0
  53. package/logic/locales/en-US/logic.html +12 -0
  54. package/logic/locales/en-US/logic.json +26 -0
  55. package/logic/logic.html +48 -64
  56. package/logic/logic.js +63 -29
  57. package/long-press/locales/de-DE/long-press.html +5 -0
  58. package/long-press/locales/de-DE/long-press.json +13 -0
  59. package/long-press/locales/en-US/long-press.html +5 -0
  60. package/long-press/locales/en-US/long-press.json +13 -0
  61. package/{long-press-control/long-press-control.html → long-press/long-press.html} +10 -14
  62. package/long-press/long-press.js +163 -0
  63. package/mixing-valve/locales/de-DE/mixing-valve.html +65 -0
  64. package/mixing-valve/locales/de-DE/mixing-valve.json +19 -0
  65. package/mixing-valve/locales/en-US/mixing-valve.html +66 -0
  66. package/mixing-valve/locales/en-US/mixing-valve.json +19 -0
  67. package/mixing-valve/mixing-valve.html +27 -93
  68. package/mixing-valve/mixing-valve.js +87 -61
  69. package/multi-press/locales/de-DE/multi-press.html +5 -0
  70. package/multi-press/locales/de-DE/multi-press.json +12 -0
  71. package/multi-press/locales/en-US/multi-press.html +5 -0
  72. package/multi-press/locales/en-US/multi-press.json +12 -0
  73. package/{multi-press-control/multi-press-control.html → multi-press/multi-press.html} +9 -13
  74. package/{multi-press-control/multi-press-control.js → multi-press/multi-press.js} +53 -5
  75. package/package.json +7 -7
  76. package/persistence.js +1 -0
  77. package/scene/locales/de-DE/scene.html +105 -0
  78. package/scene/locales/de-DE/scene.json +21 -0
  79. package/scene/locales/en-US/scene.html +107 -0
  80. package/scene/locales/en-US/scene.json +20 -0
  81. package/{scene-control/scene-control.html → scene/scene.html} +30 -132
  82. package/{scene-control/scene-control.js → scene/scene.js} +76 -26
  83. package/scheduler/locales/de-DE/scheduler.html +30 -0
  84. package/scheduler/locales/de-DE/scheduler.json +21 -0
  85. package/scheduler/locales/en-US/scheduler.html +30 -0
  86. package/scheduler/locales/en-US/scheduler.json +21 -0
  87. package/scheduler/scheduler.html +34 -64
  88. package/scheduler/scheduler.js +85 -53
  89. package/shutter/locales/de-DE/shutter.html +127 -0
  90. package/shutter/locales/de-DE/shutter.json +11 -0
  91. package/shutter/locales/en-US/shutter.html +133 -0
  92. package/shutter/locales/en-US/shutter.json +11 -0
  93. package/{shutter-control/shutter-control.html → shutter/shutter.html} +7 -133
  94. package/{shutter-control/shutter-control.js → shutter/shutter.js} +116 -56
  95. package/shutter-complex/locales/de-DE/shutter-complex.html +120 -0
  96. package/shutter-complex/locales/de-DE/shutter-complex.json +20 -0
  97. package/shutter-complex/locales/en-US/shutter-complex.html +120 -0
  98. package/shutter-complex/locales/en-US/shutter-complex.json +20 -0
  99. package/{shutter-complex-control/shutter-complex-control.html → shutter-complex/shutter-complex.html} +36 -140
  100. package/shutter-complex/shutter-complex.js +578 -0
  101. package/smart_helper.js +52 -9
  102. package/statistic/locales/de-DE/statistic.html +10 -0
  103. package/statistic/locales/de-DE/statistic.json +29 -0
  104. package/statistic/locales/en-US/statistic.html +10 -0
  105. package/statistic/locales/en-US/statistic.json +29 -0
  106. package/statistic/statistic.html +38 -43
  107. package/statistic/statistic.js +57 -28
  108. package/text-exec/locales/de-DE/text-exec.html +18 -0
  109. package/text-exec/locales/de-DE/text-exec.json +7 -0
  110. package/text-exec/locales/en-US/text-exec.html +18 -0
  111. package/text-exec/locales/en-US/text-exec.json +7 -0
  112. package/text-exec/text-exec.html +9 -25
  113. package/text-exec/text-exec.js +43 -2
  114. package/light-control/light-control.html +0 -363
  115. package/long-press-control/long-press-control.js +0 -76
  116. package/shutter-complex-control/shutter-complex-control.js +0 -442
@@ -0,0 +1,133 @@
1
+ <script type="text/html" data-help-name="smart_shutter-control">
2
+ <p>
3
+ <b>Note:</b> Smart Nodes use topics in the format <code>name#number</code>, so that different Smart Nodes can be controlled with the same topic.<br />
4
+ This node only uses the part <code>name</code>. <code>#</code> and <code>number</code> are optional.
5
+ </p>
6
+ <p>
7
+ This node controls roller shutters or blinds. There are 3 outputs that can be controlled:
8
+ <ol>
9
+ <li><b>Up/Down:</b> <code>msg.payload = false</code> moves the roller shutter up and <code>msg.payload = true</code> moves the roller shutter down.</li>
10
+ <li><b>Stop:</b> <code>msg.payload = true</code> stops the roller shutter.</li>
11
+ <li><b>Position:</b> <code>msg.payload = 42</code> Moves the roller shutter to 42%.</li>
12
+ </ol>
13
+ The outputs are assigned to the respective KNX group addresses.
14
+ </p>
15
+ <p>
16
+ This node expects the following topics as input:<br />
17
+ <table>
18
+ <thead>
19
+ <tr>
20
+ <th>Topic</th>
21
+ <th>Description</th>
22
+ <th>msg.payload == false is ignored</th>
23
+ </tr>
24
+ </thead>
25
+ <tbody>
26
+ <tr>
27
+ <td><code>status</code> or <code>status_position</code></td>
28
+ <td>
29
+ The node reports the current position.
30
+ This allows the node to determine in which direction a roller shutter last moved.
31
+ This is required for the topic <b>toggle</b> to determine the next direction of travel for a 1-button
32
+ operation.
33
+ </td>
34
+ </tr>
35
+ <tr>
36
+ <td><code>up_down</code></td>
37
+ <td>
38
+ Receives a command from Home Assistant to be informed whether the roller shutter is running and in which
39
+ direction.<br />
40
+ <strong>Important:</strong> This topic <u>does not</u> start the roller shutter, as the group address
41
+ must be directly connected to the blind module.
42
+ </td>
43
+ <td>No</td>
44
+ </tr>
45
+ <tr>
46
+ <td><code>short_up_down</code></td>
47
+ <td>
48
+ Sends a short up command if <code>msg.payload = false</code> and a short down command if
49
+ <code>msg.payload = true</code>.<br />
50
+ The time stored in the module is used. However, this can be overwritten once by setting
51
+ <code>msg.time_on = 1500</code> or <code>msg.time_on = "1.5s"</code>.
52
+ </td>
53
+ <td>No</td>
54
+ </tr>
55
+ <tr>
56
+ <td><code>up</code></td>
57
+ <td>Sends a command to raise the roller shutter if it is not already moving up. If necessary, a stop command
58
+ is sent beforehand.</td>
59
+ <td>Yes</td>
60
+ </tr>
61
+ <tr>
62
+ <td><code>up_stop</code></td>
63
+ <td>Sends a stop and an up command alternately.</td>
64
+ <td>Yes</td>
65
+ </tr>
66
+ <tr>
67
+ <td><code>down</code></td>
68
+ <td>Sends a down command if the roller shutter is not already moving down. If necessary, a stop command is
69
+ sent beforehand.</td>
70
+ <td>Yes</td>
71
+ </tr>
72
+ <tr>
73
+ <td><code>down_stop</code></td>
74
+ <td>Sends a stop and a down command alternately.</td>
75
+ <td>Yes</td>
76
+ </tr>
77
+ <tr>
78
+ <td><code>stop</code></td>
79
+ <td>Sends a stop command.</td>
80
+ <td>Yes</td>
81
+ </tr>
82
+ <tr>
83
+ <td><code>position</code></td>
84
+ <td>Sends a position command. <code>msg.payload</code> should have a value between 0 (open) and 100
85
+ (closed).</td>
86
+ <td>No</td>
87
+ </tr>
88
+ <tr>
89
+ <td><code>toggle</code> (default)</td>
90
+ <td>Switches the roller shutter alternately to up, stop, down, stop.</td>
91
+ <td>Yes</td>
92
+ </tr>
93
+ </tbody>
94
+ </table>
95
+ </p>
96
+ <p>
97
+ This node does not manage a running time for the roller shutter itself. This must be configured for the output via ETS.
98
+ However, it is possible to switch the roller shutter off automatically after a defined time.
99
+ However, it is possible to switch the roller shutter off automatically after a defined time.
100
+ Example: <code>msg = { "topic": "up", "time_on": 5000 }</code> or <code>msg = { "topic": "up", "time_on": "5s" }</code><br />
101
+ This message causes the roller shutter to move up for 5000 milliseconds / 5 seconds. If it is a Venetian blind, the slats are rotated accordingly.
102
+ The following values can be used as a unit for time:
103
+ <table>
104
+ <thead>
105
+ <tr>
106
+ <th>Unit</th>
107
+ <th>Description</th>
108
+ </tr>
109
+ </thead>
110
+ <tbody>
111
+ <tr>
112
+ <td><code>ms</code> (default)</td>
113
+ <td>Milliseconds</td>
114
+ </tr>
115
+ <tr>
116
+ <td><code>s</code> or <code>sec</code></td>
117
+ <td>Seconds</td>
118
+ </tr>
119
+ <tr>
120
+ <td><code>m</code> or <code>min</code></td>
121
+ <td>Mintun.</td>
122
+ </tr>
123
+ <tr>
124
+ <td><code>h</code></td>
125
+ <td>Hours</td>
126
+ </tr>
127
+ </tbody>
128
+ </table>
129
+ </p>
130
+ <p>
131
+ Specifying a time does not work with the topic <b>position</b>.
132
+ </p>
133
+ </script>
@@ -0,0 +1,11 @@
1
+ {
2
+ "shutter": {
3
+ "ui": {
4
+ "name": "Name",
5
+ "text": "Text",
6
+ "controlled_by_words": "This node can be controlled using the words entered. Multiple words are separated by a comma.",
7
+ "time_short": "Time short",
8
+ "controlled_by_central": "This module is controlled by the following central modules:"
9
+ }
10
+ }
11
+ }
@@ -153,6 +153,7 @@
153
153
  oneditprepare: function ()
154
154
  {
155
155
  let node = this;
156
+
156
157
  onEditPrepare(this, ["smart_central-control"]);
157
158
  initTreeList(node, ["smart_central-control"]);
158
159
 
@@ -180,145 +181,18 @@
180
181
 
181
182
  <script type="text/html" data-template-name="smart_shutter-control">
182
183
  <div class="form-row">
183
- <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
184
- <input type="text" id="node-input-name" placeholder="Name" />
184
+ <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="shutter.ui.name"></span></label>
185
+ <input type="text" id="node-input-name" data-i18n="[placeholder]shutter.ui.name" />
185
186
  </div>
186
187
  <div class="form-row">
187
- <label for="node-input-exec_text_names"><i class="fa fa-comments-o"></i> Text</label>
188
+ <label for="node-input-exec_text_names"><i class="fa fa-comments-o"></i> <span data-i18n="shutter.ui.text"></span></label>
188
189
  <input id="node-input-exec_text_names" type="text" />
189
- <div style="max-width: 450px;">Diese Node kann über die eingegebenen Wörter gesteuert werden. Mehrere Wörter werden durch ein Komma getrennt.</div>
190
+ <div style="max-width: 450px;" data-i18n="shutter.ui.controlled_by_words"></div>
190
191
  </div>
191
192
  <div class="form-row">
192
- <label for="node-input-short_time_on_ms"><i class="fa fa-clock-o"></i> Zeit kurz</label>
193
+ <label for="node-input-short_time_on_ms"><i class="fa fa-clock-o"></i> <span data-i18n="shutter.ui.time_short"></span></label>
193
194
  <input id="node-input-short_time_on_ms" placeholder="200" /> ms
194
195
  </div>
195
- <span><i class="fa fa-link"></i>Dieser Baustein wird von folgenden Zentralbausteinen gesteuert:</span>
196
+ <span><i class="fa fa-link"></i> <span data-i18n="shutter.ui.controlled_by_central"></span></span>
196
197
  <div class="form-row node-input-link-row node-input-link-rows"></div>
197
- </script>
198
-
199
- <script type="text/html" data-help-name="smart_shutter-control">
200
- <p>
201
- <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/>
202
- Diese Node verwendet nur den Teil <code>name</code>. <code>#</code> und <code>nummer</code> sind dabei optional.
203
- </p>
204
- <p>
205
- Diese Node steuert Rollladen oder Jalousien.
206
- Es gibt 3 Ausgänge die angesteuert werden können:
207
- <ol>
208
- <li><b>Auf/Ab:</b> <code>msg.payload = false</code> lässt den Rollladen nach oben fahren und <code>msg.payload = true</code> lässt den Rollladen nach unten fahren.</li>
209
- <li><b>Stop:</b> <code>msg.payload = true</code> lässt den Rollladen stoppen.</li>
210
- <li><b>Position:</b> <code>msg.payload = 42</code> Lässt den Rollladen auf 42% fahren.</li>
211
- </ol>
212
- Die Ausgänge sind den jeweiligen KNX Gruppenadressen zuzuordnen.
213
- </p>
214
- <p>
215
- Diese Node erwartet folgende Topics als Eingang:<br/>
216
- <table>
217
- <thead>
218
- <tr>
219
- <th>Topic</th>
220
- <th>Beschreibung</th>
221
- <th>msg.payload == false wird ignoriert</th>
222
- </tr>
223
- </thead>
224
- <tbody>
225
- <tr>
226
- <td><code>status</code> oder <code>status_position</code></td>
227
- <td>
228
- Meldet der Node die aktuelle Position.
229
- Dadurch kann die Node feststellen in welche Richtung ein Rollladen als letztes gefahren ist.
230
- Dies wird für das topic <b>toggle</b> benötigt um die nächste Laufrichtung bei einer 1-Tasten Bedienung zu ermitteln.
231
- </td>
232
- </tr>
233
- <tr>
234
- <td><code>up_down</code></td>
235
- <td>
236
- Nimmt einen Befehl von Home Assistant entgegen um informiert zu werden ob der Rollladen läuft und in welche Richtung.<br/>
237
- <strong>Wichtig:</strong> Dieses Topic startet den Rollladen <u>nicht</u>, da die Gruppenadresse direkt mit dem Jalousiemodul verbunden sein muss.
238
- </td>
239
- <td>Nein</td>
240
- </tr>
241
- <tr>
242
- <td><code>short_up_down</code></td>
243
- <td>
244
- Sendet bei <code>msg.payload = false</code> einen kurzen Hochfahrbefehl und bei <code>msg.payload = true</code> einen kurzen Runterfahrbefehl.<br/>
245
- Es wird dabei die im Baustein hinterlegte Zeit verwendet. Diese kann aber durch setzen von <code>msg.time_on = 1500</code> oder <code>msg.time_on = "1.5s"</code> einmalig überschrieben werden.
246
- </td>
247
- <td>Nein</td>
248
- </tr>
249
- <tr>
250
- <td><code>up</code></td>
251
- <td>Sendet einen Hochfahrbefehl, falls der Rollladen nicht bereits nach oben fährt. Ggf. wird vorher noch ein Stop-Befehl gesendet.</td>
252
- <td>Ja</td>
253
- </tr>
254
- <tr>
255
- <td><code>up_stop</code></td>
256
- <td>Sendet abwechselnd einen Stop- und einen Hochfahrbefehl.</td>
257
- <td>Ja</td>
258
- </tr>
259
- <tr>
260
- <td><code>down</code></td>
261
- <td>Sendet einen Runterfahrbefehl, falls der Rollladen nicht bereits nach unten fährt. Ggf. wird vorher noch ein Stop-Befehl gesendet.</td>
262
- <td>Ja</td>
263
- </tr>
264
- <tr>
265
- <td><code>down_stop</code></td>
266
- <td>Sendet abwechselnd einen Stop- und einen Runterfahrbefehl.</td>
267
- <td>Ja</td>
268
- </tr>
269
- <tr>
270
- <td><code>stop</code></td>
271
- <td>Sendet einen Stopbefehl.</td>
272
- <td>Ja</td>
273
- </tr>
274
- <tr>
275
- <td><code>position</code></td>
276
- <td>Sendet einen Positionsbefehl. <code>msg.payload</code> sollte ein Wert zwischen 0 (offen) und 100 (geschlossen) haben.</td>
277
- <td>Nein</td>
278
- </tr>
279
- <tr>
280
- <td><code>toggle</code> (default)</td>
281
- <td>Schaltet den Rollladen abwechselnd auf hoch, stop, runter, stop.</td>
282
- <td>Ja</td>
283
- </tr>
284
- </tbody>
285
- </table>
286
- </p>
287
- <p>
288
- Diese Node verwaltet keine Laufzeit für den Rollladen selbst. Diese muss über ETS für den Ausgang konfiguriert werden.
289
- Es ist jedoch möglich, den Rollladen nach einer definierten Zeit automatisch abzuschalten.
290
- Es ist jedoch möglich, den Rollladen nach einer definierten Zeit automatisch abzuschalten.
291
- Beispiel: <code>msg = { "topic": "up", "time_on": 5000 }</code> oder <code>msg = { "topic": "up", "time_on": "5s" }</code><br/>
292
- Diese Nachricht lässt den Rollladen für 5000 Millisekunden / 5 Sekunden nach oben fahren. Sollte es sich um eine Jalousie halten, werden die Lamellen entsprechend gedreht.
293
- Als Einheit für die Zeit können folgende Werte verwendet werden:
294
- <table>
295
- <thead>
296
- <tr>
297
- <th>Einheit</th>
298
- <th>Beschreibung</th>
299
- </tr>
300
- </thead>
301
- <tbody>
302
- <tr>
303
- <td><code>ms</code> (default)</td>
304
- <td>Millisekunden</td>
305
- </tr>
306
- <tr>
307
- <td><code>s</code> oder <code>sec</code></td>
308
- <td>Sekunden</td>
309
- </tr>
310
- <tr>
311
- <td><code>m</code> oder <code>min</code></td>
312
- <td>Mintun.</td>
313
- </tr>
314
- <tr>
315
- <td><code>h</code></td>
316
- <td>Stunden</td>
317
- </tr>
318
- </tbody>
319
- </table>
320
- </p>
321
- <p>
322
- Die Angabe einer Zeit funktioniert nicht mit dem topic <b>position</b>.
323
- </p>
324
198
  </script>
@@ -7,29 +7,57 @@ module.exports = function (RED)
7
7
  const node = this;
8
8
  RED.nodes.createNode(node, config);
9
9
 
10
+ // ###################
11
+ // # Class constants #
12
+ // ###################
13
+
14
+
15
+ // #######################
16
+ // # Global help objects #
17
+ // #######################
10
18
  const smart_context = require("../persistence.js")(RED);
11
19
  const helper = require("../smart_helper.js");
12
20
 
13
- // used from text-exec node
21
+
22
+ // ############################
23
+ // # Used from text-exec node #
24
+ // ############################
14
25
  if (typeof config.exec_text_names == "string")
15
26
  node.exec_text_names = config.exec_text_names.split(",").map(n => n.trim().toLowerCase());
16
27
  else
17
28
  node.exec_text_names = [];
18
29
 
19
- // persistent values
20
- var node_settings = Object.assign({}, {
30
+
31
+ // #####################
32
+ // # persistent values #
33
+ // #####################
34
+ var node_settings = helper.cloneObject({
21
35
  last_position: 0, // 0 = opened, 100 = closed
22
36
  last_direction_up: true, // remember last direction for toggle action
23
37
  }, smart_context.get(node.id));
24
38
 
25
- // dynamic config
26
- let short_time_on_ms = config.short_time_on_ms || 200;
27
39
 
28
- // runtime values
29
- let is_running = false; // remember if shutter is running, this is only recognized when starting within this control or position 0% or 100% is reached.
30
- let max_time_on_timeout = null; // save timeout value to cancel auto timeout if needed
40
+ // ##################
41
+ // # Dynamic config #
42
+ // ##################
43
+ let short_time_on_ms = parseInt(config.short_time_on_ms || 200, 10);
44
+
45
+
46
+ // ##################
47
+ // # Runtime values #
48
+ // ##################
49
+
50
+ // Here the setTimeout return value is stored to stop the shutter.
51
+ // That means if it is null, the shutter is stopped.
52
+ let timeout = null;
53
+
54
+ // This is set to true if a command to start the shutter is recognized.
55
+ let is_running = false;
56
+
31
57
 
32
- // central handling
58
+ // #########################
59
+ // # Central node handling #
60
+ // #########################
33
61
  var event = "node:" + config.id;
34
62
  var handler = function (msg)
35
63
  {
@@ -37,43 +65,58 @@ module.exports = function (RED)
37
65
  }
38
66
  RED.events.on(event, handler);
39
67
 
40
- node.status({ fill: "red", shape: "dot", text: helper.getCurrentTimeForStatus() + ": Stopped at " + Math.round(node_settings.last_position) + "%" });
41
68
 
69
+ // ###############
70
+ // # Node events #
71
+ // ###############
42
72
  node.on("input", function (msg)
43
73
  {
44
74
  handleTopic(msg);
45
75
 
76
+ setStatus();
46
77
  smart_context.set(node.id, node_settings);
47
78
  });
48
79
 
49
80
  node.on("close", function ()
50
81
  {
51
- stopAutoOff();
82
+ startAction(ACTION_STOP);
52
83
  RED.events.off(event, handler);
53
84
  });
54
85
 
86
+
87
+ // #####################
88
+ // # Private functions #
89
+ // #####################
90
+
91
+ // This is the main function which handles all topics that was received.
55
92
  let handleTopic = msg =>
56
93
  {
94
+ helper.log("handle topic:");
95
+ helper.log(msg);
96
+
57
97
  var resultUpDown = null;
58
98
  var resultStop = null;
59
99
  var resultPosition = null;
60
100
 
61
- var real_topic = helper.getTopicName(msg.topic);
62
-
63
- // set default topic
64
- if (!["status", "status_position", "up_down", "up", "up_stop", "down", "down_stop", "stop", "toggle", "position", "short_up_down"].includes(real_topic))
65
- real_topic = "toggle";
101
+ let real_topic = helper.getRealTopic(msg.topic, "toggle", ["status", "status_position", "up_down", "up", "up_stop", "down", "down_stop", "stop", "toggle", "position", "short_up_down"]);
66
102
 
67
103
  // skip if button is released
68
104
  if (msg.payload === false && ["up", "up_stop", "down", "down_stop", "stop", "toggle"].includes(real_topic))
69
105
  return;
70
106
 
71
107
  // Correct next topic to avoid handling up_stop, down_stop or toggle separately.
72
- if ((max_time_on_timeout != null || is_running) && (real_topic == "up_stop" || real_topic == "down_stop" || real_topic == "toggle"))
108
+ if (real_topic == "short_up_down")
109
+ {
110
+ real_topic = msg.payload ? "down" : "up";
111
+ if (msg.time_on == null)
112
+ msg.time_on = short_time_on_ms;
113
+ }
114
+
115
+ if ((timeout != null || is_running) && (real_topic == "up_stop" || real_topic == "down_stop" || real_topic == "toggle"))
73
116
  {
74
117
  real_topic = "stop";
75
118
  }
76
- else if (max_time_on_timeout == null && !is_running)
119
+ else if (timeout == null && !is_running)
77
120
  {
78
121
  // shutter is not running, set next command depending on topic
79
122
  if (real_topic == "up_stop")
@@ -86,7 +129,7 @@ module.exports = function (RED)
86
129
  real_topic = "up";
87
130
  }
88
131
 
89
-
132
+ helper.log("handle real topic: " + real_topic);
90
133
  switch (real_topic)
91
134
  {
92
135
  case "status":
@@ -96,30 +139,18 @@ module.exports = function (RED)
96
139
 
97
140
  if (is_running && (msg.payload == 0 || msg.payload == 100))
98
141
  is_running = false;
99
-
100
- node.status({ fill: "yellow", shape: "ring", text: helper.getCurrentTimeForStatus() + ": Position status received: " + msg.payload + "%" });
101
142
  return;
102
143
 
103
- // This is only used to track starting of the shutter
104
144
  case "up_down":
145
+ // This is only used to track starting of the shutter
105
146
  node_settings.last_direction_up = !msg.payload;
106
147
  is_running = true;
107
-
108
- if (node_settings.last_direction_up)
109
- node.status({ fill: "green", shape: "dot", text: helper.getCurrentTimeForStatus() + ": Up" });
110
- else
111
- node.status({ fill: "green", shape: "dot", text: helper.getCurrentTimeForStatus() + ": Down" });
112
- return;
113
-
114
- case "short_up_down":
115
- handleTopic({ topic: msg.payload ? "down" : "up", time_on: msg.time_on ?? short_time_on_ms });
116
148
  return;
117
149
 
118
150
  case "up":
119
151
  node_settings.last_direction_up = true;
120
152
  is_running = true;
121
153
  resultUpDown = false;
122
- node.status({ fill: "green", shape: "dot", text: helper.getCurrentTimeForStatus() + ": Up" });
123
154
  startAutoOffIfNeeded(msg);
124
155
  break;
125
156
 
@@ -127,14 +158,12 @@ module.exports = function (RED)
127
158
  is_running = false;
128
159
  resultStop = true;
129
160
  stopAutoOff();
130
- node.status({ fill: "red", shape: "dot", text: helper.getCurrentTimeForStatus() + ": Stopped" });
131
161
  break;
132
162
 
133
163
  case "down":
134
164
  node_settings.last_direction_up = false;
135
165
  is_running = true;
136
166
  resultUpDown = true;
137
- node.status({ fill: "green", shape: "dot", text: helper.getCurrentTimeForStatus() + ": Down" });
138
167
  startAutoOffIfNeeded(msg);
139
168
  break;
140
169
 
@@ -145,67 +174,98 @@ module.exports = function (RED)
145
174
  if (value > 100) value = 100;
146
175
  // is_running = true; // Not guaranteed that the shutter starts running.
147
176
  resultPosition = value;
148
- node.status({ fill: "green", shape: "dot", text: helper.getCurrentTimeForStatus() + ": Set position to " + value + "%" });
149
177
  break;
150
178
  }
151
179
 
152
180
  if (resultUpDown != null)
153
181
  {
154
- node.send([{ payload: resultUpDown, topic: "up_down" }, null, null]);
182
+ node.send([{ payload: resultUpDown }, null, null]);
155
183
  notifyCentral(true);
156
184
  }
157
185
  else if (resultStop != null)
158
186
  {
159
- node.send([null, { payload: resultStop, topic: "stop" }, null]);
187
+ node.send([null, { payload: resultStop }, null]);
160
188
  notifyCentral(false);
161
189
  }
162
190
  else if (resultPosition != null)
163
191
  {
164
- node.send([null, null, { payload: resultPosition, topic: "position" }]);
192
+ node.send([null, null, { payload: resultPosition }]);
165
193
  }
166
194
  };
167
195
 
196
+ /**
197
+ * This function sets a timeout to stop the shutter after the defined time is over.
198
+ * @param {*} msg The original message object
199
+ */
168
200
  let startAutoOffIfNeeded = msg =>
169
201
  {
170
- if (!msg.time_on)
202
+ if (msg.time_on == null || msg.time_on == 0)
171
203
  return;
172
204
 
205
+ // calculate needed time
173
206
  let timeMs = helper.getTimeInMsFromString(msg.time_on);
174
- if (isNaN(timeMs))
207
+ if (isNaN(timeMs) || timeMs <= 0)
175
208
  {
176
- node.status({ fill: "red", shape: "dot", text: helper.getCurrentTimeForStatus() + ": Invalid time_on value send: '" + msg.time_on + "'" });
177
- return;
178
- }
179
-
180
- if (timeMs <= 0)
181
- {
182
- node.status({ fill: "red", shape: "dot", text: helper.getCurrentTimeForStatus() + ": time_on value has to be greater than 0" });
209
+ console.warn("Invalid msg.time_on value was sent.", msg);
183
210
  return;
184
211
  }
185
212
 
186
213
  // Stop if any timeout is set
187
214
  stopAutoOff();
188
215
 
189
- node.status({ fill: "yellow", shape: "ring", text: helper.getCurrentTimeForStatus() + ": Wait " + (timeMs / 1000).toFixed(1) + " sec for auto off" });
190
- max_time_on_timeout = setTimeout(() =>
216
+ timeout = setTimeout(() =>
191
217
  {
192
- node.status({ fill: "red", shape: "dot", text: helper.getCurrentTimeForStatus() + ": Stopped" });
193
218
  is_running = false;
219
+ timeout = null;
220
+
194
221
  node.send([null, { payload: true }, null]);
195
222
  notifyCentral(false);
223
+
224
+ smart_context.set(node.id, node_settings);
225
+
226
+ setStatus();
196
227
  }, timeMs);
197
228
  };
198
229
 
230
+ /**
231
+ * Stops the current running timeout
232
+ */
199
233
  let stopAutoOff = () =>
200
234
  {
201
- if (max_time_on_timeout != null)
235
+ if (timeout != null)
202
236
  {
203
- node.status({});
204
- clearTimeout(max_time_on_timeout);
205
- max_time_on_timeout = null;
237
+ clearTimeout(timeout);
238
+ timeout = null;
206
239
  }
207
240
  };
208
241
 
242
+ /**
243
+ * Set the current node status
244
+ */
245
+ let setStatus = () =>
246
+ {
247
+ let fill = "green";
248
+ let shape = (timeout != null || is_running) ? "ring" : "dot";
249
+
250
+ // collect all texts and join later with a comma
251
+ let texts = [];
252
+
253
+ if (timeout == null && !is_running)
254
+ texts.push("Stopped");
255
+ else if (node_settings.last_direction_up)
256
+ texts.push("Up");
257
+ else
258
+ texts.push("Down");
259
+
260
+ texts.push("Position: " + node_settings.last_position?.toFixed(0) + "%");
261
+
262
+ node.status({ fill, shape, text: helper.getCurrentTimeForStatus() + ": " + texts.join(", ") });
263
+ }
264
+
265
+ /**
266
+ * Notify all connected central nodes
267
+ * @param {boolean} state The state if the shutter is running
268
+ */
209
269
  let notifyCentral = state =>
210
270
  {
211
271
  if (!config.links)
@@ -213,8 +273,8 @@ module.exports = function (RED)
213
273
 
214
274
  config.links.forEach(link =>
215
275
  {
216
- // console.log(node.id + " -> " + link);
217
- // console.log({ source: node.id, state: state });
276
+ helper.log(node.id + " -> " + link);
277
+ helper.log({ source: node.id, state: state });
218
278
  RED.events.emit("node:" + link, { source: node.id, state: state });
219
279
  });
220
280
  };