node-red-contrib-knx-ultimate 2.4.23 → 2.4.26
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 +15 -1
- package/README.md +5 -2
- package/nodes/hue-config.js +57 -5
- package/nodes/knxUltimateHueLight.js +310 -63
- package/nodes/utils/utils.js +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,10 +6,24 @@
|
|
|
6
6
|
|
|
7
7
|
# CHANGELOG
|
|
8
8
|
|
|
9
|
+
**Version 2.4.26** - Mai 2024<br/>
|
|
10
|
+
- Warning: Node-Red version **equals or major than 3.1.1** is needed to run this node.<br/>
|
|
11
|
+
- HUE light node: fixed status refresh of kelvin and brightness of a light belonging to a grouped_light.<br/>
|
|
12
|
+
- KNOW ISSUE: HUE light node: grouped_lights: the status group address is updated multiple times, equals to the lights contained in the grouped_light. To avoid repeating the same telegram multiple times, simply enable the RBE filter on the KNX node. <br/>
|
|
13
|
+
|
|
14
|
+
**Version 2.4.25** - Mai 2024<br/>
|
|
15
|
+
- Warning: Node-Red version **equals or major than 3.1.1** is needed to run this node.<br/>
|
|
16
|
+
- HUE light node: fixed status sent to the KNX bus after issuing a read request for grouped_lights.<br/>
|
|
17
|
+
- KNOW ISSUE: HUE light node: grouped_lights: the status group address is updated multiple times, equals to the lights contained in the grouped_light. To avoid repeating the same telegram multiple times, simply enable the RBE filter on the KNX node. <br/>
|
|
18
|
+
|
|
19
|
+
**Version 2.4.24** - Mai 2024<br/>
|
|
20
|
+
- Warning: Node-Red version **equals or major than 3.1.1** is needed to run this node.<br/>
|
|
21
|
+
- HUE light node: fixed kelvin temp status with datapoint 7.600, that sent wrong values to the bus.<br/>
|
|
22
|
+
|
|
9
23
|
**Version 2.4.23** - Mai 2024<br/>
|
|
10
24
|
- Warning: Node-Red version **equals or major than 3.1.1** is needed to run this node.<br/>
|
|
11
25
|
- HUE Scene node: fixed max scene count to 64.<br/>
|
|
12
|
-
|
|
26
|
+
|
|
13
27
|
**Version 2.4.22** - April 2024<br/>
|
|
14
28
|
- Warning: this version uses the Node-Red plugin system; the Node-Red version must be **equals or major than 3.1.1**<br/>
|
|
15
29
|
- HUE button node: NEW: now you can select the value and the dim direction to be transmitted, when Toggle Status is set to unchecked. Thanks @cybersmart-eu for the suggestion.<br/>
|
package/README.md
CHANGED
|
@@ -50,14 +50,17 @@ msg.payload = {red:255, green:200, blue:30} // Put some colors in our life
|
|
|
50
50
|
|
|
51
51
|
* See <a href="https://github.com/Supergiovane/node-red-contrib-knx-ultimate/blob/master/CHANGELOG.md">here the changelog</a>
|
|
52
52
|
|
|
53
|
+
|
|
54
|
+
## SUPPORTED TECHNOLOGIES
|
|
55
|
+
|
|
53
56
|
|Technology|Supported|
|
|
54
57
|
|--|--|
|
|
55
58
|
| KNX Tunnelling |  |
|
|
56
59
|
| KNX Routing |  |
|
|
57
60
|
| KNX Secure Tunnelling |  |
|
|
58
61
|
| KNX Secure Routing |  |
|
|
59
|
-
| KNX 3rd PARTY IOT API |  |
|
|
60
|
-
|
|
62
|
+
| KNX 3rd PARTY IOT API client |  |
|
|
63
|
+
| Matter |  |
|
|
61
64
|
|
|
62
65
|
<br/>
|
|
63
66
|
|
package/nodes/hue-config.js
CHANGED
|
@@ -8,6 +8,7 @@ const cloneDeep = require("lodash/cloneDeep");
|
|
|
8
8
|
const HueClass = require("./utils/hueEngine").classHUE;
|
|
9
9
|
const loggerEngine = require("./utils/sysLogger");
|
|
10
10
|
const hueColorConverter = require("./utils/colorManipulators/hueColorConverter");
|
|
11
|
+
const { AsyncObservable } = require("@project-chip/matter-node.js/util");
|
|
11
12
|
|
|
12
13
|
module.exports = (RED) => {
|
|
13
14
|
function hueConfig(config) {
|
|
@@ -50,6 +51,9 @@ module.exports = (RED) => {
|
|
|
50
51
|
node.nodeClients.forEach((_oClient) => {
|
|
51
52
|
const oClient = _oClient;
|
|
52
53
|
try {
|
|
54
|
+
// if (_event.type === "light" || _event.type === "grouped_light") {
|
|
55
|
+
// console.log(_event);
|
|
56
|
+
// }
|
|
53
57
|
if (oClient.handleSendHUE !== undefined) oClient.handleSendHUE(_event);
|
|
54
58
|
} catch (error) {
|
|
55
59
|
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`Errore node.hueManager.on(event): ${error.message}`);
|
|
@@ -65,7 +69,11 @@ module.exports = (RED) => {
|
|
|
65
69
|
node.timerDoInitialRead = setTimeout(() => {
|
|
66
70
|
(async () => {
|
|
67
71
|
try {
|
|
68
|
-
|
|
72
|
+
if (node.hueAllResources === undefined) {
|
|
73
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info(`HTTP getting resource from HUE bridge : ${node.name}`);
|
|
74
|
+
await node.loadResourcesFromHUEBridge();
|
|
75
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info(`Total HUE resources count : ${node.hueAllResources.length}`);
|
|
76
|
+
}
|
|
69
77
|
} catch (error) {
|
|
70
78
|
node.linkStatus = "disconnected";
|
|
71
79
|
node.nodeClients.forEach((_oClient) => {
|
|
@@ -114,6 +122,7 @@ module.exports = (RED) => {
|
|
|
114
122
|
};
|
|
115
123
|
node.startWatchdogTimer();
|
|
116
124
|
|
|
125
|
+
// Functions called from the nodes ----------------------------------------------------------------
|
|
117
126
|
// Query the HUE Bridge to return the resources
|
|
118
127
|
node.loadResourcesFromHUEBridge = async () => {
|
|
119
128
|
if (node.linkStatus === "disconnected") return;
|
|
@@ -136,7 +145,7 @@ module.exports = (RED) => {
|
|
|
136
145
|
});
|
|
137
146
|
_node.currentHUEDevice = cloneDeep(oHUEDevice); // Copy by Value and not by ref
|
|
138
147
|
if (_node.initializingAtStart === true) {
|
|
139
|
-
_node.handleSendHUE(
|
|
148
|
+
_node.handleSendHUE(_node.currentHUEDevice); // Pass by value
|
|
140
149
|
}
|
|
141
150
|
}
|
|
142
151
|
}
|
|
@@ -173,11 +182,13 @@ module.exports = (RED) => {
|
|
|
173
182
|
};
|
|
174
183
|
|
|
175
184
|
// Return an array of light belonging to the groupID
|
|
176
|
-
node.getAllLightsBelongingToTheGroup = async function getAllLightsBelongingToTheGroup(_groupID) {
|
|
185
|
+
node.getAllLightsBelongingToTheGroup = async function getAllLightsBelongingToTheGroup(_groupID, refreshResourcesFromBridge = true) {
|
|
177
186
|
if (node.hueAllResources === undefined || node.hueAllResources === null) return;
|
|
178
187
|
const retArr = [];
|
|
179
188
|
try {
|
|
180
|
-
|
|
189
|
+
if (refreshResourcesFromBridge === true) {
|
|
190
|
+
await node.loadResourcesFromHUEBridge();
|
|
191
|
+
}
|
|
181
192
|
node.hueAllResources.forEach((res) => {
|
|
182
193
|
if (res.services !== undefined && res.services.length > 0) {
|
|
183
194
|
res.services.forEach((serv) => {
|
|
@@ -187,7 +198,8 @@ module.exports = (RED) => {
|
|
|
187
198
|
for (let index = 0; index < children.length; index++) {
|
|
188
199
|
const element = children[index];
|
|
189
200
|
const oLight = node.hueAllResources.filter((a) => a.id === element.rid);
|
|
190
|
-
if (oLight !== null && oLight !== undefined) retArr.push({ groupID: _groupID, light: oLight });
|
|
201
|
+
//if (oLight !== null && oLight !== undefined) retArr.push({ groupID: _groupID, light: oLight[0] });
|
|
202
|
+
if (oLight !== null && oLight !== undefined) retArr.push(oLight[0]);
|
|
191
203
|
}
|
|
192
204
|
}
|
|
193
205
|
}
|
|
@@ -363,6 +375,46 @@ module.exports = (RED) => {
|
|
|
363
375
|
}
|
|
364
376
|
};
|
|
365
377
|
|
|
378
|
+
/**
|
|
379
|
+
* Get average color XY from a light array
|
|
380
|
+
* @param {array} _arrayLights - Light array
|
|
381
|
+
* @returns { x,y,mirek,brightness } - Object containing all infos
|
|
382
|
+
*/
|
|
383
|
+
node.getAverageColorsXYBrightnessAndTemperature = async function getAverageColorsXYBrightnessAndTemperature(_arrayLights) {
|
|
384
|
+
let x; let y; let mirek; let brightness;
|
|
385
|
+
let countColor = 0, countColor_Temperature = 0, countDimming = 0;
|
|
386
|
+
_arrayLights.forEach((element) => {
|
|
387
|
+
if (element.color !== undefined && element.color.xy !== undefined) {
|
|
388
|
+
if (x === undefined) { x = 0; y = 0; }
|
|
389
|
+
x += element.color.xy.x;
|
|
390
|
+
y += element.color.xy.y;
|
|
391
|
+
countColor += 1;
|
|
392
|
+
}
|
|
393
|
+
if (element.color_temperature !== undefined && element.color_temperature.mirek !== undefined) {
|
|
394
|
+
if (mirek === undefined) mirek = 0;
|
|
395
|
+
mirek += element.color_temperature.mirek;
|
|
396
|
+
countColor_Temperature += 1;
|
|
397
|
+
}
|
|
398
|
+
if (element.dimming !== undefined && element.dimming.brightness !== undefined) {
|
|
399
|
+
if (brightness === undefined) brightness = 0;
|
|
400
|
+
brightness += element.dimming.brightness;
|
|
401
|
+
countDimming += 1;
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
// Calculate and return the averages
|
|
405
|
+
const retX = countColor === 0 ? undefined : x / countColor;
|
|
406
|
+
const retY = countColor === 0 ? undefined : y / countColor;
|
|
407
|
+
const retMirek = countColor_Temperature === 0 ? undefined : mirek / countColor_Temperature;
|
|
408
|
+
const retBrightness = countDimming === 0 ? undefined : brightness / countDimming;
|
|
409
|
+
|
|
410
|
+
return {
|
|
411
|
+
x: retX, y: retY, mirek: retMirek, brightness: retBrightness
|
|
412
|
+
};
|
|
413
|
+
};
|
|
414
|
+
// END functions called from the nodes ----------------------------------------------------------------
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
|
|
366
418
|
node.addClient = (_Node) => {
|
|
367
419
|
// Update the node hue device, as soon as a node register itself to hue-config nodeClients
|
|
368
420
|
if (node.nodeClients.filter((x) => x.id === _Node.id).length === 0) {
|
|
@@ -156,14 +156,14 @@ module.exports = function (RED) {
|
|
|
156
156
|
// The DayNight has switched into day, so restore the previous light state, belonging to the group
|
|
157
157
|
let bAtLeastOneIsOn = false;
|
|
158
158
|
for (let index = 0; index < node.HUELightsBelongingToGroupWhileDaytime.length; index++) { // Ensure, at least 1 lamp was on, otherwise turn all lamps on
|
|
159
|
-
const element = node.HUELightsBelongingToGroupWhileDaytime[index]
|
|
159
|
+
const element = node.HUELightsBelongingToGroupWhileDaytime[index];
|
|
160
160
|
if (element.on.on === true) {
|
|
161
161
|
bAtLeastOneIsOn = true;
|
|
162
162
|
break;
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
165
|
for (let index = 0; index < node.HUELightsBelongingToGroupWhileDaytime.length; index++) {
|
|
166
|
-
const element = node.HUELightsBelongingToGroupWhileDaytime[index]
|
|
166
|
+
const element = node.HUELightsBelongingToGroupWhileDaytime[index];
|
|
167
167
|
if (bAtLeastOneIsOn === true) {
|
|
168
168
|
state = { on: element.on, dimming: element.dimming, color: element.color, color_temperature: element.color_temperature };
|
|
169
169
|
} else {
|
|
@@ -317,7 +317,7 @@ module.exports = function (RED) {
|
|
|
317
317
|
if (node.isGrouped_light === true) {
|
|
318
318
|
(async () => {
|
|
319
319
|
try {
|
|
320
|
-
const retLights = await node.serverHue.getAllLightsBelongingToTheGroup(node.hueDevice);
|
|
320
|
+
const retLights = await node.serverHue.getAllLightsBelongingToTheGroup(node.hueDevice, false);
|
|
321
321
|
node.HUELightsBelongingToGroupWhileDaytime = cloneDeep(retLights); // DayTime has switched to false: save the lights belonging to the group into the HUELightsBelongingToGroupWhileDaytime array
|
|
322
322
|
} catch (error) { /* empty */ }
|
|
323
323
|
})();
|
|
@@ -509,16 +509,56 @@ module.exports = function (RED) {
|
|
|
509
509
|
if (ret !== undefined) node.updateKNXLightColorState(node.currentHUEDevice.color, "response");
|
|
510
510
|
break;
|
|
511
511
|
case config.GALightKelvinPercentageState:
|
|
512
|
+
// The kelvin level belongs to the group defice, so i don't need to get the first light in the collection (if the device is a grouped_light)
|
|
512
513
|
ret = node.currentHUEDevice.color_temperature.mirek;
|
|
513
514
|
if (ret !== undefined) node.updateKNXLightKelvinPercentageState(ret, "response");
|
|
515
|
+
// if (node.isGrouped_light === false) {
|
|
516
|
+
// ret = node.currentHUEDevice.color_temperature.mirek;
|
|
517
|
+
// if (ret !== undefined) node.updateKNXLightKelvinPercentageState(ret, "response");
|
|
518
|
+
// } else {
|
|
519
|
+
// (async () => {
|
|
520
|
+
// try {
|
|
521
|
+
// // Find the first light in the collection, having the color_temperature capabilities
|
|
522
|
+
// const devices = await node.serverHue.getAllLightsBelongingToTheGroup(node.hueDevice, false);
|
|
523
|
+
// for (let index = 0; index < devices.length; index++) {
|
|
524
|
+
// const element = devices[index];
|
|
525
|
+
// if (element.light[0].color_temperature !== undefined) {
|
|
526
|
+
// ret = element.light[0].color_temperature.mirek;
|
|
527
|
+
// break;
|
|
528
|
+
// }
|
|
529
|
+
// }
|
|
530
|
+
// if (ret !== undefined) node.updateKNXLightKelvinPercentageState(ret, "response");
|
|
531
|
+
// } catch (error) { /* empty */ }
|
|
532
|
+
// })();
|
|
533
|
+
// }
|
|
514
534
|
break;
|
|
515
535
|
case config.GALightBrightnessState:
|
|
516
536
|
ret = node.currentHUEDevice.dimming.brightness;
|
|
517
537
|
if (ret !== undefined) node.updateKNXBrightnessState(ret, "response");
|
|
518
538
|
break;
|
|
519
539
|
case config.GALightKelvinState:
|
|
540
|
+
// The kelvin level belongs to the group defice, so i don't need to get the first light in the collection (if the device is a grouped_light)
|
|
520
541
|
ret = node.currentHUEDevice.color_temperature.mirek;
|
|
521
542
|
if (ret !== undefined) node.updateKNXLightKelvinState(ret, "response");
|
|
543
|
+
// if (node.isGrouped_light === false) {
|
|
544
|
+
// ret = node.currentHUEDevice.color_temperature.mirek;
|
|
545
|
+
// if (ret !== undefined) node.updateKNXLightKelvinState(ret, "response");
|
|
546
|
+
// } else {
|
|
547
|
+
// (async () => {
|
|
548
|
+
// try {
|
|
549
|
+
// // Find the first light in the collection, having the color_temperature capabilities
|
|
550
|
+
// const devices = await node.serverHue.getAllLightsBelongingToTheGroup(node.hueDevice, false);
|
|
551
|
+
// for (let index = 0; index < devices.length; index++) {
|
|
552
|
+
// const element = devices[index];
|
|
553
|
+
// if (element.light[0].color_temperature !== undefined) {
|
|
554
|
+
// ret = element.light[0].color_temperature.mirek;
|
|
555
|
+
// break;
|
|
556
|
+
// }
|
|
557
|
+
// }
|
|
558
|
+
// if (ret !== undefined) node.updateKNXLightKelvinState(ret, "response");
|
|
559
|
+
// } catch (error) { /* empty */ }
|
|
560
|
+
// })();
|
|
561
|
+
// }
|
|
522
562
|
break;
|
|
523
563
|
default:
|
|
524
564
|
break;
|
|
@@ -870,22 +910,87 @@ module.exports = function (RED) {
|
|
|
870
910
|
};
|
|
871
911
|
// ***********************************************************
|
|
872
912
|
|
|
873
|
-
node.handleSendHUE = (_event) => {
|
|
913
|
+
node.handleSendHUE = async (_event) => {
|
|
914
|
+
if (_event === undefined) return;
|
|
915
|
+
if (_event.type !== 'grouped_light' && _event.type !== 'light') return;
|
|
916
|
+
|
|
917
|
+
// !!!! >>> if the node is a grouped_light, the only values required, thus present, by HUE apis are "on" and "dimming".
|
|
918
|
+
// !!!! >>> For all others properties like for example color and tunable white, i must get the data from one of the child lights.
|
|
919
|
+
|
|
920
|
+
//(async () => {
|
|
921
|
+
// Check and set canContinue true or false ------------------------------------------------------------
|
|
874
922
|
try {
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
923
|
+
if (node.currentHUEDevice === undefined || node.serverHue === null || node.serverHue === undefined) {
|
|
924
|
+
node.setNodeStatusHue({
|
|
925
|
+
fill: "red",
|
|
926
|
+
shape: "ring",
|
|
927
|
+
text: "Rejected HUE light settings. I'm still not ready...",
|
|
928
|
+
payload: "",
|
|
929
|
+
});
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
const receivedHUEObject = cloneDeep(_event);
|
|
934
|
+
let canContinue = false;
|
|
935
|
+
// If the current node is a grouped lights, i must check wether the receivedHUEObject (containing a light) belongs to the node lights collection.
|
|
936
|
+
if (node.isGrouped_light === true && receivedHUEObject.type === 'grouped_light' && receivedHUEObject.id === node.hueDevice) {
|
|
937
|
+
canContinue = true;
|
|
938
|
+
} else if (node.isGrouped_light === true && receivedHUEObject.type === 'light') {
|
|
939
|
+
// Handling of not by HUE handled Color Temperature and ColorXY
|
|
940
|
+
// let groupChilds = undefined;
|
|
941
|
+
// let AverageColorsXYBrightnessAndTemperature = undefined; // Average color xy and color temp if the node is a grouped light.
|
|
942
|
+
// // If the current node is a grouped lights, i must check wether the receivedHUEObject (containing a light) belongs to the node lights collection.
|
|
943
|
+
// try {
|
|
944
|
+
// // Find all the lights in the collection, having the color_temperature capabilities, belonging to the group.
|
|
945
|
+
// const devices = await node.serverHue.getAllLightsBelongingToTheGroup(node.hueDevice, true);
|
|
946
|
+
// groupChilds = [];
|
|
947
|
+
// for (let index = 0; index < devices.length; index++) {
|
|
948
|
+
// const element = devices[index];
|
|
949
|
+
// if (receivedHUEObject.id === element.id) {
|
|
950
|
+
// //if (groupChilds === undefined) groupChilds = [];
|
|
951
|
+
// // groupChilds.push(element);
|
|
952
|
+
// let modifiedLight = cloneDeep(element);
|
|
953
|
+
// // The dimming is not necessary, beacause the HUE API already sends a group_light event with the average brightness //if (receivedHUEObject.dimming !== undefined) modifiedLight.dimming = { brightness: receivedHUEObject.dimming.brightness };
|
|
954
|
+
// if (receivedHUEObject.color !== undefined && receivedHUEObject.color.xy !== undefined) modifiedLight.color = receivedHUEObject.color;
|
|
955
|
+
// if (receivedHUEObject.color_temperature !== undefined) modifiedLight.color_temperature = receivedHUEObject.color_temperature;
|
|
956
|
+
// groupChilds.push(modifiedLight);
|
|
957
|
+
// } else {
|
|
958
|
+
// // Simply ppend the light
|
|
959
|
+
// groupChilds.push(element);
|
|
960
|
+
// }
|
|
961
|
+
// }
|
|
962
|
+
|
|
963
|
+
// // Use the arithmetic average of color xy, brightness and color temperature "averageColorXYandKelvinOfGroupedLights"
|
|
964
|
+
// if (groupChilds !== undefined) AverageColorsXYBrightnessAndTemperature = await node.serverHue.getAverageColorsXYBrightnessAndTemperature(groupChilds);
|
|
965
|
+
|
|
966
|
+
// // Set the new values based on average calculated above
|
|
967
|
+
// try {
|
|
968
|
+
// // CHECK FIRST THE COLOR_TEMPERATURE, because it can be undefined, because the current selected color
|
|
969
|
+
// // is out of the mirek range, so it cannot be represented with the colore temperature.
|
|
970
|
+
// if (receivedHUEObject.color_temperature !== undefined && AverageColorsXYBrightnessAndTemperature.mirek !== undefined) {
|
|
971
|
+
// receivedHUEObject.color_temperature = { mirek: AverageColorsXYBrightnessAndTemperature.mirek };
|
|
972
|
+
// } else if (receivedHUEObject.color !== undefined && AverageColorsXYBrightnessAndTemperature.x !== undefined) {
|
|
973
|
+
// receivedHUEObject.color = {
|
|
974
|
+
// xy: { x: AverageColorsXYBrightnessAndTemperature.x, y: AverageColorsXYBrightnessAndTemperature.y }
|
|
975
|
+
// };
|
|
976
|
+
// }
|
|
977
|
+
// // The dimming is not necessary, beacause the HUE API already sends a group_light event with the average brightness
|
|
978
|
+
// // if (receivedHUEObject.dimming !== undefined && AverageColorsXYBrightnessAndTemperature.brightness !== undefined) {
|
|
979
|
+
// // receivedHUEObject.dimming = { brightness: AverageColorsXYBrightnessAndTemperature.brightness };
|
|
980
|
+
// // }
|
|
981
|
+
// } catch (error) { /* empty */ }
|
|
982
|
+
|
|
983
|
+
canContinue = true;
|
|
984
|
+
//} catch (error) { /* empty */ }
|
|
985
|
+
} else if (node.isGrouped_light === false && receivedHUEObject.type === 'light' && receivedHUEObject.id === node.hueDevice) {
|
|
986
|
+
canContinue = true;
|
|
987
|
+
}
|
|
886
988
|
|
|
989
|
+
// --------------------------------------------------------------------------------------------------
|
|
990
|
+
|
|
991
|
+
if (canContinue === true) {
|
|
887
992
|
// Output the msg to the flow
|
|
888
|
-
node.send(
|
|
993
|
+
node.send(receivedHUEObject);
|
|
889
994
|
|
|
890
995
|
// // DEBUG testing enable/disable HTML UI Tabs
|
|
891
996
|
//delete _event.dimming;
|
|
@@ -893,53 +998,39 @@ module.exports = function (RED) {
|
|
|
893
998
|
//delete _event.color_temperature;
|
|
894
999
|
//delete _event.color_temperature_delta;
|
|
895
1000
|
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
if ((deviceByRef.color !== undefined || deviceByRef.dimming !== undefined || deviceByRef.color_temperature !== undefined) && deviceByRef.type === 'grouped_light') {
|
|
899
|
-
try {
|
|
900
|
-
const firstLightInGroup = node.serverHue.getFirstLightInGroup(deviceByRef.id);
|
|
901
|
-
if (firstLightInGroup !== null && firstLightInGroup !== undefined) {
|
|
902
|
-
if (deviceByRef.color === undefined) {
|
|
903
|
-
deviceByRef.color = firstLightInGroup.color;
|
|
904
|
-
}
|
|
905
|
-
if (deviceByRef.color_temperature === undefined) {
|
|
906
|
-
deviceByRef.color_temperature = firstLightInGroup.color_temperature;
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
} catch (error) { }
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
if (deviceByRef.on !== undefined) {
|
|
913
|
-
node.updateKNXLightState(deviceByRef.on.on);
|
|
1001
|
+
if (receivedHUEObject.on !== undefined) {
|
|
1002
|
+
node.updateKNXLightState(receivedHUEObject.on.on);
|
|
914
1003
|
// In case of switch off, set the dim to zero
|
|
915
|
-
if (
|
|
1004
|
+
if (receivedHUEObject.on.on === false && (config.updateKNXBrightnessStatusOnHUEOnOff === undefined || config.updateKNXBrightnessStatusOnHUEOnOff === "onhueoff")) {
|
|
916
1005
|
node.updateKNXBrightnessState(0);
|
|
917
|
-
if (
|
|
918
|
-
} else if (
|
|
919
|
-
// Turn on always update the dimming KNX Status value as well.
|
|
1006
|
+
if (receivedHUEObject.dimming !== undefined) delete receivedHUEObject.dimming; // Remove event.dimming, because has beem handled by this function and i don't want the function below to take care of it.
|
|
1007
|
+
} else if (receivedHUEObject.on.on === true && node.currentHUEDevice.on.on === false) {
|
|
920
1008
|
let brightVal = 50;
|
|
921
|
-
|
|
1009
|
+
// Turn on always update the dimming KNX Status value as well.
|
|
1010
|
+
if (node.currentHUEDevice.dimming !== undefined && node.currentHUEDevice.dimming.brightness !== undefined) {
|
|
1011
|
+
brightVal = node.currentHUEDevice.dimming.brightness;
|
|
1012
|
+
}
|
|
922
1013
|
node.updateKNXBrightnessState(brightVal);
|
|
923
1014
|
}
|
|
924
|
-
node.currentHUEDevice.on.on =
|
|
1015
|
+
node.currentHUEDevice.on.on = receivedHUEObject.on.on;
|
|
925
1016
|
}
|
|
926
1017
|
|
|
927
|
-
if (
|
|
928
|
-
node.updateKNXLightColorState(
|
|
929
|
-
node.currentHUEDevice.color =
|
|
1018
|
+
if (receivedHUEObject.color !== undefined && receivedHUEObject.color.xy !== undefined) { // fixed https://github.com/Supergiovane/node-red-contrib-knx-ultimate/issues/287
|
|
1019
|
+
node.updateKNXLightColorState(receivedHUEObject.color);
|
|
1020
|
+
node.currentHUEDevice.color = receivedHUEObject.color;
|
|
930
1021
|
}
|
|
931
1022
|
|
|
932
|
-
if (
|
|
1023
|
+
if (receivedHUEObject.dimming !== undefined && receivedHUEObject.dimming.brightness !== undefined) {
|
|
933
1024
|
// Once upon n a time, the light transmit the brightness value of 0.39.
|
|
934
1025
|
// To avoid wrongly turn light state on, exit
|
|
935
|
-
if (
|
|
936
|
-
if (node.currentHUEDevice.on !== undefined && node.currentHUEDevice.on.on === false &&
|
|
1026
|
+
if (receivedHUEObject.dimming.brightness < 1) receivedHUEObject.dimming.brightness = 0;
|
|
1027
|
+
if (node.currentHUEDevice.on !== undefined && node.currentHUEDevice.on.on === false && receivedHUEObject.dimming.brightness === 0) {
|
|
937
1028
|
// Do nothing, because the light is off and the dimming also is 0
|
|
938
1029
|
} else {
|
|
939
|
-
if (node.currentHUEDevice.on !== undefined && node.currentHUEDevice.on.on === false && (
|
|
940
|
-
node.updateKNXBrightnessState(
|
|
1030
|
+
if (node.currentHUEDevice.on !== undefined && node.currentHUEDevice.on.on === false && (receivedHUEObject.on === undefined || (receivedHUEObject.on !== undefined && receivedHUEObject.on.on === true))) node.updateKNXLightState(receivedHUEObject.dimming.brightness > 0);
|
|
1031
|
+
node.updateKNXBrightnessState(receivedHUEObject.dimming.brightness);
|
|
941
1032
|
// If the brightness reaches zero, the hue lamp "on" property must be set to zero as well
|
|
942
|
-
if (
|
|
1033
|
+
if (receivedHUEObject.dimming.brightness === 0 && node.currentHUEDevice.on !== undefined && node.currentHUEDevice.on.on === true) {
|
|
943
1034
|
node.serverHue.hueManager.writeHueQueueAdd(
|
|
944
1035
|
node.hueDevice,
|
|
945
1036
|
{ on: { on: false } },
|
|
@@ -947,14 +1038,15 @@ module.exports = function (RED) {
|
|
|
947
1038
|
);
|
|
948
1039
|
node.currentHUEDevice.on.on = false;
|
|
949
1040
|
}
|
|
950
|
-
node.currentHUEDevice.dimming.brightness =
|
|
1041
|
+
node.currentHUEDevice.dimming.brightness = receivedHUEObject.dimming.brightness;
|
|
951
1042
|
}
|
|
952
1043
|
}
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
node.
|
|
956
|
-
node.
|
|
957
|
-
|
|
1044
|
+
|
|
1045
|
+
if (receivedHUEObject.color_temperature !== undefined && receivedHUEObject.color_temperature.mirek !== undefined) {
|
|
1046
|
+
node.updateKNXLightKelvinPercentageState(receivedHUEObject.color_temperature.mirek);
|
|
1047
|
+
node.updateKNXLightKelvinState(receivedHUEObject.color_temperature.mirek);
|
|
1048
|
+
node.currentHUEDevice.color_temperature.mirek = receivedHUEObject.color_temperature.mirek;
|
|
1049
|
+
};
|
|
958
1050
|
}
|
|
959
1051
|
} catch (error) {
|
|
960
1052
|
node.status({
|
|
@@ -964,8 +1056,163 @@ module.exports = function (RED) {
|
|
|
964
1056
|
});
|
|
965
1057
|
RED.log.error(`knxUltimateHueLight: node.handleSendHUE = (_event): ${error.stack}`);
|
|
966
1058
|
}
|
|
1059
|
+
//})();
|
|
967
1060
|
};
|
|
968
1061
|
|
|
1062
|
+
|
|
1063
|
+
// node.handleSendHUE = async (_event) => {
|
|
1064
|
+
// if (_event === undefined) return;
|
|
1065
|
+
// if (_event.type !== 'grouped_light' && _event.type !== 'light') return;
|
|
1066
|
+
|
|
1067
|
+
// // if the node is a grouped_light, the only values required, thus present, by HUE apis are "on" and "dimming".
|
|
1068
|
+
// // For all others properties like for example color and tunable white, i must get the data from one of the child lights.
|
|
1069
|
+
// //(async () => {
|
|
1070
|
+
// // Check and set canContinue true or false ------------------------------------------------------------
|
|
1071
|
+
// try {
|
|
1072
|
+
// if (node.currentHUEDevice === undefined || node.serverHue === null || node.serverHue === undefined) {
|
|
1073
|
+
// node.setNodeStatusHue({
|
|
1074
|
+
// fill: "red",
|
|
1075
|
+
// shape: "ring",
|
|
1076
|
+
// text: "Rejected HUE light settings. I'm still not ready...",
|
|
1077
|
+
// payload: "",
|
|
1078
|
+
// });
|
|
1079
|
+
// return;
|
|
1080
|
+
// }
|
|
1081
|
+
|
|
1082
|
+
// const receivedHUEObject = cloneDeep(_event);
|
|
1083
|
+
// let groupChilds = undefined;
|
|
1084
|
+
// let AverageColorsXYBrightnessAndTemperature = undefined; // Average color and temp if the node is a grouped light.
|
|
1085
|
+
// let canContinue = false;
|
|
1086
|
+
// // If the current node is a grouped lights, i must check wether the receivedHUEObject (containing a light) belongs to the node lights collection.
|
|
1087
|
+
// if (node.isGrouped_light === true) {
|
|
1088
|
+
// try {
|
|
1089
|
+
// // Find all the lights in the collection, having the color_temperature capabilities, belonging to the group.
|
|
1090
|
+
// if (receivedHUEObject.type === "grouped_light") {
|
|
1091
|
+
// groupChilds = await node.serverHue.getAllLightsBelongingToTheGroup(node.hueDevice, false);
|
|
1092
|
+
// } else {
|
|
1093
|
+
// const devices = await node.serverHue.getAllLightsBelongingToTheGroup(node.hueDevice, false);
|
|
1094
|
+
// groupChilds = [];
|
|
1095
|
+
// for (let index = 0; index < devices.length; index++) {
|
|
1096
|
+
// const element = devices[index];
|
|
1097
|
+
// if (receivedHUEObject.id === element.id) {
|
|
1098
|
+
// //if (groupChilds === undefined) groupChilds = [];
|
|
1099
|
+
// // groupChilds.push(element);
|
|
1100
|
+
// let modifiedLight = cloneDeep(element);
|
|
1101
|
+
// if (receivedHUEObject.dimming !== undefined) modifiedLight.dimming = { brightness: receivedHUEObject.dimming.brightness };
|
|
1102
|
+
// if (receivedHUEObject.color !== undefined) modifiedLight.color = receivedHUEObject.color;
|
|
1103
|
+
// if (receivedHUEObject.color_temperature !== undefined) modifiedLight.color_temperature = receivedHUEObject.color_temperature;
|
|
1104
|
+
// groupChilds.push(modifiedLight);
|
|
1105
|
+
// } else {
|
|
1106
|
+
// // Simply ppend the light
|
|
1107
|
+
// groupChilds.push(element);
|
|
1108
|
+
// }
|
|
1109
|
+
// }
|
|
1110
|
+
// }
|
|
1111
|
+
// if (groupChilds !== undefined) AverageColorsXYBrightnessAndTemperature = await node.serverHue.getAverageColorsXYBrightnessAndTemperature(groupChilds);
|
|
1112
|
+
|
|
1113
|
+
// // Set the new values based on average calculated above
|
|
1114
|
+
// try {
|
|
1115
|
+
// // Use the arithmetic average of color xy, brightness and color temperature "averageColorXYandKelvinOfGroupedLights"
|
|
1116
|
+
// if (receivedHUEObject.color !== undefined && AverageColorsXYBrightnessAndTemperature.x !== undefined) {
|
|
1117
|
+
// receivedHUEObject.color = {
|
|
1118
|
+
// xy: { x: AverageColorsXYBrightnessAndTemperature.x, y: AverageColorsXYBrightnessAndTemperature.y }
|
|
1119
|
+
// };
|
|
1120
|
+
// }
|
|
1121
|
+
// if (receivedHUEObject.color_temperature !== undefined && AverageColorsXYBrightnessAndTemperature.mirek !== undefined) {
|
|
1122
|
+
// receivedHUEObject.color_temperature = { mirek: AverageColorsXYBrightnessAndTemperature.mirek };
|
|
1123
|
+
// }
|
|
1124
|
+
// if (receivedHUEObject.dimming !== undefined && AverageColorsXYBrightnessAndTemperature.brightness !== undefined) {
|
|
1125
|
+
// receivedHUEObject.dimming = { brightness: AverageColorsXYBrightnessAndTemperature.brightness };
|
|
1126
|
+
// }
|
|
1127
|
+
// } catch (error) { /* empty */ }
|
|
1128
|
+
|
|
1129
|
+
// canContinue = true;
|
|
1130
|
+
// } catch (error) { /* empty */ }
|
|
1131
|
+
// } else {
|
|
1132
|
+
// if (receivedHUEObject.type !== "light") return;
|
|
1133
|
+
// if (receivedHUEObject.id === node.hueDevice) {
|
|
1134
|
+
// canContinue = true;
|
|
1135
|
+
// }
|
|
1136
|
+
// }
|
|
1137
|
+
// // --------------------------------------------------------------------------------------------------
|
|
1138
|
+
|
|
1139
|
+
// if (canContinue === true) {
|
|
1140
|
+
|
|
1141
|
+
// // Output the msg to the flow
|
|
1142
|
+
// node.send(receivedHUEObject);
|
|
1143
|
+
|
|
1144
|
+
// // // DEBUG testing enable/disable HTML UI Tabs
|
|
1145
|
+
// //delete _event.dimming;
|
|
1146
|
+
// //delete _event.color;
|
|
1147
|
+
// //delete _event.color_temperature;
|
|
1148
|
+
// //delete _event.color_temperature_delta;
|
|
1149
|
+
|
|
1150
|
+
|
|
1151
|
+
// if (receivedHUEObject.on !== undefined) {
|
|
1152
|
+
// node.updateKNXLightState(receivedHUEObject.on.on);
|
|
1153
|
+
// // In case of switch off, set the dim to zero
|
|
1154
|
+
// if (receivedHUEObject.on.on === false && (config.updateKNXBrightnessStatusOnHUEOnOff === undefined || config.updateKNXBrightnessStatusOnHUEOnOff === "onhueoff")) {
|
|
1155
|
+
// node.updateKNXBrightnessState(0);
|
|
1156
|
+
// if (receivedHUEObject.dimming !== undefined) delete receivedHUEObject.dimming; // Remove event.dimming, because has beem handled by this function and i don't want the function below to take care of it.
|
|
1157
|
+
// } else if (receivedHUEObject.on.on === true && node.currentHUEDevice.on.on === false) {
|
|
1158
|
+
// let brightVal = 50;
|
|
1159
|
+
// if (node.currentHUEDevice.type === "light") {
|
|
1160
|
+
// // Turn on always update the dimming KNX Status value as well.
|
|
1161
|
+
// if (node.currentHUEDevice.dimming !== undefined && node.currentHUEDevice.dimming.brightness !== undefined) {
|
|
1162
|
+
// brightVal = node.currentHUEDevice.dimming.brightness;
|
|
1163
|
+
// }
|
|
1164
|
+
// } else if (node.currentHUEDevice.type === "grouped_light") {
|
|
1165
|
+
// brightVal = AverageColorsXYBrightnessAndTemperature;
|
|
1166
|
+
// }
|
|
1167
|
+
// node.updateKNXBrightnessState(brightVal);
|
|
1168
|
+
// }
|
|
1169
|
+
// node.currentHUEDevice.on.on = receivedHUEObject.on.on;
|
|
1170
|
+
// }
|
|
1171
|
+
|
|
1172
|
+
// if (receivedHUEObject.color !== undefined) { // fixed https://github.com/Supergiovane/node-red-contrib-knx-ultimate/issues/287
|
|
1173
|
+
// node.updateKNXLightColorState(receivedHUEObject.color);
|
|
1174
|
+
// node.currentHUEDevice.color = receivedHUEObject.color;
|
|
1175
|
+
// }
|
|
1176
|
+
|
|
1177
|
+
// if (receivedHUEObject.dimming !== undefined && receivedHUEObject.dimming.brightness !== undefined) {
|
|
1178
|
+
// // Once upon n a time, the light transmit the brightness value of 0.39.
|
|
1179
|
+
// // To avoid wrongly turn light state on, exit
|
|
1180
|
+
// if (receivedHUEObject.dimming.brightness < 1) receivedHUEObject.dimming.brightness = 0;
|
|
1181
|
+
// if (node.currentHUEDevice.on !== undefined && node.currentHUEDevice.on.on === false && receivedHUEObject.dimming.brightness === 0) {
|
|
1182
|
+
// // Do nothing, because the light is off and the dimming also is 0
|
|
1183
|
+
// } else {
|
|
1184
|
+
// if (node.currentHUEDevice.on !== undefined && node.currentHUEDevice.on.on === false && (receivedHUEObject.on === undefined || (receivedHUEObject.on !== undefined && receivedHUEObject.on.on === true))) node.updateKNXLightState(receivedHUEObject.dimming.brightness > 0);
|
|
1185
|
+
// node.updateKNXBrightnessState(receivedHUEObject.dimming.brightness);
|
|
1186
|
+
// // If the brightness reaches zero, the hue lamp "on" property must be set to zero as well
|
|
1187
|
+
// if (receivedHUEObject.dimming.brightness === 0 && node.currentHUEDevice.on !== undefined && node.currentHUEDevice.on.on === true) {
|
|
1188
|
+
// node.serverHue.hueManager.writeHueQueueAdd(
|
|
1189
|
+
// node.hueDevice,
|
|
1190
|
+
// { on: { on: false } },
|
|
1191
|
+
// node.isGrouped_light === false ? "setLight" : "setGroupedLight",
|
|
1192
|
+
// );
|
|
1193
|
+
// node.currentHUEDevice.on.on = false;
|
|
1194
|
+
// }
|
|
1195
|
+
// node.currentHUEDevice.dimming.brightness = receivedHUEObject.dimming.brightness;
|
|
1196
|
+
// }
|
|
1197
|
+
// }
|
|
1198
|
+
|
|
1199
|
+
// if (receivedHUEObject.color_temperature !== undefined) {
|
|
1200
|
+
// node.updateKNXLightKelvinPercentageState(receivedHUEObject.color_temperature.mirek);
|
|
1201
|
+
// node.updateKNXLightKelvinState(receivedHUEObject.color_temperature.mirek);
|
|
1202
|
+
// node.currentHUEDevice.color_temperature.mirek = receivedHUEObject.color_temperature.mirek;
|
|
1203
|
+
// };
|
|
1204
|
+
// }
|
|
1205
|
+
// } catch (error) {
|
|
1206
|
+
// node.status({
|
|
1207
|
+
// fill: "red",
|
|
1208
|
+
// shape: "dot",
|
|
1209
|
+
// text: `HUE->KNX error ${node.id} ${error.message}. Seee Log`,
|
|
1210
|
+
// });
|
|
1211
|
+
// RED.log.error(`knxUltimateHueLight: node.handleSendHUE = (_event): ${error.stack}`);
|
|
1212
|
+
// }
|
|
1213
|
+
// //})();
|
|
1214
|
+
// };
|
|
1215
|
+
|
|
969
1216
|
// Leave the name after "function", to avoid <anonymous function> in the stack trace, in caso of errors.
|
|
970
1217
|
node.updateKNXBrightnessState = function updateKNXBrightnessState(_value, _outputtype = "write") {
|
|
971
1218
|
if (config.GALightBrightnessState !== undefined && config.GALightBrightnessState !== "") {
|
|
@@ -1128,11 +1375,11 @@ module.exports = function (RED) {
|
|
|
1128
1375
|
// };
|
|
1129
1376
|
|
|
1130
1377
|
/**
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1378
|
+
* Update the KNC colors and HSV states group addresses
|
|
1379
|
+
* @param {object} _value {xy:{x,y}} in 0-1 scale
|
|
1380
|
+
* @param {string} _outputtype "write" is the default KNX command
|
|
1381
|
+
* @returns {}
|
|
1382
|
+
*/
|
|
1136
1383
|
node.updateKNXLightColorState = function updateKNXLightColorState(_value, _outputtype = "write") {
|
|
1137
1384
|
if (config.GALightColorState !== undefined && config.GALightColorState !== "") {
|
|
1138
1385
|
if (_value.xy === undefined || _value.xy.x === undefined) return;
|
|
@@ -1229,8 +1476,8 @@ module.exports = function (RED) {
|
|
|
1229
1476
|
knxMsgPayload.topic = config.GALightKelvinState;
|
|
1230
1477
|
knxMsgPayload.dpt = config.dptLightKelvinState;
|
|
1231
1478
|
if (config.dptLightKelvinState === "7.600") {
|
|
1232
|
-
|
|
1233
|
-
knxMsgPayload.payload = hueColorConverter.ColorConverter.scale(kelvinValue, [2000, 6535], [0, 65535]);
|
|
1479
|
+
knxMsgPayload.payload = hueColorConverter.ColorConverter.mirekToKelvin(_value);
|
|
1480
|
+
//knxMsgPayload.payload = hueColorConverter.ColorConverter.scale(kelvinValue, [2000, 6535], [0, 65535]);
|
|
1234
1481
|
} else if (config.dptLightKelvinState === "9.002") {
|
|
1235
1482
|
knxMsgPayload.payload = hueColorConverter.ColorConverter.mirekToKelvin(_value);
|
|
1236
1483
|
}
|
|
@@ -1250,7 +1497,7 @@ module.exports = function (RED) {
|
|
|
1250
1497
|
fill: "blue",
|
|
1251
1498
|
shape: "ring",
|
|
1252
1499
|
text: "HUE->KNX Kelvin",
|
|
1253
|
-
payload:
|
|
1500
|
+
payload: knxMsgPayload.payload,
|
|
1254
1501
|
});
|
|
1255
1502
|
}
|
|
1256
1503
|
}
|
package/nodes/utils/utils.js
CHANGED
|
@@ -53,5 +53,6 @@ module.exports.fetchFromObject = function fetchFromObject(
|
|
|
53
53
|
if (typeof _msg[_payloadPropName] === "object") return undefined;
|
|
54
54
|
return _msg[_payloadPropName];
|
|
55
55
|
};
|
|
56
|
+
|
|
56
57
|
const DEFAULTTRANSLATIONINPUT =
|
|
57
58
|
"on:true\noff:false\nactive:true\ninactive:false\nopen:true\nclosed:false\nclose:false\n1:true\n0:false\ntrue:true\nfalse:false\nhome:true\nnot_home:false\nnormal:false\nviolated:true";
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"engines": {
|
|
4
4
|
"node": ">=16.0.0"
|
|
5
5
|
},
|
|
6
|
-
"version": "2.4.
|
|
6
|
+
"version": "2.4.26",
|
|
7
7
|
"description": "Control your KNX intallation via Node-Red! A bunch of KNX nodes, with integrated Philips HUE control and ETS group address importer. Easy to use and highly configurable.",
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"binary-parser": "2.2.1",
|