Hauptmenü

neue svg-Funktion: card

Begonnen von Damian, 04 April 2021, 12:58:15

Vorheriges Thema - Nächstes Thema

Damian

Zitat von: xenos1984 am 17 Januar 2022, 23:03:50
Wie würde man denn daraus die SVG-Grafik extrahieren, um sie zu konvertieren?

In RSS habe ich es mal testweise so eingebunden:


define di_test DOIF {}
attr di_test DOIF_Readings test:[device:reading:144col6]


RSS Layout:


img   1   1   1 svg data {ui_Table::card(ReadingsVal("di_test","test",0),"Temperature","temp_temperature",-35,35,240,0,"°C",undef,1,"240,,,,,,200")}


Man müsste schauen, wie die Programme auf die Statuszeile eines Devices zugreifen. Ich habe es damals mit Floorplan erfolgreich getestet, was auch über die Statuszeile eines Devices funktioniert.

Das DOIF-Device selbst sieht ja so aus:

defmod di_cardtest DOIF {}
attr di_cardtest uiState {package ui_Table}\
card([mydevice:reading:col],...)
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

Sie rufen offenbar intern auch die summaryFn-Funktion des jeweiligen Moduls auf. Das ist ja die Funktion, die für die Darstellung des Status im jeweiligen Modul zuständig ist. Wenn es nur um eine Card-Darstellung geht und nicht um eine ganze ui-Tabelle, dann kann man auch deinen Vorschlag gut nutzen.

Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

Also den HTML-Code der ganzen Tabelle (hier in di_mytable-DOF-Device) kann man erhalten durch den Aufruf:

DOIF_detailFn(undef,"di_mytable",undef,undef)

und falls sie im Status ist mit

DOIF_summaryFn(undef,"di_mytable",undef,undef)

RSS wäre dann z. B.

img   1   1   1 svg data {DOIF_summaryFn(undef,"di_mytable",undef,undef)}

Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Adimarantis

Damit hatte ich keinen Erfolg (wobei das überhaupt meine ersten Gehversuche mit dem RSS Modul waren).
Beim rumprobieren habe ich es sogar geschafft mein FHEM aufzuhängen.

Selbst wenn das so funktioniert, ist es ja doch recht aufwändig. Wenn ich mir den Code für plotAsPng im SVG Modul anschaue, dann sieht das erstmal gar nicht kompliziert aus. Macht ja weitgehend eine Library die einfach die SVG Daten braucht. Die sollten ja jeweils bereits vorliegen?

@Damian: Wäre es nicht analog denkbar ein "uitableAsPng" zu bauen?
Aufruf z.B. uitableAsPng("DI_uitable","card",2) , wobei "card" und 2 optional wäre und ggf. den ganzen uitable (ganz ohne) oder alle Objekte vom Typ "card" - oder eben die zweite Card (oder alternativ über einen Namen) als Stream zurückgibt.

Wäre halt ziemlich Klasse und würde es ermöglichen die Elemente flexibel einzusetzen.
Raspberry 4 + HM-MOD-RPI-PCB (pivCCU) + RfxTrx433XL + 2xRaspberry 1
Module: 50_Signalbot, 52_I2C_ADS1x1x , 58_RPI_1Wire, (50_SPI_MAX31865)

Damian

#229
Zitat von: Adimarantis am 18 Januar 2022, 22:53:56
Damit hatte ich keinen Erfolg (wobei das überhaupt meine ersten Gehversuche mit dem RSS Modul waren).
Beim rumprobieren habe ich es sogar geschafft mein FHEM aufzuhängen.

Selbst wenn das so funktioniert, ist es ja doch recht aufwändig. Wenn ich mir den Code für plotAsPng im SVG Modul anschaue, dann sieht das erstmal gar nicht kompliziert aus. Macht ja weitgehend eine Library die einfach die SVG Daten braucht. Die sollten ja jeweils bereits vorliegen?

@Damian: Wäre es nicht analog denkbar ein "uitableAsPng" zu bauen?
Aufruf z.B. uitableAsPng("DI_uitable","card",2) , wobei "card" und 2 optional wäre und ggf. den ganzen uitable (ganz ohne) oder alle Objekte vom Typ "card" - oder eben die zweite Card (oder alternativ über einen Namen) als Stream zurückgibt.

Wäre halt ziemlich Klasse und würde es ermöglichen die Elemente flexibel einzusetzen.

Was soll "DI_uitable" sein? Was soll das Ergebnis sein? HTML-Code, jpeg, png ...

