node-red-contrib-knx-ultimate 1.3.28 → 1.3.32
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 +19 -0
- package/KNXEngine/dptlib/dpt16.js +5 -2
- package/README.md +24 -13
- package/img/c/agata.png +0 -0
- package/img/c/knxuserforum.png +0 -0
- package/img/c/viveresmart.png +0 -0
- package/img/wiki/viewer2.png +0 -0
- package/nodes/knxUltimateLoadControl.js +66 -36
- package/nodes/knxUltimateViewer.html +13 -1
- package/nodes/knxUltimateViewer.js +43 -25
- package/nodes/locales/de/knxUltimateLoadControl.json +3 -3
- package/nodes/locales/zh-CN/knxUltimateLoadControl.json +1 -1
- package/package.json +1 -1
- package/img/covidfree.png +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,25 @@
|
|
|
6
6
|
|
|
7
7
|
# CHANGELOG
|
|
8
8
|
|
|
9
|
+
<p>
|
|
10
|
+
<b>Version 1.3.32</b> - February 2022<br/>
|
|
11
|
+
- FIX Datapoint 16.001: fixed an issue with the ISO8859-1 encoding.<br/>
|
|
12
|
+
</p>
|
|
13
|
+
<p>
|
|
14
|
+
<b>Version 1.3.31</b> - February 2022<br/>
|
|
15
|
+
- KNX Viewer node: now the payload is formatted depending on value type.<br/>
|
|
16
|
+
- KNX Viewer node: now the list is ordered by group address.<br/>
|
|
17
|
+
- KNX Viewer node: added a second output pin, that emits an Array containing all group addresses.<br/>
|
|
18
|
+
</p>
|
|
19
|
+
<p>
|
|
20
|
+
<b>Version 1.3.30</b> - February 2022<br/>
|
|
21
|
+
- KNX Viewer node: changed the Datetime display to local time format.<br/>
|
|
22
|
+
</p>
|
|
23
|
+
<p>
|
|
24
|
+
<b>Version 1.3.29</b> - February 2022<br/>
|
|
25
|
+
- Load Control: the timer for shedding won't everytime obey to what you've set. Fixed. <br/>
|
|
26
|
+
- Load Control: Added pre-shedding yellow warning message in the node status.<br/>
|
|
27
|
+
</p>
|
|
9
28
|
<p>
|
|
10
29
|
<b>Version 1.3.28</b> - February 2022<br/>
|
|
11
30
|
- NEW: KNX Viewer: this node allow you to see all datapints and values in a dashboard wirget. https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/knxUltimateViewer<br/>
|
|
@@ -22,7 +22,8 @@ exports.formatAPDU = function (value) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
var buf = new Buffer.alloc(14);
|
|
25
|
-
buf.write(value, '
|
|
25
|
+
if (this.subtypeid === "001") buf.write(value, 'latin1');
|
|
26
|
+
if (this.subtypeid === "000") buf.write(value, 'ascii');
|
|
26
27
|
return buf;
|
|
27
28
|
|
|
28
29
|
}
|
|
@@ -33,7 +34,9 @@ exports.fromBuffer = function (buf) {
|
|
|
33
34
|
knxLog.get().error("DPT6: Buffer should be 14 byte long, got", buf.length);
|
|
34
35
|
return null;
|
|
35
36
|
}
|
|
36
|
-
return buf.toString('
|
|
37
|
+
if (this.subtypeid === "001") return buf.toString('latin1');
|
|
38
|
+
if (this.subtypeid === "000") return buf.toString('ascii');
|
|
39
|
+
|
|
37
40
|
|
|
38
41
|
}
|
|
39
42
|
|
package/README.md
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
[![NPM downloads total][npm-downloads-total-image]][npm-url]
|
|
8
8
|
[![MIT License][license-image]][license-url]
|
|
9
9
|
[](https://standardjs.com)
|
|
10
|
-
[![
|
|
10
|
+
[![Youtube][youtube-image]][youtube-url]
|
|
11
11
|
[](https://www.paypal.me/techtoday)
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|

|
|
15
15
|
|
|
16
|
-
Control your KNX intallation via Node-Red!
|
|
16
|
+
Control your KNX intallation via Node-Red!
|
|
17
17
|
|
|
18
18
|
**You can use it immediately!**
|
|
19
19
|
```javascript
|
|
@@ -21,7 +21,8 @@ payload = true // Turn light on
|
|
|
21
21
|
payload = {red:255, green:200, blue:30} // Put some colors in our life
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
|
|
25
|
+
## NODE LIST
|
|
25
26
|
|
|
26
27
|
* **KNX-ULTIMATE node** [here](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/2.-Node-Configuration), allow you to control your *KNX installation* via Node-Red. You can control all your KNX devices as well as create a *Virtual Device* in Node-Red, to link external *non KNX* devices, and make it compatible with your KNX installation. I'ts very SIMPLE TO USE thus very customizable.
|
|
27
28
|
* **SCENE CONTROLLER node** [here](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/SceneController-Configuration), The scene controller node can act as a real scene controller, with recall and save of the current scene.
|
|
@@ -29,7 +30,11 @@ payload = {red:255, green:200, blue:30} // Put some colors in our life
|
|
|
29
30
|
* **LOGGER node** [here](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/Logger-Configuration), creates an XML diagnostic file, compatible with ETS. You can open it with ETS for diagnostic pourposes. Node: the Logger currently doesn't record the telegrams coming from KNX-Ultimate if you use a **KNX/IP Interface**.
|
|
30
31
|
* **GLOBAL CONTEXT node** [here](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/GlobalVariable), exposes the group addresses to a Global Context variable, to be used in function nodes.
|
|
31
32
|
* **ALERTER node** [here](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/Alerter-Configuration). With the Alerter node you can signal to a display or to the node-red-contrib-tts-ultimate node (audio feedback), whenever the selected devices are alerted, i.e. they have payload **true**.
|
|
33
|
+
* **LOAD CONTROL node** [here](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/LoadControl-Configuration). Control your loads (Oven, Washing machine, etc..) and avoit shutting down the main voltage due to too high power consumption.
|
|
34
|
+
* **VIEWER node** [here](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/knxUltimateViewer). View all Group Addresses and values of your KNX BUS, in the Node-Red Dashboard.
|
|
32
35
|
|
|
36
|
+
<br>
|
|
37
|
+
<br>
|
|
33
38
|
|
|
34
39
|
## CHANGELOG
|
|
35
40
|
|
|
@@ -38,18 +43,25 @@ payload = {red:255, green:200, blue:30} // Put some colors in our life
|
|
|
38
43
|
<br>
|
|
39
44
|
<br>
|
|
40
45
|
|
|
41
|
-
##
|
|
46
|
+
## VOLUNTEER NEEDED FOR KNX SECURE
|
|
47
|
+
|
|
42
48
|
**************************************************
|
|
43
49
|
**************************************************
|
|
44
|
-
|
|
50
|
+
|
|
51
|
+
KNX-Secure is under development and **should** be ready by mid 2022.<br/>
|
|
52
|
+
Many users requested me to "extract" the baseline KNX API and make it accessible via npmjs. Here is it.<br/>
|
|
53
|
+
The API is named **KNXUltimate**. In the README page is well documented and there are also samples for unsecure and secure KNX connections.
|
|
54
|
+
* <a href="https://github.com/Supergiovane/KNXUltimate#readme">KNXUltimate API</a>
|
|
55
|
+
|
|
56
|
+
I need volunteer helping in development of KNX Secure.<br/>
|
|
57
|
+
High knowledge of cryptography and KNX is needed.
|
|
45
58
|
**************************************************
|
|
46
59
|
**************************************************
|
|
47
60
|
<br>
|
|
48
61
|
<br>
|
|
49
62
|
|
|
50
|
-
## KNX SECURE
|
|
51
63
|
|
|
52
|
-
|
|
64
|
+
|
|
53
65
|
|
|
54
66
|
## HELP, SAMPLES, TROUBLESHOOT, WIKI, FAQ, BEST PRACTICES
|
|
55
67
|
|
|
@@ -67,8 +79,7 @@ Click your language to go to the documentation.<br/>
|
|
|
67
79
|
## STARTER PACK
|
|
68
80
|
|
|
69
81
|
* [Wiki and Help](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki)
|
|
70
|
-
* [Youtube
|
|
71
|
-
* [Facebook page](https://www.facebook.com/supergiovaneDev)
|
|
82
|
+
* [Youtube channel](https://www.youtube.com/playlist?list=PL9Yh1bjbLAYpfy1Auz6CKDfXUusgMwOQr)
|
|
72
83
|
* [FAQ + Troubleshoot](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/5.-FAQ-Troubleshoot)
|
|
73
84
|
* [Security best practices](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/SECURITY)
|
|
74
85
|
|
|
@@ -297,11 +308,11 @@ List of commercial companies, which have given us permission to be mentioned on
|
|
|
297
308
|
## FRIENDLY COMMUNITIES AROUND THE WORLD
|
|
298
309
|
|
|
299
310
|
**Italy**
|
|
300
|
-
* [
|
|
311
|
+
* [](https://www.facebook.com/groups/viveresmart)
|
|
301
312
|
* [VivereSmart TV](https://www.youtube.com/channel/UC6GlFhcbNuoSEejZ_HlCynA)
|
|
302
313
|
|
|
303
314
|
**Germany**
|
|
304
|
-
* [knx-
|
|
315
|
+
* [](https://knx-user-forum.de/forum/öffentlicher-bereich/knx-eib-forum/1389088-knx-node-for-node-red)
|
|
305
316
|
|
|
306
317
|
**China**
|
|
307
318
|
* [QQ group: 837579219 (加群需要备注 “来自github”](tencent://groupwpa/?subcmd=all¶m=7b2267726f757055696e223a3833373537393231392c2274696d655374616d70223a313633303934363639312c22617574684b6579223a22762b72482b466f4a496a75613033794e4a30744a6970756c55753639424f4d55724f464c4a6c474b77346a30326b7a4f7a3338535536517844684d7756414d62222c2261757468223a22227d&jump_from=)
|
|
@@ -319,5 +330,5 @@ List of commercial companies, which have given us permission to be mentioned on
|
|
|
319
330
|
[npm-version-image]: https://img.shields.io/npm/v/node-red-contrib-knx-ultimate.svg
|
|
320
331
|
[npm-downloads-month-image]: https://img.shields.io/npm/dm/node-red-contrib-knx-ultimate.svg
|
|
321
332
|
[npm-downloads-total-image]: https://img.shields.io/npm/dt/node-red-contrib-knx-ultimate.svg
|
|
322
|
-
[
|
|
323
|
-
[
|
|
333
|
+
[youtube-image]: https://img.shields.io/badge/Visit%20me-Youtube-red
|
|
334
|
+
[youtube-url]: https://www.youtube.com/channel/UCA9RsLps1IthT7fDSeUbRZw/playlists
|
package/img/c/agata.png
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/img/wiki/viewer2.png
CHANGED
|
Binary file
|
|
@@ -81,7 +81,6 @@ module.exports = function (RED) {
|
|
|
81
81
|
node.status({ fill: fill, shape: shape, text: text + " Shed:" + node.sheddingStage + " Power:" + node.totalWatt + "W" + " Limit:" + node.wattLimit + "W (" + dDate.getDate() + ", " + dDate.toLocaleTimeString() + ")" });
|
|
82
82
|
} catch (error) {
|
|
83
83
|
}
|
|
84
|
-
|
|
85
84
|
}
|
|
86
85
|
|
|
87
86
|
// This function is called by the knx-ultimate config node.
|
|
@@ -90,8 +89,13 @@ module.exports = function (RED) {
|
|
|
90
89
|
// Update the Total Watt?
|
|
91
90
|
if (msg.topic === node.topic && msg.payload !== "" && msg.payload !== null && msg.payload !== undefined) {
|
|
92
91
|
node.totalWatt = msg.payload;
|
|
93
|
-
// Update current consumption
|
|
94
|
-
node.
|
|
92
|
+
// Update current consumption only if the node is in idle state
|
|
93
|
+
if (node.timerIncreaseShedding === null && node.timerDecreaseShedding === null) {
|
|
94
|
+
if (node.setLocalStatusTotalWattTimer === null) clearInterval(node.setLocalStatusTotalWattTimer);
|
|
95
|
+
node.setLocalStatusTotalWattTimer = setTimeout(() => {
|
|
96
|
+
node.setLocalStatus({ fill: "grey" });
|
|
97
|
+
}, 2000);
|
|
98
|
+
}
|
|
95
99
|
return;
|
|
96
100
|
}
|
|
97
101
|
|
|
@@ -123,7 +127,7 @@ module.exports = function (RED) {
|
|
|
123
127
|
node.initialReadAllDevicesInRules = () => {
|
|
124
128
|
if (node.server) {
|
|
125
129
|
// Read status of the Total Power GA
|
|
126
|
-
node.server.writeQueueAdd({ grpaddr: node.topic, payload: "", dpt: "", outputtype: "read", nodecallerid: node.id });
|
|
130
|
+
if (node.topic !== undefined && node.topic !== null && node.topic !== "") node.server.writeQueueAdd({ grpaddr: node.topic, payload: "", dpt: "", outputtype: "read", nodecallerid: node.id });
|
|
127
131
|
|
|
128
132
|
for (var i = 0; i < node.deviceList.length; i++) {
|
|
129
133
|
let grpaddr = node.deviceList[i].monitorGA;
|
|
@@ -143,27 +147,55 @@ module.exports = function (RED) {
|
|
|
143
147
|
}
|
|
144
148
|
}
|
|
145
149
|
|
|
150
|
+
node.startMainTimer = () => {
|
|
151
|
+
if (node.mainTimer !== null) clearInterval(node.mainTimer);// Clear the timer
|
|
152
|
+
node.mainTimer = setInterval(() => {
|
|
153
|
+
// Issue a READ on all GA's
|
|
154
|
+
node.initialReadAllDevicesInRules();
|
|
155
|
+
|
|
156
|
+
// Check consumption
|
|
157
|
+
if (node.totalWatt > node.wattLimit) {
|
|
158
|
+
// Start increasing shedding!
|
|
159
|
+
if (node.sheddingStage < node.deviceList.length) {
|
|
160
|
+
if (node.timerIncreaseShedding === null) {
|
|
161
|
+
setTimeout(() => {
|
|
162
|
+
node.setLocalStatus({ fill: "yellow", shape: "dot", text: "I'm about to shed the load " + node.sheddingStage, payload: "", GA: "", dpt: "", devicename: "" });
|
|
163
|
+
}, 2000);
|
|
164
|
+
if (node.timerDecreaseShedding !== null) clearTimeout(node.timerDecreaseShedding);// Clear the decreasing timer
|
|
165
|
+
node.startTimerIncreaseShedding();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
} else if (node.totalWatt <= node.wattLimit) {
|
|
169
|
+
// Start decreasing shedding!
|
|
170
|
+
if (node.sheddingStage > 0) {
|
|
171
|
+
if (node.timerDecreaseShedding === null) {
|
|
172
|
+
setTimeout(() => {
|
|
173
|
+
node.setLocalStatus({ fill: "yellow", shape: "dot", text: "I'm about to unshed the load " + node.sheddingStage, payload: "", GA: "", dpt: "", devicename: "" });
|
|
174
|
+
}, 2000);
|
|
175
|
+
if (node.timerIncreaseShedding !== null) clearTimeout(node.timerIncreaseShedding);// Clear the increasing timer
|
|
176
|
+
node.startTimerDecreaseShedding();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}, 10000);
|
|
181
|
+
}
|
|
146
182
|
|
|
147
183
|
// Start the timer
|
|
148
184
|
node.startTimerIncreaseShedding = () => {
|
|
149
185
|
|
|
150
186
|
// Increase shedding timer (Switch off devices)
|
|
151
|
-
if (node.timerIncreaseShedding !== null)
|
|
152
|
-
node.timerIncreaseShedding =
|
|
187
|
+
if (node.timerIncreaseShedding !== null) clearTimeout(node.timerIncreaseShedding);
|
|
188
|
+
node.timerIncreaseShedding = setTimeout(() => {
|
|
153
189
|
if (node.server) {
|
|
154
|
-
// Issue a READ request to the main Watt GA
|
|
155
|
-
// The devices should automatically send a value on change, but... you know....
|
|
156
|
-
if (node.topic !== undefined && node.topic !== null && node.topic !== "") node.server.writeQueueAdd({ grpaddr: node.topic, payload: "", dpt: "", outputtype: "read", nodecallerid: node.id });
|
|
157
|
-
|
|
158
190
|
// Check consumption
|
|
159
191
|
if (node.totalWatt > node.wattLimit) {
|
|
160
192
|
// Start increasing shedding!
|
|
161
193
|
if (node.sheddingStage < node.deviceList.length) {
|
|
162
194
|
node.increaseShedding();
|
|
163
|
-
node.startTimerDecreaseShedding(); // Reset the decreasing timer from beginning
|
|
164
195
|
}
|
|
165
196
|
}
|
|
166
197
|
}
|
|
198
|
+
node.timerIncreaseShedding = null; // Nullify the timer.
|
|
167
199
|
}, node.sheddingCheckInterval);
|
|
168
200
|
}
|
|
169
201
|
|
|
@@ -171,18 +203,18 @@ module.exports = function (RED) {
|
|
|
171
203
|
node.startTimerDecreaseShedding = () => {
|
|
172
204
|
|
|
173
205
|
// Decrease shedding timer (Switch devices on again)
|
|
174
|
-
if (node.timerDecreaseShedding !== null)
|
|
175
|
-
node.timerDecreaseShedding =
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
206
|
+
if (node.timerDecreaseShedding !== null) clearTimeout(node.timerDecreaseShedding);
|
|
207
|
+
node.timerDecreaseShedding = setTimeout(() => {
|
|
208
|
+
if (node.server) {
|
|
209
|
+
// Check consumption
|
|
210
|
+
if (node.totalWatt <= node.wattLimit) {
|
|
211
|
+
// Start decreasing shedding!
|
|
212
|
+
if (node.sheddingStage > 0) {
|
|
213
|
+
node.decreaseShedding();
|
|
214
|
+
}
|
|
183
215
|
}
|
|
184
216
|
}
|
|
185
|
-
|
|
217
|
+
node.timerDecreaseShedding = null; // Nullify timer
|
|
186
218
|
}, node.sheddingRestoreDelay);
|
|
187
219
|
}
|
|
188
220
|
|
|
@@ -266,20 +298,18 @@ module.exports = function (RED) {
|
|
|
266
298
|
node.setLocalStatus({ fill: "green", shape: "dot", text: "All loads have been restored" });
|
|
267
299
|
}, 1000);
|
|
268
300
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
301
|
}
|
|
272
302
|
|
|
273
303
|
// Start
|
|
274
|
-
node.
|
|
304
|
+
node.startMainTimer();
|
|
275
305
|
|
|
276
306
|
node.on("input", function (msg) {
|
|
277
307
|
if (typeof msg === "undefined") return;
|
|
278
308
|
|
|
279
309
|
// Reset the shedding and activate all loads
|
|
280
310
|
if (msg.hasOwnProperty("reset")) {
|
|
281
|
-
if (node.timerDecreaseShedding !== null)
|
|
282
|
-
if (node.timerIncreaseShedding !== null)
|
|
311
|
+
if (node.timerDecreaseShedding !== null) clearTimeout(node.timerDecreaseShedding);
|
|
312
|
+
if (node.timerIncreaseShedding !== null) clearTimeout(node.timerIncreaseShedding);
|
|
283
313
|
node.sheddingStage = 0;
|
|
284
314
|
for (let index = 0; index < node.deviceList.length; index++) {
|
|
285
315
|
const oRow = node.deviceList[index];
|
|
@@ -287,16 +317,15 @@ module.exports = function (RED) {
|
|
|
287
317
|
}
|
|
288
318
|
setTimeout(() => {
|
|
289
319
|
node.setLocalStatus({ fill: "green", shape: "dot", text: "All loads have been restored" });
|
|
290
|
-
// Restart shedding timer
|
|
291
|
-
node.startTimerIncreaseShedding();
|
|
292
320
|
}, 1000);
|
|
293
321
|
node.send({ topic: node.name || node.topic, operation: "Reset", payload: node.sheddingStage });
|
|
294
322
|
}
|
|
295
323
|
|
|
296
324
|
// Disable the shedding node
|
|
297
325
|
if (msg.hasOwnProperty("disable")) {
|
|
298
|
-
if (node.timerDecreaseShedding !== null)
|
|
299
|
-
if (node.timerIncreaseShedding !== null)
|
|
326
|
+
if (node.timerDecreaseShedding !== null) clearTimeout(node.timerDecreaseShedding);
|
|
327
|
+
if (node.timerIncreaseShedding !== null) clearTimeout(node.timerIncreaseShedding);
|
|
328
|
+
if (node.mainTimer !== null) clearInterval(node.mainTimer);
|
|
300
329
|
setTimeout(() => {
|
|
301
330
|
node.setLocalStatus({ fill: "grey", shape: "dot", text: "Disabled" });
|
|
302
331
|
}, 1000);
|
|
@@ -305,12 +334,12 @@ module.exports = function (RED) {
|
|
|
305
334
|
|
|
306
335
|
// Disable the shedding node
|
|
307
336
|
if (msg.hasOwnProperty("enable")) {
|
|
308
|
-
if (node.timerDecreaseShedding !== null)
|
|
309
|
-
if (node.timerIncreaseShedding !== null)
|
|
337
|
+
if (node.timerDecreaseShedding !== null) clearTimeout(node.timerDecreaseShedding);
|
|
338
|
+
if (node.timerIncreaseShedding !== null) clearTimeout(node.timerIncreaseShedding);
|
|
310
339
|
setTimeout(() => {
|
|
311
340
|
node.setLocalStatus({ fill: "green", shape: "dot", text: "Enabled" });
|
|
312
|
-
// Restart
|
|
313
|
-
node.
|
|
341
|
+
// Restart timer
|
|
342
|
+
node.startMainTimer();
|
|
314
343
|
}, 1000);
|
|
315
344
|
node.send({ topic: node.name || node.topic, operation: "Enabled", payload: node.sheddingStage });
|
|
316
345
|
}
|
|
@@ -323,8 +352,9 @@ module.exports = function (RED) {
|
|
|
323
352
|
})
|
|
324
353
|
|
|
325
354
|
node.on("close", function (done) {
|
|
326
|
-
if (node.
|
|
327
|
-
if (node.
|
|
355
|
+
if (node.mainTimer !== null) clearInterval(node.mainTimer);
|
|
356
|
+
if (node.timerDecreaseShedding !== null) clearTimeout(node.timerDecreaseShedding);
|
|
357
|
+
if (node.timerIncreaseShedding !== null) clearTimeout(node.timerIncreaseShedding);
|
|
328
358
|
if (node.server) {
|
|
329
359
|
node.server.removeClient(node)
|
|
330
360
|
}
|
|
@@ -8,7 +8,19 @@
|
|
|
8
8
|
name: { value: "KNXViewer", validate: RED.validators.regex(/^[a-z]+$/i) }
|
|
9
9
|
},
|
|
10
10
|
inputs: 0,
|
|
11
|
-
outputs:
|
|
11
|
+
outputs: 2,
|
|
12
|
+
outputLabels: function (i) {
|
|
13
|
+
switch (i) {
|
|
14
|
+
case 0:
|
|
15
|
+
return "Formatted Payload";
|
|
16
|
+
break;
|
|
17
|
+
case 1:
|
|
18
|
+
return "Simple Array";
|
|
19
|
+
break;
|
|
20
|
+
default:
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
},
|
|
12
24
|
icon: "node-eye-icon.svg",
|
|
13
25
|
label: function () {
|
|
14
26
|
return (this.name);
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
const KNXAddress = require("./../KNXEngine/protocol/KNXAddress").KNXAddress;
|
|
2
|
+
|
|
1
3
|
module.exports = function (RED) {
|
|
2
4
|
|
|
5
|
+
|
|
3
6
|
function knxUltimateViewer(config) {
|
|
4
7
|
RED.nodes.createNode(this, config)
|
|
5
8
|
var node = this
|
|
6
9
|
node.server = RED.nodes.getNode(config.server)
|
|
7
10
|
node.topic = node.name;
|
|
8
|
-
node.name = config.name === undefined ? "
|
|
11
|
+
node.name = config.name === undefined ? "KNXViewer" : config.name;
|
|
9
12
|
node.outputtopic = node.name;
|
|
10
13
|
node.dpt = "";
|
|
11
14
|
node.notifyreadrequest = false
|
|
@@ -43,14 +46,15 @@ module.exports = function (RED) {
|
|
|
43
46
|
} catch (error) {
|
|
44
47
|
|
|
45
48
|
}
|
|
46
|
-
let
|
|
49
|
+
let sDeviceName = msg.devicename === node.name ? "Import ETS file to view the group address name" : msg.devicename; // The ETS file hasn't been imported
|
|
50
|
+
let sAddressRAW = KNXAddress.createFromString(msg.knx.destination, KNXAddress.TYPE_GROUP).get(); // Address as number (for ordering later)
|
|
47
51
|
if (oGa === undefined) {
|
|
48
|
-
node.exposedGAs.push({ address: msg.knx.destination, dpt: msg.knx.dpt, payload: msg.payload, devicename:
|
|
52
|
+
node.exposedGAs.push({ address: msg.knx.destination, addressRAW: sAddressRAW, dpt: msg.knx.dpt, payload: msg.payload, devicename: sDeviceName, lastupdate: new Date() });
|
|
49
53
|
} else {
|
|
50
54
|
oGa.dpt = msg.knx.dpt;
|
|
51
55
|
oGa.payload = msg.payload;
|
|
52
|
-
oGa.devicename =
|
|
53
|
-
oGa.lastupdate =
|
|
56
|
+
oGa.devicename = sDeviceName;
|
|
57
|
+
oGa.lastupdate = new Date();
|
|
54
58
|
}
|
|
55
59
|
// Output the payload
|
|
56
60
|
node.createPayload();
|
|
@@ -63,8 +67,8 @@ module.exports = function (RED) {
|
|
|
63
67
|
<th> GA </th>
|
|
64
68
|
<th> Value </th>
|
|
65
69
|
<th> DPT </th>
|
|
66
|
-
<th>
|
|
67
|
-
<th> Name </th>
|
|
70
|
+
<th> Last updated </th>
|
|
71
|
+
<th> Group Address Name </th>
|
|
68
72
|
</tr>
|
|
69
73
|
</thead>
|
|
70
74
|
<tbody>`;
|
|
@@ -73,30 +77,44 @@ module.exports = function (RED) {
|
|
|
73
77
|
let sPayload = "";
|
|
74
78
|
|
|
75
79
|
const aSorted = node.exposedGAs.sort((a, b) => {
|
|
76
|
-
if(
|
|
77
|
-
|
|
80
|
+
if (a.addressRAW !== undefined && b.addressRAW !== undefined) {
|
|
81
|
+
return a.addressRAW > b.addressRAW ? 1 : -1;
|
|
78
82
|
} else {
|
|
79
|
-
|
|
83
|
+
return a.addressRAW !== undefined ? 1 : -1
|
|
80
84
|
}
|
|
81
85
|
});
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
86
|
+
try {
|
|
87
|
+
for (let index = 0; index < aSorted.length; index++) {
|
|
88
|
+
const element = aSorted[index];
|
|
89
|
+
sPayload += `<tr><td>` + element.address + `</td>`;
|
|
90
|
+
if (typeof element.payload === "boolean" && element.payload === true) {
|
|
91
|
+
sPayload += "<td><b><font color=green>True</font></b></td>";
|
|
92
|
+
} else if (typeof element.payload === "boolean" && element.payload === false) {
|
|
93
|
+
sPayload += "<td><font color=red>False</font></td>";
|
|
94
|
+
} else if (typeof element.payload === "object" && !isNaN(Date.parse(element.payload))) {
|
|
95
|
+
// The payload is a datetime
|
|
96
|
+
sPayload += "<td>" + element.payload.toLocaleString() + "</td>";
|
|
97
|
+
} else if (typeof element.payload === "object") {
|
|
98
|
+
// Is maybe a JSON?
|
|
99
|
+
try {
|
|
100
|
+
sPayload += "<td>" + JSON.stringify(element.payload) + "</td>";
|
|
101
|
+
} catch (error) {
|
|
102
|
+
sPayload += "<td>" + element.payload + "</td>";
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
} else {
|
|
106
|
+
sPayload += "<td>" + element.payload + "</td>";
|
|
107
|
+
}
|
|
108
|
+
sPayload += "<td>" + element.dpt + "</td>"
|
|
109
|
+
sPayload += "<td>" + element.lastupdate.toLocaleString() + "</td>";
|
|
110
|
+
sPayload += "<td><font size=2pt>" + element.devicename + "</font></td></tr>";
|
|
93
111
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
112
|
+
|
|
113
|
+
} catch (error) {
|
|
114
|
+
|
|
97
115
|
}
|
|
98
116
|
|
|
99
|
-
node.send({ topic: node.name, payload: sHead + sPayload + sFooter });
|
|
117
|
+
node.send([{ topic: node.name, payload: sHead + sPayload + sFooter }, { topic: node.name, payload: node.exposedGAs }]);
|
|
100
118
|
}
|
|
101
119
|
|
|
102
120
|
node.on("input", function (msg) {
|
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
"primoaldistacco": "(this is the first to be switched off)",
|
|
6
6
|
"properties": {
|
|
7
7
|
"node-input-server": "Gateway",
|
|
8
|
-
"node-input-name": "
|
|
8
|
+
"node-input-name": "Name",
|
|
9
9
|
"node-input-dpt": "Datapoint",
|
|
10
10
|
"node-input-topic": "Monitor W",
|
|
11
11
|
"node-input-controlGA": "Load",
|
|
12
12
|
"node-input-monitorGA": "In use",
|
|
13
13
|
"node-input-wattLimit": "Limit W",
|
|
14
|
-
"node-input-sheddingCheckInterval": "
|
|
15
|
-
"node-input-sheddingRestoreDelay": "
|
|
14
|
+
"node-input-sheddingCheckInterval": "Ausschaltverzögerung (s)",
|
|
15
|
+
"node-input-sheddingRestoreDelay": "Einschaltverzögerung (s)",
|
|
16
16
|
"node-input-autoRestore": "Automatic recovery"
|
|
17
17
|
},
|
|
18
18
|
"selectlists": {},
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"primoaldistacco": "(this is the first to be switched off)",
|
|
6
6
|
"properties": {
|
|
7
7
|
"node-input-server": "Gateway",
|
|
8
|
-
"node-input-name": "
|
|
8
|
+
"node-input-name": "Name",
|
|
9
9
|
"node-input-dpt": "Datapoint",
|
|
10
10
|
"node-input-topic": "Monitor W",
|
|
11
11
|
"node-input-controlGA": "Load",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-knx-ultimate",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.32",
|
|
4
4
|
"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.",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"fs": "0.0.1-security",
|
package/img/covidfree.png
DELETED
|
Binary file
|