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:
parent
d048872e99
commit
ce77568c40
@ -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;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user