MQTT best current practice

Begonnen von martinp876, 26 Juli 2026, 09:56:44

Vorheriges Thema - Nächstes Thema

Prof. Dr. Peter Henning

ZitatDa die Welt der "typischen MQTT-Clients" sehr bunt ist (und teils dadurch geprägt zu sein scheint, dass mal eben der Azubi eine MQTT-Funktionalität eingebaut hat - ohne Rücksicht auf Sinnhaftigkeit dessen, was da passiert - ) ist leider weiter Stand der Dinge.

Gut formuliert. MQTT erfordert einfach, sich von den eigenen Vorurteilen in Bezug auf Standardisierung zu trennen.

LG

pah

martinp876

mir ist (ernsthaft) nicht klar, was an meiner Aussage unklar ist.
Der Server ist das IO welches die MQTT cienten mit den realen devices verbindet.
In der Welt ausserhalb kann man MQTT clienten direkt verbinden - das ist vollkommen klar.
Meine Annahme ist, dass ein Client in FHEM erst einmal "nur" alle Informationen des realen Device einsammelt und darstellt. Ok, die Senderichtung habe ich gerade unterschlagen.

So funktionieren zumindest einige andere Famielen in FHEM auch. Das ist eine Kernauffassung aus der sich dann exterm viel ableitet.
Also erste Frage: haben wir hier einen Konsenz oder sind die MQTT_DEVICES bei euch anders definiert?
a) mir ist klar, das man es anders machen KANN. Aber nicht muss.
b) ich muss und will nicht alles implementieren, was MQTT hergibt. Der obige Ansatz sollte reichen, MQTT in FHEM zu harmonisieren
c) mittelfristig ist es mir zu komplex, komplexe abhängigkeiten einzubauen - das werde ich in 2 Monaten nicht mehr warten können

Ich weiss also nicht, was ich durcheinander werfe. Ich habe gesehen, dass ein MQTT_DEVISE zugriff auf alle MQTT messages haben kann. Und genau das brauche ich nicht (KISS-Ansatz).

Ich wieder hole mich wohl,wenn ich also sage:
- die Funktion IO für den Server reicht mir aus
- ein Client  oder auch MQTT_DEVICE bekommt alle Nachrichten seines realen Partners (Kanäle ist eine Abwandlung des gleichen)
- sämtliche interne Trigger werden über notify erledigt.

Das sehe ich als schlank und daher übersichtlich.
Es beraucht noch eine best-practice (nach der ich hier eigentlich gefragt habe) wie man die messages parsed, in Readings wandelt, filtert,...

Wenn der Grundgedanke nicht übereinstimmt  - was ist ein Client "in FHEM"  - dann kommen wir klar zu anderen Ergebnissen.

Noch eines (evtl wiederholt es sich): Ich will MQTT in FHEM einbinden und nicht umgekehrt.

Noch einmal explizit die Frage an das Auditorium das ein MQTT Client in FHEM  leisten soll.
Und noch eines - sorry - meine (experiment-)shelly sind auch clients - formal. die tun auch real etwas. der fhem-client ist also immer nur ein Soft-Abbild. Das ist aus meiner sicht der entscheidende Unterschied zu realen Device.

Sidey

@martinp876


Mein erster Post am Sonntag hat es nicht geschafft, seit dem hat sich die Diskussion weiter entwickelt.
Mit scheint, jetzt ein guter Zeitpunkt für einen neuen Versuch gekommen zu sein.


