node-red-contrib-knx-ultimate 3.0.0-beta1 → 3.0.0-beta3

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
@@ -6,7 +6,16 @@
6
6
 
7
7
  # CHANGELOG
8
8
 
9
- **Version 3.0.0** - Juni 2024<br/>
9
+ **Version 3.0.0-beta3** - Juni 2024<br/>
10
+ - Warning: Node-Red version **equals or major than 3.1.1** is needed to run this node.<br/>
11
+ - Chore: "betterized" the description for some captions in the KNX Device node. </br>
12
+ - NEW: KNX Viewer node: added an output PIN (third) you can use to monitor the KNX BUS congestion.</br>
13
+
14
+ **Version 3.0.0-beta2** - Juni 2024<br/>
15
+ - Warning: Node-Red version **equals or major than 3.1.1** is needed to run this node.<br/>
16
+ - NEW: Hue Node Software Update Status for the HUE devices. <br/>
17
+
18
+ **Version 3.0.0-beta1** - Juni 2024<br/>
10
19
  - Warning: Node-Red version **equals or major than 3.1.1** is needed to run this node.<br/>
11
20
  - FEATURE CAUTION: rewrote the KNX engine in Typescript. If you encounter problems, please open a gitub issue. You can revert by installing the older version 2.5.1 <br/>
12
21
  - FEATURE: KNX Ultimate node: you can now set the group address from a global, flow or $env variable, beside the standard 3-level format. <br/>
@@ -344,6 +344,14 @@ module.exports = (RED) => {
344
344
  id: resource.id,
345
345
  });
346
346
  }
