iobroker.zigbee 3.1.5 → 3.2.0

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.
@@ -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,11 +613,12 @@ 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),
620
620
  name: 'Coordinator',
621
+ mapped: { model: 'Coordinator'},
621
622
  options:{}
622
623
  };
623
624
  }
@@ -651,16 +652,23 @@ class ZigbeeController extends EventEmitter {
651
652
  }
652
653
  if (device) {
653
654
  const t = Date.now();
654
- 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
+ }
655
662
  if (!mapped) {
656
663
  if (device.type === 'Coordinator')
657
664
  return {
658
665
  type: 'device',
659
666
  device: device,
667
+ mapped: { model: 'Coordinator'},
660
668
  endpoint: device.getEndpoint(1),
661
669
  name: 'Coordinator',
662
670
  };
663
- 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}`);
664
672
  }
665
673
  const endpoints = mapped && mapped.endpoint ? mapped.endpoint(device) : null;
666
674
  let endpoint;
@@ -736,6 +744,7 @@ class ZigbeeController extends EventEmitter {
736
744
  await this.herdsman.stop();
737
745
  this.herdsmanStarted = false;
738
746
  this.info('zigbecontroller stopped successfully');
747
+ return;
739
748
  }
740
749
  else this.info('zigbecontroller stopped successfully - ZH was not running');
741
750
  } catch (error) {
@@ -759,12 +768,26 @@ class ZigbeeController extends EventEmitter {
759
768
 
760
769
  // Permit join
761
770
  async permitJoin(permitTime, devid) {
771
+ if (!this.herdsmanStarted) return false;
762
772
  try {
773
+ const starter = this._permitJoinTime > 0? 'Extending open network':'Opening network';
763
774
  this._permitJoinTime = permitTime;
764
- await this.herdsman.permitJoin(permitTime);
765
- 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
+ }
766
788
  } catch (e) {
767
789
  this._permitJoinTime = 0;
790
+ this._permitJoinDevId = undefined;
768
791
  this.sendError(e);
769
792
  this.error(`Failed to open the network: ${e.stack}`);
770
793
  return false;
@@ -779,20 +802,19 @@ class ZigbeeController extends EventEmitter {
779
802
  if (data.permitted) {
780
803
  if (!this._permitJoinInterval) {
781
804
  this.emit('pairing',`Pairing possible for ${this._permitJoinTime} seconds`)
782
- this.info(`Opening zigbee Network for ${this._permitJoinTime} seconds`)
805
+ this.info(`Opened zigbee Network for ${this._permitJoinTime} seconds`)
783
806
  this._permitJoinInterval = setInterval(async () => {
784
807
  this.emit('pairing', 'Pairing time left', this._permitJoinTime);
785
808
  this._permitJoinTime -= 1;
786
809
  }, 1000);
787
810
  }
788
811
  }
789
- else {
790
- 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}`);
791
816
  clearInterval(this._permitJoinInterval);
792
817
  this._permitJoinInterval = null;
793
- // this.emit('pairing', 'Pairing time left', 0);
794
- this.emit('pairing', 'Closing network.',0);
795
- this._permitJoinDevice = '';
796
818
  }
797
819
  }
