iobroker.esphome 0.3.2 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG_OLD.md CHANGED
@@ -1,4 +1,12 @@
1
1
  # Older changes
2
+ ## 0.2.4 (2021-08-24)
3
+ * (DutchmanNL) Version of ESPHome Dashboard updated to 2021.8
4
+ * (DutchmanNL) Add option if config of ESPHome device should be shown as states (default = FALSE, safes 8 states for each sensor)
5
+
6
+ ## 0.2.3 (2021-06-29)
7
+ * (Jey-Cee) Bugfix : Light component state not changed [#74](https://github.com/DrozmotiX/ioBroker.esphome/issues/74)
8
+ * (DutchmanNL) Update compatibility to version 1.19.4 of ESPHome Dashboard
9
+
2
10
  ## 0.2.2 (2021-06-24)
3
11
  * (DutchmanNL) [!!! Breaking !!!] Make YAML file persistent, backup your configuration before updating ! solves [#57](https://github.com/DrozmotiX/ioBroker.esphome/issues/57)
4
12
  * (DutchmanNL) Update ESPHome Dashboard to 1.18.0, requires Python >=3.7 (and ensure <4.0!)
package/README.md CHANGED
@@ -112,29 +112,31 @@ If you like my work, please consider a personal donation
112
112
  ### __WORK IN PROGRESS__
113
113
  * (DutchmanNL)
114
114
  -->
115
+ ### 0.4.1 (2023-11-05)
116
+ * (DutchmanNL) Bugfix: Password / connection issues in previous beta resolves #179
117
+ * (DutchmanNL) Bugfix: Allow individual API password or encryption keys for devices, resolves #174
118
+ * (DutchmanNL) Support ESPHome device Encryption Key (you should migrate from API password to Encryption Key ! resolves #152)
119
+
120
+ ### 0.4.0 (2023-11-03)
121
+ * (DutchmanNL) Added cleanup capability for unused channels & states after initialisation of device, resolves #39
122
+ * (DutchmanNL) Added button to info channel which allows to delete all offline devices from adapter tree. resolves #39
123
+ * (DutchmanNL) [Breaking] Backup strategy changed, requires [BackitUp v2.9.1](https://github.com/simatec/ioBroker.backitup) and activate option for ESPHome, fixes #129
124
+
115
125
  ### 0.3.2 (2023-11-01)
116
126
  * (DutchmanNL) Improved error handling if devices are not reachable/disconnected
117
- * (DutchmanNL) Bugfix: Allow control of brightness and color for light component, fixes #173
127
+ * (DutchmanNL) Bugfix: Allow control of brightness and color for light component, resolves #173
118
128
 
119
129
  ### 0.3.1 (2023-10-31)
120
- * (DutchmanNL) Bugfix: Show online state of ESP Device correctly, Fixes #106
130
+ * (DutchmanNL) Bugfix: Show online state of ESP Device correctly, resolves #106
121
131
 
122
132
  ### 0.3.0 (2023-10-31) - Bugfixes & Improvements
123
133
  * (Dutchman & SimonFischer04) Several Bugfixes
124
134
  * (SimonFischer04) Support type "select device"
125
135
  * (DutchmanNL) ESPHome dashboard default disabled
126
136
  * (SimonFischer04) Migrate to @2colors/esphome-native-api
127
- * (DutchmanNL) Automatically create needed directories, Fixes #168
137
+ * (DutchmanNL) Automatically create needed directories, resolves #168
128
138
  * (SimonFischer04) Migrate usage of python to new structure, should solve all ESPHome Dashboard related installation issues
129
139
 
130
- ### 0.2.4 (2021-08-24)
131
- * (DutchmanNL) Version of ESPHome Dashboard updated to 2021.8
132
- * (DutchmanNL) Add option if config of ESPHome device should be shown as states (default = FALSE, safes 8 states for each sensor)
133
-
134
- ### 0.2.3 (2021-06-29)
135
- * (Jey-Cee) Bugfix : Light component state not changed [#74](https://github.com/DrozmotiX/ioBroker.esphome/issues/74)
136
- * (DutchmanNL) Update compatibility to version 1.19.4 of ESPHome Dashboard
137
-
138
140
  ## License
139
141
  MIT License
140
142
 
@@ -88,15 +88,23 @@
88
88
  console.log('Return of all devices : ' + JSON.stringify(_devices));
89
89
 
90
90
  let text = '';
91
- text += `
92
- <div class="input-field ip col s4">
93
- <input placeholder="192.168.0.1" class="center" id="device-ip" type="text" value='${_devices.rows[index].value.native.ip}'/>
94
- <span class="translate">IP</span>
95
- </div>
96
- <div class="input-field pass col s4">
97
- <input class="center" id="device-pass" type="password" value='${decryptManuell(secret, _devices.rows[index].value.native.passWord)}'/>
98
- <span class="translate">device-pass</span>
99
- </div>`;
91
+ text += `
92
+
93
+ <p class="translate">You can either choose to use the Device Encryption Key (preferred) or API-Password (legacy)</p>
94
+ <p class="translate">When Encryption Key is provided, API-Password will be ignored</p>
95
+ <div class="input-field ip col s8">
96
+ <label for="device-ip"></label><input placeholder="192.168.0.100" class="center" id="device-ip" type="text"/>
97
+ <span class="translate">IP</span>
98
+ </div>
99
+ <div class="input-field pass col s8">
100
+ <label for="deviceEncryptionKey"></label><input class="center" id="deviceEncryptionKey" type="password"/>
101
+ <span class="translate">deviceEncryptionKey</span>
102
+ </div>
103
+ <div class="input-field pass col s8">
104
+ <label for="device-pass"></label><input class="center" id="device-pass" type="password"/>
105
+ <span class="translate">device-pass</span>
106
+ </div>`;
107
+
100
108
  $(`#edit_device_container`).html(text);
101
109
  });
102
110
  }
@@ -190,7 +198,8 @@
190
198
  console.log('Add device button executed');
191
199
  messageObj = {
192
200
  'device-ip': $('#device-ip').val(),
193
- 'device-pass': encryptManuell(secret, $('#device-pass').val())
201
+ 'device-pass': encryptManuell(secret, $('#device-pass').val()),
202
+ 'deviceEncryptionKey': encryptManuell(secret, $('#deviceEncryptionKey').val())
194
203
  };
195
204
  sendTo(null, 'addDevice', messageObj, (data) => {
196
205
  console.log('Message from backend : ' + data);
@@ -210,7 +219,8 @@
210
219
  messageObj = {
211
220
  'device-ip': $('#device-ip').val(),
212
221
  // 'device-pass': decryptManuell(secret, $('#device-pass').val())
213
- 'device-pass': encryptManuell(secret, $('#device-pass').val())
222
+ 'device-pass': encryptManuell(secret, $('#device-pass').val()),
223
+ 'deviceEncryptionKey': encryptManuell(secret, $('#deviceEncryptionKey').val())
214
224
  };
215
225
  sendTo(null, 'addDevice', messageObj, (data) => {
216
226
  console.log('Message from backend : ' + data);
@@ -305,31 +315,6 @@
305
315
  <input type="text" class="value" disabled="true" id="ESPHomeDashboardPort" />
306
316
  <label for="ESPHomeDashboardPort" class="translate">ESPHomeDashboardPort</label>
307
317
  </div>
308
-
309
- <!-- <div class="input-field col s4">-->
310
- <!-- <input placeholder="localhost" class="center" id="device-ip" type="text" id="ESPHomeDashboardEnabled" />-->
311
- <!-- <span class="translate">ESPHome Dashboard IP</span>-->
312
- <!-- </div>-->
313
- <!-- <div class="input-field col s4">-->
314
- <!-- <input type="number" class="value" id="ESPHomeDashboardPort" />-->
315
- <!-- <span class="translate">Port</span>-->
316
- <!-- </div>-->
317
- <!-- <div class="input-field col s2">-->
318
- <!-- <input type="checkbox" class="value" id="currentYearWeeks" />-->
319
- <!-- <span class="translate">Week</span>-->
320
- <!-- </div>-->
321
- <!-- <div class="input-field col s2">-->
322
- <!-- <input type="checkbox" class="value" id="currentYearMonth" />-->
323
- <!-- <span class="translate">Month</span>-->
324
- <!-- </div>-->
325
- <!-- <div class="input-field col s2">-->
326
- <!-- <input type="checkbox" class="value" id="currentYearQuarter" />-->
327
- <!-- <span class="translate">Quarter</span>-->
328
- <!-- </div>-->
329
- <!-- <div class="input-field col s2">-->
330
- <!-- <input type="checkbox" class="value" id="currentYearPrevious" />-->
331
- <!-- <span class="translate">Previous</span>-->
332
- <!-- </div>-->
333
318
  </div>
334
319
 
335
320
  <div class="row center">
@@ -407,11 +392,17 @@
407
392
  <div class="modal-content">
408
393
  <h5 class="translate center">Add device manually</h5>
409
394
  <div class="row center">
410
- <div class="input-field ip col s4">
411
- <label for="device-ip"></label><input placeholder="192.168.0.1" class="center" id="device-ip" type="text"/>
395
+ <p class="translate">You can either choose to use the Device Encryption Key (preferred) or API-Password (legacy)</p>
396
+ <p class="translate">When Encryption Key is provided, API-Password will be ignored</p>
397
+ <div class="input-field ip col s8">
398
+ <label for="device-ip"></label><input placeholder="192.168.0.100" class="center" id="device-ip" type="text"/>
412
399
  <span class="translate">IP</span>
413
400
  </div>
414
- <div class="input-field pass col s4">
401
+ <div class="input-field pass col s8">
402
+ <label for="deviceEncryptionKey"></label><input class="center" id="deviceEncryptionKey" type="password"/>
403
+ <span class="translate">deviceEncryptionKey</span>
404
+ </div>
405
+ <div class="input-field pass col s8">
415
406
  <label for="device-pass"></label><input class="center" id="device-pass" type="password"/>
416
407
  <span class="translate">device-pass</span>
417
408
  </div>
package/admin/words.js CHANGED
@@ -10,6 +10,8 @@ systemDictionary = {
10
10
  'Connected': { 'en': 'Connected', 'de': 'Verbunden', 'ru': 'Подключено', 'pt': 'Conectado', 'nl': 'Verbonden', 'fr': 'Lié', 'it': 'Collegato', 'es': 'Conectado', 'pl': 'Połączony', 'zh-cn': '连接的'},
11
11
  'Delete': { 'en': 'Delete', 'de': 'Löschen', 'ru': 'Удалить', 'pt': 'Excluir', 'nl': 'Verwijderen', 'fr': 'Effacer', 'it': 'Elimina', 'es': 'Borrar', 'pl': 'Usuń', 'zh-cn': '删除'},
12
12
  'Devices can be added manually or by autodiscovery': {'en': 'Devices can be added manually or by autodiscovery', 'de': 'Geräte können manuell oder durch die automatische Erkennung hinzugefügt werden', 'ru': 'Устройства можно добавлять вручную или с помощью автообнаружения', 'pt': 'Os dispositivos podem ser adicionados manualmente ou por autodiscovery', 'nl': 'Apparaten kunnen handmatig of door autodiscovery worden toegevoegd', 'fr': 'Les appareils peuvent être ajoutés manuellement ou par découverte automatique', 'it': 'I dispositivi possono essere aggiunti manualmente o tramite rilevamento automatico', 'es': 'Los dispositivos se pueden agregar manualmente o por descubrimiento automático', 'pl': 'Urządzenia można dodawać ręcznie lub przez automatyczne wykrywanie', 'zh-cn': '可以手动添加设备或通过自动发现添加设备'},
13
+ 'You can either choose to use the Device Encryption Key (preferred) or API-Password (legacy)': {'en': 'You can either choose to use the Device Encryption Key (preferred) or API-Password (legacy)', 'de': 'Sie können entweder den Geräteverschlüsselungsschlüssel (bevorzugt) oder das API-Passwort (alt) verwenden.', 'ru': 'Вы можете использовать либо ключ шифрования устройства (предпочтительно), либо пароль API (устаревший вариант).', 'pt': 'Você pode optar por usar a chave de criptografia do dispositivo (preferencial) ou a senha da API (legado)', 'nl': 'U kunt ervoor kiezen om de Device Encryption Key (bij voorkeur) of het API-wachtwoord (verouderd) te gebruiken', 'fr': "Vous pouvez choisir d'utiliser la clé de chiffrement de l'appareil (de préférence) ou le mot de passe API (ancien)", 'it': 'Puoi scegliere di utilizzare la chiave di crittografia del dispositivo (preferita) o la password API (legacy)', 'es': 'Puede optar por utilizar la clave de cifrado del dispositivo (preferida) o la contraseña API (heredada)', 'pl': 'Możesz wybrać użycie klucza szyfrowania urządzenia (preferowany) lub hasła API (starsza wersja)', 'zh-cn': '您可以选择使用设备加密密钥(首选)或 API 密码(旧版)'},
14
+ 'When Encryption Key is provided, API-Password will be ignored': {'en': 'When Encryption Key is provided, API-Password will be ignored', 'de': 'Wenn der Verschlüsselungsschlüssel bereitgestellt wird, wird das API-Passwort ignoriert', 'ru': 'Если предоставлен ключ шифрования, пароль API будет игнорироваться.', 'pt': 'Quando a chave de criptografia for fornecida, a senha da API será ignorada', 'nl': 'Wanneer de coderingssleutel wordt opgegeven, wordt het API-wachtwoord genegeerd', 'fr': 'Lorsque la clé de cryptage est fournie, le mot de passe API sera ignoré', 'it': 'Quando viene fornita la chiave di crittografia, la password API verrà ignorata', 'es': 'Cuando se proporciona la clave de cifrado, se ignorará la contraseña API', 'pl': 'Po podaniu klucza szyfrowania hasło API zostanie zignorowane', 'zh-cn': '当提供加密密钥时,API-密码将被忽略'},
13
15
  'ESPHomeDashboardEnabled': { 'en': 'Enable built-in ESPHome dashboard', 'de': 'Aktiviere das integrierte ESPHome-Dashboard', 'ru': 'Включить встроенный ESPHome Dashboard', 'pt': 'Ativar painel ESPHome integrado', 'nl': 'Schakel het ingebouwde ESPHome-dashboard in', 'fr': 'Activer le tableau de bord ESPHome intégré', 'it': 'Abilita il dashboard ESPHome integrato', 'es': 'Habilitar el panel de control ESPHome integrado', 'pl': 'Włącz wbudowany pulpit nawigacyjny ESPHome', 'zh-cn': '启用内置ESPHome仪表板'},
14
16
  'ESPHome_Dashboard_Generic_info': { 'en': 'Here you can enable / disable the ESPHome dashboard hosted by ioBroker. A separate installation of ESPHome is not required!', 'de': 'Hier wird das von ioBroker bereitgestellt ESPHome-Dashboard aktiviert / deaktiviert. Eine separate Installation von ESPHome ist nicht erforderlich!', 'ru': 'Здесь вы можете включить / отключить панель управления ESPHome, запущенную ioBroker-ом. Устанавливать дополнительно ESPhome не нужно!', 'pt': 'Aqui você pode ativar / desativar o painel ESPHome hospedado por ioBroker. ', 'nl': 'Hier kunt u het ESPHome-dashboard dat wordt gehost door ioBroker in- / uitschakelen. Een seperate installatie is niet noodzakelijk!', 'fr': 'Ici, vous pouvez activer / désactiver le tableau de bord ESPHome hébergé par ioBroker. ', 'it': 'Qui puoi abilitare / disabilitare la dashboard ESPHome ospitata da ioBroker. ', 'es': 'Aquí puede habilitar / deshabilitar el panel de ESPHome alojado por ioBroker. ', 'pl': 'Tutaj możesz włączyć / wyłączyć pulpit nawigacyjny ESPHome obsługiwany przez ioBroker. ', 'zh-cn': '您可以在此处启用/禁用ioBroker托管的ESPHome仪表板。'},
15
17
  'ESPHome_Dashboard_IP_info': { 'en': 'Please provide the IP address where the ESPHome dashboard is located (ioBroker or already existing instance).', 'de': 'Bitte die IP-Adresse angeben, unter der sich das ESPHome-Dashboard befindet (ioBroker oder bereits vorhandene ESPHome Instanz).', 'ru': 'Укажите IP-адрес, на котором расположена панель управления ESPHome (ioBroker или уже существующий экземпляр?)', 'pt': 'Forneça o endereço IP onde o ESPHome Dashboard está localizado (ioBroker ou instância já existente?)', 'nl': 'IP-adres op waar het ESPHome-dashboard zich bevindt (ioBroker of een reeds bestaande instantie?)', 'fr': "Veuillez fournir l'adresse IP où se trouve le tableau de bord ESPHome (ioBroker ou instance déjà existante?)", 'it': "Fornisci l'indirizzo IP in cui si trova ESPHome Dashboard (ioBroker o istanza già esistente?)", 'es': 'Proporcione la dirección IP donde se encuentra el panel de ESPHome (¿ioBroker o una instancia ya existente?)', 'pl': 'Podaj adres IP, pod którym znajduje się pulpit nawigacyjny ESPHome (ioBroker czy już istniejąca instancja?)', 'zh-cn': '请提供ESPHome仪表板所在的IP地址(ioBroker或已经存在的实例?)'},
@@ -20,6 +22,7 @@ systemDictionary = {
20
22
  'IP-Address': { 'en': 'IP Address', 'de': 'IP-Adresse', 'ru': 'IP адрес', 'pt': 'Endereço de IP', 'nl': 'IP adres', 'fr': 'Adresse IP', 'it': 'Indirizzo IP', 'es': 'Dirección IP', 'pl': 'Adres IP', 'zh-cn': 'IP地址'},
21
23
  'Name': { 'en': 'Name', 'de': 'Name', 'ru': 'Имя', 'pt': 'Nome', 'nl': 'Naam', 'fr': 'Nom', 'it': 'Nome', 'es': 'Nombre', 'pl': 'Nazwa', 'zh-cn': '姓名'},
22
24
  'Show configuration as states': { 'en': 'Show configuration as states', 'de': 'Konfiguration als Zustände anzeigen', 'ru': 'Показать конфигурацию как состояния', 'pt': 'Mostrar configuração como estados', 'nl': 'Configuratie weergeven als toestanden', 'fr': "Afficher la configuration sous forme d'états", 'it': 'Mostra configurazione come stati', 'es': 'Mostrar configuración como estados', 'pl': 'Pokaż konfigurację jako stany', 'zh-cn': '将配置显示为状态'},
23
- 'device-pass': { 'en': 'Password', 'de': 'Passwort', 'ru': 'Пароль', 'pt': 'Senha', 'nl': 'Wachtwoord', 'fr': 'Mot de passe', 'it': "Parola d'ordine", 'es': 'Contraseña', 'pl': 'Hasło', 'zh-cn': '密码'},
25
+ 'device-pass': { 'en': 'Device API Password (legacy)', 'de': 'Passwort', 'ru': 'Пароль', 'pt': 'Senha', 'nl': 'Wachtwoord', 'fr': 'Mot de passe', 'it': "Parola d'ordine", 'es': 'Contraseña', 'pl': 'Hasło', 'zh-cn': '密码'},
26
+ 'deviceEncryptionKey': { 'en': 'Device EncryptionKey (preferred)', 'de': 'Geräteverschlüsselungsschlüssel (bevorzugt)', 'ru': 'Ключ шифрования устройства (предпочтительно)', 'pt': 'Chave de criptografia do dispositivo (preferencial)', 'nl': 'Apparaatcoderingssleutel (bij voorkeur)', 'fr': "Clé de chiffrement de l'appareil (de préférence)", 'it': 'Chiave di crittografia del dispositivo (preferita)', 'es': 'Clave de cifrado del dispositivo (preferida)', 'pl': 'Klucz szyfrowania urządzenia (preferowany)', 'zh-cn': '设备加密密钥(首选)'},
24
27
  'reconnect interval': { 'en': 'Reconnect Interval', 'de': 'Intervall für erneutes Verbinden', 'ru': 'Интервал повторного соединения', 'pt': 'intervalo de reconexão', 'nl': 'interval voor opnieuw verbinden', 'fr': 'intervalle de reconnexion', 'it': "ricollegare l'intervallo", 'es': 'intervalo de reconexión', 'pl': 'interwał ponownego łączenia', 'zh-cn': '重新连接间隔'},
25
28
  };
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "esphome",
4
- "version": "0.3.2",
4
+ "version": "0.4.1",
5
5
  "news": {
6
+ "0.4.1": {
7
+ "en": "Bugfix: Password / connection issues in previous beta resolves #179\nBugfix: Allow individual API password or encryption keys for devices, resolves #174\nSupport ESPHome device Encryption Key (you should migrate from API password to Encryption Key ! resolves #152)",
8
+ "de": "Bugfix: Passwort / Verbindungsprobleme in vorherigen beta löst #179\nBugfix: Lassen Sie individuelle API-Passwort oder Verschlüsselungsschlüssel für Geräte, löst #174\nSupport ESPHome Gerät Verschlüsselungsschlüssel (Sie sollten von API-Passwort zu Verschlüsselungsschlüssel migrieren! löst #152)",
9
+ "ru": "Bugfix: Пароль / проблемы соединения в предыдущей бета-версии решает #179\nBugfix: Разрешить индивидуальные API-пароль или ключи шифрования для устройств, решает #174\nПоддержка ESPHome устройства Encryption Key (вы должны migrate от API-пароль для Encryption Key! решает #152)",
10
+ "pt": "Bugfix: Problemas de senha / conexão no beta anterior resolve #179\nBugfix: Permitir senha API individual ou chaves de criptografia para dispositivos, resolve #174\nSuporte ESPHome dispositivo Encryption Key (você deve migrar da senha API para Encryption Key! resolve #152)",
11
+ "nl": "Vertaling:\nBugfix: Sta individuele API wachtwoord of encryptie sleutels voor apparatuur, vaststellen\nSteun ESPHome apparatuur encryption Key (je moet migreren van API wachtwoord naar Encryption Key! Vertaling:",
12
+ "fr": "Bugfix: Mot de passe / problèmes de connexion dans la bêta précédente résout #179\nBugfix: Permettre un mot de passe ou des clés de chiffrement de l'API pour les appareils, résout #174\nSupport ESPHome périphérique Clé de chiffrement (vous devriez migrer de l'API mot de passe à la clé de chiffrement ! résout #152)",
13
+ "it": "Bugfix: Password / problemi di connessione nella precedente beta risolve #179\nBugfix: Consentire singole password API o chiavi di crittografia per i dispositivi, risolve #174\nSupporto dispositivo ESPHome chiave di crittografia (si dovrebbe migrare da password API a chiave di crittografia! risolve #152)",
14
+ "es": "Bugfix: Problemas de contraseña / conexión en beta anterior resuelve #179\nBugfix: Permitir contraseña o claves de cifrado individuales de API para dispositivos, resuelve #174\nSoporte ESPHome dispositivo clave de cifrado (debe migrar desde contraseña de API a clave de cifrado! resuelve #152)",
15
+ "pl": "Bugfix: Password / Problemy połączenia w poprzednich beta rozwiązały numer #179\nBugfix: Allow indywidualny API password lub klucze szyfrujące dla urządzeń, rozwiązał numer #174\nWsparcie ESPHome: Encryption Key (you powinien wyemigrować z hasła API do Encryption Key ! Rozwiązanie #152",
16
+ "uk": "Помилки: Пароль / питання з'єднання у попередніх бета-рішеннях #179\nПомилки: Дозволити індивідуальний пароль API або ключі шифрування для пристроїв, вирішує #174\nПідтримка ESPHome пристрій шифрування ключа (Ви повинні мигратувати від API пароля до ключа шифрування ! розчини #152)",
17
+ "zh-cn": "Bugfix:在前几批被盗用词/联系问题\nBugfix: 允许个人的通用或加密装置的关键,第174号行动\n支持ESPHome装置的加密关键(you应从API密码移到恩加密的关键! 第152号决议)"
18
+ },
19
+ "0.4.0": {
20
+ "en": "Added cleanup capability for unused channels & states after initialisation of device, resolves #39\nAdded button to info channel which allows to delete all offline devices from adapter tree. resolves #39\n[Breaking] Backup strategy changed, requires [BackitUp v2.9.1](https://github.com/simatec/ioBroker.backitup) and activate option for ESPHome, fixes #129",
21
+ "de": "Zusätzliche Reinigungsfähigkeit für ungenutzte Kanäle & Zustände nach der Initialisierung des Geräts, löst #39\nZusätzlicher Button zum Info-Kanal, der alle Offline-Geräte aus dem Adapterbaum löschen kann. löst #39\n[Breaking] Backup-Strategie geändert, erfordert [BackitUp v2.9.1](https://github.com/simatec/ioBroker.backitup) und aktiviert Option für ESPHome, repariert #129",
22
+ "ru": "Добавлена возможность очистки неиспользуемых каналов и государств после инициализации устройства, решает #39\nДобавлена кнопка к информационному каналу, что позволяет удалить все оффлайн устройства из дерева адаптера. решает #39\n[Breaking] Стратегия резервного копирования изменена, требует [BackitUp v2.9.1](https://github.com/simatec/ioBroker.backitup) и активировать вариант для ESPHome, исправлений #129",
23
+ "pt": "Adicionado capacidade de limpeza para canais não utilizados & estados após a inicialização do dispositivo, resolve #39\nAdicionado botão para info canal que permite excluir todos os dispositivos off-line da árvore do adaptador. resolve #39\n[Breaking] Estratégia de backup alterada, requer [BackitUp v2.9.1](https://github.com/simatec/ioBroker.backitup) e opção ativa para ESPHome, correções #129",
24
+ "nl": "Vervoegde schoonmaak capaciteit voor onbruikbare kanalen apparaat na de initialisatie van 39\nKnop toegevoegd aan info kanaal dat alle offline apparaten van adapter boom laat verwijderen. 39\nOndersteuningsstrategie veranderde, vereist [Backitup v2.9.1, htps:com/simateioBroker, en tactiveer de optie van 12 uur",
25
+ "fr": "Ajout d'une capacité de nettoyage pour les canaux non utilisés & états après l'initialisation du dispositif, résout #39\nAjout du bouton sur le canal d'info qui permet de supprimer tous les périphériques hors ligne de l'arbre d'adaptateur. résolution #39\n[Breaking] La stratégie de sauvegarde a changé, nécessite [BackitUp v2.9.1](https://github.com/simatec/ioBroker.backitup) et l'option active pour ESPHome, corrige #129",
26
+ "it": "Aggiunta funzionalità di pulizia per canali e stati non utilizzati dopo l'inizializzazione del dispositivo, risolve #39\nPulsante aggiunto al canale info che consente di eliminare tutti i dispositivi offline dall'albero dell'adattatore. risolve #39\n[Breaking] La strategia di backup è cambiata, richiede [BackitUp v2.9.1](https://github.com/simatec/ioBroker.backitup) e attiva l'opzione per ESPHome, corregge #129",
27
+ "es": "Capacidad de limpieza agregada para canales no utilizados \" estados después de la inicialización del dispositivo, resuelve #39\nSe agregó botón a canal de información que permite eliminar todos los dispositivos fuera de línea del árbol del adaptador. resuelve #39\n[Breaking] La estrategia de respaldo cambió, requiere [BackitUp v2.9.1](https://github.com/simatec/ioBroker.backitup) y activar la opción para ESPHome, fija #129",
28
+ "pl": "Po zainicjowaniu możliwości usuwania nieużywanych kanałów i stanów po inicjalizacji urządzenia, rozwiązał #39\nNaciśnięty przycisk do kanału informacyjnego, który umożliwia usunięcie wszystkich urządzeń offline z drzewa adapterowego. Rozwiązanie #39\n(Po zmianie strategii Backupa, wymaga od BOATUTUp v2.9.1(https:/github.com/simatec/ioBroker.backitup) i aktywacji opcji dla ESPHome, naprawa #129",
29
+ "uk": "Додано можливість очищення каналів і станів після ініціалізації пристрою, вирішує #39\nДодано кнопку на інформаційний канал, який дозволяє видалити всі offline пристрої з дерева адаптера. розчини #39\n[Breaking] Стратегія резервного копіювання змінено, вимагає [BackitUp v2.9.1](https://github.com/simatec/ioBroker.backitup) і активувати варіант для ESPHome, фіксує #129",
30
+ "zh-cn": "在装置初始化后增加无用渠道和各州的清洁能力,第39号决定\n添加但道托顿以电器,使所有线装置能够从适应树中删除。 行动39\n[Breaking] Backup战略改变,要求[BackitUp v2.9.1](http://github.com/simatec/ioBroker.backitup)和“ESPHome”的主动选择,第129号行动。"
31
+ },
6
32
  "0.3.2": {
7
33
  "en": "Improved error handling if devices are not reachable/disconnected\nBugfix: Allow control of brightness and color for light component, fixes #173",
8
34
  "de": "Verbesserte Fehlerbehandlung, wenn Geräte nicht erreichbar/vernetzt sind\nBugfix: Erlauben Sie die Kontrolle von Helligkeit und Farbe für die Lichtkomponente, fixiert #173",
@@ -65,30 +91,6 @@
65
91
  "es": "Corrección de error: el estado del componente de luz no ha cambiado [# 74] (https://github.com/DrozmotiX/ioBroker.esphome/issues/74)\nActualizar la compatibilidad a la versión 1.19.4 de ESPHome Dashboard",
66
92
  "pl": "Poprawka błędu: stan lekkiego komponentu nie uległ zmianie [#74](https://github.com/DrozmotiX/ioBroker.esphome/issues/74)\nZaktualizuj kompatybilność do wersji 1.19.4 ESPHome Dashboard",
67
93
  "zh-cn": "修正:轻组件状态没有改变 [#74](https://github.com/DrozmotiX/ioBroker.esphome/issues/74)\n更新与 ESPHome Dashboard 1.19.4 版的兼容性"
68
- },
69
- "0.2.2": {
70
- "en": "[!!! Breaking !!!] Make YAML file persistent, backup your configuration before updating ! solves [#57](https://github.com/DrozmotiX/ioBroker.esphome/issues/57)\nUpdate ESPHome Dashboard to 1.18.0, requires Python >=3.7 (and ensure <4.0!)\nBugfix : Reconnect to devices without autodiscovery / MDNS-Broadcast in network, solves [#66](https://github.com/DrozmotiX/ioBroker.esphome/issues/66)",
71
- "de": "[!!! Breaking !!!] Machen Sie die YAML-Datei persistent, sichern Sie Ihre Konfiguration vor dem Update! löst [#57](https://github.com/DrozmotiX/ioBroker.esphome/issues/57)\nAktualisieren Sie das ESPHome-Dashboard auf 1.18.0, erfordert Python >=3.7 (und stellen Sie sicher <4.0!)\nBugfix : Reconnect zu Geräten ohne Autodiscovery / MDNS-Broadcast im Netzwerk, behebt [#66](https://github.com/DrozmotiX/ioBroker.esphome/issues/66)",
72
- "ru": "[!!! Ломаю !!!] Сделайте файл YAML постоянным, сделайте резервную копию конфигурации перед обновлением! решает [# 57] (https://github.com/DrozmotiX/ioBroker.esphome/issues/57)\nОбновите ESPHome Dashboard до 1.18.0, требуется Python> = 3.7 (и убедитесь, что <4.0!)\nИсправление: повторное подключение к устройствам без автообнаружения / MDNS-трансляции в сети, решает [# 66] (https://github.com/DrozmotiX/ioBroker.esphome/issues/66)",
73
- "pt": "[!!! Quebrando !!!] Torne o arquivo YAML persistente, faça backup de sua configuração antes de atualizar! resolve [# 57] (https://github.com/DrozmotiX/ioBroker.esphome/issues/57)\nAtualize o painel ESPHome para 1.18.0, requer Python> = 3.7 (e garanta <4.0!)\nCorreção de bug: reconectar a dispositivos sem autodiscovery / MDNS-Broadcast na rede, resolve [# 66] (https://github.com/DrozmotiX/ioBroker.esphome/issues/66)",
74
- "nl": "[!!! Breaking !!!] Maak het YAML-bestand persistent, maak een back-up van uw configuratie voordat u deze bijwerkt! lost [#57](https://github.com/DrozmotiX/ioBroker.esphome/issues/57) op\nUpdate ESPHome Dashboard naar 1.18.0, vereist Python >=3.7 (en zorg voor <4.0!)\nBugfix: opnieuw verbinding maken met apparaten zonder autodiscovery / MDNS-uitzending in netwerk, lost [#66](https://github.com/DrozmotiX/ioBroker.esphome/issues/66) op",
75
- "fr": "[!!! Breaking !!!] Rendez le fichier YAML persistant, sauvegardez votre configuration avant de mettre à jour ! résout [#57](https://github.com/DrozmotiX/ioBroker.esphome/issues/57)\nMettez à jour ESPHome Dashboard vers 1.18.0, nécessite Python >=3.7 (et assurez-vous <4.0 !)\nCorrection de bug : se reconnecter aux appareils sans découverte automatique / MDNS-Broadcast en réseau, résout [#66](https://github.com/DrozmotiX/ioBroker.esphome/issues/66)",
76
- "it": "[!!! Breaking !!!] Rendi persistente il file YAML, esegui il backup della configurazione prima di aggiornare! risolve [#57](https://github.com/DrozmotiX/ioBroker.esphome/issues/57)\nAggiorna ESPHome Dashboard a 1.18.0, richiede Python >=3.7 (e assicurati <4.0!)\nBugfix: riconnettersi ai dispositivi senza autodiscovery / MDNS-Broadcast in rete, risolve [#66] (https://github.com/DrozmotiX/ioBroker.esphome/issues/66)",
77
- "es": "[!!! ¡¡¡Rompiendo !!!] ¡Haga que el archivo YAML sea persistente, haga una copia de seguridad de su configuración antes de actualizar! resuelve [# 57] (https://github.com/DrozmotiX/ioBroker.esphome/issues/57)\nActualice ESPHome Dashboard a 1.18.0, requiere Python> = 3.7 (¡y asegúrese de <4.0!)\nCorrección de error: vuelva a conectarse a dispositivos sin detección automática / MDNS-Broadcast en la red, resuelve [# 66] (https://github.com/DrozmotiX/ioBroker.esphome/issues/66)",
78
- "pl": "[!!! Breaking !!!] Utrwal plik YAML, wykonaj kopię zapasową konfiguracji przed aktualizacją! rozwiązuje [#57](https://github.com/DrozmotiX/ioBroker.esphome/issues/57)\nZaktualizuj pulpit nawigacyjny ESPHome do wersji 1.18.0, wymaga Pythona >=3.7 (i upewnij się, że <4.0!)\nBugfix: Połącz się ponownie z urządzeniami bez automatycznego wykrywania / MDNS-Broadcast w sieci, rozwiązuje [#66](https://github.com/DrozmotiX/ioBroker.esphome/issues/66)",
79
- "zh-cn": "[!!!打破 !!!] 使 YAML 文件持久化,更新前备份您的配置!解决 [#57](https://github.com/DrozmotiX/ioBroker.esphome/issues/57)\n将 ESPHome Dashboard 更新到 1.18.0,需要 Python >=3.7(并确保 <4.0!)\n修正:在网络中没有自动发现/MDNS-Broadcast 的情况下重新连接到设备,解决[#66](https://github.com/DrozmotiX/ioBroker.esphome/issues/66)"
80
- },
81
- "0.2.1-1": {
82
- "en": "add cover component\nadd transitionLength for lights",
83
- "de": "Abdeckungskomponente hinzufügen\nÜbergangslänge für Lichter hinzufügen",
84
- "ru": "добавить компонент обложки\nдобавить transitionLength для огней",
85
- "pt": "adicionar componente de capa\nadicionar comprimento de transição para luzes",
86
- "nl": "voeg een omslagcomponent toe\nvoeg transitionLength toe voor lichten",
87
- "fr": "ajouter un composant de couverture\najouter une longueur de transition pour les lumières",
88
- "it": "aggiungi il componente di copertina\naggiungi TransitionLength per le luci",
89
- "es": "agregar componente de portada\nagregar transiciónLength para luces",
90
- "pl": "dodaj komponent okładki\ndodaj długość przejścia dla świateł",
91
- "zh-cn": "添加封面组件\n为灯光添加transitionLength"
92
94
  }
93
95
  },
94
96
  "title": "Control ESPHome managed devices",
@@ -145,7 +147,6 @@
145
147
  "dsn": "https://52227cfa250b49e99b1f28408273402c@sentry.iobroker.net/122"
146
148
  }
147
149
  },
148
- "dataFolder": "esphome.%INSTANCE%",
149
150
  "localLink": "http://%ESPHomeDashboardIP%:%ESPHomeDashboardPort%",
150
151
  "adminTab": {
151
152
  "link": "http://%ESPHomeDashboardIP%:%ESPHomeDashboardPort%",
package/main.js CHANGED
@@ -14,8 +14,10 @@ const stateAttr = require(__dirname + '/lib/stateAttr.js'); // Load attribute li
14
14
  const disableSentry = false; // Ensure to set to true during development!
15
15
  const warnMessages = {}; // Store warn messages to avoid multiple sending to sentry
16
16
  const fs = require('fs');
17
+ const {clearTimeout} = require('timers');
17
18
  const client = {};
18
- let reconnectTimer, reconnectInterval, apiPass, autodiscovery, dashboardProcess, createConfigStates;
19
+ const resetTimers = {}; // Memory allocation for all running timers
20
+ let reconnectInterval, apiPass, autodiscovery, dashboardProcess, createConfigStates;
19
21
 
20
22
  class Esphome extends utils.Adapter {
21
23
 
@@ -58,7 +60,11 @@ class Esphome extends utils.Adapter {
58
60
 
59
61
  // Start MDNS discovery when enabled
60
62
  if (autodiscovery) {
61
- this.deviceDiscovery(); // Start MDNS autodiscovery
63
+ if (resetTimers['autodiscovery']) resetTimers['autodiscovery'] = clearTimeout(resetTimers['autodiscovery']);
64
+ this.log.info(`Adapter ready, automatic Device Discovery will be acticated in 30 seconds.`);
65
+ resetTimers['autodiscovery'] = setTimeout(async () => {
66
+ this.deviceDiscovery(); // Start MDNS autodiscovery
67
+ }, (30000));
62
68
  } else {
63
69
  this.log.warn(`Auto Discovery disabled, new devices (or IP changes) will NOT be detected automatically!`);
64
70
  }
@@ -70,6 +76,21 @@ class Esphome extends utils.Adapter {
70
76
  this.log.info(`Native Integration of ESPHome Dashboard disabled `);
71
77
  }
72
78
 
79
+ // Create & Subscribe to button handling offline Device cleanup
80
+ this.extendObjectAsync('esphome.0.info.deviceCleanup',
81
+ {
82
+ 'type': 'state',
83
+ 'common': {
84
+ 'role': 'button',
85
+ 'name': 'Device or service connected',
86
+ 'type': 'boolean',
87
+ 'read': false,
88
+ 'write': true,
89
+ 'def': false
90
+ }
91
+ });
92
+ this.subscribeStates('esphome.0.info.deviceCleanup');
93
+
73
94
  } catch (e) {
74
95
  this.log.error(`[Adapter start] Fatal error occurred ${e}`);
75
96
  }
@@ -154,7 +175,7 @@ class Esphome extends utils.Adapter {
154
175
 
155
176
  // Get basic data of known devices and start reading data
156
177
  for (const i in knownDevices) {
157
- this.connectDevices(knownDevices[i].native.ip, this.decrypt(knownDevices[i].native.passWord));
178
+ this.connectDevices(knownDevices[i].native.ip, knownDevices[i].native.passWord, knownDevices[i].native.encryptionKey ? knownDevices[i].native.encryptionKey : '');
158
179
  }
159
180
  } catch (e) {
160
181
  this.sendSentry(`[tryKnownDevices] ${e}`);
@@ -165,7 +186,7 @@ class Esphome extends utils.Adapter {
165
186
  deviceDiscovery() {
166
187
  try {
167
188
 
168
- this.log.info(`Auto Discovery startet, new devices (or IP changes) will be detected automatically`);
189
+ this.log.info(`Automatic device Discovery started, new devices (or IP changes) will be detected automatically`);
169
190
  discovery = new Discovery();
170
191
 
171
192
  discovery.on('info', async (message) => {
@@ -173,7 +194,13 @@ class Esphome extends utils.Adapter {
173
194
  this.log.debug(`Discovery message ${JSON.stringify(message)}`);
174
195
  if (this.deviceInfo[message.address] == null) {
175
196
  this.log.info(`[AutoDiscovery] New ESPHome device found at IP ${message.address}, trying to initialize`);
176
- this.connectDevices(`${message.address}`, `${apiPass}`);
197
+ //ToDo: Add default Encryption Key
198
+ // Only run autodiscovery if device is unknown yet
199
+ if (!this.deviceInfo[message.address]
200
+ && this.deviceInfo[message.address].connectError === false
201
+ && !this.deviceInfo[message.address] && this.deviceInfo[message.address].connecting) {
202
+ this.connectDevices(`${message.address}`, apiPass, '');
203
+ }
177
204
  }
178
205
  } catch (e) {
179
206
  this.log.error(`[deviceDiscovery handler] ${e}`);
@@ -188,42 +215,80 @@ class Esphome extends utils.Adapter {
188
215
  /**
189
216
  * Handle Socket connections
190
217
  * @param {string} host IP adress of device
191
- * @param {string | number | boolean} pass Password used for APU
218
+ * @param {string} deviceApiPass Native API credentials
219
+ * @param {string} deviceEncryptionKey Encryption Key credentials
192
220
  */
193
- connectDevices(host, pass) {
221
+ connectDevices(host, deviceApiPass, deviceEncryptionKey) {
194
222
  try {
195
223
  // const host = espDevices[device].ip;
196
224
  this.log.info(`Try to connect to ${host}`);
197
- // Prepare connection attributes
198
- client[host] = new Client({
199
- host: host,
200
- password: apiPass,
201
- clientInfo: `${this.host}`,
202
- clearSession: true,
203
- initializeDeviceInfo: true,
204
- initializeListEntities: true,
205
- initializeSubscribeStates: false,
206
- // initializeSubscribeLogs: false, //ToDo: Make configurable by adapter settings
207
- reconnect: true,
208
- reconnectInterval: reconnectInterval,
209
- pingInterval: 15000, //ToDo: Make configurable by adapter settings
210
- pingAttempts: 3
211
- // port: espDevices[device].port //ToDo: Make configurable by adapter settings
212
- });
225
+
226
+ // Cancel process if connection try is already in progress
227
+ if (this.deviceInfo[host] && this.deviceInfo[host].connecting) return;
228
+
229
+ // Clear any existing memory information for this device
230
+ delete this.deviceInfo[host];
231
+
232
+ // Reserve basic memory information for this device
233
+ this.deviceInfo[host] = {
234
+ connected : false,
235
+ connecting : true,
236
+ connectionError : false,
237
+ initialized: false,
238
+ ip : host
239
+ };
240
+
241
+ if (!deviceEncryptionKey || deviceEncryptionKey === '') {
242
+ client[host] = new Client({
243
+ host: host,
244
+ clientInfo: `${this.host}`,
245
+ clearSession: true,
246
+ initializeDeviceInfo: true,
247
+ initializeListEntities: true,
248
+ initializeSubscribeStates: false,
249
+ // initializeSubscribeLogs: false, //ToDo: Make configurable by adapter settings
250
+ reconnect: true,
251
+ reconnectInterval: reconnectInterval,
252
+ pingInterval: 15000, //ToDo: Make configurable by adapter settings
253
+ pingAttempts: 3,
254
+ password : this.decrypt(deviceApiPass)
255
+ // port: espDevices[device].port //ToDo: Make configurable by adapter settings
256
+ });
257
+ } else {
258
+ client[host] = new Client({
259
+ host: host,
260
+ clientInfo: `${this.host}`,
261
+ clearSession: true,
262
+ initializeDeviceInfo: true,
263
+ initializeListEntities: true,
264
+ initializeSubscribeStates: false,
265
+ // initializeSubscribeLogs: false, //ToDo: Make configurable by adapter settings
266
+ reconnect: true,
267
+ reconnectInterval: reconnectInterval,
268
+ pingInterval: 15000, //ToDo: Make configurable by adapter settings
269
+ pingAttempts: 3,
270
+ encryptionKey: this.decrypt(deviceEncryptionKey)
271
+ // port: espDevices[device].port //ToDo: Make configurable by adapter settings
272
+ });
273
+ }
213
274
 
214
275
  // Connection listener
215
276
  client[host].on('connected', async () => {
216
277
  try {
217
278
  // Clear any existing memory information for this device
218
279
  delete this.deviceInfo[host];
219
-
220
- // Reserve basic memory information for this device
221
- this.deviceInfo[host] = {
222
- connected : true,
223
- connectionError : false,
224
- initialized: false,
225
- ip : host
226
- };
280
+ if (!this.deviceInfo[host]) {
281
+ this.deviceInfo[host] = {
282
+ connected : false,
283
+ connecting : true,
284
+ connectionError : false,
285
+ initialized: false,
286
+ ip : host
287
+ };
288
+ } else {
289
+ this.deviceInfo[host].connected = true;
290
+ this.deviceInfo[host].connecting = false;
291
+ }
227
292
  this.log.info(`ESPHome client ${host} connected`);
228
293
  // Clear possible present warn messages for device from previous connection
229
294
  delete warnMessages[host];
@@ -232,10 +297,18 @@ class Esphome extends utils.Adapter {
232
297
  }
233
298
  });
234
299
 
235
- client[host].on('disconnected', () => {
300
+ client[host].on('disconnected', async () => {
236
301
  try {
237
302
  if (this.deviceInfo[host].deviceName != null) {
238
- this.setState(`${this.deviceInfo[host].deviceName}.info._online`, {val: false, ack: true});
303
+ await this.stateSetCreate(`${this.deviceInfo[host].deviceName}.info._online`, `Online state`, false);
304
+
305
+ // Check all created states in memory if their are related to this device
306
+ for (const state in this.createdStatesDetails) {
307
+ // Remove states from cache
308
+ if (state.split('.')[0] === this.deviceInfo[host].deviceName) {
309
+ delete this.createdStatesDetails[state];
310
+ }
311
+ }
239
312
 
240
313
  // Cache relevant data before clearing memory space of device
241
314
  const cacheDeviceInformation = {
@@ -249,6 +322,7 @@ class Esphome extends utils.Adapter {
249
322
  // Reserve basic memory information for this device
250
323
  this.deviceInfo[host] = {
251
324
  connected : false,
325
+ connecting: false,
252
326
  connectionError : true,
253
327
  deviceName : cacheDeviceInformation.deviceName,
254
328
  deviceInfoName : cacheDeviceInformation.deviceInfoName,
@@ -266,6 +340,12 @@ class Esphome extends utils.Adapter {
266
340
  client[host].on('initialized', () => {
267
341
  this.log.info(`ESPHome client ${this.deviceInfo[host].deviceInfoName} on ip ${host} initialized`);
268
342
  this.deviceInfo[host].initialized = true;
343
+
344
+ // Start timer to cleanup unneeded objects
345
+ if (resetTimers[host]) resetTimers[host] = clearTimeout(resetTimers[host]);
346
+ resetTimers[host] = setTimeout(async () => {
347
+ await this.objectCleanup(host);
348
+ }, (10000));
269
349
  });
270
350
 
271
351
  // Log message listener
@@ -286,12 +366,18 @@ class Esphome extends utils.Adapter {
286
366
  // Store device information into memory
287
367
  const deviceName = this.replaceAll(deviceInfo.macAddress, `:`, ``);
288
368
  this.deviceInfo[host] = {
369
+ adapterObjects : {
370
+ channels : []
371
+ },
289
372
  ip: host,
373
+ connectError : false,
374
+ connected : true,
290
375
  mac: deviceInfo.macAddress,
291
376
  deviceInfo: deviceInfo,
292
377
  deviceName: deviceName,
293
378
  deviceInfoName: deviceInfo.name,
294
- passWord: apiPass,
379
+ passWord: deviceApiPass,
380
+ encryptionKey: deviceEncryptionKey,
295
381
  };
296
382
 
297
383
  // Store MAC & IP relation, delete possible existing entry before
@@ -314,7 +400,8 @@ class Esphome extends utils.Adapter {
314
400
  name: this.deviceInfo[host].deviceInfoName,
315
401
  mac: deviceInfo.macAddress,
316
402
  deviceName: deviceName,
317
- passWord: this.encrypt(apiPass),
403
+ passWord: deviceApiPass,
404
+ encryptionKey: deviceEncryptionKey
318
405
  },
319
406
  });
320
407
 
@@ -368,6 +455,11 @@ class Esphome extends utils.Adapter {
368
455
  native: {},
369
456
  });
370
457
 
458
+ // Cache created channel in device memory
459
+ if (!this.deviceInfo[host].adapterObjects.channels.includes(`${this.namespace}.${this.deviceInfo[host].deviceName}.${entity.type}`)) {
460
+ this.deviceInfo[host].adapterObjects.channels.push(`${this.namespace}.${this.deviceInfo[host].deviceName}.${entity.type}`);
461
+ }
462
+
371
463
  // Create state specific channel by id
372
464
  await this.extendObjectAsync(`${this.deviceInfo[host].deviceName}.${entity.type}.${entity.id}`, {
373
465
  type: 'channel',
@@ -377,6 +469,11 @@ class Esphome extends utils.Adapter {
377
469
  native: {},
378
470
  });
379
471
 
472
+ // Cache created channel in device memory
473
+ if (!this.deviceInfo[host].adapterObjects.channels.includes(`${this.namespace}.${this.deviceInfo[host].deviceName}.${entity.type}.${entity.id}`)) {
474
+ this.deviceInfo[host].adapterObjects.channels.push(`${this.namespace}.${this.deviceInfo[host].deviceName}.${entity.type}.${entity.id}`);
475
+ }
476
+
380
477
  //Check if config channel should be created
381
478
  if (!createConfigStates) {
382
479
  // Delete folder structure if already present
@@ -397,6 +494,12 @@ class Esphome extends utils.Adapter {
397
494
  },
398
495
  native: {},
399
496
  });
497
+
498
+ // Cache created channel in device memory
499
+ if (!this.deviceInfo[host].adapterObjects.channels.includes(`${this.namespace}.${this.deviceInfo[host].deviceName}.${entity.type}.${entity.id}.config`)) {
500
+ this.deviceInfo[host].adapterObjects.channels.push(`${this.namespace}.${this.deviceInfo[host].deviceName}.${entity.type}.${entity.id}.config`);
501
+ }
502
+
400
503
  // Handle Entity JSON structure and write related config channel data
401
504
  await this.TraverseJson(entity.config, `${this.deviceInfo[host].deviceName}.${entity.type}.${entity.id}.config`);
402
505
  }
@@ -492,13 +595,13 @@ class Esphome extends utils.Adapter {
492
595
 
493
596
 
494
597
  } catch (e) {
495
- this.log.error(`Connection issue for ${entity.name} ${e}`);
598
+ this.log.error(`Connection issue for ${entity.name} ${e} | ${e.stack}`);
496
599
  }
497
600
 
498
601
  });
499
602
 
500
603
  // Connection data handler
501
- client[host].on('error', (error) => {
604
+ client[host].on('error', async (error) => {
502
605
  try {
503
606
 
504
607
  // Reserve memory space for connection error if not already exist
@@ -517,24 +620,42 @@ class Esphome extends utils.Adapter {
517
620
  if (!this.deviceInfo[host].connectError) {
518
621
  this.log.error(optimisedError);
519
622
  this.deviceInfo[host].connectError = true;
623
+ await this.stateSetCreate(`${this.deviceInfo[host].deviceName}.info._online`, `Online state`, false);
624
+ }
625
+ } else if (error.message.includes('EHOSTUNREACH')) {
626
+ optimisedError = `Client ${host} unreachable !`;
627
+ if (!this.deviceInfo[host].connectError) {
628
+ this.log.error(optimisedError);
629
+ this.deviceInfo[host].connectError = true;
630
+ await this.stateSetCreate(`${this.deviceInfo[host].deviceName}.info._online`, `Online state`, false);
520
631
  }
521
632
  } else if (error.message.includes('Invalid password')) {
522
633
  optimisedError = `Client ${host} incorrect password !`;
523
634
  if (!this.deviceInfo[host].connectError) {
524
635
  this.log.error(optimisedError);
525
636
  this.deviceInfo[host].connectError = true;
637
+ await this.stateSetCreate(`${this.deviceInfo[host].deviceName}.info._online`, `Online state`, false);
638
+ }
639
+ } else if (error.message.includes('Encryption expected')) {
640
+ optimisedError = `Client ${host} requires encryption key which has not been provided, please enter encryption key in adapter settings for this device !`;
641
+ if (!this.deviceInfo[host].connectError) {
642
+ this.log.error(optimisedError);
643
+ this.deviceInfo[host].connectError = true;
644
+ await this.stateSetCreate(`${this.deviceInfo[host].deviceName}.info._online`, `Online state`, false);
526
645
  }
527
646
  } else if (error.message.includes('ECONNRESET')) {
528
647
  optimisedError = `Client ${host} Connection Lost, will reconnect automatically when device is available!`;
529
648
  if (!this.deviceInfo[host].connectError) {
530
649
  this.log.warn(optimisedError);
531
650
  this.deviceInfo[host].connectError = true;
651
+ await this.stateSetCreate(`${this.deviceInfo[host].deviceName}.info._online`, `Online state`, false);
532
652
  }
533
653
  } else if (error.message.includes('timeout')) {
534
654
  optimisedError = `Client ${host} Timeout, will reconnect automatically when device is available!`;
535
655
  if (!this.deviceInfo[host].connectError) {
536
656
  this.log.warn(optimisedError);
537
657
  this.deviceInfo[host].connectError = true;
658
+ await this.stateSetCreate(`${this.deviceInfo[host].deviceName}.info._online`, `Online state`, false);
538
659
  }
539
660
  } else if (error.message.includes('ECONNREFUSED')) {
540
661
  optimisedError = `Client ${host} not yet ready to connect, will try again!`;
@@ -573,7 +694,7 @@ class Esphome extends utils.Adapter {
573
694
  }
574
695
 
575
696
  } catch (e) {
576
- this.log.error(`ESP device error for ${host}`);
697
+ this.log.error(`ESP device error for ${host} | ${e} | ${e.stack}`);
577
698
  }
578
699
  }
579
700
 
@@ -672,6 +793,8 @@ class Esphome extends utils.Adapter {
672
793
  // Check if state contains value
673
794
  if (transitionLength) {
674
795
  this.deviceInfo[host][entity.id].states.transitionLength = transitionLength.val;
796
+ // Run create state routine to ensure state is cached in memory
797
+ await this.stateSetCreate(`${this.deviceInfo[host].deviceName}.${entity.type}.${entity.id}.transitionLength`, `${stateName} of ${entity.config.name}`, transitionLength.val, `s`, writable);
675
798
  } else { // Else just create it
676
799
  await this.stateSetCreate(`${this.deviceInfo[host].deviceName}.${entity.type}.${entity.id}.transitionLength`, `${stateName} of ${entity.config.name}`, 0, `s`, writable);
677
800
  this.deviceInfo[host][entity.id].states.transitionLength = 0;
@@ -948,6 +1071,8 @@ class Esphome extends utils.Adapter {
948
1071
  onUnload(callback) {
949
1072
  try {
950
1073
  this.log.debug(JSON.stringify(this.deviceInfo));
1074
+
1075
+ // Set all online states to false
951
1076
  for (const device in this.deviceInfo) {
952
1077
 
953
1078
  // Ensure all known online states are set to false
@@ -962,9 +1087,12 @@ class Esphome extends utils.Adapter {
962
1087
  this.log.debug(`[onUnload] ${JSON.stringify(e)}`);
963
1088
  }
964
1089
  }
965
- if (reconnectTimer) {
966
- reconnectTimer = clearTimeout();
1090
+
1091
+ // Ensure all possible running timers are cleared
1092
+ for (const timer in resetTimers) {
1093
+ if (resetTimers[timer]) resetTimers[timer] = clearTimeout(resetTimers[timer]);
967
1094
  }
1095
+
968
1096
  try {
969
1097
  if (dashboardProcess) {
970
1098
  dashboardProcess.kill('SIGTERM', {
@@ -1028,8 +1156,7 @@ class Esphome extends utils.Adapter {
1028
1156
  } else {
1029
1157
  this.log.info(`Valid IP address received`);
1030
1158
  this.messageResponse[obj.message['device-ip']] = obj;
1031
- // const pass = this.decrypt(obj.message['device-pass']);
1032
- await this.connectDevices(obj.message['device-ip'], apiPass);
1159
+ await this.connectDevices(obj.message['device-ip'], obj.message['device-pass'], obj.message['deviceEncryptionKey']);
1033
1160
  }
1034
1161
  break;
1035
1162
  }
@@ -1079,6 +1206,17 @@ class Esphome extends utils.Adapter {
1079
1206
  try {
1080
1207
  if (state && state.ack === false) {
1081
1208
  const device = id.split('.');
1209
+
1210
+ try {
1211
+ // Verify if trigger is related to device-cleanup
1212
+ if (id.split('.')[3] === 'deviceCleanup'){
1213
+ await this.offlineDeviceCleanup();
1214
+ return;
1215
+ }
1216
+ } catch (e) {
1217
+ // Skip action
1218
+ }
1219
+
1082
1220
  const deviceIP = this.deviceStateRelation[device[2]].ip;
1083
1221
 
1084
1222
  // Handle Switch State
@@ -1226,13 +1364,84 @@ class Esphome extends utils.Adapter {
1226
1364
  });
1227
1365
 
1228
1366
  // Set online state to false, will be set to true at successfully connected
1229
- this.setState(`${_devices.rows[currDevice].id}.info._online`, {val: false, ack: true});
1367
+ await this.stateSetCreate(`${_devices.rows[currDevice].id}.info._online`, `Online state`, false);
1230
1368
  }
1231
1369
 
1232
1370
  } catch (e) {
1233
1371
  this.log.error(`[resetOnlineState] ${e}`);
1234
1372
  }
1235
1373
  }
1374
+
1375
+ async objectCleanup(ip){
1376
+ try {
1377
+ this.log.debug(`[objectCleanup] Starting channel and state cleanup for ${this.deviceInfo[ip].deviceName} | ${ip} | ${this.deviceInfo[ip].ip}`);
1378
+
1379
+ // Cancel cleanup operation in case device is not connected anymore
1380
+ if (this.deviceInfo[ip].connectionError || !this.deviceInfo[ip].connected) return;
1381
+
1382
+ // Set parameters for object view to only include objects within adapter namespace
1383
+ const params = {
1384
+ startkey : `${this.namespace}.${this.deviceInfo[ip].deviceName}.`,
1385
+ endkey : `${this.namespace}.\u9999`,
1386
+ };
1387
+
1388
+ // Get all current channels
1389
+ const _channels = await this.getObjectViewAsync('system', 'channel', params);
1390
+ // List all found channels & compare with memory, delete unneeded channels
1391
+ for (const currDevice in _channels.rows) {
1392
+ // @ts-ignore
1393
+ if (!this.deviceInfo[ip].adapterObjects.channels.includes(_channels.rows[currDevice].id)
1394
+ && _channels.rows[currDevice].id.split('.')[2] === this.deviceInfo[ip].deviceName){
1395
+ this.log.debug(`[objectCleanup] Unknown Channel found, delete ${_channels.rows[currDevice].id}`);
1396
+ await this.delObjectAsync(_channels.rows[currDevice].id, {recursive: true});
1397
+ }
1398
+ }
1399
+
1400
+ // Get all current states in adapter tree
1401
+ const _states = await this.getObjectViewAsync('system', 'state', params);
1402
+ // List all found states & compare with memory, delete unneeded states
1403
+ for (const currDevice in _states.rows) {
1404
+ if (!this.createdStatesDetails[_states.rows[currDevice].id.replace(`esphome.0.`, ``)]
1405
+ && _states.rows[currDevice].id.split('.')[2] === this.deviceInfo[ip].deviceName){
1406
+ this.log.debug(`[objectCleanup] Unknown State found, delete ${_states.rows[currDevice].id}`);
1407
+ // await this.delObjectAsync(_states.rows[currDevice].id);
1408
+ }
1409
+ }
1410
+
1411
+ } catch (e) {
1412
+ this.log.error(`[objectCleanup] Fatal error ${e} | ${e.stack}`);
1413
+ }
1414
+ }
1415
+
1416
+ async offlineDeviceCleanup () {
1417
+ this.log.info(`Offline Device cleanup started`);
1418
+
1419
+ try {
1420
+
1421
+ // Get an overview of all current devices known by adapter
1422
+ const knownDevices = await this.getDevicesAsync();
1423
+ console.log(`KnownDevices: ${knownDevices}`);
1424
+
1425
+ // Loop to all devices, check if online state = TRUE otherwise delete device
1426
+ for (const device in knownDevices){
1427
+
1428
+ // Get online value
1429
+ const online = await this.getStateAsync(`${knownDevices[device]._id}.info._online`);
1430
+ if (!online || !online.val){
1431
+ this.log.info(`Offline device ${knownDevices[device]._id.split('.')[2]} expected on ip ${knownDevices[device].native.ip} removed`);
1432
+ await this.delObjectAsync(knownDevices[device]._id, {recursive: true});
1433
+ }
1434
+
1435
+ }
1436
+
1437
+ if (!knownDevices) return; // exit function if no known device are detected
1438
+ if (knownDevices.length > 0) this.log.info(`Try to contact ${knownDevices.length} known devices`);
1439
+ } catch (e) {
1440
+ this.log.error(`[offlineDeviceCleanup] Fatal error occurred, cannot cleanup offline devices ${e} | ${e.stack}`);
1441
+
1442
+ }
1443
+ }
1444
+
1236
1445
  }
1237
1446
 
1238
1447
  if (require.main !== module) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.esphome",
3
- "version": "0.3.2",
3
+ "version": "0.4.1",
4
4
  "description": "Control your ESP8266/ESP32 with simple yet powerful configuration files created and managed by ESPHome",
5
5
  "author": {
6
6
  "name": "DutchmanNL",