76_SMAPortal: contrib 3.6.0

git-svn-id: https://svn.fhem.de/fhem/trunk@22955 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
DS_Starter
2020-10-11 19:25:54 +00:00
parent 92593eb46e
commit b1e6a5fa49

View File

@@ -312,6 +312,7 @@ sub Initialize {
"providerLevel:multiple-strict,".$prov." ". "providerLevel:multiple-strict,".$prov." ".
"showPassInLog:1,0 ". "showPassInLog:1,0 ".
"userAgent ". "userAgent ".
"useRelativeNames:1,0 ".
"verbose5Data:multiple-strict,none,loginData,".$v5d." ". "verbose5Data:multiple-strict,none,loginData,".$v5d." ".
$readingFnAttributes; $readingFnAttributes;
@@ -387,7 +388,8 @@ sub Set { ## no critic 'complexity'
$setlist = "Unknown argument $opt, choose one of ". $setlist = "Unknown argument $opt, choose one of ".
"credentials " "credentials "
; ;
} else { }
else {
# erweiterte Setlist wenn Credentials gesetzt # erweiterte Setlist wenn Credentials gesetzt
$setlist = "Unknown argument $opt, choose one of ". $setlist = "Unknown argument $opt, choose one of ".
"credentials ". "credentials ".
@@ -414,9 +416,9 @@ sub Set { ## no critic 'complexity'
# Verbraucher schalten # Verbraucher schalten
$hash->{HELPER}{GETTER} = "none"; $hash->{HELPER}{GETTER} = "none";
$hash->{HELPER}{SETTER} = "$opt:$prop"; $hash->{HELPER}{SETTER} = "$opt:$prop";
CallInfo($hash); CallInfo($hash);
}
} else { else {
my $params = { my $params = {
hash => $hash, hash => $hash,
name => $name, name => $name,
@@ -458,8 +460,8 @@ sub _setCredentials { ## no critic "not used"
delcookiefile ($hash); delcookiefile ($hash);
CallInfo($hash); CallInfo($hash);
return "Username and Password saved successfully"; return "Username and Password saved successfully";
}
} else { else {
return "Error while saving Username / Password - see logfile for details"; return "Error while saving Username / Password - see logfile for details";
} }
@@ -498,22 +500,26 @@ sub _setCreatePortalGraphic { ## no critic "not used"
$type = 'pv'; $type = 'pv';
$c = "SMA Sunny Portal Graphics - Forecast Generation"; $c = "SMA Sunny Portal Graphics - Forecast Generation";
$color2 = "000000"; # zweite Farbe als schwarz setzen $color2 = "000000"; # zweite Farbe als schwarz setzen
} elsif ($prop eq "Consumption") { }
elsif ($prop eq "Consumption") {
$htmldev = "SPG2.$name"; # Grafiktyp Consumption (Verbrauch) $htmldev = "SPG2.$name"; # Grafiktyp Consumption (Verbrauch)
$type = 'co'; $type = 'co';
$c = "SMA Sunny Portal Graphics - Forecast Consumption"; $c = "SMA Sunny Portal Graphics - Forecast Consumption";
$color2 = "000000"; # zweite Farbe als schwarz setzen $color2 = "000000"; # zweite Farbe als schwarz setzen
} elsif ($prop eq "Generation_Consumption") { }
elsif ($prop eq "Generation_Consumption") {
$htmldev = "SPG3.$name"; # Grafiktyp Generation_Consumption (Erzeugung und Verbrauch) $htmldev = "SPG3.$name"; # Grafiktyp Generation_Consumption (Erzeugung und Verbrauch)
$type = 'pvco'; $type = 'pvco';
$c = "SMA Sunny Portal Graphics - Forecast Generation & Consumption"; $c = "SMA Sunny Portal Graphics - Forecast Generation & Consumption";
$color2 = "FF5C82"; # zweite Farbe als rot setzen $color2 = "FF5C82"; # zweite Farbe als rot setzen
} elsif ($prop eq "Differential") { }
elsif ($prop eq "Differential") {
$htmldev = "SPG4.$name"; # Grafiktyp Differential (Differenzanzeige) $htmldev = "SPG4.$name"; # Grafiktyp Differential (Differenzanzeige)
$type = 'diff'; $type = 'diff';
$c = "SMA Sunny Portal Graphics - Forecast Differential"; $c = "SMA Sunny Portal Graphics - Forecast Differential";
$color2 = "FF5C82"; # zweite Farbe als rot setzen $color2 = "FF5C82"; # zweite Farbe als rot setzen
} else { }
else {
return "Invalid portal graphic devicetype ! Use one of \"Generation\", \"Consumption\", \"Generation_Consumption\", \"Differential\". " return "Invalid portal graphic devicetype ! Use one of \"Generation\", \"Consumption\", \"Generation_Consumption\", \"Differential\". "
} }
@@ -581,8 +587,8 @@ sub Get {
$hash->{HELPER}{SETTER} = "none"; $hash->{HELPER}{SETTER} = "none";
CallInfo($hash); CallInfo($hash);
}
} elsif ($opt eq "storedCredentials") { elsif ($opt eq "storedCredentials") {
if(!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials <username> <password>\"";} if(!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials <username> <password>\"";}
# Credentials abrufen # Credentials abrufen
my ($success, $username, $password) = getcredentials($hash,0); my ($success, $username, $password) = getcredentials($hash,0);
@@ -591,9 +597,9 @@ sub Get {
return "Stored Credentials to access SMA Portal:\n". return "Stored Credentials to access SMA Portal:\n".
"========================================\n". "========================================\n".
"Username: $username, Password: $password\n". "Username: $username, Password: $password\n".
"\n"; "\n";
}
} else { else {
return "$getlist"; return "$getlist";
} }
@@ -639,8 +645,9 @@ sub setcredentials {
if ($retcode) { if ($retcode) {
Log3($name, 1, "$name - Error while saving the Credentials - $retcode"); Log3($name, 1, "$name - Error while saving the Credentials - $retcode");
$success = 0; $success = 0;
} else { }
getcredentials($hash,1); # Credentials nach Speicherung lesen und in RAM laden ($boot=1) else {
getcredentials($hash,1); # Credentials nach Speicherung lesen und in RAM laden ($boot=1)
$success = 1; $success = 1;
} }
@@ -671,7 +678,8 @@ sub getcredentials {
$hash->{CREDENTIALS} = "Set"; # "Credentials" wird als Statusbit ausgewertet. Wenn nicht gesetzt -> Warnmeldung und keine weitere Verarbeitung $hash->{CREDENTIALS} = "Set"; # "Credentials" wird als Statusbit ausgewertet. Wenn nicht gesetzt -> Warnmeldung und keine weitere Verarbeitung
$success = 1; $success = 1;
} }
} else { # boot = 0 -> Credentials aus RAM lesen, decoden und zurückgeben }
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 $credstr = $hash->{HELPER}{".CREDENTIALS"} // $hash->{HELPER}{CREDENTIALS}; # Kompatibilität zu Versionen vor 2.6.1
if($credstr) { if($credstr) {
@@ -688,8 +696,8 @@ sub getcredentials {
my $logpw = AttrVal($name, "showPassInLog", 0) ? $passwd : "********"; my $logpw = AttrVal($name, "showPassInLog", 0) ? $passwd : "********";
Log3($name, 4, "$name - Credentials read from RAM: $username $logpw"); Log3($name, 4, "$name - Credentials read from RAM: $username $logpw");
}
} else { else {
Log3($name, 1, "$name - Credentials not set in RAM !"); Log3($name, 1, "$name - Credentials not set in RAM !");
} }
@@ -723,7 +731,8 @@ sub Attr {
delcookiefile ($hash); delcookiefile ($hash);
delete $hash->{MODE}; delete $hash->{MODE};
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
} else { }
else {
InternalTimer(gettimeofday()+1.0, "FHEM::SMAPortal::CallInfo", $hash, 0); InternalTimer(gettimeofday()+1.0, "FHEM::SMAPortal::CallInfo", $hash, 0);
} }
@@ -771,7 +780,8 @@ sub CallInfo { ## no critic 'complexity'
if(!$interval) { if(!$interval) {
$hash->{MODE} = "Manual"; $hash->{MODE} = "Manual";
} else { }
else {
$new = gettimeofday()+$interval; $new = gettimeofday()+$interval;
InternalTimer($new, "FHEM::SMAPortal::CallInfo", $hash, 0); # Wiederholungsintervall InternalTimer($new, "FHEM::SMAPortal::CallInfo", $hash, 0); # Wiederholungsintervall
$hash->{MODE} = "Automatic - next polltime: ".FmtTime($new); $hash->{MODE} = "Automatic - next polltime: ".FmtTime($new);
@@ -984,7 +994,8 @@ sub GetSetData { ## no critic 'complexity'
$state = "ok - switched consumer $d to $op"; $state = "ok - switched consumer $d to $op";
BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "NULL", "GETTER:all" ], 1); BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "NULL", "GETTER:all" ], 1);
BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "NULL", "SETTER:none"], 1); BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "NULL", "SETTER:none"], 1);
} else { }
else {
$state = "Error - couldn't switch consumer $d to $op"; $state = "Error - couldn't switch consumer $d to $op";
} }
} }
@@ -1111,8 +1122,8 @@ sub _doLogin {
Log3($name, 1, qq{$name - Credentials couldn't be retrieved successfully - make sure you've set it with "set $name credentials <username> <password>"}); 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"; $state = "Credentials couldn't be read";
$errstate = 1; $errstate = 1;
}
} else { else {
my $usernameField = "ctl00\$ContentPlaceHolder1\$Logincontrol1\$txtUserName"; my $usernameField = "ctl00\$ContentPlaceHolder1\$Logincontrol1\$txtUserName";
my $passwordField = "ctl00\$ContentPlaceHolder1\$Logincontrol1\$txtPassword"; my $passwordField = "ctl00\$ContentPlaceHolder1\$Logincontrol1\$txtPassword";
my $mempasswd = "ctl00\$ContentPlaceHolder1\$Logincontrol1\$MemorizePassword"; my $mempasswd = "ctl00\$ContentPlaceHolder1\$Logincontrol1\$MemorizePassword";
@@ -1135,9 +1146,9 @@ sub _doLogin {
if(__isLoggedIn ($name,$username,$loginp)) { # Login erfolgeich(Landing Pages können im Portal eingestellt werden!) if(__isLoggedIn ($name,$username,$loginp)) { # Login erfolgeich(Landing Pages können im Portal eingestellt werden!)
handleCounter ($name, "dailyIssueCookieCounter"); # Cookie Ausstellungszähler setzen handleCounter ($name, "dailyIssueCookieCounter"); # Cookie Ausstellungszähler setzen
BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "loginState:successful", "oldlogintime:".(gettimeofday())[0] ], 1); BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "loginState:successful", "oldlogintime:".(gettimeofday())[0] ], 1);
$errstate = 0; $errstate = 0;
}
} else { else {
Log3 ($name, 2, "$name - ERROR - Login into SMA-Portal failed !"); Log3 ($name, 2, "$name - ERROR - Login into SMA-Portal failed !");
$state = "login failed"; $state = "login failed";
BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "loginState:failed", "NULL" ], 1); BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "loginState:failed", "NULL" ], 1);
@@ -1145,8 +1156,8 @@ sub _doLogin {
} }
} }
} }
}
} elsif ($loginp->is_redirect) { elsif ($loginp->is_redirect) {
$retcode = $loginp->code; $retcode = $loginp->code;
$location = $loginp->header('Location') // ""; $location = $loginp->header('Location') // "";
Log3 ($name, 3, "$name - User is already logged in."); Log3 ($name, 3, "$name - User is already logged in.");
@@ -1158,8 +1169,8 @@ sub _doLogin {
BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "loginState:successful", "NULL" ], 1); BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "loginState:successful", "NULL" ], 1);
$errstate = 0; $errstate = 0;
}
} else { else {
$errstate = 1; $errstate = 1;
$state = $loginp->status_line; $state = $loginp->status_line;
BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "loginState:failed", "NULL" ], 1); BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "loginState:failed", "NULL" ], 1);
@@ -1508,19 +1519,20 @@ return ($errstate,$state,$reread,$retry);
# (anchorTime beachten !) # (anchorTime beachten !)
################################################################ ################################################################
sub _getBalanceDayData { ## no critic "not used" sub _getBalanceDayData { ## no critic "not used"
my $paref = shift; my $paref = shift;
my $name = $paref->{name}; my $name = $paref->{name};
my $ua = $paref->{ua}; # LWP Useragent my $ua = $paref->{ua}; # LWP Useragent
my $state = $paref->{state}; my $state = $paref->{state};
my $daref = $paref->{daref}; # Referenz zum Datenarray my $daref = $paref->{daref}; # Referenz zum Datenarray
my ($reread,$retry,$errstate) = (0,0,0); my ($reread,$retry,$errstate) = (0,0,0);
my @bd = split /\s+/x ,AttrVal($name, "balanceDay", "current"); my @bd = split /\s+/x ,AttrVal($name, "balanceDay", "current");
my $tag = "balanceDayData";
for my $bal (@bd) { for my $bal (@bd) {
my ($y,$m,$d); my ($y,$m,$d);
my $addon = "Day"; my $addon = "Day_";
if($bal !~ /current/ixms) { if($bal !~ /current/ixms) {
($y,$m,$d) = $bal =~ /(\d{4})-(\d{2})-(\d{2})/x; ($y,$m,$d) = $bal =~ /(\d{4})-(\d{2})-(\d{2})/x;
@@ -1530,7 +1542,7 @@ sub _getBalanceDayData { ## no critic "not used"
next; next;
} }
$addon .= "_".$bal; $addon .= $bal;
$y -= 1900; $y -= 1900;
$m -= 1; $m -= 1;
} }
@@ -1539,9 +1551,17 @@ sub _getBalanceDayData { ## no critic "not used"
my $time = time - ($mp * 86400); my $time = time - ($mp * 86400);
(undef,undef,undef,$d,$m,$y) = localtime($time); (undef,undef,undef,$d,$m,$y) = localtime($time);
$addon .= "_".($y+1900)."-".sprintf("%02d",($m+1))."-".sprintf("%02d",$d); my $addon1 = ($y+1900)."-".(sprintf "%02d", $m+1)."-".sprintf "%02d",$d;
Log3 ($name, 4, qq{$name - retrieve relative balanceDayData "$addon"}); my $params = {
name => $name,
bal => $bal,
tag => $tag,
daref => $daref,
addon => $addon,
addon1 => $addon1,
};
$addon = createDateAddon ($params);
} }
eval { timelocal(0, 0, 0, $d, $m, $y) } or do { $state = (split(" at", $@))[0]; eval { timelocal(0, 0, 0, $d, $m, $y) } or do { $state = (split(" at", $@))[0];
@@ -1549,10 +1569,12 @@ sub _getBalanceDayData { ## no critic "not used"
Log3($name, 2, "$name - ERROR - invalid date/time format in attribute 'balanceDay' detected: $state"); Log3($name, 2, "$name - ERROR - invalid date/time format in attribute 'balanceDay' detected: $state");
return ($errstate,$state,$reread,$retry); return ($errstate,$state,$reread,$retry);
}; };
Log3 ($name, 4, "$name - retrieve $tag ".($y+1900)."-".(sprintf "%02d", $m+1)."-".sprintf "%02d",$d );
my $cts = fhemTimeLocal(0, 0, 0, $d, $m, $y); my $cts = fhemTimeLocal(0, 0, 0, $d, $m, $y);
my $offset = fhemTzOffset($cts); my $offset = fhemTzOffset($cts);
my $anchort = int($cts + $offset); # anchorTime in UTC -> abzurufendes Datum my $anchort = int($cts + $offset); # anchorTime in UTC -> abzurufendes Datum
my $tab = 1; # Tab 1 -> Tag , 2->Monat, 3->Jahr, 4->Gesamt my $tab = 1; # Tab 1 -> Tag , 2->Monat, 3->Jahr, 4->Gesamt
my %fields = ("Content-Type" => "application/json; charset=utf-8"); my %fields = ("Content-Type" => "application/json; charset=utf-8");
@@ -1561,15 +1583,14 @@ sub _getBalanceDayData { ## no critic "not used"
($errstate,$state) = __dispatchPost ({ name => $name, ($errstate,$state) = __dispatchPost ({ name => $name,
ua => $ua, ua => $ua,
call => 'https://www.sunnyportal.com/FixedPages/HoManEnergyRedesign.aspx/GetLegendWithValues', call => 'https://www.sunnyportal.com/FixedPages/HoManEnergyRedesign.aspx/GetLegendWithValues',
tag => "balanceDayData", tag => $tag,
state => $state, state => $state,
fnaref => [ qw( extractStatisticData ) ], fnaref => [ qw( extractStatisticData ) ],
fields => \%fields, fields => \%fields,
content => $cont, content => $cont,
addon => $addon, addon => $addon,
daref => $daref daref => $daref
}); });
} }
return ($errstate,$state,$reread,$retry); return ($errstate,$state,$reread,$retry);
@@ -1580,19 +1601,20 @@ return ($errstate,$state,$reread,$retry);
# (anchorTime beachten !) # (anchorTime beachten !)
################################################################ ################################################################
sub _getBalanceMonthData { ## no critic "not used" sub _getBalanceMonthData { ## no critic "not used"
my $paref = shift; my $paref = shift;
my $name = $paref->{name}; my $name = $paref->{name};
my $ua = $paref->{ua}; # LWP Useragent my $ua = $paref->{ua}; # LWP Useragent
my $state = $paref->{state}; my $state = $paref->{state};
my $daref = $paref->{daref}; # Referenz zum Datenarray my $daref = $paref->{daref}; # Referenz zum Datenarray
my ($reread,$retry,$errstate) = (0,0,0); my ($reread,$retry,$errstate) = (0,0,0);
my @bd = split /\s+/x ,AttrVal($name, "balanceMonth", "current"); my @bd = split /\s+/x ,AttrVal($name, "balanceMonth", "current");
my $tag = "balanceMonthData";
for my $bal (@bd) { for my $bal (@bd) {
my ($y,$m); my ($y,$m);
my $addon = "Month"; my $addon = "Month_";
if($bal !~ /current/ixms) { if($bal !~ /current/ixms) {
($y,$m) = $bal =~ /^(\d{4})-(\d{2})$/x; ($y,$m) = $bal =~ /^(\d{4})-(\d{2})$/x;
@@ -1602,7 +1624,7 @@ sub _getBalanceMonthData { ## no critic "not used"
next; next;
} }
$addon .= "_".$bal; $addon .= $bal;
$y -= 1900; $y -= 1900;
$m -= 1; $m -= 1;
} }
@@ -1611,9 +1633,9 @@ sub _getBalanceMonthData { ## no critic "not used"
my $yc = int($mp/12); # Anzahl der Jahre my $yc = int($mp/12); # Anzahl der Jahre
my $mc = $mp % 12; # Anzahl Restmonate my $mc = $mp % 12; # Anzahl Restmonate
$y = (localtime(time))[5]; $y = (localtime(time))[5];
$y -= $yc; $y -= $yc;
$m = (localtime(time))[4]; $m = (localtime(time))[4];
if($m-$mc < 1) { if($m-$mc < 1) {
$m = 12-abs($m-$mc); $m = 12-abs($m-$mc);
@@ -1623,9 +1645,17 @@ sub _getBalanceMonthData { ## no critic "not used"
$m = $m-$mc; $m = $m-$mc;
} }
$addon .= "_".($y+1900)."-".sprintf("%02d",($m+1)); my $addon1 = ($y+1900)."-".sprintf "%02d", $m+1;
Log3 ($name, 4, qq{$name - retrieve relative balanceMonthData "$addon"}); my $params = {
name => $name,
bal => $bal,
tag => $tag,
daref => $daref,
addon => $addon,
addon1 => $addon1,
};
$addon = createDateAddon ($params);
} }
eval { timelocal(0, 0, 0, 1, $m, $y) } or do { $state = (split(" at", $@))[0]; eval { timelocal(0, 0, 0, 1, $m, $y) } or do { $state = (split(" at", $@))[0];
@@ -1634,9 +1664,11 @@ sub _getBalanceMonthData { ## no critic "not used"
return ($errstate,$state,$reread,$retry); return ($errstate,$state,$reread,$retry);
}; };
my $cts = fhemTimeLocal(0, 0, 0, 1, $m, $y); Log3 ($name, 4, "$name - retrieve $tag ".($y+1900)."-".sprintf "%02d", $m+1);
my $offset = fhemTzOffset($cts);
my $anchort = int($cts + $offset); # anchorTime in UTC -> abzurufendes Datum my $cts = fhemTimeLocal(0, 0, 0, 1, $m, $y);
my $offset = fhemTzOffset($cts);
my $anchort = int($cts + $offset); # anchorTime in UTC -> abzurufendes Datum
my $tab = 2; # Tab 1 -> Tag , 2->Monat, 3->Jahr, 4->Gesamt my $tab = 2; # Tab 1 -> Tag , 2->Monat, 3->Jahr, 4->Gesamt
my %fields = ("Content-Type" => "application/json; charset=utf-8"); my %fields = ("Content-Type" => "application/json; charset=utf-8");
@@ -1645,15 +1677,14 @@ sub _getBalanceMonthData { ## no critic "not used"
($errstate,$state) = __dispatchPost ({ name => $name, ($errstate,$state) = __dispatchPost ({ name => $name,
ua => $ua, ua => $ua,
call => 'https://www.sunnyportal.com/FixedPages/HoManEnergyRedesign.aspx/GetLegendWithValues', call => 'https://www.sunnyportal.com/FixedPages/HoManEnergyRedesign.aspx/GetLegendWithValues',
tag => "balanceMonthData", tag => $tag,
state => $state, state => $state,
fnaref => [ qw( extractStatisticData ) ], fnaref => [ qw( extractStatisticData ) ],
fields => \%fields, fields => \%fields,
content => $cont, content => $cont,
addon => $addon, addon => $addon,
daref => $daref daref => $daref
}); });
} }
return ($errstate,$state,$reread,$retry); return ($errstate,$state,$reread,$retry);
@@ -1664,19 +1695,20 @@ return ($errstate,$state,$reread,$retry);
# (anchorTime beachten !) # (anchorTime beachten !)
################################################################ ################################################################
sub _getBalanceYearData { ## no critic "not used" sub _getBalanceYearData { ## no critic "not used"
my $paref = shift; my $paref = shift;
my $name = $paref->{name}; my $name = $paref->{name};
my $ua = $paref->{ua}; # LWP Useragent my $ua = $paref->{ua}; # LWP Useragent
my $state = $paref->{state}; my $state = $paref->{state};
my $daref = $paref->{daref}; # Referenz zum Datenarray my $daref = $paref->{daref}; # Referenz zum Datenarray
my ($reread,$retry,$errstate) = (0,0,0); my ($reread,$retry,$errstate) = (0,0,0);
my @bd = split /\s+/x ,AttrVal($name, "balanceYear", "current"); my @bd = split /\s+/x ,AttrVal($name, "balanceYear", "current");
my $tag = "balanceYearData";
for my $bal (@bd) { for my $bal (@bd) {
my $y; my $y;
my $addon = "Year"; my $addon = "Year_";
if($bal !~ /current/ixms) { if($bal !~ /current/ixms) {
($y) = $bal =~ /^(\d{4})$/x; ($y) = $bal =~ /^(\d{4})$/x;
@@ -1686,16 +1718,24 @@ sub _getBalanceYearData { ## no critic "not used"
next; next;
} }
$addon .= "_".$bal; $addon .= $bal;
$y -= 1900; $y -= 1900;
} }
else { else {
my $mp = (split "-", $bal)[1] // 0; my $mp = (split "-", $bal)[1] // 0;
$y = (localtime(time))[5]; $y = (localtime(time))[5];
$y -= $mp; $y -= $mp;
$addon .= "_".($y+1900); my $addon1 = $y+1900;
Log3 ($name, 4, qq{$name - retrieve relative balanceYearData "$addon"}); my $params = {
name => $name,
bal => $bal,
tag => $tag,
daref => $daref,
addon => $addon,
addon1 => $addon1,
};
$addon = createDateAddon ($params);
} }
eval { timelocal(0, 0, 0, 1, 1, $y) } or do { $state = (split(" at", $@))[0]; eval { timelocal(0, 0, 0, 1, 1, $y) } or do { $state = (split(" at", $@))[0];
@@ -1703,10 +1743,12 @@ sub _getBalanceYearData { ## no critic "not used"
Log3($name, 2, "$name - ERROR - invalid date/time format in attribute 'balanceYear' detected: $state"); Log3($name, 2, "$name - ERROR - invalid date/time format in attribute 'balanceYear' detected: $state");
return ($errstate,$state,$reread,$retry); return ($errstate,$state,$reread,$retry);
}; };
Log3 ($name, 4, "$name - retrieve $tag ".($y+1900));
my $cts = fhemTimeLocal(0, 0, 0, 1, 1, $y); my $cts = fhemTimeLocal(0, 0, 0, 1, 1, $y);
my $offset = fhemTzOffset($cts); my $offset = fhemTzOffset($cts);
my $anchort = int($cts + $offset); # anchorTime in UTC -> abzurufendes Datum my $anchort = int($cts + $offset); # anchorTime in UTC -> abzurufendes Datum
my $tab = 3; # Tab 1 -> Tag , 2->Monat, 3->Jahr, 4->Gesamt my $tab = 3; # Tab 1 -> Tag , 2->Monat, 3->Jahr, 4->Gesamt
my %fields = ("Content-Type" => "application/json; charset=utf-8"); my %fields = ("Content-Type" => "application/json; charset=utf-8");
@@ -1715,7 +1757,7 @@ sub _getBalanceYearData { ## no critic "not used"
($errstate,$state) = __dispatchPost ({ name => $name, ($errstate,$state) = __dispatchPost ({ name => $name,
ua => $ua, ua => $ua,
call => 'https://www.sunnyportal.com/FixedPages/HoManEnergyRedesign.aspx/GetLegendWithValues', call => 'https://www.sunnyportal.com/FixedPages/HoManEnergyRedesign.aspx/GetLegendWithValues',
tag => "balanceYearData", tag => $tag,
state => $state, state => $state,
fnaref => [ qw( extractStatisticData ) ], fnaref => [ qw( extractStatisticData ) ],
fields => \%fields, fields => \%fields,
@@ -2057,8 +2099,8 @@ sub ___analyzeData { ## no critic 'complexity'
} }
} }
} }
}
} else { else {
my $njdat = encode("utf8", $ad->as_string); my $njdat = encode("utf8", $ad->as_string);
if($njdat =~ /401\s-\sUnauthorized/x) { if($njdat =~ /401\s-\sUnauthorized/x) {
@@ -2517,7 +2559,7 @@ sub extractStatisticData {
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $sd; my $sd;
Log3 ($name, 4, "$name - ##### extracting balance data #### "); Log3 ($name, 4, "$name - extracting balance data ");
$statistic = eval{decode_json($statistic)} or do { Log3 ($name, 2, "$name - ERROR - can't decode JSON Data"); $statistic = eval{decode_json($statistic)} or do { Log3 ($name, 2, "$name - ERROR - can't decode JSON Data");
return; return;
@@ -2569,7 +2611,8 @@ sub extractPlantMasterData {
Log3 ($name, 4, "$name - Plant ID: ".$plantOid); Log3 ($name, 4, "$name - Plant ID: ".$plantOid);
$hash->{HELPER}{PLANTOID} = $plantOid; $hash->{HELPER}{PLANTOID} = $plantOid;
BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "NULL", "PLANTOID:$plantOid"], 1); BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "NULL", "PLANTOID:$plantOid"], 1);
} else { }
else {
Log3 ($name, 4, "$name - Plant ID not set !"); Log3 ($name, 4, "$name - Plant ID not set !");
} }
@@ -2695,16 +2738,20 @@ sub extractConsumerPlanData {
my $rb = "${lv}_${cn}_PlannedOpTimeBegin"; my $rb = "${lv}_${cn}_PlannedOpTimeBegin";
my $re = "${lv}_${cn}_PlannedOpTimeEnd"; my $re = "${lv}_${cn}_PlannedOpTimeEnd";
my $rp = "${lv}_${cn}_Planned"; my $rp = "${lv}_${cn}_Planned";
if($pos) { if($pos) {
push @$daref, "$rb:$pos"; push @$daref, "$rb:$pos";
push @$daref, "$rp:yes"; push @$daref, "$rp:yes";
} else { }
else {
push @$daref, "$rb:undefined"; push @$daref, "$rb:undefined";
push @$daref, "$rp:no"; push @$daref, "$rp:no";
} }
if($poe) { if($poe) {
push @$daref, "$re:$poe"; push @$daref, "$re:$poe";
} else { }
else {
push @$daref, "$re:undefined"; push @$daref, "$re:undefined";
} }
} }
@@ -2814,11 +2861,14 @@ sub extractConsumerCurrentdata {
if(!$GriSwStt && $GriSwAuto) { if(!$GriSwStt && $GriSwAuto) {
$res = "off (automatic)"; $res = "off (automatic)";
} elsif (!$GriSwStt && !$GriSwAuto) { }
elsif (!$GriSwStt && !$GriSwAuto) {
$res = "off"; $res = "off";
} elsif ($GriSwStt) { }
elsif ($GriSwStt) {
$res = "on"; $res = "on";
} else { }
else {
$res = "undefined"; $res = "undefined";
} }
@@ -2943,7 +2993,8 @@ sub setVersionInfo {
$modules{$type}{META}{version} = "v".$v; # Version aus META.json überschreiben, Anzeige mit {Dumper $modules{SMAPortal}{META}} $modules{$type}{META}{version} = "v".$v; # Version aus META.json überschreiben, Anzeige mit {Dumper $modules{SMAPortal}{META}}
if($modules{$type}{META}{x_version}) { # {x_version} ( nur gesetzt wenn $Id: 76_SMAPortal.pm 22640 2020-08-21 07:30:21Z DS_Starter $ im Kopf komplett! vorhanden ) if($modules{$type}{META}{x_version}) { # {x_version} ( nur gesetzt wenn $Id: 76_SMAPortal.pm 22640 2020-08-21 07:30:21Z DS_Starter $ im Kopf komplett! vorhanden )
$modules{$type}{META}{x_version} =~ s/1\.1\.1/$v/gx; $modules{$type}{META}{x_version} =~ s/1\.1\.1/$v/gx;
} else { }
else {
$modules{$type}{META}{x_version} = $v; $modules{$type}{META}{x_version} = $v;
} }
return $@ unless (FHEM::Meta::SetInternals($hash)); # FVERSION wird gesetzt ( nur gesetzt wenn $Id: 76_SMAPortal.pm 22640 2020-08-21 07:30:21Z DS_Starter $ im Kopf komplett! vorhanden ) return $@ unless (FHEM::Meta::SetInternals($hash)); # FVERSION wird gesetzt ( nur gesetzt wenn $Id: 76_SMAPortal.pm 22640 2020-08-21 07:30:21Z DS_Starter $ im Kopf komplett! vorhanden )
@@ -2952,7 +3003,8 @@ sub setVersionInfo {
# mit {<Modul>->VERSION()} im FHEMWEB kann Modulversion abgefragt werden # mit {<Modul>->VERSION()} im FHEMWEB kann Modulversion abgefragt werden
use version 0.77; our $VERSION = FHEM::Meta::Get( $hash, 'version' ); ## no critic 'VERSION' use version 0.77; our $VERSION = FHEM::Meta::Get( $hash, 'version' ); ## no critic 'VERSION'
} }
} else { }
else {
# herkömmliche Modulstruktur # herkömmliche Modulstruktur
$hash->{VERSION} = $v; $hash->{VERSION} = $v;
} }
@@ -3024,6 +3076,31 @@ sub deleteData {
return; return;
} }
################################################################
# erstelle addon als relative oder reale Datumangabe
################################################################
sub createDateAddon {
my $paref = shift;
my $name = $paref->{name};
my $bal = $paref->{bal};
my $tag = $paref->{tag};
my $daref = $paref->{daref};
my $addon = $paref->{addon};
my $addon1 = $paref->{addon1};
if(AttrVal($name,"useRelativeNames", 0)) { # current-x verwenden statt effektives Datum
$addon .= $bal;
my $lv = $stpl{$tag}{level};
push @$daref, "${lv}_${addon}_Date:$addon1";
}
else {
$addon .= $addon1;
}
return $addon;
}
################################################################ ################################################################
# statistische Counter managen # statistische Counter managen
# $name = Name Device # $name = Name Device
@@ -3067,11 +3144,14 @@ sub setFromBlocking {
if($helper ne "NULL") { if($helper ne "NULL") {
my ($hnam,$k1,$k2,$k3) = split ":", $helper, 4; my ($hnam,$k1,$k2,$k3) = split ":", $helper, 4;
if(defined $k3) { if(defined $k3) {
$hash->{HELPER}{"$hnam"}{"$k1"}{"$k2"} = $k3; $hash->{HELPER}{"$hnam"}{"$k1"}{"$k2"} = $k3;
} elsif (defined $k2) { }
elsif (defined $k2) {
$hash->{HELPER}{"$hnam"}{"$k1"} = $k2; $hash->{HELPER}{"$hnam"}{"$k1"} = $k2;
} else { }
else {
$hash->{HELPER}{"$hnam"} = $k1; $hash->{HELPER}{"$hnam"} = $k1;
} }
} }
@@ -3113,7 +3193,8 @@ sub TimeAdjust {
if(lc($tkind) =~ /unspecified/x) { if(lc($tkind) =~ /unspecified/x) {
if($isdst) { if($isdst) {
$epoch = $epoch - 7200; $epoch = $epoch - 7200;
} else { }
else {
$epoch = $epoch - 3600; $epoch = $epoch - 3600;
} }
} }
@@ -3125,7 +3206,8 @@ sub TimeAdjust {
if(AttrVal("global","language","EN") eq "DE") { if(AttrVal("global","language","EN") eq "DE") {
return (sprintf("%02d.%02d.%04d %02d:%s", $lday,$lmonth,$lyear,$lhour,$rest)); return (sprintf("%02d.%02d.%04d %02d:%s", $lday,$lmonth,$lyear,$lhour,$rest));
} else { }
else {
return (sprintf("%04d-%02d-%02d %02d:%s", $lyear,$lmonth,$lday,$lhour,$rest)); return (sprintf("%04d-%02d-%02d %02d:%s", $lyear,$lmonth,$lday,$lhour,$rest));
} }
} }
@@ -3185,13 +3267,17 @@ sub PortalAsHtml {
$ret .= "<td>"; $ret .= "<td>";
if(!$hash) { ## no critic "Cascading" if(!$hash) { ## no critic "Cascading"
$ret .= "Device \"$name\" doesn't exist !"; $ret .= "Device \"$name\" doesn't exist !";
} elsif (!defined($defs{$wlname})) { }
elsif (!defined($defs{$wlname})) {
$ret .= "Graphic device \"$wlname\" doesn't exist !"; $ret .= "Graphic device \"$wlname\" doesn't exist !";
} elsif (!$fdo) { }
elsif (!$fdo) {
$ret .= qq{The attribute "providerLevel" of device "$name" must contain the level "forecastData" and data must be retrieved !}; $ret .= qq{The attribute "providerLevel" of device "$name" must contain the level "forecastData" and data must be retrieved !};
} elsif (!defined $pv0) { }
elsif (!defined $pv0) {
$ret .= "Awaiting minor level forecast data ..."; $ret .= "Awaiting minor level forecast data ...";
} elsif (!defined $pv1) { }
elsif (!defined $pv1) {
$ret .= "Awaiting major level forecast data ..."; $ret .= "Awaiting major level forecast data ...";
} }
@@ -3224,18 +3310,21 @@ sub PortalAsHtml {
my $swicon = "<img src=\"$FW_ME/www/images/default/1px-spacer.png\">"; my $swicon = "<img src=\"$FW_ME/www/images/default/1px-spacer.png\">";
if($swstate eq "off") { if($swstate eq "off") {
$swicon = "<a onClick=$cmdon><img src=\"$FW_ME/www/images/default/10px-kreis-rot.png\"></a>"; $swicon = "<a onClick=$cmdon><img src=\"$FW_ME/www/images/default/10px-kreis-rot.png\"></a>";
} elsif ($swstate eq "on") { }
elsif ($swstate eq "on") {
$swicon = "<a onClick=$cmdauto><img src=\"$FW_ME/www/images/default/10px-kreis-gruen.png\"></a>"; $swicon = "<a onClick=$cmdauto><img src=\"$FW_ME/www/images/default/10px-kreis-gruen.png\"></a>";
} elsif ($swstate =~ /off.*automatic.*/ix) { }
elsif ($swstate =~ /off.*automatic.*/ix) {
$swicon = "<a onClick=$cmdon><img src=\"$FW_ME/www/images/default/10px-kreis-gelb.png\"></a>"; $swicon = "<a onClick=$cmdon><img src=\"$FW_ME/www/images/default/10px-kreis-gelb.png\"></a>";
} }
if ($legend_style eq 'icon') { # mögliche Umbruchstellen mit normalen Blanks vorsehen ! if ($legend_style eq 'icon') { # mögliche Umbruchstellen mit normalen Blanks vorsehen !
$legend_txt .= $txt.'&nbsp;'.FW_makeImage($im).' '.$swicon.'&nbsp;&nbsp;'; $legend_txt .= $txt.'&nbsp;'.FW_makeImage($im).' '.$swicon.'&nbsp;&nbsp;';
} else { }
else {
my (undef,$co) = split('\@',$im); my (undef,$co) = split('\@',$im);
$co = '#cccccc' if (!$co); # Farbe per default $co = '#cccccc' if (!$co); # Farbe per default
$legend_txt .= '<font color=\''.$co.'\'>'.$txt.'</font> '.$swicon.'&nbsp;&nbsp;'; # hier auch Umbruch erlauben $legend_txt .= '<font color=\''.$co.'\'>'.$txt.'</font> '.$swicon.'&nbsp;&nbsp;'; # hier auch Umbruch erlauben
} }
} }
} }
@@ -3291,7 +3380,8 @@ sub PortalAsHtml {
$pvRe = sprintf("%.1f" , $pvRe/1000)."&nbsp;kWh"; $pvRe = sprintf("%.1f" , $pvRe/1000)."&nbsp;kWh";
$pvTo = sprintf("%.1f" , $pvTo/1000)."&nbsp;kWh"; $pvTo = sprintf("%.1f" , $pvTo/1000)."&nbsp;kWh";
$pvCu = sprintf("%.1f" , $pvCu/1000)."&nbsp;kW"; $pvCu = sprintf("%.1f" , $pvCu/1000)."&nbsp;kW";
} else { }
else {
$co4h .= "&nbsp;Wh"; $co4h .= "&nbsp;Wh";
$coRe .= "&nbsp;Wh"; $coRe .= "&nbsp;Wh";
$coTo .= "&nbsp;Wh"; $coTo .= "&nbsp;Wh";
@@ -3332,7 +3422,8 @@ sub PortalAsHtml {
if(AttrVal("global","language","EN") eq "DE") { if(AttrVal("global","language","EN") eq "DE") {
$lup = "$day.$month.$year&nbsp;$time"; $lup = "$day.$month.$year&nbsp;$time";
} else { }
else {
$lup = "$year-$month-$day&nbsp;$time"; $lup = "$year-$month-$day&nbsp;$time";
} }
@@ -3347,9 +3438,11 @@ sub PortalAsHtml {
if ($upstate =~ /ok/ix) { if ($upstate =~ /ok/ix) {
$upicon = "<a onClick=$cmdupdate><img src=\"$FW_ME/www/images/default/10px-kreis-gruen.png\"></a>"; $upicon = "<a onClick=$cmdupdate><img src=\"$FW_ME/www/images/default/10px-kreis-gruen.png\"></a>";
} elsif ($upstate =~ /running/ix) { }
elsif ($upstate =~ /running/ix) {
$upicon = "<img src=\"$FW_ME/www/images/default/10px-kreis-gelb.png\"></a>"; $upicon = "<img src=\"$FW_ME/www/images/default/10px-kreis-gelb.png\"></a>";
} else { }
else {
$upicon = "<a onClick=$cmdupdate><img src=\"$FW_ME/www/images/default/10px-kreis-rot.png\"></a>"; $upicon = "<a onClick=$cmdupdate><img src=\"$FW_ME/www/images/default/10px-kreis-rot.png\"></a>";
} }
@@ -3391,7 +3484,8 @@ sub PortalAsHtml {
if(AttrVal("global","language","EN") eq "DE") { if(AttrVal("global","language","EN") eq "DE") {
(undef,undef,undef,$t{0}) = ReadingsVal($name,"${fmin}_ThisHour_Time",'0') =~ m/(\d{2}).(\d{2}).(\d{4})\s(\d{2})/x; (undef,undef,undef,$t{0}) = ReadingsVal($name,"${fmin}_ThisHour_Time",'0') =~ m/(\d{2}).(\d{2}).(\d{4})\s(\d{2})/x;
} else { }
else {
(undef,undef,undef,$t{0}) = ReadingsVal($name,"${fmin}_ThisHour_Time",'0') =~ m/(\d{4})-(\d{2})-(\d{2})\s(\d{2})/x; (undef,undef,undef,$t{0}) = ReadingsVal($name,"${fmin}_ThisHour_Time",'0') =~ m/(\d{4})-(\d{2})-(\d{2})\s(\d{2})/x;
} }
@@ -3412,7 +3506,8 @@ sub PortalAsHtml {
if(AttrVal("global","language","EN") eq "DE") { if(AttrVal("global","language","EN") eq "DE") {
(undef,undef,undef,$start) = ReadingsVal($name,"${fmaj}_".$itemName."_PlannedOpTimeBegin",'00.00.0000 24') =~ m/(\d{2}).(\d{2}).(\d{4})\s(\d{2})/x; (undef,undef,undef,$start) = ReadingsVal($name,"${fmaj}_".$itemName."_PlannedOpTimeBegin",'00.00.0000 24') =~ m/(\d{2}).(\d{2}).(\d{4})\s(\d{2})/x;
(undef,undef,undef,$end) = ReadingsVal($name,"${fmaj}_".$itemName."_PlannedOpTimeEnd",'00.00.0000 24') =~ m/(\d{2}).(\d{2}).(\d{4})\s(\d{2})/x; (undef,undef,undef,$end) = ReadingsVal($name,"${fmaj}_".$itemName."_PlannedOpTimeEnd",'00.00.0000 24') =~ m/(\d{2}).(\d{2}).(\d{4})\s(\d{2})/x;
} else { }
else {
(undef,undef,undef,$start) = ReadingsVal($name,"${fmaj}_".$itemName."_PlannedOpTimeBegin",'0000-00-00 24') =~ m/(\d{4})-(\d{2})-(\d{2})\s(\d{2})/x; (undef,undef,undef,$start) = ReadingsVal($name,"${fmaj}_".$itemName."_PlannedOpTimeBegin",'0000-00-00 24') =~ m/(\d{4})-(\d{2})-(\d{2})\s(\d{2})/x;
(undef,undef,undef,$end) = ReadingsVal($name,"${fmaj}_".$itemName."_PlannedOpTimeEnd",'0000-00-00 24') =~ m/(\d{4})-(\d{2})-(\d{2})\s(\d{2})/x; (undef,undef,undef,$end) = ReadingsVal($name,"${fmaj}_".$itemName."_PlannedOpTimeEnd",'0000-00-00 24') =~ m/(\d{4})-(\d{2})-(\d{2})\s(\d{2})/x;
} }
@@ -3425,19 +3520,21 @@ sub PortalAsHtml {
if ($start < $t{0}) { # consumption seems to be tomorrow if ($start < $t{0}) { # consumption seems to be tomorrow
$start = 24-$t{0}+$start; $start = 24-$t{0}+$start;
$flag = 1; $flag = 1;
} else { }
else {
$start -= $t{0}; $start -= $t{0};
} }
if ($flag) { # consumption seems to be tomorrow if ($flag) { # consumption seems to be tomorrow
$end = 24-$t{0}+$end; $end = 24-$t{0}+$end;
} else { }
else {
$end -= $t{0}; $end -= $t{0};
} }
$_ .= ":".$start.":".$end; $_ .= ":".$start.":".$end;
}
} else { else {
$_ .= ":24:24"; $_ .= ":24:24";
} }
Log3($name, 4, "$name - Consumer planned data: $_"); Log3($name, 4, "$name - Consumer planned data: $_");
@@ -3464,7 +3561,8 @@ sub PortalAsHtml {
if(AttrVal("global","language","EN") eq "DE") { if(AttrVal("global","language","EN") eq "DE") {
(undef,undef,undef,$t{$i}) = ReadingsVal($name,"${fmaj}_NextHour".sprintf("%02d",$i)."_Time",'0') =~ m/(\d{2}).(\d{2}).(\d{4})\s(\d{2})/x; (undef,undef,undef,$t{$i}) = ReadingsVal($name,"${fmaj}_NextHour".sprintf("%02d",$i)."_Time",'0') =~ m/(\d{2}).(\d{2}).(\d{4})\s(\d{2})/x;
} else { }
else {
(undef,undef,undef,$t{$i}) = ReadingsVal($name,"${fmaj}_NextHour".sprintf("%02d",$i)."_Time",'0') =~ m/(\d{4})-(\d{2})-(\d{2})\s(\d{2})/x; (undef,undef,undef,$t{$i}) = ReadingsVal($name,"${fmaj}_NextHour".sprintf("%02d",$i)."_Time",'0') =~ m/(\d{4})-(\d{2})-(\d{2})\s(\d{2})/x;
} }
@@ -3511,7 +3609,8 @@ sub PortalAsHtml {
$val ='<b>???<b/>' if ($val eq $icon_name); # passendes Icon beim User nicht vorhanden ! ( attr web iconPath falsch/prüfen/update ? ) $val ='<b>???<b/>' if ($val eq $icon_name); # passendes Icon beim User nicht vorhanden ! ( attr web iconPath falsch/prüfen/update ? )
$ret .= "<td class='smaportal' width='$width' style='margin:1px; vertical-align:middle align:center; padding-bottom:1px;'>$val</td>"; $ret .= "<td class='smaportal' width='$width' style='margin:1px; vertical-align:middle align:center; padding-bottom:1px;'>$val</td>";
} else { # Kein Ertrag oder show_night = 0 }
else { # Kein Ertrag oder show_night = 0
$ret .= "<td></td>"; $we{$i} = undef; $ret .= "<td></td>"; $we{$i} = undef;
} }
# mit $we{$i} = undef kann man unten leicht feststellen ob für diese Spalte bereits ein Icon ausgegeben wurde oder nicht # mit $we{$i} = undef kann man unten leicht feststellen ob für diese Spalte bereits ein Icon ausgegeben wurde oder nicht
@@ -3545,12 +3644,12 @@ sub PortalAsHtml {
if ($type eq 'co') { if ($type eq 'co') {
$he = int(($maxCon-$co{$i})/$maxCon*$height) + $fsize; # he - freier der Raum über den Balken. $he = int(($maxCon-$co{$i})/$maxCon*$height) + $fsize; # he - freier der Raum über den Balken.
$z3 = int($height + $fsize - $he); # Resthöhe $z3 = int($height + $fsize - $he); # Resthöhe
}
} elsif ($type eq 'pv') { elsif ($type eq 'pv') {
$he = int(($maxVal-$pv{$i})/$maxVal*$height) + $fsize; $he = int(($maxVal-$pv{$i})/$maxVal*$height) + $fsize;
$z3 = int($height + $fsize - $he); $z3 = int($height + $fsize - $he);
}
} elsif ($type eq 'pvco') { elsif ($type eq 'pvco') {
# Berechnung der Zonen # Berechnung der Zonen
# he - freier der Raum über den Balken. fsize wird nicht verwendet, da bei diesem Typ keine Zahlen über den Balken stehen # he - freier der Raum über den Balken. fsize wird nicht verwendet, da bei diesem Typ keine Zahlen über den Balken stehen
# z2 - der Ertrag ggf mit Icon # z2 - der Ertrag ggf mit Icon
@@ -3561,7 +3660,8 @@ sub PortalAsHtml {
if ($pv{$i} > $co{$i}) { # pv oben , co unten if ($pv{$i} > $co{$i}) { # pv oben , co unten
$z2 = $pv{$i}; $z3 = $co{$i}; $z2 = $pv{$i}; $z3 = $co{$i};
} else { # tauschen, Verbrauch ist größer als Ertrag }
else { # tauschen, Verbrauch ist größer als Ertrag
$z3 = $pv{$i}; $z2 = $co{$i}; $z3 = $pv{$i}; $z2 = $co{$i};
} }
@@ -3572,9 +3672,9 @@ sub PortalAsHtml {
if ($z3 < int($fsize/2)) { # dünnen Strichbalken vermeiden / ca. halbe Zeichenhöhe if ($z3 < int($fsize/2)) { # dünnen Strichbalken vermeiden / ca. halbe Zeichenhöhe
$z2 += $z3; $z3 = 0; $z2 += $z3; $z3 = 0;
} }
}
} else { # Typ dif else { # Typ dif
# Berechnung der Zonen # Berechnung der Zonen
# he - freier der Raum über den Balken , Zahl positiver Wert + fsize # he - freier der Raum über den Balken , Zahl positiver Wert + fsize
# z2 - positiver Balken inkl Icon # z2 - positiver Balken inkl Icon
@@ -3587,16 +3687,18 @@ sub PortalAsHtml {
if ($maxPV) { # Feste Aufteilung +/- , jeder 50 % bei maxPV = 0 if ($maxPV) { # Feste Aufteilung +/- , jeder 50 % bei maxPV = 0
$px_pos = int($height/2); $px_pos = int($height/2);
$px_neg = $height - $px_pos; # Rundungsfehler vermeiden $px_neg = $height - $px_pos; # Rundungsfehler vermeiden
}
} else { # Dynamische hoch/runter Verschiebung der Null-Linie else { # Dynamische hoch/runter Verschiebung der Null-Linie
if ($minDif >= 0 ) { # keine negativen Balken vorhanden, die Positiven bekommen den gesammten Raum if ($minDif >= 0 ) { # keine negativen Balken vorhanden, die Positiven bekommen den gesammten Raum
$px_neg = 0; $px_neg = 0;
$px_pos = $height; $px_pos = $height;
} else { }
else {
if ($maxDif > 0) { if ($maxDif > 0) {
$px_neg = int($height * abs($minDif) / ($maxDif + abs($minDif))); # Wieviel % entfallen auf unten ? $px_neg = int($height * abs($minDif) / ($maxDif + abs($minDif))); # Wieviel % entfallen auf unten ?
$px_pos = $height-$px_neg; # der Rest ist oben $px_pos = $height-$px_neg; # der Rest ist oben
} else { # keine positiven Balken vorhanden, die Negativen bekommen den gesammten Raum }
else { # keine positiven Balken vorhanden, die Negativen bekommen den gesammten Raum
$px_neg = $height; $px_neg = $height;
$px_pos = 0; $px_pos = 0;
} }
@@ -3606,7 +3708,8 @@ sub PortalAsHtml {
if ($di{$i} >= 0) { # Zone 2 & 3 mit ihren direkten Werten vorbesetzen if ($di{$i} >= 0) { # Zone 2 & 3 mit ihren direkten Werten vorbesetzen
$z2 = $di{$i}; $z2 = $di{$i};
$z3 = abs($minDif); $z3 = abs($minDif);
} else { }
else {
$z2 = $maxDif; $z2 = $maxDif;
$z3 = abs($di{$i}); # Nur Betrag ohne Vorzeichen $z3 = abs($di{$i}); # Nur Betrag ohne Vorzeichen
} }
@@ -3654,9 +3757,9 @@ sub PortalAsHtml {
$ret .= consinject($hash,$i,@pgCDev) if($ret); $ret .= consinject($hash,$i,@pgCDev) if($ret);
$ret .= "</td></tr>"; $ret .= "</td></tr>";
} }
}
} elsif ($type eq 'pvco') { elsif ($type eq 'pvco') {
my ($color1, $color2, $style1, $style2); my ($color1, $color2, $style1, $style2);
$ret .="<table width='100%' height='100%'>\n"; # mit width=100% etwas bessere Füllung der Balken $ret .="<table width='100%' height='100%'>\n"; # mit width=100% etwas bessere Füllung der Balken
@@ -3675,8 +3778,7 @@ sub PortalAsHtml {
$color2 = $colorc; $color2 = $colorc;
$style2 = "style=\"padding-bottom:0px; padding-top:1px; vertical-align:top; margin-left:auto; margin-right:auto;"; $style2 = "style=\"padding-bottom:0px; padding-top:1px; vertical-align:top; margin-left:auto; margin-right:auto;";
$style2 .= (defined($color2)) ? " background-color:#$color2\"" : '"'; $style2 .= (defined($color2)) ? " background-color:#$color2\"" : '"';
} }
} else { } else {
$val = formatVal6($co{$i},$kw,$we{$i}); $val = formatVal6($co{$i},$kw,$we{$i});
$color1 = $colorc; $color1 = $colorc;
@@ -3705,8 +3807,8 @@ sub PortalAsHtml {
$ret .= "<tr class='odd' style='height:".$z3."px'>"; $ret .= "<tr class='odd' style='height:".$z3."px'>";
$ret .= "<td align='center' class='smaportal' ".$style2.">$v</td></tr>"; $ret .= "<td align='center' class='smaportal' ".$style2.">$v</td></tr>";
} }
}
} else { # Type dif else { # Type dif
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 .="<table width='100%' border='0'>\n"; # Tipp : das nachfolgende border=0 auf 1 setzen hilft sehr Ausgabefehler zu endecken $ret .="<table width='100%' border='0'>\n"; # Tipp : das nachfolgende border=0 auf 1 setzen hilft sehr Ausgabefehler zu endecken
@@ -3726,8 +3828,8 @@ sub PortalAsHtml {
$ret .= "<td align='center' class='smaportal' ".$style.">"; $ret .= "<td align='center' class='smaportal' ".$style.">";
$ret .= $is{$i} if (defined $is{$i}); $ret .= $is{$i} if (defined $is{$i});
$ret .="</td></tr>"; $ret .="</td></tr>";
}
} else { # ohne Farbe else { # ohne Farbe
$z2 = 2 if ($di{$i} == 0); # Sonderfall, hier wird die 0 gebraucht ! $z2 = 2 if ($di{$i} == 0); # Sonderfall, hier wird die 0 gebraucht !
if ($z2 && $val) { # z2 weglassen wenn nicht unbedigt nötig bzw. wenn zuvor he mit val keinen Wert hatte if ($z2 && $val) { # z2 weglassen wenn nicht unbedigt nötig bzw. wenn zuvor he mit val keinen Wert hatte
$ret .= "<tr class='even' style='height:".$z2."px'>"; $ret .= "<tr class='even' style='height:".$z2."px'>";
@@ -3739,8 +3841,8 @@ sub PortalAsHtml {
$style .= (defined($colorc)) ? " background-color:#$colorc\"" : '"'; # mit Farbe 2 colorc füllen $style .= (defined($colorc)) ? " background-color:#$colorc\"" : '"'; # mit Farbe 2 colorc füllen
$ret .= "<tr class='odd' style='height:".$z3."px'>"; $ret .= "<tr class='odd' style='height:".$z3."px'>";
$ret .= "<td align='center' class='smaportal' ".$style."></td></tr>"; $ret .= "<td align='center' class='smaportal' ".$style."></td></tr>";
}
} elsif ($z3) { # ohne Farbe elsif ($z3) { # ohne Farbe
$ret .="<tr class='even' style='height:".$z3."px'>"; $ret .="<tr class='even' style='height:".$z3."px'>";
$ret .="<td class='smaportal'></td></tr>"; $ret .="<td class='smaportal'></td></tr>";
} }
@@ -3833,15 +3935,19 @@ sub formatVal6 {
if (!$t) { # glatte Zahl ohne Nachkommastelle if (!$t) { # glatte Zahl ohne Nachkommastelle
if(!$v) { if(!$v) {
return '&nbsp;'; # 0 nicht anzeigen, passt eigentlich immer bis auf einen Fall im Typ diff return '&nbsp;'; # 0 nicht anzeigen, passt eigentlich immer bis auf einen Fall im Typ diff
} elsif ($v < 10) { }
elsif ($v < 10) {
return '&nbsp;&nbsp;'.$n.$v.'&nbsp;&nbsp;'; return '&nbsp;&nbsp;'.$n.$v.'&nbsp;&nbsp;';
} else { }
else {
return '&nbsp;&nbsp;'.$n.$v.'&nbsp;'; return '&nbsp;&nbsp;'.$n.$v.'&nbsp;';
} }
} else { # mit Nachkommastelle -> zwei Zeichen mehr .X }
else { # mit Nachkommastelle -> zwei Zeichen mehr .X
if ($v < 10) { if ($v < 10) {
return '&nbsp;'.$n.$v.'&nbsp;'; return '&nbsp;'.$n.$v.'&nbsp;';
} else { }
else {
return $n.$v.'&nbsp;'; return $n.$v.'&nbsp;';
} }
} }
@@ -3931,7 +4037,8 @@ sub SPGRefresh {
if (ref $hash ne "HASH") { if (ref $hash ne "HASH") {
($name,$pload,$lpollspg) = split ",",$hash; ($name,$pload,$lpollspg) = split ",",$hash;
$hash = $defs{$name}; $hash = $defs{$name};
} else { }
else {
$name = $hash->{NAME}; $name = $hash->{NAME};
} }
my $fpr = 0; my $fpr = 0;
@@ -3951,11 +4058,13 @@ sub SPGRefresh {
my $room = $_; my $room = $_;
{ map { FW_directNotify("FILTER=room=$room", "#FHEMWEB:$_", "location.reload('true')", "") } devspec2array("TYPE=FHEMWEB") } ## no critic 'void context'; { map { FW_directNotify("FILTER=room=$room", "#FHEMWEB:$_", "location.reload('true')", "") } devspec2array("TYPE=FHEMWEB") } ## no critic 'void context';
} }
} elsif ($pload && (!$hash->{HELPER}{SPGROOM} || $hash->{HELPER}{SPGDETAIL})) { }
elsif ($pload && (!$hash->{HELPER}{SPGROOM} || $hash->{HELPER}{SPGDETAIL})) {
# trifft zu bei Detailansicht oder im FLOORPLAN bzw. Dashboard oder wenn Seitenrefresh mit dem # trifft zu bei Detailansicht oder im FLOORPLAN bzw. Dashboard oder wenn Seitenrefresh mit dem
# SMAPortalSPG-Attribut "forcePageRefresh" erzwungen wird # SMAPortalSPG-Attribut "forcePageRefresh" erzwungen wird
{ map { FW_directNotify("#FHEMWEB:$_", "location.reload('true')", "") } devspec2array("TYPE=FHEMWEB") } ## no critic 'void context'; { map { FW_directNotify("#FHEMWEB:$_", "location.reload('true')", "") } devspec2array("TYPE=FHEMWEB") } ## no critic 'void context';
} else { }
else {
if($fpr) { if($fpr) {
{ map { FW_directNotify("#FHEMWEB:$_", "location.reload('true')", "") } devspec2array("TYPE=FHEMWEB") } ## no critic 'void context'; { map { FW_directNotify("#FHEMWEB:$_", "location.reload('true')", "") } devspec2array("TYPE=FHEMWEB") } ## no critic 'void context';
} }
@@ -4287,6 +4396,13 @@ return;
attr &lt;name&gt; userAgent Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:65.0) Gecko/20100101 Firefox/65.0 <br> attr &lt;name&gt; userAgent Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:65.0) Gecko/20100101 Firefox/65.0 <br>
</ul> </ul>
</li><br> </li><br>
<a name="useRelativeNames"></a>
<li><b>useRelativeNames </b><br>
When using relative dates <b>current-x</b> (see balance.* attributes) the created reading name contains
also the relative instead of the real date. <br>
(default: real date)
</li><br>
<a name="verbose5Data"></a> <a name="verbose5Data"></a>
<li><b>verbose5Data </b><br> <li><b>verbose5Data </b><br>
@@ -4613,6 +4729,13 @@ return;
</ul> </ul>
</li><br> </li><br>
<a name="useRelativeNames"></a>
<li><b>useRelativeNames </b><br>
Bei Verwendung von relativen Datumangaben <b>current-x</b> (siehe balance.*-Attribute) enthält der erstellte Readingname
ebenfalls die relative anstatt der realen Datumangabe. <br>
(default: reale Datumangabe)
</li><br>
<a name="verbose5Data"></a> <a name="verbose5Data"></a>
<li><b>verbose5Data </b><br> <li><b>verbose5Data </b><br>