mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-05-07 22:29:19 +00:00
76_SMAPortal: contrib 3.7.0
git-svn-id: https://svn.fhem.de/fhem/trunk@23170 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
bddec907e1
commit
fe39cf5130
@ -49,11 +49,10 @@ use LWP::UserAgent;
|
|||||||
use HTTP::Cookies;
|
use HTTP::Cookies;
|
||||||
use JSON qw(decode_json);
|
use JSON qw(decode_json);
|
||||||
use MIME::Base64;
|
use MIME::Base64;
|
||||||
|
use Color;
|
||||||
use Encode;
|
use Encode;
|
||||||
use utf8;
|
use utf8;
|
||||||
|
|
||||||
use HttpUtils;
|
|
||||||
|
|
||||||
# Run before module compilation
|
# Run before module compilation
|
||||||
BEGIN {
|
BEGIN {
|
||||||
# Import from main::
|
# Import from main::
|
||||||
@ -96,7 +95,6 @@ BEGIN {
|
|||||||
Log3
|
Log3
|
||||||
makeReadingName
|
makeReadingName
|
||||||
modules
|
modules
|
||||||
parseParams
|
|
||||||
readingsSingleUpdate
|
readingsSingleUpdate
|
||||||
readingsBulkUpdate
|
readingsBulkUpdate
|
||||||
readingsBulkUpdateIfChanged
|
readingsBulkUpdateIfChanged
|
||||||
@ -120,7 +118,6 @@ BEGIN {
|
|||||||
FW_room
|
FW_room
|
||||||
FW_detail
|
FW_detail
|
||||||
FW_wname
|
FW_wname
|
||||||
urlEncode
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -141,7 +138,8 @@ BEGIN {
|
|||||||
|
|
||||||
# Versions History intern
|
# Versions History intern
|
||||||
my %vNotesIntern = (
|
my %vNotesIntern = (
|
||||||
"9.9.9" => "12.11.2020 Studieb Kamik ",
|
"3.7.0" => "16.11.2020 add new consumer management for switched sockets and EVCharger ",
|
||||||
|
"3.6.5" => "12.11.2020 verbose5data switchConsumer, more preselected user agents ",
|
||||||
"3.6.4" => "11.11.2020 preselect the user agent randomly, set min. interval to 180 s ",
|
"3.6.4" => "11.11.2020 preselect the user agent randomly, set min. interval to 180 s ",
|
||||||
"3.6.3" => "05.11.2020 fix only four consumer are shown in set command drop down list ",
|
"3.6.3" => "05.11.2020 fix only four consumer are shown in set command drop down list ",
|
||||||
"3.6.2" => "03.11.2020 new function _detailViewOn to Switch the detail view on SMA energy balance site, new default userAgent ",
|
"3.6.2" => "03.11.2020 new function _detailViewOn to Switch the detail view on SMA energy balance site, new default userAgent ",
|
||||||
@ -269,7 +267,7 @@ my %stpl = (
|
|||||||
balanceTotalData => { doit => 0, nohm => 0, level => 'L14', func => '_getBalanceTotalData' },
|
balanceTotalData => { doit => 0, nohm => 0, level => 'L14', func => '_getBalanceTotalData' },
|
||||||
);
|
);
|
||||||
|
|
||||||
my %hua = ( # mögliche UserAgents für eine Round-Robin-Liste
|
my %hua = ( # mögliche Random UserAgents
|
||||||
1 => "Mozilla/5.0 (Windows NT 10.0; rv:81.0) Gecko/20100101 Firefox/81.0",
|
1 => "Mozilla/5.0 (Windows NT 10.0; rv:81.0) Gecko/20100101 Firefox/81.0",
|
||||||
2 => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.195 Safari/537.36",
|
2 => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.195 Safari/537.36",
|
||||||
3 => "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36",
|
3 => "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36",
|
||||||
@ -279,10 +277,21 @@ my %hua = (
|
|||||||
7 => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:86.0) Gecko/20100101 Firefox/86.0",
|
7 => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:86.0) Gecko/20100101 Firefox/86.0",
|
||||||
8 => "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:86.0) Gecko/20100101 Firefox/86.0",
|
8 => "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:86.0) Gecko/20100101 Firefox/86.0",
|
||||||
9 => "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0",
|
9 => "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0",
|
||||||
10 => "Mozilla/5.0 (Linux; Android 8.0.0; PRA-LX1 Build/HUAWEIPRA-LX1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/86.0.4240.198 Mobile Safari/537.36"
|
10 => "Mozilla/5.0 (Linux; Android 8.0.0; PRA-LX1 Build/HUAWEIPRA-LX1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/86.0.4240.198 Mobile Safari/537.36",
|
||||||
|
11 => "Mozilla/5.0 (Linux; Android 8.0.0; BAH2-L09 Build/HUAWEIBAH2-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/86.0.4240.185 Safari/537.36",
|
||||||
);
|
);
|
||||||
|
|
||||||
# Tags der verfügbaren Datenquellen
|
my %hal = ( # Header Accept-Language sprachenabhängig
|
||||||
|
"DE" => "de,en-US;q=0.7,en;q=0.3",
|
||||||
|
"EN" => "en-US;q=0.7,en;q=0.3"
|
||||||
|
);
|
||||||
|
|
||||||
|
my %hsusyid = ( # Schalten/Management der Verbraucher entspr. ihrer SUSyID
|
||||||
|
191 => { arg => ":on,off,auto", fn => \&_switchConsumer }, # 191 = Schaltdosen
|
||||||
|
315 => { arg => ":colorpicker,CT,0,1,100", fn => \&_manageConsumerByEnergy }, # 315 = SMA EV Charger
|
||||||
|
);
|
||||||
|
|
||||||
|
# Tags der verfügbaren Datenquellen
|
||||||
my @pd = qw( plantMasterData
|
my @pd = qw( plantMasterData
|
||||||
consumerMasterdata
|
consumerMasterdata
|
||||||
balanceDayData
|
balanceDayData
|
||||||
@ -333,7 +342,7 @@ sub Initialize {
|
|||||||
"showPassInLog:1,0 ".
|
"showPassInLog:1,0 ".
|
||||||
"userAgent ".
|
"userAgent ".
|
||||||
"useRelativeNames:1,0 ".
|
"useRelativeNames:1,0 ".
|
||||||
"verbose5Data:multiple-strict,none,loginData,detailViewSwitch,".$v5d." ".
|
"verbose5Data:multiple-strict,none,loginData,detailViewSwitch,switchConsumer,".$v5d." ".
|
||||||
$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)
|
||||||
@ -399,7 +408,7 @@ sub Set {
|
|||||||
my $arg = join " ", map { my $p = $_; $p =~ s/\s//xg; $p; } @a; ## no critic 'Map blocks'
|
my $arg = join " ", map { my $p = $_; $p =~ s/\s//xg; $p; } @a; ## no critic 'Map blocks'
|
||||||
my $prop = shift @a;
|
my $prop = shift @a;
|
||||||
my $prop1 = shift @a;
|
my $prop1 = shift @a;
|
||||||
my ($setlist,@ads);
|
my ($setlist,@ads,$susyid);
|
||||||
my $ad = "";
|
my $ad = "";
|
||||||
|
|
||||||
return if(IsDisabled($name));
|
return if(IsDisabled($name));
|
||||||
@ -417,12 +426,14 @@ sub Set {
|
|||||||
"createPortalGraphic:Generation,Consumption,Generation_Consumption,Differential ".
|
"createPortalGraphic:Generation,Consumption,Generation_Consumption,Differential ".
|
||||||
"getData:noArg "
|
"getData:noArg "
|
||||||
;
|
;
|
||||||
|
|
||||||
if($hash->{HELPER}{PLANTOID} && $hash->{HELPER}{CONSUMER}) {
|
if($hash->{HELPER}{PLANTOID} && $hash->{HELPER}{CONSUMER}) {
|
||||||
for my $key (keys %{$hash->{HELPER}{CONSUMER}}) {
|
for my $key (keys %{$hash->{HELPER}{CONSUMER}}) {
|
||||||
my $dev = $hash->{HELPER}{CONSUMER}{$key}{DeviceName};
|
my $dev = $hash->{HELPER}{CONSUMER}{$key}{DeviceName};
|
||||||
if($dev) {
|
if($dev) {
|
||||||
|
$susyid = $hash->{HELPER}{CONSUMER}{$key}{SUSyID};
|
||||||
push @ads, $dev;
|
push @ads, $dev;
|
||||||
$setlist .= "$dev ";
|
$setlist .= $dev.$hsusyid{$susyid}{arg}." ";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -432,18 +443,18 @@ sub Set {
|
|||||||
$ad = join "|", @ads;
|
$ad = join "|", @ads;
|
||||||
}
|
}
|
||||||
|
|
||||||
my ($a,$h) = parseParams ($arg);
|
|
||||||
my $gcval = $h->{gcval} // 24; # GridConsumptionValue
|
|
||||||
my $pvval = $h->{pvval} // 76; # PvValue
|
|
||||||
my $lval = $h->{lval} // 0; # LimitedEnergyValue
|
|
||||||
|
|
||||||
$gcval = $gcval/100;
|
|
||||||
$pvval = $pvval/100;
|
|
||||||
|
|
||||||
if ($opt && $ad && $opt =~ /$ad/x) {
|
if ($opt && $ad && $opt =~ /$ad/x) {
|
||||||
# Verbraucher schalten
|
# Verbraucher schalten
|
||||||
|
#$susyid = 191; # Standard ist Schaltsteckdose
|
||||||
|
for my $k (keys %{$hash->{HELPER}{CONSUMER}}) {
|
||||||
|
my $dev = $hash->{HELPER}{CONSUMER}{$k}{DeviceName};
|
||||||
|
if($opt eq $dev) {
|
||||||
|
$susyid = $hash->{HELPER}{CONSUMER}{$k}{SUSyID};
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
$hash->{HELPER}{GETTER} = "none";
|
$hash->{HELPER}{GETTER} = "none";
|
||||||
$hash->{HELPER}{SETTER} = "$opt:$gcval#$pvval#$lval";
|
$hash->{HELPER}{SETTER} = "$opt:$susyid:$prop";
|
||||||
CallInfo($hash);
|
CallInfo($hash);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -913,7 +924,7 @@ return ($interval,$maxcycles,$timeoutdef);
|
|||||||
## schaltet auch Verbraucher des Sunny Home Managers
|
## schaltet auch Verbraucher des Sunny Home Managers
|
||||||
################################################################
|
################################################################
|
||||||
sub GetSetData { ## no critic 'complexity'
|
sub GetSetData { ## no critic 'complexity'
|
||||||
my ($string) = @_;
|
my $string = shift;
|
||||||
my ($name,$getp,$setp) = split("\\|",$string);
|
my ($name,$getp,$setp) = split("\\|",$string);
|
||||||
my $hash = $defs{$name};
|
my $hash = $defs{$name};
|
||||||
my $cookieLocation = AttrVal($name, "cookieLocation", "./log/".$name."_cookie.txt");
|
my $cookieLocation = AttrVal($name, "cookieLocation", "./log/".$name."_cookie.txt");
|
||||||
@ -923,6 +934,7 @@ sub GetSetData { ## no critic 'complexity'
|
|||||||
my $state = "ok";
|
my $state = "ok";
|
||||||
my ($st,$lc) = ("","");
|
my ($st,$lc) = ("","");
|
||||||
my @da = ();
|
my @da = ();
|
||||||
|
my $params;
|
||||||
|
|
||||||
my ($errstate,$reread,$retry,$exceed,$newcycle) = (0,0,0,0,0);
|
my ($errstate,$reread,$retry,$exceed,$newcycle) = (0,0,0,0,0);
|
||||||
|
|
||||||
@ -930,21 +942,11 @@ sub GetSetData { ## no critic 'complexity'
|
|||||||
my $randomua = $ak[rand @ak];
|
my $randomua = $ak[rand @ak];
|
||||||
my $defuseragent = $hua{$randomua};
|
my $defuseragent = $hua{$randomua};
|
||||||
my $useragent = AttrVal($name, "userAgent", $defuseragent);
|
my $useragent = AttrVal($name, "userAgent", $defuseragent);
|
||||||
|
|
||||||
BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "usedUserAgent:$useragent", "NULL" ], 1);
|
BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "usedUserAgent:$useragent", "NULL" ], 1);
|
||||||
|
|
||||||
my %hal = ( # Header Accept-Language sprachenabhängig
|
|
||||||
"DE" => "de,en-US;q=0.7,en;q=0.3",
|
|
||||||
"EN" => "en-US;q=0.7,en;q=0.3"
|
|
||||||
);
|
|
||||||
|
|
||||||
my ($d,$op);
|
|
||||||
if($setp ne "none") {
|
|
||||||
# Verbraucher soll in den Status $op geschaltet werden
|
|
||||||
($d,$op) = split(":",$setp);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log3 ($name, 5, "$name - Start operation with CookieLocation: $cookieLocation and UserAgent: $useragent");
|
Log3 ($name, 5, "$name - Start operation with CookieLocation: $cookieLocation and UserAgent: $useragent");
|
||||||
Log3 ($name, 5, "$name - data get: $getp, data set: ".(($d && $op)?($d." ".$op):$setp));
|
Log3 ($name, 5, "$name - data get: $getp, data set: $setp");
|
||||||
|
|
||||||
my $ua = LWP::UserAgent->new;
|
my $ua = LWP::UserAgent->new;
|
||||||
|
|
||||||
@ -999,73 +1001,28 @@ sub GetSetData { ## no critic 'complexity'
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
### Verbraucher schalten
|
### Verbraucher schalten / managen
|
||||||
#######################################
|
#######################################
|
||||||
if($setp ne "none") {
|
if($setp ne "none") {
|
||||||
my ($serial,$id,$oid,$h,$oname);
|
my ($d,$susyid,$op) = split(":",$setp); # $op -> Verbraucher Manage Operation
|
||||||
|
|
||||||
my ($gcval, $pvval, $lval) = split "#",$op;
|
$params = {
|
||||||
|
name => $name,
|
||||||
|
ua => $ua,
|
||||||
|
state => $state,
|
||||||
|
daref => \@da,
|
||||||
|
d => $d,
|
||||||
|
susyid => $susyid,
|
||||||
|
op => $op
|
||||||
|
};
|
||||||
|
|
||||||
for my $key (keys %{$hash->{HELPER}{CONSUMER}}) {
|
if($hsusyid{$susyid} && defined &{$hsusyid{$susyid}{fn}}) {
|
||||||
$h = $hash->{HELPER}{CONSUMER}{$key}{DeviceName};
|
($errstate,$state) = &{$hsusyid{$susyid}{fn}} ($params);
|
||||||
if($h && $h eq $d) {
|
|
||||||
$serial = $hash->{HELPER}{CONSUMER}{$key}{SerialNumber};
|
|
||||||
$id = $hash->{HELPER}{CONSUMER}{$key}{SUSyID};
|
|
||||||
$oid = $hash->{HELPER}{CONSUMER}{$key}{ConsumerOid};
|
|
||||||
$oname = decode("utf8", $hash->{HELPER}{CONSUMER}{$key}{DeviceOrigName});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
my $plantOid = $hash->{HELPER}{PLANTOID};
|
|
||||||
|
|
||||||
if($verbose == 5) {
|
|
||||||
$ua->add_handler( request_send => sub { shift->dump; return } ); # for debugging
|
|
||||||
$ua->add_handler( response_done => sub { shift->dump; return } );
|
|
||||||
}
|
|
||||||
|
|
||||||
my %fields = ( "Content-Type" => "application/x-www-form-urlencoded",
|
|
||||||
"Accept" => "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
|
||||||
|
|
||||||
);
|
|
||||||
|
|
||||||
my $rgb = "rgba(49,101,255,1)";
|
|
||||||
my $content = [
|
|
||||||
'UsePriceLimit' => "False",
|
|
||||||
'UsesCanFrames' => "True",
|
|
||||||
'ConsumerOid' => "$oid",
|
|
||||||
'DeviceStatus' => "DeviceActive",
|
|
||||||
'DataAcceptance' => ["true", "false"],
|
|
||||||
'PowerConsumerName' => "$oname",
|
|
||||||
'Priority' => "1",
|
|
||||||
'RbTimeframeTypeEnergyPv_0' => "pv",
|
|
||||||
'MaxPriceAllowedValue' => 0,
|
|
||||||
'GridConsumptionValue' => $gcval,
|
|
||||||
'PvValue' => $pvval,
|
|
||||||
'LimitedEnergyValue' => $lval,
|
|
||||||
'ConsumerIcon' => "/Images/DeviceIcons/ChargingStation.png",
|
|
||||||
'ConsumerColor.ColorString' => $rgb,
|
|
||||||
];
|
|
||||||
|
|
||||||
my $res = $ua->post("https://www.sunnyportal.com/HoMan/Consumer/Semp/$oid",
|
|
||||||
%fields,
|
|
||||||
Content => $content
|
|
||||||
);
|
|
||||||
|
|
||||||
if($verbose == 5) {
|
|
||||||
Log3 ($name, 5, "$name - Return Code: ".$res->code);
|
|
||||||
}
|
|
||||||
|
|
||||||
$ua->remove_handler('request_send');
|
|
||||||
$ua->remove_handler('response_done');
|
|
||||||
|
|
||||||
$res = $res->decoded_content();
|
|
||||||
Log3 ($name, 3, "$name - Set \"$d $op\" result: ".$res);
|
|
||||||
if($res eq "true") {
|
|
||||||
$state = "ok - switched consumer $d to $op";
|
|
||||||
BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "NULL", "GETTER:all" ], 1);
|
|
||||||
BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "NULL", "SETTER:none"], 1);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$state = "Error - couldn't switch consumer $d to $op";
|
$errstate = 1;
|
||||||
|
$state = qq{ERROR - Switch or energy management function for SMA device with SUSyID '$susyid' doesn't exist. Inform Maintainer.};
|
||||||
|
Log3 ($name, 1, "$name - $state");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1110,21 +1067,21 @@ sub GetSetData { ## no critic 'complexity'
|
|||||||
$hash->{HELPER}{RETRIES}++;
|
$hash->{HELPER}{RETRIES}++;
|
||||||
my $cd = AttrVal($name, "cookieDelete", "auto");
|
my $cd = AttrVal($name, "cookieDelete", "auto");
|
||||||
|
|
||||||
if($retc == $thold || $cd =~ /Attempt/x) { # Schwellenwert Leseversuche erreicht -> Cookie File löschen
|
if($retc == $thold || $cd =~ /Attempt/x) { # Schwellenwert Leseversuche erreicht -> Cookie File löschen
|
||||||
my $msg = qq{$name - Threshold reached, delete cookie file before retry...};
|
my $msg = qq{$name - Threshold reached, delete cookie file before retry...};
|
||||||
|
|
||||||
if($cd =~ /Attempt/x) {
|
if($cd =~ /Attempt/x) {
|
||||||
$msg = qq{$name - force delete cookie file before retry...};
|
$msg = qq{$name - force delete cookie file before retry...};
|
||||||
}
|
}
|
||||||
|
|
||||||
Log3 ($name, 3, $msg);
|
Log3 ($name, 3, $msg);
|
||||||
sleep $sleepretry; # Threshold exceed -> Retry mit Cookie löschen
|
sleep $sleepretry; # Threshold exceed -> Retry mit Cookie löschen
|
||||||
$exceed = 1;
|
$exceed = 1;
|
||||||
BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "NULL", "RETRIES:".$hash->{HELPER}{RETRIES} ], 1);
|
BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "NULL", "RETRIES:".$hash->{HELPER}{RETRIES} ], 1);
|
||||||
return "$name|$exceed|$newcycle|$errstate|$getp|$setp";
|
return "$name|$exceed|$newcycle|$errstate|$getp|$setp";
|
||||||
}
|
}
|
||||||
|
|
||||||
sleep $sleepretry;
|
sleep $sleepretry;
|
||||||
goto &GetSetData;
|
goto &GetSetData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1147,7 +1104,7 @@ sub GetSetData { ## no critic 'complexity'
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Daten müssen als Einzeiler zurückgegeben werden
|
# Daten müssen als Einzeiler zurückgegeben werden
|
||||||
$st = encode_base64 ($state, "");
|
$st = encode_base64 ($state, "");
|
||||||
if(@da) {
|
if(@da) {
|
||||||
$lc = join "###", @da;
|
$lc = join "###", @da;
|
||||||
$lc = encode_base64 ($lc, "");
|
$lc = encode_base64 ($lc, "");
|
||||||
@ -1171,7 +1128,7 @@ sub _doLogin {
|
|||||||
my $v5d = AttrVal($name, "verbose5Data", "none");
|
my $v5d = AttrVal($name, "verbose5Data", "none");
|
||||||
my $verbose = AttrVal($name, "verbose", 3);
|
my $verbose = AttrVal($name, "verbose", 3);
|
||||||
|
|
||||||
if($verbose == 5 && $v5d =~ /loginData/) {
|
if($verbose == 5 && $v5d =~ /loginData/x) {
|
||||||
$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 } );
|
||||||
}
|
}
|
||||||
@ -1184,7 +1141,7 @@ sub _doLogin {
|
|||||||
my $cookie = $loginp->header('Set-Cookie') // "";
|
my $cookie = $loginp->header('Set-Cookie') // "";
|
||||||
|
|
||||||
if ($loginp->is_success) {
|
if ($loginp->is_success) {
|
||||||
if($v5d =~ /loginData/) {
|
if($v5d =~ /loginData/x) {
|
||||||
Log3 ($name, 5, "$name - Status Login Page: ".$loginp->status_line);
|
Log3 ($name, 5, "$name - Status Login Page: ".$loginp->status_line);
|
||||||
Log3 ($name, 5, "$name - Header Location: ". $location);
|
Log3 ($name, 5, "$name - Header Location: ". $location);
|
||||||
Log3 ($name, 5, "$name - Header Set-Cookie: ".$cookie);
|
Log3 ($name, 5, "$name - Header Set-Cookie: ".$cookie);
|
||||||
@ -1280,6 +1237,132 @@ sub __isLoggedIn {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# Consumer schalten
|
||||||
|
################################################################
|
||||||
|
sub _switchConsumer {
|
||||||
|
my $paref = shift;
|
||||||
|
my $name = $paref->{name};
|
||||||
|
my $ua = $paref->{ua}; # LWP Useragent
|
||||||
|
my $state = $paref->{state};
|
||||||
|
my $daref = $paref->{daref}; # Referenz zum Datenarray
|
||||||
|
my $d = $paref->{d};
|
||||||
|
my $susyid = $paref->{susyid};
|
||||||
|
my $op = $paref->{op};
|
||||||
|
|
||||||
|
my $hash = $defs{$name};
|
||||||
|
|
||||||
|
my ($serial,$id);
|
||||||
|
for my $key (keys %{$hash->{HELPER}{CONSUMER}}) {
|
||||||
|
my $h = $hash->{HELPER}{CONSUMER}{$key}{DeviceName};
|
||||||
|
if($h && $h eq $d) {
|
||||||
|
$serial = $hash->{HELPER}{CONSUMER}{$key}{SerialNumber};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my $plantOid = $hash->{HELPER}{PLANTOID};
|
||||||
|
my $errstate = 0;
|
||||||
|
my %fields = ("Content-Type" => "application/x-www-form-urlencoded; charset=UTF-8");
|
||||||
|
my $tag = "switchConsumer";
|
||||||
|
|
||||||
|
my $cont = {
|
||||||
|
'mode' => $op,
|
||||||
|
'serialNumber' => $serial,
|
||||||
|
'SUSyID' => $susyid,
|
||||||
|
'plantOid' => $plantOid
|
||||||
|
};
|
||||||
|
|
||||||
|
($errstate,$state) = __dispatchPost ({ name => $name,
|
||||||
|
ua => $ua,
|
||||||
|
call => 'https://www.sunnyportal.com/Homan/ConsumerBalance/SetOperatingMode',
|
||||||
|
tag => $tag,
|
||||||
|
state => $state,
|
||||||
|
fnaref => [ qw( extractSwitchConsumerData ) ],
|
||||||
|
fields => \%fields,
|
||||||
|
content => $cont,
|
||||||
|
addon => "$d:$susyid:$op", # optionales Addon für aufzurufende Funktion
|
||||||
|
daref => $daref
|
||||||
|
});
|
||||||
|
|
||||||
|
return ($errstate,$state);
|
||||||
|
}
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# Consumer Management abhängig von erzeugter PV-Energie
|
||||||
|
# (z.B. EV Charger)
|
||||||
|
# $op: enthält den Anteil der erzeugten PV der vorhanden
|
||||||
|
# muß bevor der Verbraucher eingeschaltet werden sollen
|
||||||
|
# (der Anteil bezogener Energie ergibt sich aus
|
||||||
|
# 100% - PV)
|
||||||
|
################################################################
|
||||||
|
sub _manageConsumerByEnergy {
|
||||||
|
my $paref = shift;
|
||||||
|
my $name = $paref->{name};
|
||||||
|
my $ua = $paref->{ua}; # LWP Useragent
|
||||||
|
my $state = $paref->{state};
|
||||||
|
my $daref = $paref->{daref}; # Referenz zum Datenarray
|
||||||
|
my $d = $paref->{d};
|
||||||
|
my $susyid = $paref->{susyid};
|
||||||
|
my $op = $paref->{op};
|
||||||
|
|
||||||
|
my $hash = $defs{$name};
|
||||||
|
|
||||||
|
my $pvval = $op/100;
|
||||||
|
my $gcval = 1-$pvval;
|
||||||
|
|
||||||
|
my ($serial,$oid,$oname);
|
||||||
|
|
||||||
|
for my $key (keys %{$hash->{HELPER}{CONSUMER}}) {
|
||||||
|
my $h = $hash->{HELPER}{CONSUMER}{$key}{DeviceName};
|
||||||
|
if($h && $h eq $d) {
|
||||||
|
$serial = $hash->{HELPER}{CONSUMER}{$key}{SerialNumber};
|
||||||
|
$oid = $hash->{HELPER}{CONSUMER}{$key}{ConsumerOid};
|
||||||
|
$oname = decode("utf8", $hash->{HELPER}{CONSUMER}{$key}{DeviceOrigName});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my $gclog = 100-$op;
|
||||||
|
Log3 ($name, 4, qq{$name - Manage consumer "$d" (SuSyID $susyid): switch on if condition PV=$op% (GridConsumption=$gclog%) is fulfilled });
|
||||||
|
|
||||||
|
my $plantOid = $hash->{HELPER}{PLANTOID};
|
||||||
|
my $errstate = 0;
|
||||||
|
my %fields = ( "Content-Type" => "application/x-www-form-urlencoded",
|
||||||
|
"Accept" => "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||||
|
);
|
||||||
|
my $rgb = "rgba(49,101,255,1)";
|
||||||
|
my $cont = [
|
||||||
|
'UsePriceLimit' => "False",
|
||||||
|
'UsesCanFrames' => "True",
|
||||||
|
'ConsumerOid' => "$oid",
|
||||||
|
'DeviceStatus' => "DeviceActive",
|
||||||
|
'DataAcceptance' => ["true", "false"],
|
||||||
|
'PowerConsumerName' => "$oname",
|
||||||
|
'Priority' => "1",
|
||||||
|
'RbTimeframeTypeEnergyPv_0' => "pv",
|
||||||
|
'MaxPriceAllowedValue' => 0,
|
||||||
|
'GridConsumptionValue' => $gcval,
|
||||||
|
'PvValue' => $pvval,
|
||||||
|
'LimitedEnergyValue' => 0,
|
||||||
|
'ConsumerIcon' => "/Images/DeviceIcons/ChargingStation.png",
|
||||||
|
'ConsumerColor.ColorString' => $rgb,
|
||||||
|
];
|
||||||
|
my $tag = "switchConsumer";
|
||||||
|
|
||||||
|
($errstate,$state) = __dispatchPost ({ name => $name,
|
||||||
|
ua => $ua,
|
||||||
|
call => 'https://www.sunnyportal.com/HoMan/Consumer/Semp/$oid',
|
||||||
|
tag => $tag,
|
||||||
|
state => $state,
|
||||||
|
fnaref => [ qw( extractSwitchConsumerData ) ],
|
||||||
|
fields => \%fields,
|
||||||
|
content => $cont,
|
||||||
|
addon => "$d:$susyid:$op", # optionales Addon für aufzurufende Funktion
|
||||||
|
daref => $daref
|
||||||
|
});
|
||||||
|
|
||||||
|
return ($errstate,$state);
|
||||||
|
}
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
# Abruf Live Daten
|
# Abruf Live Daten
|
||||||
################################################################
|
################################################################
|
||||||
@ -1431,7 +1514,7 @@ sub _getConsumerDayData { ## no critic "not used"
|
|||||||
if(!$hash->{HELPER}{PLANTOID}) {
|
if(!$hash->{HELPER}{PLANTOID}) {
|
||||||
$errstate = 1;
|
$errstate = 1;
|
||||||
$state = qq{The consumer data cannot be retrieved because the plant ID isn't set.};
|
$state = qq{The consumer data cannot be retrieved because the plant ID isn't set.};
|
||||||
Log3 $name, 2, "$name - $state";
|
Log3 ($name, 2, "$name - $state");
|
||||||
return ($errstate,$state,$reread,$retry);
|
return ($errstate,$state,$reread,$retry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1475,7 +1558,7 @@ sub _getConsumerMonthData { ## no critic "not used"
|
|||||||
if(!$hash->{HELPER}{PLANTOID}) {
|
if(!$hash->{HELPER}{PLANTOID}) {
|
||||||
$errstate = 1;
|
$errstate = 1;
|
||||||
$state = qq{The consumer data cannot be retrieved because the plant ID isn't set.};
|
$state = qq{The consumer data cannot be retrieved because the plant ID isn't set.};
|
||||||
Log3 $name, 2, "$name - $state";
|
Log3 ($name, 2, "$name - $state");
|
||||||
return ($errstate,$state,$reread,$retry);
|
return ($errstate,$state,$reread,$retry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1528,7 +1611,7 @@ sub _getConsumerYearData { ## no critic "not used"
|
|||||||
if(!$hash->{HELPER}{PLANTOID}) {
|
if(!$hash->{HELPER}{PLANTOID}) {
|
||||||
$errstate = 1;
|
$errstate = 1;
|
||||||
$state = qq{The consumer data cannot be retrieved because of the plant ID isn't set.};
|
$state = qq{The consumer data cannot be retrieved because of the plant ID isn't set.};
|
||||||
Log3 $name, 2, "$name - $state";
|
Log3 ($name, 2, "$name - $state");
|
||||||
return ($errstate,$state,$reread,$retry);
|
return ($errstate,$state,$reread,$retry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2049,7 +2132,7 @@ sub __dispatchPost {
|
|||||||
my @func = @$fnref;
|
my @func = @$fnref;
|
||||||
no strict "refs"; ## no critic 'NoStrict'
|
no strict "refs"; ## no critic 'NoStrict'
|
||||||
for my $fn (@func) {
|
for my $fn (@func) {
|
||||||
&{$fn} ($hash,$daref,$data_cont,$fnaddon,$tag);
|
$state = &{$fn} ($hash,$daref,$data_cont,$fnaddon,$tag) // $state;
|
||||||
}
|
}
|
||||||
use strict "refs";
|
use strict "refs";
|
||||||
}
|
}
|
||||||
@ -2149,6 +2232,7 @@ sub ___analyzeData { ## no critic 'complexity'
|
|||||||
my $hash = $defs{$name};
|
my $hash = $defs{$name};
|
||||||
my ($reread,$retry) = (0,0);
|
my ($reread,$retry) = (0,0);
|
||||||
my $data = "";
|
my $data = "";
|
||||||
|
my $decerror = 0; # JSON Dekodierfehler
|
||||||
|
|
||||||
my $v5d = AttrVal($name, "verbose5Data", "none");
|
my $v5d = AttrVal($name, "verbose5Data", "none");
|
||||||
my $ad_content = encode("utf8", $ad->decoded_content);
|
my $ad_content = encode("utf8", $ad->decoded_content);
|
||||||
@ -2169,7 +2253,9 @@ sub ___analyzeData { ## no critic 'complexity'
|
|||||||
name => $name,
|
name => $name,
|
||||||
});
|
});
|
||||||
|
|
||||||
$data = eval{decode_json($ad_content)} or do { $data = $ad_content };
|
$data = eval{decode_json($ad_content)} or do { $data = $ad_content;
|
||||||
|
$decerror = 1;
|
||||||
|
};
|
||||||
|
|
||||||
my $jsonerror = $ad->header('Jsonerror') // ""; # Portal meldet keine Verarbeitung des Reaquests möglich (z.B. Jahr 0000 zur Auswertung angefordert)
|
my $jsonerror = $ad->header('Jsonerror') // ""; # Portal meldet keine Verarbeitung des Reaquests möglich (z.B. Jahr 0000 zur Auswertung angefordert)
|
||||||
|
|
||||||
@ -2179,7 +2265,7 @@ sub ___analyzeData { ## no critic 'complexity'
|
|||||||
return ($reread,$retry,$errstate,$state);
|
return ($reread,$retry,$errstate,$state);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ref $data eq "HASH") {
|
if(!$decerror && ref $data eq "HASH") { # es wurde JSON empfangen und Ergebnis ist ein HASH
|
||||||
for my $k (keys %{$data}) {
|
for my $k (keys %{$data}) {
|
||||||
my $val = $data->{$k};
|
my $val = $data->{$k};
|
||||||
next if(!defined $val);
|
next if(!defined $val);
|
||||||
@ -2226,18 +2312,22 @@ sub ___analyzeData { ## no critic 'complexity'
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
elsif (!$decerror) { # es wurde JSON empfangen aber Ergebnis ist KEIN HASH
|
||||||
|
Log3 ($name, 5, "$name - decoded Content received: ". jboolmap($data));
|
||||||
|
}
|
||||||
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) {
|
||||||
Log3 ($name, 2, "$name - ERROR - User logged in but unauthorized");
|
Log3 ($name, 2, "$name - ERROR - User logged in but unauthorized");
|
||||||
my($p1,$p2) = $njdat =~ /<h2>401\s-\sUnauthorized:.(.*)?<\/h2>.*?<h3>(.*)?<\/h3>/sx;
|
my($p1,$p2) = $njdat =~ /<h2>401\s-\sUnauthorized:.(.*)?<\/h2>.*?<h3>(.*)?<\/h3>/sx;
|
||||||
$state = ($p1 // "")." ".($p2 // "");
|
$state = ($p1 // "")." ".($p2 // "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$njdat = encode("utf8", $ad->decoded_content);
|
||||||
Log3 ($name, 5, "$name - No JSON Data received:\n ".$njdat);
|
Log3 ($name, 5, "$name - No JSON Data received:\n ".$njdat);
|
||||||
|
|
||||||
$errstate = 1;
|
$errstate = 1;
|
||||||
|
$state = "ERROR - see logfile for further information";
|
||||||
}
|
}
|
||||||
|
|
||||||
return ($reread,$retry,$errstate,$state);
|
return ($reread,$retry,$errstate,$state);
|
||||||
@ -2341,8 +2431,8 @@ sub ParseData {
|
|||||||
|
|
||||||
if(!$errstate) {
|
if(!$errstate) {
|
||||||
if($setp ne "none") {
|
if($setp ne "none") {
|
||||||
my ($d,$op) = split(":",$setp);
|
my ($d,$susyid,$op) = split(":",$setp);
|
||||||
$op = ($op eq "auto") ? "off (automatic)" : $op;
|
$op = ($op eq "auto") ? "off (automatic)" : $op;
|
||||||
readingsBulkUpdate($hash, "${cclv}_${d}_Switch", $op);
|
readingsBulkUpdate($hash, "${cclv}_${d}_Switch", $op);
|
||||||
}
|
}
|
||||||
readingsBulkUpdate($hash, "lastCycleTime", $ctime ) if($ctime > 0);
|
readingsBulkUpdate($hash, "lastCycleTime", $ctime ) if($ctime > 0);
|
||||||
@ -2930,10 +3020,10 @@ sub extractConsumerMasterdata {
|
|||||||
$consumers{"${i}_ConsumerLfd"} = $i;
|
$consumers{"${i}_ConsumerLfd"} = $i;
|
||||||
my $cn = $consumers{"${i}_ConsumerName"}; # Verbrauchername
|
my $cn = $consumers{"${i}_ConsumerName"}; # Verbrauchername
|
||||||
next if(!$cn);
|
next if(!$cn);
|
||||||
$cn = replaceJunkSigns($cn); # Verbrauchername gemäß Readingreguarien verändern
|
$cn = replaceJunkSigns($cn); # Verbrauchername gemäß Readingreguarien anpassen
|
||||||
|
|
||||||
$hcon{$i}{DeviceName} = $cn;
|
$hcon{$i}{DeviceName} = $cn;
|
||||||
$hcon{$i}{DeviceOrigName} = encode("utf8", $c->{'DeviceName'}); # der originale Verbrauchername
|
$hcon{$i}{DeviceOrigName} = encode("utf8", $c->{'DeviceName'}); # der originale Verbrauchername
|
||||||
$hcon{$i}{ConsumerOid} = $consumers{"${i}_ConsumerOid"};
|
$hcon{$i}{ConsumerOid} = $consumers{"${i}_ConsumerOid"};
|
||||||
$hcon{$i}{SerialNumber} = $c->{'SerialNumber'};
|
$hcon{$i}{SerialNumber} = $c->{'SerialNumber'};
|
||||||
$hcon{$i}{SUSyID} = $c->{'SUSyID'};
|
$hcon{$i}{SUSyID} = $c->{'SUSyID'};
|
||||||
@ -3096,6 +3186,36 @@ sub extractConsumerHistData { #
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# Auswertung Ergebnis aus Switch Consumer
|
||||||
|
################################################################
|
||||||
|
sub extractSwitchConsumerData {
|
||||||
|
my $hash = shift;
|
||||||
|
my $daref = shift; # Referenz zum Datenarray
|
||||||
|
my $jdata = shift; # empfangene JSON-Daten
|
||||||
|
my $addon = shift; # ein optionales AddOn
|
||||||
|
my $tag = shift; # Kennzeichen der abgerufenen Daten/ der Abrufroutine
|
||||||
|
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
Log3 ($name, 4, "$name - extracting Switch Consumer result ");
|
||||||
|
|
||||||
|
my ($d,$susyid,$op) = split(":",$addon); # $op -> Verbraucher Manage Operation
|
||||||
|
Log3 ($name, 3, qq{$name - Set "$d $op" result: $jdata});
|
||||||
|
|
||||||
|
my $state;
|
||||||
|
if($jdata eq "true") {
|
||||||
|
$state = "ok - switched consumer $d to $op";
|
||||||
|
BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "NULL", "GETTER:all" ], 1);
|
||||||
|
BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "NULL", "SETTER:none"], 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$state = "ERROR - couldn't switch consumer $d to $op";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $state;
|
||||||
|
}
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
# Auswertung Daten aus Hilfsroutinen
|
# Auswertung Daten aus Hilfsroutinen
|
||||||
################################################################
|
################################################################
|
||||||
@ -3417,6 +3537,28 @@ sub replaceJunkSigns {
|
|||||||
return($rn);
|
return($rn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# JSON Boolean Test und Mapping
|
||||||
|
# $var = Variante der boolean Auswertung:
|
||||||
|
# "char": Rückgabe von true / false für wahr / falsch
|
||||||
|
# "bin" : Rückgabe von 1 / 0 für wahr / falsch
|
||||||
|
###############################################################################
|
||||||
|
sub jboolmap {
|
||||||
|
my $bool = shift;
|
||||||
|
my $var = shift // "char";
|
||||||
|
|
||||||
|
my $true = ($var eq "char") ? "true" : 1;
|
||||||
|
my $false = ($var eq "char") ? "false" : 0;
|
||||||
|
|
||||||
|
my $is_boolean = JSON::is_bool($bool);
|
||||||
|
|
||||||
|
if($is_boolean) {
|
||||||
|
$bool = $bool ? $true : $false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $bool;
|
||||||
|
}
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Subroutine für Portalgrafik
|
# Subroutine für Portalgrafik
|
||||||
###############################################################################
|
###############################################################################
|
||||||
@ -4984,7 +5126,8 @@ return;
|
|||||||
"LWP": 0,
|
"LWP": 0,
|
||||||
"HTTP::Cookies": 0,
|
"HTTP::Cookies": 0,
|
||||||
"MIME::Base64": 0,
|
"MIME::Base64": 0,
|
||||||
"utf8": 0
|
"utf8": 0,
|
||||||
|
"Color": 0
|
||||||
},
|
},
|
||||||
"recommends": {
|
"recommends": {
|
||||||
"FHEM::Meta": 0
|
"FHEM::Meta": 0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user