node-red-contrib-knx-ultimate 2.2.27 → 2.2.29

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,17 @@
6
6
 
7
7
  # CHANGELOG
8
8
 
9
+ **Version 2.2.29** - 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 an issue causing the node status to signal an error. Filtered the groupvalue_read from imbound KNX messages.<br/>
12
+ - WARNING: the new HUE Light options are to be considered **BETA (= in testing with user feedback)**.<br/>
13
+
14
+ **Version 2.2.28** - November 2023<br/>
15
+ This is an interim version, to quick fix some issues. Please report any issue with HUE Nodes, on gitHub.<br/>
16
+ - HUE Light: fixed an issue where dimming down with the light switched off, causes the brightness status to jump to 100%, thus the light remains off.<br/>
17
+ - HUE Light: Fixed some errors, if all devices belonging to a group, have only the dimming capability.<br/>
18
+ - WARNING: the new HUE Light options are to be considered **BETA (= in testing with user feedback)**.<br/>
19
+
9
20
  **Version 2.2.27** - November 2023<br/>
10
21
  This is an interim version, to quick fix some issues. Please report any issue with HUE Nodes, on gitHub.<br/>
11
22
  - HUE Light: the UI now changes, to adapt to lamp type.<br/>
@@ -258,6 +258,7 @@ module.exports = (RED) => {
258
258
  } else {
259
259
  allResources = node.hueAllResources.filter((a) => a.type === _rtype);
260
260
  }
261
+ if (allResources === null) return;
261
262
  for (let index = 0; index < allResources.length; index++) {
262
263
  const resource = allResources[index];
263
264
  // Get the owner
@@ -267,15 +268,17 @@ module.exports = (RED) => {
267
268
  if (_rtype === "light" || _rtype === "grouped_light") {
268
269
  // It's a service, having a owner
269
270
  const owners = node.hueAllResources.filter((a) => a.id === resource.owner.rid);
270
- for (let index = 0; index < owners.length; index++) {
271
- const owner = owners[index];
272
- if (owner.type === "bridge_home") {
273
- resourceName += "ALL GROUPS and ";
274
- } else {
275
- resourceName += `${owner.metadata.name} and `;
276
- // const room = node.hueAllRooms.find((child) => child.children.find((a) => a.rid === owner.id));
277
- // sRoom += room !== undefined ? `${room.metadata.name} + ` : " + ";
278
- sType += `${capStr(owner.type)} + `;
271
+ if (owners !== null) {
272
+ for (let index = 0; index < owners.length; index++) {
273
+ const owner = owners[index];
274
+ if (owner.type === "bridge_home") {
275
+ resourceName += "ALL GROUPS and ";
276
+ } else {
277
+ resourceName += `${owner.metadata.name} and `;
278
+ // const room = node.hueAllRooms.find((child) => child.children.find((a) => a.rid === owner.id));
279
+ // sRoom += room !== undefined ? `${room.metadata.name} + ` : " + ";
280
+ sType += `${capStr(owner.type)} + `;
281
+ }
279
282
  }
280
283
  }
281
284
  sType = sType.slice(0, -" + ".length);
@@ -497,7 +500,7 @@ module.exports = (RED) => {
497
500
  }
498
501
  res.json(oLight);
499
502
  } catch (error) {
500
- if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`KNXUltimateHue: hueEngine: knxUltimateGetLightObject: error ${error.message}`);
503
+ if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`KNXUltimateHue: hueEngine: knxUltimateGetLightObject: error ${error.message}. Resources still loading. Try later.`);
501
504
  res.json({});
502
505
  }
503
506
  });
@@ -93,251 +93,212 @@ module.exports = function (RED) {
93
93
  });
94
94
  return;
95
95
  }
