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 +5 -22
- package/admin/i18n/{de/translations.json → de.json} +2 -2
- package/admin/i18n/{en/translations.json → en.json} +2 -2
- package/admin/i18n/{es/translations.json → es.json} +2 -2
- package/admin/i18n/{fr/translations.json → fr.json} +2 -2
- package/admin/i18n/{it/translations.json → it.json} +2 -2
- package/admin/i18n/{nl/translations.json → nl.json} +2 -2
- package/admin/i18n/{pl/translations.json → pl.json} +2 -2
- package/admin/i18n/{pt/translations.json → pt.json} +2 -2
- package/admin/i18n/{ru/translations.json → ru.json} +2 -2
- package/admin/i18n/{uk/translations.json → uk.json} +2 -2
- package/admin/i18n/{zh-cn/translations.json → zh-cn.json} +2 -2
- package/io-package.json +34 -16
- package/main.js +120 -102
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
[](https://www.paypal.com/ncp/payment/TT6MTBLXX9L9U)
|
|
10
10
|
[](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
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
2
|
+
"Comma-separated numbers only, max 4 values (e.g. '22,28')": "Только числа через запятую, максимум 4 значения (например, '22,28')",
|
|
3
3
|
"Invalid host (no protocol, no spaces)": "Недопустимый хост (без протокола, без пробелов)",
|
|
4
|
-
"
|
|
4
|
+
"Only a-z, 0-9, _ and - (or leave empty)": "Только a-z, 0-9, _ и - (или оставьте пустым)"
|
|
5
5
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
2
|
+
"Comma-separated numbers only, max 4 values (e.g. '22,28')": "Лише числа через кому, максимум 4 значення (наприклад, '22,28')",
|
|
3
3
|
"Invalid host (no protocol, no spaces)": "Недійсний хост (без протоколу, без пробілів)",
|
|
4
|
-
"
|
|
4
|
+
"Only a-z, 0-9, _ and - (or leave empty)": "Лише a-z, 0-9, _ і - (або залиште порожнім)"
|
|
5
5
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
2
|
+
"Comma-separated numbers only, max 4 values (e.g. '22,28')": "仅限逗号分隔的数字,最多 4 个值(例如 '22,28')",
|
|
3
3
|
"Invalid host (no protocol, no spaces)": "主机无效(不含协议,不含空格)",
|
|
4
|
-
"
|
|
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.
|
|
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(`
|
|
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(`
|
|
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('
|
|
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(`
|
|
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('
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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-
|
|
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:
|
|
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
|
-
|
|
661
|
-
|
|
662
|
-
|
|
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}]
|
|
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
|
|
783
|
-
this.log.debug(`[${id}] Multicast
|
|
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}]
|
|
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}:
|
|
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}:
|
|
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
|
|
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}:
|
|
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}
|
|
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}]
|
|
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:
|
|
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}]
|
|
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}]
|
|
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)
|
|
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(`
|
|
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(`
|
|
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}]
|
|
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(`
|
|
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}]
|
|
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}]
|
|
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}]
|
|
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}]
|
|
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
|
|
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}]
|
|
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:
|
|
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:
|
|
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)
|
|
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)
|
|
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
|
|
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(
|
|
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(`
|
|
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('
|
|
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(
|
|
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
|
|
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.
|
|
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": "^
|
|
50
|
-
"@
|
|
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",
|