76_SolarForecast: Version 1.60.4
git-svn-id: https://svn.fhem.de/fhem/trunk@30525 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
# 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
|
||||
- feature: 76_SolarForecast: Version 1.60.4
|
||||
- feature: 76_SolarForecast: Version 1.60.3, new param barrierSoC
|
||||
- bugfix: 72_FRITZBOX: box_ppp_ überarbeitet
|
||||
- bugfix: 73_PRESENCE2: laden von hcitool nach hinten geschoben
|
||||
|
||||
@@ -29,11 +29,13 @@
|
||||
#
|
||||
#########################################################################################################################
|
||||
main::LoadModule ('Astro'); # Astro Modul für Sonnenkennzahlen laden
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
package FHEM::SolarForecast; ## no critic 'package'
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
#use strict;
|
||||
#use warnings;
|
||||
use POSIX;
|
||||
use GPUtils qw(GP_Import GP_Export); # wird für den Import der FHEM Funktionen aus der fhem.pl benötigt
|
||||
use Time::HiRes qw(gettimeofday tv_interval);
|
||||
@@ -160,6 +162,8 @@ BEGIN {
|
||||
|
||||
# Versions History intern
|
||||
my %vNotesIntern = (
|
||||
"1.60.4" => "13.11.2025 smoothValue as OOP implemantation, battery efficiency rework, edit comref, expand loadTarget by time target ".
|
||||
"_batSocTarget: surplus for next day adjusted, some code changes ",
|
||||
"1.60.3" => "06.11.2025 more preparation for barrierSoC, ___batFindMinPhWh: code change, new parameter ctrlBatSocManagementXX->barrierSoC ",
|
||||
"1.60.2" => "03.11.2025 fix lowSoC comparison, ___batAdjustPowerByMargin: more preparation for barrierSoC ",
|
||||
"1.60.1" => "02.11.2025 ___batAdjustPowerByMargin: minor code change, preparation for barrierSoC ",
|
||||
@@ -495,6 +499,8 @@ use constant {
|
||||
SFTYMARGIN_20 => 20, # Sicherheitszuschlag 20%
|
||||
SFTYMARGIN_50 => 50, # Sicherheitszuschlag 50%
|
||||
STOREFFDEF => 87, # default Batterie Effizienz (https://www.energie-experten.org/erneuerbare-energien/photovoltaik/stromspeicher/wirkungsgrad)
|
||||
OTPDEADBAND => 10.0, # Smoother Standard OTP Power Schwellenwert für Änderungen
|
||||
OTPALPHA => 1.0, # Smoother Standard OTP Power Alpha default
|
||||
TEMPCOEFFDEF => -0.45, # default Temperaturkoeffizient Pmpp (%/°C) lt. Datenblatt Solarzelle
|
||||
TEMPMODINC => 25, # default Temperaturerhöhung an Solarzellen gegenüber Umgebungstemperatur bei wolkenlosem Himmel
|
||||
TEMPBASEDEF => 25, # Temperatur Module bei Nominalleistung
|
||||
@@ -7534,7 +7540,7 @@ sub _attrBatteryDev { ## no critic "not used"
|
||||
show => { comp => '(?:[0-3](?::(?:top|bottom))?)', must => 0, act => 0 },
|
||||
label => { comp => '(none|below|beside)', must => 0, act => 0 },
|
||||
asynchron => { comp => '(0|1)', must => 0, act => 0 },
|
||||
efficiency => { comp => '(100|[1-9]?[0-9])', must => 0, act => 0 },
|
||||
efficiency => { comp => '(?:100|[1-9][0-9]?)', must => 0, act => 0 },
|
||||
};
|
||||
|
||||
if ($paref->{cmd} eq 'set') {
|
||||
@@ -7659,7 +7665,7 @@ sub _attrBatSocManagement { ## no critic "not used"
|
||||
careCycle => { comp => '\d+', must => 0, act => 0 },
|
||||
loadAbort => { comp => '(?:100|[1-9]?[0-9]):\d+(?::(?:100|[1-9]?[0-9]))?', must => 0, act => 0 },
|
||||
loadStrategy => { comp => '(loadRelease|optPower|smartPower)', must => 0, act => 0 },
|
||||
loadTarget => { comp => '(100|[1-9]?[0-9])', must => 0, act => 0 },
|
||||
loadTarget => { comp => '(?:100|[1-9]?\d)(?::-?(?:[1-9]|1[0-9]|20))?', must => 0, act => 0 },
|
||||
safetyMargin => { comp => '(?:100|[1-9]?\d)(?::(?:100|[1-9]?\d))?', must => 0, act => 0 },
|
||||
weightOwnUse => { comp => '(100|[1-9]?[0-9])', must => 0, act => 0 },
|
||||
};
|
||||
@@ -7708,6 +7714,7 @@ sub _attrBatSocManagement { ## no critic "not used"
|
||||
my $upSoc = $parsed->{upSoc};
|
||||
my $maxSoc = $parsed->{maxSoc};
|
||||
my $barrierSoc = $parsed->{barrierSoc};
|
||||
my $loadTarget = $parsed->{loadTarget};
|
||||
|
||||
unless ($lowSoc < $upSoc && $upSoc < $maxSoc) {
|
||||
return 'The specified values are not plausible. Compare the attribute help.';
|
||||
@@ -7718,6 +7725,12 @@ sub _attrBatSocManagement { ## no critic "not used"
|
||||
return 'The specified values are not plausible. Compare the attribute help.';
|
||||
}
|
||||
}
|
||||
|
||||
if (defined $loadTarget) {
|
||||
unless ($lowSoc <= $loadTarget) {
|
||||
return 'The first value of loadTarget must be higher than lowSoc.';
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
deleteReadingspec ($hash, 'Battery_.*');
|
||||
@@ -9057,15 +9070,6 @@ sub centralTask {
|
||||
# ::CommandDeleteAttr (undef, "$name graphicBeamWidth");
|
||||
#}
|
||||
|
||||
for my $c (1..MAXCONSUMER) { # 23.07.
|
||||
$c = sprintf "%02d", $c;
|
||||
my $surpmeth = ConsumerVal ($hash, $c, 'surpmeth', '');
|
||||
|
||||
if ($surpmeth =~ /^[2-9]$|^1[0-9]$|^20$/xs) {
|
||||
fhem ("set $name attrKeyVal consumer${c} surpmeth=average_${surpmeth}");
|
||||
}
|
||||
}
|
||||
|
||||
for my $bn (1..MAXBATTERIES) { # 02.10.
|
||||
$bn = sprintf "%02d", $bn;
|
||||
readingsDelete ($hash, 'Battery_ChargeRecommended_'.$bn);
|
||||
@@ -11566,17 +11570,19 @@ sub _batSocTarget {
|
||||
## erwartete PV ermitteln & Anteilsfaktor Bat anwenden
|
||||
########################################################
|
||||
my $pvfctm = ReadingsNum ($name, 'Tomorrow_PVforecast', 0); # PV Prognose morgen
|
||||
my $constm = CurrentVal ($name, 'tomorrowConsHoursWithPVGen', 0); # Verbrauch während PV-Erzeugung
|
||||
my $pvfctd = ReadingsNum ($name, 'RestOfDayPVforecast', 0); # PV Prognose Rest heute
|
||||
my $pvexpraw = $pvfctm > $pvfctd ? $pvfctm : $pvfctd - $tdconsset; # erwartete (Rest) PV-Leistung des Tages
|
||||
$pvexpraw = $pvexpraw > 0 ? $pvexpraw : 0; # erwartete PV-Leistung inkl. Verbrauchsprognose bis Sonnenuntergang
|
||||
my $surptd = $pvfctd - $tdconsset; # erwarteter (Rest)Überschuß des aktuellen Tages
|
||||
my $surptm = sprintf "%.0f", ($pvfctm - $constm * 0.5); # anteilig Überschuß am kommenden Tages während PV-Erzeugung -> Platz lassen!
|
||||
my $pvexpraw = $surptm > $surptd ? $surptm : $surptd; # V 1.60.4
|
||||
$pvexpraw = max ($pvexpraw, 0); # erwartete PV-Leistung inkl. Verbrauchsprognose bis Sonnenuntergang
|
||||
|
||||
#my $sf = __batCapShareFactor ($name, $bn); # Anteilsfaktor der Batterie XX Kapazität an Gesamtkapazität
|
||||
my $sf = __batDeficitShareFactor ($name, $bn); # V 1.59.5 Anteilsfaktor der Batterie XX Ladebedarf an Gesamtladebedarf
|
||||
my $pvexpect = $sf * $pvexpraw;
|
||||
|
||||
if ($debug =~ /batteryManagement/xs) {
|
||||
Log3 ($name, 1, "$name DEBUG> SoC Step1 Bat $bn - basics -> Battery share factor of total required load: $sf");
|
||||
Log3 ($name, 1, "$name DEBUG> SoC Step1 Bat $bn - basics -> Expected energy for charging raw: $pvexpraw Wh");
|
||||
Log3 ($name, 1, "$name DEBUG> SoC Step1 Bat $bn - basics -> Expected energy for charging with proportional consumption: $pvexpraw Wh");
|
||||
Log3 ($name, 1, "$name DEBUG> SoC Step1 Bat $bn - basics -> Expected energy for charging after application Share factor: $pvexpect Wh");
|
||||
Log3 ($name, 1, "$name DEBUG> SoC Step1 Bat $bn - compare with SoC history -> preliminary new Target: $target %");
|
||||
}
|
||||
@@ -11627,10 +11633,10 @@ sub _batSocTarget {
|
||||
## Aufladewahrscheinlichkeit beachten
|
||||
#######################################
|
||||
my $csopt = ReadingsNum ($name, 'Battery_OptimumTargetSoC_'.$bn, $lowSoc); # aktuelles SoC Optimum
|
||||
my $cantarget = sprintf "%.0f", (100 - (100 / $batinstcap) * $pvexpect); # berechneter max. möglicher Minimum-SOC nach Berücksichtigung Ladewahrscheinlichkeit
|
||||
my $cantarget = sprintf "%.0f", (100 - $pvexpect * (100 / $batinstcap)); # maximale SOC-Höhe damit prognostizierte Energie komplett gespeichert werden kann
|
||||
my $newtarget = sprintf "%.0f", ($cantarget < $target ? $cantarget : $target); # Abgleich möglicher Minimum-SOC gg. berechneten Minimum-SOC
|
||||
|
||||
debugLog ($paref, 'batteryManagement', "SoC Step3 Bat $bn - basics -> cantarget: $cantarget %, newtarget: $newtarget %");
|
||||
debugLog ($paref, 'batteryManagement', "SoC Step3 Bat $bn - basics -> max SOC so that predicted PV can be stored: $cantarget %, newtarget: $newtarget %");
|
||||
|
||||
if ($newtarget > $careSoc) {
|
||||
$docare = 0; # keine Zwangsanwendung care SoC
|
||||
@@ -11705,10 +11711,11 @@ sub __parseAttrBatSoc {
|
||||
my $name = shift;
|
||||
my $cgbt = shift // return;
|
||||
|
||||
my ($lrMargin, $otpMargin, $barrierSoC, $barrierPar);
|
||||
my ($lrMargin, $otpMargin, $barrierSoC, $barrierPar, $loadTarget, $timeTarget);
|
||||
my ($pa, $ph) = parseParams ($cgbt);
|
||||
($lrMargin, $otpMargin) = split (':', $ph->{safetyMargin}) if(defined $ph->{safetyMargin});
|
||||
($barrierSoC, $barrierPar) = split (':', $ph->{barrierSoC}, 2) if(defined $ph->{barrierSoC});
|
||||
($loadTarget, $timeTarget) = split (':', $ph->{loadTarget}) if(defined $ph->{loadTarget});
|
||||
|
||||
my $parsed = {
|
||||
lowSoc => $ph->{lowSoc},
|
||||
@@ -11719,8 +11726,9 @@ sub __parseAttrBatSoc {
|
||||
lcslot => $ph->{lcSlot},
|
||||
loadAbort => $ph->{loadAbort},
|
||||
loadStrategy => $ph->{loadStrategy},
|
||||
loadTarget => $ph->{loadTarget},
|
||||
weightOwnUse => $ph->{weightOwnUse},
|
||||
loadTarget => $loadTarget,
|
||||
timeTarget => $timeTarget,
|
||||
lrMargin => $lrMargin,
|
||||
otpMargin => $otpMargin,
|
||||
barrierSoc => $barrierSoC, # SoC Barriere ab der eine Ladeleistungssteuerung aktiv sein soll
|
||||
@@ -11880,7 +11888,6 @@ sub _batChargeMgmt {
|
||||
my $bpinreduced = BatteryVal ($name, $bn, 'bpinreduced', 0); # Standardwert bei <=lowSoC -> Anforderungsladung vom Grid
|
||||
my $befficiency = BatteryVal ($name, $bn, 'befficiency', STOREFFDEF) / 100; # Speicherwirkungsgrad
|
||||
my $cgbt = AttrVal ($name, 'ctrlBatSocManagement'.$bn, undef);
|
||||
#my $sf = __batCapShareFactor ($name, $bn); # Anteilsfaktor der Batterie XX Kapazität an Gesamtkapazität
|
||||
my $sf = __batDeficitShareFactor ($name, $bn); # V 1.59.5 Anteilsfaktor Ladungsdefizit
|
||||
$strategy = 'loadRelease'; # 'loadRelease', 'optPower', 'smartPower'
|
||||
my $wou = 0; # Gewichtung Prognose-Verbrauch als Anteil "Eigennutzung" (https://forum.fhem.de/index.php?msg=1348429)
|
||||
@@ -11890,7 +11897,7 @@ sub _batChargeMgmt {
|
||||
my $goalwh = $batinstcap; # initiales Ladeziel (Wh)
|
||||
my $lrMargin = SFTYMARGIN_50;
|
||||
my $otpMargin = SFTYMARGIN_20;
|
||||
my ($lcslot, $barrierPar);
|
||||
my ($lcslot, $barrierPar, $timeTarget);
|
||||
|
||||
if ($cgbt) {
|
||||
my $parsed = __parseAttrBatSoc ($name, $cgbt);
|
||||
@@ -11903,16 +11910,20 @@ sub _batChargeMgmt {
|
||||
$otpMargin = $parsed->{otpMargin} // $otpMargin; # Sicherheitszuschlag OTP (%)
|
||||
$strategy = $parsed->{loadStrategy} // $strategy;
|
||||
$wou = $parsed->{weightOwnUse} // $wou;
|
||||
my $tgt = $parsed->{loadTarget}; # Ladeziel-SoC in %
|
||||
$tgt = $batoptsoc if(defined $tgt && $tgt < $batoptsoc); # Wert Battery_OptimumTargetSoC_XX beachten
|
||||
$goalwh = defined $tgt
|
||||
? sprintf "%.0f", ___batSocPercentToWh ($batinstcap, $tgt)
|
||||
: $goalwh; # Ladeziel-SoC in Wh
|
||||
$timeTarget = $parsed->{timeTarget}; # Uhrzeit (volle Stunde) wann Ladeziel erreicht sein soll
|
||||
my $tgt = $parsed->{loadTarget} // 100; # Ladeziel-SoC in %
|
||||
$tgt = max ($tgt, $batoptsoc); # höheren Wert aus Ziel und optimalen SoC verwenden
|
||||
$goalwh = sprintf "%.0f", ___batSocPercentToWh ($batinstcap, $tgt); # Ladeziel-SoC in Wh
|
||||
}
|
||||
|
||||
my $barrierSocWh = sprintf "%.0f", ___batSocPercentToWh ($batinstcap, $barrierSoc);
|
||||
my $goalpercent = sprintf "%.0f", ___batSocWhToPercent ($batinstcap, $goalwh); # Ladeziel in %
|
||||
|
||||
if (defined $timeTarget && $timeTarget < 0) { # Ladezielzeit relativ zum Sonnenuntergang
|
||||
my $dt = timestringsFromOffset ($tdaysset, $timeTarget * 3600);
|
||||
$timeTarget = int ($dt->{hour}); # Uhrzeit ohne führende 0
|
||||
}
|
||||
|
||||
## generelle Ladeabbruchbedingung evaluieren
|
||||
##############################################
|
||||
if ($loadAbort) {
|
||||
@@ -11920,12 +11931,8 @@ sub _batChargeMgmt {
|
||||
|
||||
$releaseSoC //= $abortSoc;
|
||||
|
||||
if ($csoc >= $abortSoc && $bpowerin <= $abortpin) {
|
||||
$data{$name}{batteries}{$bn}{bloadAbortCond} = 1;
|
||||
}
|
||||
elsif ($csoc < $releaseSoC) {
|
||||
$data{$name}{batteries}{$bn}{bloadAbortCond} = 0;
|
||||
}
|
||||
if ($csoc >= $abortSoc && $bpowerin <= $abortpin) { $data{$name}{batteries}{$bn}{bloadAbortCond} = 1; }
|
||||
elsif ($csoc < $releaseSoC) { $data{$name}{batteries}{$bn}{bloadAbortCond} = 0; }
|
||||
}
|
||||
else {
|
||||
delete $data{$name}{batteries}{$bn}{bloadAbortCond};
|
||||
@@ -11948,14 +11955,13 @@ sub _batChargeMgmt {
|
||||
######################
|
||||
if ($paref->{debug} =~ /batteryManagement/) {
|
||||
Log3 ($name, 1, "$name DEBUG> ChargeMgmt Bat $bn - selected charging strategy: $strategy");
|
||||
Log3 ($name, 1, "$name DEBUG> ChargeMgmt Bat $bn - General load termination condition: $labortCond");
|
||||
Log3 ($name, 1, "$name DEBUG> ChargeMgmt Bat $bn - general load termination condition: $labortCond");
|
||||
Log3 ($name, 1, "$name DEBUG> ChargeMgmt Bat $bn - control time Slot - Slot start: $lcstart, Slot end: $lcend");
|
||||
Log3 ($name, 1, "$name DEBUG> ChargeMgmt Bat $bn - control barrier SoC: $barrierSoc % / $barrierSocWh Wh");
|
||||
Log3 ($name, 1, "$name DEBUG> ChargeMgmt Bat $bn - control barrier Parameter: $barrierPar");
|
||||
Log3 ($name, 1, "$name DEBUG> ChargeMgmt Bat $bn - control barrier Parameter: ".(defined $barrierPar ? $barrierPar : '-'));
|
||||
Log3 ($name, 1, "$name DEBUG> ChargeMgmt Bat $bn - Battery efficiency used: ".($befficiency * 100)." %");
|
||||
Log3 ($name, 1, "$name DEBUG> ChargeMgmt Bat $bn - weighted self-consumption: $wou %");
|
||||
Log3 ($name, 1, "$name DEBUG> ChargeMgmt Bat $bn - charging target: $goalpercent % / $goalwh Wh");
|
||||
#Log3 ($name, 1, "$name DEBUG> ChargeMgmt Bat $bn - Installed Battery capacity: $batinstcap Wh, Percentage of total capacity: ".(sprintf "%.1f", $sf*100)." %");
|
||||
Log3 ($name, 1, "$name DEBUG> ChargeMgmt Bat $bn - Target load and target time: $goalpercent % / $goalwh Wh / ".(defined $timeTarget ? $timeTarget.' oclock' : '-'));
|
||||
Log3 ($name, 1, "$name DEBUG> ChargeMgmt Bat $bn - Percentage of the total amount of charging energy required: ".(sprintf "%.1f", $sf*100)." %");
|
||||
Log3 ($name, 1, "$name DEBUG> ChargeMgmt Bat $bn - The PV generation, consumption and surplus listed below are based on the battery's share of the total amount of charging energy required!");
|
||||
}
|
||||
@@ -12033,13 +12039,14 @@ sub _batChargeMgmt {
|
||||
|
||||
## Steuerung nach Ladefreigabe
|
||||
################################
|
||||
if ( $whneed * (1 + ($lrMargin / 100)) >= $spday ) {$crel = 1} # Ladefreigabe wenn benötigte Ladeenergie >= Restüberschuß des Tages zzgl. Sicherheitsaufschlag
|
||||
if ( $whneed * (1 + ($lrMargin / 100)) >= $spday ) {$crel = 1} # Ladefreigabe wenn benötigte Ladeenergie zzgl. Sicherheitsaufschlag >= Restüberschuß des Tages
|
||||
if ( !$num && ($pvCu - $curcon) >= $inplim ) {$crel = 1} # Ladefreigabe wenn akt. PV Leistung - Abschläge >= WR-Leistungsbegrenzung
|
||||
if ( !$bpin && $gfeedin > $feedinlim ) {$crel = 1} # V 1.49.6 Ladefreigabe wenn akt. keine Bat-Ladung UND akt. Einspeisung > Einspeiselimit der Anlage
|
||||
if ( $bpin && ($gfeedin - $bpin) > $feedinlim ) {$crel = 1} # V 1.49.6 Ladefreigabe wenn akt. Bat-Ladung UND Eispeisung - Bat-Ladung > Einspeiselimit der Anlage
|
||||
if ( !$cgbt ) {$crel = 1} # generelle Ladefreigabe wenn kein BatSoc/Lade-Management
|
||||
if ( !$lcintime ) {$crel = 1} # generelle Ladefreigabe wenn nicht innerhalb Zeitslot für Ladesteuerung
|
||||
if ( $csocwh <= $barrierSocWh) {$crel = 1} # generelle Ladefreigabe wenn aktueller SoC <= Barriere-SoC
|
||||
if ( $whneed <= 0 ) {$crel = 0} # keine Ladefreigabe wenn kein Bedarf, z.B. eingestellter Ziel-SoC erreicht
|
||||
if ( $labortCond ) {$crel = 0} # keine Ladefreigabe bei genereller Abbruchbedingung
|
||||
|
||||
# Steuerhash für optimimierte Ladeleistung erstellen
|
||||
@@ -12050,10 +12057,10 @@ sub _batChargeMgmt {
|
||||
$hsurp->{$fd}{$hod}{nhr} = $nhr;
|
||||
$hsurp->{$fd}{$hod}{speff} = $surpls; # effektiver PV Überschuß bzw. effektiver Verbrauch wenn < 0
|
||||
$hsurp->{$fd}{$hod}{surplswh} = $surplswh.'.'.$hod; # absoluter Überschuß in Wh der Stunde mit Sortierhilfe
|
||||
$hsurp->{$fd}{$hod}{$bn}{spday} = $spday; # (Rest)PV-Überschuß am laufenden Tag
|
||||
$hsurp->{$fd}{$hod}{$bn}{initsocwh} = $socwh; # durch LR fortgeschriebener SoC
|
||||
$hsurp->{$fd}{$hod}{$bn}{batinstcap} = $batinstcap; # installierte Batteriekapazität (Wh)
|
||||
$hsurp->{$fd}{$hod}{$bn}{goalwh} = $goalwh; # Ladeziel
|
||||
$hsurp->{$fd}{$hod}{$bn}{timeTarget} = $timeTarget; # gewünschte Zeit (volle Stunde) für Zielerreichung
|
||||
$hsurp->{$fd}{$hod}{$bn}{bpinmax} = $bpinmax; # max. mögliche Ladeleistung
|
||||
$hsurp->{$fd}{$hod}{$bn}{bpinreduced} = $bpinreduced; # Standardwert bei <=lowSoC -> Anforderungsladung vom Grid
|
||||
$hsurp->{$fd}{$hod}{$bn}{bpoutmax} = $bpoutmax; # max. mögliche Entladeleistung
|
||||
@@ -12147,7 +12154,7 @@ sub _batChargeMgmt {
|
||||
###########################################
|
||||
for my $shod (sort { $a <=> $b } keys %{$hopt}) {
|
||||
my $nhr = $hopt->{$shod}{nhr};
|
||||
my @batteries = grep { !/^(?:fd|speff|surplswh|nhr)$/xs } keys %{$hopt->{24}};
|
||||
my @batteries = grep { !/^(?:fd|speff|surplswh|spday|nhr)$/xs } keys %{$hopt->{24}};
|
||||
|
||||
for my $bat (sort @batteries) {
|
||||
next if(!defined $hopt->{$shod}{$bat}{batinstcap});
|
||||
@@ -12179,24 +12186,25 @@ sub _batChargeMgmt {
|
||||
## Debuglog OTP
|
||||
#################
|
||||
if ($paref->{debug} =~ /batteryManagement/ && $strategy ne 'loadRelease') {
|
||||
my $spday = $hopt->{$shod}{spday};
|
||||
my $lcintime = $hopt->{$shod}{$bat}{lcintime};
|
||||
my $spls = int $hopt->{$shod}{surplswh};
|
||||
my $pneedmin = $hopt->{$shod}{$bat}{pneedmin};
|
||||
my $ttt = $hopt->{$shod}{$bat}{stt};
|
||||
my $crel = $hopt->{$shod}{$bat}{loadrel};
|
||||
my $spday = $hopt->{$shod}{$bat}{spday};
|
||||
my $frefph = $hopt->{$shod}{$bat}{frefph} // '-';
|
||||
my $iter = $hopt->{$shod}{$bat}{iterations} // '-';
|
||||
|
||||
if ($nhr eq '00') {
|
||||
$pneedmin = $otp->{$bat}{target} // 0;
|
||||
my $ratio = $otp->{$bat}{ratio} // '<unknown>';
|
||||
my $remainSurp = $otp->{$bat}{remainingSurp} // '<unknown>';
|
||||
my $achievelog = $hopt->{$shod}{$bat}{achievelog};
|
||||
my $otpMargin = $hopt->{$shod}{$bat}{otpMargin};
|
||||
|
||||
Log3 ($name, 1, "$name DEBUG> ChargeOTP Bat $bat - used safety margin: $otpMargin %");
|
||||
Log3 ($name, 1, "$name DEBUG> ChargeOTP Bat $bat - $achievelog");
|
||||
Log3 ($name, 1, "$name DEBUG> ChargeOTP Bat $bat - current Ratio of surplus / energy requirement to achieve the load target: $ratio %") if($strategy eq 'smartPower');
|
||||
Log3 ($name, 1, "$name DEBUG> ChargeOTP Bat $bat - Ratio of remaining surplus $remainSurp Wh / energy requirement to achieve the load target: $ratio %") if($strategy eq 'smartPower');
|
||||
}
|
||||
|
||||
Log3 ($name, 1, "$name DEBUG> ChargeOTP Bat $bat $ttt - hod:$shod/$nhr, lr/lc:$crel/$lcintime, SocS/E:$ssocwh/$fcendwh Wh, SurpH/D:$spls/$spday Wh, OTP:$pneedmin/$frefph W");
|
||||
@@ -12264,10 +12272,10 @@ sub __batChargeOptTargetPower {
|
||||
#######################################################################
|
||||
my $replacement;
|
||||
|
||||
for my $k (keys %$hsurp) {
|
||||
my $nh = $hsurp->{$k}{nhr};
|
||||
for my $k (sort { $a <=> $b } keys %{$hsurp}) {
|
||||
my $nhr = $hsurp->{$k}{nhr};
|
||||
|
||||
if ($nh eq '00') {
|
||||
if ($nhr eq '00') {
|
||||
my $val = $hsurp->{$k}{surplswh};
|
||||
|
||||
if (defined $val && $val =~ /^(\d+)\.(\w+)$/) {
|
||||
@@ -12275,12 +12283,12 @@ sub __batChargeOptTargetPower {
|
||||
$replacement .= '.'.$2;
|
||||
}
|
||||
|
||||
last; # da Stunde 00 nur einmal vorkommt, können wir abbrechen
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
my $fipl = CurrentVal ($name, 'feedinPowerLimit', INFINITE);
|
||||
my @batteries = grep { !/^(?:fd|speff|surplswh|nhr)$/xs } keys %{$hsurp->{24}};
|
||||
my @batteries = grep { !/^(?:fd|speff|surplswh|spday|nhr)$/xs } keys %{$hsurp->{24}};
|
||||
my @sortedhods = sort { $hsurp->{$a}{surplswh} <=> $hsurp->{$b}{surplswh} } keys %{$hsurp}; # Stunden aufsteigend nach PV-Überschuß sortiert ohne Zeitgewichtung h 00
|
||||
|
||||
my ($fcendwh, $diff, $otp, $ratio);
|
||||
@@ -12293,15 +12301,14 @@ sub __batChargeOptTargetPower {
|
||||
my $nexthod = sprintf "%02d", (int $hod + 1);
|
||||
my $nextnhr = $hsurp->{$nexthod}{nhr};
|
||||
|
||||
my @remaining_hods = grep { int $_ >= int $hod } @sortedhods;
|
||||
my $remainingSurp = 0;
|
||||
my ($remainingSurp_o, $remainingHodsRef_o) = ___batRemainHodsAndSurp ( $hod, # verbleibender Überschuß ohne Zielzeit gesetzt
|
||||
$hsurp,
|
||||
$replacement,
|
||||
\@sortedhods
|
||||
);
|
||||
$hsurp->{$hod}{spday} = $remainingSurp_o; # PV Überschuß Resttag
|
||||
|
||||
for my $h (@remaining_hods) { # Gesamtwert PV-Überschuß aller Stunden mit PV-Überschuß ermitteln
|
||||
my $val = defined $hsurp->{$h}{nhr} && $hsurp->{$h}{nhr} eq '00'
|
||||
? int ($replacement) // 0
|
||||
: $hsurp->{$h}{surplswh};
|
||||
$remainingSurp += int $val;
|
||||
}
|
||||
my ($remainingSurp, $remainingHodsRef);
|
||||
|
||||
for my $sbn (sort { $a <=> $b } @batteries) { # jede Batterie
|
||||
my $bpinmax = $hsurp->{$hod}{$sbn}{bpinmax}; # Bat max. mögliche Ladelesitung
|
||||
@@ -12314,6 +12321,7 @@ sub __batChargeOptTargetPower {
|
||||
my $bpinreduced = $hsurp->{$hod}{$sbn}{bpinreduced}; # Standardwert bei <=lowSoC -> Anforderungsladung vom Grid
|
||||
my $befficiency = $hsurp->{$hod}{$sbn}{befficiency}; # Speicherwirkungsgrad
|
||||
my $strategy = $hsurp->{$hod}{$sbn}{strategy}; # Ladestrategie
|
||||
my $timeTarget = $hsurp->{$hod}{$sbn}{timeTarget}; # gewünschte Zeit (volle Stunde) für Zielerreichung
|
||||
|
||||
# Initialisierung / Fortschreibung Prognose-SOC (Wh)
|
||||
######################################################
|
||||
@@ -12333,19 +12341,31 @@ sub __batChargeOptTargetPower {
|
||||
|
||||
## Ziel und dessen Erreichbarkeit
|
||||
###################################
|
||||
if ($timeTarget) { # verbleibender Überschuß mit Zielzeit gesetzt
|
||||
($remainingSurp, $remainingHodsRef) = ___batRemainHodsAndSurp ( $hod,
|
||||
$hsurp,
|
||||
$replacement,
|
||||
\@sortedhods,
|
||||
$timeTarget
|
||||
);
|
||||
}
|
||||
else {
|
||||
($remainingSurp, $remainingHodsRef) = ($remainingSurp_o, $remainingHodsRef_o);
|
||||
}
|
||||
|
||||
my $goalwh = $hsurp->{$hod}{$sbn}{goalwh}; # Ladeziel
|
||||
my $runwhneed = ($goalwh - $runwh) / $befficiency;
|
||||
my $runwhneed = $goalwh - $runwh;
|
||||
my $achievable = 1;
|
||||
|
||||
if ($runwhneed > 0 && $remainingSurp < $runwhneed) { # Erreichbarkeit des Ziels (benötigte Ladeenergie total) prüfen
|
||||
if ($runwhneed > 0 && $remainingSurp < ($runwhneed / $befficiency)) { # Erreichbarkeit des Ziels (benötigte Ladeenergie total) prüfen
|
||||
$achievable = 0;
|
||||
}
|
||||
|
||||
storeReading ('Battery_TargetAchievable_'.$sbn, $achievable) if($nhr eq '00');
|
||||
|
||||
$hsurp->{$hod}{$sbn}{loadrel} = $runwhneed > 0 ? 1 : 0; # Ladefreigabe abhängig von Ziel-SoC Erfüllung
|
||||
$hsurp->{$hod}{$sbn}{achievelog} = "charging target: $goalwh Wh, remaining: ".
|
||||
(sprintf "%.0f", ($runwhneed * $befficiency)).' Wh -> target likely achievable? '.
|
||||
$hsurp->{$hod}{$sbn}{achievelog} = "charging target: $goalwh Wh, E requirement incl. efficiency: ".
|
||||
(sprintf "%.0f", ($runwhneed / $befficiency)).' Wh -> target likely achievable? '.
|
||||
($achievable ? 'yes' : 'no');
|
||||
|
||||
## kein Überschuß
|
||||
@@ -12383,12 +12403,11 @@ sub __batChargeOptTargetPower {
|
||||
#########################
|
||||
my $otpMargin = $hsurp->{$hod}{$sbn}{otpMargin};
|
||||
my $fref = ___batFindMinPhWh ( { hsurp => $hsurp,
|
||||
hodsref => \@remaining_hods,
|
||||
hodsref => $remainingHodsRef,
|
||||
remainingSurp => $remainingSurp,
|
||||
Ereq => $runwhneed,
|
||||
replacement => $replacement,
|
||||
achievable => $achievable,
|
||||
befficiency => $befficiency,
|
||||
minute => $minute
|
||||
}
|
||||
);
|
||||
@@ -12400,7 +12419,7 @@ sub __batChargeOptTargetPower {
|
||||
? min ($fref->{ph}, $spls) # Ladeleistung auf den kleineren Wert begrenzen (es kommen Nachberechnungen)
|
||||
: $fref->{ph};
|
||||
|
||||
$limpower = $limpower // 0 > 0 ? $limpower / $befficiency : 0; # Zielleistung mit Batterie Effizienzgrad erhöhen
|
||||
$limpower = $limpower // 0 > 0 ? $limpower : 0;
|
||||
$limpower = $bpinmax if(!$hsurp->{$hod}{$sbn}{lcintime});
|
||||
$limpower = max ($limpower, $bpinreduced); # Mindestladeleistung bpinreduced sicherstellen
|
||||
|
||||
@@ -12413,7 +12432,8 @@ sub __batChargeOptTargetPower {
|
||||
$bpinmax,
|
||||
$runwhneed,
|
||||
$otpMargin,
|
||||
$remainingSurp
|
||||
$remainingSurp,
|
||||
$befficiency
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12428,9 +12448,7 @@ sub __batChargeOptTargetPower {
|
||||
}
|
||||
);
|
||||
|
||||
$pneedmin = min ($pneedmin, $bpinmax); # Begrenzung auf max. mögliche Batterieladeleistung
|
||||
$pneedmin = max ($pneedmin, 0);
|
||||
$pneedmin = sprintf "%.0f", $pneedmin;
|
||||
$pneedmin = ___batAdjustEfficiencyAndLimits ($pneedmin, $befficiency, $bpinmax, 0); # Apply Bat Effizienz und Ladeleistungsbegrenzungen
|
||||
|
||||
$hsurp->{$hod}{$sbn}{pneedmin} = $pneedmin;
|
||||
|
||||
@@ -12451,10 +12469,12 @@ sub __batChargeOptTargetPower {
|
||||
$bpinmax,
|
||||
$runwhneed,
|
||||
$otpMargin,
|
||||
$remainingSurp
|
||||
$remainingSurp,
|
||||
$befficiency
|
||||
);
|
||||
|
||||
$otp->{$sbn}{ratio} = sprintf ("%.2f", $ratio);
|
||||
$otp->{$sbn}{remainingSurp} = $remainingSurp;
|
||||
}
|
||||
|
||||
my $gfeedin = CurrentVal ($name, 'gridfeedin', 0); # aktuelle Netzeinspeisung
|
||||
@@ -12476,9 +12496,7 @@ sub __batChargeOptTargetPower {
|
||||
}
|
||||
);
|
||||
|
||||
$target = min ($target, $bpinmax); # Begrenzung auf max. mögliche Batterieladeleistung
|
||||
$target = max ($target, $bpinreduced);
|
||||
$target = sprintf "%.0f", $target;
|
||||
$target = ___batAdjustEfficiencyAndLimits ($target, $befficiency, $bpinmax, $bpinreduced); # Apply Bat Effizienz und Ladeleistungsbegrenzungen
|
||||
|
||||
$otp->{$sbn}{target} = $target;
|
||||
}
|
||||
@@ -12488,7 +12506,7 @@ sub __batChargeOptTargetPower {
|
||||
if ($nhr eq '00') { $diff = min ($spls, $otp->{$sbn}{target} / 60 * (60 - int $minute)) } # aktuelle (Rest)-Stunde -> zeitgewichteter Ladungszufluß
|
||||
else { $diff = min ($spls, $hsurp->{$hod}{$sbn}{pneedmin}) } # kleinster Wert aus PV-Überschuß oder Ladeleistungsbegrenzung
|
||||
|
||||
$runwh = min ($goalwh, $runwh + ($diff * $befficiency)); # Endwert Prognose
|
||||
$runwh = min ($goalwh, $runwh + $diff * $befficiency); # Endwert Prognose
|
||||
$runwh = ___batClampValue ($runwh, $lowSocwh, $batoptsocwh, $batinstcap); # runwh begrenzen
|
||||
$runwh = sprintf "%.0f", $runwh;
|
||||
|
||||
@@ -12504,20 +12522,43 @@ sub __batChargeOptTargetPower {
|
||||
return ($hsurp, $otp);
|
||||
}
|
||||
|
||||
################################################################
|
||||
# Verbleibende Aktivstunden und deren Überschußsumme liefern
|
||||
################################################################
|
||||
sub ___batRemainHodsAndSurp {
|
||||
my ($hod, $hsurp, $replacement, $sortedhodsref, $timeTarget) = @_;
|
||||
|
||||
my @remaining_hods;
|
||||
my $remainingSurp = 0;
|
||||
|
||||
if (defined $timeTarget) { @remaining_hods = grep { int $_ >= int $hod && int $_ <= int $timeTarget } @$sortedhodsref }
|
||||
else { @remaining_hods = grep { int $_ >= int $hod } @$sortedhodsref }
|
||||
|
||||
for my $h (@remaining_hods) { # Gesamtwert PV-Überschuß aller Stunden mit PV-Überschuß ermitteln
|
||||
my $val = defined $hsurp->{$h}{nhr} && $hsurp->{$h}{nhr} eq '00'
|
||||
? int ($replacement) // 0
|
||||
: $hsurp->{$h}{surplswh};
|
||||
|
||||
$remainingSurp += int $val;
|
||||
}
|
||||
|
||||
return ($remainingSurp, \@remaining_hods);
|
||||
}
|
||||
|
||||
################################################################
|
||||
# Zielleistung mit Sicherheitszuschlag behandeln
|
||||
# abfallend proportional zum linearen Rest-Überschuss des Tages
|
||||
# Forum: https://forum.fhem.de/index.php?msg=1349579
|
||||
################################################################
|
||||
sub ___batAdjustPowerByMargin {
|
||||
my ($limpower, $pinmax, $whneed, $otpMargin, $remainingSurp) = @_;
|
||||
my ($limpower, $pinmax, $runwhneed, $otpMargin, $remainingSurp, $befficiency) = @_;
|
||||
|
||||
my $pow;
|
||||
my $ratio = 0;
|
||||
|
||||
return ($limpower, $ratio) if(!defined $whneed || $whneed <= 0);
|
||||
return ($limpower, $ratio) if(!defined $runwhneed || $runwhneed <= 0);
|
||||
|
||||
$ratio = $remainingSurp * 100 / $whneed;
|
||||
$ratio = $remainingSurp * 100 / ($runwhneed / $befficiency);
|
||||
$limpower = min ($limpower, $pinmax); # limpower !> pinmax um invertierte Interpolation zu vermeiden
|
||||
|
||||
if ($limpower <= 0 || !$otpMargin) {$pow = $limpower}
|
||||
@@ -12589,6 +12630,20 @@ sub ___batApplySocAreas {
|
||||
return $ph;
|
||||
}
|
||||
|
||||
################################################################
|
||||
# Endbehandlung einer Leistungsvorgabe für Batterieladung
|
||||
################################################################
|
||||
sub ___batAdjustEfficiencyAndLimits {
|
||||
my ($ph, $eff, $max, $min) = @_;
|
||||
|
||||
$ph /= $eff;
|
||||
$ph = min ($ph, $max); # Begrenzung auf max. mögliche Batterieladeleistung
|
||||
$ph = max ($ph, $min); # Begrenzung auf min. gewünschte Batterieladeleistung
|
||||
$ph = sprintf "%.0f", $ph;
|
||||
|
||||
return $ph;
|
||||
}
|
||||
|
||||
################################################################
|
||||
# Aktionen im Bereich lowSoC <= SoC < barrierSoC
|
||||
################################################################
|
||||
@@ -12636,7 +12691,6 @@ sub ___batFindMinPhWh {
|
||||
my $Ereq = $paref->{Ereq};
|
||||
my $replacement = $paref->{replacement};
|
||||
my $achievable = $paref->{achievable};
|
||||
my $befficiency = $paref->{befficiency};
|
||||
my $minute = $paref->{minute};
|
||||
|
||||
my @hods = @$hodsref;
|
||||
@@ -12653,6 +12707,8 @@ sub ___batFindMinPhWh {
|
||||
: $hsurp->{$_}{surplswh} // 0
|
||||
} @hods;
|
||||
|
||||
$max_cap //= 0;
|
||||
|
||||
return { ph => (sprintf "%.0f", $max_cap), iterations => $loop, blur => (sprintf "%.4f", 0) };
|
||||
}
|
||||
|
||||
@@ -12666,11 +12722,10 @@ sub ___batFindMinPhWh {
|
||||
my $nhr = $hsurp->{$hod}{nhr};
|
||||
next if(!defined $nhr);
|
||||
|
||||
if ($nhr eq '00') { $cap = min ($mid, $hsurp->{$hod}{surplswh}) / 60 * (60 - int $minute)} # Zeitgewichtung aktuelle Stunde
|
||||
else { $cap = min ($mid, $hsurp->{$hod}{surplswh})}
|
||||
if ($nhr eq '00') { $cap = min ($mid, $hsurp->{$hod}{surplswh}) / 60 * (60 - int $minute) } # Zeitgewichtung aktuelle Stunde
|
||||
else { $cap = min ($mid, $hsurp->{$hod}{surplswh}) }
|
||||
|
||||
$cap *= $befficiency;
|
||||
$charged += $cap;
|
||||
$charged += $cap // 0;
|
||||
}
|
||||
|
||||
$charged >= $Ereq ? ($high = $mid) : ($low = $mid);
|
||||
@@ -12728,7 +12783,15 @@ sub ___batChargeSaveResults {
|
||||
if ($loopid eq 'OTP') {
|
||||
if ($nhr eq '00') { # Target für aktuelle Stunde
|
||||
my $needmin = $otp->{$bn}{target} // 0;
|
||||
storeReading ('Battery_ChargeOptTargetPower_'.$bn, $needmin.' W');
|
||||
my ($smoothed, $changed) = smoothValue ( { name => $name,
|
||||
chan => 'OTP',
|
||||
rdg => 'Battery_ChargeOptTargetPower_'.$bn,
|
||||
newval => $needmin,
|
||||
deadband => OTPDEADBAND,
|
||||
alpha => OTPALPHA
|
||||
}
|
||||
);
|
||||
storeReading ('Battery_ChargeOptTargetPower_'.$bn, $smoothed.' W');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12825,6 +12888,9 @@ sub _createSummaries {
|
||||
$restOfDaySum->{Consumption} = $hour00confcremain;
|
||||
|
||||
for my $h (1..MAXNEXTHOURS) {
|
||||
my ($fd, $fh) = calcDayHourMove ($chour, $h);
|
||||
next if($fd > 2);
|
||||
|
||||
my $idx = sprintf "%02d", $h;
|
||||
my $pvfc = NexthoursVal ($name, "NextHour".$idx, 'pvfc', 0);
|
||||
my $confc = NexthoursVal ($name, "NextHour".$idx, 'confc', 0);
|
||||
@@ -12879,6 +12945,19 @@ sub _createSummaries {
|
||||
$daftertomSum->{PV} += $pvfc;
|
||||
$daftertomSum->{Consumption} += $confc;
|
||||
}
|
||||
|
||||
## Summe Verbrauch der Stunden mit PV-Erzeugung am kommenden Tag
|
||||
################################################################## # V 1.60.4
|
||||
if ($fd == 1) { # für den nächsten Tag
|
||||
if ($fh == 0) {
|
||||
delete $data{$name}{current}{tomorrowConsHoursWithPVGen}; # alte Summe bereinigen
|
||||
}
|
||||
else {
|
||||
if ($pvfc) {
|
||||
$data{$name}{current}{tomorrowConsHoursWithPVGen} += $confc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for my $th (1..24) {
|
||||
@@ -25874,38 +25953,9 @@ return $def;
|
||||
}
|
||||
|
||||
###################################################################################################
|
||||
# Wert des current-Hash zurückliefern
|
||||
# Wert des Current-Speichers auslesen
|
||||
# Usage:
|
||||
# CurrentVal ($hash or $name, $key, $def)
|
||||
#
|
||||
# $key: aiinitstate - Initialisierungsstatus der KI
|
||||
# aitrainstate - Traisningsstatus der KI
|
||||
# aiaddistate - Add Instanz Status der KI
|
||||
# ctrunning - aktueller Ausführungsstatus des Central Task
|
||||
# dwdRad1hAge - Alter des Rad1h Wertes als Datumstring
|
||||
# dwdRad1hAgeTS - Alter des Rad1h Wertes als Unix Timestamp
|
||||
# genslidereg - Schieberegister PV Erzeugung (Array)
|
||||
# h4fcslidereg - Schieberegister 4h PV Forecast (Array)
|
||||
# surplusslidereg - Schieberegister PV Überschuß (Array)
|
||||
# moonPhaseI - aktuelle Mondphase (1 .. 8)
|
||||
# batsocslidereg - Schieberegister Batterie SOC (Array)
|
||||
# consumption - aktueller Verbrauch (W)
|
||||
# consumerdevs - alle registrierten Consumerdevices (Array)
|
||||
# consumerCollected - Statusbit Consumer Attr gesammelt und ausgewertet
|
||||
# gridconsumption - aktueller Netzbezug
|
||||
# temp - aktuelle Außentemperatur
|
||||
# surplus - aktueller PV Überschuß
|
||||
# tomorrowconsumption - Verbrauch des kommenden Tages
|
||||
# allstringspeak - Peakleistung aller Strings nach temperaturabhängiger Korrektur
|
||||
# allstringscount - aktuelle Anzahl der Anlagenstrings
|
||||
# tomorrowconsumption - erwarteter Gesamtverbrauch am morgigen Tag
|
||||
# sunriseToday - Sonnenaufgang heute
|
||||
# sunriseTodayTs - Sonnenaufgang heute Unix Timestamp
|
||||
# sunsetToday - Sonnenuntergang heute
|
||||
# sunsetTodayTs - Sonnenuntergang heute Unix Timestamp
|
||||
#
|
||||
# $def: Defaultwert
|
||||
#
|
||||
###################################################################################################
|
||||
sub CurrentVal {
|
||||
my $name = shift;
|
||||
@@ -26327,8 +26377,131 @@ sub StatusAPIVal {
|
||||
return $def;
|
||||
}
|
||||
|
||||
################################################################
|
||||
# Glättung des übergebenen Wertes $newval
|
||||
# $chan - ID der Glättungsgruppe
|
||||
# SM_new als Key/Value-Liste übergeben
|
||||
################################################################
|
||||
sub smoothValue {
|
||||
my $paref = shift;
|
||||
|
||||
my $name = $paref->{name};
|
||||
my $chan = $paref->{chan};
|
||||
my $rdg = $paref->{rdg};
|
||||
my $newval = $paref->{newval} // return;
|
||||
my $deadband = $paref->{deadband} // OTPDEADBAND;
|
||||
my $alpha = $paref->{alpha} // OTPALPHA;
|
||||
|
||||
my $hash = $defs{$name};
|
||||
my $vold = ReadingsNum ($name, $rdg, 0);
|
||||
my $changed = 0;
|
||||
|
||||
$data{$name}{current}{smoother}{$chan}{$rdg}{OLD} = $vold;
|
||||
$data{$name}{current}{smoother}{$chan}{$rdg}{NEWVAL} = $newval;
|
||||
$data{$name}{current}{smoother}{$chan}{$rdg}{ALPHA} = $alpha;
|
||||
$data{$name}{current}{smoother}{$chan}{$rdg}{DEADBAND} = $deadband;
|
||||
|
||||
unless ($data{$name}{current}{smoother}{$chan}{$rdg}{OBJ}) {
|
||||
$data{$name}{current}{smoother}{$chan}{$rdg}{OBJ} = FHEM::SolarForecast::Smoother->SM_new ( initial => $vold,
|
||||
deadband => $deadband,
|
||||
alpha => $alpha
|
||||
);
|
||||
}
|
||||
|
||||
my $s = $data{$name}{current}{smoother}{$chan}{$rdg}{OBJ};
|
||||
|
||||
unless (blessed ($s)) {
|
||||
Log3 ($name, 1, "$name - ERROR - Wrong Smoother class: ".ref($s));
|
||||
|
||||
$changed = 1 if($newval != $vold);
|
||||
return ($newval, $changed);
|
||||
}
|
||||
|
||||
my $smoothed = $s->SM_update ($newval);
|
||||
$changed = 1 if($smoothed != $vold);
|
||||
|
||||
$data{$name}{current}{smoother}{$chan}{$rdg}{SMOOTHED} = $smoothed;
|
||||
$data{$name}{current}{smoother}{$chan}{$rdg}{CHANGED} = $changed;
|
||||
|
||||
return ($smoothed, $changed);
|
||||
}
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
# Smoother - Glättungsfilter
|
||||
# Kleine Änderungen (<= X) werden ignoriert und nur bei größeren Änderungen
|
||||
# auf einen neuen Wert reagiert. Der Übergang kann glatt
|
||||
# (anstatt eines plötzlichen Sprungs) erfolgen.
|
||||
#
|
||||
# deadband: Schwellwert X; Änderungen ≤ X werden nicht als neuer Zielwert übernommen.
|
||||
# step-smoothing: Wenn Änderung > X, kann der aktuelle Wert entweder sofort auf den neuen gesetzt werden oder
|
||||
# schrittweise (mit Faktor alpha) Richtung Ziel gleiten.
|
||||
|
||||
# konfigurierbar: X und Alpha sind Parameter.
|
||||
#####################################################################################################################
|
||||
|
||||
package FHEM::SolarForecast::Smoother;
|
||||
|
||||
sub SM_new {
|
||||
my ($class, %opts) = @_;
|
||||
my $self = {
|
||||
value => $opts{initial} // 0, # aktueller (ausgegebener) Wert
|
||||
deadband => $opts{deadband} // 0.5, # X: minimale Änderungsamplitude (Schwellenwert für Ergebnisanpassung)
|
||||
alpha => defined $opts{alpha} ? $opts{alpha} : 1.0, # 1.0 = sofortiger Sprung; <1 = gleitend
|
||||
};
|
||||
|
||||
bless $self, $class;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub SM_update { # update mit neuem Messwert; gibt den geglätteten Wert zurück
|
||||
my ($self, $newval) = @_;
|
||||
return $self->{value} unless defined $newval;
|
||||
|
||||
my $current = $self->{value};
|
||||
my $diff = $newval - $current;
|
||||
my $abs = $diff >= 0 ? $diff : -$diff;
|
||||
|
||||
if ($abs <= $self->{deadband}) { # innerhalb der deadband: kein Wechsel
|
||||
return $current;
|
||||
}
|
||||
|
||||
if ($self->{alpha} >= 1) { $self->{value} = $newval } # außerhalb deadband: Ziel ist der neue Messwert; gleiten je nach alpha
|
||||
else { $self->{value} = $current + $self->{alpha} * $diff }
|
||||
|
||||
return $self->{value};
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Hilfs-Accessoren
|
||||
# Routinen arbeiten auf dem Objekt-Hash (typisches Perl-OO mit blessed Hash):
|
||||
# $_[0] ist das Objekt, $_[1] das übergebene Argument.
|
||||
#
|
||||
#
|
||||
# # my $s = $hash->{HELPER}{SMOOTHER}{$chan};
|
||||
# unless ($s) {
|
||||
# return;
|
||||
# }
|
||||
# Getter (akt. Wert lesen):
|
||||
# my $current = $s->SM_getValue;
|
||||
#
|
||||
# Setter zur Änderung von Objektwerten:
|
||||
# my $dead = 0.5;
|
||||
# my $alpha = 1.0;
|
||||
# my $value = 222;
|
||||
# $s->SM_setDeadband ($dead);
|
||||
# $s->SM_setAlpha ($alpha);
|
||||
# $s->SM_setValue ($value);
|
||||
################################################################################
|
||||
sub SM_getValue { $_[0]->{value} } # gibt den aktuellen geglätteten Wert zurück
|
||||
sub SM_setValue { $_[0]->{value} = $_[1] } # setzt einen neuen Vergleichswert im Objekt
|
||||
sub SM_setDeadband { $_[0]->{deadband} = $_[1] } # setzt den Deadband-Parameter des Objekts
|
||||
sub SM_setAlpha { $_[0]->{alpha} = $_[1] } # setzt den Alpha-Parameter des Objekts
|
||||
|
||||
1;
|
||||
|
||||
|
||||
=pod
|
||||
=item summary Visualization of solar predictions for PV systems and Consumer control
|
||||
=item summary_DE Visualisierung von solaren Vorhersagen für PV Anlagen und Verbrauchersteuerung
|
||||
@@ -27785,11 +27958,14 @@ to ensure that the system configuration is correct.
|
||||
<tr><td> </td><td>For more information on selecting a strategy, see german <a href="https://wiki.fhem.de/wiki/SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#Welche_Ladestrategie_soll_ich_w%C3%A4hlen?_-_eine_M%C3%B6glichkeit_zur_Best-Practice_Findung_mit_Codebeispiel">Wiki</a>. </td></tr>
|
||||
<tr><td> </td><td>Value: <b>loadRelease</b> | <b>optPower</b> | <b>smartPower</b>, default: loadRelease </td></tr>
|
||||
<tr><td> </td><td> </td></tr>
|
||||
<tr><td> <b>loadTarget</b> </td><td>Optional target SoC in % for calculating charge release or optimal charging power. </td></tr>
|
||||
<tr><td> </td><td>The target value is a calculated figure. The actual SoC may be higher or lower than this within </td></tr>
|
||||
<tr><td> </td><td>certain limits, depending on the situation. The higher value from Reading </td></tr>
|
||||
<tr><td> </td><td><b>Battery_OptimumTargetSoC_XX</b> and 'loadTarget' takes precedence for the calculation. </td></tr>
|
||||
<tr><td> </td><td>Value: <b>0..100</b>, default: 100 </td></tr>
|
||||
<tr><td> <b>loadTarget</b> </td><td>Optional target SoC (%), target time for calculating charge release, and optimal charging power.</td></tr>
|
||||
<tr><td> </td><td>The specified target SoC must be greater than the value of 'lowSoC'. A higher value in the </td></tr>
|
||||
<tr><td> </td><td>reading <b>Battery_OptimumTargetSoC_XX</b> takes precedence over the parameter setting. </td></tr>
|
||||
<tr><td> </td><td>A specified target time is the full hour (1..20) or, as a negative value (-20..-1), the </td></tr>
|
||||
<tr><td> </td><td>last full hour before sunset minus this value. </td></tr>
|
||||
<tr><td> </td><td>Syntax: <b><Target SoC>[:<Target time>]</b> </td></tr>
|
||||
<tr><td> </td><td>Value range Target SoC: <b>lowSoc..100</b>, default: 100 </td></tr>
|
||||
<tr><td> </td><td>Value range Target time: <b>-20..20</b>(without leading zero), default: undefined </td></tr>
|
||||
<tr><td> </td><td> </td></tr>
|
||||
<tr><td> <b>safetyMargin</b> </td><td>When calculating the load clearance and optimized load capacity, safety margins are taken </td></tr>
|
||||
<tr><td> </td><td>into account in the predicted load requirements. </td></tr>
|
||||
@@ -27811,7 +27987,7 @@ to ensure that the system configuration is correct.
|
||||
All SoC values are whole numbers in %. The following applies: 'lowSoc' < 'upSoC' < 'maxSoC'. <br><br>
|
||||
|
||||
<b>Example: </b> <br>
|
||||
attr <name> ctrlBatSocManagement01 lowSoc=10 upSoC=50 maxSoC=99 careCycle=25 lcSlot=11:00-17:30 loadAbort=99:40:90 safetyMargin=30 weightOwnUse=20 <br>
|
||||
attr <name> ctrlBatSocManagement01 lowSoc=10 upSoC=50 maxSoC=99 careCycle=25 lcSlot=11:00-17:30 loadAbort=99:40:90 safetyMargin=30 weightOwnUse=20 loadTarget=90:-2 <br>
|
||||
</li>
|
||||
<br>
|
||||
|
||||
@@ -28543,7 +28719,7 @@ to ensure that the system configuration is correct.
|
||||
<tr><td><b>efficiency</b> </td><td>Optional specification of the energy storage efficiency in %. This efficiency describes not </td></tr>
|
||||
<tr><td> </td><td>only the battery itself, but also the chain of effects, including the inverters involved. </td></tr>
|
||||
<tr><td> </td><td>Depending on the type of coupling and other factors, the typical efficiency is between 75 and 90%. </td></tr>
|
||||
<tr><td> </td><td>Value: <b>0..100</b> default: 87 </td></tr>
|
||||
<tr><td> </td><td>Value: <b>1..100</b> default: 87 </td></tr>
|
||||
<tr><td> </td><td> </td></tr>
|
||||
<tr><td> <b>icon</b> </td><td>Icon and/or (only) color of the battery in the bar graph according to the status (optional). </td></tr>
|
||||
<tr><td> </td><td>The identifier (e.g. blue), HEX value (e.g. #d9d9d9) or 'dyn' can be specified as the color. </td></tr>
|
||||
@@ -30566,11 +30742,14 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
||||
<tr><td> </td><td>Weitere Informationen zur Auswahl der Strategie siehe <a href="https://wiki.fhem.de/wiki/SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#Welche_Ladestrategie_soll_ich_w%C3%A4hlen?_-_eine_M%C3%B6glichkeit_zur_Best-Practice_Findung_mit_Codebeispiel">Wiki</a>. </td></tr>
|
||||
<tr><td> </td><td>Wert: <b>loadRelease</b> | <b>optPower</b> | <b>smartPower</b>, default: loadRelease </td></tr>
|
||||
<tr><td> </td><td> </td></tr>
|
||||
<tr><td> <b>loadTarget</b> </td><td>Optionaler Ziel-SoC in % für die Berechnung der Ladefreigabe bzw. der optimalen Ladeleistung. </td></tr>
|
||||
<tr><td> </td><td>Der Zielwert ist eine kalkulatorische Rechengröße. Der reale SoC kann situativ in Grenzen </td></tr>
|
||||
<tr><td> </td><td>über- oder unterschritten werden. Der höhere Wert aus Reading <b>Battery_OptimumTargetSoC_XX</b></td></tr>
|
||||
<tr><td> </td><td>und 'loadTarget' hat für die Berechnung Vorrang. </td></tr>
|
||||
<tr><td> </td><td>Wert: <b>0..100</b>, default: 100 </td></tr>
|
||||
<tr><td> <b>loadTarget</b> </td><td>Optionaler Ziel-SoC (%), Zielzeit zur Berechnung der Ladefreigabe und optimalen Ladeleistung. </td></tr>
|
||||
<tr><td> </td><td>Der angegebene Ziel-SoC muß größer als der Wert von 'lowSoC' sein. Ein höherer Wert im Reading </td></tr>
|
||||
<tr><td> </td><td><b>Battery_OptimumTargetSoC_XX</b> gegenüber der Parametervorgabe hat Vorrang. </td></tr>
|
||||
<tr><td> </td><td>Eine angegebene Zielzeit ist die volle Stunde (1..20) oder als negativer Wert (-20..-1) die </td></tr>
|
||||
<tr><td> </td><td>letzte volle Stunde vor dem Sonnenuntergang abzüglich diesem Wert. </td></tr>
|
||||
<tr><td> </td><td>Syntax: <b><Ziel-SoC>[:<Zielzeit>]</b> </td></tr>
|
||||
<tr><td> </td><td>Wertebereich Ziel-SoC: <b>lowSoc..100</b>, default: 100 </td></tr>
|
||||
<tr><td> </td><td>Wertebereich Zielzeit: <b>-20..20</b> (ohne führende Null), default: undefiniert </td></tr>
|
||||
<tr><td> </td><td> </td></tr>
|
||||
<tr><td> <b>safetyMargin</b> </td><td>Bei der Berechnung der Ladefreigabe und optimierten Ladeleistung werden Sicherheitszuschläge </td></tr>
|
||||
<tr><td> </td><td>auf den prognostizierten Ladungsbedarf berücksichtigt. </td></tr>
|
||||
@@ -30592,7 +30771,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
||||
Alle SoC-Werte sind ganze Zahlen in %. Dabei gilt: 'lowSoc' < 'upSoC' < 'maxSoC'. <br><br>
|
||||
|
||||
<b>Beispiel: </b> <br>
|
||||
attr <name> ctrlBatSocManagement01 lowSoc=10 upSoC=50 maxSoC=99 careCycle=25 lcSlot=11:00-17:30 loadAbort=99:40:90 safetyMargin=30 weightOwnUse=20 <br>
|
||||
attr <name> ctrlBatSocManagement01 lowSoc=10 upSoC=50 maxSoC=99 careCycle=25 lcSlot=11:00-17:30 loadAbort=99:40:90 safetyMargin=30 weightOwnUse=20 loadTarget=90:-2 <br>
|
||||
</li>
|
||||
<br>
|
||||
|
||||
@@ -31321,7 +31500,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
||||
<tr><td><b>efficiency</b> </td><td>Optionale Angabe des Wirkungsgrades der Energiespeicherung in %. Dieser Wirkungsgrad beschreibt nicht </td></tr>
|
||||
<tr><td> </td><td>nur die Batterie selbst, sondern die Wirkkette inkl. der betroffenen Wechselrichter. </td></tr>
|
||||
<tr><td> </td><td>Je nach Koppelart und anderen Faktoren liegt der typische Wirkungsgrad zwischen 75 - 90 %. </td></tr>
|
||||
<tr><td> </td><td>Wert: <b>0..100</b> default: 87 </td></tr>
|
||||
<tr><td> </td><td>Wert: <b>1..100</b> default: 87 </td></tr>
|
||||
<tr><td> </td><td> </td></tr>
|
||||
<tr><td> <b>icon</b> </td><td>Icon und/oder (nur) Farbe der Batterie in der Balkengrafik entsprechend des Status (optional). </td></tr>
|
||||
<tr><td> </td><td>Als Farbe kann der Bezeichner (z.B. blue), HEX-Wert (z.B. #d9d9d9) oder 'dyn' angegeben werden. </td></tr>
|
||||
|
||||
Reference in New Issue
Block a user