iobroker.zigbee 3.1.6 → 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,6 +9,7 @@ const localConfig = require('./localConfig');
9
9
  const path = require('path');
10
10
  const axios = require('axios');
11
11
  const zigbeeHerdsmanConvertersUtils = require('zigbee-herdsman-converters/lib/utils');
12
+ const utils = require('./utils');
12
13
 
13
14
  class StatesController extends EventEmitter {
14
15
  constructor(adapter) {
@@ -24,7 +25,7 @@ class StatesController extends EventEmitter {
24
25
  this.timeoutHandleUpload = null;
25
26
  this.ImagesToDownload = [];
26
27
  this.stashedErrors = {};
27
- this.stashedUnknownModels = [];
28
+ this.stashedUnknownModels = {};
28
29
  this.debugMessages = { nodevice:{ in:[], out: []} };
29
30
  this.debugActive = true;
30
31
  this.deviceQueryBlock = [];
@@ -58,9 +59,9 @@ class StatesController extends EventEmitter {
58
59
  rv.push('<p><b>Stashed Messages</b></p>')
59
60
  rv.push(Object.values(this.stashedErrors).join('<br>'));
60
61
  }
61
- if (this.stashedUnknownModels.length > 0) {
62
+ if (Object.keys(this.stashedUnknownModels).length > 0) {
62
63
  rv.push('<p><b>Devices whithout Model definition</b></p>')
63
- rv.push()
64
+ rv.push(Object.values(this.stashedUnknownModels).join('<br>'));
64
65
  }
65
66
  }
66
67
  catch (error) {
@@ -76,7 +77,7 @@ class StatesController extends EventEmitter {
76
77
  async AddModelFromHerdsman(device, model) {
77
78
  const namespace = `${this.adapter.name}.admin`;
78
79
 
79
- if (this.localConfig.getOverrideWithTargetAndKey(model, 'legacy', true)) {
80
+ if (this.localConfig.getOverrideWithTargetAndKey(model, 'options.legacy', true)) {
80
81
  this.debug('Applying legacy definition for ' + model);
81
82
  await this.addLegacyDevice(model);
82
83
  }
@@ -84,7 +85,7 @@ class StatesController extends EventEmitter {
84
85
  this.debug('Generating states from exposes for ' + model);
85
86
  // download icon if it external and not undefined
86
87
  if (model === undefined) {
87
- const dev_name = this.verifyDeviceName(device.ieeeAddr.substr(2), model, device.modelID);
88
+ const dev_name = this.verifyDeviceName(utils.zbIdorIeeetoAdId(this.adapter, device.ieeeAddr, false), model, device.modelID);
88
89
  this.warn(`icon ${dev_name} for undefined Device not available. Check your devices.`);
89
90
  } else {
90
91
  const modelDesc = await statesMapping.addExposeToDevices(device, this, model);
@@ -101,7 +102,7 @@ class StatesController extends EventEmitter {
101
102
  }
102
103
  return;
103
104
  }
104
- // source is inline basee64
105
+ // source is inline basee64F
105
106
  const base64Match = srcIcon.match(/data:image\/(.+);base64,/);
106
107
  if (base64Match) {
107
108
  this.warn(`base 64 Icon matched, trying to save it to disk as ${pathToAdminIcon}`);
@@ -119,7 +120,7 @@ class StatesController extends EventEmitter {
119
120
  this.warn(`${msg} -- failed: ${err && err.message ? err.message : 'no reason given'}`);
120
121
  return;
121
122
  }
122
- this.info(`${msg} -- success`);
123
+ this.debug(`${msg} -- success`);
123
124
  });
124
125
  }
125
126
  catch (err) {
@@ -165,7 +166,7 @@ class StatesController extends EventEmitter {
165
166
  this.error(`error writing file ${path}: ${err.message ? err.message : 'no reason given'}`);
166
167
  return;
167
168
  }
168
- this.info('Updated image file ' + pathToAdminIcon);
169
+ this.debug('Updated image file ' + pathToAdminIcon);
169
170
  });
170
171
  return;
171
172
  }
@@ -361,12 +362,7 @@ class StatesController extends EventEmitter {
361
362
  stateModel = statesMapping.findModel(model);
362
363
  if (!stateModel) {
363
364
  // try to get the model from the exposes
364
- if (!this.stashedErrors.hasOwnProperty(`${deviceId}.nomodel`))
365
- {
366
- this.warn(`Device ${deviceId} "${model}" not found.`);
367
- this.stashedErrors[`${deviceId}.nomodel`] = `Device ${deviceId} "${model}" not found.`;
368
- }
369
-
365
+ this.stashUnknownModel(`getDevStates:${deviceId}`,`Model "${model}" not found for Device ${deviceId}`);
370
366
  states = statesMapping.commonStates;
371
367
  } else {
372
368
  states = stateModel.states;
@@ -385,6 +381,21 @@ class StatesController extends EventEmitter {
385
381
  }
386
382
  }
387
383
 
384
+ stashErrors(key, msg, error) {
385
+ if (!this.stashedErrors.hasOwnProperty(key))
386
+ {
387
+ if (error) this.error(msg); else this.warn(msg);
388
+ this.stashedErrors[key] = msg;
389
+ }
390
+ }
391
+
392
+ stashUnknownModel(model, msg) {
393
+ if (!this.stashedUnknownModels.hasOwnProperty(model)) {
394
+ this.stashedUnknownModels[model] = msg;
395
+ this.error(`Unknown ${model}: ${msg}`)
396
+ }
397
+ }
398
+
388
399
  async triggerComposite(_deviceId, stateDesc, interactive) {
389
400
  const deviceId = (_deviceId.replace('0x', ''));
390
401
  const idParts = stateDesc.id.split('.').slice(-2);
@@ -506,7 +517,7 @@ class StatesController extends EventEmitter {
506
517
  if (! res.stateDesc.isOption) stateList.push(res);
507
518
  }
508
519
  else {
509
- res.forEach((ls) => { if (!ls.stateDesc.isOption) stateList.push(res)} );
520
+ res.forEach((ls) => { if (!ls.stateDesc.isOption) stateList.push(ls)} );
510
521
  }
511
522
  }
512
523
  } else {
@@ -564,19 +575,31 @@ class StatesController extends EventEmitter {
564
575
  async deleteObj(devId) {
565
576
  const options = { recursive:true };
566
577
  try {
567
- this.adapter.delObject(devId,options), (err) => {
568
- this.adapter.log.info(`Cannot delete Object ${devId}: ${err}`);
569
- }
570
-
578
+ await this.adapter.delObjectAsync(devId,options);
571
579
  } catch (error) {
572
580
  this.adapter.log.warn(`Cannot delete Object ${devId}: ${error && error.message ? error.message : 'without error message'}`);
581
+ return `Cannot delete Object ${devId}: ${error && error.message ? error.message : 'without error message'}`;
582
+ }
583
+ }
584
+
585
+ async checkIfModelUpdate(entity) {
586
+ const model = entity.mapped ? entity.mapped.model : entity.device.modelID;
587
+ const device = entity.device;
588
+ const devId = utils.zbIdorIeeetoAdId(this.adapter, device.ieeeAddr, false);
589
+
590
+ const obj = await this.adapter.getObjectAsync(devId);
591
+ if (obj && obj.common.type !== model) {
592
+ await this.updateDev(devId, model, model);
593
+ await this.syncDevStates(device, model);
594
+ await this.deleteOrphanedDeviceStates(device.ieeeAddr, model,false, () => {}, true);
573
595
  }
574
596
  }
575
597
 
598
+
576
599
  async deleteOrphanedDeviceStates(ieeeAddr, model, force, callback, markOnly) {
577
600
  const devStates = await this.getDevStates(ieeeAddr, model);
578
601
  const commonStates = statesMapping.commonStates;
579
- const devId = ieeeAddr.substr(2);
602
+ const devId = utils.zbIdorIeeetoAdId(this.adapter, ieeeAddr, false);
580
603
  const messages = [];
581
604
  this.adapter.getStatesOf(devId, (err, states) => {
582
605
  if (!err && states) {
@@ -706,22 +729,14 @@ class StatesController extends EventEmitter {
706
729
  hasChanges = true;
707
730
  new_common.color = '#FF0000'
708
731
  value = minval
709
- if (!this.stashedErrors.hasOwnProperty(`${stateId}.min`))
710
- {
711
- this.warn(`State value for ${stateId} has value "${nval}" less than min "${minval}". - this eror is recorded and not repeated`);
712
- this.stashedErrors[`${stateId}.min`] = `State value for ${stateId} has value "${nval}." less than min "${minval}"`;
713
- }
732
+ this.stashErrors(`${stateId}.min`,`State value for ${stateId} has value "${nval}" less than min "${minval}".`, false );
714
733
  }
715
734
  if (typeof maxval == 'number' && nval > maxval) {
716
735
  hasChanges = true;
717
736
  hasChanges = true;
718
737
  new_common.color = '#FF0000'
719
738
  value = maxval;
720
- if (!this.stashedErrors.hasOwnProperty(`${stateId}.max`))
721
- {
722
- this.warn(`State value for ${stateId} has value "${nval}" more than max "${maxval}". - this eror is recorded and not repeated`);
723
- this.stashedErrors[`${stateId}.max`] = `State value for ${stateId} has value "${nval}" more than max "${maxval}".`;
724
- }
739
+ this.stashErrors(`${stateId}.max`,`State value for ${stateId} has value "${nval}" greater than max "${maxval}".`, false );
725
740
  }
726
741
  }
727
742
  }
@@ -777,6 +792,11 @@ class StatesController extends EventEmitter {
777
792
  value = sval === 'true' || sval === 'yes' || sval === 'on';
778
793
  }
779
794
  break;
795
+ case 'object' : break;
796
+ default:
797
+ if (['array','file','json','mixed','multistate','number','object','string'].includes(type)) break;
798
+ this.stashErrors(`etype:${id}`,`set_state_typed: trying to set a value on a strangely typed object: ${type} for id ${id} : ${JSON.stringify(Error().stack)}`);
799
+ break;
780
800
  }
781
801
  }
782
802
  this.adapter.setState(id, value, ack, callback);
@@ -798,15 +818,15 @@ class StatesController extends EventEmitter {
798
818
 
799
819
 
800
820
  async getDefaultGroupIcon(id, members) {
801
- let groupID = 0;
802
- if (typeof id == 'string') {
821
+ let groupID = Number(id);
822
+ if (typeof id == 'string' && isNaN(groupID)) {
803
823
  const regexResult = id.match(new RegExp(/group_(\d+)/));
804
824
  if (!regexResult) return '';
805
825
  groupID = Number(regexResult[1]);
806
826
  } else if (typeof id == 'number') {
807
827
  groupID = id;
808
828
  }
809
- if (groupID <= 0) return;
829
+ if (groupID <= 0 || groupID > 65535) return 'img/group_x.png';
810
830
  if (typeof members != 'number') {
811
831
  const group = await this.adapter.zbController.getGroupMembersFromController(groupID)
812
832
  if (typeof group == 'object')
@@ -845,8 +865,9 @@ class StatesController extends EventEmitter {
845
865
  }
846
866
  }
847
867
  }
868
+ const objId = model=='group' ? `group_${dev_id}` : dev_id;
848
869
 
849
- const obj = await this.adapter.getObjectAsync(id);
870
+ const obj = await this.adapter.getObjectAsync( objId);
850
871
 
851
872
  const myCommon = {
852
873
  name: __dev_name,
@@ -1005,7 +1026,7 @@ class StatesController extends EventEmitter {
1005
1026
 
1006
1027
  async syncDevStates(dev, model) {
1007
1028
  if (this.debugActive) this.debug('synchronizing device states for ' + dev.ieeeAddr + ' (' + model + ')');
1008
- const devId = dev.ieeeAddr.substr(2);
1029
+ const devId = utils.zbIdorIeeetoAdId(this.adapter, dev.ieeeAddr, false);
1009
1030
  // devId - iobroker device id
1010
1031
  const devStates = await this.getDevStates(dev.ieeeAddr, model);
1011
1032
  if (!devStates) {
@@ -1222,18 +1243,28 @@ class StatesController extends EventEmitter {
1222
1243
  const idx = cnt++;
1223
1244
  chain.push(new Promise((resolve) => {
1224
1245
  if (has_elevated_debug) this.emit('device_debug', {ID:debugId,data: { flag:`02.${cnt}a`, IO:true }, message:`converter ${cnt} : Cluster ${converter.cluster}`})
1225
- const payloadConv = converter.convert(mappedModel, message, publish, options, meta);
1226
- const metapost = meta ? {
1227
- deviceIEEE: meta.device ? meta.device.ieeeAddr : 'no device',
1228
- deviceModelId: meta.device ? meta.device.ModelId : 'no device',
1229
- logger: meta.logger ? (meta.logger.constructor ? meta.logger.constructor.name : 'not a class') : 'undefined',
1230
- state : meta.state
1231
- } : 'undefined';
1232
- if (has_elevated_debug) this.emit('device_debug', {ID:debugId,data: { flag:`02.${idx}b`, IO:true }, message:` data: ${safeJsonStringify(message.data)} options: ${safeJsonStringify(options)} meta:${safeJsonStringify(metapost)} result:${safeJsonStringify(payloadConv)}`})
1233
- if (typeof payloadConv === 'object') {
1234
- resolve(payloadConv);
1246
+ try {
1247
+ const payloadConv = converter.convert(mappedModel, message, publish, options, meta);
1248
+ if (has_elevated_debug) {
1249
+ const metapost = meta ? {
1250
+ deviceIEEE: meta.device ? meta.device.ieeeAddr : 'no device',
1251
+ deviceModelId: meta.device ? meta.device.ModelId : 'no device',
1252
+ logger: meta.logger ? (meta.logger.constructor ? meta.logger.constructor.name : 'not a class') : 'undefined',
1253
+ state : meta.state
1254
+ } : 'undefined';
1255
+ this.emit('device_debug', {ID:debugId,data: { flag:`02.${idx}b`, IO:true }, message:` data: ${safeJsonStringify(message.data)} options: ${safeJsonStringify(options)} meta:${safeJsonStringify(metapost)} result:${safeJsonStringify(payloadConv)}`})
1256
+ }
1257
+ if (typeof payloadConv === 'object') {
1258
+ resolve(payloadConv);
1259
+ }
1260
+ else resolve({});
1261
+ }
1262
+ catch (error) {
1263
+ const msg = `Error while processing converters DEVICE_ID: '${devId}' cluster '${converter.cluster}' type '${converter.type}' ${error && error.message ? ' message: ' + error.message : ''}`;
1264
+ if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { error:'EPROC', IO:true }, message: msg });
1265
+ this.warn(msg);
1266
+ resolve ({})
1235
1267
  }
1236
- else resolve({});
1237
1268
  }));
1238
1269
  }
1239
1270
  const candidates = await Promise.all(chain);
@@ -1246,7 +1277,7 @@ class StatesController extends EventEmitter {
1246
1277
 
1247
1278
  if (Object.keys(payload).length > 0 && Object.keys(options).length > 0) {
1248
1279
  const premsg = `candidates: ${JSON.stringify(candidates)} => payload ${JSON.stringify(payload)}`
1249
- this.postProcessConvertedFromZigbeeMessage(mappedModel, payload, options, null);
1280
+ this.postProcessConvertedFromZigbeeMessage(mappedModel, payload, options, message.device);
1250
1281
  if (has_elevated_debug) this.emit('device_debug', {ID:debugId,data: { flag:`02.${cnt}d`, IO:true }, message:`${premsg} => processed payload : ${JSON.stringify(payload)}`})
1251
1282
  }
1252
1283
  else if (has_elevated_debug) this.emit('device_debug', {ID:debugId,data: { flag:`02.${cnt}c`, IO:true }, message:`candidates: ${JSON.stringify(candidates)} => payload ${JSON.stringify(payload)}`})
@@ -1261,7 +1292,7 @@ class StatesController extends EventEmitter {
1261
1292
  const mappedModel = entity.mapped;
1262
1293
  const model = entity.mapped ? entity.mapped.model : entity.device.modelID;
1263
1294
  const cluster = message.cluster;
1264
- const devId = device.ieeeAddr.substr(2);
1295
+ const devId = utils.zbIdorIeeetoAdId(this.adapter, device.ieeeAddr, false);
1265
1296
  const meta = {device};
1266
1297
 
1267
1298
  const has_elevated_debug = this.checkDebugDevice(devId);
@@ -1285,7 +1316,7 @@ class StatesController extends EventEmitter {
1285
1316
  // this assigment give possibility to use iobroker logger in code of the converters, via meta.logger
1286
1317
  meta.logger = this;
1287
1318
 
1288
- await this.adapter.checkIfModelUpdate(entity);
1319
+ await this.checkIfModelUpdate(entity);
1289
1320
 
1290
1321
  let _voltage = 0;
1291
1322
  let _temperature = 0;
@@ -1344,7 +1375,7 @@ class StatesController extends EventEmitter {
1344
1375
  return;
1345
1376
  }
1346
1377
 
1347
- let converters = [...mappedModel.fromZigbee,...mappedModel.toZigbee].filter(c => c && c.cluster === cluster && (
1378
+ let converters = [...(mappedModel?.fromZigbee || []),...(mappedModel?.toZigbee || [])].filter(c => c && c.cluster === cluster && (
1348
1379
  Array.isArray(c.type) ? c.type.includes(type) : c.type === type));
1349
1380
 
1350
1381
 
package/lib/utils.js CHANGED
@@ -108,6 +108,27 @@ function flatten(arr) {
108
108
 
109
109
  const forceEndDevice = ['QBKG03LM', 'QBKG04LM', 'ZNMS13LM', 'ZNMS12LM'];
110
110
 
111
+
112
+ function zbIdorIeeetoAdId(adapter, source, withNamespace) {
113
+ const preface = withNamespace ? `${adapter.namespace}.` : '';
114
+ const s = String(source);
115
+ // 0xdeadbeefdeadbeef
116
+ // deadbeefdeadbeef
117
+ if (s.length > 15)
118
+ return `${preface}${source.replace('0x','')}`;
119
+ // 2
120
+ if (Number(s))
121
+ return `${preface}group_${source}`;
122
+ // group_2
123
+ return `${preface}${source}`;
124
+ }
125
+
126
+ function adIdtoZbIdorIeee(adapter, source) {
127
+ const s = `${source}`.replace(`${adapter.namespace}.`, '');
128
+ if (s.startsWith('group')) return Number(s.substring(6));
129
+ if (s.length === 16 && Number(`0x${s}`)) return `0x${s}`;
130
+ return 'illegal'
131
+ }
111
132
  /*flatten(
112
133
  ['QBKG03LM', 'QBKG04LM', 'ZNMS13LM', 'ZNMS12LM']
113
134
  .map(model => zigbeeHerdsmanConverters.findByModel(model))
@@ -176,6 +197,23 @@ function reverseByteString(source) {
176
197
  return '';
177
198
  }
178
199
 
200
+ function removeFromArray(arr, toRemove) {
201
+ let removed = 0;
202
+ if (Array.isArray(arr)) {
203
+ const _remove = toRemove ? [...toRemove] : [undefined, null];
204
+ let idx = 0;
205
+ while (idx < arr.length) {
206
+ if (_remove.includes(arr[idx])) {
207
+ arr.splice(idx, 1);
208
+ removed ++;
209
+ }
210
+ else
211
+ idx++;
212
+ }
213
+ }
214
+ return removed;
215
+ }
216
+
179
217
 
180
218
 
181
219
  exports.secondsToMilliseconds = seconds => seconds * 1000;
@@ -200,3 +238,6 @@ exports.getEntityInfo = getEntityInfo;
200
238
  exports.getNetAddress = getNetAddress;
201
239
  exports.byteArrayToString = byteArrayToString;
202
240
  exports.reverseByteString = reverseByteString;
241
+ exports.adIdtoZbIdorIeee = adIdtoZbIdorIeee;
242
+ exports.zbIdorIeeetoAdId = zbIdorIeeetoAdId;
243
+ exports.removeFromArray = removeFromArray;
@@ -99,7 +99,7 @@ class DelayedAction extends BaseExtension {
99
99
  continue;
100
100
  }
101
101
  actionDef.inAction = true;
102
- this.info(`Do action on ${device.ieeeAddr} ${device.modelID}`);
102
+ this.debug(`Do action on ${device.ieeeAddr} ${device.modelID}`);
103
103
  try {
104
104
  // do action
105
105
  await actionDef.action(device);
@@ -168,7 +168,7 @@ class DeviceAvailability extends BaseExtension {
168
168
  }
169
169
  }
170
170
  if (this.startReadDelay > 0 && readables.length > 0) {
171
- this.info(`Triggering device_query on ${readables.length} devices in ${this.startReadDelay / 1000} seconds.`)
171
+ this.debug(`Triggering device_query on ${readables.length} devices in ${this.startReadDelay / 1000} seconds.`)
172
172
  setTimeout(() => {
173
173
  readables.forEach(device => this.zigbee.doDeviceQuery(device, Date().now, false));
174
174
  }, this.startReadDelay)
@@ -312,9 +312,9 @@ class DeviceAvailability extends BaseExtension {
312
312
  this.state[ieeeAddr] = available;
313
313
  const payload = {available: available};
314
314
  this.debug(`Publish available for ${ieeeAddr} = ${available}`);
315
- this.zigbee.emit('publish', ieeeAddr.substr(2), entity.mapped.model, payload);
315
+ this.zigbee.emit('publish', utils.zbIdorIeeetoAdId(this.adapter, ieeeAddr, false), entity.mapped.model, payload);
316
316
  this.debug(`Publish LQ for ${ieeeAddr} = ${(available ? 10 : 0)}`);
317
- this.zigbee.emit('publish', ieeeAddr.substr(2), entity.mapped.model, {linkquality: (available ? 10 : 0)});
317
+ this.zigbee.emit('publish', utils.zbIdorIeeetoAdId(this.adapter, ieeeAddr, false), entity.mapped.model, {linkquality: (available ? 10 : 0)});
318
318
  }
319
319
  }
320
320
  }
File without changes
@@ -11,8 +11,7 @@ class DeviceEvent extends BaseExtension {
11
11
  async onZigbeeStarted() {
12
12
  for (const device of await this.zigbee.getClientIterator()) {
13
13
  const entity = await this.zigbee.resolveEntity(device);
14
-
15
- await this.callOnEvent(device, 'start', {device, options:entity.options || {}});
14
+ await this.callOnEvent(device, 'start', {device, options:entity?.options || {}});
16
15
  }
17
16
  }
18
17
 
@@ -39,6 +38,8 @@ class DeviceEvent extends BaseExtension {
39
38
  if (!mappedDevice) {
40
39
  mappedDevice = await zigbeeHerdsmanConverters.findByDevice(device);
41
40
  }
41
+ if (!device) return;
42
+
42
43
  const baseData = {device, deviceExposeChanged: function() { return; }, options: data.options || {}, state: data.state || {}}
43
44
  const eventData = {
44
45
  type,
@@ -62,6 +63,7 @@ class DeviceEvent extends BaseExtension {
62
63
  case 'deviceOptionsChanged':
63
64
  // NOTE: This does not currently work. OptionsChange is not yet defined.
64
65
  eventData.data = {...baseData, from:data.from || {}, to:data.to || {}};
66
+ eventData.data.options = data.to;
65
67
  break;
66
68
  }
67
69
 
@@ -179,13 +179,13 @@ class ZigbeeController extends EventEmitter {
179
179
  try {
180
180
  this.emit('pairing', 'stopping zigbee-herdsman');
181
181
  await this.herdsman.stop();
182
- this.emit('pairing', 'herdsman stopped !');
183
- this.herdsmanStarted = false;
184
182
  }
185
183
  catch (error) {
186
184
  this.emit('pairing', `error stopping zigbee-herdsman: ${error && error.message ? error.message : 'no reason given'}`);
187
185
  }
188
186
  this.isConfigured = false;
187
+ this.emit('pairing', 'herdsman stopped !');
188
+ this.herdsmanStarted = false;
189
189
  delete this.herdsman;
190
190
 
191
191
  }
@@ -263,13 +263,13 @@ class ZigbeeController extends EventEmitter {
263
263
  // Check if we have to turn off the LED
264
264
  try {
265
265
  if (this.disableLed) {
266
- this.info('Disable LED');
266
+ this.debug('Disable LED');
267
267
  await this.herdsman.setLED(false);
268
268
  } else {
269
269
  await this.herdsman.setLED(true);
270
270
  }
271
271
  } catch (e) {
272
- this.info('Unable to disable LED, unsupported function.');
272
+ this.debug('Unable to disable LED, unsupported function.');
273
273
  this.emit('pairing','Unable to disable LED, unsupported function.');
274
274
  }
275
275
 
@@ -287,7 +287,7 @@ class ZigbeeController extends EventEmitter {
287
287
  }
288
288
  //await this.adapter.stController.AddModelFromHerdsman(device, entity.mapped.model);
289
289
 
290
- this.adapter.getObject(device.ieeeAddr.substr(2), (err, obj) => {
290
+ this.adapter.getObject(utils.zbIdorIeeetoAdId(this.adapter, device.ieeeAddr, false), (err, obj) => {
291
291
  if (obj && obj.common && obj.common.deactivated) {
292
292
  this.callExtensionMethod('deregisterDevicePing', [device, entity]);
293
293
  } else {
@@ -613,7 +613,7 @@ class ZigbeeController extends EventEmitter {
613
613
 
614
614
  if (_key.kind == 'coordinator') {
615
615
  const coordinator = this.herdsman.getDevicesByType('Coordinator')[0];
616
- return {
616
+ if (coordinator) return {
617
617
  type: 'device',
618
618
  device: coordinator,
619
619
  endpoint: coordinator.getEndpoint(1),
@@ -652,7 +652,13 @@ class ZigbeeController extends EventEmitter {
652
652
  }
653
653
  if (device) {
654
654
  const t = Date.now();
655
- const mapped = await zigbeeHerdsmanConverters.findByDevice(device, false);
655
+ let mapped = undefined;
656
+ try {
657
+ mapped = await zigbeeHerdsmanConverters.findByDevice(device, false);
658
+ }
659
+ catch (error) {
660
+
661
+ }
656
662
  if (!mapped) {
657
663
  if (device.type === 'Coordinator')
658
664
  return {
@@ -662,7 +668,7 @@ class ZigbeeController extends EventEmitter {
662
668
  endpoint: device.getEndpoint(1),
663
669
  name: 'Coordinator',
664
670
  };
665
- this.warn(`Resolve Entity did not manage to find a mapped device for ${device.ieeeAddr} of type ${device.modelID}`);
671
+ this.emit('stash_unknown_model', `resoveEntity${device.ieeeAddr}`,`Resolve Entity did not manage to find a mapped device for ${device.ieeeAddr} of type ${device.modelID}`);
666
672
  }
667
673
  const endpoints = mapped && mapped.endpoint ? mapped.endpoint(device) : null;
668
674
  let endpoint;
@@ -764,11 +770,24 @@ class ZigbeeController extends EventEmitter {
764
770
  async permitJoin(permitTime, devid) {
765
771
  if (!this.herdsmanStarted) return false;
766
772
  try {
773
+ const starter = this._permitJoinTime > 0? 'Extending open network':'Opening network';
767
774
  this._permitJoinTime = permitTime;
768
- await this.herdsman.permitJoin(permitTime);
769
- this._permitJoinDevice = (permitTime > 0 && devid ? devid : '');
775
+ const dId = devid || this._permitJoinDevId
776
+ if (permitTime > 0) {
777
+ this.info(`${starter}${dId ? ' on device ' + dId : ''}.`);
778
+ this.emit('pairing', `${starter}${dId ? 'on device ' + dId : ''}.`);
779
+ await this.herdsman.permitJoin(permitTime, dId);
780
+ this._permitJoinDevId = dId;
781
+ }
782
+ else {
783
+ await this.herdsman.permitJoin(0);
784
+ this.info(`Closing network.`);
785
+ this.emit('pairing', `Closing network${this._permitJoinDevId ? ' on '+this._permitJoinDevId : ''}.`)
786
+ this._permitJoinDevId = undefined;
787
+ }
770
788
  } catch (e) {
771
789
  this._permitJoinTime = 0;
790
+ this._permitJoinDevId = undefined;
772
791
  this.sendError(e);
773
792
  this.error(`Failed to open the network: ${e.stack}`);
774
793
  return false;
@@ -783,20 +802,19 @@ class ZigbeeController extends EventEmitter {
783
802
  if (data.permitted) {
784
803
  if (!this._permitJoinInterval) {
785
804
  this.emit('pairing',`Pairing possible for ${this._permitJoinTime} seconds`)
786
- this.info(`Opening zigbee Network for ${this._permitJoinTime} seconds`)
805
+ this.info(`Opened zigbee Network for ${this._permitJoinTime} seconds`)
787
806
  this._permitJoinInterval = setInterval(async () => {
788
807
  this.emit('pairing', 'Pairing time left', this._permitJoinTime);
789
808
  this._permitJoinTime -= 1;
790
809
  }, 1000);
791
810
  }
792
811
  }
793
- else {
794
- this.info(`Closing Zigbee network, ${this._permitJoinTime} seconds remaining`)
812
+ else if (this._permitJoinInterval) {
813
+ const timestr = this._permitJoinTime > 0 ? ` with ${this._permitJoinTime} second${this._permitJoinTime > 1 ? 's':''} remaining.`: '.';
814
+ this.info(`Closed Zigbee network${timestr}`)
815
+ this.emit('pairing', `Closed network${timestr}`);
795
816
  clearInterval(this._permitJoinInterval);
796
817
  this._permitJoinInterval = null;
797
- // this.emit('pairing', 'Pairing time left', 0);
798
- this.emit('pairing', 'Closing network.',0);
799
- this._permitJoinDevice = '';
800
818
  }
801
819
  }
802
820
  catch (error) {
@@ -864,7 +882,7 @@ class ZigbeeController extends EventEmitter {
864
882
  this.emit('device_debug', {ID: Date.now(), data: {flag:'dl', states:[{id: '--', value:'--', payload:message}], IO:true},message:`Device '${friendlyName}' has left the network`});
865
883
  }
866
884
  else
867
- this.info(`Device '${friendlyName}' left the network`);
885
+ this.warn(`Device '${friendlyName}' left the network`);
868
886
  this.emit('leave', message.ieeeAddr);
869
887
  // Call extensions
870
888
  this.callExtensionMethod(
@@ -997,7 +1015,7 @@ class ZigbeeController extends EventEmitter {
997
1015
 
998
1016
  const is = data.device.interviewState;
999
1017
  if (is != 'SUCCESSFUL' && is != 'FAILED') {
1000
- this.info(`message ${JSON.stringify(data)} received during interview.`)
1018
+ this.debug(`message ${JSON.stringify(data)} received during interview.`)
1001
1019
  }
1002
1020
  const entity = await this.resolveEntity(data.device || data.ieeeAddr);
1003
1021
  const name = (entity && entity._modelID) ? entity._modelID : data.device.ieeeAddr;
@@ -1160,6 +1178,7 @@ class ZigbeeController extends EventEmitter {
1160
1178
  }
1161
1179
  */
1162
1180
  }
1181
+
1163
1182
  // publish via converter
1164
1183
  //
1165
1184
  async publishFromState(deviceId, model, stateModel, stateList, options, debugID, has_elevated_debug) {
@@ -1408,11 +1427,12 @@ class ZigbeeController extends EventEmitter {
1408
1427
  payloadObj = payload;
1409
1428
  } else return { success: false, error: 'illegal type of payload: ' + typeof payload};
1410
1429
 
1430
+
1411
1431
  if (payloadObj.hasOwnProperty('device') && payloadObj.hasOwnProperty('payload')) {
1412
1432
  try {
1413
- const isDevice = !payload.device.includes('group_');
1433
+ const isDevice = !payloadObj.device.includes('group_');
1414
1434
  const stateList = [];
1415
- const devID = isDevice ? `0x${payload.device}` : parseInt(payload.device.replace('group_', ''));
1435
+ const devID = isDevice ? `0x${payloadObj.device}` : parseInt(payloadObj.device.replace('group_', ''));
1416
1436
 
1417
1437
  const entity = await this.resolveEntity(devID);
1418
1438
  if (!entity) {