iobroker.esphome 0.7.0-beta.4 → 0.7.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 +40 -8
- package/admin/i18n/en/translations.json +1 -1
- package/admin/i18n/fr/translations.json +1 -1
- package/admin/i18n/it/translations.json +1 -1
- package/admin/i18n/nl/translations.json +1 -1
- package/admin/i18n/pl/translations.json +1 -1
- package/admin/i18n/pt/translations.json +1 -1
- package/admin/i18n/ru/translations.json +1 -1
- package/admin/i18n/uk/translations.json +1 -1
- package/admin/i18n/zh-cn/translations.json +1 -1
- package/admin/jsonConfig.json5 +2 -3
- package/io-package.json +14 -27
- package/lib/dashboardApi.js +317 -0
- package/lib/stateAttr.js +7 -0
- package/lib/yamlFileManager.js +3 -3
- package/main.js +62 -7
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -340,6 +340,39 @@ Example config, for more examples see [The DrozmotiX Docu Page](https://Drozmoti
|
|
|
340
340
|
output: 'gpio_12'
|
|
341
341
|
</details>
|
|
342
342
|
|
|
343
|
+
## Controlling RGBW Lights
|
|
344
|
+
|
|
345
|
+
### RGB vs RGBW — What's the Difference?
|
|
346
|
+
|
|
347
|
+
**RGB lights** use three channels (red, green, blue) to produce colours, including white by mixing all three at maximum. **RGBW lights** add a dedicated fourth white channel (`white`) that provides a cleaner, brighter white than mixing RGB.
|
|
348
|
+
|
|
349
|
+
### Available states for a light entity
|
|
350
|
+
|
|
351
|
+
| State | Description |
|
|
352
|
+
|---|---|
|
|
353
|
+
| `colorHEX` | Writable hex colour string, e.g. `#ff6600`. Writing here updates red/green/blue and sends the command. |
|
|
354
|
+
| `red` / `green` / `blue` | Individual colour channels (0 – 255). |
|
|
355
|
+
| `white` | Dedicated white channel (0 – 255). Only present on RGBW-capable lights. |
|
|
356
|
+
| `brightness` | Overall brightness (0 – 255). |
|
|
357
|
+
| `config.rgbAutoWhite` | **RGBW only** — when set to `true`, writing `#ffffff` to `colorHEX` automatically activates the white channel and sets RGB to zero. Writing any other colour disables the white channel and uses RGB. |
|
|
358
|
+
|
|
359
|
+
### Auto white-channel switching (`rgbAutoWhite`)
|
|
360
|
+
|
|
361
|
+
When an RGBW-capable light is detected (i.e. it exposes a `white` state), the adapter automatically creates a writable `config.rgbAutoWhite` toggle state for that entity. It defaults to `false` (disabled).
|
|
362
|
+
|
|
363
|
+
**To enable:**
|
|
364
|
+
1. Open the ioBroker **Objects** view and navigate to your light entity, e.g. `esphome.0.MyLight.Light.1.config.rgbAutoWhite`.
|
|
365
|
+
2. Set the value to `true`.
|
|
366
|
+
|
|
367
|
+
**Behaviour when enabled:**
|
|
368
|
+
|
|
369
|
+
| `colorHEX` input | Result |
|
|
370
|
+
|---|---|
|
|
371
|
+
| `#ffffff` | `white` → 1 (full), `red` / `green` / `blue` → 0 |
|
|
372
|
+
| Any other colour | `white` → 0, RGB channels set to the colour values |
|
|
373
|
+
|
|
374
|
+
**Behaviour when disabled (default):** the `white` channel is never touched automatically; users control it independently.
|
|
375
|
+
|
|
343
376
|
## Tasmota / ESPEasy migration
|
|
344
377
|
|
|
345
378
|
Migrating from previous Sonoff Tasmota or ESPEasy setups is very easy. You just need to have ESPHome create a binary for you and then upload that in the web interface.
|
|
@@ -360,9 +393,16 @@ If you like my work, please consider a personal donation
|
|
|
360
393
|
### __WORK IN PROGRESS__
|
|
361
394
|
* (DutchmanNL)
|
|
362
395
|
-->
|
|
396
|
+
### 0.7.0 (2026-05-17)
|
|
397
|
+
* (@SimonFischer04) **FIXED** copilot hallucinations
|
|
398
|
+
* (@SimonFischer04) **NEW** 'Always last available' for pillow version
|
|
399
|
+
* (@copilot) **FIXED**: Invalid jsonConfig warning on adapter installation
|
|
400
|
+
* (DutchmanNL) **FIXED**: ESLint errors by code refactoring
|
|
401
|
+
|
|
363
402
|
### 0.7.0-beta.4 (2026-02-21)
|
|
364
403
|
* (DutchmanNL) **FIXED**: ESLint errors by code refactoring
|
|
365
404
|
* (@copilot) **FIXED**: Restore missing `configStates` option in admin UI to allow configuring whether configuration states are shown per entity
|
|
405
|
+
* (@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))
|
|
366
406
|
|
|
367
407
|
### 0.7.0-beta.3 (2026-02-20)
|
|
368
408
|
* (@copilot) **NEW**: Added support for `colorBrightness`, `coldWhite`, `warmWhite`, and `colorMode` states for lights using the new ESPHome color mode API
|
|
@@ -387,14 +427,6 @@ If you like my work, please consider a personal donation
|
|
|
387
427
|
* (@SimonFischer04) automatic migration from versions prior to ESPHomeDashboardUrl introduction (pre v0.6.1)
|
|
388
428
|
* (@copilot) **FIXED**: Invalid jsonConfig schema - removed unsupported `doNotSave` property from table elements
|
|
389
429
|
|
|
390
|
-
### 0.6.3 (2025-09-16)
|
|
391
|
-
* (@DutchmanNL) Fixed an admin error related to `jsonConfig` validation. #287
|
|
392
|
-
* (@DutchmanNL) Various general fixes and dependency updates to improve stability.
|
|
393
|
-
* (@DutchmanNL) Improved responsive design for better usability across devices. #284
|
|
394
|
-
* (@DutchmanNL) Introduced GitHub Actions to automatically verify the ESPHome Dashboard. #290
|
|
395
|
-
* (@DutchmanNL) Added a comprehensive FAQ section to the README to help users with common questions. #286
|
|
396
|
-
* (@DutchmanNL) Updated the `esphome-native-api` library to V1.3.3, which may resolve connection issues. #201
|
|
397
|
-
|
|
398
430
|
## License
|
|
399
431
|
MIT License
|
|
400
432
|
|
package/admin/jsonConfig.json5
CHANGED
|
@@ -668,7 +668,7 @@
|
|
|
668
668
|
"advanced": {
|
|
669
669
|
"type": "panel",
|
|
670
670
|
"label": "Advanced",
|
|
671
|
-
|
|
671
|
+
"collapsable": true,
|
|
672
672
|
"xs": 12,
|
|
673
673
|
"sm": 12,
|
|
674
674
|
"md": 12,
|
|
@@ -827,9 +827,8 @@
|
|
|
827
827
|
// YAML content input
|
|
828
828
|
"uploadContent": {
|
|
829
829
|
"newLine": true,
|
|
830
|
-
"type": "
|
|
830
|
+
"type": "textarea",
|
|
831
831
|
"label": "lblUploadContent",
|
|
832
|
-
"multiline": true,
|
|
833
832
|
"rows": 10,
|
|
834
833
|
"placeholder": "esphome:\n name: my-device\n platform: ESP8266\n board: nodemcuv2",
|
|
835
834
|
"xs": 12,
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "esphome",
|
|
4
|
-
"version": "0.7.0
|
|
4
|
+
"version": "0.7.0",
|
|
5
5
|
"news": {
|
|
6
|
+
"0.7.0": {
|
|
7
|
+
"en": "**FIXED** copilot hallucinations\n**NEW** 'Always last available' for pillow version\n**FIXED**: Invalid jsonConfig warning on adapter installation\n**FIXED**: ESLint errors by code refactoring",
|
|
8
|
+
"de": "**FIXED** Copilot Halluzinationen\n**NEW** 'Always last available' für Kissenversion\n**FIXED**: Invalid jsonConfig Warnung auf Adapterinstallation\n**FIXED**: ESLint-Fehler durch Code-Refactoring",
|
|
9
|
+
"ru": "** Исправленные** галлюцинации копилота\n** «Всегда последний доступный» для подушки\n** Фиксированный**: Недействительное предупреждение jsonConfig об установке адаптера\n**FIXED**: ошибки ESLint при рефакторинге кода",
|
|
10
|
+
"pt": "**FIXED** alucinações copiloto\n** NOVA** 'Sempre última disponível' para a versão do travesseiro\n**FIXED**: Aviso inválido do jsonConfig na instalação do adaptador\n**FIXED**: ESLint errors by code refatoring",
|
|
11
|
+
"nl": "** VASTE** copiloothallucinaties\n**NIEUW** 'Always last available' voor kussenversie\n** VAST**: Ongeldige jsonConfig waarschuwing bij adapter installatie\n** VAST**: ESLint fouten door code refactoring",
|
|
12
|
+
"fr": "**FIXED** hallucinations copilotes\n**NOUVEAU** 'Toujours dernier disponible' pour la version oreiller\n**FIXÉ**: Avertissement jsonConfig non valide sur l'installation de l'adaptateur\n**FIXED**: erreurs ESLint par refactoring de code",
|
|
13
|
+
"it": "**FISSO ** Allucinazioni copilota\n**NEW** 'Always last available' per cuscino versione\n**FISSO ** Avvertenza jsonConfig non valida per l'installazione dell'adattatore\n**FIXED**: Errori di ESLint per refactoring del codice",
|
|
14
|
+
"es": "**FIXED ** copiloto alucinaciones\n**NEW** 'Siempre disponible' para la versión de almohada\n**FIXED**: Invalid jsonConfig warning on adapter installation\n**FIXED**: errores ESLint por refactorización de código",
|
|
15
|
+
"pl": "* *FIXED* * omamy drugiego pilota \n* *NOWOŚĆ* * \"Zawsze ostatni dostępny\" dla wersji poduszki \n* *FIXED* * Nieprawidłowe ostrzeżenie jsonConfig na instalacji adaptera \n** FIXED*",
|
|
16
|
+
"uk": "**FIXED*** копілотні галюцинації\n**NEW**** 'Всі останні доступні' для версії подушки\n**FIXED***: Інвалідний jsonПопередження про встановлення адаптера\n**FIXED***: помилки ESLint від рефакторингу коду",
|
|
17
|
+
"zh-cn": "** 副驾驶幻觉\n** NEW** 枕头版本“ 永远可用”\n(单位:千美元) 在适配器安装上无效的 jsonConfig 警告\n**FIXED**:通过代码重构的ESLint错误"
|
|
18
|
+
},
|
|
6
19
|
"0.7.0-beta.4": {
|
|
7
20
|
"en": "**FIXED**: ESLint errors by code refactoring\n**FIXED**: Restore missing `configStates` option in admin UI to allow configuring whether configuration states are shown per entity",
|
|
8
21
|
"de": "**FIXED**: ESLint-Fehler durch Code-Refactoring\n**FIXED**: Wiederherstellen fehlender `configStates` Option im Admin UI, um die Konfiguration zu ermöglichen, ob Konfigurationszustände pro Einheit angezeigt werden",
|
|
@@ -16,19 +29,6 @@
|
|
|
16
29
|
"uk": "**FIXED***: помилки ESLint від рефакторингу коду\n**FIXED***: Відновити варіант `configStates` в admin UI, щоб дозволити налаштування станів конфігурації відображається на суб’єкті",
|
|
17
30
|
"zh-cn": "**FIXED**:通过代码重构的ESLint错误\n** FIXED**:在管理UI中恢复缺失的“配置状态”选项,以便配置是否显示每个实体的配置状态"
|
|
18
31
|
},
|
|
19
|
-
"0.7.0-beta.3": {
|
|
20
|
-
"en": "**NEW**: Added support for `colorBrightness`, `coldWhite`, `warmWhite`, and `colorMode` states for lights using the new ESPHome color mode API\n**FIXED**: RGB light control (brightness, color, white, colorTemperature) not working with newer ESPHome firmware that uses `supportedColorModesList` instead of deprecated legacy flags (#338)",
|
|
21
|
-
"de": "**NEW**: Unterstützung für `colorBrightness`, `coldWhite`, `warmWhite` und `colorMode` Zustände für Lichter mit der neuen ESPHome Farbmodus API\n**FIXED**: RGB Lichtsteuerung (Brightness, Farbe, weiß, FarbeTemperature) nicht mit neuerer ESPHome Firmware, die `supportedColorModesList` anstelle von deprecated Legacy Fahnen verwendet (#338)",
|
|
22
|
-
"ru": "**NEW**: Добавлена поддержка состояний «colorBrightness», «coldWhite», «warmWhite» и «colorMode» для огней с использованием нового API цветового режима ESPHome\n** FIXED**: Управление светом RGB (яркость, цвет, белый, цветовая температура) не работает с новой прошивкой ESPHome, которая использует «поддерживаемый ColorModesList» вместо устаревших флагов (#338)",
|
|
23
|
-
"pt": "**NOVO**: Adicionado suporte para `colorBrightness`, `coldWhite`, `warmWhite`, e `colorMode` estados para luzes usando o novo modo de cores ESPHome API\n**FIXED**: Controle de luz RGB (brilho, cor, branco, corTemperatura) não trabalhando com firmware ESPHome mais recente que usa `supportedColorModesList` em vez de bandeiras legadas deprecadas (#338)",
|
|
24
|
-
"nl": "**NIEUW**: Toegevoegde ondersteuning voor kleurBrightness, \n**FIXED**: RGB lichtcontrole (helderheid, kleur, wit, kleurTemperatuur) werkt niet met nieuwere ESPHome firmware die gebruik maakt van de ondersteundeColorModesListIn plaats van verouderde legacy vlaggen (#338)",
|
|
25
|
-
"fr": "**NOUVEAU**: Ajout du support pour les états `colorBrightness`, `coldWhite`, `warmWhite` et `colorMode` pour les lumières en utilisant la nouvelle API en mode couleur ESPHome\n**FIXED**: RGB contrôle de la lumière (brightness, color, white, colorTemperature) ne fonctionne pas avec le nouveau firmware ESPHome qui utilise `supportedColorModesList` au lieu de drapeaux hérités dépréciés (#338)",
|
|
26
|
-
"it": "**NEW**: Aggiunto il supporto per `colorBrightness`, `coldWhite`, `warmWhite`, e `colorMode` stati per le luci utilizzando il nuovo ESPHome colore modalità API\n**FIXED**: RGB light control (luce, colore, bianco, coloreTemperatura) non funziona con il nuovo firmware ESPHome che utilizza `supportedColorModesList` invece di bandiere legacy deprecate (#338)",
|
|
27
|
-
"es": "**NEW**: Añadido soporte para `colorBrightness`, `coldWhite`, `warmWhite`, and `colorMode` states for lights using the new ESPHome color mode API\n**FIXED**: Control de luz RGB (brightness, color, blanco, colorTemperatura) no trabaja con el firmware ESPHome más nuevo que utiliza 'ColorModesList` compatible en lugar de banderas heredadas deprecatadas (#338)",
|
|
28
|
-
"pl": "* *NOWOŚĆ* *: Dodano wsparcie dla 'colorBrighty', 'coldWhite', 'colorWhite' i 'colorMode' dla świateł przy użyciu nowego trybu kolorów ESPHome API \n* *FIXED* *: Sterowanie światłem RGB (jasność, kolor, biel, barwa) niepracujące z nowszymi firmware ESPHome, które używa 'supportedColorModesList' zamiast zdeprecjonowanych flag (# 338) ",
|
|
29
|
-
"uk": "**NEW**: Додана підтримка `colorBrightness`, `coldWhite`, `warmWhite`, `colorMode` станів для вогнів, використовуючи новий ESPHome колірний режим API\n**FIXED**: RGB легкий контроль (яскравість, колір, білий, колірTemperature) не працює з новою прошивкою ESPHome, яка використовує `supportedColorModesList` замість відхилених прапорів спадщини (#338)",
|
|
30
|
-
"zh-cn": "**NEW**: 添加支持使用新的ESPHome颜色模式API的“彩色亮度”、“冷白”、“暖白”和“彩色模式”照明装置\n** FIXED**:RGB灯光控制(亮度,颜色,白色,色质)不与使用`支持的ColorModesList ' 而不是被磨损的遗留旗帜的更新ESPHome固件合作(# 338)"
|
|
31
|
-
},
|
|
32
32
|
"0.7.0-beta.2": {
|
|
33
33
|
"en": "improve README\nfix #394, actually fix #340, #356\n**FIXED**: Fan component not working #205\n**NEW**: Allow customization of Pillow version used by ESPHome Dashboard, similar to ESPHome version selector\n**NEW**: Add \"Clear Autopy Cache\" button in ESPHome Dashboard configuration tab to resolve dashboard loading issues (#209)",
|
|
34
34
|
"de": "rEADME\nfix #394, eigentlich fix #340, #356\n**FIXED**: Fan-Komponente nicht funktioniert #205\n**NEW**: Erlauben Anpassung der Pillow-Version, die von ESPHome Dashboard verwendet wird, ähnlich wie ESPHome-Versionsauswahl\n**NEW**: Fügen Sie die Schaltfläche \"Klar Autopy Cache\" in ESPHome Dashboard Konfigurationstabelle hinzu, um dashboard Ladeprobleme zu lösen (#209)",
|
|
@@ -42,19 +42,6 @@
|
|
|
42
42
|
"uk": "покращити резюме\nвиправити #394, насправді виправити #340, #356\n**FIXED***: Компонент вентилятора не працює #205\n**NEW**: Дозволяє налаштування версії Pillow, що використовується ESPHome Dashboard, схожим на ESPHome версія selector\n**NEW**: Додати кнопку \"Зареєструвати Autopy Cache\" у вкладці конфігурації ESPHome Dashboard для вирішення проблем завантаження панелі (#209)",
|
|
43
43
|
"zh-cn": "改进读取\n修复394号,实际修复340号,356号\n** FIXED**:粉丝组件不工作#205\n**NEW**:允许定制ESPHome Dashboard使用的Pillow版本,类似于ESPHome版本选择器\n**NEW**:在ESPHome Dashboard配置标签中添加\"清除自动缓存\"按钮以解决仪表板加载问题(# 209)"
|
|
44
44
|
},
|
|
45
|
-
"0.7.0": {
|
|
46
|
-
"en": "**NEW**: Add support for Lock entity type - Lock devices now properly display state and control options #353\n**NEW**: YAML file management interface in admin UI for upload/download/delete operations (#369)\nimprove dashboard testing\nimprove logging for #201\nupdate pillow\nfix readme link to lib\nfix connection status #311\nremove unneeded node-fetch dependency\nautomatic migration from versions prior to ESPHomeDashboardUrl introduction (pre v0.6.1)\n**FIXED**: Invalid jsonConfig schema - removed unsupported `doNotSave` property from table elements",
|
|
47
|
-
"de": "**NEW**: Unterstützung für Lock-Entity-Typ hinzufügen - Lock-Geräte jetzt richtig anzeigen Zustand und Steueroptionen #353\n**NEW**: YAML-Dateiverwaltungsschnittstelle in admin UI für Upload/Download/Auslöschung (#369)\ndashboard testen\nverbesserung der protokollierung für #201\nupdate kissen\nreadme link zu lib reparieren\nfix-verbindungsstatus #311\nnicht benötigte knoten-fetch-abhängigkeit entfernen\nautomatische Migration von Versionen vor der Einführung von ESPHomeDashboardUrl (pre v0.6.1)\n**FIXED**: Invalid jsonConfig Schema - entfernte nicht unterstützte `doNotSave` Eigenschaft von Tabellenelementen",
|
|
48
|
-
"ru": "**NEW**: Добавить поддержку типа объекта блокировки - устройства блокировки теперь должным образом отображают параметры состояния и управления #353\n**NEW**: интерфейс управления файлами YAML в интерфейсе администратора для операций загрузки/загрузки/удаления (#369)\nулучшить тестирование приборной панели\nулучшение регистрации для #201\nобновление подушки\nобсуждение readme link to lib\nстатус подключения #311\nустранение ненужной зависимости от узла\nавтоматическая миграция с версий до внедрения ESPHomeDashboardUrl (pre v0.6.1)\n** FIXED**: Недействительная схема jsonConfig - удалено неподдерживаемое свойство \"doNotSave\" из элементов таблицы",
|
|
49
|
-
"pt": "**NOVO**: Adicionar suporte para o tipo de entidade Lock - Dispositivos Lock agora exibem corretamente as opções de estado e controle #353\n**NOVO**: Interface de gerenciamento de arquivos YAML na interface de administração UI para operações de upload/download/delete (#369)\nmelhorar o teste do painel\nmelhorar o registro para #201\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\nmigração automática de versões anteriores à introdução ESPHomeDashboardUrl (pré v0.6.1)\n**FIXED**: Esquema inválido do jsonConfig - propriedade `doNotSave` não suportada",
|
|
50
|
-
"nl": "**NEW**: Voeg ondersteuning toe voor het type Lock-entiteit - Vergrendel apparaten nu correct de status en controle opties #353\n**NIEUW**: YAML file management interface in admin UI voor upload/download/delete operaties (#369)\nverbeteren van dashboard testen\nverbeteren logging voor #201\nupdate kussen\nrepareren readme link naar lib\nfix verbindingsstatus #311\nverwijder ongewenste node-fetch afhankelijkheid\nautomatische migratie van versies voorafgaand aan ESPHomeDashboardUrl introductie (pre v0.6.1)\n** VAST**: Ongeldig jsonConfig schema - verwijderde niet-ondersteunde eigenschap van tabelelementen",
|
|
51
|
-
"fr": "**NOUVEAU**: Ajout de la prise en charge du type d'entité de verrouillage - Verrouiller les appareils maintenant correctement afficher l'état et les options de contrôle #353\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 les essais de tableau de bord\naméliorer l'enregistrement pour #201\nmettre à jour l'oreiller\ncorriger le lien readme vers lib\nfixer l'état de connexion #311\nsupprimer la dépendance inutile des nœuds\nmigration automatique des versions avant l'introduction de ESPHomeDashboardUrl (pre v0.6.1)\n**FIXED**: Schéma jsonConfig non valide - supprimé la propriété `doNotSave` non supportée des éléments de table",
|
|
52
|
-
"it": "**NEW**: Aggiungi supporto per tipo di entità Lock - Dispositivi di blocco ora correttamente visualizzano le opzioni di stato e controllo #353\n**NEW**: interfaccia di gestione dei file YAML nell'interfaccia di amministrazione UI per operazioni di upload/download/delete (#369)\nmigliorare il test del cruscotto\nmigliorare logging per #201\naggiornamento cuscino\ncorrezione link di lettura per lib\nstato di connessione di correzione #311\nrimuovere la dipendenza da nodo-fetch non necessaria\nmigrazione automatica dalle versioni precedenti l'introduzione di ESPHomeDashboardUrl (pre v0.6.1)\n**FIXED**: Invalid jsonConfig schema - rimosso `doNotSave` immobili da elementi da tavolo",
|
|
53
|
-
"es": "**NEW**: Añadir soporte para el tipo de entidad de bloqueo - Los dispositivos de bloqueo ahora muestran correctamente las opciones de estado y control #353\n**NEW**: Interfaz de gestión de archivos YAML en admin UI para operaciones de carga/descarga/delete (#369)\nmejorar las pruebas de dashboard\nmejorar el registro para #201\nactualización almohada\narreglar el vínculo readme con la lib\nestado de conexión de fijación #311\neliminar la dependencia sin necesidad\nmigración automática desde versiones anteriores a ESPHomeDashboardIntroducción del usuario (pre v0.6.1)\n**FIXED**: Invalid jsonConfig schema - eliminó propiedad no apoyada `doNotSave` de elementos de mesa",
|
|
54
|
-
"pl": "* *NOWOŚĆ* *: Dodaj wsparcie dla typu podmiotu blokującego - Urządzenia blokujące teraz poprawnie wyświetlają stan i opcje sterowania # 353 \n* *NOWOŚĆ* *: Interfejs do zarządzania plikami YAML w UI administratora do wysyłania / pobierania / usuwania operacji (# 369) \npoprawić testowanie płyt rozdzielczych\npoprawić logowanie dla # 201\naktualizacji poduszki\nfix readme link do lib\nustaw status połączenia # 311\nusuń niepotrzebną zależność node-get\nautomatyczna migracja z wersji przed wprowadzeniem ESPHOmeDashboardUrl (pre v0.6.1)\n* *FIXED* *: Nieprawidłowy schemat jsonConfig - usunięte nieobsługiwane właściwości 'doNotSave' z elementów tabeli ",
|
|
55
|
-
"uk": "**NEW**: Додати підтримку типу Lock - Lock devices тепер правильно відображати стан та параметри контролю #353\n**NEW**: Інтерфейс керування файлами YAML в адмін UI для завантаження / завантаження / видалення операцій (#369)\nполіпшити тестування панелі приладів\nпокращити залогів на #201\nоновлення подушки\nвиправити посилання для читання\nстатус на сервери\nвидалити невидиму залежність від вузла\nавтоматична міграція з версій до ESPHomeDashboardUrl введення (pre v0.6.1)\n**FIXED**: Invalid jsonConfig schema - вилучено непідтримуваний `doNotSave` майно з елементів таблиці",
|
|
56
|
-
"zh-cn": "** NEW**: 添加对 Lock 实体类型的支持 - 锁定设备现在正确显示状态和控制选项 # 353\n**NEW**:用于上传/下载/删除操作的管理员UI中的YAML文件管理接口(# 369)\n改进仪表板测试\n改进201号记录\n更新枕头\n固定读取链接到 lib\n固定连接状态 # 311\n删除不需要的节点依赖\n从ESPHomeDashboardUrl引入前的版本自动迁移(pre v0.6.1)\n** FIXED**: 无效的jsonConfig schema - 从表格元素中删除未支持的“不拯救”属性"
|
|
57
|
-
},
|
|
58
45
|
"0.7.0-beta.1": {
|
|
59
46
|
"en": "improve dashboard testing",
|
|
60
47
|
"de": "dashboard testen",
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ESPHome Dashboard API client
|
|
5
|
+
* Inspired by https://github.com/esphome/dashboard-api
|
|
6
|
+
*
|
|
7
|
+
* Wraps all endpoints exposed by the ESPHome Dashboard HTTP/WebSocket API.
|
|
8
|
+
* The dashboard URL is taken from adapter settings (ESPHomeDashboardUrl).
|
|
9
|
+
*
|
|
10
|
+
* HTTP endpoints use the native fetch API (Node >= 18).
|
|
11
|
+
* WebSocket streaming endpoints (compile, upload) use the native WebSocket API on Node >= 22,
|
|
12
|
+
* or fall back to the `ws` npm package on Node 20 if it is installed.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Make an HTTP request to the ESPHome Dashboard and return the parsed JSON body.
|
|
17
|
+
*
|
|
18
|
+
* @param {string} method - HTTP method (e.g. 'GET', 'POST')
|
|
19
|
+
* @param {string} dashboardUrl - Base URL of the ESPHome Dashboard (e.g. http://192.168.1.100:6052)
|
|
20
|
+
* @param {string} path - API path (e.g. 'devices')
|
|
21
|
+
* @param {object} [params] - Optional query parameters
|
|
22
|
+
* @returns {Promise<object>} Parsed JSON response
|
|
23
|
+
*/
|
|
24
|
+
async function request(method, dashboardUrl, path, params) {
|
|
25
|
+
const base = dashboardUrl.replace(/\/+$/, '');
|
|
26
|
+
const url = new URL(`${base}/${path}`);
|
|
27
|
+
if (params) {
|
|
28
|
+
for (const [key, value] of Object.entries(params)) {
|
|
29
|
+
url.searchParams.set(key, value);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const response = await fetch(url.toString(), { method });
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
const error = new Error(`Dashboard API ${method} /${path} failed: ${response.status} ${response.statusText}`);
|
|
35
|
+
// @ts-expect-error custom property not in Error type
|
|
36
|
+
error.status = response.status;
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
return await response.json();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Stream output from a long-running ESPHome Dashboard command over a WebSocket.
|
|
44
|
+
*
|
|
45
|
+
* The dashboard sends JSON frames with an `event` field:
|
|
46
|
+
* - `{ event: 'line', data: '<text>' }` – a line of log output
|
|
47
|
+
* - `{ event: 'exit', code: <number> }` – the command finished
|
|
48
|
+
*
|
|
49
|
+
* Uses the native WebSocket API on Node >= 22, or falls back to the `ws` npm
|
|
50
|
+
* package on Node 20. If neither is available, an error is thrown.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} dashboardUrl - Base URL of the ESPHome Dashboard
|
|
53
|
+
* @param {string} path - WebSocket endpoint path (e.g. 'compile', 'upload')
|
|
54
|
+
* @param {object} spawnParams - Parameters forwarded as `{ type: 'spawn', ...spawnParams }`
|
|
55
|
+
* @param {((line: string) => void) | null} [lineReceivedCb] - Optional callback invoked for each log line
|
|
56
|
+
* @returns {Promise<boolean>} Resolves to true when the command exits with code 0, false otherwise
|
|
57
|
+
*/
|
|
58
|
+
async function streamLogs(dashboardUrl, path, spawnParams, lineReceivedCb) {
|
|
59
|
+
const wsUrl = `${dashboardUrl.replace(/\/+$/, '').replace(/^http/, 'ws')}/${path}`;
|
|
60
|
+
|
|
61
|
+
// Prefer native WebSocket (Node >= 22), but fall back to "ws" package on older Node versions
|
|
62
|
+
const hasNativeWebSocket = typeof WebSocket !== 'undefined';
|
|
63
|
+
let WebSocketImpl = hasNativeWebSocket ? WebSocket : null;
|
|
64
|
+
|
|
65
|
+
if (!WebSocketImpl) {
|
|
66
|
+
try {
|
|
67
|
+
WebSocketImpl = require('ws');
|
|
68
|
+
} catch {
|
|
69
|
+
throw new Error(
|
|
70
|
+
'WebSocket streaming requires either Node.js >= 22 with native WebSocket support ' +
|
|
71
|
+
'or the "ws" package installed. Please upgrade Node.js or install "ws" to use compile/upload functionality.',
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return new Promise((resolve, reject) => {
|
|
77
|
+
let settled = false;
|
|
78
|
+
const ws = new WebSocketImpl(wsUrl);
|
|
79
|
+
|
|
80
|
+
const settle = fn => {
|
|
81
|
+
if (!settled) {
|
|
82
|
+
settled = true;
|
|
83
|
+
fn();
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
if (hasNativeWebSocket) {
|
|
88
|
+
// Native WebSocket (Node >= 22 / browser-like API)
|
|
89
|
+
ws.addEventListener('open', () => {
|
|
90
|
+
ws.send(JSON.stringify({ type: 'spawn', ...spawnParams }));
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
ws.addEventListener('message', event => {
|
|
94
|
+
let data;
|
|
95
|
+
try {
|
|
96
|
+
data = JSON.parse(event.data);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
// Log at debug level to help diagnose malformed or unexpected messages
|
|
99
|
+
if (typeof console !== 'undefined' && typeof console.debug === 'function') {
|
|
100
|
+
console.debug(
|
|
101
|
+
`Failed to parse WebSocket message from /${path}:`,
|
|
102
|
+
error,
|
|
103
|
+
'raw message:',
|
|
104
|
+
event.data,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const wsEvent = data.event;
|
|
111
|
+
|
|
112
|
+
if (wsEvent === 'exit') {
|
|
113
|
+
ws.close();
|
|
114
|
+
settle(() => resolve(data.code === 0));
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (wsEvent === 'line') {
|
|
119
|
+
if (lineReceivedCb) {
|
|
120
|
+
lineReceivedCb(data.data);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
ws.addEventListener('error', err => {
|
|
126
|
+
const message =
|
|
127
|
+
err && typeof err === 'object' && typeof err.message === 'string' ? err.message : String(err);
|
|
128
|
+
settle(() => reject(new Error(`WebSocket error on /${path}: ${message}`)));
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
ws.addEventListener('close', event => {
|
|
132
|
+
if (!event.wasClean) {
|
|
133
|
+
settle(() => reject(new Error(`WebSocket closed unexpectedly on /${path}: code ${event.code}`)));
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
} else {
|
|
137
|
+
// "ws" package (Node >= 20 without native WebSocket)
|
|
138
|
+
ws.on('open', () => {
|
|
139
|
+
ws.send(JSON.stringify({ type: 'spawn', ...spawnParams }));
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
ws.on('message', message => {
|
|
143
|
+
let data;
|
|
144
|
+
try {
|
|
145
|
+
data = JSON.parse(message);
|
|
146
|
+
} catch (error) {
|
|
147
|
+
// Log at debug level to help diagnose malformed or unexpected messages
|
|
148
|
+
if (typeof console !== 'undefined' && typeof console.debug === 'function') {
|
|
149
|
+
console.debug(
|
|
150
|
+
`Failed to parse WebSocket message from /${path}:`,
|
|
151
|
+
error,
|
|
152
|
+
'raw message:',
|
|
153
|
+
message,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const wsEvent = data.event;
|
|
160
|
+
|
|
161
|
+
if (wsEvent === 'exit') {
|
|
162
|
+
ws.close();
|
|
163
|
+
settle(() => resolve(data.code === 0));
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (wsEvent === 'line') {
|
|
168
|
+
if (lineReceivedCb) {
|
|
169
|
+
lineReceivedCb(data.data);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
ws.on('error', err => {
|
|
175
|
+
const message = err && err.message ? err.message : String(err);
|
|
176
|
+
settle(() => reject(new Error(`WebSocket error on /${path}: ${message}`)));
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
ws.on('close', (code, reason) => {
|
|
180
|
+
// "ws" does not provide wasClean flag by default; treat non-normal close as unexpected
|
|
181
|
+
if (code !== 1000) {
|
|
182
|
+
settle(() =>
|
|
183
|
+
reject(
|
|
184
|
+
new Error(
|
|
185
|
+
`WebSocket closed unexpectedly on /${path}: code ${code}${
|
|
186
|
+
reason ? `, reason: ${reason.toString()}` : ''
|
|
187
|
+
}`,
|
|
188
|
+
),
|
|
189
|
+
),
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Fetch all configured and importable devices from the ESPHome Dashboard.
|
|
199
|
+
*
|
|
200
|
+
* Response shape:
|
|
201
|
+
* ```
|
|
202
|
+
* {
|
|
203
|
+
* configured: [{
|
|
204
|
+
* address: string, // IP / hostname used for communication
|
|
205
|
+
* comment: string | null,
|
|
206
|
+
* configuration: string, // YAML filename (used for other API calls)
|
|
207
|
+
* current_version: string,
|
|
208
|
+
* deployed_version: string,
|
|
209
|
+
* loaded_integrations: string[],
|
|
210
|
+
* name: string,
|
|
211
|
+
* path: string,
|
|
212
|
+
* target_platform: string,
|
|
213
|
+
* web_port: string | null,
|
|
214
|
+
* }],
|
|
215
|
+
* importable: [{
|
|
216
|
+
* name: string,
|
|
217
|
+
* network: string,
|
|
218
|
+
* package_import_url: string,
|
|
219
|
+
* project_name: string,
|
|
220
|
+
* project_version: string,
|
|
221
|
+
* }]
|
|
222
|
+
* }
|
|
223
|
+
* ```
|
|
224
|
+
*
|
|
225
|
+
* @param {string} dashboardUrl - Base URL of the ESPHome Dashboard
|
|
226
|
+
* @returns {Promise<{configured: Array<object>, importable: Array<object>}>} Devices list
|
|
227
|
+
*/
|
|
228
|
+
async function getDevices(dashboardUrl) {
|
|
229
|
+
return request('GET', dashboardUrl, 'devices');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Fetch the JSON representation of a device configuration from the ESPHome Dashboard.
|
|
234
|
+
*
|
|
235
|
+
* @param {string} dashboardUrl - Base URL of the ESPHome Dashboard
|
|
236
|
+
* @param {string} configuration - Configuration filename (e.g. my-device.yaml)
|
|
237
|
+
* @returns {Promise<object|null>} Parsed configuration object, or null when not found (404)
|
|
238
|
+
*/
|
|
239
|
+
async function getConfig(dashboardUrl, configuration) {
|
|
240
|
+
try {
|
|
241
|
+
return await request('GET', dashboardUrl, 'json-config', { configuration });
|
|
242
|
+
} catch (error) {
|
|
243
|
+
// @ts-expect-error custom property not in Error type
|
|
244
|
+
if (error.status === 404) {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
throw error;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Extract the native API encryption key for a device configuration.
|
|
253
|
+
*
|
|
254
|
+
* Returns null when:
|
|
255
|
+
* - the configuration file does not exist
|
|
256
|
+
* - the YAML has no `api:` section
|
|
257
|
+
* - the `api:` section has no `encryption:` block
|
|
258
|
+
*
|
|
259
|
+
* @param {string} dashboardUrl - Base URL of the ESPHome Dashboard
|
|
260
|
+
* @param {string} configuration - Configuration filename (e.g. my-device.yaml)
|
|
261
|
+
* @returns {Promise<string|null>} Base64-encoded encryption key, or null
|
|
262
|
+
*/
|
|
263
|
+
async function getEncryptionKey(dashboardUrl, configuration) {
|
|
264
|
+
const config = await getConfig(dashboardUrl, configuration);
|
|
265
|
+
if (!config) {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
// An empty `api:` section in YAML becomes null in JSON
|
|
269
|
+
const api = config.api;
|
|
270
|
+
if (!api) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
const encryption = api.encryption;
|
|
274
|
+
if (!encryption) {
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
return encryption.key || null;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Compile the firmware for a device configuration.
|
|
282
|
+
*
|
|
283
|
+
* Uses the native WebSocket API (Node >= 22) or falls back to the `ws` npm package (Node 20).
|
|
284
|
+
*
|
|
285
|
+
* @param {string} dashboardUrl - Base URL of the ESPHome Dashboard
|
|
286
|
+
* @param {string} configuration - Configuration filename (e.g. my-device.yaml)
|
|
287
|
+
* @param {((line: string) => void) | null} [lineReceivedCb] - Optional callback invoked for each log line
|
|
288
|
+
* @returns {Promise<boolean>} Resolves to true on success, false on failure
|
|
289
|
+
*/
|
|
290
|
+
async function compile(dashboardUrl, configuration, lineReceivedCb) {
|
|
291
|
+
return streamLogs(dashboardUrl, 'compile', { configuration }, lineReceivedCb);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Upload (flash) firmware to a device.
|
|
296
|
+
*
|
|
297
|
+
* Uses the native WebSocket API (Node >= 22) or falls back to the `ws` npm package (Node 20).
|
|
298
|
+
*
|
|
299
|
+
* @param {string} dashboardUrl - Base URL of the ESPHome Dashboard
|
|
300
|
+
* @param {string} configuration - Configuration filename (e.g. my-device.yaml)
|
|
301
|
+
* @param {string} port - Serial port or OTA target (e.g. '/dev/ttyUSB0' or device IP)
|
|
302
|
+
* @param {((line: string) => void) | null} [lineReceivedCb] - Optional callback invoked for each log line
|
|
303
|
+
* @returns {Promise<boolean>} Resolves to true on success, false on failure
|
|
304
|
+
*/
|
|
305
|
+
async function upload(dashboardUrl, configuration, port, lineReceivedCb) {
|
|
306
|
+
return streamLogs(dashboardUrl, 'upload', { configuration, port }, lineReceivedCb);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
module.exports = {
|
|
310
|
+
request,
|
|
311
|
+
streamLogs,
|
|
312
|
+
getDevices,
|
|
313
|
+
getConfig,
|
|
314
|
+
getEncryptionKey,
|
|
315
|
+
compile,
|
|
316
|
+
upload,
|
|
317
|
+
};
|
package/lib/stateAttr.js
CHANGED
package/lib/yamlFileManager.js
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
* @module yamlFileManager
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
const fs = require('fs');
|
|
10
|
-
const path = require('path');
|
|
11
|
-
const { promisify } = require('util');
|
|
9
|
+
const fs = require('node:fs');
|
|
10
|
+
const path = require('node:path');
|
|
11
|
+
const { promisify } = require('node:util');
|
|
12
12
|
|
|
13
13
|
const readdir = promisify(fs.readdir);
|
|
14
14
|
const stat = promisify(fs.stat);
|
package/main.js
CHANGED
|
@@ -14,10 +14,10 @@ const { Client, Discovery } = require('@2colors/esphome-native-api');
|
|
|
14
14
|
const stateAttr = require(`${__dirname}/lib/stateAttr.js`); // Load attribute library
|
|
15
15
|
const disableSentry = false; // Ensure to set to true during development!
|
|
16
16
|
const warnMessages = {}; // Store warn messages to avoid multiple sending to sentry
|
|
17
|
-
const fs = require('fs');
|
|
18
|
-
const path = require('path');
|
|
19
|
-
const os = require('os');
|
|
20
|
-
const { clearTimeout } = require('timers');
|
|
17
|
+
const fs = require('node:fs');
|
|
18
|
+
const path = require('node:path');
|
|
19
|
+
const os = require('node:os');
|
|
20
|
+
const { clearTimeout } = require('node:timers');
|
|
21
21
|
const resetTimers = {}; // Memory allocation for all running timers
|
|
22
22
|
let autodiscovery, dashboardProcess, createConfigStates, discovery;
|
|
23
23
|
const clientDetails = {}; // Memory cache of all devices and their connection status
|
|
@@ -252,17 +252,19 @@ class Esphome extends utils.Adapter {
|
|
|
252
252
|
pillowVersions.push(...versions);
|
|
253
253
|
|
|
254
254
|
// Determine Pillow version to use
|
|
255
|
-
let usePillowVersion = '
|
|
255
|
+
let usePillowVersion = '';
|
|
256
256
|
|
|
257
|
-
//
|
|
257
|
+
// Use configured version unless "Always last available" is selected
|
|
258
258
|
if (
|
|
259
259
|
this.config.PillowVersion &&
|
|
260
260
|
this.config.PillowVersion !== '' &&
|
|
261
261
|
this.config.PillowVersion !== 'Always last available'
|
|
262
262
|
) {
|
|
263
263
|
usePillowVersion = this.config.PillowVersion;
|
|
264
|
+
} else if (this.config.PillowVersion === 'Always last available' && versions.length > 0) {
|
|
265
|
+
// Mirror dashboard behavior: pick newest fetched/cached release
|
|
266
|
+
usePillowVersion = versions[0];
|
|
264
267
|
}
|
|
265
|
-
// If "Always last available" is selected, keep the default latest version
|
|
266
268
|
|
|
267
269
|
this.log.debug(`Using Pillow version: ${usePillowVersion}`);
|
|
268
270
|
|
|
@@ -1164,6 +1166,38 @@ class Esphome extends utils.Adapter {
|
|
|
1164
1166
|
clientDetails[host][entity.id].states.transitionLength = 0;
|
|
1165
1167
|
}
|
|
1166
1168
|
}
|
|
1169
|
+
|
|
1170
|
+
// Create rgbAutoWhite state only once for lights with a dedicated white channel
|
|
1171
|
+
if (stateName === 'white' && clientDetails[host][entity.id].rgbAutoWhite === undefined) {
|
|
1172
|
+
let rgbAutoWhiteVal = false;
|
|
1173
|
+
try {
|
|
1174
|
+
const existing = await this.getStateAsync(
|
|
1175
|
+
`${clientDetails[host].deviceName}.${entity.type}.${entity.id}.config.rgbAutoWhite`,
|
|
1176
|
+
);
|
|
1177
|
+
if (existing != null) {
|
|
1178
|
+
rgbAutoWhiteVal = !!existing.val;
|
|
1179
|
+
}
|
|
1180
|
+
} catch (e) {
|
|
1181
|
+
this.log.debug(`[handleStateArrays] Could not read rgbAutoWhite state: ${e}`);
|
|
1182
|
+
}
|
|
1183
|
+
clientDetails[host][entity.id].rgbAutoWhite = rgbAutoWhiteVal;
|
|
1184
|
+
// Ensure config channel exists before creating the state inside it
|
|
1185
|
+
await this.extendObjectAsync(
|
|
1186
|
+
`${clientDetails[host].deviceName}.${entity.type}.${entity.id}.config`,
|
|
1187
|
+
{
|
|
1188
|
+
type: 'channel',
|
|
1189
|
+
common: { name: 'Configuration data' },
|
|
1190
|
+
native: {},
|
|
1191
|
+
},
|
|
1192
|
+
);
|
|
1193
|
+
await this.stateSetCreate(
|
|
1194
|
+
`${clientDetails[host].deviceName}.${entity.type}.${entity.id}.config.rgbAutoWhite`,
|
|
1195
|
+
'rgbAutoWhite',
|
|
1196
|
+
rgbAutoWhiteVal,
|
|
1197
|
+
'',
|
|
1198
|
+
true,
|
|
1199
|
+
);
|
|
1200
|
+
}
|
|
1167
1201
|
}
|
|
1168
1202
|
|
|
1169
1203
|
if (stateName !== 'key') {
|
|
@@ -2199,6 +2233,12 @@ class Esphome extends utils.Adapter {
|
|
|
2199
2233
|
clientDetails[deviceIP][device[4]].states.effect = writeValue;
|
|
2200
2234
|
} else if (device[5] === 'state') {
|
|
2201
2235
|
clientDetails[deviceIP][device[4]].states.state = writeValue;
|
|
2236
|
+
} else if (device[5] === 'config' && device[6] === 'rgbAutoWhite') {
|
|
2237
|
+
// Store device-specific preference; no light command needed
|
|
2238
|
+
clientDetails[deviceIP][device[4]].rgbAutoWhite = writeValue;
|
|
2239
|
+
// Acknowledge the preference update so the state does not stay with ack=false
|
|
2240
|
+
this.setState(id, !!writeValue, true);
|
|
2241
|
+
return;
|
|
2202
2242
|
}
|
|
2203
2243
|
|
|
2204
2244
|
const data = {
|
|
@@ -2222,6 +2262,21 @@ class Esphome extends utils.Adapter {
|
|
|
2222
2262
|
colorModesList.some(m => [4, 8].includes(m));
|
|
2223
2263
|
const supportsColdWarmWhite = colorModesList.some(m => [5, 9].includes(m));
|
|
2224
2264
|
|
|
2265
|
+
// Auto white channel: when colorHEX is set to white (#ffffff) on RGBW lights,
|
|
2266
|
+
// automatically switch to white channel; otherwise switch to RGB mode
|
|
2267
|
+
if (clientDetails[deviceIP][device[4]].rgbAutoWhite && supportsWhite && device[5] === 'colorHEX') {
|
|
2268
|
+
if (writeValue.replace(/^#/, '').toLowerCase() === 'ffffff') {
|
|
2269
|
+
// White color detected: redirect to dedicated white channel
|
|
2270
|
+
clientDetails[deviceIP][device[4]].states.red = 0;
|
|
2271
|
+
clientDetails[deviceIP][device[4]].states.green = 0;
|
|
2272
|
+
clientDetails[deviceIP][device[4]].states.blue = 0;
|
|
2273
|
+
clientDetails[deviceIP][device[4]].states.white = 1;
|
|
2274
|
+
} else {
|
|
2275
|
+
// Non-white color: disable white channel
|
|
2276
|
+
clientDetails[deviceIP][device[4]].states.white = 0;
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2225
2280
|
if (supportsBrightness) {
|
|
2226
2281
|
data.brightness = clientDetails[deviceIP][device[4]].states.brightness;
|
|
2227
2282
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.esphome",
|
|
3
|
-
"version": "0.7.0
|
|
3
|
+
"version": "0.7.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",
|
|
@@ -30,20 +30,20 @@
|
|
|
30
30
|
"node": ">= 20"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@2colors/esphome-native-api": "^1.3.
|
|
33
|
+
"@2colors/esphome-native-api": "^1.3.6",
|
|
34
34
|
"@iobroker/adapter-core": "^3.3.2",
|
|
35
35
|
"autopy": "github:SimonFischer04/autopy#fbe016f35e13e335e1c44783a1e7273bbb2a08c1"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@alcalzone/release-script": "^5.0.0",
|
|
39
|
-
"@alcalzone/release-script-plugin-iobroker": "^
|
|
40
|
-
"@alcalzone/release-script-plugin-license": "^
|
|
41
|
-
"@alcalzone/release-script-plugin-manual-review": "^
|
|
39
|
+
"@alcalzone/release-script-plugin-iobroker": "^5.1.2",
|
|
40
|
+
"@alcalzone/release-script-plugin-license": "^5.1.1",
|
|
41
|
+
"@alcalzone/release-script-plugin-manual-review": "^5.1.1",
|
|
42
42
|
"@iobroker/adapter-dev": "^1.5.0",
|
|
43
43
|
"@iobroker/dev-server": "^0.8.0",
|
|
44
44
|
"@iobroker/eslint-config": "^2.2.0",
|
|
45
45
|
"@iobroker/testing": "^5.2.2",
|
|
46
|
-
"@types/node": "^25.0
|
|
46
|
+
"@types/node": "^25.6.0",
|
|
47
47
|
"eslint": "^9.39.2",
|
|
48
48
|
"promisify": "~0.0.3",
|
|
49
49
|
"typescript": "~5.9.3"
|