2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-13 23:36:37 +00:00

76_SolarForecast: calculation of consumption forecast

git-svn-id: https://svn.fhem.de/fhem/trunk@29156 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
nasseeder1 2024-09-21 09:17:58 +00:00
parent c22b662e66
commit 5e56c2ea1a
3 changed files with 495 additions and 157 deletions

View File

@ -1,5 +1,6 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it
- bugfix: 76_SolarForecast: calculation of consumption forecast
- bugfix: 36_Shelly: removed irrelevant log-entries for dimmer
- bugfix: 36_Shelly: removed package command: use Sub::Util
- bugfix: 36_Shelly: added package command: use Sub::Util

View File

@ -155,6 +155,9 @@ BEGIN {
# Versions History intern
my %vNotesIntern = (
"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 ".
"bugfix in _estConsumptionForecast, new ctrlDebug consumption_long ",
"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 ",
"1.29.4" => "03.08.2024 delete writeCacheToFile from _getRoofTopData, _specialActivities: avoid loop caused by \@widgetreadings ",
@ -437,6 +440,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
@ -482,6 +486,17 @@ my $cssdef = qq{.flowg.text { stroke: none; fill: gray; font-size: 60p
qq{.flowg.active_bat_in { stroke: darkorange; stroke-dashoffset: 20; stroke-dasharray: 10; opacity: 0.8; animation: dash 0.5s linear; animation-iteration-count: infinite; } \n}.
qq{.flowg.active_bat_out { stroke: green; stroke-dashoffset: 20; stroke-dasharray: 10; opacity: 0.8; animation: dash 0.5s linear; animation-iteration-count: infinite; } \n}
;
# initiale Hashes für Stunden Consumption Forecast inkl. und exkl. Verbraucher
my $conhfc = { "01" => 0, "02" => 0, "03" => 0, "04" => 0, "05" => 0, "06" => 0, "07" => 0, "08" => 0,
"09" => 0, "10" => 0, "11" => 0, "12" => 0, "13" => 0, "14" => 0, "15" => 0, "16" => 0,
"17" => 0, "18" => 0, "19" => 0, "20" => 0, "21" => 0, "22" => 0, "23" => 0, "24" => 0,
};
my $conhfcex = { "01" => 0, "02" => 0, "03" => 0, "04" => 0, "05" => 0, "06" => 0, "07" => 0, "08" => 0,
"09" => 0, "10" => 0, "11" => 0, "12" => 0, "13" => 0, "14" => 0, "15" => 0, "16" => 0,
"17" => 0, "18" => 0, "19" => 0, "20" => 0, "21" => 0, "22" => 0, "23" => 0, "24" => 0,
};
# mögliche Debug-Module
my @dd = qw( aiProcess
@ -492,6 +507,7 @@ my @dd = qw( aiProcess
collectData
consumerPlanning
consumption
consumption_long
dwdComm
epiecesCalc
graphic
@ -524,6 +540,7 @@ my @aconfigs = qw( affect70percentRule affectBatteryPreferredCharge affectConsFo
ctrlLanguage ctrlNextDayForecastReadings ctrlShowLink ctrlSolCastAPImaxReq
ctrlSolCastAPIoptimizeReq ctrlStatisticReadings ctrlUserExitFn
setupWeatherDev1 setupWeatherDev2 setupWeatherDev3
setupOtherProducer01 setupOtherProducer02 setupOtherProducer03
disable
flowGraphicSize flowGraphicAnimate flowGraphicConsumerDistance flowGraphicShowConsumer
flowGraphicShowConsumerDummy flowGraphicShowConsumerPower flowGraphicShowConsumerRemainTime
@ -546,6 +563,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
@ -622,6 +644,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 <tr>
0 => { cl => 'even' },
1 => { cl => 'odd' },
@ -1104,7 +1131,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
);
@ -1136,12 +1169,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;
@ -1237,6 +1275,7 @@ sub Initialize {
"setupBatteryDev:textField-long ".
"setupRadiationAPI ".
"setupStringPeak ".
$setupprod.
$consumer.
$readingFnAttributes;
@ -2025,7 +2064,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') {
@ -2041,11 +2079,13 @@ sub _setreset { ## no critic "not used"
$paref->{reorg} = 1; # den Tag Stunde "99" reorganisieren
$paref->{reorgday} = $dday;
$paref->{histname} = '';
setPVhistory ($paref);
delete $paref->{reorg};
delete $paref->{reorgday};
delete $paref->{histname};
}
else {
delete $data{$type}{$name}{pvhist}{$dday};
@ -2071,11 +2111,13 @@ sub _setreset { ## no critic "not used"
$paref->{reorg} = 1; # den Tag Stunde "99" reorganisieren
$paref->{reorgday} = $dday;
$paref->{histname} = '';
setPVhistory ($paref);
delete $paref->{reorg};
delete $paref->{reorgday};
delete $paref->{histname};
}
else {
for my $hr (sort keys %{$data{$type}{$name}{pvhist}{$dday}}) {
@ -5452,6 +5494,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
################################################################
@ -6587,6 +6695,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
@ -7119,8 +7228,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');
@ -8229,6 +8337,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
################################################################
@ -8882,11 +9067,18 @@ sub _createSummaries {
my $batin = CurrentVal ($hash, "powerbatin", 0); # aktuelle Batterieladung
my $batout = CurrentVal ($hash, "powerbatout", 0); # aktuelle Batterieentladung
my $consumption = int ($pvgen - $gfeedin + $gcon - $batin + $batout);
my $othprod = 0; # Summe Otherproducer
for my $prn (1..$maxproducer) { # V1.32.0 : Erzeugung sonstiger Producer (01..03) hinzufügen
$prn = sprintf "%02d", $prn;
$othprod += CurrentVal ($hash, 'generationp'.$prn, 0);
}
my $consumption = int ($pvgen + $othprod - $gfeedin + $gcon - $batin + $batout);
my $selfconsumption = int ($pvgen - $gfeedin - $batin);
$selfconsumption = $selfconsumption < 0 ? 0 : $selfconsumption;
my $surplus = int ($pvgen - $consumption); # aktueller Überschuß
my $surplus = int ($pvgen + $othprod - $consumption); # aktueller Überschuß
$surplus = 0 if($surplus < 0); # wegen Vergleich nompower vs. surplus
my $selfconsumptionrate = 0;
@ -8987,6 +9179,19 @@ sub _manageConsumerData {
my $consumerco = $etot - $ehist;
$consumerco += HistoryVal ($hash, $day, sprintf("%02d",$nhour), "csme${c}", 0);
if ($consumerco < 0) { # V1.32.0
$consumerco = 0;
my $vl = 3;
my $pre = '- WARNING -';
if ($paref->{debug} =~ /consumption/xs) {
$vl = 1;
$pre = 'DEBUG> - WARNING -';
}
Log3 ($name, $vl, "$name $pre The calculated Energy consumption of >$consumer< is negative. This appears to be an error and the energy consumption of the consumer for the current hour is set to '0'.");
}
$paref->{val} = $consumerco; # Verbrauch des Consumers aktuelle Stunde
$paref->{histname} = "csme${c}";
@ -10502,7 +10707,7 @@ sub _estConsumptionForecast {
my $totcon = 0;
my $dnum = 0;
debugLog ($paref, 'consumption', "################### Consumption forecast for the next day ###################");
debugLog ($paref, 'consumption|consumption_long', "################### Consumption forecast for the next day ###################");
for my $n (sort{$a<=>$b} keys %{$data{$type}{$name}{pvhist}}) {
next if ($n eq $day); # aktuellen (unvollständigen) Tag nicht berücksichtigen
@ -10515,7 +10720,7 @@ sub _estConsumptionForecast {
my $dcon = HistoryVal ($hash, $n, 99, 'con', 0);
if(!$dcon) {
debugLog ($paref, 'consumption', "Day >$n< has no registered consumption, ignore it.");
debugLog ($paref, 'consumption|consumption_long', "Day >$n< has no registered consumption, ignore it.");
next;
}
@ -10525,11 +10730,11 @@ sub _estConsumptionForecast {
if ($exconfc) {
$dcon -= $csme;
debugLog ($paref, 'consumption', "Consumer '$c' values excluded from forecast calc by 'exconfc' - day: $n, csme: $csme");
debugLog ($paref, 'consumption|consumption_long', "Consumer '$c' values excluded from forecast calc by 'exconfc' - day: $n, csme: $csme");
}
}
debugLog ($paref, 'consumption', "History Consumption day >$n< considering possible exclusions: $dcon");
debugLog ($paref, 'consumption|consumption_long', "History Consumption day >$n< considering possible exclusions: $dcon");
$totcon += $dcon;
$dnum++;
@ -10539,7 +10744,7 @@ sub _estConsumptionForecast {
my $tomavg = int ($totcon / $dnum);
$data{$type}{$name}{current}{tomorrowconsumption} = $tomavg; # prognostizierter Durchschnittsverbrauch aller (gleicher) Wochentage
debugLog ($paref, 'consumption', "estimated Consumption for tomorrow: $tomavg, days for avg: $dnum");
debugLog ($paref, 'consumption|consumption_long', "estimated Consumption for tomorrow: $tomavg, days for avg: $dnum");
}
else {
my $lang = $paref->{lang};
@ -10548,27 +10753,21 @@ sub _estConsumptionForecast {
## Verbrauchsvorhersage für die nächsten Stunden
##################################################
my $conh = { "01" => 0, "02" => 0, "03" => 0, "04" => 0,
"05" => 0, "06" => 0, "07" => 0, "08" => 0,
"09" => 0, "10" => 0, "11" => 0, "12" => 0,
"13" => 0, "14" => 0, "15" => 0, "16" => 0,
"17" => 0, "18" => 0, "19" => 0, "20" => 0,
"21" => 0, "22" => 0, "23" => 0, "24" => 0,
};
my $conhex = { "01" => 0, "02" => 0, "03" => 0, "04" => 0,
"05" => 0, "06" => 0, "07" => 0, "08" => 0,
"09" => 0, "10" => 0, "11" => 0, "12" => 0,
"13" => 0, "14" => 0, "15" => 0, "16" => 0,
"17" => 0, "18" => 0, "19" => 0, "20" => 0,
"21" => 0, "22" => 0, "23" => 0, "24" => 0,
};
debugLog ($paref, 'consumption', "################### Consumption forecast for the next hours ###################");
debugLog ($paref, 'consumption|consumption_long', "################### Consumption forecast for the next hours ###################");
for my $k (sort keys %{$data{$type}{$name}{nexthours}}) {
my $nhtime = NexthoursVal ($hash, $k, "starttime", undef); # Startzeit
next if(!$nhtime);
$conhfc = { "01" => 0, "02" => 0, "03" => 0, "04" => 0, "05" => 0, "06" => 0, "07" => 0, "08" => 0,
"09" => 0, "10" => 0, "11" => 0, "12" => 0, "13" => 0, "14" => 0, "15" => 0, "16" => 0,
"17" => 0, "18" => 0, "19" => 0, "20" => 0, "21" => 0, "22" => 0, "23" => 0, "24" => 0,
};
$conhfcex = { "01" => 0, "02" => 0, "03" => 0, "04" => 0, "05" => 0, "06" => 0, "07" => 0, "08" => 0,
"09" => 0, "10" => 0, "11" => 0, "12" => 0, "13" => 0, "14" => 0, "15" => 0, "16" => 0,
"17" => 0, "18" => 0, "19" => 0, "20" => 0, "21" => 0, "22" => 0, "23" => 0, "24" => 0,
};
$dnum = 0;
my $consumerco = 0;
@ -10580,34 +10779,48 @@ 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);
my $hcon = HistoryVal ($hash, $m, $nhhr, 'con', 0); # historische Verbrauchswerte
next if(!$hcon);
debugLog ($paref, 'consumption_long', " historical Consumption added for $nhday -> date: $m, hod: $nhhr -> $hcon Wh");
if ($hcon < 0) { # V1.32.0
my $vl = 3;
my $pre = '- WARNING -';
for my $c (sort{$a<=>$b} keys %{$acref}) { # historischer Verbrauch aller registrierten Verbraucher aufaddieren
$exconfc = ConsumerVal ($hash, $c, 'exconfc', 0); # 1 -> Consumer Verbrauch von Erstelleung der Verbrauchsprognose ausschließen
$csme = HistoryVal ($hash, $m, $nhhr, "csme${c}", 0);
if ($exconfc) {
debugLog ($paref, 'consumption', "Consumer '$c' values excluded from forecast calc by 'exconfc' - day: $m, hour: $nhhr, csme: $csme");
$hcon -= $csme;
next;
if ($paref->{debug} =~ /consumption/xs) {
$vl = 1;
$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. 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
$exconfc = ConsumerVal ($hash, $c, 'exconfc', 0); # 1 -> Consumer Verbrauch von Erstelleung der Verbrauchsprognose ausschließen
$csme = HistoryVal ($hash, $m, $nhhr, "csme${c}", 0);
$consumerco += $csme;
if ($exconfc) {
debugLog ($paref, 'consumption_long', "Consumer '$c' values excluded from forecast calc by 'exconfc' - day: $m, hour: $nhhr, csme: $csme");
$consumerco -= $csme; # V1.32.0
$hcon -= $csme; # V1.32.0, excludierte Verbraucherconsumption von Forecast ausschließen
}
}
$conhex->{$nhhr} += $hcon - $consumerco if($hcon >= $consumerco); # prognostizierter Verbrauch Ex registrierter Verbraucher
$conh->{$nhhr} += $hcon;
$conhfcex->{$nhhr} += ($hcon - $consumerco) if($hcon >= $consumerco); # prognostizierter Verbrauch Ex registrierter Verbraucher
$conhfc->{$nhhr} += $hcon;
$dnum++;
}
if ($dnum) {
$data{$type}{$name}{nexthours}{$k}{confcEx} = int ($conhex->{$nhhr} / $dnum);
my $conavg = int ($conh->{$nhhr} / $dnum);
my $conavgex = int ($conhfcex->{$nhhr} / $dnum);
$data{$type}{$name}{nexthours}{$k}{confcEx} = $conavgex;
my $conavg = int ($conhfc->{$nhhr} / $dnum);
$data{$type}{$name}{nexthours}{$k}{confc} = $conavg; # Durchschnittsverbrauch aller gleicher Wochentage pro Stunde
if (NexthoursVal ($hash, $k, "today", 0)) { # nur Werte des aktuellen Tag speichern
@ -10615,7 +10828,7 @@ sub _estConsumptionForecast {
writeToHistory ( { paref => $paref, key => 'confc', val => $conavg, hour => $nhhr } );
}
debugLog ($paref, 'consumption', "estimated Consumption for $nhday -> starttime: $nhtime, confc: $conavg, days for avg: $dnum, hist. consumption registered consumers: ".sprintf "%.2f", $consumerco);
debugLog ($paref, 'consumption|consumption_long', "estimated Consumption for $nhday -> starttime: $nhtime, confc: $conavg, days for avg: $dnum, hist. consumption registered consumers: ".sprintf "%.2f", $consumerco);
}
}
@ -11122,17 +11335,35 @@ 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;
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 -';
if ($paref->{debug} =~ /consumption/xs) {
$vl = 1;
$pre = 'DEBUG> - WARNING -';
}
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 } );
@ -11669,15 +11900,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);
}
##########################################################################################
@ -13574,6 +13796,9 @@ sub _flowGraphic {
my $cgfi = ReadingsNum ($name, 'Current_GridFeedIn', 0);
my $csc = ReadingsNum ($name, 'Current_SelfConsumption', 0);
my $cc = CurrentVal ($hash, 'consumption', 0);
my $gp01 = CurrentVal ($hash, 'generationp01', 0);
my $gp02 = CurrentVal ($hash, 'generationp02', 0);
my $gp03 = CurrentVal ($hash, 'generationp03', 0);
my $cc_dummy = $cc;
my $batin = ReadingsNum ($name, 'Current_PowerBatIn', undef);
my $batout = ReadingsNum ($name, 'Current_PowerBatOut', undef);
@ -13602,7 +13827,7 @@ sub _flowGraphic {
my $cgc_direction = 'M490,305 L670,510'; # Batterientladung ins Netz
if($batout) {
if ($batout) {
my $cgfo = $cgfi - $cpv;
if($cgfo > 1) {
@ -13694,7 +13919,7 @@ END0
my @consumers;
if ($flowgcons) {
my $type = $paref->{type};
my $type = $paref->{type};
for my $c (sort{$a<=>$b} keys %{$data{$type}{$name}{consumers}}) { # definierte Verbraucher ermitteln
next if(isConsumerNoshow ($hash, $c) =~ /^[13]$/xs); # ausgeblendete Consumer nicht berücksichtigen
@ -13799,7 +14024,6 @@ END3
my $p = $currentPower;
$p = (($currentPower / $power) * 100) if ($power > 0);
my $consumer_style = 'flowg inactive_out';
$consumer_style = 'flowg active_out' if($p > $defpopercent);
my $chain_color = ""; # Farbe der Laufkette des Consumers
@ -14955,7 +15179,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;
@ -14984,28 +15214,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) {
@ -15150,31 +15396,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} );
@ -15188,6 +15437,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";
@ -16379,7 +16629,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);
@ -16398,6 +16648,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 = ();
@ -18978,7 +19235,7 @@ to ensure that the system configuration is correct.
<colgroup> <col width="10%"> <col width="90%"> </colgroup>
<tr><td> <b>aihit</b> </td><td>delivery status of the AI for the PV forecast (0-no delivery, 1-delivery) </td></tr>
<tr><td> <b>confc</b> </td><td>expected energy consumption including the shares of registered consumers </td></tr>
<tr><td> <b>confcEx</b> </td><td>expected energy consumption without the shares of registered consumers </td></tr>
<tr><td> <b>confcEx</b> </td><td>expected energy consumption without consumer shares with set key exconfc=1 </td></tr>
<tr><td> <b>crange</b> </td><td>calculated cloud area </td></tr>
<tr><td> <b>correff</b> </td><td>correction factor/quality used </td></tr>
<tr><td> </td><td>factor/- -> no quality defined </td></tr>
@ -19033,12 +19290,14 @@ to ensure that the system configuration is correct.
<tr><td> <b>csmeXX</b> </td><td>Energy consumption of ConsumerXX in the hour of the day (hour 99 = daily energy consumption) </td></tr>
<tr><td> <b>cyclescsmXX</b> </td><td>Number of active cycles of ConsumerXX of the day </td></tr>
<tr><td> <b>DoN</b> </td><td>Sunrise and sunset status (0 - night, 1 - day) </td></tr>
<tr><td> <b>etotal</b> </td><td>total energy yield (Wh) at the beginning of the hour </td></tr>
<tr><td> <b>etotal</b> </td><td>PV meter reading Total energy yield (Wh) at the beginning of the hour </td></tr>
<tr><td> <b>etotalpXX</b> </td><td>Meter reading Total energy yield (Wh) of producer XX at the beginning of the hour </td></tr>
<tr><td> <b>gcons</b> </td><td>real power consumption (Wh) from the electricity grid </td></tr>
<tr><td> <b>gfeedin</b> </td><td>real feed-in (Wh) into the electricity grid </td></tr>
<tr><td> <b>feedprice</b> </td><td>Remuneration for the feed-in of one kWh. The currency of the price is defined in the setupMeterDev. </td></tr>
<tr><td> <b>hourscsmeXX</b> </td><td>total active hours of the day from ConsumerXX </td></tr>
<tr><td> <b>minutescsmXX</b> </td><td>total active minutes in the hour of ConsumerXX </td></tr>
<tr><td> <b>pprlXX</b> </td><td>Energy generation of producer XX (see attribute setupOtherProducerXX) in the hour (Wh) </td></tr>
<tr><td> <b>pvfc</b> </td><td>the predicted PV yield (Wh) </td></tr>
<tr><td> <b>pvrl</b> </td><td>real PV generation (Wh) </td></tr>
<tr><td> <b>pvrlvd</b> </td><td>1-'pvrl' is valid and is taken into account in the learning process, 0-'pvrl' is assessed as abnormal </td></tr>
@ -19095,6 +19354,7 @@ to ensure that the system configuration is correct.
<tr><td> <b>pvfcsum</b> </td><td>summary PV forecast per cloud area over the entire term </td></tr>
<tr><td> <b>pvrl</b> </td><td>real PV generation of the last 24h (Attention: pvforecast and pvreal do not refer to the same period!) </td></tr>
<tr><td> <b>pvrlsum</b> </td><td>summary real PV generation per cloud area over the entire term </td></tr>
<tr><td> <b>pprlXX</b> </td><td>Energy generation of producer XX (see attribute setupOtherProducerXX) in the last 24 hours (Wh) </td></tr>
<tr><td> <b>quality</b> </td><td>Quality of the autocorrection factors (0..1), where 'simple' is the quality of the simple correction factor. </td></tr>
<tr><td> <b>runTimeTrainAI</b> </td><td>Duration of the last AI training </td></tr>
<tr><td> <b>aitrainLastFinishTs</b> </td><td>Timestamp of the last successful AI training </td></tr>
@ -19587,6 +19847,7 @@ to ensure that the system configuration is correct.
<tr><td> <b>consumerPlanning</b> </td><td>Consumer scheduling processes </td></tr>
<tr><td> <b>consumerSwitchingXX</b> </td><td>Operations of the internal consumer switching module of consumer XX </td></tr>
<tr><td> <b>consumption</b> </td><td>Consumption calculation, consumption forecasting and utilization </td></tr>
<tr><td> <b>consumption_long</b> </td><td>extended output of the consumption forecast Determination </td></tr>
<tr><td> <b>dwdComm</b> </td><td>Communication with the website or server of the German Weather Service (DWD) </td></tr>
<tr><td> <b>epiecesCalc</b> </td><td>Calculation of specific energy consumption per operating hour and consumer </td></tr>
<tr><td> <b>graphic</b> </td><td>Module graphic information </td></tr>
@ -20337,7 +20598,7 @@ to ensure that the system configuration is correct.
<ul>
<table>
<colgroup> <col width="15%"> <col width="85%"> </colgroup>
<tr><td> <b>pv</b> </td><td>Reading which provides the current PV generation </td></tr>
<tr><td> <b>pv</b> </td><td>Reading which provides the current PV generation as a positive value </td></tr>
<tr><td> <b>etotal</b> </td><td>Reading which provides the total PV energy generated (a steadily increasing counter). </td></tr>
<tr><td> </td><td>If the reading violates the specification of a continuously rising counter, </td></tr>
<tr><td> </td><td>SolarForecast handles this error and reports the situation by means of a log message. </td></tr>
@ -20426,6 +20687,36 @@ to ensure that the system configuration is correct.
</ul>
<br>
<b>Note:</b> Deleting the attribute also removes the internally corresponding data.
</li>
<br>
<a id="SolarForecast-attr-setupOtherProducer" data-pattern="setupOtherProducer.*"></a>
<li><b>setupOtherProducerXX &lt;Device Name&gt; pcurr=&lt;Readingname&gt;:&lt;Unit&gt; etotal=&lt;Readingname&gt;:&lt;Unit&gt; </b> <br><br>
Defines any device and its readings for the delivery of other generation values
(e.g. CHP, wind generation, emergency generator).
It can also be a dummy device with corresponding readings.
<br><br>
<ul>
<table>
<colgroup> <col width="15%"> <col width="85%"> </colgroup>
<tr><td> <b>pcurr</b> </td><td>Reading which returns the current generation as a positive value or a self-consumption (special case) as a negative value </td></tr>
<tr><td> <b>etotal</b> </td><td>Reading which supplies the total energy generated (a continuously ascending counter) </td></tr>
<tr><td> </td><td>If the reading violates the specification of a continuously rising counter, </td></tr>
<tr><td> </td><td>SolarForecast handles this error and reports the situation that has occurred with a log entry. </td></tr>
<tr><td> <b>Einheit</b> </td><td>the respective unit (W,kW,Wh,kWh) </td></tr>
</table>
</ul>
<br>
<ul>
<b>Example: </b> <br>
attr &lt;name&gt; setupOtherProducer01 windwheel pcurr=total_pac:kW etotal=etotal:kWh
</ul>
<br>
<b>Note:</b> Deleting the attribute also removes the internally corresponding data.
</li>
<br>
@ -21267,28 +21558,28 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
<ul>
<table>
<colgroup> <col width="10%"> <col width="90%"> </colgroup>
<tr><td> <b>aihit</b> </td><td>Lieferstatus der KI für die PV Vorhersage (0-keine Lieferung, 1-Lieferung) </td></tr>
<tr><td> <b>confc</b> </td><td>erwarteter Energieverbrauch inklusive der Anteile registrierter Verbraucher </td></tr>
<tr><td> <b>confcEx</b> </td><td>erwarteter Energieverbrauch ohne der Anteile registrierter Verbraucher </td></tr>
<tr><td> <b>crange</b> </td><td>berechneter Bewölkungsbereich </td></tr>
<tr><td> <b>correff</b> </td><td>verwendeter Korrekturfaktor/Qualität </td></tr>
<tr><td> </td><td>Faktor/- -> keine Qualität definiert </td></tr>
<tr><td> </td><td>Faktor/0..1 - Qualität der PV Prognose (1 = beste Qualität) </td></tr>
<tr><td> <b>DoN</b> </td><td>Sonnenauf- und untergangsstatus (0 - Nacht, 1 - Tag) </td></tr>
<tr><td> <b>hourofday</b> </td><td>laufende Stunde des Tages </td></tr>
<tr><td> <b>pvapifc</b> </td><td>erwartete PV Erzeugung (Wh) der verwendeten API inkl. einer eventuellen Korrektur </td></tr>
<tr><td> <b>pvaifc</b> </td><td>erwartete PV Erzeugung der KI (Wh) </td></tr>
<tr><td> <b>pvfc</b> </td><td>verwendete PV Erzeugungsprognose (Wh) </td></tr>
<tr><td> <b>rad1h</b> </td><td>vorhergesagte Globalstrahlung </td></tr>
<tr><td> <b>starttime</b> </td><td>Startzeit des Datensatzes </td></tr>
<tr><td> <b>sunaz</b> </td><td>Azimuth der Sonne (in Dezimalgrad) </td></tr>
<tr><td> <b>sunalt</b> </td><td>Höhe der Sonne (in Dezimalgrad) </td></tr>
<tr><td> <b>temp</b> </td><td>vorhergesagte Außentemperatur </td></tr>
<tr><td> <b>today</b> </td><td>hat Wert '1' wenn Startdatum am aktuellen Tag </td></tr>
<tr><td> <b>rr1c</b> </td><td>Gesamtniederschlag in der letzten Stunde kg/m2 </td></tr>
<tr><td> <b>rrange</b> </td><td>Bereich des Gesamtniederschlags </td></tr>
<tr><td> <b>wid</b> </td><td>ID des vorhergesagten Wetters </td></tr>
<tr><td> <b>wcc</b> </td><td>vorhergesagter Grad der Bewölkung </td></tr>
<tr><td> <b>aihit</b> </td><td>Lieferstatus der KI für die PV Vorhersage (0-keine Lieferung, 1-Lieferung) </td></tr>
<tr><td> <b>confc</b> </td><td>erwarteter Energieverbrauch inklusive der Anteile registrierter Verbraucher </td></tr>
<tr><td> <b>confcEx</b> </td><td>erwarteter Energieverbrauch ohne Anteile Verbraucher mit gesetztem Schlüssel exconfc=1 </td></tr>
<tr><td> <b>crange</b> </td><td>berechneter Bewölkungsbereich </td></tr>
<tr><td> <b>correff</b> </td><td>verwendeter Korrekturfaktor/Qualität </td></tr>
<tr><td> </td><td>Faktor/- -> keine Qualität definiert </td></tr>
<tr><td> </td><td>Faktor/0..1 - Qualität der PV Prognose (1 = beste Qualität) </td></tr>
<tr><td> <b>DoN</b> </td><td>Sonnenauf- und untergangsstatus (0 - Nacht, 1 - Tag) </td></tr>
<tr><td> <b>hourofday</b> </td><td>laufende Stunde des Tages </td></tr>
<tr><td> <b>pvapifc</b> </td><td>erwartete PV Erzeugung (Wh) der verwendeten API inkl. einer eventuellen Korrektur </td></tr>
<tr><td> <b>pvaifc</b> </td><td>erwartete PV Erzeugung der KI (Wh) </td></tr>
<tr><td> <b>pvfc</b> </td><td>verwendete PV Erzeugungsprognose (Wh) </td></tr>
<tr><td> <b>rad1h</b> </td><td>vorhergesagte Globalstrahlung </td></tr>
<tr><td> <b>starttime</b> </td><td>Startzeit des Datensatzes </td></tr>
<tr><td> <b>sunaz</b> </td><td>Azimuth der Sonne (in Dezimalgrad) </td></tr>
<tr><td> <b>sunalt</b> </td><td>Höhe der Sonne (in Dezimalgrad) </td></tr>
<tr><td> <b>temp</b> </td><td>vorhergesagte Außentemperatur </td></tr>
<tr><td> <b>today</b> </td><td>hat Wert '1' wenn Startdatum am aktuellen Tag </td></tr>
<tr><td> <b>rr1c</b> </td><td>Gesamtniederschlag in der letzten Stunde kg/m2 </td></tr>
<tr><td> <b>rrange</b> </td><td>Bereich des Gesamtniederschlags </td></tr>
<tr><td> <b>wid</b> </td><td>ID des vorhergesagten Wetters </td></tr>
<tr><td> <b>wcc</b> </td><td>vorhergesagter Grad der Bewölkung </td></tr>
</table>
</ul>
</li>
@ -21324,13 +21615,15 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
<tr><td> <b>conprice</b> </td><td>Preis für den Bezug einer kWh. Die Einheit des Preises ist im setupMeterDev definiert. </td></tr>
<tr><td> <b>cyclescsmXX</b> </td><td>Anzahl aktive Zyklen von ConsumerXX des Tages </td></tr>
<tr><td> <b>DoN</b> </td><td>Sonnenauf- und untergangsstatus (0 - Nacht, 1 - Tag) </td></tr>
<tr><td> <b>etotal</b> </td><td>totaler Energieertrag (Wh) zu Beginn der Stunde </td></tr>
<tr><td> <b>etotal</b> </td><td>PV Zählerstand "Energieertrag total" (Wh) zu Beginn der Stunde </td></tr>
<tr><td> <b>etotalpXX</b> </td><td>Zählerstand "Energieertrag total" (Wh) des Produzenten XX zu Beginn der Stunde </td></tr>
<tr><td> <b>gcons</b> </td><td>realer Leistungsbezug (Wh) aus dem Stromnetz </td></tr>
<tr><td> <b>gfeedin</b> </td><td>reale Einspeisung (Wh) in das Stromnetz </td></tr>
<tr><td> <b>feedprice</b> </td><td>Vergütung für die Einpeisung einer kWh. Die Währung des Preises ist im setupMeterDev definiert. </td></tr>
<tr><td> <b>avgcycmntscsmXX</b> </td><td>durchschnittliche Dauer eines Einschaltzyklus des Tages von ConsumerXX in Minuten </td></tr>
<tr><td> <b>hourscsmeXX</b> </td><td>Summe Aktivstunden des Tages von ConsumerXX </td></tr>
<tr><td> <b>minutescsmXX</b> </td><td>Summe Aktivminuten in der Stunde von ConsumerXX </td></tr>
<tr><td> <b>pprlXX</b> </td><td>Energieerzeugung des Produzenten XX (siehe Attribut setupOtherProducerXX) in der Stunde (Wh) </td></tr>
<tr><td> <b>pvfc</b> </td><td>der prognostizierte PV Ertrag (Wh) </td></tr>
<tr><td> <b>pvrl</b> </td><td>reale PV Erzeugung (Wh) </td></tr>
<tr><td> <b>pvrlvd</b> </td><td>1-'pvrl' ist gültig und wird im Lernprozess berücksichtigt, 0-'pvrl' ist als abnormal bewertet </td></tr>
@ -21388,6 +21681,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
<tr><td> <b>pvfcsum</b> </td><td>Summe PV Prognose pro Bewölkungsbereich über die gesamte Laufzeit </td></tr>
<tr><td> <b>pvrl</b> </td><td>reale PV Erzeugung der letzten 24h (Achtung: pvforecast und pvreal beziehen sich nicht auf den gleichen Zeitraum!) </td></tr>
<tr><td> <b>pvrlsum</b> </td><td>Summe reale PV Erzeugung pro Bewölkungsbereich über die gesamte Laufzeit </td></tr>
<tr><td> <b>pprlXX</b> </td><td>Energieerzeugung des Produzenten XX (siehe Attribut setupOtherProducerXX) der letzten 24 Stunden (Wh) </td></tr>
<tr><td> <b>quality</b> </td><td>Qualität der Autokorrekturfaktoren (0..1), wobei 'simple' die Qualität des einfach berechneten Korrekturfaktors ist. </td></tr>
<tr><td> <b>runTimeTrainAI</b> </td><td>Laufzeit des letzten KI Trainings </td></tr>
<tr><td> <b>aitrainLastFinishTs</b> </td><td>Timestamp des letzten erfolgreichen KI Trainings </td></tr>
@ -21880,6 +22174,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
<tr><td> <b>consumerPlanning</b> </td><td>Consumer Einplanungsprozesse </td></tr>
<tr><td> <b>consumerSwitchingXX</b> </td><td>Operationen des internen Consumer Schaltmodul für Verbraucher XX </td></tr>
<tr><td> <b>consumption</b> </td><td>Verbrauchskalkulation, Verbrauchsvorhersage und -nutzung </td></tr>
<tr><td> <b>consumption_long</b> </td><td>erweiterte Ausgabe der Verbrauchsvorhersage Ermittlung </td></tr>
<tr><td> <b>dwdComm</b> </td><td>Kommunikation mit Webseite oder Server des Deutschen Wetterdienst (DWD) </td></tr>
<tr><td> <b>epiecesCalc</b> </td><td>Berechnung des spezifischen Energieverbrauchs je Betriebsstunde und Verbraucher </td></tr>
<tr><td> <b>graphic</b> </td><td>Informationen der Modulgrafik </td></tr>
@ -22628,10 +22923,10 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
<ul>
<table>
<colgroup> <col width="15%"> <col width="85%"> </colgroup>
<tr><td> <b>pv</b> </td><td>Reading welches die aktuelle PV-Erzeugung liefert </td></tr>
<tr><td> <b>pv</b> </td><td>Reading welches die aktuelle PV-Erzeugung als positiven Wert liefert </td></tr>
<tr><td> <b>etotal</b> </td><td>Reading welches die gesamte erzeugte PV-Energie liefert (ein stetig aufsteigender Zähler) </td></tr>
<tr><td> </td><td>Sollte des Reading die Vorgabe eines stetig aufsteigenden Zählers verletzen, behandelt </td></tr>
<tr><td> </td><td>SolarForecast diesen Fehler und meldet die aufgetretene Situation durch eine Logmeldung. </td></tr>
<tr><td> </td><td>SolarForecast diesen Fehler und meldet die aufgetretene Situation durch einen Logeintrag. </td></tr>
<tr><td> <b>Einheit</b> </td><td>die jeweilige Einheit (W,kW,Wh,kWh) </td></tr>
<tr><td> <b>capacity</b> </td><td>Bemessungsleistung des Wechselrichters gemäß Datenblatt, d.h. max. möglicher Output in Watt </td></tr>
<tr><td> </td><td>(Die Angabe ist optional, wird aber dringend empfohlen zu setzen) </td></tr>
@ -22720,6 +23015,36 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
<b>Hinweis:</b> Durch Löschen des Attributes werden ebenfalls die intern korrespondierenden Daten entfernt.
</li>
<br>
<a id="SolarForecast-attr-setupOtherProducer" data-pattern="setupOtherProducer.*"></a>
<li><b>setupOtherProducerXX &lt;Device Name&gt; pcurr=&lt;Readingname&gt;:&lt;Einheit&gt; etotal=&lt;Readingname&gt;:&lt;Einheit&gt; </b> <br><br>
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.
<br><br>
<ul>
<table>
<colgroup> <col width="15%"> <col width="85%"> </colgroup>
<tr><td> <b>pcurr</b> </td><td>Reading welches die aktuelle Erzeugung als positiven Wert oder einen Eigenverbrauch (Sonderfall) als negativen Wert liefert </td></tr>
<tr><td> <b>etotal</b> </td><td>Reading welches die gesamte erzeugte Energie liefert (ein stetig aufsteigender Zähler) </td></tr>
<tr><td> </td><td>Sollte des Reading die Vorgabe eines stetig aufsteigenden Zählers verletzen, behandelt </td></tr>
<tr><td> </td><td>SolarForecast diesen Fehler und meldet die aufgetretene Situation durch einen Logeintrag. </td></tr>
<tr><td> <b>Einheit</b> </td><td>die jeweilige Einheit (W,kW,Wh,kWh) </td></tr>
</table>
</ul>
<br>
<ul>
<b>Beispiel: </b> <br>
attr &lt;name&gt; setupOtherProducer01 windwheel pcurr=total_pac:kW etotal=etotal:kWh
</ul>
<br>
<b>Hinweis:</b> Durch Löschen des Attributes werden ebenfalls die intern korrespondierenden Daten entfernt.
</li>
<br>
<a id="SolarForecast-attr-setupRadiationAPI"></a>
<li><b>setupRadiationAPI </b> <br><br>

View File

@ -156,7 +156,8 @@ BEGIN {
# Versions History intern
my %vNotesIntern = (
"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 ",
"Forum: https://forum.fhem.de/index.php?msg=1319083 ".
"bugfix in _estConsumptionForecast, new ctrlDebug consumption_long ",
"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 ",
"1.29.4" => "03.08.2024 delete writeCacheToFile from _getRoofTopData, _specialActivities: avoid loop caused by \@widgetreadings ",
@ -485,6 +486,17 @@ my $cssdef = qq{.flowg.text { stroke: none; fill: gray; font-size: 60p
qq{.flowg.active_bat_in { stroke: darkorange; stroke-dashoffset: 20; stroke-dasharray: 10; opacity: 0.8; animation: dash 0.5s linear; animation-iteration-count: infinite; } \n}.
qq{.flowg.active_bat_out { stroke: green; stroke-dashoffset: 20; stroke-dasharray: 10; opacity: 0.8; animation: dash 0.5s linear; animation-iteration-count: infinite; } \n}
;
# initiale Hashes für Stunden Consumption Forecast inkl. und exkl. Verbraucher
my $conhfc = { "01" => 0, "02" => 0, "03" => 0, "04" => 0, "05" => 0, "06" => 0, "07" => 0, "08" => 0,
"09" => 0, "10" => 0, "11" => 0, "12" => 0, "13" => 0, "14" => 0, "15" => 0, "16" => 0,
"17" => 0, "18" => 0, "19" => 0, "20" => 0, "21" => 0, "22" => 0, "23" => 0, "24" => 0,
};
my $conhfcex = { "01" => 0, "02" => 0, "03" => 0, "04" => 0, "05" => 0, "06" => 0, "07" => 0, "08" => 0,
"09" => 0, "10" => 0, "11" => 0, "12" => 0, "13" => 0, "14" => 0, "15" => 0, "16" => 0,
"17" => 0, "18" => 0, "19" => 0, "20" => 0, "21" => 0, "22" => 0, "23" => 0, "24" => 0,
};
# mögliche Debug-Module
my @dd = qw( aiProcess
@ -495,6 +507,7 @@ my @dd = qw( aiProcess
collectData
consumerPlanning
consumption
consumption_long
dwdComm
epiecesCalc
graphic
@ -10694,7 +10707,7 @@ sub _estConsumptionForecast {
my $totcon = 0;
my $dnum = 0;
debugLog ($paref, 'consumption', "################### Consumption forecast for the next day ###################");
debugLog ($paref, 'consumption|consumption_long', "################### Consumption forecast for the next day ###################");
for my $n (sort{$a<=>$b} keys %{$data{$type}{$name}{pvhist}}) {
next if ($n eq $day); # aktuellen (unvollständigen) Tag nicht berücksichtigen
@ -10707,7 +10720,7 @@ sub _estConsumptionForecast {
my $dcon = HistoryVal ($hash, $n, 99, 'con', 0);
if(!$dcon) {
debugLog ($paref, 'consumption', "Day >$n< has no registered consumption, ignore it.");
debugLog ($paref, 'consumption|consumption_long', "Day >$n< has no registered consumption, ignore it.");
next;
}
@ -10717,11 +10730,11 @@ sub _estConsumptionForecast {
if ($exconfc) {
$dcon -= $csme;
debugLog ($paref, 'consumption', "Consumer '$c' values excluded from forecast calc by 'exconfc' - day: $n, csme: $csme");
debugLog ($paref, 'consumption|consumption_long', "Consumer '$c' values excluded from forecast calc by 'exconfc' - day: $n, csme: $csme");
}
}
debugLog ($paref, 'consumption', "History Consumption day >$n< considering possible exclusions: $dcon");
debugLog ($paref, 'consumption|consumption_long', "History Consumption day >$n< considering possible exclusions: $dcon");
$totcon += $dcon;
$dnum++;
@ -10731,7 +10744,7 @@ sub _estConsumptionForecast {
my $tomavg = int ($totcon / $dnum);
$data{$type}{$name}{current}{tomorrowconsumption} = $tomavg; # prognostizierter Durchschnittsverbrauch aller (gleicher) Wochentage
debugLog ($paref, 'consumption', "estimated Consumption for tomorrow: $tomavg, days for avg: $dnum");
debugLog ($paref, 'consumption|consumption_long', "estimated Consumption for tomorrow: $tomavg, days for avg: $dnum");
}
else {
my $lang = $paref->{lang};
@ -10740,27 +10753,21 @@ sub _estConsumptionForecast {
## Verbrauchsvorhersage für die nächsten Stunden
##################################################
my $conh = { "01" => 0, "02" => 0, "03" => 0, "04" => 0,
"05" => 0, "06" => 0, "07" => 0, "08" => 0,
"09" => 0, "10" => 0, "11" => 0, "12" => 0,
"13" => 0, "14" => 0, "15" => 0, "16" => 0,
"17" => 0, "18" => 0, "19" => 0, "20" => 0,
"21" => 0, "22" => 0, "23" => 0, "24" => 0,
};
my $conhex = { "01" => 0, "02" => 0, "03" => 0, "04" => 0,
"05" => 0, "06" => 0, "07" => 0, "08" => 0,
"09" => 0, "10" => 0, "11" => 0, "12" => 0,
"13" => 0, "14" => 0, "15" => 0, "16" => 0,
"17" => 0, "18" => 0, "19" => 0, "20" => 0,
"21" => 0, "22" => 0, "23" => 0, "24" => 0,
};
debugLog ($paref, 'consumption', "################### Consumption forecast for the next hours ###################");
debugLog ($paref, 'consumption|consumption_long', "################### Consumption forecast for the next hours ###################");
for my $k (sort keys %{$data{$type}{$name}{nexthours}}) {
my $nhtime = NexthoursVal ($hash, $k, "starttime", undef); # Startzeit
next if(!$nhtime);
$conhfc = { "01" => 0, "02" => 0, "03" => 0, "04" => 0, "05" => 0, "06" => 0, "07" => 0, "08" => 0,
"09" => 0, "10" => 0, "11" => 0, "12" => 0, "13" => 0, "14" => 0, "15" => 0, "16" => 0,
"17" => 0, "18" => 0, "19" => 0, "20" => 0, "21" => 0, "22" => 0, "23" => 0, "24" => 0,
};
$conhfcex = { "01" => 0, "02" => 0, "03" => 0, "04" => 0, "05" => 0, "06" => 0, "07" => 0, "08" => 0,
"09" => 0, "10" => 0, "11" => 0, "12" => 0, "13" => 0, "14" => 0, "15" => 0, "16" => 0,
"17" => 0, "18" => 0, "19" => 0, "20" => 0, "21" => 0, "22" => 0, "23" => 0, "24" => 0,
};
$dnum = 0;
my $consumerco = 0;
@ -10779,8 +10786,9 @@ sub _estConsumptionForecast {
my $hcon = HistoryVal ($hash, $m, $nhhr, 'con', 0); # historische Verbrauchswerte
next if(!$hcon);
debugLog ($paref, 'consumption_long', " historical Consumption added for $nhday -> date: $m, hod: $nhhr -> $hcon Wh");
if ($hcon < 0) { # V1.32.0
#$hcon = 0;
my $vl = 3;
my $pre = '- WARNING -';
@ -10798,19 +10806,21 @@ sub _estConsumptionForecast {
$consumerco += $csme;
if ($exconfc) {
debugLog ($paref, 'consumption', "Consumer '$c' values excluded from forecast calc by 'exconfc' - day: $m, hour: $nhhr, csme: $csme");
debugLog ($paref, 'consumption_long', "Consumer '$c' values excluded from forecast calc by 'exconfc' - day: $m, hour: $nhhr, csme: $csme");
$consumerco -= $csme; # V1.32.0
$hcon -= $csme; # V1.32.0, excludierte Verbraucherconsumption von Forecast ausschließen
}
}
$conhex->{$nhhr} += ($hcon - $consumerco) if($hcon >= $consumerco); # prognostizierter Verbrauch Ex registrierter Verbraucher
$conh->{$nhhr} += $hcon;
$conhfcex->{$nhhr} += ($hcon - $consumerco) if($hcon >= $consumerco); # prognostizierter Verbrauch Ex registrierter Verbraucher
$conhfc->{$nhhr} += $hcon;
$dnum++;
}
if ($dnum) {
$data{$type}{$name}{nexthours}{$k}{confcEx} = int ($conhex->{$nhhr} / $dnum);
my $conavg = int ($conh->{$nhhr} / $dnum);
my $conavgex = int ($conhfcex->{$nhhr} / $dnum);
$data{$type}{$name}{nexthours}{$k}{confcEx} = $conavgex;
my $conavg = int ($conhfc->{$nhhr} / $dnum);
$data{$type}{$name}{nexthours}{$k}{confc} = $conavg; # Durchschnittsverbrauch aller gleicher Wochentage pro Stunde
if (NexthoursVal ($hash, $k, "today", 0)) { # nur Werte des aktuellen Tag speichern
@ -10818,7 +10828,7 @@ sub _estConsumptionForecast {
writeToHistory ( { paref => $paref, key => 'confc', val => $conavg, hour => $nhhr } );
}
debugLog ($paref, 'consumption', "estimated Consumption for $nhday -> starttime: $nhtime, confc: $conavg, days for avg: $dnum, hist. consumption registered consumers: ".sprintf "%.2f", $consumerco);
debugLog ($paref, 'consumption|consumption_long', "estimated Consumption for $nhday -> starttime: $nhtime, confc: $conavg, days for avg: $dnum, hist. consumption registered consumers: ".sprintf "%.2f", $consumerco);
}
}
@ -19837,6 +19847,7 @@ to ensure that the system configuration is correct.
<tr><td> <b>consumerPlanning</b> </td><td>Consumer scheduling processes </td></tr>
<tr><td> <b>consumerSwitchingXX</b> </td><td>Operations of the internal consumer switching module of consumer XX </td></tr>
<tr><td> <b>consumption</b> </td><td>Consumption calculation, consumption forecasting and utilization </td></tr>
<tr><td> <b>consumption_long</b> </td><td>extended output of the consumption forecast Determination </td></tr>
<tr><td> <b>dwdComm</b> </td><td>Communication with the website or server of the German Weather Service (DWD) </td></tr>
<tr><td> <b>epiecesCalc</b> </td><td>Calculation of specific energy consumption per operating hour and consumer </td></tr>
<tr><td> <b>graphic</b> </td><td>Module graphic information </td></tr>
@ -22163,6 +22174,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
<tr><td> <b>consumerPlanning</b> </td><td>Consumer Einplanungsprozesse </td></tr>
<tr><td> <b>consumerSwitchingXX</b> </td><td>Operationen des internen Consumer Schaltmodul für Verbraucher XX </td></tr>
<tr><td> <b>consumption</b> </td><td>Verbrauchskalkulation, Verbrauchsvorhersage und -nutzung </td></tr>
<tr><td> <b>consumption_long</b> </td><td>erweiterte Ausgabe der Verbrauchsvorhersage Ermittlung </td></tr>
<tr><td> <b>dwdComm</b> </td><td>Kommunikation mit Webseite oder Server des Deutschen Wetterdienst (DWD) </td></tr>
<tr><td> <b>epiecesCalc</b> </td><td>Berechnung des spezifischen Energieverbrauchs je Betriebsstunde und Verbraucher </td></tr>
<tr><td> <b>graphic</b> </td><td>Informationen der Modulgrafik </td></tr>