node-red-contrib-knx-ultimate 2.2.20 → 2.2.22

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.
@@ -35,6 +35,32 @@ module.exports = function (RED) {
35
35
  node.isGrouped_light = config.hueDevice.split("#")[1] === "grouped_light";
36
36
  node.hueDevice = config.hueDevice.split("#")[0];
37
37
  node.initializingAtStart = (config.readStatusAtStartup === undefined || config.readStatusAtStartup === "yes");
38
+ config.specifySwitchOnBrightness === undefined ? "yes" : config.specifySwitchOnBrightness;
39
+ config.colorAtSwitchOnDayTime = (config.colorAtSwitchOnDayTime === '' || config.colorAtSwitchOnDayTime === undefined) ? '{ "kelvin":3000, "brightness":100 }' : config.colorAtSwitchOnDayTime;
40
+ config.colorAtSwitchOnNightTime = (config.colorAtSwitchOnNightTime === '' || config.colorAtSwitchOnNightTime === undefined) ? '{ "kelvin":2700, "brightness":20 }' : config.colorAtSwitchOnNightTime;
41
+
42
+ // Transform HEX in RGB and stringified json in json oblects.
43
+ if (config.colorAtSwitchOnDayTime.indexOf("#") !== -1) {
44
+ // Transform to rgb.
45
+ try {
46
+ config.colorAtSwitchOnDayTime = hueColorConverter.ColorConverter.hexRgb(config.colorAtSwitchOnDayTime.replace("#", ""));
47
+ } catch (error) {
48
+ config.colorAtSwitchOnDayTime = { kelvin: 3000, brightness: 100 };
49
+ }
50
+ } else {
51
+ config.colorAtSwitchOnDayTime = JSON.parse(config.colorAtSwitchOnDayTime);
52
+ }
53
+ // Same thing, but with night color
54
+ if (config.colorAtSwitchOnNightTime.indexOf("#") !== -1) {
55
+ // Transform to rgb.
56
+ try {
57
+ config.colorAtSwitchOnNightTime = hueColorConverter.ColorConverter.hexRgb(config.colorAtSwitchOnNightTime.replace("#", ""));
58
+ } catch (error) {
59
+ config.colorAtSwitchOnNightTime = { kelvin: 2700, brightness: 20 };
60
+ }
61
+ } else {
62
+ config.colorAtSwitchOnNightTime = JSON.parse(config.colorAtSwitchOnNightTime);
63
+ }
38
64
 
39
65
  // Used to call the status update from the config node.
40
66
  node.setNodeStatus = ({
@@ -71,42 +97,42 @@ module.exports = function (RED) {
71
97
  case config.GALightSwitch:
72
98
  msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightSwitch));
73
99
  if (msg.payload === true) {
74
- // The user selected specific color/brightness at switch on.
100
+ // Check wether the user selected specific color/brightness at switch on.
75
101
  let jColorChoosen = null;
76
- if (node.DayTime === true && (config.specifySwitchOnBrightness === undefined || config.specifySwitchOnBrightness === "yes")) {
77
- try {
78
- jColorChoosen = JSON.parse(config.colorAtSwitchOnDayTime);
79
- if (jColorChoosen.green === undefined) jColorChoosen.green = jColorChoosen.hasOwnProperty("geen") ? jColorChoosen.geen : jColorChoosen.green;
80
- } catch (error) {
81
- jColorChoosen = { red: 255, green: 255, blue: 255 };
82
- }
83
- } else if (node.DayTime === false && config.enableDayNightLighting === "yes") {
84
- try {
85
- jColorChoosen = JSON.parse(config.colorAtSwitchOnNightTime);
86
- } catch (error) {
87
- jColorChoosen = { red: 10, green: 10, blue: 10 };
88
- }
102
+ if (node.DayTime === true && (config.specifySwitchOnBrightness === "yes" || config.specifySwitchOnBrightness === "temperature")) {
103
+ jColorChoosen = config.colorAtSwitchOnDayTime;
104
+ } else if (node.DayTime === false && (config.enableDayNightLighting === "yes" || config.enableDayNightLighting === "temperature")) {
105
+ jColorChoosen = config.colorAtSwitchOnNightTime;
89
106
  }
90
- if (jColorChoosen !== null) {
107
+
108
+ // Now we have a jColorChoosen. Proceed illuminating the light
109
+ if (jColorChoosen !== null && jColorChoosen.kelvin === undefined) {
110
+ // RGB
91
111
  let gamut = null;
92
- if (
93
- node.currentHUEDevice !== undefined
94
- && node.currentHUEDevice.hasOwnProperty("color")
95
- && node.currentHUEDevice.color.hasOwnProperty("gamut_type")
96
- ) {
112
+ if (node.currentHUEDevice !== undefined && node.currentHUEDevice.hasOwnProperty("color") && node.currentHUEDevice.color.hasOwnProperty("gamut_type")) {
97
113
  gamut = node.currentHUEDevice.color.gamut_type;
98
114
  }
99
115
  const dretXY = hueColorConverter.ColorConverter.rgbToXy(jColorChoosen.red, jColorChoosen.green, jColorChoosen.blue, gamut);
100
- const dbright = hueColorConverter.ColorConverter.getBrightnessFromRGB(jColorChoosen.red, jColorChoosen.green, jColorChoosen.blue);
101
- node.currentHUEDevice.dimming.brightness = dbright;
116
+ const dbright = hueColorConverter.ColorConverter.getBrightnessFromRGBOrHex(jColorChoosen.red, jColorChoosen.green, jColorChoosen.blue);
117
+ node.currentHUEDevice.dimming.brightness = Math.round(dbright, 0);
102
118
  node.updateKNXBrightnessState(node.currentHUEDevice.dimming.brightness);
103
119
  state = dbright > 0 ? { on: { on: true }, dimming: { brightness: dbright }, color: { xy: dretXY } } : { on: { on: false } };
104
- } else {
120
+ } if (jColorChoosen !== null && jColorChoosen.kelvin !== undefined) {
121
+ // Kelvin
122
+ const dbright = jColorChoosen.brightness;
123
+ const mirek = hueColorConverter.ColorConverter.kelvinToMirek(jColorChoosen.kelvin);
124
+ node.currentHUEDevice.color_temperature.mirek = mirek;
125
+ node.currentHUEDevice.dimming.brightness = dbright;
126
+ node.updateKNXBrightnessState(node.currentHUEDevice.dimming.brightness);
127
+ // Kelvin temp
128
+ state = dbright > 0 ? { on: { on: true }, dimming: { brightness: dbright }, color_temperature: { mirek: mirek } } : { on: { on: false } };
129
+ } else if (jColorChoosen === null || jColorChoosen === undefined) {
105
130
  state = { on: { on: true } };
106
131
  }
107
132
  } else {
108
133
  state = { on: { on: false } };
109
134
  }
135
+
110
136
  node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
111
137
  node.setNodeStatusHue({
112
138
  fill: "green",
@@ -125,30 +151,37 @@ module.exports = function (RED) {
125
151
  });
126
152
  break;
127
153
  case config.GALightKelvin:
154
+ let retMirek;
128
155
  msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightKelvin));
129
- let retMirek = hueColorConverter.ColorConverter.scale(msg.payload, [0, 65535], [153, 500]);
156
+ if (config.dptLightKelvin === "7.600") {
157
+ if (msg.payload > 65535) msg.payload = 65535;
158
+ if (msg.payload < 0) msg.payload = 0;
159
+ retMirek = hueColorConverter.ColorConverter.scale(msg.payload, [0, 65535], [500, 153]);
160
+ } else if (config.dptLightKelvin === "9.002") {
161
+ // Relative temperature in Kelvin. Use HUE scale.
162
+ if (msg.payload > 6535) msg.payload = 6535;
163
+ if (msg.payload < 2000) msg.payload = 2000;
164
+ retMirek = hueColorConverter.ColorConverter.scale(msg.payload, [2000, 6535], [500, 153]);
165
+ }
130
166
  state = { color_temperature: { mirek: retMirek } };
131
167
  node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
132
168
  node.setNodeStatusHue({
133
169
  fill: "green",
134
170
  shape: "dot",
135
171
  text: "KNX->HUE",
136
- payload: state,
172
+ payload: msg.payload,
137
173
  });
138
174
  break;
139
175
  case config.GADaylightSensor:
140
- if (config.enableDayNightLighting === "yes") {
141
- node.DayTime = Boolean(dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptDaylightSensor)));
142
- if (config.invertDayNight !== undefined && config.invertDayNight === true) node.DayTime = !node.DayTime;
143
- node.setNodeStatusHue({
144
- fill: "green",
145
- shape: "dot",
146
- text: "KNX->HUE Daytime",
147
- payload: node.DayTime,
148
- });
149
- } else {
150
- node.DayTime = true;
151
- }
176
+ node.DayTime = Boolean(dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptDaylightSensor)));
177
+ if (config.invertDayNight !== undefined && config.invertDayNight === true) node.DayTime = !node.DayTime;
178
+ node.setNodeStatusHue({
179
+ fill: "green",
180
+ shape: "dot",
181
+ text: "KNX->HUE Daytime",
182
+ payload: node.DayTime,
183
+ });
184
+
152
185
  break;
