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 +11 -0
- package/nodes/hue-config.js +13 -10
- package/nodes/knxUltimateHueLight.js +260 -248
- package/package.json +1 -1
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/>
|
package/nodes/hue-config.js
CHANGED
|
@@ -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
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (node.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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:
|
|
156
|
+
state = { on: { on: false } };
|
|
153
157
|
}
|
|
154
|
-
} else {
|
|
155
|
-
state = { on: { on: false } };
|
|
156
|
-
}
|
|
157
158
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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.
|
|
214
|
-
node.
|
|
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
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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:
|
|
195
|
+
payload: msg.payload,
|
|
234
196
|
});
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
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
|
-
|
|
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:
|
|
312
|
+
payload: gaVal,
|
|
352
313
|
});
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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 (
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 =
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 (
|
|
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.
|
|
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",
|