96
- let state = {};
97
- try {
98
- switch (msg.knx.destination) {
99
- case config.GALightSwitch:
100
- msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightSwitch));
101
- if (msg.payload === true) {
102
- let colorChoosen;
103
- let temperatureChoosen;
104
- let brightnessChoosen;
105
- // The light must support the temperature (in this case, colorAtSwitchOnNightTime is an object {kelvin:xx, brightness:yy})
106
- if (node.currentHUEDevice.color_temperature !== undefined) {
107
- if (node.DayTime === true && config.specifySwitchOnBrightness === "temperature") {
108
- temperatureChoosen = config.colorAtSwitchOnDayTime.kelvin;
109
- } else if (node.DayTime === false && config.enableDayNightLighting === "temperature") {
110
- temperatureChoosen = config.colorAtSwitchOnNightTime.kelvin;
96
+ if (msg.knx.event !== "GroupValue_Read" && node.currentHUEDevice !== undefined) {
97
+ let state = {};
98
+ try {
99
+ switch (msg.knx.destination) {
100
+ case config.GALightSwitch:
101
+ msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightSwitch));
102
+ if (msg.payload === true) {
103
+ let colorChoosen;
104
+ let temperatureChoosen;
105
+ let brightnessChoosen;
106
+ // The light must support the temperature (in this case, colorAtSwitchOnNightTime is an object {kelvin:xx, brightness:yy})
107
+ if (node.currentHUEDevice.color_temperature !== undefined) {
108
+ if (node.DayTime === true && config.specifySwitchOnBrightness === "temperature") {
109
+ temperatureChoosen = config.colorAtSwitchOnDayTime.kelvin;
110
+ } else if (node.DayTime === false && config.enableDayNightLighting === "temperature") {
111
+ temperatureChoosen = config.colorAtSwitchOnNightTime.kelvin;
112
+ }
111
113
  }
112
- }
113
- if (node.currentHUEDevice.dimming !== undefined) {
114
- // Check wether the user selected specific brightness at switch on (in this case, colorAtSwitchOnNightTime is an object {kelvin:xx, brightness:yy})
115
- if (node.DayTime === true && config.specifySwitchOnBrightness === "temperature") {
116
- brightnessChoosen = config.colorAtSwitchOnDayTime.brightness;
117
- } else if (node.DayTime === false && config.enableDayNightLighting === "temperature") {
118
- brightnessChoosen = config.colorAtSwitchOnNightTime.brightness;
114
+ if (node.currentHUEDevice.dimming !== undefined) {
115
+ // Check wether the user selected specific brightness at switch on (in this case, colorAtSwitchOnNightTime is an object {kelvin:xx, brightness:yy})
116
+ if (node.DayTime === true && config.specifySwitchOnBrightness === "temperature") {
117
+ brightnessChoosen = config.colorAtSwitchOnDayTime.brightness;
118
+ } else if (node.DayTime === false && config.enableDayNightLighting === "temperature") {
119
+ brightnessChoosen = config.colorAtSwitchOnNightTime.brightness;
120
+ }
119
121
  }
120
- }
121
- if (node.currentHUEDevice.color !== undefined) {
122
- // Check wether the user selected specific color at switch on (in this case, colorAtSwitchOnDayTime is a text with HTML web color)
123
- if (node.DayTime === true && config.specifySwitchOnBrightness === "yes") {
124
- colorChoosen = config.colorAtSwitchOnDayTime;
125
- } else if (node.DayTime === false && config.enableDayNightLighting === "yes") {
126
- colorChoosen = config.colorAtSwitchOnNightTime;
122
+ if (node.currentHUEDevice.color !== undefined) {
123
+ // Check wether the user selected specific color at switch on (in this case, colorAtSwitchOnDayTime is a text with HTML web color)
124
+ if (node.DayTime === true && config.specifySwitchOnBrightness === "yes") {
125
+ colorChoosen = config.colorAtSwitchOnDayTime;
126
+ } else if (node.DayTime === false && config.enableDayNightLighting === "yes") {
127
+ colorChoosen = config.colorAtSwitchOnNightTime;
128
+ }
127
129
  }
128
- }
129
130
 
130
- if (colorChoosen !== undefined) {
131
- // Now we have a jColorChoosen. Proceed illuminating the light
132
- let gamut = null;
133
- if (node.currentHUEDevice.color.gamut_type !== undefined) {
134
- gamut = node.currentHUEDevice.color.gamut_type;
131
+ if (colorChoosen !== undefined) {
132
+ // Now we have a jColorChoosen. Proceed illuminating the light
133
+ let gamut = null;
134
+ if (node.currentHUEDevice.color.gamut_type !== undefined) {
135
+ gamut = node.currentHUEDevice.color.gamut_type;
136
+ }
137
+ const dretXY = hueColorConverter.ColorConverter.rgbToXy(colorChoosen.red, colorChoosen.green, colorChoosen.blue, gamut);
138
+ const dbright = hueColorConverter.ColorConverter.getBrightnessFromRGBOrHex(colorChoosen.red, colorChoosen.green, colorChoosen.blue);
139
+ node.currentHUEDevice.dimming.brightness = Math.round(dbright, 0);
140
+ node.updateKNXBrightnessState(node.currentHUEDevice.dimming.brightness);
141
+ state = dbright > 0 ? { on: { on: true }, dimming: { brightness: dbright }, color: { xy: dretXY } } : { on: { on: false } };
142
+ } else if (temperatureChoosen !== undefined) {
143
+ // Kelvin
144
+ const mirek = hueColorConverter.ColorConverter.kelvinToMirek(temperatureChoosen);
145
+ node.currentHUEDevice.color_temperature.mirek = mirek;
146
+ node.currentHUEDevice.dimming.brightness = brightnessChoosen;
147
+ node.updateKNXBrightnessState(node.currentHUEDevice.dimming.brightness);
148
+ // Kelvin temp
149
+ state = brightnessChoosen > 0 ? { on: { on: true }, dimming: { brightness: brightnessChoosen }, color_temperature: { mirek: mirek } } : { on: { on: false } };
150
+ } else if (brightnessChoosen !== undefined) {
151
+ state = brightnessChoosen > 0 ? { on: { on: true }, dimming: { brightness: brightnessChoosen } } : { on: { on: false } };
152
+ } else {
153
+ state = { on: { on: true } };
135
154
  }
136
- const dretXY = hueColorConverter.ColorConverter.rgbToXy(colorChoosen.red, colorChoosen.green, colorChoosen.blue, gamut);
137
- const dbright = hueColorConverter.ColorConverter.getBrightnessFromRGBOrHex(colorChoosen.red, colorChoosen.green, colorChoosen.blue);
138
- node.currentHUEDevice.dimming.brightness = Math.round(dbright, 0);
139
- node.updateKNXBrightnessState(node.currentHUEDevice.dimming.brightness);
140
- state = dbright > 0 ? { on: { on: true }, dimming: { brightness: dbright }, color: { xy: dretXY } } : { on: { on: false } };
141
- } else if (temperatureChoosen !== undefined) {
142
- // Kelvin
143
- const mirek = hueColorConverter.ColorConverter.kelvinToMirek(temperatureChoosen);
144
- node.currentHUEDevice.color_temperature.mirek = mirek;
145
- node.currentHUEDevice.dimming.brightness = brightnessChoosen;
146
- node.updateKNXBrightnessState(node.currentHUEDevice.dimming.brightness);
147
- // Kelvin temp
148
- state = brightnessChoosen > 0 ? { on: { on: true }, dimming: { brightness: brightnessChoosen }, color_temperature: { mirek: mirek } } : { on: { on: false } };
149
- } else if (brightnessChoosen !== undefined) {
150
- state = brightnessChoosen > 0 ? { on: { on: true }, dimming: { brightness: brightnessChoosen } } : { on: { on: false } };
151
155
  } else {
152
- state = { on: { on: true } };
156
+ state = { on: { on: false } };
153
157
  }
154
- } else {
155
- state = { on: { on: false } };
156
- }
157
158
 
158
- node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
159
- node.setNodeStatusHue({
160
- fill: "green",
161
- shape: "dot",
162
- text: "KNX->HUE",
163
- payload: state,
164
- });
165
- break;
166
- case config.GALightDIM:
167
- // { decr_incr: 1, data: 1 } : Start increasing until { decr_incr: 0, data: 0 } is received.
168
- // { decr_incr: 0, data: 1 } : Start decreasing until { decr_incr: 0, data: 0 } is received.
169
- msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightDIM));
170
- node.hueDimming(msg.payload.decr_incr, msg.payload.data, config.dimSpeed);
171
- node.setNodeStatusHue({
172
- fill: "green", shape: "dot", text: "KNX->HUE", payload: JSON.stringify(msg.payload),
173
- });
174
- break;
175
- case config.GALightKelvin:
176
- let retMirek;
177
- msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightKelvin));
178
- if (config.dptLightKelvin === "7.600") {
179
- if (msg.payload > 65535) msg.payload = 65535;
180
- if (msg.payload < 0) msg.payload = 0;
181
- retMirek = hueColorConverter.ColorConverter.scale(msg.payload, [0, 65535], [500, 153]);
182
- } else if (config.dptLightKelvin === "9.002") {
183
- // Relative temperature in Kelvin. Use HUE scale.
184
- if (msg.payload > 6535) msg.payload = 6535;
185
- if (msg.payload < 2000) msg.payload = 2000;
186
- retMirek = hueColorConverter.ColorConverter.scale(msg.payload, [2000, 6535], [500, 153]);
187
- }
188
- state = { color_temperature: { mirek: retMirek } };
189
- node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
190
- node.setNodeStatusHue({
191
- fill: "green",
192
- shape: "dot",
193
- text: "KNX->HUE",
194
- payload: msg.payload,
195
- });
196
- break;
197
- case config.GADaylightSensor:
198
- node.DayTime = Boolean(dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptDaylightSensor)));
199
- if (config.invertDayNight !== undefined && config.invertDayNight === true) node.DayTime = !node.DayTime;
200
- node.setNodeStatusHue({
201
- fill: "green",
202
- shape: "dot",
203
- text: "KNX->HUE Daytime",
204
- payload: node.DayTime,
205
- });
206
-
207
- break;
208
- case config.GALightHSV:
209
- if (config.dptLightHSV === "3.007") {
210
- // MDT smartbutton will dim the color temperature
159
+ node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
160
+ node.setNodeStatusHue({
161
+ fill: "green",
162
+ shape: "dot",
163
+ text: "KNX->HUE",
164
+ payload: state,
165
+ });
166
+ break;
167
+ case config.GALightDIM:
211
168
  // { decr_incr: 1, data: 1 } : Start increasing until { decr_incr: 0, data: 0 } is received.
212
169
  // { decr_incr: 0, data: 1 } : Start decreasing until { decr_incr: 0, data: 0 } is received.
213
- msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightHSV));
214
- node.hueDimmingTunableWhite(msg.payload.decr_incr, msg.payload.data, config.dimSpeed);
170
+ msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightDIM));
171
+ node.hueDimming(msg.payload.decr_incr, msg.payload.data, config.dimSpeed);
215
172
  node.setNodeStatusHue({
216
173
  fill: "green", shape: "dot", text: "KNX->HUE", payload: JSON.stringify(msg.payload),
217
174
  });
