node-red-contrib-knx-ultimate 2.2.2 → 2.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/nodes/hue-config.js +79 -78
- package/nodes/knxUltimate-config.js +6 -4
- package/nodes/knxUltimateHueBattery.js +69 -68
- package/nodes/knxUltimateHueButton.js +1 -1
- package/nodes/knxUltimateHueLight.html +25 -5
- package/nodes/knxUltimateHueLight.js +168 -138
- package/nodes/knxUltimateHueLightSensor.js +53 -62
- package/nodes/knxUltimateHueMotion.js +4 -4
- package/nodes/knxUltimateHueScene.js +1 -1
- package/nodes/knxUltimateHueTapDial.js +1 -1
- package/nodes/knxUltimateHueTemperatureSensor.js +70 -71
- package/nodes/utils/hueEngine.js +57 -42
- package/package.json +1 -1
|
@@ -37,29 +37,30 @@ module.exports = function (RED) {
|
|
|
37
37
|
node.hueDevice = config.hueDevice.split("#")[0];
|
|
38
38
|
|
|
39
39
|
// Used to call the status update from the config node.
|
|
40
|
-
node.setNodeStatus = ({
|
|
40
|
+
node.setNodeStatus = ({
|
|
41
|
+
fill, shape, text, payload,
|
|
42
|
+
}) => { };
|
|
41
43
|
// Used to call the status update from the HUE config node.
|
|
42
44
|
node.setNodeStatusHue = ({ fill, shape, text, payload }) => {
|
|
43
|
-
if (payload === undefined)
|
|
45
|
+
if (payload === undefined) payload = '';
|
|
44
46
|
const dDate = new Date();
|
|
45
47
|
payload = typeof payload === "object" ? JSON.stringify(payload) : payload.toString();
|
|
46
48
|
node.status({ fill, shape, text: `${text} ${payload} (${dDate.getDate()}, ${dDate.toLocaleTimeString()})` });
|
|
47
49
|
};
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
});
|
|
51
|
+
function getRandomIntInclusive(min, max) {
|
|
52
|
+
min = Math.ceil(min);
|
|
53
|
+
max = Math.floor(max);
|
|
54
|
+
return Math.floor(Math.random() * (max - min + 1) + min); // The maximum is inclusive and the minimum is inclusive
|
|
55
|
+
}
|
|
55
56
|
|
|
56
57
|
// This function is called by the hue-config.js
|
|
57
58
|
node.handleSend = (msg) => {
|
|
58
59
|
if (node.currentHUEDevice === undefined) {
|
|
59
60
|
node.setNodeStatusHue({
|
|
60
|
-
fill: "
|
|
61
|
+
fill: "grey",
|
|
61
62
|
shape: "ring",
|
|
62
|
-
text: "
|
|
63
|
+
text: "Currently not ready.",
|
|
63
64
|
payload: "",
|
|
64
65
|
});
|
|
65
66
|
return;
|
|
@@ -89,9 +90,9 @@ module.exports = function (RED) {
|
|
|
89
90
|
if (jColorChoosen !== null) {
|
|
90
91
|
let gamut = null;
|
|
91
92
|
if (
|
|
92
|
-
node.currentHUEDevice !== undefined
|
|
93
|
-
node.currentHUEDevice.hasOwnProperty("color")
|
|
94
|
-
node.currentHUEDevice.color.hasOwnProperty("gamut_type")
|
|
93
|
+
node.currentHUEDevice !== undefined
|
|
94
|
+
&& node.currentHUEDevice.hasOwnProperty("color")
|
|
95
|
+
&& node.currentHUEDevice.color.hasOwnProperty("gamut_type")
|
|
95
96
|
) {
|
|
96
97
|
gamut = node.currentHUEDevice.color.gamut_type;
|
|
97
98
|
}
|
|
@@ -101,7 +102,7 @@ module.exports = function (RED) {
|
|
|
101
102
|
node.updateKNXBrightnessState(node.currentHUEDevice.dimming.brightness);
|
|
102
103
|
state = dbright > 0 ? { on: { on: true }, dimming: { brightness: dbright }, color: { xy: dretXY } } : { on: { on: false } };
|
|
103
104
|
} else {
|
|
104
|
-
state = { on: { on: true }
|
|
105
|
+
state = { on: { on: true } };
|
|
105
106
|
}
|
|
106
107
|
} else {
|
|
107
108
|
state = { on: { on: false } };
|
|
@@ -119,7 +120,9 @@ module.exports = function (RED) {
|
|
|
119
120
|
// { decr_incr: 0, data: 1 } : Start decreasing until { decr_incr: 0, data: 0 } is received.
|
|
120
121
|
msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightDIM));
|
|
121
122
|
node.hueDimming(msg.payload.decr_incr, msg.payload.data, config.dimSpeed);
|
|
122
|
-
node.setNodeStatusHue({
|
|
123
|
+
node.setNodeStatusHue({
|
|
124
|
+
fill: "green", shape: "dot", text: "KNX->HUE", payload: JSON.stringify(msg.payload),
|
|
125
|
+
});
|
|
123
126
|
break;
|
|
124
127
|
case config.GADaylightSensor:
|
|
125
128
|
if (config.enableDayNightLighting === "yes") {
|
|
@@ -142,7 +145,9 @@ module.exports = function (RED) {
|
|
|
142
145
|
// { decr_incr: 0, data: 1 } : Start decreasing until { decr_incr: 0, data: 0 } is received.
|
|
143
146
|
msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightHSV));
|
|
144
147
|
node.hueDimmingTunableWhite(msg.payload.decr_incr, msg.payload.data, config.dimSpeed);
|
|
145
|
-
node.setNodeStatusHue({
|
|
148
|
+
node.setNodeStatusHue({
|
|
149
|
+
fill: "green", shape: "dot", text: "KNX->HUE", payload: JSON.stringify(msg.payload),
|
|
150
|
+
});
|
|
146
151
|
}
|
|
147
152
|
break;
|
|
148
153
|
case config.GALightHSVPercentage:
|
|
@@ -191,9 +196,9 @@ module.exports = function (RED) {
|
|
|
191
196
|
msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightColor));
|
|
192
197
|
let gamut = null;
|
|
193
198
|
if (
|
|
194
|
-
node.currentHUEDevice !== undefined
|
|
195
|
-
node.currentHUEDevice.hasOwnProperty("color")
|
|
196
|
-
node.currentHUEDevice.color.hasOwnProperty("gamut_type")
|
|
199
|
+
node.currentHUEDevice !== undefined
|
|
200
|
+
&& node.currentHUEDevice.hasOwnProperty("color")
|
|
201
|
+
&& node.currentHUEDevice.color.hasOwnProperty("gamut_type")
|
|
197
202
|
) {
|
|
198
203
|
gamut = node.currentHUEDevice.color.gamut_type;
|
|
199
204
|
}
|
|
@@ -225,10 +230,9 @@ module.exports = function (RED) {
|
|
|
225
230
|
node.blinkValue = !node.blinkValue;
|
|
226
231
|
msg.payload = node.blinkValue;
|
|
227
232
|
// state = msg.payload === true ? { on: { on: true } } : { on: { on: false } }
|
|
228
|
-
state =
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
: { on: { on: false }, dimming: { brightness: 0 }, dynamics: { duration: 0 } };
|
|
233
|
+
state = msg.payload === true
|
|
234
|
+
? { on: { on: true }, dimming: { brightness: 100 }, dynamics: { duration: 0 } }
|
|
235
|
+
: { on: { on: false }, dimming: { brightness: 0 }, dynamics: { duration: 0 } };
|
|
232
236
|
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
233
237
|
}, 1000);
|
|
234
238
|
} else {
|
|
@@ -236,7 +240,7 @@ module.exports = function (RED) {
|
|
|
236
240
|
node.serverHue.hueManager.writeHueQueueAdd(
|
|
237
241
|
node.hueDevice,
|
|
238
242
|
{ on: { on: false } },
|
|
239
|
-
node.isGrouped_light === false ? "setLight" : "setGroupedLight"
|
|
243
|
+
node.isGrouped_light === false ? "setLight" : "setGroupedLight",
|
|
240
244
|
);
|
|
241
245
|
}
|
|
242
246
|
node.setNodeStatusHue({
|
|
@@ -247,48 +251,45 @@ module.exports = function (RED) {
|
|
|
247
251
|
});
|
|
248
252
|
break;
|
|
249
253
|
case config.GALightColorCycle:
|
|
250
|
-
|
|
251
|
-
const gaValColorCycle = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightColorCycle));
|
|
252
|
-
if (gaValColorCycle === true) {
|
|
253
|
-
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, { on: { on: true } }, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
254
|
-
node.timerColorCycle = setInterval(() => {
|
|
255
|
-
try {
|
|
256
|
-
function getRandomIntInclusive(min, max) {
|
|
257
|
-
min = Math.ceil(min);
|
|
258
|
-
max = Math.floor(max);
|
|
259
|
-
return Math.floor(Math.random() * (max - min + 1) + min); // The maximum is inclusive and the minimum is inclusive
|
|
260
|
-
}
|
|
261
|
-
const red = getRandomIntInclusive(0, 255);
|
|
262
|
-
const green = getRandomIntInclusive(0, 255);
|
|
263
|
-
const blue = getRandomIntInclusive(0, 255);
|
|
264
|
-
let gamut = null;
|
|
265
|
-
if (
|
|
266
|
-
node.currentHUEDevice !== undefined &&
|
|
267
|
-
node.currentHUEDevice.hasOwnProperty("color") &&
|
|
268
|
-
node.currentHUEDevice.color.hasOwnProperty("gamut_type")
|
|
269
|
-
) {
|
|
270
|
-
gamut = node.currentHUEDevice.color.gamut_type;
|
|
271
|
-
}
|
|
272
|
-
const retXY = hueColorConverter.ColorConverter.rgbToXy(red, green, blue, gamut);
|
|
273
|
-
const bright = hueColorConverter.ColorConverter.getBrightnessFromRGB(red, green, blue);
|
|
274
|
-
state = bright > 0 ? { on: { on: true }, dimming: { brightness: bright }, color: { xy: retXY } } : { on: { on: false } };
|
|
275
|
-
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
276
|
-
} catch (error) { }
|
|
277
|
-
}, 10000);
|
|
278
|
-
} else {
|
|
254
|
+
{
|
|
279
255
|
if (node.timerColorCycle !== undefined) clearInterval(node.timerColorCycle);
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
{ on: { on:
|
|
283
|
-
node.
|
|
284
|
-
|
|
256
|
+
const gaValColorCycle = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightColorCycle));
|
|
257
|
+
if (gaValColorCycle === true) {
|
|
258
|
+
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, { on: { on: true } }, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
259
|
+
node.timerColorCycle = setInterval(() => {
|
|
260
|
+
try {
|
|
261
|
+
const red = getRandomIntInclusive(0, 255);
|
|
262
|
+
const green = getRandomIntInclusive(0, 255);
|
|
263
|
+
const blue = getRandomIntInclusive(0, 255);
|
|
264
|
+
let gamut = null;
|
|
265
|
+
if (
|
|
266
|
+
node.currentHUEDevice !== undefined
|
|
267
|
+
&& node.currentHUEDevice.hasOwnProperty("color")
|
|
268
|
+
&& node.currentHUEDevice.color.hasOwnProperty("gamut_type")
|
|
269
|
+
) {
|
|
270
|
+
gamut = node.currentHUEDevice.color.gamut_type;
|
|
271
|
+
}
|
|
272
|
+
const retXY = hueColorConverter.ColorConverter.rgbToXy(red, green, blue, gamut);
|
|
273
|
+
const bright = hueColorConverter.ColorConverter.getBrightnessFromRGB(red, green, blue);
|
|
274
|
+
state = bright > 0 ? { on: { on: true }, dimming: { brightness: bright }, color: { xy: retXY } } : { on: { on: false } };
|
|
275
|
+
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
276
|
+
} catch (error) { }
|
|
277
|
+
}, 10000);
|
|
278
|
+
} else {
|
|
279
|
+
if (node.timerColorCycle !== undefined) clearInterval(node.timerColorCycle);
|
|
280
|
+
node.serverHue.hueManager.writeHueQueueAdd(
|
|
281
|
+
node.hueDevice,
|
|
282
|
+
{ on: { on: false } },
|
|
283
|
+
node.isGrouped_light === false ? "setLight" : "setGroupedLight",
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
node.setNodeStatusHue({
|
|
287
|
+
fill: "green",
|
|
288
|
+
shape: "dot",
|
|
289
|
+
text: "KNX->HUE",
|
|
290
|
+
payload: gaValColorCycle,
|
|
291
|
+
});
|
|
285
292
|
}
|
|
286
|
-
node.setNodeStatusHue({
|
|
287
|
-
fill: "green",
|
|
288
|
-
shape: "dot",
|
|
289
|
-
text: "KNX->HUE",
|
|
290
|
-
payload: gaValColorCycle,
|
|
291
|
-
});
|
|
292
293
|
break;
|
|
293
294
|
default:
|
|
294
295
|
break;
|
|
@@ -305,42 +306,63 @@ module.exports = function (RED) {
|
|
|
305
306
|
// Start dimming
|
|
306
307
|
// ***********************************************************
|
|
307
308
|
node.hueDimming = function hueDimming(_KNXaction, _KNXbrightness_delta, _dimSpeedInMillisecs = undefined) {
|
|
308
|
-
|
|
309
|
+
// 31/10/2023 after many attempts to use dimming_delta function of the HueApeV2, loosing days of my life, without a decent success
|
|
310
|
+
// i decide to go to the "step brightness" way.
|
|
309
311
|
let hueTelegram = {};
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
// We have also minDimLevelLight and maxDimLevelLight to take care of.
|
|
314
|
-
let minDimLevelLight = config.minDimLevelLight === undefined ? 10 : config.minDimLevelLight;
|
|
315
|
-
if (config.minDimLevelLight === "useHueLightLevel" && node.currentHUEDevice.dimming.min_dim_level === undefined) minDimLevelLight = 10;
|
|
316
|
-
const maxDimLevelLight = config.maxDimLevelLight === undefined ? 100 : config.maxDimLevelLight;
|
|
312
|
+
const numStep = 10; // Steps from 0 to 100 by 10
|
|
313
|
+
if (_dimSpeedInMillisecs === undefined || _dimSpeedInMillisecs === '') _dimSpeedInMillisecs = 5000;
|
|
317
314
|
|
|
318
315
|
if (_KNXbrightness_delta === 0 && _KNXaction === 0) {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
node.
|
|
316
|
+
// STOP DIM
|
|
317
|
+
if (node.timerStepDim !== undefined) clearInterval(node.timerStepDim);
|
|
318
|
+
node.brightnessStep = null;
|
|
322
319
|
return;
|
|
323
320
|
}
|
|
321
|
+
|
|
322
|
+
// Set the actual brightness to start with
|
|
323
|
+
if (node.brightnessStep === null || node.brightnessStep === undefined) node.brightnessStep = node.currentHUEDevice.dimming.brightness || 50;
|
|
324
|
+
node.brightnessStep = Math.ceil(Number(node.brightnessStep));
|
|
325
|
+
|
|
326
|
+
// Set the speed
|
|
327
|
+
_dimSpeedInMillisecs /= numStep;
|
|
328
|
+
|
|
329
|
+
// We have also minDimLevelLight and maxDimLevelLight to take care of.
|
|
330
|
+
let minDimLevelLight = config.minDimLevelLight === undefined ? 10 : Number(config.minDimLevelLight);
|
|
331
|
+
if (config.minDimLevelLight === "useHueLightLevel" && node.currentHUEDevice.dimming.min_dim_level === undefined) minDimLevelLight = 10;
|
|
332
|
+
const maxDimLevelLight = config.maxDimLevelLight === undefined ? 100 : Number(config.maxDimLevelLight);
|
|
333
|
+
|
|
324
334
|
if (_KNXbrightness_delta > 0 && _KNXaction === 1) {
|
|
325
|
-
|
|
326
|
-
|
|
335
|
+
// DIM UP
|
|
336
|
+
if (node.timerStepDim !== undefined) clearInterval(node.timerStepDim);
|
|
337
|
+
node.timerStepDim = setInterval(() => {
|
|
338
|
+
node.updateKNXBrightnessState(node.brightnessStep); // Unnecessary, but necessary to set the KNX Status in real time.
|
|
339
|
+
node.brightnessStep += numStep;
|
|
340
|
+
if (node.brightnessStep > maxDimLevelLight) node.brightnessStep = maxDimLevelLight;
|
|
341
|
+
hueTelegram = { dimming: { brightness: node.brightnessStep }, dynamics: { duration: _dimSpeedInMillisecs } };
|
|
342
|
+
// Switch on the light if off
|
|
343
|
+
if (node.currentHUEDevice.hasOwnProperty("on") !== undefined && node.currentHUEDevice.on.on === false) {
|
|
344
|
+
hueTelegram.on = { on: true };
|
|
345
|
+
}
|
|
346
|
+
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, hueTelegram, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
347
|
+
if (node.brightnessStep >= maxDimLevelLight) clearInterval(node.timerStepDim);
|
|
348
|
+
}, _dimSpeedInMillisecs);
|
|
327
349
|
}
|
|
328
350
|
if (_KNXbrightness_delta > 0 && _KNXaction === 0) {
|
|
329
|
-
//
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
351
|
+
// DIM DOWN
|
|
352
|
+
if (node.timerStepDim !== undefined) clearInterval(node.timerStepDim);
|
|
353
|
+
node.timerStepDim = setInterval(() => {
|
|
354
|
+
node.updateKNXBrightnessState(node.brightnessStep); // Unnecessary, but necessary to set the KNX Status in real time.
|
|
355
|
+
node.brightnessStep -= numStep;
|
|
356
|
+
if (node.brightnessStep < minDimLevelLight) node.brightnessStep = minDimLevelLight;
|
|
357
|
+
hueTelegram = { dimming: { brightness: node.brightnessStep }, dynamics: { duration: _dimSpeedInMillisecs } };
|
|
358
|
+
// Switch off the light if on
|
|
359
|
+
if (node.currentHUEDevice.hasOwnProperty("on") !== undefined && node.currentHUEDevice.on.on === true && node.brightnessStep === 0) {
|
|
360
|
+
hueTelegram.on = { on: false };
|
|
361
|
+
}
|
|
362
|
+
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, hueTelegram, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
363
|
+
if (node.brightnessStep <= minDimLevelLight) clearInterval(node.timerStepDim);
|
|
364
|
+
}, _dimSpeedInMillisecs);
|
|
342
365
|
}
|
|
343
|
-
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, hueTelegram, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
344
366
|
};
|
|
345
367
|
// ***********************************************************
|
|
346
368
|
|
|
@@ -401,57 +423,69 @@ module.exports = function (RED) {
|
|
|
401
423
|
node.updateKNXLightState(_event.on.on);
|
|
402
424
|
// In case of switch off, set the dim to zero
|
|
403
425
|
if (
|
|
404
|
-
_event.on.on === false
|
|
405
|
-
(config.updateKNXBrightnessStatusOnHUEOnOff === undefined || config.updateKNXBrightnessStatusOnHUEOnOff === "onhueoff")
|
|
426
|
+
_event.on.on === false
|
|
427
|
+
&& (config.updateKNXBrightnessStatusOnHUEOnOff === undefined || config.updateKNXBrightnessStatusOnHUEOnOff === "onhueoff")
|
|
406
428
|
) {
|
|
407
429
|
node.updateKNXBrightnessState(0);
|
|
430
|
+
node.currentHUEDevice.dimming.brightness = 0;
|
|
408
431
|
} else {
|
|
409
|
-
// Sends the previous brightness value
|
|
410
|
-
try {
|
|
411
|
-
|
|
412
|
-
} catch (error) {
|
|
413
|
-
|
|
414
|
-
}
|
|
432
|
+
// // Sends the previous brightness value
|
|
433
|
+
// try {
|
|
434
|
+
// node.updateKNXBrightnessState(node.currentHUEDevice.dimming.brightness);
|
|
435
|
+
// } catch (error) {
|
|
436
|
+
// /* empty */
|
|
437
|
+
// }
|
|
415
438
|
}
|
|
439
|
+
node.currentHUEDevice.on.on = _event.on.on;
|
|
416
440
|
}
|
|
417
441
|
if (_event.hasOwnProperty("color")) {
|
|
418
|
-
|
|
442
|
+
if (_event.type !== 'grouped_light') {
|
|
443
|
+
// In grouped lights, there is group color, but each light has it's own color.
|
|
444
|
+
node.updateKNXLightColorState(_event.color);
|
|
445
|
+
node.currentHUEDevice.color = _event.color;
|
|
446
|
+
}
|
|
419
447
|
}
|
|
420
|
-
if (_event.hasOwnProperty("dimming")) {
|
|
448
|
+
if (_event.hasOwnProperty("dimming") && _event.dimming.brightness !== undefined) {
|
|
421
449
|
// Every once on a time, the light transmit the brightness value of 0.39.
|
|
422
450
|
// To avoid wrongly turn light state on, exit
|
|
423
451
|
if (_event.dimming.brightness < 1) _event.dimming.brightness = 0;
|
|
424
|
-
if (node.currentHUEDevice.hasOwnProperty("on") && node.currentHUEDevice.on.on === false && _event.dimming.brightness === 0)
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
452
|
+
if (node.currentHUEDevice.hasOwnProperty("on") && node.currentHUEDevice.on.on === false && _event.dimming.brightness === 0) {
|
|
453
|
+
// Do nothing, because the light is off and the dimming also is 0
|
|
454
|
+
} else {
|
|
455
|
+
if (node.currentHUEDevice.on.on === false) node.updateKNXLightState(_event.dimming.brightness > 0);
|
|
456
|
+
node.updateKNXBrightnessState(_event.dimming.brightness);
|
|
457
|
+
// If the brightness reaches zero, the hue lamp "on" property must be set to zero as well
|
|
458
|
+
if (_event.dimming.brightness === 0) {
|
|
459
|
+
node.serverHue.hueManager.writeHueQueueAdd(
|
|
460
|
+
node.hueDevice,
|
|
461
|
+
{ on: { on: false } },
|
|
462
|
+
node.isGrouped_light === false ? "setLight" : "setGroupedLight",
|
|
463
|
+
);
|
|
464
|
+
node.currentHUEDevice.on.on = false;
|
|
465
|
+
}
|
|
466
|
+
node.currentHUEDevice.dimming.brightness = _event.dimming.brightness;
|
|
434
467
|
}
|
|
435
468
|
}
|
|
436
|
-
if (_event.hasOwnProperty("color_temperature")) {
|
|
469
|
+
if (_event.hasOwnProperty("color_temperature") && _event.color_temperature.mirek !== undefined) {
|
|
437
470
|
node.updateKNXLightHSVState(_event.color_temperature.mirek);
|
|
471
|
+
node.currentHUEDevice.color_temperature.mirek = _event.color_temperature.mirek;
|
|
438
472
|
}
|
|
439
473
|
|
|
440
|
-
// Update the current HUE Device with the new _event
|
|
441
|
-
function copiaOggettoRicorsivo(objDestinazione, objOrigine) {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
}
|
|
453
|
-
// Copia l'oggettoOrigine nell'oggettoDestinazione mantenendo le proprietà esistenti
|
|
454
|
-
copiaOggettoRicorsivo(node.currentHUEDevice, _event);
|
|
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);
|
|
455
489
|
}
|
|
456
490
|
} catch (error) {
|
|
457
491
|
node.status({
|
|
@@ -482,7 +516,7 @@ module.exports = function (RED) {
|
|
|
482
516
|
node.setNodeStatusHue({
|
|
483
517
|
fill: "blue",
|
|
484
518
|
shape: "ring",
|
|
485
|
-
text: "HUE->KNX
|
|
519
|
+
text: "HUE->KNX Bright",
|
|
486
520
|
payload: knxMsgPayload.payload,
|
|
487
521
|
});
|
|
488
522
|
}
|
|
@@ -510,7 +544,7 @@ module.exports = function (RED) {
|
|
|
510
544
|
node.setNodeStatusHue({
|
|
511
545
|
fill: "blue",
|
|
512
546
|
shape: "ring",
|
|
513
|
-
text: "HUE->KNX
|
|
547
|
+
text: "HUE->KNX On/Off",
|
|
514
548
|
payload: knxMsgPayload.payload,
|
|
515
549
|
});
|
|
516
550
|
}
|
|
@@ -543,7 +577,7 @@ module.exports = function (RED) {
|
|
|
543
577
|
node.setNodeStatusHue({
|
|
544
578
|
fill: "blue",
|
|
545
579
|
shape: "ring",
|
|
546
|
-
text: "HUE->KNX
|
|
580
|
+
text: "HUE->KNX HSV",
|
|
547
581
|
payload: knxMsgPayload.payload,
|
|
548
582
|
});
|
|
549
583
|
}
|
|
@@ -557,7 +591,7 @@ module.exports = function (RED) {
|
|
|
557
591
|
knxMsgPayload.payload = hueColorConverter.ColorConverter.xyBriToRgb(
|
|
558
592
|
_value.xy.x,
|
|
559
593
|
_value.xy.y,
|
|
560
|
-
node.currentHUEDevice !== undefined ? node.currentHUEDevice.dimming.brightness : 100
|
|
594
|
+
node.currentHUEDevice !== undefined ? node.currentHUEDevice.dimming.brightness : 100,
|
|
561
595
|
);
|
|
562
596
|
// Send to KNX bus
|
|
563
597
|
if (knxMsgPayload.topic !== "" && knxMsgPayload.topic !== undefined) {
|
|
@@ -572,12 +606,14 @@ module.exports = function (RED) {
|
|
|
572
606
|
node.setNodeStatusHue({
|
|
573
607
|
fill: "blue",
|
|
574
608
|
shape: "ring",
|
|
575
|
-
text: "HUE->KNX
|
|
609
|
+
text: "HUE->KNX Color",
|
|
576
610
|
payload: knxMsgPayload.payload,
|
|
577
611
|
});
|
|
578
612
|
}
|
|
579
613
|
};
|
|
580
614
|
|
|
615
|
+
|
|
616
|
+
|
|
581
617
|
// On each deploy, unsubscribe+resubscribe
|
|
582
618
|
if (node.server) {
|
|
583
619
|
node.server.removeClient(node);
|
|
@@ -586,12 +622,6 @@ module.exports = function (RED) {
|
|
|
586
622
|
if (node.serverHue) {
|
|
587
623
|
node.serverHue.removeClient(node);
|
|
588
624
|
if (node.serverHue !== null && node.serverHue.hueManager !== null) {
|
|
589
|
-
try {
|
|
590
|
-
// Everytime the node is edited, it comes here to read the currentHueDevice, if any.
|
|
591
|
-
// It comes here also at node-red boot, but at node-red boot, the hueAllResources is null.
|
|
592
|
-
// The hue-config take cares of filling all properties of the node, after it has been connected to the hue Brigde.
|
|
593
|
-
if (node.serverHue.hueAllResources !== null) node.currentHUEDevice = node.serverHue.loadResourcesFromHUEBridge(node);
|
|
594
|
-
} catch (error) { }
|
|
595
625
|
try {
|
|
596
626
|
node.serverHue.addClient(node);
|
|
597
627
|
} catch (err) {
|
|
@@ -1,110 +1,101 @@
|
|
|
1
1
|
module.exports = function (RED) {
|
|
2
2
|
function knxUltimateHueLightSensor(config) {
|
|
3
|
-
RED.nodes.createNode(this, config)
|
|
4
|
-
const node = this
|
|
5
|
-
node.server = RED.nodes.getNode(config.server)
|
|
6
|
-
node.serverHue = RED.nodes.getNode(config.serverHue)
|
|
7
|
-
node.topic = node.name
|
|
8
|
-
node.name = config.name === undefined ? 'Hue' : config.name
|
|
9
|
-
node.dpt = ''
|
|
10
|
-
node.notifyreadrequest = false
|
|
11
|
-
node.notifyreadrequestalsorespondtobus = 'false'
|
|
12
|
-
node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = ''
|
|
13
|
-
node.notifyresponse = false
|
|
14
|
-
node.notifywrite = true
|
|
15
|
-
node.initialread = true
|
|
16
|
-
node.listenallga = true // Don't remove
|
|
17
|
-
node.outputtype = 'write'
|
|
18
|
-
node.outputRBE = false // Apply or not RBE to the output (Messages coming from flow)
|
|
19
|
-
node.inputRBE = false // Apply or not RBE to the input (Messages coming from BUS)
|
|
20
|
-
node.currentPayload = '' // Current value for the RBE input and for the .previouspayload msg
|
|
21
|
-
node.passthrough = 'no'
|
|
22
|
-
node.formatmultiplyvalue = 1
|
|
23
|
-
node.formatnegativevalue = 'leave'
|
|
24
|
-
node.formatdecimalsvalue = 2
|
|
3
|
+
RED.nodes.createNode(this, config);
|
|
4
|
+
const node = this;
|
|
5
|
+
node.server = RED.nodes.getNode(config.server);
|
|
6
|
+
node.serverHue = RED.nodes.getNode(config.serverHue);
|
|
7
|
+
node.topic = node.name;
|
|
8
|
+
node.name = config.name === undefined ? 'Hue' : config.name;
|
|
9
|
+
node.dpt = '';
|
|
10
|
+
node.notifyreadrequest = false;
|
|
11
|
+
node.notifyreadrequestalsorespondtobus = 'false';
|
|
12
|
+
node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = '';
|
|
13
|
+
node.notifyresponse = false;
|
|
14
|
+
node.notifywrite = true;
|
|
15
|
+
node.initialread = true;
|
|
16
|
+
node.listenallga = true; // Don't remove
|
|
17
|
+
node.outputtype = 'write';
|
|
18
|
+
node.outputRBE = false; // Apply or not RBE to the output (Messages coming from flow)
|
|
19
|
+
node.inputRBE = false; // Apply or not RBE to the input (Messages coming from BUS)
|
|
20
|
+
node.currentPayload = ''; // Current value for the RBE input and for the .previouspayload msg
|
|
21
|
+
node.passthrough = 'no';
|
|
22
|
+
node.formatmultiplyvalue = 1;
|
|
23
|
+
node.formatnegativevalue = 'leave';
|
|
24
|
+
node.formatdecimalsvalue = 2;
|
|
25
25
|
|
|
26
26
|
// Used to call the status update from the config node.
|
|
27
27
|
node.setNodeStatus = ({ fill, shape, text, payload }) => {
|
|
28
28
|
|
|
29
|
-
}
|
|
29
|
+
};
|
|
30
30
|
// Used to call the status update from the HUE config node.
|
|
31
31
|
node.setNodeStatusHue = ({ fill, shape, text, payload }) => {
|
|
32
|
-
if (payload === undefined)
|
|
33
|
-
const dDate = new Date()
|
|
34
|
-
payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString()
|
|
35
|
-
node.status({ fill, shape, text: text + ' ' + payload + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ')' })
|
|
36
|
-
}
|
|
32
|
+
if (payload === undefined) payload = '';
|
|
33
|
+
const dDate = new Date();
|
|
34
|
+
payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString();
|
|
35
|
+
node.status({ fill, shape, text: text + ' ' + payload + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ')' });
|
|
36
|
+
};
|
|
37
37
|
|
|
38
38
|
// This function is called by the knx-ultimate config node, to output a msg.payload.
|
|
39
39
|
node.handleSend = msg => {
|
|
40
|
-
}
|
|
40
|
+
};
|
|
41
41
|
|
|
42
42
|
node.handleSendHUE = _event => {
|
|
43
43
|
try {
|
|
44
44
|
if (_event.id === config.hueDevice) {
|
|
45
|
-
const knxMsgPayload = {}
|
|
46
|
-
knxMsgPayload.topic = config.GAlightsensor
|
|
47
|
-
knxMsgPayload.dpt = config.dptlightsensor
|
|
45
|
+
const knxMsgPayload = {};
|
|
46
|
+
knxMsgPayload.topic = config.GAlightsensor;
|
|
47
|
+
knxMsgPayload.dpt = config.dptlightsensor;
|
|
48
48
|
|
|
49
49
|
if (_event.hasOwnProperty('light') && _event.light.hasOwnProperty('light_level')) {
|
|
50
50
|
//console.log(Math.round(10 ** ((_event.light.light_level - 1) / 10000)))
|
|
51
51
|
//console.log(_event.light.light_level === 0 ? 0 : Math.round(Math.pow(10, (_event.light.light_level - 1) / 10000)))
|
|
52
|
-
knxMsgPayload.payload = _event.light.light_level === 0 ? 0 : Math.round(Math.pow(10, (_event.light.light_level - 1) / 10000))
|
|
52
|
+
knxMsgPayload.payload = _event.light.light_level === 0 ? 0 : Math.round(Math.pow(10, (_event.light.light_level - 1) / 10000));
|
|
53
53
|
// Send to KNX bus
|
|
54
|
-
if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
|
|
55
|
-
node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX ' + JSON.stringify(knxMsgPayload.payload) + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
|
|
54
|
+
if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id });
|
|
55
|
+
node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX ' + JSON.stringify(knxMsgPayload.payload) + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' });
|
|
56
56
|
|
|
57
57
|
// Setup the output msg
|
|
58
|
-
knxMsgPayload.name = node.name
|
|
59
|
-
knxMsgPayload.event = 'light_level'
|
|
58
|
+
knxMsgPayload.name = node.name;
|
|
59
|
+
knxMsgPayload.event = 'light_level';
|
|
60
60
|
|
|
61
61
|
// Send payload
|
|
62
|
-
knxMsgPayload.rawEvent = _event
|
|
63
|
-
node.send(knxMsgPayload)
|
|
64
|
-
node.setNodeStatusHue({ fill: 'blue', shape: 'ring', text: 'HUE->KNX', payload: knxMsgPayload.payload })
|
|
62
|
+
knxMsgPayload.rawEvent = _event;
|
|
63
|
+
node.send(knxMsgPayload);
|
|
64
|
+
node.setNodeStatusHue({ fill: 'blue', shape: 'ring', text: 'HUE->KNX', payload: knxMsgPayload.payload });
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
} catch (error) {
|
|
68
|
-
node.status({ fill: 'red', shape: 'dot', text: 'HUE->KNX error ' + error.message + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
|
|
68
|
+
node.status({ fill: 'red', shape: 'dot', text: 'HUE->KNX error ' + error.message + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' });
|
|
69
69
|
}
|
|
70
|
-
}
|
|
70
|
+
};
|
|
71
71
|
|
|
72
72
|
// On each deploy, unsubscribe+resubscribe
|
|
73
73
|
if (node.server) {
|
|
74
|
-
node.server.removeClient(node)
|
|
75
|
-
node.server.addClient(node)
|
|
74
|
+
node.server.removeClient(node);
|
|
75
|
+
node.server.addClient(node);
|
|
76
76
|
}
|
|
77
77
|
if (node.serverHue) {
|
|
78
|
-
node.serverHue.removeClient(node)
|
|
78
|
+
node.serverHue.removeClient(node);
|
|
79
79
|
// I must get the object, to store read the status
|
|
80
80
|
// I queue the state request, by passing the callback to call whenever the HUE bridge send me the light status async
|
|
81
81
|
if (node.serverHue !== null && node.serverHue.hueManager !== null) {
|
|
82
|
-
(
|
|
83
|
-
node.serverHue.addClient(node);
|
|
84
|
-
try {
|
|
85
|
-
node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, null, 'getLightLevel', (jLight) => {
|
|
86
|
-
node.handleSendHUE(jLight);
|
|
87
|
-
})
|
|
88
|
-
} catch (error) {
|
|
89
|
-
RED.log.error('Errore knxUltimateHueLightSensor subscribing: ' + error.message)
|
|
90
|
-
}
|
|
91
|
-
})()
|
|
82
|
+
node.serverHue.addClient(node);
|
|
92
83
|
}
|
|
93
84
|
}
|
|
94
85
|
|
|
95
86
|
node.on('input', function (msg) {
|
|
96
87
|
|
|
97
|
-
})
|
|
88
|
+
});
|
|
98
89
|
|
|
99
90
|
node.on('close', function (done) {
|
|
100
91
|
if (node.server) {
|
|
101
|
-
node.server.removeClient(node)
|
|
92
|
+
node.server.removeClient(node);
|
|
102
93
|
}
|
|
103
94
|
if (node.serverHue) {
|
|
104
|
-
node.serverHue.removeClient(node)
|
|
95
|
+
node.serverHue.removeClient(node);
|
|
105
96
|
}
|
|
106
|
-
done()
|
|
107
|
-
})
|
|
97
|
+
done();
|
|
98
|
+
});
|
|
108
99
|
}
|
|
109
|
-
RED.nodes.registerType('knxUltimateHueLightSensor', knxUltimateHueLightSensor)
|
|
110
|
-
}
|
|
100
|
+
RED.nodes.registerType('knxUltimateHueLightSensor', knxUltimateHueLightSensor);
|
|
101
|
+
};
|