Bei Card und Co. handelt es sich um reine Perl-Funktionen, daher weiß das Modul nicht, was es da aufruft, deswegen gib es auch keine Typen als Unterscheidungskriterium.

Man muss wissen, dass Card, im Gegensatz zu anderen SVG-Funktionen, wie z. B. ring, ein DOIF-Device zum Sammeln von Daten benötigt. Daher ist ein einfacher Funktionsaufruf der Perl-Funktion immer an ein Objekt, welches in einem DOIF-Device definiert werden muss [<device>:<reading>:col] gebunden.

Ich finde, dass man mit HTML-Code schon ziemlich flexibel, effizient und speicherplatzschonend in der Ausgabe ist, wenn man es als reines png haben will, dann sollte man entsprechende Tools zur Wandlung benutzen
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Adimarantis

Ich habe jetzt weiter rumexperimentiert und hinbekommen, was ich wollte - nur mache ich da sehr viele Annahmen darüber wie DOIF intern funktioniert und jede Änderung könnte das ganze wieder schmeissen. Ist also eher ein Proof of Concept.

Ich kann mir jetzt per
set SignalBot send @Joerg &({uiAsPng(\"DI_collect\",3)})
zum Beispiel ein Bild der vierten "card" die im DOIF "DI_collect" als uiTable definiert ist aufs Handy schicken.
Zum Umwandeln des SVG in PNG verwende ich die selbe Library die auch plotAsPng vom SVG Modul verwendet.
Die verträgt allerdings einige HTML Konstrukte nicht, daher rufe ich die "card" Funktion direkt auf (und hole mir die mit Parametern aus den internen Readings) und selbst da muss ich noch was per regexp entfernen.

Mein (verpäteter) Weihnachtswunsch wäre jetzt eine öffentliche API die mir das pure SVG (oder gleich das PNG) liefert. Ich verwende jetzt als Argument den Index, schöner wäre wahrscheinlich der Name der Überschrift oder des Readings. Ich matche jetzt explizit auf "card" - sollte aber dann wohl für beliebige Widgets funktionieren.

Dazu hab ich mir in myUitls folgendes definiert:
sub
uiAsPng($$)
{
  my ($DoifName,$id) = @_;
  my (@webs, $mimetype, $svgdata, $rsvg, $pngImg);
 
  my $hash = $defs{$DoifName};
  if (!defined $hash) {
return "$DoifName not found";
  }
 
  if (!defined $hash->{'uiTable'}{table} ) {
return "DOIF has not uiTable defined";
}

my $table=$hash->{'uiTable'}{table}{0}{$id}{0}{0};

if (!defined $table) {
return "uiTable with ID $id not found";
}

$table =~ /.*(card\(.*\)),""\)/;

my $card="package ui_Table;"."$1".";";

if (defined $card && $card ne "") {
$svgdata=eval($card);
print $@;
} else {
return "uiTable format error (no card)";
}

if (! defined $svgdata) {
return "Error getting data from card";
}
$svgdata='<?xml version="1.0" encoding="UTF-8"?> <svg>'.$svgdata.'</svg>';

eval {
require Image::LibRSVG;
$rsvg = new Image::LibRSVG();
$rsvg->loadImageFromString($svgdata);
$pngImg = $rsvg->getImageBitmap();
};
if (defined $pngImg && $pngImg ne "") {
return $pngImg if $pngImg;
}
return "Error retrieving PNG";
}
Raspberry 4 + HM-MOD-RPI-PCB (pivCCU) + RfxTrx433XL + 2xRaspberry 1
Module: 50_Signalbot, 52_I2C_ADS1x1x , 58_RPI_1Wire, (50_SPI_MAX31865)

Damian

OK. Benutzt du denn bei dir in FHEM DOIFs mit uiTable mit mehreren Elementen?
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Adimarantis

Meine (bisher einzige) Definition schaut so aus:
{package ui_Table;}
card([out_temp:temperature:col24],"Außen","out_temp",-10,30,undef,undef,"°C",\&temp_hue,undef)|
card([WS4500_WIND_1:wind_speed:col3d],"Wind",[WS4500_WIND_1:wind_avspeed] > 0 ? "wind".",1,0,0,".[WS4500_WIND_1:degree]:"no_wind",0,30,90,30,"km/h",undef,1)|
card([Tankstelle:Diesel:col7d],"Diesel,fill:darkorange","fuel","1.30","1.60",120,0,"Diesel €",undef,"2",",,1")|
card([Tankstelle:SuperE10:col7d],"Super E10,fill:darkorange","fuel","1.40","1.70",120,0,"E10 €",undef,"2",",,1")

und mir ist klar, dass die Indexe in deiner internen Tabelle nur genau für die Variante funktionieren - ich habe mich jetzt nicht damit beschäftigt wie die ganzen Ebenen verschachtelt sind. Daher wäre ja auch statt Index ein Namensmatching besser (z.B. dann eben "Super E10") - mir ging es aber erstmal nur darum das überhaupt irgendwie hinzukriegen.
Raspberry 4 + HM-MOD-RPI-PCB (pivCCU) + RfxTrx433XL + 2xRaspberry 1
Module: 50_Signalbot, 52_I2C_ADS1x1x , 58_RPI_1Wire, (50_SPI_MAX31865)

Damian

#233
Ich würde einen anderen Ansatz wählen. Wenn man weiß, dass man irgendwo außerhalb des DOIF Moduls zusätzlich den HTML-Code bestimmter Card-Funktion benötigt, dann würde ich folgende Vorgehensweise empfehlen - hier am Beispiel deiner Definition für die Darstellung des Sprits:

aus:

{package ui_Table;}
...
card([Tankstelle:Diesel:col7d],"Diesel,fill:darkorange","fuel","1.30","1.60",120,0,"Diesel €",undef,"2",",,1")|
card([Tankstelle:SuperE10:col7d],"Super E10,fill:darkorange","fuel","1.40","1.70",120,0,"E10 €",undef,"2",",,1")
...


definiert man passende Readings für die benötigten Daten:

attr DOIF_Readings Diesel:[Tankstelle:Diesel:col7d], SuperE10:[Tankstelle:SuperE10:col7d]

die Tabelle wird dann auf die eigenen Readings angepasst:

{package ui_Table;}
...
card([$SELF:Diesel],"Diesel,fill:darkorange","fuel","1.30","1.60",120,0,"Diesel €",undef,"2",",,1")|
card([$SELF:SuperE10],"Super E10,fill:darkorange","fuel","1.40","1.70",120,0,"E10 €",undef,"2",",,1")
...



jetzt kann man an einer beliebigen Stelle in FHEM die gewünschte Card-Funktion aufrufen, hier z. B. mit:

ui_Table::card(ReadingsVal("<Name des DOIFs>","Diesel",0),"Diesel,fill:darkorange","fuel","1.30","1.60",120,0,"Diesel €",undef,"2",",,1")

Der zusätzliche Vorteil ist, dass man z. B. die Größe der Grafik oder sonstige Parameter für den eigenen Output ändern kann, trotzdem werden die benötigten Daten nur einmal im DOIF gesammelt und verbleiben auch dort. Das ursprüngliche Layout der eigenen Tabelle im DOIF bleibt dabei unverändert.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Adimarantis

Hallo Damian,

danke. Ich hab versucht dein Beispiel nachzubauen, aber irgendwie scheitere ich dann dabei die card Funktion aufzurufen
Can't use string ("0") as a HASH ref while "strict refs" in use at ./FHEM/98_DOIF.pm line 4808.
Ich habe es allerdings zum Testen einfach in die Eingabezeile eingebettet {....;;} - vielleicht wird da was komisch escaped.

Grundsätzlich stimmt es schon, dass diese Variante zumindest schon mal nicht mehr von internen Datenstrukturen abhängig ist und eine abweichende Darstellung zulässt.
Mich persönlich stört dabei allerdings, dass man eben zweimal "die selbe" Konfigration braucht und diese noch dazu "hardcoded" z.B. in myUtils oder entsprechenden Perl Code in ein DOIF muss. Auch finde ich es ja gerade gut, dass mir das DOIF quasi einen Preview zeigt. Eine Zoomfunktion hätte libsvg, das liesse sich leicht als optionalen Parameter einflechten.

In a nutshell: Eine Funktion in DOIF mit der ich aus einem anderen Modul (würde ich dann z.B. in den SignalBot einbauen) einfach pure SVG (oder sogar PNG) Daten holen kann, fände ich benutzerfreundlicher und wäre auch für nicht so versierte Anwender eine machbare Option.
Die entsprechende Funktion sollte am Besten als Parameter einfach den Namen des DOIFs und den Namen des Readings nehmen.
Ich denke so eine Funktion würde auch ganz andere Anwendungsfälle erleichtern, z.B. die DOIF Widgets einfacher in FTUI einzubinden.

Aber wie gesagt ist nur ein Wunsch und ich bedanke mich auf jeden Fall schon mal für alle Hilfestellungen. Wahrscheinlich bleibe ich vorerst bei meiner Lösung (evtl. etwas verfeinert, z.B. könnte ich ja den Hash durchiterieren und eben auf den Readingsnamen matchen statt Annahmen über den Index zu machen).

Gruß,
Jörg
Raspberry 4 + HM-MOD-RPI-PCB (pivCCU) + RfxTrx433XL + 2xRaspberry 1
Module: 50_Signalbot, 52_I2C_ADS1x1x , 58_RPI_1Wire, (50_SPI_MAX31865)

Damian

Zitat von: Adimarantis am 23 Januar 2022, 11:16:36

Can't use string ("0") as a HASH ref while "strict refs" in use at ./FHEM/98_DOIF.pm line 4808.


Vermutlich hast du ein ReadingsVal angegeben, für ein nicht vorhandenes Reading mit dem Defaultwert 0, 0 ist dann keine Hash-Referenz, die sonst in dem Reading erwartet wird.

Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

#236
ZitatMich persönlich stört dabei allerdings, dass man eben zweimal "die selbe" Konfigration braucht und diese noch dazu "hardcoded" z.B. in myUtils oder entsprechenden Perl Code in ein DOIF muss.

Die interne Struktur ist eben nicht dafür ausgelegt nach bestimmten Datenstrukturen zu suchen, intern wird allerdings aufgrund eines triggernden Devices und Readings eine Zuordnung zu einzelnen Zellen gemacht, aber das ist ja wieder etwas anders.

Auch den gleichen Code kann man minimieren, in dem man eine Perlfunktion definiert, die genau den card-Aufruf tätigt, dann braucht man nur diese in der Tabelle, wie auch beim externen Aufruf nutzen. Klar, man muss in diesen Fällen im Vorfeld aktiv werden.

Edit: Deswegen ist auch die Suche nach "card" nicht zielführend, da sie in einer anderen Perlfunktion stecken kann.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Adimarantis

Zitat von: Damian am 23 Januar 2022, 12:40:07
Vermutlich hast du ein ReadingsVal angegeben, für ein nicht vorhandenes Reading mit dem Defaultwert 0, 0 ist dann keine Hash-Referenz, die sonst in dem Reading erwartet wird.
Ja, richtig, da hatte ich mich wohl vertippt. Kaum macht man es richtig, geht es auch.
ZitatEdit: Deswegen ist auch die Suche nach "card" nicht zielführend, da sie in einer anderen Perlfunktion stecken kann.
Ja, meine "card" regexp war jetzt beim reverse engineering einfach quick&dirty um den DOIF_Widget Teil (der ja noch das <div> hinzufügt) wegzufiltern.

Das könnte man sicher sauberer machen, bis hin zur Verwendung einer XML Library mit der man aus dem Ergebnis einfach die störenden Tags entfernt und den gewünschten Abschnitt rausfiltert.

Wie gesagt. Mir reichts das erstmal so und wenn künftige DOIFs was ändern, muss ichs halt anpassen.

Vielleicht packt dich aber ja doch irgendwann mal Lust&Laune eine einfache und stabile Methode zur Verfügung zu stellen :)
Raspberry 4 + HM-MOD-RPI-PCB (pivCCU) + RfxTrx433XL + 2xRaspberry 1
Module: 50_Signalbot, 52_I2C_ADS1x1x , 58_RPI_1Wire, (50_SPI_MAX31865)

Adimarantis

Ich habe meinen Code noch etwas verfeinert, so dass er jetzt hoffentlich relativ robust gegen DOIF Änderungen sein sollte und mal in eine Testversion meines Signalbot Moduls eingebaut:
https://forum.fhem.de/index.php/topic,118370.msg1204008.html#msg1204008

Ist jetzt flexibel über (hoffentlich alle) verschiedenen Widgets und über Index, Device und/oder Reading adressierbar.
Bei Interesse - die Funktion heisst: Signalbot_DOIFAsPng

Jörg
Raspberry 4 + HM-MOD-RPI-PCB (pivCCU) + RfxTrx433XL + 2xRaspberry 1
Module: 50_Signalbot, 52_I2C_ADS1x1x , 58_RPI_1Wire, (50_SPI_MAX31865)

Damian

Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF