2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-03 16:56:54 +00:00

76_Solarforcast: contrib 0.13.0

git-svn-id: https://svn.fhem.de/fhem/trunk@23984 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
nasseeder1 2021-03-16 14:53:54 +00:00
parent b76384b5af
commit d12bebc72f

View File

@ -116,6 +116,8 @@ BEGIN {
# Versions History intern
my %vNotesIntern = (
"0.13.0" => "16.03.2021 changed sub forecastGraphic from Wzut ",
"0.12.0" => "16.03.2021 switch etoday to etotal ",
"0.11.0" => "14.03.2021 new attr history_hour, beam1Content, beam2Content, implement sub forecastGraphic from Wzut, ".
"rename attr beamColor, beamColor2 , more fixes ",
"0.10.0" => "13.03.2021 hour shifter in sub _transferMeterValues, lot of fixes ",
@ -557,10 +559,11 @@ sub _setinverterDevice { ## no critic "not used"
return qq{The device "$indev" doesn't exist!};
}
if(!$h->{pv} || !$h->{etoday}) {
if(!$h->{pv} || !$h->{etotal}) {
return qq{The syntax of "$opt" isn't right. Please consider the commandref.};
}
delete $hash->{HELPER}{INITETOTAL};
readingsSingleUpdate($hash, "currentInverterDev", $arg, 1);
createNotifyDev ($hash);
@ -776,6 +779,7 @@ sub _setreset { ## no critic "not used"
if($prop eq "pvHistory") {
my $type = $hash->{TYPE};
delete $data{$type}{$name}{pvhist};
delete $hash->{HELPER}{INITETOTAL};
return;
}
@ -793,6 +797,7 @@ sub _setreset { ## no critic "not used"
if($prop eq "currentInverterDev") {
readingsDelete ($hash, "Current_PV");
deleteReadingspec ($hash, ".*_PVreal" );
delete $hash->{HELPER}{INITETOTAL};
}
createNotifyDev ($hash);
@ -1089,12 +1094,14 @@ sub centralTask {
my @da;
my $t = time; # aktuelle Unix-Zeit
my $chour = strftime "%H", localtime($t); # aktuelle Stunde
my $day = strftime "%d", localtime($t); # aktueller Tag
my $params = {
hash => $hash,
name => $name,
t => $t,
chour => $chour,
day => $day,
daref => \@da
};
@ -1288,11 +1295,8 @@ sub _transferDWDForecastValues {
# deleteReadingspec ($hash, "NextHour.*");
for my $num (0..47) {
my $fh = $chour + $num;
my $fd = int ($fh / 24) ;
$fh = $fh - ($fd * 24);
next if($fd > 1);
my ($fd,$fh) = _calcDayHourMove ($chour, $num);
last if($fd > 1);
my $v = ReadingsVal($fcname, "fc${fd}_${fh}_Rad1h", 0);
@ -1357,15 +1361,14 @@ sub _transferWeatherValues {
push @$daref, "Tomorrow_SunRise:".$fc1_SunRise;
push @$daref, "Tomorrow_SunSet:". $fc1_SunSet;
my $fc0_SunRise_round = (sprintf "%02d", (split ":", $fc0_SunRise)[0]);
my $fc0_SunSet_round = (sprintf "%02d", (split ":", $fc0_SunSet)[0]);
my $fc0_SunRise_round = sprintf "%02d", (split ":", $fc0_SunRise)[0];
my $fc0_SunSet_round = sprintf "%02d", (split ":", $fc0_SunSet)[0];
my $fc1_SunRise_round = sprintf "%02d", (split ":", $fc1_SunRise)[0];
my $fc1_SunSet_round = sprintf "%02d", (split ":", $fc1_SunSet)[0];
for my $num (0..47) {
my $fh = $chour + $num;
my $fd = int ($fh / 24) ;
$fh = $fh - ($fd * 24);
next if($fd > 1);
my ($fd,$fh) = _calcDayHourMove ($chour, $num);
last if($fd > 1);
if($num == 0) {
$time_str = "ThisHour";
@ -1385,7 +1388,7 @@ sub _transferWeatherValues {
if($fd == 0 && ($fhstr lt $fc0_SunRise_round || $fhstr gt $fc0_SunSet_round)) { # Zeit vor Sonnenaufgang oder nach Sonnenuntergang heute
$wid += 100; # "1" der WeatherID voranstellen wenn Nacht
}
elsif ($fd == 1 && ($fhstr lt $fc0_SunRise_round || $fhstr gt $fc0_SunSet_round)) { # Zeit vor Sonnenaufgang oder nach Sonnenuntergang morgen
elsif ($fd == 1 && ($fhstr lt $fc1_SunRise_round || $fhstr gt $fc1_SunSet_round)) { # Zeit vor Sonnenaufgang oder nach Sonnenuntergang morgen
$wid += 100; # "1" der WeatherID voranstellen wenn Nacht
}
@ -1416,6 +1419,7 @@ sub _transferInverterValues {
my $name = $paref->{name};
my $t = $paref->{t}; # aktuelle Unix-Zeit
my $chour = $paref->{chour};
my $day = $paref->{day};
my $daref = $paref->{daref};
my $indev = ReadingsVal($name, "currentInverterDev", "");
@ -1423,41 +1427,65 @@ sub _transferInverterValues {
$indev = $a->[0] // "";
return if(!$indev || !$defs{$indev});
my $tlim = "00|23"; # Stunde 00/23 -> bestimmte Aktionen
my $tlim = "23"; # Stunde 23 -> bestimmte Aktionen
my $type = $hash->{TYPE};
if($chour =~ /^($tlim)$/x) {
deleteReadingspec ($hash, "Today_Hour.*_PV.*");
delete $hash->{HELPER}{INITETOTAL};
}
my ($pvread,$pvunit) = split ":", $h->{pv}; # Readingname/Unit für aktuelle PV Erzeugung
my ($edread,$edunit) = split ":", $h->{etoday}; # Readingname/Unit für Tagesenergie
my ($edread,$etunit) = split ":", $h->{etotal}; # Readingname/Unit für Energie total
Log3($name, 5, "$name - collect Inverter data: device=$indev, pv=$pvread ($pvunit), etoday=$edread ($edunit)");
Log3($name, 5, "$name - collect Inverter data: device=$indev, pv=$pvread ($pvunit), etotal=$edread ($etunit)");
my $pvuf = $pvunit =~ /^kW$/xi ? 1000 : 1;
my $pv = ReadingsNum ($indev, $pvread, 0) * $pvuf; # aktuelle Erzeugung (W)
push @$daref, "Current_PV:". $pv." W";
$data{$hash->{TYPE}}{$name}{current}{generation} = $pv; # Hilfshash Wert current generation Forum: https://forum.fhem.de/index.php/topic,117864.msg1139251.html#msg1139251
$data{$type}{$name}{current}{generation} = $pv; # Hilfshash Wert current generation Forum: https://forum.fhem.de/index.php/topic,117864.msg1139251.html#msg1139251
my $eduf = $edunit =~ /^kWh$/xi ? 1000 : 1;
my $etoday = ReadingsNum ($indev, $edread, 0) * $eduf; # aktuelle Erzeugung (W)
my $etuf = $etunit =~ /^kWh$/xi ? 1000 : 1;
my $etotal = ReadingsNum ($indev, $edread, 0) * $etuf; # Erzeugung total (Wh)
my $edaypast = 0;
deleteReadingspec ($hash, "Today_Hour00_PVreal");
# deleteReadingspec ($hash, "Today_Hour00_PVreal");
for my $hour (0..int $chour) { # alle bisherigen Erzeugungen des Tages summieren
$edaypast += ReadingsNum ($name, "Today_Hour".sprintf("%02d",$hour)."_PVreal", 0);
}
my $ethishour = int ($etoday - $edaypast);
my $do = 0;
if ($edaypast == 0) {
if (defined $hash->{HELPER}{INITETOTAL}) {
# $edaypast = $hash->{HELPER}{INITETOTAL};
# $hash->{HELPER}{INITETOTAL} = $etotal;
$do = 1;
}
else {
$hash->{HELPER}{INITETOTAL} = $etotal;
}
}
elsif (!defined $hash->{HELPER}{INITETOTAL}) {
$hash->{HELPER}{INITETOTAL} = $etotal-$edaypast-ReadingsNum($name, "Today_Hour".sprintf("%02d",$chour+1)."_PVreal", 0);
}
else {
$do = 1;
}
if ($do) {
my $ethishour = int ($etotal - ($edaypast + $hash->{HELPER}{INITETOTAL}));
# Log3($name, 1, "$name - etotal: $etotal, edaypast: $edaypast, HELPER: $hash->{HELPER}{INITETOTAL}, ethishour: $ethishour ");
if($chour !~ /^($tlim)$/x) { # nicht setzen wenn Stunde 23 des Tages
if($ethishour < 0) {
$ethishour = 0;
}
my $nhour = $chour+1;
push @$daref, "Today_Hour".sprintf("%02d",$nhour)."_PVreal:".$ethishour." Wh";
$data{$hash->{TYPE}}{$name}{pvreal}{sprintf("%02d",$nhour)} = $ethishour; # Hilfshash Wert PV real Forum: https://forum.fhem.de/index.php/topic,117864.msg1133350.html#msg1133350
$data{$type}{$name}{pvreal}{sprintf("%02d",$nhour)} = $ethishour; # Hilfshash Wert PV real Forum: https://forum.fhem.de/index.php/topic,117864.msg1133350.html#msg1133350
$paref->{ethishour} = $ethishour;
$paref->{nhour} = sprintf("%02d",$nhour);
@ -1485,8 +1513,6 @@ sub _transferMeterValues {
$medev = $a->[0] // "";
return if(!$medev || !$defs{$medev});
## aktuelle Consumption
#########################
my ($gc,$gcunit) = split ":", $h->{gcon}; # Readingname/Unit für aktuellen Netzbezug
Log3($name, 5, "$name - collect Meter data: device=$medev, gcon=$gc ($gcunit)");
@ -1500,6 +1526,21 @@ sub _transferMeterValues {
return;
}
################################################################
# Berechnen Forecast Tag / Stunden Verschieber
# aus aktueller Stunde + lfd. Nummer
################################################################
sub _calcDayHourMove {
my $chour = shift;
my $num = shift;
my $fh = $chour + $num;
my $fd = int ($fh / 24) ;
$fh = $fh - ($fd * 24);
return ($fd,$fh);
}
################################################################
# FHEMWEB Fn
################################################################
@ -1667,8 +1708,8 @@ sub forecastGraphic {
}
my $cclv = "L05";
my @pgCDev = split ',', AttrVal($name,"consumerList",""); # definierte Verbraucher ermitteln
my ($legend_style, $legend) = split '_', AttrVal($name,'consumerLegend','icon_top');
my @pgCDev = split(',',AttrVal($name,"consumerList","")); # definierte Verbraucher ermitteln
my ($legend_style, $legend) = split('_',AttrVal($name,'consumerLegend','icon_top'));
$legend = '' if(($legend_style eq 'none') || (!int(@pgCDev)));
@ -1717,11 +1758,15 @@ sub forecastGraphic {
# Parameter f. Anzeige extrahieren
###################################
my $maxhours = AttrNum ($name, 'hourCount', 24 );
my $offset = AttrNum($name, 'history_hour', 0 );
my $hourstyle = AttrVal ($name, 'hourStyle', undef );
my $colorfc = AttrVal ($name, 'beam1Color', undef );
my $colorfc = AttrVal ($name, 'beam1Color', '000000');
my $colorc = AttrVal ($name, 'beam2Color', 'C4C4A7');
my $beam1cont = AttrVal ($name, 'beam1Content', 'forecast');
my $beam2cont = AttrVal ($name, 'beam2Content', 'forecast');
my $icon = AttrVal ($name, 'consumerAdviceIcon', undef );
my $html_start = AttrVal ($name, 'htmlStart', undef ); # beliebige HTML Strings die vor der Grafik ausgegeben werden
my $html_end = AttrVal ($name, 'htmlEnd', undef ); # beliebige HTML Strings die nach der Grafik ausgegeben werden
@ -1738,7 +1783,7 @@ sub forecastGraphic {
my $show_night = AttrNum ($name, 'showNight', 0 ); # alle Balken (Spalten) anzeigen ?
my $show_diff = AttrVal ($name, 'showDiff', 'no' ); # zusätzliche Anzeige $di{} in allen Typen
my $weather = AttrNum ($name, 'showWeather', 1 );
my $colorw = AttrVal ($name, 'weatherColor', undef ); # Wetter Icon Farbe
my $colorw = AttrVal ($name, 'weatherColor', 'FFFFFF' ); # Wetter Icon Farbe
my $colorwn = AttrVal ($name, 'weatherColor_night', $colorw ); # Wetter Icon Farbe Nacht
my $wlalias = AttrVal ($name, 'alias', $name );
@ -1902,44 +1947,41 @@ sub forecastGraphic {
# Werte aktuelle Stunde
##########################
my $t0;
my $thishour;
(undef,undef,undef,$t0) = ReadingsVal($name, "ThisHour_Time", '0000-00-00 24') =~ m/(\d{4})-(\d{2})-(\d{2})\s(\d{2})/x;
(undef,undef,undef,$t0) = ReadingsVal($name, "ThisHour_Time", '00.00.0000 24') =~ m/(\d{2}).(\d{2}).(\d{4})\s(\d{2})/x if (AttrVal('global', 'language', '') eq 'DE');
(undef,undef,undef,$thishour) = ReadingsVal($name, "ThisHour_Time", '0000-00-00 24') =~ m/(\d{4})-(\d{2})-(\d{2})\s(\d{2})/x;
(undef,undef,undef,$thishour) = ReadingsVal($name, "ThisHour_Time", '00.00.0000 24') =~ m/(\d{2}).(\d{2}).(\d{4})\s(\d{2})/x if (AttrVal('global', 'language', '') eq 'DE');
$thishour = int($thishour); # keine führende Null
$t{0} = $thishour;
$t{0} = int($t0);
my $thishour = $t{0}; # aktuelle Stunde, wird erst später gebraucht.
my $offset = AttrNum($name, 'history_hour', 0);
my $val1;
my $val2;
if ($offset) {
$t{0} += $offset;
$t{0} += 24 if ($t{0} < 0);
$t0 = sprintf('%02d', $t{0});
$we{0} = (exists($data{$hash->{TYPE}}{$name}{weather}{$t0}{id})) ? $data{$hash->{TYPE}}{$name}{weather}{$t0}{id} : -1 if ($weather);
my $t0 = sprintf('%02d', $t{0}+1); # Index liegt eins höher : 10:00 = Index '11'
$val1 = (exists($data{$hash->{TYPE}}{$name}{pvfc}{$t0})) ? $data{$hash->{TYPE}}{$name}{pvfc}{$t0} : 0;
$val2 = (exists($data{$hash->{TYPE}}{$name}{pvreal}{$t0})) ? $data{$hash->{TYPE}}{$name}{pvreal}{$t0} : 0;
$we{0} = (exists($data{$hash->{TYPE}}{$name}{weather}{$t0}{id})) ? $data{$hash->{TYPE}}{$name}{weather}{$t0}{id} : -1;
#$is{0} = undef;
}
else {
$we{0} = (exists($hash->{HELPER}{'ThisHour_WeatherId'})) ? $hash->{HELPER}{"ThisHour_WeatherId"} : -1 if ($weather);
my $t0 = sprintf('%02d', $t{0}+1);
$val1 = (exists($data{$hash->{TYPE}}{$name}{pvfc}{$t0})) ? $data{$hash->{TYPE}}{$name}{pvfc}{$t0} : 0;
$val2 = (exists($data{$hash->{TYPE}}{$name}{pvreal}{$t0})) ? $data{$hash->{TYPE}}{$name}{pvreal}{$t0} : 0;
# ToDo : klären ob ThisHour:weather_Id stimmt in Bezug zu ThisHour_Time
$we{0} = (exists($hash->{HELPER}{'ThisHour_WeatherId'})) ? $hash->{HELPER}{"ThisHour_WeatherId"} : -1;
#$is{0} = (ReadingsVal($name,"ThisHour_IsConsumptionRecommended",'no') eq 'yes' ) ? $icon : undef;
}
my $pvfc0 = exists $data{$hash->{TYPE}}{$name}{pvfc}{$t0} ? $data{$hash->{TYPE}}{$name}{pvfc}{$t0} : 0;
my $pvreal0 = exists $data{$hash->{TYPE}}{$name}{pvreal}{$t0} ? $data{$hash->{TYPE}}{$name}{pvreal}{$t0} : 0;
$beam1{0} = $beam1cont eq 'forecast' ? $pvfc0 : $pvreal0;
$beam2{0} = $beam2cont eq 'forecast' ? $pvfc0 : $pvreal0;
$beam1{0} = ($beam1cont eq 'forecast') ? $val1 : $val2;
$beam2{0} = ($beam2cont eq 'forecast') ? $val1 : $val2;
$di{0} = $beam1{0} - $beam2{0};
# User Auswahl überschreiben wenn beide Werte die gleiche Basis haben !
$lotype = 'pv' if ($beam1cont eq $beam2cont);
$we{0} //= -1;
###########################################################
# get consumer list and display it in portalGraphics
###########################################################
@ -1993,56 +2035,55 @@ sub forecastGraphic {
}
$maxVal = !$maxVal ? $beam1{0} : $maxVal; # Startwert wenn kein Wert bereits via attr vorgegeben ist
my $maxCon = $beam2{0}; # für Typ co
my $maxDif = $di{0}; # für Typ diff
my $minDif = $di{0}; # für Typ diff
#Log3($hash,3 , Dumper($data{$hash->{TYPE}}{$name}));
for my $i (1..$maxhours-1) {
my $ti;
my $pvfci;
my $pvreali = 0;
for my $i (1..($maxhours*2)-1) { # doppelte Anzahl berechnen
my $val1;
my $val2 = 0;
my $ii = sprintf('%02d',$i);
(undef,undef,undef,$ti) = ReadingsVal($name,"NextHour".$ii."_Time", '0000-00-00 24') =~ m/(\d{4})-(\d{2})-(\d{2})\s(\d{2})/x;
(undef,undef,undef,$ti) = ReadingsVal($name,"NextHour".$ii."_Time", '00.00.0000 24') =~ m/(\d{2}).(\d{2}).(\d{4})\s(\d{2})/x if (AttrVal('global', 'language', '') eq 'DE');
$t{$i} = $thishour +$i;
$t{$i} -= 24 if ($t{$i} > 23);
$t{$i} = int($ti); # keine führende 0
if ($offset < 0) {
$t{$i} += $offset;
$t{$i} += 24 if ($t{$i} < 0);
if ($offset < 0) {
my $j = $t{0} + $i;
$j -= 24 if ($j > 23);
my $jj = sprintf('%02d',$j);
my $jj = sprintf('%02d',$t{$i});
if ($i <= abs($offset)) {
$pvfci = (exists($data{$hash->{TYPE}}{$name}{pvfc}{$jj})) ? $data{$hash->{TYPE}}{$name}{pvfc}{$jj} : 0;
$pvreali = (exists($data{$hash->{TYPE}}{$name}{pvreal}{$jj})) ? $data{$hash->{TYPE}}{$name}{pvreal}{$jj} : 0;
$we{$i} = (exists($data{$hash->{TYPE}}{$name}{weather}{$jj}{id})) ? $data{$hash->{TYPE}}{$name}{weather}{$jj}{id} : -1 if ($weather);
$val1 = (exists($data{$hash->{TYPE}}{$name}{pvfc}{$jj})) ? $data{$hash->{TYPE}}{$name}{pvfc}{$jj} : 0;
$val2 = (exists($data{$hash->{TYPE}}{$name}{pvreal}{$jj})) ? $data{$hash->{TYPE}}{$name}{pvreal}{$jj} : 0;
$we{$i} = (exists($data{$hash->{TYPE}}{$name}{weather}{$jj}{id})) ? $data{$hash->{TYPE}}{$name}{weather}{$jj}{id} : -1;
}
else {
my $nh = sprintf('%02d', $t{0}+$i-$thishour);
$pvfci = ReadingsNum($name, 'NextHour'.$nh.'_PVforecast', 0);
$we{$i} = $hash->{HELPER}{'NextHour'.$nh.'_WeatherId'} if($weather);
my $nh = sprintf('%02d', $i+$offset);
$val1 = ReadingsNum($name, 'NextHour'.$nh.'_PVforecast', 0);
# ToDo : klären ob -1 oder nicht !
#$nh = sprintf('%02d', $i+$offset-1);
$we{$i} = (exists($hash->{HELPER}{'NextHour'.$nh.'_WeatherId'})) ?$hash->{HELPER}{'NextHour'.$nh.'_WeatherId'} : -1;
}
}
else {
$pvfci = ReadingsNum($name, 'NextHour'.$ii.'_PVforecast', 0); # Forecast
$we{$i} = $hash->{HELPER}{'NextHour'.$ii.'_WeatherId'} if($weather); # für Wettericons
$val1 = ReadingsNum($name, 'NextHour'.$ii.'_PVforecast', 0); # Forecast
$we{$i} = (exists($hash->{HELPER}{'NextHour'.$ii.'_WeatherId'})) ? $hash->{HELPER}{'NextHour'.$ii.'_WeatherId'} : -1; # für Wettericons
#$is{$i} = (ReadingsVal($name,"NextHour".$ii."_IsConsumptionRecommended",'no') eq 'yes') ? $icon : undef;
}
$beam1{$i} = $beam1cont eq 'forecast' ? $pvfci : $pvreali;
$beam2{$i} = $beam2cont eq 'forecast' ? $pvfci : $pvreali;
$beam1{$i} = ($beam1cont eq 'forecast') ? $val1 :$val2;
$beam2{$i} = ($beam2cont eq 'forecast') ? $val1 :$val2;
# sicher stellen das wir keine undefs in der Liste haben !
$beam1{$i} //= 0;
$beam2{$i} //= 0;
$di{$i} = $beam1{$i} - $beam2{$i};
$we{$i} //= -1;
$maxVal = $beam1{$i} if ($beam1{$i} > $maxVal);
$maxCon = $beam2{$i} if ($beam2{$i} > $maxCon);
@ -2079,15 +2120,20 @@ sub forecastGraphic {
if ($weather) {
$ret .= "<tr class='even'><td class='smaportal'></td>"; # freier Platz am Anfang
for my $i (0..$maxhours-1) { # keine Anzeige bei Null Ertrag bzw. in der Nacht , Typ pcvo & diff haben aber immer Daten in der Nacht
if ($beam1{$i} || $show_night || ($lotype eq 'pvco') || ($lotype eq 'diff')) { # FHEM Wetter Icons (weather_xxx) , Skalierung und Farbe durch FHEM Bordmittel
my $night = ($we{$i} > 99) ? 1 : 0;
$we{$i} -= 100 if ($night);
my ($icon_name, $title) = weather_icon($we{$i}); # unknown -> FHEM Icon Fragezeichen im Kreis wird als Ersatz Icon ausgegeben
Log3($name, 3, "$name - unknown weather id: ".$we{$i}.", please inform the maintainer") if($icon_name eq 'unknown');
my $ii;
for my $i (0..($maxhours*2)-1) {
next if (!$show_night && ($we{$i} > 99) && !$beam1{$i} && !$beam2{$i});
# Lässt Nachticons aber noch durch wenn es einen Wert gibt , ToDo : klären ob die Nacht richtig gesetzt wurde
$ii++; # wieviele Stunden haben wir bisher angezeigt ?
last if ($ii > $maxhours); # vorzeitiger Abbruch
$icon_name .='@'.$colorw if (defined($colorw) && !$night);
$icon_name .='@'.$colorwn if (defined($colorwn) && $night);
# FHEM Wetter Icons (weather_xxx) , Skalierung und Farbe durch FHEM Bordmittel
# ToDo : weather_icon sollte im Fehlerfall Title mit der ID besetzen um in FHEMWEB sofort die ID sehen zu können
my ($icon_name, $title) = (($we{$i} > 99)) ? weather_icon($we{$i}-100) : weather_icon($we{$i});
# unknown -> FHEM Icon Fragezeichen im Kreis wird als Ersatz Icon ausgegeben
Log3($name, 4, "$name - unknown weather id: ".$we{$i}.", please inform the maintainer") if($icon_name eq 'unknown');
$icon_name .= ($we{$i} < 100 ) ? '@'.$colorw : '@'.$colorwn;
$val = FW_makeImage($icon_name);
if ($val eq $icon_name) { # passendes Icon beim User nicht vorhanden ! ( attr web iconPath falsch/prüfen/update ? )
@ -2096,11 +2142,8 @@ sub forecastGraphic {
}
$ret .= "<td title='$title' class='smaportal' width='$width' style='margin:1px; vertical-align:middle align:center; padding-bottom:1px;'>$val</td>"; # title -> Mouse Over Text
}
else { # Kein Ertrag oder show_night = 0
$ret .= "<td></td>"; $we{$i} = undef;
}
# mit $we{$i} = undef kann man unten leicht feststellen ob für diese Spalte bereits ein Icon ausgegeben wurde oder nicht
# Todo : ist jetzt nicht so , prüfen da formatVal6 we undef auswertet
}
$ret .= "<td class='smaportal'></td></tr>"; # freier Platz am Ende der Icon Zeile
@ -2108,11 +2151,15 @@ sub forecastGraphic {
if($show_diff eq 'top') { # Zusätzliche Zeile Ertrag - Verbrauch
$ret .= "<tr class='even'><td class='smaportal'></td>"; # freier Platz am Anfang
my $ii;
for my $i (0..($maxhours*2)-1) {
# gleiche Bedingung wie oben
next if (!$show_night && ($we{$i} > 99) && !$beam1{$i} && !$beam2{$i});
$ii++; # wieviele Stunden haben wir bisher angezeigt ?
last if ($ii > $maxhours); # vorzeitiger Abbruch
for my $i (0..$maxhours-1) {
$val = formatVal6($di{$i},$kw,$we{$i});
#$val = ($di{$i} < 0) ? '<b>'.$val.'<b/>' : '+'.$val; # negative Zahlen in Fettschrift
$val = '<b>'.$val.'<b/>' if ($di{$i} < 0);
$val = ($di{$i} < 0) ? '<b>'.$val.'<b/>' : ($val>0) ? '+'.$val : $val; # negative Zahlen in Fettschrift, 0 aber ohne +
$ret .= "<td class='smaportal' style='vertical-align:middle; text-align:center;'>$val</td>";
}
$ret .= "<td class='smaportal'></td></tr>"; # freier Platz am Ende
@ -2120,24 +2167,22 @@ sub forecastGraphic {
$ret .= "<tr class='even'><td class='smaportal'></td>"; # Neue Zeile mit freiem Platz am Anfang
my $ii = 0;
for my $i (0..($maxhours*2)-1) {
# gleiche Bedingung wie oben
next if (!$show_night && ($we{$i} > 99) && !$beam1{$i} && !$beam2{$i});
$ii++;
last if ($ii > $maxhours);
for my $i (0..$maxhours-1) {
# Achtung Falle, Division by Zero möglich,
# maxVal kann gerade bei kleineren maxhours Ausgaben in der Nacht leicht auf 0 fallen
$height = 200 if (!$height); # Fallback, sollte eigentlich nicht vorkommen, außer der User setzt es auf 0
$maxVal = 1 if (!int $maxVal);
$maxCon = 1 if (!$maxCon);
#Log3($hash,3, "h $height , V:$maxVal , C: $maxCon");
# Der zusätzliche Offset durch $fsize verhindert bei den meisten Skins
# dass die Grundlinie der Balken nach unten durchbrochen wird
#if($lotype eq 'co') {
# $he = int(($maxCon-$beam2{$i})/$maxCon*$height) + $fsize; # he - freier der Raum über den Balken.
# $z3 = int($height + $fsize - $he); # Resthöhe
#}
if ($lotype eq 'pv') {
$he = int(($maxVal-$beam1{$i}) / $maxVal*$height) + $fsize;
$z3 = int($height + $fsize - $he);
@ -2210,10 +2255,10 @@ sub forecastGraphic {
}
# Alle vorbesetzen Werte umrechnen auf echte Ausgabe px
$he = (!$px_pos) ? 0 : int(($maxDif-$z2)/$maxDif*$px_pos); # Teilung durch 0 vermeiden
$he = (!$px_pos || !$maxDif) ? 0 : int(($maxDif-$z2)/$maxDif*$px_pos); # Teilung durch 0 vermeiden
$z2 = ($px_pos - $he) ;
$z4 = (!$px_neg) ? 0 : int((abs($minDif)-$z3)/abs($minDif)*$px_neg); # Teilung durch 0 unbedingt vermeiden
$z4 = (!$px_neg || !$minDif) ? 0 : int((abs($minDif)-$z3)/abs($minDif)*$px_neg); # Teilung durch 0 unbedingt vermeiden
$z3 = ($px_neg - $z4);
# Beiden Zonen die Werte ausgeben könnten muß fsize als zusätzlicher Raum zugeschlagen werden !
@ -2227,17 +2272,16 @@ sub forecastGraphic {
$ret .="<td style='text-align: center; padding-left:1px; padding-right:1px; margin:0px; vertical-align:bottom; padding-top:0px'>\n";
my $v;
if ($lotype eq 'pv') {
$v = ($lotype eq 'co') ? $beam2{$i} : $beam1{$i} ;
#my $v = ($lotype eq 'co') ? $beam2{$i} : $beam1{$i} ;
#$v = 0 if (($lotype eq 'co') && !$beam1{$i} && !$show_night); # auch bei type co die Nacht ggf. unterdrücken
$val = formatVal6($v,$kw,$we{$i});
$val = formatVal6($beam1{$i},$kw,$we{$i});
$ret .="<table width='100%' height='100%'>"; # mit width=100% etwas bessere Füllung der Balken
$ret .="<tr class='even' style='height:".$he."px'>";
$ret .="<td class='smaportal' style='vertical-align:bottom'>".$val."</td></tr>";
if ($v || $show_night) { # Balken nur einfärben wenn der User via Attr eine Farbe vorgibt, sonst bestimmt class odd von TR alleine die Farbe
if ($beam1{$i} || $show_night) { # Balken nur einfärben wenn der User via Attr eine Farbe vorgibt, sonst bestimmt class odd von TR alleine die Farbe
my $style = "style=\"padding-bottom:0px; vertical-align:top; margin-left:auto; margin-right:auto;";
$style .= defined $colorfc ? " background-color:#$colorfc\"" : '"'; # Syntaxhilight
@ -2249,20 +2293,19 @@ sub forecastGraphic {
##################################
# inject the new icon if defined
$ret .= consinject($hash,$i,@pgCDev) if($ret);
#$ret .= consinject($hash,$i,@pgCDev) if($s);
$ret .= "</td></tr>";
}
}
if ($lotype eq 'pvco') {
my ($color1, $color2, $style1, $style2);
my ($color1, $color2, $style1, $style2, $v);
$ret .="<table width='100%' height='100%'>\n"; # mit width=100% etwas bessere Füllung der Balken
if($he) { # der Freiraum oben kann beim größten Balken ganz entfallen
$ret .="<tr class='even' style='height:".$he."px'><td class='smaportal'></td></tr>";
}
# der Freiraum oben kann beim größten Balken ganz entfallen
$ret .="<tr class='even' style='height:".$he."px'><td class='smaportal'></td></tr>" if ($he);
if($beam1{$i} > $beam2{$i}) { # wer ist oben, co pder pv ? Wert und Farbe für Zone 2 & 3 vorbesetzen
$val = formatVal6($beam1{$i},$kw,$we{$i});
@ -2297,7 +2340,7 @@ sub forecastGraphic {
##################################
# inject the new icon if defined
$ret .= consinject($hash,$i,@pgCDev) if($ret);
#$ret .= consinject($hash,$i,@pgCDev) if($s);
$ret .= "</td></tr>";
@ -2355,17 +2398,17 @@ sub forecastGraphic {
if ($show_diff eq 'bottom') { # zusätzliche diff Anzeige
$val = formatVal6($di{$i},$kw,$we{$i});
#$val = ($di{$i} < 0) ? '<b>'.$val.'<b/>' : '+'.$val; # Kommentar siehe oben bei show_diff eq top
$val = '<b>'.$val.'<b/>' if ($di{$i} < 0);
$val = ($di{$i} < 0) ? '<b>'.$val.'<b/>' : ($val>0) ? '+'.$val : $val; # negative Zahlen in Fettschrift, 0 aber ohne +
$ret .= "<tr class='even'><td class='smaportal' style='vertical-align:middle; text-align:center;'>$val</td></tr>";
}
$ret .= "<tr class='even'><td class='smaportal' style='vertical-align:bottom; text-align:center;'>";
$t{$i} = $t{$i}.$hourstyle if(defined($hourstyle));# z.B. 10:00 statt 10
#$ret .= (($t{$i} == $thishour) && ($offset < 0)) ? "<b>$t{$i}</b>" : $t{$i}; # Stundenwerte ohne führende 0
$ret .= (($t{$i} == $thishour) && ($offset < 0)) ? '<a class="changed" style="visibility:visible"><span>'.$t{$i}.'</span></a>' : $t{$i};
$thishour = 24 if ($t{$i} == $thishour); # nur einmal verwenden !
$ret .="</td></tr></table></td>";
}
} ## for i
$ret .= "<td class='smaportal'></td></tr>";
@ -2773,13 +2816,12 @@ sub setPVhistory {
my $name = $paref->{name};
my $t = $paref->{t}; # aktuelle Unix-Zeit
my $nhour = $paref->{nhour};
my $day = $paref->{day};
my $histname = $paref->{histname} // qq{};
my $ethishour = $paref->{ethishour} // 0;
my $calcpv = $paref->{calcpv} // 0;
my $type = $hash->{TYPE};
my $day = strftime "%d", localtime($t); # aktueller Tag
my $val = q{};
if($histname eq "pvrl") { # realer Energieertrag
@ -2807,7 +2849,6 @@ sub setPVhistory {
}
Log3 ($name, 5, "$name - set PV History hour: $nhour, hash: $histname, val: $val");
delete $data{$type}{$name}{pvhist}{$day}{summary};
return;
}
@ -3117,7 +3158,7 @@ werden weitere SolarForecast Devices zugeordnet.
<ul>
<a name="currentInverterDev"></a>
<li><b>currentInverterDev &lt;Inverter Device Name&gt; pv=&lt;Reading aktuelle PV-Leistung&gt;:&lt;Einheit&gt; etoday=&lt;Reading Energieerzeugung aktueller Tag&gt;:&lt;Einheit&gt; </b> <br>
<li><b>currentInverterDev &lt;Inverter Device Name&gt; pv=&lt;Reading aktuelle PV-Leistung&gt;:&lt;Einheit&gt; etotal=&lt;Reading Energieerzeugung total&gt;:&lt;Einheit&gt; </b> <br>
Legt ein beliebiges Device zur Lieferung der aktuellen PV Erzeugungswerte fest.
<br>
@ -3125,7 +3166,7 @@ werden weitere SolarForecast Devices zugeordnet.
<table>
<colgroup> <col width=10%> <col width=90%> </colgroup>
<tr><td> <b>pv</b> </td><td>Reading mit aktueller PV-Leistung </td></tr>
<tr><td> <b>etoday</b> </td><td>PV Erzeugung des aktuellen Tages beginnend mit 0 um 00:00 Uhr aufsteigend bis zum maximalen täglichen Ertrag am Ende des Tages </td></tr>
<tr><td> <b>etotal</b> </td><td>ein stetig aufsteigender Zähler der gesamten erzeugten Energie </td></tr>
<tr><td> <b>Einheit</b> </td><td>die jeweilige Einheit (W,kW,Wh,kWh) </td></tr>
</table>
</ul>
@ -3133,9 +3174,9 @@ werden weitere SolarForecast Devices zugeordnet.
<ul>
<b>Beispiel: </b> <br>
set &lt;name&gt; currentInverterDev STP5000 pv=total_pac:kW etoday=etoday:kWh <br>
set &lt;name&gt; currentInverterDev STP5000 pv=total_pac:kW etotal=etotal:kWh <br>
# Device STP5000 liefert PV-Werte. Die aktuell erzeugte Leistung im Reading "total_pac" (kW) und die tägliche Erzeugung im
Reading "etoday" (kWh)
Reading "etotal" (kWh)
</ul>
</li>
</ul>