MQTT Einführung Teil 2

Aus FHEMWiki

MQTT Teil 2

Konzepte und best practices

Vorwort

Wer den Teil 1 noch nicht gelesen hat, ist herzlich eingeladen das vorher noch schnell zu machen :)

Hier ist Teil 1


Dieser Artikel beschreibt, etwas trocken, weiterführend Konzepte und Denkweisen des Übertragungsprotokolls MQTT. Es wird etwas technischer als die Einführung und der ein oder andere Vergleich zu anderen Protokollen wird auch gezogen. Vieles davon kann man auch so nachlesen, wenngleich nicht immer auf deutsch. Ich gehe davon aus, dass der Leser MQTT vornehmlich mit FHEM einsetzen will.

Ein Beispielkurs ist in Planung. Dort werden wir die Konzepte praktisch kennen lernen.

Retained Messages

Szenario

Stellen wir uns vor, wir starten FHEM neu. Wer Homematic Devices kennt weiß, spätestens nach einem Blick ins Logfile, dass nun zunächst ein Statusrequest aller Geräte erfolgt. Schließlich konnte sich zwischen Beenden und Neustart von FHEM etwas geändert haben.

  • Homematic
  • Es wird jedes Gerät angesprochen und abgefragt:
  • "Lieber Lichtschalter, was tust du grade?"
  • "Ich lasse das Licht über dir leuchten mein User."

MQTT hat hier ein anderes Konzept. Wenn in ein Topic gepostet wird, kann ein "retained Flag" gesetzt werden.

Das bedeutet

Die zuletzt gepostete Nachricht wird vom Broker nicht gelöscht. Publishen wir also den Status unseres Lichts:

  • zuHause/1OG/Kueche/Licht/state "on" oder "off" + retained flag

Sobald wir dieses Topic auf einem Gerät subscriben, bekommen wir den Status angezeigt. Ein neu startendes FHEM ist so ein Fall.

Was bei Homematic erst über einen (zugegeben automatisierten Request) abläuft, kann MQTT mit den retained Messages auch so.

Trifft am Broker eine neue retained Message ein, so wird die alte Nachricht gelöscht. Zum absichtlichen Löschen einer retained Message, etwa weil man weiß, dass dieses Topic zukünftig nicht mehr benötigt wird, kann man eine leere Message posten.

Fazit

Retained Messages sind gut, um jeweils die letzte aktuelle Statusmeldung zu erhalten. Was damit nicht geht, ist z.B. die letzten 30 Messungen eines Temperatursensors zu speichern und auszuliefern! Sobald eine retained Message überschrieben wird, ist der alte Wert futsch.

Last Will and Testament (LWT)

Szenario

Letzter Wille und Testament.


Unser batteriebetriebener Sensor hat keinen Strom mehr, das billige 5V Netzteil stellt seinen Dienst ein, ein netter Nachbar eröffnet ein neues WLAN Netz welches unser Bestehendes gnadenlos stört, unsere süßen kleinen Miezekatzen finden Patchkabel so spannend, dass sie es für erfolgreiche Beißübungen verwenden… Gründe für einen Sensorausfall gibt es durchaus. Was passiert in FHEM? Der letzte Sensorwert bleibt im Reading stehen. Blöd wenn man erst abends feststellt, dass der Kühlschrank offen stand oder der Schaltbefehl zum Heizung einschalten nicht angekommen ist. Ersteres sorgt für aufgetautes Essen, letzteres für kalte Füße. Wie fängt man das ab?

FHEM-seitig könnte man einen Watchdog oder ein DOIF einrichten. Es muss geprüft werden, ob sich das entsprechende Reading in einer bestimmten Zeit verändert hat.


Möglich.

MQTT Lösung

Der Letzte Wille, das Testament (LWT)

Beim Anmelden an einen Broker kann ein Last Will - Topic angegeben werden. Ebenso eine Payload. Wenn das Gerät verschwindet ohne sich sauber abgemeldet zu haben, wird der Broker die gewünschte Nachricht auf dem hinterlegten Topic publishen. Das retained Flag kann auch gesetzt werden. Was bedeutet das für uns?

Zusätzlich zu:

  • zuHause/1_OG/Kueche/Licht/state
  • zuHause/1_OG/Kueche/Licht/set

könnten wir zusätzlich

  • zuHause/1_OG/Kueche/Licht/status

definieren.

Beim Anmelden an den Broker schicken wir (mit retained flag) ein "online" Als LWT geben wir das gleiche Topic an, aber mit "offline" und retained flag. Meldet sich unser Device sauber ab (disconnect), wird das LWT nicht verwendet. Es dient nur dazu, einen Fehler deutlich zu machen.

Vorsicht

LWT bezieht sich auf die Client ID. Was das schon wieder? Jedes angemeldete Gerät hat eine einzige Client ID, unabhängig davon wie viele Topics es published und subscribed hat. LWT auf ein Topic zu beziehen macht wenig Sinn, wenn es mehrere Topics auf dem Gerät gibt. Obiges Beispiel ist also gut, wenn an dem MC genau dieses eine Licht hängt!

  • zuHause/1OG/Arduino_1

Ist der bessere Weg! Jetzt können wir in FHEM auf online / offline reagieren und den davon abhängigen Geräten eine Fehlerbehandlung spendieren. Da ist unser DOIF wieder :)

