diff --git a/contrib/DS_Starter/76_SolarForecast.pm b/contrib/DS_Starter/76_SolarForecast.pm index 02c754994..14d51e2a7 100644 --- a/contrib/DS_Starter/76_SolarForecast.pm +++ b/contrib/DS_Starter/76_SolarForecast.pm @@ -119,6 +119,7 @@ BEGIN { # Versions History intern my %vNotesIntern = ( + "0.53.0" => "17.06.2021 Logic for preferential charging battery, attr preferredChargeBattery ", "0.52.5" => "16.06.2021 sub __weatherOnBeam ", "0.52.4" => "15.06.2021 minor fix, possible avoid implausible inverter values ", "0.52.3" => "14.06.2021 consumer on/off icon gray if no on/off command is defined, more consumer debug log ", @@ -583,6 +584,7 @@ sub Initialize { "maxVariancePerDay ". "maxValBeam ". "numHistDays:slider,1,1,30 ". + "preferredChargeBattery:slider,0,1,100 ". "rainFactorDamping:slider,0,1,100 ". "sameWeekdaysForConsfc:1,0 ". "showDiff:no,top,bottom ". @@ -1331,7 +1333,7 @@ sub _setconsumerAction { ## no critic "not used" my $action = shift @args; # z.B. set, setreading my $cname = shift @args; # Consumername - my $tail = join " ", map { my $p = $_; $p =~ s/\s//xg; $p; } @args; # restliche Befehlsargumente ## no critic 'Map blocks' + my $tail = join " ", map { my $p = $_; $p =~ s/\s//xg; $p; } @args; ## no critic 'Map blocks' # restliche Befehlsargumente if($action eq "set") { CommandSet (undef,"$cname $tail"); @@ -1651,7 +1653,7 @@ sub Notify { $event = "" if(!defined($event)); my @evl = split(/\s+/x, $event); - my @parts = split(/: /,$event, 2); + my @parts = split(/: /x,$event, 2); my $reading = shift @parts; if ($reading eq "state" || $reading eq $autoreading) { @@ -2637,7 +2639,7 @@ sub _manageConsumerData { ## consumer Hash ergänzen, Reading generieren ############################################### my $costate = ReadingsVal ($consumer, "state", ""); - my ($pstate,$starttime,$stoptime) = __planningStateandTimes ($paref); + my ($pstate,$starttime,$stoptime) = __planningStateAndTimes ($paref); $data{$type}{$name}{consumers}{$c}{state} = $costate; push @$daref, "consumer${c}<>" ."name='$alias' state='$costate' planningstate='$pstate' "; # Consumer Infos @@ -2943,14 +2945,20 @@ sub __switchConsumer { ## Verbraucher einschalten ############################ my $oncom = ConsumerVal ($hash, $c, "oncom", ""); # Set Command für "on" - my $auto = ConsumerVal ($hash, $c, "auto", 1); + my $auto = ConsumerVal ($hash, $c, "auto", 1); - if($auto && $oncom && $pstate =~ /planned/xs && $startts && $t >= $startts) { # Verbraucher Start ist geplant && Startzeit überschritten + if($auto && $oncom && $pstate =~ /planned|priority/xs && $startts && $t >= $startts) { # Verbraucher Start ist geplant && Startzeit überschritten my $surplus = CurrentVal ($hash, "surplus", 0); # aktueller Überschuß my $mode = ConsumerVal ($hash, $c, "mode", $defcmode); # Consumer Planungsmode my $power = ConsumerVal ($hash, $c, "power", 0); # Consumer nominale Leistungsaufnahme (W) + my $enable = ___enableSwitchByBatPrioCharge ($paref); # Vorrangladung Batterie ? - if($mode eq "must" || $surplus >= $power) { # "Muss"-Planung oder Überschuß > Leistungsaufnahme + Log3 ($name, 1, "$name - Is consumer switch enabled by battery: $enable"); + + if($mode eq "can" && !$enable) { + $data{$type}{$name}{consumers}{$c}{planstate} = "priority charging battery"; + } + elsif($mode eq "must" || $surplus >= $power) { # "Muss"-Planung oder Überschuß > Leistungsaufnahme CommandSet(undef,"$cname $oncom"); my (undef,undef,undef,$starttime) = timestampToTimestring ($t); my $stopdiff = ceil(ConsumerVal ($hash, $c, "mintime", $defmintime) / 60) * 3600; @@ -2984,10 +2992,33 @@ sub __switchConsumer { return; } +################################################################ +# Freigabe Einschalten Verbraucher durch Batterie Vorrangladung +# return 0 -> keine Einschaltfreigabe Verbraucher +# return 1 -> Einschaltfreigabe Verbraucher +################################################################ +sub ___enableSwitchByBatPrioCharge { + my $paref = shift; + my $hash = $paref->{hash}; + my $name = $paref->{name}; + my $c = $paref->{consumer}; + + my $ena = 1; + my $pcb = AttrVal ($name, "preferredChargeBattery", 0); # Vorrangladung Batterie zu X% + my ($badev) = hasBattery ($name); + + return $ena if(!$pcb || !$badev); # Freigabe Schalten Consumer wenn kein Prefered Battery/Soll-Ladung 0 oder keine Batterie installiert + + my $cbcharge = CurrentVal ($hash, "batcharge", 0); # aktuelle Batterieladung + $ena = 0 if($cbcharge < $pcb); # keine Freigabe wenn Batterieladung kleiner Soll-Ladung + +return $ena; +} + ################################################################### # Consumer Planungsstatus mit Schaltzeiten liefern ################################################################### -sub __planningStateandTimes { +sub __planningStateAndTimes { my $paref = shift; my $hash = $paref->{hash}; my $c = $paref->{consumer}; @@ -2997,6 +3028,7 @@ sub __planningStateandTimes { $pstate = $pstate =~ /planned/xs ? "planned" : $pstate =~ /switched\son/xs ? "started" : $pstate =~ /switched\soff/xs ? "finished" : + $pstate =~ /priority/xs ? $pstate : "unknown"; my $startts = ConsumerVal ($hash, $c, "planswitchon", ""); @@ -3021,10 +3053,8 @@ sub _transferBatteryValues { my $day = $paref->{day}; my $daref = $paref->{daref}; - my $badev = ReadingsVal($name, "currentBatteryDev", ""); # aktuelles Meter device für Batteriewerte - my ($a,$h) = parseParams ($badev); - $badev = $a->[0] // ""; - return if(!$badev || !$defs{$badev}); + my ($badev,$a,$h) = hasBattery ($name); + return if(!$badev); my $type = $hash->{TYPE}; @@ -3043,16 +3073,16 @@ sub _transferBatteryValues { Log3 ($name, 5, "$name - collect Battery data: device=$badev, pin=$pin ($piunit), pout=$pou ($pounit), totalin: $bin ($binunit), totalout: $bout ($boutunit), charge: $batchr"); - my $piuf = $piunit =~ /^kW$/xi ? 1000 : 1; - my $pouf = $pounit =~ /^kW$/xi ? 1000 : 1; - my $binuf = $binunit =~ /^kWh$/xi ? 1000 : 1; - my $boutuf = $boutunit =~ /^kWh$/xi ? 1000 : 1; + my $piuf = $piunit =~ /^kW$/xi ? 1000 : 1; + my $pouf = $pounit =~ /^kW$/xi ? 1000 : 1; + my $binuf = $binunit =~ /^kWh$/xi ? 1000 : 1; + my $boutuf = $boutunit =~ /^kWh$/xi ? 1000 : 1; - my $pbo = ReadingsNum ($badev, $pou, 0) * $pouf; # aktuelle Batterieentladung (W) - my $pbi = ReadingsNum ($badev, $pin, 0) * $piuf; # aktueller Batterieladung (W) - my $btotout = ReadingsNum ($badev, $bout, 0) * $boutuf; # totale Batterieentladung (Wh) - my $btotin = ReadingsNum ($badev, $bin, 0) * $binuf; # totale Batterieladung (Wh) - my $batcharge = ReadingsNum ($badev, $batchr, "-"); + my $pbo = ReadingsNum ($badev, $pou, 0) * $pouf; # aktuelle Batterieentladung (W) + my $pbi = ReadingsNum ($badev, $pin, 0) * $piuf; # aktueller Batterieladung (W) + my $btotout = ReadingsNum ($badev, $bout, 0) * $boutuf; # totale Batterieentladung (Wh) + my $btotin = ReadingsNum ($badev, $bin, 0) * $binuf; # totale Batterieladung (Wh) + my $batcharge = ReadingsNum ($badev, $batchr, 0); my $params; @@ -4285,7 +4315,7 @@ sub _graphicConsumerLegend { $paref->{consumer} = $c; - my ($planstate,$starttime,$stoptime) = __planningStateandTimes ($paref); + my ($planstate,$starttime,$stoptime) = __planningStateAndTimes ($paref); my $pstate = $caicon eq "times" ? $hqtxt{pstate}{$lang} : $htitles{pstate}{$lang}; $pstate =~ s//$planstate/xs; @@ -4300,6 +4330,11 @@ sub _graphicConsumerLegend { } else { $isricon = "".FW_makeImage($caicon, ''); + if($planstate =~ /priority/xs) { + my (undef,$color) = split('@', $caicon); + $color = $color ? '@'.$color : ''; + $isricon = "".FW_makeImage('it_ups_charging'.$color, ''); + } } } else { @@ -4360,8 +4395,8 @@ sub _graphicConsumerLegend { $ctable .= "$auicon "; } else { - my (undef,$co) = split('\@', $cicon); - $co = '' if (!$co); + my (undef,$co) = split('@', $cicon); + $co = '' if (!$co); $ctable .= "$calias "; $ctable .= " "; @@ -4967,15 +5002,13 @@ sub _flowGraphic { my $grid_color = $cgfi ? 'flowg grid_color1' : 'flowg grid_color2'; $grid_color = 'flowg grid_color3' if (!$cgfi && !$cgc && $batout); # dritte Farbe - my $ret; - - $ret .= qq{ + my $ret = << "END0"; - + @@ -5011,15 +5044,15 @@ sub _flowGraphic { - }; +END0 if ($hasbat) { - $ret .= qq{ - - - - }; + $ret .= << "END1"; + + + +END1 $ret .= '' if ($soc > 12); $ret .= '' if ($soc > 38); @@ -5028,18 +5061,19 @@ sub _flowGraphic { $ret .= ''; } - $ret .= qq{ + $ret .= << "END2"; - - - - }; + + + +END2 - $ret .= qq{ + if ($hasbat) { + $ret .= << "END3"; - } if ($hasbat); - +END3 + } $ret .= qq{$cpv} if ($cpv); $ret .= qq{$soc %} if ($hasbat); @@ -5188,6 +5222,21 @@ sub checkdwdattr { return $err; } +################################################################ +# ist Batterie installiert ? +# 1 - ja, 0 - nein +################################################################ +sub hasBattery { + my $name = shift; + + my $badev = ReadingsVal($name, "currentBatteryDev", ""); # aktuelles Meter device für Batteriewerte + my ($a,$h) = parseParams ($badev); + $badev = $a->[0] // ""; + return if(!$badev || !$defs{$badev}); + +return ($badev, $a ,$h); +} + ################################################################################################## # PV Forecast Rad1h in kWh / Wh # Berechnung nach Formel 1 aus http://www.ing-büro-junge.de/html/photovoltaik.html: @@ -7432,6 +7481,15 @@ Ein/Ausschaltzeiten sowie deren Ausführung vom SolarForecast Modul übernehmen
+
+
  • preferredChargeBattery
    + Es werden Verbraucher mit dem Mode can erst dann eingeschaltet, wenn die angegebene Batterieladung (%) + erreicht ist.
    + Verbraucher mit dem Mode must beachten die Vorrangladung der Batterie nicht.
    + (default: 0) +
  • +
    +
  • rainFactorDamping
    Prozentuale Berücksichtigung (Dämpfung) des Regenprognosefaktors bei der solaren Vorhersage.