node-red-contrib-knx-ultimate 1.3.25 → 1.3.29

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 CHANGED
@@ -3,6 +3,26 @@
3
3
  [![Donate via PayPal](https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square)](https://www.paypal.me/techtoday)
4
4
 
5
5
  <br/>
6
+
7
+ # CHANGELOG
8
+
9
+ <p>
10
+ <b>Version 1.3.29</b> - February 2022<br/>
11
+ - Load Control: the timer for shedding won't everytime obey to what you've set. Fixed. <br/>
12
+ - Load Control: Added pre-shedding yellow warning message in the node status.<br/>
13
+ </p>
14
+ <p>
15
+ <b>Version 1.3.28</b> - February 2022<br/>
16
+ - 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/>
17
+ </p>
18
+ <p>
19
+ <b>Version 1.3.27</b> - February 2022<br/>
20
+ - Load Control: minor fixes + issue a KNX read of Watt values, in case the GA doesn't automatically send a power value on change.<br/>
21
+ </p>
22
+ <p>
23
+ <b>Version 1.3.26</b> - February 2022<br/>
24
+ - FIX: fix a crash occurring it the KNX Gateway is set to "emulate" (that means, don't write to the bus).<br/>
25
+ </p>
6
26
  <p>
7
27
  <b>Version 1.3.25</b> - February 2022<br/>
8
28
  - FIX: Load Control: measure unit was Wh. Corrected in W. Thank @Mauro of VivereSmart Facebook group https://www.facebook.com/groups/viveresmart<br/>
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
  [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
10
- [![Facebook][facebook-image]][facebook-url]
10
+ [![Youtube][youtube-image]][youtube-url]
11
11
  [![Donate via PayPal](https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square)](https://www.paypal.me/techtoday)
12
12
 
13
13
 
14
14
  ![Sample Node](img/readmemain.png)
15
15
 
16
- Control your KNX intallation via Node-Red! Single Node KNX IN/OUT with optional ETS group address importer and gateway simulation. Easy to use and highly configurable.
16
+ Control your KNX intallation via Node-Red!
17
17
 
18
18
  **You can use it immediately!**
19
19
  ```javascript
@@ -29,7 +29,8 @@ payload = {red:255, green:200, blue:30} // Put some colors in our life
29
29
  * **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
30
  * **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
31
  * **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**.
32
-
32
+ * **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.
33
+ * **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.
33
34
 
34
35
  ## CHANGELOG
35
36
 
@@ -67,8 +68,7 @@ Click your language to go to the documentation.<br/>
67
68
  ## STARTER PACK
68
69
 
69
70
  * [Wiki and Help](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki)
70
- * [Youtube video](https://www.youtube.com/playlist?list=PL9Yh1bjbLAYpfy1Auz6CKDfXUusgMwOQr)
71
- * [Facebook page](https://www.facebook.com/supergiovaneDev)
71
+ * [Youtube channel](https://www.youtube.com/playlist?list=PL9Yh1bjbLAYpfy1Auz6CKDfXUusgMwOQr)
72
72
  * [FAQ + Troubleshoot](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/5.-FAQ-Troubleshoot)
73
73
  * [Security best practices](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/SECURITY)
74
74
 
@@ -297,11 +297,11 @@ List of commercial companies, which have given us permission to be mentioned on
297
297
  ## FRIENDLY COMMUNITIES AROUND THE WORLD
298
298
 
299
299
  **Italy**
300
- * [VivereSmart](https://www.facebook.com/groups/viveresmart)
300
+ * [![](https://raw.githubusercontent.com/Supergiovane/node-red-contrib-knx-ultimate/master/img/c/viveresmart.png)](https://www.facebook.com/groups/viveresmart)
301
301
  * [VivereSmart TV](https://www.youtube.com/channel/UC6GlFhcbNuoSEejZ_HlCynA)
302
302
 
303
303
  **Germany**
304
- * [knx-user-forum](https://knx-user-forum.de/forum/öffentlicher-bereich/knx-eib-forum/1389088-knx-node-for-node-red)
304
+ * [![](https://raw.githubusercontent.com/Supergiovane/node-red-contrib-knx-ultimate/master/img/c/knxuserforum.png)](https://knx-user-forum.de/forum/öffentlicher-bereich/knx-eib-forum/1389088-knx-node-for-node-red)
305
305
 
306
306
  **China**
307
307
  * [QQ group: 837579219 (加群需要备注 “来自github”](tencent://groupwpa/?subcmd=all&param=7b2267726f757055696e223a3833373537393231392c2274696d655374616d70223a313633303934363639312c22617574684b6579223a22762b72482b466f4a496a75613033794e4a30744a6970756c55753639424f4d55724f464c4a6c474b77346a30326b7a4f7a3338535536517844684d7756414d62222c2261757468223a22227d&jump_from=)
@@ -319,5 +319,5 @@ List of commercial companies, which have given us permission to be mentioned on
319
319
  [npm-version-image]: https://img.shields.io/npm/v/node-red-contrib-knx-ultimate.svg
320
320
  [npm-downloads-month-image]: https://img.shields.io/npm/dm/node-red-contrib-knx-ultimate.svg
321
321
  [npm-downloads-total-image]: https://img.shields.io/npm/dt/node-red-contrib-knx-ultimate.svg
322
- [facebook-image]: https://img.shields.io/badge/Visit%20me-Facebook-blue
323
- [facebook-url]: https://www.facebook.com/supergiovaneDev
322
+ [youtube-image]: https://img.shields.io/badge/Visit%20me-Youtube-red
323
+ [youtube-url]: https://www.youtube.com/channel/UCA9RsLps1IthT7fDSeUbRZw/playlists
package/img/c/agata.png CHANGED
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg width="40px" height="60px" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
3
+ <!-- Generated by Pixelmator Pro 2.2 -->
4
+ <g id="group">
5
+ <g id="group-1">
6
+ <path id="Percorso" d="M34 256 L60.2 282.2 C168.2 390.2 343.9 390.2 451.9 282.2 L478 256 451.8 229.8 C343.8 121.8 168.1 121.8 60.1 229.8 Z M256 382.2 C180.2 382.2 104.4 353.3 46.7 295.6 L13.8 262.7 C10.1 259 10.1 253 13.8 249.2 L46.7 216.3 C162.1 100.9 349.9 100.9 465.3 216.3 L498.2 249.2 C501.9 252.9 501.9 258.9 498.2 262.7 L465.3 295.6 C407.6 353.3 331.8 382.2 256 382.2 Z" fill="#ffffff" fill-opacity="1" stroke="none"/>
7
+ <path id="Percorso-1" d="M256 183.5 C216 183.5 183.5 216 183.5 256 183.5 296 216 328.5 256 328.5 296 328.5 328.5 296 328.5 256 328.5 216 296 183.5 256 183.5 Z M256 347.5 C205.5 347.5 164.5 306.4 164.5 256 164.5 205.5 205.6 164.5 256 164.5 306.4 164.5 347.5 205.6 347.5 256 347.5 306.5 306.5 347.5 256 347.5 Z" fill="#ffffff" fill-opacity="1" stroke="none"/>
8
+ </g>
9
+ </g>
10
+ </svg>
@@ -1115,15 +1115,16 @@ return msg;`, "helplink": "https://github.com/Supergiovane/node-red-contrib-knx-
1115
1115
  }
1116
1116
 
1117
1117
  // 26/12/2021 If the KNXEngine is busy waiting for telegram's ACK, exit
1118
- if (!node.knxConnection._getClearToSend()) {
1119
- node.lockHandleTelegramQueue = false; // Unlock the function
1120
- if (node.telegramsQueue.length > 0) {
1121
- if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.warn("knxUltimate-config: handleTelegramQueue: the KNXEngine is busy or is waiting for a telegram ACK with seqNumner " + node.knxConnection._getSeqNumber() + ". Delay handling queue.");
1118
+ if (node.host.toUpperCase() !== "EMULATE") {
1119
+ if (!node.knxConnection._getClearToSend()) {
1120
+ node.lockHandleTelegramQueue = false; // Unlock the function
1121
+ if (node.telegramsQueue.length > 0) {
1122
+ if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.warn("knxUltimate-config: handleTelegramQueue: the KNXEngine is busy or is waiting for a telegram ACK with seqNumner " + node.knxConnection._getSeqNumber() + ". Delay handling queue.");
1123
+ }
1124
+ return;
1122
1125
  }
1123
- return;
1124
1126
  }
1125
1127
 
1126
-
1127
1128
  // Retrieving oKNXMessage { grpaddr, payload,dpt,outputtype (write or response),nodecallerid (node caller)}. 06/03/2020 "Read" request does have the lower priority in the queue, so firstly, i search for "read" telegrams and i move it on the top of the queue pile.
1128
1129
  var aTelegramsFiltered = [];
1129
1130
  aTelegramsFiltered = node.telegramsQueue.filter(a => a.outputtype !== "read");
@@ -21,6 +21,10 @@ module.exports = function (RED) {
21
21
  node.inputRBE = "false"
22
22
  node.isLoadControlNode = true; // Signal to config node, that this is a Load Control node
23
23
  node.initialread = true;
24
+ node.formatmultiplyvalue = 1;
25
+ node.formatnegativevalue = "zero";
26
+ node.formatdecimalsvalue = 0;
27
+
24
28
  node.sheddingStage = 0;
25
29
  node.timerIncreaseShedding = null;
26
30
  node.timerDecreaseShedding = null;
@@ -77,17 +81,21 @@ module.exports = function (RED) {
77
81
  node.status({ fill: fill, shape: shape, text: text + " Shed:" + node.sheddingStage + " Power:" + node.totalWatt + "W" + " Limit:" + node.wattLimit + "W (" + dDate.getDate() + ", " + dDate.toLocaleTimeString() + ")" });
78
82
  } catch (error) {
79
83
  }
80
-
81
84
  }
82
85
 
83
86
  // This function is called by the knx-ultimate config node.
84
87
  node.handleSend = msg => {
85
88
 
86
89
  // Update the Total Watt?
87
- if (msg.topic === node.topic) {
90
+ if (msg.topic === node.topic && msg.payload !== "" && msg.payload !== null && msg.payload !== undefined) {
88
91
  node.totalWatt = msg.payload;
89
- // Update current consumption
90
- node.setLocalStatus({ fill: "blue" });
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
+ }
91
99
  return;
92
100
  }
93
101
 
@@ -105,7 +113,7 @@ module.exports = function (RED) {
105
113
  // monitorVal: null
106
114
 
107
115
  var oRow = node.deviceList[i];
108
- if (msg.topic === oRow.monitorGA) {
116
+ if (msg.topic === oRow.monitorGA && msg.payload !== null && msg.payload !== undefined) {
109
117
  oRow.monitorVal = msg.payload;
110
118
  //node.setLocalStatus({ fill: "blue", shape: "dot", text: "Updated", payload: oRow.monitorVal, GA: msg.topic, dpt: "", devicename: oRow.monitorName });
111
119
  }
@@ -118,13 +126,16 @@ module.exports = function (RED) {
118
126
  // 03/02/2022 perform a read on all GA in the list
119
127
  node.initialReadAllDevicesInRules = () => {
120
128
  if (node.server) {
129
+ // Read status of the Total Power GA
130
+ if (node.topic !== undefined && node.topic !== null && node.topic !== "") node.server.writeQueueAdd({ grpaddr: node.topic, payload: "", dpt: "", outputtype: "read", nodecallerid: node.id });
131
+
121
132
  for (var i = 0; i < node.deviceList.length; i++) {
122
133
  let grpaddr = node.deviceList[i].monitorGA;
123
- if (grpaddr !== undefined && grpaddr !== "") {
134
+ if (grpaddr !== undefined && grpaddr !== "" && grpaddr !== null) {
124
135
  try {
125
136
  // Check if it's a group address
126
137
  let ret = Address.KNXAddress.createFromString(grpaddr, Address.KNXAddress.TYPE_GROUP);
127
- node.setLocalStatus({ fill: "grey", shape: "dot", text: "Read Power from BUS" });
138
+ //node.setLocalStatus({ fill: "grey", shape: "dot", text: "Read Power from BUS" });
128
139
  node.server.writeQueueAdd({ grpaddr: grpaddr, payload: "", dpt: "", outputtype: "read", nodecallerid: node.id });
129
140
  } catch (error) {
130
141
  node.setLocalStatus({ fill: "grey", shape: "dot", text: "Not a KNX GA " + error.message });
@@ -136,25 +147,55 @@ module.exports = function (RED) {
136
147
  }
137
148
  }
138
149
 
139
-
140
- // Start the timer
141
- node.startTimerIncreaseShedding = () => {
142
-
143
- // Increase shedding timer (Switch off devices)
144
- if (node.timerIncreaseShedding !== null) clearInterval(node.timerIncreaseShedding);
145
- node.timerIncreaseShedding = setInterval(() => {
146
-
147
- // Issue a READ request to the main Watt GA
148
- if (node.topic !== undefined && node.topic !== null && node.topic !== "") node.server.writeQueueAdd({ grpaddr: node.ga, payload: "", dpt: "", outputtype: "read", nodecallerid: node.id });
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();
149
155
 
150
156
  // Check consumption
151
157
  if (node.totalWatt > node.wattLimit) {
152
158
  // Start increasing shedding!
153
159
  if (node.sheddingStage < node.deviceList.length) {
154
- node.increaseShedding();
155
- node.startTimerDecreaseShedding();
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
+ }
156
178
  }
157
179
  }
180
+ }, 10000);
181
+ }
182
+
183
+ // Start the timer
184
+ node.startTimerIncreaseShedding = () => {
185
+
186
+ // Increase shedding timer (Switch off devices)
187
+ if (node.timerIncreaseShedding !== null) clearTimeout(node.timerIncreaseShedding);
188
+ node.timerIncreaseShedding = setTimeout(() => {
189
+ if (node.server) {
190
+ // Check consumption
191
+ if (node.totalWatt > node.wattLimit) {
192
+ // Start increasing shedding!
193
+ if (node.sheddingStage < node.deviceList.length) {
194
+ node.increaseShedding();
195
+ }
196
+ }
197
+ }
198
+ node.timerIncreaseShedding = null; // Nullify the timer.
158
199
  }, node.sheddingCheckInterval);
159
200
  }
160
201
 
@@ -162,17 +203,18 @@ module.exports = function (RED) {
162
203
  node.startTimerDecreaseShedding = () => {
163
204
 
164
205
  // Decrease shedding timer (Switch devices on again)
165
- if (node.timerDecreaseShedding !== null) clearInterval(node.timerDecreaseShedding);
166
- node.timerDecreaseShedding = setInterval(() => {
167
-
168
- // Check consumption
169
- if (node.totalWatt <= node.wattLimit) {
170
- // Start decreasing shedding!
171
- if (node.sheddingStage > 0) {
172
- node.decreaseShedding();
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
+ }
173
215
  }
174
216
  }
175
-
217
+ node.timerDecreaseShedding = null; // Nullify timer
176
218
  }, node.sheddingRestoreDelay);
177
219
  }
178
220
 
@@ -256,20 +298,18 @@ module.exports = function (RED) {
256
298
  node.setLocalStatus({ fill: "green", shape: "dot", text: "All loads have been restored" });
257
299
  }, 1000);
258
300
  }
259
-
260
-
261
301
  }
262
302
 
263
303
  // Start
264
- node.startTimerIncreaseShedding();
304
+ node.startMainTimer();
265
305
 
266
306
  node.on("input", function (msg) {
267
307
  if (typeof msg === "undefined") return;
268
308
 
269
309
  // Reset the shedding and activate all loads
270
310
  if (msg.hasOwnProperty("reset")) {
271
- if (node.timerDecreaseShedding !== null) clearInterval(node.timerDecreaseShedding);
272
- if (node.timerIncreaseShedding !== null) clearInterval(node.timerIncreaseShedding);
311
+ if (node.timerDecreaseShedding !== null) clearTimeout(node.timerDecreaseShedding);
312
+ if (node.timerIncreaseShedding !== null) clearTimeout(node.timerIncreaseShedding);
273
313
  node.sheddingStage = 0;
274
314
  for (let index = 0; index < node.deviceList.length; index++) {
275
315
  const oRow = node.deviceList[index];
@@ -277,16 +317,15 @@ module.exports = function (RED) {
277
317
  }
278
318
  setTimeout(() => {
279
319
  node.setLocalStatus({ fill: "green", shape: "dot", text: "All loads have been restored" });
280
- // Restart shedding timer
281
- node.startTimerIncreaseShedding();
282
320
  }, 1000);
283
321
  node.send({ topic: node.name || node.topic, operation: "Reset", payload: node.sheddingStage });
284
322
  }
285
323
 
286
324
  // Disable the shedding node
287
325
  if (msg.hasOwnProperty("disable")) {
288
- if (node.timerDecreaseShedding !== null) clearInterval(node.timerDecreaseShedding);
289
- if (node.timerIncreaseShedding !== null) clearInterval(node.timerIncreaseShedding);
326
+ if (node.timerDecreaseShedding !== null) clearTimeout(node.timerDecreaseShedding);
327
+ if (node.timerIncreaseShedding !== null) clearTimeout(node.timerIncreaseShedding);
328
+ if (node.mainTimer !== null) clearInterval(node.mainTimer);
290
329
  setTimeout(() => {
291
330
  node.setLocalStatus({ fill: "grey", shape: "dot", text: "Disabled" });
292
331
  }, 1000);
@@ -295,12 +334,12 @@ module.exports = function (RED) {
295
334
 
296
335
  // Disable the shedding node
297
336
  if (msg.hasOwnProperty("enable")) {
298
- if (node.timerDecreaseShedding !== null) clearInterval(node.timerDecreaseShedding);
299
- if (node.timerIncreaseShedding !== null) clearInterval(node.timerIncreaseShedding);
337
+ if (node.timerDecreaseShedding !== null) clearTimeout(node.timerDecreaseShedding);
338
+ if (node.timerIncreaseShedding !== null) clearTimeout(node.timerIncreaseShedding);
300
339
  setTimeout(() => {
301
340
  node.setLocalStatus({ fill: "green", shape: "dot", text: "Enabled" });
302
- // Restart shedding timer
303
- node.startTimerIncreaseShedding();
341
+ // Restart timer
342
+ node.startMainTimer();
304
343
  }, 1000);
305
344
  node.send({ topic: node.name || node.topic, operation: "Enabled", payload: node.sheddingStage });
306
345
  }
@@ -313,8 +352,9 @@ module.exports = function (RED) {
313
352
  })
314
353
 
315
354
  node.on("close", function (done) {
316
- if (node.timerDecreaseShedding !== null) clearInterval(node.timerDecreaseShedding);
317
- if (node.timerIncreaseShedding !== null) clearInterval(node.timerIncreaseShedding);
355
+ if (node.mainTimer !== null) clearInterval(node.mainTimer);
356
+ if (node.timerDecreaseShedding !== null) clearTimeout(node.timerDecreaseShedding);
357
+ if (node.timerIncreaseShedding !== null) clearTimeout(node.timerIncreaseShedding);
318
358
  if (node.server) {
319
359
  node.server.removeClient(node)
320
360
  }
@@ -0,0 +1,71 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType('knxUltimateViewer', {
3
+ category: "KNX Ultimate",
4
+ color: '#C7E9C0',
5
+ defaults: {
6
+ //buttonState: {value: true},
7
+ server: { type: "knxUltimate-config", required: true },
8
+ name: { value: "KNXViewer", validate: RED.validators.regex(/^[a-z]+$/i) }
9
+ },
10
+ inputs: 0,
11
+ outputs: 1,
12
+ icon: "node-eye-icon.svg",
13
+ label: function () {
14
+ return (this.name);
15
+ },
16
+ paletteLabel: "KNX Viewer",
17
+ // button: {
18
+ // enabled: function() {
19
+ // // return whether or not the button is enabled, based on the current
20
+ // // configuration of the node
21
+ // return !this.changed
22
+ // },
23
+ // visible: function() {
24
+ // // return whether or not the button is visible, based on the current
25
+ // // configuration of the node
26
+ // return this.hasButton
27
+ // },
28
+ // //toggle: "buttonState",
29
+ // onclick: function() {}
30
+ // },
31
+ oneditprepare: function () {
32
+
33
+
34
+ },
35
+ oneditsave: function () {
36
+
37
+
38
+ },
39
+ oneditcancel: function () {
40
+
41
+ }
42
+ })
43
+
44
+ </script>
45
+
46
+ <script type="text/x-red" data-template-name="knxUltimateViewer">
47
+
48
+
49
+ <div class="form-row">
50
+ <b><span data-i18n="knxUltimateViewer.title"></span></b>&nbsp&nbsp<span style="color:red"
51
+ data-i18n="[html]knxUltimateViewer.helplink"></span>
52
+ <br />
53
+ <label for="node-input-server">
54
+ <img
55
+ src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAKnRFWHRDcmVhdGlvbiBUaW1lAEZyIDYgQXVnIDIwMTAgMjE6NTI6MTkgKzAxMDD84aS8AAAAB3RJTUUH3gYYCicNV+4WIQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAARnQU1BAACxjwv8YQUAAACUSURBVHjaY2CgFZg5c+Z/ZEyWAZ8+f/6/ZsWs/xoamqMGkGrA6Wla/1+fVARjEBuGsSoGmY4eZSCNL59d/g8DIDbIAHR14OgFGQByKjIGKX5+6/T///8gGMQGiV1+/B0Fg70GIkD+RMYgxf/O5/7//2MSmAZhkBi6OrgB6Bg5DGB4ajr3f2xqsYYLSDE2THJUDg0AAAqyDVd4tp4YAAAAAElFTkSuQmCC"></img>
56
+ <span data-i18n="knxUltimateViewer.advanced.node-input-server"></span>
57
+ </label>
58
+ <input type="text" id="node-input-server" />
59
+ </div>
60
+
61
+ <div class="form-row">
62
+ <label for="node-input-name">
63
+ <i class="fa fa-tag"></i>
64
+ <span data-i18n="knxUltimateViewer.node-input-name"></span>
65
+ </label>
66
+ <input type="text" id="node-input-name" data-i18n="[placeholder]knxUltimateViewer.node-input-name" />
67
+ </div>
68
+
69
+
70
+
71
+ </script>
@@ -0,0 +1,123 @@
1
+ module.exports = function (RED) {
2
+
3
+ function knxUltimateViewer(config) {
4
+ RED.nodes.createNode(this, config)
5
+ var node = this
6
+ node.server = RED.nodes.getNode(config.server)
7
+ node.topic = node.name;
8
+ node.name = config.name === undefined ? "KNXGlobalContext" : config.name;
9
+ node.outputtopic = node.name;
10
+ node.dpt = "";
11
+ node.notifyreadrequest = false
12
+ node.notifyreadrequestalsorespondtobus = "false";
13
+ node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = "";
14
+ node.notifyresponse = true;
15
+ node.notifywrite = true;
16
+ node.initialread = false
17
+ node.listenallga = true;
18
+ node.outputtype = "write";
19
+ node.outputRBE = false // Apply or not RBE to the output (Messages coming from flow)
20
+ node.inputRBE = false // Apply or not RBE to the input (Messages coming from BUS)
21
+ node.currentPayload = "" // Current value for the RBE input and for the .previouspayload msg
22
+ node.passthrough = "no";
23
+ node.formatmultiplyvalue = 1;
24
+ node.formatnegativevalue = "leave";
25
+ node.formatdecimalsvalue = 2;
26
+
27
+ node.exposedGAs = [];
28
+
29
+ // Used to call the status update from the config node.
30
+ node.setNodeStatus = ({ fill, shape, text, payload, GA, dpt, devicename }) => {
31
+ if (node.server == null) { node.status({ fill: "red", shape: "dot", text: "[NO GATEWAY SELECTED]" }); return; }
32
+ GA = GA === undefined ? "" : GA;
33
+ payload = payload === undefined ? "" : payload;
34
+ let dDate = new Date();
35
+ node.status({ fill: fill, shape: shape, text: GA + " " + payload + " " + text + " (" + dDate.getDate() + ", " + dDate.toLocaleTimeString() + ")" });
36
+ }
37
+
38
+
39
+ // This function is called by the knx-ultimate config node, to output a msg.payload.
40
+ node.handleSend = msg => {
41
+ try {
42
+ var oGa = node.exposedGAs.find(ga => ga.address === msg.knx.destination);
43
+ } catch (error) {
44
+
45
+ }
46
+ let dDate = new Date();
47
+ if (oGa === undefined) {
48
+ node.exposedGAs.push({ address: msg.knx.destination, dpt: msg.knx.dpt, payload: msg.payload, devicename: msg.devicename || "Import ETS file", lastupdate: + dDate.getDate() + ", " + dDate.toLocaleTimeString() });
49
+ } else {
50
+ oGa.dpt = msg.knx.dpt;
51
+ oGa.payload = msg.payload;
52
+ oGa.devicename = msg.devicename || "Import ETS file";
53
+ oGa.lastupdate = dDate.getDate() + ", " + dDate.toLocaleTimeString()
54
+ }
55
+ // Output the payload
56
+ node.createPayload();
57
+ };
58
+
59
+ node.createPayload = () => {
60
+ let sHead = `<table>
61
+ <thead>
62
+ <tr>
63
+ <th> GA </th>
64
+ <th> Value </th>
65
+ <th> DPT </th>
66
+ <th> Day, time </th>
67
+ <th> Name </th>
68
+ </tr>
69
+ </thead>
70
+ <tbody>`;
71
+ let sFooter = `</tbody>
72
+ </table>`;
73
+ let sPayload = "";
74
+
75
+ const aSorted = node.exposedGAs.sort((a, b) => {
76
+ if( a.address !== undefined && b.address !== undefined ) {
77
+ return a.address > b.address ? 1 : -1;
78
+ } else {
79
+ return a.address !== undefined ? 1 : -1
80
+ }
81
+ });
82
+
83
+ for (let index = 0; index < aSorted.length; index++) {
84
+ const element = aSorted[index];
85
+ sPayload += `<tr>
86
+ <td>` + element.address + `</td>`;
87
+ if (typeof element.payload === "boolean" && element.payload === true) {
88
+ sPayload += "<td><b><font color=green>True</font></b></td>";
89
+ } else if (typeof element.payload === "boolean" && element.payload === false) {
90
+ sPayload += "<td><font color=red>False</font></td>";
91
+ } else {
92
+ sPayload += "<td>" + element.payload + "</td>";
93
+ }
94
+ sPayload += "<td>" + element.dpt + "</td>"
95
+ sPayload += "<td>" + element.lastupdate + "</td>";
96
+ sPayload += "<td><font size=2pt>" + element.devicename + "</font></td></tr>";
97
+ }
98
+
99
+ node.send({ topic: node.name, payload: sHead + sPayload + sFooter });
100
+ }
101
+
102
+ node.on("input", function (msg) {
103
+
104
+
105
+ })
106
+
107
+ node.on("close", function (done) {
108
+ if (node.server) {
109
+ node.server.removeClient(node);
110
+ }
111
+ done();
112
+ })
113
+
114
+ // On each deploy, unsubscribe+resubscribe
115
+ if (node.server) {
116
+ node.server.removeClient(node);
117
+ node.server.addClient(node);
118
+ }
119
+
120
+
121
+ }
122
+ RED.nodes.registerType("knxUltimateViewer", knxUltimateViewer)
123
+ }
@@ -0,0 +1,14 @@
1
+ <script type="text/x-red" data-help-name="knxUltimate-config">
2
+ <h1>KNX Ultimate - Nodo Global Context</h1>
3
+
4
+
5
+ <p>
6
+ <a href="https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/de-knxUltimateViewer" target="_blank"><i class="fa fa-info-circle"></i>&nbsp Beispiel</a>
7
+
8
+ </p>
9
+
10
+ <p>
11
+ <a href="https://www.paypal.me/techtoday" target="_blank"><img src='https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square' width='30%'></a>
12
+
13
+ </p>
14
+ </script>
@@ -0,0 +1,7 @@
1
+ {
2
+ "knxUltimateViewer": {
3
+ "helplink" : "&nbsp<i class=\"fa fa-question-circle\"></i>&nbsp<a target=\"_blank\" href=\"https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/de-knxUltimateViewer\"><u>Esempio</u></a>",
4
+ "title": "KNX Viewer",
5
+ "node-input-name": "Name"
6
+ }
7
+ }
@@ -0,0 +1,14 @@
1
+ <script type="text/x-red" data-help-name="knxUltimate-config">
2
+ <h1>KNX Ultimate - Nodo Global Context</h1>
3
+
4
+
5
+ <p>
6
+ <a href="https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/knxUltimateViewer" target="_blank"><i class="fa fa-info-circle"></i>&nbsp Sample</a>
7
+
8
+ </p>
9
+
10
+ <p>
11
+ <a href="https://www.paypal.me/techtoday" target="_blank"><img src='https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square' width='30%'></a>
12
+
13
+ </p>
14
+ </script>
@@ -0,0 +1,7 @@
1
+ {
2
+ "knxUltimateViewer": {
3
+ "helplink" : "&nbsp<i class=\"fa fa-question-circle\"></i>&nbsp<a target=\"_blank\" href=\"https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/knxUltimateViewer\"><u>Esempio</u></a>",
4
+ "title": "KNX Viewer",
5
+ "node-input-name": "Name"
6
+ }
7
+ }
@@ -0,0 +1,14 @@
1
+ <script type="text/x-red" data-help-name="knxUltimate-config">
2
+ <h1>KNX Ultimate - Nodo Global Context</h1>
3
+
4
+
5
+ <p>
6
+ <a href="https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/it-knxUltimateViewer" target="_blank"><i class="fa fa-info-circle"></i>&nbsp Esempio</a>
7
+
8
+ </p>
9
+
10
+ <p>
11
+ <a href="https://www.paypal.me/techtoday" target="_blank"><img src='https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square' width='30%'></a>
12
+
13
+ </p>
14
+ </script>
@@ -0,0 +1,7 @@
1
+ {
2
+ "knxUltimateViewer": {
3
+ "helplink" : "&nbsp<i class=\"fa fa-question-circle\"></i>&nbsp<a target=\"_blank\" href=\"https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/it-knxUltimateViewer\"><u>Esempio</u></a>",
4
+ "title": "KNX Viewer",
5
+ "node-input-name": "Nome"
6
+ }
7
+ }
@@ -0,0 +1,14 @@
1
+ <script type="text/x-red" data-help-name="knxUltimate-config">
2
+ <h1>KNX Ultimate - Nodo Global Context</h1>
3
+
4
+
5
+ <p>
6
+ <a href="https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/cn-knxUltimateViewer" target="_blank"><i class="fa fa-info-circle"></i>&nbsp Sample</a>
7
+
8
+ </p>
9
+
10
+ <p>
11
+ <a href="https://www.paypal.me/techtoday" target="_blank"><img src='https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square' width='30%'></a>
12
+
13
+ </p>
14
+ </script>
@@ -0,0 +1,7 @@
1
+ {
2
+ "knxUltimateViewer": {
3
+ "helplink" : "&nbsp<i class=\"fa fa-question-circle\"></i>&nbsp<a target=\"_blank\" href=\"https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/cn-knxUltimateViewer\"><u>Esempio</u></a>",
4
+ "title": "KNX Viewer",
5
+ "node-input-name": "节点名称"
6
+ }
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-knx-ultimate",
3
- "version": "1.3.25",
3
+ "version": "1.3.29",
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",
@@ -28,7 +28,8 @@
28
28
  "knxUltimate-config": "/nodes/knxUltimate-config.js",
29
29
  "knxUltimateGlobalContext": "/nodes/knxUltimateGlobalContext.js",
30
30
  "knxUltimateAlerter": "/nodes/knxUltimateAlerter.js",
31
- "knxUltimateLoadControl":"/nodes/knxUltimateLoadControl.js"
31
+ "knxUltimateLoadControl":"/nodes/knxUltimateLoadControl.js",
32
+ "knxUltimateViewer":"/nodes/knxUltimateViewer.js"
32
33
  }
33
34
  },
34
35
  "repository": {
package/img/covidfree.png DELETED
Binary file