From f825e086f33f373d23b59a9bd398ff035d4b66ad Mon Sep 17 00:00:00 2001 From: nasseeder1 Date: Sun, 1 May 2022 13:34:49 +0000 Subject: [PATCH] 76_SolarForecast.pm: contrib 0.59.0 git-svn-id: https://svn.fhem.de/fhem/trunk@26013 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/contrib/DS_Starter/76_SolarForecast.pm | 439 ++++++++++++++------ 1 file changed, 316 insertions(+), 123 deletions(-) diff --git a/fhem/contrib/DS_Starter/76_SolarForecast.pm b/fhem/contrib/DS_Starter/76_SolarForecast.pm index e9caf1e76..437d9f106 100644 --- a/fhem/contrib/DS_Starter/76_SolarForecast.pm +++ b/fhem/contrib/DS_Starter/76_SolarForecast.pm @@ -3,7 +3,7 @@ ######################################################################################################################### # 76_SolarForecast.pm # -# (c) 2020-2021 by Heiko Maaz e-mail: Heiko dot Maaz at t-online dot de +# (c) 2020-2022 by Heiko Maaz e-mail: Heiko dot Maaz at t-online dot de # # This script is part of fhem. # @@ -120,7 +120,8 @@ BEGIN { # Versions History intern my %vNotesIntern = ( - "0.58.0 "=> "10.04.2022 new setter consumerImmediatePlanning ", + "0.59.0 "=> "01.05.2022 new attr createTomorrowPVFcReadings ", + "0.58.0 "=> "20.04.2022 new setter consumerImmediatePlanning, functions isConsumerOn isConsumerOff ", "0.57.3 "=> "10.04.2022 some fixes (\$eavg in ___csmSpecificEpieces, useAutoCorrection switch to regex) ", "0.57.2 "=> "03.04.2022 area factor for 25° added ", "0.57.1 "=> "28.02.2022 new attr flowGraphicShowConsumerPower and flowGraphicShowConsumerRemainTime (Consumer remainTime in flowGraphic)", @@ -334,8 +335,8 @@ my %hqtxt = ( DE => qq{Warten auf Solarvorhersagedaten ...} }, cnsm => { EN => qq{Consumer}, DE => qq{Verbraucher} }, - eiau => { EN => qq{On/Off}, - DE => qq{Ein/Aus} }, + eiau => { EN => qq{Off/On}, + DE => qq{Aus/Ein} }, auto => { EN => qq{Auto}, DE => qq{Auto} }, pstate => { EN => qq{Planning status: 
On: 
Off: }, @@ -348,35 +349,35 @@ my %hqtxt = ( my %htitles = ( # Hash Hilfetexte (Mouse Over) iaaf => { EN => qq{Automatic mode off -> Enable automatic mode}, - DE => qq{Automatikmodus aus -> Automatik freigeben} }, + DE => qq{Automatikmodus aus -> Automatik freigeben} }, ieas => { EN => qq{Automatic mode on -> Lock automatic mode}, - DE => qq{Automatikmodus ein -> Automatik sperren} }, + DE => qq{Automatikmodus ein -> Automatik sperren} }, iave => { EN => qq{Off -> Switch on consumer}, - DE => qq{Aus -> Verbraucher einschalten} }, + DE => qq{Aus -> Verbraucher einschalten} }, ians => { EN => qq{Off -> no on-command defined!}, - DE => qq{Aus -> kein on-Kommando definiert!} }, + DE => qq{Aus -> kein on-Kommando definiert!} }, ieva => { EN => qq{On -> Switch off consumer}, - DE => qq{Ein -> Verbraucher ausschalten} }, + DE => qq{Ein -> Verbraucher ausschalten} }, iens => { EN => qq{On -> no off-command defined!}, - DE => qq{Ein -> kein off-Kommando definiert!} }, + DE => qq{Ein -> kein off-Kommando definiert!} }, upd => { EN => qq{Update}, - DE => qq{Update} }, + DE => qq{Update} }, on => { EN => qq{switched on}, - DE => qq{eingeschaltet} }, + DE => qq{eingeschaltet} }, off => { EN => qq{switched off}, - DE => qq{ausgeschaltet} }, + DE => qq{ausgeschaltet} }, undef => { EN => qq{undefined}, - DE => qq{undefiniert} }, + DE => qq{undefiniert} }, dela => { EN => qq{delayed}, - DE => qq{verzoegert} }, + DE => qq{verzoegert} }, conrec => { EN => qq{Current time is within the consumption planning}, - DE => qq{Aktuelle Zeit liegt innerhalb der Verbrauchsplanung} }, - connorec => { EN => qq{Consumption planning is outside current time}, - DE => qq{Verbrauchsplanung liegt ausserhalb aktueller Zeit} }, + DE => qq{Aktuelle Zeit liegt innerhalb der Verbrauchsplanung} }, + connorec => { EN => qq{Consumption planning is outside current time\n(Click for immediate planning)}, + DE => qq{Verbrauchsplanung liegt ausserhalb aktueller Zeit\n(Click für sofortige Einplanung)} }, pstate => { EN => qq{Planning status: \n\nOn: \nOff: }, - DE => qq{Planungsstatus: \n\nEin: \nAus: } }, + DE => qq{Planungsstatus: \n\nEin: \nAus: } }, akorron => { EN => qq{Enable auto correction with:\nset pvCorrectionFactor_Auto on}, - DE => qq{Einschalten Autokorrektur mit:\nset pvCorrectionFactor_Auto on} }, + DE => qq{Einschalten Autokorrektur mit:\nset pvCorrectionFactor_Auto on} }, ); my %weather_ids = ( @@ -524,7 +525,7 @@ my @ctypes = qw(dishwasher dryer washingmachine heater other); my $defmintime = 60; # default min. Einschalt- bzw. Zykluszeit in Minuten my $defctype = "other"; # default Verbrauchertyp my $defcmode = "can"; # default Planungsmode der Verbraucher -my $caicondef = 'light_light_dim_100@gold'; # default consumerAdviceIcon +my $caicondef = 'clock@gold'; # default consumerAdviceIcon my $defflowGSize = 300; # default flowGraphicSize my $defpopercent = 0.5; # Standard % aktuelle Leistung an nominaler Leistung gemäß Typenschild @@ -570,6 +571,7 @@ sub Initialize { my $hash = shift; my $fwd = join ",", devspec2array("TYPE=FHEMWEB:FILTER=STATE=Initialized"); + my $hod = join ",", map { sprintf "%02d", $_} (01..24); my $consumer; for my $c (1..$maxconsumer) { @@ -598,9 +600,10 @@ sub Initialize { "beam2FontColor:colorpicker,RGB ". "beamHeight ". "beamWidth ". + "cloudFactorDamping:slider,0,1,100 ". "consumerLegend:none,icon_top,icon_bottom,text_top,text_bottom ". "consumerAdviceIcon ". - "cloudFactorDamping:slider,0,1,100 ". + "createTomorrowPVFcReadings:multiple-strict,$hod ". "Css:textField-long ". "debug:1,0 ". "disable:1,0 ". @@ -615,7 +618,7 @@ sub Initialize { "graphicSelect:both,flow,forecast,none ". "headerDetail:all,co,pv,pvco,statusLink ". "historyHour:slider,-23,-1,0 ". - "hourCount:slider,4,1,24 ". + "hourCount:slider,4,1,24 ". "hourStyle ". "htmlStart ". "htmlEnd ". @@ -632,7 +635,7 @@ sub Initialize { "showLink:1,0 ". "showNight:1,0 ". "showWeather:1,0 ". - "spaceSize ". + "spaceSize ". "Wh/kWh:Wh,kWh ". "weatherColor:colorpicker,RGB ". "weatherColorNight:colorpicker,RGB ". @@ -816,30 +819,38 @@ return "$setlist"; # Setter consumerImmediatePlanning ################################################################ sub _setconsumerImmediatePlanning { ## no critic "not used" - my $paref = shift; - my $hash = $paref->{hash}; - my $name = $paref->{name}; - my $type = $paref->{type}; - my $opt = $paref->{opt}; - my $c = $paref->{arg}; + my $paref = shift; + my $hash = $paref->{hash}; + my $name = $paref->{name}; + my $type = $paref->{type}; + my $opt = $paref->{opt}; + my $arg = $paref->{arg}; + + my ($c,$noTaskcall) = split " ", $arg; return qq{no consumer number specified} if(!$c); return qq{no valid consumer id "$c"} if(!ConsumerVal ($hash, $c, "name", "")); - my $startts = time; - my (undef,undef,undef,$starttime) = timestampToTimestring ($startts); - - my $mintime = ConsumerVal ($hash, $c, "mintime", $defmintime); - my $stopdiff = ceil ($mintime * 60); - my $stopts = $startts + $stopdiff; + my $startts = time; + my $mintime = ConsumerVal ($hash, $c, "mintime", $defmintime); + my $stopdiff = ceil ($mintime * 60); + my $stopts = $startts + $stopdiff; - my (undef,undef,undef,$stoptime) = timestampToTimestring ($stopts); - - $data{$type}{$name}{consumers}{$c}{planstate} = "planned: ".$starttime." - ".$stoptime; - $data{$type}{$name}{consumers}{$c}{planswitchon} = $startts; # Unix Timestamp für geplanten Switch on - $data{$type}{$name}{consumers}{$c}{planswitchoff} = $stopts; # Unix Timestamp für geplanten Switch off + $paref->{consumer} = $c; + $paref->{ps} = "planned:"; + $paref->{startts} = $startts; # Unix Timestamp für geplanten Switch on + $paref->{stopts} = $stopts; # Unix Timestamp für geplanten Switch off - _getdata ($paref); + ___setPlanningState ($paref); + + my $planstate = ConsumerVal ($hash, $c, "planstate", ""); + my $calias = ConsumerVal ($hash, $c, "alias", ""); + + writeDataToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben + + Log3 ($name, 3, qq{$name - Consumer "$calias" $planstate}) if($planstate); + + centralTask ($hash) if(!$noTaskcall); return; } @@ -1460,11 +1471,15 @@ sub _setconsumerAction { ## no critic "not used" 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"); + CommandSet (undef, "$cname $tail"); } if($action eq "setreading") { - CommandSetReading (undef,"$cname $tail"); + CommandSetReading (undef, "$cname $tail"); + } + + if($action eq "consumerImmediatePlanning") { + CommandSet (undef, "$name $action $cname noTaskcall"); } Log3($name, 4, qq{$name - Consumer Action received / executed: "$action $cname $tail"}); @@ -2001,9 +2016,9 @@ sub centralTask { # delete $data{$type}{$name}{pvhist}{$i} if(!$i); # evtl. vorhandene leere Schlüssel entfernen #} - for my $c (keys %{$data{$type}{$name}{consumers}}) { - delete $data{$type}{$name}{consumers}{$c}{OnOff}; - } + #for my $c (keys %{$data{$type}{$name}{consumers}}) { + # delete $data{$type}{$name}{consumers}{$c}{OnOff}; + #} #deleteReadingspec ($hash, "Today_Hour.*_Consumption"); #deleteReadingspec ($hash, "ThisHour_.*"); @@ -2080,6 +2095,7 @@ sub centralTask { _manageConsumerData ($centpars); # Consumerdaten sammeln und planen _estConsumptionForecast ($centpars); # erwarteten Verbrauch berechnen _evaluateThresholds ($centpars); # Schwellenwerte bewerten und signalisieren + _calcReadingsTomorrowPVFc ($centpars); # zusätzliche Readings Tomorrow_HourXX_PVforecast berechnen _calcSummaries ($centpars); # Zusammenfassungen erstellen if(@da) { @@ -2329,13 +2345,16 @@ sub _transferDWDForecastValues { }; my $calcpv = calcPVforecast ($params); # Vorhersage gewichtet kalkulieren - + $time_str = "NextHour".sprintf "%02d", $num; $epoche = $t + (3600*$num); my ($ta,$tsdef,$realts) = timestampToTimestring ($epoche); + my ($hod) = $tsdef =~ /\s(\d{2}):/xs; # Stunde des Tages + $hod = sprintf "%02d", int ($hod)+1; $data{$type}{$name}{nexthours}{$time_str}{pvforecast} = $calcpv; $data{$type}{$name}{nexthours}{$time_str}{starttime} = $tsdef; + $data{$type}{$name}{nexthours}{$time_str}{hourofday} = $hod; $data{$type}{$name}{nexthours}{$time_str}{today} = $fd == 0 ? 1 : 0; $data{$type}{$name}{nexthours}{$time_str}{Rad1h} = $rad; # nur Info: original Vorhersage Strahlungsdaten @@ -2779,9 +2798,9 @@ sub _manageConsumerData { ####################################################### my $nompower = ConsumerVal ($hash, $c, "power", 0); # nominale Leistung lt. Typenschild my $rpcurr = ConsumerVal ($hash, $c, "rpcurr", ""); # Reading für akt. Verbrauch angegeben ? - my $swstate = ConsumerVal ($hash, $c, "state", "undef"); # Schaltzustand des Consumerdevices + #my $swstate = ConsumerVal ($hash, $c, "state", "undef"); # Schaltzustand des Consumerdevices - if (!$rpcurr && $swstate eq "on") { # Workaround wenn Verbraucher ohne Leistungsmessung + if (!$rpcurr && isConsumerOn($hash, $c)) { # Workaround wenn Verbraucher ohne Leistungsmessung $pcurr = $nompower; } @@ -2871,7 +2890,8 @@ sub _manageConsumerData { ## consumer Hash ergänzen, Reading generieren ############################################### - my $costate = ReadingsVal ($consumer, "state", ""); + my $rswstate = ConsumerVal ($hash, $c, "rswstate", "state"); # Reading mit Schaltstatus + my $costate = ReadingsVal ($consumer, $rswstate, ""); # Schaltstatus my ($pstate,$starttime,$stoptime) = __planningStateAndTimes ($paref); $data{$type}{$name}{consumers}{$c}{state} = $costate; @@ -3147,25 +3167,34 @@ sub __planSwitchTimes { for my $ts (sort{$a<=>$b} keys %mtimes) { if($mtimes{$ts}{surplus} >= $epiece1) { # die früheste Startzeit sofern Überschuß größer als Bedarf - my $starttime = $mtimes{$ts}{starttime}; - $paref->{starttime} = $starttime; - $starttime = ___switchonTimelimits ($paref); + my $starttime = $mtimes{$ts}{starttime}; + $paref->{starttime} = $starttime; + $starttime = ___switchonTimelimits ($paref); delete $paref->{starttime}; - my $startts = timestringToTimestamp ($starttime); # Unix Timestamp für geplanten Switch on - my $stopts = $startts + $stopdiff; - my (undef,undef,undef,$stoptime) = timestampToTimestring ($stopts); + my $startts = timestringToTimestamp ($starttime); # Unix Timestamp für geplanten Switch on + + $paref->{ps} = "planned:"; + $paref->{startts} = $startts; + $paref->{stopts} = $startts + $stopdiff; + + ___setPlanningState ($paref); + + delete $paref->{ps}; + delete $paref->{startts}; + delete $paref->{stopts}; - $data{$type}{$name}{consumers}{$c}{planswitchon} = $startts; - $data{$type}{$name}{consumers}{$c}{planswitchoff} = $stopts; - $data{$type}{$name}{consumers}{$c}{planstate} = "planned: ".$starttime." - ".$stoptime; last; } else { - $data{$type}{$name}{consumers}{$c}{planstate} = "not planned: the max expected surplus is less $epiece1"; + $paref->{ps} = "no planning: the max expected surplus is less $epiece1"; + + ___setPlanningState ($paref); + + delete $paref->{ps}; } - } + } } else { # Verbraucher _muß_ geplant werden if($debug) { # nur für Debugging @@ -3198,6 +3227,42 @@ sub __planSwitchTimes { return; } +################################################################ +# Planungsdaten setzen +################################################################ +sub ___setPlanningState { + my $paref = shift; + my $hash = $paref->{hash}; + my $c = $paref->{consumer}; + my $ps = $paref->{ps}; # Planstatus + my $startts = $paref->{startts}; # Unix Timestamp für geplanten Switch on + my $stopts = $paref->{stopts}; # Unix Timestamp für geplanten Switch off + + my $type = $hash->{TYPE}; + my $name = $hash->{NAME}; + + my ($starttime,$stoptime); + + if ($startts) { + (undef,undef,undef,$starttime) = timestampToTimestring ($startts); + $data{$type}{$name}{consumers}{$c}{planswitchon} = $startts; + } + + if ($stopts) { + (undef,undef,undef,$stoptime) = timestampToTimestring ($stopts); + $data{$type}{$name}{consumers}{$c}{planswitchoff} = $stopts; + } + + $ps .= " " if ($starttime || $stoptime); + $ps .= $starttime if ($starttime); + $ps .= $stoptime if (!$starttime && $stoptime); + $ps .= " - ".$stoptime if ($starttime && $stoptime); + + $data{$type}{$name}{consumers}{$c}{planstate} = $ps; + +return; +} + ################################################################ # Consumer Zeiten MUST planen ################################################################ @@ -3224,11 +3289,16 @@ sub ___planMust { $startts = timestringToTimestamp ($starttime); my $stopts = $startts + $stopdiff; - my (undef,undef,undef,$stoptime) = timestampToTimestring ($stopts); - $data{$type}{$name}{consumers}{$c}{planstate} = "planned: ".$starttime." - ".$stoptime; - $data{$type}{$name}{consumers}{$c}{planswitchon} = $startts; # Unix Timestamp für geplanten Switch on - $data{$type}{$name}{consumers}{$c}{planswitchoff} = $stopts; # Unix Timestamp für geplanten Switch off + $paref->{ps} = "planned:"; + $paref->{startts} = $startts; # Unix Timestamp für geplanten Switch on + $paref->{stopts} = $stopts; # Unix Timestamp für geplanten Switch off + + ___setPlanningState ($paref); + + delete $paref->{ps}; + delete $paref->{startts}; + delete $paref->{stopts}; return; } @@ -3291,8 +3361,6 @@ sub __switchConsumer { my $cname = ConsumerVal ($hash, $c, "name", ""); # Consumer Device Name my $calias = ConsumerVal ($hash, $c, "alias", ""); # Consumer Device Alias - my $stoptime; - ## Ist Verbraucher empfohlen ? ################################ if ($startts && $t >= $startts && $stopts && $t <= $stopts) { @@ -3315,20 +3383,31 @@ sub __switchConsumer { Log3 ($name, 4, "$name - Consumer switch enabled by battery: $enable"); - if($mode eq "can" && !$enable) { - $data{$type}{$name}{consumers}{$c}{planstate} = "priority charging battery"; + if ($mode eq "can" && !$enable) { + $paref->{ps} = "priority charging battery"; + + ___setPlanningState ($paref); + + delete $paref->{ps}; } - elsif($mode eq "must" || $surplus >= $power) { # "Muss"-Planung oder Überschuß > Leistungsaufnahme + 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; - (undef,undef,undef,$stoptime) = timestampToTimestring ($t + $stopdiff); - $data{$type}{$name}{consumers}{$c}{planstate} = "switched on: ".$starttime." - ".$stoptime; - $data{$type}{$name}{consumers}{$c}{planswitchon} = $t; - $data{$type}{$name}{consumers}{$c}{planswitchoff} = $t + $stopdiff; - $state = qq{Consumer "$calias" switched on}; + my $stopdiff = ceil(ConsumerVal ($hash, $c, "mintime", $defmintime) / 60) * 3600; + + $paref->{ps} = "switched on:"; + $paref->{startts} = $t; + $paref->{stopts} = $t + $stopdiff; + + ___setPlanningState ($paref); + + delete $paref->{ps}; + delete $paref->{startts}; + delete $paref->{stopts}; + + $state = qq{Consumer "$calias" switched on}; writeDataToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben + Log3 ($name, 2, "$name - $state (Automatic = $auto)"); } } @@ -3338,12 +3417,19 @@ sub __switchConsumer { my $offcom = ConsumerVal ($hash, $c, "offcom", ""); # Set Command für "off" if($auto && $offcom && $pstate !~ /switched\soff/xs && $stopts && $t >= $stopts) { # Verbraucher nicht switched off && Stopzeit überschritten CommandSet(undef,"$cname $offcom"); - (undef,undef,undef,$stoptime) = timestampToTimestring ($t); - $data{$type}{$name}{consumers}{$c}{planstate} = "switched off: ".$stoptime; - $data{$type}{$name}{consumers}{$c}{planswitchoff} = $t; # tatsächliche Ausschaltzeit - $state = qq{Consumer "$calias" switched off}; + + $paref->{ps} = "switched off:"; + $paref->{stopts} = $t; + + ___setPlanningState ($paref); + + delete $paref->{ps}; + delete $paref->{stopts}; + + $state = qq{Consumer "$calias" switched off}; writeDataToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben + Log3 ($name, 2, "$name - $state (Automatic = $auto)"); } @@ -3351,16 +3437,15 @@ sub __switchConsumer { ###################################### my ($planstate,$startstr,$stoptstr) = __planningStateAndTimes ($paref); my $isConsRecommended = ConsumerVal ($hash, $c, "isConsumptionRecommended", 0); - my $costate = ConsumerVal ($hash, $c, "state", "off"); + # my $costate = ConsumerVal ($hash, $c, "state", "off"); $data{$type}{$name}{consumers}{$c}{remainTime} = 0; - if ($isConsRecommended && $planstate eq "started" && $costate eq "on") { + if ($isConsRecommended && $planstate eq "started" && isConsumerOn($hash, $c)) { my $remainTime = $stopts - $t ; $data{$type}{$name}{consumers}{$c}{remainTime} = sprintf "%.0f", ($remainTime / 60) if($remainTime > 0); } - $paref->{state} = $state; return; @@ -3782,6 +3867,43 @@ sub __evaluateArray { return; } +################################################################ +# zusätzliche Readings Tomorrow_HourXX_PVforecast +# berechnen +################################################################ +sub _calcReadingsTomorrowPVFc { + my $paref = shift; + my $hash = $paref->{hash}; + my $name = $paref->{name}; + my $daref = $paref->{daref}; + + my $type = $hash->{TYPE}; + + deleteReadingspec ($hash, "Tomorrow_Hour.*"); + + my $h = $data{$type}{$name}{nexthours}; + my $hods = AttrVal($name, 'createTomorrowPVFcReadings', ''); + return if(!keys %{$h} || !$hods); + + for my $idx (sort keys %{$h}) { + my $today = NexthoursVal ($hash, $idx, 'today', 1); + next if($today); # aktueller Tag wird nicht benötigt + + my $h = NexthoursVal ($hash, $idx, 'hourofday', ''); + next if(!$h); + + next if($hods !~ /$h/xs); # diese Stunde des Tages soll nicht erzeugt werden + + my $st = NexthoursVal ($hash, $idx, 'starttime', 'XXXX-XX-XX XX:XX:XX'); # Starttime + + my $pvfc = NexthoursVal ($hash, $idx, 'pvforecast', 0); + + push @$daref, "Tomorrow_Hour".$h."_PVforecast<>".$pvfc." Wh"; + } + +return; +} + ################################################################ # Zusammenfassungen erstellen ################################################################ @@ -4006,13 +4128,18 @@ sub collectAllRegConsumers { my ($rtot,$utot,$pthreshold); if(exists $hc->{etotal}) { my $etotal = $hc->{etotal}; - ($rtot,$utot,$pthreshold) = split ":",$etotal; + ($rtot,$utot,$pthreshold) = split ":", $etotal; } my ($rpcurr,$upcurr); if(exists $hc->{pcurr}) { my $pcurr = $hc->{pcurr}; - ($rpcurr,$upcurr) = split ":",$pcurr; + ($rpcurr,$upcurr) = split ":", $pcurr; + } + + my ($rswstate,$onreg,$offreg); + if(exists $hc->{swstate}) { + ($rswstate,$onreg,$offreg) = split ":", $hc->{swstate}; } my $rauto = $hc->{auto} // q{}; @@ -4041,6 +4168,9 @@ sub collectAllRegConsumers { $data{$type}{$name}{consumers}{$c}{upcurr} = $upcurr // q{}; # Unit der aktuellen Leistungsaufnahme $data{$type}{$name}{consumers}{$c}{notbefore} = $hc->{notbefore} // q{}; # nicht einschalten vor Stunde in 24h Format (00-23) $data{$type}{$name}{consumers}{$c}{notafter} = $hc->{notafter} // q{}; # nicht einschalten nach Stunde in 24h Format (00-23) + $data{$type}{$name}{consumers}{$c}{rswstate} = $rswstate // 'state'; # Schaltstatus Reading + $data{$type}{$name}{consumers}{$c}{onreg} = $onreg // 'on'; # Regex für 'ein' + $data{$type}{$name}{consumers}{$c}{offreg} = $offreg // 'off'; # Regex für 'aus' } Log3 ($name, 5, "$name - all registered consumers:\n".Dumper $data{$type}{$name}{consumers}); @@ -4670,7 +4800,7 @@ sub _graphicConsumerLegend { my $tro = 0; for my $c (@consumers) { - my $caicon = $paref->{caicon}; + my $caicon = $paref->{caicon}; # Consumer AdviceIcon my $cname = ConsumerVal ($hash, $c, "name", ""); # Name des Consumerdevices my $calias = ConsumerVal ($hash, $c, "alias", $cname); # Alias des Consumerdevices my $cicon = ConsumerVal ($hash, $c, "icon", ""); # Icon des Consumerdevices @@ -4684,12 +4814,14 @@ sub _graphicConsumerLegend { my $cmdoff = qq{"FW_cmd('$FW_ME$FW_subdir?XHR=1&cmd=set $name consumerAction set $cname $offcom')"}; my $cmdautoon = qq{"FW_cmd('$FW_ME$FW_subdir?XHR=1&cmd=set $name consumerAction setreading $cname $autord 1')"}; my $cmdautooff = qq{"FW_cmd('$FW_ME$FW_subdir?XHR=1&cmd=set $name consumerAction setreading $cname $autord 0')"}; + my $implan = qq{"FW_cmd('$FW_ME$FW_subdir?XHR=1&cmd=set $name consumerAction consumerImmediatePlanning $c')"}; if ($ftui eq "ftui") { $cmdon = qq{"ftui.setFhemStatus('set $name consumerAction set $cname $oncom')"}; $cmdoff = qq{"ftui.setFhemStatus('set $name consumerAction set $cname $offcom')"}; $cmdautoon = qq{"ftui.setFhemStatus('set $name consumerAction set $cname setreading $cname $autord 1')"}; - $cmdautooff = qq{"ftui.setFhemStatus('set $name consumerAction set $cname setreading $cname $autord 0')"}; + $cmdautooff = qq{"ftui.setFhemStatus('set $name consumerAction set $cname setreading $cname $autord 0')"}; + $implan = qq{"ftui.setFhemStatus('set $name consumerAction consumerImmediatePlanning $c')"}; } $cmdon = q{} if(!$oncom); @@ -4697,7 +4829,7 @@ sub _graphicConsumerLegend { $cmdautoon = q{} if(!$autord); $cmdautooff = q{} if(!$autord); - my $swstate = ConsumerVal ($hash, $c, "state", "undef"); # Schaltzustand des Consumerdevices + #my $swstate = ConsumerVal ($hash, $c, "state", "undef"); # Schaltzustand des Consumerdevices my $swicon = q{}; # Schalter ein/aus Icon my $auicon = q{}; # Schalter Automatic Icon my $isricon = q{}; # Zustand IsRecommended Icon @@ -4718,11 +4850,11 @@ sub _graphicConsumerLegend { $isricon = $pstate; } else { - $isricon = "".FW_makeImage($caicon, ''); + $isricon = "".FW_makeImage($caicon, '')." "; if($planstate =~ /priority/xs) { my (undef,$color) = split('@', $caicon); $color = $color ? '@'.$color : ''; - $isricon = "".FW_makeImage('it_ups_charging'.$color, ''); + $isricon = "".FW_makeImage('it_ups_charging'.$color, '')." "; } } } @@ -4732,7 +4864,7 @@ sub _graphicConsumerLegend { } else { ($caicon) = split('@', $caicon); - $isricon = "".FW_makeImage($caicon.'@grey', ''); + $isricon = "".FW_makeImage($caicon.'@grey', '')." "; } } } @@ -4752,7 +4884,7 @@ sub _graphicConsumerLegend { $auicon = " $staticon"; } - if ($swstate eq "off") { + if (isConsumerOff($hash, $c)) { # Schaltzustand des Consumerdevices off if($cmdon) { $staticon = FW_makeImage('ios_off_fill@red', $htitles{iave}{$lang}); $swicon = " $staticon"; @@ -4763,7 +4895,7 @@ sub _graphicConsumerLegend { } } - if ($swstate eq "on") { + if (isConsumerOn($hash, $c)) { # Schaltzustand des Consumerdevices on if($cmdoff) { $staticon = FW_makeImage('ios_on_fill@green', $htitles{ieva}{$lang}); $swicon = " $staticon"; @@ -5572,10 +5704,10 @@ END3 for my $c1 (@consumers) { my $power = ConsumerVal ($hash, $c1, "power", 0); my $rpcurr = ConsumerVal ($hash, $c1, "rpcurr", ""); # Reading für akt. Verbrauch angegeben ? - my $swstate = ConsumerVal ($hash, $c1, "state", "undef"); # Schaltzustand des Consumerdevices + #my $swstate = ConsumerVal ($hash, $c1, "state", "undef"); # Schaltzustand des Consumerdevices $currentPower = ReadingsNum ($name, "consumer${c1}_currentPower", 0); - if (!$rpcurr && $swstate eq "on") { # Workaround wenn Verbraucher ohne Leistungsmessung + if (!$rpcurr && isConsumerOn($hash, $c1)) { # Workaround wenn Verbraucher ohne Leistungsmessung $currentPower = $power; } @@ -5619,11 +5751,11 @@ END3 $currentPower = sprintf("%.1f", ReadingsNum($name, "consumer${c2}_currentPower", 0)); my $consumerTime = ConsumerVal ($hash, $c2, "remainTime", ""); # Restlaufzeit - my $swstate = ConsumerVal ($hash, $c2, "state", "undef"); # Schaltzustand des Consumerdevices + #my $swstate = ConsumerVal ($hash, $c2, "state", "undef"); # Schaltzustand des Consumerdevices my $rpcurr = ConsumerVal ($hash, $c2, "rpcurr", ""); # Readingname f. current Power if (!$rpcurr) { # Workaround wenn Verbraucher ohne Leistungsmessung - $currentPower = $swstate eq "on" ? 'on' : 'off'; + $currentPower = isConsumerOn($hash, $c2) ? 'on' : 'off'; } $ret .= qq{$currentPower} if ($flowgconPower); # Current_Consumption Consumer @@ -5640,16 +5772,16 @@ return $ret; ################################################################ # prüfe ob Verbrauchericon + Farbe angegeben ist # und setze ggf. Ersatzwerte -# $csm - Consumer Nummer +# $c - Consumer Nummer ################################################################ sub substConsumerIcon { - my $hash = shift; - my $csm = shift; + my $hash = shift; + my $c = shift; my $name = $hash->{NAME}; - my $cicon = ConsumerVal ($hash, $csm, "icon", ""); # Icon des Consumerdevices angegeben ? - my $swstate = ConsumerVal ($hash, $csm, "state", "undef"); # Schaltzustand des Consumerdevices + my $cicon = ConsumerVal ($hash, $c, "icon", ""); # Icon des Consumerdevices angegeben ? + #my $swstate = ConsumerVal ($hash, $c, "state", "undef"); # Schaltzustand des Consumerdevices if (!$cicon) { $cicon = 'light_light_dim_100'; @@ -5659,7 +5791,7 @@ sub substConsumerIcon { ($cicon,$color) = split '@', $cicon; if (!$color) { - $color = $swstate eq "on" ? 'darkorange' : ''; + $color = isConsumerOn($hash, $c) ? 'darkorange' : ''; } $cicon .= '@'.$color if($color); @@ -6677,6 +6809,7 @@ sub listDataPool { } for my $idx (sort keys %{$h}) { my $nhts = NexthoursVal ($hash, $idx, "starttime", "-"); + my $hod = NexthoursVal ($hash, $idx, "hourofday", "-"); my $today = NexthoursVal ($hash, $idx, "today", "-"); my $pvfc = NexthoursVal ($hash, $idx, "pvforecast", "-"); my $wid = NexthoursVal ($hash, $idx, "weatherid", "-"); @@ -6688,7 +6821,7 @@ sub listDataPool { my $temp = NexthoursVal ($hash, $idx, "temp", "-"); my $confc = NexthoursVal ($hash, $idx, "confc", "-"); $sq .= "\n" if($sq); - $sq .= $idx." => starttime: $nhts, today: $today\n"; + $sq .= $idx." => starttime: $nhts, hourofday: $hod, today: $today\n"; $sq .= " pvfc: $pvfc, confc: $confc, Rad1h: $rad1h\n"; $sq .= " wid: $wid, wcc: $neff, wrp: $r101, temp=$temp\n"; $sq .= " crange: $crange, correff: $pvcorrf"; @@ -6960,11 +7093,47 @@ sub deleteConsumerPlanning { delete $data{$type}{$name}{consumers}{$c}{planswitchon}; delete $data{$type}{$name}{consumers}{$c}{planswitchoff}; + $data{$type}{$name}{consumers}{$c}{isConsumptionRecommended} = 0; + deleteReadingspec ($hash, "consumer${c}.*" ); return; } +################################################################ +# Funktion liefert 1 wenn Consumer Status ist "eingeschaltet" +################################################################ +sub isConsumerOn { + my $hash = shift; + my $c = shift; + + my $val = ConsumerVal ($hash, $c, "onreg", "on"); + my $swstate = ConsumerVal ($hash, $c, "state", "undef"); + + if ($swstate =~ m/^$val$/x) { + return 1; + } + +return; +} + +################################################################ +# Funktion liefert 1 wenn Consumer Status ist "ausgeschaltet" +################################################################ +sub isConsumerOff { + my $hash = shift; + my $c = shift; + + my $val = ConsumerVal ($hash, $c, "offreg", "off"); + my $swstate = ConsumerVal ($hash, $c, "state", "undef"); + + if ($swstate =~ m/^$val$/x) { + return 1; + } + +return; +} + ############################################################################### # Wert des pvhist-Hash zurückliefern # Usage: @@ -7107,6 +7276,7 @@ return ($pvcorrf, $quality); # # $hod: nächste Stunde (NextHour00, NextHour01,...) # $key: starttime - Startzeit der abgefragten nächsten Stunde +# hourofday - Stunde des Tages # pvforecast - PV Vorhersage # weatherid - DWD Wetter id # cloudcover - DWD Wolkendichte @@ -7652,9 +7822,10 @@ Ein/Ausschaltzeiten sowie deren Ausführung vom SolarForecast Modul übernehmen consumerPlanning löscht die Planungsdaten aller registrierten Verbraucher Um die Planungsdaten nur eines Verbrauchers zu löschen verwendet man:
    set <name> reset consumerPlanning <Verbrauchernummer>
- consumerMaster löscht die Daten aller registrierten Verbraucher aus dem Speicher - Um die Daten nur eines Verbrauchers zu löschen verwendet man: -
    set <name> reset consumerMaster <Verbrauchernummer>
+ Das Modul führt eine automatische Neuplanung der Verbraucherschaltung durch. + consumerMaster löscht die Daten aller registrierten Verbraucher aus dem Speicher + Um die Daten nur eines Verbrauchers zu löschen verwendet man: +
    set <name> reset consumerMaster <Verbrauchernummer>
currentBatteryDev löscht das eingestellte Batteriedevice und korrespondierende Daten currentForecastDev löscht das eingestellte Device für Wetterdaten currentInverterDev löscht das eingestellte Inverterdevice und korrespondierende Daten @@ -7731,19 +7902,21 @@ Ein/Ausschaltzeiten sowie deren Ausführung vom SolarForecast Modul übernehmen
    - - - - - - - - - - - - - + + + + + + + + + + + + + + +
    pvfc erwartete PV Erzeugung
    today =1 wenn Startdatum am aktuellen Tag
    confc erwarteter Energieverbrauch
    wid ID des vorhergesagten Wetters
    wcc vorhergesagter Grad der Bewölkung
    crange berechneter Bewölkungsbereich
    correff effektiv verwendeter Korrekturfaktor/Qualität
    Faktor/m - manuell
    Faktor/0 - Korrekturfaktor nicht in Store vorhanden (default wird verwendet)
    Faktor/1...X - Korrekturfaktor aus Store genutzt (höhere Zahl = bessere Qualität)
    wrp vorhergesagter Grad der Regenwahrscheinlichkeit
    Rad1h vorhergesagte Globalstrahlung
    temp vorhergesagte Außentemperatur
    starttime Startzeit des Datensatzes
    hourofday laufende Stunde des Tages
    pvfc erwartete PV Erzeugung
    today =1 wenn Startdatum am aktuellen Tag
    confc erwarteter Energieverbrauch
    wid ID des vorhergesagten Wetters
    wcc vorhergesagter Grad der Bewölkung
    crange berechneter Bewölkungsbereich
    correff effektiv verwendeter Korrekturfaktor/Qualität
    Faktor/m - manuell
    Faktor/0 - Korrekturfaktor nicht in Store vorhanden (default wird verwendet)
    Faktor/1...X - Korrekturfaktor aus Store genutzt (höhere Zahl = bessere Qualität)
    wrp vorhergesagter Grad der Regenwahrscheinlichkeit
    Rad1h vorhergesagte Globalstrahlung
    temp vorhergesagte Außentemperatur
@@ -7954,7 +8127,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>] [notbefore=<Stunde>] [notafter=<Stunde>] [auto=<Readingname>] [pcurr=<Readingname>:<Einheit>] [etotal=<Readingname>:<Einheit>[:<Schwellenwert>]]

    +
  • 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>] [etotal=<Readingname>:<Einheit>[:<Schwellenwert>]]

    Registriert einen Verbraucher <Device Name> beim SolarForecast Device. Dabei ist <Device Name> ein in FHEM bereits angelegtes Verbraucher Device, z.B. eine Schaltsteckdose. Die meisten Schlüssel sind optional, @@ -7980,6 +8153,9 @@ Ein/Ausschaltzeiten sowie deren Ausführung vom SolarForecast Modul übernehmen mintime Mindestlaufzeit bzw. typische Laufzeit für einen Zyklus des Verbrauchers nach dem Einschalten in Minuten (default: Typ bezogen) on Set-Kommando zum Einschalten des Verbrauchers (optional) off Set-Kommando zum Ausschalten des Verbrauchers (optional) + swstate Reading welches den Schaltzustand des Consumers anzeigt (optional). Im default wird 'state' verwendet. + on-Regex - regulärer Ausdruck für den Zustand 'ein' + off-Regex - regulärer Ausdruck für den Zustand 'aus' notbefore Verbraucher nicht vor angegebener Stunde (01..23) einschalten (optional) notafter Verbraucher nicht nach angegebener Stunde (01..23) einschalten (optional) auto Reading im Verbraucherdevice welches das Schalten des Verbrauchers freigibt bzw. blockiert (optional) @@ -7999,6 +8175,23 @@ Ein/Ausschaltzeiten sowie deren Ausführung vom SolarForecast Modul übernehmen

  • + +
  • createTomorrowPVFcReadings <01,02,..,24>
    + Wenn gesetzt, werden Readings der Form Tomorrow_Hour<hour>_PVforecast erstellt.
    + Diese Readings enthalten die voraussichtliche PV Erzeugung des kommenden Tages. Dabei ist <hour> die + Stunde des Tages.
    +
    + +
      + Beispiel:
      + attr <name> createTomorrowPVFcReadings 09,11
      + + # erstellt Readings für die Stunde 09 (08:00-09:00) und 11 (10:00 bis 11:00) des kommenden Tages +
    + +
  • +
    +
  • Css
    Definiert den Style für die Energieflußgrafik. Das Attribut wird automatisch vorbelegt.