218
- }
219
- break;
220
- case config.GALightHSVPercentage:
221
- if (config.dptLightHSVPercentage === "5.001") {
222
- // 0-100% tunable white
223
- msg.payload = 100 - dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightHSVPercentage));
224
- // msg.payload = msg.payload <= 0 ? 1 : msg.payload
225
- const retMirek = hueColorConverter.ColorConverter.scale(msg.payload, [0, 100], [153, 500]);
226
- msg.payload = retMirek;
227
- state = { color_temperature: { mirek: msg.payload } };
175
+ break;
176
+ case config.GALightKelvin:
177
+ let retMirek;
178
+ msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightKelvin));
179
+ if (config.dptLightKelvin === "7.600") {
180
+ if (msg.payload > 65535) msg.payload = 65535;
181
+ if (msg.payload < 0) msg.payload = 0;
182
+ retMirek = hueColorConverter.ColorConverter.scale(msg.payload, [0, 65535], [500, 153]);
183
+ } else if (config.dptLightKelvin === "9.002") {
184
+ // Relative temperature in Kelvin. Use HUE scale.
185
+ if (msg.payload > 6535) msg.payload = 6535;
186
+ if (msg.payload < 2000) msg.payload = 2000;
187
+ retMirek = hueColorConverter.ColorConverter.scale(msg.payload, [2000, 6535], [500, 153]);
188
+ }
189
+ state = { color_temperature: { mirek: retMirek } };
228
190
  node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
