node-red-contrib-knx-ultimate 1.3.24 → 1.3.28

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.28</b> - February 2022<br/>
11
+ - 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/>
12
+ </p>
13
+ <p>
14
+ <b>Version 1.3.27</b> - February 2022<br/>
15
+ - 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/>
16
+ </p>
17
+ <p>
18
+ <b>Version 1.3.26</b> - February 2022<br/>
19
+ - FIX: fix a crash occurring it the KNX Gateway is set to "emulate" (that means, don't write to the bus).<br/>
20
+ </p>
21
+ <p>
22
+ <b>Version 1.3.25</b> - February 2022<br/>
23
+ - FIX: Load Control: measure unit was Wh. Corrected in W. Thank @Mauro of VivereSmart Facebook group https://www.facebook.com/groups/viveresmart<br/>
24
+ - Load Control: Added more info to the output message. Updated the online help.<br/>
25
+ </p>
6
26
  <p>
7
27
  <b>Version 1.3.24</b> - February 2022<br/>
8
28
  - NEW: Load Control node: switch off your device if you're exceeding the Watt limit of your house.<br/>
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;
@@ -28,7 +32,7 @@ module.exports = function (RED) {
28
32
  node.sheddingRestoreDelay = config.sheddingRestoreDelay !== undefined ? config.sheddingRestoreDelay * 1000 : 60000;
29
33
 
30
34
  node.totalWatt = 0; // Current total watt consumption
31
- node.wattLimit = config.wattLimit === undefined ? 3000 : config.wattLimit;
35
+ node.wattLimit = config.wattLimit === undefined ? 3000 : Number(config.wattLimit);
32
36
  node.deviceList = [];
33
37
  for (let index = 1; index < 6; index++) {
34
38
  // Eval, the magic. Fill in the device list. DEFINITION DEVICELIST
@@ -69,17 +73,13 @@ module.exports = function (RED) {
69
73
 
70
74
  }
71
75
 
72
- // Used to call the status update from the config node.
73
- node.setLocalStatus = ({ fill, shape, text, payload, GA, dpt, devicename }) => {
76
+ // Used to call the status update from this node
77
+ node.setLocalStatus = ({ fill = "green", shape = "ring", text = "" }) => {
78
+ if (text !== "") text += ".";
74
79
  var dDate = new Date();
75
- // 30/08/2019 Display only the things selected in the config
76
- _GA = (typeof _GA == "undefined" || GA == "") ? "" : "(" + GA + ") ";
77
- _devicename = devicename || "";
78
- _dpt = (typeof dpt == "undefined" || dpt == "") ? "" : " DPT" + dpt;
79
80
  try {
80
- node.status({ fill: fill, shape: shape, text: _GA + payload + ((node.listenallga && node.server.statusDisplayDeviceNameWhenALL) === true ? " " + _devicename : "") + (node.server.statusDisplayDataPoint === true ? _dpt : "") + (node.server.statusDisplayLastUpdate === true ? " (" + dDate.getDate() + ", " + dDate.toLocaleTimeString() + ")" : "") + " " + text });
81
+ node.status({ fill: fill, shape: shape, text: text + " Shed:" + node.sheddingStage + " Power:" + node.totalWatt + "W" + " Limit:" + node.wattLimit + "W (" + dDate.getDate() + ", " + dDate.toLocaleTimeString() + ")" });
81
82
  } catch (error) {
82
- node.status({ fill: fill, shape: shape, text: _GA + payload + ((true === true) ? " " + _devicename : "") + (false === true ? _dpt : "") + (true === true ? " (" + dDate.getDate() + ", " + dDate.toLocaleTimeString() + ")" : "") + " " + text });
83
83
  }
84
84
 
85
85
  }
@@ -88,9 +88,10 @@ module.exports = function (RED) {
88
88
  node.handleSend = msg => {
89
89
 
90
90
  // Update the Total Watt?
91
- if (msg.topic === node.topic) {
91
+ if (msg.topic === node.topic && msg.payload !== "" && msg.payload !== null && msg.payload !== undefined) {
92
92
  node.totalWatt = msg.payload;
93
- //node.setLocalStatus({ fill: "blue", shape: "dot", text: "Total consumption", payload: node.totalWatt, GA: msg.topic, dpt: "", devicename: node.name });
93
+ // Update current consumption
94
+ node.setLocalStatus({ fill: "blue" });
94
95
  return;
95
96
  }
96
97
 
@@ -108,7 +109,7 @@ module.exports = function (RED) {
108
109
  // monitorVal: null
109
110
 
110
111
  var oRow = node.deviceList[i];
111
- if (msg.topic === oRow.monitorGA) {
112
+ if (msg.topic === oRow.monitorGA && msg.payload !== null && msg.payload !== undefined) {
112
113
  oRow.monitorVal = msg.payload;
113
114
  //node.setLocalStatus({ fill: "blue", shape: "dot", text: "Updated", payload: oRow.monitorVal, GA: msg.topic, dpt: "", devicename: oRow.monitorName });
114
115
  }
@@ -121,21 +122,24 @@ module.exports = function (RED) {
121
122
  // 03/02/2022 perform a read on all GA in the list
122
123
  node.initialReadAllDevicesInRules = () => {
123
124
  if (node.server) {
125
+ // Read status of the Total Power GA
126
+ node.server.writeQueueAdd({ grpaddr: node.topic, payload: "", dpt: "", outputtype: "read", nodecallerid: node.id });
127
+
124
128
  for (var i = 0; i < node.deviceList.length; i++) {
125
129
  let grpaddr = node.deviceList[i].monitorGA;
126
- if (grpaddr !== undefined && grpaddr !== "") {
130
+ if (grpaddr !== undefined && grpaddr !== "" && grpaddr !== null) {
127
131
  try {
128
132
  // Check if it's a group address
129
133
  let ret = Address.KNXAddress.createFromString(grpaddr, Address.KNXAddress.TYPE_GROUP);
130
- node.setLocalStatus({ fill: "grey", shape: "dot", text: "Read", payload: "", GA: grpaddr, dpt: "", devicename: node.deviceList[i].monitorName });
134
+ //node.setLocalStatus({ fill: "grey", shape: "dot", text: "Read Power from BUS" });
131
135
  node.server.writeQueueAdd({ grpaddr: grpaddr, payload: "", dpt: "", outputtype: "read", nodecallerid: node.id });
132
136
  } catch (error) {
133
- node.setLocalStatus({ fill: "grey", shape: "dot", text: "Not a KNX GA " + error.message, payload: "", GA: grpaddr, dpt: "", devicename: node.deviceList[i].monitorName });
137
+ node.setLocalStatus({ fill: "grey", shape: "dot", text: "Not a KNX GA " + error.message });
134
138
  }
135
139
  }
136
140
  }
137
141
  } else {
138
- node.setLocalStatus({ fill: "red", shape: "ring", text: "No gateway selected. Unable to read from KNX bus", payload: "", GA: "", dpt: "", devicename: "" });
142
+ node.setLocalStatus({ fill: "red", shape: "ring", text: "No gateway selected. Unable to read from KNX bus" });
139
143
  }
140
144
  }
141
145
 
@@ -146,16 +150,18 @@ module.exports = function (RED) {
146
150
  // Increase shedding timer (Switch off devices)
147
151
  if (node.timerIncreaseShedding !== null) clearInterval(node.timerIncreaseShedding);
148
152
  node.timerIncreaseShedding = setInterval(() => {
149
-
150
- // Issue a READ request to the main Watt GA
151
- if (node.topic !== undefined && node.topic !== null && node.topic !== "") node.server.writeQueueAdd({ grpaddr: node.ga, payload: "", dpt: "", outputtype: "read", nodecallerid: node.id });
152
-
153
- // Check consumption
154
- if (node.totalWatt > node.wattLimit) {
155
- // Start increasing shedding!
156
- if (node.sheddingStage < node.deviceList.length) {
157
- node.increaseShedding();
158
- node.startTimerDecreaseShedding();
153
+ 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
+ // Check consumption
159
+ if (node.totalWatt > node.wattLimit) {
160
+ // Start increasing shedding!
161
+ if (node.sheddingStage < node.deviceList.length) {
162
+ node.increaseShedding();
163
+ node.startTimerDecreaseShedding(); // Reset the decreasing timer from beginning
164
+ }
159
165
  }
160
166
  }
161
167
  }, node.sheddingCheckInterval);
@@ -173,6 +179,7 @@ module.exports = function (RED) {
173
179
  // Start decreasing shedding!
174
180
  if (node.sheddingStage > 0) {
175
181
  node.decreaseShedding();
182
+ node.startTimerIncreaseShedding(); // Reset the increasing timer from beginning
176
183
  }
177
184
  }
178
185
 
@@ -200,13 +207,12 @@ module.exports = function (RED) {
200
207
  const oRow = node.deviceList[iRowIndex];
201
208
  if (oRow.ga !== undefined && oRow.ga !== "" && oRow.ga !== null) {
202
209
  // Check if the device is in use. If not, turn off the device and further increase the shedding stage to turn off the next one.
203
- node.setLocalStatus({ fill: "red", shape: "dot", text: "Switch off", payload: "Shedding stage " + node.sheddingStage, GA: oRow.ga, dpt: oRow.dpt, devicename: oRow.name });
210
+ node.setLocalStatus({ fill: "red", shape: "dot", text: "OFF " + oRow.name });
204
211
  node.server.writeQueueAdd({ grpaddr: oRow.ga, payload: false, dpt: oRow.dpt, outputtype: "write", nodecallerid: node.id });
205
212
  } else {
206
- node.setLocalStatus({ fill: "yellow", shape: "dot", text: "No GA defined", payload: "Shedding stage " + node.sheddingStage, GA: "", dpt: "", devicename: "" });
213
+ node.setLocalStatus({ fill: "grey", shape: "dot", text: "No GA defined" });
207
214
  }
208
-
209
- node.send({ topic: node.name || node.topic, payload: "Shedding stage " + node.sheddingStage });
215
+ node.send({ topic: node.name || node.topic, operation: "Increase Shedding", device: oRow.name || "", ga: oRow.ga || "", totalPowerConsumption: node.totalWatt, wattLimit: node.wattLimit, payload: node.sheddingStage });
210
216
  // Go furhter ?
211
217
  if (oRow.monitorGA !== undefined && oRow.monitorGA !== "" && oRow.monitorGA !== null) {
212
218
  // Minimum consumption must be at lease xx Watt
@@ -229,33 +235,35 @@ module.exports = function (RED) {
229
235
  // monitorVal: null
230
236
  if (node.sheddingStage <= 0) {
231
237
  node.sheddingStage = 0;
232
- node.setLocalStatus({ fill: "green", shape: "dot", text: "All loads are on", payload: "", GA: "", dpt: "", devicename: "" });
238
+ node.setLocalStatus({ fill: "green", shape: "dot", text: "All loads are ON" });
233
239
  return;
234
240
  }
235
241
 
236
- let iRowIndex = node.sheddingStage - 1; // Array is base 0
237
- if (iRowIndex < 0) {
238
- iRowIndex = 0;
239
- }
242
+ node.sheddingStage--;
243
+ let iRowIndex = node.sheddingStage; // Array is base 0
244
+ if (iRowIndex < 0) iRowIndex = 0;
245
+ if (iRowIndex > node.deviceList.length - 1) return;
246
+
240
247
  const oRow = node.deviceList[iRowIndex];
241
- if (oRow.autoRestore === true) {
242
- if (oRow.ga !== undefined && oRow.ga !== "" && oRow.ga !== null) {
248
+ if (oRow.ga !== undefined && oRow.ga !== "" && oRow.ga !== null) {
249
+ if (oRow.autoRestore === true) {
243
250
  // Check if the device is in use. If not, turn off the device and further increase the shedding stage to turn off the next one.
244
- node.setLocalStatus({ fill: "green", shape: "dot", text: "Switch on", payload: "Shedding stage " + (node.sheddingStage - 1), GA: oRow.ga, dpt: oRow.dpt, devicename: oRow.name });
251
+ node.setLocalStatus({ fill: "green", shape: "dot", text: "ON " + oRow.name });
245
252
  node.server.writeQueueAdd({ grpaddr: oRow.ga, payload: true, dpt: oRow.dpt, outputtype: "write", nodecallerid: node.id });
246
253
  } else {
247
- node.setLocalStatus({ fill: "yellow", shape: "dot", text: "No GA defined", payload: "Shedding stage " + (node.sheddingStage - 1), GA: "", dpt: "", devicename: "" });
254
+ // Cannot auto switch on the load.
255
+ node.setLocalStatus({ fill: "yellow", shape: "dot", text: "Auto Restore disabled " + oRow.name });
248
256
  }
249
257
  } else {
250
- // Cannot auto switch on the load.
251
- node.setLocalStatus({ fill: "yellow", shape: "dot", text: "Auto Restore disabled", payload: "Shedding stage " + node.sheddingStage, GA: oRow.ga, dpt: oRow.dpt, devicename: oRow.name });
258
+ // No load GA defined
259
+ node.setLocalStatus({ fill: "grey", shape: "dot", text: "No Load GA defined" });
252
260
  }
253
- node.send({ topic: node.name || node.topic, payload: "Shedding stage " + (node.sheddingStage - 1) });
254
- if (node.sheddingStage > 0) {
255
- node.sheddingStage--;
256
- } else {
261
+ node.send({ topic: node.name || node.topic, operation: "Decrease Shedding", device: oRow.name || "", ga: oRow.ga || "", totalPowerConsumption: node.totalWatt, wattLimit: node.wattLimit, payload: node.sheddingStage });
262
+
263
+ if (node.sheddingStage < 0) {
264
+ node.sheddingStage = 0;
257
265
  setTimeout(() => {
258
- node.setLocalStatus({ fill: "green", shape: "dot", text: "All loads have been restored", payload: "", GA: "", dpt: "", devicename: "" });
266
+ node.setLocalStatus({ fill: "green", shape: "dot", text: "All loads have been restored" });
259
267
  }, 1000);
260
268
  }
261
269
 
@@ -278,22 +286,21 @@ module.exports = function (RED) {
278
286
  if (oRow.autoRestore === true) node.server.writeQueueAdd({ grpaddr: oRow.ga, payload: true, dpt: oRow.dpt, outputtype: "write", nodecallerid: node.id });
279
287
  }
280
288
  setTimeout(() => {
281
- node.setLocalStatus({ fill: "green", shape: "dot", text: "All loads have been restored", payload: "", GA: "", dpt: "", devicename: "" });
289
+ node.setLocalStatus({ fill: "green", shape: "dot", text: "All loads have been restored" });
282
290
  // Restart shedding timer
283
291
  node.startTimerIncreaseShedding();
284
292
  }, 1000);
285
- node.send({ topic: node.name || node.topic, payload: "reset" });
293
+ node.send({ topic: node.name || node.topic, operation: "Reset", payload: node.sheddingStage });
286
294
  }
287
295
 
288
296
  // Disable the shedding node
289
297
  if (msg.hasOwnProperty("disable")) {
290
298
  if (node.timerDecreaseShedding !== null) clearInterval(node.timerDecreaseShedding);
291
299
  if (node.timerIncreaseShedding !== null) clearInterval(node.timerIncreaseShedding);
292
- node.sheddingStage = 0;
293
300
  setTimeout(() => {
294
- node.setLocalStatus({ fill: "grey", shape: "dot", text: "Disabled", payload: "", GA: "", dpt: "", devicename: "" });
301
+ node.setLocalStatus({ fill: "grey", shape: "dot", text: "Disabled" });
295
302
  }, 1000);
296
- node.send({ topic: node.name || node.topic, payload: "disabled" });
303
+ node.send({ topic: node.name || node.topic, operation: "Disabled", payload: node.sheddingStage });
297
304
  }
298
305
 
299
306
  // Disable the shedding node
@@ -301,11 +308,11 @@ module.exports = function (RED) {
301
308
  if (node.timerDecreaseShedding !== null) clearInterval(node.timerDecreaseShedding);
302
309
  if (node.timerIncreaseShedding !== null) clearInterval(node.timerIncreaseShedding);
303
310
  setTimeout(() => {
304
- node.setLocalStatus({ fill: "green", shape: "dot", text: "Enabled", payload: "", GA: "", dpt: "", devicename: "" });
311
+ node.setLocalStatus({ fill: "green", shape: "dot", text: "Enabled" });
305
312
  // Restart shedding timer
306
313
  node.startTimerIncreaseShedding();
307
314
  }, 1000);
308
- node.send({ topic: node.name || node.topic, payload: "enabled" });
315
+ node.send({ topic: node.name || node.topic, operation: "Enabled", payload: node.sheddingStage });
309
316
  }
310
317
 
311
318
  // 24/04/2021 if payload is read or the output type is set to "read", do a read
@@ -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
+ }
@@ -7,10 +7,10 @@
7
7
  "node-input-server": "Gateway",
8
8
  "node-input-name": "NoNameme",
9
9
  "node-input-dpt": "Datapoint",
10
- "node-input-topic": "Monitor Wh",
10
+ "node-input-topic": "Monitor W",
11
11
  "node-input-controlGA": "Load",
12
12
  "node-input-monitorGA": "In use",
13
- "node-input-wattLimit": "Limit Wh",
13
+ "node-input-wattLimit": "Limit W",
14
14
  "node-input-sheddingCheckInterval": "Delay switch off (s)",
15
15
  "node-input-sheddingRestoreDelay": "Delay switch on (s)",
16
16
  "node-input-autoRestore": "Automatic recovery"
@@ -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
+ }
@@ -7,10 +7,10 @@
7
7
  "node-input-server": "Gateway",
8
8
  "node-input-name": "NoNameme",
9
9
  "node-input-dpt": "Datapoint",
10
- "node-input-topic": "Monitor Wh",
10
+ "node-input-topic": "Monitor W",
11
11
  "node-input-controlGA": "Load",
12
12
  "node-input-monitorGA": "In use",
13
- "node-input-wattLimit": "Limit Wh",
13
+ "node-input-wattLimit": "Limit W",
14
14
  "node-input-sheddingCheckInterval": "Delay switch off (s)",
15
15
  "node-input-sheddingRestoreDelay": "Delay switch on (s)",
16
16
  "node-input-autoRestore": "Automatic recovery"
@@ -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
+ }
@@ -7,10 +7,10 @@
7
7
  "node-input-server": "Gateway",
8
8
  "node-input-name": "Nome",
9
9
  "node-input-dpt": "Datapoint",
10
- "node-input-topic": "Monitor Wh",
10
+ "node-input-topic": "Monitor W",
11
11
  "node-input-controlGA": "Carico",
12
12
  "node-input-monitorGA": "In uso",
13
- "node-input-wattLimit": "Soglia Wh",
13
+ "node-input-wattLimit": "Soglia W",
14
14
  "node-input-sheddingCheckInterval": "Ritardo distacco (s)",
15
15
  "node-input-sheddingRestoreDelay": "Ritardo ripristino (s)",
16
16
  "node-input-autoRestore": "Ripristino automatico"
@@ -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
+ }
@@ -7,10 +7,10 @@
7
7
  "node-input-server": "Gateway",
8
8
  "node-input-name": "NoNameme",
9
9
  "node-input-dpt": "Datapoint",
10
- "node-input-topic": "Monitor Wh",
10
+ "node-input-topic": "Monitor W",
11
11
  "node-input-controlGA": "Load",
12
12
  "node-input-monitorGA": "In use",
13
- "node-input-wattLimit": "Limit Wh",
13
+ "node-input-wattLimit": "Limit W",
14
14
  "node-input-sheddingCheckInterval": "Delay switch off (s)",
15
15
  "node-input-sheddingRestoreDelay": "Delay switch on (s)",
16
16
  "node-input-autoRestore": "Automatic recovery"
@@ -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.24",
3
+ "version": "1.3.28",
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": {