From e325f514e6cbdd97a9f34b017463f41766716492 Mon Sep 17 00:00:00 2001 From: nasseeder1 Date: Thu, 31 Oct 2024 20:25:29 +0000 Subject: [PATCH] 76_SolarForecast: attr setupInverterDevXX: new key 'limit', the key 'capacity' is now mandatory! Attr affect70percentRule, ctrlAutoRefresh, ctrlAutoRefreshFW are deleted git-svn-id: https://svn.fhem.de/fhem/trunk@29321 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 3 + fhem/FHEM/76_SolarForecast.pm | 319 +++++++++++++++------------------- 2 files changed, 146 insertions(+), 176 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index 58854dfa1..31b433d00 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,8 @@ # 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 + - change: 76_SolarForecast: attr setupInverterDevXX: new key 'limit', + the key 'capacity' is now mandatory! Attr affect70percentRule, + ctrlAutoRefresh, ctrlAutoRefreshFW are deleted - feature: 36_Shelly: add Shelly Plug S MTR Gen3 - change: 76_SolarForecast: Attr graphicStartHtml, graphicEndHtml removed - feature: 49_SSCam: Compatibility (Snapshot API) to SVS version >= 9.2.1 diff --git a/fhem/FHEM/76_SolarForecast.pm b/fhem/FHEM/76_SolarForecast.pm index f0afe7825..9e33ce2fc 100644 --- a/fhem/FHEM/76_SolarForecast.pm +++ b/fhem/FHEM/76_SolarForecast.pm @@ -156,6 +156,8 @@ BEGIN { # Versions History intern my %vNotesIntern = ( + "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 +495,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 +1166,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 +1203,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 +1213,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 +1280,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 @@ -5329,13 +5329,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,11 +5351,6 @@ 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.*"); } @@ -5837,13 +5835,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 +5864,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}; @@ -7023,6 +7028,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 +8393,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 +8402,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 +8516,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 +8619,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 +9281,107 @@ 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 $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"); + + return if(!$inpmax); + + 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 >= $inpmax ) {$dold = 1} # Ladeempfehlung wenn akt. PV Leistung >= WR-Leistungsbegrenzung + + my $msg = "(Eneed: $betEneed -> Surplus Day: $spday, Curr PV: $pvCu -> 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 +12092,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) ################################################################ @@ -20537,22 +20568,6 @@ to ensure that the system configuration is correct.