2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 18:59:33 +00:00
fhem-mirror/fhem/contrib/DS_Starter/98_SMAUtils.pm
nasseeder1 12faf0eca7 98_SMAUtils: contrib 2.4
git-svn-id: https://svn.fhem.de/fhem/trunk@26263 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2022-07-27 12:33:05 +00:00

373 lines
12 KiB
Perl

#################################################################
#
# A module to plot Inverterdata from SMA - Solar Technology
#
# written 2013 by Sven Köthe <sven at koethe-privat dot de>
#
# The modul is based on SBFSpot - Linux Tool
#
# Projectpage: https://code.google.com/p/sma-spot/
#
#################################################################
# Definition: define <name> SMAUtils <address> <interval>
# Parameters:
# address - Specify the BT/IP-Address of your Inverter
# interval - Specify the delay time for update the readings
#
#################################################################
# Versions History
#
# 2.4 27.07.2022 Forum: https://forum.fhem.de/index.php/topic,14624.msg1229389.html#msg1229389
# 2.3 08.04.2022 SBFspot V3.9.4 support added by Obi-Wan
# 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;
use Blocking;
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 <name> SMAUtils <address> <interval>";
}
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+)?)/; # Forum: https://forum.fhem.de/index.php/topic,14624.msg1229389.html#msg1229389
$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 = 'mppt';
if (index($readingsname, $substr) != -1) {
my $linesreading= "string_".(substr $readingsname, 5,1);
my $linesvalue= substr $line, 7;
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;