diff --git a/contrib/98_GAEBUS.pm b/contrib/98_GAEBUS.pm deleted file mode 100644 index de60dcd71..000000000 --- a/contrib/98_GAEBUS.pm +++ /dev/null @@ -1,1241 +0,0 @@ -############################################# -# $Id: 98_GAEBUS.pm 1 2015-07-03 00:00:00 Andreas Goebel $ -# derived from 00_TUL.pm -# -# 17.07.2015 : A.Goebel : initiale Version mit loop, readingname via attribut, keine writes -# 21.07.2015 : A.Goebel : start implementation for "set .. write" -# 23.07.2015 : A.Goebel : event-on-change-reading added to attributes -# 08.09.2015 : A.Goebel : limit number of socket-open retries in GetUpdates loop -# 10.09.2015 : A.Goebel : fix html code of commandref -# 11.09.2015 : A.Goebel : add attribute "ebusWritesEnable:0,1" -# 11.09.2015 : A.Goebel : add set w~ commands to set attributes for writing -# 11.09.2015 : A.Goebel : add set command to write to ebusd -# 13.09.2015 : A.Goebel : increase timeout for reads from ebusd from 1.8 to 5.0 -# 14.09.2015 : A.Goebel : use utf-8 coding to display values from ".csv" files -# 14.09.2015 : A.Goebel : add optional parameter [FIELD[.N]] of read from ebusd to reading name -# 15.09.2015 : A.Goebel : get rid of perl warnings when attribute value is empty -# 16.09.2015 : A.Goebel : allow write to parameters protected by #install -# 21.09.2015 : A.Goebel : implement BlockingCall Interface -# 07.10.2015 : A.Goebel : beautify and complete commandref -# 12.10.2015 : A.Goebel : fix handling of timeouts in BlockingCall Interface (additional parameter in doEbusCmd forces restart (no shutdown restart)) -# timeout for reads increased -# 19.10.2015 : A.Goebel : add attribute disable to disable loop to collect readings -# 05.11.2015 : A.Goebel : add support for "h" (broadcast update) commands from csv, handle them equal to (r)ead -# 09.11.2015 : A.Goebel : add support for multiple readings generated from one command to ebusd -# ebusd may return a list of values like "52.0;43.0;8.000;41.0;45.0;error" -# defining a reading "VL;RL;dummy;VLWW;RLWW" will create redings VL, RL, VLWW and RLWW -# 04.12.2015 : A.Goebel : add event-min-interval to attributes -# 14.12.2015 : A.Goebel : add read possible commmands for ebusd using "find -f" instead of reading the ".csv" files directly ("get ebusd_find") -# 25.01.2016 : A.Goebel : fix duplicate log entries for readings -# 05.02.2016 : A.Goebel : add valueFormat attribute -# 18.08.2016 : A.Goebel : fix workarround for perl warning with keys of hash reference -# 30.08.2016 : A.Goebel : add reading "state_ebus" containing output from "state" of ebusd -# 16.09.2016 : A.Goebel : add reset "state_ebus" if ebus is not connected - -package main; - -use strict; -use warnings; -use Time::HiRes qw(gettimeofday); -use IO::Socket; -use IO::Select; -use Encode; -use Blocking; - -sub GAEBUS_Attr(@); - -sub GAEBUS_OpenDev($$); -sub GAEBUS_CloseDev($); -sub GAEBUS_Disconnected($); -sub GAEBUS_Shutdown($); - -sub GAEBUS_doEbusCmd($$$$$$$); -sub GAEBUS_GetUpdates($); - -sub GAEBUS_GetUpdatesDoit($); -sub GAEBUS_GetUpdatesDone($); -sub GAEBUS_GetUpdatesAborted($); - -sub GAEBUS_State($); - -my %gets = ( # Name, Data to send to the GAEBUS, Regexp for the answer -); - -my %sets = ( - #"reopen" => [] -); - -my %setsForWriting = (); - -my $allSetParams = ""; -my $allSetParamsForWriting = ""; -my $allGetParams = ""; -my $delimiter = "~"; - -my $attrsDefault = "do_not_notify:1,0 disable:1,0 dummy:1,0 showtime:1,0 loglevel:0,1,2,3,4,5,6 event-on-change-reading event-min-interval ebusWritesEnabled:0,1 valueFormat:textField-long"; -my %ebusCmd = (); - -##################################### -sub -GAEBUS_Initialize($) -{ - my ($hash) = @_; - -# Normal devices - $hash->{DefFn} = "GAEBUS_Define"; - $hash->{UndefFn} = "GAEBUS_Undef"; - $hash->{GetFn} = "GAEBUS_Get"; - $hash->{SetFn} = "GAEBUS_Set"; - #$hash->{StateFn} = "GAEBUS_SetState"; - $hash->{AttrFn} = "GAEBUS_Attr"; - $hash->{AttrList} = $attrsDefault; - $hash->{ShutdownFn} = "GAEBUS_Shutdown"; - - - %sets = ( "reopen" => [] ); - %gets = ( "ebusd_find" => [], "ebusd_info" => [] ); - %setsForWriting = ( ); - - GAEBUS_initParams($hash); - -} - -##################################### -sub -GAEBUS_initParams ($) -{ - my ($hash) = @_; - - # bulid set and get Params and store them in - # $allSetParams - # $allSetParamsForWriting - # $allGetParams - - $allSetParams = ""; - foreach my $setval (sort keys %sets) - { - Log3 ($hash, 4, "GAEBUS Initialize params for set: $setval"); - if ( (@{$sets{$setval}}) > 0) - { - $allSetParams .= $setval.":".join (",", @{$sets{$setval}})." "; - } - else - { - $allSetParams .= $setval." "; - } - #Log3 ($hash, 2, "GAEBUS Initialize: $setval:$allSetParams"); - } - - $allSetParamsForWriting = ""; - foreach my $setval (sort keys %setsForWriting) - { - Log3 ($hash, 4, "GAEBUS Initialize params for setsForWriting: $setval"); - if ( (@{$setsForWriting{$setval}}) > 0) - { - $allSetParamsForWriting .= $setval.":".join (",", @{$setsForWriting{$setval}})." "; - } - else - { - $allSetParamsForWriting .= $setval." "; - } - #Log3 ($hash, 2, "GAEBUS Initialize: $setval:$allSetParamsForWriting"); - } - - $allGetParams = ""; - foreach my $getval (sort keys %gets) - { - Log3 ($hash, 4, "GAEBUS Initialize params for get: $getval"); - if ( (@{$gets{$getval}}) > 0) - { - $allGetParams .= $getval.":".join (",", @{$gets{$getval}})." "; - } - else - { - $allGetParams .= $getval." "; - } - #Log3 ($hash, 2, "GAEBUS Initialize: $getval:$allGetParams"); - } -} - -##################################### -sub -GAEBUS_Define($$) -{ - my ($hash, $def) = @_; - my @a = split("[ \t][ \t]*", $def); - - if(@a < 3) { - my $msg = "wrong syntax: define GAEBUS [:] [interval]"; - Log (2, $msg); - return $msg; - } - - GAEBUS_CloseDev($hash); - - my $name = $a[0]; - my $devaddr = $a[2]; - my $interval = $a[3]; - - $hash->{DeviceName} = $hash->{NAME}; - $hash->{DeviceAddress} = $devaddr; - $hash->{Interval} = defined ($interval) ? int ($interval) : 150; - $hash->{UpdateCnt} = 0; - - my $ret = GAEBUS_OpenDev($hash, 0); - - RemoveInternalTimer($hash); - InternalTimer(gettimeofday()+10, "GAEBUS_GetUpdates", $hash, 0); - - return undef; -} - - -##################################### -sub -GAEBUS_Undef($$) -{ - my ($hash, $arg) = @_; - GAEBUS_CloseDev($hash); - - BlockingKill($hash->{helper}{RUNNING_PID}) if(defined($hash->{helper}{RUNNING_PID})); - - return undef; -} - -##################################### -sub GAEBUS_Shutdown($) -{ - my ($hash) = @_; - GAEBUS_CloseDev($hash); - return undef; -} - -##################################### -sub -GAEBUS_Set($@) -{ - my ($hash, @a) = @_; - - return "\"set GAEBUS\" needs at least one parameter" if(@a < 2); - - my $name = shift @a; - my $type = shift @a; - my $arg = join("", @a); - - #return "No $a[1] for dummies" if(IsDummy($name)); - - #Log3 ($hash, 3, "ebus1: reopen $name"); - - #Log3 ($hash, 2, "$name: set $arg $type invalid parameter"); - - if ($type eq "reopen") { - Log3 ($hash, 3, "ebus1: reopen"); - GAEBUS_CloseDev($hash); - GAEBUS_OpenDev($hash,0); - return undef; - } - - # handle commands defined in %sets - - if (defined ($sets{$type})) - { - unless (grep {$_ eq $arg} @{$sets{$type}}) - { - return "invalid parameter"; - } - - my $attrname = $type.$delimiter.$arg; - $attrname =~ s/\#install/install/; - - Log3 ($hash, 3, "$name: set $attrname"); - addToDevAttrList($name, $attrname); - $attr{$name}{$attrname} = "" unless (defined $attr{$name}{$attrname}); - - return undef; - } - - # - # extend possible parameters by the readings defined for writing in attributes - # - - - my %writings = (); - - my $actSetParams = "$allSetParams "; - - my $ebusWritesEnabled = (defined($attr{$name}{"ebusWritesEnabled"})) ? $attr{$name}{"ebusWritesEnabled"} : 0; - - if ($ebusWritesEnabled) { - $actSetParams .= "$allSetParams$allSetParamsForWriting " if ($ebusWritesEnabled); - - foreach my $oneattr (sort keys %{$attr{$name}}) - { - my $readingname = $attr{$name}{$oneattr}; - my $readingcmdname = $oneattr; - $readingname =~ s/ .*//; - $readingname =~ s/:.*//; - - # only for "w" commands - if ($oneattr =~ /^w.*$delimiter.*$delimiter.*$delimiter.*$/) - { - unless ($readingname =~ /^\s*$/ or $readingname eq "1") - { - $writings{$readingname} = $readingcmdname; - #Log3 ($name, 2, "$name SetParams $readingname"); - } - } - - #Log3 ($name, 4, "$name Set attr name $readingname"); - #Log3 ($name, 4, "$name Set attr cmd $readingcmdname"); - } - $actSetParams .= join (" ", sort keys %writings); - - - # handle write commands (which were defined above) - - if (defined ($setsForWriting{$type})) - { - unless (grep {$_ eq $arg} @{$setsForWriting{$type}}) - { - return "invalid parameter"; - } - - my $attrname = $type.$delimiter.$arg; - $attrname =~ s/\#install/install/; - - Log3 ($hash, 3, "$name: set $attrname"); - addToDevAttrList($name, $attrname); - $attr{$name}{$attrname} = "" unless (defined $attr{$name}{$attrname}); - - return undef; - } - - if (defined ($writings{$type})) - { - foreach my $oneattr (sort keys %{$attr{$name}}) - { - next unless ($oneattr =~ /^w.*$delimiter.*$delimiter.*$delimiter.*$/); - - my $readingname = $attr{$name}{$oneattr}; - next if ($readingname ne $type); - - my $answer = GAEBUS_doEbusCmd ($hash, "w", $readingname, $oneattr, $arg, "", 0); - return "$answer"; - } - } - - } - - - return "Unknown argument $type, choose one of " . $actSetParams - if(!defined($sets{$type})); - - return undef; -} - -##################################### -sub -GAEBUS_Get($@) -{ - my ($hash, @a) = @_; - my $type = $hash->{TYPE}; - my $name = $hash->{NAME}; - - my $arg = (defined($a[2]) ? $a[2] : ""); - my $rsp; - my $varname = $a[0]; - - - my $readingname = ""; - my $readingcmdname = ""; - - return "\"get $type\" needs at least one parameter" if(@a < 2); - - # extend possible parameters by the readings defined in attributes - - my %readings = (); - my %readingsCmdaddon = (); - my $actGetParams .= "$allGetParams reading:"; - foreach my $oneattr (sort keys %{$attr{$name}}) - { - my ($readingnameX, $cmdaddon) = split (" ", $attr{$name}{$oneattr}, 2); - $cmdaddon = "" unless (defined ($cmdaddon)); - - next unless defined ($readingnameX); - next if ($readingnameX =~ /^\s*$/); - next if ($readingnameX eq "1"); - - my ($readingname, $doCntNo) = split (":", $readingnameX, 2); # split name from cycle number - $doCntNo = 1 unless (defined ($doCntNo)); - - #my $readingname = $attr{$name}{$oneattr}; - my $readingcmdname = $oneattr; - $readingname =~ s/ .*//; - $readingname =~ s/:.*//; - - # only for "r" commands - if ($oneattr =~ /^[rh].*$delimiter.*$delimiter.*$delimiter.*$/) - { - $readings{$readingname} = $readingcmdname; - $readingsCmdaddon{$readingname} = $cmdaddon; - - #Log3 ($name, 2, "$name GetParams $readingname"); - } - - #Log3 ($name, 4, "$name Get attr name $readingname"); - #Log3 ($name, 4, "$name Get attr cmd $readingcmdname"); - } - $actGetParams .= join (",", sort keys %readings); - - - # handle "read" parameters and update Reading - - if ($a[1] eq "reading") - { - my $readingname = $a[2]; - my $readingcmdname = $readings{$readingname}; - my $cmdaddon = $readingsCmdaddon{$readingname}; - - Log3 ($name, 4, "$name Get name $readingname"); - Log3 ($name, 4, "$name Get cmd r $readingcmdname"); - - my $answer = GAEBUS_doEbusCmd ($hash, "r", $readingname, $readingcmdname, "", $cmdaddon, 0); - - #return "$answer"; - return undef; - - } - - if ($a[1] eq "ebusd_find") - { - Log3 ($name, 4, "$name Get $a[1]"); - - my $answer = GAEBUS_doEbusCmd ($hash, "f", "", "", "", "", 0); - - return $answer; - } - - if ($a[1] eq "ebusd_info") - { - Log3 ($name, 4, "$name Get $a[1]"); - - my $answer = GAEBUS_doEbusCmd ($hash, "i", "", "", "", "", 0); - - return $answer; - } - - # other read commands - - if ($a[1] =~ /^[rh]$delimiter/) - { - my $readingname = ""; - my $readingcmdname = $a[1].$delimiter.$a[2]; - - Log3 ($name, 3, "$name get cmd v $readingcmdname"); - - my $answer = GAEBUS_doEbusCmd ($hash, "v", $readingname, $readingcmdname, "", "", 0); - #return (defined($answer ? $answer : "")); - return "$answer"; - - } - - # handle commands from %gets and show result from ebusd - - - return "Unknown argument $a[1], choose one of " . $actGetParams - if(!defined($gets{$a[1]})); - - - #return "No $a[1] for dummies" if(IsDummy($varname)); - - return "nix"; -} - -##################################### -sub -GAEBUS_SetState($$$$) -{ - my ($hash, $tim, $vt, $val) = @_; - return undef; -} - -######################## -sub -GAEBUS_CloseDev($) -{ - my ($hash) = @_; - my $name = $hash->{NAME}; - my $dev = $hash->{DeviceName}; - - return if(!$dev); - - if($hash->{TCPDev}) { - $hash->{TCPDev}->close(); - delete($hash->{TCPDev}); - - } - - #delete($selectlist{"$name.$dev"}); - #delete($readyfnlist{"$name.$dev"}); - delete($hash->{FD}); - $hash->{STATE} = "closed"; - readingsSingleUpdate ($hash, "state_ebus", "unknown", 1); -} - -######################## -sub -GAEBUS_OpenDev($$) -{ - my ($hash, $reopen) = @_; - my $dev = $hash->{DeviceName}; - my $name = $hash->{NAME}; - - my $host = $hash->{DeviceAddress}; - my $port = 8888; - if($host =~ m/^(.+):(.+)$/) { # host[:port] - $host = $1; - $port = $2; - } - - $hash->{PARTIAL} = ""; - Log3 $hash, 3, "GAEBUS opening $name device $host($port)" - if($reopen == 0); - - # This part is called every time the timeout (5sec) is expired _OR_ - # somebody is communicating over another TCP connection. As the connect - # for non-existent devices has a delay of 3 sec, we are sitting all the - # time in this connect. NEXT_OPEN tries to avoid this problem. - - if($hash->{NEXT_OPEN} && time() < $hash->{NEXT_OPEN}) { - Log3 $hash, 5, "GAEBUS NEXT_OPEN prevented opening $name device $host($port)"; - return; - } - - my $conn = new IO::Socket::INET ( - PeerAddr => "$host", - PeerPort => '8888', - Proto => 'tcp', - Reuse => 0, - Timeout => 10 - ); - - if(defined ($conn)) { - delete($hash->{NEXT_OPEN}); - - - } else { - Log(3, "Can't connect to $dev: $!") if(!$reopen); - - #$readyfnlist{"$name.$dev"} = $hash; - - $hash->{STATE} = "disconnected"; - $hash->{NEXT_OPEN} = time()+60; - return ""; - } - - my $sel = new IO::Select($conn); - - $hash->{DevType} = 'EBUSD'; - $hash->{TCPDev} = $conn; - $hash->{FD} = $conn->fileno(); - $hash->{SELECTOR} = $sel; - - #delete($readyfnlist{"$name.$dev"}); - #$selectlist{"$name.$dev"} = $hash; - - if($reopen) { - Log3 $hash, 1, "GAEBUS $dev reappeared ($name)"; - } else { - Log3 $hash, 3, "GAEBUS device opened ($name)"; - } - - #$hash->{STATE}="Initialized"; - $hash->{STATE}="Connected"; - - DoTrigger($name, "CONNECTED") if($reopen); - return 0; -} - -sub -GAEBUS_Disconnected($) -{ - my $hash = shift; - my $dev = $hash->{DeviceName}; - my $name = $hash->{NAME}; - - return if(!defined($hash->{FD})); # Already deleted or RFR - - Log3 $hash, 1, "$dev disconnected, waiting to reappear"; - GAEBUS_CloseDev($hash); - #$readyfnlist{"$name.$dev"} = $hash; # Start polling - $hash->{STATE} = "disconnected"; - - # Without the following sleep the open of the device causes a SIGSEGV, - # and following opens block infinitely. Only a reboot helps. - sleep(5); - - DoTrigger($name, "DISCONNECTED"); -} - -sub -GAEBUS_Attr(@) -{ - my @a = @_; - my ($action, $name, $attrname, $attrval) = @a; - - my $hash = $defs{$name}; - - $attrval = "" unless defined ($attrval); - - if ($action eq "del") - { - my $userattr = $attr{$name}{userattr}; - #Log3 ($hash, 2, ">$userattr<>$attrname<"); - if ( " $userattr " =~ / $attrname / ) - { - #Log3 ($hash, 2, "match"); - # " a" or "^a$" - $userattr =~ s/ *$attrname//; - if ($userattr eq "") - { - delete($attr{$name}{userattr}); - } - else - { - $attr{$name}{userattr} = $userattr; - } - - } - - # delete reading if attribute name contains $delimiter - - if ($attrname =~ /^.*$delimiter/) { - my $reading = $attr{$name}{$attrname}; - $reading =~ s/ .*//; - $reading =~ s/:.*//; - - foreach my $r (split /;/, $reading) { - Log3 ($name, 3, "$name: delete reading: $reading"); - delete($defs{$name}{READINGS}{$reading}); - } - } - - if ($attrname eq "valueFormat" and defined ($hash->{helper}{$attrname})) { - delete ($hash->{helper}{$attrname}); - } - - return undef; - } - elsif ($action eq "set") - { - - if ($attrname eq "valueFormat") { - my $attrVal = $attrval; - if( $attrVal =~ m/^{.*}$/s && $attrVal =~ m/=>/ && $attrVal !~ m/\$/ ) { - my $av = eval $attrVal; - if( $@ ) { - Log3 ($hash->{NAME}, 3, $hash->{NAME} ." $attrname: ". $@); - } else { - $attrVal = $av if( ref($av) eq "HASH" ); - } - $hash->{helper}{$attrname} = $attrVal; - - foreach my $key (keys %{ $hash->{helper}{$attrname} }) { - Log3 ($hash->{NAME}, 3, $hash->{NAME} ." $key ".$hash->{helper}{$attrname}{$key}); - } - #return "set HERE??"; - - } else { - # if valueFormat is not verified sucessfully ... the helper is deleted (=not used) - delete $hash->{helper}{$attrname}; - } - return undef; - } - - if (defined $attr{$name}{$attrname}) - { - my $oldreading = $attr{$name}{$attrname}; - $oldreading =~ s/ .*//; - $oldreading =~ s/:.*//; - my $newreading = $attrval; - $newreading =~ s/ .*//; - $newreading =~ s/:.*//; - - my @or = split /;/, $oldreading; - my @nr = split /;/, $newreading; - - for (my $i; $i <= $#or; $i++) - { - if ($or[$i] ne $nr[$i]) - { - #Log3 ($name, 2, "$name: adjust reading: $or[$i]"); - - if (defined($defs{$name}{READINGS}{$or[$i]})) - { - if (defined ($nr[$i] and $nr[$i] ne "dummy" )) - { - unless ($nr[$i] =~ /^1*$/) # matches "1" or "" - { - #Log3 ($name, 2, "$name: change attribute $attrname ($or[$i] -> $nr[$i])"); - - $defs{$name}{READINGS}{$nr[$i]}{VAL} = $defs{$name}{READINGS}{$or[$i]}{VAL}; - $defs{$name}{READINGS}{$nr[$i]}{TIME} = $defs{$name}{READINGS}{$or[$i]}{TIME}; - } - } - - delete($defs{$name}{READINGS}{$or[$i]}); - - } - } - } - } - } - - Log3 (undef, 2, "called GAEBUS_Attr($a[0],$a[1],$a[2],<$a[3]>)"); - - return undef; -} - -sub -GAEBUS_State($) -{ - my $hash = shift; - my $name = $hash->{NAME}; - my $state = ""; - my $actMessage = ""; - - if (($hash->{STATE} eq "Connected") and ($hash->{TCPDev}->connected()) ) - { - - my $timeout = 10; - - syswrite ($hash->{TCPDev}, "state\n"); - if ($hash->{SELECTOR}->can_read($timeout)) - { - sysread ($hash->{TCPDev}, $actMessage, 4096); - $actMessage =~ s/\n$/ /g; - $actMessage =~ s/ {1,}$//; - if ($actMessage =~ /^signal acquired/) { - $state = "ok"; - } - - } - else - { - $state = "no answer"; - Log3 ($name, 2, "$name state $state ($actMessage)"); - } - - } - - return ($state, $actMessage); -} - -sub -GAEBUS_doEbusCmd($$$$$$$) -{ - my $hash = shift; - my $action = shift; # "r" = get reading, "v" = verbose mode, "w" = write to ebus, "f" = execute find to read in config, "i" = execute info - my $readingname = shift; - my $readingcmdname = shift; - my $writeValues = shift; - my $cmdaddon = shift; - my $inBlockingCall = shift; - my $actMessage = ""; - my $name = $hash->{NAME}; - - if (($hash->{STATE} ne "Connected") or (!$hash->{TCPDev}->connected()) ) - { - Log3 ($name, 2, "$name device closed. Try to reopen"); - GAEBUS_CloseDev($hash); - GAEBUS_OpenDev($hash,0); - - if ($hash->{STATE} ne "Connected") { - if ($inBlockingCall) { - return ""; - } - else { - return undef; - } - } - } - - #my $timeout = 1.8; - my $timeout = 15.0; - $timeout = 10.0 if ($action eq "v"); - $timeout = 10.0 if ($action eq "w"); - - - my ($io,$class,$var,$comment) = split ($delimiter, $readingcmdname, 4); - - my $cmd = ""; - - if ($action eq "w") { - - $class =~ s/install/#install/; - - $cmd = "$io "; - $cmd .= "-c $class $var "; - $cmd .= "$writeValues"; - - } elsif ($action eq "f") { - $cmd = "find -f -r -w"; - - } elsif ($action eq "i") { - $cmd = "info"; - - } else { - - $cmd = "$io "; - $cmd .= " -f " if ($io ne "h"); - $cmd .= "-v " if ($action eq "v"); - $cmd .= "-c $class $var"; - $cmd .= " $cmdaddon" if ($action eq "r"); - - $cmd =~ s/^h /r /; - } - - Log3 ($name, 3, "$name execute $cmd"); - - if ($hash->{SELECTOR}->can_read(0.1)) - { - sysread ($hash->{TCPDev}, $actMessage, 4096); - $actMessage =~ s/\n//g; - Log3 ($name, 2, "$name old answer $actMessage\n"); - $actMessage = ""; - } - - syswrite ($hash->{TCPDev}, $cmd."\n"); - if (0 and $hash->{SELECTOR}->can_read($timeout)) - { - #Log3 ($name, 2, "$name try to read"); - sysread ($hash->{TCPDev}, $actMessage, 4096); - $actMessage =~ s/\n//g; - #$actMessage =~ s/;/ /g; - } - - if ($hash->{SELECTOR}->can_read($timeout)) { - - my $rbuffer = ""; - - while ($hash->{SELECTOR}->can_read(0.1) and sysread ($hash->{TCPDev}, $rbuffer, 4096)) { - $actMessage .= $rbuffer; - - if ( $rbuffer =~ /\n\n$/ ) - { - Log3 ($name, 3, "$name answer terminated by empty line"); - last; - } - - } - #$actMessage =~ s/\n//g; - - - #Log3 ($name, 3, "$name answer $action $readingname $actMessage"); - - } - else - { - #return "timeout reading answer for ($readingname) $cmd"; - - Log3 ($name, 2, "$name: timeout reading answer for $cmd"); - - return ""; - } - - if ($action eq "f") - { - - %sets = ( "reopen" => [] ); - %gets = ( "ebusd_find" => [], "ebusd_info" => [] ); - %setsForWriting = ( ); - - my $cnt = 0; - foreach my $line (split /\n/, $actMessage) { - $cnt++; - - $line =~ s/ /_/g; # no blanks in names and comments - $line =~ s/$delimiter/_/g; # clean up the delimiter within the text - - Log3 ($name, 3, "$name $line"); - - #my ($io,$class,$var) = split (",", $line, 3); - my ($io, $class, $var, $comment, @params) = split (",", $line, 5); - - $io =~ s/[0-9]//g; - - # drop "memory" - - next if ($class eq "memory"); - next if ($class eq "scan"); - - - push @{$sets{$io.$delimiter.$class}}, $var.$delimiter.$comment if ($io eq "r" or $io eq "h"); - - push @{$setsForWriting{$io.$delimiter.$class}}, $var.$delimiter.$comment if ($io eq "w" or $io eq "wi"); - - push @{$gets{$io.$delimiter.$class}}, $var.$delimiter.$comment if ($io eq "r" or $io eq "h"); - - } - - GAEBUS_initParams($hash); - - Log3 ($name, 3, "$name find done."); - return "$cnt definitions processed"; - - } - if ($action eq "i") - { - #Log3 ($name, 3, "$name info done."); - return "$actMessage"; - - } - - $actMessage =~ s/\n//g; - Log3 ($name, 3, "$name answer $action $readingname $actMessage"); - - my @values = split /;/, $actMessage; - my @targetreading = defined ($readingname) ? split /;/, $readingname : (); - - if ($inBlockingCall) - { - $actMessage = ""; - - for (my $i=0; $i <= $#targetreading; $i++) - { - next if ($targetreading[$i] eq "dummy"); - my $v = defined($values[$i]) ? $values[$i] : ""; - - $actMessage .= $targetreading[$i]."|".$v."|"; - } - - $actMessage =~ s/\|$//; - - # readings will be updated in main fhem process - return $actMessage; - } - - if ($action eq "r") - { - readingsBeginUpdate ($hash); - for (my $i=0; $i <= $#targetreading; $i++) - { - next if ($targetreading[$i] eq "dummy"); - my $v = defined($values[$i]) ? $values[$i] : ""; - - $v = GAEBUS_valueFormat ($hash, $targetreading[$i], $v); - - readingsBulkUpdate ($hash, $targetreading[$i], $v); - } - readingsEndUpdate($hash, 1); - } - - - #if ($inBlockingCall) { - # $actMessage = $readingname."|".$actMessage; - #} - #else { - # - # if ($action eq "r") { - # if (defined ($readingname)) { - # # BlockingCall changes - # readingsSingleUpdate ($hash, $readingname, "$actMessage", 1); - # } - # } - #} - - - return $actMessage; - -} - -sub -GAEBUS_GetUpdates($) -{ - my ($hash) = @_; - - my $name = $hash->{NAME}; - - if (defined($attr{$name}{disable}) and ($attr{$name}{disable} == 1)) { - Log3 $hash, 4, "$name GetUpdates2 is disabled"; - - InternalTimer(gettimeofday()+$hash->{Interval}, "GAEBUS_GetUpdates", $hash, 0); - return; - - } else { - Log3 $hash, 4, "$name start GetUpdates2"; - - } - - $hash->{UpdateCnt} = $hash->{UpdateCnt} + 1; - - $hash->{helper}{RUNNING_PID} = BlockingCall("GAEBUS_GetUpdatesDoit", $name, "GAEBUS_GetUpdatesDone", 120, "GAEBUS_GetUpdatesAborted", $hash) - unless(exists($hash->{helper}{RUNNING_PID})); - -} - -sub -GAEBUS_GetUpdatesDoit($) -{ - my ($string) = (@_); - #my ($name, $nochwas) = split ("|", $string); - my ($name) = $string; - my ($hash) = $defs{$name}; - - my $readingname = ""; - my $tryOpenCnt = 2; # no of tries to open the device, before giving up - - my $readingsToUpdate = ""; - - # don't use socket inherited from fhem by BlockingCall.pm - delete($hash->{TCPDev}); - $hash->{STATE} = "pleaseReconnect"; - my $ret = GAEBUS_OpenDev($hash, 0); - if (($hash->{STATE} ne "Connected") or (!$hash->{TCPDev}->connected()) ) - { - return "$name"; - } - - # syncronize with ebusd - - my ($state, $actMessage) = GAEBUS_State($hash); - if ($state ne "ok") { - Log3 ($name, 2, "$name: ebusd no connection or signal state($state)"); - return "$name"; - } - Log3 ($name, 5, "$name: ebusd state($actMessage)"); - $actMessage =~ s/,.*//; - $readingsToUpdate .= "|state_ebus|".$actMessage; - - foreach my $oneattr (keys %{$attr{$name}}) - { - # only for "r" commands - if ($oneattr =~ /^[rh].*$delimiter.*$delimiter.*$delimiter.*$/) - { - - my ($readingnameX, $cmdaddon) = split (" ", $attr{$name}{$oneattr}, 2); - $cmdaddon = "" unless (defined ($cmdaddon)); - - next unless defined ($readingnameX); - next if ($readingnameX =~ /^\s*$/); - next if ($readingnameX eq "1"); - - my ($readingname, $doCntNo) = split (":", $readingnameX, 2); # split name from cycle number - $doCntNo = 1 unless (defined ($doCntNo)); - - Log3 ($name, 5, "$name GetUpdates: $readingname:$doCntNo"); - - #Log3 ($name, 2, "$name check modulo ".$hash->{UpdateCnt}." mod $doCntNo -> ".($hash->{UpdateCnt} % $doCntNo)); - if (($hash->{UpdateCnt} % $doCntNo) == 0) - { - $readingsToUpdate .= "|".GAEBUS_doEbusCmd ($hash, "r", $readingname, $oneattr, "", $cmdaddon, 1); - } - - # limit number of reopens if ebusd cannot be reached - if (($hash->{STATE} ne "Connected") or (!$hash->{TCPDev}->connected()) ) - { - if (--$tryOpenCnt <= 0) - { - Log3 ($name, 2, "$name: not connected, stop GetUpdates loop"); - last; - } - } - - } - } - - # returnvalue for BlockingCall ... done routine - return $name.$readingsToUpdate; - - -} - -sub -GAEBUS_GetUpdatesDone($) -{ - my ($string) = @_; - - return unless(defined($string)); - - my @a = split("\\|",$string); - my ($hash) = $defs{$a[0]}; - my $name = $hash->{NAME}; - - delete($hash->{helper}{RUNNING_PID}); - - #Log3 ($name, 2, "$name: GetUpdatesDoit returned $string"); - - readingsBeginUpdate ($hash); - for (my $i = 1; $i < $#a; $i = $i + 2) - { - my $v = GAEBUS_valueFormat ($hash, $$a[$i], $a[$i+1]); - #readingsBulkUpdate ($hash, $a[$i], $a[$i+1]); - readingsBulkUpdate ($hash, $a[$i], $v); - } - readingsEndUpdate($hash, 1); - - InternalTimer(gettimeofday()+$hash->{Interval}, "GAEBUS_GetUpdates", $hash, 0); - -} - - -sub -GAEBUS_GetUpdatesAborted($) -{ - my ($hash) = @_; - - delete($hash->{helper}{RUNNING_PID}); - - Log3 $hash->{NAME}, 3, "BlockingCall for ".$hash->{NAME}." was aborted"; - - RemoveInternalTimer($hash); - InternalTimer(gettimeofday()+$hash->{Interval}, "GAEBUS_GetUpdates", $hash, 0); -} - -sub -GAEBUS_valueFormat(@) -{ - my ($hash, $reading, $value) = @_; - - return $value unless (defined ($reading)); - - if (ref($hash->{helper}{valueFormat}) eq 'HASH') - { - - if (exists($hash->{helper}{valueFormat}->{$reading})) { - - my $vf = $hash->{helper}{valueFormat}->{$reading}; - return sprintf ("$vf", $value); - } - } - - return $value; - -} - - -1; - -=pod -=begin html - - -

