iobroker.zigbee 2.0.0 → 2.0.2

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.
@@ -11,8 +11,12 @@ const DeviceAvailabilityExt = require('./zbDeviceAvailability');
11
11
  const DeviceConfigureExt = require('./zbDeviceConfigure');
12
12
  const DeviceEventExt = require('./zbDeviceEvent');
13
13
  const DelayedActionExt = require('./zbDelayedAction');
14
+ const Groups = require('./groups');
15
+
14
16
  const utils = require('./utils');
15
17
  const { waitForDebugger } = require('inspector');
18
+ const { table } = require('console');
19
+ const { extract } = require('tar');
16
20
  const groupConverters = [
17
21
  zigbeeHerdsmanConverters.toZigbee.light_onoff_brightness,
18
22
  zigbeeHerdsmanConverters.toZigbee.light_color_colortemp,
@@ -68,6 +72,18 @@ class ZigbeeController extends EventEmitter {
68
72
  new DeviceEventExt(this, {}),
69
73
  new DelayedActionExt(this, {}),
70
74
  ];
75
+ this.herdsmanTimeoutRegexp = new RegExp(/(\d+)ms/);
76
+ this.herdsmanLogSettings = {}
77
+ }
78
+
79
+
80
+ ByteArrayToString(data) {
81
+ return data.map(function (x) {
82
+ x = x + 0x100 + 1; // twos complement
83
+ x = x.toString(16); // to hex
84
+ x = ('00'+x).substr(-2); // zero-pad to 8-digits
85
+ return x
86
+ }).join('');
71
87
  }
72
88
 
