10_KNX.pm 82 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389
  1. ##############################################
  2. # $Id: 10_KNX.pm 17471 2018-10-07 06:17:05Z andi291 $
  3. # ABU 20180218 restructuring, removed older documentation
  4. # ABU 20180317 setExtensions reingebaut, set funktion
  5. # ABU 20180319 repaired "reply"-function
  6. # ABU 20180319 tuned "reply"-function
  7. # ABU 20180322 switch context for put-cmd, minor fixes
  8. # ABU 20180328 fixed get-name containing "-"
  9. # ABU 20180408 Added attriut screening, implemented set/get/listenonly, prevent to identical GADS in one device
  10. # ABU 20180411 Added timer functions, prevented two identical GAD in one device
  11. # ABU 20180413 Fixed some naming issues in defined, made en-doku, removed DE-Doku
  12. # ABU 20180416 corrected timedev in doku
  13. # ABU 20180418 removed spam-log in "get"; replaced "$value" by "undef" in encode and decode function if model not defined
  14. # ABU 20180419 fixed Doku, added nosuffix, added dpt1.000
  15. # ABU 20180426 minor fixes in answering bus-requests
  16. # ABU 20180509 Added dpt14.033
  17. # ABU 20180519 Added dpt17.001, adjustet $PAT_GAD_OPTIONS with boundaries and whitespace
  18. # ABU 20180523 Added dpt7.007
  19. # ABU 20180528 Patched dpt1 in dpt-list and encodyByDpt for being backward-compatible
  20. # ABU 20180604 Set dpt17-offset to "0", added examples
  21. # ABU 20180605 Added dpt18, tuned doku
  22. # ABU 20180605 Corrected dpt18
  23. # ABU 20180605 Added example for autogenerated devices
  24. # ABU 20180605 Added workaround for STATE
  25. # ABU 20180606 Fixed dpt18, fixed offset-addition in decode (was "-" instead of "+"), fixed issue with slider
  26. # ABU 20180607 seperated limit and scale from encode/decode in order to avoid warnings and clean up
  27. # ABU 20180613 fixed scaling algo
  28. # ABU 20180613 fixed scaling algo part 2
  29. # ABU 20180624 no set-option for listenoly- or get-devices, warning for illegal EVAL
  30. # ABU 20180626 fixed last changes
  31. # ABU 20180706 changed eval, removed stateCopy
  32. # ABU 20180706 fixed doku: changed readonly in listenonly
  33. # ABU 20180815 updated link in doku, changed (dpt16$) to dpt16 in set, tried to fix öast-sender (replaced bulk by single in decoding loop)
  34. # ABU 20180829 added dpt9.0020, tried workaround in putCmd, remove non printable chars
  35. # ABU 20180925 added dpt3.007, added last-sender "fhem"
  36. # ABU 20180926 fixed KNX_Eval in line 1291 (replaced hash by deviceHash), fixed decoding dpt3
  37. # ABU 20181007 fixed dpt19
  38. package main;
  39. use strict;
  40. use warnings;
  41. use Encode;
  42. use SetExtensions;
  43. #set to 1 for debug
  44. my $debug = 0;
  45. #string constant for autocreate
  46. my $modelErr = "MODEL_NOT_DEFINED";
  47. my $OFF = "off";
  48. my $ON = "on";
  49. my $ONFORTIMER = "on-for-timer";
  50. my $ONUNTIL = "on-until";
  51. my $OFFFORTIMER = "off-for-timer";
  52. my $OFFUNTIL = "off-until";
  53. my $TOGGLE = "toggle";
  54. my $RAW = "raw";
  55. my $RGB = "rgb";
  56. my $STRING = "string";
  57. my $VALUE = "value";
  58. #valid set commands
  59. my %sets = (
  60. $OFF => "",
  61. $ON => "",
  62. $ONFORTIMER => "",
  63. $ONUNTIL => "",
  64. $OFFFORTIMER => "",
  65. $OFFUNTIL => "",
  66. $TOGGLE => "",
  67. $RAW => "",
  68. $RGB => "colorpicker",
  69. $STRING => "",
  70. $VALUE => ""
  71. );
  72. #identifier for TUL
  73. my $id = 'C';
  74. #regex patterns
  75. #pattern for group-adress
  76. my $PAT_GAD = '^[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{1,3}$';
  77. #pattern for group-adress in hex-format
  78. #new syntax for extended adressing
  79. my $PAT_GAD_HEX = '^[0-9a-f]{5}$';
  80. #old syntax
  81. #my $PAT_GAD_HEX = qr/^[0-9a-f]{4}$/;
  82. #pattern for group-no
  83. my $PAT_GNO = '[gG][1-9][0-9]?';
  84. #pattern for GAD-Options
  85. my $PAT_GAD_OPTIONS = '^\s*((get)|(set)|(listenonly))\s*$';
  86. #pattern for GAD-suffixes
  87. my $PAT_GAD_SUFFIX = 'nosuffix';
  88. #pattern for forbidden GAD-Names
  89. #my $PAT_GAD_NONAME = '((on)|(off)|(value)|(raw)|' . $PAT_GAD_OPTIONS . ')$';
  90. #pattern for DPT
  91. my $PAT_GAD_DPT = 'dpt\d*\.?\d*';
  92. #CODE is the identifier for the en- and decode algos. See encode and decode functions
  93. #UNIT is appended to state for a better reading
  94. #FACTOR and OFFSET are used to normalize a value. value = FACTOR * (RAW - OFFSET). Must be undef for non-numeric values.
  95. #PATTERN is used to check an trim the input-values
  96. #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
  97. #if supplied, setlist is passed directly to fhemweb in order to show comand-buttons in the details-view (e.g. "colorpicker" or "item1,item2,item3")
  98. #if setlist is not supplied and min/max are given, a slider is shown for numeric values. Otherwise min/max value are shown in a list
  99. my %dpttypes = (
  100. #Binary value
  101. "dpt1" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(0?1)|(0?0))$/i, MIN=>"off", MAX=>"on"},
  102. "dpt1.000" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(0?1)|(0?0))$/i, MIN=>"0", MAX=>"1"},
  103. "dpt1.001" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(0?1)|(0?0))$/i, MIN=>"off", MAX=>"on"},
  104. "dpt1.002" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(true)|(false)|(0?1)|(0?0))$/i, MIN=>"false", MAX=>"true"},
  105. "dpt1.003" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(enable)|(disable)|(0?1)|(0?0))$/i, MIN=>"disable", MAX=>"enable"},
  106. "dpt1.004" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(0?1)|(0?0))$/i, MIN=>"no ramp", MAX=>"ramp"},
  107. "dpt1.005" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(0?1)|(0?0))$/i, MIN=>"no alarm", MAX=>"alarm"},
  108. "dpt1.006" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(0?1)|(0?0))$/i, MIN=>"low", MAX=>"high"},
  109. "dpt1.007" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(0?1)|(0?0))$/i, MIN=>"decrease", MAX=>"increase"},
  110. "dpt1.008" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(up)|(down)|(0?1)|(0?0))$/i, MIN=>"up", MAX=>"down"},
  111. "dpt1.009" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(closed)|(open)|(0?1)|(0?0))$/i, MIN=>"open", MAX=>"closed"},
  112. "dpt1.010" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(start)|(stop)|(0?1)|(0?0))$/i, MIN=>"stop", MAX=>"start"},
  113. "dpt1.011" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(0?1)|(0?0))$/i, MIN=>"inactive", MAX=>"active"},
  114. "dpt1.012" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(0?1)|(0?0))$/i, MIN=>"not inverted", MAX=>"inverted"},
  115. "dpt1.013" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(0?1)|(0?0))$/i, MIN=>"start/stop", MAX=>"cyclically"},
  116. "dpt1.014" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(0?1)|(0?0))$/i, MIN=>"fixed", MAX=>"calculated"},
  117. "dpt1.015" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(0?1)|(0?0))$/i, MIN=>"no action", MAX=>"reset"},
  118. "dpt1.016" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(0?1)|(0?0))$/i, MIN=>"no action", MAX=>"acknowledge"},
  119. "dpt1.017" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(0?1)|(0?0))$/i, MIN=>"trigger", MAX=>"trigger"},
  120. "dpt1.018" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(0?1)|(0?0))$/i, MIN=>"not occupied", MAX=>"occupied"},
  121. "dpt1.019" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(closed)|(open)|(0?1)|(0?0))$/i, MIN=>"closed", MAX=>"open"},
  122. "dpt1.021" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(0?1)|(0?0))$/i, MIN=>"logical or", MAX=>"logical and"},
  123. "dpt1.022" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(0?1)|(0?0))$/i, MIN=>"scene A", MAX=>"scene B"},
  124. "dpt1.023" => {CODE=>"dpt1", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(0?1)|(0?0))$/i, MIN=>"move up/down", MAX=>"move and step mode"},
  125. #Step value (two-bit)
  126. "dpt2" => {CODE=>"dpt2", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/((on)|(off)|(forceon)|(forceoff))$/i, MIN=>undef, MAX=>undef, SETLIST=>"on,off,forceon,forceoff"},
  127. #Step value (four-bit)
  128. "dpt3" => {CODE=>"dpt3", UNIT=>"", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/i, MIN=>-100, MAX=>100},
  129. "dpt3.007" => {CODE=>"dpt3", UNIT=>"%", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/i, MIN=>-100, MAX=>100},
  130. # 1-Octet unsigned value
  131. "dpt5" => {CODE=>"dpt5", UNIT=>"", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/i, MIN=>0, MAX=>255},
  132. "dpt5.001" => {CODE=>"dpt5", UNIT=>"%", FACTOR=>100/255, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/i, MIN=>0, MAX=>100},
  133. "dpt5.003" => {CODE=>"dpt5", UNIT=>"°", FACTOR=>360/255, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/i, MIN=>0, MAX=>360},
  134. "dpt5.004" => {CODE=>"dpt5", UNIT=>"%", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/i, MIN=>0, MAX=>255},
  135. # 1-Octet signed value
  136. "dpt6" => {CODE=>"dpt6", UNIT=>"", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/i, MIN=>-127, MAX=>127},
  137. "dpt6.001" => {CODE=>"dpt6", UNIT=>"%", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/i, MIN=>0, MAX=>100},
  138. # 2-Octet unsigned Value
  139. "dpt7" => {CODE=>"dpt7", UNIT=>"", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/i, MIN=>0, MAX=>65535},
  140. "dpt7.001" => {CODE=>"dpt7", UNIT=>"", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/i, MIN=>0, MAX=>65535},
  141. "dpt7.005" => {CODE=>"dpt7", UNIT=>"s", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/i, MIN=>0, MAX=>65535},
  142. "dpt7.006" => {CODE=>"dpt7", UNIT=>"m", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/i, MIN=>0, MAX=>65535},
  143. "dpt7.007" => {CODE=>"dpt7", UNIT=>"h", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/i, MIN=>0, MAX=>65535},
  144. "dpt7.012" => {CODE=>"dpt7", UNIT=>"mA", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/i, MIN=>0, MAX=>65535},
  145. "dpt7.013" => {CODE=>"dpt7", UNIT=>"lux", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/i, MIN=>0, MAX=>65535},
  146. # 2-Octet signed Value
  147. "dpt8" => {CODE=>"dpt8", UNIT=>"", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/i, MIN=>-32768, MAX=>32768},
  148. "dpt8.005" => {CODE=>"dpt8", UNIT=>"s", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/i, MIN=>-32768, MAX=>32768},
  149. "dpt8.010" => {CODE=>"dpt8", UNIT=>"%", FACTOR=>0.01, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/i, MIN=>-32768, MAX=>32768},
  150. "dpt8.011" => {CODE=>"dpt8", UNIT=>"°", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/i, MIN=>-32768, MAX=>32768},
  151. # 2-Octet Float value
  152. "dpt9" => {CODE=>"dpt9", UNIT=>"", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/i, MIN=>-670760, MAX=>670760},
  153. "dpt9.001" => {CODE=>"dpt9", UNIT=>"°C", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/i, MIN=>-670760, MAX=>670760},
  154. "dpt9.004" => {CODE=>"dpt9", UNIT=>"lux", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/i, MIN=>-670760, MAX=>670760},
  155. "dpt9.006" => {CODE=>"dpt9", UNIT=>"Pa", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/i, MIN=>-670760, MAX=>670760},
  156. "dpt9.005" => {CODE=>"dpt9", UNIT=>"m/s", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/i, MIN=>-670760, MAX=>670760},
  157. "dpt9.007" => {CODE=>"dpt9", UNIT=>"%", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/i, MIN=>-670760, MAX=>670760},
  158. "dpt9.008" => {CODE=>"dpt9", UNIT=>"ppm", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/i, MIN=>-670760, MAX=>670760},
  159. "dpt9.009" => {CODE=>"dpt9", UNIT=>"m&sup3/h", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/i, MIN=>-670760, MAX=>670760},
  160. "dpt9.010" => {CODE=>"dpt9", UNIT=>"s", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/i, MIN=>-670760, MAX=>670760},
  161. "dpt9.020" => {CODE=>"dpt9", UNIT=>"mV", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/i, MIN=>-670760, MAX=>670760},
  162. "dpt9.021" => {CODE=>"dpt9", UNIT=>"mA", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/i, MIN=>-670760, MAX=>670760},
  163. "dpt9.024" => {CODE=>"dpt9", UNIT=>"kW", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/i, MIN=>-670760, MAX=>670760},
  164. "dpt9.025" => {CODE=>"dpt9", UNIT=>"l/h", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/i, MIN=>-670760, MAX=>670760},
  165. "dpt9.026" => {CODE=>"dpt9", UNIT=>"l/h", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/i, MIN=>-670760, MAX=>670760},
  166. "dpt9.028" => {CODE=>"dpt9", UNIT=>"km/h", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/i, MIN=>-670760, MAX=>670760},
  167. # Time of Day
  168. "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]))|(now)/i, MIN=>undef, MAX=>undef},
  169. # Date
  170. "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]))|(now)/i, MIN=>undef, MAX=>undef},
  171. # 4-Octet unsigned value (handled as dpt7)
  172. "dpt12" => {CODE=>"dpt12", UNIT=>"", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,10}/i, MIN=>0, MAX=>4294967295},
  173. # 4-Octet Signed Value
  174. "dpt13" => {CODE=>"dpt13", UNIT=>"", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,10}/i, MIN=>-2147483647, MAX=>2147483647},
  175. "dpt13.010" => {CODE=>"dpt13", UNIT=>"Wh", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,10}/i, MIN=>-2147483647, MAX=>2147483647},
  176. "dpt13.013" => {CODE=>"dpt13", UNIT=>"kWh", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,10}/i, MIN=>-2147483647, MAX=>2147483647},
  177. # 4-Octet single precision float
  178. "dpt14" => {CODE=>"dpt14", UNIT=>"", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/i, MIN=>undef, MAX=>undef},
  179. "dpt14.019" => {CODE=>"dpt14", UNIT=>"A", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/i, MIN=>undef, MAX=>undef},
  180. "dpt14.027" => {CODE=>"dpt14", UNIT=>"V", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/i, MIN=>undef, MAX=>undef},
  181. "dpt14.033" => {CODE=>"dpt14", UNIT=>"Hz", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/i, MIN=>undef, MAX=>undef},
  182. "dpt14.056" => {CODE=>"dpt14", UNIT=>"W", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/i, MIN=>undef, MAX=>undef},
  183. "dpt14.068" => {CODE=>"dpt14", UNIT=>"°C", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/i, MIN=>undef, MAX=>undef},
  184. "dpt14.076" => {CODE=>"dpt14", UNIT=>"m³", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/i, MIN=>undef, MAX=>undef},
  185. "dpt14.057" => {CODE=>"dpt14", UNIT=>"cos Φ", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/i, MIN=>undef, MAX=>undef},
  186. # 14-Octet String
  187. "dpt16" => {CODE=>"dpt16", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/.{1,14}/i, MIN=>undef, MAX=>undef},
  188. "dpt16.000" => {CODE=>"dpt16", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/.{1,14}/i, MIN=>undef, MAX=>undef},
  189. "dpt16.001" => {CODE=>"dpt16", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/.{1,14}/i, MIN=>undef, MAX=>undef},
  190. # Scene, 0-63
  191. "dpt17.001" => {CODE=>"dpt5", UNIT=>"", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/i, MIN=>0, MAX=>63},
  192. # Scene, 1-64
  193. "dpt18.001" => {CODE=>"dpt5", UNIT=>"", FACTOR=>1, OFFSET=>1, PATTERN=>qr/[+-]?\d{1,3}/i, MIN=>1, MAX=>64},
  194. #date and time
  195. "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])))|(now)/i, MIN=>undef, MAX=>undef},
  196. # Color-Code
  197. "dpt232" => {CODE=>"dpt232", UNIT=>"", FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/[0-9a-f]{6}/i, MIN=>undef, MAX=>undef, SETLIST=>"colorpicker"}
  198. );
  199. #Init this device
  200. #This declares the interface to fhem
  201. #############################
  202. sub
  203. KNX_Initialize($) {
  204. my ($hash) = @_;
  205. $hash->{Match} = "^$id.*";
  206. $hash->{GetFn} = "KNX_Get";
  207. $hash->{SetFn} = "KNX_Set";
  208. $hash->{StateFn} = "KNX_State";
  209. $hash->{DefFn} = "KNX_Define";
  210. $hash->{UndefFn} = "KNX_Undef";
  211. $hash->{ParseFn} = "KNX_Parse";
  212. $hash->{AttrFn} = "KNX_Attr";
  213. $hash->{NotifyFn} = "KNX_Notify";
  214. $hash->{DbLog_splitFn} = "KNX_DbLog_split";
  215. $hash->{AttrList} = "IODev " . #tells the module the IO-Device to communicate with. Optionally set within definition.
  216. "do_not_notify:1,0 " . #supress any notification (including log)
  217. "showtime:1,0 " . #shows time instead of received value in state
  218. "answerReading:1,0 " . #allows FHEM to answer a read telegram
  219. "stateRegex:textField-long " .#modifies state value
  220. "stateCmd:textField-long " . #modify state value
  221. "putCmd:textField-long " . #called when the KNX bus asks for a -put reading
  222. "format " . #supplies post-string
  223. "listenonly:1,0 " . #DEPRECATED
  224. "readonly:1,0 " . #DEPRECATED
  225. "slider " . #DEPRECATED
  226. "useSetExtensions:1,0 " . #DEPRECATED
  227. "$readingFnAttributes "; #standard attributes
  228. }
  229. #Define this device
  230. #Is called at every define
  231. #############################
  232. sub
  233. KNX_Define($$) {
  234. my ($hash, $def) = @_;
  235. #enable newline within define with \
  236. $def =~ s/\n/ /g;
  237. my @a = split("[ \t][ \t]*", $def);
  238. #device name
  239. my $name = $a[0];
  240. #set verbose to 5, if debug enabled
  241. $attr{$name}{verbose} = 5 if ($debug eq 1);
  242. my $tempStr = join (", ", @a);
  243. Log3 ($name, 5, "define $name: enter $hash, attributes: $tempStr");
  244. #too less arguments
  245. return "wrong syntax - define <name> KNX <group:model[:GAD-name][:set|get|listenonly]> [<group:model[:GAD-name][:set|get|listenonly]>*] [<IODev>]" if (int(@a) < 3);
  246. #check for IODev
  247. #is last argument not a group or a group:model pair? Then assign for IODev.
  248. my $lastGroupDef = int(@a);
  249. #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))
  250. 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]:[dpt]/i))
  251. {
  252. $attr{$name}{IODev} = $a[int(@a) - 1];
  253. $lastGroupDef--;
  254. }
  255. #reset
  256. my $firstrun = 1;
  257. $hash->{GADDETAILS} = {};
  258. $hash->{GADTABLE} = {};
  259. #create groups and models, iterate through all possible args
  260. for (my $i = 2; $i < $lastGroupDef; $i++)
  261. {
  262. #backup actual GAD
  263. my $gadDef = $a[$i];
  264. my ($gad, $gadModel, $gadArg3, $gadArg4, $gadArg5) = split /:/, $gadDef;
  265. my $gadCode = undef;
  266. my $gadName = undef;
  267. my $gadOption = undef;
  268. my $gadNoSuffix = undef;
  269. my $rdNameGet = undef;
  270. my $rdNameSet = undef;
  271. my $rdNamePut = undef;
  272. Log3 ($name, 5, "define $name: argCtr $i, string: $a[$i]");
  273. #G-nr
  274. my $gadNo = $i - 1;
  275. #GAD not defined
  276. return "GAD not defined for group-number $gadNo" if (!defined($gad));
  277. #GAD wrong syntax
  278. #either 1/2/3 or 1203
  279. return "wrong group name format in group-number $gadNo: specify as 0-15/0-15/0-255 or as hex" if (($gad !~ m/${PAT_GAD}/i) and ($gad !~ m/${PAT_GAD_HEX}/i));
  280. #check if model supplied
  281. return "no model defined for group-number $gadNo" if (!defined($gadModel));
  282. if (defined ($gadArg3) and defined ($gadArg4) and defined ($gadArg5))
  283. {
  284. Log3 ($name, 5, "define $name: found GAD: $gad, MODEL: $gadModel, Arg3: $gadArg3, Arg4: $gadArg4, Arg5: $gadArg5")
  285. }
  286. elsif (defined ($gadArg3) and defined ($gadArg4))
  287. {
  288. Log3 ($name, 5, "define $name: found GAD: $gad, MODEL: $gadModel, Arg3: $gadArg3, Arg4: $gadArg4");
  289. }
  290. elsif (defined ($gadArg3))
  291. {
  292. Log3 ($name, 5, "define $name: found GAD: $gad, MODEL: $gadModel, Arg3: $gadArg3");
  293. }
  294. #within autocreate no model is supplied - throw warning
  295. if ($gadModel eq $modelErr)
  296. {
  297. Log3 ($name, 2, "define $name: autocreate defines no model - only restricted functions are available");
  298. }
  299. else
  300. {
  301. #check model-type
  302. return "invalid model for group-number $gadNo. Use " .join(",", keys %dpttypes) if (!defined($dpttypes{$gadModel}));
  303. }
  304. #convert to string, if supplied in Hex
  305. #old syntax
  306. #$group = KNX_hexToName ($group) if ($group =~ m/^[0-9a-f]{4}$/i);
  307. #new syntax for extended adressing
  308. $gad = KNX_hexToName ($gad) if ($gad =~ m/^[0-9a-f]{5}$/i);
  309. #convert it vice-versa, just to be sure
  310. $gadCode = KNX_nameToHex ($gad);
  311. ###GADTABLE
  312. #create a hash with gadCode and gadName for later mapping
  313. my $tableHashRef = $hash->{GADTABLE};
  314. #if not defined yet, define a new hash
  315. if (not(defined($tableHashRef)))
  316. {
  317. $tableHashRef={};
  318. $hash->{GADTABLE}=$tableHashRef;
  319. }
  320. ###GADTABLE
  321. return "GAD $gad may be supplied only once per device." if (defined ($tableHashRef->{$gadCode}));
  322. #Arg3 supplied? May be name or option. If not --> Error!
  323. if (defined ($gadArg3))
  324. {
  325. #Arg3 is an option
  326. if ($gadArg3 =~ m/$PAT_GAD_OPTIONS/i)
  327. {
  328. $gadOption = $gadArg3;
  329. }
  330. #Arg3 is a fordbidden name (set-command)
  331. elsif (defined ($sets{$gadArg3}))
  332. {
  333. return "invalid name: $gadArg3. Forbidden names: " .join(",", keys %sets) ;
  334. }
  335. elsif ($gadArg3 =~ m/$PAT_GAD_SUFFIX/i)
  336. {
  337. return "not allowed: supplied \"nosuffix\" without \"name\"" ;
  338. }
  339. #Arg3 is a name -> assign it
  340. else
  341. {
  342. $gadName = $gadArg3;
  343. }
  344. }
  345. #Arg4 supplied? May be option or nosuffix. If not --> Error!
  346. if (defined ($gadArg4))
  347. {
  348. #Arg4 is an option
  349. if ($gadArg4 =~ m/$PAT_GAD_OPTIONS/i)
  350. {
  351. $gadOption = $gadArg4;
  352. }
  353. elsif ($gadArg4 =~ m/$PAT_GAD_SUFFIX/i)
  354. {
  355. $gadNoSuffix = $gadArg4;
  356. }
  357. #Arg4 is unknown
  358. else
  359. {
  360. return "invalid option for group-number $gadNo. Use $PAT_GAD_OPTIONS or $PAT_GAD_SUFFIX";
  361. }
  362. }
  363. #Arg5 supplied? Must be preventSuffix. If not --> Error!
  364. if (defined ($gadArg5))
  365. {
  366. if ($gadArg5 =~ m/$PAT_GAD_SUFFIX/i)
  367. {
  368. $gadNoSuffix = $gadArg5;
  369. }
  370. #Arg5 is unknown
  371. else
  372. {
  373. return "invalid option for group-number $gadNo. Use $PAT_GAD_SUFFIX";
  374. }
  375. }
  376. #cache suffixes
  377. my $suffixGet = "-get";
  378. my $suffixSet = "-set";
  379. my $suffixPut = "-put";
  380. if (defined ($gadNoSuffix) and not ($gadNoSuffix eq ""))
  381. {
  382. $suffixGet = "";
  383. $suffixSet = "";
  384. $suffixPut = "";
  385. }
  386. if (defined ($gadName) and not ($gadName eq ""))
  387. {
  388. if (defined ($gadOption) and not ($gadOption eq ""))
  389. {
  390. #get - prohibit set
  391. if ($gadOption =~ m/(get)|(listenonly)/i)
  392. {
  393. $rdNameGet = $gadName . $suffixGet;
  394. $rdNameSet = "";
  395. $rdNamePut = $gadName . $suffixPut;;
  396. }
  397. #listenonly - prohibit set and put
  398. elsif ($gadOption =~ m/(get)|(listenonly)/i)
  399. {
  400. $rdNameGet = $gadName . $suffixGet;
  401. $rdNameSet = "";
  402. $rdNamePut = "";
  403. }
  404. #set - prohibit put and get
  405. elsif ($gadOption =~ m/(set)/i)
  406. {
  407. $rdNameGet = "";
  408. $rdNameSet = $gadName . $suffixSet;
  409. $rdNamePut = "";
  410. }
  411. }
  412. else
  413. {
  414. $rdNameGet = $gadName . $suffixGet;
  415. $rdNameSet = $gadName . $suffixSet;
  416. $rdNamePut = $gadName . $suffixPut;
  417. }
  418. }
  419. else
  420. {
  421. if (defined ($gadOption) and not ($gadOption eq ""))
  422. {
  423. #get - prohibit set
  424. if ($gadOption =~ m/(get)|(listenonly)/i)
  425. {
  426. $rdNameGet = "getG" . $gadNo;
  427. $rdNameSet = "";
  428. $rdNamePut = "putG" . $gadNo;
  429. }
  430. #listenonly - prohibit set and put
  431. elsif ($gadOption =~ m/(get)|(listenonly)/i)
  432. {
  433. $rdNameGet = "getG" . $gadNo;
  434. $rdNameSet = "";
  435. $rdNamePut = "";
  436. }
  437. #set - prohibit put and get
  438. elsif ($gadOption =~ m/(set)/i)
  439. {
  440. $rdNameGet = "";
  441. $rdNameSet = "setG" . $gadNo;
  442. $rdNamePut = "";
  443. }
  444. }
  445. else
  446. {
  447. $rdNameGet = "getG" . $gadNo;
  448. $rdNameSet = "setG" . $gadNo;
  449. $rdNamePut = "putG" . $gadNo;
  450. }
  451. }
  452. #assuming name in old syntax, if not given...
  453. $gadName = "g" . $gadNo if (!defined ($gadName));
  454. my $log = "define $name: found GAD: $gad, NAME: $gadName NO: $gadNo, HEX: $gadCode, DPT: $gadModel";
  455. $log .= ", OPTION: $gadOption" if (defined ($gadOption));
  456. Log3 ($name, 5, "$log");
  457. #determint dpt-details
  458. my $dptDetails = $dpttypes{$gadModel};
  459. my $setlist;
  460. #case list is given, pass it through
  461. if (defined ($dptDetails->{SETLIST}))
  462. {
  463. $setlist = ":" . $dptDetails->{SETLIST};
  464. }
  465. #case number - place slider
  466. elsif (defined ($dptDetails->{MIN}) and ($dptDetails->{MIN} =~ m/0|[+-]?\d*[(.|,)\d*]/))
  467. {
  468. my $min = $dptDetails->{MIN};
  469. my $max = $dptDetails->{MAX};
  470. my $interval = int(($max-$min)/100);
  471. $interval = 1 if ($interval == 0);
  472. $setlist = ":slider," . $min . "," . $interval . "," . $max;
  473. }
  474. #on/off/...
  475. elsif (defined ($dptDetails->{MIN}))
  476. {
  477. my $min = $dptDetails->{MIN};
  478. my $max = $dptDetails->{MAX};
  479. $setlist = ":" . $min . "," . $max;
  480. }
  481. #plain input field
  482. else
  483. {
  484. $setlist = "";
  485. }
  486. Log3 ($name, 5, "define $name, Estimated reading-names: $rdNameGet, $rdNameSet, $rdNamePut");
  487. Log3 ($name, 5, "define $name, SetList: $setlist") if (defined ($setlist));
  488. #add details to hash
  489. $hash->{GADDETAILS}{$gadName} = {GROUP => $gad, CODE => $gadCode, MODEL => $gadModel, NO => $gadNo, OPTION => $gadOption, RDNAMEGET => $rdNameGet, RDNAMESET => $rdNameSet, RDNAMEPUT => $rdNamePut, SETLIST => $setlist};
  490. #add key and value to GADTABLE
  491. $tableHashRef->{$gadCode} = $gadName;
  492. ###DEFPTR
  493. my @devList = ();
  494. #Restore list, if at least one GAD is installed
  495. @devList = @{$modules{KNX}{defptr}{$gadCode}} if (defined ($modules{KNX}{defptr}{$gadCode}));
  496. #push actual hash to list
  497. push (@devList, $hash);
  498. #backup list
  499. @{$modules{KNX}{defptr}{$gadCode}} = @devList;
  500. ###DEFPTR
  501. #in firstrun backup gadName for later backwardCompatibility
  502. $hash->{FIRSTGADNAME} = $gadName if ($firstrun == 1);
  503. #create getlist for getFn
  504. $hash->{GETSTRING} = join (":noArg ", keys %{$hash->{GADDETAILS}}) . ":noArg";
  505. #create setlist for setFn
  506. my $setString = "";
  507. foreach my $key (keys %{$hash->{GADDETAILS}})
  508. {
  509. #no set-command for listenonly or get
  510. my $option = $hash->{GADDETAILS}{$key}{OPTION};
  511. if (defined ($option) and ($option =~ m/(get)|(listenonly)/i))
  512. {
  513. #readonly, do nothing
  514. }
  515. else
  516. {
  517. $setString .= " " if (length ($setString) > 1);
  518. $setString = $setString . $key . $hash->{GADDETAILS}{$key}{SETLIST};
  519. }
  520. }
  521. $hash->{SETSTRING} = $setString;
  522. Log3 ($name, 5, "GETSTR: " . $hash->{GETSTRING} . ", SETSTR: " . $hash->{SETSTRING});
  523. $firstrun = 0;
  524. }
  525. #common name
  526. $hash->{NAME} = $name;
  527. #backup name for a later rename
  528. $hash->{DEVNAME} = $name;
  529. #assign io-dev automatically, if not given via definition
  530. AssignIoPort($hash);
  531. Log3 ($name, 5, "exit define");
  532. #debug GAD-codes
  533. if (0)
  534. {
  535. foreach my $gd (keys %{$modules{KNX}{defptr}})
  536. {
  537. Log3 ($name, 5, "GAD: $gd");
  538. foreach my $dv (@{$modules{KNX}{defptr}{$gd}})
  539. {
  540. Log3 ($name, 5, "DEV: " . $dv->{NAME} . " (GAD: $gd)");
  541. }
  542. }
  543. }
  544. return undef;
  545. }
  546. #Release this device
  547. #Is called at every delete / shutdown
  548. #############################
  549. sub
  550. KNX_Undef($$) {
  551. my ($hash, $name) = @_;
  552. Log3 ($name, 5, "enter undef $name: hash: $hash name: $name");
  553. #remove hash-pointer from available devices-list
  554. #parse through all valid GAD in this deive
  555. foreach my $gadCode (keys %{$hash->{GADTABLE}})
  556. {
  557. my $gadName = $hash->{GADTABLE}{$gadCode};
  558. Log3 ($name, 5, "undef $name: remove $gadName, $gadCode");
  559. #get list of hash-pointers
  560. my @oldDeviceList = @{$modules{KNX}{defptr}{$gadCode}};
  561. my @newDeviceList = ();
  562. #create new list without this device
  563. foreach my $devHash (@oldDeviceList)
  564. {
  565. push (@newDeviceList, $devHash) if (not ($devHash == $hash));
  566. }
  567. #backup new list
  568. @{$modules{KNX}{defptr}{$gadCode}} = @newDeviceList;
  569. }
  570. Log3 ($name, 5, "exit undef");
  571. return undef;
  572. }
  573. #Places a "read" Message on the KNX-Bus
  574. #The answer is treated as regular telegram
  575. #############################
  576. sub
  577. KNX_Get($@) {
  578. my ($hash, @a) = @_;
  579. my $name = $hash->{NAME};
  580. my $groupnr = 1;
  581. my $tempStr = join (", ", @a);
  582. Log3 ($name, 5, "enter get $name: hash: $hash, attributes: $tempStr");
  583. #not necessary any more - was used to get rid of the "-" from the checkboxes
  584. #splice(@a, 1, 1) if (defined ($a[1]) and ($a[1] =~ m/-/));
  585. my $na = int(@a);
  586. #not more then 2 arguments allowed
  587. Log3 ($name, 2, "get: too much arguments. Only one argument allowed (group-address). Other Arguments are discarded.") if ($na > 2);
  588. #FHEM asks with a ? at startup - no action, no log
  589. return "Unknown argument, choose one of " . $hash->{GETSTRING} if(defined($a[1]) and ($a[1] =~ m/\?/));
  590. #determine gadName to read
  591. #ask for first defined GAD if no argument is supplied
  592. my $gadName;
  593. if (defined ($a[1]))
  594. {
  595. $gadName = $a[1];
  596. }
  597. else
  598. {
  599. $gadName = $hash->{FIRSTGADNAME};
  600. }
  601. #get groupCode
  602. my $groupc = $hash->{GADDETAILS}{$gadName}{CODE};
  603. #get groupAddress
  604. my $group = $hash->{GADDETAILS}{$gadName}{GROUP};
  605. #get option
  606. my $option = $hash->{GADDETAILS}{$gadName}{OPTION};
  607. #return, if unknown group
  608. return "no valid address stored for gad: $gadName" if(!$groupc);
  609. #exit, if read is prohibited
  610. #return "did not request a value - \"listenonly\" is set." if (AttrVal ($name, "listenonly", 0) =~ m/1/);
  611. #exit if get is prohibited
  612. return "did not request a value - \"listenonly\" is set." if (defined ($option) and ($option =~ m/listenonly/i));
  613. return "did not request a value - \"set\" is set." if (defined ($option) and ($option =~ m/set/i));
  614. #send read-request to the bus
  615. Log3 ($name, 5, "get $name: request value for GAD: $group, GAD-NAME: $gadName");
  616. IOWrite($hash, $id, "r" . $groupc);
  617. Log3 ($name, 5, "exit get");
  618. return "current value for $name ($group) requested";
  619. }
  620. #Does something according the given cmd...
  621. #############################
  622. sub
  623. KNX_Set($@) {
  624. my ($hash, @a) = @_;
  625. my $name = $hash->{NAME};
  626. my $ret = "";
  627. my $na = int(@a);
  628. my $tempStr = join (", ", @a);
  629. #log only, if not called with cmd = ?
  630. Log3 ($name, 5, "enter set $name: hash: $hash, attributes: $tempStr") if ((defined ($a[1])) and (not ($a[1] eq "?")));
  631. #return, if no set value specified
  632. return "no set value specified" if($na < 2);
  633. #backup values
  634. my $arg1 = $a[1];
  635. my $arg2 = $a[2] if defined ($a[2]);
  636. #remove whitespaces
  637. $arg1 =~ s/^\s+|\s+$//gi;
  638. #FHEM asks with a "?" at startup or any reload of the device-detail-view
  639. #return string for enabling webfrontend to show boxes, ...
  640. #Log3 ($name, 5, "Unknown argument, choose one of " . $hash->{SETSTRING}) if(defined($arg1) and ($arg1 =~ m/\?/));
  641. return "Unknown argument, choose one of " . $hash->{SETSTRING} if(defined($arg1) and ($arg1 =~ m/\?/));
  642. #contains gadNames to be executed
  643. my $targetGadName = undef;
  644. my $cmd = undef;
  645. my @arg = ();
  646. #check, if old or new syntax
  647. #new syntax, if first arg is a valid gadName
  648. if (defined ($hash->{GADDETAILS}{$arg1}))
  649. {
  650. $targetGadName = $arg1;
  651. $cmd = $arg2;
  652. #backup args
  653. for (my $i = 3; $i <= scalar(@a); $i++)
  654. {
  655. push (@arg, $a[$i]) if (defined ($a[$i]));
  656. }
  657. }
  658. #oldsyntax
  659. else
  660. {
  661. #the command can be send to any of the defined groups indexed starting by 1
  662. #optional last argument starting with g indicates the group
  663. #default
  664. my $groupnr = 1;
  665. my $lastArg = $na - 1;
  666. #select another group, if the last arg starts with a g
  667. if($na > 2 && $a[$lastArg]=~ m/${PAT_GNO}/i)
  668. {
  669. $groupnr = $a[$lastArg];
  670. #remove "g"
  671. $groupnr =~ s/^g//gi;
  672. $lastArg--;
  673. }
  674. #unknown groupnr
  675. return "group-no. not found" if(!defined($groupnr));
  676. foreach my $key (keys %{$hash->{GADDETAILS}})
  677. {
  678. $targetGadName = $key if (int ($hash->{GADDETAILS}{$key}{NO}) == int ($groupnr));
  679. }
  680. $cmd = $arg1;
  681. #backup args
  682. for (my $i = 2; $i <= $lastArg; $i++)
  683. {
  684. push (@arg, $a[$i]) if (defined ($a[$i]));
  685. }
  686. if ($cmd =~ m/$RAW/i)
  687. {
  688. return "no data for cmd $cmd" if ($lastArg < 2);
  689. #check for 1-16 hex-digits
  690. if ($a[2] =~ m/[0-9A-F]{1,16}/i)
  691. {
  692. $cmd = $a[2];
  693. }
  694. else
  695. {
  696. return "$a[2] has wrong syntax. Use hex-format only.";
  697. }
  698. }
  699. elsif ($cmd =~ m/$VALUE/i)
  700. {
  701. my $code = $hash->{GADDETAILS}{$targetGadName}{MODEL};
  702. return "\"value\" not allowed for dpt1, dpt16 and dpt232" if ($code =~ m/(dpt1$)|(dpt16$)|(dpt232$)/i);
  703. return "no data for cmd $cmd" if ($lastArg < 2);
  704. $cmd = $a[2];
  705. $cmd =~ s/,/\./g;
  706. }
  707. #set string <val1 val2 valn>
  708. elsif ($cmd =~ m/$STRING/i)
  709. {
  710. my $code = $hash->{GADDETAILS}{$targetGadName}{MODEL};
  711. return "\"string\" only allowed for dpt16" if (not($code =~ m/dpt16/i));
  712. return "no data for cmd $cmd" if ($lastArg < 2);
  713. $cmd = $a[2];
  714. for (my $i=3; $i<=$lastArg; $i++)
  715. {
  716. $cmd.= " ".$a[$i];
  717. }
  718. }
  719. #set RGB <RRGGBB>
  720. elsif ($cmd =~ m/$RGB/i)
  721. {
  722. my $code = $hash->{GADDETAILS}{$targetGadName}{MODEL};
  723. return "\"RGB\" only allowed for dpt232" if (not($code =~ m/(dpt232$)/i));
  724. return "no data for cmd $cmd" if ($lastArg < 2);
  725. #check for 1-16 hex-digits
  726. if ($a[2] =~ m/[0-9A-F]{6}/i)
  727. {
  728. $cmd = lc($a[2]);
  729. }
  730. else
  731. {
  732. return "$a[2] has wrong syntax. Use hex-format only.";
  733. }
  734. }
  735. }
  736. return "no target and cmd found" if(!defined($targetGadName) and !defined($cmd));
  737. return "no cmd found" if(!defined($cmd));
  738. return "no target found" if(!defined($targetGadName));
  739. $tempStr = join (" ", @arg);
  740. Log3 ($name, 5, "set $name: desired target is gad $targetGadName, command: $cmd, args: $tempStr");
  741. #get details
  742. my $groupCode = $hash->{GADDETAILS}{$targetGadName}{CODE};
  743. my $option = $hash->{GADDETAILS}{$targetGadName}{OPTION};
  744. my $rdString = $hash->{GADDETAILS}{$targetGadName}{RDNAMESET};
  745. #This contains the input
  746. my $value = "";
  747. return "did not set a value - \"listenonly\" is set." if (defined ($option) and ($option =~ m/listenonly/i));
  748. return "did not set a value - \"get\" is set." if (defined ($option) and ($option =~ m/get/i));
  749. ##############################
  750. #process set command with $value as output
  751. #
  752. $value = $cmd;
  753. #Text neads special treatment - additional args may be blanked words
  754. $value .= " " . join (" ", @arg) if (($hash->{GADDETAILS}{$targetGadName}{MODEL} =~ m/dpt16$/i) and (scalar (@arg) > 0));
  755. #Special commands for dpt1 and dpt1.001
  756. if ($hash->{GADDETAILS}{$targetGadName}{MODEL} =~ m/((dpt1)|(dpt1.001))$/i)
  757. {
  758. #delete any running timers
  759. #on-for-timer
  760. if ($hash->{"ON-FOR-TIMER_$groupCode"})
  761. {
  762. CommandDelete(undef, $name . "_timer_$groupCode");
  763. delete $hash->{"ON-FOR-TIMER_$groupCode"};
  764. }
  765. #on-until
  766. if($hash->{"ON-UNTIL_$groupCode"})
  767. {
  768. CommandDelete(undef, $name . "_until_$groupCode");
  769. delete $hash->{"ON-UNTIL_$groupCode"};
  770. }
  771. #off-for-timer
  772. if ($hash->{"OFF-FOR-TIMER_$groupCode"})
  773. {
  774. CommandDelete(undef, $name . "_timer_$groupCode");
  775. delete $hash->{"OFF-FOR-TIMER_$groupCode"};
  776. }
  777. #off-until
  778. if($hash->{"OFF-UNTIL_$groupCode"})
  779. {
  780. CommandDelete(undef, $name . "_until_$groupCode");
  781. delete $hash->{"OFF-UNTIL_$groupCode"};
  782. }
  783. #set on-for-timer / off-for-timer
  784. if ($cmd =~ m/($ONFORTIMER)|($OFFFORTIMER)/i)
  785. {
  786. #get duration
  787. my $duration = sprintf("%02d:%02d:%02d", $arg[0]/3600, ($arg[0]%3600)/60, $arg[0]%60);
  788. Log3 ($name, 5, "set $name: \"on-for-timer\" for $duration");
  789. #create local marker
  790. $hash->{"ON-FOR-TIMER_$groupCode"} = $duration;
  791. #place at-command for switching off
  792. CommandDefine(undef, $name . "_timer_$groupCode at +$duration set $name $targetGadName off");
  793. #switch on or off...
  794. if ($cmd =~ m/on/i)
  795. {
  796. $value = "on";
  797. }
  798. else
  799. {
  800. $value = "off";
  801. }
  802. }
  803. #set on-until / off-until
  804. elsif ($cmd =~ m/($ONUNTIL)|($OFFUNTIL)/i)
  805. {
  806. #get off-time
  807. my ($err, $hr, $min, $sec, $fn) = GetTimeSpec($arg[0]);
  808. return "Error trying to parse timespec for $arg[0]: $err" if (defined($err));
  809. #build of-time
  810. my @lt = localtime;
  811. my $hms_til = sprintf("%02d:%02d:%02d", $hr, $min, $sec);
  812. my $hms_now = sprintf("%02d:%02d:%02d", $lt[2], $lt[1], $lt[0]);
  813. return "Won't switch - now ($hms_now) is later than $hms_til" if($hms_now ge $hms_til);
  814. Log3 ($name, 5, "set $name: \"on-until\" up to $hms_til");
  815. #create local marker
  816. $hash->{"ON-UNTIL_$groupCode"} = $hms_til;
  817. #place at-command for switching off
  818. CommandDefine(undef, $name . "_until_$groupCode at $hms_til set $name $targetGadName off");
  819. #switch on or off...
  820. if ($cmd =~ m/on/i)
  821. {
  822. $value = "on";
  823. }
  824. else
  825. {
  826. $value = "off";
  827. }
  828. }
  829. #toggle
  830. elsif ($cmd =~ m/$TOGGLE/i)
  831. {
  832. if (ReadingsVal($name, $hash->{GADDETAILS}{$targetGadName}{RDNAMEGET}, "") =~ m/off/i)
  833. {
  834. $value = "on";
  835. }
  836. else
  837. {
  838. $value = "off";
  839. }
  840. }
  841. }
  842. #check and cast value
  843. my $transval = KNX_checkAndClean($hash, $value, $targetGadName);
  844. #if cast not successful
  845. return "invalid value: $value" if (!defined($transval));
  846. #
  847. #
  848. #/process set command
  849. ##############################
  850. #send value
  851. $transval = KNX_encodeByDpt($hash, $transval, $targetGadName);
  852. IOWrite($hash, $id, "w" . $groupCode . $transval);
  853. Log3 ($name, 5, "set $name: cmd: $cmd, value: $value, translated: $transval");
  854. #re-read value, do not modify variable name due to usage in cmdAttr
  855. $transval = KNX_decodeByDpt($hash, $transval, $targetGadName);
  856. #append post-string, if supplied
  857. my $suffix = AttrVal($name, "format",undef);
  858. $transval = $transval . " " . $suffix if (defined($suffix));
  859. #execute regex, if defined
  860. my $regAttr = AttrVal($name, "stateRegex", undef);
  861. my $state = KNX_replaceByRegex ($regAttr, $rdString . ":", $transval);
  862. Log3 ($name, 5, "set name: $name - replaced $rdString:$transval to $state") if (not ($transval eq $state));
  863. if (defined($state))
  864. {
  865. readingsBeginUpdate($hash);
  866. readingsBulkUpdate($hash, $rdString, $transval);
  867. #execute state-command if defined
  868. #must be placed after first reading, because it may have a reference
  869. my $cmdAttr = AttrVal($name, "stateCmd", undef);
  870. if (defined ($cmdAttr) and !($cmdAttr eq ""))
  871. {
  872. $state = KNX_eval ($hash, $targetGadName, $state, $cmdAttr);
  873. Log3 ($name, 5, "set name: $name - state replaced via command, result: state:$state");
  874. }
  875. readingsBulkUpdate($hash, "state", $state);
  876. readingsBulkUpdate($hash, "last-sender", "fhem");
  877. readingsEndUpdate($hash, 1);
  878. }
  879. Log3 ($name, 5, "exit set");
  880. return undef;
  881. }
  882. #In case setstate is executed, a readingsupdate is initiated
  883. #############################
  884. sub
  885. KNX_State($$$$) {
  886. my ($hash, $time, $reading, $value) = @_;
  887. my $name = $hash->{NAME};
  888. my $tempStr = join (", ", @_);
  889. Log3 ($name, 5, "enter state: hash: $hash name: $name, attributes: $tempStr");
  890. #in some cases state is submitted within value - if found, take only the stuff after state
  891. #my @strings = split("[sS][tT][aA][tT][eE]", $val);
  892. #$val = $strings[int(@strings) - 1];
  893. return undef if (not (defined($value)));
  894. return undef if (not (defined($reading)));
  895. #remove whitespaces
  896. $value =~ s/^\s+|\s+$//gi;
  897. $reading =~ s/^\s+|\s+$//gi;
  898. #$reading = $reading if ($reading =~ m/state/i);
  899. #workaround for STATE in capitol letters (caused by unknown external function)
  900. $reading = "state" if ($reading =~ m/state/i);
  901. Log3 ($name, 5, "state $name: update $reading with value: $value");
  902. #write value and update reading
  903. readingsSingleUpdate($hash, $reading, $value, 1);
  904. return undef;
  905. }
  906. #Get the chance to qualify attributes
  907. #############################
  908. sub
  909. KNX_Attr(@) {
  910. my ($cmd,$name,$aName,$aVal) = @_;
  911. #if($cmd eq "set")
  912. #{
  913. # if(($attr_name eq "debug") and (($attr_value eq "1") or ($attr_value eq "true")))
  914. # {
  915. # }
  916. #}
  917. Log3 ($name, 2, "Attribut \"listenonly\" is deprecated. Please supply in definition - see commandref for details.") if ($aName =~ m/listenonly/i);
  918. Log3 ($name, 2, "Attribut \"readonly\" is deprecated. Please supply \"get\" in definition - see commandref for details.") if ($aName =~ m/readonly/i);
  919. Log3 ($name, 2, "Attribut \"slider\" is deprecated. Please use widgetOverride in Combination with WebCmd instead. See commandref for details.") if ($aName =~ m/slider/i);
  920. Log3 ($name, 2, "Attribut \"useSetExtensions\" is deprecated.") if ($aName =~ m/useSetExtensions/i);
  921. return undef;
  922. }
  923. #Split reading for DBLOG
  924. #############################
  925. sub KNX_DbLog_split($) {
  926. my ($event) = @_;
  927. my ($reading, $value, $unit);
  928. my $tempStr = join (", ", @_);
  929. Log (5, "splitFn - enter, attributes: $tempStr");
  930. #detect reading - real reading or state?
  931. my $isReading = "false";
  932. $isReading = "true" if ($event =~ m/: /);
  933. #split input-string
  934. my @strings = split (" ", $event);
  935. my $startIndex = undef;
  936. $unit = "";
  937. return undef if (not defined ($strings[0]));
  938. #real reading?
  939. if ($isReading =~ m/true/i)
  940. {
  941. #first one is always reading
  942. $reading = $strings[0];
  943. $reading =~ s/:?$//;
  944. $startIndex = 1;
  945. }
  946. #plain state
  947. else
  948. {
  949. #for reading state nothing is supplied
  950. $reading = "state";
  951. $startIndex = 0;
  952. }
  953. return undef if (not defined ($strings[$startIndex]));
  954. #per default join all single pieces
  955. $value = join(" ", @strings[$startIndex..(int(@strings) - 1)]);
  956. #numeric value?
  957. #if ($strings[$startIndex] =~ /^[+-]?\d*[.,]?\d+/)
  958. if ($strings[$startIndex] =~ /^[+-]?\d*[.,]?\d+$/)
  959. {
  960. $value = $strings[$startIndex];
  961. #single numeric value? Assume second par is unit...
  962. if ((defined ($strings[$startIndex + 1])) && !($strings[$startIndex+1] =~ /^[+-]?\d*[.,]?\d+/))
  963. {
  964. $unit = $strings[$startIndex + 1] if (defined ($strings[$startIndex + 1]));
  965. }
  966. }
  967. #numeric value?
  968. #if ($strings[$startIndex] =~ /^[+-]?\d*[.,]?\d+/)
  969. #{
  970. # $value = $strings[$startIndex];
  971. # $unit = $strings[$startIndex + 1] if (defined ($strings[$startIndex + 1]));
  972. #}
  973. #string or raw
  974. #else
  975. #{
  976. # $value = join(" ", @strings[$startIndex..(int(@strings) - 1)]);
  977. #}
  978. Log (5, "splitFn - READING: $reading, VALUE: $value, UNIT: $unit");
  979. return ($reading, $value, $unit);
  980. }
  981. #Handle incoming messages
  982. #############################
  983. sub
  984. KNX_Parse($$) {
  985. my ($hash, $msg) = @_;
  986. my $name = $hash->{NAME};
  987. #Msg format:
  988. #C(w/r/p)<group><value> i.e. Bw00000101
  989. #we will also take reply telegrams into account,
  990. #as they will be sent if the status is asked from bus
  991. #split message into parts
  992. #old syntax
  993. #$msg =~ m/^$id(.{4})(.{1})(.{4})(.*)$/;
  994. #new syntax for extended adressing
  995. $msg =~ m/^$id(.{5})(.{1})(.{5})(.*)$/;
  996. my $src = $1;
  997. my $cmd = $2;
  998. my $dest = $3;
  999. my $val = $4;
  1000. my $gadCode = $dest;
  1001. my @foundMsgs;
  1002. Log3 ($name, 5, "enter parse: hash: $hash name: $name, dest: $dest, msg: $msg");
  1003. #gad not defined yet, give feedback for autocreate
  1004. if (not (exists $modules{KNX}{defptr}{$gadCode}))
  1005. {
  1006. #format gat
  1007. my $gad = KNX_hexToName($gadCode);
  1008. #create name
  1009. my ($line, $area, $device) = split ("/", $gad);
  1010. my $newDevName = sprintf("KNX_%.2d%.2d%.3d", $line, $area, $device);
  1011. return "UNDEFINED $newDevName KNX $gad:$modelErr";
  1012. }
  1013. #get list from device-hashes using given gadCode (==destination)
  1014. my @deviceList = @{$modules{KNX}{defptr}{$gadCode}};
  1015. #process message for all affected devices and gad's
  1016. #debug GAD-codes
  1017. if (0)
  1018. {
  1019. Log3 ($name, 5, "GAD: $gadCode");
  1020. foreach my $dv (@{$modules{KNX}{defptr}{$gadCode}})
  1021. {
  1022. Log3 ($name, 5, "DEV: " . $dv->{NAME} . " (GAD: $gadCode)");
  1023. }
  1024. }
  1025. foreach my $deviceHash (@deviceList)
  1026. {
  1027. #get details
  1028. my $deviceName = $deviceHash->{NAME};
  1029. my $gadName = $deviceHash->{GADTABLE}{$gadCode};
  1030. my $model = $deviceHash->{GADDETAILS}{$gadName}{MODEL};
  1031. my $option = $deviceHash->{GADDETAILS}{$gadName}{OPTION};
  1032. my $rdString = $deviceHash->{GADDETAILS}{$gadName}{RDNAMEGET};
  1033. my $putString = $deviceHash->{GADDETAILS}{$gadName}{RDNAMEPUT};
  1034. Log3 ($deviceName, 5, "parse: process message, device-name: $deviceName, rd-name: $gadName, gadCode: $gadCode, model: $model");
  1035. #########################
  1036. #process message
  1037. #
  1038. #handle write and reply messages
  1039. if ($cmd =~ /[w|p]/i)
  1040. {
  1041. #decode message
  1042. my $transval = KNX_decodeByDpt ($deviceHash, $val, $gadName);
  1043. #message invalid
  1044. if (not defined($transval) or ($transval eq ""))
  1045. {
  1046. readingsSingleUpdate($deviceHash, "last-sender", KNX_hexToName($src), 1);
  1047. Log3 ($deviceName, 2, "parse device hash (wpi): $deviceHash name: $deviceName, message could not be decoded - see log for details");
  1048. next;
  1049. }
  1050. Log3 ($deviceName, 5, "received hash (wpi): $deviceHash name: $deviceName, STATE: $transval, READING: $gadName, SENDER: $src");
  1051. #append post-string, if supplied
  1052. my $suffix = AttrVal($deviceName, "format",undef);
  1053. $transval = $transval . " " . $suffix if (defined($suffix));
  1054. #execute regex, if defined
  1055. my $regAttr = AttrVal($deviceName, "stateRegex", undef);
  1056. my $state = KNX_replaceByRegex ($regAttr, $rdString . ":", $transval);
  1057. Log3 ($deviceName, 5, "parse device hash (wpi): $deviceHash name: $deviceName - replaced $rdString:$transval to $state") if (not ($transval eq $state));
  1058. if (defined($state))
  1059. {
  1060. readingsBeginUpdate($deviceHash);
  1061. readingsBulkUpdate($deviceHash, $rdString, $transval);
  1062. readingsBulkUpdate($deviceHash, "last-sender", KNX_hexToName($src));
  1063. #execute state-command if defined
  1064. #must be placed after first readings, because it may have a reference
  1065. #
  1066. #hack for being backward compatible - serve $name
  1067. $name = $deviceName;
  1068. my $cmdAttr = AttrVal($deviceName, "stateCmd", undef);
  1069. if (defined ($cmdAttr) and !($cmdAttr eq ""))
  1070. {
  1071. $state = KNX_eval ($deviceHash, $gadName, $state, $cmdAttr);
  1072. Log3 ($deviceName, 5, "parse device hash (wpi): $deviceHash name: $deviceName - state replaced via command $cmdAttr - state: $state");
  1073. }
  1074. #reassign original name...
  1075. $name = $hash->{NAME};
  1076. readingsBulkUpdate($deviceHash, "state", $state);
  1077. readingsEndUpdate($deviceHash, 1);
  1078. }
  1079. }
  1080. #handle read messages
  1081. elsif ($cmd =~ /[r]/)
  1082. {
  1083. if (defined ($option) and ($option =~ m/listenonly/i))
  1084. {
  1085. Log3 ($deviceName, 5, "received hash (r), ignored request due to option \"listenonly\"");
  1086. next;
  1087. }
  1088. Log3 ($deviceName, 5, "received hash (r): $deviceHash name: $deviceName, GET");
  1089. my $transval = undef;
  1090. #answer "old school"
  1091. my $value = undef;
  1092. if (AttrVal($deviceName, "answerReading", 0) =~ m/1/)
  1093. {
  1094. my $putVal = ReadingsVal($deviceName, "putString", undef);
  1095. if (defined ($putVal) and !($putVal eq ""))
  1096. {
  1097. #medium priority, overwrite $value
  1098. $value = $putVal;
  1099. }
  1100. else
  1101. {
  1102. #lowest priority - use state
  1103. $value = ReadingsVal($deviceName, "state", undef) if (AttrVal($deviceName, "answerReading", 0) =~ m/1/);
  1104. }
  1105. }
  1106. #high priority - eval
  1107. ###
  1108. my $cmdAttr = AttrVal($deviceName, "putCmd", undef);
  1109. if (defined ($cmdAttr) and !($cmdAttr eq ""))
  1110. {
  1111. my $orgValue = $value;
  1112. $value = KNX_eval ($deviceHash, $gadName, $value, $cmdAttr);
  1113. #if ($orgValue ne $value)
  1114. #try to fix: answer only, if eval was successful
  1115. if (($orgValue ne $value) and ($value !~ m/ERROR/i))
  1116. {
  1117. Log3 ($deviceName, 5, "parse device hash (r): $deviceHash name: $deviceName - put replaced via command $cmdAttr - value: $value");
  1118. readingsSingleUpdate($deviceHash, $putString, $value,1);
  1119. }
  1120. }
  1121. ###/
  1122. #send transval
  1123. if (defined($value))
  1124. {
  1125. $transval = KNX_encodeByDpt($deviceHash, $value, $gadName);
  1126. Log3 ($deviceName, 5, "received, send answer hash: $deviceHash name: $deviceName, GET: $transval, READING: $gadName");
  1127. IOWrite ($deviceHash, "B", "p" . $gadCode . $transval);
  1128. }
  1129. }
  1130. #skip, if this is ignored
  1131. next if (IsIgnored($deviceName));
  1132. #save to list
  1133. push(@foundMsgs, $deviceName);
  1134. #
  1135. #/process message
  1136. #########################
  1137. }
  1138. Log3 ($name, 5, "exit parse");
  1139. #return values
  1140. return @foundMsgs;
  1141. }
  1142. #Function is called at every notify
  1143. #############################
  1144. sub
  1145. KNX_Notify($$)
  1146. {
  1147. my ($ownHash, $callHash) = @_;
  1148. #own name / hash
  1149. my $ownName = $ownHash->{NAME};
  1150. #Device that created the events
  1151. my $callName = $callHash->{NAME};
  1152. return undef;
  1153. }
  1154. #Private function to convert GAD from hex to readable version
  1155. #############################
  1156. sub
  1157. KNX_hexToName ($)
  1158. {
  1159. my $v = shift;
  1160. #old syntax
  1161. #my $p1 = hex(substr($v,0,1));
  1162. #my $p2 = hex(substr($v,1,1));
  1163. #my $p3 = hex(substr($v,2,2));
  1164. #new syntax for extended adressing
  1165. my $p1 = hex(substr($v,0,2));
  1166. my $p2 = hex(substr($v,2,1));
  1167. my $p3 = hex(substr($v,3,2));
  1168. my $r = sprintf("%d/%d/%d", $p1,$p2,$p3);
  1169. return $r;
  1170. }
  1171. #Private function to convert GAD from readable version to hex
  1172. #############################
  1173. sub
  1174. KNX_nameToHex ($)
  1175. {
  1176. my $v = shift;
  1177. my $r = $v;
  1178. if($v =~ /^([0-9]{1,2})\/([0-9]{1,2})\/([0-9]{1,3})$/)
  1179. {
  1180. #old syntax
  1181. #$r = sprintf("%01x%01x%02x",$1,$2,$3);
  1182. #new syntax for extended adressing
  1183. $r = sprintf("%02x%01x%02x",$1,$2,$3);
  1184. }
  1185. #elsif($v =~ /^([0-9]{1,2})\.([0-9]{1,2})\.([0-9]{1,3})$/)
  1186. #{
  1187. # $r = sprintf("%01x%01x%02x",$1,$2,$3);
  1188. #}
  1189. return $r;
  1190. }
  1191. #Private function to clean input string according DPT
  1192. #############################
  1193. sub
  1194. KNX_checkAndClean ($$$)
  1195. {
  1196. my ($hash, $value, $gadName) = @_;
  1197. my $name = $hash->{NAME};
  1198. my $orgValue = $value;
  1199. Log3 ($name, 5, "check value: $value, gadName: $gadName");
  1200. #get model
  1201. my $model = $hash->{GADDETAILS}{$gadName}{MODEL};
  1202. #return unchecked, if this is a autocreate-device
  1203. return $value if ($model eq $modelErr);
  1204. #get pattern
  1205. my $pattern = $dpttypes{$model}{PATTERN};
  1206. #trim whitespaces at the end
  1207. $value =~ s/^\s+|\s+$//gi;
  1208. #match against model pattern
  1209. my @tmp = ($value =~ m/$pattern/gi);
  1210. #loop through results
  1211. my $found = 0;
  1212. foreach my $str (@tmp)
  1213. {
  1214. #assign first match and exit loop
  1215. if (defined($str))
  1216. {
  1217. $found = 1;
  1218. $value = $str;
  1219. last;
  1220. }
  1221. }
  1222. return undef if ($found == 0);
  1223. #get min
  1224. #my $min = $dpttypes{"$model"}{MIN};
  1225. #if min is numeric, cast to min
  1226. #$value = $min if (defined ($min) and ($min =~ /^[+-]?\d*[.,]?\d+/) and ($value < $min));
  1227. #get max
  1228. #my $max = $dpttypes{"$model"}{MAX};
  1229. #if max is numeric, cast to max
  1230. #$value = $max if (defined ($max) and ($max =~ /^[+-]?\d*[.,]?\d+/) and ($value > $max));
  1231. $value = KNX_limit ($hash, $value, $gadName, undef);
  1232. Log3 ($name, 3, "check value: input-value $orgValue was casted to $value") if (not($orgValue eq $value));
  1233. Log3 ($name, 5, "check value: $value, gadName: $gadName, model: $model, pattern: $pattern");
  1234. return $value;
  1235. }
  1236. #Private function to replace state-values
  1237. #############################
  1238. sub
  1239. KNX_replaceByRegex ($$$) {
  1240. my ($regAttr, $prefix, $input) = @_;
  1241. my $retVal = $input;
  1242. #execute regex, if defined
  1243. if (defined($regAttr))
  1244. {
  1245. #get array of given attributes
  1246. my @reg = split(" /", $regAttr);
  1247. my $tempVal = $prefix . $input;
  1248. #loop over all regex
  1249. foreach my $regex (@reg)
  1250. {
  1251. #trim leading and trailing slashes
  1252. $regex =~ s/^\/|\/$//gi;
  1253. #get pairs
  1254. my @regPair = split("\/", $regex);
  1255. #skip if not at least 2 values supplied
  1256. #next if (int(@regPair < 2));
  1257. next if (not defined($regPair[0]));
  1258. if (not defined ($regPair[1]))
  1259. {
  1260. #cut value
  1261. $tempVal =~ s/$regPair[0]//gi;
  1262. }
  1263. else
  1264. {
  1265. #replace value
  1266. $tempVal =~ s/$regPair[0]/$regPair[1]/gi;
  1267. }
  1268. #restore value
  1269. $retVal = $tempVal;
  1270. }
  1271. }
  1272. return $retVal;
  1273. }
  1274. #Private function to limit numeric values. Valid directions: encode, decode
  1275. #############################
  1276. sub
  1277. KNX_limit ($$$$) {
  1278. my ($hash, $value, $gadName, $direction) = @_;
  1279. my $name = $hash->{NAME};
  1280. my $model = $hash->{GADDETAILS}{$gadName}{MODEL};
  1281. my $retVal = $value;
  1282. #get correction details
  1283. my $factor = $dpttypes{$model}{FACTOR};
  1284. my $offset = $dpttypes{$model}{OFFSET};
  1285. #get limits
  1286. my $min = $dpttypes{$model}{MIN};
  1287. my $max = $dpttypes{$model}{MAX};
  1288. #only execute, if nummeric value
  1289. if (looks_like_number ($value))
  1290. {
  1291. #determine direction of scaling, do only if defined
  1292. if (defined ($direction))
  1293. {
  1294. if ($direction =~ m/^encode/i)
  1295. {
  1296. #limitValue
  1297. $retVal = $min if (defined ($min) and ($retVal < $min));
  1298. $retVal = $max if (defined ($max) and ($retVal > $max));
  1299. #correct value
  1300. $retVal /= $factor if (defined ($factor));
  1301. $retVal -= $offset if (defined ($offset));
  1302. }
  1303. elsif ($direction =~ m/^decode/i)
  1304. {
  1305. #correct value
  1306. $retVal += $offset if (defined ($offset));
  1307. $retVal *= $factor if (defined ($factor));
  1308. #limitValue
  1309. $retVal = $min if (defined ($min) and ($retVal < $min));
  1310. $retVal = $max if (defined ($max) and ($retVal > $max));
  1311. }
  1312. my $logString = "limit:";
  1313. $logString .= " DIR: $direction" if (defined ($direction));
  1314. $logString .= " FACTOR: $factor" if (defined ($factor));
  1315. $logString .= " OFFSET: $factor" if (defined ($offset));
  1316. $logString .= " MIN: $factor" if (defined ($min));
  1317. $logString .= " MAX: $factor" if (defined ($max));
  1318. Log3 ($name, 5, $logString);
  1319. Log3 ($name, 5, "limit: modified... Output: $retVal, Input: $value, Model: $model") if ($retVal != $value);
  1320. }
  1321. }
  1322. else
  1323. {
  1324. #Log3 ($name, 2, "limit: did not execute any action. Value should be numeric, but wasn't...");
  1325. }
  1326. return $retVal;
  1327. }
  1328. #Private function to encode KNX-Message according DPT
  1329. #############################
  1330. sub
  1331. KNX_eval ($$$$) {
  1332. my ($hash, $gadName, $state, $evalString) = @_;
  1333. my $name = $hash->{NAME};
  1334. my $retVal = undef;
  1335. Log3 ($name, 5, "Enter eval...name: $name, gadName: $gadName, evalString: \n$evalString\n");
  1336. $retVal = eval $evalString;
  1337. Log3 ($name, 2, "set name: Eval error - $@") if $@;
  1338. Log3 ($name, 5, "Exit eval...result: $retVal");
  1339. $retVal = "ERROR" if (not defined ($retVal));
  1340. return $retVal;
  1341. }
  1342. #Private function to encode KNX-Message according DPT
  1343. #############################
  1344. sub
  1345. KNX_encodeByDpt ($$$) {
  1346. my ($hash, $value, $gadName) = @_;
  1347. my $name = $hash->{NAME};
  1348. Log3 ($name, 5, "encode value: $value, gadName: $gadName");
  1349. #get model
  1350. my $model = $hash->{GADDETAILS}{$gadName}{MODEL};
  1351. my $code = $dpttypes{$model}{CODE};
  1352. #return unchecked, if this is a autocreate-device
  1353. return undef if ($model eq $modelErr);
  1354. #this one stores the translated value (readble)
  1355. my $numval = undef;
  1356. #this one stores the translated hex-value
  1357. my $hexval = undef;
  1358. Log3 ($name, 5, "encode model: $model, code: $code, value: $value");
  1359. $value = KNX_limit ($hash, $value, $gadName, "ENCODE");
  1360. #Binary value
  1361. if ($code eq "dpt1")
  1362. {
  1363. $numval = "00" if ($value eq 0);
  1364. $numval = "01" if ($value eq 1);
  1365. $numval = "00" if ($value =~ m/off$/i);
  1366. $numval = "01" if ($value =~ m/on$/i);
  1367. $numval = "00" if ($value eq $dpttypes{$model}{MIN});
  1368. $numval = "01" if ($value eq $dpttypes{$model}{MAX});
  1369. $hexval = $numval;
  1370. }
  1371. #Step value (two-bit)
  1372. elsif ($code eq "dpt2")
  1373. {
  1374. $numval = "00" if ($value =~ m/off/i);
  1375. $numval = "01" if ($value =~ m/on/i);
  1376. $numval = "02" if ($value =~ m/forceoff/i);
  1377. $numval = "03" if ($value =~ m/forceon/i);
  1378. $hexval = $numval;
  1379. }
  1380. #Step value (four-bit)
  1381. elsif ($code eq "dpt3")
  1382. {
  1383. $numval = 0;
  1384. #get dim-direction
  1385. my $sign = 0;
  1386. $sign = 1 if ($value >= 0);
  1387. #trim sign
  1388. $value =~ s/^-//g;
  1389. #get dim-value
  1390. $numval = 7 if ($value >= 1);
  1391. $numval = 6 if ($value >= 3);
  1392. $numval = 5 if ($value >= 6);
  1393. $numval = 4 if ($value >= 12);
  1394. $numval = 3 if ($value >= 25);
  1395. $numval = 2 if ($value >= 50);
  1396. $numval = 1 if ($value >= 75);
  1397. #assign dim direction
  1398. $numval += 8 if ($sign == 1);
  1399. #get hex representation
  1400. $hexval = sprintf("%.2x",$numval);
  1401. }
  1402. #1-Octet unsigned value
  1403. elsif ($code eq "dpt5")
  1404. {
  1405. $numval = $value;
  1406. $hexval = sprintf("00%.2x",($numval));
  1407. }
  1408. #1-Octet signed value
  1409. elsif ($code eq "dpt6")
  1410. {
  1411. #build 2-complement
  1412. $numval = $value;
  1413. $numval += 0x100 if ($numval < 0);
  1414. $numval = 0x00 if ($numval < 0x00);
  1415. $numval = 0xFF if ($numval > 0xFF);
  1416. #get hex representation
  1417. $hexval = sprintf("00%.2x",$numval);
  1418. }
  1419. #2-Octet unsigned Value
  1420. elsif ($code eq "dpt7")
  1421. {
  1422. $numval = $value;
  1423. $hexval = sprintf("00%.4x",($numval));
  1424. }
  1425. #2-Octet signed Value
  1426. elsif ($code eq "dpt8")
  1427. {
  1428. #build 2-complement
  1429. $numval = $value;
  1430. $numval += 0x10000 if ($numval < 0);
  1431. $numval = 0x00 if ($numval < 0x00);
  1432. $numval = 0xFFFF if ($numval > 0xFFFF);
  1433. #get hex representation
  1434. $hexval = sprintf("00%.4x",$numval);
  1435. }
  1436. #2-Octet Float value
  1437. elsif ($code eq "dpt9")
  1438. {
  1439. my $sign = ($value <0 ? 0x8000 : 0);
  1440. my $exp = 0;
  1441. my $mant = 0;
  1442. $mant = int($value * 100.0);
  1443. while (abs($mant) > 0x7FF)
  1444. {
  1445. $mant /= 2;
  1446. $exp++;
  1447. }
  1448. $numval = $sign | ($exp << 11) | ($mant & 0x07ff);
  1449. #get hex representation
  1450. $hexval = sprintf("00%.4x",$numval);
  1451. }
  1452. #Time of Day
  1453. elsif ($code eq "dpt10")
  1454. {
  1455. if ($value =~ m/now/i)
  1456. {
  1457. #get actual time
  1458. my ($secs,$mins,$hours,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  1459. my $hoffset;
  1460. #add offsets
  1461. $year+=1900;
  1462. $mon++;
  1463. # calculate offset for weekday
  1464. $wday = 7 if ($wday eq "0");
  1465. $hoffset = 32*$wday;
  1466. $hours += $hoffset;
  1467. $value = "$hours:$mins:$secs";
  1468. $numval = $secs + ($mins<<8) + ($hours<<16);
  1469. } else
  1470. {
  1471. my ($hh, $mm, $ss) = split (":", $value);
  1472. $numval = $ss + ($mm<<8) + (($hh)<<16);
  1473. }
  1474. #get hex representation
  1475. $hexval = sprintf("00%.6x",$numval);
  1476. }
  1477. #Date
  1478. elsif ($code eq "dpt11")
  1479. {
  1480. if ($value =~ m/now/i)
  1481. {
  1482. #get actual time
  1483. my ($secs,$mins,$hours,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  1484. my $hoffset;
  1485. #add offsets
  1486. $year+=1900;
  1487. $mon++;
  1488. # calculate offset for weekday
  1489. $wday = 7 if ($wday eq "0");
  1490. $value = "$mday.$mon.$year";
  1491. $numval = ($year - 2000) + ($mon<<8) + ($mday<<16);
  1492. } else
  1493. {
  1494. my ($dd, $mm, $yyyy) = split (/\./, $value);
  1495. if ($yyyy >= 2000)
  1496. {
  1497. $yyyy -= 2000;
  1498. } else
  1499. {
  1500. $yyyy -= 1900;
  1501. }
  1502. $numval = ($yyyy) + ($mm<<8) + ($dd<<16);
  1503. }
  1504. #get hex representation
  1505. $hexval = sprintf("00%.6x",$numval);
  1506. }
  1507. #4-Octet unsigned value (handled as dpt7)
  1508. elsif ($code eq "dpt12")
  1509. {
  1510. $numval = $value;
  1511. $hexval = sprintf("00%.8x",($numval));
  1512. }
  1513. #4-Octet Signed Value
  1514. elsif ($code eq "dpt13")
  1515. {
  1516. #build 2-complement
  1517. $numval = $value;
  1518. $numval += 4294967296 if ($numval < 0);
  1519. $numval = 0x00 if ($numval < 0x00);
  1520. $numval = 0xFFFFFFFF if ($numval > 0xFFFFFFFF);
  1521. #get hex representation
  1522. $hexval = sprintf("00%.8x",$numval);
  1523. }
  1524. #4-Octet single precision float
  1525. elsif ($code eq "dpt14")
  1526. {
  1527. $numval = unpack("L", pack("f", $value));
  1528. #get hex representation
  1529. $hexval = sprintf("00%.8x",$numval);
  1530. }
  1531. #14-Octet String
  1532. elsif ($code eq "dpt16")
  1533. {
  1534. #convert to latin-1
  1535. $value = encode("iso-8859-1", decode("utf8", $value));
  1536. #convert to hex-string
  1537. my $dat = unpack "H*", $value;
  1538. #format for 14-byte-length
  1539. $dat = sprintf("%-028s",$dat);
  1540. #append leading zeros
  1541. $dat = "00" . $dat;
  1542. $numval = $value;
  1543. $hexval = $dat;
  1544. }
  1545. #DateTime
  1546. elsif ($code eq "dpt19")
  1547. {
  1548. if ($value =~ m/now/i)
  1549. {
  1550. #get actual time
  1551. my ($secs,$mins,$hours,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  1552. my $hoffset;
  1553. #add offsets
  1554. #$year+=1900;
  1555. $mon++;
  1556. # calculate offset for weekday
  1557. $wday = 7 if ($wday eq "0");
  1558. $hexval = 0;
  1559. $hexval = sprintf ("00%.16x", (($secs<<16) + ($mins<<24) + ($hours<<32) + ($mday<<40) + ($mon<<48) + ($year<<56)));
  1560. } else
  1561. {
  1562. my ($date, $time) = split ('_', $value);
  1563. my ($dd, $mm, $yyyy) = split (/\./, $date);
  1564. my ($hh, $mi, $ss) = split (':', $time);
  1565. #add offsets
  1566. $yyyy -= 1900; # year is based on 1900
  1567. my $wday = 0;
  1568. $hexval = 0;
  1569. $hexval = sprintf ("00%.16x", (($ss<<16) + ($mi<<24) + ($hh<<32) + ($dd<<40) + ($mm<<48) + ($yyyy<<56)));
  1570. }
  1571. $numval = 0;
  1572. }
  1573. #RGB-Code
  1574. elsif ($code eq "dpt232")
  1575. {
  1576. $hexval = "00" . $value;
  1577. $numval = $value;
  1578. }
  1579. else
  1580. {
  1581. Log3 ($name, 2, "encode model: $model, no valid model defined");
  1582. return undef;
  1583. }
  1584. Log3 ($name, 5, "encode model: $model, code: $code, value: $value, numval: $numval, hexval: $hexval");
  1585. return $hexval;
  1586. }
  1587. #Private function to decode KNX-Message according DPT
  1588. #############################
  1589. sub
  1590. KNX_decodeByDpt ($$$) {
  1591. my ($hash, $value, $gadName) = @_;
  1592. my $name = $hash->{NAME};
  1593. Log3 ($name, 5, "decode value: $value, gadName: $gadName");
  1594. #get model
  1595. my $model = $hash->{GADDETAILS}{$gadName}{MODEL};
  1596. my $code = $dpttypes{$model}{CODE};
  1597. #return unchecked, if this is a autocreate-device
  1598. return undef if ($model eq $modelErr);
  1599. #this one stores the translated value (readble)
  1600. my $numval = undef;
  1601. #this one contains the return-value
  1602. my $state = undef;
  1603. Log3 ($name, 5, "decode model: $model, code: $code, value: $value");
  1604. my $min = $dpttypes{"$model"}{MIN};
  1605. my $max = $dpttypes{"$model"}{MAX};
  1606. #Binary value
  1607. if ($code eq "dpt1")
  1608. {
  1609. $numval = $min if ($value =~ m/00/i);
  1610. $numval = $max if ($value =~ m/01/i);
  1611. $state = $numval;
  1612. }
  1613. #Step value (two-bit)
  1614. elsif ($code eq "dpt2")
  1615. {
  1616. #get numeric value
  1617. $numval = hex ($value);
  1618. $state = "off" if ($numval == 0);
  1619. $state = "on" if ($numval == 1);
  1620. $state = "forceOff" if ($numval == 2);
  1621. $state = "forceOn" if ($numval == 3);
  1622. }
  1623. #Step value (four-bit)
  1624. elsif ($code eq "dpt3")
  1625. {
  1626. #get numeric value
  1627. $numval = hex ($value);
  1628. #get dim-direction
  1629. my $sign = 1;
  1630. if ($numval >= 8)
  1631. {
  1632. $sign = 0;
  1633. $numval -= 8;
  1634. }
  1635. $state = 100 if ($numval >= 1);
  1636. $state = 50 if ($numval >= 2);
  1637. $state = 25 if ($numval >= 3);
  1638. $state = 12 if ($numval >= 4);
  1639. $state = 6 if ($numval >= 5);
  1640. $state = 3 if ($numval >= 6);
  1641. $state = 1 if ($numval >= 7);
  1642. $state = 0 - $state if ($sign == 1);
  1643. $state = sprintf ("%.0f", $state);
  1644. }
  1645. #1-Octet unsigned value
  1646. elsif ($code eq "dpt5")
  1647. {
  1648. $numval = hex ($value);
  1649. $state = KNX_limit ($hash, $numval, $gadName, "DECODE");
  1650. $state = sprintf ("%.0f", $state);
  1651. }
  1652. #1-Octet signed value
  1653. elsif ($code eq "dpt6")
  1654. {
  1655. $numval = hex ($value);
  1656. $numval -= 0x100 if ($numval >= 0x80);
  1657. $state = KNX_limit ($hash, $numval, $gadName, "DECODE");
  1658. $state = sprintf ("%.0f", $state);
  1659. }
  1660. #2-Octet unsigned Value
  1661. elsif ($code eq "dpt7")
  1662. {
  1663. $numval = hex ($value);
  1664. $state = KNX_limit ($hash, $numval, $gadName, "DECODE");
  1665. $state = sprintf ("%.0f", $state);
  1666. }
  1667. #2-Octet signed Value
  1668. elsif ($code eq "dpt8")
  1669. {
  1670. $numval = hex ($value);
  1671. $numval -= 0x10000 if ($numval >= 0x8000);
  1672. $state = KNX_limit ($hash, $numval, $gadName, "DECODE");
  1673. $state = sprintf ("%.0f", $state);
  1674. }
  1675. #2-Octet Float value
  1676. elsif ($code eq "dpt9")
  1677. {
  1678. $numval = hex($value);
  1679. my $sign = 1;
  1680. $sign = -1 if(($numval & 0x8000) > 0);
  1681. my $exp = ($numval & 0x7800) >> 11;
  1682. my $mant = ($numval & 0x07FF);
  1683. $mant = -(~($mant-1) & 0x07FF) if($sign == -1);
  1684. $numval = (1 << $exp) * 0.01 * $mant;
  1685. $numval = KNX_limit ($hash, $numval, $gadName, "DECODE");
  1686. $state = sprintf ("%.2f","$numval");
  1687. }
  1688. #Time of Day
  1689. elsif ($code eq "dpt10")
  1690. {
  1691. $numval = hex($value);
  1692. my $hours = ($numval & 0x1F0000)>>16;
  1693. my $mins = ($numval & 0x3F00)>>8;
  1694. my $secs = ($numval & 0x3F);
  1695. $state = sprintf("%02d:%02d:%02d",$hours,$mins,$secs);
  1696. }
  1697. #Date
  1698. elsif ($code eq "dpt11")
  1699. {
  1700. $numval = hex($value);
  1701. my $day = ($numval & 0x1F0000) >> 16;
  1702. my $month = ($numval & 0x0F00) >> 8;
  1703. my $year = ($numval & 0x7F);
  1704. #translate year (21st cent if <90 / else 20th century)
  1705. $year += 1900 if($year >= 90);
  1706. $year += 2000 if($year < 90);
  1707. $state = sprintf("%02d.%02d.%04d",$day,$month,$year);
  1708. }
  1709. #4-Octet unsigned value (handled as dpt7)
  1710. elsif ($code eq "dpt12")
  1711. {
  1712. $numval = hex ($value);
  1713. $state = KNX_limit ($hash, $numval, $gadName, "DECODE");
  1714. $state = sprintf ("%.0f", $state);
  1715. }
  1716. #4-Octet Signed Value
  1717. elsif ($code eq "dpt13")
  1718. {
  1719. $numval = hex ($value);
  1720. $numval -= 4294967296 if ($numval >= 0x80000000);
  1721. $state = KNX_limit ($hash, $numval, $gadName, "DECODE");
  1722. $state = sprintf ("%.0f", $state);
  1723. }
  1724. #4-Octet single precision float
  1725. elsif ($code eq "dpt14")
  1726. {
  1727. $numval = unpack "f", pack "L", hex ($value);
  1728. $numval = KNX_limit ($hash, $numval, $gadName, "DECODE");
  1729. $state = sprintf ("%.3f","$numval");
  1730. }
  1731. #14-Octet String
  1732. elsif ($code eq "dpt16")
  1733. {
  1734. $numval = 0;
  1735. $state = "";
  1736. for (my $i = 0; $i < 14; $i++)
  1737. {
  1738. my $c = hex(substr($value, $i * 2, 2));
  1739. #exit at string terminator, otherwise append current char
  1740. if (($i != 0) and ($c eq 0))
  1741. {
  1742. $i = 14;
  1743. }
  1744. else
  1745. {
  1746. $state .= sprintf("%c", $c);
  1747. }
  1748. }
  1749. #convert to latin-1
  1750. $state = encode ("utf8", $state) if ($model =~ m/16.001/);
  1751. #remove non printable chars
  1752. $state =~ s/[\x00-\x1F]+//g;
  1753. }
  1754. #DateTime
  1755. elsif ($code eq "dpt19")
  1756. {
  1757. $numval = $value;
  1758. my $time = hex (substr ($numval, 8, 6));
  1759. my $date = hex (substr ($numval, 2, 6));
  1760. my $secs = ($time & 0x3F) >> 0;
  1761. my $mins = ($time & 0x3F00) >> 8;
  1762. my $hours = ($time & 0x1F0000) >> 16;
  1763. #my $day = ($date & 0x1F) >> 0;
  1764. #my $month = ($date & 0x0F00) >> 8;
  1765. #my $year = ($date & 0xFFFF0000) >> 16;
  1766. my $day = ($date & 0xFF) >> 0;
  1767. my $month = ($date & 0xFF00) >> 8;
  1768. my $year = ($date & 0xFF0000) >> 16;
  1769. $year += 1900;
  1770. $state = sprintf("%02d.%02d.%04d_%02d:%02d:%02d", $day, $month, $year, $hours, $mins, $secs);
  1771. }
  1772. #RGB-Code
  1773. elsif ($code eq "dpt232")
  1774. {
  1775. $numval = hex ($value);
  1776. $state = $numval;
  1777. $state = sprintf ("%.6x", $state);
  1778. }
  1779. else
  1780. {
  1781. Log3 ($name, 2, "decode model: $model, no valid model defined");
  1782. return undef;
  1783. }
  1784. #append unit, if supplied
  1785. my $unit = $dpttypes{$model}{UNIT};
  1786. $state = $state . " " . $unit if (defined ($unit) and not($unit eq ""));
  1787. Log3 ($name, 5, "decode model: $model, code: $code, value: $value, numval: $numval, state: $state");
  1788. return $state;
  1789. }
  1790. 1;
  1791. =pod
  1792. =begin html
  1793. <p><a name="KNX"></a></p>
  1794. <h3>KNX</h3>
  1795. <p>KNX is a standard for building automation / home automation. It is mainly based on a twisted pair wiring, but also other mediums (ip, wireless) are specified.</p>
  1796. <p>For getting started, please refer to this document: <a href="https://www2.knx.org/media/docs/downloads/Marketing/Flyers/KNX-Basics/KNX-Basics_en.pdf">KNX-Basics</a></p>
  1797. <p>While the module <a href="#TUL">TUL</a> represents the connection to the KNX network, the KNX modules represent individual KNX devices. <br />
  1798. This module provides a basic set of operations (on, off, toggle, on-until, on-for-timer) to switch on/off KNX devices and to send values to the bus.&nbsp;</p>
  1799. <p>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>
  1800. <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. These datatypes are used to interpret the status of a device, so the state in FHEM will then show the correct value.</p>
  1801. <p>For each received telegram there will be a reading with containing the received value and the sender address.<br />
  1802. For every set, there will be a reading containing the sent value.<br />
  1803. The reading &lt;state&gt; will be updated with the last sent or received value.&nbsp;</p>
  1804. <p>&nbsp;</p>
  1805. <p><strong>Define</strong></p>
  1806. <p><code>d</code><code>efine &lt;name&gt; KNX &lt;group&gt;:&lt;DPT&gt;:[gadName]:[set|get|listenonly]:[nosuffix] [&lt;group&gt;:&lt;DPT&gt; ..] [IODev]</code></p>
  1807. <p><strong>Important:&nbsp;a KNX device needs at least one&nbsp;concrete DPT. Please refer to <a href="#KNXdpt">available DPT</a>. Otherwise the system cannot en- or decode the messages.</strong><br />
  1808. <strong>Devices defined by autocreate have to be reworked with the suitable dpt. Otherwise they won't be able to work productively.</strong></p>
  1809. <strong>Unless older versions the on-off command and the clickable bulb are not shown in the webfrontend out-of-the box. You have to add webCmd and devStateIcon. See examples...</strong></p>
  1810. <p>Examples:</p>
  1811. <pre style="padding-left: 30px;">define lamp1 KNX 0/10/11:dpt1:listenonly</pre>
  1812. <pre style="padding-left: 30px;">arr lamp1 webCmd on:off</pre>
  1813. <pre style="padding-left: 30px;">attr lamp1 devStateIcon on::off off::on</pre>
  1814. <br>
  1815. <pre style="padding-left: 30px;">define lamp2 KNX 0/10/12:dpt1:steuern 0/10/13:dpt1.001:status</pre>
  1816. <pre style="padding-left: 30px;">define lamp3 KNX 0A0D:dpt1.003 myTul</pre>
  1817. <p>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). All of the defined groups can be used for bus-communication. It is not allowed to have the same group more then once in one device. You can have several devices containing the same adresses.<br />
  1818. As described above the parameter &lt;DPT&gt; must contain the corresponding DPT.<br />
  1819. The optional parameteter [gadName] may contain an alias for the GAD.&nbsp;The name must not cotain one of the following strings: on, off, on-for-timer, on-until, off-for-timer, off-until, toggle, raw, rgb, string, value, set, get, listenonly.<br />
  1820. Especially if answerReading is set to 1, it might be useful to modifiy the behaviour of single GADs. If you want to restrict the GAD, you can raise the flags "get", "set", or "listenonly". The usage should be self-explainable. It is not possible to combine the flags.<br />
  1821. Furthermore you can supply a IO-Device directly at startup. This can be done later on via attribute as well.</p>
  1822. <p>The GAD's are per default named with "g&lt;number&gt;". The correspunding readings are calles getG&lt;number&gt;, setG&lt;number&gt; and putG&lt;number&gt;.<br />
  1823. If you supply &lt;gadName&gt; this name is used instead. The readings are &lt;gadName&gt;-get, &lt;gadName&gt;-set and &lt;gadName&gt;-put. We will use the synonyms &lt;getName&gt;, &lt;setName&gt; and &lt;putName&gt; in this documentation.
  1824. If you add the option "nosuffix", &lt;getName&gt;, &lt;setName&gt; and &lt;putName&gt; have the identical name - only &lt;gadName&gt;.</p>
  1825. <p>Per default, the first group is used for sending. If you want to send via a different group, you have to address it it (set &lt;name&gt; &lt;gadName&gt; value).</p>
  1826. <p>Without further attributes, all incoming and outgoing messages are translated into reading &lt;state&gt;.</p>
  1827. <p>If enabled, 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 KNX_nnmmooo where nn is the line adress, mm the area and ooo the device.</p>
  1828. <p>&nbsp;</p>
  1829. <p><strong>Set</strong></p>
  1830. <p><code>set &lt;deviceName&gt; [gadName] &lt;on|off|toggle&gt;<br />
  1831. set &lt;deviceName&gt; [gadName] &lt;on-for-timer|on-until|off-for-timer|off-until&gt;
  1832. &lt;timespec&gt;<br />
  1833. set &lt;deviceName&gt; [gadName] &lt;value&gt;<br /></code></p>
  1834. <p>Set sends the given value to the bus.<br /> If &lt;gadName&gt; is omitted, the first listed GAD of the device is used. If the GAD is restricted in the definition with "get" or "listenonly", the set-command will be refused.<br />
  1835. <strong>For dpt1 and dpt1.001 valid values are on, off and toggle. Also the timer-functions can be used. For all other binary DPT (dpt1.xxx) the min- and max-values can be used for en- and decoding alternatively to on/off. This is different to older versions.</strong><br />
  1836. After successful sending the value, it is stored in the readings &lt;setName&gt;.</p>
  1837. <p>Example:</p>
  1838. <p><code></code></p>
  1839. <pre style="padding-left: 30px;">set lamp2 on</pre>
  1840. <pre style="padding-left: 30px;">set lamp2 off</pre>
  1841. <pre style="padding-left: 30px;">set lamp2 on-for-timer 10</pre>
  1842. <pre style="padding-left: 30px;">set lamp2 on-until 13:15:00</pre>
  1843. <pre style="padding-left: 30px;">set lamp2 steuern on-until 13:15:00</pre>
  1844. <pre style="padding-left: 30px;">set myThermoDev 23.44</pre>
  1845. <pre style="padding-left: 30px;">set my MessageDev Hallo Welt</pre>
  1846. <p>&nbsp;</p>
  1847. <p><strong>Get</strong></p>
  1848. <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 />
  1849. If the GAD is restricted in the definition with "listenonly", the execution will be refused.<br />
  1850. The answer from the bus-device is not shown in the toolbox, but is treated like a regular telegram.</p>
  1851. <p>&nbsp;</p>
  1852. <p><strong>Common attributes</strong></p>
  1853. <p><a href="#DbLogInclude">DbLogInclude</a><br />
  1854. <a href="#DbLogExclude">DbLogExclude</a><br /><a href="#IODev">IODev</a><br />
  1855. <a href="#alias">alias</a><br /> <a href="#alias">attributesExclude</a><br />
  1856. <a href="#alias">cmdIcon</a><br /> <a href="#comment">comment</a><br />
  1857. <a href="#devStateIcon">devStateIcon</a><br />
  1858. <a href="#devStateStyle">devStateStyle</a><br />
  1859. <a href="#do_not_notify">do_not_notify</a><br />
  1860. <a href="#event-aggregator">event-aggregator</a><br />
  1861. <a href="#event-min-interval">event-min-interval</a><br />
  1862. <a href="#event-on-change-reading">event-on-change-reading</a><br />
  1863. <a href="#event-on-update-reading">event-on-update-reading</a><br />
  1864. <a href="#eventMap">eventMap</a><br /> <a href="#group">group</a><br />
  1865. <a href="#icon">icon</a><br />
  1866. <a href="#room">room</a><br />
  1867. <a href="#showtime">showtime</a><br />
  1868. <a href="#sortby">sortby</a><br />
  1869. <a href="#stateFormat">stateFormat</a><br />
  1870. <a href="#supressReading">supressReading</a><br />
  1871. <a href="#timestamp-on-change-reading">timestamp-on-change-reading</a><br />
  1872. <a href="#userReadings">userReadings</a><br />
  1873. <a href="#userattr">userattr</a><br /> <a href="#verbose">verbose</a><br />
  1874. <a href="#webCmd">webCmd</a><br />
  1875. <a href="#webCmdLabel">webCmdLabel</a><br />
  1876. <a href="#widgetOverride">widgetOverride</a></p>
  1877. <p>&nbsp;</p>
  1878. <p><strong>Special attributes</strong></p>
  1879. <p><a name="KNXanswerReading"></a> <strong>answerReading</strong></p>
  1880. <ul>If enabled, FHEM answers on read requests. The content of reading &lt;state&gt; is send to the bus as answer. If supplied, the content of the reading &lt;putName&gt; is used to supply the data for the answer.</ul>
  1881. <p><a name="KNXstateRegex"></a> <strong>stateRegex</strong></p>
  1882. <ul>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 &lt;getName&gt;:&lt;state-value&gt;. The substitution is done every time, a new object is received. You can use this function for converting, adding units, having more fun with icons, ...</ul>
  1883. <ul>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.</ul>
  1884. <p><a name="KNXstateCmd"></a> <strong>stateCmd</strong></p>
  1885. <ul>You can supply a perl-command for modifying state. This command is executed directly before updating the reading - so after renaming, format and regex. Please supply a valid perl command like using the attribute stateFormat.</ul>
  1886. <ul>Unlike stateFormat the stateCmd modifies also the content of the reading, not only the hash-content for visualization.</ul>
  1887. <ul>You can access the device-hash directly in the perl string. You can access "$hash", "$name" and "$state". The return-value overrides "state".</ul>
  1888. <p><a name="KNXputCmd"></a> <strong>putCmd</strong></p>
  1889. <ul>Every time a KNX-value is requested from the bus to FHEM, the content of putCmd is evaluated before the answer is send. You can supply a perl-command for modifying content. This command is executed directly before sending the data. A copy is stored in the reading &lt;putName&gt;.</ul>
  1890. <ul>Each device only knows one putCmd, so you have to take care about the different GAD's in the perl string.</ul>
  1891. <ul>Like in stateCmd you can access the device hash in this perl string. You can access "$hash", "$name" and "$state". "$state" contains the prefilled return-value. The return-value overrides "state".</ul>
  1892. <p><a name="KNXformat"></a> <strong>format</strong></p>
  1893. <ul>The content of this attribute is added to every received value, before this is copied to state.</ul>
  1894. <p>&nbsp;</p>
  1895. <p><a name="KNXdpt"></a> <strong>DPT - datapoint-types</strong></p>
  1896. <p>The following dpt are implemented and have to be assigned within the device definition.</p>
  1897. <ul>dpt1 on, off</ul>
  1898. <ul>dpt1.000 1, 0</ul>
  1899. <ul>dpt1.001 on, off</ul>
  1900. <ul>dpt1.002 true, false</ul>
  1901. <ul>dpt1.003 enable, disable</ul>
  1902. <ul>dpt1.004 no ramp, ramp</ul>
  1903. <ul>dpt1.005 no alarm, alarm</ul>
  1904. <ul>dpt1.006 low, high</ul>
  1905. <ul>dpt1.007 decrease, increase</ul>
  1906. <ul>dpt1.008 up, down</ul>
  1907. <ul>dpt1.009 open, closed</ul>
  1908. <ul>dpt1.010 start, stop</ul>
  1909. <ul>dpt1.011 inactive, active</ul>
  1910. <ul>dpt1.012 not inverted, inverted</ul>
  1911. <ul>dpt1.013 start/stop, ciclically</ul>
  1912. <ul>dpt1.014 fixed, calculated</ul>
  1913. <ul>dpt1.015 no action, reset</ul>
  1914. <ul>dpt1.016 no action, acknowledge</ul>
  1915. <ul>dpt1.017 trigger, trigger</ul>
  1916. <ul>dpt1.018 not occupied, occupied</ul>
  1917. <ul>dpt1.019 closed, open</ul>
  1918. <ul>dpt1.021 logical or, logical and</ul>
  1919. <ul>dpt1.022 scene A, scene B</ul>
  1920. <ul>dpt1.023 move up/down, move and step mode</ul>
  1921. <ul>dpt2 on, off, forceOn, forceOff</ul>
  1922. <ul>dpt3 -100..+100</ul>
  1923. <ul>dpt3.007 -100..+100 %</ul>
  1924. <ul>dpt5 0..255</ul>
  1925. <ul>dpt5.001 0..100 %</ul>
  1926. <ul>dpt5.003 0..360 &deg;</ul>
  1927. <ul>dpt5.004 0..255 %</ul>
  1928. <ul>dpt6 -127..+127</ul>
  1929. <ul>dpt6.001 0..100 %</ul>
  1930. <ul>dpt7 0..65535</ul>
  1931. <ul>dpt7.001 0..65535 s</ul>
  1932. <ul>dpt7.005 0..65535 s</ul>
  1933. <ul>dpt7.006 0..65535 m</ul>
  1934. <ul>dpt7.007 0..65535 h</ul>
  1935. <ul>dpt7.012 0..65535 mA</ul>
  1936. <ul>dpt7.013 0..65535 lux</ul>
  1937. <ul>dpt8 -32768..32768</ul>
  1938. <ul>dpt8.005 -32768..32768 s</ul>
  1939. <ul>dpt8.010 -32768..32768 %</ul>
  1940. <ul>dpt8.011 -32768..32768 &deg;</ul>
  1941. <ul>dpt9 -670760.0..+670760.0</ul>
  1942. <ul>dpt9.001 -670760.0..+670760.0 &deg;</ul>
  1943. <ul>dpt9.004 -670760.0..+670760.0 lux</ul>
  1944. <ul>dpt9.005 -670760.0..+670760.0 m/s</ul>
  1945. <ul>dpt9.006 -670760.0..+670760.0 Pa</ul>
  1946. <ul>dpt9.007 -670760.0..+670760.0 %</ul>
  1947. <ul>dpt9.008 -670760.0..+670760.0 ppm</ul>
  1948. <ul>dpt9.009 -670760.0..+670760.0 m&sup3;/h</ul>
  1949. <ul>dpt9.010 -670760.0..+670760.0 s</ul>
  1950. <ul>dpt9.021 -670760.0..+670760.0 mA</ul>
  1951. <ul>dpt9.024 -670760.0..+670760.0 kW</ul>
  1952. <ul>dpt9.025 -670760.0..+670760.0 l/h</ul>
  1953. <ul>dpt9.026 -670760.0..+670760.0 l/h</ul>
  1954. <ul>dpt9.028 -670760.0..+670760.0 km/h</ul>
  1955. <ul>dpt10 01:00:00</ul>
  1956. <ul>dpt11 01.01.2000</ul>
  1957. <ul>dpt12 0..+Inf</ul>
  1958. <ul>dpt13 -Inf..+Inf</ul>
  1959. <ul>dpt13.010 -Inf..+Inf Wh</ul>
  1960. <ul>dpt13.013 -Inf..+Inf kWh</ul>
  1961. <ul>dpt14 -Inf.0..+Inf.0</ul>
  1962. <ul>dpt14.019 -Inf.0..+Inf.0 A</ul>
  1963. <ul>dpt14.027 -Inf.0..+Inf.0 V</ul>
  1964. <ul>dpt14.033 -Inf.0..+Inf.0 Hz</ul>
  1965. <ul>dpt14.056 -Inf.0..+Inf.0 W</ul>
  1966. <ul>dpt14.057 -Inf.0..+Inf.0 cos&Phi;</ul>
  1967. <ul>dpt14.068 -Inf.0..+Inf.0 &deg;C</ul>
  1968. <ul>dpt14.076 -Inf.0..+Inf.0 m&sup3;</ul>
  1969. <ul>dpt16 String</ul>
  1970. <ul>dpt16.000 ASCII-String</ul>
  1971. <ul>dpt16.001 ISO-8859-1-String (Latin1)</ul>
  1972. <ul>dpt17.001 Scene number: 0..63</ul>
  1973. <ul>dpt18.001 Scene number: 1..64. Watch out - only "activation" works. "Learning" will be limited to 64...</ul>
  1974. <ul>dpt19 01.12.2010_01:00:00</ul>
  1975. <ul>dpt232 RGB-Value RRGGBB</ul>
  1976. <p>&nbsp;</p>
  1977. <p><strong>More complex examples</strong></p>
  1978. <p>&nbsp;</p>
  1979. <p><em>Rollo:</em></p>
  1980. <pre>define rollo KNX 0/10/12:dpt1.008:wdw1 0/10/13:dpt1</pre>
  1981. <p>moves down rollo at window 1:</p>
  1982. <pre>set rollo wdw1 down</pre>
  1983. <p>moves up rollo at window 2:</p>
  1984. <pre>set rollo g2 on</pre>
  1985. <p>moves down rollo at window 2 for 5s:</p>
  1986. <pre>set rollo g2 off-for-timer 5</pre>
  1987. <p>&nbsp;</p>
  1988. <p><em>Object with feedback, icon showing transistions:</em></p>
  1989. <pre>define sps KNX 0/4/0:dpt1:steuern 0/4/1:dpt1:status</pre>
  1990. <pre>attr sps devStateIcon status-on:general_an:Aus status-off:general_aus:Ein steuern.*:hourglass:Aus</pre>
  1991. <pre>attr sps eventMap /steuern on:Ein/steuern off:Aus/</pre>
  1992. <pre>attr sps stateRegex /steuern-[sg]et:/steuern-/ /status-get:/status-/</pre>
  1993. <pre>attr sps webCmd Ein:Aus</pre>
  1994. <p>&nbsp;</p>
  1995. <p><em>Object with feedback, state is always showing status:</em></p>
  1996. <pre>define wasser_status KNX 11/3/0:dpt1.001:status:listenonly 11/3/1:dpt1.001:steuern-auf 11/3/2:dpt1.001:steuern-zu</pre>
  1997. <pre>attr wasser_status devStateIcon on:general_an off:general_aus</pre>
  1998. <pre>attr wasser_status stateCmd {sprintf("%s", ReadingsVal($name,"status-get",""))}</pre>
  1999. <pre>attr wasser_status webCmd :</pre>
  2000. <p>&nbsp;</p>
  2001. <p><em>If requested, fhem answers content of GAD refVal to GAD temp, answer nothing to GAD humidity:</em></p>
  2002. <pre>define demo KNX 1/0/30:dpt9.001:temp 1/0/31:dpt9.001:humidity 1/0/32:dpt9:refVal</pre>
  2003. <pre>attr demo putCmd {$value= ReadingsNum("demo","temp",0,1) if ($gad =~ /temp/);;}</pre>
  2004. <p>&nbsp;</p>
  2005. <p><em>Time master:</em></p>
  2006. <pre>define timedev KNX 0/0/7:dpt10:time 0/0/8:dpt11:date 0/0/9:dpt19</pre>
  2007. <pre>attr timedev eventMap /time now:timeNow/</pre>
  2008. <pre>attr timedev webCmd value timeNow</pre>
  2009. <p>Sends actual time to the bus:</p>
  2010. <pre>set timedev now</pre>
  2011. <p>Sends actual date to the bus:</p>
  2012. <pre>set timedev date now</pre>
  2013. <p>Sends actual date and time to the bus (combinded):</p>
  2014. <pre>set timedev g3 now</pre>
  2015. <p>&nbsp;</p>
  2016. <p><em>Slider:</em></p>
  2017. <pre>define newTest KNX 15/2/2:dpt5</pre>
  2018. <pre>attr newTest webCmd g1</pre>
  2019. <pre>attr newTest widgetOverride g1:slider,0,5,100</pre>
  2020. <p>&nbsp;</p>
  2021. <p><em>Shows two independent slider and on/off buttons:</em></p>
  2022. <pre>define newTest KNX 15/2/9:dpt5 15/2/3:dpt5 15/2/2:dpt1.001:power</pre>
  2023. <pre>attr newTest IODev knxd</pre>
  2024. <pre>attr newTest eventMap {\</pre>
  2025. <pre> usr=>{\</pre>
  2026. <pre> '^getG1 (\d+)'=>'g1 $1',\</pre>
  2027. <pre> '^getG2 (\d+)'=>'g2 $1',\</pre>
  2028. <pre> '^An'=>'power on',\</pre>
  2029. <pre> '^Aus'=>'power off',\</pre>
  2030. <pre> },\</pre>
  2031. <pre> fw=>{\</pre>
  2032. <pre> '^getG1 (\d+)'=>'getG1',\</pre>
  2033. <pre> '^getG2 (\d+)'=>'getG2',\</pre>
  2034. <pre> '^power-get'=>'state',\</pre>
  2035. <pre> }\</pre>
  2036. <pre>}</pre>
  2037. <pre>attr newTest webCmd An:Aus::Label1:getG1::Label2:getG2</pre>
  2038. <pre>attr newTest widgetOverride getG1:slider,0,5,100 getG2:slider,0,5,100</pre>
  2039. <p>&nbsp;</p>
  2040. <p><em>Has a synchronized slider for send and reveive-values, on/off-buttons and a state-icon:</em></p>
  2041. <pre>define testDev11 KNX 15/1/19:dpt1:steuern 15/1/20:dpt1:status 15/1/21:dpt5.001:dimmwert:nosuffix\</pre>
  2042. <pre>attr testDev11 IODev knxd\</pre>
  2043. <pre>attr testDev11 eventMap {\</pre>
  2044. <pre>#Von Device nach Frontend sollte eigentlich bei einem Dezimalwert\\</pre>
  2045. <pre>#das Reading status-get angezeigt werden. Geht aber nicht. Deshalb: stateCmd...\\</pre>
  2046. <pre> dev=>{\\</pre>
  2047. <pre> # '^(\d+)?.%$'=>ReadingsVal($dev,'status-get',"Zefix..."),\\</pre>
  2048. <pre> },\\</pre>
  2049. <pre> #Frontend nach Device: Ersetze "An"/"Aus" durch "Steuern on/off".\\</pre>
  2050. <pre> #Alle numerischen Werte vom Slider landen in "dimmwert"\\</pre>
  2051. <pre> usr=>{\\</pre>
  2052. <pre> '^An'=>'steuern on',\\</pre>
  2053. <pre> '^Aus'=>'steuern off',\\</pre>
  2054. <pre> },\\</pre>
  2055. <pre> #Tuning für die Detailseite...Zeige An/Aus richtig an\\</pre>
  2056. <pre> fw=>{\\</pre>
  2057. <pre> '^An'=>'An',\\</pre>
  2058. <pre> '^Aus'=>'Aus',\\</pre>
  2059. <pre> }\\</pre>
  2060. <pre>}\</pre>
  2061. <pre>attr testDev11 stateRegex /steuern-[sg]et:/steuern-/ /status-get:/status-/</pre>
  2062. <pre>attr testDev11 stateCmd {\</pre>
  2063. <pre> if ($state =~ m/dimmwert:/i) {'status-' . ReadingsVal($name,"status-get","")}\</pre>
  2064. <pre> else {return $state}\</pre>
  2065. <pre>}</pre>
  2066. <pre>attr testDev11 devStateIcon status-on:general_an:Aus status-off:general_aus:Ein steuern.*:hourglass:Aus</pre>
  2067. <pre>#Dimmwert muss ein reales reading sein. Kann auch ...-get sein, wenn\</pre>
  2068. <pre>#nosuffix nicht angegeben ist.\</pre>
  2069. <pre>#Achtung: bei einem Userreading sind die Werte nicht persistent, also\</pre>
  2070. <pre>#nicht machen!!!\</pre>
  2071. <pre>attr testDev11 webCmd An:Aus:dimmwert\</pre>
  2072. <pre>attr testDev11 widgetOverride dimmwert:slider,0,5,100\</pre>
  2073. =end html
  2074. =device
  2075. =item summary Communicates to KNX via module TUL
  2076. =cut