iobroker.zigbee 1.8.0 → 1.8.3

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.
Files changed (89) hide show
  1. package/.eslintignore +2 -0
  2. package/.eslintrc.json +37 -0
  3. package/.github/FUNDING.yml +3 -0
  4. package/.github/auto-merge.yml +17 -0
  5. package/.github/dependabot.yml +24 -0
  6. package/.github/stale.yml +13 -0
  7. package/.github/workflows/codeql.yml +41 -0
  8. package/.github/workflows/dependabot-automerge.yml +22 -0
  9. package/.github/workflows/test-and-release.yml +149 -0
  10. package/.releaseconfig.json +3 -0
  11. package/.travis/wiki.sh +28 -0
  12. package/.travis.yml +41 -0
  13. package/README.md +32 -9
  14. package/admin/admin.js +466 -482
  15. package/admin/i18n/de/translations.json +2 -2
  16. package/admin/index_m.html +1 -1
  17. package/admin/tab_m.html +3 -44
  18. package/admin/words.js +2 -2
  19. package/gulpfile.js +464 -0
  20. package/io-package.json +19 -26
  21. package/lib/backup.js +2 -2
  22. package/lib/binding.js +24 -23
  23. package/lib/colors.js +14 -16
  24. package/lib/commands.js +82 -89
  25. package/lib/developer.js +7 -6
  26. package/lib/devices.js +153 -144
  27. package/lib/exclude.js +36 -30
  28. package/lib/exposes.js +111 -106
  29. package/lib/groups.js +54 -53
  30. package/lib/json.js +4 -3
  31. package/lib/networkmap.js +2 -2
  32. package/lib/ota.js +15 -23
  33. package/lib/rgb.js +44 -47
  34. package/lib/seriallist.js +16 -21
  35. package/lib/states.js +496 -482
  36. package/lib/statescontroller.js +164 -170
  37. package/lib/utils.js +21 -22
  38. package/lib/zbBaseExtension.js +4 -4
  39. package/lib/zbDelayedAction.js +13 -5
  40. package/lib/zbDeviceAvailability.js +44 -47
  41. package/lib/zbDeviceConfigure.js +19 -7
  42. package/lib/zbDeviceEvent.js +4 -3
  43. package/lib/zigbeecontroller.js +96 -88
  44. package/main.js +133 -149
  45. package/package.json +15 -29
  46. package/test/integration.js +5 -0
  47. package/test/mocha.custom.opts +2 -0
  48. package/test/mocha.setup.js +14 -0
  49. package/test/package.js +5 -0
  50. package/test/unit.js +5 -0
  51. package/docs/de/img/CC2531.png +0 -0
  52. package/docs/de/img/CC2538_CC2592_PA.PNG +0 -0
  53. package/docs/de/img/CC2591.png +0 -0
  54. package/docs/de/img/boards.jpg +0 -0
  55. package/docs/de/img/cc26x2r.PNG +0 -0
  56. package/docs/de/img/results.jpg +0 -0
  57. package/docs/de/img/sku_429478_2.png +0 -0
  58. package/docs/de/img/sku_429601_2.png +0 -0
  59. package/docs/de/readme.md +0 -27
  60. package/docs/en/img/CC2531.png +0 -0
  61. package/docs/en/img/CC2591.png +0 -0
  62. package/docs/en/img/deconz.png +0 -0
  63. package/docs/en/img/sku_429478_2.png +0 -0
  64. package/docs/en/img/sku_429601_2.png +0 -0
  65. package/docs/en/readme.md +0 -30
  66. package/docs/flashing_via_arduino_(en).md +0 -110
  67. package/docs/ru/img/CC2531.png +0 -0
  68. package/docs/ru/img/CC2591.png +0 -0
  69. package/docs/ru/img/sku_429478_2.png +0 -0
  70. package/docs/ru/img/sku_429601_2.png +0 -0
  71. package/docs/ru/readme.md +0 -28
  72. package/docs/tutorial/CC2530_20190425.zip +0 -0
  73. package/docs/tutorial/CC2530_CC2591_20190515.zip +0 -0
  74. package/docs/tutorial/CC2530_CC2592_20190515.zip +0 -0
  75. package/docs/tutorial/CC2531_20190425.zip +0 -0
  76. package/docs/tutorial/adm5_1.PNG +0 -0
  77. package/docs/tutorial/adm5_2.PNG +0 -0
  78. package/docs/tutorial/cat.PNG +0 -0
  79. package/docs/tutorial/groups-1.png +0 -0
  80. package/docs/tutorial/groups-2.png +0 -0
  81. package/docs/tutorial/inst.PNG +0 -0
  82. package/docs/tutorial/reflash-finish.PNG +0 -0
  83. package/docs/tutorial/reflash-step0.png +0 -0
  84. package/docs/tutorial/reflash-step1.PNG +0 -0
  85. package/docs/tutorial/reflash-step2.PNG +0 -0
  86. package/docs/tutorial/settings.png +0 -0
  87. package/docs/tutorial/tab-dev-1.png +0 -0
  88. package/docs/tutorial/zigbee.png +0 -0
  89. package/docs/tutorial/zigbee15.png +0 -0
