diff --git a/fhem/CHANGED b/fhem/CHANGED index 45234df82..c6adbed65 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,6 @@ # 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 + - feature: 10_KNX: new attribute readingNmap - feature: 76_SolarForecast: max. possible inverters set to 4 - feature: 98_vitoconnect.pm: enhanced error mapping now also language dependent diff --git a/fhem/FHEM/00_KNXIO.pm b/fhem/FHEM/00_KNXIO.pm index b52a431ce..7484de93d 100644 --- a/fhem/FHEM/00_KNXIO.pm +++ b/fhem/FHEM/00_KNXIO.pm @@ -82,9 +82,10 @@ # 25/04/2024 changed _open for mode S # replaced/removed experimental given/when # 19/08/2024 fix error-msg when mode S fails to open -# xx/11/2024 replace getimeofday w. Time::HiRes::time +# 07/11/2024 replace getimeofday w. Time::HiRes::time # use AttrNum instead of AttrVal where possible # PBP remove postfix if +# xx/01/2025 add a few responseIDs in readH fn. package KNXIO; ## no critic 'package' @@ -415,7 +416,8 @@ sub KNXIO_ReadH { my $name = $hash->{NAME}; - if ( unpack('n',$buf) != 0x0610) { + my ($header, $responseID, $total_length) = unpack('nnn',$buf); + if ($header != 0x0610) { KNXIO_Log ($name, 3, 'invalid Frame Header received - discarded'); return; } @@ -425,23 +427,34 @@ sub KNXIO_ReadH { my $rxseqcntr = 0; my $txseqcntr = 0; my $errcode = 0; - my $responseID = unpack('x2n',$buf); my %resIDs = ( + 0x0201 => sub { # search request + KNXIO_Log ($name, 4, 'SearchRequest received'); + return; + }, 0x0202 => sub { # Search response KNXIO_Log ($name, 4, 'SearchResponse received'); my (@contolpointIp, $controlpointPort) = unpack('x6CCCn',$buf); return; }, + 0x0203 => sub { # Decription request + KNXIO_Log ($name, 4, 'DescriptionRequest received'); + return; + }, 0x0204 => sub { # Decription response KNXIO_Log ($name, 4, 'DescriptionResponse received'); return; }, - 0x0206 => sub { # Connection response + 0x0205 => sub { # Connect request + KNXIO_Log ($name, 4, 'ConnectionRequest received'); + return; + }, + 0x0206 => sub { # Connect response ($hash->{KNXIOhelper}->{CCID},$errcode) = unpack('x6CC',$buf); # save Comm Channel ID,errcode RemoveInternalTimer($hash,\&KNXIO_keepAlive); if ($errcode > 0) { - KNXIO_Log ($name, 3, q{ConnectionResponse received } . + KNXIO_Log ($name, 3, q{ConnectResponse received } . qq{CCID= $hash->{KNXIOhelper}->{CCID} Status=} . KNXIO_errCodes($errcode)); KNXIO_disconnect($hash,2); return; @@ -454,6 +467,10 @@ sub KNXIO_ReadH { InternalTimer(Time::HiRes::time() + 60, \&KNXIO_keepAlive, $hash); # start keepalive return; }, + 0x0207 => sub { # ConnectionState request + KNXIO_Log ($name, 4, 'ConnectioStatenRequest received'); + return; + }, 0x0208 => sub { # ConnectionState response ($hash->{KNXIOhelper}->{CCID}, $errcode) = unpack('x6CC',$buf); RemoveInternalTimer($hash,\&KNXIO_keepAlive); @@ -1047,8 +1064,8 @@ sub KNXIO_dispatch2 { my $name = $hash->{NAME}; - $hash->{'msg_count'}++; - $hash->{'msg_time'} = TimeNow(); + $hash->{'MSGCNT'}++; + $hash->{'MSGTIME'} = TimeNow(); Dispatch($hash, $buf); @@ -1132,8 +1149,8 @@ sub KNXIO_closeDev { delete $hash->{stacktrace}; # clean delete $hash->{nextOpenDelay}; - delete $hash->{'msg_count'}; - delete $hash->{'msg_time'}; + delete $hash->{'MSGCNT'}; + delete $hash->{'MSGTIME'}; #NO! delete $hash->{'.CCID'}; delete $hash->{KNXIOhelper}->{SEQUENCECNTR}; @@ -1479,7 +1496,8 @@ __END__
  • S Socket mode - communicate via KNXD's UNIX-socket on localhost. default Socket-path: /var/run/knx
    Path might be different, depending on knxd-version or -config specification! - This mode is tested ok with KNXD version 0.14.30. It does NOT work with ver. 0.10.0! + This mode is tested ok with KNXD version 0.14.30. It does NOT work with ver. 0.10.0!
    + If you run FHEM and KNXD in Docker, this is the preferred mode to run both in network-mode bridge - see wiki!.
  • X Special mode - for details see KNXIO-wiki!
  • diff --git a/fhem/FHEM/10_KNX.pm b/fhem/FHEM/10_KNX.pm index 332e68712..102d5396e 100644 --- a/fhem/FHEM/10_KNX.pm +++ b/fhem/FHEM/10_KNX.pm @@ -193,8 +193,10 @@ # dpt18 "learn" now implemented for dpt18.001, experimental dpt18.099 removed # PBP: replace unless w. if not..., fix postfix if # cmd-ref update +# MH 20250201 fix dpt16 dblog-split fn +# feature: new attr readingNmap - modify readingnames on the fly +# internal code change: _define2, _attr, _set, _encodeByDpt # -# postponed new attr readingRegex - allow manipulating reading-names on the fly # todo-4/2024 remove support for oldsyntax cmd's: raw,value,string,rgb @@ -241,7 +243,6 @@ BEGIN { AnalyzePerlCommand AnalyzeCommandChain EvalSpecials fhemTimeLocal) ); -# addToDevAttrList # removed 11/2024 } #string constants @@ -546,9 +547,9 @@ my %dpttypes = ( 'dpt17.001' => {CODE=>'dpt17', UNIT=>q{}, PATTERN=>qr/[+]?\d{1,2}/xms, MIN=>0, MAX=>63}, # Scene, 1-64 - 'dpt18' => {CODE=>'dpt18', UNIT=>q{}, PATTERN=>qr/[+]?\d{1,2}/xms, OFFSET=>1, MIN=>1, MAX=>64, + 'dpt18' => {CODE=>'dpt18', UNIT=>q{}, PATTERN=>qr/(activate[,]|learn[,])?[+]?\d{1,2}/xms, OFFSET=>1, MIN=>1, MAX=>64, DEC=>\&dec_dpt18,ENC=>\&enc_dpt18,}, - 'dpt18.001' => {CODE=>'dpt18', UNIT=>q{}, PATTERN=>qr/(activate|learn)?[,]?[+]?\d{1,2}/xms, OFFSET=>1, MIN=>1, MAX=>64, SETLIST=>'widgetList,3,select,activate,learn,1,textField'}, + 'dpt18.001' => {CODE=>'dpt18', UNIT=>q{}, PATTERN=>qr/(activate[,]|learn[,])?[+]?\d{1,2}/xms, OFFSET=>1, MIN=>1, MAX=>64, SETLIST=>'widgetList,3,select,activate,learn,1,textField'}, #date and time 'dpt19' => {CODE=>'dpt19', UNIT=>q{}, PATTERN=>qr/($PAT_DATE$PAT_DTSEP$PAT_TIME|now)/ixms, MIN=>undef, MAX=>undef, @@ -616,7 +617,7 @@ sub Initialize { 'showtime:0,1 ' . #show event-time instead of value in device overview 'stateRegex:textField-long ' . #modifies state value 'stateCmd:textField-long ' . #modify state value -#not yet 'readingRegex:textField-long ' . #modify reading name + 'readingNmap:textField-long ' . #modify reading names 'putCmd:textField-long ' . #enable FHEM to answer KNX read telegrams 'format ' . #supplies post-string 'KNX_toggle:textField ' . #toggle source : @@ -684,8 +685,7 @@ sub KNX_Define2 { foreach my $gaddef (@a) { my $gadOption = undef; my $gadNoSuffix = undef; - - my $gadName = 'g' . $gadNo; # old syntax + my $gadName = 'g' . $gadNo; # old syntax KNX_Log ($name, 5, qq{gadNr= $gadNo def-string= $gaddef}); @@ -695,6 +695,10 @@ sub KNX_Define2 { push(@logarr,qq{GAD not defined or wrong format for group-number $gadNo, specify as 0-31/0-7/0-255}); next; } + elsif (defined($hash->{GADTABLE}->{$gadCode})) { + push(@logarr,qq{GAD $gad may be supplied only once per device}); + next; + } if (! defined($gadModel)) { push(@logarr,qq{no model defined for group-number $gadNo}); @@ -714,6 +718,7 @@ sub KNX_Define2 { $hash->{model} = $dpttypes{$gadModel}->{CODE}; } + # optional param if (scalar(@gadArgs)) { if ($gadArgs[-1] =~ /$PAT_GAD_SUFFIX/ixms) {$gadNoSuffix = lc(pop(@gadArgs));} if (@gadArgs && $gadArgs[-1] =~ /^($PAT_GAD_OPTIONS)$/ixms) {$gadOption = lc(pop(@gadArgs));} @@ -729,29 +734,10 @@ sub KNX_Define2 { $gadName = q{g} . $gadNo; } - if (defined($hash->{GADTABLE}->{$gadCode})) { - push(@logarr,qq{GAD $gad may be supplied only once per device}); - next; - } - $hash->{GADTABLE}->{$gadCode} = $gadName; #add key and value to GADTABLE - #cache suffixes - my ($suffixGet, $suffixSet) = qw(-get -set); - if (defined($gadNoSuffix)) {($suffixGet, $suffixSet) = (q{},q{});} - - # new syntax readingNames - my $rdNameGet = $gadName . $suffixGet; - my $rdNameSet = $gadName . $suffixSet; - if (($gadName eq 'g' . $gadNo) && (! defined($gadNoSuffix))) { # old syntax - $rdNameGet = 'getG' . $gadNo; - $rdNameSet = 'setG' . $gadNo; - } - - if (defined ($gadOption)) { - KNX_Log ($name, 5, qq{found GAD: $gad NAME: $gadName NO: $gadNo HEX: $gadCode DPT: $gadModel} . - qq{ OPTION: $gadOption}); - } + # create readingnames + my ($rdNameSet, $rdNameGet) = KNX_makeRdNames($hash, $gadName, $gadNo, $gadNoSuffix); #determine dpt-details my $dptDetails = $dpttypes{$gadModel}; @@ -768,7 +754,8 @@ sub KNX_Define2 { $setlist = q{:} . $min . q{,} . $max; } - KNX_Log ($name, 5, qq{Reading-names: $rdNameSet, $rdNameGet SetList: $setlist}); + KNX_Log ($name, 5, qq{define GAD: $gad NAME: $gadName NO: $gadNo HEX: $gadCode DPT: $gadModel} . + qq{ RD-NAMES: $rdNameSet $rdNameGet SETLIST: $setlist} ); #add details to hash $hash->{GADDETAILS}->{$gadName} = {CODE => $gadCode, MODEL => $gadModel, NO => $gadNo, OPTION => $gadOption, @@ -920,12 +907,8 @@ sub KNX_Set { return $err if defined($err); } - #Text neads special treatment - additional args may be blanked words - truncate to 14 char - elsif ($model =~ m/^dpt16(?:[.][\d]{3})?$/xms) { - if (scalar(@arg) > 0) {$value .= q{ } . join (q{ }, @arg);} - } - - my $transval = KNX_encodeByDpt($hash, $value, $targetGadName); #process set command + #special treatment for dpt 10,16,18 done in KNX_encodeByDpt + my $transval = KNX_encodeByDpt($hash, $targetGadName, $value, @arg); #process set command return qq{"set $name $targetGadName $value" failed, see Log-Messages} if (!defined($transval)); # encodeByDpt failed IOWrite($hash, $KNXID, 'w' . $groupCode . $transval); @@ -933,7 +916,9 @@ sub KNX_Set { KNX_Log ($name, 5, qq{cmd= $cmd , value= $value , translated= $transval}); # decode again for values that have been changed in encode process - if ($model =~ m/^dpt(?:3|10|11|16|18|19)(?:[.][\d]{3})?$/xms) {$value = KNX_decodeByDpt($hash, $transval, $targetGadName);} + if ($model =~ m/^dpt(?:3|10|11|16|18|19)(?:[.][\d]{3})?$/xms) { + $value = KNX_decodeByDpt($hash, $targetGadName, $transval); + } #apply post processing for state and set all readings KNX_SetReadings($hash, $targetGadName, $value, undef, undef); @@ -975,6 +960,7 @@ sub KNX_Set_oldsyntax { # pass thru -for-timer,-until,blink cmds... return (undef, $targetGadName, $cmd, @arg) if ($cmd =~ m/(?:-till|-until|-till-overnight|-for-timer|$BLINK)$/ixms); +## prepare for deletion of deprecated cmds my $code = $hash->{GADDETAILS}->{$targetGadName}->{MODEL}; my $value = shift(@arg); my $rtxt = qq{"set $name $cmd $value }; # log text @@ -1000,10 +986,14 @@ sub KNX_Set_oldsyntax { return q{invalid cmd: } . $rtxt . join(q{ },@arg) . q{" issued - ignored}; } - KNX_Log ($name, 3, q{This cmd will be deprecated by 1/2024: } . $rtxt . join(q{ },@arg) . + KNX_Log ($name, 2, q{This cmd is deprecated since 1/2024: } . $rtxt . join(q{ },@arg) . qq{" - use: "set $name $targetGadName $value } . join(q{ },@arg) . q{"} ); return (undef, $targetGadName, $value, @arg); +## prepare for deletion of deprecated cmds +## my $rtxt = q{invalid cmd: "set } . qq{$name $cmd $value } . join(q{ },@arg) . q{" issued - ignored}; +## KNX_Log ($name, 2, $rtxt); +## return $rtxt; } # process special dpt1, dpt1.001 set @@ -1060,7 +1050,6 @@ sub KNX_Set_dpt1 { my $hms_til = ::FmtDateTime($endTS) =~ s/[\s]/T/rxms; # fhem.pl alternativ use full datespec $hash->{".TIMER_$groupCode"} = $hms_til; #create local marker #place at-command for switching on / off -# CommandDefMod(undef, '-temporary ' . $name . qq{_TIMER_$groupCode at $hms_til set $name $targetGadName $tvalue}); CommandDefMod(undef, '-silent ' . $name . qq{_TIMER_$groupCode at $hms_til set $name $targetGadName $tvalue}); return (undef,$value); } @@ -1131,12 +1120,14 @@ sub KNX_State { sub KNX_Attr { my ($cmd,$name,$aName,$aVal) = @_; - my $hash = $defs{$name}; + return KNX_Attr_readingNmap($cmd, $name, $aVal) if ($aName eq q{readingNmap}); + + my $hash = $defs{$name}; my $value = undef; if ($cmd eq 'set' && $init_done) { # check valid IODev - if ($aName eq 'IODev') { return KNX_chkIODev($hash,$aVal); } + return KNX_chkIODev($hash,$aVal) if ($aName eq 'IODev'); if ($aName eq 'KNX_toggle') { # validate device/reading my ($srcDev,$srcReading) = split(qr/:/xms,$aVal); # format: : @@ -1152,26 +1143,22 @@ sub KNX_Attr { return qq{syntax check failed for $aName: \n $err} if($err); } - elsif ($aName =~ /(stateRegex|readingRegex)/xms) { # test for syntax errors -# elsif ($aName eq 'stateRegex') { # test for syntax errors + elsif ($aName eq 'stateRegex') { # test for syntax errors my @aValS = split(/\s+|\//xms,$aVal); foreach (@aValS) { next if ($_ eq q{} ); my $err = main::CheckRegexp($_,'Attr ' . $aName); # fhem.pl -# my $err = main::CheckRegexp($_,'Attr stateRegex'); # fhem.pl return qq{syntax check failed for $aName: \n $err} if (defined($err)); } } } # /set - if ($cmd eq 'del') { + elsif ($cmd eq 'del') { if ($aName eq 'disable') { - my @defentries = split(/\s/ixms,$hash->{DEF}); + my @defentries = split(/[\s\t\n]+/xms,$hash->{DEF}); foreach my $def (@defentries) { # check all entries - next if ($def eq ReadingsVal($name,'IODev',undef)); # deprecated IOdev +# next if ($def eq ReadingsVal($name,'IODev',undef)); # deprecated IOdev next if ($def =~ /:dpt(?:[\d+]|RAW)/xms); - - KNX_Log ($name, 2, q{Attribut "disable" cannot be deleted for this device until you specify a valid dpt!}); return qq{Attribut "disable" cannot be deleted for device $name until you specify a valid dpt!}; } delete $hash->{RAWMSG}; # debug internal @@ -1180,14 +1167,57 @@ sub KNX_Attr { return; } +### special Handling Attr readingNmap +############################# +sub KNX_Attr_readingNmap { + my ($cmd, $name, $aVal) = @_; + + if(ref($cmd) eq 'HASH') { # called from timer + ($cmd, $name, $aVal) = ($cmd->{cmd}, $cmd->{name}, $cmd->{aVal}); + } + my $hash = $defs{$name}; + + if ($cmd eq 'set' ) { + if (! $init_done) { #postpone attr def + my $param = {cmd=>$cmd, name=>$name, aVal=>$aVal}; + InternalTimer(Time::HiRes::time() + 3, \&KNX_Attr_readingNmap, $param); + return; + } + my @aValS = split(/\s+|\//xms,$aVal); + delete $hash->{Helper}->{RDNAMEMAP}; + foreach my $pairs (@aValS) { + my ($gadName, $newrdName) = split(/[:]/xms,$pairs); + if (! (defined($gadName) && (exists( $hash->{GADDETAILS}->{$gadName})))) { + return qq{invalid first parameter $gadName, use a valid }; + } + next if (! defined($newrdName)); + #delete affected readings + ::readingsDelete($hash, $hash->{GADDETAILS}->{$gadName}->{RDNAMESET}); # fhem.pl + ::readingsDelete($hash, $hash->{GADDETAILS}->{$gadName}->{RDNAMEGET}); # fhem.pl + $hash->{Helper}->{RDNAMEMAP}->{$gadName} = $newrdName; + } + } + + elsif ($cmd eq 'del') { # delete mapped readings + foreach my $gadName (keys %{$hash->{Helper}->{RDNAMEMAP}}) { + ::readingsDelete($hash, $hash->{GADDETAILS}->{$gadName}->{RDNAMESET}); # fhem.pl + ::readingsDelete($hash, $hash->{GADDETAILS}->{$gadName}->{RDNAMEGET}); # fhem.pl + } + delete $hash->{Helper}->{RDNAMEMAP}; + } + + CommandDefMod(undef, '-silent ' . $name . ' KNX ' . $hash->{DEF}); + return; +} + #Split reading for DBLOG ############################# sub KNX_DbLog_split { my $event = shift; my $device = shift; - my $reading = 'state'; # default - my $unit = q{}; # default + my $reading = q{state}; # default + my $unit = q{}; # default my $dpt16flag = 0; # is it a dpt16 ? # split event into pieces @@ -1202,17 +1232,21 @@ sub KNX_DbLog_split { #numeric value? and last value non numeric? - assume unit - except for dpt16 my $devhash = $defs{$device}; foreach my $key (keys %{$devhash->{GADDETAILS}}) { - next if ($key ne $reading); next if ($devhash->{GADDETAILS}->{$key}->{MODEL} !~ /^dpt16/xms); - $dpt16flag = 1; + next if ($reading ne q{state} && + $reading ne $devhash->{GADDETAILS}->{$key}->{RDNAMESET} && + $reading ne $devhash->{GADDETAILS}->{$key}->{RDNAMEGET}); + $dpt16flag = 1; last; } - if (($dpt16flag == 0) && Scalar::Util::looks_like_number($strings[0]) && (! Scalar::Util::looks_like_number($strings[scalar(@strings)-1]))) { + if (($dpt16flag == 0) && + Scalar::Util::looks_like_number($strings[0]) && + (! Scalar::Util::looks_like_number($strings[scalar(@strings)-1]))) { $unit = pop(@strings); } my $value = join(q{ },@strings); - if (!defined($unit)) {$unit = q{};} +# if (!defined($unit)) {$unit = q{};} KNX_Log ($device, 5, qq{EVENT= $event READING= $reading VALUE= $value UNIT= $unit}); return ($reading, $value, $unit); @@ -1253,7 +1287,6 @@ sub KNX_Parse { my $gadName = $deviceHash->{GADTABLE}->{$gadCode}; if (IsDevice($deviceName)) { push(@foundMsgs, $deviceName);} # save to list even if dev is disabled - dev must exist! -# push(@foundMsgs, $deviceName); # save to list even if dev is disabled if (IsDisabled($deviceName) == 1) { $deviceHash->{RAWMSG} = qq{gadName=$gadName cmd=$cmd, hexvalue=$val}; # for debugging @@ -1262,8 +1295,7 @@ sub KNX_Parse { =begin comment # ignore input from "wrong" IO-dev - my $IODevAttr = AttrVal($deviceName,'IODev',$ioName); - if ($IODevAttr ne $ioName) { + if (AttrVal($deviceName,'IODev',$ioName) ne $ioName) { KNX_Log ($deviceName, 2, qq{msg for gad-name: $gadName from wrong IO-device: $ioName - ignored}); next; } @@ -1294,7 +1326,7 @@ sub KNX_Parse { #handle GroupValueWrite and GroupValueResponse messages if ($cmd =~ /[w|p]/ixms) { #decode message - my $transval = KNX_decodeByDpt ($deviceHash, $val, $gadName); + my $transval = KNX_decodeByDpt ($deviceHash, $gadName, $val); #message invalid if (! defined($transval)) { KNX_Log ($deviceName, 2, qq{readingName=$getName message=$msg could not be decoded}); @@ -1344,7 +1376,7 @@ sub KNX_Parse { KNX_Log ($deviceName, 5, qq{replaced by Attr putCmd=$cmdAttr VALUE=$value}); #send transval - my $transval = KNX_encodeByDpt($deviceHash, $value, $gadName); + my $transval = KNX_encodeByDpt($deviceHash, $gadName, $value); if (defined($transval)) { KNX_Log ($deviceName, 4, qq{putCmd send answer: readingName=$putName value=$value sendvalue=$transval}); readingsSingleUpdate($deviceHash, $putName, $value,0); @@ -1409,9 +1441,6 @@ sub KNX_SetReadings { $rdName = $hash->{GADDETAILS}->{$gadName}->{RDNAMEGET}; } -### test remapping of readings -# $rdName = KNX_readingRegex($hash,$rdName); # modify reading name - #execute stateRegex my $state = KNX_replaceByRegex ($hash, $rdName, $value); @@ -1446,9 +1475,9 @@ sub doKNX_Set { my @param = @_; my ($hash, $gadName, $cmd) = ($param[0]->{hash}, $param[0]->{gadName}, $param[0]->{cmd}); - my $name = $hash->{NAME}; - my @arg = (); - $arg[0] = $cmd; + my $name = $hash->{NAME}; + my @arg = (); + $arg[0] = $cmd; return KNX_Set($hash, $name, $gadName, @arg); } @@ -1587,39 +1616,34 @@ sub KNX_replaceByRegex { return ($retVal eq 'undefined')?undef:$retVal; } -### replace reading Name by regex -### called from KNX_SetReadings -### input: hash, readingname -### return: new readingname -sub KNX_readingRegex { - my $hash = shift; - my $rName = shift; +### make and replace reading names +### called from KNX_define2 +### input: hash, gadName, gadNo, nosuffix +### return: rdNameset, rdNameget +sub KNX_makeRdNames { + my $hash = shift; + my $gadName = shift; + my $gadNo = shift; # // $hash->{GADDETAILS}->{$gadName}->{NO}; # sttartup? + my $nosuffix = shift; - my $name = $hash->{NAME}; - my $regAttr = AttrVal($name, 'readingRegex', undef); - return $rName if (! defined($regAttr)); - - #get array of given attributes - my @reg = split(/\s\//xms, $regAttr); - my $newrName = $rName; # default if no match - - #loop over all regex - foreach my $regex (@reg) { - #trim leading and trailing slashes - $regex =~ s/^\/|\/$//gixms; - # get pairs - my @regPair = split(/\//xms, $regex); -#KNX_Log ($name, 1, qq{r0=$regPair[0] r1=$regPair[1] rName=$rName}); - next if ((! defined($regPair[0])) || ($rName !~ /$regPair[0]/xms) ); # no match - if ($regPair[0] =~ /[(].*[)]/xms) { # we have a capture group - my $g1 = $rName =~ s/$regPair[0]/$1/rxms; # extract 1st capture group - $regPair[1] =~ s/\$1/$g1/xms; - } - $newrName =~ s/$regPair[0]/$regPair[1]/xms; - - KNX_Log ($name, 4, qq{replaced $rName with $newrName}); + my $rdNameSet = $gadName; + if (exists ($hash->{Helper}->{RDNAMEMAP}->{$gadName})) { + $rdNameSet = $hash->{Helper}->{RDNAMEMAP}->{$gadName}; } - return $newrName; + + my $rdNameGet = $rdNameSet; # assume nosuffix + + if (! defined($nosuffix)) { + if ($rdNameSet eq 'g' . $gadNo) { # old syntax + $rdNameSet = q{setG} . $gadNo; + $rdNameGet = q{getG} . $gadNo; + } + else { + $rdNameSet .= q{-set}; + $rdNameGet .= q{-get}; + } + } + return ($rdNameSet, $rdNameGet); } ### limit numeric values. Valid directions: encode, decode @@ -1693,9 +1717,7 @@ sub KNX_eval { ### encode KNX-Message according DPT # on return: hex string to be sent to bus / undef on error sub KNX_encodeByDpt { - my $hash = shift; - my $value = shift; - my $gadName = shift; + my ($hash, $gadName, $value, @arg) = @_; my $name = $hash->{NAME}; my $model = $hash->{GADDETAILS}->{$gadName}->{MODEL}; @@ -1704,10 +1726,14 @@ sub KNX_encodeByDpt { return if ($model eq $MODELERR); #return unchecked, if this is a autocreate-device # special handling - if ($model !~ /^dpt16/xms) { # ...except for txt! + if ($code eq 'dpt16') { # $arg contains additional words + if (scalar(@arg) > 0) {$value .= q{ } . join(q{ }, @arg);} + } + else { # strip off whitespace and unit $value =~ s/^\s+|\s+$//gxms; # white space at begin/end $value = (split(/\s+/xms,$value))[0]; # strip off unit } + # compatibility with widgetoverride :time if ($code eq 'dpt10' && $value =~ /^[\d]{2}:[\d]{2}$/xms) {$value .= ':00';} @@ -1715,9 +1741,16 @@ sub KNX_encodeByDpt { my $arg1 = undef; my $arg2 = undef; if ($code eq 'dpt18') { - ($arg1, $arg2) = split(/[,]/xms,$value); - $value = (defined($arg2))?$arg2:$arg1; + if ($value =~ /(activate|learn)$/xms) { + $value = shift(@arg); # blank separated + $arg1 = $1; + } + else { + ($arg1, $arg2) = split(/[,]/xms,$value); # widget or just number? + $value = (defined($arg2))?$arg2:$arg1; + } } + # match against model pattern my $pattern = $dpttypes{$model}->{PATTERN}; if ($value !~ /^$pattern$/ixms) { @@ -1746,12 +1779,12 @@ sub KNX_encodeByDpt { # on return: decoded value from bus / on error: undef sub KNX_decodeByDpt { my $hash = shift; - my $value = shift; my $gadName = shift; + my $value = shift; - my $name = $hash->{NAME}; + my $name = $hash->{NAME}; my $model = $hash->{GADDETAILS}->{$gadName}->{MODEL}; - my $code = $dpttypes{$model}->{CODE}; + my $code = $dpttypes{$model}->{CODE}; return if ($model eq $MODELERR); #return unchecked, if this is a autocreate-device @@ -2417,12 +2450,12 @@ The optional parameteter <gadName> may contain an alias for the GAD. The f state, on, off, on-for-timer, on-until, off-for-timer, off-until, toggle, raw, rgb, string, value, set, get, listenonly, nosuffix - because of conflict with cmds & parameters.
    If you supply <gadName> this name is used instead as cmd prefix. The reading-names are <gadName>-get and <gadName>-set. - The synonyms <getName> and <setName> are used in this documentation.
    + The synonyms <getName> and <setName> are used in this documentation. + Reading-names may be modified by attribute readingNmap, see examples.
    If you add the option "nosuffix", <getName> and <setName> have the identical name - <gadName>. - Both sent and received bus messages will be stored in the same reading <gadName>
    + Both sent and received bus messages will be stored in the same reading <gadName>.
    If you want to restrict the GAD, use the options "get", "set", or "listenonly". The usage is described in Set/Get-cmd chapter. - It is not possible to combine the options.
    - + It is not possible to combine the options.

    Specifying an IO-Device in define is deprecated! Use attribute IODev instead, but only if absolutely required!

    @@ -2525,9 +2558,9 @@ Examples:
  • showtime
  • sortby
  • stateFormat
  • +
  • suppressReading
  • timestamp-on-change-reading
  • userReadings
  • -
  • suppressReading
  • userattr
  • verbose
  • webCmd
  • @@ -2539,23 +2572,24 @@ Examples:
  • Special attributes
      -
    • stateRegex
      This function modifies the value of reading state. It is executed directly after replacing the reading-names and processing "format" Attr, but before stateCmd.
      @@ -2639,11 +2673,11 @@ Examples:
    • Events
      -

      Events are generated for each reading sent or received to/from KNX-Bus unless restricted by event-xxx attributes - or modified by eventMap, stateRegex attributes. +

      Events are generated for each message sent or received to/from KNX-Bus unless restricted by event-xxx attributes + or modified by readingNmap, eventMap, stateRegex attributes.
      KNX-events have this format:

      -
         <device> <readingName>: <value> # reading event
      -   <device> <value>                # state event
      +
         <device> <readingName>: <value> [<unit>] # reading event, unit optional
      +   <device> <value> [<unit>]                # state event
    • DPT - data-point-types