229
191
  node.setNodeStatusHue({
230
192
  fill: "green",
231
193
  shape: "dot",
232
194
  text: "KNX->HUE",
233
- payload: state,
195
+ payload: msg.payload,
234
196
  });
235
- }
236
- break;
237
- case config.GALightBrightness:
238
- msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightBrightness));
239
- state = { dimming: { brightness: msg.payload } };
240
- if (node.currentHUEDevice === undefined) {
241
- // Grouped light
242
- state.on = { on: msg.payload > 0 };
243
- } else {
244
- // Light
245
- if (node.currentHUEDevice.on.on === false && msg.payload > 0) state.on = { on: true };
246
- if (node.currentHUEDevice.on.on === true && msg.payload === 0) state.on = { on: false };
247
- }
248
- node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
249
- node.setNodeStatusHue({
250
- fill: "green",
251
- shape: "dot",
252
- text: "KNX->HUE",
253
- payload: state,
254
- });
255
- break;
256
- case config.GALightColor:
257
- msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightColor));
258
- let gamut = null;
259
- if (
260
- node.currentHUEDevice !== undefined
261
- && node.currentHUEDevice.hasOwnProperty("color")
262
- && node.currentHUEDevice.color.hasOwnProperty("gamut_type")
263
- ) {
264
- gamut = node.currentHUEDevice.color.gamut_type;
265
- }
266
- const retXY = hueColorConverter.ColorConverter.rgbToXy(msg.payload.red, msg.payload.green, msg.payload.blue, gamut);
267
- const bright = hueColorConverter.ColorConverter.getBrightnessFromRGBOrHex(msg.payload.red, msg.payload.green, msg.payload.blue);
268
- // state = bright > 0 ? { on: { on: true }, dimming: { brightness: bright }, color: { xy: retXY } } : { on: { on: false } }
269
- state = { dimming: { brightness: bright }, color: { xy: retXY } };
270
- if (node.currentHUEDevice === undefined) {
271
- // Grouped light
272
- state.on = { on: bright > 0 };
273
- } else {
274
- // Light
275
- if (node.currentHUEDevice.on.on === false && bright > 0) state.on = { on: true };
276
- if (node.currentHUEDevice.on.on === true && bright === 0) state = { on: { on: false }, dimming: { brightness: bright } };
277
- }
278
- node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
279
- node.setNodeStatusHue({
280
- fill: "green",
281
- shape: "dot",
282
- text: "KNX->HUE",
283
- payload: state,
284
- });
285
- break;
286
- case config.GALightBlink:
287
- const gaVal = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightBlink));
288
- if (gaVal) {
289
- node.timerBlink = setInterval(() => {
290
- if (node.blinkValue === undefined) node.blinkValue = true;
291
- node.blinkValue = !node.blinkValue;
292
- msg.payload = node.blinkValue;
293
- // state = msg.payload === true ? { on: { on: true } } : { on: { on: false } }
294
- state = msg.payload === true
295
- ? { on: { on: true }, dimming: { brightness: 100 }, dynamics: { duration: 0 } }
296
- : { on: { on: false }, dimming: { brightness: 0 }, dynamics: { duration: 0 } };
197
+ break;
198
+ case config.GADaylightSensor:
199
+ node.DayTime = Boolean(dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptDaylightSensor)));
200
+ if (config.invertDayNight !== undefined && config.invertDayNight === true) node.DayTime = !node.DayTime;
201
+ node.setNodeStatusHue({
202
+ fill: "green",
203
+ shape: "dot",
204
+ text: "KNX->HUE Daytime",
205
+ payload: node.DayTime,
206
+ });
207
+
208
+ break;
209
+ case config.GALightHSV:
210
+ if (config.dptLightHSV === "3.007") {
211
+ // MDT smartbutton will dim the color temperature
212
+ // { decr_incr: 1, data: 1 } : Start increasing until { decr_incr: 0, data: 0 } is received.
213
+ // { decr_incr: 0, data: 1 } : Start decreasing until { decr_incr: 0, data: 0 } is received.
214
+ msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightHSV));
215
+ node.hueDimmingTunableWhite(msg.payload.decr_incr, msg.payload.data, config.dimSpeed);
216
+ node.setNodeStatusHue({
217
+ fill: "green", shape: "dot", text: "KNX->HUE", payload: JSON.stringify(msg.payload),
218
+ });
219
+ }
220
+ break;
221
+ case config.GALightHSVPercentage:
222
+ if (config.dptLightHSVPercentage === "5.001") {
223
+ // 0-100% tunable white
224
+ msg.payload = 100 - dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightHSVPercentage));
225
+ // msg.payload = msg.payload <= 0 ? 1 : msg.payload
226
+ const retMirek = hueColorConverter.ColorConverter.scale(msg.payload, [0, 100], [153, 500]);
227
+ msg.payload = retMirek;
228
+ state = { color_temperature: { mirek: msg.payload } };
297
229
  node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
