Skip to content

Commit 87ddadb

Browse files
author
sidey79
committed
feat: mqtt Implementierungstoptionen ausgearbeitet
1 parent 4fc12ea commit 87ddadb

File tree

2 files changed

+340
-57
lines changed

2 files changed

+340
-57
lines changed
Lines changed: 340 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
= PySignalDuino - FHEM Integrationsoptionen
2+
3+
Dieses Dokument skizziert 3 Optionen zur Integration der über MQTT publizierten JSON-Nachrichten von PySignalDuino in ein FHEM-System, das traditionell String-basierte Nachrichten via `Dispatch()` erwartet.
4+
5+
Das Quell-Topic ist: `signalduino/v1/state/messages` (oder ähnlich, basierend auf der Konfiguration des Basis-Topics in PySignalDuino), mit einem JSON-Payload, der mindestens `id` (Protokoll-ID) und `data` (dekodierte Hex-Payload) enthält.
6+
7+
== Lösungsoptionen
8+
9+
=== Option 1: MQTT2_DEVICE + Perl-Mapping (`json2nameValue` in `attr`)
10+
11+
Diese Option nutzt die Standardfunktionalität des FHEM-Moduls `MQTT2_DEVICE` in Verbindung mit einem `attr` (Attribut), um das JSON-Payload zu parsen und spezifische Readings zu erstellen.
12+
13+
[cols="1,3"]
14+
|===
15+
|Kriterium | Beschreibung
16+
|**Vorteile** | Keine neue Modul-Entwicklung in FHEM nötig. Reine Konfiguration. Nutzt FHEM-Bordmittel. Geringste Abhängigkeiten.
17+
|**Nachteile** | FHEM-interne `Dispatch()`-Logik wird umgangen. Es muss für jeden Sensortyp ein eigenes FHEM-Device angelegt werden, das die Readings direkt von MQTT liest. Die existierenden Module wie link:../../../.devcontainer/fhem-data/FHEM/14_SD_WS.pm[`14_SD_WS.pm`] (die auf `Dispatch()` warten) können nicht direkt verwendet werden.
18+
|**Implementierungsaufwand (grob)** | **Niedrig**. Konfiguration von einem `MQTT2_DEVICE` und Erstellung der `attr` mit Perl-Code zum Parsen/Dispatch.
19+
|**Notwendige Änderungen** | **FHEM:** Ein `MQTT2_DEVICE` muss abonniert und konfiguriert werden. Wichtig: Das Attribut `Clients` muss manuell gesetzt werden (z.B. `:SD_WS:SD_...`), damit `Dispatch` die Module findet. Es ist ein Perl-Code-Snippet in den Attributen erforderlich, um den `data`-String aus dem JSON-Objekt in das erwartete Format zu transformieren und dann die Readings zu setzen.
20+
|===
21+
22+
23+
=== Option 1b: MQTT2_DEVICE + Perl-Bibliothek (Utils-Funktion)
24+
25+
Diese Option nutzt `MQTT2_DEVICE` zum Empfang des JSONs und lagert die Parsing- und Dispatch-Logik in eine Utility-Bibliothek (eine `.pm`-Datei, die Funktionen exportiert) aus. Die Funktion dieser Bibliothek wird direkt über die `readingList` des `MQTT2_DEVICE` aufgerufen.
26+
27+
[cols="1,3"]
28+
|===
29+
|Kriterium | Beschreibung
30+
|**Vorteile** | Sauberer Code, aber manuelle Integration nötig. Das `MQTT2_DEVICE` wird minimalistisch gehalten. Erlaubt die Wiederverwendung von `Dispatch()`.
31+
|**Nachteile** | Erfordert Installation einer `.pm`-Datei (ähnlich wie Option 2, aber kein volles Modul). Erfordert einen `notify` oder einen `userReadings`-Aufruf, um die Logik auszuführen.
32+
|**Implementierungsaufwand (grob)** | **Niedrig bis Mittel**. Erstellung einer `.pm`-Utility-Datei mit einer Pars- und Dispatch-Funktion. Konfiguration eines `MQTT2_DEVICE`.
33+
|**Notwendige Änderungen** | **FHEM:** Erstellung einer `.pm`-Datei mit einer Utility-Funktion (z.B. `SDU_DispatchJSON($$)`). Diese Funktion wird direkt in der `readingList` des `MQTT2_DEVICE` aufgerufen. Wichtig: Das Attribut `Clients` muss am `MQTT2_DEVICE` manuell gepflegt werden. **PySignalDuino:** Keine Änderungen.
34+
|===
35+
36+
=== Option 2: Eigenes FHEM-Modul (PySignalDuino-Bridge)
37+
38+
Ein neues FHEM-Modul, das die MQTT-Nachrichten von PySignalDuino abonniert und intern die `Dispatch()`-Funktion von FHEM mit den traditionellen Signalduino-Strings aufruft.
39+
40+
[cols="1,3"]
41+
|===
42+
|Kriterium | Beschreibung
43+
|**Vorteile** | **Beste Kompatibilität**. Ermöglicht die Wiederverwendung aller bestehenden FHEM-Module (z.B. link:../../../.devcontainer/fhem-data/FHEM/14_SD_WS.pm[`14_SD_WS.pm`]), da die Bridge das ursprüngliche `Dispatch()`-Verhalten emuliert. Trennung von PySignalDuino-Logik (JSON) und FHEM-Logik (String-Dispatch).
44+
|**Nachteile** | Erfordert die Entwicklung, Wartung und Installation eines neuen Perl-Moduls in FHEM. Komplexität in der JSON-zu-String-Konvertierung (Mapping der Protokoll-ID auf den String-Präfix, z.B. ID 49 auf "W49#...").
45+
|**Implementierungsaufwand (grob)** | **Mittel**. Entwicklung des Bridge-Moduls in Perl, das die MQTT-Subscription und die JSON-Parsing/Dispatch-Logik implementiert.
46+
|**Notwendige Änderungen** | **FHEM:** Neues Perl-Modul (z.B. `98_PySignalDuinoBridge.pm` oder `00_PySignalDuinoBridge.pm`) muss erstellt werden, das den JSON-Payload parst und basierend auf der Protokoll-ID den FHEM-kompatiblen String generiert (z.B. `P<ID>#<Hex>` oder `W<ID>#<Hex>`). **PySignalDuino:** Keine Änderungen.
47+
|===
48+
49+
=== Option 3: Anpassung in PySignalDuino (FHEM-Mode)
50+
51+
PySignalDuino würde eine neue Konfigurationsoption erhalten, die es ihm erlaubt, *zusätzlich* zu oder *anstelle* des Standard-JSON-Formats die traditionellen, von FHEM erwarteten Strings zu publizieren.
52+
53+
[cols="1,3"]
54+
|===
55+
|Kriterium | Beschreibung
56+
|**Vorteile** | Höchste Performance (keine Parsing/Konvertierung in FHEM). Direkte Wiederverwendung der `00_SIGNALduino.pm` (oder `MQTT2_DEVICE` mit einfacher Regex-Subscription) zur Übergabe der Strings an `Dispatch()`.
57+
|**Nachteile** | **Verletzung des Architekturprinzips** (AGENTS.md: Architecture-First Development Process). PySignalDuino sollte eine reine Bridge sein und das standardisierte JSON-Format beibehalten. Ein FHEM-spezifisches Ausgabeformat erhöht die Wartungslast und die Kopplung.
58+
|**Implementierungsaufwand (grob)** | **Mittel**. Änderung der Python-Logik (in z.B. link:../../../signalduino/mqtt.py[`signalduino/mqtt.py`]) zur String-Formatierung basierend auf der Protokoll-ID.
59+
|**Notwendige Änderungen** | **PySignalDuino:** Implementierung der FHEM-String-Konvertierungslogik. Neue Umgebungsvariable (z.B. `MQTT_FHEM_MODE=true`). **FHEM:** Es kann der vorhandene `MQTT2_DEVICE` oder eine geringfügig angepasste Version von link:../../../.devcontainer/fhem-data/FHEM/00_SIGNALduino.pm[`00_SIGNALduino.pm`] verwendet werden, um den String direkt zu abonnieren und zu dispatchen.
60+
|===
61+
62+
=== Option 4: Portierung der Dekodier-Logik (Client-Module) nach PySignalDuino
63+
64+
Anstatt nur Rohdaten zu senden, übernimmt PySignalDuino auch die Interpretation der Daten (z.B. Umrechnung von Hex-Werten in Temperatur, Luftfeuchtigkeit, Batteriestatus, Windgeschwindigkeit etc.). Das entspricht der Logik, die aktuell in FHEM-Modulen wie `14_SD_WS.pm` liegt.
65+
66+
[cols="1,3"]
67+
|===
68+
|Kriterium | Beschreibung
69+
|**Vorteile** | **Perfekt für alle Konsumenten** (Home Assistant, Node-RED, FHEM, ioBroker), da keine proprietäre Dekodier-Logik im Zielsystem nötig ist. Die MQTT-Nachricht enthält direkt verwendbare Schlüssel-Wert-Paare (z.B. `{"temperature": 21.5, "humidity": 60, "battery": "ok"}`). Ermöglicht echtes "MQTT Auto Discovery" (z.B. für Home Assistant). Entkoppelt die Empfangslogik komplett von FHEM.
70+
|**Nachteile** | **Extremer Aufwand.** Es müssen Dutzende/Hunderte von Protokoll-Dekodern von Perl nach Python portiert werden. Verschiebt die Komplexität massiv in dieses Projekt.
71+
|**Implementierungsaufwand (grob)** | **Sehr Hoch**. Systematische Portierung der Logik aus diversen FHEM-Modulen nach Python.
72+
|**Notwendige Änderungen** | **PySignalDuino:** Entwicklung einer Decoder-Schicht, die basierend auf Protokoll-ID den Hex-Payload interpretiert. Erweiterung des JSON-Payloads um dekodierte Felder. **FHEM:** `MQTT2_DEVICE` kann die JSON-Werte direkt als Readings übernehmen (kein Dispatch mehr nötig für diese Protokolle).
73+
|===
74+
75+
76+
== Betrachtung anderer Automatisierungslösungen (Home Assistant, Node-RED)
77+
78+
PySignalDuino publiziert die demodulierten Nachrichten in einem standardisierten JSON-Format über MQTT, was die Integration in andere Smart-Home-Systeme als FHEM erheblich vereinfacht.
79+
80+
[cols="1,1,1,2"]
81+
|===
82+
|Option | PySignalDuino-Payload | FHEM-spezifische Konsequenzen | Systemübergreifende Eignung (HA/Node-RED)
83+
|**1, 1b, 2** | Standardisiertes JSON | Konvertierung/Parsing in FHEM notwendig. | **Gut.** Diese Systeme können JSON nativ und effizient parsen, benötigen aber eigene Logik (Templates/Scripts) zur Interpretation der Rohdaten, wenn FHEM dies nicht übernimmt.
84+
|**3** | FHEM-spezifischer String (z.B. P49#...) | Direkte Integration in FHEM möglich. | **Schlecht.** HA/Node-RED müssten proprietäre FHEM-Strings parsen oder PySignalDuino müsste doppelt senden (JSON + String), was Bandbreite/Performance kostet.
85+
|**4** | Dekodiertes JSON (Werte) | Keine Dispatch-Logik nötig; direkte Nutzung der Readings. | **Exzellent.** Der "Gold Standard". MQTT-Nachrichten sind selbsterklärend und sofort nutzbar. Ermöglicht Plug & Play.
86+
|===
87+
88+
**Fazit:**
89+
* Varianten, die das standardisierte JSON von PySignalDuino beibehalten (Option 1, 1b, 2), sind für eine Koexistenz gut geeignet.
90+
* **Option 4** ist der klare Gewinner für moderne IoT-Landschaften (HA, Node-RED), erfordert aber den größten Aufwand in PySignalDuino.
91+
92+
93+
== Grobe POC-Implementierungen
94+
95+
Um die verschiedenen Optionen besser visualisieren zu können, folgen hier Code-Snippets und Diagramme.
96+
97+
=== Option 1: MQTT2_DEVICE + Perl-Mapping (`json2nameValue` in `attr`)
98+
99+
**FHEM-Konfiguration (MQTT2_DEVICE `readingList`):**
100+
*(Hinweis: Das Attribut `Clients` muss am MQTT2_DEVICE manuell gesetzt werden, z.B. `:SD_WS:SD_...`)*
101+
[source,perl]
102+
----
103+
signalduino/v1/state/messages:.* { json2nameValue($EVENT, 'sd_') }
104+
sd_data:.* { my ($id, $data) = ($EVTPART0 =~ /sd_id_(\d+) /g, $EVTPART0 =~ /sd_data_([0-9A-F]+)/g);; if($data) { Dispatch('signalduino', "P$id\#$data") } }
105+
----
106+
107+
**Flussdiagramm:**
108+
[mermaid]
109+
....
110+
sequenceDiagram
111+
participant P as PySignalDuino
112+
participant M as MQTT Broker
113+
participant F as FHEM (MQTT2_DEVICE)
114+
participant D as FHEM (Sensor Device)
115+
116+
P ->> M: Publish JSON Payload
117+
M ->> F: MQTT Message
118+
F ->> F: readingList: json2nameValue() -> Readings
119+
F ->> F: userReadings (optional): Trigger Dispatch()
120+
F ->> F: Dispatch("P<ID>#<DATA>")
121+
F ->> D: Message an Dispatch()
122+
D ->> D: Handle Message
123+
....
124+
125+
=== Option 1b: MQTT2_DEVICE + Perl-Bibliothek (Utils-Funktion)
126+
127+
**FHEM-Utility-Datei (z.B. in `FHEM/MyUtils.pm` oder separater `.pm`-Datei):**
128+
[source,perl]
129+
----
130+
# In Utility-Datei
131+
sub SDU_DispatchJSON($$) {
132+
my ($hash, $json_payload) = @_;
133+
use JSON;
134+
my $data_hash = JSON::decode_json($json_payload);
135+
my $id = $data_hash->{id};
136+
my $data = $data_hash->{data};
137+
138+
# Konvertierung und Dispatch
139+
my $msg = "P" . $id . "\#" . $data;
140+
Dispatch($hash, $msg);
141+
return 1;
142+
}
143+
----
144+
145+
**FHEM-Konfiguration (MQTT2_DEVICE `readingList`):**
146+
*(Hinweis: Das Attribut `Clients` muss am MQTT2_DEVICE manuell gesetzt werden, z.B. `:SD_WS:SD_...`)*
147+
[source,perl]
148+
----
149+
signalduino/v1/state/messages:.* { SDU_DispatchJSON($hash, $EVENT) }
150+
----
151+
152+
**Flussdiagramm:**
153+
[mermaid]
154+
....
155+
sequenceDiagram
156+
participant P as PySignalDuino
157+
participant M as MQTT Broker
158+
participant F as FHEM (MQTT2_DEVICE)
159+
participant U as FHEM Utility (.pm)
160+
participant D as FHEM (Sensor Device)
161+
162+
P ->> M: Publish JSON Payload
163+
M ->> F: MQTT Message
164+
F ->> U: readingList: SDU_DispatchJSON($hash, $EVENT)
165+
U ->> U: Parse JSON
166+
U ->> U: Convert to "P<ID>#<DATA>"
167+
U ->> F: Dispatch()
168+
F ->> D: Message an Dispatch()
169+
D ->> D: Handle Message
170+
....
171+
172+
=== Option 2: Eigenes FHEM-Modul (PySignalDuino-Bridge)
173+
174+
**FHEM-Modul (Auszug aus `Bridge.pm`):**
175+
[source,perl]
176+
----
177+
sub Bridge_Parse($) {
178+
my ($hash) = @_;
179+
# ... MQTT-Message-Queue abrufen ...
180+
# ... JSON-Payload abrufen ...
181+
use JSON;
182+
my $data_hash = JSON::decode_json($json_payload);
183+
my $id = $data_hash->{id};
184+
my $data = $data_hash->{data};
185+
186+
my $msg = "P" . $id . "\#" . $data;
187+
188+
# Aufruf des Standard-Dispatch
189+
return Dispatch($hash, $msg);
190+
}
191+
----
192+
193+
**Flussdiagramm (Kapselung):**
194+
[mermaid]
195+
....
196+
sequenceDiagram
197+
participant P as PySignalDuino
198+
participant M as MQTT Broker
199+
participant B as FHEM (Bridge Module)
200+
participant D as FHEM (Sensor Device)
201+
202+
P ->> M: Publish JSON Payload
203+
M ->> B: MQTT Message (Subscription)
204+
B ->> B: Internal Parse/Convert Logic
205+
B ->> D: Dispatch("P<ID>#<DATA>")
206+
D ->> D: Handle Message
207+
....
208+
209+
=== Option 3: Anpassung in PySignalDuino (FHEM-Mode)
210+
211+
**PySignalDuino-Code (in link:../../../signalduino/mqtt.py[`signalduino/mqtt.py`]):**
212+
[source,python]
213+
----
214+
# Wenn FHEM-Mode aktiv:
215+
fhem_string = f"P{protocol_id}#{hex_data}"
216+
mqtt_client.publish(fhem_topic, fhem_string)
217+
----
218+
219+
**FHEM-Konfiguration (MQTT2_DEVICE `readingList`):**
220+
[source,perl]
221+
----
222+
signalduino/v1/state/messages:.* { Dispatch('signalduino', $EVENT) }
223+
----
224+
225+
**Flussdiagramm:**
226+
[mermaid]
227+
....
228+
sequenceDiagram
229+
participant P as PySignalDuino
230+
participant M as MQTT Broker
231+
participant F as FHEM (MQTT2_DEVICE)
232+
participant D as FHEM (Sensor Device)
233+
234+
P ->> P: Convert to "P<ID>#<DATA>" String
235+
P ->> M: Publish FHEM String Payload
236+
M ->> F: MQTT Message
237+
F ->> D: readingList: Dispatch('signalduino', $EVENT)
238+
D ->> D: Handle Message
239+
....
240+
241+
=== Option 4: Portierung der Dekodier-Logik (Client-Module) nach PySignalDuino
242+
243+
**PySignalDuino-Code (Erweiterung der Protokoll-Handler):**
244+
[source,python]
245+
----
246+
# Beispiel: Dekodierung eines Temperatursensors
247+
def decode_protocol_49(hex_data):
248+
# Portierte Logik aus 14_SD_WS.pm
249+
temp_raw = int(hex_data[2:4], 16)
250+
temp = (temp_raw - 50) / 10.0
251+
return {
252+
"temperature": temp,
253+
"battery": "ok" if hex_data[0] == 'A' else "low"
254+
}
255+
256+
# Im MQTT-Publisher:
257+
decoded_values = decode_protocol_49(data)
258+
payload = {
259+
"id": 49,
260+
"data": data,
261+
"values": decoded_values # Neues Feld mit interpretierten Werten
262+
}
263+
mqtt_client.publish(topic, json.dumps(payload))
264+
----
265+
266+
**FHEM-Konfiguration (MQTT2_DEVICE):**
267+
[source,perl]
268+
----
269+
# Automatische Erstellung von Readings aus dem JSON
270+
signalduino/v1/state/messages:.* { json2nameValue($EVENT) }
271+
----
272+
Dies erzeugt Readings wie `values_temperature` und `values_battery` direkt am Device.
273+
274+
**Flussdiagramm:**
275+
[mermaid]
276+
....
277+
sequenceDiagram
278+
participant P as PySignalDuino
279+
participant M as MQTT Broker
280+
participant F as FHEM (MQTT2_DEVICE)
281+
participant H as Home Assistant
282+
283+
P ->> P: Demodulate Signal
284+
P ->> P: Decode Values (Temp, Hum, etc.)
285+
P ->> M: Publish JSON with "values"
286+
par To FHEM
287+
M ->> F: MQTT Message
288+
F ->> F: Create Readings (Temp, Hum)
289+
and To Home Assistant
290+
M ->> H: MQTT Message
291+
H ->> H: Auto Discovery / Sensor Update
292+
end
293+
....
294+
295+
== Detaillierte Bewertung
296+
297+
[cols="1,1,1,1,1"]
298+
|===
299+
|Feature | Option 1 (Mapping) | Option 1b (Utils) | Option 2 (Bridge-Modul) | Option 4 (Portierung)
300+
|**Architektur** | "Bastel"-Lösung, unübersichtlich in Attributen | Sauberer Code, aber manuelle Integration nötig | Volle, standardkonforme FHEM-Integration | "Gold Standard", komplette Entkopplung
301+
|**Dispatch-Fähigkeit** | **Kritisch:** Erfordert manuelles `Clients`-Attribut (fehleranfällig) | **Kritisch:** Erfordert manuelles `Clients`-Attribut | **Automatisch:** Integriert im Modul | Nicht nötig (Werte kommen direkt)
302+
|**Konfigurationsaufwand** | Hoch (komplexe Regex im Attribut) | Mittel (Datei + Attribut) | Niedrig (ein `define`) | Sehr Niedrig (Auto-Create Readings)
303+
|**Wartbarkeit** | Schlecht | Gut (Code ist versionierbar) | Sehr gut (gekapseltes Modul) | Sehr gut (zentral in Python)
304+
|**Systemübergreifend** | Neutral (JSON bleibt erhalten) | Neutral (JSON bleibt erhalten) | Neutral (JSON bleibt erhalten) | **Exzellent** (Universal nutzbar)
305+
|**Entwicklungsaufwand** | Niedrig | Mittel | Mittel | **Sehr Hoch**
306+
|===
307+
308+
== Empfehlung
309+
310+
**Kurzfristig / Mittelfristig:**
311+
**Option 2: Eigenes FHEM-Modul (PySignalDuino-Bridge)** oder **Option 1b (Utils-Funktion)**.
312+
313+
* Diese Optionen ermöglichen die sofortige Weiternutzung der existierenden, mächtigen FHEM-Module ohne riesigen Portierungsaufwand.
314+
* Option 2 ist robuster für Endanwender ("Plug & Play"). Option 1b ist schneller für Entwickler umzusetzen.
315+
316+
**Langfristig / Vision:**
317+
**Option 4: Portierung der Dekodier-Logik nach PySignalDuino**.
318+
319+
* Dies sollte das strategische Ziel sein, um PySignalDuino zu einem echten, universellen IoT-Gateway zu machen.
320+
* Es wird empfohlen, dies **schrittweise** für die populärsten Protokolle (z.B. IT, WS) umzusetzen, während die anderen Protokolle weiterhin über Option 2 (Bridge) an FHEM übergeben werden.
321+
* Dies ermöglicht einen sanften Übergang: PySignalDuino liefert `data` (für die Bridge) UND `values` (wenn der Decoder schon portiert ist).
322+
323+
=== Implementierungs-ToDos für Option 2 (Bridge-Modul)
324+
325+
1. **Erstellung des Bridge-Moduls:** Erstellen des FHEM Perl-Moduls (z.B. `98_PySignalDuinoBridge.pm` oder `00_PySignalDuinoBridge.pm`).
326+
2. **MQTT-Abonnement:** Implementierung der Logik zur Subscription des Topics `signalduino/v1/state/messages` (oder konfiguriertem Topic).
327+
3. **JSON-Parsing:** Implementierung der Perl-Logik zum Parsen des JSON-Payloads (z.g. mit `JSON::decode_json`).
328+
4. **String-Konvertierung:** Implementierung der Logik, die `protocol_id` und `data` aus dem JSON-Objekt nimmt und den traditionellen String (z.B. `P<ID>#<Hex>` oder `W<ID>#<Hex>`) generiert.
329+
5. **Dispatching:** Aufruf von `Dispatch(<msg>)` innerhalb des Moduls, um die Nachricht an das FHEM-System weiterzugeben.
330+
6. **Konfiguration und Tests:** Dokumentation und Test der FHEM-Konfiguration (Definieren der Bridge).
331+
332+
== Geplanter Arbeitsablauf
333+
334+
1. **Phase 1 (Design/Planung):** Abschluss der Architekturanalyse und Erstellung des Plans (Abgeschlossen mit diesem Dokument).
335+
2. **Phase 2 (Implementierung):**
336+
* Erstellung des FHEM-Bridge-Moduls.
337+
* Einrichtung der FHEM-Konfiguration für das Modul.
338+
3. **Phase 3 (Validierung):** Testen der End-to-End-Kette (PySignalDuino publiziert JSON -> Bridge parst -> Bridge dismatched String -> FHEM Sensor-Device reagiert).
339+
340+
**Nächster Schritt:** Wechsel in den Code-Modus, um das FHEM-Bridge-Modul zu implementieren (oder mit der Implementierung zu beginnen).

plans/fhem_mqtt_integration_plan.md

Lines changed: 0 additions & 57 deletions
This file was deleted.

0 commit comments

Comments
 (0)