73
89
  configure(options) {
@@ -105,8 +121,9 @@ class ZigbeeController extends EventEmitter {
105
121
  }
106
122
  this.disableLed = options.disableLed;
107
123
  this.warnOnDeviceAnnouncement = options.warnOnDeviceAnnouncement;
108
-
109
- this.debug(`Using zigbee-herdsman with settings: ${JSON.stringify(herdsmanSettings)}`);
124
+ this.herdsmanLogSettings.panID = herdsmanSettings.network.panID;
125
+ this.herdsmanLogSettings.channel = herdsmanSettings.network.channelList[0];
126
+ this.herdsmanLogSettings.extendedPanID = this.ByteArrayToString(herdsmanSettings.network.extendedPanID);
110
127
  this.herdsman = new ZigbeeHerdsman.Controller(herdsmanSettings, this.adapter.log);
111
128
  this.callExtensionMethod('setOptions', [{
112
129
  disableActivePing: options.disablePing,
@@ -129,12 +146,12 @@ class ZigbeeController extends EventEmitter {
129
146
  this.herdsman.on('deviceJoined', this.handleDeviceJoined.bind(this));
130
147
  this.herdsman.on('deviceLeave', this.handleDeviceLeave.bind(this));
131
148
  this.herdsman.on('message', this.handleMessage.bind(this));
149
+ this.herdsman.on('permitJoinChanged', this.handlePermitJoinChanged.bind(this));
132
150
 
151
+ this.info('Starting Zigbee-Herdsman');
133
152
  await this.herdsman.start();
134
-
135
- this.debug('zigbee-herdsman started');
136
153
  this.herdsmanStarted = true;
137
- this.info(`Coordinator firmware version: ${JSON.stringify(await this.herdsman.getCoordinatorVersion())}`);
154
+ this.info(`Zigbee-Herdsman started successfully with Coordinator firmware version: ${JSON.stringify(await this.herdsman.getCoordinatorVersion())}`);
138
155
 
139
156
  // debug info from herdsman getNetworkParameters
140
157
  const debNetworkParam = JSON.parse(JSON.stringify(await this.herdsman.getNetworkParameters()));
@@ -146,11 +163,32 @@ class ZigbeeController extends EventEmitter {
146
163
  extPanIDDebug += extendedPanIDDebug[i];
147
164
  i--;
148
165
  }
149
-
150
- this.debug(`Zigbee network parameters: panID=${debNetworkParam.panID} channel=${debNetworkParam.channel} extendedPanID=${extPanIDDebug}`);
151
- } catch (error) {
152
- this.sendError(error);
153
- this.error(`Starting zigbee-herdsman problem : ${(error && error.message ? error.message : 'no error message')}`);
166
+ this.debug(`Network parameters: panID=${debNetworkParam.panID} channel=${debNetworkParam.channel} extendedPanID=${extPanIDDebug}`);
167
+ } catch (e) {
168
+ try {
169
+ const debNetworkParam = JSON.parse(JSON.stringify(await this.herdsman.getNetworkParameters()));
170
+ const extendedPanIDDebug = typeof debNetworkParam.extendedPanID === 'string' ? debNetworkParam.extendedPanID.replace('0x', '') : debNetworkParam.extendedPanID;
171
+
172
+ let extPanIDDebug = '';
173
+ for (let i = extendedPanIDDebug.length - 1; i >= 0; i--) {
174
+ extPanIDDebug += extendedPanIDDebug[i - 1];
175
+ extPanIDDebug += extendedPanIDDebug[i];
176
+ i--;
177
+ }
178
+ this.warn(`Network parameters in Config : panID=${this.herdsmanLogSettings.panID} channel=${this.herdsmanLogSettings.channel} extendedPanID=${this.herdsmanLogSettings.extendedPanID}`)
179
+ this.warn(`Network parameters on Coordinator: panID=${debNetworkParam.panID} channel=${debNetworkParam.channel} extendedPanID=${extPanIDDebug}`);
180
+ }
181
+ catch (error) {
182
+ this.warn(`Unable to obtain herdsman settings`)
183
+ }
184
+ try {
185
+ await this.herdsman.stop();
186
+ }
187
+ catch (error) {
188
+ this.warn('unable to stop zigbee-herdsman after failed startup');
189
+ }
190
+ this.sendError(e);
191
+ this.error(`Starting zigbee-herdsman problem : ${(e && e.message ? e.message : 'no error message')}`);
154
192
  throw 'Error herdsman start';
155
193
  }
156
194
  // Check if we have to turn off the LED
@@ -195,40 +233,60 @@ class ZigbeeController extends EventEmitter {
195
233
  }
196
234
 
197
235
  // Call extensions
198
- this.callExtensionMethod('onZigbeeStarted', []);
199
236
 
200
- // Log zigbee clients on startup
201
- const devices = await this.getClients();
202
- if (devices.length > 0) {
203
- this.info(`Currently ${devices.length} devices are joined:`);
204
- } else {
205
- this.info(`Currently no devices.`);
206
- }
237
+ const deviceIterator = this.getClientIterator();
238
+ let deviceCount = 0;
239
+ try {
240
+ for (const device of deviceIterator) {
241
+ deviceCount++;
242
+ // get the model description for the known devices
243
+ const entity = await this.resolveEntity(device);
244
+ if (!entity) {
245
+ this.warn('failed to resolve Entity for ' + device.ieeeAddr);
246
+ continue;
247
+ }
248
+ //await this.adapter.stController.AddModelFromHerdsman(device, entity.mapped.model);
207
249
 
208
- for (const device of devices) {
209
- const entity = await this.resolveEntity(device);
210
- this.adapter.getObject(device.ieeeAddr.substr(2), (err, obj) => {
211
- if (obj && obj.common && obj.common.deactivated) {
212
- this.callExtensionMethod('deregisterDevicePing', [device, entity]);
213
- } else {
214
- this.callExtensionMethod('registerDevicePing', [device, entity]);
250
+ this.adapter.getObject(device.ieeeAddr.substr(2), (err, obj) => {
251
+ if (obj && obj.common && obj.common.deactivated) {
252
+ this.callExtensionMethod('deregisterDevicePing', [device, entity]);
253
+ } else {
254
+ this.callExtensionMethod('registerDevicePing', [device, entity]);
255
+ }
256
+ });
257
+ // ensure that objects for all found clients are present
258
+
259
+ if (entity.mapped) {
260
+ this.emit('new', entity);
215
261
  }
216
- });
217
- // ensure that objects for all found clients are present
262
+ this.info(
263
+ (entity.device.ieeeAddr) +
264
+ ` (addr ${entity.device.networkAddress}): ` +
265
+ (entity.mapped ?
266
+ `${entity.mapped.model} - ${entity.mapped.vendor} ${entity.mapped.description} ` :
267
+ `Unsupported (model ${entity.device.modelID})`) +
268
+ `(${entity.device.type})`
269
+ );
270
+ }
218
271
 
219
- if (entity.mapped) {
220
- this.emit('new', entity);
272
+ // Log zigbee clients on startup
273
+ // const devices = await this.getClients();
274
+ if (deviceCount > 0) {
275
+ this.info(`Currently ${deviceCount} devices are joined:`);
276
+ } else {
277
+ this.info(`Currently no devices.`);
221
278
  }
222
- this.info(
223
- (entity.device.ieeeAddr) +
224
- ` (addr ${entity.device.networkAddress}): ` +
225
- (entity.mapped ?
226
- `${entity.mapped.model} - ${entity.mapped.vendor} ${entity.mapped.description} ` :
227
- `Not supported (model ${entity.device.modelID})`) +
228
- `(${entity.device.type})`
229
- );
279
+ this.callExtensionMethod('onZigbeeStarted', []);
280
+ }
281
+ catch (error) {
282
+ this.error('error iterating devices : '+ (error && error.message ? error.message: 'no reason given'));
283
+ }
284
+ try {
285
+ this.getGroups();
286
+ }
287
+ catch (error) {
288
+ this.error('error iterating groups : '+ (error && error.message ? error.message: 'no reason given'));
230
289
  }
231
-
232
290
  this.emit('ready');
233
291
  }
234
292
 
@@ -256,6 +314,12 @@ class ZigbeeController extends EventEmitter {
256
314
  this.adapter.sendError(error, message);
257
315
  }
258
316
 
317
+ filterHerdsmanError(message) {
318
+ if (typeof message != 'string' || message == '') return 'no error message';
319
+ if (message.match(this.herdsmanTimeoutRegexp)) return 'Timeout';
320
+ return message;
321
+ }
322
+
259
323
  callExtensionMethod(method, parameters) {
260
324
  const result = [];
261
325
  for (const extension of this.extensions) {
@@ -288,16 +352,30 @@ class ZigbeeController extends EventEmitter {
288
352
  }
289
353
  }
290
354
 
355
+ getClientIterator(all) {
356
+ if (this.herdsman.database) {
357
+ return this.herdsman.getDevicesIterator( function(device) { return (all? true: device.type !== 'Coordinator')});
358
+ } else {
359
+ return [].values();
360
+ }
361
+ }
362
+
291
363
  async getGroups() {
292
364
  try {
293
365
  if (this.herdsman) {
294
- return await this.herdsman.getGroups();
366
+ const rv = [];
367
+ const groupIterator = this.herdsman.getGroupsIterator();
368
+ for (const g of groupIterator) {
369
+ const members = await this.getGroupMembersFromController(g.groupID);
370
+ rv.push({ id: g.groupID, size: (members ? members.length : 0), stateName: 'group_'+g.groupID});
371
+ }
372
+ return rv;
295
373
  } else {
296
374
  return null;
297
375
  }
298
376
  } catch (error) {
299
377
  this.sendError(error);
300
- this.error(`error in getGroups: ${(error && error.message ? error.message : 'no error message')} ${(error && error.stack ? error.stack : 'no call stack')}`);
378
+ this.error(JSON.stringify(error));
301
379
  return undefined;
302
380
  }
303
381
  }
@@ -308,15 +386,17 @@ class ZigbeeController extends EventEmitter {
308
386
  group && group.removeFromNetwork();
309
387
  } catch (error) {
310
388
  this.sendError(error);
311
- this.error(`error in removeGroupById: ${(error && error.message ? error.message : 'no error message')} ${(error && error.stack ? error.stack : 'no call stack')}`);
389
+ this.error(`error in removeGroupById: ${error}`);
312
390
  }
313
391
  }
314
392
 
315
393
  async getGroupByID(id) {
316
394
  try {
317
- return this.herdsman.getGroupByID(id);
395
+ const rv = this.herdsman.getGroupByID(id);
396
+ return rv;
318
397
  } catch (error) {
319
398
  this.sendError(error);
399
+ // this.error('error getting group for ' + id + ' ' + (error && error.message ? error.message : 'without message'));
320
400
  return undefined;
321
401
  }
322
402
  }
@@ -333,12 +413,23 @@ class ZigbeeController extends EventEmitter {
333
413
  } else {
334
414
  this.debug(`verifyGroupExists: group ${nid} exists`);
335
415
  }
416
+ return group
336
417
  }
337
418
  catch (error) {
338
419
  this.error(`verifyGroupExists: ${error && error.message ? error.message : 'unspecified error'}`);
339
420
  }
340
421
  }
341
422
 
423
+ async rebuildGroupIcon(grp) {
424
+ const g = (typeof grp === 'number') ? this.herdsman.getGroupByID(grp) : grp;
425
+ if (typeof g === 'object' && g.groupID)
426
+ {
427
+ const members = await this.getGroupMembersFromController(g.groupID);
428
+ return `img/group_${members.length}.png`
429
+ }
430
+ return 'img/group_x.png';
431
+ }
432
+
342
433
  async addPairingCode(code) {
343
434
  this.debug(`calling addPairingCode with ${code}`);
344
435
  if (code) {
@@ -357,7 +448,7 @@ class ZigbeeController extends EventEmitter {
357
448
  async getGroupMembersFromController(id) {
358
449
  const members = [];
359
450
  try {
360
- const group = await this.getGroupByID(id);
451
+ const group = await this.getGroupByID(Number(id));
361
452
  if (group) {
362
453
  const groupMembers = group.members;
363
454
  for (const member of groupMembers) {
@@ -391,7 +482,8 @@ class ZigbeeController extends EventEmitter {
391
482
  try {
392
483
  return this.herdsman.getDeviceByIeeeAddr(key);
393
484
  }
394
- catch {
485
+ catch (error) {
486
+ this.error(`getDeviceByIeeeAddr: ${(error && error.message ? error.message : 'no error message')}`);
395
487
  return undefined;
396
488
  }
397
489
  }
@@ -400,7 +492,8 @@ class ZigbeeController extends EventEmitter {
400
492
  try {
401
493
  return this.herdsman.getDevicesByType(type);
402
494
  }
403
- catch {
495
+ catch (error) {
496
+ this.error(`getDevicesByType: ${(error && error.message ? error.message : 'no error message')}`);
404
497
  return undefined;
405
498
  }
406
499
  }
@@ -409,82 +502,109 @@ class ZigbeeController extends EventEmitter {
409
502
  try {
410
503
  return this.herdsman.getDeviceByNetworkAddress(networkAddress);
411
504
  }
412
- catch {
505
+ catch (error) {
506
+ this.error(`getDeviceByNetworkAddress: ${(error && error.message ? error.message : 'no error message')}`);
413
507
  return undefined;
414
508
  }
415
509
  }
416
510
 
511
+ async analyzeKey(key) {
512
+ const rv = {
513
+ kind: 'device',
514
+ key: key,
515
+ message: 'success'
516
+ }
517
+ if (typeof key === 'object') return rv;
518
+ if (typeof key === 'number') {
519
+ rv.kind = 'group';
520
+ return rv;
521
+ }
522
+ if (typeof key === 'string') {
523
+ if (key === 'coordinator') {
524
+ rv.kind = 'coordinator'
525
+ return rv;
526
+ };
527
+ const kp = key.split('_');
528
+ if (kp[0] === 'group' && kp.length > 1) {
529
+ rv.kind = 'group';
530
+ rv.key = Number(kp[1]);
531
+ return rv;
532
+ }
533
+ if (key.startsWith('0x')) {
534
+ rv.kind = 'ieee'
535
+ return rv;
536
+ };
537
+ if (key.trim().length === 16) {
538
+ rv.key = `0x${key.trim()}`
539
+ rv.kind = 'ieee';
540
+ return rv;
541
+ };
542
+ }
543
+ rv.message = 'failed';
544
+ return rv;
545
+ }
546
+
417
547
  async resolveEntity(key, ep) {
418
- // assert(typeof key === 'string' || key.constructor.name === 'Device', `Wrong type '${typeof key}'`);
548
+ // this.warn('resolve entity with key of tyoe ' + typeof (key));
419
549
  try {
420
- if (typeof key === 'string') {
421
- if (key === 'coordinator') {
422
- const coordinator = this.herdsman.getDevicesByType('Coordinator')[0];
423
- return {
424
- type: 'device',
425
- device: coordinator,
426
- endpoint: coordinator.getEndpoint(1),
427
- name: 'Coordinator',
428
- };
429
- } else {
430
- const device = await this.herdsman.getDeviceByIeeeAddr(key);
431
- if (device) {
432
- const mapped = await zigbeeHerdsmanConverters.findByDevice(device);
433
- const endpoints = mapped && mapped.endpoint ? mapped.endpoint(device) : null;
434
- let endpoint;
435
- if (endpoints && ep != undefined && endpoints[ep]) {
436
- endpoint = device.getEndpoint(endpoints[ep]);
437
- } else if (endpoints && endpoints['default']) {
438
- endpoint = device.getEndpoint(endpoints['default']);
439
- } else {
440
- const epNum = parseInt(ep);
441
- if (!isNaN(epNum)) {
442
- endpoint = device.getEndpoint(epNum);
443
- } else {
444
- endpoint = device.endpoints[0];
445
- }
446
- }
447
- return {
448
- type: 'device',
449
- device,
450
- mapped,
451
- endpoint,
452
- endpoints: device.endpoints,
453
- name: key,
454
- };
455
- }
456
- }
457
- } else if (typeof key === 'number') {
458
- let group = await this.herdsman.getGroupByID(key);
459
- if (!group) group = await this.herdsman.createGroup(key);
550
+ const _key = await this.analyzeKey(key);
551
+ if (_key.message !== 'success') return undefined;
552
+
553
+ if (_key.kind == 'coordinator') {
554
+ const coordinator = this.herdsman.getDevicesByType('Coordinator')[0];
555
+ return {
556
+ type: 'device',
557
+ device: coordinator,
558
+ endpoint: coordinator.getEndpoint(1),
559
+ name: 'Coordinator',
560
+ };
561
+ }
562
+ if (_key.kind === 'group') {
563
+ let group = await this.herdsman.getGroupByID(_key.key);
564
+ if (!group) group = await this.herdsman.createGroup(_key.key);
460
565
  group.toZigbee = groupConverters;
461
566
  group.model = 'group';
462
567
  return {
463
568
  type: 'group',
464
569
  mapped: group,
465
570
  group,
466
- name: `Group ${key}`,
571
+ name: `Group ${_key.key}`,
467
572
  };
468
- } else {
469
- let mapped;
470
- try {
471
- mapped = await zigbeeHerdsmanConverters.findByDevice(key);
472
- } catch (err) {
473
- this.error(`zigbeeHerdsmanConverters findByDevice ${key.ieeeAddr}`);
474
- }
475
573
 
574
+ }
575
+ if (_key.kind === 'ieee') _key.key = await this.herdsman.getDeviceByIeeeAddr(_key.key);
576
+ const device = _key.key;
577
+ if (device) {
578
+ const mapped = await zigbeeHerdsmanConverters.findByDevice(device);
579
+ const endpoints = mapped && mapped.endpoint ? mapped.endpoint(device) : null;
580
+ let endpoint;
581
+ if (endpoints && ep != undefined && endpoints[ep]) {
582
+ endpoint = device.getEndpoint(endpoints[ep]);
583
+ } else if (endpoints && endpoints['default']) {
584
+ endpoint = device.getEndpoint(endpoints['default']);
585
+ } else {
586
+ const epNum = parseInt(ep);
587
+ if (!isNaN(epNum)) {
588
+ endpoint = device.getEndpoint(epNum);
589
+ } else {
590
+ endpoint = device.endpoints[0];
591
+ }
592
+ }
476
593
  return {
477
594
  type: 'device',
478
- device: key,
479
- mapped: mapped,
480
- name: key.type === 'Coordinator' ? 'Coordinator' : key.ieeeAddr,
595
+ device,
596
+ mapped,
597
+ endpoint,
598
+ endpoints: device.endpoints,
599
+ name: device._ieeeAddr,
481
600
  };
482
601
  }
483
602
  }
484
- catch {
485
- return undefined;
603
+ catch (error)
604
+ {
605
+ this.error('Resolve entity error: ' + (error && error.message ? error.message : 'no reason given'))
486
606
  }
487
-
607
+ return undefined;
488
608
  }
489
609
 
490
610
  async incMsgHandler(message) {
@@ -504,15 +624,20 @@ class ZigbeeController extends EventEmitter {
504
624
  modelId: device.modelId
505
625
  });
506
626
  }
507
- catch {
508
- return;
627
+ catch (error) {
628
+ this.error('incMsgHandler: ' + (error && error.message ? error.message : 'no error message'));
509
629
  }
510
630
  }
511
631
 
512
632
  // Stop controller
513
633
  async stop() {
514
634
  // Call extensions
515
- await this.callExtensionMethod('stop', []);
635
+ try {
636
+ await this.callExtensionMethod('stop', []);
637
+ }
638
+ catch (error) {
639
+ this.error('unable to call extension Method stop: ' + (error && error.message ? error.message : 'no error message'));
640
+ }
516
641
 
517
642
  try {
518
643
  await this.permitJoin(0);
@@ -538,55 +663,46 @@ class ZigbeeController extends EventEmitter {
538
663
 
539
664
  // Permit join
540
665
  async permitJoin(permitTime, devid, failure) {
541
- let permitDev;
542
- if (isFunction(devid) && !isFunction(failure)) {
543
- failure = devid;
544
- } else {
545
- if (devid != '') {
546
- permitDev = this.getDevice(devid);
547
- } else {
548
- permitDev = '';
549
- }
550
- }
551
-
552
- if (permitTime) {
553
- this.info('Zigbee: allowing new devices to join.');
554
- } else {
555
- this.info('Zigbee: disabling joining new devices.');
666
+ try {
667
+ this._permitJoinTime = permitTime;
668
+ await this.herdsman.permitJoin(permitTime);
669
+ } catch (e) {
670
+ this.sendError(e);
671
+ this.error(`Failed to open the network: ${e.stack}`);
556
672
  }
673
+ }
557
674
 
675
+ async handlePermitJoinChanged(data)
676
+ {
558
677
  try {
559
- if (permitTime && !this.herdsman.getPermitJoin()) {
560
- clearInterval(this._permitJoinInterval);
561
- this._permitJoinTime = permitTime;
562
- await this.herdsman.permitJoin(true, permitDev, this._permitJoinTime);
563
- this._permitJoinInterval = setInterval(async () => {
564
- this.emit('pairing', 'Pairing time left', this._permitJoinTime);
565
- if (this._permitJoinTime === 0) {
566
- this.info('Zigbee: stop joining');
567
- clearInterval(this._permitJoinInterval);
568
- await this.herdsman.permitJoin(false);
569
- }
570
- this._permitJoinTime -= 1;
571
- }, 1000);
572
- } else if (this.herdsman.getPermitJoin()) {
573
- if (permitTime) {
574
- this.info('Joining already permitted');
575
- } else {
576
- clearInterval(this._permitJoinInterval);
577
- await this.herdsman.permitJoin(false, permitDev);
678
+ this.debug(`Event handlePermitJoinChanged received with ${JSON.stringify(data)}`);
679
+ if (data.permitted) {
680
+ if (!this._permitJoinInterval) {
681
+ this.emit('pairing',`Pairing possible for ${this._permitJoinTime} seconds`)
682
+ this.info(`Opening zigbee Network for ${this._permitJoinTime} seconds`)
683
+ this._permitJoinInterval = setInterval(async () => {
684
+ this.emit('pairing', 'Pairing time left', this._permitJoinTime);
685
+ this._permitJoinTime -= 1;
686
+ }, 1000);
578
687
  }
579
688
  }
580
- } catch (e) {
581
- this.sendError(e);
582
- this.error(`Failed to open the network: ${e.stack}`);
689
+ else {
690
+ this.info(`Closing Zigbee network, ${this._permitJoinTime} seconds remaining`)
691
+ clearInterval(this._permitJoinInterval);
692
+ this._permitJoinInterval = null;
693
+ // this.emit('pairing', 'Pairing time left', 0);
694
+ this.emit('pairing', 'Closing network.',0);
695
+ }
696
+ }
697
+ catch (error) {
698
+ this.error(`Error in handlePermitJoinChanged: ${error.message}`);
583
699
  }
584
700
  }
585
701
 
586
702
  // Remove device
587
703
  async remove(deviceID, force, callback) {
588
704
  try {
589
- const device = await this.herdsman.getDeviceByIeeeAddr(deviceID);
705
+ const device = this.herdsman.getDeviceByIeeeAddr(deviceID);
590
706
  if (device) {
591
707
  try {
592
708
  await device.removeFromNetwork();
@@ -604,7 +720,7 @@ class ZigbeeController extends EventEmitter {
604
720
  }
605
721
 
606
722
  try {
607
- await device.removeFromDatabase();
723
+ device.removeFromDatabase();
608
724
  } catch (error) {
609
725
  this.sendError(error);
610
726
  // skip error
@@ -640,7 +756,7 @@ class ZigbeeController extends EventEmitter {
640
756
  );
641
757
  } catch (error) {
642
758
  this.sendError(error);
643
- this.error(`Failed to handleDeviceLeave ${error.stack}`);
759
+ this.error(`Failed to handleDeviceLeave ${error && error.message ? error.message : 'no error message given'}`);
644
760
  }
645
761
  }
646
762
 
@@ -689,46 +805,51 @@ class ZigbeeController extends EventEmitter {
689
805
  this.warn(`Blocked interview for '${message.ieeeAddr}' because the network is closed`);
690
806
  return;
691
807
  }
692
- const entity = await this.resolveEntity(message.device || message.ieeeAddr);
693
- const friendlyName = entity.name;
694
- if (message.status === 'successful') {
695
- this.info(`Successfully interviewed '${friendlyName}', device has successfully been paired`);
696
-
697
- if (entity.mapped) {
698
- const {vendor, description, model} = entity.mapped;
699
- this.info(
700
- `Device '${friendlyName}' is supported, identified as: ${vendor} ${description} (${model})`
701
- );
702
-
703
- const log = {friendly_name: friendlyName, model, vendor, description, supported: true};
704
- this.emit('pairing', 'Interview successful', JSON.stringify(log));
705
- entity.device.modelID = entity.device._modelID;
706
- this.emit('new', entity);
707
- // send to extensions again (for configure)
708
- this.callExtensionMethod(
709
- 'onZigbeeEvent',
710
- [message, entity ? entity.mapped : null],
711
- );
808
+ try {
809
+ const entity = await this.resolveEntity(message.device || message.ieeeAddr);
810
+ const friendlyName = entity.name;
811
+ if (message.status === 'successful') {
812
+ this.info(`Successfully interviewed '${friendlyName}', device has successfully been paired`);
813
+
814
+ if (entity.mapped) {
815
+ const {vendor, description, model} = entity.mapped;
816
+ this.info(
817
+ `Device '${friendlyName}' is supported, identified as: ${vendor} ${description} (${model})`
818
+ );
819
+
820
+ const log = {friendly_name: friendlyName, model, vendor, description, supported: true};
821
+ this.emit('pairing', 'Interview successful', JSON.stringify(log));
822
+ entity.device.modelID = entity.device._modelID;
823
+ this.emit('new', entity);
824
+ // send to extensions again (for configure)
825
+ this.callExtensionMethod(
826
+ 'onZigbeeEvent',
827
+ [message, entity ? entity.mapped : null],
828
+ );
829
+ } else {
830
+ this.debug(
831
+ `Device '${friendlyName}' with Zigbee model '${message.device.modelID}' is NOT supported, ` +
832
+ `please follow https://www.zigbee2mqtt.io/how_tos/how_to_support_new_devices.html`
833
+ );
834
+ const frName = {friendly_name: friendlyName, supported: false};
835
+ this.emit('pairing', 'Interview successful', JSON.stringify(frName));
836
+ entity.device.modelID = entity.device._modelID;
837
+ this.emit('new', entity);
838
+ }
839
+ } else if (message.status === 'failed') {
840
+ this.error(`Failed to interview '${friendlyName}', device has not successfully been paired. Try again !!!!!!!!!! `);
841
+ //this.error(`Failed to interview '${friendlyName}', device has not successfully been paired. Try again !!!!!!!!!! ${message.error}`);
842
+ this.emit('pairing', 'Interview failed', friendlyName);
712
843
  } else {
713
- this.debug(
714
- `Device '${friendlyName}' with Zigbee model '${message.device.modelID}' is NOT supported, ` +
715
- `please follow https://www.zigbee2mqtt.io/how_tos/how_to_support_new_devices.html`
716
- );
717
- const frName = {friendly_name: friendlyName, supported: false};
718
- this.emit('pairing', 'Interview successful', JSON.stringify(frName));
719
- entity.device.modelID = entity.device._modelID;
720
- this.emit('new', entity);
721
- }
722
- } else if (message.status === 'failed') {
723
- this.error(`Failed to interview '${friendlyName}', device has not successfully been paired. Try again !!!!!!!!!! `);
724
- //this.error(`Failed to interview '${friendlyName}', device has not successfully been paired. Try again !!!!!!!!!! ${message.error}`);
725
- this.emit('pairing', 'Interview failed', friendlyName);
726
- } else {
727
- if (message.status === 'started') {
728
- this.info(`Starting interview of '${friendlyName}'`);
729
- this.emit('pairing', 'Interview started', friendlyName);
844
+ if (message.status === 'started') {
845
+ this.info(`Starting interview of '${friendlyName}'`);
846
+ this.emit('pairing', 'Interview started', friendlyName);
847
+ }
730
848
  }
731
849
  }
850
+ catch (error) {
851
+ this.error('handleDeviceInterview: ' + (error && error.message ? error.message : 'no error message'));
852
+ }
732
853
  }
733
854
 
734
855
  async handleMessage(data) {
@@ -751,20 +872,25 @@ class ZigbeeController extends EventEmitter {
751
872
 
752
873
  async getMap(callback) {
753
874
  try {
875
+ this.info('Collecting Map Data');
754
876
  const devices = this.herdsman.getDevices(true);
755
877
  const lqis = [];
756
878
  const routing = [];
757
-
758
- for (const device of devices.filter((d) => d.type !== 'EndDevice')) {
759
- const resolved = await this.resolveEntity(device);
879
+ const errors = [];
880
+
881
+ await Promise.all(devices.filter((d) => d.type !== 'EndDevice').map(async device =>
882
+ {
883
+ let resolved = await this.resolveEntity(device, 0);
884
+ if (!resolved) {
885
+ resolved = { name:'unresolved device', device:device }
886
+ this.warn('resolve Entity failed for ' + device.ieeeAddr)
887
+ }
760
888
  let result;
761
889
 
762
890
  try {
763
891
  result = await device.lqi();
764
892
  } catch (error) {
765
- this.sendError(error);
766
- error && this.debug(`Failed to execute LQI for '${resolved.name}'. ${safeJsonStringify(error.stack)}`);
767
-
893
+ errors.push(`Failed to execute LQI for '${resolved ? resolved.name : 'unresolved device'} (${resolved ? resolved.device.modelID : 'unknown'}') : ${this.filterHerdsmanError(error.message)}.`);
768
894
  lqis.push({
769
895
  parent: 'undefined',
770
896
  networkAddress: 0,
@@ -780,7 +906,7 @@ class ZigbeeController extends EventEmitter {
780
906
  for (const dev of result.neighbors) {
781
907
  if (dev !== undefined && dev.ieeeAddr !== '0xffffffffffffffff') {
782
908
  lqis.push({
783
- parent: resolved.device.ieeeAddr,
909
+ parent: (resolved ? resolved.device.ieeeAddr : undefined),
784
910
  networkAddress: dev.networkAddress,
785
911
  ieeeAddr: dev.ieeeAddr,
786
912
  lqi: dev.linkquality,
@@ -792,18 +918,14 @@ class ZigbeeController extends EventEmitter {
792
918
  }
793
919
  }
794
920
 
795
- this.debug(`LQI succeeded for '${resolved.name}'`);
796
-
797
921
  try {
798
922
  result = await device.routingTable();
799
923
  } catch (error) {
800
- this.sendError(error);
801
924
  if (error) {
802
- this.debug(`Failed to execute routing table for '${resolved.name}'. ${safeJsonStringify(error.stack)}`);
925
+ errors.push(`Failed to collect routing table for '${resolved ? resolved.name : 'unresolved device'} (${resolved ? resolved.device.modelID : 'unknown'}') : ${this.filterHerdsmanError(error.message)}`);
803
926
  }
804
927
  }
805
928
 
806
- this.debug(`Routing for '${resolved.name}': ${safeJsonStringify(result)}`);
807
929
  if (result !== undefined) {
808
930
  if (result.table !== undefined) {
809
931
  for (const dev of result.table) {
@@ -816,17 +938,24 @@ class ZigbeeController extends EventEmitter {
816
938
  }
817
939
  }
818
940
  }
819
- this.debug(`Routing table succeeded for '${resolved.name}'`);
820
941
  }
821
- this.debug(`Get map succeeded ${safeJsonStringify(lqis)}`);
942
+ ));
822
943
 
823
- callback && callback({lqis, routing});
944
+ callback && callback({lqis, routing, errors});
945
+ if (errors.length) {
946
+ this.warn(`Map Data collection complete with ${errors.length} issues:`);
947
+ for (const msg of errors)
948
+ this.warn(msg);
949
+ }
950
+ else
951
+ this.info('Map data collection complete');
824
952
  } catch (error) {
825
953
  this.sendError(error);
826
- this.debug(`Failed to get map: ${safeJsonStringify(error.stack)}`);
954
+ this.error(`Failed to get map: ${safeJsonStringify(error.stack)}`);
827
955
  }
828
956
  }
829
957
 
958
+
830
959
  async publish(deviceID, cid, cmd, zclData, cfg, ep, type, callback, zclSeqNum) {
831
960
  const entity = await this.resolveEntity(deviceID, ep);
832
961
  const device = entity.device;
@@ -849,20 +978,11 @@ class ZigbeeController extends EventEmitter {
849
978
  if (cfg == null) {
850
979
  cfg = {};
851
980
  }
981
+
852
982
  try {
983
+
853
984
  if (type === 'foundation') {
854
985
  cfg.disableDefaultResponse = true;
855
- /*
856
- if (cmd === 'read' && !Array.isArray(zclData)) {
857
- // needs to be iterable (string[] | number [])
858
- zclData[Symbol.iterator] = function* () {
859
- let k;
860
- for (k in this) {
861
- yield k;
862
- }
863
- };
864
- }
865
- */
866
986
  let result;
867
987
  if (cmd === 'configReport') {
868
988
  result = await endpoint.configureReporting(cid, zclData, cfg);
@@ -935,7 +1055,7 @@ class ZigbeeController extends EventEmitter {
935
1055
  } catch (error) {
936
1056
  this.sendError(error);
937
1057
  this.error(`Exception when trying to Add ${devId} to group ${groupId}`, error);
938
- return {error: `Failed to add ${devId} to group ${groupId}: ${(error && error.message ? error.message : 'no error message')} ${(error && error.stack ? error.stack : 'no call stack')}`};
1058
+ return {error: `Failed to add ${devId} to group ${groupId}: ${JSON.stringify(error)}`};
939
1059
  }
940
1060
  return {};
941
1061
  }
@@ -947,6 +1067,7 @@ class ZigbeeController extends EventEmitter {
947
1067
  entity = await this.resolveEntity(devId);
948
1068
  const group = await this.resolveEntity(groupId);
949
1069
 
1070
+ /*
950
1071
  const members = await this.getGroupMembersFromController(groupId);
951
1072
  const memberIDs = [];
952
1073
  for (const member of members) {
@@ -955,7 +1076,7 @@ class ZigbeeController extends EventEmitter {
955
1076
 
956
1077
  this.debug(`removeDevFromGroup - entity: ${utils.getEntityInfo(entity)}`);
957
1078
  this.debug(`removeDevFromGroup ${groupId} with ${memberIDs.length} members ${safeJsonStringify(memberIDs)}`);
958
-
1079
+ */
959
1080
  if (epid != undefined) {
960
1081
  for (const ep of entity.endpoints) {
961
1082
  this.debug(`checking ep ${ep.ID} of ${devId} (${epid})`);
@@ -968,6 +1089,7 @@ class ZigbeeController extends EventEmitter {
968
1089
  await entity.endpoint.removeFromGroup(group.mapped);
969
1090
  this.info(`removing endpoint ${entity.endpoint.ID} of ${devId} from group ${groupId}`);
970
1091
  }
1092
+
971
1093
  } catch (error) {
972
1094
  this.sendError(error);
973
1095
  this.error(`Exception when trying remove ${devId} (ep ${epid ? epid : (entity ? entity.endpoint.ID : '')}) from group ${devId}`, error);
@@ -1011,7 +1133,7 @@ class ZigbeeController extends EventEmitter {
1011
1133
  });
1012
1134
  }
1013
1135
  catch (error) {
1014
- callback(error);
1136
+ callback(error)
1015
1137
  }
1016
1138
  }
1017
1139
 
@@ -1031,9 +1153,8 @@ class ZigbeeController extends EventEmitter {
1031
1153
  callback(error);
1032
1154
  });
1033
1155
  }
1034
- catch (error)
1035
- {
1036
- callback(error);
1156
+ catch (error) {
1157
+ callback (error)
1037
1158
  }
1038
1159
  }
1039
1160