diff --git a/fhem/CHANGED b/fhem/CHANGED
index 3ae94dbe9..96efb61e2 100644
--- a/fhem/CHANGED
+++ b/fhem/CHANGED
@@ -1,5 +1,7 @@
# 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: change Attr setupBatteryDev to setupBatteryDev01,
+ new getter valBattery
- bugfix: 88_HMCCU: Role STATUS_INDICATOR
- feature: 76_SolarForecast: consumer key surpmeth - various surplus variants
- change: 76_SolarForecast: improve Bat careSoC management when dark doldrums
diff --git a/fhem/FHEM/76_SolarForecast.pm b/fhem/FHEM/76_SolarForecast.pm
index 28233e294..dc4ca4fc6 100644
--- a/fhem/FHEM/76_SolarForecast.pm
+++ b/fhem/FHEM/76_SolarForecast.pm
@@ -158,7 +158,8 @@ BEGIN {
# Versions History intern
my %vNotesIntern = (
- "1.40.0" => "21.12.2024 new consumer key 'surpmeth' to calculate surplus in various variants for cunsumer switching ",
+ "1.41.0" => "28.12.2024 _batSocTarget: minor code change, change setupBatteryDev to setupBatteryDev01, getter valBattery ",
+ "1.40.0" => "21.12.2024 new consumer key 'surpmeth' to calculate surplus in various variants for consumer switching ",
"1.39.8" => "21.12.2024 prepare of new consumer key 'surpmeth', _batSocTarget: improve care SoC management when dark doldrums ",
"1.39.7" => "18.12.2024 ConsumptionRecommended calc method medianArray, change local owndata to global data ",
"1.39.6" => "17.12.2024 replace global data-store by local owndata-store, remove sub _composeRemoteObj, delHashRefDeep removed ".
@@ -404,6 +405,7 @@ my $tempcoeffdef = -0.45;
my $tempmodinc = 25; # default Temperaturerhöhung an Solarzellen gegenüber Umgebungstemperatur bei wolkenlosem Himmel
my $tempbasedef = 25; # Temperatur Module bei Nominalleistung
+my $maxbatteries = 1; # maximale Anzahl der möglichen Batterien
my $maxconsumer = 16; # maximale Anzahl der möglichen Consumer (Attribut)
my $maxproducer = 3; # maximale Anzahl der möglichen anderen Produzenten (Attribut)
my $maxinverter = 3; # maximale Anzahl der möglichen Inverter
@@ -515,7 +517,7 @@ my @aconfigs = qw( affectBatteryPreferredCharge affectConsForecastIdentWeekdays
graphicHeaderDetail graphicHeaderShow graphicHistoryHour graphicHourCount graphicHourStyle
graphicLayoutType graphicSelect graphicShowDiff graphicShowNight graphicShowWeather
graphicSpaceSize graphicWeatherColor graphicWeatherColorNight
- setupMeterDev setupBatteryDev setupInverterStrings setupRadiationAPI setupStringPeak
+ setupMeterDev setupInverterStrings setupRadiationAPI setupStringPeak
setupRoofTops
);
@@ -525,6 +527,11 @@ for my $cn (1..$maxconsumer) {
push @dd, "consumerSwitching${cn}"; # ctrlDebug: add specific Consumer
}
+for my $bn (1..$maxbatteries) {
+ $bn = sprintf "%02d", $bn;
+ push @aconfigs, "setupBatteryDev${bn}"; # Anlagenkonfiguration: add Battery Attribute
+}
+
for my $in (1..$maxinverter) {
$in = sprintf "%02d", $in;
push @aconfigs, "setupInverterDev${in}"; # Anlagenkonfiguration: add Inverter Attribute
@@ -580,6 +587,7 @@ my %hget = ( # Ha
data => { fn => \&_getdata, needcred => 0 },
html => { fn => \&_gethtml, needcred => 0 },
ftui => { fn => \&_getftui, needcred => 0 },
+ valBattery => { fn => \&_getlistvalBattery, needcred => 0 },
valCurrent => { fn => \&_getlistCurrent, needcred => 0 },
valInverter => { fn => \&_getlistvalInverter, needcred => 0 },
valProducer => { fn => \&_getlistvalProducer, needcred => 0 },
@@ -608,8 +616,6 @@ my %hattr = ( # H
setupWeatherDev2 => { fn => \&_attrWeatherDev },
setupWeatherDev3 => { fn => \&_attrWeatherDev },
setupMeterDev => { fn => \&_attrMeterDev },
- setupBatteryDev => { fn => \&_attrBatteryDev },
- setupInverterDev => { fn => \&_attrInverterDev },
setupInverterStrings => { fn => \&_attrInverterStrings },
setupRadiationAPI => { fn => \&_attrRadiationAPI },
setupStringPeak => { fn => \&_attrStringPeak },
@@ -617,6 +623,11 @@ my %hattr = ( # H
flowGraphicControl => { fn => \&_attrflowGraphicControl },
);
+ for my $bn (1..$maxbatteries) {
+ $bn = sprintf "%02d", $bn;
+ $hattr{'setupBatteryDev'.$bn}{fn} = \&_attrBatteryDev;
+ }
+
for my $in (1..$maxinverter) {
$in = sprintf "%02d", $in;
$hattr{'setupInverterDev'.$in}{fn} = \&_attrInverterDev;
@@ -1179,12 +1190,17 @@ sub Initialize {
my $srd = join ",", sort keys (%hcsr);
my $gbc = 'pvReal,pvForecast,consumption,consumptionForecast,gridconsumption,energycosts,gridfeedin,feedincome';
- my ($consumer, $setupprod, $setupinv, @allc);
+ my ($consumer, $setupbat, $setupprod, $setupinv, @allc);
for my $c (1..$maxconsumer) {
$c = sprintf "%02d", $c;
$consumer .= "consumer${c}:textField-long ";
push @allc, $c;
}
+
+ for my $bn (1..$maxbatteries) {
+ $bn = sprintf "%02d", $bn;
+ $setupbat .= "setupBatteryDev${bn}:textField-long ";
+ }
for my $in (1..$maxinverter) {
$in = sprintf "%02d", $in;
@@ -1276,9 +1292,9 @@ sub Initialize {
"setupWeatherDev2 ".
"setupWeatherDev3 ".
"setupRoofTops ".
- "setupBatteryDev:textField-long ".
"setupRadiationAPI ".
"setupStringPeak ".
+ $setupbat.
$setupinv.
$setupprod.
$consumer.
@@ -1295,11 +1311,8 @@ sub Initialize {
# $hash->{FW_addDetailToSummary} = 1;
# $hash->{FW_atPageEnd} = 1; # wenn 1 -> kein Longpoll ohne informid in HTML-Tag
- $hash->{AttrRenameMap} = { "graphicBeamHeight" => "graphicBeamHeightLevel1", # 07.05.24
- "ctrlWeatherDev1" => "setupWeatherDev1", # 20.08.24
- "ctrlWeatherDev2" => "setupWeatherDev2",
- "ctrlWeatherDev3" => "setupWeatherDev3",
- "setupInverterDev" => "setupInverterDev01", # 11.10.24
+ $hash->{AttrRenameMap} = { "setupBatteryDev" => "setupBatteryDev01", # 28.12.24
+ "setupInverterDev" => "setupInverterDev01", # 11.10.24
};
eval { FHEM::Meta::InitMod( __FILE__, $hash ) }; ## no critic 'eval'
@@ -2318,6 +2331,7 @@ sub Get {
my @pha = map {sprintf "%02d", $_} sort {$a<=>$b} keys %{$data{$name}{pvhist}};
my @vcm = map {sprintf "%02d", $_} sort {$a<=>$b} keys %{$data{$name}{consumers}};
+ my @vba = map {sprintf "%02d", $_} sort {$a<=>$b} keys %{$data{$name}{batteries}};
my @vin = map {sprintf "%02d", $_} sort {$a<=>$b} keys %{$data{$name}{inverters}};
my @vpn = map {sprintf "%02d", $_} sort {$a<=>$b} keys %{$data{$name}{producers}};
my @vst = sort keys %{$data{$name}{strings}};
@@ -2325,11 +2339,13 @@ sub Get {
my $hol = join ",", @ho;
my $pvl = join ",", @pha;
my $cml = join ",", @vcm;
+ my $bal = join ",", @vba;
my $inl = join ",", @vin;
my $pnl = join ",", @vpn;
my $str = join ",", @vst;
my $getlist = "Unknown argument $opt, choose one of ".
+ "valBattery:#,$bal ".
"valConsumerMaster:#,$cml ".
"valInverter:#,$inl ".
"valProducer:#,$pnl ".
@@ -4518,6 +4534,21 @@ sub _getlistCurrent {
return $ret;
}
+###############################################################
+# Getter valBattery
+###############################################################
+sub _getlistvalBattery {
+ my $paref = shift;
+ my $name = $paref->{name};
+ my $arg = $paref->{arg};
+ my $hash = $defs{$name};
+
+ my $ret = listDataPool ($hash, 'batteries', $arg);
+ $ret .= lineFromSpaces ($ret, 30);
+
+return $ret;
+}
+
###############################################################
# Getter valConsumerMaster
###############################################################
@@ -5302,8 +5333,8 @@ sub Attr {
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.}
- if(!CurrentVal($hash, 'batinstcap', 0)); # https://forum.fhem.de/index.php?msg=1310930
+ return qq{Define the key 'cap' with "attr $name setupBatteryDev01" before this attribute in the correct form.}
+ if(!BatteryVal ($hash, '01', 'binstcap', 0)); # https://forum.fhem.de/index.php?msg=1310930
my ($lowSoc, $upSoc, $maxsoc, $careCycle) = __parseAttrBatSoc ($name, $aVal);
@@ -5836,9 +5867,7 @@ sub _attrInverterDev { ## no critic "not used"
delete $data{$name}{inverters}{$in}{ifeed};
}
elsif ($paref->{cmd} eq 'del') {
- for my $k (keys %{$data{$name}{inverters}}) {
- delete $data{$name}{inverters}{$k} if($k eq $in);
- }
+ delete $data{$name}{inverters}{$in};
readingsDelete ($hash, 'Current_PV');
undef @{$data{$name}{current}{genslidereg}};
@@ -5992,7 +6021,8 @@ sub _attrBatteryDev { ## no critic "not used"
return if(!$init_done);
- my $hash = $defs{$name};
+ my $hash = $defs{$name};
+ my $bn = (split 'setupBatteryDev', $aName)[1];
if ($paref->{cmd} eq 'set') {
my ($err, $badev, $h) = isDeviceValid ( { name => $name, obj => $aVal, method => 'string' } );
@@ -6010,6 +6040,8 @@ sub _attrBatteryDev { ## no critic "not used"
if ($h->{pin} eq "-pout" && $h->{pout} eq "-pin") {
return qq{Incorrect input. It is not allowed that the keys pin and pout refer to each other.};
}
+
+ delete $data{$name}{batteries}{$bn}{basynchron};
}
elsif ($paref->{cmd} eq 'del') {
readingsDelete ($hash, 'Current_PowerBatIn');
@@ -6025,11 +6057,7 @@ sub _attrBatteryDev { ## no critic "not used"
delete $data{$name}{circular}{99}{batintot};
delete $data{$name}{circular}{99}{batouttot};
- delete $data{$name}{current}{powerbatout};
- delete $data{$name}{current}{powerbatin};
- delete $data{$name}{current}{batcharge};
- delete $data{$name}{current}{batinstcap};
- delete $data{$name}{current}{batasynchron};
+ delete $data{$name}{batteries}{$bn};
}
InternalTimer (gettimeofday() + 2, 'FHEM::SolarForecast::createAssociatedWith', $hash, 0);
@@ -6210,7 +6238,7 @@ sub Notify {
my $debug = getDebug ($myHash); # Debug Mode
- my ($err, $medev, $badev, $h, $async);
+ my ($err, $medev, $bname, $iname, $h, $async);
## Meter Event?
#################
@@ -6241,11 +6269,12 @@ sub Notify {
## Battery Event?
###################
- ($err, $badev, $h) = isDeviceValid ( { name => $myName, obj => 'setupBatteryDev', method => 'attr' } );
+ for my $bn (1..$maxbatteries) {
+ $bn = sprintf "%02d", $bn;
+ $bname = BatteryVal ($myHash, $bn, 'bname', '');
- if (!$err) {
- if ($devName eq $badev) {
- $async = $h->{asynchron} // 0;
+ if ($devName eq $bname) {
+ $async = BatteryVal ($myHash, $bn, 'basynchron', 0);
if ($debug =~ /notifyHandling/x) {
Log3 ($myName, 1, qq{$myName DEBUG> notifyHandling - Event of Battery device >$devName< received - asynchronous mode: $async});
@@ -6264,22 +6293,23 @@ sub Notify {
return;
}
}
+
}
## Inverter Event?
####################
for my $in (1..$maxinverter) {
- $in = sprintf "%02d", $in;
- my $iname = InverterVal ($myHash, $in, 'iname', '');
+ $in = sprintf "%02d", $in;
+ $iname = InverterVal ($myHash, $in, 'iname', '');
if ($devName eq $iname) {
- my $iasync = InverterVal ($myHash, $in, 'iasynchron', 0);
+ $async = InverterVal ($myHash, $in, 'iasynchron', 0);
if ($debug =~ /notifyHandling/x) {
- Log3 ($myName, 1, qq{$myName DEBUG> notifyHandling - Event of Inverter device >$devName< received - asynchronous mode: $iasync});
+ Log3 ($myName, 1, qq{$myName DEBUG> notifyHandling - Event of Inverter device >$devName< received - asynchronous mode: $async});
}
- if ($iasync) {
+ if ($async) {
if (CurrentVal ($myHash, 'ctrunning', 0)) {
if ($debug =~ /notifyHandling/x) {
Log3 ($myName, 1, qq{$myName DEBUG> notifyHandling - central task was called from NOTIFY when it is already running ... end this call});
@@ -6302,7 +6332,6 @@ sub Notify {
if (@consumers && grep /^$devName$/, @consumers) {
my ($cname, $cindex, $dswname);
- my $type = $myHash->{TYPE};
for my $c (sort{$a<=>$b} keys %{$data{$myName}{consumers}}) {
($err, $cname, $dswname) = getCDnames ($myHash, $c);
@@ -7286,6 +7315,9 @@ sub centralTask {
Log3 ($name, 3, "$name - INFO - central task was called when it was already running ... end this call");
return;
}
+
+ $data{$name}{current}{ctrunning} = 1; # Central Task running Statusbit
+ InternalTimer (gettimeofday() + 1.2, "FHEM::SolarForecast::releaseCentralTask", $hash, 0); # Freigabe centralTask
my $t = time; # aktuelle Unix-Zeit
my $date = strftime "%Y-%m-%d", localtime($t); # aktuelles Datum
@@ -7293,11 +7325,7 @@ sub centralTask {
my $minute = strftime "%M", localtime($t); # aktuelle Minute (00-59)
my $day = strftime "%d", localtime($t); # aktueller Tag (range 01 .. 31)
my $dayname = strftime "%a", localtime($t); # aktueller Wochentagsname
- my $debug = getDebug ($hash); # Debug Module
-
- $data{$name}{current}{ctrunning} = 1; # Central Task running Statusbit
-
- InternalTimer (gettimeofday() + 1.2, "FHEM::SolarForecast::releaseCentralTask", $hash, 0); # Freigabe centralTask
+ my $debug = getDebug ($hash); # Debug Module
my $centpars = {
name => $name,
@@ -8435,9 +8463,9 @@ sub _transferInverterValues {
storeReading ('Current_PV', $pvsum.' W');
storeReading ('Today_Hour'.sprintf("%02d",$nhour).'_PVreal', $ethishoursum.' Wh'.$warn);
- $data{$name}{circular}{sprintf("%02d",$nhour)}{pvrl} = $ethishoursum; # Ringspeicher PV real Forum: https://forum.fhem.de/index.php/topic,117864.msg1133350.html#msg1133350
+ $data{$name}{circular}{sprintf("%02d",$nhour)}{pvrl} = $ethishoursum; # Ringspeicher PV real Forum: https://forum.fhem.de/index.php/topic,117864.msg1133350.html#msg1133350
- push @{$data{$name}{current}{genslidereg}}, $pvsum; # Schieberegister PV Erzeugung
+ push @{$data{$name}{current}{genslidereg}}, $pvsum; # Schieberegister PV Erzeugung
limitArray ($data{$name}{current}{genslidereg}, $slidenummax);
writeToHistory ( { paref => $paref, key => 'pvrl', val => $ethishoursum, hour => $nhour, valid => $aln } ); # valid=1: beim Learning berücksichtigen, 0: nicht
@@ -9197,158 +9225,174 @@ sub _transferBatteryValues {
my $chour = $paref->{chour};
my $day = $paref->{day};
- my $hash = $defs{$name};
- my ($err, $badev, $h) = isDeviceValid ( { name => $name, obj => 'setupBatteryDev', method => 'attr' } );
- return if($err);
+ my $hash = $defs{$name};
+ my $num = 0;
+ my $socsum;
+
+ for my $bn (1..$maxbatteries) {
+ $bn = sprintf "%02d", $bn;
+
+ my ($err, $badev, $h) = isDeviceValid ( { name => $name, obj => 'setupBatteryDev'.$bn, method => 'attr' } );
+ next if($err);
- my $type = $paref->{type};
+ $num++;
- my ($pin,$piunit) = split ":", $h->{pin}; # Readingname/Unit für aktuelle Batterieladung
- my ($pou,$pounit) = split ":", $h->{pout}; # Readingname/Unit für aktuelle Batterieentladung
- my ($bin,$binunit) = split ":", $h->{intotal} // "-:-"; # Readingname/Unit der total in die Batterie eingespeisten Energie (Zähler)
- my ($bout,$boutunit) = split ":", $h->{outtotal} // "-:-"; # Readingname/Unit der total aus der Batterie entnommenen Energie (Zähler)
- my $batchr = $h->{charge} // ""; # Readingname Ladezustand Batterie
- my $instcap = $h->{cap}; # numerischer Wert (Wh) oder Readingname installierte Batteriekapazität
+ my ($pin,$piunit) = split ":", $h->{pin}; # Readingname/Unit für aktuelle Batterieladung
+ my ($pou,$pounit) = split ":", $h->{pout}; # Readingname/Unit für aktuelle Batterieentladung
+ my ($bin,$binunit) = split ":", $h->{intotal} // "-:-"; # Readingname/Unit der total in die Batterie eingespeisten Energie (Zähler)
+ my ($bout,$boutunit) = split ":", $h->{outtotal} // "-:-"; # Readingname/Unit der total aus der Batterie entnommenen Energie (Zähler)
+ my $batchr = $h->{charge} // ""; # Readingname Ladezustand Batterie
+ my $instcap = $h->{cap}; # numerischer Wert (Wh) oder Readingname installierte Batteriekapazität
- return if(!$pin || !$pou);
+ return if(!$pin || !$pou);
- $pounit //= $piunit;
- $piunit //= $pounit;
- $boutunit //= $binunit;
- $binunit //= $boutunit;
+ $pounit //= $piunit;
+ $piunit //= $pounit;
+ $boutunit //= $binunit;
+ $binunit //= $boutunit;
- my $piuf = $piunit =~ /^kW$/xi ? 1000 : 1;
- my $pouf = $pounit =~ /^kW$/xi ? 1000 : 1;
- my $binuf = $binunit =~ /^kWh$/xi ? 1000 : 1;
- my $boutuf = $boutunit =~ /^kWh$/xi ? 1000 : 1;
+ my $piuf = $piunit =~ /^kW$/xi ? 1000 : 1;
+ my $pouf = $pounit =~ /^kW$/xi ? 1000 : 1;
+ my $binuf = $binunit =~ /^kWh$/xi ? 1000 : 1;
+ my $boutuf = $boutunit =~ /^kWh$/xi ? 1000 : 1;
- my $pbo = ReadingsNum ($badev, $pou, 0) * $pouf; # aktuelle Batterieentladung (W)
- my $pbi = ReadingsNum ($badev, $pin, 0) * $piuf; # aktueller Batterieladung (W)
- my $btotout = ReadingsNum ($badev, $bout, 0) * $boutuf; # totale Batterieentladung (Wh)
- my $btotin = ReadingsNum ($badev, $bin, 0) * $binuf; # totale Batterieladung (Wh)
- my $soc = ReadingsNum ($badev, $batchr, 0);
+ my $pbo = ReadingsNum ($badev, $pou, 0) * $pouf; # aktuelle Batterieentladung (W)
+ my $pbi = ReadingsNum ($badev, $pin, 0) * $piuf; # aktueller Batterieladung (W)
+ my $btotout = ReadingsNum ($badev, $bout, 0) * $boutuf; # totale Batterieentladung (Wh)
+ my $btotin = ReadingsNum ($badev, $bin, 0) * $binuf; # totale Batterieladung (Wh)
+ my $soc = ReadingsNum ($badev, $batchr, 0);
- if ($instcap) {
- if (!isNumeric ($instcap)) { # wenn $instcap Reading Wert abfragen
- my ($bcapr,$bcapunit) = split ':', $instcap;
- $bcapunit //= 'Wh';
- $instcap = ReadingsNum ($badev, $bcapr, 0);
- $instcap = $instcap * ($bcapunit =~ /^kWh$/xi ? 1000 : 1);
+ if ($instcap) {
+ if (!isNumeric ($instcap)) { # wenn $instcap Reading Wert abfragen
+ my ($bcapr,$bcapunit) = split ':', $instcap;
+ $bcapunit //= 'Wh';
+ $instcap = ReadingsNum ($badev, $bcapr, 0);
+ $instcap = $instcap * ($bcapunit =~ /^kWh$/xi ? 1000 : 1);
+ }
+
+ $data{$name}{batteries}{$bn}{binstcap} = $instcap; # installierte Batteriekapazität
+ }
+ else {
+ delete $data{$name}{batteries}{$bn}{binstcap};
}
- $data{$name}{current}{batinstcap} = $instcap; # installierte Batteriekapazität
+ my $debug = $paref->{debug};
+ if ($debug =~ /collectData/x) {
+ Log3 ($name, 1, "$name DEBUG> collect Battery data: device=$badev =>");
+ Log3 ($name, 1, "$name DEBUG> pin=$pbi W, pout=$pbo W, totalin: $btotin Wh, totalout: $btotout Wh, soc: $soc");
+ }
+
+ my $params;
+
+ if ($pin eq "-pout") { # Spezialfall pin bei neg. pout
+ $params = {
+ dev => $badev,
+ rdg => $pou,
+ rdgf => $pouf
+ };
+
+ ($pbo,$pbi) = substSpecialCases ($params);
+ }
+
+ if ($pou eq "-pin") { # Spezialfall pout bei neg. pin
+ $params = {
+ dev => $badev,
+ rdg => $pin,
+ rdgf => $piuf
+ };
+
+ ($pbi,$pbo) = substSpecialCases ($params);
+ }
+
+ # Batterielade, -entladeenergie in Circular speichern
+ #######################################################
+ if (!defined CircularVal ($hash, 99, 'initdaybatintot', undef)) {
+ $data{$name}{circular}{99}{initdaybatintot} = $btotin; # total Batterieladung zu Tagbeginn (Wh)
+ }
+
+ if (!defined CircularVal ($hash, 99, 'initdaybatouttot', undef)) { # total Batterieentladung zu Tagbeginn (Wh)
+ $data{$name}{circular}{99}{initdaybatouttot} = $btotout;
+ }
+
+ $data{$name}{circular}{99}{batintot} = $btotin; # aktuell total Batterieladung
+ $data{$name}{circular}{99}{batouttot} = $btotout; # aktuell total Batterieentladung
+
+ my $nhour = $chour+1;
+
+
+ # Batterieladung aktuelle Stunde in pvHistory speichern
+ #########################################################
+ my $histbatintot = HistoryVal ($hash, $day, sprintf("%02d",$nhour), "batintotal", undef); # totale Batterieladung zu Beginn einer Stunde
+ my $batinthishour;
+
+ if (!defined $histbatintot) { # totale Batterieladung der aktuelle Stunde gesetzt?
+ writeToHistory ( { paref => $paref, key => 'batintotal', val => $btotin, hour => $nhour } );
+
+ my $bitot = CurrentVal ($hash, "batintotal", $btotin);
+ $batinthishour = int ($btotin - $bitot);
+ }
+ else {
+ $batinthishour = int ($btotin - $histbatintot);
+ }
+
+ $batinthishour = 0 if($batinthishour < 0);
+
+ $data{$name}{circular}{sprintf("%02d",$nhour)}{batin} = $batinthishour; # Ringspeicher Battery In Forum: https://forum.fhem.de/index.php/topic,117864.msg1133350.html#msg1133350
+ writeToHistory ( { paref => $paref, key => 'batinthishour', val => $batinthishour, hour => $nhour } );
+
+
+ # Batterieentladung aktuelle Stunde in pvHistory speichern
+ ############################################################
+ my $histbatouttot = HistoryVal ($hash, $day, sprintf("%02d",$nhour), 'batouttotal', undef); # totale Betterieladung zu Beginn einer Stunde
+ my $batoutthishour;
+
+ if (!defined $histbatouttot) { # totale Betterieladung der aktuelle Stunde gesetzt?
+ writeToHistory ( { paref => $paref, key => 'batouttotal', val => $btotout, hour => $nhour } );
+
+ my $botot = CurrentVal ($hash, 'batouttotal', $btotout);
+ $batoutthishour = int ($btotout - $botot);
+ }
+ else {
+ $batoutthishour = int ($btotout - $histbatouttot);
+ }
+
+ $batoutthishour = 0 if($batoutthishour < 0);
+
+ $data{$name}{circular}{sprintf("%02d",$nhour)}{batout} = $batoutthishour; # Ringspeicher Battery In Forum: https://forum.fhem.de/index.php/topic,117864.msg1133350.html#msg1133350
+ writeToHistory ( { paref => $paref, key => 'batoutthishour', val => $batoutthishour, hour => $nhour } );
+
+
+ # täglichen max. SOC in pvHistory speichern
+ #############################################
+ my $batmaxsoc = HistoryVal ($hash, $day, 99, 'batmaxsoc', 0); # gespeicherter max. SOC des Tages
+
+ if ($soc >= $batmaxsoc) {
+ writeToHistory ( { paref => $paref, key => 'batmaxsoc', val => $soc, hour => 99 } );
+ }
+
+ ######
+
+ storeReading ('Today_Hour'.sprintf("%02d",$nhour).'_BatIn', $batinthishour.' Wh');
+ storeReading ('Today_Hour'.sprintf("%02d",$nhour).'_BatOut', $batoutthishour.' Wh');
+ storeReading ('Current_PowerBatIn', (int $pbi).' W');
+ storeReading ('Current_PowerBatOut', (int $pbo).' W');
+ storeReading ('Current_BatCharge', $soc.' %');
+
+ $data{$name}{batteries}{$bn}{bname} = $badev; # Batterie Devicename
+ $data{$name}{batteries}{$bn}{balias} = AttrVal ($badev, 'alias', $badev); # Alias Batterie Device
+ $data{$name}{batteries}{$bn}{bpowerin} = int $pbi; # momentane Batterieladung
+ $data{$name}{batteries}{$bn}{bpowerout} = int $pbo; # momentane Batterieentladung
+ $data{$name}{batteries}{$bn}{bcharge} = $soc; # aktuelle Batterieladung
+ $data{$name}{batteries}{$bn}{basynchron} = $h->{asynchron} // 0; # asynchroner Modus = X
+
+ $socsum += $soc;
}
- else {
- delete $data{$name}{current}{batinstcap};
+
+ if ($num) {
+ push @{$data{$name}{current}{socslidereg}}, $socsum / $num; # Schieberegister average SOC aller Batterien
+ limitArray ($data{$name}{current}{socslidereg}, $slidenummax);
}
- my $debug = $paref->{debug};
- if ($debug =~ /collectData/x) {
- Log3 ($name, 1, "$name DEBUG> collect Battery data: device=$badev =>");
- Log3 ($name, 1, "$name DEBUG> pin=$pbi W, pout=$pbo W, totalin: $btotin Wh, totalout: $btotout Wh, soc: $soc");
- }
-
- my $params;
-
- if ($pin eq "-pout") { # Spezialfall pin bei neg. pout
- $params = {
- dev => $badev,
- rdg => $pou,
- rdgf => $pouf
- };
-
- ($pbo,$pbi) = substSpecialCases ($params);
- }
-
- if ($pou eq "-pin") { # Spezialfall pout bei neg. pin
- $params = {
- dev => $badev,
- rdg => $pin,
- rdgf => $piuf
- };
-
- ($pbi,$pbo) = substSpecialCases ($params);
- }
-
- # Batterielade-, enladeenergie in Circular speichern
- ######################################################
- if (!defined CircularVal ($hash, 99, 'initdaybatintot', undef)) {
- $data{$name}{circular}{99}{initdaybatintot} = $btotin; # total Batterieladung zu Tagbeginn (Wh)
- }
-
- if (!defined CircularVal ($hash, 99, 'initdaybatouttot', undef)) { # total Batterieentladung zu Tagbeginn (Wh)
- $data{$name}{circular}{99}{initdaybatouttot} = $btotout;
- }
-
- $data{$name}{circular}{99}{batintot} = $btotin; # aktuell total Batterieladung
- $data{$name}{circular}{99}{batouttot} = $btotout; # aktuell total Batterieentladung
-
- my $nhour = $chour+1;
-
- # Batterieladung aktuelle Stunde in pvHistory speichern
- #########################################################
- my $histbatintot = HistoryVal ($hash, $day, sprintf("%02d",$nhour), "batintotal", undef); # totale Batterieladung zu Beginn einer Stunde
- my $batinthishour;
-
- if (!defined $histbatintot) { # totale Batterieladung der aktuelle Stunde gesetzt?
- writeToHistory ( { paref => $paref, key => 'batintotal', val => $btotin, hour => $nhour } );
-
- my $bitot = CurrentVal ($hash, "batintotal", $btotin);
- $batinthishour = int ($btotin - $bitot);
- }
- else {
- $batinthishour = int ($btotin - $histbatintot);
- }
-
- $batinthishour = 0 if($batinthishour < 0);
-
- $data{$name}{circular}{sprintf("%02d",$nhour)}{batin} = $batinthishour; # Ringspeicher Battery In Forum: https://forum.fhem.de/index.php/topic,117864.msg1133350.html#msg1133350
- writeToHistory ( { paref => $paref, key => 'batinthishour', val => $batinthishour, hour => $nhour } );
-
- # Batterieentladung aktuelle Stunde in pvHistory speichern
- ############################################################
- my $histbatouttot = HistoryVal ($hash, $day, sprintf("%02d",$nhour), 'batouttotal', undef); # totale Betterieladung zu Beginn einer Stunde
- my $batoutthishour;
-
- if (!defined $histbatouttot) { # totale Betterieladung der aktuelle Stunde gesetzt?
- writeToHistory ( { paref => $paref, key => 'batouttotal', val => $btotout, hour => $nhour } );
-
- my $botot = CurrentVal ($hash, 'batouttotal', $btotout);
- $batoutthishour = int ($btotout - $botot);
- }
- else {
- $batoutthishour = int ($btotout - $histbatouttot);
- }
-
- $batoutthishour = 0 if($batoutthishour < 0);
-
- $data{$name}{circular}{sprintf("%02d",$nhour)}{batout} = $batoutthishour; # Ringspeicher Battery In Forum: https://forum.fhem.de/index.php/topic,117864.msg1133350.html#msg1133350
- writeToHistory ( { paref => $paref, key => 'batoutthishour', val => $batoutthishour, hour => $nhour } );
-
- # täglichen max. SOC in pvHistory speichern
- #############################################
- my $batmaxsoc = HistoryVal ($hash, $day, 99, 'batmaxsoc', 0); # gespeicherter max. SOC des Tages
-
- if ($soc >= $batmaxsoc) {
- writeToHistory ( { paref => $paref, key => 'batmaxsoc', val => $soc, hour => 99 } );
- }
-
- ######
-
- storeReading ('Today_Hour'.sprintf("%02d",$nhour).'_BatIn', $batinthishour.' Wh');
- storeReading ('Today_Hour'.sprintf("%02d",$nhour).'_BatOut', $batoutthishour.' Wh');
- storeReading ('Current_PowerBatIn', (int $pbi).' W');
- storeReading ('Current_PowerBatOut', (int $pbo).' W');
- storeReading ('Current_BatCharge', $soc.' %');
-
- $data{$name}{current}{powerbatin} = int $pbi; # Hilfshash Wert aktuelle Batterieladung
- $data{$name}{current}{powerbatout} = int $pbo; # Hilfshash Wert aktuelle Batterieentladung
- $data{$name}{current}{batcharge} = $soc; # aktuelle Batterieladung
- $data{$name}{current}{batasynchron} = $h->{asynchron} if($h->{asynchron}); # asynchroner Modus = X
-
- push @{$data{$name}{current}{socslidereg}}, $soc; # Schieberegister Batterie SOC
- limitArray ($data{$name}{current}{socslidereg}, $slidenummax);
-
return;
}
@@ -9358,7 +9402,6 @@ return;
sub _batSocTarget {
my $paref = shift;
my $name = $paref->{name};
- my $type = $paref->{type};
my $t = $paref->{t}; # aktuelle Zeit
return if(!isBatteryUsed ($name));
@@ -9366,8 +9409,8 @@ sub _batSocTarget {
my $hash = $defs{$name};
my $oldd2care = CircularVal ($hash, 99, 'days2care', 0);
my $ltsmsr = CircularVal ($hash, 99, 'lastTsMaxSocRchd', undef);
- my $batcharge = CurrentVal ($hash, 'batcharge', 0); # aktuelle Ladung in %
- my $batinstcap = CurrentVal ($hash, 'batinstcap', 0); # installierte Batteriekapazität Wh
+ my $batcharge = BatteryVal ($hash, '01', 'bcharge', 0); # aktuelle Ladung in %
+ my $batinstcap = BatteryVal ($hash, '01', 'binstcap', 0); # installierte Batteriekapazität Wh
my $cgbt = AttrVal ($name, 'ctrlBatSocManagement', undef);
if ($cgbt && !$batinstcap) {
@@ -9484,7 +9527,8 @@ sub _batSocTarget {
$target < $lowSoc ? $lowSoc :
$target;
- debugLog ($paref, 'batteryManagement', "SoC calc Step4 - observe low/up limits -> docare: $docare, Target: $target %");
+ debugLog ($paref, 'batteryManagement', "SoC calc Step4 - basics -> docare: $docare, lowSoc: $lowSoc %, upSoc: $upSoc %");
+ debugLog ($paref, 'batteryManagement', "SoC calc Step4 - observe low/up limits -> Target: $target %");
## auf 5er Schritte anpassen (40,45,50,...)
#############################################
@@ -9569,8 +9613,8 @@ sub _batChargeRecmd {
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 $batcap = BatteryVal ($hash, '01', 'binstcap', 0); # installierte Batteriekapazität Wh
+ my $soc = BatteryVal ($hash, '01', 'bcharge', 0); # aktuelle Ladung in % # aktueller SOC (%)
my $curcon = ReadingsNum ($name, 'Current_Consumption', 0); # aktueller Verbrauch
my $inpmax = 0;
@@ -9750,14 +9794,14 @@ sub _createSummaries {
my $pvre = int $todaySumRe->{PV};
- push @{$data{$name}{current}{h4fcslidereg}}, int $next4HoursSum->{PV}; # Schieberegister 4h Summe Forecast
+ push @{$data{$name}{current}{h4fcslidereg}}, int $next4HoursSum->{PV}; # Schieberegister 4h Summe Forecast
limitArray ($data{$name}{current}{h4fcslidereg}, $slidenummax);
my $gcon = CurrentVal ($hash, 'gridconsumption', 0); # aktueller Netzbezug
my $tconsum = CurrentVal ($hash, 'tomorrowconsumption', undef); # Verbrauchsprognose für folgenden Tag
my $gfeedin = CurrentVal ($hash, 'gridfeedin', 0);
- my $batin = CurrentVal ($hash, 'powerbatin', 0); # aktuelle Batterieladung
- my $batout = CurrentVal ($hash, 'powerbatout', 0); # aktuelle Batterieentladung
+ my $batin = BatteryVal ($hash, '01', 'bpowerin', 0); # momentane Batterieladung
+ my $batout = BatteryVal ($hash, '01', 'bpowerout', 0); # momentane Batterieentladung
my $pvgen = 0;
my $pv2grid = 0; # PV-Erzeugung zu Grid-only
@@ -11400,8 +11444,8 @@ sub ___enableSwitchByBatPrioCharge {
return $ena if(!$pcb || !$badev); # Freigabe Schalten Consumer wenn kein Prefered Battery/Soll-Ladung 0 oder keine Batterie installiert
- my $cbcharge = CurrentVal ($hash, "batcharge", 0); # aktuelle Batterieladung
- $ena = 0 if($cbcharge < $pcb); # keine Freigabe wenn Batterieladung kleiner Soll-Ladung
+ my $bcharge = BatteryVal ($hash, '01', 'bcharge', 0); # aktuelle Ladung in %
+ $ena = 0 if($bcharge < $pcb); # keine Freigabe wenn Batterieladung kleiner Soll-Ladung
return $ena;
}
@@ -16611,11 +16655,12 @@ sub listDataPool {
}
}
- if ($htol =~ /consumers|inverters|producers|strings/xs) {
+ if ($htol =~ /consumers|inverters|producers|strings|batteries/xs) {
my $sub = $htol eq 'consumers' ? \&ConsumerVal :
$htol eq 'inverters' ? \&InverterVal :
$htol eq 'producers' ? \&ProducerVal :
$htol eq 'strings' ? \&StringVal :
+ $htol eq 'batteries' ? \&BatteryVal :
'';
$h = $data{$name}{$htol};
@@ -16635,8 +16680,13 @@ sub listDataPool {
next if($par && $idx ne $par);
my ($cret, $s1);
my $sp1 = _ldpspaces ($idx, q{});
-
+
for my $ckey (sort keys %{$h->{$idx}}) {
+ if (ref $h->{$idx}{$ckey} eq 'ARRAY') {
+ my $aser = join " ",@{$h->{$idx}{$ckey}};
+ $cret .= ($s1 ? $sp1 : "").$ckey." => ".$aser."\n";
+ }
+
if (ref $h->{$idx}{$ckey} eq 'HASH') {
my $hk = qq{};
for my $f (sort {$a<=>$b} keys %{$h->{$idx}{$ckey}}) {
@@ -16648,7 +16698,8 @@ sub listDataPool {
else {
$cret .= ($s1 ? $sp1 : "").$ckey." => ". &{$sub} ($hash, $idx, $ckey, "")."\n";
}
- $s1 = 1;
+
+ $s1 = 1;
}
$sq .= $idx." => ".$cret."\n";
}
@@ -18050,12 +18101,7 @@ sub createAssociatedWith {
$medev = $ame->[0] // '';
push @cd, $medev;
- my $badev = AttrVal ($name, 'setupBatteryDev', ''); # Battery Device
- ($aba,$h) = parseParams ($badev);
- $badev = $aba->[0] // '';
- push @cd, $badev;
-
- for my $c (sort{$a<=>$b} keys %{$data{$name}{consumers}}) { # Consumer Devices
+ for my $c (sort{$a<=>$b} keys %{$data{$name}{consumers}}) { # Consumer Devices
my $consumer = AttrVal ($name, "consumer${c}", "");
my ($ac,$hc) = parseParams ($consumer);
my $codev = $ac->[0] // '';
@@ -18064,9 +18110,16 @@ sub createAssociatedWith {
push @cd, $dswitch if($dswitch);
}
+ for my $bn (1..$maxbatteries) { # Battery Devices
+ $bn = sprintf "%02d", $bn;
+ my $badev = AttrVal ($name, "setupBatteryDev${bn}", '');
+ my ($aba) = parseParams ($badev);
+ push @cd, $aba->[0] if($aba->[0]);
+ }
+
for my $in (1..$maxinverter) { # Inverter Devices
$in = sprintf "%02d", $in;
- my $inc = AttrVal ($name, "setupInverterDev${in}", "");
+ my $inc = AttrVal ($name, "setupInverterDev${in}", '');
my ($ind) = parseParams ($inc);
push @cd, $ind->[0] if($ind->[0]);
}
@@ -18078,7 +18131,6 @@ sub createAssociatedWith {
push @nd, $fcdev3 if($fcdev3 && $fcdev3 !~ /-API/xs);
push @nd, $radev if($radev && $radev !~ /-API/xs);
push @nd, $medev;
- push @nd, $badev;
for my $prn (1..$maxproducer) { # Producer Devices
$prn = sprintf "%02d", $prn;
@@ -18623,11 +18675,18 @@ return ConsumerVal ($hash, $c, 'isConsumptionRecommended', 0);
################################################################
sub isBatteryUsed {
my $name = shift;
+
+ my $valid;
- my ($err) = isDeviceValid ( { name => $name, obj => 'setupBatteryDev', method => 'attr' } );
- return if($err);
+ for my $bn (1..$maxbatteries) {
+ $bn = sprintf "%02d", $bn;
+ my ($err) = isDeviceValid ( { name => $name, obj => 'setupBatteryDev'.$bn, method => 'attr' } );
+ next if($err);
+
+ $valid = 1;
+ }
-return 1;
+return $valid;
}
################################################################
@@ -19809,8 +19868,6 @@ return $def;
# $key: aiinitstate - Initialisierungsstatus der KI
# aitrainstate - Traisningsstatus der KI
# aiaddistate - Add Instanz Status der KI
-# batcharge - Bat SOC in %
-# batinstcap - installierte Batteriekapazität in Wh
# ctrunning - aktueller Ausführungsstatus des Central Task
# dwdRad1hAge - Alter des Rad1h Wertes als Datumstring
# dwdRad1hAgeTS - Alter des Rad1h Wertes als Unix Timestamp
@@ -19823,8 +19880,6 @@ return $def;
# consumerdevs - alle registrierten Consumerdevices (Array)
# consumerCollected - Statusbit Consumer Attr gesammelt und ausgewertet
# gridconsumption - aktueller Netzbezug
-# powerbatin - Batterie Ladeleistung
-# powerbatout - Batterie Entladeleistung
# temp - aktuelle Außentemperatur
# surplus - aktueller PV Überschuß
# tomorrowconsumption - Verbrauch des kommenden Tages
@@ -20032,6 +20087,42 @@ sub ConsumerVal {
return $def;
}
+###################################################################################################
+# Wert des Batterie-Hash zurückliefern
+# Usage:
+# BatteryVal ($hash or $name, $bn, $key, $def)
+#
+# $bn: Batterie Nummer (01,02,03,...)
+# $key: balias - Alias des Batterie Devices
+# bname - Name des Batterie Devices
+# basynchron - Asynchron Modus
+# bcharge - Bat SOC in %
+# binstcap - installierte Batteriekapazität in Wh
+# bpowerin - Batterie momentane Ladeleistung
+# bpowerout - Batterie momentane Entladeleistung
+#
+# $def: Defaultwert
+#
+###################################################################################################
+sub BatteryVal {
+ my $name = shift;
+ my $bn = shift;
+ my $key = shift;
+ my $def = shift;
+
+ if (ref $name eq 'HASH') {
+ $name = $name->{NAME};
+ }
+
+ if (defined $data{$name}{batteries} &&
+ defined $data{$name}{batteries}{$bn} &&
+ defined $data{$name}{batteries}{$bn}{$key}) {
+ return $data{$name}{batteries}{$bn}{$key};
+ }
+
+return $def;
+}
+
###################################################################################################
# Wert des Inverter-Hash zurückliefern
# Usage:
@@ -20289,7 +20380,7 @@ to ensure that the system configuration is correct.
setupRadiationAPI | DWD_OpenData Device or API for the delivery of radiation data. |
setupInverterDevXX | Device which provides PV performance data |
setupMeterDev | Device which supplies network I/O data |
- setupBatteryDev | Device which provides battery performance data (if available) |
+ setupBatteryDevXX | Device which provides battery performance data (if available) |
setupInverterStrings | Identifier of the existing plant strings |
setupStringAzimuth | Azimuth of the plant strings |
setupStringPeak | the DC peak power of the plant strings |
@@ -20372,6 +20463,7 @@ to ensure that the system configuration is correct.
batteryTrigger <1on>=<Value> <1off>=<Value> [<2on>=<Value> <2off>=<Value> ...]
Generates triggers when the battery charge exceeds or falls below certain values (SoC in %).
+ The SoC used is formed as an average of the SoCs of all defined battery devices.
If the last three SoC measurements exceed a defined Xon-Bedingung, the reading batteryTrigger_X = on
is created/set.
If the last three SoC measurements fall below a defined Xoff-Bedingung, the reading
@@ -21105,6 +21197,28 @@ to ensure that the system configuration is correct.
+
+
+
+ - valBattery
+ Shows the operating values determined for the selected battery or all defined battery devices.
+
+
+
+
+ bname | Name of the device |
+ balias | Alias of the device |
+ basynchron | Mode of processing received battery events |
+ bcharge | SOC (State of Charge) of the battery (%) |
+ binstcap | installed battery capacity (Wh) |
+ bpowerin | current charging power (W) |
+ bpowerout | current discharge power (W) |
+
+
+
+
+
+
@@ -21542,7 +21656,7 @@ to ensure that the system configuration is correct.
- ctrlBatSocManagement lowSoc=<Value> upSoC=<Value> [maxSoC=<Value>] [careCycle=<Value>]
- If a battery device (setupBatteryDev) is installed, this attribute activates the battery SoC management.
+ If a battery device (setupBatteryDevXX) is installed, this attribute activates the battery SoC management.
The Battery_OptimumTargetSoC reading contains the optimum minimum SoC calculated by the module.
The Battery_ChargeRequest reading is set to '1' if the current SoC has fallen below the minimum SoC.
In this case, the battery should be forcibly charged, possibly with mains power.
@@ -21764,11 +21878,11 @@ to ensure that the system configuration is correct.
- Beispiel:
+ Example:
{
- my $batdev = (split " ", AttrVal ($name, 'setupBatteryDev', ''))[0];
- my $pvfc = ReadingsNum ($name, 'RestOfDayPVforecast', 0);
- my $cofc = ReadingsNum ($name, 'RestOfDayConsumptionForecast', 0);
+ my $batdev = (split " ", AttrVal ($name, 'setupBatteryDev01', ''))[0];
+ my $pvfc = ReadingsNum ($name, 'RestOfDayPVforecast', 0);
+ my $cofc = ReadingsNum ($name, 'RestOfDayConsumptionForecast', 0);
my $diff = $pvfc - $cofc;
storeReading ('userFn_Battery_device', $batdev);
@@ -22204,10 +22318,10 @@ to ensure that the system configuration is correct.
-
- - setupBatteryDev <Battery Device Name> pin=<Readingname>:<Unit> pout=<Readingname>:<Unit>
- [intotal=<Readingname>:<Unit>] [outtotal=<Readingname>:<Unit>]
- cap=<Option> [charge=<Readingname>] [asynchron=<Option>]
+
+ - setupBatteryDevXX <Battery Device Name> pin=<Readingname>:<Unit> pout=<Readingname>:<Unit>
+ cap=<Option> [intotal=<Readingname>:<Unit>] [outtotal=<Readingname>:<Unit>]
+ [charge=<Readingname>] [asynchron=<Option>]
Specifies an arbitrary Device and its Readings to deliver the battery performance data.
The module assumes that the numerical value of the readings is always positive.
@@ -22247,7 +22361,7 @@ to ensure that the system configuration is correct.
Example:
- attr <name> setupBatteryDev BatDummy pin=BatVal:W pout=-pin intotal=BatInTot:Wh outtotal=BatOutTot:Wh cap=BatCap:kWh
+ attr <name> setupBatteryDev01 BatDummy pin=BatVal:W pout=-pin intotal=BatInTot:Wh outtotal=BatOutTot:Wh cap=BatCap:kWh
@@ -22711,7 +22825,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
setupRadiationAPI | DWD_OpenData Device bzw. API zur Lieferung von Strahlungsdaten |
setupInverterDevXX | Device welches PV Leistungsdaten liefert |
setupMeterDev | Device welches Netz I/O-Daten liefert |
- setupBatteryDev | Device welches Batterie Leistungsdaten liefert (sofern vorhanden) |
+ setupBatteryDevXX | Device welches Batterie Leistungsdaten liefert (sofern vorhanden) |
setupInverterStrings | Bezeichner der vorhandenen Anlagenstrings |
setupStringAzimuth | Ausrichtung (Azimut) der Anlagenstrings |
setupStringPeak | die DC-Peakleistung der Anlagenstrings |
@@ -22793,6 +22907,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
- batteryTrigger <1on>=<Wert> <1off>=<Wert> [<2on>=<Wert> <2off>=<Wert> ...]
Generiert Trigger bei Über- bzw. Unterschreitung bestimmter Batterieladungswerte (SoC in %).
+ Der verwendete SoC wird als Durchschnitt der SoC's aller definierten Batterie Geräte gebildet.
Überschreiten die letzten drei SoC-Messungen eine definierte Xon-Bedingung, wird das Reading
batteryTrigger_X = on erstellt/gesetzt.
Unterschreiten die letzten drei SoC-Messungen eine definierte Xoff-Bedingung, wird das Reading
@@ -23536,6 +23651,28 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
+
+
+
+ - valBattery
+ Zeigt die ermittelten Betriebswerte der ausgewählten Batterie oder aller definierten Batteriegeräte.
+
+
+
+
+ bname | Name des Gerätes |
+ balias | Alias des Gerätes |
+ basynchron | Modus der Verarbeitung empfangener Batterie-Events |
+ bcharge | SOC (State of Charge) der Batterie (%) |
+ binstcap | installierte Batteriekapazität (Wh) |
+ bpowerin | momentane Ladeleistung (W) |
+ bpowerout | momentane Entladeleistung (W) |
+
+
+
+
+
+
@@ -23589,8 +23726,8 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
ifeed | Eigenschaften der Energielieferung |
igeneration | aktuelle PV Erzeugung (W) |
iicon | die evtl. festgelegten Icons zur Darstellung des Gerätes in der Grafik |
- ialias | Alias des Devices |
- iname | Name des Devices |
+ ialias | Alias des Gerätes |
+ iname | Name des Gerätes |
invertercap | die nominale Leistung (W) des Wechselrichters (falls definiert) |
istrings | Liste der dem Wechselrichter zugeordneten Strings (falls definiert) |
@@ -23612,8 +23749,8 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
pfeed | Eigenschaften der Energielieferung |
pgeneration | aktuelle Leistung (W) |
picon | die evtl. festgelegten Icons zur Darstellung des Gerätes in der Grafik |
- palias | Alias des Devices |
- pname | Name des Devices |
+ palias | Alias des Gerätes |
+ pname | Name des Gerätes |
@@ -23971,7 +24108,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
ctrlBatSocManagement lowSoc=<Wert> upSoC=<Wert> [maxSoC=<Wert>] [careCycle=<Wert>]
- Sofern ein Batterie Device (setupBatteryDev) installiert ist, aktiviert dieses Attribut das Batterie
+ Sofern ein Batterie Device (setupBatteryDevXX) installiert ist, aktiviert dieses Attribut das Batterie
SoC-Management.
Das Reading Battery_OptimumTargetSoC enthält den vom Modul berechneten optimalen Mindest-SoC.
Das Reading Battery_ChargeRequest wird auf '1' gesetzt, wenn der aktuelle SoC unter den Mindest-SoC gefallen
@@ -24197,9 +24334,9 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
Beispiel:
{
- my $batdev = (split " ", AttrVal ($name, 'setupBatteryDev', ''))[0];
- my $pvfc = ReadingsNum ($name, 'RestOfDayPVforecast', 0);
- my $cofc = ReadingsNum ($name, 'RestOfDayConsumptionForecast', 0);
+ my $batdev = (split " ", AttrVal ($name, 'setupBatteryDev01', ''))[0];
+ my $pvfc = ReadingsNum ($name, 'RestOfDayPVforecast', 0);
+ my $cofc = ReadingsNum ($name, 'RestOfDayConsumptionForecast', 0);
my $diff = $pvfc - $cofc;
storeReading ('userFn_Battery_device', $batdev);
@@ -24634,10 +24771,10 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
-
- setupBatteryDev <Batterie Device Name> pin=<Readingname>:<Einheit> pout=<Readingname>:<Einheit>
- [intotal=<Readingname>:<Einheit>] [outtotal=<Readingname>:<Einheit>]
- cap=<Option> [charge=<Readingname>] [asynchron=<Option>]
+
+ setupBatteryDevXX <Batterie Device Name> pin=<Readingname>:<Einheit> pout=<Readingname>:<Einheit>
+ cap=<Option> [intotal=<Readingname>:<Einheit>] [outtotal=<Readingname>:<Einheit>]
+ [charge=<Readingname>] [asynchron=<Option>]
Legt ein beliebiges Device und seine Readings zur Lieferung der Batterie Leistungsdaten fest.
Das Modul geht davon aus, dass der numerische Wert der Readings immer positiv ist.
@@ -24677,7 +24814,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
Beispiel:
- attr <name> setupBatteryDev BatDummy pin=BatVal:W pout=-pin intotal=BatInTot:Wh outtotal=BatOutTot:Wh cap=BatCap:kWh
+ attr <name> setupBatteryDev01 BatDummy pin=BatVal:W pout=-pin intotal=BatInTot:Wh outtotal=BatOutTot:Wh cap=BatCap:kWh
diff --git a/fhem/contrib/DS_Starter/76_SolarForecast.pm b/fhem/contrib/DS_Starter/76_SolarForecast.pm
index 28233e294..dc4ca4fc6 100644
--- a/fhem/contrib/DS_Starter/76_SolarForecast.pm
+++ b/fhem/contrib/DS_Starter/76_SolarForecast.pm
@@ -158,7 +158,8 @@ BEGIN {
# Versions History intern
my %vNotesIntern = (
- "1.40.0" => "21.12.2024 new consumer key 'surpmeth' to calculate surplus in various variants for cunsumer switching ",
+ "1.41.0" => "28.12.2024 _batSocTarget: minor code change, change setupBatteryDev to setupBatteryDev01, getter valBattery ",
+ "1.40.0" => "21.12.2024 new consumer key 'surpmeth' to calculate surplus in various variants for consumer switching ",
"1.39.8" => "21.12.2024 prepare of new consumer key 'surpmeth', _batSocTarget: improve care SoC management when dark doldrums ",
"1.39.7" => "18.12.2024 ConsumptionRecommended calc method medianArray, change local owndata to global data ",
"1.39.6" => "17.12.2024 replace global data-store by local owndata-store, remove sub _composeRemoteObj, delHashRefDeep removed ".
@@ -404,6 +405,7 @@ my $tempcoeffdef = -0.45;
my $tempmodinc = 25; # default Temperaturerhöhung an Solarzellen gegenüber Umgebungstemperatur bei wolkenlosem Himmel
my $tempbasedef = 25; # Temperatur Module bei Nominalleistung
+my $maxbatteries = 1; # maximale Anzahl der möglichen Batterien
my $maxconsumer = 16; # maximale Anzahl der möglichen Consumer (Attribut)
my $maxproducer = 3; # maximale Anzahl der möglichen anderen Produzenten (Attribut)
my $maxinverter = 3; # maximale Anzahl der möglichen Inverter
@@ -515,7 +517,7 @@ my @aconfigs = qw( affectBatteryPreferredCharge affectConsForecastIdentWeekdays
graphicHeaderDetail graphicHeaderShow graphicHistoryHour graphicHourCount graphicHourStyle
graphicLayoutType graphicSelect graphicShowDiff graphicShowNight graphicShowWeather
graphicSpaceSize graphicWeatherColor graphicWeatherColorNight
- setupMeterDev setupBatteryDev setupInverterStrings setupRadiationAPI setupStringPeak
+ setupMeterDev setupInverterStrings setupRadiationAPI setupStringPeak
setupRoofTops
);
@@ -525,6 +527,11 @@ for my $cn (1..$maxconsumer) {
push @dd, "consumerSwitching${cn}"; # ctrlDebug: add specific Consumer
}
+for my $bn (1..$maxbatteries) {
+ $bn = sprintf "%02d", $bn;
+ push @aconfigs, "setupBatteryDev${bn}"; # Anlagenkonfiguration: add Battery Attribute
+}
+
for my $in (1..$maxinverter) {
$in = sprintf "%02d", $in;
push @aconfigs, "setupInverterDev${in}"; # Anlagenkonfiguration: add Inverter Attribute
@@ -580,6 +587,7 @@ my %hget = ( # Ha
data => { fn => \&_getdata, needcred => 0 },
html => { fn => \&_gethtml, needcred => 0 },
ftui => { fn => \&_getftui, needcred => 0 },
+ valBattery => { fn => \&_getlistvalBattery, needcred => 0 },
valCurrent => { fn => \&_getlistCurrent, needcred => 0 },
valInverter => { fn => \&_getlistvalInverter, needcred => 0 },
valProducer => { fn => \&_getlistvalProducer, needcred => 0 },
@@ -608,8 +616,6 @@ my %hattr = ( # H
setupWeatherDev2 => { fn => \&_attrWeatherDev },
setupWeatherDev3 => { fn => \&_attrWeatherDev },
setupMeterDev => { fn => \&_attrMeterDev },
- setupBatteryDev => { fn => \&_attrBatteryDev },
- setupInverterDev => { fn => \&_attrInverterDev },
setupInverterStrings => { fn => \&_attrInverterStrings },
setupRadiationAPI => { fn => \&_attrRadiationAPI },
setupStringPeak => { fn => \&_attrStringPeak },
@@ -617,6 +623,11 @@ my %hattr = ( # H
flowGraphicControl => { fn => \&_attrflowGraphicControl },
);
+ for my $bn (1..$maxbatteries) {
+ $bn = sprintf "%02d", $bn;
+ $hattr{'setupBatteryDev'.$bn}{fn} = \&_attrBatteryDev;
+ }
+
for my $in (1..$maxinverter) {
$in = sprintf "%02d", $in;
$hattr{'setupInverterDev'.$in}{fn} = \&_attrInverterDev;
@@ -1179,12 +1190,17 @@ sub Initialize {
my $srd = join ",", sort keys (%hcsr);
my $gbc = 'pvReal,pvForecast,consumption,consumptionForecast,gridconsumption,energycosts,gridfeedin,feedincome';
- my ($consumer, $setupprod, $setupinv, @allc);
+ my ($consumer, $setupbat, $setupprod, $setupinv, @allc);
for my $c (1..$maxconsumer) {
$c = sprintf "%02d", $c;
$consumer .= "consumer${c}:textField-long ";
push @allc, $c;
}
+
+ for my $bn (1..$maxbatteries) {
+ $bn = sprintf "%02d", $bn;
+ $setupbat .= "setupBatteryDev${bn}:textField-long ";
+ }
for my $in (1..$maxinverter) {
$in = sprintf "%02d", $in;
@@ -1276,9 +1292,9 @@ sub Initialize {
"setupWeatherDev2 ".
"setupWeatherDev3 ".
"setupRoofTops ".
- "setupBatteryDev:textField-long ".
"setupRadiationAPI ".
"setupStringPeak ".
+ $setupbat.
$setupinv.
$setupprod.
$consumer.
@@ -1295,11 +1311,8 @@ sub Initialize {
# $hash->{FW_addDetailToSummary} = 1;
# $hash->{FW_atPageEnd} = 1; # wenn 1 -> kein Longpoll ohne informid in HTML-Tag
- $hash->{AttrRenameMap} = { "graphicBeamHeight" => "graphicBeamHeightLevel1", # 07.05.24
- "ctrlWeatherDev1" => "setupWeatherDev1", # 20.08.24
- "ctrlWeatherDev2" => "setupWeatherDev2",
- "ctrlWeatherDev3" => "setupWeatherDev3",
- "setupInverterDev" => "setupInverterDev01", # 11.10.24
+ $hash->{AttrRenameMap} = { "setupBatteryDev" => "setupBatteryDev01", # 28.12.24
+ "setupInverterDev" => "setupInverterDev01", # 11.10.24
};
eval { FHEM::Meta::InitMod( __FILE__, $hash ) }; ## no critic 'eval'
@@ -2318,6 +2331,7 @@ sub Get {
my @pha = map {sprintf "%02d", $_} sort {$a<=>$b} keys %{$data{$name}{pvhist}};
my @vcm = map {sprintf "%02d", $_} sort {$a<=>$b} keys %{$data{$name}{consumers}};
+ my @vba = map {sprintf "%02d", $_} sort {$a<=>$b} keys %{$data{$name}{batteries}};
my @vin = map {sprintf "%02d", $_} sort {$a<=>$b} keys %{$data{$name}{inverters}};
my @vpn = map {sprintf "%02d", $_} sort {$a<=>$b} keys %{$data{$name}{producers}};
my @vst = sort keys %{$data{$name}{strings}};
@@ -2325,11 +2339,13 @@ sub Get {
my $hol = join ",", @ho;
my $pvl = join ",", @pha;
my $cml = join ",", @vcm;
+ my $bal = join ",", @vba;
my $inl = join ",", @vin;
my $pnl = join ",", @vpn;
my $str = join ",", @vst;
my $getlist = "Unknown argument $opt, choose one of ".
+ "valBattery:#,$bal ".
"valConsumerMaster:#,$cml ".
"valInverter:#,$inl ".
"valProducer:#,$pnl ".
@@ -4518,6 +4534,21 @@ sub _getlistCurrent {
return $ret;
}
+###############################################################
+# Getter valBattery
+###############################################################
+sub _getlistvalBattery {
+ my $paref = shift;
+ my $name = $paref->{name};
+ my $arg = $paref->{arg};
+ my $hash = $defs{$name};
+
+ my $ret = listDataPool ($hash, 'batteries', $arg);
+ $ret .= lineFromSpaces ($ret, 30);
+
+return $ret;
+}
+
###############################################################
# Getter valConsumerMaster
###############################################################
@@ -5302,8 +5333,8 @@ sub Attr {
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.}
- if(!CurrentVal($hash, 'batinstcap', 0)); # https://forum.fhem.de/index.php?msg=1310930
+ return qq{Define the key 'cap' with "attr $name setupBatteryDev01" before this attribute in the correct form.}
+ if(!BatteryVal ($hash, '01', 'binstcap', 0)); # https://forum.fhem.de/index.php?msg=1310930
my ($lowSoc, $upSoc, $maxsoc, $careCycle) = __parseAttrBatSoc ($name, $aVal);
@@ -5836,9 +5867,7 @@ sub _attrInverterDev { ## no critic "not used"
delete $data{$name}{inverters}{$in}{ifeed};
}
elsif ($paref->{cmd} eq 'del') {
- for my $k (keys %{$data{$name}{inverters}}) {
- delete $data{$name}{inverters}{$k} if($k eq $in);
- }
+ delete $data{$name}{inverters}{$in};
readingsDelete ($hash, 'Current_PV');
undef @{$data{$name}{current}{genslidereg}};
@@ -5992,7 +6021,8 @@ sub _attrBatteryDev { ## no critic "not used"
return if(!$init_done);
- my $hash = $defs{$name};
+ my $hash = $defs{$name};
+ my $bn = (split 'setupBatteryDev', $aName)[1];
if ($paref->{cmd} eq 'set') {
my ($err, $badev, $h) = isDeviceValid ( { name => $name, obj => $aVal, method => 'string' } );
@@ -6010,6 +6040,8 @@ sub _attrBatteryDev { ## no critic "not used"
if ($h->{pin} eq "-pout" && $h->{pout} eq "-pin") {
return qq{Incorrect input. It is not allowed that the keys pin and pout refer to each other.};
}
+
+ delete $data{$name}{batteries}{$bn}{basynchron};
}
elsif ($paref->{cmd} eq 'del') {
readingsDelete ($hash, 'Current_PowerBatIn');
@@ -6025,11 +6057,7 @@ sub _attrBatteryDev { ## no critic "not used"
delete $data{$name}{circular}{99}{batintot};
delete $data{$name}{circular}{99}{batouttot};
- delete $data{$name}{current}{powerbatout};
- delete $data{$name}{current}{powerbatin};
- delete $data{$name}{current}{batcharge};
- delete $data{$name}{current}{batinstcap};
- delete $data{$name}{current}{batasynchron};
+ delete $data{$name}{batteries}{$bn};
}
InternalTimer (gettimeofday() + 2, 'FHEM::SolarForecast::createAssociatedWith', $hash, 0);
@@ -6210,7 +6238,7 @@ sub Notify {
my $debug = getDebug ($myHash); # Debug Mode
- my ($err, $medev, $badev, $h, $async);
+ my ($err, $medev, $bname, $iname, $h, $async);
## Meter Event?
#################
@@ -6241,11 +6269,12 @@ sub Notify {
## Battery Event?
###################
- ($err, $badev, $h) = isDeviceValid ( { name => $myName, obj => 'setupBatteryDev', method => 'attr' } );
+ for my $bn (1..$maxbatteries) {
+ $bn = sprintf "%02d", $bn;
+ $bname = BatteryVal ($myHash, $bn, 'bname', '');
- if (!$err) {
- if ($devName eq $badev) {
- $async = $h->{asynchron} // 0;
+ if ($devName eq $bname) {
+ $async = BatteryVal ($myHash, $bn, 'basynchron', 0);
if ($debug =~ /notifyHandling/x) {
Log3 ($myName, 1, qq{$myName DEBUG> notifyHandling - Event of Battery device >$devName< received - asynchronous mode: $async});
@@ -6264,22 +6293,23 @@ sub Notify {
return;
}
}
+
}
## Inverter Event?
####################
for my $in (1..$maxinverter) {
- $in = sprintf "%02d", $in;
- my $iname = InverterVal ($myHash, $in, 'iname', '');
+ $in = sprintf "%02d", $in;
+ $iname = InverterVal ($myHash, $in, 'iname', '');
if ($devName eq $iname) {
- my $iasync = InverterVal ($myHash, $in, 'iasynchron', 0);
+ $async = InverterVal ($myHash, $in, 'iasynchron', 0);
if ($debug =~ /notifyHandling/x) {
- Log3 ($myName, 1, qq{$myName DEBUG> notifyHandling - Event of Inverter device >$devName< received - asynchronous mode: $iasync});
+ Log3 ($myName, 1, qq{$myName DEBUG> notifyHandling - Event of Inverter device >$devName< received - asynchronous mode: $async});
}
- if ($iasync) {
+ if ($async) {
if (CurrentVal ($myHash, 'ctrunning', 0)) {
if ($debug =~ /notifyHandling/x) {
Log3 ($myName, 1, qq{$myName DEBUG> notifyHandling - central task was called from NOTIFY when it is already running ... end this call});
@@ -6302,7 +6332,6 @@ sub Notify {
if (@consumers && grep /^$devName$/, @consumers) {
my ($cname, $cindex, $dswname);
- my $type = $myHash->{TYPE};
for my $c (sort{$a<=>$b} keys %{$data{$myName}{consumers}}) {
($err, $cname, $dswname) = getCDnames ($myHash, $c);
@@ -7286,6 +7315,9 @@ sub centralTask {
Log3 ($name, 3, "$name - INFO - central task was called when it was already running ... end this call");
return;
}
+
+ $data{$name}{current}{ctrunning} = 1; # Central Task running Statusbit
+ InternalTimer (gettimeofday() + 1.2, "FHEM::SolarForecast::releaseCentralTask", $hash, 0); # Freigabe centralTask
my $t = time; # aktuelle Unix-Zeit
my $date = strftime "%Y-%m-%d", localtime($t); # aktuelles Datum
@@ -7293,11 +7325,7 @@ sub centralTask {
my $minute = strftime "%M", localtime($t); # aktuelle Minute (00-59)
my $day = strftime "%d", localtime($t); # aktueller Tag (range 01 .. 31)
my $dayname = strftime "%a", localtime($t); # aktueller Wochentagsname
- my $debug = getDebug ($hash); # Debug Module
-
- $data{$name}{current}{ctrunning} = 1; # Central Task running Statusbit
-
- InternalTimer (gettimeofday() + 1.2, "FHEM::SolarForecast::releaseCentralTask", $hash, 0); # Freigabe centralTask
+ my $debug = getDebug ($hash); # Debug Module
my $centpars = {
name => $name,
@@ -8435,9 +8463,9 @@ sub _transferInverterValues {
storeReading ('Current_PV', $pvsum.' W');
storeReading ('Today_Hour'.sprintf("%02d",$nhour).'_PVreal', $ethishoursum.' Wh'.$warn);
- $data{$name}{circular}{sprintf("%02d",$nhour)}{pvrl} = $ethishoursum; # Ringspeicher PV real Forum: https://forum.fhem.de/index.php/topic,117864.msg1133350.html#msg1133350
+ $data{$name}{circular}{sprintf("%02d",$nhour)}{pvrl} = $ethishoursum; # Ringspeicher PV real Forum: https://forum.fhem.de/index.php/topic,117864.msg1133350.html#msg1133350
- push @{$data{$name}{current}{genslidereg}}, $pvsum; # Schieberegister PV Erzeugung
+ push @{$data{$name}{current}{genslidereg}}, $pvsum; # Schieberegister PV Erzeugung
limitArray ($data{$name}{current}{genslidereg}, $slidenummax);
writeToHistory ( { paref => $paref, key => 'pvrl', val => $ethishoursum, hour => $nhour, valid => $aln } ); # valid=1: beim Learning berücksichtigen, 0: nicht
@@ -9197,158 +9225,174 @@ sub _transferBatteryValues {
my $chour = $paref->{chour};
my $day = $paref->{day};
- my $hash = $defs{$name};
- my ($err, $badev, $h) = isDeviceValid ( { name => $name, obj => 'setupBatteryDev', method => 'attr' } );
- return if($err);
+ my $hash = $defs{$name};
+ my $num = 0;
+ my $socsum;
+
+ for my $bn (1..$maxbatteries) {
+ $bn = sprintf "%02d", $bn;
+
+ my ($err, $badev, $h) = isDeviceValid ( { name => $name, obj => 'setupBatteryDev'.$bn, method => 'attr' } );
+ next if($err);
- my $type = $paref->{type};
+ $num++;
- my ($pin,$piunit) = split ":", $h->{pin}; # Readingname/Unit für aktuelle Batterieladung
- my ($pou,$pounit) = split ":", $h->{pout}; # Readingname/Unit für aktuelle Batterieentladung
- my ($bin,$binunit) = split ":", $h->{intotal} // "-:-"; # Readingname/Unit der total in die Batterie eingespeisten Energie (Zähler)
- my ($bout,$boutunit) = split ":", $h->{outtotal} // "-:-"; # Readingname/Unit der total aus der Batterie entnommenen Energie (Zähler)
- my $batchr = $h->{charge} // ""; # Readingname Ladezustand Batterie
- my $instcap = $h->{cap}; # numerischer Wert (Wh) oder Readingname installierte Batteriekapazität
+ my ($pin,$piunit) = split ":", $h->{pin}; # Readingname/Unit für aktuelle Batterieladung
+ my ($pou,$pounit) = split ":", $h->{pout}; # Readingname/Unit für aktuelle Batterieentladung
+ my ($bin,$binunit) = split ":", $h->{intotal} // "-:-"; # Readingname/Unit der total in die Batterie eingespeisten Energie (Zähler)
+ my ($bout,$boutunit) = split ":", $h->{outtotal} // "-:-"; # Readingname/Unit der total aus der Batterie entnommenen Energie (Zähler)
+ my $batchr = $h->{charge} // ""; # Readingname Ladezustand Batterie
+ my $instcap = $h->{cap}; # numerischer Wert (Wh) oder Readingname installierte Batteriekapazität
- return if(!$pin || !$pou);
+ return if(!$pin || !$pou);
- $pounit //= $piunit;
- $piunit //= $pounit;
- $boutunit //= $binunit;
- $binunit //= $boutunit;
+ $pounit //= $piunit;
+ $piunit //= $pounit;
+ $boutunit //= $binunit;
+ $binunit //= $boutunit;
- my $piuf = $piunit =~ /^kW$/xi ? 1000 : 1;
- my $pouf = $pounit =~ /^kW$/xi ? 1000 : 1;
- my $binuf = $binunit =~ /^kWh$/xi ? 1000 : 1;
- my $boutuf = $boutunit =~ /^kWh$/xi ? 1000 : 1;
+ my $piuf = $piunit =~ /^kW$/xi ? 1000 : 1;
+ my $pouf = $pounit =~ /^kW$/xi ? 1000 : 1;
+ my $binuf = $binunit =~ /^kWh$/xi ? 1000 : 1;
+ my $boutuf = $boutunit =~ /^kWh$/xi ? 1000 : 1;
- my $pbo = ReadingsNum ($badev, $pou, 0) * $pouf; # aktuelle Batterieentladung (W)
- my $pbi = ReadingsNum ($badev, $pin, 0) * $piuf; # aktueller Batterieladung (W)
- my $btotout = ReadingsNum ($badev, $bout, 0) * $boutuf; # totale Batterieentladung (Wh)
- my $btotin = ReadingsNum ($badev, $bin, 0) * $binuf; # totale Batterieladung (Wh)
- my $soc = ReadingsNum ($badev, $batchr, 0);
+ my $pbo = ReadingsNum ($badev, $pou, 0) * $pouf; # aktuelle Batterieentladung (W)
+ my $pbi = ReadingsNum ($badev, $pin, 0) * $piuf; # aktueller Batterieladung (W)
+ my $btotout = ReadingsNum ($badev, $bout, 0) * $boutuf; # totale Batterieentladung (Wh)
+ my $btotin = ReadingsNum ($badev, $bin, 0) * $binuf; # totale Batterieladung (Wh)
+ my $soc = ReadingsNum ($badev, $batchr, 0);
- if ($instcap) {
- if (!isNumeric ($instcap)) { # wenn $instcap Reading Wert abfragen
- my ($bcapr,$bcapunit) = split ':', $instcap;
- $bcapunit //= 'Wh';
- $instcap = ReadingsNum ($badev, $bcapr, 0);
- $instcap = $instcap * ($bcapunit =~ /^kWh$/xi ? 1000 : 1);
+ if ($instcap) {
+ if (!isNumeric ($instcap)) { # wenn $instcap Reading Wert abfragen
+ my ($bcapr,$bcapunit) = split ':', $instcap;
+ $bcapunit //= 'Wh';
+ $instcap = ReadingsNum ($badev, $bcapr, 0);
+ $instcap = $instcap * ($bcapunit =~ /^kWh$/xi ? 1000 : 1);
+ }
+
+ $data{$name}{batteries}{$bn}{binstcap} = $instcap; # installierte Batteriekapazität
+ }
+ else {
+ delete $data{$name}{batteries}{$bn}{binstcap};
}
- $data{$name}{current}{batinstcap} = $instcap; # installierte Batteriekapazität
+ my $debug = $paref->{debug};
+ if ($debug =~ /collectData/x) {
+ Log3 ($name, 1, "$name DEBUG> collect Battery data: device=$badev =>");
+ Log3 ($name, 1, "$name DEBUG> pin=$pbi W, pout=$pbo W, totalin: $btotin Wh, totalout: $btotout Wh, soc: $soc");
+ }
+
+ my $params;
+
+ if ($pin eq "-pout") { # Spezialfall pin bei neg. pout
+ $params = {
+ dev => $badev,
+ rdg => $pou,
+ rdgf => $pouf
+ };
+
+ ($pbo,$pbi) = substSpecialCases ($params);
+ }
+
+ if ($pou eq "-pin") { # Spezialfall pout bei neg. pin
+ $params = {
+ dev => $badev,
+ rdg => $pin,
+ rdgf => $piuf
+ };
+
+ ($pbi,$pbo) = substSpecialCases ($params);
+ }
+
+ # Batterielade, -entladeenergie in Circular speichern
+ #######################################################
+ if (!defined CircularVal ($hash, 99, 'initdaybatintot', undef)) {
+ $data{$name}{circular}{99}{initdaybatintot} = $btotin; # total Batterieladung zu Tagbeginn (Wh)
+ }
+
+ if (!defined CircularVal ($hash, 99, 'initdaybatouttot', undef)) { # total Batterieentladung zu Tagbeginn (Wh)
+ $data{$name}{circular}{99}{initdaybatouttot} = $btotout;
+ }
+
+ $data{$name}{circular}{99}{batintot} = $btotin; # aktuell total Batterieladung
+ $data{$name}{circular}{99}{batouttot} = $btotout; # aktuell total Batterieentladung
+
+ my $nhour = $chour+1;
+
+
+ # Batterieladung aktuelle Stunde in pvHistory speichern
+ #########################################################
+ my $histbatintot = HistoryVal ($hash, $day, sprintf("%02d",$nhour), "batintotal", undef); # totale Batterieladung zu Beginn einer Stunde
+ my $batinthishour;
+
+ if (!defined $histbatintot) { # totale Batterieladung der aktuelle Stunde gesetzt?
+ writeToHistory ( { paref => $paref, key => 'batintotal', val => $btotin, hour => $nhour } );
+
+ my $bitot = CurrentVal ($hash, "batintotal", $btotin);
+ $batinthishour = int ($btotin - $bitot);
+ }
+ else {
+ $batinthishour = int ($btotin - $histbatintot);
+ }
+
+ $batinthishour = 0 if($batinthishour < 0);
+
+ $data{$name}{circular}{sprintf("%02d",$nhour)}{batin} = $batinthishour; # Ringspeicher Battery In Forum: https://forum.fhem.de/index.php/topic,117864.msg1133350.html#msg1133350
+ writeToHistory ( { paref => $paref, key => 'batinthishour', val => $batinthishour, hour => $nhour } );
+
+
+ # Batterieentladung aktuelle Stunde in pvHistory speichern
+ ############################################################
+ my $histbatouttot = HistoryVal ($hash, $day, sprintf("%02d",$nhour), 'batouttotal', undef); # totale Betterieladung zu Beginn einer Stunde
+ my $batoutthishour;
+
+ if (!defined $histbatouttot) { # totale Betterieladung der aktuelle Stunde gesetzt?
+ writeToHistory ( { paref => $paref, key => 'batouttotal', val => $btotout, hour => $nhour } );
+
+ my $botot = CurrentVal ($hash, 'batouttotal', $btotout);
+ $batoutthishour = int ($btotout - $botot);
+ }
+ else {
+ $batoutthishour = int ($btotout - $histbatouttot);
+ }
+
+ $batoutthishour = 0 if($batoutthishour < 0);
+
+ $data{$name}{circular}{sprintf("%02d",$nhour)}{batout} = $batoutthishour; # Ringspeicher Battery In Forum: https://forum.fhem.de/index.php/topic,117864.msg1133350.html#msg1133350
+ writeToHistory ( { paref => $paref, key => 'batoutthishour', val => $batoutthishour, hour => $nhour } );
+
+
+ # täglichen max. SOC in pvHistory speichern
+ #############################################
+ my $batmaxsoc = HistoryVal ($hash, $day, 99, 'batmaxsoc', 0); # gespeicherter max. SOC des Tages
+
+ if ($soc >= $batmaxsoc) {
+ writeToHistory ( { paref => $paref, key => 'batmaxsoc', val => $soc, hour => 99 } );
+ }
+
+ ######
+
+ storeReading ('Today_Hour'.sprintf("%02d",$nhour).'_BatIn', $batinthishour.' Wh');
+ storeReading ('Today_Hour'.sprintf("%02d",$nhour).'_BatOut', $batoutthishour.' Wh');
+ storeReading ('Current_PowerBatIn', (int $pbi).' W');
+ storeReading ('Current_PowerBatOut', (int $pbo).' W');
+ storeReading ('Current_BatCharge', $soc.' %');
+
+ $data{$name}{batteries}{$bn}{bname} = $badev; # Batterie Devicename
+ $data{$name}{batteries}{$bn}{balias} = AttrVal ($badev, 'alias', $badev); # Alias Batterie Device
+ $data{$name}{batteries}{$bn}{bpowerin} = int $pbi; # momentane Batterieladung
+ $data{$name}{batteries}{$bn}{bpowerout} = int $pbo; # momentane Batterieentladung
+ $data{$name}{batteries}{$bn}{bcharge} = $soc; # aktuelle Batterieladung
+ $data{$name}{batteries}{$bn}{basynchron} = $h->{asynchron} // 0; # asynchroner Modus = X
+
+ $socsum += $soc;
}
- else {
- delete $data{$name}{current}{batinstcap};
+
+ if ($num) {
+ push @{$data{$name}{current}{socslidereg}}, $socsum / $num; # Schieberegister average SOC aller Batterien
+ limitArray ($data{$name}{current}{socslidereg}, $slidenummax);
}
- my $debug = $paref->{debug};
- if ($debug =~ /collectData/x) {
- Log3 ($name, 1, "$name DEBUG> collect Battery data: device=$badev =>");
- Log3 ($name, 1, "$name DEBUG> pin=$pbi W, pout=$pbo W, totalin: $btotin Wh, totalout: $btotout Wh, soc: $soc");
- }
-
- my $params;
-
- if ($pin eq "-pout") { # Spezialfall pin bei neg. pout
- $params = {
- dev => $badev,
- rdg => $pou,
- rdgf => $pouf
- };
-
- ($pbo,$pbi) = substSpecialCases ($params);
- }
-
- if ($pou eq "-pin") { # Spezialfall pout bei neg. pin
- $params = {
- dev => $badev,
- rdg => $pin,
- rdgf => $piuf
- };
-
- ($pbi,$pbo) = substSpecialCases ($params);
- }
-
- # Batterielade-, enladeenergie in Circular speichern
- ######################################################
- if (!defined CircularVal ($hash, 99, 'initdaybatintot', undef)) {
- $data{$name}{circular}{99}{initdaybatintot} = $btotin; # total Batterieladung zu Tagbeginn (Wh)
- }
-
- if (!defined CircularVal ($hash, 99, 'initdaybatouttot', undef)) { # total Batterieentladung zu Tagbeginn (Wh)
- $data{$name}{circular}{99}{initdaybatouttot} = $btotout;
- }
-
- $data{$name}{circular}{99}{batintot} = $btotin; # aktuell total Batterieladung
- $data{$name}{circular}{99}{batouttot} = $btotout; # aktuell total Batterieentladung
-
- my $nhour = $chour+1;
-
- # Batterieladung aktuelle Stunde in pvHistory speichern
- #########################################################
- my $histbatintot = HistoryVal ($hash, $day, sprintf("%02d",$nhour), "batintotal", undef); # totale Batterieladung zu Beginn einer Stunde
- my $batinthishour;
-
- if (!defined $histbatintot) { # totale Batterieladung der aktuelle Stunde gesetzt?
- writeToHistory ( { paref => $paref, key => 'batintotal', val => $btotin, hour => $nhour } );
-
- my $bitot = CurrentVal ($hash, "batintotal", $btotin);
- $batinthishour = int ($btotin - $bitot);
- }
- else {
- $batinthishour = int ($btotin - $histbatintot);
- }
-
- $batinthishour = 0 if($batinthishour < 0);
-
- $data{$name}{circular}{sprintf("%02d",$nhour)}{batin} = $batinthishour; # Ringspeicher Battery In Forum: https://forum.fhem.de/index.php/topic,117864.msg1133350.html#msg1133350
- writeToHistory ( { paref => $paref, key => 'batinthishour', val => $batinthishour, hour => $nhour } );
-
- # Batterieentladung aktuelle Stunde in pvHistory speichern
- ############################################################
- my $histbatouttot = HistoryVal ($hash, $day, sprintf("%02d",$nhour), 'batouttotal', undef); # totale Betterieladung zu Beginn einer Stunde
- my $batoutthishour;
-
- if (!defined $histbatouttot) { # totale Betterieladung der aktuelle Stunde gesetzt?
- writeToHistory ( { paref => $paref, key => 'batouttotal', val => $btotout, hour => $nhour } );
-
- my $botot = CurrentVal ($hash, 'batouttotal', $btotout);
- $batoutthishour = int ($btotout - $botot);
- }
- else {
- $batoutthishour = int ($btotout - $histbatouttot);
- }
-
- $batoutthishour = 0 if($batoutthishour < 0);
-
- $data{$name}{circular}{sprintf("%02d",$nhour)}{batout} = $batoutthishour; # Ringspeicher Battery In Forum: https://forum.fhem.de/index.php/topic,117864.msg1133350.html#msg1133350
- writeToHistory ( { paref => $paref, key => 'batoutthishour', val => $batoutthishour, hour => $nhour } );
-
- # täglichen max. SOC in pvHistory speichern
- #############################################
- my $batmaxsoc = HistoryVal ($hash, $day, 99, 'batmaxsoc', 0); # gespeicherter max. SOC des Tages
-
- if ($soc >= $batmaxsoc) {
- writeToHistory ( { paref => $paref, key => 'batmaxsoc', val => $soc, hour => 99 } );
- }
-
- ######
-
- storeReading ('Today_Hour'.sprintf("%02d",$nhour).'_BatIn', $batinthishour.' Wh');
- storeReading ('Today_Hour'.sprintf("%02d",$nhour).'_BatOut', $batoutthishour.' Wh');
- storeReading ('Current_PowerBatIn', (int $pbi).' W');
- storeReading ('Current_PowerBatOut', (int $pbo).' W');
- storeReading ('Current_BatCharge', $soc.' %');
-
- $data{$name}{current}{powerbatin} = int $pbi; # Hilfshash Wert aktuelle Batterieladung
- $data{$name}{current}{powerbatout} = int $pbo; # Hilfshash Wert aktuelle Batterieentladung
- $data{$name}{current}{batcharge} = $soc; # aktuelle Batterieladung
- $data{$name}{current}{batasynchron} = $h->{asynchron} if($h->{asynchron}); # asynchroner Modus = X
-
- push @{$data{$name}{current}{socslidereg}}, $soc; # Schieberegister Batterie SOC
- limitArray ($data{$name}{current}{socslidereg}, $slidenummax);
-
return;
}
@@ -9358,7 +9402,6 @@ return;
sub _batSocTarget {
my $paref = shift;
my $name = $paref->{name};
- my $type = $paref->{type};
my $t = $paref->{t}; # aktuelle Zeit
return if(!isBatteryUsed ($name));
@@ -9366,8 +9409,8 @@ sub _batSocTarget {
my $hash = $defs{$name};
my $oldd2care = CircularVal ($hash, 99, 'days2care', 0);
my $ltsmsr = CircularVal ($hash, 99, 'lastTsMaxSocRchd', undef);
- my $batcharge = CurrentVal ($hash, 'batcharge', 0); # aktuelle Ladung in %
- my $batinstcap = CurrentVal ($hash, 'batinstcap', 0); # installierte Batteriekapazität Wh
+ my $batcharge = BatteryVal ($hash, '01', 'bcharge', 0); # aktuelle Ladung in %
+ my $batinstcap = BatteryVal ($hash, '01', 'binstcap', 0); # installierte Batteriekapazität Wh
my $cgbt = AttrVal ($name, 'ctrlBatSocManagement', undef);
if ($cgbt && !$batinstcap) {
@@ -9484,7 +9527,8 @@ sub _batSocTarget {
$target < $lowSoc ? $lowSoc :
$target;
- debugLog ($paref, 'batteryManagement', "SoC calc Step4 - observe low/up limits -> docare: $docare, Target: $target %");
+ debugLog ($paref, 'batteryManagement', "SoC calc Step4 - basics -> docare: $docare, lowSoc: $lowSoc %, upSoc: $upSoc %");
+ debugLog ($paref, 'batteryManagement', "SoC calc Step4 - observe low/up limits -> Target: $target %");
## auf 5er Schritte anpassen (40,45,50,...)
#############################################
@@ -9569,8 +9613,8 @@ sub _batChargeRecmd {
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 $batcap = BatteryVal ($hash, '01', 'binstcap', 0); # installierte Batteriekapazität Wh
+ my $soc = BatteryVal ($hash, '01', 'bcharge', 0); # aktuelle Ladung in % # aktueller SOC (%)
my $curcon = ReadingsNum ($name, 'Current_Consumption', 0); # aktueller Verbrauch
my $inpmax = 0;
@@ -9750,14 +9794,14 @@ sub _createSummaries {
my $pvre = int $todaySumRe->{PV};
- push @{$data{$name}{current}{h4fcslidereg}}, int $next4HoursSum->{PV}; # Schieberegister 4h Summe Forecast
+ push @{$data{$name}{current}{h4fcslidereg}}, int $next4HoursSum->{PV}; # Schieberegister 4h Summe Forecast
limitArray ($data{$name}{current}{h4fcslidereg}, $slidenummax);
my $gcon = CurrentVal ($hash, 'gridconsumption', 0); # aktueller Netzbezug
my $tconsum = CurrentVal ($hash, 'tomorrowconsumption', undef); # Verbrauchsprognose für folgenden Tag
my $gfeedin = CurrentVal ($hash, 'gridfeedin', 0);
- my $batin = CurrentVal ($hash, 'powerbatin', 0); # aktuelle Batterieladung
- my $batout = CurrentVal ($hash, 'powerbatout', 0); # aktuelle Batterieentladung
+ my $batin = BatteryVal ($hash, '01', 'bpowerin', 0); # momentane Batterieladung
+ my $batout = BatteryVal ($hash, '01', 'bpowerout', 0); # momentane Batterieentladung
my $pvgen = 0;
my $pv2grid = 0; # PV-Erzeugung zu Grid-only
@@ -11400,8 +11444,8 @@ sub ___enableSwitchByBatPrioCharge {
return $ena if(!$pcb || !$badev); # Freigabe Schalten Consumer wenn kein Prefered Battery/Soll-Ladung 0 oder keine Batterie installiert
- my $cbcharge = CurrentVal ($hash, "batcharge", 0); # aktuelle Batterieladung
- $ena = 0 if($cbcharge < $pcb); # keine Freigabe wenn Batterieladung kleiner Soll-Ladung
+ my $bcharge = BatteryVal ($hash, '01', 'bcharge', 0); # aktuelle Ladung in %
+ $ena = 0 if($bcharge < $pcb); # keine Freigabe wenn Batterieladung kleiner Soll-Ladung
return $ena;
}
@@ -16611,11 +16655,12 @@ sub listDataPool {
}
}
- if ($htol =~ /consumers|inverters|producers|strings/xs) {
+ if ($htol =~ /consumers|inverters|producers|strings|batteries/xs) {
my $sub = $htol eq 'consumers' ? \&ConsumerVal :
$htol eq 'inverters' ? \&InverterVal :
$htol eq 'producers' ? \&ProducerVal :
$htol eq 'strings' ? \&StringVal :
+ $htol eq 'batteries' ? \&BatteryVal :
'';
$h = $data{$name}{$htol};
@@ -16635,8 +16680,13 @@ sub listDataPool {
next if($par && $idx ne $par);
my ($cret, $s1);
my $sp1 = _ldpspaces ($idx, q{});
-
+
for my $ckey (sort keys %{$h->{$idx}}) {
+ if (ref $h->{$idx}{$ckey} eq 'ARRAY') {
+ my $aser = join " ",@{$h->{$idx}{$ckey}};
+ $cret .= ($s1 ? $sp1 : "").$ckey." => ".$aser."\n";
+ }
+
if (ref $h->{$idx}{$ckey} eq 'HASH') {
my $hk = qq{};
for my $f (sort {$a<=>$b} keys %{$h->{$idx}{$ckey}}) {
@@ -16648,7 +16698,8 @@ sub listDataPool {
else {
$cret .= ($s1 ? $sp1 : "").$ckey." => ". &{$sub} ($hash, $idx, $ckey, "")."\n";
}
- $s1 = 1;
+
+ $s1 = 1;
}
$sq .= $idx." => ".$cret."\n";
}
@@ -18050,12 +18101,7 @@ sub createAssociatedWith {
$medev = $ame->[0] // '';
push @cd, $medev;
- my $badev = AttrVal ($name, 'setupBatteryDev', ''); # Battery Device
- ($aba,$h) = parseParams ($badev);
- $badev = $aba->[0] // '';
- push @cd, $badev;
-
- for my $c (sort{$a<=>$b} keys %{$data{$name}{consumers}}) { # Consumer Devices
+ for my $c (sort{$a<=>$b} keys %{$data{$name}{consumers}}) { # Consumer Devices
my $consumer = AttrVal ($name, "consumer${c}", "");
my ($ac,$hc) = parseParams ($consumer);
my $codev = $ac->[0] // '';
@@ -18064,9 +18110,16 @@ sub createAssociatedWith {
push @cd, $dswitch if($dswitch);
}
+ for my $bn (1..$maxbatteries) { # Battery Devices
+ $bn = sprintf "%02d", $bn;
+ my $badev = AttrVal ($name, "setupBatteryDev${bn}", '');
+ my ($aba) = parseParams ($badev);
+ push @cd, $aba->[0] if($aba->[0]);
+ }
+
for my $in (1..$maxinverter) { # Inverter Devices
$in = sprintf "%02d", $in;
- my $inc = AttrVal ($name, "setupInverterDev${in}", "");
+ my $inc = AttrVal ($name, "setupInverterDev${in}", '');
my ($ind) = parseParams ($inc);
push @cd, $ind->[0] if($ind->[0]);
}
@@ -18078,7 +18131,6 @@ sub createAssociatedWith {
push @nd, $fcdev3 if($fcdev3 && $fcdev3 !~ /-API/xs);
push @nd, $radev if($radev && $radev !~ /-API/xs);
push @nd, $medev;
- push @nd, $badev;
for my $prn (1..$maxproducer) { # Producer Devices
$prn = sprintf "%02d", $prn;
@@ -18623,11 +18675,18 @@ return ConsumerVal ($hash, $c, 'isConsumptionRecommended', 0);
################################################################
sub isBatteryUsed {
my $name = shift;
+
+ my $valid;
- my ($err) = isDeviceValid ( { name => $name, obj => 'setupBatteryDev', method => 'attr' } );
- return if($err);
+ for my $bn (1..$maxbatteries) {
+ $bn = sprintf "%02d", $bn;
+ my ($err) = isDeviceValid ( { name => $name, obj => 'setupBatteryDev'.$bn, method => 'attr' } );
+ next if($err);
+
+ $valid = 1;
+ }
-return 1;
+return $valid;
}
################################################################
@@ -19809,8 +19868,6 @@ return $def;
# $key: aiinitstate - Initialisierungsstatus der KI
# aitrainstate - Traisningsstatus der KI
# aiaddistate - Add Instanz Status der KI
-# batcharge - Bat SOC in %
-# batinstcap - installierte Batteriekapazität in Wh
# ctrunning - aktueller Ausführungsstatus des Central Task
# dwdRad1hAge - Alter des Rad1h Wertes als Datumstring
# dwdRad1hAgeTS - Alter des Rad1h Wertes als Unix Timestamp
@@ -19823,8 +19880,6 @@ return $def;
# consumerdevs - alle registrierten Consumerdevices (Array)
# consumerCollected - Statusbit Consumer Attr gesammelt und ausgewertet
# gridconsumption - aktueller Netzbezug
-# powerbatin - Batterie Ladeleistung
-# powerbatout - Batterie Entladeleistung
# temp - aktuelle Außentemperatur
# surplus - aktueller PV Überschuß
# tomorrowconsumption - Verbrauch des kommenden Tages
@@ -20032,6 +20087,42 @@ sub ConsumerVal {
return $def;
}
+###################################################################################################
+# Wert des Batterie-Hash zurückliefern
+# Usage:
+# BatteryVal ($hash or $name, $bn, $key, $def)
+#
+# $bn: Batterie Nummer (01,02,03,...)
+# $key: balias - Alias des Batterie Devices
+# bname - Name des Batterie Devices
+# basynchron - Asynchron Modus
+# bcharge - Bat SOC in %
+# binstcap - installierte Batteriekapazität in Wh
+# bpowerin - Batterie momentane Ladeleistung
+# bpowerout - Batterie momentane Entladeleistung
+#
+# $def: Defaultwert
+#
+###################################################################################################
+sub BatteryVal {
+ my $name = shift;
+ my $bn = shift;
+ my $key = shift;
+ my $def = shift;
+
+ if (ref $name eq 'HASH') {
+ $name = $name->{NAME};
+ }
+
+ if (defined $data{$name}{batteries} &&
+ defined $data{$name}{batteries}{$bn} &&
+ defined $data{$name}{batteries}{$bn}{$key}) {
+ return $data{$name}{batteries}{$bn}{$key};
+ }
+
+return $def;
+}
+
###################################################################################################
# Wert des Inverter-Hash zurückliefern
# Usage:
@@ -20289,7 +20380,7 @@ to ensure that the system configuration is correct.
setupRadiationAPI | DWD_OpenData Device or API for the delivery of radiation data. |
setupInverterDevXX | Device which provides PV performance data |
setupMeterDev | Device which supplies network I/O data |
- setupBatteryDev | Device which provides battery performance data (if available) |
+ setupBatteryDevXX | Device which provides battery performance data (if available) |
setupInverterStrings | Identifier of the existing plant strings |
setupStringAzimuth | Azimuth of the plant strings |
setupStringPeak | the DC peak power of the plant strings |
@@ -20372,6 +20463,7 @@ to ensure that the system configuration is correct.
batteryTrigger <1on>=<Value> <1off>=<Value> [<2on>=<Value> <2off>=<Value> ...]
Generates triggers when the battery charge exceeds or falls below certain values (SoC in %).
+ The SoC used is formed as an average of the SoCs of all defined battery devices.
If the last three SoC measurements exceed a defined Xon-Bedingung, the reading batteryTrigger_X = on
is created/set.
If the last three SoC measurements fall below a defined Xoff-Bedingung, the reading
@@ -21105,6 +21197,28 @@ to ensure that the system configuration is correct.
+
+
+
+ - valBattery
+ Shows the operating values determined for the selected battery or all defined battery devices.
+
+
+
+
+ bname | Name of the device |
+ balias | Alias of the device |
+ basynchron | Mode of processing received battery events |
+ bcharge | SOC (State of Charge) of the battery (%) |
+ binstcap | installed battery capacity (Wh) |
+ bpowerin | current charging power (W) |
+ bpowerout | current discharge power (W) |
+
+
+
+
+
+
@@ -21542,7 +21656,7 @@ to ensure that the system configuration is correct.
- ctrlBatSocManagement lowSoc=<Value> upSoC=<Value> [maxSoC=<Value>] [careCycle=<Value>]
- If a battery device (setupBatteryDev) is installed, this attribute activates the battery SoC management.
+ If a battery device (setupBatteryDevXX) is installed, this attribute activates the battery SoC management.
The Battery_OptimumTargetSoC reading contains the optimum minimum SoC calculated by the module.
The Battery_ChargeRequest reading is set to '1' if the current SoC has fallen below the minimum SoC.
In this case, the battery should be forcibly charged, possibly with mains power.
@@ -21764,11 +21878,11 @@ to ensure that the system configuration is correct.
- Beispiel:
+ Example:
{
- my $batdev = (split " ", AttrVal ($name, 'setupBatteryDev', ''))[0];
- my $pvfc = ReadingsNum ($name, 'RestOfDayPVforecast', 0);
- my $cofc = ReadingsNum ($name, 'RestOfDayConsumptionForecast', 0);
+ my $batdev = (split " ", AttrVal ($name, 'setupBatteryDev01', ''))[0];
+ my $pvfc = ReadingsNum ($name, 'RestOfDayPVforecast', 0);
+ my $cofc = ReadingsNum ($name, 'RestOfDayConsumptionForecast', 0);
my $diff = $pvfc - $cofc;
storeReading ('userFn_Battery_device', $batdev);
@@ -22204,10 +22318,10 @@ to ensure that the system configuration is correct.
-
- - setupBatteryDev <Battery Device Name> pin=<Readingname>:<Unit> pout=<Readingname>:<Unit>
- [intotal=<Readingname>:<Unit>] [outtotal=<Readingname>:<Unit>]
- cap=<Option> [charge=<Readingname>] [asynchron=<Option>]
+
+ - setupBatteryDevXX <Battery Device Name> pin=<Readingname>:<Unit> pout=<Readingname>:<Unit>
+ cap=<Option> [intotal=<Readingname>:<Unit>] [outtotal=<Readingname>:<Unit>]
+ [charge=<Readingname>] [asynchron=<Option>]
Specifies an arbitrary Device and its Readings to deliver the battery performance data.
The module assumes that the numerical value of the readings is always positive.
@@ -22247,7 +22361,7 @@ to ensure that the system configuration is correct.
Example:
- attr <name> setupBatteryDev BatDummy pin=BatVal:W pout=-pin intotal=BatInTot:Wh outtotal=BatOutTot:Wh cap=BatCap:kWh
+ attr <name> setupBatteryDev01 BatDummy pin=BatVal:W pout=-pin intotal=BatInTot:Wh outtotal=BatOutTot:Wh cap=BatCap:kWh
@@ -22711,7 +22825,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
setupRadiationAPI | DWD_OpenData Device bzw. API zur Lieferung von Strahlungsdaten |
setupInverterDevXX | Device welches PV Leistungsdaten liefert |
setupMeterDev | Device welches Netz I/O-Daten liefert |
- setupBatteryDev | Device welches Batterie Leistungsdaten liefert (sofern vorhanden) |
+ setupBatteryDevXX | Device welches Batterie Leistungsdaten liefert (sofern vorhanden) |
setupInverterStrings | Bezeichner der vorhandenen Anlagenstrings |
setupStringAzimuth | Ausrichtung (Azimut) der Anlagenstrings |
setupStringPeak | die DC-Peakleistung der Anlagenstrings |
@@ -22793,6 +22907,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
- batteryTrigger <1on>=<Wert> <1off>=<Wert> [<2on>=<Wert> <2off>=<Wert> ...]
Generiert Trigger bei Über- bzw. Unterschreitung bestimmter Batterieladungswerte (SoC in %).
+ Der verwendete SoC wird als Durchschnitt der SoC's aller definierten Batterie Geräte gebildet.
Überschreiten die letzten drei SoC-Messungen eine definierte Xon-Bedingung, wird das Reading
batteryTrigger_X = on erstellt/gesetzt.
Unterschreiten die letzten drei SoC-Messungen eine definierte Xoff-Bedingung, wird das Reading
@@ -23536,6 +23651,28 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
+
+
+
+ - valBattery
+ Zeigt die ermittelten Betriebswerte der ausgewählten Batterie oder aller definierten Batteriegeräte.
+
+
+
+
+ bname | Name des Gerätes |
+ balias | Alias des Gerätes |
+ basynchron | Modus der Verarbeitung empfangener Batterie-Events |
+ bcharge | SOC (State of Charge) der Batterie (%) |
+ binstcap | installierte Batteriekapazität (Wh) |
+ bpowerin | momentane Ladeleistung (W) |
+ bpowerout | momentane Entladeleistung (W) |
+
+
+
+
+
+
@@ -23589,8 +23726,8 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
ifeed | Eigenschaften der Energielieferung |
igeneration | aktuelle PV Erzeugung (W) |
iicon | die evtl. festgelegten Icons zur Darstellung des Gerätes in der Grafik |
- ialias | Alias des Devices |
- iname | Name des Devices |
+ ialias | Alias des Gerätes |
+ iname | Name des Gerätes |
invertercap | die nominale Leistung (W) des Wechselrichters (falls definiert) |
istrings | Liste der dem Wechselrichter zugeordneten Strings (falls definiert) |
@@ -23612,8 +23749,8 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
pfeed | Eigenschaften der Energielieferung |
pgeneration | aktuelle Leistung (W) |
picon | die evtl. festgelegten Icons zur Darstellung des Gerätes in der Grafik |
- palias | Alias des Devices |
- pname | Name des Devices |
+ palias | Alias des Gerätes |
+ pname | Name des Gerätes |
@@ -23971,7 +24108,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
ctrlBatSocManagement lowSoc=<Wert> upSoC=<Wert> [maxSoC=<Wert>] [careCycle=<Wert>]
- Sofern ein Batterie Device (setupBatteryDev) installiert ist, aktiviert dieses Attribut das Batterie
+ Sofern ein Batterie Device (setupBatteryDevXX) installiert ist, aktiviert dieses Attribut das Batterie
SoC-Management.
Das Reading Battery_OptimumTargetSoC enthält den vom Modul berechneten optimalen Mindest-SoC.
Das Reading Battery_ChargeRequest wird auf '1' gesetzt, wenn der aktuelle SoC unter den Mindest-SoC gefallen
@@ -24197,9 +24334,9 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
Beispiel:
{
- my $batdev = (split " ", AttrVal ($name, 'setupBatteryDev', ''))[0];
- my $pvfc = ReadingsNum ($name, 'RestOfDayPVforecast', 0);
- my $cofc = ReadingsNum ($name, 'RestOfDayConsumptionForecast', 0);
+ my $batdev = (split " ", AttrVal ($name, 'setupBatteryDev01', ''))[0];
+ my $pvfc = ReadingsNum ($name, 'RestOfDayPVforecast', 0);
+ my $cofc = ReadingsNum ($name, 'RestOfDayConsumptionForecast', 0);
my $diff = $pvfc - $cofc;
storeReading ('userFn_Battery_device', $batdev);
@@ -24634,10 +24771,10 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
-
- setupBatteryDev <Batterie Device Name> pin=<Readingname>:<Einheit> pout=<Readingname>:<Einheit>
- [intotal=<Readingname>:<Einheit>] [outtotal=<Readingname>:<Einheit>]
- cap=<Option> [charge=<Readingname>] [asynchron=<Option>]
+
+ setupBatteryDevXX <Batterie Device Name> pin=<Readingname>:<Einheit> pout=<Readingname>:<Einheit>
+ cap=<Option> [intotal=<Readingname>:<Einheit>] [outtotal=<Readingname>:<Einheit>]
+ [charge=<Readingname>] [asynchron=<Option>]
Legt ein beliebiges Device und seine Readings zur Lieferung der Batterie Leistungsdaten fest.
Das Modul geht davon aus, dass der numerische Wert der Readings immer positiv ist.
@@ -24677,7 +24814,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
Beispiel:
- attr <name> setupBatteryDev BatDummy pin=BatVal:W pout=-pin intotal=BatInTot:Wh outtotal=BatOutTot:Wh cap=BatCap:kWh
+ attr <name> setupBatteryDev01 BatDummy pin=BatVal:W pout=-pin intotal=BatInTot:Wh outtotal=BatOutTot:Wh cap=BatCap:kWh