2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-03 16:56:54 +00:00

76_SMAPortal: contrib 2.10.0

git-svn-id: https://svn.fhem.de/fhem/trunk@22101 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
nasseeder1 2020-06-03 13:33:11 +00:00
parent 5465f042d3
commit d5d226b682

View File

@ -121,7 +121,7 @@ BEGIN {
# my $pkg = caller(0); # my $pkg = caller(0);
# my $main = $pkg; # my $main = $pkg;
# $main =~ s/^(?:.+::)?([^:]+)$/main::$1\_/gx; # $main =~ s/^(?:.+::)?([^:]+)$/main::$1\_/gx;
# foreach (@_) { # for (@_) {
# *{ $main . $_ } = *{ $pkg . '::' . $_ }; # *{ $main . $_ } = *{ $pkg . '::' . $_ };
# } # }
GP_Export( GP_Export(
@ -134,6 +134,7 @@ BEGIN {
# Versions History intern # Versions History intern
my %vNotesIntern = ( my %vNotesIntern = (
"2.10.0" => "03.06.2020 refactoring login process ",
"2.9.0" => "01.06.2020 add get today statistic data ", "2.9.0" => "01.06.2020 add get today statistic data ",
"2.8.1" => "31.05.2020 attribute timeout, maxCallCycle deleted ", "2.8.1" => "31.05.2020 attribute timeout, maxCallCycle deleted ",
"2.8.0" => "31.05.2020 refactoring process logic, attribute cookielifetime & getDataRetries deleted, command delCookieFile deleted ". "2.8.0" => "31.05.2020 refactoring process logic, attribute cookielifetime & getDataRetries deleted, command delCookieFile deleted ".
@ -193,6 +194,22 @@ my $sleepretry = 0.5; # Sleep zwischen Data Call Retries (o
my $sleepexc = 2; # Sleep vor neuem Datencall nach Überschreitung Threshold (Data Calls mit gleichem Cookie) my $sleepexc = 2; # Sleep vor neuem Datencall nach Überschreitung Threshold (Data Calls mit gleichem Cookie)
my $defmaxcycles = 19; # Standard max. Anzahl Datenabrufzyklen abgeleitet von Interval 120 (wird bei Automatic berechnet) my $defmaxcycles = 19; # Standard max. Anzahl Datenabrufzyklen abgeleitet von Interval 120 (wird bei Automatic berechnet)
my %statkeys = ( # Statistikdaten auszulesende Schlüssel
Energy => 1,
FeedIn => 1,
GridConsumption => 1,
SelfConsumption => 1,
SelfSupply => 1,
DirectConsumption => 1,
TotalConsumption => 1,
BackupOut => 1,
BackupIn => 1,
SelfConsumptionRate => 1,
DirectConsumptionRate => 1,
AutarkyRate => 1,
);
############################################################### ###############################################################
# SMAPortal Initialize # SMAPortal Initialize
############################################################### ###############################################################
@ -212,7 +229,7 @@ sub Initialize {
"interval ". "interval ".
"showPassInLog:1,0 ". "showPassInLog:1,0 ".
"userAgent ". "userAgent ".
"verbose5Data:none,balanceData,liveData,weatherData,forecastData,consumerLiveData,consumerDayData,consumerMonthData,consumerYearData ". "verbose5Data:none,loginData,balanceData,liveData,weatherData,forecastData,consumerLiveData,consumerDayData,consumerMonthData,consumerYearData ".
$readingFnAttributes; $readingFnAttributes;
eval { FHEM::Meta::InitMod( __FILE__, $hash ) }; ## no critic 'eval' # für Meta.pm (https://forum.fhem.de/index.php/topic,97589.0.html) eval { FHEM::Meta::InitMod( __FILE__, $hash ) }; ## no critic 'eval' # für Meta.pm (https://forum.fhem.de/index.php/topic,97589.0.html)
@ -295,7 +312,7 @@ sub Set { ## no critic
; ;
if($hash->{HELPER}{PLANTOID} && $hash->{HELPER}{CONSUMER}) { if($hash->{HELPER}{PLANTOID} && $hash->{HELPER}{CONSUMER}) {
my $lfd = 0; my $lfd = 0;
foreach my $key (keys %{$hash->{HELPER}{CONSUMER}{$lfd}}) { for my $key (keys %{$hash->{HELPER}{CONSUMER}{$lfd}}) {
my $dev = $hash->{HELPER}{CONSUMER}{$lfd}{DeviceName}; my $dev = $hash->{HELPER}{CONSUMER}{$lfd}{DeviceName};
if($dev) { if($dev) {
$ad .= "|" if($lfd != 0); $ad .= "|" if($lfd != 0);
@ -608,7 +625,7 @@ sub Attr {
unless ($aVal =~ /^\d+$/x) {return " The Value for $aName is not valid. Use only figures 0-9 !";} unless ($aVal =~ /^\d+$/x) {return " The Value for $aName is not valid. Use only figures 0-9 !";}
} }
if($aName =~ m/interval/x) { if($aName =~ m/interval/x) {
return qq{The interval must be >= 30 seconds or 0 if you don't want use automatic updates} if($aVal > 0 && $aVal < 30); return qq{The interval must be >= 120 seconds or 0 if you don't want use automatic updates} if($aVal > 0 && $aVal < 30);
InternalTimer(gettimeofday()+1.0, "FHEM::SMAPortal::CallInfo", $hash, 0); InternalTimer(gettimeofday()+1.0, "FHEM::SMAPortal::CallInfo", $hash, 0);
} }
} }
@ -740,9 +757,10 @@ sub GetSetData { ## no cri
my $state = "ok"; my $state = "ok";
my ($reread,$retry) = (0,0); my ($reread,$retry) = (0,0);
my ($exceed,$newcycle) = (0,0); my ($exceed,$newcycle) = (0,0);
my ($forecast_content,$weatherdata_content,$consumerlivedata_content) = ("","",""); my ($forecast_content,$weatherdata_content,$consumerlivedata_content) = ("","","");
my ($balancedataday_content,$ccdaydata_content,$ccmonthdata_content) = ("","",""); my ($balancedataday_content,$ccdaydata_content,$ccmonthdata_content,$livedata_content) = ("","","","");
my ($livedata_content,$d,$op); my ($st,$bcd,$lc,$fc,$wc,$cl,$cd,$cm,$cy) = ("","","","","","","","","");
my ($d,$op);
if($setp ne "none") { if($setp ne "none") {
# Verbraucher soll in den Status $op geschaltet werden # Verbraucher soll in den Status $op geschaltet werden
@ -757,7 +775,7 @@ sub GetSetData { ## no cri
# $ua->add_handler( request_send => sub { shift->dump; return } ); # for debugging # $ua->add_handler( request_send => sub { shift->dump; return } ); # for debugging
# $ua->add_handler( response_done => sub { shift->dump; return } ); # $ua->add_handler( response_done => sub { shift->dump; return } );
# Header Daten # Default Header Daten
$ua->default_header("Accept" => "*/*", $ua->default_header("Accept" => "*/*",
"Accept-Encoding" => "gzip, deflate, br", "Accept-Encoding" => "gzip, deflate, br",
"Accept-Language" => "en-US;q=0.7,en;q=0.3", "Accept-Language" => "en-US;q=0.7,en;q=0.3",
@ -782,69 +800,116 @@ sub GetSetData { ## no cri
my $cts = time; my $cts = time;
my $offset = fhemTzOffset($cts); my $offset = fhemTzOffset($cts);
my $time = int(($cts + $offset) * 1000); # add Timestamp in Millisekunden and UTC my $time = int(($cts + $offset) * 1000); # add Timestamp in Millisekunden and UTC
### Login
##############
my $loginp = $ua->post('https://www.sunnyportal.com/Templates/Start.aspx');
my $retcode = $loginp->code;
my $location = $loginp->header('Location') // "";
if ($loginp->is_success) {
if($v5d eq "loginData") {
Log3 ($name, 5, "$name - Status Login Page: ".$loginp->status_line);
Log3 ($name, 5, "$name - Header Location: ".$location);
Log3 ($name, 5, "$name - Login Page content: ".encode("utf8", $loginp->decoded_content));
}
$retcode = $loginp->code;
$location = $loginp->header('Location') // "";
if($location ne "/FixedPages/HoManLive.aspx" || $retcode ne "302") { # keine aktive Session -> neuer Login
Log3 ($name, 4, "$name - User not logged in. Try login with credentials ...");
# Credentials abrufen
my ($success, $username, $password) = getcredentials($hash,0);
if(!$success) {
Log3($name, 1, qq{$name - Credentials couldn't be retrieved successfully - make sure you've set it with "set $name credentials <username> <password>"});
$state = "Credentials couldn't be read";
$login_state = 0;
} else {
my $usernameField = "ctl00\$ContentPlaceHolder1\$Logincontrol1\$txtUserName";
my $passwordField = "ctl00\$ContentPlaceHolder1\$Logincontrol1\$txtPassword";
my $mempasswd = "ctl00\$ContentPlaceHolder1\$Logincontrol1\$MemorizePassword";
my $loginField = "__EVENTTARGET";
my $loginButton = "ctl00\$ContentPlaceHolder1\$Logincontrol1\$LoginBtn";
$loginp = $ua->post('https://www.sunnyportal.com/Templates/Start.aspx',[$usernameField => $username, $passwordField => $password, $mempasswd => "on", "__EVENTTARGET" => $loginButton]);
$retcode = $loginp->code;
$location = $loginp->header('Location') // "";
if($v5d eq "loginData") {
Log3 ($name, 5, "$name - Status Redirect Page : ".$retcode);
Log3 ($name, 5, "$name - Header Redirect Location: ".$location);
Log3 ($name, 5, "$name - Redirect Page content: ".encode("utf8", $loginp->decoded_content));
}
if($location eq "/FixedPages/HoManLive.aspx" && $retcode eq "302") { # Weiterleitung -> Login erfolgeich
Log3 ($name, 3, "$name - Login into SMA-Portal successfully done");
handleCounter ($name, "dailyIssueCookieCounter"); # Cookie Ausstellungszähler setzen
BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "NULL", "NULL", (gettimeofday())[0], "NULL", "NULL"], 0);
$login_state = 1;
} else {
Log3 ($name, 2, "$name - ERROR - Login into SMA-Portal failed !");
$state = "login failed - check user and password";
$login_state = 0;
}
}
}
} elsif($loginp->is_redirect) {
$retcode = $loginp->code;
$location = $loginp->header('Location') // "";
if($v5d eq "loginData") {
Log3 ($name, 5, "$name - Redirect return code: ".$retcode);
Log3 ($name, 5, "$name - Redirect Header Location: ".$location);
}
$login_state = 1;
} else {
$login_state = 0;
$state = $loginp->status_line;
Log3 ($name, 1, "$name - ERROR Login Page: ".$state);
}
if(!$login_state) {
$st = encode_base64 ( $state,"");
return "$name|0|0|$login_state|$getp|$setp|$st";
}
### Live-Daten abrufen ### Live-Daten abrufen
######################### #########################
Log3 ($name, 4, "$name - Getting Live data");
my $livedata = $ua->get( 'https://www.sunnyportal.com/homemanager?t='.$time ); # V2.6.2 my $livedata = $ua->get( 'https://www.sunnyportal.com/homemanager?t='.$time ); # V2.6.2
$livedata_content = $livedata->content; # JSON Live Daten
if(($livedata->content =~ m/FeedIn/ix) && ($livedata->content !~ m/expired/ix)) { Log3 ($name, 5, "$name - Liva Data received:\n".Dumper ($livedata_content)) if($v5d eq "liveData");
Log3 $name, 4, "$name - Login to SMA-Portal successful";
$livedata_content = $livedata->content; # JSON Live Daten
$login_state = 1;
Log3 ($name, 4, "$name - Getting live data");
Log3 ($name, 5, "$name - Data received:\n".Dumper decode_json($livedata_content)) if($v5d eq "liveData");
} else { ($reread,$retry,$login_state,$state) = analyzeData($hash,$login_state,$state,$livedata);
my $usernameField = "ctl00\$ContentPlaceHolder1\$Logincontrol1\$txtUserName";
my $passwordField = "ctl00\$ContentPlaceHolder1\$Logincontrol1\$txtPassword";
my $loginField = "__EVENTTARGET";
my $loginButton = "ctl00\$ContentPlaceHolder1\$Logincontrol1\$LoginBtn";
Log3 ($name, 4, "$name - User not logged in. Try login with credentials ...");
# Credentials abrufen
my ($success, $username, $password) = getcredentials($hash,0);
if(!$success) { if(!$login_state) {
Log3($name, 1, "$name - Credentials couldn't be retrieved successfully - make sure you've set it with \"set $name credentials <username> <password>\""); $st = encode_base64 ( $state,"");
$login_state = 0; return "$name|0|0|$login_state|$getp|$setp|$st";
} else {
my $loginp = $ua->post('https://www.sunnyportal.com/Templates/Start.aspx',[$usernameField => $username, $passwordField => $password, "__EVENTTARGET" => $loginButton]);
Log3 ($name, 5, "$name - Login-Page return code: ".$loginp->code);
Log3 ($name, 5, "$name - Login-Page return: ".$loginp->content);
if($loginp->content =~ /Logincontrol1_ErrorLabel/ix) {
Log3 ($name, 1, "$name - Error: login to SMA-Portal failed");
$livedata_content = "{\"Login-Status\":\"failed\"}";
} else {
Log3 ($name, 3, "$name - Login into SMA-Portal successfully done");
handleCounter ($name, "dailyIssueCookieCounter"); # Cookie Ausstellungszähler setzen
$livedata_content = '{"Login-Status":"successful", "InfoMessages":["login to SMA-Portal successful."]}';
BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "NULL", "NULL", (gettimeofday())[0], "NULL", "NULL"], 0);
}
my $shmp = $ua->get('https://www.sunnyportal.com/FixedPages/HoManLive.aspx');
Log3 ($name, 5, "$name - ".$shmp->code);
}
} }
($reread,$retry) = analyzeData($hash,$livedata_content);
goto &GetSetData if($reread); goto &GetSetData if($reread);
# Wiederholung Datenabruf innerhalb eines Cycle # Wiederholung Datenabruf innerhalb eines Cycle
my $retc = $hash->{HELPER}{RETRIES}; # aktuelle Retry-Zähler my $retc = $hash->{HELPER}{RETRIES}; # aktuelle Retry-Zähler
if($setp eq "none" && $retry && $retc < $maxretries) { # neuer Retry im gleichen Zyklus (nicht wenn Verbraucher schalten) if($setp eq "none" && $retry && $retc < $maxretries) { # neuer Retry im gleichen Zyklus (nicht wenn Verbraucher schalten)
$hash->{HELPER}{RETRIES}++; $hash->{HELPER}{RETRIES}++;
if($retc == $thold) { # Schwellenwert Leseversuche erreicht -> Cookie File löschen if($retc == $thold) { # Schwellenwert Leseversuche erreicht -> Cookie File löschen
Log3 ($name, 3, qq{$name - Threshold reached, delete cookie and retry ...}); Log3 ($name, 3, qq{$name - Threshold reached, delete cookie and retry ...});
sleep $sleepexc; # Threshold exceed -> Retry mit Cookie löschen sleep $sleepexc; # Threshold exceed -> Retry mit Cookie löschen
$exceed = 1; $exceed = 1;
BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "NULL", "NULL", "NULL", "NULL", $hash->{HELPER}{RETRIES}], 1); BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "NULL", "NULL", "NULL", "NULL", $hash->{HELPER}{RETRIES}], 1);
return "$name|$exceed|$newcycle|$login_state|$getp|$setp"; return "$name|$exceed|$newcycle|$login_state|$getp|$setp";
@ -857,18 +922,19 @@ sub GetSetData { ## no cri
# Wiederholung Datenabruf in einem neuen Cycle # Wiederholung Datenabruf in einem neuen Cycle
my $ac = $hash->{HELPER}{ACTCYCLE}; my $ac = $hash->{HELPER}{ACTCYCLE};
my $maxcycles = (controlParams $name)[1]; my $maxcycles = (controlParams $name)[1];
if($setp eq "none" && $retry && $ac < $maxcycles) { # neuer Zyklus (nicht wenn Verbraucher schalten) if($setp eq "none" && $retry && $ac < $maxcycles) { # neuer Zyklus (nicht wenn Verbraucher schalten)
Log3 ($name, 3, qq{$name - Maximum retries reached, delete cookie and start new cycle ...}); Log3 ($name, 3, qq{$name - Maximum retries reached, delete cookie and start new cycle ...});
$newcycle = 1; $newcycle = 1;
return "$name|$exceed|$newcycle|$login_state|$getp|$setp"; return "$name|$exceed|$newcycle|$login_state|$getp|$setp";
} }
### Verbraucher schalten ### Verbraucher schalten
####################################### #######################################
if($setp ne "none") { if($setp ne "none") {
my ($serial,$id); my ($serial,$id);
foreach my $key (keys %{$hash->{HELPER}{CONSUMER}}) { for my $key (keys %{$hash->{HELPER}{CONSUMER}}) {
my $h = $hash->{HELPER}{CONSUMER}{$key}{DeviceName}; my $h = $hash->{HELPER}{CONSUMER}{$key}{DeviceName};
if($h && $h eq $d) { if($h && $h eq $d) {
$serial = $hash->{HELPER}{CONSUMER}{$key}{SerialNumber}; $serial = $hash->{HELPER}{CONSUMER}{$key}{SerialNumber};
@ -890,7 +956,7 @@ sub GetSetData { ## no cri
$state = "ok - switched consumer $d to $op"; $state = "ok - switched consumer $d to $op";
BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "all", "none", "NULL", "NULL", "NULL"], 0); BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "all", "none", "NULL", "NULL", "NULL"], 0);
} else { } else {
$state = "Error - couldn't switched consumer $d to $op"; $state = "Error - couldn't switch consumer $d to $op";
} }
} }
@ -902,13 +968,19 @@ sub GetSetData { ## no cri
# JSON Wetterdaten # JSON Wetterdaten
Log3 ($name, 4, "$name - Getting weather data"); Log3 ($name, 4, "$name - Getting weather data");
my $weatherdata = $ua->get('https://www.sunnyportal.com/Dashboard/Weather');
my $weatherdata = $ua->get('https://www.sunnyportal.com/Dashboard/Weather');
$weatherdata_content = $weatherdata->content; $weatherdata_content = $weatherdata->content;
Log3 ($name, 5, "$name - Data received:\n".Dumper decode_json($weatherdata_content)) if($v5d eq "weatherData");
if($v5d eq "weatherData") {
Log3 ($name, 5, "$name - Return Code: ".$weatherdata->code);
Log3 ($name, 5, "$name - Data received:\n".Dumper decode_json($weatherdata_content));
}
# JSON Statistic Data Tag (anchorTime beachten !) # JSON Statistic Data Tag (anchorTime beachten !)
Log3 ($name, 4, "$name - Getting statistic day data"); Log3 ($name, 4, "$name - Getting statistic day data");
my $req = HTTP::Request->new( 'POST', 'https://www.sunnyportal.com/FixedPages/HoManEnergyRedesign.aspx/GetLegendWithValues' ); my $req = HTTP::Request->new( 'POST', 'https://www.sunnyportal.com/FixedPages/HoManEnergyRedesign.aspx/GetLegendWithValues' );
my $anchort = int ($time / 1000); # anchorTime -> abzurufendes Datum my $anchort = int ($time / 1000); # anchorTime -> abzurufendes Datum
@ -922,7 +994,10 @@ sub GetSetData { ## no cri
my $res = $ua->request( $req ); my $res = $ua->request( $req );
$balancedataday_content = $res->content; $balancedataday_content = $res->content;
Log3 ($name, 5, "$name - Data received:\n".Dumper decode_json($balancedataday_content)) if($v5d eq "balanceData"); if($v5d eq "balanceData") {
Log3 ($name, 5, "$name - Return Code: ".$res->code);
Log3 ($name, 5, "$name - Statistic data received:\n".Dumper decode_json($balancedataday_content));
}
# JSON Forecast Daten # JSON Forecast Daten
@ -930,11 +1005,11 @@ sub GetSetData { ## no cri
Log3 ($name, 4, "$name - Getting forecast data"); Log3 ($name, 4, "$name - Getting forecast data");
my $forecast_page = $ua->get('https://www.sunnyportal.com/HoMan/Forecast/LoadRecommendationData'); my $forecast_page = $ua->get('https://www.sunnyportal.com/HoMan/Forecast/LoadRecommendationData');
Log3 ($name, 5, "$name - Return Code: ".$forecast_page->code) if($v5d eq "forecastData"); $forecast_content = $forecast_page->content;
if ($forecast_page->content =~ m/ForecastChartDataPoint/ix) { if($v5d eq "forecastData") {
$forecast_content = $forecast_page->content; Log3 ($name, 5, "$name - Return Code: ".$forecast_page->code);
Log3 ($name, 5, "$name - Forecast data received:\n".Dumper decode_json($forecast_content)) if($v5d eq "forecastData"); Log3 ($name, 5, "$name - Forecast data received:\n".Dumper decode_json($forecast_content));
} }
} }
@ -942,13 +1017,12 @@ sub GetSetData { ## no cri
if($dl > 2) { if($dl > 2) {
Log3 ($name, 4, "$name - Getting consumer live data"); Log3 ($name, 4, "$name - Getting consumer live data");
my $consumerlivedata = $ua->get('https://www.sunnyportal.com/Homan/ConsumerBalance/GetLiveProxyValues'); my $consumerlivedata = $ua->get('https://www.sunnyportal.com/Homan/ConsumerBalance/GetLiveProxyValues');
$consumerlivedata_content = $consumerlivedata->content;
Log3 ($name, 5, "$name - Return Code: ".$consumerlivedata->code) if($v5d eq "consumerLiveData"); if($v5d eq "consumerLiveData") {
Log3 ($name, 5, "$name - Return Code: ".$consumerlivedata->code);
if ($consumerlivedata->content =~ m/HoManConsumerLiveData/ix) { Log3 ($name, 5, "$name - Consumer live data received:\n".Dumper decode_json($consumerlivedata_content));
$consumerlivedata_content = $consumerlivedata->content;
Log3 ($name, 5, "$name - Consumer live data received:\n".Dumper decode_json($consumerlivedata_content)) if($v5d eq "consumerLiveData");
} }
if($hash->{HELPER}{PLANTOID}) { if($hash->{HELPER}{PLANTOID}) {
@ -1014,7 +1088,6 @@ sub GetSetData { ## no cri
} }
# Daten müssen als Einzeiler zurückgegeben werden # Daten müssen als Einzeiler zurückgegeben werden
my ($st,$bcd,$lc,$fc,$wc,$cl,$cd,$cm,$cy) = ("","","","","","","","","");
$st = encode_base64 ( $state,""); $st = encode_base64 ( $state,"");
$bcd = encode_base64 ( $balancedataday_content, "" ) if($balancedataday_content); $bcd = encode_base64 ( $balancedataday_content, "" ) if($balancedataday_content);
$lc = encode_base64 ( $livedata_content, "" ) if($livedata_content); $lc = encode_base64 ( $livedata_content, "" ) if($livedata_content);
@ -1072,7 +1145,7 @@ sub ParseData { ## no critic
my $cycles = $hash->{HELPER}{ACTCYCLE}; my $cycles = $hash->{HELPER}{ACTCYCLE};
my $ctime = int(($etime - $btime) / $cycles); # durchschnittliche Laufzeit für einen Zyklus my $ctime = int(($etime - $btime) / $cycles); # durchschnittliche Laufzeit für einen Zyklus
$state = decode_base64($a[6]); $state = decode_base64($a[6]);
my ($livedata_content,$forecast_content,$weatherdata_content,$consumerlivedata_content); my ($livedata_content,$forecast_content,$weatherdata_content,$consumerlivedata_content);
my ($ccdaydata_content,$ccmonthdata_content,$ccyeardata_content,$balancedataday_content); my ($ccdaydata_content,$ccmonthdata_content,$ccyeardata_content,$balancedataday_content);
@ -1190,29 +1263,29 @@ sub ParseData { ## no critic
readingsDelete($hash,"L1_WarningMessages") if(!$warnMsg); readingsDelete($hash,"L1_WarningMessages") if(!$warnMsg);
readingsDelete($hash,"L1_InfoMessages") if(!$infoMsg); readingsDelete($hash,"L1_InfoMessages") if(!$infoMsg);
if ($forecast_content && $forecast_content !~ m/undefined/ix) { if ($forecast_content && $forecast_content !~ m/undefined/ix && $dl >= 2) {
# Auswertung der Forecast Daten # Auswertung der Forecast Daten
extractForecastData ($hash,$forecast_content); extractForecastData ($hash,$forecast_content);
extractPlantData ($hash,$forecast_content); extractPlantData ($hash,$forecast_content);
extractConsumerData ($hash,$forecast_content); extractConsumerData ($hash,$forecast_content);
} }
if ($consumerlivedata_content && $consumerlivedata_content !~ m/undefined/ix) { if ($consumerlivedata_content && $consumerlivedata_content !~ m/undefined/ix && $dl > 2) {
# Auswertung Consumer Live Daten # Auswertung Consumer Live Daten
extractConsumerLiveData ($hash,$consumerlivedata_content); extractConsumerLiveData ($hash,$consumerlivedata_content);
} }
if ($ccdaydata_content && $ccdaydata_content !~ m/undefined/ix) { if ($ccdaydata_content && $ccdaydata_content !~ m/undefined/ix && $dl > 2) {
# Auswertung Consumer Energiedaten des aktuellen Tages # Auswertung Consumer Energiedaten des aktuellen Tages
extractConsumerHistData($hash,$ccdaydata_content,"day"); extractConsumerHistData($hash,$ccdaydata_content,"day");
} }
if ($ccmonthdata_content && $ccmonthdata_content !~ m/undefined/ix) { if ($ccmonthdata_content && $ccmonthdata_content !~ m/undefined/ix && $dl > 2) {
# Auswertung Consumer Energiedaten des aktuellen Monats # Auswertung Consumer Energiedaten des aktuellen Monats
extractConsumerHistData ($hash,$ccmonthdata_content,"month"); extractConsumerHistData ($hash,$ccmonthdata_content,"month");
} }
if ($ccyeardata_content && $ccyeardata_content !~ m/undefined/ix) { if ($ccyeardata_content && $ccyeardata_content !~ m/undefined/ix && $dl > 2) {
# Auswertung Consumer Energiedaten des aktuellen Jahres # Auswertung Consumer Energiedaten des aktuellen Jahres
extractConsumerHistData ($hash,$ccyeardata_content,"year"); extractConsumerHistData ($hash,$ccyeardata_content,"year");
} }
@ -1232,25 +1305,28 @@ sub ParseData { ## no critic
my $gc = ReadingsNum($name, "L1_GridConsumption", 0); my $gc = ReadingsNum($name, "L1_GridConsumption", 0);
my $sum = $fi-$gc; my $sum = $fi-$gc;
if(!$pv && !$fi && !$gc) { if($login_state && !$pv && !$fi && !$gc) {
# keine Anlagendaten vorhanden # keine Anlagendaten vorhanden
$state = "Data can't be retrieved from SMA-Portal. Reread at next scheduled cycle."; $state = "Data can't be retrieved from SMA-Portal. Reread at next scheduled cycle.";
Log3 ($name, 2, "$name - $state"); Log3 ($name, 2, "$name - $state");
} else {
$hash->{HELPER}{LASTLDSUCCTIME} = FmtDateTime(time()) if($getp ne "none");
} }
readingsBeginUpdate($hash); readingsBeginUpdate($hash);
if($login_state) { if($login_state) {
if($setp ne "none") { if($setp ne "none") {
my ($d,$op) = split(":",$setp); 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, "L3_${d}_Switch", $op);
} }
readingsBulkUpdate($hash, "state" , $state);
readingsBulkUpdate($hash, "lastCycleTime", $ctime); readingsBulkUpdate($hash, "lastCycleTime", $ctime);
readingsBulkUpdate($hash, "summary" , "$sum W"); readingsBulkUpdate($hash, "summary" , "$sum W");
}
} else {
readingsBulkUpdate($hash, "L1_Login-Status", "failed");
}
readingsBulkUpdate($hash, "state", $state);
readingsEndUpdate($hash, 1); readingsEndUpdate($hash, 1);
delcookiefile ($hash); delcookiefile ($hash);
@ -1310,10 +1386,6 @@ sub extractForecastData { ## no critic
my $dl = AttrVal($name, "detailLevel", 1); my $dl = AttrVal($name, "detailLevel", 1);
if($dl <= 1) {
return;
}
Log3 ($name, 4, "$name - ##### extracting forecast data #### "); Log3 ($name, 4, "$name - ##### extracting forecast data #### ");
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
@ -1353,7 +1425,7 @@ sub extractForecastData { ## no critic
# Loop through all forecast objects # Loop through all forecast objects
# Energie wird als "J" geliefert, Wh = J / 3600 # Energie wird als "J" geliefert, Wh = J / 3600
foreach my $fc_obj (@{$forecast->{'ForecastSeries'}}) { for my $fc_obj (@{$forecast->{'ForecastSeries'}}) {
my $fc_datetime = $fc_obj->{'TimeStamp'}->{'DateTime'}; # Example for DateTime: 2016-02-15T23:00:00 my $fc_datetime = $fc_obj->{'TimeStamp'}->{'DateTime'}; # Example for DateTime: 2016-02-15T23:00:00
my $tkind = $fc_obj->{'TimeStamp'}->{'Kind'}; # Zeitart: Unspecified, Utc my $tkind = $fc_obj->{'TimeStamp'}->{'Kind'}; # Zeitart: Unspecified, Utc
@ -1458,55 +1530,38 @@ return;
sub extractWeatherData { sub extractWeatherData {
my ($hash,$weather) = @_; my ($hash,$weather) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my ($tsymbol,$ttoday,$ttomorrow); my @da = ();
Log3 ($name, 4, "$name - ##### extracting weather data #### "); Log3 ($name, 4, "$name - ##### extracting weather data #### ");
readingsBeginUpdate($hash);
for my $k (keys %$weather) { for my $k (keys %$weather) {
my $new_val = ""; next if(!$k);
if (defined $weather->{$k}) {
Log3 $name, 4, "$name - Weatherdata content \"$k\": ".($weather->{$k});
if ($weather->{$k} =~ m/HASH/ix) {
my $ih = $weather->{$k};
for my $i (keys %$ih) {
my $hd0 = $weather->{$k}{$i};
if(!$hd0) {
next;
}
chomp $hd0;
$hd0 =~ s/[;']//gx;
$hd0 = ($hd0 =~ /^undef$/x)?"none":$hd0;
$hd0 = encode("utf8", $hd0);
Log3 $name, 4, "$name - Weatherdata \"$k $i\": $hd0";
next if($i =~ /^WeatherIcon$/x);
$new_val = $hd0;
if ($new_val) { Log3 ($name, 4, qq{$name - Weatherdata content "$k": }.Dumper $weather->{$k});
if($i =~ /^TemperatureSymbol$/x) {
$tsymbol = $new_val; if (ref $weather->{$k} eq "HASH") {
next; my $ih = $weather->{$k};
}
if($i =~ /^Temperature$/x) { my $day = $k;
$ttoday = sprintf("%.1f",$new_val) if($k =~ /^today$/x); my $symbol = encode("utf8", $weather->{$k}{TemperatureSymbol});
$ttomorrow = sprintf("%.1f",$new_val) if($k =~ /^tomorrow$/x); my $temp = sprintf("%.1f", $weather->{$k}{Temperature});
next; my $wdesc = $weather->{$k}{WeatherDescription};
} $wdesc =~ s/t/T/x;
$k =~ s/t/T/x if($i =~ /^WeatherDescription$/x); $day =~ s/t/T/x;
Log3 ($name, 4, "$name - ${k}_${i} - $new_val"); push @da, "L1_${day}_Temperature:$temp $symbol";
readingsBulkUpdate($hash, "L1_${k}_${i}", $new_val); push @da, "L1_${day}_WeatherDescription:$wdesc";
}
}
}
} }
} }
readingsBulkUpdate($hash, "L1_Today_Temperature", "$ttoday $tsymbol") if($ttoday && $tsymbol); readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "L1_Tomorrow_Temperature", "$ttomorrow $tsymbol") if($ttomorrow && $tsymbol);
readingsEndUpdate($hash, 1); for my $elem (@da) {
my ($rn,$rval) = split ":", $elem;
readingsBulkUpdate($hash, $rn, $rval);
}
readingsEndUpdate($hash, 1);
return; return;
} }
@ -1523,21 +1578,6 @@ sub extractStatisticData {
my @da = (); my @da = ();
Log3 ($name, 4, "$name - ##### extracting statistic data #### "); Log3 ($name, 4, "$name - ##### extracting statistic data #### ");
my %ch = ( # Zieldatenhash
Energy => 1,
FeedIn => 1,
GridConsumption => 1,
SelfConsumption => 1,
SelfSupply => 1,
DirectConsumption => 1,
TotalConsumption => 1,
BackupOut => 1,
BackupIn => 1,
SelfConsumptionRate => 1,
DirectConsumptionRate => 1,
AutarkyRate => 1,
);
if(ref $statistic eq "HASH") { if(ref $statistic eq "HASH") {
$sd = decode_json ($statistic->{d}); $sd = decode_json ($statistic->{d});
@ -1547,7 +1587,7 @@ sub extractStatisticData {
for my $a (@$sd) { # jedes ARRAY-Element ist ein HASH for my $a (@$sd) { # jedes ARRAY-Element ist ein HASH
my $k = $a->{Key}; my $k = $a->{Key};
my $v = $a->{Value}; my $v = $a->{Value};
push @da, "$k:$v" if(defined $ch{$k}); push @da, "L1_Today_${k}:$v" if(defined $statkeys{$k});
} }
} }
@ -1555,7 +1595,7 @@ sub extractStatisticData {
for my $elem (@da) { for my $elem (@da) {
my ($rn,$rval) = split ":", $elem; my ($rn,$rval) = split ":", $elem;
readingsBulkUpdate($hash, "L1_Today_".$rn, $rval); readingsBulkUpdate($hash, $rn, $rval);
} }
readingsEndUpdate($hash, 1); readingsEndUpdate($hash, 1);
@ -1571,17 +1611,12 @@ sub extractPlantData {
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my ($amount,$unit); my ($amount,$unit);
my $dl = AttrVal($name, "detailLevel", 1);
if($dl <= 1) {
return;
}
Log3 ($name, 4, "$name - ##### extracting plant data #### "); Log3 ($name, 4, "$name - ##### extracting plant data #### ");
readingsBeginUpdate($hash); readingsBeginUpdate($hash);
my $ppp = $forecast->{'PlantPeakPower'}; my $ppp = $forecast->{'PlantPeakPower'};
if($ppp && $dl >= 2) { if($ppp) {
$amount = $forecast->{'PlantPeakPower'}{'Amount'}; $amount = $forecast->{'PlantPeakPower'}{'Amount'};
$unit = $forecast->{'PlantPeakPower'}{'StandardUnit'}{'Symbol'}; $unit = $forecast->{'PlantPeakPower'}{'StandardUnit'}{'Symbol'};
Log3 $name, 4, "$name - Plantdata \"PlantPeakPower Amount\": $amount"; Log3 $name, 4, "$name - Plantdata \"PlantPeakPower Amount\": $amount";
@ -1605,9 +1640,6 @@ sub extractConsumerData {
my ($key,$val); my ($key,$val);
my $dl = AttrVal($name, "detailLevel", 1); my $dl = AttrVal($name, "detailLevel", 1);
if($dl <= 1) {
return;
}
Log3 ($name, 4, "$name - ##### extracting consumer data #### "); Log3 ($name, 4, "$name - ##### extracting consumer data #### ");
@ -1615,7 +1647,7 @@ sub extractConsumerData {
# Schleife über alle Consumer Objekte # Schleife über alle Consumer Objekte
my $i = 0; my $i = 0;
foreach my $c (@{$forecast->{'Consumers'}}) { for my $c (@{$forecast->{'Consumers'}}) {
$consumers{"${i}_ConsumerName"} = encode("utf8", $c->{'ConsumerName'} ); $consumers{"${i}_ConsumerName"} = encode("utf8", $c->{'ConsumerName'} );
$consumers{"${i}_ConsumerOid"} = $c->{'ConsumerOid'}; $consumers{"${i}_ConsumerOid"} = $c->{'ConsumerOid'};
$i++; $i++;
@ -1624,13 +1656,13 @@ sub extractConsumerData {
if(%consumers && $forecast->{'ForecastTimeframes'}) { if(%consumers && $forecast->{'ForecastTimeframes'}) {
# es sind Vorhersagen zu geplanten Verbraucherschaltzeiten vorhanden # es sind Vorhersagen zu geplanten Verbraucherschaltzeiten vorhanden
# TimeFrameStart/End Kind: "Utc" # TimeFrameStart/End Kind: "Utc"
foreach my $c (@{$forecast->{'ForecastTimeframes'}{'PlannedTimeFrames'}}) { for my $c (@{$forecast->{'ForecastTimeframes'}{'PlannedTimeFrames'}}) {
my $tkind = $c->{'TimeFrameStart'}->{'Kind'}; # Zeitart: Unspecified, Utc my $tkind = $c->{'TimeFrameStart'}->{'Kind'}; # Zeitart: Unspecified, Utc
my $deviceOid = $c->{'DeviceOid'}; my $deviceOid = $c->{'DeviceOid'};
my $timeFrameStart = TimeAdjust($hash,$c->{'TimeFrameStart'}{'DateTime'},$tkind); # wandele UTC my $timeFrameStart = TimeAdjust($hash,$c->{'TimeFrameStart'}{'DateTime'},$tkind); # wandele UTC
my $timeFrameEnd = TimeAdjust($hash,$c->{'TimeFrameEnd'}{'DateTime'},$tkind); # wandele UTC my $timeFrameEnd = TimeAdjust($hash,$c->{'TimeFrameEnd'}{'DateTime'},$tkind); # wandele UTC
my $tz = $c->{'TimeFrameStart'}{'Kind'}; my $tz = $c->{'TimeFrameStart'}{'Kind'};
foreach my $k (keys(%consumers)) { for my $k (keys(%consumers)) {
$val = $consumers{$k}; $val = $consumers{$k};
if($val eq $deviceOid && $k =~ /^(\d+)_.*$/x) { if($val eq $deviceOid && $k =~ /^(\d+)_.*$/x) {
my $lfn = $1; my $lfn = $1;
@ -1642,7 +1674,7 @@ sub extractConsumerData {
} }
if(%consumers) { if(%consumers) {
foreach my $key (keys(%consumers)) { for my $key (keys(%consumers)) {
Log3 $name, 4, "$name - Consumer data \"$key\": ".$consumers{$key}; Log3 $name, 4, "$name - Consumer data \"$key\": ".$consumers{$key};
if($key =~ /ConsumerName/x && $key =~ /^(\d+)_.*$/x && $dl >= 3) { if($key =~ /ConsumerName/x && $key =~ /^(\d+)_.*$/x && $dl >= 3) {
my $lfn = $1; my $lfn = $1;
@ -1684,18 +1716,13 @@ sub extractConsumerLiveData {
my %consumers; my %consumers;
my ($key,$val,$i,$res); my ($key,$val,$i,$res);
my $dl = AttrVal($name, "detailLevel", 1);
if($dl <= 2) {
return;
}
Log3 ($name, 4, "$name - ##### extracting consumer live data #### "); Log3 ($name, 4, "$name - ##### extracting consumer live data #### ");
readingsBeginUpdate($hash); readingsBeginUpdate($hash);
# allen Consumer Objekte die ID zuordnen # allen Consumer Objekte die ID zuordnen
$i = 0; $i = 0;
foreach my $c (@{$clivedata->{'MeasurementData'}}) { for my $c (@{$clivedata->{'MeasurementData'}}) {
$consumers{"${i}_ConsumerName"} = encode("utf8", $c->{'DeviceName'} ); $consumers{"${i}_ConsumerName"} = encode("utf8", $c->{'DeviceName'} );
$consumers{"${i}_ConsumerOid"} = $c->{'Consume'}{'ConsumerOid'}; $consumers{"${i}_ConsumerOid"} = $c->{'Consume'}{'ConsumerOid'};
$consumers{"${i}_ConsumerLfd"} = $i; $consumers{"${i}_ConsumerLfd"} = $i;
@ -1718,7 +1745,7 @@ sub extractConsumerLiveData {
# es sind Daten zu den Verbrauchern vorhanden # es sind Daten zu den Verbrauchern vorhanden
# Kind: "Utc" ? # Kind: "Utc" ?
$i = 0; $i = 0;
foreach my $c (@{$clivedata->{'ParameterData'}}) { for my $c (@{$clivedata->{'ParameterData'}}) {
my $tkind = $c->{'Parameters'}[0]{'Timestamp'}{'Kind'}; # Zeitart: Unspecified, Utc my $tkind = $c->{'Parameters'}[0]{'Timestamp'}{'Kind'}; # Zeitart: Unspecified, Utc
my $GriSwStt = $c->{'Parameters'}[0]{'Value'}; # on: 1, off: 0 my $GriSwStt = $c->{'Parameters'}[0]{'Value'}; # on: 1, off: 0
my $GriSwAuto = $c->{'Parameters'}[1]{'Value'}; # automatic = 1 my $GriSwAuto = $c->{'Parameters'}[1]{'Value'}; # automatic = 1
@ -1760,11 +1787,6 @@ sub extractConsumerHistData { #
my %consumers; my %consumers;
my ($key,$val,$i,$res,$gcr,$gct,$pcr,$pct,$tct,$bcr,$bct); my ($key,$val,$i,$res,$gcr,$gct,$pcr,$pct,$tct,$bcr,$bct);
my $dl = AttrVal($name, "detailLevel", 1);
if($dl <= 2) {
return;
}
Log3 ($name, 4, "$name - ##### extracting consumer history data #### "); Log3 ($name, 4, "$name - ##### extracting consumer history data #### ");
my $bataval = (defined(ReadingsNum($name,"L1_BatteryIn", undef)) || defined(ReadingsNum($name,"L1_BatteryOut", undef)))?1:0; # Identifikation ist Battery vorhanden ? my $bataval = (defined(ReadingsNum($name,"L1_BatteryIn", undef)) || defined(ReadingsNum($name,"L1_BatteryOut", undef)))?1:0; # Identifikation ist Battery vorhanden ?
@ -1773,7 +1795,7 @@ sub extractConsumerHistData { #
# allen Consumer Objekte die ID zuordnen # allen Consumer Objekte die ID zuordnen
$i = 0; $i = 0;
foreach my $c (@{$chdata->{'Consumers'}}) { for my $c (@{$chdata->{'Consumers'}}) {
$consumers{"${i}_ConsumerName"} = encode("utf8", $c->{'DeviceName'} ); $consumers{"${i}_ConsumerName"} = encode("utf8", $c->{'DeviceName'} );
$consumers{"${i}_ConsumerOid"} = $c->{'ConsumerOid'}; $consumers{"${i}_ConsumerOid"} = $c->{'ConsumerOid'};
$consumers{"${i}_ConsumerLfd"} = $i; $consumers{"${i}_ConsumerLfd"} = $i;
@ -1887,8 +1909,8 @@ sub delread {
if($dl) { if($dl) {
# Readings ab dem angegebenen Detail-Level löschen # Readings ab dem angegebenen Detail-Level löschen
foreach my $key(@allrds) { for my $key(@allrds) {
$key =~ m/^L(\d)_.*$/x; $key =~ m/^L(\d)_/x;
if($1 && $1 >= $dl) { if($1 && $1 >= $dl) {
delete($defs{$name}{READINGS}{$key}); delete($defs{$name}{READINGS}{$key});
} }
@ -1896,7 +1918,7 @@ sub delread {
return; return;
} }
foreach my $key(@allrds) { for my $key(@allrds) {
delete($defs{$name}{READINGS}{$key}) if($key ne "state"); delete($defs{$name}{READINGS}{$key}) if($key ne "state");
} }
@ -1964,66 +1986,79 @@ return 1;
# analysiere abgerufene Daten # analysiere abgerufene Daten
################################################################ ################################################################
sub analyzeData { ## no critic 'complexity' sub analyzeData { ## no critic 'complexity'
my ($hash,$lc) = @_; my ($hash,$login_state,$state,$ad) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my ($reread,$retry) = (0,0); my ($reread,$retry) = (0,0);
my $data = "";
my $act = $hash->{HELPER}{RETRIES}; # Index aktueller Wiederholungsversuch my $v5d = AttrVal($name, "verbose5Data", "none");
my $attstr = "Attempts read data again ... ($act of $maxretries)"; my $ad_content = encode("utf8", $ad->decoded_content);
my $act = $hash->{HELPER}{RETRIES}; # Index aktueller Wiederholungsversuch
my $attstr = "Attempts read data again ... ($act of $maxretries)"; # Log vorbereiten
my $livedata_content = decode_json($lc); $data = eval{decode_json($ad_content)} or do { $data = $ad_content };
for my $k (keys %$livedata_content) {
my $new_val = ""; if (ref $data eq "HASH") {
for my $k (keys %$data) {
if (defined $livedata_content->{$k}) { my $new_val = "";
if (($livedata_content->{$k} =~ m/ARRAY/ix) || ($livedata_content->{$k} =~ m/HASH/ix)) {
if($livedata_content->{$k} =~ m/ARRAY/ix) { if (defined $data->{$k}) {
my $hd0 = Dumper($livedata_content->{$k}[0]); if (($data->{$k} =~ m/ARRAY/ix) || ($data->{$k} =~ m/HASH/ix)) {
if(!$hd0) { if($data->{$k} =~ m/ARRAY/ix) {
next; my $val = Dumper($data->{$k}[0]);
next if(!$val);
chomp $val;
$val =~ s/[;']//gx;
$val = ($val =~ /^undef$/x)? "none" : $val;
$new_val = $val;
} }
chomp $hd0;
$hd0 =~ s/[;']//gx; } else {
$hd0 = ($hd0 =~ /^undef$/x)?"none":$hd0; $new_val = $data->{$k};
$new_val = $hd0;
} }
} else {
$new_val = $livedata_content->{$k};
}
if ($new_val && $k !~ /__type/ix) { if ($new_val && $k !~ /__type/ix) {
if($k =~ m/InfoMessages/x && $new_val =~ /login to SMA-Portal successful/) { ## no critic 'regular expression' # Regular expression without "/x" flag nicht anwenden !!! if($k =~ m/WarningMessages/x && $new_val =~ /Updating of the live data was interrupted/) { ## no critic 'regular expression' # Regular expression without "/x" flag nicht anwenden !!!
# Login war erfolgreich, Daten neu lesen Log3 $name, 3, "$name - Updating of the live data was interrupted. $attstr";
Log3 $name, 3, "$name - Read portal data ..."; $retry = 1;
$reread = 1; return ($reread,$retry,$login_state,$state);
} }
if($k =~ m/WarningMessages/x && $new_val =~ /Updating of the live data was interrupted/) { ## no critic 'regular expression' # Regular expression without "/x" flag nicht anwenden !!! if($k =~ m/WarningMessages/x && $new_val =~ /The current consumption could not be determined. The current purchased electricity is unknown/) { ## no critic 'regular expression' # Regular expression without "/x" flag nicht anwenden !!!
Log3 $name, 3, "$name - Updating of the live data was interrupted. $attstr"; Log3 $name, 3, "$name - The current consumption could not be determined. The current purchased electricity is unknown. $attstr";
$retry = 1; $retry = 1;
return ($reread,$retry); return ($reread,$retry,$login_state,$state);
} }
if($k =~ m/WarningMessages/x && $new_val =~ /The current consumption could not be determined. The current purchased electricity is unknown/) { ## no critic 'regular expression' # Regular expression without "/x" flag nicht anwenden !!! if($k =~ m/ErrorMessages/x && $new_val =~ /Communication with the Sunny Home Manager is currently not possible/) { ## no critic 'regular expression' # Regular expression without "/x" flag nicht anwenden !!!
Log3 $name, 3, "$name - The current consumption could not be determined. The current purchased electricity is unknown. $attstr"; # Energiedaten konnten nicht ermittelt werden, Daten neu lesen mit Zeitverzögerung
$retry = 1; Log3 $name, 3, "$name - Communication with the Sunny Home Manager currently impossible. $attstr";
return ($reread,$retry); $retry = 1;
} return ($reread,$retry,$login_state,$state);
if($k =~ m/ErrorMessages/x && $new_val =~ /Communication with the Sunny Home Manager is currently not possible/) { ## no critic 'regular expression' # Regular expression without "/x" flag nicht anwenden !!! }
# Energiedaten konnten nicht ermittelt werden, Daten neu lesen mit Zeitverzögerung if($k =~ m/ErrorMessages/x && $new_val =~ /The current data cannot be retrieved from the PV system. Check the cabling and configuration of the following energy meters/) { ## no critic 'regular expression' # Regular expression without "/x" flag nicht anwenden !!!
Log3 $name, 3, "$name - Communication with the Sunny Home Manager currently impossible. $attstr"; # Energiedaten konnten nicht ermittelt werden, Daten neu lesen mit Zeitverzögerung
$retry = 1; Log3 $name, 3, "$name - Live data can't be retrieved. $attstr";
return ($reread,$retry); $retry = 1;
} return ($reread,$retry,$login_state,$state);
if($k =~ m/ErrorMessages/x && $new_val =~ /The current data cannot be retrieved from the PV system. Check the cabling and configuration of the following energy meters/) { ## no critic 'regular expression' # Regular expression without "/x" flag nicht anwenden !!! }
# Energiedaten konnten nicht ermittelt werden, Daten neu lesen mit Zeitverzögerung
Log3 $name, 3, "$name - Live data can't be retrieved. $attstr";
$retry = 1;
return ($reread,$retry);
} }
} }
} }
} else {
my $njdat = $ad->as_string;
if($njdat =~ /401\s-\sUnauthorized/x) {
Log3 ($name, 2, "$name - ERROR - User logged in but unauthorized");
my($p1,$p2) = $njdat =~ /<h2>401\s-\sUnauthorized:.(.*)?<\/h2>.*?<h3>(.*)?<\/h3>/sx;
$state = ($p1 // "")." ".($p2 // "");
}
Log3 ($name, 5, "$name - No JSON Data received:\n ".$njdat) if($v5d eq "loginData");
$login_state = 0;
} }
return ($reread,$retry); return ($reread,$retry,$login_state,$state);
} }
################################################################ ################################################################
@ -2134,7 +2169,7 @@ sub PortalAsHtml {
# Verbraucherlegende und Steuerung # Verbraucherlegende und Steuerung
if ($legend) { if ($legend) {
foreach (@pgCDev) { for (@pgCDev) {
my($txt,$im) = split(':',$_); # $txt ist der Verbrauchername my($txt,$im) = split(':',$_); # $txt ist der Verbrauchername
my $cmdon = "\"FW_cmd('$FW_ME$FW_subdir?XHR=1&cmd=set $name $txt on')\""; my $cmdon = "\"FW_cmd('$FW_ME$FW_subdir?XHR=1&cmd=set $name $txt on')\"";
my $cmdoff = "\"FW_cmd('$FW_ME$FW_subdir?XHR=1&cmd=set $name $txt off')\""; my $cmdoff = "\"FW_cmd('$FW_ME$FW_subdir?XHR=1&cmd=set $name $txt off')\"";
@ -2302,7 +2337,7 @@ sub PortalAsHtml {
########################################################### ###########################################################
# get consumer list and display it in portalGraphics # get consumer list and display it in portalGraphics
foreach (@pgCDev) { for (@pgCDev) {
my ($itemName, undef) = split(':',$_); my ($itemName, undef) = split(':',$_);
$itemName =~ s/^\s+|\s+$//gx; #trim it, if blanks were used $itemName =~ s/^\s+|\s+$//gx; #trim it, if blanks were used
$_ =~ s/^\s+|\s+$//gx; #trim it, if blanks were used $_ =~ s/^\s+|\s+$//gx; #trim it, if blanks were used
@ -2691,7 +2726,7 @@ sub consinject {
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $ret = ""; my $ret = "";
foreach (@pgCDev) { for (@pgCDev) {
if ($_) { if ($_) {
my ($cons,$im,$start,$end) = split (':', $_); my ($cons,$im,$start,$end) = split (':', $_);
Log3($name, 4, "$name - Consumer to show -> $cons, relative to current time -> start: $start, end: $end") if($i<1); Log3($name, 4, "$name - Consumer to show -> $cons, relative to current time -> start: $start, end: $end") if($i<1);
@ -2849,7 +2884,7 @@ sub SPGRefresh {
if($pload && ($hash->{HELPER}{SPGROOM} && !$hash->{HELPER}{SPGDETAIL} && !$fpr)) { if($pload && ($hash->{HELPER}{SPGROOM} && !$hash->{HELPER}{SPGDETAIL} && !$fpr)) {
# trifft zu wenn in einer Raumansicht # trifft zu wenn in einer Raumansicht
my @rooms = split(",",$hash->{HELPER}{SPGROOM}); my @rooms = split(",",$hash->{HELPER}{SPGROOM});
foreach (@rooms) { for (@rooms) {
my $room = $_; my $room = $_;
{ map { FW_directNotify("FILTER=room=$room", "#FHEMWEB:$_", "location.reload('true')", "") } devspec2array("TYPE=FHEMWEB") } { map { FW_directNotify("FILTER=room=$room", "#FHEMWEB:$_", "location.reload('true')", "") } devspec2array("TYPE=FHEMWEB") }
} }
@ -2866,7 +2901,7 @@ sub SPGRefresh {
# parentState des SMAPortalSPG-Device updaten # parentState des SMAPortalSPG-Device updaten
my @spgs = devspec2array("TYPE=SMAPortalSPG"); my @spgs = devspec2array("TYPE=SMAPortalSPG");
my $st = ReadingsVal($name, "state", "initialized"); my $st = ReadingsVal($name, "state", "initialized");
foreach(@spgs) { for(@spgs) {
if($defs{$_}{PARENT} eq $name) { if($defs{$_}{PARENT} eq $name) {
next if(IsDisabled($defs{$_}{NAME})); next if(IsDisabled($defs{$_}{NAME}));
readingsBeginUpdate($defs{$_}); readingsBeginUpdate($defs{$_});