############################################## # $Id: 00_FBAHA.pm 2777 2013-02-20 08:02:01Z rudolfkoenig $ package main; use strict; use warnings; use Time::HiRes qw(gettimeofday); sub FBAHA_Read($@); sub FBAHA_Write($$$); sub FBAHA_ReadAnswer($$$); sub FBAHA_Ready($); sub FBAHA_getDevList($$); sub FBAHA_Initialize($) { my ($hash) = @_; require "$attr{global}{modpath}/FHEM/DevIo.pm"; # Provider $hash->{ReadFn} = "FBAHA_Read"; $hash->{WriteFn} = "FBAHA_Write"; $hash->{ReadyFn} = "FBAHA_Ready"; $hash->{UndefFn} = "FBAHA_Undef"; $hash->{ShutdownFn} = "FBAHA_Undef"; $hash->{ReadAnswerFn} = "FBAHA_ReadAnswer"; # Normal devices $hash->{DefFn} = "FBAHA_Define"; $hash->{GetFn} = "FBAHA_Get"; $hash->{SetFn} = "FBAHA_Set"; $hash->{AttrList}= "dummy:1,0"; } ##################################### sub FBAHA_Define($$) { my ($hash, $def) = @_; my @a = split("[ \t][ \t]*", $def); if(@a != 3) { return "wrong syntax: define FBAHA hostname:2002"; } my $name = $a[0]; my $dev = $a[2]; $hash->{Clients} = ":FBDECT:"; my %matchList = ( "1:FBDECT" => ".*" ); $hash->{MatchList} = \%matchList; DevIo_CloseDev($hash); $hash->{DeviceName} = $dev; my $ret = DevIo_OpenDev($hash, 0, "FBAHA_DoInit"); return $ret; } ##################################### sub FBAHA_Set($@) { my ($hash, @a) = @_; my $name = shift @a; my %sets = ("createDevs"=>1, "reregister"=>1, "reopen"=>1); return "set $name needs at least one parameter" if(@a < 1); my $type = shift @a; return "Unknown argument $type, choose one of " . join(" ", sort keys %sets) if(!defined($sets{$type})); if($type eq "createDevs") { my @arg = FBAHA_getDevList($hash,0); foreach my $arg (@arg) { if($arg =~ m/ID:(\d+).*PROP:(.*)/) { my ($i,$p) = ($1,$2,$3); my $msg = "UNDEFINED FBDECT_$i FBDECT $i $p"; DoTrigger("global", $msg, 1); Log3 $name, 3, "$msg, please define it"; } } } if($type eq "reregister") { # Release seems to be deadly on the 546e FBAHA_Write($hash, "02", "") if($hash->{HANDLE}); # RELEASE FBAHA_Write($hash, "00", "00010001"); # REGISTER my ($err, $data) = FBAHA_ReadAnswer($hash, "REGISTER", "^01"); if($err) { Log3 $name, 1, $err; $hash->{STATE} = "???"; return $err; } if($data =~ m/^01030010(........)/) { $hash->{STATE} = "Initialized"; $hash->{HANDLE} = $1; Log3 $name, 1, "FBAHA $hash->{NAME} registered with handle: $hash->{HANDLE}"; } else { my $msg = "Got bogus answer for REGISTER request: $data"; Log3 $name, 1, $msg; $hash->{STATE} = "???"; return $msg; } FBAHA_Write($hash, "03", "0000028200000000"); # LISTEN } if($type eq "reopen") { DevIo_CloseDev($hash); delete $hash->{HANDLE}; return DevIo_OpenDev($hash, 0, "FBAHA_DoInit"); } return undef; } ##################################### sub FBAHA_Get($@) { my ($hash, @a) = @_; my $name = shift @a; my %gets = ("devList"=>1); return "get $name needs at least one parameter" if(@a < 1); my $type = shift @a; return "Unknown argument $type, choose one of ". join(" ", sort keys %gets) if(!defined($gets{$type})); if($type eq "devList") { return join("\n", FBAHA_getDevList($hash,0)); } return undef; } sub FBAHA_getDevList($$) { my ($hash, $onlyId) = @_; FBAHA_Write($hash, "05", "00000000"); # CONFIG_REQ my $data = ""; for(;;) { my ($err, $buf) = FBAHA_ReadAnswer($hash, "CONFIG_RSP", "^06"); return ($err) if($err); $data .= substr($buf, 32); last if($buf =~ m/^060[23]/); } return FBAHA_configInd($data, $onlyId); } sub FBAHA_configInd($$) { my ($data, $onlyId) = @_; my @answer; while(length($data) >= 288) { my $id = hex(substr($data, 0, 4)); my $act = hex(substr($data, 4, 2)); my $typ = hex(substr($data, 8, 8)); my $lsn = hex(substr($data, 16, 8)); my $nam = pack("H*",substr($data,24,160)); $nam =~ s/\x0//g; $act = ($act == 2 ? "active" : ($act == 1 ? "inactive" : "removed")); my %tl = ( 2=>"AVM FRITZ!Dect Powerline 546E", 9=>"AVM FRITZ!Dect 200"); $typ = $tl{$typ} ? $tl{$typ} : "unknown($typ)"; my %ll = (7=>"powerMeter",9=>"switch"); $lsn = join ",", map { $ll{$_} if((1 << $_) & $lsn) } sort keys %ll; my $dlen = hex(substr($data, 280, 8))*2; # DATA MSG push @answer, "NAME:$nam, ID:$id, $act, TYPE:$typ PROP:$lsn" if(!$onlyId || $onlyId == $id); if($onlyId && $onlyId == $id) { my $mnf = hex(substr($data,184, 8)); # empty/0 my $idf = substr($data,192,40); # empty/0 my $frm = substr($data,232,40); # empty/0 push @answer, " MANUF:$mnf"; push @answer, " UniqueID:$idf"; push @answer, " Firmware:$frm"; push @answer, substr($data, 288, $dlen); return @answer; } $data = substr($data, 288+$dlen); # rest } return @answer; } ##################################### sub FBAHA_DoInit($) { my $hash = shift; my $name = $hash->{NAME}; delete $hash->{HANDLE}; # else reregister fails / RELEASE is deadly my $ret = FBAHA_Set($hash, ($name, "reregister")); return $ret; } ##################################### sub FBAHA_Undef($@) { my ($hash, $arg) = @_; FBAHA_Write($hash, "02", ""); # RELEASE DevIo_CloseDev($hash); return undef; } ##################################### sub FBAHA_Write($$$) { my ($hash,$fn,$msg) = @_; $msg = sprintf("%s03%04x%s%s", $fn, length($msg)/2+8, $hash->{HANDLE} ? $hash->{HANDLE} : "00000000", $msg); DevIo_SimpleWrite($hash, $msg, 1); } ##################################### # called from the global loop, when the select for hash->{FD} reports data sub FBAHA_Read($@) { my ($hash, $local, $regexp) = @_; my $buf = ($local ? $local : DevIo_SimpleRead($hash)); return "" if(!defined($buf)); my $name = $hash->{NAME}; $buf = unpack('H*', $buf); my $data = ($hash->{PARTIAL} ? $hash->{PARTIAL} : ""); # drop old data if($data) { $data = "" if(gettimeofday() - $hash->{READ_TS} > 1); delete($hash->{READ_TS}); } Log3 $name, 5, "FBAHA/RAW: $data/$buf"; $data .= $buf; my $msg; while(length($data) >= 16) { my $len = hex(substr($data, 4,4))*2; if($len < 16 || $len > 10240) { # Out of Sync Log3 $name, 1, "FBAHA: resetting buffer as we are out of sync ($len)"; $hash->{PARTIAL} = ""; return ""; } last if($len > length($data)); $msg = substr($data, 0, $len); $data = substr($data, $len); last if(defined($local) && (!defined($regexp) || ($msg =~ m/$regexp/))); $hash->{"${name}_MSGCNT"}++; $hash->{"${name}_TIME"} = TimeNow(); $hash->{RAWMSG} = $msg; my %addvals = (RAWMSG => $msg); Dispatch($hash, $msg, \%addvals) if($init_done); $msg = undef; } $hash->{PARTIAL} = $data; $hash->{READ_TS} = gettimeofday() if($data); return $msg if(defined($local)); return undef; } ##################################### # This is a direct read for commands like get sub FBAHA_ReadAnswer($$$) { my ($hash, $arg, $regexp) = @_; return ("No FD (dummy device?)", undef) if(!$hash || ($^O !~ /Win/ && !defined($hash->{FD}))); for(;;) { return ("Device lost when reading answer for get $arg", undef) if(!$hash->{FD}); my $rin = ''; vec($rin, $hash->{FD}, 1) = 1; my $nfound = select($rin, undef, undef, 3); if($nfound <= 0) { next if ($! == EAGAIN() || $! == EINTR()); my $err = ($! ? $! : "Timeout"); $hash->{TIMEOUT} = 1; DevIo_Disconnected($hash); return("FBAHA_ReadAnswer $arg: $err", undef); } my $buf = DevIo_SimpleRead($hash); return ("No data", undef) if(!defined($buf)); my $ret = FBAHA_Read($hash, $buf, $regexp); return (undef, $ret) if(defined($ret)); } } ##################################### sub FBAHA_Ready($) { my ($hash) = @_; return DevIo_OpenDev($hash, 1, "FBAHA_DoInit") if($hash->{STATE} eq "disconnected"); return 0; } 1; =pod =begin html

FBAHA

=end html =begin html_DE

FBAHA

=end html_DE =cut