iobroker.zigbee 1.10.14 → 2.0.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.
package/README.md CHANGED
@@ -137,6 +137,9 @@ You can thank the authors by these links:
137
137
 
138
138
  -----------------------------------------------------------------------------------------------------
139
139
  ## Changelog
140
+ ### 2.0.0 (2025-02-22)
141
+ * (asgothian) new herdsman adaption
142
+
140
143
  ### 1.10.14 (2025-01-01)
141
144
  * (arteck) Herdsman 2.1.9, Converters 20.58.0
142
145
  * (asgothian) Fix: Aqara T1M (CL-L02D)
@@ -208,4 +211,28 @@ You can thank the authors by these links:
208
211
  * Some design update
209
212
  * Binding
210
213
 
214
+
211
215
  ------------------------------------------------------------------------------
216
+
217
+ ## License
218
+ The MIT License (MIT)
219
+
220
+ Copyright (c) 2018-2025 Kirov Ilya <kirovilya@gmail.com>
221
+
222
+ Permission is hereby granted, free of charge, to any person obtaining a copy
223
+ of this software and associated documentation files (the "Software"), to deal
224
+ in the Software without restriction, including without limitation the rights
225
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
226
+ copies of the Software, and to permit persons to whom the Software is
227
+ furnished to do so, subject to the following conditions:
228
+
229
+ The above copyright notice and this permission notice shall be included in
230
+ all copies or substantial portions of the Software.
231
+
232
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
233
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
234
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
235
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
236
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
237
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
238
+ THE SOFTWARE.
@@ -654,7 +654,7 @@
654
654
  <a id="state_cleanup_btn"
655
655
  class="btn-floating waves-effect waves-light red tooltipped center-align hoverable translateT"
656
656
  title="State Cleanup">
657
- <i class="material-icons large icon-blue">sync</i></a>
657
+ <i class="material-icons large icon-blue">delete_sweep</i></a>
658
658
  </li>
659
659
  <li>
660
660
  <a id="fw_check_btn"
package/admin/tab_m.html CHANGED
@@ -660,7 +660,7 @@
660
660
  <ul id="nav-mobile" class="right">
661
661
  <li>
662
662
  <a id="state_cleanup_btn" class="btn-floating waves-effect waves-light red tooltipped center-align hoverable translateT" title="State Cleanup">
663
- <i class="material-icons large icon-blue">sync</i></a>
663
+ <i class="material-icons large icon-blue">delete_sweep</i></a>
664
664
  </li>
665
665
  <li>