@@ -98,10 +98,10 @@
98
98
  "notImplementedText": "Sie können hier eigene Befehle an Ihre Geräte senden, die noch nicht als Objekte implementiert sind. Es kann auch verwendet werden, um undokumentierte Einstellungen Ihrer Geräte zu finden. Oder um die Auswirkungen von Einstellungen zu testen und zu entscheiden, ob es sich lohnt, sie als Objekt zu implementieren. Und so weiter...",
99
99
  "to make it available for other user too": "um es auch für andere Benutzer verfügbar zu machen",
100
100
  "Excludes": "ausschließen",
101
- "ExcludeTextTranslation": "Sie können hier einige Geräte hinzufügen, die von unserer Verarbeitungausgeschlossen werden sollten. Sie verwenden dann nur exposes vom ZigBee-Herdsman-Converter. Nach dem Hinzufügen starten Sie bitte den Adapter neu.",
101
+ "ExcludeTextTranslation": "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.",
102
102
  "Please contribute your discoveries": "Bitte geben Sie uns Ihre Vorschläge",
103
103
  "zigbee-shepherd": "Sie können die zu sendenden Rohdaten hier bearbeiten. Verwenden Sie diese nicht, wenn Sie sich nicht sicher sind, was Sie tun! Verwenden Sie das JSON-Format (Attributnamen in doppelten Anführungszeichen!). Es können auch zusätzliche Eigenschaften hinzugefügt werden. Siehe Beispiele oben. (Format wie von zigbee-shepherd verwendet)",
104
- "SettingsExclude": "Erzwingen Sie den Start des Adapters mit inkonsistenter Konfiguration (nicht empfohlen). Bitte aktualisieren Sie den Adapter auf kompatible Firmware und erstellen Sie Ihr Netzwerk so schnell wie möglich neu.",
104
+ "SettingsExclude": "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.",
105
105
  "disable internal Backup": "Deaktiviere die interne Sicherung",
106
106
  "Disable active availability check": "Aktive Verfügbarkeitsprüfung deaktivieren",
107
107
  "Others": "andere Einstellungen"
@@ -17,7 +17,7 @@
17
17
  <script type="text/javascript" src="../../js/adapter-settings.js"></script>
18
18
  <script type="text/javascript" src="words.js"></script>
19
19
 
20
-
20
+
21
21
  <script type="text/javascript" src="admin.js"></script>
22
22
  <script type="text/javascript" src="shuffle.min.js"></script>
23
23
  <script type="text/javascript" src="moment.min.js"></script>
package/admin/tab_m.html CHANGED
@@ -15,54 +15,13 @@
15
15
  <script type="text/javascript" src="../../js/translate.js"></script>
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
- <script type="text/javascript" src="../../js/adapter-settings.js"></script>
19
- <script>
20
- // overload showMessage
21
- function showMessage(message, title, icon) {
22
- var $dialogMessage;
23
- // noinspection JSJQueryEfficiency
24
- $dialogMessage = $('#dialog-message');
25
- if (!$dialogMessage.length) {
26
- $('body').append(
27
- '<div class="m"><div id="dialog-message" class="modal modal-fixed-footer">' +
28
- ' <div class="modal-content">' +
29
- ' <h6 class="dialog-title title"></h6>' +
30
- ' <p><i class="large material-icons dialog-icon"></i><span class="dialog-text"></span></p>' +
31
- ' </div>' +
32
- ' <div class="modal-footer">' +
33
- ' <a class="modal-action modal-close waves-effect waves-green btn-flat translate">Ok</a>' +
34
- ' </div>' +
35
- '</div></div>');
36
- $dialogMessage = $('#dialog-message');
37
- }
38
- if (icon) {
39
- $dialogMessage.find('.dialog-icon')
40
- .show()
41
- .html(icon);
42
- } else {
43
- $dialogMessage.find('.dialog-icon').hide();
44
- }
45
- if (title) {
46
- $dialogMessage.find('.dialog-title').html(title).show();
47
- } else {
48
- $dialogMessage.find('.dialog-title').hide();
49
- }
50
- $dialogMessage.find('.dialog-text').html(message);
51
- $dialogMessage.modal().modal('open');
52
- }
53
- </script>
18
+ <script type="text/javascript" src="adapter-settings.js"></script>
54
19
  <script type="text/javascript" src="words.js"></script>