153
186
  case config.GALightHSV:
154
187
  if (config.dptLightHSV === "3.007") {
@@ -209,7 +242,7 @@ module.exports = function (RED) {
209
242
  gamut = node.currentHUEDevice.color.gamut_type;
210
243
  }
211
244
  const retXY = hueColorConverter.ColorConverter.rgbToXy(msg.payload.red, msg.payload.green, msg.payload.blue, gamut);
212
- const bright = hueColorConverter.ColorConverter.getBrightnessFromRGB(msg.payload.red, msg.payload.green, msg.payload.blue);
245
+ const bright = hueColorConverter.ColorConverter.getBrightnessFromRGBOrHex(msg.payload.red, msg.payload.green, msg.payload.blue);
213
246
  // state = bright > 0 ? { on: { on: true }, dimming: { brightness: bright }, color: { xy: retXY } } : { on: { on: false } }
214
247
  state = { dimming: { brightness: bright }, color: { xy: retXY } };
215
248
  if (node.currentHUEDevice === undefined) {
@@ -276,7 +309,7 @@ module.exports = function (RED) {
276
309
  gamut = node.currentHUEDevice.color.gamut_type;
277
310
  }
278
311
  const retXY = hueColorConverter.ColorConverter.rgbToXy(red, green, blue, gamut);
279
- const bright = hueColorConverter.ColorConverter.getBrightnessFromRGB(red, green, blue);
312
+ const bright = hueColorConverter.ColorConverter.getBrightnessFromRGBOrHex(red, green, blue);
280
313
  state = bright > 0 ? { on: { on: true }, dimming: { brightness: bright }, color: { xy: retXY } } : { on: { on: false } };
281
314
  node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
282
315
  } catch (error) { }
@@ -455,10 +488,10 @@ module.exports = function (RED) {
455
488
  if (node.currentHUEDevice.hasOwnProperty("on") && node.currentHUEDevice.on.on === false && _event.dimming.brightness === 0) {
456
489
  // Do nothing, because the light is off and the dimming also is 0
457
490
  } else {
458
- if (node.currentHUEDevice.on.on === false && (!_event.hasOwnProperty("on") || (_event.hasOwnProperty("on") && _event.on.on === true))) node.updateKNXLightState(_event.dimming.brightness > 0);
491
+ if (node.currentHUEDevice.hasOwnProperty("on") && node.currentHUEDevice.on.on === false && (!_event.hasOwnProperty("on") || (_event.hasOwnProperty("on") && _event.on.on === true))) node.updateKNXLightState(_event.dimming.brightness > 0);
459
492
  node.updateKNXBrightnessState(_event.dimming.brightness);
460
493
  // If the brightness reaches zero, the hue lamp "on" property must be set to zero as well
461
- if (_event.dimming.brightness === 0) {
494
+ if (_event.dimming.brightness === 0 && node.currentHUEDevice.hasOwnProperty("on") && node.currentHUEDevice.on.on === true) {
462
495
  node.serverHue.hueManager.writeHueQueueAdd(
463
496
  node.hueDevice,
464
497
  { on: { on: false } },
@@ -481,6 +514,7 @@ module.exports = function (RED) {
481
514
  shape: "dot",
482
515
  text: `HUE->KNX error ${node.id} ${error.message}`,
483
516
  });
517
+ RED.log.error(`knxUltimateHueLight: node.handleSendHUE = (_event): ${error.message}`);
484
518
  }
485
519
  };
486
520
 
@@ -604,17 +638,20 @@ module.exports = function (RED) {
604
638
  knxMsgPayload.topic = config.GALightKelvinState;
605
639
  knxMsgPayload.dpt = config.dptLightKelvinState;
606
640
  if (config.dptLightKelvinState === "7.600") {
607
- knxMsgPayload.payload = hueColorConverter.ColorConverter.scale(_value, [153, 500], [0, 65535]);
608
- // Send to KNX bus
609
- if (knxMsgPayload.topic !== "" && knxMsgPayload.topic !== undefined) {
610
- node.server.writeQueueAdd({
611
- grpaddr: knxMsgPayload.topic,
612
- payload: knxMsgPayload.payload,
613
- dpt: knxMsgPayload.dpt,
614
- outputtype: "write",
615
- nodecallerid: node.id,
616
- });
617
- }
641
+ knxMsgPayload.payload = hueColorConverter.ColorConverter.scale(_value, [153, 500], [65535, 0]);
642
+ } else if (config.dptLightKelvinState === "9.002") {
643
+ knxMsgPayload.payload = hueColorConverter.ColorConverter.scale(_value, [153, 500], [6535, 2000]);
644
+ }
645
+ // Send to KNX bus
646
+ if (knxMsgPayload.topic !== "" && knxMsgPayload.topic !== undefined) {
647
+ node.server.writeQueueAdd({
648
+ grpaddr: knxMsgPayload.topic,
649
+ payload: knxMsgPayload.payload,
650
+ dpt: knxMsgPayload.dpt,
651
+ outputtype: "write",
652
+ nodecallerid: node.id,
653
+ });
654
+
618
655
  node.setNodeStatusHue({
619
656
  fill: "blue",
620
657
  shape: "ring",
@@ -1,3 +1,5 @@
1
+ <script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/11f26b4500.js"></script>
2
+
1
3
  <script type="text/javascript">
2
4
  RED.nodes.registerType('knxUltimateHueLightSensor', {
3
5
  category: "KNX Ultimate",
@@ -228,8 +230,7 @@
228
230
 
229
231
 
230
232
  </script>
231
- <script type="application/javascript;charset=utf-8" src="https://kit.fontawesome.com/11f26b4500.js"
232
- crossorigin="anonymous"></script>
233
+
233
234
 
234
235
  <script type="text/markdown" data-help-name="knxUltimateHueLightSensor">
235
236
  This node lets you get the events from your HUE motion device.
@@ -1,3 +1,5 @@
1
+ <script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/11f26b4500.js"></script>
2
+
1
3
  <script type="text/javascript">
2
4
  RED.nodes.registerType('knxUltimateHueMotion', {
3
5
  category: "KNX Ultimate",
@@ -217,8 +219,6 @@
217
219
 
218
220
 
219
221
  </script>
220
- <script type="application/javascript;charset=utf-8" src="https://kit.fontawesome.com/11f26b4500.js"
221
- crossorigin="anonymous"></script>
222
222
 
223
223
  <script type="text/markdown" data-help-name="knxUltimateHueMotion">
224
224
  This node lets you get the events from your HUE motion device.
@@ -1,3 +1,5 @@
1
+ <script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/11f26b4500.js"></script>
2
+
1
3
  <script type="text/javascript">
2
4
  RED.nodes.registerType('knxUltimateHueScene', {
3
5
  category: "KNX Ultimate",
@@ -283,8 +285,6 @@
283
285
 
284
286
 
285
287
  </script>
286
- <script type="application/javascript;charset=utf-8" src="https://kit.fontawesome.com/11f26b4500.js"
287
- crossorigin="anonymous"></script>
288
288
 
289
289
  <script type="text/markdown" data-help-name="knxUltimateHueScene">
290
290
  This node lets you recall a HUE scene, via KNX.
@@ -1,3 +1,5 @@
1
+ <script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/11f26b4500.js"></script>
2
+
1
3
  <script type="text/javascript">
2
4
  RED.nodes.registerType('knxUltimateHueTapDial', {
3
5
  category: "KNX Ultimate",
@@ -219,8 +221,6 @@
219
221
 
220
222
 
221
223
  </script>
222
- <script type="application/javascript;charset=utf-8" src="https://kit.fontawesome.com/11f26b4500.js"
223
- crossorigin="anonymous"></script>
224
224
 
225
225
  <script type="text/markdown" data-help-name="knxUltimateHueTapDial">
226
226
  This node lets you get the events from your HUE rotary device, for example the Tap Dial.
@@ -1,3 +1,5 @@
1
+ <script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/11f26b4500.js"></script>
2
+
1
3
  <script type="text/javascript">
2
4
  RED.nodes.registerType('knxUltimateHueTemperatureSensor', {
3
5
  category: "KNX Ultimate",
@@ -227,8 +229,6 @@
227
229
 
228
230
 
229
231
  </script>
230
- <script type="application/javascript;charset=utf-8" src="https://kit.fontawesome.com/11f26b4500.js"
231
- crossorigin="anonymous"></script>
232
232
 
233
233
  <script type="text/markdown" data-help-name="knxUltimateHueTemperatureSensor" This node lets you get the events from
234
234
  your HUE temperature device. Here you can get the HUE temperature events, that represents a celsius value, evetytime
@@ -1,7 +1,165 @@
1
- const convert = require('color-convert');
2
-
1
+ // Part of this code, thanks to https://github.com/Shnoo/js-CIE-1931-rgb-color-converter
3
2
  class ColorConverter {
3
+ // static getBrightnessFromRGBOrHex(red, green, blue) {
4
+ // const hsv = convert.rgb.hsv(red, green, blue);
5
+ // const brightness = hsv[2];
6
+ // return brightness;
7
+ // }
8
+
9
+
10
+ static getBrightnessFromRGBOrHex(Rint, Gint, Bint) { // takes sRGB channels as 8 bit integers
11
+
12
+ var Rlin = (Rint / 255.0) ** 2.218; // Convert int to decimal 0-1 and linearize
13
+ var Glin = (Gint / 255.0) ** 2.218; // ** is the exponentiation operator, older JS needs Math.pow() instead
14
+ var Blin = (Bint / 255.0) ** 2.218; // 2.218 Gamma for sRGB linearization. 2.218 sets unity with the piecewise sRGB at #777 .... 2.2 or 2.223 could be used instead
15
+
16
+ var Ylum = Rlin * 0.2126 + Glin * 0.7156 + Blin * 0.0722; // convert to Luminance Y
17
+
18
+ return Math.pow(Ylum, 0.43) * 100; // Convert to lightness (0 to 100)
19
+ }
20
+
21
+ static convert_1_255_ToPercentage(number) {
22
+ const percentage = (number / 255) * 100;
23
+ return percentage;
24
+ }
25
+
26
+ static kelvinToMirek(_kelvin) {
27
+ return Math.round(1000000 / _kelvin, 0);
28
+ }
29
+
30
+ static mirekToKelvin(_mirek) {
31
+ return Math.round(1000000 / _mirek, 0);
32
+ }
33
+
34
+ // Linear interpolation of input y given starting and ending ranges
35
+ static scale(y, range1 = [0, 100], range2 = [0, 255]) {
36
+ const [xMin, xMax] = range2;
37
+ const [yMin, yMax] = range1;
38
+ const percent = (y - yMin) / (yMax - yMin);
39
+ const ans = percent * (xMax - xMin) + xMin;
40
+ return Math.round(ans);
41
+ }
42
+
43
+ // Thanks to: https://github.com/sindresorhus/rgb-hex
44
+ static rgbHex(red, green, blue, alpha) {
45
+ const toHex = (red, green, blue, alpha) => ((blue | green << 8 | red << 16) | 1 << 24).toString(16).slice(1) + alpha;
46
+ const parseCssRgbString = (input) => {
47
+ const parts = input.replace(/rgba?\(([^)]+)\)/, '$1').split(/[,\s/]+/).filter(Boolean);
48
+ if (parts.length < 3) {
49
+ return;
50
+ }
51
+
52
+ const parseValue = (value, max) => {
53
+ value = value.trim();
54
+
55
+ if (value.endsWith('%')) {
56
+ return Math.min(Number.parseFloat(value) * max / 100, max);
57
+ }
58
+
59
+ return Math.min(Number.parseFloat(value), max);
60
+ };
61
+
62
+ const red = parseValue(parts[0], 255);
63
+ const green = parseValue(parts[1], 255);
64
+ const blue = parseValue(parts[2], 255);
65
+ let alpha;
66
+
67
+ if (parts.length === 4) {
68
+ alpha = parseValue(parts[3], 1);
69
+ }
70
+
71
+ return [red, green, blue, alpha];
72
+ };
73
+
74
+ let isPercent = (red + (alpha || '')).toString().includes('%');
75
+
76
+ if (typeof red === 'string' && !green) { // Single string parameter.
77
+ const parsed = parseCssRgbString(red);
78
+ if (!parsed) {
79
+ throw new TypeError('Invalid or unsupported color format.');
80
+ }
81
+
82
+ isPercent = false;
83
+ [red, green, blue, alpha] = parsed;
84
+ } else if (alpha !== undefined) {
85
+ alpha = Number.parseFloat(alpha);
86
+ }
4
87
 
88
+ if (typeof red !== 'number'
89
+ || typeof green !== 'number'
90
+ || typeof blue !== 'number'
91
+ || red > 255
92
+ || green > 255
93
+ || blue > 255
94
+ ) {
95
+ throw new TypeError('Expected three numbers below 256');
96
+ }
97
+
98
+ if (typeof alpha === 'number') {
99
+ if (!isPercent && alpha >= 0 && alpha <= 1) {
100
+ alpha = Math.round(255 * alpha);
101
+ } else if (isPercent && alpha >= 0 && alpha <= 100) {
102
+ alpha = Math.round(255 * alpha / 100);
103
+ } else {
104
+ throw new TypeError(`Expected alpha value (${alpha}) as a fraction or percentage`);
105
+ }
106
+
107
+ alpha = (alpha | 1 << 8).toString(16).slice(1); // eslint-disable-line no-mixed-operators
108
+ } else {
109
+ alpha = '';
110
+ }
111
+
112
+ return toHex(red, green, blue, alpha);
113
+ }
114
+
115
+ // Thanks to: https://github.com/sindresorhus/hex-rgb
116
+ static hexRgb(hex, options = {}) {
117
+ const hexCharacters = 'a-f\\d';
118
+ const match3or4Hex = `#?[${hexCharacters}]{3}[${hexCharacters}]?`;
119
+ const match6or8Hex = `#?[${hexCharacters}]{6}([${hexCharacters}]{2})?`;
120
+ const nonHexChars = new RegExp(`[^#${hexCharacters}]`, 'gi');
121
+ const validHexSize = new RegExp(`^${match3or4Hex}$|^${match6or8Hex}$`, 'i');
122
+
123
+ if (typeof hex !== 'string' || nonHexChars.test(hex) || !validHexSize.test(hex)) {
124
+ throw new TypeError('Expected a valid hex string');
125
+ }
126
+
127
+ hex = hex.replace(/^#/, '');
128
+ let alphaFromHex = 1;
129
+
130
+ if (hex.length === 8) {
131
+ alphaFromHex = Number.parseInt(hex.slice(6, 8), 16) / 255;
132
+ hex = hex.slice(0, 6);
133
+ }
134
+
135
+ if (hex.length === 4) {
136
+ alphaFromHex = Number.parseInt(hex.slice(3, 4).repeat(2), 16) / 255;
137
+ hex = hex.slice(0, 3);
138
+ }
139
+
140
+ if (hex.length === 3) {
141
+ hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
142
+ }
143
+
144
+ const number = Number.parseInt(hex, 16);
145
+ const red = number >> 16;
146
+ const green = (number >> 8) & 255;
147
+ const blue = number & 255;
148
+ const alpha = typeof options.alpha === 'number' ? options.alpha : alphaFromHex;
149
+
150
+ if (options.format === 'array') {
151
+ return [red, green, blue, alpha];
152
+ }
153
+
154
+ if (options.format === 'css') {
155
+ const alphaString = alpha === 1 ? '' : ` / ${Number((alpha * 100).toFixed(2))}%`;
156
+ return `rgb(${red} ${green} ${blue}${alphaString})`;
157
+ }
158
+
159
+ return {
160
+ red, green, blue, alpha,
161
+ };
162
+ }
5
163
 
6
164
  static getGamutRanges() {
7
165
  const gamutA = {
@@ -33,31 +191,52 @@ class ColorConverter {
33
191
  };
34
192
  }
35
193
 
36
- static getLightColorGamutRange(gamutTypeABC = null) {
194
+ static getLightColorGamutRange(modelId = null) {
37
195
  const ranges = ColorConverter.getGamutRanges();
38
196
  const { gamutA } = ranges;
39
197
  const { gamutB } = ranges;
40
198
  const { gamutC } = ranges;
41
199
 
42
200
  const philipsModels = {
43
- A: gamutA,
44
- B: gamutB,
45
- C: gamutC,
201
+ LST001: gamutA,
202
+ LLC010: gamutA,
203
+ LLC011: gamutA,
204
+ LLC012: gamutA,
205
+ LLC006: gamutA,
206
+ LLC005: gamutA,
207
+ LLC007: gamutA,
208
+ LLC014: gamutA,
209
+ LLC013: gamutA,
210
+
211
+ LCT001: gamutB,
212
+ LCT007: gamutB,
213
+ LCT002: gamutB,
214
+ LCT003: gamutB,
215
+ LLM001: gamutB,
216
+
217
+ LCT010: gamutC,
218
+ LCT014: gamutC,
219
+ LCT015: gamutC,
220
+ LCT016: gamutC,
221
+ LCT011: gamutC,
222
+ LLC020: gamutC,
223
+ LST002: gamutC,
224
+ LCT012: gamutC,
46
225
  };
47
226
 
48
- if (philipsModels[gamutTypeABC]) {
49
- return philipsModels[gamutTypeABC];
227
+ if (philipsModels[modelId]) {
228
+ return philipsModels[modelId];
50
229
  }
51
230
 
52
231
  return ranges.default;
53
232
  }
54
233
 
55
- static rgbToXy(red, green, blue, gamutTypeABC = null) {
234
+ static rgbToXy(red, green, blue, modelId = null) {
56
235
  function getGammaCorrectedValue(value) {
57
236
  return (value > 0.04045) ? ((value + 0.055) / (1.0 + 0.055)) ** 2.4 : (value / 12.92);
58
237
  }
59
238
 
60
- const colorGamut = ColorConverter.getLightColorGamutRange(gamutTypeABC);
239
+ const colorGamut = ColorConverter.getLightColorGamutRange(modelId);
61
240
 
62
241
  red = parseFloat(red / 255);
63
242
  green = parseFloat(green / 255);
@@ -83,17 +262,6 @@ class ColorConverter {
83
262
  return xy;
84
263
  }
85
264
 
86
- static getBrightnessFromRGB(red, green, blue) {
87
- const hsv = convert.rgb.hsv(red, green, blue);
88
- const brightness = hsv[2];
89
- return brightness;
90
- }
91
-
92
- static convert_1_255_ToPercentage(number) {
93
- const percentage = (number / 255) * 100;
94
- return percentage;
95
- }
96
-
97
265
  static xyIsInGamutRange(xy, gamut) {
98
266
  gamut = gamut || ColorConverter.getGamutRanges().gamutC;
99
267
  if (Array.isArray(xy)) {
@@ -211,9 +379,9 @@ class ColorConverter {
211
379
  const Y = bri / 255;
212
380
  const X = (Y / y) * x;
213
381
  const Z = (Y / y) * z;
214
- let r = X * 1.612 - Y * 0.203 - Z * 0.302;
215
- let g = -X * 0.509 + Y * 1.412 + Z * 0.066;
216
- let b = X * 0.026 - Y * 0.072 + Z * 0.962;
382
+ let r = X * 1.656492 - Y * 0.354851 - Z * 0.255038;
383
+ let g = -X * 0.707196 + Y * 1.655397 + Z * 0.036152;
384
+ let b = X * 0.051713 - Y * 0.121364 + Z * 1.011530;
217
385
 
218
386
  r = getReversedGammaCorrectedValue(r);
219
387
  g = getReversedGammaCorrectedValue(g);
@@ -233,19 +401,10 @@ class ColorConverter {
233
401
  }
234
402
 
235
403
  return {
236
- red: Math.floor(r * 255),
237
- green: Math.floor(g * 255),
238
- blue: Math.floor(b * 255),
404
+ r: Math.floor(r * 255),
405
+ g: Math.floor(g * 255),
406
+ b: Math.floor(b * 255),
239
407
  };
240
408
  }
241
-
242
- // Linear interpolation of input y given starting and ending ranges
243
- static scale(y, range1 = [0, 100], range2 = [0, 255]) {
244
- const [xMin, xMax] = range2;
245
- const [yMin, yMax] = range1;
246
- const percent = (y - yMin) / (yMax - yMin);
247
- const ans = percent * (xMax - xMin) + xMin;
248
- return Math.round(ans);
249
- }
250
409
  }
251
410
  exports.ColorConverter = ColorConverter;
package/package.json CHANGED
@@ -3,11 +3,10 @@
3
3
  "engines": {
4
4
  "node": ">=16.0.0"
5
5
  },
6
- "version": "2.2.20",
6
+ "version": "2.2.22",
7
7
  "description": "Control your KNX intallation via Node-Red! Single Node KNX IN/OUT with optional ETS group address importer. Easy to use and highly configurable. With integrated Philips HUE devices control.",
8
8
  "dependencies": {
9
9
  "binary-parser": "2.2.1",
10
- "color-convert": "2.0.1",
11
10
  "crypto-js": "4.2.0",
12
11
  "dns-sync": "0.2.1",
13
12
  "eventsource": "2.0.2",