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.
- package/LICENSE +21 -0
- package/README.md +124 -0
- package/admin/jsonConfig.json +358 -0
- package/admin/mqtt-plus.png +0 -0
- package/admin/readme.md +359 -0
- package/build/main.d.ts +38 -0
- package/build/main.js +1693 -0
- package/build/main.js.map +1 -0
- package/io-package.json +207 -0
- package/package.json +48 -0
package/admin/readme.md
ADDED
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
# ioBroker.mqtt-plus
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
**Tests:** 
|
|
6
|
+
**Lizenz:** 
|
|
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) |
|
package/build/main.d.ts
ADDED
|
@@ -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 {};
|