node-red-contrib-knx-ultimate 2.2.5 → 2.2.9

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,8 +6,21 @@
6
6
 
7
7
  # CHANGELOG
8
8
 
9
+ <b>Version 2.2.9</b> - November 2023<br/>
10
+ - Fixed errors in Iobroker.<br/>
11
+ </p>
12
+ <b>Version 2.2.8</b> - November 2023<br/>
13
+ - HUE Light: NEW: color selection show now the temperature in kelvin.<br/>
14
+ - HUE Light: NEW: Tunable White: added control and status in kelvin (DPT 7.600). This is in BETA testing.<br/>
15
+ - Removed some options in button and scene nodes, because they are unnecessary.<br/>
16
+ </p>
17
+ <b>Version 2.2.6</b> - October 2023<br/>
18
+ - Fix: fixed HUE button sending a KNX telegram at startup. Fixed also other nodes.<br/>
19
+ - HUE Nodes: added the option to inizialize at startup or not.<br/>
20
+ </p>
9
21
  <b>Version 2.2.5</b> - October 2023<br/>
10
22
  - Fix: fixed some HUE nodes not able to register to the event notification service.<br/>
23
+ - Restyle GUI of KNX Device node.<br/>
11
24
  </p>
12
25
  <b>Version 2.2.4</b> - October 2023<br/>
13
26
  - HUE Light: fixed some status hiccups and better handling of async hue bridge functions.<br/>
@@ -58,7 +58,7 @@ module.exports = (RED) => {
58
58
  node.nodeClientsAwaitingInit = []; // Stores the nodes client to be initialized
59
59
  node.loglevel = config.loglevel !== undefined ? config.loglevel : "error"; // 18/02/2020 Loglevel default error
60
60
  node.sysLogger = null;
61
- node.hueAllResources = null;
61
+ node.hueAllResources = undefined;
62
62
  try {
63
63
  node.sysLogger = loggerEngine.get({ loglevel: node.loglevel }); // New logger to adhere to the loglevel selected in the config-window
64
64
  } catch (error) {
@@ -91,14 +91,15 @@ module.exports = (RED) => {
91
91
  });
92
92
  // Connected
93
93
  node.hueManager.on("connected", () => {
94
- (async () => {
95
- try {
96
- await node.loadResourcesFromHUEBridge(); // Then, you can use node.getResources, that works locally and doesn't query the HUE Bridge.
97
- } catch (error) {
98
- /* empty */
99
- }
100
- })();
101
- if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info("node.hueManager connected event");
94
+ setTimeout(() => {
95
+ (async () => {
96
+ try {
97
+ await node.loadResourcesFromHUEBridge(); // Then, you can use node.getResources, that works locally and doesn't query the HUE Bridge.
98
+ } catch (error) {
99
+ if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error("hue-Config node.hueManager.on('connected' " + error.message);
100
+ }
101
+ })();
102
+ }, 5000);
102
103
  });
103
104
  };
104
105
 
@@ -119,7 +120,7 @@ module.exports = (RED) => {
119
120
  node.nodeClientsAwaitingInit = [];
120
121
  }