347
+ if (_rtype === 'device_software_update') {
348
+ const Room = node.hueAllRooms.find((room) => room.children.find((child) => child.rid === resource.owner.rid))
349
+ const linkedDevName = node.hueAllResources.find((dev) => dev.type === 'device' && dev.services.find((serv) => serv.rid === resource.id)).metadata.name || ''
350
+ retArray.push({
351
+ name: `Software status: ${linkedDevName}${Room !== undefined ? `, room ${Room.metadata.name}` : ''}`,
352
+ id: resource.id,
353
+ });
354
+ }
347
355
  } catch (error) {
348
356
  retArray.push({
349
357
  name: `${_rtype}: ERROR ${error.message}`,
@@ -498,7 +498,7 @@
498
498
  <div class="form-row" style="padding:10px">
499
499
  <div class="form-row">
500
500
  <dt>
501
- <i class="fa fa-arrow-right"></i> CONTROL (payload to KNX bus)
501
+ <i class="fa fa-arrow-right"></i> SEND msg to KNX BUS
502
502
  </dt>
503
503
  </div>
504
504
  <div class="form-row">
@@ -524,7 +524,7 @@
524
524
  <br />
525
525
  <div class="form-row">
526
526
  <dt>
527
- |<i class="fa fa-arrow-right"></i> STATUS (payload from KNX BUS)
527
+ |<i class="fa fa-arrow-right"></i> RECEIVE msg from KNX BUS
528
528
  </dt>
529
529
  </div>
530
530
  <div class="form-row" id="divNode-input-initialread">
@@ -685,10 +685,10 @@
685
685
  **Advanced options**
686
686
  |Property|Description|
687
687
  |--|--|
688
- ||**CONTROL (payload to KNX bus)**|
688
+ ||**SEND msg to KNX BUS**|
689
689
  | Telegram type | *write* to send write telegram (usually, you want that), otherwise you can choose the telegram's type to react to. |
690
690
  | RBE filter (in the CONTROL section) | *Report by change* filter. If set, only the msg input (from the Flow) having different values each time, will be sent to the KNX bus. If you intend to send everytime the same value, turn it off. |
691
- ||**STATUS (payload from KNX BUS)**|
691
+ ||**RECEIVE msg from KNX BUS**|
692
692
  | Read status on start | Read group address status, every time Node-Red starts and at every reconnection to the KNX Gateway. The node stores all group address values to a file, so you can choose wether to read from file or from the KNX bus. |
693
693
  | RBE filter (in the STATUS section)| *Report by change* filter. If set, only the msg output (to KNX bus) having different values each time, will be sent to the msg output's flow. If you intend to send everytime the same value, leave it off. |
694
694
  | React to write telegrams | The node will react (will send a msg to the flow) each time it receives a *write* telegram from the KNX bus. Usually, you want that. |
@@ -729,9 +729,9 @@ The new configuration will be retained until next msg.setConfig or until restart
729
729
  All properties (*setGroupAddress* and *setDPT*) **are mandatory**..
730
730
  Use it like that, in a functon node:
731
731
 
732
- ** Set both GA and DPT **
732
+ **Set both GA and DPT**
733
733
 
734
- ```json
734
+ ```javascript
735
735
  // Change the node properties as follows:
736
736
  // setGroupAddress: set the new group address.
737
737
  // setDPT: set the new Datapoint, as you can see in the dropdown list (the numeric part, for example "1.001", "237.600", etc...). If set to **auto**, the datapoint will be read from the ETS file (if present).
@@ -743,9 +743,9 @@ msg.setConfig = config;
743
743
  return msg;
744
744
  ```
745
745
 
746
- ** Set GA and read the datapoint from the ETS file **
746
+ **Set GA and read the datapoint from the ETS file**
747
747
 
748
- ```json
748
+ ```javascript
749
749
  // Change the node properties as follows:
750
750
  // setGroupAddress: set the new group address.
751
751
  // setDPT: set the new Datapoint, as you can see in the dropdown list (the numeric part, for example "1.001", "237.600", etc...). If set to "auto", the datapoint will be read from the ETS file (if present).
@@ -209,6 +209,7 @@ module.exports = function (RED) {
209
209
  node.setLocalStatus({ fill: 'grey', shape: 'dot', text: 'ERROR: You must provide payload (true/false)', payload: '', GA: '', dpt: '', devicename: '' });
210
210
  return;
211
211
  }
212
+
212
213
  msg.knx = { dpt: '1.001' };
213
214
  node.handleSend(msg);
214
215
  });
@@ -0,0 +1,261 @@
1
+ <script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/11f26b4500.js"></script>
2
+
3
+ <script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/htmlUtils.js"></script>
4
+
5
+ <script type="text/javascript">
6
+ RED.nodes.registerType('knxUltimateHuedevice_software_update', {
7
+ category: "KNX Ultimate",
8
+ color: '#C0C7E9',
9
+ defaults: {
10
+ //buttonState: {value: true},
11
+ server: { type: "knxUltimate-config", required: false },
12
+ serverHue: { type: "hue-config", required: true },
13
+ name: { value: "" },
14
+
15
+ namedevice_software_update: { value: "" },
16
+ GAdevice_software_update: { value: "" },
17
+ dptdevice_software_update: { value: "" },
18
+ readStatusAtStartup: { value: "yes" },
19
+
20
+
21
+ hueDevice: { value: "" }
22
+ },
23
+ inputs: 0,
24
+ outputs: 1,
25
+ icon: "node-hue-icon.svg",
26
+ label: function () {
27
+ return (this.name || "Hue Software Update");
28
+ },
29
+ paletteLabel: "Hue Software Update",
30
+ // button: {
31
+ // enabled: function() {
32
+ // // return whether or not the button is enabled, based on the current
33
+ // // configuration of the node
34
+ // return !this.changed
35
+ // },
36
+ // visible: function() {
37
+ // // return whether or not the button is visible, based on the current
38
+ // // configuration of the node
39
+ // return this.hasButton
40
+ // },
41
+ // //toggle: "buttonState",
42
+ // onclick: function() {}
43
+ // },
44
+ oneditprepare: function () {
45
+ var node = this;
46
+ var oNodeServer = RED.nodes.node($("#node-input-server").val()); // Store the config-node
47
+ var oNodeServerHue = RED.nodes.node($("#node-input-serverHue").val()); // Store the config-node
48
+
49
+ // 19/02/2020 Used to get the server sooner als deploy.
50
+ $("#node-input-server").change(function () {
51
+ try {
52
+ oNodeServer = RED.nodes.node($(this).val());
53
+ } catch (error) { }
54
+ });
55
+ // 19/02/2020 Used to get the server sooner als deploy.
56
+ $("#node-input-serverHue").change(function () {
57
+ try {
58
+ oNodeServerHue = RED.nodes.node($(this).val());
59
+ } catch (error) { }
60
+ });
61
+
62
+
63
+
64
+ // DPT
65
+ // ########################
66
+ $.getJSON('knxUltimateDpts?serverId=' + $("#node-input-server").val(), (data) => {
67
+ data.forEach(dpt => {
68
+ if (dpt.value.startsWith("1.")) {
69
+ $("#node-input-dptdevice_software_update").append($("<option></option>")
70
+ .attr("value", dpt.value)
71
+ .text(dpt.text))
72
+ }
73
+ });
74
+ $("#node-input-dptdevice_software_update").val(this.dptdevice_software_update)
75
+ })
76
+
77
+ // Autocomplete suggestion with ETS csv File
78
+ $("#node-input-GAdevice_software_update").autocomplete({
79
+ minLength: 0,
80
+ source: function (request, response) {
81
+ //$.getJSON("csv", request, function( data, status, xhr ) {
82
+ $.getJSON("knxUltimatecsv?nodeID=" + oNodeServer.id, (data) => {
83
+ response($.map(data, function (value, key) {
84
+ var sSearch = (value.ga + " (" + value.devicename + ") DPT" + value.dpt);
85
+ if (htmlUtilsfullCSVSearch(sSearch, request.term + " 1.")) {
86
+ return {
87
+ label: value.ga + " # " + value.devicename + " # " + value.dpt, // Label for Display
88
+ value: value.ga // Value
89
+ }
90
+ } else {
91
+ return null;
92
+ }
93
+ }));
94
+ });
95
+ }, select: function (event, ui) {
96
+ // Sets Datapoint and device name automatically
97
+ var sDevName = ui.item.label.split("#")[1].trim();
98
+ try {
99
+ sDevName = sDevName.substr(sDevName.indexOf(")") + 1).trim();
100
+ } catch (error) {
101
+ }
102
+ $('#node-input-namedevice_software_update').val(sDevName);
103
+ var optVal = $("#node-input-dptdevice_software_update option:contains('" + ui.item.label.split("#")[2].trim() + "')").attr('value');
104
+ // Select the option value
105
+ $("#node-input-dptdevice_software_update").val(optVal);
106
+ }
107
+ }).focus(function () {
108
+ $(this).autocomplete('search', $(this).val() + 'exactmatch');
109
+ });
110
+ // ########################
111
+
112
+
113
+
114
+
115
+ // Autocomplete suggestion with HUE
116
+ $("#node-input-name").autocomplete({
117
+ minLength: 0,
118
+ source: function (request, response) {
119
+ $.getJSON("KNXUltimateGetResourcesHUE?rtype=device_software_update&serverId=" + oNodeServerHue.id, (data) => {
120
+ response($.map(data.devices, function (value, key) {
121
+ //alert(JSON.stringify(value) + " "+ key)
122
+ var sSearch = (value.name);
123
+ if (htmlUtilsfullCSVSearch(sSearch, request.term)) {
124
+ return {
125
+ hueDevice: value.id, // Label for Display
126
+ value: value.name // Value
127
+ }
128
+ } else {
129
+ return null;
130
+ }
131
+ }));
132
+ });
133
+ }, select: function (event, ui) {
134
+ // Sets the fields
135
+ $('#node-input-hueDevice').val(ui.item.hueDevice);
136
+ }
137
+ }).focus(function () {
138
+ $(this).autocomplete('search', $(this).val() + 'exactmatch');
139
+ });
140
+
141
+
142
+ // ########################
143
+
144
+
145
+ },
146
+ oneditsave: function () {
147
+
148
+
149
+ },
150
+ oneditcancel: function () {
151
+
152
+ }
153
+ })
154
+
155
+ </script>
156
+
157
+ <script type="text/html" data-template-name="knxUltimateHuedevice_software_update">
158
+
159
+
160
+ <div class="form-row">
161
+ <b>HUE Device Software Update node</b>&nbsp&nbsp<span style="color:red"
162
+ &nbsp
163
+ &nbsp<i class="fa fa-youtube"></i></span>&nbsp<a target="_blank" href="https://youtu.be/jjEUI1J8bkA"><u>Youtube sample</u></a>
164
+ <br />
165
+ <br />
166
+ <p align="center">
167
+ <i class="fa-solid fa-tower-broadcast fa-beat fa-4x"></i>
168
+ </p>
169
+ <br />
170
+ <label for="node-input-server" >
171
+ <img 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>
172
+ KNX GW
173
+ </label>
174
+ <input type="text" id="node-input-server" />
175
+ </div>
176
+
177
+ <div class="form-row">
178
+ <label for="node-input-serverHue">
179
+ <img src="data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAABFUlEQVQ4EZWSsWoCQRCG1yiENEFEi6QSkjqWWoqFoBYJ+Br6JHkMn8Iibd4ihQpaJIhWNkry/ZtdGZY78Qa+m39nZ+dm9s4550awglNBluS/gVtAX6KgDclf68w2OThgfR9iT/jnoEv4TtByDThWTCDKW4SSZTf/zj9/eZbN+izTDuKGimu0vPF8B/YN8aC8LmcOj/AAn9CFTEs70Js/oGqy79C69bqJ5XbQI2kGO5N8QL9D08S8zBtBF5ZaVsznpCMoqJnVdjTpb1Db0fwIWmQV6BLXzFOYgA6/gDVfQN9bBWp2J2hdWDPoBV5FrKnAJutHikk/CHHR8i7x4iG7qQ720IYvu3GFbpHjx3pFrOFYkA354z/5bkK826phyAAAAABJRU5ErkJggg=="/>
180
+ HUE Bridge
181
+ </label>
182
+ <input type="text" id="node-input-serverHue" />
183
+ </div>
184
+
185
+ <br/>
186
+ <p>
187
+ <b>Philips HUE</b>
188
+ </p>
189
+
190
+ <div class="form-row">
191
+ <label for="node-input-hueDevice" >
192
+ <i class="fa fa-play-circle"></i>&nbspHue Device</label>
193
+ <input type="text" id="node-input-name" placeholder="Enter your hue device name" />
194
+ <input type="hidden" id="node-input-hueDevice" />
195
+ </div>
196
+
197
+ <br/>
198
+
199
+ <p>
200
+ <b>KNX</b>
201
+ </p>
202
+
203
+ <div class="form-row">
204
+ <label for="node-input-namedevice_software_update" style="width:100px;"><i class="fa fa-play-circle-o"></i> Status</span></label>
205
+
206
+ <label for="node-input-GAdevice_software_update" style="width:20px;">GA</label>
207
+ <input type="text" id="node-input-GAdevice_software_update" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
208
+
209
+ <label for="node-input-dptdevice_software_update" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
210
+ <select id="node-input-dptdevice_software_update" style="width:140px;"></select>
211
+
212
+ <label for="node-input-namedevice_software_update" style="width:50px; margin-left: 0px; text-align: right;">Name</label>
213
+ <input type="text" id="node-input-namedevice_software_update" style="width:200px;margin-left: 5px; text-align: left;">
214
+ </div>
215
+
216
+ <div class="form-row">
217
+ <label style="width:180px" for="node-input-readStatusAtStartup"><i class="fa fa-play-circle"></i> Read status at startup</label>
218
+ <select id="node-input-readStatusAtStartup">
219
+ <option value="no">No</option>
220
+ <option value="yes">Yes, and emit KNX telegrams.</option>
221
+ </select>
222
+ </div>
223
+ <br/>
224
+ <br/>
225
+ <br/>
226
+
227
+
228
+ </script>
229
+
230
+ <script type="text/markdown" data-help-name="knxUltimateHuedevice_software_update">
231
+ This node monitors wether the selected device has a software update avaiable.
232
+
233
+ Start typing the name or group address of your KNX device in the GA field, the avaiable devices start showing up while
234
+ you're typing.
235
+
236
+ **General**
237
+ |Property|Description|
238
+ |--|--|
239
+ | KNX GW | Select the KNX gateway to be used |
240
+ | HUE Bridge | Select the HUE Bridge to be used |
241
+ | Hue Device | HUE Device to be monitored for software update. The avaiable devices start showing up while you're typing.|
242
+
243
+ |Property|Description|
244
+ |--|--|
245
+ | Status | The software update status KNX group address. The payload of this KNX group address will change to *true* if a device has a software update avaiable or is installing the update, or if the update is ready to be installed, otherwise *false*. |
246
+ | Read status at startup | Read the status at startup and emit the event to the KNX bus at startup/reconnection. (Default "yes")|
247
+
248
+ ### Outputs
249
+
250
+ 1. Standard output
251
+ : payload (boolean) : the standard output of the command.
252
+ : status (string) : the text representing the status. It can be: **no_update, update_pending, ready_to_install, installing**.
253
+
254
+
255
+ <br />
256
+
257
+ [DONATE HERE, THANK YOU!](https://www.paypal.me/techtoday)
258
+
259
+ <br />
260
+
261
+ </script>
@@ -0,0 +1,134 @@
1
+ module.exports = function (RED) {
2
+ function knxUltimateHuedevice_software_update(config) {
3
+ RED.nodes.createNode(this, config);
4
+ const node = this;
5
+ node.server = RED.nodes.getNode(config.server);
6
+ node.serverHue = RED.nodes.getNode(config.serverHue);
7
+ node.topic = node.name;
8
+ node.name = config.name === undefined ? 'Hue' : config.name;
9
+ node.dpt = '';
10
+ node.notifyreadrequest = true;
11
+ node.notifyreadrequestalsorespondtobus = 'false';
12
+ node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = '';
13
+ node.notifyresponse = false;
14
+ node.notifywrite = true;
15
+ node.initialread = true;
16
+ node.listenallga = true; // Don't remove
17
+ node.outputtype = 'write';
18
+ node.outputRBE = 'false'; // Apply or not RBE to the output (Messages coming from flow)
19
+ node.inputRBE = 'false'; // Apply or not RBE to the input (Messages coming from BUS)
20
+ node.currentPayload = ''; // Current value for the RBE input and for the .previouspayload msg
21
+ node.passthrough = 'no';
22
+ node.formatmultiplyvalue = 1;
23
+ node.formatnegativevalue = 'leave';
24
+ node.formatdecimalsvalue = 2;
25
+ node.hueDevice = config.hueDevice;
26
+ node.initializingAtStart = (config.readStatusAtStartup === undefined || config.readStatusAtStartup === "yes");
27
+ node.currentDeviceValue = false;
28
+
29
+ // Used to call the status update from the config node.
30
+ node.setNodeStatus = ({
31
+ fill, shape, text, payload,
32
+ }) => {
33
+
34
+ };
35
+ // Used to call the status update from the HUE config node.
36
+ node.setNodeStatusHue = ({
37
+ fill, shape, text, payload,
38
+ }) => {
39
+ if (payload === undefined) payload = '';
40
+ const dDate = new Date();
41
+ payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString();
42
+ node.status({ fill, shape, text: `${text} ${payload} (${dDate.getDate()}, ${dDate.toLocaleTimeString()})` });
43
+ };
44
+
45
+ // This function is called by the knx-ultimate config node, to output a msg.payload.
46
+ node.handleSend = (msg) => {
47
+ // Respond to KNX read telegram, by sending the current value as response telegram.
48
+ if (msg.knx.event === "GroupValue_Read") {
49
+ switch (msg.knx.destination) {
50
+ case config.GAdevice_software_update:
51
+ // To the KNX bus wires
52
+ node.sendResponseToKNX(node.currentDeviceValue);
53
+ break;
54
+ default:
55
+ break;
56
+ }
57
+ }
58
+ };
59
+
60
+ node.handleSendHUE = (_event) => {
61
+ try {
62
+ // Event status: one of connected, disconnected, connectivity_issue, unidirectional_incoming
63
+ if (_event.id === config.hueDevice) {
64
+ if (!_event.hasOwnProperty("state") || _event.state === undefined) return;
65
+
66
+ const knxMsgPayload = {};
67
+ knxMsgPayload.topic = config.GAdevice_software_update;
68
+ knxMsgPayload.dpt = config.dptdevice_software_update;
69
+
70
+ knxMsgPayload.payload = (_event.state !== "no_update");
71
+ // Send to KNX bus
72
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
73
+ node.server.writeQueueAdd({
74
+ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id,
75
+ });
76
+ }
77
+ node.currentDeviceValue = knxMsgPayload.payload;
78
+ // Setup the output msg
79
+ knxMsgPayload.name = node.name;
80
+ knxMsgPayload.status = _event.state;
81
+ knxMsgPayload.event = 'device_software_update';
82
+ // Send payload
83
+ knxMsgPayload.rawEvent = _event;
84
+ node.send(knxMsgPayload);
85
+ node.setNodeStatusHue({
86
+ fill: 'blue', shape: 'ring', text: 'HUE->KNX', payload: knxMsgPayload.payload,
87
+ });
88
+ }
89
+ } catch (error) {
90
+ node.status({ fill: 'red', shape: 'dot', text: `HUE->KNX error ${error.message} (${new Date().getDate()}, ${new Date().toLocaleTimeString()})` });
91
+ }
92
+ };
93
+
94
+
95
+ node.sendResponseToKNX = (_level) => {
96
+ const knxMsgPayload = {};
97
+ knxMsgPayload.topic = config.GAdevice_software_update;
98
+ knxMsgPayload.dpt = config.dptdevice_software_update;
99
+
100
+ knxMsgPayload.payload = _level;
101
+ // Send to KNX bus
102
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
103
+ node.server.writeQueueAdd({
104
+ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'response', nodecallerid: node.id,
105
+ });
106
+ }
107
+ };
108
+
109
+ // On each deploy, unsubscribe+resubscribe
110
+ if (node.server) {
111
+ node.server.removeClient(node);
112
+ node.server.addClient(node);
113
+ }
114
+ if (node.serverHue) {
115
+ node.serverHue.removeClient(node);
116
+ node.serverHue.addClient(node);
117
+ }
118
+
119
+ node.on('input', (msg) => {
120
+
121
+ });
122
+
123
+ node.on('close', (done) => {
124
+ if (node.server) {
125
+ node.server.removeClient(node);
126
+ }
127
+ if (node.serverHue) {
128
+ node.serverHue.removeClient(node);
129
+ }
130
+ done();
131
+ });
132
+ }
133
+ RED.nodes.registerType('knxUltimateHuedevice_software_update', knxUltimateHuedevice_software_update);
134
+ };
@@ -10,14 +10,17 @@
10
10
  name: { value: "KNXViewer", required: false }
11
11
  },
12
12
  inputs: 0,
13
- outputs: 2,
13
+ outputs: 3,
14
14
  outputLabels: function (i) {
15
15
  switch (i) {
16
16
  case 0:
17
- return "Formatted Payload";
17
+ return "Dashboard Group Addresses";
18
18
  break;
19
19
  case 1:
20
- return "Simple Array";
20
+ return "Simple Array Json for data recording";
21
+ break;
22
+ case 2:
23
+ return "Dashboard Telegrams queue";
21
24
  break;
22
25
  default:
23
26
  break;
@@ -85,10 +88,10 @@
85
88
  </script>
86
89
 
87
90
  <script type="text/markdown" data-help-name="knxUltimateViewer">
88
- View all group addresses and their values in a Dashboard widget.
91
+ View all group addresses and their values in a Dashboard widget, as well as JSON Oblect output and KNX transmission queue congestion.
92
+
93
+ For all output pins marked as "Dashboard.. ", the node works in conjunction with the Node-Red dashboard UI Template node.
89
94
 
90
- This node works in conjunction with the Node-Red dashboard UI Template node.
91
- View all group addresses and their values in a Dashboard widget.
92
95
 
93
96
  **General**
94
97
  |Property|Description|
@@ -98,12 +101,28 @@ View all group addresses and their values in a Dashboard widget.
98
101
  |
99
102
  <br/>
100
103
 
101
- ### Outputs
104
+ ### Outputs PINS
102
105
 
103
- 1. Standard output
104
- : payload (json) : formatted payload. Connect it directly with the Template UI node.
105
- 2. Array of objects
106
+ 1. Dashboard Group Addresses
107
+ : payload (html) : Connect it directly with the Template UI node. It creates a table with all Group Addresses and their values.
108
+ 2. Simple Array JSON for data recording
106
109
  : payload (array) : An array containing all the GA. You can use the array to do your own format and reordering.
110
+ 2. Dashboard telegrams queue
111
+ : payload (html) : Connect it directly with the Template UI node. It creates a table with the KNX transmission queue for monitoring BUS congestion.
112
+
113
+ <br/>
114
+ <br/>
115
+
116
+ ## SAMPLE
117
+
118
+ <img src="https://raw.githubusercontent.com/Supergiovane/node-red-contrib-knx-ultimate/master/img/wiki/viewer2.png" width="90%"><br/>
119
+
120
+ In this example, you can see how the Viewer works.<br/>
121
+ Just put a Viewer node in your flow and attach a Template UI node behind it.<br/>
122
+ The **template** node (Dashboard's Template UI) contains the default text:<br/>
123
+ The Output is something as follows:<br/>
124
+
125
+ <img src="https://raw.githubusercontent.com/Supergiovane/node-red-contrib-knx-ultimate/master/img/wiki/viewer1.png" width="90%"><br/>
107
126
 
108
127
  <br/>
109
128
 
@@ -1,4 +1,5 @@
1
1
  const KNXAddress = require('knxultimate').KNXAddress;
2
+ const _ = require("lodash");
2
3
 
3
4
  module.exports = function (RED) {
4
5
  function knxUltimateViewer(config) {
@@ -24,7 +25,7 @@ module.exports = function (RED) {
24
25
  node.formatmultiplyvalue = 1;
25
26
  node.formatnegativevalue = 'leave';
26
27
  node.formatdecimalsvalue = 2;
27
-
28
+ node.timerPIN3 = null;
28
29
  node.exposedGAs = [];
29
30
 
30
31
  // Used to call the status update from the config node.
@@ -59,11 +60,24 @@ module.exports = function (RED) {
59
60
  oGa.payloadmeasureunit = (msg.payloadmeasureunit !== 'unknown' ? ' ' + msg.payloadmeasureunit : '');
60
61
  }
61
62
  // Output the payload
62
- node.createPayload();
63
+ let PIN1 = node.createPayloadPIN1();
64
+ let PIN2 = node.createPayloadPIN2();
65
+ let PIN3 = node.createPayloadPIN3();
66
+ node.send([PIN1, PIN2, PIN3]);
63
67
  };
64
68
 
65
- node.createPayload = () => {
66
- const sHead = `<table>
69
+ node.createPayloadPIN1 = () => {
70
+
71
+ const aSorted = node.exposedGAs.sort((a, b) => {
72
+ if (a.addressRAW !== undefined && b.addressRAW !== undefined) {
73
+ return a.addressRAW > b.addressRAW ? 1 : -1;
74
+ } else {
75
+ return a.addressRAW !== undefined ? 1 : -1;
76
+ }
77
+ });
78
+ let sPayload = '';
79
+
80
+ const sHead = `<div class="main"><table><caption>Current received KNX Group address values</caption>
67
81
  <thead>
68
82
  <tr>
69
83
  <th> GA </th>
@@ -74,17 +88,14 @@ module.exports = function (RED) {
74
88
  </tr>
75
89
  </thead>
76
90
  <tbody>`;
77
- const sFooter = `</tbody>
78
- </table>`;
79
- let sPayload = '';
91
+ const sFooter = `</tbody><tfoot>
92
+ <tr>
93
+ <th scope="row">Count</th>
94
+ <td>` + aSorted.length + `</td>
95
+ </tr>
96
+ </tfoot>
97
+ </table></div>`;
80
98
 
81
- const aSorted = node.exposedGAs.sort((a, b) => {
82
- if (a.addressRAW !== undefined && b.addressRAW !== undefined) {
83
- return a.addressRAW > b.addressRAW ? 1 : -1;
84
- } else {
85
- return a.addressRAW !== undefined ? 1 : -1;
86
- }
87
- });
88
99
  try {
89
100
  for (let index = 0; index < aSorted.length; index++) {
90
101
  const element = aSorted[index];
@@ -114,15 +125,87 @@ module.exports = function (RED) {
114
125
  } catch (error) {
115
126
 
116
127
  }
128
+ return { topic: node.name, payload: sHead + sPayload + sFooter };
129
+ };
130
+ node.createPayloadPIN2 = () => {
131
+ return { topic: node.name, payload: node.exposedGAs }
132
+ };
133
+ node.createPayloadPIN3 = () => {
134
+
135
+ // Object containing the telegram in the queue
136
+ // node.writeQueueAdd({
137
+ // grpaddr: _oClient.topic,
138
+ // payload: "",
139
+ // dpt: "",
140
+ // outputtype: "read",
141
+ // nodecallerid: _oClient.id,
142
+ // });
143
+ const aItems = _.clone(node.server.telegramsQueue);
144
+ let sPayload = '';
117
145
 
118
- node.send([{ topic: node.name, payload: sHead + sPayload + sFooter }, { topic: node.name, payload: node.exposedGAs }]);
146
+ const sHead = `<div class="main"><table><caption>Queue of outgoing telegrams to the KNX BUS. The more the count,</br>the more congested is the KNX BUS.</caption>
147
+ <thead>
148
+ <tr>
149
+ <th> GA </th>
150
+ <th> Value </th>
151
+ <th> DPT </th>
152
+ <th> Output Type </th>
153
+ <th> Caller Node (id) </th>
154
+ </tr>
155
+ </thead>
156
+ <tbody>`;
157
+ const sFooter = `</tbody><tfoot>
158
+ <tr>
159
+ <th scope="row">Count</th>
160
+ <td>` + aItems.length + `</td>
161
+ </tr>
162
+ </tfoot>
163
+ </table></div>`;
164
+
165
+ try {
166
+ for (let index = 0; index < aItems.length; index++) {
167
+ const element = aItems[index];
168
+ sPayload += '<tr><td>' + element.grpaddr + '</td>';
169
+ if (typeof element.payload === 'boolean' && element.payload === true) {
170
+ sPayload += '<td><b><font color=green>True</font></b></td>';
171
+ } else if (typeof element.payload === 'boolean' && element.payload === false) {
172
+ sPayload += '<td><font color=red>False</font></td>';
173
+ } else if (typeof element.payload === 'object' && !isNaN(Date.parse(element.payload))) {
174
+ // The payload is a datetime
175
+ sPayload += '<td>' + element.payload.toLocaleString() + '</td>';
176
+ } else if (typeof element.payload === 'object') {
177
+ // Is maybe a JSON?
178
+ try {
179
+ // sPayload += '<td>' + JSON.stringify(element.payload) + '</td>'
180
+ sPayload += '<td><i>' + element.rawPayload + '</i></td>';
181
+ } catch (error) {
182
+ sPayload += '<td>' + element.payload + '</td>';
183
+ }
184
+ } else {
185
+ sPayload += '<td>' + element.payload + element.payloadmeasureunit + '</td>';
186
+ }
187
+ sPayload += '<td>' + element.dpt + '</td>';
188
+ sPayload += '<td>' + element.outputtype + '</td>';
189
+ sPayload += '<td><font style="font-size: smaller;">' + element.nodecallerid + '</font></td></tr>';
190
+ }
191
+ } catch (error) {
192
+
193
+ }
194
+ return { topic: node.name, payload: sHead + sPayload + sFooter };
119
195
  };
120
196
 
197
+ // if (timerPIN3 !== null) clearInterval(timerPIN3);
198
+ // timerPIN3 = setInterval(() => {
199
+ // let PIN3 = node.createPayloadPIN3();
200
+ // node.send([null, null, PIN3]);
201
+ // }, 200);
202
+
121
203
  node.on('input', function (msg) {
122
204
 
123
205
  });
124
206
 
125
207
  node.on('close', function (done) {
208
+ if (timerPIN3 !== null) clearInterval(timerPIN3);
126
209
  if (node.server) {
127
210
  node.server.removeClient(node);
128
211
  }
@@ -36,8 +36,8 @@
36
36
  "Advancedoptions": "Advanced options",
37
37
  "Formatting": "Format INPUT (datagram coming from the KNX BUS)",
38
38
  "Operations": "NUMERIC VALUES (operations are performed in order)",
39
- "OUTPUT": "CONTROL (payload to KNX bus)",
40
- "INPUT": "STATUS (payload from KNX BUS)",
39
+ "OUTPUT": "SEND msg to KNX bus",
40
+ "INPUT": "RECEIVE msg from KNX BUS",
41
41
  "NUMERICVALUES": "NUMERIC VALUES (operations are performed in order)",
42
42
  "noETSWarning": "You haven't imported the ETS csv file. <br/> The node will work as blind transmitter/receiver. <a href=\"https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/2.-Node-Configuration\" target='_blank'>Click here for help about that</a>.",
43
43
  "notify-DPT3007": "You selected a relative DIM. Please <a target='_blank' href='https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/-Sample---Dimming'>click here to view this sample</a> and learn how to handle such payload.",
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "engines": {
4
4
  "node": ">=16.0.0"
5
5
  },
6
- "version": "3.0.0-beta1",
6
+ "version": "3.0.0-beta3",
7
7
  "description": "Control your KNX intallation via Node-Red! A bunch of KNX nodes, with integrated Philips HUE control and ETS group address importer. Easy to use and highly configurable.",
8
8
  "dependencies": {
9
9
  "binary-parser": "2.2.1",
@@ -47,7 +47,8 @@
47
47
  "knxUltimateHueBattery": "/nodes/knxUltimateHueBattery.js",
48
48
  "knxUltimateHueZigbeeConnectivity": "/nodes/knxUltimateHueZigbeeConnectivity.js",
49
49
  "knxUltimateContactSensor": "/nodes/knxUltimateHueContactSensor.js",
50
- "knxUltimateHATranslator": "/nodes/knxUltimateHATranslator.js"
50
+ "knxUltimateHATranslator": "/nodes/knxUltimateHATranslator.js",
51
+ "knxUltimateHuedevice_software_update": "/nodes/knxUltimateHuedevice_software_update.js"
51
52
  }
52
53
  },
53
54
  "repository": {