Ich setze MQTT inzwischen recht umfangreich ein: Tasmota, Shelly Gen1/Gen2, Zigbee2MQTT, OwnTracks, AWTRIX und einige eigene Dienste. Als Broker verwende ich direkt MQTT2_SERVER:

    define MQTT2_FHEM_Server MQTT2_SERVER 1883 global
  attr MQTT2_FHEM_Server autocreate complex
 

  Meine Praxis ist dabei:

  1. attrTemplate als Ausgangspunkt verwenden.
  2. Danach readingList auf die wirklich benötigten Topics und Readings reduzieren.
  3. Mehrkanalgeräte in mehrere MQTT2_DEVICE-Entities aufteilen, wo sinnvoll. Beim Rolladen natürlich nicht.
  4. Mit devicetopic, jsonMap und manchmal auch etwas Perl in der readingList die MQTT-Daten auf FHEM-übliche Readings abbilden.
  5. Erreichbarkeit über LWT beziehungsweise online/availability behandeln.

  Ein Beispiel für einen Shelly Plus 1:

    define GartenSteckdose MQTT2_DEVICE shellyplus1_xxxxxx
  attr GartenSteckdose devicetopic zuhause/terrasse/steckdose

  attr GartenSteckdose jsonMap
  switch_state:state
  switch_temperature_tC:temperature
  switch_temperature_tF:0
  params_wifi_sta_ip:ip

  attr GartenSteckdose readingList
  $DEVICETOPIC/online:.* online
  $DEVICETOPIC/events/rpc:.* { json2nameValue($EVENT,"",$JSONMAP) }
  $DEVICETOPIC/status/switch_0:.* {
  $EVENT =~ s/"output":true/"state":"on"/g;;
  $EVENT =~ s/"output":false/"state":"off"/g;;
  json2nameValue($EVENT,"switch_",$JSONMAP)
  }

  attr GartenSteckdose setList
  toggle:noArg $DEVICETOPIC/rpc {"id":0,"src":"fhem","method":"Switch.Toggle","params":{"id":0}}
  off:noArg $DEVICETOPIC/rpc {"id":0,"src":"fhem","method":"Switch.Set","params":{"id":0,"on":false}}
  on:noArg $DEVICETOPIC/rpc {"id":0,"src":"fhem","method":"Switch.Set","params":{"id":0,"on":true}}
  on-for-timer $DEVICETOPIC/rpc {"id":0,"src":"fhem","method":"Switch.Set","params":{"id":0,"on":true,"toggle_after":$EVTPART1}}

  attr GartenSteckdose setStateList on off
 

  Damit wird aus dem Shelly-Feld output:true bewusst ein FHEM-typisches state:on. Die Zuordnung mache ich nicht erst nach dem vollständigen Flattening eines großen JSON-Baums, sondern bereits anhand des konkreten Topics status/switch_0.

  Für Mehrkanalgeräte verwende ich getrennte FHEM-Devices. Ein Tasmota-Gerät (Shelly analog) mit zwei Relais sieht bei mir vereinfacht so aus:

    define LichtTisch MQTT2_DEVICE DVES_xxxxxx
  attr LichtTisch devicetopic zuhause/wohnzimmer/licht
  attr LichtTisch readingList
  tele/$DEVICETOPIC/LWT:.* LWT
  tele/$DEVICETOPIC/STATE:.* {
  my $r=json2nameValue($EVENT);;
  return { map { $_ => $r->{$_} }
  grep { /^(?:Uptime|POWER1|RSSI)$/ } keys %{$r} };;
  }
  stat/$DEVICETOPIC/RESULT:.* { json2nameValue($EVENT) }
  attr LichtTisch setList
  off:noArg cmnd/$DEVICETOPIC/POWER1 0
  on:noArg cmnd/$DEVICETOPIC/POWER1 1
  toggle:noArg cmnd/$DEVICETOPIC/POWER1 2
  attr LichtTisch stateFormat POWER1

  define LichtCouch MQTT2_DEVICE DVES_xxxxxx
  attr LichtCouch devicetopic zuhause/wohnzimmer/licht
  attr LichtCouch readingList
  tele/$DEVICETOPIC/LWT:.* LWT
  tele/$DEVICETOPIC/STATE:.* {
  my $r=json2nameValue($EVENT);;
  return { map { $_ => $r->{$_} }
  grep { /^(?:Uptime|POWER2|RSSI)$/ } keys %{$r} };;
  }
  stat/$DEVICETOPIC/RESULT:.* { json2nameValue($EVENT) }
  attr LichtCouch setList
  off:noArg cmnd/$DEVICETOPIC/POWER2 0
  on:noArg cmnd/$DEVICETOPIC/POWER2 1
  toggle:noArg cmnd/$DEVICETOPIC/POWER2 2
  attr LichtCouch stateFormat POWER2
 

