|
@ -5,20 +5,25 @@ use strict; |
|
|
use warnings; |
|
|
use warnings; |
|
|
use DevIo; # load DevIo.pm if not already loaded |
|
|
use DevIo; # load DevIo.pm if not already loaded |
|
|
|
|
|
|
|
|
# called upon loading the module MY_MODULE |
|
|
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(); |
|
|
sub AHOYUL_Initialize($) |
|
|
my $last_hour=0; |
|
|
{ |
|
|
|
|
|
my ($hash) = @_; |
|
|
|
|
|
|
|
|
|
|
|
$hash->{DefFn} = "ahoyUL_Define"; |
|
|
my %sets = ( |
|
|
$hash->{UndefFn} = "ahoyUL_Undef"; |
|
|
"a" => ":[0-9]+:", |
|
|
$hash->{SetFn} = "ahoyUL_Set"; |
|
|
"a" => ":[0-9]+:[0-9]{1}:[0-9]{12}:", #automode enable and configure |
|
|
$hash->{ReadFn} = "ahoyUL_Read"; |
|
|
"c" => "[0-9]+", |
|
|
$hash->{ReadyFn} = "ahoyUL_Ready"; |
|
|
"smac" => ":ch[0-9]{2}:[a-fA-F0-9]:rx[0-9]{2}:", #smac command (automode off) |
|
|
|
|
|
"s" => "", #automode of |
|
|
|
|
|
"d" => "0,1", #query decoding now |
|
|
|
|
|
"?" => "", |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
my %Inv = { "1141xxxxxxxx", }; |
|
|
|
|
|
my $yield_day = 0; #todo make per inverter |
|
|
|
|
|
my $yield_total = 0; |
|
|
|
|
|
|
|
|
$hash->{ParseFn} = "ahoyUL_Parse"; |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
# called when a new definition is created (by hand or from configuration read on FHEM startup) |
|
|
# called when a new definition is created (by hand or from configuration read on FHEM startup) |
|
|
sub ahoyUL_Define($$) |
|
|
sub ahoyUL_Define($$) |
|
@ -27,7 +32,6 @@ sub ahoyUL_Define($$) |
|
|
my @a = split("[ \t]+", $def); |
|
|
my @a = split("[ \t]+", $def); |
|
|
|
|
|
|
|
|
my $name = $a[0]; |
|
|
my $name = $a[0]; |
|
|
|
|
|
|
|
|
# $a[1] is always equals the module name "MY_MODULE" |
|
|
# $a[1] is always equals the module name "MY_MODULE" |
|
|
|
|
|
|
|
|
# first argument is a serial device (e.g. "/dev/ttyUSB0@57600,8,N,1") |
|
|
# first argument is a serial device (e.g. "/dev/ttyUSB0@57600,8,N,1") |
|
@ -37,36 +41,71 @@ sub ahoyUL_Define($$) |
|
|
|
|
|
|
|
|
# close connection if maybe open (on definition modify) |
|
|
# close connection if maybe open (on definition modify) |
|
|
DevIo_CloseDev($hash) if(DevIo_IsOpen($hash)); |
|
|
DevIo_CloseDev($hash) if(DevIo_IsOpen($hash)); |
|
|
|
|
|
|
|
|
# add a default baud rate (9600), if not given by user |
|
|
# add a default baud rate (9600), if not given by user |
|
|
$dev .= '@57600,8,N,1' if(not $dev =~ m/\@\d+$/); |
|
|
$dev .= '@57600,8,N,1' if(not $dev =~ m/\@\d+$/); |
|
|
|
|
|
|
|
|
# set the device to open |
|
|
# set the device to open |
|
|
$hash->{DeviceName} = $dev; |
|
|
$hash->{DeviceName} = $dev; |
|
|
|
|
|
|
|
|
# open connection with custom init function |
|
|
# open connection with custom init function |
|
|
my $ret = DevIo_OpenDev($hash, 0, "ahoyUL_Init"); |
|
|
my $ret = DevIo_OpenDev($hash, 0, "ahoyUL_Init"); |
|
|
|
|
|
|
|
|
|
|
|
# start periodic reading |
|
|
|
|
|
InternalTimer(gettimeofday()+15, "ahoyUL_GetUpdate", $hash); |
|
|
return undef; |
|
|
return undef; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
# called when definition is undefined |
|
|
|
|
|
# (config reload, shutdown or delete of definition) |
|
|
|
|
|
sub ahoyUL_Undef($$) |
|
|
|
|
|
{ |
|
|
|
|
|
my ($hash, $name) = @_; |
|
|
|
|
|
|
|
|
|
|
|
# close the connection |
|
|
|
|
|
DevIo_CloseDev($hash); |
|
|
# will be executed upon successful connection establishment (see DevIo_OpenDev()) |
|
|
|
|
|
sub ahoyUL_Init($) |
|
|
|
|
|
{ |
|
|
|
|
|
my ($hash) = @_; |
|
|
|
|
|
my $name = $hash->{NAME}; |
|
|
|
|
|
Log3 $name, 3, "ahoy device Init() called ..."; |
|
|
|
|
|
# send init to device, here e.g. enable automode to send DevInfoReq (0x15 ... 0x0B ....) every 120sec and enable simple decoding in ahoy-nano |
|
|
|
|
|
DevIo_SimpleWrite($hash, "a120:\nd1\r\n", 2); |
|
|
|
|
|
|
|
|
return undef; |
|
|
return undef; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# called upon loading the module MY_MODULE |
|
|
|
|
|
sub ahoyUL_Initialize($) |
|
|
|
|
|
{ |
|
|
|
|
|
my ($hash) = @_; |
|
|
|
|
|
|
|
|
|
|
|
$hash->{DefFn} = "ahoyUL_Define"; |
|
|
|
|
|
$hash->{UndefFn} = "ahoyUL_Undef"; |
|
|
|
|
|
$hash->{SetFn} = "ahoyUL_Set"; |
|
|
|
|
|
$hash->{ReadFn} = "ahoyUL_Read"; |
|
|
|
|
|
$hash->{ReadyFn} = "ahoyUL_Ready"; |
|
|
|
|
|
|
|
|
|
|
|
$hash->{ParseFn} = "ahoyUL_Parse"; |
|
|
|
|
|
$hash->{ParseFn} = "ahoyUL_GetUpdate"; #to be initialized in X_Define with a period |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sub ahoyUL_GetUpdate($) |
|
|
|
|
|
{ |
|
|
|
|
|
my ($hash) = @_; |
|
|
|
|
|
my $name = $hash->{NAME}; |
|
|
|
|
|
Log3 $name, 3, "ahoy_GetUpdate called ..."; |
|
|
|
|
|
|
|
|
|
|
|
# neuen Timer starten in einem konfigurierten Interval. |
|
|
|
|
|
#InternalTimer(gettimeofday()+$hash->{cmdInterval}, "ahoyUL_GetUpdate", $hash); |
|
|
|
|
|
InternalTimer(gettimeofday()+120, "ahoyUL_GetUpdate", $hash); |
|
|
|
|
|
|
|
|
|
|
|
#todo: call cmd sender method or do it right here |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# called repeatedly if device disappeared |
|
|
# called repeatedly if device disappeared |
|
|
sub ahoyUL_Ready($) |
|
|
sub ahoyUL_Ready($) |
|
|
{ |
|
|
{ |
|
|
my ($hash) = @_; |
|
|
my ($hash) = @_; |
|
|
|
|
|
my $name = $hash->{NAME}; |
|
|
|
|
|
Log3 $name, 3, "ahoyUL_Ready() called ..."; |
|
|
# try to reopen the connection in case the connection is lost |
|
|
# try to reopen the connection in case the connection is lost |
|
|
return DevIo_OpenDev($hash, 1, "ahoyUL_Init"); |
|
|
return DevIo_OpenDev($hash, 1, "ahoyUL_Init"); |
|
|
} |
|
|
} |
|
@ -103,29 +142,64 @@ sub ahoyUL_Parse($$$$) |
|
|
my ( $hash, $iohash, $name, $rmsg) = @_; |
|
|
my ( $hash, $iohash, $name, $rmsg) = @_; |
|
|
Log3 $name, 3, "ahoyUL: $rmsg"; |
|
|
Log3 $name, 3, "ahoyUL: $rmsg"; |
|
|
|
|
|
|
|
|
if($rmsg =~ m/rMAC/) { |
|
|
$last_hour = $hour; |
|
|
# handle rmac responses |
|
|
($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(); |
|
|
|
|
|
if($hour < $last_hour) { |
|
|
|
|
|
$yield_day = 0; |
|
|
|
|
|
$yield_total = 0; |
|
|
|
|
|
readingsSingleUpdate($hash, "yield_dc", "day $yield_day Wh total $yield_total kWh" , 1); |
|
|
|
|
|
}#end if |
|
|
|
|
|
|
|
|
|
|
|
if($rmsg =~ m/[rR]{1}MAC/) { |
|
|
|
|
|
# handle rmac responses |
|
|
|
|
|
$hash->{RMAC} = $rmsg; |
|
|
|
|
|
$hash->{RMAC_started} = 1; |
|
|
|
|
|
$hash->{RMAC_complete} = 0; |
|
|
|
|
|
|
|
|
|
|
|
} elsif ($hash->{RMAC_started}) { |
|
|
|
|
|
$hash->{RMAC} .= $rmsg; |
|
|
|
|
|
$hash->{RMAC_started} += 1; |
|
|
|
|
|
if($rmsg =~ m/OK|ERR/) { |
|
|
|
|
|
#end of rmac detected |
|
|
|
|
|
$hash->{RMAC_complete} = 1; |
|
|
|
|
|
$hash->{RMAC} =~ s/\r\n//g; |
|
|
|
|
|
$hash->{RMAC} .= "\n"; |
|
|
|
|
|
|
|
|
|
|
|
} elsif ($hash->{RMAC_started} > 10) { |
|
|
|
|
|
#stop rmac collection insufficiently if OK missed |
|
|
|
|
|
$hash->{RMAC_started} = 0; |
|
|
|
|
|
$hash->{RMAC_complete} = 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
} elsif($rmsg =~ m/payload/) { |
|
|
} elsif($rmsg =~ m/payload/) { |
|
|
# payload |
|
|
# user payload |
|
|
|
|
|
readingsSingleUpdate($hash, "Payload", $rmsg , 1); |
|
|
|
|
|
|
|
|
} elsif($rmsg =~ m/ch00/) { |
|
|
} elsif($rmsg =~ m/(ch0[0-4])/) { # regex match results to $1 |
|
|
# AC channel |
|
|
# decoded message from arduino |
|
|
readingsBeginUpdate($hash); |
|
|
readingsSingleUpdate($hash, "dec_$1", $rmsg , 1); |
|
|
readingsBulkUpdateIfChanged($hash, "myPV1", $rmsg , 1); |
|
|
if ($1 eq "ch00") { |
|
|
readingsEndUpdate($hash, 1); |
|
|
#end |
|
|
|
|
|
readingsSingleUpdate($hash, "yield_dc", "day $yield_day Wh total $yield_total kWh" , 1); |
|
|
|
|
|
$yield_day = 0; |
|
|
|
|
|
$yield_total = 0; |
|
|
|
|
|
} else { |
|
|
|
|
|
$rmsg =~ m/YieldDay: ([0-9\.]+).*YieldTotal: ([0-9\.]+)/; # regex match results to $1 and $2 |
|
|
|
|
|
$yield_day += $1; |
|
|
|
|
|
$yield_total += $2; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
} elsif($rmsg =~ m/ch0[1-4]/) { |
|
|
|
|
|
# one DC channel |
|
|
|
|
|
readingsBeginUpdate($hash); |
|
|
|
|
|
readingsBulkUpdateIfChanged($hash, "myPV1", $rmsg , 1); |
|
|
|
|
|
readingsEndUpdate($hash, 1); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
# do further rmac parsing hereafter |
|
|
|
|
|
if ($hash->{RMAC_complete}) { |
|
|
|
|
|
readingsSingleUpdate($hash, "MACresp", $hash->{RMAC} , 1); |
|
|
|
|
|
$hash->{RMAC_started} = 0; |
|
|
|
|
|
$hash->{RMAC_complete} = 0; |
|
|
|
|
|
#todo data valid check for OK or ERR |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
}# end X_Parse |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# called if set command is executed |
|
|
# called if set command is executed |
|
@ -134,32 +208,33 @@ sub ahoyUL_Set($$@) |
|
|
my ($hash, $name, @params) = @_; |
|
|
my ($hash, $name, @params) = @_; |
|
|
#my @a = split("[ \t]+", @params); |
|
|
#my @a = split("[ \t]+", @params); |
|
|
|
|
|
|
|
|
return "ahoyul_set needs at least a command" if(@params < 1); |
|
|
|
|
|
my $cmd = $params[0]; |
|
|
my $cmd = $params[0]; |
|
|
|
|
|
|
|
|
my $usage = "unknown argument $cmd, choose one of a:c:d:iadd:idel:ilst:sMAC:?"; |
|
|
return "unknown argument $cmd choose one of " . join(" ", sort keys %sets) |
|
|
|
|
|
if(@params < 2); |
|
|
|
|
|
|
|
|
|
|
|
my $usage = "should not come"; |
|
|
|
|
|
|
|
|
# get command overview from ahoy-nano device |
|
|
# get command overview from ahoy-nano device |
|
|
if($cmd eq "?") |
|
|
if($cmd eq "?") |
|
|
{ |
|
|
{ |
|
|
DevIo_SimpleWrite($hash, "$cmd\r\n", 2); |
|
|
DevIo_SimpleWrite($hash, "$cmd\n", 2); |
|
|
} |
|
|
} |
|
|
elsif($cmd eq "a") |
|
|
elsif($cmd eq "a") |
|
|
{ |
|
|
{ |
|
|
DevIo_SimpleWrite($hash, "a:{$params[1]}:{$params[2]}:{$params[3]}:\r\n", 2); |
|
|
DevIo_SimpleWrite($hash, "a:$params[1]:$params[2]:$params[3]:\n", 2); |
|
|
} |
|
|
} |
|
|
elsif($cmd eq "c") |
|
|
elsif($cmd eq "c") |
|
|
{ |
|
|
{ |
|
|
DevIo_SimpleWrite($hash, "c:{$params[1]}:\r\n", 2); |
|
|
DevIo_SimpleWrite($hash, "c$params[1]:\n", 2); |
|
|
} |
|
|
} |
|
|
elsif($cmd eq "d") |
|
|
elsif($cmd eq "d") |
|
|
{ |
|
|
{ |
|
|
DevIo_SimpleWrite($hash, "d:{$params[1]}:\r\n", 2); |
|
|
DevIo_SimpleWrite($hash, "d$params[1]:\n", 2); |
|
|
} |
|
|
} |
|
|
elsif($cmd eq "i") |
|
|
elsif($cmd eq "s") |
|
|
{ |
|
|
{ |
|
|
#todo |
|
|
DevIo_SimpleWrite($hash, "s\n", 2); |
|
|
#DevIo_SimpleWrite($hash, "off\r\n", 2); |
|
|
|
|
|
} |
|
|
} |
|
|
elsif($cmd eq "sMAC") |
|
|
elsif($cmd eq "sMAC") |
|
|
{ |
|
|
{ |
|
@ -170,16 +245,17 @@ sub ahoyUL_Set($$@) |
|
|
{ |
|
|
{ |
|
|
return $usage; |
|
|
return $usage; |
|
|
} |
|
|
} |
|
|
|
|
|
return undef; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
# will be executed upon successful connection establishment (see DevIo_OpenDev()) |
|
|
# called when definition is undefined |
|
|
sub ahoyUL_Init($) |
|
|
# (config reload, shutdown or delete of definition) |
|
|
|
|
|
sub ahoyUL_Undef($$) |
|
|
{ |
|
|
{ |
|
|
my ($hash) = @_; |
|
|
my ($hash, $name) = @_; |
|
|
|
|
|
|
|
|
# send init to device, here e.g. enable automode to send DevInfoReq (0x15 ... 0x0B ....) every 120sec and enable simple decoding in ahoy-nano |
|
|
|
|
|
DevIo_SimpleWrite($hash, "a120:::::d1:\r\n", 2); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# close the connection |
|
|
|
|
|
DevIo_CloseDev($hash); |
|
|
return undef; |
|
|
return undef; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|