55
20
 
56
21
  <script type="text/javascript" src="admin.js"></script>
57
22
  <script type="text/javascript" src="shuffle.min.js"></script>
58
23
  <script type="text/javascript" src="moment.min.js"></script>
59
24
  <style>
60
- .m.react-dark .tabs {
61
- background-color: #272727 !important;
62
- }
63
- .row.navbar-fixed {
64
- height: 0 !important;
65
- }
66
25
  .m .col .select-wrapper+label, .m .col input+label:not(.active) {
67
26
  top: -26px;
68
27
  }
@@ -91,8 +50,8 @@
91
50
  .input-field.suffix a {
92
51
  position: absolute;
93
52
  font-size: 2rem;
94
- top: 0;
95
- right: 0;
53
+ top: 0px;
54
+ right: 0px;
96
55
  }
97
56
 
98
57
  .input-field.suffix a.active {
package/admin/words.js CHANGED
@@ -96,8 +96,8 @@ systemDictionary = {
96
96
  "zigbee-shepherd": { "uk": "Ви можете редагувати сирі дані для надсилання сюди. Не використовуйте це, якщо ви не впевнені в тому, що робите! Використовуйте формат JSON (імена атрибутів у подвійних лапках!). Також можуть бути додані додаткові атрибути. Дивіться приклади вище. (Формат, який використовує zigbee-shepherd)", "en": "You may edit the raw data to send here. Do not use this if you are not sure of what you are doing! Use JSON format (attribute names in double quotes!). Additional properties may be added too. See examples above. (Format as used by zigbee-shepherd)", "de": "Sie können die zu sendenden Rohdaten hier bearbeiten. Verwenden Sie diese nicht, wenn Sie sich nicht sicher sind, was Sie tun! Verwenden Sie das JSON-Format (Attributnamen in doppelten Anführungszeichen!). Es können auch zusätzliche Eigenschaften hinzugefügt werden. Siehe Beispiele oben. (Format wie von zigbee-shepherd verwendet)", "ru": "Вы можете редактировать необработанные данные для отправки сюда. Не используйте это, если не уверены в том, что делаете! Используйте формат JSON (имена атрибутов в двойных кавычках!). Также могут быть добавлены дополнительные свойства. См. Примеры выше. (Формат, используемый zigbee-shepherd)", "pt": "Você pode editar os dados brutos para enviar aqui. Não use isso se não tiver certeza do que está fazendo! Use o formato JSON (nomes de atributos entre aspas duplas!). Propriedades adicionais também podem ser adicionadas. Veja os exemplos acima. (Formato usado pelo pastor zigbee)", "nl": "U kunt de onbewerkte gegevens bewerken om hier naartoe te sturen. Gebruik dit niet als u niet zeker weet wat u doet! Gebruik het JSON-formaat (attribuutnamen tussen dubbele aanhalingstekens!). Er kunnen ook aanvullende eigenschappen worden toegevoegd. Zie voorbeelden hierboven. (Formaat zoals gebruikt door zigbee-shepherd)", "fr": "Vous pouvez modifier les données brutes à envoyer ici. Ne l'utilisez pas si vous n'êtes pas sûr de ce que vous faites ! Utilisez le format JSON (noms d'attributs entre guillemets !). Des propriétés supplémentaires peuvent également être ajoutées. Voir les exemples ci-dessus. (Format utilisé par zigbee-shepherd)", "it": "Puoi modificare i dati grezzi da inviare qui. Non usarlo se non sei sicuro di quello che stai facendo! Usa il formato JSON (nomi di attributi tra virgolette doppie!). Possono essere aggiunte anche proprietà aggiuntive. Vedi esempi sopra. (Formato usato da zigbee-shepherd)", "es": "Puede editar los datos sin procesar para enviar aquí. ¡No use esto si no está seguro de lo que está haciendo! Utilice el formato JSON (nombres de atributos entre comillas dobles). También se pueden agregar propiedades adicionales. Vea los ejemplos anteriores. (Formato utilizado por zigbee-shepherd)", "pl": "Tutaj możesz edytować nieprzetworzone dane do wysłania. Nie używaj tego, jeśli nie jesteś pewien, co robisz! Użyj formatu JSON (nazwy atrybutów w podwójnych cudzysłowach!). Można również dodać dodatkowe właściwości. Zobacz przykłady powyżej. (Format używany przez zigbee-shepherd)", "zh-cn": "您可以编辑原始数据以发送到此处。如果不确定自己在做什么,请不要使用它!使用JSON格式(属性名称用双引号引起来!)。也可以添加其他属性。请参阅上面的示例。 (格式由zigbee-shepherd使用)"},
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
- "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 Verarbeitungausgeschlossen 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": "Erzwingen Sie den Start des Adapters mit inkonsistenter Konfiguration (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ć."},
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ć."},
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"},
package/gulpfile.js ADDED
@@ -0,0 +1,464 @@
1
+ /*!
2
+ * ioBroker gulpfile
3
+ * Date: 2019-01-22
4
+ */
5
+ /*eslint no-unused-vars: ['off']*/
6
+ 'use strict';
7
+
8
+ const gulp = require('gulp');
9
+ const fs = require('fs');
10
+ const pkg = require('./package.json');
11
+ const iopackage = require('./io-package.json');
12
+ const version = (pkg && pkg.version) ? pkg.version : iopackage.common.version;
13
+ const fileName = 'words.js';
14
+ const EMPTY = '';
15
+ const translate = require('./lib/tools.js').translateText;
16
+ const languages = {
17
+ en: {},
18
+ de: {},
19
+ ru: {},
20
+ pt: {},
21
+ nl: {},
22
+ fr: {},
23
+ it: {},
24
+ es: {},
25
+ pl: {},
26
+ 'zh-cn': {}
27
+ };
28
+
29
+ function lang2data(lang, isFlat) {
30
+ let str = isFlat ? '' : '{\n';
31
+ let count = 0;
32
+ for (const w in lang) {
33
+ if (lang.hasOwnProperty(w)) {
34
+ count++;
35
+ if (isFlat) {
36
+ str += (lang[w] === '' ? (isFlat[w] || w) : lang[w]) + '\n';
37
+ } else {
38
+ const key = ' "' + w.replace(/"/g, '\\"') + '": ';
39
+ str += key + '"' + lang[w].replace(/"/g, '\\"') + '",\n';
40
+ }
41
+ }
42
+ }
43
+ if (!count)
44
+ return isFlat ? '' : '{\n}';
45
+ if (isFlat) {
46
+ return str;
47
+ } else {
48
+ return str.substring(0, str.length - 2) + '\n}';
49
+ }
50
+ }
51
+
52
+ function readWordJs(src) {
53
+ try {
54
+ let words;
55
+ if (fs.existsSync(src + 'js/' + fileName)) {
56
+ words = fs.readFileSync(src + 'js/' + fileName).toString();
57
+ } else {
58
+ words = fs.readFileSync(src + fileName).toString();
59
+ }
60
+ words = words.substring(words.indexOf('{'), words.length);
61
+ words = words.substring(0, words.lastIndexOf(';'));
62
+
63
+ const resultFunc = new Function('return ' + words + ';');
64
+
65
+ return resultFunc();
66
+ } catch (e) {
67
+ return null;
68
+ }
69
+ }
70
+
71
+ function padRight(text, totalLength) {
72
+ return text + (text.length < totalLength ? new Array(totalLength - text.length).join(' ') : '');
73
+ }
74
+
75
+ function writeWordJs(data, src) {
76
+ let text = '';
77
+ text += '/*global systemDictionary:true */\n';
78
+ text += '\'use strict\';\n\n';
79
+ text += 'systemDictionary = {\n';
80
+ for (const word in data) {
81
+ if (data.hasOwnProperty(word)) {
82
+ text += ' ' + padRight('"' + word.replace(/"/g, '\\"') + '": {', 50);
83
+ let line = '';
84
+ for (const lang in data[word]) {
85
+ if (data[word].hasOwnProperty(lang)) {
86
+ line += '"' + lang + '": "' + padRight(data[word][lang].replace(/"/g, '\\"') + '",', 50) + ' ';
87
+ }
88
+ }
89
+ if (line) {
90
+ line = line.trim();
91
+ line = line.substring(0, line.length - 1);
92
+ }
93
+ text += line + '},\n';
94
+ }
95
+ }
96
+ text += '};';
97
+ if (fs.existsSync(src + 'js/' + fileName)) {
98
+ fs.writeFileSync(src + 'js/' + fileName, text);
99
+ } else {
100
+ fs.writeFileSync(src + '' + fileName, text);
101
+ }
102
+ }
103
+
104
+ function words2languages(src) {
105
+ const langs = Object.assign({}, languages);
106
+ const data = readWordJs(src);
107
+ if (data) {
108
+ for (const word in data) {
109
+ if (data.hasOwnProperty(word)) {
110
+ for (const lang in data[word]) {
111
+ if (data[word].hasOwnProperty(lang)) {
112
+ langs[lang][word] = data[word][lang];
113
+ // pre-fill all other languages
114
+ for (const j in langs) {
115
+ if (langs.hasOwnProperty(j)) {
116
+ langs[j][word] = langs[j][word] || EMPTY;
117
+ }
118
+ }
119
+ }
120
+ }
121
+ }
122
+ }
123
+ if (!fs.existsSync(src + 'i18n/')) {
124
+ fs.mkdirSync(src + 'i18n/');
125
+ }
126
+ for (const l in langs) {
127
+ if (!langs.hasOwnProperty(l))
128
+ continue;
129
+ const keys = Object.keys(langs[l]);
130
+ keys.sort();
131
+ const obj = {};
132
+ for (let k = 0; k < keys.length; k++) {
133
+ obj[keys[k]] = langs[l][keys[k]];
134
+ }
135
+ if (!fs.existsSync(src + 'i18n/' + l)) {
136
+ fs.mkdirSync(src + 'i18n/' + l);
137
+ }
138
+
139
+ fs.writeFileSync(src + 'i18n/' + l + '/translations.json', lang2data(obj));
140
+ }
141
+ } else {
142
+ console.error('Cannot read or parse ' + fileName);
143
+ }
144
+ }
145
+
146
+ function words2languagesFlat(src) {
147
+ const langs = Object.assign({}, languages);
148
+ const data = readWordJs(src);
149
+ if (data) {
150
+ for (const word in data) {
151
+ if (data.hasOwnProperty(word)) {
152
+ for (const lang in data[word]) {
153
+ if (data[word].hasOwnProperty(lang)) {
154
+ langs[lang][word] = data[word][lang];
155
+ // pre-fill all other languages
156
+ for (const j in langs) {
157
+ if (langs.hasOwnProperty(j)) {
158
+ langs[j][word] = langs[j][word] || EMPTY;
159
+ }
160
+ }
161
+ }
162
+ }
163
+ }
164
+ }
165
+ const keys = Object.keys(langs.en);
166
+ keys.sort();
167
+ for (const l in langs) {
168
+ if (!langs.hasOwnProperty(l))
169
+ continue;
170
+ const obj = {};
171
+ for (let k = 0; k < keys.length; k++) {
172
+ obj[keys[k]] = langs[l][keys[k]];
173
+ }
174
+ langs[l] = obj;
175
+ }
176
+ if (!fs.existsSync(src + 'i18n/')) {
177
+ fs.mkdirSync(src + 'i18n/');
178
+ }
179
+ for (const ll in langs) {
180
+ if (!langs.hasOwnProperty(ll))
181
+ continue;
182
+ if (!fs.existsSync(src + 'i18n/' + ll)) {
183
+ fs.mkdirSync(src + 'i18n/' + ll);
184
+ }
185
+
186
+ fs.writeFileSync(src + 'i18n/' + ll + '/flat.txt', lang2data(langs[ll], langs.en));
187
+ }
188
+ fs.writeFileSync(src + 'i18n/flat.txt', keys.join('\n'));
189
+ } else {
190
+ console.error('Cannot read or parse ' + fileName);
191
+ }
192
+ }
193
+
194
+ function languagesFlat2words(src) {
195
+ const dirs = fs.readdirSync(src + 'i18n/');
196
+ const langs = {};
197
+ const bigOne = {};
198
+ const order = Object.keys(languages);
199
+ dirs.sort(function (a, b) {
200
+ const posA = order.indexOf(a);
201
+ const posB = order.indexOf(b);
202
+ if (posA === -1 && posB === -1) {
203
+ if (a > b)
204
+ return 1;
205
+ if (a < b)
206
+ return -1;
207
+ return 0;
208
+ } else if (posA === -1) {
209
+ return -1;
210
+ } else if (posB === -1) {
211
+ return 1;
212
+ } else {
213
+ if (posA > posB)
214
+ return 1;
215
+ if (posA < posB)
216
+ return -1;
217
+ return 0;
218
+ }
219
+ });
220
+ const keys = fs.readFileSync(src + 'i18n/flat.txt').toString().split('\n');
221
+
222
+ for (let l = 0; l < dirs.length; l++) {
223
+ if (dirs[l] === 'flat.txt')
224
+ continue;
225
+ const lang = dirs[l];
226
+ const values = fs.readFileSync(src + 'i18n/' + lang + '/flat.txt').toString().split('\n');
227
+ langs[lang] = {};
228
+ keys.forEach(function (word, i) {
229
+ langs[lang][word] = values[i];
230
+ });
231
+
232
+ const words = langs[lang];
233
+ for (const word in words) {
234
+ if (words.hasOwnProperty(word)) {
235
+ bigOne[word] = bigOne[word] || {};
236
+ if (words[word] !== EMPTY) {
237
+ bigOne[word][lang] = words[word];
238
+ }
239
+ }
240
+ }
241
+ }
242
+ // read actual words.js
243
+ const aWords = readWordJs();
244
+
245
+ const temporaryIgnore = ['flat.txt'];
246
+ if (aWords) {
247
+ // Merge words together
248
+ for (const w in aWords) {
249
+ if (aWords.hasOwnProperty(w)) {
250
+ if (!bigOne[w]) {
251
+ console.warn('Take from actual words.js: ' + w);
252
+ bigOne[w] = aWords[w];
253
+ }
254
+ dirs.forEach(function (lang) {
255
+ if (temporaryIgnore.indexOf(lang) !== -1)
256
+ return;
257
+ if (!bigOne[w][lang]) {
258
+ console.warn('Missing "' + lang + '": ' + w);
259
+ }
260
+ });
261
+ }
262
+ }
263
+
264
+ }
265
+
266
+ writeWordJs(bigOne, src);
267
+ }
268
+
269
+ function languages2words(src) {
270
+ const dirs = fs.readdirSync(src + 'i18n/');
271
+ const langs = {};
272
+ const bigOne = {};
273
+ const order = Object.keys(languages);
274
+ dirs.sort(function (a, b) {
275
+ const posA = order.indexOf(a);
276
+ const posB = order.indexOf(b);
277
+ if (posA === -1 && posB === -1) {
278
+ if (a > b)
279
+ return 1;
280
+ if (a < b)
281
+ return -1;
282
+ return 0;
283
+ } else if (posA === -1) {
284
+ return -1;
285
+ } else if (posB === -1) {
286
+ return 1;
287
+ } else {
288
+ if (posA > posB)
289
+ return 1;
290
+ if (posA < posB)
291
+ return -1;
292
+ return 0;
293
+ }
294
+ });
295
+ for (let l = 0; l < dirs.length; l++) {
296
+ if (dirs[l] === 'flat.txt')
297
+ continue;
298
+ const lang = dirs[l];
299
+ langs[lang] = fs.readFileSync(src + 'i18n/' + lang + '/translations.json').toString();
300
+ langs[lang] = JSON.parse(langs[lang]);
301
+ const words = langs[lang];
302
+ for (const word in words) {
303
+ if (words.hasOwnProperty(word)) {
304
+ bigOne[word] = bigOne[word] || {};
305
+ if (words[word] !== EMPTY) {
306
+ bigOne[word][lang] = words[word];
307
+ }
308
+ }
309
+ }
310
+ }
311
+ // read actual words.js
312
+ const aWords = readWordJs();
313
+
314
+ const temporaryIgnore = ['flat.txt'];
315
+ if (aWords) {
316
+ // Merge words together
317
+ for (const w in aWords) {
318
+ if (aWords.hasOwnProperty(w)) {
319
+ if (!bigOne[w]) {
320
+ console.warn('Take from actual words.js: ' + w);
321
+ bigOne[w] = aWords[w];
322
+ }
323
+ dirs.forEach(function (lang) {
324
+ if (temporaryIgnore.indexOf(lang) !== -1)
325
+ return;
326
+ if (!bigOne[w][lang]) {
327
+ console.warn('Missing "' + lang + '": ' + w);
328
+ }
329
+ });
330
+ }
331
+ }
332
+
333
+ }
334
+
335
+ writeWordJs(bigOne, src);
336
+ }
337
+
338
+ async function translateNotExisting(obj, baseText) {
339
+ let t = obj['en'];
340
+ if (!t) {
341
+ t = baseText;
342
+ }
343
+
344
+ if (t) {
345
+ for (const l in languages) {
346
+ if (!obj[l]) {
347
+ obj[l] = await translate(t, l);
348
+ }
349
+ }
350
+ }
351
+ }
352
+
353
+ //TASKS
354
+
355
+ gulp.task('adminWords2languages', function (done) {
356
+ words2languages('./admin/');
357
+ done();
358
+ });
359
+
360
+ gulp.task('adminWords2languagesFlat', function (done) {
361
+ words2languagesFlat('./admin/');
362
+ done();
363
+ });
364
+
365
+ gulp.task('adminLanguagesFlat2words', function (done) {
366
+ languagesFlat2words('./admin/');
367
+ done();
368
+ });
369
+
370
+ gulp.task('adminLanguages2words', function (done) {
371
+ languages2words('./admin/');
372
+ done();
373
+ });
374
+
375
+ gulp.task('updatePackages', function (done) {
376
+ iopackage.common.version = pkg.version;
377
+ iopackage.common.news = iopackage.common.news || {};
378
+ if (!iopackage.common.news[pkg.version]) {
379
+ const news = iopackage.common.news;
380
+ const newNews = {};
381
+
382
+ newNews[pkg.version] = {
383
+ en: 'news',
384
+ de: 'neues',
385
+ ru: 'новое',
386
+ pt: 'novidades',
387
+ nl: 'nieuws',
388
+ fr: 'nouvelles',
389
+ it: 'notizie',
390
+ es: 'noticias',
391
+ pl: 'nowości',
392
+ 'zh-cn': '新'
393
+ };
394
+ iopackage.common.news = Object.assign(newNews, news);
395
+ }
396
+ fs.writeFileSync('io-package.json', JSON.stringify(iopackage, null, 4));
397
+ done();
398
+ });
399
+
400
+ gulp.task('updateReadme', function (done) {
401
+ const readme = fs.readFileSync('README.md').toString();
402
+ const pos = readme.indexOf('## Changelog\n');
403
+ if (pos !== -1) {
404
+ const readmeStart = readme.substring(0, pos + '## Changelog\n'.length);
405
+ const readmeEnd = readme.substring(pos + '## Changelog\n'.length);
406
+
407
+ if (readme.indexOf(version) === -1) {
408
+ const timestamp = new Date();
409
+ const date = timestamp.getFullYear() + '-' +
410
+ ('0' + (timestamp.getMonth() + 1).toString(10)).slice(-2) + '-' +
411
+ ('0' + (timestamp.getDate()).toString(10)).slice(-2);
412
+
413
+ let news = '';
414
+ if (iopackage.common.news && iopackage.common.news[pkg.version]) {
415
+ news += '* ' + iopackage.common.news[pkg.version].en;
416
+ }
417
+
418
+ fs.writeFileSync('README.md', readmeStart + '### ' + version + ' (' + date + ')\n' + (news ? news + '\n\n' : '\n') + readmeEnd);
419
+ }
420
+ }
421
+ done();
422
+ });
423
+
424
+ gulp.task('translate', async function (done) {
425
+ if (iopackage && iopackage.common) {
426
+ if (iopackage.common.news) {
427
+ for (const k in iopackage.common.news) {
428
+ const nw = iopackage.common.news[k];
429
+ await translateNotExisting(nw);
430
+ }
431
+ }
432
+ if (iopackage.common.titleLang) {
433
+ await translateNotExisting(iopackage.common.titleLang, iopackage.common.title);
434
+ }
435
+ if (iopackage.common.desc) {
436
+ await translateNotExisting(iopackage.common.desc);
437
+ }
438
+
439
+ if (fs.existsSync('./admin/i18n/en/translations.json')) {
440
+ const enTranslations = require('./admin/i18n/en/translations.json');
441
+ for (const l in languages) {
442
+ let existing = {};
443
+ if (fs.existsSync('./admin/i18n/' + l + '/translations.json')) {
444
+ existing = require('./admin/i18n/' + l + '/translations.json');
445
+ }
446
+ for (const t in enTranslations) {
447
+ if (!existing[t]) {
448
+ existing[t] = await translate(enTranslations[t], l);
449
+ }
450
+ }
451
+ if (!fs.existsSync('./admin/i18n/' + l + '/')) {
452
+ fs.mkdirSync('./admin/i18n/' + l + '/');
453
+ }
454
+ fs.writeFileSync('./admin/i18n/' + l + '/translations.json', JSON.stringify(existing, null, 4));
455
+ }
456
+ }
457
+
458
+ }
459
+ fs.writeFileSync('io-package.json', JSON.stringify(iopackage, null, 4));
460
+ });
461
+
462
+ gulp.task('translateAndUpdateWordsJS', gulp.series('translate', 'adminLanguages2words', 'adminWords2languages'));
463
+
464
+ gulp.task('default', gulp.series('updatePackages', 'updateReadme'));
package/io-package.json CHANGED
@@ -1,9 +1,26 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "zigbee",
4
- "version": "1.8.0",
4
+ "version": "1.8.3",
5
5
  "news": {
6
- "1.8.0": {
6
+ "1.8.3": {
7
+ "en": "back to old source",
8
+ "de": "zurück zur alten quelle",
9
+ "ru": "назад к старому источнику",
10
+ "pt": "voltar para a velha fonte",
11
+ "nl": "terug naar de oude bron",
12
+ "fr": "retour à la vieille source",
13
+ "it": "torna alla vecchia fonte",
14
+ "es": "volver a la vieja fuente",
15
+ "pl": "tył",
16
+ "uk": "назад до старого джерела",
17
+ "zh-cn": "背 景"
18
+ },
19
+ "1.8.2": {
20
+ "en": "back to old source",
21
+ "de": "back to old source"
22
+ },
23
+ "1.8.1": {
7
24
  "en": "Packages updated\nAdded names of serial ports in configuration dialog",
8
25
  "de": "Pakete aktualisiert\nNamen der seriellen Ports im Konfigurationsdialog hinzugefügt",
9
26
  "ru": "Пакеты обновлены\nДобавлены имена серийных портов в диалоге конфигурации",
@@ -47,30 +64,6 @@
47
64
  "es": "faltan iconos con descripción múltiple",
48
65
  "pl": "brakujące ikony z wieloma opisami",
49
66
  "zh-cn": "缺少具有多个描述的图标"
50
- },
51
- "1.7.2": {
52
- "en": "corr download missing icon",
53
- "de": "korrektur download fehlendes Icon",
54
- "ru": "Корр скачать отсутствует значок",
55
- "pt": "corr baixar ícone ausente",
56
- "nl": "corr download ontbreekt icoon",
57
- "fr": "corr télécharger l'icône manquante",
58
- "it": "corr download icona mancante",
59
- "es": "falta el icono de descarga corr",
60
- "pl": "brak ikony pobierania corr",
61
- "zh-cn": "更正下载丢失的图标"
62
- },
63
- "1.7.1": {
64
- "en": "Device status is displayed in admin interface with color and icon.\nInternal adapter backups can be disabled (for backups with BackItUp Adapter).\nOnly last 10 backups are kept (currently they accumulate and need to be deleted manually).\nNew function for missing icons in Admin Object tree.",
65
- "de": "Der Gerätestatus wird in der Admin-Oberfläche mit Farbe und Symbol angezeigt.\nInterne Adapter-Backups können deaktiviert werden (für Backups mit BackItUp Adapter).\nNur die letzten 10 Backups werden aufbewahrt (derzeit sammeln sie sich an und müssen manuell gelöscht werden).\nNeue Funktion für fehlende Symbole im Admin-Objektbaum.",
66
- "ru": "Состояние устройства отображается в административном интерфейсе цветом и значком.\nВнутренние резервные копии адаптера могут быть отключены (для резервных копий с помощью BackItUp Adapter).\nХранятся только последние 10 резервных копий (в настоящее время они накапливаются и должны быть удалены вручную).\nНовая функция для отсутствуют значки в дереве объектов администрирования.",
67
- "pt": "O status do dispositivo é exibido na interface de administração com cor e ícone.\nOs backups do adaptador interno podem ser desabilitados (para backups com o BackItUp Adapter).\nSomente os últimos 10 backups são mantidos (atualmente eles se acumulam e precisam ser excluídos manualmente).\nNova função para ícones ausentes na árvore de objetos de administração.",
68
- "nl": "Apparaatstatus wordt weergegeven in de beheerdersinterface met kleur en pictogram.\nInterne adapterback-ups kunnen worden uitgeschakeld (voor back-ups met BackItUp Adapter).\nAlleen de laatste 10 back-ups worden bewaard (momenteel worden deze verzameld en moeten ze handmatig worden verwijderd).\nNieuwe functie voor ontbrekende pictogrammen in Admin Object-structuur.",
69
- "fr": "L'état de l'appareil est affiché dans l'interface d'administration avec une couleur et une icône.\nLes sauvegardes de l'adaptateur interne peuvent être désactivées (pour les sauvegardes avec l'adaptateur BackItUp).\nSeules les 10 dernières sauvegardes sont conservées (actuellement, elles s'accumulent et doivent être supprimées manuellement).\nNouvelle fonction pour icônes manquantes dans l'arborescence des objets d'administration.",
70
- "it": "Lo stato del dispositivo viene visualizzato nell'interfaccia di amministrazione con un colore e un'icona.\nI backup interni dell'adattatore possono essere disabilitati (per i backup con l'adattatore BackItUp).\nSono conservati solo gli ultimi 10 backup (attualmente si accumulano e devono essere eliminati manualmente).\nNuova funzione per icone mancanti nell'albero degli oggetti amministratore.",
71
- "es": "El estado del dispositivo se muestra en la interfaz de administración con color e icono.\nLas copias de seguridad del adaptador interno se pueden desactivar (para copias de seguridad con el adaptador BackItUp).\nSolo se conservan las últimas 10 copias de seguridad (actualmente se acumulan y deben eliminarse manualmente).\nNueva función para Faltan iconos en el árbol de objetos de administración.",
72
- "pl": "Stan urządzenia jest wyświetlany w interfejsie administratora za pomocą koloru i ikony.\nWewnętrzne kopie adaptera można wyłączyć (dla kopii zapasowych z BackItUp Adapter).\nZachowywanych jest tylko 10 ostatnich kopii (obecnie są one gromadzone i należy je usunąć ręcznie).\nNowa funkcja dla brakujące ikony w drzewie Admin Object.",
73
- "zh-cn": "设备状态以颜色和图标显示在管理界面中。\n可以禁用内部适配器备份(对于使用 BackItUp 适配器的备份)。\n仅保留最后 10 个备份(当前它们累积并需要手动删除)。\n新功能管理对象树中缺少图标。"
74
67
  }
75
68
  },
76
69
  "title": "Zigbee",