Beide Entities haben dieselbe CID und dasselbe devicetopic. Die Trennung erfolgt durch die konkreten Topics beziehungsweise durch das Filtern auf POWER1 oder POWER2. So können Automatisierungen mit normalen FHEM-Devices wie LichtTisch und LichtCouch arbeiten, ohne die MQTT-Kanalstruktur kennen zu müssen.

Bei Zigbee2MQTT ist es meist noch kürzer:

    define Kuechenlicht MQTT2_DEVICE zigbee_kuechenlicht
  attr Kuechenlicht devicetopic zigbee2mqtt/zuhause/kueche/licht
  attr Kuechenlicht readingList
  $DEVICETOPIC:.* {
  my $r=json2nameValue($EVENT);;
  $r->{state}=lc($r->{state}) if defined $r->{state};;
  return $r;;
  }
  $DEVICETOPIC/availability:.* availability

  attr Kuechenlicht setList
  on:noArg $DEVICETOPIC/set {"state":"ON"}
  off:noArg $DEVICETOPIC/set {"state":"OFF"}
  brightness:colorpicker,BRI,0,5,255 $DEVICETOPIC/set {"state":"ON","brightness":$EVTPART1}

  attr Kuechenlicht setStateList on off
 

Hier normalisiere ich ON/OFF direkt auf on/off. Die Erreichbarkeit kommt separat über availability.

Mein Fazit: Ich versuche nicht, aus jedem eingehenden JSON automatisch ein universelles FHEM-Device zu erzeugen. MQTT ist für mich zunächst der Transport. Die Übersetzung in die FHEM-Nomenklatur erfolgt pro Gerätefamilie über attrTemplate, readingList, jsonMap und gegebenenfalls eine kleine Perl-Funktion.

Die langen Attribute sind in der Weboberfläche tatsächlich wenig angenehm. In der Konfigurationsdatei sind sie mit Einrückung und Zeilenumbrüchen aber noch gut wartbar. Wichtig für die Performance ist aus meiner Sicht vor allem, Topics möglichst konkret zu abonnieren und aus großen JSON-
Nachrichten nur die tatsächlich benötigten Readings zurückzugeben.

Damit habe ich auch bei vielen Geräten und getrennten Kanal-Entities bislang keine praktischen Performanceprobleme festgestellt.

Grüße Sidey
Signalduino, Homematic, Raspberry Pi, Mysensors, MQTT, Alexa, Docker, AlexaFhem,zigbee2mqtt

Maintainer von: SIGNALduino, fhem-docker, alexa-fhem-docker, fhempy-docker, WebAuth, fhem-mcp

Beta-User

@Sidey:
Besser kann man das kaum zusammenfassen.

@martinp876
Das mit den langen Attributen ist lästig, aber für ein generisches ("fhem-client-") Modul kaum zu vermeiden, denn letztlich muss irgendwo die Info hin, wie eine auf ein bestimmtes Topic kommende Payload auszuwerten sein soll (readingList) bzw. wie und wohin ggf. von FHEM aus zu sendende Anweisungen bzw. Anfragen zusammenzubauen sind (setList/getList).

Die "alte" Implementierung (IO-Modul MQTT, "fhem-clients" MQTT_DEVICE/MQTT_BRIDGE) macht das statt der o.g. Attribute (zu MQTT2_DEVICE) durch eine Vielzahl der "immergleichen" Attribute, ohne die Option, direkt Perl-Funktionen aufzurufen oder "generische" Variablen (=>devicetopic) zu verwenden.

Da die Einrichtung eine einmalige Sache ist, ist das mit den multiline-Attributen m.E. weiterhin der praktikabelste Weg.

