From 67c02b9c85f8efc6cacca925d5035b39e93eb577 Mon Sep 17 00:00:00 2001 From: erwin <> Date: Mon, 4 Oct 2021 19:48:15 +0000 Subject: [PATCH] 10_KNX.pm: multiple bugfixes & cleanup, pls. check (Forum Thread #122582) git-svn-id: https://svn.fhem.de/fhem/trunk@25046 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 1 + fhem/FHEM/10_KNX.pm | 497 +++++++++++++++++++++----------------------- fhem/MAINTAINER.txt | 2 +- 3 files changed, 234 insertions(+), 266 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index 3be5820bf..b4d13af02 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. + - change: 10_KNX: multiple fixes & cleanup - see Forum Thread #122582 - bugfix: 48_MieleAtHome: fix but in autocreate - feature: 32_withings: added vascularAge reading - feature: 49_SSCamSTRM: new setter snap diff --git a/fhem/FHEM/10_KNX.pm b/fhem/FHEM/10_KNX.pm index 62c23036e..ff24388e0 100644 --- a/fhem/FHEM/10_KNX.pm +++ b/fhem/FHEM/10_KNX.pm @@ -1,39 +1,7 @@ ############################################## # $Id$ # ABU 20180218 restructuring, removed older documentation -# ABU 20180317 setExtensions reingebaut, set funktion -# ABU 20180319 repaired "reply"-function -# ABU 20180319 tuned "reply"-function -# ABU 20180322 switch context for put-cmd, minor fixes -# ABU 20180328 fixed get-name containing "-" -# ABU 20180408 Added attriut screening, implemented set/get/listenonly, prevent to identical GADS in one device -# ABU 20180411 Added timer functions, prevented two identical GAD in one device -# ABU 20180413 Fixed some naming issues in defined, made en-doku, removed DE-Doku -# ABU 20180416 corrected timedev in doku -# ABU 20180418 removed spam-log in "get"; replaced "$value" by "undef" in encode and decode function if model not defined -# ABU 20180419 fixed Doku, added nosuffix, added dpt1.000 -# ABU 20180426 minor fixes in answering bus-requests -# ABU 20180509 Added dpt14.033 -# ABU 20180519 Added dpt17.001, adjustet $PAT_GAD_OPTIONS with boundaries and whitespace -# ABU 20180523 Added dpt7.007 -# ABU 20180528 Patched dpt1 in dpt-list and encodyByDpt for being backward-compatible -# ABU 20180604 Set dpt17-offset to "0", added examples -# ABU 20180605 Added dpt18, tuned doku -# ABU 20180605 Corrected dpt18 -# ABU 20180605 Added example for autogenerated devices -# ABU 20180605 Added workaround for STATE -# ABU 20180606 Fixed dpt18, fixed offset-addition in decode (was "-" instead of "+"), fixed issue with slider -# ABU 20180607 seperated limit and scale from encode/decode in order to avoid warnings and clean up -# ABU 20180613 fixed scaling algo -# ABU 20180613 fixed scaling algo part 2 -# ABU 20180624 no set-option for listenoly- or get-devices, warning for illegal EVAL -# ABU 20180626 fixed last changes -# ABU 20180706 changed eval, removed stateCopy -# ABU 20180706 fixed doku: changed readonly in listenonly -# ABU 20180815 updated link in doku, changed (dpt16$) to dpt16 in set, tried to fix last-sender (replaced bulk by single in decoding loop) -# ABU 20180829 added dpt9.0020, tried workaround in putCmd, remove non printable chars -# ABU 20180925 added dpt3.007, added last-sender "fhem" -# ABU 20180926 fixed KNX_Eval in line 1291 (replaced hash by deviceHash), fixed decoding dpt3 +# MH 20210908 deleted part of change history # ABU 20181007 fixed dpt19 # HAUSWART 20201112 implemented DPT20.102 #91462, KNX_parse set & get #115122, corrected dpt1 / dpt1.001 #112538 # HAUSWART 20201113 fixed dpt19 #91650, KNX_hexToName2 @@ -76,16 +44,27 @@ # MH 20210818 E04.67 1st checkin SVN version # docu correction # MH 20210829 fix crash when using Attr KNX_toogle & on-until (related to package) +# MH 20211002 E04.68 IODev specification on define is now deprecated +# check for valid IO-Module during IODev-Attr definition +# changed policy on forbidden GADNames e.g.: onConnect is now allowed +# removed "private" eversion +# removed unnecessary "return $UNDEF" +# removed sub KNX_Notify - not needed! +# fixed "old syntax" dpt16 set Forum #122779 +# modified examples in cmdref - added wiki link +# prevent deletion of Attr disable until a valid dpt is defined +# changed AnalyzePerlCommand to AnalyzeCommandChain to allow multiple fhem cmds in eval's +# code cleanup -#04.65 package main; -package FHEM::KNX; ## no critic 'package' #04.65 +package FHEM::KNX; ## no critic 'package' use strict; use warnings; -use Encode; -use Time::HiRes qw(gettimeofday); #04.65 nxt 3 lines +use Encode qw(encode decode); +use Time::HiRes qw(gettimeofday); use Scalar::Util qw(looks_like_number); +#use SetExtensions; # not yet! use GPUtils qw(GP_Import GP_Export); # Package Helper Fn ### perlcritic parameters @@ -107,7 +86,7 @@ BEGIN { AttrVal ReadingsVal ReadingsNum addToDevAttrList AssignIoPort IOWrite - CommandDefine CommandDelete CommandModify + CommandDefMod CommandModify CommandDelete defs modules attr FW_detail FW_wname FW_directNotify readingFnAttributes @@ -116,7 +95,7 @@ BEGIN { init_done IsDisabled IsDummy IsDevice deviceEvents devspec2array - AnalyzePerlCommand EvalSpecials + AnalyzePerlCommand AnalyzeCommandChain EvalSpecials fhemTimeLocal) ); } @@ -124,16 +103,13 @@ BEGIN { ### export to main context (with different name) GP_Export(qw(Initialize)); -### MH Evolution Version string -my $Eversion = '04.67 18-08-2021'; - #string constants -my $modelErr = "MODEL_NOT_DEFINED"; # for autocreate +my $MODELERR = "MODEL_NOT_DEFINED"; # for autocreate -my $ONFORTIMER = "on-for-timer"; -my $OFFFORTIMER = "off-for-timer"; -my $ONUNTIL = "on-until"; -my $OFFUNTIL = "off-until"; +#my $ONFORTIMER = "on-for-timer"; +#my $OFFFORTIMER = "off-for-timer"; +#my $ONUNTIL = "on-until"; +#my $OFFUNTIL = "off-until"; my $TOGGLE = "toggle"; my $RAW = "raw"; my $RGB = "rgb"; @@ -150,7 +126,7 @@ my $PAT_GAD_HEX = '[01][0-9a-f]{4}'; # max is 1FFFF -> 31/15/255 #pattern for group-no my $PAT_GNO = '[gG][1-9][0-9]?'; #pattern for GAD-Options -my $PAT_GAD_OPTIONS = '(get|set|listenonly)'; +my $PAT_GAD_OPTIONS = 'get|set|listenonly'; #pattern for GAD-suffixes my $PAT_GAD_SUFFIX = 'nosuffix'; #pattern for forbidden GAD-Names @@ -166,8 +142,6 @@ my $PAT_DATE = qr/(3[01]|[0-2]?[0-9])\.(1[0-2]|0?[0-9])\.((?:19|20)[0-9][0-9])/i my $PAT_TIME = qr/(2[0-4]|[0?1][0-9]):([0?1-5][0-9]):([0?1-5][0-9])/ix; my $PAT_DPT16_CLR = qr/>CLR {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|(move_(up_down|and_step_mode)))/ix, MIN=>"move_up_down", MAX=>"move_and_step_mode"}, #Step value (two-bit) - "dpt2" => {CODE=>"dpt2", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(forceon)|(forceoff))/ix, MIN=>undef, MAX=>undef, SETLIST=>"on,off,forceon,forceoff"}, + "dpt2" => {CODE=>"dpt2", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/(on|off|forceon|forceoff)/ix, MIN=>undef, MAX=>undef, SETLIST=>'on,off,forceon,forceoff'}, "dpt2.000" => {CODE=>"dpt2", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/(0?[0-3])/ix, MIN=>0, MAX=>3}, #Step value (four-bit) @@ -217,9 +191,9 @@ my %dpttypes = ( "dpt5.004" => {CODE=>"dpt5", UNIT=>q{%}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>0, MAX=>255}, # 1-Octet signed value - "dpt6" => {CODE=>"dpt6", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>-127, MAX=>127}, + "dpt6" => {CODE=>"dpt6", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>-128, MAX=>127}, "dpt6.001" => {CODE=>"dpt6", UNIT=>q{%}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>0, MAX=>100}, - "dpt6.010" => {CODE=>"dpt6", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>-127, MAX=>127}, + "dpt6.010" => {CODE=>"dpt6", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>-128, MAX=>127}, # 2-Octet unsigned Value "dpt7" => {CODE=>"dpt7", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>0, MAX=>65535}, @@ -229,12 +203,12 @@ my %dpttypes = ( "dpt7.007" => {CODE=>"dpt7", UNIT=>q{h}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>0, MAX=>65535}, "dpt7.012" => {CODE=>"dpt7", UNIT=>q{mA}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>0, MAX=>65535}, "dpt7.013" => {CODE=>"dpt7", UNIT=>q{lux}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>0, MAX=>65535}, - "dpt7.600" => {CODE=>"dpt7", UNIT=>q{K}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+]?\d{1,5}/ix, MIN=>0, MAX=>12000}, # 04.66 Farbtemperatur + "dpt7.600" => {CODE=>"dpt7", UNIT=>q{K}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+]?\d{1,5}/ix, MIN=>0, MAX=>12000}, # Farbtemperatur # 2-Octet signed Value "dpt8" => {CODE=>"dpt8", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>-32768, MAX=>32767}, "dpt8.005" => {CODE=>"dpt8", UNIT=>q{s}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>-32768, MAX=>32767}, - "dpt8.010" => {CODE=>"dpt8", UNIT=>q{%}, FACTOR=>0.01, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>-327.68, MAX=>327.67}, #04.66 min/max + "dpt8.010" => {CODE=>"dpt8", UNIT=>q{%}, FACTOR=>0.01, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>-327.68, MAX=>327.67}, # min/max "dpt8.011" => {CODE=>"dpt8", UNIT=>q{°}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>-32768, MAX=>32767}, # 2-Octet Float value @@ -258,8 +232,8 @@ my %dpttypes = ( "dpt9.025" => {CODE=>"dpt9", UNIT=>q{l/h}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, "dpt9.026" => {CODE=>"dpt9", UNIT=>q{l/h}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, "dpt9.028" => {CODE=>"dpt9", UNIT=>q{km/h}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, - "dpt9.029" => {CODE=>"dpt9", UNIT=>q{g/m³}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, #04.66 Abs. Luftfeuchte - "dpt9.030" => {CODE=>"dpt9", UNIT=>q{μg/m³}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, #04.66 Dichte + "dpt9.029" => {CODE=>"dpt9", UNIT=>q{g/m³}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, # Abs. Luftfeuchte + "dpt9.030" => {CODE=>"dpt9", UNIT=>q{μg/m³}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, # Dichte # Time of Day "dpt10" => {CODE=>"dpt10", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_TIME|now)/ix, MIN=>undef, MAX=>undef}, @@ -286,9 +260,9 @@ my %dpttypes = ( "dpt14.057" => {CODE=>"dpt14", UNIT=>q{cos Φ}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/ix, MIN=>undef, MAX=>undef}, # 14-Octet String - "dpt16" => {CODE=>"dpt16", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/.{1,14}/ix, MIN=>undef, MAX=>undef, SETLIST=>"multiple,>CLR<"}, - "dpt16.000" => {CODE=>"dpt16", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/.{1,14}/ix, MIN=>undef, MAX=>undef, SETLIST=>"multiple,>CLR<"}, - "dpt16.001" => {CODE=>"dpt16", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/.{1,14}/ix, MIN=>undef, MAX=>undef, SETLIST=>"multiple,>CLR<"}, + "dpt16" => {CODE=>"dpt16", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/.{1,14}/ix, MIN=>undef, MAX=>undef, SETLIST=>'multiple,>CLR<'}, + "dpt16.000" => {CODE=>"dpt16", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/.{1,14}/ix, MIN=>undef, MAX=>undef, SETLIST=>'multiple,>CLR<'}, + "dpt16.001" => {CODE=>"dpt16", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/.{1,14}/ix, MIN=>undef, MAX=>undef, SETLIST=>'multiple,>CLR<'}, # Scene, 0-63 "dpt17.001" => {CODE=>"dpt5", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>0, MAX=>63}, @@ -301,16 +275,15 @@ my %dpttypes = ( "dpt19.001" => {CODE=>"dpt19", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DATE$PAT_DTSEP$PAT_TIME|now)/ix, MIN=>undef, MAX=>undef}, # HVAC mode, 1Byte - "dpt20.102" => {CODE=>"dpt20", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/((auto)|(comfort)|(standby)|(economy|night)|(protection|frost|heat))/ix, MIN=>undef, MAX=>undef, SETLIST=>"Auto,Comfort,Standby,Economy,Protection"}, ## no critic (RegularExpressions::ProhibitComplexRegexes) + "dpt20.102" => {CODE=>"dpt20", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/(auto|comfort|standby|(economy|night)|(protection|frost|heat))/ix, MIN=>undef, MAX=>undef, SETLIST=>'Auto,Comfort,Standby,Economy,Protection'}, ## no critic (RegularExpressions::ProhibitComplexRegexes) # Color-Code - "dpt232" => {CODE=>"dpt232", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/[0-9a-f]{6}/ix, MIN=>undef, MAX=>undef, SETLIST=>"colorpicker"} + "dpt232" => {CODE=>"dpt232", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/[0-9a-f]{6}/ix, MIN=>undef, MAX=>undef, SETLIST=>'colorpicker'} ); #Init this device #This declares the interface to fhem ############################# -#04.65 sub KNX_Initialize { sub Initialize { my $hash = shift // return; @@ -321,10 +294,10 @@ sub Initialize { $hash->{GetFn} = \&KNX_Get; $hash->{StateFn} = \&KNX_State; $hash->{ParseFn} = \&KNX_Parse; - $hash->{NotifyFn} = \&KNX_Notify; +# $hash->{NotifyFn} = \&KNX_Notify; # not needed! $hash->{AttrFn} = \&KNX_Attr; $hash->{DbLog_splitFn} = \&KNX_DbLog_split; -#04.66 $hash->{FingerprintFn} = \&KNX_FingerPrint; +# $hash->{FingerprintFn} = \&KNX_FingerPrint; $hash->{AttrList} = "IODev " . #tells the module the IO-Device to communicate with. Optionally set within definition. "disable:1 " . #device disabled @@ -341,7 +314,7 @@ sub Initialize { "$readingFnAttributes "; #standard attributes $hash->{noAutocreatedFilelog} = 1; # autocreate devices create no FileLog $hash->{AutoCreate} = {"KNX_.*" => { ATTR => "disable:1"} }; # autocreate devices are disabled by default - return $UNDEF; + return undef; # really necessary? } #Define this device @@ -356,21 +329,26 @@ sub KNX_Define { my $name = $a[0]; $hash->{NAME} = $name; - $hash->{FVERSIONE} = $Eversion; ###MH Evolution version - $hash->{NOTIFYDEV} = "global,$name"; # limit notifies Log3 ($name, 5, "KNX_define -enter: $name, attributes: " . join (", ", @a)); #too less arguments - return 'KNX_define: $name -wrong syntax "define KNX [*] []" ' if (int(@a) < 3); + return 'KNX_define: $name -wrong syntax "define KNX [*]" ' if (int(@a) < 3); # check if the last arg matches any IO-Device - and assign it - else use the automatic mechanism - my @tulList = devspec2array('TYPE=(TUL|KNXTUL|KNXIO)',$hash); - foreach my $tuls (@tulList) { - if ($tuls eq $a[int(@a) - 1]) { - $attr{$name}{IODev} = pop(@a); - last; + if ( $a[int(@a) - 1] !~ m/^(?:$PAT_GAD|$PAT_GAD_HEX)/ix ) { + my $iodevCandidate = pop(@a); + my @tulList = devspec2array('TYPE=(TUL|KNXTUL|KNXIO)',$hash); + my $found = undef; + foreach my $tuls (@tulList) { + if ($tuls eq $iodevCandidate) { + $found = 1; + $attr{$name}{IODev} = $iodevCandidate; + Log3 ($name, 3, "KNX_define ($name): specifying IODev $iodevCandidate is deprecated in define - see cmd-ref"); + last; + } } + Log3 ($name, 3, "KNX_define ($name): invalid IODev $iodevCandidate specified - ignored") if (! defined($found)); } AssignIoPort($hash); # AssignIoPort will take device from $attr{$name}{IODev} if defined @@ -387,7 +365,7 @@ sub KNX_Define { my $gadOption = undef; my $gadNoSuffix = undef; - Log3 ($name, 5, "KNX_define: $name, argCtr $i, string: $a[$i]"); + Log3 ($name, 5, "KNX_define ($name):, argCtr $i, string: $a[$i]"); #G-nr my $gadNo = $i - 1; @@ -395,7 +373,7 @@ sub KNX_Define { my ($gad, $gadModel, @gadArgs) = split(/:/x, $a[$i]); $gadCode = $gad // return "GAD not defined for group-number $gadNo"; - return "KNX_define: $name -wrong GA format in group-number $gadNo: specify as 0-31/0-15/0-255 or as hex \nor invalid IO-Device specified" if (($gad !~ m/^$PAT_GAD$/ix) and ($gad !~ m/^$PAT_GAD_HEX$/ix)); + return "KNX_define ($name): -wrong GA format in group-number $gadNo: specify as 0-31/0-15/0-255 or as hex-notation" if ($gad !~ m/^(?:$PAT_GAD|$PAT_GAD_HEX)$/ix); #new syntax for extended adressing $gad = KNX_hexToName ($gad) if ($gad =~ m/^$PAT_GAD_HEX$/ix); @@ -404,34 +382,39 @@ sub KNX_Define { $gadCode = KNX_nameToHex ($gad); if(! defined($gadModel)) { - return "KNX_define: $name -no model defined for group-number $gadNo"; + return "KNX_define ($name): -no model defined for group-number $gadNo"; } else { #within autocreate no model is supplied - throw warning - if ($gadModel eq $modelErr) { - Log3 ($name, 3, "KNX_define: $name -autocreate device will be disabled, correct def with valid dpt and enable device") if ($init_done); + if ($gadModel eq $MODELERR) { + Log3 ($name, 3, "KNX_define ($name): -autocreate device will be disabled, correct def with valid dpt and enable device") if ($init_done); } elsif (!defined($dpttypes{$gadModel})) { #check model-type - return "KNX_define: $name -invalid model: $gadModel for group-number $gadNo. Please consult commanref - available DPT for correct model definition."; + return "KNX_define ($name): -invalid model: $gadModel for group-number $gadNo. Please consult commanref - available DPT for correct model definition."; } } + if ($gadModel ne $MODELERR && $gadNo == 1) { # for fheminfo statistic only + ($hash->{model} = lc($gadModel)) =~ s/^(dpt[\d]+)\..*/$1/x; # use first gad as mdl reference for fheminfo + # $hash->{model} = lc($gadModel) # this is too much! + } + if (@gadArgs) { - if ($gadArgs[0] =~ m/^$PAT_GAD_OPTIONS$/ix) { # no gadname given + if ($gadArgs[0] =~ m/^($PAT_GAD_OPTIONS)$/ix) { # no gadname given unshift ( @gadArgs , 'dummy' ); # shift option up in array } - elsif ($gadArgs[0] =~ m/^$PAT_GAD_NONAME.*/ix) { # check for forbidden names - return "KNX_define: $name -forbidden gad-name: $gadArgs[0]"; + elsif ($gadArgs[0] =~ m/^$PAT_GAD_NONAME$/ix) { # check for forbidden names forum #122582 + return "KNX_define ($name): -forbidden gad-name: $gadArgs[0]"; } else { $gadName = $gadArgs[0]; # new syntax } - $gadOption = $gadArgs[1] if(defined($gadArgs[1]) && $gadArgs[1] =~ m/$PAT_GAD_OPTIONS/ix); + $gadOption = $gadArgs[1] if(defined($gadArgs[1]) && $gadArgs[1] =~ m/($PAT_GAD_OPTIONS)/ix); $gadNoSuffix = 'noSuffix' if (join(q{ },@gadArgs) =~ m/nosuffix/ix); - return "KNX_define: $name -invalid option for group-number $gadNo. Use $PAT_GAD_OPTIONS" if (defined($gadOption) && ($gadOption !~ m/$PAT_GAD_OPTIONS/ix)); - return "KNX_define: $name -invalid suffix for group-number $gadNo. Use $PAT_GAD_SUFFIX" if (defined($gadNoSuffix) && ($gadNoSuffix !~ m/$PAT_GAD_SUFFIX/ix)); + return "KNX_define ($name): -invalid option for group-number $gadNo. Use one of: $PAT_GAD_OPTIONS" if (defined($gadOption) && ($gadOption !~ m/^($PAT_GAD_OPTIONS)$/ix)); + return "KNX_define ($name): -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 @@ -447,7 +430,7 @@ sub KNX_Define { } ###GADTABLE - return "KNX_define: $name -GAD $gad may be supplied only once per device." if (defined ($tableHashRef->{$gadCode})); + return "KNX_define ($name): -GAD $gad may be supplied only once per device." if (defined ($tableHashRef->{$gadCode})); #cache suffixes my $suffixGet = q{-get}; @@ -469,7 +452,7 @@ sub KNX_Define { $rdNamePut = "putG" . $gadNo; } - my $log = "KNX_define: $name -found GAD: $gad, NAME: $gadName NO: $gadNo, HEX: $gadCode, DPT: $gadModel"; + my $log = "KNX_define ($name): -found GAD: $gad, NAME: $gadName NO: $gadNo, HEX: $gadCode, DPT: $gadModel"; $log .= ", OPTION: $gadOption" if (defined ($gadOption)); Log3 ($name, 5, "$log"); @@ -499,8 +482,8 @@ sub KNX_Define { $setlist = q{}; } - Log3 ($name, 5, "define $name, Estimated reading-names: $rdNameGet, $rdNameSet, $rdNamePut"); - Log3 ($name, 5, "define $name, SetList: $setlist") if (defined ($setlist)); + Log3 ($name, 5, "KNX_define ($name): Estimated reading-names: $rdNameGet, $rdNameSet, $rdNamePut"); + Log3 ($name, 5, "KNX_define ($name): SetList: $setlist") if (defined ($setlist)); #add details to hash $hash->{GADDETAILS}{$gadName} = {GROUP => $gad, CODE => $gadCode, MODEL => $gadModel, NO => $gadNo, OPTION => $gadOption, RDNAMEGET => $rdNameGet, RDNAMESET => $rdNameSet, RDNAMEPUT => $rdNamePut, SETLIST => $setlist}; @@ -545,15 +528,15 @@ sub KNX_Define { $hash->{SETSTRING} = $setString; $hash->{GETSTRING} = $getString; - Log3 ($name, 5, "KNX_define: $name -GETSTR: " . $hash->{GETSTRING} . ", SETSTR: " . $hash->{SETSTRING}); + Log3 ($name, 5, "KNX_define ($name): -GETSTR= " . $hash->{GETSTRING} . ", SETSTR= " . $hash->{SETSTRING}); } #backup name for a later rename $hash->{DEVNAME} = $name; # wer braucht das? - Log3 ($name, 5, "KNX_define: $name -exit"); + Log3 ($name, 5, "KNX_define ($name): -exit"); - return $UNDEF; + return undef; # really necessary? } #Release this device @@ -569,7 +552,7 @@ sub KNX_Undef { KNX_delete_defptr($hash); # verify with: {PrintHash($modules{KNX}{defptr},3) } on FHEM-cmdline Log3 ($name, 5, "KNX_undef -exit"); - return $UNDEF; + return; } #Places a "read" Message on the KNX-Bus @@ -712,7 +695,7 @@ sub KNX_Set { KNX_SetReadings($hash, $targetGadName, $transval, $rdName, undef); Log3 ($name, 5, "$thisSub: -exit"); - return $UNDEF; + return; } # Process set command for old syntax @@ -744,8 +727,9 @@ sub KNX_Set_oldsyntax { return "KNX_Set_oldsyntax: gadName not found for $groupnr" if(!defined($targetGadName)); # all of the following cmd's need at least 1 Argument (or more) - return ($UNDEF, $targetGadName, $cmd) if (scalar(@a) <= 0); + return (undef, $targetGadName, $cmd) if (scalar(@a) <= 0); + my $code = $hash->{GADDETAILS}{$targetGadName}{MODEL}; my $value = "$cmd " . join(q{ },@a); # default if ($cmd =~ m/$RAW/ix) { # perlcritic (ControlStructures::ProhibitCascadingIfElse) ? @@ -754,7 +738,6 @@ sub KNX_Set_oldsyntax { $value = $a[0]; } elsif ($cmd =~ m/$VALUE/ix) { - my $code = $hash->{GADDETAILS}{$targetGadName}{MODEL}; return 'KNX_Set_oldsyntax: "value" not allowed for dpt1, dpt16 and dpt232' if ($code =~ m/(dpt1$)|(dpt16$)|(dpt232$)/ix); $value = $a[0]; @@ -762,21 +745,23 @@ sub KNX_Set_oldsyntax { } #set string elsif ($cmd =~ m/$STRING/ix) { - my $code = $hash->{GADDETAILS}{$targetGadName}{MODEL}; return 'KNX_Set_oldsyntax: "string" only allowed for dpt16' if ($code !~ m/dpt16/ix); - $value = q{}; # will be joined in KNX_Set + } #set RGB elsif ($cmd =~ m/$RGB/ix) { - my $code = $hash->{GADDETAILS}{$targetGadName}{MODEL}; return 'KNX_Set_oldsyntax: "RGB" only allowed for dpt232' if ($code !~ m/dpt232$/ix); #check for 6 hex-digits return "KNX_Set_oldsyntax: $cmd $a[0] has wrong syntax. Use 6 hex-digits only." if ($a[0] !~ m/[0-9A-F]{6}/ix); $value = lc($a[0]); } - return ($UNDEF, $targetGadName, $value); + elsif ($code =~ m/^dpt16/ix) { # Forum #122779 + $value = $cmd if ($cmd eq q{}); # rest will be joined in KNX_Set + } + + return (undef, $targetGadName, $value); } # process special dpt1, dpt1.001 set @@ -794,52 +779,39 @@ sub KNX_Set_dpt1 { delete $hash->{"TIMER_$groupCode"}; } - # the defaults my $value = 'off'; # default - if (($cmd eq 'on') || ($cmd eq '1')) {$value = 'on';} -# if (($cmd eq 'on') || ($cmd eq 'off')) {$value = $cmd;} -# elsif ($cmd eq '0') {$value = 'off';} -# elsif ($cmd eq '1') {$value = 'on';} + my $tvalue = 'on'; # default reversed value for timer ops + if ($cmd =~ m/(^on|1)/ix) { + $value = 'on'; + $tvalue = 'off'; + } #set on-for-timer / off-for-timer - elsif ($cmd =~ m/($ONFORTIMER)|($OFFFORTIMER)/ix) { + if ($cmd =~ m/(?:(on|off)-for-timer)$/ix) { #get duration my $duration = sprintf("%02d:%02d:%02d", $arg[0]/3600, ($arg[0]%3600)/60, $arg[0]%60); Log3 ($name, 5, "KNX_Set_dpt1 $name: \"on-for-timer\" for $duration"); - #create local marker - $hash->{"TIMER_$groupCode"} = $duration; - #place at-command for switching off - #switch on or off... - if ($cmd =~ m/on/ix) { - $value = "on"; - CommandDefine(undef, $name . "_TIMER_$groupCode at +$duration set $name $targetGadName off"); - } else { - $value = "off"; - CommandDefine(undef, $name . "_TIMER_$groupCode at +$duration set $name $targetGadName on"); - } + + $hash->{"TIMER_$groupCode"} = $duration; #create local marker + #place at-command for switching on / off + CommandDefMod(undef, '-temporary ' . $name . "_TIMER_$groupCode at +$duration set $name $targetGadName $tvalue"); } + #set on-until / off-until - elsif ($cmd =~ m/($ONUNTIL)|($OFFUNTIL)/ix) { + elsif ($cmd =~ m/(?:(on|off)-until)$/ix) { #get off-time my ($err, $hr, $min, $sec, $fn) = GetTimeSpec($arg[0]); ## fhem.pl return "KNX_Set_dpt1: Error trying to parse timespec for $arg[0]: $err" if (defined($err)); #do like (on|off)-until-overnight in at cmd ! my $hms_til = sprintf("%02d:%02d:%02d", $hr, $min, $sec); - Log3 ($name, 5, "KNX_Set_dpt1 $name: \"$cmd $hms_til\" "); - #create local marker - $hash->{"TIMER_$groupCode"} = $hms_til; - #place at-command for switching off - #switch on or off... - if ($cmd =~ m/on/ix) { - $value = "on"; - CommandDefine(undef, $name . "_TIMER_$groupCode at $hms_til set $name $targetGadName off"); - } else { - $value = "off"; - CommandDefine(undef, $name . "_TIMER_$groupCode at $hms_til set $name $targetGadName on"); - } + + $hash->{"TIMER_$groupCode"} = $hms_til; #create local marker + #place at-command for switching on / off + CommandDefMod(undef, '-temporary ' . $name . "_TIMER_$groupCode at $hms_til set $name $targetGadName $tvalue"); } + #toggle elsif ($cmd =~ m/$TOGGLE/ix) { my $togglereading = 'dummy'; @@ -858,18 +830,21 @@ sub KNX_Set_dpt1 { $toggleOldVal = ReadingsVal($tDev, $togglereading, 'dontknow'); } } - # if (ReadingsVal($name, $hash->{GADDETAILS}{$targetGadName}{RDNAMEGET}, 'on') =~ m/off/ix) { # SVN-Version Log3 ($name, 3, 'KNX_Set_dpt1: initial value for "set ' . "$name $targetGadName" . ' TOGGLE is not "on" or "off" - ' . "$targetGadName will be switched off") if ($toggleOldVal !~ /^(?:on|off)/ix); - if ($toggleOldVal =~ m/^off/ix) { - $value = "on"; - } - else { - $value = "off"; # this is the default - } + $value = q{on} if ($toggleOldVal =~ m/^off/ix); # value off is default } - return ($UNDEF,$value); +#04.68 ### setextensions trial... +# else { +# my ($ecmd,@earg) = split(/[\s]/ix,$cmd,2); +# my $cmdlist = $hash->{SETSTRING} . ' blink intervals'; +## my @earg = join(' ', @a[1 .. $na-1])) if (defined ($a[1])); +# Log3($name, 1, "Setext cmd=$ecmd, arg=" . join(',',@earg)); +# my $extret = SetExtensions($hash, $cmdlist , $name, $ecmd, @earg); # use SetExtensions +# Log3($name, 1, 'Setext returned: ' . $extret) if (defined($extret)); +# } + return (undef,$value); } #In case setstate is executed, a readingsupdate is initiated @@ -893,7 +868,7 @@ sub KNX_State { #write value and update reading readingsSingleUpdate($hash, $reading, $value, 1); - return $UNDEF; + return; } #Get the chance to qualify attributes @@ -917,18 +892,40 @@ sub KNX_Attr { return 'no valid device/reading value for attr: KNX_toggle' if (!defined($value) && $init_done); # maybe device/reading not defined during starrtup $hash->{'.TOGGLESRC'} = $srcDev . q{:} . $srcReading; # save for later processing } - if (($aName eq 'disable') && (defined($aVal)) && ($aVal == 1)) { - $hash->{"SETSTRING"} = q{}; # remove set & get options from UI - $hash->{"GETSTRING"} = q{}; + elsif (($aName eq 'disable') && (defined($aVal)) && ($aVal == 1)) { + $hash->{SETSTRING} = q{}; # remove set & get options from UI + $hash->{GETSTRING} = q{}; } - } + + # check valid IODev + elsif ($aName eq 'IODev' && $init_done) { + my @IOList = devspec2array('TYPE=(TUL|KNXTUL|KNXIO)',$hash); + foreach my $iodev (@IOList) { + return if ($iodev eq $aVal); # ok + } + return $aVal . ' is not a valid IO-Device for this Device'; + } + } # /set if ($cmd eq 'del') { - delete $hash->{'.TOGGLESRC'} if ($aName eq 'KNX_toggle'); - CommandModify(undef, "$name $hash->{'DEF'}") if ($aName eq 'disable'); # do a defmod ... - } + if ($aName eq 'KNX_toggle') { + delete $hash->{'.TOGGLESRC'}; +# CommandModify(undef, "$name $hash->{DEF}"); + } + elsif ($aName eq 'disable') { + my @defentries = split(/\s/ix,$hash->{DEF}); + foreach my $def (@defentries) { # check all entries + next if ($def eq ReadingsVal($name,'IODev',undef)); # deprecated IOdev + next if ($def =~ /:dpt\d+/ix); - return $UNDEF; + Log3 ($name, 2, 'Attribut "disable" cannot be deleted for device ' . $name . ' until you specify a valid dpt!'); + return 'Attribut "disable" cannot be deleted for device ' . $name . ' until you specify a valid dpt!'; + } +# CommandDefMod(undef, "-temporary $name KNX $hash->{DEF}"); # do a defmod ... + CommandModify(undef, "$name $hash->{DEF}"); # do a defmod ... + } + } + return; } #Split reading for DBLOG @@ -941,7 +938,7 @@ sub KNX_DbLog_split { #split input-string my @strings = split (q{ }, $event); - return $UNDEF if (not defined ($strings[0])); + return if (not defined ($strings[0])); #detect reading - real reading or state? if ($strings[0] =~ m/.*:$/x) { # real reading @@ -993,7 +990,7 @@ sub KNX_Parse { my $gad = KNX_hexToName($gadCode); #create name my $newDevName = sprintf("KNX_%.2d%.2d%.3d",split (/\//x, $gad)); - return "UNDEFINED $newDevName KNX $gad:$modelErr"; + return "UNDEFINED $newDevName KNX $gad:$MODELERR"; } #get list from device-hashes using given gadCode (==destination) @@ -1079,6 +1076,7 @@ sub KNX_Parse { } #Function is called at every notify +# not needed, will never be used ############################# sub KNX_Notify { my $ownHash = shift; @@ -1098,7 +1096,7 @@ sub KNX_Notify { } } } - return $UNDEF; + return; } # ignore duplicate messages (runs in TUL /KNXTUL context!) @@ -1195,9 +1193,7 @@ sub KNX_hexToName { my $p2 = hex(substr($v,2,1)); my $p3 = hex(substr($v,3,2)); - my $r = sprintf("%d/%d/%d", $p1,$p2,$p3); - - return $r; + return sprintf("%d/%d/%d", $p1,$p2,$p3); } # convert PHY from hex to readable version @@ -1230,7 +1226,7 @@ sub KNX_checkAndClean { my $model = $hash->{GADDETAILS}{$gadName}{MODEL}; #return unchecked, if this is a autocreate-device - return $value if ($model eq $modelErr); + return $value if ($model eq $MODELERR); my $pattern = $dpttypes{$model}{PATTERN}; @@ -1238,10 +1234,10 @@ sub KNX_checkAndClean { $value =~ s/^\s+|\s+$//gix; $value .= ':00' if ($model eq 'dpt10' && $value =~ /^[\d]{2}:[\d]{2}$/gix); # compatibility with widgetoverride :time - #match against model pattern +#new code: match against model pattern -to be tested!!! # my $pattern = qr/^($dpttypes{$model}{PATTERN})$/; # ($value) = ($value =~ m/$pattern/ix); -# return $UNDEF if (!defined($value)); +# return if (!defined($value)); my @tmp = ($value =~ m/$pattern/gix); #loop through results @@ -1255,11 +1251,11 @@ sub KNX_checkAndClean { } } - return $UNDEF if ($found == 0); + return if ($found == 0); $value = KNX_limit ($hash, $value, $gadName, undef); - Log3 ($name, 3, "KNX_checkAndClean: value= $orgValue was casted to $value") if ($orgValue ne $value); + Log3 ($name, 3, "KNX_checkAndClean: name= $name, value= $orgValue was casted to $value") if ($orgValue ne $value); # add dev-name Log3 ($name, 5, "KNX_checkAndClean -exit: value= $value, gadName= $gadName, model= $model, pattern= $pattern"); return $value; @@ -1292,7 +1288,7 @@ sub KNX_replaceByRegex { if (not defined ($regPair[1])) { #cut value - $tempVal = $UNDEF; + $tempVal = undef; } else { #replace value @@ -1330,7 +1326,6 @@ sub KNX_limit { #limitValue $retVal = $min if (defined ($min) and ($retVal < $min)); $retVal = $max if (defined ($max) and ($retVal > $max)); - #correct value $retVal /= $factor if (defined ($factor)); $retVal -= $offset if (defined ($offset)); @@ -1339,7 +1334,6 @@ sub KNX_limit { #correct value $retVal += $offset if (defined ($offset)); $retVal *= $factor if (defined ($factor)); - #limitValue $retVal = $min if (defined ($min) and ($retVal < $min)); $retVal = $max if (defined ($max) and ($retVal > $max)); @@ -1362,19 +1356,15 @@ sub KNX_eval { my $name = $hash->{NAME}; my $retVal = undef; -#04.65 Log3 ($name, 5, "KNX_Eval-enter: $name, gadName: $gadName, evalString: $evalString"); - my $code = EvalSpecials($evalString,("%hash" => $hash, '%name' => $name, '%gadName' => $gadName, '%state' => $state)); # prepare vars for AnalyzePerlCommand - $retVal = AnalyzePerlCommand(undef, $code); - + $retVal = AnalyzeCommandChain(undef, $code); +# $retVal = AnalyzePerlCommand(undef, $code); $retVal = "ERROR" if (not defined ($retVal)); if ($retVal =~ /(^Forbidden|error)/ix) { # eval error or forbidden by Authorize - Log3 ($name, 2, "KNX_Eval-error: device= $name, gadName= $gadName, evalString= $evalString, result= $retVal"); #04.65 -#04.65 Log3 ($name, 2, "KNX_Eval-error: $retVal"); + Log3 ($name, 2, "KNX_Eval-error: device= $name, gadName= $gadName, evalString= $evalString, result= $retVal"); $retVal = 'ERROR'; } -#04.65 Log3 ($name, 5, "KNX_Eval-exit: result: $retVal"); return $retVal; } @@ -1387,10 +1377,10 @@ sub KNX_encodeByDpt { my $code = $dpttypes{$model}{CODE}; #return unchecked, if this is a autocreate-device - return if ($model eq $modelErr); + return if ($model eq $MODELERR); #this one stores the translated value (readble) - my $numval = undef; + my $numval = 0; # default #this one stores the translated hex-value my $hexval = undef; @@ -1400,18 +1390,12 @@ sub KNX_encodeByDpt { #Binary value if ($code eq "dpt1") { - $numval = "00" if ($value eq q{0}); - $numval = "01" if ($value eq q{1}); - $numval = "00" if ($value =~ m/off$/ix); - $numval = "01" if ($value =~ m/on$/ix); - $numval = "00" if ($value eq $dpttypes{$model}{MIN}); - $numval = "01" if ($value eq $dpttypes{$model}{MAX}); + $numval = 1 if ($value =~ m/(1$|on$|$dpttypes{$model}{MAX})/ix); - $hexval = $numval; + $hexval = sprintf("%.2x",$numval); } #Step value (two-bit) elsif ($code eq "dpt2") { - $numval = 0; # default $numval = $value if ($value =~ m/^0?[0-3]$/ix); ## JoeALLb request $numval = 0 if ($value =~ m/off/ix); $numval = 1 if ($value =~ m/on/ix); @@ -1422,7 +1406,6 @@ sub KNX_encodeByDpt { } #Step value (four-bit) elsif ($code eq "dpt3") { - $numval = 0; my $sign = ($value >=0 )?1:0; $value = abs($value); @@ -1440,8 +1423,7 @@ sub KNX_encodeByDpt { } #1-Octet unsigned value elsif ($code eq "dpt5") { - $numval = $value; - $hexval = sprintf("00%.2x",($numval)); + $hexval = sprintf("00%.2x",$value); } #1-Octet signed value elsif ($code eq "dpt6") { @@ -1452,8 +1434,7 @@ sub KNX_encodeByDpt { } #2-Octet unsigned Value elsif ($code eq "dpt7") { - $numval = $value; - $hexval = sprintf("00%.4x",($numval)); + $hexval = sprintf("00%.4x",$value); } #2-Octet signed Value elsif ($code eq "dpt8") { @@ -1526,8 +1507,7 @@ sub KNX_encodeByDpt { } #4-Octet unsigned value elsif ($code eq "dpt12") { - $numval = $value; - $hexval = sprintf("00%.8x",($numval)); + $hexval = sprintf("00%.8x",$value); } #4-Octet Signed Value elsif ($code eq "dpt13") { @@ -1566,29 +1546,27 @@ sub KNX_encodeByDpt { $status1 += 1 if ($isdst == 1); my $status0 = 0x00; # CLQ=0 $mon++; - $numval = 0; $hexval = sprintf("00%02x%02x%02x%02x%02x%02x%02x%02x",$year,$mon,$mday,$hours,$mins,$secs,$status1,$status0); } # HVAC 1Byte elsif ($code eq "dpt20") { - $numval = "00" if ($value =~ m/Auto/ix); - $numval = "01" if ($value =~ m/Comfort/ix); - $numval = "02" if ($value =~ m/Standby/ix); - $numval = "03" if ($value =~ m/Economy/ix); - $numval = "04" if ($value =~ m/Protection/ix); - $hexval = sprintf("00%.2x",($numval)); + $numval = 0 if ($value =~ m/Auto/ix); + $numval = 1 if ($value =~ m/Comfort/ix); + $numval = 2 if ($value =~ m/Standby/ix); + $numval = 3 if ($value =~ m/Economy/ix); + $numval = 4 if ($value =~ m/Protection/ix); + $hexval = sprintf("00%.2x",$numval); } #RGB-Code elsif ($code eq "dpt232") { $hexval = "00" . $value; - $numval = $value; } else { Log3 ($name, 2, "KNX_encodeByDpt: $gadName, model: $model not valid"); - return $UNDEF; + return; } - Log3 ($name, 5, "KNX_encodeByDpt -exit: model: $model, code: $code, value: $value, numval: $numval, hexval: $hexval"); + Log3 ($name, 5, "KNX_encodeByDpt -exit: model: $model, code: $code, value: $value, hexval: $hexval"); return $hexval; } @@ -1602,42 +1580,35 @@ sub KNX_decodeByDpt { my $code = $dpttypes{$model}{CODE}; #return unchecked, if this is a autocreate-device - return if ($model eq $modelErr); + return if ($model eq $MODELERR); #this one stores the translated value (readble) my $numval = undef; #this one contains the return-value my $state = undef; - Log3 ($name, 5, "KNX_decodeByDpt -enter: model: $model, code: $code, value: $value"); - - my $min = $dpttypes{"$model"}{MIN}; - my $max = $dpttypes{"$model"}{MAX}; + Log3 ($name, 5, "KNX_decodeByDpt -enter: model: $model, code: $code, value: $value, length-value: " . length($value)); #Binary value if ($code eq "dpt1") { - $numval = $min; - $numval = $max if ($value =~ m/01/ix); - $state = $numval; + $numval = hex ($value); + $numval = ($numval & 0x01); + $state = $dpttypes{"$model"}{MIN}; # default + + $state = $dpttypes{"$model"}{MAX} if ($numval == 1); } #Step value (two-bit) elsif ($code eq "dpt2") { $numval = hex ($value); + $state = ($numval & 0x03); + my @dpt2txt = qw(off on forceOff forceOn); + $state = $dpt2txt[$state] if ($model ne 'dpt2.000') # JoeALLb request; - if ($model eq 'dpt2.000') { ## JoeALLb request - $state = $numval; - } - else { - $state = "off" if ($numval == 0); - $state = "on" if ($numval == 1); - $state = "forceOff" if ($numval == 2); - $state = "forceOn" if ($numval == 3); - } } #Step value (four-bit) elsif ($code eq "dpt3") { $numval = hex ($value); - + $numval = $numval & 0x0F; my $dir = ($numval & 0x08) >> 3; my $step = ($numval & 0x07); my $stepcode = 0; @@ -1645,7 +1616,7 @@ sub KNX_decodeByDpt { $stepcode = int(100 / (2**($step-1))); $stepcode *= -1 if ($dir == 0); } - $state = sprintf ("%.0f", $stepcode); + $state = sprintf ("%d", $stepcode); } #1-Octet unsigned value elsif ($code eq "dpt5") { @@ -1750,8 +1721,7 @@ sub KNX_decodeByDpt { $state = q{} if ($state =~ m/^[\x00]/ix); # case all zeros received - #remove non printable chars - $state =~ s/[\x00-\x1F]+//gx; + $state =~ s/[\x00-\x1F]+//gx; # remove non printable chars } #DateTime elsif ($code eq "dpt19") { @@ -1766,21 +1736,22 @@ sub KNX_decodeByDpt { my $year = ($date & 0xFF0000) >> 16; #extras my $wday = ($time & 0xE00000) >> 21; # 0 = anyday/not valid, 1= Monday,... - $year += 1900; + $state = sprintf("%02d.%02d.%04d_%02d:%02d:%02d", $day, $month, $year, $hours, $mins, $secs); } elsif ($code eq "dpt20") { $numval = hex ($value); - $state = "Auto" if ($numval >=0); - $state = "Comfort" if ($numval >=1); - $state = "Standby" if ($numval >=2); - $state = "Economy/Night" if ($numval >=3); - $state = "Protection/Frost/Heat" if ($numval >=4); + $numval = ($numval & 0xff); + my @dpt20_102txt = qw(Auto Comfort Standby Economy Protection reserved); + $numval = 5 if ($numval > 4); # dpt20.102 + + $state = $dpt20_102txt[$numval]; } #RGB-Code elsif ($code eq "dpt232") { $numval = hex ($value); + $state = sprintf ("%.6x",$numval); } else { @@ -1792,7 +1763,7 @@ sub KNX_decodeByDpt { my $unit = $dpttypes{$model}{UNIT}; $state = $state . q{ } . $unit if (defined ($unit) && ($unit ne q{})); - Log3 ($name, 5, "KNX_decodeByDpt -exit: model: $model, code: $code, value: $value, numval: $numval, state: $state"); + Log3 ($name, 5, "KNX_decodeByDpt -exit: model: $model, code: $code, value: $value, state: $state"); return $state; } @@ -1803,8 +1774,8 @@ sub KNX_decodeByDpt { =encoding utf8 =item [device] -=item summary Devices communicate via the IO-Device TUL or KNXTUL with KNX-bus -=item summary_DE Geräte kommunizieren über IO-Gerät TUL / mit dem KNX-Bus +=item summary Devices communicate via the IO-Device TUL/KNXTUL/KNXIO with KNX-bus +=item summary_DE Geräte kommunizieren über IO-Gerät TUL/KNXTUL/KNXIO mit KNX-Bus =begin html @@ -1823,16 +1794,18 @@ sub KNX_decodeByDpt { float: left; margin: 3px; padding: 3px; - width: 380px; - border: 3px solid gray; } + width: 40%; } .wrap li { white-space: pre; } .wrap.a { float: left; margin: 3px; padding: 3px; - width: 380px; - border: 3px solid gray; } + width: 40%; } + /* For mobile phones: */ + @media only screen and (max-width: 800px) { + .wrap {column-count:1; background-color: lightblue;} + } .pad20l {padding-left: 20px;} .pad30l {padding-left: 30px;} .pad40l {padding-left: 40px;} @@ -1841,10 +1814,11 @@ sub KNX_decodeByDpt {

KNX

    KNX is a standard for building automation / home automation. It is mainly based on a twisted pair wiring, but also other mediums (ip, wireless) are specified.

    -

    For getting started, please refer to this document: KNX-Basics

    -

    While the TUL-module or the KNXTUL-module represent the connection to the KNX network, the KNX modules represent individual KNX devices.
    +

    For getting started, please refer to this document: KNX for your home auf der knx.org WebSeite.

    +

    While the TUL-module, KNXTUL-module, or KNXIO-module represent the connection to the KNX network, + the KNX module represent a individual KNX device.
    This module provides a basic set of operations (on, off, toggle, on-until, on-for-timer) to switch on/off KNX devices and to send values to the bus. 

    -

    Sophisticated setups can be achieved by combining a number of KNX module instances. Therefore you can define a number of different GAD/DPT combinations per each device.

    +

    Sophisticated setups can be achieved by combining multiple KNX-groupaddresses:datapoints (GAD's:dpt's) in one KNX device instance.

    KNX defines a series of Datapoint Type as standard data types used to allow general interpretation of values of devices manufactured by different companies. These datatypes are used to interpret the status of a device, so the state in FHEM will then show the correct value.

    For each received telegram there will be a reading with containing the received value and the sender address.
    @@ -1853,7 +1827,7 @@ The reading <state> will be updated with the last sent or received value.&

    Define

    -

    define <name> KNX <group>:<DPT>[:[gadName]:[set|get|listenonly]:[nosuffix]] [<group>:<DPT> ..] [IODev]

    +

    define <name> KNX <group>:<dpt>[:[gadName]:[set|get|listenonly]:[nosuffix]] [<group>:<dpt> ..] [IODev]

    Important: a KNX device needs at least one concrete DPT. Please refer to available DPT. Otherwise the system cannot en- or decode the messages.
    Devices defined by autocreate have to be reworked with the suitable dpt and the disable attribute cleared. Otherwise they won't do anything.

    @@ -1861,11 +1835,11 @@ The reading <state> will be updated with the last sent or received value.& All of the defined groups can be used for bus-communication. It is not allowed to have the same group more then once in one device. You can have multiple devices containing the same adresses.
    As described above the parameter <DPT> must contain the corresponding DPT.
    -The optional parameteter [gadName] may contain an alias for the GAD. The gadName must not begin with one of the following strings: on, off, on-for-timer, - on-until, off-for-timer, off-until, toggle, raw, rgb, string, value, set, get, listenonly, nosuffix.
    +The optional parameteter [gadName] may contain an alias for the GAD. The gadName must not begin with one of the following strings: The following gadNames are not allowed: 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.
    Especially if attribute answerReading is set to 1, it might be useful to modifiy the behaviour of single GADs. If you want to restrict the GAD, you can raise the flags "get", "set", or "listenonly". The usage should be self-explanatory. It is not possible to combine the flags.
    -Furthermore you can supply a IO-Device directly at startup. This is only required in special cases. For details see: IODev Attribute.

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

    The GAD's are per default named with "g<number>". The corresponding reading-names are getG<number>, setG<number> and putG<number>.
    If you supply <gadName> this name is used instead. The readings are <gadName>-get, <gadName>-set and <gadName>-put. We will use the synonyms <getName>, <setName> and <putName> in this documentation. @@ -1886,7 +1860,7 @@ If you add the option "nosuffix", <getName>, <setName> and <putNa
    define lamp2 KNX 0/10/12:dpt1:steuern 0/10/13:dpt1.001:status

    -define lamp3 KNX 00A0D:dpt1.003 myTul
    +define lamp3 KNX 00A0D:dpt1.003
    @@ -1901,20 +1875,22 @@ If you add the option "nosuffix", <getName>, <setName> and <putNa After successful sending the value, it is stored in the readings <setName>.

    Examples:

    -set lamp2 on
    -set lamp2 off
    -set lamp2 on-for-timer 10
    -set lamp2 on-until 13:15:00
    +set lamp2 on # gadName omitted
    +set lamp2 off # gadName omitted
    +set lamp2 g1 on
    +set lamp2 g1 off
    +set lamp2 g1 on-for-timer 10
    +set lamp2 g1 on-until 13:15:00
    set lamp2 steuern on-until 13:15:00

    -set myThermoDev 23.44
    +set myThermoDev g1 23.44

    -set my MessageDev Hallo Welt
    +set myMessageDev g1 Hallo Welt # dpt16 def

    Get

    -

    If you execute "get" for a KNX-Element the status will be requested from the device. The device has to be able to respond to a read - this is not given for all devices.
    +

    If you execute "get" for a KNX-Element the status will be requested from the device. The device has to be able to respond to a read - this might not be supported by the target device.
    If the GAD is restricted in the definition with "set", the execution will be refused.
    The answer from the bus-device updates reading and state.

    @@ -1957,9 +1933,11 @@ The answer from the bus-device updates reading and state.

    If defined, the content of the reading <putName> is used as value for the answer.
  • stateRegex
    - You can pass n pairs of regex-pattern and string to replace, seperated by a slash. Internally the "new" state is always in the format <getName>:<state-value>. - The substitution is done every time, a new object is received. You can use this function for converting, adding units, having more fun with icons, ...
    - This function has only an impact on the content of state - no other functions are disturbed. It is executed directly after replacing the reading-names and setting the formats, but before stateCmd.
  • + You can pass n pairs of regex-patterns and strings to replace, seperated by a space. A regex-pair is always in the format /<readingName>[:<value>]/[2nd part]/. + The first part of the regex must exactly match the readingname, and optional the readingValue, separated by a colon. If first part match, the matching part will be replaced by the 2nd part of the regex. + If the 2nd part is empty, the value will be ignored and state-reading is not updated. + The substitution is done every time, a reading is updated. You can use this function for converting, adding units, having more fun with icons, ...
    + This function has only an impact on the content of reading state. It is executed directly after replacing the reading-names and setting the formats, but before stateCmd.
  • stateCmd
    You can supply a perl-command for modifying state. This command is executed directly before updating the reading - so after renaming, format and regex. @@ -1990,9 +1968,9 @@ The answer from the bus-device updates reading and state.

    If this attribute is not defined, the current value will be taken from owndevice:readingName-get or, if readingName-get is not defined, the value will be taken from readingName-set.

  • IODev
    - This attr will be set to the default (or only) TUL/KNXTUL device during device definition. Manually setting this attr is not required, except in cases where multiple IO-devices - (of type TUL/KNXTUL) are defined. Defining more than one IO-device is NOT recommended unless you take special care with yr. knxd or KNX-router definitions - - to prevent multiple path resulting in message loops.
  • + Due to changes in IO-Device handling, (default IO-Device will be stored in reading IODev), setting this Attribute is no longer required, + except in cases where multiple IO-devices (of type TUL/KNXTUL/KNXIO) are defined. Defining more than one IO-device is 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.