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

Solarview: now also reading IDC*, UDC* and grid*

git-svn-id: https://svn.fhem.de/fhem/trunk@1213 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
to-be 2012-01-23 18:54:35 +00:00
parent d048872e99
commit ce77568c40

View File

@ -1,207 +1,252 @@
############################################################################## ##############################################################################
# #
# 70_SolarView.pm # 70_SolarView.pm
# #
# A FHEM module to read power/energy values from solarview. # A FHEM module to read power/energy values from solarview.
# #
# written 2012 by Tobe Toben <fhem@toben.net> # written 2012 by Tobe Toben <fhem@toben.net>
# #
# $Id$ # $Id$
# #
############################################################################## ##############################################################################
# #
# 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.amhamberg.de/solarview_fritzbox.aspx
# #
# 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> [<interval> [<timeout>]]
# #
# 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> # get <name> <key>
# #
# where <key> is one of currentPower, totalEnergy, totalEnergyDay, # where <key> is one of currentPower, totalEnergy, totalEnergyDay,
# totalEnergyMonth, totalEnergyYear and temperature. # totalEnergyMonth, totalEnergyYear, UDC, IDC, UDCB, IDCB, UDCC, IDCC,
# # gridVoltage, gridPower and temperature.
############################################################################## #
# ##############################################################################
# Copyright notice #
# # Copyright notice
# (c) 2012 Tobe Toben <fhem@toben.net> #
# # (c) 2012 Tobe Toben <fhem@toben.net>
# This script is free software; you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by # This script is free software; you can redistribute it and/or modify
# the Free Software Foundation; either version 2 of the License, or # it under the terms of the GNU General Public License as published by
# (at your option) any later version. # the Free Software Foundation; either version 2 of the License, or
# # (at your option) any later version.
# The GNU General Public License can be found at #
# http://www.gnu.org/copyleft/gpl.html. # The GNU General Public License can be found at
# # http://www.gnu.org/copyleft/gpl.html.
# This script is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of # This script is distributed in the hope that it will be useful,
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # but WITHOUT ANY WARRANTY; without even the implied warranty of
# GNU General Public License for more details. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# # GNU General Public License for more details.
# This copyright notice MUST APPEAR in all copies of the script! #
# # This copyright notice MUST APPEAR in all copies of the script!
############################################################################## #
##############################################################################
package main;
package main;
use strict;
use warnings; use strict;
use warnings;
use IO::Socket::INET;
use IO::Socket::INET;
sub
SolarView_Initialize($) my @gets = ('totalEnergyDay', # kWh
{ 'totalEnergyMonth', # kWh
my ($hash) = @_; 'totalEnergyYear', # kWh
'totalEnergy', # kWh
$hash->{DefFn} = "SolarView_Define"; 'currentPower', # W
$hash->{UndefFn} = "SolarView_Undef"; 'UDC', 'IDC', 'UDCB', # V, A, V
$hash->{GetFn} = "SolarView_Get"; 'IDCB', 'UDCC', 'IDCC', # A, V, A
$hash->{AttrList} = "loglevel:0,1,2,3,4,5"; 'gridVoltage', 'gridPower', # V, A
} 'temperature'); # °C
sub sub
SolarView_Define($$) SolarView_Initialize($)
{ {
my ($hash, $def) = @_; my ($hash) = @_;
my @args = split("[ \t]+", $def); $hash->{DefFn} = "SolarView_Define";
$hash->{UndefFn} = "SolarView_Undef";
if (@args < 4) $hash->{GetFn} = "SolarView_Get";
{ $hash->{AttrList} = "loglevel:0,1,2,3,4,5";
return "SolarView_Define: too few arguments. Usage:\n" . }
"define <name> SolarView <host> <port> [<interval> [<timeout>]]";
} sub
SolarView_Define($$)
$hash->{HOST} = $args[2]; {
$hash->{PORT} = $args[3]; my ($hash, $def) = @_;
$hash->{INTERVAL} = (@args>=5) ? int($args[4]) : 300;
$hash->{TIMEOUT} = (@args>=6) ? int($args[5]) : 4; my @args = split("[ \t]+", $def);
$hash->{INVALID} = -1;
if (@args < 4)
SolarView_Update($hash); {
return "SolarView_Define: too few arguments. Usage:\n" .
$hash->{STATE} = 'Initialized'; "define <name> SolarView <host> <port> [<interval> [<timeout>]]";
}
Log 2, "$hash->{NAME} will read power values from solarview at $hash->{HOST}:$hash->{PORT} " .
($hash->{INTERVAL} ? "every $hash->{INTERVAL} seconds" : "for every 'get $hash->{NAME} <key>' request"); $hash->{Host} = $args[2];
$hash->{Port} = $args[3];
return undef; $hash->{Interval} = (@args>=5) ? int($args[4]) : 300;
} $hash->{Timeout} = (@args>=6) ? int($args[5]) : 4;
sub $hash->{Invalid} = -1;
SolarView_Update($) $hash->{NightOff} = 1;
{ $hash->{Debounce} = 50;
my ($hash) = @_; $hash->{Sleep} = 0;
my $timenow = TimeNow(); $hash->{STATE} = 'Initializing';
my %gets = ('totalEnergy' => $hash->{INVALID}, my $timenow = TimeNow();
'totalEnergyDay' => $hash->{INVALID},
'totalEnergyMonth' => $hash->{INVALID}, for my $get (@gets)
'totalEnergyYear' => $hash->{INVALID}, {
'currentPower' => $hash->{INVALID}, $hash->{READINGS}{$get}{VAL} = $hash->{Invalid};
'temperature' => $hash->{INVALID},); $hash->{READINGS}{$get}{TIME} = $timenow;
}
if ($hash->{INTERVAL} > 0) {
InternalTimer(gettimeofday() + $hash->{INTERVAL}, "SolarView_Update", $hash, 0); SolarView_Update($hash);
}
Log 2, "$hash->{NAME} will read from solarview at $hash->{Host}:$hash->{Port} " .
Log 4, "$hash->{NAME} tries to connect solarview at $hash->{HOST}:$hash->{PORT}"; ($hash->{Interval} ? "every $hash->{Interval} seconds" : "for every 'get $hash->{NAME} <key>' request");
eval { return undef;
local $SIG{ALRM} = sub { die 'timeout'; }; }
alarm $hash->{TIMEOUT};
sub
my $socket = IO::Socket::INET->new(PeerAddr => $hash->{HOST}, SolarView_Update($)
PeerPort => $hash->{PORT}, {
Timeout => $hash->{TIMEOUT}); my ($hash) = @_;
if ($socket and $socket->connected()) if ($hash->{Interval} > 0) {
{ InternalTimer(gettimeofday() + $hash->{Interval}, "SolarView_Update", $hash, 0);
$socket->autoflush(1); }
print $socket "00*\r\n";
my $res = <$socket>; # if NightOff is set and there has been a successful
$timenow = TimeNow(); # reading before, then skip this update "at night"
close($socket); #
alarm 0; if ($hash->{NightOff} and $hash->{READINGS}{currentPower}{VAL} != $hash->{Invalid})
{
if ($res and $res =~ /^\{(00,.*)\},.+$/) my ($sec,$min,$hour) = localtime(time);
{ return undef if ($hour < 6 or $hour > 22);
my @vals = split(/,/, $1); }
$gets{'totalEnergyDay'} = 0 + $vals[6] if defined($vals[6]); sleep($hash->{Sleep}) if $hash->{Sleep};
$gets{'totalEnergyMonth'} = 0 + $vals[7] if defined($vals[7]);
$gets{'totalEnergyYear'} = 0 + $vals[8] if defined($vals[8]); Log 4, "$hash->{NAME} tries to connect solarview at $hash->{Host}:$hash->{Port}";
$gets{'totalEnergy'} = 0 + $vals[9] if defined($vals[9]);
$gets{'currentPower'} = 0 + $vals[10] if defined($vals[10]); my $success = 0;
$gets{'temperature'} = 0 + $vals[19] if defined($vals[19]);
} eval {
} local $SIG{ALRM} = sub { die 'timeout'; };
}; alarm $hash->{Timeout};
alarm 0; my $socket = IO::Socket::INET->new(PeerAddr => $hash->{Host},
PeerPort => $hash->{Port},
if ($gets{'currentPower'} != $hash->{INVALID}) { Timeout => $hash->{Timeout});
Log 4, "$hash->{NAME} got fresh values from solarview, currentPower: $gets{'currentPower'}";
} else { if ($socket and $socket->connected())
Log 4, "$hash->{NAME} was unable to get fresh values from solarview"; {
} $socket->autoflush(1);
print $socket "00*\r\n";
while ( my ($key,$val) = each(%gets) ) my $res = <$socket>;
{ close($socket);
$hash->{READINGS}{$key}{VAL} = $val;
$hash->{READINGS}{$key}{TIME} = $timenow; alarm 0;
Log 5, "$hash->{NAME} $key => $gets{$key}"; if ($res and $res =~ /^\{(00,[\d\.,]+)\},/)
} {
my @vals = split(/,/, $1);
return undef;
} my $tn = sprintf("%04d-%02d-%02d %02d:%02d:00",
$vals[3], $vals[2], $vals[1], $vals[4], $vals[5]);
sub
SolarView_Get($@) my $cpVal = $hash->{READINGS}{currentPower}{VAL};
{ my $cpTime = $hash->{READINGS}{currentPower}{TIME};
my ($hash, @args) = @_;
for my $i (6..19)
return 'SolarView_Get needs two arguments' if (@args != 2); {
my $getIdx = $i-6;
SolarView_Update($hash) unless $hash->{INTERVAL};
if (defined($vals[$i]))
my $get = $args[1]; {
my $val = $hash->{INVALID}; $hash->{READINGS}{$gets[$getIdx]}{VAL} = 0 + $vals[$i];
$hash->{READINGS}{$gets[$getIdx]}{TIME} = $tn;
if (defined($hash->{READINGS}{$get})) { }
$val = $hash->{READINGS}{$get}{VAL}; }
} else {
return "SolarView_Get: no such reading: $get"; # if Debounce is enabled, then skip one drop of
} # currentPower from 'greater than Debounce' to 'Zero'
#
Log 3, "$args[0] $get => $val"; if ($hash->{Debounce} > 0 and
$hash->{Debounce} < $cpVal and
return $val; $hash->{READINGS}{currentPower}{VAL} == 0)
} {
$hash->{READINGS}{currentPower}{VAL} = $cpVal;
sub $hash->{READINGS}{currentPower}{TIME} = $cpTime;
SolarView_Undef($$) }
{
my ($hash, $args) = @_; $success = 1;
}
RemoveInternalTimer($hash) if $hash->{INTERVAL}; }
};
return undef;
} alarm 0;
1; if ($success)
{
$hash->{STATE} = 'Initialized';
Log 4, "$hash->{NAME} got fresh values from solarview";
} else {
$hash->{STATE} = 'Failure';
Log 4, "$hash->{NAME} was unable to get fresh values from solarview";
}
return undef;
}
sub
SolarView_Get($@)
{
my ($hash, @args) = @_;
return 'SolarView_Get needs two arguments' if (@args != 2);
SolarView_Update($hash) unless $hash->{Interval};
my $get = $args[1];
my $val = $hash->{Invalid};
if (defined($hash->{READINGS}{$get})) {
$val = $hash->{READINGS}{$get}{VAL};
} else {
return "SolarView_Get: no such reading: $get";
}
Log 3, "$args[0] $get => $val";
return $val;
}
sub
SolarView_Undef($$)
{
my ($hash, $args) = @_;
RemoveInternalTimer($hash) if $hash->{Interval};
return undef;
}
1;