Für manche Zwecke rufe ich auch schon mal direkt modifizierte Varianten von json2nameValue() auf. Hier z.B. ein (zigbee-) Device, das an den zigbee2mqtt-Dienst eben sekündlich Daten sendet, die dann von dort aus auch so (ungefiltert) eingehen:
defmod Solaranlage_FI MQTT2_DEVICE zigbee_0xblub
attr Solaranlage_FI devicetopic zigbee2mqtt/0xblub
attr Solaranlage_FI readingList $DEVICETOPIC:.* { FHEM::attrT_z2m_eocr_Utils::j2nV_extended($NAME,$EVENT) }\
$DEVICETOPIC/availability:.* { $EVENT=~s{state}{availability}x;; my $ret=json2nameValue($EVENT);; $ret->{state}='off' if $ret->{availability} eq 'offline';; return $ret }


Es gibt auch noch ein paar fhem-client-Module, die sehr spezifischen Code beinhalten, aber das macht in der Regel nur Sinn, wenn es eigentlich nicht darum geht, erst mal Infos zu sammeln, die dann FHEM-typisch per notifyFn weiter ausgewertet werden. (Bsp: RHASSPY als Sprachsteuerungsoption).

Ansonsten wäre meine Anregung, dich an die für MQTT@FHEM üblichen Begrifflichkeiten zu gewöhnen, sonst gibt das eine Unzahl von Einbruchsstellen für Missverständnisse... (Meintest du in deinem letzten Beitrag tatsächlich MQTT_DEVICE, oder war nicht doch MQTT2_DEVICE gemeint? Eindeutig letzteres...)
Server: HP-elitedesk@Debian 13, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

martinp876

@Sidey,
1) dein Ansatz der FHEM Entites bezüglich der Kanäle entspricht exakt meiner Vorstellung - und Planung.
2) die Auswertung der ReadingList werde ich mir Ansehen

@Beta-User
ZitatDas mit den langen Attributen ist lästig, aber für ein generisches ("fhem-client-") Modul kaum zu vermeiden...
sehe ich ganz und garnicht so. Ok, wenn man es in Attributen machen will, schon. Aber notwendig ist das nicht. Ich habe einfach eine Sub in meinen Utils hierfür erstellt.
attr mydevice coverAbDoor:shelly.*:.* {muParseMQTT($NAME,$CID,$TOPIC,$EVENT)}diese werden ich ausbauen analog wie es bei CUL_HM funktioniert.
- dem MQTT Device werden ich einen "Modelnamen" zu weisen (attr) und ggf eine "Kanal" extra. Weiter werde ich eine ID verankern in def oder in attr - funktional egal.
- die (meine) templates werde ich dann in einer entsprechenden Struktur hinterlegen, welche die Auswertung und die Readings steuert. Um kompatibel zu bleiben wird die Struktur in gewohnter weise zurückgegeben.

Damit erreiche ich, dass die Auswertung für alle gleichem Model gleich (gut oder schlecht) ist.
- Auch setlist wird über das "model" gesteuert werden. Wie alle Typen welche ich ernsthaft betreibe werden auch hier noch meine generischen kommandos addiert - da kann ich die setlist  abfrühstücken.

damit ist meine Installation (cfg) einfach zu prüfen - der Rest ist code und nach "abnahme" statisch und stabil. So brauche ich es.

also danke für den Vorschlag zu den readings - interessiert mich sehr.

@pha @Beta-User klar was ihr sagt - unklar, was ich damit anfangen soll. JEDES device und jede variante/kanalmodel muss jemand /werde ich bei Einsatz prüfen müssen und ein Template erstellen.

Das ganze geht noch nicht meine Bedenken der Performance an. Und da bin ich etwas sensibel... mal sehen, evlrt glätte ich MQTT_DEVICE etwas.
Und dann kommt noch die Maintenance Aufgabe - bspw prüfen der Devices auf "lebt noch".

Vielen Dank für die gute Diskussion bis jetzt.

Beta-User