121
122
  node.nodeClients.forEach((nodeClient) => {
122
- if (nodeClient.hueDevice !== undefined) {
123
+ if (nodeClient.hueDevice !== undefined && node.hueAllResources !== undefined) {
123
124
  const oHUEDevice = node.hueAllResources.filter((a) => a.id === nodeClient.hueDevice)[0];
124
125
  if (oHUEDevice !== undefined) {
125
126
  // Add _Node to the clients array
@@ -129,6 +130,7 @@ module.exports = (RED) => {
129
130
  text: "Ready :-)",
130
131
  });
131
132
  nodeClient.currentHUEDevice = oHUEDevice;
133
+ oHUEDevice.initializingAtStart = true; // Signalling first connection after restart.
132
134
  nodeClient.handleSendHUE(oHUEDevice);
133
135
  }
134
136
  }
@@ -273,11 +275,12 @@ module.exports = (RED) => {
273
275
 
274
276
  node.addClient = (_Node) => {
275
277
  // Update the node hue device, as soon as a node register itself to hue-config nodeClients
276
- if (node.hueAllResources !== null) {
278
+ if (node.hueAllResources !== undefined && node.hueAllResources !== null) {
277
279
  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")
278
280
  const oHUEDevice = node.hueAllResources.filter((a) => a.id === _Node.hueDevice)[0];
279
281
  _Node.currentHUEDevice = oHUEDevice;
280
- if (oHUEDevice !== undefined) _Node.handleSendHUE(oHUEDevice);
282
+ oHUEDevice.initializingAtStart = true; // Signalling first connection after restart.
283
+ _Node.handleSendHUE(oHUEDevice);
281
284
  node.nodeClients.push(_Node);
282
285
  // Add _Node to the clients array
283
286
  _Node.setNodeStatusHue({
@@ -2110,7 +2110,7 @@ return msg;`,
2110
2110
  node.allowLauch_initKNXConnection = true; // Next cycle, launch initKNXConnection, so it pauses more and leave more time
2111
2111
  const t = setTimeout(() => {
2112
2112
  // 21/03/2022 fixed possible memory leak. Previously was setTimeout without "let t = ".
2113
- node.setAllClientsStatus("Next cycle will reconnect...", "grey", "");
2113
+ node.setAllClientsStatus("Retry connection", "grey", "");
2114
2114
  }, 1000);
2115
2115
  if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.debug(
2116
2116
  "knxUltimate-config: Waiting next cycle to reconect. node.LinkStatus: " + node.linkStatus + ", node.autoReconnect:" + node.autoReconnect,
@@ -11,6 +11,8 @@
11
11
  namebatterysensor: { value: "" },
12
12
  GAbatterysensor: { value: "" },
13
13
  dptbatterysensor: { value: "" },
14
+ readStatusAtStartup: { value: "no" },
15
+
14
16
 
15
17
  hueDevice: { value: "" }
16
18
  },
@@ -212,8 +214,13 @@
212
214
  <input type="text" id="node-input-namebatterysensor" style="width:200px;margin-left: 5px; text-align: left;">
213
215
  </div>
214
216
 
215
-
216
-
217
+ <div class="form-row">
218
+ <label style="width:180px" for="node-input-readStatusAtStartup"><i class="fa fa-play-circle"></i> Read status at startup</label>
219
+ <select id="node-input-readStatusAtStartup">
220
+ <option value="no">No</option>
221
+ <option value="yes">Yes, and emit KNX telegrams.</option>
222
+ </select>
223
+ </div>
217
224
  <br/>
218
225
  <br/>
219
226
  <br/>
@@ -222,11 +229,12 @@
222
229
  </script>
223
230
  <script src="https://kit.fontawesome.com/11f26b4500.js" crossorigin="anonymous"></script>
224
231
 
225
- <script type="text/markdown" data-help-name="knxUltimateHueBattery"
226
- This node lets you get the battery level from your HUE device.
227
-
228
- Here you can get the HUE battery level events, that represents a percentage 0-100% value, evetytime the battery level changes.<br/>
229
- Start typing in the GA field, the name or group address of your KNX device, the avaiable devices start showing up while you're typing.
232
+ <script type="text/markdown" data-help-name="knxUltimateHueBattery">
233
+ This node lets you get the battery level from your HUE device. Here you can get the HUE battery level events, that represents a percentage 0-100% value, evetytime the
234
+ battery level changes.
235
+
236
+ Start typing in the GA field, the name or group address of your KNX device, the avaiable devices start showing up while
237
+ you're typing.
230
238
 
231
239
  **General**
232
240
  |Property|Description|
@@ -234,16 +242,17 @@ Start typing in the GA field, the name or group address of your KNX device, the
234
242
  | KNX GW | Select the KNX gateway to be used |
235
243
  | HUE Bridge | Select the HUE Bridge to be used |
236
244
  | Hue Sensor | HUE sensor to be used. The avaiable devices start showing up while you're typing.|
245
+ | Read status at startup | Read the status at startup and emit the event to the KNX bus at startup/reconnection. (Default "no")|
237
246
 
238
247
  |Property|Description|
239
248
  |--|--|
240
249
  | Level | The battery level group address. The group address must be a percentage 0-100% (5.001) |
241
250
 
242
251
 
243
- <br/>
252
+ <br />
244
253
 
245
254
  [Find it useful?](https://www.paypal.me/techtoday)
246
255
 
247
- <br/>
256
+ <br />
248
257
 
249
- </script>
258
+ </script>
@@ -47,30 +47,34 @@ module.exports = function (RED) {
47
47
  node.handleSendHUE = (_event) => {
48
48
  try {
49
49
  if (_event.id === config.hueDevice) {
50
+
51
+ // IMPORTANT: exit if no event presen.
52
+ if (_event.initializingAtStart === true && (config.readStatusAtStartup === undefined || config.readStatusAtStartup === "no")) return;
53
+ if (!_event.hasOwnProperty("power_state") || _event.power_state.battery_level === undefined) return;
54
+
50
55
  const knxMsgPayload = {};
51
56
  knxMsgPayload.topic = config.GAbatterysensor;
52
57
  knxMsgPayload.dpt = config.dptbatterysensor;
53
58
 
54
- if (_event.hasOwnProperty('power_state') && _event.power_state.hasOwnProperty('battery_level')) {
55
- knxMsgPayload.payload = _event.power_state.battery_level;
56
- // Send to KNX bus
57
- if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
58
- node.server.writeQueueAdd({
59
- grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id,
60
- });
61
- }
62
-
63
- // Setup the output msg
64
- knxMsgPayload.name = node.name;
65
- knxMsgPayload.event = 'power_state';
66
-
67
- // Send payload
68
- knxMsgPayload.rawEvent = _event;
69
- node.send(knxMsgPayload);
70
- node.setNodeStatusHue({
71
- fill: 'blue', shape: 'ring', text: 'HUE->KNX', payload: knxMsgPayload.payload,
59
+ knxMsgPayload.payload = _event.power_state.battery_level;
60
+ // Send to KNX bus
61
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
62
+ node.server.writeQueueAdd({
63
+ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id,
72
64
  });
73
65
  }
66
+
67
+ // Setup the output msg
68
+ knxMsgPayload.name = node.name;
69
+ knxMsgPayload.event = 'power_state';
70
+
71
+ // Send payload
72
+ knxMsgPayload.rawEvent = _event;
73
+ node.send(knxMsgPayload);
74
+ node.setNodeStatusHue({
75
+ fill: 'blue', shape: 'ring', text: 'HUE->KNX', payload: knxMsgPayload.payload,
76
+ });
77
+
74
78
  }
75
79
  } catch (error) {
76
80
  node.status({ fill: 'red', shape: 'dot', text: `HUE->KNX error ${error.message} (${new Date().getDate()}, ${new Date().toLocaleTimeString()})` });
@@ -396,7 +396,6 @@
396
396
  Toggle values
397
397
  </label>
398
398
  </div>
399
-
400
399
 
401
400
 
402
401
  <br/>
@@ -76,6 +76,11 @@ module.exports = function (RED) {
76
76
  node.handleSendHUE = (_event) => {
77
77
  try {
78
78
  if (_event.id === config.hueDevice) {
79
+
80
+ // IMPORTANT: exit if no button last_event present.
81
+ if (_event.initializingAtStart === true) return;
82
+ if (!_event.hasOwnProperty("button") || _event.button.last_event === undefined) return;
83
+
79
84
  const knxMsgPayload = {};
80
85
  let flowMsgPayload = true;
81
86
  // Handling events with toggles
@@ -85,11 +85,20 @@
85
85
  GADaylightSensor: { value: "" },
86
86
  dptDaylightSensor: { value: "" },
87
87
 
88
+ nameLightKelvin: { value: "" },
89
+ GALightKelvin: { value: "" },
90
+ dptLightKelvin: { value: "" },
91
+
92
+ nameLightKelvinState: { value: "" },
93
+ GALightKelvinState: { value: "" },
94
+ dptLightKelvinState: { value: "" },
95
+
88
96
  specifySwitchOnBrightness: { value: "yes" },
89
97
  updateKNXBrightnessStatusOnHUEOnOff: { value: "no" },
90
98
  dimSpeed: { value: 5000, required: false },
91
99
  minDimLevelLight: { value: 10, required: false },
92
100
  maxDimLevelLight: { value: 100, required: false },
101
+ readStatusAtStartup: { value: "yes" },
93
102
 
94
103
  hueDevice: { value: "" },
95
104
  },
@@ -127,23 +136,25 @@
127
136
  <div class="red-ui-sidebar-header">Color Selector</div>
128
137
  </head>
129
138
  <div style='position:relative;height:100%;margin:10px'>
130
- <p>Choose the desired color/temperature, copy the <b>Result RGB</b> text and paste it into the required fields.</p>
139
+ <p>Choose the desired color/temperature, then click <b>Apply</b></p>
131
140
  <div>
132
- <h3>Color</h3>
141
+ <h3>Color RGB</h3>
133
142
  <div id="colorPicker"></div>
134
143
  </div>
135
- </br>
144
+ </br>
145
+ <div>
146
+ <input style="width:100%" type="text" id="resultRGB">
147
+ <input type="hidden" id="tabNRColor_destinationTextbox">
148
+ </div>
149
+ </br>
136
150
  <div>
137
- <h3>Temperature</h3>
151
+ <h3>Temperature Kelvin</h3>
138
152
  <div id="kelvinPicker"></div>
139
153
  </div>
140
- </br>
154
+ </br>
141
155
  <div>
142
- <h3>Result RGB</h3>
143
- <div>
144
- <input style="width:100%" type="text" id="resultRGB">
145
- <input type="hidden" id="tabNRColor_destinationTextbox">
146
- </div>
156
+ <input style="width:100%" type="text" id="resultKelvin">
157
+ <input type="hidden" id="tabNRColor_destinationTextbox">
147
158
  </div>
148
159
  <div id="tabNRColor_divApplyCancel" hidden>
149
160
  <button id="tabNRColor_colorSelectorTABApplyTextButton" type="button" class="red-ui-button">Apply</button>
@@ -185,7 +196,18 @@
185
196
  node.kelvinPicker.on("color:change", function (color) {
186
197
  const resultRGBForNode = '{"red": ' + color.rgb.r + ', "green": ' + color.rgb.g + ', "blue": ' + color.rgb.b + "}";
187
198
  $("#resultRGB").val(resultRGBForNode);
199
+ $("#resultKelvin").val(Math.round(color.kelvin, 0));
200
+ });
201
+
202
+ $("#resultKelvin").on("keyup", function () {
203
+ setIroColorKelvin();
188
204
  });
205
+ function setIroColorKelvin() {
206
+ try {
207
+ const color = JSON.parse($("#resultKelvin").val());
208
+ node.kelvinPicker.color.setChannel("kelvin", color);
209
+ } catch (error) { }
210
+ }
189
211
 
190
212
  // Color
191
213
  node.colorPicker = new iro.ColorPicker("#colorPicker", {
@@ -212,6 +234,7 @@
212
234
  const resultRGBForNode = '{"red": ' + color.rgb.r + ', "green": ' + color.rgb.g + ', "blue": ' + color.rgb.b + "}";
213
235
  $("#resultRGB").val(resultRGBForNode);
214
236
  });
237
+
215
238
  $("#resultRGB").on("keyup", function () {
216
239
  setIroColor();
217
240
  });
@@ -229,7 +252,12 @@
229
252
  // navigator.clipboard.writeText(aa)
230
253
  const destTextBox = "#" + $("#tabNRColor_destinationTextbox").val();
231
254
  const resultRGB = $("#resultRGB").val();
232
- $(destTextBox).val(resultRGB);
255
+ const resultKelvin = node.kelvinPicker.colors[0].kelvin;
256
+ if (destTextBox === "#node-input-colorAtSwitchOnNightTime" || destTextBox === "#node-input-colorAtSwitchOnDayTime") {
257
+ $(destTextBox).val(resultRGB);
258
+ } else {
259
+ $(destTextBox).val(resultKelvin);
260
+ }
233
261
  // Flash the destination control
234
262
  $(destTextBox).css("background-color", "lightgreen");
235
263
  $("#tabNRColor_divApplyCancel").hide();
@@ -379,6 +407,13 @@
379
407
  getDPT("1.", "#node-input-dptDaylightSensor");
380
408
  getGroupAddress("#node-input-GADaylightSensor", "#node-input-nameDaylightSensor", "#node-input-dptDaylightSensor", " 1.");
381
409
 
410
+ getDPT("7.600", "#node-input-dptLightKelvin");
411
+ getGroupAddress("#node-input-GALightKelvin", "#node-input-nameLightKelvin", "#node-input-dptLightKelvin", " 7.600");
412
+
413
+ getDPT("7.600", "#node-input-dptLightKelvinState");
414
+ getGroupAddress("#node-input-GALightKelvinState", "#node-input-nameLightKelvinState", "#node-input-dptLightKelvinState", " 7.600");
415
+
416
+
382
417
  // Show/Hide and enable/disable day/night Lighting behaviour
383
418
  if (this.enableDayNightLighting === "yes") {
384
419
  $("#divEnableDayNightLighting").show();
@@ -483,7 +518,6 @@
483
518
  });
484
519
 
485
520
  $("#getColorAtSwitchOnDayTimeButton").on("click", function () {
486
- // Get the HUE capabilities to enable/disable UI parts
487
521
  $("#getColorAtSwitchOnDayTimeButton").text("Wait...");
488
522
  $.getJSON("knxUltimateGetHueColor?id=" + $("#node-input-hueDevice").val().split("#")[0], (data) => {
489
523
  $("#node-input-colorAtSwitchOnDayTime").val(data.toString());
@@ -495,7 +529,6 @@
495
529
  });
496
530
  });
497
531
  $("#getColorAtSwitchOnNightTimeButton").on("click", function () {
498
- // Get the HUE capabilities to enable/disable UI parts
499
532
  $("#getColorAtSwitchOnNightTimeButton").text("Wait...");
500
533
  $.getJSON("knxUltimateGetHueColor?id=" + $("#node-input-hueDevice").val().split("#")[0], (data) => {
501
534
  $("#node-input-colorAtSwitchOnNightTime").val(data.toString());
@@ -527,6 +560,8 @@
527
560
  $("#node-input-maxDimLevelLight").val(node.maxDimLevelLight);
528
561
 
529
562
  if (this.hueDevice !== "") $("#tabs").show();
563
+
564
+
530
565
  },
531
566
  oneditsave: function () {
532
567
  RED.sidebar.removeTab("tabNRColor");
@@ -664,43 +699,68 @@
664
699
  </div>
665
700
  <div id="tabs-3">
666
701
  <p>
667
- <div class="form-row">
668
- <label for="node-input-nameLightHSV" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control</label>
702
+ <div class="form-row">
703
+ <label for="node-input-nameLightHSV" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control dim</label>
669
704
 
670
- <label for="node-input-GALightHSV" style="width:20px;">GA</label>
671
- <input type="text" id="node-input-GALightHSV" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
705
+ <label for="node-input-GALightHSV" style="width:20px;">GA</label>
706
+ <input type="text" id="node-input-GALightHSV" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
672
707
 
673
- <label for="node-input-dptLightHSV" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
674
- <select id="node-input-dptLightHSV" style="width:140px;"></select>
708
+ <label for="node-input-dptLightHSV" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
709
+ <select id="node-input-dptLightHSV" style="width:140px;"></select>
675
710
 
676
- <label for="node-input-nameLightHSV" style="width:50px; margin-left: 0px; text-align: right;">Name</label>
677
- <input type="text" id="node-input-nameLightHSV" style="width:190px;margin-left: 5px; text-align: left;">
678
- </div>
679
- <div class="form-row">
680
- <label for="node-input-nameLightHSVPercentage" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control %</label>
711
+ <label for="node-input-nameLightHSV" style="width:50px; margin-left: 0px; text-align: right;">Name</label>
712
+ <input type="text" id="node-input-nameLightHSV" style="width:190px;margin-left: 5px; text-align: left;">
713
+ </div>
714
+ <div class="form-row">
715
+ <label for="node-input-nameLightHSVPercentage" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control %</label>
716
+
717
+ <label for="node-input-GALightHSVPercentage" style="width:20px;">GA</label>
718
+ <input type="text" id="node-input-GALightHSVPercentage" placeholder="Ex: 1/1/1"
719
+ style="width:70px;margin-left: 5px; text-align: left;">
720
+
721
+ <label for="node-input-dptLightHSVPercentage" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
722
+ <select id="node-input-dptLightHSVPercentage" style="width:140px;"></select>
723
+
724
+ <label for="node-input-nameLightHSVPercentage" style="width:50px; margin-left: 0px; text-align: right;">Name</label>
725
+ <input type="text" id="node-input-nameLightHSVPercentage" style="width:190px;margin-left: 5px; text-align: left;">
726
+ </div>
727
+ <div class="form-row">
728
+ <label for="node-input-nameLightKelvin" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control Kelvin</label>
681
729
 
682
- <label for="node-input-GALightHSVPercentage" style="width:20px;">GA</label>
683
- <input type="text" id="node-input-GALightHSVPercentage" placeholder="Ex: 1/1/1"
730
+ <label for="node-input-GALightKelvin" style="width:20px;">GA</label>
731
+ <input type="text" id="node-input-GALightKelvin" placeholder="Ex: 1/1/1"
684
732
  style="width:70px;margin-left: 5px; text-align: left;">
685
733
 
686
- <label for="node-input-dptLightHSVPercentage" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
687
- <select id="node-input-dptLightHSVPercentage" style="width:140px;"></select>
734
+ <label for="node-input-dptLightKelvin" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
735
+ <select id="node-input-dptLightKelvin" style="width:140px;"></select>
688
736
 
689
- <label for="node-input-nameLightHSVPercentage" style="width:50px; margin-left: 0px; text-align: right;">Name</label>
690
- <input type="text" id="node-input-nameLightHSVPercentage" style="width:190px;margin-left: 5px; text-align: left;">
737
+ <label for="node-input-nameLightKelvin" style="width:50px; margin-left: 0px; text-align: right;">Name</label>
738
+ <input type="text" id="node-input-nameLightKelvin" style="width:190px;margin-left: 5px; text-align: left;">
691
739
  </div>
692
- <div class="form-row">
693
- <label for="node-input-nameLightHSVState" style="width:110px;"><i class="fa fa-play-circle-o"></i> Status</label>
740
+ <div class="form-row">
741
+ <label for="node-input-nameLightHSVState" style="width:110px;"><i class="fa fa-play-circle-o"></i> Status %</label>
694
742
 
695
- <label for="node-input-GALightHSVState" style="width:20px;"><span data-i18n="knxUltimateHueLight.node-input-GALightState"></span></label>
696
- <input type="text" id="node-input-GALightHSVState" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
743
+ <label for="node-input-GALightHSVState" style="width:20px;"><span data-i18n="knxUltimateHueLight.node-input-GALightState"></span></label>
744
+ <input type="text" id="node-input-GALightHSVState" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
697
745
 
698
- <label for="node-input-dptLightHSVState" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
699
- <select id="node-input-dptLightHSVState" style="width:140px;"></select>
746
+ <label for="node-input-dptLightHSVState" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
747
+ <select id="node-input-dptLightHSVState" style="width:140px;"></select>
700
748
 
701
- <label for="node-input-nameLightHSVState" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="knxUltimateHueLight.node-input-name"></span></label>
702
- <input type="text" id="node-input-nameLightHSVState" style="width:190px;margin-left: 5px; text-align: left;">
703
- </div>
749
+ <label for="node-input-nameLightHSVState" style="width:50px; margin-left: 0px; text-align: right;">Name</label>
750
+ <input type="text" id="node-input-nameLightHSVState" style="width:190px;margin-left: 5px; text-align: left;">
751
+ </div>
752
+ <div class="form-row">
753
+ <label for="node-input-nameLightKelvinState" style="width:110px;"><i class="fa fa-play-circle-o"></i> Status Kelvin</label>
754
+
755
+ <label for="node-input-GALightKelvinState" style="width:20px;">GA</label>
756
+ <input type="text" id="node-input-GALightKelvinState" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
757
+
758
+ <label for="node-input-dptLightKelvinState" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
759
+ <select id="node-input-dptLightKelvinState" style="width:140px;"></select>
760
+
761
+ <label for="node-input-nameLightKelvinState" style="width:50px; margin-left: 0px; text-align: right;">Name</label>
762
+ <input type="text" id="node-input-nameLightKelvinState" style="width:190px;margin-left: 5px; text-align: left;">
763
+ </div>
704
764
  </p>
705
765
  </div>
706
766
  <div id="tabs-4">
@@ -765,7 +825,14 @@
765
825
  <div id="tabs-6">
766
826
  <p>
767
827
  <div class="form-row">
768
- <label for="node-input-updateKNXBrightnessStatusOnHUEOnOff" style="width:260px;">
828
+ <label style="width:260px;" for="node-input-readStatusAtStartup"><i class="fa fa-play-circle"></i> Read status at startup</label>
829
+ <select id="node-input-readStatusAtStartup">
830
+ <option value="no">No</option>
831
+ <option value="yes">Yes, and emit KNX telegrams.</option>
832
+ </select>
833
+ </div>
834
+ <div class="form-row">
835
+ <label style="width:260px;" for="node-input-updateKNXBrightnessStatusOnHUEOnOff">
769
836
  <i class="fa fa-tag"></i> KNX Brightness Status
770
837
  </label>
771
838
  <select id="node-input-updateKNXBrightnessStatusOnHUEOnOff">
@@ -899,9 +966,11 @@
899
966
  **Tunable white**
900
967
  |Property|Description|
901
968
  |--|--|
902
- | Control| Changes the HUE light's white temperature, using DPT 3.007 dimming. You can set the dimming speed in the **_Behaviour_** tab.|
969
+ | Control dim | Changes the HUE light's white temperature, using DPT 3.007 dimming. You can set the dimming speed in the **_Behaviour_** tab.|
903
970
  | Control % | Changes the HUE light's white temperature, using the DPT 5.001. A value of 0 is full warm, a value of 100 is full cold.|
904
- | Status | Link this to the light temperature status group address. Datapoint is 5.001 absolute value. 0 is full warm, 100 is full cold.|
971
+ | Control kelvin | Changes the HUE light's temperature in Kelvin degrees, using the DPT 7.600. |
972
+ | Status %| Link this to the light temperature status group address. Datapoint is 5.001 absolute value. 0 is full warm, 100 is full cold.|
973
+ | Status kelvin | Link this to the light temperature status group address. Datapoint 7.600. |
905
974
 
906
975
  <br/>
907
976
 
@@ -935,6 +1004,7 @@
935
1004
  | 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%. |
936
1005
  | Min Dim brightness | Tha Minimum brightness that the lamp can reach. For example, if you are dimming the light down, the light will stop dimming at the specified brightness %. |
937
1006
  | Max Dim brightness | Tha Maximum brightness that the lamp can reach. For example, if you are dimming the light up, the light will stop dimming at the specified brightness %. |
1007
+ | Read status at startup | Read the status at startup and emit the event to the KNX bus at startup/reconnection. (Default "no")|
938
1008
 
939
1009
  ### Note
940
1010
 
@@ -35,6 +35,8 @@ module.exports = function (RED) {
35
35
  node.DayTime = true;
36
36
  node.isGrouped_light = config.hueDevice.split("#")[1] === "grouped_light";
37
37
  node.hueDevice = config.hueDevice.split("#")[0];
38
+ node.readStatusAtStartup = config.readStatusAtStartup;
39
+ if (config.readStatusAtStartup === undefined) node.readStatusAtStartup = "yes";
38
40
 
39
41
  // Used to call the status update from the config node.
40
42
  node.setNodeStatus = ({
@@ -58,9 +60,9 @@ module.exports = function (RED) {
58
60
  node.handleSend = (msg) => {
59
61
  if (node.currentHUEDevice === undefined) {
60
62
  node.setNodeStatusHue({
61
- fill: "red",
63
+ fill: "grey",
62
64
  shape: "ring",
63
- text: "Currently not ready.",
65
+ text: "Initializing. Please wait.",
64
66
  payload: "",
65
67
  });
66
68
  return;
@@ -124,6 +126,18 @@ module.exports = function (RED) {
124
126
  fill: "green", shape: "dot", text: "KNX->HUE", payload: JSON.stringify(msg.payload),
125
127
  });
126
128
  break;
129
+ case config.GALightKelvin:
130
+ msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightKelvin));
131
+ let retMirek = hueColorConverter.ColorConverter.scale(msg.payload, [0, 65535], [153, 500]);
132
+ state = { color_temperature: { mirek: retMirek } };
133
+ node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
134
+ node.setNodeStatusHue({
135
+ fill: "green",
136
+ shape: "dot",
137
+ text: "KNX->HUE",
138
+ payload: state,
139
+ });
140
+ break;
127
141
  case config.GADaylightSensor:
128
142
  if (config.enableDayNightLighting === "yes") {
129
143
  node.DayTime = Boolean(dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptDaylightSensor)));
@@ -166,12 +180,6 @@ module.exports = function (RED) {
166
180
  payload: state,
167
181
  });
168
182
  }
169
- node.setNodeStatusHue({
170
- fill: "green",
171
- shape: "dot",
172
- text: "KNX->HUE",
173
- payload: msg.payload,
174
- });
175
183
  break;
176
184
  case config.GALightBrightness:
177
185
  msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightBrightness));
@@ -419,6 +427,9 @@ module.exports = function (RED) {
419
427
  });
420
428
  return;
421
429
  }
430
+ // IMPORTANT: exit if no button last_event present.
431
+ if (_event.initializingAtStart === true && node.readStatusAtStartup === "no") return;
432
+
422
433
  if (_event.hasOwnProperty("on")) {
423
434
  node.updateKNXLightState(_event.on.on);
424
435
  // In case of switch off, set the dim to zero
@@ -428,13 +439,6 @@ module.exports = function (RED) {
428
439
  ) {
429
440
  node.updateKNXBrightnessState(0);
430
441
  node.currentHUEDevice.dimming.brightness = 0;
431
- } else {
432
- // // Sends the previous brightness value
433
- // try {
434
- // node.updateKNXBrightnessState(node.currentHUEDevice.dimming.brightness);
435
- // } catch (error) {
436
- // /* empty */
437
- // }
438
442
  }
439
443
  node.currentHUEDevice.on.on = _event.on.on;
440
444
  }
@@ -468,24 +472,9 @@ module.exports = function (RED) {
468
472
  }
469
473
  if (_event.hasOwnProperty("color_temperature") && _event.color_temperature.mirek !== undefined) {
470
474
  node.updateKNXLightHSVState(_event.color_temperature.mirek);
475
+ node.updateKNXLightKelvinState(_event.color_temperature.mirek);
471
476
  node.currentHUEDevice.color_temperature.mirek = _event.color_temperature.mirek;
472
477
  }
473
-
474
- // // Update the current HUE Device with the new _event
475
- // function copiaOggettoRicorsivo(objDestinazione, objOrigine) {
476
- // for (const prop in objOrigine) {
477
- // if (typeof objOrigine[prop] === "object" && objOrigine[prop] !== null) {
478
- // // Se la proprietà è un oggetto, copiamola in modo ricorsivo
479
- // objDestinazione[prop] = objDestinazione[prop] || {};
480
- // copiaOggettoRicorsivo(objDestinazione[prop], objOrigine[prop]);
481
- // } else {
482
- // // Altrimenti, copia il valore della proprietà
483
- // objDestinazione[prop] = objOrigine[prop];
484
- // }
485
- // }
486
- // }
487
- // // Copia l'oggettoOrigine nell'oggettoDestinazione mantenendo le proprietà esistenti
488
- // copiaOggettoRicorsivo(node.currentHUEDevice, _event);
489
478
  }
490
479
  } catch (error) {
491
480
  node.status({
@@ -612,7 +601,32 @@ module.exports = function (RED) {
612
601
  }
613
602
  };
614
603
 
615
-
604
+ node.updateKNXLightKelvinState = function updateKNXLightKelvinState(_value) {
605
+ if (config.GALightKelvinState !== undefined && config.GALightKelvinState !== "") {
606
+ const knxMsgPayload = {};
607
+ knxMsgPayload.topic = config.GALightKelvinState;
608
+ knxMsgPayload.dpt = config.dptLightKelvinState;
609
+ if (config.dptLightKelvinState === "7.600") {
610
+ knxMsgPayload.payload = hueColorConverter.ColorConverter.scale(_value, [153, 500], [0, 65535]);
611
+ // Send to KNX bus
612
+ if (knxMsgPayload.topic !== "" && knxMsgPayload.topic !== undefined) {
613
+ node.server.writeQueueAdd({
614
+ grpaddr: knxMsgPayload.topic,
615
+ payload: knxMsgPayload.payload,
616
+ dpt: knxMsgPayload.dpt,
617
+ outputtype: "write",
618
+ nodecallerid: node.id,
619
+ });
620
+ }
621
+ node.setNodeStatusHue({
622
+ fill: "blue",
623
+ shape: "ring",
624
+ text: "HUE->KNX HSV",
625
+ payload: knxMsgPayload.payload,
626
+ });
627
+ }
628
+ }
629
+ };
616
630
 
617
631
  // On each deploy, unsubscribe+resubscribe
618
632
  if (node.server) {
@@ -620,8 +634,12 @@ module.exports = function (RED) {
620
634
  node.server.addClient(node);
621
635
  }
622
636
  if (node.serverHue) {
623
- node.serverHue.removeClient(node);
624
- node.serverHue.addClient(node);
637
+ try {
638
+ node.serverHue.removeClient(node);
639
+ node.serverHue.addClient(node);
640
+ } catch (error) {
641
+ RED.log.error("knxUltimateHueLight: if (node.server): " + error.message);
642
+ }
625
643
  }
626
644
 
627
645
  node.on("input", (msg) => { });
@@ -11,6 +11,7 @@
11
11
  namelightsensor: { value: "" },
12
12
  GAlightsensor: { value: "" },
13
13
  dptlightsensor: { value: "" },
14
+ readStatusAtStartup: { value: "no" },
14
15
 
15
16
  hueDevice: { value: "" }
16
17
  },
@@ -211,7 +212,13 @@
211
212
  <input type="text" id="node-input-namelightsensor" style="width:200px;margin-left: 5px; text-align: left;">
212
213
  </div>
213
214
 
214
-
215
+ <div class="form-row">
216
+ <label style="width:180px" for="node-input-readStatusAtStartup"><i class="fa fa-play-circle"></i> Read status at startup</label>
217
+ <select id="node-input-readStatusAtStartup">
218
+ <option value="no">No</option>
219
+ <option value="yes">Yes, and emit KNX telegrams.</option>
220
+ </select>
221
+ </div>
215
222
 
216
223
 
217
224
  <br/>
@@ -234,6 +241,7 @@ Start typing in the GA field, the name or group address of your KNX device, the
234
241
  | KNX GW | Select the KNX gateway to be used |
235
242
  | HUE Bridge | Select the HUE Bridge to be used |
236
243
  | Hue Sensor | HUE sensor to be used. The avaiable buttons start showing up while you're typing.|
244
+ | Read status at startup | Read the status at startup and emit the event to the KNX bus at startup/reconnection. (Default "no")|
237
245
 
238
246
  |Property|Description|
239
247
  |--|--|
@@ -43,6 +43,11 @@ module.exports = function (RED) {
43
43
  node.handleSendHUE = _event => {
44
44
  try {
45
45
  if (_event.id === config.hueDevice) {
46
+
47
+ // IMPORTANT: exit if no event presen.
48
+ if (_event.initializingAtStart === true && (config.readStatusAtStartup === undefined || config.readStatusAtStartup === "no")) return;
49
+ if (!_event.hasOwnProperty('light') || _event.light.light_level === undefined) return;
50
+
46
51
  const knxMsgPayload = {};
47
52
  knxMsgPayload.topic = config.GAlightsensor;
48
53
  knxMsgPayload.dpt = config.dptlightsensor;
@@ -235,7 +235,7 @@ Start typing in the GA field, the name or group address of your KNX device, the
235
235
  |Property|Description|
236
236
  |--|--|
237
237
  | Motion | As soon as someone moves in the motion device's range, a *true* KNX value is sent to this group address, otherwise *false* is sent. |
238
-
238
+
239
239
  ### Outputs
240
240
 
241
241
  1. Standard output
@@ -40,6 +40,12 @@ module.exports = function (RED) {
40
40
  node.handleSendHUE = (_event) => {
41
41
  try {
42
42
  if (_event.id === config.hueDevice) {
43
+
44
+ // IMPORTANT: exit if no event presen.
45
+ if (_event.initializingAtStart === true) return;
46
+ if (!_event.hasOwnProperty("motion") || _event.motion.motion === undefined) return;
47
+
48
+
43
49
  const knxMsgPayload = {};
44
50
  knxMsgPayload.topic = config.GAmotion;
45
51
  knxMsgPayload.dpt = config.dptmotion;
@@ -251,6 +251,7 @@
251
251
  <select id="node-input-valscene" style="width:180px;margin-left: 5px; text-align: left;"></select>
252
252
  </div>
253
253
 
254
+
254
255
  <br/>
255
256
  <br/>
256
257
  <br/>
@@ -78,6 +78,10 @@ module.exports = function (RED) {
78
78
  node.handleSendHUE = _event => {
79
79
  try {
80
80
  if (_event.id === config.hueDevice) {
81
+
82
+ // IMPORTANT: exit if no event presen.
83
+ if (_event.initializingAtStart === true) return;
84
+
81
85
  // const knxMsgPayload = {}
82
86
  // knxMsgPayload.topic = config.GAmotion
83
87
  // knxMsgPayload.dpt = config.dptmotion
@@ -213,10 +213,6 @@
213
213
  <input type="text" id="node-input-namerepeat" style="width:200px;margin-left: 5px; text-align: left;">
214
214
  </div>
215
215
 
216
-
217
-
218
-
219
-
220
216
  <br/>
221
217
  <br/>
222
218
  <br/>
@@ -49,6 +49,17 @@ module.exports = function (RED) {
49
49
  node.handleSendHUE = (_event) => {
50
50
  try {
51
51
  if (_event.id === config.hueDevice) {
52
+
53
+ // IMPORTANT: exit if no event presen.
54
+ if (_event.initializingAtStart === true) return;
55
+ if (!_event.hasOwnProperty("relative_rotary")
56
+ || !_event.relative_rotary.hasOwnProperty("last_event")
57
+ || _event.relative_rotary.last_event === undefined
58
+ || !_event.relative_rotary.last_event.hasOwnProperty("rotation")
59
+ || !_event.relative_rotary.last_event.rotation.direction === undefined
60
+ || _event.relative_rotary.last_event.action === undefined) return;
61
+
62
+
52
63
  const knxMsgPayload = {};
53
64
  knxMsgPayload.topic = config.GArepeat;
54
65
  knxMsgPayload.dpt = config.dptrepeat;
@@ -1,6 +1,8 @@
1
1
  const convert = require('color-convert');
2
2
 
3
3
  class ColorConverter {
4
+
5
+
4
6
  static getGamutRanges() {
5
7
  const gamutA = {
6
8
  red: [0.704, 0.296],
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "engines": {
4
4
  "node": ">=16.0.0"
5
5
  },
6
- "version": "2.2.5",
6
+ "version": "2.2.9",
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",