From 9354ee4319638982e3cc0b1c9e4e7e90e22e8348 Mon Sep 17 00:00:00 2001 From: nasseeder1 Date: Mon, 25 Nov 2024 21:28:59 +0000 Subject: [PATCH] 76_SolarForecast: contrib 1.37.7 git-svn-id: https://svn.fhem.de/fhem/trunk@29362 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/contrib/DS_Starter/76_SolarForecast.pm | 374 ++++++++++---------- 1 file changed, 181 insertions(+), 193 deletions(-) diff --git a/fhem/contrib/DS_Starter/76_SolarForecast.pm b/fhem/contrib/DS_Starter/76_SolarForecast.pm index f0afe7825..a950da09f 100644 --- a/fhem/contrib/DS_Starter/76_SolarForecast.pm +++ b/fhem/contrib/DS_Starter/76_SolarForecast.pm @@ -156,6 +156,11 @@ BEGIN { # Versions History intern my %vNotesIntern = ( + "1.37.7" => "25.11.2024 Attr flowGraphicControl: key shift changed to shiftx, new key shifty ". + "change: 'trackFlex' && \$wcc >= 70 to \$wcc >= 80 ", + "1.37.6" => "01.11.2024 minor code change, Attr setupBatteryDev: the key 'cap' is mandatory now ", + "1.37.5" => "31.10.2024 attr setupInverterDevXX: new key 'limit', the key 'capacity' is now mandatory ". + "Attr affect70percentRule, ctrlAutoRefresh, ctrlAutoRefreshFW deleted ", "1.37.4" => "29.10.2024 both attr graphicStartHtml, graphicEndHtml removed, fix flowGraphic when device name contains '.' ", "1.37.3" => "25.10.2024 _flowGraphic: grid, dummy and battery displacement by kask ". "Attr flowGraphicControl: new key h2consumerdist, animate=1 is default now ", @@ -493,10 +498,10 @@ my @rconfigs = qw( pvCorrectionFactor_Auto energyH4Trigger ); # Anlagenkonfiguration: maßgebliche Attribute -my @aconfigs = qw( affect70percentRule affectBatteryPreferredCharge affectConsForecastIdentWeekdays +my @aconfigs = qw( affectBatteryPreferredCharge affectConsForecastIdentWeekdays affectConsForecastInPlanning affectSolCastPercentile consumerLegend consumerAdviceIcon consumerLink - ctrlAIdataStorageDuration ctrlAutoRefresh ctrlAutoRefreshFW ctrlBackupFilesKeep + ctrlAIdataStorageDuration ctrlBackupFilesKeep ctrlBatSocManagement ctrlConsRecommendReadings ctrlGenPVdeviation ctrlInterval ctrlLanguage ctrlNextDayForecastReadings ctrlShowLink ctrlSolCastAPImaxReq ctrlSolCastAPIoptimizeReq ctrlStatisticReadings ctrlUserExitFn @@ -1164,7 +1169,6 @@ my %hfspvh = ( sub Initialize { my $hash = shift; - my $fwd = join ",", devspec2array("TYPE=FHEMWEB:FILTER=STATE=Initialized"); my $hod = join ",", map { sprintf "%02d", $_} (1..24); my $srd = join ",", sort keys (%hcsr); my $gbc = 'pvReal,pvForecast,consumption,consumptionForecast,gridconsumption,energycosts,gridfeedin,feedincome'; @@ -1202,8 +1206,7 @@ sub Initialize { $hash->{AttrFn} = \&Attr; $hash->{NotifyFn} = \&Notify; $hash->{ReadyFn} = \&runTask; - $hash->{AttrList} = "affect70percentRule:1,dynamic,0 ". - "affectBatteryPreferredCharge:slider,0,1,100 ". + $hash->{AttrList} = "affectBatteryPreferredCharge:slider,0,1,100 ". "affectConsForecastIdentWeekdays:1,0 ". "affectConsForecastInPlanning:1,0 ". "affectConsForecastLastDays:slider,1,1,31 ". @@ -1213,12 +1216,10 @@ sub Initialize { "consumerLink:0,1 ". "ctrlAIdataStorageDuration ". "ctrlAIshiftTrainStart:slider,1,1,23 ". - "ctrlAutoRefresh:selectnumbers,120,0.2,1800,0,log10 ". - "ctrlAutoRefreshFW:$fwd ". "ctrlBackupFilesKeep ". "ctrlBatSocManagement:textField-long ". "ctrlConsRecommendReadings:multiple-strict,$allcs ". - "ctrlDebug:multiple-strict,$dm,#14 ". + "ctrlDebug:multiple-strict,$dm,#10 ". "ctrlAreaFactorUsage:fix,trackFull,trackShared,trackFlex ". "ctrlGenPVdeviation:daily,continuously ". "ctrlInterval ". @@ -1282,6 +1283,8 @@ sub Initialize { my $av = 'obsolete#-#use#attr#flowGraphicControl#instead'; $hash->{AttrList} .= " flowGraphicCss:$av flowGraphicSize:$av flowGraphicAnimate:$av flowGraphicConsumerDistance:$av flowGraphicShowConsumer:$av flowGraphicShowConsumerDummy:$av flowGraphicShowConsumerPower:$av flowGraphicShowConsumerRemainTime:$av flowGraphicShift:$av "; + my $av1 = "obsolete#-#the#attribute#will#be#deleted#soon"; + $hash->{AttrList} .= " affect70percentRule:$av1 ctrlAutoRefresh:$av1 ctrlAutoRefreshFW:$av1 "; ########################################################################################################################## $hash->{FW_hideDisplayName} = 1; # Forum 88667 @@ -3451,7 +3454,7 @@ sub __getDWDSolarData { $af = 1.00 if(!isNumeric($af)); $sdr = 0.75 if(!isNumeric($sdr)); - if ($cafd eq 'trackShared'|| ($cafd eq 'trackFlex' && $wcc >= 70)) { # Direktstrahlung + Diffusstrahlung + if ($cafd eq 'trackShared'|| ($cafd eq 'trackFlex' && $wcc >= 80)) { # Direktstrahlung + Diffusstrahlung my $dirrad = $rad * $sdr; # Anteil Direktstrahlung an Globalstrahlung my $difrad = $rad - $dirrad; # Anteil Diffusstrahlung an Globalstrahlung @@ -5329,13 +5332,16 @@ sub Attr { return qq{The attribute '$aName' is obsolete and replaced by 'flowGraphicControl'.}; } } - # 29.10.2024 - if ($cmd eq 'set' && $aName =~ /^graphicStartHtml|graphicEndHtml$/) { + # 31.10.2024 + if ($cmd eq 'set' && $aName =~ /^graphicStartHtml|affect70percentRule|graphicEndHtml|ctrlAutoRefresh|ctrlAutoRefreshFW$/) { if (!$init_done) { my $msg = "The attribute $aName has been removed and is no longer valid."; Log3 ($name, 1, "$name - $msg"); return qq{Device "$name" -> $msg}; } + else { + return qq{The attribute '$aName' is obsolete.}; + } } ###################################################################################################################### @@ -5348,18 +5354,13 @@ sub Attr { singleUpdateState ( {hash => $hash, state => $val, evt => 1} ); } - if ($aName eq 'ctrlAutoRefresh') { - delete $hash->{HELPER}{AREFRESH}; - delete $hash->{AUTOREFRESH}; - } - if ($aName eq 'ctrlNextDayForecastReadings') { deleteReadingspec ($hash, "Tomorrow_Hour.*"); } if ($aName eq 'ctrlBatSocManagement' && $init_done) { if ($cmd eq 'set') { - return qq{Define the key "cap" with "attr $name setupBatteryDev" before this attribute in the correct form.} + return qq{Define the key 'cap' with "attr $name setupBatteryDev" before this attribute in the correct form.} if(!CurrentVal($hash, 'batinstcap', 0)); # https://forum.fhem.de/index.php?msg=1310930 my ($lowSoc, $upSoc, $maxsoc, $careCycle) = __parseAttrBatSoc ($name, $aVal); @@ -5656,7 +5657,8 @@ sub _attrflowGraphicControl { ## no critic "not used" for my $av ( qw( animate consumerdist h2consumerdist - shift + shiftx + shifty showconsumer showconsumerremaintime size @@ -5676,7 +5678,8 @@ sub _attrflowGraphicControl { ## no critic "not used" animate => '0|1', consumerdist => '[89]\d{1}|[1234]\d{2}|500', h2consumerdist => '\d{1,3}', - shift => '-?[0-7]\d{0,1}|-?80', + shiftx => '-?[0-7]\d{0,1}|-?80', + shifty => '\d+', size => '\d+', showconsumer => '0|1', showconsumerdummy => '0|1', @@ -5837,13 +5840,19 @@ sub _attrInverterDev { ## no critic "not used" return qq{Set the first Inverter device with attribute 'setupInverterDev01'}; } - if (!$h->{pv} || !$h->{etotal}) { - return qq{The syntax of '$aName' is not valid. Please consider the commandref.}; + if (!$h->{pv} || !$h->{etotal} || !$h->{capacity}) { + return qq{One or more of the keys 'pv, etotal, capacity' are missing. Please consider the commandref.}; } - if ($h->{capacity} && !isNumeric($h->{capacity})) { - return qq{The syntax of key 'capacity' is not valid. Please consider the commandref.}; + if (!isNumeric($h->{capacity})) { + return qq{The value of key 'capacity' must be numeric. Please consider the commandref.}; } + + if ($h->{limit}) { + if (!isNumeric($h->{limit}) || $h->{limit} < 0 || $h->{limit} > 100) { + return qq{The value of key 'limit' is not valid. Please consider the commandref.}; + } + } if ($h->{feed} && $h->{feed} !~ /^grid|bat$/xs) { return qq{The value of key 'feed' is not valid. Please consider the commandref.}; @@ -5860,6 +5869,7 @@ sub _attrInverterDev { ## no critic "not used" $data{$type}{$name}{circular}{99}{attrInvChangedTs} = int time; delete $data{$type}{$name}{inverters}{$in}{invertercap}; + delete $data{$type}{$name}{inverters}{$in}{ilimit}; delete $data{$type}{$name}{inverters}{$in}{iicon}; delete $data{$type}{$name}{inverters}{$in}{istrings}; delete $data{$type}{$name}{inverters}{$in}{ifeed}; @@ -6027,8 +6037,8 @@ sub _attrBatteryDev { ## no critic "not used" my ($err, $badev, $h) = isDeviceValid ( { name => $name, obj => $aVal, method => 'string' } ); return $err if($err); - if (!$h->{pin} || !$h->{pout}) { - return qq{The keys 'pin' and/or 'pout' are not set. Please note the command reference.}; + if (!$h->{pin} || !$h->{pout} || !$h->{cap}) { + return qq{One or more of the keys 'pin, pout, cap' are missing. Please note the command reference.}; } if (($h->{pin} !~ /-/xs && $h->{pin} !~ /:/xs) || @@ -7023,6 +7033,7 @@ sub centralTask { _transferMeterValues ($centpars); # Energy Meter auswerten _transferBatteryValues ($centpars); # Batteriewerte einsammeln _batSocTarget ($centpars); # Batterie Optimum Ziel SOC berechnen + _batChargeRecmd ($centpars); # Batterie Ladeempfehlung berechnen und erstellen _manageConsumerData ($centpars); # Consumer Daten sammeln und Zeiten planen _estConsumptionForecast ($centpars); # Verbrauchsprognose erstellen _evaluateThresholds ($centpars); # Schwellenwerte bewerten und signalisieren @@ -8387,16 +8398,8 @@ sub __calcPVestimates { $data{$type}{$name}{current}{allstringspeak} = $peaksum; # temperaturbedingte Korrektur der installierten Peakleistung in W $pvsum = $peaksum if($peaksum && $pvsum > $peaksum); # Vorhersage nicht größer als die Summe aller PV-Strings Peak - - my $logao = qq{}; - $paref->{pvsum} = $pvsum; - $paref->{peaksum} = $peaksum; + $pvsum = sprintf "%.0f", $pvsum; - ($pvsum, $logao) = ___70percentRule ($paref); - - delete $paref->{peaksum}; - delete $paref->{pvsum}; - if ($debug =~ /radiationProcess/xs) { $lh = { # Log-Hash zur Ausgabe "Starttime" => $wantdt, @@ -8404,7 +8407,7 @@ sub __calcPVestimates { "Cloudcover" => $wcc, "Total Rain last hour" => $rr1c." kg/m2", "PV Correction mode" => ($acu ? $acu : 'no'), - "PV generation forecast" => $pvsum." Wh ".$logao, + "PV generation forecast" => $pvsum." Wh", }; $sq = q{}; @@ -8518,36 +8521,6 @@ sub ___calcPeaklossByTemp { return ($peakloss, $modtemp); } -################################################################ -# 70% Regel kalkulieren -################################################################ -sub ___70percentRule { - my $paref = shift; - my $name = $paref->{name}; - my $pvsum = $paref->{pvsum}; - my $peaksum = $paref->{peaksum}; - my $num = $paref->{num}; # Nexthour - - my $hash = $defs{$name}; - my $logao = qq{}; - my $confc = NexthoursVal ($hash, "NextHour".sprintf("%02d",$num), "confc", 0); - my $max70 = $peaksum/100 * 70; - - if (AttrVal ($name, "affect70percentRule", "0") eq "1" && $pvsum > $max70) { - $pvsum = $max70; - $logao = qq{(reduced by 70 percent rule)}; - } - - if (AttrVal ($name, "affect70percentRule", "0") eq "dynamic" && $pvsum > $max70 + $confc) { - $pvsum = $max70 + $confc; - $logao = qq{(reduced by 70 percent dynamic rule)}; - } - - $pvsum = int $pvsum; - -return ($pvsum, $logao); -} - ################################################################ # den Maximalwert PV Vorhersage für Heute ermitteln ################################################################ @@ -8651,14 +8624,15 @@ sub _transferInverterValues { my $feed = $h->{feed} // 'default'; - $data{$type}{$name}{inverters}{$in}{igeneration} = $pv; # Hilfshash Wert current generation, Forum: https://forum.fhem.de/index.php/topic,117864.msg1139251.html#msg1139251 - $data{$type}{$name}{inverters}{$in}{ietotal} = $etotal; # aktuellen etotal des WR speichern - $data{$type}{$name}{inverters}{$in}{iname} = $indev; # Name des Inverterdevices - $data{$type}{$name}{inverters}{$in}{ialias} = AttrVal ($indev, 'alias', $indev); # Alias Inverter - $data{$type}{$name}{inverters}{$in}{invertercap} = $h->{capacity} if(defined $h->{capacity}); # optionale Angabe max. WR-Leistung - $data{$type}{$name}{inverters}{$in}{iicon} = $h->{icon} if($h->{icon}); # Icon des Inverters - $data{$type}{$name}{inverters}{$in}{istrings} = $h->{strings} if($h->{strings}); # dem Inverter zugeordnete Strings - $data{$type}{$name}{inverters}{$in}{ifeed} = $feed; # Eigenschaften der Energielieferung + $data{$type}{$name}{inverters}{$in}{igeneration} = $pv; # Hilfshash Wert current generation, Forum: https://forum.fhem.de/index.php/topic,117864.msg1139251.html#msg1139251 + $data{$type}{$name}{inverters}{$in}{ietotal} = $etotal; # aktuellen etotal des WR speichern + $data{$type}{$name}{inverters}{$in}{iname} = $indev; # Name des Inverterdevices + $data{$type}{$name}{inverters}{$in}{ialias} = AttrVal ($indev, 'alias', $indev); # Alias Inverter + $data{$type}{$name}{inverters}{$in}{invertercap} = $h->{capacity} if(defined $h->{capacity}); # optionale Angabe max. WR-Leistung + $data{$type}{$name}{inverters}{$in}{ilimit} = $h->{limit} // 100; # Wirkleistungsbegrenzung + $data{$type}{$name}{inverters}{$in}{iicon} = $h->{icon} if($h->{icon}); # Icon des Inverters + $data{$type}{$name}{inverters}{$in}{istrings} = $h->{strings} if($h->{strings}); # dem Inverter zugeordnete Strings + $data{$type}{$name}{inverters}{$in}{ifeed} = $feed; # Eigenschaften der Energielieferung $pvsum += $pv; $ethishoursum += $ethishour; @@ -9312,6 +9286,112 @@ sub __batSaveSocKeyFigures { return; } +################################################################ +# Erstellung Batterie Ladeempfehlung +################################################################ +sub _batChargeRecmd { + my $paref = shift; + my $name = $paref->{name}; + my $chour = $paref->{chour}; + + return if(!isBatteryUsed ($name)); + + my $hash = $defs{$name}; + + my $rodpvfc = ReadingsNum ($name, 'RestOfDayPVforecast', 0); # PV Prognose Rest des Tages + my $tompvfc = ReadingsNum ($name, 'Tomorrow_PVforecast', 0); # PV Prognose nächster Tag + + my $confcss = CurrentVal ($hash, 'tdConFcTillSunset', 0); # Verbrauchsprognose bis Sonnenuntergang + my $tomconfc = ReadingsNum ($name, 'Tomorrow_ConsumptionForecast', 0); + + my $pvCu = ReadingsNum ($name, 'Current_PV', 0); # aktuelle PV Erzeugung + my $batcap = CurrentVal ($hash, 'batinstcap', 0); # installierte Batteriekapazität Wh + my $soc = CurrentVal ($hash, 'batcharge', 0); # aktueller SOC (%) + my $curcon = ReadingsNum ($name, 'Current_Consumption', 0); # aktueller Verbrauch + + my $inpmax = 0; + + for my $in (1..$maxinverter) { + $in = sprintf "%02d", $in; + my $feed = InverterVal ($hash, $in, 'ifeed', ''); + next if(!$feed || $feed eq 'grid'); # Inverter 'Grid' ausschließen + + my $iname = InverterVal ($hash, $in, 'iname', ''); + my $icap = InverterVal ($hash, $in, 'invertercap', 0); + my $limit = InverterVal ($hash, $in, 'ilimit', 100); # Wirkleistungsbegrenzung (default keine Begrenzung) + my $aplim = $icap * $limit / 100; + $inpmax += $aplim; # max. Leistung aller WR mit Berücksichtigung Wirkleistungsbegrenzung + + debugLog ($paref, 'batteryManagement', "Inverter '$iname' capacity: $icap, Active power limit: $limit % -> Pmax limited: $aplim"); + } + + debugLog ($paref, 'batteryManagement', "Summary active power limit of all Inverter (except feed 'grid'): $inpmax"); + debugLog ($paref, 'batteryManagement', "Installed Battery capacity: $batcap"); + + if (!$inpmax || !$batcap) { + debugLog ($paref, 'batteryManagement', "WARNING - The requirements for dynamic battery charge recommendation are not met. Exit."); + return; + } + + my $sfmargin = $inpmax * 0.5; # Sicherheitszuschlag 50% der installierten Leistung (Wh) + my $betEneed = sprintf "%.0f", ($batcap - ($batcap * $soc / 100)); # benötigte Energie bis 100% Batteriekapazität Wh + + for my $num (0..47) { + my ($fd,$fh) = calcDayHourMove ($chour, $num); + next if($fd > 1); + + my $today = NexthoursVal ($hash, 'NextHour'.sprintf("%02d",$num), 'today', 0); + my $confc = NexthoursVal ($hash, 'NextHour'.sprintf("%02d",$num), 'confc', 0); + my $pvfc = NexthoursVal ($hash, 'NextHour'.sprintf("%02d",$num), 'pvfc', 0); + my $stt = NexthoursVal ($hash, 'NextHour'.sprintf("%02d",$num), 'starttime', ''); + $stt = (split '-', $stt)[2] if($stt); + + my $dold = 0; # Ladeempfehlung 0 per Default + my $spday = 0; + + if ($today) { # (Rest) heutiger Tag + $spday = $rodpvfc - $confcss; + } + else { # nächster Tag + $spday = $tompvfc - $tomconfc; + } + + $spday = 0 if($spday < 0); # PV Überschuß Prognose bis Sonnenuntergang + + if ( $betEneed + $sfmargin >= $spday ) {$dold = 1} # Ladeempfehlung wenn benötigte Ladeenergie >= Restüberschuß des Tages zzgl. Sicherheitsaufschlag + if ( !$num && $pvCu - $curcon >= $inpmax ) {$dold = 1} # Ladeempfehlung wenn akt. PV Leistung >= WR-Leistungsbegrenzung + + my $msg = "(Eneed: $betEneed -> Surplus Day: $spday, Curr PV: $pvCu, Curr Consumption: $curcon -> Limit: $inpmax)"; + + if ($num) { + $msg = "(Eneed: $betEneed -> Surplus Day: $spday)"; + } + else { + storeReading ('Battery_ChargeRecommended', $dold); # Reading nur für aktuelle Stunde + } + + debugLog ($paref, 'batteryManagement', "Charge activation $stt -> $dold $msg"); + + if ($pvfc) { + if ($today) { # (Rest) heutiger Tag + $confcss -= $confc; + $confcss = 0 if($confcss < 0); + $rodpvfc -= $pvfc; + } + else { # nächster Tag + $tomconfc -= $confc; + $tomconfc = 0 if($tomconfc < 0); + $tompvfc -= $pvfc; + } + } + + $betEneed -= sprintf "%.0f", ($pvfc - $confc); + $betEneed = $betEneed < 0 ? 0 : $betEneed; + } + +return; +} + ################################################################ # Zusammenfassungen erstellen ################################################################ @@ -12022,48 +12102,9 @@ sub FwFn { $ret .= entryGraphic ($name); $ret .= ""; - # Autorefresh nur des aufrufenden FHEMWEB-Devices - my $al = AttrVal ($name, 'ctrlAutoRefresh', 0); - if ($al) { - pageRefresh ($hash); - } - return $ret; } -########################################################################### -# Seitenrefresh festgelegt durch SolarForecast-Attribut "ctrlAutoRefresh" -# und "ctrlAutoRefreshFW" -########################################################################### -sub pageRefresh { - my $hash = shift; - my $name = $hash->{NAME}; - - my $al = AttrVal($name, 'ctrlAutoRefresh', 0); - - if ($al) { - my $rftime = gettimeofday()+$al; - - if (!$hash->{HELPER}{AREFRESH} || $hash->{HELPER}{AREFRESH} <= gettimeofday()) { - RemoveInternalTimer ($hash, \&pageRefresh); - InternalTimer($rftime, \&pageRefresh, $hash, 0); - - my $rd = AttrVal ($name, 'ctrlAutoRefreshFW', $hash->{HELPER}{FW}); - { map { FW_directNotify("#FHEMWEB:$_", "location.reload('true')", "") } $rd } ## no critic 'Map blocks' - - $hash->{HELPER}{AREFRESH} = $rftime; - $hash->{AUTOREFRESH} = FmtDateTime($rftime); - } - } - else { - delete $hash->{HELPER}{AREFRESH}; - delete $hash->{AUTOREFRESH}; - RemoveInternalTimer ($hash, \&pageRefresh); - } - -return; -} - ################################################################ # Grafik als HTML zurück liefern (z.B. für Widget) ################################################################ @@ -12153,7 +12194,8 @@ sub entryGraphic { hdrDetail => AttrVal ($name, 'graphicHeaderDetail', 'all'), # ermöglicht den Inhalt zu begrenzen, um bspw. passgenau in ftui einzubetten flowgsize => CurrentVal ($hash, 'size', $flowGSizedef), # Größe Energieflußgrafik flowgani => CurrentVal ($hash, 'animate', 1), # Animation Energieflußgrafik - flowgshift => CurrentVal ($hash, 'shift', 0), # Verschiebung der Flußgrafikbox (muß negiert werden) + flowgxshift => CurrentVal ($hash, 'shiftx', 0), # X-Verschiebung der Flußgrafikbox (muß negiert werden) + flowgyshift => CurrentVal ($hash, 'shifty', 0), # Y-Verschiebung der Flußgrafikbox (muß negiert werden) flowgcons => CurrentVal ($hash, 'showconsumer', 1), # Verbraucher in der Energieflußgrafik anzeigen flowgconX => CurrentVal ($hash, 'showconsumerdummy', 1), # Dummyverbraucher in der Energieflußgrafik anzeigen flowgconsPower => CurrentVal ($hash, 'showconsumerpower', 1), # Verbraucher Leistung in der Energieflußgrafik anzeigen @@ -14217,7 +14259,8 @@ sub _flowGraphic { my $type = $paref->{type}; my $flowgsize = $paref->{flowgsize}; my $flowgani = $paref->{flowgani}; - my $flowgshift = $paref->{flowgshift}; # Verschiebung der Flußgrafikbox (muß negiert werden) + my $flowgxshift = $paref->{flowgxshift}; # X-Verschiebung der Flußgrafikbox (muß negiert werden) + my $flowgyshift = $paref->{flowgyshift}; # Y-Verschiebung der Flußgrafikbox (muß negiert werden) my $flowgcons = $paref->{flowgcons}; # Verbraucher in der Energieflußgrafik anzeigen my $flowgconsTime = $paref->{flowgconsTime}; # Verbraucher Restlaufeit in der Energieflußgrafik anzeigen my $flowgconX = $paref->{flowgconX}; @@ -14373,7 +14416,7 @@ sub _flowGraphic { ## SVG Box initialisieren mit Grid-Icon ######################################### my $vbwidth = 800; # width and height specify the viewBox size - my $vbminx = -10 * $flowgshift; # min-x and min-y represent the smallest X and Y coordinates that the viewBox may have + my $vbminx = -10 * $flowgxshift; # min-x and min-y represent the smallest X and Y coordinates that the viewBox may have my $vbminy = $doproducerrow ? -25 : 125; # Grafik höher positionieren wenn keine Poducerreihe angezeigt my $vbhight = !$flowgcons ? 380 : @@ -14381,6 +14424,9 @@ sub _flowGraphic { 610; $vbhight += $exth2cdist; + $vbminy -= $flowgyshift; # Y-Verschiebung berücksichtigen + $vbhight += $flowgyshift; # Y-Verschiebung berücksichtigen + if ($doproducerrow) {$vbhight += 100}; # Höhe Box vergrößern wenn Poducerreihe angezeigt my $vbox = "$vbminx $vbminy $vbwidth $vbhight"; @@ -20537,22 +20583,6 @@ to ensure that the system configuration is correct.