🖼️ Chart-Plot nach PNG oder SVG in einem Reading, keine Datei dabei, (Non-)Blocking

Begonnen von Torxgewinde, 14 September 2023, 23:05:59

Vorheriges Thema - Nächstes Thema

Torxgewinde

Edit #1: BlockingCall (Im Hintergrundprozess berechnen) ist in dem Post unten zu finden, das erlaubt flackerfreies Updaten eines SVGs: https://forum.fhem.de/index.php?topic=134978.msg1287077#msg1287077

Ich benötige das SVG eines ein SVG-Device als Reading (für FhemNative um es dort als HTML darstellen zu können). In dem SVG Modul gibt es eine nicht sonderlich gut dokumentierte Funktion plotAsPng(). Diese liefert aber immer ein PNG zurück. Die PNG Datei ist leicht unscharf, deswegen wollte ich die SVG Daten in einem Reading erhalten. Da ich nicht in dem Modul 98_SVG.pm direkt editieren wollte, habe ich es in ein dummy-Device ausgelagert.

Das geht wie folgt:
defmod bla dummy
attr bla userattr SVG_Device_Name
attr bla SVG_Device_Name SVG_GartenLogfile_1
attr bla readingList wert
attr bla setList wert
attr bla userReadings PlotAsSVG:wert.* {\
    my $svgName = AttrVal($name, "SVG_Device_Name", undef);;\
    \
    $FW_RET                 = undef;;\
    $FW_webArgs{dev}        = $svgName;;\
    $FW_webArgs{logdev}     = InternalVal($svgName, "LOGDEVICE", "");;\
    $FW_webArgs{gplotfile}  = InternalVal($svgName, "GPLOTFILE", "");;\
    $FW_webArgs{logfile}    = InternalVal($svgName, "LOGFILE", "CURRENT");; \
    ##$FW_pos{zoom}           = ...;;\
    ##$FW_pos{off}            = ...;;\
    \
    my ($mime, $svgdata) = SVG_showLog("unused");;\
    my ($w, $h) = split(",", AttrVal($svgName, "plotsize", "800,160"));;\
    $svgdata =~ s/<\/svg>/<polyline opacity="0" points="0,0 $w,$h"\/><\/svg>/;;\
    $svgdata =~ s/\.SVGplot\./\./g if (AttrVal($svgName, "plotAsPngFix", 0));;\
    $svgdata = Encode::decode("UTF-8", $svgdata) if (!$unicodeEncoding);;\
    $svgdata =~ s/\n/ /g;;\
    \
    $FW_RET = undef;;\
    return "<html>$svgdata</html>";;\
},\
PlotAsPNG:PlotAsSVG.* {\
    #https://metacpan.org/pod/Image::LibRSVG\
    require Image::LibRSVG;;\
    \
    my $svgdata = ReadingsVal($name, "PlotAsSVG", undef);;\
    $svgdata =~ s/^<html>|<\/html>$//gs;;\
    \
    my $rsvg = new Image::LibRSVG();;\
    $rsvg->loadImageFromString($svgdata);;\
    my $img = $rsvg->getImageBitmap();;\
    \
    my $img_base64 = MIME::Base64::encode_base64($img);;\
    $img_base64 =~ s/\n//g;;\
    return "<html><img src=\"data:image/png;;base64,$img_base64\"></img></html>";;\
},\
PlotAsPNG2:wert.* {\
    my $svgName = AttrVal($name, "SVG_Device_Name", undef);;\
    my $img = plotAsPng($svgName);;\
    $FW_RET = undef;;\
    \
    my $img_base64 = MIME::Base64::encode_base64($img);;\
    $img_base64 =~ s/\n//g;;\
    return "<html><img src=\"data:image/png;;base64,$img_base64\"></img></html>";;\
}
attr bla webCmd wert

So sieht es dann in FHEMWEB aus:
Du darfst diesen Dateianhang nicht ansehen.

Man kann in dem Screenshot erkennen, dass das SVG deutlich schärfer auflöst als die PNG Variante:
Du darfst diesen Dateianhang nicht ansehen.

betateilchen

