From c210296dff2b9ec5204020e9123ee3b0ff919373 Mon Sep 17 00:00:00 2001 From: erwin <> Date: Tue, 27 Dec 2022 21:08:38 +0000 Subject: [PATCH] 10_KNX.pm: multiple bugfixes & changes, (Forum #122582) git-svn-id: https://svn.fhem.de/fhem/trunk@26910 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 2 + fhem/FHEM/00_KNXIO.pm | 28 ++--- fhem/FHEM/10_KNX.pm | 266 +++++++++++++++++++++++------------------- 3 files changed, 157 insertions(+), 139 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index 18e35dd41..39a90d4fd 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,7 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. + - change: 00_KNXIO: minor changes, Forum #127792 + - change: 10_KNX: multiple changes, Forum #122582 - change: 93_DbLog: roll out version 5.5.7, NOTE: Please read Forum https://forum.fhem.de/index.php/topic,130743.0.html for information about this version diff --git a/fhem/FHEM/00_KNXIO.pm b/fhem/FHEM/00_KNXIO.pm index 22a5fd049..e28af8377 100644 --- a/fhem/FHEM/00_KNXIO.pm +++ b/fhem/FHEM/00_KNXIO.pm @@ -1,4 +1,4 @@ -############################################## +## no critic (Modules::RequireVersionVar) ###################################### # $Id$ # base module for KNX-communication # idea: merge some functions of TUL- & KNXTUL-module into one and add more connectivity @@ -13,8 +13,7 @@ # FIFO - queing of incoming messages (less latency for fhem-system) read= ~4ms vs ~34ms with KNXTUL/TUL # discard duplicate incoming messages # more robust parser of incoming messages -# -############################################## +################################################################################ ### changelog: # 19/10/2021 01.60 initial beta version # enable hostnames for mode H & T @@ -37,11 +36,12 @@ # unify Log msgs # 13/11/2022 modify fifo logic # improve cmd-ref -# xx/12/2022 change parameter parsing in define +# 05/12/2022 change parameter parsing in define # add renameFn - correct reading & attr IODev in KNX-devices after rename of KNXIO-device # change disabled handling # fix src-addr for Mode M,H # change internal PhyAddr to reabable format + range checking on define. +# 19/12/2022 cleanup package KNXIO; ## no critic 'package' @@ -57,7 +57,7 @@ use HttpUtils; use GPUtils qw(GP_Import GP_Export); # Package Helper Fn ### perlcritic parameters -# these ones are NOT used! (constants,Policy::Modules::RequireFilenameMatchesPackage,Modules::RequireVersionVar,NamingConventions::Capitalization) +# these ones are NOT used! (constants,Policy::Modules::RequireFilenameMatchesPackage,NamingConventions::Capitalization) ### the following percritic items will be ignored global ### ## no critic (ValuesAndExpressions::RequireNumberSeparators,ValuesAndExpressions::ProhibitMagicNumbers) ## no critic (RegularExpressions::RequireDotMatchAnything,RegularExpressions::RequireLineBoundaryMatching) @@ -142,16 +142,12 @@ sub KNXIO_Define { return InternalTimer(gettimeofday() + 0.2,\&KNXIO_openDev,$hash) if ($mode eq q{X}); return q{KNXIO-define syntax: "define KNXIO : " } . "\n" . - q{ or "define KNXIO S " } if (scalar(@arg) < 5); + q{ or "define KNXIO S " } if (scalar(@arg) < 5); my ($host,$port) = split(/[:]/ix,$arg[3]); return q{KNXIO-define: invalid ip-address or port, correct syntax is: } . - q{"define KNXIO : "} if ($mode =~ /[MHT]/ix && $port !~ /$PAT_PORT/ix); - - if (exists($hash->{OLDDEF})) { # modify definition.... - KNXIO_closeDev($hash); - } + q{"define KNXIO : "} if ($mode =~ /[MHT]/ix && $port !~ /$PAT_PORT/ix); if ($mode eq q{M}) { # multicast my $host1 = (split(/\./ix,$host))[0]; @@ -183,12 +179,11 @@ sub KNXIO_Define { } } - my $phyaddr = (defined($arg[4]))?$arg[4]:'0.0.0'; -# $hash->{PhyAddr} = sprintf('%05x',KNXIO_hex2addr($phyaddr)); + my $phyaddr = (defined($arg[4]))?$arg[4]:'0.0.0'; my $phytemp = KNXIO_hex2addr($phyaddr); $hash->{PhyAddr} = KNXIO_addr2hex($phytemp,2); #convert 2 times for correcting input! - KNXIO_closeDev($hash) if ($init_done); + KNXIO_closeDev($hash) if ($init_done || exists($hash->{OLDDEF})); # modify definition.... $hash->{PARTIAL} = q{}; # define helpers @@ -215,8 +210,6 @@ sub KNXIO_Attr { if ($cmd eq 'set' && defined($aVal) && $aVal == 1) { KNXIO_closeDev($hash); } else { -### should work w. KNXIO_Open -# CommandModify(undef, qq{-silent $name $hash->{DEF}}); # do a defmod ... InternalTimer(gettimeofday() + 0.2,\&KNXIO_openDev,$hash); } } @@ -430,7 +423,6 @@ sub KNXIO_ReadH { } my $phyaddr = unpack('x18n',$buf); $hash->{PhyAddr} = KNXIO_addr2hex($phyaddr,2); # correct Phyaddr. -# $hash->{PhyAddr} = sprintf('%05x',$phyaddr); # correct Phyaddr. readingsSingleUpdate($hash, 'state', 'connected', 1); Log3 ($name, 3, qq{KNXIO ($name) connected}); InternalTimer(gettimeofday() + 60, \&KNXIO_keepAlive, $hash); # start keepalive @@ -528,14 +520,12 @@ sub KNXIO_Write { } elsif ($mode eq 'M') { $completemsg = pack('nnnnnnnCCC*',0x0610,0x0530,$datasize + 16,0x2900,0xBCE0,$src,$dst,$datasize,0,@data); # use src addr -# $completemsg = pack('nnnnnnnCCC*',0x0610,0x0530,$datasize + 16,0x2900,0xBCE0,0,$dst,$datasize,0,@data); $ret = TcpServer_MCastSend($hash,$completemsg); } else { # $mode eq 'H' # total length= $size+20 - include 2900BCEO,src,dst,size,0 $completemsg = pack('nnnCC',0x0610,0x0420,$datasize + 20,4,$hash->{KNXIOhelper}->{CCID}) . pack('CCnnnnCCC*',$hash->{KNXIOhelper}->{SEQUENCECNTR_W},0,0x1100,0xBCE0,$src,$dst,$datasize,0,@data); # send TunnelInd -# pack('CCnnnnCCC*',$hash->{KNXIOhelper}->{SEQUENCECNTR_W},0,0x1100,0xBCE0,0,$dst,$datasize,0,@data); # send TunnelInd # Timeout function - expect TunnelAck within 1 sec! - but if fhem has a delay.... $hash->{KNXIOhelper}->{LASTSENTMSG} = $completemsg; # save msg for resend in case of TO diff --git a/fhem/FHEM/10_KNX.pm b/fhem/FHEM/10_KNX.pm index 34c21e016..fb8f43048 100644 --- a/fhem/FHEM/10_KNX.pm +++ b/fhem/FHEM/10_KNX.pm @@ -1,5 +1,7 @@ -############################################## +## no critic (Modules::RequireVersionVar) ###################################### # $Id$ +################################################################################ +### changelog: # ABU 20180218 restructuring, removed older documentation # MH 20210908 deleted part of change history # ABU 20181007 fixed dpt19 @@ -100,6 +102,12 @@ # MH 202212xx fix dpt217 range/fomatting, cmd-ref links, # remove support for IODev in define # modify disabled logic +# MH 20221226 device define after init_complete +# remove $hash->{DEVNAME} +# modify autocreate, get/set logic +# changed not user relevant internals to {.XXXX} +# changed DbLog_split function +# disabled StateFn package KNX; ## no critic 'package' @@ -112,7 +120,7 @@ use Scalar::Util qw(looks_like_number); use GPUtils qw(GP_Import GP_Export); # Package Helper Fn ### perlcritic parameters -# these ones are NOT used! (constants,Policy::Modules::RequireFilenameMatchesPackage,Modules::RequireVersionVar,NamingConventions::Capitalization) +# these ones are NOT used! (constants,Policy::Modules::RequireFilenameMatchesPackage,NamingConventions::Capitalization) # these ones are NOT used! (ControlStructures::ProhibitCascadingIfElse) ### the following percritic items will be ignored global ### ## no critic (ValuesAndExpressions::RequireNumberSeparators,ValuesAndExpressions::ProhibitMagicNumbers) @@ -144,10 +152,8 @@ BEGIN { fhemTimeLocal) ); } - # export to main context - GP_Export( - qw(Initialize) - ); +# export to main context +GP_Export( qw(Initialize) ); #string constants my $MODELERR = 'MODEL_NOT_DEFINED'; # for autocreate @@ -427,7 +433,7 @@ sub KNX_Define { $hash->{NAME} = $name; $svnid =~ s/.*\.pm\s(.+)Z.*/$1/ix; - $hash->{SVN} = $svnid; # store svn info in dev hash + $hash->{'.SVN'} = $svnid; # store svn info in dev hash my $logtxt = qq{KNX_define ($name): }; # leading txt @@ -442,11 +448,30 @@ sub KNX_Define { if ( $a[int(@a) - 1] !~ m/^(?:$PAT_GAD|$PAT_GAD_HEX)/ix ) { my $iodevCandidate = pop(@a); # remove from array, but do nothing with it! my $logtxtIO = qq{$logtxt specifying IODev $iodevCandidate is deprecated in define } . - qq{- use "attr $name IODev $iodevCandidate"}; + qq{- use "attr $name IODev $iodevCandidate"}; Log3 ($name, 2, $logtxtIO); return $logtxtIO if ($init_done); # allow durin start } +### new wait for initdone... + $hash->{'.DEFLINE'} = join(q{ },@a); # temp store defs for define2... + return InternalTimer(gettimeofday() + 5.0,\&KNX_Define2,$hash) if (! $init_done); + return KNX_Define2($hash); +} + +### continue define 5 sec after init complete! +sub KNX_Define2 { + my $hash = shift // return; + + my $name = $hash->{NAME}; + my $def = $hash->{'.DEFLINE'}; + delete $hash->{'.DEFLINE'}; + my @a = split(/\s+/x, $def); + RemoveInternalTimer($hash); + + my $logtxt = qq{KNX_define2 ($name): }; # leading txt +### end new + AssignIoPort($hash); # AssignIoPort will take device from $attr{$name}{IODev} if defined #reset @@ -454,7 +479,7 @@ sub KNX_Define { $hash->{GADTABLE} = {}; #delete all defptr entries for this device (defmod & copy problem) - KNX_delete_defptr($hash) if ($init_done); # verify with: {PrintHash($modules{KNX}->{defptr},3) } + KNX_delete_defptr($hash); # verify with: {PrintHash($modules{KNX}->{defptr},3) } #create groups and models, iterate through all possible args foreach my $i (2 .. $#a) { @@ -478,7 +503,8 @@ sub KNX_Define { return ($logtxt . qq{no model defined for group-number $gadNo}) if(! defined($gadModel)); if ($gadModel eq $MODELERR) { #within autocreate no model is supplied - throw warning - Log3 ($name, 3, $logtxt . 'autocreate device will be disabled, correct def with valid dpt and enable device') if ($init_done); + Log3 ($name, 3, $logtxt . 'autocreate device will be disabled, correct def with valid dpt and enable device'); + $attr{$name}->{disable} = 1 if (AttrVal($name,'disable',0) != 1); } elsif (!defined($dpttypes{$gadModel})) { #check model-type return $logtxt . qq{invalid model: $gadModel for group-number $gadNo} . @@ -505,9 +531,6 @@ sub KNX_Define { return ($logtxt . qq{invalid suffix for group-number $gadNo. Use $PAT_GAD_SUFFIX}) if (defined($gadNoSuffix) && ($gadNoSuffix !~ m/$PAT_GAD_SUFFIX/ix)); } - #save 1st gadName for later backwardCompatibility - $hash->{FIRSTGADNAME} = $gadName if ($gadNo == 1); - ###GADTABLE #create a hash with gadCode and gadName for later mapping my $tableHashRef = $hash->{GADTABLE}; @@ -545,7 +568,7 @@ sub KNX_Define { if (defined ($dptDetails->{SETLIST})) { # list is given, pass it through $setlist = q{:} . $dptDetails->{SETLIST}; } - elsif (defined ($dptDetails->{MIN}) and looks_like_number($dptDetails->{MIN})) { #number? - place slider + elsif (defined ($dptDetails->{MIN}) && looks_like_number($dptDetails->{MIN})) { #number? - place slider my $min = $dptDetails->{MIN}; my $max = $dptDetails->{MAX}; my $interval = int(($max-$min)/100); @@ -574,39 +597,21 @@ sub KNX_Define { #restore list @{$modules{KNX}->{defptr}->{$gadCode}} = @devList; - #create setlist/getlist for setFn / getFn + #create setlist for setFn / getlist will be created during get-cmd! my $setString = q{}; - my $getString = q{}; foreach my $key (keys %{$hash->{GADDETAILS}}) { - #no set-command for listenonly or get / no get cmds for set + #no set-command for get or listenonly my $option = $hash->{GADDETAILS}->{$key}->{OPTION}; - if (defined ($option)) { - if ($option eq 'get') { - $getString .= q{ } . $key . ':noArg'; - } - elsif ($option eq 'set') { - $setString .= ' on:noArg off:noArg' if (($hash->{GADDETAILS}->{$key}->{NO} == 1) && ($hash->{GADDETAILS}->{$key}->{MODEL} =~ /^(dpt1|dpt1.001)$/x)); - $setString .= q{ } . $key . $hash->{GADDETAILS}->{$key}->{SETLIST}; - } - # must be listenonly, do nothing - } - else { # no option def, select all - $getString .= q{ } . $key . ':noArg'; - $setString .= ' on:noArg off:noArg' if (($hash->{GADDETAILS}->{$key}->{NO} == 1) && ($hash->{GADDETAILS}->{$key}->{MODEL} =~ /^(dpt1|dpt1.001)$/x)); - $setString .= q{ } . $key . $hash->{GADDETAILS}->{$key}->{SETLIST}; + if ((defined($option) && $option eq 'set') || (! defined($option))) { + $setString .= 'on:noArg off:noArg ' if (($hash->{GADDETAILS}->{$key}->{NO} == 1) && ($hash->{GADDETAILS}->{$key}->{MODEL} =~ /^(dpt1|dpt1.001)$/x)); + $setString .= $key . $hash->{GADDETAILS}->{$key}->{SETLIST} . q{ }; } } - $setString =~ s/^[\s?](.*)/$1/ix; # trim leading blank - $getString =~ s/^[\s?](.*)/$1/ix; - $hash->{SETSTRING} = $setString; - $hash->{GETSTRING} = $getString; + $hash->{'.SETSTRING'} = $setString; - Log3 ($name, 5, $logtxt . qq{getstring= $hash->{GETSTRING} , setstring= $hash->{SETSTRING}}); + Log3 ($name, 5, qq{$logtxt setstring= $hash->{'.SETSTRING'}}); } - #backup name for a later rename - $hash->{DEVNAME} = $name; - Log3 ($name, 5, $logtxt . 'define complete'); return; } @@ -629,45 +634,38 @@ sub KNX_Undef { #The answer is treated as regular telegram ############################# sub KNX_Get { - my ($hash, $name, @args) = @_; - - #determine gadName to read - use first defined GAD if no argument is supplied - my $gadName = $args[0] // $hash->{FIRSTGADNAME}; + my $hash = shift; + my $name = shift; + my $gadName = shift // KNX_gadNameByNO($hash,1); # use first defined GAD if no argument is supplied + + return qq{KNX_Get ($name): gadName not defined} if (! defined($gadName)); + Log3 ($name, 3, qq{KNX_Get ($name): too much arguments. Only one argument allowed (gadName). Other Arguments are discarded.}) if (defined(shift)); #FHEM asks with a ? at startup - no action, no log - if dev is disabled: no SET/GET pulldown ! if ($gadName =~ m/\?/x) { - return (IsDisabled($name) == 1)?undef:qq{unknown argument choose one of $hash->{GETSTRING}}; -=pod -### option for future use - return if (IsDisabled($name) == 1); # no get option - - #check for a widgetoverride that we dont want in get's... - my @getlist = split(/\s/gix,$hash->{GETSTRING}); - my @wgoverride = split(/\s/gix,AttrVal($name,'widgetOverride',q{})); - foreach my $wgentry (@wgoverride) { -#tbT $wgentry =~ s/^([^:]).*/$1/gix; - $wgentry =~ s/(.*)[:].*/$1/gix; - @getlist = grep(! /$wgentry/, @getlist); # remove it + my $getter = q{}; + foreach my $key (keys %{$hash->{GADDETAILS}}) { + last if (! defined($key)); + my $option = $hash->{GADDETAILS}->{$key}->{OPTION}; + next if (defined($option) && $option =~ /(?:set|listenonly)/ix); + $getter .= q{ } . $key . ':noArg'; } - return qq{unknown argument $cmd choose one of } . join(q{ },@getlist); -=cut + $getter =~ s/^\s+//gix; #trim leading blank + $getter = q{} if (IsDisabled($name) == 1); + + return qq{unknown argument $gadName choose one of $getter}; } return qq{KNX_Get ($name): is disabled} if (IsDisabled($name) == 1); - Log3 ($name, 5, qq{KNX_Get ($name): -enter: CMD= $gadName}); - #no more than 1 argument allowed - Log3 ($name, 3, qq{KNX_Get ($name): too much arguments. Only one argument allowed (gadName). Other Arguments are discarded.}) if (scalar(@args) > 1); - + #return, if unknown group + return qq{KNX_Get ($name): invalid gadName: $gadName} if(! exists($hash->{GADDETAILS}->{$gadName})); #get groupCode, groupAddress, option my $groupc = $hash->{GADDETAILS}->{$gadName}->{CODE}; my $group = $hash->{GADDETAILS}->{$gadName}->{GROUP}; my $option = $hash->{GADDETAILS}->{$gadName}->{OPTION}; - #return, if unknown group - return qq{KNX_Get ($name): no valid address stored for gad: $gadName} if(!$groupc); - #exit if get is prohibited return qq{KNX_Get ($name): did not request a value - "set" or "listenonly" option is defined.} if (defined ($option) and ($option =~ m/(set|listenonly)/ix)); @@ -691,14 +689,16 @@ sub KNX_Set { #FHEM asks with a "?" at startup or any reload of the device-detail-view - if dev is disabled: no SET/GET pulldown ! if(defined($cmd) && ($cmd =~ m/\?/x)) { - return (IsDisabled($name) == 1)?undef:qq{unknown argument $cmd choose one of $hash->{SETSTRING}}; + my $setter = exists($hash->{'.SETSTRING'})?$hash->{'.SETSTRING'}:q{}; + $setter = q{} if (IsDisabled($name) == 1); + return qq{unknown argument $cmd choose one of $setter}; } + return $thisSub . 'is disabled' if (IsDisabled($name) == 1); - - Log3 ($name, 5, $thisSub . qq{-enter: $cmd } . join(q{ }, @arg)) if (defined ($cmd)); - return $thisSub . 'no parameter(s) specified for set cmd' if((!defined($cmd)) || ($cmd eq q{})); #return, if no cmd specified + Log3 ($name, 5, $thisSub . qq{-enter: $cmd } . join(q{ }, @arg)); + my $targetGadName = $cmd =~ s/^\s+|\s+$//girx; # gad-name or cmd (in old syntax) if (defined ($hash->{GADDETAILS}->{$targetGadName})) { # #new syntax, if first arg is a valid gadName @@ -718,14 +718,14 @@ sub KNX_Set { my $rdName = $hash->{GADDETAILS}->{$targetGadName}->{RDNAMESET}; my $model = $hash->{GADDETAILS}->{$targetGadName}->{MODEL}; - return $thisSub . q{did not set a value - "get" or "listenonly" option is defined.} if (defined ($option) and ($option =~ m/(get|listenonly)/ix)); + return $thisSub . q{did not set a value - "get" or "listenonly" option is defined.} if (defined ($option) and ($option =~ m/(?:get|listenonly)/ix)); my $value = $cmd; #process set command with $value as output #Text neads special treatment - additional args may be blanked words $value .= q{ } . join (q{ }, @arg) if (($model =~ m/^dpt16/ix) and (scalar (@arg) > 0)); #Special commands for dpt1 and dpt1.001 - if ($model =~ m/((dpt1)|(dpt1.001))$/ix) { + if ($model =~ m/^(?:dpt1|dpt1.001)$/ix) { (my $err, $value) = KNX_Set_dpt1($hash, $targetGadName, $cmd, @arg); return $err if defined($err); } @@ -741,9 +741,7 @@ sub KNX_Set { Log3 ($name, 4, $thisSub . qq{cmd= $cmd , value= $value , translated= $transvale}); # decode again for values that have been changed in encode process - if ($model =~ m/^(dpt3|dpt10|dpt11|dpt19)/ix) { - $transval = KNX_decodeByDpt($hash, $transvale, $targetGadName); - } + $transval = KNX_decodeByDpt($hash, $transvale, $targetGadName) if ($model =~ m/^(?:dpt3|dpt10|dpt11|dpt19)/ix); #apply post processing for state and set all readings KNX_SetReadings($hash, $targetGadName, $transval, $rdName, undef); @@ -776,12 +774,7 @@ sub KNX_Set_oldsyntax { return qq{an invalid gadName: $cmd was used in set-cmd}; } - foreach my $key (keys %{$hash->{GADDETAILS}}) { - if (int ($hash->{GADDETAILS}->{$key}->{NO}) == int ($groupnr)) { - $targetGadName = $key; - last; - } - } + $targetGadName = KNX_gadNameByNO($hash, $groupnr); return qq{gadName not found for $groupnr} if(!defined($targetGadName)); # all of the following cmd's need at least 1 Argument (or more) @@ -903,6 +896,7 @@ sub KNX_Set_dpt1 { #In case setstate is executed, a readingsupdate is initiated ############################# sub KNX_State { +=pod my $hash = shift; my $time = shift; my $reading = shift // return; @@ -920,7 +914,7 @@ sub KNX_State { #write value and update reading readingsSingleUpdate($hash, $reading, $value, 1); - +=cut return; } @@ -946,6 +940,24 @@ sub KNX_Attr { elsif ($aName eq 'IODev' && $init_done) { return KNX_chkIODev($hash,$aVal); } +=pod + # force @set for widgetoverride + # new function introduced 12/2022 by Rudi + elsif ($aName eq 'widgetOverride' ) { + my @overrides = split(/\s/ix,$aVal); + foreach my $overr (@overrides) { +### $overr =~ s/([^\@^\:]+)(?:@.*?)?:([^ ]+)?[\s]?/$1\@set:$2 /ixg; # add @set to each wgoverride + $overr =~ /([^\@^\:]+)(?:\@(?:set|get|attr))?:?([^ ]+)?[\s]?/ixg; + if (exists($hash->{GADDETAILS}->{$1}) && defined($2)) { # only for existing gadNames + $value .= qq{$1\@set:$2 }; + } + else { + $value .= $overr . q{ }; # append original + } + } + @_[3] = $value; # change $aVal in caller + } +=cut } # /set if ($cmd eq 'del') { @@ -968,24 +980,20 @@ sub KNX_Attr { ############################# sub KNX_DbLog_split { my ($event, $device) = @_; - my ($reading, $value, $unit); - #split input-string - my @strings = split (q{ }, $event); - return if (not defined ($strings[0])); + my $unit = q{}; # default - #detect reading - real reading or state? - if ($strings[0] =~ m/.*:$/x) { # real reading - $reading = shift(@strings); - $reading =~ s/:$//x; - } - else { + # split event into reading & value + my ($reading, $value) = split(/:\s/x, $event, 2); + if (! defined($reading)) { $reading = 'state'; - } - - #per default join all single pieces - $value = join(q{ }, @strings); + $value = $event; + } + # split value + my @strings = split(/\s/x, $value); + $strings[0] = q{} if (! defined($strings[0])); + #numeric value? and last value non numeric? - assume unit if (looks_like_number($strings[0]) && (! looks_like_number($strings[scalar(@strings)-1]))) { $value = join(q{ },@strings[0 .. (scalar(@strings)-2)]); @@ -1013,9 +1021,7 @@ sub KNX_Parse { Log3 ($ioName, 4, qq{KNX_Parse -enter: IO-name=$ioName src=} . KNX_hexToName2($src) . q{ dest=} . KNX_hexToName($gadCode) . qq{ msg=$msg}); #gad not defined yet, give feedback for autocreate - if (not (exists $modules{KNX}->{defptr}->{$gadCode})) { - return KNX_autoCreate($iohash,$gadCode); - } + return KNX_autoCreate($iohash,$gadCode) if (! (exists $modules{KNX}->{defptr}->{$gadCode})); #get list from device-hashes using given gadCode (==destination) # check on cmd line with: {PrintHash($modules{KNX}->{defptr},3) } @@ -1042,24 +1048,23 @@ sub KNX_Parse { my $getName = $deviceHash->{GADDETAILS}->{$gadName}->{RDNAMEGET}; my $transval = KNX_decodeByDpt ($deviceHash, $val, $gadName); #message invalid - if (not defined($transval) or ($transval eq q{})) { - Log3 ($deviceName, 2, qq{KNX_Parse ($deviceName): [wp] readingName=$getName message=$msg} . + if (! defined($transval) || ($transval eq q{})) { + Log3 ($deviceName, 2, qq{KNX_Parse_wp ($deviceName): readingName=$getName message=$msg} . ' could not be decoded'); next; } - Log3 ($deviceName, 4, qq{KNX_Parse ($deviceName): [wp] readingName=$getName value=$transval}); + Log3 ($deviceName, 4, qq{KNX_Parse_wp ($deviceName): readingName=$getName value=$transval}); #apply post processing for state and set all readings KNX_SetReadings($deviceHash, $gadName, $transval, $getName, $src); } #handle read messages - elsif ($cmd =~ /[r]/x) { + elsif ($cmd =~ /[r]/ix) { my $putName = $deviceHash->{GADDETAILS}->{$gadName}->{RDNAMEPUT}; - Log3 ($deviceName, 5, qq{KNX_Parse ($deviceName): [r] GET}); + Log3 ($deviceName, 5, qq{KNX_Parse_r ($deviceName): GET}); #answer "old school" -# my $value = ReadingsVal($deviceName, 'state', undef); # fetch default value from state my $value = undef; if (AttrVal($deviceName, 'answerReading', 0) != 0) { my $putVal = ReadingsVal($deviceName, $putName, undef); @@ -1076,11 +1081,11 @@ sub KNX_Parse { if ((defined($cmdAttr)) && ($cmdAttr ne q{})) { $value = KNX_eval ($deviceHash, $gadName, $value, $cmdAttr); if (defined($value) && ($value ne q{}) && ($value ne 'ERROR')) { # answer only, if eval was successful - Log3 ($deviceName, 5, qq{KNX_Parse ($deviceName): [r] replaced by Attr putCmd=$cmdAttr VALUE=$value}); - readingsSingleUpdate($deviceHash, $putName, $value,1); + Log3 ($deviceName, 5, qq{KNX_Parse_r ($deviceName): replaced by Attr putCmd=$cmdAttr VALUE=$value}); + readingsSingleUpdate($deviceHash, $putName, $value,0); } else { - Log3 ($deviceName, 5, qq{KNX_Parse ($deviceName): [r] gadName=$gadName - no reply sent!}); + Log3 ($deviceName, 5, qq{KNX_Parse_r ($deviceName): gadName=$gadName - no reply sent!}); $value = undef; # dont send ! } } @@ -1088,7 +1093,7 @@ sub KNX_Parse { #send transval if (defined($value)) { my $transval = KNX_encodeByDpt($deviceHash, $value, $gadName); - Log3 ($deviceName, 4, qq{KNX_Parse ($deviceName): [r] send answer: reading=$gadName VALUE=$transval}); + Log3 ($deviceName, 4, qq{KNX_Parse_r ($deviceName): send answer: reading=$gadName VALUE=$transval}); IOWrite ($deviceHash, $TULid, 'p' . $gadCode . $transval); } } @@ -1185,6 +1190,7 @@ sub KNX_chkIODev { push(@IOList2,$iodev); next if ($iodev ne $iocandidate); return if ($defs{$iodev}->{TYPE} ne 'FHEM2FHEM'); # ok for std io-dev + # add support for fhem2fhem as io-dev my $rawdef = $defs{$iodev}->{rawDevice}; #name of fake local IO-dev or remote IO-dev if (defined($rawdef)) { @@ -1320,8 +1326,7 @@ sub KNX_replaceByRegex { next if ($rdName ne $regName); # must match completely! if (not defined ($regPair[1])) { # cut value - Log3 ($name, 5, qq{KNX_replaceByRegex ($name): replaced $rdName value from: $input to undefined}); - return; + $retVal = 'undefined'; } elsif ($regPair[0] eq $tempVal) { # complete match $retVal = $regPair[1]; @@ -1336,7 +1341,7 @@ sub KNX_replaceByRegex { last; } Log3 ($name, 5, qq{KNX_replaceByRegex ($name): replaced $rdName value from: $input to $retVal}) if ($input ne $retVal); - return $retVal; + return ($retVal eq 'undefined')?undef:$retVal; } # limit numeric values. Valid directions: encode, decode @@ -1834,6 +1839,24 @@ sub dec_dpt232 { #RGB-Code return sprintf ('%.6x',$numval); } +### lookup gadname by gadnumber +### called from: KNX_Get, KNX_setOldsyntax +### entry: $hash, desired gadNO +### return: undef on error / gadName +sub KNX_gadNameByNO { + my $hash = shift; + my $groupnr = shift // 1; # default: seaarch for g1 + + my $targetGadName = undef; + foreach my $key (keys %{$hash->{GADDETAILS}}) { + if ($hash->{GADDETAILS}->{$key}->{NO} == $groupnr) { + $targetGadName = $key; + last; + } + } + return $targetGadName; +} + ########## public utility functions ########## ### get state of devices from KNX_Hardware @@ -1870,17 +1893,15 @@ sub main::KNX_scan { next if ($iostate ne 'connected'); $i++; - my $getstring = $devhash->{GETSTRING}; - next if ((! defined($getstring)) || $getstring eq q{}); - $j++; - my @getnames = split(/\s/ix,$getstring); - - foreach my $gads (@getnames) { - my $gad = (split(/[:]/ix,$gads))[0]; + my $k0 = $k; #save previous number of get's + foreach my $key (keys %{$devhash->{GADDETAILS}}) { + last if (! defined($key)); + my $option = $devhash->{GADDETAILS}->{$key}->{OPTION}; + next if (defined($option) && $option =~ /(?:set|listenonly)/ix); $k++; - Log3 ($knxdef, 4, qq{KNX_scan-exec: [$k] CMD= "get $knxdef $gad"}); - $getsarr .= $knxdef . q{ } . $gad . q{,}; + $getsarr .= $knxdef . q{ } . $key . q{,}; } + $j++ if ($k > $k0); } Log3 (undef, 3, qq{KNX_scan: $i devices selected / $j devices with get / $k "gets" executing...}); doKNX_scan($getsarr) if ($k > 0); @@ -2070,7 +2091,7 @@ The answer from the bus-device updates the readings <getName> and state.verbose
webCmd
webCmdLabel
-widgetOverride +widgetOverride

Special attributes

@@ -2122,6 +2143,11 @@ The answer from the bus-device updates the readings <getName> and state.NOT recommended unless you take special care with yr. knxd or KNX-router definitions - to prevent multiple path from KNX-Bus to FHEM resulting in message loops.
+
  • widgetOverride
    + This is a standard FHEMWEB-attribute, the recommendation for use in KNX-module is to specify the following form: + <gadName>@set:<widgetName,parameter> This avoids overwriting the GET pulldown in FHEMWEB detail page. + For details, pls see FHEMWEB-attribute.
  • +
  • listenonly - This attr is deprecated - use "listenonly" option in device definition
  • readonly - This attr is deprecated - use "get" option in device definition
  • slider - This attr is deprecated - use attribute widgetOverride <gadName>:slider,<start->,<step->,<end-range> instead