666
666
  <a id="fw_check_btn" class="btn-floating waves-effect waves-light blue tooltipped center-align hoverable translateT" title="Check firmware updates">
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "zigbee",
4
- "version": "1.10.14",
4
+ "version": "2.0.0",
5
5
  "news": {
6
+ "2.0.0": {
7
+ "en": "new herdsman adaption",
8
+ "de": "neue herdsman adaption",
9
+ "ru": "адаптация нового пастуха",
10
+ "pt": "nova adaptação herdsman",
11
+ "nl": "nieuwe aanpassing van herders",
12
+ "fr": "nouvelle adaptation des éleveurs",
13
+ "it": "nuovo adattamento mandriano",
14
+ "es": "nueva adaptación del pastor",
15
+ "pl": "nowa adaptacja zioła",
16
+ "uk": "новий гердман адаптація",
17
+ "zh-cn": "新牧民适应"
18
+ },
19
+ "1.10.15": {
20
+ "en": "Work in progress",
21
+ "de": "Work in progress",
22
+ "ru": "Work in progress",
23
+ "pt": "Work in progress",
24
+ "nl": "Work in progress",
25
+ "fr": "Work in progress",
26
+ "it": "Work in progress",
27
+ "es": "Work in progress",
28
+ "pl": "Work in progress",
29
+ "uk": "Work in progress",
30
+ "zh-cn": "Work in progress"
31
+ },
6
32
  "1.10.14": {
7
33
  "en": "Herdsman 2.1.9, Converters 20.58.0\nFix: Aqara T1M (CL-L02D) \ndeleteDeviceStates change to deleteObj",
8
34
  "de": "Herdsman 2.1.9, Konverter 20.58.0\nFix: Aqara T1M (CL-L02D)\nlöschen DeviceState Änderung zum Löschen Ob",
@@ -54,45 +80,6 @@
54
80
  "pl": "ZMIANA ZBIORCZA\n\nbłędy: ChannelScan jest obecnie niedostępny\n\n\nnaprawić błędy lintera\nwyłączyć wyświetlacz mapy dla wyłączonych urządzeń\nnowa opcja na mapie: wyłączenie interakcji fizyki\nnowe konwertery zigbee- herdsman- 20.28.0\nnowy zigbee-herdsman 2.1.1\nZezwalaj na stosowanie konwerterów bezklawiszowych (używanych dla TuYa i kompatybilnych urządzeń w konwerterach zigbee- herdsman-\nzamiana z wniosku na aksjos\nusuń grupy działa ponownie",
55
81
  "uk": "БРЕАКІНГ ЗМІН\n\nпомилки : CanalScan наразі немає\n\n\nвиправлено помилки linter\nвідключення відображення карти для деактивованих пристроїв\nновий варіант на карті: відключена фізика взаємодія\nнові zigbee-herdsman-converters 20.28.0\nновий zigbee-herdsman 2.1.1\nДозволити використання без ключів конвертерів (використовується для TuYa та сумісних пристроїв в zigbee-herdsman-converters\nковтання від запиту в осях\nвидалити групи знову",
56
82
  "zh-cn": "破坏变化\n\n错误 : 频道扫描目前不可用\n\n\n修正线性错误\n禁用已停用设备的地图显示\n地图上的新选项: 禁用物理交互\n新齐格比-牧民-转换器 20.28.0\n新齐格比牧民 2.1.1\n允许使用无密钥转换器( 用于 TuYa 和 Zigbee- herdsman- 转换器中的兼容设备)\n从请求转换为轴\n再次删除组工作"
57
- },
58
- "1.10.10": {
59
- "en": "core update\ndependency update",
60
- "de": "kern-update\naktualisierung der abhängigkeit",
61
- "ru": "обновление\nобновление",
62
- "pt": "atualização do núcleo\natualização de dependência",
63
- "nl": "kernupdate\nafhankelijkheidsupdate",
64
- "fr": "mise à jour de base\nmise à jour de la dépendance",
65
- "it": "aggiornamento del core\naggiornamento della dipendenza",
66
- "es": "actualización básica\nactualización de la dependencia",
67
- "pl": "podstawowa aktualizacja\naktualizacja zależności",
68
- "uk": "оновлення ядра\nоновлення залежності",
69
- "zh-cn": "核心更新\n依赖性更新"
70
- },
71
- "1.10.9": {
72
- "en": "typo admin settings\neslint config",
73
- "de": "typo admin einstellungen\neslint config",
74
- "ru": "настройки admin\neslint config",
75
- "pt": "configurações de administração do tipo\neslint config",
76
- "nl": "typo admin instellingen\neslint config",
77
- "fr": "paramètres d'administration typo\nconfig eslint",
78
- "it": "impostazioni dell'amministratore\neslint config",
79
- "es": "configuración de administración de tipo\neslint config",
80
- "pl": "ustawienia admin typo\neslint config",
81
- "uk": "параметри адміністратора\neslint config",
82
- "zh-cn": "类型管理员设置\n埃斯林特配置"
83
- },
84
- "1.10.8": {
85
- "en": "corr admin settings\nadd new eslint version\n",
86
- "de": "korr admin einstellungen\nneue eslint-version hinzufügen\n",
87
- "ru": "настройки администрирования\nдобавить новую версию eslint\n",
88
- "pt": "configurações de administração de corrimento\nadicionar nova versão eslint\n",
89
- "nl": "corr admin instellingen\nnieuwe eslint versie toevoegen\n",
90
- "fr": "paramètres d'administration corr\najouter une nouvelle version eslint\n",
91
- "it": "impostazioni dell'amministratore\naggiungere nuova versione eslint\n",
92
- "es": "configuración de administración de corr\nañadir nueva versión eslint\n",
93
- "pl": "ustawienia admin corr\ndodaj nową wersję eslint\n",
94
- "uk": "корр налаштування адміністратора\nдодати нову версію eslint\n",
95
- "zh-cn": "corr 管理员设置\n添加新的 eslint 版本\n"
96
83
  }
97
84
  },
98
85
  "titleLang": {
@@ -192,7 +179,7 @@
192
179
  ],
193
180
  "dependencies": [
194
181
  {
195
- "js-controller": ">=3.3.0"
182
+ "js-controller": ">=5.0.19"
196
183
  }
197
184
  ],
198
185
  "messages": [
package/lib/binding.js CHANGED
@@ -238,7 +238,7 @@ class Binding {
238
238
  await this.doBindUnbind(type, bind_source, bind_source_ep, 'coordinator', '1');
239
239
  this.debug('Successfully ' + (type === 'bind' ? 'bound' : 'unbound') + ' Coordinator from ' + bind_source);
240
240
  } catch (e) {
241
- this.error(`Could not ${type} Coordinator from ${bind_source}: ${JSON.stringify(e)}`);
241
+ this.error(`Could not ${type} Coordinator from ${bind_source}: ${(e && e.message ? e.message : 'no error message')} ${(e && e.stack ? e.stack : 'no call stack')}`);
242
242
  }
243
243
  }
244
244
  } catch (error) {
package/lib/commands.js CHANGED
@@ -150,8 +150,8 @@ class Commands {
150
150
  return;
151
151
  }
152
152
  }
