node-red-contrib-knx-ultimate 2.2.24 → 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,22 @@
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
+
18
+ **Version 2.2.25** - November 2023<br/>
19
+ - HUE Light: fixed settings of some behaviour options.<br/>
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/>
21
+ - Fixed some little bugs.<br/>
22
+ - Fixed an issue where in some circumstances, the HUE Light turn off by itself.<br/>
23
+ - WARNING: the new HUE Light options are to be considered **BETA (= in testing with user feedback)**.<br/>
24
+
9
25
  **Version 2.2.24** - November 2023<br/>
10
26
  - HUE Light: added DPT 9.002 for direct kelvin selection, with HUE range (2000K-6535K). There is another DPT 7.600 with the KNX scale (0K-6553K) avaiable.<br/>
11
27
  - NEW: HUE Light: now you can choose the "switch on" behaviour between color and temperature (in Kelvin) + brightness.<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>
@@ -111,29 +111,30 @@ module.exports = (RED) => {
111
111
  });
112
112
  // Connected
113
113
  node.hueManager.on("connected", () => {
114
- node.linkStatus = "connected";
115
- // Start the timer to do initial read.
116
- if (node.timerDoInitialRead !== null) clearTimeout(node.timerDoInitialRead);
117
- node.timerDoInitialRead = setTimeout(() => {
118
- (async () => {
119
- try {
120
- await node.loadResourcesFromHUEBridge();
121
- } catch (error) {
122
- node.linkStatus = "disconnected";
123
- node.nodeClients.forEach((_oClient) => {
124
- setTimeout(() => {
125
- _oClient.setNodeStatusHue({
126
- fill: "red",
127
- shape: "ring",
128
- text: "HUE",
129
- payload: error.message,
130
- });
131
- }, 1000);
132
- });
133
- }
134
- node.startWatchdogTimer();
135
- })();
136
- }, 6000); // 17/02/2020 Do initial read of all nodes requesting initial read
114
+ if (node.linkStatus === "disconnected") {
115
+ node.linkStatus = "connected";
116
+ // Start the timer to do initial read.
117
+ if (node.timerDoInitialRead !== null) clearTimeout(node.timerDoInitialRead);
118
+ node.timerDoInitialRead = setTimeout(() => {
119
+ (async () => {
120
+ try {
121
+ await node.loadResourcesFromHUEBridge();
122
+ } catch (error) {
123
+ node.linkStatus = "disconnected";
124
+ node.nodeClients.forEach((_oClient) => {
125
+ setTimeout(() => {
126
+ _oClient.setNodeStatusHue({
127
+ fill: "red",
128
+ shape: "ring",
129
+ text: "HUE",
130
+ payload: error.message,
131
+ });
132
+ }, 1000);
133
+ });
134
+ }
135
+ })();
136
+ }, 6000); // 17/02/2020 Do initial read of all nodes requesting initial read
137
+ }
137
138
  });
138
139
 
