From d43851d2d23f19600bc52b77dfc9b31e601563cf Mon Sep 17 00:00:00 2001 From: nasseeder1 Date: Thu, 14 Jul 2022 19:34:15 +0000 Subject: [PATCH] 76_SolarForecast.pm: contrib 0.65.5 git-svn-id: https://svn.fhem.de/fhem/trunk@26228 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/contrib/DS_Starter/76_SolarForecast.pm | 246 +++++++++++++------- 1 file changed, 160 insertions(+), 86 deletions(-) diff --git a/fhem/contrib/DS_Starter/76_SolarForecast.pm b/fhem/contrib/DS_Starter/76_SolarForecast.pm index bc81cdbf5..175c84972 100644 --- a/fhem/contrib/DS_Starter/76_SolarForecast.pm +++ b/fhem/contrib/DS_Starter/76_SolarForecast.pm @@ -120,6 +120,8 @@ BEGIN { # Versions History intern my %vNotesIntern = ( + "0.65.5 "=> "13.07.2022 extend isInterruptable and isAddSwitchOffCond ", + "0.65.4 "=> "11.07.2022 new function isConsumerLogOn, minor fixes ", "0.65.3 "=> "10.07.2022 consumer with mode=must are now interruptable, change hourscsme ", "0.65.2 "=> "08.07.2022 change avgenergy to W p. hour ", "0.65.1 "=> "07.07.2022 change logic of __calcEnergyPieces function and the \%hef hash ", @@ -2323,7 +2325,7 @@ sub _specialActivities { $data{$type}{$name}{consumers}{$c}{onoff} = "off"; } - deleteReadingspec ($hash, "consumer.*_planned.*"); + # deleteReadingspec ($hash, "consumer.*_planned.*"); writeDataToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben @@ -2835,21 +2837,9 @@ sub _manageConsumerData { ## Verbraucher - Laufzeit und Zyklen pro Tag ermitteln ## Laufzeit (in Minuten) wird pro Stunde erfasst ## bei Tageswechsel Rücksetzen in _specialActivities - ####################################################### - my $nompower = ConsumerVal ($hash, $c, "power", 0); # nominale Leistung lt. Typenschild - my $rpcurr = ConsumerVal ($hash, $c, "rpcurr", ""); # Reading für akt. Verbrauch angegeben ? - - if (!$rpcurr && isConsumerPhysOn($hash, $c)) { # Workaround wenn Verbraucher ohne Leistungsmessung - $pcurr = $nompower; - } - - my $currpowerpercent = $pcurr; - $currpowerpercent = (($pcurr / $nompower) * 100) if($nompower > 0); - - $data{$type}{$name}{consumers}{$c}{currpowerpercent} = $currpowerpercent; - + ####################################################### my $starthour; - if($pcurr > $ethreshold || $currpowerpercent > $defpopercent) { # Verbraucher ist aktiv + if(isConsumerLogOn ($hash, $c, $pcurr)) { # Verbraucher ist logisch "an" if(ConsumerVal ($hash, $c, "onoff", "off") eq "off") { $data{$type}{$name}{consumers}{$c}{startTime} = $t; $data{$type}{$name}{consumers}{$c}{onoff} = "on"; @@ -3518,13 +3508,13 @@ sub ___switchConsumerOn { $state = qq{switching Consumer "$calias" to "$oncom"}; - writeDataToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben + writeDataToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben Log3 ($name, 2, "$name - $state (Automatic = $auto)"); } } - elsif (isInterruptible($hash, $c) && isConsRcmd ($hash, $c) && # unterbrochenen Consumer fortsetzen - isInTimeframe ($hash, $c) && simplifyCstate ($pstate) =~ /interrupted|interrupting/xs && + elsif (((isInterruptable($hash, $c) == 1 && isConsRcmd ($hash, $c)) || isInterruptable($hash, $c) == 3) && # unterbrochenen Consumer fortsetzen + isInTimeframe ($hash, $c) && simplifyCstate ($pstate) =~ /interrupted|interrupting/xs && $auto && $oncom) { CommandSet(undef,"$cname $oncom"); @@ -3535,7 +3525,8 @@ sub ___switchConsumerOn { delete $paref->{ps}; - $state = qq{switching Consumer "$calias" to "$oncom", caution: continuing by surplus}; + my $caution = isInterruptable($hash, $c) == 3 ? 'interrupt condition no longer present' : 'existing surplus'; + $state = qq{switching Consumer "$calias" to "$oncom", caution: $caution}; writeDataToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben @@ -3566,7 +3557,8 @@ sub ___switchConsumerOff { my $offcom = ConsumerVal ($hash, $c, "offcom", ""); # Set Command für "off" my ($swoffcond,$info,$err) = isAddSwitchOffCond ($hash, $c); # zusätzliche Switch on Bedingung - + my $caution; + Log3 ($name, 1, "$name - $err") if($err); if($debug) { # nur für Debugging @@ -3587,15 +3579,15 @@ sub ___switchConsumerOff { delete $paref->{ps}; - my $caution = $swoffcond ? "switch-off condition (key swoffcond) is true" : "planned switch-off time reached/exceeded"; - $state = qq{switching Consumer "$calias" to "$offcom", caution: $caution}; + $caution = $swoffcond ? "switch-off condition (key swoffcond) is true" : "planned switch-off time reached/exceeded"; + $state = qq{switching Consumer "$calias" to "$offcom", caution: $caution}; - writeDataToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben + writeDataToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben Log3 ($name, 2, "$name - $state (Automatic = $auto)"); } - elsif (isInterruptible($hash, $c) && !isConsRcmd ($hash, $c) && # Consumer unterbrechen - isInTimeframe ($hash, $c) && simplifyCstate ($pstate) =~ /started|continued|interrupting/xs && + elsif (((isInterruptable($hash, $c) == 1 && !isConsRcmd ($hash, $c)) || isInterruptable($hash, $c) == 2) && # Consumer unterbrechen + isInTimeframe ($hash, $c) && simplifyCstate ($pstate) =~ /started|continued|interrupting/xs && $auto && $offcom) { CommandSet(undef,"$cname $offcom"); @@ -3606,7 +3598,8 @@ sub ___switchConsumerOff { delete $paref->{ps}; - $state = qq{switching Consumer "$calias" to "$offcom", caution: surplus shortage}; + $caution = isInterruptable($hash, $c) == 2 ? 'interrupt condition' : 'surplus shortage'; + $state = qq{switching Consumer "$calias" to "$offcom", caution: $caution}; writeDataToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben @@ -3654,7 +3647,7 @@ sub ___setConsumerSwitchingState { writeDataToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben - Log3 ($name, 2, "$name - $state (Automatic = $auto)"); + Log3 ($name, 2, "$name - $state"); } elsif ($pstate eq 'stopping' && isConsumerPhysOff ($hash, $c)) { $paref->{ps} = "switched off:"; @@ -3669,7 +3662,7 @@ sub ___setConsumerSwitchingState { writeDataToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben - Log3 ($name, 2, "$name - $state (Automatic = $auto)"); + Log3 ($name, 2, "$name - $state"); } elsif ($pstate eq 'continuing' && isConsumerPhysOn ($hash, $c)) { $paref->{ps} = "continued:"; @@ -4424,7 +4417,7 @@ sub collectAllRegConsumers { my $interruptable = 0; if(exists $hc->{interruptable}) { - $interruptable = 1 if($hc->{interruptable} ne '0'); + $interruptable = $hc->{interruptable} if($hc->{interruptable} ne '0'); } my $rauto = $hc->{auto} // q{}; @@ -7413,7 +7406,7 @@ sub isConsumerPhysOn { if(!$defs{$cname}) { Log3($name, 1, qq{$name - the consumer device "$cname" is invalid, the "on" state can't be identified}); - return; + return 0; } my $reg = ConsumerVal ($hash, $c, "onreg", "on"); @@ -7424,11 +7417,11 @@ sub isConsumerPhysOn { return 1; } -return; +return 0; } ################################################################ -# Funktion liefert 1 wenn Consumer physisch "ausngeschaltet" +# Funktion liefert 1 wenn Consumer physisch "ausgeschaltet" # ist, d.h. der Wert offreg des Readings rswstate wahr ist ################################################################ sub isConsumerPhysOff { @@ -7440,7 +7433,7 @@ sub isConsumerPhysOff { if(!$defs{$cname}) { Log3($name, 1, qq{$name - the consumer device "$cname" is invalid, the "off" state can't be identified}); - return; + return 0; } my $reg = ConsumerVal ($hash, $c, "offreg", "off"); @@ -7451,7 +7444,53 @@ sub isConsumerPhysOff { return 1; } -return; +return 0; +} + +################################################################ +# Funktion liefert 1 wenn Consumer logisch "eingeschaltet" +# ist, d.h. wenn der Energieverbrauch über einem bestimmten +# Schwellenwert oder der prozentuale Verbrauch über dem +# Defaultwert $defpopercent ist. +# +# Logisch "on" schließt physisch "on" mit ein. +################################################################ +sub isConsumerLogOn { + my $hash = shift; + my $c = shift; + my $pcurr = shift // 0; + + my $name = $hash->{NAME}; + my $cname = ConsumerVal ($hash, $c, "name", ""); # Devicename Customer + + if(!$defs{$cname}) { + Log3($name, 1, qq{$name - the consumer device "$cname" is invalid, the "on" state can't be identified}); + return 0; + } + + if(isConsumerPhysOff($hash, $c)) { # Device ist physisch ausgeschaltet + return 0; + } + + my $type = $hash->{TYPE}; + my $nompower = ConsumerVal ($hash, $c, "power", 0); # nominale Leistung lt. Typenschild + my $rpcurr = ConsumerVal ($hash, $c, "rpcurr", ""); # Reading für akt. Verbrauch angegeben ? + my $ethreshold = ConsumerVal ($hash, $c, "energythreshold", 0); # Schwellenwert (Wh pro Stunde) ab der ein Verbraucher als aktiv gewertet wird + + if (!$rpcurr && isConsumerPhysOn($hash, $c)) { # Workaround wenn Verbraucher ohne Leistungsmessung + $pcurr = $nompower; + } + + my $currpowerpercent = $pcurr; + $currpowerpercent = ($pcurr / $nompower) * 100 if($nompower > 0); + + $data{$type}{$name}{consumers}{$c}{currpowerpercent} = $currpowerpercent; + + if($pcurr > $ethreshold || $currpowerpercent > $defpopercent) { # Verbraucher ist logisch aktiv + return 1; + } + +return 0; } ################################################################ @@ -7490,8 +7529,11 @@ return (0, $info, $err); } ################################################################ -# Funktion liefert "1" wenn die vorrangige Ausschaltbedingung -# aus dem Schlüssel "swoffcond" im Consumer Attribut wahr ist +# Funktion liefert "1" wenn eine Ausschaltbedingung +# erfüllt ist +# ("swoffcond" oder "interruptable" im Consumer Attribut) +# Der Inhalt von "interruptable" wird optional in $cond +# übergeben. # # $info - den Info-Status # $err - einen Error-Status @@ -7500,22 +7542,31 @@ return (0, $info, $err); sub isAddSwitchOffCond { my $hash = shift; my $c = shift; + my $cond = shift // q{}; - my $info = q{}; - my $err = q{}; + my $info = q{}; + my $err = q{}; + my $dswoffcond = q{}; # Device zur Lieferung einer Ausschaltbedingung + my $rswoffcond = q{}; # Reading zur Lieferung einer Ausschaltbedingung + my $swoffcondregex = q{}; # Regex der Ausschaltbedingung (wenn wahr) - my $dswoffcond = ConsumerVal ($hash, $c, "dswoffcond", ""); # Device zur Lieferung einer vorrangigen Ausschaltbedingung + if ($cond) { + ($dswoffcond,$rswoffcond,$swoffcondregex) = split ":", $cond; + } + else { + $dswoffcond = ConsumerVal ($hash, $c, "dswoffcond", ""); + $rswoffcond = ConsumerVal ($hash, $c, "rswoffcond", ""); + $swoffcondregex = ConsumerVal ($hash, $c, "swoffcondregex", ""); + } if($dswoffcond && !$defs{$dswoffcond}) { - $err = qq{ERROR - the device "$dswoffcond" doesn't exist! Check the key "swoffcond" in attribute "consumer${c}"}; + $err = qq{ERROR - the device "$dswoffcond" doesn't exist! Check the key "swoffcond" or "interruptable" in attribute "consumer${c}"}; return (0, $info, $err); - } + } - my $rswoffcond = ConsumerVal ($hash, $c, "rswoffcond", ""); # Reading zur Lieferung einer vorrangigen Ausschaltbedingung - my $swoffcondregex = ConsumerVal ($hash, $c, "swoffcondregex", ""); # Regex einer vorrangigen Ausschaltbedingung - my $condstate = ReadingsVal ($dswoffcond, $rswoffcond, ""); + my $condstate = ReadingsVal ($dswoffcond, $rswoffcond, ""); - if ($condstate && $condstate =~ m/^$swoffcondregex$/x) { + if ($condstate && $condstate =~ m/^$swoffcondregex$/x) { return (1, $info, $err); } @@ -7545,13 +7596,31 @@ return ConsumerVal ($hash, $c, 'isConsumptionRecommended', 0); } ################################################################ -# ist Consumer $c unterbrechbar (1) oder nicht (0) +# ist Consumer $c unterbrechbar (1|2) oder nicht (0|3) ################################################################ -sub isInterruptible { +sub isInterruptable { my $hash = shift; my $c = shift; -return ConsumerVal ($hash, $c, 'interruptable', 0); + my $intable = ConsumerVal ($hash, $c, 'interruptable', 0); + + if ($intable eq '0') { + return 0; + } + elsif ($intable eq '1') { + return 1; + } + + my ($swoffcond,$info,$err) = isAddSwitchOffCond ($hash, $c, $intable); + + if ($swoffcond) { + return 2; + } + else { + return 3; + } + +return; } ################################################################ @@ -8608,7 +8677,7 @@ Ein/Ausschaltzeiten sowie deren Ausführung vom SolarForecast Modul übernehmen
  • consumerXX <Device Name> type=<type> power=<power> [mode=<mode>] [icon=<Icon>] [mintime=<minutes>]
    [on=<Kommando>] [off=<Kommando>] [swstate=<Readingname>:<on-Regex>:<off-Regex>] [notbefore=<Stunde>] [notafter=<Stunde>]
    [auto=<Readingname>] [pcurr=<Readingname>:<Einheit>[:<Schwellenwert>]] [etotal=<Readingname>:<Einheit>[:<Schwellenwert>]]
    - [swoncond=<Device>:<Reading>:<Regex>] [swoffcond=<Device>:<Reading>:<Regex>] [interruptable=0|1]


    + [swoncond=<Device>:<Reading>:<Regex>] [swoffcond=<Device>:<Reading>:<Regex>] [interruptable=<Option>]

    Registriert einen Verbraucher <Device Name> beim SolarForecast Device. Dabei ist <Device Name> ein in FHEM bereits angelegtes Verbraucher Device, z.B. eine Schaltsteckdose. @@ -8630,48 +8699,53 @@ Ein/Ausschaltzeiten sowie deren Ausführung vom SolarForecast Modul übernehmen Mit dem optionalen Schlüssel interruptable kann während der geplanten Einschaltzeit eine automatische Unterbrechung sowie Wiedereinschaltung des Verbrauchers vorgenommen werden. - Unterschreitet der PV Überschuß die benötigte Energie, wird der Verbraucher ausgeschaltet (interrupted) und - eingeschaltet wenn wieder ausreichend PV Überschuß vorhanden ist (continued). + Der Verbraucher wird temporär ausgeschaltet (interrupted) und wieder eingeschaltet (continued) wenn die + Interrupt-Bedingung nicht mehr vorliegt. Die verbleibende Laufzeit wird durch einen Interrupt nicht beeinflusst !

    +