10_KNX.pm 75 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235
  1. ##############################################
  2. # $Id: 10_KNX.pm 15259 2017-10-14 18:48:05Z andi291 $
  3. # ABU 20160307 First release
  4. # ABU 20160309 Fixed issue for sending group-indexed with dpt1. Added debug-information. Fixed issue for indexed get. Fixed regex-replace-issue.
  5. # ABU 20160312 Fixed error while receiving numeric DPT with value 0. Added factor for dpt 08.010.
  6. # ABU 20160312 Fixed Regex-Attributes. Syntax changed from space-seperated to " /".
  7. # ABU 20160322 Fixed dpt1.008
  8. # ABU 20160326 Added fix for stateFormat
  9. # ABU 20160327 Removed readingRegex, writingRegex, created stateRegex, stateCmd, added reading-name support, fixed dblog-split
  10. # ABU 20160403 Fixed various minor perl warnings
  11. # ABU 20160413 Changed SplitFn
  12. # ABU 20160414 Changed SplitFn again
  13. # ABU 20160416 Changed SplitFn again
  14. # ABU 20160422 Added dpt9.021 - mA
  15. # ABU 20160529 Changed Doku
  16. # ABU 20160605 Changed Doku, changed autocreate-naming, fixed dpt10-sending-now
  17. # ABU 20160608 changed sprintf for int-dpt from %d to %.0f
  18. # ABU 20160624 corrected Doku: till->until
  19. # ABU 20161121 cleaned get/set options
  20. # ABU 20161122 fixed set-handling
  21. # ABU 20161126 added summary
  22. # ABU 20161126 fixed doku
  23. # ABU 20161127 adjusted dpt-16-sending, added dpt16.001
  24. # ABU 20161129 fixed get-mechanism
  25. # ABU 20170106 corrected doku for time, finetuned dpt9-regex, added dpt 7.001 7.012 9.007 9.008, , added mod for extended adressing (thx to its2bit)
  26. # ABU 20170110 removed mod for extended adressing
  27. # ABU 20100114 fixed dpt9-regex
  28. # ABU 20100116 fixed dpt9-regex again
  29. # ABU 20170427 reintegrated mechanism for extended adressing
  30. # ABU 20170427 integrated setExtensions
  31. # ABU 20170427 added dpt1.010 (start/stop)
  32. # ABU 20170427 added dpt2
  33. # ABU 20170503 corrected DPT1.010
  34. # ABU 20170503 changed regex for all dpt9
  35. # ABU 20170507 changed regex for all dpt9
  36. # ABU 20170517 added useSetExtensions
  37. # ABU 20170622 finetuned doku
  38. # ABU 20171006 added sub-dpt1
  39. # ABU 20171006 added dpt19
  40. package main;
  41. use strict;
  42. use warnings;
  43. use Encode;
  44. use SetExtensions;
  45. #set to 1 for debug
  46. my $debug = 0;
  47. #string constant for autocreate
  48. my $modelErr = "MODEL_NOT_DEFINED";
  49. my $OFF = "off";
  50. my $ON = "on";
  51. my $ONFORTIMER = "on-for-timer";
  52. my $ONUNTIL = "on-until";
  53. my $VALUE = "value";
  54. my $STRING = "string";
  55. my $RAW = "raw";
  56. my $RGB = "rgb";
  57. #valid set commands
  58. my %sets = (
  59. #"off" => "noArg",
  60. #"on" => "noArg",
  61. $OFF => "",
  62. $ON => "",
  63. $ONFORTIMER => "",
  64. $ONUNTIL => "",
  65. $VALUE => "",
  66. $STRING => "",
  67. $RAW => "",
  68. $RGB => "colorpicker"
  69. );
  70. #identifier for TUL
  71. my $id = 'C';
  72. #regex patterns
  73. my $PAT_GAD = qr/^[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{1,3}$/;
  74. #old syntax
  75. #my $PAT_GAD_HEX = qr/^[0-9a-f]{4}$/;
  76. #new syntax for extended adressing
  77. my $PAT_GAD_HEX = qr/^[0-9a-f]{5}$/;
  78. my $PAT_GNO = qr/[gG][1-9][0-9]?/;
  79. #CODE is the identifier for the en- and decode algos. See encode and decode functions
  80. #UNIT is appended to state for a better reading
  81. #FACTOR and OFFSET are used to normalize a value. value = FACTOR * (RAW - OFFSET). Must be undef for non-numeric values.
  82. #PATTERN is used to check an trim the input-values
  83. #MIN and MAX are used to cast numeric values. Must be undef for non-numeric dpt. Special Usecase: DPT1 - MIN represents 00, MAX represents 01
  84. my %dpttypes = (
  85. #Binary value
  86. "dpt1" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/([oO][nN])|([oO][fF][fF])|(0?1)|(0?0)/, MIN=>"off", MAX=>"on"},
  87. "dpt1.001" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/([oO][nN])|([oO][fF][fF])|(0?1)|(0?0)/, MIN=>"off", MAX=>"on"},
  88. "dpt1.002" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/([tT][rR][uU][eE])|([fF][aA][lL][sS][eE])|(0?1)|(0?0)/, MIN=>"false", MAX=>"true"},
  89. "dpt1.003" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/(([eE][nN]|[dD][iI][sS])[aA][bB][lL][eE])|(0?1)|(0?0)/, MIN=>"disable", MAX=>"enable"},
  90. "dpt1.004" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/(0?1)|(0?0)/, MIN=>"no ramp", MAX=>"ramp"},
  91. "dpt1.005" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/(0?1)|(0?0)/, MIN=>"no alarm", MAX=>"alarm"},
  92. "dpt1.006" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/(0?1)|(0?0)/, MIN=>"low", MAX=>"high"},
  93. "dpt1.007" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/(0?1)|(0?0)/, MIN=>"decrease", MAX=>"increase"},
  94. "dpt1.008" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/([uU][pP])|([dD][oO][wW][nN])|(0?1)|(0?0)/, MIN=>"up", MAX=>"down"},
  95. "dpt1.009" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/([cC][lL][oO][sS][eE][dD])|([oO][pP][eE][nN])|(0?1)|(0?0)/, MIN=>"open", MAX=>"closed"},
  96. "dpt1.010" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/([sS][tT][aA][rR][tT])|([sS][tT][oO][pP])|(0?1)|(0?0)/, MIN=>"stop", MAX=>"start"},
  97. "dpt1.011" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/(0?1)|(0?0)/, MIN=>"inactive", MAX=>"active"},
  98. "dpt1.012" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/(0?1)|(0?0)/, MIN=>"not inverted", MAX=>"inverted"},
  99. "dpt1.013" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/(0?1)|(0?0)/, MIN=>"start/stop", MAX=>"cyclically"},
  100. "dpt1.014" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/(0?1)|(0?0)/, MIN=>"fixed", MAX=>"calculated"},
  101. "dpt1.015" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/(0?1)|(0?0)/, MIN=>"no action", MAX=>"reset"},
  102. "dpt1.016" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/(0?1)|(0?0)/, MIN=>"no action", MAX=>"acknowledge"},
  103. "dpt1.017" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/(0?1)|(0?0)/, MIN=>"trigger", MAX=>"trigger"},
  104. "dpt1.018" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/(0?1)|(0?0)/, MIN=>"not occupied", MAX=>"occupied"},
  105. "dpt1.019" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/([cC][lL][oO][sS][eE][dD])|([oO][pP][eE][nN])|(0?1)|(0?0)/, MIN=>"closed", MAX=>"open"},
  106. "dpt1.021" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/(0?1)|(0?0)/, MIN=>"logical or", MAX=>"logical and"},
  107. "dpt1.022" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/(0?1)|(0?0)/, MIN=>"scene A", MAX=>"scene B"},
  108. "dpt1.023" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/(0?1)|(0?0)/, MIN=>"move up/down", MAX=>"move and step mode"},
  109. #Step value (two-bit)
  110. "dpt2" => {CODE=>"dpt2", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/([oO][nN])|([oO][fF][fF])|([fF][oO][rR][cC][eE][oO][nN])|([fF][oO][rR][cC][eE][oO][fF][fF])/, MIN=>undef, MAX=>undef},
  111. #Step value (four-bit)
  112. "dpt3" => {CODE=>"dpt3", UNIT=>"", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/, MIN=>-100, MAX=>100},
  113. # 1-Octet unsigned value
  114. "dpt5" => {CODE=>"dpt5", UNIT=>"", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/, MIN=>0, MAX=>255},
  115. "dpt5.001" => {CODE=>"dpt5", UNIT=>"%", FACTOR=>100/255, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/, MIN=>0, MAX=>100},
  116. "dpt5.003" => {CODE=>"dpt5", UNIT=>"°", FACTOR=>360/255, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/, MIN=>0, MAX=>360},
  117. "dpt5.004" => {CODE=>"dpt5", UNIT=>"%", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/, MIN=>0, MAX=>255},
  118. # 1-Octet signed value
  119. "dpt6" => {CODE=>"dpt6", UNIT=>"", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/, MIN=>-127, MAX=>127},
  120. "dpt6.001" => {CODE=>"dpt6", UNIT=>"%", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/, MIN=>0, MAX=>100},
  121. # 2-Octet unsigned Value
  122. "dpt7" => {CODE=>"dpt7", UNIT=>"", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/, MIN=>0, MAX=>65535},
  123. "dpt7.001" => {CODE=>"dpt7", UNIT=>"", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/, MIN=>0, MAX=>65535},
  124. "dpt7.005" => {CODE=>"dpt7", UNIT=>"s", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/, MIN=>0, MAX=>65535},
  125. "dpt7.006" => {CODE=>"dpt7", UNIT=>"m", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/, MIN=>0, MAX=>65535},
  126. "dpt7.012" => {CODE=>"dpt7", UNIT=>"mA", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/, MIN=>0, MAX=>65535},
  127. "dpt7.013" => {CODE=>"dpt7", UNIT=>"lux", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/, MIN=>0, MAX=>65535},
  128. # 2-Octet signed Value
  129. "dpt8" => {CODE=>"dpt8", UNIT=>"", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/, MIN=>-32768, MAX=>32768},
  130. "dpt8.005" => {CODE=>"dpt8", UNIT=>"s", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/, MIN=>-32768, MAX=>32768},
  131. "dpt8.010" => {CODE=>"dpt8", UNIT=>"%", FACTOR=>0.01, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/, MIN=>-32768, MAX=>32768},
  132. "dpt8.011" => {CODE=>"dpt8", UNIT=>"°", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/, MIN=>-32768, MAX=>32768},
  133. # 2-Octet Float value
  134. "dpt9" => {CODE=>"dpt9", UNIT=>"", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/, MIN=>-670760, MAX=>670760},
  135. "dpt9.001" => {CODE=>"dpt9", UNIT=>"°C", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/, MIN=>-670760, MAX=>670760},
  136. "dpt9.004" => {CODE=>"dpt9", UNIT=>"lux", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/, MIN=>-670760, MAX=>670760},
  137. "dpt9.006" => {CODE=>"dpt9", UNIT=>"Pa", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/, MIN=>-670760, MAX=>670760},
  138. "dpt9.005" => {CODE=>"dpt9", UNIT=>"m/s", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/, MIN=>-670760, MAX=>670760},
  139. "dpt9.007" => {CODE=>"dpt9", UNIT=>"%", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/, MIN=>-670760, MAX=>670760},
  140. "dpt9.008" => {CODE=>"dpt9", UNIT=>"ppm", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/, MIN=>-670760, MAX=>670760},
  141. "dpt9.009" => {CODE=>"dpt9", UNIT=>"m&sup3/h", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/, MIN=>-670760, MAX=>670760},
  142. "dpt9.010" => {CODE=>"dpt9", UNIT=>"s", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/, MIN=>-670760, MAX=>670760},
  143. "dpt9.021" => {CODE=>"dpt9", UNIT=>"mA", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/, MIN=>-670760, MAX=>670760},
  144. "dpt9.024" => {CODE=>"dpt9", UNIT=>"kW", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/, MIN=>-670760, MAX=>670760},
  145. "dpt9.025" => {CODE=>"dpt9", UNIT=>"l/h", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/, MIN=>-670760, MAX=>670760},
  146. "dpt9.026" => {CODE=>"dpt9", UNIT=>"l/h", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/, MIN=>-670760, MAX=>670760},
  147. "dpt9.028" => {CODE=>"dpt9", UNIT=>"km/h", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/, MIN=>-670760, MAX=>670760},
  148. # Time of Day
  149. "dpt10" => {CODE=>"dpt10", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((2[0-4]|[0?1][0-9]):(60|[0?1-5]?[0-9]):(60|[0?1-5]?[0-9]))|([nN][oO][wW])/, MIN=>undef, MAX=>undef},
  150. # Date
  151. "dpt11" => {CODE=>"dpt11", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((3[01]|[0-2]?[0-9]).(1[0-2]|0?[0-9]).(19[0-9][0-9]|2[01][0-9][0-9]))|([nN][oO][wW])/, MIN=>undef, MAX=>undef},
  152. # 4-Octet unsigned value (handled as dpt7)
  153. "dpt12" => {CODE=>"dpt12", UNIT=>"", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,10}/, MIN=>0, MAX=>4294967295},
  154. # 4-Octet Signed Value
  155. "dpt13" => {CODE=>"dpt13", UNIT=>"", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,10}/, MIN=>-2147483647, MAX=>2147483647},
  156. "dpt13.010" => {CODE=>"dpt13", UNIT=>"Wh", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,10}/, MIN=>-2147483647, MAX=>2147483647},
  157. "dpt13.013" => {CODE=>"dpt13", UNIT=>"kWh", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,10}/, MIN=>-2147483647, MAX=>2147483647},
  158. # 4-Octet single precision float
  159. "dpt14" => {CODE=>"dpt14", UNIT=>"", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/, MIN=>undef, MAX=>undef},
  160. "dpt14.019" => {CODE=>"dpt14", UNIT=>"A", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/, MIN=>undef, MAX=>undef},
  161. "dpt14.027" => {CODE=>"dpt14", UNIT=>"V", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/, MIN=>undef, MAX=>undef},
  162. "dpt14.056" => {CODE=>"dpt14", UNIT=>"W", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/, MIN=>undef, MAX=>undef},
  163. "dpt14.068" => {CODE=>"dpt14", UNIT=>"°C", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/, MIN=>undef, MAX=>undef},
  164. "dpt14.076" => {CODE=>"dpt14", UNIT=>"m³", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/, MIN=>undef, MAX=>undef},
  165. # 14-Octet String
  166. "dpt16" => {CODE=>"dpt16", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/.{1,14}/, MIN=>undef, MAX=>undef},
  167. "dpt16.000" => {CODE=>"dpt16", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/.{1,14}/, MIN=>undef, MAX=>undef},
  168. "dpt16.001" => {CODE=>"dpt16", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/.{1,14}/, MIN=>undef, MAX=>undef},
  169. "dpt19" => {CODE=>"dpt19", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/(((3[01]|[0-2]?[0-9]).(1[0-2]|0?[0-9]).(19[0-9][0-9]|2[01][0-9][0-9]))_((2[0-4]|[0?1][0-9]):(60|[0?1-5]?[0-9]):(60|[0?1-5]?[0-9])))|([nN][oO][wW])/, MIN=>undef, MAX=>undef},
  170. # Color-Code
  171. "dpt232" => {CODE=>"dpt232", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/[0-9A-Fa-f]{6}/, MIN=>undef, MAX=>undef},
  172. );
  173. #Init this device
  174. #This declares the interface to fhem
  175. #############################
  176. sub
  177. KNX_Initialize($) {
  178. my ($hash) = @_;
  179. $hash->{Match} = "^$id.*";
  180. $hash->{GetFn} = "KNX_Get";
  181. $hash->{SetFn} = "KNX_Set";
  182. $hash->{StateFn} = "KNX_State";
  183. $hash->{DefFn} = "KNX_Define";
  184. $hash->{UndefFn} = "KNX_Undef";
  185. $hash->{ParseFn} = "KNX_Parse";
  186. $hash->{AttrFn} = "KNX_Attr";
  187. $hash->{NotifyFn} = "KNX_Notify";
  188. $hash->{DbLog_splitFn} = "KNX_DbLog_split";
  189. $hash->{AttrList} = "IODev " . #tells the module the IO-Device to communicate with. Optionally set within definition.
  190. "do_not_notify:1,0 " . #supress any notification (including log)
  191. "listenonly:1,0 " . #device may not send any messages. answering is prohibited. get is prohibited.
  192. "readonly:1,0 " . #device may not send any messages. answering is prohibited. get is allowed.
  193. "showtime:1,0 " . #shows time instead of received value in state
  194. "answerReading:1,0 " . #allows FHEM to answer a read telegram
  195. "stateRegex " . #modifies state value
  196. "stateCmd " . #modify state value
  197. "stateCopy " . #backup content of state in this reading (only for received telegrams)
  198. "format " . #supplies post-string
  199. "slider " . #creates slider. Syntax: min, step, max
  200. "$readingFnAttributes "; #standard attributes
  201. }
  202. #Define this device
  203. #Is called at every define
  204. #############################
  205. sub
  206. KNX_Define($$) {
  207. my ($hash, $def) = @_;
  208. my @a = split("[ \t][ \t]*", $def);
  209. #device name
  210. my $name = $a[0];
  211. #set verbose to 5, if debug enabled
  212. $attr{$name}{verbose} = 5 if ($debug eq 1);
  213. my $tempStr = join (", ", @a);
  214. Log3 ($name, 5, "define $name: enter $hash, attributes: $tempStr");
  215. #too less arguments
  216. return "wrong syntax - define <name> KNX <group:model[:reading-name]> [<group:model[:reading-name]>*] [<IODev>]" if (int(@a) < 3);
  217. #check for IODev
  218. #is last argument a group or a group:model pair?
  219. my $lastGroupDef = int(@a);
  220. #if (($a[int(@a) - 1] !~ m/^[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{1,3}$/i) and ($a[int(@a) - 1] !~ m/^[0-9a-f]{4}$/i) and ($a[int(@a) - 1] !~ m/[0-9a-fA-F]:[dD][pP][tT]/i))
  221. if (($a[int(@a) - 1] !~ m/${PAT_GAD}/i) and ($a[int(@a) - 1] !~ m/${PAT_GAD_HEX}/i) and ($a[int(@a) - 1] !~ m/[0-9a-fA-F]:[dD][pP][tT]/i))
  222. {
  223. $attr{$name}{IODev} = $a[int(@a) - 1];
  224. $lastGroupDef--;
  225. }
  226. #create groups and models, iterate through all possible args
  227. for (my $i = 2; $i < $lastGroupDef; $i++)
  228. {
  229. #backup actual GAD
  230. my $inp = lc($a[$i]);
  231. my ($group, $model, $rdname) = split /:/, $inp;
  232. my $groupc;
  233. #G-nr
  234. my $gno = $i - 1;
  235. #GAD not defined
  236. return "GAD not defined for group-number $gno" if (!defined($group));
  237. #GAD wrong syntax
  238. #either 1/2/3 or 1203
  239. #return "wrong group name format: specify as 0-15/0-15/0-255 or as hex" if (($group !~ m/^[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{1,3}$/i) && (lc($group) !~ m/^[0-9a-f]{4}$/i));
  240. return "wrong group name format: specify as 0-15/0-15/0-255 or as hex" if (($group !~ m/${PAT_GAD}/i) && (lc($group) !~ m/${PAT_GAD_HEX}/i));
  241. #check if model supplied
  242. return "no model defined" if (!defined($model));
  243. #within autocreate no model is supplied - throw warning
  244. if ($model eq $modelErr)
  245. {
  246. Log3 ($name, 2, "define $name: autocreate defines no model - only restricted functions are available");
  247. }
  248. else
  249. {
  250. #check model-type
  251. return "invalid model. Use " .join(",", keys %dpttypes) if (!defined($dpttypes{$model}));
  252. }
  253. #convert to string, if supplied in Hex
  254. #old syntax
  255. #$group = KNX_hexToName ($group) if ($group =~ m/^[0-9a-f]{4}$/i);
  256. #new syntax for extended adressing
  257. $group = KNX_hexToName ($group) if ($group =~ m/^[0-9a-f]{5}$/i);
  258. $groupc = KNX_nameToHex ($group);
  259. Log3 ($name, 5, "define $name: found GAD: $group, NO: $gno, HEX: $groupc, DPT: $model");
  260. Log3 ($name, 5, "define $name: found Readings-Name: $rdname") if (defined ($rdname));
  261. #add indexed group to hash. Index starts with one
  262. #readable GAD
  263. $hash->{GADDR}{$gno} = $group;
  264. #same as hex
  265. $hash->{GCODE}{$gno} = $groupc;
  266. #model
  267. $hash->{MODEL}{$gno} = $model;
  268. #backup readings-name
  269. $hash->{READINGSNAME}{$gno} = $rdname if (defined ($rdname) and !($rdname eq ""));
  270. }
  271. #common name
  272. $hash->{NAME} = $name;
  273. #backup name for a later rename
  274. $hash->{DEVNAME} = $name;
  275. #finally create decice
  276. #defptr is needed to supress FHEM input
  277. $modules{KNX}{defptr}{$name} = $hash;
  278. #assign io-dev automatically, if not given via definition
  279. AssignIoPort($hash);
  280. Log3 ($name, 5, "exit define");
  281. CommandDefine(undef, 'findMe');
  282. CommandDefine('myClass', 'findMe2');
  283. return undef;
  284. }
  285. #Release this device
  286. #Is called at every delete / shutdown
  287. #############################
  288. sub
  289. KNX_Undef($$) {
  290. my ($hash, $name) = @_;
  291. Log3 ($name, 5, "enter undef $name: hash: $hash name: $name");
  292. #remove all groups
  293. foreach my $group (keys %{$hash->{GCODE}})
  294. {
  295. Log3 ($name, 5, "undef $name: remove name: $hash->{NAME}, orig.-Name: $hash->{DEVNAME}, GAD: $group");
  296. delete $hash->{GADDR}{$group};
  297. delete $hash->{GCODE}{$group};
  298. delete $hash->{MODEL}{$group};
  299. }
  300. #remove module. Refer to DevName, because module may be renamed
  301. delete $modules{KNX}{defptr}{$hash->{DEVNAME}};
  302. #remove name
  303. delete $hash->{NAME};
  304. #remove backuped name
  305. delete $hash->{DEVNAME};
  306. Log3 ($name, 5, "exit undef");
  307. return undef;
  308. }
  309. #Places a "read" Message on the KNX-Bus
  310. #The answer is treated as regular telegram
  311. #############################
  312. sub
  313. KNX_Get($@) {
  314. my ($hash, @a) = @_;
  315. my $name = $hash->{NAME};
  316. my $groupnr = 1;
  317. my $tempStr = join (", ", @a);
  318. Log3 ($name, 5, "enter get $name: hash: $hash, attributes: $tempStr");
  319. #FHEM asks with a ? at startup - no action, no log
  320. #do not use KNX_getCmdList because argument will be a group-adress
  321. return "Unknown argument ?, choose one of -" if(defined($a[1]) and ($a[1] =~ m/\?/));
  322. splice(@a, 1, 1) if (defined ($a[1]) and ($a[1] =~ m/-/));
  323. my $na = int(@a);
  324. #not more then 2 arguments allowed
  325. return "too much arguments. Only one argument allowed (group-address)." if($na>2);
  326. # the command can be send to any of the defined groups indexed starting by 1
  327. # optional last argument starting with g indicates the group
  328. if(defined ($a[1]))
  329. {
  330. #check syntax
  331. if ($a[1]=~ m/${PAT_GNO}/)
  332. {
  333. #assign group-no
  334. $groupnr = $a[1];
  335. $groupnr =~ s/^g//;
  336. } else
  337. {
  338. return "$a[1] is invalid. Second argument only may be a group g<no>";
  339. }
  340. }
  341. #get group from hash (hex)
  342. my $groupc = $hash->{GCODE}{$groupnr};
  343. #get group from hash
  344. my $group = $hash->{GADDR}{$groupnr};
  345. #return, if unknown group
  346. return "groupnr: $groupnr not known" if(!$groupc);
  347. #exit, if reat is prohibited
  348. return "did not request a value - \"listenonly\" is set." if (AttrVal ($name, "listenonly", 0) =~ m/1/);
  349. #send read-request to the bus
  350. Log3 ($name, 5, "get $name: request value for GAD $group");
  351. IOWrite($hash, $id, "r" . $groupc);
  352. Log3 ($name, 5, "exit get");
  353. return "current value for $name ($group) requested";
  354. }
  355. #Does something according the given cmd...
  356. #############################
  357. sub
  358. KNX_Set($@) {
  359. my ($hash, @a) = @_;
  360. my $name = $hash->{NAME};
  361. my $ret = "";
  362. my $na = int(@a);
  363. my $tempStr = join (", ", @a);
  364. #log only, if not called with cmd = ?
  365. Log3 ($name, 5, "enter set $name: hash: $hash, attributes: $tempStr") if ((defined ($a[1])) and (not ($a[1] eq "?")));
  366. #return, if no set value specified
  367. return "no set value specified" if($na < 2);
  368. #return, if this is a readonly-device
  369. return "this device is readonly" if(defined($hash->{readonly}));
  370. #backup values
  371. my $cmd = lc($a[1]);
  372. #remove whitespaces
  373. $cmd =~ s/^\s+|\s+$//g;
  374. #get slider definition
  375. my $slider = AttrVal ($name, "slider", undef);
  376. #hash has to be copied. Otherwise silder-operation affects all devices
  377. my %mySets = %sets;
  378. #append slider-definition, if set...Necessary for FHEM
  379. $mySets{$VALUE} = $mySets{$VALUE} . "slider,$slider" if ((defined $slider) and !($mySets{$VALUE} =~ m/slider/));
  380. #create response, if cmd is wrong or gui asks
  381. my $cmdTemp = KNX_getCmdList ($hash, $cmd, %mySets);
  382. #return "Unknown argument $cmd, choose one of " . $cmdTemp if (defined ($cmdTemp));
  383. return SetExtensions($hash, $cmdTemp, $name, $cmd, @a) if (defined ($cmdTemp));
  384. #the command can be send to any of the defined groups indexed starting by 1
  385. #optional last argument starting with g indicates the group
  386. #default
  387. my $groupnr = 1;
  388. my $lastArg = $na - 1;
  389. #select another group, if the last arg starts with a g
  390. if($na > 2 && $a[$lastArg]=~ m/${PAT_GNO}/)
  391. {
  392. $groupnr = $a[$lastArg];
  393. #remove "g"
  394. $groupnr =~ s/^g//g;
  395. $lastArg--;
  396. }
  397. #unknown groupnr
  398. return "group-no. not found" if(!defined($groupnr));
  399. #group
  400. my $group = $hash->{GADDR}{$groupnr};
  401. my $groupc = $hash->{GCODE}{$groupnr};
  402. #unknown groupnr
  403. return "group-no. $groupnr not known" if(!defined($group));
  404. #get model
  405. my $model = $hash->{MODEL}{$groupnr};
  406. my $code = $dpttypes{$model}{CODE};
  407. Log3 ($name, 5, "set $name: model: $model, GAD: $group, GAD hex: $groupc, gno: $groupnr");
  408. #This contains the input
  409. my $value = "";
  410. #delete any running timers
  411. if ($hash->{"ON-FOR-TIMER_G$groupnr"})
  412. {
  413. CommandDelete(undef, $name . "_timer_$groupnr");
  414. delete $hash->{"ON-FOR-TIMER_G$groupnr"};
  415. }
  416. if($hash->{"ON-UNTIL_G$groupnr"})
  417. {
  418. CommandDelete(undef, $name . "_until_$groupnr");
  419. delete $hash->{"ON-UNTIL_G$groupnr"};
  420. }
  421. #set on-for-timer
  422. if ($cmd =~ m/$ONFORTIMER/)
  423. {
  424. return "\"on-for-timer\" only allowed for dpt1" if (not($code eq "dpt1"));
  425. #get duration
  426. my $duration = sprintf("%02d:%02d:%02d", $a[2]/3600, ($a[2]%3600)/60, $a[2]%60);
  427. #$modules{KNX}{"on-for-timer"}{$name} = $duration;
  428. $hash->{"ON-FOR-TIMER_G$groupnr"} = $duration;
  429. Log3 ($name, 5, "set $name: \"on-for-timer\" for $duration");
  430. #set to on
  431. $value = 1;
  432. #place at-command for switching off
  433. CommandDefine(undef, $name . "_timer_$groupnr at +$duration set $name off g$groupnr");
  434. }
  435. #set on-till
  436. elsif ($cmd =~ m/$ONUNTIL/)
  437. {
  438. return "\"on\" only allowed for dpt1" if (not($code eq "dpt1"));
  439. #get off-time
  440. my ($err, $hr, $min, $sec, $fn) = GetTimeSpec($a[2]);
  441. return "Error trying to parse timespec for $a[2]: $err" if (defined($err));
  442. #build of-time
  443. my @lt = localtime;
  444. my $hms_til = sprintf("%02d:%02d:%02d", $hr, $min, $sec);
  445. my $hms_now = sprintf("%02d:%02d:%02d", $lt[2], $lt[1], $lt[0]);
  446. return "Won't switch - now ($hms_now) is later than $hms_til" if($hms_now ge $hms_til);
  447. #$modules{KNX}{"on-until"}{$name} = $hms_til;
  448. $hash->{"ON-UNTIL_G$groupnr"} = $hms_til;
  449. Log3 ($name, 5, "set $name: \"on-until\" up to $hms_til");
  450. #set to on
  451. $value = 1;
  452. #place at-command for switching off
  453. CommandDefine(undef, $name . "_until_$groupnr at $hms_til set $name off g$groupnr");
  454. }
  455. #set on
  456. elsif ($cmd =~ m/$ON/)
  457. {
  458. return "\"on\" only allowed for dpt1" if (not($code eq "dpt1"));
  459. $value = 1;
  460. }
  461. #set off
  462. elsif ($cmd =~ m/$OFF/)
  463. {
  464. return "\"off\" only allowed for dpt1" if (not($code eq "dpt1"));
  465. $value = 0;
  466. }
  467. #set raw <value>
  468. elsif ($cmd =~ m/$RAW/)
  469. {
  470. return "no data for cmd $cmd" if ($lastArg < 2);
  471. #check for 1-16 hex-digits
  472. if ($a[2] =~ m/[0-9a-fA-F]{1,16}/)
  473. {
  474. $value = lc($a[2]);
  475. } else
  476. {
  477. return "$a[2] has wrong syntax. Use hex-format only.";
  478. }
  479. }
  480. #set value <value>
  481. elsif ($cmd =~ m/$VALUE/)
  482. {
  483. return "\"value\" not allowed for dpt1, dpt16 and dpt232" if (($code eq "dpt1") or ($code eq "dpt16") or ($code eq "dpt232"));
  484. #return "\"value\" not allowed for dpt1 and dpt16" if ($code eq "dpt16");
  485. return "no data for cmd $cmd" if ($lastArg < 2);
  486. $value = $a[2];
  487. #replace , with .
  488. $value =~ s/,/\./g;
  489. }
  490. #set string <val1 val2 valn>
  491. elsif ($cmd =~ m/$STRING/)
  492. {
  493. return "\"string\" only allowed for dpt16" if (not($code eq "dpt16"));
  494. return "no data for cmd $cmd" if ($lastArg < 2);
  495. #join string
  496. for (my $i=2; $i<=$lastArg; $i++)
  497. {
  498. $value.= $a[$i]." ";
  499. }
  500. }
  501. #set RGB <RRGGBB>
  502. elsif ($cmd =~ m/$RGB/)
  503. {
  504. return "\"RGB\" only allowed for dpt232" if (not($code eq "dpt232"));
  505. return "no data for cmd $cmd" if ($lastArg < 2);
  506. #check for 1-16 hex-digits
  507. if ($a[2] =~ m/[0-9A-Fa-f]{6}/)
  508. {
  509. $value = lc($a[2]);
  510. } else
  511. {
  512. return "$a[2] has wrong syntax. Use hex-format only.";
  513. }
  514. }
  515. #check and cast value
  516. my $transval = KNX_checkAndClean($hash, $value, $groupnr);
  517. return "invalid value: $value" if (!defined($transval));
  518. #exit, if sending is prohibited
  519. return "did not send value - \"listenonly\" is set." if (AttrVal ($name, "listenonly", 0) =~ m/1/);
  520. return "did not send value - \"readonly\" is set." if (AttrVal ($name, "readonly", 0) =~ m/1/);
  521. #send value
  522. $transval = KNX_encodeByDpt($hash, $transval, $groupnr);
  523. IOWrite($hash, $id, "w" . $groupc . $transval);
  524. Log3 ($name, 5, "set $name: cmd: $cmd, value: $value, translated: $transval");
  525. #build readingsName
  526. my $rdName = $hash->{READINGSNAME}{$groupnr};
  527. if (defined ($rdName) and !($rdName eq ""))
  528. {
  529. Log3 ($name, 5, "set name: $name, replaced \"getG\" with custom readingName \"$rdName\"");
  530. $rdName = $rdName . "-set";
  531. }
  532. else
  533. {
  534. $rdName = "setG" . $groupnr;
  535. }
  536. #re-read value, do not modify variable name due to usage in cmdAttr
  537. $transval = KNX_decodeByDpt($hash, $transval, $groupnr);
  538. #append post-string, if supplied
  539. my $suffix = AttrVal($name, "format",undef);
  540. $transval = $transval . " " . $suffix if (defined($suffix));
  541. #execute regex, if defined
  542. my $regAttr = AttrVal($name, "stateRegex", undef);
  543. my $state = KNX_replaceByRegex ($regAttr, $rdName . ":", $transval);
  544. Log3 ($name, 5, "set name: $name - replaced $rdName:$transval to $state") if (not ($transval eq $state));
  545. if (defined($state))
  546. {
  547. readingsBeginUpdate($hash);
  548. readingsBulkUpdate($hash, $rdName, $transval);
  549. #execute state-command if defined
  550. #must be placed after first reading, because it may have a reference
  551. my $cmdAttr = AttrVal($name, "stateCmd", undef);
  552. if (defined ($cmdAttr) and !($cmdAttr eq ""))
  553. {
  554. $state = eval $cmdAttr;
  555. Log3 ($name, 5, "set name: $name - state replaced via command, result: state:$state");
  556. }
  557. readingsBulkUpdate($hash, "state", $state);
  558. readingsEndUpdate($hash, 1);
  559. }
  560. Log3 ($name, 5, "exit set");
  561. return undef;
  562. }
  563. #In case setstate is executed, a readingsupdate is initiated
  564. #############################
  565. sub
  566. KNX_State($$$$) {
  567. my ($hash, $time, $reading, $value) = @_;
  568. my $name = $hash->{NAME};
  569. my $tempStr = join (", ", @_);
  570. Log3 ($name, 5, "enter state: hash: $hash name: $name, attributes: $tempStr");
  571. #in some cases state is submitted within value - if found, take only the stuff after state
  572. #my @strings = split("[sS][tT][aA][tT][eE]", $val);
  573. #$val = $strings[int(@strings) - 1];
  574. return undef if (not (defined($value)));
  575. return undef if (not (defined($reading)));
  576. #remove whitespaces
  577. $value =~ s/^\s+|\s+$//g;
  578. $reading =~ s/^\s+|\s+$//g;
  579. $reading = lc ($reading) if ($reading =~ m/[sS][tT][aA][tT][eE]/);
  580. Log3 ($name, 5, "state $name: update $reading with value: $value");
  581. #write value and update reading
  582. readingsSingleUpdate($hash, $reading, $value, 1);
  583. return undef;
  584. }
  585. #Get the chance to qualify attributes
  586. #############################
  587. sub
  588. KNX_Attr(@) {
  589. my ($cmd,$name,$aName,$aVal) = @_;
  590. return undef;
  591. }
  592. #Split reading for DBLOG
  593. #############################
  594. sub KNX_DbLog_split($) {
  595. my ($event) = @_;
  596. my ($reading, $value, $unit);
  597. my $tempStr = join (", ", @_);
  598. Log (5, "splitFn - enter, attributes: $tempStr");
  599. #detect reading - real reading or state?
  600. my $isReading = "false";
  601. $isReading = "true" if ($event =~ m/: /);
  602. #split input-string
  603. my @strings = split (" ", $event);
  604. my $startIndex = undef;
  605. $unit = "";
  606. return undef if (not defined ($strings[0]));
  607. #real reading?
  608. if ($isReading =~ m/true/)
  609. {
  610. #first one is always reading
  611. $reading = $strings[0];
  612. $reading =~ s/:?$//;
  613. $startIndex = 1;
  614. }
  615. #plain state
  616. else
  617. {
  618. #for reading state nothing is supplied
  619. $reading = "state";
  620. $startIndex = 0;
  621. }
  622. return undef if (not defined ($strings[$startIndex]));
  623. #per default join all single pieces
  624. $value = join(" ", @strings[$startIndex..(int(@strings) - 1)]);
  625. #numeric value?
  626. #if ($strings[$startIndex] =~ /^[+-]?\d*[.,]?\d+/)
  627. if ($strings[$startIndex] =~ /^[+-]?\d*[.,]?\d+$/)
  628. {
  629. $value = $strings[$startIndex];
  630. #single numeric value? Assume second par is unit...
  631. if ((defined ($strings[$startIndex + 1])) && !($strings[$startIndex+1] =~ /^[+-]?\d*[.,]?\d+/))
  632. {
  633. $unit = $strings[$startIndex + 1] if (defined ($strings[$startIndex + 1]));
  634. }
  635. }
  636. #numeric value?
  637. #if ($strings[$startIndex] =~ /^[+-]?\d*[.,]?\d+/)
  638. #{
  639. # $value = $strings[$startIndex];
  640. # $unit = $strings[$startIndex + 1] if (defined ($strings[$startIndex + 1]));
  641. #}
  642. #string or raw
  643. #else
  644. #{
  645. # $value = join(" ", @strings[$startIndex..(int(@strings) - 1)]);
  646. #}
  647. Log (5, "splitFn - READING: $reading, VALUE: $value, UNIT: $unit");
  648. return ($reading, $value, $unit);
  649. }
  650. #Handle incoming messages
  651. #############################
  652. sub
  653. KNX_Parse($$) {
  654. my ($hash, $msg) = @_;
  655. #Msg format:
  656. #C(w/r/p)<group><value> i.e. Bw00000101
  657. #we will also take reply telegrams into account,
  658. #as they will be sent if the status is asked from bus
  659. #split message into parts
  660. #old syntax
  661. #$msg =~ m/^$id(.{4})(.{1})(.{4})(.*)$/;
  662. #new syntax for extended adressing
  663. $msg =~ m/^$id(.{5})(.{1})(.{5})(.*)$/;
  664. my $src = $1;
  665. my $cmd = $2;
  666. my $dest = $3;
  667. my $val = $4;
  668. my @foundMsgs;
  669. Log3 ($hash->{NAME}, 5, "enter parse: hash: $hash name: $hash->{NAME}, msg: $msg");
  670. #check if the code is within the read groups
  671. foreach my $deviceName (keys %{$modules{KNX}{defptr}})
  672. {
  673. #fetch device
  674. my $deviceHash = $modules{KNX}{defptr}{$deviceName};
  675. #skip, if name not defined
  676. next if (!defined($deviceHash));
  677. #loop through all defined group-numbers
  678. foreach my $gno (keys %{$deviceHash->{GCODE}})
  679. {
  680. #fetch groupcode
  681. my $groupc = $deviceHash->{GCODE}{$gno};
  682. #GAD in message is matching GAD in device
  683. if (defined ($groupc) and ($groupc eq $dest))
  684. {
  685. #get details
  686. my $name = $deviceHash->{NAME};
  687. my $groupAddr = $deviceHash->{GADDR}{$gno};
  688. my $model = $deviceHash->{MODEL}{$gno};
  689. Log3 ($name, 5, "parse device hash: $deviceHash name: $name, GADDR: $groupAddr, GCODE: $groupc, MODEL: $model");
  690. #handle write and reply messages
  691. if ($cmd =~ /[w|p]/)
  692. {
  693. #decode message
  694. my $transval = KNX_decodeByDpt ($deviceHash, $val, $gno);
  695. #message invalid
  696. if (not defined($transval) or ($transval eq ""))
  697. {
  698. Log3 ($name, 2, "parse device hash: $deviceHash name: $name, message could not be decoded - see log for details");
  699. next;
  700. }
  701. Log3 ($name, 5, "received hash: $deviceHash name: $name, STATE: $transval, GNO: $gno, SENDER: $src");
  702. #build readingsName
  703. my $rdName = $deviceHash->{READINGSNAME}{$gno};
  704. if (defined ($rdName) and !($rdName eq ""))
  705. {
  706. Log3 ($name, 5, "parse device hash: $deviceHash name: $name, replaced \"getG\" with custom readingName \"$rdName\"");
  707. $rdName = $rdName . "-get";
  708. }
  709. else
  710. {
  711. $rdName = "getG" . $gno;
  712. }
  713. #append post-string, if supplied
  714. my $suffix = AttrVal($name, "format",undef);
  715. $transval = $transval . " " . $suffix if (defined($suffix));
  716. #execute regex, if defined
  717. my $regAttr = AttrVal($name, "stateRegex", undef);
  718. my $state = KNX_replaceByRegex ($regAttr, $rdName . ":", $transval);
  719. Log3 ($name, 5, "parse device hash: $deviceHash name: $name - replaced $rdName:$transval to $state") if (not ($transval eq $state));
  720. if (defined($state))
  721. {
  722. readingsBeginUpdate($deviceHash);
  723. readingsBulkUpdate($deviceHash, $rdName, $transval);
  724. readingsBulkUpdate($deviceHash, "last-sender", KNX_hexToName($src));
  725. #execute state-command if defined
  726. #must be placed after first readings, because it may have a reference
  727. my $cmdAttr = AttrVal($name, "stateCmd", undef);
  728. if (defined ($cmdAttr) and !($cmdAttr eq ""))
  729. {
  730. $state = eval $cmdAttr;
  731. Log3 ($name, 5, "parse device hash: $deviceHash name: $name - state replaced via command - result: state:$state");
  732. }
  733. readingsBulkUpdate($deviceHash, "state", $state);
  734. readingsEndUpdate($deviceHash, 1);
  735. }
  736. }
  737. #handle read messages, if Attribute is set
  738. elsif (($cmd =~ /[r]/) && (AttrVal($name, "answerReading",0) =~ m/1/))
  739. {
  740. Log3 ($name, 5, "received hash: $deviceHash name: $name, GET");
  741. my $transval = KNX_encodeByDpt($deviceHash, $deviceHash->{STATE}, $gno);
  742. if (defined($transval))
  743. {
  744. Log3 ($name, 5, "received hash: $deviceHash name: $name, GET: $transval, GNO: $gno");
  745. IOWrite ($deviceHash, "B", "p" . $groupc . $transval);
  746. }
  747. }
  748. #skip, if this is ignored
  749. next if (IsIgnored($name));
  750. #save to list
  751. push(@foundMsgs, $name);
  752. }
  753. }
  754. }
  755. Log3 ($hash->{NAME}, 5, "exit parse");
  756. #return values
  757. if (int(@foundMsgs))
  758. {
  759. return @foundMsgs;
  760. } else
  761. {
  762. my $gad = KNX_hexToName($dest);
  763. #remove slashes
  764. #$name =~ s/\///g;
  765. #my $name = "KNX_" . $gad;
  766. my ($line, $area, $device) = split ("/", $gad);
  767. my $name = sprintf("KNX_%.2d%.2d%.3d", $line, $area, $device);
  768. my $ret = "KNX Unknown device $dest ($gad), Value $val, please define it";
  769. Log3 ($name, 3, "KNX Unknown device $dest ($gad), Value $val, please define it");
  770. #needed for autocreate
  771. return "UNDEFINED $name KNX $gad:$modelErr";
  772. }
  773. }
  774. #Function is called at every notify
  775. #############################
  776. sub
  777. KNX_Notify($$)
  778. {
  779. my ($ownHash, $callHash) = @_;
  780. #own name / hash
  781. my $ownName = $ownHash->{NAME};
  782. #Device that created the events
  783. my $callName = $callHash->{NAME};
  784. return undef;
  785. }
  786. #Private function to convert GAD from hex to readable version
  787. #############################
  788. sub
  789. KNX_hexToName ($)
  790. {
  791. my $v = shift;
  792. #old syntax
  793. #my $p1 = hex(substr($v,0,1));
  794. #my $p2 = hex(substr($v,1,1));
  795. #my $p3 = hex(substr($v,2,2));
  796. #new syntax for extended adressing
  797. my $p1 = hex(substr($v,0,2));
  798. my $p2 = hex(substr($v,2,1));
  799. my $p3 = hex(substr($v,3,2));
  800. my $r = sprintf("%d/%d/%d", $p1,$p2,$p3);
  801. return $r;
  802. }
  803. #Private function to convert GAD from readable version to hex
  804. #############################
  805. sub
  806. KNX_nameToHex ($)
  807. {
  808. my $v = shift;
  809. my $r = $v;
  810. if($v =~ /^([0-9]{1,2})\/([0-9]{1,2})\/([0-9]{1,3})$/)
  811. {
  812. #old syntax
  813. #$r = sprintf("%01x%01x%02x",$1,$2,$3);
  814. #new syntax for extended adressing
  815. $r = sprintf("%02x%01x%02x",$1,$2,$3);
  816. }
  817. #elsif($v =~ /^([0-9]{1,2})\.([0-9]{1,2})\.([0-9]{1,3})$/)
  818. #{
  819. # $r = sprintf("%01x%01x%02x",$1,$2,$3);
  820. #}
  821. return $r;
  822. }
  823. #Private function to clean input string according DPT
  824. #############################
  825. sub
  826. KNX_checkAndClean ($$$)
  827. {
  828. my ($hash, $value, $gno) = @_;
  829. my $name = $hash->{NAME};
  830. my $orgValue = $value;
  831. Log3 ($name, 5, "check value: $value, gno: $gno");
  832. #get model
  833. my $model = $hash->{MODEL}{$gno};
  834. #return unchecked, if this is a autocreate-device
  835. return $value if ($model eq $modelErr);
  836. #get pattern
  837. my $pattern = $dpttypes{$model}{PATTERN};
  838. #trim whitespaces at the end
  839. $value =~ s/^\s+|\s+$//g;
  840. #match against model pattern
  841. my @tmp = ($value =~ m/$pattern/g);
  842. #loop through results
  843. my $found = 0;
  844. foreach my $str (@tmp)
  845. {
  846. #assign first match and exit loop
  847. if (defined($str))
  848. {
  849. $found = 1;
  850. $value = $str;
  851. last;
  852. }
  853. }
  854. return undef if ($found == 0);
  855. #get min
  856. my $min = $dpttypes{"$model"}{MIN};
  857. #if min is numeric, cast to min
  858. $value = $min if (defined ($min) and ($min =~ /^[+-]?\d*[.,]?\d+/) and ($value < $min));
  859. #get max
  860. my $max = $dpttypes{"$model"}{MAX};
  861. #if max is numeric, cast to max
  862. $value = $max if (defined ($max) and ($max =~ /^[+-]?\d*[.,]?\d+/) and ($value > $max));
  863. Log3 ($name, 3, "check value: input-value $orgValue was casted to $value") if (not($orgValue eq $value));
  864. Log3 ($name, 5, "check value: $value, gno: $gno, model: $model, pattern: $pattern");
  865. return $value;
  866. }
  867. #Private function to encode KNX-Message according DPT
  868. #############################
  869. sub
  870. KNX_encodeByDpt ($$$) {
  871. my ($hash, $value, $gno) = @_;
  872. my $name = $hash->{NAME};
  873. Log3 ($name, 5, "encode value: $value, gno: $gno");
  874. #get model
  875. my $model = $hash->{MODEL}{$gno};
  876. my $code = $dpttypes{$model}{CODE};
  877. #return unchecked, if this is a autocreate-device
  878. return $value if ($model eq $modelErr);
  879. #this one stores the translated value (readble)
  880. my $numval = undef;
  881. #this one stores the translated hex-value
  882. my $hexval = undef;
  883. Log3 ($name, 5, "encode model: $model, code: $code, value: $value");
  884. #get correction details
  885. my $factor = $dpttypes{$model}{FACTOR};
  886. my $offset = $dpttypes{$model}{OFFSET};
  887. #correct value
  888. $value /= $factor if (defined ($factor));
  889. $value -= $offset if (defined ($offset));
  890. Log3 ($name, 5, "encode normalized value: $value");
  891. #Binary value
  892. if ($code eq "dpt1")
  893. {
  894. $numval = "00" if ($value eq 0);
  895. $numval = "01" if ($value eq 1);
  896. $numval = "00" if ($value eq $dpttypes{$model}{MIN});
  897. $numval = "01" if ($value eq $dpttypes{$model}{MAX});
  898. $hexval = $numval;
  899. }
  900. #Step value (two-bit)
  901. elsif ($code eq "dpt2")
  902. {
  903. $numval = "00" if ($value =~ m/[oO][fF][fF]/);
  904. $numval = "01" if ($value =~ m/[oO][nN]/);
  905. $numval = "02" if ($value =~ m/[fF][oO][rR][cC][eE][oO][fF][fF]/);
  906. $numval = "03" if ($value =~ m/[fF][oO][rR][cC][eE][oO][nN]/);
  907. $hexval = $numval;
  908. }
  909. #Step value (four-bit)
  910. elsif ($code eq "dpt3")
  911. {
  912. $numval = 0;
  913. #get dim-direction
  914. my $sign = 0;
  915. $sign = 1 if ($value >= 0);
  916. #trim sign
  917. $value =~ s/^-//g;
  918. #get dim-value
  919. $numval = 7 if ($value >= 1);
  920. $numval = 6 if ($value >= 3);
  921. $numval = 5 if ($value >= 6);
  922. $numval = 4 if ($value >= 12);
  923. $numval = 3 if ($value >= 25);
  924. $numval = 2 if ($value >= 50);
  925. $numval = 1 if ($value >= 75);
  926. #assign dim direction
  927. $numval += 8 if ($sign == 1);
  928. #get hex representation
  929. $hexval = sprintf("%.2x",$numval);
  930. }
  931. #1-Octet unsigned value
  932. elsif ($code eq "dpt5")
  933. {
  934. $numval = $value;
  935. $hexval = sprintf("00%.2x",($numval));
  936. }
  937. #1-Octet signed value
  938. elsif ($code eq "dpt6")
  939. {
  940. #build 2-complement
  941. $numval = $value;
  942. $numval += 0x100 if ($numval < 0);
  943. $numval = 0x00 if ($numval < 0x00);
  944. $numval = 0xFF if ($numval > 0xFF);
  945. #get hex representation
  946. $hexval = sprintf("00%.2x",$numval);
  947. }
  948. #2-Octet unsigned Value
  949. elsif ($code eq "dpt7")
  950. {
  951. $numval = $value;
  952. $hexval = sprintf("00%.4x",($numval));
  953. }
  954. #2-Octet signed Value
  955. elsif ($code eq "dpt8")
  956. {
  957. #build 2-complement
  958. $numval = $value;
  959. $numval += 0x10000 if ($numval < 0);
  960. $numval = 0x00 if ($numval < 0x00);
  961. $numval = 0xFFFF if ($numval > 0xFFFF);
  962. #get hex representation
  963. $hexval = sprintf("00%.4x",$numval);
  964. }
  965. #2-Octet Float value
  966. elsif ($code eq "dpt9")
  967. {
  968. my $sign = ($value <0 ? 0x8000 : 0);
  969. my $exp = 0;
  970. my $mant = 0;
  971. $mant = int($value * 100.0);
  972. while (abs($mant) > 0x7FF)
  973. {
  974. $mant /= 2;
  975. $exp++;
  976. }
  977. $numval = $sign | ($exp << 11) | ($mant & 0x07ff);
  978. #get hex representation
  979. $hexval = sprintf("00%.4x",$numval);
  980. }
  981. #Time of Day
  982. elsif ($code eq "dpt10")
  983. {
  984. if (lc($value) eq "now")
  985. {
  986. #get actual time
  987. my ($secs,$mins,$hours,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  988. my $hoffset;
  989. #add offsets
  990. $year+=1900;
  991. $mon++;
  992. # calculate offset for weekday
  993. $wday = 7 if ($wday eq "0");
  994. $hoffset = 32*$wday;
  995. $hours += $hoffset;
  996. $value = "$hours:$mins:$secs";
  997. $numval = $secs + ($mins<<8) + ($hours<<16);
  998. } else
  999. {
  1000. my ($hh, $mm, $ss) = split (":", $value);
  1001. $numval = $ss + ($mm<<8) + (($hh)<<16);
  1002. }
  1003. #get hex representation
  1004. $hexval = sprintf("00%.6x",$numval);
  1005. }
  1006. #Date
  1007. elsif ($code eq "dpt11")
  1008. {
  1009. if (lc($value) eq "now")
  1010. {
  1011. #get actual time
  1012. my ($secs,$mins,$hours,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  1013. my $hoffset;
  1014. #add offsets
  1015. $year+=1900;
  1016. $mon++;
  1017. # calculate offset for weekday
  1018. $wday = 7 if ($wday eq "0");
  1019. $value = "$mday.$mon.$year";
  1020. $numval = ($year - 2000) + ($mon<<8) + ($mday<<16);
  1021. } else
  1022. {
  1023. my ($dd, $mm, $yyyy) = split (/\./, $value);
  1024. if ($yyyy >= 2000)
  1025. {
  1026. $yyyy -= 2000;
  1027. } else
  1028. {
  1029. $yyyy -= 1900;
  1030. }
  1031. $numval = ($yyyy) + ($mm<<8) + ($dd<<16);
  1032. }
  1033. #get hex representation
  1034. $hexval = sprintf("00%.6x",$numval);
  1035. }
  1036. #4-Octet unsigned value (handled as dpt7)
  1037. elsif ($code eq "dpt12")
  1038. {
  1039. $numval = $value;
  1040. $hexval = sprintf("00%.8x",($numval));
  1041. }
  1042. #4-Octet Signed Value
  1043. elsif ($code eq "dpt13")
  1044. {
  1045. #build 2-complement
  1046. $numval = $value;
  1047. $numval += 4294967296 if ($numval < 0);
  1048. $numval = 0x00 if ($numval < 0x00);
  1049. $numval = 0xFFFFFFFF if ($numval > 0xFFFFFFFF);
  1050. #get hex representation
  1051. $hexval = sprintf("00%.8x",$numval);
  1052. }
  1053. #4-Octet single precision float
  1054. elsif ($code eq "dpt14")
  1055. {
  1056. $numval = unpack("L", pack("f", $value));
  1057. #get hex representation
  1058. $hexval = sprintf("00%.8x",$numval);
  1059. }
  1060. #14-Octet String
  1061. elsif ($code eq "dpt16")
  1062. {
  1063. #convert to latin-1
  1064. $value = encode("iso-8859-1", decode("utf8", $value));
  1065. #convert to hex-string
  1066. my $dat = unpack "H*", $value;
  1067. #format for 14-byte-length
  1068. $dat = sprintf("%-028s",$dat);
  1069. #append leading zeros
  1070. $dat = "00" . $dat;
  1071. $numval = $value;
  1072. $hexval = $dat;
  1073. }
  1074. #DateTime
  1075. elsif ($code eq "dpt19")
  1076. {
  1077. if (lc($value) eq "now")
  1078. {
  1079. #get actual time
  1080. my ($secs,$mins,$hours,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  1081. my $hoffset;
  1082. #add offsets
  1083. $mon++;
  1084. # calculate offset for weekday
  1085. $wday = 7 if ($wday eq "0");
  1086. $hexval = 0;
  1087. $hexval = sprintf ("00%.8x", (($secs<<16) + ($mins<<24) + ($hours<<32) + ($wday<<37) + ($mday<<40) + ($mon<<48) + ($year<<56)));
  1088. } else
  1089. {
  1090. my ($date, $time) = split ('_', $value);
  1091. my ($dd, $mm, $yyyy) = split (/\./, $date);
  1092. my ($hh, $mi, $ss) = split (':', $time);
  1093. #add offsets
  1094. $yyyy -= 1900; # year is based on 1900
  1095. my $wday = 0;
  1096. $hexval = 0;
  1097. $hexval = sprintf ("00%.8x", (($ss<<16) + ($mi<<24) + ($hh<<32) + ($wday<<37) + ($dd<<40) + ($mm<<48) + ($yyyy<<56)));
  1098. }
  1099. $numval = 0;
  1100. }
  1101. #RGB-Code
  1102. elsif ($code eq "dpt232")
  1103. {
  1104. $hexval = "00" . $value;
  1105. $numval = $value;
  1106. }
  1107. else
  1108. {
  1109. Log3 ($name, 2, "encode model: $model, no vaild model defined");
  1110. return undef;
  1111. }
  1112. Log3 ($name, 5, "encode model: $model, code: $code, value: $value, numval: $numval, hexval: $hexval");
  1113. return $hexval;
  1114. }
  1115. #Private function to replace state-values
  1116. #############################
  1117. sub
  1118. KNX_replaceByRegex ($$$) {
  1119. my ($regAttr, $prefix, $input) = @_;
  1120. my $retVal = $input;
  1121. #execute regex, if defined
  1122. if (defined($regAttr))
  1123. {
  1124. #get array of given attributes
  1125. my @reg = split(" /", $regAttr);
  1126. my $tempVal = $prefix . $input;
  1127. #loop over all regex
  1128. foreach my $regex (@reg)
  1129. {
  1130. #trim leading and trailing slashes
  1131. $regex =~ s/^\/|\/$//g;
  1132. #get pairs
  1133. my @regPair = split("\/", $regex);
  1134. #skip if not at least 2 values supplied
  1135. #next if (int(@regPair < 2));
  1136. next if (not defined($regPair[0]));
  1137. if (not defined ($regPair[1]))
  1138. {
  1139. #cut value
  1140. $tempVal =~ s/$regPair[0]//g;
  1141. }
  1142. else
  1143. {
  1144. #replace value
  1145. $tempVal =~ s/$regPair[0]/$regPair[1]/g;
  1146. }
  1147. #restore value
  1148. $retVal = $tempVal;
  1149. }
  1150. }
  1151. return $retVal;
  1152. }
  1153. #Private function to decode KNX-Message according DPT
  1154. #############################
  1155. sub
  1156. KNX_decodeByDpt ($$$) {
  1157. my ($hash, $value, $gno) = @_;
  1158. my $name = $hash->{NAME};
  1159. Log3 ($name, 5, "decode value: $value, gno: $gno");
  1160. #get model
  1161. my $model = $hash->{MODEL}{$gno};
  1162. my $code = $dpttypes{$model}{CODE};
  1163. #return unchecked, if this is a autocreate-device
  1164. return $value if ($model eq $modelErr);
  1165. #this one stores the translated value (readble)
  1166. my $numval = undef;
  1167. #this one contains the return-value
  1168. my $state = undef;
  1169. Log3 ($name, 5, "decode model: $model, code: $code, value: $value");
  1170. #get correction details
  1171. my $factor = $dpttypes{$model}{FACTOR};
  1172. my $offset = $dpttypes{$model}{OFFSET};
  1173. #Binary value
  1174. if ($code eq "dpt1")
  1175. {
  1176. my $min = $dpttypes{"$model"}{MIN};
  1177. my $max = $dpttypes{"$model"}{MAX};
  1178. $numval = $min if (lc($value) eq "00");
  1179. $numval = $max if (lc($value) eq "01");
  1180. $state = $numval;
  1181. }
  1182. #Step value (two-bit)
  1183. elsif ($code eq "dpt2")
  1184. {
  1185. #get numeric value
  1186. $numval = hex ($value);
  1187. $state = "off" if ($numval == 0);
  1188. $state = "on" if ($numval == 1);
  1189. $state = "forceOff" if ($numval == 2);
  1190. $state = "forceOn" if ($numval == 3);
  1191. }
  1192. #Step value (four-bit)
  1193. elsif ($code eq "dpt3")
  1194. {
  1195. #get numeric value
  1196. $numval = hex ($value);
  1197. $state = 1 if ($numval & 7);
  1198. $state = 3 if ($numval & 6);
  1199. $state = 6 if ($numval & 5);
  1200. $state = 12 if ($numval & 4);
  1201. $state = 25 if ($numval & 3);
  1202. $state = 50 if ($numval & 2);
  1203. $state = 100 if ($numval & 1);
  1204. #get dim-direction
  1205. $state = 0 - $state if (not ($numval & 8));
  1206. #correct value
  1207. $state -= $offset if (defined ($offset));
  1208. $state *= $factor if (defined ($factor));
  1209. $state = sprintf ("%.0f", $state);
  1210. }
  1211. #1-Octet unsigned value
  1212. elsif ($code eq "dpt5")
  1213. {
  1214. $numval = hex ($value);
  1215. $state = $numval;
  1216. #correct value
  1217. $state -= $offset if (defined ($offset));
  1218. $state *= $factor if (defined ($factor));
  1219. $state = sprintf ("%.0f", $state);
  1220. }
  1221. #1-Octet signed value
  1222. elsif ($code eq "dpt6")
  1223. {
  1224. $numval = hex ($value);
  1225. $numval -= 0x100 if ($numval >= 0x80);
  1226. $state = $numval;
  1227. #correct value
  1228. $state -= $offset if (defined ($offset));
  1229. $state *= $factor if (defined ($factor));
  1230. $state = sprintf ("%.0f", $state);
  1231. }
  1232. #2-Octet unsigned Value
  1233. elsif ($code eq "dpt7")
  1234. {
  1235. $numval = hex ($value);
  1236. $state = $numval;
  1237. #correct value
  1238. $state -= $offset if (defined ($offset));
  1239. $state *= $factor if (defined ($factor));
  1240. $state = sprintf ("%.0f", $state);
  1241. }
  1242. #2-Octet signed Value
  1243. elsif ($code eq "dpt8")
  1244. {
  1245. $numval = hex ($value);
  1246. $numval -= 0x10000 if ($numval >= 0x8000);
  1247. $state = $numval;
  1248. #correct value
  1249. $state -= $offset if (defined ($offset));
  1250. $state *= $factor if (defined ($factor));
  1251. $state = sprintf ("%.0f", $state);
  1252. }
  1253. #2-Octet Float value
  1254. elsif ($code eq "dpt9")
  1255. {
  1256. $numval = hex($value);
  1257. my $sign = 1;
  1258. $sign = -1 if(($numval & 0x8000) > 0);
  1259. my $exp = ($numval & 0x7800) >> 11;
  1260. my $mant = ($numval & 0x07FF);
  1261. $mant = -(~($mant-1) & 0x07FF) if($sign == -1);
  1262. $numval = (1 << $exp) * 0.01 * $mant;
  1263. #correct value
  1264. $state -= $offset if (defined ($offset));
  1265. $state *= $factor if (defined ($factor));
  1266. $state = sprintf ("%.2f","$numval");
  1267. }
  1268. #Time of Day
  1269. elsif ($code eq "dpt10")
  1270. {
  1271. $numval = hex($value);
  1272. my $hours = ($numval & 0x1F0000)>>16;
  1273. my $mins = ($numval & 0x3F00)>>8;
  1274. my $secs = ($numval & 0x3F);
  1275. $state = sprintf("%02d:%02d:%02d",$hours,$mins,$secs);
  1276. }
  1277. #Date
  1278. elsif ($code eq "dpt11")
  1279. {
  1280. $numval = hex($value);
  1281. my $day = ($numval & 0x1F0000) >> 16;
  1282. my $month = ($numval & 0x0F00) >> 8;
  1283. my $year = ($numval & 0x7F);
  1284. #translate year (21st cent if <90 / else 20th century)
  1285. $year += 1900 if($year >= 90);
  1286. $year += 2000 if($year < 90);
  1287. $state = sprintf("%02d.%02d.%04d",$day,$month,$year);
  1288. }
  1289. #4-Octet unsigned value (handled as dpt7)
  1290. elsif ($code eq "dpt12")
  1291. {
  1292. $numval = hex ($value);
  1293. $state = $numval;
  1294. #correct value
  1295. $state -= $offset if (defined ($offset));
  1296. $state *= $factor if (defined ($factor));
  1297. $state = sprintf ("%.0f", $state);
  1298. }
  1299. #4-Octet Signed Value
  1300. elsif ($code eq "dpt13")
  1301. {
  1302. $numval = hex ($value);
  1303. $numval -= 4294967296 if ($numval >= 0x80000000);
  1304. $state = $numval;
  1305. #correct value
  1306. $state -= $offset if (defined ($offset));
  1307. $state *= $factor if (defined ($factor));
  1308. $state = sprintf ("%.0f", $state);
  1309. }
  1310. #4-Octet single precision float
  1311. elsif ($code eq "dpt14")
  1312. {
  1313. $numval = unpack "f", pack "L", hex ($value);
  1314. #correct value
  1315. $state -= $offset if (defined ($offset));
  1316. $state *= $factor if (defined ($factor));
  1317. $state = sprintf ("%.3f","$numval");
  1318. }
  1319. #14-Octet String
  1320. elsif ($code eq "dpt16")
  1321. {
  1322. $numval = 0;
  1323. $state = "";
  1324. for (my $i = 0; $i < 14; $i++)
  1325. {
  1326. my $c = hex(substr($value, $i * 2, 2));
  1327. #exit at string terminator, otherwise append current char
  1328. if (($i != 0) and ($c eq 0))
  1329. {
  1330. $i = 14;
  1331. }
  1332. else
  1333. {
  1334. $state .= sprintf("%c", $c);
  1335. }
  1336. }
  1337. #convert to latin-1
  1338. $state = encode ("utf8", $state) if ($model =~ m/16.001/);
  1339. }
  1340. #DateTime
  1341. elsif ($code eq "dpt19")
  1342. {
  1343. $numval = $value;
  1344. my $time = hex (substr ($value, 6, 6));
  1345. my $date = hex (substr ($value, 0, 6));
  1346. my $secs = ($time & 0x3F) >> 0;
  1347. my $mins = ($time & 0x3F00) >> 8;
  1348. my $hours = ($time & 0x1F0000) >> 16;
  1349. my $day = ($date & 0x1F) >> 0;
  1350. my $month = ($date & 0x0F00) >> 8;
  1351. my $year = ($date & 0xFFFF0000) >> 16;
  1352. $year += 1900;
  1353. $state = sprintf("%02d.%02d.%04d_%02d:%02d:%02d", $day, $month, $year, $hours, $mins, $secs);
  1354. }
  1355. #RGB-Code
  1356. elsif ($code eq "dpt232")
  1357. {
  1358. $numval = hex ($value);
  1359. $state = $numval;
  1360. $state = sprintf ("%.6x", $state);
  1361. }
  1362. else
  1363. {
  1364. Log3 ($name, 2, "decode model: $model, no valid model defined");
  1365. return undef;
  1366. }
  1367. #append unit, if supplied
  1368. my $unit = $dpttypes{$model}{UNIT};
  1369. $state = $state . " " . $unit if (defined ($unit) and not($unit eq ""));
  1370. Log3 ($name, 5, "decode model: $model, code: $code, value: $value, numval: $numval, state: $state");
  1371. return $state;
  1372. }
  1373. #Private function to evaluate command-lists
  1374. #############################
  1375. sub KNX_getCmdList ($$$)
  1376. {
  1377. my ($hash, $cmd, %cmdArray) = @_;
  1378. my $name = $hash->{NAME};
  1379. #return, if cmd is valid
  1380. return undef if (defined ($cmd) and defined ($cmdArray{$cmd}));
  1381. #response for gui or the user, if command is invalid
  1382. my $retVal;
  1383. foreach my $mySet (keys %cmdArray)
  1384. {
  1385. #append set-command
  1386. $retVal = $retVal . " " if (defined ($retVal));
  1387. $retVal = $retVal . $mySet;
  1388. #get options
  1389. my $myOpt = $cmdArray{$mySet};
  1390. #append option, if valid
  1391. $retVal = $retVal . ":" . $myOpt if (defined ($myOpt) and (length ($myOpt) > 0));
  1392. $myOpt = "" if (!defined($myOpt));
  1393. Log3 ($name, 5, "parse cmd-table - Set:$mySet, Option:$myOpt, RetVal:$retVal");
  1394. }
  1395. #if (!defined ($retVal))
  1396. #{
  1397. # $retVal = "error while parsing set-table" ;
  1398. #}
  1399. #else
  1400. #{
  1401. # $retVal = "Unknown argument $cmd, choose one of " . $retVal;
  1402. #}
  1403. return $retVal;
  1404. }
  1405. 1;
  1406. =pod
  1407. =begin html
  1408. <a name="KNX"></a>
  1409. <h3>KNX</h3>
  1410. <ul>
  1411. <p>KNX is a standard for building automation / home automation.
  1412. It is mainly based on a twisted pair wiring, but also other mediums (ip, wireless) are specified.</p>
  1413. For getting started, please refer to this document: <a href="http://www.knx.org/media/docs/Flyers/KNX-Basics/KNX-Basics_de.pdf">KNX-Basics</a>
  1414. <p>While the module <a href="#TUL">TUL</a> represents the connection to the KNX network, the KNX modules represent individual KNX devices. This module provides a basic set of operations (on, off, on-until, on-for-timer)
  1415. to switch on/off KNX devices. For numeric DPT you can use value (set &lt;devname&gt; value &lt;177.45&gt;). For string-DPT you can use string (set &lt;devname&gt; string &lt;Hello World&gt;). For other, non-defined
  1416. dpt you can send raw hex values to the network (set &lt;devname&gt; raw &lt;hexval&gt;).<br>
  1417. 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.</p>
  1418. <p>KNX defines a series of Datapoint Type as standard data types used to allow general interpretation of values of devices manufactured by different companies.
  1419. 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 state, getG&lt;group&gt; and the sender
  1420. address. For every set, there will be a reading with state and setG&lt;group&gt;.</p>
  1421. <p><a name="KNXdefine"></a> <b>Define</b></p>
  1422. <ul>
  1423. <code>define &lt;name&gt; KNX &lt;group&gt;:&lt;DPT&gt;:&lt[;readingName]&gt; [&lt;group&gt;:&lt;DPT&gt; ..] [IODev]</code>
  1424. <p>A KNX device need a concrete DPT. Please refer to <a href="#KNXdpt">Available DPT</a>. Otherwise the system cannot en- or decode the messages. Furthermore you can supply a IO-Device directly at startup. This can be done later on via attribute as well.</p>
  1425. <p>Define an KNX device, connected via a <a href="#TUL">TUL</a>. The &lt;group&gt; parameters are either a group name notation (0-15/0-15/0-255) or the hex representation of the value (0-f0-f0-ff).
  1426. All of the defined groups can be used for bus-communication. Without further attributes, all incoming messages are translated into state. Per default, the first group is used for sending. If you want to send
  1427. via a different group, you have to index it (set &lt;devname&gt; value &lt;17.0&gt; &lt;g2&gt;).<br>
  1428. If you use the readingName, readings are based on this name (e.g. hugo-set, hugo-get for name hugo).</p>
  1429. <p>The module <a href="#autocreate">autocreate</a> is creating a new definition for any unknown sender. The device itself will be NOT fully available, until you added a DPT to the definition. The name will be
  1430. KNX_nnmmooo where nn is the line adress, mm the area and ooo the device.</p>
  1431. <p>Example:</p>
  1432. <pre>
  1433. define lamp1 KNX 0/10/12:dpt1
  1434. define lamp1 KNX 0/10/12:dpt1:meinName 0/0/5:dpt1.001
  1435. define lamp1 KNX 0A0C:dpt1.003 myTul
  1436. </pre>
  1437. One hint regarding dpt1 (binary): all the sub-types have to be used with keyword value. Received telegrams are already encoded to their representation. This mechanism does not work for send-telegrams.
  1438. Here on/off has to be supplied.<br>
  1439. Having the on/off button (for send values) without keyword value is an absolutely special use-case and only valid for dpt1 and its sub-types.<br>
  1440. <p>Example:</p>
  1441. <pre>
  1442. define rollo KNX 0/10/12:dpt1.008
  1443. set rollo value off
  1444. set rollo value on
  1445. </pre>
  1446. </ul>
  1447. <p><a name="KNXset"></a> <b>Set</b></p>
  1448. <ul>
  1449. <code>set &lt;name&gt; &lt;on, off&gt;</code> [g&lt;groupnr&gt;]
  1450. <code>set &lt;name&gt; &lt;on-for-timer, on-until&gt; &lt;time&gt; [g&lt;groupnr&gt;]</code>
  1451. <code>set &lt;name&gt; &lt;value&gt; [g&lt;groupnr&gt;]</code>
  1452. <code>set &lt;name&gt; &lt;string&gt; [g&lt;groupnr&gt;]</code>
  1453. <code>set &lt;name&gt; &lt;raw&gt; [g&lt;groupnr&gt;]</code>
  1454. <p>Example:</p>
  1455. <pre>
  1456. set lamp1 on
  1457. set lamp1 off
  1458. set lamp1 on-for-timer 10
  1459. set lamp1 on-until 13:15:00
  1460. set foobar raw 234578
  1461. set thermo value 23.44
  1462. set message value Hallo Welt
  1463. </pre>
  1464. <p>When as last argument a g&lt;groupnr&gt; is present, the command will be sent
  1465. to the KNX group indexed by the groupnr (starting by 1, in the order as given in define).</p>
  1466. <pre>
  1467. define lamp1 KNX 0/10/01:dpt1 0/10/02:dpt1
  1468. set lamp1 on g2 (will send "on" to 0/10/02)
  1469. </pre>
  1470. <p>A dimmer can be used with a slider as shown in following example:</p>
  1471. <pre>
  1472. define dim1 KNX 0/0/5:dpt5.001
  1473. attr dim1 slider 0,1,100
  1474. attr dim1 webCmd value
  1475. </pre>
  1476. <p>The current date and time can be sent to the bus by the following settings:</p>
  1477. <pre>
  1478. define timedev KNX 0/0/7:dpt10
  1479. attr timedev webCmd value now
  1480. define datedev KNX 0/0/8:dpt11
  1481. attr datedev webCmd value now
  1482. # send every midnight the new date
  1483. define dateset at *00:00:00 set datedev value now
  1484. # send every hour the current time
  1485. define timeset at +*01:00:00 set timedev value now
  1486. </pre>
  1487. </ul>
  1488. <p><a name="KNXget"></a> <b>Get</b></p>
  1489. <ul>
  1490. <p>If you execute get for a KNX-Element the status will be requested a state from the device. The device has to be able to respond to a read - this is not given for all devices.<br>
  1491. The answer from the bus-device is not shown in the toolbox, but is treated like a regular telegram.</p>
  1492. </ul>
  1493. <p><a name="KNXattr"></a> <b>Attributes</b></p>
  1494. <ul><br>
  1495. Common attributes:<br>
  1496. <a href="#DbLogInclude">DbLogInclude</a><br>
  1497. <a href="#DbLogExclude">DbLogExclude</a><br>
  1498. <a href="#IODev">IODev</a><br>
  1499. <a href="#alias">alias</a><br>
  1500. <a href="#comment">comment</a><br>
  1501. <a href="#devStateIcon">devStateIcon</a><br>
  1502. <a href="#devStateStyle">devStateStyle</a><br>
  1503. <a href="#do_not_notify">do_not_notify</a><br>
  1504. <a href="#readingFnAttributes">readingFnAttributes</a><br>
  1505. <a href="#event-aggregator">event-aggregator</a><br>
  1506. <a href="#event-min-interval">event-min-interval</a><br>
  1507. <a href="#event-on-change-reading">event-on-change-reading</a><br>
  1508. <a href="#event-on-update-reading">event-on-update-reading</a><br>
  1509. <a href="#eventMap">eventMap</a><br>
  1510. <a href="#group">group</a><br>
  1511. <a href="#icon">icon</a><br>
  1512. <a href="#room">room</a><br>
  1513. <a href="#showtime">showtime</a><br>
  1514. <a href="#sortby">sortby</a><br>
  1515. <a href="#stateFormat">stateFormat</a><br>
  1516. <a href="#userReadings">userReadings</a><br>
  1517. <a href="#userattr">userattr</a><br>
  1518. <a href="#verbose">verbose</a><br>
  1519. <a href="#webCmd">webCmd</a><br>
  1520. <a href="#widgetOverride">widgetOverride</a><br>
  1521. <br>
  1522. </ul>
  1523. <p><a name="KNXformat"></a> <b>format</b></p>
  1524. <ul>
  1525. The content of this attribute is added to every received value, before this is copied to state.
  1526. <p>Example:</p>
  1527. <pre>
  1528. define myTemperature KNX 0/1/1:dpt5
  1529. attr myTemperature format &degC;
  1530. </pre>
  1531. </ul>
  1532. <p><a name="KNXstateRegex"></a> <b>stateRegex</b></p>
  1533. <ul>
  1534. 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 getG&lt;group&gt;:&lt;state-value&gt;. The substitution is done every time,
  1535. a new object is received. You can use this function for converting, adding units, having more fun with icons, ...
  1536. 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
  1537. <p>Example:</p>
  1538. <pre>
  1539. define myLamp KNX 0/1/1:dpt1 0/1/2:dpt1 0/1/2:dpt1
  1540. attr myLamp stateRegex /getG1:/steuern:/ /getG2:/status:/ /getG3:/sperre:/ /setG[13]:/steuern:/ /setG[3]://
  1541. attr myLamp devStateIcon status.on:general_an status.off:general_aus sperre.on:lock steuern.*:hourglass
  1542. </pre>
  1543. </ul>
  1544. <p><a name="KNXstateCmd"></a> <b>stateCmd</b></p>
  1545. <ul>
  1546. You can supply a perl-command for modifying state. This command is executed directly before updating the reading - so after renaming, format and regex.
  1547. Please supply a valid perl command like using the attribute stateFormat.
  1548. Unlike stateFormat the stateCmd modifies also the content of the reading, not only the hash-conten for visualization.
  1549. <p>Example:</p>
  1550. <pre>
  1551. define myLamp KNX 0/1/1:dpt1 0/1/2:dpt1 0/1/2:dpt1
  1552. attr myLamp stateCmd {$state = sprintf("%s", ReadingsVal($name,"getG2","undef"))}
  1553. </pre>
  1554. </ul>
  1555. <p><a name="KNXanswerReading"></a> <b>answerReading</b></p>
  1556. <ul>
  1557. If enabled, FHEM answers on read requests. The content of state is send to the bus as answer.
  1558. <p>If set to 1, read-requests are answered</p>
  1559. </ul>
  1560. <p><a name="KNXlistenonly"></a> <b>listenonly</b></p>
  1561. <ul>
  1562. If set to 1, the device may not send any messages. As well answering requests although get is prohibited.
  1563. </ul>
  1564. <p><a name="KNXreadonly"></a> <b>readonly</b></p>
  1565. <ul>
  1566. If set to 1, the device may not send any messages. Answering requests are prohibited.Get is allowed.
  1567. </ul>
  1568. <p><a name="KNXslider"></a> <b>slider</b></p>
  1569. <ul>
  1570. slider &lt;min&gt;,&lt;step&gt;,&lt;max&gt;<br>
  1571. With this attribute you can add a slider to any device.
  1572. <p>Example:</p>
  1573. <pre>
  1574. define myDimmer KNX 0/1/1:dpt5
  1575. attr myDimmer slider 0,1,100
  1576. attr myDimmer webCmd value
  1577. </pre>
  1578. </ul>
  1579. <p><a name="KNXdpt"></a> <b>DPT - datapoint-types</b></p>
  1580. <ul>
  1581. <p>The following dpt are implemented and have to be assigned within the device definition.</p>
  1582. dpt1 on, off<br>
  1583. dpt1.001 on, off<br>
  1584. dpt1.002 true, false<br>
  1585. dpt1.003 enable, disable<br>
  1586. dpt1.004 no ramp, ramp<br>
  1587. dpt1.005 no alarm, alarm<br>
  1588. dpt1.006 low, high<br>
  1589. dpt1.007 decrease, increase<br>
  1590. dpt1.008 up, down<br>
  1591. dpt1.009 open, closed<br>
  1592. dpt1.010 start, stop<br>
  1593. dpt1.011 inactive, active<br>
  1594. dpt1.012 not inverted, inverted<br>
  1595. dpt1.013 start/stop, ciclically<br>
  1596. dpt1.014 fixed, calculated<br>
  1597. dpt1.015 no action, reset<br>
  1598. dpt1.016 no action, acknowledge<br>
  1599. dpt1.017 trigger, trigger<br>
  1600. dpt1.018 not occupied, occupied<br>
  1601. dpt1.019 closed, open<br>
  1602. dpt1.020 logical or, logical and<br>
  1603. dpt1.021 scene A, scene B<br>
  1604. dpt1.022 move up/down, move and step mode<br>
  1605. dpt2 value on, value off, value forceOn, value forceOff<br>
  1606. dpt3 -100..+100<br>
  1607. dpt5 0..255<br>
  1608. dpt5.001 0..100 %<br>
  1609. dpt5.003 0..360 &deg;<br>
  1610. dpt5.004 0..255 %<br>
  1611. dpt6 -127..+127<br>
  1612. dpt6.001 0..100 %<br>
  1613. dpt7 0..65535<br>
  1614. dpt7.001 0..65535 s<br>
  1615. dpt7.005 0..65535 s<br>
  1616. dpt7.005 0..65535 m<br>
  1617. dpt7.012 0..65535 mA<br>
  1618. dpt7.013 0..65535 lux<br>
  1619. dpt8 -32768..32768<br>
  1620. dpt8.005 -32768..32768 s<br>
  1621. dpt8.010 -32768..32768 %<br>
  1622. dpt8.011 -32768..32768 &deg;<br>
  1623. dpt9 -670760.0..+670760.0<br>
  1624. dpt9.001 -670760.0..+670760.0 &deg;<br>
  1625. dpt9.004 -670760.0..+670760.0 lux<br>
  1626. dpt9.005 -670760.0..+670760.0 m/s<br>
  1627. dpt9.006 -670760.0..+670760.0 Pa<br>
  1628. dpt9.007 -670760.0..+670760.0 %<br>
  1629. dpt9.008 -670760.0..+670760.0 ppm<br>
  1630. dpt9.009 -670760.0..+670760.0 m³/h<br>
  1631. dpt9.010 -670760.0..+670760.0 s<br>
  1632. dpt9.021 -670760.0..+670760.0 mA<br>
  1633. dpt9.024 -670760.0..+670760.0 kW<br>
  1634. dpt9.025 -670760.0..+670760.0 l/h<br>
  1635. dpt9.026 -670760.0..+670760.0 l/h<br>
  1636. dpt9.028 -670760.0..+670760.0 km/h<br>
  1637. dpt10 01:00:00<br>
  1638. dpt11 01.01.2000<br>
  1639. dpt12 0..+Inf<br>
  1640. dpt13 -Inf..+Inf<br>
  1641. dpt13.010 -Inf..+Inf Wh<br>
  1642. dpt13.013 -Inf..+Inf kWh<br>
  1643. dpt14 -Inf.0..+Inf.0<br>
  1644. dpt14.019 -Inf.0..+Inf.0 A<br>
  1645. dpt14.027 -Inf.0..+Inf.0 V<br>
  1646. dpt14.056 -Inf.0..+Inf.0 W<br>
  1647. dpt14.068 -Inf.0..+Inf.0 &degC;<br>
  1648. dpt14.076 -Inf.0..+Inf.0 m&sup3;<br>
  1649. dpt16 String;<br>
  1650. dpt16.000 ASCII-String;<br>
  1651. dpt16.001 ISO-8859-1-String (Latin1);<br>
  1652. dpt232 RGB-Value RRGGBB<br>
  1653. </ul>
  1654. </ul>
  1655. =end html
  1656. =device
  1657. =item summary Communicates to KNX via module TUL
  1658. =item summary_DE Kommuniziert mit dem KNX über das Modul TUL
  1659. =begin html_DE
  1660. <a name="KNX"></a>
  1661. <h3>KNX</h3>
  1662. <ul>
  1663. <p>KNX ist ein Standard zur Haus- und Geb&auml;udeautomatisierung.
  1664. Der Standard begr&uuml;ndet sich haupts&auml;chlich auf twisted pair, findet aber auch zunehmende Verbreitung auf andere Medien (Funk, Ethernet, ...)</p>
  1665. F&uuml;r Anf&auml;nger sei folgende Lekt&uuml;re empfohlen: <a href="http://www.knx.org/media/docs/Flyers/KNX-Basics/KNX-Basics_de.pdf">KNX-Basics</a>
  1666. <p>Das Modul <a href="#TUL">TUL</a> stellt die Verbindung zum Bus her, Das KNX-Modul stellt die Verbindung zu den einzelnen KNX-/EIB-Ger&auml;ten her. Das Modul stellt Befehle (on, off, on-until, on-for-timer)
  1667. zum ein- und Ausschalten von Ger&auml;ten zur Verf&uuml;gung. F&uuml;r numerische DPT nutzt bitte value (set &lt;devname&gt; value &lt;177.45&gt;). F&uuml;r string-DPT nutzt bitte string
  1668. (set &lt;devname&gt; string &lt;Hello World&gt;). F&uuml;r andere, undefinierte DPT k&ouml;nnt Ihr raw hex Werte ans Netzwerk senden (set &lt;devname&gt; raw &lt;hexval&gt;).<br>
  1669. Komplexe Konfigurationen k&ouml;nnen aufgebaut werden, indem mehrere Modulinstanzen in einem Ger&auml;t definiert werden. Daf&uuml;r werden mehrere Kombinationen aus GAD und DPT in einem Ger&auml;t definiert werden.</p>
  1670. <p>Der KNX-Standard stellt eine Reihe vordefinierter Datentypen zur Verf&uuml;gung. Dies sichert die Hersteller&uuml;bergreifende Kompatibilit&auml;t.
  1671. Basierend auf diesen DPT wird der Status eines Ger&auml;tes interpretiert und in FHEM angezeigt. F&uuml;r jedes empfangene Telegramm wird ein reading mit state, getG&lt;group&gt; und der Absenderadresse angelegt.
  1672. F&uuml;r jedes ser-command wird ein Reading mit state und setG&lt;group&gt; angelegt.</p>
  1673. <p><a name="KNXdefine"></a> <b>Define</b></p>
  1674. <ul>
  1675. <code>define &lt;name&gt; KNX &lt;group&gt;:&lt;DPT&gt;:&lt[;readingName]&gt; [&lt;group&gt;:&lt;DPT&gt; ..] [IODev]</code>
  1676. <p>Ein KNX-device ben&ouml;tigt einen konkreten DPT. Bitte schaut die verf&uuml;gbaren DPT unter <a href="#KNXdpt">Available DPT</a> nach. Wird kein korrekter DPT angegeben, kann das system die Nachrichten nicht korrekt de- / codieren.
  1677. Weiterhin kann bei der Ger&auml;tedefinition eine IO-Schnittstelle angegeben werden. Dies kann sp&auml;ter ebenfalls per Attribut erfolgen.</p>
  1678. <p>Jedes Device muss an eine <a href="#TUL">TUL</a> gebunden sein. Die &lt;group&gt; Parameter werden entweder als Gruppenadresse (0-15/0-15/0-255) oder als Hex-notation angegeben (0-f0-f0-ff).
  1679. Alle definierten Gruppen k&ouml;nnen f&uuml;r die Buskommunikation verwendet werden. Ohne weitere Attribute, werden alle eingehenden Nachrichten in state &uuml;bersetzt.
  1680. Per default wird &uuml;ber die erste Gruppe gesendet.<br>
  1681. Wenn Ihr einen readingNamen angebt, wird dieser als Basis für die Readings benutzt (z.B. hugo-set, hugo-get for name hugo).<br>
  1682. Wollt Ihr &uuml;ber eine andere Gruppe senden. m&uuml;sst Ihr diese indizieren (set &lt;devname&gt; value &lt;17.0&gt; &lt;g2&gt;).</p>
  1683. <p>Das Modul <a href="#autocreate">autocreate</a> generiert eine Instanz f&uuml;r jede unbekannte Gruppenadresse. Das Ger&auml;t selbst wird jedoch NICHT korrekt funktionieren, so lange noch kein korrekter
  1684. DPT angelegt ist. Der Name ist immer KNX_nnmmooo wobei nn die Linie ist, mm der Bereich und ooo die Geräteadresse.</p>
  1685. <p>Example:</p>
  1686. <pre>
  1687. define lamp1 KNX 0/10/12:dpt1
  1688. define lamp1 KNX 0/10/12:dpt1:meinName 0/0/5:dpt1.001
  1689. define lamp1 KNX 0A0C:dpt1.003 myTul
  1690. </pre>
  1691. Ein Hinweis bezüglich dem binären Datentyp dpt1: alle Untertypen müssen über das Schlüsselwort value gesetzt werden. Empfangene Telegramme werden entsprechend ihrer Definition automatisch
  1692. umbenannt. Zu sendende Telegramme sind immer min on/off zu belegen!<br>
  1693. Die zur Verfügung stehenden on/off Schaltflächen ohne den Schlüssel value sind ein absoluter Sonderfall und gelten für den dpt1 und alle Untertypen.
  1694. <p>Example:</p>
  1695. <pre>
  1696. define rollo KNX 0/10/12:dpt1.008
  1697. set rollo value off
  1698. set rollo value on
  1699. </pre>
  1700. </ul>
  1701. <p><a name="KNXset"></a> <b>Set</b></p>
  1702. <ul>
  1703. <code>set &lt;name&gt; &lt;on, off&gt;</code> [g&lt;groupnr&gt;]
  1704. <code>set &lt;name&gt; &lt;on-for-timer, on-until&gt; &lt;time&gt; [g&lt;groupnr&gt;]</code>
  1705. <code>set &lt;name&gt; &lt;value&gt; [g&lt;groupnr&gt;]</code>
  1706. <code>set &lt;name&gt; &lt;string&gt; [g&lt;groupnr&gt;]</code>
  1707. <code>set &lt;name&gt; &lt;raw&gt; [g&lt;groupnr&gt;]</code>
  1708. <p>Example:</p>
  1709. <pre>
  1710. set lamp1 on
  1711. set lamp1 off
  1712. set lamp1 on-for-timer 10
  1713. set lamp1 on-until 13:15:00
  1714. set foobar raw 234578
  1715. set thermo value 23.44
  1716. set message value Hallo Welt
  1717. </pre>
  1718. <p>Wenn eine Gruppe angegeben wurde (g&lt;groupnr&gt;) wird das Telegramm an de indizierte Gruppe gesendet (start bei 1, wie in der Definition angegeben).</p>
  1719. <pre>
  1720. define lamp1 KNX 0/10/01:dpt1 0/10/02:dpt1
  1721. set lamp1 on g2 (will send "on" to 0/10/02)
  1722. </pre>
  1723. <p>Ein Dimmer mit Slider:</p>
  1724. <pre>
  1725. define dim1 KNX 0/0/5:dpt5.001
  1726. attr dim1 slider 0,1,100
  1727. attr dim1 webCmd value
  1728. </pre>
  1729. <p>Aktuelle Uhrzeit / Datum k&ouml;nnen wie folgt auf den Bus gelegt werden:</p>
  1730. <pre>
  1731. define timedev KNX 0/0/7:dpt10
  1732. attr timedev webCmd value now
  1733. define datedev KNX 0/0/8:dpt11
  1734. attr datedev webCmd value now
  1735. # send every midnight the new date
  1736. define dateset at *00:00:00 set datedev value now
  1737. # send every hour the current time
  1738. define timeset at +*01:00:00 set timedev value now
  1739. </pre>
  1740. </ul>
  1741. <p><a name="KNXget"></a> <b>Get</b></p>
  1742. <ul>
  1743. <p>Bei jeder Ausf&uuml;hrung wird eine Leseanfrage an die entsprechende Gruppe geschickt. Die Gruppe muss in der Lage sein, auf diese Anfrage zu antworten (dies ist nicht immer der Fall).<br>
  1744. Die Antwort der Gruppe wird nicht im FHEMWEB angezeigt. Das empfangene Telegramm wird (wie jedes andere) ausgewertet.</p>
  1745. </ul>
  1746. <p><a name="KNXattr"></a> <b>Attributes</b></p>
  1747. <ul><br>
  1748. Common attributes:<br>
  1749. <a href="#DbLogInclude">DbLogInclude</a><br>
  1750. <a href="#DbLogExclude">DbLogExclude</a><br>
  1751. <a href="#IODev">IODev</a><br>
  1752. <a href="#alias">alias</a><br>
  1753. <a href="#comment">comment</a><br>
  1754. <a href="#devStateIcon">devStateIcon</a><br>
  1755. <a href="#devStateStyle">devStateStyle</a><br>
  1756. <a href="#do_not_notify">do_not_notify</a><br>
  1757. <a href="#readingFnAttributes">readingFnAttributes</a><br>
  1758. <a href="#event-aggregator">event-aggregator</a><br>
  1759. <a href="#event-min-interval">event-min-interval</a><br>
  1760. <a href="#event-on-change-reading">event-on-change-reading</a><br>
  1761. <a href="#event-on-update-reading">event-on-update-reading</a><br>
  1762. <a href="#eventMap">eventMap</a><br>
  1763. <a href="#group">group</a><br>
  1764. <a href="#icon">icon</a><br>
  1765. <a href="#room">room</a><br>
  1766. <a href="#showtime">showtime</a><br>
  1767. <a href="#sortby">sortby</a><br>
  1768. <a href="#stateFormat">stateFormat</a><br>
  1769. <a href="#userReadings">userReadings</a><br>
  1770. <a href="#userattr">userattr</a><br>
  1771. <a href="#verbose">verbose</a><br>
  1772. <a href="#webCmd">webCmd</a><br>
  1773. <a href="#widgetOverride">widgetOverride</a><br>
  1774. <br>
  1775. </ul>
  1776. <p><a name="KNXformat"></a> <b>format</b></p>
  1777. <ul>
  1778. Der Inhalt dieses Attributes wird bei jedem empfangenen Wert angehangen, bevor der Wert in state kopeiert wird.
  1779. <p>Example:</p>
  1780. <pre>
  1781. define myTemperature KNX 0/1/1:dpt5
  1782. attr myTemperature format &degC;
  1783. </pre>
  1784. </ul>
  1785. <p><a name="KNXstateRegex"></a> <b>stateRegex</b></p>
  1786. <ul>
  1787. Es kann eine Reihe an Search/Replace Patterns &uuml;bergeben werden (getrennt durch einen Slash). Intern wird der neue Wert von state immer im Format getG&lt;group&gt;:&lt;state-value&gt;. abgebildet.
  1788. Die Ersetzungen werden bei bei jedem neuen Telegramm vorgenommen. Ihr k&ouml;nnt die Funktion f&uuml;r Konvertierungen nutzen, Einheiten hinzuf&uuml;gen, Spaß mit Icons haben, ...
  1789. Diese Funktion wirkt nur auf den Inhalt von State - sonst wird nichts beeinflusst.
  1790. Die Funktion wird direkt nach dem Ersetzen der Readings-Namen und dem ergänzen der Formate ausgeführt.
  1791. <p>Example:</p>
  1792. <pre>
  1793. define myLamp KNX 0/1/1:dpt1 0/1/2:dpt1 0/1/2:dpt1
  1794. attr myLamp stateRegex /getG1:/steuern:/ /getG2:/status:/ /getG3:/sperre:/ /setG[13]:/steuern:/ /setG[3]://
  1795. attr myLamp devStateIcon status.on:general_an status.off:general_aus sperre.on:lock steuern.*:hourglass
  1796. </pre>
  1797. </ul>
  1798. <p><a name="KNXstateCmd"></a> <b>stateCmd</b></p>
  1799. <ul>
  1800. Hier könnt Ihr ein perl-Kommando angeben, welches state beeinflusst. Die Funktion wird unmittelbar vor dem Update des Readings aufgerufen - also nach dem Umbennenen der Readings, format und regex.
  1801. Es ist ein gültiges Perl-Kommando anzugeben (vgl. stateFormat). Im Gegensatz zu StateFormat wirkt sich dieses Attribut inhaltlich auf das Reading aus, und nicht "nur" auf die Anzeige im FHEMWEB.
  1802. <p>Beispiel:</p>
  1803. <pre>
  1804. define myLamp KNX 0/1/1:dpt1 0/1/2:dpt1 0/1/2:dpt1
  1805. attr myLamp stateCmd {$state = sprintf("%s", ReadingsVal($name,"getG2","undef"))}
  1806. </pre>
  1807. </ul>
  1808. <p><a name="KNXanswerReading"></a> <b>answerReading</b></p>
  1809. <ul>
  1810. Wenn aktiviert, antwortet FHEM auf Leseanfragen. Der Inhalt von state wird auf den Bus gelegt.
  1811. <p>Leseanfragen werden beantwortet, wenn der Wert auf 1 gesetzt ist.</p>
  1812. </ul>
  1813. <p><a name="KNXlistenonly"></a> <b>listenonly</b></p>
  1814. <ul>
  1815. Wenn auf 1 gesetzt, kann das Ger&auml;t keine Nachrichten senden. Sowohl Leseanfragen als auch get sind verboten.
  1816. </ul>
  1817. <p><a name="KNXreadonly"></a> <b>readonly</b></p>
  1818. <ul>
  1819. Wenn auf 1 gesetzt, kann das Ger&auml;t keine Nachrichten senden. Leseanfragen sind verboten. Get ist erlaubt.
  1820. </ul>
  1821. <p><a name="KNXslider"></a> <b>slider</b></p>
  1822. <ul>
  1823. slider &lt;min&gt;,&lt;step&gt;,&lt;max&gt;<br>
  1824. Mit diesem Attribut k&ouml;nnt Ihr jedem Ger&auml;t einen Slider verpassen.
  1825. <p>Example:</p>
  1826. <pre>
  1827. define myDimmer KNX 0/1/1:dpt5
  1828. attr myDimmer slider 0,1,100
  1829. attr myDimmer webCmd value
  1830. </pre>
  1831. </ul>
  1832. <p><a name="KNXdpt"></a> <b>DPT - datapoint-types</b></p>
  1833. <ul>
  1834. <p>Die folgenden DPT sind implementiert und m&uuml;ssen in der Gruppendefinition angegeben werden.</p>
  1835. dpt1 on, off<br>
  1836. dpt1.001 on, off<br>
  1837. dpt1.002 true, false<br>
  1838. dpt1.003 enable, disable<br>
  1839. dpt1.004 no ramp, ramp<br>
  1840. dpt1.005 no alarm, alarm<br>
  1841. dpt1.006 low, high<br>
  1842. dpt1.007 decrease, increase<br>
  1843. dpt1.008 up, down<br>
  1844. dpt1.009 open, closed<br>
  1845. dpt1.010 start, stop<br>
  1846. dpt1.011 inactive, active<br>
  1847. dpt1.012 not inverted, inverted<br>
  1848. dpt1.013 start/stop, ciclically<br>
  1849. dpt1.014 fixed, calculated<br>
  1850. dpt1.015 no action, reset<br>
  1851. dpt1.016 no action, acknowledge<br>
  1852. dpt1.017 trigger, trigger<br>
  1853. dpt1.018 not occupied, occupied<br>
  1854. dpt1.019 closed, open<br>
  1855. dpt1.020 logical or, logical and<br>
  1856. dpt1.021 scene A, scene B<br>
  1857. dpt1.022 move up/down, move and step mode<br>
  1858. dpt2 value on, value off, value forceOn, value forceOff<br>
  1859. dpt3 -100..+100<br>
  1860. dpt5 0..255<br>
  1861. dpt5.001 0..100 %<br>
  1862. dpt5.003 0..360 &deg;<br>
  1863. dpt5.004 0..255 %<br>
  1864. dpt6 -127..+127<br>
  1865. dpt6.001 0..100 %<br>
  1866. dpt7 0..65535<br>
  1867. dpt7.001 0..65535 s<br>
  1868. dpt7.005 0..65535 s<br>
  1869. dpt7.005 0..65535 m<br>
  1870. dpt7.012 0..65535 mA<br>
  1871. dpt7.013 0..65535 lux<br>
  1872. dpt8 -32768..32768<br>
  1873. dpt8.005 -32768..32768 s<br>
  1874. dpt8.010 -32768..32768 %<br>
  1875. dpt8.011 -32768..32768 &deg;<br>
  1876. dpt9 -670760.0..+670760.0<br>
  1877. dpt9.001 -670760.0..+670760.0 &deg;<br>
  1878. dpt9.004 -670760.0..+670760.0 lux<br>
  1879. dpt9.005 -670760.0..+670760.0 m/s<br>
  1880. dpt9.006 -670760.0..+670760.0 Pa<br>
  1881. dpt9.007 -670760.0..+670760.0 %<br>
  1882. dpt9.008 -670760.0..+670760.0 ppm<br>
  1883. dpt9.009 -670760.0..+670760.0 m³/h<br>
  1884. dpt9.010 -670760.0..+670760.0 s<br>
  1885. dpt9.021 -670760.0..+670760.0 mA<br>
  1886. dpt9.024 -670760.0..+670760.0 kW<br>
  1887. dpt9.025 -670760.0..+670760.0 l/h<br>
  1888. dpt9.026 -670760.0..+670760.0 l/h<br>
  1889. dpt9.028 -670760.0..+670760.0 km/h<br>
  1890. dpt10 01:00:00<br>
  1891. dpt11 01.01.2000<br>
  1892. dpt12 0..+Inf<br>
  1893. dpt13 -Inf..+Inf<br>
  1894. dpt13.010 -Inf..+Inf Wh<br>
  1895. dpt13.013 -Inf..+Inf kWh<br>
  1896. dpt14 -Inf.0..+Inf.0<br>
  1897. dpt14.019 -Inf.0..+Inf.0 A<br>
  1898. dpt14.027 -Inf.0..+Inf.0 V<br>
  1899. dpt14.056 -Inf.0..+Inf.0 W<br>
  1900. dpt14.068 -Inf.0..+Inf.0 &degC;<br>
  1901. dpt14.076 -Inf.0..+Inf.0 m&sup3;<br>
  1902. dpt16 String;<br>
  1903. dpt16.000 ASCII-String;<br>
  1904. dpt16.001 ISO-8859-1-String (Latin1);<br>
  1905. dpt232 RGB-Value RRGGBB<br>
  1906. </ul>
  1907. </ul>
  1908. =end html_DE
  1909. =cut