Zitat von: Torxgewinde am 14 September 2023, 23:05:59In dem SVG Modul gibt es eine nicht sonderlich gut dokumentierte Funktion plotAsPng(). Diese liefert aber immer ein PNG zurück.

Deshalb heisst die Funktion ja auch so...

Das was du willst liefert das Modul InfoPanel seit vielen Jahren out-of-the-box.

Dort gibt es eine Funktion, die plots als SVG in base64 liefert und auf Wunsch auch gleich noch beliebig skaliert.


-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

rudolfkoenig

ZitatIch benötige das SVG eines ein SVG-Device als Reading (für FhemNative um es dort als HTML darstellen zu können).
Warum verwendet man nicht direkt SVG?

betateilchen

Zitat von: rudolfkoenig am 15 September 2023, 10:01:32Warum verwendet man nicht direkt SVG?

Das wäre zu einfach. Einfache Lösungen sind bei FHEM Anwendern selten erwünscht.
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

Torxgewinde

Vielen Dank für die Andeutung, dass es ggf. einfacher geht ein SVG-Plot als Reading zu erhalten.  Die Andeutung durch einen konkreten Codesnippet zu untermauern wäre jetzt der logische und konstruktive nächste Schritt.

betateilchen

Wozu braucht man einen Codeschnipsel, wenn es eine Funktion SVG_showLog() gibt, die einen plot als SVG zurückliefert?
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

rudolfkoenig

ZitatVielen Dank für die Andeutung, dass es ggf. einfacher geht ein SVG-Plot als Reading zu erhalten.
Ich wollte nur wissen, warum man im HTML nicht direkt die SVG Daten anfordert, von einem Reading war nie die Rede.
Wozu braucht man die Daten in einem Reading?

ZitatDie Andeutung durch einen konkreten Codesnippet zu untermauern wäre jetzt der logische und konstruktive nächste Schritt.
Ich wuerde ins HTML Folgendes einfuegen:
<img src="http://fhemhost:8083/fhem/SVG_showLog?dev=<FHEM-SVG-NAME>&logdev=<Log-Device>"/>
wobei <...> zu ersetzen ist.

Torxgewinde

Folgende Gründe:
  • FhemNative baut eine Websocket Verbindung zu FHEMWEB auf
  • holt sich die Daten für HTML aus einem Device->Reading
  • und aktualisiert die Anzeige flackerfrei wenn das Reading aktualisiert wird

@rudolfkoenig: Bei der Methode ist schon viel Ok, aber es aktualisiert sich nicht mehr in FhemNative.
@betateilchen: SVG_ShowLog() ist doch genau das was ich in dem Snippet nutze. Du hattest angedeutet, dass InfoPanel das einfacher macht.

rudolfkoenig

Zitatholt sich die Daten für HTML aus einem Device->Reading
Merkwuerdige Schnittstelle.

Torxgewinde