Zitat von: martinp876 am 29 Juli 2026, 18:41:381) dein Ansatz der FHEM Entites bezüglich der Kanäle entspricht exakt meiner Vorstellung - und Planung.
Wir haben da vermutlich alle miteinander eine ziemlich ähnliche Vorstellung: Jeder (on-/off-) schaltbare (Haupt-) Kanal sollte ein fhem-device ("entity") ergeben.

Einige "Sonderlocken", die es sonst (fast) nur in CUL_HM gibt, finden sich auch in MQTT2_DEVICE: So kann man (via setStateList) einen on-Befehl übergangsweise mit "set_on" nach "state" bekommen, bis die Bestätigung von der Hardware zurückkommt, dasselbe z.B. für "brightness" oder "pct"... 

Die allermeisten in attrTemplate (mqtt2.template) hinterlegten Beispiele berücksichtigen das ;).

Zitat von: martinp876 am 29 Juli 2026, 18:41:38sehe ich ganz und garnicht so. Ok, wenn man es in Attributen machen will, schon. Aber notwendig ist das nicht. Ich habe einfach eine Sub in meinen Utils hierfür erstellt.
Code Auswählen Erweitern
attr mydevice coverAbDoor:shelly.*:.* {muParseMQTT($NAME,$CID,$TOPIC,$EVENT)}diese werden ich ausbauen analog wie es bei CUL_HM funktioniert.
Zwei Anmerkungen:
1. Ist das nicht genau das, was ich beschrieben habe? Du nimmst ein Attribut und schreibst rein, wie eingehende Info zu verarbeiten ist? Wie lange der auslösende Topic-Abschnitt ist, ist in dem Zusammenhang erst mal irrelevant, da in der Regel auf der Hardware zu konfigurieren.

2. Tipp: Verwirf den Gedanken, $CID sei irgendwie wichtig. Klare Topic-Strukturen sind hilfreich, alles andere zur Identifikation der dahinter liegenden Hardware kann (und sollte) man daraus ableiten können.

Zitat von: martinp876 am 29 Juli 2026, 18:41:38Und dann kommt noch die Maintenance Aufgabe - bspw prüfen der Devices auf "lebt noch".
"pct set_90" hatte ich ja bereits als Indikator genannt.
Was "LWT" angeht, sind meine Erfahrungen etwas gemischt. MQTT2_SERVER hat da bei mir z.B. ein Problem mit einem Tasmota, der (jetzt) im LAN hängt (der Vorgänger war mit demselben Symptom via WLAN eingebunden...).

Und was macht man mit Hardware, die hinter einem (Hardware-Interfacing)-Dienst hängt? zigbee2mqtt geht offline, wenn die Software streikt, und dann ist alle Hardware, die dahinter hängt nicht mehr erreichbar (ähnlich, wie wenn das letzte CUL_HM-IO ausfällt). (Ähnliches gilt für den AHOY-DTU-ESP32, mit dem meine PV-Inverter abgehorcht werden).

Kurz: Sowas ist imo eine Aufgabe für ein Querschnittsmodul wie ReadingsWatcher (oder ein "at"...).

Was Performane angeht, kann man sicher das eine oder andere straffer vercoden, v.a., wenn man nur jeweils den Teil berücksichtigt, den man in der eigenen Installation braucht und dabei ggf. den "Zoo" noch dadurch vereinfacht, dass man nur bestimmte Hersteller/firmwares wählt. Solange das klappt, ist alles gut, wenn nicht, wird es schnell (imo) unwartbar.

Aber vermutlich übersehe ich was wichtiges oder wir sprechen aneinander vorbei.
Server: HP-elitedesk@Debian 13, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

TomLee

#21
ZitatMQTT2_SERVER hat da bei mir z.B. ein Problem mit einem Tasmota, der (jetzt) im LAN hängt (der Vorgänger war mit demselben Symptom via WLAN eingebunden...)

Zitat(Ähnliches gilt für den AHOY-DTU-ESP32, mit dem meine PV-Inverter abgehorcht werden)

Beides find ich interessant. Hattest Du dazu mal irgendwo mehr Hintergrund geteilt?