Noch eine Falle

Wer denkt, er wirft alle Geräte in ein einziges retained last will Topic, sieht immer nur die letzte Meldung!

Böse:

zuHause/MQTT/Status (<= nicht nachmachen)

Fazit

MQTT kann einen eleganten Weg bieten, über das Ende eines Gerätes zu informieren. Genau wie bei Homematic aber auch, muss man diese Meldungen auswerten um was sinnvolles damit anzufangen! Im Fall eines einfachen Sensors ist es recht easy, bei einem Arduino Mega mit 30 Sensoren wird es dann aufwendig (außer man liest weiter bei den Filtern)

Organisation der Topics

Basics, erlaubte Zeichen

MQTT erlaubt prinzipiell den kompletten UTF8 Zeichensatz.

Außer:

  • U+0001..U+001F control characters
  • U+007F..U+009F control characters

Theoretisch gehen also Umlaute, Leerzeichen etc. Praktisch kann es passieren, dass ein Endgerät mit Umlauten einfach nix anfangen kann. Leerzeichen sind auch ein Problem, weil UTF 8 davon doch recht viele Möglichkeiten bietet. Ich würde daher empfehlen, Topicbezeichnungen eher wie Gerätenamen in FHEM zu betrachten: Unterstriche statt Leerzeichen, Umlaute ausschreiben (=> ü => ue...)

Groß- und Kleinschreibung muss beachtet werden. Ein Topic muss mindestens ein Zeichen haben, um gültig zu sein.

Schlechte Beispiele:

  • /zuHause/

nicht ganz falsch, aber der / am Anfang ist nur zusätzlicher Overhead, daher weglassen


  • zuHause//1_OG

Hier wird ein leeres Topic definiert. Das ist ungültig.

Basics, Strukturierung

MQTT kennt publishen und subscriben.

FHEM kennt für jedes Gerät einen "state" und ein "set" Kommando. Das ist bei MQTT nicht nötig, aber durchaus sehr sinnvoll.

Nehmen wir wieder unseren Lichtschalter: Ein Taster an einem Arduino mit einem Relais nebst Lampe daran.

Unglücklich:

  • zuHause/1_OG/Kueche/Lichtschalter

Wenn da ein "on" steht, woher will man wissen ob es sich um einen Befehl oder um eine Statusmeldung handelt? Daher bauen wir die FHEM übliche Logik einfach nach:

Etwas glücklicher (kann man machen, sollte man aber nicht)

  • zuHause/1_OG/Kueche/Lichtschalter/set
  • zuHause/1_OG/Kueche/Lichtschalter/state

Richtig schlau (warum, kommt bei den Filtern)

  • set/zuHause/1_OG/Kueche/Lichtschalter
  • state/zuHause/1_OG/Kueche/Lichtschalter/state

Jetzt wird es einfach:

auf dem set-Topic publishen wir immer Schaltbefehle. Mit oder ohne retained flag, darüber kann man streiten. Unser Arduino muss also dieses Topic abhören und auswerten.

Das state-Topic nehmen wir um darauf zu publishen, wie der Zustand unserer Lampe gerade ist. Unser Arduino muss dieses Topic nur schreiben, niemals selber lesen.

Damit haben wir eine saubere Trennung von Befehl und Status erreicht.

Aufbau eines Topics

Wie bereits geschrieben, kann man die Topics recht frei einfach selbst erfinden. Sinnvoll ist es jedoch, jedem gewünschten Wert ein eigenes Topic zu spendieren.

Ungünstig:

  • zuHause/state/1_OG/Kueche

=> In diesem Topic Licht, Temperatur, Luftfeuchte, Bewegungsmelder etc zu publishen ist eine nicht schlaue Idee; das Problem beginnt damit, dass man erst mal selber rausfinden muss, welchen Wert man gerade hat. Viel Programmcode für quasi umsonst.

Besser:

  • zuHause/state/1_OG/Kueche/Licht
  • zuHause/state/1_OG/Kueche/Raumtemperatur
  • zuHause/state/1_OG/Kueche/Luftfeuchte
  • zuHause/state/1_OG/Kueche/Bewegungsmelder

=> Auf diese Weise weiß man vorher, welche Zahlenwerte in einem Topic stehen und was sie bedeuten. Das hat man automatisch durch die geschickte Topic Wahl festgelegt.

Filter

AB HIER NOCH NICHT FERTIG

Eine witzige Sache von MQTT sind Filter. Das ist in etwa wie in FHEM eine readingsGroup für Arme.

Es gibt zwei Arten von Filtern:

  • Single Level Filter: +
  • Multi Level Filter: #


Beispiele:

  • zuHause/state/1_OG/Kueche/Temperatur
  • zuHause/state/1_OG/Kueche/Kuehlschrank/Temperatur
  • zuHause/state/1_OG/Kueche/Licht
  • zuHause/state/1_OG/Wohnzimmer/Licht
  • zuHause/state/1_OG/Schlafzimmer/Licht

Jetzt filtern wir:

"#" => muss immer der letzte Filter sein

  • zuHause/state/# gibt uns alle oben genannten Topics zurück
  • zuHause/state/1_OG/+/Licht