iobroker.zeptrion 0.8.2
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/LICENSE +21 -0
- package/README.md +302 -0
- package/admin/jsonConfig.json +245 -0
- package/admin/zeptrion.png +0 -0
- package/io-package.json +196 -0
- package/main.js +1638 -0
- package/package.json +61 -0
- package/zeptrion.png +0 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Stefan Bühler
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
# ioBroker.zeptrion
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/iobroker.zeptrion)
|
|
6
|
+
[](https://www.npmjs.com/package/iobroker.zeptrion)
|
|
7
|
+
[](https://github.com/bueste/ioBroker.zeptrion/actions)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
|
|
10
|
+
Adapter für Feller **zeptrion / zApp** WLAN-Aktoren (WLAN-Nebenstelle 4K = zApp-Gateway,
|
|
11
|
+
WLAN-Zwischenmodul 2K = zApp-Booster) für Licht- und Storensteuerung, basierend auf der
|
|
12
|
+
zrap Webservice API (Feller-Dokument 10.ZEPAPI-E.1612 / Version 1.0, Firmware ab 01.08.18).
|
|
13
|
+
|
|
14
|
+
> Vor der Store-Veröffentlichung muss noch die Autor-E-Mail-Adresse
|
|
15
|
+
> in `package.json` eingetragen werden (Platzhalter CHANGE_ME@example.com).
|
|
16
|
+
|
|
17
|
+
## Funktionsumfang
|
|
18
|
+
|
|
19
|
+
- **Kanalsteuerung** (`zrap/chctrl`): on/off/stop/toggle, open/close, move_open/move_close,
|
|
20
|
+
dim_up/dim_down inkl. zeitgesteuerter Varianten (`_t` in ms), sowie Szenen
|
|
21
|
+
recall_s1-4 / store_s1-4 / delete_s1-4 - als einzelne Buttons UND als freies
|
|
22
|
+
`command`-Textfeld.
|
|
23
|
+
- **Kanalzustand** (`zrap/chscan` als periodischer Resync + `zrap/chnotify` als
|
|
24
|
+
Long-Poll-Push für nahezu Echtzeit-Updates) und **Kanalbeschreibung** (`zrap/chdes`,
|
|
25
|
+
read/write: Name, Gruppe, Icon, Typ, Kategorie).
|
|
26
|
+
- **Geräteinformationen** (`zrap/id`): Hardware-/Software-/Bootloader-Version, Seriennummer,
|
|
27
|
+
Systemname, Gerätetyp.
|
|
28
|
+
- **Signalstärke** (`zrap/rssi`, gepollt).
|
|
29
|
+
- **Netzwerkstatus** (`zrap/net`, read-only): SSID, IP, MAC, Modus, Verschlüsselung,
|
|
30
|
+
Maske, Gateway.
|
|
31
|
+
- **Systembefehle** (`zrap/sys`): Neustart, Werksreset, Zurücksetzen in Access-Point-Modus.
|
|
32
|
+
- **Standort** (`zrap/loc`), **NTP-Konfiguration** (`zrap/ntp`) und **Datum/Zeit**
|
|
33
|
+
(`zrap/date`) inkl. Ein-Klick-Synchronisation der Geräte-Uhrzeit mit dem ioBroker-Host.
|
|
34
|
+
- **mDNS-Discovery** (Kapitel 4 der API-Doku): durchsucht das lokale Netz nach
|
|
35
|
+
zeptrion-Geräten und übernimmt Funde deaktiviert in die Konfigurationstabelle
|
|
36
|
+
(Discovery kombiniert mit manueller Kontrolle/Aktivierung).
|
|
37
|
+
- **Sammelbefehle für Hagelalarm**: `control.closeAllShutters` / `openAllShutters` /
|
|
38
|
+
`stopAllShutters` steuern alle konfigurierten Kanäle über alle aktiven Geräte gleichzeitig
|
|
39
|
+
- dank Multicast-Bündelung (siehe unten) als je EIN Request pro Gerät, nicht pro Kanal.
|
|
40
|
+
- **Multicast-Befehlsbündelung**: Kanalbefehle desselben Geräts, die innerhalb von 50ms
|
|
41
|
+
eintreffen, werden automatisch zu einem einzigen `zrap/chctrl`-Multicast-POST gebündelt
|
|
42
|
+
(Kapitel 3.6.5 der API-Doku) statt mehrerer sequentieller Einzelrequests.
|
|
43
|
+
- **Storen-Positionsschätzung** (optional, `posEstimate`): da die Hardware laut Doku für
|
|
44
|
+
Storenkanäle praktisch immer `-1` (unbekannt) liefert, kann pro Gerät eine
|
|
45
|
+
Motor-Laufzeit hinterlegt werden; der Adapter schätzt daraus die Position anhand
|
|
46
|
+
Bewegungsrichtung und verstrichener Zeit (Best Effort, kein Hardware-Feedback,
|
|
47
|
+
manuell kalibrierbar).
|
|
48
|
+
- **Smartfront-Unterstützung** (optional, `zapi/smartfront/*`): Temperatur/Helligkeit/
|
|
49
|
+
Luftfeuchtigkeit auslesen, LED-Hintergrundfarbe setzen (nur für Geräte mit
|
|
50
|
+
angeschlossenem Feller-Smartfront-Taster, Checkbox in der Konfiguration).
|
|
51
|
+
- Robustes Error-Handling: unterscheidet ECONNREFUSED/Timeout/DNS-Fehler, Backoff bei
|
|
52
|
+
wiederholten Fehlern, pro Gerät und global sichtbarer Verbindungsstatus. mDNS-Discovery
|
|
53
|
+
ist zusätzlich gegen Exceptions durch fremde/kaputte Netzwerkpakete abgesichert.
|
|
54
|
+
|
|
55
|
+
Nicht implementiert (siehe "Bekannte Einschränkungen"): Schreibzugriff auf `zrap/net`
|
|
56
|
+
(WLAN-Zugangsdaten ändern), `zrap/scheduler`, Smartbutton-Webhook-Programmierung
|
|
57
|
+
(`zapi/smartbt/*`).
|
|
58
|
+
|
|
59
|
+
## Installation
|
|
60
|
+
|
|
61
|
+
### a) Lokal/manuell (vor Store-Veröffentlichung)
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
cd /opt/iobroker/node_modules
|
|
65
|
+
mkdir iobroker.zeptrion
|
|
66
|
+
# Dateien dieses Pakets hierhin kopieren
|
|
67
|
+
cd iobroker.zeptrion
|
|
68
|
+
npm install --production
|
|
69
|
+
|
|
70
|
+
cd /opt/iobroker
|
|
71
|
+
iobroker upload zeptrion
|
|
72
|
+
iobroker add zeptrion
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### b) Über den ioBroker Adapter-Store (nach erfolgter Veröffentlichung)
|
|
76
|
+
|
|
77
|
+
Admin-Oberfläche -> Adapter -> "zeptrion" suchen -> Installieren.
|
|
78
|
+
|
|
79
|
+
## Konfiguration
|
|
80
|
+
|
|
81
|
+
- **HTTP Timeout**: Timeout pro Request an ein Gerät (Default 4000 ms).
|
|
82
|
+
- **Discovery-Button**: durchsucht das lokale Netz per mDNS (Service-Type `_zapp._tcp`,
|
|
83
|
+
Fallback `_http._tcp` für Firmware < 01.08.xx anhand des Hostname-Musters
|
|
84
|
+
`zapp-YYWWNNNN`). Neu gefundene Geräte werden **deaktiviert** in die Tabelle
|
|
85
|
+
übernommen - Zeile danach prüfen, ID/Name vergeben, Kanalzahl kontrollieren
|
|
86
|
+
(3340-4-x = 4 Kanäle, 3340-2-x = 2 Kanäle) und aktivieren. mDNS funktioniert nur
|
|
87
|
+
innerhalb des gleichen Netzsegments/VLANs.
|
|
88
|
+
- **Geräte-Tabelle** (auch komplett manuell befüllbar, ohne Discovery):
|
|
89
|
+
- `Aktiv`, `ID` (a-z 0-9 _ -), `Bezeichnung`, `IP-Adresse/Hostname`,
|
|
90
|
+
`Kanäle` (1-4), `Art` (Storen/Licht/unbekannt - steuert die ioBroker-Objektrollen,
|
|
91
|
+
siehe unten), `Laufzeit Storenmotor` (Sekunden, 0=deaktiviert - schaltet
|
|
92
|
+
`posEstimate` frei, siehe unten, gilt als Standard für alle Kanäle),
|
|
93
|
+
`Laufzeit/Kanal` (optional, kommagetrennt, z.B. `22,28` - überschreibt die
|
|
94
|
+
Standard-Laufzeit einzeln je Kanal; nützlich bei 2K-Geräten, deren beide
|
|
95
|
+
Kanäle unterschiedliche Motor-Laufzeiten haben; leere Einträge fallen auf
|
|
96
|
+
die Standard-Laufzeit zurück), `Smartfront` (Checkbox, nur bei angeschlossenem
|
|
97
|
+
Feller-Smartfront-Taster aktivieren), `Poll (s)` (Default 30, für RSSI +
|
|
98
|
+
periodischen chscan-Resync; die eigentlichen Kanalupdates laufen über den
|
|
99
|
+
chnotify-Long-Poll unabhängig davon).
|
|
100
|
+
|
|
101
|
+
## Objektbaum pro Gerät (`zeptrion.0.<id>`)
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
<id>.info.connection / lastError / hw / sw / boot / sn / sys / type / oen / rssi / refresh
|
|
105
|
+
<id>.network.ssid / ip / mac / mode / enc / mask / gw / bssid (read-only)
|
|
106
|
+
<id>.system.reboot / unlock / factoryDefault / networkDefault (Buttons; factoryDefault erfordert unlock binnen 30s)
|
|
107
|
+
<id>.location.name (read/write)
|
|
108
|
+
<id>.ntp.url / per (read/write)
|
|
109
|
+
<id>.date.rfc1123 / tz / dst / syncNow (read/write + Button)
|
|
110
|
+
|
|
111
|
+
<id>.channels.chN.val Kanalzustand 0-100 / -1 (roher Hardwarewert)
|
|
112
|
+
<id>.channels.chN.posEstimate nur bei Art=Storen: Software-Positionsschätzung
|
|
113
|
+
0=zu/100=offen, auch manuell schreibbar (Kalibrierung)
|
|
114
|
+
<id>.channels.chN.name / group / icon / type / cat Kanalbeschreibung (read/write)
|
|
115
|
+
<id>.channels.chN.command freies Kommando (String)
|
|
116
|
+
<id>.channels.chN.stop / on / off / toggle / open / close /
|
|
117
|
+
move_open / move_close / dim_up / dim_down (Buttons)
|
|
118
|
+
<id>.channels.chN.recall_s1..4 / store_s1..4 / delete_s1..4 (Buttons)
|
|
119
|
+
|
|
120
|
+
<id>.smartfront.temp / lux / hum nur wenn "Smartfront" aktiviert (read)
|
|
121
|
+
<id>.smartfront.ledState aktueller LED-Status als JSON (read)
|
|
122
|
+
<id>.smartfront.ledSet LED(s) setzen, JSON-Array (write)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Global:
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
info.connection mind. ein Gerät erreichbar
|
|
129
|
+
control.closeAllShutters Button: ALLE konfigurierten Kanäle -> "close"
|
|
130
|
+
control.openAllShutters Button: ALLE konfigurierten Kanäle -> "open"
|
|
131
|
+
control.stopAllShutters Button: ALLE konfigurierten Kanäle -> "stop"
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Objekt-Rollen und "Art" (kind)
|
|
135
|
+
|
|
136
|
+
Die zrap-API selbst unterscheidet nicht zwischen Licht- und Storenkanal - das steckt
|
|
137
|
+
allein in der Verkabelung/im Aktor. Damit Visualisierungen (VIS, evtl. spätere
|
|
138
|
+
ioBroker.iot/Alexa-Anbindung) Kanäle trotzdem sinnvoll klassifizieren können, kann pro
|
|
139
|
+
Gerät die "Art" gesetzt werden:
|
|
140
|
+
|
|
141
|
+
| Art | `<ch>.val` Rolle | `stop`/`open`/`close` Rolle |
|
|
142
|
+
|---|---|---|
|
|
143
|
+
| Storen/Rolladen | `level.blind` | `button.stop` / `button.open.blind` / `button.close.blind` |
|
|
144
|
+
| Licht | `level.dimmer` | generisch `button` |
|
|
145
|
+
| unbekannt (Default) | `value` | generisch `button` |
|
|
146
|
+
|
|
147
|
+
Wichtig: `level.blind` täuscht **keine** echte Positionsrückmeldung vor - laut Feller-Doku
|
|
148
|
+
liefert `chscan`/`chnotify` für einen Storenkanal so gut wie immer `-1` (unbekannt), da die
|
|
149
|
+
Hardware selbst keine Lamellenposition zurückmeldet. Die Rolle verbessert nur die
|
|
150
|
+
Erkennung durch VIS-Widgets, der Zahlenwert bleibt i.d.R. uninformativ.
|
|
151
|
+
|
|
152
|
+
## Hagelalarm-Nutzung
|
|
153
|
+
|
|
154
|
+
```javascript
|
|
155
|
+
// JavaScript-Adapter Beispiel
|
|
156
|
+
on({id: 'wetter.0.warnungen.hagel', val: true}, function () {
|
|
157
|
+
setState('zeptrion.0.control.closeAllShutters', true);
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Fehler bei einzelnen Geräten (offline etc.) unterbrechen die übrigen Kanäle nicht -
|
|
162
|
+
jeder fehlgeschlagene Kanal wird einzeln geloggt und in `<id>.info.lastError` vermerkt.
|
|
163
|
+
|
|
164
|
+
## Bekannte Einschränkungen / bewusste Entscheidungen
|
|
165
|
+
|
|
166
|
+
- **Smartbutton-Webhook-Programmierung** (`zapi/smartbt/prgm`/`prgn`/`prgs`) ist nicht
|
|
167
|
+
implementiert: dabei ruft der Taster bei Tastendruck direkt eine URL auf ioBroker auf
|
|
168
|
+
(echtes Push, ganz ohne Polling). Das würde einen eingehenden HTTP-Server im Adapter
|
|
169
|
+
voraussetzen, den es aktuell nicht gibt - eine grössere Architekturerweiterung, kein
|
|
170
|
+
kleiner Zusatz. Bleibt als möglicher zukünftiger Ausbauschritt dokumentiert.
|
|
171
|
+
- **Schreibzugriff auf `zrap/net`** ist nicht implementiert - WLAN-Zugangsdaten eines
|
|
172
|
+
Aktors per Skript zu ändern ist riskant (Verbindungsverlust, Reboot nötig). Kann bei
|
|
173
|
+
Bedarf ergänzt werden.
|
|
174
|
+
- **Scheduler (`zrap/scheduler`)** und die **zeptrionAir-Smartfront-Services**
|
|
175
|
+
(`zapi/smartfront/*`, `zapi/smartbt/*`) sind nicht implementiert, da für den
|
|
176
|
+
Storen-/Hagel-Use-Case nicht relevant. Die vorhandene `zrapGet`/`zrapPost`-Struktur
|
|
177
|
+
in `main.js` lässt sich leicht erweitern.
|
|
178
|
+
- `chctrl` liefert laut Doku HTTP 302 ohne Body - Redirects werden bewusst nicht verfolgt
|
|
179
|
+
(`maxRedirects: 0`), um unnötige Zusatzrequests zu vermeiden.
|
|
180
|
+
- Bei wiederholten Fehlern eines Geräts wird das Poll-Intervall bis maximal das
|
|
181
|
+
5-fache verlängert (einfacher Backoff).
|
|
182
|
+
|
|
183
|
+
## Entwicklung / Tests
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
npm install
|
|
187
|
+
npm run lint
|
|
188
|
+
npm test # Package-Konsistenz + Unit-Tests
|
|
189
|
+
npm run test:integration # startet echten js-controller (dauert länger)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Veröffentlichung im ioBroker Adapter-Store
|
|
193
|
+
|
|
194
|
+
Kurzer Fahrplan (Details: https://github.com/ioBroker/ioBroker.docs/blob/master/docs/de/dev/adaptercreation.md):
|
|
195
|
+
|
|
196
|
+
1. Autor-E-Mail (`CHANGE_ME@example.com`) in `package.json` eintragen.
|
|
197
|
+
2. Eigenes öffentliches GitHub-Repository `ioBroker.zeptrion` anlegen, Code pushen.
|
|
198
|
+
3. `npx @iobroker/adapter-checker` (bzw. https://adapter-check.iobroker.in/) gegen das
|
|
199
|
+
Repository laufen lassen und alle Fehler/Warnungen bereinigen.
|
|
200
|
+
4. Ersten Release-Tag setzen (`v0.1.0`), CI-Workflow veröffentlicht bei Bedarf automatisch
|
|
201
|
+
auf npm (Secret `NPM_TOKEN` in den Repo-Settings hinterlegen) oder manuell `npm publish`.
|
|
202
|
+
5. Pull Request gegen `ioBroker/ioBroker.repositories` (`sources-dist.json`, Eintrag unter
|
|
203
|
+
`latest`) erstellen.
|
|
204
|
+
6. Nach Review/Merge erscheint der Adapter im Adapter-Store; nach ausreichender Stabilität
|
|
205
|
+
und positiven Bewertungen kann später ein Antrag auf Aufnahme in den `stable`-Zweig
|
|
206
|
+
gestellt werden.
|
|
207
|
+
|
|
208
|
+
## Changelog
|
|
209
|
+
|
|
210
|
+
### 0.7.0 (2026-07-10)
|
|
211
|
+
- Skalierung für 20+ Geräte: paralleles Setup, Poll-Jitter, Duplikat-Erkennung
|
|
212
|
+
- Strikte Startup-Validierung jeder konfigurierten Geräte-Zeile
|
|
213
|
+
- CSV-Massenimport (eigener Konfig-Tab) mit Zeilen-Validierung und Auto-ID
|
|
214
|
+
- FIX: Positionsschätzung nach Stopp während Endlagenfahrt korrekt
|
|
215
|
+
- FIX: Adapter-Timer-Cleanup (this.clearTimeout), führende Nullen in chdes-Codes bleiben erhalten
|
|
216
|
+
|
|
217
|
+
### 0.6.0 (2026-07-10)
|
|
218
|
+
- Auto-ID aus Host, Geräte-Test-Button (Erreichbarkeit + zeptrion-Verifikation + Kanalzahl-Prüfung)
|
|
219
|
+
- Kanal-Objektnamen aus dem Gerät (chdes), neues Icon, Geräte-Icons
|
|
220
|
+
|
|
221
|
+
### 0.5.1 (2026-07-10)
|
|
222
|
+
- KRITISCHER FIX: XML-Parser übersprang die Nutzdaten wegen des XML-Deklarations-Keys - alle GET-Werte blieben in 0.5.0 null
|
|
223
|
+
|
|
224
|
+
### 0.5.0 (2026-07-07)
|
|
225
|
+
- setPosition: zeitbasierte %-Anfahrt für Storen (Chunking wegen 32s-API-Limit, Referenzfahrt bei unbekannter Position)
|
|
226
|
+
- tiltOpen/tiltClose: Lamellen-Kipp-Impulse (konfigurierbare Impulsdauer)
|
|
227
|
+
- calibrate: Positionsschätzung ohne Fahrt setzen
|
|
228
|
+
|
|
229
|
+
### 0.4.0 (2026-07-07) - Security- & Qualitäts-Härtung
|
|
230
|
+
- **Verriegelter Werksreset**: `system.factoryDefault` funktioniert nur noch innerhalb
|
|
231
|
+
von 30s nach Setzen von `system.unlock` - ein einzelner versehentlicher setState aus
|
|
232
|
+
Script/VIS kann das Gerät nicht mehr plätten.
|
|
233
|
+
- **Crashsicheres onStateChange**: der komplette Handler (inkl. der Sammelbefehle) läuft
|
|
234
|
+
jetzt in einer zentralen Fehlerbehandlung - keine Unhandled Promise Rejections mehr
|
|
235
|
+
möglich.
|
|
236
|
+
- **Eingabevalidierung**: Kanalbeschreibung (32/32/24/4/4 Bytes UTF-8), Standort (32),
|
|
237
|
+
NTP-URL (32) und NTP-Intervall (0-255) werden vor dem Senden geprüft; klare
|
|
238
|
+
Fehlermeldung statt HTTP-400 vom Gerät. Umlaute zählen korrekt als 2 Bytes.
|
|
239
|
+
- **Adapter-verwaltete Timer** (`this.setTimeout`) überall - automatische Aufräumung
|
|
240
|
+
beim Unload gemäss ioBroker-Guidelines.
|
|
241
|
+
- **Verbindungs-Ökonomie**: solange der chnotify-Long-Poll gesund läuft, wird der
|
|
242
|
+
redundante chscan-Resync nur noch bei jedem 5. Poll ausgeführt (schont die
|
|
243
|
+
schwachen Embedded-Webserver der Unterputzaktoren).
|
|
244
|
+
- **chnotify abschaltbar** (Experten-Tab) für Umgebungen mit Verbindungsproblemen.
|
|
245
|
+
- **Admin-UI neu**: Tabs (Geräte/Experten), durchgängig EN+DE, Eingabe-Validatoren
|
|
246
|
+
(ID-Muster, Host-Muster), Tooltips an jeder Spalte, Sicherheitshinweis.
|
|
247
|
+
- ESLint auf Flat Config (v9) migriert, Lint läuft sauber durch; Smoke-Tests für
|
|
248
|
+
Kommando-Validierung, Byte-Limits, Positionsmathematik und Multicast-Body.
|
|
249
|
+
|
|
250
|
+
### 0.3.0 (2026-07-07)
|
|
251
|
+
- Kanalbefehle desselben Geräts werden innerhalb eines 50ms-Fensters automatisch zu
|
|
252
|
+
einem einzigen Multicast-POST an `/zrap/chctrl` gebündelt statt sequentiell einzeln
|
|
253
|
+
gesendet - insbesondere `control.closeAllShutters` (Hagelalarm) profitiert davon
|
|
254
|
+
massiv (ein Request pro Gerät statt einer pro Kanal).
|
|
255
|
+
- Optionale zeitbasierte Storen-Positionsschätzung (`posEstimate`) anhand konfigurierbarer
|
|
256
|
+
Motor-Laufzeit, da die Hardware selbst keine Position zurückmeldet.
|
|
257
|
+
- Optionale Smartfront-Unterstützung (`zapi/smartfront/*`): Temperatur/Helligkeit/
|
|
258
|
+
Feuchtigkeit auslesen, LED-Hintergrundfarbe setzen.
|
|
259
|
+
- Rollen-Korrektur: `level.blind` sitzt jetzt auf der Positionsschätzung statt auf dem
|
|
260
|
+
rohen (meist -1) Hardwarewert.
|
|
261
|
+
|
|
262
|
+
### 0.2.0 (2026-07-07)
|
|
263
|
+
- Kanalzustände werden jetzt primär über `zrap/chnotify` (Long-Poll) nahezu in
|
|
264
|
+
Echtzeit aktualisiert statt nur per Intervall-Polling; `chscan`-Poll bleibt als
|
|
265
|
+
periodischer Resync/Fallback erhalten.
|
|
266
|
+
- Zusätzliches Sicherheitsnetz (Busy-Window, 5s) verhindert, dass ein zeitgleicher
|
|
267
|
+
chscan-Resync einen gerade gesendeten Bewegungsbefehl mit einem veralteten Wert
|
|
268
|
+
überschreibt.
|
|
269
|
+
- mDNS-Discovery-Handler gegen Exceptions durch fremde/kaputte Netzwerkpakete
|
|
270
|
+
abgesichert (try/catch je Service-Event statt nur um die Subscription herum).
|
|
271
|
+
- Neues Geräte-Feld "Art" (Storen/Licht/unbekannt) steuert Standard-Objektrollen
|
|
272
|
+
(`level.blind`, `button.stop`, `button.open.blind`, `button.close.blind` bzw.
|
|
273
|
+
`level.dimmer`) für bessere VIS-/Smart-Home-Integration.
|
|
274
|
+
- Strukturierte `native`-Metadaten (Host, Kanalnummer, Art) an Geräte-/Kanal-Objekten.
|
|
275
|
+
|
|
276
|
+
### 0.1.0 (2026-07-07)
|
|
277
|
+
- Erste Version: Kanalsteuerung, Kanalzustand/-beschreibung, Geräte-/Netzwerkinfo,
|
|
278
|
+
Systembefehle, Standort/NTP/Datum, Sammelbefehle für Hagelalarm, mDNS-Discovery.
|
|
279
|
+
|
|
280
|
+
## License
|
|
281
|
+
|
|
282
|
+
MIT License
|
|
283
|
+
|
|
284
|
+
Copyright (c) 2026 Stefan Bühler
|
|
285
|
+
|
|
286
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
287
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
288
|
+
in the Software without restriction, including without limitation the rights
|
|
289
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
290
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
291
|
+
furnished to do so, subject to the following conditions:
|
|
292
|
+
|
|
293
|
+
The above copyright notice and this permission notice shall be included in all
|
|
294
|
+
copies or substantial portions of the Software.
|
|
295
|
+
|
|
296
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
297
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
298
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
299
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
300
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
301
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
302
|
+
SOFTWARE.
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "tabs",
|
|
3
|
+
"items": {
|
|
4
|
+
"tabDevices": {
|
|
5
|
+
"type": "panel",
|
|
6
|
+
"label": { "en": "Devices", "de": "Geräte" },
|
|
7
|
+
"items": {
|
|
8
|
+
"_infoHeader": {
|
|
9
|
+
"type": "staticText",
|
|
10
|
+
"text": {
|
|
11
|
+
"en": "Feller zeptrion / zApp WLAN actuators (WLAN-Nebenstelle 4K, WLAN-Zwischenmodul 2K). Host = IP address or mDNS name (e.g. zapp-14250034.local).",
|
|
12
|
+
"de": "Feller zeptrion / zApp WLAN-Aktoren (WLAN-Nebenstelle 4K, WLAN-Zwischenmodul 2K). Host = IP-Adresse oder mDNS-Name (z.B. zapp-14250034.local)."
|
|
13
|
+
},
|
|
14
|
+
"sm": 12
|
|
15
|
+
},
|
|
16
|
+
"discoverBtn": {
|
|
17
|
+
"newLine": true,
|
|
18
|
+
"type": "sendTo",
|
|
19
|
+
"command": "discover",
|
|
20
|
+
"jsonData": "{}",
|
|
21
|
+
"variant": "contained",
|
|
22
|
+
"label": {
|
|
23
|
+
"en": "🔍 Scan network for zeptrion devices (mDNS, ~4s)",
|
|
24
|
+
"de": "🔍 Netzwerk nach zeptrion-Geräten durchsuchen (mDNS, ca. 4s)"
|
|
25
|
+
},
|
|
26
|
+
"showProcess": true,
|
|
27
|
+
"sm": 12, "md": 6
|
|
28
|
+
},
|
|
29
|
+
"testBtn": {
|
|
30
|
+
"type": "sendTo",
|
|
31
|
+
"command": "testDevices",
|
|
32
|
+
"jsonData": "{}",
|
|
33
|
+
"variant": "outlined",
|
|
34
|
+
"label": {
|
|
35
|
+
"en": "🔎 Test configured devices (reachable? really zeptrion?)",
|
|
36
|
+
"de": "🔎 Konfigurierte Geräte testen (erreichbar? wirklich zeptrion?)"
|
|
37
|
+
},
|
|
38
|
+
"showProcess": true,
|
|
39
|
+
"sm": 12, "md": 6
|
|
40
|
+
},
|
|
41
|
+
"_discoverHint": {
|
|
42
|
+
"type": "staticText",
|
|
43
|
+
"text": {
|
|
44
|
+
"en": "Found devices are added DISABLED to the table below. Close and reopen this dialog after scanning, then verify each row (name, channel count, kind) and enable it. mDNS only works within the same network segment / VLAN.",
|
|
45
|
+
"de": "Gefundene Geräte werden DEAKTIVIERT in die Tabelle übernommen. Dialog nach der Suche schliessen und neu öffnen, dann jede Zeile prüfen (Name, Kanalzahl, Art) und aktivieren. mDNS funktioniert nur im gleichen Netzsegment/VLAN."
|
|
46
|
+
},
|
|
47
|
+
"sm": 12
|
|
48
|
+
},
|
|
49
|
+
"devices": {
|
|
50
|
+
"newLine": true,
|
|
51
|
+
"type": "table",
|
|
52
|
+
"label": { "en": "Devices", "de": "Geräte" },
|
|
53
|
+
"sm": 12,
|
|
54
|
+
"items": [
|
|
55
|
+
{
|
|
56
|
+
"type": "checkbox",
|
|
57
|
+
"attr": "enabled",
|
|
58
|
+
"title": { "en": "Active", "de": "Aktiv" },
|
|
59
|
+
"default": true,
|
|
60
|
+
"width": "5%"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"type": "text",
|
|
64
|
+
"attr": "id",
|
|
65
|
+
"title": "ID",
|
|
66
|
+
"tooltip": { "en": "Leave empty = auto-generated from host. Only a-z, 0-9, _ and -", "de": "Leer lassen = wird automatisch aus dem Host erzeugt. Nur a-z, 0-9, _ und -" },
|
|
67
|
+
"validator": "!data.id || /^[a-zA-Z0-9_-]+$/.test(data.id)",
|
|
68
|
+
"validatorErrorText": { "en": "Only a-z, 0-9, _ and - (or leave empty)", "de": "Nur a-z, 0-9, _ und - (oder leer lassen)" },
|
|
69
|
+
"width": "10%"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"type": "text",
|
|
73
|
+
"attr": "name",
|
|
74
|
+
"title": { "en": "Label", "de": "Bezeichnung" },
|
|
75
|
+
"width": "14%"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"type": "text",
|
|
79
|
+
"attr": "host",
|
|
80
|
+
"title": { "en": "IP address / hostname", "de": "IP-Adresse / Hostname" },
|
|
81
|
+
"validator": "!data.host || /^[a-zA-Z0-9.\\-]+$/.test(data.host)",
|
|
82
|
+
"validatorErrorText": { "en": "Invalid host (no protocol, no spaces)", "de": "Ungültiger Host (kein Protokoll, keine Leerzeichen)" },
|
|
83
|
+
"width": "16%"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"type": "number",
|
|
87
|
+
"attr": "channels",
|
|
88
|
+
"title": { "en": "Channels", "de": "Kanäle" },
|
|
89
|
+
"tooltip": { "en": "3340-4-x = 4, 3340-2-x = 2", "de": "3340-4-x = 4, 3340-2-x = 2" },
|
|
90
|
+
"min": 1, "max": 4, "default": 1,
|
|
91
|
+
"width": "6%"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"type": "select",
|
|
95
|
+
"attr": "kind",
|
|
96
|
+
"title": { "en": "Kind", "de": "Art" },
|
|
97
|
+
"tooltip": { "en": "Controls ioBroker object roles for VIS/smart home integrations", "de": "Steuert ioBroker-Objektrollen für VIS/Smart-Home-Integrationen" },
|
|
98
|
+
"options": [
|
|
99
|
+
{ "value": "unknown", "label": { "en": "unknown/mixed", "de": "unbekannt/gemischt" } },
|
|
100
|
+
{ "value": "blind", "label": { "en": "Blinds/shutters", "de": "Storen/Rolladen" } },
|
|
101
|
+
{ "value": "light", "label": { "en": "Light", "de": "Licht" } }
|
|
102
|
+
],
|
|
103
|
+
"default": "unknown",
|
|
104
|
+
"width": "12%"
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"type": "number",
|
|
108
|
+
"attr": "travelTimeSec",
|
|
109
|
+
"title": { "en": "Motor travel (s)", "de": "Motor-Laufzeit (s)" },
|
|
110
|
+
"tooltip": {
|
|
111
|
+
"en": "Default travel time in seconds, used for all channels unless overridden per-channel below. 0 = position estimation and setPosition disabled. Only relevant for kind=Blinds.",
|
|
112
|
+
"de": "Standard-Laufzeit in Sekunden, gilt für alle Kanäle, sofern nicht rechts pro Kanal überschrieben. 0 = Positionsschätzung und setPosition deaktiviert. Nur relevant bei Art=Storen."
|
|
113
|
+
},
|
|
114
|
+
"min": 0, "max": 300, "default": 0,
|
|
115
|
+
"width": "8%"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"type": "text",
|
|
119
|
+
"attr": "travelTimeSecCh",
|
|
120
|
+
"title": { "en": "Travel/ch (s)", "de": "Laufzeit/Kanal (s)" },
|
|
121
|
+
"tooltip": {
|
|
122
|
+
"en": "Optional: comma-separated per-channel override, one value per channel in order (e.g. '22,28' for a 2K device where channel 1 and 2 have different motor travel times). Empty entries fall back to 'Motor travel (s)'. Leave the whole field empty to use the same value for all channels.",
|
|
123
|
+
"de": "Optional: Kommagetrennte Laufzeit pro Kanal, ein Wert je Kanal in Reihenfolge (z.B. '22,28' für ein 2K-Gerät, bei dem Kanal 1 und 2 unterschiedliche Motor-Laufzeiten haben). Leere Einträge verwenden 'Motor-Laufzeit (s)'. Ganzes Feld leer lassen = gleicher Wert für alle Kanäle."
|
|
124
|
+
},
|
|
125
|
+
"validator": "!data.travelTimeSecCh || /^\\s*\\d*\\s*(,\\s*\\d*\\s*){0,3}$/.test(data.travelTimeSecCh)",
|
|
126
|
+
"validatorErrorText": { "en": "Comma-separated numbers only, max 4 values (e.g. '22,28')", "de": "Nur kommagetrennte Zahlen, max. 4 Werte (z.B. '22,28')" },
|
|
127
|
+
"width": "9%"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"type": "number",
|
|
131
|
+
"attr": "tiltTimeMs",
|
|
132
|
+
"title": { "en": "Tilt pulse (ms)", "de": "Kipp-Impuls (ms)" },
|
|
133
|
+
"tooltip": {
|
|
134
|
+
"en": "Short move pulse for tilting raffstore lamellas via tiltOpen/tiltClose (typically 300-800ms). 0 = disabled. Only relevant for kind=Blinds.",
|
|
135
|
+
"de": "Kurzer Fahr-Impuls zum Schrägstellen von Rafflamellen via tiltOpen/tiltClose (typisch 300-800ms). 0 = deaktiviert. Nur relevant bei Art=Storen."
|
|
136
|
+
},
|
|
137
|
+
"min": 0, "max": 5000, "default": 0,
|
|
138
|
+
"width": "8%"
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"type": "checkbox",
|
|
142
|
+
"attr": "smartfront",
|
|
143
|
+
"title": "Smartfront",
|
|
144
|
+
"tooltip": {
|
|
145
|
+
"en": "Only enable if a Feller Smartfront (920-330x.xx.ZS/.ZU) is attached: adds temperature/brightness/humidity sensors and LED control",
|
|
146
|
+
"de": "Nur aktivieren, wenn ein Feller-Smartfront (920-330x.xx.ZS/.ZU) montiert ist: ergänzt Temperatur/Helligkeit/Feuchte-Sensoren und LED-Steuerung"
|
|
147
|
+
},
|
|
148
|
+
"default": false,
|
|
149
|
+
"width": "8%"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"type": "number",
|
|
153
|
+
"attr": "pollInterval",
|
|
154
|
+
"title": "Poll (s)",
|
|
155
|
+
"tooltip": {
|
|
156
|
+
"en": "Interval for RSSI + periodic resync. Real-time channel updates come via long-poll independently.",
|
|
157
|
+
"de": "Intervall für RSSI + periodischen Resync. Echtzeit-Kanalupdates kommen unabhängig davon per Long-Poll."
|
|
158
|
+
},
|
|
159
|
+
"min": 5, "max": 3600, "default": 30,
|
|
160
|
+
"width": "7%"
|
|
161
|
+
}
|
|
162
|
+
]
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
"tabImport": {
|
|
167
|
+
"type": "panel",
|
|
168
|
+
"label": { "en": "CSV import", "de": "CSV-Import" },
|
|
169
|
+
"items": {
|
|
170
|
+
"_csvHelp": {
|
|
171
|
+
"type": "staticText",
|
|
172
|
+
"text": {
|
|
173
|
+
"en": "Bulk-add devices. One line per device, semicolon separated (recommended if using the last column, since it contains commas):\nhost;name;channels;kind;travel_s;tilt_ms;smartfront;poll_s;travel_s_per_channel\nOnly host is mandatory. kind: blind/light/unknown (also: storen, licht). smartfront: 1/0. travel_s_per_channel (optional): comma-separated per-channel override, e.g. '22,28' for a 2K device with different travel times, empty = use travel_s for all channels. Lines starting with # are ignored, a header line 'host;...' is skipped.\nExample:\n10.195.36.116;Storen Büro;2;blind;22;500;0;30;22,28\n10.195.36.117;Storen Wohnen;2;storen;25",
|
|
174
|
+
"de": "Geräte in Serie anlegen. Eine Zeile pro Gerät, Semikolon getrennt (empfohlen bei Nutzung der letzten Spalte, da diese Kommas enthält):\nhost;name;kanäle;art;laufzeit_s;kipp_ms;smartfront;poll_s;laufzeit_kanal_s\nNur host ist Pflicht. art: blind/light/unknown (auch: storen, licht). smartfront: 1/0. laufzeit_kanal_s (optional): kommagetrennte Laufzeit pro Kanal, z.B. '22,28' für ein 2K-Gerät mit unterschiedlichen Laufzeiten, leer = laufzeit_s für alle Kanäle. Zeilen mit # werden ignoriert, eine Kopfzeile 'host;...' wird übersprungen.\nBeispiel:\n10.195.36.116;Storen Büro;2;blind;22;500;0;30;22,28\n10.195.36.117;Storen Wohnen;2;storen;25"
|
|
175
|
+
},
|
|
176
|
+
"sm": 12
|
|
177
|
+
},
|
|
178
|
+
"csvText": {
|
|
179
|
+
"newLine": true,
|
|
180
|
+
"type": "text",
|
|
181
|
+
"label": { "en": "CSV lines", "de": "CSV-Zeilen" },
|
|
182
|
+
"minRows": 8,
|
|
183
|
+
"maxRows": 20,
|
|
184
|
+
"trim": false,
|
|
185
|
+
"noClearButton": true,
|
|
186
|
+
"sm": 12
|
|
187
|
+
},
|
|
188
|
+
"importBtn": {
|
|
189
|
+
"newLine": true,
|
|
190
|
+
"type": "sendTo",
|
|
191
|
+
"command": "importCsv",
|
|
192
|
+
"jsonData": "{\"csv\": ${JSON.stringify(data.csvText)}}",
|
|
193
|
+
"variant": "contained",
|
|
194
|
+
"label": { "en": "⬆️ Import (validates every line)", "de": "⬆️ Importieren (validiert jede Zeile)" },
|
|
195
|
+
"showProcess": true,
|
|
196
|
+
"sm": 12, "md": 5
|
|
197
|
+
},
|
|
198
|
+
"_importHint": {
|
|
199
|
+
"type": "staticText",
|
|
200
|
+
"text": {
|
|
201
|
+
"en": "Valid lines are added ENABLED, invalid lines are reported and skipped. Duplicate hosts are rejected. After import the adapter restarts - close and reopen this dialog to see the new table rows.",
|
|
202
|
+
"de": "Gültige Zeilen werden AKTIVIERT übernommen, ungültige gemeldet und übersprungen. Doppelte Hosts werden abgelehnt. Nach dem Import startet der Adapter neu - Dialog schliessen und neu öffnen, um die neuen Tabellenzeilen zu sehen."
|
|
203
|
+
},
|
|
204
|
+
"sm": 12
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
"tabExpert": {
|
|
209
|
+
"type": "panel",
|
|
210
|
+
"label": { "en": "Expert", "de": "Experten" },
|
|
211
|
+
"items": {
|
|
212
|
+
"requestTimeout": {
|
|
213
|
+
"type": "number",
|
|
214
|
+
"label": { "en": "HTTP timeout (ms)", "de": "HTTP Timeout (ms)" },
|
|
215
|
+
"help": {
|
|
216
|
+
"en": "Timeout per request to a device (does not apply to the chnotify long-poll, which uses a fixed 35s).",
|
|
217
|
+
"de": "Timeout pro Request an ein Gerät (gilt nicht für den chnotify-Long-Poll, der fix 35s verwendet)."
|
|
218
|
+
},
|
|
219
|
+
"min": 500, "max": 30000, "default": 4000,
|
|
220
|
+
"sm": 12, "md": 4
|
|
221
|
+
},
|
|
222
|
+
"useNotify": {
|
|
223
|
+
"newLine": true,
|
|
224
|
+
"type": "checkbox",
|
|
225
|
+
"label": { "en": "Use chnotify long-poll (near real-time updates)", "de": "chnotify-Long-Poll verwenden (nahezu Echtzeit-Updates)" },
|
|
226
|
+
"help": {
|
|
227
|
+
"en": "Keeps one HTTP connection per device permanently open (device answers within 30s at the latest). Disable only if devices show connection problems; channel states then update only at the poll interval.",
|
|
228
|
+
"de": "Hält dauerhaft eine HTTP-Verbindung pro Gerät offen (Gerät antwortet spätestens nach 30s). Nur deaktivieren, falls Geräte Verbindungsprobleme zeigen; Kanalzustände aktualisieren dann nur noch im Poll-Intervall."
|
|
229
|
+
},
|
|
230
|
+
"default": true,
|
|
231
|
+
"sm": 12, "md": 8
|
|
232
|
+
},
|
|
233
|
+
"_safetyHint": {
|
|
234
|
+
"newLine": true,
|
|
235
|
+
"type": "staticText",
|
|
236
|
+
"text": {
|
|
237
|
+
"en": "⚠️ Safety: the factory reset button (system.factoryDefault) is interlocked - it only works within 30s after setting system.unlock. This prevents accidental wipes from scripts/VIS.",
|
|
238
|
+
"de": "⚠️ Sicherheit: der Werksreset-Button (system.factoryDefault) ist verriegelt - er funktioniert nur innerhalb von 30s nach Setzen von system.unlock. Das verhindert versehentliches Löschen durch Scripts/VIS."
|
|
239
|
+
},
|
|
240
|
+
"sm": 12
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
Binary file
|