2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-09 20:57:11 +00:00

76_SolarForecast: graphicHeaderOwnspec: can add set/attr commands, new setter consumerNewPlanning

git-svn-id: https://svn.fhem.de/fhem/trunk@28174 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
nasseeder1 2023-11-15 19:36:28 +00:00
parent 1fb994d013
commit 4db23a2307
2 changed files with 279 additions and 111 deletions

View File

@ -1,5 +1,7 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it. # Do not insert empty lines here, update check depends on it.
- feature: 76_SolarForecast: graphicHeaderOwnspec: can add set/attr commands,
new setter consumerNewPlanning
- feature: 72_FB_CALLMONITOR commandRef - feature: 72_FB_CALLMONITOR commandRef
- feature: 72_FRITZBOX set <name> lockFilterProfile ... - feature: 72_FRITZBOX set <name> lockFilterProfile ...
- feature: 72_FRITZBOX set <name> energyMode ... - feature: 72_FRITZBOX set <name> energyMode ...

View File

@ -94,6 +94,9 @@ BEGIN {
FmtDateTime FmtDateTime
FW_makeImage FW_makeImage
getKeyValue getKeyValue
getAllAttr
getAllGets
getAllSets
HttpUtils_NonblockingGet HttpUtils_NonblockingGet
init_done init_done
InternalTimer InternalTimer
@ -119,8 +122,10 @@ BEGIN {
FW_directNotify FW_directNotify
FW_ME FW_ME
FW_subdir FW_subdir
FW_pH
FW_room FW_room
FW_detail FW_detail
FW_widgetFallbackFn
FW_wname FW_wname
) )
); );
@ -144,6 +149,7 @@ BEGIN {
# Versions History intern # Versions History intern
my %vNotesIntern = ( my %vNotesIntern = (
"1.1.0" => "14.11.2023 graphicHeaderOwnspec: possible add set/attr commands, new setter consumerNewPlanning ",
"1.0.10" => "31.10.2023 fix warnings, edit comref ", "1.0.10" => "31.10.2023 fix warnings, edit comref ",
"1.0.9" => "29.10.2023 _aiGetSpread: set spread from 50 to 20 ", "1.0.9" => "29.10.2023 _aiGetSpread: set spread from 50 to 20 ",
"1.0.8" => "22.10.2023 codechange: add central readings store array, new function storeReading, writeCacheToFile ". "1.0.8" => "22.10.2023 codechange: add central readings store array, new function storeReading, writeCacheToFile ".
@ -442,6 +448,8 @@ my $kJtokWh = 0.00027778;
my $defmaxvar = 0.5; # max. Varianz pro Tagesberechnung Autokorrekturfaktor my $defmaxvar = 0.5; # max. Varianz pro Tagesberechnung Autokorrekturfaktor
my $definterval = 70; # Standard Abfrageintervall my $definterval = 70; # Standard Abfrageintervall
my $defslidenum = 3; # max. Anzahl der Arrayelemente in Schieberegistern my $defslidenum = 3; # max. Anzahl der Arrayelemente in Schieberegistern
my $webCmdFn = 'FW_widgetFallbackFn'; # FHEMWEB Widgets Funktion
my @attrreadings = (); # Array der Hilfsreadings als Attributspeicher
my $pvhcache = $attr{global}{modpath}."/FHEM/FhemUtils/PVH_SolarForecast_"; # Filename-Fragment für PV History (wird mit Devicename ergänzt) my $pvhcache = $attr{global}{modpath}."/FHEM/FhemUtils/PVH_SolarForecast_"; # Filename-Fragment für PV History (wird mit Devicename ergänzt)
my $pvccache = $attr{global}{modpath}."/FHEM/FhemUtils/PVC_SolarForecast_"; # Filename-Fragment für PV Circular (wird mit Devicename ergänzt) my $pvccache = $attr{global}{modpath}."/FHEM/FhemUtils/PVC_SolarForecast_"; # Filename-Fragment für PV Circular (wird mit Devicename ergänzt)
@ -528,12 +536,15 @@ my @dd = qw( none
radiationProcess radiationProcess
saveData2Cache saveData2Cache
); );
my $allwidgets = 'icon|sortable|uzsu|knob|noArg|time|text|slider|multiple|select|bitfield|widgetList|colorpicker';
# Steuerhashes # Steuerhashes
############### ###############
my %hset = ( # Hash der Set-Funktion my %hset = ( # Hash der Set-Funktion
consumerImmediatePlanning => { fn => \&_setconsumerImmediatePlanning }, consumerImmediatePlanning => { fn => \&_setconsumerImmediatePlanning },
consumerNewPlanning => { fn => \&_setconsumerNewPlanning },
currentWeatherDev => { fn => \&_setcurrentWeatherDev }, currentWeatherDev => { fn => \&_setcurrentWeatherDev },
currentRadiationAPI => { fn => \&_setcurrentRadiationAPI }, currentRadiationAPI => { fn => \&_setcurrentRadiationAPI },
modulePeakString => { fn => \&_setmodulePeakString }, modulePeakString => { fn => \&_setmodulePeakString },
@ -1302,6 +1313,7 @@ sub Set {
my $ipai = isPrepared4AI ($hash); my $ipai = isPrepared4AI ($hash);
$setlist = "Unknown argument $opt, choose one of ". $setlist = "Unknown argument $opt, choose one of ".
"consumerImmediatePlanning:$coms ". "consumerImmediatePlanning:$coms ".
"consumerNewPlanning:$coms ".
"currentWeatherDev:$fcd ". "currentWeatherDev:$fcd ".
"currentRadiationAPI:$rdd ". "currentRadiationAPI:$rdd ".
"currentBatteryDev:textField-long ". "currentBatteryDev:textField-long ".
@ -1379,7 +1391,7 @@ sub _setconsumerImmediatePlanning { ## no critic "not used"
my $type = $paref->{type}; my $type = $paref->{type};
my $opt = $paref->{opt}; my $opt = $paref->{opt};
my $c = $paref->{prop}; my $c = $paref->{prop};
my $evt = $paref->{prop1} // 1; my $evt = $paref->{prop1} // 0; # geändert V 1.1.0 - 1 -> 0
return qq{no consumer number specified} if(!$c); return qq{no consumer number specified} if(!$c);
return qq{no valid consumer id "$c"} if(!ConsumerVal ($hash, $c, "name", "")); return qq{no valid consumer id "$c"} if(!ConsumerVal ($hash, $c, "name", ""));
@ -1430,6 +1442,29 @@ sub _setconsumerImmediatePlanning { ## no critic "not used"
return; return;
} }
################################################################
# Setter consumerNewPlanning
################################################################
sub _setconsumerNewPlanning { ## no critic "not used"
my $paref = shift;
my $hash = $paref->{hash};
my $name = $paref->{name};
my $c = $paref->{prop};
my $evt = $paref->{prop1} // 0; # geändert V 1.1.0 - 1 -> 0
return qq{no consumer number specified} if(!$c);
return qq{no valid consumer id "$c"} if(!ConsumerVal ($hash, $c, 'name', ''));
if ($c) {
deleteConsumerPlanning ($hash, $c);
writeCacheToFile ($hash, 'consumers', $csmcache.$name); # Cache File Consumer schreiben
}
centralTask ($hash, $evt);
return;
}
################################################################ ################################################################
# Setter currentWeatherDev (Wetterdaten) # Setter currentWeatherDev (Wetterdaten)
################################################################ ################################################################
@ -2280,7 +2315,7 @@ sub _setreset { ## no critic "not used"
delete $data{$type}{$name}{current}{powerbatin}; delete $data{$type}{$name}{current}{powerbatin};
delete $data{$type}{$name}{current}{batcharge}; delete $data{$type}{$name}{current}{batcharge};
writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben
} }
if ($prop eq 'currentInverterDev') { if ($prop eq 'currentInverterDev') {
@ -2289,7 +2324,7 @@ sub _setreset { ## no critic "not used"
writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben
} }
if ($prop eq 'consumerPlanning') { # Verbraucherplanung resetten if ($prop eq 'consumerPlanning') { # Verbraucherplanung resetten
my $c = $paref->{prop1} // ""; # bestimmten Verbraucher setzen falls angegeben my $c = $paref->{prop1} // ""; # bestimmten Verbraucher setzen falls angegeben
if ($c) { if ($c) {
@ -2320,7 +2355,7 @@ sub _setreset { ## no critic "not used"
} }
} }
writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben
} }
createAssociatedWith ($hash); createAssociatedWith ($hash);
@ -3860,7 +3895,8 @@ sub _getlistPVHistory {
my $ret = listDataPool ($hash, 'pvhist', $arg); my $ret = listDataPool ($hash, 'pvhist', $arg);
$ret .= lineFromSpaces ($ret, 20); $ret .= lineFromSpaces ($ret, 20);
$ret =~ s/\n/<br>/g;
return $ret; return $ret;
} }
@ -3886,7 +3922,7 @@ sub _getlistNextHours {
my $ret = listDataPool ($hash, 'nexthours'); my $ret = listDataPool ($hash, 'nexthours');
$ret .= lineFromSpaces ($ret, 10); $ret .= lineFromSpaces ($ret, 10);
return $ret; return $ret;
} }
@ -5031,7 +5067,10 @@ sub _specialActivities {
deleteReadingspec ($hash, "Today_MaxPVforecast.*"); deleteReadingspec ($hash, "Today_MaxPVforecast.*");
deleteReadingspec ($hash, "Today_PVdeviation"); deleteReadingspec ($hash, "Today_PVdeviation");
deleteReadingspec ($hash, "Today_PVreal"); deleteReadingspec ($hash, "Today_PVreal");
for my $atr (@attrreadings) {
deleteReadingspec ($hash, $atr);
}
for my $n (1..24) { for my $n (1..24) {
$n = sprintf "%02d", $n; $n = sprintf "%02d", $n;
@ -9072,6 +9111,9 @@ sub __createOwnSpec {
my $show = $hdrDetail =~ /all|own/xs ? 1 : 0; my $show = $hdrDetail =~ /all|own/xs ? 1 : 0;
return if(!$spec || !$show); return if(!$spec || !$show);
my $allsets = getAllSets ($name);
my $allattrs = getAllAttr ($name);
my @fields = split (/\s+/sx, $spec); my @fields = split (/\s+/sx, $spec);
@ -9102,27 +9144,37 @@ sub __createOwnSpec {
next; next;
} }
($v->{$k}, $u->{$k}) = split /\s+/, ReadingsVal ($name, $h->{$k}{rdg}, ''); my $setcmd = ___getFWwidget ($name, $h->{$k}{rdg}, $allsets, 'set');
}
if ($setcmd) {
if ($uatr eq 'kWh') { $v->{$k} = $setcmd;
for (my $r = 0 ; $r < $vinr; $r++) { $u->{$k} = q{};
next if(!$u->{$r}); next;
}
if ($u->{$r} =~ /^Wh/xs) {
$v->{$r} = sprintf "%.1f",($v->{$r} / 1000); my $attrcmd = ___getFWwidget ($name, $h->{$k}{rdg}, $allattrs, 'attr');
$u->{$r} = 'kWh';
if ($attrcmd) {
$v->{$k} = $attrcmd;
$u->{$k} = q{};
next;
}
($v->{$k}, $u->{$k}) = split /\s+/, ReadingsVal ($name, $h->{$k}{rdg}, ' ');
next if(!$u->{$k});
if ($uatr eq 'kWh') {
if ($u->{$k} =~ /^Wh/xs) {
$v->{$k} = sprintf "%.1f",($v->{$k} / 1000);
$u->{$k} = 'kWh';
} }
} }
}
if ($uatr eq 'Wh') { if ($uatr eq 'Wh') {
for (my $r = 0 ; $r < $vinr; $r++) { if ($u->{$k} =~ /^kWh/xs) {
next if(!$u->{$r}); $v->{$k} = sprintf "%.0f",($v->{$k} * 1000);
$u->{$k} = 'Wh';
if ($u->{$r} =~ /^kWh/xs) {
$v->{$r} = sprintf "%.0f",($v->{$r} * 1000);
$u->{$r} = 'Wh';
} }
} }
} }
@ -9143,6 +9195,67 @@ sub __createOwnSpec {
return $ownv; return $ownv;
} }
################################################################
# liefert ein FHEMWEB set/attr Widget zurück
################################################################
sub ___getFWwidget {
my $name = shift;
my $elm = shift; # Element
my $allc = shift; # Kommandovorrat -> ist Element enthalten?
my $ctyp = shift // 'set'; # Kommandotyp: set/attr
my $widget = '';
my ($current, $reading);
if ($allc =~ /$elm:?(.*?)\s/xs) {
my $arg = $1;
$arg = 'textFieldNL' if(!$arg);
if ($arg !~ /^\#/xs && $arg !~ /^$allwidgets/xs) {
$arg = '#,'.$arg;
}
if ($ctyp eq 'attr') {
$current = AttrVal ($name, $elm, '');
$reading = '.'.$elm;
push @attrreadings, $reading;
readingsSingleUpdate ($defs{$name}, $reading, $current, 0);
}
no strict "refs"; ## no critic 'NoStrict'
$widget = &{$webCmdFn} ($FW_wname, $name, "", $elm, $arg);
use strict "refs";
if ($widget) {
$widget =~ s,^<td[^>]*>(.*)</td>$,$1,x;
$widget =~ s,></div>, type='$ctyp'></div>,x;
}
else {
$widget = FW_pH ("cmd=$ctyp $name $elm", $elm, 0, "", 1, 1);
}
if ($ctyp eq 'attr') {
my ($sc) = $widget =~ /current='(.*?)'/xs;
my ($sr) = $widget =~ /reading='(.*?)'/xs;
$widget =~ s/$sc/$current/ if(defined $sc);
$widget =~ s/$sr/$reading/ if(defined $sr);
}
if ($arg eq 'textField' || $arg eq 'textField-long') { # Label (Reading) ausblenden -> siehe fhemweb.js function FW_createTextField Zeile 1657
$widget =~ s/arg='textField/arg='textFieldNL/xs;
}
if ($arg =~ 'slider') { # Widget slider in selectnumbers für Kopfgrafik umsetzen
my ($wid, $min, $step, $max, $float) = split ",", $arg;
$widget =~ s/arg='(.*?)'/arg='selectnumbers,$min,$step,$max,0,lin'/xs;
}
}
return $widget;
}
################################################################ ################################################################
# Consumer in forecastGraphic (Balken) anzeigen # Consumer in forecastGraphic (Balken) anzeigen
# (Hat zur Zeit keine Wirkung !) # (Hat zur Zeit keine Wirkung !)
@ -9448,8 +9561,8 @@ sub _beamGraphicFirstHour {
my $day; my $day;
my $t = NexthoursVal ($hash, "NextHour00", "starttime", '0000-00-00 24'); my $stt = NexthoursVal ($hash, "NextHour00", "starttime", '0000-00-00 24');
my ($year,$month,$day_str,$thishour) = $t =~ m/(\d{4})-(\d{2})-(\d{2})\s(\d{2})/x; my ($year,$month,$day_str,$thishour) = $stt =~ m/(\d{4})-(\d{2})-(\d{2})\s(\d{2})/x;
my ($val1,$val2,$val3,$val4) = (0,0,0,0); my ($val1,$val2,$val3,$val4) = (0,0,0,0);
$thishour++; $thishour++;
@ -9489,7 +9602,7 @@ sub _beamGraphicFirstHour {
$val3 = CircularVal ($hash, $hfcg->{0}{time_str}, 'gcons', 0); $val3 = CircularVal ($hash, $hfcg->{0}{time_str}, 'gcons', 0);
$val4 = CircularVal ($hash, $hfcg->{0}{time_str}, 'confc', 0); $val4 = CircularVal ($hash, $hfcg->{0}{time_str}, 'confc', 0);
$hfcg->{0}{weather} = CircularVal ($hash, $hfcg->{0}{time_str}, "weatherid", 999); $hfcg->{0}{weather} = CircularVal ($hash, $hfcg->{0}{time_str}, 'weatherid', 999);
#$val4 = (ReadingsVal($name,"ThisHour_IsConsumptionRecommended",'no') eq 'yes' ) ? $icon : 999; #$val4 = (ReadingsVal($name,"ThisHour_IsConsumptionRecommended",'no') eq 'yes' ) ? $icon : 999;
} }
@ -9554,14 +9667,14 @@ sub _beamGraphicRemainingHours {
$hfcg->{$i}{wcc} = HistoryVal ($hash, $ds, $hfcg->{$i}{time_str}, 'wcc', '-'); $hfcg->{$i}{wcc} = HistoryVal ($hash, $ds, $hfcg->{$i}{time_str}, 'wcc', '-');
} }
else { else {
$nh = sprintf('%02d', $i+$offset); $nh = sprintf '%02d', $i + $offset;
} }
} }
else { else {
$nh = sprintf('%02d', $i); $nh = sprintf '%02d', $i;
} }
if (defined($nh)) { if (defined $nh) {
$val1 = NexthoursVal ($hash, 'NextHour'.$nh, 'pvfc', 0); $val1 = NexthoursVal ($hash, 'NextHour'.$nh, 'pvfc', 0);
$val4 = NexthoursVal ($hash, 'NextHour'.$nh, 'confc', 0); $val4 = NexthoursVal ($hash, 'NextHour'.$nh, 'confc', 0);
$hfcg->{$i}{weather} = NexthoursVal ($hash, 'NextHour'.$nh, 'weatherid', 999); $hfcg->{$i}{weather} = NexthoursVal ($hash, 'NextHour'.$nh, 'weatherid', 999);
@ -9638,15 +9751,15 @@ sub _beamGraphic {
if($show_diff eq 'top') { # Zusätzliche Zeile Ertrag - Verbrauch if($show_diff eq 'top') { # Zusätzliche Zeile Ertrag - Verbrauch
$ret .= "<tr class='$htr{$m}{cl}'><td class='solarfc'></td>"; $ret .= "<tr class='$htr{$m}{cl}'><td class='solarfc'></td>";
my $ii; my $ii;
for my $i (0..($maxhours*2)-1) { # gleiche Bedingung wie oben for my $i (0..($maxhours * 2) - 1) { # gleiche Bedingung wie oben
next if (!$show_night && ($hfcg->{$i}{weather} > 99) next if(!$show_night && $hfcg->{$i}{weather} > 99
&& !$hfcg->{$i}{beam1} && !$hfcg->{$i}{beam1}
&& !$hfcg->{$i}{beam2}); && !$hfcg->{$i}{beam2});
$ii++; # wieviele Stunden haben wir bisher angezeigt ? $ii++; # wieviele Stunden haben wir bisher angezeigt ?
last if ($ii > $maxhours); # vorzeitiger Abbruch last if ($ii > $maxhours); # vorzeitiger Abbruch
$val = formatVal6($hfcg->{$i}{diff},$kw,$hfcg->{$i}{weather}); $val = formatVal6 ($hfcg->{$i}{diff}, $kw, $hfcg->{$i}{weather});
if ($val ne '&nbsp;') { # Forum: https://forum.fhem.de/index.php/topic,117864.msg1166215.html#msg1166215 if ($val ne '&nbsp;') { # Forum: https://forum.fhem.de/index.php/topic,117864.msg1166215.html#msg1166215
$val = $hfcg->{$i}{diff} < 0 ? '<b>'.$val.'<b/>' : $val = $hfcg->{$i}{diff} < 0 ? '<b>'.$val.'<b/>' :
@ -9663,11 +9776,10 @@ sub _beamGraphic {
my $ii = 0; my $ii = 0;
for my $i (0..($maxhours*2)-1) { # gleiche Bedingung wie oben for my $i (0..($maxhours * 2) - 1) { # gleiche Bedingung wie oben
next if (!$show_night && defined($hfcg->{$i}{weather}) next if(!$show_night && $hfcg->{$i}{weather} > 99
&& ($hfcg->{$i}{weather} > 99) && !$hfcg->{$i}{beam1}
&& !$hfcg->{$i}{beam1} && !$hfcg->{$i}{beam2});
&& !$hfcg->{$i}{beam2});
$ii++; $ii++;
last if ($ii > $maxhours); last if ($ii > $maxhours);
@ -9768,7 +9880,7 @@ sub _beamGraphic {
$ret .="<td style='text-align: center; padding-left:1px; padding-right:1px; margin:0px; vertical-align:bottom; padding-top:0px'>\n"; $ret .="<td style='text-align: center; padding-left:1px; padding-right:1px; margin:0px; vertical-align:bottom; padding-top:0px'>\n";
if ($lotype eq 'single') { if ($lotype eq 'single') {
$val = formatVal6($hfcg->{$i}{beam1},$kw,$hfcg->{$i}{weather}); $val = formatVal6 ($hfcg->{$i}{beam1}, $kw, $hfcg->{$i}{weather});
$ret .="<table width='100%' height='100%'>"; # mit width=100% etwas bessere Füllung der Balken $ret .="<table width='100%' height='100%'>"; # mit width=100% etwas bessere Füllung der Balken
$ret .="<tr class='$htr{$m}{cl}' style='height:".$he."px'>"; $ret .="<tr class='$htr{$m}{cl}' style='height:".$he."px'>";
@ -9793,32 +9905,32 @@ sub _beamGraphic {
if ($lotype eq 'double') { if ($lotype eq 'double') {
my ($color1, $color2, $style1, $style2, $v); my ($color1, $color2, $style1, $style2, $v);
my $style = "style='padding-bottom:0px; padding-top:1px; vertical-align:top; margin-left:auto; margin-right:auto;"; my $style = "style='padding-bottom:0px; padding-top:1px; vertical-align:top; margin-left:auto; margin-right:auto;";
$ret .="<table width='100%' height='100%'>\n"; # mit width=100% etwas bessere Füllung der Balken $ret .="<table width='100%' height='100%'>\n"; # mit width=100% etwas bessere Füllung der Balken
# der Freiraum oben kann beim größten Balken ganz entfallen # der Freiraum oben kann beim größten Balken ganz entfallen
$ret .="<tr class='$htr{$m}{cl}' style='height:".$he."px'><td class='solarfc'></td></tr>" if ($he); $ret .="<tr class='$htr{$m}{cl}' style='height:".$he."px'><td class='solarfc'></td></tr>" if($he);
if($hfcg->{$i}{beam1} > $hfcg->{$i}{beam2}) { # wer ist oben, Beam2 oder Beam1 ? Wert und Farbe für Zone 2 & 3 vorbesetzen if ($hfcg->{$i}{beam1} > $hfcg->{$i}{beam2}) { # wer ist oben, Beam2 oder Beam1 ? Wert und Farbe für Zone 2 & 3 vorbesetzen
$val = formatVal6($hfcg->{$i}{beam1},$kw,$hfcg->{$i}{weather}); $val = formatVal6 ($hfcg->{$i}{beam1}, $kw, $hfcg->{$i}{weather});
$color1 = $colorb1; $color1 = $colorb1;
$style1 = $style." background-color:#$color1; color:#$fcolor1;'"; $style1 = $style." background-color:#$color1; color:#$fcolor1;'";
if ($z3) { # die Zuweisung können wir uns sparen wenn Zone 3 nachher eh nicht ausgegeben wird if ($z3) { # die Zuweisung können wir uns sparen wenn Zone 3 nachher eh nicht ausgegeben wird
$v = formatVal6($hfcg->{$i}{beam2},$kw,$hfcg->{$i}{weather}); $v = formatVal6 ($hfcg->{$i}{beam2}, $kw, $hfcg->{$i}{weather});
$color2 = $colorb2; $color2 = $colorb2;
$style2 = $style." background-color:#$color2; color:#$fcolor2;'"; $style2 = $style." background-color:#$color2; color:#$fcolor2;'";
} }
} }
else { else {
$val = formatVal6($hfcg->{$i}{beam2},$kw,$hfcg->{$i}{weather}); $val = formatVal6 ($hfcg->{$i}{beam2}, $kw, $hfcg->{$i}{weather});
$color1 = $colorb2; $color1 = $colorb2;
$style1 = $style." background-color:#$color1; color:#$fcolor2;'"; $style1 = $style." background-color:#$color1; color:#$fcolor2;'";
if ($z3) { if ($z3) {
$v = formatVal6($hfcg->{$i}{beam1},$kw,$hfcg->{$i}{weather}); $v = formatVal6 ($hfcg->{$i}{beam1}, $kw, $hfcg->{$i}{weather});
$color2 = $colorb1; $color2 = $colorb1;
$style2 = $style." background-color:#$color2; color:#$fcolor1;'"; $style2 = $style." background-color:#$color2; color:#$fcolor1;'";
} }
} }
@ -9841,7 +9953,7 @@ sub _beamGraphic {
my $style = "style='padding-bottom:0px; padding-top:1px; vertical-align:top; margin-left:auto; margin-right:auto;"; my $style = "style='padding-bottom:0px; padding-top:1px; vertical-align:top; margin-left:auto; margin-right:auto;";
$ret .= "<table width='100%' border='0'>\n"; # Tipp : das nachfolgende border=0 auf 1 setzen hilft sehr Ausgabefehler zu endecken $ret .= "<table width='100%' border='0'>\n"; # Tipp : das nachfolgende border=0 auf 1 setzen hilft sehr Ausgabefehler zu endecken
$val = ($hfcg->{$i}{diff} > 0) ? formatVal6($hfcg->{$i}{diff},$kw,$hfcg->{$i}{weather}) : ''; $val = ($hfcg->{$i}{diff} > 0) ? formatVal6 ($hfcg->{$i}{diff}, $kw, $hfcg->{$i}{weather}) : '';
$val = '&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;' if ($hfcg->{$i}{diff} == 0); # Sonderfall , hier wird die 0 gebraucht ! $val = '&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;' if ($hfcg->{$i}{diff} == 0); # Sonderfall , hier wird die 0 gebraucht !
if ($val) { if ($val) {
@ -9875,14 +9987,14 @@ sub _beamGraphic {
} }
if($z4) { # kann entfallen wenn auch z3 0 ist if($z4) { # kann entfallen wenn auch z3 0 ist
$val = ($hfcg->{$i}{diff} < 0) ? formatVal6($hfcg->{$i}{diff},$kw,$hfcg->{$i}{weather}) : '&nbsp;'; $val = ($hfcg->{$i}{diff} < 0) ? formatVal6 ($hfcg->{$i}{diff}, $kw, $hfcg->{$i}{weather}) : '&nbsp;';
$ret .= "<tr class='$htr{$m}{cl}' style='height:".$z4."px'>"; $ret .= "<tr class='$htr{$m}{cl}' style='height:".$z4."px'>";
$ret .= "<td class='solarfc' style='vertical-align:top'>".$val."</td></tr>"; $ret .= "<td class='solarfc' style='vertical-align:top'>".$val."</td></tr>";
} }
} }
if ($show_diff eq 'bottom') { # zusätzliche diff Anzeige if ($show_diff eq 'bottom') { # zusätzliche diff Anzeige
$val = formatVal6($hfcg->{$i}{diff},$kw,$hfcg->{$i}{weather}); $val = formatVal6 ($hfcg->{$i}{diff}, $kw, $hfcg->{$i}{weather});
$val = ($hfcg->{$i}{diff} < 0) ? '<b>'.$val.'<b/>' : ($val > 0 ) ? '+'.$val : $val if ($val ne '&nbsp;'); # negative Zahlen in Fettschrift, 0 aber ohne + $val = ($hfcg->{$i}{diff} < 0) ? '<b>'.$val.'<b/>' : ($val > 0 ) ? '+'.$val : $val if ($val ne '&nbsp;'); # negative Zahlen in Fettschrift, 0 aber ohne +
$ret .= "<tr class='$htr{$m}{cl}'><td class='solarfc' style='vertical-align:middle; text-align:center;'>$val</td></tr>"; $ret .= "<tr class='$htr{$m}{cl}'><td class='solarfc' style='vertical-align:middle; text-align:center;'>$val</td></tr>";
} }
@ -9926,53 +10038,50 @@ sub __weatherOnBeam {
return $ret if(!$weather); return $ret if(!$weather);
my $m = $paref->{modulo} % 2; my $m = $paref->{modulo} % 2;
my $ii = 0;
$ret .= "<tr class='$htr{$m}{cl}'><td class='solarfc'></td>"; # freier Platz am Anfang $ret .= "<tr class='$htr{$m}{cl}'><td class='solarfc'></td>"; # freier Platz am Anfang
my $ii; for my $i (0..($maxhours * 2) - 1) {
for my $i (0..($maxhours*2)-1) { last if (!exists ($hfcg->{$i}{weather}));
last if (!exists($hfcg->{$i}{weather}));
next if (!$show_night && defined($hfcg->{$i}{weather}) $hfcg->{$i}{weather} = 999 if(!defined $hfcg->{$i}{weather});
&& ($hfcg->{$i}{weather} > 99) my $wcc = $hfcg->{$i}{wcc} // '-'; # Bewölkungsgrad ergänzen
&& !$hfcg->{$i}{beam1}
&& !$hfcg->{$i}{beam2}); debugLog ($paref, 'graphic', "weather id beam number >$i< (start hour $hfcg->{$i}{time_str}): wid $hfcg->{$i}{weather} / wcc $wcc") if($ii < $maxhours);
if (!$show_night && $hfcg->{$i}{weather} > 99
&& !$hfcg->{$i}{beam1}
&& !$hfcg->{$i}{beam2}) {
debugLog ($paref, 'graphic', "weather id >$i< don't show night condition ... is skipped") if($ii < $maxhours);
next;
};
# Lässt Nachticons aber noch durch wenn es einen Wert gibt , ToDo : klären ob die Nacht richtig gesetzt wurde # Lässt Nachticons aber noch durch wenn es einen Wert gibt , ToDo : klären ob die Nacht richtig gesetzt wurde
$ii++; # wieviele Stunden Icons haben wir bisher beechnet ? $ii++; # wieviele Stunden Icons haben wir bisher beechnet ?
last if ($ii > $maxhours); last if($ii > $maxhours);
# ToDo : weather_icon sollte im Fehlerfall Title mit der ID besetzen um in FHEMWEB sofort die ID sehen zu können # ToDo : weather_icon sollte im Fehlerfall Title mit der ID besetzen um in FHEMWEB sofort die ID sehen zu können
if (exists($hfcg->{$i}{weather}) && defined($hfcg->{$i}{weather})) { my ($icon_name, $title) = $hfcg->{$i}{weather} > 100 ?
my ($icon_name, $title) = $hfcg->{$i}{weather} > 100 ? weather_icon ($name, $lang, $hfcg->{$i}{weather}-100) :
weather_icon ($name, $lang, $hfcg->{$i}{weather}-100) : weather_icon ($name, $lang, $hfcg->{$i}{weather});
weather_icon ($name, $lang, $hfcg->{$i}{weather});
my $wcc = $hfcg->{$i}{wcc} // "-"; # Bewölkungsgrad ergänzen $wcc += 0 if(isNumeric ($wcc)); # Javascript Fehler vermeiden: https://forum.fhem.de/index.php/topic,117864.msg1233661.html#msg1233661
$title .= ': '.$wcc;
if(isNumeric ($wcc)) { # Javascript Fehler vermeiden: https://forum.fhem.de/index.php/topic,117864.msg1233661.html#msg1233661 if ($icon_name eq 'unknown') {
$wcc += 0; debugLog ($paref, "graphic", "unknown weather id: ".$hfcg->{$i}{weather}.", please inform the maintainer");
}
$title .= ': '.$wcc;
if($icon_name eq 'unknown') {
debugLog ($paref, "graphic", "unknown weather id: ".$hfcg->{$i}{weather}.", please inform the maintainer");
}
$icon_name .= $hfcg->{$i}{weather} < 100 ? '@'.$colorw : '@'.$colorwn;
my $val = FW_makeImage($icon_name) // q{};
if ($val eq $icon_name) { # passendes Icon beim User nicht vorhanden ! ( attr web iconPath falsch/prüfen/update ? )
$val = '<b>???<b/>';
debugLog ($paref, "graphic", qq{the icon "$weather_ids{$hfcg->{$i}{weather}}{icon}" not found. Please check attribute "iconPath" of your FHEMWEB instance and/or update your FHEM software});
}
$ret .= "<td title='$title' class='solarfc' width='$width' style='margin:1px; vertical-align:middle align:center; padding-bottom:1px;'>$val</td>";
} }
else { # mit $hfcg->{$i}{weather} = undef kann man unten leicht feststellen ob für diese Spalte bereits ein Icon ausgegeben wurde oder nicht
$ret .= "<td></td>"; $icon_name .= $hfcg->{$i}{weather} < 100 ? '@'.$colorw : '@'.$colorwn;
$hfcg->{$i}{weather} = undef; # ToDo : prüfen ob noch nötig my $val = FW_makeImage ($icon_name) // q{};
if ($val =~ /title="$icon_name"/xs) { # passendes Icon beim User nicht vorhanden ! ( attr web iconPath falsch/prüfen/update ? )
$val = '<b>???<b/>';
debugLog ($paref, "graphic", qq{ERROR - the icon "$weather_ids{$hfcg->{$i}{weather}}{icon}.svg" not found. Please check attribute "iconPath" of your FHEMWEB instance and/or update your FHEM software});
} }
$ret .= "<td title='$title' class='solarfc' width='$width' style='margin:1px; vertical-align:middle align:center; padding-bottom:1px;'>$val</td>";
} }
$ret .= "<td class='solarfc'></td></tr>"; # freier Platz am Ende der Icon Zeile $ret .= "<td class='solarfc'></td></tr>"; # freier Platz am Ende der Icon Zeile
@ -10391,23 +10500,26 @@ return $ret;
# #
############################################################################### ###############################################################################
sub formatVal6 { sub formatVal6 {
my ($v,$kw,$w) = @_; my $v = shift;
my $n = '&nbsp;'; # positive Zahl my $kw = shift;
my $w = shift;
my $n = '&nbsp;'; # positive Zahl
if($v < 0) { if ($v < 0) {
$n = '-'; # negatives Vorzeichen merken $n = '-'; # negatives Vorzeichen merken
$v = abs($v); $v = abs($v);
} }
if($kw eq 'kWh') { # bei Anzeige in kWh muss weniger aufgefüllt werden if ($kw eq 'kWh') { # bei Anzeige in kWh muss weniger aufgefüllt werden
$v = sprintf "%.1f",($v/1000); $v = sprintf "%.1f",($v / 1000);
$v += 0; # keine 0.0 oder 6.0 etc $v += 0; # keine 0.0 oder 6.0 etc
return ($n eq '-') ? ($v*-1) : $v if defined($w) ; return ($n eq '-') ? ($v * -1) : $v if(defined $w);
my $t = $v - int($v); # Nachkommstelle ? my $t = $v - int($v); # Nachkommstelle ?
if(!$t) { # glatte Zahl ohne Nachkommastelle if (!$t) { # glatte Zahl ohne Nachkommastelle
if(!$v) { if(!$v) {
return '&nbsp;'; # 0 nicht anzeigen, passt eigentlich immer bis auf einen Fall im Typ diff return '&nbsp;'; # 0 nicht anzeigen, passt eigentlich immer bis auf einen Fall im Typ diff
} }
@ -10428,7 +10540,7 @@ sub formatVal6 {
} }
} }
return ($n eq '-') ? ($v*-1) : $v if defined($w); return ($n eq '-') ? ($v * -1) : $v if(defined $w);
# Werte bleiben in Watt # Werte bleiben in Watt
if (!$v) { return '&nbsp;'; } ## no critic "Cascading" # keine Anzeige bei Null if (!$v) { return '&nbsp;'; } ## no critic "Cascading" # keine Anzeige bei Null
@ -10454,7 +10566,7 @@ sub weather_icon {
return $weather_ids{$id}{icon}, encode("utf8", $weather_ids{$id}{$txt}); return $weather_ids{$id}{icon}, encode("utf8", $weather_ids{$id}{$txt});
} }
return 'unknown',''; return ('unknown','');
} }
################################################################ ################################################################
@ -10506,7 +10618,7 @@ return ($badev, $a ,$h);
################################################################ ################################################################
# Korrekturen und Qualität berechnen / speichern # Korrekturen und Qualität berechnen / speichern
# sowie AI Instanzen hinzufügen # sowie AI Quellen Daten hinzufügen
################################################################ ################################################################
sub calcValueImproves { sub calcValueImproves {
my $paref = shift; my $paref = shift;
@ -10551,7 +10663,7 @@ sub calcValueImproves {
_calcCaQcomplex ($paref); # Korrekturberechnung mit Bewölkung duchführen/speichern _calcCaQcomplex ($paref); # Korrekturberechnung mit Bewölkung duchführen/speichern
_calcCaQsimple ($paref); # einfache Korrekturberechnung duchführen/speichern _calcCaQsimple ($paref); # einfache Korrekturberechnung duchführen/speichern
_addHourAiRawdata ($paref); # AI Instanz hinzufügen _addHourAiRawdata ($paref); # AI Raw Data hinzufügen
delete $paref->{h}; delete $paref->{h};
} }
@ -10722,7 +10834,7 @@ return;
} }
################################################################ ################################################################
# AI Instanz für die abgeschlossene Stunde hinzufügen # AI Daten für die abgeschlossene Stunde hinzufügen
################################################################ ################################################################
sub _addHourAiRawdata { sub _addHourAiRawdata {
my $paref = shift; my $paref = shift;
@ -10737,7 +10849,7 @@ sub _addHourAiRawdata {
debugLog ($paref, 'aiProcess', "start add AI raw data for hour: $h"); debugLog ($paref, 'aiProcess', "start add AI raw data for hour: $h");
$paref->{ood} = 1; $paref->{ood} = 1; # Only One Day
$paref->{rho} = $rho; $paref->{rho} = $rho;
aiAddRawData ($paref); # Raw Daten für AI hinzufügen und sichern aiAddRawData ($paref); # Raw Daten für AI hinzufügen und sichern
@ -14429,6 +14541,22 @@ to ensure that the system configuration is correct.
</li> </li>
</ul> </ul>
<br> <br>
<ul>
<a id="SolarForecast-set-consumerNewPlanning"></a>
<li><b>consumerNewPlanning &lt;Consumer number&gt; </b> <br><br>
The existing planning of the specified consumer is deleted. <br>
The new planning is carried out immediately, taking into account the parameters set in the consumerXX attribute.
<br><br>
<ul>
<b>Beispiel: </b> <br>
set &lt;name&gt; consumerNewPlanning 01 <br>
</ul>
</li>
</ul>
<br>
<ul> <ul>
<a id="SolarForecast-set-consumerImmediatePlanning"></a> <a id="SolarForecast-set-consumerImmediatePlanning"></a>
@ -15272,7 +15400,7 @@ to ensure that the system configuration is correct.
<a id="SolarForecast-get-valDecTree"></a> <a id="SolarForecast-get-valDecTree"></a>
<li><b>valDecTree </b> <br><br> <li><b>valDecTree </b> <br><br>
If AI support is activated in the SolarForecast Device, various AI-relevant data can be displayed: If AI support is activated in the SolarForecast Device, various AI-relevant data can be displayed :
<br><br> <br><br>
<ul> <ul>
@ -15962,7 +16090,7 @@ to ensure that the system configuration is correct.
<a id="SolarForecast-attr-graphicHeaderOwnspec"></a> <a id="SolarForecast-attr-graphicHeaderOwnspec"></a>
<li><b>graphicHeaderOwnspec &lt;Label&gt;:&lt;Reading&gt; &lt;Label&gt;:&lt;Reading&gt; ... </b><br> <li><b>graphicHeaderOwnspec &lt;Label&gt;:&lt;Reading&gt; &lt;Label&gt;:&lt;Reading&gt; ... </b><br>
Display of any reading values of the device. <br> Display of any readings, set commands and attributes of the device in the graphic header. <br>
The values to be displayed are separated by spaces. The values to be displayed are separated by spaces.
Four values (fields) are displayed per line. <br> Four values (fields) are displayed per line. <br>
The input can be made in multiple lines. Values with the units "Wh" or "kWh" are converted according to the The input can be made in multiple lines. Values with the units "Wh" or "kWh" are converted according to the
@ -15992,6 +16120,17 @@ to ensure that the system configuration is correct.
<tr><td> </td><td>#Battery </td></tr> <tr><td> </td><td>#Battery </td></tr>
<tr><td> </td><td>in&amp;nbsp;today:statistic_todayBatIn </td></tr> <tr><td> </td><td>in&amp;nbsp;today:statistic_todayBatIn </td></tr>
<tr><td> </td><td>out&amp;nbsp;today:statistic_todayBatOut </td></tr> <tr><td> </td><td>out&amp;nbsp;today:statistic_todayBatOut </td></tr>
<tr><td> </td><td>: </td></tr>
<tr><td> </td><td>: </td></tr>
<tr><td> </td><td>#Settings </td></tr>
<tr><td> </td><td>Autocorrection:pvCorrectionFactor_Auto : : : </td></tr>
<tr><td> </td><td>Consumer&lt;br&gt;Replanning:consumerNewPlanning : : : </td></tr>
<tr><td> </td><td>Consumer&lt;br&gt;Quickstart:consumerImmediatePlanning : : : </td></tr>
<tr><td> </td><td>Weather:graphicShowWeather : : : </td></tr>
<tr><td> </td><td>History:graphicHistoryHour : : : </td></tr>
<tr><td> </td><td>GraphicSize:flowGraphicSize : : : </td></tr>
<tr><td> </td><td>ShowNight:graphicShowNight : : : </td></tr>
<tr><td> </td><td>Debug:ctrlDebug : : : </td></tr>
</table> </table>
</ul> </ul>
</li> </li>
@ -16274,6 +16413,22 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
</ul> </ul>
<br> <br>
<ul>
<a id="SolarForecast-set-consumerNewPlanning"></a>
<li><b>consumerNewPlanning &lt;Verbrauchernummer&gt; </b> <br><br>
Es wird die vorhandene Planung des angegebenen Verbrauchers gelöscht. <br>
Die Neuplanung wird unter Berücksichtigung der im consumerXX Attribut gesetzten Parameter sofort vorgenommen.
<br><br>
<ul>
<b>Beispiel: </b> <br>
set &lt;name&gt; consumerNewPlanning 01 <br>
</ul>
</li>
</ul>
<br>
<ul> <ul>
<a id="SolarForecast-set-consumerImmediatePlanning"></a> <a id="SolarForecast-set-consumerImmediatePlanning"></a>
<li><b>consumerImmediatePlanning &lt;Verbrauchernummer&gt; </b> <br><br> <li><b>consumerImmediatePlanning &lt;Verbrauchernummer&gt; </b> <br><br>
@ -17119,7 +17274,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
<a id="SolarForecast-get-valDecTree"></a> <a id="SolarForecast-get-valDecTree"></a>
<li><b>valDecTree </b> <br><br> <li><b>valDecTree </b> <br><br>
Ist der KI Support im SolarForecast Device aktiviert, können verschiedene KI relevante Daten angezeigt werden: Ist der KI Support im SolarForecast Device aktiviert, können verschiedene KI relevante Daten angezeigt werden :
<br><br> <br><br>
<ul> <ul>
@ -17808,7 +17963,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
<a id="SolarForecast-attr-graphicHeaderOwnspec"></a> <a id="SolarForecast-attr-graphicHeaderOwnspec"></a>
<li><b>graphicHeaderOwnspec &lt;Label&gt;:&lt;Reading&gt; &lt;Label&gt;:&lt;Reading&gt; ... </b><br> <li><b>graphicHeaderOwnspec &lt;Label&gt;:&lt;Reading&gt; &lt;Label&gt;:&lt;Reading&gt; ... </b><br>
Anzeige beliebiger Readingswerte des Devices. <br> Anzeige beliebiger Readings, Set-Kommandos und Attribute des Devices im Grafikkopf. <br>
Die anzuzeigenden Werte werden durch Leerzeichen getrennt. Die anzuzeigenden Werte werden durch Leerzeichen getrennt.
Es werden vier Werte (Felder) pro Zeile dargestellt. <br> Es werden vier Werte (Felder) pro Zeile dargestellt. <br>
Die Eingabe kann mehrzeilig erfolgen. Werte mit den Einheiten "Wh" bzw. "kWh" werden entsprechend der Einstellung Die Eingabe kann mehrzeilig erfolgen. Werte mit den Einheiten "Wh" bzw. "kWh" werden entsprechend der Einstellung
@ -17838,6 +17993,17 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
<tr><td> </td><td>#Batterie </td></tr> <tr><td> </td><td>#Batterie </td></tr>
<tr><td> </td><td>in&amp;nbsp;heute:statistic_todayBatIn </td></tr> <tr><td> </td><td>in&amp;nbsp;heute:statistic_todayBatIn </td></tr>
<tr><td> </td><td>out&amp;nbsp;heute:statistic_todayBatOut </td></tr> <tr><td> </td><td>out&amp;nbsp;heute:statistic_todayBatOut </td></tr>
<tr><td> </td><td>: </td></tr>
<tr><td> </td><td>: </td></tr>
<tr><td> </td><td>#Settings </td></tr>
<tr><td> </td><td>Autokorrektur:pvCorrectionFactor_Auto : : : </td></tr>
<tr><td> </td><td>Consumer&lt;br&gt;Neuplanung:consumerNewPlanning : : : </td></tr>
<tr><td> </td><td>Consumer&lt;br&gt;Sofortstart:consumerImmediatePlanning : : : </td></tr>
<tr><td> </td><td>Wetter:graphicShowWeather : : : </td></tr>
<tr><td> </td><td>History:graphicHistoryHour : : : </td></tr>
<tr><td> </td><td>GraphicSize:flowGraphicSize : : : </td></tr>
<tr><td> </td><td>ShowNight:graphicShowNight : : : </td></tr>
<tr><td> </td><td>Debug:ctrlDebug : : : </td></tr>
</table> </table>
</ul> </ul>
</li> </li>