iobroker.zigbee 1.6.6 → 1.6.15
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 +73 -14
- package/admin/admin.js +261 -39
- 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/index_m.html +165 -19
- package/admin/tab_m.html +161 -211
- package/admin/words.js +29 -28
- package/docs/tutorial/zigbee.png +0 -0
- package/io-package.json +36 -2
- package/lib/commands.js +75 -9
- package/lib/developer.js +6 -2
- package/lib/devices.js +41 -2
- package/lib/exposes.js +16 -10
- 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 +217 -92
- 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 +8 -4
- package/package.json +11 -8
- package/.devcontainer/devcontainer.json +0 -36
- package/.devcontainer/docker-compose.yml +0 -51
- package/.devcontainer/iobroker/Dockerfile +0 -2
- package/.devcontainer/nginx/nginx.conf +0 -33
- package/.devcontainer/parcel/Dockerfile +0 -9
- package/.devcontainer/parcel/run.sh +0 -7
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,21 @@ 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
|
+
this.retTimeoutHandle = null;
|
|
22
|
+
fs.readFile(this.dev_names_fn, (err, data) => {
|
|
23
|
+
if (!err) {
|
|
24
|
+
try
|
|
25
|
+
{
|
|
26
|
+
savedDeviceNames = JSON.parse(data);
|
|
27
|
+
}
|
|
28
|
+
catch
|
|
29
|
+
{
|
|
30
|
+
savedDeviceNames = {};
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
});
|
|
16
34
|
}
|
|
17
35
|
|
|
18
36
|
info(message, data) {
|
|
@@ -35,6 +53,18 @@ class StatesController extends EventEmitter {
|
|
|
35
53
|
this.adapter.sendError(error, message);
|
|
36
54
|
}
|
|
37
55
|
|
|
56
|
+
retainDeviceNames()
|
|
57
|
+
{
|
|
58
|
+
clearTimeout(this.retTimeoutHandle);
|
|
59
|
+
this.retTimeoutHanlde = setTimeout(()=> {
|
|
60
|
+
fs.writeFile(this.dev_names_fn, JSON.stringify(savedDeviceNames, null, 2), (err) => {
|
|
61
|
+
if (err)
|
|
62
|
+
this.error('error saving device names: ' + JSON.Stringify(err));
|
|
63
|
+
else
|
|
64
|
+
this.debug('saved device names');
|
|
65
|
+
});},5000);
|
|
66
|
+
}
|
|
67
|
+
|
|
38
68
|
getDebugDevices() {
|
|
39
69
|
this.debugDevices = [];
|
|
40
70
|
this.adapter.getState(this.adapter.namespace + '.info.debugmessages', (err, state) => {
|
|
@@ -55,8 +85,20 @@ class StatesController extends EventEmitter {
|
|
|
55
85
|
});
|
|
56
86
|
}
|
|
57
87
|
});
|
|
58
|
-
|
|
88
|
+
this.adapter.setObject('info.undefinedDevices', {
|
|
89
|
+
'type': 'state',
|
|
90
|
+
'common': {
|
|
91
|
+
'name': 'Recorded undefined devices',
|
|
92
|
+
'role': '',
|
|
93
|
+
'type': 'string',
|
|
94
|
+
'read': true,
|
|
95
|
+
'write': false,
|
|
96
|
+
},
|
|
97
|
+
'native': {},
|
|
98
|
+
});
|
|
99
|
+
this.adapter.setStateAsync(`info.undefinedDevices`, JSON.stringify(knownUndefinedDevices), true);
|
|
59
100
|
}
|
|
101
|
+
|
|
60
102
|
onStateChange(id, state){
|
|
61
103
|
if (!this.adapter.zbController || !this.adapter.zbController.connected()) return;
|
|
62
104
|
if (this.debugDevices === undefined) this.getDebugDevices();
|
|
@@ -91,6 +133,10 @@ class StatesController extends EventEmitter {
|
|
|
91
133
|
if (obj) {
|
|
92
134
|
const model = obj.common.type;
|
|
93
135
|
if (!model) return;
|
|
136
|
+
if (obj.common.deactivated) {
|
|
137
|
+
this.debug('State Change detected on deactivated Device - ignored');
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
94
140
|
if (model === 'group') {
|
|
95
141
|
deviceId = parseInt(deviceId.replace('0xgroup_', ''));
|
|
96
142
|
}
|
|
@@ -116,20 +162,25 @@ class StatesController extends EventEmitter {
|
|
|
116
162
|
return;
|
|
117
163
|
}
|
|
118
164
|
let cnt = 0;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
165
|
+
try {
|
|
166
|
+
const len = states.length;
|
|
167
|
+
states.forEach(statedesc => {
|
|
168
|
+
const id = this.adapter.namespace + '.' + devId + '.' + statedesc.id;
|
|
169
|
+
this.adapter.getState(id, (err, state) => {
|
|
170
|
+
cnt = cnt + 1;
|
|
171
|
+
if (!err && state) {
|
|
172
|
+
result[statedesc.id] = state.val;
|
|
173
|
+
}
|
|
174
|
+
if (cnt === len) {
|
|
175
|
+
callback(result);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
130
178
|
});
|
|
131
|
-
|
|
132
|
-
|
|
179
|
+
if (!len) callback(result);
|
|
180
|
+
} catch (error) {
|
|
181
|
+
this.sendError(error);
|
|
182
|
+
this.error(`Error collectOptions for ${devId}. Error: ${error.stack}`);
|
|
183
|
+
}
|
|
133
184
|
}
|
|
134
185
|
|
|
135
186
|
async getDevStates(deviceId, model) {
|
|
@@ -141,7 +192,15 @@ class StatesController extends EventEmitter {
|
|
|
141
192
|
} else {
|
|
142
193
|
stateModel = statesMapping.findModel(model);
|
|
143
194
|
if (!stateModel) {
|
|
144
|
-
|
|
195
|
+
if (knownUndefinedDevices[deviceId])
|
|
196
|
+
{
|
|
197
|
+
knownUndefinedDevices[deviceId]++;
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
knownUndefinedDevices[deviceId] = 1;
|
|
201
|
+
this.error('Device ' + deviceId + ' "' + model + '" not described in statesMapping.');
|
|
202
|
+
}
|
|
203
|
+
this.adapter.setStateAsync(`info.undefinedDevices`, JSON.stringify(knownUndefinedDevices), true);
|
|
145
204
|
states = statesMapping.commonStates;
|
|
146
205
|
} else {
|
|
147
206
|
states = stateModel.states;
|
|
@@ -221,9 +280,28 @@ class StatesController extends EventEmitter {
|
|
|
221
280
|
}
|
|
222
281
|
|
|
223
282
|
renameDevice(id, newName) {
|
|
283
|
+
this.storeDeviceName(id, newName);
|
|
224
284
|
this.adapter.extendObject(id, {common: {name: newName}});
|
|
225
285
|
}
|
|
226
286
|
|
|
287
|
+
setDeviceActivated(id, active) {
|
|
288
|
+
this.adapter.extendObject(id, {common: {deactivated: active }})
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
storeDeviceName(id, name) {
|
|
292
|
+
savedDeviceNames[id.replace(`${this.adapter.namespace}.`, '')] = name;
|
|
293
|
+
this.retainDeviceNames();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
verifyDeviceName(id, name) {
|
|
297
|
+
const savedId = id.replace(`${this.adapter.namespace}.`, '');
|
|
298
|
+
if (!savedDeviceNames.hasOwnProperty(savedId)) {
|
|
299
|
+
savedDeviceNames[savedId] = name;
|
|
300
|
+
this.retainDeviceNames();
|
|
301
|
+
}
|
|
302
|
+
return savedDeviceNames[savedId];
|
|
303
|
+
}
|
|
304
|
+
|
|
227
305
|
deleteDeviceStates(devId, callback) {
|
|
228
306
|
this.adapter.getStatesOf(devId, (err, states) => {
|
|
229
307
|
if (!err && states) {
|
|
@@ -236,6 +314,15 @@ class StatesController extends EventEmitter {
|
|
|
236
314
|
});
|
|
237
315
|
});
|
|
238
316
|
}
|
|
317
|
+
|
|
318
|
+
async deleteDeviceStatesAsync(devId) {
|
|
319
|
+
const states = await this.adapter.getStatesOf(devId);
|
|
320
|
+
if (states) {
|
|
321
|
+
await this.adapter.deleteState(devId, null, state._id);
|
|
322
|
+
}
|
|
323
|
+
await this.adapter.deleteDevice(devId);
|
|
324
|
+
}
|
|
325
|
+
|
|
239
326
|
// eslint-disable-next-line no-unused-vars
|
|
240
327
|
async deleteOrphanedDeviceStates(ieeeAddr, model, force, callback) {
|
|
241
328
|
const devStates = await this.getDevStates(ieeeAddr, model);
|
|
@@ -254,7 +341,7 @@ class StatesController extends EventEmitter {
|
|
|
254
341
|
statename = arr[1];
|
|
255
342
|
}
|
|
256
343
|
if (commonStates.find((statedesc) => statename === statedesc.id) === undefined &&
|
|
257
|
-
devStates.states.find((statedesc) => statename === statedesc.id) === undefined
|
|
344
|
+
devStates.states.find((statedesc) => statename === statedesc.id) === undefined) {
|
|
258
345
|
if (state.common.hasOwnProperty('custom') && !force) {
|
|
259
346
|
this.info(`keeping disconnected state ${JSON.stringify(statename)} of ${devId} `);
|
|
260
347
|
} else {
|
|
@@ -278,88 +365,127 @@ class StatesController extends EventEmitter {
|
|
|
278
365
|
updateState(devId, name, value, common) {
|
|
279
366
|
this.adapter.getObject(devId, (err, obj) => {
|
|
280
367
|
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
|
-
|
|
368
|
+
if (!obj.common.deactivated) {
|
|
369
|
+
const new_common = {name: name};
|
|
370
|
+
const id = devId + '.' + name;
|
|
371
|
+
const new_name = obj.common.name;
|
|
372
|
+
if (common) {
|
|
373
|
+
if (common.name !== undefined) {
|
|
374
|
+
new_common.name = common.name;
|
|
375
|
+
}
|
|
376
|
+
if (common.type !== undefined) {
|
|
377
|
+
new_common.type = common.type;
|
|
378
|
+
}
|
|
379
|
+
if (common.unit !== undefined) {
|
|
380
|
+
new_common.unit = common.unit;
|
|
381
|
+
}
|
|
382
|
+
if (common.states !== undefined) {
|
|
383
|
+
new_common.states = common.states;
|
|
384
|
+
}
|
|
385
|
+
if (common.read !== undefined) {
|
|
386
|
+
new_common.read = common.read;
|
|
387
|
+
}
|
|
388
|
+
if (common.write !== undefined) {
|
|
389
|
+
new_common.write = common.write;
|
|
390
|
+
}
|
|
391
|
+
if (common.role !== undefined) {
|
|
392
|
+
new_common.role = common.role;
|
|
393
|
+
}
|
|
394
|
+
if (common.min !== undefined) {
|
|
395
|
+
new_common.min = common.min;
|
|
396
|
+
}
|
|
397
|
+
if (common.max !== undefined) {
|
|
398
|
+
new_common.max = common.max;
|
|
399
|
+
}
|
|
400
|
+
if (common.icon !== undefined) {
|
|
401
|
+
new_common.icon = common.icon;
|
|
402
|
+
}
|
|
314
403
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
404
|
+
// check if state exist
|
|
405
|
+
this.adapter.getObject(id, (err, stobj) => {
|
|
406
|
+
let hasChanges = false;
|
|
407
|
+
if (stobj) {
|
|
408
|
+
// update state - not change name and role (user can it changed)
|
|
409
|
+
if (stobj.common.name)
|
|
410
|
+
delete new_common.name;
|
|
411
|
+
else
|
|
412
|
+
new_common.name = new_name + ' ' + new_common.name;
|
|
413
|
+
delete new_common.role;
|
|
414
|
+
|
|
415
|
+
// check whether any common property is different
|
|
416
|
+
if (stobj.common) {
|
|
417
|
+
for (const property in new_common) {
|
|
418
|
+
if (stobj.common.hasOwnProperty(property)) {
|
|
419
|
+
if (stobj.common[property] === new_common[property]) {
|
|
420
|
+
delete new_common[property];
|
|
421
|
+
} else {
|
|
422
|
+
hasChanges = true;
|
|
423
|
+
}
|
|
335
424
|
}
|
|
336
425
|
}
|
|
337
426
|
}
|
|
427
|
+
} else {
|
|
428
|
+
hasChanges = true;
|
|
338
429
|
}
|
|
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
430
|
|
|
353
|
-
|
|
431
|
+
// only change object when any common property has changed
|
|
432
|
+
if (hasChanges) {
|
|
433
|
+
this.adapter.extendObject(id, {type: 'state', common: new_common, native: {} }, () => {
|
|
434
|
+
value !== undefined && this.setState_typed(id, value, true, (stobj) ? stobj.common.type : new_common.type);
|
|
435
|
+
});
|
|
436
|
+
} else if (value !== undefined) {
|
|
437
|
+
this.setState_typed(id, value, true, stobj.common.type);
|
|
438
|
+
}
|
|
354
439
|
|
|
355
|
-
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
else this.debug(`UpdateState: Device is deactivated ${devId} ${JSON.stringify(obj)}`);
|
|
356
443
|
} else {
|
|
357
|
-
this.debug(`
|
|
444
|
+
this.debug(`UpdateState: missing device ${devId} ${JSON.stringify(obj)}`);
|
|
358
445
|
}
|
|
359
446
|
});
|
|
360
447
|
}
|
|
361
448
|
|
|
449
|
+
setState_typed(id, value, ack, type, callback)
|
|
450
|
+
{
|
|
451
|
+
// never set a null or undefined value
|
|
452
|
+
if (value === null || value === undefined) return;
|
|
453
|
+
if (!type) {
|
|
454
|
+
this.debug("SetState_typed called without type");
|
|
455
|
+
// identify datatype, recursively call this function with set datatype
|
|
456
|
+
this.adapter.getObject(id, (err, obj) => {
|
|
457
|
+
if (obj && obj.common)
|
|
458
|
+
this.setState_typed(id, value, ack, obj.common.type, callback);
|
|
459
|
+
else {
|
|
460
|
+
this.setState_typed(id, value, ack, 'noobj', callback);
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
if (typeof value != type) {
|
|
466
|
+
this.debug("SetState_typed : converting " + JSON.stringify(value) + " for " + id + " from " + typeof value + " to " + type);
|
|
467
|
+
switch (type) {
|
|
468
|
+
case 'number':
|
|
469
|
+
value = parseFloat(value);
|
|
470
|
+
if (isNaN (value)) value = 0;
|
|
471
|
+
break;
|
|
472
|
+
case 'string':
|
|
473
|
+
case 'text': value = JSON.stringify(value); break;
|
|
474
|
+
case 'boolean':
|
|
475
|
+
if (typeof value == 'number') {
|
|
476
|
+
value = (value != 0);
|
|
477
|
+
break;
|
|
478
|
+
}
|
|
479
|
+
const sval = JSON.stringify(value).toLowerCase().trim();
|
|
480
|
+
value = (sval == 'true' || sval == 'yes' || sval == 'on');
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
this.adapter.setState(id, value, ack, callback);
|
|
485
|
+
}
|
|
486
|
+
|
|
362
487
|
updateDev(dev_id, dev_name, model, callback) {
|
|
488
|
+
const __dev_name = this.verifyDeviceName(dev_id, dev_name);
|
|
363
489
|
const id = '' + dev_id;
|
|
364
490
|
const modelDesc = statesMapping.findModel(model);
|
|
365
491
|
let icon = (modelDesc && modelDesc.icon) ? modelDesc.icon : 'img/unknown.png';
|
|
@@ -368,7 +494,7 @@ class StatesController extends EventEmitter {
|
|
|
368
494
|
this.adapter.setObjectNotExists(id, {
|
|
369
495
|
type: 'device',
|
|
370
496
|
// actually this is an error, so device.common has no attribute type. It must be in native part
|
|
371
|
-
common: {name:
|
|
497
|
+
common: {name: __dev_name, type: model, icon: icon},
|
|
372
498
|
native: {id: dev_id}
|
|
373
499
|
}, () => {
|
|
374
500
|
// update type and icon
|
|
@@ -377,15 +503,13 @@ class StatesController extends EventEmitter {
|
|
|
377
503
|
}
|
|
378
504
|
|
|
379
505
|
async syncDevStates(dev, model) {
|
|
380
|
-
const devId = dev.ieeeAddr.substr(2)
|
|
381
|
-
hasGroups = dev.type === 'Router';
|
|
506
|
+
const devId = dev.ieeeAddr.substr(2);
|
|
382
507
|
// devId - iobroker device id
|
|
383
508
|
const devStates = await this.getDevStates(dev.ieeeAddr, model);
|
|
384
509
|
if (!devStates) {
|
|
385
510
|
return;
|
|
386
511
|
}
|
|
387
|
-
const states = statesMapping.commonStates.concat(devStates.states)
|
|
388
|
-
.concat((hasGroups) ? [statesMapping.groupsState] : []);
|
|
512
|
+
const states = statesMapping.commonStates.concat(devStates.states);
|
|
389
513
|
|
|
390
514
|
for (const stateInd in states) {
|
|
391
515
|
if (!states.hasOwnProperty(stateInd)) continue;
|
|
@@ -432,6 +556,7 @@ class StatesController extends EventEmitter {
|
|
|
432
556
|
for (const addressPart of this.debugDevices) {
|
|
433
557
|
if (typeof(devId) == 'string' && devId.indexOf(addressPart) > -1)
|
|
434
558
|
{
|
|
559
|
+
if (payload.hasOwnProperty('msg_from_zigbee')) break;
|
|
435
560
|
this.warn(`ELEVATED publishToState: message received '${JSON.stringify(payload)}' from device ${devId} type '${model}'`);
|
|
436
561
|
has_debug = true;
|
|
437
562
|
break;
|
|
@@ -454,10 +579,10 @@ class StatesController extends EventEmitter {
|
|
|
454
579
|
value = payload[statedesc.prop || statedesc.id];
|
|
455
580
|
}
|
|
456
581
|
// checking value
|
|
457
|
-
if (value === undefined) continue;
|
|
582
|
+
if (value === undefined || value === null) continue;
|
|
458
583
|
let stateID = statedesc.id;
|
|
459
584
|
|
|
460
|
-
if (has_debug) {
|
|
585
|
+
if (has_debug && statedesc.id != 'msg_from_zigbee') {
|
|
461
586
|
this.warn(`ELEVATED publishToState: value generated '${JSON.stringify(value)}' from device ${devId} for '${statedesc.name}'`);
|
|
462
587
|
}
|
|
463
588
|
|
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', {});
|