iobroker.esphome 1.0.0-beta.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -12
- package/admin/i18n/de.json +1 -1
- package/admin/i18n/en.json +1 -1
- package/admin/i18n/es.json +1 -1
- package/admin/i18n/fr.json +1 -1
- package/admin/i18n/it.json +1 -1
- package/admin/i18n/nl.json +1 -1
- package/admin/i18n/pl.json +1 -1
- package/admin/i18n/pt.json +1 -1
- package/admin/i18n/ru.json +1 -1
- package/admin/i18n/uk.json +1 -1
- package/admin/i18n/zh-cn.json +1 -1
- package/io-package.json +31 -29
- package/lib/dashboardApi.js +8 -3
- package/lib/helpers.js +5 -0
- package/lib/stateAttr.js +7 -0
- package/lib/yamlFileManager.js +6 -3
- package/main.js +203 -36
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -147,11 +147,38 @@ All our adapter documentation can be found at [The DrozmotiX Docu Page](https://
|
|
|
147
147
|
|
|
148
148
|
## Prerequisites
|
|
149
149
|
|
|
150
|
-
* NodeJS >=
|
|
150
|
+
* NodeJS >= 22.x
|
|
151
151
|
* API is activated in YAML
|
|
152
152
|
* For admin tabs (optional)
|
|
153
153
|
* ESPHome Dashboard IP is provided in instance settings
|
|
154
154
|
|
|
155
|
+
## Testing pre-releases
|
|
156
|
+
|
|
157
|
+
Pre-releases are published to npm under the `next` dist-tag and are **not** offered by the
|
|
158
|
+
ioBroker repository - they have to be installed explicitly. Feedback on a beta is very welcome,
|
|
159
|
+
please report anything you find in the [issue tracker](https://github.com/DrozmotiX/ioBroker.esphome/issues).
|
|
160
|
+
|
|
161
|
+
Install the current pre-release from the ioBroker host:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
iobroker url iobroker.esphome@next
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Or install one specific version:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
iobroker url iobroker.esphome@1.0.0-beta.1
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
To go back to the released version afterwards:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
iobroker url iobroker.esphome
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Every pre-release is also listed under
|
|
180
|
+
[Releases](https://github.com/DrozmotiX/ioBroker.esphome/releases) with its changelog.
|
|
181
|
+
|
|
155
182
|
## ESPHome Dashboard Integration (Optional)
|
|
156
183
|
|
|
157
184
|
This adapter can optionally integrate the ESPHome Dashboard into ioBroker's admin interface. You have several options:
|
|
@@ -413,6 +440,21 @@ If you like my work, please consider a personal donation
|
|
|
413
440
|
### __WORK IN PROGRESS__
|
|
414
441
|
* (DutchmanNL)
|
|
415
442
|
-->
|
|
443
|
+
### 1.0.0 (2026-09-26) - Public Stable Release
|
|
444
|
+
|
|
445
|
+
- (@arteck) **FIXED**: A device that drops while its entities are still being announced no longer throws "Not connected", the state subscription is left to the reconnect
|
|
446
|
+
- (@arteck) **FIXED**: A connection loss logs one "Connection destroyed" warning per device instead of one per entity
|
|
447
|
+
- (@DutchmanNL) **ENHANCED**: Updated @iobroker/testing to 6.2.2 and @iobroker/adapter-core to 3.4.3
|
|
448
|
+
- (@DutchmanNL) **ENHANCED**: Tests now cover Node.js 22, 24 and 26 and the websocket library ws was updated to 8.21.3
|
|
449
|
+
|
|
450
|
+
### 1.0.0-beta.2 (2026-07-30)
|
|
451
|
+
|
|
452
|
+
- (@SimonFischer04) **FIXED**: ESPHome Dashboard 2026.6.5 and Pillow 12.2.0 are used instead of the latest available release, the 2026.7.x releases currently fail to install (#463)
|
|
453
|
+
- (@SimonFischer04) **FIXED**: Installations still set to "Always last available" are migrated to those versions once, so an update no longer leaves behind a dashboard that cannot be installed (#463)
|
|
454
|
+
- (@SimonFischer04) **NEW**: Selecting "Always last available" again is respected, the migration runs only once and such a setup only gets a warning at startup (#463)
|
|
455
|
+
- (@DutchmanNL) **FIXED**: Falling back to the latest ESPHome or Pillow release no longer aborts the dashboard setup, the requirement was passed to pip without a version specifier
|
|
456
|
+
- (@DutchmanNL) **FIXED**: Added the missing "pingInterval" and "pingAttempts" defaults to io-package.json, they were only defined in the admin configuration
|
|
457
|
+
- (@DutchmanNL) **ENHANCED**: "npm run check" is free of type errors, so type regressions are visible again
|
|
416
458
|
|
|
417
459
|
### 1.0.0-beta.1 (2026-07-28)
|
|
418
460
|
|
|
@@ -441,17 +483,6 @@ If you like my work, please consider a personal donation
|
|
|
441
483
|
- (@copilot) **FIXED**: Invalid jsonConfig warning on adapter installation
|
|
442
484
|
- (DutchmanNL) **FIXED**: ESLint errors by code refactoring
|
|
443
485
|
|
|
444
|
-
### 0.7.0-beta.4 (2026-02-21)
|
|
445
|
-
|
|
446
|
-
- (DutchmanNL) **FIXED**: ESLint errors by code refactoring
|
|
447
|
-
- (@copilot) **FIXED**: Restore missing `configStates` option in admin UI to allow configuring whether configuration states are shown per entity
|
|
448
|
-
- (@copilot) **NEW**: Per-device `rgbAutoWhite` toggle in the light config channel for automatic white-channel routing on RGBW lights (see [Controlling RGBW Lights](#controlling-rgbw-lights))
|
|
449
|
-
|
|
450
|
-
### 0.7.0-beta.3 (2026-02-20)
|
|
451
|
-
|
|
452
|
-
- (@copilot) **NEW**: Added support for `colorBrightness`, `coldWhite`, `warmWhite`, and `colorMode` states for lights using the new ESPHome color mode API
|
|
453
|
-
- (@copilot) **FIXED**: RGB light control (brightness, color, white, colorTemperature) not working with newer ESPHome firmware that uses `supportedColorModesList` instead of deprecated legacy flags (#338)
|
|
454
|
-
|
|
455
486
|
[Older changelogs can be found there](CHANGELOG_OLD.md)
|
|
456
487
|
|
|
457
488
|
## License
|
package/admin/i18n/de.json
CHANGED
|
@@ -104,5 +104,5 @@
|
|
|
104
104
|
"reConInterval": "Einstellung zwischen ioBroker versucht, dieses Gerät erneut zu verbinden",
|
|
105
105
|
"templateKeyTip": "Dieser Wert wird vom Adapter bereitgestellt.",
|
|
106
106
|
"ttGetStationId": "Führen Sie diese Aktion für das ausgewählte Gerät aus.",
|
|
107
|
-
"yamlUploadContentPlaceholder": "
|
|
107
|
+
"yamlUploadContentPlaceholder": "# Beispielkonfiguration\nesphome:\n name: my-device\n platform: ESP8266\n board: nodemcuv2"
|
|
108
108
|
}
|
package/admin/i18n/en.json
CHANGED
|
@@ -104,5 +104,5 @@
|
|
|
104
104
|
"reConInterval": "Setting between ioBroker tries to reconnect this device",
|
|
105
105
|
"templateKeyTip": "This value is provided by the adapter.",
|
|
106
106
|
"ttGetStationId": "Run this action for the selected device.",
|
|
107
|
-
"yamlUploadContentPlaceholder": "
|
|
107
|
+
"yamlUploadContentPlaceholder": "# Example configuration\nesphome:\n name: my-device\n platform: ESP8266\n board: nodemcuv2"
|
|
108
108
|
}
|
package/admin/i18n/es.json
CHANGED
|
@@ -104,5 +104,5 @@
|
|
|
104
104
|
"reConInterval": "Configuración entre intentos de ioBroker para volver a conectar este dispositivo",
|
|
105
105
|
"templateKeyTip": "Este valor lo proporciona el adaptador.",
|
|
106
106
|
"ttGetStationId": "Ejecute esta acción para el dispositivo seleccionado.",
|
|
107
|
-
"yamlUploadContentPlaceholder": "
|
|
107
|
+
"yamlUploadContentPlaceholder": "# Configuración de ejemplo\nesphome:\n name: my-device\n platform: ESP8266\n board: nodemcuv2"
|
|
108
108
|
}
|
package/admin/i18n/fr.json
CHANGED
|
@@ -104,5 +104,5 @@
|
|
|
104
104
|
"reConInterval": "Paramètres entre ioBroker essayant de reconnecter cet appareil",
|
|
105
105
|
"templateKeyTip": "Cette valeur est fournie par l'adaptateur.",
|
|
106
106
|
"ttGetStationId": "Exécutez cette action pour le périphérique sélectionné.",
|
|
107
|
-
"yamlUploadContentPlaceholder": "
|
|
107
|
+
"yamlUploadContentPlaceholder": "# Exemple de configuration\nesphome:\n name: my-device\n platform: ESP8266\n board: nodemcuv2"
|
|
108
108
|
}
|
package/admin/i18n/it.json
CHANGED
|
@@ -104,5 +104,5 @@
|
|
|
104
104
|
"reConInterval": "Impostazione tra i tentativi di ioBroker di riconnettere questo dispositivo",
|
|
105
105
|
"templateKeyTip": "Questo valore è fornito dall'adattatore.",
|
|
106
106
|
"ttGetStationId": "Esegui questa azione per il dispositivo selezionato.",
|
|
107
|
-
"yamlUploadContentPlaceholder": "
|
|
107
|
+
"yamlUploadContentPlaceholder": "# Configurazione di esempio\nesphome:\n name: my-device\n platform: ESP8266\n board: nodemcuv2"
|
|
108
108
|
}
|
package/admin/i18n/nl.json
CHANGED
|
@@ -104,5 +104,5 @@
|
|
|
104
104
|
"reConInterval": "Instelling tussen ioBroker-pogingen om dit apparaat opnieuw te verbinden",
|
|
105
105
|
"templateKeyTip": "Deze waarde wordt geleverd door de adapter.",
|
|
106
106
|
"ttGetStationId": "Voer deze actie uit voor het geselecteerde apparaat.",
|
|
107
|
-
"yamlUploadContentPlaceholder": "
|
|
107
|
+
"yamlUploadContentPlaceholder": "# Voorbeeldconfiguratie\nesphome:\n name: my-device\n platform: ESP8266\n board: nodemcuv2"
|
|
108
108
|
}
|
package/admin/i18n/pl.json
CHANGED
|
@@ -104,5 +104,5 @@
|
|
|
104
104
|
"reConInterval": "Ustawienie między próbą ponownego podłączenia tego urządzenia przez ioBroker",
|
|
105
105
|
"templateKeyTip": "Ta wartość jest dostarczana przez adapter.",
|
|
106
106
|
"ttGetStationId": "Uruchom tę akcję dla wybranego urządzenia.",
|
|
107
|
-
"yamlUploadContentPlaceholder": "
|
|
107
|
+
"yamlUploadContentPlaceholder": "# Przykładowa konfiguracja\nesphome:\n name: my-device\n platform: ESP8266\n board: nodemcuv2"
|
|
108
108
|
}
|
package/admin/i18n/pt.json
CHANGED
|
@@ -104,5 +104,5 @@
|
|
|
104
104
|
"reConInterval": "Configuração entre tentativas do ioBroker para reconectar este dispositivo",
|
|
105
105
|
"templateKeyTip": "Esse valor é fornecido pelo adaptador.",
|
|
106
106
|
"ttGetStationId": "Execute esta ação para o dispositivo selecionado.",
|
|
107
|
-
"yamlUploadContentPlaceholder": "
|
|
107
|
+
"yamlUploadContentPlaceholder": "# Configuração de exemplo\nesphome:\n name: my-device\n platform: ESP8266\n board: nodemcuv2"
|
|
108
108
|
}
|
package/admin/i18n/ru.json
CHANGED
|
@@ -104,5 +104,5 @@
|
|
|
104
104
|
"reConInterval": "Настройка между попытками ioBroker повторно подключить это устройство",
|
|
105
105
|
"templateKeyTip": "Это значение предоставляется адаптером.",
|
|
106
106
|
"ttGetStationId": "Запустите это действие для выбранного устройства.",
|
|
107
|
-
"yamlUploadContentPlaceholder": "
|
|
107
|
+
"yamlUploadContentPlaceholder": "# Пример конфигурации\nesphome:\n name: my-device\n platform: ESP8266\n board: nodemcuv2"
|
|
108
108
|
}
|
package/admin/i18n/uk.json
CHANGED
|
@@ -104,5 +104,5 @@
|
|
|
104
104
|
"reConInterval": "Інтервал, через який ioBroker намагається повторно підключити цей пристрій",
|
|
105
105
|
"templateKeyTip": "Це значення забезпечує адаптер.",
|
|
106
106
|
"ttGetStationId": "Запустіть цю дію для вибраного пристрою.",
|
|
107
|
-
"yamlUploadContentPlaceholder": "
|
|
107
|
+
"yamlUploadContentPlaceholder": "# Приклад конфігурації\nesphome:\n name: my-device\n platform: ESP8266\n board: nodemcuv2"
|
|
108
108
|
}
|
package/admin/i18n/zh-cn.json
CHANGED
|
@@ -104,5 +104,5 @@
|
|
|
104
104
|
"reConInterval": "ioBroker 尝试重新连接此设备之间的设置",
|
|
105
105
|
"templateKeyTip": "该值由适配器提供。",
|
|
106
106
|
"ttGetStationId": "为选定的设备运行此操作。",
|
|
107
|
-
"yamlUploadContentPlaceholder": "
|
|
107
|
+
"yamlUploadContentPlaceholder": "# 示例配置\nesphome:\n name: my-device\n platform: ESP8266\n board: nodemcuv2"
|
|
108
108
|
}
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "esphome",
|
|
4
|
-
"version": "1.0.0
|
|
4
|
+
"version": "1.0.0",
|
|
5
5
|
"news": {
|
|
6
|
+
"1.0.0": {
|
|
7
|
+
"en": "**FIXED**: A device that drops while its entities are still being announced no longer throws \"Not connected\", the state subscription is left to the reconnect\n**FIXED**: A connection loss logs one \"Connection destroyed\" warning per device instead of one per entity\n**ENHANCED**: Updated @iobroker/testing to 6.2.2 and @iobroker/adapter-core to 3.4.3\n**ENHANCED**: Tests now cover Node.js 22, 24 and 26 and the websocket library ws was updated to 8.21.3",
|
|
8
|
+
"de": "**BEHOBEN**: Ein Gerät, das abbricht, während seine Entitäten noch angekündigt werden, gibt nicht mehr „Nicht verbunden“ aus, das Statusabonnement bleibt der erneuten Verbindung überlassen\n**BEHOBEN**: Bei einem Verbindungsverlust wird eine Warnung „Verbindung zerstört“ pro Gerät protokolliert statt einer pro Entität\n**VERBESSERT**: @iobroker/testing auf 6.2.2 und @iobroker/adapter-core auf 3.4.3 aktualisiert\n**VERBESSERT**: Tests decken jetzt Node.js 22, 24 und 26 ab und die Websocket-Bibliothek ws wurde auf 8.21.3 aktualisiert",
|
|
9
|
+
"ru": "**ИСПРАВЛЕНО**: устройство, которое отключается, пока его объекты все еще объявляются, больше не выдает сообщение «Не подключено», государственная подписка остается на повторное подключение.\n**ИСПРАВЛЕНО**: при потере соединения регистрируется одно предупреждение «Соединение разрушено» для каждого устройства вместо одного для каждого объекта.\n**УЛУЧШЕНО**: @iobroker/testing обновлен до версии 6.2.2 и @iobroker/adapter-core до версии 3.4.3.\n**УЛУЧШЕНО**: тесты теперь охватывают Node.js 22, 24 и 26, а библиотека веб-сокетов ws обновлена до версии 8.21.3.",
|
|
10
|
+
"pt": "**CORRIGIDO**: Um dispositivo que cai enquanto suas entidades ainda estão sendo anunciadas não gera mais \"Não conectado\", a assinatura do estado fica para a reconexão\n**CORRIGIDO**: uma perda de conexão registra um aviso de \"Conexão destruída\" por dispositivo em vez de um por entidade\n**MELHORADO**: @iobroker/testing atualizado para 6.2.2 e @iobroker/adapter-core para 3.4.3\n**MELHORADO**: Os testes agora cobrem o Node.js 22, 24 e 26 e a biblioteca websocket ws foi atualizada para 8.21.3",
|
|
11
|
+
"nl": "**OPGELOST**: een apparaat dat uitvalt terwijl de entiteiten nog worden aangekondigd, geeft niet langer de melding \"Niet verbonden\", het statusabonnement wordt overgelaten aan het opnieuw verbinden\n**OPGELOST**: Bij verbindingsverlies wordt één waarschuwing 'Verbinding vernietigd' per apparaat geregistreerd in plaats van één per entiteit\n**VERBETERD**: @iobroker/testing bijgewerkt naar 6.2.2 en @iobroker/adapter-core naar 3.4.3\n**VERBETERD**: tests omvatten nu Node.js 22, 24 en 26 en de websocketbibliotheek ws is bijgewerkt naar 8.21.3",
|
|
12
|
+
"fr": "**CORRIGÉ** : Un appareil qui tombe alors que ses entités sont encore annoncées ne lance plus \"Non connecté\", l'abonnement de l'état est laissé à la reconnexion\n**CORRIGÉ** : Une perte de connexion enregistre un avertissement « Connexion détruite » par appareil au lieu d'un par entité.\n**AMÉLIORÉ** : mise à jour de @iobroker/testing vers 6.2.2 et de @iobroker/adapter-core vers 3.4.3\n**AMÉLIORÉ** : les tests couvrent désormais Node.js 22, 24 et 26 et la bibliothèque websocket ws a été mise à jour vers 8.21.3",
|
|
13
|
+
"it": "**RISOLTO**: Un dispositivo che cade mentre le sue entità vengono ancora annunciate non lancia più \"Non connesso\", l'abbonamento statale viene lasciato alla riconnessione\n**RISOLTO**: una perdita di connessione registra un avviso \"Connessione distrutta\" per dispositivo anziché uno per entità\n**MIGLIORATO**: aggiornato @iobroker/testing alla 6.2.2 e @iobroker/adapter-core alla 3.4.3\n**MIGLIORATO**: i test ora coprono Node.js 22, 24 e 26 e la libreria websocket ws è stata aggiornata alla versione 8.21.3",
|
|
14
|
+
"es": "**CORREGIDO**: Un dispositivo que se cae mientras sus entidades aún se están anunciando ya no arroja \"No conectado\", la suscripción estatal se deja para la reconexión\n**SOLUCIONADO**: Una pérdida de conexión registra una advertencia de \"Conexión destruida\" por dispositivo en lugar de una por entidad\n**MEJORADO**: @iobroker/testing actualizado a 6.2.2 y @iobroker/adapter-core a 3.4.3\n**MEJORADO**: Las pruebas ahora cubren Node.js 22, 24 y 26 y la biblioteca websocket ws se actualizó a 8.21.3",
|
|
15
|
+
"pl": "**NAPRAWIONO**: Urządzenie, które zostanie utracone, gdy nadal ogłaszane są jego elementy, nie wyświetla już komunikatu „Brak połączenia”, subskrypcja stanowa pozostaje do ponownego połączenia\n**NAPRAWIONO**: W przypadku utraty połączenia rejestrowane jest jedno ostrzeżenie „Połączenie zniszczone” na każde urządzenie zamiast jednego na jednostkę\n**ULEPSZONE**: Zaktualizowano @iobroker/testing do wersji 6.2.2 i @iobroker/adapter-core do wersji 3.4.3\n**ULEPSZONE**: Testy obejmują teraz Node.js 22, 24 i 26, a biblioteka websocket ws została zaktualizowana do wersji 8.21.3",
|
|
16
|
+
"uk": "**ВИПРАВЛЕНО**: пристрій, який відключається, поки його об’єкти все ще оголошуються, більше не видає повідомлення «Не підключено», підписка на стан залишається для повторного підключення\n**ВИПРАВЛЕНО**: втрата з’єднання реєструє одне попередження «З’єднання зруйновано» на пристрій замість одного на об’єкт.\n**ПОКРАЩЕНО**: оновлено @iobroker/testing до 6.2.2 і @iobroker/adapter-core до 3.4.3\n**ПОКРАЩЕНО**: тепер тести охоплюють Node.js 22, 24 і 26, а бібліотеку websocket ws оновлено до 8.21.3",
|
|
17
|
+
"zh-cn": "**已修复**:在其实体仍在宣布时掉落的设备不再抛出“未连接”,状态订阅将留给重新连接\n**已修复**:连接丢失会为每个设备记录一个“连接已损坏”警告,而不是每个实体一个\n**增强**:将 @iobroker/testing 更新至 6.2.2,将 @iobroker/adapter-core 更新至 3.4.3\n**增强**:测试现在涵盖 Node.js 22、24 和 26,并且 websocket 库 ws 已更新至 8.21.3"
|
|
18
|
+
},
|
|
19
|
+
"1.0.0-beta.2": {
|
|
20
|
+
"en": "**FIXED**: ESPHome Dashboard 2026.6.5 and Pillow 12.2.0 are used instead of the latest available release, the 2026.7.x releases currently fail to install (#463)\n**FIXED**: Installations still set to \"Always last available\" are migrated to those versions once, so an update no longer leaves behind a dashboard that cannot be installed (#463)\n**NEW**: Selecting \"Always last available\" again is respected, the migration runs only once and such a setup only gets a warning at startup (#463)\n**FIXED**: Falling back to the latest ESPHome or Pillow release no longer aborts the dashboard setup, the requirement was passed to pip without a version specifier\n**FIXED**: Added the missing \"pingInterval\" and \"pingAttempts\" defaults to io-package.json, they were only defined in the admin configuration\n**ENHANCED**: \"npm run check\" is free of type errors, so type regressions are visible again",
|
|
21
|
+
"de": "**BEHOBEN**: ESPHome Dashboard 2026.6.5 und Pillow 12.2.0 werden anstelle der neuesten verfügbaren Version verwendet, die 2026.7.x-Versionen können derzeit nicht installiert werden (#463)\n**BEHOBEN**: Installationen, die noch auf „Immer zuletzt verfügbar“ eingestellt sind, werden einmal auf diese Versionen migriert, sodass ein Update kein Dashboard mehr hinterlässt, das nicht installiert werden kann (#463)\n**NEU**: Die erneute Auswahl von „Immer zuletzt verfügbar“ wird berücksichtigt, die Migration wird nur einmal ausgeführt und ein solches Setup erhält nur beim Start eine Warnung (#463)\n**BEHOBEN**: Beim Zurückgreifen auf die neueste ESPHome- oder Pillow-Version wird die Dashboard-Einrichtung nicht mehr abgebrochen. Die Anforderung wurde ohne Versionsspezifizierer an pip übergeben\n**BEHOBEN**: Die fehlenden Standardeinstellungen „pingInterval“ und „pingAttempts“ wurden zu io-package.json hinzugefügt. Sie wurden nur in der Administratorkonfiguration definiert\n**VERBESSERT**: „npm run check“ ist frei von Typfehlern, sodass Typregressionen wieder sichtbar sind",
|
|
22
|
+
"ru": "**ИСПРАВЛЕНО**: ESPHome Dashboard 2026.6.5 и Pillow 12.2.0 используются вместо последней доступной версии, версии 2026.7.x в настоящее время не устанавливаются (#463).\n**ИСПРАВЛЕНО**: Установки, для которых по-прежнему установлен режим «Всегда последний доступный», переносятся в эти версии один раз, поэтому обновление больше не оставляет после себя панель управления, которую невозможно установить (#463).\n**НОВОЕ**: повторный выбор «Всегда последний доступен» учитывается, миграция выполняется только один раз, и такая настройка получает предупреждение только при запуске (# 463).\n**ИСПРАВЛЕНО**: возврат к последней версии ESPHome или Pillow больше не прерывает настройку информационной панели, требование было передано в pip без указания версии.\n**ИСПРАВЛЕНО**: в io-package.json добавлены отсутствующие значения по умолчанию «pingInterval» и «pingAttempts», они были определены только в конфигурации администратора.\n**УЛУЧШЕНО**: «проверка запуска npm» не содержит ошибок типа, поэтому регрессия типов снова видна.",
|
|
23
|
+
"pt": "**CORRIGIDO**: ESPHome Dashboard 2026.6.5 e Pillow 12.2.0 são usados em vez da versão mais recente disponível, as versões 2026.7.x atualmente não conseguem ser instaladas (#463)\n**CORRIGIDO**: As instalações ainda definidas como \"Sempre últimas disponíveis\" são migradas para essas versões uma vez, portanto, uma atualização não deixa mais um painel que não pode ser instalado (#463)\n**NOVO**: Selecionar \"Sempre o último disponível\" novamente é respeitado, a migração é executada apenas uma vez e tal configuração só recebe um aviso na inicialização (#463)\n**CORRIGIDO**: Voltar para a versão mais recente do ESPHome ou Pillow não cancela mais a configuração do painel, o requisito foi passado para pip sem um especificador de versão\n**CORRIGIDO**: Adicionados os padrões \"pingInterval\" e \"pingAttempts\" ausentes para io-package.json, eles foram definidos apenas na configuração do administrador\n**MELHORADO**: \"npm run check\" está livre de erros de tipo, então as regressões de tipo são visíveis novamente",
|
|
24
|
+
"nl": "**OPGELOST**: ESPHome Dashboard 2026.6.5 en Pillow 12.2.0 worden gebruikt in plaats van de nieuwste beschikbare release, de 2026.7.x-releases kunnen momenteel niet worden geïnstalleerd (#463)\n**OPGELOST**: Installaties die nog steeds zijn ingesteld op 'Altijd laatst beschikbaar' worden één keer naar die versies gemigreerd, zodat een update niet langer een dashboard achterlaat dat niet kan worden geïnstalleerd (#463)\n**NIEUW**: Opnieuw \"Altijd laatst beschikbaar\" selecteren wordt gerespecteerd, de migratie wordt slechts één keer uitgevoerd en zo'n setup krijgt alleen een waarschuwing bij het opstarten (#463)\n**OPGELOST**: Terugvallen naar de nieuwste ESPHome- of Pillow-release breekt niet langer de dashboardconfiguratie af, de vereiste werd doorgegeven aan pip zonder een versiespecificatie\n**OPGELOST**: De ontbrekende standaardwaarden \"pingInterval\" en \"pingAttempts\" zijn toegevoegd aan io-package.json, deze werden alleen gedefinieerd in de beheerdersconfiguratie\n**VERBETERD**: \"npm run check\" bevat geen typefouten, dus typeregressies zijn weer zichtbaar",
|
|
25
|
+
"fr": "**CORRIGÉ** : ESPHome Dashboard 2026.6.5 et Pillow 12.2.0 sont utilisés à la place de la dernière version disponible, les versions 2026.7.x ne parviennent actuellement pas à s'installer (#463)\n**CORRIGÉ** : Les installations toujours définies sur \"Toujours dernière disponible\" sont migrées vers ces versions une fois, donc une mise à jour ne laisse plus derrière elle un tableau de bord qui ne peut pas être installé (#463).\n**NOUVEAU** : La sélection à nouveau de \"Toujours dernier disponible\" est respectée, la migration ne s'exécute qu'une seule fois et une telle configuration ne reçoit qu'un avertissement au démarrage (#463)\n** CORRIGÉ ** : le retour à la dernière version d'ESPHome ou Pillow n'interrompt plus la configuration du tableau de bord, l'exigence a été transmise à pip sans spécificateur de version.\n**FIXED** : Ajout des valeurs par défaut manquantes \"pingInterval\" et \"pingAttempts\" à io-package.json, elles n'étaient définies que dans la configuration de l'administrateur\n**AMÉLIORÉ** : \"npm run check\" est exempt d'erreurs de type, donc les régressions de type sont à nouveau visibles",
|
|
26
|
+
"it": "**RISOLTO**: ESPHome Dashboard 2026.6.5 e Pillow 12.2.0 vengono utilizzati al posto dell'ultima versione disponibile, le versioni 2026.7.x attualmente non vengono installate (#463)\n**RISOLTO**: le installazioni ancora impostate su \"Sempre ultime disponibili\" vengono migrate a quelle versioni una volta, quindi un aggiornamento non lascia più un dashboard che non può essere installato (#463)\n**NOVITÀ**: Selezionando nuovamente \"Sempre ultimo disponibile\" viene rispettata, la migrazione viene eseguita solo una volta e tale configurazione riceve un avviso solo all'avvio (#463)\n**RISOLTO**: il ritorno all'ultima versione di ESPHome o Pillow non interrompe più la configurazione del dashboard, il requisito veniva passato a pip senza uno specificatore di versione\n**RISOLTO**: aggiunti i valori predefiniti mancanti \"pingInterval\" e \"pingAttempts\" a io-package.json, erano definiti solo nella configurazione dell'amministratore\n**MIGLIORATO**: \"npm run check\" è privo di errori di tipo, quindi le regressioni di tipo sono nuovamente visibili",
|
|
27
|
+
"es": "**CORREGIDO**: ESPHome Dashboard 2026.6.5 y Pillow 12.2.0 se utilizan en lugar de la última versión disponible; las versiones 2026.7.x actualmente no se instalan (#463)\n**CORREGIDO**: Las instalaciones que aún están configuradas como \"Siempre disponibles por última vez\" se migran a esas versiones una vez, por lo que una actualización ya no deja un panel que no se puede instalar (#463)\n**NUEVO**: Se respeta la selección \"Siempre último disponible\", la migración se ejecuta solo una vez y dicha configuración solo recibe una advertencia al inicio (#463)\n**CORREGIDO**: Volver a la última versión de ESPHome o Pillow ya no cancela la configuración del panel, el requisito se pasó a pip sin un especificador de versión\n**CORREGIDO**: Se agregaron los valores predeterminados faltantes de \"pingInterval\" y \"pingAttempts\" a io-package.json, solo se definieron en la configuración del administrador.\n**MEJORADO**: \"npm run check\" no tiene errores de tipo, por lo que las regresiones de tipo son visibles nuevamente",
|
|
28
|
+
"pl": "**NAPRAWIONO**: ESPHome Dashboard 2026.6.5 i Pillow 12.2.0 są używane zamiast najnowszej dostępnej wersji, obecnie nie można zainstalować wersji 2026.7.x (#463)\n**NAPRAWIONO**: Instalacje nadal ustawione na „Zawsze ostatnio dostępne” są migrowane do tych wersji tylko raz, więc aktualizacja nie pozostawia już pulpitu nawigacyjnego, którego nie można zainstalować (#463)\n**NOWOŚĆ**: Ponowne wybranie opcji „Zawsze ostatnia dostępna” jest przestrzegane, migracja odbywa się tylko raz, a taka konfiguracja wyświetla ostrzeżenie tylko przy uruchomieniu (#463)\n**NAPRAWIONO**: Powrót do najnowszej wersji ESPHome lub Pillow nie powoduje już przerwania konfiguracji pulpitu nawigacyjnego, wymaganie zostało przekazane do pip bez specyfikatora wersji\n**NAPRAWIONO**: Dodano brakujące wartości domyślne „pingInterval” i „pingAttempts” do io-package.json, zostały one zdefiniowane tylko w konfiguracji administratora\n**ULEPSZONE**: „sprawdzenie przebiegu npm” jest wolne od błędów typów, więc regresje typów są ponownie widoczne",
|
|
29
|
+
"uk": "**ВИПРАВЛЕНО**: ESPHome Dashboard 2026.6.5 і Pillow 12.2.0 використовуються замість останньої доступної версії, версії 2026.7.x наразі не вдається встановити (#463)\n**ВИПРАВЛЕНО**: інсталяції, які все ще мають значення «Завжди останні доступні», переносяться до цих версій один раз, тому оновлення більше не залишає інформаційну панель, яку неможливо встановити (#463)\n**НОВЕ**: вибір «Завжди останній доступний» знову виконується, міграція виконується лише один раз, і таке налаштування отримує лише попередження під час запуску (#463)\n**ВИПРАВЛЕНО**: повернення до останньої версії ESPHome або Pillow більше не припиняє налаштування інформаційної панелі, вимогу було передано pip без специфікатора версії\n**ВИПРАВЛЕНО**: до io-package.json додано відсутні параметри «pingInterval» і «pingAttempts» за замовчуванням, їх було визначено лише в конфігурації адміністратора\n**ПОКРАЩЕНО**: \"npm run check\" не містить помилок типу, тому регресії типу знову видно",
|
|
30
|
+
"zh-cn": "**已修复**:使用 ESPHome Dashboard 2026.6.5 和 Pillow 12.2.0,而不是最新的可用版本,2026.7.x 版本当前无法安装 (#463)\n**已修复**:仍设置为“始终最后可用”的安装会迁移到这些版本一次,因此更新不再留下无法安装的仪表板 (#463)\n**新**:再次选择“始终最后可用”受到尊重,迁移仅运行一次,并且此类设置仅在启动时收到警告(#463)\n**已修复**:回退到最新的 ESPHome 或 Pillow 版本不再中止仪表板设置,要求已在没有版本说明符的情况下传递给 pip\n**已修复**:将缺少的“pingInterval”和“pingAttempts”默认值添加到 io-package.json,它们仅在管理配置中定义\n**增强**:“npm run check”没有类型错误,因此类型回归再次可见"
|
|
31
|
+
},
|
|
6
32
|
"1.0.0-beta.1": {
|
|
7
33
|
"en": "**FIXED**: Restored a green CI matrix, the dashboard integration tests no longer hang the test runner\n**FIXED**: Corrected the changelog entry in io-package.json so it matches the published version\n**FIXED**: Admin configuration is fully translated again, 29 texts were only available in English\n**FIXED**: YAML upload field renders as a proper multi-line text area again\n**ENHANCED**: Timers are now managed by the adapter, so they are always cleaned up on unload\n**ENHANCED**: Pre-releases are published under the npm \"beta\" tag instead of \"latest\"\n**ENHANCED**: Removed the obsolete admin/words.js and added prettier formatting scripts\n**ENHANCED**: Updated @iobroker/testing to 5.3.0 and the GitHub actions used in CI",
|
|
8
34
|
"de": "**BEHOBEN**: Eine grüne CI-Matrix wurde wiederhergestellt. Die Dashboard-Integrationstests hängen den Test Runner nicht mehr auf\n**BEHOBEN**: Der Changelog-Eintrag in io-package.json wurde korrigiert, sodass er mit der veröffentlichten Version übereinstimmt\n**BEHOBEN**: Admin-Konfiguration ist wieder vollständig übersetzt, 29 Texte waren nur auf Englisch verfügbar\n**BEHOBEN**: Das YAML-Upload-Feld wird wieder als richtiger mehrzeiliger Textbereich dargestellt\n**VERBESSERT**: Timer werden jetzt vom Adapter verwaltet, sodass sie beim Entladen immer bereinigt werden\n**VERBESSERT**: Vorabversionen werden unter dem npm-Tag „beta“ anstelle von „latest“ veröffentlicht.\n**VERBESSERT**: Die veraltete Datei admin/words.js wurde entfernt und hübschere Formatierungsskripte hinzugefügt\n**VERBESSERT**: @iobroker/testing auf 5.3.0 und die in CI verwendeten GitHub-Aktionen aktualisiert",
|
|
@@ -67,32 +93,6 @@
|
|
|
67
93
|
"pl": "ulepszenie README\nfix # 394, actually fix # 340, # 356\n* *FIXED* *: Element wentylatora nie działa # 205 \n* *NOWOŚĆ* *: Zezwalaj na personalizację wersji Poduszki używanej przez ESPHome Dashboard, podobną do wyboru wersji ESPHome \n* *NOWOŚĆ* *: Dodaj przycisk \"Clear Autopy Cache\" w zakładce Konfiguracja tablicy rozdzielczej ESPHome, aby rozwiązać problemy z załadowaniem tablicy rozdzielczej (# 209) ",
|
|
68
94
|
"uk": "покращити резюме\nвиправити #394, насправді виправити #340, #356\n**FIXED***: Компонент вентилятора не працює #205\n**NEW**: Дозволяє налаштування версії Pillow, що використовується ESPHome Dashboard, схожим на ESPHome версія selector\n**NEW**: Додати кнопку \"Зареєструвати Autopy Cache\" у вкладці конфігурації ESPHome Dashboard для вирішення проблем завантаження панелі (#209)",
|
|
69
95
|
"zh-cn": "改进读取\n修复394号,实际修复340号,356号\n** FIXED**:粉丝组件不工作#205\n**NEW**:允许定制ESPHome Dashboard使用的Pillow版本,类似于ESPHome版本选择器\n**NEW**:在ESPHome Dashboard配置标签中添加\"清除自动缓存\"按钮以解决仪表板加载问题(# 209)"
|
|
70
|
-
},
|
|
71
|
-
"0.7.0-beta.1": {
|
|
72
|
-
"en": "improve dashboard testing",
|
|
73
|
-
"de": "dashboard testen",
|
|
74
|
-
"ru": "улучшить тестирование приборной панели",
|
|
75
|
-
"pt": "melhorar o teste do painel",
|
|
76
|
-
"nl": "verbeteren van dashboard testen",
|
|
77
|
-
"fr": "améliorer les essais de tableau de bord",
|
|
78
|
-
"it": "migliorare il test del cruscotto",
|
|
79
|
-
"es": "mejorar las pruebas de dashboard",
|
|
80
|
-
"pl": "poprawić testowanie płyt rozdzielczych",
|
|
81
|
-
"uk": "поліпшити тестування панелі приладів",
|
|
82
|
-
"zh-cn": "改进仪表板测试"
|
|
83
|
-
},
|
|
84
|
-
"0.7.0-beta.0": {
|
|
85
|
-
"en": "**FIXED**: Invalid jsonConfig schema - removed unsupported `doNotSave` property from table elements\n**NEW**: YAML file management interface in admin UI for upload/download/delete operations (#369)\nimprove logging for #201\nautomatic migration from versions prior to ESPHomeDashboardUrl introduction (pre v0.6.1)\nupdate pillow\nfix readme link to lib\nfix connection status #311\nremove unneeded node-fetch dependency",
|
|
86
|
-
"de": "**FIXED**: Invalid jsonConfig Schema - entfernte nicht unterstützte `doNotSave` Eigenschaft von Tabellenelementen\n**NEW**: YAML-Dateiverwaltungsschnittstelle in admin UI für Upload/Download/Auslöschung (#369)\nverbesserung der protokollierung für #201\nautomatische Migration von Versionen vor der Einführung von ESPHomeDashboardUrl (pre v0.6.1)\nupdate kissen\nreadme link zu lib reparieren\nfix-verbindungsstatus #311\nnicht benötigte knoten-fetch-abhängigkeit entfernen",
|
|
87
|
-
"ru": "** FIXED**: Недействительная схема jsonConfig - удалено неподдерживаемое свойство \"doNotSave\" из элементов таблицы\n**NEW**: интерфейс управления файлами YAML в интерфейсе администратора для операций загрузки/загрузки/удаления (#369)\nулучшение регистрации для #201\nавтоматическая миграция с версий до внедрения ESPHomeDashboardUrl (pre v0.6.1)\nобновление подушки\nобсуждение readme link to lib\nстатус подключения #311\nустранение ненужной зависимости от узла",
|
|
88
|
-
"pt": "**FIXED**: Esquema inválido do jsonConfig - propriedade `doNotSave` não suportada\n**NOVO**: Interface de gerenciamento de arquivos YAML na interface de administração UI para operações de upload/download/delete (#369)\nmelhorar o registro para #201\nmigração automática de versões anteriores à introdução ESPHomeDashboardUrl (pré v0.6.1)\natualizar almofada\ncorrigir o link readme para a lib\ncorrigir o estado da ligação # 311\nremover a dependência de busca de nó desnecessária",
|
|
89
|
-
"nl": "** VAST**: Ongeldig jsonConfig schema - verwijderde niet-ondersteunde eigenschap van tabelelementen\n**NIEUW**: YAML file management interface in admin UI voor upload/download/delete operaties (#369)\nverbeteren logging voor #201\nautomatische migratie van versies voorafgaand aan ESPHomeDashboardUrl introductie (pre v0.6.1)\nupdate kussen\nrepareren readme link naar lib\nfix verbindingsstatus #311\nverwijder ongewenste node-fetch afhankelijkheid",
|
|
90
|
-
"fr": "**FIXED**: Schéma jsonConfig non valide - supprimé la propriété `doNotSave` non supportée des éléments de table\n**NOUVEAU**: Interface de gestion de fichiers YAML dans l'interface utilisateur admin pour les opérations de téléchargement/téléchargement/effacement (#369)\naméliorer l'enregistrement pour #201\nmigration automatique des versions avant l'introduction de ESPHomeDashboardUrl (pre v0.6.1)\nmettre à jour l'oreiller\ncorriger le lien readme vers lib\nfixer l'état de connexion #311\nsupprimer la dépendance inutile des nœuds",
|
|
91
|
-
"it": "**FIXED**: Invalid jsonConfig schema - rimosso `doNotSave` immobili da elementi da tavolo\n**NEW**: interfaccia di gestione dei file YAML nell'interfaccia di amministrazione UI per operazioni di upload/download/delete (#369)\nmigliorare logging per #201\nmigrazione automatica dalle versioni precedenti l'introduzione di ESPHomeDashboardUrl (pre v0.6.1)\naggiornamento cuscino\ncorrezione link di lettura per lib\nstato di connessione di correzione #311\nrimuovere la dipendenza da nodo-fetch non necessaria",
|
|
92
|
-
"es": "**FIXED**: Invalid jsonConfig schema - eliminó propiedad no apoyada `doNotSave` de elementos de mesa\n**NEW**: Interfaz de gestión de archivos YAML en admin UI para operaciones de carga/descarga/delete (#369)\nmejorar el registro para #201\nmigración automática desde versiones anteriores a ESPHomeDashboardIntroducción del usuario (pre v0.6.1)\nactualización almohada\narreglar el vínculo readme con la lib\nestado de conexión de fijación #311\neliminar la dependencia sin necesidad",
|
|
93
|
-
"pl": "* *FIXED* *: Nieprawidłowy schemat jsonConfig - usunięte nieobsługiwane właściwości 'doNotSave' z elementów tabeli \n* *NOWOŚĆ* *: Interfejs do zarządzania plikami YAML w UI administratora do wysyłania / pobierania / usuwania operacji (# 369) \npoprawić logowanie dla # 201\nautomatyczna migracja z wersji przed wprowadzeniem ESPHOmeDashboardUrl (pre v0.6.1)\naktualizacji poduszki\nfix readme link do lib\nustaw status połączenia # 311\nusuń niepotrzebną zależność node-get",
|
|
94
|
-
"uk": "**FIXED**: Invalid jsonConfig schema - вилучено непідтримуваний `doNotSave` майно з елементів таблиці\n**NEW**: Інтерфейс керування файлами YAML в адмін UI для завантаження / завантаження / видалення операцій (#369)\nпокращити залогів на #201\nавтоматична міграція з версій до ESPHomeDashboardUrl введення (pre v0.6.1)\nоновлення подушки\nвиправити посилання для читання\nстатус на сервери\nвидалити невидиму залежність від вузла",
|
|
95
|
-
"zh-cn": "** FIXED**: 无效的jsonConfig schema - 从表格元素中删除未支持的“不拯救”属性\n**NEW**:用于上传/下载/删除操作的管理员UI中的YAML文件管理接口(# 369)\n改进201号记录\n从ESPHomeDashboardUrl引入前的版本自动迁移(pre v0.6.1)\n更新枕头\n固定读取链接到 lib\n固定连接状态 # 311\n删除不需要的节点依赖"
|
|
96
96
|
}
|
|
97
97
|
},
|
|
98
98
|
"titleLang": {
|
|
@@ -182,9 +182,11 @@
|
|
|
182
182
|
"apiPassword"
|
|
183
183
|
],
|
|
184
184
|
"native": {
|
|
185
|
-
"ESPHomeDashboardVersion": "
|
|
186
|
-
"PillowVersion": "
|
|
185
|
+
"ESPHomeDashboardVersion": "2026.6.5",
|
|
186
|
+
"PillowVersion": "12.2.0",
|
|
187
187
|
"reconnectInterval": 30,
|
|
188
|
+
"pingInterval": 5,
|
|
189
|
+
"pingAttempts": 1,
|
|
188
190
|
"apiPass": "",
|
|
189
191
|
"apiPassword": "",
|
|
190
192
|
"encryptionKey": "",
|
package/lib/dashboardApi.js
CHANGED
|
@@ -58,11 +58,16 @@ async function request(method, dashboardUrl, path, params) {
|
|
|
58
58
|
async function streamLogs(dashboardUrl, path, spawnParams, lineReceivedCb) {
|
|
59
59
|
const wsUrl = `${dashboardUrl.replace(/\/+$/, '').replace(/^http/, 'ws')}/${path}`;
|
|
60
60
|
|
|
61
|
-
// Prefer native WebSocket (Node >= 22), but fall back to "ws" package on older Node versions
|
|
61
|
+
// Prefer native WebSocket (Node >= 22), but fall back to "ws" package on older Node versions.
|
|
62
|
+
// Both constructors are used interchangeably here although they expose different APIs
|
|
63
|
+
// (addEventListener vs. on), which is why the two branches below exist and why the instance
|
|
64
|
+
// cannot be described by a single type.
|
|
62
65
|
const hasNativeWebSocket = typeof WebSocket !== 'undefined';
|
|
63
|
-
let WebSocketImpl
|
|
66
|
+
let WebSocketImpl;
|
|
64
67
|
|
|
65
|
-
if (
|
|
68
|
+
if (hasNativeWebSocket) {
|
|
69
|
+
WebSocketImpl = WebSocket;
|
|
70
|
+
} else {
|
|
66
71
|
try {
|
|
67
72
|
WebSocketImpl = require('ws');
|
|
68
73
|
} catch {
|
package/lib/helpers.js
CHANGED
|
@@ -16,13 +16,18 @@ class DeviceInfo {
|
|
|
16
16
|
connected = false;
|
|
17
17
|
connecting = false;
|
|
18
18
|
connectionError = false;
|
|
19
|
+
connectionDestroyedLogged = false;
|
|
19
20
|
connectStatus = 'Initialisation needed';
|
|
20
21
|
deletionRequested = false;
|
|
22
|
+
/** @type {string | null} */
|
|
21
23
|
deviceFriendlyName = null;
|
|
22
24
|
deviceInfo = null;
|
|
25
|
+
/** @type {string | null} */
|
|
23
26
|
deviceName = null;
|
|
24
27
|
initialized = false;
|
|
28
|
+
/** @type {string | null} */
|
|
25
29
|
ip = null;
|
|
30
|
+
/** @type {string | null} */
|
|
26
31
|
mac = null;
|
|
27
32
|
}
|
|
28
33
|
|
package/lib/stateAttr.js
CHANGED
|
@@ -96,6 +96,13 @@ const stateAttrb = {
|
|
|
96
96
|
write: true,
|
|
97
97
|
role: 'switch',
|
|
98
98
|
},
|
|
99
|
+
versionPinMigrationDone: {
|
|
100
|
+
name: 'Migration to pinned dashboard versions applied',
|
|
101
|
+
type: 'boolean',
|
|
102
|
+
read: true,
|
|
103
|
+
write: false,
|
|
104
|
+
role: 'indicator',
|
|
105
|
+
},
|
|
99
106
|
};
|
|
100
107
|
|
|
101
108
|
module.exports = stateAttrb;
|
package/lib/yamlFileManager.js
CHANGED
|
@@ -163,10 +163,11 @@ class YamlFileManager {
|
|
|
163
163
|
message: `File ${filename} uploaded successfully`,
|
|
164
164
|
};
|
|
165
165
|
} catch (error) {
|
|
166
|
+
const reason = error instanceof Error && error.message ? error.message : String(error);
|
|
166
167
|
this.adapter.log.error(`[yamlFileManager.uploadYamlFile] ${error}`);
|
|
167
168
|
return {
|
|
168
169
|
success: false,
|
|
169
|
-
error: `Failed to upload file: ${
|
|
170
|
+
error: `Failed to upload file: ${reason}`,
|
|
170
171
|
};
|
|
171
172
|
}
|
|
172
173
|
}
|
|
@@ -208,10 +209,11 @@ class YamlFileManager {
|
|
|
208
209
|
filename: filename,
|
|
209
210
|
};
|
|
210
211
|
} catch (error) {
|
|
212
|
+
const reason = error instanceof Error && error.message ? error.message : String(error);
|
|
211
213
|
this.adapter.log.error(`[yamlFileManager.downloadYamlFile] ${error}`);
|
|
212
214
|
return {
|
|
213
215
|
success: false,
|
|
214
|
-
error: `Failed to download file: ${
|
|
216
|
+
error: `Failed to download file: ${reason}`,
|
|
215
217
|
};
|
|
216
218
|
}
|
|
217
219
|
}
|
|
@@ -253,10 +255,11 @@ class YamlFileManager {
|
|
|
253
255
|
message: `File ${filename} deleted successfully`,
|
|
254
256
|
};
|
|
255
257
|
} catch (error) {
|
|
258
|
+
const reason = error instanceof Error && error.message ? error.message : String(error);
|
|
256
259
|
this.adapter.log.error(`[yamlFileManager.deleteYamlFile] ${error}`);
|
|
257
260
|
return {
|
|
258
261
|
success: false,
|
|
259
|
-
error: `Failed to delete file: ${
|
|
262
|
+
error: `Failed to delete file: ${reason}`,
|
|
260
263
|
};
|
|
261
264
|
}
|
|
262
265
|
}
|
package/main.js
CHANGED
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
const utils = require('@iobroker/adapter-core');
|
|
10
10
|
const clientDevice = require('./lib/helpers.js');
|
|
11
11
|
const YamlFileManager = require('./lib/yamlFileManager.js');
|
|
12
|
-
// @ts-expect-error Client is just missing in index.d.ts file
|
|
13
12
|
const { Client, Discovery } = require('@2colors/esphome-native-api');
|
|
14
13
|
const stateAttr = require(`${__dirname}/lib/stateAttr.js`); // Load attribute library
|
|
15
14
|
const disableSentry = false; // Ensure to set to true during development!
|
|
@@ -23,6 +22,17 @@ const clientDetails = {}; // Memory cache of all devices and their connection st
|
|
|
23
22
|
const newlyDiscoveredClient = {}; // Memory cache of all newly discovered devices and their connection status
|
|
24
23
|
const dashboardVersions = [];
|
|
25
24
|
const pillowVersions = []; // Memory cache for available Pillow versions
|
|
25
|
+
|
|
26
|
+
// Versions installations are pinned to. ESPHome 2026.7.x currently fails to install for a part of
|
|
27
|
+
// our users (#463), so both the defaults for new installations and the one-time migration in
|
|
28
|
+
// migratePinnedVersions() point at the last combination known to work instead of
|
|
29
|
+
// "Always last available". Keep in sync with the "native" defaults in io-package.json.
|
|
30
|
+
const defaultDashboardVersion = '2026.6.5';
|
|
31
|
+
const defaultPillowVersion = '12.2.0';
|
|
32
|
+
|
|
33
|
+
// State holding whether the one-time migration away from "Always last available" already ran.
|
|
34
|
+
const versionMigrationState = '_ESPHomeDashboard.versionPinMigrationDone';
|
|
35
|
+
|
|
26
36
|
class Esphome extends utils.Adapter {
|
|
27
37
|
/**
|
|
28
38
|
* @param {Partial<utils.AdapterOptions>} [options] - Adapter configuration options
|
|
@@ -55,6 +65,9 @@ class Esphome extends utils.Adapter {
|
|
|
55
65
|
// Migrate from older adapter versions that only had ESPHomeDashboardIP
|
|
56
66
|
await this.migrateConfig();
|
|
57
67
|
|
|
68
|
+
// Move installations still on "Always last available" to the pinned versions, once
|
|
69
|
+
await this.migratePinnedVersions();
|
|
70
|
+
|
|
58
71
|
//ToDo: store default data into clientDetails object instead of global variable
|
|
59
72
|
// Store settings in global variables
|
|
60
73
|
// defaultApiPass = this.config.apiPass;
|
|
@@ -152,8 +165,105 @@ class Esphome extends utils.Adapter {
|
|
|
152
165
|
// adapter will restart
|
|
153
166
|
}
|
|
154
167
|
} catch (error) {
|
|
168
|
+
const reason = error instanceof Error && error.message ? error.message : String(error);
|
|
155
169
|
this.log.error(
|
|
156
|
-
`Error during configuration migration from ESPHomeDashboardIP to ESPHomeDashboardUrl: ${
|
|
170
|
+
`Error during configuration migration from ESPHomeDashboardIP to ESPHomeDashboardUrl: ${reason}`,
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Migrate installations which are still on "Always last available" to the pinned versions, once.
|
|
177
|
+
*
|
|
178
|
+
* The pinned defaults in io-package.json only apply to new installations, so without this every
|
|
179
|
+
* existing installation would stay on a release line that currently cannot be installed (#463)
|
|
180
|
+
* and would have to be corrected by hand.
|
|
181
|
+
*
|
|
182
|
+
* Whether the migration already ran is stored in a state, not in "native": a new installation
|
|
183
|
+
* already ships the pinned defaults, and a "native" marker would be seeded into every existing
|
|
184
|
+
* instance on upgrade, so neither could tell "not migrated yet" from "nothing to migrate".
|
|
185
|
+
* Once the marker is set, a deliberate switch back to "Always last available" is never touched.
|
|
186
|
+
*/
|
|
187
|
+
async migratePinnedVersions() {
|
|
188
|
+
try {
|
|
189
|
+
await this.extendObjectAsync('_ESPHomeDashboard', {
|
|
190
|
+
type: 'channel',
|
|
191
|
+
common: {
|
|
192
|
+
name: 'ESPHome Dashboard details',
|
|
193
|
+
},
|
|
194
|
+
native: {},
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const migrationDone = await this.getStateAsync(versionMigrationState);
|
|
198
|
+
if (migrationDone && migrationDone.val === true) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** @type {Record<string, string>} */
|
|
203
|
+
const migrated = {};
|
|
204
|
+
if (this.config.ESPHomeDashboardVersion === 'Always last available') {
|
|
205
|
+
migrated.ESPHomeDashboardVersion = defaultDashboardVersion;
|
|
206
|
+
}
|
|
207
|
+
if (this.config.PillowVersion === 'Always last available') {
|
|
208
|
+
migrated.PillowVersion = defaultPillowVersion;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// New installation, or the versions were already pinned by hand: only remember that the
|
|
212
|
+
// migration is done so a later switch to "Always last available" is respected.
|
|
213
|
+
if (!Object.keys(migrated).length) {
|
|
214
|
+
await this.stateSetCreate(versionMigrationState, 'versionPinMigrationDone', true);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const adapterObj = await this.getForeignObjectAsync(`system.adapter.${this.namespace}`);
|
|
219
|
+
if (!adapterObj || !adapterObj.native) {
|
|
220
|
+
this.log.error(
|
|
221
|
+
`Migration to pinned versions failed: could not read the configuration object of ${this.namespace}. Please select a fixed ESPHome Dashboard version in the adapter configuration.`,
|
|
222
|
+
);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const changes = Object.entries(migrated)
|
|
227
|
+
.map(([key, value]) => `${key} = ${value}`)
|
|
228
|
+
.join(', ');
|
|
229
|
+
this.log.info(
|
|
230
|
+
`Not every ESPHome release can be installed on every system, so your configuration has been migrated once from "Always last available" to the last combination known to work: ${changes}. Select "Always last available" again in the adapter configuration if you prefer to follow the newest release, this migration only runs once.`,
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
Object.assign(adapterObj.native, migrated);
|
|
234
|
+
// Keep this run consistent until the restart caused by the write below kicks in
|
|
235
|
+
Object.assign(this.config, migrated);
|
|
236
|
+
|
|
237
|
+
await this.setForeignObject(adapterObj._id, adapterObj);
|
|
238
|
+
// Written after the configuration: should the write above fail, the migration is retried
|
|
239
|
+
// on the next start instead of being silently skipped forever
|
|
240
|
+
await this.stateSetCreate(versionMigrationState, 'versionPinMigrationDone', true);
|
|
241
|
+
|
|
242
|
+
// adapter will restart
|
|
243
|
+
} catch (error) {
|
|
244
|
+
this.errorHandler(`[migratePinnedVersions]`, error);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Warn when a version is left at "Always last available". Such a setup can break without any
|
|
250
|
+
* change on the user side as soon as an ESPHome release cannot be installed (#463). Reached only
|
|
251
|
+
* when the user selected it deliberately, migratePinnedVersions() moves existing installations
|
|
252
|
+
* off that setting once.
|
|
253
|
+
*
|
|
254
|
+
* @param {string} useDashBoardVersion - ESPHome Dashboard version which will be installed
|
|
255
|
+
* @param {string} usePillowVersion - Pillow version which will be installed
|
|
256
|
+
*/
|
|
257
|
+
warnUnpinnedVersions(useDashBoardVersion, usePillowVersion) {
|
|
258
|
+
if (this.config.ESPHomeDashboardVersion === 'Always last available') {
|
|
259
|
+
this.log.warn(
|
|
260
|
+
`ESPHome Dashboard version is set to "Always last available" and resolves to ${useDashBoardVersion || 'the newest release'}. Not every ESPHome release can be installed on every system. If the dashboard does not start, select a fixed version in the adapter configuration, ${defaultDashboardVersion} is known to work.`,
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (this.config.PillowVersion === 'Always last available') {
|
|
265
|
+
this.log.warn(
|
|
266
|
+
`Pillow version is set to "Always last available" and resolves to ${usePillowVersion || 'the newest release'}. If the dashboard does not start, select a fixed version in the adapter configuration, ${defaultPillowVersion} is known to work.`,
|
|
157
267
|
);
|
|
158
268
|
}
|
|
159
269
|
}
|
|
@@ -211,7 +321,7 @@ class Esphome extends utils.Adapter {
|
|
|
211
321
|
);
|
|
212
322
|
let cachedVersions = await this.getStateAsync(`_ESPHomeDashboard.versionCache`);
|
|
213
323
|
if (cachedVersions && cachedVersions.val) {
|
|
214
|
-
cachedVersions = JSON.parse(cachedVersions.val);
|
|
324
|
+
cachedVersions = JSON.parse(String(cachedVersions.val));
|
|
215
325
|
for (const version in cachedVersions) {
|
|
216
326
|
dashboardVersions.push(cachedVersions[version].name);
|
|
217
327
|
}
|
|
@@ -266,11 +376,16 @@ class Esphome extends utils.Adapter {
|
|
|
266
376
|
// Start Dashboard Process
|
|
267
377
|
if (this.config.ESPHomeDashboardEnabled) {
|
|
268
378
|
this.log.info(`Native Integration of ESPHome Dashboard enabled, making environment ready`);
|
|
379
|
+
|
|
380
|
+
this.warnUnpinnedVersions(useDashBoardVersion, usePillowVersion);
|
|
269
381
|
try {
|
|
270
|
-
// @ts-expect-error autopy types are incomplete
|
|
271
382
|
const { getVenv } = await import('autopy');
|
|
272
383
|
let python;
|
|
273
384
|
try {
|
|
385
|
+
// "version" must be present on every requirement even when no version is
|
|
386
|
+
// pinned: autopy builds the pip argument as `${name}${version}` and passes
|
|
387
|
+
// it to pep440 satisfies(), both of which break on undefined. An empty
|
|
388
|
+
// specifier means "any version" for both.
|
|
274
389
|
const requirements = [];
|
|
275
390
|
if (useDashBoardVersion) {
|
|
276
391
|
requirements.push({ name: 'esphome', version: `==${useDashBoardVersion}` });
|
|
@@ -278,7 +393,7 @@ class Esphome extends utils.Adapter {
|
|
|
278
393
|
this.log.warn(
|
|
279
394
|
`Unable to determine a specific ESPHome dashboard version. Falling back to latest available version from PyPI`,
|
|
280
395
|
);
|
|
281
|
-
requirements.push({ name: 'esphome' });
|
|
396
|
+
requirements.push({ name: 'esphome', version: '' });
|
|
282
397
|
}
|
|
283
398
|
if (usePillowVersion) {
|
|
284
399
|
requirements.push({ name: 'pillow', version: `==${usePillowVersion}` });
|
|
@@ -286,7 +401,7 @@ class Esphome extends utils.Adapter {
|
|
|
286
401
|
this.log.warn(
|
|
287
402
|
`Unable to determine a specific Pillow version. Falling back to latest available version from PyPI`,
|
|
288
403
|
);
|
|
289
|
-
requirements.push({ name: 'pillow' });
|
|
404
|
+
requirements.push({ name: 'pillow', version: '' });
|
|
290
405
|
}
|
|
291
406
|
// Create a virtual environment with esphome installed.
|
|
292
407
|
python = await getVenv({
|
|
@@ -295,7 +410,8 @@ class Esphome extends utils.Adapter {
|
|
|
295
410
|
requirements,
|
|
296
411
|
});
|
|
297
412
|
} catch (error) {
|
|
298
|
-
|
|
413
|
+
const stack = error instanceof Error ? error.stack : undefined;
|
|
414
|
+
this.log.error(`Fatal error starting ESPHomeDashboard | ${error} | ${stack}`);
|
|
299
415
|
return;
|
|
300
416
|
}
|
|
301
417
|
|
|
@@ -321,7 +437,7 @@ class Esphome extends utils.Adapter {
|
|
|
321
437
|
dashboardProcess = python('esphome', [
|
|
322
438
|
'dashboard',
|
|
323
439
|
'--port',
|
|
324
|
-
this.config.ESPHomeDashboardPort,
|
|
440
|
+
String(this.config.ESPHomeDashboardPort),
|
|
325
441
|
`${dataDir}esphome.${this.instance}`,
|
|
326
442
|
]);
|
|
327
443
|
|
|
@@ -523,6 +639,8 @@ class Esphome extends utils.Adapter {
|
|
|
523
639
|
this.log.info(`ESPHome client ${host} connected`);
|
|
524
640
|
// Clear possible present warning messages for devices from previous connection
|
|
525
641
|
delete warnMessages[host];
|
|
642
|
+
// Allow the next connection-loss event to log a "Connection destroyed" warning again
|
|
643
|
+
clientDetails[host].connectionDestroyedLogged = false;
|
|
526
644
|
|
|
527
645
|
// Check if device connection is caused by adding device from admin, if yes send OK message
|
|
528
646
|
if (this.messageResponse[host]) {
|
|
@@ -636,7 +754,6 @@ class Esphome extends utils.Adapter {
|
|
|
636
754
|
statusStates: {
|
|
637
755
|
onlineId: `${this.namespace}.${deviceName}.info._online`,
|
|
638
756
|
},
|
|
639
|
-
// @ts-expect-error js-controller issue - desc should be string but friendlyName may be undefined
|
|
640
757
|
desc: deviceInfo.friendlyName,
|
|
641
758
|
},
|
|
642
759
|
native: {
|
|
@@ -804,8 +921,19 @@ class Esphome extends utils.Adapter {
|
|
|
804
921
|
|
|
805
922
|
await this.createNonStateDevices(host, entity);
|
|
806
923
|
|
|
807
|
-
// Request current state values
|
|
808
|
-
|
|
924
|
+
// Request current state values. If the connection dropped while entities were
|
|
925
|
+
// still being announced, `newEntity` fires again on the stale connection and
|
|
926
|
+
// subscribeStatesService() throws "Not connected". Skip it silently here; the
|
|
927
|
+
// reconnection cycle handles the fresh subscription.
|
|
928
|
+
if (clientDetails[host].client.connection.connected) {
|
|
929
|
+
try {
|
|
930
|
+
await clientDetails[host].client.connection.subscribeStatesService();
|
|
931
|
+
} catch (e) {
|
|
932
|
+
this.log.debug(
|
|
933
|
+
`[subscribeStates] ${clientDetails[host].deviceFriendlyName}: ${e instanceof Error ? e.message : e}`,
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
809
937
|
this.log.debug(
|
|
810
938
|
`[DeviceInfoData] ${clientDetails[host].deviceFriendlyName} ${JSON.stringify(clientDetails[host].deviceInfo)}`,
|
|
811
939
|
);
|
|
@@ -929,6 +1057,14 @@ class Esphome extends utils.Adapter {
|
|
|
929
1057
|
|
|
930
1058
|
entity.on(`destroyed`, async state => {
|
|
931
1059
|
try {
|
|
1060
|
+
// `destroyed` fires once per entity when the connection goes down.
|
|
1061
|
+
// Log only the first one per connection-loss event and let the
|
|
1062
|
+
// 'disconnected' handler summarize the device that went away.
|
|
1063
|
+
if (clientDetails[host].connectionDestroyedLogged) {
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
clientDetails[host].connectionDestroyedLogged = true;
|
|
1067
|
+
|
|
932
1068
|
const entityIdentity = entity?.name || entity?.id || host || 'unknown';
|
|
933
1069
|
if (state !== undefined) {
|
|
934
1070
|
this.log.warn(`Connection destroyed for ${state} (${entityIdentity})`);
|
|
@@ -944,7 +1080,8 @@ class Esphome extends utils.Adapter {
|
|
|
944
1080
|
this.log.error(`Entity error: ${name}`);
|
|
945
1081
|
});
|
|
946
1082
|
} catch (e) {
|
|
947
|
-
|
|
1083
|
+
const stack = e instanceof Error ? e.stack : undefined;
|
|
1084
|
+
this.log.error(`Connection issue for ${entity.name} ${e} | ${stack}`);
|
|
948
1085
|
}
|
|
949
1086
|
});
|
|
950
1087
|
|
|
@@ -1030,7 +1167,8 @@ class Esphome extends utils.Adapter {
|
|
|
1030
1167
|
);
|
|
1031
1168
|
}
|
|
1032
1169
|
} catch (e) {
|
|
1033
|
-
|
|
1170
|
+
const stack = e instanceof Error ? e.stack : undefined;
|
|
1171
|
+
this.log.error(`ESP device error for ${host} | ${e} | ${stack}`);
|
|
1034
1172
|
}
|
|
1035
1173
|
}
|
|
1036
1174
|
|
|
@@ -1412,7 +1550,6 @@ class Esphome extends utils.Adapter {
|
|
|
1412
1550
|
common.write !== this.createdStatesDetails[objName].write))
|
|
1413
1551
|
) {
|
|
1414
1552
|
// console.log(`An attribute has changed : ${state}`);
|
|
1415
|
-
// @ts-expect-error values are correctly provided by state Attribute definitions, error can be ignored
|
|
1416
1553
|
await this.extendObjectAsync(objName, {
|
|
1417
1554
|
type: 'state',
|
|
1418
1555
|
common,
|
|
@@ -1443,7 +1580,7 @@ class Esphome extends utils.Adapter {
|
|
|
1443
1580
|
* Handles error messages for log and Sentry
|
|
1444
1581
|
*
|
|
1445
1582
|
* @param {string} codepart Function were exception occurred
|
|
1446
|
-
* @param {
|
|
1583
|
+
* @param {unknown} error Caught value, anything can be thrown in JavaScript
|
|
1447
1584
|
*/
|
|
1448
1585
|
errorHandler(codepart, error) {
|
|
1449
1586
|
let errorMsg = error;
|
|
@@ -1490,6 +1627,17 @@ class Esphome extends utils.Adapter {
|
|
|
1490
1627
|
return str.replace(new RegExp(this.escapeRegExp(find), 'g'), replace);
|
|
1491
1628
|
}
|
|
1492
1629
|
|
|
1630
|
+
/**
|
|
1631
|
+
* Extract the argument of a modify method, so "multiply(2.5)" yields "2.5"
|
|
1632
|
+
*
|
|
1633
|
+
* @param {string} method defines the method to be executed (e.g. round())
|
|
1634
|
+
* @returns {string} text between the brackets, empty when the method has no argument
|
|
1635
|
+
*/
|
|
1636
|
+
modifyArgument(method) {
|
|
1637
|
+
const inBracket = method.match(/(?<=\()(.*?)(?=\))/);
|
|
1638
|
+
return inBracket ? inBracket[0] : '';
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1493
1641
|
/**
|
|
1494
1642
|
* Analysis modify an element in stateAttr.js and execute command
|
|
1495
1643
|
*
|
|
@@ -1505,24 +1653,24 @@ class Esphome extends utils.Adapter {
|
|
|
1505
1653
|
value = eval(method.replace(/^custom:/gi, '')); //get value without "custom:"
|
|
1506
1654
|
} else if (method.match(/^multiply\(/gi) != null) {
|
|
1507
1655
|
//check if starts with "multiply("
|
|
1508
|
-
const inBracket = parseFloat(
|
|
1509
|
-
value = value * inBracket;
|
|
1656
|
+
const inBracket = parseFloat(this.modifyArgument(method)); //get value in brackets
|
|
1657
|
+
value = Number(value) * inBracket;
|
|
1510
1658
|
} else if (method.match(/^divide\(/gi) != null) {
|
|
1511
1659
|
//check if starts with "divide("
|
|
1512
|
-
const inBracket = parseFloat(
|
|
1513
|
-
value = value / inBracket;
|
|
1660
|
+
const inBracket = parseFloat(this.modifyArgument(method)); //get value in brackets
|
|
1661
|
+
value = Number(value) / inBracket;
|
|
1514
1662
|
} else if (method.match(/^round\(/gi) != null) {
|
|
1515
1663
|
//check if starts with "round("
|
|
1516
|
-
const inBracket = parseInt(
|
|
1517
|
-
value = Math.round(value * Math.pow(10, inBracket)) / Math.pow(10, inBracket);
|
|
1664
|
+
const inBracket = parseInt(this.modifyArgument(method)); //get value in brackets
|
|
1665
|
+
value = Math.round(Number(value) * Math.pow(10, inBracket)) / Math.pow(10, inBracket);
|
|
1518
1666
|
} else if (method.match(/^add\(/gi) != null) {
|
|
1519
1667
|
//check if starts with "add("
|
|
1520
|
-
const inBracket = parseFloat(
|
|
1521
|
-
value = parseFloat(value) + inBracket;
|
|
1668
|
+
const inBracket = parseFloat(this.modifyArgument(method)); //get value in brackets
|
|
1669
|
+
value = parseFloat(String(value)) + inBracket;
|
|
1522
1670
|
} else if (method.match(/^substract\(/gi) != null) {
|
|
1523
1671
|
//check if starts with "substract("
|
|
1524
|
-
const inBracket = parseFloat(
|
|
1525
|
-
value = parseFloat(value) - inBracket;
|
|
1672
|
+
const inBracket = parseFloat(this.modifyArgument(method)); //get value in brackets
|
|
1673
|
+
value = parseFloat(String(value)) - inBracket;
|
|
1526
1674
|
} else {
|
|
1527
1675
|
const methodUC = method.toUpperCase();
|
|
1528
1676
|
switch (methodUC) {
|
|
@@ -1725,6 +1873,9 @@ class Esphome extends utils.Adapter {
|
|
|
1725
1873
|
value: dashboardVersions[versions],
|
|
1726
1874
|
});
|
|
1727
1875
|
}
|
|
1876
|
+
// The version new installations default to must always be selectable, also
|
|
1877
|
+
// when the release list could not be retrieved from GitHub
|
|
1878
|
+
this.ensureVersionInDropDown(dropDownEntry, defaultDashboardVersion);
|
|
1728
1879
|
|
|
1729
1880
|
this.sendTo(obj.from, obj.command, dropDownEntry, obj.callback);
|
|
1730
1881
|
}
|
|
@@ -1747,7 +1898,7 @@ class Esphome extends utils.Adapter {
|
|
|
1747
1898
|
} else {
|
|
1748
1899
|
// Fallback versions if cache is empty
|
|
1749
1900
|
this.log.info('No cached Pillow versions available, using fallback versions');
|
|
1750
|
-
const fallbackVersions = ['
|
|
1901
|
+
const fallbackVersions = ['12.2.0', '12.1.1', '12.0.0', '11.3.0', '11.2.0', '11.1.0'];
|
|
1751
1902
|
for (const version of fallbackVersions) {
|
|
1752
1903
|
dropDownEntry.push({
|
|
1753
1904
|
label: version,
|
|
@@ -1755,6 +1906,9 @@ class Esphome extends utils.Adapter {
|
|
|
1755
1906
|
});
|
|
1756
1907
|
}
|
|
1757
1908
|
}
|
|
1909
|
+
// The version new installations default to must always be selectable, also
|
|
1910
|
+
// when the release list could not be retrieved from PyPI
|
|
1911
|
+
this.ensureVersionInDropDown(dropDownEntry, defaultPillowVersion);
|
|
1758
1912
|
|
|
1759
1913
|
this.sendTo(obj.from, obj.command, dropDownEntry, obj.callback);
|
|
1760
1914
|
}
|
|
@@ -1988,18 +2142,32 @@ class Esphome extends utils.Adapter {
|
|
|
1988
2142
|
}
|
|
1989
2143
|
}
|
|
1990
2144
|
|
|
2145
|
+
/**
|
|
2146
|
+
* Add a version to a dropDown when it is not part of it yet, so a configured version can never
|
|
2147
|
+
* silently disappear from the selection when the release list is unavailable or outdated
|
|
2148
|
+
*
|
|
2149
|
+
* @param {Array<string | {label: string, value: string}>} dropDownEntry - dropDown entries to extend
|
|
2150
|
+
* @param {string} version - version which must be selectable
|
|
2151
|
+
*/
|
|
2152
|
+
ensureVersionInDropDown(dropDownEntry, version) {
|
|
2153
|
+
const known = dropDownEntry.some(entry => (typeof entry === 'string' ? entry : entry.value) === version);
|
|
2154
|
+
if (!known) {
|
|
2155
|
+
dropDownEntry.push({ label: version, value: version });
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
|
|
1991
2159
|
/**
|
|
1992
2160
|
* Fetch Pillow versions from PyPI and cache them
|
|
1993
2161
|
*
|
|
1994
2162
|
* @returns {Promise<string[]>} Array of available Pillow versions
|
|
1995
2163
|
*/
|
|
1996
2164
|
async fetchAndCachePillowVersions() {
|
|
1997
|
-
const fallbackVersions = ['
|
|
2165
|
+
const fallbackVersions = ['12.2.0', '12.1.1', '12.0.0', '11.3.0', '11.2.0', '11.1.0'];
|
|
1998
2166
|
|
|
1999
2167
|
try {
|
|
2000
2168
|
const response = await fetch('https://pypi.org/pypi/pillow/json');
|
|
2001
2169
|
if (response.ok) {
|
|
2002
|
-
const data = await response.json();
|
|
2170
|
+
const data = /** @type {{ releases: Record<string, unknown> }} */ (await response.json());
|
|
2003
2171
|
const versions = Object.keys(data.releases)
|
|
2004
2172
|
.filter(v => !v.includes('a') && !v.includes('b') && !v.includes('rc')) // Filter out alpha/beta/rc versions
|
|
2005
2173
|
.sort((a, b) => {
|
|
@@ -2037,16 +2205,15 @@ class Esphome extends utils.Adapter {
|
|
|
2037
2205
|
this.log.warn(`Unable to fetch Pillow versions from PyPI: ${response.status}, using cached values`);
|
|
2038
2206
|
}
|
|
2039
2207
|
} catch (error) {
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
);
|
|
2208
|
+
const reason = error instanceof Error && error.message ? error.message : String(error);
|
|
2209
|
+
this.log.warn(`Error fetching Pillow versions from PyPI: ${reason}, using cached or fallback versions`);
|
|
2043
2210
|
}
|
|
2044
2211
|
|
|
2045
2212
|
// Try to load from cache
|
|
2046
2213
|
try {
|
|
2047
2214
|
const cachedPillowVersions = await this.getStateAsync(`_ESPHomeDashboard.pillowVersionCache`);
|
|
2048
2215
|
if (cachedPillowVersions && cachedPillowVersions.val) {
|
|
2049
|
-
const versions = JSON.parse(cachedPillowVersions.val);
|
|
2216
|
+
const versions = JSON.parse(String(cachedPillowVersions.val));
|
|
2050
2217
|
this.log.info(`Loaded ${versions.length} Pillow versions from cache`);
|
|
2051
2218
|
return versions;
|
|
2052
2219
|
}
|
|
@@ -2078,7 +2245,8 @@ class Esphome extends utils.Adapter {
|
|
|
2078
2245
|
this.log.info('Autopy cache directory does not exist');
|
|
2079
2246
|
}
|
|
2080
2247
|
} catch (error) {
|
|
2081
|
-
|
|
2248
|
+
const reason = error instanceof Error && error.message ? error.message : String(error);
|
|
2249
|
+
this.log.error(`Error clearing autopy cache: ${reason}`);
|
|
2082
2250
|
throw error;
|
|
2083
2251
|
}
|
|
2084
2252
|
}
|
|
@@ -2256,7 +2424,7 @@ class Esphome extends utils.Adapter {
|
|
|
2256
2424
|
device[5] === `warmWhite`
|
|
2257
2425
|
) {
|
|
2258
2426
|
// Convert value to 255 range
|
|
2259
|
-
writeValue = writeValue / 100 / 2.55;
|
|
2427
|
+
writeValue = Number(writeValue) / 100 / 2.55;
|
|
2260
2428
|
|
|
2261
2429
|
// Store value to memory
|
|
2262
2430
|
clientDetails[deviceIP][device[4]].states[device[5]] = writeValue;
|
|
@@ -2309,7 +2477,7 @@ class Esphome extends utils.Adapter {
|
|
|
2309
2477
|
// Auto white channel: when colorHEX is set to white (#ffffff) on RGBW lights,
|
|
2310
2478
|
// automatically switch to white channel; otherwise switch to RGB mode
|
|
2311
2479
|
if (clientDetails[deviceIP][device[4]].rgbAutoWhite && supportsWhite && device[5] === 'colorHEX') {
|
|
2312
|
-
if (writeValue.replace(/^#/, '').toLowerCase() === 'ffffff') {
|
|
2480
|
+
if (String(writeValue).replace(/^#/, '').toLowerCase() === 'ffffff') {
|
|
2313
2481
|
// White color detected: redirect to dedicated white channel
|
|
2314
2482
|
clientDetails[deviceIP][device[4]].states.red = 0;
|
|
2315
2483
|
clientDetails[deviceIP][device[4]].states.green = 0;
|
|
@@ -2438,7 +2606,6 @@ class Esphome extends utils.Adapter {
|
|
|
2438
2606
|
const _channels = await this.getObjectViewAsync('system', 'channel', params);
|
|
2439
2607
|
// List all found channels & compare with memory, delete unneeded channels
|
|
2440
2608
|
for (const currDevice in _channels.rows) {
|
|
2441
|
-
// @ts-expect-error _channels.rows is an array but treated as object here
|
|
2442
2609
|
if (
|
|
2443
2610
|
!clientDetails[ip].adapterObjects.channels.includes(_channels.rows[currDevice].id) &&
|
|
2444
2611
|
_channels.rows[currDevice].id.split('.')[2] === clientDetails[ip].deviceName
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.esphome",
|
|
3
|
-
"version": "1.0.0
|
|
3
|
+
"version": "1.0.0",
|
|
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",
|
|
@@ -24,27 +24,27 @@
|
|
|
24
24
|
],
|
|
25
25
|
"repository": {
|
|
26
26
|
"type": "git",
|
|
27
|
-
"url": "https://github.com/DrozmotiX/ioBroker.esphome"
|
|
27
|
+
"url": "git+https://github.com/DrozmotiX/ioBroker.esphome.git"
|
|
28
28
|
},
|
|
29
29
|
"engines": {
|
|
30
30
|
"node": ">= 22"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@2colors/esphome-native-api": "^1.3.6",
|
|
34
|
-
"@iobroker/adapter-core": "^3.4.
|
|
34
|
+
"@iobroker/adapter-core": "^3.4.3",
|
|
35
35
|
"autopy": "github:SimonFischer04/autopy#fbe016f35e13e335e1c44783a1e7273bbb2a08c1",
|
|
36
|
-
"ws": "^8.21.
|
|
36
|
+
"ws": "^8.21.3"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@alcalzone/release-script": "^5.2.1",
|
|
40
40
|
"@alcalzone/release-script-plugin-iobroker": "^5.2.0",
|
|
41
|
-
"@alcalzone/release-script-plugin-license": "^5.2.
|
|
41
|
+
"@alcalzone/release-script-plugin-license": "^5.2.2",
|
|
42
42
|
"@alcalzone/release-script-plugin-manual-review": "^5.2.0",
|
|
43
43
|
"@iobroker/adapter-dev": "^1.5.0",
|
|
44
44
|
"@iobroker/dev-server": "^0.8.0",
|
|
45
45
|
"@iobroker/eslint-config": "^2.3.4",
|
|
46
|
-
"@iobroker/testing": "^
|
|
47
|
-
"@tsconfig/node22": "^22.0.
|
|
46
|
+
"@iobroker/testing": "^6.2.2",
|
|
47
|
+
"@tsconfig/node22": "^22.0.6",
|
|
48
48
|
"@types/node": "^22.0.5",
|
|
49
49
|
"promisify": "~0.0.3",
|
|
50
50
|
"typescript": "~5.9.3"
|
|
@@ -69,8 +69,8 @@
|
|
|
69
69
|
"check": "tsc --noEmit -p tsconfig.check.json",
|
|
70
70
|
"lint": "eslint .",
|
|
71
71
|
"lint:fix": "eslint . --fix",
|
|
72
|
-
"format": "prettier . --write",
|
|
73
|
-
"format:check": "prettier . --check",
|
|
72
|
+
"format": "prettier . --write --ignore-path .gitignore --ignore-path prettier.ignore",
|
|
73
|
+
"format:check": "prettier . --check --ignore-path .gitignore --ignore-path prettier.ignore",
|
|
74
74
|
"release": "release-script --all --branchPattern '*'",
|
|
75
75
|
"release-dry": "release-script --all --dry"
|
|
76
76
|
},
|