mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-04-22 20:24:36 +00:00
76_SolarForecast: version 1.47.0
git-svn-id: https://svn.fhem.de/fhem/trunk@29711 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
0cbeacdf82
commit
605a4b2ead
@ -38,7 +38,7 @@ use POSIX;
|
|||||||
use GPUtils qw(GP_Import GP_Export); # wird für den Import der FHEM Funktionen aus der fhem.pl benötigt
|
use GPUtils qw(GP_Import GP_Export); # wird für den Import der FHEM Funktionen aus der fhem.pl benötigt
|
||||||
use Time::HiRes qw(gettimeofday tv_interval);
|
use Time::HiRes qw(gettimeofday tv_interval);
|
||||||
use Math::Trig;
|
use Math::Trig;
|
||||||
use List::Util qw(max);
|
use List::Util qw(max shuffle);
|
||||||
|
|
||||||
eval "use FHEM::Meta;1" or my $modMetaAbsent = 1; ## no critic 'eval'
|
eval "use FHEM::Meta;1" or my $modMetaAbsent = 1; ## no critic 'eval'
|
||||||
eval "use FHEM::Utility::CTZ qw(:all);1;" or my $ctzAbsent = 1; ## no critic 'eval'
|
eval "use FHEM::Utility::CTZ qw(:all);1;" or my $ctzAbsent = 1; ## no critic 'eval'
|
||||||
@ -159,7 +159,10 @@ BEGIN {
|
|||||||
|
|
||||||
# Versions History intern
|
# Versions History intern
|
||||||
my %vNotesIntern = (
|
my %vNotesIntern = (
|
||||||
"1.46.4" => "23.02.2025 _flowGraphic: fix clculation of node2home (Forum: https://forum.fhem.de/index.php?msg=1334798) ".
|
"1.47.0" => "02.03.2025 aiInit: change AI init sequence, use Random Forest with Ensemble algorithm ".
|
||||||
|
"_beamGraphic.*: change decimal places für battery SoC ",
|
||||||
|
"1.46.5" => "28.02.2025 new ctrlSpecialReadings key todayConsumptionForecastDay ",
|
||||||
|
"1.46.4" => "25.02.2025 _flowGraphic: fix clculation of node2home (Forum: https://forum.fhem.de/index.php?msg=1334798) ".
|
||||||
"_transferBatteryValues: change Debug Logging ",
|
"_transferBatteryValues: change Debug Logging ",
|
||||||
"1.46.3" => "22.02.2025 new sub getConsumerMintime, consumer key 'mintime' can handle a device/reading combination that deliver minutes ".
|
"1.46.3" => "22.02.2025 new sub getConsumerMintime, consumer key 'mintime' can handle a device/reading combination that deliver minutes ".
|
||||||
"reports violation of the continuity specification for battery in/out energy ",
|
"reports violation of the continuity specification for battery in/out energy ",
|
||||||
@ -402,16 +405,17 @@ use constant {
|
|||||||
GMFILERANDOM => 10800, # Random AddOn zu GMFILEREPEAT
|
GMFILERANDOM => 10800, # Random AddOn zu GMFILEREPEAT
|
||||||
IDXLIMIT => 900000, # Notification System: Indexe > IDXLIMIT sind reserviert für Steuerungsaufgaben
|
IDXLIMIT => 900000, # Notification System: Indexe > IDXLIMIT sind reserviert für Steuerungsaufgaben
|
||||||
|
|
||||||
|
AINUMTREES => 10, # Anzahl der Entscheidungsbäume im Ensemble
|
||||||
AITRBLTO => 7200, # KI Training BlockingCall Timeout
|
AITRBLTO => 7200, # KI Training BlockingCall Timeout
|
||||||
AIBCTHHLD => 0.2, # Schwelle der KI Trainigszeit ab der BlockingCall benutzt wird
|
AIBCTHHLD => 0.2, # Schwelle der KI Trainigszeit ab der BlockingCall benutzt wird
|
||||||
AITRSTARTDEF => 2, # default Stunde f. Start AI-Training
|
AITRSTARTDEF => 2, # default Stunde f. Start AI-Training
|
||||||
AISTDUDEF => 1825, # default Haltezeit KI Raw Daten (Tage)
|
AISTDUDEF => 1825, # default Haltezeit KI Raw Daten (Tage)
|
||||||
AISPREADUPLIM => 120, # obere Abweichungsgrenze (%) AI 'Spread' von API Prognose
|
AISPREADUPLIM => 120, # obere Abweichungsgrenze (%) AI 'Spread' von API Prognose
|
||||||
AISPREADLOWLIM => 80, # untere Abweichungsgrenze (%) AI 'Spread' von API Prognose
|
AISPREADLOWLIM => 80, # untere Abweichungsgrenze (%) AI 'Spread' von API Prognose
|
||||||
AIACCUPLIM => 130, # obere Abweichungsgrenze (%) AI 'Accurate' von API Prognose
|
AIACCUPLIM => 150, # obere Abweichungsgrenze (%) AI 'Accurate' von API Prognose
|
||||||
AIACCLOWLIM => 70, # untere Abweichungsgrenze (%) AI 'Accurate' von API Prognose
|
AIACCLOWLIM => 50, # untere Abweichungsgrenze (%) AI 'Accurate' von API Prognose
|
||||||
AIACCTRNMIN => 5500, # Mindestanzahl KI Trainingssätze für Verwendung "KI Accurate"
|
AIACCTRNMIN => 2500, # Mindestanzahl KI Trainingssätze für Verwendung "KI Accurate"
|
||||||
AISPREADTRNMIN => 7000, # Mindestanzahl KI Trainingssätze für Verwendung "KI Spreaded"
|
AISPREADTRNMIN => 5000, # Mindestanzahl KI Trainingssätze für Verwendung "KI Spreaded"
|
||||||
|
|
||||||
SOLAPIREPDEF => 3600, # default Abrufintervall SolCast API (s)
|
SOLAPIREPDEF => 3600, # default Abrufintervall SolCast API (s)
|
||||||
FORAPIREPDEF => 900, # default Abrufintervall ForecastSolar API (s)
|
FORAPIREPDEF => 900, # default Abrufintervall ForecastSolar API (s)
|
||||||
@ -1241,45 +1245,46 @@ my %hef = (
|
|||||||
);
|
);
|
||||||
|
|
||||||
my %hcsr = ( # Funktiontemplate zur Erstellung optionaler Statistikreadings
|
my %hcsr = ( # Funktiontemplate zur Erstellung optionaler Statistikreadings
|
||||||
currentAPIinterval => { fnr => 1, fn => \&StatusAPIVal, par => '', unit => '', def => 0 }, # par = Parameter zur spezifischen Verwendung
|
currentAPIinterval => { fnr => 1, fn => \&StatusAPIVal, par => '', par1 => '', unit => '', def => 0 }, # par = Parameter zur spezifischen Verwendung
|
||||||
lastretrieval_time => { fnr => 1, fn => \&StatusAPIVal, par => '', unit => '', def => '-' },
|
lastretrieval_time => { fnr => 1, fn => \&StatusAPIVal, par => '', par1 => '', unit => '', def => '-' },
|
||||||
lastretrieval_timestamp => { fnr => 1, fn => \&StatusAPIVal, par => '', unit => '', def => '-' },
|
lastretrieval_timestamp => { fnr => 1, fn => \&StatusAPIVal, par => '', par1 => '', unit => '', def => '-' },
|
||||||
response_message => { fnr => 1, fn => \&StatusAPIVal, par => '', unit => '', def => '-' },
|
response_message => { fnr => 1, fn => \&StatusAPIVal, par => '', par1 => '', unit => '', def => '-' },
|
||||||
todayMaxAPIcalls => { fnr => 1, fn => \&StatusAPIVal, par => '', unit => '', def => 'apimaxreq' },
|
todayMaxAPIcalls => { fnr => 1, fn => \&StatusAPIVal, par => '', par1 => '', unit => '', def => 'apimaxreq' },
|
||||||
todayDoneAPIcalls => { fnr => 1, fn => \&StatusAPIVal, par => '', unit => '', def => 0 },
|
todayDoneAPIcalls => { fnr => 1, fn => \&StatusAPIVal, par => '', par1 => '', unit => '', def => 0 },
|
||||||
todayDoneAPIrequests => { fnr => 1, fn => \&StatusAPIVal, par => '', unit => '', def => 0 },
|
todayDoneAPIrequests => { fnr => 1, fn => \&StatusAPIVal, par => '', par1 => '', unit => '', def => 0 },
|
||||||
todayRemainingAPIcalls => { fnr => 1, fn => \&StatusAPIVal, par => '', unit => '', def => 'apimaxreq' },
|
todayRemainingAPIcalls => { fnr => 1, fn => \&StatusAPIVal, par => '', par1 => '', unit => '', def => 'apimaxreq' },
|
||||||
todayRemainingAPIrequests => { fnr => 1, fn => \&StatusAPIVal, par => '', unit => '', def => 'apimaxreq' },
|
todayRemainingAPIrequests => { fnr => 1, fn => \&StatusAPIVal, par => '', par1 => '', unit => '', def => 'apimaxreq' },
|
||||||
runTimeCentralTask => { fnr => 2, fn => \&CurrentVal, par => '', unit => ' s', def => '-' },
|
runTimeCentralTask => { fnr => 2, fn => \&CurrentVal, par => '', par1 => '', unit => ' s', def => '-' },
|
||||||
runTimeLastAPIAnswer => { fnr => 2, fn => \&CurrentVal, par => '', unit => '', def => '-' },
|
runTimeLastAPIAnswer => { fnr => 2, fn => \&CurrentVal, par => '', par1 => '', unit => '', def => '-' },
|
||||||
runTimeLastAPIProc => { fnr => 2, fn => \&CurrentVal, par => '', unit => '', def => '-' },
|
runTimeLastAPIProc => { fnr => 2, fn => \&CurrentVal, par => '', par1 => '', unit => '', def => '-' },
|
||||||
allStringsFullfilled => { fnr => 2, fn => \&CurrentVal, par => '', unit => '', def => 0 },
|
allStringsFullfilled => { fnr => 2, fn => \&CurrentVal, par => '', par1 => '', unit => '', def => 0 },
|
||||||
todayConForecastTillSunset => { fnr => 2, fn => \&CurrentVal, par => 'tdConFcTillSunset', unit => ' Wh', def => 0 },
|
todayConForecastTillSunset => { fnr => 2, fn => \&CurrentVal, par => 'tdConFcTillSunset', par1 => '', unit => ' Wh', def => 0 },
|
||||||
runTimeTrainAI => { fnr => 3, fn => \&CircularVal, par => 99, unit => ' s', def => '-' },
|
runTimeTrainAI => { fnr => 3, fn => \&CircularVal, par => 99, par1 => '', unit => ' s', def => '-' },
|
||||||
todayConsumption => { fnr => 3, fn => \&CircularVal, par => 99, unit => ' Wh', def => 0 },
|
todayConsumption => { fnr => 3, fn => \&CircularVal, par => 99, par1 => '', unit => ' Wh', def => 0 },
|
||||||
BatPowerIn_Sum => { fnr => 4, fn => \&CurrentVal, par => 'batpowerinsum', unit => ' W', def => '-' },
|
todayConsumptionForecastDay => { fnr => 4, fn => \&HistoryVal, par => 99, par1 => 'confc', unit => ' Wh', def => '-' },
|
||||||
BatPowerOut_Sum => { fnr => 4, fn => \&CurrentVal, par => 'batpoweroutsum', unit => ' W', def => '-' },
|
BatPowerIn_Sum => { fnr => 5, fn => \&CurrentVal, par => 'batpowerinsum', par1 => '', unit => ' W', def => '-' },
|
||||||
SunHours_Remain => { fnr => 4, fn => \&CurrentVal, par => '', unit => '', def => 0 }, # fnr => 3 -> Custom Calc
|
BatPowerOut_Sum => { fnr => 5, fn => \&CurrentVal, par => 'batpoweroutsum', par1 => '', unit => ' W', def => '-' },
|
||||||
SunMinutes_Remain => { fnr => 4, fn => \&CurrentVal, par => '', unit => '', def => 0 },
|
SunHours_Remain => { fnr => 5, fn => \&CurrentVal, par => '', par1 => '', unit => '', def => 0 }, # fnr => 3 -> Custom Calc
|
||||||
dayAfterTomorrowPVforecast => { fnr => 4, fn => \&RadiationAPIVal, par => 'pv_estimate50', unit => '', def => 0 },
|
SunMinutes_Remain => { fnr => 5, fn => \&CurrentVal, par => '', par1 => '', unit => '', def => 0 },
|
||||||
todayGridFeedIn => { fnr => 4, fn => \&CircularVal, par => 99, unit => '', def => 0 },
|
dayAfterTomorrowPVforecast => { fnr => 5, fn => \&RadiationAPIVal, par => 'pv_estimate50', par1 => '', unit => '', def => 0 },
|
||||||
todayGridConsumption => { fnr => 4, fn => \&CircularVal, par => 99, unit => '', def => 0 },
|
todayGridFeedIn => { fnr => 5, fn => \&CircularVal, par => 99, par1 => '', unit => '', def => 0 },
|
||||||
todayConsumptionForecast => { fnr => 4, fn => \&NexthoursVal, par => 'confc', unit => ' Wh', def => '-' },
|
todayGridConsumption => { fnr => 5, fn => \&CircularVal, par => 99, par1 => '', unit => '', def => 0 },
|
||||||
conForecastTillNextSunrise => { fnr => 4, fn => \&NexthoursVal, par => 'confc', unit => ' Wh', def => 0 },
|
todayConsumptionForecast => { fnr => 5, fn => \&HistoryVal, par => '', par1 => 'confc', unit => ' Wh', def => '-' },
|
||||||
todayBatInSum => { fnr => 4, fn => \&CircularVal, par => 99, unit => ' Wh', def => 0 },
|
conForecastTillNextSunrise => { fnr => 5, fn => \&NexthoursVal, par => 'confc', par1 => '', unit => ' Wh', def => 0 },
|
||||||
todayBatOutSum => { fnr => 4, fn => \&CircularVal, par => 99, unit => ' Wh', def => 0 },
|
todayBatInSum => { fnr => 5, fn => \&CircularVal, par => 99, par1 => '', unit => ' Wh', def => 0 },
|
||||||
|
todayBatOutSum => { fnr => 5, fn => \&CircularVal, par => 99, par1 => '', unit => ' Wh', def => 0 },
|
||||||
);
|
);
|
||||||
|
|
||||||
for my $csr (1..MAXCONSUMER) {
|
for my $csr (1..MAXCONSUMER) {
|
||||||
$csr = sprintf "%02d", $csr;
|
$csr = sprintf "%02d", $csr;
|
||||||
|
|
||||||
$hcsr{'currentRunMtsConsumer_'.$csr}{fnr} = 4;
|
$hcsr{'currentRunMtsConsumer_'.$csr}{fnr} = 5;
|
||||||
$hcsr{'currentRunMtsConsumer_'.$csr}{fn} = \&ConsumerVal;
|
$hcsr{'currentRunMtsConsumer_'.$csr}{fn} = \&ConsumerVal;
|
||||||
$hcsr{'currentRunMtsConsumer_'.$csr}{par} = 'cycleTime';
|
$hcsr{'currentRunMtsConsumer_'.$csr}{par} = 'cycleTime';
|
||||||
$hcsr{'currentRunMtsConsumer_'.$csr}{unit} = ' min';
|
$hcsr{'currentRunMtsConsumer_'.$csr}{unit} = ' min';
|
||||||
$hcsr{'currentRunMtsConsumer_'.$csr}{def} = 0;
|
$hcsr{'currentRunMtsConsumer_'.$csr}{def} = 0;
|
||||||
|
|
||||||
$hcsr{'runTimeAvgDayConsumer_'.$csr}{fnr} = 4;
|
$hcsr{'runTimeAvgDayConsumer_'.$csr}{fnr} = 5;
|
||||||
$hcsr{'runTimeAvgDayConsumer_'.$csr}{fn} = \&ConsumerVal;
|
$hcsr{'runTimeAvgDayConsumer_'.$csr}{fn} = \&ConsumerVal;
|
||||||
$hcsr{'runTimeAvgDayConsumer_'.$csr}{par} = 'runtimeAvgDay';
|
$hcsr{'runTimeAvgDayConsumer_'.$csr}{par} = 'runtimeAvgDay';
|
||||||
$hcsr{'runTimeAvgDayConsumer_'.$csr}{unit} = ' min';
|
$hcsr{'runTimeAvgDayConsumer_'.$csr}{unit} = ' min';
|
||||||
@ -1289,19 +1294,19 @@ my %hcsr = (
|
|||||||
for my $bn (1..MAXBATTERIES) {
|
for my $bn (1..MAXBATTERIES) {
|
||||||
$bn = sprintf "%02d", $bn;
|
$bn = sprintf "%02d", $bn;
|
||||||
|
|
||||||
$hcsr{'daysUntilBatteryCare_'.$bn}{fnr} = 4;
|
$hcsr{'daysUntilBatteryCare_'.$bn}{fnr} = 5;
|
||||||
$hcsr{'daysUntilBatteryCare_'.$bn}{fn} = \&CircularVal;
|
$hcsr{'daysUntilBatteryCare_'.$bn}{fn} = \&CircularVal;
|
||||||
$hcsr{'daysUntilBatteryCare_'.$bn}{par} = 99;
|
$hcsr{'daysUntilBatteryCare_'.$bn}{par} = 99;
|
||||||
$hcsr{'daysUntilBatteryCare_'.$bn}{unit} = '';
|
$hcsr{'daysUntilBatteryCare_'.$bn}{unit} = '';
|
||||||
$hcsr{'daysUntilBatteryCare_'.$bn}{def} = '-';
|
$hcsr{'daysUntilBatteryCare_'.$bn}{def} = '-';
|
||||||
|
|
||||||
$hcsr{'todayBatIn_'.$bn}{fnr} = 4;
|
$hcsr{'todayBatIn_'.$bn}{fnr} = 5;
|
||||||
$hcsr{'todayBatIn_'.$bn}{fn} = \&CircularVal;
|
$hcsr{'todayBatIn_'.$bn}{fn} = \&CircularVal;
|
||||||
$hcsr{'todayBatIn_'.$bn}{par} = 99;
|
$hcsr{'todayBatIn_'.$bn}{par} = 99;
|
||||||
$hcsr{'todayBatIn_'.$bn}{unit} = ' Wh';
|
$hcsr{'todayBatIn_'.$bn}{unit} = ' Wh';
|
||||||
$hcsr{'todayBatIn_'.$bn}{def} = 0;
|
$hcsr{'todayBatIn_'.$bn}{def} = 0;
|
||||||
|
|
||||||
$hcsr{'todayBatOut_'.$bn}{fnr} = 4;
|
$hcsr{'todayBatOut_'.$bn}{fnr} = 5;
|
||||||
$hcsr{'todayBatOut_'.$bn}{fn} = \&CircularVal;
|
$hcsr{'todayBatOut_'.$bn}{fn} = \&CircularVal;
|
||||||
$hcsr{'todayBatOut_'.$bn}{par} = 99;
|
$hcsr{'todayBatOut_'.$bn}{par} = 99;
|
||||||
$hcsr{'todayBatOut_'.$bn}{unit} = ' Wh';
|
$hcsr{'todayBatOut_'.$bn}{unit} = ' Wh';
|
||||||
@ -2353,8 +2358,6 @@ sub _setreset { ## no critic "not used"
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
aiInit ($paref);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5328,22 +5331,32 @@ sub __getaiRuleStrings { ## no critic "not used"
|
|||||||
|
|
||||||
return 'the AI usage is not prepared' if(!isPrepared4AI ($hash));
|
return 'the AI usage is not prepared' if(!isPrepared4AI ($hash));
|
||||||
|
|
||||||
my $dtree = AiDetreeVal ($hash, 'aitrained', undef);
|
my $objref = AiDetreeVal ($hash, 'aitrained', '');
|
||||||
|
return 'AI trained object is missed or not an ARRAY' if(ref $objref ne 'ARRAY');
|
||||||
if (!$dtree) {
|
|
||||||
return 'AI trained object is missed';
|
|
||||||
}
|
|
||||||
|
|
||||||
my $rs = 'no rules delivered';
|
my $rs = 'no rules delivered';
|
||||||
my (@rsl, $nodes, $depth);
|
my (@rsl, %entities);
|
||||||
|
my $tn = 0;
|
||||||
|
|
||||||
eval { @rsl = $dtree->rule_statements(); # Returns a list of strings that describe the tree in rule-form
|
for my $dtree (@{$objref}) {
|
||||||
$nodes = $dtree->nodes(); # Returns the number of nodes in the trained decision tree
|
eval { my @rules = $dtree->rule_statements(); # Returns a list of strings that describe the tree in rule-form
|
||||||
$depth = $dtree->depth(); # Returns the depth of the tree. This is the maximum number of decisions that would need to be made to classify an unseen instance, i.e. the length of the longest path from the tree's root to a leaf.
|
$tn++;
|
||||||
|
|
||||||
|
if ($tn == 1) { # nur den ersten Tree ausgeben
|
||||||
|
push @rsl, ' ';
|
||||||
|
push @rsl, 'Tree: '.$tn;
|
||||||
|
push @rsl, ' ';
|
||||||
|
push @rsl, @rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
$entities{$tn}{rules} = scalar @rules; # Anzahl der Regeln
|
||||||
|
$entities{$tn}{nodes} = $dtree->nodes(); # Returns the number of nodes in the trained decision tree
|
||||||
|
$entities{$tn}{depth} = $dtree->depth(); # Returns the depth of the tree. This is the maximum number of decisions that would need to be made to classify an unseen instance, i.e. the length of the longest path from the tree's root to a leaf.
|
||||||
1;
|
1;
|
||||||
}
|
}
|
||||||
or do { return $@;
|
or do { return $@;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
my $atf = CircularVal ($hash, 99, 'aitrainLastFinishTs', 0);
|
my $atf = CircularVal ($hash, 99, 'aitrainLastFinishTs', 0);
|
||||||
$atf = '<b>'.$hqtxt{ailatr}{$lang}.' </b>'.($atf ? (timestampToTimestring ($atf, $lang))[0] : '-');
|
$atf = '<b>'.$hqtxt{ailatr}{$lang}.' </b>'.($atf ? (timestampToTimestring ($atf, $lang))[0] : '-');
|
||||||
@ -5351,7 +5364,13 @@ sub __getaiRuleStrings { ## no critic "not used"
|
|||||||
|
|
||||||
if (@rsl) {
|
if (@rsl) {
|
||||||
my $l = scalar @rsl;
|
my $l = scalar @rsl;
|
||||||
$rs = "<b>Number of Rules: $l / Number of Nodes: $nodes / Depth: $depth</b>\n";
|
$rs = "<b>Trained AI Object contains an Ensemble of $tn trees (only the first Tree is printed out)</b>\n";
|
||||||
|
|
||||||
|
for my $tree (1..$tn) {
|
||||||
|
$rs .= "<b>Tree: $tree / Number of Rules: $entities{$tree}{rules} / Number of Nodes: $entities{$tree}{nodes} / Depth: $entities{$tree}{depth} </b>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$rs .= "\n\n";
|
||||||
$rs .= "Rules: ".$hqtxt{airule}{$lang}."\n";
|
$rs .= "Rules: ".$hqtxt{airule}{$lang}."\n";
|
||||||
$rs .= "Nodes: ".$hqtxt{ainode}{$lang}."\n";
|
$rs .= "Nodes: ".$hqtxt{ainode}{$lang}."\n";
|
||||||
$rs .= "Depth: ".$hqtxt{aidept}{$lang};
|
$rs .= "Depth: ".$hqtxt{aidept}{$lang};
|
||||||
@ -7053,19 +7072,28 @@ sub readCacheFile {
|
|||||||
my $hash = $defs{$name};
|
my $hash = $defs{$name};
|
||||||
|
|
||||||
if ($cachename eq 'aitrained') {
|
if ($cachename eq 'aitrained') {
|
||||||
my ($err, $dtree) = fileRetrieve ($file);
|
my ($err, $objref) = fileRetrieve ($file);
|
||||||
|
|
||||||
if (!$err && $dtree) {
|
if (!$err && $objref) {
|
||||||
my $valid = $dtree->isa('AI::DecisionTree');
|
if (ref $objref ne 'ARRAY') {
|
||||||
|
return "The file $file was restored but the content is not an ARRAY";
|
||||||
|
}
|
||||||
|
|
||||||
|
for my $obj (@{$objref}) {
|
||||||
|
my $valid = $obj->isa('AI::DecisionTree');
|
||||||
|
return 'The trained object is not AI::DecisionTree' if(!$valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
undef @{$data{$name}{aidectree}{aitrained}};
|
||||||
|
delete $data{$name}{aidectree}{aitrained};
|
||||||
|
|
||||||
|
push @{$data{$name}{aidectree}{aitrained}}, @{$objref};
|
||||||
|
|
||||||
if ($valid) {
|
|
||||||
$data{$name}{aidectree}{aitrained} = $dtree;
|
|
||||||
$data{$name}{current}{aitrainstate} = 'ok';
|
$data{$name}{current}{aitrainstate} = 'ok';
|
||||||
|
|
||||||
Log3 ($name, 3, qq{$name - cached data "$title" restored});
|
Log3 ($name, 3, qq{$name - cached data "$title" restored});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
delete $data{$name}{circular}{99}{aitrainLastFinishTs};
|
delete $data{$name}{circular}{99}{aitrainLastFinishTs};
|
||||||
delete $data{$name}{circular}{99}{runTimeTrainAI};
|
delete $data{$name}{circular}{99}{runTimeTrainAI};
|
||||||
@ -7175,10 +7203,14 @@ sub writeCacheToFile {
|
|||||||
my ($error, $err, $lw);
|
my ($error, $err, $lw);
|
||||||
|
|
||||||
if ($cachename eq 'aitrained') {
|
if ($cachename eq 'aitrained') {
|
||||||
my $dtree = AiDetreeVal ($hash, 'aitrained', '');
|
my $objref = AiDetreeVal ($hash, 'aitrained', '');
|
||||||
return if(ref $dtree ne 'AI::DecisionTree');
|
return 'trained object is not an ARRAY' if(ref $objref ne 'ARRAY');
|
||||||
|
|
||||||
$error = fileStore ($dtree, $file);
|
for my $obj (@{$objref}) {
|
||||||
|
return 'wrong trained object' if(ref $obj ne 'AI::DecisionTree');
|
||||||
|
}
|
||||||
|
|
||||||
|
$error = fileStore ($objref, $file);
|
||||||
|
|
||||||
if ($error) {
|
if ($error) {
|
||||||
$err = qq{ERROR while writing AI data to file "$file": $error};
|
$err = qq{ERROR while writing AI data to file "$file": $error};
|
||||||
@ -7737,7 +7769,7 @@ sub centralTask {
|
|||||||
_calcDataEveryFullHour ($centpars); # Daten berechnen/speichern die nur einmal nach jeder vollen Stunde ermittelt werden
|
_calcDataEveryFullHour ($centpars); # Daten berechnen/speichern die nur einmal nach jeder vollen Stunde ermittelt werden
|
||||||
_saveEnergyConsumption ($centpars); # Energie Hausverbrauch speichern
|
_saveEnergyConsumption ($centpars); # Energie Hausverbrauch speichern
|
||||||
_createSummaries ($centpars); # Zusammenfassungen erstellen
|
_createSummaries ($centpars); # Zusammenfassungen erstellen
|
||||||
_genSpecialReadings ($centpars); # optionale Statistikreadings erstellen
|
_genSpecialReadings ($centpars); # optionale Spezialreadings erstellen
|
||||||
|
|
||||||
userExit ($centpars); # User spezifische Funktionen ausführen
|
userExit ($centpars); # User spezifische Funktionen ausführen
|
||||||
setTimeTracking ($hash, $cst, 'runTimeCentralTask'); # Zyklus-Laufzeit ermitteln
|
setTimeTracking ($hash, $cst, 'runTimeCentralTask'); # Zyklus-Laufzeit ermitteln
|
||||||
@ -12927,6 +12959,9 @@ sub _genSpecialReadings {
|
|||||||
storeReading ($prpo.'_'.$kpi, &{$hcsr{$kpi}{fn}} ($hash, $hcsr{$kpi}{par}, $kpi, $def).$hcsr{$kpi}{unit});
|
storeReading ($prpo.'_'.$kpi, &{$hcsr{$kpi}{fn}} ($hash, $hcsr{$kpi}{par}, $kpi, $def).$hcsr{$kpi}{unit});
|
||||||
}
|
}
|
||||||
elsif ($hcsr{$kpi}{fnr} == 4) {
|
elsif ($hcsr{$kpi}{fnr} == 4) {
|
||||||
|
storeReading ($prpo.'_'.$kpi, &{$hcsr{$kpi}{fn}} ($hash, $day, $hcsr{$kpi}{par}, $hcsr{$kpi}{par1}, $def).$hcsr{$kpi}{unit});
|
||||||
|
}
|
||||||
|
elsif ($hcsr{$kpi}{fnr} == 5) {
|
||||||
if ($kpi eq 'SunHours_Remain') {
|
if ($kpi eq 'SunHours_Remain') {
|
||||||
my $ss = &{$hcsr{$kpi}{fn}} ($hash, 'sunsetTodayTs', $def);
|
my $ss = &{$hcsr{$kpi}{fn}} ($hash, 'sunsetTodayTs', $def);
|
||||||
my $shr = ($ss - $t) / 3600;
|
my $shr = ($ss - $t) / 3600;
|
||||||
@ -13029,7 +13064,6 @@ sub _genSpecialReadings {
|
|||||||
my $dayaftertomorrow = strftime "%Y-%m-%d", localtime($t + 172800); # Datum von Übermorgen
|
my $dayaftertomorrow = strftime "%Y-%m-%d", localtime($t + 172800); # Datum von Übermorgen
|
||||||
my @allstrings = split ",", AttrVal ($name, 'setupInverterStrings', '');
|
my @allstrings = split ",", AttrVal ($name, 'setupInverterStrings', '');
|
||||||
my $fcsumdat = 0;
|
my $fcsumdat = 0;
|
||||||
my $type = $paref->{type};
|
|
||||||
|
|
||||||
for my $strg (@allstrings) {
|
for my $strg (@allstrings) {
|
||||||
for my $starttmstr (sort keys %{$data{$name}{solcastapi}{$strg}}) {
|
for my $starttmstr (sort keys %{$data{$name}{solcastapi}{$strg}}) {
|
||||||
@ -13077,8 +13111,16 @@ sub _genSpecialReadings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($kpi eq 'todayConsumptionForecast') {
|
if ($kpi eq 'todayConsumptionForecast') {
|
||||||
my $type = $paref->{type};
|
for my $hod (sort keys %{$data{$name}{pvhist}{$day}}) {
|
||||||
|
next if(!$hod || $hod == 99);
|
||||||
|
|
||||||
|
my $confc = &{$hcsr{$kpi}{fn}} ($hash, $day, $hod, $hcsr{$kpi}{par1}, $def);
|
||||||
|
|
||||||
|
storeReading ($prpo.'_'.$kpi.'_'.$hod, $confc.$hcsr{$kpi}{unit});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($kpi eq 'todayConsumptionForecastNh') {
|
||||||
for my $idx (sort keys %{$data{$name}{nexthours}}) {
|
for my $idx (sort keys %{$data{$name}{nexthours}}) {
|
||||||
my $istoday = NexthoursVal ($hash, $idx, 'today', 0);
|
my $istoday = NexthoursVal ($hash, $idx, 'today', 0);
|
||||||
last if(!$istoday);
|
last if(!$istoday);
|
||||||
@ -13091,7 +13133,6 @@ sub _genSpecialReadings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($kpi eq 'conForecastTillNextSunrise') {
|
if ($kpi eq 'conForecastTillNextSunrise') {
|
||||||
my $type = $paref->{type};
|
|
||||||
my $confc = 0;
|
my $confc = 0;
|
||||||
my $dono = 1;
|
my $dono = 1;
|
||||||
my $hrs = 0;
|
my $hrs = 0;
|
||||||
@ -14796,7 +14837,7 @@ sub _beamGraphicFirstHour {
|
|||||||
$beam1cont eq 'energycosts' ? $val6 :
|
$beam1cont eq 'energycosts' ? $val6 :
|
||||||
$beam1cont eq 'gridfeedin' ? $val7 :
|
$beam1cont eq 'gridfeedin' ? $val7 :
|
||||||
$beam1cont eq 'feedincome' ? $val8 :
|
$beam1cont eq 'feedincome' ? $val8 :
|
||||||
$beam1cont =~ /batsocforecast_/xs ? $hbsocs->{0}{(split '_', $beam1cont)[1]} :
|
$beam1cont =~ /batsocforecast_/xs ? ($hbsocs->{0}{(split '_', $beam1cont)[1]} < 100 ? sprintf "%.1f", $hbsocs->{0}{(split '_', $beam1cont)[1]} : 100) :
|
||||||
undef;
|
undef;
|
||||||
|
|
||||||
$hfcg->{0}{beam2} = $beam2cont eq 'pvForecast' ? $val1 :
|
$hfcg->{0}{beam2} = $beam2cont eq 'pvForecast' ? $val1 :
|
||||||
@ -14807,7 +14848,7 @@ sub _beamGraphicFirstHour {
|
|||||||
$beam2cont eq 'energycosts' ? $val6 :
|
$beam2cont eq 'energycosts' ? $val6 :
|
||||||
$beam2cont eq 'gridfeedin' ? $val7 :
|
$beam2cont eq 'gridfeedin' ? $val7 :
|
||||||
$beam2cont eq 'feedincome' ? $val8 :
|
$beam2cont eq 'feedincome' ? $val8 :
|
||||||
$beam2cont =~ /batsocforecast_/xs ? $hbsocs->{0}{(split '_', $beam2cont)[1]} :
|
$beam2cont =~ /batsocforecast_/xs ? ($hbsocs->{0}{(split '_', $beam2cont)[1]} < 100 ? sprintf "%.1f", $hbsocs->{0}{(split '_', $beam2cont)[1]} : 100) :
|
||||||
undef;
|
undef;
|
||||||
|
|
||||||
$hfcg->{0}{beam1} //= 0;
|
$hfcg->{0}{beam1} //= 0;
|
||||||
@ -14955,7 +14996,7 @@ sub _beamGraphicRemainingHours {
|
|||||||
$beam1cont eq 'energycosts' ? $val6 :
|
$beam1cont eq 'energycosts' ? $val6 :
|
||||||
$beam1cont eq 'gridfeedin' ? $val7 :
|
$beam1cont eq 'gridfeedin' ? $val7 :
|
||||||
$beam1cont eq 'feedincome' ? $val8 :
|
$beam1cont eq 'feedincome' ? $val8 :
|
||||||
$beam1cont =~ /batsocforecast_/xs ? $hbsocs->{$i}{(split '_', $beam1cont)[1]} :
|
$beam1cont =~ /batsocforecast_/xs ? ($hbsocs->{$i}{(split '_', $beam1cont)[1]} < 100 ? sprintf "%.1f", $hbsocs->{$i}{(split '_', $beam1cont)[1]} : 100) :
|
||||||
undef;
|
undef;
|
||||||
|
|
||||||
$hfcg->{$i}{beam2} = $beam2cont eq 'pvForecast' ? $val1 :
|
$hfcg->{$i}{beam2} = $beam2cont eq 'pvForecast' ? $val1 :
|
||||||
@ -14966,7 +15007,7 @@ sub _beamGraphicRemainingHours {
|
|||||||
$beam2cont eq 'energycosts' ? $val6 :
|
$beam2cont eq 'energycosts' ? $val6 :
|
||||||
$beam2cont eq 'gridfeedin' ? $val7 :
|
$beam2cont eq 'gridfeedin' ? $val7 :
|
||||||
$beam2cont eq 'feedincome' ? $val8 :
|
$beam2cont eq 'feedincome' ? $val8 :
|
||||||
$beam2cont =~ /batsocforecast_/xs ? $hbsocs->{$i}{(split '_', $beam2cont)[1]} :
|
$beam2cont =~ /batsocforecast_/xs ? ($hbsocs->{$i}{(split '_', $beam2cont)[1]} < 100 ? sprintf "%.1f", $hbsocs->{$i}{(split '_', $beam2cont)[1]} : 100) :
|
||||||
undef;
|
undef;
|
||||||
|
|
||||||
$hfcg->{$i}{time_str} = sprintf ('%02d', $hfcg->{$i}{time}-1).$hourstyle;
|
$hfcg->{$i}{time_str} = sprintf ('%02d', $hfcg->{$i}{time}-1).$hourstyle;
|
||||||
@ -15763,11 +15804,9 @@ sub _flowGraphic {
|
|||||||
######################################################
|
######################################################
|
||||||
my $pnodesum = __normDecPlaces ($ppall + $pv2node); # Erzeugung Summe im Knoten
|
my $pnodesum = __normDecPlaces ($ppall + $pv2node); # Erzeugung Summe im Knoten
|
||||||
$node2bat -= $pv2bat; # Knoten-Bat -> abzüglich Direktladung (pv2bat)
|
$node2bat -= $pv2bat; # Knoten-Bat -> abzüglich Direktladung (pv2bat)
|
||||||
#Log3 ($name, 1, "$name - pv2bat: $pv2bat, node2bat:$node2bat ");
|
|
||||||
$pnodesum += $node2bat < 0 ? abs $node2bat : 0; # V 1.46.4 - Batterie ist voll und SolarLader liefert an Knoten
|
$pnodesum += $node2bat < 0 ? abs $node2bat : 0; # V 1.46.4 - Batterie ist voll und SolarLader liefert an Knoten
|
||||||
|
my $node2home = $pnodesum - $node2grid - ($node2bat > 0 ? $node2bat : 0); # V 1.46.4 - Energiefluß vom Knoten zum Haus
|
||||||
#my $node2home = __normDecPlaces ($cself + $ppall); # Energiefluß vom Knoten zum Haus: Selbstverbrauch + alle Producer (Batterie-In/Solar-Ladegeräte sind nicht in SelfConsumtion enthalten)
|
$node2home = __normDecPlaces ($node2home); # V 1.46.4
|
||||||
my $node2home = __normDecPlaces ($pnodesum - $node2grid - ($node2bat > 0 ? $node2bat : 0)); # V 1.46.4 - Energiefluß vom Knoten zum Haus
|
|
||||||
|
|
||||||
## SVG Box initialisieren mit Grid-Icon
|
## SVG Box initialisieren mit Grid-Icon
|
||||||
#########################################
|
#########################################
|
||||||
@ -17001,6 +17040,105 @@ sub outputMessages {
|
|||||||
return $out;
|
return $out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# KI Instanz(en) aus Raw Daten Hash erzeugen
|
||||||
|
# mit Ensemble-Algorithmus
|
||||||
|
################################################################
|
||||||
|
sub aiAddInstancePV {
|
||||||
|
my $paref = shift;
|
||||||
|
my $name = $paref->{name};
|
||||||
|
my $taa = $paref->{taa}; # do train after add
|
||||||
|
|
||||||
|
my $hash = $defs{$name};
|
||||||
|
|
||||||
|
return if(!isPrepared4AI ($hash));
|
||||||
|
|
||||||
|
sub sample_data { # Hilfsfunktion zum Erstellen einer Stichprobe der Daten
|
||||||
|
my $data = shift;
|
||||||
|
my @shuffled = shuffle @$data;
|
||||||
|
my $sample_size = int (scalar (@$data) * 0.8);
|
||||||
|
return @shuffled[0 .. $sample_size - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
my @data;
|
||||||
|
|
||||||
|
for my $idx (sort keys %{$data{$name}{aidectree}{airaw}}) {
|
||||||
|
next if(!$idx);
|
||||||
|
|
||||||
|
my $pvrl = AiRawdataVal ($hash, $idx, 'pvrl', undef);
|
||||||
|
next if(!defined $pvrl);
|
||||||
|
|
||||||
|
my $hod = AiRawdataVal ($hash, $idx, 'hod', undef);
|
||||||
|
next if(!defined $hod);
|
||||||
|
|
||||||
|
my $rad1h = AiRawdataVal ($hash, $idx, 'rad1h', 0);
|
||||||
|
next if($rad1h <= 0);
|
||||||
|
|
||||||
|
my $temp = AiRawdataVal ($hash, $idx, 'temp', undef);
|
||||||
|
my $wcc = AiRawdataVal ($hash, $idx, 'wcc', undef);
|
||||||
|
my $rr1c = AiRawdataVal ($hash, $idx, 'rr1c', undef);
|
||||||
|
my $sunalt = AiRawdataVal ($hash, $idx, 'sunalt', 0);
|
||||||
|
|
||||||
|
my $tbin = temp2bin ($temp) if(defined $temp);
|
||||||
|
my $cbin = cloud2bin ($wcc) if(defined $wcc);
|
||||||
|
my $sabin = sunalt2bin ($sunalt);
|
||||||
|
|
||||||
|
push @data, { rad1h => $rad1h, temp => $tbin, wcc => $cbin, rr1c => $rr1c, sunalt => $sunalt, hod => $hod, pvrl => $pvrl };
|
||||||
|
}
|
||||||
|
|
||||||
|
return if(!scalar @data);
|
||||||
|
|
||||||
|
for my $tn (1 .. AINUMTREES) { # Trainiere mehrere Entscheidungsbäume auf unterschiedlichen Stichproben
|
||||||
|
my @sampled_data = sample_data (\@data);
|
||||||
|
my ($err, $dtree) = aiInit ($paref);
|
||||||
|
return if($err);
|
||||||
|
|
||||||
|
my $aiAddedToTrain = 0;
|
||||||
|
|
||||||
|
for my $instance (@sampled_data) {
|
||||||
|
eval { $dtree->add_instance (attributes => { rad1h => $instance->{rad1h},
|
||||||
|
temp => $instance->{temp},
|
||||||
|
wcc => $instance->{wcc},
|
||||||
|
rr1c => $instance->{rr1c},
|
||||||
|
sunalt => $instance->{sunalt},
|
||||||
|
hod => $instance->{hod}
|
||||||
|
},
|
||||||
|
result => $instance->{pvrl}
|
||||||
|
);
|
||||||
|
1;
|
||||||
|
}
|
||||||
|
or do { Log3 ($name, 1, "$name - aiAddInstancePV ERROR: $@");
|
||||||
|
$data{$name}{current}{aiaddistate} = $@;
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
$aiAddedToTrain++;
|
||||||
|
|
||||||
|
debugLog ($paref, 'aiProcess', "AI Instance added Tree $tn - ".
|
||||||
|
"hod: $instance->{hod}, ".
|
||||||
|
"sunalt: $instance->{sunalt}, ".
|
||||||
|
"rad1h: $instance->{rad1h}, pvrl: instance->{pvrl}, ".
|
||||||
|
"wcc: ".(defined $instance->{wcc} ? $instance->{wcc} : '-').", ".
|
||||||
|
"rr1c: ".(defined $instance->{rr1c} ? $instance->{rr1c} : '-').", ".
|
||||||
|
"temp: ".(defined $instance->{temp} ? $instance->{temp} : '-'),
|
||||||
|
4);
|
||||||
|
}
|
||||||
|
|
||||||
|
debugLog ($paref, 'aiProcess', "AI Instance add - Tree: $tn -> ".$aiAddedToTrain." entities added for training ".(AttrVal ($name, 'verbose', 3) < 4 ? '(set verbose 4 for output more detail)' : ''));
|
||||||
|
|
||||||
|
$data{$name}{aidectree}{object}{$tn}{dtree} = $dtree;
|
||||||
|
$data{$name}{aidectree}{object}{$tn}{enum} = $aiAddedToTrain;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data{$name}{current}{aiaddistate} = 'ok';
|
||||||
|
|
||||||
|
if ($taa) {
|
||||||
|
manageTrain ($paref);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
###############################################################
|
###############################################################
|
||||||
# Eintritt in den KI Train Prozess normal/Blocking
|
# Eintritt in den KI Train Prozess normal/Blocking
|
||||||
###############################################################
|
###############################################################
|
||||||
@ -17043,6 +17181,117 @@ sub manageTrain {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# KI trainieren
|
||||||
|
################################################################
|
||||||
|
sub aiTrain {
|
||||||
|
my $paref = shift;
|
||||||
|
my $name = $paref->{name};
|
||||||
|
my $block = $paref->{block} // 0;
|
||||||
|
|
||||||
|
my $hash = $defs{$name};
|
||||||
|
my ($serial, $err);
|
||||||
|
|
||||||
|
if (!isPrepared4AI ($hash)) {
|
||||||
|
$err = CurrentVal ($hash, 'aicanuse', '');
|
||||||
|
$serial = encode_base64 (Serialize ( { name => $name,
|
||||||
|
aitrainstate => "Train: not performed -> $err",
|
||||||
|
aicanuse => $err
|
||||||
|
}
|
||||||
|
), "");
|
||||||
|
|
||||||
|
$block ? return ($serial) : return \&finishTrain ($serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
my $cst = [gettimeofday]; # Train Startzeit
|
||||||
|
my $object = AiDetreeVal ($hash, 'object', undef);
|
||||||
|
|
||||||
|
if (!$object) {
|
||||||
|
$err = 'no AI::DecisionTree object present';
|
||||||
|
$serial = encode_base64 (Serialize ( {name => $name,
|
||||||
|
aitrainstate => "Train: not performed -> $err",
|
||||||
|
aiinitstate => "Init: $err",
|
||||||
|
aicanuse => 'ok'
|
||||||
|
}
|
||||||
|
), "");
|
||||||
|
|
||||||
|
$block ? return ($serial) : return \&finishTrain ($serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
my @ensemble; # Erstelle das Ensemble
|
||||||
|
my %entities;
|
||||||
|
|
||||||
|
for my $tn (1 .. AINUMTREES) { # Trainiere mehrere Entscheidungsbäume auf unterschiedlichen Stichproben
|
||||||
|
my $dtree = $object->{$tn}{dtree}; # dtree Objekt
|
||||||
|
my $enum = $object->{$tn}{enum}; # Anazhl Elemente im Tree
|
||||||
|
|
||||||
|
eval { $dtree->train();
|
||||||
|
1;
|
||||||
|
}
|
||||||
|
or do { Log3 ($name, 1, "$name - aiTrain ERROR: $@");
|
||||||
|
$err = (split / at/, $@)[0];
|
||||||
|
$serial = encode_base64 (Serialize ( {name => $name,
|
||||||
|
aitrainstate => "Train: $err",
|
||||||
|
aicanuse => 'ok'
|
||||||
|
}
|
||||||
|
), "");
|
||||||
|
|
||||||
|
$block ? return ($serial) : return \&finishTrain ($serial);
|
||||||
|
};
|
||||||
|
|
||||||
|
push @ensemble, $dtree;
|
||||||
|
|
||||||
|
$entities{$tn} = $enum;
|
||||||
|
$entities{rn} += scalar $dtree->rule_statements();
|
||||||
|
}
|
||||||
|
|
||||||
|
undef @{$data{$name}{aidectree}{aitrained}};
|
||||||
|
delete $data{$name}{aidectree}{aitrained};
|
||||||
|
|
||||||
|
push @{$data{$name}{aidectree}{aitrained}}, @ensemble;
|
||||||
|
|
||||||
|
$err = writeCacheToFile ($hash, 'aitrained', $aitrained.$name);
|
||||||
|
my $rn;
|
||||||
|
|
||||||
|
if (!$err) {
|
||||||
|
$rn = delete $entities{rn};
|
||||||
|
|
||||||
|
while (my ($tree, $ent) = each %entities) {
|
||||||
|
debugLog ($paref, 'aiProcess', "AI trained Tree: $tree, number of entities: $ent");
|
||||||
|
}
|
||||||
|
|
||||||
|
debugLog ($paref, 'aiProcess', qq{AI trained and saved data into file: }.$aitrained.$name);
|
||||||
|
debugLog ($paref, 'aiProcess', qq{Training instances and their associated information where purged from the AI object});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$serial = encode_base64 (Serialize ( {name => $name,
|
||||||
|
aitrainstate => "Train performed but not written -> $err",
|
||||||
|
aicanuse => 'ok'
|
||||||
|
}
|
||||||
|
), "");
|
||||||
|
|
||||||
|
$block ? return ($serial) : return \&finishTrain ($serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeTracking ($hash, $cst, 'runTimeTrainAI'); # Zyklus-Laufzeit ermitteln
|
||||||
|
|
||||||
|
$serial = encode_base64 (Serialize ( {name => $name,
|
||||||
|
aitrainstate => 'ok',
|
||||||
|
runTimeTrainAI => CurrentVal ($hash, 'runTimeTrainAI', ''),
|
||||||
|
aitrainLastFinishTs => int time,
|
||||||
|
aiRulesNumber => $rn, # Returns a list of strings that describe the tree in rule-form
|
||||||
|
aicanuse => 'ok'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, "");
|
||||||
|
|
||||||
|
delete $data{$name}{current}{runTimeTrainAI};
|
||||||
|
|
||||||
|
$block ? return ($serial) : return \&finishTrain ($serial);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
###############################################################
|
###############################################################
|
||||||
# Restaufgaben nach AI Train
|
# Restaufgaben nach AI Train
|
||||||
###############################################################
|
###############################################################
|
||||||
@ -17062,6 +17311,7 @@ sub finishTrain {
|
|||||||
my $aiRulesNumber = $paref->{aiRulesNumber};
|
my $aiRulesNumber = $paref->{aiRulesNumber};
|
||||||
|
|
||||||
delete $data{$name}{circular}{99}{aiRulesNumber};
|
delete $data{$name}{circular}{99}{aiRulesNumber};
|
||||||
|
delete $data{$name}{aidectree}{object};
|
||||||
|
|
||||||
$data{$name}{current}{aiAddedToTrain} = 0;
|
$data{$name}{current}{aiAddedToTrain} = 0;
|
||||||
$data{$name}{current}{aicanuse} = $aicanuse;
|
$data{$name}{current}{aicanuse} = $aicanuse;
|
||||||
@ -17106,6 +17356,7 @@ sub abortTrain {
|
|||||||
Log3 ($name, 1, "$name -> BlockingCall $hash->{HELPER}{AIBLOCKRUNNING}{fn} pid:$hash->{HELPER}{AIBLOCKRUNNING}{pid} aborted: $cause");
|
Log3 ($name, 1, "$name -> BlockingCall $hash->{HELPER}{AIBLOCKRUNNING}{fn} pid:$hash->{HELPER}{AIBLOCKRUNNING}{pid} aborted: $cause");
|
||||||
|
|
||||||
delete($hash->{HELPER}{AIBLOCKRUNNING});
|
delete($hash->{HELPER}{AIBLOCKRUNNING});
|
||||||
|
delete $data{$name}{aidectree}{object};
|
||||||
|
|
||||||
$data{$name}{current}{aitrainstate} = 'Traing (Child) process timed out';
|
$data{$name}{current}{aitrainstate} = 'Traing (Child) process timed out';
|
||||||
$data{$name}{current}{aiAddedToTrain} = 0;
|
$data{$name}{current}{aiAddedToTrain} = 0;
|
||||||
@ -17113,157 +17364,6 @@ sub abortTrain {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
################################################################
|
|
||||||
# KI Instanz(en) aus Raw Daten Hash erzeugen
|
|
||||||
################################################################
|
|
||||||
sub aiAddInstancePV { ## no critic "not used"
|
|
||||||
my $paref = shift;
|
|
||||||
my $name = $paref->{name};
|
|
||||||
my $type = $paref->{type};
|
|
||||||
my $taa = $paref->{taa}; # do train after add
|
|
||||||
|
|
||||||
my $hash = $defs{$name};
|
|
||||||
|
|
||||||
return if(!isPrepared4AI ($hash));
|
|
||||||
|
|
||||||
my $err = aiInit ($paref);
|
|
||||||
return if($err);
|
|
||||||
my $dtree = AiDetreeVal ($hash, 'object', undef);
|
|
||||||
|
|
||||||
$data{$name}{current}{aiAddedToTrain} = 0;
|
|
||||||
|
|
||||||
for my $idx (sort keys %{$data{$name}{aidectree}{airaw}}) {
|
|
||||||
next if(!$idx);
|
|
||||||
|
|
||||||
my $pvrl = AiRawdataVal ($hash, $idx, 'pvrl', undef);
|
|
||||||
next if(!defined $pvrl);
|
|
||||||
|
|
||||||
my $hod = AiRawdataVal ($hash, $idx, 'hod', undef);
|
|
||||||
next if(!defined $hod);
|
|
||||||
|
|
||||||
my $rad1h = AiRawdataVal ($hash, $idx, 'rad1h', 0);
|
|
||||||
next if($rad1h <= 0);
|
|
||||||
|
|
||||||
my $temp = AiRawdataVal ($hash, $idx, 'temp', undef);
|
|
||||||
my $wcc = AiRawdataVal ($hash, $idx, 'wcc', undef);
|
|
||||||
my $rr1c = AiRawdataVal ($hash, $idx, 'rr1c', undef);
|
|
||||||
my $sunalt = AiRawdataVal ($hash, $idx, 'sunalt', 0);
|
|
||||||
|
|
||||||
my $tbin = temp2bin ($temp) if(defined $temp);
|
|
||||||
my $cbin = cloud2bin ($wcc) if(defined $wcc);
|
|
||||||
my $sabin = sunalt2bin ($sunalt);
|
|
||||||
|
|
||||||
eval { $dtree->add_instance (attributes => { rad1h => $rad1h,
|
|
||||||
temp => $tbin,
|
|
||||||
wcc => $cbin,
|
|
||||||
rr1c => $rr1c,
|
|
||||||
sunalt => $sunalt,
|
|
||||||
hod => $hod
|
|
||||||
},
|
|
||||||
result => $pvrl
|
|
||||||
);
|
|
||||||
1;
|
|
||||||
}
|
|
||||||
or do { Log3 ($name, 1, "$name - aiAddInstancePV ERROR: $@");
|
|
||||||
$data{$name}{current}{aiaddistate} = $@;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
$data{$name}{current}{aiAddedToTrain}++;
|
|
||||||
|
|
||||||
debugLog ($paref, 'aiProcess', "AI Instance added $idx - hod: $hod, sunalt: $sunalt, rad1h: $rad1h, pvrl: $pvrl, wcc: ".(defined $wcc ? $wcc : '-').", rr1c: ".(defined $rr1c ? $rr1c : '-').", temp: ".(defined $temp ? $temp : '-'), 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
debugLog ($paref, 'aiProcess', "AI Instance add - ".$data{$name}{current}{aiAddedToTrain}." entities added for training ".(AttrVal ($name, 'verbose', 3) != 4 ? '(set verbose 4 for output more detail)' : ''));
|
|
||||||
|
|
||||||
$data{$name}{aidectree}{object} = $dtree;
|
|
||||||
$data{$name}{current}{aiaddistate} = 'ok';
|
|
||||||
|
|
||||||
if ($taa) {
|
|
||||||
manageTrain ($paref);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
################################################################
|
|
||||||
# KI trainieren
|
|
||||||
################################################################
|
|
||||||
sub aiTrain { ## no critic "not used"
|
|
||||||
my $paref = shift;
|
|
||||||
my $name = $paref->{name};
|
|
||||||
my $block = $paref->{block} // 0;
|
|
||||||
|
|
||||||
my $hash = $defs{$name};
|
|
||||||
my ($serial, $err);
|
|
||||||
|
|
||||||
if (!isPrepared4AI ($hash)) {
|
|
||||||
$err = CurrentVal ($hash, 'aicanuse', '');
|
|
||||||
$serial = encode_base64 (Serialize ( { name => $name,
|
|
||||||
aitrainstate => "Train: not performed -> $err",
|
|
||||||
aicanuse => $err
|
|
||||||
}
|
|
||||||
), "");
|
|
||||||
|
|
||||||
$block ? return ($serial) : return \&finishTrain ($serial);
|
|
||||||
}
|
|
||||||
|
|
||||||
my $cst = [gettimeofday]; # Zyklus-Startzeit
|
|
||||||
my $dtree = AiDetreeVal ($hash, 'object', undef);
|
|
||||||
|
|
||||||
if (!$dtree) {
|
|
||||||
$err = 'no AI::DecisionTree object present';
|
|
||||||
$serial = encode_base64 (Serialize ( {name => $name,
|
|
||||||
aitrainstate => "Train: not performed -> $err",
|
|
||||||
aiinitstate => "Init: $err",
|
|
||||||
aicanuse => 'ok'
|
|
||||||
}
|
|
||||||
), "");
|
|
||||||
$block ? return ($serial) : return \&finishTrain ($serial);
|
|
||||||
}
|
|
||||||
|
|
||||||
eval { $dtree->train();
|
|
||||||
1;
|
|
||||||
}
|
|
||||||
or do { Log3 ($name, 1, "$name - aiTrain ERROR: $@");
|
|
||||||
$err = (split / at/, $@)[0];
|
|
||||||
$serial = encode_base64 (Serialize ( {name => $name,
|
|
||||||
aitrainstate => "Train: $err",
|
|
||||||
aicanuse => 'ok'
|
|
||||||
}
|
|
||||||
), "");
|
|
||||||
|
|
||||||
$block ? return ($serial) : return \&finishTrain ($serial);
|
|
||||||
};
|
|
||||||
|
|
||||||
$data{$name}{aidectree}{aitrained} = $dtree;
|
|
||||||
$err = writeCacheToFile ($hash, 'aitrained', $aitrained.$name);
|
|
||||||
|
|
||||||
if (!$err) {
|
|
||||||
debugLog ($paref, 'aiProcess', qq{AI trained number of entities: }. $data{$name}{current}{aiAddedToTrain});
|
|
||||||
debugLog ($paref, 'aiProcess', qq{AI trained and saved data into file: }.$aitrained.$name);
|
|
||||||
debugLog ($paref, 'aiProcess', qq{Training instances and their associated information where purged from the AI object});
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeTracking ($hash, $cst, 'runTimeTrainAI'); # Zyklus-Laufzeit ermitteln
|
|
||||||
|
|
||||||
$serial = encode_base64 (Serialize ( {name => $name,
|
|
||||||
aitrainstate => 'ok',
|
|
||||||
runTimeTrainAI => CurrentVal ($hash, 'runTimeTrainAI', ''),
|
|
||||||
aitrainLastFinishTs => int time,
|
|
||||||
aiRulesNumber => scalar $dtree->rule_statements(), # Returns a list of strings that describe the tree in rule-form
|
|
||||||
aicanuse => 'ok'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
, "");
|
|
||||||
|
|
||||||
delete $data{$name}{current}{runTimeTrainAI};
|
|
||||||
|
|
||||||
$block ? return ($serial) : return \&finishTrain ($serial);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
# AI Ergebnis für ermitteln
|
# AI Ergebnis für ermitteln
|
||||||
################################################################
|
################################################################
|
||||||
@ -17278,11 +17378,8 @@ sub aiGetResult {
|
|||||||
|
|
||||||
return 'AI usage is not prepared' if(!isPrepared4AI ($hash, 'full'));
|
return 'AI usage is not prepared' if(!isPrepared4AI ($hash, 'full'));
|
||||||
|
|
||||||
my $dtree = AiDetreeVal ($hash, 'aitrained', undef);
|
my $objref = AiDetreeVal ($hash, 'aitrained', '');
|
||||||
|
return 'AI trained object is missed or not an ARRAY' if(ref $objref ne 'ARRAY');
|
||||||
if (!$dtree) {
|
|
||||||
return 'AI trained object is missed';
|
|
||||||
}
|
|
||||||
|
|
||||||
my $rad1h = NexthoursVal ($hash, $nhtstr, 'rad1h', 0);
|
my $rad1h = NexthoursVal ($hash, $nhtstr, 'rad1h', 0);
|
||||||
return "no rad1h for hod: $hod" if($rad1h <= 0);
|
return "no rad1h for hod: $hod" if($rad1h <= 0);
|
||||||
@ -17299,48 +17396,86 @@ sub aiGetResult {
|
|||||||
my $cbin = cloud2bin ($wcc);
|
my $cbin = cloud2bin ($wcc);
|
||||||
my $sabin = sunalt2bin ($sunalt);
|
my $sabin = sunalt2bin ($sunalt);
|
||||||
|
|
||||||
my $pvaifc;
|
my $new_data = { # Prognose für neue Daten
|
||||||
|
|
||||||
eval { $pvaifc = $dtree->get_result (attributes => { rad1h => $rad1h,
|
|
||||||
temp => $tbin,
|
|
||||||
wcc => $cbin,
|
|
||||||
rr1c => $rr1c,
|
|
||||||
sunalt => $sabin,
|
|
||||||
sunaz => $sunaz,
|
|
||||||
hod => $hod
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
if ($@) {
|
|
||||||
Log3 ($name, 1, "$name - aiGetResult ERROR: $@");
|
|
||||||
return $@;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (defined $pvaifc) {
|
|
||||||
debugLog ($paref, 'aiData', qq{AI accurate result found: pvaifc: $pvaifc (hod: $hod, sunaz: $sunaz, sunalt: $sabin, Rad1h: $rad1h, wcc: $wcc, rr1c: $rr1c, temp: $tbin)});
|
|
||||||
return ('accurate', $pvaifc);
|
|
||||||
}
|
|
||||||
|
|
||||||
(my $msg, $pvaifc) = _aiGetSpread ( { name => $name,
|
|
||||||
type => $type,
|
|
||||||
rad1h => $rad1h,
|
rad1h => $rad1h,
|
||||||
temp => $tbin,
|
temp => $tbin,
|
||||||
wcc => $cbin,
|
wcc => $cbin,
|
||||||
rr1c => $rr1c,
|
rr1c => $rr1c,
|
||||||
sunalt => $sabin,
|
sunalt => $sabin,
|
||||||
sunaz => $sunaz,
|
sunaz => $sunaz,
|
||||||
hod => $hod,
|
hod => $hod
|
||||||
dtree => $dtree,
|
};
|
||||||
debug => $paref->{debug}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (defined $pvaifc) {
|
## Accurate Decision
|
||||||
return ($msg, $pvaifc);
|
######################
|
||||||
|
my @total_prediction;
|
||||||
|
my $tn = 0;
|
||||||
|
|
||||||
|
for my $dtree (@{$objref}) {
|
||||||
|
$tn++;
|
||||||
|
my $res;
|
||||||
|
|
||||||
|
eval { $res = $dtree->get_result (attributes => $new_data);
|
||||||
|
push @total_prediction, $res if(defined $res);
|
||||||
|
1;
|
||||||
|
}
|
||||||
|
or do { Log3 ($name, 1, "$name - aiGetResult ERROR: $@");
|
||||||
|
return $@;
|
||||||
|
};
|
||||||
|
|
||||||
|
debugLog ($paref, 'aiData', "got AI result from Tree number $tn: $res") if(defined $res);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'No AI decition delivered';
|
my $tprnum = scalar @total_prediction;
|
||||||
|
|
||||||
|
if ($tprnum) {
|
||||||
|
my $avg_prediction = sprintf '%.0f', avgArray (\@total_prediction, $tprnum);
|
||||||
|
# my $avg_prediction = sprintf '%.0f', medianArray (\@total_prediction);
|
||||||
|
|
||||||
|
debugLog ($paref, 'aiData', qq{AI accurate result found: pvaifc: $avg_prediction (hod: $hod, sunaz: $sunaz, sunalt: $sabin, Rad1h: $rad1h, wcc: $wcc, rr1c: $rr1c, temp: $tbin)});
|
||||||
|
return ('accurate', $avg_prediction);
|
||||||
|
}
|
||||||
|
|
||||||
|
## Spread Decision
|
||||||
|
####################
|
||||||
|
# undef @total_prediction;
|
||||||
|
# $tn = 0;
|
||||||
|
|
||||||
|
# debugLog ($paref, 'aiData', qq{AI no accurate result found with initial value "Rad1h: $rad1h" (hod: $hod)});
|
||||||
|
|
||||||
|
# for my $dtree (@{$objref}) {
|
||||||
|
# $tn++;
|
||||||
|
|
||||||
|
# debugLog ($paref, 'aiData', "Start get AI spreaded result from Tree number $tn");
|
||||||
|
|
||||||
|
# my ($msg, $res) = _aiGetSpread ( { name => $name,
|
||||||
|
# rad1h => $rad1h,
|
||||||
|
# temp => $tbin,
|
||||||
|
# wcc => $cbin,
|
||||||
|
# rr1c => $rr1c,
|
||||||
|
# sunalt => $sabin,
|
||||||
|
# sunaz => $sunaz,
|
||||||
|
# hod => $hod,
|
||||||
|
# dtree => $dtree,
|
||||||
|
# debug => $paref->{debug}
|
||||||
|
# }
|
||||||
|
# );
|
||||||
|
|
||||||
|
# push @total_prediction, $res if($msg eq 'spreaded');
|
||||||
|
# }
|
||||||
|
|
||||||
|
# my $sprnum = scalar @total_prediction;
|
||||||
|
|
||||||
|
|
||||||
|
# if ($sprnum) {
|
||||||
|
# my $avg_prediction = sprintf '%.0f', avgArray (\@total_prediction, $sprnum);
|
||||||
|
# # my $avg_prediction = sprintf '%.0f', medianArray (\@total_prediction);
|
||||||
|
|
||||||
|
# debugLog ($paref, 'aiData', qq{AI spreaded result found: pvaifc: $avg_prediction (hod: $hod, sunaz: $sunaz, sunalt: $sabin, Rad1h: $rad1h, wcc: $wcc, rr1c: $rr1c, temp: $tbin)});
|
||||||
|
# return ('spreaded', $avg_prediction);
|
||||||
|
# }
|
||||||
|
|
||||||
|
return 'No AI decision delivered';
|
||||||
}
|
}
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
@ -17363,9 +17498,6 @@ sub _aiGetSpread {
|
|||||||
|
|
||||||
my ($pos, $neg, $p, $n);
|
my ($pos, $neg, $p, $n);
|
||||||
|
|
||||||
debugLog ($paref, 'aiData', qq{AI no accurate result found with initial value "Rad1h: $rad1h" (hod: $hod)});
|
|
||||||
debugLog ($paref, 'aiData', qq{AI test Rad1h variance "$dtn" and positive/negative spread with step size "$step"});
|
|
||||||
|
|
||||||
my $gra = {
|
my $gra = {
|
||||||
temp => $temp,
|
temp => $temp,
|
||||||
wcc => $wcc,
|
wcc => $wcc,
|
||||||
@ -17382,11 +17514,10 @@ sub _aiGetSpread {
|
|||||||
debugLog ($paref, 'aiData', qq{AI positive test value "Rad1h: $p"});
|
debugLog ($paref, 'aiData', qq{AI positive test value "Rad1h: $p"});
|
||||||
|
|
||||||
eval { $pos = $dtree->get_result (attributes => $gra);
|
eval { $pos = $dtree->get_result (attributes => $gra);
|
||||||
};
|
1;
|
||||||
|
|
||||||
if ($@) {
|
|
||||||
return $@;
|
|
||||||
}
|
}
|
||||||
|
or do { return $@;
|
||||||
|
};
|
||||||
|
|
||||||
if ($pos) {
|
if ($pos) {
|
||||||
debugLog ($paref, 'aiData', qq{AI positive tolerance hit: $pos Wh});
|
debugLog ($paref, 'aiData', qq{AI positive tolerance hit: $pos Wh});
|
||||||
@ -17402,11 +17533,10 @@ sub _aiGetSpread {
|
|||||||
debugLog ($paref, 'aiData', qq{AI negative test value "Rad1h: $n"});
|
debugLog ($paref, 'aiData', qq{AI negative test value "Rad1h: $n"});
|
||||||
|
|
||||||
eval { $neg = $dtree->get_result (attributes => $gra);
|
eval { $neg = $dtree->get_result (attributes => $gra);
|
||||||
};
|
1;
|
||||||
|
|
||||||
if ($@) {
|
|
||||||
return $@;
|
|
||||||
}
|
}
|
||||||
|
or do { return $@;
|
||||||
|
};
|
||||||
|
|
||||||
if ($neg) {
|
if ($neg) {
|
||||||
debugLog ($paref, 'aiData', qq{AI negative tolerance hit: $neg Wh});
|
debugLog ($paref, 'aiData', qq{AI negative tolerance hit: $neg Wh});
|
||||||
@ -17414,14 +17544,13 @@ sub _aiGetSpread {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
my $pvaifc = $pos && $neg ? sprintf "%.0f", (($pos + $neg) / 2) : undef;
|
my $result = $pos && $neg ? sprintf "%.0f", (($pos + $neg) / 2) : undef;
|
||||||
|
|
||||||
if (defined $pvaifc) {
|
if (defined $result) {
|
||||||
debugLog ($paref, 'aiData', qq{AI determined average result: pvaifc: $pvaifc Wh (hod: $hod, sunaz: $sunaz, sunalt: $sunalt, wcc: $wcc, rr1c: $rr1c, temp: $temp)});
|
return ('spreaded', $result);
|
||||||
return ('spreaded', $pvaifc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'No AI decition delivered';
|
return 'No AI decision delivered';
|
||||||
}
|
}
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
@ -17430,8 +17559,6 @@ return 'No AI decition delivered';
|
|||||||
sub aiInit { ## no critic "not used"
|
sub aiInit { ## no critic "not used"
|
||||||
my $paref = shift;
|
my $paref = shift;
|
||||||
my $name = $paref->{name};
|
my $name = $paref->{name};
|
||||||
my $type = $paref->{type};
|
|
||||||
|
|
||||||
my $hash = $defs{$name};
|
my $hash = $defs{$name};
|
||||||
|
|
||||||
if (!isPrepared4AI ($hash)) {
|
if (!isPrepared4AI ($hash)) {
|
||||||
@ -17444,17 +17571,15 @@ sub aiInit { ## no critic "not used"
|
|||||||
debugLog ($paref, 'aiProcess', $err);
|
debugLog ($paref, 'aiProcess', $err);
|
||||||
|
|
||||||
$data{$name}{current}{aiinitstate} = $err;
|
$data{$name}{current}{aiinitstate} = $err;
|
||||||
return $err;
|
return ($err);
|
||||||
}
|
}
|
||||||
|
|
||||||
my $dtree = new AI::DecisionTree ( verbose => 0, noise_mode => 'pick_best' );
|
my $dtree = new AI::DecisionTree ( verbose => 0, noise_mode => 'pick_best' );
|
||||||
|
|
||||||
$data{$name}{aidectree}{object} = $dtree;
|
|
||||||
$data{$name}{current}{aiinitstate} = 'ok';
|
$data{$name}{current}{aiinitstate} = 'ok';
|
||||||
|
|
||||||
Log3 ($name, 3, "$name - AI::DecisionTree initialized");
|
Log3 ($name, 3, "$name - AI::DecisionTree initialized");
|
||||||
|
|
||||||
return;
|
return ('', $dtree);
|
||||||
}
|
}
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
@ -21536,6 +21661,7 @@ return;
|
|||||||
# dayname - Tagesname (Kürzel)
|
# dayname - Tagesname (Kürzel)
|
||||||
# csmt${c} - Totalconsumption Consumer $c (1..MAXCONSUMER)
|
# csmt${c} - Totalconsumption Consumer $c (1..MAXCONSUMER)
|
||||||
# csme${c} - Consumption Consumer $c (1..MAXCONSUMER) in $hod
|
# csme${c} - Consumption Consumer $c (1..MAXCONSUMER) in $hod
|
||||||
|
# minutescsm${c} - Laufzeit des Consumers in Minuten in $hod
|
||||||
# sunaz - Azimuth der Sonne (in Dezimalgrad)
|
# sunaz - Azimuth der Sonne (in Dezimalgrad)
|
||||||
# sunalt - Höhe der Sonne (in Dezimalgrad)
|
# sunalt - Höhe der Sonne (in Dezimalgrad)
|
||||||
# $def: Defaultwert
|
# $def: Defaultwert
|
||||||
@ -23797,6 +23923,7 @@ to ensure that the system configuration is correct.
|
|||||||
<tr><td> <b>SunMinutes_Remain</b> </td><td>the remaining minutes until sunset of the current day </td></tr>
|
<tr><td> <b>SunMinutes_Remain</b> </td><td>the remaining minutes until sunset of the current day </td></tr>
|
||||||
<tr><td> <b>SunHours_Remain</b> </td><td>the remaining hours until sunset of the current day </td></tr>
|
<tr><td> <b>SunHours_Remain</b> </td><td>the remaining hours until sunset of the current day </td></tr>
|
||||||
<tr><td> <b>todayConsumption</b> </td><td>the energy consumption of the house on the current day </td></tr>
|
<tr><td> <b>todayConsumption</b> </td><td>the energy consumption of the house on the current day </td></tr>
|
||||||
|
<tr><td> <b>todayConsumptionForecastDay</b></td><td>Consumption forecast for the current day </td></tr>
|
||||||
<tr><td> <b>todayConsumptionForecast</b> </td><td>Consumption forecast per hour of the current day (01-24) </td></tr>
|
<tr><td> <b>todayConsumptionForecast</b> </td><td>Consumption forecast per hour of the current day (01-24) </td></tr>
|
||||||
<tr><td> <b>todayConForecastTillSunset</b> </td><td>Consumption forecast from current hour to hour before sunset </td></tr>
|
<tr><td> <b>todayConForecastTillSunset</b> </td><td>Consumption forecast from current hour to hour before sunset </td></tr>
|
||||||
<tr><td> <b>todayDoneAPIcalls</b> </td><td>the number of radiation data API calls executed on the current day </td></tr>
|
<tr><td> <b>todayDoneAPIcalls</b> </td><td>the number of radiation data API calls executed on the current day </td></tr>
|
||||||
@ -26301,6 +26428,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
|||||||
<tr><td> <b>SunMinutes_Remain</b> </td><td>die verbleibenden Minuten bis Sonnenuntergang des aktuellen Tages </td></tr>
|
<tr><td> <b>SunMinutes_Remain</b> </td><td>die verbleibenden Minuten bis Sonnenuntergang des aktuellen Tages </td></tr>
|
||||||
<tr><td> <b>SunHours_Remain</b> </td><td>die verbleibenden Stunden bis Sonnenuntergang des aktuellen Tages </td></tr>
|
<tr><td> <b>SunHours_Remain</b> </td><td>die verbleibenden Stunden bis Sonnenuntergang des aktuellen Tages </td></tr>
|
||||||
<tr><td> <b>todayConsumption</b> </td><td>der Energieverbrauch des Hauses am aktuellen Tag </td></tr>
|
<tr><td> <b>todayConsumption</b> </td><td>der Energieverbrauch des Hauses am aktuellen Tag </td></tr>
|
||||||
|
<tr><td> <b>todayConsumptionForecastDay</b></td><td>Verbrauchsprognose für den aktuellen Tag </td></tr>
|
||||||
<tr><td> <b>todayConsumptionForecast</b> </td><td>Verbrauchsprognose pro Stunde des aktuellen Tages (01-24) </td></tr>
|
<tr><td> <b>todayConsumptionForecast</b> </td><td>Verbrauchsprognose pro Stunde des aktuellen Tages (01-24) </td></tr>
|
||||||
<tr><td> <b>todayConForecastTillSunset</b> </td><td>Verbrauchsprognose von aktueller Stunde bis Stunde vor Sonnenuntergang </td></tr>
|
<tr><td> <b>todayConForecastTillSunset</b> </td><td>Verbrauchsprognose von aktueller Stunde bis Stunde vor Sonnenuntergang </td></tr>
|
||||||
<tr><td> <b>todayDoneAPIcalls</b> </td><td>die Anzahl der am aktuellen Tag ausgeführten Strahlungsdaten-API Calls </td></tr>
|
<tr><td> <b>todayDoneAPIcalls</b> </td><td>die Anzahl der am aktuellen Tag ausgeführten Strahlungsdaten-API Calls </td></tr>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user