From 4337a1e3b0a30eacce5af2f1af02e3c5565a0829 Mon Sep 17 00:00:00 2001 From: nasseeder1 Date: Sat, 25 Nov 2023 15:07:53 +0000 Subject: [PATCH] 76_SolarForecast: graphicHeaderOwnspec: show readings of other devs, new set/reset batteryTrigger command git-svn-id: https://svn.fhem.de/fhem/trunk@28204 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 2 + fhem/FHEM/76_SolarForecast.pm | 251 ++++++++------ fhem/contrib/DS_Starter/76_SolarForecast.pm | 356 +++++++++++--------- 3 files changed, 348 insertions(+), 261 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index 2851d5c63..0dd09204e 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,7 @@ # 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: graphicHeaderOwnspec: show readings of other devs + new set/reset batteryTrigger command - bugfix: 98_CDCOpenData: warning Net::FTP bei Fhem Start - bugfix: 72_FRITZBOX set lockLandevice für FritzOS >= 7.50 - feature: 72_FRITZBOX set lockLandevice Parameter rt (realtime) diff --git a/fhem/FHEM/76_SolarForecast.pm b/fhem/FHEM/76_SolarForecast.pm index 94f37459e..519d1b5bc 100644 --- a/fhem/FHEM/76_SolarForecast.pm +++ b/fhem/FHEM/76_SolarForecast.pm @@ -150,6 +150,7 @@ BEGIN { # Versions History intern my %vNotesIntern = ( + "1.2.0" => "25.11.2023 graphicHeaderOwnspec: show readings of other devs by @, Set/reset batteryTrigger ", "1.1.3" => "24.11.2023 rename reset arguments according possible adjustable textField width ", "1.1.2" => "20.11.2023 ctrlDebug Adjustment of column width, must have new fhemweb.js Forum:#135850 ", "1.1.1" => "19.11.2023 graphicHeaderOwnspec: fix ignoring the last element of allsets/allattr ", @@ -557,9 +558,10 @@ my %hset = ( # Ha currentInverterDev => { fn => \&_setinverterDevice }, currentMeterDev => { fn => \&_setmeterDevice }, currentBatteryDev => { fn => \&_setbatteryDevice }, - energyH4Trigger => { fn => \&_setenergyH4Trigger }, + energyH4Trigger => { fn => \&_setTrigger }, plantConfiguration => { fn => \&_setplantConfiguration }, - powerTrigger => { fn => \&_setpowerTrigger }, + batteryTrigger => { fn => \&_setTrigger }, + powerTrigger => { fn => \&_setTrigger }, pvCorrectionFactor_05 => { fn => \&_setpvCorrectionFactor }, pvCorrectionFactor_06 => { fn => \&_setpvCorrectionFactor }, pvCorrectionFactor_07 => { fn => \&_setpvCorrectionFactor }, @@ -1275,6 +1277,7 @@ sub Set { my ($fcd,$ind,$med,$cf,$sp,$coms) = ('','','','','',''); my @re = qw( aiData + batteryTriggerSet consumerMaster consumerPlanning consumption @@ -1360,6 +1363,12 @@ sub Set { if ($ipai) { $setlist .= "aiDecTree:addInstances,addRawData,train "; } + + ## Batterie spezifische Setter + ################################ + if (isBatteryUsed ($name)) { + $setlist .= "batteryTrigger:textField-long "; + } my $params = { hash => $hash, @@ -1709,7 +1718,7 @@ sub _setinverterStrings { ## no critic "not used" } readingsSingleUpdate ($hash, "inverterStrings", $prop, 1); - writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben + writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben return if(_checkSetupNotComplete ($hash)); # keine Stringkonfiguration wenn Setup noch nicht komplett @@ -1814,9 +1823,9 @@ return; } ################################################################ -# Setter powerTrigger +# Setter powerTrigger / batterytrigger / energyH4Trigger ################################################################ -sub _setpowerTrigger { ## no critic "not used" +sub _setTrigger { ## no critic "not used" my $paref = shift; my $hash = $paref->{hash}; my $name = $paref->{name}; @@ -1839,40 +1848,20 @@ sub _setpowerTrigger { ## no critic "not used" } } - writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben - readingsSingleUpdate ($hash, "powerTrigger", $arg, 1); - -return; -} - -################################################################ -# Setter energyH4Trigger -################################################################ -sub _setenergyH4Trigger { ## no critic "not used" - my $paref = shift; - my $hash = $paref->{hash}; - my $name = $paref->{name}; - my $opt = $paref->{opt}; - my $arg = $paref->{arg}; - - if(!$arg) { - return qq{The command "$opt" needs an argument !}; + if ($opt eq 'powerTrigger') { + deleteReadingspec ($hash, 'powerTrigger.*'); + readingsSingleUpdate ($hash, 'powerTrigger', $arg, 1); } - - my ($a,$h) = parseParams ($arg); - - if(!$h) { - return qq{The syntax of "$opt" is not correct. Please consider the commandref.}; + elsif ($opt eq 'batteryTrigger') { + deleteReadingspec ($hash, 'batteryTrigger.*'); + readingsSingleUpdate ($hash, 'batteryTrigger', $arg, 1); + } + elsif ($opt eq 'energyH4Trigger') { + deleteReadingspec ($hash, 'energyH4Trigger.*'); + readingsSingleUpdate ($hash, 'energyH4Trigger', $arg, 1); } - - for my $key (keys %{$h}) { - if($key !~ /^[0-9]+(?:on|off)$/x || $h->{$key} !~ /^[0-9]+$/x) { - return qq{The key "$key" is invalid. Please consider the commandref.}; - } - } - - writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben - readingsSingleUpdate ($hash, "energyH4Trigger", $arg, 1); + + writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben return; } @@ -2257,6 +2246,12 @@ sub _setreset { ## no critic "not used" writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben return; } + + if ($prop eq 'batteryTriggerSet') { + deleteReadingspec ($hash, "batteryTrigger.*"); + writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); + return; + } if ($prop eq 'energyH4TriggerSet') { deleteReadingspec ($hash, "energyH4Trigger.*"); @@ -2310,6 +2305,7 @@ sub _setreset { ## no critic "not used" readingsDelete ($hash, "Current_PowerBatIn"); readingsDelete ($hash, "Current_PowerBatOut"); readingsDelete ($hash, "Current_BatCharge"); + undef @{$data{$type}{$name}{current}{socslidereg}}; delete $data{$type}{$name}{circular}{'99'}{initdaybatintot}; delete $data{$type}{$name}{circular}{'99'}{initdaybatouttot}; delete $data{$type}{$name}{circular}{'99'}{batintot}; @@ -2322,6 +2318,7 @@ sub _setreset { ## no critic "not used" } if ($prop eq 'currentInverterSet') { + undef @{$data{$type}{$name}{current}{genslidereg}}; readingsDelete ($hash, "Current_PV"); deleteReadingspec ($hash, ".*_PVreal" ); writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben @@ -5973,7 +5970,7 @@ sub _transferBatteryValues { my $chour = $paref->{chour}; my $day = $paref->{day}; - my ($badev,$a,$h) = useBattery ($name); + my ($badev,$a,$h) = isBatteryUsed ($name); return if(!$badev); my $type = $paref->{type}; @@ -6107,7 +6104,7 @@ sub _transferBatteryValues { setPVhistory ($paref); delete $paref->{histname}; -###### + ###### storeReading ('Today_Hour'.sprintf("%02d",$nhour).'_BatIn', $batinthishour.' Wh'); storeReading ('Today_Hour'.sprintf("%02d",$nhour).'_BatOut', $batoutthishour.' Wh'); @@ -6118,6 +6115,9 @@ sub _transferBatteryValues { $data{$type}{$name}{current}{powerbatin} = int $pbi; # Hilfshash Wert aktuelle Batterieladung $data{$type}{$name}{current}{powerbatout} = int $pbo; # Hilfshash Wert aktuelle Batterieentladung $data{$type}{$name}{current}{batcharge} = $soc; # aktuelle Batterieladung + + push @{$data{$type}{$name}{current}{socslidereg}}, $soc; # Schieberegister Batterie SOC + limitArray ($data{$type}{$name}{current}{socslidereg}, $defslidenum); return; } @@ -7492,7 +7492,7 @@ sub ___enableSwitchByBatPrioCharge { my $ena = 1; my $pcb = AttrVal ($name, 'affectBatteryPreferredCharge', 0); # Vorrangladung Batterie zu X% - my ($badev) = useBattery ($name); + my ($badev) = isBatteryUsed ($name); return $ena if(!$pcb || !$badev); # Freigabe Schalten Consumer wenn kein Prefered Battery/Soll-Ladung 0 oder keine Batterie installiert @@ -7668,38 +7668,35 @@ sub _evaluateThresholds { my $hash = $paref->{hash}; my $name = $paref->{name}; - my $pt = ReadingsVal($name, "powerTrigger", ""); - my $eh4t = ReadingsVal($name, "energyH4Trigger", ""); - + my $bt = ReadingsVal($name, 'batteryTrigger', ''); + my $pt = ReadingsVal($name, 'powerTrigger', ''); + my $eh4t = ReadingsVal($name, 'energyH4Trigger', ''); + + if ($bt) { + $paref->{cobj} = 'socslidereg'; + $paref->{tname} = 'batteryTrigger'; + $paref->{tholds} = $bt; + + __evaluateArray ($paref); + } + if ($pt) { - my $aaref = CurrentVal ($hash, "genslidereg", ""); - my @aa = (); - @aa = @{$aaref} if (ref $aaref eq "ARRAY"); - - if (scalar @aa >= $defslidenum) { - $paref->{taref} = \@aa; - $paref->{tname} = "powerTrigger"; - $paref->{tholds} = $pt; - - __evaluateArray ($paref); - } + $paref->{cobj} = 'genslidereg'; + $paref->{tname} = 'powerTrigger'; + $paref->{tholds} = $pt; + + __evaluateArray ($paref); } - + if ($eh4t) { - my $aaref = CurrentVal ($hash, "h4fcslidereg", ""); - my @aa = (); - @aa = @{$aaref} if (ref $aaref eq "ARRAY"); + $paref->{cobj} = 'h4fcslidereg'; + $paref->{tname} = 'energyH4Trigger'; + $paref->{tholds} = $eh4t; + + __evaluateArray ($paref); + } - if (scalar @aa >= $defslidenum) { - $paref->{taref} = \@aa; - $paref->{tname} = "energyH4Trigger"; - $paref->{tholds} = $eh4t; - - __evaluateArray ($paref); - } - } - - delete $paref->{taref}; + delete $paref->{cobj}; delete $paref->{tname}; delete $paref->{tholds}; @@ -7711,14 +7708,22 @@ return; ################################################################ sub __evaluateArray { my $paref = shift; + + my $hash = $paref->{hash}; my $name = $paref->{name}; - my $taref = $paref->{taref}; # Referenz zum Threshold-Array + my $cobj = $paref->{cobj}; # das CurrentVal Objekt, z.B. genslidereg my $tname = $paref->{tname}; # Thresholdname, z.B. powerTrigger my $tholds = $paref->{tholds}; # Triggervorgaben, z.B. aus Reading powerTrigger + + my $aaref = CurrentVal ($hash, $cobj, ''); + my @aa = (); + @aa = @{$aaref} if (ref $aaref eq 'ARRAY'); + + return if(scalar @aa < $defslidenum); - my $gen1 = @$taref[0]; - my $gen2 = @$taref[1]; - my $gen3 = @$taref[2]; + my $gen1 = @aa[0]; + my $gen2 = @aa[1]; + my $gen3 = @aa[2]; my ($a,$h) = parseParams ($tholds); @@ -9138,8 +9143,8 @@ sub __createOwnSpec { for (my $i = 1 ; $i <= $rows; $i++) { my ($h, $v, $u); - for (my $k = 0 ; $k < $vinr; $k++) { - ($h->{$k}{label}, $h->{$k}{rdg}) = split ":", $vals[$col] if($vals[$col]); + for (my $k = 0 ; $k < $vinr; $k++) { + ($h->{$k}{label}, $h->{$k}{elm}) = split ":", $vals[$col] if($vals[$col]); $col++; if (!$h->{$k}{label}) { @@ -9147,23 +9152,31 @@ sub __createOwnSpec { next; } - my $setcmd = ___getFWwidget ($name, $h->{$k}{rdg}, $allsets, 'set'); - - if ($setcmd) { - $v->{$k} = $setcmd; - $u->{$k} = q{}; - next; + if ($h->{$k}{elm} !~ /@/xs) { + my $setcmd = ___getFWwidget ($name, $h->{$k}{elm}, $allsets, 'set'); + + if ($setcmd) { + $v->{$k} = $setcmd; + $u->{$k} = q{}; + next; + } + + my $attrcmd = ___getFWwidget ($name, $h->{$k}{elm}, $allattrs, 'attr'); + + if ($attrcmd) { + $v->{$k} = $attrcmd; + $u->{$k} = q{}; + next; + } } - my $attrcmd = ___getFWwidget ($name, $h->{$k}{rdg}, $allattrs, 'attr'); + my ($rdg, $dev) = split "@", $h->{$k}{elm}; + $dev //= $name; - if ($attrcmd) { - $v->{$k} = $attrcmd; - $u->{$k} = q{}; - next; - } + ($v->{$k}, $u->{$k}) = split /\s+/, ReadingsVal ($dev, $rdg, ''); - ($v->{$k}, $u->{$k}) = split /\s+/, ReadingsVal ($name, $h->{$k}{rdg}, ' '); + $v->{$k} //= q{}; + $u->{$k} //= q{}; next if(!$u->{$k}); @@ -10605,21 +10618,6 @@ sub checkdwdattr { return $err; } -################################################################ -# ist Batterie installiert ? -# 1 - ja, 0 - nein -################################################################ -sub useBattery { - 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); -} - ################################################################ # Korrekturen und Qualität berechnen / speichern # sowie AI Quellen Daten hinzufügen @@ -10629,7 +10627,7 @@ sub calcValueImproves { my $hash = $paref->{hash}; my $name = $paref->{name}; my $chour = $paref->{chour}; - my $t = $paref->{t}; # aktuelle Unix-Zeit + my $t = $paref->{t}; # aktuelle Unix-Zeit my $idts = ReadingsTimestamp ($name, "currentInverterDev", ""); # Definitionstimestamp des Inverterdevice return if(!$idts); @@ -13435,6 +13433,22 @@ sub isConsRcmd { return ConsumerVal ($hash, $c, 'isConsumptionRecommended', 0); } +################################################################ +# ist Batterie installiert ? +# 1 - ja, 0 - nein +################################################################ +sub isBatteryUsed { + 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); +} + ################################################################ # ist Consumer $c unterbrechbar (1|2) oder nicht (0|3) ################################################################ @@ -14144,6 +14158,7 @@ return $def; # aiaddistate - Add Instanz Status der KI # genslidereg - Schieberegister PV Erzeugung (Array) # h4fcslidereg - Schieberegister 4h PV Forecast (Array) +# socslidereg - Schieberegister Batterie SOC (Array) # consumption - aktueller Verbrauch (W) # consumerdevs - alle registrierten Consumerdevices (Array) # gridconsumption - aktueller Netzbezug @@ -15054,6 +15069,7 @@ to ensure that the system configuration is correct. + @@ -16415,6 +16431,27 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
+ +
    + +
  • batteryTrigger <1on>=<Wert> <1off>=<Wert> [<2on>=<Wert> <2off>=<Wert> ...]

    + + Generiert Trigger bei Über- bzw. Unterschreitung bestimmter Batterieladungswerte (SOC in %).
    + Überschreiten die letzten drei SOC-Messungen eine definierte Xon-Bedingung, wird das Reading + batteryTrigger_X = on erstellt/gesetzt. + Unterschreiten die letzten drei SOC-Messungen eine definierte Xoff-Bedingung, wird das Reading + batteryTrigger_X = off erstellt/gesetzt.
    + Es kann eine beliebige Anzahl von Triggerbedingungen angegeben werden. Xon/Xoff-Bedingungen müssen nicht zwingend paarweise + definiert werden.
    +
    + +
      + Beispiel:
      + set <name> batteryTrigger 1on=30 1off=10 2on=70 2off=20 3on=15 4off=90
      +
    +
  • +
