SolarView: Auslesen dedizierter Wechselrichter

git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1552 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
to-be
2012-05-12 06:47:28 +00:00
parent 46e64808e5
commit c134b62122

View File

@@ -10,22 +10,30 @@
# #
############################################################################## ##############################################################################
# #
# SolarView is a powerful datalogger for photovoltaic systems that runs on # SolarView is a powerful ;) datalogger for photovoltaic systems that runs on
# an AVM Fritz!Box (and also on x86 systems). For details see the SV homepage: # an AVM Fritz!Box (and also on x86 systems). For details see the SV homepage:
# http://www.amhamberg.de/solarview_fritzbox.aspx # http://www.solarview.info
# #
# SV supports many different inverters. To read the SV power values using # SV supports many different inverters. To read the SV power values using
# this module, a TCP-Server must be enabled for SV by adding the parameter # this module, a TCP-Server must be enabled for SV by adding the parameter
# "-TCP <port>" to the startscript (see the SV manual). # "-TCP <port>" to the startscript (see the SV manual).
# #
# usage: # usage:
# define <name> SolarView <host> <port> [<interval> [<timeout>]] # define <name> SolarView <host> <port> [wr<i> wr...] [<interval> [<timeout>]]
#
# example:
# define sv SolarView fritz.box 15000 wr1 wr2 60
# #
# If <interval> is positive, new values are read every <interval> seconds. # If <interval> is positive, new values are read every <interval> seconds.
# If <interval> is 0, new values are read whenever a get request is called # If <interval> is 0, new values are read whenever a get request is called
# on <name>. The default for <interval> is 300 (i.e. 5 minutes). # on <name>. The default for <interval> is 300 (i.e. 5 minutes).
# #
# get <name> <key> # The parameters wr<i> specify the number(s) of the inverter(s) to be read.
# When omitted, the sum of all inverters is read. If more than one inverter
# is specified, the names of the readings are prefixed with the inverter
# number, e.g. 'wr2_currentPower'.
#
# get <name> [wr<i>_]<key>
# #
# where <key> is one of currentPower, totalEnergy, totalEnergyDay, # where <key> is one of currentPower, totalEnergy, totalEnergyDay,
# totalEnergyMonth, totalEnergyYear, UDC, IDC, UDCB, IDCB, UDCC, IDCC, # totalEnergyMonth, totalEnergyYear, UDC, IDC, UDCB, IDCB, UDCC, IDCC,
@@ -92,11 +100,22 @@ SolarView_Define($$)
if (int(@args) < 4) if (int(@args) < 4)
{ {
return "SolarView_Define: too few arguments. Usage:\n" . return "SolarView_Define: too few arguments. Usage:\n" .
"define <name> SolarView <host> <port> [<interval> [<timeout>]]"; "define <name> SolarView <host> <port> [wr<i> wr...] [<interval> [<timeout>]]";
} }
$hash->{Host} = $args[2]; $hash->{Host} = $args[2];
$hash->{Port} = $args[3]; $hash->{Port} = $args[3];
# collect the set of inverters which are to be read
@{$hash->{WRs2Read}} = (0);
while ((int(@args) >= 5) && ($args[4] =~ /^[Ww][Rr](\d+)$/))
{
push @{$hash->{WRs2Read}}, $1 if int($1);
splice(@args, 4, 1);
}
# remove WR0 if exactly one inverter has been specified
shift @{$hash->{WRs2Read}} if (int(@{$hash->{WRs2Read}}) == 2);
$hash->{Interval} = int(@args) >= 5 ? int($args[4]) : 300; $hash->{Interval} = int(@args) >= 5 ? int($args[4]) : 300;
$hash->{Timeout} = int(@args) >= 6 ? int($args[5]) : 4; $hash->{Timeout} = int(@args) >= 6 ? int($args[5]) : 4;
@@ -109,17 +128,20 @@ SolarView_Define($$)
$hash->{UseSVNight} = 'yes'; # use the on/off timings from SV (else: SUNRISE_EL) $hash->{UseSVNight} = 'yes'; # use the on/off timings from SV (else: SUNRISE_EL)
$hash->{UseSVTime} = ''; # use the SV time as timestamp (else: TimeNow()) $hash->{UseSVTime} = ''; # use the SV time as timestamp (else: TimeNow())
# internal variables
$hash->{Debounced} = 0;
$hash->{STATE} = 'Initializing'; $hash->{STATE} = 'Initializing';
my $timenow = TimeNow(); my $timenow = TimeNow();
# initialization
for my $wr (@{$hash->{WRs2Read}})
{
$hash->{SolarView_WR($hash, 'Debounced', $wr)} = 0;
for my $get (@gets) for my $get (@gets)
{ {
$hash->{READINGS}{$get}{VAL} = $hash->{Invalid}; $hash->{READINGS}{SolarView_WR($hash, $get, $wr)}{VAL} = $hash->{Invalid};
$hash->{READINGS}{$get}{TIME} = $timenow; $hash->{READINGS}{SolarView_WR($hash, $get, $wr)}{TIME} = $timenow;
}
} }
SolarView_Update($hash); SolarView_Update($hash);
@@ -154,8 +176,11 @@ SolarView_Update($)
Log 4, "$hash->{NAME} tries to contact solarview at $hash->{Host}:$hash->{Port}"; Log 4, "$hash->{NAME} tries to contact solarview at $hash->{Host}:$hash->{Port}";
my $success = 0; my $success = 0;
my %readings = ();
my $timenow = TimeNow(); my $timenow = TimeNow();
for my $wr (@{$hash->{WRs2Read}})
{
my %readings = ();
my $rereads = $hash->{Rereads}; my $rereads = $hash->{Rereads};
eval { eval {
@@ -170,11 +195,11 @@ SolarView_Update($)
if ($socket and $socket->connected()) if ($socket and $socket->connected())
{ {
$socket->autoflush(1); $socket->autoflush(1);
print $socket "00*\r\n"; printf $socket "%02d*\r\n", int($wr);
my $res = <$socket>; my $res = <$socket>;
close($socket); close($socket);
if ($res and $res =~ /^\{(00,[\d\.,]+)\},/) if ($res and $res =~ /^\{(\d\d,[\d\.,]+)\},/)
{ {
my @vals = split(/,/, $1); my @vals = split(/,/, $1);
@@ -184,6 +209,7 @@ SolarView_Update($)
$vals[3], $vals[2], $vals[1], $vals[4], $vals[5]); $vals[3], $vals[2], $vals[1], $vals[4], $vals[5]);
} }
# parse the result from SV to dedicated values
for my $i (6..19) for my $i (6..19)
{ {
if (defined($vals[$i])) if (defined($vals[$i]))
@@ -192,6 +218,7 @@ SolarView_Update($)
} }
} }
# need to reread?
if ($rereads and $readings{currentPower} == 0) if ($rereads and $readings{currentPower} == 0)
{ {
sleep(1); sleep(1);
@@ -199,50 +226,51 @@ SolarView_Update($)
goto READ_SV; goto READ_SV;
} }
alarm 0;
# if Debounce is enabled (>0), then skip one! drop of # if Debounce is enabled (>0), then skip one! drop of
# currentPower from 'greater than Debounce' to 'Zero' # currentPower from 'greater than Debounce' to 'Zero'
# #
if ($hash->{Debounce} > 0 and if ($hash->{Debounce} > 0 and
$hash->{Debounce} < $hash->{READINGS}{currentPower}{VAL} and $hash->{Debounce} < $hash->{READINGS}{SolarView_WR($hash, 'currentPower', $wr)}{VAL} and
$readings{currentPower} == 0 and not $hash->{Debounced}) $readings{currentPower} == 0 and not $hash->{SolarView_WR($hash, 'Debounced', $wr)})
{ {
# revert to the previous value # revert to the previous value
$readings{currentPower} = $hash->{READINGS}{currentPower}{VAL}; $readings{currentPower} = $hash->{READINGS}{SolarView_WR($hash, 'currentPower', $wr)}{VAL};
$hash->{Debounced} = 1; $hash->{SolarView_WR($hash, 'Debounced', $wr)} = 1;
} else { } else {
$hash->{Debounced} = 0; $hash->{SolarView_WR($hash, 'Debounced', $wr)} = 0;
} }
$success = 1; # copy the values to the READINGS
}
}
};
alarm 0;
if ($success)
{
for my $get (@gets) for my $get (@gets)
{ {
# update and notify readings if they have changed # update and notify readings if they have changed
if ($hash->{READINGS}{$get}{VAL} != $readings{$get}) if ($hash->{READINGS}{SolarView_WR($hash, $get, $wr)}{VAL} != $readings{$get})
{ {
$hash->{READINGS}{$get}{VAL} = $readings{$get}; $hash->{READINGS}{SolarView_WR($hash, $get, $wr)}{VAL} = $readings{$get};
$hash->{READINGS}{$get}{TIME} = $timenow; $hash->{READINGS}{SolarView_WR($hash, $get, $wr)}{TIME} = $timenow;
# #
push @{$hash->{CHANGED}}, "$get: $readings{$get}"; push @{$hash->{CHANGED}}, sprintf("%s: %s (wr%s)", $get, $readings{$get}, $wr);
} }
} }
DoTrigger($hash->{NAME}, undef) if ($init_done);
} alarm 0;
$success = 1;
} # res okay
} # socket okay
}; # eval
alarm 0;
} # wr loop
$hash->{STATE} = $hash->{READINGS}{currentPower}{VAL}.' W, '.$hash->{READINGS}{totalEnergyDay}{VAL}.' kWh'; $hash->{STATE} = $hash->{READINGS}{currentPower}{VAL}.' W, '.$hash->{READINGS}{totalEnergyDay}{VAL}.' kWh';
if ($success) { if ($success)
{
DoTrigger($hash->{NAME}, undef) if ($init_done);
Log 4, "$hash->{NAME} got fresh values from solarview"; Log 4, "$hash->{NAME} got fresh values from solarview";
} else { }
else
{
$hash->{STATE} .= ' (Fail)'; $hash->{STATE} .= ' (Fail)';
Log 4, "$hash->{NAME} was unable to get fresh values from solarview"; Log 4, "$hash->{NAME} was unable to get fresh values from solarview";
} }
@@ -292,23 +320,32 @@ SolarView_IsNight($)
my ($sec,$min,$hour,$mday,$mon) = localtime(time); my ($sec,$min,$hour,$mday,$mon) = localtime(time);
# reset totalEnergyX if needed # reset totalEnergyX at midnight
if ($hour == 0) if ($hour == 0)
{ {
my $timenow = TimeNow(); my $timenow = TimeNow();
$hash->{READINGS}{totalEnergyDay}{VAL} = 0; for my $wr (@{$hash->{WRs2Read}})
$hash->{READINGS}{totalEnergyDay}{TIME} = $timenow; {
$hash->{READINGS}{SolarView_WR($hash, 'totalEnergyDay', $wr)}{VAL} = 0;
$hash->{READINGS}{SolarView_WR($hash, 'totalEnergyDay', $wr)}{TIME} = $timenow;
}
# #
if ($mday == 1) if ($mday == 1)
{ {
$hash->{READINGS}{totalEnergyMonth}{VAL} = 0; for my $wr (@{$hash->{WRs2Read}})
$hash->{READINGS}{totalEnergyMonth}{TIME} = $timenow; {
$hash->{READINGS}{SolarView_WR($hash, 'totalEnergyMonth', $wr)}{VAL} = 0;
$hash->{READINGS}{SolarView_WR($hash, 'totalEnergyMonth', $wr)}{TIME} = $timenow;
}
# #
if ($mon == 0) if ($mon == 0)
{ {
$hash->{READINGS}{totalEnergyYear}{VAL} = 0; for my $wr (@{$hash->{WRs2Read}})
$hash->{READINGS}{totalEnergyYear}{TIME} = $timenow; {
$hash->{READINGS}{SolarView_WR($hash, 'totalEnergyYear', $wr)}{VAL} = 0;
$hash->{READINGS}{SolarView_WR($hash, 'totalEnergyYear', $wr)}{TIME} = $timenow;
}
} }
} }
} }
@@ -350,5 +387,21 @@ SolarView_IsNight($)
return $isNight; return $isNight;
} }
# prefix the reading name with inverter number
sub
SolarView_WR($$$)
{
my ($hash, $reading, $wr) = @_;
if ((int(@{$hash->{WRs2Read}}) > 1) && (int($wr) > 0))
{
return sprintf("wr%s_%s", $wr, $reading);
}
else
{
return $reading;
}
}
1; 1;