From 92fb192e050de9994e7c59a84f006c9804052b90 Mon Sep 17 00:00:00 2001 From: rudolfkoenig <> Date: Mon, 2 Apr 2012 11:04:52 +0000 Subject: [PATCH] Perliminary (read-only) HM-USB Support git-svn-id: https://svn.fhem.de/fhem/trunk@1405 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/contrib/HM-USB-CFG/00_HMLAN.pm | 405 +++++++++++++++++++++++++ fhem/contrib/HM-USB-CFG/DevIo.pm | 443 ++++++++++++++++++++++++++++ fhem/contrib/HM-USB-CFG/README.txt | 90 ++++++ 3 files changed, 938 insertions(+) create mode 100644 fhem/contrib/HM-USB-CFG/00_HMLAN.pm create mode 100644 fhem/contrib/HM-USB-CFG/DevIo.pm create mode 100644 fhem/contrib/HM-USB-CFG/README.txt diff --git a/fhem/contrib/HM-USB-CFG/00_HMLAN.pm b/fhem/contrib/HM-USB-CFG/00_HMLAN.pm new file mode 100644 index 000000000..3df851839 --- /dev/null +++ b/fhem/contrib/HM-USB-CFG/00_HMLAN.pm @@ -0,0 +1,405 @@ +############################################## +# $Id$ +package main; + +use strict; +use warnings; +use Time::HiRes qw(gettimeofday); + +sub HMLAN_Parse($$); +sub HMLAN_Read($); +sub HMLAN_Write($$$); +sub HMLAN_ReadAnswer($$$); +sub HMLAN_uptime($); + +sub HMLAN_SimpleWrite(@); + +my %sets = ( + "hmPairForSec" => "HomeMatic", + "hmPairSerial" => "HomeMatic", +); + +sub +HMLAN_Initialize($) +{ + my ($hash) = @_; + + require "$attr{global}{modpath}/FHEM/DevIo.pm"; + +# Provider + $hash->{ReadFn} = "HMLAN_Read"; + $hash->{WriteFn} = "HMLAN_Write"; + $hash->{ReadyFn} = "HMLAN_Ready"; + $hash->{SetFn} = "HMLAN_Set"; + $hash->{Clients} = ":CUL_HM:"; + my %mc = ( + "1:CUL_HM" => "^A......................", + ); + $hash->{MatchList} = \%mc; + +# Normal devices + $hash->{DefFn} = "HMLAN_Define"; + $hash->{UndefFn} = "HMLAN_Undef"; + $hash->{AttrList}= "do_not_notify:1,0 dummy:1,0 " . + "loglevel:0,1,2,3,4,5,6 addvaltrigger " . + "hmId hmProtocolEvents hmKey"; +} + +##################################### +sub +HMLAN_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + if(@a != 3) { + my $msg = "wrong syntax: define ip[:port] | /path/to/HM-USB-CFG device as a parameter"; #added for HM-USB-CFG by peterp + + Log 2, $msg; + return $msg; + } + DevIo_CloseDev($hash); + + my $name = $a[0]; + my $dev = $a[2]; + $dev .= ":1000" if($dev !~ m/:/ && $dev ne "none" && $dev !~ m/\@/ && $dev !~ m/hiddev/ ); #changed for HM-USB-CFG by peterp + $attr{$name}{hmId} = sprintf("%06X", time() % 0xffffff); # Will be overwritten + + if($dev eq "none") { + Log 1, "$name device is none, commands will be echoed only"; + $attr{$name}{dummy} = 1; + return undef; + } + $hash->{DeviceName} = $dev; + my $ret = DevIo_OpenDev($hash, 0, "HMLAN_DoInit"); + return $ret; +} + + +##################################### +sub +HMLAN_Undef($$) +{ + my ($hash, $arg) = @_; + my $name = $hash->{NAME}; + + foreach my $d (sort keys %defs) { + if(defined($defs{$d}) && + defined($defs{$d}{IODev}) && + $defs{$d}{IODev} == $hash) + { + my $lev = ($reread_active ? 4 : 2); + Log GetLogLevel($name,$lev), "deleting port for $d"; + delete $defs{$d}{IODev}; + } + } + + DevIo_CloseDev($hash); + return undef; +} + +##################################### +sub +HMLAN_RemoveHMPair($) +{ + my $hash = shift; + delete($hash->{hmPair}); +} + + +##################################### +sub +HMLAN_Set($@) +{ + my ($hash, @a) = @_; + + return "\"set HMLAN\" needs at least one parameter" if(@a < 2); + return "Unknown argument $a[1], choose one of " . join(" ", sort keys %sets) + if(!defined($sets{$a[1]})); + + my $name = shift @a; + my $type = shift @a; + my $arg = join("", @a); + my $ll = GetLogLevel($name,3); + + if($type eq "hmPairForSec") { #################################### + return "Usage: set $name hmPairForSec " + if(!$arg || $arg !~ m/^\d+$/); + $hash->{hmPair} = 1; + InternalTimer(gettimeofday()+$arg, "HMLAN_RemoveHMPair", $hash, 1); + + } elsif($type eq "hmPairSerial") { ################################ + return "Usage: set $name hmPairSerial <10-character-serialnumber>" + if(!$arg || $arg !~ m/^.{10}$/); + + my $id = AttrVal($hash->{NAME}, "hmId", "123456"); + $hash->{HM_CMDNR} = $hash->{HM_CMDNR} ? ($hash->{HM_CMDNR}+1)%256 : 1; + + HMLAN_Write($hash, undef, sprintf("As15%02X8401%s000000010A%s", + $hash->{HM_CMDNR}, $id, unpack('H*', $arg))); + $hash->{hmPairSerial} = $arg; + + } + return undef; +} + + +##################################### +# This is a direct read for commands like get +sub +HMLAN_ReadAnswer($$$) +{ + my ($hash, $arg, $regexp) = @_; + my $type = $hash->{TYPE}; + + return ("No FD", undef) + if(!$hash && !defined($hash->{FD})); + + my ($mdata, $rin) = ("", ''); + my $buf; + my $to = 3; # 3 seconds timeout + $to = $hash->{RA_Timeout} if($hash->{RA_Timeout}); # ...or less + for(;;) { + + return ("Device lost when reading answer for get $arg", undef) + if(!$hash->{FD}); + vec($rin, $hash->{FD}, 1) = 1; + my $nfound = select($rin, undef, undef, $to); + if($nfound < 0) { + next if ($! == EAGAIN() || $! == EINTR() || $! == 0); + my $err = $!; + DevIo_Disconnected($hash); + return("HMLAN_ReadAnswer $arg: $err", undef); + } + return ("Timeout reading answer for get $arg", undef) + if($nfound == 0); + $buf = DevIo_SimpleRead($hash); + return ("No data", undef) if(!defined($buf)); + + if($buf) { + Log 5, "HMLAN/RAW (ReadAnswer): $buf"; + $mdata .= $buf; + } + if($mdata =~ m/\r\n/) { + if($regexp && $mdata !~ m/$regexp/) { + HMLAN_Parse($hash, $mdata); + } else { + return (undef, $mdata) + } + } + } +} + +my %lhash; + +##################################### +sub +HMLAN_Write($$$) +{ + my ($hash,$fn,$msg) = @_; + + my $dst = substr($msg, 16, 6); + if(!$lhash{$dst} && $dst ne "000000") { # Don't think I grok the logic + HMLAN_SimpleWrite($hash, "+$dst,00,00,"); + HMLAN_SimpleWrite($hash, "+$dst,00,00,"); + HMLAN_SimpleWrite($hash, "+$dst,00,00,"); + HMLAN_SimpleWrite($hash, "-$dst"); + HMLAN_SimpleWrite($hash, "+$dst,00,00,"); + HMLAN_SimpleWrite($hash, "+$dst,00,00,"); + HMLAN_SimpleWrite($hash, "+$dst,00,00,"); + HMLAN_SimpleWrite($hash, "+$dst,00,00,"); + $lhash{$dst} = 1; + } + my $tm = int(gettimeofday()*1000) % 0xffffffff; + $msg = sprintf("S%08X,00,00000000,01,%08X,%s", + $tm, $tm, substr($msg, 4)); + HMLAN_SimpleWrite($hash, $msg); + + # Avoid problems with structure set + # TODO: rewrite it to use a queue+internaltimer like the CUL + select(undef, undef, undef, 0.03); +} + +##################################### +# called from the global loop, when the select for hash->{FD} reports data +sub +HMLAN_Read($) +{ + my ($hash) = @_; + + my $buf = DevIo_SimpleRead($hash); + return "" if(!defined($buf)); + my $name = $hash->{NAME}; + + my $hmdata = $hash->{PARTIAL}; + Log 5, "HMLAN/RAW: $hmdata/$buf"; + $hmdata .= $buf; + + while($hmdata =~ m/\n/) { + my $rmsg; + ($rmsg,$hmdata) = split("\n", $hmdata, 2); + $rmsg =~ s/\r//; + HMLAN_Parse($hash, $rmsg) if($rmsg); + } + $hash->{PARTIAL} = $hmdata; +} + +sub +HMLAN_uptime($) +{ + my $msec = shift; + + $msec = hex($msec); + my $sec = int($msec/1000); + return sprintf("%03d %02d:%02d:%02d.%03d", + int($msec/86400000), int($sec/3600), + int(($sec%3600)/60), $sec%60, $msec % 1000); +} + +sub +HMLAN_Parse($$) +{ + my ($hash, $rmsg) = @_; + my $name = $hash->{NAME}; + my $ll5 = GetLogLevel($name,5); + my ($src, $status, $msec, $d2, $rssi, $msg); + + my $dmsg = $rmsg; + + Log $ll5, "HMLAN_Parse: $name $rmsg"; + if($rmsg =~ m/^E(......),(....),(........),(..),(....),(.*)/) { + ($src, $status, $msec, $d2, $rssi, $msg) = + ($1, $2, $3, $4, $5, $6); + if ($hash->{HIDDev}) #added for HM-USB-CFG by peterp + { + $dmsg = sprintf("A%s", uc($msg)); + } + else + { + $dmsg = sprintf("A%02X%s", length($msg)/2, uc($msg)); + } + $hash->{uptime} = HMLAN_uptime($msec); + + } elsif($rmsg =~ m/^R(........),(....),(........),(..),(....),(.*)/) { + ($src, $status, $msec, $d2, $rssi, $msg) = + ($1, $2, $3, $4, $5, $6); + + if ($hash->{HIDDev}) #added for HM-USB-CFG by peterp + { + $dmsg = sprintf("A%s", uc($msg)); + } + else + { + $dmsg = sprintf("A%02X%s", length($msg)/2, uc($msg)); + } + $dmsg .= "NACK" if($status !~ m/00(01|02|21)/); + $hash->{uptime} = HMLAN_uptime($msec); + + } elsif($rmsg =~ + m/^HHM-LAN-IF,(....),(..........),(......),(......),(........),(....)/) { + my ($vers, $serno, $d1, $owner, $msec, $d2) = + (hex($1), $2, $3, $4, $5, $6); + $hash->{serialNr} = $serno; + $hash->{firmware} = sprintf("%d.%d", ($vers>>12)&0xf, $vers & 0xffff); + $hash->{owner} = $owner; + $hash->{uptime} = HMLAN_uptime($msec); + my $myId = AttrVal($name, "hmId", $owner); + if(lc($owner) ne lc($myId) && !AttrVal($name, "dummy", 0)) { + Log 1, "HMLAN setting owner to $myId from $owner"; + HMLAN_SimpleWrite($hash, "A$myId"); + } + return; + + } elsif($rmsg =~ m/^I00.*/) { + # Ack from the HMLAN + return; + + } else { + Log $ll5, "$name Unknown msg >$rmsg<"; + return; + + } + + $hash->{"${name}_MSGCNT"}++; + $hash->{"${name}_TIME"} = TimeNow(); + $hash->{RAWMSG} = $rmsg; + my %addvals = (RAWMSG => $rmsg); + if(defined($rssi)) { + $rssi = hex($rssi)-65536; + $hash->{RSSI} = $rssi; + $addvals{RSSI} = $rssi; + } + Dispatch($hash, $dmsg, \%addvals); +} + + +##################################### +sub +HMLAN_Ready($) +{ + my ($hash) = @_; + + return DevIo_OpenDev($hash, 1, "HMLAN_DoInit"); +} + +######################## +sub +HMLAN_SimpleWrite(@) +{ + my ($hash, $msg, $nonl) = @_; + my $name = $hash->{NAME}; + return if(!$hash || AttrVal($hash->{NAME}, "dummy", undef)); + + select(undef, undef, undef, 0.01); + Log GetLogLevel($name,5), "SW: $msg"; + if (!($hash->{HIDDev})) + { + $msg .= "\r\n" unless($nonl) ; #changed for HM-USB-CFG by peterp + } + syswrite($hash->{TCPDev}, $msg) if($hash->{TCPDev}); + DevIo_SimpleWrite($hash, $msg) if ($hash->{HIDDev}); #added for HM-USB-CFG by peterp +} + +######################## +sub +HMLAN_DoInit($) +{ + my ($hash) = @_; + my $name = $hash->{NAME}; + + my $id = AttrVal($name, "hmId", undef); + my $key = AttrVal($name, "hmKey", ""); # 36(!) hex digits + + #my $s2000 = sprintf("%02X", time()-946681200); # sec since 2000 + + # Calculate the local time in seconds from 2000. + my $t = time(); + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($t); + $t -= 946684800; # seconds between 01.01.2000, 00:00 and THE EPOCH (1970) + $t -= 1*3600; # Timezone offset from UTC * 3600 (MEZ=1). FIXME/HARDCODED + $t += 3600 if $isdst; + my $s2000 = sprintf("%02X", $t); + + HMLAN_SimpleWrite($hash, "A$id") if($id); + HMLAN_SimpleWrite($hash, "C"); + HMLAN_SimpleWrite($hash, "Y01,01,$key"); + HMLAN_SimpleWrite($hash, "Y02,00,"); + HMLAN_SimpleWrite($hash, "Y03,00,"); + HMLAN_SimpleWrite($hash, "Y03,00,"); + HMLAN_SimpleWrite($hash, "T$s2000,04,00,00000000"); + + InternalTimer(gettimeofday()+25, "HMLAN_KeepAlive", $hash, 0); + return undef; +} + +##################################### +sub +HMLAN_KeepAlive($) +{ + my $hash = shift; + return if(!$hash->{FD}); + HMLAN_SimpleWrite($hash, "K"); + InternalTimer(gettimeofday()+25, "HMLAN_KeepAlive", $hash, 1) if (!($hash->{HIDDev})); #changed for HM-USB-CFG by peterp +} + +1; \ No newline at end of file diff --git a/fhem/contrib/HM-USB-CFG/DevIo.pm b/fhem/contrib/HM-USB-CFG/DevIo.pm new file mode 100644 index 000000000..aa9e10053 --- /dev/null +++ b/fhem/contrib/HM-USB-CFG/DevIo.pm @@ -0,0 +1,443 @@ +############################################## +# $Id$ +package main; +use Device::USB; +my $timeout = 1000 ; + +sub DevIo_SimpleRead($); +sub DevIo_TimeoutRead($$); +sub DevIo_SimpleWrite($$); +sub DevIo_OpenDev($$$); +sub DevIo_CloseDev($); +sub DevIo_Disconnected($); + +######################## +sub +DevIo_DoSimpleRead($) +{ + my ($hash) = @_; + my ($buf, $res); + + if($hash->{USBDev}) { + $buf = $hash->{USBDev}->input(); + + } elsif($hash->{DIODev}) { + $res = sysread($hash->{DIODev}, $buf, 256); + $buf = undef if(!defined($res)); + + } elsif($hash->{TCPDev}) { + $res = sysread($hash->{TCPDev}, $buf, 256); + $buf = undef if(!defined($res)); + } +######################################### HID by peterp + elsif($hash->{HIDDev}) { + my $r; #raw message + my $b=0; #raw message payload + my $c=0; # ignore counter + my $d=0; #HM message length + my $s = 0; #start counter + my $start = 0; #raw header flag + my $typ =""; + + $res = sysread($hash->{HIDDev}, $buf, 512); + $buf = undef if(!defined($res)); + +## HID specific + for (my $i=0; $i<64;$i++) + { + if ($start != 0) + { + $r .= unpack('H*', substr($buf,4+$i*8,1)); #copy to raw HMmessage + if ($typ eq "E") + { + if ($b > 12) #raw message payload + { + $d--; + if ($d == 0) + { + $r .= "\n"; #form a raw HMmessage for parse like HMLAN + $start = 0; +# Log 4, "HMUSB HMmessage:$r"; + } + } + $b++; + } + else + { + $d--; + if ($d == 0) + { + Log 2, "HMUSB HMmessage:$r"; + $r .= "\n"; #form a raw HMmessage for parse like HMLAN + $start = 0; + } + } + } + else + { +# $r = unpack('H*', substr($buf,4+$i*8,1)); +# Log 4, "$r\t"; + if ( ord(substr($buf,4+$i*8,1)) == 69) + { + $start = 1; #raw header found + $r = "E"; #start a raw HMmessage for parse like HMLAN + $d = ord(substr($buf,4+($i+13)*8,1)); #calc HM message length + $s = $i; + $typ ="E"; +# Log 4, "HMUSB ReadSimple Magic found HMlen:$d"; + } + elsif ( ord(substr($buf,4+$i*8,1)) == 73) + { + Log 2, "HMUSB ReadSimple Magic >I< found i:$i b:$b "; + $start = 1; #raw header found + $typ = "I"; + $d = 4; + } + elsif ( ord(substr($buf,4+$i*8,1)) == 82) + { + Log 2, "HMUSB ReadSimple Magic >R< found i:$i b:$b "; + } + elsif ( ord(substr($buf,4+$i*8,1)) == 72) + { + Log 2, "HMUSB ReadSimple Magic >H< USB-IF found i:$i b:$b"; + $start = 1; #raw header found + $typ = "H"; + $d = 40; + } + else + { + $c++; #ignore counter + } + } + } +# Log 4, "HMUSB ReadSimple all >$r< (raw Start $s ignored $c)"; +if ($typ eq "E") + { + my ($src, $status, $msec, $d2, $rssi, $msg); + $r =~ m/^E(......)(....)(........)(..)(....)(.*)/; + ($src, $status, $msec, $d2, $rssi, $msg) = + ($1, $2, $3, $4, $5, $6); + my $cmsg = "E".$src.",".$status.",".$msec.",".$d2.",".$rssi.",".$msg."\n"; + Log 4, "HMUSB ReadSimple converted $cmsg"; + return $cmsg; + } +elsif ($typ eq "H") + { + Log 4, "HMUSB ReadSimple Wakup found"; + my ($vers, $serno, $d1, $owner, $msec, $d2); + $r =~ m/^HHM-USB-IF(....)(..........)(......)(......)(........)(....)/; + ($vers, $serno, $d1, $owner, $msec, $d2) = + (hex($1), $2, $3, $4, $5, $6); + my $wmsg = "HHM-USB-IF".",".$vers.",".$serno.",".$d1.",".$owner.",".$msec.",".$d2."\n"; + Log 2, "HMUSB ReadSimple Wakeup converted $wmsg"; + return $wmsg; + } +elsif ($typ eq "I") + { + $r =~ m/^I00.*/; + return $r; + } +######################################### HIDDEV by peterp + } + return $buf; +} + +######################## +sub +DevIo_SimpleRead($) +{ + my ($hash) = @_; + my $buf = DevIo_DoSimpleRead($hash); + + ########### + # Lets' try again: Some drivers return len(0) on the first read... + if(defined($buf) && length($buf) == 0) { + $buf = DevIo_DoSimpleRead($hash); + } + + if(!defined($buf) || length($buf) == 0) { + DevIo_Disconnected($hash); + return undef; + } + return $buf; +} + +######################## +# Read until you get the timeout. Use it with care +sub +DevIo_TimeoutRead($$) +{ + my ($hash, $timeout) = @_; + + my $answer = ""; + for(;;) { + my $rin = ""; + vec($rin, $hash->{FD}, 1) = 1; + my $nfound = select($rin, undef, undef, $timeout); + last if($nfound <= 0); + my $r = DevIo_DoSimpleRead($hash); + last if(!defined($r)); + $answer .= $r; + } + return $answer; +} + +######################## +# Input is HEX, with header and CRC +sub +DevIo_SimpleWrite($$) +{ + my ($hash, $msg) = @_; + return if(!$hash); + + my $name = $hash->{NAME}; + my $ll5 = GetLogLevel($name,3); + Log $ll5, "DevIo SW: $msg"; +#################################################### + if($hash->{HIDDev}) #added for HM-USB-CFG by peterp + { + $msg =~ s/,//g; + my $msg1 = substr($msg,0,1); + my $msg2 = pack('H*', substr($msg,1)); + $msg = $msg1 . $msg2 . "\r\n"; + + syswrite($hash->{HIDDev}, $msg); + + my $tmsg = unpack('H*', $msg); + Log 2, "DevIo_SimpleWrite: $tmsg"; + } + else +#################################################### + { + $msg = pack('H*', $msg) if($ishex); + $hash->{USBDev}->write($msg) if($hash->{USBDev}); + syswrite($hash->{TCPDev}, $msg) if($hash->{TCPDev}); + syswrite($hash->{DIODev}, $msg) if($hash->{DIODev}); + } + select(undef, undef, undef, 0.001); +} + + +######################## +sub +DevIo_OpenDev($$$) +{ + my ($hash, $reopen, $initfn) = @_; + my $dev = $hash->{DeviceName}; + my $name = $hash->{NAME}; + my $po; + my $baudrate; + ($dev, $baudrate) = split("@", $dev); + + + $hash->{PARTIAL} = ""; + Log 4, "DEVIO OpenDev $name device $dev" + if(!$reopen); + + if($dev =~ m/^(.+):([0-9]+)$/) { # host:port + + # 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}) { + return; + } + + my $conn = IO::Socket::INET->new(PeerAddr => $dev); + if($conn) { + delete($hash->{NEXT_OPEN}) + + } else { + Log(3, "Can't connect to IPDEV $dev: $!") if(!$reopen); + $readyfnlist{"$name.$dev"} = $hash; + $hash->{STATE} = "disconnected"; + $hash->{NEXT_OPEN} = time()+60; + return ""; + } + + $hash->{TCPDev} = $conn; + $hash->{FD} = $conn->fileno(); + delete($readyfnlist{"$name.$dev"}); + $selectlist{"$name.$dev"} = $hash; + + } elsif($baudrate && lc($baudrate) eq "directio") { # Without Device::SerialPort + + if(!open($po, "+<$dev")) { + return undef if($reopen); + Log(3, "Can't open $dev: $!"); + $readyfnlist{"$name.$dev"} = $hash; + $hash->{STATE} = "disconnected"; + return ""; + } + + $hash->{DIODev} = $po; + + if( $^O =~ /Win/ ) { + $readyfnlist{"$name.$dev"} = $hash; + } else { + $hash->{FD} = fileno($po); + delete($readyfnlist{"$name.$dev"}); + $selectlist{"$name.$dev"} = $hash; + } +#################################################### HIDDEV by peterp + } elsif($dev =~ m/^\/dev\/usb\/hiddev[0-9]$/) + { + if(!open($po, "+<$dev")) + { + return undef if($reopen); + Log(3, "Can't open HIDD $dev: $!"); + $readyfnlist{"$name.$dev"} = $hash; + $hash->{STATE} = "disconnected"; + return ""; + } + Log(2, "DevIo opened HID $dev"); #peterp + + $hash->{HIDDev} = $po; + + if( $^O =~ /Win/ ) { + $readyfnlist{"$name.$dev"} = $hash; + } else { + $hash->{FD} = fileno($po); + delete($readyfnlist{"$name.$dev"}); + $selectlist{"$name.$dev"} = $hash; + } +#################################################### HIDDEV by peterp + } else { # USB/Serial device + + + if ($^O=~/Win/) { + require Win32::SerialPort; + $po = new Win32::SerialPort ($dev); + } else { + require Device::SerialPort; + $po = new Device::SerialPort ($dev); + } + + if(!$po) { + return undef if($reopen); + Log(3, "Can't open USB/Seriell $dev: $!"); + $readyfnlist{"$name.$dev"} = $hash; + $hash->{STATE} = "disconnected"; + return ""; + } + $hash->{USBDev} = $po; + if( $^O =~ /Win/ ) { + $readyfnlist{"$name.$dev"} = $hash; + } else { + $hash->{FD} = $po->FILENO; + delete($readyfnlist{"$name.$dev"}); + $selectlist{"$name.$dev"} = $hash; + } + + if($baudrate) { + $po->reset_error(); + Log 3, "Setting $name baudrate to $baudrate"; + $po->baudrate($baudrate); + $po->databits(8); + $po->parity('none'); + $po->stopbits(1); + $po->handshake('none'); + + # This part is for some Linux kernel versions whih has strange default + # settings. Device::SerialPort is nice: if the flag is not defined for your + # OS then it will be ignored. + $po->stty_icanon(0); + #$po->stty_parmrk(0); # The debian standard install does not have it + $po->stty_icrnl(0); + $po->stty_echoe(0); + $po->stty_echok(0); + $po->stty_echoctl(0); + + # Needed for some strange distros + $po->stty_echo(0); + $po->stty_icanon(0); + $po->stty_isig(0); + $po->stty_opost(0); + $po->stty_icrnl(0); + } + + $po->write_settings; + + + } + + if($reopen) { + Log 1, "$dev reappeared ($name)"; + } else { + Log 3, "$name device $dev opened"; + } + + $hash->{STATE}="opened"; + + my $ret; + if($initfn) { + my $ret = &$initfn($hash); + if($ret) { + DevIo_CloseDev($hash); + Log 1, "Cannot init $dev, ignoring it"; + } + } + + DoTrigger($name, "CONNECTED") if($reopen); + return $ret; +} + +######################## +sub +DevIo_CloseDev($) +{ + my ($hash) = @_; + my $name = $hash->{NAME}; + my $dev = $hash->{DeviceName}; + + return if(!$dev); + + if($hash->{TCPDev}) { + $hash->{TCPDev}->close(); + delete($hash->{TCPDev}); + + } elsif($hash->{USBDev}) { + $hash->{USBDev}->close() ; + delete($hash->{USBDev}); + + } elsif($hash->{DIODev}) { + close($hash->{DIODev}); + delete($hash->{DIODev}); + + } elsif($hash->{HIDDev}) { #added for HM-USB-CFG by peterp + close($hash->{HIDDev}); + delete($hash->{HIDDev}); + + } + ($dev, undef) = split("@", $dev); # Remove the baudrate + delete($selectlist{"$name.$dev"}); + delete($readyfnlist{"$name.$dev"}); + delete($hash->{FD}); +} + +sub +DevIo_Disconnected($) +{ + my $hash = shift; + my $dev = $hash->{DeviceName}; + my $name = $hash->{NAME}; + my $baudrate; + ($dev, $baudrate) = split("@", $dev); + + return if(!defined($hash->{FD})); # Already deleted or RFR + + Log 1, "$dev disconnected, waiting to reappear"; + DevIo_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"); +} + + +1; \ No newline at end of file diff --git a/fhem/contrib/HM-USB-CFG/README.txt b/fhem/contrib/HM-USB-CFG/README.txt new file mode 100644 index 000000000..07bbeb80d --- /dev/null +++ b/fhem/contrib/HM-USB-CFG/README.txt @@ -0,0 +1,90 @@ +Hallo Rudi, + +anbei meine aktuelle Version von HMLAN ud DevIo für den HM-USB-CFG. +Ich hab es bislang aber nicht geschaft zu senden. Als Monitor ist die +Version aber brauchbar. Bitte schau drüber ob die Qualität for das +contrib Verzeichniss reicht. Wenn's passt bitte einchecken. Vielleicht +hat ja jemand eine Idee was fehlt... + +Ich hab alle Änderungen deutlich mit + +for HM-USB-CFG by peterp bzw. HIDDEV by peterp + +gekennzeichnet und mit + +if($hash->{HIDDev}) + +abgetrennt. Die Originalfuntionalität sollte also durch meine +Änderungen nicht beeinträchtigt sein. + +In der config datei definiert man + +define HMUSB HMLAN /dev/usb/hiddev0 +attr HMUSB addvaltrigger RSSI +attr HMUSB hmId 123456 +attr HMUSB hmProtocolEvents 1 +attr HMUSB room Innen + +in der Logdatei sieht das dann so aus: + +2012.04.01 20:02:22.828 2: DevIo opened HID /dev/usb/hiddev0 +2012.04.01 20:02:22.839 2: DevIo_SimpleWrite: 4178987d0d0a +2012.04.01 20:02:22.850 2: DevIo_SimpleWrite: 430d0a +2012.04.01 20:02:22.862 2: DevIo_SimpleWrite: 5901010d0a +2012.04.01 20:02:22.873 2: DevIo_SimpleWrite: 5902000d0a +2012.04.01 20:02:22.885 2: DevIo_SimpleWrite: 5903000d0a +2012.04.01 20:02:22.896 2: DevIo_SimpleWrite: 5903000d0a +2012.04.01 20:02:22.908 2: DevIo_SimpleWrite: 54170b54ae0400000000000d0a +2012.04.01 20:02:23.607 0: Server started (version =VERS= from =DATE= +($Id$), pid 10178) +2012.04.01 20:02:47.946 2: DevIo_SimpleWrite: 4b0d0a +2012.04.01 20:03:10.639 2: SYS HMUSB RCV L:0C N:8A CMD:A041 +(TYPE=65,BCAST,BIDI,RPTEN) SRC:14F617 DST:123456 018100 +2012.04.01 20:03:10.755 2: DevIo_SimpleWrite: 2b14f61700000d0a +2012.04.01 20:03:10.767 2: DevIo_SimpleWrite: 2b14f61700000d0a +2012.04.01 20:03:10.779 2: DevIo_SimpleWrite: 2b14f61700000d0a +2012.04.01 20:03:10.790 2: DevIo_SimpleWrite: 2d14f6170d0a +2012.04.01 20:03:10.802 2: DevIo_SimpleWrite: 2b14f61700000d0a +2012.04.01 20:03:10.813 2: DevIo_SimpleWrite: 2b14f61700000d0a +2012.04.01 20:03:10.825 2: DevIo_SimpleWrite: 2b14f61700000d0a +2012.04.01 20:03:10.837 2: DevIo_SimpleWrite: 2b14f61700000d0a +2012.04.01 20:03:10.848 2: DevIo_SimpleWrite: +536f1333ac0000000000016f1333ac01800212345614f617010100000d0a +2012.04.01 20:03:10.889 2: SYS HMUSB SND L:0D N:01 CMD:8002 +(TYPE=2,RPTEN) SRC:123456 DST:14F617 01010000 (ACK_STATUS CHANNEL:01 +STATUS:00) +2012.04.01 20:03:10.908 2: HM-TFK LSE_TFKTEST closed +2012.04.01 20:03:10.911 2: HM-TFK LSE_TFKTEST contact: closed +2012.04.01 20:03:10.930 2: SYS HMUSB RCV L:0C N:8A CMD:A041 +(TYPE=65,BIDI,RPTEN) SRC:14F617 DST:123456 018100 +2012.04.01 20:03:11.046 2: DevIo_SimpleWrite: +536f1334710000000000016f13347102800212345614f617010100000d0a +2012.04.01 20:03:11.086 2: SYS HMUSB SND L:0D N:02 CMD:8002 +(TYPE=2,RPTEN) SRC:123456 DST:14F617 01010000 (ACK_STATUS CHANNEL:01 +STATUS:00) +2012.04.01 20:03:11.407 2: SYS HMUSB RCV L:0C N:8A CMD:A041 +(TYPE=65,BIDI,RPTEN) SRC:14F617 DST:123456 018100 +2012.04.01 20:03:11.523 2: DevIo_SimpleWrite: +536f13364e0000000000016f13364e03800212345614f617010100000d0a +2012.04.01 20:03:11.563 2: SYS HMUSB SND L:0D N:03 CMD:8002 +(TYPE=2,RPTEN) SRC:123456 DST:14F617 01010000 (ACK_STATUS CHANNEL:01 +STATUS:00) +2012.04.01 20:03:12.431 2: SYS HMUSB RCV L:0C N:8A CMD:A041 +(TYPE=65,BIDI,RPTEN) SRC:14F617 DST:123456 018100 +2012.04.01 20:03:12.547 2: DevIo_SimpleWrite: +536f133a4e0000000000016f133a4e04800212345614f617010100000d0a +2012.04.01 20:03:12.587 2: SYS HMUSB SND L:0D N:04 CMD:8002 +(TYPE=2,RPTEN) SRC:123456 DST:14F617 01010000 (ACK_STATUS CHANNEL:01 +STATUS:00) +2012.04.01 20:03:14.447 2: SYS HMUSB RCV L:0C N:8A CMD:A041 +(TYPE=65,BIDI,RPTEN) SRC:14F617 DST:123456 018100 +2012.04.01 20:03:14.563 2: DevIo_SimpleWrite: +536f13422e0000000000016f13422e05800212345614f617010100000d0a +2012.04.01 20:03:14.603 2: SYS HMUSB SND L:0D N:05 CMD:8002 +(TYPE=2,RPTEN) SRC:123456 DST:14F617 01010000 (ACK_STATUS CHANNEL:01 +STATUS:00) +2012.04.01 20:03:18.511 2: SYS HMUSB RCV L:0C N:8A CMD:A041 +(TYPE=65,BIDI,RPTEN) SRC:14F617 DST:123456 018100 + +Grüße aus Wien +Peter