2011-06-30 10:18:08 +00:00
##############################################
2011-11-12 07:51:08 +00:00
# $Id$
2015-10-06 18:01:49 +00:00
# ABU 20150916 removed print: simpleWriteDate, cleaned init
# ABU 20150918 fixed deprecated warning, fixed warning related to hex-conversion in simple-write
2015-11-23 19:15:39 +00:00
# ABU 20151123 added error-label in getGroup. Responsible for error-handling, if knxd is not accesible
2015-12-21 19:07:34 +00:00
# ABU 20151213 changed message-check in decode_tpuart() to avoid ignore while receiving repeated messages
2016-04-03 19:44:22 +00:00
# ABU 20160308 remoced set, get. Changed loglevel to verbose. Added KNX/EIB-Split. Added EIB-backward-compatibility.
# ABU 20160309 fixed log2
# ABU 20160310 repaired dispatch events - inform EIB, only is useEIB is set
2016-05-15 19:28:08 +00:00
# ABU 20160515 removed compatibility flag for EIB
2016-05-16 15:12:37 +00:00
# ABU 20160516 added log entry for non-compatibility of tul
2016-06-13 18:01:40 +00:00
# ABU 20160613 changed log entry for startup
2016-11-08 19:15:55 +00:00
# ABU 20161108 added knxd. Added doku as well. Added summary. Treat it like eibd. See thread #58375
2017-01-08 16:52:19 +00:00
# ABU 20170102 fixed write-mechanism, added mod for extended adressing (thx to its2bit)
2017-01-10 19:24:35 +00:00
# ABU 20170110 removed mod for extended adressing
2017-04-28 08:04:26 +00:00
# ABU 20170427 reintegrated mechanism for extenden GAD-Support
# ABU 20170427 cleaned logs
2017-10-14 18:48:39 +00:00
# ABU 20171006 deactivated default-log-entry
# ABU 20171006 EIB requires different handling of extended GAD --> added
2017-12-15 18:39:59 +00:00
# docM 20171106 fixed problem when OBD-IP adapter is offline during FHEM startup
2017-10-14 18:48:39 +00:00
2015-10-06 18:01:49 +00:00
2011-06-30 10:18:08 +00:00
package main ;
use strict ;
use warnings ;
use Time::HiRes qw( gettimeofday ) ;
sub TUL_Attr (@) ;
sub TUL_Clear ($) ;
sub TUL_Parse ($$$$$) ;
sub TUL_Read ($) ;
sub TUL_Ready ($) ;
sub TUL_Write ($$$) ;
sub TUL_OpenDev ($$) ;
sub TUL_CloseDev ($) ;
sub TUL_SimpleWrite (@) ;
sub TUL_SimpleRead ($) ;
sub TUL_Disconnected ($) ;
sub TUL_Shutdown ($) ;
my % gets = ( # Name, Data to send to the TUL, Regexp for the answer
"raw" = > [ "r" , '.*' ] ,
) ;
my % sets = (
"raw" = > "" ,
) ;
2016-04-03 19:44:22 +00:00
my $ clients = ":KNX:EIB:" ;
2011-06-30 10:18:08 +00:00
my % matchList = (
2016-04-03 19:44:22 +00:00
"2:KNX" = > "^C.*" ,
2011-06-30 10:18:08 +00:00
"3:EIB" = > "^B.*" ,
) ;
2017-10-14 18:48:39 +00:00
my $ useEIB = '0' ;
2011-06-30 10:18:08 +00:00
sub
TUL_Initialize ( $ )
{
my ( $ hash ) = @ _ ;
# Provider
$ hash - > { ReadFn } = "TUL_Read" ;
$ hash - > { WriteFn } = "TUL_Write" ;
$ hash - > { ReadyFn } = "TUL_Ready" ;
# Normal devices
$ hash - > { DefFn } = "TUL_Define" ;
$ hash - > { UndefFn } = "TUL_Undef" ;
$ hash - > { StateFn } = "TUL_SetState" ;
$ hash - > { AttrFn } = "TUL_Attr" ;
2015-10-06 18:01:49 +00:00
2016-04-03 19:44:22 +00:00
$ hash - > { AttrList } = "do_not_notify:1,0 " .
"dummy:1,0 " .
"showtime:1,0 " .
"verbose:0,1,2,3,4,5 " .
"useEIB:1,0 " ;
2015-10-06 18:01:49 +00:00
2011-06-30 10:18:08 +00:00
$ hash - > { ShutdownFn } = "TUL_Shutdown" ;
2015-10-06 18:01:49 +00:00
2011-06-30 10:18:08 +00:00
}
#####################################
sub
TUL_Define ( $$ )
{
2016-04-03 19:44:22 +00:00
my ( $ hash , $ def ) = @ _ ;
my @ a = split ( "[ \t][ \t]*" , $ def ) ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
if ( @ a < 4 )
{
my $ msg = "wrong syntax: define <name> TUL <devicename> <device addr> [<line def in hex>]" ;
return $ msg ;
}
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
TUL_CloseDev ( $ hash ) ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
my $ name = $ a [ 0 ] ;
my $ dev = $ a [ 2 ] ;
my $ devaddr = tul_str2hex ( $ a [ 3 ] ) ;
my $ linedef = substr ( tul_str2hex ( $ a [ 4 ] ) , 0 , 2 ) if ( @ a > 4 ) ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
if ( $ dev eq "none" )
{
Log3 ( $ name , 1 , "device is none, commands will be echoed only" ) ;
$ attr { $ name } { dummy } = 1 ;
return undef ;
}
#Set attributes in order to control backward-compatibility
2016-05-15 19:28:08 +00:00
#$attr{$name}{useEIB} = 1;
2017-10-14 18:48:39 +00:00
#Log3 ($name, 0, "Using EIB is deprecated. Please migrate to KNX soon. Module 10_EIB is not maintained any longer. If you still want to use the module EIB,
#please set the attribute useEIB to 1 within the tul-device. Please keep in mind, that 10_KNX has a changed syntax regarding the definition, arguments and readings. Please refer to the commandref.
#As well 10_EIB and 10_KNX are compatible to daemon eibd and knxd.") if (AttrVal($name, "useEIB", 0) =~ m/0/);
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
$ hash - > { DeviceName } = $ dev ;
$ hash - > { DeviceAddress } = $ devaddr ;
$ hash - > { Clients } = $ clients ;
$ hash - > { MatchList } = \ % matchList ;
$ hash - > { AckLineDef } = $ linedef ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
my $ ret = TUL_OpenDev ( $ hash , 0 ) ;
return $ ret ;
2011-06-30 10:18:08 +00:00
}
#####################################
sub
TUL_Undef ( $$ )
{
2016-04-03 19:44:22 +00:00
my ( $ hash , $ arg ) = @ _ ;
my $ name = $ hash - > { NAME } ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
foreach my $ d ( sort keys % defs )
{
if ( defined ( $ defs { $ d } ) && defined ( $ defs { $ d } { IODev } ) && $ defs { $ d } { IODev } == $ hash )
{
my $ lev = ( $ reread_active ? 4 : 2 ) ;
2017-12-15 18:39:59 +00:00
Log ( GetLogLevel ( $ name , $ lev ) , "deleting port for $d" ) ;
2016-04-03 19:44:22 +00:00
delete $ defs { $ d } { IODev } ;
}
}
2012-01-24 19:53:16 +00:00
2016-04-03 19:44:22 +00:00
TUL_CloseDev ( $ hash ) ;
return undef ;
2011-06-30 10:18:08 +00:00
}
#####################################
2016-04-03 19:44:22 +00:00
sub TUL_Shutdown ($)
2011-06-30 10:18:08 +00:00
{
2016-04-03 19:44:22 +00:00
my ( $ hash ) = @ _ ;
TUL_CloseDev ( $ hash ) ;
return undef ;
2011-06-30 10:18:08 +00:00
}
#####################################
sub
TUL_SetState ( $$ $$ )
{
2016-04-03 19:44:22 +00:00
my ( $ hash , $ tim , $ vt , $ val ) = @ _ ;
return undef ;
2011-06-30 10:18:08 +00:00
}
sub
TUL_Clear ( $ )
{
2016-04-03 19:44:22 +00:00
my $ hash = shift ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
#Clear the pipe
#TUL has no pipe....
2011-06-30 10:18:08 +00:00
}
#####################################
sub
TUL_DoInit ( $ )
{
2016-04-03 19:44:22 +00:00
my $ hash = shift ;
my $ name = $ hash - > { NAME } ;
my $ err ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
TUL_Clear ( $ hash ) ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
# send any initializing request if needed
2017-12-15 18:39:59 +00:00
# TODO move to device init
# docM 2017-11-05
# moved openGroupSocket() to TUL_OpenDev.
# return 1 unless openGroupSocket($hash);
# /docM
2012-03-25 12:50:50 +00:00
2016-04-03 19:44:22 +00:00
# reset buffer
purgeReceiverBuf ( $ hash ) ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
$ hash - > { STATE } = "Initialized" if ( ! $ hash - > { STATE } ) ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
# Reset the counter
delete ( $ hash - > { XMIT_TIME } ) ;
delete ( $ hash - > { NR_CMD_LAST_H } ) ;
return undef ;
2011-06-30 10:18:08 +00:00
}
#####################################
sub
TUL_Write ( $$ $ )
{
2016-04-03 19:44:22 +00:00
my ( $ hash , $ fn , $ msg ) = @ _ ;
my $ name = $ hash - > { NAME } ;
return if ( ! defined ( $ fn ) ) ;
2017-12-15 18:39:59 +00:00
# docm 2017-11-05
# Discard message if TUL is disconnected
return if ( $ hash - > { STATE } eq "disconnected" ) ;
# /docm
2016-04-03 19:44:22 +00:00
#Discard message, if not set to backward-compatibility
2017-10-14 18:48:39 +00:00
if ( ( $ useEIB =~ m/0/ ) and ( $ fn =~ m/\^B/ ) )
2016-04-03 19:44:22 +00:00
{
Log3 ( $ name , 0 , "EIB is no longer supported. Message discarded." ) ;
return ;
}
2015-10-06 18:01:49 +00:00
2016-04-03 19:44:22 +00:00
Log3 ( $ name , 5 , "sending $fn$msg" ) ;
my $ bstring = "$fn$msg" ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
TUL_SimpleWrite ( $ hash , $ bstring ) ;
2011-06-30 10:18:08 +00:00
}
#####################################
# called from the global loop, when the select for hash->{FD} reports data
sub
TUL_Read ( $ )
{
2016-04-03 19:44:22 +00:00
my ( $ hash ) = @ _ ;
#reset the refused flag, so we can check if a telegram was refused
# and therefor we did not get a response
$ hash - > { REFUSED } = undef ;
my $ buf = TUL_SimpleRead ( $ hash ) ;
my $ name = $ hash - > { NAME } ;
# check if refused
if ( defined ( $ hash - > { REFUSED } ) )
{
Log3 ( $ name , 3 , "TUL $name refused message: $hash->{REFUSED}" ) ;
$ hash - > { REFUSED } = undef ;
return "" ;
}
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
###########
# Lets' try again: Some drivers return len(0) on the first read...
if ( defined ( $ buf ) && length ( $ buf ) == 0 )
{
$ buf = TUL_SimpleRead ( $ hash ) ;
}
if ( ! defined ( $ buf ) || length ( $ buf ) == 0 )
{
TUL_Disconnected ( $ hash ) ;
return "" ;
}
#place KNX-Message
2017-10-14 18:48:39 +00:00
TUL_Parse ( $ hash , $ hash , $ name , "B" . $ buf , $ hash - > { initString } ) if ( $ useEIB =~ m/1/ ) ;
2016-04-03 19:44:22 +00:00
#place EIB-Message
TUL_Parse ( $ hash , $ hash , $ name , "C" . $ buf , $ hash - > { initString } ) ;
2011-06-30 10:18:08 +00:00
}
sub
TUL_Parse ( $$ $$ $ )
{
2016-04-03 19:44:22 +00:00
my ( $ hash , $ iohash , $ name , $ rmsg , $ initstr ) = @ _ ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
# there is nothing specal to do at the moment.
# just dispatch
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
my $ dmsg = $ rmsg ;
Log3 ( $ name , 4 , "$name: $dmsg" ) ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
$ hash - > { "${name}_MSGCNT" } + + ;
$ hash - > { "${name}_TIME" } = TimeNow ( ) ;
$ hash - > { RAWMSG } = $ rmsg ;
my % addvals = ( RAWMSG = > $ rmsg ) ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
Dispatch ( $ hash , $ dmsg , \ % addvals ) ;
2011-06-30 10:18:08 +00:00
}
#####################################
sub
TUL_Ready ( $ )
{
2016-04-03 19:44:22 +00:00
my ( $ hash ) = @ _ ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
return TUL_OpenDev ( $ hash , 1 ) if ( $ hash - > { STATE } eq "disconnected" ) ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
# This is relevant for windows/USB only
my $ po = $ hash - > { USBDev } ;
my ( $ BlockingFlags , $ InBytes , $ OutBytes , $ ErrorFlags ) = $ po - > status ;
return ( $ InBytes > 0 ) ;
2011-06-30 10:18:08 +00:00
}
########################
sub
TUL_SimpleWrite ( @ )
{
2016-04-03 19:44:22 +00:00
my ( $ hash , $ msg ) = @ _ ;
return if ( ! $ hash ) ;
# Msg must have the format B(w,r,p)g1g2g3v....
# w-> write, r-> read, p-> reply
# g1,g2,g3 are the hex parts of the group name
# v is a simple (1 Byte) or complex value (n bytes)
# For eibd we need a more elaborate structure
2017-04-28 08:04:26 +00:00
# Old
#if($msg =~ /^[BC](.)(.{4})(.*)$/)
# New: its2bit
2017-10-14 18:48:39 +00:00
#if($msg =~ /^[BC](.)(.{5})(.*)$/)
#extended adressing
if ( ( ( $ useEIB =~ m/1/ ) and ( $ msg =~ /^[BC](.)(.{4})(.*)$/ ) ) or ( ( $ useEIB =~ m/0/ ) and ( $ msg =~ /^[BC](.)(.{5})(.*)$/ ) ) )
2015-10-06 18:01:49 +00:00
{
2016-04-03 19:44:22 +00:00
my $ eibmsg ;
if ( $ 1 eq "w" )
{
$ eibmsg - > { 'type' } = 'write' ;
}
elsif ( $ 1 eq "r" )
{
$ eibmsg - > { 'type' } = 'read' ;
}
elsif ( $ 1 eq "p" )
{
$ eibmsg - > { 'type' } = 'reply' ;
}
$ eibmsg - > { 'dst' } = $ 2 ;
my $ hexvalues = $ 3 ;
#The array has to have a given length. During Hex-conversion Trailing
#0 are recognizes for warnings.
#Therefore we backup the length, trim, and reappend the 0
#
#save length and trim right side
my $ strLen = length ( $ hexvalues ) / 2 ;
$ hexvalues =~ s/\s+$// ;
#convert hex-string to array with dezimal values
my @ data = map hex ( $ _ ) , $ hexvalues =~ /(..)/g ;
#re-append 0x00
for ( my $ i = 0 ; $ strLen - scalar @ data ; $ i + + )
{
push ( @ data , 0 ) ;
}
2015-10-06 18:01:49 +00:00
2016-04-03 19:44:22 +00:00
# check: first byte is only allowed to contain data in the lower 6bits
# to make sure all is fine, we mask the first byte
$ data [ 0 ] = $ data [ 0 ] & 0x3f if ( defined ( $ data [ 0 ] ) ) ;
2013-05-01 22:41:47 +00:00
2016-04-03 19:44:22 +00:00
$ eibmsg - > { 'data' } = \ @ data ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
sendGroup ( $ hash , $ eibmsg ) ;
}
else
{
Log3 ( $ hash - > { NAME } , 1 , "Could not parse message $msg" ) ;
return undef ;
}
select ( undef , undef , undef , 0.001 ) ;
2011-06-30 10:18:08 +00:00
}
########################
sub
TUL_SimpleRead ( $ )
{
2016-04-03 19:44:22 +00:00
my ( $ hash ) = @ _ ;
my $ name = $ hash - > { NAME } ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
my $ msg = getGroup ( $ hash ) ;
if ( ! defined ( $ msg ) )
{
Log3 ( $ name , 4 , "No data received." ) ;
return undef ;
}
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
my $ type = $ msg - > { 'type' } ;
my $ dst = $ msg - > { 'dst' } ;
my $ src = $ msg - > { 'src' } ;
my @ bindata = @ { $ msg - > { 'data' } } ;
my $ data = "" ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
# convert bin data to hex
foreach my $ c ( @ bindata )
{
$ data . = sprintf ( "%02x" , $ c ) ;
}
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
Log3 ( $ name , 5 , "SimpleRead msg.type: $type, msg.src: $msg->{'src'}, msg.dst: $msg->{'dst'}" ) ;
Log3 ( $ name , 5 , "SimpleRead data: $data" ) ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
# we will build a string like:
# Bs1s2s3(w|r|p)g1g2g3v
# s -> src
my $ buf ;
#$buf = "C$src";
$ buf = $ src ;
if ( $ type eq "write" )
{
$ buf . = "w" ;
}
elsif ( $ type eq "read" )
{
$ buf . = "r" ;
}
else
{
$ buf . = "p" ;
}
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
$ buf . = $ dst ;
$ buf . = $ data ;
2011-06-30 10:18:08 +00:00
2017-12-15 18:39:59 +00:00
Log ( 4 , "SimpleRead: $buf\n" ) ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
return $ buf ;
2011-06-30 10:18:08 +00:00
}
########################
sub
TUL_CloseDev ( $ )
{
2016-04-03 19:44:22 +00:00
my ( $ hash ) = @ _ ;
my $ name = $ hash - > { NAME } ;
my $ dev = $ hash - > { DeviceName } ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
return if ( ! $ dev ) ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
if ( $ hash - > { TCPDev } )
{
$ hash - > { TCPDev } - > close ( ) ;
delete ( $ hash - > { TCPDev } ) ;
}
elsif ( $ hash - > { USBDev } )
{
$ hash - > { USBDev } - > close ( ) ;
delete ( $ hash - > { USBDev } ) ;
}
delete ( $ selectlist { "$name.$dev" } ) ;
delete ( $ readyfnlist { "$name.$dev" } ) ;
delete ( $ hash - > { FD } ) ;
2011-06-30 10:18:08 +00:00
}
########################
sub
TUL_OpenDev ( $$ )
{
2016-04-03 19:44:22 +00:00
my ( $ hash , $ reopen ) = @ _ ;
my $ dev = $ hash - > { DeviceName } ;
my $ name = $ hash - > { NAME } ;
my $ po ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
$ hash - > { PARTIAL } = "" ;
Log 3 , "TUL opening $name device $dev" if ( ! $ reopen ) ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
# eibd:host[:port]
2016-11-08 19:15:55 +00:00
#if($dev =~ m/^(eibd):(.+)$/)
if ( $ dev =~ m/^(eibd|knxd):(.+)$/ )
2016-04-03 19:44:22 +00:00
{
my $ host = $ 2 ;
my $ port = 6720 ;
#host:port
if ( $ host =~ m/^(.+):([0-9]+)$/ )
{
$ host = $ 1 ;
$ port = $ 2 ;
}
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
# 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.
return if ( $ hash - > { NEXT_OPEN } && time ( ) < $ hash - > { NEXT_OPEN } ) ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
my $ conn = IO::Socket::INET - > new ( PeerAddr = > $ host , PeerPort = > $ port , Proto = > 'tcp' ) ;
if ( $ conn )
{
delete ( $ hash - > { NEXT_OPEN } )
}
else
{
Log3 ( $ name , 3 , "Can't connect to $dev: $!" ) if ( ! $ reopen ) ;
$ readyfnlist { "$name.$dev" } = $ hash ;
$ hash - > { STATE } = "disconnected" ;
$ hash - > { NEXT_OPEN } = time ( ) + 60 ;
return "" ;
}
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
$ hash - > { DevType } = 'EIBD' ;
$ hash - > { TCPDev } = $ conn ;
$ hash - > { FD } = $ conn - > fileno ( ) ;
2017-12-15 18:39:59 +00:00
# docM 2017-11-05
# Call openGroupSocket() here, as it is part of device initialization.
if ( openGroupSocket ( $ hash ) )
{
Log ( 3 , "OpenDev: OBD response from $dev" ) if ( $ reopen ) ;
}
else
{
# failed to connect to OBD. Close socket and start polling
Log ( 3 , "OpenDev: No OBD response from $dev" ) if ( ! $ reopen ) ;
TUL_CloseDev ( $ hash ) ;
$ readyfnlist { "$name.$dev" } = $ hash ;
$ hash - > { STATE } = "disconnected" ;
$ hash - > { NEXT_OPEN } = time ( ) + 60 ;
return "" ;
}
# /docM
2016-04-03 19:44:22 +00:00
delete ( $ readyfnlist { "$name.$dev" } ) ;
$ selectlist { "$name.$dev" } = $ hash ;
}
# tpuart:ttydev[@baudrate] / USB/Serial device
elsif ( $ dev =~ m/^(tul|tpuart):(.+)$/ )
{
my $ dev = $ 2 ;
my $ baudrate ;
( $ dev , $ baudrate ) = split ( "@" , $ dev ) ;
$ baudrate = 19200 if ( ! $ baudrate ) ; # fix for TUL board
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
if ( $^O =~ /Win/ )
{
require Win32::SerialPort ;
$ po = new Win32:: SerialPort ( $ dev ) ;
} else
{
require Device::SerialPort ;
$ po = new Device:: SerialPort ( $ dev ) ;
}
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
if ( ! $ po )
{
return undef if ( $ reopen ) ;
Log3 ( $ name , 3 , "Can't open $dev: $!" ) ;
$ readyfnlist { "$name.$dev" } = $ hash ;
$ hash - > { STATE } = "disconnected" ;
return "" ;
}
$ hash - > { DevType } = 'TPUART' ;
$ hash - > { USBDev } = $ po ;
if ( $^O =~ /Win/ )
{
$ readyfnlist { "$name.$dev" } = $ hash ;
}
else
{
$ hash - > { FD } = $ po - > FILENO ;
delete ( $ readyfnlist { "$name.$dev" } ) ;
$ selectlist { "$name.$dev" } = $ hash ;
}
# assumed always available
if ( $ baudrate )
{
$ po - > reset_error ( ) ;
Log3 ( $ name , 3 , "TUL setting $name baudrate to $baudrate" ) ;
$ po - > baudrate ( $ baudrate ) ;
$ po - > databits ( 8 ) ;
$ po - > parity ( 'even' ) ;
$ po - > stopbits ( 1 ) ;
$ po - > handshake ( 'none' ) ;
# This part is for some Linux kernel versions which 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 ) ;
}
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
$ po - > write_settings ;
}
# No more devices supported now
else
{
Log3 ( $ name , 1 , "$dev protocol is not supported" ) ;
}
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
if ( $ reopen )
{
Log3 ( $ name , 1 , "TUL $dev reappeared ($name)" ) ;
}
else
{
Log3 ( $ name , 3 , "TUL device opened" ) ;
}
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
$ hash - > { STATE } = "" ; # Allow InitDev to set the state
my $ ret = TUL_DoInit ( $ hash ) ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
if ( $ ret )
{
TUL_CloseDev ( $ hash ) ;
2017-12-15 18:39:59 +00:00
Log ( 1 , "OpenDev: Cannot init $dev, ignoring it" ) ;
2016-04-03 19:44:22 +00:00
}
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
DoTrigger ( $ name , "CONNECTED" ) if ( $ reopen ) ;
return $ ret ;
2011-06-30 10:18:08 +00:00
}
2016-04-03 19:44:22 +00:00
########################
2011-06-30 10:18:08 +00:00
sub
TUL_Disconnected ( $ )
{
2016-04-03 19:44:22 +00:00
my $ hash = shift ;
my $ dev = $ hash - > { DeviceName } ;
my $ name = $ hash - > { NAME } ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
return if ( ! defined ( $ hash - > { FD } ) ) ; # Already deleted or RFR
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
Log3 ( $ name , 1 , "$dev disconnected, waiting to reappear" ) ;
TUL_CloseDev ( $ hash ) ;
$ readyfnlist { "$name.$dev" } = $ hash ; # Start polling
$ hash - > { STATE } = "disconnected" ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
# Without the following sleep the open of the device causes a SIGSEGV,
# and following opens block infinitely. Only a reboot helps.
sleep ( 5 ) ;
2011-06-30 10:18:08 +00:00
2016-04-03 19:44:22 +00:00
DoTrigger ( $ name , "DISCONNECTED" ) ;
2011-06-30 10:18:08 +00:00
}
2016-04-03 19:44:22 +00:00
########################
2011-06-30 10:18:08 +00:00
sub
TUL_Attr ( @ )
{
2017-10-14 18:48:39 +00:00
my ( $ cmd , $ name , $ aName , $ aVal ) = @ _ ;
Log3 ( $ name , 5 , "changing value, ATTR: $aName, VALUE: $aVal" ) ;
if ( $ aName =~ m/useEIB/ )
{
if ( $ aVal =~ m/1/ )
{
$ useEIB = '1' ;
}
else
{
$ useEIB = '0' ;
}
}
2016-04-03 19:44:22 +00:00
return undef ;
2011-06-30 10:18:08 +00:00
}
####################################################################################
####################################################################################
#
#
# The following section has been inspired by the EIB module from MrHouse project
# written by Peter Sj?din peter@sjodin.net and Mike Pieper eibdmh@pieper-family.de
# Code has been mainly changed to fit to the FHEM framework by Maz Rashid
2011-10-08 16:43:00 +00:00
# (to be honest the code had to be reworked very intensively due the lack of code quality)
2011-06-30 10:18:08 +00:00
#
# Utility functions
2016-04-03 19:44:22 +00:00
sub tul_hex2addr
{
2011-06-30 10:18:08 +00:00
my $ str = lc ( $ _ [ 0 ] ) ;
2017-04-28 08:04:26 +00:00
# Old
#if ($str =~ /([0-9a-f])([0-9a-f])([0-9a-f]{2})/)
# New its2bit
2017-10-14 18:48:39 +00:00
#if ($str =~ /([0-9a-f]{2})([0-9a-f])([0-9a-f]{2})/)
#extended adressing
if ( ( ( $ useEIB =~ m/1/ ) and ( $ str =~ /([0-9a-f])([0-9a-f])([0-9a-f]{2})/ ) ) or ( ( $ useEIB =~ m/0/ ) and ( $ str =~ /([0-9a-f]{2})([0-9a-f])([0-9a-f]{2})/ ) ) )
2016-04-03 19:44:22 +00:00
{
2011-10-08 16:43:00 +00:00
return ( hex ( $ 1 ) << 11 ) | ( hex ( $ 2 ) << 8 ) | hex ( $ 3 ) ;
2011-06-30 10:18:08 +00:00
}
else
{
2017-12-15 18:39:59 +00:00
Log ( 3 , "hex2addr: Bad KNX address string: \'$str\'\n" ) ;
2011-06-30 10:18:08 +00:00
return ;
}
}
2016-04-03 19:44:22 +00:00
sub tul_addr2hex
{
2011-06-30 10:18:08 +00:00
my $ a = $ _ [ 0 ] ;
my $ b = $ _ [ 1 ] ; # 1 if local (group) address, else physical address
my $ str ;
2017-10-14 18:48:39 +00:00
2016-04-03 19:44:22 +00:00
if ( $ b == 1 )
2017-10-14 18:48:39 +00:00
{
2017-01-08 16:52:19 +00:00
#logical address used
#old, short-syntax
2017-04-28 08:04:26 +00:00
#$str = sprintf "%01x%01x%02x", ($a >> 11) & 0xf, ($a >> 8) & 0x7, $a & 0xff;
2017-01-08 16:52:19 +00:00
#extended adress-range
2017-10-14 18:48:39 +00:00
#$str = sprintf "%02x%01x%02x", ($a >> 11) & 0x1f, ($a >> 8) & 0x7, $a & 0xff;
#extended adressing
if ( $ useEIB =~ m/1/ )
{
$ str = sprintf "%01x%01x%02x" , ( $ a >> 11 ) & 0xf , ( $ a >> 8 ) & 0x7 , $ a & 0xff ;
}
else
{
$ str = sprintf "%02x%01x%02x" , ( $ a >> 11 ) & 0x1f , ( $ a >> 8 ) & 0x7 , $ a & 0xff ;
}
2011-06-30 10:18:08 +00:00
}
2017-01-08 16:52:19 +00:00
else
{
#physical address used
2017-04-28 08:04:26 +00:00
# Old
# $str = sprintf "%01x%01x%02x", $a >> 12, ($a >> 8) & 0xf, $a & 0xff;
# New
2017-10-14 18:48:39 +00:00
#$str = sprintf "%02x%01x%02x", $a >> 12, ($a >> 8) & 0xf, $a & 0xff;
#extended adressing
if ( $ useEIB =~ m/1/ )
{
$ str = sprintf "%01x%01x%02x" , $ a >> 12 , ( $ a >> 8 ) & 0xf , $ a & 0xff ;
}
else
{
$ str = sprintf "%02x%01x%02x" , $ a >> 12 , ( $ a >> 8 ) & 0xf , $ a & 0xff ;
}
2011-06-30 10:18:08 +00:00
}
2017-10-14 18:48:39 +00:00
2011-06-30 10:18:08 +00:00
return $ str ;
}
2016-04-03 19:44:22 +00:00
sub tul_str2hex
{
2011-06-30 10:18:08 +00:00
my $ str = $ _ [ 0 ] ;
2017-10-14 18:48:39 +00:00
my $ hex ;
if ( ( $ str =~ /(\d+)\/(\d+)\/(\d+)/ ) or ( $ str =~ /(\d+)\.(\d+)\.(\d+)/ ) )
{
# logical address
2017-04-28 08:04:26 +00:00
# old
# my $hex = sprintf("%01x%01x%02x",$1,$2,$3);
# New
2017-10-14 18:48:39 +00:00
#my $hex = sprintf("%02x%01x%02x",$1,$2,$3);
#extended adressing
if ( $ useEIB =~ m/1/ )
{
$ hex = sprintf ( "%01x%01x%02x" , $ 1 , $ 2 , $ 3 ) ;
}
else
{
$ hex = sprintf ( "%02x%01x%02x" , $ 1 , $ 2 , $ 3 ) ;
}
2011-06-30 10:18:08 +00:00
return $ hex ;
}
}
# For mapping between APCI symbols and values
my @ apcicodes = ( 'read' , 'reply' , 'write' ) ;
my % apcivalues = ( 'read' = > 0 , 'reply' = > 1 , 'write' = > 2 , ) ;
# decode: unmarshall a string with an EIB message into a hash
# The hash has the follwing fields:
# - type: APCI (symbolic value)
# - src: source address
# - dst: destiniation address
# - data: array of integers; one for each byte of data
sub decode_eibd ($)
{
my ( $ buf ) = @ _ ;
my $ drl = 0xe1 ; # dummy value
my % msg ;
my @ data ;
my ( $ src , $ dst , $ bytes ) = unpack ( "nnxa*" , $ buf ) ;
my $ apci ;
$ apci = vec ( $ bytes , 3 , 2 ) ;
# mask out apci bits, so we can use the whole byte as data:
vec ( $ bytes , 3 , 2 ) = 0 ;
2016-04-03 19:44:22 +00:00
if ( $ apci >= 0 && $ apci <= $# apcicodes )
{
2011-06-30 10:18:08 +00:00
$ msg { 'type' } = $ apcicodes [ $ apci ] ;
}
2016-04-03 19:44:22 +00:00
else
{
2011-06-30 10:18:08 +00:00
$ msg { 'type' } = 'apci ' . $ apci ;
}
$ msg { 'src' } = tul_addr2hex ( $ src , 0 ) ;
$ msg { 'dst' } = tul_addr2hex ( $ dst , 1 ) ;
@ data = unpack ( "C" . length ( $ bytes ) , $ bytes ) ;
my $ datalen = @ data ;
2017-12-15 18:39:59 +00:00
Log ( 5 , "decode_eibd: byte len: " . length ( $ bytes ) . " array size: $datalen" ) ;
2011-06-30 10:18:08 +00:00
# in case of data len > 1, the first byte (the one with apci) seems not to be used
# and only the following byte are of interest.
2016-04-03 19:44:22 +00:00
if ( $ datalen > 1 )
{
2011-06-30 10:18:08 +00:00
shift @ data ;
}
$ msg { 'data' } = \ @ data ;
return \ % msg ;
}
# encode: marshall a hash into a EIB message string
sub encode_eibd ($)
{
my ( $ mref ) = @ _ ;
my @ msg ;
my $ APCI ;
my @ data ;
$ APCI = $ apcivalues { $ mref - > { 'type' } } ;
2016-04-03 19:44:22 +00:00
if ( ! ( defined $ APCI ) )
{
2017-12-15 18:39:59 +00:00
Log ( 3 , "encode_eibd: Bad KNX message type $mref->{'type'}\n" ) ;
2011-06-30 10:18:08 +00:00
return ;
}
@ data = @ { $ mref - > { 'data' } } ;
2015-10-06 18:01:49 +00:00
@ data = ( 0x0 ) if ( ! @ data || ! defined ( $ data [ 0 ] ) ) ; #make sure data has at least one element
#@data = (0x0) if(!(defined @data) || !(defined $data[0])); #make sure data has at least one element
2011-06-30 10:18:08 +00:00
my $ datalen = @ data ;
2017-12-15 18:39:59 +00:00
Log ( 5 , "encode_eibd: dst: $mref->{'dst'} apci: $APCI datalen: $datalen data: @data" ) ;
2011-06-30 10:18:08 +00:00
@ msg = (
tul_hex2addr ( $ mref - > { 'dst' } ) , # Destination address
0x0 | ( $ APCI >> 2 ) , # TPDU type, Sequence no, APCI (msb)
2013-05-01 22:41:47 +00:00
( ( $ APCI & 0x3 ) << 6 ) | $ data [ 0 ] ,
2011-06-30 10:18:08 +00:00
) ;
2016-04-03 19:44:22 +00:00
if ( $ datalen > 1 )
{
2013-05-01 22:41:47 +00:00
shift ( @ data ) ;
2011-06-30 10:18:08 +00:00
push @ msg , @ data ;
}
return @ msg ;
}
# decode: unmarshall a string with an EIB telegram into a hash
# A typical telegram looks like: bc110a0002e100813a
# checks:
# - 1st byte must have at least the bits $90 set. (otherwise it is false or a repeat)
# - 2nd/3rd byte are the source (1.1.10)
# - 4th/5th byte are the dst group (0/0/2)
# - 6th byte (msb if 1 dst is group, else a phys. address )
# - low nibble is length of data (counting from 0) (->2)
# - 7th byte is ignored
# - 8th byte is the command / short data byte
# - if 8th byte >>6 is 0 -> read
# - is 2 -> write
# - is 1 -> reply
# - if length is 2 -> 8th byte & 0x3F is data
# otherwise data start after 8th byte
# - last byte is the crc (ignored)
# The hash has the follwing fields:
# - type: APCI (symbolic value)
# - src: source address
# - dst: destiniation address
# - data: array of integers; one for each byte of data
sub decode_tpuart ($)
{
my ( $ buf ) = @ _ ;
my ( $ ctrl , $ src , $ dst , $ routingcnt , $ cmd , $ bytes ) = unpack ( "CnnCxCa*" , $ buf ) ;
my $ drl = $ routingcnt >> 7 ;
my $ len = ( $ routingcnt & 0x0F ) + 1 ;
2015-12-21 19:07:34 +00:00
#if(($ctrl & 0xB0)!=0xB0)
if ( ( $ ctrl & 0x90 ) != 0x90 )
2011-06-30 10:18:08 +00:00
{
2017-12-15 18:39:59 +00:00
Log ( 3 , "decode_tpuart: Control Byte " . sprintf ( "0x%02x" , $ ctrl ) . " does not match expected mask 2x1001nnnn" ) ;
2011-06-30 10:18:08 +00:00
return undef ;
}
2012-03-25 12:50:50 +00:00
2017-12-15 18:39:59 +00:00
Log ( 5 , "decode_tpuart: msg cmd: " . sprintf ( "0x%02x" , $ cmd ) . " datalen: $len" ) ;
2011-06-30 10:18:08 +00:00
my $ apci = ( $ cmd >> 6 ) & 0x0F ;
2016-04-03 19:44:22 +00:00
if ( $ len == 2 )
{ # 1 byte data
2011-06-30 10:18:08 +00:00
$ bytes = pack ( "C" , $ cmd & 0x3F ) ;
}
2012-03-25 12:50:50 +00:00
2017-12-15 18:39:59 +00:00
Log ( 5 , "decode_tpuart: msg cmd: " . sprintf ( "0x%02x" , $ cmd ) . " datalen: $len apci: $apci" ) ;
2011-06-30 10:18:08 +00:00
my % msg ;
my @ data ;
2016-04-03 19:44:22 +00:00
if ( $ apci >= 0 && $ apci <= $# apcicodes )
{
2011-06-30 10:18:08 +00:00
$ msg { 'type' } = $ apcicodes [ $ apci ] ;
}
2016-04-03 19:44:22 +00:00
else
{
2011-06-30 10:18:08 +00:00
$ msg { 'type' } = 'apci ' . $ apci ;
}
$ msg { 'src' } = tul_addr2hex ( $ src , 0 ) ;
$ msg { 'dst' } = tul_addr2hex ( $ dst , $ drl ) ;
@ data = unpack ( "C" . length ( $ bytes ) , $ bytes ) ;
my $ datalen = @ data ;
2017-12-15 18:39:59 +00:00
Log ( 5 , "decode_tpuart: decode_tpuart byte len: " . length ( $ bytes ) . " array size: $datalen" ) ;
2011-06-30 10:18:08 +00:00
$ msg { 'data' } = \ @ data ;
return \ % msg ;
}
# encode: marshall a hash into a EIB message string
sub encode_tpuart ($)
{
my ( $ mref ) = @ _ ;
my @ msg ;
my $ APCI ;
my @ data ;
$ APCI = $ apcivalues { $ mref - > { 'type' } } ;
2016-04-03 19:44:22 +00:00
if ( ! ( defined $ APCI ) )
{
2017-12-15 18:39:59 +00:00
Log ( 3 , "encode_tpuart: Bad KNX message type $mref->{'type'}\n" ) ;
2011-06-30 10:18:08 +00:00
return ;
}
@ data = @ { $ mref - > { 'data' } } ;
my $ datalen = @ data ;
if ( $ datalen > 14 )
{
2017-12-15 18:39:59 +00:00
Log ( 3 , "encode_tpuart: Bad KNX message length $datalen\n" ) ;
2011-06-30 10:18:08 +00:00
return ;
}
2017-12-15 18:39:59 +00:00
Log ( 5 , "encode_tpuart: dst: $mref->{'dst'} apci: $APCI datalen: $datalen data: @data" ) ;
2011-06-30 10:18:08 +00:00
@ msg = (
0xBC , # EIB ctrl byte
tul_hex2addr ( $ mref - > { 'src' } ) , # src address
tul_hex2addr ( $ mref - > { 'dst' } ) , # Destination address
2013-05-01 22:41:47 +00:00
0xE0 | $ datalen , # Routing counter + data len
2011-06-30 10:18:08 +00:00
0x00 ,
2013-05-01 22:41:47 +00:00
( ( $ APCI & 0x3 ) << 6 ) | $ data [ 0 ] ,
2011-06-30 10:18:08 +00:00
) ;
2016-04-03 19:44:22 +00:00
if ( $ datalen > 1 )
{
2013-05-01 22:41:47 +00:00
shift ( @ data ) ;
2011-06-30 10:18:08 +00:00
push @ msg , @ data ;
}
# convert to byte array
my $ arraystr = pack ( "CnnC*" , @ msg ) ;
@ msg = unpack ( "C*" , $ arraystr ) ;
my @ tpuartmsg ;
# calculate crc
my $ crc = 0xFF ;
my $ i ;
for ( $ i = 0 ; $ i < @ msg ; $ i + + )
{
$ crc ^= $ msg [ $ i ] ;
push @ tpuartmsg , ( 0x80 | $ i ) ;
push @ tpuartmsg , $ msg [ $ i ] ;
}
push @ tpuartmsg , ( 0x40 | $ i ) ;
push @ tpuartmsg , $ crc ;
return @ tpuartmsg ;
}
#
# eibd communication part
#
# Functions four group socket communication
# Open a group socket for group communication
# openGroupSocket SOCK
sub openGroupSocket ($)
{
my $ hash = shift ;
## only needed if EIBD
if ( $ hash - > { DevType } eq 'EIBD' )
{
my @ msg = ( 0x0026 , 0x0000 , 0x00 ) ; # EIB_OPEN_GROUPCON
sendRequest ( $ hash , pack "nnC" , @ msg ) ;
2017-12-15 18:39:59 +00:00
# docM 2017-11-06
use IO::Select ;
goto error unless ( IO::Select - > new ( $ hash - > { TCPDev } ) - > can_read ( 10 ) ) ;
# /docM
2011-06-30 10:18:08 +00:00
goto error unless my $ answer = getRequest ( $ hash ) ;
my $ head = unpack ( "n" , $ answer ) ;
goto error unless $ head == 0x0026 ;
}
return 1 ;
error:
2017-12-15 18:39:59 +00:00
Log ( 0 , "openGroupSocket: failed\n" ) ;
# docM 2017-11-05
# removed print
# print "openGroupSocket failed\n";
# /docM
2011-06-30 10:18:08 +00:00
return undef ;
}
# Send group data
# sendGroup Hash DEST DATA
sub sendGroup ($$)
{
my ( $ hash , $ msgref ) = @ _ ;
my $ dst = $ msgref - > { 'dst' } ;
my $ src = $ hash - > { DeviceAddress } ;
$ msgref - > { 'src' } = $ src ;
if ( $ hash - > { DevType } eq 'EIBD' )
{
my @ encmsg = encode_eibd ( $ msgref ) ;
2017-04-28 08:04:26 +00:00
Log ( 5 , "SendGroup: dst: $dst, msg: @encmsg \n" ) ;
2011-06-30 10:18:08 +00:00
my @ msg = ( 0x0027 ) ; # EIB_GROUP_PACKET
push @ msg , @ encmsg ;
sendRequest ( $ hash , pack ( "nnCC*" , @ msg ) ) ;
}
elsif ( $ hash - > { DevType } eq 'TPUART' )
{
my @ encmsg = encode_tpuart ( $ msgref ) ;
2017-04-28 08:04:26 +00:00
Log ( 5 , "SendGroup: dst: $dst, msg: @encmsg \n" ) ;
2011-06-30 10:18:08 +00:00
sendRequest ( $ hash , pack ( "C*" , @ encmsg ) ) ;
my $ response = getRequestFixLength ( $ hash , ( $# encmsg + 1 ) / 2 + 1 ) ;
}
return 1 ;
}
2012-03-25 12:50:50 +00:00
# will read as much byte as exists at the
# serial buffer.
sub purgeReceiverBuf ($)
{
my ( $ hash ) = @ _ ;
if ( $ hash - > { DevType } eq 'TPUART' )
{
2017-12-15 18:39:59 +00:00
Log ( 5 , "purgeReceiverBuf: purging..." ) ;
2012-03-25 12:50:50 +00:00
my $ data = undef ;
do
{
my ( undef , $ data ) = $ hash - > { USBDev } - > read ( 100 ) ;
2017-12-15 18:39:59 +00:00
Log ( 5 , "purgeReceiverBuf: purging packet: " . unpack ( "H*" , $ data ) . "\n" ) if ( defined ( $ data ) and length ( $ data ) > 0 ) ;
2012-03-25 12:50:50 +00:00
} while ( defined ( $ data ) and length ( $ data ) > 0 )
}
}
2011-06-30 10:18:08 +00:00
sub getRequestFixLength ($$)
{
my ( $ hash , $ len ) = @ _ ;
if ( $ hash - > { DevType } eq 'TPUART' )
{
2017-12-15 18:39:59 +00:00
Log ( 5 , "getRequestFixLength: waiting to receive $len bytes ..." ) ;
2011-06-30 10:18:08 +00:00
my $ buf = "" ;
while ( length ( $ buf ) < $ len )
{
#select(undef,undef,undef,0.5);
my ( undef , $ data ) = $ hash - > { USBDev } - > read ( $ len - length ( $ buf ) ) ;
2017-12-15 18:39:59 +00:00
Log ( 5 , "getRequestFixLength: Received fixlen packet: " . unpack ( "H*" , $ data ) . "\n" ) if ( defined ( $ data ) and length ( $ data ) > 0 ) ;
2011-06-30 10:18:08 +00:00
$ buf . = $ data if ( defined ( $ data ) ) ;
2017-04-28 08:04:26 +00:00
#Log (5,"buf len: " . length($buf) . " expected: $len");
2012-03-25 12:50:50 +00:00
# TODO: if we are longer than 5 seconds here, we should reset
2011-06-30 10:18:08 +00:00
}
# # we got more than needed
if ( length ( $ buf ) > $ len )
{
2012-03-25 12:50:50 +00:00
#check if this is ok
my $ remainpart = substr ( $ buf , $ len ) ;
2011-06-30 10:18:08 +00:00
$ hash - > { PARTIAL } . = $ remainpart ;
2012-03-25 12:50:50 +00:00
$ buf = substr ( $ buf , 0 , $ len ) ;
2017-12-15 18:39:59 +00:00
Log ( 5 , "getRequestFiLength: we got too much.. buf(" . unpack ( "H*" , $ buf ) . ") remainingpart(" . unpack ( "H*" , $ remainpart ) . ")" ) ;
2011-06-30 10:18:08 +00:00
}
2017-12-15 18:39:59 +00:00
Log ( 5 , "getRequestFixLength: len: $len packet: " . unpack ( "H*" , $ buf ) . "\n" ) ;
2011-06-30 10:18:08 +00:00
return $ buf ;
}
return undef ;
}
# Receive group data
# getGroup hash
sub getGroup ($)
{
my $ hash = shift ;
if ( $ hash - > { DevType } eq 'EIBD' )
{
goto error unless my $ buf = getRequest ( $ hash ) ;
my ( $ head , $ data ) = unpack ( "na*" , $ buf ) ;
goto error unless $ head == 0x0027 ;
return decode_eibd ( $ data ) ;
}
elsif ( $ hash - > { DevType } eq 'TPUART' )
{
my $ ackdst = $ hash - > { AckLineDef } ;
my $ buf = $ hash - > { PARTIAL } ;
my $ reqlen = 8 ;
my $ telegram ;
do
{
my $ data = getRequestFixLength ( $ hash , $ reqlen - length ( $ buf ) ) if ( $ reqlen > length ( $ buf ) ) ;
if ( length ( $ buf ) == 0 && ( ! defined ( $ data ) || length ( $ data ) == 0 ) )
{
2017-12-15 18:39:59 +00:00
Log ( 5 , "getGroup: read fix length delivered no data." ) ;
2011-06-30 10:18:08 +00:00
return undef ;
}
$ buf . = $ data if ( defined ( $ data ) ) ;
# check that control byte is correct
my $ ctrl = unpack ( "C" , $ buf ) if ( length ( $ buf ) > 0 ) ;
if ( defined ( $ ctrl ) && ( $ ctrl & 0x40 ) )
{
$ buf = substr ( $ buf , 1 ) ;
$ hash - > { PARTIAL } = $ buf ;
2017-12-15 18:39:59 +00:00
Log ( 5 , "getGroup: TPUART RSP " . sprintf ( "0x%02x" , $ ctrl ) . " ignored." ) ;
2011-06-30 10:18:08 +00:00
return undef ;
}
if ( length ( $ buf ) > 5 )
{
my $ routingcnt = unpack ( "xxxxxC" , $ buf ) ;
$ reqlen = ( $ routingcnt & 0x0F ) + 8 ;
2017-12-15 18:39:59 +00:00
Log ( 5 , "getGroup: receiving telegram with len: $reqlen" ) ;
2011-06-30 10:18:08 +00:00
}
if ( $ reqlen <= length ( $ buf ) )
{
$ telegram = substr ( $ buf , 0 , $ reqlen - 1 ) ;
$ buf = substr ( $ buf , $ reqlen ) ;
}
}
while ( ! defined ( $ telegram ) ) ;
2017-12-15 18:39:59 +00:00
Log ( 5 , "getGroup: Telegram: (" . length ( $ telegram ) . "): " . unpack ( "H*" , $ telegram ) ) ;
Log ( 5 , "getGroup: Buf: (" . length ( $ buf ) . "): " . unpack ( "H*" , $ buf ) ) ;
2011-06-30 10:18:08 +00:00
$ hash - > { PARTIAL } = $ buf ;
my $ msg = decode_tpuart ( $ telegram ) ;
2012-03-25 12:50:50 +00:00
#check if we refused a telegram (i.e. repeats)
$ hash - > { REFUSED } = unpack ( "H*" , $ telegram ) if ( ! defined ( $ msg ) ) ;
2011-06-30 10:18:08 +00:00
# We are always too late for Ack
# if(defined($msg) && (substr($msg->{'dst'},0,2) eq $ackdst))
# {
# # ACK
# sendRequest($hash,pack('C',0x11));
2017-04-28 08:04:26 +00:00
# Log (5,"Ack!");
2011-06-30 10:18:08 +00:00
# }
return $ msg ;
}
2017-12-15 18:39:59 +00:00
Log ( 2 , "GetGroup: DevType $hash->{DevType} not supported for getGroup\n" ) ;
2011-06-30 10:18:08 +00:00
return undef ;
2015-11-23 19:15:39 +00:00
error:
2017-12-15 18:39:59 +00:00
Log ( 2 , "GetGroup: seems like knxd not connected\n" ) ;
2015-11-23 19:15:39 +00:00
return undef ;
2011-06-30 10:18:08 +00:00
}
# Gets a request from eibd
# DATA = getRequest SOCK
sub getRequest ($)
{
my $ hash = shift ;
my ( $ data ) ;
if ( $ hash - > { TCPDev } && $ hash - > { DevType } eq 'EIBD' )
{
goto error unless sysread ( $ hash - > { TCPDev } , $ data , 2 ) ;
my $ size = unpack ( "n" , $ data ) ;
goto error unless sysread ( $ hash - > { TCPDev } , $ data , $ size ) ;
2017-12-15 18:39:59 +00:00
Log ( 5 , "getRequest: Received packet: " . unpack ( "H*" , $ data ) . "\n" ) ;
2011-06-30 10:18:08 +00:00
return $ data ;
}
elsif ( $ hash - > { USBDev } ) {
my $ data = $ hash - > { USBDev } - > input ( ) ;
2017-12-15 18:39:59 +00:00
Log ( 5 , "getRequest: Received packet: " . unpack ( "H*" , $ data ) . "\n" ) if ( defined ( $ data ) and length ( $ data ) > 0 ) ;
2011-06-30 10:18:08 +00:00
return $ data ;
}
2017-12-15 18:39:59 +00:00
Log ( 1 , "getRequest: TUL $hash->{NAME}: can not select a source for reading data." ) ;
2011-06-30 10:18:08 +00:00
return undef ;
error:
2017-12-15 18:39:59 +00:00
# docM 2017-11-05 remove print
# printf "eibd communication failed\n";
# /docM
Log ( 2 , "getRequest: communication to knxd failed\n" ) ;
return undef ;
2011-06-30 10:18:08 +00:00
}
# Sends a request to eibd
# sendRequest Hash,DATA
sub sendRequest ($$)
{
my ( $ hash , $ str ) = @ _ ;
2017-04-28 08:04:26 +00:00
Log ( 5 , "sendRequest: " . unpack ( "H*" , $ str ) . "\n" ) ;
2011-06-30 10:18:08 +00:00
if ( $ hash - > { TCPDev } )
{
my $ size = length ( $ str ) ;
my @ head = ( ( $ size >> 8 ) & 0xff , $ size & 0xff ) ;
return undef unless syswrite ( $ hash - > { TCPDev } , pack ( "CC" , @ head ) ) ;
return undef unless syswrite ( $ hash - > { TCPDev } , $ str ) ;
}
elsif ( $ hash - > { USBDev } )
{
$ hash - > { USBDev } - > write ( $ str ) ;
}
else
{
2017-12-15 18:39:59 +00:00
Log ( 2 , "sendRequest: TUL $hash->{NAME}: No known physical protocoll defined." ) ;
2011-06-30 10:18:08 +00:00
return undef ;
}
return 1 ;
}
1 ;
2012-11-04 13:49:43 +00:00
= pod
= begin html
< a name = "TUL" > </a>
<h3> TUL </h3>
<ul>
<table>
<tr> <td>
The TUL module is the representation of a EIB / KNX connector in FHEM .
2016-04-03 19:44:22 +00:00
< a href = "#KNX" > KNX </a> instances represent the EIB / KNX devices and will need a TUL as IODev to communicate with the EIB / KNX network . <br>
2016-11-08 19:15:55 +00:00
The TUL module is designed to connect to EIB network either using eibd , knxd or the < a href = "http://busware.de/tiki-index.php?page=TUL" target = "_blank" > TUL usb stick </a> created by busware . de
2012-11-04 13:49:43 +00:00
2016-04-03 19:44:22 +00:00
Note: this module may require the Device:: SerialPort or Win32:: SerialPort module if you attach the device via USB and the OS sets strange default parameters for serial devices .
2012-11-04 13:49:43 +00:00
</td> <td>
2014-04-15 13:28:54 +00:00
< img src = "IMG_0483.jpg" width = "100%" height = "100%" / >
2012-11-04 13:49:43 +00:00
</td> </tr>
</table>
< a name = "TULdefine" > </a>
<b> Define </b>
<ul>
<code> define & lt ; name & gt ; TUL & lt ; device & gt ; & lt ; physical address & gt ; </code> <br>
<br>
TUL usb stick / TPUART serial devices: <br> <ul>
2016-04-03 19:44:22 +00:00
& lt ; device & gt ; specifies the serial port to communicate with the TUL . The name of the serial - device depends on your distribution , under linux the cdc_acm kernel module is responsible , and usually a
/dev/ ttyACM0 device will be created . If your distribution does not have a cdc_acm module , you can force usbserial to handle the TUL by the following command: <ul> modprobe usbserial vendor = 0x03eb
product = 0x204b </ul> In this case the device is most probably /dev/ ttyUSB0 . <br> <br>
You can also specify a baudrate if the device name contains the @ character , e . g . : /dev/ ttyACM0 @ 19200 <br> <br>
Note: For TUL usb stick the baudrate 19200 is needed and this is the default when no baudrate is given .
2012-11-04 13:49:43 +00:00
<br> <br>
Example: <br>
<code> define tul TUL tul: /dev/ ttyACM0 1.1 .249 </code>
</ul>
2016-04-03 19:44:22 +00:00
2012-11-04 13:49:43 +00:00
EIBD: <br> <ul>
2016-04-03 19:44:22 +00:00
& lt ; device & gt ; specifies the host:port of the eibd device . E . g . eibd:192 .168 .0 .244 : 2323 . When using the standard port , the port can be omitted .
2012-11-04 13:49:43 +00:00
<br> <br>
Example: <br>
<code> define tul TUL eibd:localhost 1.1 .249 </code>
2016-11-08 19:15:55 +00:00
<code> define tul TUL knxd:192 .168 .178 .1 1.1 .248 </code>
2012-11-04 13:49:43 +00:00
</ul>
<br>
2016-04-03 19:44:22 +00:00
If the device is called none , then no device will be opened , so you can experiment without hardware attached . <br>
2012-11-04 13:49:43 +00:00
The physical address is used as the source address of telegrams sent to EIB network .
</ul>
<br>
2016-04-03 19:44:22 +00:00
< a name = "TULattr" > </a>
<b> Attributes </b>
2012-11-04 13:49:43 +00:00
<ul>
2016-04-03 19:44:22 +00:00
<li> < a href = "#do_not_notify" > do_not_notify </a> </li> <br>
<li> < a href = "#attrdummy" > dummy </a> </li> <br>
<li> < a href = "#showtime" > showtime </a> </li> <br>
<li> < a href = "#verbose" > verbose </a> </li> <br>
<li> < a href = "#useEIB" > useEIB </a> </li> <br>
<ul>
The device operates the module 10 _EIB , if this flag is set to 1 . This is used for backward compatibility only . Otherwise , only the client 10 _KNX is used .
</ul>
2012-11-04 13:49:43 +00:00
</ul>
2016-04-03 19:44:22 +00:00
<br>
</ul>
= end html
2016-11-08 19:15:55 +00:00
= device
= item summary Connects FHEM to KNX - Bus ( Base - device )
= item summary_DE Verbindet FHEM mit dem KNX - Bus ( Basisger & umlat )
2016-04-03 19:44:22 +00:00
= begin html_DE
< a name = "TUL" > </a>
<h3> TUL </h3>
<ul>
<table>
<tr> <td>
Das Modul TUL stellt die Verbindung von FHEM zum EIB / KNX dar .
< a href = "#KNX" > KNX </a> Instanzen stellen die Vrbindung zu den KNX - Gruppen dar und ben & Ouml ; tigen ein TUL - Device als IO - Schnittstelle . <br>
2016-11-08 19:15:55 +00:00
Das Modul TUL kommuniziert mit dem KNX entweder & Uuml ; ber den eibd , den knxd oder den TUL < a href = "http://busware.de/tiki-index.php?page=TUL" target = "_blank" > TUL usb stick </a> hergestellt von busware . de
2016-04-03 19:44:22 +00:00
Anmerkung: das Modul ben & Ouml ; tigt die Device:: SerialPort oder Win32:: SerialPort wenn der Stick & Uuml ; ber USB angeschlossen wird , und das OS unrealistische Parameter f & Uuml ; r das Device einstellt .
</td> <td>
< img src = "IMG_0483.jpg" width = "100%" height = "100%" / >
</td> </tr>
</table>
2012-11-04 13:49:43 +00:00
2016-04-03 19:44:22 +00:00
< a name = "TULdefine" > </a>
<b> Define </b>
2012-11-04 13:49:43 +00:00
<ul>
2016-04-03 19:44:22 +00:00
<code> define & lt ; name & gt ; TUL & lt ; device & gt ; & lt ; physical address & gt ; </code> <br>
<br>
TUL usb stick / TPUART serial devices: <br> <ul>
& lt ; device & gt ; enth & auml ; lt die serielle Schnittstelle der TUL . Der name der Schnittstelle h & auml ; ngt von Eurer Distribution ab . Unter linux wird f & Uuml ; r gew & Ouml ; hnlich /dev/ ttyACM0 verwandt .
Wenn Eure Distribution das modul cdc_acm nicht enth & auml ; lt , k & Ouml ; nnt Ihr das Laden des handles der TUL mit dem folgenden Befehl erzwingen: <ul> modprobe usbserial vendor = 0x03eb
product = 0x204b </ul> Dann ist die Schnittstelle meist /dev/ ttyUSB0 . <br> <br>
Ihr k & Ouml ; nnt dem Ger & auml ; t eine Baudrate vorgeben . Dazu dem Ger & auml ; tenamen das Zeichen @ hinzuf & Uuml ; gen , z . B . : /dev/ ttyACM0 @ 19200 <br> <br>
Anmerkung: F & Uuml ; r den TUL - USB - Stick wird die Baudrate 19200 ben & Ouml ; tigt . Dies entspricht der Defaulteinstellung .
<br> <br>
Beispiel: <br>
<code> define tul TUL tul: /dev/ ttyACM0 1.1 .249 </code>
</ul>
EIBD: <br> <ul>
& lt ; device & gt ; entspricht dem host:port des eibd - servers . z . B . eibd:192 .168 .0 .244 : 2323 . Wenn der Standardport genutzt wird , muss dieser nicht angegeben werden .
<br> <br>
Beispiel: <br>
<code> define tul TUL eibd:localhost 1.1 .249 </code>
2016-11-08 19:15:55 +00:00
<code> define tul TUL knxd:192 .168 .178 .2 1.1 .248 </code>
2016-04-03 19:44:22 +00:00
</ul>
<br>
Wenn das Ger & auml ; t none konfiguriert wird , wird kein device ge & Ouml ; ffnet . So k & Ouml ; nnt Ihr ohne angeschlossene Hardware experimentieren . <br>
Die physikalische Adresse wird als Absender f & Uuml ; r KNX - Telegramme genutzt .
2012-11-04 13:49:43 +00:00
</ul>
2016-04-03 19:44:22 +00:00
<br>
2012-11-04 13:49:43 +00:00
< a name = "TULattr" > </a>
2016-04-03 19:44:22 +00:00
<b> Attribute </b>
2012-11-04 13:49:43 +00:00
<ul>
<li> < a href = "#do_not_notify" > do_not_notify </a> </li> <br>
<li> < a href = "#attrdummy" > dummy </a> </li> <br>
<li> < a href = "#showtime" > showtime </a> </li> <br>
2016-04-03 19:44:22 +00:00
<li> < a href = "#verbose" > verbose </a> </li> <br>
<li> < a href = "#useEIB" > useEIB </a> </li> <br>
<ul>
Das Ger & auml ; t kann das Modul 10 _EIB bedienen , wenn das Flag auf 1 gesetzt ist . Dies ist nur f & Uuml ; r R & Uuml ; ckw & auml ; rtskompatibili & auml ; t genutzt . Andernfalls wird nur das Modul 10 _KNX bedient .
</ul>
2012-11-04 13:49:43 +00:00
</ul>
<br>
</ul>
2016-04-03 19:44:22 +00:00
= end html_DE
2012-11-04 13:49:43 +00:00
= cut