diff --git a/fhem/contrib/70_SML.pm b/fhem/contrib/70_SML.pm new file mode 100644 index 000000000..dad9c1b58 --- /dev/null +++ b/fhem/contrib/70_SML.pm @@ -0,0 +1,215 @@ +############################################################################## +# +# 70_SML.pm +# +# a module to show smartmeter data +# +# written 2012 by Gabriel Bentele > +# +# $Id$ +# +# Version = 1.0 +# +############################################################################## +# +# define SML [ []] +# +# 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 300 (i.e. 5 minutes). +# +# get +# +# where is one of currentPower, intervalPower +############################################################################## + + +package main; +use IO::Socket::INET; + +my @gets = ('currentPower', # value now + 'intervalPower'); # value in the interval + +sub +SML_Initialize($) +{ +my ($hash) = @_; + + $hash->{DefFn} = "energy_Define"; + $hash->{UndefFn} = "energy_Undef"; + $hash->{GetFn} = "energy_Get"; + $hash->{AttrList} = "loglevel:0,1,2,3,4,5"; +} + +sub +energy_Define($$) +{ + my ($hash, $def) = @_; + + my @args = split("[ \t]+", $def); + + if (int(@args) < 4) + { + return "energy_Define: too few arguments. Usage:\n" . + "define SML [ []]"; + } + + $hash->{Host} = $args[2]; + $hash->{Port} = $args[3]; + $hash->{Interval} = int(@args) >= 5 ? int($args[4]) : 300; + $hash->{Timeout} = int(@args) >= 6 ? int($args[5]) : 4; + + Log 3, "$hash->{NAME} will read from SML at $hash->{Host}:$hash->{Port} " ; + # config variables + $hash->{Invalid} = -1; # default value for invalid readings + $hash->{Rereads} = 2; # number of retries when reading curPwr of 0 + $hash->{UseSVTime} = ''; # use the SV time as timestamp (else: TimeNow()) + + $hash->{STATE} = 'Initializing'; + + my $timenow = TimeNow(); + + for my $get (@gets) + { + $hash->{READINGS}{$get}{VAL} = $hash->{Invalid}; + $hash->{READINGS}{$get}{TIME} = $timenow; + } + + energy_Update($hash); + + Log 3, "$hash->{NAME} will read from SML at $hash->{Host}:$hash->{Port} " . + ($hash->{Interval} ? "every $hash->{Interval} seconds" : "for every 'get $hash->{NAME} ' request"); + + return undef; +} + +sub +energy_Update($) +{ + my ($hash) = @_; + + if ($hash->{Interval} > 0) { + InternalTimer(gettimeofday() + $hash->{Interval}, "energy_Update", $hash, 0); + } + + Log 3, "$hash->{NAME} tries to contact SML at $hash->{Host}:$hash->{Port}"; + + my $success = 0; + my %readings = (); + my $timenow = TimeNow(); + my $rereads = $hash->{Rereads}; + my $ip = $hash->{Host}; + my $port = $hash->{Port}; + my $interval = $hash->{Interval}; + my $timeout = $hash->{Timeout}; + my $counts = 0 ; + my $summary = 0 ; + my $url = "/InstantView/request/getPowerProfile.html?n=$interval\¶m=Wirkleistung\&format=1"; + my $socket ; + my $buf ; + my $message ; + my $tmp; + my @array; + +Log 4, "$url"; + +$socket = new IO::Socket::INET ( + PeerAddr => $ip, + PeerPort => $port, + Proto => 'tcp', + Reuse => 0, + Timeout => $timeout + ); + +Log 4, "socket new"; +if (defined ($socket) and $socket and $socket->connected()) +{ + Log 4, "Connected ..."; + print $socket "GET $url HTTP/1.0\r\n\r\n"; + $socket->autoflush(1); + while ((read $socket, $buf, 1024) > 0) + { + Log 5,"buf: $buf"; + $message .= $buf; + } + $socket->close(); + Log 4, "Socket closed"; + + @array = split(/\n/,$message); + foreach (@array){ + if ( $_ =~ /(.*)<\/v>/ ) + { + Log 5, "$hash->{NAME} got fresh values from $ip ($1)"; + $tmp = $1; + $counts++ ; + $summary += $1; + } + if ( $_ =~ /(.*)<\/error>/ ) + { + if ( $1 eq "true" ) + { + $success = 1; + Log 4, "$hash->{NAME} error from the $ip ($1)"; + } + } + } +}else{ + Log 3, "Cannot open socket ..."; + $success = 1; + return 0; +} + +Log 5, "reading done."; +if ( $success == 0 and $summary >= 0) +{ + $hash->{READINGS}{$gets[0]}{VAL} = $tmp; + $hash->{READINGS}{$gets[0]}{TIME} = $timenow; + push @{$hash->{CHANGED}}, "currentPower: $tmp W"; + $tmp = $summary/$counts; + $tmp =sprintf("%.2f",$tmp); + $hash->{READINGS}{$gets[1]}{VAL} = $tmp; + $hash->{READINGS}{$gets[1]}{TIME} = $timenow; + push @{$hash->{CHANGED}}, "intervalPower: $tmp W"; + + DoTrigger($hash->{NAME}, undef) if ($init_done); + $hash->{STATE} = $hash->{READINGS}{currentPower}{VAL}.' W, '.$hash->{READINGS}{intervalPower}{VAL}.' W'; +}else{ + Log 3, "$hash->{NAME} can't update - device send a error"; +} + +return undef; +} + +sub +energy_Get($@) +{ + +my ($hash, @args) = @_; + + return 'energy_Get needs two arguments' if (@args != 2); + +energy_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 "energy_Get: no such reading: $get"; + } + + Log 3, "$args[0] $get => $val"; + + return $val; +} + +sub +energy_Undef($$) +{ + my ($hash, $args) = @_; + + RemoveInternalTimer($hash) if $hash->{Interval}; + + return undef; +}