iobroker.zigbee 1.8.9 → 1.8.10

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2018-2022 Kirov Ilya <kirovilya@gmail.com>
3
+ Copyright (c) 2018-2023 Kirov Ilya <kirovilya@gmail.com>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -133,6 +133,21 @@ You can thank the authors by these links:
133
133
 
134
134
 
135
135
  ## Changelog
136
+ ### 1.8.10 (2023-01-04) (2022-12-12)
137
+ * (asgothian) fix group access
138
+ * (asgothian) add option for pairing code:
139
+ A new icon allows to open the networ after first entering a pairing code
140
+ listed on the device
141
+ * (asgothian) easier use of external converters
142
+ - external converters can now be placed in the zigbee adapter data folder
143
+ - no absolite path is required to access them
144
+ - external converters posted on the github for zigbee-herdsman-converters
145
+ should work as they are - folders for libraries are rewritten to match
146
+ the expected location when 'required' from within the zigbee adapter
147
+ - Log entries will identify which files are entered as converters. Errors
148
+ in these files should not cause the adapter to crash - instead, use of
149
+ external converters may be unavailable.
150
+
136
151
  ### 1.8.9 (2022-12-10)
137
152
  * (arteck) fix lidl plug
138
153
 
@@ -159,7 +174,7 @@ You can thank the authors by these links:
159
174
  ### 1.7.5 (2022-06-01)
160
175
  * (arteck) error message for undefined devices or icons
161
176
 
162
- ### 1.7.4 (2022-05-30)
177
+ ### 1.7.4 (2022-05-30)
163
178
  * (arteck) missing icons with multiple description
164
179
 
165
180
  ### 1.7.2 (2022-05-28)
@@ -168,7 +183,7 @@ You can thank the authors by these links:
168
183
  ### 1.7.1 (2022-05-28)
169
184
  * (arteck) available status in admin is colored
170
185
  * (arteck) disable Backups checkbox in settings
171
- * (arteck) we keep last 10 backup files
186
+ * (arteck) we keep last 10 backup files
172
187
  * (arteck) download missing icons automatically (manual upload needed)
173
188
 
174
189
  ### 1.6.18 (2022-04-21)
@@ -448,7 +463,7 @@ new Zigbee-herdsman features:
448
463
  ## License
449
464
  The MIT License (MIT)
450
465
 
451
- Copyright (c) 2018-2022 Kirov Ilya <kirovilya@gmail.com>
466
+ Copyright (c) 2018-2023 Kirov Ilya <kirovilya@gmail.com>
452
467
 
453
468
  Permission is hereby granted, free of charge, to any person obtaining a copy
454
469
  of this software and associated documentation files (the "Software"), to deal
package/admin/admin.js CHANGED
@@ -683,6 +683,18 @@ function checkFwUpdate() {
683
683
  }
684
684
  }
685
685
 