139
140
  node.hueManager.on("disconnected", () => {
@@ -173,6 +174,24 @@ module.exports = (RED) => {
173
174
  // °°°°°° Load ALL resources
174
175
  try {
175
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
+
176
195
  if (node.hueAllResources !== undefined) {
177
196
  node.hueAllRooms = node.hueAllResources.filter((a) => a.type === "room");
178
197
  // Update all KNX State of the nodes with the new hue device values
@@ -204,6 +223,29 @@ module.exports = (RED) => {
204
223
  //})();
205
224
  };
206
225
 
226
+ node.getFirstLightInGroup = function getFirstLightInGroup(_groupID) {
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
+ }
242
+ }
243
+ }
244
+ } catch (error) {
245
+
246
+ }
247
+ };
248
+
207
249
  // Returns the cached devices (node.hueAllResources) by type.
208
250
  node.getResources = function getResources(_rtype) {
209
251
  try {
@@ -423,8 +465,9 @@ module.exports = (RED) => {
423
465
  // °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
424
466
  const serverNode = RED.nodes.getNode(req.query.nodeID); // Retrieve node.id of the config node.
425
467
  if (serverNode === null) {
426
- RED.log.error(`Warn KNXUltimateGetResourcesHUE serverNode is null`);
468
+ RED.log.warn(`Warn KNXUltimateGetResourcesHUE serverNode is null`);
427
469
  res.json({ devices: `serverNode not set` });
470
+ return;
428
471
  }
429
472
  const jRet = serverNode.getResources(req.query.rtype);
430
473
  if (jRet !== undefined) {
@@ -436,6 +479,7 @@ module.exports = (RED) => {
436
479
  } catch (error) {
437
480
  //RED.log.error(`Errore KNXUltimateGetResourcesHUE non gestito ${error.message}`);
438
481
  res.json({ devices: error.message });
482
+ RED.log.error(`Err KNXUltimateGetResourcesHUE: ${error.message}`);
439
483
  // (async () => {
440
484
  // await node.initHUEConnection();
441
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" }
@@ -1244,7 +1244,7 @@ return msg;`,
1244
1244
  devicename: msg.devicename,
1245
1245
  });
1246
1246
  input.handleSend(msg);
1247
- } else if (input.topic == _dest) {
1247
+ } else if (input.topic === _dest) {
1248
1248
  // 04/02/2020 Watchdog implementation
1249
1249
  if (input.hasOwnProperty("isWatchDog")) {
1250
1250
  // Is a watchdog node
@@ -1333,7 +1333,7 @@ return msg;`,
1333
1333
  devicename: msg.devicename,
1334
1334
  });
1335
1335
  input.handleSend(msg);
1336
- } else if (input.topic == _dest) {
1336
+ } else if (input.topic === _dest) {
1337
1337
  // 04/02/2020 Watchdog implementation
1338
1338
  if (input.hasOwnProperty("isWatchDog")) {
1339
1339
  // Is a watchdog node
@@ -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,14 +100,37 @@
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
106
107
  var oNodeServerHue = RED.nodes.node($("#node-input-serverHue").val()); // Store the config-node
107
108
 
109
+ // TIMER BLINK ####################################################
110
+ let blinkStatus = 2;
111
+ let timerBlinkBackground;
112
+ function blinkBackground(_elementIDwithHashAtTheBeginning) {
113
+ if (timerBlinkBackground !== undefined) clearInterval(timerBlinkBackground);
114
+ timerBlinkBackground = setInterval(() => {
115
+ if (isEven(blinkStatus)) $(_elementIDwithHashAtTheBeginning).css("background-color", "lightgreen");
116
+ if (!isEven(blinkStatus)) $(_elementIDwithHashAtTheBeginning).css("background-color", "");
117
+ blinkStatus += 1;
118
+ if (blinkStatus >= 14) {
119
+ clearInterval(timerBlinkBackground);
120
+ blinkStatus = 2;
121
+ $(_elementIDwithHashAtTheBeginning).css("background-color", "");
122
+ }
123
+ }, 100);
124
+ }
125
+ function isEven(n) {
126
+ return (n % 2 == 0);
127
+ }
128
+ // ################################################################
129
+
130
+
108
131
  $("#tabs").tabs(); // Tabs gestione KNX
109
132
 
133
+
110
134
  // 19/02/2020 Used to get the server sooner als deploy.
111
135
  $("#node-input-server").change(function () {
112
136
  try {
@@ -148,7 +172,7 @@
148
172
  }
149
173
  });
150
174
  // Eval
151
- const format = "node." + _destinationWidget.replace("#node-input-", "");
175
+ const format = "this." + _destinationWidget.replace("#node-input-", "");
152
176
  try {
153
177
  if (format !== undefined) $(_destinationWidget).val(eval(format).toString());
154
178
  } catch (error) { }
@@ -243,6 +267,52 @@
243
267
  getGroupAddress("#node-input-GALightKelvinState", "#node-input-nameLightKelvinState", "#node-input-dptLightKelvinState", " 9.002");
244
268
 
245
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
+
246
316
  // Show/Hide the div of the color at swich on
247
317
  if (this.specifySwitchOnBrightness === "yes") {
248
318
  $("#divColorsAtSwitchOn").show();
@@ -259,6 +329,7 @@
259
329
  if ($("#node-input-specifySwitchOnBrightness").val() === "yes") {
260
330
  $("#divColorsAtSwitchOn").show();
261
331
  $("#divTemperatureAtSwitchOn").hide();
332
+ blinkBackground("#colorPickerDay");
262
333
  } else if ($("#node-input-specifySwitchOnBrightness").val() === "temperature") {
263
334
  $("#divColorsAtSwitchOn").hide();
264
335
  $("#divTemperatureAtSwitchOn").show();
@@ -290,6 +361,8 @@
290
361
  $("#divCCSBoxAtNightLighting").css({ border: "1px solid dimgrey", "border-radius": "12px", padding: "5px" }); // Add little box to better understand the property page
291
362
  $("#divColorsAtSwitchOnNightTime").show();
292
363
  $("#divTemperatureAtSwitchOnNightTime").hide();
364
+ blinkBackground("#colorPickerNight")
365
+ $("#getColorAtSwitchOnDayTimeButton").text("Get current");
293
366
  } else if ($("#node-input-enableDayNightLighting").val() === "temperature") {
294
367
  $("#divEnableDayNightLighting").show();
295
368
  $("#divCCSBoxAtNightLighting").css({ border: "1px solid dimgrey", "border-radius": "12px", padding: "5px" }); // Add little box to better understand the property page
@@ -302,67 +375,65 @@
302
375
  });
303
376
 
304
377
 
305
-
306
- // Autocomplete suggestion with HUE Lights
307
- $("#node-input-name").autocomplete({
308
- minLength: 1,
309
- source: function (request, response) {
310
- $.getJSON("KNXUltimateGetResourcesHUE?rtype=light&nodeID=" + oNodeServerHue.id, (data) => {
311
- response(
312
- $.map(data.devices, function (value, key) {
313
- //alert(JSON.stringify(value) + " "+ key)
314
- var sSearch = value.name;
315
- if (!value.name.includes("I'm still connecting")) {
316
- if (fullSearch(sSearch, request.term)) {
317
- return {
318
- hueDevice: value.id,
319
- value: value.name,
320
- };
321
- } else {
322
- return null;
323
- }
324
- } else {
325
- return {
326
- hueDevice: value.id,
327
- value: value.name,
328
- };
329
- }
330
- })
331
- );
332
- });
333
- },
334
- select: function (event, ui) {
335
- // Distinguish between group of lights an single light.
336
- if (ui.item.value.toLowerCase().startsWith("grouped_light")) {
337
- $("#node-input-hueDevice").val(ui.item.hueDevice + "#grouped_light");
338
- $("#getColorAtSwitchOnDayTimeButton").hide();
339
- $("#getColorAtSwitchOnNightTimeButton").hide();
340
- } else {
341
- $("#node-input-hueDevice").val(ui.item.hueDevice + "#light");
342
- $("#getColorAtSwitchOnDayTimeButton").show();
343
- $("#getColorAtSwitchOnNightTimeButton").show();
344
- }
345
- $("#tabs").show();
346
- },
347
- });
348
-
349
378
  // Get the HUE capabilities to enable/disable UI parts
350
379
  $.getJSON("KNXUltimateGetResourcesHUE?rtype=light&nodeID=" + oNodeServerHue.id, (data) => {
351
380
  data.devices.forEach((element) => {
352
381
  if (element.id === this.hueDevice.split("#")[0] && element.deviceObject !== undefined) {
353
- // Check dimming
354
- if (element.deviceObject.dimming === undefined) {
355
- $("#tabs").tabs("disable", "#tabs-2");
356
- $("#divColorsAtSwitchOn").hide();
357
- $("#divColorCycle").hide();
358
- }
382
+
359
383
  // Check color
360
384
  if (element.deviceObject.color_temperature === undefined) $("#tabs").tabs("disable", "#tabs-3");
361
- if (element.deviceObject.color === undefined) {
385
+ if (element.deviceObject.color === undefined || JSON.stringify(deviceObject.color) === "{}") {
362
386
  $("#tabs").tabs("disable", "#tabs-4");
363
387
  $("#divColorsAtSwitchOn").hide();
364
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');
365
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
+ )
434
+ }
435
+ $("#node-input-enableDayNightLighting").val(this.enableDayNightLighting).trigger('change');
436
+
366
437
  // Check if grouped, to hide/show the "Get current" buttons
367
438
  if (element.deviceObject.type === "grouped_light") {
368
439
  $("#getColorAtSwitchOnDayTimeButton").hide();
@@ -385,10 +456,7 @@
385
456
  $.getJSON(sQuery + "?id=" + $("#node-input-hueDevice").val().split("#")[0], (data) => {
386
457
  $("#node-input-colorAtSwitchOnDayTime").val(data);
387
458
  $("#colorPickerDay").val(data);
388
- $("#colorPickerDay").css("background-color", "lightgreen");
389
- setTimeout(() => {
390
- $("#colorPickerDay").css("background-color", "");
391
- }, 500);
459
+ blinkBackground("#colorPickerDay")
392
460
  $("#getColorAtSwitchOnDayTimeButton").text("Get again");
393
461
  });
394
462
  });
@@ -402,10 +470,7 @@
402
470
  $.getJSON(sQuery + "?id=" + $("#node-input-hueDevice").val().split("#")[0], (data) => {
403
471
  $("#node-input-colorAtSwitchOnNightTime").val(data);
404
472
  $("#colorPickerNight").val(data);
405
- $("#colorPickerNight").css("background-color", "lightgreen");
406
- setTimeout(() => {
407
- $("#colorPickerNight").css("background-color", "");
408
- }, 500);
473
+ blinkBackground("#colorPickerNight")
409
474
  $("#getColorAtSwitchOnNightTimeButton").text("Get again");
410
475
  });
411
476
  });
@@ -458,7 +523,9 @@
458
523
  this.colorAtSwitchOnNightTime = this.colorAtSwitchOnNightTime.replace("geen", "green"); // Old bug in "geen" property
459
524
  try {
460
525
  json = JSON.parse(this.colorAtSwitchOnDayTime);
461
- } catch (error) { }
526
+ } catch (error) {
527
+ console.log("json = JSON.parse(this.colorAtSwitchOnDayTime) in HTML: " + error.message)
528
+ }
462
529
  if (json !== undefined && json.kelvin !== undefined) {
463
530
  // Kelvin
464
531
  $("#comboTemperatureAtSwitchOn").val(json.kelvin);
@@ -548,6 +615,9 @@
548
615
  }
549
616
  });
550
617
 
618
+
619
+
620
+
551
621
  function rgbHex(red, green, blue, alpha) {
552
622
  const toHex = (red, green, blue, alpha) => ((blue | green << 8 | red << 16) | 1 << 24).toString(16).slice(1) + alpha;
553
623
  const parseCssRgbString = (input) => {
@@ -919,12 +989,12 @@
919
989
  <option value="yes">Yes, and emit KNX telegrams.</option>
920
990
  </select>
921
991
  </div>
922
- <div class="form-row">
992
+ <div class="form-row" id="divUpdateKNXBrightnessStatusOnHUEOnOff">
923
993
  <label style="width:260px;" for="node-input-updateKNXBrightnessStatusOnHUEOnOff">
924
994
  <i class="fa fa-tag"></i> KNX Brightness Status
925
995
  </label>
926
996
  <select id="node-input-updateKNXBrightnessStatusOnHUEOnOff">
927
- <option value="onhueoff">When HUE light is Off, send 0% (Default KNX behaviour)</option>
997
+ <option value="onhueoff">When HUE light is Off send 0%. When HUE On, restore previous value (Default KNX behaviour)</option>
928
998
  <option value="no">Leave as is (default HUE behaviour)</option>
929
999
  </select>
930
1000
  </div>
@@ -1009,19 +1079,21 @@
1009
1079
  </label>
1010
1080
  <input type="text" id="node-input-dimSpeed" placeholder='Default is 5000' style="width:260px">
1011
1081
  </div>
1012
- <div class="form-row">
1013
- <label for="node-input-minDimLevelLight" style="width:260px;">
1014
- <i class="fa fa-clone"></i> Min Dim Brightness
1015
- </label>
1016
- <select id="node-input-minDimLevelLight">
1017
- <option value="useHueLightLevel">Use minimum brightness specified in the HUE light</option>
1018
- </select>
1019
- </div>
1020
- <div class="form-row">
1021
- <label for="node-input-maxDimLevelLight" style="width:260px;">
1022
- <i class="fa fa-clone"></i> Max Dim Brightness
1023
- </label>
1024
- <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>
1025
1097
  </div>
1026
1098
  <div class="form-row">
1027
1099
  <label for="node-input-enableNodePINS" style="width:260px;">
@@ -1103,9 +1175,9 @@ Start typing in the GA field, the name or group address of your KNX device, the
1103
1175
 
1104
1176
  | Property | Description |
1105
1177
  | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1106
- | KNX Brightness Status | Updates the KNX brightness group address status, whenever the HUE lamp is switched ON/OFF. The options are **When HUE light is Off, send 0% (Default KNX behaviour)** and **Leave as is (default HUE behaviour)** |
1107
- | Switch on behaviour | It sets the behaviour of your lights when switched on. You can choose from differents behaviours.<br/>**Select color:** the light will be switched on with the color of your choice.<br/>**Select temperature and brightness:** the light will be switched on with the temperature (Kelvin) and brightness (0-100) of your choice.<br>**Last status:** the light will be switched on in the last status. |
1108
- | Night Lighting | It allows to set a particular light color/brightness at nighttime. If **checked**, a further set of option shows up and the light changes the behaviour based on these options, everytime you switch the light _on_. |
1178
+ | KNX Brightness Status | Updates the KNX brightness group address status, whenever the HUE lamp is switched ON/OFF. The options are **When HUE light is Off send 0%. When HUE On, restore previous value (Default KNX behaviour)** and **Leave as is (default HUE behaviour)**. If you have KNX dimmer with brightness status, like MDT, the suggested option is ***When HUE light is Off send 0%. When HUE On, restore previous value (Default KNX behaviour)*** |
1179
+ | Switch on behaviour | It sets the behaviour of your lights when switched on. You can choose from differents behaviours.<br/>**Select color:** the light will be switched on with the color of your choice. To change color, just CLICK on the color selector (under the *Select color* control).<br/>**Select temperature and brightness:** the light will be switched on with the temperature (Kelvin) and brightness (0-100) of your choice.<br>**Last status:** the light will be switched on in the last status. |
1180
+ | Night Lighting | It allows to set a particular light color/brightness at nighttime. The options are the same as the daytime. You could select either a temperature/brightness or color. A cozy temperature of 2700 Kelvin, with a brightness of 10% or 20%, is a good choice for bathroom's night light.|
1109
1181
  | Day/Night | Select the group address used to set the day/night behaviour. The group address value is _true_ if daytime, _false_ if nighttime. |
1110
1182
  | Invert day/night values | Invert the values of _Day/Night_ group address. Default value is **unchecked**. |
1111
1183
  | Dim Speed (ms) | The dimming speed, in Milliseconds. This applies to the **light** and also to the **tunable white** dimming datapoints. It' calculated from 0% to 100%. |
@@ -15,7 +15,7 @@ module.exports = function (RED) {
15
15
  node.name = config.name === undefined ? "Hue" : config.name;
16
16
  node.outputtopic = node.name;
17
17
  node.dpt = "";
18
- node.notifyreadrequest = false;
18
+ node.notifyreadrequest = true;
19
19
  node.notifyreadrequestalsorespondtobus = "false";
20
20
  node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = "";
21
21
  node.notifyresponse = false;
@@ -335,6 +335,36 @@ module.exports = function (RED) {
335
335
  default:
336
336
  break;
337
337
  }
338
+
339
+ // I must respond to query requests (read request) sent from the KNX BUS
340
+
341
+ if (msg.knx.event === "GroupValue_Read" && node.currentHUEDevice !== undefined) {
342
+ let ret;
343
+ switch (msg.knx.destination) {
344
+ case config.GALightState:
345
+ ret = node.currentHUEDevice.on.on;
346
+ if (ret !== undefined) node.updateKNXLightState(ret, "response");
347
+ break;
348
+ case config.GALightColorState:
349
+ ret = node.currentHUEDevice.color.xy;
350
+ if (ret !== undefined) node.updateKNXLightColorState(node.currentHUEDevice.color, "response");
351
+ break;
352
+ case config.GALightHSVState:
353
+ ret = node.currentHUEDevice.color_temperature.mirek;
354
+ if (ret !== undefined) node.updateKNXLightHSVState(ret, "response");
355
+ break;
356
+ case config.GALightBrightnessState:
357
+ ret = node.currentHUEDevice.dimming.brightness;
358
+ if (ret !== undefined) node.updateKNXBrightnessState(ret, "response");
359
+ break;
360
+ case config.GALightKelvinState:
361
+ ret = node.currentHUEDevice.color_temperature.mirek;
362
+ if (ret !== undefined) node.updateKNXLightKelvinState(ret, "response");
363
+ break;
364
+ default:
365
+ break;
366
+ }
367
+ }
338
368
  } catch (error) {
339
369
  node.status({
340
370
  fill: "red",
@@ -451,7 +481,7 @@ module.exports = function (RED) {
451
481
  node.handleSendHUE = (_event) => {
452
482
  try {
453
483
  if (_event.id === node.hueDevice) {
454
- if (node.currentHUEDevice === undefined) {
484
+ if (node.currentHUEDevice === undefined || node.serverHue === null || node.serverHue === undefined) {
455
485
  node.setNodeStatusHue({
456
486
  fill: "red",
457
487
  shape: "ring",
@@ -464,25 +494,48 @@ module.exports = function (RED) {
464
494
  // Output the msg to the flow
465
495
  node.send(_event);
466
496
 
497
+ // // DEBUG
498
+ //delete _event.dimming;
499
+ //delete _event.color;
500
+ //delete _event.color_temperature;
501
+ //delete _event.color_temperature_delta;
502
+
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
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!
505
+ if ((_event.color !== undefined || _event.dimming !== undefined || _event.color_temperature !== undefined) && _event.type === 'grouped_light') {
506
+ try {
507
+ const firstLightInGroup = node.serverHue.getFirstLightInGroup(_event.id);
508
+ if (firstLightInGroup !== null && firstLightInGroup !== undefined) {
509
+ if (_event.color === undefined || Object.keys(_event.color).length === 0) {
510
+ _event.color = firstLightInGroup.color;
511
+ }
512
+ if (_event.color_temperature === undefined || Object.keys(_event.color_temperature).length === 0) {
513
+ _event.color_temperature = firstLightInGroup.color_temperature;
514
+ }
515
+ }
516
+ } catch (error) { }
517
+ }
518
+
467
519
  if (_event.hasOwnProperty("on")) {
468
520
  node.updateKNXLightState(_event.on.on);
469
521
  // In case of switch off, set the dim to zero
470
- if (
471
- _event.on.on === false
472
- && (config.updateKNXBrightnessStatusOnHUEOnOff === undefined || config.updateKNXBrightnessStatusOnHUEOnOff === "onhueoff")
473
- ) {
522
+ if (_event.on.on === false && (config.updateKNXBrightnessStatusOnHUEOnOff === undefined || config.updateKNXBrightnessStatusOnHUEOnOff === "onhueoff")) {
474
523
  node.updateKNXBrightnessState(0);
475
- node.currentHUEDevice.dimming.brightness = 0;
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);
476
530
  }
477
531
  node.currentHUEDevice.on.on = _event.on.on;
478
532
  }
479
- if (_event.hasOwnProperty("color")) {
480
- if (_event.type !== 'grouped_light') {
481
- // In grouped lights, there is group color, but each light has it's own color.
482
- node.updateKNXLightColorState(_event.color);
483
- node.currentHUEDevice.color = _event.color;
484
- }
533
+
534
+ if (_event.color !== undefined) { // fixed https://github.com/Supergiovane/node-red-contrib-knx-ultimate/issues/287
535
+ node.updateKNXLightColorState(_event.color);
536
+ node.currentHUEDevice.color = _event.color;
485
537
  }
538
+
486
539
  if (_event.hasOwnProperty("dimming") && _event.dimming.brightness !== undefined) {
487
540
  // Every once on a time, the light transmit the brightness value of 0.39.
488
541
  // To avoid wrongly turn light state on, exit
@@ -521,7 +574,7 @@ module.exports = function (RED) {
521
574
  };
522
575
 
523
576
  // Leave the name after "function", to avoid <anonymous function> in the stack trace, in caso of errors.
524
- node.updateKNXBrightnessState = function updateKNXBrightnessState(_value) {
577
+ node.updateKNXBrightnessState = function updateKNXBrightnessState(_value, _outputtype = "write") {
525
578
  if (config.GALightBrightnessState !== undefined && config.GALightBrightnessState !== "") {
526
579
  const knxMsgPayload = {};
527
580
  knxMsgPayload.topic = config.GALightBrightnessState;
@@ -529,13 +582,15 @@ module.exports = function (RED) {
529
582
  knxMsgPayload.payload = _value;
530
583
  // Send to KNX bus
531
584
  if (knxMsgPayload.topic !== "" && knxMsgPayload.topic !== undefined) {
532
- node.server.writeQueueAdd({
533
- grpaddr: knxMsgPayload.topic,
534
- payload: knxMsgPayload.payload,
535
- dpt: knxMsgPayload.dpt,
536
- outputtype: "write",
537
- nodecallerid: node.id,
538
- });
585
+ if (node.server !== null && node.server !== undefined) {
586
+ node.server.writeQueueAdd({
587
+ grpaddr: knxMsgPayload.topic,
588
+ payload: knxMsgPayload.payload,
589
+ dpt: knxMsgPayload.dpt,
590
+ outputtype: _outputtype,
591
+ nodecallerid: node.id,
592
+ });
593
+ }
539
594
  }
540
595
  node.setNodeStatusHue({
541
596
  fill: "blue",
@@ -546,7 +601,7 @@ module.exports = function (RED) {
546
601
  }
547
602
  };
548
603
 
549
- node.updateKNXLightState = function updateKNXBrightnessState(_value) {
604
+ node.updateKNXLightState = function updateKNXLightState(_value, _outputtype = "write") {
550
605
  try {
551
606
  const knxMsgPayload = {};
552
607
  knxMsgPayload.topic = config.GALightState;
@@ -557,13 +612,15 @@ module.exports = function (RED) {
557
612
  // Check not to have already sent the value
558
613
  // Send to KNX bus
559
614
  if (knxMsgPayload.topic !== "" && knxMsgPayload.topic !== undefined) {
560
- node.server.writeQueueAdd({
561
- grpaddr: knxMsgPayload.topic,
562
- payload: knxMsgPayload.payload,
563
- dpt: knxMsgPayload.dpt,
564
- outputtype: "write",
565
- nodecallerid: node.id,
566
- });
615
+ if (node.server !== null && node.server !== undefined) {
616
+ node.server.writeQueueAdd({
617
+ grpaddr: knxMsgPayload.topic,
618
+ payload: knxMsgPayload.payload,
619
+ dpt: knxMsgPayload.dpt,
620
+ outputtype: _outputtype,
621
+ nodecallerid: node.id,
622
+ });
623
+ }
567
624
  }
568
625
  node.setNodeStatusHue({
569
626
  fill: "blue",
@@ -577,7 +634,7 @@ module.exports = function (RED) {
577
634
  }
578
635
  };
579
636
 
580
- node.updateKNXLightHSVState = function updateKNXLightState(_value) {
637
+ node.updateKNXLightHSVState = function updateKNXLightHSVState(_value, _outputtype = "write") {
581
638
  if (config.GALightHSVState !== undefined && config.GALightHSVState !== "") {
582
639
  const knxMsgPayload = {};
583
640
  knxMsgPayload.topic = config.GALightHSVState;
@@ -588,13 +645,15 @@ module.exports = function (RED) {
588
645
  }
589
646
  // Send to KNX bus
590
647
  if (knxMsgPayload.topic !== "" && knxMsgPayload.topic !== undefined) {
591
- node.server.writeQueueAdd({
592
- grpaddr: knxMsgPayload.topic,
593
- payload: knxMsgPayload.payload,
594
- dpt: knxMsgPayload.dpt,
595
- outputtype: "write",
596
- nodecallerid: node.id,
597
- });
648
+ if (node.server !== null && node.server !== undefined) {
649
+ node.server.writeQueueAdd({
650
+ grpaddr: knxMsgPayload.topic,
651
+ payload: knxMsgPayload.payload,
652
+ dpt: knxMsgPayload.dpt,
653
+ outputtype: _outputtype,
654
+ nodecallerid: node.id,
655
+ });
656
+ }
598
657
  }
599
658
  node.setNodeStatusHue({
600
659
  fill: "blue",
@@ -605,36 +664,42 @@ module.exports = function (RED) {
605
664
  }
606
665
  };
607
666
 
608
- node.updateKNXLightColorState = function updateKNXLightColorState(_value) {
667
+ node.updateKNXLightColorState = function updateKNXLightColorState(_value, _outputtype = "write") {
609
668
  if (config.GALightColorState !== undefined && config.GALightColorState !== "") {
669
+ if (!_value.hasOwnProperty('xy') || _value.xy.x === undefined) return;
610
670
  const knxMsgPayload = {};
611
671
  knxMsgPayload.topic = config.GALightColorState;
612
672
  knxMsgPayload.dpt = config.dptLightColorState;
613
- knxMsgPayload.payload = hueColorConverter.ColorConverter.xyBriToRgb(
614
- _value.xy.x,
615
- _value.xy.y,
616
- node.currentHUEDevice !== undefined ? node.currentHUEDevice.dimming.brightness : 100,
617
- );
618
- // Send to KNX bus
619
- if (knxMsgPayload.topic !== "" && knxMsgPayload.topic !== undefined) {
620
- node.server.writeQueueAdd({
621
- grpaddr: knxMsgPayload.topic,
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
+ }
691
+ }
692
+ node.setNodeStatusHue({
693
+ fill: "blue",
694
+ shape: "ring",
695
+ text: "HUE->KNX Color",
622
696
  payload: knxMsgPayload.payload,
623
- dpt: knxMsgPayload.dpt,
624
- outputtype: "write",
625
- nodecallerid: node.id,
626
697
  });
627
- }
628
- node.setNodeStatusHue({
629
- fill: "blue",
630
- shape: "ring",
631
- text: "HUE->KNX Color",
632
- payload: knxMsgPayload.payload,
633
- });
698
+ } catch (error) { }
634
699
  }
635
700
  };
636
701
 
637
- node.updateKNXLightKelvinState = function updateKNXLightKelvinState(_value) {
702
+ node.updateKNXLightKelvinState = function updateKNXLightKelvinState(_value, _outputtype = "write") {
638
703
  if (config.GALightKelvinState !== undefined && config.GALightKelvinState !== "") {
639
704
  const knxMsgPayload = {};
640
705
  knxMsgPayload.topic = config.GALightKelvinState;
@@ -646,13 +711,15 @@ module.exports = function (RED) {
646
711
  }
647
712
  // Send to KNX bus
648
713
  if (knxMsgPayload.topic !== "" && knxMsgPayload.topic !== undefined) {
649
- node.server.writeQueueAdd({
650
- grpaddr: knxMsgPayload.topic,
651
- payload: knxMsgPayload.payload,
652
- dpt: knxMsgPayload.dpt,
653
- outputtype: "write",
654
- nodecallerid: node.id,
655
- });
714
+ if (node.server !== null && node.server !== undefined) {
715
+ node.server.writeQueueAdd({
716
+ grpaddr: knxMsgPayload.topic,
717
+ payload: knxMsgPayload.payload,
718
+ dpt: knxMsgPayload.dpt,
719
+ outputtype: _outputtype,
720
+ nodecallerid: node.id,
721
+ });
722
+ }
656
723
 
657
724
  node.setNodeStatusHue({
658
725
  fill: "blue",
@@ -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);
@@ -104,7 +104,7 @@ class classHUE extends EventEmitter {
104
104
  try {
105
105
  this.Connect();
106
106
  } catch (error) { }
107
- }, 300000);
107
+ }, 10 * (60 * 1000)); // 10 minutes
108
108
  };
109
109
 
110
110
  // Handle the send queue
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "engines": {
4
4
  "node": ">=16.0.0"
5
5
  },
6
- "version": "2.2.24",
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",
@@ -19,6 +19,7 @@
19
19
  "simple-get": "4.0.1",
20
20
  "xml2js": "0.6.0"
21
21
  },
22
+
22
23
  "node-red": {
23
24
  "version": ">=2.0.0",
24
25
  "nodes": {