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