Hi, ich mal wieder.
Bezüglich dieser beiden Beitrage,
https://forum.fhem.de/index.php/topic,81942.msg740059.html#msg740059 (https://forum.fhem.de/index.php/topic,81942.msg740059.html#msg740059)
https://forum.fhem.de/index.php/topic,84370.0.html (https://forum.fhem.de/index.php/topic,84370.0.html)
habe ich mir das Problem mal genauer angeschaut und folgendes festgestellt. Die Aussage $EVTPART geht nur bis 9 ist nicht ganz falsch.
Und zwar funktioniert es innerhalb Perl Codes und wahrscheinlich auch Shellcommands bei größeren Zahlen wie 10. Innerhalb von FHEM Befehlen gibt es ein Problem. Das fatale, nicht immer. Das liegt an der grundsätzlichen Unsortiertheit der HASHes. Hier $evalSpecials.
Das bedeutet, wird zuerst $EVTPART1 verarbeitet werden alle $EVTPART1* ersetzt, wird aber zuerst $EVTPART10 ausgewertet dementsprechend dann alle $EVTPART10*.
Lässt sich aber leicht durch ein sort beheben.
Gruß
Index: fhem.pl
===================================================================
--- fhem.pl (Revision 16184)
+++ fhem.pl (Arbeitskopie)
@@ -1116,7 +1116,7 @@
$cmd =~ s/^[ \t]*//;
if($evalSpecials) {
map { my $n = substr($_,1); my $v = $evalSpecials->{$_};
- $cmd =~ s/\$$n/$v/g; } keys %{$evalSpecials};
+ $cmd =~ s/\$$n/$v/g; } sort { $b cmp $a } keys %{$evalSpecials};
$evalSpecials = undef if(!$calledFromChain || $calledFromChain ne "ACC");
}
my ($fn, $param) = split("[ \t][ \t]*", $cmd, 2);
[EDIT]
Lässt sich übrigens folgendermaßen veranschaulichen:
define d dummy
defmod n notify n set d $EVTPART12
trigger n a b c d e f g h i j k l m
Bringt manchmal m und manchmal b2.
Wohingegen
defmod n notify n {Log 1, $EVTPART12}
immer funktioniert.
Hier ist mir auch noch aufgefallen, wenn man weniger als 12 Evtparts hat, wird auch bei meinem Patch b2 im Dummy stehen. Auch etwas suboptimal.
Da events mit mehr als 10 Teilen ziemlich selten vorkommen und auch Dein patch das Problem nicht wirklich behebt, sollte man einfach alles so lassen wie es ist.
Wer tatsächlich einen event mit so vielen Einzelteilen verarbeiten will, kann das jederzeit in seiner 99_myUtils.pm machen und dort den split einfach selbst vornehmen. Das läßt sich sowieso einfacher verarbeiten.
Das ist nicht wirklich dein Ernst. Du willst mich einen Zustand bestehen lassen der einem falsche und unzuverlässige Werte zurück gibt.
Ich bin schockiert.
Natürlich behebt der Patch DAS Problem, es gibt einfach noch ein zusätzliches Problem, welches eventuell noch gar nicht aufgetreten ist. Siehe mein letzter Satz. Und ich mache mir tatsächlich Gedanken darüber ob ich das Problem nicht etwas zu ausführlich beschrieben habe. ;D
Aber anstatt dauernd im gestern zu leben vielleicht einfach Mal selbst Gedanken zu einer Problemlösung machen. Vielleicht so.Index: fhem.pl
===================================================================
--- fhem.pl (Revision 16184)
+++ fhem.pl (Arbeitskopie)
@@ -1116,7 +1116,7 @@
$cmd =~ s/^[ \t]*//;
if($evalSpecials) {
map { my $n = substr($_,1); my $v = $evalSpecials->{$_};
- $cmd =~ s/\$$n/$v/g; } keys %{$evalSpecials};
+ $cmd =~ s/\$$n(?!\d+)/$v/g; } keys %{$evalSpecials};
$evalSpecials = undef if(!$calledFromChain || $calledFromChain ne "ACC");
}
my ($fn, $param) = split("[ \t][ \t]*", $cmd, 2);
Damit dürften beide Probleme behoben sein. Noch nicht getestet, hab gerade ein schlafendes Kind auf mir und schreibe mit dem Handy.
Da die zweite Variante auch nicht perfekt ist (wenn $EVENT12 stehenbleibt, hilft auch keinem) habe ich die einfacher zu verstehende erste Version kurz getestet und eingecheckt. Und wer damit immer noch Probleme hat, der moege sich via shell oder perl weiterhelfen.
Den Teufel mit dem Beelzebub austreiben... welch ein Quatsch.
Hallo Rudi,
Warum so einen halbgarren Patch für eine Anforderung einpflegen welche einmal in hundert Jahren vor kommt. Sowas kann man nun wirklich mit split und ner myUtils machen. Niemand wird mehr wie 10 Teile eines Events einfach so im Notify verarbeiten. Sowas ist einfach Blödsinn.
Bitte überlege es Dir noch mal. Gibt genug andere Baustellen.
Grüße
Weil der Patch klein und verstaendlich ist, keine Nebenwirkungen hat, und im Normalfall hilft.
Hallo zusammen, auch wenn dieser Thread alt ist, beschreibt er mein Problem ganz gut.
Ich nutze das THZ Modul https://wiki.fhem.de/wiki/Tecalor_THZ_W%C3%A4rmepumpe (https://wiki.fhem.de/wiki/Tecalor_THZ_W%C3%A4rmepumpe)
Dort gibt es Readings, die regelmäßig aktualisiert werden, die ich gerne als sauber formatiertes JSON über MQTT verschicke.
Ich habe das über ein notify in 86 EVTPARTs zerlegt.
Vor x Jahren hatte ich mich mühsam durch den PEARL Code gefriemelt ;-)
Mit einer alten FHEM Version lief der Code jetzt auch jahrelang zuverlässig:
define notify_publish_LWZ303i_sGlobal notify Mythz:sGlobal.* set mqtt2Mosquitto publish -r LWZ303i/Readings/{((split(":","$EVTPART0"))[0])} {"{((split(":","$EVTPART0"))[0])}":{"{((split(":","$EVTPART1"))[0])}":$EVTPART2,"{((split(":","$EVTPART3"))[0])}":$EVTPART4,"{((split(":","$EVTPART5"))[0])}":$EVTPART6,"{((split(":","$EVTPART7"))[0])}":$EVTPART8,"{((split(":","$EVTPART9"))[0])}":$EVTPART10,"{((split(":","$EVTPART11"))[0])}":$EVTPART12,"{((split(":","$EVTPART13"))[0])}":$EVTPART14,"{((split(":","$EVTPART15"))[0])}":$EVTPART16,"{((split(":","$EVTPART17"))[0])}":$EVTPART18,"{((split(":","$EVTPART19"))[0])}":$EVTPART20,"{((split(":","$EVTPART21"))[0])}":$EVTPART22,"{((split(":","$EVTPART23"))[0])}":$EVTPART24,"{((split(":","$EVTPART25"))[0])}":$EVTPART26,"{((split(":","$EVTPART27"))[0])}":$EVTPART28,"{((split(":","$EVTPART29"))[0])}":$EVTPART30,"{((split(":","$EVTPART31"))[0])}":$EVTPART32,"{((split(":","$EVTPART33"))[0])}":$EVTPART34,"{((split(":","$EVTPART35"))[0])}":$EVTPART36,"{((split(":","$EVTPART37"))[0])}":$EVTPART38,"{((split(":","$EVTPART39"))[0])}":$EVTPART40,"{((split(":","$EVTPART41"))[0])}":$EVTPART42,"{((split(":","$EVTPART43"))[0])}":$EVTPART44,"{((split(":","$EVTPART45"))[0])}":$EVTPART46,"{((split(":","$EVTPART47"))[0])}":$EVTPART48,"{((split(":","$EVTPART49"))[0])}":$EVTPART50,"{((split(":","$EVTPART51"))[0])}":$EVTPART52,"{((split(":","$EVTPART53"))[0])}":$EVTPART54,"{((split(":","$EVTPART55"))[0])}":$EVTPART56,"{((split(":","$EVTPART57"))[0])}":$EVTPART58,"{((split(":","$EVTPART59"))[0])}":$EVTPART60,"{((split(":","$EVTPART61"))[0])}":$EVTPART62,"{((split(":","$EVTPART63"))[0])}":$EVTPART64,"{((split(":","$EVTPART65"))[0])}":$EVTPART66,"{((split(":","$EVTPART67"))[0])}":$EVTPART68,"{((split(":","$EVTPART69"))[0])}":$EVTPART70,"{((split(":","$EVTPART71"))[0])}":$EVTPART72,"{((split(":","$EVTPART73"))[0])}":$EVTPART74,"{((split(":","$EVTPART75"))[0])}":$EVTPART76,"{((split(":","$EVTPART77"))[0])}":$EVTPART78,"{((split(":","$EVTPART79"))[0])}":$EVTPART80,"{((split(":","$EVTPART81"))[0])}":$EVTPART82,"{((split(":","$EVTPART83"))[0])}":$EVTPART84,"{((split(":","$EVTPART85"))[0])}":$EVTPART86}}
Nachdem ich das FHEM jetzt neu aufsetzen musste, funktioniert der PEARL Code leider nicht mehr.
Bis $EVTPART20 funktioniert es. Ab $EVTPART21 kommt Unsinn raus.
Ist 20 jetzt die neue Grenze für $EVTPARTx?
Wie kann ich alternativ die Nachricht so verarbeiten, dass hinten ein sauberes JSON rauskommt?
Zitat von: frankef am 13 Dezember 2022, 21:15:29
Wie kann ich alternativ die Nachricht so verarbeiten, dass hinten ein sauberes JSON rauskommt?
Mehrere Möglichkeiten...
- FHEM hat eine interne Funktion toJSON(), die Dir vielleicht weiterhilft
- Du kannst $EVENT in einer eigenen Funktion in beliebig viele Teile splitten
Und es heisst perl, nicht pearl.
Oh toJSON() kling schonmal gut.
Ich finde irgendwie kein passendes Beispiel für mich, oder kannst mir kurz mit der Syntax auf die Sprünge helfen?
Ich nehme mal ein kürzeres Beispiel aus meinem alten Code:
Das Reading ist "sHC2"
Der Inhalt ist z.B: "outsideTemp: -9.5 returnTemp: 30.2 vorlaufTemp: 32.7 heatSetTemp: 32.7 heatTemp: 32.7 stellgroesse: -0.7 seasonMode: winter hcOpMode: setback"
Das war mein altes Notify:
define notify_publish_LWZ303i_sHC2 notify Mythz:sHC2.* set mqtt2Mosquitto publish -r LWZ303i/Readings/{((split(":","$EVTPART0"))[0])} {"{((split(":","$EVTPART0"))[0])}":{"{((split(":","$EVTPART1"))[0])}":$EVTPART2,"{((split(":","$EVTPART3"))[0])}":$EVTPART4,"{((split(":","$EVTPART5"))[0])}":$EVTPART6,"{((split(":","$EVTPART7"))[0])}":$EVTPART8,"{((split(":","$EVTPART9"))[0])}":$EVTPART10,"{((split(":","$EVTPART11"))[0])}":$EVTPART12,"{((split(":","$EVTPART13"))[0])}":"$EVTPART14","{((split(":","$EVTPART15"))[0])}":"$EVTPART16"}}
Und dieses Notiy hatte bei einem Update des Readings eine solche JSON Message über MQTT versendet (auf dem Topic "LWZ303i/Readings/sHC2"):
{"sHC2":{"outsideTemp":-9.5,"returnTemp":30.2,"vorlaufTemp":32.7,"heatSetTemp":32.7,"heatTemp":32.7,"stellgroesse":-0.7,"seasonMode":"winter","hcOpMode":"setback"}}
Wie könnte ich das jetzt mit der toJSON() Funktion machen?
Verwende bitte code Tags in Deinen Beiträgen! Danke.
In der 99_myUtils.pm habe ich folgendes eingetragen:
sub test {
my $text = "outsideTemp: -9.5 returnTemp: 30.2 vorlaufTemp: 32.7 heatSetTemp: 32.7 heatTemp: 32.7 stellgroesse: -0.7 seasonMode: winter hcOpMode: setback";
my %a = split(/ /,$text);
return toJSON \%a;
}
Dann habe ich in der FHEM Befehlszeile eingegeben:
{test}
und erhalte folgendes Ergebnis:
{"hcOpMode:":"setback","heatSetTemp:":"32.7","heatTemp:":"32.7","outsideTemp:":"-9.5","returnTemp:":"30.2","seasonMode:":"winter","stellgroesse:":"-0.7","vorlaufTemp:":"32.7"}
Was will man mehr?
Zitat von: betateilchen am 13 Dezember 2022, 22:50:00
Was will man mehr?
Die numerischen Werte ohne Quotes, für einen korrekten JSON.
Hallo zusammen, ja genau.
Für die Weiterverarbeitung beim Empfänger der MQTT-Nachricht ist es wichtig, dass das ein korrektes JSON ist, wo numerische Werte nicht als Strings drin stehen.
Ich versuche das jetzt jeweils in einer eigenen Funktion für jedes Reading (mit unterschiedlichen Längen und Positionen der Werte).
Eine generische Funktion, die
- selbst erkennt, was ein nummerischer Wert ist
- funktioniert, egal ob im Event 5 oder 50 Werte drin stehen
- das Ergebnis als JSON wieder ins notify zurückliefert um dort weiterverarbeitet zu werden
wäre toll, aber da habe ich zu wenig Verständnis von Perl.
Mein Codeschnipsel war doch nur die Prinzipbeschreibung.
Es ist doch gar kein Problem, sowohl die überflüssigen Doppelpunkte in den keys zu entfernen als auch die Anführungszeichen um numerische Werte.
Mein Ziel war lediglich, eine mögliche Vorgehensweise darzustellen, nicht eine fertige copy&paste Lösung zu präsentieren.
Zitat von: frankef am 14 Dezember 2022, 19:59:35
Ich versuche das jetzt jeweils in einer eigenen Funktion für jedes Reading
Das ist doch totaler bullshit. Den Namen von device und reading kann man doch einfach als Parameter an die Funktion übergeben und dann in einem Aufruf von ReadingsVal() verwenden, um den Quellwert zu lesen, den man dann verarbeiten will.
Das, was Du als Anforderung an eine "generische Funktion" forderst, bringt FHEM alles mit.
Zitat von: betateilchen am 14 Dezember 2022, 21:03:31
Das ist doch totaler bullshit. Den Namen von device und reading kann man doch einfach als Parameter an die Funktion übergeben und dann in einem Aufruf von ReadingsVal() verwenden, um den Quellwert zu lesen, den man dann verarbeiten will.
Das, was Du als Anforderung an eine "generische Funktion" forderst, bringt FHEM alles mit.
Die Eigene Funktion für jedes Reading brauche ich nicht, weil ich device und reading drinnen brauche (das Reading steckt ja auch im $Event mit drin.), sondern weil der Inhalt der jeweiligen Readings unterschiedlich lang ist und weil manchmal auch strings statt nummerische Werte drin vorkommen und zwar an uneinheitlichen Stellen.
Um dafür etwas generisches zu bauen müsste man die Anzahl der mit Leerzeichen getrennten Einträge ermitteln (-> ok das würde ich bestimmt hinbekommen) und dann für jeden Wert (Eintrag im Array) rausbekommen ob es ein nummerischer Wert ist (-> dann nicht als String "-6.5" sondern als Wert -6.5 in das JSON packen).
Hast du vielleicht noch einen Hinweis, wie man das mit Perl-Mitteln machen würde?
Zitat von: betateilchen am 13 Dezember 2022, 22:50:00
und erhalte folgendes Ergebnis:
{"hcOpMode:":"setback","heatSetTemp:":"32.7","heatTemp:":"32.7","outsideTemp:":"-9.5","returnTemp:":"30.2","seasonMode:":"winter","stellgroesse:":"-0.7","vorlaufTemp:":"32.7"}
Was will man mehr?
Hier ist mit noch aufgefallen, dass dei toJSON-Funktion die Werte neu sortiert. Kann man das vermeiden?
Zitat von: frankef am 14 Dezember 2022, 21:45:20
Hier ist mit noch aufgefallen, dass dei toJSON-Funktion die Werte neu sortiert.
Falsch.
Das macht nicht die toJSON Funktion, sondern das ist ein Verhalten des perl Datentyps "hash". Die Elementreihenfolge in einem hash ist mehr oder weniger willkürlich. Deshalb greift man ja in einem hash über den key auf einen bestimmten Wert zu und nicht über seine Position (das wäre in einem array so)
Zitat von: frankef am 14 Dezember 2022, 21:37:20
Hast du vielleicht noch einen Hinweis, wie man das mit Perl-Mitteln machen würde?
Natürlich... simpelstes perl...
use JSON;
use Scalar::Util qw/looks_like_number/;
sub test {
my $text = "outsideTemp: -9.5 returnTemp: 30.2 vorlaufTemp: 32.7 heatSetTemp: 32.7 heatTemp: 32.7 stellgroesse: -0.7 seasonMode: winter hcOpMode: setback";
$text =~ s/://g;
my @a = split (/ /,$text);
map {$_ += 0 if looks_like_number($_)} @a;
my %a = @a;
return encode_json \%a;
}
Ergebnis:
{"outsideTemp":-9.5,"hcOpMode":"setback","heatTemp":32.7,"heatSetTemp":32.7,"stellgroesse":-0.7,"returnTemp":30.2,"vorlaufTemp":32.7,"seasonMode":"winter"}
Es ist übrigens dieser Funktion schnurzpiepegal, wieviele Werte in Deinem Quellstring stecken und wie lang sie sind...
--
Zitat von: betateilchen am 14 Dezember 2022, 21:54:04
Natürlich... simpelstes perl...
use JSON;
use Scalar::Util qw/looks_like_number/;
sub test {
my $text = "outsideTemp: -9.5 returnTemp: 30.2 vorlaufTemp: 32.7 heatSetTemp: 32.7 heatTemp: 32.7 stellgroesse: -0.7 seasonMode: winter hcOpMode: setback";
$text =~ s/://g;
my @a = split (/ /,$text);
map {$_ += 0 if looks_like_number($_)} @a;
my %a = @a;
return encode_json \%a;
}
--
Super, vielen vielen Dank.
Damit habe ich mir jetzt eine generische Funktion "ReadingtoJSON" gebaut, die meine Werte wieder wie vorher als JSON einpackt.
Ich rufe sie innerhalb des Notify auf (hier für das Reading sGlobal), welches die MQTT Nachricht auf dem richtigen Topic versendet:
define notify_publish_LWZ303i_sGlobal_TEST notify Mythz:sGlobal.* set mqtt2Mosquitto publish -r LWZ303i/Readings/sGlobal_TEST {(ReadingtoJSON("Mythz","sGlobal"))}
Die Funtion sieht so aus:
use JSON;
use Scalar::Util qw/looks_like_number/;
sub ReadingtoJSON($$) {
my ($device, $reading) = @_;
my $text = ReadingsVal("$device","$reading","");
$text =~ s/://g;
my @a = split (/ /,$text);
map {$_ += 0 if looks_like_number($_)} @a;
my %a = @a;
my $text1 = encode_json \%a;
return "{"."\"".$reading."\":".$text1."}";
}
Das ist sicher nicht perfekt, aber es funktioniert erstmal für mich - soweit ich das gerade überblicke.
Nochmal vielen Dank!
PS: Ich habe ne Menge an Perl noch nicht verstanden und kratze an der Oberfläche. Ich vermute Perl und ich werden leider keine Freunde ;-)
Doch noch eine Frage:
Dass die Reihenfolge der Werte im Hash (und dann auch in der MQTT Nachricht) zufällig ist, erschwert das debugging später.
Kann man die noch sortieren, damit sie wenigstens immer in der gleich Reihenfolge kommen (auch wenn die unterschiedlich zur Reihenfolge im Reading sein mag)?
Nein.
Zitat von: frankef am 14 Dezember 2022, 23:13:48
use JSON;
use Scalar::Util qw/looks_like_number/;
sub ReadingtoJSON($$) {
my ($device, $reading) = @_;
my $text = ReadingsVal("$device","$reading","");
$text =~ s/://g;
my @a = split (/ /,$text);
map {$_ += 0 if looks_like_number($_)} @a;
my %a = @a;
my $text1 = encode_json \%a;
return "{"."\"".$reading."\":".$text1."}";
}
Das ist sicher nicht perfekt, aber es funktioniert erstmal für mich - soweit ich das gerade überblicke.
Nochmal vielen Dank!
PS: Ich habe ne Menge an Perl noch nicht verstanden und kratze an der Oberfläche. Ich vermute Perl und ich werden leider keine Freunde ;-)
Die Funktion klappt bei den meisten Readings. Allerdings nicht bei sFan. Das liegt vermutlich daran, dass dieses Reading eine führende null hat.
Ich spreche mal immi an, ich vermute das ist ein Versehen.
Hier noch was aus der RHASSPY-Küche, Rhasspy ist auch etwas wählerisch, was das JSON-Format angeht:
sub _toCleanJSON {
my $data = shift // return;
return $data if ref $data ne 'HASH';
my $json = toJSON($data);
$json =~ s{(":"(true|false|null)")}{": $2}gxms;
$json =~ s{":"}{": "}gxms;
$json =~ s{("enable": (?:false|true)),("intentId": "[^"]+")}{$2,$1}gms;
return $json;
}
Übergeben wird ein HASH-Type-Datenelement, die Rückgabe müßte sortiert sein, soweit ich mich entsinne, und wenn man die letzte spezielle rauswirft, müßte das auch den Anforderungen genügen... (toJSON handhabt Ziffern korrekt).