iobroker.zigbee 1.10.14 → 2.0.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/README.md +52 -7
- package/admin/admin.js +312 -125
- package/admin/img/PTM 215Z.png +0 -0
- package/admin/img/group_0.png +0 -0
- package/admin/img/group_x.png +0 -0
- package/admin/img/philips_hue_lom001.png +0 -0
- package/admin/index_m.html +95 -45
- package/admin/tab_m.html +117 -49
- package/docs/de/img/Zigbee_config_de.png +0 -0
- package/docs/de/img/Zigbee_tab_de.png +0 -0
- package/docs/en/img/Zigbee_config_en.png +0 -0
- package/docs/en/img/Zigbee_tab_en.png +0 -0
- package/docs/tutorial/groups-1.png +0 -0
- package/docs/tutorial/groups-2.png +0 -0
- package/docs/tutorial/tab-dev-1.png +0 -0
- package/io-package.json +43 -55
- package/lib/colors.js +7 -0
- package/lib/commands.js +125 -15
- package/lib/developer.js +0 -0
- package/lib/devices.js +78 -74
- package/lib/exclude.js +30 -54
- package/lib/exposes.js +224 -249
- package/lib/groups.js +80 -25
- package/lib/localConfig.js +295 -0
- package/lib/ota.js +0 -0
- package/lib/statescontroller.js +412 -185
- package/lib/utils.js +1 -1
- package/lib/zbDeviceAvailability.js +15 -23
- package/lib/zbDeviceConfigure.js +0 -0
- package/lib/zigbeecontroller.js +396 -252
- package/main.js +159 -54
- package/package.json +6 -5
package/lib/exposes.js
CHANGED
|
@@ -16,6 +16,7 @@ function genState(expose, role, name, desc) {
|
|
|
16
16
|
const stateId = stname.replace(/\*/g, '');
|
|
17
17
|
const stateName = (desc || expose.description || expose.name);
|
|
18
18
|
const propName = expose.property;
|
|
19
|
+
// 'switch' | 'lock' | 'binary' | 'list' | 'numeric' | 'enum' | 'text' | 'composite' | 'light' | 'cover' | 'fan' | 'climate';
|
|
19
20
|
switch (expose.type) {
|
|
20
21
|
case 'binary':
|
|
21
22
|
state = {
|
|
@@ -275,12 +276,24 @@ function createFromExposes(model, def) {
|
|
|
275
276
|
|
|
276
277
|
return newDev;
|
|
277
278
|
|
|
278
|
-
|
|
279
|
+
function hasMultipleProperties(obj, prop, len) {
|
|
280
|
+
const l = (len ? len: Object.keys.length(obj));
|
|
281
|
+
if (l != prop.length) return false;
|
|
282
|
+
for (const key of prop) {
|
|
283
|
+
if (!obj.hasOwnProperty(key)) return false;
|
|
284
|
+
}
|
|
285
|
+
return true;
|
|
286
|
+
};
|
|
279
287
|
|
|
280
288
|
function genStateFromExpose(expose) {
|
|
281
289
|
let state;
|
|
282
290
|
switch (expose.type) {
|
|
283
|
-
case 'light':
|
|
291
|
+
case 'light': {
|
|
292
|
+
let hasColorXY = false;
|
|
293
|
+
let hasColorHS = false;
|
|
294
|
+
let colorXYprop = undefined;
|
|
295
|
+
let colorHSprop = undefined;
|
|
296
|
+
|
|
284
297
|
for (const prop of expose.features) {
|
|
285
298
|
switch (prop.name) {
|
|
286
299
|
case 'state': {
|
|
@@ -364,237 +377,14 @@ function createFromExposes(model, def) {
|
|
|
364
377
|
pushToStates(statesDefs.colortemp_move, prop.access);
|
|
365
378
|
break;
|
|
366
379
|
}
|
|
367
|
-
case 'color_xy':
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
id: stateNameC,
|
|
371
|
-
prop: expose.endpoint ? `color_${expose.endpoint}` : 'color',
|
|
372
|
-
name: `Color ${expose.endpoint ? expose.endpoint : ''}`.trim(),
|
|
373
|
-
icon: undefined,
|
|
374
|
-
role: 'level.color.rgb',
|
|
375
|
-
write: true,
|
|
376
|
-
read: true,
|
|
377
|
-
type: 'string',
|
|
378
|
-
setter: value => {
|
|
379
|
-
// convert RGB to XY for set
|
|
380
|
-
/*
|
|
381
|
-
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(value);
|
|
382
|
-
let xy = [0, 0];
|
|
383
|
-
if (result) {
|
|
384
|
-
const r = parseInt(result[1], 16),
|
|
385
|
-
g = parseInt(result[2], 16),
|
|
386
|
-
b = parseInt(result[3], 16);
|
|
387
|
-
xy = rgb.rgb_to_cie(r, g, b);
|
|
388
|
-
}
|
|
389
|
-
return {
|
|
390
|
-
x: xy[0],
|
|
391
|
-
y: xy[1]
|
|
392
|
-
};
|
|
393
|
-
*/
|
|
394
|
-
let xy = [0, 0];
|
|
395
|
-
const rgbcolor = colors.ParseColor(value);
|
|
396
|
-
|
|
397
|
-
xy = rgb.rgb_to_cie(rgbcolor.r, rgbcolor.g, rgbcolor.b);
|
|
398
|
-
return {
|
|
399
|
-
x: xy[0],
|
|
400
|
-
y: xy[1],
|
|
401
|
-
};
|
|
402
|
-
},
|
|
403
|
-
setterOpt: (value, options) => {
|
|
404
|
-
const hasTransitionTime = options && options.hasOwnProperty('transition_time');
|
|
405
|
-
const transitionTime = hasTransitionTime ? options.transition_time : 0;
|
|
406
|
-
return {...options, transition: transitionTime};
|
|
407
|
-
},
|
|
408
|
-
getter: payload => {
|
|
409
|
-
if (payload.color && payload.color.hasOwnProperty('x') && payload.color.hasOwnProperty('y')) {
|
|
410
|
-
const colorval = rgb.cie_to_rgb(payload.color.x, payload.color.y);
|
|
411
|
-
return `#${utils.decimalToHex(colorval[0])}${utils.decimalToHex(colorval[1])}${utils.decimalToHex(colorval[2])}`;
|
|
412
|
-
} else {
|
|
413
|
-
return undefined;
|
|
414
|
-
}
|
|
415
|
-
},
|
|
416
|
-
epname: expose.endpoint,
|
|
417
|
-
setattr: 'color',
|
|
418
|
-
}, prop.access);
|
|
419
|
-
break;
|
|
420
|
-
}
|
|
380
|
+
case 'color_xy':
|
|
381
|
+
colorXYprop = prop;
|
|
382
|
+
hasColorXY = true; break;
|
|
421
383
|
case 'color_hs': {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
id: stateNameH,
|
|
425
|
-
prop: expose.endpoint ? `color_${expose.endpoint}` : 'color',
|
|
426
|
-
name: `Color ${expose.endpoint ? expose.endpoint : ''}`.trim(),
|
|
427
|
-
icon: undefined,
|
|
428
|
-
role: 'level.color.rgb',
|
|
429
|
-
write: true,
|
|
430
|
-
read: true,
|
|
431
|
-
type: 'string',
|
|
432
|
-
setter: value => {
|
|
433
|
-
const _rgb = colors.ParseColor(value);
|
|
434
|
-
const hsv = rgb.rgbToHSV(_rgb.r, _rgb.g, _rgb.b, true);
|
|
435
|
-
return {
|
|
436
|
-
hue: Math.min(Math.max(hsv.h, 1), 359),
|
|
437
|
-
saturation: hsv.s,
|
|
438
|
-
// brightness: Math.floor(hsv.v * 2.55),
|
|
439
|
-
};
|
|
440
|
-
},
|
|
441
|
-
setterOpt: (value, options) => {
|
|
442
|
-
const hasTransitionTime = options && options.hasOwnProperty('transition_time');
|
|
443
|
-
const transitionTime = hasTransitionTime ? options.transition_time : 0;
|
|
444
|
-
return {...options, transition: transitionTime};
|
|
445
|
-
},
|
|
446
|
-
epname: expose.endpoint,
|
|
447
|
-
setattr: 'color',
|
|
448
|
-
}, prop.access);
|
|
449
|
-
pushToStates({
|
|
450
|
-
id: expose.endpoint ? `hue_${expose.endpoint}` : 'hue',
|
|
451
|
-
prop: expose.endpoint ? `color_${expose.endpoint}` : 'color',
|
|
452
|
-
name: `Hue ${expose.endpoint || ''}`.trim(),
|
|
453
|
-
icon: undefined,
|
|
454
|
-
role: 'level.color.hue',
|
|
455
|
-
write: true,
|
|
456
|
-
read: false,
|
|
457
|
-
type: 'number',
|
|
458
|
-
min: 0,
|
|
459
|
-
max: 360,
|
|
460
|
-
inOptions: true,
|
|
461
|
-
setter: (value, options) => {
|
|
462
|
-
return {
|
|
463
|
-
hue: value,
|
|
464
|
-
saturation: options.saturation,
|
|
465
|
-
};
|
|
466
|
-
},
|
|
467
|
-
setterOpt: (value, options) => {
|
|
468
|
-
const hasTransitionTime = options && options.hasOwnProperty('transition_time');
|
|
469
|
-
const transitionTime = hasTransitionTime ? options.transition_time : 0;
|
|
470
|
-
const hasHueCalibrationTable = options && options.hasOwnProperty('hue_calibration');
|
|
471
|
-
if (hasHueCalibrationTable)
|
|
472
|
-
try {
|
|
473
|
-
return {
|
|
474
|
-
...options,
|
|
475
|
-
transition: transitionTime,
|
|
476
|
-
hue_correction: JSON.parse(options.hue_calibration)
|
|
477
|
-
};
|
|
478
|
-
} catch {
|
|
479
|
-
const hue_correction_table = [];
|
|
480
|
-
options.hue_calibration.split(',').forEach(element => {
|
|
481
|
-
const match = /([0-9]+):([0-9]+)/.exec(element);
|
|
482
|
-
if (match && match.length === 3)
|
|
483
|
-
hue_correction_table.push({
|
|
484
|
-
in: Number(match[1]),
|
|
485
|
-
out: Number(match[2])
|
|
486
|
-
});
|
|
487
|
-
});
|
|
488
|
-
if (hue_correction_table.length > 0) {
|
|
489
|
-
return {
|
|
490
|
-
...options,
|
|
491
|
-
transition: transitionTime,
|
|
492
|
-
hue_correction: hue_correction_table
|
|
493
|
-
};
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
return {...options, transition: transitionTime};
|
|
497
|
-
},
|
|
498
|
-
|
|
499
|
-
}, prop.access);
|
|
500
|
-
pushToStates({
|
|
501
|
-
id: expose.endpoint ? `saturation_${expose.endpoint}` : 'saturation',
|
|
502
|
-
prop: expose.endpoint ? `color_${expose.endpoint}` : 'color',
|
|
503
|
-
name: `Saturation ${expose.endpoint ? expose.endpoint : ''}`.trim(),
|
|
504
|
-
icon: undefined,
|
|
505
|
-
role: 'level.color.saturation',
|
|
506
|
-
write: true,
|
|
507
|
-
read: false,
|
|
508
|
-
type: 'number',
|
|
509
|
-
min: 0,
|
|
510
|
-
max: 100,
|
|
511
|
-
inOptions: true,
|
|
512
|
-
setter: (value, options) => ({
|
|
513
|
-
hue: options.hue,
|
|
514
|
-
saturation: value,
|
|
515
|
-
}),
|
|
516
|
-
setterOpt: (value, options) => {
|
|
517
|
-
const hasTransitionTime = options && options.hasOwnProperty('transition_time');
|
|
518
|
-
const transitionTime = hasTransitionTime ? options.transition_time : 0;
|
|
519
|
-
const hasHueCalibrationTable = options && options.hasOwnProperty('hue_calibration');
|
|
520
|
-
if (hasHueCalibrationTable)
|
|
521
|
-
try {
|
|
522
|
-
return {
|
|
523
|
-
...options,
|
|
524
|
-
transition: transitionTime,
|
|
525
|
-
hue_correction: JSON.parse(options.hue_calibration)
|
|
526
|
-
};
|
|
527
|
-
} catch {
|
|
528
|
-
const hue_correction_table = [];
|
|
529
|
-
options.hue_calibration.split(',').forEach(element => {
|
|
530
|
-
const match = /([0-9]+):([0-9]+)/.exec(element);
|
|
531
|
-
if (match && match.length === 3)
|
|
532
|
-
hue_correction_table.push({
|
|
533
|
-
in: Number(match[1]),
|
|
534
|
-
out: Number(match[2])
|
|
535
|
-
});
|
|
536
|
-
});
|
|
537
|
-
if (hue_correction_table.length > 0) {
|
|
538
|
-
return {
|
|
539
|
-
...options,
|
|
540
|
-
transition: transitionTime,
|
|
541
|
-
hue_correction: hue_correction_table
|
|
542
|
-
};
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
return {...options, transition: transitionTime};
|
|
546
|
-
},
|
|
547
|
-
|
|
548
|
-
}, prop.access);
|
|
384
|
+
colorHSprop = prop;
|
|
385
|
+
hasColorHS = true;
|
|
549
386
|
pushToStates(statesDefs.hue_move, prop.access);
|
|
550
387
|
pushToStates(statesDefs.saturation_move, prop.access);
|
|
551
|
-
pushToStates({
|
|
552
|
-
id: 'hue_calibration',
|
|
553
|
-
prop: 'color',
|
|
554
|
-
name: 'Hue color calibration table',
|
|
555
|
-
icon: undefined,
|
|
556
|
-
role: 'table',
|
|
557
|
-
write: true,
|
|
558
|
-
read: false,
|
|
559
|
-
type: 'string',
|
|
560
|
-
inOptions: true,
|
|
561
|
-
setter: (value, options) => ({
|
|
562
|
-
hue: options.hue,
|
|
563
|
-
saturation: options.saturation,
|
|
564
|
-
}),
|
|
565
|
-
setterOpt: (value, options) => {
|
|
566
|
-
const hasTransitionTime = options && options.hasOwnProperty('transition_time');
|
|
567
|
-
const transitionTime = hasTransitionTime ? options.transition_time : 0;
|
|
568
|
-
const hasHueCalibrationTable = options && options.hasOwnProperty('hue_calibration');
|
|
569
|
-
if (hasHueCalibrationTable)
|
|
570
|
-
try {
|
|
571
|
-
return {
|
|
572
|
-
...options,
|
|
573
|
-
transition: transitionTime,
|
|
574
|
-
hue_correction: JSON.parse(options.hue_calibration)
|
|
575
|
-
};
|
|
576
|
-
} catch {
|
|
577
|
-
const hue_correction_table = [];
|
|
578
|
-
options.hue_calibration.split(',').forEach(element => {
|
|
579
|
-
const match = /([0-9]+):([0-9]+)/.exec(element);
|
|
580
|
-
if (match && match.length === 3) {
|
|
581
|
-
hue_correction_table.push({
|
|
582
|
-
in: Number(match[1]),
|
|
583
|
-
out: Number(match[2])
|
|
584
|
-
});
|
|
585
|
-
}
|
|
586
|
-
});
|
|
587
|
-
if (hue_correction_table.length > 0) {
|
|
588
|
-
return {
|
|
589
|
-
...options,
|
|
590
|
-
transition: transitionTime,
|
|
591
|
-
hue_correction: hue_correction_table
|
|
592
|
-
};
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
return {...options, transition: transitionTime};
|
|
596
|
-
},
|
|
597
|
-
}, prop.access);
|
|
598
388
|
break;
|
|
599
389
|
}
|
|
600
390
|
default:
|
|
@@ -602,9 +392,181 @@ function createFromExposes(model, def) {
|
|
|
602
392
|
break;
|
|
603
393
|
}
|
|
604
394
|
}
|
|
395
|
+
if (hasColorXY || hasColorHS) {
|
|
396
|
+
const nameWithEp = expose.endpoint ? `color_${expose.endpoint}` : 'color';
|
|
397
|
+
pushToStates({
|
|
398
|
+
id: nameWithEp,
|
|
399
|
+
name: `Color ${expose.endpoint ? expose.endpoint : ''}`.trim(),
|
|
400
|
+
icon: undefined,
|
|
401
|
+
role: 'level.color.rgb',
|
|
402
|
+
write: true,
|
|
403
|
+
read: true,
|
|
404
|
+
type: 'string',
|
|
405
|
+
setter: value => {
|
|
406
|
+
try {
|
|
407
|
+
// JSON
|
|
408
|
+
const colorJSON = JSON.parse(value.replaceAll("'",'"'));
|
|
409
|
+
const numProp = Object.keys(colorJSON).length;
|
|
410
|
+
if (hasMultipleProperties(colorJSON, ['hsb'], numProp)) return colorJSON;
|
|
411
|
+
if (hasMultipleProperties(colorJSON, ['hsl'], numProp)) return colorJSON;
|
|
412
|
+
if (hasMultipleProperties(colorJSON, ['hsv'], numProp)) return colorJSON;
|
|
413
|
+
if (hasMultipleProperties(colorJSON, ['h','s','b'], numProp)) return colorJSON;
|
|
414
|
+
if (hasMultipleProperties(colorJSON, ['h','s','v'], numProp)) return colorJSON;
|
|
415
|
+
if (hasMultipleProperties(colorJSON, ['h','s','l'], numProp)) return colorJSON;
|
|
416
|
+
if (hasMultipleProperties(colorJSON, ['hue', 'saturation'], numProp)) return colorJSON;
|
|
417
|
+
if (hasMultipleProperties(colorJSON, ['hex'], numProp)) return colorJSON;
|
|
418
|
+
if (hasMultipleProperties(colorJSON, ['rgb'], numProp)) return colorJSON;
|
|
419
|
+
if (hasMultipleProperties(colorJSON, ['x', 'y'], numProp)) return colorJSON;
|
|
420
|
+
if (hasMultipleProperties(colorJSON, ['r', 'g', 'b'], numProp)) return colorJSON;
|
|
421
|
+
//return { json:colorJSON, numProp:numProp, value:value };
|
|
422
|
+
}
|
|
423
|
+
catch (error) {
|
|
424
|
+
//return { error: error.message };
|
|
425
|
+
};
|
|
426
|
+
// hex or named color
|
|
427
|
+
const rgbcolor = colors.ParseColor(value);
|
|
428
|
+
return rgbcolor;
|
|
429
|
+
},
|
|
430
|
+
setterOpt: (value, options) => {
|
|
431
|
+
const hasTransitionTime = options && options.hasOwnProperty('transition_time');
|
|
432
|
+
const transitionTime = hasTransitionTime ? options.transition_time : 0;
|
|
433
|
+
return {...options, transition: transitionTime};
|
|
434
|
+
},
|
|
435
|
+
getter: payload => {
|
|
436
|
+
if (typeof payload.color == 'object') {
|
|
437
|
+
const colorJSON = payload.color;
|
|
438
|
+
const color = JSON.stringify(colorJSON)
|
|
439
|
+
const numProp = Object.keys(colorJSON);
|
|
440
|
+
if (hasMultipleProperties(colorJSON, ['hsb'], numProp)) return color;
|
|
441
|
+
if (hasMultipleProperties(colorJSON, ['hsl'], numProp)) return color;
|
|
442
|
+
if (hasMultipleProperties(colorJSON, ['hsv'], numProp)) return color;
|
|
443
|
+
if (hasMultipleProperties(colorJSON, ['h','s','b'], numProp)) return color;
|
|
444
|
+
if (hasMultipleProperties(colorJSON, ['h','s','v'], numProp)) return color;
|
|
445
|
+
if (hasMultipleProperties(colorJSON, ['h','s','l'], numProp)) return color;
|
|
446
|
+
if (hasMultipleProperties(colorJSON, ['hue', 'saturation'], numProp)) return color;
|
|
447
|
+
if (hasMultipleProperties(colorJSON, ['hex'], numProp)) return color;
|
|
448
|
+
if (hasMultipleProperties(colorJSON, ['rgb'], numProp)) return color;
|
|
449
|
+
if (hasMultipleProperties(colorJSON, ['x', 'y'], numProp)) return color;
|
|
450
|
+
if (hasMultipleProperties(colorJSON, ['r', 'g', 'b'], numProp)) return color;
|
|
451
|
+
}
|
|
452
|
+
return undefined;
|
|
453
|
+
},
|
|
454
|
+
epname: expose.endpoint,
|
|
455
|
+
setattr: 'color',
|
|
456
|
+
}, (colorHSprop ? colorHSprop.access : colorXYprop.access));
|
|
457
|
+
if (hasColorXY) {
|
|
458
|
+
let channelWithEp = expose.endpoint ? `color_xy_${expose.endpoint}` : 'color_xy';
|
|
459
|
+
pushToStates({
|
|
460
|
+
id: `${channelWithEp}.x`,
|
|
461
|
+
name: `X`,
|
|
462
|
+
icon: undefined,
|
|
463
|
+
role: 'level.color',
|
|
464
|
+
write: true,
|
|
465
|
+
read: true,
|
|
466
|
+
type: 'number',
|
|
467
|
+
min: 0,
|
|
468
|
+
max: 1,
|
|
469
|
+
compositeKey: channelWithEp,
|
|
470
|
+
compositeTimeout: 500,
|
|
471
|
+
compositeState: 'color'
|
|
472
|
+
}, colorXYprop.access);
|
|
473
|
+
pushToStates({
|
|
474
|
+
id: `${channelWithEp}.y`,
|
|
475
|
+
name: `Y`,
|
|
476
|
+
icon: undefined,
|
|
477
|
+
role: 'level.color',
|
|
478
|
+
write: true,
|
|
479
|
+
read: true,
|
|
480
|
+
type: 'number',
|
|
481
|
+
min: 0,
|
|
482
|
+
max: 1,
|
|
483
|
+
compositeKey: channelWithEp,
|
|
484
|
+
compositeTimeout: 500,
|
|
485
|
+
compositeState: 'color'
|
|
486
|
+
}, colorXYprop.access);
|
|
487
|
+
channelWithEp = expose.endpoint ? `color_rgb_${expose.endpoint}` : 'color_rgb';
|
|
488
|
+
pushToStates({
|
|
489
|
+
id: `${channelWithEp}.r`,
|
|
490
|
+
name: `Red`,
|
|
491
|
+
icon: undefined,
|
|
492
|
+
role: 'level.color.red',
|
|
493
|
+
write: true,
|
|
494
|
+
read: true,
|
|
495
|
+
type: 'number',
|
|
496
|
+
min: 0,
|
|
497
|
+
max: 255,
|
|
498
|
+
compositeKey: channelWithEp,
|
|
499
|
+
compositeTimeout: 500,
|
|
500
|
+
compositeState: 'color',
|
|
501
|
+
composites: [`${channelWithEp}.r`,`${channelWithEp}.g`]
|
|
502
|
+
}, colorXYprop.access);
|
|
503
|
+
pushToStates({
|
|
504
|
+
id: `${channelWithEp}.g`,
|
|
505
|
+
name: `Green`,
|
|
506
|
+
icon: undefined,
|
|
507
|
+
role: 'level.color.green',
|
|
508
|
+
write: true,
|
|
509
|
+
read: true,
|
|
510
|
+
type: 'number',
|
|
511
|
+
min: 0,
|
|
512
|
+
max: 255,
|
|
513
|
+
compositeKey: channelWithEp,
|
|
514
|
+
compositeTimeout: 500,
|
|
515
|
+
compositeState: 'color',
|
|
516
|
+
composites: [`${channelWithEp}.x`,`${channelWithEp}.y`]
|
|
517
|
+
});
|
|
518
|
+
pushToStates({
|
|
519
|
+
id: `${channelWithEp}.b`,
|
|
520
|
+
name: `Blue`,
|
|
521
|
+
icon: undefined,
|
|
522
|
+
role: 'level.color.blue',
|
|
523
|
+
write: true,
|
|
524
|
+
read: true,
|
|
525
|
+
type: 'number',
|
|
526
|
+
min: 0,
|
|
527
|
+
max: 255,
|
|
528
|
+
compositeKey: channelWithEp,
|
|
529
|
+
compositeTimeout: 500,
|
|
530
|
+
compositeState: 'color'
|
|
531
|
+
}, colorXYprop.access);
|
|
532
|
+
}
|
|
533
|
+
if (hasColorHS) {
|
|
534
|
+
const channelWithEp = expose.endpoint ? `color_hs_${expose.endpoint}` : 'color_hs';
|
|
535
|
+
pushToStates({
|
|
536
|
+
id: `${channelWithEp}.hue`,
|
|
537
|
+
name: `Hue`,
|
|
538
|
+
icon: undefined,
|
|
539
|
+
role: 'level.color.hue',
|
|
540
|
+
write: true,
|
|
541
|
+
read: true,
|
|
542
|
+
type: 'number',
|
|
543
|
+
min: 0,
|
|
544
|
+
max: 360,
|
|
545
|
+
compositeKey: channelWithEp,
|
|
546
|
+
compositeTimeout: 500,
|
|
547
|
+
compositeState: 'color'
|
|
548
|
+
}, colorHSprop.access);
|
|
549
|
+
pushToStates({
|
|
550
|
+
id: `${channelWithEp}.saturation`,
|
|
551
|
+
name: `Saturation`,
|
|
552
|
+
icon: undefined,
|
|
553
|
+
role: 'level.color',
|
|
554
|
+
write: true,
|
|
555
|
+
read: true,
|
|
556
|
+
type: 'number',
|
|
557
|
+
min: 0,
|
|
558
|
+
max: 100,
|
|
559
|
+
compositeKey: channelWithEp,
|
|
560
|
+
compositeTimeout: 500,
|
|
561
|
+
compositeState: 'color'
|
|
562
|
+
}, colorHSprop.access);
|
|
563
|
+
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
605
567
|
pushToStates(statesDefs.transition_time, ea.STATE_SET);
|
|
606
568
|
break;
|
|
607
|
-
|
|
569
|
+
}
|
|
608
570
|
case 'switch':
|
|
609
571
|
for (const prop of expose.features) {
|
|
610
572
|
switch (prop.name) {
|
|
@@ -671,6 +633,7 @@ function createFromExposes(model, def) {
|
|
|
671
633
|
|
|
672
634
|
case 'enum':
|
|
673
635
|
switch (expose.name) {
|
|
636
|
+
|
|
674
637
|
case 'action': {
|
|
675
638
|
// Ansatz:
|
|
676
639
|
|
|
@@ -681,12 +644,15 @@ function createFromExposes(model, def) {
|
|
|
681
644
|
if (!Array.isArray(expose.values)) break;
|
|
682
645
|
const hasHold = expose.values.find((actionName) => actionName.includes('hold'));
|
|
683
646
|
const hasRelease = expose.values.find((actionName) => actionName.includes('release'));
|
|
647
|
+
const hasPress = expose.values.find((actionName) => actionName.includes('press'));
|
|
648
|
+
const hasPressRelease = expose.values.find((actionName) => actionName.includes('press_release'));
|
|
684
649
|
for (const actionName of expose.values) {
|
|
685
650
|
// is release state ? - skip
|
|
686
651
|
if (hasHold && hasRelease && actionName.includes('release')) continue;
|
|
687
652
|
// is hold state ?
|
|
688
653
|
if (hasHold && hasRelease && actionName.includes('hold')) {
|
|
689
654
|
const releaseActionName = actionName.replace('hold', 'release');
|
|
655
|
+
const releaseActionName2 = actionName.concat('_release');
|
|
690
656
|
state = {
|
|
691
657
|
id: actionName.replace(/\*/g, ''),
|
|
692
658
|
prop: 'action',
|
|
@@ -696,9 +662,11 @@ function createFromExposes(model, def) {
|
|
|
696
662
|
write: false,
|
|
697
663
|
read: true,
|
|
698
664
|
type: 'boolean',
|
|
699
|
-
getter: payload => payload.action === actionName ? true : (payload.action === releaseActionName ? false : undefined),
|
|
665
|
+
getter: payload => payload.action === actionName ? true : (payload.action === releaseActionName || payload.action === releaseActionName2 ? false : undefined),
|
|
700
666
|
};
|
|
701
|
-
} else {
|
|
667
|
+
} else if (hasPress && hasPressRelease && actionName.includes('press')) {
|
|
668
|
+
let getterKey = actionName.concat('_release');
|
|
669
|
+
if (expose.values.indexOf(getterKey) < 0) getterKey = actionName;
|
|
702
670
|
state = {
|
|
703
671
|
id: actionName.replace(/\*/g, ''),
|
|
704
672
|
prop: 'action',
|
|
@@ -708,10 +676,23 @@ function createFromExposes(model, def) {
|
|
|
708
676
|
write: false,
|
|
709
677
|
read: true,
|
|
710
678
|
type: 'boolean',
|
|
679
|
+
getter: payload => payload.action === getterKey ? true : undefined,
|
|
680
|
+
isEvent: true,
|
|
681
|
+
};
|
|
682
|
+
} else {
|
|
683
|
+
state = {
|
|
684
|
+
id: actionName.replace(/\*/g, ''),
|
|
685
|
+
prop: 'action',
|
|
686
|
+
name: actionName,
|
|
687
|
+
icon: undefined,
|
|
688
|
+
role: 'button',
|
|
689
|
+
write: false,
|
|
690
|
+
read: true,
|
|
691
|
+
type: 'boolean',
|
|
711
692
|
getter: payload => payload.action === actionName ? true : undefined,
|
|
712
693
|
isEvent: true,
|
|
713
694
|
};
|
|
714
|
-
}
|
|
695
|
+
};
|
|
715
696
|
pushToStates(state, expose.access);
|
|
716
697
|
}
|
|
717
698
|
state = null;
|
|
@@ -893,16 +874,14 @@ function createFromExposes(model, def) {
|
|
|
893
874
|
}
|
|
894
875
|
|
|
895
876
|
}
|
|
896
|
-
function applyExposes(mappedDevices, byModel
|
|
897
|
-
//
|
|
898
|
-
const allExcludesStr = JSON.stringify(allExcludesObj);
|
|
899
|
-
// create or update device from exposes
|
|
877
|
+
function applyExposes(mappedDevices, byModel) {
|
|
878
|
+
// create or device from exposes
|
|
900
879
|
for (const deviceDef of zigbeeHerdsmanConverters.definitions) {
|
|
901
|
-
applyDeviceDef(mappedDevices, byModel,
|
|
880
|
+
applyDeviceDef(mappedDevices, byModel, deviceDef);
|
|
902
881
|
|
|
903
882
|
if (deviceDef.hasOwnProperty('whiteLabel')) {
|
|
904
883
|
for (const deviceWhiteLabel of deviceDef.whiteLabel) {
|
|
905
|
-
applyDeviceDef(mappedDevices, byModel,
|
|
884
|
+
applyDeviceDef(mappedDevices, byModel, {
|
|
906
885
|
...deviceDef,
|
|
907
886
|
model: deviceWhiteLabel.model,
|
|
908
887
|
vendor: deviceWhiteLabel.vendor,
|
|
@@ -913,20 +892,16 @@ function applyExposes(mappedDevices, byModel, allExcludesObj) {
|
|
|
913
892
|
}
|
|
914
893
|
}
|
|
915
894
|
|
|
916
|
-
function applyDeviceDef(mappedDevices, byModel,
|
|
895
|
+
function applyDeviceDef(mappedDevices, byModel, deviceDef) {
|
|
917
896
|
const stripModel = utils.getModelRegEx(deviceDef.model);
|
|
918
897
|
const existsMap = byModel.get(stripModel);
|
|
919
898
|
|
|
920
|
-
if (
|
|
899
|
+
if (deviceDef.hasOwnProperty('exposes') && (!existsMap || !existsMap.hasOwnProperty('states'))) {
|
|
921
900
|
try {
|
|
922
901
|
const newDevice = createFromExposes(stripModel, deviceDef);
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
} else {
|
|
927
|
-
existsMap.states = newDevice.states;
|
|
928
|
-
existsMap.exposed = true;
|
|
929
|
-
}
|
|
902
|
+
mappedDevices.push(newDevice);
|
|
903
|
+
byModel.set(stripModel, newDevice);
|
|
904
|
+
|
|
930
905
|
} catch (e) {
|
|
931
906
|
console.log(`Wrong expose device definition ${deviceDef.vendor} ${stripModel}`);
|
|
932
907
|
}
|