diff --git a/fhem/FHEM/00_CUL.pm b/fhem/FHEM/00_CUL.pm index ae9bf67d4..cfb876003 100755 --- a/fhem/FHEM/00_CUL.pm +++ b/fhem/FHEM/00_CUL.pm @@ -15,11 +15,7 @@ sub CUL_ReadAnswer($$$$); sub CUL_Ready($); sub CUL_Write($$$); -sub CUL_OpenDev($$); -sub CUL_CloseDev($); sub CUL_SimpleWrite(@); -sub CUL_SimpleRead($); -sub CUL_Disconnected($); my %gets = ( # Name, Data to send to the CUL, Regexp for the answer "ccconf" => 1, @@ -79,6 +75,8 @@ CUL_Initialize($) { my ($hash) = @_; + require "$attr{global}{modpath}/FHEM/DevIo.pm"; + # Provider $hash->{ReadFn} = "CUL_Read"; $hash->{WriteFn} = "CUL_Write"; @@ -96,6 +94,7 @@ CUL_Initialize($) "fhtsoftbuffer:1,0 sendpool addvaltrigger " . "rfmode:SlowRF,HomeMatic hmId hmProtocolEvents"; $hash->{ShutdownFn} = "CUL_Shutdown"; + } ##################################### @@ -112,7 +111,7 @@ CUL_Define($$) return $msg; } - CUL_CloseDev($hash); + DevIo_CloseDev($hash); my $name = $a[0]; my $dev = $a[2]; @@ -145,7 +144,7 @@ CUL_Define($$) } $hash->{DeviceName} = $dev; - my $ret = CUL_OpenDev($hash, 0); + my $ret = DevIo_OpenDev($hash, 0, "CUL_DoInit"); return $ret; } @@ -169,7 +168,7 @@ CUL_Undef($$) } CUL_SimpleWrite($hash, "X00"); # Switch reception off, it may hang up the CUL - CUL_CloseDev($hash); + DevIo_CloseDev($hash); return undef; } @@ -450,7 +449,7 @@ READEND: CUL_SimpleWrite($hash, $gets{$a[1]}[0] . $arg); ($err, $msg) = CUL_ReadAnswer($hash, $a[1], 0, $gets{$a[1]}[1]); if(!defined($msg)) { - CUL_Disconnected($hash); + DevIo_Disconnected($hash); $msg = "No answer"; } elsif($a[1] eq "uptime") { # decode it @@ -540,7 +539,7 @@ CUL_DoInit($) CUL_SimpleWrite($hash, "T01" . $hash->{FHTID}); } - $hash->{STATE} = "Initialized" if(!$hash->{STATE}); + $hash->{STATE} = "Initialized"; # Reset the counter delete($hash->{XMIT_TIME}); @@ -586,12 +585,12 @@ CUL_ReadAnswer($$$$) if($nfound < 0) { next if ($! == EAGAIN() || $! == EINTR() || $! == 0); my $err = $!; - CUL_Disconnected($hash); + DevIo_Disconnected($hash); return("CUL_ReadAnswer $arg: $err", undef); } return ("Timeout reading answer for get $arg", undef) if($nfound == 0); - $buf = CUL_SimpleRead($hash); + $buf = DevIo_SimpleRead($hash); return ("No data", undef) if(!defined($buf)); } @@ -784,20 +783,10 @@ CUL_Read($) { my ($hash) = @_; - my $buf = CUL_SimpleRead($hash); + my $buf = DevIo_SimpleRead($hash); + return "" if(!defined($buf)); my $name = $hash->{NAME}; - ########### - # Lets' try again: Some drivers return len(0) on the first read... - if(defined($buf) && length($buf) == 0) { - $buf = CUL_SimpleRead($hash); - } - - if(!defined($buf) || length($buf) == 0) { - CUL_Disconnected($hash); - return ""; - } - my $culdata = $hash->{PARTIAL}; Log 5, "CUL/RAW: $culdata/$buf"; $culdata .= $buf; @@ -842,8 +831,7 @@ CUL_Parse($$$$$) my $len = length($dmsg); if($fn eq "F" && $len >= 9) { # Reformat for 10_FS20.pm - - CUL_AddFS20Queue($iohash, ""); # Block immediate replies + CUL_AddFS20Queue($iohash, ""); # Delay immediate replies $dmsg = sprintf("81%02x04xx0101a001%s00%s", $len/2+7, substr($dmsg,1,6), substr($dmsg,7)); $dmsg = lc($dmsg); @@ -922,7 +910,7 @@ CUL_Ready($) { my ($hash) = @_; - return CUL_OpenDev($hash, 1) + return DevIo_OpenDev($hash, 1, "CUL_DoInit") if($hash->{STATE} eq "disconnected"); # This is relevant for windows/USB only @@ -956,221 +944,6 @@ CUL_SimpleWrite(@) select(undef, undef, undef, 0.001); } -######################## -sub -CUL_SimpleRead($) -{ - 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)); - - } - return $buf; -} - -######################## -sub -CUL_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}); - - } - ($dev, undef) = split("@", $dev); # Remove the baudrate - delete($selectlist{"$name.$dev"}); - delete($readyfnlist{"$name.$dev"}); - delete($hash->{FD}); -} - -######################## -sub -CUL_OpenDev($$) -{ - my ($hash, $reopen) = @_; - my $dev = $hash->{DeviceName}; - my $name = $hash->{NAME}; - my $po; - my $baudrate; - ($dev, $baudrate) = split("@", $dev); - - - $hash->{PARTIAL} = ""; - Log 3, "CUL opening $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 $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; - } - - - } 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 $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, "CUL 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, "CUL $dev reappeared ($name)"; - } else { - Log 3, "CUL device opened"; - } - - $hash->{STATE}=""; # Allow InitDev to set the state - my $ret = CUL_DoInit($hash); - - if($ret) { - CUL_CloseDev($hash); - Log 1, "Cannot init $dev, ignoring it"; - } - - DoTrigger($name, "CONNECTED") if($reopen); - return $ret; -} - -sub -CUL_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"; - CUL_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 CUL_Attr(@) { diff --git a/fhem/FHEM/00_HMLAN.pm b/fhem/FHEM/00_HMLAN.pm index 82e27ea87..d510c42c5 100755 --- a/fhem/FHEM/00_HMLAN.pm +++ b/fhem/FHEM/00_HMLAN.pm @@ -11,11 +11,7 @@ sub HMLAN_Write($$$); sub HMLAN_ReadAnswer($$$); sub HMLAN_uptime($); -sub HMLAN_OpenDev($$); -sub HMLAN_CloseDev($); sub HMLAN_SimpleWrite(@); -sub HMLAN_SimpleRead($); -sub HMLAN_Disconnected($); my %sets = ( "hmPairForSec" => "HomeMatic", @@ -27,6 +23,8 @@ HMLAN_Initialize($) { my ($hash) = @_; + require "$attr{global}{modpath}/FHEM/DevIo.pm"; + # Provider $hash->{ReadFn} = "HMLAN_Read"; $hash->{WriteFn} = "HMLAN_Write"; @@ -58,11 +56,11 @@ HMLAN_Define($$) Log 2, $msg; return $msg; } - HMLAN_CloseDev($hash); + DevIo_CloseDev($hash); my $name = $a[0]; my $dev = $a[2]; - $dev .= ":1000" if($dev !~ m/:/); + $dev .= ":1000" if($dev !~ m/:/ && $dev ne "none" && $dev !~ m/\@/); $attr{$name}{hmId} = sprintf("%06X", time() % 0xffffff); # Will be overwritten if($dev eq "none") { @@ -71,7 +69,7 @@ HMLAN_Define($$) return undef; } $hash->{DeviceName} = $dev; - my $ret = HMLAN_OpenDev($hash, 0); + my $ret = DevIo_OpenDev($hash, 0, "HMLAN_DoInit"); return $ret; } @@ -94,7 +92,7 @@ HMLAN_Undef($$) } } - HMLAN_CloseDev($hash); + DevIo_CloseDev($hash); return undef; } @@ -168,12 +166,12 @@ HMLAN_ReadAnswer($$$) if($nfound < 0) { next if ($! == EAGAIN() || $! == EINTR() || $! == 0); my $err = $!; - HMLAN_Disconnected($hash); + DevIo_Disconnected($hash); return("HMLAN_ReadAnswer $arg: $err", undef); } return ("Timeout reading answer for get $arg", undef) if($nfound == 0); - $buf = HMLAN_SimpleRead($hash); + $buf = DevIo_SimpleRead($hash); return ("No data", undef) if(!defined($buf)); if($buf) { @@ -223,14 +221,10 @@ HMLAN_Read($) { my ($hash) = @_; - my $buf = HMLAN_SimpleRead($hash); + my $buf = DevIo_SimpleRead($hash); + return "" if(!defined($buf)); my $name = $hash->{NAME}; - if(!defined($buf) || length($buf) == 0) { - HMLAN_Disconnected($hash); - return ""; - } - my $hmdata = $hash->{PARTIAL}; Log 5, "HMLAN/RAW: $hmdata/$buf"; $hmdata .= $buf; @@ -325,7 +319,7 @@ HMLAN_Ready($) { my ($hash) = @_; - return HMLAN_OpenDev($hash, 1); + return DevIo_OpenDev($hash, 1, "HMLAN_DoInit"); } ######################## @@ -345,88 +339,10 @@ HMLAN_SimpleWrite(@) ######################## sub -HMLAN_SimpleRead($) -{ - my ($hash) = @_; - - if($hash->{TCPDev}) { - my $buf; - if(!defined(sysread($hash->{TCPDev}, $buf, 256))) { - HMLAN_Disconnected($hash); - return undef; - } - return $buf; - } - return undef; -} - -######################## -sub -HMLAN_CloseDev($) +HMLAN_DoInit($) { 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}); -} - -######################## -sub -HMLAN_OpenDev($$) -{ - my ($hash, $reopen) = @_; - my $dev = $hash->{DeviceName}; - my $name = $hash->{NAME}; - - $hash->{PARTIAL} = ""; - Log 3, "HMLAN opening $name device $dev" - if(!$reopen); - - if($dev =~ m/^.+:\d+$/) { # 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 $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; - - InternalTimer(gettimeofday()+25, "HMLAN_KeepAlive", $hash, 0); - } - - if($reopen) { - Log 1, "HMLAN $dev reappeared ($name)"; - } else { - Log 3, "HMLAN device opened"; - } my $id = AttrVal($name, "hmId", undef); my $key = AttrVal($name, "hmKey", ""); # 36(!) hex digits @@ -440,10 +356,8 @@ HMLAN_OpenDev($$) HMLAN_SimpleWrite($hash, "Y03,00,"); HMLAN_SimpleWrite($hash, "T$s2000,04,00,00000000"); - $hash->{STATE}="Initialized"; - - DoTrigger($name, "CONNECTED") if($reopen); - return ""; + InternalTimer(gettimeofday()+25, "HMLAN_KeepAlive", $hash, 0); + return undef; } ##################################### @@ -456,27 +370,4 @@ HMLAN_KeepAlive($) InternalTimer(gettimeofday()+25, "HMLAN_KeepAlive", $hash, 1); } - - -sub -HMLAN_Disconnected($) -{ - my $hash = shift; - my $dev = $hash->{DeviceName}; - my $name = $hash->{NAME}; - - return if(!defined($hash->{FD})); # Already deleted or RFR - - Log 1, "$dev disconnected, waiting to reappear"; - RemoveInternalTimer($hash); - HMLAN_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; diff --git a/fhem/FHEM/00_KM271.pm b/fhem/FHEM/00_KM271.pm index f58146eb8..19ed978cb 100755 --- a/fhem/FHEM/00_KM271.pm +++ b/fhem/FHEM/00_KM271.pm @@ -13,10 +13,6 @@ use Time::HiRes qw(gettimeofday); sub KM271_Read($); sub KM271_Ready($); -sub KM271_OpenDev($); -sub KM271_CloseDev($); -sub KM271_SimpleWrite(@); -sub KM271_SimpleRead($); sub KM271_crc($); sub KM271_setbits($$); sub KM271_GetReading($$); @@ -180,6 +176,8 @@ KM271_Initialize($) { my ($hash) = @_; + require "$attr{global}{modpath}/FHEM/DevIo.pm"; + $hash->{ReadFn} = "KM271_Read"; $hash->{ReadyFn} = "KM271_Ready"; @@ -208,7 +206,7 @@ KM271_Define($$) return "wrong syntax: define KM271 [devicename|none]" if(@a != 3); - KM271_CloseDev($hash); + DevIo_CloseDev($hash); my $name = $a[0]; my $dev = $a[2]; @@ -218,7 +216,7 @@ KM271_Define($$) } $hash->{DeviceName} = $dev; - my $ret = KM271_OpenDev($hash); + my $ret = DevIo_OpenDev($hash, 0, "KM271_DoInit"); return $ret; } @@ -228,7 +226,7 @@ sub KM271_Undef($$) { my ($hash, $arg) = @_; - KM271_CloseDev($hash); + DevIo_CloseDev($hash); return undef; } @@ -269,7 +267,7 @@ KM271_Set($@) my $data = ($val ? sprintf($fmt, $val) : $fmt); push @{$hash->{SENDBUFFER}}, $data; - KM271_SimpleWrite($hash, "02") if(!$hash->{WAITING}); + DevIo_SimpleWrite($hash, "02") if(!$hash->{WAITING}); return undef; } @@ -284,45 +282,40 @@ KM271_Read($) my $name = $hash->{NAME}; my ($data, $crc); - my $buf = KM271_SimpleRead($hash); - Log 5, "KM271RAW: " . unpack('H*', $buf); - - if(!defined($buf)) { - Log 1, "$name: EOF"; - KM271_CloseDev($hash); - return; - } + my $buf = DevIo_SimpleRead($hash); + return "" if(!defined($buf)); $buf = unpack('H*', $buf); + Log 5, "KM271RAW: $buf"; if(@{$hash->{SENDBUFFER}} || $hash->{DATASENT}) { # Send data if($buf eq "02") { # KM271 Wants to send, override - KM271_SimpleWrite($hash, "02"); + DevIo_SimpleWrite($hash, "02"); return; } if($buf eq "10") { if($hash->{DATASENT}) { delete($hash->{DATASENT}); - KM271_SimpleWrite($hash, "02") if(@{$hash->{SENDBUFFER}}); + DevIo_SimpleWrite($hash, "02") if(@{$hash->{SENDBUFFER}}); return; } $data = pop @{ $hash->{SENDBUFFER} }; $data =~ s/10/1010/g; $crc = KM271_crc($data); - KM271_SimpleWrite($hash, $data."1003$crc"); # Send the data + DevIo_SimpleWrite($hash, $data."1003$crc"); # Send the data } if($buf eq "15") { # NACK from the KM271 Log 1, "$name: NACK!"; delete($hash->{DATASENT}); - KM271_SimpleWrite($hash, "02") if(@{$hash->{SENDBUFFER}}); + DevIo_SimpleWrite($hash, "02") if(@{$hash->{SENDBUFFER}}); return; } } elsif($buf eq "02") { # KM271 Wants to send - KM271_SimpleWrite($hash, "10"); # We are ready + DevIo_SimpleWrite($hash, "10"); # We are ready $hash->{PARTIAL} = ""; $hash->{WAITING} = 1; return; @@ -339,12 +332,12 @@ KM271_Read($) if(KM271_crc($data) ne $crc) { Log 1, "Wrong CRC in $hash->{PARTIAL}: $crc vs. ". KM271_crc($data); - KM271_SimpleWrite($hash, "15"); # NAK - KM271_SimpleWrite($hash, "02") if(@{$hash->{SENDBUFFER}}); # want to send + DevIo_SimpleWrite($hash, "15"); # NAK + DevIo_SimpleWrite($hash, "02") if(@{$hash->{SENDBUFFER}}); # want to send return; } - KM271_SimpleWrite($hash, "10"); # ACK, Data received ok + DevIo_SimpleWrite($hash, "10"); # ACK, Data received ok $data =~ s/1010/10/g; @@ -406,97 +399,21 @@ KM271_Ready($) { my ($hash) = @_; + return DevIo_OpenDev($hash, 1, undef) + if($hash->{STATE} eq "disconnected"); + # This is relevant for windows/USB only - my $po = $hash->{Dev}; + my $po = $hash->{USBDev}; my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status; return ($InBytes>0); } -######################## sub -KM271_SimpleWrite(@) -{ - my ($hash, $msg) = @_; - Log 3, "KM271 SimpleWrite $msg" if(length($msg) != 2); - $hash->{Dev}->write(pack('H*',$msg)) if($hash->{DeviceName}); -} - -######################## -sub -KM271_SimpleRead($) +KM271_DoInit($) { my ($hash) = @_; - return $hash->{Dev}->input() if($hash->{Dev}); - return undef; -} - -######################## -sub -KM271_CloseDev($) -{ - my ($hash) = @_; - my $name = $hash->{NAME}; - my $dev = $hash->{DeviceName}; - - return if(!$dev); # "none" - - if($hash->{Dev}) { - $hash->{Dev}->close() ; - delete($hash->{Dev}); - - } - delete($selectlist{"$name.$dev"}); - delete($readyfnlist{"$name.$dev"}); - delete($hash->{FD}); - delete($hash->{DeviceName}); -} - -######################## -sub -KM271_OpenDev($) -{ - my ($hash) = @_; - my $dev = $hash->{DeviceName}; - my $name = $hash->{NAME}; - my $po; - - $hash->{PARTIAL} = ""; - Log 3, "KM271 opening $name device $dev"; - - if ($^O=~/Win/) { - require Win32::SerialPort; - $po = new Win32::SerialPort ($dev); - } else { - require Device::SerialPort; - $po = new Device::SerialPort ($dev); - } - - if(!$po) { - Log(3, "Can't open $dev: $!"); - return ""; - } - $hash->{Dev} = $po; - if( $^O =~ /Win/ ) { - $readyfnlist{"$name.$dev"} = $hash; - } else { - $hash->{FD} = $po->FILENO; - delete($readyfnlist{"$name.$dev"}); - $selectlist{"$name.$dev"} = $hash; - } - - $po->reset_error(); - $po->baudrate(2400); - $po->databits(8); - $po->parity('none'); - $po->stopbits(1); - $po->handshake('none'); - - $hash->{STATE} = "Initialized"; - push @{$hash->{SENDBUFFER}}, "EE0000"; - KM271_SimpleWrite($hash, "02"); # STX - - Log 3, "$dev opened"; + DevIo_SimpleWrite($hash, "02"); # STX return undef; } diff --git a/fhem/FHEM/00_TCM.pm b/fhem/FHEM/00_TCM.pm index 781f49db7..0b3ba482e 100755 --- a/fhem/FHEM/00_TCM.pm +++ b/fhem/FHEM/00_TCM.pm @@ -26,11 +26,6 @@ sub TCM_ReadAnswer($$); sub TCM_Ready($); sub TCM_Write($$$); -sub TCM_OpenDev($$); -sub TCM_CloseDev($); -sub TCM_SimpleWrite($$); -sub TCM_SimpleRead($); -sub TCM_Disconnected($); sub TCM_Parse120($$$); sub TCM_CRC8($); sub TCM_CSUM($); @@ -40,6 +35,8 @@ TCM_Initialize($) { my ($hash) = @_; + require "$attr{global}{modpath}/FHEM/DevIo.pm"; + # Provider $hash->{ReadFn} = "TCM_Read"; $hash->{WriteFn} = "TCM_Write"; @@ -70,7 +67,7 @@ TCM_Define($$) "{devicename[\@baudrate]|ip:port}" if(@a != 4 || $model !~ m/^(120|310)$/); - TCM_CloseDev($hash); + DevIo_CloseDev($hash); my $dev = $a[3]; if($dev eq "none") { @@ -81,7 +78,7 @@ TCM_Define($$) $hash->{DeviceName} = $dev; $hash->{MODEL} = $model; - my $ret = TCM_OpenDev($hash, 0); + my $ret = DevIo_OpenDev($hash, 0, undef); return $ret; } @@ -114,7 +111,7 @@ TCM_Write($$$) } Log $ll5, "$hash->{NAME} sending $bstring"; - TCM_SimpleWrite($hash, $bstring); + DevIo_SimpleWrite($hash, $bstring); } ##################################### @@ -180,22 +177,13 @@ TCM_Read($) { my ($hash) = @_; - my $buf = TCM_SimpleRead($hash); + my $buf = DevIo_SimpleRead($hash); + return "" if(!defined($buf)); + my $name = $hash->{NAME}; my $ll5 = GetLogLevel($name,5); my $ll2 = GetLogLevel($name,2); - ########### - # Lets' try again: Some drivers return len(0) on the first read... - if(defined($buf) && length($buf) == 0) { - $buf = TCM_SimpleRead($hash); - } - - if(!defined($buf) || length($buf) == 0) { - TCM_Disconnected($hash); - return ""; - } - my $data = $hash->{PARTIAL} . uc(unpack('H*', $buf)); Log $ll5, "$name/RAW: $data"; @@ -401,7 +389,7 @@ TCM_Ready($) { my ($hash) = @_; - return TCM_OpenDev($hash, 1) + return DevIo_OpenDev($hash, 1, undef) if($hash->{STATE} eq "disconnected"); # This is relevant for windows/USB only @@ -410,193 +398,6 @@ TCM_Ready($) return ($InBytes>0); } -######################## -# Input is HEX, with header and CRC -sub -TCM_SimpleWrite($$) -{ - my ($hash, $msg) = @_; - return if(!$hash); - #Log 1, "SW: $msg"; - $msg = pack('H*', $msg); - $hash->{USBDev}->write($msg) if($hash->{USBDev}); - syswrite($hash->{TCPDev}, $msg) if($hash->{TCPDev}); - select(undef, undef, undef, 0.001); -} - -######################## -sub -TCM_SimpleRead($) -{ - my ($hash) = @_; - my $buf; - - $buf = $hash->{USBDev}->input() if($hash->{USBDev}); - $buf = sysread($hash->{TCPDev}, $buf, 256) if($hash->{TCPDev}); - return $buf; -} - -######################## -sub -TCM_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}); - - } - - ($dev, undef) = split("@", $dev); # Remove the baudrate - delete($selectlist{"$name.$dev"}); - delete($readyfnlist{"$name.$dev"}); - delete($hash->{FD}); -} - -######################## -sub -TCM_OpenDev($$) -{ - my ($hash, $reopen) = @_; - my $dev = $hash->{DeviceName}; - my $name = $hash->{NAME}; - my $po; - my $baudrate; - ($dev, $baudrate) = split("@", $dev); - if(!$baudrate) { - $baudrate = 9600 if($hash->{MODEL} == 120); - $baudrate = 57600 if($hash->{MODEL} == 310); - } - - - $hash->{PARTIAL} = ""; - Log 3, "TCM opening $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 $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; - - } 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 $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; - } - - $po->reset_error(); - $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, "TCM $dev reappeared ($name)"; - } else { - Log 3, "TCM device opened"; - } - - $hash->{STATE}="connected"; - - DoTrigger($name, "CONNECTED") if($reopen); - return ""; -} - -sub -TCM_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"; - TCM_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"); -} - my %gets120 = ( "sensitivity" => "AB48", "idbase" => "AB58", @@ -711,7 +512,7 @@ TCM_Set($@) ############################## if($hash->{MODEL} eq "120") { if($cmdHex eq "") { # wake is very special - TCM_SimpleWrite($hash, "AA"); + DevIo_SimpleWrite($hash, "AA"); return ""; } @@ -767,12 +568,12 @@ TCM_ReadAnswer($$) if($nfound < 0) { next if ($! == EAGAIN() || $! == EINTR() || $! == 0); my $err = $!; - TCM_Disconnected($hash); + DevIo_Disconnected($hash); return("TCM_ReadAnswer $err", undef); } return ("Timeout reading answer for $arg", undef) if($nfound == 0); - $buf = TCM_SimpleRead($hash); + $buf = DevIo_SimpleRead($hash); return ("No data", undef) if(!defined($buf)); }