iobroker.zigbee 3.3.1-alpha.0 → 3.3.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 +43 -50
- package/admin/admin.js +39 -7
- package/admin/tab_m.html +19 -0
- package/io-package.json +13 -13
- package/lib/colors.js +182 -421
- package/lib/exposes.js +206 -198
- package/lib/groups.js +2 -5
- package/lib/legacy/devices.js +8 -5
- package/lib/models.js +99 -11
- package/lib/statescontroller.js +125 -133
- package/lib/zigbeecontroller.js +42 -9
- package/main.js +15 -20
- package/package.json +5 -5
package/lib/exposes.js
CHANGED
|
@@ -6,12 +6,16 @@ const utils = require('./utils');
|
|
|
6
6
|
const colors = require('./colors');
|
|
7
7
|
const ea = require('zigbee-herdsman-converters/lib/exposes').access;
|
|
8
8
|
|
|
9
|
-
const LocalData = { options:{} };
|
|
9
|
+
//const LocalData = { options:{ newCompositeMethod: true } };
|
|
10
|
+
//const LocalData = { options:{ } };
|
|
10
11
|
const __logger = undefined;
|
|
11
12
|
|
|
12
|
-
function genState(expose,
|
|
13
|
+
function genState(expose, overrides) {
|
|
14
|
+
const role = overrides?.role;
|
|
15
|
+
const name = overrides?.name;
|
|
16
|
+
const desc = overrides?.desc;
|
|
13
17
|
let state;
|
|
14
|
-
const readable = (expose.access & ea.
|
|
18
|
+
const readable = (expose.access & (ea.STATE_GET)) > 0;
|
|
15
19
|
const writable = (expose.access & ea.SET) > 0;
|
|
16
20
|
const stname = (name || expose.property);
|
|
17
21
|
if (typeof stname !== 'string') return;
|
|
@@ -26,10 +30,10 @@ function genState(expose, role, name, desc) {
|
|
|
26
30
|
prop: propName,
|
|
27
31
|
name: stateName,
|
|
28
32
|
icon: undefined,
|
|
29
|
-
role: role || 'state',
|
|
33
|
+
role: role || (readable ? 'state' : 'button'),
|
|
30
34
|
write: writable,
|
|
31
|
-
read:
|
|
32
|
-
type:
|
|
35
|
+
read: readable,
|
|
36
|
+
type: `boolean`,
|
|
33
37
|
};
|
|
34
38
|
if (readable) {
|
|
35
39
|
state.getter = payload => payload[propName] === (typeof expose.value_on == 'boolean' ? expose.value_on : expose.value_on || 'ON');
|
|
@@ -55,7 +59,7 @@ function genState(expose, role, name, desc) {
|
|
|
55
59
|
write: writable,
|
|
56
60
|
read: true,
|
|
57
61
|
type: 'number',
|
|
58
|
-
min: expose.value_min
|
|
62
|
+
min: expose.value_min,
|
|
59
63
|
max: expose.value_max,
|
|
60
64
|
unit: expose.unit,
|
|
61
65
|
};
|
|
@@ -130,13 +134,180 @@ function genState(expose, role, name, desc) {
|
|
|
130
134
|
break;
|
|
131
135
|
|
|
132
136
|
default:
|
|
133
|
-
|
|
137
|
+
return state;
|
|
138
|
+
}
|
|
139
|
+
if (overrides?.fromComposite) {
|
|
140
|
+
if (overrides?.channelID) {
|
|
141
|
+
state.compositeKey = overrides.channelID;
|
|
142
|
+
state.compositeTimeout = 250;
|
|
143
|
+
state.compositeState = overrides.channelID;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
state.inOptions = true;
|
|
147
|
+
state.setterOpt = (value, options) => {
|
|
148
|
+
const result = {};
|
|
149
|
+
options[expose.property] = value;
|
|
150
|
+
result[expose.property] = options;
|
|
151
|
+
return result;
|
|
152
|
+
};
|
|
153
|
+
if (expose.access & ea.SET) {
|
|
154
|
+
state.setter = (value, options) => {
|
|
155
|
+
const result = {};
|
|
156
|
+
options[expose.property] = value;
|
|
157
|
+
result[expose.property] = options;
|
|
158
|
+
return result;
|
|
159
|
+
};
|
|
160
|
+
state.setattr = expose.property;
|
|
161
|
+
};
|
|
162
|
+
if (expose.access & ea.STATE) {
|
|
163
|
+
expose.getter = payload => {
|
|
164
|
+
if ((payload.hasOwnProperty(expose.property)) && (payload[expose.property] !== null) && payload[expose.property].hasOwnProperty(expose.property)) {
|
|
165
|
+
return !isNaN(payload[expose.property][expose.property]) ? payload[expose.property][expose.property] : undefined;
|
|
166
|
+
} else {
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
} else {
|
|
171
|
+
expose.getter = payload => undefined;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
134
174
|
}
|
|
135
175
|
|
|
176
|
+
|
|
136
177
|
return state;
|
|
137
178
|
}
|
|
138
179
|
|
|
139
|
-
|
|
180
|
+
|
|
181
|
+
function generateCompositeStates(expose, _channelID, options)
|
|
182
|
+
{
|
|
183
|
+
const states = [];
|
|
184
|
+
const stname = `${_channelID}${expose.property}`;
|
|
185
|
+
const stateId = stname.replace(/\*/g, '');
|
|
186
|
+
const stateName = (expose.description || expose.name);
|
|
187
|
+
const channelID = options.newCompositeMethod ? `c_${stateId}.` : '';
|
|
188
|
+
if (options.newCompositeMethod) {
|
|
189
|
+
|
|
190
|
+
if (typeof stname !== 'string') return undefined;;
|
|
191
|
+
states.push({
|
|
192
|
+
id: stateId,
|
|
193
|
+
name: stateName,
|
|
194
|
+
icon: undefined,
|
|
195
|
+
role: 'state',
|
|
196
|
+
write: Boolean (expose.access & ea.SET),
|
|
197
|
+
read: Boolean(expose.access & ea.GET),
|
|
198
|
+
type: 'string',
|
|
199
|
+
getter: (value) => { return (typeof value === 'object' ? JSON.stringify(value) : value) },
|
|
200
|
+
setter: (value) => {
|
|
201
|
+
try {
|
|
202
|
+
return JSON.parse(value);
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
return { error: error.message }
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
}
|
|
211
|
+
for (const prop of expose.features) {
|
|
212
|
+
if (prop.type === 'composite' || prop.type === 'list') {
|
|
213
|
+
states.push(...generateCompositeStates(prop, channelID, options))
|
|
214
|
+
}
|
|
215
|
+
else
|
|
216
|
+
states.push(genState(prop, options.newCompositeMethod ? { fromComposite: true, name: `${channelID}${prop.name}` , channelID: stateId }: { fromComposite: true }))
|
|
217
|
+
/*
|
|
218
|
+
switch (prop.type) {
|
|
219
|
+
case 'numeric':
|
|
220
|
+
case 'text':
|
|
221
|
+
case 'binary':
|
|
222
|
+
{
|
|
223
|
+
const st = genState(prop, 'state', `${channelID}${prop.name}`);
|
|
224
|
+
st.prop = expose.property;
|
|
225
|
+
st.inOptions = !LocalData.options.newCompositeMethod;
|
|
226
|
+
if (LocalData.options.newCompositeMethod) {
|
|
227
|
+
st.compositeKey = stateId;
|
|
228
|
+
st.compositeTimeout = 250;
|
|
229
|
+
st.compositeState = stateId;
|
|
230
|
+
} else
|
|
231
|
+
{
|
|
232
|
+
// if we have a composite expose, the value have to be an object {expose.property : {prop.property: value}}
|
|
233
|
+
// I'm not fully sure, as it really needed, but
|
|
234
|
+
st.setterOpt = (value, options) => {
|
|
235
|
+
const result = {};
|
|
236
|
+
options[prop.property] = value;
|
|
237
|
+
result[expose.property] = options;
|
|
238
|
+
return result;
|
|
239
|
+
};
|
|
240
|
+
if (prop.access & ea.SET) {
|
|
241
|
+
st.setter = (value, options) => {
|
|
242
|
+
const result = {};
|
|
243
|
+
options[prop.property] = value;
|
|
244
|
+
result[expose.property] = options;
|
|
245
|
+
return result;
|
|
246
|
+
};
|
|
247
|
+
st.setattr = expose.property;
|
|
248
|
+
}
|
|
249
|
+
// if we have a composite expose, the payload will be an object {expose.property : {prop.property: value}}
|
|
250
|
+
if (prop.access & ea.STATE) {
|
|
251
|
+
st.getter = payload => {
|
|
252
|
+
if ((payload.hasOwnProperty(expose.property)) && (payload[expose.property] !== null) && payload[expose.property].hasOwnProperty(prop.property)) {
|
|
253
|
+
return !isNaN(payload[expose.property][prop.property]) ? payload[expose.property][prop.property] : undefined;
|
|
254
|
+
} else {
|
|
255
|
+
return undefined;
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
} else {
|
|
259
|
+
st.getter = payload => undefined;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
pushToStates(st, prop.access);
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
case 'list': {
|
|
266
|
+
for (const propList of prop.item_type.features) {
|
|
267
|
+
const st = genState(propList);
|
|
268
|
+
st.prop = expose.property;
|
|
269
|
+
st.inOptions = true;
|
|
270
|
+
st.setterOpt = (value, options) => {
|
|
271
|
+
const result = {};
|
|
272
|
+
options[propList.property] = value;
|
|
273
|
+
result[expose.property] = options;
|
|
274
|
+
return result;
|
|
275
|
+
};
|
|
276
|
+
if (propList.access & ea.SET) {
|
|
277
|
+
st.setter = (value, options) => {
|
|
278
|
+
const result = {};
|
|
279
|
+
options[propList.property] = value;
|
|
280
|
+
result[expose.property] = options;
|
|
281
|
+
return result;
|
|
282
|
+
};
|
|
283
|
+
st.setattr = expose.property;
|
|
284
|
+
}
|
|
285
|
+
if (propList.access & ea.STATE) {
|
|
286
|
+
st.getter = payload => {
|
|
287
|
+
if ((payload.hasOwnProperty(expose.property)) && (payload[expose.property] !== null) && payload[expose.property].hasOwnProperty(propList.property)) {
|
|
288
|
+
return !isNaN(payload[expose.property][propList.property]) ? payload[expose.property][propList.property] : undefined;
|
|
289
|
+
} else {
|
|
290
|
+
return undefined;
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
} else {
|
|
294
|
+
st.getter = payload => undefined;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
st.id = st.prop + '_' + st.id;
|
|
298
|
+
pushToStates(st, propList.access);
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
}
|
|
304
|
+
*/
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return states;
|
|
308
|
+
|
|
309
|
+
}
|
|
310
|
+
function createFromExposes(model, def, device, options, log) {
|
|
140
311
|
const { getStateDefinition } = require('./models');
|
|
141
312
|
const states = [];
|
|
142
313
|
// make the different (set and get) part of state is updatable if different exposes is used for get and set
|
|
@@ -298,7 +469,7 @@ function createFromExposes(model, def, device, log) {
|
|
|
298
469
|
function definitionHasTZHandler(definition, id) {
|
|
299
470
|
const tz = definition?.herdsmanModel?.toZigbee;
|
|
300
471
|
for (const converter of tz) {
|
|
301
|
-
if (converter
|
|
472
|
+
if (converter?.key?.includes(id))
|
|
302
473
|
return true;
|
|
303
474
|
}
|
|
304
475
|
return false;
|
|
@@ -521,26 +692,8 @@ function createFromExposes(model, def, device, log) {
|
|
|
521
692
|
read: true,
|
|
522
693
|
type: 'string',
|
|
523
694
|
setter: value => {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
const colorJSON = JSON.parse(value.replaceAll("'",'"'));
|
|
527
|
-
const numProp = Object.keys(colorJSON).length;
|
|
528
|
-
if (hasMultipleProperties(colorJSON, ['hsb'], numProp)) return colorJSON;
|
|
529
|
-
if (hasMultipleProperties(colorJSON, ['hsl'], numProp)) return colorJSON;
|
|
530
|
-
if (hasMultipleProperties(colorJSON, ['hsv'], numProp)) return colorJSON;
|
|
531
|
-
if (hasMultipleProperties(colorJSON, ['h','s','b'], numProp)) return colorJSON;
|
|
532
|
-
if (hasMultipleProperties(colorJSON, ['h','s','v'], numProp)) return colorJSON;
|
|
533
|
-
if (hasMultipleProperties(colorJSON, ['h','s','l'], numProp)) return colorJSON;
|
|
534
|
-
if (hasMultipleProperties(colorJSON, ['hue', 'saturation'], numProp)) return colorJSON;
|
|
535
|
-
if (hasMultipleProperties(colorJSON, ['hex'], numProp)) return colorJSON;
|
|
536
|
-
if (hasMultipleProperties(colorJSON, ['rgb'], numProp)) return colorJSON;
|
|
537
|
-
if (hasMultipleProperties(colorJSON, ['x', 'y'], numProp)) return colorJSON;
|
|
538
|
-
if (hasMultipleProperties(colorJSON, ['r', 'g', 'b'], numProp)) return colorJSON;
|
|
539
|
-
//return { json:colorJSON, numProp:numProp, value:value };
|
|
540
|
-
}
|
|
541
|
-
catch (error) {
|
|
542
|
-
//return { error: error.message };
|
|
543
|
-
};
|
|
695
|
+
const rv = colors.complexColor(value, true);
|
|
696
|
+
if (rv) return rv;
|
|
544
697
|
// hex or named color
|
|
545
698
|
const rgbcolor = colors.ParseColor(value);
|
|
546
699
|
return rgbcolor;
|
|
@@ -551,24 +704,7 @@ function createFromExposes(model, def, device, log) {
|
|
|
551
704
|
return {...options, transition: transitionTime};
|
|
552
705
|
},
|
|
553
706
|
getter: payload => {
|
|
554
|
-
|
|
555
|
-
const colorJSON = payload.color;
|
|
556
|
-
const color = JSON.stringify(colorJSON)
|
|
557
|
-
//const numProp = Object.keys(colorJSON).length;
|
|
558
|
-
if (hasMultipleProperties(colorJSON, ['hsb'])) return color;
|
|
559
|
-
if (hasMultipleProperties(colorJSON, ['hsl'])) return color;
|
|
560
|
-
if (hasMultipleProperties(colorJSON, ['hsv'])) return color;
|
|
561
|
-
if (hasMultipleProperties(colorJSON, ['h','s','b'])) return color;
|
|
562
|
-
if (hasMultipleProperties(colorJSON, ['h','s','v'])) return color;
|
|
563
|
-
if (hasMultipleProperties(colorJSON, ['h','s','l'])) return color;
|
|
564
|
-
if (hasMultipleProperties(colorJSON, ['hue', 'saturation'])) return color;
|
|
565
|
-
if (hasMultipleProperties(colorJSON, ['h', 's'])) return color;
|
|
566
|
-
if (hasMultipleProperties(colorJSON, ['hex'])) return color;
|
|
567
|
-
if (hasMultipleProperties(colorJSON, ['rgb'])) return color;
|
|
568
|
-
if (hasMultipleProperties(colorJSON, ['x', 'y'])) return color;
|
|
569
|
-
if (hasMultipleProperties(colorJSON, ['r', 'g', 'b'])) return color;
|
|
570
|
-
}
|
|
571
|
-
return undefined;
|
|
707
|
+
return colors.complexColor(payload, false);
|
|
572
708
|
},
|
|
573
709
|
epname: expose.endpoint,
|
|
574
710
|
setattr: 'color',
|
|
@@ -617,7 +753,6 @@ function createFromExposes(model, def, device, log) {
|
|
|
617
753
|
compositeKey: channelWithEp,
|
|
618
754
|
compositeTimeout: 500,
|
|
619
755
|
compositeState: 'color',
|
|
620
|
-
composites: [`${channelWithEp}.r`,`${channelWithEp}.g`]
|
|
621
756
|
}, colorXYprop.access);
|
|
622
757
|
pushToStates({
|
|
623
758
|
id: `${channelWithEp}.g`,
|
|
@@ -632,7 +767,6 @@ function createFromExposes(model, def, device, log) {
|
|
|
632
767
|
compositeKey: channelWithEp,
|
|
633
768
|
compositeTimeout: 500,
|
|
634
769
|
compositeState: 'color',
|
|
635
|
-
composites: [`${channelWithEp}.x`,`${channelWithEp}.y`]
|
|
636
770
|
});
|
|
637
771
|
pushToStates({
|
|
638
772
|
id: `${channelWithEp}.b`,
|
|
@@ -706,7 +840,7 @@ function createFromExposes(model, def, device, log) {
|
|
|
706
840
|
for (const prop of expose.features) {
|
|
707
841
|
switch (prop.name) {
|
|
708
842
|
case 'state':
|
|
709
|
-
pushToStates(genState(prop, 'switch'), prop.access);
|
|
843
|
+
pushToStates(genState(prop, { role:'switch' }), prop.access);
|
|
710
844
|
break;
|
|
711
845
|
default:
|
|
712
846
|
pushToStates(genState(prop), prop.access);
|
|
@@ -725,44 +859,44 @@ function createFromExposes(model, def, device, log) {
|
|
|
725
859
|
break;
|
|
726
860
|
|
|
727
861
|
case 'battery':
|
|
728
|
-
state = modifyState(genState(expose, 'value.battery'), {
|
|
862
|
+
state = modifyState(genState(expose, {role:'value.battery'}), {
|
|
729
863
|
icon:'img/battery_p.png',
|
|
730
864
|
});
|
|
731
865
|
//state = getStateDefinition('battery');
|
|
732
866
|
break;
|
|
733
867
|
|
|
734
868
|
case 'voltage':
|
|
735
|
-
state = genState(expose, 'level.voltage');
|
|
869
|
+
state = genState(expose, {role:'level.voltage'});
|
|
736
870
|
// state = getStateDefinition('voltage')
|
|
737
871
|
break;
|
|
738
872
|
|
|
739
873
|
case 'temperature':
|
|
740
|
-
state = genState(expose, 'value.temperature');
|
|
874
|
+
state = genState(expose, {role:'value.temperature'});
|
|
741
875
|
//state = getStateDefinition('temperature');
|
|
742
876
|
break;
|
|
743
877
|
|
|
744
878
|
case 'humidity':
|
|
745
|
-
state = genState(expose, 'value.humidity');
|
|
879
|
+
state = genState(expose, {role:'value.humidity'});
|
|
746
880
|
//state = getStateDefinition('humidity');
|
|
747
881
|
break;
|
|
748
882
|
|
|
749
883
|
case 'pressure':
|
|
750
|
-
state = genState(expose, 'value.pressure');
|
|
884
|
+
state = genState(expose, {role:'value.pressure'});
|
|
751
885
|
//state = getStateDefinition('pressure');
|
|
752
886
|
break;
|
|
753
887
|
|
|
754
888
|
case 'illuminance':
|
|
755
|
-
state = genState(expose, 'value.brightness', 'illuminance_raw' )
|
|
889
|
+
state = genState(expose, {role:'value.brightness', name: 'illuminance_raw'} );
|
|
756
890
|
//state = getStateDefinition('illuminance_raw');
|
|
757
891
|
break;
|
|
758
892
|
|
|
759
893
|
case 'illuminance_lux':
|
|
760
|
-
state = genState(expose, 'value.brightness', 'illuminance');
|
|
894
|
+
state = genState(expose, {role:'value.brightness', name:'illuminance'});
|
|
761
895
|
//state = getStateDefinition('illuminance');
|
|
762
896
|
break;
|
|
763
897
|
|
|
764
898
|
case 'power':
|
|
765
|
-
state = genState(expose, 'value.power', 'load_power');
|
|
899
|
+
state = genState(expose, {role:'value.power', name:'load_power'});
|
|
766
900
|
//state = getStateDefinition('load_power');
|
|
767
901
|
break;
|
|
768
902
|
|
|
@@ -848,9 +982,9 @@ function createFromExposes(model, def, device, log) {
|
|
|
848
982
|
} else {
|
|
849
983
|
switch (expose.name) {
|
|
850
984
|
case 'contact': {
|
|
851
|
-
state = modifyState(genState(expose, 'sensor'), { getter: payload => payload.contact });
|
|
985
|
+
state = modifyState(genState(expose, {role:'sensor'}), { getter: payload => payload.contact });
|
|
852
986
|
// state = getStateDefinition('contact');
|
|
853
|
-
pushToStates(modifyState(genState(expose, 'sensor'), {
|
|
987
|
+
pushToStates(modifyState(genState(expose,{role: 'sensor'}), {
|
|
854
988
|
id:'opened',
|
|
855
989
|
name:'Is open',
|
|
856
990
|
role:'sensor.contact',
|
|
@@ -860,18 +994,18 @@ function createFromExposes(model, def, device, log) {
|
|
|
860
994
|
break;
|
|
861
995
|
}
|
|
862
996
|
case 'battery_low':
|
|
863
|
-
state = genState(expose,'indicator.lowbat', expose.name, 'Battery Status Low');
|
|
997
|
+
state = genState(expose, {role:'indicator.lowbat', name:expose.name, desc:'Battery Status Low'});
|
|
864
998
|
break;
|
|
865
999
|
|
|
866
1000
|
case 'tamper':
|
|
867
|
-
state = modifyState(genState(expose, 'indicator', 'tampered'), {
|
|
1001
|
+
state = modifyState(genState(expose, {role: 'indicator',name: 'tampered'}), {
|
|
868
1002
|
prop:'tamper',
|
|
869
1003
|
name:'Is tampered'
|
|
870
1004
|
});
|
|
871
1005
|
break;
|
|
872
1006
|
|
|
873
1007
|
case 'water_leak':
|
|
874
|
-
state = modifyState(genState(expose, 'indicator', 'detected'), {
|
|
1008
|
+
state = modifyState(genState(expose, {role:'indicator', name:'detected'}), {
|
|
875
1009
|
prop:'water_leak',
|
|
876
1010
|
name:'Water leak detected'
|
|
877
1011
|
})
|
|
@@ -879,7 +1013,7 @@ function createFromExposes(model, def, device, log) {
|
|
|
879
1013
|
break;
|
|
880
1014
|
|
|
881
1015
|
case 'lock':
|
|
882
|
-
state = modifyState(genState(expose, 'switch.lock'), {
|
|
1016
|
+
state = modifyState(genState(expose, {role:'switch.lock'}), {
|
|
883
1017
|
prop: 'child_lock',
|
|
884
1018
|
name: 'Locked',
|
|
885
1019
|
getter: payload => (payload.child_lock === 'LOCKED'),
|
|
@@ -889,7 +1023,7 @@ function createFromExposes(model, def, device, log) {
|
|
|
889
1023
|
break;
|
|
890
1024
|
|
|
891
1025
|
case 'occupancy':
|
|
892
|
-
state = genState(expose, 'sensor.motion');
|
|
1026
|
+
state = genState(expose, {role:'sensor.motion'});
|
|
893
1027
|
//state = getStateDefinition('occupancy');
|
|
894
1028
|
break;
|
|
895
1029
|
|
|
@@ -914,7 +1048,7 @@ function createFromExposes(model, def, device, log) {
|
|
|
914
1048
|
for (const prop of expose.features) {
|
|
915
1049
|
switch (prop.name) {
|
|
916
1050
|
case 'state':
|
|
917
|
-
pushToStates(genState(prop, 'switch'), prop.access);
|
|
1051
|
+
pushToStates(genState(prop, {role:'switch'}), prop.access);
|
|
918
1052
|
break;
|
|
919
1053
|
default:
|
|
920
1054
|
pushToStates(genState(prop), prop.access);
|
|
@@ -931,105 +1065,9 @@ function createFromExposes(model, def, device, log) {
|
|
|
931
1065
|
|
|
932
1066
|
case 'composite':
|
|
933
1067
|
{
|
|
934
|
-
const
|
|
935
|
-
const
|
|
936
|
-
|
|
937
|
-
const channelID = LocalData.options.newCompositeMethod ? `${stateId}.` : '';
|
|
938
|
-
if (LocalData.options.newCompositeMethod) {
|
|
939
|
-
|
|
940
|
-
if (typeof stname !== 'string') break;
|
|
941
|
-
pushToStates({
|
|
942
|
-
id: stateId,
|
|
943
|
-
name: stateName,
|
|
944
|
-
icon: undefined,
|
|
945
|
-
role: 'state',
|
|
946
|
-
write: expose.access & ea.SET,
|
|
947
|
-
read: expose.access & ea.GET,
|
|
948
|
-
type: 'string',
|
|
949
|
-
}, expose.access)
|
|
950
|
-
|
|
951
|
-
}
|
|
952
|
-
for (const prop of expose.features) {
|
|
953
|
-
if (prop.type == 'numeric') {
|
|
954
|
-
const st = genState(prop, 'state', `${channelID}${prop.name}`);
|
|
955
|
-
st.prop = expose.property;
|
|
956
|
-
st.inOptions = true;
|
|
957
|
-
// I'm not fully sure, as it really needed, but
|
|
958
|
-
st.setterOpt = (value, options) => {
|
|
959
|
-
const result = {};
|
|
960
|
-
options[prop.property] = value;
|
|
961
|
-
result[expose.property] = options;
|
|
962
|
-
return result;
|
|
963
|
-
};
|
|
964
|
-
if (LocalData.options.newCompositeMethod) {
|
|
965
|
-
st.compositeKey = stateId;
|
|
966
|
-
st.compositeTimeout = 250;
|
|
967
|
-
st.compositeState = stateId;
|
|
968
|
-
} else
|
|
969
|
-
{
|
|
970
|
-
// if we have a composite expose, the value have to be an object {expose.property : {prop.property: value}}
|
|
971
|
-
if (prop.access & ea.SET) {
|
|
972
|
-
st.setter = (value, options) => {
|
|
973
|
-
const result = {};
|
|
974
|
-
options[prop.property] = value;
|
|
975
|
-
result[expose.property] = options;
|
|
976
|
-
return result;
|
|
977
|
-
};
|
|
978
|
-
st.setattr = expose.property;
|
|
979
|
-
}
|
|
980
|
-
// if we have a composite expose, the payload will be an object {expose.property : {prop.property: value}}
|
|
981
|
-
if (prop.access & ea.STATE) {
|
|
982
|
-
st.getter = payload => {
|
|
983
|
-
if ((payload.hasOwnProperty(expose.property)) && (payload[expose.property] !== null) && payload[expose.property].hasOwnProperty(prop.property)) {
|
|
984
|
-
return !isNaN(payload[expose.property][prop.property]) ? payload[expose.property][prop.property] : undefined;
|
|
985
|
-
} else {
|
|
986
|
-
return undefined;
|
|
987
|
-
}
|
|
988
|
-
};
|
|
989
|
-
} else {
|
|
990
|
-
st.getter = payload => undefined;
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
pushToStates(st, prop.access);
|
|
994
|
-
}
|
|
995
|
-
|
|
996
|
-
if (prop.type == 'list') {
|
|
997
|
-
for (const propList of prop.item_type.features) {
|
|
998
|
-
const st = genState(propList);
|
|
999
|
-
st.prop = expose.property;
|
|
1000
|
-
st.inOptions = true;
|
|
1001
|
-
st.setterOpt = (value, options) => {
|
|
1002
|
-
const result = {};
|
|
1003
|
-
options[propList.property] = value;
|
|
1004
|
-
result[expose.property] = options;
|
|
1005
|
-
return result;
|
|
1006
|
-
};
|
|
1007
|
-
if (propList.access & ea.SET) {
|
|
1008
|
-
st.setter = (value, options) => {
|
|
1009
|
-
const result = {};
|
|
1010
|
-
options[propList.property] = value;
|
|
1011
|
-
result[expose.property] = options;
|
|
1012
|
-
return result;
|
|
1013
|
-
};
|
|
1014
|
-
st.setattr = expose.property;
|
|
1015
|
-
}
|
|
1016
|
-
if (propList.access & ea.STATE) {
|
|
1017
|
-
st.getter = payload => {
|
|
1018
|
-
if ((payload.hasOwnProperty(expose.property)) && (payload[expose.property] !== null) && payload[expose.property].hasOwnProperty(propList.property)) {
|
|
1019
|
-
return !isNaN(payload[expose.property][propList.property]) ? payload[expose.property][propList.property] : undefined;
|
|
1020
|
-
} else {
|
|
1021
|
-
return undefined;
|
|
1022
|
-
}
|
|
1023
|
-
};
|
|
1024
|
-
} else {
|
|
1025
|
-
st.getter = payload => undefined;
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
st.id = st.prop + '_' + st.id;
|
|
1029
|
-
pushToStates(st, propList.access);
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1068
|
+
const cStates = generateCompositeStates(expose, '', options);
|
|
1069
|
+
for (const state of cStates)
|
|
1070
|
+
pushToStates(state, expose.access);
|
|
1033
1071
|
break;
|
|
1034
1072
|
}
|
|
1035
1073
|
case 'list':
|
|
@@ -1047,20 +1085,9 @@ function createFromExposes(model, def, device, log) {
|
|
|
1047
1085
|
|
|
1048
1086
|
}
|
|
1049
1087
|
|
|
1050
|
-
async function
|
|
1051
|
-
if (options && typeof options == 'object') {
|
|
1052
|
-
LocalData.options = options;
|
|
1053
|
-
}
|
|
1054
|
-
const deviceDef = await zigbeeHerdsmanConverters.findByDevice(device);
|
|
1055
|
-
if (!deviceDef) {
|
|
1056
|
-
return undefined;
|
|
1057
|
-
}
|
|
1058
|
-
return applyDeviceDef(mappedDevices, byModel, deviceDef, device);
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
async function applyHerdsmanModel(modelDesc) {
|
|
1088
|
+
async function applyHerdsmanModel(modelDesc, options) {
|
|
1062
1089
|
try {
|
|
1063
|
-
const newModel = createFromExposes(modelDesc.key, modelDesc, modelDesc.device)
|
|
1090
|
+
const newModel = createFromExposes(modelDesc.key, modelDesc, modelDesc.device, options)
|
|
1064
1091
|
if (newModel) {
|
|
1065
1092
|
if (modelDesc.UUID) newModel.UUID = modelDesc.UUID;
|
|
1066
1093
|
}
|
|
@@ -1071,25 +1098,6 @@ async function applyHerdsmanModel(modelDesc) {
|
|
|
1071
1098
|
}
|
|
1072
1099
|
}
|
|
1073
1100
|
|
|
1074
|
-
|
|
1075
|
-
function applyDeviceDef(mappedDevices, byModel, deviceDef, device) {
|
|
1076
|
-
const stripModel = utils.getModelRegEx(deviceDef.model);
|
|
1077
|
-
const existsMap = byModel.get(stripModel);
|
|
1078
|
-
if (deviceDef.hasOwnProperty('exposes') && (!existsMap || !existsMap.hasOwnProperty('states'))) {
|
|
1079
|
-
try {
|
|
1080
|
-
const newDevice = createFromExposes(stripModel, deviceDef, device);
|
|
1081
|
-
mappedDevices.push(newDevice);
|
|
1082
|
-
byModel.set(stripModel, newDevice);
|
|
1083
|
-
return newDevice;
|
|
1084
|
-
|
|
1085
|
-
} catch (e) {
|
|
1086
|
-
return undefined;
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
return existsMap;
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
1101
|
module.exports = {
|
|
1093
|
-
applyExposeForDevice: applyExposeForDevice,
|
|
1094
1102
|
applyHerdsmanModel: applyHerdsmanModel,
|
|
1095
1103
|
};
|
package/lib/groups.js
CHANGED
|
@@ -676,11 +676,8 @@ class Groups {
|
|
|
676
676
|
}, () => {
|
|
677
677
|
this.adapter.extendObject(id, {common: {name: name, type: 'group', icon: icon}});
|
|
678
678
|
// create writable states for groups from their devices
|
|
679
|
-
for (const
|
|
680
|
-
if (!
|
|
681
|
-
continue;
|
|
682
|
-
}
|
|
683
|
-
const statedesc = stateDefinitions.groupStates[stateInd];
|
|
679
|
+
for (const statedesc of stateDefinitions.groupStates) {
|
|
680
|
+
if (!statedesc) continue;
|
|
684
681
|
const common = {};
|
|
685
682
|
for (const prop in statedesc)
|
|
686
683
|
if (typeof statedesc[prop] != 'function')
|
package/lib/legacy/devices.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const states = require('./states.js').states;
|
|
4
4
|
const utils = require('../utils.js');
|
|
5
5
|
const rgb = require('../rgb.js');
|
|
6
|
-
const { applyExposeForDevice} = require('../exposes.js');
|
|
6
|
+
//const { applyExposeForDevice} = require('../exposes.js');
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
// return list of changing states when incoming state is changed
|
|
@@ -3119,6 +3119,7 @@ function getByModel() {
|
|
|
3119
3119
|
|
|
3120
3120
|
removeEmptyStates(devices);
|
|
3121
3121
|
|
|
3122
|
+
/*
|
|
3122
3123
|
function fillStatesWithExposes(logger) {
|
|
3123
3124
|
if (DevicesByModel.size <1) getByModel();
|
|
3124
3125
|
return;
|
|
@@ -3133,7 +3134,7 @@ async function addExposeToDevices(device, logger, model) {
|
|
|
3133
3134
|
removeEmptyStates(devices);
|
|
3134
3135
|
return rv;
|
|
3135
3136
|
}
|
|
3136
|
-
|
|
3137
|
+
*/
|
|
3137
3138
|
// remove empty states
|
|
3138
3139
|
function removeEmptyStates(devices) {
|
|
3139
3140
|
for (const device of devices) {
|
|
@@ -3200,6 +3201,7 @@ function hasLegacyDevice(model) {
|
|
|
3200
3201
|
}
|
|
3201
3202
|
|
|
3202
3203
|
module.exports = {
|
|
3204
|
+
/*
|
|
3203
3205
|
devices,
|
|
3204
3206
|
legacy_devices,
|
|
3205
3207
|
commonStates,
|
|
@@ -3211,10 +3213,11 @@ module.exports = {
|
|
|
3211
3213
|
fillStatesWithExposes,
|
|
3212
3214
|
addExposeToDevices,
|
|
3213
3215
|
getByModel,
|
|
3216
|
+
*/
|
|
3214
3217
|
findModel,
|
|
3215
|
-
fillDevicesForLegacy,
|
|
3216
|
-
pairedLegacyDevices,
|
|
3217
|
-
setLegacyDevices,
|
|
3218
|
+
//fillDevicesForLegacy,
|
|
3219
|
+
//pairedLegacyDevices,
|
|
3220
|
+
//setLegacyDevices,
|
|
3218
3221
|
hasLegacyDevice,
|
|
3219
3222
|
getIconforLegacyModel,
|
|
3220
3223
|
};
|