686
+ function letsPairingWithCode(code) {
687
+ messages = [];
688
+ sendTo(namespace, 'letsPairing', {code: code}, function (msg) {
689
+ if (msg && msg.error) {
690
+ showMessage(msg.error, _('Error'));
691
+ }
692
+ else {
693
+ showPairingProcess();
694
+ }
695
+ });
696
+ }
697
+
686
698
  function letsPairing() {
687
699
  messages = [];
688
700
  sendTo(namespace, 'letsPairing', {}, function (msg) {
@@ -777,7 +789,7 @@ function load(settings, onChange) {
777
789
  if (settings.extPanID === 'DDDDDDDDDDDDDDD') {
778
790
  settings.extPanID = 'DDDDDDDDDDDDDDDD';
779
791
  }
780
-
792
+
781
793
  if (settings.precfgkey === undefined) {
782
794
  settings.precfgkey = '01030507090B0D0F00020406080A0C0D';
783
795
  }
@@ -787,7 +799,7 @@ function load(settings, onChange) {
787
799
  if (settings.disablePing === undefined) {
788
800
  settings.disablePing = false;
789
801
  }
790
-
802
+
791
803
  // example: select elements with id=key and class=value and insert value
792
804
  for (const key in settings) {
793
805
  if (savedSettings.indexOf(key) === -1) {
@@ -857,16 +869,25 @@ function load(settings, onChange) {
857
869
  });
858
870
 
859
871
  $('#add_group').click(function () {
860
- // showGroupList(true);
861
872
  const maxind = parseInt(Object.getOwnPropertyNames(groups).reduce((a, b) => a > b ? a : b, 0));
862
873
  editGroupName(maxind + 1, 'Group ' + maxind + 1, true);
863
874
  });
875
+
864
876
  $('#add_grp_btn').click(function () {
865
- // showGroupList(true);
866
877
  const maxind = parseInt(Object.getOwnPropertyNames(groups).reduce((a, b) => a > b ? a : b, 0));
867
878
  editGroupName(maxind + 1, 'Group ' + maxind + 1, true);
868
879
  });
869
880
 
881
+ $('#code_pairing').click(function () {
882
+ if (!$('#pairing').hasClass('pulse')) {
883
+ $('#codeentry a.btn[name=\'pair\']').click(() => {
884
+ const code = $('#codeentry').find('input[id=\'qr_code\']').val();
885
+ letsPairingWithCode(code)
886
+ });
887
+ $('#codeentry').modal('open');
888
+ }
889
+ });
890
+
870
891
  $(document).ready(function () {
871
892
  $('.modal').modal({
872
893
  startingTop: '30%',
@@ -1920,9 +1941,8 @@ function updateDev(id, newName, newGroups) {
1920
1941
  showMessage(msg.error, _('Error'));
1921
1942
  } else {
1922
1943
  // save dev-groups on success
1923
- dev.groups = newGroups;
1944
+ getDevices();
1924
1945
  }
1925
- showDevices();
1926
1946
  });
1927
1947
  showWaitingDialog('Updating group memberships', 10);
1928
1948
 
@@ -478,6 +478,12 @@
478
478
  title="Touchlink reset and pairing">
479
479
  <i class="material-icons large">wifi_tethering</i></a>
480
480
  </li>
481
+ <li>
482
+ <a id="code_pairing"
483
+ class="btn-floating waves-effect waves-light green tooltipped center-align hoverable translateT"
484
+ title="Pairing with QR Code">
485
+ <i class="material-icons large">select_all</i></a>
486
+ </li>
481
487
  <li>
482
488
  <a id="pairing"
483
489
  class="btn-floating waves-effect waves-light green tooltipped center-align hoverable translateT"
@@ -1079,6 +1085,25 @@
1079
1085
  </div>
1080
1086
  </div>
1081
1087
 
1088
+ <div id="codeentry" class="modal">
1089
+ <div class="modal-content">
1090
+ <div class="addgroup">
1091
+ <h3 class="translate">Enter Pairing Code</h3>
1092
+ </div>
1093
+ <div class="row">
1094
+ <div class="input-field">
1095
+ <input id="qr_code" type="text" class="value validate">
1096
+ <label for="qr_code" class="translate">QR Code</label>
1097
+ </div>
1098
+ </div>
1099
+ </div>
1100
+ <div class="modal-footer">
1101
+ <a name="pair" href="#!"
1102
+ class="modal-action modal-close waves-effect waves-green btn green translate">Start Pairing</a>
1103
+ <a href="#!" class="modal-action modal-close waves-effect waves-red btn-flat translate">Cancel</a>
1104
+ </div>
1105
+ </div>
1106
+
1082
1107
  <div id="modaldelete" class="modal">
1083
1108
  <div class="modal-content">
1084
1109
  <h3 class="translate">Delete confirmation</h3>
package/admin/tab_m.html CHANGED
@@ -16,7 +16,7 @@
16
16
  <script type="text/javascript" src="../../lib/js/materialize.js"></script>
17
17
  <script type="text/javascript" >var noConfigDialog = true;</script><!-- Deactivate buttons -->
18
18
  <script type="text/javascript" src="adapter-settings.js"></script>
19
-
19
+
20
20
  <script>
21
21
  // overload showMessage
22
22
  function showMessage(message, title, icon) {
@@ -437,6 +437,12 @@
437
437
  <a id="touchlink_btn" class="btn-floating waves-effect waves-light green tooltipped center-align hoverable translateT" title="Touchlink reset and pairing">
438
438
  <i class="material-icons large">wifi_tethering</i></a>
439
439
  </li>
440
+ <li>
441
+ <a id="code_pairing"
442
+ class="btn-floating waves-effect waves-light green tooltipped center-align hoverable translateT"
443
+ title="Pairing with QR Code">
444
+ <i class="material-icons large">select_all</i></a>
445
+ </li>
440
446
  <li>
441
447
  <a id="pairing" class="btn-floating waves-effect waves-light green tooltipped center-align hoverable translateT" title="Let's pairing!">
442
448
  <i class="material-icons large">leak_add</i></a>
@@ -729,7 +735,24 @@
729
735
  <a href="#!" class="modal-action modal-close waves-effect waves-red btn-flat translate">Cancel</a>
730
736
  </div>
731
737
  </div>
732
-
738
+ <div id="codeentry" class="modal">
739
+ <div class="modal-content">
740
+ <div class="addgroup">
741
+ <h3 class="translate">Enter Pairing Code</h3>
742
+ </div>
743
+ <div class="row">
744
+ <div class="input-field">
745
+ <input id="qr_code" type="text" class="value validate">
746
+ <label for="qr_code" class="translate">QR Code</label>
747
+ </div>
748
+ </div>
749
+ </div>
750
+ <div class="modal-footer">
751
+ <a name="pair" href="#!"
752
+ class="modal-action modal-close waves-effect waves-green btn green translate">Start Pairing</a>
753
+ <a href="#!" class="modal-action modal-close waves-effect waves-red btn-flat translate">Cancel</a>
754
+ </div>
755
+ </div>
733
756
  <div id="groupedit" class="modal">
734
757
  <div class="modal-content">
735
758
  <div class = "addgroup">
package/admin/words.js CHANGED
@@ -97,7 +97,7 @@ systemDictionary = {
97
97
  "№": { "uk": "№", "en": "№", "de": "Nr.", "ru": "№", "pt": "№", "nl": "№", "fr": "№", "it": "№", "es": "№", "pl": "Nr", "zh-cn": "№"},
98
98
  "Excludes": { "uk": "виключити", "en": "to exclude", "de": "ausschließen", "ru": "Исключить", "nl": "uitsluiten", "fr": "d'exclure", "pl": "wyklucz"},
99
99
  "ExcludeTextTranslation": { "uk": "Тут ви можете додати деякі пристрої, які слід виключити з нашого опису. Вони використовують лише \"викриття (exposes)\" від zigbee-herdsman-converter. Після додавання перезапустіть адаптер.", "en": "Here you can add some devices that should be excluded from our description. They only use \"exposes\" from zigbee-herdsman-converter. After adding, please restart the adapter.", "de": "Sie können hier einige Geräte hinzufügen, die von unserer Verarbeitung ausgeschlossen werden sollten. Sie verwenden dann nur exposes vom ZigBee-Herdsman-Converter. Nach dem Hinzufügen starten Sie bitte den Adapter neu.", "ru": "Здесь вы можете добавить устройства, для которых надо исключить представление заданное адаптером (iobroker.zigbee). В этом случае они будут использовать \"exposes\" из zigbee-herdsman-converter. После добавления перезапустите адаптер.", "nl": "Hier kunt u enkele apparaten toevoegen die moeten worden uitgesloten van onze beschrijving. Ze gebruiken alleen \"exposes\" van zigbee-herdsman-converter. Na het toevoegen moet u de adapter opnieuw opstarten.", "fr": "Ici, vous pouvez ajouter certains dispositifs qui doivent être exclus de notre description. Ils utilisent uniquement les \"exposes\" de zigbee-herdsman-converter. Après l'ajout, veuillez redémarrer l'adaptateur.", "pl": "Możesz dodać tutaj kilka urządzeń, które powinny zostać wyłączone z naszego opisu. Używasz tylko ekspozycji z zigbee-herdsman-converter. Po dodaniu zrestartuj adapter"},
100
- "SettingsExclude": { "uk": "Примусовий запуск адаптера з не цілісною конфігурацією (не рекомендується). Оновіть адаптер до сумісної версії мікропрограми та перестворіть свою мережу якомога швидше.", "en": "Force start adapter with inconsistent configuration (not recommended). Please update the adapter to compatible firmware and recreate your network as soon as possible.", "de": "Den Start des Adapters mit inkonsistenter Konfiguration erzwingen (nicht empfohlen). Bitte aktualisieren Sie den Adapter auf kompatible Firmware und erstellen Sie Ihr Netzwerk so schnell wie möglich neu.", "ru": "Принудительный запуск адаптера с несовместимой конфигурацией (не рекомендуется). Пожалуйста, обновите адаптер до совместимой прошивки и воссоздайте сеть как можно скорее.", "nl": "Forceer de adapter met een inconsistente configuratie (niet aanbevolen). Update de adapter naar compatibele firmware en maak uw netwerk zo snel mogelijk opnieuw aan.", "fr": "Démarrage forcé de l'adaptateur avec une configuration incohérente (non recommandé). Veuillez mettre à jour l'adaptateur avec un micrologiciel compatible et recréer votre réseau dès que possible.", "pl": "Wystaruj Adapter bez zgodnej konfiguracji (uwaga). Zaktualizuj adapter i jak najszybciej odtwórz sieć."},
100
+ "SettingsExclude": { "uk": "Примусовий запуск адаптера з не цілісною конфігурацією (не рекомендується). Оновіть адаптер до сумісної версії мікропрограми та перестворіть свою мережу якомога швидше.", "en": "Force start adapter with inconsistent configuration (not recommended). Please update the adapter to compatible firmware and recreate your network as soon as possible.", "de": "Den Start des Adapters mit inkonsistenter Konfiguration erzwingen(nicht empfohlen). Bitte aktualisieren Sie den Adapter auf kompatible Firmware und erstellen Sie Ihr Netzwerk so schnell wie möglich neu.", "ru": "Принудительный запуск адаптера с несовместимой конфигурацией (не рекомендуется). Пожалуйста, обновите адаптер до совместимой прошивки и воссоздайте сеть как можно скорее.", "nl": "Forceer de adapter met een inconsistente configuratie (niet aanbevolen). Update de adapter naar compatibele firmware en maak uw netwerk zo snel mogelijk opnieuw aan.", "fr": "Démarrage forcé de l'adaptateur avec une configuration incohérente (non recommandé). Veuillez mettre à jour l'adaptateur avec un micrologiciel compatible et recréer votre réseau dès que possible.", "pl": "Wystaruj Adapter bez zgodnej konfiguracji (uwaga). Zaktualizuj adapter i jak najszybciej odtwórz sieć."},
101
101
  "Others": { "uk": "інші налаштування", "en": "other settings", "de": "andere Einstellungen", "ru": "другие настройки", "nl": "andere instellingen", "fr": "autres réglages", "pl": "inne ustawienia"},
102
102
  "disable internal Backup": { "uk": "вимкнути внутрішнє резервне копіювання", "en": "disable internal Backup", "de": "Deaktiviere die interne Sicherung", "ru": "отключить внутреннее резервное копирование", "nl": "interne back-up uitschakelen", "fr": "désactiver la sauvegarde interne", "pl": "wyłącz wewnętrzną kopię"},
103
103
  "Disable active availability check": { "uk": "Вимкнути активну перевірку доступності", "en": "Disable active availability check", "de": "Aktive Verfügbarkeitsprüfung deaktivieren", "ru": "Отключить активную проверку доступности", "nl": "Actieve beschikbaarheidscontrole uitschakelen", "fr": "Désactiver le contrôle de disponibilité actif", "pl": "Wyłącz aktywne sprawdzanie dostępności"},
@@ -0,0 +1,19 @@
1
+ ![Logo](../../admin/zigbee.png)
2
+ # ioBroker.zigbee
3
+
4
+ ![Number of Installations](http://iobroker.live/badges/zigbee-installed.svg)
5
+ ![Number of Installations](http://iobroker.live/badges/zigbee-stable.svg)
6
+ [![NPM version](http://img.shields.io/npm/v/iobroker.zigbee.svg)](https://www.npmjs.com/package/iobroker.zigbee)
7
+
8
+ [![Translation status](https://weblate.iobroker.net/widgets/adapters/-/zigbee/svg-badge.svg)](https://weblate.iobroker.net/engage/adapters/?utm_source=widget)
9
+ [![Downloads](https://img.shields.io/npm/dm/iobroker.zigbee.svg)](https://www.npmjs.com/package/iobroker.zigbee)
10
+
11
+ Der Zigbee Adapter dient zur Ansteuerung eines Zigbee Netzes unter zu Hilfe nahme eines direkt am ioBroker angeschlossenen Koordinators. Der Koordinator kann dabei über USB, Seriell oder über TCP/IP angeschlossen werden.
12
+
13
+ Vor dem ersten Start des Adpaters sollten auf der Konfigurationsseite einige Einstellungen vorgenommen werden:
14
+ - Com Anschlussnahme
15
+ - Koordinator Typ
16
+ - Netzwerk verschlüsselung über PanID und extPanID
17
+ - Zigbee Kanal
18
+
19
+ Nachdem diese Parameter eingestellt wurden kann der Adapter gestartet werden.
package/io-package.json CHANGED
@@ -1,8 +1,12 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "zigbee",
4
- "version": "1.8.9",
4
+ "version": "1.8.10",
5
5
  "news": {
6
+ "1.8.10": {
7
+ "en": "fix group access and any new functions",
8
+ "de": "fix group access and any new functions"
9
+ },
6
10
  "1.8.9": {
7
11
  "en": "fix lidl plug",
8
12
  "de": "deckelstecker",
@@ -52,19 +56,6 @@
52
56
  },
53
57
  "1.8.4": {
54
58
  "en": "fix for new code"
55
- },
56
- "1.8.3": {
57
- "en": "back to old source",
58
- "de": "zurück zur alten quelle",
59
- "ru": "назад к старому источнику",
60
- "pt": "voltar para a velha fonte",
61
- "nl": "terug naar de oude bron",
62
- "fr": "retour à la vieille source",
63
- "it": "torna alla vecchia fonte",
64
- "es": "volver a la vieja fuente",
65
- "pl": "tył",
66
- "uk": "назад до старого джерела",
67
- "zh-cn": "背 景"
68
59
  }
69
60
  },
70
61
  "title": "Zigbee",
package/lib/binding.js CHANGED
@@ -104,7 +104,6 @@ class Binding {
104
104
  async doBindUnbind(type, bind_source, bind_source_ep, bind_target, bind_target_ep, callback) {
105
105
  try {
106
106
  const id = this.getBindingId(bind_source, bind_source_ep, bind_target, bind_target_ep);
107
-
108
107
  const source = await this.zbController.resolveEntity(`0x${this.extractDeviceId(bind_source)}`, this.getBindEp(bind_source_ep));
109
108
  this.debug(`source: ${safeJsonStringify(source)}`);
110
109
  let target = await this.zbController.resolveEntity(`0x${this.extractDeviceId(bind_target)}`, this.getBindEp(bind_target_ep));
package/lib/commands.js CHANGED
@@ -112,11 +112,35 @@ class Commands {
112
112
  }
113
113
  }
114
114
 
115
- letsPairing(from, command, message, callback) {
115
+ async letsPairing(from, command, message, callback) {
116
116
  if (this.zbController) {
117
117
  let devId = '';
118
- if (message && message.id) {
119
- devId = getZbId(message.id);
118
+ if (message) {
119
+ if (message.id) devId = getZbId(message.id);
120
+ if (message.code) {
121
+ try {
122
+ this.debug(`letsPairing called with code ${message.code}`);
123
+ const success = await this.zbController.addPairingCode(message.code)
124
+ if (!success) {
125
+ this.adapter.sendTo(
126
+ from, command,
127
+ {error: 'Pairing code rejected by Coordinator!'},
128
+ callback
129
+ );
130
+ return;
131
+ };
132
+ }
133
+ catch (e) {
134
+ this.error(JSON.stringify(e))
135
+ this.adapter.sendTo(
136
+ from, command,
137
+ {error: 'Exception when trying to add QR code'},
138
+ callback
139
+ );
140
+ return;
141
+
142
+ }
143
+ }
120
144
  }
121
145
  // allow devices to join the network within 60 secs
122
146
  this.adapter.logToPairing('Pairing started ' + devId, true);
@@ -257,7 +281,7 @@ class Commands {
257
281
  memberinfo.push(member);
258
282
  }
259
283
  devInfo.memberinfo = memberinfo;
260
- this.debug(`memberinfo: ${JSON.stringify(devInfo.memberinfo)}`);
284
+ this.debug(`memberinfo for ${match[1]}: ${JSON.stringify(devInfo.memberinfo)}`);
261
285
  }
262
286
  }
263
287
  } else {
@@ -291,11 +315,11 @@ class Commands {
291
315
  .then(async (devices) => {
292
316
  // fill group info
293
317
  for (const groupdev in groups) {
294
- this.debug(`GetDevices scanning group ${groupdev} ${JSON.stringify(groups[groupdev])}`);
318
+ //this.debug(`GetDevices scanning group ${groupdev} ${JSON.stringify(groups[groupdev])}`);
295
319
  const device = devices.find(dev => (groupdev === getZbId(dev._id)));
296
320
  if (device) {
297
321
  device.groups = groups[groupdev];
298
- this.debug(`adding group info to device ${groupdev}`);
322
+ //this.debug(`adding group info to device ${groupdev}`);
299
323
  }
300
324
  }
301
325
  // append devices that paired but not created
package/lib/groups.js CHANGED
@@ -123,6 +123,7 @@ class Groups {
123
123
  try {
124
124
  const groups = message && message.groups ? message.groups : {};
125
125
  const devId = message && message.id ? message.id : undefined;
126
+ this.warn('updateGroupMembership called with ' + JSON.stringify(devId))
126
127
  if (devId === undefined) {
127
128
  this.adapter.sendTo(from, command, {error: 'No device specified'}, callback);
128
129
  }
@@ -137,12 +138,15 @@ class Groups {
137
138
  for (const gpid of groups[epid]) {
138
139
  const gpidn = parseInt(gpid);
139
140
  if (gpidn < 0) {
141
+ this.warn(`calling removeDevFromGroup with ${sysid}, ${-gpidn}, ${epid}` )
140
142
  const response = await this.zbController.removeDevFromGroup(sysid, (-gpidn), epid);
141
143
  if (response && response.error) {
142
144
  errors.push(response.error);
143
145
  this.error(`remove dev from group Error: ${JSON.stringify(response.error)}`);
144
146
  }
147
+
145
148
  } else if (gpidn > 0) {
149
+ this.warn(`calling addDevToGroup with ${sysid}, ${gpidn}, ${epid}` )
146
150
  const response = await this.zbController.addDevToGroup(sysid, (gpidn), epid);
147
151
  if (response && response.error) {
148
152
  errors.push(response.error);
@@ -154,6 +158,7 @@ class Groups {
154
158
  }
155
159
  }
156
160
  } catch (e) {
161
+ this.warn('caught error ' + JSON.stringify(e) + ' in updateGroupMembership')
157
162
  this.adapter.sendTo(from, command, {error: e}, callback);
158
163
  return;
159
164
  }
package/lib/utils.js CHANGED
@@ -137,6 +137,11 @@ function getModelRegEx( model) {
137
137
  return stripModel;
138
138
  }
139
139
 
140
+ function getEntityInfo(entity) {
141
+ if (entity) return `Type: ${entity.type} Name: ${entity.name}`
142
+ return `getEntityInfo: Illegal Entity ${JSON.stringify(entity)}`
143
+ }
144
+
140
145
  exports.secondsToMilliseconds = seconds => seconds * 1000;
141
146
  exports.bulbLevelToAdapterLevel = bulbLevelToAdapterLevel;
142
147
  exports.adapterLevelToBulbLevel = adapterLevelToBulbLevel;
@@ -155,3 +160,4 @@ exports.isXiaomiDevice = device =>
155
160
  (!device.manufacturerName || !device.manufacturerName.startsWith('Trust'));
156
161
  exports.isIkeaTradfriDevice = device => ikeaTradfriManufacturerID.includes(device.manufacturerID);
157
162
  exports.getDeviceIcon = getDeviceIcon;
163
+ exports.getEntityInfo = getEntityInfo;
@@ -9,6 +9,7 @@ const DeviceAvailabilityExt = require('./zbDeviceAvailability');
9
9
  const DeviceConfigureExt = require('./zbDeviceConfigure');
10
10
  const DeviceEventExt = require('./zbDeviceEvent');
11
11
  const DelayedActionExt = require('./zbDelayedAction');
12
+ const utils = require('./utils');
12
13
  const groupConverters = [
13
14
  zigbeeHerdsmanConverters.toZigbeeConverters.light_onoff_brightness,
14
15
  zigbeeHerdsmanConverters.toZigbeeConverters.light_colortemp,
@@ -311,6 +312,16 @@ class ZigbeeController extends EventEmitter {
311
312
  }
312
313
  }
313
314
 
315
+ async addPairingCode(code) {
316
+ this.debug(`calling addPairingCode with ${code}`);
317
+ if (code) {
318
+ await this.herdsman.addInstallCode(code)
319
+ this.info(`added code ${code} for pairing`)
320
+ return true;
321
+ }
322
+ return false;
323
+ }
324
+
314
325
  async getGroupMembersFromController(id) {
315
326
  const members = [];
316
327
  try {
@@ -795,26 +806,36 @@ class ZigbeeController extends EventEmitter {
795
806
 
796
807
  async addDevToGroup(devId, groupId, epid) {
797
808
  try {
809
+ this.debug(`called addDevToGroup with ${devId}, ${groupId}, ${epid}`)
798
810
  const entity = await this.resolveEntity(devId);
799
811
  const group = await this.resolveEntity(groupId);
800
- this.debug(`addDevFromGroup - entity: ${safeJsonStringify(entity)}`);
801
- this.debug(`addDevFromGroup - group: ${safeJsonStringify(group)}`);
812
+ this.debug(`addDevFromGroup - entity: ${utils.getEntityInfo(entity)}`);
813
+ // generate group debug info and display it
814
+ const members = await this.getGroupMembersFromController(groupId)
815
+ let memberIDs = []
816
+ for (let member of members) {
817
+ memberIDs.push(member.ieee)
818
+ }
819
+ this.debug(`addDevToGroup ${groupId} with ${memberIDs.length} members ${safeJsonStringify(memberIDs)}`);
802
820
  if (epid != undefined) {
803
821
  for (const ep of entity.endpoints) {
804
- if (ep.ID === epid && (ep.inputClusters.includes(4) || ep.outputClusters.includes(4))) {
822
+ this.debug(`checking ep ${ep.ID} of ${devId} (${epid})`)
823
+ if (ep.ID == epid) {
824
+ if (ep.inputClusters.includes(4) || ep.outputClusters.includes(4)) {
805
825
  this.debug(`adding endpoint ${ep.ID} (${epid}) to group ${groupId}`);
806
826
  await (ep.addToGroup(group.mapped));
827
+ }
828
+ else this.error(`cluster genGroups not supported for endpoint ${epid} of ${devId}`)
807
829
  }
808
830
  }
809
831
  } else {
810
832
  if (entity.endpoint.inputClusters.includes(4)) {
811
- this.debug(`adding endpoint ${entity.endpoint.ID} to group`);
833
+ this.info(`adding endpoint ${entity.endpoint.ID} of ${devId} to group`);
812
834
  await entity.endpoint.addToGroup(group.mapped);
813
835
  } else {
814
836
  let added = false;
815
837
  for (const ep of entity.endpoints) {
816
838
  if (ep.inputClusters.includes(4)) {
817
- this.debug(`adding endpoint ${ep.ID} to group`);
818
839
  await ep.addToGroup(group.mapped);
819
840
  added = true;
820
841
  break;
@@ -834,20 +855,33 @@ class ZigbeeController extends EventEmitter {
834
855
  }
835
856
 
836
857
  async removeDevFromGroup(devId, groupId, epid) {
858
+ this.debug(`removeDevFromGroup with ${devId}, ${groupId}, ${epid}`)
837
859
  let entity;
838
860
  try {
839
861
  entity = await this.resolveEntity(devId);
840
862
  const group = await this.resolveEntity(groupId);
841
- this.debug(`removeDevFromGroup - entity: ${safeJsonStringify(entity)}`);
842
- this.debug(`removeDevFromGroup - group: ${safeJsonStringify(group)}`);
863
+
864
+ const members = await this.getGroupMembersFromController(groupId)
865
+ let memberIDs = []
866
+ for (let member of members) {
867
+ memberIDs.push(member.ieee)
868
+ }
869
+
870
+ this.debug(`removeDevFromGroup - entity: ${utils.getEntityInfo(entity)}`);
871
+ this.debug(`removeDevFromGroup ${groupId} with ${memberIDs.length} members ${safeJsonStringify(memberIDs)}`);
872
+
843
873
  if (epid != undefined) {
844
874
  for (const ep of entity.endpoints) {
845
- if (ep.ID === epid && (ep.inputClusters.includes(4) || ep.outputClusters.includes(4))) {
846
- this.debug(`removing endpoint ${ep.ID} (${epid}) group ${groupId}`);
875
+ this.debug(`checking ep ${ep.ID} of ${devId} (${epid})`)
876
+ if (ep.ID == epid && (ep.inputClusters.includes(4) || ep.outputClusters.includes(4))) {
847
877
  await ep.removeFromGroup(group.mapped);
878
+ this.info(`removing endpoint ${ep.ID} of ${devId} from group ${groupId}`);
848
879
  }
849
880
  }
850
- } else await entity.endpoint.removeFromGroup(group.mapped);
881
+ } else {
882
+ await entity.endpoint.removeFromGroup(group.mapped);
883
+ this.info(`removing endpoint ${entity.endpoint.ID} of ${devId} from group ${groupId}`);
884
+ }
851
885
  } catch (error) {
852
886
  this.sendError(error);
853
887
  this.error(`Exception when trying remove ${devId} (ep ${epid ? epid : (entity ? entity.endpoint.ID : '')}) from group ${devId}`, error);
@@ -865,7 +899,6 @@ class ZigbeeController extends EventEmitter {
865
899
  await ep.removefromAllGroups();
866
900
  }
867
901
  }
868
- // await entity.endpoint.removeFromAllGroups();
869
902
  } catch (error) {
870
903
  this.sendError(error);
871
904
  this.error(`Exception when trying remove ${devId} from all groups`, error);
package/main.js CHANGED
@@ -216,9 +216,64 @@ class Zigbee extends utils.Adapter {
216
216
  }
217
217
  const extfiles = this.config.external.split(';');
218
218
  for (const moduleName of extfiles) {
219
- if (!moduleName) {
220
- continue;
219
+ if (!moduleName) continue;
220
+ const sandbox = {
221
+ require,
222
+ module: {},
223
+ };
224
+ const mN = (fs.existsSync(moduleName) ? moduleName : this.expandFileName(moduleName).replace('.', '_'));
225
+ if (!fs.existsSync(mN)) {
226
+ this.log.warn(`External converter not loaded - neither ${moduleName} nor ${mN} exist.`)
227
+
221
228
  }
229
+ else {
230
+ const converterCode = fs.readFileSync(mN, {encoding: 'utf8'}).toString();
231
+ let converterLoaded = true;
232
+ if (converterCode.match(/..\/lib\/legacy/gm)) {
233
+ this.log.warn(`External converter ${mN} contains an unsupported reference to '/lib/legacy' - external converter not loaded.`)
234
+ converterLoaded = false;
235
+ }
236
+ else
237
+ {
238
+ // remove the require statements and attempt to place them in the sandbox
239
+ const requiredLibraries = converterCode.matchAll(/(\w+) += +require\(['"](\S+)['"]\);/gm);
240
+ for (const line of requiredLibraries) {
241
+ const movedLine = line[2].replace('..', '../zigbee-herdsman-converters')
242
+ try {
243
+ sandbox[line[1]] = require(movedLine);
244
+ }
245
+ catch (e) {
246
+ this.log.warn(`error adding ${line[1]} (${movedLine}) to the sandbox: ${e}`);
247
+ converterLoaded = false;
248
+ }
249
+ }
250
+ }
251
+ if (converterLoaded) {
252
+ this.log.info(`Apply converter from module: ${mN}`);
253
+ //this.log.warn(converterCode.replace(/const (\w+) += +require\(['"](\S+)['"]\);/gm, ''));
254
+ try {
255
+ vm.runInNewContext(converterCode.replace(/const (\w+) += +require\(['"](\S+)['"]\);/gm, ''), sandbox);
256
+ const converter = sandbox.module.exports;
257
+
258
+ if (Array.isArray(converter)) for (const item of converter) yield item;
259
+ else yield converter;
260
+ }
261
+ catch (e) {
262
+ this.log.error(`Unable to apply converter from module: ${mN} - the code does not run: ${e}`)
263
+ }
264
+ }
265
+ else
266
+ this.log.info(`Ignoring converter from module: ${mN} - see warn messages for reason`);
267
+
268
+ }
269
+ }
270
+
271
+ /* if (this.config.external === undefined) {
272
+ return;
273
+ }
274
+ const extfiles = this.config.external.split(';');
275
+ for (const moduleName of extfiles) {
276
+ if (!moduleName) continue;
222
277
  this.log.info(`Apply converter from module: ${moduleName}`);
223
278
  const sandbox = {
224
279
  require,
@@ -235,14 +290,23 @@ class Zigbee extends utils.Adapter {
235
290
  yield converter;
236
291
  }
237
292
  }
238
- }
293
+ */
294
+ }
239
295
 
240
296
  applyExternalConverters() {
297
+ try {
241
298
  for (const definition of this.getExternalDefinition()) {
242
299
  const toAdd = {...definition};
243
300
  delete toAdd['homeassistant'];
244
- zigbeeHerdsmanConverters.addDeviceDefinition(toAdd);
301
+ try {
302
+ zigbeeHerdsmanConverters.addDeviceDefinition(toAdd);
303
+ }
304
+ catch { this.log.error(`unable to apply external converter ${JSON.stringfy(toAdd)}`) }
245
305
  }
306
+ }
307
+ catch(error) {
308
+ this.log.error('error applying external converters');
309
+ }
246
310
  }
247
311
 
248
312
  async doConnect() {
@@ -851,6 +915,7 @@ class Zigbee extends utils.Adapter {
851
915
 
852
916
  onPairing(message, data) {
853
917
  if (Number.isInteger(data)) {
918
+ _pairingMode = true;
854
919
  this.setState('info.pairingCountdown', data, true);
855
920
  _pairingMode = true;
856
921
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.zigbee",
3
- "version": "1.8.9",
3
+ "version": "1.8.10",
4
4
  "author": {
5
5
  "name": "Kirov Ilya",
6
6
  "email": "kirovilya@gmail.com"
@@ -24,8 +24,8 @@
24
24
  "@iobroker/adapter-core": "^2.6.7",
25
25
  "tar": "^6.1.12",
26
26
  "typescript": "^4.9.3",
27
- "zigbee-herdsman": "0.14.80",
28
- "zigbee-herdsman-converters": "14.0.687"
27
+ "zigbee-herdsman": "0.14.83",
28
+ "zigbee-herdsman-converters": "15.0.15"
29
29
  },
30
30
  "description": "Zigbee devices",
31
31
  "devDependencies": {
@@ -36,14 +36,14 @@
36
36
  "axios": "^1.2.0",
37
37
  "chai": "^4.3.7",
38
38
  "chai-as-promised": "^7.1.1",
39
- "eslint": "^8.28.0",
39
+ "eslint": "^8.31.0",
40
40
  "eslint-config-prettier": "^8.5.0",
41
41
  "eslint-plugin-prettier": "^4.2.1",
42
42
  "gulp": "^4.0.2",
43
43
  "gulp-jsdoc3": "^3.0.0",
44
44
  "gulp-replace": "^1.1.3",
45
45
  "mixin-deep": "^2.0.1",
46
- "mocha": "^10.1.0"
46
+ "mocha": "^10.2.0"
47
47
  },
48
48
  "homepage": "https://github.com/ioBroker/ioBroker.zigbee",
49
49
  "keywords": [