From 96205e9d4f427afc04ab66135f3d1ff41f2f0546 Mon Sep 17 00:00:00 2001 From: nasseeder1 Date: Tue, 4 Dec 2018 14:16:55 +0000 Subject: [PATCH] 98_SMAUtils: contrib V2.2 git-svn-id: https://svn.fhem.de/fhem/trunk@17893 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/contrib/DS_Starter/98_SMAUtils.pm | 348 +++++++++++++++++++++++++ 1 file changed, 348 insertions(+) create mode 100644 fhem/contrib/DS_Starter/98_SMAUtils.pm diff --git a/fhem/contrib/DS_Starter/98_SMAUtils.pm b/fhem/contrib/DS_Starter/98_SMAUtils.pm new file mode 100644 index 000000000..80de32ef4 --- /dev/null +++ b/fhem/contrib/DS_Starter/98_SMAUtils.pm @@ -0,0 +1,348 @@ +################################################################# +# +# A module to plot Inverterdata from SMA - Solar Technology +# +# written 2013 by Sven Köthe +# +# The modul is based on SBFSpot - Linux Tool +# +# Projectpage: https://code.google.com/p/sma-spot/ +# +################################################################# +# Definition: define SMAUtils
+# Parameters: +# address - Specify the BT/IP-Address of your Inverter +# interval - Specify the delay time for update the readings +# +################################################################# +# Versions History +# +# 2.2 16.09.2016 SMAUtils_Attr added +# 2.1 16.09.2016 sub Define changed +# 2.0 06.09.2016 attribute mode + + +package main; + +use strict; +use warnings; +use MIME::Base64; + + +sub +SMAUtils_Initialize($) +{ + my ($hash) = @_; + + $hash->{DefFn} = "SMAUtils_Define"; + $hash->{GetFn} = "SMAUtils_Get"; + $hash->{UndefFn} = "SMAUtils_Undef"; + $hash->{AttrFn} = "SMAUtils_Attr"; + $hash->{AttrList} = + "timeout ". + "mode:manual,automatic ". + "disable:0,1 ". + $readingFnAttributes; +} + +################################### + +sub +SMAUtils_Define($$) +{ + my ($hash, $def) = @_; + my $name=$hash->{NAME}; + + my @a = split("[ \t][ \t]*", $def); + + if(int(@a) < 3) { + return "You need to specify more parameters.\n". "Format: define SMAUtils
"; + } + + my $address = $a[2]; + $hash->{helper}{interval} = $a[3]; + + unless(($address =~ /^\s*([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}\s*$/) || ($address=~/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ &&(($1<=255 && $2<=255 && $3<=255 &&$4<=255 )))) { + return "given address is not a valid bluetooth or IP address"; + } + else + { + $hash->{ADDRESS} = $address; + } + +delete($hash->{helper}{RUNNING_PID}) if(defined($hash->{helper}{RUNNING_PID})); +RemoveInternalTimer($hash); + +if (AttrVal($name,"mode","automatic") eq "automatic") { + # "0" ist Aufrufmode + InternalTimer(gettimeofday()+$hash->{helper}{interval}, "Inverter_CallData", $hash, 0); +} + +return; +} + +############################## +sub +SMAUtils_Get($$) { + my ($hash, @a) = @_; + return "\"get X\" needs at least an argument" if ( @a < 2 ); + my $name = shift @a; + my $opt = shift @a; + + my $getlist = "Unknown argument $opt, choose one of ". + "data:noArg "; + + return "module is deactivated" if(IsDisabled($name)); + + if ($opt eq "data") { + # "1" ist Statusbit für manuelle Abfrage + Inverter_CallData($hash,1); + } else { + return "$getlist"; + } + +} + +##################################### +sub SMAUtils_Attr { + my ($cmd,$name,$aName,$aVal) = @_; + # $cmd can be "del" or "set" + # $name is device name + # aName and aVal are Attribute name and value + my $hash = $defs{$name}; + if ($aName eq "mode") { + readingsSingleUpdate($hash, "state", $aVal, 1); + } +return undef; +} + +##################################### +sub SMAUtils_Undef($$) { + my ($hash, $arg) = @_; + + RemoveInternalTimer($hash); + + BlockingKill($hash->{helper}{RUNNING_PID}) if(defined($hash->{helper}{RUNNING_PID})); + +return undef; +} + + +################################################################################################################################# +## Hauptschleife BlockingCall +################################################################################################################################# + +sub Inverter_CallData($$) { + my ($hash, $mode) = @_; + my $name = $hash->{NAME}; + my $timeout = AttrVal($name, "timeout", 30); + + if (!AttrVal($name, "disable", undef)) { + if (exists($hash->{helper}{RUNNING_PID})) { + Log3 $name, 1, "$hash->{NAME} - Error: another BlockingCall is already running, can't start BlockingCall Inverter_CallData"; + } + else + { + $hash->{helper}{RUNNING_PID} = BlockingCall("Get_Inverterdata", $name, "Parse_Inverterdata", $timeout, "ParseInverterdata_Aborted", $hash); + } + } + else + { + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, "state", "disabled"); + readingsBulkUpdate($hash, "summary", "disabled"); + readingsEndUpdate($hash, 1); + + Log3 $name, 5, "$hash->{NAME} - is currently disabled"; + } + +if(AttrVal($name,"mode","automatic") eq "automatic") { + RemoveInternalTimer($hash, "Inverter_CallData"); + InternalTimer(gettimeofday()+$hash->{helper}{interval}, "Inverter_CallData", $hash, 0); +} + +return; +} + + +################################################################################################################################# +## Abruf Inverterdaten mit SBFSpot +################################################################################################################################# +sub Get_Inverterdata { + my ($name) = @_; + my $hash = $defs{$name}; + my $data = ""; + + Log3 $name, 4, "$hash->{NAME} start BlockingCall Get_Inverterdata"; + + $data .= qx( /usr/local/bin/sbfspot.3/SBFspot -nocsv -nosql -v ); + + # $data = "das ist ein test"; + + Log3 $name, 5, "$hash->{NAME} BlockingCall Get_Inverterdata Data returned:"."\n"."$data"; + + # Daten müssen als Einzeiler zurückgegeben werden + my $dataenc = encode_base64($data,""); + + + Log3 $name, 5, "$hash->{NAME} BlockingCall Get_Inverterdata Data Base64: $dataenc"; + + Log3 $name, 4, "$hash->{NAME} BlockingCall Get_Inverterdata finished"; + +return "$name|$dataenc"; +} + + +################################################################################################################################# +## Auswerten Inverterdaten und Setzen Readings +################################################################################################################################# +sub Parse_Inverterdata($) { + my ($string) = @_; + my @a = split("\\|",$string); + + my $hash = $defs{$a[0]}; + my $name = $hash->{NAME}; + my $response = decode_base64($a[1]); + + Log3 $hash->{NAME}, 4, "$hash->{NAME} start BlockingCall Parse_Inverterdata"; + # Log3 $name, 5, "$hash->{NAME} BlockingCall Parse_Inverterdata received Data to parse:"."\n"."$response"; + + my $err_log=''; + my %pos; + + # abgerufene Inverterdaten auswerten + my @lines = split /\n/, $response; + + + my $readingsname = ''; + my $readingsvalue = ''; + my $value = ''; + my $substr = ''; + +readingsBeginUpdate($hash); + + my $end = 0; + my $i = 0; + foreach my $line (@lines) { + # von 5 auf 16 hochgesetzt da neue Version von SMAspot mehr Headerzeilen + if($i > 16 && $end != 1) { + $pos{$line} = index($response, $line); + my @reading = split(":",$line,2); + + $readingsname = ltrim($reading[0]); + $readingsname = rtrim($readingsname); + $readingsname = lc($readingsname); + $readingsname =~ s/ /_/g; + + $readingsvalue = ltrim($reading[1]); + + $substr = 'freq'; + + if (index($readingsname, $substr) != -1) { + $value = ltrim($reading[1]); + $value =~ /(\d+(?:\.\d+)?)/; + $readingsvalue = $1; + } + + $substr = 'phase'; + + if (index($readingsname, $substr) != -1) { + my $linesreading= substr $readingsname, 0,7; + my $linesvalue= substr $line, 8; + my @line_readings = split("-",$linesvalue); + + foreach my $line_readings (@line_readings) { + @reading = split(":",$line_readings); + $readingsname = ltrim($reading[0]); + $readingsname = $linesreading."_".$readingsname; + $readingsname = rtrim($readingsname); + $readingsname = lc($readingsname); + $readingsname =~ s/ /_/g; + + $value = ltrim($reading[1]); + $value =~ /(\d+(?:\.\d+)?)/; + $readingsvalue = $1; + + readingsBulkUpdate($hash,$readingsname,$readingsvalue); + } + } + + $substr = 'total'; + + if (index($readingsname, $substr) != -1) { + $value = ltrim($reading[1]); + $value =~ /(\d+(?:\.\d+)?)/; + $readingsvalue = $1; + } + + $substr = 'today'; + + if (index($readingsname, $substr) != -1) { + $value = ltrim($reading[1]); + $value =~ /(\d+(?:\.\d+)?)/; + $readingsvalue = $1; + } + + $substr = 'string'; + + if (index($readingsname, $substr) != -1) { + my $linesreading= substr $readingsname, 0,8; + my $linesvalue= substr $line, 9; + my @line_readings = split("-",$linesvalue); + + foreach my $line_readings (@line_readings) { + @reading = split(":",$line_readings); + $readingsname = ltrim($reading[0]); + $readingsname = $linesreading."_".$readingsname; + $readingsname = rtrim($readingsname); + $readingsname = lc($readingsname); + $readingsname =~ s/ /_/g; + + $value = ltrim($reading[1]); + $value =~ /(\d+(?:\.\d+)?)/; + $readingsvalue = $1; + + readingsBulkUpdate($hash,$readingsname,$readingsvalue); + } + } + + $substr = 'starttime'; + + if (index($readingsname, $substr) == -1) { + readingsBulkUpdate($hash,$readingsname,$readingsvalue); + } + + $substr = 'Sleep'; + + if (index($line, $substr) != -1) { + $end = 1; + } + } + + $i++; + } +my $mode = (AttrVal($name,"mode","automatic") eq "automatic")?"automatic":"manual"; +readingsBulkUpdate($hash, "state", $mode); +readingsBulkUpdate($hash, "summary", "enabled"); +readingsEndUpdate($hash, 1); + + +Log3 $hash->{NAME}, 4, "$hash->{NAME} BlockingCall Parse_Inverterdata finished"; +delete($hash->{helper}{RUNNING_PID}); + +return; +} + +################################################################################################################################# +## Timeout BlockingCall +################################################################################################################################# + +sub ParseInverterdata_Aborted($) { + my ($hash) = @_; + + Log3 $hash, 1, "$hash->{NAME} -> BlockingCall Get_Inverterdata timed out"; + delete($hash->{helper}{RUNNING_PID}); +} + +1; +