2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 12:49:34 +00:00

70_SolarEdgeAPI: source code formatting

git-svn-id: https://svn.fhem.de/fhem/trunk@20234 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
pizmus 2019-09-23 20:47:21 +00:00
parent 6a1baaaf16
commit b4a3a9ff52
2 changed files with 554 additions and 505 deletions

View File

@ -1,5 +1,6 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it. # Do not insert empty lines here, update check depends on it.
- change: 70_SolarEdgeAPI: source code formatting
- bugfix: 89_FULLY: Support Fully version 1.34 - bugfix: 89_FULLY: Support Fully version 1.34
- change: 93_DbRep: comma can be shown in sqlCmdHistory, Forum: #103908 - change: 93_DbRep: comma can be shown in sqlCmdHistory, Forum: #103908
- feature: 49_SSCamSTRM: new attribute "hideAudio" - feature: 49_SSCamSTRM: new attribute "hideAudio"

View File

@ -74,17 +74,24 @@ eval "use JSON;1" or $solarEdgeAPI_missingModul .= "JSON ";
# 1.0.0 initial version as copied from https://github.com/felixmartens/fhem # 1.0.0 initial version as copied from https://github.com/felixmartens/fhem
# with minimal changes to be able to submit it to FHEM SVN # with minimal changes to be able to submit it to FHEM SVN
# #
# 1.1.0 Detect that site does not support the "currentPowerFlow" API. # 1.1.0beta Detect that site does not support the "currentPowerFlow" API.
# Read "overview" API to get the current power. # Read "overview" API to get the current power.
# Added attributes enableStatusReadings, enableAggregatesReadings, # Added attributes enableStatusReadings, enableAggregatesReadings,
# and enableOverviewReadings. # and enableOverviewReadings.
# Note: This version was released by accident with "beta" in the
# version string.
#
# 1.1.1 source code formatting
# added TODOs in the source code
# #
############################################################################### ###############################################################################
my $solarEdgeAPI_version = "1.1.0beta"; # TODO move into hash
my $solarEdgeAPI_version = "1.1.1";
############################################################################### ###############################################################################
# TODO Are these necessary?
# Declare functions # Declare functions
sub SolarEdgeAPI_Attr(@); sub SolarEdgeAPI_Attr(@);
sub SolarEdgeAPI_Define($$); sub SolarEdgeAPI_Define($$);
@ -101,22 +108,23 @@ sub SolarEdgeAPI_ErrorHandling($$$);
sub SolarEdgeAPI_WriteReadings($$$); sub SolarEdgeAPI_WriteReadings($$$);
sub SolarEdgeAPI_Timer_GetData($); sub SolarEdgeAPI_Timer_GetData($);
# TODO move into hash
# TODO why does one of the paths have a ".json" and the others do not?
# TODO understand why reading names differ from API names
my %solarEdgeAPI_paths = ( my %solarEdgeAPI_paths = (
'status' => 'currentPowerFlow.json', 'status' => 'currentPowerFlow.json',
'aggregates' => 'energyDetails', 'aggregates' => 'energyDetails',
'overview' => 'overview' 'overview' => 'overview'
); );
sub SolarEdgeAPI_Initialize($) { sub SolarEdgeAPI_Initialize($)
{
my ($hash) = @_; my ($hash) = @_;
# Consumer
$hash->{GetFn} = "SolarEdgeAPI_Get"; $hash->{GetFn} = "SolarEdgeAPI_Get";
$hash->{DefFn} = "SolarEdgeAPI_Define"; $hash->{DefFn} = "SolarEdgeAPI_Define";
$hash->{UndefFn} = "SolarEdgeAPI_Undef"; $hash->{UndefFn} = "SolarEdgeAPI_Undef";
$hash->{NotifyFn} = "SolarEdgeAPI_Notify"; $hash->{NotifyFn} = "SolarEdgeAPI_Notify";
$hash->{AttrFn} = "SolarEdgeAPI_Attr"; $hash->{AttrFn} = "SolarEdgeAPI_Attr";
$hash->{AttrList} = "interval ". $hash->{AttrList} = "interval ".
"disable:1 ". "disable:1 ".
@ -125,103 +133,119 @@ sub SolarEdgeAPI_Initialize($) {
"enableOverviewReadings:1,0 " . "enableOverviewReadings:1,0 " .
$readingFnAttributes; $readingFnAttributes;
foreach my $d(sort keys %{$modules{SolarEdgeAPI}{defptr}}) { # TODO Is this required? Is it the right place?
foreach my $d(sort keys %{$modules{SolarEdgeAPI}{defptr}})
{
my $hash = $modules{SolarEdgeAPI}{defptr}{$d}; my $hash = $modules{SolarEdgeAPI}{defptr}{$d};
$hash->{VERSION} = $solarEdgeAPI_version; $hash->{VERSION} = $solarEdgeAPI_version;
} }
} }
sub SolarEdgeAPI_Define($$) { sub SolarEdgeAPI_Define($$)
{
my ($hash, $def) = @_; my ($hash, $def) = @_;
my @a = split( "[ \t][ \t]*", $def ); my @a = split( "[ \t][ \t]*", $def );
if (int(@a) != 5)
{
return "too few parameters: define <name> SolarEdgeAPI <API-Key> <Site-ID> <interval>";
}
return "too few parameters: define <name> SolarEdgeAPI <API-Key> <Site-ID> <interval>" if(int(@a) != 5); if ($solarEdgeAPI_missingModul)
return "Cannot define a SolarEdgeAPI device. Perl modul $solarEdgeAPI_missingModul is missing." if ( $solarEdgeAPI_missingModul ); {
return "Cannot define a SolarEdgeAPI device. Perl modul $solarEdgeAPI_missingModul is missing.";
}
my $name = $a[0]; my $name = $a[0];
my $apikey = $a[2]; my $apikey = $a[2];
my $siteid = $a[3]; my $siteid = $a[3];
my $interval = $a[4] || 'auto'; my $interval = $a[4] || 'auto';
$hash->{APIKEY} = $apikey; $hash->{APIKEY} = $apikey;
$hash->{SITEID} = $siteid; $hash->{SITEID} = $siteid;
$hash->{INTERVAL} = $interval; $hash->{INTERVAL} = $interval;
$hash->{PORT} = 80; $hash->{PORT} = 80;
$hash->{VERSION} = $solarEdgeAPI_version; $hash->{VERSION} = $solarEdgeAPI_version;
$hash->{NOTIFYDEV} = "global"; $hash->{NOTIFYDEV} = "global";
$hash->{actionQueue} = []; $hash->{actionQueue} = [];
# TODO Remove this?
$attr{$name}{room} = "Photovoltaik" if( !defined( $attr{$name}{room} ) ); $attr{$name}{room} = "Photovoltaik" if( !defined( $attr{$name}{room} ) );
Log3 $name, 3, "SolarEdgeAPI ($name) - defined SolarEdgeAPI Device with SiteID $hash->{SITEID} and Interval $hash->{INTERVAL}"; Log3 $name, 3, "SolarEdgeAPI ($name) - defined, SiteID $hash->{SITEID}, Interval $hash->{INTERVAL}";
$modules{SolarEdgeAPI}{defptr}{SITEID} = $hash; $modules{SolarEdgeAPI}{defptr}{SITEID} = $hash;
return undef; return undef;
} }
sub SolarEdgeAPI_Undef($$) { sub SolarEdgeAPI_Undef($$)
{
my ($hash, $arg) = @_; my ($hash, $arg) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
Log3 $name, 3, "SolarEdgeAPI ($name) - deleted";
Log3 $name, 3, "SolarEdgeAPI ($name) - Device $name deleted";
delete $modules{SolarEdgeAPI}{defptr}{SITEID} if( defined($modules{SolarEdgeAPI}{defptr}{SITEID}) and $hash->{SITEID} ); delete $modules{SolarEdgeAPI}{defptr}{SITEID} if( defined($modules{SolarEdgeAPI}{defptr}{SITEID}) and $hash->{SITEID} );
return undef; return undef;
} }
sub SolarEdgeAPI_Attr(@) { sub SolarEdgeAPI_Attr(@)
{
my ($cmd, $name, $attrName, $attrVal) = @_; my ($cmd, $name, $attrName, $attrVal) = @_;
my $hash = $defs{$name}; my $hash = $defs{$name};
if ($attrName eq "disable")
if( $attrName eq "disable" ) { {
if( $cmd eq "set" and $attrVal eq "1" ) { if (($cmd eq "set") and ($attrVal eq "1"))
{
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
readingsSingleUpdate ( $hash, "state", "disabled", 1); readingsSingleUpdate ( $hash, "state", "disabled", 1);
Log3 $name, 3, "SolarEdgeAPI ($name) - disabled"; Log3 $name, 3, "SolarEdgeAPI ($name) - disabled";
}
} elsif( $cmd eq "del" ) { elsif ($cmd eq "del")
{
Log3 $name, 3, "SolarEdgeAPI ($name) - enabled"; Log3 $name, 3, "SolarEdgeAPI ($name) - enabled";
} }
} }
if( $attrName eq "disabledForIntervals" ) { # TODO Is this the common/intended implementation of this feature?
if( $cmd eq "set" ) { if ($attrName eq "disabledForIntervals")
{
if ($cmd eq "set")
{
return "check disabledForIntervals Syntax HH:MM-HH:MM or 'HH:MM-HH:MM HH:MM-HH:MM ...'" return "check disabledForIntervals Syntax HH:MM-HH:MM or 'HH:MM-HH:MM HH:MM-HH:MM ...'"
unless($attrVal =~ /^((\d{2}:\d{2})-(\d{2}:\d{2})\s?)+$/); unless($attrVal =~ /^((\d{2}:\d{2})-(\d{2}:\d{2})\s?)+$/);
Log3 $name, 3, "SolarEdgeAPI ($name) - disabledForIntervals"; Log3 $name, 3, "SolarEdgeAPI ($name) - disabledForIntervals";
readingsSingleUpdate ( $hash, "state", "disabled", 1 ); readingsSingleUpdate ( $hash, "state", "disabled", 1 );
}
} elsif( $cmd eq "del" ) { elsif ($cmd eq "del")
{
Log3 $name, 3, "SolarEdgeAPI ($name) - enabled"; Log3 $name, 3, "SolarEdgeAPI ($name) - enabled";
readingsSingleUpdate( $hash, "state", "active", 1 ); readingsSingleUpdate( $hash, "state", "active", 1 );
} }
} }
if( $attrName eq "interval" ) { if ($attrName eq "interval")
if( $cmd eq "set" ) { {
if($attrVal eq "auto" || $attrVal > 120) { if ($cmd eq "set")
{
if (($attrVal eq "auto") || ($attrVal > 120))
{
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
$hash->{INTERVAL} = $attrVal; $hash->{INTERVAL} = $attrVal;
Log3 $name, 3, "SolarEdgeAPI ($name) - set interval to $attrVal"; Log3 $name, 3, "SolarEdgeAPI ($name) - set interval to $attrVal";
SolarEdgeAPI_Timer_GetData($hash); SolarEdgeAPI_Timer_GetData($hash);
} else { }
else
{
Log3 $name, 3, "SolarEdgeAPI ($name) - interval too small, please use something >= 120 (sec), default is 300 (sec)"; Log3 $name, 3, "SolarEdgeAPI ($name) - interval too small, please use something >= 120 (sec), default is 300 (sec)";
return "interval too small, please use something >= 120 (sec), default is 300 (sec) daytime and 1200 (sec) nighttime"; return "interval too small, please use something >= 120 (sec), default is 300 (sec) daytime and 1200 (sec) nighttime";
} }
} elsif( $cmd eq "del" ) { }
elsif ($cmd eq "del")
{
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
$hash->{INTERVAL} = 'auto'; $hash->{INTERVAL} = 'auto';
Log3 $name, 3, "SolarEdgeAPI ($name) - set interval to default"; Log3 $name, 3, "SolarEdgeAPI ($name) - set interval to default";
@ -271,10 +295,11 @@ sub SolarEdgeAPI_Attr(@) {
return undef; return undef;
} }
sub SolarEdgeAPI_Notify($$) { sub SolarEdgeAPI_Notify($$)
{
my ($hash,$dev) = @_; my ($hash,$dev) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
return if (IsDisabled($name)); return if (IsDisabled($name));
my $devname = $dev->{NAME}; my $devname = $dev->{NAME};
@ -282,41 +307,45 @@ sub SolarEdgeAPI_Notify($$) {
my $events = deviceEvents($dev,1); my $events = deviceEvents($dev,1);
return if (!$events); return if (!$events);
if ((grep /^INITIALIZED$/,@{$events}) or
(grep /^DELETEATTR.$name.disable$/,@{$events}) or
(grep /^DELETEATTR.$name.interval$/,@{$events}) or
((grep /^DEFINED.$name$/,@{$events}) and $init_done))
{
SolarEdgeAPI_Timer_GetData($hash);
}
SolarEdgeAPI_Timer_GetData($hash) if( grep /^INITIALIZED$/,@{$events}
or grep /^DELETEATTR.$name.disable$/,@{$events}
or grep /^DELETEATTR.$name.interval$/,@{$events}
or (grep /^DEFINED.$name$/,@{$events} and $init_done) );
return; return;
} }
sub SolarEdgeAPI_Get($@) { sub SolarEdgeAPI_Get($@)
{
my ($hash, $name, $cmd) = @_; my ($hash, $name, $cmd) = @_;
# TODO rework
my $arg; my $arg;
if ($cmd eq 'status')
{
if( $cmd eq 'status' ) {
$arg = lc($cmd); $arg = lc($cmd);
}
} elsif( $cmd eq 'aggregates' ) { elsif ($cmd eq 'aggregates')
{
$arg = lc($cmd); $arg = lc($cmd);
}
} elsif( $cmd eq 'overview' ) { elsif ($cmd eq 'overview')
{
$arg = lc($cmd); $arg = lc($cmd);
}
} else { else
{
my $list = 'status:noArg aggregates:noArg overview:noArg'; my $list = 'status:noArg aggregates:noArg overview:noArg';
return "Unknown argument $cmd, choose one of $list"; return "Unknown argument $cmd, choose one of $list";
} }
return 'There are still path commands in the action queue' if ((defined($hash->{actionQueue})) and (scalar(@{$hash->{actionQueue}}) > 0))
if( defined($hash->{actionQueue}) and scalar(@{$hash->{actionQueue}}) > 0 ); {
return 'There are still path commands in the action queue';
}
unshift( @{$hash->{actionQueue}}, $arg ); unshift( @{$hash->{actionQueue}}, $arg );
SolarEdgeAPI_GetData($hash); SolarEdgeAPI_GetData($hash);
@ -324,14 +353,18 @@ sub SolarEdgeAPI_Get($@) {
return undef; return undef;
} }
sub SolarEdgeAPI_Timer_GetData($) { # TODO rename?
sub SolarEdgeAPI_Timer_GetData($)
{
my $hash = shift; my $hash = shift;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $interval = $hash->{INTERVAL}; my $interval = $hash->{INTERVAL};
if( defined($hash->{actionQueue}) and scalar(@{$hash->{actionQueue}}) == 0 ) { if ((defined($hash->{actionQueue})) and (scalar(@{$hash->{actionQueue}}) == 0))
if( not IsDisabled($name) ) { {
if (not IsDisabled($name))
{
while (my $obj = each %solarEdgeAPI_paths) while (my $obj = each %solarEdgeAPI_paths)
{ {
if ((($obj eq "status") and (AttrVal($name, "enableStatusReadings", 1))) or if ((($obj eq "status") and (AttrVal($name, "enableStatusReadings", 1))) or
@ -341,47 +374,56 @@ sub SolarEdgeAPI_Timer_GetData($) {
unshift( @{$hash->{actionQueue}}, $obj ); unshift( @{$hash->{actionQueue}}, $obj );
} }
} }
SolarEdgeAPI_GetData($hash); SolarEdgeAPI_GetData($hash);
}
} else { else
{
# TODO is this needed?
# TODO avoid inverted condition above
readingsSingleUpdate($hash,'state','disabled',1); readingsSingleUpdate($hash,'state','disabled',1);
} }
} }
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
localtime(time);
if($interval eq "auto"){ if ($interval eq "auto")
if ($hour > 6&& $hour < 22) { $interval = 300;} {
else { $interval = 1200;} if ($hour > 6&& $hour < 22)
{
$interval = 300;
}
else
{
$interval = 1200;
}
} }
InternalTimer( gettimeofday()+$interval, 'SolarEdgeAPI_Timer_GetData', $hash ); InternalTimer( gettimeofday()+$interval, 'SolarEdgeAPI_Timer_GetData', $hash );
Log3 $name, 4, "SolarEdgeAPI ($name) - Call InternalTimer SolarEdgeAPI_Timer_GetData with interval $interval"; Log3 $name, 4, "SolarEdgeAPI ($name) - Call InternalTimer SolarEdgeAPI_Timer_GetData with interval $interval";
} }
sub SolarEdgeAPI_GetData($) { # TODO rename
sub SolarEdgeAPI_GetData($)
{
my ($hash) = @_; my ($hash) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $siteid = $hash->{SITEID};
my $siteid = $hash->{SITEID};
my $host = "monitoringapi.solaredge.com/site/" . $siteid; my $host = "monitoringapi.solaredge.com/site/" . $siteid;
my $apikey = $hash->{APIKEY}; my $apikey = $hash->{APIKEY};
my $path = pop(@{$hash->{actionQueue}}); my $path = pop(@{$hash->{actionQueue}});
# TODO explain
my $params = ""; my $params = "";
if($path eq "aggregates" ){ if ($path eq "aggregates")
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = {
localtime(time); my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
$params= "&timeUnit=QUARTER_OF_AN_HOUR&startTime=".(1900+$year)."-".(1+$mon)."-".$mday."%2000:00:00&endTime=".(1900+$year)."-".(1+$mon)."-".$mday."%20".$hour.":".$min.":".$sec; $params= "&timeUnit=QUARTER_OF_AN_HOUR&startTime=".(1900+$year)."-".(1+$mon)."-".$mday."%2000:00:00&endTime=".(1900+$year)."-".(1+$mon)."-".$mday."%20".$hour.":".$min.":".$sec;
} }
my $uri = $host . '/' . $solarEdgeAPI_paths{$path} . "?api_key=" . $apikey.$params; my $uri = $host . '/' . $solarEdgeAPI_paths{$path} . "?api_key=" . $apikey.$params;
# TODO remove this?
readingsSingleUpdate($hash, 'state', 'fetch data - '.scalar(@{$hash->{actionQueue}}).' entries in the Queue',1); readingsSingleUpdate($hash, 'state', 'fetch data - '.scalar(@{$hash->{actionQueue}}).' entries in the Queue',1);
HttpUtils_NonblockingGet( HttpUtils_NonblockingGet(
@ -399,19 +441,19 @@ sub SolarEdgeAPI_GetData($) {
Log3 $name, 4, "SolarEdgeAPI ($name) - Send with URI: http://$uri"; Log3 $name, 4, "SolarEdgeAPI ($name) - Send with URI: http://$uri";
} }
sub SolarEdgeAPI_ErrorHandling($$$) { # TODO rename
sub SolarEdgeAPI_ErrorHandling($$$)
{
my ($param,$err,$data) = @_; my ($param,$err,$data) = @_;
my $hash = $param->{hash}; my $hash = $param->{hash};
my $name = $hash->{NAME}; my $name = $hash->{NAME};
# TODO factor out error handing
### Begin Error Handling ### Begin Error Handling
if( defined( $err ) ) { if (defined($err) and ($err ne ""))
if( $err ne "" ) { {
# TODO do error reporting via Log3 only!?
readingsBeginUpdate($hash); readingsBeginUpdate($hash);
readingsBulkUpdate($hash, 'state', $err, 1); readingsBulkUpdate($hash, 'state', $err, 1);
readingsBulkUpdate($hash, 'lastRequestError', $err, 1); readingsBulkUpdate($hash, 'lastRequestError', $err, 1);
@ -422,60 +464,59 @@ sub SolarEdgeAPI_ErrorHandling($$$) {
$hash->{actionQueue} = []; $hash->{actionQueue} = [];
return; return;
} }
}
if( $data eq "" and exists( $param->{code} ) && $param->{code} ne 200 ) {
if (($data eq "") and (exists($param->{code})) and ($param->{code} ne 200))
{
readingsBeginUpdate($hash); readingsBeginUpdate($hash);
readingsBulkUpdate($hash, 'state', $param->{code}, 1); readingsBulkUpdate($hash, 'state', $param->{code}, 1);
readingsBulkUpdate($hash, 'lastRequestError', $param->{code}, 1); readingsBulkUpdate($hash, 'lastRequestError', $param->{code}, 1);
Log3 $name, 3, "SolarEdgeAPI ($name) - RequestERROR: ".$param->{code};
readingsEndUpdate($hash, 1); readingsEndUpdate($hash, 1);
Log3 $name, 3, "SolarEdgeAPI ($name) - RequestERROR: ".$param->{code};
Log3 $name, 5, "SolarEdgeAPI ($name) - RequestERROR: received http code ".$param->{code}." without any data after requesting"; Log3 $name, 5, "SolarEdgeAPI ($name) - RequestERROR: received http code ".$param->{code}." without any data after requesting";
$hash->{actionQueue} = []; $hash->{actionQueue} = [];
return; return;
} }
if( ( $data =~ /Error/i ) and exists( $param->{code} ) ) { if (($data =~ /Error/i) and (exists( $param->{code})))
{
readingsBeginUpdate($hash); readingsBeginUpdate($hash);
readingsBulkUpdate($hash, 'state', $param->{code}, 1); readingsBulkUpdate($hash, 'state', $param->{code}, 1);
readingsBulkUpdate($hash, "lastRequestError", $param->{code}, 1); readingsBulkUpdate($hash, "lastRequestError", $param->{code}, 1);
readingsEndUpdate($hash, 1); readingsEndUpdate($hash, 1);
Log3 $name, 3, "SolarEdgeAPI ($name) - statusRequestERROR: http error ".$param->{code}; Log3 $name, 3, "SolarEdgeAPI ($name) - statusRequestERROR: http error ".$param->{code};
$hash->{actionQueue} = []; $hash->{actionQueue} = [];
return; return;
### End Error Handling
} }
SolarEdgeAPI_GetData($hash) ### End Error Handling
if( defined($hash->{actionQueue}) and scalar(@{$hash->{actionQueue}}) > 0 );
# TODO Is the order ok: first sending the next request the processing the response (below)!?
if (defined($hash->{actionQueue}) and scalar(@{$hash->{actionQueue}}) > 0)
{
SolarEdgeAPI_GetData($hash);
}
Log3 $name, 4, "SolarEdgeAPI ($name) - Receive JSON data: $data"; Log3 $name, 4, "SolarEdgeAPI ($name) - Receive JSON data: $data";
SolarEdgeAPI_ResponseProcessing($hash, $param->{setCmd}, $data); SolarEdgeAPI_ResponseProcessing($hash, $param->{setCmd}, $data);
} }
sub SolarEdgeAPI_ResponseProcessing($$$) { sub SolarEdgeAPI_ResponseProcessing($$$)
{
my ($hash, $path, $json) = @_; my ($hash, $path, $json) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $decode_json;
my $readings; my $readings;
my $decode_json; # TODO rename, here and in other functions
$decode_json = eval{decode_json($json)}; $decode_json = eval{decode_json($json)};
if($@){ if ($@)
{
# TODO error reporting via Log3 only!?
Log3 $name, 4, "SolarEdgeAPI ($name) - error while request: $@"; Log3 $name, 4, "SolarEdgeAPI ($name) - error while request: $@";
readingsBeginUpdate($hash); readingsBeginUpdate($hash);
readingsBulkUpdate($hash, 'JSON Error', $@); readingsBulkUpdate($hash, 'JSON Error', $@);
@ -484,68 +525,74 @@ sub SolarEdgeAPI_ResponseProcessing($$$) {
return; return;
} }
# TODO english
#### Verarbeitung der Readings zum passenden Path #### Verarbeitung der Readings zum passenden Path
if( $path eq 'aggregates') { if ($path eq 'aggregates')
{
$readings = SolarEdgeAPI_ReadingsProcessing_Aggregates($hash,$decode_json); $readings = SolarEdgeAPI_ReadingsProcessing_Aggregates($hash,$decode_json);
}
} elsif( $path eq 'status') { elsif ($path eq 'status')
{
$readings = SolarEdgeAPI_ReadingsProcessing_Status($hash,$decode_json); $readings = SolarEdgeAPI_ReadingsProcessing_Status($hash,$decode_json);
}
} elsif( $path eq 'overview') { elsif ($path eq 'overview')
{
$readings = SolarEdgeAPI_ReadingsProcessing_Overview($hash,$decode_json); $readings = SolarEdgeAPI_ReadingsProcessing_Overview($hash,$decode_json);
}
} else { else
{
# TODO Does this make sense? Understand what this does.
$readings = $decode_json; $readings = $decode_json;
} }
SolarEdgeAPI_WriteReadings($hash, $path, $readings); SolarEdgeAPI_WriteReadings($hash, $path, $readings);
} }
sub SolarEdgeAPI_WriteReadings($$$) { sub SolarEdgeAPI_WriteReadings($$$)
{
my ($hash, $path, $readings) = @_; my ($hash, $path, $readings) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
Log3 $name, 4, "SolarEdgeAPI ($name) - Write Readings"; Log3 $name, 4, "SolarEdgeAPI ($name) - Write Readings";
readingsBeginUpdate($hash); readingsBeginUpdate($hash);
while( my ($r,$v) = each %{$readings} ) { while (my ($r,$v) = each %{$readings})
{
readingsBulkUpdate($hash,$path.'-'.$r,$v); readingsBulkUpdate($hash,$path.'-'.$r,$v);
} }
# TODO remove this reading? Replace by logging.
readingsBulkUpdateIfChanged($hash, 'actionQueue', scalar(@{$hash->{actionQueue}}).' entries in the Queue'); readingsBulkUpdateIfChanged($hash, 'actionQueue', scalar(@{$hash->{actionQueue}}).' entries in the Queue');
readingsBulkUpdateIfChanged($hash,'state',(defined($hash->{actionQueue}) and scalar(@{$hash->{actionQueue}}) == 0 ? 'ready' : 'fetch data - ' . scalar(@{$hash->{actionQueue}}) . ' paths in actionQueue')); # TODO remove this reading?
readingsBulkUpdateIfChanged($hash, 'state', ((defined($hash->{actionQueue}) and (scalar(@{$hash->{actionQueue}}) == 0)) ? 'ready' : 'fetch data - '.scalar(@{$hash->{actionQueue}}).' paths in actionQueue'));
readingsEndUpdate($hash, 1); readingsEndUpdate($hash, 1);
} }
sub SolarEdgeAPI_ReadingsProcessing_Aggregates($$) { sub SolarEdgeAPI_ReadingsProcessing_Aggregates($$)
{
my ($hash, $decode_json) = @_; my ($hash, $decode_json) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my %readings; my %readings;
if (ref($decode_json) eq "HASH")
if( ref($decode_json) eq "HASH" ) { {
my $data = $decode_json->{'energyDetails'}; my $data = $decode_json->{'energyDetails'};
$readings{'unit'} = $data->{'unit'} || "Error Reading Response"; $readings{'unit'} = $data->{'unit'} || "Error Reading Response";
$readings{'timeUnit'} = $data->{'timeUnit'} || "Error Reading Response"; $readings{'timeUnit'} = $data->{'timeUnit'} || "Error Reading Response";
$data = $decode_json->{'energyDetails'}->{'meters'};
$data = $decode_json->{'energyDetails'}->{'meters'};
my $meter_type = ""; my $meter_type = "";
my $meter_cum = 0; my $meter_cum = 0;
my $meter_val = 0; my $meter_val = 0;
foreach my $meter (@{$decode_json->{'energyDetails'}->{'meters'}})
foreach my $meter ( @{$decode_json->{'energyDetails'}->{'meters'}}) { {
# Meters # meters
$meter_type = $meter->{'type'}; $meter_type = $meter->{'type'};
$meter_cum = 0; $meter_cum = 0;
$meter_val = 0; $meter_val = 0;
foreach my $meterTelemetry (@{$meter -> {'values'}}) { foreach my $meterTelemetry (@{$meter->{'values'}})
{
my $v = $meterTelemetry->{'value'}; my $v = $meterTelemetry->{'value'};
$meter_cum = $meter_cum + $v; $meter_cum = $meter_cum + $v;
$meter_val = $v; $meter_val = $v;
@ -553,19 +600,21 @@ sub SolarEdgeAPI_ReadingsProcessing_Aggregates($$) {
$readings{$meter_type."-recent15min"} = $meter_val; $readings{$meter_type."-recent15min"} = $meter_val;
$readings{$meter_type."-cumToday"} = $meter_cum; $readings{$meter_type."-cumToday"} = $meter_cum;
} }
}
} else { else
{
# TODO do error reporting via Log3 only
$readings{'error'} = 'aggregates response is not a Hash'; $readings{'error'} = 'aggregates response is not a Hash';
} }
return \%readings; return \%readings;
} }
sub SolarEdgeAPI_ReadingsProcessing_Status($$) { sub SolarEdgeAPI_ReadingsProcessing_Status($$)
{
my ($hash, $decode_json) = @_; my ($hash, $decode_json) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my %readings; my %readings;
my $data = $decode_json->{'siteCurrentPowerFlow'}; my $data = $decode_json->{'siteCurrentPowerFlow'};
@ -580,7 +629,6 @@ sub SolarEdgeAPI_ReadingsProcessing_Status($$) {
$readings{'updateRefreshRate'} = $data->{'updateRefreshRate'} || "Error Reading Response"; $readings{'updateRefreshRate'} = $data->{'updateRefreshRate'} || "Error Reading Response";
# Connections / Directions # Connections / Directions
my $pv2load = 0; my $pv2load = 0;
my $pv2storage = 0; my $pv2storage = 0;
my $load2storage = 0; my $load2storage = 0;
@ -590,35 +638,32 @@ sub SolarEdgeAPI_ReadingsProcessing_Status($$) {
foreach my $connection ( @{ $data->{'connections'} }) { foreach my $connection ( @{ $data->{'connections'} }) {
my $from = lc($connection->{'from'}); my $from = lc($connection->{'from'});
my $to = lc($connection->{'to'}); my $to = lc($connection->{'to'});
if($from eq 'grid'&&$to eq "load"){ $grid2load = 1;} # TODO don't mix " and '
if($from eq "load"&&$to eq 'grid') {$load2grid = 1;} if (($from eq 'grid') and ($to eq "load")) { $grid2load = 1; }
if($from eq 'load'&&$to eq "storage"){ $load2storage = 1;} if (($from eq "load") and ($to eq 'grid')) { $load2grid = 1; }
if($from eq 'pv'&&$to eq "storage") {$pv2storage = 1;} if (($from eq 'load') and ($to eq "storage")) { $load2storage = 1; }
if($from eq 'pv'&&$to eq "load") {$pv2load = 1;} if (($from eq 'pv') and ($to eq "storage")) { $pv2storage = 1; }
if($from eq 'storage'&&$to eq "load"){ $storage2load = 1;} if (($from eq 'pv') and ($to eq "load")) { $pv2load = 1; }
if (($from eq 'storage') and ($to eq "load")) { $storage2load = 1; }
} }
# GRID # GRID
$readings{'grid_status'} = $data->{'GRID'}->{"status"} || "Error Reading Response"; # TODO rethink error reporting via readings
$readings{'grid_status'} = $data->{'GRID'}->{"status"}||"Error Reading Response"; $readings{'grid_power'} = (($load2grid > 0) ? "-" : "").$data->{'GRID'}->{"currentPower"};
$readings{'grid_power'} = ($load2grid >0 ? "-" : "") . $data->{'GRID'}->{"currentPower"};
# LOAD # LOAD
$readings{'load_status'} = $data->{'LOAD'}->{"status"} || "Error Reading Response"; $readings{'load_status'} = $data->{'LOAD'}->{"status"} || "Error Reading Response";
$readings{'load_power'} = $data->{'LOAD'}->{"currentPower"}; $readings{'load_power'} = $data->{'LOAD'}->{"currentPower"};
# PV # PV
$readings{'pv_status'} = $data->{'PV'}->{"status"} || "Error Reading Response"; $readings{'pv_status'} = $data->{'PV'}->{"status"} || "Error Reading Response";
$readings{'pv_power'} = $data->{'PV'}->{"currentPower"}; $readings{'pv_power'} = $data->{'PV'}->{"currentPower"};
# Storage # Storage
$readings{'storage_status'} = $data->{'STORAGE'}->{"status"} || "No storage found"; $readings{'storage_status'} = $data->{'STORAGE'}->{"status"} || "No storage found";
if ($readings{'storage_status'} ne "No storage found") if ($readings{'storage_status'} ne "No storage found")
{ {
$readings{'storage_power'} = ($storage2load >0 ? "-" : "") . $data->{'STORAGE'}->{"currentPower"}; $readings{'storage_power'} = (($storage2load > 0) ? "-" : "").$data->{'STORAGE'}->{"currentPower"};
$readings{'storage_level'} = $data->{'STORAGE'}->{"chargeLevel"} || "Error Reading Response"; $readings{'storage_level'} = $data->{'STORAGE'}->{"chargeLevel"} || "Error Reading Response";
$readings{'storage_critical'} = $data->{'STORAGE'}->{"critical"}; $readings{'storage_critical'} = $data->{'STORAGE'}->{"critical"};
} }
@ -627,7 +672,8 @@ sub SolarEdgeAPI_ReadingsProcessing_Status($$) {
return \%readings; return \%readings;
} }
sub SolarEdgeAPI_ReadingsProcessing_Overview($$) { sub SolarEdgeAPI_ReadingsProcessing_Overview($$)
{
my ($hash, $decode_json) = @_; my ($hash, $decode_json) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
@ -636,6 +682,8 @@ sub SolarEdgeAPI_ReadingsProcessing_Overview($$) {
$readings{'power'} = $data->{'currentPower'}->{"power"}; $readings{'power'} = $data->{'currentPower'}->{"power"};
# TODO generate more readings from the overview API. Some readings might only be relevant once per day.
return \%readings; return \%readings;
} }