diff --git a/fhem/CHANGED b/fhem/CHANGED index b2430a7db..3e9dcdd97 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. + - change: 76_SMAPortal: update time in portalgraphics changed to last + successful live data retrieval - change: 76_SMAPortal(SPG): change to package config, improve cookie management, decouple switch consumers from livedata retrieval, some improvements according diff --git a/fhem/FHEM/76_SMAPortal.pm b/fhem/FHEM/76_SMAPortal.pm index 0462a63cb..78c18994d 100644 --- a/fhem/FHEM/76_SMAPortal.pm +++ b/fhem/FHEM/76_SMAPortal.pm @@ -131,11 +131,9 @@ BEGIN { } -# Standardvariablen und Forward-Deklaration -# use vars qw($FW_ME); # webname (default is fhem) - # Versions History intern my %vNotesIntern = ( + "2.6.1" => "21.04.2020 update time in portalgraphics changed to last successful live data retrieval, credentials are not shown in list device ", "2.6.0" => "20.04.2020 change package config, improve cookie management, decouple switch consumers from livedata retrieval ". "some improvements according to PBP ", "2.5.0" => "25.08.2019 change switch consumer to on<->automatic only in graphic overview, Forum: https://forum.fhem.de/index.php/topic,102112.msg969002.html#msg969002", @@ -514,8 +512,7 @@ sub getcredentials { my ($success, $username, $passwd, $index, $retcode, $credstr); my (@key,$len,$i); - if ($boot) { - # mit $boot=1 Credentials von Platte lesen und als scrambled-String in RAM legen + if ($boot) { # mit $boot=1 Credentials von Platte lesen und als scrambled-String in RAM legen $index = $hash->{TYPE}."_".$hash->{NAME}."_credentials"; ($retcode, $credstr) = getKeyValue($index); @@ -524,17 +521,14 @@ sub getcredentials { $success = 0; } - if ($credstr) { - # beim Boot scrambled Credentials in den RAM laden - $hash->{HELPER}{CREDENTIALS} = $credstr; + if ($credstr) { # beim Boot scrambled Credentials in den RAM laden + $hash->{HELPER}{".CREDENTIALS"} = $credstr; - # "Credentials" wird als Statusbit ausgewertet. Wenn nicht gesetzt -> Warnmeldung und keine weitere Verarbeitung - $hash->{CREDENTIALS} = "Set"; + $hash->{CREDENTIALS} = "Set"; # "Credentials" wird als Statusbit ausgewertet. Wenn nicht gesetzt -> Warnmeldung und keine weitere Verarbeitung $success = 1; } - } else { - # boot = 0 -> Credentials aus RAM lesen, decoden und zurückgeben - $credstr = $hash->{HELPER}{CREDENTIALS}; + } else { # boot = 0 -> Credentials aus RAM lesen, decoden und zurückgeben + $credstr = $hash->{HELPER}{".CREDENTIALS"} // $hash->{HELPER}{CREDENTIALS}; # Kompatibilität zu Versionen vor 2.6.1 if($credstr) { # Beginn Descramble-Routine @@ -672,16 +666,16 @@ return; ################################################################ sub GetSetData { ## no critic 'complexity' my ($string) = @_; - my ($name,$getp,$setp) = split("\\|",$string); - my $hash = $defs{$name}; - my $login_state = 0; - my $useragent = AttrVal($name, "userAgent", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)"); - my $cookieLocation = AttrVal($name, "cookieLocation", "./log/mycookies.txt"); - my $v5d = AttrVal($name, "verbose5Data", "none"); - my ($forecast_content,$weatherdata_content,$consumerlivedata_content,$ccdaydata_content,$ccmonthdata_content) = ("","","","",""); + my ($name,$getp,$setp) = split("\\|",$string); + my $hash = $defs{$name}; + my $login_state = 0; + my $useragent = AttrVal($name, "userAgent", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)"); + my $cookieLocation = AttrVal($name, "cookieLocation", "./log/mycookies.txt"); + my $v5d = AttrVal($name, "verbose5Data", "none"); my ($ccyeardata_content) = (""); my $state = "ok"; - my ($reread,$retry) = (0,0); + my ($reread,$retry) = (0,0); + my ($forecast_content,$weatherdata_content,$consumerlivedata_content,$ccdaydata_content,$ccmonthdata_content) = ("","","","",""); my ($livedata_content,$d,$op); if($setp ne "none") { @@ -974,11 +968,11 @@ sub ParseData { ## no critic if ($new_val && $k !~ /__type/ix) { if($k =~ /^FeedIn$/x) { - $new_val = $new_val." W"; + $new_val = $new_val." W"; $FeedIn_done = 1 } if($k =~ /^GridConsumption$/x) { - $new_val = $new_val." W"; + $new_val = $new_val." W"; $GridConsumption_done = 1; } if($k =~ /^PV$/x) { @@ -986,30 +980,30 @@ sub ParseData { ## no critic $PV_done = 1; } if($k =~ /^AutarkyQuote$/x) { - $new_val = $new_val." %"; + $new_val = $new_val." %"; $AutarkyQuote_done = 1; } if($k =~ /^SelfConsumption$/x) { - $new_val = $new_val." W"; + $new_val = $new_val." W"; $SelfConsumption_done = 1; } if($k =~ /^SelfConsumptionQuote$/x) { - $new_val = $new_val." %"; + $new_val = $new_val." %"; $SelfConsumptionQuote_done = 1; } if($k =~ /^SelfSupply$/x) { - $new_val = $new_val." W"; + $new_val = $new_val." W"; $SelfSupply_done = 1; } if($k =~ /^TotalConsumption$/x) { $new_val = $new_val." W"; } if($k =~ /^BatteryIn$/x) { - $new_val = $new_val." W"; + $new_val = $new_val." W"; $batteryin = 1; } if($k =~ /^BatteryOut$/x) { - $new_val = $new_val." W"; + $new_val = $new_val." W"; $batteryout = 1; } @@ -1073,22 +1067,24 @@ sub ParseData { ## no critic extractWeatherData($hash,$weatherdata_content); } - my $pv = ReadingsNum($name, "L1_PV", 0); - my $fi = ReadingsNum($name, "L1_FeedIn", 0); - my $gc = ReadingsNum($name, "L1_GridConsumption", 0); + my $pv = ReadingsNum($name, "L1_PV", 0); + my $fi = ReadingsNum($name, "L1_FeedIn", 0); + my $gc = ReadingsNum($name, "L1_GridConsumption", 0); my $sum = $fi-$gc; if(!$hash->{HELPER}{RETRIES} && !$pv && !$fi && !$gc) { # keine Anlagendaten vorhanden $state = "Data can't be retrieved from SMA-Portal. Reread at next scheduled cycle."; Log3 ($name, 2, "$name - $state"); + } else { + $hash->{HELPER}{LASTLDSUCCTIME} = FmtDateTime(time()) if($getp ne "none"); } readingsBeginUpdate($hash); if($login_state) { if($setp ne "none") { my ($d,$op) = split(":",$setp); - $op = ($op eq "auto")?"off (automatic)":$op; + $op = ($op eq "auto")?"off (automatic)":$op; readingsBulkUpdate($hash, "L3_${d}_Switch", $op); } readingsBulkUpdate($hash, "state", $state); @@ -1224,8 +1220,8 @@ sub extractForecastData { ## no critic # Use also old data to integrate daily PV and Consumption if ($current_day == $fc_day) { - $PV_sum += int($fc_obj->{'PvMeanPower'}->{'Amount'}); # integrator of daily PV in Wh - $consum_sum += int($fc_obj->{'ConsumptionForecast'}->{'Amount'}/3600); # integrator of daily Consumption forecast in Wh + $PV_sum += int($fc_obj->{'PvMeanPower'}->{'Amount'}); # integrator of daily PV in Wh + $consum_sum += int($fc_obj->{'ConsumptionForecast'}->{'Amount'}/3600); # integrator of daily Consumption forecast in Wh } # Don't use old data @@ -1233,27 +1229,27 @@ sub extractForecastData { ## no critic # Sum up for the next few hours (4 hours total, this is current hour plus the next 3 hours) if ($obj_nr < 4) { - $nextFewHoursSum{'PV'} += $fc_obj->{'PvMeanPower'}->{'Amount'}; # Wh - $nextFewHoursSum{'Consumption'} += $fc_obj->{'ConsumptionForecast'}->{'Amount'} / 3600; # Wh - $nextFewHoursSum{'Total'} += $fc_obj->{'PvMeanPower'}->{'Amount'} - $fc_obj->{'ConsumptionForecast'}->{'Amount'} / 3600; # Wh - $nextFewHoursSum{'ConsumpRcmd'} += $fc_obj->{'IsConsumptionRecommended'} ? 1 : 0; + $nextFewHoursSum{'PV'} += $fc_obj->{'PvMeanPower'}->{'Amount'}; # Wh + $nextFewHoursSum{'Consumption'} += $fc_obj->{'ConsumptionForecast'}->{'Amount'} / 3600; # Wh + $nextFewHoursSum{'Total'} += $fc_obj->{'PvMeanPower'}->{'Amount'} - $fc_obj->{'ConsumptionForecast'}->{'Amount'} / 3600; # Wh + $nextFewHoursSum{'ConsumpRcmd'} += $fc_obj->{'IsConsumptionRecommended'} ? 1 : 0; } # If data is for the rest of the current day if ( $current_day == $fc_day ) { - $restOfDaySum{'PV'} += $fc_obj->{'PvMeanPower'}->{'Amount'}; # Wh - $restOfDaySum{'Consumption'} += $fc_obj->{'ConsumptionForecast'}->{'Amount'} / 3600; # Wh - $restOfDaySum{'Total'} += $fc_obj->{'PvMeanPower'}->{'Amount'} - $fc_obj->{'ConsumptionForecast'}->{'Amount'} / 3600; # Wh - $restOfDaySum{'ConsumpRcmd'} += $fc_obj->{'IsConsumptionRecommended'} ? 1 : 0; + $restOfDaySum{'PV'} += $fc_obj->{'PvMeanPower'}->{'Amount'}; # Wh + $restOfDaySum{'Consumption'} += $fc_obj->{'ConsumptionForecast'}->{'Amount'} / 3600; # Wh + $restOfDaySum{'Total'} += $fc_obj->{'PvMeanPower'}->{'Amount'} - $fc_obj->{'ConsumptionForecast'}->{'Amount'} / 3600; # Wh + $restOfDaySum{'ConsumpRcmd'} += $fc_obj->{'IsConsumptionRecommended'} ? 1 : 0; } # If data is for the next day (quick and dirty: current day different from this object's day) # Assuming only the current day and the next day are returned from Sunny Portal if ( $current_day != $fc_day ) { - $tomorrowSum{'PV'} += $fc_obj->{'PvMeanPower'}->{'Amount'} if(exists($fc_obj->{'PvMeanPower'}->{'Amount'})); # Wh - $tomorrowSum{'Consumption'} += $fc_obj->{'ConsumptionForecast'}->{'Amount'} / 3600; # Wh - $tomorrowSum{'Total'} += $fc_obj->{'PvMeanPower'}->{'Amount'} - $fc_obj->{'ConsumptionForecast'}->{'Amount'} / 3600 if ($fc_obj->{'PvMeanPower'}->{'Amount'}); # Wh - $tomorrowSum{'ConsumpRcmd'} += $fc_obj->{'IsConsumptionRecommended'} ? 1 : 0; + $tomorrowSum{'PV'} += $fc_obj->{'PvMeanPower'}->{'Amount'} if(exists($fc_obj->{'PvMeanPower'}->{'Amount'})); # Wh + $tomorrowSum{'Consumption'} += $fc_obj->{'ConsumptionForecast'}->{'Amount'} / 3600; # Wh + $tomorrowSum{'Total'} += $fc_obj->{'PvMeanPower'}->{'Amount'} - $fc_obj->{'ConsumptionForecast'}->{'Amount'} / 3600 if ($fc_obj->{'PvMeanPower'}->{'Amount'}); # Wh + $tomorrowSum{'ConsumpRcmd'} += $fc_obj->{'IsConsumptionRecommended'} ? 1 : 0; } # Update values in Fhem if less than 24 hours in the future @@ -1529,12 +1525,11 @@ sub extractConsumerLiveData { $i = 0; foreach my $c (@{$clivedata->{'ParameterData'}}) { my $tkind = $c->{'Parameters'}[0]{'Timestamp'}{'Kind'}; # Zeitart: Unspecified, Utc - # Log3 ($name, 1, "$name - $tkind"); my $GriSwStt = $c->{'Parameters'}[0]{'Value'}; # on: 1, off: 0 my $GriSwAuto = $c->{'Parameters'}[1]{'Value'}; # automatic = 1 my $OperationAutoEna = $c->{'Parameters'}[2]{'Value'}; # Automatic Betrieb erlaubt ? my $ltchange = TimeAdjust($hash,$c->{'Parameters'}[0]{'Timestamp'}{'DateTime'},$tkind); # letzter Schaltzeitpunkt der Bluetooth-Steckdose (Verbraucher) - my $cn = $consumers{"${i}_ConsumerName"}; # Verbrauchername + my $cn = $consumers{"${i}_ConsumerName"}; # Verbrauchername next if(!$cn); $cn = replaceJunkSigns($cn); # evtl. Umlaute/Leerzeichen im Verbrauchernamen ersetzen @@ -1735,8 +1730,8 @@ return; # analysiere Livedaten ################################################################ sub analyzeLivedata { ## no critic 'complexity' - my ($hash,$lc) = @_; - my $name = $hash->{NAME}; + my ($hash,$lc) = @_; + my $name = $hash->{NAME}; my ($reread,$retry) = (0,0); my $livedata_content = decode_json($lc); @@ -1885,14 +1880,15 @@ sub PortalAsHtml { $hash->{HELPER}{SPGROOM} = $FW_room?$FW_room:""; # Raum aus dem das SMAPortalSPG-Device die Funktion aufrief $hash->{HELPER}{SPGDETAIL} = $FW_detail?$FW_detail:""; # Name des SMAPortalSPG-Devices (wenn Detailansicht) - my $dl = AttrVal($name, "detailLevel", 1); - my $pv0 = ReadingsNum($name,"L2_ThisHour_PvMeanPower", undef); - my $pv1 = ReadingsNum($name,"L4_NextHour01_PvMeanPower", undef); + my $dl = AttrVal ($name, "detailLevel", 1); + my $pv0 = ReadingsNum($name, "L2_ThisHour_PvMeanPower", undef); + my $pv1 = ReadingsNum($name, "L4_NextHour01_PvMeanPower", undef); + if(!$hash || !defined($defs{$wlname}) || $dl != 4 || !defined $pv0 || !defined $pv1) { - $height = AttrNum($wlname, 'beamHeight', 200); - $ret .= ""; - $ret .= ""; - $ret .= ""; + $ret .= ""; + $ret .= "
"; + $height = AttrNum($wlname, 'beamHeight', 200); + $ret .= ""; + $ret .= ""; + $ret .= ""; - $ret .= ""; - $ret .= "
"; if(!$hash) { $ret .= "Device \"$name\" doesn't exist !"; } elsif (!defined($defs{$wlname})) { @@ -1905,9 +1901,9 @@ sub PortalAsHtml { $ret .= "Awaiting level 4 data ..."; } - $ret .= "
"; + $ret .= "
"; return $ret; } @@ -1982,10 +1978,9 @@ sub PortalAsHtml { # Beispiel mit Farbe: $icon = FW_makeImage('light_light_dim_100.svg@green'); $icon = FW_makeImage($icon) if (defined($icon)); - - my $co4h = ReadingsNum($name,"L2_Next04Hours_Consumption", 0); - my $coRe = ReadingsNum($name,"L2_RestOfDay_Consumption", 0); - my $coTo = ReadingsNum($name,"L2_Tomorrow_Consumption", 0); + my $co4h = ReadingsNum ($name,"L2_Next04Hours_Consumption", 0); + my $coRe = ReadingsNum ($name,"L2_RestOfDay_Consumption", 0); + my $coTo = ReadingsNum ($name,"L2_Tomorrow_Consumption", 0); my $pv4h = ReadingsNum($name,"L2_Next04Hours_PV", 0); my $pvRe = ReadingsNum($name,"L2_RestOfDay_PV", 0); @@ -2013,7 +2008,7 @@ sub PortalAsHtml { my $lang = AttrVal("global","language","EN"); my $alias = AttrVal($name, "alias", "SMA Sunny Portal"); # Linktext als Aliasname oder "SMA Sunny Portal" my $dlink = "$alias"; - my $lup = ReadingsTimestamp($name, "state", "0000-00-00 00:00:00"); # letzte Updatezeit + my $lup = $hash->{HELPER}{LASTLDSUCCTIME} // "0000-00-00 00:00:00"; # letzte erfolgreiche Updatezeit Live Daten my $lupt = "last update:"; my $lblPv4h = "4h:"; @@ -2021,28 +2016,28 @@ sub PortalAsHtml { my $lblPvTo = "tomorrow:"; if(AttrVal("global","language","EN") eq "DE") { # Header globales Sprachschema Deutsch - $lupt = "Stand:"; - $lblPvRe = "heute:"; - $lblPvTo = "morgen:"; + $lupt = "Stand:"; + $lblPvRe = "heute:"; + $lblPvTo = "morgen:"; } $header = ""; # Header Link + Status if($hdrDetail eq "all" || $hdrDetail eq "statusLink") { - my ($year, $month, $day, $hour, $min, $sec) = $lup =~ /(\d+)-(\d\d)-(\d\d)\s+(.*)/x; - $lup = "$3.$2.$1 $4"; - $header .= ""; + my ($year, $month, $day, $hour, $min, $sec) = $lup =~ /(\d+)-(\d\d)-(\d\d)\s+(.*)/x; + $lup = "$3.$2.$1 $4"; + $header .= ""; } # Header Information pv if($hdrDetail eq "all" || $hdrDetail eq "pv" || $hdrDetail eq "pvco") { - $header .= ""; + $header .= ""; } # Header Information co if($hdrDetail eq "all" || $hdrDetail eq "co" || $hdrDetail eq "pvco") { - $header .= ""; + $header .= ""; } $header .= "
".$dlink."(".$lupt." ".$lup.")
".$dlink."(".$lupt." ".$lup.")
PV => $lblPv4h $pv4h $lblPvRe $pvRe $lblPvTo $pvTo
PV => $lblPv4h $pv4h $lblPvRe $pvRe $lblPvTo $pvTo
CO => $lblPv4h $co4h $lblPvRe $coRe $lblPvTo $coTo
CO => $lblPv4h $co4h $lblPvRe $coRe $lblPvTo $coTo
"; @@ -2084,14 +2079,14 @@ sub PortalAsHtml { (undef,undef,undef,$end) = ReadingsVal($name,"L3_".$itemName."_PlannedOpTimeEnd",'0000-00-00 24') =~ m/(\d{4})-(\d{2})-(\d{2})\s(\d{2})/x; } - $start = int($start); - $end = int($end); + $start = int($start); + $end = int($end); my $flag = 0; # default kein Tagesverschieber #correct the hour for accurate display if ($start < $t{0}) { # consumption seems to be tomorrow $start = 24-$t{0}+$start; - $flag = 1; + $flag = 1; } else { $start -= $t{0}; }