+
    @@ -16929,6 +16966,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
aiData deletes an existing AI instance including all training data and reinitialises it
batteryTriggerSet deletes the trigger points of the battery storage
consumerPlanning deletes the planning data of all registered consumers
To delete the planning data of only one consumer, use:
    set <name> reset consumerPlanning <Consumer number>
+ @@ -17964,8 +18002,9 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
-
  • graphicHeaderOwnspec <Label>:<Reading> <Label>:<Reading> ...
    - Anzeige beliebiger Readings, Set-Kommandos und Attribute des Devices im Grafikkopf.
    +
  • graphicHeaderOwnspec <Label>:<Reading[@Device]> <Label>:<Reading[@Device]> ...
    + Anzeige beliebiger Readings, Set-Kommandos und Attribute des SolarForecast Devices im Grafikkopf.
    + Durch Angabe des optionalen [@Device] können Readings anderer Devices angezeigt werden.
    Die anzuzeigenden Werte werden durch Leerzeichen getrennt. Es werden vier Werte (Felder) pro Zeile dargestellt.
    Die Eingabe kann mehrzeilig erfolgen. Werte mit den Einheiten "Wh" bzw. "kWh" werden entsprechend der Einstellung @@ -17990,7 +18029,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
  • - + diff --git a/fhem/contrib/DS_Starter/76_SolarForecast.pm b/fhem/contrib/DS_Starter/76_SolarForecast.pm index 032985c72..519d1b5bc 100644 --- a/fhem/contrib/DS_Starter/76_SolarForecast.pm +++ b/fhem/contrib/DS_Starter/76_SolarForecast.pm @@ -126,6 +126,7 @@ BEGIN { FW_room FW_detail FW_widgetFallbackFn + FW_widgetOverride FW_wname ) ); @@ -149,6 +150,10 @@ BEGIN { # Versions History intern my %vNotesIntern = ( + "1.2.0" => "25.11.2023 graphicHeaderOwnspec: show readings of other devs by @, Set/reset batteryTrigger ", + "1.1.3" => "24.11.2023 rename reset arguments according possible adjustable textField width ", + "1.1.2" => "20.11.2023 ctrlDebug Adjustment of column width, must have new fhemweb.js Forum:#135850 ", + "1.1.1" => "19.11.2023 graphicHeaderOwnspec: fix ignoring the last element of allsets/allattr ", "1.1.0" => "14.11.2023 graphicHeaderOwnspec: possible add set/attr commands, new setter consumerNewPlanning ", "1.0.10" => "31.10.2023 fix warnings, edit comref ", "1.0.9" => "29.10.2023 _aiGetSpread: set spread from 50 to 20 ", @@ -553,9 +558,10 @@ my %hset = ( # Ha currentInverterDev => { fn => \&_setinverterDevice }, currentMeterDev => { fn => \&_setmeterDevice }, currentBatteryDev => { fn => \&_setbatteryDevice }, - energyH4Trigger => { fn => \&_setenergyH4Trigger }, + energyH4Trigger => { fn => \&_setTrigger }, plantConfiguration => { fn => \&_setplantConfiguration }, - powerTrigger => { fn => \&_setpowerTrigger }, + batteryTrigger => { fn => \&_setTrigger }, + powerTrigger => { fn => \&_setTrigger }, pvCorrectionFactor_05 => { fn => \&_setpvCorrectionFactor }, pvCorrectionFactor_06 => { fn => \&_setpvCorrectionFactor }, pvCorrectionFactor_07 => { fn => \&_setpvCorrectionFactor }, @@ -1032,7 +1038,7 @@ sub Initialize { "ctrlAutoRefresh:selectnumbers,120,0.2,1800,0,log10 ". "ctrlAutoRefreshFW:$fwd ". "ctrlConsRecommendReadings:multiple-strict,$allcs ". - "ctrlDebug:multiple-strict,$dm ". + "ctrlDebug:multiple-strict,$dm,#14 ". "ctrlGenPVdeviation:daily,continuously ". "ctrlInterval ". "ctrlLanguage:DE,EN ". @@ -1064,7 +1070,7 @@ sub Initialize { "graphicHeaderOwnspec:textField-long ". "graphicHeaderDetail:multiple-strict,all,co,pv,own,status ". "graphicHeaderShow:1,0 ". - "graphicHistoryHour:selectnumbers,0,1,23,0,lin ". + "graphicHistoryHour:slider,0,1,23 ". "graphicHourCount:slider,4,1,24 ". "graphicHourStyle ". "graphicLayoutType:single,double,diff ". @@ -1271,17 +1277,17 @@ sub Set { my ($fcd,$ind,$med,$cf,$sp,$coms) = ('','','','','',''); my @re = qw( aiData + batteryTriggerSet consumerMaster consumerPlanning consumption - currentBatteryDev - currentWeatherDev - currentInverterDev - currentMeterDev - energyH4Trigger - inverterStrings - moduleRoofTops - powerTrigger + currentBatterySet + currentInverterSet + currentMeterSet + energyH4TriggerSet + inverterStringSet + moduleRoofTopSet + powerTriggerSet pvCorrection roofIdentPair pvHistory @@ -1357,6 +1363,12 @@ sub Set { if ($ipai) { $setlist .= "aiDecTree:addInstances,addRawData,train "; } + + ## Batterie spezifische Setter + ################################ + if (isBatteryUsed ($name)) { + $setlist .= "batteryTrigger:textField-long "; + } my $params = { hash => $hash, @@ -1706,7 +1718,7 @@ sub _setinverterStrings { ## no critic "not used" } readingsSingleUpdate ($hash, "inverterStrings", $prop, 1); - writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben + writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben return if(_checkSetupNotComplete ($hash)); # keine Stringkonfiguration wenn Setup noch nicht komplett @@ -1811,9 +1823,9 @@ return; } ################################################################ -# Setter powerTrigger +# Setter powerTrigger / batterytrigger / energyH4Trigger ################################################################ -sub _setpowerTrigger { ## no critic "not used" +sub _setTrigger { ## no critic "not used" my $paref = shift; my $hash = $paref->{hash}; my $name = $paref->{name}; @@ -1836,40 +1848,20 @@ sub _setpowerTrigger { ## no critic "not used" } } - writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben - readingsSingleUpdate ($hash, "powerTrigger", $arg, 1); - -return; -} - -################################################################ -# Setter energyH4Trigger -################################################################ -sub _setenergyH4Trigger { ## no critic "not used" - my $paref = shift; - my $hash = $paref->{hash}; - my $name = $paref->{name}; - my $opt = $paref->{opt}; - my $arg = $paref->{arg}; - - if(!$arg) { - return qq{The command "$opt" needs an argument !}; + if ($opt eq 'powerTrigger') { + deleteReadingspec ($hash, 'powerTrigger.*'); + readingsSingleUpdate ($hash, 'powerTrigger', $arg, 1); } - - my ($a,$h) = parseParams ($arg); - - if(!$h) { - return qq{The syntax of "$opt" is not correct. Please consider the commandref.}; + elsif ($opt eq 'batteryTrigger') { + deleteReadingspec ($hash, 'batteryTrigger.*'); + readingsSingleUpdate ($hash, 'batteryTrigger', $arg, 1); + } + elsif ($opt eq 'energyH4Trigger') { + deleteReadingspec ($hash, 'energyH4Trigger.*'); + readingsSingleUpdate ($hash, 'energyH4Trigger', $arg, 1); } - - for my $key (keys %{$h}) { - if($key !~ /^[0-9]+(?:on|off)$/x || $h->{$key} !~ /^[0-9]+$/x) { - return qq{The key "$key" is invalid. Please consider the commandref.}; - } - } - - writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben - readingsSingleUpdate ($hash, "energyH4Trigger", $arg, 1); + + writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben return; } @@ -2249,21 +2241,27 @@ sub _setreset { ## no critic "not used" return; } - if ($prop eq 'powerTrigger') { + if ($prop eq 'powerTriggerSet') { deleteReadingspec ($hash, "powerTrigger.*"); - writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben + writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben + return; + } + + if ($prop eq 'batteryTriggerSet') { + deleteReadingspec ($hash, "batteryTrigger.*"); + writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); return; } - if ($prop eq 'energyH4Trigger') { + if ($prop eq 'energyH4TriggerSet') { deleteReadingspec ($hash, "energyH4Trigger.*"); - writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben + writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); return; } - if ($prop eq 'moduleRoofTops') { + if ($prop eq 'moduleRoofTopSet') { deleteReadingspec ($hash, "moduleRoofTops"); - writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben + writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); return; } @@ -2285,7 +2283,7 @@ sub _setreset { ## no critic "not used" return; } - if ($prop eq 'currentMeterDev') { + if ($prop eq 'currentMeterSet') { readingsDelete ($hash, "Current_GridConsumption"); readingsDelete ($hash, "Current_GridFeedIn"); delete $data{$type}{$name}{circular}{'99'}{initdayfeedin}; @@ -2303,10 +2301,11 @@ sub _setreset { ## no critic "not used" writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben } - if ($prop eq 'currentBatteryDev') { + if ($prop eq 'currentBatterySet') { readingsDelete ($hash, "Current_PowerBatIn"); readingsDelete ($hash, "Current_PowerBatOut"); readingsDelete ($hash, "Current_BatCharge"); + undef @{$data{$type}{$name}{current}{socslidereg}}; delete $data{$type}{$name}{circular}{'99'}{initdaybatintot}; delete $data{$type}{$name}{circular}{'99'}{initdaybatouttot}; delete $data{$type}{$name}{circular}{'99'}{batintot}; @@ -2318,7 +2317,8 @@ sub _setreset { ## no critic "not used" writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben } - if ($prop eq 'currentInverterDev') { + if ($prop eq 'currentInverterSet') { + undef @{$data{$type}{$name}{current}{genslidereg}}; readingsDelete ($hash, "Current_PV"); deleteReadingspec ($hash, ".*_PVreal" ); writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben @@ -5970,7 +5970,7 @@ sub _transferBatteryValues { my $chour = $paref->{chour}; my $day = $paref->{day}; - my ($badev,$a,$h) = useBattery ($name); + my ($badev,$a,$h) = isBatteryUsed ($name); return if(!$badev); my $type = $paref->{type}; @@ -6104,7 +6104,7 @@ sub _transferBatteryValues { setPVhistory ($paref); delete $paref->{histname}; -###### + ###### storeReading ('Today_Hour'.sprintf("%02d",$nhour).'_BatIn', $batinthishour.' Wh'); storeReading ('Today_Hour'.sprintf("%02d",$nhour).'_BatOut', $batoutthishour.' Wh'); @@ -6115,6 +6115,9 @@ sub _transferBatteryValues { $data{$type}{$name}{current}{powerbatin} = int $pbi; # Hilfshash Wert aktuelle Batterieladung $data{$type}{$name}{current}{powerbatout} = int $pbo; # Hilfshash Wert aktuelle Batterieentladung $data{$type}{$name}{current}{batcharge} = $soc; # aktuelle Batterieladung + + push @{$data{$type}{$name}{current}{socslidereg}}, $soc; # Schieberegister Batterie SOC + limitArray ($data{$type}{$name}{current}{socslidereg}, $defslidenum); return; } @@ -7489,7 +7492,7 @@ sub ___enableSwitchByBatPrioCharge { my $ena = 1; my $pcb = AttrVal ($name, 'affectBatteryPreferredCharge', 0); # Vorrangladung Batterie zu X% - my ($badev) = useBattery ($name); + my ($badev) = isBatteryUsed ($name); return $ena if(!$pcb || !$badev); # Freigabe Schalten Consumer wenn kein Prefered Battery/Soll-Ladung 0 oder keine Batterie installiert @@ -7665,38 +7668,35 @@ sub _evaluateThresholds { my $hash = $paref->{hash}; my $name = $paref->{name}; - my $pt = ReadingsVal($name, "powerTrigger", ""); - my $eh4t = ReadingsVal($name, "energyH4Trigger", ""); - + my $bt = ReadingsVal($name, 'batteryTrigger', ''); + my $pt = ReadingsVal($name, 'powerTrigger', ''); + my $eh4t = ReadingsVal($name, 'energyH4Trigger', ''); + + if ($bt) { + $paref->{cobj} = 'socslidereg'; + $paref->{tname} = 'batteryTrigger'; + $paref->{tholds} = $bt; + + __evaluateArray ($paref); + } + if ($pt) { - my $aaref = CurrentVal ($hash, "genslidereg", ""); - my @aa = (); - @aa = @{$aaref} if (ref $aaref eq "ARRAY"); - - if (scalar @aa >= $defslidenum) { - $paref->{taref} = \@aa; - $paref->{tname} = "powerTrigger"; - $paref->{tholds} = $pt; - - __evaluateArray ($paref); - } + $paref->{cobj} = 'genslidereg'; + $paref->{tname} = 'powerTrigger'; + $paref->{tholds} = $pt; + + __evaluateArray ($paref); } - + if ($eh4t) { - my $aaref = CurrentVal ($hash, "h4fcslidereg", ""); - my @aa = (); - @aa = @{$aaref} if (ref $aaref eq "ARRAY"); + $paref->{cobj} = 'h4fcslidereg'; + $paref->{tname} = 'energyH4Trigger'; + $paref->{tholds} = $eh4t; + + __evaluateArray ($paref); + } - if (scalar @aa >= $defslidenum) { - $paref->{taref} = \@aa; - $paref->{tname} = "energyH4Trigger"; - $paref->{tholds} = $eh4t; - - __evaluateArray ($paref); - } - } - - delete $paref->{taref}; + delete $paref->{cobj}; delete $paref->{tname}; delete $paref->{tholds}; @@ -7708,14 +7708,22 @@ return; ################################################################ sub __evaluateArray { my $paref = shift; + + my $hash = $paref->{hash}; my $name = $paref->{name}; - my $taref = $paref->{taref}; # Referenz zum Threshold-Array + my $cobj = $paref->{cobj}; # das CurrentVal Objekt, z.B. genslidereg my $tname = $paref->{tname}; # Thresholdname, z.B. powerTrigger my $tholds = $paref->{tholds}; # Triggervorgaben, z.B. aus Reading powerTrigger + + my $aaref = CurrentVal ($hash, $cobj, ''); + my @aa = (); + @aa = @{$aaref} if (ref $aaref eq 'ARRAY'); + + return if(scalar @aa < $defslidenum); - my $gen1 = @$taref[0]; - my $gen2 = @$taref[1]; - my $gen3 = @$taref[2]; + my $gen1 = @aa[0]; + my $gen2 = @aa[1]; + my $gen3 = @aa[2]; my ($a,$h) = parseParams ($tholds); @@ -9112,8 +9120,8 @@ sub __createOwnSpec { return if(!$spec || !$show); - my $allsets = getAllSets ($name); - my $allattrs = getAllAttr ($name); + my $allsets = FW_widgetOverride($name, getAllSets ($name), "set")." "; + my $allattrs = FW_widgetOverride($name, getAllAttr ($name), "set")." "; # Leerzeichen am Ende wichtig für Regexvergleich my @fields = split (/\s+/sx, $spec); @@ -9135,8 +9143,8 @@ sub __createOwnSpec { for (my $i = 1 ; $i <= $rows; $i++) { my ($h, $v, $u); - for (my $k = 0 ; $k < $vinr; $k++) { - ($h->{$k}{label}, $h->{$k}{rdg}) = split ":", $vals[$col] if($vals[$col]); + for (my $k = 0 ; $k < $vinr; $k++) { + ($h->{$k}{label}, $h->{$k}{elm}) = split ":", $vals[$col] if($vals[$col]); $col++; if (!$h->{$k}{label}) { @@ -9144,23 +9152,31 @@ sub __createOwnSpec { next; } - my $setcmd = ___getFWwidget ($name, $h->{$k}{rdg}, $allsets, 'set'); - - if ($setcmd) { - $v->{$k} = $setcmd; - $u->{$k} = q{}; - next; + if ($h->{$k}{elm} !~ /@/xs) { + my $setcmd = ___getFWwidget ($name, $h->{$k}{elm}, $allsets, 'set'); + + if ($setcmd) { + $v->{$k} = $setcmd; + $u->{$k} = q{}; + next; + } + + my $attrcmd = ___getFWwidget ($name, $h->{$k}{elm}, $allattrs, 'attr'); + + if ($attrcmd) { + $v->{$k} = $attrcmd; + $u->{$k} = q{}; + next; + } } - my $attrcmd = ___getFWwidget ($name, $h->{$k}{rdg}, $allattrs, 'attr'); + my ($rdg, $dev) = split "@", $h->{$k}{elm}; + $dev //= $name; - if ($attrcmd) { - $v->{$k} = $attrcmd; - $u->{$k} = q{}; - next; - } + ($v->{$k}, $u->{$k}) = split /\s+/, ReadingsVal ($dev, $rdg, ''); - ($v->{$k}, $u->{$k}) = split /\s+/, ReadingsVal ($name, $h->{$k}{rdg}, ' '); + $v->{$k} //= q{}; + $u->{$k} //= q{}; next if(!$u->{$k}); @@ -9200,7 +9216,7 @@ return $ownv; ################################################################ sub ___getFWwidget { my $name = shift; - my $elm = shift; # Element + my $elm = shift; # zu prüfendes Element my $allc = shift; # Kommandovorrat -> ist Element enthalten? my $ctyp = shift // 'set'; # Kommandotyp: set/attr @@ -9219,7 +9235,7 @@ sub ___getFWwidget { if ($ctyp eq 'attr') { $current = AttrVal ($name, $elm, ''); $reading = '.'.$elm; - + push @attrreadings, $reading; readingsSingleUpdate ($defs{$name}, $reading, $current, 0); } @@ -9237,16 +9253,22 @@ sub ___getFWwidget { } if ($ctyp eq 'attr') { - my ($sc) = $widget =~ /current='(.*?)'/xs; - my ($sr) = $widget =~ /reading='(.*?)'/xs; - $widget =~ s/$sc/$current/ if(defined $sc); - $widget =~ s/$sr/$reading/ if(defined $sr); + my ($sc) = $widget =~ /current='(.*?)'/xs; + my ($sr) = $widget =~ /reading='(.*?)'/xs; + $widget =~ s/$sc/$current/ if(defined $sc); + $widget =~ s/$sr/$reading/ if(defined $sr); } if ($arg eq 'textField' || $arg eq 'textField-long') { # Label (Reading) ausblenden -> siehe fhemweb.js function FW_createTextField Zeile 1657 $widget =~ s/arg='textField/arg='textFieldNL/xs; } + + if ($arg =~ 'slider') { # Widget slider in selectnumbers für Kopfgrafik umsetzen + my ($wid, $min, $step, $max, $float) = split ",", $arg; + $widget =~ s/arg='(.*?)'/arg='selectnumbers,$min,$step,$max,0,lin'/xs; + } } + #Log3 ($name, 1, qq{$name - widget: $widget}); return $widget; } @@ -10596,31 +10618,16 @@ sub checkdwdattr { return $err; } -################################################################ -# ist Batterie installiert ? -# 1 - ja, 0 - nein -################################################################ -sub useBattery { - 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); -} - ################################################################ # Korrekturen und Qualität berechnen / speichern -# sowie AI Instanzen hinzufügen +# sowie AI Quellen Daten hinzufügen ################################################################ sub calcValueImproves { my $paref = shift; my $hash = $paref->{hash}; my $name = $paref->{name}; my $chour = $paref->{chour}; - my $t = $paref->{t}; # aktuelle Unix-Zeit + my $t = $paref->{t}; # aktuelle Unix-Zeit my $idts = ReadingsTimestamp ($name, "currentInverterDev", ""); # Definitionstimestamp des Inverterdevice return if(!$idts); @@ -10658,7 +10665,7 @@ sub calcValueImproves { _calcCaQcomplex ($paref); # Korrekturberechnung mit Bewölkung duchführen/speichern _calcCaQsimple ($paref); # einfache Korrekturberechnung duchführen/speichern - _addHourAiRawdata ($paref); # AI Instanz hinzufügen + _addHourAiRawdata ($paref); # AI Raw Data hinzufügen delete $paref->{h}; } @@ -10829,7 +10836,7 @@ return; } ################################################################ -# AI Instanz für die abgeschlossene Stunde hinzufügen +# AI Daten für die abgeschlossene Stunde hinzufügen ################################################################ sub _addHourAiRawdata { my $paref = shift; @@ -10844,7 +10851,7 @@ sub _addHourAiRawdata { debugLog ($paref, 'aiProcess', "start add AI raw data for hour: $h"); - $paref->{ood} = 1; + $paref->{ood} = 1; # Only One Day $paref->{rho} = $rho; aiAddRawData ($paref); # Raw Daten für AI hinzufügen und sichern @@ -13426,6 +13433,22 @@ sub isConsRcmd { return ConsumerVal ($hash, $c, 'isConsumptionRecommended', 0); } +################################################################ +# ist Batterie installiert ? +# 1 - ja, 0 - nein +################################################################ +sub isBatteryUsed { + 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); +} + ################################################################ # ist Consumer $c unterbrechbar (1|2) oder nicht (0|3) ################################################################ @@ -14135,6 +14158,7 @@ return $def; # aiaddistate - Add Instanz Status der KI # genslidereg - Schieberegister PV Erzeugung (Array) # h4fcslidereg - Schieberegister 4h PV Forecast (Array) +# socslidereg - Schieberegister Batterie SOC (Array) # consumption - aktueller Verbrauch (W) # consumerdevs - alle registrierten Consumerdevices (Array) # gridconsumption - aktueller Netzbezug @@ -15045,6 +15069,7 @@ to ensure that the system configuration is correct.
    aiData löscht eine vorhandene KI Instanz inklusive aller Trainingsdaten und initialisiert sie neu
    batteryTriggerSet löscht die Triggerpunkte des Batteriespeichers
    consumerPlanning löscht die Planungsdaten aller registrierten Verbraucher
    Um die Planungsdaten nur eines Verbrauchers zu löschen verwendet man:
      set <name> reset consumerPlanning <Verbrauchernummer>
    #
    CO&nbsp;bis&nbsp;Sonnenuntergang:statistic_todayConForecastTillSunset
    PV&nbsp;Übermorgen:statistic_dayAfterTomorrowPVforecast
    :
    InverterRelay:gridrelay_status@MySTP_5000
    :
    #Batterie
    in&nbsp;heute:statistic_todayBatIn
    + @@ -15057,13 +15082,12 @@ to ensure that the system configuration is correct. - - - - - - - + + + + + + @@ -15075,7 +15099,7 @@ to ensure that the system configuration is correct. - + @@ -15395,7 +15419,7 @@ to ensure that the system configuration is correct.
  • valDecTree

    - If AI support is activated in the SolarForecast Device, various AI-relevant data can be displayed: + If AI support is activated in the SolarForecast Device, various AI-relevant data can be displayed :

      @@ -16407,6 +16431,27 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.

    + +
      + +
    • batteryTrigger <1on>=<Wert> <1off>=<Wert> [<2on>=<Wert> <2off>=<Wert> ...]

      + + Generiert Trigger bei Über- bzw. Unterschreitung bestimmter Batterieladungswerte (SOC in %).
      + Überschreiten die letzten drei SOC-Messungen eine definierte Xon-Bedingung, wird das Reading + batteryTrigger_X = on erstellt/gesetzt. + Unterschreiten die letzten drei SOC-Messungen eine definierte Xoff-Bedingung, wird das Reading + batteryTrigger_X = off erstellt/gesetzt.
      + Es kann eine beliebige Anzahl von Triggerbedingungen angegeben werden. Xon/Xoff-Bedingungen müssen nicht zwingend paarweise + definiert werden.
      +
      + +
        + Beispiel:
        + set <name> batteryTrigger 1on=30 1off=10 2on=70 2off=20 3on=15 4off=90
        +
      +
    • +
    +
      @@ -16921,6 +16966,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
  • aiData deletes an existing AI instance including all training data and reinitialises it
    batteryTriggerSet deletes the trigger points of the battery storage
    consumerPlanning deletes the planning data of all registered consumers
    To delete the planning data of only one consumer, use:
      set <name> reset consumerPlanning <Consumer number>
      set <name> reset consumption <Day> (e.g. set <name> reset consumption 08)
    To delete the consumption values of a specific hour of a day:
      set <name> reset consumption <Day> <Hour> (e.g. set <name> reset consumption 08 10)
    currentBatteryDev deletes the set battery device and corresponding data.
    currentWeatherDev deletes the set device for weather data
    currentInverterDev deletes the set inverter device and corresponding data.
    currentMeterDev deletes the set meter device and corresponding data.
    energyH4Trigger deletes the 4-hour energy trigger points
    inverterStrings deletes the string configuration of the installation
    powerTrigger deletes the trigger points for PV generation values
    currentBatterySet deletes the set battery device and corresponding data.
    currentInverterSet deletes the set inverter device and corresponding data.
    currentMeterSet deletes the set meter device and corresponding data.
    energyH4TriggerSet deletes the 4-hour energy trigger points
    inverterStringSet deletes the string configuration of the installation
    powerTriggerSet deletes the trigger points for PV generation values
    pvCorrection deletes the readings pvCorrectionFactor*
    To delete all previously stored PV correction factors from the caches:
      set <name> reset pvCorrection cached
      set <name> reset pvHistory <Day> (e.g. set <name> reset pvHistory 08)
    To delete a specific hour of a historical day:
      set <name> reset pvHistory <Day> <Hour> (e.g. set <name> reset pvHistory 08 10)
    moduleRoofTops deletes the SolCast API Rooftops
    moduleRoofTopSet deletes the SolCast API Rooftops
    roofIdentPair deletes all saved SolCast API Rooftop ID / API Key pairs.
    To delete a specific pair, specify its key <pk>:
      set <name> reset roofIdentPair <pk> (e.g. set <name> reset roofIdentPair p1)
    + @@ -16933,13 +16979,12 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden. - - - - - - - + + + + + + @@ -16951,7 +16996,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden. - + @@ -17269,7 +17314,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
  • valDecTree

    - Ist der KI Support im SolarForecast Device aktiviert, können verschiedene KI relevante Daten angezeigt werden: + Ist der KI Support im SolarForecast Device aktiviert, können verschiedene KI relevante Daten angezeigt werden :

      @@ -17957,8 +18002,9 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
      -
    • graphicHeaderOwnspec <Label>:<Reading> <Label>:<Reading> ...
      - Anzeige beliebiger Readings, Set-Kommandos und Attribute des Devices im Grafikkopf.
      +
    • graphicHeaderOwnspec <Label>:<Reading[@Device]> <Label>:<Reading[@Device]> ...
      + Anzeige beliebiger Readings, Set-Kommandos und Attribute des SolarForecast Devices im Grafikkopf.
      + Durch Angabe des optionalen [@Device] können Readings anderer Devices angezeigt werden.
      Die anzuzeigenden Werte werden durch Leerzeichen getrennt. Es werden vier Werte (Felder) pro Zeile dargestellt.
      Die Eingabe kann mehrzeilig erfolgen. Werte mit den Einheiten "Wh" bzw. "kWh" werden entsprechend der Einstellung @@ -17983,7 +18029,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
  • - +
    aiData löscht eine vorhandene KI Instanz inklusive aller Trainingsdaten und initialisiert sie neu
    batteryTriggerSet löscht die Triggerpunkte des Batteriespeichers
    consumerPlanning löscht die Planungsdaten aller registrierten Verbraucher
    Um die Planungsdaten nur eines Verbrauchers zu löschen verwendet man:
      set <name> reset consumerPlanning <Verbrauchernummer>
      set <name> reset consumption <Tag> (z.B. set <name> reset consumption 08)
    Um die Verbrauchswerte einer bestimmten Stunde eines Tages zu löschen:
      set <name> reset consumption <Tag> <Stunde> (z.B. set <name> reset consumption 08 10)
    currentBatteryDev löscht das eingestellte Batteriedevice und korrespondierende Daten
    currentWeatherDev löscht das eingestellte Device für Wetterdaten
    currentInverterDev löscht das eingestellte Inverterdevice und korrespondierende Daten
    currentMeterDev löscht das eingestellte Meterdevice und korrespondierende Daten
    energyH4Trigger löscht die 4-Stunden Energie Triggerpunkte
    inverterStrings löscht die Stringkonfiguration der Anlage
    powerTrigger löscht die Triggerpunkte für PV Erzeugungswerte
    currentBatterySet löscht das eingestellte Batteriedevice und korrespondierende Daten
    currentInverterSet löscht das eingestellte Inverterdevice und korrespondierende Daten
    currentMeterSet löscht das eingestellte Meterdevice und korrespondierende Daten
    energyH4TriggerSet löscht die 4-Stunden Energie Triggerpunkte
    inverterStringSet löscht die Stringkonfiguration der Anlage
    powerTriggerSet löscht die Triggerpunkte für PV Erzeugungswerte
    pvCorrection löscht die Readings pvCorrectionFactor*
    Um alle bisher gespeicherten PV Korrekturfaktoren aus den Caches zu löschen:
      set <name> reset pvCorrection cached
      set <name> reset pvHistory <Tag> (z.B. set <name> reset pvHistory 08)
    Um eine bestimmte Stunde eines historischer Tages zu löschen:
      set <name> reset pvHistory <Tag> <Stunde> (z.B. set <name> reset pvHistory 08 10)
    moduleRoofTops löscht die SolCast API Rooftops
    moduleRoofTopSet löscht die SolCast API Rooftops
    roofIdentPair löscht alle gespeicherten SolCast API Rooftop-ID / API-Key Paare
    Um ein bestimmtes Paar zu löschen ist dessen Schlüssel <pk> anzugeben:
      set <name> reset roofIdentPair <pk> (z.B. set <name> reset roofIdentPair p1)
    #
    CO&nbsp;bis&nbsp;Sonnenuntergang:statistic_todayConForecastTillSunset
    PV&nbsp;Übermorgen:statistic_dayAfterTomorrowPVforecast
    :
    InverterRelay:gridrelay_status@MySTP_5000
    :
    #Batterie
    in&nbsp;heute:statistic_todayBatIn