node-red-contrib-knx-ultimate 2.2.25 → 2.2.26

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,6 +6,15 @@
6
6
 
7
7
  # CHANGELOG
8
8
 
9
+ **Version 2.2.26** - November 2023<br/>
10
+ This is an interim version, to quick fix some issues. Please report any issue with HUE Nodes, on gitHub.<br/>
11
+ - HUE Light: fixed some spurious node status errors.<br/>
12
+ - HUE Light: now the brightness status is ever transmitted over the KNX bus, whenever the light is switched on.<br/>
13
+ - HUE Battery: the node can respond to KNX read request, by sending the stored value as response to the KNX bus.<br/>
14
+ - HUE light level: the node can respond to KNX read request, by sending the stored value as response to the KNX bus.<br/>
15
+ - HUE temperature sensor: the node can respond to KNX read request, by sending the stored value as response to the KNX bus.<br/>
16
+ - WARNING: the new HUE Light options are to be considered **BETA (= in testing with user feedback)**.<br/>
17
+
9
18
  **Version 2.2.25** - November 2023<br/>
10
19
  - HUE Light: fixed settings of some behaviour options.<br/>
11
20
  - You can now query the HUE Light node stati, via a "read" telegram sent to the KNX stati group address. ("Status" is Latin, not English, so the plural is "stati" and not "statuses" nor "status"). Other HUE nodes will follow asap.<br/>
@@ -18,7 +18,7 @@
18
18
  }
