iobroker.zigbee 1.5.5 → 1.6.6
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/.github/FUNDING.yml +1 -1
- package/.github/workflows/test-and-release.yml +10 -10
- package/LICENSE +1 -1
- package/README.md +40 -8
- package/admin/adapter-settings.js +39 -3
- package/admin/admin.js +23 -33
- package/admin/img/tuya_rb280.png +0 -0
- package/admin/index_m.html +84 -37
- package/admin/tab_m.html +95 -35
- package/admin/words.js +24 -24
- package/docs/tutorial/adm5_1.PNG +0 -0
- package/docs/tutorial/adm5_2.PNG +0 -0
- package/docs/tutorial/zigbee.png +0 -0
- package/io-package.json +27 -3
- package/lib/commands.js +2 -1
- package/lib/devices.js +10 -14
- package/lib/exposes.js +263 -104
- package/lib/states.js +86 -27
- package/lib/statescontroller.js +10 -2
- package/lib/utils.js +19 -0
- package/lib/zbBaseExtension.js +4 -0
- package/lib/zbDelayedAction.js +5 -0
- package/lib/zbDeviceAvailability.js +2 -0
- package/lib/zbDeviceConfigure.js +8 -5
- package/lib/zigbeecontroller.js +79 -31
- package/main.js +118 -43
- package/package.json +7 -6
package/lib/exposes.js
CHANGED
|
@@ -5,12 +5,15 @@ const statesDefs = require(__dirname + '/states.js').states;
|
|
|
5
5
|
const rgb = require(__dirname + '/rgb.js');
|
|
6
6
|
const utils = require(__dirname + '/utils.js');
|
|
7
7
|
const colors = require(__dirname + '/colors.js');
|
|
8
|
+
const ea = zigbeeHerdsmanConverters.exposes.access;
|
|
8
9
|
|
|
9
10
|
function genState(expose, role, name, desc) {
|
|
10
11
|
let state;
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
const
|
|
12
|
+
const readable = (expose.access & ea.STATE) > 0;
|
|
13
|
+
const writable = (expose.access & ea.SET) > 0;
|
|
14
|
+
const stname = (name || expose.property);
|
|
15
|
+
if (typeof stname !== 'string') return;
|
|
16
|
+
const stateId = stname.replace(/\*/g, '');
|
|
14
17
|
const stateName = (desc || expose.description || expose.name);
|
|
15
18
|
const propName = expose.property;
|
|
16
19
|
switch (expose.type) {
|
|
@@ -21,13 +24,20 @@ function genState(expose, role, name, desc) {
|
|
|
21
24
|
name: stateName,
|
|
22
25
|
icon: undefined,
|
|
23
26
|
role: role || 'state',
|
|
24
|
-
write:
|
|
27
|
+
write: writable,
|
|
25
28
|
read: true,
|
|
26
29
|
type: 'boolean',
|
|
27
|
-
getter: payload => (payload[propName] === (expose.value_on || 'ON')),
|
|
28
|
-
setter: (value) => (value) ? (expose.value_on || 'ON') : ((expose.value_off != undefined) ? expose.value_off : 'OFF'),
|
|
29
|
-
setattr: expose.name,
|
|
30
30
|
};
|
|
31
|
+
if (readable) {
|
|
32
|
+
state.getter = payload => (payload[propName] === (expose.value_on || 'ON'));
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
state.getter = payload => ( undefined);
|
|
36
|
+
}
|
|
37
|
+
if (writable) {
|
|
38
|
+
state.setter = (value) => (value) ? (expose.value_on || 'ON') : ((expose.value_off != undefined) ? expose.value_off : 'OFF');
|
|
39
|
+
state.setattr = expose.name;
|
|
40
|
+
}
|
|
31
41
|
if (expose.endpoint) {
|
|
32
42
|
state.epname = expose.endpoint;
|
|
33
43
|
}
|
|
@@ -40,7 +50,7 @@ function genState(expose, role, name, desc) {
|
|
|
40
50
|
name: stateName,
|
|
41
51
|
icon: undefined,
|
|
42
52
|
role: role || 'state',
|
|
43
|
-
write:
|
|
53
|
+
write: writable,
|
|
44
54
|
read: true,
|
|
45
55
|
type: 'number',
|
|
46
56
|
min: expose.value_min || 0,
|
|
@@ -59,13 +69,14 @@ function genState(expose, role, name, desc) {
|
|
|
59
69
|
name: stateName,
|
|
60
70
|
icon: undefined,
|
|
61
71
|
role: role || 'state',
|
|
62
|
-
write:
|
|
72
|
+
write: writable,
|
|
63
73
|
read: true,
|
|
64
74
|
type: 'string',
|
|
65
75
|
states: expose.values.map((item) => `${item}:${item}`).join(';'),
|
|
66
76
|
};
|
|
67
77
|
if (expose.endpoint) {
|
|
68
78
|
state.epname = expose.endpoint;
|
|
79
|
+
state.setattr = expose.name;
|
|
69
80
|
}
|
|
70
81
|
break;
|
|
71
82
|
|
|
@@ -76,7 +87,7 @@ function genState(expose, role, name, desc) {
|
|
|
76
87
|
name: stateName,
|
|
77
88
|
icon: undefined,
|
|
78
89
|
role: role || 'state',
|
|
79
|
-
write:
|
|
90
|
+
write: writable,
|
|
80
91
|
read: true,
|
|
81
92
|
type: 'string',
|
|
82
93
|
};
|
|
@@ -92,9 +103,119 @@ function genState(expose, role, name, desc) {
|
|
|
92
103
|
return state;
|
|
93
104
|
}
|
|
94
105
|
|
|
106
|
+
|
|
107
|
+
|
|
95
108
|
function createFromExposes(model, def) {
|
|
96
109
|
const states = [];
|
|
97
|
-
|
|
110
|
+
// make the different (set and get) part of state is updatable if different exposes is used for get and set
|
|
111
|
+
// as example:
|
|
112
|
+
// ...
|
|
113
|
+
// exposes.binary('some_option', ea.STATE, true, false).withDescription('Some Option'),
|
|
114
|
+
// exposes.composite('options', 'options')
|
|
115
|
+
// .withDescription('Some composite Options')
|
|
116
|
+
// .withFeature(exposes.binary('some_option', ea.SET, true, false).withDescription('Some Option'))
|
|
117
|
+
//in this case one state - `some_option` has two different exposes for set an get, we have to combine it ...
|
|
118
|
+
//
|
|
119
|
+
function pushToStates(state, access) {
|
|
120
|
+
if (state === undefined) {
|
|
121
|
+
return 0;
|
|
122
|
+
}
|
|
123
|
+
state.readable = (access & ea.STATE) > 0;
|
|
124
|
+
state.writable = (access & ea.SET) > 0;
|
|
125
|
+
const stateExists = states.findIndex( (element, index, array) => (element.id === state.id ));
|
|
126
|
+
if (stateExists < 0 ) {
|
|
127
|
+
state.write = state.writable;
|
|
128
|
+
if (! state.writable) {
|
|
129
|
+
if ( state.hasOwnProperty('setter') ) {
|
|
130
|
+
delete state.setter;
|
|
131
|
+
}
|
|
132
|
+
if ( state.hasOwnProperty('setattr') ) {
|
|
133
|
+
delete state.setattr;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (! state.readable) {
|
|
137
|
+
if (state.hasOwnProperty('getter') ) {
|
|
138
|
+
//to awid some worning on unprocessed data
|
|
139
|
+
state.getter = payload => ( undefined );
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return states.push(state);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
if ( (state.readable) && (! states[stateExists].readable ) ) {
|
|
146
|
+
states[stateExists].read = state.read;
|
|
147
|
+
// as state is readable, it can't be button or event
|
|
148
|
+
if (states[stateExists].role === 'button') {
|
|
149
|
+
states[stateExists].role = state.role;
|
|
150
|
+
}
|
|
151
|
+
if (states[stateExists].hasOwnProperty('isEvent')) {
|
|
152
|
+
delete states[stateExists].isEvent;
|
|
153
|
+
}
|
|
154
|
+
// we have to use the getter from "new" state
|
|
155
|
+
if ( state.hasOwnProperty('getter') ) {
|
|
156
|
+
states[stateExists].getter = state.getter;
|
|
157
|
+
}
|
|
158
|
+
// trying to remove the `prop` property, as main key for get and set,
|
|
159
|
+
// as it can be different in new and old states, and leave only:
|
|
160
|
+
// setattr for old and id for new
|
|
161
|
+
if (( state.hasOwnProperty('prop') ) && (state.prop === state.id)) {
|
|
162
|
+
if ( states[stateExists].hasOwnProperty('prop') ) {
|
|
163
|
+
if (states[stateExists].prop !== states[stateExists].id) {
|
|
164
|
+
if (! states[stateExists].hasOwnProperty('setattr')) {
|
|
165
|
+
states[stateExists].setattr = states[stateExists].prop;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
delete states[stateExists].prop;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else if ( state.hasOwnProperty('prop') ) {
|
|
172
|
+
states[stateExists].prop = state.prop;
|
|
173
|
+
}
|
|
174
|
+
states[stateExists].readable = true;
|
|
175
|
+
}
|
|
176
|
+
if ( (state.writable) && (! states[stateExists].writable ) ) {
|
|
177
|
+
states[stateExists].write = state.writable;
|
|
178
|
+
// use new state `setter`
|
|
179
|
+
if ( state.hasOwnProperty('setter') ) {
|
|
180
|
+
states[stateExists].setter = state.setter;
|
|
181
|
+
}
|
|
182
|
+
// use new state `setterOpt`
|
|
183
|
+
if ( state.hasOwnProperty('setterOpt') ) {
|
|
184
|
+
states[stateExists].setterOpt = state.setterOpt;
|
|
185
|
+
}
|
|
186
|
+
// use new state `inOptions`
|
|
187
|
+
if ( state.hasOwnProperty('inOptions') ) {
|
|
188
|
+
states[stateExists].inOptions = state.inOptions;
|
|
189
|
+
}
|
|
190
|
+
// as we have new state, responsible for set, we have to use new `isOption`
|
|
191
|
+
// or remove it
|
|
192
|
+
if (((! state.hasOwnProperty('isOption') ) || (state.isOptions === false))
|
|
193
|
+
&& (states[stateExists].hasOwnProperty('isOption'))) {
|
|
194
|
+
delete states[stateExists].isOption;
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
states[stateExists].isOption = state.isOption;
|
|
198
|
+
}
|
|
199
|
+
// use new `setattr` or `prop` as `setattr`
|
|
200
|
+
if ( state.hasOwnProperty('setattr') ) {
|
|
201
|
+
states[stateExists].setattr = state.setattr;
|
|
202
|
+
}
|
|
203
|
+
else if ( state.hasOwnProperty('prop') ) {
|
|
204
|
+
states[stateExists].setattr = state.prop;
|
|
205
|
+
}
|
|
206
|
+
// remove `prop` equal to if, due to prop is uses as key in set and get
|
|
207
|
+
if ( states[stateExists].prop === states[stateExists].id) {
|
|
208
|
+
delete states[stateExists].prop;
|
|
209
|
+
}
|
|
210
|
+
if ( state.hasOwnProperty('epname') ) {
|
|
211
|
+
states[stateExists].epname = state.epname;
|
|
212
|
+
}
|
|
213
|
+
states[stateExists].writable = true;
|
|
214
|
+
}
|
|
215
|
+
return states.length;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const icon = utils.getDeviceIcon(def);
|
|
98
219
|
for (const expose of def.exposes) {
|
|
99
220
|
let state;
|
|
100
221
|
|
|
@@ -104,7 +225,7 @@ function createFromExposes(model, def) {
|
|
|
104
225
|
switch (prop.name) {
|
|
105
226
|
case 'state': {
|
|
106
227
|
const stateNameS = expose.endpoint ? `state_${expose.endpoint}` : 'state';
|
|
107
|
-
|
|
228
|
+
pushToStates({
|
|
108
229
|
id: stateNameS,
|
|
109
230
|
name: `Switch state ${expose.endpoint ? expose.endpoint : ''}`.trim(),
|
|
110
231
|
icon: undefined,
|
|
@@ -116,12 +237,12 @@ function createFromExposes(model, def) {
|
|
|
116
237
|
setter: (value) => (value) ? prop.value_on || 'ON' : ((prop.value_off != undefined) ? prop.value_off : 'OFF'),
|
|
117
238
|
epname: expose.endpoint,
|
|
118
239
|
setattr: 'state',
|
|
119
|
-
});
|
|
240
|
+
}, prop.access);
|
|
120
241
|
break;
|
|
121
242
|
}
|
|
122
243
|
case 'brightness': {
|
|
123
244
|
const stateNameB = expose.endpoint ? `brightness_${expose.endpoint}` : 'brightness';
|
|
124
|
-
|
|
245
|
+
pushToStates({
|
|
125
246
|
id: stateNameB,
|
|
126
247
|
name: `Brightness ${expose.endpoint ? expose.endpoint : ''}`.trim(),
|
|
127
248
|
icon: undefined,
|
|
@@ -153,13 +274,13 @@ function createFromExposes(model, def) {
|
|
|
153
274
|
},
|
|
154
275
|
epname: expose.endpoint,
|
|
155
276
|
setattr: 'brightness',
|
|
156
|
-
});
|
|
157
|
-
|
|
277
|
+
}, prop.access);
|
|
278
|
+
pushToStates(statesDefs.brightness_move, prop.access);
|
|
158
279
|
break;
|
|
159
280
|
}
|
|
160
281
|
case 'color_temp': {
|
|
161
282
|
const stateNameT = expose.endpoint ? `colortemp_${expose.endpoint}` : 'colortemp';
|
|
162
|
-
|
|
283
|
+
pushToStates({
|
|
163
284
|
id: stateNameT,
|
|
164
285
|
prop: expose.endpoint ? `color_temp_${expose.endpoint}` : 'color_temp',
|
|
165
286
|
name: `Color temperature ${expose.endpoint ? expose.endpoint : ''}`.trim(),
|
|
@@ -179,13 +300,13 @@ function createFromExposes(model, def) {
|
|
|
179
300
|
return {...options, transition: transitionTime};
|
|
180
301
|
},
|
|
181
302
|
epname: expose.endpoint,
|
|
182
|
-
});
|
|
183
|
-
|
|
303
|
+
}, prop.access);
|
|
304
|
+
pushToStates(statesDefs.colortemp_move, prop.access);
|
|
184
305
|
break;
|
|
185
306
|
}
|
|
186
307
|
case 'color_xy': {
|
|
187
308
|
const stateNameC = expose.endpoint ? `color_${expose.endpoint}` : 'color';
|
|
188
|
-
|
|
309
|
+
pushToStates({
|
|
189
310
|
id: stateNameC,
|
|
190
311
|
prop: expose.endpoint ? `color_${expose.endpoint}` : 'color',
|
|
191
312
|
name: `Color ${expose.endpoint ? expose.endpoint : ''}`.trim(),
|
|
@@ -235,12 +356,12 @@ function createFromExposes(model, def) {
|
|
|
235
356
|
},
|
|
236
357
|
epname: expose.endpoint,
|
|
237
358
|
setattr: 'color',
|
|
238
|
-
});
|
|
359
|
+
}, prop.access);
|
|
239
360
|
break;
|
|
240
361
|
}
|
|
241
362
|
case 'color_hs': {
|
|
242
363
|
const stateNameH = expose.endpoint ? `color_${expose.endpoint}` : 'color';
|
|
243
|
-
|
|
364
|
+
pushToStates({
|
|
244
365
|
id: stateNameH,
|
|
245
366
|
prop: expose.endpoint ? `color_${expose.endpoint}` : 'color',
|
|
246
367
|
name: `Color ${expose.endpoint ? expose.endpoint : ''}`.trim(),
|
|
@@ -266,8 +387,8 @@ function createFromExposes(model, def) {
|
|
|
266
387
|
},
|
|
267
388
|
epname: expose.endpoint,
|
|
268
389
|
setattr: 'color',
|
|
269
|
-
});
|
|
270
|
-
|
|
390
|
+
}, prop.access);
|
|
391
|
+
pushToStates({
|
|
271
392
|
id: expose.endpoint ? `hue_${expose.endpoint}` : 'hue',
|
|
272
393
|
prop: expose.endpoint ? `color_${expose.endpoint}` : 'color',
|
|
273
394
|
name: `Hue ${expose.endpoint ? expose.endpoint : ''}`.trim(),
|
|
@@ -306,8 +427,8 @@ function createFromExposes(model, def) {
|
|
|
306
427
|
return {...options, transition: transitionTime};
|
|
307
428
|
},
|
|
308
429
|
|
|
309
|
-
});
|
|
310
|
-
|
|
430
|
+
}, prop.access);
|
|
431
|
+
pushToStates({
|
|
311
432
|
id: expose.endpoint ? `saturation_${expose.endpoint}` : 'saturation',
|
|
312
433
|
prop: expose.endpoint ? `color_${expose.endpoint}` : 'color',
|
|
313
434
|
name: `Saturation ${expose.endpoint ? expose.endpoint : ''}`.trim(),
|
|
@@ -346,10 +467,10 @@ function createFromExposes(model, def) {
|
|
|
346
467
|
return {...options, transition: transitionTime};
|
|
347
468
|
},
|
|
348
469
|
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
470
|
+
}, prop.access);
|
|
471
|
+
pushToStates(statesDefs.hue_move, prop.access);
|
|
472
|
+
pushToStates(statesDefs.saturation_move, prop.access);
|
|
473
|
+
pushToStates({
|
|
353
474
|
id: 'hue_calibration',
|
|
354
475
|
prop: 'color',
|
|
355
476
|
name: 'Hue color calibration table',
|
|
@@ -386,73 +507,77 @@ function createFromExposes(model, def) {
|
|
|
386
507
|
return {...options, transition: transitionTime};
|
|
387
508
|
},
|
|
388
509
|
|
|
389
|
-
});
|
|
510
|
+
}, prop.access);
|
|
390
511
|
break;
|
|
391
512
|
}
|
|
392
513
|
default:
|
|
393
|
-
|
|
514
|
+
pushToStates(genState(prop), prop.access);
|
|
394
515
|
break;
|
|
395
516
|
}
|
|
396
517
|
}
|
|
397
|
-
|
|
518
|
+
pushToStates(statesDefs.transition_time, ea.STATE_SET);
|
|
398
519
|
break;
|
|
399
520
|
|
|
400
521
|
case 'switch':
|
|
401
522
|
for (const prop of expose.features) {
|
|
402
523
|
switch (prop.name) {
|
|
403
524
|
case 'state':
|
|
404
|
-
|
|
525
|
+
pushToStates(genState(prop, 'switch'), prop.access);
|
|
405
526
|
break;
|
|
406
527
|
default:
|
|
407
|
-
|
|
528
|
+
pushToStates(genState(prop), prop.access);
|
|
408
529
|
break;
|
|
409
530
|
}
|
|
410
531
|
}
|
|
411
532
|
break;
|
|
412
533
|
|
|
413
534
|
case 'numeric':
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
535
|
+
if (expose.endpoint) {
|
|
536
|
+
state = genState(expose);
|
|
537
|
+
} else {
|
|
538
|
+
switch (expose.name) {
|
|
539
|
+
case 'linkquality':
|
|
540
|
+
state = undefined;
|
|
541
|
+
break;
|
|
418
542
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
543
|
+
case 'battery':
|
|
544
|
+
state = statesDefs.battery;
|
|
545
|
+
break;
|
|
422
546
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
547
|
+
case 'voltage':
|
|
548
|
+
state = statesDefs.plug_voltage;
|
|
549
|
+
break;
|
|
426
550
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
551
|
+
case 'temperature':
|
|
552
|
+
state = statesDefs.temperature;
|
|
553
|
+
break;
|
|
430
554
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
555
|
+
case 'humidity':
|
|
556
|
+
state = statesDefs.humidity;
|
|
557
|
+
break;
|
|
434
558
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
559
|
+
case 'pressure':
|
|
560
|
+
state = statesDefs.pressure;
|
|
561
|
+
break;
|
|
438
562
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
563
|
+
case 'illuminance':
|
|
564
|
+
state = statesDefs.illuminance_raw;
|
|
565
|
+
break;
|
|
442
566
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
567
|
+
case 'illuminance_lux':
|
|
568
|
+
state = statesDefs.illuminance;
|
|
569
|
+
break;
|
|
446
570
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
571
|
+
case 'power':
|
|
572
|
+
state = statesDefs.load_power;
|
|
573
|
+
break;
|
|
450
574
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
575
|
+
default:
|
|
576
|
+
state = genState(expose);
|
|
577
|
+
break;
|
|
578
|
+
}
|
|
454
579
|
}
|
|
455
|
-
if (state)
|
|
580
|
+
if (state) pushToStates(state, expose.access);
|
|
456
581
|
break;
|
|
457
582
|
|
|
458
583
|
case 'enum':
|
|
@@ -492,7 +617,7 @@ function createFromExposes(model, def) {
|
|
|
492
617
|
isEvent: true,
|
|
493
618
|
};
|
|
494
619
|
}
|
|
495
|
-
|
|
620
|
+
pushToStates(state, expose.access);
|
|
496
621
|
}
|
|
497
622
|
state = null;
|
|
498
623
|
break;
|
|
@@ -501,46 +626,50 @@ function createFromExposes(model, def) {
|
|
|
501
626
|
state = genState(expose);
|
|
502
627
|
break;
|
|
503
628
|
}
|
|
504
|
-
if (state)
|
|
629
|
+
if (state) pushToStates(state, expose.access);
|
|
505
630
|
break;
|
|
506
631
|
|
|
507
632
|
case 'binary':
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
633
|
+
if (expose.endpoint) {
|
|
634
|
+
state = genState(expose);
|
|
635
|
+
} else {
|
|
636
|
+
switch (expose.name) {
|
|
637
|
+
case 'contact':
|
|
638
|
+
state = statesDefs.contact;
|
|
639
|
+
pushToStates(statesDefs.opened, ea.STATE);
|
|
640
|
+
break;
|
|
513
641
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
642
|
+
case 'battery_low':
|
|
643
|
+
state = statesDefs.heiman_batt_low;
|
|
644
|
+
break;
|
|
517
645
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
646
|
+
case 'tamper':
|
|
647
|
+
state = statesDefs.tamper;
|
|
648
|
+
break;
|
|
521
649
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
650
|
+
case 'water_leak':
|
|
651
|
+
state = statesDefs.water_detected;
|
|
652
|
+
break;
|
|
525
653
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
654
|
+
case 'lock':
|
|
655
|
+
state = statesDefs.child_lock;
|
|
656
|
+
break;
|
|
529
657
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
658
|
+
case 'occupancy':
|
|
659
|
+
state = statesDefs.occupancy;
|
|
660
|
+
break;
|
|
533
661
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
662
|
+
default:
|
|
663
|
+
state = genState(expose);
|
|
664
|
+
break;
|
|
665
|
+
}
|
|
537
666
|
}
|
|
538
|
-
if (state)
|
|
667
|
+
if (state) pushToStates(state, expose.access);
|
|
539
668
|
break;
|
|
540
669
|
|
|
541
670
|
case 'text':
|
|
542
671
|
state = genState(expose);
|
|
543
|
-
|
|
672
|
+
pushToStates(state, expose.access);
|
|
544
673
|
break;
|
|
545
674
|
|
|
546
675
|
case 'lock':
|
|
@@ -549,10 +678,10 @@ function createFromExposes(model, def) {
|
|
|
549
678
|
for (const prop of expose.features) {
|
|
550
679
|
switch (prop.name) {
|
|
551
680
|
case 'state':
|
|
552
|
-
|
|
681
|
+
pushToStates(genState(prop, 'switch'), prop.access);
|
|
553
682
|
break;
|
|
554
683
|
default:
|
|
555
|
-
|
|
684
|
+
pushToStates(genState(prop), prop.access);
|
|
556
685
|
break;
|
|
557
686
|
}
|
|
558
687
|
}
|
|
@@ -562,16 +691,16 @@ function createFromExposes(model, def) {
|
|
|
562
691
|
for (const prop of expose.features) {
|
|
563
692
|
switch (prop.name) {
|
|
564
693
|
case 'away_mode':
|
|
565
|
-
|
|
694
|
+
pushToStates(statesDefs.climate_away_mode, prop.access);
|
|
566
695
|
break;
|
|
567
696
|
case 'system_mode':
|
|
568
|
-
|
|
697
|
+
pushToStates(statesDefs.climate_system_mode, prop.access);
|
|
569
698
|
break;
|
|
570
699
|
case 'running_mode':
|
|
571
|
-
|
|
700
|
+
pushToStates(statesDefs.climate_running_mode, prop.access);
|
|
572
701
|
break;
|
|
573
702
|
default:
|
|
574
|
-
|
|
703
|
+
pushToStates(genState(prop), prop.access);
|
|
575
704
|
break;
|
|
576
705
|
}
|
|
577
706
|
}
|
|
@@ -580,14 +709,41 @@ function createFromExposes(model, def) {
|
|
|
580
709
|
case 'composite':
|
|
581
710
|
for (const prop of expose.features) {
|
|
582
711
|
const st = genState(prop);
|
|
712
|
+
st.prop = expose.property;
|
|
583
713
|
st.inOptions = true;
|
|
714
|
+
// I'm not fully sure, as it really needed, but
|
|
584
715
|
st.setterOpt = (value, options) => {
|
|
585
716
|
const result = {};
|
|
586
|
-
|
|
717
|
+
options[prop.property] = value;
|
|
718
|
+
result[expose.property] = options;
|
|
587
719
|
return result;
|
|
588
720
|
};
|
|
589
|
-
|
|
590
|
-
|
|
721
|
+
// if we have a composite expose, the value have to be an object {expose.property : {prop.property: value}}
|
|
722
|
+
if (prop.access & ea.SET) {
|
|
723
|
+
st.setter = (value, options) => {
|
|
724
|
+
const result = {};
|
|
725
|
+
options[prop.property] = value;
|
|
726
|
+
result[expose.property] = options;
|
|
727
|
+
return result;
|
|
728
|
+
};
|
|
729
|
+
st.setattr = expose.property;
|
|
730
|
+
};
|
|
731
|
+
// if we have a composite expose, the payload will be an object {expose.property : {prop.property: value}}
|
|
732
|
+
if (prop.access & ea.STATE) {
|
|
733
|
+
st.getter = payload => {
|
|
734
|
+
if ( (payload.hasOwnProperty(expose.property)) && (payload[expose.property] !== null) && payload[expose.property].hasOwnProperty(prop.property)) {
|
|
735
|
+
return !isNaN(payload[expose.property][prop.property]) ? payload[expose.property][prop.property] : undefined
|
|
736
|
+
}
|
|
737
|
+
else {
|
|
738
|
+
return undefined;
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
else {
|
|
743
|
+
st.getter = payload => { return undefined };
|
|
744
|
+
}
|
|
745
|
+
;
|
|
746
|
+
pushToStates(st, prop.access);
|
|
591
747
|
}
|
|
592
748
|
break;
|
|
593
749
|
default:
|
|
@@ -600,7 +756,9 @@ function createFromExposes(model, def) {
|
|
|
600
756
|
states: states,
|
|
601
757
|
exposed: true,
|
|
602
758
|
};
|
|
603
|
-
//
|
|
759
|
+
// make the function code printable in log
|
|
760
|
+
//console.log(`Created mapping for device ${model}: ${JSON.stringify(newDev, function(key, value) {
|
|
761
|
+
// if (typeof value === 'function') {return value.toString() } else { return value } }, ' ')}`);
|
|
604
762
|
return newDev;
|
|
605
763
|
}
|
|
606
764
|
|
|
@@ -612,6 +770,7 @@ function applyExposes(mappedDevices, byModel, allExcludesObj) {
|
|
|
612
770
|
const strippedModel = (deviceDef.model) ? deviceDef.model.replace(/\0.*$/g, '').trim() : '';
|
|
613
771
|
// check if device is mapped
|
|
614
772
|
const existsMap = byModel.get(strippedModel);
|
|
773
|
+
|
|
615
774
|
if ((deviceDef.hasOwnProperty('exposes') && (!existsMap || !existsMap.hasOwnProperty('states'))) || allExcludesStr.indexOf(strippedModel) > 0) {
|
|
616
775
|
const newDevice = createFromExposes(strippedModel, deviceDef);
|
|
617
776
|
if (!existsMap) {
|