298
- }, 1000);
299
- } else {
300
- if (node.timerBlink !== undefined) clearInterval(node.timerBlink);
301
- node.serverHue.hueManager.writeHueQueueAdd(
302
- node.hueDevice,
303
- { on: { on: false } },
304
- node.isGrouped_light === false ? "setLight" : "setGroupedLight",
305
- );
306
- }
307
- node.setNodeStatusHue({
308
- fill: "green",
309
- shape: "dot",
310
- text: "KNX->HUE",
311
- payload: gaVal,
312
- });
313
- break;
314
- case config.GALightColorCycle:
315
- {
316
- if (node.timerColorCycle !== undefined) clearInterval(node.timerColorCycle);
317
- const gaValColorCycle = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightColorCycle));
318
- if (gaValColorCycle === true) {
319
- node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, { on: { on: true } }, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
320
- node.timerColorCycle = setInterval(() => {
321
- try {
322
- const red = getRandomIntInclusive(0, 255);
323
- const green = getRandomIntInclusive(0, 255);
324
- const blue = getRandomIntInclusive(0, 255);
325
- let gamut = null;
326
- if (
327
- node.currentHUEDevice !== undefined
328
- && node.currentHUEDevice.hasOwnProperty("color")
329
- && node.currentHUEDevice.color.hasOwnProperty("gamut_type")
330
- ) {
331
- gamut = node.currentHUEDevice.color.gamut_type;
332
- }
333
- const retXY = hueColorConverter.ColorConverter.rgbToXy(red, green, blue, gamut);
334
- const bright = hueColorConverter.ColorConverter.getBrightnessFromRGBOrHex(red, green, blue);
335
- state = bright > 0 ? { on: { on: true }, dimming: { brightness: bright }, color: { xy: retXY } } : { on: { on: false } };
336
- node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
337
- } catch (error) { }
338
- }, 10000);
230
+ node.setNodeStatusHue({
231
+ fill: "green",
232
+ shape: "dot",
233
+ text: "KNX->HUE",
234
+ payload: state,
235
+ });
236
+ }
237
+ break;
238
+ case config.GALightBrightness:
239
+ msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightBrightness));
240
+ state = { dimming: { brightness: msg.payload } };
241
+ if (node.currentHUEDevice === undefined) {
242
+ // Grouped light
243
+ state.on = { on: msg.payload > 0 };
339
244
  } else {
340
- if (node.timerColorCycle !== undefined) clearInterval(node.timerColorCycle);
245
+ // Light
246
+ if (node.currentHUEDevice.on.on === false && msg.payload > 0) state.on = { on: true };
247
+ if (node.currentHUEDevice.on.on === true && msg.payload === 0) state.on = { on: false };
248
+ }
249
+ node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
250
+ node.setNodeStatusHue({
251
+ fill: "green",
252
+ shape: "dot",
253
+ text: "KNX->HUE",
254
+ payload: state,
255
+ });
256
+ break;
257
+ case config.GALightColor:
258
+ msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightColor));
259
+ let gamut = null;
260
+ if (
261
+ node.currentHUEDevice !== undefined
262
+ && node.currentHUEDevice.color !== undefined
263
+ && node.currentHUEDevice.color.gamut_type !== undefined
264
+ ) {
265
+ gamut = node.currentHUEDevice.color.gamut_type;
266
+ }
267
+ const retXY = hueColorConverter.ColorConverter.rgbToXy(msg.payload.red, msg.payload.green, msg.payload.blue, gamut);
268
+ const bright = hueColorConverter.ColorConverter.getBrightnessFromRGBOrHex(msg.payload.red, msg.payload.green, msg.payload.blue);
269
+ // state = bright > 0 ? { on: { on: true }, dimming: { brightness: bright }, color: { xy: retXY } } : { on: { on: false } }
270
+ state = { dimming: { brightness: bright }, color: { xy: retXY } };
271
+ if (node.currentHUEDevice === undefined) {
272
+ // Grouped light
273
+ state.on = { on: bright > 0 };
274
+ } else {
275
+ // Light
276
+ if (node.currentHUEDevice.on.on === false && bright > 0) state.on = { on: true };
277
+ if (node.currentHUEDevice.on.on === true && bright === 0) state = { on: { on: false }, dimming: { brightness: bright } };
278
+ }
279
+ node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
280
+ node.setNodeStatusHue({
281
+ fill: "green",
282
+ shape: "dot",
283
+ text: "KNX->HUE",
284
+ payload: state,
285
+ });
286
+ break;
287
+ case config.GALightBlink:
288
+ const gaVal = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightBlink));
289
+ if (gaVal) {
290
+ node.timerBlink = setInterval(() => {
291
+ if (node.blinkValue === undefined) node.blinkValue = true;
292
+ node.blinkValue = !node.blinkValue;
293
+ msg.payload = node.blinkValue;
294
+ // state = msg.payload === true ? { on: { on: true } } : { on: { on: false } }
295
+ state = msg.payload === true
296
+ ? { on: { on: true }, dimming: { brightness: 100 }, dynamics: { duration: 0 } }
297
+ : { on: { on: false }, dimming: { brightness: 0 }, dynamics: { duration: 0 } };
298
+ node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
299
+ }, 1000);
300
+ } else {
301
+ if (node.timerBlink !== undefined) clearInterval(node.timerBlink);
341
302
  node.serverHue.hueManager.writeHueQueueAdd(
342
303
  node.hueDevice,
343
304
  { on: { on: false } },
@@ -348,16 +309,65 @@ module.exports = function (RED) {
348
309
  fill: "green",
349
310
  shape: "dot",
350
311
  text: "KNX->HUE",
351
- payload: gaValColorCycle,
312
+ payload: gaVal,
352
313
  });
353
- }
354
- break;
355
- default:
356
- break;
314
+ break;
315
+ case config.GALightColorCycle:
316
+ {
317
+ if (node.timerColorCycle !== undefined) clearInterval(node.timerColorCycle);
318
+ const gaValColorCycle = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightColorCycle));
319
+ if (gaValColorCycle === true) {
320
+ node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, { on: { on: true } }, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
321
+ node.timerColorCycle = setInterval(() => {
322
+ try {
323
+ const red = getRandomIntInclusive(0, 255);
324
+ const green = getRandomIntInclusive(0, 255);
325
+ const blue = getRandomIntInclusive(0, 255);
326
+ let gamut = null;
327
+ if (
328
+ node.currentHUEDevice !== undefined
329
+ && node.currentHUEDevice.color !== undefined
330
+ && node.currentHUEDevice.color.gamut_type !== undefined
331
+ ) {
332
+ gamut = node.currentHUEDevice.color.gamut_type;
333
+ }
334
+ const retXY = hueColorConverter.ColorConverter.rgbToXy(red, green, blue, gamut);
335
+ const bright = hueColorConverter.ColorConverter.getBrightnessFromRGBOrHex(red, green, blue);
336
+ state = bright > 0 ? { on: { on: true }, dimming: { brightness: bright }, color: { xy: retXY } } : { on: { on: false } };
337
+ node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
338
+ } catch (error) { }
339
+ }, 10000);
340
+ } else {
341
+ if (node.timerColorCycle !== undefined) clearInterval(node.timerColorCycle);
342
+ node.serverHue.hueManager.writeHueQueueAdd(
343
+ node.hueDevice,
344
+ { on: { on: false } },
345
+ node.isGrouped_light === false ? "setLight" : "setGroupedLight",
346
+ );
347
+ }
348
+ node.setNodeStatusHue({
349
+ fill: "green",
350
+ shape: "dot",
351
+ text: "KNX->HUE",
352
+ payload: gaValColorCycle,
353
+ });
354
+ }
355
+ break;
356
+ default:
357
+ break;
358
+ }
359
+ } catch (error) {
360
+ node.status({
361
+ fill: "red",
362
+ shape: "dot",
363
+ text: `KNX->HUE errorRead ${error.message} (${new Date().getDate()}, ${new Date().toLocaleTimeString()})`,
364
+ });
365
+ RED.log.error(`knxUltimateHueLight: node.handleSend: if (msg.knx.event !== "GroupValue_Read"): ${error.message} : ${error.stack || ""} `);
357
366
  }