Zitat von: rudolfkoenig am 15 September 2023, 18:19:22
Zitatholt sich die Daten für HTML aus einem Device->Reading
Merkwuerdige Schnittstelle.
Jo, auch wird dadurch Allowed nicht nochmal getriggert, was mich auch ein wenig beschäftigt hat (https://forum.fhem.de/index.php?topic=134929.0 und https://forum.fhem.de/index.php?topic=134798.0). Vielleicht ist es auch wieder irgendwie doch möglich und nicht so dokumentiert, dass die Info beim Verwender ankommt. Mal gucken wie sich FhemNative im Alltag schlägt, ich bin da noch in der Evaluierung. Was mich motiviert ist, dass die App recht schick und performant ist wenn es läuft...

@Rudolf: Dir schonmal danke!

Syrex-o

Zitat von: rudolfkoenig am 15 September 2023, 18:19:22
Zitatholt sich die Daten für HTML aus einem Device->Reading
Merkwuerdige Schnittstelle.

Naja... so oder so ähnlich.
SVG Plots sollten problemlos per IFrame Komponente oder Image Komponente eingebunden werden können.

VG

Torxgewinde

Zitat von: Syrex-o am 15 September 2023, 20:36:05SVG Plots sollten problemlos per IFrame Komponente oder Image Komponente eingebunden werden können.
Per Reading geht ja jetzt, vielleicht ist allerdings der Weg über IFrame / Image einfacher - ich schau' es mir nochmal an. Danke für den Hinweis!

Edit #1:
Ein Vorteil, den ich beim Experimentieren bemerke ist der dass die SVG/PNG in Readings logischerweise direkt vorgehalten/gecached sind und damit praktisch sofort zur Verfügung stehen. Das kann man sicher noch gebrauchen...

Torxgewinde

Hallo,
Ich habe ein Reading ergänzt, und auch mit "BlockingCall" so umgestellt, dass die Berechnung in einem Fork-Prozess von FHEM läuft und damit FHEMWEB etc. nicht auf die Berechnung des SVG warten müssen. Das Berechnen von SVGs löst FHEMWEB zwar auch mit einem Fork, aber um daran zu kommen wäre der Webzugriff notwendig und BlockingCall bringt es auch ins Ziel.

@betateilchen: Ich habe immer noch Interesse an dem Weg über InfoPanel und schaue es mir dann gerne an.


Hier das Device, die unterschiedlichen Methoden an einnen Plot als Reading zu gelangen kann man mit:
  • set bla wert Blocking
  • set bla wert SVG
  • set bla wert PNG
  • set bla wert PNG2
ausprobieren.

defmod bla dummy
attr bla userattr SVG_Device_Name
attr bla SVG_Device_Name SVG_DBLog_Photovoltaik_1
attr bla readingList wert
attr bla setList wert:SVG,PNG,PNG2,Blocking
attr bla userReadings PlotAsSVG2:wert:.Blocking\b {\
    no warnings 'redefine';;\
    sub func1($) {\
        my ($string) = @_;;\
        my $svgName = $string;;\
        \
        $FW_RET                 = undef;;\
        $FW_webArgs{dev}        = $svgName;;\
        $FW_webArgs{logdev}     = InternalVal($svgName, "LOGDEVICE", "");;\
        $FW_webArgs{gplotfile}  = InternalVal($svgName, "GPLOTFILE", "");;\
        $FW_webArgs{logfile}    = InternalVal($svgName, "LOGFILE", "CURRENT");; \
        \
        my ($mime, $svgdata) = SVG_showLog("unused");;\
        my ($w, $h) = split(",", AttrVal($svgName, "plotsize", "800,160"));;\
        $svgdata =~ s/<\/svg>/<polyline opacity="0" points="0,0 $w,$h"\/><\/svg>/;;\
        $svgdata =~ s/\.SVGplot\./\./g if (AttrVal($svgName, "plotAsPngFix", 0));;\
        $svgdata = Encode::decode("UTF-8", $svgdata) if (!$unicodeEncoding);;\
        $svgdata =~ s/\n/ /g;;\
        \
        $FW_RET = undef;;\
        \
        $svgdata = MIME::Base64::encode_base64($svgdata);;\
        $svgdata =~ s/\n//g;;\
        return "$svgdata";;\
    };;\
    sub func2($) {\
        my ($result) = @_;;\
        my $hash = $defs{$name};;\
        $result = MIME::Base64::decode_base64($result);;\
        #fhem("setreading $name $reading $result");;\
        readingsSingleUpdate($hash, $reading, "<html>".$result."</html>", 1);;\
    };;\
    use warnings 'redefine';;\
    \
    my $svgName = AttrVal($name, "SVG_Device_Name", undef);;\
    \
    my $result = BlockingCall("func1", "$svgName", "func2", 90);;\
    \
    return "Warten...";;\
},\
PlotAsSVG:wert:.SVG {\
    my $svgName = AttrVal($name, "SVG_Device_Name", undef);;\
    \
    $FW_RET                 = undef;;\
    $FW_webArgs{dev}        = $svgName;;\
    $FW_webArgs{logdev}     = InternalVal($svgName, "LOGDEVICE", "");;\
    $FW_webArgs{gplotfile}  = InternalVal($svgName, "GPLOTFILE", "");;\
    $FW_webArgs{logfile}    = InternalVal($svgName, "LOGFILE", "CURRENT");; \
    ##$FW_pos{zoom}           = ...;;\
    ##$FW_pos{off}            = ...;;\
    \
    my ($mime, $svgdata) = SVG_showLog("unused");;\
    my ($w, $h) = split(",", AttrVal($svgName, "plotsize", "800,160"));;\
    $svgdata =~ s/<\/svg>/<polyline opacity="0" points="0,0 $w,$h"\/><\/svg>/;;\
    $svgdata =~ s/\.SVGplot\./\./g if (AttrVal($svgName, "plotAsPngFix", 0));;\
    $svgdata = Encode::decode("UTF-8", $svgdata) if (!$unicodeEncoding);;\
    $svgdata =~ s/\n/ /g;;\
    \
    $FW_RET = undef;;\
    return "<html>$svgdata</html>";;\
},\
PlotAsPNG:wert:.PNG\b {\
    #https://metacpan.org/pod/Image::LibRSVG\
    require Image::LibRSVG;;\
    \
    my $svgdata = ReadingsVal($name, "PlotAsSVG", undef);;\
    $svgdata =~ s/^<html>|<\/html>$//gs;;\
    \
    my $rsvg = new Image::LibRSVG();;\
    $rsvg->loadImageFromString($svgdata);;\
    my $img = $rsvg->getImageBitmap();;\
    \
    my $img_base64 = MIME::Base64::encode_base64($img);;\
    $img_base64 =~ s/\n//g;;\
    return "<html><img src=\"data:image/png;;base64,$img_base64\"></img></html>";;\
},\
PlotAsPNG2:wert:.PNG2 {\
    my $svgName = AttrVal($name, "SVG_Device_Name", undef);;\
    \
    my $img = plotAsPng($svgName);;\
    $FW_RET = undef;;\
    \
    my $img_base64 = MIME::Base64::encode_base64($img);;\
    $img_base64 =~ s/\n//g;;\
    return "<html><img src=\"data:image/png;;base64,$img_base64\"></img></html>";;\
}
attr bla webCmd wert

Ein Define eines Dummy für flackerfreies Aktualisieren des SVG Plots wäre dann diese eingekürzte Version:
defmod bla dummy
attr bla userattr SVG_Device_Name
attr bla SVG_Device_Name SVG_DBLog_Photovoltaik_1
attr bla readingList wert
attr bla setList wert:Blocking
attr bla userReadings PlotAsSVG:wert:.Blocking\b {\
no warnings 'redefine';;\
sub func1($) {\
my ($svgName) = @_;;\
\
$FW_RET                 = undef;;\
$FW_webArgs{dev}        = $svgName;;\
$FW_webArgs{logdev}     = InternalVal($svgName, "LOGDEVICE", "");;\
$FW_webArgs{gplotfile}  = InternalVal($svgName, "GPLOTFILE", "");;\
$FW_webArgs{logfile}    = InternalVal($svgName, "LOGFILE", "CURRENT");; \
\
my ($mime, $svgdata) = SVG_showLog("unused");;\
my ($w, $h) = split(",", AttrVal($svgName, "plotsize", "800,160"));;\
$svgdata =~ s/<\/svg>/<polyline opacity="0" points="0,0 $w,$h"\/><\/svg>/;;\
$svgdata =~ s/\.SVGplot\./\./g if (AttrVal($svgName, "plotAsPngFix", 0));;\
$svgdata = Encode::decode("UTF-8", $svgdata) if (!$unicodeEncoding);;\
$svgdata =~ s/\n/ /g;;\
\
$FW_RET = undef;;\
\
$svgdata = MIME::Base64::encode_base64($svgdata);;\
$svgdata =~ s/\n//g;;\
return "$svgdata";;\
};;\
sub func2($) {\
my ($result) = @_;;\
my $hash = $defs{$name};;\
$result = MIME::Base64::decode_base64($result);;\
readingsSingleUpdate($hash, $reading, "<html>".$result."</html>", 1);;\
};;\
use warnings 'redefine';;\
\
my $svgName = AttrVal($name, "SVG_Device_Name", undef);;\
\
my $result = BlockingCall("func1", "$svgName", "func2", 90);;\
\
return;;\
}
attr bla webCmd wert