153
- catch (e) {
154
- this.error(JSON.stringify(e));
153
+ catch (error) {
154
+ this.error(`Pairing with code failed with: ${(error && error.message ? error.message : 'no error message')} ${(error && error.stack ? error.stack : 'no call stack')}`);
155
155
  this.adapter.sendTo(
156
156
  from, command,
157
157
  {error: 'Exception when trying to add QR code'},
package/lib/exposes.js CHANGED
@@ -681,12 +681,15 @@ function createFromExposes(model, def) {
681
681
  if (!Array.isArray(expose.values)) break;
682
682
  const hasHold = expose.values.find((actionName) => actionName.includes('hold'));
683
683
  const hasRelease = expose.values.find((actionName) => actionName.includes('release'));
684
+ const hasPress = expose.values.find((actionName) => actionName.includes('press'));
685
+ const hasPressRelease = expose.values.find((actionName) => actionName.includes('press_release'));
684
686
  for (const actionName of expose.values) {
685
687
  // is release state ? - skip
686
688
  if (hasHold && hasRelease && actionName.includes('release')) continue;
687
689
  // is hold state ?
688
690
  if (hasHold && hasRelease && actionName.includes('hold')) {
689
691
  const releaseActionName = actionName.replace('hold', 'release');
692
+ const releaseActionName2 = actionName.concat('_release');
690
693
  state = {
691
694
  id: actionName.replace(/\*/g, ''),
692
695
  prop: 'action',
@@ -696,9 +699,11 @@ function createFromExposes(model, def) {
696
699
  write: false,
697
700
  read: true,
698
701
  type: 'boolean',
699
- getter: payload => payload.action === actionName ? true : (payload.action === releaseActionName ? false : undefined),
702
+ getter: payload => payload.action === actionName ? true : (payload.action === releaseActionName || payload.action === releaseActionName2 ? false : undefined),
700
703
  };
701
- } else {
704
+ } else if (hasPress && hasPressRelease && actionName.includes('press')) {
705
+ let getterKey = actionName.concat('_release');
706
+ if (expose.values.indexOf(getterKey) < 0) getterKey = actionName;
702
707
  state = {
703
708
  id: actionName.replace(/\*/g, ''),
704
709
  prop: 'action',
@@ -708,10 +713,23 @@ function createFromExposes(model, def) {
708
713
  write: false,
709
714
  read: true,
710
715
  type: 'boolean',
716
+ getter: payload => payload.action === getterKey ? true : undefined,
717
+ isEvent: true,
718
+ };
719
+ } else {
720
+ state = {
721
+ id: actionName.replace(/\*/g, ''),
722
+ prop: 'action',
723
+ name: actionName,
724
+ icon: undefined,
725
+ role: 'button',
726
+ write: false,
727
+ read: true,
728
+ type: 'boolean',
711
729
  getter: payload => payload.action === actionName ? true : undefined,
712
730
  isEvent: true,
713
731
  };
714
- }
732
+ };
715
733
  pushToStates(state, expose.access);
716
734
  }
717
735
  state = null;
package/lib/groups.js CHANGED
@@ -85,7 +85,7 @@ class Groups {
85
85
  }
86
86
 
87
87
  } catch (error) {
88
- if (error) this.error(`getGroupMembersFromController: error is ${JSON.stringify(error)} ${JSON.stringify(new Error().stack)}`);
88
+ if (error) this.error(`getGroupMembersFromController: error is ${(error && error.message ? error.message : 'no error message')} ${(error && error.stack ? error.stack : 'no call stack')}`);
89
89
  else this.error('unidentifed error in getGroupMembersFromController');
90
90
  }
91
91
  return members;
@@ -138,7 +138,7 @@ class Groups {
138
138
  for (const gpid of groups[epid]) {
139
139
  const gpidn = parseInt(gpid);
140
140
  if (gpidn < 0) {
141
- this.warn(`calling removeDevFromGroup with ${sysid}, ${-gpidn}, ${epid}` );
141
+ this.debug(`calling removeDevFromGroup with ${sysid}, ${-gpidn}, ${epid}` );
142
142
  const response = await this.zbController.removeDevFromGroup(sysid, (-gpidn), epid);
143
143
  if (response && response.error) {
144
144
  errors.push(response.error);
@@ -146,7 +146,7 @@ class Groups {
146
146
  }
147
147
 
148
148
  } else if (gpidn > 0) {
149
- this.warn(`calling addDevToGroup with ${sysid}, ${gpidn}, ${epid}` );
149
+ this.debug(`calling addDevToGroup with ${sysid}, ${gpidn}, ${epid}` );
150
150
  const response = await this.zbController.addDevToGroup(sysid, (gpidn), epid);
151
151
  if (response && response.error) {
152
152
  errors.push(response.error);
@@ -158,7 +158,7 @@ class Groups {
158
158
  }
159
159
  }
160
160
  } catch (e) {
161
- this.warn('caught error ' + JSON.stringify(e) + ' in updateGroupMembership');
161
+ this.warn('caught error ' + (e && e.message ? e.message : 'no error message') + ' in updateGroupMembership');
162
162
  this.adapter.sendTo(from, command, {error: e}, callback);
163
163
  return;
164
164
  }
@@ -54,9 +54,9 @@ class StatesController extends EventEmitter {
54
54
  retainDeviceNames() {
55
55
  clearTimeout(this.retTimeoutHandle);
56
56
  this.retTimeoutHanlde = setTimeout(() => {
57
- fs.writeFile(this.dev_names_fn, JSON.stringify(savedDeviceNamesDB, null, 2), err => {
58
- if (err) {
59
- this.error(`error saving device names: ${JSON.stringify(err)}`);
57
+ fs.writeFile(this.dev_names_fn, JSON.stringify(savedDeviceNamesDB, null, 2), error => {
58
+ if (error) {
59
+ this.error(`error saving device names: ${(error && error.message ? error.message : 'no error message')} ${(error && error.stack ? error.stack : 'no call stack')}`);
60
60
  } else {
61
61
  this.debug('saved device names');
62
62
  }
@@ -109,7 +109,7 @@ class StatesController extends EventEmitter {
109
109
  }
110
110
 
111
111
  if (this.checkDebugDevice(id))
112
- this.warn(`ELEVATED O1: User state change of state ${id} with value ${state.val} (ack: ${state.ack}) from ${state.from}`);
112
+ this.warn(`ELEVATED O01: User state change of state ${id} with value ${state.val} (ack: ${state.ack}) from ${state.from}`);
113
113
 
114
114
  this.debug(`User stateChange ${id} ${JSON.stringify(state)}`);
115
115
  const devId = getAdId(this.adapter, id); // iobroker device id
@@ -211,7 +211,7 @@ class StatesController extends EventEmitter {
211
211
  this.debug(`Change state '${stateKey}' at device ${deviceId} type '${model}'`);
212
212
  const elevated = this.checkDebugDevice(deviceId);
213
213
 
214
- if (elevated) this.warn(`ELEVATED O2: Change state '${stateKey}' at device ${deviceId} type '${model}'`);
214
+ if (elevated) this.warn(`ELEVATED O02: Change state '${stateKey}' at device ${deviceId} type '${model}'`);
215
215
 
216
216
  const devStates = await this.getDevStates(deviceId, model);
217
217
  if (!devStates) {
@@ -181,7 +181,7 @@ class DeviceAvailability extends BaseExtension {
181
181
  }
182
182
  } catch (error) {
183
183
  this.sendError(error);
184
- this.debug(`Exception in readState of '${device.ieeeAddr}' - error : '${error}'`);
184
+ this.debug(`Exception in readState of '${device.ieeeAddr}' - error : '${(error && error.message ? error.message : 'no error message')}'`);
185
185
  // intentionally empty: Just present to ensure we cause no harm
186
186
  // when reading the state fails. => fall back on standard Ping function
187
187
  }
@@ -36,11 +36,22 @@ class DeviceEvent extends BaseExtension {
36
36
 
37
37
  async callOnEvent(device, type, data, mappedDevice) {
38
38
  if (!mappedDevice) {
39
- mappedDevice = await zigbeeHerdsmanConverters.findByDevice(device);
39
+ try {
40
+ mappedDevice = await zigbeeHerdsmanConverters.findByDevice(device);
41
+ }
42
+ catch (error) {
43
+ this.log.error(`onEvent: unable to find mapped device for ${JSON.stringify(device)} `);
44
+ return;
45
+ }
40
46
  }
41
47
 
42
48
  if (mappedDevice && mappedDevice.onEvent) {
43
- mappedDevice.onEvent(type, data, device,mappedDevice.options,'{}');
49
+ try {
50
+ mappedDevice.onEvent(type, data, device,mappedDevice.options,'{}');
51
+ }
52
+ catch (error) {
53
+ this.log.error(`onEvent for ${JSON.stringify(device)} failed with error ${error.message}`);
54
+ }
44
55
  }
45
56
  }
46
57
  }
@@ -148,9 +148,9 @@ class ZigbeeController extends EventEmitter {
148
148
  }
149
149
 
150
150
  this.debug(`Zigbee network parameters: panID=${debNetworkParam.panID} channel=${debNetworkParam.channel} extendedPanID=${extPanIDDebug}`);
151
- } catch (e) {
152
- this.sendError(e);
153
- this.error(`Starting zigbee-herdsman problem : ${JSON.stringify(e.message)}`);
151
+ } catch (error) {
152
+ this.sendError(error);
153
+ this.error(`Starting zigbee-herdsman problem : ${(error && error.message ? error.message : 'no error message')}`);
154
154
  throw 'Error herdsman start';
155
155
  }
156
156
  // Check if we have to turn off the LED
@@ -297,7 +297,7 @@ class ZigbeeController extends EventEmitter {
297
297
  }
298
298
  } catch (error) {
299
299
  this.sendError(error);
300
- this.error(JSON.stringify(error));
300
+ this.error(`error in getGroups: ${(error && error.message ? error.message : 'no error message')} ${(error && error.stack ? error.stack : 'no call stack')}`);
301
301
  return undefined;
302
302
  }
303
303
  }
@@ -308,7 +308,7 @@ class ZigbeeController extends EventEmitter {
308
308
  group && group.removeFromNetwork();
309
309
  } catch (error) {
310
310
  this.sendError(error);
311
- this.error(`error in removeGroupById: ${error}`);
311
+ this.error(`error in removeGroupById: ${(error && error.message ? error.message : 'no error message')} ${(error && error.stack ? error.stack : 'no call stack')}`);
312
312
  }
313
313
  }
314
314
 
@@ -322,24 +322,34 @@ class ZigbeeController extends EventEmitter {
322
322
  }
323
323
 
324
324
  async verifyGroupExists(id) {
325
- const nid = typeof id === 'number' ? id : parseInt(id);
326
- let group = await this.herdsman.getGroupByID(nid);
327
- if (!group) {
328
- group = await this.herdsman.createGroup(nid);
329
- group.toZigbee = groupConverters;
330
- group.model = 'group';
331
- this.debug(`verifyGroupExists: created group ${nid}`);
332
- } else {
333
- this.debug(`verifyGroupExists: group ${nid} exists`);
325
+ try {
326
+ const nid = typeof id === 'number' ? id : parseInt(id);
327
+ let group = await this.herdsman.getGroupByID(nid);
328
+ if (!group) {
329
+ group = await this.herdsman.createGroup(nid);
330
+ group.toZigbee = groupConverters;
331
+ group.model = 'group';
332
+ this.debug(`verifyGroupExists: created group ${nid}`);
333
+ } else {
334
+ this.debug(`verifyGroupExists: group ${nid} exists`);
335
+ }
336
+ }
337
+ catch (error) {
338
+ this.error(`verifyGroupExists: ${error && error.message ? error.message : 'unspecified error'}`);
334
339
  }
335
340
  }
336
341
 
337
342
  async addPairingCode(code) {
338
343
  this.debug(`calling addPairingCode with ${code}`);
339
344
  if (code) {
340
- await this.herdsman.addInstallCode(code);
341
- this.info(`added code ${code} for pairing`);
342
- return true;
345
+ try {
346
+ await this.herdsman.addInstallCode(code);
347
+ this.info(`added code ${code} for pairing`);
348
+ return true;
349
+ }
350
+ catch (error) {
351
+ this.error(`addPairingCode: ${error && error.message ? error.message : 'unspecified error'}`);
352
+ }
343
353
  }
344
354
  return false;
345
355
  }
@@ -369,7 +379,7 @@ class ZigbeeController extends EventEmitter {
369
379
  } catch (error) {
370
380
  this.sendError(error);
371
381
  if (error) {
372
- this.error(`getGroupMembersFromController: error is ${JSON.stringify(error)} ${JSON.stringify(new Error().stack)}`);
382
+ this.error(`getGroupMembersFromController: ${(error && error.message ? error.message : 'no error message')} ${(error && error.stack ? error.stack : 'no call stack')}`);
373
383
  } else {
374
384
  this.error('unidentified error in getGroupMembersFromController');
375
385
  }
@@ -378,100 +388,125 @@ class ZigbeeController extends EventEmitter {
378
388
  }
379
389
 
380
390
  getDevice(key) {
381
- return this.herdsman.getDeviceByIeeeAddr(key);
391
+ try {
392
+ return this.herdsman.getDeviceByIeeeAddr(key);
393
+ }
394
+ catch {
395
+ return undefined;
396
+ }
382
397
  }
383
398
 
384
399
  getDevicesByType(type) {
385
- return this.herdsman.getDevicesByType(type);
400
+ try {
401
+ return this.herdsman.getDevicesByType(type);
402
+ }
403
+ catch {
404
+ return undefined;
405
+ }
386
406
  }
387
407
 
388
408
  getDeviceByNetworkAddress(networkAddress) {
389
- return this.herdsman.getDeviceByNetworkAddress(networkAddress);
409
+ try {
410
+ return this.herdsman.getDeviceByNetworkAddress(networkAddress);
411
+ }
412
+ catch {
413
+ return undefined;
414
+ }
390
415
  }
391
416
 
392
417
  async resolveEntity(key, ep) {
393
418
  // assert(typeof key === 'string' || key.constructor.name === 'Device', `Wrong type '${typeof key}'`);
394
-
395
- if (typeof key === 'string') {
396
- if (key === 'coordinator') {
397
- const coordinator = this.herdsman.getDevicesByType('Coordinator')[0];
398
- return {
399
- type: 'device',
400
- device: coordinator,
401
- endpoint: coordinator.getEndpoint(1),
402
- name: 'Coordinator',
403
- };
404
- } else {
405
- const device = await this.herdsman.getDeviceByIeeeAddr(key);
406
- if (device) {
407
- const mapped = await zigbeeHerdsmanConverters.findByDevice(device);
408
- const endpoints = mapped && mapped.endpoint ? mapped.endpoint(device) : null;
409
- let endpoint;
410
- if (endpoints && ep != undefined && endpoints[ep]) {
411
- endpoint = device.getEndpoint(endpoints[ep]);
412
- } else if (endpoints && endpoints['default']) {
413
- endpoint = device.getEndpoint(endpoints['default']);
414
- } else {
415
- const epNum = parseInt(ep);
416
- if (!isNaN(epNum)) {
417
- endpoint = device.getEndpoint(epNum);
418
- } else {
419
- endpoint = device.endpoints[0];
420
- }
421
- }
419
+ try {
420
+ if (typeof key === 'string') {
421
+ if (key === 'coordinator') {
422
+ const coordinator = this.herdsman.getDevicesByType('Coordinator')[0];
422
423
  return {
423
424
  type: 'device',
424
- device,
425
- mapped,
426
- endpoint,
427
- endpoints: device.endpoints,
428
- name: key,
425
+ device: coordinator,
426
+ endpoint: coordinator.getEndpoint(1),
427
+ name: 'Coordinator',
429
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);
460
+ group.toZigbee = groupConverters;
461
+ group.model = 'group';
462
+ return {
463
+ type: 'group',
464
+ mapped: group,
465
+ group,
466
+ name: `Group ${key}`,
467
+ };
468
+ } else {
469
+ let mapped;
470
+ try {
471
+ mapped = await zigbeeHerdsmanConverters.findByDevice(key);
472
+ } catch (err) {
473
+ this.error(`zigbeeHerdsmanConverters findByDevice ${key.ieeeAddr}`);
430
474
  }
431
- }
432
- } else if (typeof key === 'number') {
433
- let group = await this.herdsman.getGroupByID(key);
434
- if (!group) group = await this.herdsman.createGroup(key);
435
- group.toZigbee = groupConverters;
436
- group.model = 'group';
437
- return {
438
- type: 'group',
439
- mapped: group,
440
- group,
441
- name: `Group ${key}`,
442
- };
443
- } else {
444
- let mapped;
445
- try {
446
- mapped = await zigbeeHerdsmanConverters.findByDevice(key);
447
- } catch (err) {
448
- this.error(`zigbeeHerdsmanConverters findByDevice ${key.ieeeAddr}`);
449
- }
450
475
 
451
- return {
452
- type: 'device',
453
- device: key,
454
- mapped: mapped,
455
- name: key.type === 'Coordinator' ? 'Coordinator' : key.ieeeAddr,
456
- };
476
+ return {
477
+ type: 'device',
478
+ device: key,
479
+ mapped: mapped,
480
+ name: key.type === 'Coordinator' ? 'Coordinator' : key.ieeeAddr,
481
+ };
482
+ }
457
483
  }
484
+ catch {
485
+ return undefined;
486
+ }
487
+
458
488
  }
459
489
 
460
490
  async incMsgHandler(message) {
461
- this.debug('incoming msg', message);
462
- const device = await this.herdsman.getDeviceByIeeeAddr(message.srcaddr);
463
- if (!device) {
464
- this.debug('Message without device!');
465
- return;
491
+ try {
492
+ this.debug('incoming msg', message);
493
+ const device = await this.herdsman.getDeviceByIeeeAddr(message.srcaddr);
494
+ if (!device) {
495
+ this.debug('Message without device!');
496
+ return;
497
+ }
498
+ // We can't handle devices without modelId.
499
+ if (!device.modelId) {
500
+ this.debug('Message without modelId!');
501
+ return;
502
+ }
503
+ this.event('msg', device.ieeeAddr, message, {
504
+ modelId: device.modelId
505
+ });
466
506
  }
467
- // We can't handle devices without modelId.
468
- if (!device.modelId) {
469
- this.debug('Message without modelId!');
507
+ catch {
470
508
  return;
471
509
  }
472
- this.event('msg', device.ieeeAddr, message, {
473
- modelId: device.modelId
474
- });
475
510
  }
476
511
 
477
512
  // Stop controller
@@ -814,39 +849,45 @@ class ZigbeeController extends EventEmitter {
814
849
  if (cfg == null) {
815
850
  cfg = {};
816
851
  }
817
-
818
- if (type === 'foundation') {
819
- cfg.disableDefaultResponse = true;
820
-
821
- if (cmd === 'read' && !Array.isArray(zclData)) {
822
- /* // needs to be iterable (string[] | number [])
823
- zclData[Symbol.iterator] = function* () {
824
- let k;
825
- for (k in this) {
826
- yield k;
827
- }
828
- };
829
- */
830
- }
831
- let result;
832
- if (cmd === 'configReport') {
833
- result = await endpoint.configureReporting(cid, zclData, cfg);
852
+ try {
853
+ if (type === 'foundation') {
854
+ 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
+ let result;
867
+ if (cmd === 'configReport') {
868
+ result = await endpoint.configureReporting(cid, zclData, cfg);
869
+ } else {
870
+ if (cmd === 'read' && !Array.isArray(zclData))
871
+ result = await endpoint[cmd](cid, Object.keys(zclData), cfg);
872
+ else
873
+ result = await endpoint[cmd](cid, zclData, cfg);
874
+ }
875
+ callback && callback(undefined, result);
876
+ } else if (type === 'functionalResp') {
877
+ cfg.disableDefaultResponse = false;
878
+ const result = await endpoint.commandResponse(cid, cmd, zclData, cfg, zclSeqNum);
879
+ callback && callback(undefined, result);
834
880
  } else {
835
- if (cmd === 'read' && !Array.isArray(zclData))
836
- result = await endpoint[cmd](cid, Object.keys(zclData), cfg);
837
- else
838
- result = await endpoint[cmd](cid, zclData, cfg);
881
+ cfg.disableDefaultResponse = false;
882
+ const result = await endpoint.command(cid, cmd, zclData, cfg);
883
+ callback && callback(undefined, result);
839
884
  }
840
- callback && callback(undefined, result);
841
- } else if (type === 'functionalResp') {
842
- cfg.disableDefaultResponse = false;
843
- const result = await endpoint.commandResponse(cid, cmd, zclData, cfg, zclSeqNum);
844
- callback && callback(undefined, result);
845
- } else {
846
- cfg.disableDefaultResponse = false;
847
- const result = await endpoint.command(cid, cmd, zclData, cfg);
848
- callback && callback(undefined, result);
849
885
  }
886
+ catch (error)
887
+ {
888
+ this.log.error(`error sending ${type} ${cmd} to endpoint: ${(error && error.message ? error.message : 'no error message')} ${(error && error.stack ? error.stack : 'no call stack')}`)
889
+ }
890
+
850
891
  }
851
892
 
852
893
  async addDevToGroup(devId, groupId, epid) {
@@ -894,7 +935,7 @@ class ZigbeeController extends EventEmitter {
894
935
  } catch (error) {
895
936
  this.sendError(error);
896
937
  this.error(`Exception when trying to Add ${devId} to group ${groupId}`, error);
897
- return {error: `Failed to add ${devId} to group ${groupId}: ${JSON.stringify(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')}`};
898
939
  }
899
940
  return {};
900
941
  }
@@ -957,16 +998,21 @@ class ZigbeeController extends EventEmitter {
957
998
  target = !target ? this.getCoordinator() : target;
958
999
 
959
1000
  this.debug(`Binding ${log}`);
960
- ep.bind(cluster, target, error => {
961
- if (error) {
962
- this.sendError(error);
963
- this.error(`Failed to bind ${log} - (${error})`);
964
- } else {
965
- this.debug(`Successfully bound ${log}`);
966
- }
1001
+ try {
1002
+ ep.bind(cluster, target, error => {
1003
+ if (error) {
1004
+ this.sendError(error);
1005
+ this.error(`Failed to bind ${log} - (${error})`);
1006
+ } else {
1007
+ this.debug(`Successfully bound ${log}`);
1008
+ }
967
1009
 
1010
+ callback(error);
1011
+ });
1012
+ }
1013
+ catch (error) {
968
1014
  callback(error);
969
- });
1015
+ }
970
1016
  }
971
1017
 
972
1018
  unbind(ep, cluster, target, callback) {
@@ -974,15 +1020,21 @@ class ZigbeeController extends EventEmitter {
974
1020
  target = !target ? this.getCoordinator() : target;
975
1021
 
976
1022
  this.debug(`Unbinding ${log}`);
977
- ep.unbind(cluster, target, (error) => {
978
- if (error) {
979
- this.error(`Failed to unbind ${log} - (${error})`);
980
- } else {
981
- this.debug(`Successfully unbound ${log}`);
982
- }
1023
+ try {
1024
+ ep.unbind(cluster, target, (error) => {
1025
+ if (error) {
1026
+ this.error(`Failed to unbind ${log} - (${error})`);
1027
+ } else {
1028
+ this.debug(`Successfully unbound ${log}`);
1029
+ }
983
1030
 
1031
+ callback(error);
1032
+ });
1033
+ }
1034
+ catch (error)
1035
+ {
984
1036
  callback(error);
985
- });
1037
+ }
986
1038
  }
987
1039
 
988
1040
  reset(mode, callback) {
package/main.js CHANGED
@@ -629,7 +629,14 @@ class Zigbee extends utils.Adapter {
629
629
  let isGroup = false;
630
630
  const has_elevated_debug = this.stController.checkDebugDevice(deviceId)
631
631
 
632
- this.log.debug(`publishFromState : ${deviceId} ${model} ${safeJsonStringify(stateList)}`);
632
+ if (has_elevated_debug)
633
+ {
634
+ const stateNames = [];
635
+ stateList.forEach( state => stateNames.push(state.id));
636
+ this.log.warn(`ELEVATED O03: Publishing to ${deviceId} of model ${model} ${stateNames.join(', ')}`);
637
+ }
638
+ else
639
+ this.log.debug(`publishFromState : ${deviceId} ${model} ${safeJsonStringify(stateList)}`);
633
640
  if (model === 'group') {
634
641
  isGroup = true;
635
642
  deviceId = parseInt(deviceId);
@@ -637,13 +644,11 @@ class Zigbee extends utils.Adapter {
637
644
  try {
638
645
  const entity = await this.zbController.resolveEntity(deviceId);
639
646
 
640
- this.log.debug(`entity: ${deviceId} ${model} ${safeJsonStringify(entity)}`);
641
-
642
- const mappedModel = entity.mapped;
647
+ const mappedModel = (entity ? entity.mapped : undefined);
643
648
 
644
649
  if (!mappedModel) {
645
- this.log.debug(`No mapped model for ${model}`);
646
- if (has_elevated_debug) this.log.warn(`ELEVATED O2: No mapped model for ${model}`)
650
+ this.log.debug(`No mapped model for ${deviceId} (model ${model})`);
651
+ if (has_elevated_debug) this.log.error(`ELEVATED OE01: No mapped model ${deviceId} (model ${model})`)
647
652
  return;
648
653
  }
649
654
 
@@ -682,25 +687,34 @@ class Zigbee extends utils.Adapter {
682
687
  // on activation of the 'device_query' state trigger hardware query where possible
683
688
  if (stateDesc.id === 'device_query') {
684
689
  if (this.query_device_block.indexOf(deviceId) > -1) {
685
- this.log.warn(`Device query for '${entity.device.ieeeAddr}' blocked`);
690
+ this.log.info(`Device query for '${entity.device.ieeeAddr}' blocked`);
686
691
  return;
687
692
  }
688
693
  if (mappedModel) {
689
694
  this.query_device_block.push(deviceId);
690
- this.log.debug(`Device query for '${entity.device.ieeeAddr}' started`);
695
+ if (has_elevated_debug)
696
+ this.log.warn(`ELEVATED O06: Device query for '${entity.device.ieeeAddr}/${entity.device.endpoints[0].ID}' triggered`);
697
+ let t;
691
698
  for (const converter of mappedModel.toZigbee) {
692
699
  if (converter.hasOwnProperty('convertGet')) {
693
700
  for (const ckey of converter.key) {
694
701
  try {
695
- await converter.convertGet(entity.device.endpoints[0], ckey, {});
702
+ await converter.convertGet(entity.device.endpoints[0], ckey, {endpoint_name:entity.device.endpoints[0].ID.toString()});
696
703
  } catch (error) {
697
- this.log.warn(`Failed to read state '${JSON.stringify(ckey)}'of '${entity.device.ieeeAddr}' after query with '${JSON.stringify(error)}'`);
698
-
704
+ if (has_elevated_debug) {
705
+ this.log.warn(`ELEVATED OE02.1 Failed to read state '${JSON.stringify(ckey)}'of '${entity.device.ieeeAddr}/${entity.device.endpoints[0].ID}' from query with '${error && error.message ? error.message : 'no error message'}`);
706
+ }
707
+ else
708
+ this.log.info(`failed to read state ${JSON.stringify(ckey)} of ${entity.device.ieeeAddr}/${entity.device.endpoints[0].ID} after device query`);
699
709
  }
700
710
  }
701
711
  }
702
712
  }
703
- this.log.debug(`Device query for '${entity.device.ieeeAddr}' done`);
713
+ if (has_elevated_debug)
714
+ this.log.warn(`ELEVATED O07: Device query for '${entity.device.ieeeAddr}/${entity.device.endpoints[0].ID}' complete`);
715
+ else
716
+ this.log.info(`Device query for '${entity.device.ieeeAddr}/${entity.device.endpoints[0].ID}' complete`);
717
+
704
718
  const idToRemove = deviceId;
705
719
  setTimeout(() => {
706
720
  const idx = this.query_device_block.indexOf(idToRemove);
@@ -715,6 +729,7 @@ class Zigbee extends utils.Adapter {
715
729
  }
716
730
 
717
731
  let converter = undefined;
732
+ let msgCnt = 1;
718
733
  for (const c of mappedModel.toZigbee) {
719
734
 
720
735
  if (!c.hasOwnProperty('convertSet')) continue;
@@ -724,13 +739,14 @@ class Zigbee extends utils.Adapter {
724
739
  if (c.hasOwnProperty('convertSet') && converter === undefined)
725
740
  {
726
741
  converter = c;
727
- if (has_elevated_debug)
728
- this.log.warn(`ELEVATED O3A: Setting converter to keyless converter for ${deviceId} of type ${model}`)
742
+ if (has_elevated_debug) {
743
+ this.log.warn(`ELEVATED O4.${msgCnt++}: Setting converter to keyless converter for ${deviceId} of type ${model}`)
744
+ }
729
745
  this.log.debug('setting converter to keyless converter')
730
746
  }
731
747
  else
732
748
  {
733
- if (has_elevated_debug) this.log.warn(`ELEVATED O3B: ignoring keyless converter for ${deviceId} of type ${model}`)
749
+ if (has_elevated_debug) this.log.warn(`ELEVATED O4.${msgCnt++}: ignoring keyless converter for ${deviceId} of type ${model}`)
734
750
  this.log.debug('ignoring keyless converter')
735
751
  }
736
752
  continue;
@@ -738,7 +754,7 @@ class Zigbee extends utils.Adapter {
738
754
  if (c.key.includes(stateDesc.prop) || c.key.includes(stateDesc.setattr) || c.key.includes(stateDesc.id))
739
755
  {
740
756
  this.log.debug(`${(converter===undefined?'Setting':'Overriding')}' converter to converter with key(s)'${JSON.stringify(c.key)}}`)
741
- if (has_elevated_debug) this.log.warn(`ELEVATED O3C: ${(converter===undefined?'Setting':'Overriding')}' converter to converter with key(s)'${JSON.stringify(c.key)}}`)
757
+ if (has_elevated_debug) this.log.warn(`ELEVATED O4.${msgCnt++}: ${(converter===undefined?'Setting':'Overriding')}' converter to converter with key(s)'${JSON.stringify(c.key)}}`)
742
758
  converter = c;
743
759
  }
744
760
  }
@@ -804,7 +820,7 @@ class Zigbee extends utils.Adapter {
804
820
  try {
805
821
  const result = await converter.convertSet(target, key, preparedValue, meta);
806
822
  this.log.debug(`convert result ${safeJsonStringify(result)}`);
807
- if (has_elevated_debug) this.log.warn(`ELEVATED O5: convert result ${safeJsonStringify(result)} for device ${deviceId}`);
823
+ if (has_elevated_debug) this.log.warn(`ELEVATED O05: convert result ${safeJsonStringify(result)} sent to device ${deviceId}`);
808
824
  if (result !== undefined) {
809
825
  if (stateModel && !isGroup) {
810
826
  this.acknowledgeState(deviceId, model, stateDesc, value);
@@ -824,7 +840,7 @@ class Zigbee extends utils.Adapter {
824
840
  }
825
841
  });
826
842
  } catch (err) {
827
- this.log.error(`No entity for ${deviceId}`);
843
+ this.log.error(`No entity for ${deviceId} : ${err && err.message ? err.message : ''}`);
828
844
  }
829
845
  }
830
846
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.zigbee",
3
- "version": "1.10.14",
3
+ "version": "2.0.0",
4
4
  "author": {
5
5
  "name": "Kirov Ilya",
6
6
  "email": "kirovilya@gmail.com"
@@ -22,12 +22,12 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@iobroker/adapter-core": "^3.2.2",
25
- "@iobroker/dm-utils": "^0.5.0",
25
+ "@iobroker/dm-utils": "^1.0.9",
26
26
  "humanize-duration": "^3.32.1",
27
27
  "tar": "^7.4.3",
28
28
  "ajv": "^8.17.1",
29
29
  "uri-js": "^4.4.1",
30
- "typescript": "^5.6.3",
30
+ "typescript": "^5.7.3",
31
31
  "zigbee-herdsman": "2.1.9",
32
32
  "zigbee-herdsman-converters": "20.58.0"
33
33
  },
@@ -40,14 +40,14 @@
40
40
  "@iobroker/testing": "^5.0.0",
41
41
  "chai": "^5.1.2",
42
42
  "chai-as-promised": "^7.1.1",
43
- "eslint": "^9.17.0",
43
+ "eslint": "^9.19.0",
44
44
  "eslint-config-prettier": "^9.1.0",
45
45
  "eslint-plugin-prettier": "^5.2.1",
46
46
  "gulp": "^4.0.2",
47
47
  "gulp-jsdoc3": "^3.0.0",
48
48
  "gulp-replace": "^1.1.4",
49
49
  "mixin-deep": "^2.0.1",
50
- "mocha": "^10.8.2"
50
+ "mocha": "^11.1.0"
51
51
  },
52
52
  "homepage": "https://github.com/ioBroker/ioBroker.zigbee",
53
53
  "keywords": [