367
+ }
358
368
 
359
- // I must respond to query requests (read request) sent from the KNX BUS
360
-
369
+ // I must respond to query requests (read request) sent from the KNX BUS
370
+ try {
361
371
  if (msg.knx.event === "GroupValue_Read" && node.currentHUEDevice !== undefined) {
362
372
  let ret;
363
373
  switch (msg.knx.destination) {
@@ -389,8 +399,9 @@ module.exports = function (RED) {
389
399
  node.status({
390
400
  fill: "red",
391
401
  shape: "dot",
392
- text: `KNX->HUE error ${error.message || error} (${new Date().getDate()}, ${new Date().toLocaleTimeString()})`,
402
+ text: `KNX->HUE error :-( ${error.message} (${new Date().getDate()}, ${new Date().toLocaleTimeString()})`,
393
403
  });
404
+ RED.log.error(`knxUltimateHueLight: node.handleSend: if (msg.knx.event === "GroupValue_Read" && node.currentHUEDevice !== undefined): ${error.message} : ${error.stack || ""} `);
394
405
  }
395
406
  };
396
407
 
@@ -431,7 +442,7 @@ module.exports = function (RED) {
431
442
  if (node.brightnessStep > maxDimLevelLight) node.brightnessStep = maxDimLevelLight;
432
443
  hueTelegram = { dimming: { brightness: node.brightnessStep }, dynamics: { duration: _dimSpeedInMillisecs } };
433
444
  // Switch on the light if off
434
- if (node.currentHUEDevice.hasOwnProperty("on") !== undefined && node.currentHUEDevice.on.on === false) {
445
+ if (node.currentHUEDevice.on !== undefined && node.currentHUEDevice.on.on === false) {
435
446
  hueTelegram.on = { on: true };
436
447
  }
437
448
  node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, hueTelegram, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
@@ -439,6 +450,7 @@ module.exports = function (RED) {
439
450
  }, _dimSpeedInMillisecs);
440
451
  }
441
452
  if (_KNXbrightness_delta > 0 && _KNXaction === 0) {
453
+ if (node.currentHUEDevice.on.on === false) return; // Don't dim down, if the light is already off.
442
454
  // DIM DOWN
443
455
  if (node.timerStepDim !== undefined) clearInterval(node.timerStepDim);
444
456
  node.timerStepDim = setInterval(() => {
@@ -447,7 +459,7 @@ module.exports = function (RED) {
447
459
  if (node.brightnessStep < minDimLevelLight) node.brightnessStep = minDimLevelLight;
448
460
  hueTelegram = { dimming: { brightness: node.brightnessStep }, dynamics: { duration: _dimSpeedInMillisecs } };
449
461
  // Switch off the light if on
450
- if (node.currentHUEDevice.hasOwnProperty("on") !== undefined && node.currentHUEDevice.on.on === true && node.brightnessStep === 0) {
462
+ if (node.currentHUEDevice.on !== undefined && node.currentHUEDevice.on.on === true && node.brightnessStep === 0) {
451
463
  hueTelegram.on = { on: false };
452
464
  }
453
465
  node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, hueTelegram, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
@@ -465,7 +477,7 @@ module.exports = function (RED) {
465
477
  let hueTelegram = {};
466
478
  _dimSpeedInMillisecs = _dimSpeedInMillisecs === undefined || _dimSpeedInMillisecs === "" ? 5000 : _dimSpeedInMillisecs;
467
479
  let delta = 0;
468
- if (!node.currentHUEDevice.color_temperature.hasOwnProperty("mirek")) delta = 347 - Math.round(173, 0); // Unable to read the mirek, set medium as default
480
+ if (node.currentHUEDevice.color_temperature.mirek === undefined) delta = 347 - Math.round(173, 0); // Unable to read the mirek, set medium as default
469
481
  // We have also minDimLevelLight and maxDimLevelLight to take care of.
470
482
  // Mirek limits are not taken in consideration.
471
483
  // Maximum mirek is 347
@@ -476,12 +488,12 @@ module.exports = function (RED) {
476
488
  return;
477
489
  }
478
490
  if (_KNXbrightness_delta > 0 && _KNXaction === 1) {
479
- if (node.currentHUEDevice.color_temperature.hasOwnProperty("mirek")) delta = 347 - Math.round(node.currentHUEDevice.color_temperature.mirek, 0);
491
+ if (node.currentHUEDevice.color_temperature.mirek !== undefined) delta = 347 - Math.round(node.currentHUEDevice.color_temperature.mirek, 0);
480
492
  dimDirection = "up";
481
493
  }
482
494
  if (_KNXbrightness_delta > 0 && _KNXaction === 0) {
483
495
  // Set the minumum delta, taking care of the minimum brightness specified either in the HUE lamp itself, or specified by the user (parameter node.minDimLevelLight)
484
- if (node.currentHUEDevice.color_temperature.hasOwnProperty("mirek")) delta = Math.round(node.currentHUEDevice.color_temperature.mirek, 0);
496
+ if (node.currentHUEDevice.color_temperature.mirek !== undefined) delta = Math.round(node.currentHUEDevice.color_temperature.mirek, 0);
485
497
  dimDirection = "down";
486
498
  }
487
499
  // Calculate the dimming time based on delta
@@ -491,7 +503,7 @@ module.exports = function (RED) {
491
503
 
492
504
  hueTelegram = { color_temperature_delta: { action: dimDirection, mirek_delta: delta }, dynamics: { duration: _dimSpeedInMillisecs } };
493
505
  // Switch on the light if off
494
- if (node.currentHUEDevice.hasOwnProperty("on") !== undefined && node.currentHUEDevice.on.on === false && dimDirection === "up") {
506
+ if (node.currentHUEDevice.on !== undefined && node.currentHUEDevice.on.on === false && dimDirection === "up") {
495
507
  hueTelegram.on = { on: true };
496
508
  }
497
509
  node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, hueTelegram, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
@@ -536,7 +548,7 @@ module.exports = function (RED) {
536
548
  } catch (error) { }
537
549
  }
538
550
 
539
- if (_event.hasOwnProperty("on")) {
551
+ if (_event.on !== undefined) {
540
552
  node.updateKNXLightState(_event.on.on);
541
553
  // In case of switch off, set the dim to zero
542
554
  if (_event.on.on === false && (config.updateKNXBrightnessStatusOnHUEOnOff === undefined || config.updateKNXBrightnessStatusOnHUEOnOff === "onhueoff")) {
@@ -544,7 +556,7 @@ module.exports = function (RED) {
544
556
  //node.currentHUEDevice.dimming.brightness = 0;
545
557
  } else if (_event.on.on === true && node.currentHUEDevice.on.on === false) {
546
558
  // Turn on always update the dimming KNX Status value as well.
547
- let brightVal = 100;
559
+ let brightVal = 50;
548
560
  if (node.currentHUEDevice.dimming !== undefined && node.currentHUEDevice.dimming.brightness !== undefined) brightVal = node.currentHUEDevice.dimming.brightness;
549
561
  node.updateKNXBrightnessState(brightVal);
550
562
  }
@@ -556,17 +568,17 @@ module.exports = function (RED) {
556
568
  node.currentHUEDevice.color = _event.color;
557
569
  }
558
570
 
559
- if (_event.hasOwnProperty("dimming") && _event.dimming.brightness !== undefined) {
571
+ if (_event.dimming !== undefined && _event.dimming.brightness !== undefined) {
560
572
  // Once upon n a time, the light transmit the brightness value of 0.39.
561
573
  // To avoid wrongly turn light state on, exit
562
574
  if (_event.dimming.brightness < 1) _event.dimming.brightness = 0;
563
- if (node.currentHUEDevice.hasOwnProperty("on") && node.currentHUEDevice.on.on === false && _event.dimming.brightness === 0) {
575
+ if (node.currentHUEDevice.on !== undefined && node.currentHUEDevice.on.on === false && _event.dimming.brightness === 0) {
564
576
  // Do nothing, because the light is off and the dimming also is 0
565
577
  } else {
566
- if (node.currentHUEDevice.hasOwnProperty("on") && node.currentHUEDevice.on.on === false && (!_event.hasOwnProperty("on") || (_event.hasOwnProperty("on") && _event.on.on === true))) node.updateKNXLightState(_event.dimming.brightness > 0);
578
+ if (node.currentHUEDevice.on !== undefined && node.currentHUEDevice.on.on === false && (!_event.on !== undefined || (_event.on !== undefined && _event.on.on === true))) node.updateKNXLightState(_event.dimming.brightness > 0);
567
579
  node.updateKNXBrightnessState(_event.dimming.brightness);
568
580
  // If the brightness reaches zero, the hue lamp "on" property must be set to zero as well
569
- if (_event.dimming.brightness === 0 && node.currentHUEDevice.hasOwnProperty("on") && node.currentHUEDevice.on.on === true) {
581
+ if (_event.dimming.brightness === 0 && node.currentHUEDevice.on !== undefined && node.currentHUEDevice.on.on === true) {
570
582
  node.serverHue.hueManager.writeHueQueueAdd(
571
583
  node.hueDevice,
572
584
  { on: { on: false } },
@@ -577,7 +589,7 @@ module.exports = function (RED) {
577
589
  node.currentHUEDevice.dimming.brightness = _event.dimming.brightness;
578
590
  }
579
591
  }
580
- if (_event.hasOwnProperty("color_temperature") && _event.color_temperature.mirek !== undefined) {
592
+ if (_event.color_temperature !== undefined && _event.color_temperature.mirek !== undefined) {
581
593
  node.updateKNXLightHSVState(_event.color_temperature.mirek);
582
594
  node.updateKNXLightKelvinState(_event.color_temperature.mirek);
583
595
  node.currentHUEDevice.color_temperature.mirek = _event.color_temperature.mirek;
@@ -587,7 +599,7 @@ module.exports = function (RED) {
587
599
  node.status({
588
600
  fill: "red",
589
601
  shape: "dot",
590
- text: `HUE->KNX error ${node.id} ${error.message}`,
602
+ text: `HUE->KNX error ${node.id} ${error.message}. Seee Log`,
591
603
  });
592
604
  RED.log.error(`knxUltimateHueLight: node.handleSendHUE = (_event): ${error.message}`);
593
605
  }
@@ -686,7 +698,7 @@ module.exports = function (RED) {
686
698
 
687
699
  node.updateKNXLightColorState = function updateKNXLightColorState(_value, _outputtype = "write") {
688
700
  if (config.GALightColorState !== undefined && config.GALightColorState !== "") {
689
- if (!_value.hasOwnProperty('xy') || _value.xy.x === undefined) return;
701
+ if (_value.xy === undefined || _value.xy.x === undefined) return;
690
702
  const knxMsgPayload = {};
691
703
  knxMsgPayload.topic = config.GALightColorState;
692
704
  knxMsgPayload.dpt = config.dptLightColorState;
@@ -800,6 +812,6 @@ module.exports = function (RED) {
800
812
  }
801
813
  done();
802
814
  });
803
- }
815
+ };
804
816
  RED.nodes.registerType("knxUltimateHueLight", knxUltimateHueLight);
805
817
  };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "engines": {
4
4
  "node": ">=16.0.0"
5
5
  },
6
- "version": "2.2.27",
6
+ "version": "2.2.29",
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",