diff --git a/fhem/contrib/DS_Starter/76_SolarForecast.pm b/fhem/contrib/DS_Starter/76_SolarForecast.pm index a98498c69..09f9302e6 100644 --- a/fhem/contrib/DS_Starter/76_SolarForecast.pm +++ b/fhem/contrib/DS_Starter/76_SolarForecast.pm @@ -160,7 +160,7 @@ BEGIN { # Versions History intern my %vNotesIntern = ( - "1.51.1" => "19.04.2025 consumer: interruptable, swoncond, swoffcond can be perl code enclosed by {..} ". + "1.51.1" => "19.04.2025 consumer: interruptable, swoncond, swoffcond, spignorecond can be perl code enclosed by {..} ". "check key is valid in plantControl, aiControl, flowGraphicControl, consumerControl, setupMeterDev ". "setupOtherProducer, setupInverterDev, setupBatteryDev, consumer ". "writeCacheToFile: bugfix - cache File on OS is deleted if cache is empty ", @@ -6136,7 +6136,7 @@ sub _attrconsumer { ## no critic "not used" } } - if (exists $h->{interruptable}) { # Check Regex,Code/Hysterese + if (exists $h->{interruptable}) { if ($h->{interruptable} !~ /^[01]$/xs) { my ($dev, $rd, $code, $hyst); @@ -6152,7 +6152,7 @@ sub _attrconsumer { ## no critic "not used" } if (!$dev || !$rd || !defined $code) { - return qq{A Device, Reading and Regex/Code must be specified for the 'interruptable' key!}; + return qq{A Device, Reading and Regex/Code must be specified for the 'interruptable' key}; } if ($code =~ m/^\s*\{.*\}\s*$/xs) { # interruptable prüft Perl-Code @@ -6171,8 +6171,12 @@ sub _attrconsumer { ## no critic "not used" } } - if (exists $h->{swoncond}) { # Check Regex - my (undef, undef, $code) = split ":", $h->{swoncond}, 3; + if (exists $h->{swoncond}) { + my ($dev, $rd, $code) = split ":", $h->{swoncond}, 3; + + if (!$dev || !$rd || !defined $code) { + return qq{A Device, Reading and Regex/Code must be specified for the 'swoncond' key}; + } if ($code =~ m/^\s*\{.*\}\s*$/xs) { # swoncond prüft Perl-Code $code =~ s/\s//xg; @@ -6185,8 +6189,12 @@ sub _attrconsumer { ## no critic "not used" } } - if (exists $h->{swoffcond}) { # Check Regex - my (undef, undef, $code) = split ":", $h->{swoffcond}, 3; + if (exists $h->{swoffcond}) { + my ($dev, $rd, $code) = split ":", $h->{swoffcond}, 3; + + if (!$dev || !$rd || !defined $code) { + return qq{A Device, Reading and Regex/Code must be specified for the 'swoffcond' key}; + } if ($code =~ m/^\s*\{.*\}\s*$/xs) { # swoffcond prüft Perl-Code $code =~ s/\s//xg; @@ -6198,6 +6206,24 @@ sub _attrconsumer { ## no critic "not used" return "swoffcond: $err" if($err); } } + + if (exists $h->{spignorecond}) { + my ($dev, $rd, $code) = split ":", $h->{spignorecond}, 3; + + if (!$dev || !$rd || !defined $code) { + return qq{A Device, Reading and Regex/Code must be specified for the 'spignorecond' key}; + } + + if ($code =~ m/^\s*\{.*\}\s*$/xs) { # spignorecond prüft Perl-Code + $code =~ s/\s//xg; + ($err) = checkCode ($name, $code); + return "spignorecond: $err" if($err); + } + else { # spignorecond prüft Regex + $err = checkRegex ($code); + return "spignorecond: $err" if($err); + } + } if (exists $h->{swstate}) { # Check Regex my (undef,$onregex,$offregex) = split ":", $h->{swstate}; @@ -8277,8 +8303,9 @@ sub centralTask { for my $c (1..MAXCONSUMER) { # 19.04.2025 $c = sprintf "%02d", $c; if (defined $data{$name}{consumers} && defined $data{$name}{consumers}{$c}) { - delete $data{$name}{consumers}{$c}{swoncondregex} if(exists $data{$name}{consumers}{$c}{swoncondregex}); - delete $data{$name}{consumers}{$c}{swoffcondregex} if(exists $data{$name}{consumers}{$c}{swoffcondregex}); + delete $data{$name}{consumers}{$c}{swoncondregex} if(exists $data{$name}{consumers}{$c}{swoncondregex}); + delete $data{$name}{consumers}{$c}{swoffcondregex} if(exists $data{$name}{consumers}{$c}{swoffcondregex}); + delete $data{$name}{consumers}{$c}{spignorecondregex} if(exists $data{$name}{consumers}{$c}{spignorecondregex}); } } } @@ -8618,9 +8645,9 @@ sub _collectAllRegConsumers { ($dswoffcond, $rswoffcond, $swoffcondition) = split ":", $hc->{swoffcond}, 3; } - my ($dspignorecond, $rigncond, $spignorecondregex); + my ($dspignorecond, $rigncond, $spignorecondition); if (exists $hc->{spignorecond}) { # Bedingung um vorhandenen PV Überschuß zu ignorieren - ($dspignorecond, $rigncond, $spignorecondregex) = split ":", $hc->{spignorecond}, 3; + ($dspignorecond, $rigncond, $spignorecondition) = split ":", $hc->{spignorecond}, 3; } my $interruptable = 0; @@ -8703,7 +8730,7 @@ sub _collectAllRegConsumers { $data{$name}{consumers}{$c}{swoffcondition} = $swoffcondition // q{}; # Regex einer vorrangigen Ausschaltbedingung $data{$name}{consumers}{$c}{dspignorecond} = $dspignorecond // q{}; # Device liefert Ignore Bedingung $data{$name}{consumers}{$c}{rigncond} = $rigncond // q{}; # Reading liefert Ignore Bedingung - $data{$name}{consumers}{$c}{spignorecondregex} = $spignorecondregex // q{}; # Regex der Ignore Bedingung + $data{$name}{consumers}{$c}{spignorecondition} = $spignorecondition // q{}; # Code/Regex der Ignore Bedingung $data{$name}{consumers}{$c}{interruptable} = $interruptable; # Ein-Zustand des Verbrauchers ist unterbrechbar $data{$name}{consumers}{$c}{hysteresis} = $hyst; # Hysterese $data{$name}{consumers}{$c}{sunriseshift} = $riseshift if(defined $riseshift); # Verschiebung (Sekunden) Sonnenaufgang bei SunPath Verwendung @@ -12164,7 +12191,11 @@ sub __setConsRcmdState { } 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}); + } if (!defined $surplus) { # $surplus kann undef sein! -> dann bisherigen isConsumptionRecommended verwenden $data{$name}{consumers}{$c}{isConsumptionRecommended} = ReadingsVal ($name, "consumer${c}_ConsumptionRecommended", 0); @@ -21481,36 +21512,58 @@ sub isSurplusIgnoCond { my $c = shift; my $debug = shift; + my $name = $hash->{NAME}; + my $igno = 0; my $info = q{}; my $digncond = ConsumerVal ($hash, $c, 'dspignorecond', ''); # Device zur Lieferung einer "Überschuß Ignore-Bedingung" - my ($err) = isDeviceValid ( { name => $hash->{NAME}, + my ($err) = isDeviceValid ( { name => $name, obj => $digncond, method => 'string', } ); if ($digncond && $err) { $err = qq{ERROR - the device "$digncond" doesn't exist! Check the key "spignorecond" in attribute "consumer${c}"}; - return (0, $info, $err); + return ($igno, $info, $err); } - $err = q{}; - my $rigncond = ConsumerVal ($hash, $c, 'rigncond', ''); # Reading zur Lieferung einer zusätzlichen Einschaltbedingung - my $spignorecondregex = ConsumerVal ($hash, $c, 'spignorecondregex', ''); # Regex einer zusätzliche Einschaltbedingung - my $condval = ReadingsVal ($digncond, $rigncond, ''); # Wert zum Vergleich mit Regex + $err = q{}; + my $rigncond = ConsumerVal ($hash, $c, 'rigncond', ''); # Reading zur Lieferung einer zusätzlichen Einschaltbedingung + my $ignorecode = ConsumerVal ($hash, $c, 'spignorecondition', ''); # Code/Regex für PV Überschuß Ignore + my $condval = ReadingsVal ($digncond, $rigncond, undef); # Wert zum Vergleich mit Code/Regex - if ($condval && $debug =~ /consumerSwitching${c}/x) { - my $name = $hash->{NAME}; - Log3 ($name, 1, qq{$name DEBUG> consumer "$c" - PV surplus ignore condition ist set - device: $digncond, reading: $rigncond, condition: $spignorecondregex}); + if ($debug =~ /consumerSwitching${c}/x && defined $condval) { + Log3 ($name, 1, qq{$name DEBUG> consumer "$c" - PV surplus ignore condition - device: $digncond, reading: $rigncond, condition: $ignorecode}); + } + + if (defined $condval) { + if ($ignorecode =~ m/^\{.*\}$/xs) { # wertet Perl-Code aus + my $VALUE = $condval; + my $true = eval $ignorecode; + + if ($@) { + Log3 ($name, 1, "$name - ERROR in surplus ignore condition Code execution: ".$@); + } + + if ($true) { + $info = qq{Value “$condval” resulted in 'true' after exec "$ignorecode" \n}; + $info .= "-> Check successful "; + $igno = 1; + } + else { + $info = qq{Value “$condval” resulted in 'false' after exec "$ignorecode" \n}; + $igno = 0; + } + } + elsif ($condval =~ m/^$ignorecode$/x) { # wertet Regex aus + $igno = 1; + } + else { + $info = qq{Value "$condval" doesn't match the condition: "$ignorecode"}; + } } - if ($condval && $condval =~ m/^$spignorecondregex$/x) { - return (1, $info, $err); - } - - $info = qq{The device "$digncond", reading "$rigncond" doesn't match the Regex "$spignorecondregex"}; - -return (0, $info, $err); +return ($igno, $info, $err); } ################################################################ @@ -24416,13 +24469,13 @@ to ensure that the system configuration is correct.
-
  • consumerXX <Device>[:<Alias>] type=<type> power=<power> [switchdev=<device>]
    - [mode=<mode>] [icon=<Icon>[@<Color>]] [mintime=<Option>]
    - [on=<command>] [off=<command>] [swstate=<Readingname>:<on-Regex>:<off-Regex>] [asynchron=<Option>]
    - [notbefore=<Expression>] [notafter=<Expression>] [locktime=<offlt>[:<onlt>]]
    - [auto=<Readingname>] [pcurr=<Readingname>:<Unit>[:<Threshold>]] [etotal=<Readingname>:<Einheit>[:<Threshold>]]
    - [swoncond=<Device>:<Reading>:<Condition>] [swoffcond=<Device>:<Reading>:<Condition>]
    - [spignorecond=<Device>:<Reading>:<Regex>] [surpmeth=<Option>] [interruptable=<Option>] [noshow=<Option>] [exconfc=<Option>]

    +
  • consumerXX <Device>[:<Alias>] type=<type> power=<power> [switchdev=<device>]
    + [mode=<mode>] [icon=<Icon>[@<Color>]] [mintime=<Option>]
    + [on=<command>] [off=<command>] [swstate=<Readingname>:<on-Regex>:<off-Regex>] [asynchron=<Option>]
    + [notbefore=<Expression>] [notafter=<Expression>] [locktime=<offlt>[:<onlt>]]
    + [auto=<Readingname>] [pcurr=<Readingname>:<Unit>[:<Threshold>]] [etotal=<Readingname>:<Einheit>[:<Threshold>]]
    + [swoncond=<Device>:<Reading>:<Condition>] [swoffcond=<Device>:<Reading>:<Condition>]
    + [spignorecond=<Device>:<Reading>:<Condition>] [surpmeth=<Option>] [interruptable=<Option>] [noshow=<Option>] [exconfc=<Option>]


    Registers a consumer <Device> with the SolarForecast Device. An optional alias can be specified.
    @@ -24567,7 +24620,10 @@ to ensure that the system configuration is correct. CAUTION: Using both keys spignorecond and interruptable can lead to undesired behaviour! Device - Device to deliver the condition Reading - Reading which contains the condition - Regex - regular expression that must be satisfied for a 'true' condition to be true + The condition can be formulated as a regular expression or as Perl code enclosed in {..}: + Regex - regular expression that must be fulfilled for a 'true' condition + {Perl-Code} - the Perl code enclosed in {..} must return 'true' to fulfill the condition. It must not contain spaces. + The value of Device:Reading is transferred to the code with the variable $VALUE. interruptable defines the possible interruption options for the consumer after it has been started (optional). Options can be: 0 - Load is not temporarily switched off even if the PV surplus falls below the required energy (default) @@ -24613,11 +24669,11 @@ to ensure that the system configuration is correct. Examples:
    attr <name> consumer01 wallplug icon=scene_dishwasher@orange type=dishwasher mode=can power=2500 on=on off=off notafter=20 etotal=total:kWh:5
    attr <name> consumer02 WPxw type=heater mode=can power=3000 mintime=180 on="on-for-timer 3600" notafter=12 auto=automatic
    - attr <name> consumer03 Shelly.shellyplug2 type=other power=300 mode=must icon=it_ups_on_battery mintime=120 on=on off=off swstate=state:on:off auto=automatic pcurr=relay_0_power:W etotal:relay_0_energy_Wh:Wh swoncond=EcoFlow:data_data_socSum:-?([1-7][0-9]|[0-9]) swoffcond:EcoFlow:data_data_socSum:{$VALUE==100?1:0}
    - attr <name> consumer04 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
    - attr <name> consumer05 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
    - attr <name> consumer06 Shelly.shellyplug5 icon=sani_buffer_electric_heater_side type=heater mode=must power=1000 notbefore=07:05 notafter={return'20:05'} auto=automatic pcurr=actpow:W on=on off=off mintime=SunPath:60:-120 interruptable=1
    - attr <name> consumer07 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=1
    + attr <name> consumer03 Shelly.shellyplug2 type=other power=300 mode=must icon=it_ups_on_battery mintime=120 on=on off=off swstate=state:on:off auto=automatic pcurr=relay_0_power:W etotal:relay_0_energy_Wh:Wh swoncond=EcoFlow:data_data_socSum:-?([1-7][0-9]|[0-9]) swoffcond:EcoFlow:data_data_socSum:{$VALUE==100?1:0}
    + attr <name> consumer04 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
    + attr <name> consumer05 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
    + attr <name> consumer06 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;}
    + attr <name> consumer07 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=1

  • @@ -26930,13 +26986,13 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
    -
  • consumerXX <Device>[:<Alias>] type=<type> power=<power> [switchdev=<device>]
    - [mode=<mode>] [icon=<Icon>[@<Farbe>]] [mintime=<Option>]
    - [on=<Kommando>] [off=<Kommando>] [swstate=<Readingname>:<on-Regex>:<off-Regex>] [asynchron=<Option>]
    - [notbefore=<Ausdruck>] [notafter=<Ausdruck>] [locktime=<offlt>[:<onlt>]]
    - [auto=<Readingname>] [pcurr=<Readingname>:<Einheit>[:<Schwellenwert>]] [etotal=<Readingname>:<Einheit>[:<Schwellenwert>]]
    - [swoncond=<Device>:<Reading>:<Bedingung>] [swoffcond=<Device>:<Reading>:<Bedingung>]
    - [spignorecond=<Device>:<Reading>:<Regex>] [surpmeth=<Option>] [interruptable=<Option>] [noshow=<Option>] [exconfc=<Option>]

    +
  • consumerXX <Device>[:<Alias>] type=<type> power=<power> [switchdev=<device>]
    + [mode=<mode>] [icon=<Icon>[@<Farbe>]] [mintime=<Option>]
    + [on=<Kommando>] [off=<Kommando>] [swstate=<Readingname>:<on-Regex>:<off-Regex>] [asynchron=<Option>]
    + [notbefore=<Ausdruck>] [notafter=<Ausdruck>] [locktime=<offlt>[:<onlt>]]
    + [auto=<Readingname>] [pcurr=<Readingname>:<Einheit>[:<Schwellenwert>]] [etotal=<Readingname>:<Einheit>[:<Schwellenwert>]]
    + [swoncond=<Device>:<Reading>:<Bedingung>] [swoffcond=<Device>:<Reading>:<Bedingung>]
    + [spignorecond=<Device>:<Reading>:<Bedingung>] [surpmeth=<Option>] [interruptable=<Option>] [noshow=<Option>] [exconfc=<Option>]


    Registriert einen Verbraucher <Device> beim SolarForecast Device. Ein optionaler Alias kann angegeben werden.
    @@ -27081,7 +27137,10 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden. ACHTUNG: Die Verwendung beider Schlüssel spignorecond und interruptable kann zu einem unerwünschten Verhalten führen! Device - Device zur Lieferung der Bedingung Reading - Reading welches die Bedingung enthält + Die Bedingung kann als regulärer Ausdruck oder als in {..} eingeschlossener Perl-Code formuliert sein: Regex - regulärer Ausdruck der für eine 'wahre' Bedingung erfüllt sein muß + {Perl-Code} - der in {..} eingeschlossene Perl-Code muß 'wahr' liefern um die Bedingung zu erfüllen. Er darf keine Leerzeichen enthalten. + Der Wert von Device:Reading wird dem Code mit der Variable $VALUE übergeben. interruptable definiert die möglichen Unterbrechungsoptionen für den Verbraucher nachdem er gestartet wurde (optional). Optionen können sein: 0 - Verbraucher wird nicht temporär ausgeschaltet auch wenn der PV Überschuß die benötigte Energie unterschreitet (default) @@ -27127,11 +27186,11 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden. Beispiele:
    attr <name> consumer01 wallplug icon=scene_dishwasher@orange type=dishwasher mode=can power=2500 on=on off=off notafter=20 etotal=total:kWh:5
    attr <name> consumer02 WPxw type=heater mode=can power=3000 mintime=180 on="on-for-timer 3600" notafter=12 auto=automatic
    - attr <name> consumer03 Shelly.shellyplug2 type=other power=300 mode=must icon=it_ups_on_battery mintime=120 on=on off=off swstate=state:on:off auto=automatic pcurr=relay_0_power:W etotal:relay_0_energy_Wh:Wh swoncond=EcoFlow:data_data_socSum:-?([1-7][0-9]|[0-9]) swoffcond:EcoFlow:data_data_socSum:{$VALUE==100?1:0}
    - attr <name> consumer04 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
    - attr <name> consumer05 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
    - attr <name> consumer06 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
    - attr <name> consumer07 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=1
    + attr <name> consumer03 Shelly.shellyplug2 type=other power=300 mode=must icon=it_ups_on_battery mintime=120 on=on off=off swstate=state:on:off auto=automatic pcurr=relay_0_power:W etotal:relay_0_energy_Wh:Wh swoncond=EcoFlow:data_data_socSum:-?([1-7][0-9]|[0-9]) swoffcond:EcoFlow:data_data_socSum:{$VALUE==100?1:0}
    + attr <name> consumer04 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
    + attr <name> consumer05 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
    + attr <name> consumer06 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;}
    + attr <name> consumer07 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=1