Überarbeitet 2024-04-03
MQTT ist ein Protokoll zur Nachrichtenübertragung mit Geräten in nicht funktionssicheren Netzwerken. Es ist insbesondere für kleine und kleinste Geräte geeignet, also auch für den Bereich "Internet of Things" (IoT). Eine wesentliche Fähigkeit von MQTT ermöglicht jedem Gerät, unmittelbar nach seiner Anmeldung am Broker eine Nachricht darüber zu erhalten, ob ein bestimmtes anderes Gerät gegenwärtig online ist oder nicht.
(An dieser Stelle erkläre ich keine Grundlagen zu MQTT. Eine sehr gute englischsprachige Dokumentation ist bei HiveMQ zu finden.)
Das hierfür geeignete Verfahren beschreibe ich für Shelly Geräte ab der zweiten Generation, weil ich mit solchen Geräten Versuche durchgeführt habe. Vermutlich gelten die Erkenntnisse auch für Shelly Geräte der ersten Generation. Eine zielführende Anwendung ist in der Shelly Dokumentation zu finden. Auch gibt es eine kurze Sequenz in der Shelly API Dokumentation, in welcher der "Last Will and Testament" Mechanismus erwähnt wird. Dort fand ich aber nichts über die Arbeitsweise der Shelly Firmware, um die gewünschte Online-Information sicherzustellen. Diese Arbeitsweise kann dann besonders interessant sein, wenn man spezifische Anwendungen per Skripte implementiert bzw. implementieren will. Meine Versuche habe ich mit einem Shelly Plus Plug S und einem Shelly Plus 1 sowie dem öffentlichen MQTT Broker test.mosquitto.org:1883 durchgeführt.
Im folgenden wird vorausgesetzt, dass auf den betreffenden Shelly MQTT aktiviert und ein verfügbarer MQTT Broker in der Konfiguration eingetragen ist. Zunächst sei der MQTT Prefix unverändert, also wie im Auslieferungszustand vorgegeben.
Die Nachricht-Eigenschaft retained
Eine mit retained gekennzeichnete Nachricht speichert der Broker in seiner Datenbank. Sie wird immer dann an einen Subscriber gesendet, wenn sich ein solcher beim Broker mit dem zur Nachricht gehörenden Topic-Abonnement anmeldet. Somit stellt eine retained Nachricht immer die letzte Information zum Topic dar. Dies ist nicht selbstverständlich. Nicht retained Nachrichten werden vom Broker nicht gespeichert, sondern nur an Subscriber verteilt, die unmittelbar nach Eintreffen der Nachricht erreichbar sind. Wer nicht da ist, bekommt nichts und dem wird auch nichts nachgereicht. Retained Nachrichten mögen auf den ersten Blick nur Vorteile besitzen, sie können aber zu Fehlverhalten führen - insbesondere bei Steuerungen und Regelungen. Deshalb ist retained nur dann einzusetzen, wenn dies nicht zu Komplikationen führen kann.
Was tut die Firmware eines Shelly nach dem Booten mit MQTT?
- Sie versucht, sich mit dem eingetragenen Broker zu verbinden - als MQTT Client.
Wenn ihr dies nicht gelingt, wird sie es später wiederholt versuchen. Es sei nun vorausgesetzt, dass die Verbindung hergestellt wird. - Zusammen mit dem Herstellen der Verbindung sendet die Firmware an den Broker eine besondere Information, die "Last Will (and) Testament" (LWT) Nachricht. Das Topic dieser Nachricht lautet "<MQTT Prefix>/online", also "<ShellyTyp-MACAdresse>/online". Die Payload besteht aus dem Wort "false". Damit weiß der Broker, dass er die Nachricht an Subscriber senden soll, falls die Verbindung mit dem Shelly unterbrochen ist. Dies tut der Broker immer dann, wenn sich ein Subscriber dieses Topics bei ihm anmeldet. LWT Nachrichten können afaik ausschließlich mit einer Verbindungsherstellung an den Broker übertragen werden, nicht etwa als eigenständige Nachricht. Dies ist zweckdienlich und wird u.a. von einer Dokumentation zur Bibliothek pubsubclient gestützt. Eine LWT Nachricht wird vom Broker gespeichert, damit er sie an Subscriber senden kann, wenn die Kommunikation zwischen dem LWT Client und dem Broker unterbrochen ist. Die Ursachen für eine solche Unterbrechung können unterschiedlich sein. Wenn bspw. der Shelly von der Stromversorgung getrennt wird, kann er nicht mehr mit dem Broker kommunizieren und sich deshalb auch nicht am Broker abmelden. Dann wird der Broker nach einer Weile die LWT Nachricht des "Vermissten" an die Subscriber senden. Meine Versuche ergaben eine Latenzzeit von ca. 90s nach dem "Ableben" des Shelly wegen Power off. Der Broker löscht die gespeicherte LWT Nachricht, wenn sich deren Client vom Broker abmeldet, es sein denn afaik, die LWT Nachricht ist als LWT retained gekennzeichnet. Ein Shelly meldet sich vermutlich nur in folgenden Situationen vom Broker ab.
- MQTT wird in der Konfiguration deaktiviert.
- Der MQTT Broker wird in der Konfiguration geändert.
- Der Shelly rebootet, meldet sich zunächst ab und nach dem Booten wieder an.
Nicht notwendigerweise zu wissende Details
Ich fand nichts eindeutiges darüber, wann ein Shelly sein LWT dem Broker mitteilt, es muss jedenfalls mit einem Verbindungsaufbau erfolgen. Auch habe ich nichts über die genaue Wirkung des LWT retain finden können. Normalerweise (ohne retain?) speichert der Broker das LWT eines Client mit dem Beginn einer Session, also dem Verbindungsaufbau. Wenn dabei der Client kein LWT mitteilt, wird das ggf. bereits gespeicherte LWT gelöscht, wodurch der Broker zu diesem Client keine LWT Nachricht senden wird. Ob daran ein LWT retain etwas ändert, habe ich bisher nicht finden können. Demzufolge ist anzunehmen, dass der Shelly mit jedem Verbindungsaufbau sein LWT mitteilt. Diese Annahme wird gestützt durch die Möglichkeiten, in der Shelly Konfiguration den Broker zu wechseln oder gar MQTT zu deaktivieren. - Weiterhin sendet der Shelly eine normale retained Nachricht an den Broker mit demselben Topic wie unter 2, aber mit der Payload "true". Der Broker sendet an alle Subscriber des Topics diese Payload "true", wenn der Shelly "nicht vermisst" wird, auch an später sich anmeldende Subscriber. So ist sichergestellt, dass jeder Subscriber des LWT Topic aktuelle Informationen über den Online Status des Shelly erhält, solange der Broker verlässlich arbeitet.
Weil die Firmware diese aufgeführten Schritte von alleine durchführt, ist es sinnlos, diese beiden Nachrichten per Skript senden zu wollen. Ein MQTT Subscriber ist aber zu (fast) jeder Zeit darüber informiert, ob ein bestimmter Shelly online ist oder nicht. Während der Latenzzeit von ca. 90s versendet der Broker nicht die LWT Nachricht. Diese Latenzzeit ergibt sich aus dem keep alive Wert, der vom Client mitgeteilt werden kann und ansonsten 60s beträgt. Während jedes keep alive Intervalls muss der Client ein PINGREQ Paket an den Broker senden, um als nicht vermisst zu gelten. Der Broker antwortet mit einem PINGRESP Paket. Eine LWT Nachricht kann ebenso retained sein wie eine normale Nachricht. Vermutlich bleibt eine als retained gekennzeichnete LWT Nachricht auch nach dem regulären Abmelden des Client vom Broker gespeichert. Was dann der Broker beim eintreffen eines korrespondierenden Subscriber Topic tut, konnte ich bisher weder dokumentiert finden noch testen. Vermutlich sendet dann der Broker die LWT Nachricht an den Subscriber, was zumindest zweckmäßig wäre.
Wie sichert der Shelly die Information über seine Nichterreichbarkeit per MQTT?
Wann hierfür per LWT der Broker zuständig ist, wurde bereits dargelegt. Aber auch der Shelly tut etwas dafür, wenn er seine anstehende Nichterreichbarkeit kennt. Er sendet eine vorläufig letzte retained Nachricht mit dem LWT Topic und der Payload "false". Dann meldet er sich beim Broker ab. Mit einer solchen Abmeldung greift die LWT Zuständigkeit des Brokers nicht mehr. Dafür liegt nun ersatzweise die reguläre retained Nachricht vor, die dasselbe Topic und dieselbe Payload wir die LWT Nachricht hat und über die Nichterreichbarkeit des Shelly an Subscriber Auskunft gibt. Vielleicht löscht der Broker die LWT Nachricht mit der regulären Abmeldung des Client. Darüber konnte ich nichts eindeutiges finden.
So oder so greift die letzte retained Nachricht vor dem Abmelden. Dies ist dann nützlich, wenn der Shelly zu einem anderen Broker oder in den Schlafmodus wechselt. Auch bei einem anstehenden Shutdown ist dies zielführend, allerdings kenne ich derzeit keine Möglichkeit, einen Shelly herunterzufahren. Vielleicht findet dies bei Akku oder Batterie betriebenen Shelly statt, falls eine schwache Versorgung festgestellt wird. Vielleicht werden zukünftige Firmware Versionen ermöglichen, gestörtes oder schwaches WLAN festzustellen und als Event oder Status mitzuteilen. Wenn dann Methoden "MQTT.Disconnect" und "MQTT.Connect" zur Verfügung stünden, wäre deren Nutzung auch bei laufendem Skript möglich. Derzeit kann ein Disconnect nur aufwändig in zwei Schritten per RPC "MQTT.SetConfig" mit "enable":false und anschließendem "Shelly.Reboot" erfolgen. Ein Connect entsprechend mit "enable".true ...
Ich suche, lese und teste nach wie vor. Tiefergehende Informationen über LWT sind nicht leicht zu finden, weshalb ich mitunter auf Vermutungen bzw. naheliegenden Schlussfolgerungen angewiesen bin. Man muss dbzgl. auch nicht alles wissen, wenn man keine Systemsoftware entwickelt. 😉
Nicht sehr leicht zu verstehen, aber wichtig!
Ein LWT Eintrag in der MQTT Broker Datenbank bezieht sich immer auf einen MQTT Client bzw. dessen Client Id. Die LWT Nachricht dieses Client wird ausschließlich dann vom Broker an Subscriber gesendet, wenn die Kommunikation mit dem Client unterbrochen ist, ohne dass sich der Client regulär vom Broker abgemeldet hat. Mit dem LWT Eintrag zum Client erhält also der Broker einen Auftrag vom Client, dessen LWT Nachricht zu senden, wenn der Client verhindert ist. Reguläre Nachrichten sind davon unberührt, weil solche vom Client (als Publisher) eigenständig gesendet werden. Eine reguläre Nachricht kann sehr wohl das selbe Topic verwenden wie die LWT Nachricht. Dann erhält der Subscriber dieses Topics die reguläre Nachricht, ohne diese von der LWT Nachricht unterscheiden zu können.
Es kommt somit darauf an, die Bausteine von MQTT geeignet zusammengestellt zu nutzen, damit das gewünschte Ziel mit hinreichender Verlässlichkeit erreicht wird. Dabei kann man diese Bausteine auch fehlerträchtig zusammenstellen. Aus diesem Grund ist u.a. das Thema LWT von Bedeutung.
Wenn du nun verwirrt sein solltest, empfehle ich, den vorletzten Absatz erneut zu lesen und zu verstehen. Danach beginne diesen Artikel noch einmal zu lesen! Dies mag nicht angenehm sein, aber die Begriffe und Zusammenhänge sind vermutlich nicht mit dem ersten Lesen hinreichend zu verarbeiten. Falls du bessere Erklärungen finden solltest als sie bspw. bei HiveMQ zu finden sind, lasse es mich bitte wissen. Ich fand bisher keine besseren, obwohl mir die Erklärungen von HiveMQ nicht in allen Teilen genügen.
Was tut die Firmware bei einem regulären Reboot?
Ich konnte nur die Wirkung feststellen. Diese Wirkung lässt vermuten, dass die Firmware nach dem Auslösen des Reboots eine MQTT Nachricht mit LWT Topic und der Payload "false" sendet, weil diese Nachricht unmittelbar danach beim Subscriber eintrifft. Danach meldet er sich vermutlich beim Broker ab und nach dem Reboot wieder an. Etwa 2 bis 3 Sekunden später, also nach dem Reboot, wird wie oben beschrieben das LWT Topic mit der Payload "true" retained gesendet.
Wie wirkt sich eine Änderung der MQTT Konfiguration aus, bspw. des Prefix?
Versuche mit einem Shelly Plus 1 ergaben, dass nach der Änderung des MQTT Prefix und eines erforderlichen Reboots der Shelly seine beiden online Nachrichten mit dem neuen Prefix im Topic sendet. Da der Shelly dem Broker sein LWT mitteilt, ist darin das neue Prefix enthalten. Vermutlich erfordern Änderungen in der MQTT Konfiguration deshalb ein Reboot, weil der Shelly damit
- per LWT Topic und der Payload "false" retained sein offline mitteilt,
- sich am Broker abmeldet, also die Session schließt,
- wenn MQTT enabled ist, sich mit dem Reboot am Broker anmeldet, also eine Session beginnt und damit sein LWT mit dem neuen Topic mitteilt.
In diesem Zusammenhang ist interessant und naheliegend, dass ein Subscriber des LWT Topic auch dann noch die vom Shelly vor seinem Reboot retained veröffentlichte Nachricht mit LWT Topic und "false" erhält, wenn MQTT vor dem Reboot deaktiviert wurde.
Wozu kann LWT bzw. die Online-Nachricht verwendet werden?
Wenn eine Anwendung Nachrichten per MQTT überträgt, kann sie unter Berücksichtigung einer gewissen Latenzzeit feststellen, ob die Empfangsstelle, hier ein Shelly, gegenwärtig fähig ist, solche Nachrichten zu empfangen. Eine solche Anwendung kann auch per Shelly Skript implementiert sein. Dann kann der Entwickler des Skriptes Sicherheitsmaßnahmen einbauen, die erkennen lassen, ob das Senden einer Nachricht erfolgversprechend ist. Andernfalls kann ein solches Skript entweder die Nachricht verwerfen oder deren Inhalt solange lokal speichern, bis die vorgesehene Empfangsstelle erreichbar ist. Dies kann die Verlässlichkeit von Shelly-Anwendungen erheblich steigern.
2024-04-03