798
820
  catch (error) {
@@ -860,7 +882,7 @@ class ZigbeeController extends EventEmitter {
860
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`});
861
883
  }
862
884
  else
863
- this.info(`Device '${friendlyName}' left the network`);
885
+ this.warn(`Device '${friendlyName}' left the network`);
864
886
  this.emit('leave', message.ieeeAddr);
865
887
  // Call extensions
866
888
  this.callExtensionMethod(
@@ -993,7 +1015,7 @@ class ZigbeeController extends EventEmitter {
993
1015
 
994
1016
  const is = data.device.interviewState;
995
1017
  if (is != 'SUCCESSFUL' && is != 'FAILED') {
996
- this.info(`message ${JSON.stringify(data)} received during interview.`)
1018
+ this.debug(`message ${JSON.stringify(data)} received during interview.`)
997
1019
  }
998
1020
  const entity = await this.resolveEntity(data.device || data.ieeeAddr);
999
1021
  const name = (entity && entity._modelID) ? entity._modelID : data.device.ieeeAddr;
@@ -1156,6 +1178,7 @@ class ZigbeeController extends EventEmitter {
1156
1178
  }
1157
1179
  */
1158
1180
  }
1181
+
1159
1182
  // publish via converter
1160
1183
  //
1161
1184
  async publishFromState(deviceId, model, stateModel, stateList, options, debugID, has_elevated_debug) {
@@ -1404,11 +1427,12 @@ class ZigbeeController extends EventEmitter {
1404
1427
  payloadObj = payload;
1405
1428
  } else return { success: false, error: 'illegal type of payload: ' + typeof payload};
1406
1429
 
1430
+
1407
1431
  if (payloadObj.hasOwnProperty('device') && payloadObj.hasOwnProperty('payload')) {
1408
1432
  try {
1409
- const isDevice = !payload.device.includes('group_');
1433
+ const isDevice = !payloadObj.device.includes('group_');
1410
1434
  const stateList = [];
1411
- const devID = isDevice ? `0x${payload.device}` : parseInt(payload.device.replace('group_', ''));
1435
+ const devID = isDevice ? `0x${payloadObj.device}` : parseInt(payloadObj.device.replace('group_', ''));
1412
1436
 
1413
1437
  const entity = await this.resolveEntity(devID);
1414
1438
  if (!entity) {
package/main.js CHANGED
@@ -37,7 +37,7 @@ const dmZigbee = require('./lib/devicemgmt.js');
37
37
  const DeviceDebug = require('./lib/DeviceDebug');
38
38
  const dns = require('dns');
39
39
  const net = require('net');
40
- const { getNetAddress } = require('./lib/utils')
40
+ const { getNetAddress, zbIdorIeeetoAdId, adIdtoZbIdorIeee , removeFromArray } = require('./lib/utils');
41
41
 
42
42
  const createByteArray = function (hexString) {
43
43
  const bytes = [];
@@ -231,6 +231,8 @@ class Zigbee extends utils.Adapter {
231
231
  this.stController.on('changed', this.zbController.publishFromState.bind(this.zbController));
232
232
  this.stController.on('device_query', this.zbController.deviceQuery.bind(this.zbController));
233
233
  this.zbController.on('acknowledge_state', this.acknowledgeState.bind(this));
234
+ this.zbController.on('stash_error', this.stController.stashErrors.bind(this.stController));
235
+ this.zbController.on('stash_unknown_model', this.stController.stashUnknownModel.bind(this.stController));
234
236
 
235
237
  this.zbController.configure(zigbeeOptions);
236
238
  this.zbController.debugActive = this.debugActive;
@@ -302,6 +304,7 @@ class Zigbee extends utils.Adapter {
302
304
 
303
305
  * getExternalDefinition() {
304
306
 
307
+
305
308
  if (this.config.external === undefined) {
306
309
  return;
307
310
  }
@@ -353,20 +356,22 @@ class Zigbee extends utils.Adapter {
353
356
  try {
354
357
  this.log.warn('Trying to run sandbox for ' + mN);
355
358
  vm.runInNewContext(modifiedCode, sandbox);
356
- const converter = sandbox.module.exports;
359
+ const sandboxResult = sandbox.module.exports;
360
+ const converter = Array.isArray(sandboxResult) ? sandboxResult : [sandboxResult];
357
361
 
358
- if (Array.isArray(converter)) for (const item of converter) {
359
- this.log.info('Model ' + item.model + ' defined in external converter ' + mN);
362
+ for (const item of converter) {
360
363
  if (item.hasOwnProperty('icon')) {
361
364
  if (!item.icon.toLowerCase().startsWith('http') && !item.useadaptericon)
362
365
  item.icon = path.join(path.dirname(mN), item.icon);
363
366
  }
364
- yield item;
365
- }
366
- else {
367
- this.log.info('Model ' + converter.model + ' defined in external converter ' + mN);
368
- yield converter;
367
+ const rtz = removeFromArray(item.toZigbee);
368
+ const rfz = removeFromArray(item.fromZigbee);
369
+ const rtzfzmsg = [];
370
+ if (rtz) rtzfzmsg.push(`${rtz} unknown entr${rtz>1?'ies' : 'y'} in toZigbee`);
371
+ if (rfz) rtzfzmsg.push(`${rfz} unknown entr${rtz>1?'ies' : 'y'} in fromZigbee`);
372
+ this.log.info(`Model ${item.model} defined ${rtz+rfz ? 'with '+ rtzfzmsg.join(' and ') + ' ' : ''}in external converter ${mN}`);
369
373
  }
374
+ yield converter;
370
375
  }
371
376
  catch (e) {
372
377
  this.log.error(`Unable to apply converter from module: ${mN} - the code does not run: ${e}`);
@@ -381,17 +386,19 @@ class Zigbee extends utils.Adapter {
381
386
 
382
387
  applyExternalConverters() {
383
388
  for (const definition of this.getExternalDefinition()) {
384
- const toAdd = {...definition};
385
- delete toAdd['homeassistant'];
389
+ const toAdd = definition;
386
390
  try {
387
- const t = Date.now();
388
- if (zigbeeHerdsmanConverters.hasOwnProperty('addExternalDefinition')) {
389
- zigbeeHerdsmanConverters.addExternalDefinition(toAdd);
390
- this.log.info(`added external converter using addExternalDefinition (${Date.now()-t} ms)`)
391
- }
392
- else if (zigbeeHerdsmanConverters.hasOwnProperty('addDefinition')) {
393
- zigbeeHerdsmanConverters.addDefinition(toAdd);
394
- this.log.info(`added external converter using addDefinition (${Date.now()-t} ms)`);
391
+ for (const item of toAdd) {
392
+ delete item['homeassistant'];
393
+ const t = Date.now();
394
+ if (zigbeeHerdsmanConverters.hasOwnProperty('addExternalDefinition')) {
395
+ zigbeeHerdsmanConverters.addExternalDefinition(item);
396
+ this.log.info(`added external converter using addExternalDefinition (${Date.now()-t} ms)`)
397
+ }
398
+ else if (zigbeeHerdsmanConverters.hasOwnProperty('addDefinition')) {
399
+ zigbeeHerdsmanConverters.addDefinition(item);
400
+ this.log.info(`added external converter using addDefinition (${Date.now()-t} ms)`);
401
+ }
395
402
  }
396
403
  } catch (e) {
397
404
  this.log.error(`unable to apply external converter for ${JSON.stringify(toAdd.model)}: ${e && e.message ? e.message : 'no error message available'}`);
@@ -399,32 +406,39 @@ class Zigbee extends utils.Adapter {
399
406
  }
400
407
  }
401
408
 
402
- async testConnect(from, command, message, callback) {
409
+ async testConnect(message) {
403
410
  const response = {};
411
+ if (this.reconnectTimer) this.clearTimeout(this.reconnectTimer);
412
+ this.reconnectTimer = null;
413
+ const zo = message.zigbeeOptions || {};
404
414
  if (message.start) {
405
415
  try {
406
- this.logToPairing(`overriding zigbee options with:`);
407
- for (const k of Object.keys(message.zigbeeOptions)) {
408
- this.logToPairing(`${k} : ${message.zigbeeOptions[k]}`)
416
+ const keys = Object.keys(zo);
417
+ if (keys) {
418
+ this.logToPairing(`overriding zigbee options with:`);
419
+ for (const k of keys) {
420
+ this.logToPairing(`${k} : ${zo[k]}`)
421
+ }
409
422
  }
410
- this.zbController.configure(this.getZigbeeOptions(message.zigbeeOptions));
423
+ this.zbController.configure(this.getZigbeeOptions(zo));
411
424
  response.status = await this.doConnect(true);
412
425
  if (!response.status) response.error = { message: 'Unable to start the Zigbee Network. Please check the previous messages.'}
413
- this.sendTo(from, command, response, callback);
426
+ return response;
414
427
  }
415
428
  catch (error) {
416
- this.sendTo(from, command, { status:false, error }, callback);
429
+ return { status:false, error };
417
430
  }
418
431
  }
419
432
  else try {
420
433
  await this.zbController.stopHerdsman();
421
- this.sendTo(from, command, { status:true }, callback);
434
+ return { status:true };
422
435
  } catch (error) {
423
- this.sendTo(from, command, { status:true, error }, callback);
436
+ return { status:true, error };
424
437
  }
425
438
  }
426
439
 
427
440
  async doConnect(noReconnect) {
441
+
428
442
  let debugversion = '';
429
443
  try {
430
444
  const DebugIdentify = require('./debugidentify');
@@ -625,7 +639,7 @@ class Zigbee extends utils.Adapter {
625
639
  const model = entity.mapped ? entity.mapped.model : entity.device.modelID;
626
640
  const idx = devicesFromObjects.indexOf(device.ieeeAddr);
627
641
  if (idx > -1) devicesFromObjects.splice(idx, 1);
628
- this.stController.updateDev(device.ieeeAddr.substr(2), model, model, () =>
642
+ this.stController.updateDev(zbIdorIeeetoAdId(this.adapter, device.ieeeAddr, false), model, model, () =>
629
643
  this.stController.syncDevStates(device, model));
630
644
  }
631
645
  else (this.log.warn('resolveEntity returned no entity'));
@@ -643,25 +657,29 @@ class Zigbee extends utils.Adapter {
643
657
  }
644
658
 
645
659
 
660
+ /*
646
661
  async checkIfModelUpdate(entity) {
647
662
  const model = entity.mapped ? entity.mapped.model : entity.device.modelID;
648
663
  const device = entity.device;
649
- const devId = device.ieeeAddr.substr(2);
664
+ const devId = zbIdorIeeetoAdId(this.adapter, device.ieeeAddr, false);//device.ieeeAddr.substr(2);
650
665
 
651
666
  const obj = await this.getObjectAsync(devId);
652
667
  if (obj && obj.common.type !== model) {
653
- await this.stController.deleteObj(devId);
668
+ // await this.stController.deleteObj(devId);
654
669
  await this.stController.updateDev(devId, model, model);
655
670
  await this.stController.syncDevStates(device, model);
671
+ await this.stController.deleteOrphanedDeviceStates();
656
672
  }
657
673
  }
674
+ */
658
675
 
659
676
  acknowledgeState(deviceId, model, stateDesc, value) {
660
- const stateId = (model === 'group' ?
677
+ const stateId = `${zbIdorIeeetoAdId(this, deviceId, true)}.${stateDesc.id}`;
678
+ /*const stateId = (model === 'group' ?
661
679
  `${this.namespace}.group_${deviceId}.${stateDesc.id}` :
662
- `${this.namespace}.${deviceId.replace('0x', '')}.${stateDesc.id}`);
680
+ `${this.namespace}.${deviceId.replace('0x', '')}.${stateDesc.id}`); */
663
681
  if (value === undefined) try {
664
- this.getState(stateId, (err, state) => { if (!err && state.hasOwnProperty('val')) this.setState(stateId, state.val, true)});
682
+ this.getState(stateId, (err, state) => { if (!err && state?.hasOwnProperty('val')) this.setState(stateId, state.val, true)});
665
683
  }
666
684
  catch (error) {
667
685
  this.log.warn(`Error acknowledging ${stateId} without value: ${error && error.message ? error.message : 'no reason given'}`);
@@ -691,13 +709,13 @@ class Zigbee extends utils.Adapter {
691
709
  }
692
710
  await this.stController.AddModelFromHerdsman(entity.device, model)
693
711
  if (dev) {
694
- this.getObject(dev.ieeeAddr.substr(2), (err, obj) => {
712
+ this.getObject(zbIdorIeeetoAdId(this.adapter, dev.ieeeAddr, false), (err, obj) => {
695
713
  if (!obj) {
696
714
  const model = (entity.mapped) ? entity.mapped.model : entity.device.modelID;
697
715
  if (this.debugActive) this.log.debug(`new device ${dev.ieeeAddr} ${dev.networkAddress} ${model} `);
698
716
 
699
717
  this.logToPairing(`New device joined '${dev.ieeeAddr}' model ${model}`, true);
700
- this.stController.updateDev(dev.ieeeAddr.substr(2), model, model, () =>
718
+ this.stController.updateDev(zbIdorIeeetoAdId(this.adapter, dev.ieeeAddr, false), model, model, () =>
701
719
  this.stController.syncDevStates(dev, model));
702
720
  }
703
721
  else if (this.debugActive) this.log.debug(`Device ${safeJsonStringify(entity)} rejoined, no new device`);
@@ -708,7 +726,7 @@ class Zigbee extends utils.Adapter {
708
726
  leaveDevice(ieeeAddr) {
709
727
  if (this.debugActive) this.log.debug(`Leave device event: ${ieeeAddr}`);
710
728
  if (ieeeAddr) {
711
- const devId = ieeeAddr.substr(2);
729
+ const devId = zbIdorIeeetoAdId(this.adapter, ieeeAddr, false);
712
730
  if (this.debugActive) this.log.debug(`Delete device ${devId} from iobroker.`);
713
731
  this.stController.deleteObj(devId);
714
732
  }
@@ -808,6 +826,7 @@ class Zigbee extends utils.Adapter {
808
826
  dbDir: dbDir,
809
827
  dbPath: 'shepherd.db',
810
828
  backupPath: 'nvbackup.json',
829
+ localConfigPath: 'LocalOverrides.json',
811
830
  disableLed: this.config.disableLed,
812
831
  disablePing: (this.config.pingCluster=='off'),
813
832
  transmitPower: this.config.transmitPower,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.zigbee",
3
- "version": "3.1.5",
3
+ "version": "3.2.0",
4
4
  "author": {
5
5
  "name": "Kirov Ilya",
6
6
  "email": "kirovilya@gmail.com"
@@ -29,7 +29,7 @@
29
29
  "uri-js": "^4.4.1",
30
30
  "typescript": "^5.9.2",
31
31
  "zigbee-herdsman": "^6.0.0",
32
- "zigbee-herdsman-converters": "25.31.0"
32
+ "zigbee-herdsman-converters": "^25.37.0"
33
33
  },
34
34
  "description": "Zigbee devices",
35
35
  "devDependencies": {
@@ -39,12 +39,10 @@
39
39
  "@alcalzone/release-script-plugin-manual-review": "^3.7.0",
40
40
  "@iobroker/testing": "^5.1.1",
41
41
  "chai": "^5.2.1",
42
- "chai-as-promised": "^7.1.1",
43
42
  "eslint": "^9.36.0",
44
43
  "eslint-config-prettier": "^9.1.0",
45
44
  "eslint-plugin-prettier": "^5.5.4",
46
45
  "mixin-deep": "^2.0.1",
47
- "mocha": "^11.7.1",
48
46
  "@iobroker/dev-server": "^0.7.8"
49
47
  },
50
48
  "homepage": "https://github.com/ioBroker/ioBroker.zigbee",