From 4db23a2307d6627c1370d9f6d2efb3750d7ad3db Mon Sep 17 00:00:00 2001 From: nasseeder1 Date: Wed, 15 Nov 2023 19:36:28 +0000 Subject: [PATCH] 76_SolarForecast: graphicHeaderOwnspec: can add set/attr commands, new setter consumerNewPlanning git-svn-id: https://svn.fhem.de/fhem/trunk@28174 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 2 + fhem/FHEM/76_SolarForecast.pm | 388 ++++++++++++++++++++++++---------- 2 files changed, 279 insertions(+), 111 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index ee3704f82..ba8ae9746 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: can add set/attr commands, + new setter consumerNewPlanning - feature: 72_FB_CALLMONITOR commandRef - feature: 72_FRITZBOX set lockFilterProfile ... - feature: 72_FRITZBOX set energyMode ... diff --git a/fhem/FHEM/76_SolarForecast.pm b/fhem/FHEM/76_SolarForecast.pm index 322d5a2b3..824a7568b 100644 --- a/fhem/FHEM/76_SolarForecast.pm +++ b/fhem/FHEM/76_SolarForecast.pm @@ -94,6 +94,9 @@ BEGIN { FmtDateTime FW_makeImage getKeyValue + getAllAttr + getAllGets + getAllSets HttpUtils_NonblockingGet init_done InternalTimer @@ -119,8 +122,10 @@ BEGIN { FW_directNotify FW_ME FW_subdir + FW_pH FW_room FW_detail + FW_widgetFallbackFn FW_wname ) ); @@ -144,6 +149,7 @@ BEGIN { # Versions History intern my %vNotesIntern = ( + "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 ", "1.0.8" => "22.10.2023 codechange: add central readings store array, new function storeReading, writeCacheToFile ". @@ -442,6 +448,8 @@ my $kJtokWh = 0.00027778; my $defmaxvar = 0.5; # max. Varianz pro Tagesberechnung Autokorrekturfaktor my $definterval = 70; # Standard Abfrageintervall my $defslidenum = 3; # max. Anzahl der Arrayelemente in Schieberegistern +my $webCmdFn = 'FW_widgetFallbackFn'; # FHEMWEB Widgets Funktion +my @attrreadings = (); # Array der Hilfsreadings als Attributspeicher my $pvhcache = $attr{global}{modpath}."/FHEM/FhemUtils/PVH_SolarForecast_"; # Filename-Fragment für PV History (wird mit Devicename ergänzt) my $pvccache = $attr{global}{modpath}."/FHEM/FhemUtils/PVC_SolarForecast_"; # Filename-Fragment für PV Circular (wird mit Devicename ergänzt) @@ -528,12 +536,15 @@ my @dd = qw( none radiationProcess saveData2Cache ); + +my $allwidgets = 'icon|sortable|uzsu|knob|noArg|time|text|slider|multiple|select|bitfield|widgetList|colorpicker'; # Steuerhashes ############### my %hset = ( # Hash der Set-Funktion consumerImmediatePlanning => { fn => \&_setconsumerImmediatePlanning }, + consumerNewPlanning => { fn => \&_setconsumerNewPlanning }, currentWeatherDev => { fn => \&_setcurrentWeatherDev }, currentRadiationAPI => { fn => \&_setcurrentRadiationAPI }, modulePeakString => { fn => \&_setmodulePeakString }, @@ -1302,6 +1313,7 @@ sub Set { my $ipai = isPrepared4AI ($hash); $setlist = "Unknown argument $opt, choose one of ". "consumerImmediatePlanning:$coms ". + "consumerNewPlanning:$coms ". "currentWeatherDev:$fcd ". "currentRadiationAPI:$rdd ". "currentBatteryDev:textField-long ". @@ -1379,7 +1391,7 @@ sub _setconsumerImmediatePlanning { ## no critic "not used" my $type = $paref->{type}; my $opt = $paref->{opt}; my $c = $paref->{prop}; - my $evt = $paref->{prop1} // 1; + my $evt = $paref->{prop1} // 0; # geändert V 1.1.0 - 1 -> 0 return qq{no consumer number specified} if(!$c); return qq{no valid consumer id "$c"} if(!ConsumerVal ($hash, $c, "name", "")); @@ -1430,6 +1442,29 @@ sub _setconsumerImmediatePlanning { ## no critic "not used" return; } +################################################################ +# Setter consumerNewPlanning +################################################################ +sub _setconsumerNewPlanning { ## no critic "not used" + my $paref = shift; + my $hash = $paref->{hash}; + my $name = $paref->{name}; + my $c = $paref->{prop}; + my $evt = $paref->{prop1} // 0; # geändert V 1.1.0 - 1 -> 0 + + return qq{no consumer number specified} if(!$c); + return qq{no valid consumer id "$c"} if(!ConsumerVal ($hash, $c, 'name', '')); + + if ($c) { + deleteConsumerPlanning ($hash, $c); + writeCacheToFile ($hash, 'consumers', $csmcache.$name); # Cache File Consumer schreiben + } + + centralTask ($hash, $evt); + +return; +} + ################################################################ # Setter currentWeatherDev (Wetterdaten) ################################################################ @@ -2280,7 +2315,7 @@ sub _setreset { ## no critic "not used" delete $data{$type}{$name}{current}{powerbatin}; delete $data{$type}{$name}{current}{batcharge}; - writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben + writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben } if ($prop eq 'currentInverterDev') { @@ -2289,7 +2324,7 @@ sub _setreset { ## no critic "not used" writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben } - if ($prop eq 'consumerPlanning') { # Verbraucherplanung resetten + if ($prop eq 'consumerPlanning') { # Verbraucherplanung resetten my $c = $paref->{prop1} // ""; # bestimmten Verbraucher setzen falls angegeben if ($c) { @@ -2320,7 +2355,7 @@ sub _setreset { ## no critic "not used" } } - writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben + writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben } createAssociatedWith ($hash); @@ -3860,7 +3895,8 @@ sub _getlistPVHistory { my $ret = listDataPool ($hash, 'pvhist', $arg); $ret .= lineFromSpaces ($ret, 20); - + $ret =~ s/\n/
/g; + return $ret; } @@ -3886,7 +3922,7 @@ sub _getlistNextHours { my $ret = listDataPool ($hash, 'nexthours'); $ret .= lineFromSpaces ($ret, 10); - + return $ret; } @@ -5031,7 +5067,10 @@ sub _specialActivities { deleteReadingspec ($hash, "Today_MaxPVforecast.*"); deleteReadingspec ($hash, "Today_PVdeviation"); deleteReadingspec ($hash, "Today_PVreal"); - + + for my $atr (@attrreadings) { + deleteReadingspec ($hash, $atr); + } for my $n (1..24) { $n = sprintf "%02d", $n; @@ -9072,6 +9111,9 @@ sub __createOwnSpec { my $show = $hdrDetail =~ /all|own/xs ? 1 : 0; return if(!$spec || !$show); + + my $allsets = getAllSets ($name); + my $allattrs = getAllAttr ($name); my @fields = split (/\s+/sx, $spec); @@ -9102,27 +9144,37 @@ sub __createOwnSpec { next; } - ($v->{$k}, $u->{$k}) = split /\s+/, ReadingsVal ($name, $h->{$k}{rdg}, ''); - } - - if ($uatr eq 'kWh') { - for (my $r = 0 ; $r < $vinr; $r++) { - next if(!$u->{$r}); - - if ($u->{$r} =~ /^Wh/xs) { - $v->{$r} = sprintf "%.1f",($v->{$r} / 1000); - $u->{$r} = 'kWh'; + my $setcmd = ___getFWwidget ($name, $h->{$k}{rdg}, $allsets, 'set'); + + if ($setcmd) { + $v->{$k} = $setcmd; + $u->{$k} = q{}; + next; + } + + my $attrcmd = ___getFWwidget ($name, $h->{$k}{rdg}, $allattrs, 'attr'); + + if ($attrcmd) { + $v->{$k} = $attrcmd; + $u->{$k} = q{}; + next; + } + + ($v->{$k}, $u->{$k}) = split /\s+/, ReadingsVal ($name, $h->{$k}{rdg}, ' '); + + next if(!$u->{$k}); + + if ($uatr eq 'kWh') { + if ($u->{$k} =~ /^Wh/xs) { + $v->{$k} = sprintf "%.1f",($v->{$k} / 1000); + $u->{$k} = 'kWh'; } } - } - if ($uatr eq 'Wh') { - for (my $r = 0 ; $r < $vinr; $r++) { - next if(!$u->{$r}); - - if ($u->{$r} =~ /^kWh/xs) { - $v->{$r} = sprintf "%.0f",($v->{$r} * 1000); - $u->{$r} = 'Wh'; + if ($uatr eq 'Wh') { + if ($u->{$k} =~ /^kWh/xs) { + $v->{$k} = sprintf "%.0f",($v->{$k} * 1000); + $u->{$k} = 'Wh'; } } } @@ -9143,6 +9195,67 @@ sub __createOwnSpec { return $ownv; } +################################################################ +# liefert ein FHEMWEB set/attr Widget zurück +################################################################ +sub ___getFWwidget { + my $name = shift; + my $elm = shift; # Element + my $allc = shift; # Kommandovorrat -> ist Element enthalten? + my $ctyp = shift // 'set'; # Kommandotyp: set/attr + + my $widget = ''; + + my ($current, $reading); + + if ($allc =~ /$elm:?(.*?)\s/xs) { + my $arg = $1; + $arg = 'textFieldNL' if(!$arg); + + if ($arg !~ /^\#/xs && $arg !~ /^$allwidgets/xs) { + $arg = '#,'.$arg; + } + + if ($ctyp eq 'attr') { + $current = AttrVal ($name, $elm, ''); + $reading = '.'.$elm; + + push @attrreadings, $reading; + readingsSingleUpdate ($defs{$name}, $reading, $current, 0); + } + + no strict "refs"; ## no critic 'NoStrict' + $widget = &{$webCmdFn} ($FW_wname, $name, "", $elm, $arg); + use strict "refs"; + + if ($widget) { + $widget =~ s,^]*>(.*)$,$1,x; + $widget =~ s,>, type='$ctyp'>,x; + } + else { + $widget = FW_pH ("cmd=$ctyp $name $elm", $elm, 0, "", 1, 1); + } + + 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); + } + + 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; + } + } + +return $widget; +} + ################################################################ # Consumer in forecastGraphic (Balken) anzeigen # (Hat zur Zeit keine Wirkung !) @@ -9448,8 +9561,8 @@ sub _beamGraphicFirstHour { my $day; - my $t = NexthoursVal ($hash, "NextHour00", "starttime", '0000-00-00 24'); - my ($year,$month,$day_str,$thishour) = $t =~ m/(\d{4})-(\d{2})-(\d{2})\s(\d{2})/x; + my $stt = NexthoursVal ($hash, "NextHour00", "starttime", '0000-00-00 24'); + my ($year,$month,$day_str,$thishour) = $stt =~ m/(\d{4})-(\d{2})-(\d{2})\s(\d{2})/x; my ($val1,$val2,$val3,$val4) = (0,0,0,0); $thishour++; @@ -9489,7 +9602,7 @@ sub _beamGraphicFirstHour { $val3 = CircularVal ($hash, $hfcg->{0}{time_str}, 'gcons', 0); $val4 = CircularVal ($hash, $hfcg->{0}{time_str}, 'confc', 0); - $hfcg->{0}{weather} = CircularVal ($hash, $hfcg->{0}{time_str}, "weatherid", 999); + $hfcg->{0}{weather} = CircularVal ($hash, $hfcg->{0}{time_str}, 'weatherid', 999); #$val4 = (ReadingsVal($name,"ThisHour_IsConsumptionRecommended",'no') eq 'yes' ) ? $icon : 999; } @@ -9554,14 +9667,14 @@ sub _beamGraphicRemainingHours { $hfcg->{$i}{wcc} = HistoryVal ($hash, $ds, $hfcg->{$i}{time_str}, 'wcc', '-'); } else { - $nh = sprintf('%02d', $i+$offset); + $nh = sprintf '%02d', $i + $offset; } } else { - $nh = sprintf('%02d', $i); + $nh = sprintf '%02d', $i; } - if (defined($nh)) { + if (defined $nh) { $val1 = NexthoursVal ($hash, 'NextHour'.$nh, 'pvfc', 0); $val4 = NexthoursVal ($hash, 'NextHour'.$nh, 'confc', 0); $hfcg->{$i}{weather} = NexthoursVal ($hash, 'NextHour'.$nh, 'weatherid', 999); @@ -9638,15 +9751,15 @@ sub _beamGraphic { if($show_diff eq 'top') { # Zusätzliche Zeile Ertrag - Verbrauch $ret .= ""; my $ii; - for my $i (0..($maxhours*2)-1) { # gleiche Bedingung wie oben - next if (!$show_night && ($hfcg->{$i}{weather} > 99) - && !$hfcg->{$i}{beam1} - && !$hfcg->{$i}{beam2}); + for my $i (0..($maxhours * 2) - 1) { # gleiche Bedingung wie oben + next if(!$show_night && $hfcg->{$i}{weather} > 99 + && !$hfcg->{$i}{beam1} + && !$hfcg->{$i}{beam2}); $ii++; # wieviele Stunden haben wir bisher angezeigt ? last if ($ii > $maxhours); # vorzeitiger Abbruch - $val = formatVal6($hfcg->{$i}{diff},$kw,$hfcg->{$i}{weather}); + $val = formatVal6 ($hfcg->{$i}{diff}, $kw, $hfcg->{$i}{weather}); if ($val ne ' ') { # Forum: https://forum.fhem.de/index.php/topic,117864.msg1166215.html#msg1166215 $val = $hfcg->{$i}{diff} < 0 ? ''.$val.'' : @@ -9663,11 +9776,10 @@ sub _beamGraphic { my $ii = 0; - for my $i (0..($maxhours*2)-1) { # gleiche Bedingung wie oben - next if (!$show_night && defined($hfcg->{$i}{weather}) - && ($hfcg->{$i}{weather} > 99) - && !$hfcg->{$i}{beam1} - && !$hfcg->{$i}{beam2}); + for my $i (0..($maxhours * 2) - 1) { # gleiche Bedingung wie oben + next if(!$show_night && $hfcg->{$i}{weather} > 99 + && !$hfcg->{$i}{beam1} + && !$hfcg->{$i}{beam2}); $ii++; last if ($ii > $maxhours); @@ -9768,7 +9880,7 @@ sub _beamGraphic { $ret .="\n"; if ($lotype eq 'single') { - $val = formatVal6($hfcg->{$i}{beam1},$kw,$hfcg->{$i}{weather}); + $val = formatVal6 ($hfcg->{$i}{beam1}, $kw, $hfcg->{$i}{weather}); $ret .=""; # mit width=100% etwas bessere Füllung der Balken $ret .=""; @@ -9793,32 +9905,32 @@ sub _beamGraphic { if ($lotype eq 'double') { my ($color1, $color2, $style1, $style2, $v); - my $style = "style='padding-bottom:0px; padding-top:1px; vertical-align:top; margin-left:auto; margin-right:auto;"; + my $style = "style='padding-bottom:0px; padding-top:1px; vertical-align:top; margin-left:auto; margin-right:auto;"; $ret .="
\n"; # mit width=100% etwas bessere Füllung der Balken # der Freiraum oben kann beim größten Balken ganz entfallen - $ret .="" if ($he); + $ret .="" if($he); - if($hfcg->{$i}{beam1} > $hfcg->{$i}{beam2}) { # wer ist oben, Beam2 oder Beam1 ? Wert und Farbe für Zone 2 & 3 vorbesetzen - $val = formatVal6($hfcg->{$i}{beam1},$kw,$hfcg->{$i}{weather}); + if ($hfcg->{$i}{beam1} > $hfcg->{$i}{beam2}) { # wer ist oben, Beam2 oder Beam1 ? Wert und Farbe für Zone 2 & 3 vorbesetzen + $val = formatVal6 ($hfcg->{$i}{beam1}, $kw, $hfcg->{$i}{weather}); $color1 = $colorb1; $style1 = $style." background-color:#$color1; color:#$fcolor1;'"; if ($z3) { # die Zuweisung können wir uns sparen wenn Zone 3 nachher eh nicht ausgegeben wird - $v = formatVal6($hfcg->{$i}{beam2},$kw,$hfcg->{$i}{weather}); + $v = formatVal6 ($hfcg->{$i}{beam2}, $kw, $hfcg->{$i}{weather}); $color2 = $colorb2; $style2 = $style." background-color:#$color2; color:#$fcolor2;'"; } } else { - $val = formatVal6($hfcg->{$i}{beam2},$kw,$hfcg->{$i}{weather}); - $color1 = $colorb2; - $style1 = $style." background-color:#$color1; color:#$fcolor2;'"; + $val = formatVal6 ($hfcg->{$i}{beam2}, $kw, $hfcg->{$i}{weather}); + $color1 = $colorb2; + $style1 = $style." background-color:#$color1; color:#$fcolor2;'"; if ($z3) { - $v = formatVal6($hfcg->{$i}{beam1},$kw,$hfcg->{$i}{weather}); - $color2 = $colorb1; - $style2 = $style." background-color:#$color2; color:#$fcolor1;'"; + $v = formatVal6 ($hfcg->{$i}{beam1}, $kw, $hfcg->{$i}{weather}); + $color2 = $colorb1; + $style2 = $style." background-color:#$color2; color:#$fcolor1;'"; } } @@ -9841,7 +9953,7 @@ sub _beamGraphic { my $style = "style='padding-bottom:0px; padding-top:1px; vertical-align:top; margin-left:auto; margin-right:auto;"; $ret .= "
\n"; # Tipp : das nachfolgende border=0 auf 1 setzen hilft sehr Ausgabefehler zu endecken - $val = ($hfcg->{$i}{diff} > 0) ? formatVal6($hfcg->{$i}{diff},$kw,$hfcg->{$i}{weather}) : ''; + $val = ($hfcg->{$i}{diff} > 0) ? formatVal6 ($hfcg->{$i}{diff}, $kw, $hfcg->{$i}{weather}) : ''; $val = '   0  ' if ($hfcg->{$i}{diff} == 0); # Sonderfall , hier wird die 0 gebraucht ! if ($val) { @@ -9875,14 +9987,14 @@ sub _beamGraphic { } if($z4) { # kann entfallen wenn auch z3 0 ist - $val = ($hfcg->{$i}{diff} < 0) ? formatVal6($hfcg->{$i}{diff},$kw,$hfcg->{$i}{weather}) : ' '; + $val = ($hfcg->{$i}{diff} < 0) ? formatVal6 ($hfcg->{$i}{diff}, $kw, $hfcg->{$i}{weather}) : ' '; $ret .= ""; $ret .= ""; } } if ($show_diff eq 'bottom') { # zusätzliche diff Anzeige - $val = formatVal6($hfcg->{$i}{diff},$kw,$hfcg->{$i}{weather}); + $val = formatVal6 ($hfcg->{$i}{diff}, $kw, $hfcg->{$i}{weather}); $val = ($hfcg->{$i}{diff} < 0) ? ''.$val.'' : ($val > 0 ) ? '+'.$val : $val if ($val ne ' '); # negative Zahlen in Fettschrift, 0 aber ohne + $ret .= ""; } @@ -9926,53 +10038,50 @@ sub __weatherOnBeam { return $ret if(!$weather); - my $m = $paref->{modulo} % 2; + my $m = $paref->{modulo} % 2; + my $ii = 0; $ret .= ""; # freier Platz am Anfang - my $ii; - for my $i (0..($maxhours*2)-1) { - last if (!exists($hfcg->{$i}{weather})); - next if (!$show_night && defined($hfcg->{$i}{weather}) - && ($hfcg->{$i}{weather} > 99) - && !$hfcg->{$i}{beam1} - && !$hfcg->{$i}{beam2}); + for my $i (0..($maxhours * 2) - 1) { + last if (!exists ($hfcg->{$i}{weather})); + + $hfcg->{$i}{weather} = 999 if(!defined $hfcg->{$i}{weather}); + my $wcc = $hfcg->{$i}{wcc} // '-'; # Bewölkungsgrad ergänzen + + debugLog ($paref, 'graphic', "weather id beam number >$i< (start hour $hfcg->{$i}{time_str}): wid $hfcg->{$i}{weather} / wcc $wcc") if($ii < $maxhours); + + if (!$show_night && $hfcg->{$i}{weather} > 99 + && !$hfcg->{$i}{beam1} + && !$hfcg->{$i}{beam2}) { + + debugLog ($paref, 'graphic', "weather id >$i< don't show night condition ... is skipped") if($ii < $maxhours); + next; + }; # Lässt Nachticons aber noch durch wenn es einen Wert gibt , ToDo : klären ob die Nacht richtig gesetzt wurde $ii++; # wieviele Stunden Icons haben wir bisher beechnet ? - last if ($ii > $maxhours); - # ToDo : weather_icon sollte im Fehlerfall Title mit der ID besetzen um in FHEMWEB sofort die ID sehen zu können - if (exists($hfcg->{$i}{weather}) && defined($hfcg->{$i}{weather})) { - my ($icon_name, $title) = $hfcg->{$i}{weather} > 100 ? - weather_icon ($name, $lang, $hfcg->{$i}{weather}-100) : - weather_icon ($name, $lang, $hfcg->{$i}{weather}); + last if($ii > $maxhours); + # ToDo : weather_icon sollte im Fehlerfall Title mit der ID besetzen um in FHEMWEB sofort die ID sehen zu können + my ($icon_name, $title) = $hfcg->{$i}{weather} > 100 ? + weather_icon ($name, $lang, $hfcg->{$i}{weather}-100) : + weather_icon ($name, $lang, $hfcg->{$i}{weather}); - my $wcc = $hfcg->{$i}{wcc} // "-"; # Bewölkungsgrad ergänzen + $wcc += 0 if(isNumeric ($wcc)); # Javascript Fehler vermeiden: https://forum.fhem.de/index.php/topic,117864.msg1233661.html#msg1233661 + $title .= ': '.$wcc; - if(isNumeric ($wcc)) { # Javascript Fehler vermeiden: https://forum.fhem.de/index.php/topic,117864.msg1233661.html#msg1233661 - $wcc += 0; - } - - $title .= ': '.$wcc; - - if($icon_name eq 'unknown') { - debugLog ($paref, "graphic", "unknown weather id: ".$hfcg->{$i}{weather}.", please inform the maintainer"); - } - - $icon_name .= $hfcg->{$i}{weather} < 100 ? '@'.$colorw : '@'.$colorwn; - my $val = FW_makeImage($icon_name) // q{}; - - if ($val eq $icon_name) { # passendes Icon beim User nicht vorhanden ! ( attr web iconPath falsch/prüfen/update ? ) - $val = '???'; - - debugLog ($paref, "graphic", qq{the icon "$weather_ids{$hfcg->{$i}{weather}}{icon}" not found. Please check attribute "iconPath" of your FHEMWEB instance and/or update your FHEM software}); - } - - $ret .= ""; + if ($icon_name eq 'unknown') { + debugLog ($paref, "graphic", "unknown weather id: ".$hfcg->{$i}{weather}.", please inform the maintainer"); } - else { # mit $hfcg->{$i}{weather} = undef kann man unten leicht feststellen ob für diese Spalte bereits ein Icon ausgegeben wurde oder nicht - $ret .= ""; - $hfcg->{$i}{weather} = undef; # ToDo : prüfen ob noch nötig + + $icon_name .= $hfcg->{$i}{weather} < 100 ? '@'.$colorw : '@'.$colorwn; + my $val = FW_makeImage ($icon_name) // q{}; + + if ($val =~ /title="$icon_name"/xs) { # passendes Icon beim User nicht vorhanden ! ( attr web iconPath falsch/prüfen/update ? ) + $val = '???'; + debugLog ($paref, "graphic", qq{ERROR - the icon "$weather_ids{$hfcg->{$i}{weather}}{icon}.svg" not found. Please check attribute "iconPath" of your FHEMWEB instance and/or update your FHEM software}); } + + $ret .= ""; } $ret .= ""; # freier Platz am Ende der Icon Zeile @@ -10391,23 +10500,26 @@ return $ret; # ############################################################################### sub formatVal6 { - my ($v,$kw,$w) = @_; - my $n = ' '; # positive Zahl + my $v = shift; + my $kw = shift; + my $w = shift; + + my $n = ' '; # positive Zahl - if($v < 0) { + if ($v < 0) { $n = '-'; # negatives Vorzeichen merken $v = abs($v); } - if($kw eq 'kWh') { # bei Anzeige in kWh muss weniger aufgefüllt werden - $v = sprintf "%.1f",($v/1000); + if ($kw eq 'kWh') { # bei Anzeige in kWh muss weniger aufgefüllt werden + $v = sprintf "%.1f",($v / 1000); $v += 0; # keine 0.0 oder 6.0 etc - return ($n eq '-') ? ($v*-1) : $v if defined($w) ; + return ($n eq '-') ? ($v * -1) : $v if(defined $w); my $t = $v - int($v); # Nachkommstelle ? - if(!$t) { # glatte Zahl ohne Nachkommastelle + if (!$t) { # glatte Zahl ohne Nachkommastelle if(!$v) { return ' '; # 0 nicht anzeigen, passt eigentlich immer bis auf einen Fall im Typ diff } @@ -10428,7 +10540,7 @@ sub formatVal6 { } } - return ($n eq '-') ? ($v*-1) : $v if defined($w); + return ($n eq '-') ? ($v * -1) : $v if(defined $w); # Werte bleiben in Watt if (!$v) { return ' '; } ## no critic "Cascading" # keine Anzeige bei Null @@ -10454,7 +10566,7 @@ sub weather_icon { return $weather_ids{$id}{icon}, encode("utf8", $weather_ids{$id}{$txt}); } -return 'unknown',''; +return ('unknown',''); } ################################################################ @@ -10506,7 +10618,7 @@ 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; @@ -10551,7 +10663,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}; } @@ -10722,7 +10834,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; @@ -10737,7 +10849,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 @@ -14429,6 +14541,22 @@ to ensure that the system configuration is correct.
+ +
    + +
  • consumerNewPlanning <Consumer number>

    + + The existing planning of the specified consumer is deleted.
    + The new planning is carried out immediately, taking into account the parameters set in the consumerXX attribute. +

    + +
      + Beispiel:
      + set <name> consumerNewPlanning 01
      +
    +
  • +
+
    @@ -15272,7 +15400,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 :

      @@ -15962,7 +16090,7 @@ to ensure that the system configuration is correct.
    • graphicHeaderOwnspec <Label>:<Reading> <Label>:<Reading> ...
      - Display of any reading values of the device.
      + Display of any readings, set commands and attributes of the device in the graphic header.
      The values to be displayed are separated by spaces. Four values (fields) are displayed per line.
      The input can be made in multiple lines. Values with the units "Wh" or "kWh" are converted according to the @@ -15992,6 +16120,17 @@ to ensure that the system configuration is correct.
+ + + + + + + + + + +
".$val."
$val
$val$val
#Battery
in&nbsp;today:statistic_todayBatIn
out&nbsp;today:statistic_todayBatOut
:
:
#Settings
Autocorrection:pvCorrectionFactor_Auto : : :
Consumer<br>Replanning:consumerNewPlanning : : :
Consumer<br>Quickstart:consumerImmediatePlanning : : :
Weather:graphicShowWeather : : :
History:graphicHistoryHour : : :
GraphicSize:flowGraphicSize : : :
ShowNight:graphicShowNight : : :
Debug:ctrlDebug : : :
@@ -16274,6 +16413,22 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
+
    + +
  • consumerNewPlanning <Verbrauchernummer>

    + + Es wird die vorhandene Planung des angegebenen Verbrauchers gelöscht.
    + Die Neuplanung wird unter Berücksichtigung der im consumerXX Attribut gesetzten Parameter sofort vorgenommen. +

    + +
      + Beispiel:
      + set <name> consumerNewPlanning 01
      +
    +
  • +
+
+
  • consumerImmediatePlanning <Verbrauchernummer>

    @@ -17119,7 +17274,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 :

      @@ -17808,7 +17963,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
    • graphicHeaderOwnspec <Label>:<Reading> <Label>:<Reading> ...
      - Anzeige beliebiger Readingswerte des Devices.
      + Anzeige beliebiger Readings, Set-Kommandos und Attribute des Devices im Grafikkopf.
      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 @@ -17838,6 +17993,17 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden. #Batterie in&nbsp;heute:statistic_todayBatIn out&nbsp;heute:statistic_todayBatOut + : + : + #Settings + Autokorrektur:pvCorrectionFactor_Auto : : : + Consumer<br>Neuplanung:consumerNewPlanning : : : + Consumer<br>Sofortstart:consumerImmediatePlanning : : : + Wetter:graphicShowWeather : : : + History:graphicHistoryHour : : : + GraphicSize:flowGraphicSize : : : + ShowNight:graphicShowNight : : : + Debug:ctrlDebug : : :