76_SolarForecast: Version 1.54.4
git-svn-id: https://svn.fhem.de/fhem/trunk@30146 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
|
||||
# Do not insert empty lines here, update check depends on it
|
||||
- feature: 76_SolarForecast: Version 1.54.4
|
||||
- feature: 76_SolarForecast: ctrlDebug new collectData_long
|
||||
- change: 76_SolarForecast: version 1.54.2, more debug info
|
||||
- feature: 72_FRITZBOX: Vorbereitung auf Fritz!OS 8.10
|
||||
|
||||
@@ -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 Time::HiRes qw(gettimeofday tv_interval);
|
||||
use Math::Trig;
|
||||
use List::Util qw(min max shuffle);
|
||||
use List::Util qw(sum min max shuffle);
|
||||
use Scalar::Util qw(blessed weaken);
|
||||
|
||||
eval "use FHEM::Meta;1" or my $modMetaAbsent = 1; ## no critic 'eval'
|
||||
@@ -160,6 +160,11 @@ BEGIN {
|
||||
|
||||
# Versions History intern
|
||||
my %vNotesIntern = (
|
||||
"1.54.4" => "22.07.2025 replace length by new sub strlength, Consumer attr new key 'aliasshort', change code of medianArray ".
|
||||
"medianArray: can optional use newest 3..20 elements, avgArray: use the newest elements if num is set ".
|
||||
"Debug consumerSwitching: print out info message of compare operation, remove attr graphicShowDiff ".
|
||||
"store surpmeth calc result in key surpmethResult in Consumer master record, __readFileMessages: refactored code ".
|
||||
"surpmeth: use average[_2..20] instead of numeric values 2.20 only ",
|
||||
"1.54.3" => "19.07.2025 ctrlDebug: add collectData_long ",
|
||||
"1.54.2" => "18.07.2025 _createSummaries: add debug infos ",
|
||||
"1.54.1" => "08.07.2025 userExit: new coding, __createReduceIcon: fix Wide character in syswrite - https://forum.fhem.de/index.php?msg=1344368 ".
|
||||
@@ -1693,10 +1698,10 @@ sub Initialize {
|
||||
|
||||
### nicht mehr benötigte Daten verarbeiten - Bereich kann später wieder raus !!
|
||||
##########################################################################################################################
|
||||
my $av = 'obsolete#-#use#attr#graphicControl#instead';
|
||||
# my $av = 'obsolete#-#use#attr#graphicControl#instead';
|
||||
# my $av1 = 'obsolete#-#will#be#deleted#soon';
|
||||
# my $av2 = 'obsolete#-#use#attr#graphicSelect#instead';
|
||||
$hash->{AttrList} .= " graphicShowDiff:$av ";
|
||||
# $hash->{AttrList} .= " graphicShowDiff:$av ";
|
||||
##########################################################################################################################
|
||||
|
||||
$hash->{FW_hideDisplayName} = 1; # Forum 88667
|
||||
@@ -5886,7 +5891,7 @@ sub __updPreFile {
|
||||
return $err;
|
||||
}
|
||||
|
||||
if ($lencheck && length $remFile ne $cmlen) {
|
||||
if ($lencheck && length ($remFile) ne $cmlen) {
|
||||
$err = "update ERROR: length of $file is not $cmlen Bytes";
|
||||
Log3 ($name, 1, "$name - $err");
|
||||
return $err;
|
||||
@@ -5947,22 +5952,21 @@ sub __updWriteFile {
|
||||
my $content = shift;
|
||||
|
||||
my $fPath = "$root/$fName";
|
||||
my $err;
|
||||
|
||||
if (!open(FD, ">$fPath")) {
|
||||
$err = "update ERROR open $fPath failed: $!";
|
||||
return $err;
|
||||
open my $fh, '>:raw', $fPath or return "update ERROR open $fPath failed: $!";
|
||||
|
||||
my $bytes = encode ('UTF-8', $content);
|
||||
my $written = syswrite $fh, $bytes;
|
||||
close $fh or return "update ERROR closing $fPath failed: $!";
|
||||
|
||||
unless (defined $written) {
|
||||
return "update ERROR writing $fPath failed: $!";
|
||||
}
|
||||
|
||||
binmode(FD);
|
||||
print FD $content;
|
||||
close(FD);
|
||||
my $expected = length $bytes;
|
||||
|
||||
my $written = -s "$fPath";
|
||||
|
||||
if ($written != length $content) {
|
||||
$err = "update ERROR writing $fPath failed: $!";
|
||||
return $err;
|
||||
if ($written != $expected) {
|
||||
return sprintf "update ERROR wrote %d of %d bytes to %s", $written, $expected, $fPath;
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -5987,15 +5991,15 @@ sub Attr {
|
||||
|
||||
### nicht mehr benötigte Daten verarbeiten - Bereich kann später wieder raus !!
|
||||
######################################################################################################################
|
||||
if ($cmd eq 'set' && $aName =~ /^graphicShowDiff$/) { # 25.06.
|
||||
my $msg = "The attribute $aName is replaced by 'graphicControl'.";
|
||||
if (!$init_done) {
|
||||
Log3 ($name, 1, "$name - $msg");
|
||||
}
|
||||
else {
|
||||
return $msg;
|
||||
}
|
||||
}
|
||||
#if ($cmd eq 'set' && $aName =~ /^graphicShowDiff$/) { # 25.06.
|
||||
# my $msg = "The attribute $aName is replaced by 'graphicControl'.";
|
||||
# if (!$init_done) {
|
||||
# Log3 ($name, 1, "$name - $msg");
|
||||
# }
|
||||
# else {
|
||||
# return $msg;
|
||||
# }
|
||||
#}
|
||||
|
||||
#if ($cmd eq 'set' && $aName =~ /^graphicHeaderShow$/) { # 15.04.
|
||||
# my $msg = "The attribute $aName is replaced by 'graphicSelect'.";
|
||||
@@ -6085,6 +6089,7 @@ sub _attrconsumer { ## no critic "not used"
|
||||
my $hash = $defs{$name};
|
||||
|
||||
my $valid = {
|
||||
aliasshort => '',
|
||||
type => '',
|
||||
power => '',
|
||||
switchdev => '',
|
||||
@@ -6144,6 +6149,11 @@ sub _attrconsumer { ## no critic "not used"
|
||||
return qq{The key "exconfc" is not set correct. Please consider the command reference.};
|
||||
}
|
||||
|
||||
if (exists $h->{aliasshort}) { # Kurzalias
|
||||
return qq{The short alias "$h->{aliasshort}" longer than allowed. See command reference.}
|
||||
if(strlength ($h->{aliasshort})> 10);
|
||||
}
|
||||
|
||||
if (exists $h->{mode} && $h->{mode} !~ /^(?:can|must)$/xs) {
|
||||
if ($h->{mode} =~ /.*:.*/xs) {
|
||||
my ($dv, $rd) = split ':', $h->{mode};
|
||||
@@ -6170,8 +6180,8 @@ sub _attrconsumer { ## no critic "not used"
|
||||
return "The reading '$rd' of device '$dv' is invalid or doesn't contain a valid numeric value";
|
||||
}
|
||||
}
|
||||
elsif ($h->{surpmeth} !~ /^[2-9]$|^1[0-9]$|^20$|^median$|^default$/xs) {
|
||||
return qq{The surpmeth "$h->{surpmeth}" is wrong. It must contain a '<device>:<reading>', 'median', 'default' or an integer value of '2 .. 20'.};
|
||||
elsif ($h->{surpmeth} !~ /^(?:median|average)(?:_(?:[2-9]|1[0-9]|20))?$|^default$/xs) {
|
||||
return qq{The surpmeth "$h->{surpmeth}" is wrong. It must contain a '<device>:<reading>', 'median[_2..20]', 'average[_2..20]' or 'default'.};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8840,15 +8850,16 @@ sub centralTask {
|
||||
# ::CommandDeleteAttr (undef, "$name graphicBeamWidth");
|
||||
#}
|
||||
|
||||
my $gsd = AttrVal ($name, 'graphicShowDiff ', undef); # 25.06.
|
||||
my $gco = AttrVal ($name, 'graphicControl', '');
|
||||
for my $c (1..MAXCONSUMER) { # 23.07.
|
||||
$c = sprintf "%02d", $c;
|
||||
my $surpmeth = ConsumerVal ($hash, $c, 'surpmeth', '');
|
||||
|
||||
if (defined $gsd) {
|
||||
my $newval = $gco." showDiff=$gsd";
|
||||
CommandAttr (undef, "$name graphicControl $newval");
|
||||
::CommandDeleteAttr (undef, "$name graphicShowDiff");
|
||||
if ($surpmeth =~ /^[2-9]$|^1[0-9]$|^20$/xs) {
|
||||
fhem ("set $name attrKeyVal consumer${c} surpmeth=average_${surpmeth}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
##########################################################################################################################
|
||||
|
||||
if (!CurrentVal ($hash, 'allStringsFullfilled', 0)) { # die String Konfiguration erstellen wenn noch nicht erfolgreich ausgeführt
|
||||
@@ -9236,6 +9247,7 @@ sub _collectAllRegConsumers {
|
||||
|
||||
$data{$name}{consumers}{$c}{name} = $consumer; # Name des Verbrauchers (Device)
|
||||
$data{$name}{consumers}{$c}{alias} = $alias; # Alias des Verbrauchers (Device)
|
||||
$data{$name}{consumers}{$c}{aliasshort} = $hc->{aliasshort} // q{}; # Kurzalias des Verbrauchers
|
||||
$data{$name}{consumers}{$c}{type} = $hc->{type} // DEFCTYPE; # Typ des Verbrauchers
|
||||
$data{$name}{consumers}{$c}{power} = $hc->{power}; # Leistungsaufnahme des Verbrauchers in W
|
||||
$data{$name}{consumers}{$c}{avgenergy} = q{}; # Initialwert Energieverbrauch (evtl. Überschreiben in manageConsumerData)
|
||||
@@ -9315,10 +9327,10 @@ sub _specialActivities {
|
||||
##########################################################################
|
||||
|
||||
for my $c (keys %{$data{$name}{consumers}}) {
|
||||
next if(ConsumerVal ($hash, $c, "plandelete", "regular") eq "regular");
|
||||
next if(ConsumerVal ($hash, $c, 'plandelete', 'regular') eq 'regular');
|
||||
|
||||
my $planswitchoff = ConsumerVal ($hash, $c, "planswitchoff", $t);
|
||||
my $simpCstat = simplifyCstate (ConsumerVal ($hash, $c, "planstate", ""));
|
||||
my $planswitchoff = ConsumerVal ($hash, $c, 'planswitchoff', $t);
|
||||
my $simpCstat = simplifyCstate (ConsumerVal ($hash, $c, 'planstate', ''));
|
||||
|
||||
if ($t > $planswitchoff && $simpCstat =~ /planned|finished|unknown/xs) {
|
||||
deleteConsumerPlanning ($hash, $c);
|
||||
@@ -12003,8 +12015,8 @@ sub _manageConsumerData {
|
||||
|
||||
for my $c (sort{$a<=>$b} keys %{$data{$name}{consumers}}) {
|
||||
$paref->{consumer} = $c;
|
||||
my $consumer = ConsumerVal ($hash, $c, "name", "");
|
||||
my $alias = ConsumerVal ($hash, $c, "alias", "");
|
||||
my $consumer = ConsumerVal ($hash, $c, 'name', '');
|
||||
my $alias = ConsumerVal ($hash, $c, 'alias', '');
|
||||
|
||||
## aktuelle Leistung auslesen
|
||||
##############################
|
||||
@@ -12083,15 +12095,15 @@ sub _manageConsumerData {
|
||||
|
||||
$paref->{pcurr} = $pcurr;
|
||||
|
||||
__getAutomaticState ($paref); # Automatic Status des Consumers abfragen
|
||||
__calcEnergyPieces ($paref); # Energieverbrauch auf einzelne Stunden für Planungsgrundlage aufteilen
|
||||
__planInitialSwitchTime ($paref); # Consumer Switch Zeiten planen
|
||||
__setTimeframeState ($paref); # Timeframe Status ermitteln
|
||||
__setConsRcmdState ($paref); # Consumption Recommended Status setzen
|
||||
__switchConsumer ($paref); # Consumer schalten
|
||||
__getCyclesAndRuntime ($paref); # Verbraucher - Laufzeit, Tagesstarts und Aktivminuten pro Stunde ermitteln
|
||||
__reviewSwitchTime ($paref); # Planungsdaten überprüfen und ggf. neu planen
|
||||
__remainConsumerTime ($paref); # Restlaufzeit Verbraucher ermitteln
|
||||
__getAutomaticState ($paref); # Automatic Status des Consumers abfragen
|
||||
__calcEnergyPieces ($paref); # Energieverbrauch auf einzelne Stunden für Planungsgrundlage aufteilen
|
||||
__planInitialSwitchTime ($paref); # Consumer Switch Zeiten planen
|
||||
__setTimeframeState ($paref); # Timeframe Status ermitteln
|
||||
__setConsRcmdState ($paref); # Consumption Recommended Status setzen
|
||||
__switchConsumer ($paref); # Consumer schalten
|
||||
__getCyclesAndRuntime ($paref); # Verbraucher - Laufzeit, Tagesstarts und Aktivminuten pro Stunde ermitteln
|
||||
__reviewSwitchTime ($paref); # Planungsdaten überprüfen und ggf. neu planen
|
||||
__remainConsumerTime ($paref); # Restlaufzeit Verbraucher ermitteln
|
||||
|
||||
delete $paref->{pcurr};
|
||||
|
||||
@@ -12987,16 +12999,23 @@ sub __setConsRcmdState {
|
||||
|
||||
my ($method, $surplus) = determSurplus ($hash, $c); # Consumer spezifische Ermittlung des Energieüberschußes
|
||||
|
||||
$data{$name}{consumers}{$c}{surpmethResult} = $surplus; # Ergebnis der Surplus Ermittlung im Consumerstammsatz speichern, Forum: https://forum.fhem.de/index.php?msg=1345058
|
||||
|
||||
if ($debug =~ /consumerSwitching${c}/x) {
|
||||
my $splref = CurrentVal ($name, 'surplusslidereg', '.');
|
||||
my $spser = ref $splref eq 'ARRAY' ? join ' ', @{$splref} : undef;
|
||||
|
||||
Log3 ($name, 1, qq{$name DEBUG> ############### consumerSwitching consumer "$c" ###############});
|
||||
Log3 ($name, 1, qq{$name DEBUG> consumer "$c" - ConsumptionRecommended calc method: $method, surplus: }.
|
||||
(defined $surplus ? $surplus : 'undef'));
|
||||
Log3 ($name, 1, qq{$name DEBUG> consumer "$c" - method base: $spser}) if($method =~ /average|median/xs);
|
||||
Log3 ($name, 1, qq{$name DEBUG> consumer "$c" - additional consumption after switching on (if currently 'off'): $rescons W});
|
||||
}
|
||||
|
||||
my ($spignore, $info, $err) = isSurplusIgnoCond ($hash, $c, $debug); # PV Überschuß ignorieren?
|
||||
|
||||
Log3 ($name, 1, "$name - $err") if($err);
|
||||
|
||||
if ($debug =~ /consumerSwitching${c}/x && $info) {
|
||||
Log3 ($name, 1, qq{$name DEBUG> consumer "$c" - IgnoreCondition - $info});
|
||||
}
|
||||
@@ -13100,8 +13119,8 @@ sub ___switchConsumerOn {
|
||||
Log3 ($name, 1, qq{$name DEBUG> consumer "$c" - Check Context 'switch on' => }.
|
||||
qq{swoncond: $swoncond, on-command: $oncom }
|
||||
);
|
||||
Log3 ($name, 1, qq{$name DEBUG> consumer "$c" - isAddSwitchOnCond Info: $infon}) if($swoncond && $infon);
|
||||
Log3 ($name, 1, qq{$name DEBUG> consumer "$c" - isAddSwitchOffCond Info: $infoff}) if($swoffcond && $infoff);
|
||||
Log3 ($name, 1, qq{$name DEBUG> consumer "$c" - isAddSwitchOnCond Info: $infon}) if($infon);
|
||||
Log3 ($name, 1, qq{$name DEBUG> consumer "$c" - isAddSwitchOffCond Info: $infoff}) if($infoff);
|
||||
Log3 ($name, 1, qq{$name DEBUG> consumer "$c" - device '$dswname' is used as switching device});
|
||||
|
||||
if ($simpCstat =~ /planned|priority|starting|continuing/xs && $isInTime && $iilt) {
|
||||
@@ -13230,7 +13249,7 @@ sub ___switchConsumerOff {
|
||||
qq{swoffcond: $swoffcond, off-command: $offcom}
|
||||
);
|
||||
Log3 ($name, 1, qq{$name DEBUG> consumer "$c" - is Consumption recommended: $isConsRcmd});
|
||||
Log3 ($name, 1, qq{$name DEBUG> consumer "$c" - isAddSwitchOffCond Info: $infoff}) if($swoffcond && $infoff);
|
||||
Log3 ($name, 1, qq{$name DEBUG> consumer "$c" - isAddSwitchOffCond Info: $infoff}) if($infoff);
|
||||
|
||||
if ($stopts && $t >= $stopts && $iilt) {
|
||||
Log3 ($name, 1, qq{$name DEBUG> consumer "$c" - switching off postponed by >isInLocktime<});
|
||||
@@ -13678,7 +13697,6 @@ sub _calcConsForecast_circular {
|
||||
$hnum = scalar @conh;
|
||||
}
|
||||
|
||||
# my $hcon = sprintf "%.0f", medianArray (\@conh);
|
||||
my $hcon = $ncds <= $nhist ? (sprintf "%.0f", avgArray (\@conh, $hnum)) :
|
||||
(sprintf "%.0f", medianArray (\@conh)); # V 1.52.8
|
||||
$usage{$hh}{con} = $hcon; # prognostizierter Verbrauch (Median) der Stunde hh (Hour of Day)
|
||||
@@ -13691,7 +13709,6 @@ sub _calcConsForecast_circular {
|
||||
$hnumtom = scalar @conhtom;
|
||||
}
|
||||
|
||||
# my $hcontom = sprintf "%.0f", medianArray (\@conhtom);
|
||||
my $hcontom = $ncds <= $nhist ? (sprintf "%.0f", avgArray (\@conhtom, $hnumtom)) :
|
||||
(sprintf "%.0f", medianArray (\@conhtom)); # V 1.52.8
|
||||
$usage{tom}{con} += $hcontom; # Summe prognostizierter Verbrauch (Median) des Tages
|
||||
@@ -17746,7 +17763,7 @@ sub _flowGraphic {
|
||||
my $flowgxshift = $paref->{flowgxshift}; # X-Verschiebung der Flußgrafikbox (muß negiert werden)
|
||||
my $flowgyshift = $paref->{flowgyshift}; # Y-Verschiebung der Flußgrafikbox (muß negiert werden)
|
||||
my $flowgconsumer = $paref->{flowgconsumer}; # Verbraucher in der Energieflußgrafik anzeigen
|
||||
my $flowgconsTime = $paref->{flowgconsTime}; # Verbraucher Restlaufeit in der Energieflußgrafik anzeigen
|
||||
my $flowgconsTime = $paref->{flowgconsTime}; # Verbraucher Restlaufzeit in der Energieflußgrafik anzeigen
|
||||
my $flowgconX = $paref->{flowgconX};
|
||||
my $flowgconsPower = $paref->{flowgconsPower};
|
||||
my $cdist = $paref->{flowgconsDist}; # Abstand Consumer zueinander
|
||||
@@ -17764,7 +17781,7 @@ sub _flowGraphic {
|
||||
my $stna = $name;
|
||||
$stna .= int (rand (1500));
|
||||
|
||||
my ($y_pos, $y_pos1, $err);
|
||||
my ($y_pos, $y_pos1, $y_pos2, $err);
|
||||
|
||||
for my $re (keys %hrepl) { # V 1.37.1 Ziffern etc. eliminieren, Forum: https://forum.fhem.de/index.php?msg=1323229
|
||||
$stna =~ s/$re/$hrepl{$re}/gxs;
|
||||
@@ -17913,18 +17930,24 @@ sub _flowGraphic {
|
||||
|
||||
## definierte Verbraucher ermitteln
|
||||
#####################################
|
||||
my $cnsmr = {}; # Hashref Consumer current power
|
||||
my $cnsmr = {}; # Consumer Hilfshash Referenz
|
||||
|
||||
for my $c (sort{$a<=>$b} keys %{$data{$name}{consumers}}) { # definierte Verbraucher ermitteln
|
||||
next if(isConsumerNoshow ($hash, $c) =~ /[13]/xs); # auszublendende Consumer nicht berücksichtigen
|
||||
$cnsmr->{$c}{p} = ReadingsNum ($name, "consumer${c}_currentPower", 0);
|
||||
$cnsmr->{$c}{ptyp} = 'consumer';
|
||||
for my $c (sort{$a<=>$b} keys %{$data{$name}{consumers}}) { # definierte Verbraucher ermitteln
|
||||
next if(isConsumerNoshow ($hash, $c) =~ /[13]/xs); # auszublendende Consumer nicht berücksichtigen
|
||||
$cnsmr->{$c}{p} = ReadingsNum ($name, "consumer${c}_currentPower", 0);
|
||||
$cnsmr->{$c}{shortalias} = ConsumerVal ($name, $c, 'aliasshort', ''); # Consumer Kurzalias
|
||||
$cnsmr->{$c}{ptyp} = 'consumer';
|
||||
}
|
||||
|
||||
my $consumercount = keys %{$cnsmr};
|
||||
$flowgconsumer = 0 if(!$consumercount); # Consumer Anzeige ausschalten wenn keine Consumer definiert
|
||||
$flowgconsumer = 0 if(!$consumercount); # Consumer Anzeige ausschalten wenn keine Consumer definiert
|
||||
my @consumers = sort{$a<=>$b} keys %{$cnsmr};
|
||||
|
||||
my $total_shortalias_length = sum map { my $a = $_->{shortalias} // '';
|
||||
strlength ($a); # Länge in Zeichen nach Zeichen-Dekodierung
|
||||
}
|
||||
values %{$cnsmr};
|
||||
|
||||
|
||||
## Producer / Inverter Koordinaten Steuerhash
|
||||
###############################################
|
||||
@@ -17951,8 +17974,9 @@ sub _flowGraphic {
|
||||
$vbminy -= 150 if($showproducers); # mehr Platz oben schaffen wenn Poducerreihe angezeigt
|
||||
$vbminy -= INPUTROWSHIFT if($showgenerators); # mehr Platz oben schaffen wenn Zellen/Input-Reihe angezeigt
|
||||
|
||||
my $vbhight = 610;
|
||||
my $vbhight = 630;
|
||||
$vbhight -= 20 if(!$flowgconsTime);
|
||||
$vbhight -= 20 if(!$total_shortalias_length);
|
||||
$vbhight -= 230 if(!$flowgconsumer);
|
||||
|
||||
$vbhight += PRDCRROWSHIFT if($showproducers); # Höhe Box vergrößern wenn Poducerreihe angezeigt
|
||||
@@ -18329,7 +18353,7 @@ END3
|
||||
$ytext = $showproducers ? $ytext - PRDCRROWSHIFT + 5 : $ytext + PRDCRROWSHIFT - 30; # Unterscheidung wenn ProducerZeile angezeigt werden soll
|
||||
|
||||
my $genpow = __getGeneratorPower ( { pdcr => $pdcr, lfn => $lfn } ); # aktuelle Generatorleistung
|
||||
my $lpv1 = length $genpow;
|
||||
my $lpv1 = strlength ($genpow);
|
||||
|
||||
# Leistungszahl abhängig von der Größe entsprechend auf der x-Achse verschieben
|
||||
#################################################################################
|
||||
@@ -18359,7 +18383,7 @@ END3
|
||||
|
||||
$xtext = $xtext * 2 - 70; # Korrektur Start X-Koordinate des Textes
|
||||
my $pdrpow = __getProducerPower ( { pdcr => $pdcr, lfn => $lfn } );
|
||||
my $lpv1 = length $pdrpow;
|
||||
my $lpv1 = strlength ($pdrpow);
|
||||
|
||||
# Leistungszahl abhängig von der Größe entsprechend auf der x-Achse verschieben
|
||||
###############################################################################
|
||||
@@ -18378,10 +18402,28 @@ END3
|
||||
########################
|
||||
if ($flowgconsumer) {
|
||||
$cons_left = ($consumer_start * 2) - 50; # -XX -> Start Lage Consumer Beschriftung
|
||||
$y_pos = 1110 + 2 * $exth2cdist;
|
||||
$y_pos1 = 1170 + 2 * $exth2cdist;
|
||||
|
||||
my %offset = (
|
||||
0b00 => 0, # weder Power noch Time
|
||||
0b01 => 60, # nur Power
|
||||
0b10 => 60, # nur Time
|
||||
0b11 => 120, # beide
|
||||
);
|
||||
|
||||
my $y_base = 1110 + 2 * $exth2cdist;
|
||||
$y_pos = $y_base; # flowgconsPower
|
||||
my $mask = $flowgconsPower ? 1 : 0;
|
||||
$y_pos1 = $y_base + $offset{$mask}; # flowgconsTime
|
||||
|
||||
$mask = $flowgconsPower && $flowgconsTime ? 3 :
|
||||
$flowgconsTime ? 2 :
|
||||
$flowgconsPower ? 1 :
|
||||
0;
|
||||
|
||||
$y_pos2 = $y_base + $offset{$mask}; # shortalias
|
||||
|
||||
for my $c (@consumers) {
|
||||
my $shortalias = $cnsmr->{$c}{shortalias} // '';
|
||||
$cnsmrpower = sprintf "%.1f", $cnsmr->{$c}{p};
|
||||
$cnsmrpower = sprintf "%.0f", $cnsmrpower if($cnsmrpower > 10);
|
||||
my $consumerTime = ConsumerVal ($name, $c, 'remainTime', ''); # Restlaufzeit
|
||||
@@ -18391,31 +18433,54 @@ END3
|
||||
$cnsmrpower = isConsumerPhysOn($hash, $c) ? 'on' : 'off';
|
||||
}
|
||||
|
||||
my $lcp = length $cnsmrpower;
|
||||
my $lcp = strlength ($cnsmrpower);
|
||||
my $lct = strlength ($consumerTime);
|
||||
my $lcs = strlength ($shortalias);
|
||||
|
||||
#$ret .= qq{<text class="$stna text" id="consumertxt_${c}_$stna" x="$cons_left" y="1110" style="text-anchor: start;">$cnsmrpower</text>} if($flowgconsPower); # Lage Consumer Consumption
|
||||
#$ret .= qq{<text class="$stna text" id="consumertxt_time_${c}_$stna" x="$cons_left" y="1170" style="text-anchor: start;">$consumerTime</text>} if($flowgconsTime); # Lage Consumer Restlaufzeit
|
||||
# Texte abhängig von ihrer Größe entsprechend auf der x-Achse verschieben
|
||||
###########################################################################
|
||||
if ($flowgconsPower) { # Lage Consumer Consumption
|
||||
my $lcp_cons_left = $cons_left;
|
||||
|
||||
# Verbrauchszahl abhängig von der Größe entsprechend auf der x-Achse verschieben
|
||||
##################################################################################
|
||||
if ($lcp >= 5) {$cons_left -= 40}
|
||||
elsif ($lcp == 4) {$cons_left -= 25}
|
||||
elsif ($lcp == 3) {$cons_left -= 5 }
|
||||
elsif ($lcp == 2) {$cons_left += 7 }
|
||||
elsif ($lcp == 1) {$cons_left += 25}
|
||||
if ($lcp >= 5) {$lcp_cons_left -= 40}
|
||||
elsif ($lcp == 4) {$lcp_cons_left -= 25}
|
||||
elsif ($lcp == 3) {$lcp_cons_left -= 5 }
|
||||
elsif ($lcp == 2) {$lcp_cons_left += 7 }
|
||||
elsif ($lcp == 1) {$lcp_cons_left += 25}
|
||||
|
||||
$ret .= qq{<text class="$stna text" id="consumertxt_${c}_$stna" x="$cons_left" y="$y_pos">$cnsmrpower</text>} if($flowgconsPower); # Lage Consumer Consumption
|
||||
$ret .= qq{<text class="$stna text" id="consumertxt_time_${c}_$stna" x="$cons_left" y="$y_pos1">$consumerTime</text>} if($flowgconsTime); # Lage Consumer Restlaufzeit
|
||||
$ret .= qq{<text class="$stna text" id="consumertxt_${c}_$stna" x="$lcp_cons_left" y="$y_pos">$cnsmrpower</text>};
|
||||
}
|
||||
|
||||
# Verbrauchszahl wieder zurück an den Ursprungspunkt
|
||||
######################################################
|
||||
if ($lcp >= 5) {$cons_left += 40}
|
||||
elsif ($lcp == 4) {$cons_left += 25}
|
||||
elsif ($lcp == 3) {$cons_left += 5 }
|
||||
elsif ($lcp == 2) {$cons_left -= 7 }
|
||||
elsif ($lcp == 1) {$cons_left -= 25}
|
||||
if ($flowgconsTime) { # Lage Consumer Restlaufzeit
|
||||
my $lct_cons_left = $cons_left;
|
||||
|
||||
$cons_left += ($cdist * 2);
|
||||
if ($lct >= 5) {$lct_cons_left -= 40}
|
||||
elsif ($lct == 4) {$lct_cons_left -= 25}
|
||||
elsif ($lct == 3) {$lct_cons_left -= 5 }
|
||||
elsif ($lct == 2) {$lct_cons_left += 7 }
|
||||
elsif ($lct == 1) {$lct_cons_left += 25}
|
||||
|
||||
$ret .= qq{<text class="$stna text" id="consumertxt_time_${c}_$stna" x="$lct_cons_left" y="$y_pos1">$consumerTime</text>};
|
||||
}
|
||||
|
||||
if ($shortalias) { # Lage Consumer Kurzalias
|
||||
my $lcs_cons_left = $cons_left;
|
||||
|
||||
if ($lcs >= 10) {$lcs_cons_left -= 85}
|
||||
elsif ($lcs == 9) {$lcs_cons_left -= 85}
|
||||
elsif ($lcs == 8) {$lcs_cons_left -= 70}
|
||||
elsif ($lcs == 7) {$lcs_cons_left -= 60}
|
||||
elsif ($lcs == 6) {$lcs_cons_left -= 35}
|
||||
elsif ($lcs == 5) {$lcs_cons_left -= 20}
|
||||
elsif ($lcs == 4) {$lcs_cons_left -= 10}
|
||||
elsif ($lcs == 3) {$lcs_cons_left -= 0 }
|
||||
elsif ($lcs == 2) {$lcs_cons_left += 7 }
|
||||
elsif ($lcs == 1) {$lcs_cons_left += 25}
|
||||
|
||||
$ret .= qq{<text class="$stna text" id="consumertxtalias_${c}_$stna" x="$lcs_cons_left" y="$y_pos2">$shortalias</text>};
|
||||
}
|
||||
|
||||
$cons_left += ($cdist * 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19263,26 +19328,31 @@ sub __readFileMessages {
|
||||
my $name = $paref->{name};
|
||||
my $tsnext = $paref->{tsnext};
|
||||
|
||||
my $hash = $defs{$name};
|
||||
my $file = "$root/FHEM/$messagefile";
|
||||
|
||||
open (FD, "$root/FHEM/$messagefile") or do { return $! };
|
||||
open my $fh, '<:encoding(UTF-8)', $file or return "Cannot open $file: $!";
|
||||
|
||||
delete $data{$name}{filemessages};
|
||||
|
||||
my @locList = map { $_ =~ s/[\r\n]//; $_ } <FD>;
|
||||
close (FD);
|
||||
my $count = 0;
|
||||
|
||||
Log3 ($name, 4, "$name - Notification System - read local Message File >$messagefile< with ".scalar @locList." entries.");
|
||||
while (my $line = <$fh>) {
|
||||
chomp $line;
|
||||
next if $line =~ /^\s*#/; # Kommentarzeilen überspringen
|
||||
|
||||
for my $l (@locList) {
|
||||
next if ($l =~ /^\#/xs);
|
||||
my @l = split /\|/, $l, 3;
|
||||
next if(!isNumeric ($l[0]));
|
||||
next if($l[1] !~ /^(DE|EN|SV)$/xs);
|
||||
my ($id, $lang, $msg) = split /\|/, $line, 3;
|
||||
next if(!isNumeric ($id)); # nur numeric IDs
|
||||
next if($lang !~ /^(DE|EN|SV)$/xs); # nur gültige Sprachen
|
||||
|
||||
$data{$name}{filemessages}{$l[0]}{$l[1]} = $l[2];
|
||||
$data{$name}{filemessages}{$id}{$lang} = $msg;
|
||||
$count++;
|
||||
}
|
||||
|
||||
close $fh;
|
||||
|
||||
Log3 ($name, 4, "$name - Notification System - read local Message File >$messagefile< with $count entries.");
|
||||
|
||||
|
||||
$data{$name}{filemessages}{999000}{TS} = time;
|
||||
$data{$name}{filemessages}{999000}{TSNEXT} = $tsnext;
|
||||
|
||||
@@ -19863,7 +19933,6 @@ sub aiGetResult {
|
||||
|
||||
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);
|
||||
@@ -21161,11 +21230,11 @@ sub _ldchash2val {
|
||||
|
||||
if (ref $pool->{$idx}{$key}{$f} eq 'ARRAY') {
|
||||
my @sub_arrays = arraySplitBy (20, @{$pool->{$idx}{$key}{$f}}); # Array in Teil-Arrays zu je 20 Elemente aufteilen
|
||||
my $ln0 = length $key;
|
||||
my $ln0 = strlength ($key);
|
||||
my $blk0 = ' ' x 17;
|
||||
my $blkadd0 = ' ' x (7 - ($ln0 > 7 ? 0 : $ln0));
|
||||
|
||||
my $ln1 = length $f;
|
||||
my $ln1 = strlength ($f);
|
||||
my $blkadd1 = ' ' x (3 - ($ln1 > 3 ? 0 : $ln1));
|
||||
|
||||
for my $suaref (@sub_arrays) { # für jedes Teil-Array Join ausführen
|
||||
@@ -21226,7 +21295,7 @@ sub _ldpspaces {
|
||||
my $sp = shift // q{};
|
||||
my $const = shift // 4;
|
||||
|
||||
my $le = $const + length Encode::decode('UTF-8', $str);
|
||||
my $le = $const + strlength ($str);
|
||||
my $spn = $sp;
|
||||
|
||||
for (my $i = 0; $i < $le; $i++) {
|
||||
@@ -21990,18 +22059,20 @@ sub determSurplus {
|
||||
|
||||
my ($surplus, $fallback);
|
||||
|
||||
if ($surpmeth eq 'median') { # Median der Werte in surplusslidereg, !kann UNDEF sein!
|
||||
$surplus = medianArray ($splref);
|
||||
$method = 'median';
|
||||
if ($surpmeth =~ /median/xs) { # Median der Werte in surplusslidereg, !kann UNDEF sein!
|
||||
my $num = (split '_', $surpmeth)[1]; # Anzahl der (letzten) Array-Elemente die für Median verwendet werden sollen
|
||||
$surplus = medianArray ($splref, $num);
|
||||
$method = $num ? "median:$num" : "median:all";
|
||||
}
|
||||
elsif ($surpmeth =~ /average/xs) { # Average Ermittlung, !kann UNDEF sein!
|
||||
my $num = (split '_', $surpmeth)[1];
|
||||
$surplus = avgArray ($splref, $num);
|
||||
$method = $num ? "average:$num" : "average:all";
|
||||
}
|
||||
elsif ($surpmeth eq 'default') { # aktueller Energieüberschuß
|
||||
$surplus = CurrentVal ($hash, 'surplus', 0);
|
||||
$method = 'default';
|
||||
}
|
||||
elsif ($surpmeth =~ /^[2-9]$|^1[0-9]$|^20$/xs) {
|
||||
$surplus = avgArray ($splref, $surpmeth); # Average Ermittlung, !kann UNDEF sein!
|
||||
$method = "average:$surpmeth";
|
||||
}
|
||||
elsif ($surpmeth =~ /.*:.*/xs) {
|
||||
my ($dv, $rd) = split ':', $surpmeth;
|
||||
$method = "$dv:$rd";
|
||||
@@ -22089,11 +22160,10 @@ sub avgArray {
|
||||
|
||||
return undef if(ref $aref ne 'ARRAY' || scalar @{$aref} < $num);
|
||||
|
||||
my $sum = 0;
|
||||
my @tail = @{$aref}[-$num .. -1]; # es werden die neuesten num Elemente verwendet
|
||||
|
||||
for my $i (0 .. $num-1) {
|
||||
$sum += ${$aref}[$i];
|
||||
}
|
||||
my $sum = 0;
|
||||
$sum += $_ for @tail;
|
||||
|
||||
my $avg = $sum / $num;
|
||||
|
||||
@@ -22105,24 +22175,28 @@ return $avg;
|
||||
# (https://www.ionos.de/digitalguide/online-marketing/web-analyse/median-berechnen/)
|
||||
#
|
||||
# $aref = Referenz zum Array
|
||||
# $num = Anzahl der neuesten zu verwendenden Array Elemente
|
||||
#
|
||||
######################################################################################
|
||||
sub medianArray {
|
||||
my $aref = shift;
|
||||
my $num = shift;
|
||||
|
||||
return undef if(ref $aref ne 'ARRAY' || !scalar @{$aref});
|
||||
return if(ref $aref ne 'ARRAY' || !scalar @{$aref});
|
||||
|
||||
my $enum = scalar @{$aref}; # Anzahl der Elemente im Array
|
||||
my @sorted = sort { $a <=> $b } @{$aref}; # Numerisch aufsteigend
|
||||
|
||||
if ($enum % 2) { # Array enthält ungerade Anzahl Elemente
|
||||
return $sorted[$enum/2]; # ungerade Elemente -> Median Element steht in der Mitte von @sorted
|
||||
}
|
||||
else {
|
||||
return ($sorted[$enum/2 - 1] + $sorted[$enum/2]) / 2; # gerade Elemente -> Median ist der Durchschnitt der beiden mittleren Elemente
|
||||
if (defined $num) { # Anzahl der (neuesten) Elemente die verwendet werden sollen
|
||||
return unless $num =~ /^\d+$/ && $num > 0 && $num <= @$aref;
|
||||
}
|
||||
|
||||
return;
|
||||
my @tail = defined $num ? @{$aref}[-$num .. -1] : @{$aref};
|
||||
my @sorted = sort { $a <=> $b } @tail; # Numerisch aufsteigend
|
||||
my $n = scalar @sorted;
|
||||
my $mid = int ($n/2);
|
||||
|
||||
my $median = $n % 2 ? $sorted[$mid] : # ungerade Elemente -> Median Element steht in der Mitte von @sorted
|
||||
($sorted[$mid - 1] + $sorted[$mid]) / 2; # gerade Elemente -> Median ist der Durchschnitt der beiden mittleren Elemente
|
||||
|
||||
return $median;
|
||||
}
|
||||
|
||||
################################################################
|
||||
@@ -22194,7 +22268,7 @@ sub timestampToTimestring {
|
||||
|
||||
return if($epoch !~ /[0-9]/xs);
|
||||
|
||||
if (length ($epoch) == 13) { # Millisekunden
|
||||
if (strlength ($epoch) == 13) { # Millisekunden
|
||||
$epoch = $epoch / 1000;
|
||||
}
|
||||
|
||||
@@ -22286,7 +22360,7 @@ sub timestringsFromOffset {
|
||||
|
||||
return if($epoch !~ /^-?[0-9]*(.[0-9]*)?$/xs);
|
||||
|
||||
if (length ($epoch) == 13) { # Millisekunden
|
||||
if (strlength ($epoch) == 13) { # Millisekunden
|
||||
$epoch = $epoch / 1000;
|
||||
}
|
||||
|
||||
@@ -23074,12 +23148,12 @@ sub isAddSwitchOffCond {
|
||||
}
|
||||
|
||||
if ($true) {
|
||||
$info = qq{The value “$condval” resulted in 'true' after exec "$swoffcode" \n};
|
||||
$info = qq{The reference value “$condval” resulted in 'true' after exec "$swoffcode" \n};
|
||||
$info .= "-> Check successful ";
|
||||
$swoff = 1;
|
||||
}
|
||||
else {
|
||||
$info = qq{The value “$condval” resulted in 'false' after exec "$swoffcode" \n};
|
||||
$info = qq{The reference value “$condval” resulted in 'false' after exec "$swoffcode" \n};
|
||||
$swoff = 0;
|
||||
}
|
||||
}
|
||||
@@ -23107,7 +23181,7 @@ sub isAddSwitchOffCond {
|
||||
}
|
||||
}
|
||||
|
||||
$info .= qq{-> the effect depends on the switch context};
|
||||
$info .= qq{(the effect depends on the switch context)};
|
||||
}
|
||||
|
||||
return ($swoff, $info, $err);
|
||||
@@ -23816,6 +23890,17 @@ sub simplifyCstate {
|
||||
return $ps;
|
||||
}
|
||||
|
||||
################################################################
|
||||
# Länge eines Strings (auch mit Umlauten)
|
||||
################################################################
|
||||
sub strlength {
|
||||
my $string = shift // return 0;
|
||||
|
||||
my $decoded = decode ('UTF-8', $string);
|
||||
|
||||
return length ($decoded);
|
||||
}
|
||||
|
||||
################################################################
|
||||
# Prüfung eines übergebenen Regex
|
||||
################################################################
|
||||
@@ -24239,7 +24324,7 @@ sub lineFromSpaces {
|
||||
my $mlen = 1;
|
||||
|
||||
for my $s (@sps) {
|
||||
my $len = length ($s);
|
||||
my $len = strlength ($s);
|
||||
$mlen = $len if($len && $len > $mlen);
|
||||
}
|
||||
|
||||
@@ -26145,7 +26230,7 @@ to ensure that the system configuration is correct.
|
||||
|
||||
<a id="SolarForecast-attr-consumer" data-pattern="consumer.*"></a>
|
||||
<li><b>consumerXX <Device>[:<Alias>] type=<type> power=<power> [switchdev=<device>] <br>
|
||||
[mode=<mode>] [icon=<Icon>[@<Color>]] [mintime=<Option>] <br>
|
||||
[aliasshort=<String>] [mode=<mode>] [icon=<Icon>[@<Color>]] [mintime=<Option>] <br>
|
||||
[on=<command>] [off=<command>] [swstate=<Readingname>:<on-Regex>:<off-Regex>] [asynchron=<Option>] <br>
|
||||
[notbefore=<Expression>] [notafter=<Expression>] [locktime=<offlt>[:<onlt>]] <br>
|
||||
[auto=<Readingname>] [pcurr=<Readingname>:<Unit>[:<Threshold>]] [etotal=<Readingname>:<Einheit>[:<Threshold>]] <br>
|
||||
@@ -26194,6 +26279,8 @@ to ensure that the system configuration is correct.
|
||||
<tr><td> </td><td>If the consumer consists of different devices/channels (e.g. Homematic), the energy meter is defined as a <Device>. </td></tr>
|
||||
<tr><td> </td><td>The associated switching device is specified with the key 'switchdev'. </td></tr>
|
||||
<tr><td> </td><td> </td></tr>
|
||||
<tr><td> <b>aliasshort</b> </td><td>Short alias of the consumer for display in the flow chart. A maximum of 10 characters and no spaces are allowed. </td></tr>
|
||||
<tr><td> </td><td> </td></tr>
|
||||
<tr><td> <b>type</b> </td><td>Type of consumer. The following types are allowed: </td></tr>
|
||||
<tr><td> </td><td><b>dishwasher</b> - Consumer is a dishwasher </td></tr>
|
||||
<tr><td> </td><td><b>dryer</b> - Consumer is a tumble dryer </td></tr>
|
||||
@@ -26269,36 +26356,34 @@ to ensure that the system configuration is correct.
|
||||
<tr><td> </td><td>:<Threshold> (Wh) - From this energy consumption per hour, the consumption is considered valid. Optional specification (default: 0) </td></tr>
|
||||
<tr><td> </td><td> </td></tr>
|
||||
<tr><td> <b>swoncond</b> </td><td>Condition that must also be fulfilled in order to switch on the consumer (optional). The scheduled cycle is started. </td></tr>
|
||||
<tr><td> </td><td><b>Device</b> - Device to supply the additional switch-on condition </td></tr>
|
||||
<tr><td> </td><td><b>Reading</b> - Reading for delivery of the additional switch-on condition </td></tr>
|
||||
<tr><td> </td><td>The condition can be formulated as a regular expression or as Perl code enclosed in {..}: </td></tr>
|
||||
<tr><td> </td><td><b>Regex</b> - regular expression that must be fulfilled for a 'true' condition </td></tr>
|
||||
<tr><td> </td><td><b>{Perl-Code}</b> - the Perl code enclosed in {..} must return 'true' to fulfill the condition. It must not contain spaces. </td></tr>
|
||||
<tr><td> </td><td>The value of Device:Reading is transferred to the code with the variable $VALUE. </td></tr>
|
||||
<tr><td> </td><td><b>Device:Reading</b> - the device/reading combination returns the check value $VALUE (‘undef’ is ignored) </td></tr>
|
||||
<tr><td> </td><td>The check can be formulated as a regular expression or as Perl code enclosed in {..}: </td></tr>
|
||||
<tr><td> </td><td><b>Regex</b> - regular expression for checking $VALUE which must return ‘true’ if successful </td></tr>
|
||||
<tr><td> </td><td><b>{Perl-Code}</b> - the Perl code enclosed in {..} must not contain any spaces. The variable $VALUE can be evaluated by the code. </td></tr>
|
||||
<tr><td> </td><td>The return value must be ‘true’ if successful. </td></tr>
|
||||
<tr><td> </td><td> </td></tr>
|
||||
<tr><td> <b>swoffcond</b> </td><td>priority condition to switch off the consumer (optional). The scheduled cycle is stopped. </td></tr>
|
||||
<tr><td> </td><td><b>Device</b> - Device to supply the priority switch-off condition </td></tr>
|
||||
<tr><td> </td><td><b>Reading</b> - Reading for the delivery of the priority switch-off condition </td></tr>
|
||||
<tr><td> </td><td>The condition can be formulated as a regular expression or as Perl code enclosed in {..}: </td></tr>
|
||||
<tr><td> </td><td><b>Regex</b> - regular expression that must be fulfilled for a 'true' condition </td></tr>
|
||||
<tr><td> </td><td><b>{Perl-Code}</b> - the Perl code enclosed in {..} must return 'true' to fulfill the condition. It must not contain spaces. </td></tr>
|
||||
<tr><td> </td><td><b>Device:Reading</b> - the device/reading combination returns the check value $VALUE (‘undef’ is ignored) </td></tr>
|
||||
<tr><td> </td><td>The check can be formulated as a regular expression or as Perl code enclosed in {..}: </td></tr>
|
||||
<tr><td> </td><td><b>Regex</b> - regular expression for checking $VALUE which must return ‘true’ if successful </td></tr>
|
||||
<tr><td> </td><td><b>{Perl-Code}</b> - the Perl code enclosed in {..} must not contain any spaces. The variable $VALUE can be evaluated by the code. </td></tr>
|
||||
<tr><td> </td><td>The return value must be ‘true’ if successful. </td></tr>
|
||||
<tr><td> </td><td> </td></tr>
|
||||
<tr><td> <b>surpmeth</b> </td><td>The possible options define the procedure for determining the PV surplus. (optional) </td></tr>
|
||||
<tr><td> </td><td><b>default</b> - the PV surplus is read directly from the 'Current_Surplus' reading. (default) </td></tr>
|
||||
<tr><td> </td><td><b>median</b> - the median of the last PV surplus measurements (max. 20) is used. </td></tr>
|
||||
<tr><td> </td><td><b>2 .. 20</b> - the PV surplus used is calculated from the average of the specified number of measured values. </td></tr>
|
||||
<tr><td> </td><td><b>median[_2..20]</b> - The median of the last PV surplus values is used. The optional specification ‘_XX’ uses the last XX measured values. </td></tr>
|
||||
<tr><td> </td><td><b>average[_2..20]</b> - is the average of 20 PV surplus values. The optional specification ‘_XX’ uses the last XX measured values. </td></tr>
|
||||
<tr><td> </td><td><b><Device>:<Reading></b> - Device/Reading combination that provides a numerical PV surplus value in Watt
|
||||
determined or calculated by the user. </td></tr>
|
||||
<tr><td> </td><td> </td></tr>
|
||||
<tr><td> <b>spignorecond</b> </td><td>Condition to ignore a missing PV surplus (optional). If the condition is fulfilled, the load is switched on according to </td></tr>
|
||||
<tr><td> </td><td>the planning even if there is no PV surplus at the time. </td></tr>
|
||||
<tr><td> </td><td><b>CAUTION:</b> Using both keys <I>spignorecond</I> and <I>interruptable</I> can lead to undesired behaviour! </td></tr>
|
||||
<tr><td> </td><td><b>Device</b> - Device to deliver the condition </td></tr>
|
||||
<tr><td> </td><td><b>Reading</b> - Reading which contains the condition </td></tr>
|
||||
<tr><td> </td><td>The condition can be formulated as a regular expression or as Perl code enclosed in {..}: </td></tr>
|
||||
<tr><td> </td><td><b>Regex</b> - regular expression that must be fulfilled for a 'true' condition </td></tr>
|
||||
<tr><td> </td><td><b>{Perl-Code}</b> - the Perl code enclosed in {..} must return 'true' to fulfill the condition. It must not contain spaces. </td></tr>
|
||||
<tr><td> </td><td>The value of Device:Reading is transferred to the code with the variable $VALUE. </td></tr>
|
||||
<tr><td> </td><td><b>Device:Reading</b> - the device/reading combination returns the check value $VALUE (‘undef’ is ignored) </td></tr>
|
||||
<tr><td> </td><td>The check can be formulated as a regular expression or as Perl code enclosed in {..}: </td></tr>
|
||||
<tr><td> </td><td><b>Regex</b> - regular expression for checking $VALUE which must return ‘true’ if successful </td></tr>
|
||||
<tr><td> </td><td><b>{Perl-Code}</b> - the Perl code enclosed in {..} must not contain any spaces. The variable $VALUE can be evaluated by the code. </td></tr>
|
||||
<tr><td> </td><td>The return value must be ‘true’ if successful. </td></tr>
|
||||
<tr><td> </td><td> </td></tr>
|
||||
<tr><td> <b>interruptable</b> </td><td>defines the possible interruption options for the consumer after it has been started (optional). Options can be: </td></tr>
|
||||
<tr><td> </td><td><b>0</b> - Load is not temporarily switched off even if the PV surplus falls below the required energy (default) </td></tr>
|
||||
@@ -26353,7 +26438,7 @@ to ensure that the system configuration is correct.
|
||||
<b>attr <name> consumer04</b> Shelly.shellyplug3 icon=scene_microwave_oven@ed type=heater power=2000 mode=must notbefore=07 mintime=600 on=on off=off etotal=relay_0_energy_Wh:Wh pcurr=relay_0_power:W auto=automatic interruptable=eg.wz.wandthermostat:diff-temp:(22)(\.[2-9])|([2-9][3-9])(\.[0-9]):0.2 <br>
|
||||
<b>attr <name> consumer05</b> Shelly.shellyplug4 icon=sani_buffer_electric_heater_side type=heater mode=must power=1000 notbefore=7 notafter=20:10 auto=automatic pcurr=actpow:W on=on off=off mintime=SunPath interruptable=1 <br>
|
||||
<b>attr <name> consumer06</b> Shelly.shellyplug5 icon=sani_buffer_electric_heater_side type=heater mode=must power=1000 notbefore=07:20 notafter={return'20:05'} auto=automatic pcurr=actpow:W on=on off=off mintime=SunPath:60:-120 interruptable=1 spignorecond=SolCast:Current_PV:{($VALUE)=split/\s/,$VALUE;$VALUE>10?1:0;} <br>
|
||||
<b>attr <name> consumer07</b> SolCastDummy icon=sani_buffer_electric_heater_side type=heater mode=can power=600 auto=automatic pcurr=actpow:W on=on off=off mintime=15 asynchron=1 locktime=300:1200 interruptable=1 noshow=39 <br>
|
||||
<b>attr <name> consumer07</b> SolCastDummy icon=sani_buffer_electric_heater_side type=heater mode=can power=600 auto=automatic pcurr=actpow:W on=on off=off mintime=15 asynchron=1 locktime=300:1200 interruptable=1 noshow=39 surpmeth=median_10 <br>
|
||||
</ul>
|
||||
</li>
|
||||
<br>
|
||||
@@ -28804,7 +28889,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
||||
|
||||
<a id="SolarForecast-attr-consumer" data-pattern="consumer.*"></a>
|
||||
<li><b>consumerXX <Device>[:<Alias>] type=<type> power=<power> [switchdev=<device>] <br>
|
||||
[mode=<mode>] [icon=<Icon>[@<Farbe>]] [mintime=<Option>] <br>
|
||||
[aliasshort=<String>] [mode=<mode>] [icon=<Icon>[@<Farbe>]] [mintime=<Option>] <br>
|
||||
[on=<Kommando>] [off=<Kommando>] [swstate=<Readingname>:<on-Regex>:<off-Regex>] [asynchron=<Option>] <br>
|
||||
[notbefore=<Ausdruck>] [notafter=<Ausdruck>] [locktime=<offlt>[:<onlt>]] <br>
|
||||
[auto=<Readingname>] [pcurr=<Readingname>:<Einheit>[:<Schwellenwert>]] [etotal=<Readingname>:<Einheit>[:<Schwellenwert>]] <br>
|
||||
@@ -28852,6 +28937,8 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
||||
<tr><td> </td><td>Besteht der Verbraucher aus verschiedenen Geräten/Kanälen (z.B. Homematic), wird der Energiemesser als <Device> definiert. </td></tr>
|
||||
<tr><td> </td><td>Das dazugehörige Schalt-Gerät wird mit dem Schlüssel 'switchdev' spezifiziert. </td></tr>
|
||||
<tr><td> </td><td> </td></tr>
|
||||
<tr><td> <b>aliasshort</b> </td><td>Kurzalias des Verbrauchers zur Anzeige in der Flußgrafik. Es sind maximal 10 Zeichen und keine Leerzeichen erlaubt. </td></tr>
|
||||
<tr><td> </td><td> </td></tr>
|
||||
<tr><td> <b>type</b> </td><td>Typ des Verbrauchers. Folgende Typen sind erlaubt: </td></tr>
|
||||
<tr><td> </td><td><b>dishwasher</b> - Verbraucher ist eine Spülmaschine </td></tr>
|
||||
<tr><td> </td><td><b>dryer</b> - Verbraucher ist ein Wäschetrockner </td></tr>
|
||||
@@ -28927,37 +29014,34 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
||||
<tr><td> </td><td>:<Schwellenwert> (Wh) - Ab diesem Energieverbrauch pro Stunde wird der Verbrauch als gültig gewertet. Optionale Angabe (default: 0) </td></tr>
|
||||
<tr><td> </td><td> </td></tr>
|
||||
<tr><td> <b>swoncond</b> </td><td>Bedingung die zusätzlich erfüllt sein muß um den geplanten Zyklus zu starten und den Verbraucher einzuschalten (optional). </td></tr>
|
||||
<tr><td> </td><td><b>Device</b> - Device zur Lieferung der zusätzlichen Einschaltbedingung </td></tr>
|
||||
<tr><td> </td><td><b>Reading</b> - Reading zur Lieferung der zusätzlichen Einschaltbedingung </td></tr>
|
||||
<tr><td> </td><td>Die Bedingung kann als regulärer Ausdruck oder als in {..} eingeschlossener Perl-Code formuliert sein: </td></tr>
|
||||
<tr><td> </td><td><b>Regex</b> - regulärer Ausdruck der für eine 'wahre' Bedingung erfüllt sein muß </td></tr>
|
||||
<tr><td> </td><td><b>{Perl-Code}</b> - der in {..} eingeschlossene Perl-Code muß 'wahr' liefern um die Bedingung zu erfüllen. Er darf keine Leerzeichen enthalten. </td></tr>
|
||||
<tr><td> </td><td>Der Wert von Device:Reading wird dem Code mit der Variable $VALUE übergeben. </td></tr>
|
||||
<tr><td> </td><td><b>Device:Reading</b> - die Device/Reading Kombination liefert den Prüfwert $VALUE ('undef' wird ignoriert) </td></tr>
|
||||
<tr><td> </td><td>Die Prüfung kann als regulärer Ausdruck oder als in {..} eingeschlossener Perl-Code formuliert sein: </td></tr>
|
||||
<tr><td> </td><td><b>Regex</b> - regulärer Ausdruck zur Prüfung von $VALUE der im Erfolgsfall 'wahr' liefern muß </td></tr>
|
||||
<tr><td> </td><td><b>{Perl-Code}</b> - der in {..} eingeschlossene Perl-Code darf keine Leerzeichen enthalten. Die Variable $VALUE kann vom Code ausgewertet werden. </td></tr>
|
||||
<tr><td> </td><td>Der return Wert muß im Erfolgsfall 'wahr' sein. </td></tr>
|
||||
<tr><td> </td><td> </td></tr>
|
||||
<tr><td> <b>swoffcond</b> </td><td>vorrangige Bedingung um den Verbraucher auszuschalten (optional). Der geplante Zyklus wird gestoppt. </td></tr>
|
||||
<tr><td> </td><td><b>Device</b> - Device zur Lieferung der vorrangigen Ausschaltbedingung </td></tr>
|
||||
<tr><td> </td><td><b>Reading</b> - Reading zur Lieferung der vorrangigen Ausschaltbedingung </td></tr>
|
||||
<tr><td> </td><td>Die Bedingung kann als regulärer Ausdruck oder als in {..} eingeschlossener Perl-Code formuliert sein: </td></tr>
|
||||
<tr><td> </td><td><b>Regex</b> - regulärer Ausdruck der für eine 'wahre' Bedingung erfüllt sein muß </td></tr>
|
||||
<tr><td> </td><td><b>{Perl-Code}</b> - der in {..} eingeschlossene Perl-Code muß 'wahr' liefern um die Bedingung zu erfüllen. Er darf keine Leerzeichen enthalten. </td></tr>
|
||||
<tr><td> </td><td>Der Wert von Device:Reading wird dem Code mit der Variable $VALUE übergeben. </td></tr>
|
||||
<tr><td> </td><td><b>Device:Reading</b> - die Device/Reading Kombination liefert den Prüfwert $VALUE ('undef' wird ignoriert) </td></tr>
|
||||
<tr><td> </td><td>Die Prüfung kann als regulärer Ausdruck oder als in {..} eingeschlossener Perl-Code formuliert sein: </td></tr>
|
||||
<tr><td> </td><td><b>Regex</b> - regulärer Ausdruck zur Prüfung von $VALUE der im Erfolgsfall 'wahr' liefern muß </td></tr>
|
||||
<tr><td> </td><td><b>{Perl-Code}</b> - der in {..} eingeschlossene Perl-Code darf keine Leerzeichen enthalten. Die Variable $VALUE kann vom Code ausgewertet werden. </td></tr>
|
||||
<tr><td> </td><td>Der return Wert muß im Erfolgsfall 'wahr' sein. </td></tr>
|
||||
<tr><td> </td><td> </td></tr>
|
||||
<tr><td> <b>surpmeth</b> </td><td>Die möglichen Optionen legen das Verfahren zur Ermittlung des PV-Überschusses fest. (optional) </td></tr>
|
||||
<tr><td> </td><td><b>default</b> - der PV-Überschuß wird aus dem Reading 'Current_Surplus' direkt ausgelesen. (default) </td></tr>
|
||||
<tr><td> </td><td><b>median</b> - es wird der Median der letzten PV-Überschuß Messungen (max. 20) verwendet. </td></tr>
|
||||
<tr><td> </td><td><b>2 .. 20</b> - der verwendete PV-Überschuß wird als Durchschnitt der angegebenen Anzahl Meßwerte gebildet. </td></tr>
|
||||
<tr><td> </td><td><b>median[_2..20]</b> - es wird der Median der letzten PV-Überschuß Werte verwendet. Die optionale Angabe '_XX' verwendet die letzten XX Meßwerte. </td></tr>
|
||||
<tr><td> </td><td><b>average[_2..20]</b> - bildet den Durchschnitt von 20 PV-Überschuß Werten. Die optionale Angabe '_XX' verwendet die letzten XX Meßwerte. </td></tr>
|
||||
<tr><td> </td><td><b><Device>:<Reading></b> - Device/Reading-Kombination die einen vom Nutzer bestimmten bzw. berechneten
|
||||
numerischen PV-Überschuß in Watt liefert. </td></tr>
|
||||
<tr><td> </td><td> </td></tr>
|
||||
<tr><td> <b>spignorecond</b> </td><td>Bedingung um einen fehlenden PV Überschuß zu ignorieren (optional). Bei erfüllter Bedingung wird der Verbraucher entsprechend </td></tr>
|
||||
<tr><td> </td><td>der Planung eingeschaltet auch wenn zu dem Zeitpunkt kein PV Überschuß vorliegt. </td></tr>
|
||||
<tr><td> </td><td><b>ACHTUNG:</b> Die Verwendung beider Schlüssel <I>spignorecond</I> und <I>interruptable</I> kann zu einem unerwünschten Verhalten führen! </td></tr>
|
||||
<tr><td> </td><td><b>Device</b> - Device zur Lieferung der Bedingung </td></tr>
|
||||
<tr><td> </td><td><b>Reading</b> - Reading welches die Bedingung enthält </td></tr>
|
||||
<tr><td> </td><td>Die Bedingung kann als regulärer Ausdruck oder als in {..} eingeschlossener Perl-Code formuliert sein: </td></tr>
|
||||
<tr><td> </td><td><b>Regex</b> - regulärer Ausdruck der für eine 'wahre' Bedingung erfüllt sein muß </td></tr>
|
||||
<tr><td> </td><td><b>{Perl-Code}</b> - der in {..} eingeschlossene Perl-Code muß 'wahr' liefern um die Bedingung zu erfüllen. Er darf keine Leerzeichen enthalten. </td></tr>
|
||||
<tr><td> </td><td>Der Wert von Device:Reading wird dem Code mit der Variable $VALUE übergeben. </td></tr>
|
||||
<tr><td> </td><td><b>Device:Reading</b> - die Device/Reading Kombination liefert den Prüfwert $VALUE ('undef' wird ignoriert) </td></tr>
|
||||
<tr><td> </td><td>Die Prüfung kann als regulärer Ausdruck oder als in {..} eingeschlossener Perl-Code formuliert sein: </td></tr>
|
||||
<tr><td> </td><td><b>Regex</b> - regulärer Ausdruck zur Prüfung von $VALUE der im Erfolgsfall 'wahr' liefern muß </td></tr>
|
||||
<tr><td> </td><td><b>{Perl-Code}</b> - der in {..} eingeschlossene Perl-Code darf keine Leerzeichen enthalten. Die Variable $VALUE kann vom Code ausgewertet werden. </td></tr>
|
||||
<tr><td> </td><td>Der return Wert muß im Erfolgsfall 'wahr' sein. </td></tr>
|
||||
<tr><td> </td><td> </td></tr>
|
||||
<tr><td> <b>interruptable</b> </td><td>definiert die möglichen Unterbrechungsoptionen für den Verbraucher nachdem er gestartet wurde (optional). Optionen können sein: </td></tr>
|
||||
<tr><td> </td><td><b>0</b> - Verbraucher wird nicht temporär ausgeschaltet auch wenn der PV Überschuß die benötigte Energie unterschreitet (default) </td></tr>
|
||||
@@ -29012,7 +29096,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
||||
<b>attr <name> consumer04</b> Shelly.shellyplug3 icon=scene_microwave_oven@red type=heater power=2000 mode=must notbefore=07 mintime=600 on=on off=off etotal=relay_0_energy_Wh:Wh pcurr=relay_0_power:W auto=automatic interruptable=eg.wz.wandthermostat:diff-temp:(22)(\.[2-9])|([2-9][3-9])(\.[0-9]):0.2 <br>
|
||||
<b>attr <name> consumer05</b> Shelly.shellyplug4 icon=sani_buffer_electric_heater_side type=heater mode=must power=1000 notbefore=7 notafter=20:10 auto=automatic pcurr=actpow:W on=on off=off mintime=SunPath interruptable=1 <br>
|
||||
<b>attr <name> consumer06</b> Shelly.shellyplug5 icon=sani_buffer_electric_heater_side type=heater mode=must power=1000 notbefore=07:20 notafter={return'20:05'} auto=automatic pcurr=actpow:W on=on off=off mintime=SunPath:60:-120 interruptable=1 spignorecond=SolCast:Current_PV:{($VALUE)=split/\s/,$VALUE;$VALUE>10?1:0;} <br>
|
||||
<b>attr <name> consumer07</b> SolCastDummy icon=sani_buffer_electric_heater_side type=heater mode=can power=600 auto=automatic pcurr=actpow:W on=on off=off mintime=15 asynchron=1 locktime=300:1200 interruptable=1 noshow=39 <br>
|
||||
<b>attr <name> consumer07</b> SolCastDummy icon=sani_buffer_electric_heater_side type=heater mode=can power=600 auto=automatic pcurr=actpow:W on=on off=off mintime=15 asynchron=1 locktime=300:1200 interruptable=1 noshow=39 surpmeth=median_10 <br>
|
||||
</ul>
|
||||
</li>
|
||||
<br>
|
||||
|
||||
@@ -160,10 +160,11 @@ BEGIN {
|
||||
|
||||
# Versions History intern
|
||||
my %vNotesIntern = (
|
||||
"1.54.4" => "21.07.2025 replace length by new sub strlength, Consumer attr new key 'aliasshort', change code of medianArray ".
|
||||
"1.54.4" => "22.07.2025 replace length by new sub strlength, Consumer attr new key 'aliasshort', change code of medianArray ".
|
||||
"medianArray: can optional use newest 3..20 elements, avgArray: use the newest elements if num is set ".
|
||||
"Debug consumerSwitching: print out info message of compare operation, remove attr graphicShowDiff ".
|
||||
"store surpmeth calc result in key surpmethResult in Consumer master record, __readFileMessages: refactored code ",
|
||||
"store surpmeth calc result in key surpmethResult in Consumer master record, __readFileMessages: refactored code ".
|
||||
"surpmeth: use average[_2..20] instead of numeric values 2.20 only ",
|
||||
"1.54.3" => "19.07.2025 ctrlDebug: add collectData_long ",
|
||||
"1.54.2" => "18.07.2025 _createSummaries: add debug infos ",
|
||||
"1.54.1" => "08.07.2025 userExit: new coding, __createReduceIcon: fix Wide character in syswrite - https://forum.fhem.de/index.php?msg=1344368 ".
|
||||
@@ -6179,8 +6180,8 @@ sub _attrconsumer { ## no critic "not used"
|
||||
return "The reading '$rd' of device '$dv' is invalid or doesn't contain a valid numeric value";
|
||||
}
|
||||
}
|
||||
elsif ($h->{surpmeth} !~ /^[2-9]$|^1[0-9]$|^20$|^median(?:_(?:[3-9]|1[0-9]|20))?$|^default$/xs) {
|
||||
return qq{The surpmeth "$h->{surpmeth}" is wrong. It must contain a '<device>:<reading>', 'median[_3..20]', 'default' or an integer value of '2 .. 20'.};
|
||||
elsif ($h->{surpmeth} !~ /^(?:median|average)(?:_(?:[2-9]|1[0-9]|20))?$|^default$/xs) {
|
||||
return qq{The surpmeth "$h->{surpmeth}" is wrong. It must contain a '<device>:<reading>', 'median[_2..20]', 'average[_2..20]' or 'default'.};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8849,15 +8850,16 @@ sub centralTask {
|
||||
# ::CommandDeleteAttr (undef, "$name graphicBeamWidth");
|
||||
#}
|
||||
|
||||
my $gsd = AttrVal ($name, 'graphicShowDiff ', undef); # 25.06.
|
||||
my $gco = AttrVal ($name, 'graphicControl', '');
|
||||
for my $c (1..MAXCONSUMER) { # 23.07.
|
||||
$c = sprintf "%02d", $c;
|
||||
my $surpmeth = ConsumerVal ($hash, $c, 'surpmeth', '');
|
||||
|
||||
if (defined $gsd) {
|
||||
my $newval = $gco." showDiff=$gsd";
|
||||
CommandAttr (undef, "$name graphicControl $newval");
|
||||
::CommandDeleteAttr (undef, "$name graphicShowDiff");
|
||||
if ($surpmeth =~ /^[2-9]$|^1[0-9]$|^20$/xs) {
|
||||
fhem ("set $name attrKeyVal consumer${c} surpmeth=average_${surpmeth}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
##########################################################################################################################
|
||||
|
||||
if (!CurrentVal ($hash, 'allStringsFullfilled', 0)) { # die String Konfiguration erstellen wenn noch nicht erfolgreich ausgeführt
|
||||
@@ -9325,10 +9327,10 @@ sub _specialActivities {
|
||||
##########################################################################
|
||||
|
||||
for my $c (keys %{$data{$name}{consumers}}) {
|
||||
next if(ConsumerVal ($hash, $c, "plandelete", "regular") eq "regular");
|
||||
next if(ConsumerVal ($hash, $c, 'plandelete', 'regular') eq 'regular');
|
||||
|
||||
my $planswitchoff = ConsumerVal ($hash, $c, "planswitchoff", $t);
|
||||
my $simpCstat = simplifyCstate (ConsumerVal ($hash, $c, "planstate", ""));
|
||||
my $planswitchoff = ConsumerVal ($hash, $c, 'planswitchoff', $t);
|
||||
my $simpCstat = simplifyCstate (ConsumerVal ($hash, $c, 'planstate', ''));
|
||||
|
||||
if ($t > $planswitchoff && $simpCstat =~ /planned|finished|unknown/xs) {
|
||||
deleteConsumerPlanning ($hash, $c);
|
||||
@@ -12013,8 +12015,8 @@ sub _manageConsumerData {
|
||||
|
||||
for my $c (sort{$a<=>$b} keys %{$data{$name}{consumers}}) {
|
||||
$paref->{consumer} = $c;
|
||||
my $consumer = ConsumerVal ($hash, $c, "name", "");
|
||||
my $alias = ConsumerVal ($hash, $c, "alias", "");
|
||||
my $consumer = ConsumerVal ($hash, $c, 'name', '');
|
||||
my $alias = ConsumerVal ($hash, $c, 'alias', '');
|
||||
|
||||
## aktuelle Leistung auslesen
|
||||
##############################
|
||||
@@ -13001,8 +13003,7 @@ sub __setConsRcmdState {
|
||||
|
||||
if ($debug =~ /consumerSwitching${c}/x) {
|
||||
my $splref = CurrentVal ($name, 'surplusslidereg', '.');
|
||||
my $spser = 'undef';
|
||||
$spser = join " ", @{$splref} if(ref $splref eq 'ARRAY');
|
||||
my $spser = ref $splref eq 'ARRAY' ? join ' ', @{$splref} : undef;
|
||||
|
||||
Log3 ($name, 1, qq{$name DEBUG> ############### consumerSwitching consumer "$c" ###############});
|
||||
Log3 ($name, 1, qq{$name DEBUG> consumer "$c" - ConsumptionRecommended calc method: $method, surplus: }.
|
||||
@@ -22063,14 +22064,15 @@ sub determSurplus {
|
||||
$surplus = medianArray ($splref, $num);
|
||||
$method = $num ? "median:$num" : "median:all";
|
||||
}
|
||||
elsif ($surpmeth =~ /average/xs) { # Average Ermittlung, !kann UNDEF sein!
|
||||
my $num = (split '_', $surpmeth)[1];
|
||||
$surplus = avgArray ($splref, $num);
|
||||
$method = $num ? "average:$num" : "average:all";
|
||||
}
|
||||
elsif ($surpmeth eq 'default') { # aktueller Energieüberschuß
|
||||
$surplus = CurrentVal ($hash, 'surplus', 0);
|
||||
$method = 'default';
|
||||
}
|
||||
elsif ($surpmeth =~ /^[2-9]$|^1[0-9]$|^20$/xs) {
|
||||
$surplus = avgArray ($splref, $surpmeth); # Average Ermittlung, !kann UNDEF sein!
|
||||
$method = "average:$surpmeth";
|
||||
}
|
||||
elsif ($surpmeth =~ /.*:.*/xs) {
|
||||
my ($dv, $rd) = split ':', $surpmeth;
|
||||
$method = "$dv:$rd";
|
||||
@@ -23179,7 +23181,7 @@ sub isAddSwitchOffCond {
|
||||
}
|
||||
}
|
||||
|
||||
$info .= qq{-> the effect depends on the switch context};
|
||||
$info .= qq{(the effect depends on the switch context)};
|
||||
}
|
||||
|
||||
return ($swoff, $info, $err);
|
||||
@@ -26369,8 +26371,8 @@ to ensure that the system configuration is correct.
|
||||
<tr><td> </td><td> </td></tr>
|
||||
<tr><td> <b>surpmeth</b> </td><td>The possible options define the procedure for determining the PV surplus. (optional) </td></tr>
|
||||
<tr><td> </td><td><b>default</b> - the PV surplus is read directly from the 'Current_Surplus' reading. (default) </td></tr>
|
||||
<tr><td> </td><td><b>median[_3..20]</b> - The median of the last PV surplus values is used. The optional specification ‘_XX’ uses the last XX measured values. </td></tr>
|
||||
<tr><td> </td><td><b>2 .. 20</b> - the PV surplus used is calculated from the average of the specified number of measured values. </td></tr>
|
||||
<tr><td> </td><td><b>median[_2..20]</b> - The median of the last PV surplus values is used. The optional specification ‘_XX’ uses the last XX measured values. </td></tr>
|
||||
<tr><td> </td><td><b>average[_2..20]</b> - is the average of 20 PV surplus values. The optional specification ‘_XX’ uses the last XX measured values. </td></tr>
|
||||
<tr><td> </td><td><b><Device>:<Reading></b> - Device/Reading combination that provides a numerical PV surplus value in Watt
|
||||
determined or calculated by the user. </td></tr>
|
||||
<tr><td> </td><td> </td></tr>
|
||||
@@ -29027,8 +29029,8 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
||||
<tr><td> </td><td> </td></tr>
|
||||
<tr><td> <b>surpmeth</b> </td><td>Die möglichen Optionen legen das Verfahren zur Ermittlung des PV-Überschusses fest. (optional) </td></tr>
|
||||
<tr><td> </td><td><b>default</b> - der PV-Überschuß wird aus dem Reading 'Current_Surplus' direkt ausgelesen. (default) </td></tr>
|
||||
<tr><td> </td><td><b>median[_3..20]</b> - es wird der Median der letzten PV-Überschuß Werte verwendet. Die optionale Angabe '_XX' verwendet die letzten XX Meßwerte. </td></tr>
|
||||
<tr><td> </td><td><b>2 .. 20</b> - der verwendete PV-Überschuß wird als Durchschnitt der angegebenen Anzahl Meßwerte gebildet. </td></tr>
|
||||
<tr><td> </td><td><b>median[_2..20]</b> - es wird der Median der letzten PV-Überschuß Werte verwendet. Die optionale Angabe '_XX' verwendet die letzten XX Meßwerte. </td></tr>
|
||||
<tr><td> </td><td><b>average[_2..20]</b> - bildet den Durchschnitt von 20 PV-Überschuß Werten. Die optionale Angabe '_XX' verwendet die letzten XX Meßwerte. </td></tr>
|
||||
<tr><td> </td><td><b><Device>:<Reading></b> - Device/Reading-Kombination die einen vom Nutzer bestimmten bzw. berechneten
|
||||
numerischen PV-Überschuß in Watt liefert. </td></tr>
|
||||
<tr><td> </td><td> </td></tr>
|
||||
|
||||
Reference in New Issue
Block a user