iobroker.zigbee 2.0.0 → 2.0.2
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 +40 -10
- 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 +116 -48
- package/docs/de/img/Zigbee_config_de.png +0 -0
- package/docs/de/img/Zigbee_tab_de.png +0 -0
- package/docs/de/readme.md +21 -28
- 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 +55 -41
- package/lib/binding.js +1 -1
- package/lib/colors.js +7 -0
- package/lib/commands.js +136 -20
- package/lib/developer.js +0 -0
- package/lib/devices.js +88 -74
- package/lib/exclude.js +30 -54
- package/lib/exposes.js +247 -290
- package/lib/groups.js +84 -29
- package/lib/localConfig.js +301 -0
- package/lib/ota.js +5 -4
- package/lib/statescontroller.js +452 -185
- package/lib/utils.js +5 -3
- package/lib/zbDeviceAvailability.js +16 -30
- package/lib/zbDeviceConfigure.js +55 -28
- package/lib/zbDeviceEvent.js +2 -13
- package/lib/zigbeecontroller.js +335 -214
- package/main.js +181 -65
- package/package.json +8 -7
package/lib/exposes.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
|
|
4
|
-
const statesDefs = require('./states
|
|
5
|
-
const rgb = require('./rgb
|
|
6
|
-
const utils = require('./utils
|
|
7
|
-
const colors = require('./colors
|
|
8
|
-
const ea = require('zigbee-herdsman-converters/lib/exposes
|
|
4
|
+
const statesDefs = require('./states').states;
|
|
5
|
+
const rgb = require('./rgb');
|
|
6
|
+
const utils = require('./utils');
|
|
7
|
+
const colors = require('./colors');
|
|
8
|
+
const ea = require('zigbee-herdsman-converters/lib/exposes').access;
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
const __logger = undefined;
|
|
9
12
|
|
|
10
13
|
function genState(expose, role, name, desc) {
|
|
11
14
|
let state;
|
|
@@ -16,6 +19,7 @@ function genState(expose, role, name, desc) {
|
|
|
16
19
|
const stateId = stname.replace(/\*/g, '');
|
|
17
20
|
const stateName = (desc || expose.description || expose.name);
|
|
18
21
|
const propName = expose.property;
|
|
22
|
+
// 'switch' | 'lock' | 'binary' | 'list' | 'numeric' | 'enum' | 'text' | 'composite' | 'light' | 'cover' | 'fan' | 'climate';
|
|
19
23
|
switch (expose.type) {
|
|
20
24
|
case 'binary':
|
|
21
25
|
state = {
|
|
@@ -133,7 +137,7 @@ function genState(expose, role, name, desc) {
|
|
|
133
137
|
return state;
|
|
134
138
|
}
|
|
135
139
|
|
|
136
|
-
function createFromExposes(model, def) {
|
|
140
|
+
function createFromExposes(model, def, device, log) {
|
|
137
141
|
const states = [];
|
|
138
142
|
// make the different (set and get) part of state is updatable if different exposes is used for get and set
|
|
139
143
|
// as example:
|
|
@@ -252,15 +256,12 @@ function createFromExposes(model, def) {
|
|
|
252
256
|
if (typeof def.exposes == 'object') {
|
|
253
257
|
for (const expose of def.exposes) {
|
|
254
258
|
genStateFromExpose(expose);
|
|
255
|
-
|
|
256
|
-
|
|
257
259
|
}
|
|
258
260
|
}
|
|
259
261
|
|
|
260
262
|
// maybee here check manufacturerName for tuya devices
|
|
261
263
|
if (typeof def.exposes == 'function') {
|
|
262
|
-
const expFunction = def.exposes(
|
|
263
|
-
|
|
264
|
+
const expFunction = def.exposes(device, {}); // maybee here check manufacturerName for tuya devices
|
|
264
265
|
for (const expose of expFunction) {
|
|
265
266
|
genStateFromExpose(expose);
|
|
266
267
|
}
|
|
@@ -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,25 +633,33 @@ function createFromExposes(model, def) {
|
|
|
671
633
|
|
|
672
634
|
case 'enum':
|
|
673
635
|
switch (expose.name) {
|
|
636
|
+
|
|
674
637
|
case 'action': {
|
|
675
638
|
// Ansatz:
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
639
|
+
//generate an 'action' state\
|
|
640
|
+
state = genState(expose);
|
|
641
|
+
state.isEvent = true;
|
|
642
|
+
pushToStates(state, expose.access);
|
|
680
643
|
|
|
681
644
|
if (!Array.isArray(expose.values)) break;
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
645
|
+
// identify hold/release pairs
|
|
646
|
+
// if pairs of (prefix)press(postfix) and (prefix)release(postfix) or
|
|
647
|
+
// (prefix)hold(postfix) and (prefix)release(postfix)
|
|
648
|
+
// exist, the release action will not get its own state. Instead, the
|
|
649
|
+
// respective press or hold state will not be an event and be cleared at release time
|
|
650
|
+
//
|
|
651
|
+
const phr = {};
|
|
652
|
+
const phc = expose.values.filter((actionName) => actionName.match(/hold|press/gm));
|
|
653
|
+
for (const actionName of phc) {
|
|
654
|
+
const releasestr = actionName.replace(/hold|press/gm, 'release');
|
|
655
|
+
const release = expose.values.find((actionName) => actionName == releasestr);
|
|
656
|
+
phr[actionName]=release;
|
|
657
|
+
if (release) phr[release]= 'IGNORE';
|
|
658
|
+
}
|
|
686
659
|
for (const actionName of expose.values) {
|
|
687
|
-
|
|
688
|
-
if (
|
|
689
|
-
//
|
|
690
|
-
if (hasHold && hasRelease && actionName.includes('hold')) {
|
|
691
|
-
const releaseActionName = actionName.replace('hold', 'release');
|
|
692
|
-
const releaseActionName2 = actionName.concat('_release');
|
|
660
|
+
const release = phr[actionName];
|
|
661
|
+
if (release === 'IGNORE') continue // a release message for which a press or hold exists.
|
|
662
|
+
if (release) { // a press or hold state with a matching release state
|
|
693
663
|
state = {
|
|
694
664
|
id: actionName.replace(/\*/g, ''),
|
|
695
665
|
prop: 'action',
|
|
@@ -699,25 +669,10 @@ function createFromExposes(model, def) {
|
|
|
699
669
|
write: false,
|
|
700
670
|
read: true,
|
|
701
671
|
type: 'boolean',
|
|
702
|
-
getter: payload => payload.action === actionName ? true : (payload.action ===
|
|
703
|
-
};
|
|
704
|
-
} else if (hasPress && hasPressRelease && actionName.includes('press')) {
|
|
705
|
-
let getterKey = actionName.concat('_release');
|
|
706
|
-
if (expose.values.indexOf(getterKey) < 0) getterKey = actionName;
|
|
707
|
-
state = {
|
|
708
|
-
id: actionName.replace(/\*/g, ''),
|
|
709
|
-
prop: 'action',
|
|
710
|
-
name: actionName,
|
|
711
|
-
icon: undefined,
|
|
712
|
-
role: 'button',
|
|
713
|
-
write: false,
|
|
714
|
-
read: true,
|
|
715
|
-
type: 'boolean',
|
|
716
|
-
getter: payload => payload.action === getterKey ? true : undefined,
|
|
717
|
-
isEvent: true,
|
|
672
|
+
getter: payload => payload.action === actionName ? true : (payload.action === release ? false : undefined),
|
|
718
673
|
};
|
|
719
674
|
} else {
|
|
720
|
-
|
|
675
|
+
state = {
|
|
721
676
|
id: actionName.replace(/\*/g, ''),
|
|
722
677
|
prop: 'action',
|
|
723
678
|
name: actionName,
|
|
@@ -911,16 +866,15 @@ function createFromExposes(model, def) {
|
|
|
911
866
|
}
|
|
912
867
|
|
|
913
868
|
}
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
// create or update device from exposes
|
|
869
|
+
|
|
870
|
+
function applyExposes(mappedDevices, byModel) {
|
|
871
|
+
// create or device from exposes
|
|
918
872
|
for (const deviceDef of zigbeeHerdsmanConverters.definitions) {
|
|
919
|
-
applyDeviceDef(mappedDevices, byModel,
|
|
873
|
+
applyDeviceDef(mappedDevices, byModel, deviceDef);
|
|
920
874
|
|
|
921
875
|
if (deviceDef.hasOwnProperty('whiteLabel')) {
|
|
922
876
|
for (const deviceWhiteLabel of deviceDef.whiteLabel) {
|
|
923
|
-
applyDeviceDef(mappedDevices, byModel,
|
|
877
|
+
applyDeviceDef(mappedDevices, byModel, {
|
|
924
878
|
...deviceDef,
|
|
925
879
|
model: deviceWhiteLabel.model,
|
|
926
880
|
vendor: deviceWhiteLabel.vendor,
|
|
@@ -931,26 +885,29 @@ function applyExposes(mappedDevices, byModel, allExcludesObj) {
|
|
|
931
885
|
}
|
|
932
886
|
}
|
|
933
887
|
|
|
934
|
-
function
|
|
888
|
+
async function applyExposeForDevice(mappedDevices, byModel, device) {
|
|
889
|
+
const deviceDef = await zigbeeHerdsmanConverters.findByDevice(device);
|
|
890
|
+
if (!deviceDef) return false;
|
|
891
|
+
applyDeviceDef(mappedDevices, byModel, deviceDef, device);
|
|
892
|
+
return true;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
function applyDeviceDef(mappedDevices, byModel, deviceDef, device) {
|
|
935
896
|
const stripModel = utils.getModelRegEx(deviceDef.model);
|
|
936
897
|
const existsMap = byModel.get(stripModel);
|
|
937
|
-
|
|
938
|
-
if ((deviceDef.hasOwnProperty('exposes') && (!existsMap || !existsMap.hasOwnProperty('states'))) || allExcludesStr.indexOf(stripModel) > 0) {
|
|
898
|
+
if (deviceDef.hasOwnProperty('exposes') && (!existsMap || !existsMap.hasOwnProperty('states'))) {
|
|
939
899
|
try {
|
|
940
|
-
const newDevice = createFromExposes(stripModel, deviceDef);
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
} else {
|
|
945
|
-
existsMap.states = newDevice.states;
|
|
946
|
-
existsMap.exposed = true;
|
|
947
|
-
}
|
|
900
|
+
const newDevice = createFromExposes(stripModel, deviceDef, device);
|
|
901
|
+
mappedDevices.push(newDevice);
|
|
902
|
+
byModel.set(stripModel, newDevice);
|
|
903
|
+
|
|
948
904
|
} catch (e) {
|
|
949
|
-
|
|
905
|
+
//this.debug('empty catch in exposes');
|
|
950
906
|
}
|
|
951
907
|
}
|
|
952
908
|
}
|
|
953
909
|
|
|
954
910
|
module.exports = {
|
|
955
911
|
applyExposes: applyExposes,
|
|
912
|
+
applyExposeForDevice: applyExposeForDevice,
|
|
956
913
|
};
|