iobroker.mqtt-plus 1.6.1

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.
@@ -0,0 +1,359 @@
1
+ # ioBroker.mqtt-plus
2
+
3
+ ![Logo](mqtt-plus.png)
4
+
5
+ **Tests:** ![Test Status](https://img.shields.io/badge/Tests-not%20implemented-yellow)
6
+ **Lizenz:** ![License](https://img.shields.io/badge/License-MIT-blue)
7
+
8
+ ## Zusammenfassung
9
+
10
+ `mqtt-plus` ist ein Adapter für ioBroker, der als Brücke zwischen ioBroker-Datenpunkten und
11
+ einem MQTT-Broker fungiert (genauer: zwischen ioBroker-Datenpunkten und dem Namespace eines
12
+ bereits installierten MQTT-Client-Adapters, z.B. `mqtt.0`). Er legt automatisch die nötige
13
+ Ordnerstruktur auf der Zielseite an, konvertiert Werte live, bietet ein abgesichertes
14
+ Web-Dashboard für Backup/Restore und Struktur-Analyse und kann Daten zusätzlich aktiv an einen
15
+ externen Webhook (Remote Sync) senden.
16
+
17
+ `mqtt-plus` spricht selbst kein MQTT-Protokoll — Verbindung, QoS und `retain` zum eigentlichen
18
+ Broker liegen vollständig beim separat installierten MQTT-Adapter, auf dessen Namespace
19
+ `mqtt-plus` nur liest/schreibt.
20
+
21
+ ## Hauptfunktionen
22
+
23
+ * **Bidirektionale Spiegelung:** Synchronisiert Werte von ioBroker zu MQTT (`out`), von MQTT zu
24
+ ioBroker (`in`) oder in beide Richtungen (`both`).
25
+ * **Topic-Modus pro Mapping:** `single` (ein gemeinsames Topic) oder `dual` nach üblicher
26
+ MQTT-Konvention — Status auf `<topic>`, Befehle auf `<topic>/set` (Suffix frei wählbar).
27
+ * **Automatische Strukturerstellung:** Erstellt rekursiv fehlende Ordner und Datenpunkte auf der
28
+ Zielseite basierend auf den MQTT-Pfaden (z.B. wird `home/küche/licht` zu `home.küche.licht`).
29
+ * **Typ-Konvertierung:** Rundet Zahlen (mit konfigurierbaren Nachkommastellen), wandelt
30
+ Boolean ↔ Zahl um oder erkennt bei `Auto` automatisch den tatsächlichen ioBroker-Datentyp des
31
+ Zielobjekts — in beide Richtungen.
32
+ * **Deterministischer Echo-Schutz:** Verhindert Rückkopplungsschleifen bei `both`-Mappings, ohne
33
+ echte Gerätebestätigungen zu verschlucken (siehe [Echo-Schutz](#echo-schutz-bei-both-mappings)).
34
+ * **Ack-Filter pro Mapping:** Datenpunkte ohne echtes Gerät dahinter (`0_userdata.0.*`,
35
+ `alias.0.*`) können sofort statt erst zeitverzögert synchronisiert werden.
36
+ * **Force-Sync:** Zusätzliches, konfigurierbares Intervall, das den Cache umgeht, Quelle und
37
+ Ziel direkt vergleicht und nur tatsächlich abweichende Werte neu schreibt — heilt Zustände,
38
+ die durch einen Neustart oder Verbindungsabbruch auseinandergelaufen sind.
39
+ * **Unverfälschte Zeitstempel:** Der Spiegel übernimmt `ts` (letzte Aktualisierung), `lc`
40
+ (letzte Änderung) und `q` (Qualität) der Quelle. Inaktive Geräte werden auf der Zielseite
41
+ nicht wieder „frisch“ (siehe [Zeitstempel & Aktualität](#zeitstempel--aktualität)).
42
+ * **Abgesichertes Web-Dashboard:** Login-Pflicht (Basic-Auth), optional per HTTPS, mit
43
+ Brute-Force-Sperre. Bietet Live-Status, JSON-Struktur-Vorschau, Backup & Restore und einen
44
+ Template-Editor für den Remote-Sync.
45
+ * **Remote Sync Client:** Sendet die gesammelten Daten zyklisch oder manuell per HTTP(S) POST an
46
+ einen externen Server. Payload-Format frei per Template konfigurierbar, TLS-Zertifikatsprüfung
47
+ aktiv (mit optionaler eigener CA für interne/selbstsignierte Ziele).
48
+ * **Admin 7 Ready:** Nutzt modernes `jsonConfig` mit responsiven Elementen.
49
+
50
+ ---
51
+
52
+ ## Konfiguration
53
+
54
+ Die Konfiguration erfolgt über die ioBroker Admin-Oberfläche, verteilt auf drei Reiter.
55
+
56
+ ### Reiter 1: Einstellungen & Routen
57
+
58
+ * **MQTT Ziel-Pfad (Prefix):** Der Basis-Namespace, unter dem die gespiegelten Objekte angelegt
59
+ werden. Muss mit einem Punkt enden (Standard: `mqtt.0.`). Ist das Feld leer, wird automatisch
60
+ auf `mqtt.0.` zurückgefallen.
61
+ * **Update Intervall (s):** Zyklischer Abgleich der `out`/`both`-Richtungen — schreibt nur
62
+ Werte, die sich seit dem letzten Durchlauf tatsächlich geändert haben (kein Zwangs-Schreiben,
63
+ das übernimmt der Force-Sync). Minimum 5s, auch wenn ein kleinerer Wert eingetragen wird.
64
+ * **Log Aktiv:** Schreibt jeden synchronisierten Wert als Info-Zeile ins ioBroker-Protokoll.
65
+ * **Force-Sync Intervall (Min, 0 = aus):** Ignoriert den Cache und vergleicht alle
66
+ `out`/`both`-Werte sowie alle `in`-Werte (Modus `Single`) direkt mit dem aktuellen Wert auf der
67
+ Zielseite. Geschrieben wird nur, wo beide tatsächlich voneinander abweichen und die Quelle
68
+ aktiv ist — zur Heilung von Zuständen, die z.B. nach einem Neustart auseinandergelaufen sind.
69
+ Die Schreibvorgänge werden einzeln um 75ms gestaffelt, um das Funkbudget (Zigbee/433MHz)
70
+ nicht als Burst zu belasten.
71
+ * **Aktualitätsgrenze (Min, 0 = aus):** Standard `1440` (24 h). Quellen, deren letztes Update
72
+ älter ist oder die eine Qualität `q ≠ 0` melden, werden beim Start, im Update-Intervall und
73
+ beim Force-Sync nicht gespiegelt. Pro Mapping überschreibbar (Spalte „Aktualität“).
74
+ * **Mapping Tabelle:** Das Herzstück des Adapters.
75
+ * **Quell-ID (ioBroker):** Der originale ioBroker-Datenpunkt
76
+ (z.B. `shelly.0.SHSW-25#D8BFC01A#1.Relay0.Switch`).
77
+ * **Pfad Suffix:** Der gewünschte MQTT-Pfad (z.B. `büro/licht/decke`). `/` wird automatisch
78
+ zu `.`; Zeichen, die ioBroker in IDs verbietet, werden durch `_` ersetzt.
79
+ * **Richtung:** `IOB -> MQTT`, `MQTT -> IOB` oder `Beide`.
80
+ * **MQTT-Topic-Modus:** Betrifft **ausschließlich die MQTT-Seite**. Siehe
81
+ [Topic-Modus](#topic-modus-single-vs-dual) weiter unten. `Single` (Standard) entspricht
82
+ dem bisherigen Verhalten.
83
+ * **Befehls-Suffix (MQTT):** Nur bei `Dual` relevant, Standard `/set`. Ein führender Slash
84
+ ist optional, verschachtelte Suffixe (`/cmd/write`) sind möglich.
85
+ * **Typ:**
86
+ * `Auto`: Ermittelt den tatsächlichen ioBroker-Datentyp des Zielobjekts (`boolean`,
87
+ `number`) und konvertiert passend dorthin — funktioniert in beide Richtungen.
88
+ * `Runden`: Rundet numerische Werte auf die eingestellten Nachkommastellen.
89
+ * `Bool->Num`: `true`/`false` → `1`/`0`.
90
+ * `Num->Bool`: Erkennt `"0"`/`"false"`/`"off"`/leer als `false` und
91
+ `"1"`/`"true"`/`"on"` als `true`, alles andere über normale JavaScript-Wahrheitswerte.
92
+ * Bei `Beide` wird `Bool->Num`/`Num->Bool` in der Rückrichtung automatisch getauscht
93
+ (nie eine Wert-Invertierung, nur die Darstellung passt sich der jeweiligen Seite an).
94
+ * **Nachkommastellen:** Nur für `Runden` relevant, Standard 2.
95
+ * **Einheit:** Optional, für Exporte/Remote-Sync und neu angelegte Zielobjekte.
96
+ * **Ack-Filter:** Siehe [Ack-Filter](#ack-filter-für-datenpunkte-ohne-gerät) weiter unten.
97
+ * **Aktualität (Min):** Leer = globale Aktualitätsgrenze, `0` = für diesen Eintrag keine
98
+ Altersprüfung (sinnvoll bei Geräten, die nur bei Wertänderung melden, z.B. Fensterkontakte).
99
+ * **Sync-Modus:** Siehe [Sync-Modus pro Mapping](#sync-modus-pro-mapping) weiter unten.
100
+
101
+ Die Mappings erscheinen als aufklappbare Liste mit dem Pfad-Suffix als Titel. Jeder Eintrag
102
+ hat zwei Zeilen: oben *was wohin* (Quelle, Pfad, Richtung, Topic-Modus), unten *wie* (Typ,
103
+ Einheit, Ack-Filter, Aktualität, Sync-Modus). Befehls-Suffix und Nachkommastellen werden nur
104
+ angezeigt, wenn sie wirken (Topic-Modus `Dual` bzw. Typ `Runden`).
105
+
106
+ ### Reiter 2: Web Dashboard
107
+
108
+ * **Webserver Port:** Standard 8095.
109
+ * **Bind-Interface:** `0.0.0.0` (alle Interfaces, aus dem LAN erreichbar) oder `127.0.0.1`
110
+ (nur lokal auf dem ioBroker-Host).
111
+ * **Dashboard Benutzername / Passwort:** Zugangsdaten fürs Dashboard (HTTP Basic-Auth). Bleibt
112
+ das Passwort leer, ist der Webserver **ohne Zugangsschutz** erreichbar — der Adapter warnt
113
+ dann beim Start explizit im Log.
114
+ * **TLS-Zertifikat / TLS-Key (PEM, optional):** Beide zusammen gesetzt → das Dashboard läuft
115
+ über HTTPS statt HTTP, Zugangsdaten werden dann verschlüsselt übertragen. Ohne TLS werden
116
+ Basic-Auth-Zugangsdaten im Klartext (Base64 ist keine Verschlüsselung) über das Netz
117
+ übertragen — auf einem reinen LAN ein geringeres, aber reales Risiko.
118
+ * **Dashboard URL:** Schreibgeschütztes Feld mit dem fertigen Link zum Dashboard.
119
+
120
+ ### Reiter 3: Remote Sync
121
+
122
+ * **Webhook URL:** Die vollständige Ziel-URL, inkl. eventueller API-Key-Parameter.
123
+ * **Intervall (Min):** Wie oft automatisch gesendet wird.
124
+ * **Inaktive Quellen weglassen:** Werte, die laut Aktualitätsgrenze/Qualität als inaktiv gelten,
125
+ werden nicht an den Webhook gesendet (Standard: aus).
126
+ * **Vertrauenswürdiges Zertifikat / CA (PEM, optional):** Nur bei selbstsigniertem/internem
127
+ Zertifikat der Sync-Gegenstelle nötig. Leer lassen bei öffentlichem Zertifikat — dann gilt der
128
+ normale System-Vertrauensstore. **Die TLS-Zertifikatsprüfung ist immer aktiv**, es gibt keine
129
+ Möglichkeit, sie abzuschalten; für interne Ziele wird stattdessen gezielt das eigene
130
+ Zertifikat/CA vertraut, statt die Prüfung pauschal zu deaktivieren.
131
+ * **Verbindung Prüfen (Button):** Führt sofort eine Synchronisation aus und zeigt das Ergebnis
132
+ direkt im Admin. Loggt bei diesem manuellen Test zusätzlich den Fingerprint der geladenen CA
133
+ (falls konfiguriert) — nützlich zur Fehlersuche bei Zertifikatsproblemen.
134
+
135
+ ---
136
+
137
+ ## Nutzungskonzepte im Detail
138
+
139
+ ### Das Web Dashboard (Backup & Restore)
140
+
141
+ Erreichbar über die im Reiter „Web Dashboard“ angezeigte URL (Login erforderlich, falls ein
142
+ Passwort gesetzt ist).
143
+
144
+ **Funktionen:**
145
+ 1. **Status:** Watchdog, Mapping-Anzahl, Uptime.
146
+ 2. **Backup & Restore:**
147
+ * **Download Backup:** Lädt eine `.json`-Datei mit Baumstruktur und kompletter
148
+ Mapping-Konfiguration herunter.
149
+ * **Backup Wiederherstellen:** Lädt eine zuvor gesicherte Datei hoch, ersetzt die
150
+ Mapping-Konfiguration und startet den Adapter neu. Der Upload muss von derselben Origin
151
+ kommen wie das Dashboard selbst (CSRF-Schutz) — ein Upload aus einer fremden Webseite
152
+ heraus wird abgelehnt.
153
+ 3. **JSON Struktur:** Live-Vorschau der generierten MQTT-Baumstruktur.
154
+ 4. **Remote Sync Konfiguration (Template):** Format des JSON-Objekts pro Datenpunkt, das an den
155
+ Remote-Server gesendet wird.
156
+
157
+ ### Sicherheit
158
+
159
+ * Ohne gesetztes Dashboard-Passwort ist der Webserver offen erreichbar — der Adapter weist beim
160
+ Start ausdrücklich im Log darauf hin.
161
+ * Nach 10 fehlgeschlagenen Login-Versuchen wird die anfragende IP für 5 Minuten gesperrt. Diese
162
+ Sperre übersteht auch einen Adapter-Neustart (wird persistiert).
163
+ * Zustandsändernde Requests (z.B. Backup-Upload) werden nur akzeptiert, wenn Origin/Referer zum
164
+ eigenen Host passen — verhindert, dass eine fremde Webseite die Konfiguration über den Browser
165
+ eines eingeloggten Nutzers überschreibt.
166
+ * Empfehlung: Passwort setzen und, falls das Dashboard außerhalb eines vertrauenswürdigen LANs
167
+ erreichbar ist, zusätzlich TLS-Zertifikat/Key hinterlegen.
168
+
169
+ ### Ack-Filter für Datenpunkte ohne Gerät
170
+
171
+ Bei der Richtung ioBroker → MQTT wird standardmäßig nur weitergeleitet, wenn eine
172
+ Wertänderung mit `ack: true` markiert ist — das signalisiert normalerweise, dass ein echtes
173
+ Gerät/eine Bridge den Wert bestätigt hat (z.B. ein Shelly-Relais). Bei `0_userdata.0.*` und
174
+ `alias.0.*` gibt es kein Gerät, das eine solche Bestätigung setzt — Werte, die dort z.B. über
175
+ die Admin-UI oder ein Skript geschrieben werden, kommen typischerweise mit `ack: false` an und
176
+ werden mit dem Standard-Filter gar nicht übertragen. Der Filter gilt für alle Wege
177
+ (Ereignis, Start, Update-Intervall, Force-Sync) — ein nie bestätigter Befehl an ein
178
+ Offline-Gerät erscheint so auch nicht nachträglich als Status auf MQTT.
179
+
180
+ Für genau solche Mappings auf den Ack-Filter **„Auch unbestätigt“** stellen: Jede Änderung wird
181
+ dann sofort weitergeleitet, unabhängig vom `ack`-Flag. Bei Mappings mit echtem Gerät dahinter
182
+ bleibt der Standard **„Nur bestätigt“** die richtige Wahl. Die Einstellung wirkt ausschließlich
183
+ auf die Richtung IOB→MQTT, die Rückrichtung MQTT→IOB verlangt immer eine echte
184
+ Broker-Bestätigung.
185
+
186
+ ### Topic-Modus: `single` vs. `dual`
187
+
188
+ Der Modus wird **pro Mapping** eingestellt, nicht global — verschiedene Geräte können also
189
+ unterschiedliche Konventionen nutzen.
190
+
191
+ **Wichtig zur Abgrenzung:** Der Modus betrifft **nur die MQTT-Seite**. Der ioBroker-Datenpunkt
192
+ aus der Spalte „Quell-ID" wird immer direkt angesprochen, ohne jeden Suffix — egal welcher Modus
193
+ eingestellt ist. Ein Shelly-Relais oder eine selbst angelegte `0_userdata`-Variable braucht also
194
+ kein `/set` und bekommt auch keins; nur der Broker sieht die getrennten Topics.
195
+
196
+ | Seite | Modus `Single` | Modus `Dual` |
197
+ |---|---|---|
198
+ | ioBroker (Quell-ID) | `shelly.0.…Relay0.Switch` | `shelly.0.…Relay0.Switch` (identisch) |
199
+ | MQTT Status | `wohnzimmer/licht` | `wohnzimmer/licht` |
200
+ | MQTT Befehl | `wohnzimmer/licht` (dasselbe) | `wohnzimmer/licht/set` |
201
+
202
+ **`Single` (Standard, bisheriges Verhalten):** Befehl und Status teilen sich ein Topic. Bei
203
+ `Richtung = Beide` wird also auf dasselbe Topic geschrieben, von dem auch gelesen wird.
204
+
205
+ **`Dual`:** Folgt der üblichen MQTT-Konvention und trennt beide Aufgaben:
206
+
207
+ | | Topic | Wer schreibt |
208
+ |---|---|---|
209
+ | Befehl (Schreibzugriff) | `wohnzimmer/licht/set` | externer Client / Dashboard |
210
+ | Status (Zustandsmeldung) | `wohnzimmer/licht` | `mqtt-plus`, sobald das Gerät bestätigt |
211
+
212
+ Der Ablauf bei `Richtung = Beide` + `Dual`:
213
+
214
+ 1. Ein Befehl trifft auf `…/licht/set` ein und wird auf den ioBroker-Datenpunkt geschrieben —
215
+ mit `ack: false`, also als unbestätigter Steuerbefehl.
216
+ 2. Das Gerät schaltet und meldet seinen neuen Zustand zurück (`ack: true`).
217
+ 3. `mqtt-plus` spiegelt diese bestätigte Meldung auf das Basis-Topic `…/licht`.
218
+
219
+ Damit sind Schreib- und Leserichtung physisch getrennte Topics — eine Rückkopplung zwischen
220
+ beiden ist strukturell ausgeschlossen, nicht nur durch Schutzmechanismen abgefedert.
221
+
222
+ Die resultierende Zuordnung von Topic und `ack`-Flag:
223
+
224
+ | Funktion | MQTT-Topic | ioBroker `ack` | Zweck |
225
+ |---|---|---|---|
226
+ | Lesen / Status (Get) | `<prefix>/<pfad>` | `ack: true` (bestätigt) | Tatsächlicher Ist-Zustand des Geräts |
227
+ | Setzen / Befehl (Set) | `<prefix>/<pfad>/set` | `ack: false` (Befehl) | Schaltanforderung an das Gerät |
228
+
229
+ Konkret heißt das: Ein eingehender Befehl wird immer mit `ack: false` auf den ioBroker-Datenpunkt
230
+ geschrieben — also als unbestätigte Schaltanforderung, die der zuständige Geräte-Adapter ausführt.
231
+ Auf das Status-Topic wird umgekehrt erst dann etwas gemeldet, wenn das Gerät den neuen Zustand
232
+ mit `ack: true` bestätigt hat. Ein noch unbestätigter Befehl erscheint dort also nicht.
233
+
234
+ Auf dem Befehls-Topic selbst akzeptiert der Adapter im Modus `Dual` **jede** eingehende Nachricht,
235
+ unabhängig vom `ack`-Flag: Viele MQTT-Adapter reichen `/set`-Nachrichten bewusst als
236
+ unbestätigten Steuerbefehl (`ack: false`) weiter, und ein Echo kann dort nicht entstehen, weil
237
+ `mqtt-plus` auf das Befehls-Topic nie selbst schreibt. Im Modus `Single` bleibt die Prüfung auf
238
+ `ack: true` dagegen zwingend — dort wäre ein `ack: false`-Ereignis der eigene Schreibvorgang.
239
+
240
+ Der Adapter legt bei `Dual` beide Objekte im Zielbaum an, das Basis-Topic zuerst und das
241
+ Befehls-Topic darunter. In der JSON-Struktur-Vorschau und im Backup erscheinen beide Topics
242
+ (`full_topic` und `command_topic`) samt Modus.
243
+
244
+ **Wichtig — Befehle werden bei `Dual` nie wiederholt:** Ein Befehls-Topic ist ein Kommando-Kanal,
245
+ kein Zustandsspeicher. Deshalb überspringen sowohl der Initial-Abgleich beim Adapterstart als
246
+ auch der Force-Sync die Richtung MQTT → IOB, wenn `Dual` eingestellt ist. Andernfalls würde ein
247
+ dort liegender (womöglich retained) Befehl nach jedem Neustart bzw. alle paar Minuten erneut
248
+ ausgeführt — ein am Wandschalter ausgeschaltetes Licht ginge dann von selbst wieder an. Im Modus
249
+ `Single` bleibt dieser Abgleich aktiv, weil das Topic dort tatsächlich den Zustand führt.
250
+
251
+ ### Echo-Schutz bei `both`-Mappings
252
+
253
+ Bei `dir: "both"` dient ein Topic gleichzeitig als Quelle und Ziel — ohne Schutzmechanismus
254
+ würde ein eigener Schreibvorgang eine Rückkopplungsschleife auslösen. `mqtt-plus` verhindert das
255
+ auf zwei Ebenen:
256
+
257
+ 1. **MQTT → IOB** ist immer an `ack: true` gebunden. Der MQTT-Adapter markiert eingehende
258
+ Broker-Nachrichten mit `ack: true`, eigene Schreibvorgänge laufen dagegen immer mit
259
+ `ack: false` — ein `ack: false`-Ereignis kann also nie ein eigenes Echo *auf diesem Weg*
260
+ verursachen und wird ignoriert, ohne dass eine echte Bestätigung riskiert wird.
261
+ 2. **IOB → MQTT** verwendet standardmäßig ebenfalls die Ack-Prüfung (siehe Ack-Filter oben) und
262
+ braucht deshalb keinen zusätzlichen Echo-Schutz. Nur bei `ackFilter: "any"` (wo auch eigene
263
+ `ack: false`-Schreibvorgänge durchkommen können) merkt sich der Adapter kurzzeitig
264
+ (max. 10 Sekunden) den zuletzt selbst geschriebenen Wert je Ziel-ID und verwirft ein exakt
265
+ übereinstimmendes Echo — eine echte, abweichende Änderung wird davon nie blockiert.
266
+
267
+ Ergebnis: Echte Gerätebestätigungen kommen zuverlässig durch, auch bei `both`-Mappings, während
268
+ echte Selbst-Loops (v.a. bei `ackFilter: "any"`) weiterhin verhindert werden.
269
+
270
+ Im Topic-Modus `Dual` entfällt die Frage ohnehin: Dort wird auf ein anderes Topic geschrieben
271
+ als gelesen, ein Selbst-Echo kann also gar nicht erst entstehen.
272
+
273
+ ### Zeitstempel & Aktualität
274
+
275
+ Jeder ioBroker-State hat `ts` (letzte Aktualisierung, auch bei gleichem Wert) und `lc` (letzte
276
+ Änderung). Früher bekam jeder Spiegel beim Kopieren `ts = jetzt` — nach jedem Neustart und bei
277
+ jedem Force-Sync. Ein seit Tagen totes Gerät sah dadurch auf der Zielseite dauerhaft aktiv aus.
278
+
279
+ Seit 1.6.0 gilt:
280
+
281
+ 1. **Zeitstempel werden durchgereicht:** Beim Spiegeln eines Zustands werden `ts`, `lc` und `q`
282
+ der Quelle übernommen (`c = "mqtt-plus"` kennzeichnet die Herkunft). Ausnahme: Befehle im
283
+ Topic-Modus `Dual` (`/set`) bekommen den aktuellen Zeitpunkt — ein Befehl ist tatsächlich neu.
284
+ 2. **Nur aktive Quellen:** Start, Update-Intervall und Force-Sync spiegeln eine Quelle nur, wenn
285
+ ihr `ts` jünger als die Aktualitätsgrenze ist und `q = 0` gilt. Echte Änderungs-Ereignisse
286
+ gehen immer durch — das Ereignis selbst belegt, dass das Gerät lebt. Wechsel zwischen
287
+ „inaktiv“ und „wieder aktiv“ werden einmalig im Log gemeldet.
288
+ 3. **Kein blindes Neuschreiben:** Start und Force-Sync lesen zuerst den Zielwert; stimmt er schon,
289
+ wird nicht geschrieben (kein neues `ts`, keine MQTT-Nachricht, kein Funkverkehr).
290
+
291
+ **Hinweis:** Manche Adapter schreiben nur bei Wertänderung — dort bleibt `ts` auch bei aktivem
292
+ Gerät lange stehen. Für solche Mappings die Spalte „Aktualität“ auf `0` setzen oder großzügig
293
+ wählen.
294
+
295
+ ### Sync-Modus pro Mapping
296
+
297
+ | Modus | Verhalten | Wofür |
298
+ |---|---|---|
299
+ | **Standard (nur Änderungen)** | Nur echte Wertänderungen, Original-Zeitstempel, inaktive Quellen ruhen. | Normalfall |
300
+ | **Jede Meldung weiterreichen** | Wie Standard, aber auch jede neue Meldung der Quelle mit *gleichem* Wert wird gespiegelt – `ts` wandert mit. Verstummt das Gerät, veraltet das Ziel ehrlich. | Sensoren, die korrekt messen, deren Wert sich aber kaum ändert und die auf der Zielseite sonst „ausgegraut“ würden |
301
+ | **Force wie vor 1.6.0** | Keine Aktualitätsprüfung, kein Zielvergleich; Start und Force-Sync schreiben immer mit `ts = jetzt`. | Wenn das Ziel unbedingt frisch wirken soll |
302
+
303
+ **Achtung bei „Force wie vor 1.6.0“:** Das Ziel wirkt dauerhaft aktiv, auch wenn das Gerät
304
+ längst nicht mehr meldet – genau das Verhalten, das 1.6.0 abgestellt hat. Wo das Gerät
305
+ regelmäßig meldet (auch mit gleichem Wert), ist „Jede Meldung weiterreichen“ die ehrlichere
306
+ Wahl. „Weiterreichen“ erzeugt dafür mehr MQTT-Nachrichten (eine pro Gerätemeldung).
307
+
308
+ ### Remote Sync & Templates
309
+
310
+ Der Adapter sendet ein Array von Objekten an den konfigurierten Webhook. Bei sehr vielen
311
+ Mappings wird die Übertragung automatisch in Häppchen von je 200 Einträgen aufgeteilt, damit ein
312
+ einzelner Timeout nicht die komplette Payload verwirft; schlägt ein Häppchen fehl, zeigt die
313
+ Fehlermeldung, wie viele Werte bereits angekommen sind. Netzwerkfehler und 5xx-Antworten werden
314
+ mit steigender Wartezeit bis zu zweimal wiederholt, TLS-Zertifikatsfehler und 4xx-Antworten
315
+ dagegen nicht (die ändern sich durch einen Retry ohnehin nicht).
316
+
317
+ **Verfügbare Platzhalter:**
318
+ * `%ID%`: Die ioBroker-Quell-ID (z.B. `shelly.0.relay`).
319
+ * `%MQTT%`: Das definierte MQTT-Suffix (z.B. `licht/kueche`).
320
+ * `%PREFIX%`: Der in den Einstellungen definierte Basis-Pfad (z.B. `mqtt.0.`).
321
+ * `%VAL%`: Der aktuelle Wert (Zahl, String oder Boolean).
322
+ * `%TS%`: Zeitstempel der letzten Aktualisierung (`ts`, ms seit 1970).
323
+ * `%LC%`: Zeitstempel der letzten Wertänderung (`lc`).
324
+ * `%ACK%`: `true`, wenn der Wert vom Gerät bestätigt ist.
325
+ * `%Q%`: Qualität des Werts (`0` = gut).
326
+ * `%UNIT%`: Die definierte Einheit.
327
+ * `%DIR%`: Die konfigurierte Richtung (`in`, `out`, `both`).
328
+
329
+ Alle Platzhalter außer `%VAL%`/`%TS%`/`%LC%`/`%ACK%`/`%Q%` werden beim Einsetzen automatisch JSON-escaped — ein
330
+ Anführungszeichen in einer ID bricht das erzeugte JSON also nicht mehr.
331
+
332
+ **Standard-Template:**
333
+ ```json
334
+ {
335
+ "id": "%ID%",
336
+ "topic": "%MQTT%",
337
+ "value": %VAL%,
338
+ "ts": %TS%,
339
+ "unit": "%UNIT%",
340
+ "prefix": "%PREFIX%",
341
+ "dir": "%DIR%"
342
+ }
343
+ ```
344
+
345
+ ---
346
+
347
+ ## Diagnose-States
348
+
349
+ Neben den Config-Reitern legt der Adapter folgende Status-Datenpunkte an:
350
+
351
+ | State | Bedeutung |
352
+ |---|---|
353
+ | `info.connection` | Webserver läuft/läuft nicht |
354
+ | `info.version` | Aktuell laufende Adapter-Version |
355
+ | `info.status` | Maschinenlesbarer Status (`Running`, `Cycle OK`, `Force-Sync OK`, …) |
356
+ | `info.lastCycle` | Unix-Zeitstempel des letzten abgeschlossenen Sync-Zyklus |
357
+ | `info.lastSyncStatus` | Ergebnis des letzten Remote-Sync-Laufs (Erfolg/Fehlertext) |
358
+ | `info.dashboardUrl` | Fertiger Link zum Web-Dashboard |
359
+ | `watchdog` | Freitext-Statuszeile (historisch, für Übersicht im Objektbaum) |
@@ -0,0 +1,38 @@
1
+ interface MappingEntry {
2
+ id: string;
3
+ mqttName: string;
4
+ dir: "in" | "out" | "both";
5
+ type: "auto" | "round" | "boolToNum" | "numToBool";
6
+ unit?: string;
7
+ ackFilter?: "confirmed" | "any";
8
+ decimals?: number;
9
+ topicMode?: "single" | "dual";
10
+ commandSuffix?: string;
11
+ staleAfterMin?: number | string | null;
12
+ syncMode?: "standard" | "refresh" | "force";
13
+ fullTargetPath?: string;
14
+ commandPath?: string;
15
+ }
16
+ declare global {
17
+ namespace ioBroker {
18
+ interface AdapterConfig {
19
+ targetBasePath: string;
20
+ updateIntervalSec: number;
21
+ logTransfers: boolean;
22
+ mappings: MappingEntry[];
23
+ syncUrl: string;
24
+ syncIntervalMin: number;
25
+ serverPort: number;
26
+ dashboardUser: string;
27
+ dashboardPassword: string;
28
+ dashboardTlsCert: string;
29
+ dashboardTlsKey: string;
30
+ syncCaCert: string;
31
+ forceSyncIntervalMin: number;
32
+ bindHost: string;
33
+ staleAfterMin: number;
34
+ remoteSyncSkipStale: boolean;
35
+ }
36
+ }
37
+ }
38
+ export {};