FHEM Forum

FHEM - Hausautomations-Systeme => MQTT => Thema gestartet von: Guybrush am 26 August 2026, 19:00:37

Titel: MQTT2_Server Retained Bug
Beitrag von: Guybrush am 26 August 2026, 19:00:37
Beim Entwickeln vom MQTT2_Discovery Modul bin ich über einen Bug im MQTT2_Server gestolpert, der dazu führte, dass bei mir kein Aufruf des mqtt2 server devices mehr möglich war. Alles anderen devices gingen, nur dieses hing. Deswegen hab ich gezielt nach dem Fehler gesucht und tatsächlich wohl einen Bug im MQTT2_Server Modul gefunden.

Bei einem MQTT2_SERVER mit aktivem respectRetain und nicht gesetztem global unicodeEncoding werden retained UTF-8-Payloads beim Fhem Start / Wiederherstellen aus dem RETAIN-/.RETAIN-Reading erneut codiert. Aus "Küche" wird nach dem ersten betroffenen Neustart "Küche", danach "Küche" usw. Nach mehreren Restarts war das entsprechend auf enorme längen gestiegen, wodurch sich symptomatisch zeigte, dass der Aufruf des Devices nicht mehr ging.

Die Wiederherstellung erfolgt soweit ich das recherchiert habe beim FHEM-Start: Beim Einlesen des Statefiles ruft FHEM für das gespeicherte Retain-Reading "MQTT2_SERVER_State()" auf. toJSON() hat die ursprünglichen Bytes zu diesem Zeitpunkt noch korrekt gespeichert. json2nameValue erkennt jedoch die von toJSON erzeugten "\u0022"-Escapes und führt bei deaktiviertem unicodeEncoding anschließend Encode::encode("UTF-8", $val) auf dem gesamten, bereits als UTF-8 vorliegenden Payload aus.

Reproduktion:
respectRetain 1, global unicodeEncoding nicht gesetzt und ein externer MQTT-Client.

1. mosquitto_pub -h <fhem-host> -t test/retain-utf8 -r -m '{"name":"Küche"}'
2. FHEM regulär neu starten, sodass das Statefile geschrieben und anschließend wieder eingelesen wird.
3. Das Topic mit einem neuen Subscriber abrufen. Der Name ist nun `Küche`:
   mosquitto_sub -h <fhem-host> -t test/retain-utf8 -C 1
4. Nach dem Neustart ein anderes retained Topic veröffentlichen. Dadurch wird der gesamte bereits wiederhergestellte Retain-Cache erneut ins Reading geschrieben:
   mosquitto_pub -h <fhem-host> -t test/retain-trigger -r -m 1
5. FHEM erneut starten und "test/retain-utf8" wieder abrufen. Der Name ist jetzt "Küche". Weitere solche Zyklen vergrößern die Verfälschung exponentiell.

Das Problem fiel hier erst spät auf, weil ASCII-Payloads unverändert bleiben und regelmäßig publizierte Status-Topics nach einem Neustart wieder mit korrekten Werten überschrieben werden. Statische Discovery-Topics werden dagegen häufig nur beim Start ihres Publishers gesendet und können deshalb viele FHEM-Neustarts durchlaufen. Für den beobachteten Namen mit 4.100 Zeichen waren ungefähr zwölf Wiederherstellungszyklen erforderlich.

Beim Wiederherstellen des internen Retain-Readings sollte ausschließlich die Legacy-Nachcodierung von json2nameValue unterdrückt werden:
diff --git a/FHEM/00_MQTT2_SERVER.pm b/FHEM/00_MQTT2_SERVER.pm
--- a/FHEM/00_MQTT2_SERVER.pm
+++ b/FHEM/00_MQTT2_SERVER.pm
@@ -267,7 +267,14 @@ MQTT2_SERVER_State()
 
   if($name eq "RETAIN" || $name eq ".RETAIN") {
     my $now = gettimeofday;
-    my $ret = json2nameValue($val);
+    my $ret;
+    {
+      # Retained MQTT-Payloads sind bereits Bytefolgen. Deshalb darf die
+      # Legacy-UTF-8-Konvertierung von json2nameValue hier nicht greifen.
+      local $unicodeEncoding = 1;
+      $ret = json2nameValue($val);
+    }
     for my $k (keys %{$ret}) {
       my %h = ( ts=>$now, val=>$ret->{$k} );
       $hash->{retain}{$k} = \%h;


Die globale Einstellung bleibt dabei unverändert. Im konkreten Test ergaben sich für den Namen folgende Bytefolgen:

vorher: 4b c3 bc 63 68 65
bisherige Wiederherstellung: 4b c3 83 c2 bc 63 68 65
mit dem Patch: 4b c3 bc 63 68 65
Titel: Aw: MQTT2_Server Retained Bug
Beitrag von: rudolfkoenig am 26 August 2026, 19:17:48
Danke, eingecheckt.