node-red-contrib-knx-ultimate 2.2.3 → 2.2.4

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,9 @@
6
6
 
7
7
  # CHANGELOG
8
8
 
9
+ <b>Version 2.2.4</b> - October 2023<br/>
10
+ - HUE Light: fixed some status hiccups and better handling of async hue bridge functions.<br/>
11
+ </p>
9
12
  <b>Version 2.2.3</b> - October 2023<br/>
10
13
  - HUE Light: Again, rewrite of the DIM function to get rid of the dimming_delta.<br/>
11
14
  </p>
@@ -1,3 +1,4 @@
1
+ /* eslint-disable no-lonely-if */
1
2
  /* eslint-disable no-param-reassign */
2
3
  /* eslint-disable no-inner-declarations */
3
4
  /* eslint-disable max-len */
@@ -54,6 +55,7 @@ module.exports = (RED) => {
54
55
  const node = this;
55
56
  node.host = config.host;
56
57
  node.nodeClients = []; // Stores the registered clients
58
+ node.nodeClientsAwaitingInit = []; // Stores the nodes client to be initialized
57
59
  node.loglevel = config.loglevel !== undefined ? config.loglevel : "error"; // 18/02/2020 Loglevel default error
58
60
  node.sysLogger = null;
59
61
  node.hueAllResources = null;
@@ -108,18 +110,20 @@ module.exports = (RED) => {
108
110
  node.hueAllResources = await node.hueManager.hueApiV2.get("/resource");
109
111
  node.hueAllRooms = node.hueAllResources.filter((a) => a.type === "room");
110
112
  // Update all KNX State of the nodes with the new hue device values
113
+ if (node.nodeClientsAwaitingInit !== undefined) {
114
+ // Because the whole process is async, if the function await node.hueManager.hueApiV2.get("/resource") has not jet been called or is late,
115
+ // the node/nodes belonging to this server, has been previously added to the nodeClientsAwaitingInit list.
116
+ node.nodeClientsAwaitingInit.forEach((nodeClient) => {
117
+ node.nodeClients.push(nodeClient);
118
+ });
119
+ node.nodeClientsAwaitingInit = [];
120
+ }
111
121
  node.nodeClients.forEach((nodeClient) => {
112
122
  if (nodeClient.hueDevice !== undefined) {
113
123
  const oHUEDevice = node.hueAllResources.filter((a) => a.id === nodeClient.hueDevice)[0];
114
124
  if (oHUEDevice !== undefined) {
115
125
  nodeClient.currentHUEDevice = oHUEDevice;
116
126
  nodeClient.handleSendHUE(oHUEDevice);
117
- // for (const [key, value] of Object.entries(oHUEDevice)) {
118
- // // Update KNX State
119
- // const oProperty = { id: oHUEDevice.id };
120
- // oProperty[key] = value;
121
- // nodeClient.handleSendHUE(oProperty);
122
- // }
123
127
  }
124
128
  }
125
129
  });
@@ -262,20 +266,29 @@ module.exports = (RED) => {
262
266
  };
263
267
 
264
268
  node.addClient = (_Node) => {
265
- // Check if node already exists
266
- if (node.nodeClients.filter((x) => x.id === _Node.id).length === 0) {
267
- // Add _Node to the clients array
268
- _Node.setNodeStatusHue({
269
- fill: "grey",
270
- shape: "ring",
271
- text: "Hue initialized.",
272
- });
273
- node.nodeClients.push(_Node);
274
- // Update the node hue device, as soon as a node register itself to hue-config nodeClients
275
- if (node.hueAllResources !== null) { // At first start, due to the async method for retrieving hueAllResources, hueAllResources is still null. The first start is handled in node.hueManager.on("connected")
269
+ // Update the node hue device, as soon as a node register itself to hue-config nodeClients
270
+ if (node.hueAllResources !== null) {
271
+ if (node.nodeClients.filter((x) => x.id === _Node.id).length === 0) { // At first start, due to the async method for retrieving hueAllResources, hueAllResources is still null. The first start is handled in node.hueManager.on("connected")
276
272
  const oHUEDevice = node.hueAllResources.filter((a) => a.id === _Node.hueDevice)[0];
277
273
  _Node.currentHUEDevice = oHUEDevice;
278
274
  if (oHUEDevice !== undefined) _Node.handleSendHUE(oHUEDevice);
275
+ node.nodeClients.push(_Node);
276
+ // Add _Node to the clients array
277
+ _Node.setNodeStatusHue({
278
+ fill: "green",
279
+ shape: "ring",
280
+ text: "Ready",
281
+ });
282
+ }
283
+ } else {
284
+ if (node.nodeClientsAwaitingInit.filter((x) => x.id === _Node.id).length === 0) {
285
+ // Put the node in the waiting list
286
+ node.nodeClientsAwaitingInit.push(_Node);
287
+ _Node.setNodeStatusHue({
288
+ fill: "grey",
289
+ shape: "dot",
290
+ text: "Awaiting HUE Resources",
291
+ });
279
292
  }
280
293
  }
281
294
  };
@@ -33,7 +33,7 @@ module.exports = function (RED) {
33
33
  node.setNodeStatusHue = ({
34
34
  fill, shape, text, payload,
35
35
  }) => {
36
- if (payload === undefined) return;
36
+ if (payload === undefined) payload = '';
37
37
  const dDate = new Date();
38
38
  payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString();
39
39
  node.status({ fill, shape, text: `${text} ${payload} (${dDate.getDate()}, ${dDate.toLocaleTimeString()})` });
@@ -37,7 +37,7 @@ module.exports = function (RED) {
37
37
  node.setNodeStatusHue = ({
38
38
  fill, shape, text, payload,
39
39
  }) => {
40
- if (payload === undefined) return;
40
+ if (payload === undefined) payload = '';
41
41
  const dDate = new Date();
42
42
  payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString();
43
43
  node.status({ fill, shape, text: `${text} ${payload} (${dDate.getDate()}, ${dDate.toLocaleTimeString()})` });
@@ -1,4 +1,24 @@
1
1
  <script type="text/javascript">
2
+ RED.events.on('nodes:add', function (node) {
3
+ if (node.type === 'uibuilder') {
4
+ // Keep a list of uib nodes in the editor
5
+ // may be different to the deployed list
6
+ editorInstances[node.id] = node.url
7
+ // -- IF uibuilderInstances <> editorInstances THEN there are undeployed instances. --
8
+ }
9
+ })
10
+ RED.events.on('nodes:change', function (node) {
11
+ if (node.type === 'uibuilder') {
12
+ mylog('nodes:change:', node)
13
+ editorInstances[node.id] = node.url
14
+ }
15
+ })
16
+ RED.events.on('nodes:remove', function (node) {
17
+ if (node.type === 'uibuilder') {
18
+ mylog('>> nodes:remove >>', node)
19
+ delete editorInstances[node.id]
20
+ }
21
+ })
2
22
  RED.nodes.registerType("knxUltimateHueLight", {
3
23
  category: "KNX Ultimate",
4
24
  color: "#C0C7E9",
@@ -486,15 +506,15 @@
486
506
  $("#getColorAtSwitchOnNightTimeButton").text("Get current");
487
507
  });
488
508
  });
489
-
509
+
490
510
  // Fill options for minDimLevel and maxDimLevel
491
511
  for (let index = 0; index <= 90; index++) {
492
- if (index === 0){
512
+ if (index === 0) {
493
513
  $("#node-input-minDimLevelLight").append($("<option>").val(index).text(index.toString() + "% (Switch Off)"));
494
- }else{
514
+ } else {
495
515
  $("#node-input-minDimLevelLight").append($("<option>").val(index).text(index.toString() + "%"));
496
516
  }
497
-
517
+
498
518
  }
499
519
  $("#node-input-minDimLevelLight").val(node.minDimLevelLight);
500
520
  for (let index = 100; index >= 10; index--) {
@@ -41,22 +41,13 @@ module.exports = function (RED) {
41
41
  fill, shape, text, payload,
42
42
  }) => { };
43
43
  // Used to call the status update from the HUE config node.
44
- node.setNodeStatusHue = ({
45
- fill, shape, text, payload,
46
- }) => {
47
- if (payload === undefined) return;
44
+ node.setNodeStatusHue = ({ fill, shape, text, payload }) => {
45
+ if (payload === undefined) payload = '';
48
46
  const dDate = new Date();
49
47
  payload = typeof payload === "object" ? JSON.stringify(payload) : payload.toString();
50
48
  node.status({ fill, shape, text: `${text} ${payload} (${dDate.getDate()}, ${dDate.toLocaleTimeString()})` });
51
49
  };
52
50
 
53
- node.setNodeStatusHue({
54
- fill: "grey",
55
- shape: "ring",
56
- text: "Connecting to the Bridge...",
57
- payload: "",
58
- });
59
-
60
51
  function getRandomIntInclusive(min, max) {
61
52
  min = Math.ceil(min);
62
53
  max = Math.floor(max);
@@ -67,9 +58,9 @@ module.exports = function (RED) {
67
58
  node.handleSend = (msg) => {
68
59
  if (node.currentHUEDevice === undefined) {
69
60
  node.setNodeStatusHue({
70
- fill: "red",
61
+ fill: "grey",
71
62
  shape: "ring",
72
- text: "Rejected KNX message. I'm connecting to the Bridge...",
63
+ text: "Currently not ready.",
73
64
  payload: "",
74
65
  });
75
66
  return;
@@ -448,26 +439,32 @@ module.exports = function (RED) {
448
439
  node.currentHUEDevice.on.on = _event.on.on;
449
440
  }
450
441
  if (_event.hasOwnProperty("color")) {
451
- node.updateKNXLightColorState(_event.color);
452
- node.currentHUEDevice.color = _event.color;
442
+ if (_event.type !== 'grouped_light') {
443
+ // In grouped lights, there is group color, but each light has it's own color.
444
+ node.updateKNXLightColorState(_event.color);
445
+ node.currentHUEDevice.color = _event.color;
446
+ }
453
447
  }
454
448
  if (_event.hasOwnProperty("dimming") && _event.dimming.brightness !== undefined) {
455
449
  // Every once on a time, the light transmit the brightness value of 0.39.
456
450
  // To avoid wrongly turn light state on, exit
457
451
  if (_event.dimming.brightness < 1) _event.dimming.brightness = 0;
458
- if (node.currentHUEDevice.hasOwnProperty("on") && node.currentHUEDevice.on.on === false && _event.dimming.brightness === 0) return;
459
- if (node.currentHUEDevice.on.on === false) node.updateKNXLightState(_event.dimming.brightness > 0);
460
- node.updateKNXBrightnessState(_event.dimming.brightness);
461
- // If the brightness reaches zero, the hue lamp "on" property must be set to zero as well
462
- if (_event.dimming.brightness === 0) {
463
- node.serverHue.hueManager.writeHueQueueAdd(
464
- node.hueDevice,
465
- { on: { on: false } },
466
- node.isGrouped_light === false ? "setLight" : "setGroupedLight",
467
- );
468
- node.currentHUEDevice.on.on = false;
452
+ if (node.currentHUEDevice.hasOwnProperty("on") && node.currentHUEDevice.on.on === false && _event.dimming.brightness === 0) {
453
+ // Do nothing, because the light is off and the dimming also is 0
454
+ } else {
455
+ if (node.currentHUEDevice.on.on === false) node.updateKNXLightState(_event.dimming.brightness > 0);
456
+ node.updateKNXBrightnessState(_event.dimming.brightness);
457
+ // If the brightness reaches zero, the hue lamp "on" property must be set to zero as well
458
+ if (_event.dimming.brightness === 0) {
459
+ node.serverHue.hueManager.writeHueQueueAdd(
460
+ node.hueDevice,
461
+ { on: { on: false } },
462
+ node.isGrouped_light === false ? "setLight" : "setGroupedLight",
463
+ );
464
+ node.currentHUEDevice.on.on = false;
465
+ }
466
+ node.currentHUEDevice.dimming.brightness = _event.dimming.brightness;
469
467
  }
470
- node.currentHUEDevice.dimming.brightness = _event.dimming.brightness;
471
468
  }
472
469
  if (_event.hasOwnProperty("color_temperature") && _event.color_temperature.mirek !== undefined) {
473
470
  node.updateKNXLightHSVState(_event.color_temperature.mirek);
@@ -615,6 +612,8 @@ module.exports = function (RED) {
615
612
  }
616
613
  };
617
614
 
615
+
616
+
618
617
  // On each deploy, unsubscribe+resubscribe
619
618
  if (node.server) {
620
619
  node.server.removeClient(node);
@@ -29,7 +29,7 @@ module.exports = function (RED) {
29
29
  };
30
30
  // Used to call the status update from the HUE config node.
31
31
  node.setNodeStatusHue = ({ fill, shape, text, payload }) => {
32
- if (payload === undefined) return;
32
+ if (payload === undefined) payload = '';
33
33
  const dDate = new Date();
34
34
  payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString();
35
35
  node.status({ fill, shape, text: text + ' ' + payload + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ')' });
@@ -24,17 +24,17 @@ module.exports = function (RED) {
24
24
  node.formatdecimalsvalue = 2;
25
25
 
26
26
  // Used to call the status update from the config node.
27
- node.setNodeStatus = ({ fill, shape, text, payload }) => {};
27
+ node.setNodeStatus = ({ fill, shape, text, payload }) => { };
28
28
  // Used to call the status update from the HUE config node.
29
29
  node.setNodeStatusHue = ({ fill, shape, text, payload }) => {
30
- if (payload === undefined) return;
30
+ if (payload === undefined) payload = '';
31
31
  const dDate = new Date();
32
32
  payload = typeof payload === "object" ? JSON.stringify(payload) : payload.toString();
33
33
  node.status({ fill, shape, text: `${text} ${payload} (${dDate.getDate()}, ${dDate.toLocaleTimeString()})` });
34
34
  };
35
35
 
36
36
  // This function is called by the knx-ultimate config node, to output a msg.payload.
37
- node.handleSend = (msg) => {};
37
+ node.handleSend = (msg) => { };
38
38
 
39
39
  node.handleSendHUE = (_event) => {
40
40
  try {
@@ -91,7 +91,7 @@ module.exports = function (RED) {
91
91
  node.serverHue.addClient(node);
92
92
  }
93
93
 
94
- node.on("input", (msg) => {});
94
+ node.on("input", (msg) => { });
95
95
 
96
96
  node.on("close", (done) => {
97
97
  if (node.server) {
@@ -31,7 +31,7 @@ module.exports = function (RED) {
31
31
  };
32
32
  // Used to call the status update from the HUE config node.
33
33
  node.setNodeStatusHue = ({ fill, shape, text, payload }) => {
34
- if (payload === undefined) return;
34
+ if (payload === undefined) payload = '';
35
35
  const dDate = new Date();
36
36
  payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString();
37
37
  node.status({ fill, shape, text: text + ' ' + payload + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ')' });
@@ -35,7 +35,7 @@ module.exports = function (RED) {
35
35
  node.setNodeStatusHue = ({
36
36
  fill, shape, text, payload,
37
37
  }) => {
38
- if (payload === undefined) return;
38
+ if (payload === undefined) payload = '';
39
39
  const dDate = new Date();
40
40
  payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString();
41
41
  node.status({ fill, shape, text: `${text} ${payload} (${dDate.getDate()}, ${dDate.toLocaleTimeString()})` });
@@ -33,7 +33,7 @@ module.exports = function (RED) {
33
33
  node.setNodeStatusHue = ({
34
34
  fill, shape, text, payload,
35
35
  }) => {
36
- if (payload === undefined) return;
36
+ if (payload === undefined) payload = '';
37
37
  const dDate = new Date();
38
38
  payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString();
39
39
  node.status({ fill, shape, text: `${text} ${payload} (${dDate.getDate()}, ${dDate.toLocaleTimeString()})` });
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "engines": {
4
4
  "node": ">=16.0.0"
5
5
  },
6
- "version": "2.2.3",
6
+ "version": "2.2.4",
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",