diff --git a/fhem/contrib/DS_Starter/76_SolarForecast.pm b/fhem/contrib/DS_Starter/76_SolarForecast.pm
index 10d84c4b8..a42c0a160 100644
--- a/fhem/contrib/DS_Starter/76_SolarForecast.pm
+++ b/fhem/contrib/DS_Starter/76_SolarForecast.pm
@@ -155,7 +155,7 @@ BEGIN {
# Versions History intern
my %vNotesIntern = (
- "1.32.0" => "26.08.2024 vermeide Kalkulation und Speicherung negativer Verbrauchswerte ".
+ "1.32.0" => "02.09.2024 new attr setupOtherProducerXX, report calculation and storage of negative consumption values ".
"Forum: https://forum.fhem.de/index.php?msg=1319083 ",
"1.31.0" => "20.08.2024 rename attributes ctrlWeatherDevX to setupWeatherDevX ",
"1.30.0" => "18.08.2024 new attribute flowGraphicShift, Forum:https://forum.fhem.de/index.php?msg=1318597 ",
@@ -439,6 +439,7 @@ my $tempmodinc = 25;
my $tempbasedef = 25; # Temperatur Module bei Nominalleistung
my $maxconsumer = 16; # maximale Anzahl der möglichen Consumer (Attribut)
+my $maxproducer = 3; # maximale Anzahl der möglichen anderen Produzenten (Attribut)
my $epiecMaxCycles = 10; # Anzahl Einschaltzyklen (Consumer) für verbraucherspezifische Energiestück Ermittlung
my @ctypes = qw(dishwasher dryer washingmachine heater charger other
noSchedule); # erlaubte Consumer Typen
@@ -548,6 +549,11 @@ for my $cinit (1..$maxconsumer) {
push @dd, "consumerSwitching${cinit}"; # ctrlDebug: add specific Consumer
}
+for my $prn (1..$maxproducer) {
+ $prn = sprintf "%02d", $prn;
+ push @aconfigs, "setupOtherProducer${prn}"; # Anlagenkonfiguration: add Producer Attribute
+}
+
my $allwidgets = 'icon|sortable|uzsu|knob|noArg|time|text|slider|multiple|select|bitfield|widgetList|colorpicker';
# Steuerhashes
@@ -624,6 +630,11 @@ my %hattr = ( # H
setupRoofTops => { fn => \&_attrRoofTops },
);
+ for my $prn (1..$maxproducer) {
+ $prn = sprintf "%02d", $prn;
+ $hattr{'setupOtherProducer'.$prn}{fn} = \&_attrOtherProducer;
+ }
+
my %htr = ( # Hash even/odd für
0 => { cl => 'even' },
1 => { cl => 'odd' },
@@ -1106,7 +1117,13 @@ my %hfspvh = (
gcons => { fn => \&_storeVal, storname => 'gcons', validkey => undef, fpar => 'comp99' }, # bezogene Energie
gfeedin => { fn => \&_storeVal, storname => 'gfeedin', validkey => undef, fpar => 'comp99' }, # eingespeiste Energie
con => { fn => \&_storeVal, storname => 'con', validkey => undef, fpar => 'comp99' }, # realer Hausverbrauch Energie
- pvrl => { fn => \&_storeVal, storname => 'pvrl', validkey => 'pvrlvd', fpar => 'comp99' }, # realer Energieertrag
+ pvrl => { fn => \&_storeVal, storname => 'pvrl', validkey => 'pvrlvd', fpar => 'comp99' }, # realer Energieertrag PV
+ pprl01 => { fn => \&_storeVal, storname => 'pprl01', validkey => undef, fpar => 'comp99' }, # realer Energieertrag sonstiger Erzeuger 01
+ pprl02 => { fn => \&_storeVal, storname => 'pprl02', validkey => undef, fpar => 'comp99' }, # realer Energieertrag sonstiger Erzeuger 02
+ pprl03 => { fn => \&_storeVal, storname => 'pprl03', validkey => undef, fpar => 'comp99' }, # realer Energieertrag sonstiger Erzeuger 03
+ etotalp01 => { fn => \&_storeVal, storname => 'etotalp01', validkey => undef, fpar => undef }, # etotal sonstiger Erzeuger 01
+ etotalp02 => { fn => \&_storeVal, storname => 'etotalp02', validkey => undef, fpar => undef }, # etotal sonstiger Erzeuger 02
+ etotalp03 => { fn => \&_storeVal, storname => 'etotalp03', validkey => undef, fpar => undef }, # etotal sonstiger Erzeuger 03
);
@@ -1138,12 +1155,17 @@ sub Initialize {
my $srd = join ",", sort keys (%hcsr);
my $gbc = 'pvReal,pvForecast,consumption,consumptionForecast,gridconsumption,energycosts,gridfeedin,feedincome';
- my ($consumer,@allc);
+ my ($consumer, $setupprod, @allc);
for my $c (1..$maxconsumer) {
$c = sprintf "%02d", $c;
$consumer .= "consumer${c}:textField-long ";
push @allc, $c;
}
+
+ for my $prn (1..$maxproducer) {
+ $prn = sprintf "%02d", $prn;
+ $setupprod .= "setupOtherProducer${prn}:textField-long ";
+ }
my $allcs = join ",", @allc;
my $dm = 'none,'.join ",", sort @dd;
@@ -1239,6 +1261,7 @@ sub Initialize {
"setupBatteryDev:textField-long ".
"setupRadiationAPI ".
"setupStringPeak ".
+ $setupprod.
$consumer.
$readingFnAttributes;
@@ -2027,7 +2050,6 @@ sub _setreset { ## no critic "not used"
my $name = $paref->{name};
my $prop = $paref->{prop} // return qq{no source specified for reset};
my $type = $paref->{type};
-
my $hash = $defs{$name};
if ($prop eq 'pvHistory') {
@@ -5458,6 +5480,72 @@ sub _attrMeterDev { ## no critic "not used"
return;
}
+################################################################
+# Attr setupOtherProducer
+################################################################
+sub _attrOtherProducer { ## no critic "not used"
+ my $paref = shift;
+ my $name = $paref->{name};
+ my $aVal = $paref->{aVal};
+ my $aName = $paref->{aName};
+ my $type = $paref->{type};
+
+ return if(!$init_done);
+
+ my $hash = $defs{$name};
+ my $prn = (split 'Producer', $aName)[1];
+
+ if ($paref->{cmd} eq 'set') {
+ my ($err, $dev, $h) = isDeviceValid ( { name => $name, obj => $aVal, method => 'string' } );
+ return $err if($err);
+
+ if (!$h->{pcurr} || !$h->{etotal}) {
+ return qq{The syntax of '$aName' is not correct. Please consider the commandref.};
+ }
+ }
+ elsif ($paref->{cmd} eq 'del') {
+ $paref->{prn} = $prn;
+ __delProducerValues ($paref);
+ delete $paref->{prn};
+ }
+
+ InternalTimer (gettimeofday() + 2, 'FHEM::SolarForecast::createAssociatedWith', $hash, 0);
+ InternalTimer (gettimeofday() + 3, 'FHEM::SolarForecast::writeCacheToFile', [$name, 'plantconfig', $plantcfg.$name], 0); # Anlagenkonfiguration File schreiben
+
+return;
+}
+
+################################################################
+# löschen Werte eines Producers aus Speicherhashes
+################################################################
+sub __delProducerValues {
+ my $paref = shift;
+ my $name = $paref->{name};
+ my $prn = $paref->{prn} // return 'The producer number is empty'; # Producernummer (01, 02, 03)
+ my $type = $paref->{type};
+ my $hash = $defs{$name};
+
+ deleteReadingspec ($hash, ".*_PPreal".$prn);
+ readingsDelete ($hash, 'Current_PP'.$prn);
+ delete $data{$type}{$name}{current}{'generationp'.$prn};
+ delete $data{$type}{$name}{current}{'etotalp'.$prn};
+
+ for my $hod (keys %{$data{$type}{$name}{circular}}) {
+ delete $data{$type}{$name}{circular}{$hod}{'pprl'.$prn};
+ }
+
+ for my $dy (sort keys %{$data{$type}{$name}{pvhist}}) {
+ for my $hr (sort keys %{$data{$type}{$name}{pvhist}{$dy}}) {
+ delete $data{$type}{$name}{pvhist}{$dy}{$hr}{'pprl'.$prn};
+ delete $data{$type}{$name}{pvhist}{$dy}{$hr}{'etotalp'.$prn};
+ }
+ }
+
+ Log3 ($name, 3, qq{$name - all stored data from producer $prn has been deleted});
+
+return;
+}
+
################################################################
# Attr setupInverterDev
################################################################
@@ -6593,6 +6681,7 @@ sub centralTask {
_transferAPIRadiationValues ($centpars); # Raw Erzeugungswerte aus solcastapi-Hash übertragen und Forecast mit/ohne Korrektur erstellen
_calcMaxEstimateToday ($centpars); # heutigen Max PV Estimate & dessen Tageszeit ermitteln
_transferInverterValues ($centpars); # WR Werte übertragen
+ _transferProducerValues ($centpars); # Werte anderer Erzeuger übertragen
_transferMeterValues ($centpars); # Energy Meter auswerten
_transferBatteryValues ($centpars); # Batteriewerte einsammeln
_batSocTarget ($centpars); # Batterie Optimum Ziel SOC berechnen
@@ -7125,8 +7214,7 @@ sub _specialActivities {
$gcon = ReadingsNum ($name, "Today_Hour24_GridConsumption", 0);
storeReading ('LastHourGridconsumptionReal', "$gcon Wh", $ts);
- deleteReadingspec ($hash, '(Today_Hour(.*_Grid.*|.*_PV.*|.*_Bat.*)|powerTrigger_.*|Today_MaxPVforecast.*)');
-
+ deleteReadingspec ($hash, '(Today_Hour(.*_Grid.*|.*_PV.*|.*_PPreal.*|.*_Bat.*)|powerTrigger_.*|Today_MaxPVforecast.*)');
readingsDelete ($hash, 'Today_PVdeviation');
readingsDelete ($hash, 'Today_PVreal');
@@ -8235,6 +8323,83 @@ sub _transferInverterValues {
return;
}
+################################################################
+# Werte anderer Erzeuger ermitteln und übertragen
+################################################################
+sub _transferProducerValues {
+ my $paref = shift;
+ my $name = $paref->{name};
+ my $t = $paref->{t}; # aktuelle Unix-Zeit
+ my $chour = $paref->{chour};
+ my $day = $paref->{day};
+
+ my $hash = $defs{$name};
+
+ for my $prn (1..$maxproducer) {
+ $prn = sprintf "%02d", $prn;
+ my ($err, $prdev, $h) = isDeviceValid ( { name => $name, obj => 'setupOtherProducer'.$prn, method => 'attr' } );
+ next if($err);
+
+ my $type = $paref->{type};
+
+ my ($pcread, $pcunit) = split ":", $h->{pcurr}; # Readingname/Unit für aktuelle Erzeugung
+ my ($edread, $etunit) = split ":", $h->{etotal}; # Readingname/Unit für Energie total (Erzeugung)
+
+ next if(!$pcread || !$edread);
+
+ my $pu = $pcunit =~ /^kW$/xi ? 1000 : 1;
+ my $p = ReadingsNum ($prdev, $pcread, 0) * $pu; # aktuelle Erzeugung (W)
+ $p = $p < 0 ? 0 : sprintf("%.0f", $p);
+
+ storeReading ('Current_PP'.$prn, $p.' W');
+ $data{$type}{$name}{current}{'generationp'.$prn} = $p;
+
+ my $etu = $etunit =~ /^kWh$/xi ? 1000 : 1;
+ my $etotal = ReadingsNum ($prdev, $edread, 0) * $etu; # Erzeugung total (Wh)
+
+ debugLog ($paref, "collectData", "collect Producer$prn data - device: $prdev =>");
+ debugLog ($paref, "collectData", "pcurr: $p W, etotalp$prn: $etotal Wh");
+
+ my $nhour = $chour + 1;
+ my $histetot = HistoryVal ($hash, $day, sprintf("%02d",$nhour), 'etotalp'.$prn, 0); # etotal zu Beginn einer Stunde
+ my $warn = '';
+ my ($ethishour, $etotsvd);
+
+ if (!$histetot) { # etotal der aktuelle Stunde gesetzt ?
+ writeToHistory ( { paref => $paref, key => 'etotalp'.$prn, val => $etotal, hour => $nhour } );
+
+ $etotsvd = CurrentVal ($hash, 'etotalp'.$prn, $etotal);
+ $ethishour = int ($etotal - $etotsvd);
+ }
+ else {
+ $ethishour = int ($etotal - $histetot);
+ }
+
+ $data{$type}{$name}{current}{'etotalp'.$prn} = $etotal; # aktuellen etotal des WR speichern
+
+ if ($ethishour < 0) {
+ $ethishour = 0;
+ my $vl = 3;
+ my $pre = '- WARNING -';
+
+ if ($paref->{debug} =~ /collectData/xs) {
+ $vl = 1;
+ $pre = 'DEBUG> - WARNING -';
+ }
+
+ Log3 ($name, $vl, "$name $pre The Total Energy of Producer$prn '$prdev' is lower than the value saved before. This situation is unexpected and the Energy generated of current hour is set to '0'.");
+ $warn = ' (WARNING $prdev invalid real produced energy occured - see Logfile)';
+ }
+
+ storeReading ('Today_Hour'.sprintf("%02d",$nhour).'_PPreal'.$prn, $ethishour.' Wh'.$warn);
+ $data{$type}{$name}{circular}{sprintf("%02d",$nhour)}{'pprl'.$prn} = $ethishour; # Ringspeicher P real
+
+ writeToHistory ( { paref => $paref, key => 'pprl'.$prn, val => $ethishour, hour => $nhour } );
+ }
+
+return;
+}
+
################################################################
# Werte Meter Device ermitteln und übertragen
################################################################
@@ -10599,15 +10764,15 @@ sub _estConsumptionForecast {
next if($m eq $day); # next wenn gleicher Tag (Datum) wie heute
if ($swdfcfc) { # nur gleiche Tage (Mo...So) einbeziehen
- my $hdn = HistoryVal ($hash, $m, 99, "dayname", undef);
+ my $hdn = HistoryVal ($hash, $m, 99, 'dayname', undef);
next if(!$hdn || $hdn ne $nhday);
}
- my $hcon = HistoryVal ($hash, $m, $nhhr, "con", 0); # historische Verbrauchswerte
+ my $hcon = HistoryVal ($hash, $m, $nhhr, 'con', 0); # historische Verbrauchswerte
next if(!$hcon);
if ($hcon < 0) { # V1.32.0
- $hcon = 0;
+ #$hcon = 0;
my $vl = 3;
my $pre = '- WARNING -';
@@ -10616,7 +10781,7 @@ sub _estConsumptionForecast {
$pre = 'DEBUG> - WARNING -';
}
- Log3 ($name, $vl, "$name $pre The stored Energy consumption of day/hour $m/$nhhr is negative. This appears to be an error and this hour is taken into account in the consumption forecast with value '0'.");
+ Log3 ($name, $vl, "$name $pre The stored Energy consumption of day/hour $m/$nhhr is negative. This appears to be an error. The incorrect value can be deleted with 'set $name reset consumption $m $nhhr'.");
}
for my $c (sort{$a<=>$b} keys %{$acref}) { # historischen Verbrauch aller registrierten Verbraucher aufaddieren
@@ -11152,20 +11317,25 @@ return ($val1,$val2);
# Energieverbrauch des Hauses in History speichern
################################################################
sub saveEnergyConsumption {
- my $paref = shift;
- my $name = $paref->{name};
- my $chour = $paref->{chour};
+ my $paref = shift;
+ my $name = $paref->{name};
+ my $chour = $paref->{chour};
- my $shr = $chour + 1;
- my $pvrl = ReadingsNum ($name, "Today_Hour".sprintf("%02d",$shr)."_PVreal", 0);
- my $gfeedin = ReadingsNum ($name, "Today_Hour".sprintf("%02d",$shr)."_GridFeedIn", 0);
- my $gcon = ReadingsNum ($name, "Today_Hour".sprintf("%02d",$shr)."_GridConsumption", 0);
- my $batin = ReadingsNum ($name, "Today_Hour".sprintf("%02d",$shr)."_BatIn", 0);
- my $batout = ReadingsNum ($name, "Today_Hour".sprintf("%02d",$shr)."_BatOut", 0);
+ my $shr = sprintf "%02d", ($chour + 1);
+ my $pvrl = ReadingsNum ($name, "Today_Hour".$shr."_PVreal", 0);
+ my $gfeedin = ReadingsNum ($name, "Today_Hour".$shr."_GridFeedIn", 0);
+ my $gcon = ReadingsNum ($name, "Today_Hour".$shr."_GridConsumption", 0);
+ my $batin = ReadingsNum ($name, "Today_Hour".$shr."_BatIn", 0);
+ my $batout = ReadingsNum ($name, "Today_Hour".$shr."_BatOut", 0);
+
my $con = $pvrl - $gfeedin + $gcon - $batin + $batout;
- if ($con < 0) { # V1.32.0
- $con = 0;
+ for my $prn (1..$maxproducer) { # V1.32.0 : Erzeugung sonstiger Producer (01..03) hinzufügen
+ $prn = sprintf "%02d", $prn;
+ $con += ReadingsNum ($name, "Today_Hour".$shr."_PPreal".$prn, 0);
+ }
+
+ if (int $paref->{minute} > 30 && $con < 0) { # V1.32.0 : erst den "eingeschwungenen" Zustand mit mehreren Meßwerten auswerten
my $vl = 3;
my $pre = '- WARNING -';
@@ -11174,7 +11344,7 @@ sub saveEnergyConsumption {
$pre = 'DEBUG> - WARNING -';
}
- Log3 ($name, $vl, "$name $pre The calculated Energy consumption of the house is negative. This appears to be an error and the energy consumption for the current hour is set to '0'.");
+ Log3 ($name, $vl, "$name $pre The calculated Energy consumption of the house is negative. This appears to be an error. Check Readings _PVreal, _GridFeedIn, _GridConsumption, _BatIn, _BatOut of hour >$shr<");
}
writeToHistory ( { paref => $paref, key => 'con', val => $con, hour => $shr } );
@@ -11712,15 +11882,6 @@ sub _checkSetupNotComplete {
### nicht mehr benötigte Daten verarbeiten - Bereich kann später wieder raus !!
##########################################################################################
- my $dir = ReadingsVal ($name, 'moduleAzimuth', ''); # 16.06.2024
- if ($dir) {
- readingsSingleUpdate ($hash, 'setupStringAzimuth', $dir, 0);
- }
-
- my $dec = ReadingsVal ($name, 'moduleDeclination', ''); # 16.06.2024
- if ($dec) {
- readingsSingleUpdate ($hash, 'setupStringDeclination', $dec, 0);
- }
##########################################################################################
@@ -14998,7 +15159,13 @@ sub listDataPool {
my $don = HistoryVal ($hash, $day, $key, 'DoN', '-');
my $conprc = HistoryVal ($hash, $day, $key, 'conprice', '-');
my $feedprc = HistoryVal ($hash, $day, $key, 'feedprice', '-');
-
+ my $etotp01 = HistoryVal ($hash, $day, $key, 'etotalp01', '-');
+ my $etotp02 = HistoryVal ($hash, $day, $key, 'etotalp02', '-');
+ my $etotp03 = HistoryVal ($hash, $day, $key, 'etotalp03', '-');
+ my $pprl01 = HistoryVal ($hash, $day, $key, 'pprl01', '-');
+ my $pprl02 = HistoryVal ($hash, $day, $key, 'pprl02', '-');
+ my $pprl03 = HistoryVal ($hash, $day, $key, 'pprl03', '-');
+
if ($export eq 'csv') {
$hexp->{$day}{$key}{PVreal} = $pvrl;
$hexp->{$day}{$key}{PVrealValid} = $pvrlvd;
@@ -15027,28 +15194,44 @@ sub listDataPool {
$hexp->{$day}{$key}{DayOrNight} = $don;
$hexp->{$day}{$key}{PurchasePrice} = $conprc;
$hexp->{$day}{$key}{FeedInPrice} = $feedprc;
+ $hexp->{$day}{$key}{etotalp01} = $etotp01;
+ $hexp->{$day}{$key}{etotalp02} = $etotp02;
+ $hexp->{$day}{$key}{etotalp03} = $etotp03;
+ $hexp->{$day}{$key}{pprl01} = $pprl01;
+ $hexp->{$day}{$key}{pprl02} = $pprl02;
+ $hexp->{$day}{$key}{pprl03} = $pprl03;
}
$ret .= "\n " if($ret);
$ret .= $key." => ";
- $ret .= "etotal: $etotal, pvfc: $pvfc, pvrl: $pvrl, pvrlvd: $pvrlvd, rad1h: $rad1h";
+ $ret .= "etotal: $etotal, " if($key ne '99');
+ $ret .= "pvfc: $pvfc, pvrl: $pvrl, pvrlvd: $pvrlvd, rad1h: $rad1h";
$ret .= "\n ";
+ $ret .= "etotalp01: $etotp01, etotalp02: $etotp02, etotalp03: $etotp03" if($key ne '99');
+ $ret .= "\n " if($key ne '99');
+ $ret .= "pprl01: $pprl01, pprl02: $pprl02, pprl03: $pprl03";
+ $ret .= "\n ";
$ret .= "confc: $confc, con: $con, gcons: $gcons, conprice: $conprc";
$ret .= "\n ";
$ret .= "gfeedin: $gfeedin, feedprice: $feedprc";
$ret .= "\n ";
$ret .= "DoN: $don, sunaz: $sunaz, sunalt: $sunalt";
$ret .= "\n ";
- $ret .= "batintotal: $btotin, batin: $batin, batouttotal: $btotout, batout: $batout";
- $ret .= "\n " if($key eq '99');
- $ret .= "batmaxsoc: $batmsoc, batsetsoc: $batssoc" if($key eq '99');
+ $ret .= "batintotal: $btotin, batouttotal: $btotout, " if($key ne '99');
+ $ret .= "batin: $batin, batout: $batout";
+ $ret .= "\n " if($key eq '99');
+ $ret .= "batmaxsoc: $batmsoc, batsetsoc: $batssoc" if($key eq '99');
$ret .= "\n ";
- $ret .= "wid: $wid";
- $ret .= ", wcc: $wcc";
- $ret .= ", rr1c: $rr1c";
- $ret .= ", temp: $temp" if($temp);
- $ret .= ", pvcorrf: $pvcorrf";
- $ret .= ", dayname: $dayname" if($dayname);
+
+ if ($key ne '99') {
+ $ret .= "wid: $wid, ";
+ $ret .= "wcc: $wcc, ";
+ $ret .= "rr1c: $rr1c, ";
+ $ret .= "pvcorrf: $pvcorrf";
+ }
+
+ $ret .= "temp: $temp, " if($temp);
+ $ret .= "dayname: $dayname, " if($dayname);
my $csm;
for my $c (1..$maxconsumer) {
@@ -15193,31 +15376,34 @@ sub listDataPool {
my $confc = CircularVal ($hash, $idx, 'confc', '-');
my $gcons = CircularVal ($hash, $idx, 'gcons', '-');
my $gfeedin = CircularVal ($hash, $idx, 'gfeedin', '-');
- my $wid = CircularVal ($hash, $idx, "weatherid", '-');
- my $wtxt = CircularVal ($hash, $idx, "weathertxt", '-');
- my $wccv = CircularVal ($hash, $idx, "wcc", '-');
- my $rr1c = CircularVal ($hash, $idx, "rr1c", '-');
- my $temp = CircularVal ($hash, $idx, "temp", '-');
- my $pvcorrf = CircularVal ($hash, $idx, "pvcorrf", '-');
- my $quality = CircularVal ($hash, $idx, "quality", '-');
- my $batin = CircularVal ($hash, $idx, "batin", '-');
- my $batout = CircularVal ($hash, $idx, "batout", '-');
- my $tdayDvtn = CircularVal ($hash, $idx, "tdayDvtn", '-');
- my $ydayDvtn = CircularVal ($hash, $idx, "ydayDvtn", '-');
+ my $wid = CircularVal ($hash, $idx, 'weatherid', '-');
+ my $wtxt = CircularVal ($hash, $idx, 'weathertxt', '-');
+ my $wccv = CircularVal ($hash, $idx, 'wcc', '-');
+ my $rr1c = CircularVal ($hash, $idx, 'rr1c', '-');
+ my $temp = CircularVal ($hash, $idx, 'temp', '-');
+ my $pvcorrf = CircularVal ($hash, $idx, 'pvcorrf', '-');
+ my $quality = CircularVal ($hash, $idx, 'quality', '-');
+ my $batin = CircularVal ($hash, $idx, 'batin', '-');
+ my $batout = CircularVal ($hash, $idx, 'batout', '-');
+ my $tdayDvtn = CircularVal ($hash, $idx, 'tdayDvtn', '-');
+ my $ydayDvtn = CircularVal ($hash, $idx, 'ydayDvtn', '-');
my $ltsmsr = CircularVal ($hash, $idx, 'lastTsMaxSocRchd', '-');
my $ntsmsc = CircularVal ($hash, $idx, 'nextTsMaxSocChge', '-');
my $dtocare = CircularVal ($hash, $idx, 'days2care', '-');
- my $fitot = CircularVal ($hash, $idx, "feedintotal", '-');
- my $idfi = CircularVal ($hash, $idx, "initdayfeedin", '-');
- my $gcontot = CircularVal ($hash, $idx, "gridcontotal", '-');
- my $idgcon = CircularVal ($hash, $idx, "initdaygcon", '-');
- my $idbitot = CircularVal ($hash, $idx, "initdaybatintot", '-');
- my $bitot = CircularVal ($hash, $idx, "batintot", '-');
- my $idbotot = CircularVal ($hash, $idx, "initdaybatouttot", '-');
- my $botot = CircularVal ($hash, $idx, "batouttot", '-');
- my $rtaitr = CircularVal ($hash, $idx, "runTimeTrainAI", '-');
- my $fsaitr = CircularVal ($hash, $idx, "aitrainLastFinishTs", '-');
- my $aicts = CircularVal ($hash, $idx, "attrInvChangedTs", '-');
+ my $fitot = CircularVal ($hash, $idx, 'feedintotal', '-');
+ my $idfi = CircularVal ($hash, $idx, 'initdayfeedin', '-');
+ my $gcontot = CircularVal ($hash, $idx, 'gridcontotal', '-');
+ my $idgcon = CircularVal ($hash, $idx, 'initdaygcon', '-');
+ my $idbitot = CircularVal ($hash, $idx, 'initdaybatintot', '-');
+ my $bitot = CircularVal ($hash, $idx, 'batintot', '-');
+ my $idbotot = CircularVal ($hash, $idx, 'initdaybatouttot', '-');
+ my $botot = CircularVal ($hash, $idx, 'batouttot', '-');
+ my $rtaitr = CircularVal ($hash, $idx, 'runTimeTrainAI', '-');
+ my $fsaitr = CircularVal ($hash, $idx, 'aitrainLastFinishTs', '-');
+ my $aicts = CircularVal ($hash, $idx, 'attrInvChangedTs', '-');
+ my $pprl01 = CircularVal ($hash, $idx, 'pprl01', '-');
+ my $pprl02 = CircularVal ($hash, $idx, 'pprl02', '-');
+ my $pprl03 = CircularVal ($hash, $idx, 'pprl03', '-');
my $pvcf = _ldchash2val ( {pool => $h, idx => $idx, key => 'pvcorrf', cval => $pvcorrf} );
my $cfq = _ldchash2val ( {pool => $h, idx => $idx, key => 'quality', cval => $quality} );
@@ -15231,6 +15417,7 @@ sub listDataPool {
$sq .= $idx." => pvapifc: $pvapifc, pvaifc: $pvaifc, pvfc: $pvfc, aihit: $aihit, pvrl: $pvrl\n";
$sq .= " batin: $batin, batout: $batout, confc: $confc, gcon: $gcons, gfeedin: $gfeedin, wcc: $wccv, rr1c: $rr1c\n";
$sq .= " temp: $temp, wid: $wid, wtxt: $wtxt\n";
+ $sq .= " pprl01: $pprl01, pprl02: $pprl02, pprl03: $pprl03\n";
$sq .= " pvcorrf: $pvcf\n";
$sq .= " quality: $cfq\n";
$sq .= " pvrlsum: $pvrs\n";
@@ -16422,7 +16609,7 @@ sub createAssociatedWith {
my $badev = AttrVal ($name, 'setupBatteryDev', ''); # Battery Device
($aba,$h) = parseParams ($badev);
$badev = $aba->[0] // "";
-
+
for my $c (sort{$a<=>$b} keys %{$data{$type}{$name}{consumers}}) { # Consumer Devices
my $consumer = AttrVal ($name, "consumer${c}", "");
my ($ac,$hc) = parseParams ($consumer);
@@ -16441,6 +16628,13 @@ sub createAssociatedWith {
push @nd, $indev;
push @nd, $medev;
push @nd, $badev;
+
+ for my $prn (1..$maxproducer) {
+ $prn = sprintf "%02d", $prn;
+ my $pdc = AttrVal ($name, "setupOtherProducer${prn}", "");
+ my ($prd) = parseParams ($pdc);
+ push @nd, $prd->[0] if($prd->[0]);
+ }
my @ndn = ();
@@ -19076,12 +19270,14 @@ to ensure that the system configuration is correct.
| csmeXX | Energy consumption of ConsumerXX in the hour of the day (hour 99 = daily energy consumption) |
| cyclescsmXX | Number of active cycles of ConsumerXX of the day |
| DoN | Sunrise and sunset status (0 - night, 1 - day) |
- | etotal | total energy yield (Wh) at the beginning of the hour |
+ | etotal | PV meter reading “Total energy yield” (Wh) at the beginning of the hour |
+ | etotalpXX | Meter reading “Total energy yield” (Wh) of producer XX at the beginning of the hour |
| gcons | real power consumption (Wh) from the electricity grid |
| gfeedin | real feed-in (Wh) into the electricity grid |
| feedprice | Remuneration for the feed-in of one kWh. The currency of the price is defined in the setupMeterDev. |
| hourscsmeXX | total active hours of the day from ConsumerXX |
| minutescsmXX | total active minutes in the hour of ConsumerXX |
+ | pprlXX | Energy generation of producer XX (see attribute setupOtherProducerXX) in the hour (Wh) |
| pvfc | the predicted PV yield (Wh) |
| pvrl | real PV generation (Wh) |
| pvrlvd | 1-'pvrl' is valid and is taken into account in the learning process, 0-'pvrl' is assessed as abnormal |
@@ -19138,6 +19334,7 @@ to ensure that the system configuration is correct.
| pvfcsum | summary PV forecast per cloud area over the entire term |
| pvrl | real PV generation of the last 24h (Attention: pvforecast and pvreal do not refer to the same period!) |
| pvrlsum | summary real PV generation per cloud area over the entire term |
+ | pprlXX | Energy generation of producer XX (see attribute setupOtherProducerXX) in the last 24 hours (Wh) |
| quality | Quality of the autocorrection factors (0..1), where 'simple' is the quality of the simple correction factor. |
| runTimeTrainAI | Duration of the last AI training |
| aitrainLastFinishTs | Timestamp of the last successful AI training |
@@ -20380,7 +20577,7 @@ to ensure that the system configuration is correct.
- | pv | Reading which provides the current PV generation |
+ | pv | Reading which provides the current PV generation as a positive value |
| etotal | Reading which provides the total PV energy generated (a steadily increasing counter). |
| | If the reading violates the specification of a continuously rising counter, |
| | SolarForecast handles this error and reports the situation by means of a log message. |
@@ -21367,13 +21564,15 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
| conprice | Preis für den Bezug einer kWh. Die Einheit des Preises ist im setupMeterDev definiert. |
| cyclescsmXX | Anzahl aktive Zyklen von ConsumerXX des Tages |
| DoN | Sonnenauf- und untergangsstatus (0 - Nacht, 1 - Tag) |
- | etotal | totaler Energieertrag (Wh) zu Beginn der Stunde |
+ | etotal | PV Zählerstand "Energieertrag total" (Wh) zu Beginn der Stunde |
+ | etotalpXX | Zählerstand "Energieertrag total" (Wh) des Produzenten XX zu Beginn der Stunde |
| gcons | realer Leistungsbezug (Wh) aus dem Stromnetz |
| gfeedin | reale Einspeisung (Wh) in das Stromnetz |
| feedprice | Vergütung für die Einpeisung einer kWh. Die Währung des Preises ist im setupMeterDev definiert. |
| avgcycmntscsmXX | durchschnittliche Dauer eines Einschaltzyklus des Tages von ConsumerXX in Minuten |
| hourscsmeXX | Summe Aktivstunden des Tages von ConsumerXX |
| minutescsmXX | Summe Aktivminuten in der Stunde von ConsumerXX |
+ | pprlXX | Energieerzeugung des Produzenten XX (siehe Attribut setupOtherProducerXX) in der Stunde (Wh) |
| pvfc | der prognostizierte PV Ertrag (Wh) |
| pvrl | reale PV Erzeugung (Wh) |
| pvrlvd | 1-'pvrl' ist gültig und wird im Lernprozess berücksichtigt, 0-'pvrl' ist als abnormal bewertet |
@@ -21431,6 +21630,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
| pvfcsum | Summe PV Prognose pro Bewölkungsbereich über die gesamte Laufzeit |
| pvrl | reale PV Erzeugung der letzten 24h (Achtung: pvforecast und pvreal beziehen sich nicht auf den gleichen Zeitraum!) |
| pvrlsum | Summe reale PV Erzeugung pro Bewölkungsbereich über die gesamte Laufzeit |
+ | pprlXX | Energieerzeugung des Produzenten XX (siehe Attribut setupOtherProducerXX) der letzten 24 Stunden (Wh) |
| quality | Qualität der Autokorrekturfaktoren (0..1), wobei 'simple' die Qualität des einfach berechneten Korrekturfaktors ist. |
| runTimeTrainAI | Laufzeit des letzten KI Trainings |
| aitrainLastFinishTs | Timestamp des letzten erfolgreichen KI Trainings |
@@ -22671,10 +22871,10 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
- | pv | Reading welches die aktuelle PV-Erzeugung liefert |
+ | pv | Reading welches die aktuelle PV-Erzeugung als positiven Wert liefert |
| etotal | Reading welches die gesamte erzeugte PV-Energie liefert (ein stetig aufsteigender Zähler) |
| | Sollte des Reading die Vorgabe eines stetig aufsteigenden Zählers verletzen, behandelt |
- | | SolarForecast diesen Fehler und meldet die aufgetretene Situation durch eine Logmeldung. |
+ | | SolarForecast diesen Fehler und meldet die aufgetretene Situation durch einen Logeintrag. |
| Einheit | die jeweilige Einheit (W,kW,Wh,kWh) |
| capacity | Bemessungsleistung des Wechselrichters gemäß Datenblatt, d.h. max. möglicher Output in Watt |
| | (Die Angabe ist optional, wird aber dringend empfohlen zu setzen) |
@@ -22763,6 +22963,36 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
Hinweis: Durch Löschen des Attributes werden ebenfalls die intern korrespondierenden Daten entfernt.
+
+
+ - setupOtherProducerXX <Device Name> pcurr=<Readingname>:<Einheit> etotal=<Readingname>:<Einheit>
+
+ Legt ein beliebiges Device und dessen Readings zur Lieferung sonstiger Erzeugungswerte fest
+ (z.B. BHKW, Winderzeugung, Notstromaggregat).
+ Es kann auch ein Dummy Device mit entsprechenden Readings sein.
+
+
+
+
+
+ | pcurr | Reading welches die aktuelle Erzeugung als positiven Wert liefert |
+ | etotal | Reading welches die gesamte erzeugte Energie liefert (ein stetig aufsteigender Zähler) |
+ | | Sollte des Reading die Vorgabe eines stetig aufsteigenden Zählers verletzen, behandelt |
+ | | SolarForecast diesen Fehler und meldet die aufgetretene Situation durch einen Logeintrag. |
+ | Einheit | die jeweilige Einheit (W,kW,Wh,kWh) |
+
+
+
+
+
+ Beispiel:
+ attr <name> setupOtherProducer01 windwheel p=total_pac:kW etotal=etotal:kWh
+
+
+
+ Hinweis: Durch Löschen des Attributes werden ebenfalls die intern korrespondierenden Daten entfernt.
+
+
- setupRadiationAPI