diff --git a/fhem/contrib/70_SolarView.pm b/fhem/contrib/70_SolarView.pm new file mode 100644 index 000000000..8e51c870c --- /dev/null +++ b/fhem/contrib/70_SolarView.pm @@ -0,0 +1,207 @@ +############################################################################## +# +# 70_SolarView.pm +# +# A FHEM module to read the current power/energy values from solarview. +# +# written 2012 by Tobe Toben +# +# $Id$ +# +############################################################################## +# +# 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: +# http://www.amhamberg.de/solarlogger.aspx +# +# 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 +# "-TCP " to the startscript (see the SV manual). +# +# usage: +# define SolarView [ []] +# +# If is positive, new values are read every seconds. +# If is 0, new values are read whenever a get request is called +# on . The default for is 3600. +# +# get +# +# where is one of currentPower, totalEnergy, totalEnergyDay, +# totalEnergyMonth, totalEnergyYear and temperature. +# +############################################################################## +# +# Copyright notice +# +# (c) 2012 Tobe Toben +# +# 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 +# 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. +# +# This script is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# 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! +# +############################################################################## + +package main; + +use strict; +use warnings; + +use IO::Socket::INET; + +sub +SolarView_Initialize($) +{ + my ($hash) = @_; + + $hash->{DefFn} = "SolarView_Define"; + $hash->{UndefFn} = "SolarView_Undef"; + $hash->{GetFn} = "SolarView_Get"; + $hash->{AttrList} = "loglevel:0,1,2,3,4,5"; +} + +sub +SolarView_Define($$) +{ + my ($hash, $def) = @_; + + my @args = split("[ \t]+", $def); + + if (@args < 4) + { + return "SolarView_Define: too few arguments. Usage:\n" . + "define SolarView [ []]"; + } + + $hash->{HOST} = $args[2]; + $hash->{PORT} = $args[3]; + $hash->{INTERVAL} = (@args>=5) ? int($args[4]) : 3600; + $hash->{TIMEOUT} = (@args>=6) ? int($args[5]) : 4; + $hash->{INVALID} = -1; + + SolarView_Update($hash); + + $hash->{STATE} = 'Initialized'; + + Log 1, "$hash->{NAME} will read power values from solarview at $hash->{HOST}:$hash->{PORT} " . + ($hash->{INTERVAL} ? "every $hash->{INTERVAL} seconds" : "for every 'get $hash->{NAME} ' request"); + + return undef; +} + +sub +SolarView_Update($) +{ + my ($hash) = @_; + + my $timenow = TimeNow(); + + my %gets = ('totalEnergy' => $hash->{INVALID}, + 'totalEnergyDay' => $hash->{INVALID}, + 'totalEnergyMonth' => $hash->{INVALID}, + 'totalEnergyYear' => $hash->{INVALID}, + 'currentPower' => $hash->{INVALID}, + 'temperature' => $hash->{INVALID},); + + if ($hash->{INTERVAL} > 0) { + InternalTimer(gettimeofday() + $hash->{INTERVAL}, "SolarView_Update", $hash, 0); + } + + Log 3, "$hash->{NAME} tries to connect solarview at $hash->{HOST}:$hash->{PORT}"; + + eval { + local $SIG{ALRM} = sub { die 'timeout'; }; + alarm $hash->{TIMEOUT}; + + my $socket = IO::Socket::INET->new(PeerAddr => $hash->{HOST}, + PeerPort => $hash->{PORT}, + Timeout => $hash->{TIMEOUT}); + + if ($socket and $socket->connected()) + { + $socket->autoflush(1); + print $socket "00*\r\n"; + my $res = <$socket>; + $timenow = TimeNow(); + close($socket); + alarm 0; + + if ($res and $res =~ /^\{(00,.*)\},.+$/) + { + my @vals = split(/,/, $1); + + $gets{'totalEnergyDay'} = 0 + $vals[6] if defined($vals[6]); + $gets{'totalEnergyMonth'} = 0 + $vals[7] if defined($vals[7]); + $gets{'totalEnergyYear'} = 0 + $vals[8] if defined($vals[8]); + $gets{'totalEnergy'} = 0 + $vals[9] if defined($vals[9]); + $gets{'currentPower'} = 0 + $vals[10] if defined($vals[10]); + $gets{'temperature'} = 0 + $vals[19] if defined($vals[19]); + } + } + }; + + alarm 0; + + if ($gets{'currentPower'} != $hash->{INVALID}) { + Log 3, "$hash->{NAME} got fresh values from solarview, currentPower: $gets{'currentPower'}"; + } else { + Log 3, "$hash->{NAME} was unable to get fresh values from solarview"; + } + + while ( my ($key,$val) = each(%gets) ) + { + $hash->{READINGS}{$key}{VAL} = $val; + $hash->{READINGS}{$key}{TIME} = $timenow; + + Log 4, "$hash->{NAME} $key => $gets{$key}"; + } + + 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 2, "$args[0] $get => $val"; + + return "$args[0] $get => $val"; +} + +sub +SolarView_Undef($$) +{ + my ($hash, $args) = @_; + + RemoveInternalTimer($hash) if $hash->{INTERVAL}; + + return undef; +} + +1; +