19
19
  $("#getinfocam").click(function () {
20
20
 
21
- var myNotification = RED.notify("Please press the Link button on the HUE Bridge",
21
+ var myNotification = RED.notify("Please press the Link button on the HUE Bridge. Once pressed, click OK.",
22
22
  {
23
23
  modal: true,
24
24
  fixed: true,
@@ -125,17 +125,15 @@
125
125
  <label for="node-config-input-clientkey"> Bridge Key</label>
126
126
  <input type="password" id="node-config-input-clientkey" placeholder="" disabled>
127
127
  </div>
128
+ <div class="form-row">
129
+ <label for="node-config-input-debug"> Bridge Key</label>
130
+ <input rows="20" style="width:100%" type="textarea" id="node-config-input-debug" placeholder="" disabled>
131
+ </div>
128
132
  </div>
129
133
 
130
134
 
131
135
  </script>
132
- <script type="text/markdown" data-help-name="hue-config"
133
- This node registers to the Hue Bridge.
134
-
135
- Just set the Bridge's IP and click **CONNECT** button.
136
-
137
- [Find it useful?](https://www.paypal.me/techtoday)
138
-
139
- <br/>
136
+ <script type="text/markdown" data-help-name="hue-config" This node registers to the Hue Bridge. Just set the Bridge's IP
137
+ and click **CONNECT** button. [Find it useful?](https://www.paypal.me/techtoday) <br />
140
138
 
141
139
  </script>
@@ -174,6 +174,24 @@ module.exports = (RED) => {
174
174
  // °°°°°° Load ALL resources
175
175
  try {
176
176
  node.hueAllResources = await node.hueManager.hueApiV2.get("/resource");
177
+
178
+ // // DEBUG
179
+ // try {
180
+ // const fs = require('fs');
181
+ // const { resolve } = require('path');
182
+ // const content = JSON.stringify(node.hueAllResources);
183
+ // try {
184
+ // fs.writeFileSync('resources.json', content);
185
+ // RED.log.info("******************************* FILE WROTE IN resources.json " + resolve("resources.json"))
186
+ // // file written successfully
187
+ // } catch (error) {
188
+ // RED.log.error("********************************************* const content = JSON.stringify(node.hueAllResources)2222: " + error.message)
189
+ // }
190
+ // } catch (error) {
191
+ // RED.log.error("********************************************* const content = JSON.stringify(node.hueAllResources): " + error.message)
192
+ // }
193
+
194
+
177
195
  if (node.hueAllResources !== undefined) {
178
196
  node.hueAllRooms = node.hueAllResources.filter((a) => a.type === "room");
179
197
  // Update all KNX State of the nodes with the new hue device values
@@ -206,21 +224,25 @@ module.exports = (RED) => {
206
224
  };
207
225
 
208
226
  node.getFirstLightInGroup = function getFirstLightInGroup(_groupID) {
209
- if (node.hueAllResources === undefined) return;
210
- // Find the group
211
- const group = node.hueAllResources.filter((a) => a.id === _groupID)[0];
212
- if (group === null || group === undefined) return;
213
- const owner = node.hueAllResources.filter((a) => a.id === group.owner.rid)[0];
214
- if (owner.children !== undefined && owner.children.length > 0) {
215
- const firstLightId = owner.children.find((a) => a.rtype === "light").rid;
216
- if (firstLightId !== undefined && firstLightId !== null) {
217
- const firstLight = node.hueAllResources.find((a) => a.id === firstLightId);
218
- if (firstLight !== null && firstLight !== undefined) {
219
- return firstLight;
220
- } else {
221
- return;
227
+ if (node.hueAllResources === undefined || node.hueAllResources === null) return;
228
+ try {
229
+ // Find the group
230
+ const group = node.hueAllResources.filter((a) => a.id === _groupID)[0];
231
+ if (group === null || group === undefined) return;
232
+ const owner = node.hueAllResources.filter((a) => a.id === group.owner.rid)[0];
233
+ if (owner.children !== undefined && owner.children.length > 0) {
234
+ const firstLightId = owner.children.find((a) => a.rtype === "light").rid;
235
+ if (firstLightId !== undefined && firstLightId !== null) {
236
+ const firstLight = node.hueAllResources.find((a) => a.id === firstLightId);
237
+ if (firstLight !== null && firstLight !== undefined) {
238
+ return firstLight;
239
+ } else {
240
+ return;
241
+ }
222
242
  }
223
243
  }
244
+ } catch (error) {
245
+
224
246
  }
225
247
  };
226
248
 
@@ -443,7 +465,7 @@ module.exports = (RED) => {
443
465
  // °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
444
466
  const serverNode = RED.nodes.getNode(req.query.nodeID); // Retrieve node.id of the config node.
445
467
  if (serverNode === null) {
446
- RED.log.error(`Warn KNXUltimateGetResourcesHUE serverNode is null`);
468
+ RED.log.warn(`Warn KNXUltimateGetResourcesHUE serverNode is null`);
447
469
  res.json({ devices: `serverNode not set` });
448
470
  return;
449
471
  }
@@ -457,6 +479,7 @@ module.exports = (RED) => {
457
479
  } catch (error) {
458
480
  //RED.log.error(`Errore KNXUltimateGetResourcesHUE non gestito ${error.message}`);
459
481
  res.json({ devices: error.message });
482
+ RED.log.error(`Err KNXUltimateGetResourcesHUE: ${error.message}`);
460
483
  // (async () => {
461
484
  // await node.initHUEConnection();
462
485
  // })();
@@ -21,7 +21,7 @@
21
21
  ignoreTelegramsWithRepeatedFlag: { value: false, required: false },
22
22
  keyringFileXML: { value: "" },
23
23
  knxSecureSelected: { value: false },
24
- autoReconnect: { value: true }
24
+ autoReconnect: { value: "yes" }
25
25
  },
26
26
  credentials: {
27
27
  keyringFilePassword: { type: "password" }
@@ -13,13 +13,13 @@
13
13
  namebatterysensor: { value: "" },
14
14
  GAbatterysensor: { value: "" },
15
15
  dptbatterysensor: { value: "" },
16
- readStatusAtStartup: { value: "no" },
16
+ readStatusAtStartup: { value: "yes" },
17
17
 
18
18
 
19
19
  hueDevice: { value: "" }
20
20
  },
21
21
  inputs: 0,
22
- outputs: 0,
22
+ outputs: 1,
23
23
  icon: "node-hue-icon.svg",
24
24
  label: function () {
25
25
  return (this.name);
@@ -7,7 +7,7 @@ module.exports = function (RED) {
7
7
  node.topic = node.name;
8
8
  node.name = config.name === undefined ? 'Hue' : config.name;
9
9
  node.dpt = '';
10
- node.notifyreadrequest = false;
10
+ node.notifyreadrequest = true;
11
11
  node.notifyreadrequestalsorespondtobus = 'false';
12
12
  node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = '';
13
13
  node.notifyresponse = false;
@@ -23,7 +23,9 @@ module.exports = function (RED) {
23
23
  node.formatnegativevalue = 'leave';
24
24
  node.formatdecimalsvalue = 2;
25
25
  node.hueDevice = config.hueDevice;
26
- node.initializingAtStart = !((config.readStatusAtStartup === undefined || config.readStatusAtStartup === "no"));
26
+ node.initializingAtStart = !((config.readStatusAtStartup === undefined || config.readStatusAtStartup === "yes"));
27
+ node.currentDeviceValue = 0;
28
+
27
29
  // Used to call the status update from the config node.
28
30
  node.setNodeStatus = ({
29
31
  fill, shape, text, payload,
@@ -42,13 +44,22 @@ module.exports = function (RED) {
42
44
 
43
45
  // This function is called by the knx-ultimate config node, to output a msg.payload.
44
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.GAbatterysensor:
51
+ // To the KNX bus wires
52
+ node.sendResponseToKNX(node.currentDeviceValue);
53
+ break;
54
+ default:
55
+ break;
56
+ }
57
+ }
45
58
  };
46
59
 
47
60
  node.handleSendHUE = (_event) => {
48
61
  try {
49
62
  if (_event.id === config.hueDevice) {
50
-
51
- // IMPORTANT: exit if no event presen.
52
63
  if (!_event.hasOwnProperty("power_state") || _event.power_state.battery_level === undefined) return;
53
64
 
54
65
  const knxMsgPayload = {};
@@ -62,6 +73,7 @@ module.exports = function (RED) {
62
73
  grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id,
63
74
  });
64
75
  }
76
+ node.currentDeviceValue = knxMsgPayload.payload;
65
77
 
66
78
  // Setup the output msg
67
79
  knxMsgPayload.name = node.name;
@@ -73,13 +85,27 @@ module.exports = function (RED) {
73
85
  node.setNodeStatusHue({
74
86
  fill: 'blue', shape: 'ring', text: 'HUE->KNX', payload: knxMsgPayload.payload,
75
87
  });
76
-
77
88
  }
78
89
  } catch (error) {
79
90
  node.status({ fill: 'red', shape: 'dot', text: `HUE->KNX error ${error.message} (${new Date().getDate()}, ${new Date().toLocaleTimeString()})` });
80
91
  }
81
92
  };
82
93
 
94
+
95
+ node.sendResponseToKNX = (_level) => {
96
+ const knxMsgPayload = {};
97
+ knxMsgPayload.topic = config.GAbatterysensor;
98
+ knxMsgPayload.dpt = config.dptbatterysensor;
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
+
83
109
  // On each deploy, unsubscribe+resubscribe
84
110
  if (node.server) {
85
111
  node.server.removeClient(node);
@@ -194,8 +194,6 @@ module.exports = function (RED) {
194
194
  node.server.writeQueueAdd({
195
195
  grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id,
196
196
  });
197
- }
198
- if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
199
197
  node.setNodeStatusHue({
200
198
  fill: 'grey', shape: 'ring', text: 'HUE->KNX STOP DIM', payload: knxMsgPayload.payload,
201
199
  });
@@ -92,6 +92,7 @@
92
92
  inputs: { value: 0 },
93
93
 
94
94
  hueDevice: { value: "" },
95
+ hueDeviceObject: { value: {} },
95
96
  },
96
97
  inputs: 0,
97
98
  outputs: 0,
@@ -99,7 +100,7 @@
99
100
  label: function () {
100
101
  return this.name;
101
102
  },
102
- paletteLabel: "Hue Light",
103
+ paletteLabel: "Hue Light (beta)",
103
104
  oneditprepare: function () {
104
105
  var node = this;
105
106
  var oNodeServer = RED.nodes.node($("#node-input-server").val()); // Store the config-node
@@ -126,8 +127,10 @@
126
127
  }
127
128
  // ################################################################
128
129
 
130
+
129
131
  $("#tabs").tabs(); // Tabs gestione KNX
130
132
 
133
+
131
134
  // 19/02/2020 Used to get the server sooner als deploy.
132
135
  $("#node-input-server").change(function () {
133
136
  try {
@@ -169,7 +172,7 @@
169
172
  }
170
173
  });
171
174
  // Eval
172
- const format = "node." + _destinationWidget.replace("#node-input-", "");
175
+ const format = "this." + _destinationWidget.replace("#node-input-", "");
173
176
  try {
174
177
  if (format !== undefined) $(_destinationWidget).val(eval(format).toString());
175
178
  } catch (error) { }
@@ -264,6 +267,52 @@
264
267
  getGroupAddress("#node-input-GALightKelvinState", "#node-input-nameLightKelvinState", "#node-input-dptLightKelvinState", " 9.002");
265
268
 
266
269
 
270
+
271
+ // Autocomplete suggestion with HUE Lights
272
+ $("#node-input-name").autocomplete({
273
+ minLength: 1,
274
+ source: function (request, response) {
275
+ $.getJSON("KNXUltimateGetResourcesHUE?rtype=light&nodeID=" + oNodeServerHue.id, (data) => {
276
+ response(
277
+ $.map(data.devices, function (value, key) {
278
+ //alert(JSON.stringify(value) + " "+ key)
279
+ var sSearch = value.name;
280
+ if (!value.name.includes("I'm still connecting")) {
281
+ if (fullSearch(sSearch, request.term)) {
282
+ return {
283
+ hueDevice: value.id,
284
+ value: value.name,
285
+ };
286
+ } else {
287
+ return null;
288
+ }
289
+ } else {
290
+ return {
291
+ hueDevice: value.id,
292
+ value: value.name,
293
+ };
294
+ }
295
+ })
296
+ );
297
+ });
298
+ },
299
+ select: function (event, ui) {
300
+ // Distinguish between group of lights an single light.
301
+ if (ui.item.value.toLowerCase().startsWith("grouped_light")) {
302
+ $("#node-input-hueDevice").val(ui.item.hueDevice + "#grouped_light");
303
+ $("#getColorAtSwitchOnDayTimeButton").hide();
304
+ $("#getColorAtSwitchOnNightTimeButton").hide();
305
+ } else {
306
+ $("#node-input-hueDevice").val(ui.item.hueDevice + "#light");
307
+ $("#getColorAtSwitchOnDayTimeButton").show();
308
+ $("#getColorAtSwitchOnNightTimeButton").show();
309
+ }
310
+ $("#tabs").show();
311
+ },
312
+ });
313
+
314
+
315
+
267
316
  // Show/Hide the div of the color at swich on
268
317
  if (this.specifySwitchOnBrightness === "yes") {
269
318
  $("#divColorsAtSwitchOn").show();
@@ -313,7 +362,7 @@
313
362
  $("#divColorsAtSwitchOnNightTime").show();
314
363
  $("#divTemperatureAtSwitchOnNightTime").hide();
315
364
  blinkBackground("#colorPickerNight")
316
- $("#getColorAtSwitchOnDayTimeButton").text("Get again");
365
+ $("#getColorAtSwitchOnDayTimeButton").text("Get current");
317
366
  } else if ($("#node-input-enableDayNightLighting").val() === "temperature") {
318
367
  $("#divEnableDayNightLighting").show();
319
368
  $("#divCCSBoxAtNightLighting").css({ border: "1px solid dimgrey", "border-radius": "12px", padding: "5px" }); // Add little box to better understand the property page
@@ -326,67 +375,65 @@
326
375
  });
327
376
 
328
377
 
329
-
330
- // Autocomplete suggestion with HUE Lights
331
- $("#node-input-name").autocomplete({
332
- minLength: 1,
333
- source: function (request, response) {
334
- $.getJSON("KNXUltimateGetResourcesHUE?rtype=light&nodeID=" + oNodeServerHue.id, (data) => {
335
- response(
336
- $.map(data.devices, function (value, key) {
337
- //alert(JSON.stringify(value) + " "+ key)
338
- var sSearch = value.name;
339
- if (!value.name.includes("I'm still connecting")) {
340
- if (fullSearch(sSearch, request.term)) {
341
- return {
342
- hueDevice: value.id,
343
- value: value.name,
344
- };
345
- } else {
346
- return null;
347
- }
348
- } else {
349
- return {
350
- hueDevice: value.id,
351
- value: value.name,
352
- };
353
- }
354
- })
355
- );
356
- });
357
- },
358
- select: function (event, ui) {
359
- // Distinguish between group of lights an single light.
360
- if (ui.item.value.toLowerCase().startsWith("grouped_light")) {
361
- $("#node-input-hueDevice").val(ui.item.hueDevice + "#grouped_light");
362
- $("#getColorAtSwitchOnDayTimeButton").hide();
363
- $("#getColorAtSwitchOnNightTimeButton").hide();
364
- } else {
365
- $("#node-input-hueDevice").val(ui.item.hueDevice + "#light");
366
- $("#getColorAtSwitchOnDayTimeButton").show();
367
- $("#getColorAtSwitchOnNightTimeButton").show();
368
- }
369
- $("#tabs").show();
370
- },
371
- });
372
-
373
378
  // Get the HUE capabilities to enable/disable UI parts
374
379
  $.getJSON("KNXUltimateGetResourcesHUE?rtype=light&nodeID=" + oNodeServerHue.id, (data) => {
375
380
  data.devices.forEach((element) => {
376
381
  if (element.id === this.hueDevice.split("#")[0] && element.deviceObject !== undefined) {
377
- // Check dimming
378
- if (element.deviceObject.dimming === undefined) {
379
- $("#tabs").tabs("disable", "#tabs-2");
380
- $("#divColorsAtSwitchOn").hide();
381
- $("#divColorCycle").hide();
382
- }
382
+
383
383
  // Check color
384
384
  if (element.deviceObject.color_temperature === undefined) $("#tabs").tabs("disable", "#tabs-3");
385
- if (element.deviceObject.color === undefined) {
385
+ if (element.deviceObject.color === undefined || JSON.stringify(deviceObject.color) === "{}") {
386
386
  $("#tabs").tabs("disable", "#tabs-4");
387
387
  $("#divColorsAtSwitchOn").hide();
388
388
  $("#divColorCycle").hide();
389
+ $("#node-input-specifySwitchOnBrightness").empty().append(
390
+ $("<option>")
391
+ .val("no")
392
+ .text("Last status")
393
+ ).append(
394
+ $("<option>")
395
+ .val("temperature")
396
+ .text("Select temperature and brightness")
397
+ );
398
+ $("#node-input-specifySwitchOnBrightness").val(this.specifySwitchOnBrightness).trigger('change');
399
+
400
+ $("#node-input-enableDayNightLighting").empty().append(
401
+ $("<option>")
402
+ .val("no")
403
+ .text("Last status")
404
+ ).append(
405
+ $("<option>")
406
+ .val("temperature")
407
+ .text("Select temperature and brightness")
408
+ );
409
+ $("#node-input-enableDayNightLighting").val(this.enableDayNightLighting).trigger('change');
410
+ }
411
+
412
+ // Check dimming
413
+ if (element.deviceObject.dimming === undefined) {
414
+ $("#tabs").tabs("disable", "#tabs-2");
415
+ $("#divColorsAtSwitchOn").hide();
416
+ $("#divColorCycle").hide();
417
+ $("#divUpdateKNXBrightnessStatusOnHUEOnOff").hide();
418
+ $("#divCCSBoxAtNightLighting").hide();
419
+ $("#node-input-specifySwitchOnBrightness").val("no");
420
+ $("#node-input-enableDayNightLighting").val("no");
421
+ $("#divMinMaxBrightness").hide();
422
+ $("#node-input-specifySwitchOnBrightness").empty().append(
423
+ $("<option>")
424
+ .val("no")
425
+ .text("Last status")
426
+ )
427
+ $("#node-input-specifySwitchOnBrightness").val(this.specifySwitchOnBrightness).trigger('change');
428
+
429
+ $("#node-input-enableDayNightLighting").empty().append(
430
+ $("<option>")
431
+ .val("no")
432
+ .text("Last status")
433
+ )
389
434
  }
435
+ $("#node-input-enableDayNightLighting").val(this.enableDayNightLighting).trigger('change');
436
+
390
437
  // Check if grouped, to hide/show the "Get current" buttons
391
438
  if (element.deviceObject.type === "grouped_light") {
392
439
  $("#getColorAtSwitchOnDayTimeButton").hide();
@@ -476,7 +523,9 @@
476
523
  this.colorAtSwitchOnNightTime = this.colorAtSwitchOnNightTime.replace("geen", "green"); // Old bug in "geen" property
477
524
  try {
478
525
  json = JSON.parse(this.colorAtSwitchOnDayTime);
479
- } catch (error) { }
526
+ } catch (error) {
527
+ console.log("json = JSON.parse(this.colorAtSwitchOnDayTime) in HTML: " + error.message)
528
+ }
480
529
  if (json !== undefined && json.kelvin !== undefined) {
481
530
  // Kelvin
482
531
  $("#comboTemperatureAtSwitchOn").val(json.kelvin);
@@ -566,6 +615,9 @@
566
615
  }
567
616
  });
568
617
 
618
+
619
+
620
+
569
621
  function rgbHex(red, green, blue, alpha) {
570
622
  const toHex = (red, green, blue, alpha) => ((blue | green << 8 | red << 16) | 1 << 24).toString(16).slice(1) + alpha;
571
623
  const parseCssRgbString = (input) => {
@@ -937,7 +989,7 @@
937
989
  <option value="yes">Yes, and emit KNX telegrams.</option>
938
990
  </select>
939
991
  </div>
940
- <div class="form-row">
992
+ <div class="form-row" id="divUpdateKNXBrightnessStatusOnHUEOnOff">
941
993
  <label style="width:260px;" for="node-input-updateKNXBrightnessStatusOnHUEOnOff">
942
994
  <i class="fa fa-tag"></i> KNX Brightness Status
943
995
  </label>
@@ -1027,19 +1079,21 @@
1027
1079
  </label>
1028
1080
  <input type="text" id="node-input-dimSpeed" placeholder='Default is 5000' style="width:260px">
1029
1081
  </div>
1030
- <div class="form-row">
1031
- <label for="node-input-minDimLevelLight" style="width:260px;">
1032
- <i class="fa fa-clone"></i> Min Dim Brightness
1033
- </label>
1034
- <select id="node-input-minDimLevelLight">
1035
- <option value="useHueLightLevel">Use minimum brightness specified in the HUE light</option>
1036
- </select>
1037
- </div>
1038
- <div class="form-row">
1039
- <label for="node-input-maxDimLevelLight" style="width:260px;">
1040
- <i class="fa fa-clone"></i> Max Dim Brightness
1041
- </label>
1042
- <select id="node-input-maxDimLevelLight"></select>
1082
+ <div id ="divMinMaxBrightness">
1083
+ <div class="form-row">
1084
+ <label for="node-input-minDimLevelLight" style="width:260px;">
1085
+ <i class="fa fa-clone"></i> Min Dim Brightness
1086
+ </label>
1087
+ <select id="node-input-minDimLevelLight">
1088
+ <option value="useHueLightLevel">Use minimum brightness specified in the HUE light</option>
1089
+ </select>
1090
+ </div>
1091
+ <div class="form-row">
1092
+ <label for="node-input-maxDimLevelLight" style="width:260px;">
1093
+ <i class="fa fa-clone"></i> Max Dim Brightness
1094
+ </label>
1095
+ <select id="node-input-maxDimLevelLight"></select>
1096
+ </div>
1043
1097
  </div>
1044
1098
  <div class="form-row">
1045
1099
  <label for="node-input-enableNodePINS" style="width:260px;">
@@ -481,7 +481,7 @@ module.exports = function (RED) {
481
481
  node.handleSendHUE = (_event) => {
482
482
  try {
483
483
  if (_event.id === node.hueDevice) {
484
- if (node.currentHUEDevice === undefined) {
484
+ if (node.currentHUEDevice === undefined || node.serverHue === null || node.serverHue === undefined) {
485
485
  node.setNodeStatusHue({
486
486
  fill: "red",
487
487
  shape: "ring",
@@ -494,9 +494,15 @@ module.exports = function (RED) {
494
494
  // Output the msg to the flow
495
495
  node.send(_event);
496
496
 
497
+ // // DEBUG
498
+ //delete _event.dimming;
499
+ //delete _event.color;
500
+ //delete _event.color_temperature;
501
+ //delete _event.color_temperature_delta;
502
+
497
503
  // As grouped_light doesn't contain all requested properties, i find the first light in the group, and use this below in the code
498
504
  // If the event type is grouped light, and there are missing properties, i infer these missing properties from the first light in the group!
499
- if ((_event.hasOwnProperty("color") || _event.hasOwnProperty("dimming")) && _event.type === 'grouped_light') {
505
+ if ((_event.color !== undefined || _event.dimming !== undefined || _event.color_temperature !== undefined) && _event.type === 'grouped_light') {
500
506
  try {
501
507
  const firstLightInGroup = node.serverHue.getFirstLightInGroup(_event.id);
502
508
  if (firstLightInGroup !== null && firstLightInGroup !== undefined) {
@@ -513,17 +519,19 @@ module.exports = function (RED) {
513
519
  if (_event.hasOwnProperty("on")) {
514
520
  node.updateKNXLightState(_event.on.on);
515
521
  // In case of switch off, set the dim to zero
516
- if (
517
- _event.on.on === false
518
- && (config.updateKNXBrightnessStatusOnHUEOnOff === undefined || config.updateKNXBrightnessStatusOnHUEOnOff === "onhueoff")
519
- ) {
522
+ if (_event.on.on === false && (config.updateKNXBrightnessStatusOnHUEOnOff === undefined || config.updateKNXBrightnessStatusOnHUEOnOff === "onhueoff")) {
520
523
  node.updateKNXBrightnessState(0);
521
524
  //node.currentHUEDevice.dimming.brightness = 0;
525
+ } else if (_event.on.on === true && node.currentHUEDevice.on.on === false) {
526
+ // Turn on always update the dimming KNX Status value as well.
527
+ let brightVal = 100;
528
+ if (node.currentHUEDevice.dimming !== undefined && node.currentHUEDevice.dimming.brightness !== undefined) brightVal = node.currentHUEDevice.dimming.brightness;
529
+ node.updateKNXBrightnessState(brightVal);
522
530
  }
523
531
  node.currentHUEDevice.on.on = _event.on.on;
524
532
  }
525
533
 
526
- if (_event.hasOwnProperty("color")) {
534
+ if (_event.color !== undefined) { // fixed https://github.com/Supergiovane/node-red-contrib-knx-ultimate/issues/287
527
535
  node.updateKNXLightColorState(_event.color);
528
536
  node.currentHUEDevice.color = _event.color;
529
537
  }
@@ -658,33 +666,36 @@ module.exports = function (RED) {
658
666
 
659
667
  node.updateKNXLightColorState = function updateKNXLightColorState(_value, _outputtype = "write") {
660
668
  if (config.GALightColorState !== undefined && config.GALightColorState !== "") {
669
+ if (!_value.hasOwnProperty('xy') || _value.xy.x === undefined) return;
661
670
  const knxMsgPayload = {};
662
671
  knxMsgPayload.topic = config.GALightColorState;
663
672
  knxMsgPayload.dpt = config.dptLightColorState;
664
- knxMsgPayload.payload = hueColorConverter.ColorConverter.xyBriToRgb(
665
- _value.xy.x,
666
- _value.xy.y,
667
- node.currentHUEDevice !== undefined ? node.currentHUEDevice.dimming.brightness : 100,
668
- );
669
- knxMsgPayload.payload = { red: knxMsgPayload.payload.r, green: knxMsgPayload.payload.g, blue: knxMsgPayload.payload.b };
670
- // Send to KNX bus
671
- if (knxMsgPayload.topic !== "" && knxMsgPayload.topic !== undefined) {
672
- if (node.server !== null && node.server !== undefined) {
673
- node.server.writeQueueAdd({
674
- grpaddr: knxMsgPayload.topic,
675
- payload: knxMsgPayload.payload,
676
- dpt: knxMsgPayload.dpt,
677
- outputtype: _outputtype,
678
- nodecallerid: node.id,
679
- });
673
+ try {
674
+ knxMsgPayload.payload = hueColorConverter.ColorConverter.xyBriToRgb(
675
+ _value.xy.x,
676
+ _value.xy.y,
677
+ node.currentHUEDevice !== undefined && node.currentHUEDevice.dimming !== undefined && node.currentHUEDevice.dimming.brightness === undefined ? node.currentHUEDevice.dimming.brightness : 100,
678
+ );
679
+ knxMsgPayload.payload = { red: knxMsgPayload.payload.r, green: knxMsgPayload.payload.g, blue: knxMsgPayload.payload.b };
680
+ // Send to KNX bus
681
+ if (knxMsgPayload.topic !== "" && knxMsgPayload.topic !== undefined) {
682
+ if (node.server !== null && node.server !== undefined) {
683
+ node.server.writeQueueAdd({
684
+ grpaddr: knxMsgPayload.topic,
685
+ payload: knxMsgPayload.payload,
686
+ dpt: knxMsgPayload.dpt,
687
+ outputtype: _outputtype,
688
+ nodecallerid: node.id,
689
+ });
690
+ }
680
691
  }
681
- }
682
- node.setNodeStatusHue({
683
- fill: "blue",
684
- shape: "ring",
685
- text: "HUE->KNX Color",
686
- payload: knxMsgPayload.payload,
687
- });
692
+ node.setNodeStatusHue({
693
+ fill: "blue",
694
+ shape: "ring",
695
+ text: "HUE->KNX Color",
696
+ payload: knxMsgPayload.payload,
697
+ });
698
+ } catch (error) { }
688
699
  }
689
700
  };
690
701
 
@@ -13,7 +13,7 @@
13
13
  namelightsensor: { value: "" },
14
14
  GAlightsensor: { value: "" },
15
15
  dptlightsensor: { value: "" },
16
- readStatusAtStartup: { value: "no" },
16
+ readStatusAtStartup: { value: "yes" },
17
17
 
18
18
  hueDevice: { value: "" }
19
19
  },
@@ -7,7 +7,7 @@ module.exports = function (RED) {
7
7
  node.topic = node.name;
8
8
  node.name = config.name === undefined ? 'Hue' : config.name;
9
9
  node.dpt = '';
10
- node.notifyreadrequest = false;
10
+ node.notifyreadrequest = true;
11
11
  node.notifyreadrequestalsorespondtobus = 'false';
12
12
  node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = '';
13
13
  node.notifyresponse = false;
@@ -23,7 +23,8 @@ module.exports = function (RED) {
23
23
  node.formatnegativevalue = 'leave';
24
24
  node.formatdecimalsvalue = 2;
25
25
  node.hueDevice = config.hueDevice;
26
- node.initializingAtStart = !((config.readStatusAtStartup === undefined || config.readStatusAtStartup === "no"));
26
+ node.initializingAtStart = !((config.readStatusAtStartup === undefined || config.readStatusAtStartup === "yes"));
27
+ node.currentDeviceValue = 0;
27
28
 
28
29
  // Used to call the status update from the config node.
29
30
  node.setNodeStatus = ({ fill, shape, text, payload }) => {
@@ -39,6 +40,19 @@ module.exports = function (RED) {
39
40
 
40
41
  // This function is called by the knx-ultimate config node, to output a msg.payload.
41
42
  node.handleSend = msg => {
43
+
44
+ // Respond to KNX read telegram, by sending the current value as response telegram.
45
+ if (msg.knx.event === "GroupValue_Read") {
46
+ switch (msg.knx.destination) {
47
+ case config.GAlightsensor:
48
+ // To the KNX bus wires
49
+ node.sendResponseToKNX(node.currentDeviceValue);
50
+ break;
51
+ default:
52
+ break;
53
+ }
54
+ }
55
+
42
56
  };
43
57
 
44
58
  node.handleSendHUE = _event => {
@@ -57,6 +71,8 @@ module.exports = function (RED) {
57
71
  knxMsgPayload.payload = _event.light.light_level === 0 ? 0 : Math.round(Math.pow(10, (_event.light.light_level - 1) / 10000));
58
72
  // Send to KNX bus
59
73
  if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id });
74
+ node.currentDeviceValue = knxMsgPayload.payload;
75
+
60
76
  node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX ' + JSON.stringify(knxMsgPayload.payload) + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' });
61
77
 
62
78
  // Setup the output msg
@@ -74,6 +90,20 @@ module.exports = function (RED) {
74
90
  }
75
91
  };
76
92
 
93
+ node.sendResponseToKNX = (_level) => {
94
+ const knxMsgPayload = {};
95
+ knxMsgPayload.topic = config.GAlightsensor;
96
+ knxMsgPayload.dpt = config.dptlightsensor;
97
+
98
+ knxMsgPayload.payload = _level;
99
+ // Send to KNX bus
100
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
101
+ node.server.writeQueueAdd({
102
+ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'response', nodecallerid: node.id,
103
+ });
104
+ }
105
+ };
106
+
77
107
  // On each deploy, unsubscribe+resubscribe
78
108
  if (node.server) {
79
109
  node.server.removeClient(node);
@@ -13,7 +13,7 @@
13
13
  nametemperaturesensor: { value: "" },
14
14
  GAtemperaturesensor: { value: "" },
15
15
  dpttemperaturesensor: { value: "" },
16
- readStatusAtStartup: { value: "no" },
16
+ readStatusAtStartup: { value: "yes" },
17
17
 
18
18
  hueDevice: { value: "" }
19
19
  },
@@ -7,7 +7,7 @@ module.exports = function (RED) {
7
7
  node.topic = node.name;
8
8
  node.name = config.name === undefined ? 'Hue' : config.name;
9
9
  node.dpt = '';
10
- node.notifyreadrequest = false;
10
+ node.notifyreadrequest = true;
11
11
  node.notifyreadrequestalsorespondtobus = 'false';
12
12
  node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = '';
13
13
  node.notifyresponse = false;
@@ -23,7 +23,8 @@ module.exports = function (RED) {
23
23
  node.formatnegativevalue = 'leave';
24
24
  node.formatdecimalsvalue = 2;
25
25
  node.hueDevice = config.hueDevice;
26
- node.initializingAtStart = !((config.readStatusAtStartup === undefined || config.readStatusAtStartup === "no"));
26
+ node.initializingAtStart = !((config.readStatusAtStartup === undefined || config.readStatusAtStartup === "yes"));
27
+ node.currentDeviceValue = 0;
27
28
 
28
29
  // Used to call the status update from the config node.
29
30
  node.setNodeStatus = ({
@@ -43,6 +44,17 @@ module.exports = function (RED) {
43
44
 
44
45
  // This function is called by the knx-ultimate config node, to output a msg.payload.
45
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.GAtemperaturesensor:
51
+ // To the KNX bus wires
52
+ node.sendResponseToKNX(node.currentDeviceValue);
53
+ break;
54
+ default:
55
+ break;
56
+ }
57
+ }
46
58
  };
47
59
 
48
60
  node.handleSendHUE = (_event) => {
@@ -60,6 +72,8 @@ module.exports = function (RED) {
60
72
  grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id,
61
73
  });
62
74
  }
75
+ node.currentDeviceValue = knxMsgPayload.payload;
76
+
63
77
  node.status({ fill: 'green', shape: 'dot', text: `HUE->KNX ${JSON.stringify(knxMsgPayload.payload)} (${new Date().getDate()}, ${new Date().toLocaleTimeString()})` });
64
78
 
65
79
  // Setup the output msg
@@ -79,6 +93,22 @@ module.exports = function (RED) {
79
93
  }
80
94
  };
81
95
 
96
+
97
+ node.sendResponseToKNX = (_level) => {
98
+ const knxMsgPayload = {};
99
+ knxMsgPayload.topic = config.GAtemperaturesensor;
100
+ knxMsgPayload.dpt = config.dpttemperaturesensor;
101
+
102
+ knxMsgPayload.payload = _level;
103
+ // Send to KNX bus
104
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
105
+ node.server.writeQueueAdd({
106
+ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'response', nodecallerid: node.id,
107
+ });
108
+ }
109
+ };
110
+
111
+
82
112
  // On each deploy, unsubscribe+resubscribe
83
113
  if (node.server) {
84
114
  node.server.removeClient(node);
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "engines": {
4
4
  "node": ">=16.0.0"
5
5
  },
6
- "version": "2.2.25",
6
+ "version": "2.2.26",
7
7
  "description": "Control your KNX intallation via Node-Red! Single Node KNX IN/OUT with optional ETS group address importer. Easy to use and highly configurable. With integrated Philips HUE devices control.",
8
8
  "dependencies": {
9
9
  "binary-parser": "2.2.1",