mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-01-31 18:59:33 +00:00
12faf0eca7
git-svn-id: https://svn.fhem.de/fhem/trunk@26263 2b470e98-0d58-463d-a4d8-8e2adae1ed80
373 lines
12 KiB
Perl
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;
|
|
|