Mini-Dashboard für PV-Anlage: Unterschied zwischen den Versionen
Seite wurde neu angelegt. |
Teile geschrieben |
||
| Zeile 1: | Zeile 1: | ||
== Sinn und Zweck == | == Sinn und Zweck == | ||
Aus Fernost gab es gerade sehr billig ein Gadget in Form eines Mini-Fernsehers, der im wesentlichen aus einem ESP32 mit einem 240x240 Pixel großen Farb-LCD besteht: das GIFTV von GeekMagic. Ich habe mir damit ein Mini-Dashboard für meine Photovoltaik-Anlage gebaut. | [[Datei:Sensorfeed2.jpg|mini|links]]Aus Fernost gab es gerade sehr billig ein Gadget in Form eines Mini-Fernsehers, der im wesentlichen aus einem ESP32 mit einem 240x240 Pixel großen Farb-LCD besteht: das GIFTV von GeekMagic. Ich habe mir damit ein Mini-Dashboard für meine Photovoltaik-Anlage gebaut. | ||
[[ | |||
== Komponenten == | |||
=== Übersicht === | |||
Das Dashboard wird als JPG-Bild mit dem Modul RSS on-the-fly erstellt. Ein at-Kommando ruft jede Minute ein Bild vom Modul ab und schiebt es über eine Perl-Funktion auf das GIFTV. Das GIFTV arbeitet dabei mit seiner Standard-Firmware im Bilderalbum-Modus. | |||
Die Werte für das Dashboard stammen aus einem readingsProxy (neue Version ab April 2026), der die angezeigten Werte aus mehreren Geräten (SMA Energy Meter, SMA Inverter, Electricity Calculator) zusammenführt. Die Anzeige im Dashboard bedient sich der Widgets von DOIF (siehe [[DOIF/uiTable Schnelleinstieg]]). | |||
=== readingsProxy === | |||
=== RSS === | |||
==== Definition ==== | |||
<code>define sensorfeed2 RSS jpg has-1.example.com /opt/fhem/conf/sensorfeed2.layout | |||
attr sensorfeed2 bg /opt/fhem/PictureFrame/sensorfeed2 | |||
attr sensorfeed2 size 240x240</code> | |||
Das KI-generierte Hintergrundbild /opt/fhem/PictureFrame/sensorfeed2 dient als Symbolbild. | |||
==== Layout ==== | |||
Das Modul 98_DOIF.pm wird benötigt, damit RSS die darin enthaltenen Widgets verwenden kann. Wenn man kein DOIF benutzt, muss man das Modul mit <code>require 98_DOIF.pm</code> im Perl-Kode laden - das erfolgt unprätentiös in dem automatisch geladenen 99_GIFTV.pm. | |||
/opt/fhem/conf/sensorfeed2.layout | |||
<code> | |||
# ------------------ | |||
# font | |||
# ------------------ | |||
font /opt/fhem/conf/DroidSans.ttf | |||
# ------------------ | |||
# bottom left corner | |||
# ------------------ | |||
# | |||
# date/time | |||
# | |||
rgb "c0c0c0" | |||
pt 10 | |||
date 10 230 | |||
time 100 230 | |||
# | |||
# links oben: Erzeugung | |||
# | |||
img 10 10 100w svg data { ui_Table::ring2( \ | |||
main::ReadingsVal('aggr.PV','PVPowerSourceW',0.0), 0.0, 4.0, 0, 240, "PV", 100, undef, "0,,,W", \ | |||
main::ReadingsVal('aggr.PV','PVEnergySourcekWh',0.0), 0, 50, 0, 120, undef, undef, "0,,,kWh") \ | |||
} | |||
# | |||
# rechts oben: Verbrauch | |||
# | |||
img 130 10 100w svg data { ui_Table::ring2( \ | |||
main::ReadingsVal('aggr.PV','HousePowerSinkW',0.0), 0.0, 4.0, 0, 240, "Haus", 100, undef, "0,,,W", \ | |||
main::ReadingsVal('aggr.PV','HouseEnergySinkkWh',0.0), 0, 50, 0, 120, undef, undef, "0,,,kWh") \ | |||
} | |||
# | |||
# links unten: Netzbezug (+)/Netzeinspeisung (-) | |||
# | |||
img 10 130 100w svg data { ui_Table::ring2( \ | |||
main::ReadingsVal('aggr.PV','GridPowerW',0.0), -4.0, 4.0, 0, 240, "Netz", 100, undef, "0,,,W", \ | |||
main::ReadingsVal('aggr.PV','GridEnergySourcekWh',0.0), 0, 100, 0, 120, undef, undef, "0,,,kWh") \ | |||
} | |||
# | |||
# rechts unten: Batterie | |||
# | |||
img 130 130 100w svg data { ui_Table::ring2( \ | |||
main::ReadingsVal('aggr.PV','BatteryPowerW',0.0), -4.0, 4.0, 0, 240, "Batt", 100, undef, "0,,,W", \ | |||
main::ReadingsVal('aggr.PV','BatteryPercent',0.0), 0, 100, 0, 120, undef, undef, "0,,,%") \ | |||
} | |||
</code> | |||
=== Bespielung des GIFTV === | |||
==== at ==== | |||
Der at-Befehl schiebt durch Aufruf von push2giftv() in Sekunde 1 jeder Minute das Bild vom RSS-Gerät sensorfeed2 auf zwei im Haus verteilte GIFTVs. | |||
<code>define at.sensorfeed at +*00:01:00 { push2giftv( { "sensorfeed2" => ["giftv-1.example.com","giftv-3.example.com"] } ) } | |||
attr at.sensorfeed alignTime 01:01:01</code> | |||
==== push2giftv() ==== | |||
Es wird ein automatisch von FHEM geladenes Modul 99_GIFTV.pm mit folgendem Inhalt im Order FHEM (unter Linux üblicherweise <code>/opt/fhem/FHEM/99_GIFTV.pm</code>) bei den anderen Modulen angelegt. | |||
<code>require '98_DOIF.pm'; | |||
sub push2giftv { | |||
my $uploadsref = shift; | |||
# wrap code in eval to avoid lethal errors crashing FHEM | |||
eval { | |||
my $type = "jpg"; | |||
while( my ($rssname, $hostnamesref) = each(%$uploadsref) ) { | |||
# make RSS generate the image | |||
my ($mimetype, $image) = RSS_returnIMG($rssname, $type); | |||
my $targetfilename = "$rssname.$type"; | |||
foreach my $hostname (@$hostnamesref) { | |||
# parameters for GIFTV | |||
my $url = "http://$hostname/doUpload?dir=%2Fimage%2F"; | |||
my $param = { | |||
loglevel => 4, | |||
url => $url, | |||
method => "POST", | |||
hideurl => 0, | |||
noshutdown => 0, | |||
callback => sub($$$) { Log3 $name, 1,"ERR:$_[1] DATA:".length($_[2]) }, | |||
}; | |||
# add image as multipart form data | |||
HttpUtils_AddMultipartData($param, $image, | |||
{"Content-Disposition" => "form-data; name=\"file\"; filename=\"$targetfilename\"", "Content-Type" => $mimetype } | |||
); | |||
# upload to GIFTV $hostname | |||
HttpUtils_NonblockingGet($param); | |||
} | |||
} | |||
}; | |||
if ($@) { | |||
my $msg = $@; | |||
chomp $msg; | |||
Log3 $name, 2, $msg; | |||
} | |||
} | |||
</code> | |||
Das Modul 98_DOIF.pm wird benötigt, damit RSS die darin enthaltenen Widgets verwenden kann. | |||
push2giftv() nimmt als Argument ein Hash, das jedem RSS-Gerät (hier sensorfeed2) ein Array mit den Hostnamen oder IP-Adressen der GIFTVs zuordnet, auf die das Bild vom RSS-Gerät geschoben wird. | |||
Beispielaufruf, um mehrere Dashboards (sensorfeed1 und sensorfeed2) auf zwei GIFTVs zu schieben: | |||
<code>push2giftv( { "sensorfeed1" => ["giftv-1.example.com","giftv-3.example.com"], "sensorfeed2" => ["giftv-1.example.com","giftv-3.example.com"] } ) }<code> | |||
Das GIFTV zeigt dann die beiden Dashboards im Wechsel an. | |||
Version vom 29. März 2026, 17:23 Uhr
Sinn und Zweck

Aus Fernost gab es gerade sehr billig ein Gadget in Form eines Mini-Fernsehers, der im wesentlichen aus einem ESP32 mit einem 240x240 Pixel großen Farb-LCD besteht: das GIFTV von GeekMagic. Ich habe mir damit ein Mini-Dashboard für meine Photovoltaik-Anlage gebaut.
Komponenten
Übersicht
Das Dashboard wird als JPG-Bild mit dem Modul RSS on-the-fly erstellt. Ein at-Kommando ruft jede Minute ein Bild vom Modul ab und schiebt es über eine Perl-Funktion auf das GIFTV. Das GIFTV arbeitet dabei mit seiner Standard-Firmware im Bilderalbum-Modus.
Die Werte für das Dashboard stammen aus einem readingsProxy (neue Version ab April 2026), der die angezeigten Werte aus mehreren Geräten (SMA Energy Meter, SMA Inverter, Electricity Calculator) zusammenführt. Die Anzeige im Dashboard bedient sich der Widgets von DOIF (siehe DOIF/uiTable Schnelleinstieg).
readingsProxy
RSS
Definition
define sensorfeed2 RSS jpg has-1.example.com /opt/fhem/conf/sensorfeed2.layout
attr sensorfeed2 bg /opt/fhem/PictureFrame/sensorfeed2
attr sensorfeed2 size 240x240
Das KI-generierte Hintergrundbild /opt/fhem/PictureFrame/sensorfeed2 dient als Symbolbild.
Layout
Das Modul 98_DOIF.pm wird benötigt, damit RSS die darin enthaltenen Widgets verwenden kann. Wenn man kein DOIF benutzt, muss man das Modul mit require 98_DOIF.pm im Perl-Kode laden - das erfolgt unprätentiös in dem automatisch geladenen 99_GIFTV.pm.
/opt/fhem/conf/sensorfeed2.layout
- ------------------
- font
- ------------------
font /opt/fhem/conf/DroidSans.ttf
- ------------------
- bottom left corner
- ------------------
- date/time
rgb "c0c0c0"
pt 10
date 10 230
time 100 230
- links oben: Erzeugung
img 10 10 100w svg data { ui_Table::ring2( \
main::ReadingsVal('aggr.PV','PVPowerSourceW',0.0), 0.0, 4.0, 0, 240, "PV", 100, undef, "0,,,W", \
main::ReadingsVal('aggr.PV','PVEnergySourcekWh',0.0), 0, 50, 0, 120, undef, undef, "0,,,kWh") \
}
- rechts oben: Verbrauch
img 130 10 100w svg data { ui_Table::ring2( \
main::ReadingsVal('aggr.PV','HousePowerSinkW',0.0), 0.0, 4.0, 0, 240, "Haus", 100, undef, "0,,,W", \
main::ReadingsVal('aggr.PV','HouseEnergySinkkWh',0.0), 0, 50, 0, 120, undef, undef, "0,,,kWh") \
}
- links unten: Netzbezug (+)/Netzeinspeisung (-)
img 10 130 100w svg data { ui_Table::ring2( \
main::ReadingsVal('aggr.PV','GridPowerW',0.0), -4.0, 4.0, 0, 240, "Netz", 100, undef, "0,,,W", \
main::ReadingsVal('aggr.PV','GridEnergySourcekWh',0.0), 0, 100, 0, 120, undef, undef, "0,,,kWh") \
}
- rechts unten: Batterie
img 130 130 100w svg data { ui_Table::ring2( \
main::ReadingsVal('aggr.PV','BatteryPowerW',0.0), -4.0, 4.0, 0, 240, "Batt", 100, undef, "0,,,W", \
main::ReadingsVal('aggr.PV','BatteryPercent',0.0), 0, 100, 0, 120, undef, undef, "0,,,%") \
}
Bespielung des GIFTV
at
Der at-Befehl schiebt durch Aufruf von push2giftv() in Sekunde 1 jeder Minute das Bild vom RSS-Gerät sensorfeed2 auf zwei im Haus verteilte GIFTVs.
define at.sensorfeed at +*00:01:00 { push2giftv( { "sensorfeed2" => ["giftv-1.example.com","giftv-3.example.com"] } ) }
attr at.sensorfeed alignTime 01:01:01
push2giftv()
Es wird ein automatisch von FHEM geladenes Modul 99_GIFTV.pm mit folgendem Inhalt im Order FHEM (unter Linux üblicherweise /opt/fhem/FHEM/99_GIFTV.pm) bei den anderen Modulen angelegt.
require '98_DOIF.pm';
sub push2giftv {
my $uploadsref = shift;
# wrap code in eval to avoid lethal errors crashing FHEM
eval {
my $type = "jpg";
while( my ($rssname, $hostnamesref) = each(%$uploadsref) ) {
# make RSS generate the image
my ($mimetype, $image) = RSS_returnIMG($rssname, $type);
my $targetfilename = "$rssname.$type";
foreach my $hostname (@$hostnamesref) {
# parameters for GIFTV
my $url = "http://$hostname/doUpload?dir=%2Fimage%2F";
my $param = {
loglevel => 4,
url => $url,
method => "POST",
hideurl => 0,
noshutdown => 0,
callback => sub($$$) { Log3 $name, 1,"ERR:$_[1] DATA:".length($_[2]) },
};
# add image as multipart form data
HttpUtils_AddMultipartData($param, $image,
{"Content-Disposition" => "form-data; name=\"file\"; filename=\"$targetfilename\"", "Content-Type" => $mimetype }
);
# upload to GIFTV $hostname
HttpUtils_NonblockingGet($param);
}
}
};
if ($@) {
my $msg = $@;
chomp $msg;
Log3 $name, 2, $msg;
}
}
Das Modul 98_DOIF.pm wird benötigt, damit RSS die darin enthaltenen Widgets verwenden kann.
push2giftv() nimmt als Argument ein Hash, das jedem RSS-Gerät (hier sensorfeed2) ein Array mit den Hostnamen oder IP-Adressen der GIFTVs zuordnet, auf die das Bild vom RSS-Gerät geschoben wird.
Beispielaufruf, um mehrere Dashboards (sensorfeed1 und sensorfeed2) auf zwei GIFTVs zu schieben:
push2giftv( { "sensorfeed1" => ["giftv-1.example.com","giftv-3.example.com"], "sensorfeed2" => ["giftv-1.example.com","giftv-3.example.com"] } ) }
Das GIFTV zeigt dann die beiden Dashboards im Wechsel an.