00_SIGNALduino.pm 116 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736
  1. ##############################################
  2. # $Id: 00_SIGNALduino.pm 13215 2017-01-23 20:09:44Z Sidey $
  3. #
  4. # v3.3.1 (release 3.3)
  5. # The module is inspired by the FHEMduino project and modified in serval ways for processing the incomming messages
  6. # see http://www.fhemwiki.de/wiki/SIGNALDuino
  7. # It was modified also to provide support for raw message handling which can be send from the SIGNALduino
  8. # The purpos is to use it as addition to the SIGNALduino which runs on an arduno nano or arduino uno.
  9. # It routes Messages serval Modules which are already integrated in FHEM. But there are also modules which comes with it.
  10. # N. Butzek, S. Butzek, 2014-2015
  11. # S.Butzek,Ralf9 2016-2017
  12. package main;
  13. use strict;
  14. use warnings;
  15. use Time::HiRes qw(gettimeofday);
  16. use Data::Dumper qw(Dumper);
  17. use Scalar::Util qw(looks_like_number);
  18. no warnings 'portable';
  19. #use POSIX qw( floor); # can be removed
  20. #use Math::Round qw();
  21. use constant {
  22. SDUINO_VERSION => "v3.3.1",
  23. SDUINO_INIT_WAIT_XQ => 1.5, # wait disable device
  24. SDUINO_INIT_WAIT => 2,
  25. SDUINO_INIT_MAXRETRY => 3,
  26. SDUINO_CMD_TIMEOUT => 10,
  27. SDUINO_KEEPALIVE_TIMEOUT => 60,
  28. SDUINO_KEEPALIVE_MAXRETRY => 3,
  29. SDUINO_WRITEQUEUE_NEXT => 0.3,
  30. SDUINO_WRITEQUEUE_TIMEOUT => 2,
  31. };
  32. sub SIGNALduino_Attr(@);
  33. #sub SIGNALduino_Clear($); # wird nicht mehr benoetigt
  34. sub SIGNALduino_HandleWriteQueue($);
  35. sub SIGNALduino_Parse($$$$@);
  36. sub SIGNALduino_Read($);
  37. #sub SIGNALduino_ReadAnswer($$$$); # wird nicht mehr benoetigt
  38. sub SIGNALduino_Ready($);
  39. sub SIGNALduino_Write($$$);
  40. sub SIGNALduino_SimpleWrite(@);
  41. #my $debug=0;
  42. my %gets = ( # Name, Data to send to the SIGNALduino, Regexp for the answer
  43. "version" => ["V", 'V\s.*SIGNAL(duino|ESP).*'],
  44. "freeram" => ["R", '^[0-9]+'],
  45. "raw" => ["", '.*'],
  46. "uptime" => ["t", '^[0-9]+' ],
  47. "cmds" => ["?", '.*Use one of[ 0-9A-Za-z]+[\r\n]*$' ],
  48. "ITParms" => ["ip",'.*'],
  49. "ping" => ["P",'^OK$'],
  50. "config" => ["CG",'^MS.*MU.*MC.*'],
  51. "protocolIDs" => ["none",'none'],
  52. # "ITClock" => ["ic", '\d+'],
  53. # "FAParms" => ["fp", '.*' ],
  54. # "TCParms" => ["dp", '.*' ],
  55. # "HXParms" => ["hp", '.*' ]
  56. );
  57. my %sets = (
  58. "raw" => '',
  59. "flash" => '',
  60. "reset" => 'noArg',
  61. "close" => 'noArg',
  62. #"disablereceiver" => "",
  63. "ITClock" => 'slider,100,20,700',
  64. "enableMessagetype" => 'syncedMS,unsyncedMU,manchesterMC',
  65. "disableMessagetype" => 'syncedMS,unsyncedMU,manchesterMC',
  66. 'sendMsg' => "",
  67. );
  68. ## Supported Clients per default
  69. my $clientsSIGNALduino = ":IT:"
  70. ."CUL_TCM97001:"
  71. # ."SIGNALduino_RSL:"
  72. ."OREGON:"
  73. ."CUL_TX:"
  74. # ."SD_AS:"
  75. ."Hideki:"
  76. ."SD_WS07:"
  77. ."SD_WS09:"
  78. ." :" # Zeilenumbruch
  79. ."SD_WS:"
  80. ."RFXX10REC:"
  81. ."Dooya:"
  82. ."SOMFY:"
  83. # ."SD_UT:" ## BELL 201.2 TXA
  84. ."SD_WS_Maverick:"
  85. ."SIGNALduino_un:"
  86. ;
  87. ## default regex match List for dispatching message to logical modules, can be updated during runtime because it is referenced
  88. my %matchListSIGNALduino = (
  89. "1:IT" => "^i......", # Intertechno Format
  90. "2:CUL_TCM97001" => "^s[A-Fa-f0-9]+", # Any hex string beginning with s
  91. # "3:SIGNALduino_RSL" => "^r[A-Fa-f0-9]+", # Any hex string beginning with r
  92. "5:CUL_TX" => "^TX..........", # Need TX to avoid FHTTK
  93. # "6:SD_AS" => "^P2#[A-Fa-f0-9]{7,8}", # Arduino based Sensors, should not be default
  94. "4:OREGON" => "^(3[8-9A-F]|[4-6][0-9A-F]|7[0-8]).*",
  95. "7:Hideki" => "^P12#75[A-F0-9]+",
  96. "10:SD_WS07" => "^P7#[A-Fa-f0-9]{6}F[A-Fa-f0-9]{2}",
  97. "11:SD_WS09" => "^P9#[A-Fa-f0-9]+",
  98. "12:SD_WS" => '^W\d+x{0,1}#.*',
  99. "13:RFXX10REC" => '^(20|29)[A-Fa-f0-9]+',
  100. "14:Dooya" => '^P16#[A-Fa-f0-9]+',
  101. "15:SOMFY" => '^YsA[0-9A-F]+',
  102. "16:SD_WS_Maverick" => '^P47#[A-Fa-f0-9]+',
  103. # "17:SD_UT" => '^u30#.*', ## BELL 201.2 TXA
  104. "X:SIGNALduino_un" => '^[uP]\d+#.*',
  105. );
  106. my %ProtocolListSIGNALduino = (
  107. "0" =>
  108. {
  109. name => 'weather1', # Logilink, NC, WS, TCM97001 etc.
  110. comment => 'Logilink, NC, WS, TCM97001 etc',
  111. id => '0',
  112. one => [1,-8],
  113. zero => [1,-4],
  114. sync => [1,-18],
  115. clockabs => '500', # not used now
  116. format => 'twostate', # not used now
  117. preamble => 's', # prepend to converted message
  118. postamble => '00', # Append to converted message
  119. clientmodule => 'CUL_TCM97001', # not used now
  120. #modulematch => '^s[A-Fa-f0-9]+', # not used now
  121. length_min => '24',
  122. length_max => '40',
  123. paddingbits => '8', # pad up to 8 bits, default is 4
  124. },
  125. "1" =>
  126. {
  127. name => 'ConradRSL', #
  128. id => '1',
  129. one => [2,-1],
  130. zero => [1,-2],
  131. sync => [1,-11],
  132. clockabs => '560', # not used now
  133. format => 'twostate', # not used now
  134. preamble => 'r', # prepend to converted message
  135. postamble => '', # Append to converted message
  136. clientmodule => 'SIGNALduino_RSL', # not used now
  137. modulematch => '^r[A-Fa-f0-9]+', # not used now
  138. length_min => '23',
  139. length_max => '24',
  140. },
  141. "2" =>
  142. {
  143. name => 'AS', # Self build arduino sensor
  144. comment => 'Self build arduino sensor',
  145. id => '2',
  146. one => [1,-2],
  147. zero => [1,-1],
  148. sync => [1,-20],
  149. clockabs => '500', # not used now
  150. format => 'twostate',
  151. preamble => 'P2#', # prepend to converted message
  152. clientmodule => 'SD_AS', # not used now
  153. modulematch => '^P2#.{7,8}',
  154. length_min => '32',
  155. length_max => '34', # Don't know maximal lenth of a valid message
  156. paddingbits => '8', # pad up to 8 bits, default is 4
  157. },
  158. "3" =>
  159. {
  160. name => 'itv1',
  161. id => '3',
  162. one => [3,-1],
  163. zero => [1,-3],
  164. #float => [-1,3], # not full supported now later use
  165. sync => [1,-31],
  166. clockabs => -1, # -1=auto
  167. format => 'twostate', # not used now
  168. preamble => 'i',
  169. clientmodule => 'IT', # not used now
  170. modulematch => '^i......', # not used now
  171. length_min => '24',
  172. #length_max => '800', # Don't know maximal lenth of a valid message
  173. },
  174. "4" =>
  175. {
  176. name => 'arctech2',
  177. id => '4',
  178. #one => [1,-5,1,-1],
  179. #zero => [1,-1,1,-5],
  180. one => [1,-5],
  181. zero => [1,-1],
  182. #float => [-1,3], # not full supported now, for later use
  183. sync => [1,-14],
  184. clockabs => -1, # -1 = auto
  185. format => 'twostate', # tristate can't be migrated from bin into hex!
  186. preamble => 'i', # Append to converted message
  187. postamble => '00', # Append to converted message
  188. clientmodule => 'IT', # not used now
  189. modulematch => '^i......', # not used now
  190. length_min => '32',
  191. #length_max => '76', # Don't know maximal lenth of a valid message
  192. },
  193. "5" => ## Similar protocol as intertechno, but without sync
  194. {
  195. name => 'unitec6899',
  196. id => '5',
  197. one => [3,-1],
  198. zero => [1,-3],
  199. clockabs => 500, # -1 = auto
  200. format => 'twostate', # tristate can't be migrated from bin into hex!
  201. preamble => 'p5#', # Append to converted message
  202. clientmodule => 'IT', # not used now
  203. modulematch => '^i......', # not used now
  204. length_min => '24',
  205. },
  206. "6" => ## Eurochron Protocol
  207. {
  208. name => 'weatherID6',
  209. id => '6',
  210. one => [1,-10],
  211. zero => [1,-5],
  212. sync => [1,-36], # This special device has no sync
  213. clockabs => 220, # -1 = auto
  214. format => 'twostate', # tristate can't be migrated from bin into hex!
  215. preamble => 'u6#', # Append to converted message
  216. #clientmodule => '', # not used now
  217. #modulematch => '^u......', # not used now
  218. length_min => '24',
  219. },
  220. "7" => ## weather sensors like EAS800z
  221. {
  222. name => 'weatherID7',
  223. comment => 'EAS800z, FreeTec NC-7344',
  224. id => '7',
  225. one => [1,-4],
  226. zero => [1,-2],
  227. sync => [1,-8],
  228. clockabs => 484,
  229. format => 'twostate',
  230. preamble => 'P7#', # prepend to converted message
  231. clientmodule => 'SD_WS07', # not used now
  232. modulematch => '^P7#.{6}F.{2}', # not used now
  233. length_min => '35',
  234. length_max => '40',
  235. },
  236. "8" => ## TX3 (ITTX) Protocol
  237. {
  238. name => 'TX3 Protocol',
  239. id => '8',
  240. one => [1,-2],
  241. zero => [2,-2],
  242. #sync => [1,-8], #
  243. clockabs => 470, #
  244. format => 'pwm', #
  245. preamble => 'TX', # prepend to converted message
  246. clientmodule => 'CUL_TX', # not used now
  247. modulematch => '^TX......', # not used now
  248. length_min => '43',
  249. length_max => '44',
  250. remove_zero => 1, # Removes leading zeros from output
  251. },
  252. "9" => ## Funk Wetterstation CTW600
  253. {
  254. name => 'CTW 600',
  255. comment => 'Funk Wetterstation CTW600',
  256. id => '9',
  257. zero => [3,-2],
  258. one => [1,-2],
  259. #float => [-1,3], # not full supported now, for later use
  260. #sync => [1,-8], #
  261. clockabs => 480, # -1 = auto undef=noclock
  262. format => 'pwm', # tristate can't be migrated from bin into hex!
  263. preamble => 'P9#', # prepend to converted message
  264. clientmodule => 'SD_WS09', # not used now
  265. #modulematch => '^u9#.....', # not used now
  266. length_min => '70',
  267. length_max => '120',
  268. },
  269. "10" => ## Oregon Scientific 2
  270. {
  271. name => 'OSV2o3',
  272. id => '10',
  273. clockrange => [300,520], # min , max
  274. format => 'manchester', # tristate can't be migrated from bin into hex!
  275. clientmodule => 'OREGON',
  276. modulematch => '^(3[8-9A-F]|[4-6][0-9A-F]|7[0-8]).*',
  277. length_min => '64',
  278. length_max => '220',
  279. method => \&SIGNALduino_OSV2, # Call to process this message
  280. polarity => 'invert',
  281. },
  282. "11" => ## Arduino Sensor
  283. {
  284. name => 'AS',
  285. id => '11',
  286. clockrange => [380,425], # min , max
  287. format => 'manchester', # tristate can't be migrated from bin into hex!
  288. preamble => 'P2#', # prepend to converted message
  289. clientmodule => 'SD_AS', # not used now
  290. modulematch => '^P2#.{7,8}',
  291. length_min => '52',
  292. length_max => '56',
  293. method => \&SIGNALduino_AS # Call to process this message
  294. },
  295. "12" => ## hideki
  296. {
  297. name => 'Hideki protocol',
  298. id => '12',
  299. clockrange => [420,510], # min, max better for Bresser Sensors, OK for hideki/Hideki/TFA too
  300. format => 'manchester',
  301. preamble => 'P12#', # prepend to converted message
  302. clientmodule => 'hideki', # not used now
  303. modulematch => '^P12#75.+', # not used now
  304. length_min => '72',
  305. length_max => '104',
  306. method => \&SIGNALduino_Hideki, # Call to process this message
  307. polarity => 'invert',
  308. },
  309. "13" => ## FA21RF
  310. {
  311. name => '21RF',
  312. id => '13',
  313. one => [1,-2],
  314. zero => [1,-4],
  315. sync => [10,-1],
  316. clockabs => 800,
  317. format => 'twostate',
  318. preamble => 'u13#', # prepend to converted message
  319. #clientmodule => '', # not used now
  320. #modulematch => '', # not used now
  321. length_min => '20',
  322. length_max => '40',
  323. },
  324. "14" => ## Heidemann HX
  325. {
  326. name => 'Heidemann HX',
  327. id => '14',
  328. one => [1,-2],
  329. zero => [1,-1],
  330. #float => [-1,3], # not full supported now, for later use
  331. sync => [1,-14], #
  332. clockabs => 350,
  333. format => 'twostate',
  334. preamble => 'u14#', # prepend to converted message
  335. #clientmodule => '', # not used now
  336. #modulematch => '', # not used now
  337. length_min => '10',
  338. length_max => '20',
  339. },
  340. "15" => ## TCM234759
  341. {
  342. name => 'TCM Bell',
  343. id => '15',
  344. one => [1,-1],
  345. zero => [1,-2],
  346. sync => [1,-45], #
  347. clockabs => 700,
  348. format => 'twostate',
  349. preamble => 'u15#', # prepend to converted message
  350. #clientmodule => '', # not used now
  351. #modulematch => '', # not used now
  352. length_min => '10',
  353. length_max => '20',
  354. #method => \&SIGNALduino_Cresta # Call to process this message
  355. },
  356. "16" => # Rohrmotor24 und andere Funk Rolladen / Markisen Motoren
  357. {
  358. name => 'Dooya shutter',
  359. id => '16',
  360. one => [2,-1],
  361. zero => [1,-3],
  362. start => [17,-5],
  363. clockabs => 280,
  364. format => 'twostate',
  365. preamble => 'P16#', # prepend to converted message
  366. clientmodule => 'Dooya', # not used now
  367. #modulematch => '', # not used now
  368. length_min => '39',
  369. length_max => '40',
  370. },
  371. "17" =>
  372. {
  373. name => 'arctech',
  374. id => '17',
  375. one => [1,-5,1,-1],
  376. zero => [1,-1,1,-5],
  377. #one => [1,-5],
  378. #zero => [1,-1],
  379. sync => [1,-10],
  380. float => [1,-1,1,-1],
  381. clockabs => -1, # -1 = auto
  382. format => 'twostate', # tristate can't be migrated from bin into hex!
  383. preamble => 'i', # Append to converted message
  384. postamble => '00', # Append to converted message
  385. clientmodule => 'IT', # not used now
  386. modulematch => '^i......', # not used now
  387. length_min => '32',
  388. #length_max => '76', # Don't know maximal lenth of a valid message
  389. postDemodulation => \&SIGNALduino_bit2Arctec,
  390. },
  391. "18" => ## Oregon Scientific v1
  392. {
  393. name => 'OSV1',
  394. id => '18',
  395. clockrange => [1550,1650], # min , max
  396. format => 'manchester', # tristate can't be migrated from bin into hex!
  397. #preamble => '', # prepend to converted message
  398. #clientmodule => 'to be written', # not used now
  399. modulematch => '^(3[8-9A-F]|[4-6][0-9A-F]|7[0-8]).*',
  400. length_min => '8',
  401. length_max => '8',
  402. method => \&SIGNALduino_OSV1 # Call to process this message
  403. },
  404. #"19" => # nothing knowing about this 2015-09-28 01:25:40-MS;P0=-8916;P1=-19904;P2=390;P3=-535;P4=-1020;P5=12846;P6=1371;D=2120232323232324242423232323232323232320239;CP=2;SP=1;
  405. #
  406. # {
  407. # name => 'unknown19',
  408. # id => '19',
  409. # one => [1,-2],
  410. # zero => [1,-1],
  411. # sync => [1,-50,1,-22],
  412. # clockabs => 395,
  413. # format => 'twostate',
  414. # preamble => 'u19#', # prepend to converted message
  415. # #clientmodule => '', # not used now
  416. # #modulematch => '', # not used now
  417. # length_min => '16',
  418. # length_max => '32',
  419. # },
  420. "20" => #Livolo
  421. {
  422. name => 'livolo',
  423. id => '20',
  424. one => [3],
  425. zero => [1],
  426. start => [5],
  427. clockabs => 110, #can be 90-140
  428. format => 'twostate',
  429. preamble => 'u20#', # prepend to converted message
  430. #clientmodule => '', # not used now
  431. #modulematch => '', # not used now
  432. length_min => '16',
  433. filterfunc => 'SIGNALduino_filterSign',
  434. },
  435. "21" => #Einhell Garagentor
  436. {
  437. name => 'einhell garagedoor',
  438. id => '21',
  439. one => [-3,1],
  440. zero => [-1,3],
  441. #sync => [-50,1],
  442. start => [-50,1],
  443. clockabs => 400, #ca 400us
  444. format => 'twostate',
  445. preamble => 'u21#', # prepend to converted message
  446. #clientmodule => '', # not used now
  447. #modulematch => '', # not used now
  448. length_min => '32',
  449. length_max => '32',
  450. paddingbits => '1', # This will disable padding
  451. },
  452. "22" => #TX-EZ6 / Meteo
  453. {
  454. name => 'TX-EZ6',
  455. id => '22',
  456. one => [1,-8],
  457. zero => [1,-3],
  458. sync => [1,16],
  459. clockabs => 500, #ca 400us
  460. format => 'twostate',
  461. preamble => 'u22#', # prepend to converted message
  462. #clientmodule => '', # not used now
  463. #modulematch => '', # not used now
  464. length_min => '40',
  465. #length_max => '', # must be tested
  466. },
  467. "23" => # Pearl Sensor
  468. {
  469. name => 'perl unknown',
  470. id => '23',
  471. one => [1,-6],
  472. zero => [1,-1],
  473. sync => [1,-50],
  474. clockabs => 200, #ca 200us
  475. format => 'twostate',
  476. preamble => 'u23#', # prepend to converted message
  477. #clientmodule => '', # not used now
  478. #modulematch => '', # not used now
  479. length_min => '36',
  480. length_max => '44',
  481. },
  482. "24" => # visivon
  483. {
  484. name => 'visivon remote',
  485. id => '24',
  486. one => [3,-2],
  487. zero => [1,-5],
  488. #one => [3,-2],
  489. #zero => [1,-1],
  490. start => [30,-5],
  491. clockabs => 150, #ca 150us
  492. format => 'twostate',
  493. preamble => 'u24#', # prepend to converted message
  494. #clientmodule => '', # not used now
  495. #modulematch => '', # not used now
  496. length_min => '54',
  497. length_max => '58',
  498. },
  499. "25" => # LES remote for led lamp
  500. {
  501. name => 'les led remote',
  502. id => '25',
  503. one => [-2,1],
  504. zero => [-1,2],
  505. sync => [-46,1], # this is a end marker, but we use this as a start marker
  506. clockabs => 350, #ca 350us
  507. format => 'twostate',
  508. preamble => 'u25#', # prepend to converted message
  509. #clientmodule => '', # not used now
  510. #modulematch => '', # not used now
  511. length_min => '24',
  512. length_max => '50', # message has only 24 bit, but we get more than one message, calculation has to be corrected
  513. },
  514. "26" => # some remote code send by flamingo style remote controls
  515. {
  516. name => 'remote26',
  517. id => '26',
  518. one => [1,-3],
  519. zero => [3,-1],
  520. # sync => [1,-6], # Message is not provided as MS, due to small fact
  521. start => [1,-6], # Message is not provided as MS, due to small fact
  522. clockabs => 380, #ca 380
  523. format => 'twostate',
  524. preamble => 'u26#', # prepend to converted message
  525. #clientmodule => '', # not used now
  526. #modulematch => '', # not used now
  527. length_min => '24',
  528. length_max => '24', # message has only 24 bit, but we get more than one message, calculation has to be corrected
  529. },
  530. "27" => # some remote code, send by flamingo style remote controls
  531. {
  532. name => 'remote27',
  533. id => '27',
  534. one => [1,-2],
  535. zero => [2,-1],
  536. start => [6,-15], # Message is not provided as MS, worakround is start
  537. clockabs => 480, #ca 480
  538. format => 'twostate',
  539. preamble => 'u27#', # prepend to converted message
  540. #clientmodule => '', # not used now
  541. #modulematch => '', # not used now
  542. length_min => '24',
  543. length_max => '24',
  544. },
  545. "28" => # some remote code, send by aldi IC Ledspots
  546. {
  547. name => 'IC Ledspot',
  548. id => '28',
  549. one => [1,-1],
  550. zero => [1,-2],
  551. start => [4,-5],
  552. clockabs => 600, #ca 600
  553. format => 'twostate',
  554. preamble => 'u28#', # prepend to converted message
  555. #clientmodule => '', # not used now
  556. #modulematch => '', # not used now
  557. length_min => '8',
  558. length_max => '8',
  559. },
  560. "29" => #
  561. {
  562. name => 'HT12e remote',
  563. id => '29',
  564. one => [-2,1],
  565. zero => [-1,2],
  566. #float => [1,-1],
  567. start => [-38,1], # Message is not provided as MS, worakround is start
  568. clockabs => 220, #ca 220
  569. format => 'tristate', # there is a pause puls between words
  570. preamble => 'u29#', # prepend to converted message
  571. #clientmodule => '', # not used now
  572. #modulematch => '', # not used now
  573. length_min => '10',
  574. length_max => '12', # message has only 10 bit but is paddet to 12
  575. },
  576. "30" => # a unitec remote door reed switch
  577. {
  578. name => 'unitec47031',
  579. id => '30',
  580. one => [-1,2],
  581. zero => [-2,1],
  582. start => [-33,1], # Message is not provided as MS, worakround is start
  583. clockabs => 300, # ca 300 us
  584. format => 'twostate', # there is a pause puls between words
  585. preamble => 'u30#', # prepend to converted message
  586. clientmodule => 'SD_UT', # not used now
  587. modulematch => '^u30', # not used now
  588. length_min => '12',
  589. length_max => '12', # message has only 10 bit but is paddet to 12
  590. },
  591. "31" => # Pollin Isotronic
  592. {
  593. name => 'pollin isotronic',
  594. id => '31',
  595. one => [-1,2],
  596. zero => [-2,1],
  597. start => [1],
  598. clockabs => 600,
  599. format => 'twostate',
  600. preamble => 'u31#', # prepend to converted message
  601. #clientmodule => '', # not used now
  602. #modulematch => '', # not used now
  603. length_min => '20',
  604. length_max => '20',
  605. },
  606. "32" => #FreeTec PE-6946 -> http://www.free-tec.de/Funkklingel-mit-Voic-PE-6946-919.shtml
  607. {
  608. name => 'freetec 6946',
  609. id => '32',
  610. one => [4,-2],
  611. zero => [1,-5],
  612. sync => [1,-49],
  613. clockabs => 140, #ca 140us
  614. format => 'twostate',
  615. preamble => 'u32#', # prepend to converted message
  616. #clientmodule => '', # not used now
  617. #modulematch => '', # not used now
  618. length_min => '24',
  619. length_max => '24',
  620. },
  621. "33" => #Thermo-/Hygrosensor S014
  622. {
  623. name => 'weather33', #
  624. id => '33',
  625. one => [1,-8],
  626. zero => [1,-4],
  627. sync => [1,-15],
  628. clockabs => '500', # not used now
  629. format => 'twostate', # not used now
  630. preamble => 'W33#', # prepend to converted message
  631. postamble => '', # Append to converted message
  632. clientmodule => 'SD_WS', # not used now
  633. #modulematch => '', # not used now
  634. length_min => '42',
  635. length_max => '44',
  636. },
  637. "34" =>
  638. {
  639. name => 'unknown34',
  640. id => '34',
  641. one => [2,-1],
  642. zero => [1,-2],
  643. start => [3,-3,3,-3,3,-3,3,-3],
  644. clockabs => '240',
  645. format => 'twostate', # not used now
  646. preamble => 'u34#', # prepend to converted message
  647. postamble => '', # Append to converted message
  648. #clientmodule => '', # not used now
  649. #modulematch => '', # not used now
  650. length_min => '40',
  651. length_max => '40',
  652. },
  653. "35" =>
  654. {
  655. name => 'socket35',
  656. id => '35',
  657. one => [1,-4],
  658. zero => [4,-1],
  659. sync => [1,-19],
  660. clockabs => '280',
  661. format => 'twostate', # not used now
  662. preamble => 'u35#', # prepend to converted message
  663. postamble => '', # Append to converted message
  664. #clientmodule => '', # not used now
  665. #modulematch => '', # not used now
  666. length_min => '28',
  667. length_max => '32',
  668. },
  669. "36" =>
  670. {
  671. name => 'socket36',
  672. id => '36',
  673. one => [1,-3],
  674. zero => [1,-1],
  675. start => [20,-20],
  676. clockabs => '500',
  677. format => 'twostate', # not used now
  678. preamble => 'u36#', # prepend to converted message
  679. postamble => '', # Append to converted message
  680. #clientmodule => '', # not used now
  681. #modulematch => '', # not used now
  682. length_min => '24',
  683. length_max => '24',
  684. },
  685. "37" =>
  686. {
  687. name => 'weather37',
  688. id => '37',
  689. one => [2,-1],
  690. zero => [1,-2],
  691. start => [3,-3,3,-3],
  692. clockabs => '230',
  693. format => 'twostate', # not used now
  694. preamble => 'W37#', # prepend to converted message
  695. postamble => '', # Append to converted message
  696. clientmodule => 'SD_WS', # not used now
  697. #modulematch => '', # not used now
  698. length_min => '40',
  699. length_max => '44',
  700. },
  701. "38" =>
  702. {
  703. name => 'weather38',
  704. id => '38',
  705. one => [1,-10],
  706. zero => [1,-5],
  707. sync => [1,-25],
  708. clockabs => '360', # not used now
  709. format => 'twostate', # not used now
  710. preamble => 's', # prepend to converted message
  711. postamble => '00', # Append to converted message
  712. clientmodule => 'CUL_TCM97001', # not used now
  713. #modulematch => '^s[A-Fa-f0-9]+', # not used now
  714. length_min => '32',
  715. length_max => '32',
  716. paddingbits => '8',
  717. },
  718. "39" => ## X10 Protocol
  719. {
  720. name => 'X10 Protocol',
  721. id => '39',
  722. one => [1,-3],
  723. zero => [1,-1],
  724. start => [16,-4],
  725. clockabs => 650,
  726. format => 'twostate',
  727. preamble => '', # prepend to converted message
  728. clientmodule => 'RFXX10REC', # not used now
  729. #modulematch => '^TX......', # not used now
  730. length_min => '38',
  731. length_max => '44',
  732. paddingbits => '8',
  733. postDemodulation => \&SIGNALduino_lengtnPrefix,
  734. filterfunc => 'SIGNALduino_compPattern',
  735. },
  736. "40" => ## Romotec
  737. {
  738. name => 'romotec',
  739. id => '40',
  740. one => [3,-2],
  741. zero => [1,-3],
  742. start => [1,-2],
  743. clockabs => 250,
  744. preamble => 'u40#', # prepend to converted message
  745. #clientmodule => '', # not used now
  746. #modulematch => '', # not used now
  747. length_min => '10',
  748. },
  749. "41" => ## Elro (Smartwares) Doorbell DB200
  750. {
  751. name => 'elro doorbell',
  752. comment => 'Elro (Smartwares) Doorbell DB200',
  753. id => '41',
  754. zero => [1,-3],
  755. one => [3,-1],
  756. sync => [1,-15],
  757. clockabs => 450,
  758. preamble => 'u41#', # prepend to converted message
  759. #clientmodule => '', # not used now
  760. #modulematch => '', # not used now
  761. length_min => '20',
  762. },
  763. "42" => ## MKT Multi Kon Trade
  764. {
  765. name => 'MKT motionsensor',
  766. id => '42',
  767. zero => [1,-3],
  768. one => [3,-1],
  769. start => [-28],
  770. clockabs => 550,
  771. preamble => 'u42#', # prepend to converted message
  772. #clientmodule => '', # not used now
  773. #modulematch => '',
  774. length_min => '24',
  775. },
  776. "43" => ## Somfy RTS
  777. {
  778. name => 'Somfy RTS',
  779. id => '43',
  780. clockrange => [610,670], # min , max
  781. format => 'manchester',
  782. preamble => 'Ys',
  783. clientmodule => 'SOMFY', # not used now
  784. modulematch => '^YsA[0-9A-F]{13}',
  785. length_min => '56',
  786. length_max => '56',
  787. method => \&SIGNALduino_SomfyRTS, # Call to process this message
  788. msgIntro => 'SR;P0=-2560;P1=2560;P3=-640;D=10101010101010113;',
  789. #msgOutro => 'SR;P0=-30415;D=0;',
  790. },
  791. "44" => ## Bresser Temeo Trend
  792. {
  793. name => 'BresserTemeo',
  794. id => '44',
  795. clockabs => 500,
  796. zero => [4,-4],
  797. one => [4,-8],
  798. start => [8,-8],
  799. preamble => 'W44#',
  800. clientmodule => 'SD_WS',
  801. modulematch => '^W44#[A-F0-9]{18}',
  802. length_min => '64',
  803. length_max => '72',
  804. },
  805. "51" => ## Bresser Temeo Trend
  806. {
  807. name => 'BresserTemeo',
  808. id => '44x',
  809. clockabs => 500,
  810. zero => [4,-4],
  811. one => [4,-8],
  812. start => [8,-12],
  813. preamble => 'W44x#',
  814. clientmodule => 'SD_WS',
  815. modulematch => '^W44x#[A-F0-9]{18}',
  816. length_min => '64',
  817. length_max => '72',
  818. },
  819. "45" =>
  820. {
  821. name => 'revolt',
  822. id => '45',
  823. one => [3,-1],
  824. zero => [1,-3],
  825. #float => [-1,3], # not full supported now later use
  826. sync => [1,-24],
  827. clockabs => -1, # -1=auto
  828. format => 'twostate', # not used now
  829. preamble => 'i',
  830. clientmodule => 'IT', # not used now
  831. modulematch => '^i......', # not used now
  832. length_min => '24',
  833. },
  834. "46" =>
  835. {
  836. name => 'EKX1BE',
  837. id => '46',
  838. one => [1,-8],
  839. zero => [8,-1],
  840. clockabs => 250, # -1=auto
  841. format => 'twostate', # not used now
  842. preamble => 'u46#',
  843. #clientmodule => '', # not used now
  844. #modulematch => '', # not used now
  845. length_min => '16',
  846. length_max => '18',
  847. },
  848. "47" => ## maverick
  849. {
  850. name => 'Maverick protocol',
  851. id => '47',
  852. clockrange => [220,260],
  853. format => 'manchester',
  854. preamble => 'P47#', # prepend to converted message
  855. clientmodule => 'SD_WS_Maverick', # not used now
  856. modulematch => '^P47#.*', # not used now
  857. length_min => '100',
  858. length_max => '108',
  859. method => \&SIGNALduino_Maverick # Call to process this message
  860. },
  861. "48" => ## Joker Dostmann TFA
  862. {
  863. name => 'TFA Dostmann',
  864. id => '48',
  865. clockabs => 250, # In real it is 500 but this leads to unprceise demodulation
  866. one => [-4,6],
  867. zero => [-4,2],
  868. start => [-6,2],
  869. format => 'twostate',
  870. preamble => 'U48#', # prepend to converted message
  871. #clientmodule => '', # not used now
  872. modulematch => '^U48#.*', # not used now
  873. length_min => '47',
  874. length_max => '48',
  875. },
  876. "49" => ## quigg / Aldi gt_9000
  877. {
  878. name => 'quigg_gt9000',
  879. id => '49',
  880. clockabs => 400,
  881. one => [2,-1],
  882. zero => [1,-3],
  883. start => [-15,2,-1],
  884. format => 'twostate',
  885. preamble => 'U49#', # prepend to converted message
  886. #clientmodule => '', # not used now
  887. modulematch => '^U49#.*', # not used now
  888. length_min => '22',
  889. length_max => '28',
  890. },
  891. "50" => ## Opus XT300
  892. {
  893. name => 'optus_XT300',
  894. id => '50',
  895. clockabs => 500,
  896. zero => [3,-2],
  897. one => [1,-2],
  898. # start => [1,-25], # Wenn das startsignal empfangen wird, fehlt das 1 bit
  899. format => 'twostate',
  900. preamble => 'W50#', # prepend to converted message
  901. clientmodule => 'SD_WS', # not used now
  902. modulematch => '^W50#.*', # not used now
  903. length_min => '47',
  904. length_max => '48',
  905. },
  906. );
  907. sub
  908. SIGNALduino_Initialize($)
  909. {
  910. my ($hash) = @_;
  911. require "$attr{global}{modpath}/FHEM/DevIo.pm";
  912. # Provider
  913. $hash->{ReadFn} = "SIGNALduino_Read";
  914. $hash->{WriteFn} = "SIGNALduino_Write";
  915. $hash->{ReadyFn} = "SIGNALduino_Ready";
  916. # Normal devices
  917. $hash->{DefFn} = "SIGNALduino_Define";
  918. $hash->{FingerprintFn} = "SIGNALduino_FingerprintFn";
  919. $hash->{UndefFn} = "SIGNALduino_Undef";
  920. $hash->{GetFn} = "SIGNALduino_Get";
  921. $hash->{SetFn} = "SIGNALduino_Set";
  922. $hash->{AttrFn} = "SIGNALduino_Attr";
  923. $hash->{AttrList} =
  924. "Clients MatchList do_not_notify:1,0 dummy:1,0"
  925. ." hexFile"
  926. ." initCommands"
  927. ." flashCommand"
  928. ." hardware:nano328,uno,promini328"
  929. ." debug:0,1"
  930. ." longids"
  931. ." minsecs"
  932. ." whitelist_IDs"
  933. ." WS09_WSModel:undef,WH1080,CTW600"
  934. ." WS09_CRCAUS:0,1"
  935. ." $readingFnAttributes";
  936. $hash->{ShutdownFn} = "SIGNALduino_Shutdown";
  937. }
  938. sub
  939. SIGNALduino_FingerprintFn($$)
  940. {
  941. my ($name, $msg) = @_;
  942. # Store only the "relevant" part, as the Signalduino won't compute the checksum
  943. #$msg = substr($msg, 8) if($msg =~ m/^81/ && length($msg) > 8);
  944. return ($name, $msg);
  945. }
  946. #####################################
  947. sub
  948. SIGNALduino_Define($$)
  949. {
  950. my ($hash, $def) = @_;
  951. my @a = split("[ \t][ \t]*", $def);
  952. if(@a != 3) {
  953. my $msg = "wrong syntax: define <name> SIGNALduino {none | devicename[\@baudrate] | devicename\@directio | hostname:port}";
  954. Log3 undef, 2, $msg;
  955. return $msg;
  956. }
  957. DevIo_CloseDev($hash);
  958. my $name = $a[0];
  959. if (!exists &round)
  960. {
  961. Log3 $name, 1, "$name: Signalduino can't be activated (sub round not found). Please update Fhem via update command";
  962. return undef;
  963. }
  964. my $dev = $a[2];
  965. #Debug "dev: $dev" if ($debug);
  966. #my $hardware=AttrVal($name,"hardware","nano328");
  967. #Debug "hardware: $hardware" if ($debug);
  968. if($dev eq "none") {
  969. Log3 $name, 1, "$name: device is none, commands will be echoed only";
  970. $attr{$name}{dummy} = 1;
  971. #return undef;
  972. }
  973. if ($dev ne "none" && $dev =~ m/[a-zA-Z]/ && $dev !~ m/\@/) { # bei einer IP wird kein \@57600 angehaengt
  974. $dev .= "\@57600";
  975. }
  976. #$hash->{CMDS} = "";
  977. $hash->{Clients} = $clientsSIGNALduino;
  978. $hash->{MatchList} = \%matchListSIGNALduino;
  979. #if( !defined( $attr{$name}{hardware} ) ) {
  980. # $attr{$name}{hardware} = "nano328";
  981. #}
  982. if( !defined( $attr{$name}{flashCommand} ) ) {
  983. # $attr{$name}{flashCommand} = "avrdude -p atmega328P -c arduino -P [PORT] -D -U flash:w:[HEXFILE] 2>[LOGFILE]"
  984. $attr{$name}{flashCommand} = "avrdude -c arduino -b 57600 -P [PORT] -p atmega328p -vv -U flash:w:[HEXFILE] 2>[LOGFILE]"
  985. }
  986. $hash->{DeviceName} = $dev;
  987. my $ret=undef;
  988. my $whitelistIDs = AttrVal($name,"whitelist_IDs","");
  989. SIGNALduino_IdList($hash ,$name, $whitelistIDs);
  990. if($dev ne "none") {
  991. $ret = DevIo_OpenDev($hash, 0, "SIGNALduino_DoInit", 'SIGNALduino_Connect');
  992. } else {
  993. $hash->{DevState} = 'initialized';
  994. readingsSingleUpdate($hash, "state", "opened", 1);
  995. }
  996. $hash->{DMSG}="nothing";
  997. $hash->{TIME}=time();
  998. Log3 $name, 3, "$name: Firmwareversion: ".$hash->{READINGS}{version}{VAL} if ($hash->{READINGS}{version}{VAL});
  999. return $ret;
  1000. }
  1001. ###############################
  1002. sub SIGNALduino_Connect($$)
  1003. {
  1004. my ($hash, $err) = @_;
  1005. # damit wird die err-msg nur einmal ausgegeben
  1006. if (!defined($hash->{disConnFlag}) && $err) {
  1007. Log3($hash, 3, "SIGNALduino $hash->{NAME}: ${err}");
  1008. $hash->{disConnFlag} = 1;
  1009. }
  1010. }
  1011. #####################################
  1012. sub
  1013. SIGNALduino_Undef($$)
  1014. {
  1015. my ($hash, $arg) = @_;
  1016. my $name = $hash->{NAME};
  1017. foreach my $d (sort keys %defs) {
  1018. if(defined($defs{$d}) &&
  1019. defined($defs{$d}{IODev}) &&
  1020. $defs{$d}{IODev} == $hash)
  1021. {
  1022. my $lev = ($reread_active ? 4 : 2);
  1023. Log3 $name, $lev, "$name: deleting port for $d";
  1024. delete $defs{$d}{IODev};
  1025. }
  1026. }
  1027. SIGNALduino_Shutdown($hash);
  1028. DevIo_CloseDev($hash);
  1029. RemoveInternalTimer($hash);
  1030. return undef;
  1031. }
  1032. #####################################
  1033. sub
  1034. SIGNALduino_Shutdown($)
  1035. {
  1036. my ($hash) = @_;
  1037. #DevIo_SimpleWrite($hash, "XQ\n",2);
  1038. SIGNALduino_SimpleWrite($hash, "XQ"); # Switch reception off, it may hang up the SIGNALduino
  1039. return undef;
  1040. }
  1041. #####################################
  1042. #$hash,$name,"sendmsg","P17;R6#".substr($arg,2)
  1043. sub
  1044. SIGNALduino_Set($@)
  1045. {
  1046. my ($hash, @a) = @_;
  1047. return "\"set SIGNALduino\" needs at least one parameter" if(@a < 2);
  1048. if (!defined($sets{$a[1]})) {
  1049. my $arguments = ' ';
  1050. foreach my $arg (sort keys %sets) {
  1051. $arguments.= $arg . ($sets{$arg} ? (':' . $sets{$arg}) : '') . ' ';
  1052. }
  1053. #Log3 $hash, 3, "set arg = $arguments";
  1054. return "Unknown argument $a[1], choose one of " . $arguments;
  1055. }
  1056. my $name = shift @a;
  1057. my $cmd = shift @a;
  1058. my $arg = join(" ", @a);
  1059. return "$name is not active, may firmware is not suppoted, please flash or reset" if ($cmd ne 'reset' && $cmd ne 'flash' && exists($hash->{DevState}) && $hash->{DevState} ne 'initialized');
  1060. if($cmd eq "raw") {
  1061. Log3 $name, 4, "set $name $cmd $arg";
  1062. #SIGNALduino_SimpleWrite($hash, $arg);
  1063. SIGNALduino_AddSendQueue($hash,$arg);
  1064. } elsif( $cmd eq "flash" ) {
  1065. my @args = split(' ', $arg);
  1066. my $log = "";
  1067. my $hexFile = "";
  1068. my @deviceName = split('@', $hash->{DeviceName});
  1069. my $port = $deviceName[0];
  1070. my $hardware=AttrVal($name,"hardware","nano328");
  1071. my $defaultHexFile = "./FHEM/firmware/$hash->{TYPE}_$hardware.hex";
  1072. my $logFile = AttrVal("global", "logdir", "./log/") . "$hash->{TYPE}-Flash.log";
  1073. if(!$arg || $args[0] !~ m/^(\w|\/|.)+$/) {
  1074. $hexFile = AttrVal($name, "hexFile", "");
  1075. if ($hexFile eq "") {
  1076. $hexFile = $defaultHexFile;
  1077. }
  1078. }
  1079. else {
  1080. $hexFile = $args[0];
  1081. }
  1082. return "Usage: set $name flash [filename]\n\nor use the hexFile attribute" if($hexFile !~ m/^(\w|\/|.)+$/);
  1083. $log .= "flashing Arduino $name\n";
  1084. $log .= "hex file: $hexFile\n";
  1085. $log .= "port: $port\n";
  1086. $log .= "log file: $logFile\n";
  1087. my $flashCommand = AttrVal($name, "flashCommand", "");
  1088. if($flashCommand ne "") {
  1089. if (-e $logFile) {
  1090. unlink $logFile;
  1091. }
  1092. DevIo_CloseDev($hash);
  1093. $hash->{STATE} = "disconnected";
  1094. $log .= "$name closed\n";
  1095. my $avrdude = $flashCommand;
  1096. $avrdude =~ s/\Q[PORT]\E/$port/g;
  1097. $avrdude =~ s/\Q[HEXFILE]\E/$hexFile/g;
  1098. $avrdude =~ s/\Q[LOGFILE]\E/$logFile/g;
  1099. $log .= "command: $avrdude\n\n";
  1100. `$avrdude`;
  1101. local $/=undef;
  1102. if (-e $logFile) {
  1103. open FILE, $logFile;
  1104. my $logText = <FILE>;
  1105. close FILE;
  1106. $log .= "--- AVRDUDE ---------------------------------------------------------------------------------\n";
  1107. $log .= $logText;
  1108. $log .= "--- AVRDUDE ---------------------------------------------------------------------------------\n\n";
  1109. }
  1110. else {
  1111. $log .= "WARNING: avrdude created no log file\n\n";
  1112. }
  1113. }
  1114. else {
  1115. $log .= "\n\nNo flashCommand found. Please define this attribute.\n\n";
  1116. }
  1117. DevIo_OpenDev($hash, 0, "SIGNALduino_DoInit", 'SIGNALduino_Connect');
  1118. $log .= "$name opened\n";
  1119. return $log;
  1120. } elsif ($cmd =~ m/reset/i) {
  1121. delete($hash->{initResetFlag}) if defined($hash->{initResetFlag});
  1122. return SIGNALduino_ResetDevice($hash);
  1123. } elsif( $cmd eq "close" ) {
  1124. $hash->{DevState} = 'closed';
  1125. return SIGNALduino_CloseDevice($hash);
  1126. } elsif( $cmd eq "ITClock" ) {
  1127. Log3 $name, 4, "set $name $cmd $arg";
  1128. my $clock = shift @a;
  1129. $clock=250 if ($clock eq "" );
  1130. return "argument $arg is not numeric" if($clock !~ /^\d+$/);
  1131. Log3 $name, 3, "$name: Setting ITClock to $clock (sending $arg)";
  1132. $arg="ic$clock";
  1133. #SIGNALduino_SimpleWrite($hash, $arg);
  1134. SIGNALduino_AddSendQueue($hash,$arg);
  1135. $hash->{$cmd}=$clock;
  1136. } elsif( $cmd eq "disableMessagetype" ) {
  1137. my $argm = 'CD' . substr($arg,-1,1);
  1138. #SIGNALduino_SimpleWrite($hash, $argm);
  1139. SIGNALduino_AddSendQueue($hash,$argm);
  1140. Log3 $name, 4, "set $name $cmd $arg $argm";;
  1141. } elsif( $cmd eq "enableMessagetype" ) {
  1142. my $argm = 'CE' . substr($arg,-1,1);
  1143. #SIGNALduino_SimpleWrite($hash, $argm);
  1144. SIGNALduino_AddSendQueue($hash,$argm);
  1145. Log3 $name, 4, "set $name $cmd $arg $argm";
  1146. } elsif( $cmd eq "sendMsg" ) {
  1147. my ($protocol,$data,$repeats,$clock) = split("#",$arg);
  1148. $protocol=~ s/[Pp](\d+)/$1/; # extract protocol num
  1149. $repeats=~ s/[rR](\d+)/$1/; # extract repeat num
  1150. $clock=~ s/[Cc](\d+)/$1/ if (defined($clock)); # extract ITClock num
  1151. $repeats=1 if (!defined($repeats));
  1152. return "$name: sendmsg, unknown protocol: $protocol" if (!exists($ProtocolListSIGNALduino{$protocol}));
  1153. #print ("data = $data \n");
  1154. #print ("protocol = $protocol \n");
  1155. #print ("repeats = $repeats \n");
  1156. my %signalHash;
  1157. my %patternHash;
  1158. my $pattern="";
  1159. my $cnt=0;
  1160. my $sendData;
  1161. if ($ProtocolListSIGNALduino{$protocol}{format} eq 'manchester')
  1162. {
  1163. #$clock = (map { $clock += $_ } @{$ProtocolListSIGNALduino{$protocol}{clockrange}}) / 2 if (!defined($clock));
  1164. $clock += $_ for(@{$ProtocolListSIGNALduino{$protocol}{clockrange}});
  1165. $clock = round($clock/2,0);
  1166. if ($protocol == 43) {
  1167. #$data =~ tr/0123456789ABCDEF/FEDCBA9876543210/;
  1168. }
  1169. my $intro = "";
  1170. my $outro = "";
  1171. $intro = $ProtocolListSIGNALduino{$protocol}{msgIntro} if ($ProtocolListSIGNALduino{$protocol}{msgIntro});
  1172. $outro = $ProtocolListSIGNALduino{$protocol}{msgOutro}.";" if ($ProtocolListSIGNALduino{$protocol}{msgOutro});
  1173. if ($intro ne "" || $outro ne "")
  1174. {
  1175. $intro = "SC;R=$repeats;" . $intro;
  1176. $repeats = 0;
  1177. }
  1178. $sendData = $intro . "SM;" . ($repeats > 0 ? "R=$repeats;" : "") . "C=$clock;D=$data;" . $outro; # SM;R=2;C=400;D=AFAFAF;
  1179. Log3 $name, 5, "$name: sendmsg Preparing manchester protocol=$protocol, repeats=$repeats, clock=$clock data=$data";
  1180. } else {
  1181. if ($protocol == 3 || substr($data,0,2) eq "is") {
  1182. if (substr($data,0,2) eq "is") {
  1183. $data = substr($data,2); # is am Anfang entfernen
  1184. }
  1185. $data = SIGNALduino_ITV1_tristateToBit($data);
  1186. Log3 $name, 5, "$name: sendmsg IT V1 convertet tristate to bits=$data";
  1187. }
  1188. if (!defined($clock)) {
  1189. $hash->{ITClock} = 250 if (!defined($hash->{ITClock})); # Todo: Klaeren wo ITClock verwendet wird und ob wir diesen Teil nicht auf Protokoll 3,4 und 17 minimieren
  1190. $clock=$ProtocolListSIGNALduino{$protocol}{clockabs} > 1 ?$ProtocolListSIGNALduino{$protocol}{clockabs}:$hash->{ITClock};
  1191. }
  1192. Log3 $name, 5, "$name: sendmsg Preparing rawsend command for protocol=$protocol, repeats=$repeats, clock=$clock bits=$data";
  1193. foreach my $item (qw(sync start one zero float))
  1194. {
  1195. #print ("item= $item \n");
  1196. next if (!exists($ProtocolListSIGNALduino{$protocol}{$item}));
  1197. foreach my $p (@{$ProtocolListSIGNALduino{$protocol}{$item}})
  1198. {
  1199. #print (" p = $p \n");
  1200. if (!exists($patternHash{$p}))
  1201. {
  1202. $patternHash{$p}=$cnt;
  1203. $pattern.="P".$patternHash{$p}."=".$p*$clock.";";
  1204. $cnt++;
  1205. }
  1206. $signalHash{$item}.=$patternHash{$p};
  1207. #print (" signalHash{$item} = $signalHash{$item} \n");
  1208. }
  1209. }
  1210. my @bits = split("", $data);
  1211. my %bitconv = (1=>"one", 0=>"zero", 'D'=> "float");
  1212. my $SignalData="D=";
  1213. $SignalData.=$signalHash{sync} if (exists($signalHash{sync}));
  1214. $SignalData.=$signalHash{start} if (exists($signalHash{start}));
  1215. foreach my $bit (@bits)
  1216. {
  1217. next if (!exists($bitconv{$bit}));
  1218. #Log3 $name, 5, "encoding $bit";
  1219. $SignalData.=$signalHash{$bitconv{$bit}}; ## Add the signal to our data string
  1220. }
  1221. $sendData = "SR;R=$repeats;$pattern$SignalData;";
  1222. }
  1223. #SIGNALduino_SimpleWrite($hash, $sendData);
  1224. SIGNALduino_AddSendQueue($hash,$sendData);
  1225. Log3 $name, 4, "$name/set: sending via SendMsg: $sendData";
  1226. } else {
  1227. Log3 $name, 5, "$name/set: set $name $cmd $arg";
  1228. #SIGNALduino_SimpleWrite($hash, $arg);
  1229. return "Unknown argument $cmd, choose one of ". ReadingsVal($name,'cmd',' help me');
  1230. }
  1231. return undef;
  1232. }
  1233. #####################################
  1234. sub
  1235. SIGNALduino_Get($@)
  1236. {
  1237. my ($hash, @a) = @_;
  1238. my $type = $hash->{TYPE};
  1239. my $name = $hash->{NAME};
  1240. return "$name is not active, may firmware is not suppoted, please flash or reset" if (exists($hash->{DevState}) && $hash->{DevState} ne 'initialized');
  1241. #my $name = $a[0];
  1242. Log3 $name, 5, "\"get $type\" needs at least one parameter" if(@a < 2);
  1243. return "\"get $type\" needs at least one parameter" if(@a < 2);
  1244. if(!defined($gets{$a[1]})) {
  1245. my @cList = map { $_ =~ m/^(file|raw)$/ ? $_ : "$_:noArg" } sort keys %gets;
  1246. return "Unknown argument $a[1], choose one of " . join(" ", @cList);
  1247. }
  1248. my $arg = ($a[2] ? $a[2] : "");
  1249. return "no command to send, get aborted." if (length($gets{$a[1]}[0]) == 0 && length($arg) == 0);
  1250. my ($msg, $err);
  1251. if (IsDummy($name))
  1252. {
  1253. if ($arg =~ /^M[CSU];.*/)
  1254. {
  1255. $arg="\002$arg\003"; ## Add start end end marker if not already there
  1256. Log3 $name, 5, "$name/msg adding start and endmarker to message";
  1257. }
  1258. if ($arg =~ /\002M.;.*;\003$/)
  1259. {
  1260. Log3 $name, 4, "$name/msg get raw: $arg";
  1261. return SIGNALduino_Parse($hash, $hash, $hash->{NAME}, $arg);
  1262. }
  1263. else {
  1264. my $arg2 = "";
  1265. if ($arg =~ m/^version=/) { # set version
  1266. $arg2 = substr($arg,8);
  1267. $hash->{version} = "V " . $arg2;
  1268. }
  1269. elsif ($arg =~ m/^regexp=/) { # set fileRegexp for get raw messages from file
  1270. $arg2 = substr($arg,7);
  1271. $hash->{fileRegexp} = $arg2;
  1272. delete($hash->{fileRegexp}) if (!$arg2);
  1273. }
  1274. elsif ($arg =~ m/^file=/) {
  1275. $arg2 = substr($arg,5);
  1276. my $n = 0;
  1277. if (open(my $fh, '<', $arg2)) {
  1278. my $fileRegexp = $hash->{fileRegexp};
  1279. while (my $row = <$fh>) {
  1280. if ($row =~ /.*\002M.;.*;\003$/) {
  1281. chomp $row;
  1282. $row =~ s/.*\002(M.;.*;)\003/$1/;
  1283. if (!defined($fileRegexp) || $row =~ m/$fileRegexp/) {
  1284. $n += 1;
  1285. $row="\002$row\003";
  1286. Log3 $name, 4, "$name/msg fileGetRaw: $row";
  1287. SIGNALduino_Parse($hash, $hash, $hash->{NAME}, $row);
  1288. }
  1289. }
  1290. }
  1291. return $n . " raw Nachrichten eingelesen";
  1292. } else {
  1293. return "Could not open file $arg2";
  1294. }
  1295. }
  1296. elsif ($arg eq '?') {
  1297. my $ret;
  1298. $ret = "dummy get raw\n\n";
  1299. $ret .= "raw message e.g. MS;P0=-392;P1=...\n";
  1300. $ret .= "dispatch message e.g. P7#6290DCF37\n";
  1301. $ret .= "version=x.x.x sets version. e.g. (version=3.2.0) to get old MC messages\n";
  1302. $ret .= "regexp= set fileRegexp for get raw messages from file. e.g. regexp=^MC\n";
  1303. $ret .= "file= gets raw messages from file in the fhem directory\n";
  1304. return $ret;
  1305. }
  1306. else {
  1307. Log3 $name, 4, "$name/msg get dispatch: $arg";
  1308. Dispatch($hash, $arg, undef);
  1309. }
  1310. return "";
  1311. }
  1312. }
  1313. return "No $a[1] for dummies" if(IsDummy($name));
  1314. Log3 $name, 5, "$name: command for gets: " . $gets{$a[1]}[0] . " " . $arg;
  1315. if ($a[1] eq "raw")
  1316. {
  1317. # Dirty hack to check and modify direct communication from logical modules with hardware
  1318. if ($arg =~ /^is.*/ && length($arg) == 34)
  1319. {
  1320. # Arctec protocol
  1321. Log3 $name, 5, "$name: calling set :sendmsg P17;R6#".substr($arg,2);
  1322. SIGNALduino_Set($hash,$name,"sendMsg","P17#",substr($arg,2),"#R6");
  1323. return "$a[0] $a[1] => $arg";
  1324. }
  1325. }
  1326. elsif ($a[1] eq "protocolIDs")
  1327. {
  1328. my $id;
  1329. my $ret;
  1330. my $s;
  1331. my $moduleId;
  1332. my @IdList = ();
  1333. foreach $id (keys %ProtocolListSIGNALduino)
  1334. {
  1335. next if ($id eq 'id');
  1336. push (@IdList, $id);
  1337. }
  1338. @IdList = sort { $a <=> $b } @IdList;
  1339. $ret = " ID modulname protocolname # comment\n\n";
  1340. foreach $id (@IdList)
  1341. {
  1342. $ret .= sprintf("%3s",$id) . " ";
  1343. if (exists ($ProtocolListSIGNALduino{$id}{format}) && $ProtocolListSIGNALduino{$id}{format} eq "manchester")
  1344. {
  1345. $ret .= "MC";
  1346. }
  1347. elsif (exists $ProtocolListSIGNALduino{$id}{sync})
  1348. {
  1349. $ret .= "MS";
  1350. }
  1351. elsif (exists ($ProtocolListSIGNALduino{$id}{clockabs}))
  1352. {
  1353. $ret .= "MU";
  1354. }
  1355. if (exists ($ProtocolListSIGNALduino{$id}{clientmodule}))
  1356. {
  1357. $moduleId .= "$id,";
  1358. $s = $ProtocolListSIGNALduino{$id}{clientmodule};
  1359. if (length($s) < 15)
  1360. {
  1361. $s .= substr(" ",length($s) - 15);
  1362. }
  1363. $ret .= " $s";
  1364. }
  1365. else
  1366. {
  1367. $ret .= " ";
  1368. }
  1369. if (exists ($ProtocolListSIGNALduino{$id}{name}))
  1370. {
  1371. $ret .= " $ProtocolListSIGNALduino{$id}{name}";
  1372. }
  1373. if (exists ($ProtocolListSIGNALduino{$id}{comment}))
  1374. {
  1375. $ret .= " # $ProtocolListSIGNALduino{$id}{comment}";
  1376. }
  1377. $ret .= "\n";
  1378. }
  1379. $moduleId =~ s/,$//;
  1380. return "$a[1]: \n\n$ret\nIds with modules: $moduleId";
  1381. }
  1382. #SIGNALduino_SimpleWrite($hash, $gets{$a[1]}[0] . $arg);
  1383. SIGNALduino_AddSendQueue($hash, $gets{$a[1]}[0] . $arg);
  1384. $hash->{getcmd}->{cmd}=$a[1];
  1385. $hash->{getcmd}->{asyncOut}=$hash->{CL};
  1386. $hash->{getcmd}->{timenow}=time();
  1387. return undef; # We will exit here, and give an output only, if asny output is supported. If this is not supported, only the readings are updated
  1388. }
  1389. sub SIGNALduino_parseResponse($$$)
  1390. {
  1391. my $hash = shift;
  1392. my $cmd = shift;
  1393. my $msg = shift;
  1394. my $name=$hash->{NAME};
  1395. $msg =~ s/[\r\n]//g;
  1396. if($cmd eq "cmds")
  1397. { # nice it up
  1398. $msg =~ s/$name cmds =>//g;
  1399. $msg =~ s/.*Use one of//g;
  1400. }
  1401. elsif($cmd eq "uptime")
  1402. { # decode it
  1403. #$msg = hex($msg); # /125; only for col or coc
  1404. $msg = sprintf("%d %02d:%02d:%02d", $msg/86400, ($msg%86400)/3600, ($msg%3600)/60, $msg%60);
  1405. }
  1406. return $msg;
  1407. }
  1408. #####################################
  1409. sub
  1410. SIGNALduino_ResetDevice($)
  1411. {
  1412. my ($hash) = @_;
  1413. my $name = $hash->{NAME};
  1414. Log3 $hash, 3, "$name reset";
  1415. DevIo_CloseDev($hash);
  1416. my $ret = DevIo_OpenDev($hash, 0, "SIGNALduino_DoInit", 'SIGNALduino_Connect');
  1417. return $ret;
  1418. }
  1419. #####################################
  1420. sub
  1421. SIGNALduino_CloseDevice($)
  1422. {
  1423. my ($hash) = @_;
  1424. my $name = $hash->{NAME};
  1425. Log3 $hash, 2, "$name closed";
  1426. RemoveInternalTimer($hash);
  1427. DevIo_CloseDev($hash);
  1428. readingsSingleUpdate($hash, "state", "closed", 1);
  1429. return undef;
  1430. }
  1431. #####################################
  1432. sub
  1433. SIGNALduino_DoInit($)
  1434. {
  1435. my $hash = shift;
  1436. my $name = $hash->{NAME};
  1437. my $err;
  1438. my $msg = undef;
  1439. my ($ver, $try) = ("", 0);
  1440. #Dirty hack to allow initialisation of DirectIO Device for some debugging and tesing
  1441. Log3 $hash, 1, "$name/define: ".$hash->{DEF};
  1442. delete($hash->{disConnFlag}) if defined($hash->{disConnFlag});
  1443. RemoveInternalTimer("HandleWriteQueue:$name");
  1444. @{$hash->{QUEUE}} = ();
  1445. if (($hash->{DEF} !~ m/\@DirectIO/) and ($hash->{DEF} !~ m/none/) )
  1446. {
  1447. Log3 $hash, 1, "$name/init: ".$hash->{DEF};
  1448. $hash->{initretry} = 0;
  1449. RemoveInternalTimer($hash);
  1450. #SIGNALduino_SimpleWrite($hash, "XQ"); # Disable receiver
  1451. InternalTimer(gettimeofday() + SDUINO_INIT_WAIT_XQ, "SIGNALduino_SimpleWrite_XQ", $hash, 0);
  1452. InternalTimer(gettimeofday() + SDUINO_INIT_WAIT, "SIGNALduino_StartInit", $hash, 0);
  1453. }
  1454. # Reset the counter
  1455. delete($hash->{XMIT_TIME});
  1456. delete($hash->{NR_CMD_LAST_H});
  1457. return;
  1458. #return undef;
  1459. }
  1460. # Disable receiver
  1461. sub SIGNALduino_SimpleWrite_XQ($) {
  1462. my ($hash) = @_;
  1463. my $name = $hash->{NAME};
  1464. Log3 $hash, 3, "$name/init: disable receiver (XQ)";
  1465. SIGNALduino_SimpleWrite($hash, "XQ");
  1466. #DevIo_SimpleWrite($hash, "XQ\n",2);
  1467. }
  1468. sub SIGNALduino_StartInit($)
  1469. {
  1470. my ($hash) = @_;
  1471. my $name = $hash->{NAME};
  1472. $hash->{version} = undef;
  1473. Log3 $name,3 , "$name/init: get version, retry = " . $hash->{initretry};
  1474. if ($hash->{initretry} >= SDUINO_INIT_MAXRETRY) {
  1475. $hash->{DevState} = 'INACTIVE';
  1476. # einmaliger reset, wenn danach immer noch 'init retry count reached', dann SIGNALduino_CloseDevice()
  1477. if (!defined($hash->{initResetFlag})) {
  1478. Log3 $name,2 , "$name/init retry count reached. Reset";
  1479. $hash->{initResetFlag} = 1;
  1480. SIGNALduino_ResetDevice($hash);
  1481. } else {
  1482. Log3 $name,2 , "$name/init retry count reached. Closed";
  1483. SIGNALduino_CloseDevice($hash);
  1484. }
  1485. return;
  1486. }
  1487. else {
  1488. $hash->{getcmd}->{cmd} = "version";
  1489. SIGNALduino_SimpleWrite($hash, "V");
  1490. #DevIo_SimpleWrite($hash, "V\n",2);
  1491. $hash->{DevState} = 'waitInit';
  1492. RemoveInternalTimer($hash);
  1493. InternalTimer(gettimeofday() + SDUINO_CMD_TIMEOUT, "SIGNALduino_CheckCmdResp", $hash, 0);
  1494. }
  1495. }
  1496. ####################
  1497. sub SIGNALduino_CheckCmdResp($)
  1498. {
  1499. my ($hash) = @_;
  1500. my $name = $hash->{NAME};
  1501. my $msg = undef;
  1502. my $ver;
  1503. if ($hash->{version}) {
  1504. $ver = $hash->{version};
  1505. if ($ver !~ m/SIGNAL(duino|ESP)/) {
  1506. $msg = "$name: Not an SIGNALduino device, setting attribute dummy=1 got for V: $ver";
  1507. Log3 $hash, 1, $msg;
  1508. readingsSingleUpdate($hash, "state", "no SIGNALduino found", 1);
  1509. $hash->{DevState} = 'INACTIVE';
  1510. SIGNALduino_CloseDevice($hash);
  1511. }
  1512. elsif($ver =~ m/^V 3\.1\./) {
  1513. $msg = "$name: Version of your arduino is not compatible, pleas flash new firmware. (device closed) Got for V: $ver";
  1514. readingsSingleUpdate($hash, "state", "unsupported firmware found", 1);
  1515. Log3 $hash, 1, $msg;
  1516. $hash->{DevState} = 'INACTIVE';
  1517. SIGNALduino_CloseDevice($hash);
  1518. }
  1519. else {
  1520. readingsSingleUpdate($hash, "state", "opened", 1);
  1521. Log3 $name, 2, "$name: initialized. " . SDUINO_VERSION;
  1522. $hash->{DevState} = 'initialized';
  1523. delete($hash->{initResetFlag}) if defined($hash->{initResetFlag});
  1524. SIGNALduino_SimpleWrite($hash, "XE"); # Enable receiver
  1525. #DevIo_SimpleWrite($hash, "XE\n",2);
  1526. Log3 $hash, 3, "$name/init: enable receiver (XE)";
  1527. delete($hash->{initretry});
  1528. # initialize keepalive
  1529. $hash->{keepalive}{ok} = 0;
  1530. $hash->{keepalive}{retry} = 0;
  1531. InternalTimer(gettimeofday() + SDUINO_KEEPALIVE_TIMEOUT, "SIGNALduino_KeepAlive", $hash, 0);
  1532. }
  1533. }
  1534. else {
  1535. delete($hash->{getcmd});
  1536. $hash->{initretry} ++;
  1537. #InternalTimer(gettimeofday()+1, "SIGNALduino_StartInit", $hash, 0);
  1538. SIGNALduino_StartInit($hash);
  1539. }
  1540. }
  1541. #####################################
  1542. # Check if the 1% limit is reached and trigger notifies
  1543. sub
  1544. SIGNALduino_XmitLimitCheck($$)
  1545. {
  1546. my ($hash,$fn) = @_;
  1547. return if ($fn !~ m/^(is|SR).*/);
  1548. my $now = time();
  1549. if(!$hash->{XMIT_TIME}) {
  1550. $hash->{XMIT_TIME}[0] = $now;
  1551. $hash->{NR_CMD_LAST_H} = 1;
  1552. return;
  1553. }
  1554. my $nowM1h = $now-3600;
  1555. my @b = grep { $_ > $nowM1h } @{$hash->{XMIT_TIME}};
  1556. if(@b > 163) { # Maximum nr of transmissions per hour (unconfirmed).
  1557. my $name = $hash->{NAME};
  1558. Log3 $name, 2, "SIGNALduino TRANSMIT LIMIT EXCEEDED";
  1559. DoTrigger($name, "TRANSMIT LIMIT EXCEEDED");
  1560. } else {
  1561. push(@b, $now);
  1562. }
  1563. $hash->{XMIT_TIME} = \@b;
  1564. $hash->{NR_CMD_LAST_H} = int(@b);
  1565. }
  1566. #####################################
  1567. ## API to logical modules: Provide as Hash of IO Device, type of function ; command to call ; message to send
  1568. sub
  1569. SIGNALduino_Write($$$)
  1570. {
  1571. my ($hash,$fn,$msg) = @_;
  1572. my $name = $hash->{NAME};
  1573. $fn="RAW" if $fn eq "";
  1574. Log3 $name, 5, "$name/write: adding to queue $fn $msg";
  1575. #SIGNALduino_SimpleWrite($hash, $bstring);
  1576. SIGNALduino_Set($hash,$name,$fn,$msg);
  1577. #SIGNALduino_AddSendQueue($hash,$bstring);
  1578. }
  1579. sub SIGNALduino_AddSendQueue($$)
  1580. {
  1581. my ($hash, $msg) = @_;
  1582. my $name = $hash->{NAME};
  1583. #Log3 $hash, 3,"AddSendQueue: " . $hash->{NAME} . ": $msg";
  1584. push(@{$hash->{QUEUE}}, $msg);
  1585. #Log3 $hash , 5, Dumper($hash->{QUEUE});
  1586. InternalTimer(gettimeofday() + 0.1, "SIGNALduino_HandleWriteQueue", "HandleWriteQueue:$name") if (@{$hash->{QUEUE}} == 1);
  1587. }
  1588. sub
  1589. SIGNALduino_SendFromQueue($$)
  1590. {
  1591. my ($hash, $msg) = @_;
  1592. my $name = $hash->{NAME};
  1593. if($msg ne "") {
  1594. SIGNALduino_XmitLimitCheck($hash,$msg);
  1595. #DevIo_SimpleWrite($hash, $msg . "\n", 2);
  1596. SIGNALduino_SimpleWrite($hash,$msg);
  1597. if ($msg =~ m/^S(R|C|M);/) {
  1598. $hash->{getcmd}->{cmd} = 'sendraw';
  1599. Log3 $hash, 4, "$name SendFromQueue: msg=$msg"; # zu testen der Queue, kann wenn es funktioniert auskommentiert werden
  1600. }
  1601. }
  1602. ##############
  1603. # Write the next buffer not earlier than 0.23 seconds
  1604. # else it will be sent too early by the SIGNALduino, resulting in a collision, or may the last command is not finished
  1605. if (defined($hash->{getcmd}->{cmd}) && $hash->{getcmd}->{cmd} eq 'sendraw') {
  1606. InternalTimer(gettimeofday() + SDUINO_WRITEQUEUE_TIMEOUT, "SIGNALduino_HandleWriteQueue", "HandleWriteQueue:$name");
  1607. } else {
  1608. InternalTimer(gettimeofday() + SDUINO_WRITEQUEUE_NEXT, "SIGNALduino_HandleWriteQueue", "HandleWriteQueue:$name");
  1609. }
  1610. }
  1611. ####################################
  1612. sub
  1613. SIGNALduino_HandleWriteQueue($)
  1614. {
  1615. my($param) = @_;
  1616. my(undef,$name) = split(':', $param);
  1617. my $hash = $defs{$name};
  1618. #my @arr = @{$hash->{QUEUE}};
  1619. if (defined($hash->{getcmd}->{cmd}) && $hash->{getcmd}->{cmd} eq 'sendraw') {
  1620. Log3 $name, 4, "$name/HandleWriteQueue: sendraw no answer (timeout)";
  1621. delete($hash->{getcmd});
  1622. }
  1623. if(@{$hash->{QUEUE}}) {
  1624. my $msg= shift(@{$hash->{QUEUE}});
  1625. if($msg eq "") {
  1626. SIGNALduino_HandleWriteQueue("x:$name");
  1627. } else {
  1628. SIGNALduino_SendFromQueue($hash, $msg);
  1629. }
  1630. } else {
  1631. Log3 $name, 4, "$name/HandleWriteQueue: nothing to send, stopping timer";
  1632. RemoveInternalTimer("HandleWriteQueue:$name");
  1633. }
  1634. }
  1635. #####################################
  1636. # called from the global loop, when the select for hash->{FD} reports data
  1637. sub
  1638. SIGNALduino_Read($)
  1639. {
  1640. my ($hash) = @_;
  1641. my $buf = DevIo_SimpleRead($hash);
  1642. return "" if(!defined($buf));
  1643. my $name = $hash->{NAME};
  1644. my $debug = AttrVal($name,"debug",0);
  1645. my $SIGNALduinodata = $hash->{PARTIAL};
  1646. Log3 $name, 5, "$name/RAW READ: $SIGNALduinodata/$buf" if ($debug);
  1647. $SIGNALduinodata .= $buf;
  1648. while($SIGNALduinodata =~ m/\n/) {
  1649. my $rmsg;
  1650. ($rmsg,$SIGNALduinodata) = split("\n", $SIGNALduinodata, 2);
  1651. $rmsg =~ s/\r//;
  1652. Log3 $name, 4, "$name/msg READ: $rmsg";
  1653. if ( $rmsg && !SIGNALduino_Parse($hash, $hash, $name, $rmsg) && defined($hash->{getcmd}) && defined($hash->{getcmd}->{cmd}))
  1654. {
  1655. my $regexp;
  1656. if ($hash->{getcmd}->{cmd} eq 'sendraw') {
  1657. $regexp = '^S(R|C|M);';
  1658. }
  1659. else {
  1660. $regexp = $gets{$hash->{getcmd}->{cmd}}[1];
  1661. }
  1662. if(!defined($regexp) || $rmsg =~ m/$regexp/) {
  1663. if (defined($hash->{keepalive})) {
  1664. $hash->{keepalive}{ok} = 1;
  1665. $hash->{keepalive}{retry} = 0;
  1666. }
  1667. Log3 $name, 5, "$name/msg READ: regexp=$regexp cmd=$hash->{getcmd}->{cmd} msg=$rmsg";
  1668. if ($hash->{getcmd}->{cmd} eq 'version') {
  1669. my $msg_start = index($rmsg, 'V 3.');
  1670. if ($msg_start > 0) {
  1671. $rmsg = substr($rmsg, $msg_start);
  1672. Log3 $name, 4, "$name/read: cut chars at begin. msgstart = $msg_start msg = $rmsg";
  1673. }
  1674. $hash->{version} = $rmsg;
  1675. if (defined($hash->{DevState}) && $hash->{DevState} eq 'waitInit') {
  1676. RemoveInternalTimer($hash);
  1677. SIGNALduino_CheckCmdResp($hash);
  1678. }
  1679. }
  1680. if ($hash->{getcmd}->{cmd} eq 'sendraw') {
  1681. # zu testen der sendeQueue, kann wenn es funktioniert auf verbose 5
  1682. Log3 $name, 4, "$name/read sendraw answer: $rmsg";
  1683. delete($hash->{getcmd});
  1684. RemoveInternalTimer("HandleWriteQueue:$name");
  1685. SIGNALduino_HandleWriteQueue("x:$name");
  1686. }
  1687. else {
  1688. $rmsg = SIGNALduino_parseResponse($hash,$hash->{getcmd}->{cmd},$rmsg);
  1689. readingsSingleUpdate($hash, $hash->{getcmd}->{cmd}, $rmsg, 0);
  1690. if (defined($hash->{getcmd}->{asyncOut})) {
  1691. #Log3 $name, 4, "$name/msg READ: asyncOutput";
  1692. my $ao = asyncOutput( $hash->{getcmd}->{asyncOut}, $hash->{getcmd}->{cmd}.": " . $rmsg );
  1693. }
  1694. delete($hash->{getcmd});
  1695. }
  1696. } else {
  1697. Log3 $name, 4, "$name/msg READ: Received answer ($rmsg) for ". $hash->{getcmd}->{cmd}." does not match $regexp";
  1698. }
  1699. }
  1700. }
  1701. $hash->{PARTIAL} = $SIGNALduinodata;
  1702. }
  1703. sub SIGNALduino_KeepAlive($){
  1704. my ($hash) = @_;
  1705. my $name = $hash->{NAME};
  1706. return if ($hash->{DevState} eq 'disconnected');
  1707. Log3 $name,4 , "$name/KeepAliveOk: " . $hash->{keepalive}{ok};
  1708. if (!$hash->{keepalive}{ok}) {
  1709. delete($hash->{getcmd});
  1710. if ($hash->{keepalive}{retry} >= SDUINO_KEEPALIVE_MAXRETRY) {
  1711. Log3 $name,4 , "$name/keepalive retry count reached. Reset";
  1712. $hash->{DevState} = 'INACTIVE';
  1713. SIGNALduino_ResetDevice($hash);
  1714. return;
  1715. }
  1716. else {
  1717. $hash->{keepalive}{retry} ++;
  1718. Log3 $name,3 , "$name/KeepAliveOk: " . $hash->{keepalive}{ok} . " retry = " . $hash->{keepalive}{retry} . " -> get ping";
  1719. $hash->{getcmd}->{cmd} = "ping";
  1720. SIGNALduino_AddSendQueue($hash, "P");
  1721. #SIGNALduino_SimpleWrite($hash, "P");
  1722. }
  1723. }
  1724. Log3 $name,4 , "$name/keepalive retry = " . $hash->{keepalive}{retry};
  1725. $hash->{keepalive}{ok} = 0;
  1726. InternalTimer(gettimeofday() + SDUINO_KEEPALIVE_TIMEOUT, "SIGNALduino_KeepAlive", $hash);
  1727. }
  1728. ### Helper Subs >>>
  1729. sub SIGNALduino_splitMsg
  1730. {
  1731. my $txt = shift;
  1732. my $delim = shift;
  1733. my @msg_parts = split(/$delim/,$txt);
  1734. return @msg_parts;
  1735. }
  1736. # $value - $set <= $tolerance
  1737. sub SIGNALduino_inTol($$$)
  1738. {
  1739. #Debug "sduino abs \($_[0] - $_[1]\) <= $_[2] ";
  1740. return (abs($_[0]-$_[1])<=$_[2]);
  1741. }
  1742. # - - - - - - - - - - - -
  1743. #=item SIGNALduino_PatternExists()
  1744. #This functons, needs reference to $hash, @array of values to search and %patternList where to find the matches.
  1745. #
  1746. # Will return -1 if pattern is not found or a string, containing the indexes which are in tolerance and have the smallest gap to what we searched
  1747. # =cut
  1748. # 01232323242423 while ($message =~ /$pstr/g) { $count++ }
  1749. sub SIGNALduino_PatternExists
  1750. {
  1751. my ($hash,$search,$patternList,$data) = @_;
  1752. #my %patternList=$arg3;
  1753. #Debug "plist: ".Dumper($patternList) if($debug);
  1754. #Debug "searchlist: ".Dumper($search) if($debug);
  1755. my $searchpattern;
  1756. my $valid=1;
  1757. my @pstr;
  1758. my $debug = AttrVal($hash->{NAME},"debug",0);
  1759. my $i=0;
  1760. my $maxcol=0;
  1761. foreach $searchpattern (@{$search}) # z.B. [1, -4]
  1762. {
  1763. #my $patt_id;
  1764. # Calculate tolernace for search
  1765. #my $tol=abs(abs($searchpattern)>=2 ?$searchpattern*0.3:$searchpattern*1.5);
  1766. my $tol=abs(abs($searchpattern)>3 ? abs($searchpattern)>16 ? $searchpattern*0.18 : $searchpattern*0.3 : 1); #tol is minimum 1 or higer, depending on our searched pulselengh
  1767. Debug "tol: looking for ($searchpattern +- $tol)" if($debug);
  1768. my %pattern_gap ; #= {};
  1769. # Find and store the gap of every pattern, which is in tolerance
  1770. %pattern_gap = map { $_ => abs($patternList->{$_}-$searchpattern) } grep { abs($patternList->{$_}-$searchpattern) <= $tol} (keys %$patternList);
  1771. if (scalar keys %pattern_gap > 0)
  1772. {
  1773. Debug "index => gap in tol (+- $tol) of pulse ($searchpattern) : ".Dumper(\%pattern_gap) if($debug);
  1774. # Extract fist pattern, which is nearst to our searched value
  1775. my @closestidx = (sort {$pattern_gap{$a} <=> $pattern_gap{$b}} keys %pattern_gap);
  1776. my $idxstr="";
  1777. my $r=0;
  1778. while (my ($item) = splice(@closestidx, 0, 1))
  1779. {
  1780. $pstr[$i][$r]=$item;
  1781. $r++;
  1782. Debug "closest pattern has index: $item" if($debug);
  1783. }
  1784. $valid=1;
  1785. } else {
  1786. # search is not found, return -1
  1787. return -1;
  1788. last;
  1789. }
  1790. $i++;
  1791. #return ($valid ? $pstr : -1); # return $pstr if $valid or -1
  1792. #foreach $patt_id (keys %$patternList) {
  1793. #Debug "$patt_id. chk ->intol $patternList->{$patt_id} $searchpattern $tol";
  1794. #$valid = SIGNALduino_inTol($patternList->{$patt_id}, $searchpattern, $tol);
  1795. #if ( $valid) #one pulse found in tolerance, search next one
  1796. #{
  1797. # $pstr="$pstr$patt_id";
  1798. # # provide this index for further lookup table -> {$patt_id = $searchpattern}
  1799. # Debug "pulse found";
  1800. # last ; ## Exit foreach loop if searched pattern matches pattern in list
  1801. #}
  1802. #}
  1803. #last if (!$valid); ## Exit loop if a complete iteration has not found anything
  1804. }
  1805. my @results = ('');
  1806. foreach my $subarray (@pstr)
  1807. {
  1808. @results = map {my $res = $_; map $res.$_, @$subarray } @results;
  1809. }
  1810. foreach my $search (@results)
  1811. {
  1812. Debug "looking for substr $search" if($debug);
  1813. return $search if (index( ${$data}, $search) >= 0);
  1814. }
  1815. return -1;
  1816. #return ($valid ? @results : -1); # return @pstr if $valid or -1
  1817. }
  1818. #SIGNALduino_MatchSignalPattern{$hash,@array, %hash, @array, $scalar}; not used >v3.1.3
  1819. sub SIGNALduino_MatchSignalPattern($\@\%\@$){
  1820. my ( $hash, $signalpattern, $patternList, $data_array, $idx) = @_;
  1821. my $name = $hash->{NAME};
  1822. #print Dumper($patternList);
  1823. #print Dumper($idx);
  1824. #Debug Dumper($signalpattern) if ($debug);
  1825. my $tol="0.2"; # Tolerance factor
  1826. my $found=0;
  1827. my $debug = AttrVal($hash->{NAME},"debug",0);
  1828. foreach ( @{$signalpattern} )
  1829. {
  1830. #Debug " $idx check: ".$patternList->{$data_array->[$idx]}." == ".$_;
  1831. Debug "$name: idx: $idx check: abs(". $patternList->{$data_array->[$idx]}." - ".$_.") > ". ceil(abs($patternList->{$data_array->[$idx]}*$tol)) if ($debug);
  1832. #print "\n";;
  1833. #if ($patternList->{$data_array->[$idx]} ne $_ )
  1834. ### Nachkommastelle von ceil!!!
  1835. if (!defined( $patternList->{$data_array->[$idx]})){
  1836. Debug "$name: Error index ($idx) does not exist!!" if ($debug);
  1837. return -1;
  1838. }
  1839. if (abs($patternList->{$data_array->[$idx]} - $_) > ceil(abs($patternList->{$data_array->[$idx]}*$tol)))
  1840. {
  1841. return -1; ## Pattern does not match, return -1 = not matched
  1842. }
  1843. $found=1;
  1844. $idx++;
  1845. }
  1846. if ($found)
  1847. {
  1848. return $idx; ## Return new Index Position
  1849. }
  1850. }
  1851. sub SIGNALduino_b2h {
  1852. my $num = shift;
  1853. my $WIDTH = 4;
  1854. my $index = length($num) - $WIDTH;
  1855. my $hex = '';
  1856. do {
  1857. my $width = $WIDTH;
  1858. if ($index < 0) {
  1859. $width += $index;
  1860. $index = 0;
  1861. }
  1862. my $cut_string = substr($num, $index, $width);
  1863. $hex = sprintf('%X', oct("0b$cut_string")) . $hex;
  1864. $index -= $WIDTH;
  1865. } while ($index > (-1 * $WIDTH));
  1866. return $hex;
  1867. }
  1868. sub SIGNALduino_Split_Message($$)
  1869. {
  1870. my $rmsg = shift;
  1871. my $name = shift;
  1872. my %patternList;
  1873. my $clockidx;
  1874. my $syncidx;
  1875. my $rawData;
  1876. my $clockabs;
  1877. my $mcbitnum;
  1878. my @msg_parts = SIGNALduino_splitMsg($rmsg,';'); ## Split message parts by ";"
  1879. my %ret;
  1880. my $debug = AttrVal($name,"debug",0);
  1881. foreach (@msg_parts)
  1882. {
  1883. #Debug "$name: checking msg part:( $_ )" if ($debug);
  1884. if ($_ =~ m/^MS/ or $_ =~ m/^MC/ or $_ =~ m/^MU/) #### Synced Message start
  1885. {
  1886. $ret{messagetype} = $_;
  1887. }
  1888. elsif ($_ =~ m/^P\d=-?\d{2,}/ or $_ =~ m/^[SL][LH]=-?\d{2,}/) #### Extract Pattern List from array
  1889. {
  1890. $_ =~ s/^P+//;
  1891. $_ =~ s/^P\d//;
  1892. my @pattern = split(/=/,$_);
  1893. $patternList{$pattern[0]} = $pattern[1];
  1894. Debug "$name: extracted pattern @pattern \n" if ($debug);
  1895. }
  1896. elsif($_ =~ m/D=\d+/ or $_ =~ m/^D=[A-F0-9]+/) #### Message from array
  1897. {
  1898. $_ =~ s/D=//;
  1899. $rawData = $_ ;
  1900. Debug "$name: extracted data $rawData\n" if ($debug);
  1901. $ret{rawData} = $rawData;
  1902. }
  1903. elsif($_ =~ m/^SP=\d{1}/) #### Sync Pulse Index
  1904. {
  1905. (undef, $syncidx) = split(/=/,$_);
  1906. Debug "$name: extracted syncidx $syncidx\n" if ($debug);
  1907. #return undef if (!defined($patternList{$syncidx}));
  1908. $ret{syncidx} = $syncidx;
  1909. }
  1910. elsif($_ =~ m/^CP=\d{1}/) #### Clock Pulse Index
  1911. {
  1912. (undef, $clockidx) = split(/=/,$_);
  1913. Debug "$name: extracted clockidx $clockidx\n" if ($debug);;
  1914. #return undef if (!defined($patternList{$clockidx}));
  1915. $ret{clockidx} = $clockidx;
  1916. }
  1917. elsif($_ =~ m/^L=\d/) #### MC bit length
  1918. {
  1919. (undef, $mcbitnum) = split(/=/,$_);
  1920. Debug "$name: extracted number of $mcbitnum bits\n" if ($debug);;
  1921. $ret{mcbitnum} = $mcbitnum;
  1922. }
  1923. elsif($_ =~ m/^C=\d+/) #### Message from array
  1924. {
  1925. $_ =~ s/C=//;
  1926. $clockabs = $_ ;
  1927. Debug "$name: extracted absolute clock $clockabs \n" if ($debug);
  1928. $ret{clockabs} = $clockabs;
  1929. } else {
  1930. Debug "$name: unknown Message part $_" if ($debug);;
  1931. }
  1932. #print "$_\n";
  1933. }
  1934. $ret{pattern} = {%patternList};
  1935. return %ret;
  1936. }
  1937. # Function which dispatches a message if needed.
  1938. sub SIGNALduno_Dispatch($$$)
  1939. {
  1940. my ($hash, $rmsg, $dmsg) = @_;
  1941. my $name = $hash->{NAME};
  1942. if (!defined($dmsg))
  1943. {
  1944. Log3 $name, 5, "$name: (SIGNALduno_Dispatch) dmsg is undef. Skipping dispatch call";
  1945. return;
  1946. }
  1947. Log3 $name, 5, "$name: converted Data to ($dmsg)";
  1948. #Dispatch only if $dmsg is different from last $dmsg, or if 2 seconds are between transmits
  1949. if ( ($hash->{DMSG} ne $dmsg) || ($hash->{TIME}+1 < time()) ) {
  1950. $hash->{MSGCNT}++;
  1951. $hash->{TIME} = time();
  1952. $hash->{DMSG} = $dmsg;
  1953. my $event = 0;
  1954. if (substr($dmsg,0,1) eq 'u') {
  1955. $event = 1;
  1956. }
  1957. readingsSingleUpdate($hash, "state", $hash->{READINGS}{state}{VAL}, $event);
  1958. $hash->{RAWMSG} = $rmsg;
  1959. my %addvals = (RAWMSG => $rmsg, DMSG => $dmsg);
  1960. Dispatch($hash, $dmsg, \%addvals); ## Dispatch to other Modules
  1961. } else {
  1962. Log3 $name, 4, "$name: Dropped ($dmsg) due to short time or equal msg";
  1963. }
  1964. }
  1965. sub
  1966. SIGNALduino_Parse_MS($$$$%)
  1967. {
  1968. my ($hash, $iohash, $name, $rmsg,%msg_parts) = @_;
  1969. my $protocolid;
  1970. my $syncidx=$msg_parts{syncidx};
  1971. my $clockidx=$msg_parts{clockidx};
  1972. my $protocol=undef;
  1973. my $rawData=$msg_parts{rawData};
  1974. my %patternList;
  1975. #$patternList{$_} = $msg_parts{rawData}{$_] for keys %msg_parts{rawData};
  1976. #$patternList = \%msg_parts{pattern};
  1977. #Debug "Message splitted:";
  1978. #Debug Dumper(\@msg_parts);
  1979. my $debug = AttrVal($iohash->{NAME},"debug",0);
  1980. if (defined($clockidx) and defined($syncidx))
  1981. {
  1982. ## Make a lookup table for our pattern index ids
  1983. #Debug "List of pattern:";
  1984. my $clockabs= $msg_parts{pattern}{$msg_parts{clockidx}};
  1985. return undef if ($clockabs == 0);
  1986. $patternList{$_} = round($msg_parts{pattern}{$_}/$clockabs,1) for keys %{$msg_parts{pattern}};
  1987. #Debug Dumper(\%patternList);
  1988. #my $syncfact = $patternList{$syncidx}/$patternList{$clockidx};
  1989. #$syncfact=$patternList{$syncidx};
  1990. #Debug "SF=$syncfact";
  1991. #### Convert rawData in Message
  1992. my $signal_length = length($rawData); # Length of data array
  1993. ## Iterate over the data_array and find zero, one, float and sync bits with the signalpattern
  1994. ## Find matching protocols
  1995. my $id;
  1996. my $message_dispatched=0;
  1997. foreach $id (@{$hash->{msIdList}}) {
  1998. my $valid=1;
  1999. #$debug=1;
  2000. Debug "Testing against Protocol id $id -> $ProtocolListSIGNALduino{$id}{name}" if ($debug);
  2001. # Check Clock if is it in range
  2002. $valid=SIGNALduino_inTol($ProtocolListSIGNALduino{$id}{clockabs},$clockabs,$clockabs*0.30) if ($ProtocolListSIGNALduino{$id}{clockabs} > 0);
  2003. Debug "validclock = $valid" if ($debug);
  2004. next if (!$valid) ;
  2005. my $bit_length = ($signal_length-(scalar @{$ProtocolListSIGNALduino{$id}{sync}}))/((scalar @{$ProtocolListSIGNALduino{$id}{one}} + scalar @{$ProtocolListSIGNALduino{$id}{zero}})/2);
  2006. #Check calculated min length
  2007. $valid = $valid && $ProtocolListSIGNALduino{$id}{length_min} <= $bit_length if (exists $ProtocolListSIGNALduino{$id}{length_min});
  2008. #Check calculated max length
  2009. $valid = $valid && $ProtocolListSIGNALduino{$id}{length_max} >= $bit_length if (exists $ProtocolListSIGNALduino{$id}{length_max});
  2010. next if (!$valid) ;
  2011. Debug "expecting $bit_length bits in signal" if ($debug && $valid);
  2012. #Debug Dumper(@{$ProtocolListSIGNALduino{$id}{sync}});
  2013. Debug "Searching in patternList: ".Dumper(\%patternList) if($debug);
  2014. Debug "searching sync: @{$ProtocolListSIGNALduino{$id}{sync}}[0] @{$ProtocolListSIGNALduino{$id}{sync}}[1]" if($debug); # z.B. [1, -18]
  2015. #$valid = $valid && SIGNALduino_inTol($patternList{$clockidx}, @{$ProtocolListSIGNALduino{$id}{sync}}[0], 3); #sync in tolerance
  2016. #$valid = $valid && SIGNALduino_inTol($patternList{$syncidx}, @{$ProtocolListSIGNALduino{$id}{sync}}[1], 3); #sync in tolerance
  2017. my $pstr;
  2018. my %patternLookupHash=();
  2019. $valid = $valid && ($pstr=SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{sync}},\%patternList,\$rawData)) >=0;
  2020. Debug "Found matched sync with indexes: ($pstr)" if ($debug && $valid);
  2021. $patternLookupHash{$pstr}="" if ($valid); ## Append Sync to our lookuptable
  2022. my $syncstr=$pstr; # Store for later start search
  2023. Debug "sync not found " if (!$valid && $debug); # z.B. [1, -18]
  2024. next if (!$valid) ;
  2025. $valid = $valid && ($pstr=SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{one}},\%patternList,\$rawData)) >=0;
  2026. Debug "Found matched one with indexes: ($pstr)" if ($debug && $valid);
  2027. $patternLookupHash{$pstr}="1" if ($valid); ## Append Sync to our lookuptable
  2028. #Debug "added $pstr " if ($debug && $valid);
  2029. Debug "one pattern not found" if ($debug && !$valid);
  2030. $valid = $valid && ($pstr=SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{zero}},\%patternList,\$rawData)) >=0;
  2031. Debug "Found matched zero with indexes: ($pstr)" if ($debug && $valid);
  2032. $patternLookupHash{$pstr}="0" if ($valid); ## Append Sync to our lookuptable
  2033. Debug "zero pattern not found" if ($debug && !$valid);
  2034. #Debug "added $pstr " if ($debug && $valid);
  2035. next if (!$valid) ;
  2036. #Debug "Pattern Lookup Table".Dumper(%patternLookupHash);
  2037. ## Check somethin else
  2038. #Anything seems to be valid, we can start decoding this.
  2039. Log3 $name, 4, "$name: Matched MS Protocol id $id -> $ProtocolListSIGNALduino{$id}{name}" if ($valid);
  2040. my $signal_width= @{$ProtocolListSIGNALduino{$id}{one}};
  2041. #Debug $signal_width;
  2042. my @bit_msg; # array to store decoded signal bits
  2043. #for (my $i=index($rawData,SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{sync}}))+$signal_width;$i<length($rawData);$i+=$signal_width)
  2044. #for (my $i=scalar@{$ProtocolListSIGNALduino{$id}{sync}};$i<length($rawData);$i+=$signal_width)
  2045. my $message_start =index($rawData,$syncstr)+length($syncstr);
  2046. Log3 $name, 5, "$name: Starting demodulation at Position $message_start";
  2047. for (my $i=$message_start;$i<length($rawData);$i+=$signal_width)
  2048. {
  2049. my $sig_str= substr($rawData,$i,$signal_width);
  2050. #Log3 $name, 5, "demodulating $sig_str";
  2051. #Debug $patternLookupHash{substr($rawData,$i,$signal_width)}; ## Get $signal_width number of chars from raw data string
  2052. if (exists $patternLookupHash{$sig_str}) { ## Add the bits to our bit array
  2053. push(@bit_msg,$patternLookupHash{$sig_str})
  2054. } else {
  2055. Log3 $name, 5, "$name: Found wrong signalpattern, catched ".scalar @bit_msg." bits, aborting demodulation";
  2056. last;
  2057. }
  2058. }
  2059. Debug "$name: decoded message raw (@bit_msg), ".@bit_msg." bits\n" if ($debug);;
  2060. my ($rcode,@retvalue) = SIGNALduino_callsub('postDemodulation',$ProtocolListSIGNALduino{$id}{postDemodulation},$name,@bit_msg);
  2061. next if (!$rcode);
  2062. #Log3 $name, 5, "$name: postdemodulation value @retvalue";
  2063. @bit_msg = @retvalue;
  2064. undef(@retvalue); undef($rcode);
  2065. my $padwith = defined($ProtocolListSIGNALduino{$id}{paddingbits}) ? $ProtocolListSIGNALduino{$id}{paddingbits} : 4;
  2066. my $i=0;
  2067. while (scalar @bit_msg % $padwith > 0) ## will pad up full nibbles per default or full byte if specified in protocol
  2068. {
  2069. push(@bit_msg,'0');
  2070. $i++;
  2071. }
  2072. Debug "$name padded $i bits to bit_msg array" if ($debug);
  2073. #my $logmsg = SIGNALduino_padbits(@bit_msg,$padwith);
  2074. #Check converted message against lengths
  2075. $valid = $valid && $ProtocolListSIGNALduino{$id}{length_min} <= scalar @bit_msg if (defined($ProtocolListSIGNALduino{$id}{length_min}));
  2076. $valid = $valid && $ProtocolListSIGNALduino{$id}{length_max} >= scalar @bit_msg if (defined($ProtocolListSIGNALduino{$id}{length_max}));
  2077. next if (!$valid);
  2078. #my $dmsg = sprintf "%02x", oct "0b" . join "", @bit_msg; ## Array -> String -> bin -> hex
  2079. my $dmsg = SIGNALduino_b2h(join "", @bit_msg);
  2080. $dmsg = "$dmsg"."$ProtocolListSIGNALduino{$id}{postamble}" if (defined($ProtocolListSIGNALduino{$id}{postamble}));
  2081. $dmsg = "$ProtocolListSIGNALduino{$id}{preamble}"."$dmsg" if (defined($ProtocolListSIGNALduino{$id}{preamble}));
  2082. Log3 $name, 4, "$name: Decoded MS Protocol id $id dmsg $dmsg length " . scalar @bit_msg;
  2083. #my ($rcode,@retvalue) = SIGNALduino_callsub('preDispatchfunc',$ProtocolListSIGNALduino{$id}{preDispatchfunc},$name,$dmsg);
  2084. #next if (!$rcode);
  2085. #$dmsg = @retvalue;
  2086. #undef(@retvalue); undef($rcode);
  2087. my $modulematch;
  2088. if (defined($ProtocolListSIGNALduino{$id}{modulematch})) {
  2089. $modulematch = $ProtocolListSIGNALduino{$id}{modulematch};
  2090. }
  2091. if (!defined($modulematch) || $dmsg =~ m/$modulematch/) {
  2092. Debug "$name: dispatching now msg: $dmsg" if ($debug);
  2093. SIGNALduno_Dispatch($hash,$rmsg,$dmsg);
  2094. $message_dispatched=1;
  2095. }
  2096. }
  2097. return 0 if (!$message_dispatched);
  2098. return 1;
  2099. }
  2100. }
  2101. ## //Todo: check list as reference
  2102. sub SIGNALduino_padbits(\@$)
  2103. {
  2104. my $i=@{$_[0]} % $_[1];
  2105. while (@{$_[0]} % $_[1] > 0) ## will pad up full nibbles per default or full byte if specified in protocol
  2106. {
  2107. push(@{$_[0]},'0');
  2108. }
  2109. return " padded $i bits to bit_msg array";
  2110. }
  2111. # - - - - - - - - - - - -
  2112. #=item SIGNALduino_getProtoProp()
  2113. #This functons, will return a value from the Protocolist and check if it is defined
  2114. #
  2115. # returns "" if the var is not defined
  2116. # =cut
  2117. # $id, $propertyname,
  2118. sub SIGNALduino_getProtoProp($$)
  2119. {
  2120. my $id = shift;
  2121. my $propNameLst = shift;
  2122. return $ProtocolListSIGNALduino{$id}{$propNameLst} if defined($ProtocolListSIGNALduino{$id}{$propNameLst});
  2123. return undef;
  2124. }
  2125. sub SIGNALduino_Parse_MU($$$$@)
  2126. {
  2127. my ($hash, $iohash, $name, $rmsg,%msg_parts) = @_;
  2128. my $protocolid;
  2129. my $clockidx=$msg_parts{clockidx};
  2130. my $protocol=undef;
  2131. my $rawData;
  2132. my %patternListRaw;
  2133. my $message_dispatched=0;
  2134. my $debug = AttrVal($iohash->{NAME},"debug",0);
  2135. Debug "$name: processing unsynced message\n" if ($debug);
  2136. #my $clockabs; #Clock will be fetched from Protocol
  2137. #$patternListRaw{$_} = floor($msg_parts{pattern}{$_}/$clockabs) for keys $msg_parts{pattern};
  2138. $patternListRaw{$_} = $msg_parts{pattern}{$_} for keys %{$msg_parts{pattern}};
  2139. if (defined($clockidx))
  2140. {
  2141. ## Make a lookup table for our pattern index ids
  2142. #Debug "List of pattern:"; #Debug Dumper(\%patternList);
  2143. ## Find matching protocols
  2144. my $id;
  2145. foreach $id (@{$hash->{muIdList}}) {
  2146. my $valid=1;
  2147. my $clockabs= $ProtocolListSIGNALduino{$id}{clockabs};
  2148. my %patternList;
  2149. $rawData=$msg_parts{rawData};
  2150. if (exists($ProtocolListSIGNALduino{$id}{filterfunc}))
  2151. {
  2152. my $method = $ProtocolListSIGNALduino{$id}{filterfunc};
  2153. if (!exists &$method)
  2154. {
  2155. Log3 $name, 5, "$name: Error: Unknown filtermethod=$method. Please define it in file $0";
  2156. next;
  2157. } else {
  2158. Log3 $name, 5, "$name: applying filterfunc $method";
  2159. no strict "refs";
  2160. (my $count_changes,$rawData,my %patternListRaw_tmp) = $method->($name,$id,$rawData,%patternListRaw);
  2161. use strict "refs";
  2162. %patternList = map { $_ => round($patternListRaw_tmp{$_}/$clockabs,1) } keys %patternListRaw_tmp;
  2163. }
  2164. } else {
  2165. %patternList = map { $_ => round($patternListRaw{$_}/$clockabs,1) } keys %patternListRaw;
  2166. }
  2167. my $signal_length = length($rawData); # Length of data array
  2168. my @keys = sort { $patternList{$a} <=> $patternList{$b} } keys %patternList;
  2169. #Debug Dumper(\%patternList);
  2170. #Debug Dumper(@keys);
  2171. #$debug=1;
  2172. Debug "Testing against Protocol id $id -> $ProtocolListSIGNALduino{$id}{name}" if ($debug);
  2173. # $valid=SIGNALduino_inTol($ProtocolListSIGNALduino{$id}{clockabs},$clockabs,$clockabs*0.30) if ($ProtocolListSIGNALduino{$id}{clockabs} > 0);
  2174. next if (!$valid) ;
  2175. my $bit_length = ($signal_length/((scalar @{$ProtocolListSIGNALduino{$id}{one}} + scalar @{$ProtocolListSIGNALduino{$id}{zero}})/2));
  2176. Debug "Expect $bit_length bits in message" if ($valid && $debug);
  2177. #Check calculated min length
  2178. #$valid = $valid && $ProtocolListSIGNALduino{$id}{length_min} <= $bit_length if (exists $ProtocolListSIGNALduino{$id}{length_min});
  2179. #Check calculated max length
  2180. #$valid = $valid && $ProtocolListSIGNALduino{$id}{length_max} >= $bit_length if (exists $ProtocolListSIGNALduino{$id}{length_max});
  2181. #next if (!$valid) ;
  2182. #Debug "expecting $bit_length bits in signal" if ($debug && $valid);
  2183. Debug "Searching in patternList: ".Dumper(\%patternList) if($debug);
  2184. next if (!$valid) ;
  2185. my %patternLookupHash=();
  2186. #Debug "phash:".Dumper(%patternLookupHash);
  2187. my $pstr="";
  2188. $valid = $valid && ($pstr=SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{one}},\%patternList,\$rawData)) >=0;
  2189. Debug "Found matched one" if ($debug && $valid);
  2190. my $oneStr=$pstr if ($valid);
  2191. $patternLookupHash{$pstr}="1" if ($valid); ## Append one to our lookuptable
  2192. Debug "added $pstr " if ($debug && $valid);
  2193. $valid = $valid && ($pstr=SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{zero}},\%patternList,\$rawData)) >=0;
  2194. Debug "Found matched zero" if ($debug && $valid);
  2195. my $zeroStr=$pstr if ($valid);
  2196. $patternLookupHash{$pstr}="0" if ($valid); ## Append zero to our lookuptable
  2197. Debug "added $pstr " if ($debug && $valid);
  2198. if (defined($ProtocolListSIGNALduino{$id}{float}))
  2199. {
  2200. $valid = $valid && ($pstr=SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{float}},\%patternList,\$rawData)) >=0;
  2201. Debug "Found matched float" if ($debug && $valid);
  2202. $patternLookupHash{$pstr}="F" if ($valid); ## Append float to our lookuptable
  2203. Debug "added $pstr " if ($debug && $valid);
  2204. }
  2205. next if (!$valid) ;
  2206. #Debug "Pattern Lookup Table".Dumper(%patternLookupHash);
  2207. ## Check somethin else
  2208. #Anything seems to be valid, we can start decoding this.
  2209. Log3 $name, 4, "$name: Fingerprint for MU Protocol id $id -> $ProtocolListSIGNALduino{$id}{name} matches, trying to demodulate" if ($valid);
  2210. my $signal_width= @{$ProtocolListSIGNALduino{$id}{one}};
  2211. #Debug $signal_width;
  2212. my @bit_msg=(); # array to store decoded signal bits
  2213. my $message_start=0 ;
  2214. my @msgStartLst;
  2215. my $startStr="";
  2216. my $start_regex;
  2217. #my $oneStr=SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{one}},\%patternList,\$rawData);
  2218. #my $zeroStr=SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{zero}},\%patternList,\$rawData);
  2219. if (@msgStartLst = SIGNALduino_getProtoProp($id,"start"))
  2220. {
  2221. Debug "msgStartLst: ".Dumper(@msgStartLst) if ($debug);
  2222. if ( ($startStr=SIGNALduino_PatternExists($hash,@msgStartLst,\%patternList,\$rawData)) eq -1)
  2223. {
  2224. Log3 $name, 5, "$name: start pattern for MU Protocol id $id -> $ProtocolListSIGNALduino{$id}{name} mismatches, aborting" ;
  2225. $valid=0;
  2226. next;
  2227. };
  2228. }
  2229. $start_regex="$startStr($oneStr|$zeroStr)";
  2230. Debug "Regex is: $start_regex" if ($debug);
  2231. $rawData =~ /$start_regex/;
  2232. if (defined($-[0] && $-[0] > 0)) {
  2233. $message_start=$-[0]+ length($startStr);
  2234. } else {
  2235. undef($message_start);
  2236. }
  2237. undef @msgStartLst;
  2238. #for (my $i=index($rawData,SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{sync}}))+$signal_width;$i<length($rawData);$i+=$signal_width)
  2239. Debug "Message starts at $message_start - length of data is ".length($rawData) if ($debug);
  2240. next if (!defined($message_start));
  2241. Log3 $name, 5, "$name: Starting demodulation at Position $message_start";
  2242. #my $onepos= index($rawData,SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{one}},\%patternList));
  2243. #my $zeropos=index($rawData,SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{zero}},\%patternList));
  2244. #Log3 $name, 3, "op=$onepos zp=$zeropos";
  2245. #Debug "phash:".Dumper(%patternLookupHash);
  2246. my $padwith = defined($ProtocolListSIGNALduino{$id}{paddingbits}) ? $ProtocolListSIGNALduino{$id}{paddingbits} : 4;
  2247. for (my $i=$message_start;$i<=length($rawData)-$signal_width;$i+=$signal_width)
  2248. {
  2249. my $sig_str= substr($rawData,$i,$signal_width);
  2250. Debug "$name: i=$i search=$sig_str" if ($debug);
  2251. $valid=1; # Set valid to 1 for every loop
  2252. #Debug $patternLookupHash{substr($rawData,$i,$signal_width)}; ## Get $signal_width number of chars from raw data string
  2253. if (exists $patternLookupHash{$sig_str})
  2254. {
  2255. my $bit = $patternLookupHash{$sig_str};
  2256. push(@bit_msg,$bit) if (looks_like_number($bit)) ; ## Add the bits to our bit array
  2257. }
  2258. if (!exists $patternLookupHash{$sig_str} || $i+$signal_width>length($rawData)-$signal_width) ## Dispatch if last signal or unknown data
  2259. {
  2260. Debug "$name: demodulated message raw (@bit_msg), ".@bit_msg." bits\n" if ($debug);
  2261. #Check converted message against lengths
  2262. $valid = $valid && $ProtocolListSIGNALduino{$id}{length_max} >= scalar @bit_msg if (defined($ProtocolListSIGNALduino{$id}{length_max}));
  2263. $valid = $valid && $ProtocolListSIGNALduino{$id}{length_min} <= scalar @bit_msg if (defined($ProtocolListSIGNALduino{$id}{length_min}));
  2264. #next if (!$valid); ## Last chance to try next protocol if there is somethin invalid
  2265. if ($valid) {
  2266. my ($rcode,@retvalue) = SIGNALduino_callsub('postDemodulation',$ProtocolListSIGNALduino{$id}{postDemodulation},$name,@bit_msg);
  2267. next if (!$rcode);
  2268. #Log3 $name, 5, "$name: postdemodulation value @retvalue";
  2269. @bit_msg = @retvalue;
  2270. undef(@retvalue); undef($rcode);
  2271. while (scalar @bit_msg % $padwith > 0) ## will pad up full nibbles per default or full byte if specified in protocol
  2272. {
  2273. push(@bit_msg,'0');
  2274. Debug "$name: padding 0 bit to bit_msg array" if ($debug);
  2275. }
  2276. Log3 $name, 5, "$name: dispatching bits: @bit_msg";
  2277. my $dmsg = SIGNALduino_b2h(join "", @bit_msg);
  2278. $dmsg =~ s/^0+// if (defined($ProtocolListSIGNALduino{$id}{remove_zero}));
  2279. $dmsg = "$dmsg"."$ProtocolListSIGNALduino{$id}{postamble}" if (defined($ProtocolListSIGNALduino{$id}{postamble}));
  2280. $dmsg = "$ProtocolListSIGNALduino{$id}{preamble}"."$dmsg" if (defined($ProtocolListSIGNALduino{$id}{preamble}));
  2281. Log3 $name, 4, "$name: decoded matched MU Protocol id $id dmsg $dmsg length " . scalar @bit_msg;
  2282. my $modulematch;
  2283. if (defined($ProtocolListSIGNALduino{$id}{modulematch})) {
  2284. $modulematch = $ProtocolListSIGNALduino{$id}{modulematch};
  2285. }
  2286. if (!defined($modulematch) || $dmsg =~ m/$modulematch/) {
  2287. Debug "$name: dispatching now msg: $dmsg" if ($debug);
  2288. SIGNALduno_Dispatch($hash,$rmsg,$dmsg);
  2289. $message_dispatched=1;
  2290. }
  2291. } else {
  2292. if ($debug)
  2293. {
  2294. my $debugstr;
  2295. $debugstr.=$ProtocolListSIGNALduino{$id}{length_min} if defined($ProtocolListSIGNALduino{$id}{length_min});
  2296. $debugstr.="/";
  2297. $debugstr.=$ProtocolListSIGNALduino{$id}{length_max} if defined($ProtocolListSIGNALduino{$id}{length_max});
  2298. Debug "$name: length ($debugstr) does not match (@bit_msg), ".@bit_msg." bits\n";
  2299. }
  2300. }
  2301. @bit_msg=(); # clear bit_msg array
  2302. #Find next position of valid signal (skip invalid pieces)
  2303. my $regex=".{$i}".$start_regex;
  2304. Debug "$name: searching new start with ($regex)\n" if ($debug);
  2305. $rawData =~ /$regex/;
  2306. if (defined($-[0]) && ($-[0] > 0)) {
  2307. #$i=$-[0]+ $i+ length($startStr);
  2308. $i=$-[0]+ $i;
  2309. $i=$i-$signal_width if ($i>0 && length($startStr) == 0); #Todo:
  2310. Debug "$name: found restart at Position $i ($regex)\n" if ($debug);
  2311. } else {
  2312. last;
  2313. }
  2314. #if ($startStr)
  2315. #{
  2316. # $i= index($rawData,$startStr,$i);
  2317. # } else {
  2318. # $i = (index($rawData,SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{one}},\%patternList),$i+$signal_width) < index($rawData,SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{zero}},\%patternList),$i+$signal_width) ? index($rawData,SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{one}},\%patternList),$i+$signal_width) : index($rawData,SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{zero}},\%patternList),$i+$signal_width));
  2319. # $i-=$signal_width if ($i<length($rawData)-$signal_width) ;
  2320. #
  2321. # }
  2322. # last if ($i <=-1);
  2323. Log3 $name, 5, "$name: restarting demodulation at Position $i+$signal_width" if ($debug);
  2324. }
  2325. }
  2326. #my $dmsg = sprintf "%02x", oct "0b" . join "", @bit_msg; ## Array -> String -> bin -> hex
  2327. }
  2328. return 0 if (!$message_dispatched);
  2329. return 1;
  2330. }
  2331. }
  2332. sub
  2333. SIGNALduino_Parse_MC($$$$@)
  2334. {
  2335. my ($hash, $iohash, $name, $rmsg,%msg_parts) = @_;
  2336. my $clock=$msg_parts{clockabs}; ## absolute clock
  2337. my $rawData=$msg_parts{rawData};
  2338. my $bitData;
  2339. my $dmsg;
  2340. my $message_dispatched=0;
  2341. my $debug = AttrVal($iohash->{NAME},"debug",0);
  2342. return undef if (!$clock);
  2343. #my $protocol=undef;
  2344. #my %patternListRaw = %msg_parts{patternList};
  2345. Debug "$name: processing manchester messag len:".length($rawData) if ($debug);
  2346. my $hlen = length($rawData);
  2347. my $blen = $hlen * 4;
  2348. my $id;
  2349. my $rawDataInverted;
  2350. ($rawDataInverted = $rawData) =~ tr/0123456789ABCDEF/FEDCBA9876543210/; # Some Manchester Data is inverted
  2351. foreach $id (@{$hash->{mcIdList}}) {
  2352. if ( $clock >$ProtocolListSIGNALduino{$id}{clockrange}[0] and $clock <$ProtocolListSIGNALduino{$id}{clockrange}[1] and length($rawData)*4 >= $ProtocolListSIGNALduino{$id}{length_min} )
  2353. {
  2354. Debug "clock and min length matched" if ($debug);
  2355. Log3 $name, 4, "$name: Found manchester Protocol id $id clock $clock -> $ProtocolListSIGNALduino{$id}{name}";
  2356. if (exists($ProtocolListSIGNALduino{$id}{polarity}) && ($ProtocolListSIGNALduino{$id}{polarity} eq 'invert') && (!defined($hash->{version}) || substr($hash->{version},0,6) ne 'V 3.2.'))
  2357. # todo && substr($hash->{version},0,6) ne 'V 3.2.') # bei version V 3.2. nicht invertieren
  2358. {
  2359. $bitData= unpack("B$blen", pack("H$hlen", $rawDataInverted));
  2360. } else {
  2361. $bitData= unpack("B$blen", pack("H$hlen", $rawData));
  2362. }
  2363. Debug "$name: extracted data $bitData (bin)\n" if ($debug); ## Convert Message from hex to bits
  2364. Log3 $name, 5, "$name: extracted data $bitData (bin)";
  2365. my $method = $ProtocolListSIGNALduino{$id}{method};
  2366. if (!exists &$method)
  2367. {
  2368. Log3 $name, 5, "$name: Error: Unknown function=$method. Please define it in file $0";
  2369. } else {
  2370. my ($rcode,$res) = $method->($name,$bitData,$id);
  2371. if ($rcode != -1) {
  2372. $dmsg = $res;
  2373. $dmsg=$ProtocolListSIGNALduino{$id}{preamble}.$dmsg if (defined($ProtocolListSIGNALduino{$id}{preamble}));
  2374. my $modulematch;
  2375. if (defined($ProtocolListSIGNALduino{$id}{modulematch})) {
  2376. $modulematch = $ProtocolListSIGNALduino{$id}{modulematch};
  2377. }
  2378. if (!defined($modulematch) || $dmsg =~ m/$modulematch/) {
  2379. SIGNALduno_Dispatch($hash,$rmsg,$dmsg);
  2380. $message_dispatched=1;
  2381. }
  2382. } else {
  2383. Log3 $name, 5, "$name: protocol does not match return from method: ($res)" if ($debug);
  2384. }
  2385. }
  2386. }
  2387. }
  2388. return 0 if (!$message_dispatched);
  2389. return 1;
  2390. }
  2391. sub
  2392. SIGNALduino_Parse($$$$@)
  2393. {
  2394. my ($hash, $iohash, $name, $rmsg, $initstr) = @_;
  2395. #print Dumper(\%ProtocolListSIGNALduino);
  2396. return undef if !($rmsg=~ s/^\002(M.;.*;)\003/$1/); ## Check if a Data Message arrived and if it's complete (start & end control char are received)
  2397. # cut off start end end character from message for further processing they are not needed
  2398. if (defined($hash->{keepalive})) {
  2399. $hash->{keepalive}{ok} = 1;
  2400. $hash->{keepalive}{retry} = 0;
  2401. }
  2402. my $debug = AttrVal($iohash->{NAME},"debug",0);
  2403. Debug "$name: incomming message: ($rmsg)\n" if ($debug);
  2404. my %signal_parts=SIGNALduino_Split_Message($rmsg,$name); ## Split message and save anything in an hash %signal_parts
  2405. #Debug "raw data ". $signal_parts{rawData};
  2406. my $dispatched;
  2407. # Message Synced type -> M#
  2408. if (@{$hash->{msIdList}} && $rmsg=~ m/^MS;(P\d=-?\d+;){3,8}D=\d+;CP=\d;SP=\d;/)
  2409. {
  2410. $dispatched= SIGNALduino_Parse_MS($hash, $iohash, $name, $rmsg,%signal_parts);
  2411. }
  2412. # Message unsynced type -> MU
  2413. elsif (@{$hash->{muIdList}} && $rmsg=~ m/^MU;(P\d=-?\d+;){3,8}D=\d+;CP=\d;/)
  2414. {
  2415. $dispatched= SIGNALduino_Parse_MU($hash, $iohash, $name, $rmsg,%signal_parts);
  2416. }
  2417. # Manchester encoded Data -> MC
  2418. elsif (@{$hash->{mcIdList}} && $rmsg=~ m/^MC;.*;/)
  2419. {
  2420. $dispatched= SIGNALduino_Parse_MC($hash, $iohash, $name, $rmsg,%signal_parts);
  2421. }
  2422. else {
  2423. Debug "$name: unknown Messageformat, aborting\n" if ($debug);
  2424. return undef;
  2425. }
  2426. if ( AttrVal($hash->{NAME},"verbose","0") > 4 && !$dispatched)
  2427. {
  2428. my $notdisplist;
  2429. my @lines;
  2430. if (defined($hash->{unknownmessages}))
  2431. {
  2432. $notdisplist=$hash->{unknownmessages};
  2433. @lines = split ('#', $notdisplist); # or whatever
  2434. }
  2435. push(@lines,FmtDateTime(time())."-".$rmsg);
  2436. shift(@lines)if (scalar @lines >25);
  2437. $notdisplist = join('#',@lines);
  2438. $hash->{unknownmessages}=$notdisplist;
  2439. return undef;
  2440. #Todo compare Sync/Clock fact and length of D= if equal, then it's the same protocol!
  2441. }
  2442. }
  2443. #####################################
  2444. sub
  2445. SIGNALduino_Ready($)
  2446. {
  2447. my ($hash) = @_;
  2448. if ($hash->{STATE} eq 'disconnected') {
  2449. $hash->{DevState} = 'disconnected';
  2450. return DevIo_OpenDev($hash, 1, "SIGNALduino_DoInit", 'SIGNALduino_Connect')
  2451. }
  2452. # This is relevant for windows/USB only
  2453. my $po = $hash->{USBDev};
  2454. my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags);
  2455. if($po) {
  2456. ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status;
  2457. }
  2458. return ($InBytes && $InBytes>0);
  2459. }
  2460. ########################
  2461. sub
  2462. SIGNALduino_SimpleWrite(@)
  2463. {
  2464. my ($hash, $msg, $nonl) = @_;
  2465. return if(!$hash);
  2466. if($hash->{TYPE} eq "SIGNALduino_RFR") {
  2467. # Prefix $msg with RRBBU and return the corresponding SIGNALduino hash.
  2468. ($hash, $msg) = SIGNALduino_RFR_AddPrefix($hash, $msg);
  2469. }
  2470. my $name = $hash->{NAME};
  2471. Log3 $name, 5, "$name SW: $msg";
  2472. $msg .= "\n" unless($nonl);
  2473. $hash->{USBDev}->write($msg) if($hash->{USBDev});
  2474. syswrite($hash->{TCPDev}, $msg) if($hash->{TCPDev});
  2475. syswrite($hash->{DIODev}, $msg) if($hash->{DIODev});
  2476. # Some linux installations are broken with 0.001, T01 returns no answer
  2477. select(undef, undef, undef, 0.01);
  2478. }
  2479. sub
  2480. SIGNALduino_Attr(@)
  2481. {
  2482. my ($cmd,$name,$aName,$aVal) = @_;
  2483. my $hash = $defs{$name};
  2484. my $debug = AttrVal($name,"debug",0);
  2485. $aVal= "" if (!defined($aVal));
  2486. Log3 $name, 4, "$name: Calling Getting Attr sub with args: $cmd $aName = $aVal";
  2487. if( $aName eq "Clients" ) { ## Change clientList
  2488. $hash->{Clients} = $aVal;
  2489. $hash->{Clients} = $clientsSIGNALduino if( !$hash->{Clients}) ; ## Set defaults
  2490. return "Setting defaults";
  2491. } elsif( $aName eq "MatchList" ) { ## Change matchList
  2492. my $match_list;
  2493. if( $cmd eq "set" ) {
  2494. $match_list = eval $aVal;
  2495. if( $@ ) {
  2496. Log3 $name, 2, $name .": $aVal: ". $@;
  2497. }
  2498. }
  2499. if( ref($match_list) eq 'HASH' ) {
  2500. $hash->{MatchList} = $match_list;
  2501. } else {
  2502. $hash->{MatchList} = \%matchListSIGNALduino; ## Set defaults
  2503. Log3 $name, 2, $name .": $aVal: not a HASH using defaults" if( $aVal );
  2504. }
  2505. }
  2506. elsif ($aName eq "verbose")
  2507. {
  2508. Log3 $name, 3, "$name: setting Verbose to: " . $aVal;
  2509. $hash->{unknownmessages}="" if $aVal <4;
  2510. }
  2511. elsif ($aName eq "debug")
  2512. {
  2513. $debug = $aVal;
  2514. Log3 $name, 3, "$name: setting debug to: " . $debug;
  2515. }
  2516. elsif ($aName eq "whitelist_IDs")
  2517. {
  2518. SIGNALduino_IdList($hash, $name, $aVal);
  2519. }
  2520. return undef;
  2521. }
  2522. sub SIGNALduino_IdList($$$)
  2523. {
  2524. my ($hash, $name, $aVal) = @_;
  2525. my @msIdList = ();
  2526. my @muIdList = ();
  2527. my @mcIdList = ();
  2528. my %WhitelistIDs;
  2529. my $wflag = 0;
  2530. if (defined($aVal) && length($aVal)>0)
  2531. {
  2532. if (substr($aVal,0 ,1) eq '#') {
  2533. Log3 $name, 3, "Attr whitelist deaktiviert: $aVal";
  2534. }
  2535. else {
  2536. %WhitelistIDs = map { $_ => 1 } split(",", $aVal);
  2537. #my $w = join ', ' => map "$_" => keys %WhitelistIDs;
  2538. #Log3 $name, 3, "Attr whitelist $w";
  2539. $wflag = 1;
  2540. }
  2541. }
  2542. my $id;
  2543. foreach $id (keys %ProtocolListSIGNALduino)
  2544. {
  2545. next if ($id eq 'id');
  2546. if ($wflag == 1 && !defined($WhitelistIDs{$id}))
  2547. {
  2548. #Log3 $name, 3, "skip ID $id";
  2549. next;
  2550. }
  2551. if (exists ($ProtocolListSIGNALduino{$id}{format}) && $ProtocolListSIGNALduino{$id}{format} eq "manchester")
  2552. {
  2553. push (@mcIdList, $id);
  2554. }
  2555. elsif (exists $ProtocolListSIGNALduino{$id}{sync})
  2556. {
  2557. push (@msIdList, $id);
  2558. }
  2559. elsif (exists ($ProtocolListSIGNALduino{$id}{clockabs}))
  2560. {
  2561. push (@muIdList, $id);
  2562. }
  2563. }
  2564. @msIdList = sort @msIdList;
  2565. @muIdList = sort @muIdList;
  2566. @mcIdList = sort @mcIdList;
  2567. Log3 $name, 3, "$name: IDlist MS @msIdList";
  2568. Log3 $name, 3, "$name: IDlist MU @muIdList";
  2569. Log3 $name, 3, "$name: IDlist MC @mcIdList";
  2570. $hash->{msIdList} = \@msIdList;
  2571. $hash->{muIdList} = \@muIdList;
  2572. $hash->{mcIdList} = \@mcIdList;
  2573. }
  2574. sub SIGNALduino_callsub
  2575. {
  2576. my $funcname =shift;
  2577. my $method = shift;
  2578. my $name = shift;
  2579. my @args = @_;
  2580. if ( defined $method && defined &$method )
  2581. {
  2582. Log3 $name, 5, "$name: applying $funcname method $method";
  2583. #Log3 $name, 5, "$name: value bevore $funcname: @args";
  2584. my @returnvalues = $method->(@args) ;
  2585. Log3 $name, 5, "$name: modified value after $funcname: @returnvalues";
  2586. return (1,@returnvalues);
  2587. } elsif (defined $method ) {
  2588. Log3 $name, 5, "$name: Error: Unknown method $funcname Please check definition";
  2589. return (0,undef);
  2590. }
  2591. return (1,@args);
  2592. }
  2593. # calculates the hex (in bits) and adds it at the beginning of the message
  2594. # input = @list
  2595. # output = @list
  2596. sub SIGNALduino_lengtnPrefix
  2597. {
  2598. my $msg = join("",@_);
  2599. #$msg = unpack("B8", pack("N", length($msg))).$msg;
  2600. $msg=sprintf('%08b', length($msg)).$msg;
  2601. return split("",$msg);
  2602. }
  2603. sub SIGNALduino_bit2Arctec
  2604. {
  2605. my $msg = join("",@_);
  2606. # Convert 0 -> 01 1 -> 10 to be compatible with IT Module
  2607. $msg =~ s/0/z/g;
  2608. $msg =~ s/1/10/g;
  2609. $msg =~ s/z/01/g;
  2610. return split("",$msg);
  2611. }
  2612. sub SIGNALduino_ITV1_tristateToBit($)
  2613. {
  2614. my ($msg) = @_;
  2615. # Convert 0 -> 00 1 -> 11 F => 01 to be compatible with IT Module
  2616. $msg =~ s/0/00/g;
  2617. $msg =~ s/1/11/g;
  2618. $msg =~ s/F/01/g;
  2619. $msg =~ s/D/10/g;
  2620. return $msg;
  2621. }
  2622. sub SIGNALduino_OSV2()
  2623. {
  2624. my ($name,$bitData,$id) = @_;
  2625. my $preamble_pos;
  2626. my $message_end;
  2627. my $message_length;
  2628. #$bitData =~ tr/10/01/;
  2629. if ($bitData =~ m/^.?(01){12,17}.?10011001/)
  2630. { # Valid OSV2 detected!
  2631. #$preamble_pos=index($bitData,"10011001",24);
  2632. $preamble_pos=$+[1];
  2633. Log3 $name, 4, "$name: OSV2 protocol detected: preamble_pos = $preamble_pos";
  2634. return return (-1," sync not found") if ($preamble_pos <=24);
  2635. $message_end=$-[1] if ($bitData =~ m/^.{44,}(01){16,17}.?10011001/); #Todo regex .{44,} 44 should be calculated from $preamble_pos+ min message lengh (44)
  2636. if (!defined($message_end) || $message_end < $preamble_pos) {
  2637. $message_end = length($bitData);
  2638. } else {
  2639. $message_end += 16;
  2640. Log3 $name, 4, "$name: OSV2 message end pattern found at pos $message_end lengthBitData=".length($bitData);
  2641. }
  2642. $message_length = ($message_end - $preamble_pos)/2;
  2643. return (-1," message is to short") if (defined($ProtocolListSIGNALduino{$id}{length_min}) && $message_length < $ProtocolListSIGNALduino{$id}{length_min} );
  2644. return (-1," message is to long") if (defined($ProtocolListSIGNALduino{$id}{length_max}) && $message_length > $ProtocolListSIGNALduino{$id}{length_max} );
  2645. my $idx=0;
  2646. my $osv2bits="";
  2647. my $osv2hex ="";
  2648. for ($idx=$preamble_pos;$idx<$message_end;$idx=$idx+16)
  2649. {
  2650. if ($message_end-$idx < 8 )
  2651. {
  2652. last;
  2653. }
  2654. my $osv2byte = "";
  2655. $osv2byte=NULL;
  2656. $osv2byte=substr($bitData,$idx,16);
  2657. my $rvosv2byte="";
  2658. for (my $p=0;$p<length($osv2byte);$p=$p+2)
  2659. {
  2660. $rvosv2byte = substr($osv2byte,$p,1).$rvosv2byte;
  2661. }
  2662. $rvosv2byte =~ tr/10/01/;
  2663. if (length($rvosv2byte) eq 8) {
  2664. $osv2hex=$osv2hex.sprintf('%02X', oct("0b$rvosv2byte")) ;
  2665. } else {
  2666. $osv2hex=$osv2hex.sprintf('%X', oct("0b$rvosv2byte")) ;
  2667. }
  2668. $osv2bits = $osv2bits.$rvosv2byte;
  2669. }
  2670. $osv2hex = sprintf("%02X", length($osv2hex)*4).$osv2hex;
  2671. Log3 $name, 4, "$name: OSV2 protocol converted to hex: ($osv2hex) with length (".(length($osv2hex)*4).") bits";
  2672. #$found=1;
  2673. #$dmsg=$osv2hex;
  2674. return (1,$osv2hex);
  2675. }
  2676. elsif ($bitData =~ m/^.?(1){16,24}0101/) { # Valid OSV3 detected!
  2677. $preamble_pos = index($bitData, '0101', 16);
  2678. $message_end = length($bitData);
  2679. $message_length = $message_end - ($preamble_pos+4);
  2680. Log3 $name, 4, "$name: OSV3 protocol detected: preamble_pos = $preamble_pos, message_length = $message_length";
  2681. my $idx=0;
  2682. #my $osv3bits="";
  2683. my $osv3hex ="";
  2684. for ($idx=$preamble_pos+4;$idx<length($bitData);$idx=$idx+4)
  2685. {
  2686. if (length($bitData)-$idx < 4 )
  2687. {
  2688. last;
  2689. }
  2690. my $osv3nibble = "";
  2691. $osv3nibble=NULL;
  2692. $osv3nibble=substr($bitData,$idx,4);
  2693. my $rvosv3nibble="";
  2694. for (my $p=0;$p<length($osv3nibble);$p++)
  2695. {
  2696. $rvosv3nibble = substr($osv3nibble,$p,1).$rvosv3nibble;
  2697. }
  2698. $osv3hex=$osv3hex.sprintf('%X', oct("0b$rvosv3nibble"));
  2699. #$osv3bits = $osv3bits.$rvosv3nibble;
  2700. }
  2701. Log3 $name, 4, "$name: OSV3 protocol = $osv3hex";
  2702. my $korr = 10;
  2703. # Check if nibble 1 is A
  2704. if (substr($osv3hex,1,1) ne 'A')
  2705. {
  2706. my $n1=substr($osv3hex,1,1);
  2707. $korr = hex(substr($osv3hex,3,1));
  2708. substr($osv3hex,1,1,'A'); # nibble 1 = A
  2709. substr($osv3hex,3,1,$n1); # nibble 3 = nibble1
  2710. }
  2711. # Korrektur nibble
  2712. my $insKorr = sprintf('%X', $korr);
  2713. # Check for ending 00
  2714. if (substr($osv3hex,-2,2) eq '00')
  2715. {
  2716. #substr($osv3hex,1,-2); # remove 00 at end
  2717. $osv3hex = substr($osv3hex, 0, length($osv3hex)-2);
  2718. }
  2719. my $osv3len = length($osv3hex);
  2720. $osv3hex .= '0';
  2721. my $turn0 = substr($osv3hex,5, $osv3len-4);
  2722. my $turn = '';
  2723. for ($idx=0; $idx<$osv3len-5; $idx=$idx+2) {
  2724. $turn = $turn . substr($turn0,$idx+1,1) . substr($turn0,$idx,1);
  2725. }
  2726. $osv3hex = substr($osv3hex,0,5) . $insKorr . $turn;
  2727. $osv3hex = substr($osv3hex,0,$osv3len+1);
  2728. $osv3hex = sprintf("%02X", length($osv3hex)*4).$osv3hex;
  2729. Log3 $name, 4, "$name: OSV3 protocol converted to hex: ($osv3hex) with length (".((length($osv3hex)-2)*4).") bits";
  2730. #$found=1;
  2731. #$dmsg=$osv2hex;
  2732. return (1,$osv3hex);
  2733. }
  2734. return (-1,undef);
  2735. }
  2736. sub SIGNALduino_OSV1()
  2737. {
  2738. my ($name,$bitData,$rawData) = @_;
  2739. my $idx=0;
  2740. my $osv1hex ;# ~hex('0x'.$rawData);
  2741. #my $osv1bit = $bitData =~ tr/10/01/r;
  2742. my $osv1bit;
  2743. ($osv1bit = $bitData) =~ tr/10/01/;
  2744. #Log3 $name, 5, "$name: OSV1 protocol converted from ($bitData) to bit: ($osv1bit)" ;
  2745. $osv1hex=sprintf("%02X", length($rawData)*4, $osv1hex).SIGNALduino_b2h($osv1bit);
  2746. Log3 $name, 5, "$name: OSV1 protocol converted to hex: ($osv1hex) with length (".(length($rawData)*4).") bits \n";
  2747. return (1,$osv1hex);
  2748. }
  2749. sub SIGNALduino_AS()
  2750. {
  2751. my ($name,$bitData,$id) = @_;
  2752. my $debug = AttrVal($name,"debug",0);
  2753. if(index($bitData,"1100",16) >= 0) # $rawData =~ m/^A{2,3}/)
  2754. { # Valid AS detected!
  2755. my $message_start = index($bitData,"1100",16);
  2756. Debug "$name: AS protocol detected \n" if ($debug);
  2757. my $message_end=index($bitData,"1100",$message_start+16);
  2758. $message_end = length($bitData) if ($message_end == -1);
  2759. my $message_length = $message_end - $message_start;
  2760. return (-1," message is to short") if (defined($ProtocolListSIGNALduino{$id}{length_min}) && $message_length < $ProtocolListSIGNALduino{$id}{length_min} );
  2761. return (-1," message is to long") if (defined($ProtocolListSIGNALduino{$id}{length_max}) && $message_length > $ProtocolListSIGNALduino{$id}{length_max} );
  2762. my $msgbits =substr($bitData,$message_start);
  2763. my $ashex=sprintf('%02X', oct("0b$msgbits"));
  2764. Log3 $name, 5, "$name: AS protocol converted to hex: ($ashex) with length ($message_length) bits \n";
  2765. return (1,$bitData);
  2766. }
  2767. return (-1,undef);
  2768. }
  2769. sub SIGNALduino_Hideki()
  2770. {
  2771. my ($name,$bitData,$id) = @_;
  2772. my $debug = AttrVal($name,"debug",0);
  2773. Debug "$name: search in $bitData \n" if ($debug);
  2774. my $message_start = index($bitData,"10101110");
  2775. if ($message_start >= 0 ) # 0x75 but in reverse order
  2776. {
  2777. Debug "$name: Hideki protocol detected \n" if ($debug);
  2778. # Todo: Mindest Laenge fuer startpunkt vorspringen
  2779. # Todo: Wiederholung auch an das Modul weitergeben, damit es dort geprueft werden kann
  2780. my $message_end = index($bitData,"10101110",$message_start+71); # pruefen auf ein zweites 0x75, mindestens 72 bit nach 1. 0x75, da der Regensensor minimum 8 Byte besitzt je byte haben wir 9 bit
  2781. $message_end = length($bitData) if ($message_end == -1);
  2782. my $message_length = $message_end - $message_start;
  2783. return (-1,"message is to short") if (defined($ProtocolListSIGNALduino{$id}{length_min}) && $message_length < $ProtocolListSIGNALduino{$id}{length_min} );
  2784. return (-1,"message is to long") if (defined($ProtocolListSIGNALduino{$id}{length_max}) && $message_length > $ProtocolListSIGNALduino{$id}{length_max} );
  2785. my $hidekihex;
  2786. my $idx;
  2787. for ($idx=$message_start; $idx<$message_end; $idx=$idx+9)
  2788. {
  2789. my $byte = "";
  2790. $byte= substr($bitData,$idx,8); ## Ignore every 9th bit
  2791. Debug "$name: byte in order $byte " if ($debug);
  2792. $byte = scalar reverse $byte;
  2793. Debug "$name: byte reversed $byte , as hex: ".sprintf('%X', oct("0b$byte"))."\n" if ($debug);
  2794. $hidekihex=$hidekihex.sprintf('%02X', oct("0b$byte"));
  2795. }
  2796. Log3 $name, 4, "$name: hideki protocol converted to hex: $hidekihex with " .$message_length ." bits, messagestart $message_start";
  2797. return (1,$hidekihex); ## Return only the original bits, include length
  2798. }
  2799. return (-1,"");
  2800. }
  2801. sub SIGNALduino_Maverick()
  2802. {
  2803. my ($name,$bitData,$id) = @_;
  2804. my $debug = AttrVal($name,"debug",0);
  2805. if ($bitData =~ m/^.*(101010101001100110010101).*/)
  2806. { # Valid Maverick header detected
  2807. my $header_pos=$+[1];
  2808. Log3 $name, 4, "$name: Maverick protocol detected: header_pos = $header_pos";
  2809. my $hex=SIGNALduino_b2h(substr($bitData,$header_pos,26*4));
  2810. return (1,$hex); ## Return the bits unchanged in hex
  2811. } else {
  2812. return return (-1," header not found");
  2813. }
  2814. }
  2815. sub SIGNALduino_SomfyRTS()
  2816. {
  2817. my ($name, $bitData, $rawData) = @_;
  2818. #(my $negBits = $bitData) =~ tr/10/01/; # Todo: eventuell auf pack umstellen
  2819. my $encData = SIGNALduino_b2h($bitData);
  2820. #Log3 $name, 4, "$name: Somfy RTS protocol enc: $encData";
  2821. return (1, $encData);
  2822. }
  2823. # - - - - - - - - - - - -
  2824. #=item SIGNALduino_filterSign()
  2825. #This functons, will act as a filter function. It will remove the sign from the pattern, and compress message and pattern
  2826. #
  2827. # Will return $count of combined values, modified $rawData , modified %patternListRaw,
  2828. # =cut
  2829. sub SIGNALduino_filterSign($$$%)
  2830. {
  2831. my ($name,$id,$rawData,%patternListRaw) = @_;
  2832. my $debug = AttrVal($name,"debug",0);
  2833. my %buckets;
  2834. # Remove Sign
  2835. %patternListRaw = map { $_ => abs($patternListRaw{$_})} keys %patternListRaw; ## remove sing from all
  2836. my $intol=0;
  2837. my $cnt=0;
  2838. # compress pattern hash
  2839. foreach my $key (keys %patternListRaw) {
  2840. #print "chk:".$patternListRaw{$key};
  2841. #print "\n";
  2842. $intol=0;
  2843. foreach my $b_key (keys %buckets){
  2844. #print "with:".$buckets{$b_key};
  2845. #print "\n";
  2846. # $value - $set <= $tolerance
  2847. if (SIGNALduino_inTol($patternListRaw{$key},$buckets{$b_key},$buckets{$b_key}*0.25))
  2848. {
  2849. #print"\t". $patternListRaw{$key}."($key) is intol of ".$buckets{$b_key}."($b_key) \n";
  2850. $cnt++;
  2851. eval "\$rawData =~ tr/$key/$b_key/";
  2852. #if ($key == $msg_parts{clockidx})
  2853. #{
  2854. # $msg_pats{syncidx} = $buckets{$key};
  2855. # }
  2856. # elsif ($key == $msg_parts{syncidx})
  2857. # {
  2858. # $msg_pats{syncidx} = $buckets{$key};
  2859. # }
  2860. $buckets{$b_key} = ($buckets{$b_key} + $patternListRaw{$key}) /2;
  2861. #print"\t recalc to ". $buckets{$b_key}."\n";
  2862. delete ($patternListRaw{$key}); # deletes the compressed entry
  2863. $intol=1;
  2864. last;
  2865. }
  2866. }
  2867. if ($intol == 0) {
  2868. $buckets{$key}=abs($patternListRaw{$key});
  2869. }
  2870. }
  2871. return ($cnt,$rawData, %patternListRaw);
  2872. #print "rdata: ".$msg_parts{rawData}."\n";
  2873. #print Dumper (%buckets);
  2874. #print Dumper (%msg_parts);
  2875. #modify msg_parts pattern hash
  2876. #$patternListRaw = \%buckets;
  2877. }
  2878. # - - - - - - - - - - - -
  2879. #=item SIGNALduino_compPattern()
  2880. #This functons, will act as a filter function. It will remove the sign from the pattern, and compress message and pattern
  2881. #
  2882. # Will return $count of combined values, modified $rawData , modified %patternListRaw,
  2883. # =cut
  2884. sub SIGNALduino_compPattern($$$%)
  2885. {
  2886. my ($name,$id,$rawData,%patternListRaw) = @_;
  2887. my $debug = AttrVal($name,"debug",0);
  2888. my %buckets;
  2889. # Remove Sign
  2890. #%patternListRaw = map { $_ => abs($patternListRaw{$_})} keys %patternListRaw; ## remove sing from all
  2891. my $intol=0;
  2892. my $cnt=0;
  2893. # compress pattern hash
  2894. foreach my $key (keys %patternListRaw) {
  2895. #print "chk:".$patternListRaw{$key};
  2896. #print "\n";
  2897. $intol=0;
  2898. foreach my $b_key (keys %buckets){
  2899. #print "with:".$buckets{$b_key};
  2900. #print "\n";
  2901. # $value - $set <= $tolerance
  2902. if (SIGNALduino_inTol($patternListRaw{$key},$buckets{$b_key},$buckets{$b_key}*0.4))
  2903. {
  2904. #print"\t". $patternListRaw{$key}."($key) is intol of ".$buckets{$b_key}."($b_key) \n";
  2905. $cnt++;
  2906. eval "\$rawData =~ tr/$key/$b_key/";
  2907. #if ($key == $msg_parts{clockidx})
  2908. #{
  2909. # $msg_pats{syncidx} = $buckets{$key};
  2910. # }
  2911. # elsif ($key == $msg_parts{syncidx})
  2912. # {
  2913. # $msg_pats{syncidx} = $buckets{$key};
  2914. # }
  2915. $buckets{$b_key} = ($buckets{$b_key} + $patternListRaw{$key}) /2;
  2916. #print"\t recalc to ". $buckets{$b_key}."\n";
  2917. delete ($patternListRaw{$key}); # deletes the compressed entry
  2918. $intol=1;
  2919. last;
  2920. }
  2921. }
  2922. if ($intol == 0) {
  2923. $buckets{$key}=$patternListRaw{$key};
  2924. }
  2925. }
  2926. return ($cnt,$rawData, %patternListRaw);
  2927. #print "rdata: ".$msg_parts{rawData}."\n";
  2928. #print Dumper (%buckets);
  2929. #print Dumper (%msg_parts);
  2930. #modify msg_parts pattern hash
  2931. #$patternListRaw = \%buckets;
  2932. }
  2933. #print Dumper (%msg_parts);
  2934. #print "\n";
  2935. #SIGNALduino_filterSign(%msg_parts);
  2936. #print Dumper (%msg_parts);
  2937. #print "\n";
  2938. 1;
  2939. =pod
  2940. =item summary supports the same low-cost receiver for digital signals
  2941. =item summary_DE Unterst&uumltzt den gleichnamigen Low-Cost Empf&aumlnger fuer digitale Signale
  2942. =begin html
  2943. <a name="SIGNALduino"></a>
  2944. <h3>SIGNALduino</h3>
  2945. <table>
  2946. <tr><td>
  2947. The SIGNALduino ia based on an idea from mdorenka published at <a
  2948. href="http://forum.fhem.de/index.php/topic,17196.0.html">FHEM Forum</a>.
  2949. With the opensource firmware (see this <a
  2950. href="https://github.com/RFD-FHEM/SIGNALduino">link</a>) it is capable
  2951. to receive and send different protocols over different medias. Currently are 433Mhz protocols implemented.
  2952. <br><br>
  2953. The following device support is currently available:
  2954. <br><br>
  2955. Wireless switches <br>
  2956. ITv1 & ITv3/Elro and other brands using pt2263 or arctech protocol--> uses IT.pm<br>
  2957. <br><br>
  2958. Temperatur / humidity senso
  2959. <ul>
  2960. <li>PEARL NC7159, LogiLink WS0002,GT-WT-02,AURIOL,TCM97001, TCM27 and many more -> 14_CUL_TCM97001 </li>
  2961. <li>Oregon Scientific v2 and v3 Sensors -> 41_OREGON.pm</li>
  2962. <li>Temperatur / humidity sensors suppored -> 14_SD_WS07</li>
  2963. <li>technoline WS 6750 and TX70DTH -> 14_SD_WS07</li>
  2964. <li>Eurochon EAS 800z -> 14_SD_WS07</li>
  2965. <li>CTW600, WH1080 -> 14_SD_WS09 </li>
  2966. <li>Hama TS33C, Bresser Thermo/Hygro Sensor -> 14_Hideki</li>
  2967. <li>FreeTec Aussenmodul NC-7344 -> 14_SD_WS07</li>
  2968. </ul>
  2969. <br><br>
  2970. It is possible to attach more than one device in order to get better
  2971. reception, fhem will filter out duplicate messages.<br><br>
  2972. Note: this module require the Device::SerialPort or Win32::SerialPort
  2973. module. It can currently only attatched via USB.
  2974. </td>
  2975. </tr>
  2976. </table>
  2977. <a name="SIGNALduinodefine"></a>
  2978. <b>Define</b><br>
  2979. <code>define &lt;name&gt; SIGNALduino &lt;device&gt; </code> <br>
  2980. <br>
  2981. USB-connected devices (SIGNALduino):<br>
  2982. <ul><li>
  2983. &lt;device&gt; specifies the serial port to communicate with the SIGNALduino.
  2984. The name of the serial-device depends on your distribution, under
  2985. linux the cdc_acm kernel module is responsible, and usually a
  2986. /dev/ttyACM0 or /dev/ttyUSB0 device will be created. If your distribution does not have a
  2987. cdc_acm module, you can force usbserial to handle the SIGNALduino by the
  2988. following command:
  2989. <ul>
  2990. modprobe usbserial
  2991. vendor=0x03eb
  2992. product=0x204b
  2993. </ul>In this case the device is most probably
  2994. /dev/ttyUSB0.<br><br>
  2995. You can also specify a baudrate if the device name contains the @
  2996. character, e.g.: /dev/ttyACM0@57600<br><br>This is also the default baudrate
  2997. It is recommended to specify the device via a name which does not change:
  2998. e.g. via by-id devicename: /dev/serial/by-id/usb-1a86_USB2.0-Serial-if00-port0@57600
  2999. If the baudrate is "directio" (e.g.: /dev/ttyACM0@directio), then the
  3000. perl module Device::SerialPort is not needed, and fhem opens the device
  3001. with simple file io. This might work if the operating system uses sane
  3002. defaults for the serial parameters, e.g. some Linux distributions and
  3003. OSX. <br><br>
  3004. </li>
  3005. </ul>
  3006. <br>
  3007. <li><a href="#do_not_notify">do_not_notify</a></li>
  3008. <li><a href="#attrdummy">dummy</a></li>
  3009. <li>debug<br>
  3010. This will bring the module in a very verbose debug output. Usefull to find new signals and verify if the demodulation works correctly.
  3011. </li>
  3012. <li>flashCommand<br>
  3013. This is the command, that is executed to performa the firmware flash. Do not edit, if you don't know what you are doing.<br>
  3014. The default is: avrdude -p atmega328P -c arduino -P [PORT] -D -U flash:w:[HEXFILE] 2>[LOGFILE]<br>
  3015. It contains some place-holders that automatically get filled with the according values:<br>
  3016. <ul>
  3017. <li>[PORT]<br>
  3018. is the port the Signalduino is connectd to (e.g. /dev/ttyUSB0) and will be used from the defenition</li>
  3019. <li>[HEXFILE]<br>
  3020. is the .hex file that shall get flashed. There are three options (applied in this order):<br>
  3021. - passed in set flash<br>
  3022. - taken from the hexFile attribute<br>
  3023. - the default value defined in the module<br>
  3024. </li>
  3025. <li>[LOGFILE]<br>
  3026. The logfile that collects information about the flash process. It gets displayed in FHEM after finishing the flash process</li>
  3027. </ul>
  3028. </li>
  3029. <li>hardware<br>
  3030. When using the flash command, you should specify whar hardware you have connected to the usbport. Doing not, can cause failures of the device.
  3031. </li>
  3032. <li>minsecs<br>
  3033. This is a very special attribute. It is provided to other modules. minsecs should act like a threshold. All logic must be done in the logical module.
  3034. If specified, then supported modules will discard new messages if minsecs isn't past.
  3035. </li>
  3036. <li>longids<br>
  3037. Comma separated list of device-types for SIGNALduino that should be handled using long IDs. This additional ID allows it to differentiate some weather sensors, if they are sending on the same channel. Therfor a random generated id is added. If you choose to use longids, then you'll have to define a different device after battery change.<br>
  3038. Default is to not to use long IDs for all devices.
  3039. <br><br>
  3040. Examples:<PRE>
  3041. # Do not use any long IDs for any devices:
  3042. attr sduino longids 0
  3043. # Use any long IDs for all devices (this is default):
  3044. attr sduino longids 1
  3045. # Use longids for BTHR918N devices.
  3046. # Will generate devices names like BTHR918N_f3.
  3047. attr sduino longids BTHR918N
  3048. </PRE></li>
  3049. <li>whitelistIDs<br>
  3050. This attribute allows it, to specify whichs protocos are considured from this module.
  3051. Protocols which are not considured, will not generate logmessages or events. They are then completly ignored.
  3052. This makes it possible to lower ressource usage and give some better clearnes in the logs.
  3053. You can specify multiple whitelistIDs wih a colon : 0,3,7,12<br>
  3054. With a # at the beginnging whitelistIDs can be deactivated.
  3055. </li><br>
  3056. <li>WS09_Model<br>
  3057. WS09_WSModel:undef -> check all, WH1080 -> support WH1080/WS0101 , CTW600 -> support CTW600
  3058. </li>
  3059. <li>WS09_CRCAUS<br>
  3060. WS09_CRCAUS:0,1
  3061. WS09_CRCAUS = 0 is default -> check CRC Calculation for WH1080
  3062. </li><br>
  3063. <a name="SIGNALduinoget"></a>
  3064. <b>Get</b>
  3065. <ul>
  3066. <li>version<br>
  3067. return the SIGNALduino firmware version
  3068. </li><br>
  3069. <li>raw<br>
  3070. Issue a SIGNALduino firmware command, and wait for one line of data returned by
  3071. the SIGNALduino. See the SIGNALduino firmware code for details on SIGNALduino
  3072. commands. With this line, you can send almost any signal via a transmitter connected
  3073. </li><br>
  3074. <li>cmds<br>
  3075. Depending on the firmware installed, SIGNALduinos have a different set of
  3076. possible commands. Please refer to the sourcecode of the firmware of your
  3077. SIGNALduino to interpret the response of this command. See also the raw-
  3078. command.
  3079. </li><br>
  3080. <li>ITParms<br>
  3081. For sending IT Signals for wireless switches, the number of repeats and the base duration can be set.
  3082. With the get command, you can verify what is programmed into the uC.
  3083. </li><br>
  3084. <li>protocolIDs<br>
  3085. display a list of the protocol IDs
  3086. </li><br>
  3087. </ul>
  3088. <a name="SIGNALduinoset"></a>
  3089. <b>SET</b>
  3090. <ul>
  3091. <li>ITClock<br>
  3092. Sets the clock which is used to send the signal for IT switches. (Default is 300)
  3093. </li><br>
  3094. <li>raw<br>
  3095. Issue a SIGNALduino firmware command, without waiting data returned by
  3096. the SIGNALduino. See the SIGNALduino firmware code for details on SIGNALduino
  3097. commands. With this line, you can send almost any signal via a transmitter connected
  3098. To send some raw data look at these examples:
  3099. P<protocol id>#binarydata#R<num of repeats>#C<optional clock> (#C is optional)
  3100. <br>Example 1: set sduino raw SR;R=3;P0=500;P1=-9000;P2=-4000;P3=-2000;D=0302030 sends the data in raw mode 3 times repeated
  3101. <br>Example 2: set sduino raw SM;R=3;P0=500;C=250;D=A4F7FDDE sends the data manchester encoded with a clock of 250uS
  3102. <br>Example 3: set sduino raw SC;R=3;SR;P0=5000;SM;P0=500;C=250;D=A4F7FDDE sends a combined message of raw and manchester encoded repeated 3 times
  3103. <br>;
  3104. </p>
  3105. </li><br>
  3106. <li>reset<br>
  3107. This will do a reset of the usb port and normaly causes to reset the uC connected.
  3108. </li><br>
  3109. <li>close<br>
  3110. Closes the connection to the device.
  3111. </li><br>
  3112. <li>flash [hexFile]<br>
  3113. The SIGNALduino needs the right firmware to be able to receive and deliver the sensor data to fhem. In addition to the way using the
  3114. arduino IDE to flash the firmware into the SIGNALduino this provides a way to flash it directly from FHEM.
  3115. There are some requirements:
  3116. <ul>
  3117. <li>avrdude must be installed on the host<br>
  3118. On a Raspberry PI this can be done with: sudo apt-get install avrdude</li>
  3119. <li>the hardware attribute must be set if using any other hardware as an Arduino nano<br>
  3120. This attribute defines the command, that gets sent to avrdude to flash the uC.<br></li>
  3121. <br>
  3122. </ul>
  3123. </li>
  3124. <li>sendMsg<br>
  3125. This command will create the needed instructions for sending raw data via the signalduino. Insteaf of specifying the signaldata by your own you specify
  3126. a protocol and the bits you want to send. The command will generate the needed command, that the signalduino will send this.
  3127. <br><br>
  3128. Please note, that this command will work only for MU or MS protocols. You can't transmit manchester data this way.
  3129. <br><br>
  3130. Input args are:
  3131. <p>
  3132. P<protocol id>#binarydata#R<num of repeats>#C<optional clock> (#C is optional)
  3133. <br>Example: P0#0101#R3#C500
  3134. <br>Will generate the raw send command for the message 0101 with protocol 0 and instruct the arduino to send this three times and the clock is 500.
  3135. <br>SR;R=3;P0=500;P1=-9000;P2=-4000;P3=-2000;D=03020302;
  3136. </p>
  3137. </li><br>
  3138. <li>enableMessagetype<br>
  3139. Allows you to enable the message processing for
  3140. <ul>
  3141. <li>messages with sync (syncedMS),</li>
  3142. <li>messages without a sync pulse (unsyncedMU) </li>
  3143. <li>manchester encoded messages (manchesterMC) </li>
  3144. </ul>
  3145. The new state will be saved into the eeprom of your arduino.
  3146. </li><br>
  3147. <li>disableMessagetype<br>
  3148. Allows you to disable the message processing for
  3149. <ul>
  3150. <li>messages with sync (syncedMS),</li>
  3151. <li>messages without a sync pulse (unsyncedMU)</li>
  3152. <li>manchester encoded messages (manchesterMC) </li>
  3153. </ul>
  3154. The new state will be saved into the eeprom of your arduino.
  3155. </li>
  3156. </ul>
  3157. =end html
  3158. =cut