GAEBUS

-
    - - - -
    - The GAEBUS module is the representation of a Ebus connector in FHEM. - The GAEBUS module is designed to connect to ebusd (ebus daemon) via a socket connection (default is port 8888)
    - -
    - - - Define -
      - define <name> GAEBUS <device-addr>[:<port>] [<interval>];
      -
      - <device-addr>[:<port>] specifies the host:port of the ebusd device. E.g. - 192.168.0.244:8888 or servername:8888. When using the standard port, the port can be omitted. -

      - - Example:

      - define ebus1 GAEBUS localhost 300 -

      - When initializing the object no device specific commands are known. Please call "get ebusd_find" to read in supported commands from ebusd.
      - After fresh restart of ebusd it may take a while until all supported devices and their commands are visible.
      - -
    -
    - - - Set -
      -
    • reopen
      - Will close and open the socket connection. -

    • -
    • [r]~<class> <variable-name>~<comment>
      - Will define a attribute with the following syntax:
      - [r]~<class>~<variable-name>~<comment>
      - Valid combinations are read from ebusd (using "get ebusd_find") and are selectable.
      - Values from the attributes will be used as the name for the reading which are read from ebusd in the interval specified.
      -

    • -
    • [w]~<class> <variable-name>~<comment>
      - Will define a attribute with the following syntax:
      - [w]~<class>~<variable-name>~<comment>
      - They will only appear if the attribute "ebusWritesEnabled" is set to "1"
      - Valid combinations are read from ebusd (using "get ebusd_find") and are selectable.
      - Values from the attributes will be used for set commands to modify parameters for ebus devices
      - Hint: if the values for the attributes are prefixed by "set-" then all possible parameters will be listed in one block
      -

    • -
    - - - Get -
      -
    • ebusd_info
      - Execude info command on ebusd and show result. -

    • - -
    • ebusd_find
      - Execude find command on ebusd. Result will be used to display supported "set" and "get" commands. -

    • - - -
    • reading <reading-name>
      - Will read the actual value form ebusd and update the reading. -

    • - -
    • [r]~<class> <variable-name>~<comment>
      - Will read this variable from the ebusd and show the result as a popup.
      - Valid combinations are read from ebusd (using "get ebusd_find") and are selectable.
      -

    • - -
    -
    - - - Attributes -
      -
    • do_not_notify

    • -
    • disable

    • -
    • dummy

    • -
    • showtime

    • -
    • loglevel

    • -
    • ebusWritesEnabled 0,1
      - disable (0) or enable (1) that commands can be send to ebus devices
      - See also description for Set and Get
      - If Attribute is missing, default value is 0 (disable writes)
      -

    • -
    • Attributes of the format
      - [r|h]~<class>~<variable-name>~<comment>
      - define variables that can be retrieved from the ebusd. - They will appear when they are defined by a "set" command as described above.
      - The value assigned to an attribute specifies the name of the reading for this variable.
      - If ebusd returns a list of semicolon separated values then several semicolon separated readings can be defined.
      - "dummy" is a placeholder for a reading that will be ignored. (e.g.: temperature;dummy;pressure).
      - The name of the reading can be suffixed by "<:number>" which is a multiplicator for - the evaluation within the specified interval. (eg. OutsideTemp:3 will evaluate this reading every 3-th cycle)
      - All text followed the reading seperated by a blank is given as an additional parameter to ebusd. - This can be used to request a single value if more than one is retrieved from ebus.
      -

    • -
    • Attributes of the format
      - [w]~<class>~<variable-name>~<comment from csv>
      - define parameters that can be changed on ebus devices (using the write command from ebusctl) - They will appear when they are defined by a "set" command as described above.
      - The value assigned to an attribute specifies the name that will be used in set to change a parameter for a ebus device.
      -

    • -
    • valueFormat
      - Defines a map to format values within GAEBUS.
      - All readings can be formated using syntax of sprinf. -
      - Example: { "temperature" => "%0.2f" } -

    • - -
    -
    -
- -=end html -=cut