iobroker.zigbee 1.6.3 → 1.6.14
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 +68 -14
- package/admin/adapter-settings.js +39 -3
- package/admin/admin.js +273 -47
- package/admin/img/14153905L.png +0 -0
- package/admin/img/81855.png +0 -0
- package/admin/img/HG06338.png +0 -0
- package/admin/img/R7060.png +0 -0
- package/admin/img/TI0001-cover.png +0 -0
- package/admin/img/WHD02.png +0 -0
- package/admin/img/ikea_E1812.png +0 -0
- package/admin/img/tuya_rb280.png +0 -0
- package/admin/index_m.html +170 -24
- package/admin/tab_m.html +181 -216
- package/admin/words.js +30 -29
- package/docs/tutorial/zigbee.png +0 -0
- package/io-package.json +28 -1
- package/lib/commands.js +75 -9
- package/lib/developer.js +6 -2
- package/lib/devices.js +46 -1
- package/lib/exposes.js +3 -1
- package/lib/groups.js +54 -28
- package/lib/ota.js +26 -0
- package/lib/rgb.js +30 -0
- package/lib/states.js +40 -14
- package/lib/statescontroller.js +207 -92
- package/lib/utils.js +1 -1
- package/lib/zbBaseExtension.js +4 -3
- package/lib/zbDelayedAction.js +1 -0
- package/lib/zbDeviceAvailability.js +14 -2
- package/lib/zbDeviceConfigure.js +11 -7
- package/lib/zbDeviceEvent.js +6 -0
- package/lib/zigbeecontroller.js +119 -17
- package/main.js +12 -4
- package/package.json +3 -3
package/lib/statescontroller.js
CHANGED
|
@@ -4,6 +4,9 @@ const EventEmitter = require('events').EventEmitter;
|
|
|
4
4
|
const statesMapping = require('./devices');
|
|
5
5
|
const getAdId = require('./utils').getAdId;
|
|
6
6
|
const getZbId = require('./utils').getZbId;
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
var savedDeviceNames = {};
|
|
9
|
+
var knownUndefinedDevices = {};
|
|
7
10
|
|
|
8
11
|
|
|
9
12
|
class StatesController extends EventEmitter {
|
|
@@ -13,6 +16,13 @@ class StatesController extends EventEmitter {
|
|
|
13
16
|
this.adapter.on('stateChange', this.onStateChange.bind(this));
|
|
14
17
|
this.query_device_block = [];
|
|
15
18
|
this.debugDevices = undefined;
|
|
19
|
+
let fn = adapter.expandFileName('dev_names.json');
|
|
20
|
+
this.dev_names_fn = fn.replace('.', '_');
|
|
21
|
+
fs.readFile(this.dev_names_fn, (err, data) => {
|
|
22
|
+
if (!err) {
|
|
23
|
+
savedDeviceNames = JSON.parse(data);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
16
26
|
}
|
|
17
27
|
|
|
18
28
|
info(message, data) {
|
|
@@ -35,6 +45,16 @@ class StatesController extends EventEmitter {
|
|
|
35
45
|
this.adapter.sendError(error, message);
|
|
36
46
|
}
|
|
37
47
|
|
|
48
|
+
retainDeviceNames()
|
|
49
|
+
{
|
|
50
|
+
fs.writeFile(this.dev_names_fn, JSON.stringify(savedDeviceNames, null, 2), (err) => {
|
|
51
|
+
if (err)
|
|
52
|
+
this.error('error saving device names: ' + JSON.Stringify(err));
|
|
53
|
+
else
|
|
54
|
+
this.debug('saved device names');
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
38
58
|
getDebugDevices() {
|
|
39
59
|
this.debugDevices = [];
|
|
40
60
|
this.adapter.getState(this.adapter.namespace + '.info.debugmessages', (err, state) => {
|
|
@@ -55,8 +75,20 @@ class StatesController extends EventEmitter {
|
|
|
55
75
|
});
|
|
56
76
|
}
|
|
57
77
|
});
|
|
58
|
-
|
|
78
|
+
this.adapter.setObject('info.undefinedDevices', {
|
|
79
|
+
'type': 'state',
|
|
80
|
+
'common': {
|
|
81
|
+
'name': 'Recorded undefined devices',
|
|
82
|
+
'role': '',
|
|
83
|
+
'type': 'string',
|
|
84
|
+
'read': true,
|
|
85
|
+
'write': false,
|
|
86
|
+
},
|
|
87
|
+
'native': {},
|
|
88
|
+
});
|
|
89
|
+
this.adapter.setStateAsync(`info.undefinedDevices`, JSON.stringify(knownUndefinedDevices), true);
|
|
59
90
|
}
|
|
91
|
+
|
|
60
92
|
onStateChange(id, state){
|
|
61
93
|
if (!this.adapter.zbController || !this.adapter.zbController.connected()) return;
|
|
62
94
|
if (this.debugDevices === undefined) this.getDebugDevices();
|
|
@@ -91,6 +123,10 @@ class StatesController extends EventEmitter {
|
|
|
91
123
|
if (obj) {
|
|
92
124
|
const model = obj.common.type;
|
|
93
125
|
if (!model) return;
|
|
126
|
+
if (obj.common.deactivated) {
|
|
127
|
+
this.debug('State Change detected on deactivated Device - ignored');
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
94
130
|
if (model === 'group') {
|
|
95
131
|
deviceId = parseInt(deviceId.replace('0xgroup_', ''));
|
|
96
132
|
}
|
|
@@ -116,20 +152,25 @@ class StatesController extends EventEmitter {
|
|
|
116
152
|
return;
|
|
117
153
|
}
|
|
118
154
|
let cnt = 0;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
155
|
+
try {
|
|
156
|
+
const len = states.length;
|
|
157
|
+
states.forEach(statedesc => {
|
|
158
|
+
const id = this.adapter.namespace + '.' + devId + '.' + statedesc.id;
|
|
159
|
+
this.adapter.getState(id, (err, state) => {
|
|
160
|
+
cnt = cnt + 1;
|
|
161
|
+
if (!err && state) {
|
|
162
|
+
result[statedesc.id] = state.val;
|
|
163
|
+
}
|
|
164
|
+
if (cnt === len) {
|
|
165
|
+
callback(result);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
130
168
|
});
|
|
131
|
-
|
|
132
|
-
|
|
169
|
+
if (!len) callback(result);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
this.sendError(error);
|
|
172
|
+
this.error(`Error collectOptions for ${devId}. Error: ${error.stack}`);
|
|
173
|
+
}
|
|
133
174
|
}
|
|
134
175
|
|
|
135
176
|
async getDevStates(deviceId, model) {
|
|
@@ -141,7 +182,15 @@ class StatesController extends EventEmitter {
|
|
|
141
182
|
} else {
|
|
142
183
|
stateModel = statesMapping.findModel(model);
|
|
143
184
|
if (!stateModel) {
|
|
144
|
-
|
|
185
|
+
if (knownUndefinedDevices[deviceId])
|
|
186
|
+
{
|
|
187
|
+
knownUndefinedDevices[deviceId]++;
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
knownUndefinedDevices[deviceId] = 1;
|
|
191
|
+
this.error('Device ' + deviceId + ' "' + model + '" not described in statesMapping.');
|
|
192
|
+
}
|
|
193
|
+
this.adapter.setStateAsync(`info.undefinedDevices`, JSON.stringify(knownUndefinedDevices), true);
|
|
145
194
|
states = statesMapping.commonStates;
|
|
146
195
|
} else {
|
|
147
196
|
states = stateModel.states;
|
|
@@ -221,9 +270,28 @@ class StatesController extends EventEmitter {
|
|
|
221
270
|
}
|
|
222
271
|
|
|
223
272
|
renameDevice(id, newName) {
|
|
273
|
+
this.storeDeviceName(id, newName);
|
|
224
274
|
this.adapter.extendObject(id, {common: {name: newName}});
|
|
225
275
|
}
|
|
226
276
|
|
|
277
|
+
setDeviceActivated(id, active) {
|
|
278
|
+
this.adapter.extendObject(id, {common: {deactivated: active }})
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
storeDeviceName(id, name) {
|
|
282
|
+
savedDeviceNames[id.replace(`${this.adapter.namespace}.`, '')] = name;
|
|
283
|
+
this.retainDeviceNames();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
verifyDeviceName(id, name) {
|
|
287
|
+
const savedId = id.replace(`${this.adapter.namespace}.`, '');
|
|
288
|
+
if (!savedDeviceNames.hasOwnProperty(savedId)) {
|
|
289
|
+
savedDeviceNames[savedId] = name;
|
|
290
|
+
this.retainDeviceNames();
|
|
291
|
+
}
|
|
292
|
+
return savedDeviceNames[savedId];
|
|
293
|
+
}
|
|
294
|
+
|
|
227
295
|
deleteDeviceStates(devId, callback) {
|
|
228
296
|
this.adapter.getStatesOf(devId, (err, states) => {
|
|
229
297
|
if (!err && states) {
|
|
@@ -236,6 +304,15 @@ class StatesController extends EventEmitter {
|
|
|
236
304
|
});
|
|
237
305
|
});
|
|
238
306
|
}
|
|
307
|
+
|
|
308
|
+
async deleteDeviceStatesAsync(devId) {
|
|
309
|
+
const states = await this.adapter.getStatesOf(devId);
|
|
310
|
+
if (states) {
|
|
311
|
+
await this.adapter.deleteState(devId, null, state._id);
|
|
312
|
+
}
|
|
313
|
+
await this.adapter.deleteDevice(devId);
|
|
314
|
+
}
|
|
315
|
+
|
|
239
316
|
// eslint-disable-next-line no-unused-vars
|
|
240
317
|
async deleteOrphanedDeviceStates(ieeeAddr, model, force, callback) {
|
|
241
318
|
const devStates = await this.getDevStates(ieeeAddr, model);
|
|
@@ -254,7 +331,7 @@ class StatesController extends EventEmitter {
|
|
|
254
331
|
statename = arr[1];
|
|
255
332
|
}
|
|
256
333
|
if (commonStates.find((statedesc) => statename === statedesc.id) === undefined &&
|
|
257
|
-
devStates.states.find((statedesc) => statename === statedesc.id) === undefined
|
|
334
|
+
devStates.states.find((statedesc) => statename === statedesc.id) === undefined) {
|
|
258
335
|
if (state.common.hasOwnProperty('custom') && !force) {
|
|
259
336
|
this.info(`keeping disconnected state ${JSON.stringify(statename)} of ${devId} `);
|
|
260
337
|
} else {
|
|
@@ -278,88 +355,127 @@ class StatesController extends EventEmitter {
|
|
|
278
355
|
updateState(devId, name, value, common) {
|
|
279
356
|
this.adapter.getObject(devId, (err, obj) => {
|
|
280
357
|
if (obj) {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
if (common
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
358
|
+
if (!obj.common.deactivated) {
|
|
359
|
+
const new_common = {name: name};
|
|
360
|
+
const id = devId + '.' + name;
|
|
361
|
+
const new_name = obj.common.name;
|
|
362
|
+
if (common) {
|
|
363
|
+
if (common.name !== undefined) {
|
|
364
|
+
new_common.name = common.name;
|
|
365
|
+
}
|
|
366
|
+
if (common.type !== undefined) {
|
|
367
|
+
new_common.type = common.type;
|
|
368
|
+
}
|
|
369
|
+
if (common.unit !== undefined) {
|
|
370
|
+
new_common.unit = common.unit;
|
|
371
|
+
}
|
|
372
|
+
if (common.states !== undefined) {
|
|
373
|
+
new_common.states = common.states;
|
|
374
|
+
}
|
|
375
|
+
if (common.read !== undefined) {
|
|
376
|
+
new_common.read = common.read;
|
|
377
|
+
}
|
|
378
|
+
if (common.write !== undefined) {
|
|
379
|
+
new_common.write = common.write;
|
|
380
|
+
}
|
|
381
|
+
if (common.role !== undefined) {
|
|
382
|
+
new_common.role = common.role;
|
|
383
|
+
}
|
|
384
|
+
if (common.min !== undefined) {
|
|
385
|
+
new_common.min = common.min;
|
|
386
|
+
}
|
|
387
|
+
if (common.max !== undefined) {
|
|
388
|
+
new_common.max = common.max;
|
|
389
|
+
}
|
|
390
|
+
if (common.icon !== undefined) {
|
|
391
|
+
new_common.icon = common.icon;
|
|
392
|
+
}
|
|
314
393
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
394
|
+
// check if state exist
|
|
395
|
+
this.adapter.getObject(id, (err, stobj) => {
|
|
396
|
+
let hasChanges = false;
|
|
397
|
+
if (stobj) {
|
|
398
|
+
// update state - not change name and role (user can it changed)
|
|
399
|
+
if (stobj.common.name)
|
|
400
|
+
delete new_common.name;
|
|
401
|
+
else
|
|
402
|
+
new_common.name = new_name + ' ' + new_common.name;
|
|
403
|
+
delete new_common.role;
|
|
404
|
+
|
|
405
|
+
// check whether any common property is different
|
|
406
|
+
if (stobj.common) {
|
|
407
|
+
for (const property in new_common) {
|
|
408
|
+
if (stobj.common.hasOwnProperty(property)) {
|
|
409
|
+
if (stobj.common[property] === new_common[property]) {
|
|
410
|
+
delete new_common[property];
|
|
411
|
+
} else {
|
|
412
|
+
hasChanges = true;
|
|
413
|
+
}
|
|
335
414
|
}
|
|
336
415
|
}
|
|
337
416
|
}
|
|
417
|
+
} else {
|
|
418
|
+
hasChanges = true;
|
|
338
419
|
}
|
|
339
|
-
} else {
|
|
340
|
-
hasChanges = true;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// only change object when any common property has changed
|
|
344
|
-
if (hasChanges) {
|
|
345
|
-
this.adapter.extendObject(id, {type: 'state', common: new_common, native: {} }, () => {
|
|
346
|
-
value !== undefined && this.adapter.setState(id, value, true);
|
|
347
|
-
});
|
|
348
|
-
} else if (value !== undefined) {
|
|
349
|
-
if (typeof(value) !== 'object') {
|
|
350
|
-
this.adapter.setState(id, value, true);
|
|
351
|
-
} else this.warn('set state with object for id :' + JSON.stringify(id) + ' '+ JSON.stringify(value));
|
|
352
420
|
|
|
353
|
-
|
|
421
|
+
// only change object when any common property has changed
|
|
422
|
+
if (hasChanges) {
|
|
423
|
+
this.adapter.extendObject(id, {type: 'state', common: new_common, native: {} }, () => {
|
|
424
|
+
value !== undefined && this.setState_typed(id, value, true, (stobj) ? stobj.common.type : new_common.type);
|
|
425
|
+
});
|
|
426
|
+
} else if (value !== undefined) {
|
|
427
|
+
this.setState_typed(id, value, true, stobj.common.type);
|
|
428
|
+
}
|
|
354
429
|
|
|
355
|
-
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
else this.debug(`UpdateState: Device is deactivated ${devId} ${JSON.stringify(obj)}`);
|
|
356
433
|
} else {
|
|
357
|
-
this.debug(`
|
|
434
|
+
this.debug(`UpdateState: missing device ${devId} ${JSON.stringify(obj)}`);
|
|
358
435
|
}
|
|
359
436
|
});
|
|
360
437
|
}
|
|
361
438
|
|
|
439
|
+
setState_typed(id, value, ack, type, callback)
|
|
440
|
+
{
|
|
441
|
+
// never set a null or undefined value
|
|
442
|
+
if (value === null || value === undefined) return;
|
|
443
|
+
if (!type) {
|
|
444
|
+
this.debug("SetState_typed called without type");
|
|
445
|
+
// identify datatype, recursively call this function with set datatype
|
|
446
|
+
this.adapter.getObject(id, (err, obj) => {
|
|
447
|
+
if (obj && obj.common)
|
|
448
|
+
this.setState_typed(id, value, ack, obj.common.type, callback);
|
|
449
|
+
else {
|
|
450
|
+
this.setState_typed(id, value, ack, 'noobj', callback);
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
if (typeof value != type) {
|
|
456
|
+
this.debug("SetState_typed : converting " + JSON.stringify(value) + " for " + id + " from " + typeof value + " to " + type);
|
|
457
|
+
switch (type) {
|
|
458
|
+
case 'number':
|
|
459
|
+
value = parseFloat(value);
|
|
460
|
+
if (isNaN (value)) value = 0;
|
|
461
|
+
break;
|
|
462
|
+
case 'string':
|
|
463
|
+
case 'text': value = JSON.stringify(value); break;
|
|
464
|
+
case 'boolean':
|
|
465
|
+
if (typeof value == 'number') {
|
|
466
|
+
value = (value != 0);
|
|
467
|
+
break;
|
|
468
|
+
}
|
|
469
|
+
const sval = JSON.stringify(value).toLowerCase().trim();
|
|
470
|
+
value = (sval == 'true' || sval == 'yes' || sval == 'on');
|
|
471
|
+
break;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
this.adapter.setState(id, value, ack, callback);
|
|
475
|
+
}
|
|
476
|
+
|
|
362
477
|
updateDev(dev_id, dev_name, model, callback) {
|
|
478
|
+
const __dev_name = this.verifyDeviceName(dev_id, dev_name);
|
|
363
479
|
const id = '' + dev_id;
|
|
364
480
|
const modelDesc = statesMapping.findModel(model);
|
|
365
481
|
let icon = (modelDesc && modelDesc.icon) ? modelDesc.icon : 'img/unknown.png';
|
|
@@ -368,7 +484,7 @@ class StatesController extends EventEmitter {
|
|
|
368
484
|
this.adapter.setObjectNotExists(id, {
|
|
369
485
|
type: 'device',
|
|
370
486
|
// actually this is an error, so device.common has no attribute type. It must be in native part
|
|
371
|
-
common: {name:
|
|
487
|
+
common: {name: __dev_name, type: model, icon: icon},
|
|
372
488
|
native: {id: dev_id}
|
|
373
489
|
}, () => {
|
|
374
490
|
// update type and icon
|
|
@@ -377,15 +493,13 @@ class StatesController extends EventEmitter {
|
|
|
377
493
|
}
|
|
378
494
|
|
|
379
495
|
async syncDevStates(dev, model) {
|
|
380
|
-
const devId = dev.ieeeAddr.substr(2)
|
|
381
|
-
hasGroups = dev.type === 'Router';
|
|
496
|
+
const devId = dev.ieeeAddr.substr(2);
|
|
382
497
|
// devId - iobroker device id
|
|
383
498
|
const devStates = await this.getDevStates(dev.ieeeAddr, model);
|
|
384
499
|
if (!devStates) {
|
|
385
500
|
return;
|
|
386
501
|
}
|
|
387
|
-
const states = statesMapping.commonStates.concat(devStates.states)
|
|
388
|
-
.concat((hasGroups) ? [statesMapping.groupsState] : []);
|
|
502
|
+
const states = statesMapping.commonStates.concat(devStates.states);
|
|
389
503
|
|
|
390
504
|
for (const stateInd in states) {
|
|
391
505
|
if (!states.hasOwnProperty(stateInd)) continue;
|
|
@@ -432,6 +546,7 @@ class StatesController extends EventEmitter {
|
|
|
432
546
|
for (const addressPart of this.debugDevices) {
|
|
433
547
|
if (typeof(devId) == 'string' && devId.indexOf(addressPart) > -1)
|
|
434
548
|
{
|
|
549
|
+
if (payload.hasOwnProperty('msg_from_zigbee')) break;
|
|
435
550
|
this.warn(`ELEVATED publishToState: message received '${JSON.stringify(payload)}' from device ${devId} type '${model}'`);
|
|
436
551
|
has_debug = true;
|
|
437
552
|
break;
|
|
@@ -454,10 +569,10 @@ class StatesController extends EventEmitter {
|
|
|
454
569
|
value = payload[statedesc.prop || statedesc.id];
|
|
455
570
|
}
|
|
456
571
|
// checking value
|
|
457
|
-
if (value === undefined) continue;
|
|
572
|
+
if (value === undefined || value === null) continue;
|
|
458
573
|
let stateID = statedesc.id;
|
|
459
574
|
|
|
460
|
-
if (has_debug) {
|
|
575
|
+
if (has_debug && statedesc.id != 'msg_from_zigbee') {
|
|
461
576
|
this.warn(`ELEVATED publishToState: value generated '${JSON.stringify(value)}' from device ${devId} for '${statedesc.name}'`);
|
|
462
577
|
}
|
|
463
578
|
|
package/lib/utils.js
CHANGED
|
@@ -115,7 +115,7 @@ const xiaomiManufacturerID = [4151, 4447];
|
|
|
115
115
|
const ikeaTradfriManufacturerID = [4476];
|
|
116
116
|
|
|
117
117
|
function sanitizeImageParameter(parameter) {
|
|
118
|
-
const replaceByDash = [/\?/g, /&/g, /[^a-z\d\-
|
|
118
|
+
const replaceByDash = [/\?/g, /&/g, /[^a-z\d\-_./:]/gi, /[/]/gi];
|
|
119
119
|
let sanitized = parameter;
|
|
120
120
|
replaceByDash.forEach((r) => sanitized = sanitized.replace(r, '-'));
|
|
121
121
|
return sanitized;
|
package/lib/zbBaseExtension.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
class BaseExtension {
|
|
6
6
|
constructor(zigbee, options) {
|
|
7
7
|
this.zigbee = zigbee;
|
|
8
|
+
this.name = 'BaseExtension';
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
info(message, data) {
|
|
@@ -12,15 +13,15 @@ class BaseExtension {
|
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
error(message, data) {
|
|
15
|
-
this.zigbee.error(message, data);
|
|
16
|
+
this.zigbee.error(this.name + ':' + message, data);
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
warn(message, data) {
|
|
19
|
-
this.zigbee.warn(message, data);
|
|
20
|
+
this.zigbee.warn(this.name + ':' + message, data);
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
debug(message, data) {
|
|
23
|
-
this.zigbee.debug(message, data);
|
|
24
|
+
this.zigbee.debug(this.name + ':' + message, data);
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
sendError(error, message) {
|
package/lib/zbDelayedAction.js
CHANGED
|
@@ -39,6 +39,7 @@ class DeviceAvailability extends BaseExtension {
|
|
|
39
39
|
this.state = {};
|
|
40
40
|
this.active_ping = true;
|
|
41
41
|
this.forced_ping = true;
|
|
42
|
+
this.forcedNonPingable = {};
|
|
42
43
|
this.number_of_registered_devices = 0;
|
|
43
44
|
// force publish availability for new devices
|
|
44
45
|
this.zigbee.on('new', (entity) => {
|
|
@@ -50,6 +51,7 @@ class DeviceAvailability extends BaseExtension {
|
|
|
50
51
|
this.startDevicePingQueue = []; // simple fifo array for starting device pings
|
|
51
52
|
this.startDevicePingTimeout = null; // handle for the timeout which empties the queue
|
|
52
53
|
this.startDevicePingDelay = 200; // 200 ms delay between starting the ping timeout
|
|
54
|
+
this.name = "DeviceAvailability";
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
setOptions(options) {
|
|
@@ -80,6 +82,8 @@ class DeviceAvailability extends BaseExtension {
|
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
async registerDevicePing(device, entity) {
|
|
85
|
+
this.debug('register device Ping for ' + JSON.stringify(device.ieeeAddr));
|
|
86
|
+
this.forcedNonPingable[device.ieeeAddr] = false;
|
|
83
87
|
// this.warn(`Called registerDevicePing for '${device}' of '${entity}'`);
|
|
84
88
|
if (!this.isPingable(device)) return;
|
|
85
89
|
// ensure we do not already have this device in the queue
|
|
@@ -96,6 +100,14 @@ class DeviceAvailability extends BaseExtension {
|
|
|
96
100
|
}, this.startDevicePingDelay);
|
|
97
101
|
}
|
|
98
102
|
|
|
103
|
+
async deregisterDevicePing(device) {
|
|
104
|
+
this.info('deregister device Ping for deactivated device ' + JSON.stringify(device.ieeeAddr));
|
|
105
|
+
this.forcedNonPingable[device.ieeeAddr] = true;
|
|
106
|
+
if (this.timers[device.ieeeAddr]) {
|
|
107
|
+
clearTimeout(this.timers[device.ieeeAddr]);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
99
111
|
async startDevicePing() {
|
|
100
112
|
// this.warn(JSON.stringify(this));
|
|
101
113
|
this.startDevicePingTimeout = null;
|
|
@@ -265,10 +277,10 @@ class DeviceAvailability extends BaseExtension {
|
|
|
265
277
|
// }
|
|
266
278
|
}
|
|
267
279
|
}
|
|
268
|
-
|
|
280
|
+
|
|
269
281
|
onZigbeeEvent(data) {
|
|
270
282
|
const device = data.device;
|
|
271
|
-
if (!device) {
|
|
283
|
+
if (!device || this.forcedNonPingable[device.ieeeAddr]) {
|
|
272
284
|
return;
|
|
273
285
|
}
|
|
274
286
|
|
package/lib/zbDeviceConfigure.js
CHANGED
|
@@ -18,6 +18,7 @@ class DeviceConfigure extends BaseExtension {
|
|
|
18
18
|
|
|
19
19
|
this.configuring = new Set();
|
|
20
20
|
this.attempts = {};
|
|
21
|
+
this.name = "DeviceConfigure";
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
setOptions(options) {
|
|
@@ -119,13 +120,8 @@ class DeviceConfigure extends BaseExtension {
|
|
|
119
120
|
if (!this.attempts.hasOwnProperty(device.ieeeAddr)) {
|
|
120
121
|
this.attempts[device.ieeeAddr] = 0;
|
|
121
122
|
}
|
|
122
|
-
|
|
123
|
-
this.info(`Configuring ${device.ieeeAddr} ${device.modelID}`);
|
|
124
123
|
try {
|
|
125
|
-
await
|
|
126
|
-
this.info(`DeviceConfigure successful ${device.ieeeAddr} ${device.modelID}`);
|
|
127
|
-
device.meta.configured = zigbeeHerdsmanConverters.getConfigureKey(mappedDevice);
|
|
128
|
-
device.save();
|
|
124
|
+
await this.doConfigure(device, mappedDevice);
|
|
129
125
|
} catch (error) {
|
|
130
126
|
this.sendError(error);
|
|
131
127
|
this.warn(
|
|
@@ -134,7 +130,6 @@ class DeviceConfigure extends BaseExtension {
|
|
|
134
130
|
);
|
|
135
131
|
this.attempts[device.ieeeAddr]++;
|
|
136
132
|
}
|
|
137
|
-
|
|
138
133
|
this.configuring.delete(device.ieeeAddr);
|
|
139
134
|
} catch (error) {
|
|
140
135
|
this.sendError(error);
|
|
@@ -143,6 +138,15 @@ class DeviceConfigure extends BaseExtension {
|
|
|
143
138
|
);
|
|
144
139
|
}
|
|
145
140
|
}
|
|
141
|
+
|
|
142
|
+
async doConfigure(device, mappedDevice) {
|
|
143
|
+
this.info(`Configuring ${device.ieeeAddr} ${device.modelID}`);
|
|
144
|
+
const coordinatorEndpoint = await this.zigbee.getDevicesByType('Coordinator')[0].endpoints[0];
|
|
145
|
+
await mappedDevice.configure(device, coordinatorEndpoint);
|
|
146
|
+
device.meta.configured = zigbeeHerdsmanConverters.getConfigureKey(mappedDevice);
|
|
147
|
+
device.save();
|
|
148
|
+
this.info(`DeviceConfigure successful ${device.ieeeAddr} ${device.modelID}`);
|
|
149
|
+
}
|
|
146
150
|
}
|
|
147
151
|
|
|
148
152
|
module.exports = DeviceConfigure;
|
package/lib/zbDeviceEvent.js
CHANGED
|
@@ -4,6 +4,12 @@ const BaseExtension = require('./zbBaseExtension');
|
|
|
4
4
|
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
|
|
5
5
|
|
|
6
6
|
class DeviceEvent extends BaseExtension {
|
|
7
|
+
constructor(zigbee, options) {
|
|
8
|
+
super(zigbee, options);
|
|
9
|
+
this.name = "DeviceEvent";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
7
13
|
async onZigbeeStarted() {
|
|
8
14
|
for (const device of await this.zigbee.getClients()) {
|
|
9
15
|
this.callOnEvent(device, 'start', {});
|