node-red-contrib-knx-ultimate 2.1.25 → 2.1.27
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 +9 -0
- package/nodes/hue-config.html +2 -2
- package/nodes/knxUltimate-config.html +132 -122
- package/nodes/knxUltimate-config.js +0 -96
- package/nodes/knxUltimate.html +1 -1
- package/nodes/knxUltimate.js +1 -1
- package/nodes/knxUltimateAlerter.js +192 -193
- package/nodes/knxUltimateHueLight.html +1 -1
- package/nodes/knxUltimateHueLight.js +13 -9
- package/nodes/knxUltimateHueTapDial.js +1 -1
- package/nodes/knxUltimateLoadControl.js +1 -1
- package/nodes/knxUltimateLogger.js +1 -1
- package/nodes/knxUltimateSceneController.js +1 -1
- package/nodes/knxUltimateWatchDog.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,15 @@
|
|
|
6
6
|
|
|
7
7
|
# CHANGELOG
|
|
8
8
|
|
|
9
|
+
<p>
|
|
10
|
+
<b>Version 2.1.27</b> - July 2023<br/>
|
|
11
|
+
- Trashed some old unuseful code and status options.<br/>
|
|
12
|
+
- Keep moving help to the help box of node-red.<br/>
|
|
13
|
+
</p>
|
|
14
|
+
<p>
|
|
15
|
+
<b>Version 2.1.26</b> - July 2023<br/>
|
|
16
|
+
- Hue Light: fixed brightness states issue.<br/>
|
|
17
|
+
</p>
|
|
9
18
|
<p>
|
|
10
19
|
<b>Version 2.1.25</b> - July 2023<br/>
|
|
11
20
|
- Hue Light: added the option to update the KNX Brightness status when turn on/off the HUE light.<br/>
|
package/nodes/hue-config.html
CHANGED
|
@@ -137,10 +137,10 @@
|
|
|
137
137
|
|
|
138
138
|
|
|
139
139
|
</script>
|
|
140
|
-
<script type="text/
|
|
140
|
+
<script type="text/markdown" data-help-name="hue-config">
|
|
141
141
|
<p> This node registers to the Hue Bridge.<br/>
|
|
142
142
|
|
|
143
|
-
Just set the Bridge's
|
|
143
|
+
Just set the Bridge's IP and click **CONNECT** button.
|
|
144
144
|
|
|
145
145
|
[Find it useful?](https://www.paypal.me/techtoday)
|
|
146
146
|
|
|
@@ -12,9 +12,6 @@
|
|
|
12
12
|
csv: { value: "", required: false },
|
|
13
13
|
KNXEthInterface: { value: "Auto" },
|
|
14
14
|
KNXEthInterfaceManuallyInput: { value: "" },
|
|
15
|
-
statusDisplayLastUpdate: { value: true },
|
|
16
|
-
statusDisplayDeviceNameWhenALL: { value: true },
|
|
17
|
-
statusDisplayDataPoint: { value: false },
|
|
18
15
|
stopETSImportIfNoDatapoint: { value: "stop" },
|
|
19
16
|
loglevel: { value: "error" },
|
|
20
17
|
name: { value: "KNX Gateway" },
|
|
@@ -112,10 +109,6 @@
|
|
|
112
109
|
});
|
|
113
110
|
});
|
|
114
111
|
|
|
115
|
-
// 14/02/2020 San Valentino. Retrieve all nodes and show here. Requested by use Alloc in knx-user-forum.de
|
|
116
|
-
$.getJSON("nodeList?nodeID=" + node.id, (data) => {
|
|
117
|
-
$("#nodeList").val(data);
|
|
118
|
-
});
|
|
119
112
|
|
|
120
113
|
// 14/08/2021 Elimino il file delle persistenze di questo nodo
|
|
121
114
|
$.getJSON("deletePersistGAFile?nodeID=" + node.id, (data) => {
|
|
@@ -134,22 +127,13 @@
|
|
|
134
127
|
// })
|
|
135
128
|
});
|
|
136
129
|
|
|
137
|
-
// 07/
|
|
130
|
+
// 06/07/2023 Tabs
|
|
138
131
|
// *****************************
|
|
139
|
-
$("#
|
|
140
|
-
header: "h3",
|
|
141
|
-
heightStyle: "content",
|
|
142
|
-
collapsible: true,
|
|
143
|
-
active: false
|
|
144
|
-
});
|
|
145
|
-
$("#etsCSVListBox").accordion({
|
|
146
|
-
header: "h3",
|
|
147
|
-
heightStyle: "content",
|
|
148
|
-
collapsible: true,
|
|
149
|
-
active: false
|
|
150
|
-
});
|
|
132
|
+
$("#tabs").tabs();
|
|
151
133
|
// *****************************
|
|
152
134
|
|
|
135
|
+
|
|
136
|
+
|
|
153
137
|
var sRetDebugText = "";
|
|
154
138
|
$("#getinfocam").click(function () {
|
|
155
139
|
$("#divDebugText").show();
|
|
@@ -220,6 +204,8 @@
|
|
|
220
204
|
color: #459e00;
|
|
221
205
|
}
|
|
222
206
|
</style>
|
|
207
|
+
|
|
208
|
+
|
|
223
209
|
<script type="text/html" data-template-name="knxUltimate-config">
|
|
224
210
|
<div class="form-row">
|
|
225
211
|
<b><span data-i18n="knxUltimate-config.properties.title"></span></b>  <span style="color:red" data-i18n="[html]knxUltimate-config.properties.helplink"></span>
|
|
@@ -282,73 +268,64 @@
|
|
|
282
268
|
</p>
|
|
283
269
|
</div>
|
|
284
270
|
</div> -->
|
|
285
|
-
|
|
286
|
-
<br/>
|
|
287
|
-
<div class="form-row">
|
|
288
|
-
<label for="node-config-input-KNXEthInterface" style="width: 200px">
|
|
289
|
-
<i class="fa fa-wifi"></i>
|
|
290
|
-
<span data-i18n="knxUltimate-config.properties.bind_local_int"></span>
|
|
291
|
-
</label>
|
|
292
|
-
<select id="node-config-input-KNXEthInterface"></select>
|
|
293
|
-
</div>
|
|
294
|
-
<div class="form-row" id="divKNXEthInterfaceManuallyInput" style="display: none;">
|
|
295
|
-
<label for="node-config-input-KNXEthInterfaceManuallyInput">Interface name:</label>
|
|
296
|
-
<input type="text" id="node-config-input-KNXEthInterfaceManuallyInput" placeholder="Interface name, ex: eth0 or ens1 or Ethernet 1 and so on..."></input>
|
|
297
|
-
</div>
|
|
298
|
-
<div class="form-row">
|
|
299
|
-
<label for="node-config-input-autoReconnect" style="width: 200px">
|
|
300
|
-
<i class="fa fa-plug"></i>
|
|
301
|
-
<span data-i18n="knxUltimate-config.properties.autoReconnect"></span>
|
|
302
|
-
</label>
|
|
303
|
-
<select id="node-config-input-autoReconnect">
|
|
304
|
-
<option value="yes" data-i18n="knxUltimate-config.properties.autoReconnect_yes"></option>
|
|
305
|
-
<option value="no" data-i18n="knxUltimate-config.properties.autoReconnect_no"></option>
|
|
306
|
-
</select>
|
|
307
|
-
</div>
|
|
308
271
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
272
|
+
<br/>
|
|
273
|
+
|
|
274
|
+
<div id="tabs">
|
|
275
|
+
<ul>
|
|
276
|
+
<li><a href="#tabs-1"><i class="fa fa-list-ol"></i> Configuration</a></li>
|
|
277
|
+
<li><a href="#tabs-2"><i class="fa fa-braille"></i> Advanced</a></li>
|
|
278
|
+
<li><a href="#tabs-3"><i class="fa fa-code"></i> ETS file import</a></li>
|
|
279
|
+
<li><a href="#tabs-4"><i class="fa fa-key"></i> Maintenance</a></li>
|
|
280
|
+
</ul>
|
|
281
|
+
<div id="tabs-1">
|
|
282
|
+
<p>
|
|
312
283
|
<div class="form-row">
|
|
313
|
-
<label for="node-config-input-physAddr" style="width:
|
|
284
|
+
<label for="node-config-input-physAddr" style="width: 200px">
|
|
314
285
|
<i class="fa fa-microchip"></i>
|
|
315
286
|
<span data-i18n="knxUltimate-config.advanced.knx_phy_addr"></span>
|
|
316
287
|
</label>
|
|
317
288
|
<input type="text" id="node-config-input-physAddr" style="width:30%">
|
|
318
289
|
</div>
|
|
319
|
-
|
|
290
|
+
|
|
320
291
|
<div class="form-row">
|
|
321
|
-
<
|
|
322
|
-
|
|
323
|
-
<
|
|
324
|
-
<span data-i18n="knxUltimate-config.advanced.localEchoInTunneling"></span>
|
|
292
|
+
<label for="node-config-input-KNXEthInterface" style="width: 200px">
|
|
293
|
+
<i class="fa fa-wifi"></i>
|
|
294
|
+
<span data-i18n="knxUltimate-config.properties.bind_local_int"></span>
|
|
325
295
|
</label>
|
|
296
|
+
<select id="node-config-input-KNXEthInterface"></select>
|
|
326
297
|
</div>
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
<input type="
|
|
330
|
-
|
|
331
|
-
<i class="fa fa-comment-o"></i>
|
|
332
|
-
<span data-i18n="knxUltimate-config.advanced.show_date_status"></span>
|
|
333
|
-
</label>
|
|
298
|
+
<div class="form-row" id="divKNXEthInterfaceManuallyInput" style="display: none;">
|
|
299
|
+
<label for="node-config-input-KNXEthInterfaceManuallyInput">Interface name:</label>
|
|
300
|
+
<input type="text" id="node-config-input-KNXEthInterfaceManuallyInput"
|
|
301
|
+
placeholder="Interface name, ex: eth0 or ens1 or Ethernet 1 and so on...">
|
|
334
302
|
</div>
|
|
335
303
|
<div class="form-row">
|
|
336
|
-
<
|
|
337
|
-
|
|
338
|
-
<
|
|
339
|
-
<span data-i18n="knxUltimate-config.advanced.show_device_status"></span>
|
|
304
|
+
<label for="node-config-input-autoReconnect" style="width: 200px">
|
|
305
|
+
<i class="fa fa-plug"></i>
|
|
306
|
+
<span data-i18n="knxUltimate-config.properties.autoReconnect"></span>
|
|
340
307
|
</label>
|
|
308
|
+
<select id="node-config-input-autoReconnect">
|
|
309
|
+
<option value="yes" data-i18n="knxUltimate-config.properties.autoReconnect_yes"></option>
|
|
310
|
+
<option value="no" data-i18n="knxUltimate-config.properties.autoReconnect_no"></option>
|
|
311
|
+
</select>
|
|
341
312
|
</div>
|
|
313
|
+
</p>
|
|
314
|
+
</div>
|
|
315
|
+
<div id="tabs-2">
|
|
316
|
+
<p>
|
|
342
317
|
<div class="form-row">
|
|
343
|
-
<input type="checkbox" id="node-config-input-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
<
|
|
318
|
+
<input type="checkbox" id="node-config-input-localEchoInTunneling"
|
|
319
|
+
style="display:inline-block; width:auto; vertical-align:top;">
|
|
320
|
+
<label style="width:85%" for="node-config-input-localEchoInTunneling">
|
|
321
|
+
<i class="fa fa-bullhorn"></i>
|
|
322
|
+
<span data-i18n="knxUltimate-config.advanced.localEchoInTunneling"></span>
|
|
347
323
|
</label>
|
|
348
324
|
</div>
|
|
349
325
|
|
|
350
326
|
<div class="form-row">
|
|
351
|
-
<input type="checkbox" id="node-config-input-ignoreTelegramsWithRepeatedFlag"
|
|
327
|
+
<input type="checkbox" id="node-config-input-ignoreTelegramsWithRepeatedFlag"
|
|
328
|
+
style="display:inline-block; width:auto; vertical-align:top;">
|
|
352
329
|
<label style="width:85%" for="node-config-input-ignoreTelegramsWithRepeatedFlag">
|
|
353
330
|
<i class="fa fa-ban"></i>
|
|
354
331
|
<span data-i18n="knxUltimate-config.advanced.ignoreTelegramsWithRepeatedFlag"></span>
|
|
@@ -356,23 +333,22 @@
|
|
|
356
333
|
</div>
|
|
357
334
|
|
|
358
335
|
<div class="form-row">
|
|
359
|
-
<input type="checkbox" id="node-config-input-suppressACKRequest"
|
|
336
|
+
<input type="checkbox" id="node-config-input-suppressACKRequest"
|
|
337
|
+
style="display:inline-block; width:auto; vertical-align:top;">
|
|
360
338
|
<label style="width:85%" for="node-config-input-suppressACKRequest">
|
|
361
339
|
<i class="fa fa-ban"></i>
|
|
362
340
|
<span data-i18n="knxUltimate-config.advanced.suppress_ack"></span>
|
|
363
341
|
</label>
|
|
364
|
-
<div id="helpallga" class="form-tips" style="margin-top:11px">
|
|
365
|
-
<span data-i18n="knxUltimate-config.advanced.suppress_ack_help"></span>
|
|
366
|
-
</div>
|
|
367
342
|
</div>
|
|
368
|
-
|
|
343
|
+
|
|
369
344
|
<div class="form-row">
|
|
370
345
|
<label for="node-config-input-delaybetweentelegrams" style="width:auto">
|
|
371
346
|
<i class="fa fa-hourglass-start"></i>
|
|
372
347
|
<span data-i18n="knxUltimate-config.advanced.delaybetweentelegrams"></span>
|
|
373
348
|
</label>
|
|
374
349
|
<input type="number" id="node-config-input-delaybetweentelegrams" style="width:20%">
|
|
375
|
-
<span data-i18n="knxUltimate-config.advanced.delaybetweentelegramsfurtherdelayREAD"></span><input type="number"
|
|
350
|
+
<span data-i18n="knxUltimate-config.advanced.delaybetweentelegramsfurtherdelayREAD"></span><input type="number"
|
|
351
|
+
id="node-config-input-delaybetweentelegramsfurtherdelayREAD" style="width:15%">X
|
|
376
352
|
</div>
|
|
377
353
|
|
|
378
354
|
<div class="form-row">
|
|
@@ -389,59 +365,69 @@
|
|
|
389
365
|
<option value="trace" data-i18n="knxUltimate-config.advanced.select_trace"></option>
|
|
390
366
|
</select>
|
|
391
367
|
<!-- <div class="form-tips" style="margin-top: 11px;background-color:#FFEEEE;text-align:center">
|
|
392
|
-
|
|
393
|
-
|
|
368
|
+
<b><span data-i18n="knxUltimate-config.properties.restart_hint"></span></b>
|
|
369
|
+
</div> -->
|
|
394
370
|
</div>
|
|
395
|
-
|
|
396
|
-
<span data-i18n="knxUltimate-config.advanced.nodes_list_title"></span>
|
|
397
|
-
<textarea rows="20" id="nodeList" style="width:100%" data-i18n="[placeholder]knxUltimate-config.advanced.nodes_list_help"></textarea>
|
|
398
|
-
</p>
|
|
399
|
-
</div>
|
|
371
|
+
</p>
|
|
400
372
|
</div>
|
|
401
373
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
<
|
|
405
|
-
<div
|
|
406
|
-
<span data-i18n="knxUltimate-config.
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
<
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
374
|
+
|
|
375
|
+
<div id="tabs-3">
|
|
376
|
+
<p>
|
|
377
|
+
<div id="etsCSVListBox">
|
|
378
|
+
<h3><span data-i18n="knxUltimate-config.properties.ets_import"></span></h3>
|
|
379
|
+
<div>
|
|
380
|
+
<div class="form-row">
|
|
381
|
+
<span data-i18n="knxUltimate-config.ets.description"></span>
|
|
382
|
+
</div>
|
|
383
|
+
<div class="form-row">
|
|
384
|
+
<span style="color:red" data-i18n="[html]knxUltimate-config.ets.instruction"></span>
|
|
385
|
+
</div>
|
|
386
|
+
<div class="form-row">
|
|
387
|
+
<span style="color:red" data-i18n="[html]knxUltimate-config.ets.youtube"></span>
|
|
388
|
+
</div>
|
|
389
|
+
<div class="form-row">
|
|
390
|
+
<label for="node-config-input-stopETSImportIfNoDatapoint" style="width:250px">
|
|
391
|
+
<i class="fa fa-question-circle"></i>
|
|
392
|
+
<span data-i18n="knxUltimate-config.ets.help_ga"></span>
|
|
393
|
+
</label>
|
|
394
|
+
<select id="node-config-input-stopETSImportIfNoDatapoint" style="width:210px">
|
|
395
|
+
<option value="stop" data-i18n="knxUltimate-config.ets.import_select_stop"></option>
|
|
396
|
+
<option value="fake" data-i18n="knxUltimate-config.ets.import_select_fake"></option>
|
|
397
|
+
<option value="skip" data-i18n="knxUltimate-config.ets.import_select_skip"></option>
|
|
398
|
+
</select>
|
|
399
|
+
</div>
|
|
400
|
+
<div class="form-row">
|
|
401
|
+
<label style="width:auto" for="node-config-input-csv">
|
|
402
|
+
<i class="fa fa-th-list"></i> ETS group address list
|
|
403
|
+
</label>
|
|
404
|
+
</div>
|
|
405
|
+
<div class="form-row">
|
|
406
|
+
<textarea rows="20" id="node-config-input-csv" style="width:100%"
|
|
407
|
+
data-i18n="[placeholder]knxUltimate-config.ets.ga_list_help"></textarea>
|
|
408
|
+
</div>
|
|
409
|
+
</div>
|
|
410
|
+
</div>
|
|
411
|
+
</p>
|
|
412
|
+
</div>
|
|
413
|
+
|
|
414
|
+
<div id="tabs-4">
|
|
415
|
+
<p>
|
|
425
416
|
<div class="form-row">
|
|
426
|
-
<label style="width:
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
</label>
|
|
417
|
+
<label style="width:300px"><i class="fa fa-sign-in"></i> Gather debug info for troubleshoot</label>
|
|
418
|
+
<input type="button" id="getinfocam" class="ui-button ui-corner-all ui-widget"
|
|
419
|
+
style="background-color:#AEE1FF;width:150px" value="Read">
|
|
430
420
|
</div>
|
|
431
|
-
<div class="form-row">
|
|
432
|
-
|
|
421
|
+
<div class="form-row" id="divDebugText" style="display: none;">
|
|
422
|
+
Copy this text, open a new github issue <a target="_blank"
|
|
423
|
+
href="https://github.com/Supergiovane/node-red-contrib-knx-ultimate/issues/new?assignees=Supergiovane&labels=&template=bug_report.md&title=KNXDebugText">
|
|
424
|
+
->by CLICKING HERE</a>, or paste it to an already open issue.
|
|
425
|
+
<textarea rows="10" id="debugText" style="width:100%"></textarea>
|
|
433
426
|
</div>
|
|
434
|
-
</
|
|
435
|
-
</div>
|
|
436
|
-
<br/>
|
|
437
|
-
<div class="form-row">
|
|
438
|
-
<label style="width:300px"><i class="fa fa-sign-in"></i> Gather debug info for troubleshoot</label>
|
|
439
|
-
<input type="button" id="getinfocam" class="ui-button ui-corner-all ui-widget" style="background-color:#AEE1FF;width:150px" value="Read">
|
|
440
|
-
</div>
|
|
441
|
-
<div class="form-row" id="divDebugText" style="display: none;">
|
|
442
|
-
Copy this text, open a new github issue <a target="_blank" href="https://github.com/Supergiovane/node-red-contrib-knx-ultimate/issues/new?assignees=Supergiovane&labels=&template=bug_report.md&title=KNXDebugText"> ->by CLICKING HERE</a>, or paste it to an already open issue.
|
|
443
|
-
<textarea rows="10" id="debugText" style="width:100%"></textarea>
|
|
427
|
+
</p>
|
|
444
428
|
</div>
|
|
429
|
+
</div>
|
|
430
|
+
|
|
445
431
|
</script>
|
|
446
432
|
<script type="text/markdown" data-help-name="knxUltimate-config">
|
|
447
433
|
<p>This node connects to your KNX/IP Gateway.
|
|
@@ -453,16 +439,40 @@
|
|
|
453
439
|
| IP/Hostname | ETH/KNX Router multicast address or Interface unicast IP address. If you have an KNX/IP interface, use the interface's IP address, for example 1982.168.1.22, otherwise, if you have a KNX/IP router, put the multicast address 224.0.23.12. You can also type an **Hostname** instead of an IP. |
|
|
454
440
|
| Port | The port. |
|
|
455
441
|
| Protocol dropdown | *Tunnel UDP* is for KNX/IP interfaces, *Multicast UDP* is for KNX/IP Routers. |
|
|
442
|
+
|
|
443
|
+
<br/>
|
|
444
|
+
|
|
445
|
+
**Configuration**
|
|
446
|
+
|Property|Description|
|
|
447
|
+
|--|--|
|
|
456
448
|
| Bind to local interface | The Node will use this local interface for communications. Leave "Auto" for automatic selection. If you have more than one lan connection, for example Ethernet and Wifi, it's strongly recommended to manually select the interface, otherwise not all UDP telegram will reach your computer, thus the Node may not work as expected. |
|
|
457
449
|
| Automatically connect to KNX BUS at start | Auto connect to the bus at start. You want to leave it enabled. |
|
|
458
450
|
| KNX Physical Address | The physical KNX address, example 1.1.200 |
|
|
451
|
+
|
|
452
|
+
<br/>
|
|
453
|
+
|
|
454
|
+
**Advanced**
|
|
455
|
+
|Property|Description|
|
|
456
|
+
|--|--|
|
|
459
457
|
| Suppress repeated (R-Flag) telegrams fom BUS | Ignore repeated KNX telegrams coming from the bus. |
|
|
460
458
|
| Suppress ACK request in tunneling mode | Enable it if you have a very old KNX/IP gateway. You want to leave it disabled. |
|
|
461
459
|
| Delay between each telegram (in milliseconds) | Leave it 50ms, unless you're connecting to a remote KNX Gateway via a slow internet connection. |
|
|
462
460
|
| and further multiply delay only between -read- telegrams | Multiply the delay for read requests to the KNX bus. Again, for slow KNX Gateways. |
|
|
463
461
|
| Loglevel | Log level, in case you need to debug something with the dev. |
|
|
464
|
-
|
|
465
|
-
|
|
462
|
+
|
|
463
|
+
<br/>
|
|
464
|
+
|
|
465
|
+
**ETS file import**
|
|
466
|
+
|Property|Description|
|
|
467
|
+
|--|--|
|
|
468
|
+
| If Group Address has no Datapoint | If a group address doesn't have a datapoint, it allow to choose wether to stop import, import quth a fake datapoint of 1.001 or to skip import of that group address |
|
|
469
|
+
| ETS group address list | Use this section to import your ETS CSV or ESF file. You can either **paste the CSV or ESF file content** or **set the file path**, for example *./pi/homecsv.csv*. Please refer to the help links for further infos. |
|
|
470
|
+
|
|
471
|
+
<br/>
|
|
472
|
+
|
|
473
|
+
**Maintenance**
|
|
474
|
+
|Property|Description|
|
|
475
|
+
|--|--|
|
|
466
476
|
| Gather debug info for troubleshoot | Please click the button and add it to the gitHub issue you want to open, it will help me a lot to helping you. |
|
|
467
477
|
|
|
468
478
|
<br/>
|
|
@@ -110,9 +110,6 @@ return msg;`,
|
|
|
110
110
|
node.nodeClients = [] // Stores the registered clients
|
|
111
111
|
node.KNXEthInterface = typeof config.KNXEthInterface === 'undefined' ? 'Auto' : config.KNXEthInterface
|
|
112
112
|
node.KNXEthInterfaceManuallyInput = typeof config.KNXEthInterfaceManuallyInput === 'undefined' ? '' : config.KNXEthInterfaceManuallyInput // If you manually set the interface name, it will be wrote here
|
|
113
|
-
node.statusDisplayLastUpdate = typeof config.statusDisplayLastUpdate === 'undefined' ? true : config.statusDisplayLastUpdate
|
|
114
|
-
node.statusDisplayDeviceNameWhenALL = typeof config.statusDisplayDeviceNameWhenALL === 'undefined' ? false : config.statusDisplayDeviceNameWhenALL
|
|
115
|
-
node.statusDisplayDataPoint = typeof config.statusDisplayDataPoint === 'undefined' ? false : config.statusDisplayDataPoint
|
|
116
113
|
node.telegramsQueue = [] // 02/01/2020 Queue containing telegrams
|
|
117
114
|
node.timerSendTelegramFromQueue = null
|
|
118
115
|
node.delaybetweentelegramsfurtherdelayREAD = (typeof config.delaybetweentelegramsfurtherdelayREAD === 'undefined' || Number(config.delaybetweentelegramsfurtherdelayREAD < 1)) ? 1 : Number(config.delaybetweentelegramsfurtherdelayREAD) // 18/05/2020 delay multiplicator only for "read" telegrams.
|
|
@@ -385,99 +382,6 @@ return msg;`,
|
|
|
385
382
|
res.json(jListInterfaces)
|
|
386
383
|
})
|
|
387
384
|
|
|
388
|
-
// 14/02/2020 Endpoint for retrieving all nodes in all flows
|
|
389
|
-
RED.httpAdmin.get('/nodeList', RED.auth.needsPermission('knxUltimate-config.read'), function (req, res) {
|
|
390
|
-
let sNodeID = req.query.nodeID // Retrieve node.id of the config node.
|
|
391
|
-
const _node = RED.nodes.getNode(sNodeID)
|
|
392
|
-
if (_node === null) {
|
|
393
|
-
// 27/09/2020 Something wrong
|
|
394
|
-
return
|
|
395
|
-
}
|
|
396
|
-
let sNodes = '"Group Address"\t"Datapoint"\t"Node ID"\t"Device Name"\t"Options"\n' // Contains the text with nodes
|
|
397
|
-
let sGA = ''
|
|
398
|
-
let sDPT = ''
|
|
399
|
-
let sName = ''
|
|
400
|
-
let sOptions = ''
|
|
401
|
-
try {
|
|
402
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info('KNXUltimate-config: Total knx-ultimate nodes: ' + _node.nodeClients.length || 0)
|
|
403
|
-
_node.nodeClients
|
|
404
|
-
// .map( a => a.topic.indexOf("/") !== -1 ? a.topic.split('/').map( n => +n+100000 ).join('/'):0 ).sort().map( a => a.topic.indexOf("/") !== -1 ? a.topic.split('/').map( n => +n-100000 ).join('/'):0 )
|
|
405
|
-
.sort((a, b) => {
|
|
406
|
-
if (a.topic !== undefined && b.topic !== undefined) {
|
|
407
|
-
if (a.topic.indexOf('/') === -1) return -1
|
|
408
|
-
if (b.topic.indexOf('/') === -1) return -1
|
|
409
|
-
let date1 = a.topic.split('/')
|
|
410
|
-
let date2 = b.topic.split('/')
|
|
411
|
-
date1 = date1[0].padStart(2, '0') + date1[1].padStart(2, '0') + date1[2].padStart(2, '0')
|
|
412
|
-
date2 = date2[0].padStart(2, '0') + date2[1].padStart(2, '0') + date2[2].padStart(2, '0')
|
|
413
|
-
return date1.localeCompare(date2)
|
|
414
|
-
} else { return -1 }
|
|
415
|
-
})
|
|
416
|
-
.forEach(_input => {
|
|
417
|
-
const input = RED.nodes.getNode(_input.id)
|
|
418
|
-
sNodeID = '"' + input.id + '"'
|
|
419
|
-
sName = '"' + (input.name !== undefined ? input.name : '') + '"'
|
|
420
|
-
sOptions = '"' + '"'
|
|
421
|
-
if (input.listenallga === true) {
|
|
422
|
-
if (input.hasOwnProperty('isSceneController')) {
|
|
423
|
-
// Is a Scene Controller
|
|
424
|
-
sGA = '"Scene Controller"'
|
|
425
|
-
sDPT = '"Any"'
|
|
426
|
-
} else if (input.hasOwnProperty('isLogger')) {
|
|
427
|
-
// Is a Scene Controller
|
|
428
|
-
sGA = '"Logger"'
|
|
429
|
-
sDPT = '"Any"'
|
|
430
|
-
} else if (input.hasOwnProperty('isalertnode')) {
|
|
431
|
-
// Is a Scene Controller
|
|
432
|
-
sGA = '"Alerter"'
|
|
433
|
-
sDPT = '"Any"'
|
|
434
|
-
} else if (input.hasOwnProperty('isLoadControlNode')) {
|
|
435
|
-
// Is a Load Controller
|
|
436
|
-
sGA = '"LoadControl"'
|
|
437
|
-
sDPT = '"Any"'
|
|
438
|
-
} else {
|
|
439
|
-
// Is a ListenallGA
|
|
440
|
-
sGA = '"Universal Node"'
|
|
441
|
-
sDPT = '"Any"'
|
|
442
|
-
sOptions = '"' + 'No Initial Read' + ', '
|
|
443
|
-
|
|
444
|
-
sOptions += (input.notifywrite === true ? 'React to Write' : 'No React to Write') + ', '
|
|
445
|
-
sOptions += (input.notifyresponse === true ? 'React to Response' : 'No React to Response') + ', '
|
|
446
|
-
sOptions += (input.notifyreadrequest === true ? 'React to Read' : 'No React to Read') + ', '
|
|
447
|
-
sOptions += 'No Autorespond to Read Requests' + ', '
|
|
448
|
-
|
|
449
|
-
sOptions += 'Output type ' + input.outputtype + ', '
|
|
450
|
-
sOptions += 'No RBE on Output to Bus' + ', '
|
|
451
|
-
sOptions += 'No RBE on Input from Bus' + '"'
|
|
452
|
-
}
|
|
453
|
-
} else {
|
|
454
|
-
sGA = '"' + (input.topic !== undefined ? input.topic : '') + '"'
|
|
455
|
-
sDPT = '"' + (input.dpt !== undefined ? input.dpt : '') + '"'
|
|
456
|
-
|
|
457
|
-
if (input.hasOwnProperty('isWatchDog')) {
|
|
458
|
-
// Is a watchdog node
|
|
459
|
-
|
|
460
|
-
} else {
|
|
461
|
-
// Is a device node
|
|
462
|
-
sOptions = '"' + (Number(input.initialread) > 0 ? 'Initial Read' : 'No Initial Read') + ', '
|
|
463
|
-
sOptions += (input.notifywrite === true ? 'React to Write' : 'No React to Write') + ', '
|
|
464
|
-
sOptions += (input.notifyresponse === true ? 'React to Response' : 'No React to Response') + ', '
|
|
465
|
-
sOptions += (input.notifyreadrequest === true ? 'React to Read' : 'No React to Read') + ', '
|
|
466
|
-
sOptions += (input.notifyreadrequestalsorespondtobus === true ? 'Autorespond to Read Requests' : 'No Autorespond to Read Requests') + ', '
|
|
467
|
-
|
|
468
|
-
sOptions += 'Output type ' + input.outputtype + ', '
|
|
469
|
-
sOptions += (input.outputRBE === true ? 'RBE on Output to Bus' : 'No RBE on Output to Bus') + ', '
|
|
470
|
-
sOptions += (input.inputRBE === true ? 'RBE on Input from Bus' : 'No RBE on Input from Bus') + '"'
|
|
471
|
-
};
|
|
472
|
-
};
|
|
473
|
-
sNodes += sGA + '\t' + sDPT + '\t' + sNodeID + '\t' + sName + '\t' + sOptions + '\n'
|
|
474
|
-
})
|
|
475
|
-
res.json(sNodes)
|
|
476
|
-
} catch (error) {
|
|
477
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.warn('D ' + error)
|
|
478
|
-
}
|
|
479
|
-
})
|
|
480
|
-
|
|
481
385
|
// 12/08/2021 Endpoint for deleting the GA persistent file for the current gateway
|
|
482
386
|
RED.httpAdmin.get('/deletePersistGAFile', RED.auth.needsPermission('knxUltimate-config.read'), function (req, res) {
|
|
483
387
|
if (typeof req.query.nodeID !== 'undefined' && req.query.nodeID !== null && req.query.nodeID !== '') {
|
package/nodes/knxUltimate.html
CHANGED
|
@@ -397,7 +397,7 @@
|
|
|
397
397
|
<ul>
|
|
398
398
|
<li><a href="#tabs-1"><i class="fa fa-code"></i> Sample in Function</a></li>
|
|
399
399
|
<li><a href="#tabs-2"><i class="fa fa-braille"></i> Advanced options</a></li>
|
|
400
|
-
<li><a href="#tabs-3"><i class="fa fa-list-ol"></i>Format input</a></li>
|
|
400
|
+
<li><a href="#tabs-3"><i class="fa fa-list-ol"></i> Format input</a></li>
|
|
401
401
|
</ul>
|
|
402
402
|
<div id="tabs-1">
|
|
403
403
|
<p>
|
package/nodes/knxUltimate.js
CHANGED
|
@@ -49,7 +49,7 @@ module.exports = function (RED) {
|
|
|
49
49
|
devicename = devicename || ''
|
|
50
50
|
dpt = (typeof dpt === 'undefined' || dpt == '') ? '' : ' DPT' + dpt
|
|
51
51
|
payload = typeof payload === 'object' ? JSON.stringify(payload) : payload
|
|
52
|
-
node.status({ fill, shape, text: GA + payload + (
|
|
52
|
+
node.status({ fill, shape, text: GA + payload + (node.listenallga === true ? ' ' + devicename : '') + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ' ' + text })
|
|
53
53
|
// 16/02/2020 signal errors to the server
|
|
54
54
|
if (fill.toUpperCase() === 'RED') {
|
|
55
55
|
if (node.server) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
module.exports = function (RED) {
|
|
3
|
-
function knxUltimateAlerter
|
|
3
|
+
function knxUltimateAlerter(config) {
|
|
4
4
|
const fs = require('fs')
|
|
5
5
|
const path = require('path')
|
|
6
6
|
const mkdirp = require('mkdirp')
|
|
@@ -53,235 +53,234 @@ module.exports = function (RED) {
|
|
|
53
53
|
devicename = devicename || ''
|
|
54
54
|
dpt = (typeof dpt === 'undefined' || dpt == '') ? '' : ' DPT' + dpt
|
|
55
55
|
payload = typeof payload === 'object' ? JSON.stringify(payload) : payload
|
|
56
|
-
node.status({ fill, shape, text: GA + payload + (
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
node.status({ fill, shape, text: GA + payload + (node.listenallga === true ? ' ' + devicename : '') + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ' ' + text })
|
|
57
|
+
} catch (error) {
|
|
58
|
+
}
|
|
59
59
|
}
|
|
60
|
-
}
|
|
61
60
|
|
|
62
61
|
// Used to call the status update from the config node.
|
|
63
62
|
node.setLocalStatus = ({ fill, shape, text, payload, GA, dpt, devicename }) => {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
63
|
+
const dDate = new Date()
|
|
64
|
+
// 30/08/2019 Display only the things selected in the config
|
|
65
|
+
GA = (typeof GA === 'undefined' || GA == '') ? '' : '(' + GA + ') '
|
|
66
|
+
devicename = devicename || ''
|
|
67
|
+
dpt = (typeof dpt === 'undefined' || dpt == '') ? '' : ' DPT' + dpt
|
|
68
|
+
try {
|
|
69
|
+
node.status({ fill, shape, text: GA + payload + (node.listenallga === true ? ' ' + devicename : '') + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ' ' + text })
|
|
70
|
+
} catch (error) {
|
|
71
|
+
}
|
|
72
72
|
}
|
|
73
|
-
}
|
|
74
73
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
74
|
+
// This function is called by the knx-ultimate config node, to output a msg.payload.
|
|
75
|
+
node.handleSend = msg => {
|
|
76
|
+
try {
|
|
77
|
+
if (!msg.knx.dpt.startsWith('1.')) return
|
|
78
|
+
} catch (error) {
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
let bFound = false // 24/04/2021 true if the cycle below found a match, otherwise false
|
|
83
82
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
83
|
+
// Update the node.rules with the values taken from the file, if any, otherwise leave the default value
|
|
84
|
+
for (let i = 0; i < node.rules.length; i++) {
|
|
85
|
+
// rule is { topic: rowRuleTopic, devicename: rowRuleDeviceName, longdevicename: rowRuleLongDeviceName}
|
|
86
|
+
var rule = node.rules[i]
|
|
87
|
+
if (msg.topic === rule.topic) {
|
|
88
|
+
if (msg.payload == true) {
|
|
89
|
+
bFound = true
|
|
90
|
+
// Add the device to the array of alertedDevices
|
|
91
|
+
const oTrovato = node.alertedDevices.find(a => a.topic === rule.topic)
|
|
92
|
+
if (oTrovato === undefined) {
|
|
93
|
+
node.alertedDevices.unshift({ topic: rule.topic, devicename: rule.devicename, longdevicename: rule.longdevicename }) // Add to the begin of array
|
|
94
|
+
if (node.whentostart === 'ifnewalert') node.send([null, null, node.getThirdPinMSG()])
|
|
95
|
+
}
|
|
96
|
+
node.setLocalStatus({ fill: 'red', shape: 'dot', text: 'Alert', payload: '', GA: msg.topic, dpt: '', devicename: rule.devicename })
|
|
97
|
+
} else {
|
|
98
|
+
// Remove the device from the array
|
|
99
|
+
node.alertedDevices = node.alertedDevices.filter(a => a.topic !== msg.topic)
|
|
100
|
+
node.setLocalStatus({ fill: 'green', shape: 'dot', text: 'Restore', payload: '', GA: msg.topic, dpt: '', devicename: rule.devicename })
|
|
96
101
|
}
|
|
97
|
-
node.setLocalStatus({ fill: 'red', shape: 'dot', text: 'Alert', payload: '', GA: msg.topic, dpt: '', devicename: rule.devicename })
|
|
98
|
-
} else {
|
|
99
|
-
// Remove the device from the array
|
|
100
|
-
node.alertedDevices = node.alertedDevices.filter(a => a.topic !== msg.topic)
|
|
101
|
-
node.setLocalStatus({ fill: 'green', shape: 'dot', text: 'Restore', payload: '', GA: msg.topic, dpt: '', devicename: rule.devicename })
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
|
-
}
|
|
105
104
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
105
|
+
// If there's some device to alert, stop current timer and restart
|
|
106
|
+
// This allow the last alerted device to be outputted immediately
|
|
107
|
+
if (bFound && node.whentostart === 'ifnewalert' && node.alertedDevices.length > 0) {
|
|
108
|
+
clearTimeout(node.timerSend)
|
|
109
|
+
// Send directly the second and third message PIN
|
|
110
|
+
node.send([null, node.getSecondPinMSG(), null])
|
|
111
|
+
node.curIndexAlertedDevice = 0 // Restart form the beginning
|
|
112
|
+
node.startTimer()
|
|
113
|
+
}
|
|
114
114
|
}
|
|
115
|
-
}
|
|
116
115
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
116
|
+
// Get the msg to be outputted on second PIN
|
|
117
|
+
node.getSecondPinMSG = () => {
|
|
118
|
+
if (node.alertedDevices.length > 0) {
|
|
119
|
+
const msg = {}
|
|
120
|
+
let sRet = ''
|
|
121
|
+
let sRetLong = ''
|
|
122
|
+
let sTopic = ''
|
|
123
|
+
node.alertedDevices.forEach(function (item) {
|
|
124
|
+
sTopic += item.topic + ', '
|
|
125
|
+
if (item.devicename !== undefined && item.devicename !== '') sRet += item.devicename + ', '
|
|
126
|
+
if (item.longdevicename !== undefined && item.longdevicename !== '') sRetLong += item.longdevicename + ', '
|
|
127
|
+
})
|
|
128
|
+
sTopic = sTopic.slice(0, -2)
|
|
129
|
+
if (sRet.length > 2) sRet = sRet.slice(0, -2)
|
|
130
|
+
if (sRetLong.length > 2) sRetLong = sRetLong.slice(0, -2)
|
|
131
|
+
msg.topic = sTopic
|
|
132
|
+
msg.devicename = sRet
|
|
133
|
+
msg.longdevicename = sRetLong
|
|
134
|
+
msg.count = node.alertedDevices.length
|
|
135
|
+
msg.payload = true
|
|
136
|
+
return msg
|
|
137
|
+
}
|
|
138
138
|
}
|
|
139
|
-
}
|
|
140
139
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
140
|
+
// Get the msg to be outputted on third PIN
|
|
141
|
+
node.getThirdPinMSG = () => {
|
|
142
|
+
if (node.alertedDevices.length > 0) {
|
|
143
|
+
const msg = {}
|
|
144
|
+
let sRet = ''
|
|
145
|
+
let sRetLong = ''
|
|
146
|
+
let sTopic = ''
|
|
147
|
+
const item = node.alertedDevices[0] // Pick the last alerted device
|
|
148
|
+
sTopic = item.topic
|
|
149
|
+
if (item.devicename !== undefined && item.devicename !== '') sRet = item.devicename
|
|
150
|
+
if (item.longdevicename !== undefined && item.longdevicename !== '') sRetLong = item.longdevicename
|
|
151
|
+
msg.topic = sTopic
|
|
152
|
+
msg.devicename = sRet
|
|
153
|
+
msg.longdevicename = sRetLong
|
|
154
|
+
msg.count = node.alertedDevices.length
|
|
155
|
+
msg.payload = true
|
|
156
|
+
return msg
|
|
157
|
+
}
|
|
158
158
|
}
|
|
159
|
-
}
|
|
160
159
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
160
|
+
// 24/04/2021 perform a read on all GA in the rule list. Called both from node.on("input") and knxUltimate-config
|
|
161
|
+
node.initialReadAllDevicesInRules = () => {
|
|
162
|
+
if (node.server) {
|
|
163
|
+
let grpaddr = ''
|
|
164
|
+
for (let i = 0; i < node.rules.length; i++) {
|
|
165
|
+
// rule is { topic: rowRuleTopic, devicename: rowRuleDeviceName, longdevicename: rowRuleLongDeviceName}
|
|
166
|
+
const rule = node.rules[i]
|
|
167
|
+
// READ: Send a Read request to the bus
|
|
168
|
+
grpaddr = rule.topic
|
|
169
|
+
try {
|
|
170
|
+
// Check if it's a group address
|
|
171
|
+
// const ret = Address.KNXAddress.createFromString(grpaddr, Address.KNXAddress.TYPE_GROUP)
|
|
172
|
+
node.setLocalStatus({ fill: 'grey', shape: 'dot', text: 'Read', payload: '', GA: grpaddr, dpt: '', devicename: rule.devicename })
|
|
173
|
+
node.server.writeQueueAdd({ grpaddr, payload: '', dpt: '', outputtype: 'read', nodecallerid: node.id })
|
|
174
|
+
} catch (error) {
|
|
175
|
+
node.setLocalStatus({ fill: 'grey', shape: 'dot', text: 'Not a KNX GA ' + error.message, payload: '', GA: grpaddr, dpt: '', devicename: rule.devicename })
|
|
176
|
+
}
|
|
177
177
|
}
|
|
178
|
+
} else {
|
|
179
|
+
node.setLocalStatus({ fill: 'red', shape: 'ring', text: 'No gateway selected. Unable to read from KNX bus', payload: '', GA: '', dpt: '', devicename: '' })
|
|
178
180
|
}
|
|
179
|
-
} else {
|
|
180
|
-
node.setLocalStatus({ fill: 'red', shape: 'ring', text: 'No gateway selected. Unable to read from KNX bus', payload: '', GA: '', dpt: '', devicename: '' })
|
|
181
181
|
}
|
|
182
|
-
}
|
|
183
182
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
183
|
+
node.on('input', function (msg) {
|
|
184
|
+
if (typeof msg === 'undefined') return
|
|
185
|
+
if (msg.hasOwnProperty('start')) {
|
|
186
|
+
clearTimeout(node.timerSend)
|
|
187
|
+
node.curIndexAlertedDevice = 0 // Restart form the beginning
|
|
188
|
+
if (node.alertedDevices.length > 0) {
|
|
189
|
+
node.send([null, node.getSecondPinMSG(), node.getThirdPinMSG()])
|
|
190
|
+
node.startTimer()
|
|
191
|
+
} else {
|
|
192
|
+
// Nothing more to output
|
|
193
|
+
node.sendNoMoreDevices()
|
|
194
|
+
}
|
|
195
|
+
return
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// 24/04/2021 if payload is read or the output type is set to "read", do a read
|
|
199
|
+
if ((msg.hasOwnProperty('readstatus') && msg.readstatus === true)) {
|
|
200
|
+
node.initialReadAllDevicesInRules()
|
|
201
|
+
return
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (msg.topic === undefined) {
|
|
205
|
+
node.setLocalStatus({ fill: 'grey', shape: 'dot', text: 'ERROR: You must provide a msg.topic', payload: '', GA: '', dpt: '', devicename: '' })
|
|
206
|
+
return
|
|
207
|
+
}
|
|
208
|
+
if (msg.payload === undefined) {
|
|
209
|
+
node.setLocalStatus({ fill: 'grey', shape: 'dot', text: 'ERROR: You must provide payload (true/false)', payload: '', GA: '', dpt: '', devicename: '' })
|
|
210
|
+
return
|
|
211
|
+
}
|
|
212
|
+
msg.knx = { dpt: '1.001' }
|
|
213
|
+
node.handleSend(msg)
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
node.on('close', function (done) {
|
|
187
217
|
clearTimeout(node.timerSend)
|
|
188
|
-
node.
|
|
218
|
+
if (node.server) {
|
|
219
|
+
node.server.removeClient(node)
|
|
220
|
+
}
|
|
221
|
+
done()
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
node.handleTimer = () => {
|
|
189
225
|
if (node.alertedDevices.length > 0) {
|
|
190
|
-
|
|
226
|
+
const count = node.alertedDevices.length
|
|
227
|
+
if (node.curIndexAlertedDevice > count - 1) {
|
|
228
|
+
node.curIndexAlertedDevice = 0
|
|
229
|
+
if (node.whentostart === 'manualstart') {
|
|
230
|
+
node.curIndexAlertedDevice = 0 // Restart form the beginning
|
|
231
|
+
return
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// Create output message
|
|
235
|
+
try {
|
|
236
|
+
const curDev = node.alertedDevices[node.curIndexAlertedDevice] // is { topic: rule.topic, devicename: rule.devicename }
|
|
237
|
+
const msg = {}
|
|
238
|
+
msg.topic = curDev.topic
|
|
239
|
+
msg.count = count
|
|
240
|
+
msg.devicename = curDev.devicename
|
|
241
|
+
msg.longdevicename = curDev.longdevicename
|
|
242
|
+
msg.payload = true
|
|
243
|
+
node.send([msg, null, null])
|
|
244
|
+
} catch (error) {
|
|
245
|
+
}
|
|
246
|
+
node.curIndexAlertedDevice += 1
|
|
247
|
+
// Restart timer
|
|
191
248
|
node.startTimer()
|
|
192
249
|
} else {
|
|
193
250
|
// Nothing more to output
|
|
194
251
|
node.sendNoMoreDevices()
|
|
195
252
|
}
|
|
196
|
-
return
|
|
197
253
|
}
|
|
198
254
|
|
|
199
|
-
//
|
|
200
|
-
|
|
201
|
-
node.
|
|
202
|
-
|
|
255
|
+
// Start timer
|
|
256
|
+
node.startTimer = () => {
|
|
257
|
+
clearTimeout(node.timerSend)
|
|
258
|
+
node.timerSend = setTimeout(() => {
|
|
259
|
+
node.handleTimer()
|
|
260
|
+
}, node.timerinterval * 1000)
|
|
203
261
|
}
|
|
204
262
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
263
|
+
// As soon as there no more devices..
|
|
264
|
+
node.sendNoMoreDevices = () => {
|
|
265
|
+
const msg = {}
|
|
266
|
+
msg.topic = ''
|
|
267
|
+
msg.count = 0
|
|
268
|
+
msg.devicename = ''
|
|
269
|
+
msg.longdevicename = ''
|
|
270
|
+
msg.payload = false
|
|
271
|
+
node.send([msg, msg, msg])
|
|
212
272
|
}
|
|
213
|
-
msg.knx = { dpt: '1.001' }
|
|
214
|
-
node.handleSend(msg)
|
|
215
|
-
})
|
|
216
273
|
|
|
217
|
-
|
|
218
|
-
|
|
274
|
+
// Init
|
|
275
|
+
node.sendNoMoreDevices()
|
|
276
|
+
|
|
277
|
+
// On each deploy, unsubscribe+resubscribe
|
|
219
278
|
if (node.server) {
|
|
220
279
|
node.server.removeClient(node)
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
})
|
|
224
|
-
|
|
225
|
-
node.handleTimer = () => {
|
|
226
|
-
if (node.alertedDevices.length > 0) {
|
|
227
|
-
const count = node.alertedDevices.length
|
|
228
|
-
if (node.curIndexAlertedDevice > count - 1) {
|
|
229
|
-
node.curIndexAlertedDevice = 0
|
|
230
|
-
if (node.whentostart === 'manualstart') {
|
|
231
|
-
node.curIndexAlertedDevice = 0 // Restart form the beginning
|
|
232
|
-
return
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
// Create output message
|
|
236
|
-
try {
|
|
237
|
-
const curDev = node.alertedDevices[node.curIndexAlertedDevice] // is { topic: rule.topic, devicename: rule.devicename }
|
|
238
|
-
const msg = {}
|
|
239
|
-
msg.topic = curDev.topic
|
|
240
|
-
msg.count = count
|
|
241
|
-
msg.devicename = curDev.devicename
|
|
242
|
-
msg.longdevicename = curDev.longdevicename
|
|
243
|
-
msg.payload = true
|
|
244
|
-
node.send([msg, null, null])
|
|
245
|
-
} catch (error) {
|
|
280
|
+
if (node.topic !== '' || node.topicSave !== '') {
|
|
281
|
+
node.server.addClient(node)
|
|
246
282
|
}
|
|
247
|
-
node.curIndexAlertedDevice += 1
|
|
248
|
-
// Restart timer
|
|
249
|
-
node.startTimer()
|
|
250
|
-
} else {
|
|
251
|
-
// Nothing more to output
|
|
252
|
-
node.sendNoMoreDevices()
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Start timer
|
|
257
|
-
node.startTimer = () => {
|
|
258
|
-
clearTimeout(node.timerSend)
|
|
259
|
-
node.timerSend = setTimeout(() => {
|
|
260
|
-
node.handleTimer()
|
|
261
|
-
}, node.timerinterval * 1000)
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// As soon as there no more devices..
|
|
265
|
-
node.sendNoMoreDevices = () => {
|
|
266
|
-
const msg = {}
|
|
267
|
-
msg.topic = ''
|
|
268
|
-
msg.count = 0
|
|
269
|
-
msg.devicename = ''
|
|
270
|
-
msg.longdevicename = ''
|
|
271
|
-
msg.payload = false
|
|
272
|
-
node.send([msg, msg, msg])
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// Init
|
|
276
|
-
node.sendNoMoreDevices()
|
|
277
|
-
|
|
278
|
-
// On each deploy, unsubscribe+resubscribe
|
|
279
|
-
if (node.server) {
|
|
280
|
-
node.server.removeClient(node)
|
|
281
|
-
if (node.topic !== '' || node.topicSave !== '') {
|
|
282
|
-
node.server.addClient(node)
|
|
283
283
|
}
|
|
284
284
|
}
|
|
285
|
+
RED.nodes.registerType('knxUltimateAlerter', knxUltimateAlerter)
|
|
285
286
|
}
|
|
286
|
-
RED.nodes.registerType('knxUltimateAlerter', knxUltimateAlerter)
|
|
287
|
-
}
|
|
@@ -1055,7 +1055,7 @@ Start typing in the GA field, the name or group address of your KNX device, the
|
|
|
1055
1055
|
|Property|Description|
|
|
1056
1056
|
|--|--|
|
|
1057
1057
|
| Blink | *true* Blink the light, *false* Stop blinking. Blinks the light on and off. Useful for signalling. Works with all HUE lights. |
|
|
1058
|
-
| Color Cycle | *true* start cycle, *false* Stop cycle. Randomly changes the HUE light's color at regular interval. Works with all HUE lights having color capabilities. |
|
|
1058
|
+
| Color Cycle | *true* start cycle, *false* Stop cycle. Randomly changes the HUE light's color at regular interval. Works with all HUE lights having color capabilities. The color effect will start 10 seconds after set. |
|
|
1059
1059
|
|
|
1060
1060
|
<br/>
|
|
1061
1061
|
|
|
@@ -84,7 +84,6 @@ module.exports = function (RED) {
|
|
|
84
84
|
|
|
85
85
|
// First, switch on the light if off
|
|
86
86
|
if (node.currentHUEDevice !== undefined && node.currentHUEDevice.on.on === false) {
|
|
87
|
-
node.disableBSC = true // Temporary disable sending the 100% to the KNX brightness state group address.
|
|
88
87
|
node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, { on: { on: true } }, 'setLight')
|
|
89
88
|
}
|
|
90
89
|
|
|
@@ -120,15 +119,13 @@ module.exports = function (RED) {
|
|
|
120
119
|
|
|
121
120
|
//If the brightness is not zero, must send true to the "on" property of the HUE lamp.
|
|
122
121
|
if (node.currentHUEDevice !== undefined && node.currentHUEDevice.on.on === false && msg.payload > 0) {
|
|
123
|
-
node.disableBSC = true
|
|
124
122
|
node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, { on: { on: true } }, 'setLight')
|
|
125
123
|
setTimeout(() => {
|
|
126
|
-
// Wait to the light to turn on and disable the node.disableBSC, then send the brightness %
|
|
127
124
|
node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state, 'setLight')
|
|
128
125
|
}, 1000);
|
|
129
126
|
} else {
|
|
130
|
-
node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state, 'setLight')
|
|
131
|
-
}
|
|
127
|
+
node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state, 'setLight')
|
|
128
|
+
}
|
|
132
129
|
node.setNodeStatusHue({ fill: 'green', shape: 'dot', text: 'KNX->HUE', payload: state })
|
|
133
130
|
break
|
|
134
131
|
case config.GALightColor:
|
|
@@ -195,7 +192,10 @@ module.exports = function (RED) {
|
|
|
195
192
|
node.status({ fill: 'red', shape: 'dot', text: 'KNX->HUE error ' + error.message + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
|
|
196
193
|
}
|
|
197
194
|
}
|
|
195
|
+
|
|
196
|
+
|
|
198
197
|
// Start dimming
|
|
198
|
+
// ***********************************************************
|
|
199
199
|
node.timerDim = undefined
|
|
200
200
|
node.dimDirection = {}
|
|
201
201
|
node.timeoutDim = 0
|
|
@@ -221,9 +221,12 @@ module.exports = function (RED) {
|
|
|
221
221
|
node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, node.dimDirection, 'setLight')
|
|
222
222
|
}, 700)
|
|
223
223
|
}
|
|
224
|
+
// ***********************************************************
|
|
225
|
+
|
|
224
226
|
|
|
225
227
|
// Start dimming tunable white
|
|
226
228
|
// mirek: required(integer minimum: 153, maximum: 500)
|
|
229
|
+
// ***********************************************************
|
|
227
230
|
node.timerDimTunableWhite = undefined
|
|
228
231
|
node.dimDirectionTunableWhite = {}
|
|
229
232
|
node.timeoutDimTunableWhite = 0
|
|
@@ -247,16 +250,17 @@ module.exports = function (RED) {
|
|
|
247
250
|
node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, node.dimDirectionTunableWhite, 'setLight')
|
|
248
251
|
}, 700)
|
|
249
252
|
}
|
|
253
|
+
// ***********************************************************
|
|
254
|
+
|
|
250
255
|
|
|
251
256
|
node.handleSendHUE = _event => {
|
|
252
257
|
try {
|
|
253
258
|
if (_event.id === config.hueDevice) {
|
|
254
259
|
if (_event.hasOwnProperty('on')) {
|
|
255
260
|
node.updateKNXLightState(_event.on.on)
|
|
256
|
-
if (
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
node.updateKNXBrightnessState(_event.on.on === true ? 100 : 0)
|
|
261
|
+
if (config.updateGALightBrightnessStateOnHUELightStatusChange === undefined || config.updateGALightBrightnessStateOnHUELightStatusChange === 'yes') {
|
|
262
|
+
// Mimic KNX by sending 100% or 0% brightness
|
|
263
|
+
node.updateKNXBrightnessState(_event.on.on === true ? 100 : 0)
|
|
260
264
|
}
|
|
261
265
|
if (node.currentHUEDevice !== undefined) node.currentHUEDevice.on = _event.on // Update the internal object representing the current light
|
|
262
266
|
}
|
|
@@ -58,7 +58,7 @@ module.exports = function (RED) {
|
|
|
58
58
|
}
|
|
59
59
|
node.startDimStopper(knxMsgPayload)
|
|
60
60
|
} else if (knxMsgPayload.dpt.startsWith('5.001')) {
|
|
61
|
-
// 0
|
|
61
|
+
// 0 - maximum: 32767
|
|
62
62
|
node.brightnessState < 100 ? node.brightnessState += 20 : node.brightnessState = 100
|
|
63
63
|
knxMsgPayload.payload = node.brightnessState
|
|
64
64
|
} else if (knxMsgPayload.dpt.startsWith('232.600')) {
|
|
@@ -64,7 +64,7 @@ module.exports = function (RED) {
|
|
|
64
64
|
devicename = devicename || ''
|
|
65
65
|
dpt = (typeof dpt === 'undefined' || dpt == '') ? '' : ' DPT' + dpt
|
|
66
66
|
payload = typeof payload === 'object' ? JSON.stringify(payload) : payload
|
|
67
|
-
node.status({ fill, shape, text: GA + payload + (
|
|
67
|
+
node.status({ fill, shape, text: GA + payload + (node.listenallga === true ? ' ' + devicename : '') + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ' ' + text })
|
|
68
68
|
} catch (error) {
|
|
69
69
|
|
|
70
70
|
}
|
|
@@ -36,7 +36,7 @@ module.exports = function (RED) {
|
|
|
36
36
|
devicename = devicename || ''
|
|
37
37
|
dpt = (typeof dpt === 'undefined' || dpt == '') ? '' : ' DPT' + dpt
|
|
38
38
|
payload = typeof payload === 'object' ? JSON.stringify(payload) : payload
|
|
39
|
-
node.status({ fill, shape, text: GA + payload + (
|
|
39
|
+
node.status({ fill, shape, text: GA + payload + (node.listenallga === true ? ' ' + devicename : '') + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ' ' + text })
|
|
40
40
|
} catch (error) {
|
|
41
41
|
}
|
|
42
42
|
}
|
|
@@ -102,7 +102,7 @@ module.exports = function (RED) {
|
|
|
102
102
|
devicename = devicename || ''
|
|
103
103
|
dpt = (typeof dpt === 'undefined' || dpt === '') ? '' : ' DPT' + dpt
|
|
104
104
|
payload = typeof payload === 'object' ? JSON.stringify(payload) : payload
|
|
105
|
-
node.status({ fill, shape, text: GA + payload + (
|
|
105
|
+
node.status({ fill, shape, text: GA + payload + (node.listenallga === true ? ' ' + devicename : '') + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ' ' + text })
|
|
106
106
|
// 16/02/2020 signal errors to the server
|
|
107
107
|
if (fill.toUpperCase() === 'RED') {
|
|
108
108
|
if (node.server) {
|
|
@@ -39,7 +39,7 @@ module.exports = function (RED) {
|
|
|
39
39
|
devicename = devicename || ''
|
|
40
40
|
dpt = (typeof dpt === 'undefined' || dpt == '') ? '' : ' DPT' + dpt
|
|
41
41
|
payload = typeof payload === 'object' ? JSON.stringify(payload) : payload
|
|
42
|
-
node.status({ fill, shape, text: GA + payload + (
|
|
42
|
+
node.status({ fill, shape, text: GA + payload + (node.listenallga === true ? ' ' + devicename : '') + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ' ' + text })
|
|
43
43
|
} catch (error) {
|
|
44
44
|
}
|
|
45
45
|
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"engines": {
|
|
4
4
|
"node": ">=16.0.0"
|
|
5
5
|
},
|
|
6
|
-
"version": "2.1.
|
|
6
|
+
"version": "2.1.27",
|
|
7
7
|
"description": "Control your KNX intallation via Node-Red! Single Node KNX IN/OUT with optional ETS group address importer. Easy to use and highly configurable. With integrated Philips HUE devices handling.",
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"mkdirp": "3.0.1",
|