iobroker.zeptrion 1.0.7 → 1.0.8

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 CHANGED
@@ -9,7 +9,7 @@
9
9
  [![Donate](https://img.shields.io/badge/Donate-PayPal-blue.svg)](https://www.paypal.com/ncp/payment/TT6MTBLXX9L9U)
10
10
  [![Buy me a coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-FFDD00?style=flat&logo=buy-me-a-coffee&logoColor=black)](https://buymeacoffee.com/stefanbuehler)
11
11
 
12
- Adapter for Feller **zeptrion / zApp** WLAN actuators (WLAN Nebenstelle 4K = zApp gateway,
12
+ Adapter for [Feller](https://www.feller.ch/) **[zeptrion / zApp](https://www.feller.ch/de/connected-buildings/zeptrion)** WLAN actuators (WLAN Nebenstelle 4K = zApp gateway,
13
13
  WLAN Zwischenmodul 2K = zApp booster) for light and shutter/blind control, based on the
14
14
  zrap web service API (Feller document 10.ZEPAPI-E.1612 / version 1.0, firmware from 01.08.18).
15
15
 
@@ -59,22 +59,6 @@ Not implemented (see "Known limitations"): write access to `zrap/net`
59
59
 
60
60
  ## Installation
61
61
 
62
- ### a) Local/manual (before store publication)
63
-
64
- ```bash
65
- cd /opt/iobroker/node_modules
66
- mkdir iobroker.zeptrion
67
- ## copy the files of this package here
68
- cd iobroker.zeptrion
69
- npm install --production
70
-
71
- cd /opt/iobroker
72
- iobroker upload zeptrion
73
- iobroker add zeptrion
74
- ```
75
-
76
- ### b) Via the ioBroker Adapter Store (once published)
77
-
78
62
  Admin UI -> Adapters -> search for "zeptrion" -> Install.
79
63
 
80
64
  ## Configuration
@@ -192,6 +176,9 @@ npm run test:integration # starts a real js-controller (takes longer)
192
176
 
193
177
  ## Changelog
194
178
 
179
+ ### 1.0.8 (2026-08-08)
180
+ - Fix all findings from the manual maintainer review (PR #6327): removed the manual npm installation section from README.md/README_de.md (E6012, prohibited regardless of stated intent); added a verified link to the Feller product page; translated all 40+ German log messages to English (UI-facing result text for CSV import/discovery, shown in the admin config dialog, is intentionally kept German and decoupled from the log call); converted all 50 German common.name strings (incl. the CH_BUTTONS constant and dynamic channel/scene names) to full 11-language i18n objects; completed io-package.json instanceObjects translations for 'info' and 'control' (info.connection already had all 11 languages).
181
+
195
182
  ### 1.0.7 (2026-07-22)
196
183
  - Enable global i18n support (jsonConfig i18n: true) with translation files under admin/i18n/ for all 11 supported languages, resolving the checker's i18n warnings the correct way (validatorErrorText stays a plain string per schema; ioBroker resolves the translation via the files, falling back to the English text if no entry is found). Added @iobroker/adapter-dev and @alcalzone/release-script as devDependencies with translate/release npm scripts. (Migrating to @iobroker/eslint-config was evaluated but reverted: its eslint-plugin-import dependency does not yet support eslint 10.x, which broke npm install.)
197
184
 
@@ -211,11 +198,7 @@ npm run test:integration # starts a real js-controller (takes longer)
211
198
  ### 1.0.2 (2026-07-17)
212
199
  - FIX: the global (adapter-level) info.connection translations added in 1.0.1 are only synced automatically by js-controller on certain update paths (e.g. 'iobroker upgrade'), not reliably when installing via 'iobroker url' - added this to the startup migration too, so it no longer depends on that.
213
200
 
214
- ### 1.0.1 (2026-07-17)
215
- - FIX: calibrate declared role 'level' but read=false - role 'level' requires read=true per the ioBroker role catalogue. Changed to read=true and extended the startup migration to also correct existing objects still holding the old read=false.
216
- - Added the missing translations for the global info.connection object (was only en/de).
217
-
218
- ### 1.0.0 and older
201
+ ### 1.0.1 and older
219
202
 
220
203
  Older changelog entries can be found in [CHANGELOG_OLD.md](CHANGELOG_OLD.md).
221
204
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "Only a-z, 0-9, _ and - (or leave empty)": "Nur a-z, 0-9, _ und - (oder leer lassen)",
2
+ "Comma-separated numbers only, max 4 values (e.g. '22,28')": "Nur kommagetrennte Zahlen, max. 4 Werte (z. B. '22,28')",
3
3
  "Invalid host (no protocol, no spaces)": "Ungültiger Host (kein Protokoll, keine Leerzeichen)",
4
- "Comma-separated numbers only, max 4 values (e.g. '22,28')": "Nur kommagetrennte Zahlen, max. 4 Werte (z. B. '22,28')"
4
+ "Only a-z, 0-9, _ and - (or leave empty)": "Nur a-z, 0-9, _ und - (oder leer lassen)"
5
5
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "Only a-z, 0-9, _ and - (or leave empty)": "Only a-z, 0-9, _ and - (or leave empty)",
2
+ "Comma-separated numbers only, max 4 values (e.g. '22,28')": "Comma-separated numbers only, max 4 values (e.g. '22,28')",
3
3
  "Invalid host (no protocol, no spaces)": "Invalid host (no protocol, no spaces)",
4
- "Comma-separated numbers only, max 4 values (e.g. '22,28')": "Comma-separated numbers only, max 4 values (e.g. '22,28')"
4
+ "Only a-z, 0-9, _ and - (or leave empty)": "Only a-z, 0-9, _ and - (or leave empty)"
5
5
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "Only a-z, 0-9, _ and - (or leave empty)": "Solo a-z, 0-9, _ y - (o dejar vacío)",
2
+ "Comma-separated numbers only, max 4 values (e.g. '22,28')": "Solo números separados por comas, máx. 4 valores (p. ej., '22,28')",
3
3
  "Invalid host (no protocol, no spaces)": "Host no válido (sin protocolo, sin espacios)",
4
- "Comma-separated numbers only, max 4 values (e.g. '22,28')": "Solo números separados por comas, máx. 4 valores (p. ej., '22,28')"
4
+ "Only a-z, 0-9, _ and - (or leave empty)": "Solo a-z, 0-9, _ y - (o dejar vacío)"
5
5
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "Only a-z, 0-9, _ and - (or leave empty)": "Uniquement a-z, 0-9, _ et - (ou laisser vide)",
2
+ "Comma-separated numbers only, max 4 values (e.g. '22,28')": "Uniquement des nombres séparés par des virgules, 4 valeurs max. (p. ex. '22,28')",
3
3
  "Invalid host (no protocol, no spaces)": "Hôte invalide (pas de protocole, pas d'espaces)",
4
- "Comma-separated numbers only, max 4 values (e.g. '22,28')": "Uniquement des nombres séparés par des virgules, 4 valeurs max. (p. ex. '22,28')"
4
+ "Only a-z, 0-9, _ and - (or leave empty)": "Uniquement a-z, 0-9, _ et - (ou laisser vide)"
5
5
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "Only a-z, 0-9, _ and - (or leave empty)": "Solo a-z, 0-9, _ e - (o lasciare vuoto)",
2
+ "Comma-separated numbers only, max 4 values (e.g. '22,28')": "Solo numeri separati da virgole, max 4 valori (ad es. '22,28')",
3
3
  "Invalid host (no protocol, no spaces)": "Host non valido (nessun protocollo, nessuno spazio)",
4
- "Comma-separated numbers only, max 4 values (e.g. '22,28')": "Solo numeri separati da virgole, max 4 valori (ad es. '22,28')"
4
+ "Only a-z, 0-9, _ and - (or leave empty)": "Solo a-z, 0-9, _ e - (o lasciare vuoto)"
5
5
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "Only a-z, 0-9, _ and - (or leave empty)": "Alleen a-z, 0-9, _ en - (of leeg laten)",
2
+ "Comma-separated numbers only, max 4 values (e.g. '22,28')": "Alleen door komma's gescheiden getallen, max. 4 waarden (bijv. '22,28')",
3
3
  "Invalid host (no protocol, no spaces)": "Ongeldige host (geen protocol, geen spaties)",
4
- "Comma-separated numbers only, max 4 values (e.g. '22,28')": "Alleen door komma's gescheiden getallen, max. 4 waarden (bijv. '22,28')"
4
+ "Only a-z, 0-9, _ and - (or leave empty)": "Alleen a-z, 0-9, _ en - (of leeg laten)"
5
5
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "Only a-z, 0-9, _ and - (or leave empty)": "Tylko a-z, 0-9, _ i - (lub pozostaw puste)",
2
+ "Comma-separated numbers only, max 4 values (e.g. '22,28')": "Tylko liczby oddzielone przecinkami, maks. 4 wartości (np. '22,28')",
3
3
  "Invalid host (no protocol, no spaces)": "Nieprawidłowy host (bez protokołu, bez spacji)",
4
- "Comma-separated numbers only, max 4 values (e.g. '22,28')": "Tylko liczby oddzielone przecinkami, maks. 4 wartości (np. '22,28')"
4
+ "Only a-z, 0-9, _ and - (or leave empty)": "Tylko a-z, 0-9, _ i - (lub pozostaw puste)"
5
5
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "Only a-z, 0-9, _ and - (or leave empty)": "Apenas a-z, 0-9, _ e - (ou deixe vazio)",
2
+ "Comma-separated numbers only, max 4 values (e.g. '22,28')": "Apenas números separados por vírgulas, máx. 4 valores (por exemplo, '22,28')",
3
3
  "Invalid host (no protocol, no spaces)": "Host inválido (sem protocolo, sem espaços)",
4
- "Comma-separated numbers only, max 4 values (e.g. '22,28')": "Apenas números separados por vírgulas, máx. 4 valores (por exemplo, '22,28')"
4
+ "Only a-z, 0-9, _ and - (or leave empty)": "Apenas a-z, 0-9, _ e - (ou deixe vazio)"
5
5
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "Only a-z, 0-9, _ and - (or leave empty)": "Только a-z, 0-9, _ и - (или оставьте пустым)",
2
+ "Comma-separated numbers only, max 4 values (e.g. '22,28')": "Только числа через запятую, максимум 4 значения (например, '22,28')",
3
3
  "Invalid host (no protocol, no spaces)": "Недопустимый хост (без протокола, без пробелов)",
4
- "Comma-separated numbers only, max 4 values (e.g. '22,28')": "Только числа через запятую, максимум 4 значения (например, '22,28')"
4
+ "Only a-z, 0-9, _ and - (or leave empty)": "Только a-z, 0-9, _ и - (или оставьте пустым)"
5
5
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "Only a-z, 0-9, _ and - (or leave empty)": "Лише a-z, 0-9, _ і - (або залиште порожнім)",
2
+ "Comma-separated numbers only, max 4 values (e.g. '22,28')": "Лише числа через кому, максимум 4 значення (наприклад, '22,28')",
3
3
  "Invalid host (no protocol, no spaces)": "Недійсний хост (без протоколу, без пробілів)",
4
- "Comma-separated numbers only, max 4 values (e.g. '22,28')": "Лише числа через кому, максимум 4 значення (наприклад, '22,28')"
4
+ "Only a-z, 0-9, _ and - (or leave empty)": "Лише a-z, 0-9, _ і - (або залиште порожнім)"
5
5
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "Only a-z, 0-9, _ and - (or leave empty)": "仅限 a-z、0-9、_ -(或留空)",
2
+ "Comma-separated numbers only, max 4 values (e.g. '22,28')": "仅限逗号分隔的数字,最多 4 个值(例如 '22,28')",
3
3
  "Invalid host (no protocol, no spaces)": "主机无效(不含协议,不含空格)",
4
- "Comma-separated numbers only, max 4 values (e.g. '22,28')": "仅限逗号分隔的数字,最多 4 个值(例如 '22,28')"
4
+ "Only a-z, 0-9, _ and - (or leave empty)": "仅限 a-z、0-9、_ -(或留空)"
5
5
  }
package/io-package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "zeptrion",
4
- "version": "1.0.7",
4
+ "version": "1.0.8",
5
5
  "news": {
6
+ "1.0.8": {
7
+ "en": "Fix all findings from the manual maintainer review (PR #6327): removed the manual npm installation section from README.md/README_de.md (E6012, prohibited regardless of stated intent); added a verified link to the Feller product page; translated all 40+ German log messages to English (UI-facing result text for CSV import/discovery, shown in the admin config dialog, is intentionally kept German and decoupled from the log call); converted all 50 German common.name strings (incl. the CH_BUTTONS constant and dynamic channel/scene names) to full 11-language i18n objects; completed io-package.json instanceObjects translations for 'info' and 'control' (info.connection already had all 11 languages).",
8
+ "de": "Behebt alle Befunde aus dem manuellen Maintainer-Review (PR #6327): manuelle npm-Installationsanleitung aus README.md/README_de.md entfernt (E6012, unabhängig von der Absicht untersagt); verifizierten Link zur Feller-Produktseite ergänzt; alle 40+ deutschen Log-Meldungen ins Englische übersetzt (UI-Feedback-Text für CSV-Import/Discovery im Admin-Dialog bewusst Deutsch belassen und vom Log-Aufruf entkoppelt); alle 50 deutschen common.name-Strings (inkl. CH_BUTTONS-Konstante und dynamischer Kanal-/Szenen-Namen) auf vollständige 11-Sprachen-i18n-Objekte umgestellt; io-package.json instanceObjects-Übersetzungen für 'info' und 'control' vervollständigt (info.connection hatte bereits alle 11 Sprachen).",
9
+ "ru": "Исправлены все замечания из ручного обзора мейнтейнера (PR #6327): удалена инструкция по ручной установке через npm; добавлена ссылка на страницу продукта Feller; все логи переведены на английский; все имена объектов переведены на 11 языков.",
10
+ "pt": "Corrigidos todos os pontos do review manual do mantenedor (PR #6327): removida a instalação manual via npm; adicionado link para a página do produto Feller; todos os logs traduzidos para inglês; todos os nomes de objetos convertidos para 11 idiomas.",
11
+ "nl": "Alle bevindingen uit de handmatige maintainer-review verholpen (PR #6327): handmatige npm-installatie-instructie verwijderd; link naar de Feller-productpagina toegevoegd; alle logberichten vertaald naar het Engels; alle objectnamen omgezet naar 11 talen.",
12
+ "fr": "Correction de tous les points du contrôle manuel du mainteneur (PR #6327) : suppression de l'installation manuelle via npm ; ajout d'un lien vers la page produit Feller ; tous les journaux traduits en anglais ; tous les noms d'objets convertis en 11 langues.",
13
+ "it": "Corretti tutti i rilievi della revisione manuale del maintainer (PR #6327): rimossa l'installazione manuale via npm; aggiunto link alla pagina prodotto Feller; tutti i log tradotti in inglese; tutti i nomi degli oggetti convertiti in 11 lingue.",
14
+ "es": "Se corrigieron todos los hallazgos de la revisión manual del mantenedor (PR #6327): se eliminó la instalación manual vía npm; se añadió un enlace a la página del producto Feller; todos los logs traducidos al inglés; todos los nombres de objetos convertidos a 11 idiomas.",
15
+ "pl": "Naprawiono wszystkie uwagi z ręcznego przeglądu opiekuna (PR #6327): usunięto ręczną instalację przez npm; dodano link do strony produktu Feller; wszystkie logi przetłumaczono na angielski; wszystkie nazwy obiektów przekonwertowano na 11 języków.",
16
+ "uk": "Виправлено всі зауваження з ручного огляду мейнтейнера (PR #6327): видалено ручну інсталяцію через npm; додано посилання на сторінку продукту Feller; усі логи перекладено англійською; усі назви об'єктів перекладено на 11 мов.",
17
+ "zh-cn": "修复了维护者人工审查(PR #6327)中的所有问题:移除了手动 npm 安装说明;添加了 Feller 产品页面链接;所有日志信息已翻译为英文;所有对象名称已转换为 11 种语言的 i18n 对象。"
18
+ },
6
19
  "1.0.7": {
7
20
  "en": "Enable global i18n support (jsonConfig i18n: true) with translation files under admin/i18n/ for all 11 supported languages, resolving the checker's i18n warnings the correct way (validatorErrorText stays a plain string per schema; ioBroker resolves the translation via the files, falling back to the English text if no entry is found). Added @iobroker/adapter-dev and @alcalzone/release-script as devDependencies with translate/release npm scripts. (Migrating to @iobroker/eslint-config was evaluated but reverted: its eslint-plugin-import dependency does not yet support eslint 10.x, which broke npm install.)",
8
21
  "de": "Globale i18n-Unterstützung aktiviert (jsonConfig i18n: true) mit Übersetzungsdateien unter admin/i18n/ für alle 11 unterstützten Sprachen - löst die i18n-Warnungen des Checkers auf die vorgesehene Weise (validatorErrorText bleibt gemäss Schema ein einfacher String; ioBroker löst die Übersetzung über die Dateien auf und fällt bei fehlendem Eintrag auf den englischen Text zurück). @iobroker/adapter-dev und @alcalzone/release-script als devDependencies mit translate/release-Skripten ergänzt. (Die Migration auf @iobroker/eslint-config wurde geprüft, aber zurückgenommen: dessen Abhängigkeit eslint-plugin-import unterstützt eslint 10.x noch nicht und hätte npm install gebrochen.)",
@@ -80,19 +93,6 @@
80
93
  "pl": "POPRAWKA: globalne tłumaczenia info.connection dodane w 1.0.1 są automatycznie synchronizowane przez js-controller tylko przy określonych ścieżkach aktualizacji, niewiarygodnie przy instalacji przez 'iobroker url' - dodano również do migracji przy starcie.",
81
94
  "uk": "ВИПРАВЛЕННЯ: глобальні переклади info.connection, додані в 1.0.1, синхронізуються js-controller автоматично лише за певних шляхів оновлення, ненадійно при встановленні через 'iobroker url' - додано також до міграції під час запуску.",
82
95
  "zh-cn": "修复:1.0.1 中添加的全局 info.connection 翻译仅在特定更新路径(例如 'iobroker upgrade')下由 js-controller 自动同步,通过 'iobroker url' 安装时并不可靠——现已同样加入启动迁移中。"
83
- },
84
- "1.0.1": {
85
- "en": "FIX: calibrate declared role 'level' but read=false - role 'level' requires read=true per the ioBroker role catalogue. Changed to read=true and extended the startup migration to also correct existing objects still holding the old read=false. Added the missing translations for the global info.connection object (was only en/de).",
86
- "de": "FIX: calibrate deklarierte Rolle 'level', aber read=false - die Rolle 'level' erfordert laut ioBroker-Rollenkatalog read=true. Auf read=true geändert und die Start-Migration erweitert, damit auch bestehende Objekte mit dem alten read=false korrigiert werden. Fehlende Übersetzungen für das globale info.connection-Objekt ergänzt (waren nur en/de).",
87
- "ru": "ИСПРАВЛЕНИЕ: calibrate объявлял роль 'level', но read=false - роль 'level' требует read=true согласно каталогу ролей ioBroker. Изменено на read=true, миграция при запуске расширена. Добавлены отсутствующие переводы для глобального объекта info.connection.",
88
- "pt": "CORREÇÃO: calibrate declarava a função 'level', mas read=false - a função 'level' exige read=true segundo o catálogo de funções do ioBroker. Alterado para read=true, migração de arranque estendida. Adicionadas as traduções em falta para o objeto global info.connection.",
89
- "nl": "FIX: calibrate declareerde rol 'level', maar read=false - rol 'level' vereist read=true volgens de ioBroker-rolcatalogus. Gewijzigd naar read=true, opstartmigratie uitgebreid. Ontbrekende vertalingen voor het globale info.connection-object toegevoegd.",
90
- "fr": "CORRECTIF : calibrate déclarait le rôle 'level', mais read=false - le rôle 'level' nécessite read=true selon le catalogue de rôles ioBroker. Changé en read=true, migration au démarrage étendue. Traductions manquantes pour l'objet global info.connection ajoutées.",
91
- "it": "CORREZIONE: calibrate dichiarava il ruolo 'level', ma read=false - il ruolo 'level' richiede read=true secondo il catalogo dei ruoli di ioBroker. Cambiato in read=true, migrazione all'avvio estesa. Aggiunte le traduzioni mancanti per l'oggetto globale info.connection.",
92
- "es": "CORRECCIÓN: calibrate declaraba el rol 'level', pero read=false - el rol 'level' requiere read=true según el catálogo de roles de ioBroker. Cambiado a read=true, migración de inicio ampliada. Se añadieron las traducciones faltantes para el objeto global info.connection.",
93
- "pl": "POPRAWKA: calibrate deklarował rolę 'level', ale read=false - rola 'level' wymaga read=true zgodnie z katalogiem ról ioBroker. Zmieniono na read=true, rozszerzono migrację przy starcie. Dodano brakujące tłumaczenia dla globalnego obiektu info.connection.",
94
- "uk": "ВИПРАВЛЕННЯ: calibrate декларував роль 'level', але read=false - роль 'level' вимагає read=true згідно з каталогом ролей ioBroker. Змінено на read=true, розширено міграцію під час запуску. Додано відсутні переклади для глобального об'єкта info.connection.",
95
- "zh-cn": "修复:calibrate 声明角色为 'level',但 read=false——根据 ioBroker 角色目录,角色 'level' 要求 read=true。已更改为 read=true,并扩展了启动迁移。补充了全局 info.connection 对象缺失的翻译。"
96
96
  }
97
97
  },
98
98
  "titleLang": {
@@ -178,7 +178,16 @@
178
178
  "common": {
179
179
  "name": {
180
180
  "en": "Information",
181
- "de": "Information"
181
+ "de": "Information",
182
+ "ru": "Информация",
183
+ "pt": "Informação",
184
+ "nl": "Informatie",
185
+ "fr": "Informations",
186
+ "it": "Informazioni",
187
+ "es": "Información",
188
+ "pl": "Informacje",
189
+ "uk": "Інформація",
190
+ "zh-cn": "信息"
182
191
  }
183
192
  },
184
193
  "native": {}
@@ -214,7 +223,16 @@
214
223
  "common": {
215
224
  "name": {
216
225
  "en": "Collective commands",
217
- "de": "Sammelbefehle"
226
+ "de": "Sammelbefehle",
227
+ "ru": "Групповые команды",
228
+ "pt": "Comandos coletivos",
229
+ "nl": "Verzamelcommando's",
230
+ "fr": "Commandes groupées",
231
+ "it": "Comandi collettivi",
232
+ "es": "Comandos colectivos",
233
+ "pl": "Polecenia zbiorcze",
234
+ "uk": "Групові команди",
235
+ "zh-cn": "集合命令"
218
236
  }
219
237
  },
220
238
  "native": {}
package/main.js CHANGED
@@ -94,16 +94,16 @@ const MAX_TIMED_MS = 32000;
94
94
  const DRIVE_GAP_MS = 400;
95
95
 
96
96
  const CH_BUTTONS = {
97
- stop: 'Stopp',
98
- on: 'Ein (100%)',
99
- off: 'Aus (0%)',
100
- toggle: 'Umschalten',
101
- open: 'Öffnen',
102
- close: 'Schliessen',
103
- move_open: 'Öffnen (Taste halten)',
104
- move_close: 'Schliessen (Taste halten)',
105
- dim_up: 'Dimmen hoch (Taste halten)',
106
- dim_down: 'Dimmen runter (Taste halten)'
97
+ stop: { en: 'Stop', de: 'Stopp', ru: 'Стоп', pt: 'Parar', nl: 'Stop', fr: 'Arrêt', it: 'Stop', es: 'Detener', pl: 'Stop', uk: 'Стоп', 'zh-cn': '停止' },
98
+ on: { en: 'On (100%)', de: 'Ein (100%)', ru: 'Вкл (100%)', pt: 'Ligado (100%)', nl: 'Aan (100%)', fr: 'Marche (100%)', it: 'Acceso (100%)', es: 'Encendido (100%)', pl: 'Wł. (100%)', uk: 'Увімк (100%)', 'zh-cn': '开 (100%)' },
99
+ off: { en: 'Off (0%)', de: 'Aus (0%)', ru: 'Выкл (0%)', pt: 'Desligado (0%)', nl: 'Uit (0%)', fr: 'Arrêt (0%)', it: 'Spento (0%)', es: 'Apagado (0%)', pl: 'Wył. (0%)', uk: 'Вимк (0%)', 'zh-cn': '关 (0%)' },
100
+ toggle: { en: 'Toggle', de: 'Umschalten', ru: 'Переключить', pt: 'Alternar', nl: 'Omschakelen', fr: 'Basculer', it: 'Commuta', es: 'Alternar', pl: 'Przełącz', uk: 'Перемкнути', 'zh-cn': '切换' },
101
+ open: { en: 'Open', de: 'Öffnen', ru: 'Открыть', pt: 'Abrir', nl: 'Openen', fr: 'Ouvrir', it: 'Apri', es: 'Abrir', pl: 'Otwórz', uk: 'Відкрити', 'zh-cn': '打开' },
102
+ close: { en: 'Close', de: 'Schliessen', ru: 'Закрыть', pt: 'Fechar', nl: 'Sluiten', fr: 'Fermer', it: 'Chiudi', es: 'Cerrar', pl: 'Zamknij', uk: 'Закрити', 'zh-cn': '关闭' },
103
+ move_open: { en: 'Open (hold button)', de: 'Öffnen (Taste halten)', ru: 'Открыть (удерж. кнопку)', pt: 'Abrir (manter botão)', nl: 'Openen (knop ingedrukt houden)', fr: 'Ouvrir (maintenir le bouton)', it: 'Apri (tieni premuto)', es: 'Abrir (mantener pulsado)', pl: 'Otwórz (przytrzymaj przycisk)', uk: 'Відкрити (утримувати кнопку)', 'zh-cn': '打开(长按按钮)' },
104
+ move_close: { en: 'Close (hold button)', de: 'Schliessen (Taste halten)', ru: 'Закрыть (удерж. кнопку)', pt: 'Fechar (manter botão)', nl: 'Sluiten (knop ingedrukt houden)', fr: 'Fermer (maintenir le bouton)', it: 'Chiudi (tieni premuto)', es: 'Cerrar (mantener pulsado)', pl: 'Zamknij (przytrzymaj przycisk)', uk: 'Закрити (утримувати кнопку)', 'zh-cn': '关闭(长按按钮)' },
105
+ dim_up: { en: 'Dim up (hold button)', de: 'Dimmen hoch (Taste halten)', ru: 'Увеличить яркость (удерж. кнопку)', pt: 'Aumentar luminosidade (manter botão)', nl: 'Dimmen omhoog (knop ingedrukt houden)', fr: 'Augmenter la lumière (maintenir le bouton)', it: 'Aumenta luminosità (tieni premuto)', es: 'Aumentar brillo (mantener pulsado)', pl: 'Rozjaśnij (przytrzymaj przycisk)', uk: 'Збільшити яскравість (утримувати кнопку)', 'zh-cn': '调亮(长按按钮)' },
106
+ dim_down: { en: 'Dim down (hold button)', de: 'Dimmen runter (Taste halten)', ru: 'Уменьшить яркость (удерж. кнопку)', pt: 'Diminuir luminosidade (manter botão)', nl: 'Dimmen omlaag (knop ingedrukt houden)', fr: 'Diminuer la lumière (maintenir le bouton)', it: 'Diminuisci luminosità (tieni premuto)', es: 'Disminuir brillo (mantener pulsado)', pl: 'Przyciemnij (przytrzymaj przycisk)', uk: 'Зменшити яскравість (утримувати кнопку)', 'zh-cn': '调暗(长按按钮)' }
107
107
  };
108
108
 
109
109
  class Zeptrion extends utils.Adapter {
@@ -233,7 +233,7 @@ class Zeptrion extends utils.Adapter {
233
233
  usedIds.add(candidate);
234
234
  if (!d.name) d.name = d.host;
235
235
  cfgChanged = true;
236
- this.log.info(`Geräte-ID automatisch vergeben: "${candidate}" für Host ${d.host}`);
236
+ this.log.info(`Device ID auto-assigned: "${candidate}" for host ${d.host}`);
237
237
  }
238
238
  }
239
239
  if (cfgChanged) {
@@ -262,7 +262,7 @@ class Zeptrion extends utils.Adapter {
262
262
  const hostKey = String(d.host).trim().toLowerCase();
263
263
  if (seenHosts.has(hostKey)) errs.push(`Host "${d.host}" ist doppelt konfiguriert`);
264
264
  if (errs.length) {
265
- this.log.error(`Gerät "${d.name || d.id || d.host}" übersprungen: ${errs.join('; ')}`);
265
+ this.log.error(`Device "${d.name || d.id || d.host}" skipped: ${errs.join('; ')}`);
266
266
  continue;
267
267
  }
268
268
  seenIds.add(sanId);
@@ -271,7 +271,7 @@ class Zeptrion extends utils.Adapter {
271
271
  }
272
272
 
273
273
  if (!validated.length && active.length) {
274
- this.log.error('Alle konfigurierten Geräte sind ungültig - bitte Konfiguration prüfen (Test-Button verwenden).');
274
+ this.log.error('All configured devices are invalid - please check the configuration (use the Test button).');
275
275
  }
276
276
 
277
277
  // --- Paralleles Setup ---
@@ -281,7 +281,7 @@ class Zeptrion extends utils.Adapter {
281
281
  try {
282
282
  await this.setupDevice(dev);
283
283
  } catch (err) {
284
- this.log.error(`Gerät ${dev.id} konnte nicht initialisiert werden: ${err.message || err}`);
284
+ this.log.error(`Device ${dev.id} could not be initialized: ${err.message || err}`);
285
285
  }
286
286
  }));
287
287
 
@@ -292,7 +292,7 @@ class Zeptrion extends utils.Adapter {
292
292
  await this.cleanupOrphanedDevices(validated);
293
293
 
294
294
  if (!active.length) {
295
- this.log.warn('Keine aktiven zeptrion Geräte konfiguriert. Bitte in der Instanz-Konfiguration Geräte anlegen oder Discovery-Button verwenden.');
295
+ this.log.warn('No active zeptrion devices configured. Please add devices in the instance configuration or use the Discovery button.');
296
296
  }
297
297
 
298
298
  await this.createGlobalControlObjects();
@@ -321,12 +321,12 @@ class Zeptrion extends utils.Adapter {
321
321
  }
322
322
  for (const devId of deviceIds) {
323
323
  if (!keepIds.has(devId)) {
324
- this.log.info(`Entferne verwaistes Gerät "${devId}" (nicht mehr in der Konfiguration).`);
324
+ this.log.info(`Removing orphaned device "${devId}" (no longer in the configuration).`);
325
325
  await this.delObjectAsync(devId, { recursive: true });
326
326
  }
327
327
  }
328
328
  } catch (err) {
329
- this.log.warn(`Aufräumen verwaister Objekte fehlgeschlagen: ${err.message || err}`);
329
+ this.log.warn(`Cleanup of orphaned objects failed: ${err.message || err}`);
330
330
  }
331
331
  }
332
332
 
@@ -399,19 +399,19 @@ class Zeptrion extends utils.Adapter {
399
399
  async createGlobalControlObjects() {
400
400
  await this.setObjectNotExistsAsync('control', {
401
401
  type: 'channel',
402
- common: { name: 'Sammelbefehle' },
402
+ common: { name: { en: 'Collective commands', de: 'Sammelbefehle', ru: 'Групповые команды', pt: 'Comandos coletivos', nl: 'Verzamelcommando\'s', fr: 'Commandes groupées', it: 'Comandi collettivi', es: 'Comandos colectivos', pl: 'Polecenia zbiorcze', uk: 'Групові команди', 'zh-cn': '集合命令' } },
403
403
  native: {}
404
404
  });
405
405
  await this.ensureState('control.closeAllShutters', {
406
- name: 'ALLE Storen schliessen (z.B. Hagelalarm)',
406
+ name: { en: 'Close ALL shutters (e.g. hail alarm)', de: 'ALLE Storen schliessen (z.B. Hagelalarm)', ru: 'Закрыть ВСЕ жалюзи (напр. градовая тревога)', pt: 'Fechar TODOS os estores (p.ex. alarme de granizo)', nl: 'ALLE zonweringen sluiten (bijv. hagelalarm)', fr: 'Fermer TOUS les stores (p.ex. alarme grêle)', it: 'Chiudi TUTTE le tapparelle (es. allarme grandine)', es: 'Cerrar TODAS las persianas (p.ej. alarma de granizo)', pl: 'Zamknij WSZYSTKIE rolety (np. alarm gradowy)', uk: 'Закрити ВСІ жалюзі (напр. градова тривога)', 'zh-cn': '关闭所有卷帘(例如冰雹警报)' },
407
407
  type: 'boolean', role: 'button', read: false, write: true, def: false
408
408
  });
409
409
  await this.ensureState('control.openAllShutters', {
410
- name: 'Alle Storen öffnen',
410
+ name: { en: 'Open all shutters', de: 'Alle Storen öffnen', ru: 'Открыть все жалюзи', pt: 'Abrir todos os estores', nl: 'Alle zonweringen openen', fr: 'Ouvrir tous les stores', it: 'Apri tutte le tapparelle', es: 'Abrir todas las persianas', pl: 'Otwórz wszystkie rolety', uk: 'Відкрити всі жалюзі', 'zh-cn': '打开所有卷帘' },
411
411
  type: 'boolean', role: 'button', read: false, write: true, def: false
412
412
  });
413
413
  await this.ensureState('control.stopAllShutters', {
414
- name: 'Alle Storen stoppen',
414
+ name: { en: 'Stop all shutters', de: 'Alle Storen stoppen', ru: 'Остановить все жалюзи', pt: 'Parar todos os estores', nl: 'Alle zonweringen stoppen', fr: 'Arrêter tous les stores', it: 'Ferma tutte le tapparelle', es: 'Detener todas las persianas', pl: 'Zatrzymaj wszystkie rolety', uk: 'Зупинити всі жалюзі', 'zh-cn': '停止所有卷帘' },
415
415
  type: 'boolean', role: 'button', read: false, write: true, def: false
416
416
  });
417
417
  }
@@ -435,7 +435,7 @@ class Zeptrion extends utils.Adapter {
435
435
  const smartfront = devCfg.smartfront === true;
436
436
 
437
437
  if (!host) {
438
- this.log.warn(`Gerät ${id}: kein Host angegeben, wird übersprungen.`);
438
+ this.log.warn(`Device ${id}: no host specified, skipping.`);
439
439
  return;
440
440
  }
441
441
 
@@ -468,7 +468,7 @@ class Zeptrion extends utils.Adapter {
468
468
  if (this.config.useNotify !== false) {
469
469
  this.startNotifyLoop(id);
470
470
  } else {
471
- this.log.info(`[${id}] chnotify-Long-Poll per Konfiguration deaktiviert, nur Intervall-Polling aktiv.`);
471
+ this.log.info(`[${id}] chnotify long-poll disabled by configuration, interval polling only.`);
472
472
  }
473
473
  }
474
474
 
@@ -482,7 +482,7 @@ class Zeptrion extends utils.Adapter {
482
482
  });
483
483
 
484
484
  // --- info ---
485
- await this.setObjectNotExistsAsync(`${id}.info`, { type: 'channel', common: { name: 'Geräteinformationen' }, native: {} });
485
+ await this.setObjectNotExistsAsync(`${id}.info`, { type: 'channel', common: { name: { en: 'Device information', de: 'Geräteinformationen', ru: 'Информация об устройстве', pt: 'Informação do dispositivo', nl: 'Apparaatinformatie', fr: 'Informations sur l\'appareil', it: 'Informazioni sul dispositivo', es: 'Información del dispositivo', pl: 'Informacje o urządzeniu', uk: 'Інформація про пристрій', 'zh-cn': '设备信息' } }, native: {} });
486
486
  await this.ensureState(`${id}.info.connection`, {
487
487
  name: {
488
488
  en: 'Connection OK',
@@ -499,19 +499,19 @@ class Zeptrion extends utils.Adapter {
499
499
  },
500
500
  type: 'boolean', role: 'indicator.reachable', read: true, write: false, def: false
501
501
  });
502
- await this.ensureState(`${id}.info.lastError`, { name: 'Letzter Fehler', type: 'string', role: 'text', read: true, write: false, def: '' });
503
- await this.ensureState(`${id}.info.hw`, { name: 'Hardware-Version', type: 'string', role: 'info.hardware', read: true, write: false });
504
- await this.ensureState(`${id}.info.sw`, { name: 'Software-Version', type: 'string', role: 'info.firmware', read: true, write: false });
505
- await this.ensureState(`${id}.info.boot`, { name: 'Bootloader-Version', type: 'string', role: 'text', read: true, write: false });
506
- await this.ensureState(`${id}.info.sn`, { name: 'Seriennummer', type: 'string', role: 'info.serial', read: true, write: false });
507
- await this.ensureState(`${id}.info.sys`, { name: 'System-Name', type: 'string', role: 'text', read: true, write: false });
508
- await this.ensureState(`${id}.info.type`, { name: 'Gerätetyp (Device ID)', type: 'string', role: 'text', read: true, write: false });
502
+ await this.ensureState(`${id}.info.lastError`, { name: { en: 'Last error', de: 'Letzter Fehler', ru: 'Последняя ошибка', pt: 'Último erro', nl: 'Laatste fout', fr: 'Dernière erreur', it: 'Ultimo errore', es: 'Último error', pl: 'Ostatni błąd', uk: 'Остання помилка', 'zh-cn': '最近错误' }, type: 'string', role: 'text', read: true, write: false, def: '' });
503
+ await this.ensureState(`${id}.info.hw`, { name: { en: 'Hardware version', de: 'Hardware-Version', ru: 'Версия оборудования', pt: 'Versão do hardware', nl: 'Hardwareversie', fr: 'Version matérielle', it: 'Versione hardware', es: 'Versión de hardware', pl: 'Wersja sprzętu', uk: 'Версія обладнання', 'zh-cn': '硬件版本' }, type: 'string', role: 'info.hardware', read: true, write: false });
504
+ await this.ensureState(`${id}.info.sw`, { name: { en: 'Software version', de: 'Software-Version', ru: 'Версия ПО', pt: 'Versão do software', nl: 'Softwareversie', fr: 'Version logicielle', it: 'Versione software', es: 'Versión de software', pl: 'Wersja oprogramowania', uk: 'Версія ПЗ', 'zh-cn': '软件版本' }, type: 'string', role: 'info.firmware', read: true, write: false });
505
+ await this.ensureState(`${id}.info.boot`, { name: { en: 'Bootloader version', de: 'Bootloader-Version', ru: 'Версия загрузчика', pt: 'Versão do bootloader', nl: 'Bootloaderversie', fr: 'Version du bootloader', it: 'Versione bootloader', es: 'Versión del bootloader', pl: 'Wersja bootloadera', uk: 'Версія завантажувача', 'zh-cn': '引导程序版本' }, type: 'string', role: 'text', read: true, write: false });
506
+ await this.ensureState(`${id}.info.sn`, { name: { en: 'Serial number', de: 'Seriennummer', ru: 'Серийный номер', pt: 'Número de série', nl: 'Serienummer', fr: 'Numéro de série', it: 'Numero di serie', es: 'Número de serie', pl: 'Numer seryjny', uk: 'Серійний номер', 'zh-cn': '序列号' }, type: 'string', role: 'info.serial', read: true, write: false });
507
+ await this.ensureState(`${id}.info.sys`, { name: { en: 'System name', de: 'System-Name', ru: 'Имя системы', pt: 'Nome do sistema', nl: 'Systeemnaam', fr: 'Nom du système', it: 'Nome del sistema', es: 'Nombre del sistema', pl: 'Nazwa systemu', uk: 'Ім\'я системи', 'zh-cn': '系统名称' }, type: 'string', role: 'text', read: true, write: false });
508
+ await this.ensureState(`${id}.info.type`, { name: { en: 'Device type (Device ID)', de: 'Gerätetyp (Device ID)', ru: 'Тип устройства (Device ID)', pt: 'Tipo de dispositivo (Device ID)', nl: 'Apparaattype (Device ID)', fr: 'Type d\'appareil (Device ID)', it: 'Tipo di dispositivo (Device ID)', es: 'Tipo de dispositivo (Device ID)', pl: 'Typ urządzenia (Device ID)', uk: 'Тип пристрою (Device ID)', 'zh-cn': '设备类型 (Device ID)' }, type: 'string', role: 'text', read: true, write: false });
509
509
  await this.ensureState(`${id}.info.oen`, { name: 'Owner Environment', type: 'string', role: 'text', read: true, write: false });
510
- await this.ensureState(`${id}.info.rssi`, { name: 'Signalstärke', type: 'number', role: 'value', unit: 'dBm', read: true, write: false });
511
- await this.ensureState(`${id}.info.refresh`, { name: 'Statische Infos neu laden (id/net/chdes)', type: 'boolean', role: 'button', read: false, write: true, def: false });
510
+ await this.ensureState(`${id}.info.rssi`, { name: { en: 'Signal strength', de: 'Signalstärke', ru: 'Мощность сигнала', pt: 'Força do sinal', nl: 'Signaalsterkte', fr: 'Force du signal', it: 'Potenza del segnale', es: 'Intensidad de la señal', pl: 'Siła sygnału', uk: 'Потужність сигналу', 'zh-cn': '信号强度' }, type: 'number', role: 'value', unit: 'dBm', read: true, write: false });
511
+ await this.ensureState(`${id}.info.refresh`, { name: { en: 'Reload static info (id/net/chdes)', de: 'Statische Infos neu laden (id/net/chdes)', ru: 'Перезагрузить статическую информацию (id/net/chdes)', pt: 'Recarregar informação estática (id/net/chdes)', nl: 'Statische info opnieuw laden (id/net/chdes)', fr: 'Recharger les infos statiques (id/net/chdes)', it: 'Ricarica informazioni statiche (id/net/chdes)', es: 'Recargar información estática (id/net/chdes)', pl: 'Odśwież informacje statyczne (id/net/chdes)', uk: 'Перезавантажити статичну інформацію (id/net/chdes)', 'zh-cn': '重新加载静态信息 (id/net/chdes)' }, type: 'boolean', role: 'button', read: false, write: true, def: false });
512
512
 
513
513
  // --- network (read-only Anzeige, siehe README für Gründe) ---
514
- await this.setObjectNotExistsAsync(`${id}.network`, { type: 'channel', common: { name: 'Netzwerk' }, native: {} });
514
+ await this.setObjectNotExistsAsync(`${id}.network`, { type: 'channel', common: { name: { en: 'Network', de: 'Netzwerk', ru: 'Сеть', pt: 'Rede', nl: 'Netwerk', fr: 'Réseau', it: 'Rete', es: 'Red', pl: 'Sieć', uk: 'Мережа', 'zh-cn': '网络' } }, native: {} });
515
515
  const netFields = {
516
516
  ssid: 'SSID', ip: 'IP-Adresse', mac: 'MAC-Adresse',
517
517
  mode: 'Netzwerkmodus (0=AccessPoint, 1=Associate)', enc: 'Verschlüsselung',
@@ -522,40 +522,40 @@ class Zeptrion extends utils.Adapter {
522
522
  }
523
523
 
524
524
  // --- system ---
525
- await this.setObjectNotExistsAsync(`${id}.system`, { type: 'channel', common: { name: 'Systembefehle' }, native: {} });
526
- await this.ensureState(`${id}.system.reboot`, { name: 'Neustart', type: 'boolean', role: 'button', read: false, write: true, def: false });
525
+ await this.setObjectNotExistsAsync(`${id}.system`, { type: 'channel', common: { name: { en: 'System commands', de: 'Systembefehle', ru: 'Системные команды', pt: 'Comandos do sistema', nl: 'Systeemcommando\'s', fr: 'Commandes système', it: 'Comandi di sistema', es: 'Comandos del sistema', pl: 'Polecenia systemowe', uk: 'Системні команди', 'zh-cn': '系统命令' } }, native: {} });
526
+ await this.ensureState(`${id}.system.reboot`, { name: { en: 'Restart', de: 'Neustart', ru: 'Перезапуск', pt: 'Reiniciar', nl: 'Herstarten', fr: 'Redémarrer', it: 'Riavvio', es: 'Reiniciar', pl: 'Restart', uk: 'Перезапуск', 'zh-cn': '重启' }, type: 'boolean', role: 'button', read: false, write: true, def: false });
527
527
  await this.ensureState(`${id}.system.unlock`, {
528
- name: 'Entriegelung für Werksreset (Sicherheitsverriegelung: muss max. 30s VOR factoryDefault auf true gesetzt werden)',
528
+ name: { en: 'Unlock for factory reset (safety interlock: must be set to true max. 30s BEFORE factoryDefault)', de: 'Entriegelung für Werksreset (Sicherheitsverriegelung: muss max. 30s VOR factoryDefault auf true gesetzt werden)', ru: 'Разблокировка для сброса до заводских настроек (интерлок: должно быть true не более чем за 30с ДО factoryDefault)', pt: 'Desbloqueio para reposição de fábrica (interbloqueio de segurança: deve ser definido como true no máx. 30s ANTES de factoryDefault)', nl: 'Ontgrendeling voor fabrieksreset (veiligheidsvergrendeling: moet max. 30s VOOR factoryDefault op true gezet worden)', fr: 'Déverrouillage pour réinitialisation d\'usine (verrouillage de sécurité : doit être mis à true max. 30s AVANT factoryDefault)', it: 'Sblocco per il ripristino di fabbrica (interblocco di sicurezza: deve essere impostato su true max. 30s PRIMA di factoryDefault)', es: 'Desbloqueo para restablecimiento de fábrica (enclavamiento de seguridad: debe ponerse a true máx. 30s ANTES de factoryDefault)', pl: 'Odblokowanie przywracania ustawień fabrycznych (blokada bezpieczeństwa: musi być ustawione na true maks. 30s PRZED factoryDefault)', uk: 'Розблокування для скидання до заводських налаштувань (блокування безпеки: має бути true не більше ніж за 30с ДО factoryDefault)', 'zh-cn': '解锁恢复出厂设置(安全联锁:必须在 factoryDefault 之前最多 30 秒设置为 true)' },
529
529
  type: 'boolean', role: 'button', read: false, write: true, def: false
530
530
  });
531
- await this.ensureState(`${id}.system.factoryDefault`, { name: 'ACHTUNG: Werksreset - löscht ALLE Einstellungen inkl. WLAN, Gerät fällt vom Netz! Erfordert vorheriges system.unlock (30s-Fenster)', type: 'boolean', role: 'button', read: false, write: true, def: false });
532
- await this.ensureState(`${id}.system.networkDefault`, { name: 'Zurück in Access-Point-Modus (Konfiguration bleibt erhalten)', type: 'boolean', role: 'button', read: false, write: true, def: false });
531
+ await this.ensureState(`${id}.system.factoryDefault`, { name: { en: 'WARNING: Factory reset - deletes ALL settings incl. WLAN, device will drop off the network! Requires prior system.unlock (30s window)', de: 'ACHTUNG: Werksreset - löscht ALLE Einstellungen inkl. WLAN, Gerät fällt vom Netz! Erfordert vorheriges system.unlock (30s-Fenster)', ru: 'ВНИМАНИЕ: сброс до заводских настроек - удаляет ВСЕ настройки, вкл. WLAN, устройство отключится от сети! Требуется предварительный system.unlock (окно 30с)', pt: 'ATENÇÃO: reposição de fábrica - apaga TODAS as definições incl. WLAN, o dispositivo sairá da rede! Requer system.unlock prévio (janela de 30s)', nl: 'LET OP: fabrieksreset - wist ALLE instellingen incl. WLAN, apparaat valt van het netwerk! Vereist voorafgaand system.unlock (30s-venster)', fr: 'ATTENTION : réinitialisation d\'usine - supprime TOUS les réglages, y compris le WLAN, l\'appareil sera déconnecté du réseau ! Nécessite un system.unlock préalable (fenêtre de 30s)', it: 'ATTENZIONE: ripristino di fabbrica - cancella TUTTE le impostazioni incl. WLAN, il dispositivo si disconnette dalla rete! Richiede system.unlock preventivo (finestra di 30s)', es: 'ATENCIÓN: restablecimiento de fábrica - borra TODOS los ajustes incl. WLAN, ¡el dispositivo se desconectará de la red! Requiere system.unlock previo (ventana de 30s)', pl: 'UWAGA: przywrócenie ustawień fabrycznych - usuwa WSZYSTKIE ustawienia wraz z WLAN, urządzenie odłączy się od sieci! Wymaga wcześniejszego system.unlock (okno 30s)', uk: 'УВАГА: скидання до заводських налаштувань - видаляє ВСІ налаштування, включно з WLAN, пристрій відключиться від мережі! Потрібен попередній system.unlock (вікно 30с)', 'zh-cn': '警告:恢复出厂设置 - 将删除所有设置(包括 WLAN),设备将离线!需要事先执行 system.unlock(30秒窗口)' }, type: 'boolean', role: 'button', read: false, write: true, def: false });
532
+ await this.ensureState(`${id}.system.networkDefault`, { name: { en: 'Back to access point mode (configuration is retained)', de: 'Zurück in Access-Point-Modus (Konfiguration bleibt erhalten)', ru: 'Вернуться в режим точки доступа (конфигурация сохраняется)', pt: 'Voltar ao modo ponto de acesso (a configuração é mantida)', nl: 'Terug naar access point-modus (configuratie blijft behouden)', fr: 'Retour en mode point d\'accès (la configuration est conservée)', it: 'Torna alla modalità access point (la configurazione viene mantenuta)', es: 'Volver al modo punto de acceso (se conserva la configuración)', pl: 'Powrót do trybu punktu dostępu (konfiguracja zostaje zachowana)', uk: 'Повернутися в режим точки доступу (конфігурація зберігається)', 'zh-cn': '返回接入点模式(保留配置)' }, type: 'boolean', role: 'button', read: false, write: true, def: false });
533
533
 
534
534
  // --- location (zrap/loc) ---
535
- await this.setObjectNotExistsAsync(`${id}.location`, { type: 'channel', common: { name: 'Standort' }, native: {} });
536
- await this.ensureState(`${id}.location.name`, { name: 'Standortbezeichnung (frei wählbar, z.B. "Fideris Valzigg")', type: 'string', role: 'text', read: true, write: true });
535
+ await this.setObjectNotExistsAsync(`${id}.location`, { type: 'channel', common: { name: { en: 'Location', de: 'Standort', ru: 'Местоположение', pt: 'Localização', nl: 'Locatie', fr: 'Emplacement', it: 'Posizione', es: 'Ubicación', pl: 'Lokalizacja', uk: 'Розташування', 'zh-cn': '位置' } }, native: {} });
536
+ await this.ensureState(`${id}.location.name`, { name: { en: 'Location label (freely choosable, e.g. "Fideris Valzigg")', de: 'Standortbezeichnung (frei wählbar, z.B. "Fideris Valzigg")', ru: 'Обозначение местоположения (произвольное, напр. "Fideris Valzigg")', pt: 'Designação da localização (livremente escolhível, p.ex. "Fideris Valzigg")', nl: 'Locatieomschrijving (vrij te kiezen, bijv. "Fideris Valzigg")', fr: 'Nom de l\'emplacement (libre, p.ex. "Fideris Valzigg")', it: 'Descrizione della posizione (a scelta libera, es. "Fideris Valzigg")', es: 'Nombre de la ubicación (libre, p.ej. "Fideris Valzigg")', pl: 'Nazwa lokalizacji (dowolna, np. "Fideris Valzigg")', uk: 'Позначення розташування (довільне, напр. "Fideris Valzigg")', 'zh-cn': '位置名称(可自定义,例如 "Fideris Valzigg")' }, type: 'string', role: 'text', read: true, write: true });
537
537
 
538
538
  // --- ntp (zrap/ntp) ---
539
- await this.setObjectNotExistsAsync(`${id}.ntp`, { type: 'channel', common: { name: 'NTP' }, native: {} });
540
- await this.ensureState(`${id}.ntp.url`, { name: 'NTP-Server (URL/IP, max. 32 Zeichen)', type: 'string', role: 'text', read: true, write: true });
541
- await this.ensureState(`${id}.ntp.per`, { name: 'Abfrageintervall in Stunden (0=deaktiviert)', type: 'number', role: 'level', read: true, write: true, min: 0, max: 255 });
539
+ await this.setObjectNotExistsAsync(`${id}.ntp`, { type: 'channel', common: { name: { en: 'NTP', de: 'NTP', ru: 'NTP', pt: 'NTP', nl: 'NTP', fr: 'NTP', it: 'NTP', es: 'NTP', pl: 'NTP', uk: 'NTP', 'zh-cn': 'NTP' } }, native: {} });
540
+ await this.ensureState(`${id}.ntp.url`, { name: { en: 'NTP server (URL/IP, max. 32 characters)', de: 'NTP-Server (URL/IP, max. 32 Zeichen)', ru: 'NTP-сервер (URL/IP, макс. 32 символа)', pt: 'Servidor NTP (URL/IP, máx. 32 caracteres)', nl: 'NTP-server (URL/IP, max. 32 tekens)', fr: 'Serveur NTP (URL/IP, max. 32 caractères)', it: 'Server NTP (URL/IP, max. 32 caratteri)', es: 'Servidor NTP (URL/IP, máx. 32 caracteres)', pl: 'Serwer NTP (URL/IP, maks. 32 znaki)', uk: 'NTP-сервер (URL/IP, макс. 32 символи)', 'zh-cn': 'NTP 服务器(URL/IP,最多 32 个字符)' }, type: 'string', role: 'text', read: true, write: true });
541
+ await this.ensureState(`${id}.ntp.per`, { name: { en: 'Polling interval in hours (0=disabled)', de: 'Abfrageintervall in Stunden (0=deaktiviert)', ru: 'Интервал опроса в часах (0=отключено)', pt: 'Intervalo de consulta em horas (0=desativado)', nl: 'Poll-interval in uren (0=uitgeschakeld)', fr: 'Intervalle d\'interrogation en heures (0=désactivé)', it: 'Intervallo di polling in ore (0=disattivato)', es: 'Intervalo de consulta en horas (0=desactivado)', pl: 'Interwał odpytywania w godzinach (0=wyłączone)', uk: 'Інтервал опитування в годинах (0=вимкнено)', 'zh-cn': '轮询间隔(小时,0=禁用)' }, type: 'number', role: 'level', read: true, write: true, min: 0, max: 255 });
542
542
 
543
543
  // --- date (zrap/date) ---
544
- await this.setObjectNotExistsAsync(`${id}.date`, { type: 'channel', common: { name: 'Datum/Zeit' }, native: {} });
545
- await this.ensureState(`${id}.date.rfc1123`, { name: 'RFC1123 Zeitstempel (muss GMT sein)', type: 'string', role: 'text', read: true, write: true });
546
- await this.ensureState(`${id}.date.tz`, { name: 'Zeitzonen-Offset HHMM (z.B. +0200)', type: 'string', role: 'text', read: true, write: true });
547
- await this.ensureState(`${id}.date.dst`, { name: 'Sommerzeit-Offset HHMM', type: 'string', role: 'text', read: true, write: true });
548
- await this.ensureState(`${id}.date.syncNow`, { name: 'Button: Geräte-Uhrzeit mit ioBroker-Host synchronisieren', type: 'boolean', role: 'button', read: false, write: true, def: false });
544
+ await this.setObjectNotExistsAsync(`${id}.date`, { type: 'channel', common: { name: { en: 'Date/time', de: 'Datum/Zeit', ru: 'Дата/время', pt: 'Data/hora', nl: 'Datum/tijd', fr: 'Date/heure', it: 'Data/ora', es: 'Fecha/hora', pl: 'Data/godzina', uk: 'Дата/час', 'zh-cn': '日期/时间' } }, native: {} });
545
+ await this.ensureState(`${id}.date.rfc1123`, { name: { en: 'RFC1123 timestamp (must be GMT)', de: 'RFC1123 Zeitstempel (muss GMT sein)', ru: 'Метка времени RFC1123 (должна быть GMT)', pt: 'Timestamp RFC1123 (deve ser GMT)', nl: 'RFC1123-tijdstempel (moet GMT zijn)', fr: 'Horodatage RFC1123 (doit être GMT)', it: 'Timestamp RFC1123 (deve essere GMT)', es: 'Marca de tiempo RFC1123 (debe ser GMT)', pl: 'Znacznik czasu RFC1123 (musi być GMT)', uk: 'Мітка часу RFC1123 (має бути GMT)', 'zh-cn': 'RFC1123 时间戳(必须为 GMT)' }, type: 'string', role: 'text', read: true, write: true });
546
+ await this.ensureState(`${id}.date.tz`, { name: { en: 'Timezone offset HHMM (e.g. +0200)', de: 'Zeitzonen-Offset HHMM (z.B. +0200)', ru: 'Смещение часового пояса HHMM (напр. +0200)', pt: 'Offset de fuso horário HHMM (p.ex. +0200)', nl: 'Tijdzone-offset HHMM (bijv. +0200)', fr: 'Décalage de fuseau horaire HHMM (p.ex. +0200)', it: 'Offset fuso orario HHMM (es. +0200)', es: 'Desfase horario HHMM (p.ej. +0200)', pl: 'Przesunięcie strefy czasowej HHMM (np. +0200)', uk: 'Зміщення часового поясу HHMM (напр. +0200)', 'zh-cn': '时区偏移 HHMM(例如 +0200)' }, type: 'string', role: 'text', read: true, write: true });
547
+ await this.ensureState(`${id}.date.dst`, { name: { en: 'Daylight saving offset HHMM', de: 'Sommerzeit-Offset HHMM', ru: 'Смещение летнего времени HHMM', pt: 'Offset de horário de verão HHMM', nl: 'Zomertijd-offset HHMM', fr: 'Décalage heure d\'été HHMM', it: 'Offset ora legale HHMM', es: 'Desfase de horario de verano HHMM', pl: 'Przesunięcie czasu letniego HHMM', uk: 'Зміщення літнього часу HHMM', 'zh-cn': '夏令时偏移 HHMM' }, type: 'string', role: 'text', read: true, write: true });
548
+ await this.ensureState(`${id}.date.syncNow`, { name: { en: 'Button: synchronize device time with the ioBroker host', de: 'Button: Geräte-Uhrzeit mit ioBroker-Host synchronisieren', ru: 'Кнопка: синхронизировать время устройства с хостом ioBroker', pt: 'Botão: sincronizar hora do dispositivo com o host ioBroker', nl: 'Knop: apparaatklok synchroniseren met de ioBroker-host', fr: 'Bouton : synchroniser l\'heure de l\'appareil avec l\'hôte ioBroker', it: 'Pulsante: sincronizza l\'ora del dispositivo con l\'host ioBroker', es: 'Botón: sincronizar la hora del dispositivo con el host de ioBroker', pl: 'Przycisk: synchronizuj czas urządzenia z hostem ioBroker', uk: 'Кнопка: синхронізувати час пристрою з хостом ioBroker', 'zh-cn': '按钮:将设备时间与 ioBroker 主机同步' }, type: 'boolean', role: 'button', read: false, write: true, def: false });
549
549
 
550
550
  // --- smartfront (zapi, optional - nur bei angeschlossenem Smartfront-Taster) ---
551
551
  if (dev.cfg.smartfront) {
552
- await this.setObjectNotExistsAsync(`${id}.smartfront`, { type: 'channel', common: { name: 'Smartfront' }, native: {} });
553
- await this.ensureState(`${id}.smartfront.temp`, { name: 'Temperatur', type: 'number', role: 'value.temperature', unit: '°C', read: true, write: false });
554
- await this.ensureState(`${id}.smartfront.lux`, { name: 'Helligkeit', type: 'number', role: 'value.brightness', unit: 'lx', read: true, write: false });
555
- await this.ensureState(`${id}.smartfront.hum`, { name: 'Luftfeuchtigkeit', type: 'number', role: 'value.humidity', unit: '%', read: true, write: false });
556
- await this.ensureState(`${id}.smartfront.ledState`, { name: 'Aktueller LED-Status (JSON, read-only)', type: 'string', role: 'json', read: true, write: false });
552
+ await this.setObjectNotExistsAsync(`${id}.smartfront`, { type: 'channel', common: { name: { en: 'Smartfront', de: 'Smartfront', ru: 'Smartfront', pt: 'Smartfront', nl: 'Smartfront', fr: 'Smartfront', it: 'Smartfront', es: 'Smartfront', pl: 'Smartfront', uk: 'Smartfront', 'zh-cn': 'Smartfront' } }, native: {} });
553
+ await this.ensureState(`${id}.smartfront.temp`, { name: { en: 'Temperature', de: 'Temperatur', ru: 'Температура', pt: 'Temperatura', nl: 'Temperatuur', fr: 'Température', it: 'Temperatura', es: 'Temperatura', pl: 'Temperatura', uk: 'Температура', 'zh-cn': '温度' }, type: 'number', role: 'value.temperature', unit: '°C', read: true, write: false });
554
+ await this.ensureState(`${id}.smartfront.lux`, { name: { en: 'Brightness', de: 'Helligkeit', ru: 'Яркость', pt: 'Luminosidade', nl: 'Helderheid', fr: 'Luminosité', it: 'Luminosità', es: 'Luminosidad', pl: 'Jasność', uk: 'Яскравість', 'zh-cn': '亮度' }, type: 'number', role: 'value.brightness', unit: 'lx', read: true, write: false });
555
+ await this.ensureState(`${id}.smartfront.hum`, { name: { en: 'Humidity', de: 'Luftfeuchtigkeit', ru: 'Влажность', pt: 'Humidade', nl: 'Luchtvochtigheid', fr: 'Humidité', it: 'Umidità', es: 'Humedad', pl: 'Wilgotność', uk: 'Вологість', 'zh-cn': '湿度' }, type: 'number', role: 'value.humidity', unit: '%', read: true, write: false });
556
+ await this.ensureState(`${id}.smartfront.ledState`, { name: { en: 'Current LED status (JSON, read-only)', de: 'Aktueller LED-Status (JSON, read-only)', ru: 'Текущий статус светодиода (JSON, только чтение)', pt: 'Estado atual do LED (JSON, só leitura)', nl: 'Huidige LED-status (JSON, alleen-lezen)', fr: 'État actuel de la LED (JSON, lecture seule)', it: 'Stato attuale del LED (JSON, sola lettura)', es: 'Estado actual del LED (JSON, solo lectura)', pl: 'Aktualny stan LED (JSON, tylko do odczytu)', uk: 'Поточний стан світлодіода (JSON, лише читання)', 'zh-cn': '当前 LED 状态(JSON,只读)' }, type: 'string', role: 'json', read: true, write: false });
557
557
  await this.ensureState(`${id}.smartfront.ledSet`, {
558
- name: 'LED(s) setzen - JSON-Array wie in API-Doku 5.1.3.4, z.B. [{"id":2,"bg":"#220000"}]. Laut Doku nur "bg" (Hintergrundfarbe) unbedenklich extern setzbar.',
558
+ name: { en: 'Set LED(s) - JSON array as in API doc 5.1.3.4, e.g. [{"id":2,"bg":"#220000"}]. Per the docs, only "bg" (background color) is safe to set externally.', de: 'LED(s) setzen - JSON-Array wie in API-Doku 5.1.3.4, z.B. [{"id":2,"bg":"#220000"}]. Laut Doku nur "bg" (Hintergrundfarbe) unbedenklich extern setzbar.', ru: 'Установить LED - JSON-массив как в документации API 5.1.3.4, напр. [{"id":2,"bg":"#220000"}]. Согласно документации, только "bg" (цвет фона) безопасно устанавливать извне.', pt: 'Definir LED(s) - array JSON conforme doc. API 5.1.3.4, p.ex. [{"id":2,"bg":"#220000"}]. Segundo a documentação, apenas "bg" (cor de fundo) pode ser definido externamente com segurança.', nl: 'LED(s) instellen - JSON-array zoals in API-doc 5.1.3.4, bijv. [{"id":2,"bg":"#220000"}]. Volgens de documentatie is alleen "bg" (achtergrondkleur) veilig extern instelbaar.', fr: 'Définir la/les LED - tableau JSON comme dans la doc API 5.1.3.4, p.ex. [{"id":2,"bg":"#220000"}]. Selon la doc, seul "bg" (couleur de fond) peut être défini en externe sans risque.', it: 'Imposta LED - array JSON come da documentazione API 5.1.3.4, es. [{"id":2,"bg":"#220000"}]. Secondo la documentazione, solo "bg" (colore di sfondo) è sicuro da impostare esternamente.', es: 'Definir LED(s) - array JSON como en la doc. API 5.1.3.4, p.ej. [{"id":2,"bg":"#220000"}]. Según la documentación, solo "bg" (color de fondo) es seguro de establecer externamente.', pl: 'Ustaw LED - tablica JSON jak w dokumentacji API 5.1.3.4, np. [{"id":2,"bg":"#220000"}]. Wg dokumentacji tylko "bg" (kolor tła) można bezpiecznie ustawiać zewnętrznie.', uk: 'Встановити світлодіоди - JSON-масив як у документації API 5.1.3.4, напр. [{"id":2,"bg":"#220000"}]. Згідно з документацією, лише "bg" (колір фону) безпечно встановлювати ззовні.', 'zh-cn': '设置 LED - JSON 数组,格式见 API 文档 5.1.3.4,例如 [{"id":2,"bg":"#220000"}]。根据文档,仅 "bg"(背景色)可安全地从外部设置。' },
559
559
  type: 'string', role: 'json', read: false, write: true, def: ''
560
560
  });
561
561
  }
@@ -576,17 +576,20 @@ class Zeptrion extends utils.Adapter {
576
576
  ? { stop: 'button.stop', open: 'button.open.blind', close: 'button.close.blind' }
577
577
  : {};
578
578
 
579
- await this.setObjectNotExistsAsync(`${id}.channels`, { type: 'channel', common: { name: 'Kanäle' }, native: {} });
579
+ await this.setObjectNotExistsAsync(`${id}.channels`, { type: 'channel', common: { name: { en: 'Channels', de: 'Kanäle', ru: 'Каналы', pt: 'Canais', nl: 'Kanalen', fr: 'Canaux', it: 'Canali', es: 'Canales', pl: 'Kanały', uk: 'Канали', 'zh-cn': '通道' } }, native: {} });
580
580
  for (let n = 1; n <= channelCount; n++) {
581
581
  const ch = `${id}.channels.ch${n}`;
582
582
  await this.setObjectNotExistsAsync(ch, {
583
583
  type: 'channel',
584
- common: { name: `Kanal ${n}` },
584
+ common: { name: {
585
+ en: `Channel ${n}`, de: `Kanal ${n}`, ru: `Канал ${n}`, pt: `Canal ${n}`, nl: `Kanaal ${n}`,
586
+ fr: `Canal ${n}`, it: `Canale ${n}`, es: `Canal ${n}`, pl: `Kanał ${n}`, uk: `Канал ${n}`, 'zh-cn': `通道 ${n}`
587
+ } },
585
588
  native: { channelNumber: n, host: dev.cfg.host, kind }
586
589
  });
587
590
 
588
591
  await this.ensureState(`${ch}.val`, {
589
- name: 'Zustand (0-100, bei Storen meist -1=unbekannt)',
592
+ name: { en: 'State (0-100, for shutters usually -1=unknown)', de: 'Zustand (0-100, bei Storen meist -1=unbekannt)', ru: 'Состояние (0-100, для жалюзи обычно -1=неизвестно)', pt: 'Estado (0-100, em estores geralmente -1=desconhecido)', nl: 'Status (0-100, bij zonwering meestal -1=onbekend)', fr: 'État (0-100, pour les stores généralement -1=inconnu)', it: 'Stato (0-100, per le tapparelle solitamente -1=sconosciuto)', es: 'Estado (0-100, en persianas normalmente -1=desconocido)', pl: 'Stan (0-100, dla rolet zwykle -1=nieznany)', uk: 'Стан (0-100, для жалюзі зазвичай -1=невідомо)', 'zh-cn': '状态 (0-100,卷帘通常 -1=未知)' },
590
593
  type: 'number', role: valRole, min: -1, max: 100, read: true, write: false
591
594
  });
592
595
 
@@ -615,7 +618,7 @@ class Zeptrion extends utils.Adapter {
615
618
  native: {}
616
619
  });
617
620
  await this.ensureState(`${ch}.calibrate`, {
618
- name: 'Schätzung setzen OHNE Fahrt (z.B. nach manueller Bedienung am Wandtaster): aktuellen Ist-Zustand in % eintragen',
621
+ name: { en: 'Set the estimate WITHOUT moving (e.g. after manual operation at the wall switch): enter the current actual state in %', de: 'Schätzung setzen OHNE Fahrt (z.B. nach manueller Bedienung am Wandtaster): aktuellen Ist-Zustand in % eintragen', ru: 'Установить оценку БЕЗ движения (напр. после ручного управления настенным выключателем): ввести текущее фактическое состояние в %', pt: 'Definir a estimativa SEM movimento (p.ex. após operação manual no interruptor de parede): introduzir o estado atual em %', nl: 'Schatting instellen ZONDER beweging (bijv. na handmatige bediening op de wandschakelaar): huidige werkelijke status in % invoeren', fr: 'Définir l\'estimation SANS mouvement (p.ex. après commande manuelle sur l\'interrupteur mural) : saisir l\'état réel actuel en %', it: 'Imposta la stima SENZA movimento (es. dopo comando manuale sul pulsante a muro): inserire lo stato attuale in %', es: 'Definir la estimación SIN movimiento (p.ej. tras el manejo manual en el interruptor de pared): introducir el estado actual en %', pl: 'Ustaw szacunek BEZ ruchu (np. po ręcznej obsłudze przełącznika ściennego): wpisz aktualny stan rzeczywisty w %', uk: 'Встановити оцінку БЕЗ руху (напр. після ручного керування настінним вимикачем): ввести поточний фактичний стан у %', 'zh-cn': '设置估计值但不移动(例如手动操作墙壁开关后):输入当前实际状态百分比' },
619
622
  type: 'number', role: 'level', min: 0, max: 100, read: true, write: true
620
623
  });
621
624
  await this.extendObjectAsync(`${ch}.tiltOpen`, {
@@ -640,14 +643,14 @@ class Zeptrion extends utils.Adapter {
640
643
  });
641
644
  }
642
645
 
643
- await this.ensureState(`${ch}.name`, { name: 'Kanalname (chdes)', type: 'string', role: 'text', read: true, write: true });
644
- await this.ensureState(`${ch}.group`, { name: 'Gruppe (chdes)', type: 'string', role: 'text', read: true, write: true });
645
- await this.ensureState(`${ch}.icon`, { name: 'Icon (chdes)', type: 'string', role: 'text', read: true, write: true });
646
- await this.ensureState(`${ch}.type`, { name: 'Typ-Code (chdes)', type: 'string', role: 'text', read: true, write: true });
647
- await this.ensureState(`${ch}.cat`, { name: 'Kategorie-Code (chdes)', type: 'string', role: 'text', read: true, write: true });
646
+ await this.ensureState(`${ch}.name`, { name: { en: 'Channel name (chdes)', de: 'Kanalname (chdes)', ru: 'Имя канала (chdes)', pt: 'Nome do canal (chdes)', nl: 'Kanaalnaam (chdes)', fr: 'Nom du canal (chdes)', it: 'Nome canale (chdes)', es: 'Nombre del canal (chdes)', pl: 'Nazwa kanału (chdes)', uk: 'Ім\'я каналу (chdes)', 'zh-cn': '通道名称 (chdes)' }, type: 'string', role: 'text', read: true, write: true });
647
+ await this.ensureState(`${ch}.group`, { name: { en: 'Group (chdes)', de: 'Gruppe (chdes)', ru: 'Группа (chdes)', pt: 'Grupo (chdes)', nl: 'Groep (chdes)', fr: 'Groupe (chdes)', it: 'Gruppo (chdes)', es: 'Grupo (chdes)', pl: 'Grupa (chdes)', uk: 'Група (chdes)', 'zh-cn': '分组 (chdes)' }, type: 'string', role: 'text', read: true, write: true });
648
+ await this.ensureState(`${ch}.icon`, { name: { en: 'Icon (chdes)', de: 'Icon (chdes)', ru: 'Иконка (chdes)', pt: 'Ícone (chdes)', nl: 'Icoon (chdes)', fr: 'Icône (chdes)', it: 'Icona (chdes)', es: 'Icono (chdes)', pl: 'Ikona (chdes)', uk: 'Іконка (chdes)', 'zh-cn': '图标 (chdes)' }, type: 'string', role: 'text', read: true, write: true });
649
+ await this.ensureState(`${ch}.type`, { name: { en: 'Type code (chdes)', de: 'Typ-Code (chdes)', ru: 'Код типа (chdes)', pt: 'Código de tipo (chdes)', nl: 'Typecode (chdes)', fr: 'Code de type (chdes)', it: 'Codice tipo (chdes)', es: 'Código de tipo (chdes)', pl: 'Kod typu (chdes)', uk: 'Код типу (chdes)', 'zh-cn': '类型代码 (chdes)' }, type: 'string', role: 'text', read: true, write: true });
650
+ await this.ensureState(`${ch}.cat`, { name: { en: 'Category code (chdes)', de: 'Kategorie-Code (chdes)', ru: 'Код категории (chdes)', pt: 'Código de categoria (chdes)', nl: 'Categoriecode (chdes)', fr: 'Code de catégorie (chdes)', it: 'Codice categoria (chdes)', es: 'Código de categoría (chdes)', pl: 'Kod kategorii (chdes)', uk: 'Код категорії (chdes)', 'zh-cn': '类别代码 (chdes)' }, type: 'string', role: 'text', read: true, write: true });
648
651
 
649
652
  await this.ensureState(`${ch}.command`, {
650
- name: 'Freier Befehl (z.B. dim_2000, move_close_5000, recall_s1 …)',
653
+ name: { en: 'Free-text command (e.g. dim_2000, move_close_5000, recall_s1 ...)', de: 'Freier Befehl (z.B. dim_2000, move_close_5000, recall_s1 …)', ru: 'Произвольная команда (напр. dim_2000, move_close_5000, recall_s1 …)', pt: 'Comando livre (p.ex. dim_2000, move_close_5000, recall_s1 …)', nl: 'Vrije opdracht (bijv. dim_2000, move_close_5000, recall_s1 …)', fr: 'Commande libre (p.ex. dim_2000, move_close_5000, recall_s1 …)', it: 'Comando libero (es. dim_2000, move_close_5000, recall_s1 …)', es: 'Comando libre (p.ej. dim_2000, move_close_5000, recall_s1 …)', pl: 'Dowolne polecenie (np. dim_2000, move_close_5000, recall_s1 …)', uk: 'Довільна команда (напр. dim_2000, move_close_5000, recall_s1 …)', 'zh-cn': '自由命令(例如 dim_2000, move_close_5000, recall_s1 …)' },
651
654
  type: 'string', role: 'text', read: false, write: true, def: ''
652
655
  });
653
656
 
@@ -657,9 +660,15 @@ class Zeptrion extends utils.Adapter {
657
660
  });
658
661
  }
659
662
  for (let s = 1; s <= 4; s++) {
660
- await this.ensureState(`${ch}.recall_s${s}`, { name: `Szene ${s} abrufen`, type: 'boolean', role: 'button', read: false, write: true, def: false });
661
- await this.ensureState(`${ch}.store_s${s}`, { name: `Szene ${s} speichern`, type: 'boolean', role: 'button', read: false, write: true, def: false });
662
- await this.ensureState(`${ch}.delete_s${s}`, { name: `Szene ${s} löschen`, type: 'boolean', role: 'button', read: false, write: true, def: false });
663
+ const sceneName = (verb) => ({
664
+ en: `Scene ${s} ${verb.en}`, de: `Szene ${s} ${verb.de}`, ru: `Сцена ${s}: ${verb.ru}`,
665
+ pt: `Cena ${s} ${verb.pt}`, nl: `Scène ${s} ${verb.nl}`, fr: `Scène ${s} ${verb.fr}`,
666
+ it: `Scena ${s} ${verb.it}`, es: `Escena ${s} ${verb.es}`, pl: `Scena ${s}: ${verb.pl}`,
667
+ uk: `Сцена ${s}: ${verb.uk}`, 'zh-cn': `场景 ${s} ${verb['zh-cn']}`
668
+ });
669
+ await this.ensureState(`${ch}.recall_s${s}`, { name: sceneName({ en: 'recall', de: 'abrufen', ru: 'вызвать', pt: 'chamar', nl: 'oproepen', fr: 'rappeler', it: 'richiama', es: 'recuperar', pl: 'przywołaj', uk: 'викликати', 'zh-cn': '调用' }), type: 'boolean', role: 'button', read: false, write: true, def: false });
670
+ await this.ensureState(`${ch}.store_s${s}`, { name: sceneName({ en: 'store', de: 'speichern', ru: 'сохранить', pt: 'guardar', nl: 'opslaan', fr: 'enregistrer', it: 'salva', es: 'guardar', pl: 'zapisz', uk: 'зберегти', 'zh-cn': '保存' }), type: 'boolean', role: 'button', read: false, write: true, def: false });
671
+ await this.ensureState(`${ch}.delete_s${s}`, { name: sceneName({ en: 'delete', de: 'löschen', ru: 'удалить', pt: 'eliminar', nl: 'verwijderen', fr: 'supprimer', it: 'elimina', es: 'eliminar', pl: 'usuń', uk: 'видалити', 'zh-cn': '删除' }), type: 'boolean', role: 'button', read: false, write: true, def: false });
663
672
  }
664
673
  }
665
674
  }
@@ -773,14 +782,14 @@ class Zeptrion extends utils.Adapter {
773
782
  if (chNums.length === 1) {
774
783
  const chNum = chNums[0];
775
784
  await this.zrapPost(id, `/zrap/chctrl/ch${chNum}`, { cmd: cmds[chNum] });
776
- this.log.info(`[${id}] Kanalbefehl gesendet: ch${chNum} -> ${cmds[chNum]}`);
785
+ this.log.info(`[${id}] Channel command sent: ch${chNum} -> ${cmds[chNum]}`);
777
786
  } else {
778
787
  const body = {};
779
788
  for (const chNum of chNums) body[`cmd${chNum}`] = cmds[chNum];
780
789
  await this.zrapPost(id, '/zrap/chctrl', body);
781
790
  const summary = chNums.map(n => `ch${n}->${cmds[n]}`).join(', ');
782
- this.log.info(`[${id}] Multicast-Befehl gesendet: ${summary}`);
783
- this.log.debug(`[${id}] Multicast-Befehl gebündelt: ${JSON.stringify(body)}`);
791
+ this.log.info(`[${id}] Multicast command sent: ${summary}`);
792
+ this.log.debug(`[${id}] Multicast command bundled: ${JSON.stringify(body)}`);
784
793
  }
785
794
  for (const chNum of chNums) {
786
795
  this.markChannelBusy(dev, chNum, cmds[chNum]);
@@ -789,7 +798,7 @@ class Zeptrion extends utils.Adapter {
789
798
  callbacks.forEach(cb => cb.resolve());
790
799
  } catch (err) {
791
800
  const summary = chNums.map(n => `ch${n}->${cmds[n]}`).join(', ');
792
- this.log.warn(`[${id}] Kanalbefehl fehlgeschlagen (${summary}): ${err.message || err}`);
801
+ this.log.warn(`[${id}] Channel command failed (${summary}): ${err.message || err}`);
793
802
  callbacks.forEach(cb => cb.reject(err));
794
803
  }
795
804
  }
@@ -924,7 +933,7 @@ class Zeptrion extends utils.Adapter {
924
933
  if (dev.posEstimate[chNum] === undefined) {
925
934
  const refCmd = target < 50 ? 'close' : 'open';
926
935
  const refPos = target < 50 ? 0 : 100;
927
- this.log.info(`[${id}] ch${chNum}: Position unbekannt - Referenzfahrt (${refCmd}, ${Math.round(travel / 1000)}s) vor Anfahrt auf ${target}%`);
936
+ this.log.info(`[${id}] ch${chNum}: position unknown - reference run (${refCmd}, ${Math.round(travel / 1000)}s) before moving to ${target}%`);
928
937
  await this.sendChannelCommand(id, chNum, refCmd);
929
938
  await this.delay(travel + 1000);
930
939
  if (aborted()) return;
@@ -943,14 +952,14 @@ class Zeptrion extends utils.Adapter {
943
952
  const dirCmd = deltaPct > 0 ? 'move_open' : 'move_close';
944
953
  let remainingMs = Math.round(Math.abs(deltaPct) / 100 * travel);
945
954
  if (remainingMs < MIN_TIMED_MS) {
946
- this.log.debug(`[${id}] ch${chNum}: Differenz ${deltaPct}% ergäbe ${remainingMs}ms < API-Minimum ${MIN_TIMED_MS}ms - keine Fahrt`);
955
+ this.log.debug(`[${id}] ch${chNum}: difference ${deltaPct}% would give ${remainingMs}ms < API minimum ${MIN_TIMED_MS}ms - no movement`);
947
956
  await this.setStateAsync(`${id}.channels.ch${chNum}.setPosition`, { val: current, ack: true });
948
957
  return;
949
958
  }
950
959
 
951
960
  while (remainingMs > 0) {
952
961
  if (aborted()) {
953
- this.log.debug(`[${id}] ch${chNum}: setPosition-Sequenz abgebrochen`);
962
+ this.log.debug(`[${id}] ch${chNum}: setPosition sequence aborted`);
954
963
  return;
955
964
  }
956
965
  const pulse = Math.max(MIN_TIMED_MS, Math.min(remainingMs, MAX_TIMED_MS));
@@ -962,7 +971,7 @@ class Zeptrion extends utils.Adapter {
962
971
  }
963
972
  if (aborted()) return;
964
973
  await this.setStateAsync(`${id}.channels.ch${chNum}.setPosition`, { val: target, ack: true });
965
- this.log.debug(`[${id}] ch${chNum}: Zielposition ${target}% angefahren (Schätzung)`);
974
+ this.log.debug(`[${id}] ch${chNum}: target position ${target}% reached (estimate)`);
966
975
  }
967
976
 
968
977
  // ------------------------------------------------------- statische Infos
@@ -974,7 +983,7 @@ class Zeptrion extends utils.Adapter {
974
983
 
975
984
  // Verifikation: antwortet hier wirklich ein zeptrion-Gerät?
976
985
  if (idData.sys !== undefined && String(idData.sys).toUpperCase() !== 'ZEPTRION') {
977
- this.log.warn(`[${id}] Host ${dev.cfg.host} antwortet, meldet aber sys="${idData.sys}" statt "ZEPTRION" - vermutlich falsche IP oder kein zeptrion-Gerät!`);
986
+ this.log.warn(`[${id}] Host ${dev.cfg.host} responds, but reports sys="${idData.sys}" instead of "ZEPTRION" - likely wrong IP or not a zeptrion device!`);
978
987
  }
979
988
  // Plausibilisierung: Kanalzahl aus dem Gerätetyp ableiten (3340-4-x = 4, 3340-2-x = 2)
980
989
  const typeStr = String(idData.type ?? '');
@@ -982,7 +991,7 @@ class Zeptrion extends utils.Adapter {
982
991
  if (m) {
983
992
  const hwChannels = parseInt(m[1], 10);
984
993
  if (hwChannels !== dev.cfg.channels) {
985
- this.log.warn(`[${id}] Gerätetyp ${typeStr} hat ${hwChannels} Kanäle, konfiguriert sind ${dev.cfg.channels} - bitte in der Instanz-Konfiguration korrigieren.`);
994
+ this.log.warn(`[${id}] Device type ${typeStr} has ${hwChannels} channels, ${dev.cfg.channels} configured - please correct in the instance configuration.`);
986
995
  }
987
996
  }
988
997
 
@@ -1015,7 +1024,12 @@ class Zeptrion extends utils.Adapter {
1015
1024
  const chName = String(chData.name ?? '').trim();
1016
1025
  if (chName) {
1017
1026
  await this.extendObjectAsync(`${id}.channels.ch${n}`, {
1018
- common: { name: `Kanal ${n} - ${chName}` }
1027
+ common: { name: {
1028
+ en: `Channel ${n} - ${chName}`, de: `Kanal ${n} - ${chName}`, ru: `Канал ${n} - ${chName}`,
1029
+ pt: `Canal ${n} - ${chName}`, nl: `Kanaal ${n} - ${chName}`, fr: `Canal ${n} - ${chName}`,
1030
+ it: `Canale ${n} - ${chName}`, es: `Canal ${n} - ${chName}`, pl: `Kanał ${n} - ${chName}`,
1031
+ uk: `Канал ${n} - ${chName}`, 'zh-cn': `通道 ${n} - ${chName}`
1032
+ } }
1019
1033
  });
1020
1034
  }
1021
1035
  }
@@ -1049,7 +1063,7 @@ class Zeptrion extends utils.Adapter {
1049
1063
  const data = await this.zrapGet(id, path);
1050
1064
  await apply(data);
1051
1065
  } catch (err) {
1052
- this.log.debug(`[${id}] optionaler Service ${path} nicht verfügbar/fehlgeschlagen: ${err.message || err}`);
1066
+ this.log.debug(`[${id}] optional service ${path} unavailable/failed: ${err.message || err}`);
1053
1067
  }
1054
1068
  }
1055
1069
 
@@ -1069,7 +1083,7 @@ class Zeptrion extends utils.Adapter {
1069
1083
  await this.setStateAsync(`${id}.date.rfc1123`, { val: rfc1123, ack: true });
1070
1084
  await this.setStateAsync(`${id}.date.tz`, { val: tz, ack: true });
1071
1085
  await this.setStateAsync(`${id}.date.dst`, { val: '0000', ack: true });
1072
- this.log.info(`[${id}] Geräte-Zeit synchronisiert: ${rfc1123} (tz=${tz})`);
1086
+ this.log.info(`[${id}] Device time synchronized: ${rfc1123} (tz=${tz})`);
1073
1087
  }
1074
1088
 
1075
1089
  // ------------------------------------------------------------- Polling
@@ -1149,7 +1163,7 @@ class Zeptrion extends utils.Adapter {
1149
1163
  await this.setStateAsync(`${id}.smartfront.ledState`, { val: JSON.stringify(led), ack: true });
1150
1164
  }
1151
1165
  } catch (err) {
1152
- this.log.debug(`[${id}] Smartfront (zapi) nicht verfügbar: ${err.message || err}`);
1166
+ this.log.debug(`[${id}] Smartfront (zapi) unavailable: ${err.message || err}`);
1153
1167
  }
1154
1168
  }
1155
1169
  }
@@ -1206,11 +1220,11 @@ class Zeptrion extends utils.Adapter {
1206
1220
  dev.fails = 0;
1207
1221
  this.setStateChangedAsync(`${id}.info.connection`, { val: true, ack: true });
1208
1222
  this.setStateChangedAsync(`${id}.info.lastError`, { val: '', ack: true });
1209
- if (!was) this.log.info(`Gerät ${id} (${dev.cfg.host}) ist erreichbar.`);
1223
+ if (!was) this.log.info(`Device ${id} (${dev.cfg.host}) is reachable.`);
1210
1224
  } else {
1211
1225
  dev.fails++;
1212
1226
  this.setStateChangedAsync(`${id}.info.connection`, { val: false, ack: true });
1213
- if (was) this.log.warn(`Gerät ${id} (${dev.cfg.host}) nicht mehr erreichbar.`);
1227
+ if (was) this.log.warn(`Device ${id} (${dev.cfg.host}) no longer reachable.`);
1214
1228
  }
1215
1229
  this.updateGlobalConnection();
1216
1230
  }
@@ -1228,7 +1242,7 @@ class Zeptrion extends utils.Adapter {
1228
1242
  else if (code === 'EHOSTUNREACH') msg = 'Host nicht erreichbar (Netzwerk/Routing prüfen)';
1229
1243
  else if (code === 'ENOTFOUND') msg = 'Hostname/mDNS-Name nicht auflösbar';
1230
1244
  else if (code === 'ETIMEDOUT') msg = 'Zeitüberschreitung beim Verbindungsaufbau';
1231
- this.log.warn(`[${id}] Fehler bei ${context}: ${msg}`);
1245
+ this.log.warn(`[${id}] Error during ${context}: ${msg}`);
1232
1246
  this.setStateAsync(`${id}.info.lastError`, { val: msg, ack: true }).catch(() => {});
1233
1247
  this.markConnected(id, false);
1234
1248
  }
@@ -1247,7 +1261,7 @@ class Zeptrion extends utils.Adapter {
1247
1261
  if (this.devices[devId]) {
1248
1262
  this.handleDeviceError(devId, err, `onStateChange(${rel})`);
1249
1263
  } else {
1250
- this.log.warn(`Fehler bei onStateChange(${rel}): ${err.message || err}`);
1264
+ this.log.warn(`Error in onStateChange(${rel}): ${err.message || err}`);
1251
1265
  }
1252
1266
  if (typeof state.val === 'boolean') {
1253
1267
  await this.setStateAsync(idFull, { val: false, ack: true }).catch(() => {});
@@ -1289,7 +1303,7 @@ class Zeptrion extends utils.Adapter {
1289
1303
  if (parts[1] === 'system') {
1290
1304
  if (parts[2] === 'unlock' && state.val) {
1291
1305
  dev.unlockUntil = Date.now() + 30000;
1292
- this.log.warn(`[${id}] Werksreset für 30 Sekunden entriegelt.`);
1306
+ this.log.warn(`[${id}] Factory reset unlocked for 30 seconds.`);
1293
1307
  await this.setStateAsync(idFull, { val: false, ack: true });
1294
1308
  return;
1295
1309
  }
@@ -1301,15 +1315,15 @@ class Zeptrion extends utils.Adapter {
1301
1315
  // physisch neu eingerichtet werden. Ein einzelner (versehent-
1302
1316
  // licher) setState aus Script/VIS darf das nicht auslösen können.
1303
1317
  if (!dev.unlockUntil || Date.now() > dev.unlockUntil) {
1304
- this.log.error(`[${id}] Werksreset ABGELEHNT: zuerst ${id}.system.unlock setzen (30s-Fenster). Das Gerät würde sonst inkl. WLAN-Konfiguration gelöscht und vom Netz fallen.`);
1318
+ this.log.error(`[${id}] Factory reset REJECTED: set ${id}.system.unlock first (30s window). Otherwise the device incl. WLAN configuration would be wiped and drop off the network.`);
1305
1319
  await this.setStateAsync(idFull, { val: false, ack: true });
1306
1320
  return;
1307
1321
  }
1308
1322
  dev.unlockUntil = 0;
1309
- this.log.warn(`[${id}] WERKSRESET wird ausgeführt - Gerät verliert alle Einstellungen inkl. WLAN!`);
1323
+ this.log.warn(`[${id}] FACTORY RESET is being executed - device will lose all settings incl. WLAN!`);
1310
1324
  }
1311
1325
  await this.zrapPost(id, '/zrap/sys', { cmd });
1312
- this.log.info(`[${id}] Systembefehl gesendet: ${cmd}`);
1326
+ this.log.info(`[${id}] System command sent: ${cmd}`);
1313
1327
  await this.setStateAsync(idFull, { val: false, ack: true });
1314
1328
  }
1315
1329
  return;
@@ -1376,7 +1390,7 @@ class Zeptrion extends utils.Adapter {
1376
1390
 
1377
1391
  if (action === 'posEstimate') {
1378
1392
  // read-only seit 0.5.0 - Hinweis für alte Scripts
1379
- this.log.warn(`[${id}] posEstimate ist jetzt read-only. Zum Kalibrieren "calibrate", zum Anfahren "setPosition" verwenden.`);
1393
+ this.log.warn(`[${id}] posEstimate is now read-only. Use "calibrate" to calibrate, "setPosition" to move.`);
1380
1394
  return;
1381
1395
  }
1382
1396
 
@@ -1405,7 +1419,7 @@ class Zeptrion extends utils.Adapter {
1405
1419
  if (action === 'tiltOpen' || action === 'tiltClose') {
1406
1420
  if (state.val !== true) return;
1407
1421
  if (!dev.cfg.tiltTimeMs) {
1408
- this.log.warn(`[${id}] Kipp-Impuls nicht konfiguriert ("Kipp-Impuls (ms)" in der Geräte-Tabelle setzen, typisch 300-800ms für Rafflamellen).`);
1422
+ this.log.warn(`[${id}] Tilt pulse not configured (set "Tilt pulse (ms)" in the device table, typically 300-800ms for slats).`);
1409
1423
  await this.setStateAsync(idFull, { val: false, ack: true });
1410
1424
  return;
1411
1425
  }
@@ -1497,7 +1511,7 @@ class Zeptrion extends utils.Adapter {
1497
1511
  channels
1498
1512
  });
1499
1513
  } catch (err) {
1500
- this.log.debug(`Discovery: unerwartetes/fremdes mDNS-Paket ignoriert (${err.message || err})`);
1514
+ this.log.debug(`Discovery: unexpected/foreign mDNS packet ignored (${err.message || err})`);
1501
1515
  }
1502
1516
  };
1503
1517
 
@@ -1509,16 +1523,16 @@ class Zeptrion extends utils.Adapter {
1509
1523
  try {
1510
1524
  if (service && service.name && /^zapp-\d{8}$/i.test(service.name)) handle(service);
1511
1525
  } catch (err) {
1512
- this.log.debug(`Discovery: Fallback-Filter (_http._tcp) Fehler ignoriert (${err.message || err})`);
1526
+ this.log.debug(`Discovery: fallback filter (_http._tcp) error ignored (${err.message || err})`);
1513
1527
  }
1514
1528
  });
1515
1529
  // Auch auf explizite Fehler-Events der Browser reagieren, statt sie
1516
1530
  // als unhandled 'error' durchfallen zu lassen.
1517
1531
  if (browserNew && typeof browserNew.on === 'function') {
1518
- browserNew.on('error', err => this.log.debug(`Discovery (_zapp._tcp) Fehler: ${err.message || err}`));
1532
+ browserNew.on('error', err => this.log.debug(`Discovery (_zapp._tcp) error: ${err.message || err}`));
1519
1533
  }
1520
1534
  if (browserOld && typeof browserOld.on === 'function') {
1521
- browserOld.on('error', err => this.log.debug(`Discovery (_http._tcp) Fehler: ${err.message || err}`));
1535
+ browserOld.on('error', err => this.log.debug(`Discovery (_http._tcp) error: ${err.message || err}`));
1522
1536
  }
1523
1537
  } catch (err) {
1524
1538
  try { bonjour.destroy(); } catch (e) { /* ignore */ }
@@ -1641,11 +1655,13 @@ class Zeptrion extends utils.Adapter {
1641
1655
  await this.setForeignObjectAsync(`system.adapter.${this.namespace}`, instObj);
1642
1656
  }
1643
1657
  const result = `${added} von ${lines.length} Zeile(n) importiert.${added ? ' Adapter startet neu; Dialog schliessen und neu öffnen.' : ''}\n\n${report.join('\n')}`;
1644
- this.log.info(`CSV-Import: ${added}/${lines.length} übernommen`);
1658
+ this.log.info(`CSV import: ${added}/${lines.length} rows imported`);
1645
1659
  if (obj.callback) this.sendTo(obj.from, obj.command, { result }, obj.callback);
1646
1660
  } catch (err) {
1661
+ // UI-facing text (shown in the admin config dialog) stays German for the
1662
+ // German-speaking user base; the log entry itself must be English per checklist.
1647
1663
  const msg = `CSV-Import fehlgeschlagen: ${err.message || err}`;
1648
- this.log.warn(msg);
1664
+ this.log.warn(`CSV import failed: ${err.message || err}`);
1649
1665
  if (obj.callback) this.sendTo(obj.from, obj.command, { error: msg }, obj.callback);
1650
1666
  }
1651
1667
  return;
@@ -1690,25 +1706,27 @@ class Zeptrion extends utils.Adapter {
1690
1706
  }
1691
1707
  }
1692
1708
  const result = lines.join('\n');
1693
- this.log.info(`Gerätetest:\n${result}`);
1709
+ this.log.info(`Device test:\n${result}`);
1694
1710
  if (obj.callback) this.sendTo(obj.from, obj.command, { result }, obj.callback);
1695
1711
  return;
1696
1712
  }
1697
1713
 
1698
1714
  if (obj.command === 'discover') {
1699
1715
  try {
1700
- this.log.info('Starte mDNS-Discovery nach zeptrion-Geräten ');
1716
+ this.log.info('Starting mDNS discovery for zeptrion devices...');
1701
1717
  const results = await this.discoverDevices(4000);
1702
1718
  const added = await this.mergeDiscoveredDevices(results);
1719
+ // UI-facing text (shown in the admin config dialog) stays German for the
1720
+ // German-speaking user base; the log entry itself must be English per checklist.
1703
1721
  const msg = `Suche abgeschlossen: ${results.length} Gerät(e) im Netz gefunden, ${added} neu (deaktiviert) übernommen. ` +
1704
1722
  `Instanz-Konfiguration schliessen und neu öffnen, um sie in der Tabelle zu sehen und zu aktivieren.`;
1705
- this.log.info(msg);
1723
+ this.log.info(`Discovery finished: ${results.length} device(s) found on the network, ${added} newly added (disabled).`);
1706
1724
  if (obj.callback) {
1707
1725
  this.sendTo(obj.from, obj.command, { result: msg, devices: results }, obj.callback);
1708
1726
  }
1709
1727
  } catch (err) {
1710
1728
  const msg = err.message || String(err);
1711
- this.log.warn(`Discovery fehlgeschlagen: ${msg}`);
1729
+ this.log.warn(`Discovery failed: ${msg}`);
1712
1730
  if (obj.callback) {
1713
1731
  this.sendTo(obj.from, obj.command, { error: msg }, obj.callback);
1714
1732
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.zeptrion",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "ioBroker Adapter für Feller zeptrion / zApp WLAN-Aktoren (WLAN-Nebenstelle 4K, WLAN-Zwischenmodul 2K) über die zrap Webservice API - inkl. Sammelbefehlen für Hagelalarm und mDNS-Discovery",
5
5
  "author": "Stefan Bühler",
6
6
  "license": "MIT",
@@ -46,8 +46,10 @@
46
46
  "bonjour-service": "^1.4.3"
47
47
  },
48
48
  "devDependencies": {
49
- "@alcalzone/release-script": "^3.8.0",
50
- "@iobroker/adapter-dev": "^1.4.0",
49
+ "@alcalzone/release-script": "^5.2.1",
50
+ "@alcalzone/release-script-plugin-iobroker": "^5.2.0",
51
+ "@alcalzone/release-script-plugin-license": "^5.2.0",
52
+ "@iobroker/adapter-dev": "^1.5.0",
51
53
  "@iobroker/testing": "^5.3.0",
52
54
  "@tsconfig/node22": "^22.0.5",
53
55
  "@types/node": "^22.20.1",