node-red-contrib-knx-ultimate 2.2.1 → 2.2.3
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/.eslintrc.json +12 -12
- package/CHANGELOG.md +13 -1
- package/nodes/hue-config.js +60 -71
- package/nodes/knxUltimate-config.js +6 -4
- package/nodes/knxUltimateHueBattery.js +69 -68
- package/nodes/knxUltimateHueLight.html +844 -835
- package/nodes/knxUltimateHueLight.js +147 -115
- package/nodes/knxUltimateHueLightSensor.js +53 -62
- package/nodes/knxUltimateHueMotion.html +3 -4
- package/nodes/knxUltimateHueMotion.js +73 -61
- package/nodes/knxUltimateHueScene.js +1 -1
- package/nodes/knxUltimateHueTemperatureSensor.js +70 -71
- package/nodes/knxUltimateViewer.html +28 -0
- package/nodes/utils/hueEngine.js +57 -42
- package/package.json +25 -4
|
@@ -57,6 +57,12 @@ module.exports = function (RED) {
|
|
|
57
57
|
payload: "",
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
+
function getRandomIntInclusive(min, max) {
|
|
61
|
+
min = Math.ceil(min);
|
|
62
|
+
max = Math.floor(max);
|
|
63
|
+
return Math.floor(Math.random() * (max - min + 1) + min); // The maximum is inclusive and the minimum is inclusive
|
|
64
|
+
}
|
|
65
|
+
|
|
60
66
|
// This function is called by the hue-config.js
|
|
61
67
|
node.handleSend = (msg) => {
|
|
62
68
|
if (node.currentHUEDevice === undefined) {
|
|
@@ -105,7 +111,7 @@ module.exports = function (RED) {
|
|
|
105
111
|
node.updateKNXBrightnessState(node.currentHUEDevice.dimming.brightness);
|
|
106
112
|
state = dbright > 0 ? { on: { on: true }, dimming: { brightness: dbright }, color: { xy: dretXY } } : { on: { on: false } };
|
|
107
113
|
} else {
|
|
108
|
-
state = { on: { on: true }
|
|
114
|
+
state = { on: { on: true } };
|
|
109
115
|
}
|
|
110
116
|
} else {
|
|
111
117
|
state = { on: { on: false } };
|
|
@@ -123,7 +129,9 @@ module.exports = function (RED) {
|
|
|
123
129
|
// { decr_incr: 0, data: 1 } : Start decreasing until { decr_incr: 0, data: 0 } is received.
|
|
124
130
|
msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightDIM));
|
|
125
131
|
node.hueDimming(msg.payload.decr_incr, msg.payload.data, config.dimSpeed);
|
|
126
|
-
node.setNodeStatusHue({
|
|
132
|
+
node.setNodeStatusHue({
|
|
133
|
+
fill: "green", shape: "dot", text: "KNX->HUE", payload: JSON.stringify(msg.payload),
|
|
134
|
+
});
|
|
127
135
|
break;
|
|
128
136
|
case config.GADaylightSensor:
|
|
129
137
|
if (config.enableDayNightLighting === "yes") {
|
|
@@ -146,7 +154,9 @@ module.exports = function (RED) {
|
|
|
146
154
|
// { decr_incr: 0, data: 1 } : Start decreasing until { decr_incr: 0, data: 0 } is received.
|
|
147
155
|
msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightHSV));
|
|
148
156
|
node.hueDimmingTunableWhite(msg.payload.decr_incr, msg.payload.data, config.dimSpeed);
|
|
149
|
-
node.setNodeStatusHue({
|
|
157
|
+
node.setNodeStatusHue({
|
|
158
|
+
fill: "green", shape: "dot", text: "KNX->HUE", payload: JSON.stringify(msg.payload),
|
|
159
|
+
});
|
|
150
160
|
}
|
|
151
161
|
break;
|
|
152
162
|
case config.GALightHSVPercentage:
|
|
@@ -250,52 +260,45 @@ module.exports = function (RED) {
|
|
|
250
260
|
});
|
|
251
261
|
break;
|
|
252
262
|
case config.GALightColorCycle:
|
|
253
|
-
|
|
254
|
-
const gaValColorCycle = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightColorCycle));
|
|
255
|
-
if (gaValColorCycle === true) {
|
|
256
|
-
node.serverHue.hueManager.writeHueQueueAdd(
|
|
257
|
-
node.hueDevice,
|
|
258
|
-
{ on: { on: true } },
|
|
259
|
-
node.isGrouped_light === false ? "setLight" : "setGroupedLight",
|
|
260
|
-
);
|
|
261
|
-
node.timerColorCycle = setInterval(() => {
|
|
262
|
-
try {
|
|
263
|
-
function getRandomIntInclusive(min, max) {
|
|
264
|
-
min = Math.ceil(min);
|
|
265
|
-
max = Math.floor(max);
|
|
266
|
-
return Math.floor(Math.random() * (max - min + 1) + min); // The maximum is inclusive and the minimum is inclusive
|
|
267
|
-
}
|
|
268
|
-
const red = getRandomIntInclusive(0, 255);
|
|
269
|
-
const green = getRandomIntInclusive(0, 255);
|
|
270
|
-
const blue = getRandomIntInclusive(0, 255);
|
|
271
|
-
let gamut = null;
|
|
272
|
-
if (
|
|
273
|
-
node.currentHUEDevice !== undefined
|
|
274
|
-
&& node.currentHUEDevice.hasOwnProperty("color")
|
|
275
|
-
&& node.currentHUEDevice.color.hasOwnProperty("gamut_type")
|
|
276
|
-
) {
|
|
277
|
-
gamut = node.currentHUEDevice.color.gamut_type;
|
|
278
|
-
}
|
|
279
|
-
const retXY = hueColorConverter.ColorConverter.rgbToXy(red, green, blue, gamut);
|
|
280
|
-
const bright = hueColorConverter.ColorConverter.getBrightnessFromRGB(red, green, blue);
|
|
281
|
-
state = bright > 0 ? { on: { on: true }, dimming: { brightness: bright }, color: { xy: retXY } } : { on: { on: false } };
|
|
282
|
-
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
283
|
-
} catch (error) { }
|
|
284
|
-
}, 10000);
|
|
285
|
-
} else {
|
|
263
|
+
{
|
|
286
264
|
if (node.timerColorCycle !== undefined) clearInterval(node.timerColorCycle);
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
{ on: { on:
|
|
290
|
-
node.
|
|
291
|
-
|
|
265
|
+
const gaValColorCycle = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightColorCycle));
|
|
266
|
+
if (gaValColorCycle === true) {
|
|
267
|
+
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, { on: { on: true } }, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
268
|
+
node.timerColorCycle = setInterval(() => {
|
|
269
|
+
try {
|
|
270
|
+
const red = getRandomIntInclusive(0, 255);
|
|
271
|
+
const green = getRandomIntInclusive(0, 255);
|
|
272
|
+
const blue = getRandomIntInclusive(0, 255);
|
|
273
|
+
let gamut = null;
|
|
274
|
+
if (
|
|
275
|
+
node.currentHUEDevice !== undefined
|
|
276
|
+
&& node.currentHUEDevice.hasOwnProperty("color")
|
|
277
|
+
&& node.currentHUEDevice.color.hasOwnProperty("gamut_type")
|
|
278
|
+
) {
|
|
279
|
+
gamut = node.currentHUEDevice.color.gamut_type;
|
|
280
|
+
}
|
|
281
|
+
const retXY = hueColorConverter.ColorConverter.rgbToXy(red, green, blue, gamut);
|
|
282
|
+
const bright = hueColorConverter.ColorConverter.getBrightnessFromRGB(red, green, blue);
|
|
283
|
+
state = bright > 0 ? { on: { on: true }, dimming: { brightness: bright }, color: { xy: retXY } } : { on: { on: false } };
|
|
284
|
+
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
285
|
+
} catch (error) { }
|
|
286
|
+
}, 10000);
|
|
287
|
+
} else {
|
|
288
|
+
if (node.timerColorCycle !== undefined) clearInterval(node.timerColorCycle);
|
|
289
|
+
node.serverHue.hueManager.writeHueQueueAdd(
|
|
290
|
+
node.hueDevice,
|
|
291
|
+
{ on: { on: false } },
|
|
292
|
+
node.isGrouped_light === false ? "setLight" : "setGroupedLight",
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
node.setNodeStatusHue({
|
|
296
|
+
fill: "green",
|
|
297
|
+
shape: "dot",
|
|
298
|
+
text: "KNX->HUE",
|
|
299
|
+
payload: gaValColorCycle,
|
|
300
|
+
});
|
|
292
301
|
}
|
|
293
|
-
node.setNodeStatusHue({
|
|
294
|
-
fill: "green",
|
|
295
|
-
shape: "dot",
|
|
296
|
-
text: "KNX->HUE",
|
|
297
|
-
payload: gaValColorCycle,
|
|
298
|
-
});
|
|
299
302
|
break;
|
|
300
303
|
default:
|
|
301
304
|
break;
|
|
@@ -312,41 +315,63 @@ module.exports = function (RED) {
|
|
|
312
315
|
// Start dimming
|
|
313
316
|
// ***********************************************************
|
|
314
317
|
node.hueDimming = function hueDimming(_KNXaction, _KNXbrightness_delta, _dimSpeedInMillisecs = undefined) {
|
|
315
|
-
|
|
318
|
+
// 31/10/2023 after many attempts to use dimming_delta function of the HueApeV2, loosing days of my life, without a decent success
|
|
319
|
+
// i decide to go to the "step brightness" way.
|
|
316
320
|
let hueTelegram = {};
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
const minDimLevelLight = config.minDimLevelLight === undefined ? 3 : config.minDimLevelLight;
|
|
320
|
-
const maxDimLevelLight = config.maxDimLevelLight === undefined ? 100 : config.maxDimLevelLight;
|
|
321
|
-
|
|
322
|
-
// We have also minDimLevelLight and maxDimLevelLight to take care of.
|
|
321
|
+
const numStep = 10; // Steps from 0 to 100 by 10
|
|
322
|
+
if (_dimSpeedInMillisecs === undefined || _dimSpeedInMillisecs === '') _dimSpeedInMillisecs = 5000;
|
|
323
323
|
|
|
324
324
|
if (_KNXbrightness_delta === 0 && _KNXaction === 0) {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
node.
|
|
325
|
+
// STOP DIM
|
|
326
|
+
if (node.timerStepDim !== undefined) clearInterval(node.timerStepDim);
|
|
327
|
+
node.brightnessStep = null;
|
|
328
328
|
return;
|
|
329
329
|
}
|
|
330
|
+
|
|
331
|
+
// Set the actual brightness to start with
|
|
332
|
+
if (node.brightnessStep === null || node.brightnessStep === undefined) node.brightnessStep = node.currentHUEDevice.dimming.brightness || 50;
|
|
333
|
+
node.brightnessStep = Math.ceil(Number(node.brightnessStep));
|
|
334
|
+
|
|
335
|
+
// Set the speed
|
|
336
|
+
_dimSpeedInMillisecs /= numStep;
|
|
337
|
+
|
|
338
|
+
// We have also minDimLevelLight and maxDimLevelLight to take care of.
|
|
339
|
+
let minDimLevelLight = config.minDimLevelLight === undefined ? 10 : Number(config.minDimLevelLight);
|
|
340
|
+
if (config.minDimLevelLight === "useHueLightLevel" && node.currentHUEDevice.dimming.min_dim_level === undefined) minDimLevelLight = 10;
|
|
341
|
+
const maxDimLevelLight = config.maxDimLevelLight === undefined ? 100 : Number(config.maxDimLevelLight);
|
|
342
|
+
|
|
330
343
|
if (_KNXbrightness_delta > 0 && _KNXaction === 1) {
|
|
331
|
-
|
|
332
|
-
|
|
344
|
+
// DIM UP
|
|
345
|
+
if (node.timerStepDim !== undefined) clearInterval(node.timerStepDim);
|
|
346
|
+
node.timerStepDim = setInterval(() => {
|
|
347
|
+
node.updateKNXBrightnessState(node.brightnessStep); // Unnecessary, but necessary to set the KNX Status in real time.
|
|
348
|
+
node.brightnessStep += numStep;
|
|
349
|
+
if (node.brightnessStep > maxDimLevelLight) node.brightnessStep = maxDimLevelLight;
|
|
350
|
+
hueTelegram = { dimming: { brightness: node.brightnessStep }, dynamics: { duration: _dimSpeedInMillisecs } };
|
|
351
|
+
// Switch on the light if off
|
|
352
|
+
if (node.currentHUEDevice.hasOwnProperty("on") !== undefined && node.currentHUEDevice.on.on === false) {
|
|
353
|
+
hueTelegram.on = { on: true };
|
|
354
|
+
}
|
|
355
|
+
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, hueTelegram, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
356
|
+
if (node.brightnessStep >= maxDimLevelLight) clearInterval(node.timerStepDim);
|
|
357
|
+
}, _dimSpeedInMillisecs);
|
|
333
358
|
}
|
|
334
359
|
if (_KNXbrightness_delta > 0 && _KNXaction === 0) {
|
|
335
|
-
//
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
360
|
+
// DIM DOWN
|
|
361
|
+
if (node.timerStepDim !== undefined) clearInterval(node.timerStepDim);
|
|
362
|
+
node.timerStepDim = setInterval(() => {
|
|
363
|
+
node.updateKNXBrightnessState(node.brightnessStep); // Unnecessary, but necessary to set the KNX Status in real time.
|
|
364
|
+
node.brightnessStep -= numStep;
|
|
365
|
+
if (node.brightnessStep < minDimLevelLight) node.brightnessStep = minDimLevelLight;
|
|
366
|
+
hueTelegram = { dimming: { brightness: node.brightnessStep }, dynamics: { duration: _dimSpeedInMillisecs } };
|
|
367
|
+
// Switch off the light if on
|
|
368
|
+
if (node.currentHUEDevice.hasOwnProperty("on") !== undefined && node.currentHUEDevice.on.on === true && node.brightnessStep === 0) {
|
|
369
|
+
hueTelegram.on = { on: false };
|
|
370
|
+
}
|
|
371
|
+
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, hueTelegram, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
372
|
+
if (node.brightnessStep <= minDimLevelLight) clearInterval(node.timerStepDim);
|
|
373
|
+
}, _dimSpeedInMillisecs);
|
|
348
374
|
}
|
|
349
|
-
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, hueTelegram, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
350
375
|
};
|
|
351
376
|
// ***********************************************************
|
|
352
377
|
|
|
@@ -354,28 +379,28 @@ module.exports = function (RED) {
|
|
|
354
379
|
// mirek: required(integer minimum: 153, maximum: 500)
|
|
355
380
|
// ***********************************************************
|
|
356
381
|
node.hueDimmingTunableWhite = function hueDimming(_KNXaction, _KNXbrightness_delta, _dimSpeedInMillisecs = undefined) {
|
|
357
|
-
let dimDirection =
|
|
382
|
+
let dimDirection = "stop";
|
|
358
383
|
let hueTelegram = {};
|
|
359
|
-
_dimSpeedInMillisecs =
|
|
384
|
+
_dimSpeedInMillisecs = _dimSpeedInMillisecs === undefined || _dimSpeedInMillisecs === "" ? 5000 : _dimSpeedInMillisecs;
|
|
360
385
|
let delta = 0;
|
|
361
386
|
if (!node.currentHUEDevice.color_temperature.hasOwnProperty("mirek")) delta = 347 - Math.round(173, 0); // Unable to read the mirek, set medium as default
|
|
362
387
|
// We have also minDimLevelLight and maxDimLevelLight to take care of.
|
|
363
388
|
// Mirek limits are not taken in consideration.
|
|
364
389
|
// Maximum mirek is 347
|
|
365
390
|
if (_KNXbrightness_delta === 0 && _KNXaction === 0) {
|
|
366
|
-
dimDirection =
|
|
391
|
+
dimDirection = "stop";
|
|
367
392
|
hueTelegram = { color_temperature_delta: { action: dimDirection } };
|
|
368
393
|
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, hueTelegram, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
369
394
|
return;
|
|
370
395
|
}
|
|
371
396
|
if (_KNXbrightness_delta > 0 && _KNXaction === 1) {
|
|
372
397
|
if (node.currentHUEDevice.color_temperature.hasOwnProperty("mirek")) delta = 347 - Math.round(node.currentHUEDevice.color_temperature.mirek, 0);
|
|
373
|
-
dimDirection =
|
|
398
|
+
dimDirection = "up";
|
|
374
399
|
}
|
|
375
400
|
if (_KNXbrightness_delta > 0 && _KNXaction === 0) {
|
|
376
401
|
// 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)
|
|
377
402
|
if (node.currentHUEDevice.color_temperature.hasOwnProperty("mirek")) delta = Math.round(node.currentHUEDevice.color_temperature.mirek, 0);
|
|
378
|
-
dimDirection =
|
|
403
|
+
dimDirection = "down";
|
|
379
404
|
}
|
|
380
405
|
// Calculate the dimming time based on delta
|
|
381
406
|
// 10000:x=347:delta
|
|
@@ -384,7 +409,7 @@ module.exports = function (RED) {
|
|
|
384
409
|
|
|
385
410
|
hueTelegram = { color_temperature_delta: { action: dimDirection, mirek_delta: delta }, dynamics: { duration: _dimSpeedInMillisecs } };
|
|
386
411
|
// Switch on the light if off
|
|
387
|
-
if (node.currentHUEDevice.hasOwnProperty(
|
|
412
|
+
if (node.currentHUEDevice.hasOwnProperty("on") !== undefined && node.currentHUEDevice.on.on === false && dimDirection === "up") {
|
|
388
413
|
hueTelegram.on = { on: true };
|
|
389
414
|
}
|
|
390
415
|
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, hueTelegram, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
@@ -406,51 +431,64 @@ module.exports = function (RED) {
|
|
|
406
431
|
if (_event.hasOwnProperty("on")) {
|
|
407
432
|
node.updateKNXLightState(_event.on.on);
|
|
408
433
|
// In case of switch off, set the dim to zero
|
|
409
|
-
if (
|
|
434
|
+
if (
|
|
435
|
+
_event.on.on === false
|
|
436
|
+
&& (config.updateKNXBrightnessStatusOnHUEOnOff === undefined || config.updateKNXBrightnessStatusOnHUEOnOff === "onhueoff")
|
|
437
|
+
) {
|
|
410
438
|
node.updateKNXBrightnessState(0);
|
|
439
|
+
node.currentHUEDevice.dimming.brightness = 0;
|
|
411
440
|
} else {
|
|
412
|
-
// Sends the previous brightness value
|
|
413
|
-
try {
|
|
414
|
-
|
|
415
|
-
} catch (error) {
|
|
416
|
-
|
|
417
|
-
}
|
|
441
|
+
// // Sends the previous brightness value
|
|
442
|
+
// try {
|
|
443
|
+
// node.updateKNXBrightnessState(node.currentHUEDevice.dimming.brightness);
|
|
444
|
+
// } catch (error) {
|
|
445
|
+
// /* empty */
|
|
446
|
+
// }
|
|
418
447
|
}
|
|
448
|
+
node.currentHUEDevice.on.on = _event.on.on;
|
|
419
449
|
}
|
|
420
450
|
if (_event.hasOwnProperty("color")) {
|
|
421
451
|
node.updateKNXLightColorState(_event.color);
|
|
452
|
+
node.currentHUEDevice.color = _event.color;
|
|
422
453
|
}
|
|
423
|
-
if (_event.hasOwnProperty("dimming")) {
|
|
454
|
+
if (_event.hasOwnProperty("dimming") && _event.dimming.brightness !== undefined) {
|
|
424
455
|
// Every once on a time, the light transmit the brightness value of 0.39.
|
|
425
456
|
// To avoid wrongly turn light state on, exit
|
|
426
457
|
if (_event.dimming.brightness < 1) _event.dimming.brightness = 0;
|
|
427
|
-
if (node.currentHUEDevice.hasOwnProperty(
|
|
458
|
+
if (node.currentHUEDevice.hasOwnProperty("on") && node.currentHUEDevice.on.on === false && _event.dimming.brightness === 0) return;
|
|
428
459
|
if (node.currentHUEDevice.on.on === false) node.updateKNXLightState(_event.dimming.brightness > 0);
|
|
429
460
|
node.updateKNXBrightnessState(_event.dimming.brightness);
|
|
430
461
|
// If the brightness reaches zero, the hue lamp "on" property must be set to zero as well
|
|
431
462
|
if (_event.dimming.brightness === 0) {
|
|
432
|
-
node.serverHue.hueManager.writeHueQueueAdd(
|
|
463
|
+
node.serverHue.hueManager.writeHueQueueAdd(
|
|
464
|
+
node.hueDevice,
|
|
465
|
+
{ on: { on: false } },
|
|
466
|
+
node.isGrouped_light === false ? "setLight" : "setGroupedLight",
|
|
467
|
+
);
|
|
468
|
+
node.currentHUEDevice.on.on = false;
|
|
433
469
|
}
|
|
470
|
+
node.currentHUEDevice.dimming.brightness = _event.dimming.brightness;
|
|
434
471
|
}
|
|
435
|
-
if (_event.hasOwnProperty("color_temperature")) {
|
|
472
|
+
if (_event.hasOwnProperty("color_temperature") && _event.color_temperature.mirek !== undefined) {
|
|
436
473
|
node.updateKNXLightHSVState(_event.color_temperature.mirek);
|
|
474
|
+
node.currentHUEDevice.color_temperature.mirek = _event.color_temperature.mirek;
|
|
437
475
|
}
|
|
438
476
|
|
|
439
|
-
// Update the current HUE Device with the new _event
|
|
440
|
-
function copiaOggettoRicorsivo(objDestinazione, objOrigine) {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
}
|
|
452
|
-
// Copia l'oggettoOrigine nell'oggettoDestinazione mantenendo le proprietà esistenti
|
|
453
|
-
copiaOggettoRicorsivo(node.currentHUEDevice, _event);
|
|
477
|
+
// // Update the current HUE Device with the new _event
|
|
478
|
+
// function copiaOggettoRicorsivo(objDestinazione, objOrigine) {
|
|
479
|
+
// for (const prop in objOrigine) {
|
|
480
|
+
// if (typeof objOrigine[prop] === "object" && objOrigine[prop] !== null) {
|
|
481
|
+
// // Se la proprietà è un oggetto, copiamola in modo ricorsivo
|
|
482
|
+
// objDestinazione[prop] = objDestinazione[prop] || {};
|
|
483
|
+
// copiaOggettoRicorsivo(objDestinazione[prop], objOrigine[prop]);
|
|
484
|
+
// } else {
|
|
485
|
+
// // Altrimenti, copia il valore della proprietà
|
|
486
|
+
// objDestinazione[prop] = objOrigine[prop];
|
|
487
|
+
// }
|
|
488
|
+
// }
|
|
489
|
+
// }
|
|
490
|
+
// // Copia l'oggettoOrigine nell'oggettoDestinazione mantenendo le proprietà esistenti
|
|
491
|
+
// copiaOggettoRicorsivo(node.currentHUEDevice, _event);
|
|
454
492
|
}
|
|
455
493
|
} catch (error) {
|
|
456
494
|
node.status({
|
|
@@ -481,7 +519,7 @@ module.exports = function (RED) {
|
|
|
481
519
|
node.setNodeStatusHue({
|
|
482
520
|
fill: "blue",
|
|
483
521
|
shape: "ring",
|
|
484
|
-
text: "HUE->KNX
|
|
522
|
+
text: "HUE->KNX Bright",
|
|
485
523
|
payload: knxMsgPayload.payload,
|
|
486
524
|
});
|
|
487
525
|
}
|
|
@@ -509,7 +547,7 @@ module.exports = function (RED) {
|
|
|
509
547
|
node.setNodeStatusHue({
|
|
510
548
|
fill: "blue",
|
|
511
549
|
shape: "ring",
|
|
512
|
-
text: "HUE->KNX
|
|
550
|
+
text: "HUE->KNX On/Off",
|
|
513
551
|
payload: knxMsgPayload.payload,
|
|
514
552
|
});
|
|
515
553
|
}
|
|
@@ -542,7 +580,7 @@ module.exports = function (RED) {
|
|
|
542
580
|
node.setNodeStatusHue({
|
|
543
581
|
fill: "blue",
|
|
544
582
|
shape: "ring",
|
|
545
|
-
text: "HUE->KNX
|
|
583
|
+
text: "HUE->KNX HSV",
|
|
546
584
|
payload: knxMsgPayload.payload,
|
|
547
585
|
});
|
|
548
586
|
}
|
|
@@ -571,7 +609,7 @@ module.exports = function (RED) {
|
|
|
571
609
|
node.setNodeStatusHue({
|
|
572
610
|
fill: "blue",
|
|
573
611
|
shape: "ring",
|
|
574
|
-
text: "HUE->KNX
|
|
612
|
+
text: "HUE->KNX Color",
|
|
575
613
|
payload: knxMsgPayload.payload,
|
|
576
614
|
});
|
|
577
615
|
}
|
|
@@ -585,12 +623,6 @@ module.exports = function (RED) {
|
|
|
585
623
|
if (node.serverHue) {
|
|
586
624
|
node.serverHue.removeClient(node);
|
|
587
625
|
if (node.serverHue !== null && node.serverHue.hueManager !== null) {
|
|
588
|
-
try {
|
|
589
|
-
// Everytime the node is edited, it comes here to read the currentHueDevice, if any.
|
|
590
|
-
// It comes here also at node-red boot, but at node-red boot, the hueAllResources is null.
|
|
591
|
-
// The hue-config take cares of filling all properties of the node, after it has been connected to the hue Brigde.
|
|
592
|
-
if (node.serverHue.hueAllResources !== null) node.currentHUEDevice = node.serverHue.loadResourcesFromHUEBridge(node);
|
|
593
|
-
} catch (error) { }
|
|
594
626
|
try {
|
|
595
627
|
node.serverHue.addClient(node);
|
|
596
628
|
} 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) return
|
|
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) return;
|
|
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
|
+
};
|
|
@@ -211,7 +211,7 @@
|
|
|
211
211
|
<input type="text" id="node-input-namemotion" style="width:200px;margin-left: 5px; text-align: left;">
|
|
212
212
|
</div>
|
|
213
213
|
|
|
214
|
-
|
|
214
|
+
<br/>
|
|
215
215
|
<br/>
|
|
216
216
|
<br/>
|
|
217
217
|
|
|
@@ -219,10 +219,10 @@ s<br/>
|
|
|
219
219
|
</script>
|
|
220
220
|
<script src="https://kit.fontawesome.com/11f26b4500.js" crossorigin="anonymous"></script>
|
|
221
221
|
|
|
222
|
-
<script type="text/markdown" data-help-name="knxUltimateHueMotion"
|
|
222
|
+
<script type="text/markdown" data-help-name="knxUltimateHueMotion">
|
|
223
223
|
This node lets you get the events from your HUE motion device.
|
|
224
224
|
|
|
225
|
-
Here you can get the HUE motion events
|
|
225
|
+
Here you can get the HUE motion events.
|
|
226
226
|
Start typing in the GA field, the name or group address of your KNX device, the avaiable devices start showing up while you're typing.
|
|
227
227
|
|
|
228
228
|
**General**
|
|
@@ -245,7 +245,6 @@ Start typing in the GA field, the name or group address of your KNX device, the
|
|
|
245
245
|
|
|
246
246
|
`msg.payload` is used as the payload of the published message.
|
|
247
247
|
It contains the detailed event sent by your Hue devicem so you can use it for whatever you want.
|
|
248
|
-
It contains the detailed event sent by your Hue devicem so you can use it for whatever you want.
|
|
249
248
|
|
|
250
249
|
<br/>
|
|
251
250
|
|