iobroker.zigbee 1.8.1 → 1.8.3
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/.eslintignore +2 -0
- package/.eslintrc.json +37 -0
- package/.github/FUNDING.yml +3 -0
- package/.github/auto-merge.yml +17 -0
- package/.github/dependabot.yml +24 -0
- package/.github/stale.yml +13 -0
- package/.github/workflows/codeql.yml +41 -0
- package/.github/workflows/dependabot-automerge.yml +22 -0
- package/.github/workflows/test-and-release.yml +149 -0
- package/.releaseconfig.json +3 -0
- package/.travis/wiki.sh +28 -0
- package/.travis.yml +41 -0
- package/README.md +31 -8
- package/admin/admin.js +466 -482
- package/admin/i18n/de/translations.json +2 -2
- package/admin/index_m.html +1 -1
- package/admin/tab_m.html +3 -44
- package/admin/words.js +2 -2
- package/gulpfile.js +464 -0
- package/io-package.json +18 -25
- package/lib/backup.js +2 -2
- package/lib/binding.js +37 -32
- package/lib/colors.js +158 -163
- package/lib/commands.js +90 -99
- package/lib/developer.js +12 -9
- package/lib/devices.js +179 -169
- package/lib/exclude.js +36 -30
- package/lib/exposes.js +139 -163
- package/lib/groups.js +83 -81
- package/lib/json.js +6 -5
- package/lib/networkmap.js +3 -2
- package/lib/ota.js +18 -34
- package/lib/rgb.js +72 -114
- package/lib/seriallist.js +20 -25
- package/lib/states.js +526 -511
- package/lib/statescontroller.js +183 -206
- package/lib/utils.js +23 -24
- package/lib/zbBaseExtension.js +4 -4
- package/lib/zbDelayedAction.js +13 -5
- package/lib/zbDeviceAvailability.js +65 -69
- package/lib/zbDeviceConfigure.js +21 -9
- package/lib/zbDeviceEvent.js +4 -3
- package/lib/zigbeecontroller.js +103 -109
- package/main.js +147 -163
- package/package.json +15 -29
- package/test/integration.js +5 -0
- package/test/mocha.custom.opts +2 -0
- package/test/mocha.setup.js +14 -0
- package/test/package.js +5 -0
- package/test/unit.js +5 -0
- package/docs/de/img/CC2531.png +0 -0
- package/docs/de/img/CC2538_CC2592_PA.PNG +0 -0
- package/docs/de/img/CC2591.png +0 -0
- package/docs/de/img/boards.jpg +0 -0
- package/docs/de/img/cc26x2r.PNG +0 -0
- package/docs/de/img/results.jpg +0 -0
- package/docs/de/img/sku_429478_2.png +0 -0
- package/docs/de/img/sku_429601_2.png +0 -0
- package/docs/de/readme.md +0 -27
- package/docs/en/img/CC2531.png +0 -0
- package/docs/en/img/CC2591.png +0 -0
- package/docs/en/img/deconz.png +0 -0
- package/docs/en/img/sku_429478_2.png +0 -0
- package/docs/en/img/sku_429601_2.png +0 -0
- package/docs/en/readme.md +0 -30
- package/docs/flashing_via_arduino_(en).md +0 -110
- package/docs/ru/img/CC2531.png +0 -0
- package/docs/ru/img/CC2591.png +0 -0
- package/docs/ru/img/sku_429478_2.png +0 -0
- package/docs/ru/img/sku_429601_2.png +0 -0
- package/docs/ru/readme.md +0 -28
- package/docs/tutorial/CC2530_20190425.zip +0 -0
- package/docs/tutorial/CC2530_CC2591_20190515.zip +0 -0
- package/docs/tutorial/CC2530_CC2592_20190515.zip +0 -0
- package/docs/tutorial/CC2531_20190425.zip +0 -0
- package/docs/tutorial/adm5_1.PNG +0 -0
- package/docs/tutorial/adm5_2.PNG +0 -0
- package/docs/tutorial/cat.PNG +0 -0
- package/docs/tutorial/groups-1.png +0 -0
- package/docs/tutorial/groups-2.png +0 -0
- package/docs/tutorial/inst.PNG +0 -0
- package/docs/tutorial/reflash-finish.PNG +0 -0
- package/docs/tutorial/reflash-step0.png +0 -0
- package/docs/tutorial/reflash-step1.PNG +0 -0
- package/docs/tutorial/reflash-step2.PNG +0 -0
- package/docs/tutorial/settings.png +0 -0
- package/docs/tutorial/tab-dev-1.png +0 -0
- package/docs/tutorial/zigbee.png +0 -0
- package/docs/tutorial/zigbee15.png +0 -0
package/lib/exposes.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
|
|
4
|
-
const statesDefs = require('
|
|
5
|
-
const rgb = require('
|
|
6
|
-
const utils = require('
|
|
7
|
-
const colors = require('
|
|
4
|
+
const statesDefs = require(__dirname + '/states.js').states;
|
|
5
|
+
const rgb = require(__dirname + '/rgb.js');
|
|
6
|
+
const utils = require(__dirname + '/utils.js');
|
|
7
|
+
const colors = require(__dirname + '/colors.js');
|
|
8
8
|
const ea = zigbeeHerdsmanConverters.exposes.access;
|
|
9
9
|
|
|
10
10
|
function genState(expose, role, name, desc) {
|
|
@@ -29,9 +29,10 @@ function genState(expose, role, name, desc) {
|
|
|
29
29
|
type: 'boolean',
|
|
30
30
|
};
|
|
31
31
|
if (readable) {
|
|
32
|
-
state.getter = payload => payload[propName] === (expose.value_on || 'ON');
|
|
33
|
-
}
|
|
34
|
-
|
|
32
|
+
state.getter = payload => (payload[propName] === (expose.value_on || 'ON'));
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
state.getter = payload => ( undefined);
|
|
35
36
|
}
|
|
36
37
|
if (writable) {
|
|
37
38
|
state.setter = (value) => (value) ? (expose.value_on || 'ON') : ((expose.value_off != undefined) ? expose.value_off : 'OFF');
|
|
@@ -71,7 +72,7 @@ function genState(expose, role, name, desc) {
|
|
|
71
72
|
write: writable,
|
|
72
73
|
read: true,
|
|
73
74
|
type: 'string',
|
|
74
|
-
states: expose.values.map(item => `${item}:${item}`).join(';'),
|
|
75
|
+
states: expose.values.map((item) => `${item}:${item}`).join(';'),
|
|
75
76
|
};
|
|
76
77
|
if (expose.endpoint) {
|
|
77
78
|
state.epname = expose.endpoint;
|
|
@@ -102,6 +103,8 @@ function genState(expose, role, name, desc) {
|
|
|
102
103
|
return state;
|
|
103
104
|
}
|
|
104
105
|
|
|
106
|
+
|
|
107
|
+
|
|
105
108
|
function createFromExposes(model, def) {
|
|
106
109
|
const states = [];
|
|
107
110
|
// make the different (set and get) part of state is updatable if different exposes is used for get and set
|
|
@@ -118,31 +121,30 @@ function createFromExposes(model, def) {
|
|
|
118
121
|
if (state === undefined) {
|
|
119
122
|
return 0;
|
|
120
123
|
}
|
|
121
|
-
if (access === undefined)
|
|
122
|
-
access = ea.ALL;
|
|
123
|
-
}
|
|
124
|
+
if (access === undefined) access = ea.ALL;
|
|
124
125
|
state.readable = (access & ea.STATE) > 0;
|
|
125
126
|
state.writable = (access & ea.SET) > 0;
|
|
126
|
-
const stateExists = states.findIndex((element, index, array) => element.id === state.id);
|
|
127
|
-
if (stateExists < 0) {
|
|
127
|
+
const stateExists = states.findIndex( (element, index, array) => (element.id === state.id ));
|
|
128
|
+
if (stateExists < 0 ) {
|
|
128
129
|
state.write = state.writable;
|
|
129
|
-
if (!state.writable) {
|
|
130
|
-
if (state.hasOwnProperty('setter')) {
|
|
130
|
+
if (! state.writable) {
|
|
131
|
+
if ( state.hasOwnProperty('setter') ) {
|
|
131
132
|
delete state.setter;
|
|
132
133
|
}
|
|
133
|
-
if (state.hasOwnProperty('setattr')) {
|
|
134
|
+
if ( state.hasOwnProperty('setattr') ) {
|
|
134
135
|
delete state.setattr;
|
|
135
136
|
}
|
|
136
137
|
}
|
|
137
|
-
if (!state.readable) {
|
|
138
|
-
if (state.hasOwnProperty('getter')) {
|
|
139
|
-
//
|
|
140
|
-
state.getter = payload => undefined;
|
|
138
|
+
if (! state.readable) {
|
|
139
|
+
if (state.hasOwnProperty('getter') ) {
|
|
140
|
+
//to awid some worning on unprocessed data
|
|
141
|
+
state.getter = payload => ( undefined );
|
|
141
142
|
}
|
|
142
143
|
}
|
|
143
144
|
return states.push(state);
|
|
144
|
-
}
|
|
145
|
-
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
if ( (state.readable) && (! states[stateExists].readable ) ) {
|
|
146
148
|
states[stateExists].read = state.read;
|
|
147
149
|
// as state is readable, it can't be button or event
|
|
148
150
|
if (states[stateExists].role === 'button') {
|
|
@@ -152,70 +154,69 @@ function createFromExposes(model, def) {
|
|
|
152
154
|
delete states[stateExists].isEvent;
|
|
153
155
|
}
|
|
154
156
|
// we have to use the getter from "new" state
|
|
155
|
-
if (state.hasOwnProperty('getter')) {
|
|
157
|
+
if ( state.hasOwnProperty('getter') ) {
|
|
156
158
|
states[stateExists].getter = state.getter;
|
|
157
159
|
}
|
|
158
160
|
// trying to remove the `prop` property, as main key for get and set,
|
|
159
161
|
// as it can be different in new and old states, and leave only:
|
|
160
162
|
// setattr for old and id for new
|
|
161
|
-
if ((state.hasOwnProperty('prop')) && (state.prop === state.id)) {
|
|
162
|
-
if (states[stateExists].hasOwnProperty('prop')) {
|
|
163
|
+
if (( state.hasOwnProperty('prop') ) && (state.prop === state.id)) {
|
|
164
|
+
if ( states[stateExists].hasOwnProperty('prop') ) {
|
|
163
165
|
if (states[stateExists].prop !== states[stateExists].id) {
|
|
164
|
-
if (!states[stateExists].hasOwnProperty('setattr')) {
|
|
166
|
+
if (! states[stateExists].hasOwnProperty('setattr')) {
|
|
165
167
|
states[stateExists].setattr = states[stateExists].prop;
|
|
166
168
|
}
|
|
167
169
|
}
|
|
168
170
|
delete states[stateExists].prop;
|
|
169
171
|
}
|
|
170
|
-
}
|
|
172
|
+
}
|
|
173
|
+
else if ( state.hasOwnProperty('prop') ) {
|
|
171
174
|
states[stateExists].prop = state.prop;
|
|
172
175
|
}
|
|
173
176
|
states[stateExists].readable = true;
|
|
174
177
|
}
|
|
175
|
-
if ((state.writable) && (!states[stateExists].writable)) {
|
|
178
|
+
if ( (state.writable) && (! states[stateExists].writable ) ) {
|
|
176
179
|
states[stateExists].write = state.writable;
|
|
177
180
|
// use new state `setter`
|
|
178
|
-
if (state.hasOwnProperty('setter')) {
|
|
181
|
+
if ( state.hasOwnProperty('setter') ) {
|
|
179
182
|
states[stateExists].setter = state.setter;
|
|
180
183
|
}
|
|
181
184
|
// use new state `setterOpt`
|
|
182
|
-
if (state.hasOwnProperty('setterOpt')) {
|
|
185
|
+
if ( state.hasOwnProperty('setterOpt') ) {
|
|
183
186
|
states[stateExists].setterOpt = state.setterOpt;
|
|
184
187
|
}
|
|
185
188
|
// use new state `inOptions`
|
|
186
|
-
if (state.hasOwnProperty('inOptions')) {
|
|
189
|
+
if ( state.hasOwnProperty('inOptions') ) {
|
|
187
190
|
states[stateExists].inOptions = state.inOptions;
|
|
188
191
|
}
|
|
189
192
|
// as we have new state, responsible for set, we have to use new `isOption`
|
|
190
193
|
// or remove it
|
|
191
|
-
if (((!state.hasOwnProperty('isOption')) || (state.isOptions === false))
|
|
192
|
-
|
|
194
|
+
if (((! state.hasOwnProperty('isOption') ) || (state.isOptions === false))
|
|
195
|
+
&& (states[stateExists].hasOwnProperty('isOption'))) {
|
|
193
196
|
delete states[stateExists].isOption;
|
|
194
|
-
}
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
195
199
|
states[stateExists].isOption = state.isOption;
|
|
196
200
|
}
|
|
197
|
-
|
|
198
201
|
// use new `setattr` or `prop` as `setattr`
|
|
199
|
-
if (state.hasOwnProperty('setattr')) {
|
|
202
|
+
if ( state.hasOwnProperty('setattr') ) {
|
|
200
203
|
states[stateExists].setattr = state.setattr;
|
|
201
|
-
}
|
|
204
|
+
}
|
|
205
|
+
else if ( state.hasOwnProperty('prop') ) {
|
|
202
206
|
states[stateExists].setattr = state.prop;
|
|
203
207
|
}
|
|
204
|
-
|
|
205
208
|
// remove `prop` equal to if, due to prop is uses as key in set and get
|
|
206
|
-
if (states[stateExists].prop === states[stateExists].id) {
|
|
209
|
+
if ( states[stateExists].prop === states[stateExists].id) {
|
|
207
210
|
delete states[stateExists].prop;
|
|
208
211
|
}
|
|
209
|
-
if (state.hasOwnProperty('epname')) {
|
|
212
|
+
if ( state.hasOwnProperty('epname') ) {
|
|
210
213
|
states[stateExists].epname = state.epname;
|
|
211
214
|
}
|
|
212
|
-
|
|
213
215
|
states[stateExists].writable = true;
|
|
214
216
|
}
|
|
215
217
|
return states.length;
|
|
216
218
|
}
|
|
217
219
|
}
|
|
218
|
-
|
|
219
220
|
const icon = utils.getDeviceIcon(def);
|
|
220
221
|
for (const expose of def.exposes) {
|
|
221
222
|
let state;
|
|
@@ -241,7 +242,6 @@ function createFromExposes(model, def) {
|
|
|
241
242
|
}, prop.access);
|
|
242
243
|
break;
|
|
243
244
|
}
|
|
244
|
-
|
|
245
245
|
case 'brightness': {
|
|
246
246
|
const stateNameB = expose.endpoint ? `brightness_${expose.endpoint}` : 'brightness';
|
|
247
247
|
pushToStates({
|
|
@@ -255,8 +255,12 @@ function createFromExposes(model, def) {
|
|
|
255
255
|
min: 0, // ignore expose.value_min
|
|
256
256
|
max: 100, // ignore expose.value_max
|
|
257
257
|
inOptions: true,
|
|
258
|
-
getter: payload =>
|
|
259
|
-
|
|
258
|
+
getter: payload => {
|
|
259
|
+
return utils.bulbLevelToAdapterLevel(payload[stateNameB]);
|
|
260
|
+
},
|
|
261
|
+
setter: (value) => {
|
|
262
|
+
return utils.adapterLevelToBulbLevel(value);
|
|
263
|
+
},
|
|
260
264
|
setterOpt: (value, options) => {
|
|
261
265
|
const hasTransitionTime = options && options.hasOwnProperty('transition_time');
|
|
262
266
|
const transitionTime = hasTransitionTime ? options.transition_time : 0;
|
|
@@ -264,7 +268,7 @@ function createFromExposes(model, def) {
|
|
|
264
268
|
preparedOptions.brightness = utils.adapterLevelToBulbLevel(value);
|
|
265
269
|
return preparedOptions;
|
|
266
270
|
},
|
|
267
|
-
readResponse: resp => {
|
|
271
|
+
readResponse: (resp) => {
|
|
268
272
|
const respObj = resp[0];
|
|
269
273
|
if (respObj.status === 0 && respObj.attrData != undefined) {
|
|
270
274
|
return utils.bulbLevelToAdapterLevel(respObj.attrData);
|
|
@@ -278,30 +282,30 @@ function createFromExposes(model, def) {
|
|
|
278
282
|
}
|
|
279
283
|
case 'color_temp': {
|
|
280
284
|
const stateNameT = expose.endpoint ? `colortemp_${expose.endpoint}` : 'colortemp';
|
|
281
|
-
pushToStates(
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
setterOpt: (value, options) => {
|
|
297
|
-
const hasTransitionTime = options && options.hasOwnProperty('transition_time');
|
|
298
|
-
const transitionTime = hasTransitionTime ? options.transition_time : 0;
|
|
299
|
-
return {...options, transition: transitionTime};
|
|
300
|
-
},
|
|
301
|
-
epname: expose.endpoint,
|
|
302
|
-
setattr: 'color_temp',
|
|
285
|
+
pushToStates({
|
|
286
|
+
id: stateNameT,
|
|
287
|
+
prop: expose.endpoint ? `color_temp_${expose.endpoint}` : 'color_temp',
|
|
288
|
+
name: `Color temperature ${expose.endpoint ? expose.endpoint : ''}`.trim(),
|
|
289
|
+
icon: undefined,
|
|
290
|
+
role: 'level.color.temperature',
|
|
291
|
+
write: true,
|
|
292
|
+
read: true,
|
|
293
|
+
type: 'number',
|
|
294
|
+
// Ignore min and max value, so setting mireds and Kelvin with conversion to mireds works.
|
|
295
|
+
// https://github.com/ioBroker/ioBroker.zigbee/pull/1433#issuecomment-1113837035
|
|
296
|
+
min: undefined,
|
|
297
|
+
max: undefined,
|
|
298
|
+
setter: (value) => {
|
|
299
|
+
return utils.toMired(value);
|
|
303
300
|
},
|
|
304
|
-
|
|
301
|
+
setterOpt: (value, options) => {
|
|
302
|
+
const hasTransitionTime = options && options.hasOwnProperty('transition_time');
|
|
303
|
+
const transitionTime = hasTransitionTime ? options.transition_time : 0;
|
|
304
|
+
return {...options, transition: transitionTime};
|
|
305
|
+
},
|
|
306
|
+
epname: expose.endpoint,
|
|
307
|
+
setattr: 'color_temp',
|
|
308
|
+
}, prop.access);
|
|
305
309
|
pushToStates(statesDefs.colortemp_move, prop.access);
|
|
306
310
|
break;
|
|
307
311
|
}
|
|
@@ -316,8 +320,8 @@ function createFromExposes(model, def) {
|
|
|
316
320
|
write: true,
|
|
317
321
|
read: true,
|
|
318
322
|
type: 'string',
|
|
319
|
-
setter: value => {
|
|
320
|
-
|
|
323
|
+
setter: (value) => {
|
|
324
|
+
// convert RGB to XY for set
|
|
321
325
|
/*
|
|
322
326
|
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(value);
|
|
323
327
|
let xy = [0, 0];
|
|
@@ -338,8 +342,9 @@ function createFromExposes(model, def) {
|
|
|
338
342
|
xy = rgb.rgb_to_cie(rgbcolor.r, rgbcolor.g, rgbcolor.b);
|
|
339
343
|
return {
|
|
340
344
|
x: xy[0],
|
|
341
|
-
y: xy[1]
|
|
345
|
+
y: xy[1]
|
|
342
346
|
};
|
|
347
|
+
|
|
343
348
|
},
|
|
344
349
|
setterOpt: (value, options) => {
|
|
345
350
|
const hasTransitionTime = options && options.hasOwnProperty('transition_time');
|
|
@@ -349,7 +354,7 @@ function createFromExposes(model, def) {
|
|
|
349
354
|
getter: payload => {
|
|
350
355
|
if (payload.color && payload.color.hasOwnProperty('x') && payload.color.hasOwnProperty('y')) {
|
|
351
356
|
const colorval = rgb.cie_to_rgb(payload.color.x, payload.color.y);
|
|
352
|
-
return
|
|
357
|
+
return '#' + utils.decimalToHex(colorval[0]) + utils.decimalToHex(colorval[1]) + utils.decimalToHex(colorval[2]);
|
|
353
358
|
} else {
|
|
354
359
|
return undefined;
|
|
355
360
|
}
|
|
@@ -370,13 +375,14 @@ function createFromExposes(model, def) {
|
|
|
370
375
|
write: true,
|
|
371
376
|
read: true,
|
|
372
377
|
type: 'string',
|
|
373
|
-
setter: value => {
|
|
378
|
+
setter: (value) => {
|
|
374
379
|
const _rgb = colors.ParseColor(value);
|
|
375
380
|
const hsv = rgb.rgbToHSV(_rgb.r, _rgb.g, _rgb.b, true);
|
|
376
381
|
return {
|
|
377
|
-
hue: Math.min(Math.max(hsv.h,
|
|
382
|
+
hue: Math.min(Math.max(hsv.h,1),359),
|
|
378
383
|
saturation: hsv.s,
|
|
379
384
|
// brightness: Math.floor(hsv.v * 2.55),
|
|
385
|
+
|
|
380
386
|
};
|
|
381
387
|
},
|
|
382
388
|
setterOpt: (value, options) => {
|
|
@@ -390,7 +396,7 @@ function createFromExposes(model, def) {
|
|
|
390
396
|
pushToStates({
|
|
391
397
|
id: expose.endpoint ? `hue_${expose.endpoint}` : 'hue',
|
|
392
398
|
prop: expose.endpoint ? `color_${expose.endpoint}` : 'color',
|
|
393
|
-
name: `Hue ${expose.endpoint
|
|
399
|
+
name: `Hue ${expose.endpoint ? expose.endpoint : ''}`.trim(),
|
|
394
400
|
icon: undefined,
|
|
395
401
|
role: 'level.color.hue',
|
|
396
402
|
write: true,
|
|
@@ -411,28 +417,17 @@ function createFromExposes(model, def) {
|
|
|
411
417
|
const hasHueCalibrationTable = options && options.hasOwnProperty('hue_calibration');
|
|
412
418
|
if (hasHueCalibrationTable)
|
|
413
419
|
try {
|
|
414
|
-
return {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
hue_correction: JSON.parse(options.hue_calibration)
|
|
418
|
-
};
|
|
419
|
-
} catch {
|
|
420
|
+
return {...options, transition: transitionTime, hue_correction: JSON.parse(options.hue_calibration)};
|
|
421
|
+
}
|
|
422
|
+
catch {
|
|
420
423
|
const hue_correction_table = [];
|
|
421
424
|
options.hue_calibration.split(',').forEach(element => {
|
|
422
425
|
const match = /([0-9]+):([0-9]+)/.exec(element);
|
|
423
|
-
if (match && match.length
|
|
424
|
-
hue_correction_table.push({
|
|
425
|
-
in: Number(match[1]),
|
|
426
|
-
out: Number(match[2])
|
|
427
|
-
});
|
|
426
|
+
if (match && match.length ==3)
|
|
427
|
+
hue_correction_table.push({ in: Number(match[1]), out: Number(match[2])});
|
|
428
428
|
});
|
|
429
|
-
if (hue_correction_table.length > 0)
|
|
430
|
-
return {
|
|
431
|
-
...options,
|
|
432
|
-
transition: transitionTime,
|
|
433
|
-
hue_correction: hue_correction_table
|
|
434
|
-
};
|
|
435
|
-
}
|
|
429
|
+
if (hue_correction_table.length > 0)
|
|
430
|
+
return {...options, transition: transitionTime, hue_correction: hue_correction_table};
|
|
436
431
|
}
|
|
437
432
|
return {...options, transition: transitionTime};
|
|
438
433
|
},
|
|
@@ -450,38 +445,29 @@ function createFromExposes(model, def) {
|
|
|
450
445
|
min: 0,
|
|
451
446
|
max: 100,
|
|
452
447
|
inOptions: true,
|
|
453
|
-
setter: (value, options) =>
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
448
|
+
setter: (value, options) => {
|
|
449
|
+
return {
|
|
450
|
+
hue: options.hue,
|
|
451
|
+
saturation: value,
|
|
452
|
+
};
|
|
453
|
+
},
|
|
457
454
|
setterOpt: (value, options) => {
|
|
458
455
|
const hasTransitionTime = options && options.hasOwnProperty('transition_time');
|
|
459
456
|
const transitionTime = hasTransitionTime ? options.transition_time : 0;
|
|
460
457
|
const hasHueCalibrationTable = options && options.hasOwnProperty('hue_calibration');
|
|
461
458
|
if (hasHueCalibrationTable)
|
|
462
459
|
try {
|
|
463
|
-
return {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
hue_correction: JSON.parse(options.hue_calibration)
|
|
467
|
-
};
|
|
468
|
-
} catch {
|
|
460
|
+
return {...options, transition: transitionTime, hue_correction: JSON.parse(options.hue_calibration)};
|
|
461
|
+
}
|
|
462
|
+
catch {
|
|
469
463
|
const hue_correction_table = [];
|
|
470
464
|
options.hue_calibration.split(',').forEach(element => {
|
|
471
465
|
const match = /([0-9]+):([0-9]+)/.exec(element);
|
|
472
|
-
if (match && match.length
|
|
473
|
-
hue_correction_table.push({
|
|
474
|
-
in: Number(match[1]),
|
|
475
|
-
out: Number(match[2])
|
|
476
|
-
});
|
|
466
|
+
if (match && match.length ==3)
|
|
467
|
+
hue_correction_table.push({ in: Number(match[1]), out: Number(match[2])});
|
|
477
468
|
});
|
|
478
|
-
if (hue_correction_table.length > 0)
|
|
479
|
-
return {
|
|
480
|
-
...options,
|
|
481
|
-
transition: transitionTime,
|
|
482
|
-
hue_correction: hue_correction_table
|
|
483
|
-
};
|
|
484
|
-
}
|
|
469
|
+
if (hue_correction_table.length > 0)
|
|
470
|
+
return {...options, transition: transitionTime, hue_correction: hue_correction_table};
|
|
485
471
|
}
|
|
486
472
|
return {...options, transition: transitionTime};
|
|
487
473
|
},
|
|
@@ -499,42 +485,33 @@ function createFromExposes(model, def) {
|
|
|
499
485
|
read: false,
|
|
500
486
|
type: 'string',
|
|
501
487
|
inOptions: true,
|
|
502
|
-
setter: (value, options) =>
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
488
|
+
setter: (value, options) => {
|
|
489
|
+
return {
|
|
490
|
+
hue: options.hue,
|
|
491
|
+
saturation: options.saturation,
|
|
492
|
+
};
|
|
493
|
+
},
|
|
506
494
|
setterOpt: (value, options) => {
|
|
507
495
|
const hasTransitionTime = options && options.hasOwnProperty('transition_time');
|
|
508
496
|
const transitionTime = hasTransitionTime ? options.transition_time : 0;
|
|
509
497
|
const hasHueCalibrationTable = options && options.hasOwnProperty('hue_calibration');
|
|
510
498
|
if (hasHueCalibrationTable)
|
|
511
499
|
try {
|
|
512
|
-
return {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
hue_correction: JSON.parse(options.hue_calibration)
|
|
516
|
-
};
|
|
517
|
-
} catch {
|
|
500
|
+
return {...options, transition: transitionTime, hue_correction: JSON.parse(options.hue_calibration)};
|
|
501
|
+
}
|
|
502
|
+
catch {
|
|
518
503
|
const hue_correction_table = [];
|
|
519
504
|
options.hue_calibration.split(',').forEach(element => {
|
|
520
505
|
const match = /([0-9]+):([0-9]+)/.exec(element);
|
|
521
|
-
if (match && match.length
|
|
522
|
-
hue_correction_table.push({
|
|
523
|
-
in: Number(match[1]),
|
|
524
|
-
out: Number(match[2])
|
|
525
|
-
});
|
|
526
|
-
}
|
|
506
|
+
if (match && match.length ==3)
|
|
507
|
+
hue_correction_table.push({ in: Number(match[1]), out: Number(match[2])});
|
|
527
508
|
});
|
|
528
|
-
if (hue_correction_table.length > 0)
|
|
529
|
-
return {
|
|
530
|
-
...options,
|
|
531
|
-
transition: transitionTime,
|
|
532
|
-
hue_correction: hue_correction_table
|
|
533
|
-
};
|
|
534
|
-
}
|
|
509
|
+
if (hue_correction_table.length > 0)
|
|
510
|
+
return {...options, transition: transitionTime, hue_correction: hue_correction_table};
|
|
535
511
|
}
|
|
536
512
|
return {...options, transition: transitionTime};
|
|
537
513
|
},
|
|
514
|
+
|
|
538
515
|
}, prop.access);
|
|
539
516
|
break;
|
|
540
517
|
}
|
|
@@ -605,20 +582,19 @@ function createFromExposes(model, def) {
|
|
|
605
582
|
break;
|
|
606
583
|
}
|
|
607
584
|
}
|
|
608
|
-
if (state)
|
|
609
|
-
pushToStates(state, expose.access);
|
|
610
|
-
}
|
|
585
|
+
if (state) pushToStates(state, expose.access);
|
|
611
586
|
break;
|
|
612
587
|
|
|
613
588
|
case 'enum':
|
|
614
589
|
switch (expose.name) {
|
|
615
590
|
case 'action': {
|
|
616
|
-
// Ansatz:
|
|
617
591
|
|
|
618
|
-
//
|
|
619
|
-
// Action (bekommt text ausser hold und release, auto reset nach 250 ms)
|
|
620
|
-
// Hold: wird gesetzt bei hold, gelöscht bei passendem Release
|
|
592
|
+
// Ansatz:
|
|
621
593
|
|
|
594
|
+
// Action aufspalten in 2 Blöcke:
|
|
595
|
+
// Action (bekommt text ausser hold und release, auto reset nach 250 ms)
|
|
596
|
+
// Hold: wird gesetzt bei hold, gelöscht bei passendem Release
|
|
597
|
+
|
|
622
598
|
if (!Array.isArray(expose.values)) break;
|
|
623
599
|
const hasHold = expose.values.find((actionName) => actionName.includes('hold'));
|
|
624
600
|
const hasRelease = expose.values.find((actionName) => actionName.includes('release'));
|
|
@@ -637,7 +613,7 @@ function createFromExposes(model, def) {
|
|
|
637
613
|
write: false,
|
|
638
614
|
read: true,
|
|
639
615
|
type: 'boolean',
|
|
640
|
-
getter: payload => payload.action === actionName ? true : (payload.action === releaseActionName ? false : undefined
|
|
616
|
+
getter: payload => (payload.action === actionName) ? true : (payload.action === releaseActionName) ? false : undefined,
|
|
641
617
|
};
|
|
642
618
|
} else {
|
|
643
619
|
state = {
|
|
@@ -649,7 +625,7 @@ function createFromExposes(model, def) {
|
|
|
649
625
|
write: false,
|
|
650
626
|
read: true,
|
|
651
627
|
type: 'boolean',
|
|
652
|
-
getter: payload => payload.action === actionName ? true : undefined,
|
|
628
|
+
getter: payload => (payload.action === actionName) ? true : undefined,
|
|
653
629
|
isEvent: true,
|
|
654
630
|
};
|
|
655
631
|
}
|
|
@@ -700,9 +676,7 @@ function createFromExposes(model, def) {
|
|
|
700
676
|
break;
|
|
701
677
|
}
|
|
702
678
|
}
|
|
703
|
-
if (state)
|
|
704
|
-
pushToStates(state, expose.access);
|
|
705
|
-
}
|
|
679
|
+
if (state) pushToStates(state, expose.access);
|
|
706
680
|
break;
|
|
707
681
|
|
|
708
682
|
case 'text':
|
|
@@ -765,20 +739,22 @@ function createFromExposes(model, def) {
|
|
|
765
739
|
return result;
|
|
766
740
|
};
|
|
767
741
|
st.setattr = expose.property;
|
|
768
|
-
}
|
|
742
|
+
};
|
|
769
743
|
// if we have a composite expose, the payload will be an object {expose.property : {prop.property: value}}
|
|
770
744
|
if (prop.access & ea.STATE) {
|
|
771
745
|
st.getter = payload => {
|
|
772
|
-
if ((payload.hasOwnProperty(expose.property)) && (payload[expose.property]
|
|
773
|
-
return !isNaN(payload[expose.property][prop.property]) ? payload[expose.property][prop.property] : undefined
|
|
774
|
-
}
|
|
746
|
+
if ( (payload.hasOwnProperty(expose.property)) && (payload[expose.property] !== null) && payload[expose.property].hasOwnProperty(prop.property)) {
|
|
747
|
+
return !isNaN(payload[expose.property][prop.property]) ? payload[expose.property][prop.property] : undefined
|
|
748
|
+
}
|
|
749
|
+
else {
|
|
775
750
|
return undefined;
|
|
776
751
|
}
|
|
777
752
|
};
|
|
778
|
-
} else {
|
|
779
|
-
st.getter = payload => undefined;
|
|
780
753
|
}
|
|
781
|
-
|
|
754
|
+
else {
|
|
755
|
+
st.getter = payload => { return undefined };
|
|
756
|
+
}
|
|
757
|
+
;
|
|
782
758
|
pushToStates(st, prop.access);
|
|
783
759
|
}
|
|
784
760
|
break;
|
|
@@ -788,8 +764,8 @@ function createFromExposes(model, def) {
|
|
|
788
764
|
}
|
|
789
765
|
const newDev = {
|
|
790
766
|
models: [model],
|
|
791
|
-
icon,
|
|
792
|
-
states,
|
|
767
|
+
icon: icon,
|
|
768
|
+
states: states,
|
|
793
769
|
exposed: true,
|
|
794
770
|
};
|
|
795
771
|
// make the function code printable in log
|
|
@@ -799,11 +775,11 @@ function createFromExposes(model, def) {
|
|
|
799
775
|
}
|
|
800
776
|
|
|
801
777
|
function applyExposes(mappedDevices, byModel, allExcludesObj) {
|
|
802
|
-
// for
|
|
778
|
+
// for exlude search
|
|
803
779
|
const allExcludesStr = JSON.stringify(allExcludesObj);
|
|
804
780
|
// create or update device from exposes
|
|
805
781
|
for (const deviceDef of zigbeeHerdsmanConverters.definitions) {
|
|
806
|
-
const strippedModel = (deviceDef.model) ? deviceDef.model.replace(
|
|
782
|
+
const strippedModel = (deviceDef.model) ? deviceDef.model.replace(/\0.*$/g, '').trim() : '';
|
|
807
783
|
// check if device is mapped
|
|
808
784
|
const existsMap = byModel.get(strippedModel);
|
|
809
785
|
|
|
@@ -818,7 +794,7 @@ function applyExposes(mappedDevices, byModel, allExcludesObj) {
|
|
|
818
794
|
existsMap.exposed = true;
|
|
819
795
|
}
|
|
820
796
|
} catch (e) {
|
|
821
|
-
console.log(`Wrong expose devicedefinition ${deviceDef.vendor} ${deviceDef.model}`);
|
|
797
|
+
console.log(`Wrong expose devicedefinition ${deviceDef.vendor} ${deviceDef.model}` );
|
|
822
798
|
}
|
|
823
799
|
}
|
|
824
800
|
}
|