node-red-contrib-knx-ultimate 2.1.63 → 2.2.1
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 +7 -0
- package/KNXEngine/package.json +1 -1
- package/nodes/hue-config.html +2 -2
- package/nodes/hue-config.js +75 -34
- package/nodes/knxUltimate-config.js +158 -156
- package/nodes/knxUltimateHueBattery.html +2 -2
- package/nodes/knxUltimateHueButton.html +1 -1
- package/nodes/knxUltimateHueButton.js +152 -116
- package/nodes/knxUltimateHueLight.html +64 -24
- package/nodes/knxUltimateHueLight.js +132 -137
- package/nodes/knxUltimateHueLightSensor.html +1 -1
- package/nodes/knxUltimateHueLightSensor.js +2 -2
- package/nodes/knxUltimateHueMotion.html +2 -2
- package/nodes/knxUltimateHueScene.html +1 -1
- package/nodes/knxUltimateHueTapDial.html +1 -1
- package/nodes/knxUltimateHueTapDial.js +1 -2
- package/nodes/knxUltimateHueTemperatureSensor.html +2 -2
- package/nodes/knxUltimateLoadControl.html +1 -1
- package/nodes/utils/hueEngine.js +5 -25
- package/package.json +2 -2
- package/nodes/utils/iro.js +0 -1835
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/* eslint-disable no-param-reassign */
|
|
2
|
+
/* eslint-disable camelcase */
|
|
1
3
|
/* eslint-disable max-len */
|
|
2
4
|
/* eslint-disable no-lonely-if */
|
|
3
5
|
module.exports = function (RED) {
|
|
@@ -32,12 +34,16 @@ module.exports = function (RED) {
|
|
|
32
34
|
node.currentKNXGALightState = false; // Stores the current KNX value for the GA
|
|
33
35
|
node.DayTime = true;
|
|
34
36
|
node.isGrouped_light = config.hueDevice.split("#")[1] === "grouped_light";
|
|
35
|
-
|
|
37
|
+
node.hueDevice = config.hueDevice.split("#")[0];
|
|
36
38
|
|
|
37
39
|
// Used to call the status update from the config node.
|
|
38
|
-
node.setNodeStatus = ({
|
|
40
|
+
node.setNodeStatus = ({
|
|
41
|
+
fill, shape, text, payload,
|
|
42
|
+
}) => { };
|
|
39
43
|
// Used to call the status update from the HUE config node.
|
|
40
|
-
node.setNodeStatusHue = ({
|
|
44
|
+
node.setNodeStatusHue = ({
|
|
45
|
+
fill, shape, text, payload,
|
|
46
|
+
}) => {
|
|
41
47
|
if (payload === undefined) return;
|
|
42
48
|
const dDate = new Date();
|
|
43
49
|
payload = typeof payload === "object" ? JSON.stringify(payload) : payload.toString();
|
|
@@ -48,7 +54,7 @@ module.exports = function (RED) {
|
|
|
48
54
|
fill: "grey",
|
|
49
55
|
shape: "ring",
|
|
50
56
|
text: "Connecting to the Bridge...",
|
|
51
|
-
payload: ""
|
|
57
|
+
payload: "",
|
|
52
58
|
});
|
|
53
59
|
|
|
54
60
|
// This function is called by the hue-config.js
|
|
@@ -58,7 +64,7 @@ module.exports = function (RED) {
|
|
|
58
64
|
fill: "red",
|
|
59
65
|
shape: "ring",
|
|
60
66
|
text: "Rejected KNX message. I'm connecting to the Bridge...",
|
|
61
|
-
payload: ""
|
|
67
|
+
payload: "",
|
|
62
68
|
});
|
|
63
69
|
return;
|
|
64
70
|
}
|
|
@@ -104,7 +110,7 @@ module.exports = function (RED) {
|
|
|
104
110
|
} else {
|
|
105
111
|
state = { on: { on: false } };
|
|
106
112
|
}
|
|
107
|
-
node.serverHue.hueManager.writeHueQueueAdd(
|
|
113
|
+
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
108
114
|
node.setNodeStatusHue({
|
|
109
115
|
fill: "green",
|
|
110
116
|
shape: "dot",
|
|
@@ -116,22 +122,8 @@ module.exports = function (RED) {
|
|
|
116
122
|
// { decr_incr: 1, data: 1 } : Start increasing until { decr_incr: 0, data: 0 } is received.
|
|
117
123
|
// { decr_incr: 0, data: 1 } : Start decreasing until { decr_incr: 0, data: 0 } is received.
|
|
118
124
|
msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightDIM));
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
// First, switch on the light if off
|
|
122
|
-
if (node.currentHUEDevice.hasOwnProperty('on') !== undefined && node.currentHUEDevice.on.on === false && dimDirection === "up") {
|
|
123
|
-
node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, { on: { on: true } }, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
124
|
-
}
|
|
125
|
-
node.startDimStopper(dimDirection);
|
|
126
|
-
} else {
|
|
127
|
-
node.startDimStopper("stop");
|
|
128
|
-
}
|
|
129
|
-
node.setNodeStatusHue({
|
|
130
|
-
fill: "green",
|
|
131
|
-
shape: "dot",
|
|
132
|
-
text: "KNX->HUE",
|
|
133
|
-
payload: msg.payload,
|
|
134
|
-
});
|
|
125
|
+
node.hueDimming(msg.payload.decr_incr, msg.payload.data, config.dimSpeed);
|
|
126
|
+
node.setNodeStatusHue({ fill: "green", shape: "dot", text: "KNX->HUE", payload: JSON.stringify(msg.payload) });
|
|
135
127
|
break;
|
|
136
128
|
case config.GADaylightSensor:
|
|
137
129
|
if (config.enableDayNightLighting === "yes") {
|
|
@@ -153,19 +145,9 @@ module.exports = function (RED) {
|
|
|
153
145
|
// { decr_incr: 1, data: 1 } : Start increasing until { decr_incr: 0, data: 0 } is received.
|
|
154
146
|
// { decr_incr: 0, data: 1 } : Start decreasing until { decr_incr: 0, data: 0 } is received.
|
|
155
147
|
msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightHSV));
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
node.startDimStopperTunableWhite(dimDirectionTunableWhite);
|
|
159
|
-
} else {
|
|
160
|
-
node.startDimStopperTunableWhite("stop");
|
|
161
|
-
}
|
|
148
|
+
node.hueDimmingTunableWhite(msg.payload.decr_incr, msg.payload.data, config.dimSpeed);
|
|
149
|
+
node.setNodeStatusHue({ fill: "green", shape: "dot", text: "KNX->HUE", payload: JSON.stringify(msg.payload) });
|
|
162
150
|
}
|
|
163
|
-
node.setNodeStatusHue({
|
|
164
|
-
fill: "green",
|
|
165
|
-
shape: "dot",
|
|
166
|
-
text: "KNX->HUE",
|
|
167
|
-
payload: msg.payload,
|
|
168
|
-
});
|
|
169
151
|
break;
|
|
170
152
|
case config.GALightHSVPercentage:
|
|
171
153
|
if (config.dptLightHSVPercentage === "5.001") {
|
|
@@ -175,7 +157,7 @@ module.exports = function (RED) {
|
|
|
175
157
|
const retMirek = hueColorConverter.ColorConverter.scale(msg.payload, [0, 100], [153, 500]);
|
|
176
158
|
msg.payload = retMirek;
|
|
177
159
|
state = { color_temperature: { mirek: msg.payload } };
|
|
178
|
-
node.serverHue.hueManager.writeHueQueueAdd(
|
|
160
|
+
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
179
161
|
node.setNodeStatusHue({
|
|
180
162
|
fill: "green",
|
|
181
163
|
shape: "dot",
|
|
@@ -201,7 +183,7 @@ module.exports = function (RED) {
|
|
|
201
183
|
if (node.currentHUEDevice.on.on === false && msg.payload > 0) state.on = { on: true };
|
|
202
184
|
if (node.currentHUEDevice.on.on === true && msg.payload === 0) state.on = { on: false };
|
|
203
185
|
}
|
|
204
|
-
node.serverHue.hueManager.writeHueQueueAdd(
|
|
186
|
+
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
205
187
|
node.setNodeStatusHue({
|
|
206
188
|
fill: "green",
|
|
207
189
|
shape: "dot",
|
|
@@ -231,7 +213,7 @@ module.exports = function (RED) {
|
|
|
231
213
|
if (node.currentHUEDevice.on.on === false && bright > 0) state.on = { on: true };
|
|
232
214
|
if (node.currentHUEDevice.on.on === true && bright === 0) state = { on: { on: false }, dimming: { brightness: bright } };
|
|
233
215
|
}
|
|
234
|
-
node.serverHue.hueManager.writeHueQueueAdd(
|
|
216
|
+
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
235
217
|
node.setNodeStatusHue({
|
|
236
218
|
fill: "green",
|
|
237
219
|
shape: "dot",
|
|
@@ -250,14 +232,14 @@ module.exports = function (RED) {
|
|
|
250
232
|
state = msg.payload === true
|
|
251
233
|
? { on: { on: true }, dimming: { brightness: 100 }, dynamics: { duration: 0 } }
|
|
252
234
|
: { on: { on: false }, dimming: { brightness: 0 }, dynamics: { duration: 0 } };
|
|
253
|
-
node.serverHue.hueManager.writeHueQueueAdd(
|
|
235
|
+
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
254
236
|
}, 1000);
|
|
255
237
|
} else {
|
|
256
238
|
if (node.timerBlink !== undefined) clearInterval(node.timerBlink);
|
|
257
239
|
node.serverHue.hueManager.writeHueQueueAdd(
|
|
258
|
-
|
|
240
|
+
node.hueDevice,
|
|
259
241
|
{ on: { on: false } },
|
|
260
|
-
node.isGrouped_light === false ? "setLight" : "setGroupedLight"
|
|
242
|
+
node.isGrouped_light === false ? "setLight" : "setGroupedLight",
|
|
261
243
|
);
|
|
262
244
|
}
|
|
263
245
|
node.setNodeStatusHue({
|
|
@@ -272,9 +254,9 @@ module.exports = function (RED) {
|
|
|
272
254
|
const gaValColorCycle = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightColorCycle));
|
|
273
255
|
if (gaValColorCycle === true) {
|
|
274
256
|
node.serverHue.hueManager.writeHueQueueAdd(
|
|
275
|
-
|
|
257
|
+
node.hueDevice,
|
|
276
258
|
{ on: { on: true } },
|
|
277
|
-
node.isGrouped_light === false ? "setLight" : "setGroupedLight"
|
|
259
|
+
node.isGrouped_light === false ? "setLight" : "setGroupedLight",
|
|
278
260
|
);
|
|
279
261
|
node.timerColorCycle = setInterval(() => {
|
|
280
262
|
try {
|
|
@@ -297,15 +279,15 @@ module.exports = function (RED) {
|
|
|
297
279
|
const retXY = hueColorConverter.ColorConverter.rgbToXy(red, green, blue, gamut);
|
|
298
280
|
const bright = hueColorConverter.ColorConverter.getBrightnessFromRGB(red, green, blue);
|
|
299
281
|
state = bright > 0 ? { on: { on: true }, dimming: { brightness: bright }, color: { xy: retXY } } : { on: { on: false } };
|
|
300
|
-
node.serverHue.hueManager.writeHueQueueAdd(
|
|
282
|
+
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
301
283
|
} catch (error) { }
|
|
302
284
|
}, 10000);
|
|
303
285
|
} else {
|
|
304
286
|
if (node.timerColorCycle !== undefined) clearInterval(node.timerColorCycle);
|
|
305
287
|
node.serverHue.hueManager.writeHueQueueAdd(
|
|
306
|
-
|
|
288
|
+
node.hueDevice,
|
|
307
289
|
{ on: { on: false } },
|
|
308
|
-
node.isGrouped_light === false ? "setLight" : "setGroupedLight"
|
|
290
|
+
node.isGrouped_light === false ? "setLight" : "setGroupedLight",
|
|
309
291
|
);
|
|
310
292
|
}
|
|
311
293
|
node.setNodeStatusHue({
|
|
@@ -329,80 +311,95 @@ module.exports = function (RED) {
|
|
|
329
311
|
|
|
330
312
|
// Start dimming
|
|
331
313
|
// ***********************************************************
|
|
332
|
-
node.
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
314
|
+
node.hueDimming = function hueDimming(_KNXaction, _KNXbrightness_delta, _dimSpeedInMillisecs = undefined) {
|
|
315
|
+
let dimDirection = 'stop';
|
|
316
|
+
let hueTelegram = {};
|
|
317
|
+
_dimSpeedInMillisecs = (_dimSpeedInMillisecs === undefined || _dimSpeedInMillisecs === '') ? 5000 : _dimSpeedInMillisecs;
|
|
318
|
+
let delta = 0;
|
|
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.
|
|
323
|
+
|
|
324
|
+
if (_KNXbrightness_delta === 0 && _KNXaction === 0) {
|
|
325
|
+
dimDirection = 'stop';
|
|
326
|
+
hueTelegram = { dimming_delta: { action: dimDirection } };
|
|
327
|
+
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, hueTelegram, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
339
328
|
return;
|
|
340
329
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
break;
|
|
345
|
-
case "down":
|
|
346
|
-
node.dimDirection = { dimming_delta: { action: "down", brightness_delta: 10 } };
|
|
347
|
-
break;
|
|
348
|
-
default:
|
|
349
|
-
break;
|
|
330
|
+
if (_KNXbrightness_delta > 0 && _KNXaction === 1) {
|
|
331
|
+
delta = 100 - Math.round(node.currentHUEDevice.dimming.brightness, 0) - (100 - Number(maxDimLevelLight));
|
|
332
|
+
dimDirection = 'up';
|
|
350
333
|
}
|
|
351
|
-
|
|
352
|
-
node.
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
334
|
+
if (_KNXbrightness_delta > 0 && _KNXaction === 0) {
|
|
335
|
+
// 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)
|
|
336
|
+
delta = Math.round(node.currentHUEDevice.dimming.brightness, 0) - (minDimLevelLight === 'useHueLightLevel' ? node.currentHUEDevice.dimming.min_dim_level : Number(minDimLevelLight));
|
|
337
|
+
dimDirection = 'down';
|
|
338
|
+
}
|
|
339
|
+
// Calculate the dimming time based on delta
|
|
340
|
+
// 10000:x=100:delta
|
|
341
|
+
// x = (10000 * delta) / 100
|
|
342
|
+
_dimSpeedInMillisecs = Math.round((_dimSpeedInMillisecs * delta) / 100, 0);
|
|
343
|
+
|
|
344
|
+
hueTelegram = { dimming_delta: { action: dimDirection, brightness_delta: delta }, dynamics: { duration: _dimSpeedInMillisecs } };
|
|
345
|
+
// Switch on the light if off
|
|
346
|
+
if (node.currentHUEDevice.hasOwnProperty('on') !== undefined && node.currentHUEDevice.on.on === false && dimDirection === "up") {
|
|
347
|
+
hueTelegram.on = { on: true };
|
|
348
|
+
}
|
|
349
|
+
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, hueTelegram, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
359
350
|
};
|
|
360
351
|
// ***********************************************************
|
|
361
352
|
|
|
362
353
|
// Start dimming tunable white
|
|
363
354
|
// mirek: required(integer minimum: 153, maximum: 500)
|
|
364
355
|
// ***********************************************************
|
|
365
|
-
node.
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
if (node.
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
default:
|
|
380
|
-
break;
|
|
356
|
+
node.hueDimmingTunableWhite = function hueDimming(_KNXaction, _KNXbrightness_delta, _dimSpeedInMillisecs = undefined) {
|
|
357
|
+
let dimDirection = 'stop';
|
|
358
|
+
let hueTelegram = {};
|
|
359
|
+
_dimSpeedInMillisecs = (_dimSpeedInMillisecs === undefined || _dimSpeedInMillisecs === '') ? 5000 : _dimSpeedInMillisecs;
|
|
360
|
+
let delta = 0;
|
|
361
|
+
if (!node.currentHUEDevice.color_temperature.hasOwnProperty("mirek")) delta = 347 - Math.round(173, 0); // Unable to read the mirek, set medium as default
|
|
362
|
+
// We have also minDimLevelLight and maxDimLevelLight to take care of.
|
|
363
|
+
// Mirek limits are not taken in consideration.
|
|
364
|
+
// Maximum mirek is 347
|
|
365
|
+
if (_KNXbrightness_delta === 0 && _KNXaction === 0) {
|
|
366
|
+
dimDirection = 'stop';
|
|
367
|
+
hueTelegram = { color_temperature_delta: { action: dimDirection } };
|
|
368
|
+
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, hueTelegram, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
369
|
+
return;
|
|
381
370
|
}
|
|
382
|
-
|
|
383
|
-
node.
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
node.
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
371
|
+
if (_KNXbrightness_delta > 0 && _KNXaction === 1) {
|
|
372
|
+
if (node.currentHUEDevice.color_temperature.hasOwnProperty("mirek")) delta = 347 - Math.round(node.currentHUEDevice.color_temperature.mirek, 0);
|
|
373
|
+
dimDirection = 'up';
|
|
374
|
+
}
|
|
375
|
+
if (_KNXbrightness_delta > 0 && _KNXaction === 0) {
|
|
376
|
+
// 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
|
+
if (node.currentHUEDevice.color_temperature.hasOwnProperty("mirek")) delta = Math.round(node.currentHUEDevice.color_temperature.mirek, 0);
|
|
378
|
+
dimDirection = 'down';
|
|
379
|
+
}
|
|
380
|
+
// Calculate the dimming time based on delta
|
|
381
|
+
// 10000:x=347:delta
|
|
382
|
+
// x = (10000 * delta) / 347
|
|
383
|
+
_dimSpeedInMillisecs = Math.round((_dimSpeedInMillisecs * delta) / 347, 0);
|
|
384
|
+
|
|
385
|
+
hueTelegram = { color_temperature_delta: { action: dimDirection, mirek_delta: delta }, dynamics: { duration: _dimSpeedInMillisecs } };
|
|
386
|
+
// Switch on the light if off
|
|
387
|
+
if (node.currentHUEDevice.hasOwnProperty('on') !== undefined && node.currentHUEDevice.on.on === false && dimDirection === "up") {
|
|
388
|
+
hueTelegram.on = { on: true };
|
|
389
|
+
}
|
|
390
|
+
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, hueTelegram, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
394
391
|
};
|
|
395
392
|
// ***********************************************************
|
|
396
393
|
|
|
397
394
|
node.handleSendHUE = (_event) => {
|
|
398
395
|
try {
|
|
399
|
-
if (_event.id ===
|
|
396
|
+
if (_event.id === node.hueDevice) {
|
|
400
397
|
if (node.currentHUEDevice === undefined) {
|
|
401
398
|
node.setNodeStatusHue({
|
|
402
399
|
fill: "red",
|
|
403
400
|
shape: "ring",
|
|
404
401
|
text: "Rejected HUE message. I'm connecting to the Bridge...",
|
|
405
|
-
payload: ""
|
|
402
|
+
payload: "",
|
|
406
403
|
});
|
|
407
404
|
return;
|
|
408
405
|
}
|
|
@@ -419,13 +416,9 @@ module.exports = function (RED) {
|
|
|
419
416
|
/* empty */
|
|
420
417
|
}
|
|
421
418
|
}
|
|
422
|
-
if (node.currentHUEDevice !== undefined) node.currentHUEDevice.on = _event.on; // Update the internal object representing the current light
|
|
423
|
-
return;
|
|
424
419
|
}
|
|
425
420
|
if (_event.hasOwnProperty("color")) {
|
|
426
421
|
node.updateKNXLightColorState(_event.color);
|
|
427
|
-
if (node.currentHUEDevice !== undefined) node.currentHUEDevice.color = _event.color; // Update the internal object representing the current light
|
|
428
|
-
return;
|
|
429
422
|
}
|
|
430
423
|
if (_event.hasOwnProperty("dimming")) {
|
|
431
424
|
// Every once on a time, the light transmit the brightness value of 0.39.
|
|
@@ -434,29 +427,42 @@ module.exports = function (RED) {
|
|
|
434
427
|
if (node.currentHUEDevice.hasOwnProperty('on') && node.currentHUEDevice.on.on === false && _event.dimming.brightness === 0) return;
|
|
435
428
|
if (node.currentHUEDevice.on.on === false) node.updateKNXLightState(_event.dimming.brightness > 0);
|
|
436
429
|
node.updateKNXBrightnessState(_event.dimming.brightness);
|
|
437
|
-
if (node.currentHUEDevice !== undefined) node.currentHUEDevice.dimming = _event.dimming; // Update the internal object representing the current light
|
|
438
430
|
// If the brightness reaches zero, the hue lamp "on" property must be set to zero as well
|
|
439
431
|
if (_event.dimming.brightness === 0) {
|
|
440
|
-
node.serverHue.hueManager.writeHueQueueAdd(
|
|
432
|
+
node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, { on: { on: false } }, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
|
|
441
433
|
}
|
|
442
|
-
return;
|
|
443
434
|
}
|
|
444
435
|
if (_event.hasOwnProperty("color_temperature")) {
|
|
445
436
|
node.updateKNXLightHSVState(_event.color_temperature.mirek);
|
|
446
|
-
if (node.currentHUEDevice !== undefined) node.currentHUEDevice.color_temperature = _event.color_temperature; // Update the internal object representing the current light
|
|
447
|
-
return;
|
|
448
437
|
}
|
|
438
|
+
|
|
439
|
+
// Update the current HUE Device with the new _event
|
|
440
|
+
function copiaOggettoRicorsivo(objDestinazione, objOrigine) {
|
|
441
|
+
for (const prop in objOrigine) {
|
|
442
|
+
if (typeof objOrigine[prop] === "object" && objOrigine[prop] !== null) {
|
|
443
|
+
// Se la proprietà è un oggetto, copiamola in modo ricorsivo
|
|
444
|
+
objDestinazione[prop] = objDestinazione[prop] || {};
|
|
445
|
+
copiaOggettoRicorsivo(objDestinazione[prop], objOrigine[prop]);
|
|
446
|
+
} else {
|
|
447
|
+
// Altrimenti, copia il valore della proprietà
|
|
448
|
+
objDestinazione[prop] = objOrigine[prop];
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
// Copia l'oggettoOrigine nell'oggettoDestinazione mantenendo le proprietà esistenti
|
|
453
|
+
copiaOggettoRicorsivo(node.currentHUEDevice, _event);
|
|
449
454
|
}
|
|
450
455
|
} catch (error) {
|
|
451
456
|
node.status({
|
|
452
457
|
fill: "red",
|
|
453
458
|
shape: "dot",
|
|
454
|
-
text:
|
|
459
|
+
text: `HUE->KNX error ${node.id} ${error.message}`,
|
|
455
460
|
});
|
|
456
461
|
}
|
|
457
462
|
};
|
|
458
463
|
|
|
459
|
-
|
|
464
|
+
// Leave the name after "function", to avoid <anonymous function> in the stack trace, in caso of errors.
|
|
465
|
+
node.updateKNXBrightnessState = function updateKNXBrightnessState(_value) {
|
|
460
466
|
if (config.GALightBrightnessState !== undefined && config.GALightBrightnessState !== "") {
|
|
461
467
|
const knxMsgPayload = {};
|
|
462
468
|
knxMsgPayload.topic = config.GALightBrightnessState;
|
|
@@ -480,7 +486,8 @@ module.exports = function (RED) {
|
|
|
480
486
|
});
|
|
481
487
|
}
|
|
482
488
|
};
|
|
483
|
-
|
|
489
|
+
|
|
490
|
+
node.updateKNXLightState = function updateKNXBrightnessState(_value) {
|
|
484
491
|
try {
|
|
485
492
|
const knxMsgPayload = {};
|
|
486
493
|
knxMsgPayload.topic = config.GALightState;
|
|
@@ -513,7 +520,7 @@ module.exports = function (RED) {
|
|
|
513
520
|
}
|
|
514
521
|
};
|
|
515
522
|
|
|
516
|
-
node.updateKNXLightHSVState = function (_value) {
|
|
523
|
+
node.updateKNXLightHSVState = function updateKNXLightState(_value) {
|
|
517
524
|
if (config.GALightHSVState !== undefined && config.GALightHSVState !== "") {
|
|
518
525
|
const knxMsgPayload = {};
|
|
519
526
|
knxMsgPayload.topic = config.GALightHSVState;
|
|
@@ -541,7 +548,7 @@ module.exports = function (RED) {
|
|
|
541
548
|
}
|
|
542
549
|
};
|
|
543
550
|
|
|
544
|
-
node.updateKNXLightColorState = function (_value) {
|
|
551
|
+
node.updateKNXLightColorState = function updateKNXLightColorState(_value) {
|
|
545
552
|
if (config.GALightColorState !== undefined && config.GALightColorState !== "") {
|
|
546
553
|
const knxMsgPayload = {};
|
|
547
554
|
knxMsgPayload.topic = config.GALightColorState;
|
|
@@ -549,7 +556,7 @@ module.exports = function (RED) {
|
|
|
549
556
|
knxMsgPayload.payload = hueColorConverter.ColorConverter.xyBriToRgb(
|
|
550
557
|
_value.xy.x,
|
|
551
558
|
_value.xy.y,
|
|
552
|
-
node.currentHUEDevice !== undefined ? node.currentHUEDevice.dimming.brightness : 100
|
|
559
|
+
node.currentHUEDevice !== undefined ? node.currentHUEDevice.dimming.brightness : 100,
|
|
553
560
|
);
|
|
554
561
|
// Send to KNX bus
|
|
555
562
|
if (knxMsgPayload.topic !== "" && knxMsgPayload.topic !== undefined) {
|
|
@@ -577,30 +584,18 @@ module.exports = function (RED) {
|
|
|
577
584
|
}
|
|
578
585
|
if (node.serverHue) {
|
|
579
586
|
node.serverHue.removeClient(node);
|
|
580
|
-
// I must get the light object, to store it in the node.currentHUEDevice variable
|
|
581
|
-
// I queue the state request, by passing the callback to call whenever the HUE bridge send me the light status async
|
|
582
587
|
if (node.serverHue !== null && node.serverHue.hueManager !== null) {
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
shape: "ring",
|
|
595
|
-
text: "Connected. Is on: ",
|
|
596
|
-
payload: node.currentHUEDevice.on.on === true,
|
|
597
|
-
});
|
|
598
|
-
}
|
|
599
|
-
);
|
|
600
|
-
} catch (err) {
|
|
601
|
-
RED.log.error(`Errore knxUltimateHueLight node.currentHUEDevice ${err.message}`);
|
|
602
|
-
}
|
|
603
|
-
})();
|
|
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
|
+
try {
|
|
595
|
+
node.serverHue.addClient(node);
|
|
596
|
+
} catch (err) {
|
|
597
|
+
RED.log.error(`Errore knxUltimateHueLight node.currentHUEDevice ${err.message}`);
|
|
598
|
+
}
|
|
604
599
|
}
|
|
605
600
|
}
|
|
606
601
|
|
|
@@ -223,7 +223,7 @@
|
|
|
223
223
|
<script src="https://kit.fontawesome.com/11f26b4500.js" crossorigin="anonymous"></script>
|
|
224
224
|
|
|
225
225
|
<script type="text/markdown" data-help-name="knxUltimateHueLightSensor">
|
|
226
|
-
|
|
226
|
+
This node lets you get the events from your HUE motion device.
|
|
227
227
|
|
|
228
228
|
Here you can get the HUE Light Sensors light events, that represents a lux value, evetytime the ambient light changes.<br/>
|
|
229
229
|
Start typing in the GA field, the name or group address of your KNX device, the avaiable devices start showing up while you're typing.
|
|
@@ -80,10 +80,10 @@ module.exports = function (RED) {
|
|
|
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
82
|
(async () => {
|
|
83
|
-
node.serverHue.addClient(node)
|
|
83
|
+
node.serverHue.addClient(node);
|
|
84
84
|
try {
|
|
85
85
|
node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, null, 'getLightLevel', (jLight) => {
|
|
86
|
-
node.handleSendHUE(jLight)
|
|
86
|
+
node.handleSendHUE(jLight);
|
|
87
87
|
})
|
|
88
88
|
} catch (error) {
|
|
89
89
|
RED.log.error('Errore knxUltimateHueLightSensor subscribing: ' + error.message)
|
|
@@ -219,8 +219,8 @@ 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"
|
|
223
|
-
|
|
222
|
+
<script type="text/markdown" data-help-name="knxUltimateHueMotion"
|
|
223
|
+
This node lets you get the events from your HUE motion device.
|
|
224
224
|
|
|
225
225
|
Here you can get the HUE motion events.<br/>
|
|
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.
|
|
@@ -260,7 +260,7 @@
|
|
|
260
260
|
<script src="https://kit.fontawesome.com/11f26b4500.js" crossorigin="anonymous"></script>
|
|
261
261
|
|
|
262
262
|
<script type="text/markdown" data-help-name="knxUltimateHueScene">
|
|
263
|
-
|
|
263
|
+
This node lets you recall a HUE scene, via KNX.
|
|
264
264
|
|
|
265
265
|
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.
|
|
266
266
|
|
|
@@ -226,7 +226,7 @@
|
|
|
226
226
|
<script src="https://kit.fontawesome.com/11f26b4500.js" crossorigin="anonymous"></script>
|
|
227
227
|
|
|
228
228
|
<script type="text/markdown" data-help-name="knxUltimateHueTapDial">
|
|
229
|
-
|
|
229
|
+
This node lets you get the events from your HUE rotary device, for example the Tap Dial.
|
|
230
230
|
|
|
231
231
|
The Tap Dial device has 4 buttons and 1 rotary knob.
|
|
232
232
|
You can find the 4 buttons in the **Hue Button node**, not here!
|
|
@@ -147,7 +147,6 @@ module.exports = function (RED) {
|
|
|
147
147
|
if (node.timerDimStop !== undefined) clearTimeout(node.timerDimStop);
|
|
148
148
|
node.isTimerDimStopRunning = true;
|
|
149
149
|
node.timerDimStop = setTimeout(() => {
|
|
150
|
-
console.log('Stop banana');
|
|
151
150
|
// KNX Stop DIM
|
|
152
151
|
knxMsgPayload.payload = { decr_incr: 0, data: 0 }; // Payload for the output msg
|
|
153
152
|
// Send to KNX bus
|
|
@@ -158,7 +157,7 @@ module.exports = function (RED) {
|
|
|
158
157
|
}
|
|
159
158
|
if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX Stop DIM' + ` (${new Date().getDate()}, ${new Date().toLocaleTimeString()})` });
|
|
160
159
|
node.isTimerDimStopRunning = false;
|
|
161
|
-
},
|
|
160
|
+
}, 500);
|
|
162
161
|
};
|
|
163
162
|
|
|
164
163
|
// On each deploy, unsubscribe+resubscribe
|
|
@@ -220,8 +220,8 @@
|
|
|
220
220
|
</script>
|
|
221
221
|
<script src="https://kit.fontawesome.com/11f26b4500.js" crossorigin="anonymous"></script>
|
|
222
222
|
|
|
223
|
-
<script type="text/markdown" data-help-name="knxUltimateHueTemperatureSensor"
|
|
224
|
-
|
|
223
|
+
<script type="text/markdown" data-help-name="knxUltimateHueTemperatureSensor"
|
|
224
|
+
This node lets you get the events from your HUE temperature device.
|
|
225
225
|
|
|
226
226
|
Here you can get the HUE temperature events, that represents a celsius value, evetytime the ambient temp changes.<br/>
|
|
227
227
|
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.
|
|
@@ -376,7 +376,7 @@ The node turns off one device (or multiple devices) at a time, based on the orde
|
|
|
376
376
|
|
|
377
377
|
Here you can add devices to turn off in case of overload. <br/>
|
|
378
378
|
Choose the device to turn off. Enter the device name or its group address. <br/>
|
|
379
|
-
Enter any group address that indicates the consumption of the device chosen in the first line. **
|
|
379
|
+
Enter any group address that indicates the consumption of the device chosen in the first line. **This is an optional parameter**. If the device is consuming more than a certain number of Watts, it means that it is in use. If it consumes less, the device will be considered "not in use" and both this and the next will be turned off at once.<br/>
|
|
380
380
|
If *Automatic recovery* is enabled, the device is automatically reactivated when the "reset delay" expires.
|
|
381
381
|
|
|
382
382
|
### Inputs
|
package/nodes/utils/hueEngine.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable max-len */
|
|
2
2
|
const { EventEmitter } = require('events');
|
|
3
3
|
const EventSource = require('eventsource');
|
|
4
|
-
const http = require('./http
|
|
4
|
+
const http = require('./http');
|
|
5
5
|
|
|
6
6
|
class classHUE extends EventEmitter {
|
|
7
7
|
constructor(_hueBridgeIP, _username, _clientkey, _bridgeid, _sysLogger) {
|
|
@@ -89,6 +89,7 @@ class classHUE extends EventEmitter {
|
|
|
89
89
|
if (this.commandQueue.length > 0) {
|
|
90
90
|
// const jRet = { ...this.commandQueue.shift() } //Clone the object by value
|
|
91
91
|
const jRet = this.commandQueue.shift();
|
|
92
|
+
// jRet is ({ _lightID, _state, _operation });;
|
|
92
93
|
switch (jRet._operation) {
|
|
93
94
|
case 'setLight':
|
|
94
95
|
// It can be a light or a grouped light
|
|
@@ -105,23 +106,6 @@ class classHUE extends EventEmitter {
|
|
|
105
106
|
if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.info(`KNXUltimatehueEngine: classHUE: handleQueue: setLight grouped_light: ${error.message}`);
|
|
106
107
|
}
|
|
107
108
|
break;
|
|
108
|
-
case 'getLight':
|
|
109
|
-
try {
|
|
110
|
-
const jReturn = await this.hueApiV2.get(`/resource/light/${jRet._lightID}`);
|
|
111
|
-
jRet._callback(jReturn[0]); // Need to call the callback, because the event is absolutely async
|
|
112
|
-
} catch (error) {
|
|
113
|
-
if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.info(`KNXUltimatehueEngine: classHUE: handleQueue: getLight light: ${error.message}`);
|
|
114
|
-
}
|
|
115
|
-
break;
|
|
116
|
-
case 'getGroupedLight':
|
|
117
|
-
try {
|
|
118
|
-
const jReturn = await this.hueApiV2.get(`/resource/grouped_light/${jRet._lightID}`);
|
|
119
|
-
// Need to call the callback, because the event is absolutely async
|
|
120
|
-
jRet._callback(jReturn[0]);
|
|
121
|
-
} catch (error) {
|
|
122
|
-
if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.info(`KNXUltimatehueEngine: classHUE: handleQueue: getLight grouped_light: ${error.message}`);
|
|
123
|
-
}
|
|
124
|
-
break;
|
|
125
109
|
case 'setScene':
|
|
126
110
|
try {
|
|
127
111
|
const sceneID = jRet._lightID;
|
|
@@ -146,14 +130,13 @@ class classHUE extends EventEmitter {
|
|
|
146
130
|
case 'getBattery':
|
|
147
131
|
try {
|
|
148
132
|
const jReturn = await this.hueApiV2.get(`/resource/device_power/${jRet._lightID}`);
|
|
149
|
-
jRet._callback(jReturn[0]); // Need to call the callback, because the event is absolutely async
|
|
150
133
|
} catch (error) {
|
|
151
134
|
if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error(`KNXUltimatehueEngine: classHUE: handleQueue: getBattery: ${error.message}`);
|
|
152
135
|
}
|
|
136
|
+
break;
|
|
153
137
|
case 'getLightLevel':
|
|
154
138
|
try {
|
|
155
139
|
const jReturn = await this.hueApiV2.get(`/resource/light_level/${jRet._lightID}`);
|
|
156
|
-
jRet._callback(jReturn[0]); // Need to call the callback, because the event is absolutely async
|
|
157
140
|
} catch (error) {
|
|
158
141
|
if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error(`KNXUltimatehueEngine: classHUE: handleQueue: getLightLevel: ${error.message}`);
|
|
159
142
|
}
|
|
@@ -161,7 +144,6 @@ class classHUE extends EventEmitter {
|
|
|
161
144
|
case 'getTemperature':
|
|
162
145
|
try {
|
|
163
146
|
const jReturn = await this.hueApiV2.get(`/resource/temperature/${jRet._lightID}`);
|
|
164
|
-
jRet._callback(jReturn[0]); // Need to call the callback, because the event is absolutely async
|
|
165
147
|
} catch (error) {
|
|
166
148
|
if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error(`KNXUltimatehueEngine: classHUE: handleQueue: getTemperature: ${error.message}`);
|
|
167
149
|
}
|
|
@@ -174,11 +156,9 @@ class classHUE extends EventEmitter {
|
|
|
174
156
|
this.timerwriteQueueAdd = setTimeout(this.handleQueue, 200);
|
|
175
157
|
};
|
|
176
158
|
|
|
177
|
-
writeHueQueueAdd = async (_lightID, _state, _operation
|
|
159
|
+
writeHueQueueAdd = async (_lightID, _state, _operation) => {
|
|
178
160
|
// Add the new item
|
|
179
|
-
this.commandQueue.push({
|
|
180
|
-
_lightID, _state, _operation, _callback,
|
|
181
|
-
});
|
|
161
|
+
this.commandQueue.push({ _lightID, _state, _operation });
|
|
182
162
|
};
|
|
183
163
|
// ######################################
|
|
184
164
|
|