iobroker.zigbee2mqtt 3.0.21 → 3.1.5

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/lib/rgb.js CHANGED
@@ -115,10 +115,15 @@ function cie_to_rgb(x, y, brightness) {
115
115
  * @returns {Array} Array that contains the CIE color values for x and y
116
116
  */
117
117
  function rgb_to_cie(red, green, blue) {
118
+ // Normalize RGB values from 0-255 to 0-1 before gamma correction
119
+ red = red / 255;
120
+ green = green / 255;
121
+ blue = blue / 255;
122
+
118
123
  // Apply a gamma correction to the RGB values, which makes the color more vivid and more the like the color displayed on the screen of your device
119
- red = red > 0.04045 ? Math.pow((red + 0.055) / (1.0 + 0.055), 2.4) : red / 12.92;
124
+ red = red > 0.04045 ? Math.pow((red + 0.055) / (1.0 + 0.055), 2.4) : red / 12.92;
120
125
  green = green > 0.04045 ? Math.pow((green + 0.055) / (1.0 + 0.055), 2.4) : green / 12.92;
121
- blue = blue > 0.04045 ? Math.pow((blue + 0.055) / (1.0 + 0.055), 2.4) : blue / 12.92;
126
+ blue = blue > 0.04045 ? Math.pow((blue + 0.055) / (1.0 + 0.055), 2.4) : blue / 12.92;
122
127
 
123
128
  // RGB values to XYZ using the Wide RGB D65 conversion formula
124
129
  const X = red * 0.664511 + green * 0.154324 + blue * 0.162028;
@@ -155,7 +160,9 @@ function hsvToRGB(h, s, v) {
155
160
  let g;
156
161
  let b;
157
162
  if (arguments.length === 1) {
158
- (s = h.s), (v = h.v), (h = h.h);
163
+ s = h.s;
164
+ v = h.v;
165
+ h = h.h;
159
166
  }
160
167
  const i = Math.floor(h * 6);
161
168
  const f = h * 6 - i;
@@ -163,24 +170,13 @@ function hsvToRGB(h, s, v) {
163
170
  const q = v * (1 - f * s);
164
171
  const t = v * (1 - (1 - f) * s);
165
172
  switch (i % 6) {
166
- case 0:
167
- (r = v), (g = t), (b = p);
168
- break;
169
- case 1:
170
- (r = q), (g = v), (b = p);
171
- break;
172
- case 2:
173
- (r = p), (g = v), (b = t);
174
- break;
175
- case 3:
176
- (r = p), (g = q), (b = v);
177
- break;
178
- case 4:
179
- (r = t), (g = p), (b = v);
180
- break;
181
- case 5:
182
- (r = v), (g = p), (b = q);
183
- break;
173
+ case 0: r = v; g = t; b = p; break;
174
+ case 1: r = q; g = v; b = p; break;
175
+ case 2: r = p; g = v; b = t; break;
176
+ case 3: r = p; g = q; b = v; break;
177
+ case 4: r = t; g = p; b = v; break;
178
+ case 5: r = v; g = p; b = q; break;
179
+ default: r = 0; g = 0; b = 0; break;
184
180
  }
185
181
  return {
186
182
  r: Math.round(r * 255),
@@ -198,7 +194,9 @@ function hsvToRGB(h, s, v) {
198
194
  */
199
195
  function rgbToHSV(r, g, b, numeric) {
200
196
  if (arguments.length === 1) {
201
- (g = r.g), (b = r.b), (r = r.r);
197
+ g = r.g;
198
+ b = r.b;
199
+ r = r.r;
202
200
  }
203
201
  const max = Math.max(r, g, b);
204
202
  const min = Math.min(r, g, b);
@@ -225,20 +223,18 @@ function rgbToHSV(r, g, b, numeric) {
225
223
  break;
226
224
  }
227
225
  if (numeric) {
228
- return {
229
-
226
+ return {
230
227
  h: Math.round(h * 360),
231
228
  s: Math.round(s * 100),
232
229
  v: Math.round(v * 100),
233
230
  };
234
231
  }
235
- return {
236
-
232
+ return {
237
233
  h: (h * 360).toFixed(3),
238
234
  s: (s * 100).toFixed(3),
239
235
  v: (v * 100).toFixed(3),
240
- };
241
- }
236
+ };
237
+ }
242
238
 
243
239
  /**
244
240
  *
@@ -281,20 +277,20 @@ function hsv_to_cie(h, s, v) {
281
277
  function rgb_to_rgbstring(element) {
282
278
  let col = '#';
283
279
  if (element && element.hasOwnProperty('r')) {
284
- col = col + element.r.toString(16).padStart(2, '0');
285
- } else {
286
- col = `${col }00`;
287
- }
280
+ col += element.r.toString(16).padStart(2, '0');
281
+ } else {
282
+ col += '00';
283
+ }
288
284
  if (element && element.hasOwnProperty('g')) {
289
- col = col + element.g.toString(16).padStart(2, '0');
290
- } else {
291
- col = `${col }00`;
292
- }
285
+ col += element.g.toString(16).padStart(2, '0');
286
+ } else {
287
+ col += '00';
288
+ }
293
289
  if (element && element.hasOwnProperty('b')) {
294
- col = col + element.b.toString(16).padStart(2, '0');
295
- } else {
296
- col = `${col }00`;
297
- }
290
+ col += element.b.toString(16).padStart(2, '0');
291
+ } else {
292
+ col += '00';
293
+ }
298
294
  return col;
299
295
  }
300
296
 
@@ -306,30 +302,23 @@ col = `${col }00`;
306
302
  */
307
303
  function hsbToRGB(h, s, b) {
308
304
  const br = Math.round(b * 2.55);
309
- if (s == 0) {
305
+ if (s === 0) {
310
306
  return [br, br, br];
311
- }
312
- const hue = h % 360;
313
- const f = hue % 60;
314
- const p = Math.round(b * (100 - s) * 0.0255);
315
- const q = Math.round(b * (6000 - s * f) * 0.000425);
316
- const t = Math.round(b * (6000 - s * (60 - f)) * 0.000425);
317
- switch (Math.floor(hue / 60)) {
318
- case 0:
319
- return [br, t, p];
320
- case 1:
321
- return [q, br, p];
322
- case 2:
323
- return [p, br, t];
324
- case 3:
325
- return [p, q, br];
326
- case 4:
327
- return [t, p, br];
328
- case 5:
329
- return [br, p, q];
330
- }
331
-
332
- return false;
307
+ }
308
+ const hue = h % 360;
309
+ const f = hue % 60;
310
+ const p = Math.round(b * (100 - s) * 0.0255);
311
+ const q = Math.round(b * (6000 - s * f) * 0.000425);
312
+ const t = Math.round(b * (6000 - s * (60 - f)) * 0.000425);
313
+ switch (Math.floor(hue / 60)) {
314
+ case 0: return [br, t, p];
315
+ case 1: return [q, br, p];
316
+ case 2: return [p, br, t];
317
+ case 3: return [p, q, br];
318
+ case 4: return [t, p, br];
319
+ case 5: return [br, p, q];
320
+ }
321
+ return [br, br, br]; // fallback – sollte nie erreicht werden
333
322
  }
334
323
 
335
324
  /**
package/lib/states.js CHANGED
@@ -1,11 +1,8 @@
1
-
2
1
  'use strict';
3
2
 
4
3
  /*eslint no-unused-vars: ['off']*/
5
4
 
6
- const rgb = require('./rgb.js');
7
5
  const utils = require('./utils.js');
8
- const colors = require('./colors.js');
9
6
 
10
7
  /* states for device:
11
8
  id - sysname of state, id
@@ -58,8 +55,6 @@ const unitLookup = {
58
55
  illuminance_lux: 'lx',
59
56
  };
60
57
 
61
- const timers = {};
62
-
63
58
  const states = {
64
59
  link_quality: {
65
60
  id: 'link_quality',
@@ -98,12 +93,21 @@ const states = {
98
93
  type: 'string',
99
94
  def: '',
100
95
  getter: (payload) => {
101
- return payload.last_seen.replace('T', ' ').split('+')[0];
96
+ const val = payload.last_seen;
97
+ if (val == null) {
98
+ return undefined;
99
+ }
100
+ // Z2M kann last_seen als Unix-Timestamp (number in ms) ODER als ISO-String senden
101
+ if (typeof val === 'number') {
102
+ return new Date(val).toISOString().replace('T', ' ').split('.')[0];
103
+ }
104
+ if (typeof val === 'string') {
105
+ return val.replace('T', ' ').split('+')[0].split('.')[0];
106
+ }
107
+ return String(val);
102
108
  },
103
109
  },
104
110
 
105
-
106
-
107
111
  simulated_brightness: {
108
112
  id: 'simulated_brightness',
109
113
  prop: 'brightness',
@@ -115,13 +119,17 @@ const states = {
115
119
  type: 'number',
116
120
  def: 0,
117
121
  unit: '%',
122
+ // Fix: null-Guard – bulbLevelToAdapterLevel(undefined) würde 0 liefern (Lampe scheinbar aus)
118
123
  getter: (payload) => {
124
+ if (payload.brightness == null) { return undefined; }
119
125
  return utils.bulbLevelToAdapterLevel(payload.brightness);
120
126
  },
121
127
  },
122
128
 
129
+ // Fix: prop ergänzt – statesController sucht zuerst per state.prop === payload-key
123
130
  state: {
124
131
  id: 'state',
132
+ prop: 'state',
125
133
  name: 'Switch state',
126
134
  icon: undefined,
127
135
  role: 'switch',
@@ -135,6 +143,7 @@ const states = {
135
143
 
136
144
  brightness: {
137
145
  id: 'brightness',
146
+ prop: 'brightness',
138
147
  name: 'Brightness',
139
148
  options: ['transition'],
140
149
  icon: undefined,
@@ -142,16 +151,16 @@ const states = {
142
151
  write: true,
143
152
  read: true,
144
153
  type: 'number',
154
+ def: 100,
145
155
  unit: '%',
146
156
  min: 0,
147
157
  max: 100,
148
- def: 100,
158
+ // Fix: null-Guard – payload ohne brightness-Key liefert sonst fälschlich 0
149
159
  getter: (payload) => {
160
+ if (payload.brightness == null) { return undefined; }
150
161
  return utils.bulbLevelToAdapterLevel(payload.brightness);
151
162
  },
152
- setter: (value) => {
153
- return utils.adapterLevelToBulbLevel(value);
154
- },
163
+ setter: (value) => utils.adapterLevelToBulbLevel(value),
155
164
  },
156
165
 
157
166
  brightness_move: {
@@ -163,9 +172,9 @@ const states = {
163
172
  write: true,
164
173
  read: false,
165
174
  type: 'number',
175
+ def: 0,
166
176
  min: -50,
167
177
  max: 50,
168
- def: 0,
169
178
  },
170
179
 
171
180
  colortemp_move: {
@@ -177,9 +186,9 @@ const states = {
177
186
  write: true,
178
187
  read: false,
179
188
  type: 'number',
189
+ def: 0,
180
190
  min: -50,
181
191
  max: 50,
182
- def: 0,
183
192
  },
184
193
 
185
194
  transition: {
@@ -191,12 +200,13 @@ const states = {
191
200
  write: true,
192
201
  read: false,
193
202
  type: 'number',
203
+ def: -1,
194
204
  unit: 'sec',
195
205
  min: -1,
196
206
  max: 65535,
197
- def: -1,
198
207
  isOption: true,
199
208
  },
209
+
200
210
  send_payload: {
201
211
  id: 'send_payload',
202
212
  name: 'Send a raw json payload',
@@ -207,39 +217,49 @@ const states = {
207
217
  type: 'string',
208
218
  def: '{}',
209
219
  },
220
+
221
+ // Fix: prop ergänzt – Z2M sendet payload.voltage
210
222
  voltage: {
211
223
  id: 'voltage',
224
+ prop: 'voltage',
212
225
  name: 'Voltage',
213
226
  icon: undefined,
214
227
  role: 'value.voltage',
215
228
  write: false,
216
229
  read: true,
217
230
  type: 'number',
218
- unit: 'V',
219
231
  def: 0,
232
+ unit: 'V',
220
233
  },
234
+
235
+ // Fix: prop ergänzt – Z2M sendet payload.voltage (Battery-Variante)
221
236
  battery_voltage: {
222
237
  id: 'voltage',
238
+ prop: 'voltage',
223
239
  name: 'Battery voltage',
224
240
  icon: undefined,
225
241
  role: 'battery.voltage',
226
242
  write: false,
227
243
  read: true,
228
244
  type: 'number',
229
- unit: 'V',
230
245
  def: 0,
246
+ unit: 'V',
231
247
  },
248
+
249
+ // Fix: prop ergänzt – Z2M sendet payload.energy
232
250
  energy: {
233
251
  id: 'energy',
252
+ prop: 'energy',
234
253
  name: 'Sum of consumed energy',
235
254
  icon: undefined,
236
255
  role: 'value.power.consumption',
237
256
  write: false,
238
257
  read: true,
239
258
  type: 'number',
240
- unit: 'kWh',
241
259
  def: 0,
260
+ unit: 'kWh',
242
261
  },
262
+
243
263
  battery: {
244
264
  id: 'battery',
245
265
  prop: 'battery',
@@ -249,35 +269,43 @@ const states = {
249
269
  write: false,
250
270
  read: true,
251
271
  type: 'number',
272
+ def: 0,
252
273
  unit: '%',
253
274
  min: 0,
254
275
  max: 100,
255
- def: 0,
256
276
  },
277
+
278
+ // Fix: prop ergänzt – Z2M sendet payload.device_temperature
257
279
  device_temperature: {
258
280
  id: 'device_temperature',
281
+ prop: 'device_temperature',
259
282
  name: 'Temperature of the device',
260
283
  icon: undefined,
261
284
  role: 'value.temperature',
262
285
  write: false,
263
286
  read: true,
264
287
  type: 'number',
265
- unit: '°C',
266
288
  def: 0,
289
+ unit: '°C',
267
290
  },
291
+
292
+ // Fix: prop ergänzt – Z2M sendet payload.temperature
268
293
  temperature: {
269
294
  id: 'temperature',
295
+ prop: 'temperature',
270
296
  name: 'Temperature',
271
297
  icon: undefined,
272
298
  role: 'value.temperature',
273
299
  write: false,
274
300
  read: true,
275
301
  type: 'number',
276
- unit: '°C',
277
302
  def: 0,
303
+ unit: '°C',
278
304
  },
305
+
279
306
  humidity: {
280
307
  id: 'humidity',
308
+ prop: 'humidity',
281
309
  name: 'Humidity',
282
310
  icon: undefined,
283
311
  role: 'value.humidity',
@@ -289,8 +317,10 @@ const states = {
289
317
  min: 0,
290
318
  max: 100,
291
319
  },
320
+
292
321
  pressure: {
293
322
  id: 'pressure',
323
+ prop: 'pressure',
294
324
  name: 'Pressure',
295
325
  icon: undefined,
296
326
  role: 'value.pressure',
@@ -302,6 +332,7 @@ const states = {
302
332
  min: 0,
303
333
  max: 10000,
304
334
  },
335
+
305
336
  illuminance: {
306
337
  id: 'illuminance',
307
338
  prop: 'illuminance_lux',
@@ -314,6 +345,30 @@ const states = {
314
345
  def: 0,
315
346
  unit: 'lux',
316
347
  },
348
+
349
+ /**
350
+ * Illuminance berechnet aus dem Z2M-Rohwert (prop='illuminance').
351
+ * Formel: lux = round(10^(raw / 10000))
352
+ * Wird zusätzlich zu illuminance_raw angelegt wenn das Gerät keinen
353
+ * illuminance_lux-Expose liefert.
354
+ */
355
+ illuminance_from_raw: {
356
+ id: 'illuminance',
357
+ prop: 'illuminance',
358
+ name: 'Illuminance',
359
+ icon: undefined,
360
+ role: 'value.brightness',
361
+ write: false,
362
+ read: true,
363
+ type: 'number',
364
+ def: 0,
365
+ unit: 'lux',
366
+ getter: (payload) => {
367
+ if (payload.illuminance == null) { return undefined; }
368
+ return Math.round(Math.pow(10, payload.illuminance / 10000));
369
+ },
370
+ },
371
+
317
372
  illuminance_raw: {
318
373
  id: 'illuminance_raw',
319
374
  prop: 'illuminance',
@@ -326,8 +381,51 @@ const states = {
326
381
  def: 0,
327
382
  unit: '',
328
383
  },
384
+
385
+ /**
386
+ * Rohwert-State wenn das Gerät 'illuminance_raw' als payload-Schlüssel sendet
387
+ * (z.B. Geräte mit eigenem illuminance_raw-Expose).
388
+ */
389
+ illuminance_raw_direct: {
390
+ id: 'illuminance_raw',
391
+ prop: 'illuminance_raw',
392
+ name: 'Illuminance raw',
393
+ icon: undefined,
394
+ role: 'value.brightness',
395
+ write: false,
396
+ read: true,
397
+ type: 'number',
398
+ def: 0,
399
+ unit: '',
400
+ },
401
+
402
+ /**
403
+ * Illuminance in Lux berechnet aus payload.illuminance_raw.
404
+ * Formel: lux = round(10^(illuminance_raw / 10000))
405
+ * Wird angelegt wenn das Gerät 'illuminance_raw' als Expose-Typ hat,
406
+ * aber kein separates 'illuminance_lux' liefert.
407
+ */
408
+ illuminance_from_illuminance_raw: {
409
+ id: 'illuminance',
410
+ prop: 'illuminance_raw',
411
+ name: 'Illuminance',
412
+ icon: undefined,
413
+ role: 'value.brightness',
414
+ write: false,
415
+ read: true,
416
+ type: 'number',
417
+ def: 0,
418
+ unit: 'lux',
419
+ getter: (payload) => {
420
+ if (payload.illuminance_raw == null) { return undefined; }
421
+ return Math.round(Math.pow(10, payload.illuminance_raw / 10000));
422
+ },
423
+ },
424
+
425
+ // Fix: prop ergänzt – Z2M sendet payload.occupancy
329
426
  occupancy: {
330
427
  id: 'occupancy',
428
+ prop: 'occupancy',
331
429
  name: 'Occupancy',
332
430
  icon: undefined,
333
431
  role: 'sensor.motion',
@@ -336,6 +434,7 @@ const states = {
336
434
  type: 'boolean',
337
435
  def: false,
338
436
  },
437
+
339
438
  contact: {
340
439
  id: 'contact',
341
440
  prop: 'contact',
@@ -347,6 +446,7 @@ const states = {
347
446
  type: 'boolean',
348
447
  def: false,
349
448
  },
449
+
350
450
  opened: {
351
451
  id: 'opened',
352
452
  prop: 'contact',
@@ -357,8 +457,13 @@ const states = {
357
457
  read: true,
358
458
  type: 'boolean',
359
459
  def: false,
360
- getter: (payload) => !payload.contact,
460
+ // Fix: null-Guard – !undefined wäre true (Tür fälschlich offen)
461
+ getter: (payload) => {
462
+ if (payload.contact == null) { return undefined; }
463
+ return !payload.contact;
464
+ },
361
465
  },
466
+
362
467
  tamper: {
363
468
  id: 'tampered',
364
469
  prop: 'tamper',
@@ -368,7 +473,9 @@ const states = {
368
473
  write: false,
369
474
  read: true,
370
475
  type: 'boolean',
476
+ def: false,
371
477
  },
478
+
372
479
  water_leak: {
373
480
  id: 'detected',
374
481
  prop: 'water_leak',
@@ -380,6 +487,7 @@ const states = {
380
487
  type: 'boolean',
381
488
  def: false,
382
489
  },
490
+
383
491
  batt_low_t_f: {
384
492
  id: 'battery_low',
385
493
  prop: 'battery_low',
@@ -391,6 +499,7 @@ const states = {
391
499
  type: 'boolean',
392
500
  def: false,
393
501
  },
502
+
394
503
  load_power: {
395
504
  id: 'load_power',
396
505
  prop: 'power',
@@ -413,11 +522,10 @@ const states = {
413
522
  write: false,
414
523
  read: true,
415
524
  type: 'number',
416
- unit: 'A',
417
525
  def: 0,
526
+ unit: 'A',
418
527
  },
419
528
 
420
-
421
529
  temp_calibration: {
422
530
  id: 'temperature_calibration',
423
531
  prop: 'temperature_calibration',
@@ -431,6 +539,7 @@ const states = {
431
539
  unit: '°C',
432
540
  isOption: true,
433
541
  },
542
+
434
543
  local_temperature: {
435
544
  id: 'local_temperature',
436
545
  prop: 'local_temperature',
@@ -443,6 +552,7 @@ const states = {
443
552
  def: 0,
444
553
  unit: '°C',
445
554
  },
555
+
446
556
  local_temperature_calibration: {
447
557
  id: 'local_temperature_calibration',
448
558
  prop: 'local_temperature_calibration',
@@ -456,7 +566,6 @@ const states = {
456
566
  unit: '°C',
457
567
  },
458
568
 
459
-
460
569
  climate_away_mode: {
461
570
  id: 'away_mode',
462
571
  prop: 'away_mode',
@@ -466,34 +575,41 @@ const states = {
466
575
  write: true,
467
576
  read: true,
468
577
  type: 'boolean',
578
+ def: false,
469
579
  isEvent: true,
470
- getter: (payload) => (payload.action === 'ON' ? true : false),
580
+ getter: (payload) => payload.away_mode === 'ON',
471
581
  setter: (value) => (value ? 'ON' : 'OFF'),
472
582
  },
583
+
584
+ // Fix: def ergänzt – ioBroker meldet Fehler beim Anlegen ohne def bei type:'string'
473
585
  climate_system_mode: {
474
586
  id: 'mode',
475
- name: 'Mode',
476
587
  prop: 'system_mode',
588
+ name: 'Mode',
477
589
  icon: undefined,
478
590
  role: 'state',
479
591
  write: true,
480
592
  read: true,
481
593
  type: 'string',
594
+ def: '',
482
595
  states: { auto: 'auto', off: 'off', heat: 'heat' },
483
596
  },
597
+
598
+ // Fix: write:false – running_state ist ein reiner Sensor-State (Z2M sendet nur, setzt nie)
599
+ // Fix: def ergänzt
484
600
  climate_running_mode: {
485
601
  id: 'running_state',
486
- name: 'Running Mode',
487
602
  prop: 'running_state',
603
+ name: 'Running Mode',
488
604
  icon: undefined,
489
605
  role: 'state',
490
- write: true,
606
+ write: false,
491
607
  read: true,
492
- type: 'string', // valid: low, medium, high
608
+ type: 'string',
609
+ def: '',
493
610
  states: { idle: 'idle', heat: 'heat' },
494
611
  },
495
612
 
496
-
497
613
  brightness_step: {
498
614
  id: 'brightness_step',
499
615
  prop: 'brightness_step',
@@ -507,6 +623,7 @@ const states = {
507
623
  min: -50,
508
624
  max: 50,
509
625
  },
626
+
510
627
  hue_move: {
511
628
  id: 'hue_move',
512
629
  prop: 'hue_move',
@@ -516,15 +633,15 @@ const states = {
516
633
  write: true,
517
634
  read: false,
518
635
  type: 'number',
636
+ def: 0,
519
637
  min: -50,
520
638
  max: 50,
521
- def: 0,
522
639
  },
523
640
 
524
641
  child_lock: {
525
642
  id: 'lock',
526
- name: 'Locked',
527
643
  prop: 'child_lock',
644
+ name: 'Locked',
528
645
  icon: undefined,
529
646
  role: 'state',
530
647
  write: true,