iobroker.zigbee 2.0.0 → 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 +27 -9
- 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/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 +40 -39
- package/lib/binding.js +1 -1
- package/lib/colors.js +7 -0
- package/lib/commands.js +127 -17
- package/lib/developer.js +0 -0
- package/lib/devices.js +78 -74
- package/lib/exclude.js +30 -54
- package/lib/exposes.js +203 -246
- package/lib/groups.js +84 -29
- package/lib/localConfig.js +295 -0
- package/lib/ota.js +0 -0
- package/lib/statescontroller.js +410 -183
- package/lib/utils.js +1 -1
- package/lib/zbDeviceAvailability.js +15 -23
- package/lib/zbDeviceConfigure.js +0 -0
- package/lib/zbDeviceEvent.js +2 -13
- package/lib/zigbeecontroller.js +299 -207
- package/main.js +145 -56
- package/package.json +8 -7
package/lib/exclude.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
+
const devicedefinitions = require('./devices');
|
|
2
3
|
|
|
3
4
|
class Exclude {
|
|
4
5
|
constructor(adapter) {
|
|
@@ -9,6 +10,7 @@ class Exclude {
|
|
|
9
10
|
start(zbController, stController) {
|
|
10
11
|
this.zbController = zbController;
|
|
11
12
|
this.stController = stController;
|
|
13
|
+
this.localConfig = stController.localConfig;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
stop() {
|
|
@@ -52,6 +54,12 @@ class Exclude {
|
|
|
52
54
|
this.adapter.sendTo(obj.from, obj.command, exclude, obj.callback));
|
|
53
55
|
}
|
|
54
56
|
break;
|
|
57
|
+
case 'getExcludable':
|
|
58
|
+
if (obj && obj.message && typeof obj.message === 'object') {
|
|
59
|
+
this.getExcludable(exclude =>
|
|
60
|
+
this.adapter.sendTo(obj.from, obj.command, exclude, obj.callback));
|
|
61
|
+
}
|
|
62
|
+
break;
|
|
55
63
|
case 'delExclude':
|
|
56
64
|
if (obj && obj.message) {
|
|
57
65
|
this.delExclude(obj.from, obj.command, obj.message, err =>
|
|
@@ -74,7 +82,7 @@ class Exclude {
|
|
|
74
82
|
}
|
|
75
83
|
|
|
76
84
|
extractExcludeId(stateId) {
|
|
77
|
-
return stateId.replace(`${this.adapter.namespace}.
|
|
85
|
+
return stateId.replace(`${this.adapter.namespace}.info.legacy.`, '');
|
|
78
86
|
}
|
|
79
87
|
|
|
80
88
|
getExcludeName(devName, stateId) {
|
|
@@ -83,18 +91,10 @@ class Exclude {
|
|
|
83
91
|
|
|
84
92
|
async addExclude(from, command, params, callback) {
|
|
85
93
|
try {
|
|
86
|
-
this.
|
|
94
|
+
//this.warn('addExclude message: ' + JSON.stringify(params));
|
|
87
95
|
const exclude_mod = params.exclude_model.common.type;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
this.adapter.setObjectNotExists(stateId,
|
|
91
|
-
{
|
|
92
|
-
type: 'state',
|
|
93
|
-
common: {name: exclude_mod},
|
|
94
|
-
},
|
|
95
|
-
() => this.adapter.setState(stateId, exclude_mod, true, () =>
|
|
96
|
-
callback()),
|
|
97
|
-
);
|
|
96
|
+
this.localConfig.updateLocalOverride(exclude_mod, exclude_mod, 'legacy', exclude_mod, true);
|
|
97
|
+
callback({});
|
|
98
98
|
} catch (error) {
|
|
99
99
|
this.error(`Failed to addExclude ${error.stack}`);
|
|
100
100
|
throw new Error(`Failed to addExclude ${error.stack}`);
|
|
@@ -104,58 +104,34 @@ class Exclude {
|
|
|
104
104
|
async delExclude(from, command, exclude_id, callback) {
|
|
105
105
|
try {
|
|
106
106
|
this.debug(`delExclude message: ${JSON.stringify(exclude_id)}`);
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
.then(async (stateV) => {
|
|
110
|
-
this.debug(`found state: ${JSON.stringify(stateV)}`);
|
|
111
|
-
this.adapter.deleteState(null, 'exclude', exclude_id, async () =>
|
|
112
|
-
callback());
|
|
113
|
-
});
|
|
107
|
+
this.localConfig.updateLocalOverride(exclude_id, exclude_id, 'legacy', '', true);
|
|
108
|
+
callback({});
|
|
114
109
|
} catch (error) {
|
|
115
110
|
this.error(`Failed to delExclude ${error.stack}`);
|
|
116
111
|
throw new Error(`Failed to delExclude ${error.stack}`);
|
|
117
112
|
}
|
|
118
113
|
}
|
|
119
114
|
|
|
120
|
-
getExclude(callback) {
|
|
115
|
+
async getExclude(callback) {
|
|
121
116
|
try {
|
|
122
|
-
const exclude =
|
|
123
|
-
|
|
124
|
-
if (!err && states) {
|
|
125
|
-
const exc = [];
|
|
126
|
-
states.forEach(state => {
|
|
127
|
-
if (state._id.startsWith(`${this.adapter.namespace}.exclude`)) {
|
|
128
|
-
exc.push(new Promise(resolve =>
|
|
129
|
-
this.adapter.getStateAsync(state._id)
|
|
130
|
-
.then(stateVa => {
|
|
131
|
-
if (stateVa !== null) {
|
|
132
|
-
const val = {
|
|
133
|
-
id: this.extractExcludeId(state._id),
|
|
134
|
-
name: stateVa.val
|
|
135
|
-
};
|
|
136
|
-
if (this.extractExcludeId(state._id) !== 'all') {
|
|
137
|
-
exclude.push(val);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
resolve();
|
|
141
|
-
})));
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
return Promise.all(exc)
|
|
145
|
-
.then(() => {
|
|
146
|
-
const arrExclude = JSON.stringify(exclude);
|
|
147
|
-
this.debug(`getExclude result: ${arrExclude}`);
|
|
148
|
-
this.adapter.setState('exclude.all', arrExclude, true, () =>
|
|
149
|
-
callback(exclude));
|
|
150
|
-
});
|
|
151
|
-
} else {
|
|
152
|
-
this.debug(`getExclude result: ${JSON.stringify(exclude)}`);
|
|
153
|
-
callback(exclude);
|
|
154
|
-
}
|
|
155
|
-
});
|
|
117
|
+
const exclude = this.localConfig.getOverridesWithKey('legacy', true)
|
|
118
|
+
callback({legacy: exclude});
|
|
156
119
|
} catch (error) {
|
|
157
120
|
this.error(`Failed to getExclude ${error.stack}`);
|
|
121
|
+
callback({error: 'unable to get excludes' });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async getExcludable(callback) {
|
|
126
|
+
const devices = this.zbController.getClients();
|
|
127
|
+
const excludables = [];
|
|
128
|
+
for (const device of devices) {
|
|
129
|
+
const obj = await this.adapter.getObjectAsync(device.ieeeAddr.substr(2));
|
|
130
|
+
if (obj && obj.common && obj.common.type) {
|
|
131
|
+
excludables.push(obj.common.tyoe);
|
|
132
|
+
}
|
|
158
133
|
}
|
|
134
|
+
callback(devices.pairedLegacyDevices(excludables));
|
|
159
135
|
}
|
|
160
136
|
}
|
|
161
137
|
|
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
|
|
|
@@ -911,16 +874,14 @@ function createFromExposes(model, def) {
|
|
|
911
874
|
}
|
|
912
875
|
|
|
913
876
|
}
|
|
914
|
-
function applyExposes(mappedDevices, byModel
|
|
915
|
-
//
|
|
916
|
-
const allExcludesStr = JSON.stringify(allExcludesObj);
|
|
917
|
-
// create or update device from exposes
|
|
877
|
+
function applyExposes(mappedDevices, byModel) {
|
|
878
|
+
// create or device from exposes
|
|
918
879
|
for (const deviceDef of zigbeeHerdsmanConverters.definitions) {
|
|
919
|
-
applyDeviceDef(mappedDevices, byModel,
|
|
880
|
+
applyDeviceDef(mappedDevices, byModel, deviceDef);
|
|
920
881
|
|
|
921
882
|
if (deviceDef.hasOwnProperty('whiteLabel')) {
|
|
922
883
|
for (const deviceWhiteLabel of deviceDef.whiteLabel) {
|
|
923
|
-
applyDeviceDef(mappedDevices, byModel,
|
|
884
|
+
applyDeviceDef(mappedDevices, byModel, {
|
|
924
885
|
...deviceDef,
|
|
925
886
|
model: deviceWhiteLabel.model,
|
|
926
887
|
vendor: deviceWhiteLabel.vendor,
|
|
@@ -931,20 +892,16 @@ function applyExposes(mappedDevices, byModel, allExcludesObj) {
|
|
|
931
892
|
}
|
|
932
893
|
}
|
|
933
894
|
|
|
934
|
-
function applyDeviceDef(mappedDevices, byModel,
|
|
895
|
+
function applyDeviceDef(mappedDevices, byModel, deviceDef) {
|
|
935
896
|
const stripModel = utils.getModelRegEx(deviceDef.model);
|
|
936
897
|
const existsMap = byModel.get(stripModel);
|
|
937
898
|
|
|
938
|
-
if (
|
|
899
|
+
if (deviceDef.hasOwnProperty('exposes') && (!existsMap || !existsMap.hasOwnProperty('states'))) {
|
|
939
900
|
try {
|
|
940
901
|
const newDevice = createFromExposes(stripModel, deviceDef);
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
} else {
|
|
945
|
-
existsMap.states = newDevice.states;
|
|
946
|
-
existsMap.exposed = true;
|
|
947
|
-
}
|
|
902
|
+
mappedDevices.push(newDevice);
|
|
903
|
+
byModel.set(stripModel, newDevice);
|
|
904
|
+
|
|
948
905
|
} catch (e) {
|
|
949
906
|
console.log(`Wrong expose device definition ${deviceDef.vendor} ${stripModel}`);
|
|
950
907
|
}
|