10_CUL_HM.pm 563 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572
  1. ##############################################
  2. ##############################################
  3. # CUL HomeMatic handler
  4. # $Id: 10_CUL_HM.pm 17532 2018-10-14 17:50:45Z martinp876 $
  5. package main;
  6. use strict;
  7. use warnings;
  8. use HMConfig;
  9. use Color;
  10. use Digest::MD5 qw(md5);
  11. eval "use Crypt::Rijndael";
  12. my $cryptFunc = ($@)?0:1;
  13. # ========================import constants=====================================
  14. my $culHmModel =\%HMConfig::culHmModel;
  15. my $culHmRegDefShLg =\%HMConfig::culHmRegDefShLg;
  16. my $culHmRegDefine =\%HMConfig::culHmRegDefine;
  17. my $culHmRegGeneral =\%HMConfig::culHmRegGeneral;
  18. my $culHmRegType =\%HMConfig::culHmRegType;
  19. my $culHmRegModel =\%HMConfig::culHmRegModel;
  20. my $culHmRegChan =\%HMConfig::culHmRegChan;
  21. my $culHmGlobalGets =\%HMConfig::culHmGlobalGets;
  22. my $culHmVrtGets =\%HMConfig::culHmVrtGets;
  23. my $culHmSubTypeGets =\%HMConfig::culHmSubTypeGets;
  24. my $culHmModelGets =\%HMConfig::culHmModelGets;
  25. my $culHmSubTypeDevSets =\%HMConfig::culHmSubTypeDevSets;
  26. my $culHmGlobalSetsChn =\%HMConfig::culHmGlobalSetsChn;
  27. my $culHmReglSets =\%HMConfig::culHmReglSets;
  28. my $culHmGlobalSets =\%HMConfig::culHmGlobalSets;
  29. my $culHmGlobalSetsVrtDev =\%HMConfig::culHmGlobalSetsVrtDev;
  30. my $culHmSubTypeSets =\%HMConfig::culHmSubTypeSets;
  31. my $culHmModelSets =\%HMConfig::culHmModelSets;
  32. my $culHmChanSets =\%HMConfig::culHmChanSets;
  33. my $culHmFunctSets =\%HMConfig::culHmFunctSets;
  34. my $culHmBits =\%HMConfig::culHmBits;
  35. my $culHmCmdFlags =\@HMConfig::culHmCmdFlags;
  36. my $K_actDetID ="000000";
  37. my %activeCmds = ( "valvePos" => 1,"up" => 1,"unlock" => 1,"toggleDir" => 1
  38. ,"toggle" => 1
  39. ,"tempListWed" => 1,"tempListTue" => 1,"tempListThu" => 1,"tempListSun" => 1
  40. ,"tempListSat" => 1,"tempListMon" => 1,"tempListFri" => 1
  41. ,"stop" => 1,"setRepeat" => 1
  42. ,"reset" => 1,"regSet" => 1,"regBulk" => 1
  43. ,"press" => 1,"postEvent" => 1,"playTone" => 1
  44. ,"peerIODev" => 1,"peerChan" => 1,"peerBulk" => 1
  45. ,"pctSlat" => 1,"pctLvlSlat" => 1,"pct" => 1,"pair" => 1
  46. ,"open" => 1,"on" => 1,"old" => 1,"off" => 1
  47. ,"lock" => 1,"level" => 1,"led" => 1
  48. ,"keydef" => 1,"fwUpdate" => 1,"down" => 1
  49. ,"controlParty" => 1,"controlManu" => 1
  50. ,"color" => 1,"colProgram" => 1,"brightCol" => 1,"brightAuto" => 1
  51. ,"on-till" => 1,"on-for-timer" => 1,"desired-temp" => 1
  52. );
  53. ############################################################
  54. sub CUL_HM_Initialize($);
  55. sub CUL_HM_reqStatus($);
  56. sub CUL_HM_autoReadConfig();
  57. sub CUL_HM_updateConfig($);
  58. sub CUL_HM_Define($$);
  59. sub CUL_HM_Undef($$);
  60. sub CUL_HM_Rename($$$);
  61. sub CUL_HM_Attr(@);
  62. sub CUL_HM_Parse($$);
  63. sub CUL_HM_parseCommon(@);
  64. sub CUL_HM_qAutoRead($$);
  65. sub CUL_HM_Get($@);
  66. sub CUL_HM_Set($@);
  67. sub CUL_HM_valvePosUpdt(@);
  68. sub CUL_HM_infoUpdtDevData($$$);
  69. sub CUL_HM_infoUpdtChanData(@);
  70. sub CUL_HM_getConfig($);
  71. sub CUL_HM_SndCmd($$);
  72. sub CUL_HM_responseSetup($$);
  73. sub CUL_HM_eventP($$);
  74. sub CUL_HM_protState($$);
  75. sub CUL_HM_respPendRm($);
  76. sub CUL_HM_respPendTout($);
  77. sub CUL_HM_respPendToutProlong($);
  78. sub CUL_HM_PushCmdStack($$);
  79. sub CUL_HM_ProcessCmdStack($);
  80. sub CUL_HM_pushConfig($$$$$$$$@);
  81. sub CUL_HM_ID2PeerList ($$$);
  82. sub CUL_HM_peerChId($$);
  83. sub CUL_HM_peerChName($$);
  84. sub CUL_HM_getMId($);
  85. sub CUL_HM_getRxType($);
  86. sub CUL_HM_getAssChnIds($);
  87. sub CUL_HM_h2IoId($);
  88. sub CUL_HM_IoId($);
  89. sub CUL_HM_hash2Id($);
  90. sub CUL_HM_hash2Name($);
  91. sub CUL_HM_name2Hash($);
  92. sub CUL_HM_name2Id(@);
  93. sub CUL_HM_id2Name($);
  94. sub CUL_HM_id2Hash($);
  95. sub CUL_HM_getDeviceHash($);
  96. sub CUL_HM_getDeviceName($);
  97. sub CUL_HM_DumpProtocol($$@);
  98. sub CUL_HM_getRegFromStore($$$$@);
  99. sub CUL_HM_updtRegDisp($$$);
  100. sub CUL_HM_encodeTime8($);
  101. sub CUL_HM_decodeTime8($);
  102. sub CUL_HM_encodeTime16($);
  103. sub CUL_HM_convTemp($);
  104. sub CUL_HM_decodeTime16($);
  105. sub CUL_HM_secSince2000();
  106. sub CUL_HM_getChnLvl($);
  107. sub CUL_HM_initRegHash();
  108. sub CUL_HM_fltCvT($);
  109. sub CUL_HM_CvTflt($);
  110. sub CUL_HM_getRegN($$@);
  111. sub CUL_HM_4DisText($);
  112. sub CUL_HM_TCtempReadings($);
  113. sub CUL_HM_repReadings($);
  114. sub CUL_HM_dimLog($);
  115. sub CUL_HM_ActGetCreateHash();
  116. sub CUL_HM_time2sec($);
  117. sub CUL_HM_ActAdd($$);
  118. sub CUL_HM_ActDel($);
  119. sub CUL_HM_ActCheck($);
  120. sub CUL_HM_UpdtReadBulk(@);
  121. sub CUL_HM_UpdtReadSingle(@);
  122. sub CUL_HM_setAttrIfCh($$$$);
  123. sub CUL_HM_noDup(@); #return list with no duplicates
  124. sub CUL_HM_noDupInString($);#return string with no duplicates, comma separated
  125. sub CUL_HM_storeRssi(@);
  126. sub CUL_HM_qStateUpdatIfEnab($@);
  127. sub CUL_HM_getAttrInt($@);
  128. sub CUL_HM_appFromQ($$);
  129. sub CUL_HM_autoReadReady($);
  130. sub CUL_HM_calcDisWm($$$);
  131. sub CUL_HM_statCnt(@);
  132. sub CUL_HM_trigLastEvent($$$$$);
  133. # ----------------modul globals-----------------------
  134. my $respRemoved; # used to control trigger of stack processing
  135. my $IOpoll = 0.2;# poll speed to scan IO device out of order
  136. my $maxPendCmds = 10; #number of parallel requests
  137. my @evtEt = (); #readings for entities. Format hash:trigger:reading:value
  138. my $evtDly = 0; # ugly switch to delay set readings if in parser - actually not our job, but fhem.pl refuses
  139. # need to take care that ACK is first
  140. #+++++++++++++++++ startup, init, definition+++++++++++++++++++++++++++++++++++
  141. sub CUL_HM_Initialize($) {
  142. my ($hash) = @_;
  143. $hash->{Match} = "^A....................";
  144. $hash->{DefFn} = "CUL_HM_Define";
  145. $hash->{UndefFn} = "CUL_HM_Undef";
  146. $hash->{ParseFn} = "CUL_HM_Parse";
  147. $hash->{SetFn} = "CUL_HM_Set";
  148. $hash->{GetFn} = "CUL_HM_Get";
  149. $hash->{RenameFn} = "CUL_HM_Rename";
  150. $hash->{AttrFn} = "CUL_HM_Attr";
  151. $hash->{NotifyFn} = "CUL_HM_Notify";
  152. $hash->{Attr}{dev} = "ignore:1,0 dummy:1,0 " # -- device only attributes
  153. ."IODev IOList IOgrp "
  154. ."rssiLog:1,0 " # enable writing RSSI to Readings (device only)
  155. ."actCycle " # also for action detector
  156. ."hmKey hmKey2 hmKey3 "
  157. ;
  158. $hash->{Attr}{devPhy} = # -- physical device only attributes
  159. "serialNr firmware .stc .devInfo "
  160. ."actStatus "
  161. ."autoReadReg:0_off,1_restart,2_pon-restart,3_onChange,4_reqStatus,5_readMissing,8_stateOnly "
  162. ."burstAccess:0_off,1_auto "
  163. ."msgRepeat "
  164. ."hmProtocolEvents:0_off,1_dump,2_dumpFull,3_dumpTrigger "
  165. ."aesKey:5,4,3,2,1,0 "
  166. ;
  167. $hash->{Attr}{chn} = "repPeers " # -- channel only attributes
  168. ."peerIDs "
  169. ."tempListTmpl "
  170. ."levelRange levelMap "
  171. ."cyclicMsgOffset "
  172. ;
  173. $hash->{Attr}{glb} = "do_not_notify:1,0 showtime:1,0 "
  174. ."rawToReadable unit "#"KFM-Sensor" only
  175. ."expert:0_defReg,1_allReg,2_defReg+raw,3_allReg+raw,4_off,8_templ+default,12_templOnly,251_anything "
  176. ."param "
  177. ."readOnly:0,1 "
  178. ."actAutoTry:0_off,1_on "
  179. ."aesCommReq:1,0 " # IO will request AES if
  180. ;
  181. $hash->{AttrList} = $hash->{Attr}{glb}
  182. .$hash->{Attr}{dev}
  183. .$hash->{Attr}{devPhy}
  184. .$hash->{Attr}{chn}
  185. .$readingFnAttributes
  186. ;
  187. CUL_HM_initRegHash();
  188. my @modellist;
  189. foreach my $model (keys %{$culHmModel}){
  190. push @modellist,$culHmModel->{$model}{name};
  191. }
  192. $hash->{AttrList} .= " model:" .join(",", sort @modellist);
  193. $hash->{AttrList} .= " subType:".join(",",
  194. CUL_HM_noDup(map { $culHmModel->{$_}{st} } keys %{$culHmModel}));
  195. $hash->{prot}{rspPend} = 0;#count Pending responses
  196. my @statQArr = ();
  197. my @statQWuArr = ();
  198. my @confQArr = ();
  199. my @confQWuArr = ();
  200. my @confCheckArr = ();
  201. my @confUpdt = ();
  202. $hash->{helper}{qReqStat} = \@statQArr;
  203. $hash->{helper}{qReqStatWu} = \@statQWuArr;
  204. $hash->{helper}{qReqConf} = \@confQArr;
  205. $hash->{helper}{qReqConfWu} = \@confQWuArr;
  206. $hash->{helper}{confCheckArr} = \@confCheckArr;
  207. $hash->{helper}{confUpdt} = \@confUpdt;
  208. $hash->{helper}{cfgCmpl}{init}= 1;# mark entities with complete config
  209. #statistics
  210. $hash->{stat}{s}{dummy}=0;
  211. $hash->{stat}{r}{dummy}=0;
  212. RemoveInternalTimer("StatCntRfresh");
  213. InternalTimer(gettimeofday()+3600*20,"CUL_HM_statCntRfresh","StatCntRfresh", 0);
  214. $hash->{hmIoMaxDly} = 60;# poll timeout - stop poll and discard
  215. $hash->{hmAutoReadScan} = 4; # delay autoConf readings
  216. $hash->{helper}{hmManualOper} = 0;# default automode
  217. }
  218. sub CUL_HM_updateConfig($){
  219. # this routine is called 5 sec after the last define of a restart
  220. # this gives FHEM sufficient time to fill in attributes
  221. # it will also be called after each manual definition
  222. # Purpose is to parse attributes and read config
  223. RemoveInternalTimer("updateConfig");
  224. if (!$init_done){
  225. InternalTimer(1,"CUL_HM_updateConfig", "updateConfig", 0);#start asap once FHEM is operational
  226. return;
  227. }
  228. foreach my $name (@{$modules{CUL_HM}{helper}{updtCfgLst}}){
  229. my $hash = $defs{$name};
  230. next if (!$hash->{DEF}); # likely renamed
  231. foreach my $read (grep/(RegL_0.:|_chn:\d\d)/,keys%{$hash->{READINGS}}){
  232. my $readN = $read;
  233. $readN =~ s/(RegL_0.):/$1\./;
  234. $readN =~ s/_chn:(\d\d)/_chn-$1/;
  235. $hash->{READINGS}{$readN} = $hash->{READINGS}{$read};
  236. delete $hash->{READINGS}{$read};
  237. }
  238. my $id = $hash->{DEF};
  239. my $nAttr = $modules{CUL_HM}{helper}{hmManualOper};# no update for attr
  240. if ($id eq $K_actDetID){# if action detector
  241. $attr{$name}{"event-on-change-reading"} =
  242. AttrVal($name, "event-on-change-reading", ".*")
  243. if(!$nAttr);
  244. $attr{$name}{model} = "ActionDetector";
  245. delete $hash->{helper}{role};
  246. delete $attr{$name}{$_}
  247. foreach ( "autoReadReg","actCycle","actStatus","burstAccess","serialNr"
  248. ,"IODev","IOList","IOgrp","hmProtocolEvents","rssiLog");
  249. #$hash->{helper}{role}{vrt} = 1;
  250. #$hash->{helper}{role}{dev} = 1;
  251. next;
  252. }
  253. CUL_HM_ID2PeerList($name,"",1); # update peerList out of peerIDs
  254. CUL_HM_getMId($hash); # need to set regLst in helper
  255. my $chn = substr($id."00",6,2);
  256. my $st = CUL_HM_Get($hash,$name,"param","subType");
  257. my $md = CUL_HM_Get($hash,$name,"param","model");
  258. my $dHash = CUL_HM_getDeviceHash($hash);
  259. $dHash->{helper}{role}{prs} = 1 if($hash->{helper}{regLst} && $hash->{helper}{regLst} =~ m/3p/);
  260. foreach my $rName ("D-firmware","D-serialNr",".D-devInfo",".D-stc"){
  261. # move certain attributes to readings for future handling
  262. my $aName = $rName;
  263. $aName =~ s/D-//;
  264. my $aVal = AttrVal($name,$aName,undef);
  265. CUL_HM_UpdtReadSingle($hash,$rName,$aVal,0)
  266. if (!defined ReadingsVal($name,$rName,undef));
  267. }
  268. if ($md =~ /(HM-CC-TC|ROTO_ZEL-STG-RM-FWT)/){
  269. $hash->{helper}{role}{chn} = 1 if (length($id) == 6); #tc special
  270. }
  271. elsif ($md =~ m/^HM-CC-RT-DN/){
  272. $hash->{helper}{shRegR}{"07"} = "00" if ($chn eq "04");# shadowReg List 7 read from CH 0
  273. $hash->{helper}{shRegW}{"07"} = "04" if ($chn eq "00");# shadowReg List 7 write to CH 4
  274. }
  275. elsif ($md =~ m/^HM-TC-IT-WM-W-EU/){
  276. $hash->{helper}{shRegR}{"07"} = "00" if ($chn eq "02");# shadowReg List 7 read from CH 0
  277. $hash->{helper}{shRegW}{"07"} = "02" if ($chn eq "00");# shadowReg List 7 write to CH 4
  278. }
  279. elsif ($md =~ m/^(HM-CC-VD|ROTO_ZEL-STG-RM-FSA)/){
  280. $hash->{helper}{oldDes} = "0";
  281. }
  282. elsif ($md =~ m/^(HM-Dis-WM55)/){
  283. foreach my $t ("s","l"){
  284. if(!defined $hash->{helper}{dispi}{$t}{"l1"}{d}){# setup if one is missing
  285. $hash->{helper}{dispi}{$t}{"l$_"}{d}=1 foreach (1,2,3,4,5,6);
  286. }
  287. }
  288. }
  289. elsif ($md =~ m/^(HM-Dis-EP-WM55)/){
  290. CUL_HM_UpdtReadSingle($hash,"state","-",0) if(InternalVal($name,"chanNo",0)>3);
  291. }
  292. elsif ($md =~ m/^(CCU-FHEM)/){
  293. $hash->{helper}{role}{vrt} = 1;
  294. if($hash->{helper}{role}{dev}){
  295. CUL_HM_UpdtCentral($name); # first update, then keys
  296. foreach my $io (split ",",AttrVal($name,"IOList","")) {
  297. next if(!$defs{$io});
  298. if($defs{$io}->{TYPE} eq "HMLAN" && eval "defined(&HMLAN_writeAesKey)"){
  299. HMLAN_writeAesKey($io);
  300. }
  301. elsif ($defs{$io}->{TYPE} eq "HMUARTLGW") {
  302. CallFn($io,"WriteFn",$defs{$io},undef,"writeAesKey:${io}");
  303. }
  304. elsif ($defs{$io}->{TYPE} =~ m/^(TSCUL|TSSTACKED)$/
  305. && eval "defined(&TSCUL_WriteAesKeyHM)"){
  306. TSCUL_WriteAesKeyHM($io); # noansi: for TSCUL
  307. }
  308. }
  309. }
  310. }
  311. elsif ($st =~ m/^(motionDetector|motionAndBtn)$/ ){
  312. CUL_HM_UpdtReadSingle($hash,"state","-",0);
  313. CUL_HM_UpdtReadSingle($hash,"motion","-",0);
  314. RemoveInternalTimer($name.":motionCheck");
  315. InternalTimer(gettimeofday()+30+2,"CUL_HM_motionCheck", $name.":motionCheck", 0);
  316. }
  317. elsif ($st eq "dimmer" ) {#setup virtual dimmer channels
  318. my $mId = CUL_HM_getMId($hash);
  319. #configure Dimmer virtual channel assotiation
  320. if ($hash->{helper}{role}{chn}){
  321. my $chn = (length($id) == 8)?substr($id,6,2):"01";
  322. my $devId = substr($id,0,6);
  323. if ($culHmModel->{$mId} && $culHmModel->{$mId}{chn} =~ m/Dim_V/){#virtual?
  324. my @chnPh = (grep{$_ =~ m/Sw:/ } split ',',$culHmModel->{$mId}{chn});
  325. @chnPh = split ':',$chnPh[0] if (@chnPh);
  326. my $chnPhyMax = $chnPh[2]?$chnPh[2]:1; # max Phys channels
  327. my $chnPhy = ($chnPhyMax == 2 && $chn > 4)?2:1; # assotiated phy chan( either 1 or 2)
  328. my $idPhy = $devId.sprintf("%02X",$chnPhy);# ID assot phy chan
  329. my $pHash = CUL_HM_id2Hash($idPhy); # hash assot phy chan
  330. $idPhy = $pHash->{DEF}; # could be device!!!
  331. if ($pHash){
  332. $pHash->{helper}{vDim}{idPhy} = $idPhy;
  333. my $vHash = CUL_HM_id2Hash($devId.sprintf("%02X",$chnPhyMax+2*$chnPhy-1));
  334. if ($vHash){
  335. $pHash->{helper}{vDim}{idV2} = $vHash->{DEF};
  336. $vHash->{helper}{vDim}{idPhy} = $idPhy;
  337. }
  338. else{
  339. delete $pHash->{helper}{vDim}{idV2};
  340. }
  341. $vHash = CUL_HM_id2Hash($devId.sprintf("%02X",$chnPhyMax+2*$chnPhy));
  342. if ($vHash){
  343. $pHash->{helper}{vDim}{idV3} = $vHash->{DEF};
  344. $vHash->{helper}{vDim}{idPhy} = $idPhy;
  345. }
  346. else{
  347. delete $pHash->{helper}{vDim}{idV3};
  348. }
  349. }
  350. }
  351. }
  352. }
  353. elsif ($st eq "virtual" ) {#setup virtuals
  354. $hash->{helper}{role}{vrt} = 1;
  355. if ( $hash->{helper}{fkt}
  356. && $hash->{helper}{fkt} =~ m/^(vdCtrl|virtThSens)$/){
  357. my $vId = substr($id."01",0,8);
  358. $hash->{helper}{virtTC} = "00";
  359. $hash->{helper}{vd}{msgRed}= 0 if(!defined $hash->{helper}{vd}{msgRed});
  360. if(!defined $hash->{helper}{vd}{next}){
  361. ($hash->{helper}{vd}{msgCnt},$hash->{helper}{vd}{next}) =
  362. split(";",ReadingsVal($name,".next","0;".gettimeofday()));
  363. $hash->{helper}{vd}{idl} = 0;
  364. $hash->{helper}{vd}{idh} = 0;
  365. }
  366. my $d =ReadingsVal($name,"valvePosTC","");
  367. $d =~ s/ %//;
  368. CUL_HM_Set($hash,$name,"valvePos",$d);
  369. CUL_HM_Set($hash,$name,"virtTemp",ReadingsVal($name,"temperature",""));
  370. CUL_HM_Set($hash,$name,"virtHum" ,ReadingsVal($name,"humidity",""));
  371. CUL_HM_UpdtReadSingle($hash,"valveCtrl","restart",1) if (ReadingsVal($name,"valvePosTC",""));
  372. RemoveInternalTimer("valvePos:$vId");
  373. RemoveInternalTimer("valveTmr:$vId");
  374. InternalTimer($hash->{helper}{vd}{next}
  375. ,"CUL_HM_valvePosUpdt","valvePos:$vId",0);
  376. # delete - virtuals dont have regs
  377. delete $attr{$name}{$_}
  378. foreach ("autoReadReg","actCycle","actStatus","burstAccess","serialNr");
  379. }
  380. }
  381. elsif ($st eq "sensRain") {
  382. $hash->{helper}{lastRain} = ReadingsTimestamp($name,"state","")
  383. if (ReadingsVal($name,"state","") eq "rain");
  384. }
  385. next if ($nAttr);# stop if default setting if attributes is not desired
  386. my $actCycle = AttrVal($name,"actCycle",undef);
  387. CUL_HM_ActAdd($id,$actCycle) if ($actCycle );#add 2 ActionDetect?
  388. # --- set default attributes if missing ---
  389. if ($hash->{helper}{role}{dev}){
  390. if( $st ne "virtual"){
  391. $attr{$name}{expert} = AttrVal($name,"expert" ,"2_raw");
  392. $attr{$name}{autoReadReg}= AttrVal($name,"autoReadReg","4_reqStatus");
  393. CUL_HM_hmInitMsg($hash);
  394. }
  395. if (CUL_HM_getRxType($hash)&0x02){#burst dev must restrict retries!
  396. $attr{$name}{msgRepeat} = 1 if (!$attr{$name}{msgRepeat});
  397. }
  398. }
  399. CUL_HM_Attr("attr",$name,"expert",$attr{$name}{expert})
  400. if ($attr{$name}{expert});#need update after readings are available
  401. if ($chn eq "03" &&
  402. $md =~ /(-TC|ROTO_ZEL-STG-RM-FWT|HM-CC-RT-DN)/){
  403. $attr{$name}{stateFormat} = "last:trigLast";
  404. }
  405. foreach(keys %{$attr{$name}}){
  406. delete $attr{$name}{$_} if(CUL_HM_AttrCheck($name,$_));
  407. }
  408. CUL_HM_chgExpLvl($hash);# need to update expert visib as of device
  409. # -+-+-+-+-+ add default web-commands
  410. my $webCmd;
  411. $webCmd = AttrVal($name,"webCmd",undef);
  412. if(!defined $webCmd){
  413. if ($st eq "virtual" ){
  414. if ($hash->{helper}{fkt} && $hash->{helper}{fkt} eq "sdLead1") {$webCmd="teamCall:alarmOn:alarmOff";}
  415. elsif($hash->{helper}{fkt} && $hash->{helper}{fkt} eq "vdCtrl") {$webCmd="valvePos";}
  416. elsif($hash->{helper}{fkt} && $hash->{helper}{fkt} eq "virtThSens"){$webCmd="virtTemp:virtHum";}
  417. elsif(!$hash->{helper}{role}{dev}) {$webCmd="press short:press long";}
  418. elsif($md =~ m/^virtual_/) {$webCmd="virtual";}
  419. elsif($md eq "CCU-FHEM") {$webCmd="virtual:update";}
  420. }
  421. elsif((!$hash->{helper}{role}{chn} &&
  422. $md !~ m/^(HM-CC-TC|ROTO_ZEL-STG-RM-FWT)/)
  423. ||$st eq "repeater"
  424. ||$md =~ m/^(HM-CC-VD|ROTO_ZEL-STG-RM-FSA)/ ){$webCmd="getConfig:clear msgEvents";
  425. if ($md =~ m/^HM-CC-RT-DN/) {$webCmd.=":burstXmit";}
  426. }
  427. elsif($st eq "blindActuator"){
  428. if ($hash->{helper}{role}{chn}){$webCmd="statusRequest:toggleDir:on:off:up:down:stop";}
  429. else{ $webCmd="statusRequest:getConfig:clear msgEvents";}
  430. }
  431. elsif($st eq "dimmer" ){
  432. if ($hash->{helper}{role}{chn}){$webCmd="statusRequest:toggle:on:off:up:down";}
  433. else{ $webCmd="statusRequest:getConfig:clear msgEvents";}
  434. }
  435. elsif($st eq "switch" ){
  436. if ($hash->{helper}{role}{chn}){$webCmd="statusRequest:toggle:on:off";}
  437. else{ $webCmd="statusRequest:getConfig:clear msgEvents";}
  438. }
  439. elsif($st eq "smokeDetector"){ $webCmd="statusRequest";
  440. if (defined $hash->{helper}{fkt} && $hash->{helper}{fkt} eq "sdLead1"){
  441. $webCmd.=":teamCall:alarmOn:alarmOff";}
  442. }
  443. elsif($st eq "keyMatic" ){ $webCmd="lock:inhibit on:inhibit off";
  444. }
  445. elsif( $md eq "HM-OU-CFM-PL"
  446. ||$md eq "HM-OU-CFM-TW" ){ $webCmd="press short:press long"
  447. .($chn eq "02"?":playTone replay":"");
  448. }
  449. if ($webCmd){
  450. my $eventMap = AttrVal($name,"eventMap",undef);
  451. my @wc;
  452. push @wc,ReplaceEventMap($name, $_, 1) foreach (split ":",$webCmd);
  453. $webCmd = join ":",@wc;
  454. }
  455. }
  456. $attr{$name}{webCmd} = $webCmd if ($webCmd);
  457. CUL_HM_qStateUpdatIfEnab($name);
  458. next if (0 == (0x07 & CUL_HM_getAttrInt($name,"autoReadReg")));
  459. if(CUL_HM_peerUsed($name) == 2){
  460. CUL_HM_qAutoRead($name,1);
  461. }
  462. else{
  463. foreach(CUL_HM_reglUsed($name)){
  464. next if (!$_);
  465. if(ReadingsVal($name,$_,"x") !~ m/00:00/){
  466. CUL_HM_qAutoRead($name,1);
  467. last;
  468. }
  469. }
  470. }
  471. #remove invalid attributes
  472. if (!$hash->{helper}{role}{dev}){
  473. my @l = split(" ",$modules{CUL_HM}{Attr}{dev});
  474. map {$_ =~ s/\:.*//} @l;
  475. foreach (@l){
  476. delete $attr{$name}{$_} if (defined $attr{$name}{$_});
  477. }
  478. }
  479. if (!$hash->{helper}{role}{chn}){
  480. my @l = split(" ",$modules{CUL_HM}{Attr}{chn});
  481. map {$_ =~ s/\:.*//} @l;
  482. foreach (@l){
  483. delete $attr{$name}{$_} if (defined $attr{$name}{$_});
  484. }
  485. }
  486. CUL_HM_complConfig($name);
  487. }
  488. delete $modules{CUL_HM}{helper}{updtCfgLst};
  489. }
  490. sub CUL_HM_Define($$) {##############################
  491. my ($hash, $def) = @_;
  492. my @a = split("[ \t][ \t]*", $def);
  493. my $HMid = uc($a[2]);
  494. return "wrong syntax: define <name> CUL_HM 6-digit-hex-code [Raw-Message]"
  495. if(!(int(@a)==3 || int(@a)==4) || $HMid !~ m/^[A-F0-9]{6}([A-F0-9]{2})?$/i );
  496. return "HMid DEF already used by " . CUL_HM_id2Name($HMid)
  497. if ($modules{CUL_HM}{defptr}{$HMid});
  498. my $name = $hash->{NAME};
  499. if(length($HMid) == 8) {# define a channel
  500. my $devHmId = substr($HMid, 0, 6);
  501. my $chn = substr($HMid, 6, 2);
  502. my $devHash = $modules{CUL_HM}{defptr}{$devHmId};
  503. return "please define a device with hmId:".$devHmId." first" if(!$devHash);
  504. my $devName = $devHash->{NAME};
  505. $hash->{device} = $devName; #readable ref to device name
  506. $hash->{chanNo} = $chn; #readable ref to Channel
  507. $devHash->{"channel_$chn"} = $name; #reference in device as well
  508. $attr{$name}{model} = AttrVal($devName, "model", undef);
  509. $hash->{helper}{role}{chn}=1;
  510. if($chn eq "01"){
  511. $attr{$name}{peerIDs} = AttrVal($devName, "peerIDs", "");
  512. $hash->{READINGS}{peerList}{VAL} = ReadingsVal($devName,"peerList","");
  513. $hash->{peerList} = $devHash->{peerList} if($devHash->{peerList});
  514. delete $devHash->{helper}{role}{chn};#device no longer
  515. delete $devHash->{peerList};
  516. delete $devHash->{READINGS}{peerList};
  517. delete $attr{$devName}{peerIDs};
  518. }
  519. }
  520. else{# define a device
  521. $hash->{helper}{role}{dev} = 1;
  522. $hash->{helper}{role}{chn} = 1;# take role of chn 01 until it is defined
  523. $hash->{helper}{q}{qReqConf} = ""; # queue autoConfig requests
  524. $hash->{helper}{q}{qReqStat} = ""; # queue statusRequest for this device
  525. $hash->{helper}{mRssi}{mNo} = "";
  526. $hash->{helper}{HM_CMDNR} = int(rand(250));# should be different from previous
  527. CUL_HM_prtInit ($hash);
  528. $hash->{helper}{io}{vccu} = "";
  529. $hash->{helper}{io}{prefIO} = undef;
  530. if ( $HMid ne "000000"
  531. && eval "defined(&TSCUL_RestoreHMDev)") {
  532. my $restoredIOname = TSCUL_RestoreHMDev($hash, $HMid); # noansi: restore IODev from TSCUL before the first CUL_HM_assignIO
  533. if (defined($restoredIOname)) {
  534. $hash->{IODev} = $defs{$restoredIOname};
  535. $hash->{helper}{io}{restoredIO} = $restoredIOname; # noansi: until attributes are filled, this should be the first choice
  536. @{$hash->{helper}{mRssi}{io}{$restoredIOname}} = (100,100); # noansi: set IO high rssi for first autoassign
  537. }
  538. }
  539. CUL_HM_assignIO($hash)if (!$init_done && $HMid ne "000000");
  540. }
  541. $modules{CUL_HM}{defptr}{$HMid} = $hash;
  542. $hash->{NOTIFYDEV} = "global";
  543. #- - - - create auto-update - - - - - -
  544. CUL_HM_ActGetCreateHash() if($HMid eq '000000');#startTimer
  545. $hash->{DEF} = $HMid;
  546. CUL_HM_Parse($hash, $a[3]) if(int(@a) == 4);
  547. CUL_HM_queueUpdtCfg($name);
  548. return undef;
  549. }
  550. sub CUL_HM_Undef($$) {###############################
  551. my ($hash, $name) = @_;
  552. my $devName = $hash->{device};
  553. my $HMid = $hash->{DEF};
  554. CUL_HM_unQEntity($name,"qReqConf");
  555. CUL_HM_unQEntity($name,"qReqStat");
  556. CUL_HM_complConfigTestRm($name);
  557. my $chn = substr($HMid,6,2);
  558. if ($chn){# delete a channel
  559. my $devHash = $defs{$devName};
  560. delete $devHash->{"channel_$chn"} if ($devName);
  561. $devHash->{helper}{role}{chn}=1 if($chn eq "01");# return chan 01 role
  562. }
  563. else{# delete a device
  564. CommandDelete(undef,$hash->{$_}) foreach (grep(/^channel_/,keys %{$hash}));
  565. }
  566. delete($modules{CUL_HM}{defptr}{$HMid});
  567. return undef;
  568. }
  569. sub CUL_HM_Rename($$$) {#############################
  570. my ($name, $oldName) = @_;
  571. my $hash = $defs{$name};
  572. return if($hash->{TYPE} ne "CUL_HM");
  573. my $HMid = CUL_HM_name2Id($name);
  574. if (!$hash->{helper}{role}{dev}){# we are channel, inform the device
  575. $hash->{chanNo} = substr($HMid,6,2);
  576. my $devHash = CUL_HM_id2Hash(substr($HMid,0,6));
  577. $hash->{device} = $devHash->{NAME};
  578. $devHash->{"channel_".$hash->{chanNo}} = $name;
  579. }
  580. else{# we are a device - inform channels if exist
  581. foreach (grep (/^channel_/, keys%{$hash})){
  582. next if(!$_);
  583. my $chnHash = $defs{$hash->{$_}};
  584. $chnHash->{device} = $name;
  585. }
  586. CUL_HM_UpdtCentral($name) if (AttrVal($name, "model", "") eq "CCU-FHEM");
  587. }
  588. if ($hash->{helper}{role}{chn}){
  589. my $HMidCh = substr($HMid."01",0,8);
  590. foreach my $pId (keys %{$modules{CUL_HM}{defptr}}){#all devices for peer
  591. my $pH = $modules{CUL_HM}{defptr}{$pId};
  592. my $pN = $pH->{NAME};
  593. my $pPeers = AttrVal($pN, "peerIDs", "");
  594. if ($pPeers =~ m/$HMidCh/){
  595. CUL_HM_ID2PeerList ($pN,"x",0);
  596. foreach my $pR (grep /(-|\.)$oldName(-|$)/,keys%{$pH->{READINGS}}){#update reading of the peer
  597. my $pRn = $pR;
  598. $pRn =~ s/$oldName/$name/;
  599. $pH->{READINGS}{$pRn}{VAL} = $pH->{READINGS}{$pR}{VAL};
  600. $pH->{READINGS}{$pRn}{TIME} = $pH->{READINGS}{$pR}{TIME};
  601. delete $pH->{READINGS}{$pR};
  602. }
  603. if (eval "defined(&HMinfo_templateMark)"){
  604. foreach my $pT (grep /$oldName:/,keys%{$pH->{helper}{tmpl}}){#update reading of the peer
  605. my $param = $pH->{helper}{tmpl}{$pT};
  606. my ($px,$tmpl) = split(">",$pT);
  607. HMinfo_templateDel($pN,$tmpl,$px);
  608. $px =~ s/$oldName/$name/;
  609. HMinfo_templateMark($pH,"$px>$tmpl",split(" ",$param));
  610. }
  611. }
  612. }
  613. }
  614. }
  615. return;
  616. }
  617. sub CUL_HM_Attr(@) {#################################
  618. my ($cmd,$name, $attrName,$attrVal) = @_;
  619. my $chk = ($cmd eq "set")?CUL_HM_AttrCheck($name, $attrName):"";
  620. my $hash = CUL_HM_name2Hash($name);
  621. return $chk if ($chk);
  622. my $updtReq = 0;
  623. if ($attrName eq "expert"){#[0,1,2]
  624. $attr{$name}{$attrName} = $attrVal;
  625. CUL_HM_chgExpLvl($_) foreach ((map{CUL_HM_id2Hash($_)} CUL_HM_getAssChnIds($name)),$defs{$name});
  626. }
  627. elsif($attrName eq "readOnly"){#[0,1]
  628. if ($cmd eq "set"){
  629. return "$attrName: $attrVal not allowed. Should be one of 0,1" if (int($attrVal) > 1);
  630. }
  631. }
  632. elsif($attrName eq "actCycle"){#"000:00" or 'off'
  633. if ($cmd eq "set"){
  634. if (CUL_HM_name2Id($name) eq $K_actDetID){
  635. return "$attrName must be higher then 30, $attrVal not allowed"
  636. if ($attrVal < 30);
  637. }
  638. else{
  639. return "attribut not allowed for channels"
  640. if (!$hash->{helper}{role}{dev});
  641. }
  642. }
  643. $updtReq = 1;
  644. }
  645. elsif($attrName eq "param"){
  646. my $md = CUL_HM_Get($hash,$name,"param","model");
  647. my $st = CUL_HM_Get($hash,$name,"param","subType");
  648. my $chn = substr(CUL_HM_hash2Id($hash),6,2);
  649. if ($md eq "HM-Sen-RD-O" && $chn eq "02"){
  650. delete $hash->{helper}{param};
  651. my @param = split ",",$attrVal;
  652. foreach (@param){
  653. if ($_ eq "offAtPon"){$hash->{helper}{param}{offAtPon} = 1}
  654. elsif ($_ eq "onAtRain"){$hash->{helper}{param}{onAtRain} = 1}
  655. else {return "param $_ unknown, use offAtPon or onAtRain";}
  656. }
  657. }
  658. elsif ($md eq "HM-Dis-EP-WM55" && $chn eq "03"){#reWriteDisplay
  659. if ($cmd eq "set"){
  660. if ($attrVal =~ m/^reWriteDisplay([0-9][0-9])$/){# no action, just set
  661. my $delay = $1;
  662. if($delay < 1 || $delay >99){
  663. return "invalid $delay- select between reWriteDisplay01 and reWriteDisplay99";
  664. }
  665. }
  666. else{
  667. return "attribut param $attrVal not valid for $name. Only reWriteDisplayxx allowed";
  668. }
  669. }
  670. else{
  671. delete $hash->{helper}{vd}{msgRed};
  672. }
  673. }
  674. elsif ($st eq "virtual"){
  675. if ($cmd eq "set"){
  676. if ($attrVal eq "noOnOff"){# no action
  677. }
  678. elsif ($attrVal =~ m/msgReduce/){# send only each other message
  679. my (undef,$rCnt) = split(":",$attrVal,2);
  680. $rCnt=(defined $rCnt && $rCnt =~ m/^\d$/)?$rCnt:1;
  681. $hash->{helper}{vd}{msgRed}=$rCnt;
  682. }
  683. else{
  684. return "attribut param $attrVal not valid for $name";
  685. }
  686. }
  687. else{
  688. delete $hash->{helper}{vd}{msgRed};
  689. }
  690. }
  691. else{
  692. if ($cmd eq "set"){
  693. if ($attrVal =~ m/(levelInverse|ponRestore)/){# no action
  694. }
  695. elsif ($attrVal =~ m/(showTimed)/){# no action
  696. #we could check for those subtypes
  697. #sensRain
  698. #siren
  699. #powerMeter
  700. #switch
  701. #dimmer
  702. #rgb
  703. }
  704. else{
  705. return "attribut param $attrVal not valid for $name";
  706. }
  707. }
  708. else{
  709. delete $hash->{helper}{vd}{msgRed};
  710. }
  711. }
  712. }
  713. elsif($attrName eq "peerIDs"){
  714. if ($cmd eq "set"){
  715. return "$attrName not usable for devices" if(!$hash->{helper}{role}{chn});
  716. my $id = $hash->{DEF};
  717. if ($id ne $K_actDetID && $attrVal){# if not action detector
  718. my @ids = grep /......../,split(",",$attrVal);
  719. $attr{$name}{peerIDs} = join",",@ids if (@ids);
  720. CUL_HM_ID2PeerList($name,"",1); # update peerList out of peerIDs
  721. }
  722. }
  723. else{# delete
  724. delete $hash->{peerList};
  725. delete $hash->{READINGS}{peerList};
  726. }
  727. }
  728. elsif($attrName eq "msgRepeat"){
  729. if ($cmd eq "set"){
  730. return "$attrName not usable for channels" if(!$hash->{helper}{role}{dev});#only for device
  731. return "value $attrVal ignored, must be an integer" if ($attrVal !~ m/^(\d+)$/);
  732. }
  733. return;
  734. }
  735. elsif($attrName eq "model" && $hash->{helper}{role}{dev}){
  736. delete $hash->{helper}{rxType}; # needs new calculation
  737. delete $hash->{helper}{mId};
  738. if ($attrVal eq "CCU-FHEM"){
  739. $attr{$name}{subType} = "virtual";
  740. $updtReq = 1;
  741. CUL_HM_UpdtCentral($name);
  742. }
  743. else{
  744. CUL_HM_hmInitMsg($hash);# will update mId, rxType and others
  745. }
  746. $attr{$name}{$attrName} = $attrVal if ($cmd eq "set");
  747. }
  748. elsif($attrName eq "subType"){
  749. $updtReq = 1;
  750. }
  751. elsif($attrName eq "aesCommReq" ){
  752. if ($cmd eq "set"){
  753. return "$attrName support 0 or 1 only" if ($attrVal !~ m/[01]/);
  754. return "$attrName invalid for virtal devices" if ($hash->{role}{vrt});
  755. $attr{$name}{$attrName} = $attrVal;
  756. # if ( $attrVal eq "1"
  757. # && $hash->{device}) { # is a channel
  758. # $attr{$hash->{device}}{$attrName} = $attrVal; # automatically enable on device, too - does not make sense
  759. # }
  760. }
  761. else{
  762. delete $attr{$name}{$attrName};
  763. }
  764. CUL_HM_hmInitMsg(CUL_HM_getDeviceHash($hash));
  765. }
  766. elsif($attrName eq "aesKey" ){
  767. if ($cmd eq "set"){
  768. return "$attrName support 0 to 5 only" if ($attrVal < 0 || $attrVal > 5);
  769. $attr{$name}{$attrName} = $attrVal;
  770. }
  771. else{
  772. delete $attr{$name}{$attrName};
  773. }
  774. CUL_HM_hmInitMsg(CUL_HM_getDeviceHash($hash));
  775. }
  776. elsif($attrName eq "burstAccess"){
  777. if ($cmd eq "set"){
  778. return "use burstAccess only for device" if (!$hash->{helper}{role}{dev});
  779. return $name." not a conditional burst model" if (!CUL_HM_getRxType($hash) & 0x80);
  780. return "$attrVal not a valid option for burstAccess" if ($attrVal !~ m/^[01]/);
  781. if ($attrVal =~ m/^0/){$hash->{protCondBurst} = "forced_off";}
  782. else {$hash->{protCondBurst} = "unknown";}
  783. }
  784. else{ $hash->{protCondBurst} = "forced_off";}
  785. delete $hash->{helper}{rxType}; # needs new calculation
  786. }
  787. elsif($attrName eq "IOList"){
  788. return "use $attrName only for vccu device"
  789. if (!$hash->{helper}{role}{dev}
  790. || AttrVal($name,"model","CCU-FHEM") !~ "CCU-FHEM");
  791. if($cmd eq "set"){$attr{$name}{$attrName} = $attrVal;}
  792. else {delete $attr{$name}{$attrName};}
  793. CUL_HM_UpdtCentral($name);
  794. }
  795. elsif($attrName eq "IOgrp" ){
  796. if ($cmd eq "set"){
  797. return "use $attrName only for devices" if (!$hash->{helper}{role}{dev});
  798. my ($ioCCU,$prefIO) = split(":",$attrVal,2);
  799. $hash->{helper}{io}{vccu} = $ioCCU;
  800. $hash->{helper}{io}{prefIO} = undef;
  801. if ($prefIO){
  802. my @prefIOA;
  803. if ($init_done){@prefIOA = grep /.+/,map{$defs{$_} ? $_ : ""} split(",",$prefIO);}
  804. else {@prefIOA = split(",",$prefIO);}#checkis possible after init. Assume correct if not finished
  805. $hash->{helper}{io}{prefIO} = \@prefIOA if (int(@prefIOA));
  806. my $attrValAssamble = "$ioCCU:".join(",",@prefIOA);
  807. if ($attrVal ne $attrValAssamble){# original setting not possible
  808. $attr{$name}{$attrName} = $attrValAssamble;
  809. return "value corrected $attrName:$attrValAssamble";
  810. }
  811. }
  812. }
  813. else{
  814. $hash->{helper}{io}{vccu} = "";
  815. $hash->{helper}{io}{prefIO} = undef;
  816. }
  817. }
  818. elsif($attrName eq "autoReadReg"){
  819. if ($cmd eq "set"){
  820. CUL_HM_complConfigTest($name)
  821. if (!CUL_HM_getAttrInt($name,"ignore"));;
  822. }
  823. }
  824. elsif($attrName eq "rssiLog" ){
  825. if ($cmd eq "set"){
  826. return "use $attrName only for device" if (!$hash->{helper}{role}{dev});
  827. }
  828. }
  829. elsif($attrName eq "levelRange" ){
  830. if ($cmd eq "set"){
  831. return "use $attrName only for dimmer" if ((CUL_HM_Get($defs{$name},$name,"param","subType") ne "dimmer")
  832. && $init_done );
  833. my ($min,$max) = split (",",$attrVal);
  834. return "use format min,max" if (!defined $max);
  835. return "min:$min must be between 0 and 100" if ($min<0 || $min >100);
  836. return "max:$max must be between 0 and 100" if ($max<0 || $max >100);
  837. return "min:$min mit be lower then max:$max" if ($min >= $max);
  838. }
  839. }
  840. elsif($attrName eq "levelMap" ){
  841. if ($cmd eq "set"){
  842. return "use $attrName only for channels" if (!$hash->{helper}{role}{chn});
  843. delete $hash->{helper}{lm};
  844. foreach (split":",$attrVal){
  845. my ($val,$vNm) = split"=",$_;
  846. if ($val !~ m/^\d*$/){
  847. delete $hash->{helper}{lm};
  848. return "$val is not numeric";
  849. }
  850. $hash->{helper}{lm}{$val} = $vNm;
  851. }
  852. }
  853. else{
  854. delete $hash->{helper}{lm};
  855. }
  856. }
  857. elsif($attrName eq "actAutoTry" ){
  858. if ($cmd eq "set"){
  859. return "$attrName only usable for ActionDetector" if(CUL_HM_hash2Id($hash) ne "000000");#only for device
  860. }
  861. }
  862. elsif($attrName eq "tempListTmpl" ){
  863. if ($cmd eq "set"){
  864. CUL_HM_UpdtReadSingle($hash,"tempTmplSet",$attrVal,0)
  865. }
  866. else{
  867. delete $hash->{READINGS}{"tempTmplSet"};
  868. }
  869. }
  870. elsif($attrName =~ m/^hmKey/){
  871. my $retVal= "";
  872. return "use $attrName only for vccu device"
  873. if (!$hash->{helper}{role}{dev}
  874. || AttrVal($name,"model","CCU-FHEM") !~ "CCU-FHEM");
  875. if ($cmd eq "set"){
  876. # eQ3 default key A4E375C6B09FD185F27C4E96FC273AE4
  877. my $kno = ($attrName eq "hmKey")?1:substr($attrName,5,1);
  878. my ($no,$val) = (sprintf("%02X",$kno),$attrVal);
  879. if ($attrVal =~ m/:/){#number given
  880. ($no,$val) = split ":",$attrVal;
  881. return "illegal number:$no" if (hex($no) < 1 || hex($no) > 255 || length($no) != 2);
  882. }
  883. $attr{$name}{$attrName} = "$no:".
  884. (($val =~ m/^[0-9A-Fa-f]{32}$/ )
  885. ? $val
  886. : unpack('H*', md5($val)));
  887. $retVal = "$attrName set to $attr{$name}{$attrName}"
  888. if($attrVal ne $attr{$name}{$attrName});
  889. }
  890. else{
  891. delete $attr{$name}{$attrName};
  892. }
  893. if ($init_done){
  894. foreach my $io (split ",",AttrVal($name,"IOList","")) {
  895. next if(!$defs{$io});
  896. if ($defs{$io}->{TYPE} eq "HMLAN" && eval "defined(&HMLAN_writeAesKey)"){
  897. HMLAN_writeAesKey($io);
  898. }
  899. elsif ($defs{$io}->{TYPE} eq "HMUARTLGW") {
  900. CallFn($io,"WriteFn",$defs{$io},undef,"writeAesKey:${io}");
  901. }
  902. elsif ( $defs{$io}->{TYPE} =~ m/^(TSCUL|TSSTACKED)$/
  903. && eval "defined(&TSCUL_WriteAesKeyHM)"){
  904. TSCUL_WriteAesKeyHM($io); # noansi: for TSCUL
  905. }
  906. }
  907. }
  908. return $retVal;
  909. }
  910. CUL_HM_queueUpdtCfg($name) if ($updtReq);
  911. return;
  912. }
  913. sub CUL_HM_AttrCheck(@) {############################
  914. #verify if attr is applicable
  915. my ($name, $attrName) = @_;
  916. return undef if (!$init_done); # we cannot determine if attributes are missing
  917. if ($defs{$name}{helper}{role}{vrt}){
  918. return " $attrName illegal for virtual devices"
  919. if ($modules{CUL_HM}{Attr}{devPhy} =~ m/\b$attrName\b/);
  920. }
  921. if (!$defs{$name}{helper}{role}{chn}){
  922. return " $attrName only valid for channels"
  923. if ($modules{CUL_HM}{Attr}{chn} =~ m/\b$attrName\b/);
  924. }
  925. if (!$defs{$name}{helper}{role}{dev}){
  926. return " $attrName only valid for devices"
  927. if (($modules{CUL_HM}{Attr}{dev}.$modules{CUL_HM}{Attr}{devPhy}) =~ m/\b$attrName\b/);
  928. }
  929. return undef;
  930. }
  931. sub CUL_HM_prtInit($){ #setup protocol variables after define
  932. my ($hash)=@_;
  933. $hash->{helper}{prt}{sProc} = 0; # stack not being processed by now
  934. $hash->{helper}{prt}{bErr} = 0;
  935. }
  936. sub CUL_HM_hmInitMsg($){ #define device init msg for HMLAN
  937. #message to be send to HMLAN/USB to define device communication defails
  938. #bit-usage is widely unknown.
  939. #p[1]: 00000001 = request AES
  940. #p[1]: 00000010 = data pending - autosend wakeup and lazyConfig
  941. # if device send data
  942. #p[2]: is this the number of the AES key to be used?
  943. return if (!$init_done);
  944. my ($hash)=@_;
  945. my $rxt = CUL_HM_getRxType($hash);
  946. my $id = CUL_HM_hash2Id($hash);
  947. my @p;
  948. my $name = $hash->{NAME};
  949. my $mask = ($hash->{helper}{role}{chn} && AttrVal($name,"aesCommReq",0))?2:0;
  950. foreach (grep /channel/,keys %{$hash}){
  951. $mask |= (2 ** hex(substr($_,8,2))) if(AttrVal($hash->{$_},"aesCommReq",0));
  952. }
  953. if ($mask<256) {$mask = join("",reverse unpack "(A2)*",sprintf("%02X",$mask));}
  954. elsif ($mask<65536) {$mask = join("",reverse unpack "(A2)*",sprintf("%04X",$mask));}
  955. else {$mask = join("",reverse unpack "(A2)*",sprintf("%08X",$mask));}
  956. my ($highestKey, undef) = CUL_HM_getKeys($hash);
  957. my $key = sprintf("%02X",AttrVal($name,"aesKey",$highestKey));
  958. @p = ("$id","00",$key,$mask) if (!$hash->{helper}{role}{vrt});
  959. if (AttrVal($name,"aesCommReq",0)){
  960. $p[1] = sprintf("%02X",(hex($p[1]) + 1));
  961. $p[3] = ($p[3]eq "")?"1E":$p[3];
  962. }
  963. $hash->{helper}{io}{newChn} = "";
  964. $hash->{helper}{io}{rxt} = (($rxt & 0x18) #wakeup || #lazyConfig
  965. && AttrVal($name,"model",0) ne "HM-WDS100-C6-O") #Todo - not completely clear how it works
  966. ?2:0;
  967. $hash->{helper}{io}{p} = \@p;
  968. CUL_HM_hmInitMsgUpdt($hash);
  969. }
  970. sub CUL_HM_hmInitMsgUpdt($){ #update device init msg for HMLAN
  971. my ($hash)=@_;
  972. return if ( $hash->{helper}{role}{vrt}
  973. ||!defined $hash->{helper}{io}{p});
  974. my $oldChn = $hash->{helper}{io}{newChn};
  975. my @p = @{$hash->{helper}{io}{p}};
  976. # General todo
  977. # $p[1] |= 2; need to be set if data is pending for a wakeup device.
  978. # it will force HMLAN to send A112 (have data). HMLAN will return
  979. # status "81" ACK if the device answers the A112 - FHEM should start sending Data by then
  980. #
  981. if($hash->{helper}{prt}{sProc} && $hash->{cmdStack}){
  982. $p[1] = sprintf("%02X",hex($p[1]) | $hash->{helper}{io}{rxt});
  983. }
  984. # else{
  985. # $p[1] = sprintf("%02X",hex($p[1]) & 0xFD);# remove this Bit if no more data to send
  986. # # otherwise could cause continous send (e.g. from SC)
  987. # }
  988. $hash->{helper}{io}{newChn} = '+'.join(",",@p);
  989. if (( $hash->{helper}{io}{newChn} ne $oldChn)
  990. && $hash->{IODev}
  991. && $hash->{IODev}->{TYPE}
  992. && ( $hash->{IODev}->{helper}{VTS_AES} # for TSCUL VTS0.14 up
  993. || $hash->{IODev}->{TYPE} =~ m/^(HMLAN|HMUARTLGW)$/ )) {
  994. IOWrite($hash, "", "init:$p[0]");
  995. }
  996. }
  997. sub CUL_HM_Notify(@){#################################
  998. my ($ntfy, $dev) = @_;
  999. return "" if ($dev->{NAME} ne "global");
  1000. my $events = deviceEvents($dev, AttrVal($ntfy->{NAME}, "addStateEvent", 0));
  1001. return undef if(!$events); # Some previous notify deleted the array.
  1002. return undef if (grep !/INITIALIZED/,@{$events});
  1003. delete $modules{CUL_HM}{NotifyFn};
  1004. # execute some cleanup after init
  1005. CUL_HM_updateConfig("startUp");
  1006. InternalTimer(1,"CUL_HM_setupHMLAN", "initHMLAN", 0);#start asap once FHEM is operational
  1007. return undef;
  1008. }
  1009. sub CUL_HM_setupHMLAN(@){#################################
  1010. foreach (devspec2array("TYPE=CUL_HM:FILTER=DEF=......:FILTER=subType!=virtual")){
  1011. $defs{$_}{helper}{io}{newChn} = 0;
  1012. CUL_HM_hmInitMsg($defs{$_}); #update device init msg for HMLAN
  1013. }
  1014. }
  1015. #+++++++++++++++++ msg receive, parsing++++++++++++++++++++++++++++++++++++++++
  1016. # translate level to readable
  1017. my %lvlStr = ( md =>{ "HM-SEC-WDS" =>{"00"=>"dry" ,"64"=>"damp" ,"C8"=>"wet" }
  1018. ,"HM-SEC-WDS-2" =>{"00"=>"dry" ,"64"=>"damp" ,"C8"=>"wet" }
  1019. ,"HM-CC-SCD" =>{"00"=>"normal" ,"64"=>"added" ,"C8"=>"addedStrong"}
  1020. ,"HM-Sen-RD-O" =>{"00"=>"dry" ,"C8"=>"rain"}
  1021. ,"HM-MOD-Em-8" =>{"00"=>"closed" ,"C8"=>"open"}
  1022. ,"HM-WDS100-C6-O" =>{"00"=>"quiet" ,"C8"=>"storm"}
  1023. }
  1024. ,mdCh=>{ "HM-Sen-RD-O01" =>{"00"=>"dry" ,"C8"=>"rain"}
  1025. ,"HM-Sen-RD-O02" =>{"00"=>"off" ,"C8"=>"on"}
  1026. }
  1027. ,st =>{ "smokeDetector" =>{"01"=>"no alarm","C7"=>"tone off","C8"=>"Smoke Alarm"}
  1028. ,"threeStateSensor"=>{"00"=>"closed" ,"64"=>"tilted" ,"C8"=>"open"}
  1029. }
  1030. );
  1031. my %disColor=(white=>0,red=>1,orange=>2,yellow=>3,green=>4,blue=>5);
  1032. my %disIcon=( off =>0, on=>1, open=>2, closed=>3, error=>4, ok=>5
  1033. ,info=>6, newMsg=>7, serviceMsg=>8
  1034. ,sigGreen=>9, sigYellow=>10, sigRed=>11
  1035. ,ic12=>12, ic13=>13
  1036. ,noIcon=>99
  1037. );
  1038. my %disBtn=( txt01_1=>0, txt01_2=>1, txt02_1=>2, txt02_2=>3, txt03_1=>4
  1039. , txt03_2=>5, txt04_1=>6, txt04_2=>7, txt05_1=>8, txt05_2=>9
  1040. , txt06_1=>10,txt06_2=>11,txt07_1=>12,txt07_2=>13,txt08_1=>14
  1041. , txt08_2=>15,txt09_1=>16,txt09_2=>17,txt10_1=>18,txt10_2=>19
  1042. );
  1043. sub CUL_HM_Parse($$) {#########################################################
  1044. my ($iohash, $msgIn) = @_;
  1045. my %mh; # hash for data of this message
  1046. ($mh{msg},$mh{msgStat},$mh{myRSSI},$mh{msgIO},$mh{auth}) = split(":",$msgIn,5);
  1047. ($mh{t},$mh{len},$mh{mNo},$mh{mFlg},$mh{mTp},$mh{src},$mh{dst},$mh{p}) = unpack 'A1A2A2A2A2A6A6A*',$mh{msg};
  1048. $mh{mFlgH} = hex($mh{mFlg});
  1049. # Msg format: Allnnffttssssssddddddpp...
  1050. return if (!$iohash ||
  1051. ref($iohash) ne 'HASH' ||
  1052. $mh{t} ne 'A' ||
  1053. length($mh{msg})<20);
  1054. if ($modules{CUL_HM}{helper}{updating}){
  1055. if ("done" eq CUL_HM_FWupdateSteps($mh{msg})){
  1056. my $sH = CUL_HM_id2Hash($mh{t});
  1057. my @e = CUL_HM_pushEvnts();
  1058. $defs{$_}{".noDispatchVars"} = 1 foreach (grep !/^$sH->{NAME}$/,@e);
  1059. return (@e,$sH->{NAME}); #return something to please dispatcher
  1060. }
  1061. else{
  1062. return "";
  1063. }
  1064. }
  1065. return "" if($mh{msgStat} && $mh{msgStat} eq 'NACK');# lowlevel error
  1066. $mh{rectm} = gettimeofday(); # take reception time
  1067. $mh{tmStr} = FmtDateTime($mh{rectm});
  1068. $mh{p} = "" if(!defined($mh{p})); # generate some abreviations
  1069. my @mI = unpack '(A2)*',$mh{p}; # split message info to bytes
  1070. $mh{mStp} = $mI[0] ? $mI[0] : ""; #message subtype
  1071. $mh{mTyp} = $mh{mTp}.$mh{mStp}; #message type/subtype
  1072. # $shash will be replaced for multichannel commands
  1073. $mh{devH} = CUL_HM_id2Hash($mh{src}); #sourcehash - will be modified to channel entity
  1074. $mh{dstH} = CUL_HM_id2Hash($mh{dst}); # destination device hash
  1075. $mh{id} = CUL_HM_h2IoId($iohash);
  1076. $mh{ioName} = $iohash->{NAME};
  1077. $evtDly = 1;# switch delay trigger on
  1078. CUL_HM_statCnt($mh{ioName},"r",$mh{mFlgH});
  1079. $mh{dstN} = ($mh{dst} eq "000000") ? "broadcast" :
  1080. ($mh{dstH} ? $mh{dstH}->{NAME} :
  1081. ($mh{dst} eq $mh{id} ? $mh{ioName} :
  1082. $mh{dst}));
  1083. if(!$mh{devH} && $mh{mTp} eq "00") { # generate device
  1084. my $sname = "HM_$mh{src}";
  1085. Log3 undef, 2, "CUL_HM Unknown device $sname is now defined";
  1086. DoTrigger("global","UNDEFINED $sname CUL_HM $mh{src}");
  1087. $mh{devH} = CUL_HM_id2Hash($mh{src}); #sourcehash - changed to channel entity
  1088. $mh{devH}->{IODev} = $iohash;
  1089. $mh{devH}->{helper}{io}{nextSend} = $mh{rectm}+0.09 if(!defined($mh{devH}->{helper}{io}{nextSend}));# io couldn't set
  1090. }
  1091. my @entities = ("global"); #additional entities with events to be notifies
  1092. #################### attack alarm detection#####################
  1093. if ( $mh{dstH} && $mh{dst} ne "000000"
  1094. && !CUL_HM_getAttrInt($mh{dstN},"ignore")
  1095. && ($mh{mTp} eq '01' || $mh{mTp} eq '11'
  1096. )){
  1097. my $ioId = AttrVal($mh{dstH}->{IODev}{NAME},"hmId","-");
  1098. if($ioId ne $mh{src}){
  1099. if ( !defined $mh{dstH}->{"prot"."ErrIoId_$mh{src}"}
  1100. && ReadingsVal($mh{dstN},"sabotageAttackId_ErrIoId_$mh{src}:",undef)){
  1101. (undef,$mh{dstH}->{"prot"."ErrIoId_$mh{src}"}) =
  1102. split(":",ReadingsVal($mh{dstN},"sabotageAttackId_ErrIoId_$mh{src}:",undef));
  1103. }
  1104. CUL_HM_eventP($mh{dstH},"ErrIoId_$mh{src}");
  1105. my ($evntCnt,undef) = split(' last_at:',$mh{dstH}->{"prot"."ErrIoId_$mh{src}"},2);
  1106. push @evtEt,[$mh{dstH},1,"sabotageAttackId_ErrIoId_$mh{src}: cnt:$evntCnt"];
  1107. }
  1108. my $tm = substr($mh{msg},8);
  1109. if( defined $mh{dstH}->{helper}{cSnd} &&
  1110. $mh{dstH}->{helper}{cSnd} !~ m/$tm/){
  1111. if ( !defined $mh{dstH}->{"prot"."ErrIoAttack"}
  1112. && ReadingsVal($mh{dstN},"sabotageAttack_ErrIoAttack cnt:",undef)){
  1113. $mh{dstH}->{"prot"."ErrIoAttack"} =
  1114. ReadingsVal($mh{dstN},"sabotageAttack_ErrIoAttack cnt:",undef);
  1115. }
  1116. Log3 $mh{dstN},2,"CUL_HM $mh{dstN} attack:$mh{dstH}->{helper}{cSnd}:".$tm;
  1117. CUL_HM_eventP($mh{dstH},"ErrIoAttack");
  1118. my ($evntCnt,undef) = split(' last_at:',$mh{dstH}->{"prot"."ErrIoAttack"},2);
  1119. push @evtEt,[$mh{dstH},1,"sabotageAttack_ErrIoAttack cnt:$evntCnt"];
  1120. }
  1121. }
  1122. ###########
  1123. # return "" if($mh{src} eq $mh{id});# mirrored messages - covered by !$shash
  1124. if(!$mh{devH}){ # Unknown source
  1125. $evtDly = 0;# switch delay trigger off
  1126. return "" if ($mh{msg} =~ m/998112......000001/);# HMLAN internal message, consum
  1127. my $ccu =InternalVal($mh{ioName},"owner_CCU","");
  1128. CUL_HM_DumpProtocol("RCV",$iohash,$mh{len},$mh{mNo},$mh{mFlg},$mh{mTp},$mh{src},$mh{dst},$mh{p});
  1129. if ($defs{$ccu}){#
  1130. push @evtEt,[$defs{$ccu},0,"unknown_$mh{src}:received"];# do not trigger
  1131. return CUL_HM_pushEvnts();
  1132. }
  1133. return;
  1134. }
  1135. $mh{devN} = $mh{devH}->{NAME}; # source device name
  1136. if (CUL_HM_getAttrInt($mh{devN},"ignore")){
  1137. $defs{$_}{".noDispatchVars"} = 1 foreach (grep !/^$mh{devN}$/,@entities);
  1138. return (CUL_HM_pushEvnts(),$mh{devN},@entities);
  1139. }
  1140. my $IOchanged = 0; # track a change of IO dev to ensure aesCommReq validation
  1141. if ( !defined $mh{devH}->{IODev}
  1142. || !defined $mh{devH}->{IODev}{NAME}){
  1143. $IOchanged += CUL_HM_assignIO($mh{devH}); # this way the init and remove work even on startup for TSCUL.
  1144. if ( !defined $mh{devH}->{IODev}
  1145. || !defined $mh{devH}->{IODev}{NAME}){
  1146. Log3 $mh{devH},1,"CUL_HM $mh{src} error: no IO deviced!!! correct it";
  1147. $mh{devH}->{IODev} = $iohash;
  1148. $IOchanged = 1;
  1149. }
  1150. }
  1151. $respRemoved = 0; #set to 'no response in this message' at start
  1152. $mh{shash} = $mh{devH}; # source hash - will be redirected to channel if applicable
  1153. my $ioId = CUL_HM_h2IoId($mh{devH}->{IODev});
  1154. $ioId = $mh{id} if(!$ioId);
  1155. CUL_HM_storeRssi($mh{devN}
  1156. ,"at_".(($mh{mFlgH}&0x40)?"rpt_":"").$mh{ioName} # repeater?
  1157. ,$mh{myRSSI}
  1158. ,$mh{mNo});
  1159. #----------CUL aesCommReq handling---------
  1160. my $oldIo = $mh{devH}{IODev}->{NAME};
  1161. my $aComReq = AttrVal($mh{devN},"aesCommReq",0); #aesCommReq enabled for device
  1162. my $dIoOk = ($mh{devH}{IODev}{NAME} eq $mh{ioName}) ? 1 : 0;
  1163. my $aIoAESCap = ( $mh{devH}{IODev}->{helper}{VTS_AES}
  1164. || AttrVal($mh{devH}{IODev}->{NAME},"rfmode","") ne "HomeMatic" ) ? 1 : 0; # assigned IO AES cappable
  1165. $mh{devH}->{helper}{aesAuthBytes} = $mh{auth} if($mh{auth}); # let CUL_HM ACK with authbytes. tsculfw does default ACK automatically only. A default ACK may just update a default ACK in tsculfw buffer
  1166. if ( $aComReq #aesCommReq enabled for device
  1167. && (!$dIoOk || $IOchanged) #message not received on assigned IO or change in IO
  1168. && ($mh{msgStat} !~ m/^AES/) ) { #receiving IO did not already do AES processing for us
  1169. my $oldIoAESCap = $aIoAESCap;
  1170. $IOchanged += CUL_HM_assignIO($mh{devH}); #update IO in case of roaming
  1171. $aIoAESCap = ( $mh{devH}{IODev}->{helper}{VTS_AES}
  1172. || AttrVal($mh{devH}{IODev}->{NAME},"rfmode","") ne "HomeMatic" ) ? 1 : 0; # newly assigned IO AES cappable
  1173. $dIoOk = ($mh{devH}{IODev}->{NAME} eq $mh{ioName}) ? 1 : 0; # newly assigned IO received message
  1174. if ( !$dIoOk #message not received on assigned new IO
  1175. || $IOchanged #IO changed, so AES state is unkown
  1176. || $oldIoAESCap #old IO is AES cappable (not standard CUL) and should have handled it, as it was set to do so
  1177. || $aIoAESCap ) { #new IO is AES cappable (not standard CUL), but did not handle it as it was not set to do so
  1178. Log3 $mh{devH},5,"CUL_HM ignoring message for ${oldIo} received on $mh{ioName}";
  1179. #Do not process message further, the assigned IO has to handle it
  1180. $defs{$_}{".noDispatchVars"} = 1 foreach (grep !/^$mh{devN}$/,@entities);
  1181. return (CUL_HM_pushEvnts(),$mh{devN});
  1182. }
  1183. }
  1184. #----------CUL aesCommReq handling---------
  1185. if ( !$aIoAESCap #IO is not aesCommReq cappable (standard CUL)
  1186. && $aComReq #aesCommReq enabled for device
  1187. && $dIoOk #message received on assigned IO
  1188. && $cryptFunc == 1
  1189. && $ioId eq $mh{dst}) {
  1190. if ($mh{devH}->{helper}{aesCommRq}{msgStat}) {
  1191. #----------Message was already handled, pass on result---------
  1192. $mh{msgStat} = $mh{devH}->{helper}{aesCommRq}{msgStat};
  1193. delete($mh{devH}->{helper}{aesCommRq});
  1194. }
  1195. elsif ( $mh{devH}->{helper}{aesCommRq}{msg}
  1196. && $mh{mTp} eq "03") {
  1197. #----------Check AES response from device (CUL)---------
  1198. my $aesM = $mh{devH}->{helper}{aesCommRq}{msg};
  1199. my (undef, %keys) = CUL_HM_getKeys($mh{devH});
  1200. my $key = $keys{$mh{devH}->{helper}{aesCommRq}{kNo}};
  1201. $key = $key ^ pack("H12", $mh{devH}->{helper}{aesCommRq}{challenge});
  1202. my $cipher = Crypt::Rijndael->new($key, Crypt::Rijndael::MODE_ECB());
  1203. my $iv = pack("H*", substr($aesM, 23));
  1204. my $response = $cipher->decrypt(pack("H32", $mh{p})) ^ $iv;
  1205. my $authbytes = unpack("H8", $response);
  1206. $response = $cipher->decrypt(substr($response, 0, 16));
  1207. my $cmd = uc unpack("H20",substr($response, 6, 1) .
  1208. chr(ord(substr($response, 7, 1)) & 0xbf) . #~RPTED
  1209. substr($response, 8, 8));
  1210. my $origcmd = uc substr($aesM, 3, 2) .
  1211. sprintf("%02X", (hex(substr($aesM, 5, 2)) & 0xbf)) . #~RPTED
  1212. substr($aesM, 7, 16);
  1213. Log3 $mh{devH},5,"CUL_HM $mh{dstN} iv: ".unpack("H*", $iv)
  1214. ."\n decrypted cmd: $cmd"
  1215. ."\n original cmd: $origcmd";
  1216. if ($cmd eq $origcmd) {
  1217. Log3 $mh{devH},4,"CUL_HM $mh{dstN} signature: good, authbytes: ${authbytes}";
  1218. $mh{devH}->{helper}{aesAuthBytes} = $authbytes;
  1219. $mh{devH}->{helper}{aesCommRq}{msgStat} = "AESCom-ok";
  1220. }
  1221. else {
  1222. Log3 $mh{devH},4,"CUL_HM $mh{dstN} signature: bad";
  1223. $mh{devH}->{helper}{aesCommRq}{msgStat} = "AESCom-fail";
  1224. }
  1225. #continue with old message
  1226. return CUL_HM_Parse($iohash, $mh{devH}->{helper}{aesCommRq}{msgIn});
  1227. }
  1228. else {
  1229. my $doAES = 1;
  1230. my $chn;
  1231. if($mh{mTp} =~ m/^4[01]/){ #someone is triggered##########
  1232. $chn = $mI[0];
  1233. }
  1234. elsif ($mh{mTp} eq "10") {
  1235. if ($mh{mStp} =~ m/^0[46]/) {
  1236. $chn = $mI[1];
  1237. }
  1238. elsif ($mh{mStp} eq "05") {
  1239. if ($mI[7] ne "00") { #m:1E A010 4CF663 1743BF 0500(00000000)(07)(00) # 00 is finish packet
  1240. $chn = $mI[1];
  1241. }
  1242. else {
  1243. $doAES = 0;
  1244. }
  1245. }
  1246. elsif ( $mh{mStp} eq "01"
  1247. ||!($mh{mFlgH} & 0x20)) { #response required Flag
  1248. $doAES = 0;
  1249. }
  1250. }
  1251. elsif ($mh{mTp} =~ m/^0[23]/) {
  1252. $doAES = 0;
  1253. }
  1254. if ($doAES && defined $chn && defined(CUL_HM_id2Hash($mh{src}.sprintf("%02X",$chn)))) {
  1255. CUL_HM_m_setCh(\%mh,$chn);
  1256. }
  1257. if ( $doAES
  1258. && AttrVal($mh{cName},"aesCommReq",0)) { #aesCommReq enabled for channel
  1259. #----------Generate AES challenge for device (CUL)---------
  1260. my ($kNo, %keys) = CUL_HM_getKeys($mh{devH});
  1261. $kNo = AttrVal($mh{devN},"aesKey",$kNo);
  1262. if (defined($keys{$kNo})) {
  1263. my $challenge = (defined($mh{devH}->{helper}{aesCommRq}{challenge}))
  1264. ? $mh{devH}->{helper}{aesCommRq}{challenge}
  1265. : sprintf("%08X%04X",rand(0xffffffff), rand(0xffff));
  1266. Log3 $mh{cHash},5,"CUL_HM $mh{devN} requesting signature with challenge $challenge for key $kNo";
  1267. $mh{devH}->{helper}{aesCommRq}{msg} = $mh{msg};
  1268. $mh{devH}->{helper}{aesCommRq}{msgIn} = $msgIn;
  1269. $mh{devH}->{helper}{aesCommRq}{challenge} = $challenge;
  1270. $mh{devH}->{helper}{aesCommRq}{kNo} = $kNo;
  1271. my $cmd = "$mh{mNo}A002$mh{dst}$mh{src}04${challenge}".sprintf("%02X", $kNo*2);
  1272. $cmd = sprintf("As%02X%s", length($cmd)/2, $cmd);
  1273. IOWrite($mh{devH}, "", $cmd);
  1274. $mh{msgStat}="AESpending";
  1275. }
  1276. else {
  1277. delete($mh{devH}->{helper}{aesCommRq}); # cleanup CUL aesCommReq -> we can check it in CUL_HM_assignIO not to change IO while CUL aesCommReq in progress
  1278. Log3 $mh{cHash},1,"CUL_HM $mh{devN} required key $mh{kNo} not defined in VCCU!";
  1279. }
  1280. }
  1281. else {
  1282. delete($mh{devH}->{helper}{aesCommRq});
  1283. }
  1284. }
  1285. }
  1286. if ($mh{msgStat}){
  1287. if ($mh{msgStat} =~ m/^AESKey/){
  1288. push @evtEt,[$mh{devH},1,"aesKeyNbr:".substr($mh{msgStat},7)];
  1289. $mh{msgStat} = ""; # already processed
  1290. }
  1291. elsif($mh{msgStat} =~ m/^AESpending/){# AES communication pending
  1292. push @evtEt,[$mh{devH},1,"aesCommToDev:pending"];
  1293. if ($mh{mTyp} eq "0204") {
  1294. my $aesKeyNbr = substr($mh{p},14,2);
  1295. push @evtEt,[$mh{devH},1,"aesKeyNbr:".$aesKeyNbr] if (defined $aesKeyNbr);
  1296. }
  1297. #Do not process message further, as it may be faked
  1298. $defs{$_}{".noDispatchVars"} = 1 foreach (grep !/^$mh{devN}$/,@entities);
  1299. return (CUL_HM_pushEvnts(),$mh{devN});
  1300. }
  1301. elsif($mh{msgStat} =~ m/^AESCom/){# AES communication to central
  1302. my $aesStat = substr($mh{msgStat},7);
  1303. push @evtEt,[$mh{devH},1,"aesCommToDev:".$aesStat];
  1304. ### General may need substential rework
  1305. # activate AES only for dedicated channels?
  1306. if($mh{mTp} =~ m/^4[01]/){ #someone is triggered##########
  1307. my $chn = hex($mI[0])& 0x3f;
  1308. my $cName = CUL_HM_id2Name($mh{src}.sprintf("%02X",$chn));
  1309. $cName = CUL_HM_id2Name($mh{src}) if (!defined($defs{$cName}));
  1310. my $bCnt = hex($mI[1]);
  1311. push @evtEt,[$defs{$cName},1,"trig_aes_$mh{dstN}:$aesStat:$bCnt"]
  1312. if (defined $defs{$cName});
  1313. if($aesStat eq "ok" #aes ok
  1314. && defined $mh{devH}->{cmdStacAESPend} #commands waiting
  1315. && $ioId eq $mh{dst}){ #aes from IO device
  1316. foreach (@{$mh{devH}->{cmdStacAESPend}}) {
  1317. my ($h,$c) = split(";",$_);
  1318. CUL_HM_PushCmdStack(CUL_HM_id2Hash($h),$c);
  1319. }
  1320. CUL_HM_ProcessCmdStack($mh{devH});
  1321. }
  1322. delete $mh{devH}->{cmdStacAESPend};
  1323. my @peers = grep !/00000000/,split(",",AttrVal($cName,"peerIDs",""));
  1324. foreach my $peer (grep /$mh{dst}/,@peers){
  1325. my $pName = CUL_HM_id2Name($peer);
  1326. $pName = CUL_HM_id2Name(substr($peer,0,6)) if (!$defs{$pName});
  1327. next if (!$defs{$pName});#||substr($peer,0,6) ne $mh{dst}
  1328. push @evtEt,[$defs{$pName},1,"trig_aes_$cName:$aesStat:$bCnt"];
  1329. }
  1330. }
  1331. if ($aesStat ne "ok") { #unauthenticated message, abort
  1332. $defs{$_}{".noDispatchVars"} = 1 foreach (grep !/^$mh{devH}->{NAME}$/,@entities);
  1333. return (CUL_HM_pushEvnts(),$mh{devN});
  1334. }
  1335. }
  1336. }
  1337. CUL_HM_eventP($mh{devH},"Evt_$mh{msgStat}")if ($mh{msgStat});#log io-events
  1338. my $target = " (to $mh{dstN})";
  1339. $mh{st} = AttrVal($mh{devN}, "subType", "");
  1340. $mh{md} = AttrVal($mh{devN}, "model" , "");
  1341. # +++++ check for duplicate or repeat ++++
  1342. my $msgX = "No:$mh{mNo} - t:$mh{mTp} s:$mh{src} d:$mh{dst} ".($mh{p}?$mh{p}:"");
  1343. if($mh{devH}->{lastMsg} && $mh{devH}->{lastMsg} eq $msgX) { #duplicate -lost 'ack'?
  1344. if( $mh{devH}->{helper}{rpt} #was responded
  1345. && $mh{devH}->{helper}{rpt}{IO} eq $mh{ioName} #from same IO
  1346. && $mh{devH}->{helper}{rpt}{flg} eq substr($mh{msg},5,1) #not from repeater
  1347. && $mh{devH}->{helper}{rpt}{ts} < $mh{rectm}-0.24 # again if older then 240ms (typ repeat time)
  1348. #todo: hack since HMLAN sends duplicate status messages
  1349. ){
  1350. my $ack = $mh{devH}->{helper}{rpt}{ack};#shorthand
  1351. my $i=0;
  1352. $mh{devH}->{helper}{rpt}{ts} = $mh{rectm};
  1353. CUL_HM_SndCmd(${$ack}[$i++],${$ack}[$i++]
  1354. .($mh{devH}->{helper}{aesAuthBytes}
  1355. ?$mh{devH}->{helper}{aesAuthBytes}
  1356. :"")
  1357. ) while ($i<@{$ack});
  1358. delete($mh{devH}->{helper}{aesAuthBytes});
  1359. Log3 $mh{devN},4,"CUL_HM $mh{devN} dupe: repeat ".scalar(@{$ack})." ack, dont process";
  1360. }
  1361. else{
  1362. Log3 $mh{devN},4,"CUL_HM $mh{devN} dupe: dont process";
  1363. }
  1364. CUL_HM_pushEvnts();
  1365. $defs{$_}{".noDispatchVars"} = 1 foreach (grep !/^$mh{devH}->{NAME}$/,@entities);
  1366. CUL_HM_sndIfOpen("x:$mh{ioName}");
  1367. return (CUL_HM_pushEvnts(),$mh{devN},@entities); #return something to please dispatcher
  1368. }
  1369. delete $mh{devH}->{helper}{rpt};# new message, rm recent ack
  1370. my @ack; # ack and responses, might be repeated
  1371. CUL_HM_eventP($mh{devH},"Rcv");
  1372. CUL_HM_eventP($mh{devH},"RcvB") if($mh{mFlgH} & 0x10);#burst msg received
  1373. CUL_HM_DumpProtocol("RCV",$iohash,$mh{len},$mh{mNo},$mh{mFlg},$mh{mTp},$mh{src},$mh{dst},$mh{p});
  1374. #----------start valid messages parsing ---------
  1375. my $parse = CUL_HM_parseCommon($iohash,\%mh);
  1376. $mh{devH}->{lastMsg} = $msgX;# is used in parseCommon and need previous setting. so set it here
  1377. push @evtEt,[$mh{devH},1,"powerOn:$mh{tmStr}"] if($parse eq "powerOn");
  1378. push @evtEt,[$mh{devH},1,""] if($parse eq "parsed"); # msg is parsed but may
  1379. # be processed further
  1380. if ($parse eq "ACK" ||
  1381. $parse eq "done" ){# remember - ACKinfo will be passed on
  1382. push @evtEt,[$mh{devH},1,""];
  1383. }
  1384. elsif($parse eq "NACK"){
  1385. push @evtEt,[$mh{shash},1,"state:NACK"];
  1386. }
  1387. elsif($parse eq "AES"){
  1388. return CUL_HM_pushEvnts();# exit now, don't send ACK
  1389. }
  1390. elsif($mh{mTp} eq "12") {#$lcm eq "09A112" Another fhem request (HAVE_DATA)
  1391. ;
  1392. }
  1393. elsif($mh{md} =~ m/^(KS550|KS888|HM-WDS100-C6-O)/) { ########################
  1394. if($mh{mTp} eq "70") {
  1395. my ($t,$h,$r,$w,$wd,$s,$b) = map{hex($_)} unpack 'A4A2A4A4(A2)*',$mh{p};
  1396. push @evtEt,[$mh{devH},1,"battery:". (($t & 0x8000)?"low" :"ok" )] if ($mh{md} eq "HM-WDS100-C6-O-2"); #has no battery
  1397. my $tsgn = ($t & 0x4000);
  1398. $t = ($t & 0x3fff)/10;
  1399. $t = sprintf("%0.1f", $t-1638.4) if($tsgn);
  1400. my $ir = ($r & 0x8000)?1:0;
  1401. $r = ($r & 0x7fff) * 0.295;
  1402. my $wdr = ($w>>14)*22.5;
  1403. $w = ($w & 0x3fff)/10;
  1404. $wd = $wd * 5;
  1405. my $sM = "state:";
  1406. if(defined $t) {$sM .= "T: $t " ;push @evtEt,[$mh{shash},1,"temperature:$t" ];}
  1407. if(defined $h) {$sM .= "H: $h " ;push @evtEt,[$mh{shash},1,"humidity:$h" ];}
  1408. if(defined $w) {$sM .= "W: $w " ;push @evtEt,[$mh{shash},1,"windSpeed:$w" ];}
  1409. if(defined $r) {$sM .= "R: $r " ;push @evtEt,[$mh{shash},1,"rain:$r" ];}
  1410. if(defined $ir) {$sM .= "IR: $ir " ;push @evtEt,[$mh{shash},1,"isRaining:$ir" ];}
  1411. if(defined $wd) {$sM .= "WD: $wd " ;push @evtEt,[$mh{shash},1,"windDirection:$wd" ];}
  1412. if(defined $wdr){$sM .= "WDR: $wdr ";push @evtEt,[$mh{shash},1,"windDirRange:$wdr" ];}
  1413. if(defined $s) {$sM .= "S: $s " ;push @evtEt,[$mh{shash},1,"sunshine:$s" ];}
  1414. if(defined $b) {$sM .= "B: $b " ;push @evtEt,[$mh{shash},1,"brightness:$b" ];}
  1415. push @evtEt,[$mh{shash},1,$sM];
  1416. }
  1417. elsif ($mh{mTp} eq "41"){
  1418. my ($chn,$state)=(hex($mI[0]),$mI[2]);
  1419. #my $cnt = hex($mI[1]);
  1420. my $txt;
  1421. if ($mh{cHash}->{helper}{lm} && $mh{cHash}->{helper}{lm}{hex($state)}){$txt = $mh{cHash}->{helper}{lm}{hex($state)}}
  1422. elsif ($lvlStr{md}{$mh{md}}) {$txt = $lvlStr{md}{$mh{md}}{$state}}
  1423. elsif ($lvlStr{st}{$mh{st}}) {$txt = $lvlStr{st}{$mh{st}}{$state}}
  1424. else {$txt = "unknown:$state"}
  1425. push @evtEt,[$mh{cHash},1,"storm:$txt"];
  1426. push @evtEt,[$mh{devH} ,1,"trig_$mh{chnHx}:$mh{dstN}"];
  1427. my $err = $chn & 0x80;
  1428. push @evtEt,[$mh{devH},1,"battery:". ($err?"low" :"ok" )] if ($mh{md} eq "HM-WDS100-C6-O-2"); #has no battery
  1429. }
  1430. else {
  1431. push @evtEt,[$mh{shash},1,"unknown:$mh{p}"];
  1432. }
  1433. }
  1434. elsif($mh{md} =~ m/^(HM-CC-TC|ROTO_ZEL-STG-RM-FWT)/) { ######################
  1435. my $chn = $mI[1];
  1436. if( $mh{mTp} eq "70") { # weather event
  1437. $chn = '01'; # fix definition
  1438. my ($t,$h) = (hex($mI[0].$mI[1]), hex($mI[2]));# temp is 15 bit signed
  1439. $t &= 0x7fff;
  1440. $t = -1 - ($t ^ 0x7FFF) if ($t & 0x4000);
  1441. $t /= 10;
  1442. my $chnHash = $modules{CUL_HM}{defptr}{$mh{src}.$chn};
  1443. if ($chnHash){
  1444. push @evtEt,[$chnHash,1,"state:T: $t H: $h"];
  1445. push @evtEt,[$chnHash,1,"measured-temp:$t"];
  1446. push @evtEt,[$chnHash,1,"humidity:$h"];
  1447. }
  1448. push @evtEt,[$mh{shash},1,"state:T: $t H: $h"];
  1449. push @evtEt,[$mh{shash},1,"measured-temp:$t"];
  1450. push @evtEt,[$mh{shash},1,"humidity:$h"];
  1451. }
  1452. elsif( $mh{mTp} eq "58") {# climate event
  1453. $chn = '02'; # fix definition
  1454. my ( $d1, $vp) = # adjust_command[0..4] adj_data[0..250]
  1455. ( $mI[0], hex($mI[1]));
  1456. $vp = int($vp/2.56+0.5); # valve position in %
  1457. my $chnHash = $modules{CUL_HM}{defptr}{$mh{src}.$chn};
  1458. if($chnHash){
  1459. push @evtEt,[$chnHash,1,"state:$vp"];
  1460. if ($chnHash->{helper}{needUpdate}){
  1461. if ($chnHash->{helper}{needUpdate} == 1){
  1462. $chnHash->{helper}{needUpdate}++;
  1463. }
  1464. else{
  1465. CUL_HM_qStateUpdatIfEnab(":".$chnHash->{NAME});
  1466. delete $chnHash->{helper}{needUpdate};
  1467. }
  1468. }
  1469. }
  1470. push @evtEt,[$mh{devH},1,"actuator:$vp"];
  1471. # Set the valve state too, without an extra trigger
  1472. if($mh{dstH}){
  1473. push @evtEt,[$mh{dstH},1,"state:set_$vp" ];
  1474. push @evtEt,[$mh{dstH},1,"ValveDesired:$vp"];
  1475. }
  1476. }
  1477. elsif(($mh{mTyp} eq '0201')|| # ackStatus
  1478. ($mh{mTyp} eq '1006')){ # infoStatus
  1479. my $dTemp = hex($mI[2])/2;
  1480. $dTemp = ($dTemp < 6 )?'off':
  1481. ($dTemp >30 )?'on' :sprintf("%0.1f", $dTemp);
  1482. my $err = hex($mI[3]);
  1483. my $chnHash = $modules{CUL_HM}{defptr}{$mh{src}.$chn};
  1484. if($chnHash){
  1485. my $chnName = $chnHash->{NAME};
  1486. my $mode = ReadingsVal($chnName,"R-controlMode","");
  1487. push @evtEt,[$chnHash,1,"desired-temp:$dTemp"];
  1488. push @evtEt,[$chnHash,1,"desired-temp-manu:$dTemp"] if($mode =~ m/manual/ && $mh{mTp} eq '10');
  1489. $chnHash->{helper}{needUpdate} = 1 if($mode =~ m/central/ && $mh{mTp} eq '10');
  1490. }
  1491. push @evtEt,[$mh{shash},1,"desired-temp:$dTemp"];
  1492. push @evtEt,[$mh{devH},1,"battery:".($err&0x80?"low":"ok")];
  1493. }
  1494. elsif( $mh{mTp} eq "10" && # Config change report
  1495. ($mh{p} =~ m/^0402000000000501/)) { # paramchanged L5
  1496. my $chnHash = $modules{CUL_HM}{defptr}{$mh{src}.$chn};
  1497. my $dTemp;
  1498. if($chnHash){
  1499. my $chnName = $chnHash->{NAME};
  1500. my $mode = ReadingsVal($chnName,"R-controlMode","");
  1501. $dTemp = ReadingsVal($chnName,"desired-temp","21.0");
  1502. if (!$chnHash->{helper}{oldMode} || $chnHash->{helper}{oldMode} ne $mode){
  1503. $dTemp = ReadingsVal($chnName,"desired-temp-manu",$dTemp)if ($mode =~ m/manual/);
  1504. $dTemp = ReadingsVal($chnName,"desired-temp-cent",$dTemp)if ($mode =~ m/central/);
  1505. $chnHash->{helper}{oldMode} = $mode;
  1506. }
  1507. push @evtEt,[$chnHash,1,"desired-temp:$dTemp"];
  1508. }
  1509. push @evtEt,[$mh{shash},1,"desired-temp:$dTemp"]
  1510. }
  1511. elsif( $mh{mTp} eq "01"){ # status reports
  1512. if($mh{p} =~ m/^010809(..)0A(..)/) { # TC set valve for VD => post events to VD
  1513. my ( $of, $vep) = (hex($1), hex($2));
  1514. push @evtEt,[$mh{devH},1,"ValveErrorPosition_for_$mh{dstN}: $vep"];
  1515. push @evtEt,[$mh{devH},1,"ValveOffset_for_$mh{dstN}: $of"];
  1516. push @evtEt,[$mh{dstH},1,"ValveErrorPosition:set_$vep"];
  1517. push @evtEt,[$mh{dstH},1,"ValveOffset:set_$of"];
  1518. }
  1519. elsif($mh{p} =~ m/^010[56]/){ # 'prepare to set' or 'end set'
  1520. push @evtEt,[$mh{shash},1,""]; #
  1521. }
  1522. }
  1523. elsif( $mh{mTp} eq "3F" && $ioId eq $mh{dst}) { # Timestamp request
  1524. my $s2000 = sprintf("%02X", CUL_HM_secSince2000());
  1525. push @ack,$mh{shash},"$mh{mNo}803F$ioId$mh{src}0204$s2000";
  1526. push @evtEt,[$mh{shash},1,"time-request"];
  1527. }
  1528. }
  1529. elsif($mh{md} =~ m/^(HM-CC-VD|ROTO_ZEL-STG-RM-FSA)/) { ######################
  1530. if($mh{mTp} eq "02" && @mI > 2) {#subtype+chn+value+err
  1531. my ($chn,$vp, $err) = map{hex($_)} @mI[1..3];
  1532. $chn = sprintf("%02X",$chn&0x3f);
  1533. $vp = int($vp)/2; # valve position in %
  1534. push @evtEt,[$mh{shash},1,"ValvePosition:$vp"];
  1535. push @evtEt,[$mh{shash},1,"state:$vp"];
  1536. $mh{shash} = $modules{CUL_HM}{defptr}{"$mh{src}$chn"}
  1537. if($modules{CUL_HM}{defptr}{"$mh{src}$chn"});
  1538. my $stErr = ($err >>1) & 0x7; # Status-Byte Evaluation
  1539. push @evtEt,[$mh{devH},1,"battery:".(($stErr == 4)?"critical":($err&0x80?"low":"ok"))];
  1540. if (!$stErr){#remove both conditions
  1541. push @evtEt,[$mh{devH},1,"motorErr:ok"];
  1542. }
  1543. else{
  1544. push @evtEt,[$mh{devH},1,"motorErr:blocked" ]if($stErr == 1);
  1545. push @evtEt,[$mh{devH},1,"motorErr:loose" ]if($stErr == 2);
  1546. push @evtEt,[$mh{devH},1,"motorErr:adjusting range too small"]if($stErr == 3);
  1547. # push @evtEt,[$mh{shash},1,"battery:critical" ]if($stErr == 4);
  1548. }
  1549. push @evtEt,[$mh{devH},1,"motor:opening"] if(($err&0x30) == 0x10);
  1550. push @evtEt,[$mh{devH},1,"motor:closing"] if(($err&0x30) == 0x20);
  1551. push @evtEt,[$mh{devH},1,"motor:stop" ] if(($err&0x30) == 0x00);
  1552. #VD hang detection
  1553. my $des = ReadingsVal($mh{devN}, "ValveDesired", $vp);
  1554. $des =~ s/ .*//; # remove unit
  1555. if (($des < $vp-1 || $des > $vp+1) && ($err&0x30) == 0x00){
  1556. if ($mh{shash}->{helper}{oldDes} eq $des){#desired valve position stable
  1557. push @evtEt,[$mh{shash},1,"operState:errorTargetNotMet"];
  1558. push @evtEt,[$mh{shash},1,"operStateErrCnt:".(ReadingsVal($mh{devN},"operStateErrCnt","0")+1)];
  1559. }
  1560. else{
  1561. push @evtEt,[$mh{shash},1,"operState:changed"];
  1562. }
  1563. }
  1564. else{
  1565. push @evtEt,[$mh{shash},1,"operState:".((($err&0x30) == 0x00)?"onTarget":"adjusting")];
  1566. }
  1567. $mh{shash}->{helper}{oldDes} = $des;
  1568. }
  1569. }
  1570. elsif($mh{md} =~ m/^HM-CC-RT-DN/) { #########################################
  1571. my %ctlTbl=( 0=>"auto", 1=>"manual", 2=>"party",3=>"boost");
  1572. if(($mh{mTyp} eq "100A") || #info-level/
  1573. ($mh{mTyp} eq "0201")) {#ackInfo
  1574. my %errTbl=( 0=>"ok", 1=>"ValveTight", 2=>"adjustRangeTooLarge"
  1575. ,3=>"adjustRangeTooSmall" , 4=>"communicationERR"
  1576. ,5=>"unknown", 6=>"lowBat", 7=>"ValveErrorPosition" );
  1577. my ($err ,$ctrlMode ,$setTemp ,$bTime,$pTemp,$pStart,$pEnd,$chn,$uk0,$lBat,$actTemp,$vp) =
  1578. (hex($mI[3]),hex($mI[5]),hex($mI[1].$mI[2]),"-" ,"-" ,"-" ,"-" );
  1579. if($mh{mTp} eq "10"){
  1580. $chn = "04";#fixed
  1581. my $bat =(($err ) & 0x1f)/10+1.5;
  1582. $actTemp =sprintf("%2.1f",((($setTemp ) & 0x3ff)/10));
  1583. $vp = (hex($mI[4]) ) & 0x7f ;
  1584. $setTemp = ($setTemp >>10);
  1585. $err = ($err >> 5);
  1586. $mh{shash} = $modules{CUL_HM}{defptr}{"$mh{src}$chn"} if($modules{CUL_HM}{defptr}{"$mh{src}$chn"});
  1587. push @evtEt,[$mh{shash},1,"measured-temp:$actTemp" ];
  1588. push @evtEt,[$mh{shash},1,"ValvePosition:$vp" ];
  1589. #device---
  1590. push @evtEt,[$mh{devH},1,"measured-temp:$actTemp"];
  1591. push @evtEt,[$mh{devH},1,"batteryLevel:$bat"];
  1592. push @evtEt,[$mh{devH},1,"actuator:$vp"];
  1593. if ($err == 6){ $lBat = "low";}
  1594. elsif($err == 0){ $lBat = "ok";}# we cannot determin bat if any other state!
  1595. #weather Chan
  1596. my $wHash = $modules{CUL_HM}{defptr}{$mh{src}."01"};
  1597. if ($wHash){
  1598. push @evtEt,[$wHash,1,"measured-temp:$actTemp"];
  1599. push @evtEt,[$wHash,1,"state:$actTemp"];
  1600. }
  1601. }
  1602. else{
  1603. $chn = $mI[1];
  1604. $setTemp = ($setTemp );
  1605. $lBat = $err&0x80?"low":"ok"; # prior to changes of $err!
  1606. $err = ($err >> 1);
  1607. $mh{shash} = $modules{CUL_HM}{defptr}{"$mh{src}$chn"} if($modules{CUL_HM}{defptr}{"$mh{src}$chn"});
  1608. $actTemp = ReadingsVal($mh{devN},"measured-temp","");
  1609. $vp = ReadingsVal($mh{devN},"actuator","");
  1610. }
  1611. delete $mh{devH}->{helper}{getBatState};
  1612. $setTemp =(($setTemp ) & 0x3f )/2;
  1613. $err = ($err ) & 0x7 ;
  1614. $uk0 = ($ctrlMode ) & 0x3f ;#unknown
  1615. $ctrlMode = ($ctrlMode >> 6) & 0x3 ;
  1616. $setTemp = ($setTemp < 5 )?'off':
  1617. ($setTemp >30 )?'on' :sprintf("%.1f",$setTemp);
  1618. if (defined $mI[12]){# message with party mode
  1619. my @pt = map{hex($_)} @mI[5..$#mI];
  1620. $pTemp =(($pt[7] )& 0x3f)/2 if (defined $pt[7]) ;
  1621. my $sta = ( ($pt[0] )& 0x3f)/2;
  1622. $pStart = (($pt[2] )& 0x7f) # year
  1623. ."-".(($pt[6] >> 4)& 0x0f) # month
  1624. ."-".(($pt[1] )& 0x1f) # day
  1625. ." ".int($sta) # Time h
  1626. .":".(int($sta)!=$sta?"30":"00")# Time min
  1627. ;
  1628. my $et = ( ($pt[3] )& 0x3f)/2;
  1629. $pEnd = (($pt[5] )& 0x7f) # year
  1630. ."-".(($pt[6] )& 0x0f) # month
  1631. ."-".(($pt[4] )& 0x1f) # day
  1632. ." ".int($et) # Time h
  1633. .":".(int($et)!=$et?"30":"00")# Time min
  1634. ;
  1635. }
  1636. elsif(defined $mI[5] && $ctrlMode == 3 ){#message with boost
  1637. $bTime = ((hex($mI[5]) ) & 0x3f)." min";
  1638. }
  1639. my $climaHash = CUL_HM_id2Hash($mh{src}."04");# always to Clima channel
  1640. push @evtEt,[$climaHash,1,"desired-temp:$setTemp" ];
  1641. push @evtEt,[$climaHash,1,"controlMode:$ctlTbl{$ctrlMode}"];
  1642. push @evtEt,[$climaHash,1,"state:T: $actTemp desired: $setTemp valve: $vp"];
  1643. push @evtEt,[$climaHash,1,"boostTime:$bTime"];
  1644. push @evtEt,[$climaHash,1,"partyStart:$pStart"];
  1645. push @evtEt,[$climaHash,1,"partyEnd:$pEnd"];
  1646. push @evtEt,[$climaHash,1,"partyTemp:$pTemp"];
  1647. #push @evtEt,[$mh{shash},1,"unknown0:$uk0"];
  1648. #push @evtEt,[$mh{shash},1,"unknown1:".$2 if ($p =~ m/^0A(.10)(.*)/)];
  1649. push @evtEt,[$mh{devH},1,"motorErr:$errTbl{$err}" ] if($mh{mTp} eq "10");
  1650. push @evtEt,[$mh{devH},1,"battery:$lBat"] if ($lBat);
  1651. push @evtEt,[$mh{devH},1,"desired-temp:$setTemp"];
  1652. }
  1653. elsif($mh{mTp} eq "59" && defined $mI[0]) {#inform team about new value
  1654. my $setTemp = sprintf("%.1f",int(hex($mI[0])/4)/2);
  1655. my $ctrlMode = hex($mI[0])&0x3;
  1656. push @evtEt,[$mh{shash},1,"desired-temp:$setTemp"];
  1657. push @evtEt,[$mh{shash},1,"controlMode:$ctlTbl{$ctrlMode}"];
  1658. my $tHash = $modules{CUL_HM}{defptr}{$mh{dst}."04"};
  1659. if ($tHash){
  1660. push @evtEt,[$tHash,1,"desired-temp:$setTemp"];
  1661. push @evtEt,[$tHash,1,"controlMode:$ctlTbl{$ctrlMode}"];
  1662. }
  1663. }
  1664. elsif($mh{mTp} eq "3F" && $ioId eq $mh{dst}) { # Timestamp request
  1665. my $s2000 = sprintf("%02X", CUL_HM_secSince2000());
  1666. push @ack,$mh{shash},"$mh{mNo}803F$ioId$mh{src}0204$s2000";
  1667. push @evtEt,[$mh{shash},1,"time-request"];
  1668. # schedule desired-temp just to get an AckInfo for battery state
  1669. $mh{shash}->{helper}{getBatState} = 1;
  1670. }
  1671. }
  1672. elsif($mh{md} eq "HM-TC-IT-WM-W-EU") { ######################################
  1673. my %ctlTbl=( 0=>"auto", 1=>"manual", 2=>"party",3=>"boost");
  1674. if( ( $mh{mTp} eq "10" && $mI[0] eq '0B') #info-level
  1675. ||( $mh{mTp} eq "02" && $mI[0] eq '01')) {#ack-status
  1676. my @d = map{hex($_)} unpack 'A2A4(A2)*',$mh{p};
  1677. my ($setTemp,$actTemp, $cRep,$wRep,$bat ,$lbat,$ctrlMode,$bTime,$pTemp,$pStart,$pEnd) =
  1678. ($d[1],$d[1], $d[2],$d[2],$d[2],$d[2],"" ,"-" ,"-" ,"-" ,"-");
  1679. CUL_HM_m_setCh(\%mh,"02");
  1680. $lbat = ($lbat ) & 0x80;
  1681. if ($mh{mTp} eq "10"){
  1682. $ctrlMode = $d[3];
  1683. $bat =(($bat ) & 0x1f)/10+1.5;
  1684. $setTemp =(($setTemp >>10) & 0x3f )/2;
  1685. $actTemp =(($actTemp ) & 0x3ff)/10;
  1686. $actTemp = -1 * $actTemp if ($d[1] & 0x200 );# obey signed
  1687. $actTemp = sprintf("%2.1f",$actTemp);
  1688. push @evtEt,[$mh{cHash},1,"measured-temp:$actTemp"];
  1689. push @evtEt,[$mh{devH},1,"measured-temp:$actTemp"];
  1690. push @evtEt,[$mh{devH},1,"batteryLevel:$bat"];
  1691. $cRep = (($cRep >>6) & 0x01 )?"on":"off";
  1692. $wRep = (($wRep >>5) & 0x01 )?"on":"off";
  1693. }
  1694. else{#actTemp is not provided in ack message - use old value
  1695. $ctrlMode = $d[4];
  1696. $actTemp = ReadingsVal($mh{devN},"measured-temp",0);
  1697. $setTemp =(hex($mI[2]) & 0x3f )/2;
  1698. $cRep = (($cRep >>2) & 0x01 )?"on":"off";
  1699. $wRep = (($wRep >>1) & 0x01 )?"on":"off";
  1700. }
  1701. $ctrlMode = ($ctrlMode >> 6) & 0x3 ;
  1702. $setTemp = ($setTemp < 5 )?'off':
  1703. ($setTemp >30 )?'on' :sprintf("%.1f",$setTemp);
  1704. if (defined $d[11]){# message with party mode
  1705. $pTemp =(($d[11] )& 0x3f)/2 if (defined $d[11]) ;
  1706. my @p;
  1707. if ($mh{mTp} eq "10") {@p = @d[3..9]}
  1708. else {@p = @d[4..10]}
  1709. my $sta = (($p[0] )& 0x3f)/2;
  1710. $pStart = (($p[2] )& 0x7f) # year
  1711. ."-".(($p[6] >> 4)& 0x0f) # month
  1712. ."-".(($p[1] )& 0x1f) # day
  1713. ." ".int($sta) # Time h
  1714. .":".(int($sta)!=$sta?"30":"00")# Time min
  1715. ;
  1716. my $et = (($p[3] )& 0x3f)/2;
  1717. $pEnd = (($p[5] )& 0x7f) # year
  1718. ."-".(($p[6] )& 0x0f) # month
  1719. ."-".(($p[4] )& 0x1f) # day
  1720. ." ".int($et) # Time h
  1721. .":".(int($et)!=$et?"30":"00")# Time min
  1722. ;
  1723. push @evtEt,[$mh{cHash},1,"partyStart:$pStart"];
  1724. push @evtEt,[$mh{cHash},1,"partyEnd:$pEnd"];
  1725. push @evtEt,[$mh{cHash},1,"partyTemp:$pTemp"];
  1726. }
  1727. elsif(defined $d[3] && $ctrlMode == 3 ){#message with boost
  1728. $bTime = (($d[3] ) & 0x3f)." min";
  1729. }
  1730. push @evtEt,[$mh{cHash},1,"desired-temp:$setTemp"];
  1731. push @evtEt,[$mh{cHash},1,"controlMode:$ctlTbl{$ctrlMode}"];
  1732. push @evtEt,[$mh{cHash},1,"state:T: $actTemp desired: $setTemp"];
  1733. push @evtEt,[$mh{cHash},1,"commReporting:$cRep"];
  1734. push @evtEt,[$mh{cHash},1,"winOpenReporting:$wRep"];
  1735. push @evtEt,[$mh{cHash},1,"boostTime:$bTime"];
  1736. push @evtEt,[$mh{devH},1,"battery:".($lbat?"low":"ok")];
  1737. push @evtEt,[$mh{devH},1,"desired-temp:$setTemp"];
  1738. }
  1739. elsif($mh{mTp} eq "70"){
  1740. my $chn = "01";
  1741. $mh{shash} = $modules{CUL_HM}{defptr}{"$mh{src}$chn"}
  1742. if($modules{CUL_HM}{defptr}{"$mh{src}$chn"});
  1743. my ($t,$h) = map{hex($_)} unpack 'A4A2',$mh{p};
  1744. $t &= 0x7fff;
  1745. $t -= 0x8000 if($t & 0x4000);
  1746. $t = sprintf("%0.1f", $t/10);
  1747. push @evtEt,[$mh{shash},1,"temperature:$t"];
  1748. push @evtEt,[$mh{shash},1,"humidity:$h"];
  1749. push @evtEt,[$mh{shash},1,"state:T: $t H: $h"];
  1750. }
  1751. elsif($mh{mTp} eq "5A"){# thermal control - might work with broadcast
  1752. my $chn = "02";
  1753. $mh{shash} = $modules{CUL_HM}{defptr}{"$mh{src}$chn"}
  1754. if($modules{CUL_HM}{defptr}{"$mh{src}$chn"});
  1755. my ($t,$h) = map{hex($_)} unpack 'A4A2',$mh{p};
  1756. my $setTemp =(($t >>10) & 0x3f )/2;
  1757. my $actTemp =(($t ) & 0x3ff)/10;
  1758. $actTemp = sprintf("%2.1f",$actTemp);
  1759. $setTemp = ($setTemp < 5 )?'off':
  1760. ($setTemp >30 )?'on' :sprintf("%.1f",$setTemp);
  1761. push @evtEt,[$mh{shash},1,"measured-temp:$actTemp"];
  1762. push @evtEt,[$mh{shash},1,"desired-temp:$setTemp"];
  1763. push @evtEt,[$mh{shash},1,"humidity:$h"];
  1764. push @evtEt,[$mh{shash},1,"state:T: $actTemp desired: $setTemp"];
  1765. }
  1766. elsif($mh{mTp} =~ m/^4./) {
  1767. my ($chn,$lvl) = ($mI[0],hex($mI[2])/2);
  1768. my $chnHash = $modules{CUL_HM}{defptr}{$mh{src}.$chn};
  1769. if ($chnHash){
  1770. push @evtEt,[$chnHash,1,"level:$lvl"];
  1771. }
  1772. }
  1773. elsif($mh{mTp} eq "3F" && $ioId eq $mh{dst}) { # Timestamp request
  1774. my $s2000 = sprintf("%02X", CUL_HM_secSince2000());
  1775. push @ack,$mh{shash},"$mh{mNo}803F$ioId$mh{src}0204$s2000";
  1776. push @evtEt,[$mh{shash},1,"time-request"];
  1777. }
  1778. }
  1779. elsif($mh{md} =~ m/^(HM-Sen-Wa-Od|HM-CC-SCD)$/){ ############################
  1780. if (($mh{mTyp} eq "0201") || # handle Ack_Status
  1781. ($mh{mTyp} eq "1006") || #or Info_Status message here
  1782. ($mh{mTp} eq "41")) {
  1783. my $lvl = $mI[2];
  1784. my $err = ($mh{mTp} eq "41") ? hex($mI[0]) : ($mI[3] ? hex($mI[3]) : "");
  1785. if ($lvlStr{md}{$mh{md}}){$lvl = $lvlStr{md}{$mh{md}}{$lvl}}
  1786. elsif ($lvlStr{st}{$mh{st}}){$lvl = $lvlStr{st}{$mh{st}}{$lvl} }
  1787. else {$lvl = hex($lvl)/2}
  1788. push @evtEt,[$mh{shash},1,"level:$lvl"] if($mh{md} eq "HM-Sen-Wa-Od");
  1789. push @evtEt,[$mh{shash},1,"state:$lvl"];
  1790. push @evtEt,[$mh{devH} ,1,"battery:".($err&0x80?"low":"ok")] if ($err ne "");
  1791. }
  1792. }
  1793. elsif($mh{md} eq "KFM-Sensor") { ############################################
  1794. if ($mh{mTp} eq "53"){
  1795. if($mh{p} =~ m/^(..)4(.)0200(..)(..)(..)/) {
  1796. my ($chn,$seq, $k_v1, $k_v2, $k_v3) = (hex($1),hex($2),$3,hex($4),hex($5));
  1797. push @evtEt,[$mh{devH},1,"battery:".($chn & 0x80?"low":"ok")];
  1798. my $v = 1408 - ((($k_v3 & 0x07)<<8) + $k_v2);
  1799. push @evtEt,[$mh{shash},1,"rawValue:$v"];
  1800. my $nextSeq = ReadingsVal($mh{devN},"Sequence","");
  1801. $nextSeq =~ s/_.*//;
  1802. $nextSeq = ($nextSeq %15)+1;
  1803. push @evtEt,[$mh{shash},1,"Sequence:$seq".($nextSeq ne $seq?"_seqMiss":"")];
  1804. my $r2r = AttrVal($mh{devN}, "rawToReadable", undef);
  1805. if($r2r) {
  1806. my @r2r = split("[ :]", $r2r);
  1807. foreach(my $idx = 0; $idx < @r2r-2; $idx+=2) {
  1808. if($v >= $r2r[$idx] && $v <= $r2r[$idx+2]) {
  1809. my $f = (($v-$r2r[$idx])/($r2r[$idx+2]-$r2r[$idx]));
  1810. my $cv = ($r2r[$idx+3]-$r2r[$idx+1])*$f + $r2r[$idx+1];
  1811. my $unit = AttrVal($mh{devN}, "unit", "");
  1812. push @evtEt,[$mh{shash},1,sprintf("state:%.1f %s",$cv,$unit)];
  1813. push @evtEt,[$mh{shash},1,sprintf("content:%.1f %s",$cv,$unit)];
  1814. last;
  1815. }
  1816. }
  1817. }
  1818. else {
  1819. push @evtEt,[$mh{shash},1,"state:$v"];
  1820. }
  1821. }
  1822. }
  1823. }
  1824. elsif($mh{st} eq "THSensor") { ##############################################
  1825. if ($mh{mTp} eq "70"){
  1826. my $chn;
  1827. my ($d1,$h,$ap) = map{hex($_)} unpack 'A4A2A4',$mh{p};
  1828. if ($mh{md} =~ m/^(WS550|WS888|HM-WDC7000)/){$chn = "10"}
  1829. elsif ($mh{md} =~ m/^HM-WDS30-OT2-SM/) {$chn = "05";$h=""}
  1830. elsif ($mh{md} =~ m/^(S550IA|HM-WDS30-T-O)/) {$chn = "01";$h=""}
  1831. else {$chn = "01"}
  1832. my $t = $d1 & 0x7fff;
  1833. $t -= 0x8000 if($t &0x4000);
  1834. $t = sprintf("%0.1f", $t/10);
  1835. my $statemsg = "state:T: $t";
  1836. push @evtEt,[$mh{shash},1,"temperature:$t"];#temp is always there
  1837. push @evtEt,[$mh{devH},1,"battery:".($d1 & 0x8000?"low":"ok")];
  1838. if($modules{CUL_HM}{defptr}{$mh{src}.$chn}){
  1839. my $ch = $modules{CUL_HM}{defptr}{$mh{src}.$chn};
  1840. push @evtEt,[$ch,1,$statemsg];
  1841. push @evtEt,[$ch,1,"temperature:$t"];
  1842. }
  1843. if ($h) {$statemsg .= " H: $h" ; push @evtEt,[$mh{shash},1,"humidity:$h"]; }
  1844. if ($ap){$statemsg .= " AP: $ap"; push @evtEt,[$mh{shash},1,"airpress:$ap"];}
  1845. push @evtEt,[$mh{shash},1,$statemsg];
  1846. }
  1847. elsif ($mh{mTp} eq "53"){
  1848. my ($chn,@dat) = unpack 'A2(A6)*',$mh{p};
  1849. push @evtEt,[$mh{devH},1,"battery:".(hex($chn)&0x80?"low":"ok")];
  1850. foreach (@dat){
  1851. my ($a,$d) = unpack 'A2A4',$_;
  1852. $d = hex($d);
  1853. $d -= 0x10000 if($d & 0xC000);
  1854. $d = sprintf("%0.1f",$d/10);
  1855. my $chId = sprintf("%02X",hex($a) & 0x3f);
  1856. my $chnHash = $modules{CUL_HM}{defptr}{$mh{src}.$chId};
  1857. if ($chnHash){
  1858. push @evtEt,[$chnHash,1,"state:T: $d"];
  1859. push @evtEt,[$chnHash,1,"temperature:$d"];
  1860. }
  1861. else{
  1862. push @evtEt,[$mh{shash},1,"Chan_$chId:T: $d"];
  1863. }
  1864. }
  1865. }
  1866. }
  1867. elsif($mh{st} eq "sensRain") {###############################################
  1868. my $hHash = CUL_HM_id2Hash($mh{src}."02");# hash for heating
  1869. my $pon = 0;# power on if mNo == 0 and heating status plus second msg
  1870. # status or trigger from rain channel
  1871. if (($mh{mTyp} eq "0201") || #Ack_Status
  1872. ($mh{mTyp} eq "1006")) { #Info_Status
  1873. my ($subType,$chn,$val,$err) = ($mI[0],hex($mI[1]),$mI[2],hex($mI[3]));
  1874. $chn = sprintf("%02X",$chn&0x3f);
  1875. my $chId = $mh{src}.$chn;
  1876. $mh{shash} = $modules{CUL_HM}{defptr}{$chId}
  1877. if($modules{CUL_HM}{defptr}{$chId});
  1878. my ($timedOn,$stateExt)=("off","");
  1879. if($err&0x40 && $chn eq "02"){
  1880. $timedOn = "running";
  1881. $stateExt = "-till" if(AttrVal($mh{cName},"param","") =~ m/showTimed/ );
  1882. }
  1883. push @evtEt,[$mh{cHash},1,"timedOn:$timedOn"];
  1884. my $mdCh = $mh{md}.$chn;
  1885. if($lvlStr{mdCh}{$mdCh} && $lvlStr{mdCh}{$mdCh}{$val}){
  1886. $val = $lvlStr{mdCh}{$mdCh}{$val};
  1887. }
  1888. else{
  1889. $val = hex($val)/2;
  1890. }
  1891. push @evtEt,[$mh{shash},1,"state:$val$stateExt"];
  1892. if ($val eq "rain"){#--- handle lastRain---
  1893. $mh{shash}->{helper}{lastRain} = $mh{tmStr};
  1894. }
  1895. elsif ($val eq "dry" && $mh{shash}->{helper}{lastRain}){
  1896. push @evtEt,[$mh{shash},1,"lastRain:$mh{shash}->{helper}{lastRain}"];
  1897. delete $mh{shash}->{helper}{lastRain};
  1898. }
  1899. push @evtEt,[$mh{shash},0,'.level:'.($val eq "off"?"0":"100")];
  1900. if ($mh{mNo} eq "00" && $chn eq "02" && $val eq "on"){
  1901. $hHash->{helper}{pOn} = 1;
  1902. }
  1903. elsif ($mh{mNo} eq "01" && $chn eq "01" &&
  1904. $hHash->{helper}{pOn} && $hHash->{helper}{pOn} == 1){
  1905. $pon = 1;
  1906. }
  1907. else{
  1908. delete $hHash->{helper}{pOn};
  1909. my $hHash = CUL_HM_id2Hash($mh{src}."02");# hash for heating
  1910. if ($chn eq "01" &&
  1911. $hHash->{helper}{param} && $hHash->{helper}{param}{onAtRain}){
  1912. CUL_HM_Set($hHash,$hHash->{NAME},$val eq "rain"?"on":"off");
  1913. }
  1914. }
  1915. }
  1916. elsif ($mh{mTp} eq "41") { #eventonAtRain
  1917. my ($chn,$bno,$val) = @mI;
  1918. $chn = sprintf("%02X",hex($chn)&0x3f);
  1919. $mh{shash} = $modules{CUL_HM}{defptr}{$mh{src}.$chn}
  1920. if($modules{CUL_HM}{defptr}{$mh{src}.$chn});
  1921. push @evtEt,[$mh{shash},1,"trigger:".hex($bno).":".$lvlStr{mdCh}{$mh{md}.$chn}{$val}.$target];
  1922. if ($mh{mNo} eq "01" && $bno eq "01" &&
  1923. $hHash->{helper}{pOn} && $hHash->{helper}{pOn} == 1){
  1924. $pon = 1;
  1925. }
  1926. delete $mh{shash}->{helper}{pOn};
  1927. }
  1928. if ($pon){# we have power ON, perform action
  1929. if($mh{devH}->{helper}{PONtest}){
  1930. push @evtEt,[$mh{devH},1,"powerOn:$mh{tmStr}",];
  1931. $mh{devH}->{helper}{PONtest} = 0;
  1932. }
  1933. CUL_HM_Set($hHash,$hHash->{NAME},"off")
  1934. if ($hHash && $hHash->{helper}{param}{offAtPon});
  1935. }
  1936. }
  1937. elsif($mh{st} =~ m/^(switch|dimmer|blindActuator|rgb)$/) {###################
  1938. if (($mh{mTyp} eq "0201") || # handle Ack_Status
  1939. ($mh{mTyp} eq "1006")) { # or Info_Status message here
  1940. my $rSUpdt = 0;# require status update
  1941. my ($val,$err) = (hex($mI[2]),hex($mI[3]));
  1942. $val /= 2 if ($mh{st} ne "rgb" || $mh{chn} != 3);
  1943. CUL_HM_m_setCh(\%mh,$mI[1]);
  1944. my($lvlMin,$lvlMax)=split",",AttrVal($mh{cName}, "levelRange", "0,100");
  1945. my $physLvl; #store phys level if available
  1946. if( defined $mI[5] #message with physical level?
  1947. && $mh{st} eq "dimmer"){
  1948. my $pl = hex($mI[5])/2;
  1949. my $vDim = $mh{cHash}->{helper}{vDim}; #shortcut
  1950. if ($vDim->{idPhy} &&
  1951. CUL_HM_id2Hash($vDim->{idPhy})){ #has virt chan
  1952. RemoveInternalTimer("sUpdt:".$mh{src}.$mh{chnM});
  1953. if ($mh{mTp} eq "10"){ #valid PhysLevel
  1954. foreach my $tmpKey ("idPhy","idV2","idV3",){#update all virtuals
  1955. my $vh = ($vDim->{$tmpKey} ? CUL_HM_id2Hash($vDim->{$tmpKey}) : "");
  1956. next if (!$vh || $vDim->{$tmpKey} eq $mh{src}.$mh{chnM});
  1957. my $vl = ReadingsVal($vh->{NAME},"level","???");
  1958. my $vs = ($vl eq "100"?"on":($vl eq "0"?"off":"$vl"));
  1959. my($clvlMin,$clvlMax)=split",",AttrVal($vh->{NAME}, "levelRange", "0,100");
  1960. my $plc = int((($pl-$clvlMin)*200)/($clvlMax - $clvlMin))/2;
  1961. $plc = 1 if ($pl && $plc <= 0);
  1962. $vs = ($plc ne $vl)?"chn:$vs phys:$plc":$vs;
  1963. push @evtEt,[$vh,1,"state:$vs"];
  1964. push @evtEt,[$vh,1,"phyLevel:$pl"];
  1965. }
  1966. push @evtEt,[$mh{cHash},1,"phyLevel:$pl"]; #phys level,don't use relative adjustment
  1967. $pl = (($pl-$lvlMin)<=0 && $pl)
  1968. ? ($pl?1:0)
  1969. : int((($pl-$lvlMin)*200)/($lvlMax - $lvlMin))/2;
  1970. $physLvl = $pl;
  1971. }
  1972. else{ #invalid PhysLevel
  1973. $rSUpdt = 1;
  1974. CUL_HM_stateUpdatDly($mh{cName},5) if ($mh{cHash}->{helper}{dlvl});# update to get level
  1975. }
  1976. }
  1977. }
  1978. my $pVal = $val;# necessary for oper 'off', not logical off
  1979. $val = (($val-$lvlMin)<=0)
  1980. ? ($val?1:0)
  1981. : int((($val-$lvlMin)*200)/($lvlMax - $lvlMin))/2;
  1982. # blind option: reverse Level Meaning 0 = open, 100 = closed
  1983. if (AttrVal($mh{cName}, "param", "") =~ m/levelInverse/){;
  1984. $pVal = $val = 100-$val;
  1985. }
  1986. if(!defined $physLvl){ #not updated? use old or ignore
  1987. $physLvl = ReadingsVal($mh{cName},"phyLevel",$val);
  1988. $physLvl = (($physLvl-$lvlMin)<=0 && $physLvl)
  1989. ? ($physLvl?1:0)
  1990. : int((($physLvl-$lvlMin)*200)/($lvlMax - $lvlMin))/2;
  1991. }
  1992. my $vs = ($mh{cHash}->{helper}{lm} && $mh{cHash}->{helper}{lm}{$val})
  1993. ?$mh{cHash}->{helper}{lm}{$val}
  1994. :($val==100 ? "on"
  1995. :($pVal==0 ? "off"
  1996. : "$val")); # user string...
  1997. #-- if timed on is set possibly show this in a state --
  1998. my ($timedOn,$stateExt)=("off","");
  1999. if($err&0x40){
  2000. $timedOn = "running";
  2001. $stateExt = "-till" if(AttrVal($mh{cName},"param","") =~ m/showTimed/ );
  2002. }
  2003. push @evtEt,[$mh{cHash},1,"level:$val"];
  2004. push @evtEt,[$mh{cHash},1,"pct:$val"]; # duplicate to level - necessary for "slider"
  2005. push @evtEt,[$mh{cHash},1,"deviceMsg:$vs$target"] if($mh{chnM} ne "00");
  2006. push @evtEt,[$mh{cHash},1,"state:".(($physLvl ne $val)?"chn:$vs phys:$physLvl":$vs.$stateExt)];
  2007. push @evtEt,[$mh{cHash},1,"timedOn:$timedOn"];
  2008. if ($mh{cHash}->{helper}{dlvl} && defined $err){#are we waiting?
  2009. if ($mI[2] ne $mh{cHash}->{helper}{dlvl} #level not met?
  2010. && !($err&0x70)){ #and already stopped not timedOn
  2011. #level not met, repeat
  2012. Log3 $mh{cName},3,"CUL_HM $mh{cName} repeat, level $mI[2] instead of $mh{cHash}->{helper}{dlvl}";
  2013. if ($mh{cHash}->{helper}{dlvlCmd}){# first try
  2014. CUL_HM_PushCmdStack($mh{cHash},$mh{cHash}->{helper}{dlvlCmd});
  2015. CUL_HM_ProcessCmdStack($mh{cHash});
  2016. delete $mh{cHash}->{helper}{dlvlCmd};# will prevent second try
  2017. }
  2018. else{# no second try - alarm and stop
  2019. push @evtEt,[$mh{cHash},1,"levelMissed:desired:".hex($mh{cHash}->{helper}{dlvl})/2];
  2020. delete $mh{cHash}->{helper}{dlvl};# we only make one attempt
  2021. }
  2022. }
  2023. else{# either level met, timed on or we are driving...
  2024. delete $mh{cHash}->{helper}{dlvl};
  2025. }
  2026. }
  2027. if ($mh{st} ne "switch"){
  2028. my $eventName = "unknown"; # different names for events
  2029. if ($mh{st} eq "blindActuator") {$eventName = "motor" ;}
  2030. elsif($mh{st} =~ m/^(dimmer|rgb)$/) {$eventName = "dim" ;}
  2031. my $dir = ($err >> 4) & 3;
  2032. my %dirName = ( 0=>"stop" ,1=>"up" ,2=>"down" ,3=>"err" );
  2033. push @evtEt,[$mh{cHash},1,"$eventName:$dirName{$dir}:$vs" ];
  2034. $mh{cHash}->{helper}{dir}{rct} = $mh{cHash}->{helper}{dir}{cur}
  2035. if($mh{cHash}->{helper}{dir}{cur} &&
  2036. $mh{cHash}->{helper}{dir}{cur} ne $dirName{$dir});
  2037. $mh{cHash}->{helper}{dir}{cur} = $dirName{$dir};
  2038. }
  2039. if (!$rSUpdt){#dont touch if necessary for dimmer
  2040. if(($err&0x70) == 0x10 || ($err&0x70) == 0x20){
  2041. my $wt = $mh{cHash}->{helper}{stateUpdatDly}
  2042. ?$mh{cHash}->{helper}{stateUpdatDly}
  2043. :120;
  2044. CUL_HM_stateUpdatDly($mh{cName},$wt);
  2045. }
  2046. else {
  2047. CUL_HM_unQEntity($mh{cName},"qReqStat");
  2048. }
  2049. delete $mh{cHash}->{helper}{stateUpdatDly};
  2050. }
  2051. if ($mh{st} eq "dimmer"){
  2052. if (lc($mh{md}) =~ m/^hm-lc-dim.l.*/){
  2053. push @evtEt,[$mh{cHash},1,"loadFail:".(($err == 6)?"on":"off")];#note: err is times 2!
  2054. }
  2055. else{
  2056. push @evtEt,[$mh{cHash},1,"overload:".(($err&0x02)?"on":"off")];
  2057. push @evtEt,[$mh{cHash},1,"overheat:".(($err&0x04)?"on":"off")];
  2058. push @evtEt,[$mh{cHash},1,"reduced:" .(($err&0x08)?"on":"off")];
  2059. }
  2060. #hack for blind - other then behaved devices blind does not send
  2061. # a status info for chan 0 at power on
  2062. # chn3 (virtual chan) and not used up to now
  2063. # info from it is likely a power on!
  2064. if($mh{devH}->{helper}{PONtest} && $mh{chn} == 3){
  2065. push @evtEt,[$mh{devH},1,"powerOn:$mh{tmStr}",] ;
  2066. $mh{devH}->{helper}{PONtest} = 0;
  2067. }
  2068. }
  2069. elsif ($mh{st} eq "rgb"){
  2070. if ($mh{chn} == 2){
  2071. push @evtEt,[$mh{cHash},1,"color:$val"]; # duplicate to color - necessary for "colorpicker"
  2072. push @evtEt,[$mh{cHash},1,"rgb:".(($val==100)?("FFFFFF"):(Color::hsv2hex($val/100,1,1)))];
  2073. }
  2074. push @evtEt,[$mh{cHash},1,"colProgram:$val"] if ($mh{chn} == 3); # duplicate to colProgram - necessary for "slider"
  2075. }
  2076. elsif ($mh{st} eq "blindActuator"){
  2077. my $param = AttrVal($mh{cName}, "param", "");
  2078. if ($param =~ m/ponRestoreSmart/){
  2079. if($parse eq "powerOn"){
  2080. my $level = ReadingsVal($mh{cName},"level",0);# still the old level
  2081. $mh{cHash}->{helper}{prePONlvl} = $level;
  2082. $level = ($level<50)?0:100;
  2083. CUL_HM_Set($mh{cHash},$mh{cName},"pct",$level);
  2084. }
  2085. elsif ( $mh{cHash}->{helper}{dir}{cur} eq "stop"
  2086. && defined $mh{cHash}->{helper}{prePONlvl} ){
  2087. if ($val != 0 && $val != 100){
  2088. CUL_HM_Set($mh{cHash},$mh{cName},"pct",$mh{cHash}->{helper}{prePONlvl});
  2089. }
  2090. delete $mh{cHash}->{helper}{prePONlvl};
  2091. }
  2092. }
  2093. elsif ($param =~ m/ponRestoreForce/){
  2094. if($parse eq "powerOn"){
  2095. my $level = ReadingsVal($mh{cName},"level",0);# still the old level
  2096. $mh{cHash}->{helper}{prePONlvl} = $level;
  2097. CUL_HM_Set($mh{cHash},$mh{cName},"pct","0");
  2098. }
  2099. elsif ( $mh{cHash}->{helper}{dir}{cur} eq "stop"
  2100. && defined $mh{cHash}->{helper}{prePONlvl}){
  2101. if ($val == 0){
  2102. CUL_HM_Set($mh{cHash},$mh{cName},"pct",100);
  2103. }
  2104. elsif($val == 100){
  2105. CUL_HM_Set($mh{cHash},$mh{cName},"pct",$mh{cHash}->{helper}{prePONlvl});
  2106. delete $mh{cHash}->{helper}{prePONlvl};
  2107. }
  2108. else{
  2109. delete $mh{cHash}->{helper}{prePONlvl};# some stop inbetween - maybe user action. stop processing
  2110. }
  2111. }
  2112. }
  2113. if ($mh{md} eq "HM-LC-Ja1PBU-FM" && defined $mI[6]){
  2114. my %dirName = ( 0=>"stop" ,1=>"up" ,2=>"down" ,3=>"err" );
  2115. push @evtEt,[$mh{cHash},1,"pctSlat:".hex($mI[5])/2];
  2116. push @evtEt,[$mh{cHash},1,"slatDir:".$dirName{hex($mI[6]) & 0x3}];
  2117. }
  2118. }
  2119. elsif ($mh{md} eq "HM-SEC-SFA-SM"){
  2120. push @evtEt,[$mh{devH},1,"powerError:" .(($err&0x02) ? "on":"off")];
  2121. push @evtEt,[$mh{devH},1,"sabotageError:".(($err&0x04) ? "on":"off")];
  2122. push @evtEt,[$mh{devH},1,"battery:".(($err&0x08)?"critical":($err&0x80?"low":"ok"))];
  2123. }
  2124. elsif ($mh{md} =~ m/^(HM-LC-SW.-BA-PCB|HM-Dis-TD-T)/){
  2125. push @evtEt,[$mh{devH},1,"battery:" . (($err&0x80) ? "low" : "ok" )];
  2126. }
  2127. }
  2128. }
  2129. elsif($mh{st} =~ m/^(remote|pushButton|swi|display)$/
  2130. ||$mh{md} eq "HM-SEN-EP") { #############################################
  2131. if($mh{mTp} eq "40") {
  2132. my $bat = ($mh{chnraw} & 0x80)?"low":"ok";
  2133. my $type = ($mh{chnraw} & 0x40)?"l":"s";
  2134. my $state = ($mh{chnraw} & 0x40)?"Long":"Short";
  2135. my $chId = $mh{src}.$mh{chnHx};
  2136. my $btnName = $mh{cHash}->{helper}{role}{chn}
  2137. ? $mh{cName}
  2138. : "Btn$mh{chn}";
  2139. if($type eq "l"){# long press
  2140. #$state .= ($mh{mFlgH} & 0x20 ? "Release" : "");# not sufficient
  2141. $state .= ((($mh{mFlgH} & 0x24) == 0x20) ? "Release" : "");
  2142. }
  2143. push @evtEt,[$mh{devH},1,"battery:$bat"];
  2144. push @evtEt,[$mh{devH},1,"state:$btnName $state"];
  2145. if($mh{md} eq "HM-Dis-WM55"){
  2146. if ($mh{devH}->{cmdStack}){# there are pending commands. we only send new ones
  2147. delete $mh{devH}->{cmdStack};
  2148. delete $mh{devH}->{cmdStacAESPend};
  2149. delete $mh{devH}->{helper}{prt}{rspWait};
  2150. delete $mh{devH}->{helper}{prt}{rspWaitSec};
  2151. delete $mh{devH}->{helper}{prt}{mmcA};
  2152. delete $mh{devH}->{helper}{prt}{mmcS};
  2153. delete $mh{devH}->{lastMsg};
  2154. }
  2155. CUL_HM_calcDisWm($mh{cHash},$mh{devN},$type);
  2156. if (AttrVal($btnName,"aesCommReq",0)){
  2157. my @arr = ();
  2158. $mh{devH}->{cmdStacAESPend} = \@arr;
  2159. push (@{$mh{devH}->{cmdStacAESPend} },"$mh{src};++A011$mh{id}$mh{src}$_")
  2160. foreach (@{$mh{cHash}->{helper}{disp}{$type}});
  2161. }
  2162. else{
  2163. CUL_HM_PushCmdStack($mh{devH},"++A011$mh{id}$mh{src}$_")
  2164. foreach (@{$mh{cHash}->{helper}{disp}{$type}});
  2165. }
  2166. }
  2167. }
  2168. if($mh{md} eq "HM-Dis-EP-WM55"){
  2169. my $disName = InternalVal($mh{devN},"channel_03",undef);
  2170. if (defined $disName ){
  2171. if (AttrVal($disName,"param","") =~ m/reWriteDisplay(..)/){
  2172. my $delay = $1;
  2173. RemoveInternalTimer($disName.":reWriteDisplay");
  2174. InternalTimer($mh{rectm}+$delay,"CUL_HM_reWriteDisplay", $disName.":reWriteDisplay", 0);
  2175. }
  2176. }
  2177. }
  2178. else{# could be an Em8
  2179. my($chn,$state,$err);
  2180. if($mh{mTp} eq "41"){
  2181. ($chn,$state)=(hex($mI[0]),$mI[2]);
  2182. #my $cnt = hex($mI[1]);
  2183. my $err = $chn & 0x80;
  2184. $chn = sprintf("%02X",$chn & 0x3f);
  2185. $mh{shash} = $modules{CUL_HM}{defptr}{"$mh{src}$chn"}
  2186. if($modules{CUL_HM}{defptr}{"$mh{src}$chn"});
  2187. push @evtEt,[$mh{devH},1,"battery:". ($err?"low" :"ok" )];
  2188. }
  2189. elsif(($mh{mTp} eq "10" && $mI[0] eq "06") ||
  2190. ($mh{mTp} eq "02" && $mI[0] eq "01")) {
  2191. ($chn,$state,$err) = (hex($mI[1]), $mI[2], hex($mI[3]));
  2192. $chn = sprintf("%02X",$chn&0x3f);
  2193. $mh{shash} = $modules{CUL_HM}{defptr}{"$mh{src}$chn"}
  2194. if($modules{CUL_HM}{defptr}{"$mh{src}$chn"});
  2195. push @evtEt,[$mh{devH},1,"alive:yes"];
  2196. push @evtEt,[$mh{devH},1,"battery:". (($err&0x80)?"low" :"ok" )];
  2197. }
  2198. if (defined($state) && $chn ne "00"){# if state was detected post events
  2199. my $txt;
  2200. if ($mh{shash}->{helper}{lm} && $mh{shash}->{helper}{lm}{hex($state)}){$txt = $mh{shash}->{helper}{lm}{hex($state)}}
  2201. elsif ($lvlStr{md}{$mh{md}}){$txt = $lvlStr{md}{$mh{md}}{$state}}
  2202. elsif ($lvlStr{st}{$mh{st}}){$txt = $lvlStr{st}{$mh{st}}{$state}}
  2203. else {$txt = "unknown:$state"}
  2204. push @evtEt,[$mh{shash},1,"state:$txt"];
  2205. push @evtEt,[$mh{shash},1,"contact:$txt$target"];
  2206. }
  2207. }
  2208. }
  2209. elsif($mh{st} =~ m/^(siren)$/) {#############################################
  2210. if (($mh{mTyp} eq "0201") || # handle Ack_Status
  2211. ($mh{mTyp} eq "1006")) { # or Info_Status message here
  2212. my ($chn,$val,$err) = (hex($mI[1]),hex($mI[2])/2,hex($mI[3]));
  2213. my $vs = $val == 0 ? "off" : "on";
  2214. #-- if timed on is set possibly show this in a state --
  2215. my ($timedOn,$stateExt)=("off","");
  2216. if($err&0x40){
  2217. $timedOn = "running";
  2218. $stateExt = "-till" if(AttrVal($mh{cName},"param","") =~ m/showTimed/ );
  2219. }
  2220. if ($chn == 4){
  2221. my %lvlSet = ("00"=>"disarmed","32"=>"armExtSens","C8"=>"armAll","FF"=>"armBlocked");
  2222. $vs = defined $lvlSet{$val}?$lvlSet{$val}:$val;
  2223. }
  2224. push @evtEt,[$mh{cHash},1,"level:$val"];
  2225. push @evtEt,[$mh{cHash},1,"pct:$val"]; # duplicate to level - necessary for "slider"
  2226. push @evtEt,[$mh{cHash},1,"deviceMsg:$vs$target"] if($mh{chnM} ne "00");
  2227. push @evtEt,[$mh{cHash},1,"state:$vs$stateExt"];
  2228. push @evtEt,[$mh{cHash},1,"timedOn:$timedOn"];
  2229. push @evtEt,[$mh{devH} ,1,"powerOn:$mh{tmStr}",] if ($chn == 0) ;
  2230. push @evtEt,[$mh{devH} ,1,"sabotageError:".(($err&0x04)?"on" :"off")];
  2231. push @evtEt,[$mh{devH} ,1,"battery:" .(($err&0x80)?"low":"ok" )];
  2232. }
  2233. }
  2234. elsif($mh{st} eq "senBright") { #############################################
  2235. if ($mh{mTp} =~ m/^5[34]/){
  2236. #Channel is fixed 1
  2237. my ($chn,$unkn,$dat) = unpack 'A2A2A8',$mh{p};# chn = 01
  2238. push @evtEt,[$mh{devH},1,"battery:".(hex($chn)&0x80?"low":"ok")];
  2239. $dat = sprintf("%0.2f",hex($dat))/100; #down to 0.01lux per docu
  2240. # verify whether we have a channel or will use the Device instead
  2241. my $cHash = ($modules{CUL_HM}{defptr}{$mh{src}."01"})
  2242. ?$modules{CUL_HM}{defptr}{$mh{src}."01"}
  2243. :$mh{devH};
  2244. push @evtEt,[$cHash,1,"state:B: $dat"];
  2245. push @evtEt,[$cHash,1,"brightness:$dat"];
  2246. #push @evtEt,[$cHash,1,"unknown: 0x".$unkn]; # read 0xC1, but what is it?
  2247. }
  2248. }
  2249. elsif($mh{st} eq "powerSensor") {############################################
  2250. if (($mh{mTyp} eq "0201") || # handle Ack_Status
  2251. ($mh{mTyp} eq "1006")) { # or Info_Status message here
  2252. my ($chn,$val,$err) = (hex($mI[1]),hex($mI[2])/2,hex($mI[3]));
  2253. $chn = sprintf("%02X",$chn&0x3f);
  2254. my $chId = $mh{src}.$chn;
  2255. $mh{shash} = $modules{CUL_HM}{defptr}{$chId}
  2256. if($modules{CUL_HM}{defptr}{$chId});
  2257. push @evtEt,[$mh{devH},1,"battery:".(($err&0x80)?"low" :"ok" )];
  2258. push @evtEt,[$mh{shash},1,"state:$val"];
  2259. }
  2260. elsif ($mh{mTp} eq "60" ||$mh{mTp} eq "61" ) { # IEC_POWER_EVENT_CYCLIC
  2261. my ($chn,$eUnit,$eCnt,$pUnit,$pIEC) = map{hex($_)} unpack 'A2A2A10A2A8',$mh{p};
  2262. push @evtEt,[$mh{devH},1,"battery:".(($chn&0x80)?"low" :"ok" )];
  2263. $chn = sprintf("%02X",$chn&0x3f);
  2264. my $chId = $mh{src}.$chn;
  2265. $mh{shash} = $modules{CUL_HM}{defptr}{$chId}
  2266. if($modules{CUL_HM}{defptr}{$chId});
  2267. # push @evtEt,[$mh{shash},1,"energyTariff:" .(( $eUnit & 0xfe)?(-1*($eUnit >> 4)):($eUnit >> 4))];
  2268. # push @evtEt,[$mh{shash},1,"powerTariff:" .((($pUnit >> 4) & 0xfe)?(-1*($pUnit >> 4)):($pUnit >> 4))];
  2269. push @evtEt,[$mh{shash},1,"energyTariff:" .( $eUnit >> 4 )];
  2270. push @evtEt,[$mh{shash},1,"energyUnit:" .( $eUnit & 0x01)];
  2271. push @evtEt,[$mh{shash},1,"powerTariff:" .( $pUnit >> 4 )];
  2272. push @evtEt,[$mh{shash},1,"powerUnit:" .( $pUnit & 0x01)];
  2273. push @evtEt,[$mh{shash},1,"powerSign:" .(( $pUnit >> 3) & 0x01)];
  2274. push @evtEt,[$mh{shash},1,"powerIEC:" .( $pIEC )];
  2275. push @evtEt,[$mh{shash},1,"energyIEC:" .( $eCnt )];
  2276. }
  2277. elsif ($mh{mTp} eq "53" ||$mh{mTp} eq "54" ) { # Gas_EVENT_CYCLIC
  2278. $mh{shash} = $modules{CUL_HM}{defptr}{$mh{src}."01"}
  2279. if($modules{CUL_HM}{defptr}{$mh{src}."01"});
  2280. my ($eCnt,$P) = map{hex($_)} unpack 'A8A6',$mh{p};
  2281. $eCnt = ($eCnt&0x7fffffff)/1000; #0.0 ..2147483.647 m3
  2282. $P = $P /1000; #0.0 ..16777.215 m3
  2283. push @evtEt,[$mh{shash},1,"gasCnt:" .$eCnt];
  2284. push @evtEt,[$mh{shash},1,"gasPower:" .$P];
  2285. my $sumState = "eState:E: $eCnt P: $P";
  2286. push @evtEt,[$mh{shash},1,$sumState];
  2287. push @evtEt,[$mh{shash},1,"boot:" .(($eCnt&0x800000)?"on":"off")];
  2288. my $eo = ReadingsVal($mh{shash}->{NAME},"gasCntOffset",0);
  2289. if($eCnt == 0 && hex($mh{mNo}) < 3 ){
  2290. if($mh{devH}->{helper}{PONtest}){
  2291. push @evtEt,[$mh{devH},1,"powerOn:$mh{tmStr}",] ;
  2292. $mh{devH}->{helper}{PONtest} = 0;
  2293. }
  2294. $eo += ReadingsVal($mh{shash}->{NAME},"gasCnt",0);
  2295. push @evtEt,[$mh{shash},1,"gasCntOffset:".$eo];
  2296. }
  2297. push @evtEt,[$mh{shash},1,"gasCntCalc:".($eo + $eCnt)];
  2298. }
  2299. elsif ($mh{mTp} eq "5E" ||$mh{mTp} eq "5F" ) { # POWER_EVENT_CYCLIC
  2300. $mh{shash} = $modules{CUL_HM}{defptr}{$mh{src}."01"}
  2301. if($modules{CUL_HM}{defptr}{$mh{src}."01"});
  2302. my ($eCnt,$P) = map{hex($_)} unpack 'A6A6',$mh{p};
  2303. $eCnt = ($eCnt&0x7fffff)/10; #0.0 ..838860.7 Wh
  2304. $P = $P /100; #0.0 ..167772.15 W
  2305. push @evtEt,[$mh{shash},1,"energy:" .$eCnt];
  2306. push @evtEt,[$mh{shash},1,"power:" .$P];
  2307. my $sumState = "eState:E: $eCnt P: $P";
  2308. push @evtEt,[$mh{shash},1,$sumState];
  2309. push @evtEt,[$mh{shash},1,"boot:" .(($eCnt&0x800000)?"on":"off")];
  2310. push @evtEt,[$defs{$mh{devH}->{channel_02}},1,"state:$eCnt"] if ($mh{devH}->{channel_02});
  2311. push @evtEt,[$defs{$mh{devH}->{channel_03}},1,"state:$P" ] if ($mh{devH}->{channel_03});
  2312. my $el = ReadingsVal($mh{shash}->{NAME},"energy",0);# get Energy last
  2313. my $eo = ReadingsVal($mh{shash}->{NAME},"energyOffset",0);
  2314. if($eCnt == 0 && hex($mh{mNo}) < 3 && !$mh{shash}->{helper}{pon}){
  2315. if($mh{devH}->{helper}{PONtest}){
  2316. push @evtEt,[$mh{devH},1,"powerOn:$mh{tmStr}",] ;
  2317. $mh{devH}->{helper}{PONtest} = 0;
  2318. }
  2319. $eo += $el;
  2320. push @evtEt,[$mh{shash},1,"energyOffset:".$eo];
  2321. $mh{shash}->{helper}{pon} = 1;# power on is detected - only send once
  2322. }
  2323. elsif($el > 800000 && $el > $eCnt ){# handle overflow
  2324. $eo += 838860.7;
  2325. push @evtEt,[$mh{shash},1,"energyOffset:".$eo];
  2326. }
  2327. else{
  2328. delete $mh{shash}->{helper}{pon};
  2329. }
  2330. push @evtEt,[$mh{shash},1,"energyCalc:".($eo + $eCnt)];
  2331. }
  2332. }
  2333. elsif($mh{st} eq "powerMeter") {#############################################
  2334. if (($mh{mTyp} eq "0201") || # handle Ack_Status
  2335. ($mh{mTyp} eq "1006")) { # or Info_Status message here
  2336. # powerOn
  2337. # m:01 A45F 36D06A 123ABC 8000000000000000090CFE
  2338. # m:02 A410 36D06A 123ABC 06010000
  2339. my ($val,$err) = (hex($mI[2])/2,hex($mI[3]));
  2340. my $chId = $mh{src}.$mh{chnHx};
  2341. $mh{shash} = $modules{CUL_HM}{defptr}{$chId}
  2342. if($modules{CUL_HM}{defptr}{$chId});
  2343. my $vs = ($val==100 ? "on":($val==0 ? "off":"$val %")); # user string...
  2344. #-- if timed on is set possibly show this in a state --
  2345. my ($timedOn,$stateExt)=("off","");
  2346. if($err&0x40){
  2347. $timedOn = "running";
  2348. $stateExt = "-till" if(AttrVal($mh{shash}->{NAME},"param","") =~ m/showTimed/ );
  2349. }
  2350. push @evtEt,[$mh{shash},1,"level:$val"];
  2351. push @evtEt,[$mh{shash},1,"pct:$val"]; # duplicate to level - necessary for "slider"
  2352. push @evtEt,[$mh{shash},1,"deviceMsg:$vs$target"] if($mh{chnHx} ne "00");
  2353. push @evtEt,[$mh{shash},1,"state:$vs$stateExt"];
  2354. push @evtEt,[$mh{shash},1,"timedOn:$timedOn"];
  2355. }
  2356. elsif ($mh{mTp} eq "5E" ||$mh{mTp} eq "5F" ) { # POWER_EVENT_CYCLIC
  2357. $mh{shash} = $modules{CUL_HM}{defptr}{$mh{src}."02"}
  2358. if($modules{CUL_HM}{defptr}{$mh{src}."02"});
  2359. my ($eCnt,$P,$I,$U,$F) = map{hex($_)} unpack 'A6A6A4A4A2',$mh{p};
  2360. $eCnt = ($eCnt&0x7fffff)/10; #0.0 ..838860.7 Wh
  2361. $P = $P /100; #0.0 ..167772.15 W
  2362. $I = $I /1; #0.0 ..65535.0 mA
  2363. $U = $U /10; #0.0 ..6553.5 mV
  2364. $F -= 256 if ($F > 127);
  2365. $F = $F/100+50; # 48.72..51.27 Hz
  2366. push @evtEt,[$mh{shash},1,"energy:" .$eCnt];
  2367. push @evtEt,[$mh{shash},1,"power:" .$P];
  2368. push @evtEt,[$mh{shash},1,"current:" .$I];
  2369. push @evtEt,[$mh{shash},1,"voltage:" .$U];
  2370. push @evtEt,[$mh{shash},1,"frequency:".$F];
  2371. push @evtEt,[$mh{shash},1,"eState:E: $eCnt P: $P I: $I U: $U f: $F"];
  2372. push @evtEt,[$mh{shash},1,"boot:" .(($eCnt&0x800000)?"on":"off")];
  2373. push @evtEt,[$defs{$mh{devH}->{channel_02}},1,"state:$eCnt"] if ($mh{devH}->{channel_02});
  2374. push @evtEt,[$defs{$mh{devH}->{channel_03}},1,"state:$P" ] if ($mh{devH}->{channel_03});
  2375. push @evtEt,[$defs{$mh{devH}->{channel_04}},1,"state:$I" ] if ($mh{devH}->{channel_04});
  2376. push @evtEt,[$defs{$mh{devH}->{channel_05}},1,"state:$U" ] if ($mh{devH}->{channel_05});
  2377. push @evtEt,[$defs{$mh{devH}->{channel_06}},1,"state:$F" ] if ($mh{devH}->{channel_06});
  2378. my $el = ReadingsVal($mh{shash}->{NAME},"energy",0);# get Energy last
  2379. my $eo = ReadingsVal($mh{shash}->{NAME},"energyOffset",0);
  2380. if($eCnt == 0 && hex($mh{mNo}) < 3 && !$mh{shash}->{helper}{pon}){
  2381. if($mh{devH}->{helper}{PONtest}){
  2382. push @evtEt,[$mh{devH},1,"powerOn:$mh{tmStr}",] if ($mh{md} !~ m/^HM-ES-PMSw1/);
  2383. $mh{devH}->{helper}{PONtest} = 0;
  2384. }
  2385. $eo += $el;
  2386. push @evtEt,[$mh{shash},1,"energyOffset:".$eo];
  2387. $mh{shash}->{helper}{pon} = 1;# power on is detected - only ssend once
  2388. }
  2389. elsif($el > 800000 && $el > $eCnt ){# handle overflow
  2390. $eo += 838860.7;
  2391. push @evtEt,[$mh{shash},1,"energyOffset:".$eo];
  2392. }
  2393. else{
  2394. delete $mh{shash}->{helper}{pon};
  2395. }
  2396. push @evtEt,[$mh{shash},1,"energyCalc:".($eo + $eCnt)];
  2397. CUL_HM_unQEntity($mh{shash}->{NAME},"qReqStat");
  2398. }
  2399. }
  2400. elsif($mh{st} eq "repeater"){ ###############################################
  2401. if (($mh{mTyp} eq "0201") || # handle Ack_Status
  2402. ($mh{mTyp} eq "1006")) { #or Info_Status message here
  2403. my ($state,$err);
  2404. ($state,$err) = ($1,hex($2)) if ($mh{p} =~ m/^....(..)(..)/);
  2405. # not sure what level are possible
  2406. push @evtEt,[$mh{cHash},1,"state:" .($state eq '00'?"ok":"level:".$state)];
  2407. push @evtEt,[$mh{devH} ,1,"battery:".(($err&0x80)?"low" :"ok" )];
  2408. my $flag = ($err>>4) &0x7;
  2409. push @evtEt,[$mh{cHash},1,"flags:" .(($flag)?"none" :$flag )];
  2410. }
  2411. }
  2412. elsif($mh{st} eq "virtual" && $mh{md} =~ m/^virtual_/){ #####################
  2413. # possibly add code to count all acks that are paired.
  2414. if($mh{mTp} eq "02") {# this must be a reflection from what we sent, ignore
  2415. push @evtEt,[$mh{shash},1,""];
  2416. }
  2417. elsif ($mh{mTp} =~ m/^4[01]/){# if channel is SD team we have to act
  2418. if ($mh{cHash}->{helper}{fkt} && $mh{cHash}->{helper}{fkt} eq "sdLead2"){
  2419. CUL_HM_parseSDteam_2($mh{mTp},$mh{src},$mh{dst},$mh{p});
  2420. }
  2421. else{
  2422. CUL_HM_parseSDteam($mh{mTp},$mh{src},$mh{dst},$mh{p});
  2423. }
  2424. }
  2425. }
  2426. elsif($mh{st} eq "outputUnit"){ #############################################
  2427. if($mh{mTp} eq "40" && @mI == 2){
  2428. my $bno = hex($mI[1]);
  2429. push @evtEt,[$mh{cHash},1,"state:Btn$mh{chn} on$target"];
  2430. }
  2431. elsif(($mh{mTyp} eq "0201") || # handle Ack_Status
  2432. ($mh{mTyp} eq "1006")){ # or Info_Status message
  2433. my $msgState = (@mI > 2 ? $mI[2] : "" );
  2434. if ($mh{md} eq "HM-OU-LED16") {
  2435. #special: all LEDs map to device state
  2436. my $devState = ReadingsVal($mh{devN},"color","00000000");
  2437. if($parse eq "powerOn"){# reset LEDs after power on
  2438. CUL_HM_PushCmdStack($mh{devH},'++A011'.$ioId.$mh{src}."8100".$devState);
  2439. CUL_HM_ProcessCmdStack($mh{devH});
  2440. # no event necessary, all the same as before
  2441. }
  2442. else {# just update datafields in storage
  2443. my %colTbl=("00"=>"off","01"=>"red","10"=>"green","11"=>"orange");
  2444. if (@mI > 8){#status for all channel included
  2445. # open to decode byte $mI[4] - related to backlight? seen 20 and 21
  2446. my $lStat = join("",@mI[5..8]); # all LED status in one long
  2447. my @leds = reverse(unpack('(A2)*',sprintf("%032b",hex($lStat))));
  2448. $_ = $colTbl{$_} foreach (@leds);
  2449. for(my $cCnt = 0;$cCnt<16;$cCnt++){# go for all channels
  2450. my $cH = $modules{CUL_HM}{defptr}{$mh{src}.sprintf("%02X",$cCnt+1)};
  2451. next if (!$cH);
  2452. if (ReadingsVal($cH->{NAME},"state","") ne $leds[$cCnt]) {
  2453. push @evtEt,[$cH,1,"color:$leds[$cCnt]"];
  2454. push @evtEt,[$cH,1,"state:$leds[$cCnt]"];
  2455. }
  2456. }
  2457. push @evtEt,[$mh{devH},1,"color:$lStat"];
  2458. push @evtEt,[$mh{devH},1,"state:$lStat"];
  2459. }
  2460. else{# branch can be removed if message is always that long
  2461. my $bitLoc = ($mh{chn}-1)*2;#calculate bit location
  2462. my $mask = 3<<$bitLoc;
  2463. my $value = sprintf("%08X",(hex($devState) &~$mask)|($msgState<<$bitLoc));
  2464. push @evtEt,[$mh{devH},1,"color:$value"];
  2465. push @evtEt,[$mh{devH},1,"state:$value"];
  2466. if (!$mh{cHash}{helper}{role}{dev}){
  2467. my $actColor = $colTbl{sprintf("%02b",hex($msgState))};
  2468. $actColor = "unknown" if(!$actColor);
  2469. push @evtEt,[$mh{cHash},1,"color:$actColor"];
  2470. push @evtEt,[$mh{cHash},1,"state:$actColor"];
  2471. }
  2472. }
  2473. }
  2474. }
  2475. # elsif ($mh{md} eq "HM-OU-CFM-PL"){
  2476. else{
  2477. my $val = hex($mI[2])/2;
  2478. $val = ($val == 100 ? "on" : ($val == 0 ? "off" : "$val %"));
  2479. push @evtEt,[$mh{cHash},1,"state:$val"];
  2480. push @evtEt,[$mh{devH} ,1,"battery:".(hex($mI[3]&0x80)?"low":"ok" )]if ($mh{md} eq "HM-OU-CFM-TW" && $mI[3]);
  2481. }
  2482. }
  2483. }
  2484. elsif($mh{st} =~ m/^(motionDetector|motionAndBtn)$/) { ######################
  2485. my $state = $mI[2];
  2486. if(($mh{mTyp} eq "0201") ||
  2487. ($mh{mTyp} eq "1006")) {
  2488. my ($chn,$err,$bright)=(hex($mI[1]),hex($mI[3]),hex($mI[2]));
  2489. my $chId = $mh{src}.sprintf("%02X",$chn&0x3f);
  2490. $mh{shash} = $modules{CUL_HM}{defptr}{$chId}
  2491. if($modules{CUL_HM}{defptr}{$chId});
  2492. push @evtEt,[$mh{shash},1,"brightness:".$bright];
  2493. if ($mh{md} eq "HM-Sec-MDIR"){
  2494. push @evtEt,[$mh{shash},1,"sabotageError:".(($err&0x0E)?"on":"off")];
  2495. }
  2496. else{
  2497. push @evtEt,[$mh{shash},1,"cover:". (($err&0x0E)?"open" :"closed")];
  2498. }
  2499. push @evtEt,[$mh{devH},1,"battery:". (($err&0x80)?"low" :"ok" )];
  2500. }
  2501. elsif($mh{mTp} eq "41") {#01 is channel
  2502. my($chn,$cnt,$bright,$nextTr) = map{hex($_)} (@mI,0);
  2503. push @evtEt,[$mh{devH},1,"battery:".($chn&0x80?"low":"ok")]; # observed with HM-SEN-MDIR-SM FW V1.6
  2504. if ($nextTr){
  2505. $nextTr = (15 << ($nextTr >> 4) - 4); # strange mapping of literals
  2506. RemoveInternalTimer($mh{cName}.":motionCheck");
  2507. InternalTimer($mh{rectm}+$nextTr+2,"CUL_HM_motionCheck", $mh{cName}.":motionCheck", 0);
  2508. $mh{cHash}->{helper}{moStart} = $mh{rectm} if (!defined $mh{cHash}->{helper}{moStart});
  2509. }
  2510. else{
  2511. $nextTr = "none ";
  2512. }
  2513. my $chId = $mh{src}.sprintf("%02X",$chn & 0x3f);
  2514. $mh{shash} = $modules{CUL_HM}{defptr}{$chId}
  2515. if($modules{CUL_HM}{defptr}{$chId});
  2516. push @evtEt,[$mh{shash},1,"state:motion"];
  2517. push @evtEt,[$mh{shash},1,"motion:on$target"];
  2518. push @evtEt,[$mh{shash},1,"motionCount:$cnt"."_next:$nextTr"."s"];
  2519. push @evtEt,[$mh{shash},1,"brightness:$bright"];
  2520. }
  2521. elsif($mh{mTp} eq "70" && $mh{p} =~ m/^7F(..)(.*)/) {
  2522. my($d1, $d2) = ($1, $2);
  2523. push @evtEt,[$mh{shash},1,"devState_raw$d1:$d2"];
  2524. $state = 0;
  2525. }
  2526. if($ioId eq $mh{dst} && $mh{mFlgH}&0x20 && $state){
  2527. push @ack,$mh{shash},$mh{mNo}."8002".$ioId.$mh{src}."0101".$state."00";
  2528. $mh{AckDone} = 1; # mark allready done device specific
  2529. }
  2530. }
  2531. elsif($mh{st} eq "smokeDetector") { #########################################
  2532. #Info Level: mTp=0x10 p(..)(..)(..) subtype=06, channel, state (1 byte)
  2533. #Event: mTp=0x41 p(..)(..)(..) channel , unknown, state (1 byte)
  2534. if ($mh{mTp} eq "10" && $mh{p} =~ m/^06..(..)(..)/) {
  2535. # m:A0 A010 233FCE 1743BF 0601 01 00 31
  2536. my ($state,$err) = (hex($1),hex($2));
  2537. push @evtEt,[$mh{devH} ,1,"battery:" .(($err&0x80)?"low" :"ok")];
  2538. push @evtEt,[$mh{cHash},1,"level:" .hex($state)];
  2539. $state = (($state < 2)?"off":"smoke-Alarm");
  2540. push @evtEt,[$mh{cHash},1,"state:$state"];
  2541. if ($mh{md} eq "HM-SEC-SD-2"){
  2542. push @evtEt,[$mh{cHash},1,"alarmTest:" .(($err&0x02)?"failed" :"ok")];
  2543. push @evtEt,[$mh{cHash},1,"smokeChamber:".(($err&0x04)?"degraded":"ok")];
  2544. if(length($mh{p}) == 8 && $mh{mNo} eq "80"){
  2545. push @evtEt,[$mh{devH},1,"powerOn:$mh{tmStr}",] ;
  2546. }
  2547. CUL_HM_parseSDteam_2($mh{mTp},$mh{src},$mh{dst},$mh{p});
  2548. }
  2549. else{
  2550. if($mh{devH}->{helper}{PONtest} &&(length($mh{p}) == 8 && $mh{mNo} eq "00")){
  2551. push @evtEt,[$mh{devH},1,"powerOn:$mh{tmStr}",] ;
  2552. $mh{devH}->{helper}{PONtest} = 0;
  2553. }
  2554. }
  2555. my $tName = ReadingsVal($mh{cName},"peerList","");#inform team
  2556. $tName =~ s/,.*//;
  2557. CUL_HM_updtSDTeam($tName,$mh{cName},$state);
  2558. }
  2559. elsif ($mh{mTp} =~ m/^4[01]/){ #autonomous event
  2560. #01 1441 44E347 44E347 0101960000048BAF3B0E
  2561. #02 1441 44E347 44E347 01020000000445C4A14C
  2562. if ($mh{md} eq "HM-SEC-SD-2"){
  2563. CUL_HM_parseSDteam_2($mh{mTp},$mh{src},$mh{dst},$mh{p});
  2564. }
  2565. else{
  2566. CUL_HM_parseSDteam($mh{mTp},$mh{src},$mh{dst},$mh{p});
  2567. }
  2568. }
  2569. elsif ($mh{mTp} eq "01"){ #Configs
  2570. my $sType = substr($mh{p},0,2);
  2571. if ($sType eq "01"){# add peer to group
  2572. push @evtEt,[$mh{shash},1,"SDteam:add_$mh{dstN}"];
  2573. }
  2574. elsif($sType eq "02"){# remove from group
  2575. push @evtEt,[$mh{shash},1,"SDteam:remove_".$mh{dstN}];
  2576. }
  2577. elsif($sType eq "05"){# set param List 3 and 4
  2578. push @evtEt,[$mh{shash},1,""];
  2579. }
  2580. }
  2581. else{
  2582. push @evtEt,[$mh{shash},1,"SDunknownMsg:$mh{p}"] if(!@evtEt);
  2583. }
  2584. if($ioId eq $mh{dst} && ($mh{mFlgH}&0x20)){ # Send Ack/Nack
  2585. push @ack,$mh{shash},$mh{mNo}."8002".$ioId.$mh{src}.($mh{mFlg}.$mh{mTp} eq "A001" ? "80":"00");
  2586. }
  2587. }
  2588. elsif($mh{st} eq "threeStateSensor") { ######################################
  2589. #Event: mTp=0x41 p(..)(..)(..) channel , unknown, state
  2590. #Info Level: mTp=0x10 p(..)(..)(..)(..) subty=06, chn, state,err (3bit)
  2591. #AckStatus: mTp=0x02 p(..)(..)(..)(..) subty=01, chn, state,err (3bit)
  2592. my ($chn,$state,$err,$cnt); #define locals
  2593. if(($mh{mTyp} eq "0201") ||
  2594. ($mh{mTyp} eq "1006")) {
  2595. ($chn,$state,$err) = (hex($mI[1]), $mI[2], hex($mI[3]));
  2596. $chn = sprintf("%02X",$chn&0x3f);
  2597. $mh{shash} = $modules{CUL_HM}{defptr}{"$mh{src}$chn"}
  2598. if($modules{CUL_HM}{defptr}{"$mh{src}$chn"});
  2599. push @evtEt,[$mh{devH},1,"alive:yes"];
  2600. push @evtEt,[$mh{devH},1,"battery:". (($err&0x80)?"low" :"ok" )];
  2601. if ( $mh{md} =~ m/^(HM-SEC-SC.*|HM-SEC-RHS|Roto_ZEL-STG-RM-F.K)$/){
  2602. push @evtEt,[$mh{devH},1,"sabotageError:".(($err&0x0E)?"on" :"off")];}
  2603. elsif($mh{md} ne "HM-SEC-WDS"){push @evtEt,[$mh{devH},1,"cover:" .(($err&0x0E)?"open" :"closed")];}
  2604. }
  2605. elsif($mh{mTp} eq "41"){
  2606. ($chn,$cnt,$state)=(hex($1),$2,$3) if($mh{p} =~ m/^(..)(..)(..)/);
  2607. my $err = $chn & 0x80;
  2608. $chn = sprintf("%02X",$chn & 0x3f);
  2609. $mh{shash} = $modules{CUL_HM}{defptr}{"$mh{src}$chn"}
  2610. if($modules{CUL_HM}{defptr}{"$mh{src}$chn"});
  2611. push @evtEt,[$mh{devH},1,"battery:". ($err?"low" :"ok" )];
  2612. push @ack,$mh{shash},$mh{mNo}."8002".$mh{dst}.$mh{src}."00"
  2613. if ( $ioId eq $mh{dst}
  2614. && !$mh{devH}{IODev}->{helper}{VTS_ACK}
  2615. && $mh{devH}{IODev}->{TYPE} !~ m/^(HMLAN|HMUARTLGW)$/); #noansi: additional CUL ACK
  2616. }
  2617. if (defined($state)){# if state was detected post events
  2618. my $txt;
  2619. if ($mh{shash}->{helper}{lm} && $mh{shash}->{helper}{lm}{hex($state)}){$txt = $mh{shash}->{helper}{lm}{hex($state)}}
  2620. elsif ($lvlStr{md}{$mh{md}}){$txt = $lvlStr{md}{$mh{md}}{$state}}
  2621. elsif ($lvlStr{st}{$mh{st}}){$txt = $lvlStr{st}{$mh{st}}{$state}}
  2622. else {$txt = "unknown:$state"}
  2623. push @evtEt,[$mh{shash},1,"state:$txt"];
  2624. push @evtEt,[$mh{shash},1,"contact:$txt$target"];
  2625. }
  2626. elsif(!@evtEt){push @evtEt,[$mh{devH},1,"3SSunknownMsg:$mh{p}"];}
  2627. }
  2628. elsif($mh{st} eq "winMatic") { #############################################
  2629. my($sType,$chn,$lvl,$stat) = @mI;
  2630. if(($mh{mTyp} eq "0201") ||
  2631. ($mh{mTyp} eq "1006")){
  2632. $stat = hex($stat);
  2633. $mh{shash} = $modules{CUL_HM}{defptr}{"$mh{src}$chn"}
  2634. if($modules{CUL_HM}{defptr}{"$mh{src}$chn"});
  2635. my $lvlS = $lvl eq "FF" ? 1:0;
  2636. $lvl = hex($lvl)/2;
  2637. my $dat4 = ($stat >> 4) & 0x03;
  2638. if ($chn eq "01"){
  2639. my %err = (0=>"ok",1=>"TurnError",2=>"TiltError");
  2640. my %dir = (0=>"no",1=>"up",2=>"down",3=>"undefined");
  2641. push @evtEt,[$mh{shash},1,"motorErr:" .$err{($stat >> 1) & 0x03}];
  2642. push @evtEt,[$mh{shash},1,"direction:" .$dir{$dat4}];
  2643. push @evtEt,[$mh{shash},1,"level:" .($lvlS ? "0" : $lvl) ] if($dat4 == 0);
  2644. push @evtEt,[$mh{shash},1,"lock:" .($lvlS ? "locked" : "unlocked")];
  2645. }
  2646. else{ #should be akku
  2647. my %statF = (0=>"trickleCharge",1=>"charge",2=>"discharge",3=>"unknown");
  2648. push @evtEt,[$mh{shash},1,"charge:" .$statF{$dat4}];
  2649. }
  2650. # stateflag meaning unknown
  2651. push @evtEt,[$mh{shash},1,"state:".($lvlS ? "locked" : $lvl) ];
  2652. }
  2653. }
  2654. elsif($mh{st} eq "keyMatic") { #############################################
  2655. #Info Level: mTp=0x10 p(..)(..)(..)(..) subty=06, chn, state,err (3bit)
  2656. #AckStatus: mTp=0x02 p(..)(..)(..)(..) subty=01, chn, state,err (3bit)
  2657. if(($mh{mTyp} eq "1006") ||
  2658. ($mh{mTyp} eq "0201")) {
  2659. my ($chn,$val, $err) = ($mI[1],hex($mI[2]), hex($mI[3]));
  2660. $mh{shash} = $modules{CUL_HM}{defptr}{"$mh{src}$chn"}
  2661. if($modules{CUL_HM}{defptr}{"$mh{src}$chn"});
  2662. my $stErr = ($err >>1) & 0x7;
  2663. my $error = 'unknown_'.$stErr;
  2664. $error = 'motor aborted' if ($stErr == 2);
  2665. $error = 'clutch failure' if ($stErr == 1);
  2666. $error = 'none' if ($stErr == 0);
  2667. my %dir = (0=>"none",1=>"up",2=>"down",3=>"undef");
  2668. my $state = "";
  2669. RemoveInternalTimer ($mh{devN}.":uncertain:permanent");
  2670. CUL_HM_unQEntity($mh{devN},"qReqStat");
  2671. if ($err & 0x30) { # uncertain - we have to check
  2672. CUL_HM_stateUpdatDly($mh{devN},13) if(ReadingsVal($mh{devN},"uncertain","no") eq "no");
  2673. InternalTimer($mh{rectm}+20,"CUL_HM_readValIfTO", $mh{devN}.":uncertain:permanent", 0);
  2674. $state = " (uncertain)";
  2675. }
  2676. push @evtEt,[$mh{shash},1,"unknown:40"] if($err&0x40);
  2677. push @evtEt,[$mh{devH} ,1,"battery:" .(($err&0x80) ? "low":"ok")];
  2678. push @evtEt,[$mh{shash},1,"uncertain:" .(($err&0x30) ? "yes":"no")];
  2679. push @evtEt,[$mh{shash},1,"direction:" .$dir{($err>>4)&3}];
  2680. push @evtEt,[$mh{shash},1,"error:" . ($error)];
  2681. push @evtEt,[$mh{shash},1,"lock:" . (($val == 1) ? "unlocked" : "locked")];
  2682. push @evtEt,[$mh{shash},1,"state:" . (($val == 1) ? "unlocked" : "locked") . $state];
  2683. }
  2684. }
  2685. elsif($mh{md} eq "CCU-FHEM") { #############################################
  2686. push @evtEt,[$mh{shash},1,""];
  2687. }
  2688. elsif (eval "defined(&CUL_HM_Parse$mh{st})"){################################
  2689. no strict "refs";
  2690. my @ret = &{"CUL_HM_Parse$mh{st}"}($mh{mFlg},$mh{mTp},$mh{src},$mh{dst},$mh{p},$target);
  2691. use strict "refs";
  2692. push @evtEt,@ret;
  2693. }
  2694. else{########################################################################
  2695. ; # no one wants the message
  2696. }
  2697. #------------ parse if FHEM or virtual actor is destination ---------------
  2698. if( AttrVal($mh{dstN}, "subType", "none") eq "virtual"
  2699. && AttrVal($mh{dstN}, "model", "none") =~ m/^virtual_/){# see if need for answer
  2700. my $sendAck = 0;
  2701. if($mh{mTp} =~ m/^4/ && @mI > 1) { #Push Button event
  2702. my ($recChn,$trigNo) = (hex($mI[0]),hex($mI[1]));# button number/event count
  2703. my $longPress = ($recChn & 0x40)?"long":"short";
  2704. my $recId = $mh{src}.sprintf("%02X",($recChn&0x3f));
  2705. foreach my $dChId (CUL_HM_getAssChnIds($mh{dstN})){# need to check all chan
  2706. next if (!$modules{CUL_HM}{defptr}{$dChId});
  2707. my $dChNo = substr($dChId,6,2);
  2708. my $dChName = CUL_HM_id2Name($dChId);
  2709. if(($attr{$dChName}{peerIDs}?$attr{$dChName}{peerIDs}:"") =~ m/$recId/){
  2710. my $dChHash = $defs{$dChName};
  2711. $sendAck = 1;
  2712. $dChHash->{helper}{trgLgRpt} = 0
  2713. if (!defined($dChHash->{helper}{trgLgRpt}));
  2714. $dChHash->{helper}{trgLgRpt} +=1;
  2715. my $trgLgRpt = $dChHash->{helper}{trgLgRpt};
  2716. my ($stT,$stAck) = ("ack","00");#state text and state Ack for Msg
  2717. if (AttrVal($dChName,"param","") !~ m/noOnOff/){
  2718. $stT = ReadingsVal($dChName,"virtActState","OFF");
  2719. $stT = ($stT eq "OFF")?"ON":"OFF"
  2720. if ($trigNo ne ReadingsVal($dChName,"virtActTrigNo","0"));
  2721. $stAck = '01'.$dChNo.(($stT eq "ON")?"C8":"00")."00"
  2722. }
  2723. if ((($mh{mFlgH} & 0x24) == 0x20)){
  2724. $longPress .= "_Release";
  2725. $dChHash->{helper}{trgLgRpt}=0;
  2726. push @ack,$mh{shash},$mh{mNo}."8002".$mh{dst}.$mh{src}.$stAck;
  2727. }
  2728. push @evtEt,[$dChHash,1,"state:$stT"];
  2729. push @evtEt,[$dChHash,1,"virtActState:$stT"];
  2730. push @evtEt,[$dChHash,1,"virtActTrigger:".CUL_HM_id2Name($recId)];
  2731. push @evtEt,[$dChHash,1,"virtActTrigType:$longPress"];
  2732. push @evtEt,[$dChHash,1,"virtActTrigRpt:$trgLgRpt"];
  2733. push @evtEt,[$dChHash,1,"virtActTrigNo:$trigNo"];
  2734. }
  2735. }
  2736. }
  2737. elsif($mh{mTp} eq "58" && $mh{p} =~ m/^(..)(..)/) {# climate event
  2738. my ($d1,$vp) =($1,hex($2)); # adjust_command[0..4] adj_data[0..250]
  2739. $vp = int($vp/2.56+0.5); # valve position in %
  2740. my $chnHash = $modules{CUL_HM}{defptr}{$mh{dst}."01"};
  2741. $chnHash = $mh{dstH} if (!$chnHash);
  2742. push @evtEt,[$chnHash,1,"ValvePosition:$vp %"];
  2743. push @evtEt,[$chnHash,1,"ValveAdjCmd:".$d1];
  2744. push @ack,$chnHash,$mh{mNo}."8002".$mh{dst}.$mh{src}.'0101'.
  2745. sprintf("%02X",$vp*2)."0000";
  2746. }
  2747. elsif($mh{mTp} eq "02"){
  2748. if ($mh{dstH}->{helper}{prt}{rspWait}{mNo} &&
  2749. $mh{dstH}->{helper}{prt}{rspWait}{mNo} eq $mh{mNo} ){
  2750. #ack we waited for - stop Waiting
  2751. CUL_HM_respPendRm($mh{dstH});
  2752. }
  2753. }
  2754. else{
  2755. $sendAck = 1;
  2756. }
  2757. push @ack,$mh{dstH},$mh{mNo}."8002".$mh{dst}.$mh{src}."00" if ($mh{mFlgH} & 0x20 && (!@ack) && $sendAck);
  2758. }
  2759. elsif($ioId eq $mh{dst}){# if fhem is destination check if we need to react
  2760. if( $mh{mTp} =~ m/^4./ #Push Button event
  2761. && !$mh{AckDone} #noansi: allready done device specific
  2762. && ($mh{mFlgH} & 0x20)){ #response required Flag
  2763. # fhem CUL shall ack a button press
  2764. if ($mh{md} =~ m/^(HM-SEC-SC.*|Roto_ZEL-STG-RM-FFK)$/){# SCs - depending on FW version - do not accept ACK only. Especially if peered
  2765. push @ack,$mh{shash},$mh{mNo}."8002".$mh{dst}.$mh{src}."0101".((hex($mI[0])&1)?"C8":"00")."00";
  2766. }
  2767. else{
  2768. push @ack,$mh{shash},$mh{mNo}."8002$mh{dst}$mh{src}"."00";
  2769. }
  2770. Log3 $mh{devN},5,"CUL_HM $mh{devN} prep ACK for $mI[0]";
  2771. }
  2772. }
  2773. #------------ send default ACK if not applicable------------------
  2774. # ack if we are destination, anyone did accept the message (@evtEt)
  2775. # parser did not supress
  2776. push @ack,$mh{shash}, $mh{mNo}."8002".$ioId.$mh{src}."00"
  2777. if( ($ioId eq $mh{dst}) #are we adressee
  2778. && ($mh{mFlgH} & 0x20) #response required Flag
  2779. && @evtEt #only ack if we identified it
  2780. && (!@ack) #sender requested ACK
  2781. );
  2782. if (@ack) {# send acks and store for repeat
  2783. $mh{devH}->{helper}{rpt}{IO} = $mh{ioName};
  2784. $mh{devH}->{helper}{rpt}{flg} = substr($mh{msg},5,1);
  2785. $mh{devH}->{helper}{rpt}{ack} = \@ack;
  2786. $mh{devH}->{helper}{rpt}{ts} = $mh{rectm};
  2787. my $i=0;
  2788. my $rr = $respRemoved;
  2789. CUL_HM_SndCmd($ack[$i++],$ack[$i++]
  2790. .($mh{devH}->{helper}{aesAuthBytes}
  2791. ?$mh{devH}->{helper}{aesAuthBytes}
  2792. :"")
  2793. )while ($i<@ack);
  2794. $respRemoved = $rr;
  2795. Log3 $mh{devN},5,"CUL_HM $mh{devN} sent ACK:".(int(@ack));
  2796. delete($mh{devH}->{helper}{aesAuthBytes});
  2797. }
  2798. CUL_HM_ProcessCmdStack($mh{devH}) if ($respRemoved); # cont if complete
  2799. CUL_HM_sndIfOpen("x:".$mh{ioName});
  2800. #------------ process events ------------------
  2801. push @evtEt,[$mh{devH},1,"noReceiver:src:$mh{src} ".$mh{mFlg}.$mh{mTp}." $mh{p}"]
  2802. if(!@entities && !@evtEt);
  2803. push @entities,CUL_HM_pushEvnts();
  2804. @entities = CUL_HM_noDup(@entities,$mh{devN});
  2805. $defs{$_}{".noDispatchVars"} = 1 foreach (grep !/^$mh{devN}$/,@entities);
  2806. return @entities;
  2807. }
  2808. sub CUL_HM_parseCommon(@){#####################################################
  2809. # parsing commands that are device independent
  2810. my ($ioHash,$mhp) = @_;
  2811. return "" if(!$mhp->{devH}{DEF});# this should be from ourself
  2812. my ($p) = $mhp->{p};
  2813. my $devHlpr = $mhp->{devH}{helper};
  2814. my $ret = "";
  2815. my $rspWait = $devHlpr->{prt}{rspWait};
  2816. my $pendType = $rspWait->{Pending} ? $rspWait->{Pending} : "";
  2817. #------------ parse message flag for start processing command Stack
  2818. # TC wakes up with 8270, not with A258
  2819. # VD wakes up with 8202
  2820. # 9610
  2821. my $rxt = CUL_HM_getRxType($mhp->{shash});
  2822. $devHlpr->{PONtest} = 1 if($mhp->{mNo} =~ m/^0[012]/ &&
  2823. $devHlpr->{HM_CMDNR} < 250 &&
  2824. $devHlpr->{HM_CMDNR} > 5);# this is power on
  2825. $devHlpr->{HM_CMDNR} = hex($mhp->{mNo});# sync msgNo prior to any sending
  2826. if($rxt & 0x08){ #wakeup device
  2827. if(($mhp->{mFlgH} & 0xA2) == 0x82){ #wakeup signal
  2828. CUL_HM_appFromQ($mhp->{devN},"wu");# stack cmds if waiting
  2829. if ($mhp->{devH}{cmdStack}){
  2830. CUL_HM_SndCmd($mhp->{devH}, '++A112'.CUL_HM_IoId($mhp->{devH}).$mhp->{src});
  2831. CUL_HM_ProcessCmdStack($mhp->{devH});
  2832. }
  2833. }
  2834. elsif($devHlpr->{prt}{sProc} != 1){ # no wakeup signal,
  2835. # this is an autonom message send ACK but dont process further
  2836. $devHlpr->{prt}{sleeping} = 1 if($mhp->{mFlgH} & 0x20) ;
  2837. }
  2838. }
  2839. if($rxt & 0x10 && $devHlpr->{prt}{sleeping}){ # lazy config
  2840. if($mhp->{mFlgH} & 0x02 #wakeup device
  2841. && $defs{$mhp->{devH}{IODev}{NAME}}{TYPE} =~ m/^(HMLAN|HMUARTLGW)$/){
  2842. $devHlpr->{io}{newCh} = 1 if ($devHlpr->{prt}{sProc} == 2);
  2843. CUL_HM_appFromQ($mhp->{devN},"cf");# stack cmds if waiting
  2844. $devHlpr->{prt}{sleeping} = 0;
  2845. CUL_HM_ProcessCmdStack($mhp->{devH});
  2846. }
  2847. else{
  2848. $devHlpr->{prt}{sleeping} = 1;
  2849. }
  2850. }
  2851. my $repeat;
  2852. $devHlpr->{supp_Pair_Rep} = 0 if ($mhp->{mTp} ne "00"); # noansi: reset pairing suppress flag as we got something different from device
  2853. if ($mhp->{mTp} eq "02"){# Ack/Nack/aesReq ####################
  2854. my $reply;
  2855. my $success;
  2856. if ($devHlpr->{prt}{rspWait}{brstWu}){
  2857. if ($devHlpr->{prt}{rspWait}{mNo} eq $mhp->{mNo} &&
  2858. $mhp->{mStp} eq "00"){
  2859. if ($devHlpr->{prt}{awake} && $devHlpr->{prt}{awake}==4){#re-burstWakeup
  2860. delete $devHlpr->{prt}{rspWait};#clear burst-wakeup values
  2861. $devHlpr->{prt}{rspWait}{$_} = $devHlpr->{prt}{rspWaitSec}{$_}
  2862. foreach (keys%{$devHlpr->{prt}{rspWaitSec}}); #back to original message
  2863. delete $devHlpr->{prt}{rspWaitSec};
  2864. IOWrite($mhp->{devH}, "", $devHlpr->{prt}{rspWait}{cmd}); # and send
  2865. CUL_HM_statCnt($mhp->{devH}{IODev}{NAME},"s",hex(substr($devHlpr->{prt}{rspWait}{cmd},6,2)));
  2866. return "done";
  2867. }
  2868. $mhp->{devH}{protCondBurst} = "on" if ( $mhp->{devH}{protCondBurst}
  2869. && $mhp->{devH}{protCondBurst} !~ m/forced/);
  2870. $devHlpr->{prt}{awake}=2;#awake
  2871. }
  2872. else{
  2873. $mhp->{devH}{protCondBurst} = "off" if ( !$mhp->{devH}{protCondBurst}
  2874. || $mhp->{devH}{protCondBurst} !~ m/forced/);
  2875. $devHlpr->{prt}{awake}=3;#reject
  2876. return "done";
  2877. }
  2878. }
  2879. if (defined($devHlpr->{AESreqAck})) {
  2880. if ($devHlpr->{AESreqAck} eq substr($mhp->{p}, -1 * length($devHlpr->{AESreqAck}))) {
  2881. push @evtEt,[$mhp->{devH},1,"aesCommToDev:ok"];
  2882. }
  2883. else {
  2884. push @evtEt,[$mhp->{devH},1,"aesCommToDev:fail"];
  2885. }
  2886. delete $devHlpr->{AESreqAck};
  2887. }
  2888. if ($mhp->{mStp} =~ m/^8/){#NACK
  2889. #82 : peer not accepted - list full (VD)
  2890. #84 : request undefined register
  2891. #85 : peer not accepted - why? unknown
  2892. $success = "no";
  2893. CUL_HM_eventP($mhp->{devH},"Nack");
  2894. $reply = "NACK";
  2895. }
  2896. elsif($mhp->{mStp} eq "01"){ #ACKinfo#################
  2897. $success = "yes";
  2898. CUL_HM_m_setCh($mhp,substr($mhp->{p},2,2));
  2899. push @evtEt,[$mhp->{cHash},0,"recentStateType:ack"];
  2900. if (length($mhp->{p})>9){
  2901. my $rssi = substr($mhp->{p},8,2);
  2902. CUL_HM_storeRssi( $mhp->{devN}
  2903. ,$mhp->{dstN}
  2904. ,(-1)*(hex($rssi))
  2905. ,$mhp->{mNo})
  2906. if ($rssi && $rssi ne '00' && $rssi ne'80');
  2907. }
  2908. $reply = "ACKStatus";
  2909. if ($devHlpr->{tmdOn}){
  2910. if ((not hex(substr($mhp->{p},6,2))&0x40) && # not timedOn, we have to repeat
  2911. $devHlpr->{tmdOn} eq substr($mhp->{p},2,2) ){# virtual channels for dimmer may be incorrect
  2912. my ($pre,$nbr,$msg) = unpack 'A4A2A*',$devHlpr->{prt}{rspWait}{cmd};
  2913. $devHlpr->{prt}{rspWait}{cmd} = sprintf("%s%02X%s",
  2914. $pre,hex($nbr)+1,$msg);
  2915. CUL_HM_eventP($mhp->{devH},"TimedOn");
  2916. $success = "no";
  2917. $repeat = 1;
  2918. $reply = "NACK";
  2919. }
  2920. }
  2921. }
  2922. elsif($mhp->{mStp} eq "04"){ #ACK-AES, ###############
  2923. my (undef,$challenge,$aesKeyNbr) = unpack'A2A12A2',$mhp->{p};
  2924. push @evtEt,[$mhp->{devH},1,"aesKeyNbr:".$aesKeyNbr] if (defined $aesKeyNbr);# if ($mh{msgStat} =~ m/AESKey/)
  2925. if (AttrVal($mhp->{devH}{IODev}{NAME},"rfmode","") eq "HomeMatic" &&
  2926. defined($aesKeyNbr)) {
  2927. if ($mhp->{devH}{IODev}{TYPE} =~ m/^(TSCUL|TSSTACKED)$/) { #nonsi: for TSCUL
  2928. return "AES"; # noansi: TSCUL did it, now the normal ACK is expected
  2929. }
  2930. if ($cryptFunc == 1 && #AES is available
  2931. $devHlpr->{prt}{rspWait}{cmd}){ #There is a previously executed command
  2932. my (undef, %keys) = CUL_HM_getKeys($mhp->{devH});
  2933. my $kNo = hex($aesKeyNbr) / 2;
  2934. Log3 $mhp->{devH},5,"CUL_HM $mhp->{devN} signing request for $devHlpr->{prt}{rspWait}{cmd} challenge: "
  2935. .$challenge." kNo: ".$kNo;
  2936. if (!defined($keys{$kNo})) {
  2937. Log3 $mhp->{devH},1,"CUL_HM $mhp->{devN} unknown key for index $kNo, define it in the VCCU!";
  2938. $reply = "done";
  2939. }
  2940. else {
  2941. my $key = $keys{$kNo} ^ pack("H12", $challenge);
  2942. my $cipher = Crypt::Rijndael->new($key, Crypt::Rijndael::MODE_ECB());
  2943. my($s,$us) = gettimeofday();
  2944. my $respRaw = pack("NnH20", $s, $us, substr($devHlpr->{prt}{rspWait}{cmd}, 4, 20));
  2945. my $response = $cipher->encrypt($respRaw);
  2946. $devHlpr->{AESreqAck} = uc(unpack("H*", substr($response,0,4)));
  2947. Log3 $mhp->{devH},5,"CUL_HM $mhp->{devN} signing response: ".unpack("H*", $respRaw)
  2948. ." should send $devHlpr->{AESreqAck} to authenticate";
  2949. $response = $response ^ pack("H*", substr($devHlpr->{prt}{rspWait}{cmd}, 24));
  2950. $response = $cipher->encrypt(substr($response, 0, 16));
  2951. CUL_HM_SndCmd($mhp->{devH}, $mhp->{mNo}.$mhp->{mFlg}.'03'.CUL_HM_IoId($mhp->{devH}).$mhp->{src}.unpack("H*", $response));
  2952. $reply = "AES";
  2953. $repeat = 1;#prevent stop for messagenumber match
  2954. }
  2955. }
  2956. elsif ($cryptFunc != 1){ #AES is not available
  2957. Log3 $mhp->{devH},1,"CUL_HM $mhp->{devN} need Crypt::Rijndael to answer signing request with CUL";
  2958. $reply = "done";
  2959. }
  2960. }
  2961. else {
  2962. return "done";
  2963. }
  2964. }
  2965. else{ #ACK
  2966. $success = "yes";
  2967. $reply = "ACK";
  2968. }
  2969. if (defined $success && $success eq "yes"){# search if a trigger was accepted
  2970. if($mhp->{dstH}{helper}{ack}{$mhp->{devN}}){
  2971. my ($dChN,$mNo) = split(":",$mhp->{dstH}{helper}{ack}{$mhp->{devN}});
  2972. my $rv = ReadingsVal($dChN,"triggerTo_$mhp->{devN}",undef);
  2973. if ($mNo eq $mhp->{mNo} && $rv){
  2974. push @evtEt,[$defs{$dChN},1,"triggerTo_$mhp->{devN}:${rv}_ack"];
  2975. }
  2976. delete $mhp->{dstH}{helper}{ack}{$mhp->{devN}};
  2977. }
  2978. }
  2979. if ( $devHlpr->{prt}{mmcS}
  2980. && $devHlpr->{prt}{mmcS} == 3){
  2981. # after write device might need a break
  2982. # allow for wake types only - and if commands are pending
  2983. $devHlpr->{prt}{try} = 1 if(CUL_HM_getRxType($mhp->{devH}) & 0x08 #wakeup
  2984. && $mhp->{devH}{cmdStack});
  2985. if ($success eq 'yes'){
  2986. delete $devHlpr->{prt}{mmcA};
  2987. delete $devHlpr->{prt}{mmcS};
  2988. }
  2989. };
  2990. if($success){#do we have a final ack?
  2991. #mark timing on the channel, not the device
  2992. my $chn = sprintf("%02X",hex(substr($mhp->{p},2,2))&0x3f);
  2993. my $chnhash = $modules{CUL_HM}{defptr}{$chn?$mhp->{src}.$chn:$mhp->{src}};
  2994. $chnhash = $mhp->{devH} if(!$chnhash);
  2995. push @evtEt,[$chnhash,0,"CommandAccepted:$success"];
  2996. CUL_HM_ProcessCmdStack($mhp->{devH}) if(CUL_HM_IoId($mhp->{devH}) eq $mhp->{dst});
  2997. delete $devHlpr->{prt}{wuReSent}
  2998. if (!$devHlpr->{prt}{mmcS});
  2999. }
  3000. $ret = $reply;
  3001. }
  3002. elsif($mhp->{mTp} eq "03"){# AESack #############################
  3003. #Reply to AESreq - only visible with CUL or in monitoring mode
  3004. #not with HMLAN/USB
  3005. #my $aesKey = $p;
  3006. push @evtEt,[$mhp->{devH},1,"aesReqTo:".$mhp->{dstH}{NAME}] if (defined $mhp->{dstH});
  3007. $ret = "done";
  3008. }
  3009. elsif($mhp->{mTp} eq "00"){######################################
  3010. if ($devHlpr->{supp_Pair_Rep}){# repeated # Change noansi, don`t let the user press pair button forever if first pair try failed
  3011. $devHlpr->{supp_Pair_Rep} = 0; # noansi: reset flag, suppress only once not to lockup if device answer is not received
  3012. return "done"; # suppress handling of a repeated pair request
  3013. }
  3014. $devHlpr->{supp_Pair_Rep} = 1; # noansi: suppress next handling of a repeated pair request (if nothing else arrives in between from device)
  3015. my $paired = 0; #internal flag
  3016. CUL_HM_infoUpdtDevData($mhp->{devN}, $mhp->{devH},$mhp->{p})
  3017. if (!$modules{CUL_HM}{helper}{hmManualOper});
  3018. my $ioN = $ioHash->{NAME};
  3019. # hmPair set in IOdev or eventually in ccu!
  3020. my $ioOwn = InternalVal($ioN,"owner_CCU","");
  3021. my $hmPair = InternalVal($ioN,"hmPair" ,InternalVal($ioOwn,"hmPair" ,0 ));
  3022. my $hmPser = InternalVal($ioN,"hmPairSerial",InternalVal($ioOwn,"hmPairSerial",""));
  3023. if ( $hmPair ){# pairing is active
  3024. if (!$hmPser || $hmPser eq ReadingsVal($mhp->{devN},"D-serialNr","")){
  3025. # pairing requested - shall we?
  3026. my $ioId = CUL_HM_h2IoId($ioHash);
  3027. # pair now
  3028. Log3 $mhp->{devH},3, "CUL_HM pair: $mhp->{devN} "
  3029. ."$attr{$mhp->{devN}}{subType}, "
  3030. ."model $attr{$mhp->{devN}}{model} "
  3031. ."serialNr ".ReadingsVal($mhp->{devN},"D-serialNr","");
  3032. CUL_HM_RemoveHMPair("hmPairForSec:$ioOwn");# just in case...
  3033. delete $ioHash->{hmPair};
  3034. delete $ioHash->{hmPairSerial};
  3035. CUL_HM_respPendRm($mhp->{devH}); # remove all pending messages
  3036. delete $mhp->{devH}{cmdStack};
  3037. delete $devHlpr->{prt}{rspWait};
  3038. delete $devHlpr->{prt}{rspWaitSec};
  3039. delete $mhp->{devH}{READINGS}{"RegL_00."};
  3040. delete $mhp->{devH}{READINGS}{".RegL_00."};
  3041. if (!$modules{CUL_HM}{helper}{hmManualOper}){
  3042. $attr{$mhp->{devN}}{IODev} = $ioN;
  3043. $attr{$mhp->{devN}}{IOgrp} = "$ioOwn:$ioHash->{NAME}" if($ioOwn);
  3044. CUL_HM_assignIO($mhp->{devH}) ;
  3045. }
  3046. my ($idstr, $s) = ($ioId, 0xA);
  3047. $idstr =~ s/(..)/sprintf("%02X%s",$s++,$1)/ge;
  3048. CUL_HM_pushConfig($mhp->{devH}, $ioId, $mhp->{src},0,0,0,0, "0201$idstr");
  3049. $attr{$mhp->{devN}}{autoReadReg}=
  3050. AttrVal($mhp->{devN},"autoReadReg","4_reqStatus");
  3051. CUL_HM_qAutoRead($mhp->{devN},0);
  3052. # stack cmds if waiting. Do noch start if we have a burst device
  3053. # it may not paire
  3054. CUL_HM_appFromQ($mhp->{devN},"cf") if ($rxt == 0x04);
  3055. $respRemoved = 1;#force command stack processing
  3056. $paired = 1;
  3057. }
  3058. }
  3059. if($paired == 0 && CUL_HM_getRxType($mhp->{devH}) & 0x14){#no pair -send config?
  3060. CUL_HM_appFromQ($mhp->{devN},"cf"); # stack cmds if waiting
  3061. my $ioId = CUL_HM_h2IoId($mhp->{devH}{IODev});
  3062. $respRemoved = 1;#force command stack processing
  3063. }
  3064. $devHlpr->{HM_CMDNR} += 0x27; # force new setting. Send will take care of module 255
  3065. $ret = "done";
  3066. }
  3067. elsif($mhp->{mTp} eq "10"){######################################
  3068. CUL_HM_m_setCh($mhp,substr($mhp->{p},2,2));
  3069. if ($mhp->{mStp} eq "00"){ #SerialRead====================================
  3070. my $sn = pack("H*",substr($mhp->{p},2,20));
  3071. push @evtEt,[$mhp->{devH},0,"D-serialNr:$sn"];
  3072. $attr{$mhp->{devN}}{serialNr} = $sn;
  3073. CUL_HM_respPendRm($mhp->{devH}) if ($pendType eq "SerialRead");
  3074. $ret = "done";
  3075. }
  3076. elsif($mhp->{mStp} eq "01"){ #storePeerList=================================
  3077. my $mNoInt = hex($mhp->{mNo});
  3078. if ($pendType eq "PeerList" &&
  3079. ($rspWait->{mNo} == $mNoInt || $rspWait->{mNo} == $mNoInt-1)){
  3080. $rspWait->{mNo} = $mNoInt;
  3081. my $chn = $devHlpr->{prt}{rspWait}{forChn};
  3082. my $chnhash = $modules{CUL_HM}{defptr}{$mhp->{src}.$chn};
  3083. $chnhash = $mhp->{devH} if (!$chnhash);
  3084. my $chnName = $chnhash->{NAME};
  3085. my @peers;
  3086. if($mhp->{md} eq "HM-Dis-WM55"){
  3087. #how ugly - this device adds one byte at begin - remove it.
  3088. (undef,@peers) = unpack 'A4(A8)*',$mhp->{p};
  3089. }
  3090. else{
  3091. (undef,@peers) = unpack 'A2(A8)*',$mhp->{p};
  3092. }
  3093. $_ = '00000000' foreach (grep /^000000/ ,@peers);#correct bad term(6 chars) from rain sens)
  3094. $_ .= '0x' foreach (grep /^......$/,@peers);#if channel is unknown we assume at least a device
  3095. $chnhash->{helper}{peerIDsRaw} .= ",".join(",",@peers);
  3096. CUL_HM_ID2PeerList ($chnName,$_,1) foreach (@peers);
  3097. if (grep /00000000/,@peers) {# last entry, peerList is complete
  3098. # check for request to get List3 data
  3099. my $reqPeer = $chnhash->{helper}{getCfgList};
  3100. if ($reqPeer){
  3101. my $flag = 'A0';
  3102. my $ioId = CUL_HM_IoId($mhp->{devH});
  3103. my @peerID = split(",",(AttrVal($chnName,"peerIDs","")));
  3104. foreach my $l (split ",",$chnhash->{helper}{getCfgListNo}){
  3105. next if (!$l);
  3106. my $listNo = "0".$l;
  3107. foreach my $peer (grep (!/00000000/,@peerID)){
  3108. next if ($peer =~ m/0x$/);
  3109. $peer .="01" if (length($peer) == 6); # add the default
  3110. if ($peer &&($peer eq $reqPeer || $reqPeer eq "all")){
  3111. CUL_HM_PushCmdStack($mhp->{devH},sprintf("++%s01%s%s%s04%s%s",
  3112. $flag,$ioId,$mhp->{src},$chn,$peer,$listNo));# List3 or 4
  3113. }
  3114. }
  3115. }
  3116. }
  3117. CUL_HM_respPendRm($mhp->{devH});
  3118. delete $chnhash->{helper}{getCfgList};
  3119. delete $chnhash->{helper}{getCfgListNo};
  3120. CUL_HM_rmOldRegs($chnName);
  3121. $chnhash->{READINGS}{".peerListRDate"}{VAL} = $chnhash->{READINGS}{".peerListRDate"}{TIME} = $mhp->{tmStr};
  3122. }
  3123. else{
  3124. CUL_HM_respPendToutProlong($mhp->{devH});#wasn't last - reschedule timer
  3125. }
  3126. }
  3127. $ret = "done";
  3128. }
  3129. elsif($mhp->{mStp} eq "02" ||$mhp->{mStp} eq "03"){ #ParamResp==============
  3130. my $mNoInt = hex($mhp->{mNo});
  3131. if ( $pendType eq "RegisterRead" &&
  3132. ($rspWait->{mNo} == $mNoInt || $rspWait->{mNo} == $mNoInt-1)){
  3133. $repeat = 1;#prevent stop for messagenumber match
  3134. $rspWait->{mNo} = $mNoInt; # next message will be numbered same or one plus.
  3135. CUL_HM_m_setCh($mhp,$rspWait->{forChn});
  3136. my ($format,$data);
  3137. ($format,$data) = ($1,$2) if ($mhp->{p} =~ m/^(..)(.*)/);
  3138. my $list = $rspWait->{forList};
  3139. $list = "00" if (!$list); #use the default
  3140. if ($format eq "02"){ # list 2: format aa:dd aa:dd ...
  3141. $data =~ s/(..)(..)/ $1:$2/g;
  3142. }
  3143. elsif ($format eq "03"){ # list 3: format aa:dddd
  3144. my $addr;
  3145. my @dataList;
  3146. ($addr,$data) = (hex($1),$2) if ($data =~ m/(..)(.*)/);
  3147. if ($addr == 0){
  3148. $data = "00:00";
  3149. push @dataList,"00:00";
  3150. }
  3151. else{
  3152. $data =~ s/(..)/$1:/g;
  3153. foreach my $d1 (split(":",$data)){
  3154. push (@dataList,sprintf("%02X:%s",$addr++,$d1));
  3155. }
  3156. $data = join(" ",@dataList);
  3157. }
  3158. }
  3159. my $lastAddr;
  3160. $lastAddr = hex($1) if ($data =~ m/.*(..):..$/);
  3161. my $peer = $rspWait->{forPeer};
  3162. my $regLNp = "RegL_".$list.".".$peer;# pure, no expert
  3163. my $regLN = ($mhp->{cHash}{helper}{expert}{raw}?"":".").$regLNp;
  3164. if ( defined $lastAddr
  3165. && ( $lastAddr > $rspWait->{nAddr}
  3166. || $lastAddr == 0)){
  3167. CUL_HM_UpdtReadSingle($mhp->{cHash},$regLN,ReadingsVal($mhp->{cName},$regLN,"")." $data",0);
  3168. $rspWait->{nAddr} = $lastAddr;
  3169. }
  3170. if ($data =~ m/00:00$/){ # this was the last message in the block
  3171. my $peerId = CUL_HM_peerChId($peer,$mhp->{devH}{DEF});
  3172. if($list eq "00"){
  3173. push @evtEt,[$mhp->{devH},0,"PairedTo:".CUL_HM_getRegFromStore($mhp->{devN},"pairCentral",0,"")];
  3174. }
  3175. CUL_HM_respPendRm($mhp->{devH});
  3176. delete $mhp->{cHash}{helper}{shadowReg}{$regLNp}; #rm shadow
  3177. # peerChannel name from/for user entry. <IDorName> <deviceID> <ioID>
  3178. CUL_HM_updtRegDisp($mhp->{cHash},$list,$peerId);
  3179. }
  3180. else{
  3181. CUL_HM_respPendToutProlong($mhp->{devH});#wasn't last - reschedule timer
  3182. }
  3183. }
  3184. $ret = "done";
  3185. }
  3186. elsif($mhp->{mStp} eq "04" ||$mhp->{mStp} eq "05"){ #ParamChange============
  3187. #m:1E A010 4CF663 1743BF 0500(00000000)(07)(00) # finish
  3188. #m:1E A010 4CF663 1743BF 0500(00000000)(07)(62)(2120212020EA36F643)
  3189. my($mCh,$peerID,$list,$data) = ($1,$2,$3,$4) if($mhp->{p} =~ m/^0.(..)(........)(..)(.*)/);
  3190. CUL_HM_m_setCh($mhp,$mCh);
  3191. my $fch = CUL_HM_shC($mhp->{cHash},$list,$mhp->{chnHx});
  3192. my $fHash = $modules{CUL_HM}{defptr}{$mhp->{src}.$fch};
  3193. $fHash = $mhp->{devH} if (!$fHash);
  3194. my $fName = $fHash->{NAME};
  3195. my $peer = ($peerID ne "00000000") ? CUL_HM_peerChName($peerID,"000000") : "";
  3196. if($data eq "00"){#update finished for mStp 05. Now update display
  3197. CUL_HM_updtRegDisp($fHash,$list,$peerID);
  3198. }
  3199. else{
  3200. my $regLNp = "RegL_".$list.".".$peer;
  3201. $regLNp =~ s/broadcast//;
  3202. $regLNp =~ s/ /_/g; #remove blanks
  3203. my $regLN = ($mhp->{cHash}{helper}{expert}{raw}?"":".").$regLNp;
  3204. my $rCur = ReadingsVal($fName,$regLN,"");
  3205. if ($rCur){# if list not present we cannot update
  3206. if ($mhp->{mStp} eq "05"){ # generate $data identical for 04 and 05
  3207. $data = "";
  3208. my ($addr,$data1);
  3209. ($addr,$data1) = (hex($3),$4) if($mhp->{p} =~ m/^05..(........)(..)(..)(.*)/);
  3210. foreach my $d1 ($data1 =~ m/.{2}/g){
  3211. $data .= sprintf(" %02X:%s",$addr++,$d1);
  3212. }
  3213. }
  3214. else{
  3215. $data =~ s/(..)(..)/ $1:$2/g;
  3216. }
  3217. my $sdH = CUL_HM_shH($mhp->{cHash},$list,$mhp->{dst});
  3218. my $shdwReg = $sdH->{helper}{shadowReg}{$regLNp};
  3219. foreach my $entry (split(" ",$data)){
  3220. my ($a,$d) = split(":",$entry);
  3221. last if ($a eq "00");
  3222. if ($rCur =~ m/$a:/){ $rCur =~ s/$a:../$a:$d/;}
  3223. else { $rCur .= " ".$entry;}
  3224. $shdwReg =~ s/ $a:..// if ($shdwReg);# confirmed: remove from shadow
  3225. }
  3226. CUL_HM_UpdtReadSingle($fHash,$regLN,$rCur,0);
  3227. CUL_HM_updtRegDisp($fHash,$list,$peerID) if ($mhp->{mStp} eq "04");
  3228. }
  3229. }
  3230. $ret= "parsed"; # send ACK
  3231. }
  3232. elsif($mhp->{mStp} eq "06"){ #reply to status request=======================
  3233. my $rssi = substr($mhp->{p},8,2);
  3234. push @evtEt,[$mhp->{cHash},0,"recentStateType:info"];
  3235. CUL_HM_storeRssi( $mhp->{devN}
  3236. ,$mhp->{dstN}
  3237. ,(-1)*(hex($rssi))
  3238. ,$mhp->{mNo})
  3239. if ($rssi && $rssi ne '00' && $rssi ne'80');
  3240. CUL_HM_unQEntity($mhp->{cName},"qReqStat");
  3241. if ($pendType eq "StatusReq"){#it is the answer to our request
  3242. CUL_HM_respPendRm($mhp->{devH});
  3243. $ret = "STATresp";
  3244. }
  3245. else{
  3246. if ($mhp->{chn} == 0
  3247. || ( $mhp->{chn} == 1
  3248. && $devHlpr->{PONtest})){# this is power on
  3249. CUL_HM_qStateUpdatIfEnab($mhp->{devN});
  3250. CUL_HM_qAutoRead($mhp->{devN},2);
  3251. $ret = "powerOn" ;# check dst eq "000000" as well?
  3252. $devHlpr->{PONtest} = 0;
  3253. }
  3254. }
  3255. }
  3256. }
  3257. elsif($mhp->{mTp} eq "12"){ #wakeup received - ignore############
  3258. $ret = "done";
  3259. }
  3260. elsif($mhp->{mTp} =~ m/^4[01]/){ #someone is triggered##########
  3261. CUL_HM_m_setCh($mhp,substr($mhp->{p},0,2));
  3262. my $cnt = hex(substr($mhp->{p},2,2));
  3263. my $long = ($mhp->{chnraw} & 0x40)?"long":"short";
  3264. my $level = "-";
  3265. if (length($mhp->{p})>5){
  3266. my $l = substr($mhp->{p},4,2);
  3267. if ($mhp->{cHash}{helper}{lm} && $mhp->{cHash}{helper}{lm}{hex($l)}){$level = $mhp->{cHash}{helper}{lm}{hex($l)}}
  3268. elsif ($lvlStr{md}{$mhp->{md}} && $lvlStr{md}{$mhp->{md}}{$l} ){$level = $lvlStr{md}{$mhp->{md}}{$l}}
  3269. elsif ($lvlStr{st}{$mhp->{st}} && $lvlStr{st}{$mhp->{st}}{$l} ){$level = $lvlStr{st}{$mhp->{st}}{$l}}
  3270. else {$level = hex($l)};
  3271. }
  3272. elsif($mhp->{mTp} eq "40"){
  3273. $level = $long;
  3274. my $state = ucfirst($long);
  3275. if(!defined $mhp->{cHash}{helper}{BNO} || $mhp->{cHash}{helper}{BNO} ne $cnt){#cnt = event counter
  3276. $mhp->{cHash}{helper}{BNO} = $cnt;
  3277. $mhp->{cHash}{helper}{BNOCNT} = 0; # message counter reset
  3278. }
  3279. if (($mhp->{mFlgH} & 0x24) == 0x20 && ($long eq "long")){ # release long press
  3280. $state .= "Release";
  3281. }
  3282. else{ # continue long press
  3283. $mhp->{cHash}{helper}{BNOCNT} += 1;
  3284. }
  3285. $state .= " $mhp->{cHash}{helper}{BNOCNT}_$cnt";
  3286. push @evtEt,[$mhp->{cHash},1,"trigger:".(ucfirst($long))."_$cnt"];
  3287. push @evtEt,[$mhp->{cHash},1,"state:".$state." (to $mhp->{dstN})"] if ($mhp->{devH} ne $mhp->{cHash});
  3288. if( $mhp->{mFlgH} & 0x20
  3289. && $mhp->{dst} ne "000000"
  3290. && $mhp->{dst} ne $mhp->{id}){
  3291. push @evtEt,[$mhp->{cHash},1,"triggerTo_$mhp->{dstN}:".(ucfirst($long))."_$cnt"];
  3292. $devHlpr->{ack}{$mhp->{dstN}} = "$mhp->{cName}:$mhp->{mNo}";
  3293. }
  3294. }
  3295. push @evtEt,[$mhp->{cHash},1,"trigger_cnt:$cnt"];
  3296. my $peerIDs = AttrVal($mhp->{cName},"peerIDs","");
  3297. if ($peerIDs =~ m/$mhp->{dst}/){# dst is available in the ID list
  3298. foreach my $peer (grep /^$mhp->{dst}/,split(",",$peerIDs)){
  3299. my $pName = CUL_HM_id2Name($peer);
  3300. $pName = CUL_HM_id2Name($mhp->{dst}) if (!$pName || !$defs{$pName}); #$dst - device-id of $peer
  3301. next if (!$pName || !$defs{$pName});
  3302. push @evtEt,[$defs{$pName},1,"trig_$mhp->{cName}:".(ucfirst($level))."_$cnt"];
  3303. push @evtEt,[$defs{$pName},1,"trigLast:$mhp->{cName}".(($level ne "-")?":$level":"")];
  3304. CUL_HM_stateUpdatDly($pName,10) if ($mhp->{mTp} eq "40");#conditional request may not deliver state-req
  3305. }
  3306. }
  3307. elsif($mhp->{mFlgH} & 2 # dst can be garbage - but not if answer request
  3308. && ( !$mhp->{dstH}
  3309. || $mhp->{dst} ne CUL_HM_IoId($mhp->{dstH}))
  3310. ){
  3311. my $pName = CUL_HM_id2Name($mhp->{dst});
  3312. push @evtEt,[$mhp->{cHash},1,"trigDst_$pName:noConfig"];
  3313. }
  3314. return "";
  3315. }
  3316. elsif($mhp->{mTp} eq "70"){ #Time to trigger TC##################
  3317. #send wakeup and process command stack
  3318. }
  3319. if ($rspWait->{mNo} &&
  3320. $rspWait->{mNo} eq $mhp->{mNo} &&
  3321. !$repeat){
  3322. #response we waited for - stop Waiting
  3323. CUL_HM_respPendRm($mhp->{devH});
  3324. }
  3325. return $ret;
  3326. }
  3327. sub CUL_HM_m_setCh($$){### add channel identification to Message Hash
  3328. my ($mhp,$chn) = @_;
  3329. $mhp->{chnM} = $chn;
  3330. $mhp->{chnraw}= hex($mhp->{chnM});
  3331. $mhp->{chn} = $mhp->{chnraw} & 0x3f;
  3332. $mhp->{chnHx} = sprintf("%02X",$mhp->{chn});
  3333. $mhp->{cHash} = CUL_HM_id2Hash($mhp->{src}.$mhp->{chnHx});
  3334. $mhp->{cHash} = $mhp->{shash} if (!$mhp->{cHash});
  3335. $mhp->{cName} = $mhp->{cHash}{NAME};
  3336. }
  3337. sub CUL_HM_queueUpdtCfg($){
  3338. my $name = shift;
  3339. if ($modules{CUL_HM}{helper}{hmManualOper}){ # no update when manual operation
  3340. delete $modules{CUL_HM}{helper}{updtCfgLst};
  3341. }
  3342. else{
  3343. my @arr;
  3344. if ($modules{CUL_HM}{helper}{updtCfgLst}){
  3345. @arr = CUL_HM_noDup((@{$modules{CUL_HM}{helper}{updtCfgLst}}, $name));
  3346. }
  3347. else{
  3348. push @arr,$name;
  3349. }
  3350. $modules{CUL_HM}{helper}{updtCfgLst} = \@arr;
  3351. }
  3352. RemoveInternalTimer("updateConfig");
  3353. InternalTimer(gettimeofday()+5,"CUL_HM_updateConfig", "updateConfig", 0);
  3354. }
  3355. sub CUL_HM_parseSDteam(@){#handle SD team events
  3356. my ($mTp,$sId,$dId,$p) = @_;
  3357. my @entities;
  3358. my $dHash = CUL_HM_id2Hash($dId);
  3359. my $dName = CUL_HM_id2Name($dId);
  3360. my $sHash = CUL_HM_id2Hash($sId);
  3361. my $sName = CUL_HM_hash2Name($sHash);
  3362. if (AttrVal($sName,"subType","") eq "virtual"){
  3363. foreach my $cId (CUL_HM_getAssChnIds($sName)){
  3364. my $cHash = CUL_HM_id2Hash($cId);
  3365. next if (!$cHash->{sdTeam} || $cHash->{sdTeam} ne "sdLead");
  3366. my $cName = CUL_HM_id2Name($cId);
  3367. $sHash = $cHash;
  3368. $sName = CUL_HM_id2Name($cId);
  3369. last;
  3370. }
  3371. }
  3372. return () if (!$sHash->{sdTeam} || $sHash->{sdTeam} ne "sdLead");
  3373. if ($mTp eq "40"){ #test
  3374. my $trgCnt = hex(substr($p,2,2));
  3375. my $err = hex(substr($p,0,2));
  3376. push @evtEt,[$sHash,1,"teamCall:from $dName:$trgCnt"];
  3377. push @evtEt,[$dHash,1,"battery:" .(($err&0x80) ? "low":"ok")] if (!$dHash->{helper}{role}{vrt});
  3378. foreach (split ",",$attr{$sName}{peerIDs}){
  3379. my $tHash = CUL_HM_id2Hash($_);
  3380. push @evtEt,[$tHash,1,"teamCall:from $dName:$trgCnt"];
  3381. }
  3382. }
  3383. elsif ($mTp eq "41"){ #Alarm detected
  3384. #C8: Smoke Alarm
  3385. #C7: tone off
  3386. #01: no alarm
  3387. my (undef,$No,$state) = unpack 'A2A2A2',$p;
  3388. if(($dHash) && # update source(ID reported in $dst)
  3389. (!$dHash->{helper}{alarmNo} || $dHash->{helper}{alarmNo} ne $No)){
  3390. $dHash->{helper}{alarmNo} = $No;
  3391. }
  3392. my ($sVal,$sProsa,$smokeSrc) = (hex($state),"off","none");
  3393. if ($sVal > 1){
  3394. $sProsa = "smoke-Alarm_".$No;
  3395. $smokeSrc = $dName;
  3396. }
  3397. return if($sProsa eq ReadingsVal($sHash->{NAME},"state",""));
  3398. push @evtEt,[$sHash,1,"recentAlarm:$smokeSrc"] if($sVal == 200);
  3399. push @evtEt,[$sHash,1,"state:$sProsa"];
  3400. push @evtEt,[$sHash,1,'level:'.$sVal];
  3401. push @evtEt,[$sHash,1,"eventNo:".$No];
  3402. push @evtEt,[$sHash,1,"smoke_detect:".$smokeSrc];
  3403. foreach (split ",",$attr{$sName}{peerIDs}){
  3404. my $tHash = CUL_HM_id2Hash($_);
  3405. push @evtEt,[$tHash,1,"state:$sProsa"];
  3406. push @evtEt,[$tHash,1,"smoke_detect:$smokeSrc"];
  3407. }
  3408. }
  3409. return @entities;
  3410. }
  3411. sub CUL_HM_parseSDteam_2(@){#handle SD team events
  3412. my ($mTp,$sId,$dId,$p) = @_;
  3413. my $dHash = CUL_HM_id2Hash($dId);
  3414. my $dName = CUL_HM_id2Name($dId);
  3415. my $sHash = CUL_HM_id2Hash($sId);
  3416. my $sName = CUL_HM_hash2Name($sHash);
  3417. if (AttrVal($sName,"subType","") eq "virtual"){#search for the team lead channel
  3418. foreach my $cId (CUL_HM_getAssChnIds($sName)){
  3419. my $cHash = CUL_HM_id2Hash($cId);
  3420. next if (!$cHash->{sdTeam} || $cHash->{sdTeam} ne "sdLead");
  3421. $sHash = $cHash;
  3422. $sName = CUL_HM_hash2Name($sHash);
  3423. last;
  3424. }
  3425. }
  3426. return () if (!$sHash->{sdTeam} || $sHash->{sdTeam} ne "sdLead"
  3427. ||!$dHash);
  3428. my ($chn,$No,$state,$null,$aesKNo,$aesStr) = unpack 'A2A2A2A4A2A8',$p;
  3429. if(!$dHash->{helper}{alarmNo} || $dHash->{helper}{alarmNo} ne $No){
  3430. $dHash->{helper}{alarmNo} = $No;
  3431. }
  3432. else{
  3433. return ();# duplicate alarm
  3434. }
  3435. my ($sVal,$sProsa,$smokeSrc) = (hex($state),"off","none");
  3436. my @tHash = ((map{CUL_HM_id2Hash($_)} grep !/00000000/, split ",",$attr{$sName}{peerIDs})
  3437. ,$sHash);
  3438. if ($sVal > 179 ||$sVal <51 ){# need to raise alarm
  3439. if ($sVal > 179){# need to raise alarm
  3440. #"SHORT_COND_VALUE_LO" value="50"/>
  3441. #"SHORT_COND_VALUE_HI" value="180"/>
  3442. $sProsa = "smoke-Alarm_".$No;
  3443. $smokeSrc = $dName;
  3444. push @evtEt,[$sHash,1,"recentAlarm:$smokeSrc"] if($sVal == 200);
  3445. }
  3446. elsif($sVal <51){#alarm inactive
  3447. #$sProsa = "off_".$No;
  3448. #$smokeSrc = $dName;
  3449. }
  3450. push @evtEt,[$sHash,1,'level:'.$sVal];
  3451. }
  3452. elsif($sVal == 150){#alarm teamcall
  3453. push @evtEt,[$_,1,"teamCall:from $dName:$No"] foreach (@tHash);
  3454. }
  3455. elsif($sVal == 151){#alarm teamcall repeat
  3456. push @evtEt,[$dHash,1,"MsgRepeated $No"];#unclear. first repeater send 97 instead of 96. What about 2nd ans third repeater?
  3457. }
  3458. foreach (@tHash){
  3459. push @evtEt,[$_,1,"state:$sProsa"];
  3460. push @evtEt,[$_,1,"smoke_detect:$smokeSrc"];
  3461. }
  3462. push @evtEt,[$dHash,1,"battery:" .((hex($chn)&0x80) ? "low":"ok")] if (!$dHash->{helper}{role}{vrt});
  3463. push @evtEt,[$sHash,1,"eventNo:".$No];
  3464. Log3 $sHash,5,"CUL_HM $sName sdTeam: no:$No state:$state aesNo:$aesKNo aesStr:$aesStr";
  3465. return;
  3466. }
  3467. sub CUL_HM_updtSDTeam(@){#in: TeamName, optional caller name and its new state
  3468. # update team status if virtual team lead
  3469. # check all member state
  3470. # prio: 1:alarm, 2: unknown, 3: off
  3471. # sState given in input may not yet be visible in readings
  3472. my ($name,$sName,$sState) = @_;
  3473. return undef if (!$defs{$name} || AttrVal($name,"model","") !~ m "virtual");
  3474. ($sName,$sState) = ("","") if (!$sName || !$sState);
  3475. return undef if (ReadingsVal($name,"state","off") =~ m/smoke-Alarm/);
  3476. my $dStat = "off";
  3477. foreach my $pId(split(',',AttrVal($name,"peerIDs",""))){#screen teamIDs for Alarm
  3478. my $pNam = (($pId && $pId ne "00000000") ? CUL_HM_id2Name(substr($pId,0,6)) : "");
  3479. next if (!$pNam ||!$defs{$pNam});
  3480. my $pStat = ($pNam eq $sName)
  3481. ?$sState
  3482. :ReadingsVal($pNam,"state",undef);
  3483. if (!$pStat) {$dStat = "unknown";}
  3484. elsif ($pStat ne "off") {$dStat = $pStat;last;}
  3485. }
  3486. return CUL_HM_UpdtReadSingle($defs{$name},"state",$dStat,1);
  3487. }
  3488. sub CUL_HM_pushEvnts(){########################################################
  3489. #write events to Readings and collect touched devices
  3490. my @ent = ();
  3491. $evtDly = 0;# switch delay trigger off
  3492. if (scalar(@evtEt) > 0){
  3493. @evtEt = sort {($a->[0] cmp $b->[0])|| ($a->[1] cmp $b->[1])} @evtEt;
  3494. my ($h,$x) = ("","");
  3495. my @evts = ();
  3496. foreach my $e(@evtEt){
  3497. if(scalar(@{$e} != 3)){
  3498. Log 2,"CUL_HM set reading invalid:".join(",",@{$e});
  3499. next;
  3500. }
  3501. if ($h ne ${$e}[0] || $x ne ${$e}[1]){
  3502. push @ent,CUL_HM_UpdtReadBulk($h,$x,@evts);
  3503. @evts = ();
  3504. ($h,$x) = (${$e}[0],${$e}[1]);
  3505. }
  3506. push @evts,${$e}[2] if (${$e}[2]);
  3507. }
  3508. @evtEt = ();
  3509. push @ent,CUL_HM_UpdtReadBulk($h,$x,@evts);
  3510. }
  3511. return @ent;
  3512. }
  3513. sub CUL_HM_Get($@) {#+++++++++++++++++ get command+++++++++++++++++++++++++++++
  3514. my ($hash, @a) = @_;
  3515. return "no value specified" if(@a < 2);
  3516. return "" if(!$hash->{NAME});
  3517. my $name = $hash->{NAME};
  3518. return ""
  3519. if (CUL_HM_getAttrInt($name,"ignore"));
  3520. my $devName = InternalVal($name,"device",$name);
  3521. my $st = AttrVal($devName, "subType", "");
  3522. my $md = AttrVal($devName, "model", "");
  3523. my $cmd = $a[1];
  3524. my $roleC = $hash->{helper}{role}{chn}?1:0; #entity may act in multiple roles
  3525. my $roleD = $hash->{helper}{role}{dev}?1:0;
  3526. my $roleV = $hash->{helper}{role}{vrt}?1:0;
  3527. my $fkt = $hash->{helper}{fkt}?$hash->{helper}{fkt}:"";
  3528. my ($dst,$chn) = unpack 'A6A2',$hash->{DEF}.($roleC?'01':'00');
  3529. my $h = undef;
  3530. $h = $culHmGlobalGets->{$cmd} if(!$roleV &&($roleD || $roleC));
  3531. $h = $culHmVrtGets->{$cmd} if(!defined($h) && $roleV);
  3532. $h = $culHmSubTypeGets->{$st}{$cmd} if(!defined($h) && $culHmSubTypeGets->{$st});
  3533. $h = $culHmModelGets->{$md}{$cmd} if(!defined($h) && $culHmModelGets->{$md});
  3534. $h = "" if(!defined($h) && (eval "defined(&HMinfo_GetFn)" && $cmd eq "regTable"));
  3535. my @h;
  3536. @h = split(" ", $h) if($h);
  3537. if(!defined($h)) {
  3538. my @arr = ();
  3539. if(!$roleV &&($roleD || $roleC)){foreach(keys %{$culHmGlobalGets} ){push @arr,"$_:".$culHmGlobalGets->{$_} }};
  3540. if($roleV) {foreach(keys %{$culHmVrtGets} ){push @arr,"$_:".$culHmVrtGets->{$_} }};
  3541. if($culHmSubTypeGets->{$st}) {foreach(keys %{$culHmSubTypeGets->{$st}}){push @arr,"$_:".${$culHmSubTypeGets->{$st}}{$_} }};
  3542. if($culHmModelGets->{$md}) {foreach(keys %{$culHmModelGets->{$md}} ){push @arr,"$_:".${$culHmModelGets->{$md}}{$_} }};
  3543. if($culHmModelGets->{$md}) {foreach(keys %{$culHmModelGets->{$md}} ){push @arr,"$_:".${$culHmModelGets->{$md}}{$_} }};
  3544. if(eval"defined(&HMinfo_GetFn)"){ {push @arr,"regTable:" }};
  3545. foreach(@arr){
  3546. my ($cmd,$val) = split(":",$_,2);
  3547. if (!$val ||
  3548. $val !~ m/^\[.*\]$/ ||
  3549. $val =~ m/\[.*\[/ ||
  3550. $val =~ m/(\<|\>)]/
  3551. ){
  3552. $_ = $cmd;
  3553. }
  3554. else{
  3555. $val =~ s/(\[|\])//g;
  3556. my @vArr = split('\|',$val);
  3557. foreach (@vArr){
  3558. if ($_ =~ m/(.*)\.\.(.*)/ ){
  3559. my @list = map { ($_.".0", $_+0.5) } (($1+0)..($2+0));
  3560. pop @list;
  3561. $_ = join(",",@list);
  3562. }
  3563. }
  3564. $_ = "$cmd:".join(",",@vArr);
  3565. }
  3566. }
  3567. my $usg = "Unknown argument $cmd, choose one of ".join(" ",sort @arr);
  3568. return $usg;
  3569. }
  3570. elsif($h eq "" && @a != 2) {
  3571. return "$cmd requires no parameters";
  3572. }
  3573. elsif($h !~ m/\.\.\./ && @h != @a-2) {
  3574. return "$cmd requires parameter: $h";
  3575. }
  3576. my $devHash = CUL_HM_getDeviceHash($hash);
  3577. #----------- now start processing --------------
  3578. if ($cmd eq "param") { ###################################################
  3579. my $p = $a[2];
  3580. return $attr{$name}{$p} if ($attr{$name}{$p});
  3581. return $hash->{READINGS}{$p}{VAL} if ($hash->{READINGS}{$p});
  3582. return $hash->{READINGS}{".$p"}{VAL} if ($hash->{READINGS}{".$p"});
  3583. return $hash->{$p} if ($hash->{$p});
  3584. return $hash->{helper}{$p} if ($hash->{helper}{$p} && ref($hash->{helper}{$p}) ne "HASH");
  3585. return $attr{$devName}{$p} if ($attr{$devName}{$p});
  3586. return "undefined";
  3587. }
  3588. elsif($cmd =~ m/^(reg|regVal)$/) { #########################################
  3589. my (undef,undef,$regReq,$list,$peerId) = (@a,0,0);
  3590. return if(!defined $regReq);
  3591. if ($regReq eq 'all'){
  3592. my @regArr = CUL_HM_getRegN($st,$md,($roleD?"00":""),($roleC?$chn:""));
  3593. my @peers; # get all peers we have a reglist
  3594. my @listWp; # list that require peers
  3595. foreach my $readEntry (keys %{$hash->{READINGS}}){
  3596. if ($readEntry =~ m/^[\.]?RegL_(.*)/){ #reg Reading "RegL_<list>:peerN
  3597. my $peer = substr($1,3);
  3598. next if (!$peer);
  3599. push(@peers,$peer);
  3600. push(@listWp,substr($1,1,1));
  3601. }
  3602. }
  3603. @listWp = CUL_HM_noDup(@listWp);
  3604. my @regValList; #storage of results
  3605. my $regHeader = "list:peer\tregister :value\n";
  3606. foreach my $regName (@regArr){
  3607. my $regL = $culHmRegDefine->{$regName}->{l};
  3608. my @peerExe = (grep (/$regL/,@listWp)) ? @peers : ("00000000");
  3609. @peerExe = CUL_HM_noDup(@peerExe);
  3610. foreach my $peer(@peerExe){
  3611. next if($peer eq "");
  3612. my $regVal= CUL_HM_getRegFromStore($name,$regName,0,$peer);#determine
  3613. my $peerN = CUL_HM_id2Name($peer);
  3614. $peerN = " " if ($peer eq "00000000");
  3615. push @regValList,sprintf(" %d:%s\t%-16s :%s\n",
  3616. $regL,$peerN,$regName,$regVal)
  3617. if ($regVal !~ m/invalid/);
  3618. }
  3619. }
  3620. my $addInfo = "";
  3621. if ($md =~ m/^(HM-CC-TC|ROTO_ZEL-STG-RM-FWT)/ && $chn eq "02"){$addInfo = CUL_HM_TCtempReadings($hash)}
  3622. elsif ($md =~ m/^HM-CC-RT-DN/ && $chn eq "04"){$addInfo = CUL_HM_TCITRTtempReadings($hash,$md,7)}
  3623. elsif ($md =~ m/^HM-TC-IT/ && $chn eq "02"){$addInfo = CUL_HM_TCITRTtempReadings($hash,$md,7,8,9)}
  3624. elsif ($md =~ m/^(^HM-PB-4DIS-WM|HM-Dis-WM55|HM-RC-Dis-H-x-EU|ROTO_ZEL-STG-RM-DWT-10)/)
  3625. {$addInfo = CUL_HM_4DisText($hash)}
  3626. elsif ($md eq "HM-Sys-sRP-Pl") {$addInfo = CUL_HM_repReadings($hash)}
  3627. return $name." type:".$st." - \n".
  3628. $regHeader.join("",sort(@regValList)).
  3629. $addInfo;
  3630. }
  3631. else{
  3632. my $regVal = CUL_HM_getRegFromStore($name,$regReq,$list,$peerId);
  3633. $regVal =~ s/ .*// if ($cmd eq "regVal");
  3634. return ($regVal =~ m/^invalid/)? "Value not captured:$name - $regReq"
  3635. : $regVal;
  3636. }
  3637. }
  3638. elsif($cmd eq "regTable") { ########################################
  3639. return HMinfo_GetFn($hash,$name,"register","-f","\^".$name."\$");
  3640. }
  3641. elsif($cmd eq "regList") { #################################################
  3642. return CUL_HM_getRegInfo($name) ;
  3643. }
  3644. elsif($cmd eq "cmdList") { #################################################
  3645. my @arr;
  3646. if(!$roleV) {push @arr,"$_ $culHmGlobalGets->{$_}" foreach (keys %{$culHmGlobalGets})};
  3647. if($roleV) {push @arr,"$_ $culHmVrtGets->{$_}" foreach (keys %{$culHmVrtGets})};
  3648. push @arr,"$_ $culHmSubTypeGets->{$st}{$_}" foreach (keys %{$culHmSubTypeGets->{$st}});
  3649. push @arr,"$_ $culHmModelGets->{$md}{$_}" foreach (keys %{$culHmModelGets->{$md}});
  3650. my @arr1;
  3651. if ($hash->{helper}{regLst}){
  3652. foreach my $rl(grep /./,split(",",$hash->{helper}{regLst})){
  3653. next if (!defined $culHmReglSets->{$rl});
  3654. foreach(keys %{$culHmReglSets->{$rl}} ){push @arr1,"$_:".$culHmReglSets->{$rl}{$_} };
  3655. }
  3656. }
  3657. else{#ignore e.g. for virtuals
  3658. }
  3659. if( !$roleV &&($roleD || $roleC) ){foreach(keys %{$culHmGlobalSets} ){push @arr1,"$_:".$culHmGlobalSets->{$_} }};
  3660. if(( $roleV || !$st) && $roleD){foreach(keys %{$culHmGlobalSetsVrtDev} ){push @arr1,"$_ ".$culHmGlobalSetsVrtDev->{$_} }};
  3661. if( !$roleV && $roleD){foreach(keys %{$culHmSubTypeDevSets->{$st}}){push @arr1,"$_ ".${$culHmSubTypeDevSets->{$st}}{$_}}};
  3662. if( !$roleV && $roleC){foreach(keys %{$culHmGlobalSetsChn} ){push @arr1,"$_ ".$culHmGlobalSetsChn->{$_} }};
  3663. if( $culHmSubTypeSets->{$st} && $roleC){foreach(keys %{$culHmSubTypeSets->{$st}} ){push @arr1,"$_ ".${$culHmSubTypeSets->{$st}}{$_} }};
  3664. if( $culHmModelSets->{$md}) {foreach(keys %{$culHmModelSets->{$md}} ){push @arr1,"$_ ".${$culHmModelSets->{$md}}{$_} }};
  3665. if( $culHmChanSets->{$md."00"} && $roleD){foreach(keys %{$culHmChanSets->{$md."00"}} ){push @arr1,"$_ ".${$culHmChanSets->{$md."00"}}{$_} }};
  3666. if( $culHmChanSets->{$md.$chn} && $roleC){foreach(keys %{$culHmChanSets->{$md.$chn}} ){push @arr1,"$_ ".${$culHmChanSets->{$md.$chn}}{$_} }};
  3667. if( $culHmFunctSets->{$fkt} && $roleC){foreach(keys %{$culHmFunctSets->{$fkt}} ){push @arr1,"$_ ".${$culHmFunctSets->{$fkt}}{$_} }};
  3668. my $info .= " Gets ------\n";
  3669. $info .= join("\n",sort @arr);
  3670. $info .= "\n\n Sets ------\n";
  3671. $info .= join("\n",sort @arr1);
  3672. return $info;
  3673. }
  3674. elsif($cmd eq "saveConfig"){ ###############################################
  3675. return "no filename given" if (!$a[2]);
  3676. my $fName = $a[2];
  3677. open(aSave, ">>$fName") || return("Can't open $fName: $!");
  3678. my $sName;
  3679. my @eNames;
  3680. if ($a[3] && $a[3] eq "strict"){
  3681. @eNames = ($name);
  3682. $sName = $name;
  3683. }
  3684. else{
  3685. $sName = $devName;
  3686. @eNames = CUL_HM_getAssChnNames($sName);
  3687. }
  3688. print aSave "\n\n#======== store device data:".$sName." === from: ".TimeNow();
  3689. foreach my $eName (@eNames){
  3690. print aSave "\n#--- entity:".$eName;
  3691. foreach my $rName ("D-firmware","D-serialNr",".D-devInfo",".D-stc"){
  3692. my $rVal = ReadingsVal($eName,$rName,undef);
  3693. print aSave "\nsetreading $eName $rName $rVal" if (defined $rVal);
  3694. }
  3695. my $pIds = AttrVal($eName, "peerIDs", "");
  3696. my $timestamps = "\n# timestamp of the readings for reference";
  3697. if ($pIds){
  3698. print aSave "\n# Peer Names:"
  3699. .InternalVal($eName,"peerList","");
  3700. $timestamps .= "\n# "
  3701. .InternalVal($eName,"peerList","")
  3702. ." :peerList";
  3703. print aSave "\nset $eName peerBulk $pIds#"
  3704. .ReadingsTimestamp($eName,"peerList","1900-01-01 00:00:01");
  3705. }
  3706. my $ehash = $defs{$eName};
  3707. foreach my $read (sort grep(/^[\.]?RegL_/,keys %{$ehash->{READINGS}})){
  3708. my $ts = ReadingsTimestamp($eName,$read,"1900-01-01 00:00:01");
  3709. print aSave "\nset $eName regBulk $read"
  3710. ." ".ReadingsVal($eName,$read,"")
  3711. ." #$ts";
  3712. $timestamps .= "\n# $ts :$read";
  3713. }
  3714. print aSave $timestamps;
  3715. }
  3716. print aSave "\n======= finished ===\n";
  3717. close(aSave);
  3718. }
  3719. elsif($cmd eq "listDevice"){ ###############################################
  3720. if ($md eq "CCU-FHEM"){
  3721. my @dl = grep !/^$/,
  3722. map{AttrVal($_,"IOgrp","") =~ m/^$name/ ? $_ : ""}
  3723. keys %defs;
  3724. my @rl;
  3725. foreach (@dl){
  3726. my(undef,$pref) = split":",$attr{$_}{IOgrp},2;
  3727. $pref = "---" if (!$pref);
  3728. my $IODev = $defs{$_}{IODev}->{NAME}?$defs{$_}{IODev}->{NAME}:"---";
  3729. push @rl, "$IODev / $pref $_ ";
  3730. }
  3731. return "devices using $name\ncurrent IO / preferred\n ".join "\n ", sort @rl;
  3732. }
  3733. elsif ($md eq "ActionDetector"){
  3734. my $re = $a[2]?$a[2]:"all";
  3735. if($re && $re =~ m/^(all|alive|unknown|dead|notAlive)$/){
  3736. my @fnd = map {$_.":".$defs{$name}{READINGS}{$_}{VAL}}
  3737. grep /^status_/,
  3738. keys %{$defs{ActionDetector}{READINGS}};
  3739. if ($re eq "notAlive"){ @fnd = grep !/:alive$/,@fnd; }
  3740. elsif ($re eq "all") {; }
  3741. else { @fnd = grep /:$a[2]$/,@fnd;}
  3742. $_ =~ s/status_(.*):.*/$1/ foreach(@fnd);
  3743. push @fnd,"empty" if (!scalar(@fnd));
  3744. return join",",sort(@fnd);
  3745. } else{
  3746. return "please enter parameter [alive|unknown|dead|notAlive]";
  3747. }
  3748. }
  3749. }
  3750. elsif($cmd eq "info"){ ###############################################
  3751. return CUL_HM_ActInfo();
  3752. }
  3753. Log3 $name,3,"CUL_HM get $name " . join(" ", @a[1..$#a]);
  3754. my $rxType = CUL_HM_getRxType($hash);
  3755. CUL_HM_ProcessCmdStack($devHash) if ($rxType & 0x03);#burst/all
  3756. return "";
  3757. }
  3758. sub CUL_HM_Set($@) {#+++++++++++++++++ set command+++++++++++++++++++++++++++++
  3759. my ($hash, @a) = @_;
  3760. return "no value specified" if(@a < 2);
  3761. return "FW update in progress - please wait"
  3762. if ($modules{CUL_HM}{helper}{updating});
  3763. my $act = join(" ", @a[1..$#a]);
  3764. my $name = $hash->{NAME};
  3765. return ""
  3766. if (CUL_HM_getAttrInt($name,"ignore"));
  3767. my $devName = InternalVal($name,"device",$name);
  3768. my $st = AttrVal($devName, "subType", "");
  3769. my $md = AttrVal($devName, "model" , "");
  3770. my $flag = 'A0'; #set flag
  3771. my $cmd = $a[1];
  3772. my ($dst,$chn) = unpack 'A6A2',$hash->{DEF}.'01';#default to chn 01 for dev
  3773. return "" if (!defined $chn);
  3774. my $roleC = $hash->{helper}{role}{chn}?1:0; #entity may act in multiple roles
  3775. my $roleD = $hash->{helper}{role}{dev}?1:0;
  3776. my $roleV = $hash->{helper}{role}{vrt}?1:0;
  3777. my $fkt = $hash->{helper}{fkt}?$hash->{helper}{fkt}:"";
  3778. my $oCmd = $cmd;# we extend press to press/L/S if press is defined
  3779. $cmd = "press" if ($cmd =~ m/^press/);# substitude pressL/S with press for cmd search
  3780. my $h = undef;
  3781. $h = $culHmGlobalSets->{$cmd} if( !$roleV &&($roleD || $roleC));
  3782. $h = $culHmGlobalSetsVrtDev->{$cmd} if(!defined($h) &&( $roleV || !$st) && $roleD);
  3783. $h = $culHmSubTypeDevSets->{$st}{$cmd}if(!defined($h) && !$roleV && $roleD);
  3784. $h = $culHmGlobalSetsChn->{$cmd} if(!defined($h) && !$roleV && $roleC);
  3785. $h = $culHmSubTypeSets->{$st}{$cmd} if(!defined($h) && $culHmSubTypeSets->{$st} && $roleC);
  3786. $h = $culHmModelSets->{$md}{$cmd} if(!defined($h) && $culHmModelSets->{$md} );
  3787. $h = $culHmChanSets->{$md."00"}{$cmd} if(!defined($h) && $culHmChanSets->{$md."00"} && $roleD);
  3788. $h = $culHmChanSets->{$md.$chn}{$cmd} if(!defined($h) && $culHmChanSets->{$md.$chn} && $roleC);
  3789. $h = $culHmFunctSets->{$fkt}{$cmd} if(!defined($h) && $culHmFunctSets->{$fkt});
  3790. if( !defined($h) && $hash->{helper}{regLst}){
  3791. foreach my $rl(grep /./,split(",",$hash->{helper}{regLst})){
  3792. next if (!defined $culHmReglSets->{$rl});
  3793. $h = $culHmReglSets->{$rl}{$cmd};
  3794. last if (defined($h));
  3795. }
  3796. }
  3797. $cmd = $oCmd;# necessary for press/S/L - check better implementation
  3798. my @h;
  3799. @h = split(" ", $h) if($h);
  3800. my @postCmds=(); #Commands to be appended after regSet (ugly...)
  3801. if(!defined($h) && defined($culHmSubTypeSets->{$st}{pct}) && $cmd =~ m/^\d+/) {
  3802. splice @a, 1, 0,"pct";#insert the actual command
  3803. }
  3804. elsif(!defined($h)) { ### unknown - return the commandlist
  3805. my @arr1 = ();
  3806. if ($hash->{helper}{regLst}){
  3807. foreach my $rl(grep /./,split(",",$hash->{helper}{regLst})){
  3808. next if (!defined $culHmReglSets->{$rl});
  3809. foreach(keys %{$culHmReglSets->{$rl}} ){push @arr1,"$_:".$culHmReglSets->{$rl}{$_} };
  3810. }
  3811. }
  3812. else{#ignore e.g. for virtuals
  3813. }
  3814. if( !$roleV &&($roleD || $roleC) ){foreach(keys %{$culHmGlobalSets} ){push @arr1,"$_:".$culHmGlobalSets->{$_} }};
  3815. if(( $roleV||!$st) && $roleD){foreach(keys %{$culHmGlobalSetsVrtDev} ){push @arr1,"$_:".$culHmGlobalSetsVrtDev->{$_} }};
  3816. if( !$roleV && $roleD){foreach(keys %{$culHmSubTypeDevSets->{$st}}){push @arr1,"$_:".${$culHmSubTypeDevSets->{$st}}{$_}}};
  3817. if( !$roleV && $roleC){foreach(keys %{$culHmGlobalSetsChn} ){push @arr1,"$_:".$culHmGlobalSetsChn->{$_} }};
  3818. if( $culHmSubTypeSets->{$st} && $roleC){foreach(keys %{$culHmSubTypeSets->{$st}} ){push @arr1,"$_:".${$culHmSubTypeSets->{$st}}{$_} }};
  3819. if( $culHmModelSets->{$md}) {foreach(keys %{$culHmModelSets->{$md}} ){push @arr1,"$_:".${$culHmModelSets->{$md}}{$_} }};
  3820. if( $culHmChanSets->{$md."00"} && $roleD){foreach(keys %{$culHmChanSets->{$md."00"}} ){push @arr1,"$_:".${$culHmChanSets->{$md."00"}}{$_} }};
  3821. if( $culHmChanSets->{$md.$chn} && $roleC){foreach(keys %{$culHmChanSets->{$md.$chn}} ){push @arr1,"$_:".${$culHmChanSets->{$md.$chn}}{$_} }};
  3822. if( $culHmFunctSets->{$fkt} && $roleC){foreach(keys %{$culHmFunctSets->{$fkt}} ){push @arr1,"$_:".${$culHmFunctSets->{$fkt}}{$_} }};
  3823. @arr1 = CUL_HM_noDup(@arr1);
  3824. foreach(@arr1){
  3825. my ($cmd,$val) = split(":",$_,2);
  3826. if (!$val){ # no agruments possible
  3827. $_ = "$cmd:noArg";
  3828. }
  3829. elsif($val !~ m/^\[.*\]$/ ||
  3830. $val =~ m/\[.*\[/ ||
  3831. $val =~ m/(\<|\>)]/
  3832. ){
  3833. $_ = $cmd;
  3834. }
  3835. else{
  3836. $val =~ s/(\[|\])//g;
  3837. my @vArr = split('\|',$val);
  3838. foreach (@vArr){
  3839. if ($_ =~ m/(.*)\.\.(.*)/ ){
  3840. my @list = map { ($_.".0", $_+0.5) } (($1+0)..($2+0));
  3841. pop @list;
  3842. $_ = join(",",@list);
  3843. }
  3844. }
  3845. $_ = "$cmd:".join(",",@vArr);
  3846. }
  3847. }
  3848. @arr1 = ("--") if (!scalar @arr1);
  3849. my $usg = "Unknown argument $cmd, choose one of ".join(" ",sort @arr1);
  3850. $usg =~ s/ pct/ pct:slider,0,1,100/;
  3851. $usg =~ s/ pctSlat/ pctSlat:slider,0,1,100/;
  3852. $usg =~ s/ virtual/ virtual:slider,1,1,50/;
  3853. $usg =~ s/ color/ color:colorpicker,HUE,0,0.5,100/;
  3854. if ($usg =~ m/ tempTmplSet/){
  3855. my $tl = $modules{CUL_HM}{AttrList};
  3856. my $ok = ($tl =~ s/.* (tempListTmpl)(\:.*? ).*/$2/);
  3857. $tl = $ok?$tl:"";
  3858. $usg =~ s/ tempTmplSet/ tempTmplSet$tl/;
  3859. }
  3860. if ( $usg =~ m/ templateDel/
  3861. && eval "defined(&HMinfo_templateDel)"
  3862. && keys %{$hash->{helper}{tmpl}}){
  3863. my $tl = join(",",(sort keys %{$hash->{helper}{tmpl}}));
  3864. # $tl =~ s/:/>/;
  3865. $usg =~ s/ templateDel/ templateDel:$tl/;
  3866. }
  3867. else{
  3868. $usg =~ s/ templateDel//;#not an option
  3869. }
  3870. if ( $usg =~ m/ (press|event|trgPress|trgEvent)/){
  3871. my $peers = join",",grep/./,split",",InternalVal($name,"peerList","");
  3872. if ($peers){
  3873. $usg =~ s/ press/ press pressS:$peers pressL:$peers/g;
  3874. $usg =~ s/ (trgPress.:)-peer-/ $1/g;
  3875. $usg =~ s/ (trgPress.:)/ $1all,$peers/g;
  3876. }
  3877. else{#remove command
  3878. $usg =~ s/ (press|event)[SL]//g;
  3879. $usg =~ s/ trg(Press|Event)[SL]//g;
  3880. }
  3881. }
  3882. return $usg;
  3883. }
  3884. elsif($h eq "" && @a != 2) {
  3885. return "$cmd requires no parameters";
  3886. }
  3887. elsif($h !~ m/\.\.\./ && @h != @a-2) {
  3888. return "$cmd requires parameter: $h";
  3889. }
  3890. my $id; # define id of IO device for later usage
  3891. ###------------------- commands requiring no IO action -------------------###
  3892. my $nonIOcmd = 1;
  3893. if( $cmd eq "clear") { ####################################################
  3894. my (undef,undef,$sectIn) = @a;
  3895. my @sectL;
  3896. if ($sectIn eq "all") {
  3897. @sectL = ("rssi","msgEvents","readings","attack");#readings is last - it schedules a reread possible
  3898. }
  3899. elsif($sectIn =~ m/(rssi|trigger|msgEvents|msgErrors|readings|oldRegs|register|unknownDev|attack)/){
  3900. @sectL = ($sectIn);
  3901. }
  3902. else{
  3903. return "unknown section:$sectIn. User rssi|trigger|msgEvents|readings|oldRegs|register|unknownDev|attack";
  3904. }
  3905. foreach my $sect (@sectL){
  3906. if ($sect eq "readings"){
  3907. my @cH = ($hash);
  3908. push @cH,$defs{$hash->{$_}} foreach(grep /^channel/,keys %{$hash});
  3909. delete $_->{READINGS} foreach (@cH);
  3910. delete $modules{CUL_HM}{helper}{cfgCmpl}{$name};
  3911. CUL_HM_complConfig($_->{NAME}) foreach (@cH);
  3912. CUL_HM_qStateUpdatIfEnab($_->{NAME}) foreach (@cH);
  3913. }
  3914. elsif($sect eq "unknownDev"){
  3915. delete $hash->{READINGS}{$_}
  3916. foreach (grep /^unknown_/,keys %{$hash->{READINGS}});
  3917. }
  3918. elsif($sect eq "trigger"){
  3919. delete $hash->{READINGS}{$_}
  3920. foreach (grep /^trig/,keys %{$hash->{READINGS}});
  3921. }
  3922. elsif($sect eq "register"){
  3923. my @cH = ($hash);
  3924. push @cH,$defs{$hash->{$_}} foreach(grep /^channel/,keys %{$hash});
  3925. foreach my $h(@cH){
  3926. delete $h->{READINGS}{$_}
  3927. foreach (grep /^(\.?)(R-|RegL)/,keys %{$h->{READINGS}});
  3928. delete $h->{helper}{shadowReg}{$_}
  3929. foreach (grep /^(\.?)(R-|RegL)/,keys %{$h->{helper}{shadowReg}});
  3930. delete $modules{CUL_HM}{helper}{cfgCmpl}{$name};
  3931. CUL_HM_complConfig($h->{NAME});
  3932. }
  3933. }
  3934. elsif($sect eq "oldRegs"){
  3935. my @cN = ($name);
  3936. push @cN,$hash->{$_} foreach(grep /^channel/,keys %{$hash});
  3937. CUL_HM_refreshRegs($_) foreach (@cN);
  3938. }
  3939. elsif($sect eq "msgEvents"){
  3940. CUL_HM_respPendRm($hash);
  3941. $hash->{helper}{prt}{bErr}=0;
  3942. delete $hash->{cmdStack};
  3943. delete $hash->{helper}{prt}{rspWait};
  3944. delete $hash->{helper}{prt}{rspWaitSec};
  3945. delete $hash->{helper}{prt}{mmcA};
  3946. delete $hash->{helper}{prt}{mmcS};
  3947. delete $hash->{lastMsg};
  3948. delete ($hash->{$_}) foreach (grep(/^prot/,keys %{$hash}));
  3949. if ($hash->{IODev}{NAME} &&
  3950. $modules{CUL_HM}{$hash->{IODev}{NAME}} &&
  3951. $modules{CUL_HM}{$hash->{IODev}{NAME}}{pendDev}){
  3952. @{$modules{CUL_HM}{$hash->{IODev}{NAME}}{pendDev}} =
  3953. grep !/$name/,@{$modules{CUL_HM}{$hash->{IODev}{NAME}}{pendDev}};
  3954. }
  3955. CUL_HM_unQEntity($name,"qReqConf");
  3956. CUL_HM_unQEntity($name,"qReqStat");
  3957. CUL_HM_protState($hash,"Info_Cleared");
  3958. }
  3959. elsif($sect eq "msgErrors"){
  3960. delete $hash->{protResndFail};
  3961. delete $hash->{protResnd};
  3962. delete $hash->{protCmdDel};
  3963. delete $hash->{protNACK};
  3964. delete $hash->{protIOerr};
  3965. }
  3966. elsif($sect eq "rssi"){
  3967. delete $defs{$name}{helper}{rssi};
  3968. delete ($hash->{$_}) foreach (grep(/^rssi/,keys %{$hash}))
  3969. }
  3970. elsif($sect eq "attack"){
  3971. delete $defs{$name}{helper}{rssi};
  3972. delete ($hash->{$_}) foreach (grep(/^protErrIo(Id|Attack)/,keys %{$hash}));
  3973. delete $hash->{READINGS}{$_}
  3974. foreach (grep /^sabotageAttack/,keys %{$hash->{READINGS}});
  3975. }
  3976. }
  3977. }
  3978. elsif($cmd eq "defIgnUnknown") { ############################################
  3979. foreach (map {substr($_,8)}
  3980. grep /^unknown_......$/,
  3981. keys %{$hash->{READINGS}}){
  3982. if (!$modules{CUL_HM}{defptr}{$_}){
  3983. CommandDefine(undef,"unknown_$_ CUL_HM $_") ;
  3984. $attr{"unknown_$_"}{ignore} = 1;
  3985. }
  3986. delete $hash->{READINGS}{"unknown_$_"};
  3987. }
  3988. }
  3989. elsif($cmd eq "deviceRename") { #############################################
  3990. my $newName = $a[2];
  3991. my @chLst = ("device");# entry 00 is unsed
  3992. if ($roleV){
  3993. foreach(1..50){
  3994. push @chLst,$newName."_Btn".$_;
  3995. }
  3996. }
  3997. else{
  3998. my $mId = CUL_HM_getMId($hash);# set helper valiable and use result
  3999. foreach my $chantype (split(',',$culHmModel->{$mId}{chn})){
  4000. my ($chnTpName,$chnStart,$chnEnd) = split(':',$chantype);
  4001. my $chnNoTyp = 1;
  4002. for (my $chnNoAbs = $chnStart; $chnNoAbs <= $chnEnd;$chnNoAbs++){
  4003. my $chnId = $hash->{DEF}.sprintf("%02X",$chnNoAbs);
  4004. $chLst[$chnNoAbs] = $newName."_".$chnTpName.(($chnStart == $chnEnd)
  4005. ? ''
  4006. : '_'.sprintf("%02d",$chnNoTyp));
  4007. $chnNoTyp++;
  4008. }
  4009. }
  4010. }
  4011. foreach my $cd (grep /^channel_/,keys %{$hash}){
  4012. my $cName = InternalVal($name,$cd,"");
  4013. my $no = hex(substr($cd,8));
  4014. CommandRename(undef,$cName.' '.$chLst[$no]);
  4015. }
  4016. CommandRename(undef,$name.' '.$newName);#and the device itself
  4017. }
  4018. elsif($cmd eq "tempListTmpl") { #############################################
  4019. my $action = "verify";#defaults
  4020. my ($template,$fn);
  4021. for my $ax ($a[2],$a[3]){
  4022. next if (!$ax);
  4023. if ($ax =~ m/^(verify|restore)$/){
  4024. $action = $ax;
  4025. }
  4026. else{
  4027. $template = $ax;
  4028. }
  4029. }
  4030. ($fn,$template) = split(":",($template?$template
  4031. :AttrVal($name,"tempListTmpl",$name)));
  4032. if ($modules{HMinfo}){
  4033. if (!$template){ $template = HMinfo_tempListDefFn() .":$fn" ;}
  4034. else{ $template = HMinfo_tempListDefFn($fn).":$template";}
  4035. }
  4036. else{
  4037. if (!$template){ $template = "./tempList.cfg:$fn";}
  4038. else{ $template = "$fn:$template" ;}
  4039. }
  4040. my $ret = CUL_HM_tempListTmpl($name,$action,$template);
  4041. $ret = "verifed with no faults" if (!$ret && $action eq "verify");
  4042. return $ret;
  4043. }
  4044. elsif($cmd eq "tempTmplSet") { ##############################################
  4045. return "template missing" if (!defined $a[2]);
  4046. CommandAttr(undef, "$name tempListTmpl $a[2]");
  4047. my ($fn,$template) = split(":",AttrVal($name,"tempListTmpl",$name));
  4048. if ($modules{HMinfo}){
  4049. if (!$template){ $template = HMinfo_tempListDefFn() .":$fn" ;}
  4050. else{ $template = HMinfo_tempListDefFn($fn).":$template";}
  4051. }
  4052. else{
  4053. if (!$template){ $template = "./tempList.cfg:$fn";}
  4054. else{ $template = "$fn:$template" ;}
  4055. }
  4056. CUL_HM_tempListTmpl($name,"restore",$template);
  4057. }
  4058. elsif($cmd eq "templateDel") { ##############################################
  4059. return "template missing" if (!defined $a[2]);
  4060. my ($p,$t) = split(">",$a[2]);
  4061. HMinfo_templateDel($name,$t,$p) if (eval "defined(&HMinfo_templateDel)");
  4062. return;
  4063. }
  4064. elsif($cmd eq "virtual") { ##################################################
  4065. my (undef,undef,$maxBtnNo) = @a;
  4066. return "please give a number between 1 and 50"
  4067. if ($maxBtnNo < 1 ||$maxBtnNo > 50);# arbitrary - 255 should be max
  4068. return $name." already defines as ".$attr{$name}{subType}
  4069. if ($attr{$name}{subType} && $attr{$name}{subType} ne "virtual");
  4070. $attr{$name}{subType} = "virtual";
  4071. $attr{$name}{model} = "virtual_".$maxBtnNo
  4072. if (!$attr{$name}{model} ||$attr{$name}{model} =~ m/^virtual_/);
  4073. my $devId = $hash->{DEF};
  4074. for (my $btn=1;$btn <= $maxBtnNo;$btn++){
  4075. my $chnName = $name."_Btn".$btn;
  4076. my $chnId = $devId.sprintf("%02X",$btn);
  4077. CommandDefine(undef,"$chnName CUL_HM $chnId")
  4078. if (!$modules{CUL_HM}{defptr}{$chnId});
  4079. }
  4080. foreach my $channel (keys %{$hash}){# remove higher numbers
  4081. my $chNo;
  4082. $chNo = $1 if($channel =~ m/^channel_(.*)/);
  4083. next if (!defined($chNo));
  4084. CommandDelete(undef,$hash->{$channel})
  4085. if (hex($chNo) > $maxBtnNo);
  4086. }
  4087. CUL_HM_queueUpdtCfg($name);
  4088. CUL_HM_UpdtCentral($name) if ($md eq "CCU_FHEM");
  4089. }
  4090. elsif($cmd eq "update") { ###################################################
  4091. if ($md eq "ActionDetector"){
  4092. CUL_HM_ActCheck("ActionDetector");
  4093. }
  4094. else{
  4095. CUL_HM_UpdtCentral($name);
  4096. }
  4097. }
  4098. else{ #command which requires IO#########################
  4099. $id = CUL_HM_IoId($defs{$devName});
  4100. if(length($id) != 6 && $hash->{DEF} ne "000000" ){# have to try to find an IO $devName
  4101. CUL_HM_assignIO($defs{$devName});
  4102. $id = CUL_HM_IoId($defs{$devName});
  4103. }
  4104. return "no IO device identified" if(length($id) != 6 && $st ne 'virtual');
  4105. $nonIOcmd = 0;
  4106. }
  4107. return ("",1) if($nonIOcmd);# we are done already
  4108. #convert 'old' commands to current methods like regSet and regBulk...
  4109. # Unify the interface
  4110. if( $cmd eq "sign"){ ######################################################
  4111. splice @a,1,0,"regSet";# make hash,regSet,reg,value
  4112. }
  4113. elsif($cmd eq "unpair"){ ####################################################
  4114. splice @a,1,3, ("regSet","pairCentral","000000");
  4115. }
  4116. elsif($cmd eq "ilum") { ################################################# reg
  4117. return "$a[2] not specified. choose 0-15 for brightness" if ($a[2]>15);
  4118. return "$a[3] not specified. choose 0-127 for duration" if ($a[3]>127);
  4119. return "unsupported for channel, use $devName" if (!$roleD);
  4120. splice @a,1,3, ("regBulk","RegL_00.",sprintf("04:%02X",$a[2]),sprintf("08:%02X",$a[3]*2));
  4121. }
  4122. elsif($cmd eq "text") { ################################################# reg
  4123. my ($bn,$l1, $l2) = ($chn,$a[2],$a[3]); # Create CONFIG_WRITE_INDEX string
  4124. if ($roleD){# if used on device.
  4125. return "$a[2] is not a button number" if($a[2] !~ m/^\d*$/ || $a[2] < 1);
  4126. return "$a[3] is not on or off" if($a[3] !~ m/^(on|off)$/);
  4127. $bn = $a[2]*2-($a[3] eq "on" ? 0 : 1);
  4128. ($l1, $l2) = ($a[4],$a[5]);
  4129. $chn = sprintf("%02X",$bn)
  4130. }
  4131. else{
  4132. return "to many parameter. Try set $a[0] text $a[2] $a[3]" if($a[4]);
  4133. }
  4134. my $s = 54;
  4135. $l1 =~ s/\\_/ /g;
  4136. $l1 = substr($l1."\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0, 12);
  4137. $l1 =~ s/(.)/sprintf(" %02X:%02X",$s++,ord($1))/ge;
  4138. $s = 70;
  4139. $l2 =~ s/\\_/ /g;
  4140. $l2 = substr($l2."\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0, 12);
  4141. $l2 =~ s/(.)/sprintf(" %02X:%02X",$s++,ord($1))/ge;
  4142. @a = ($a[0],"regBulk","RegL_01.",split(" ",$l1.$l2));
  4143. }
  4144. elsif($cmd =~ m/^(displayMode|displayTemp|displayTempUnit|controlMode)/) { ##
  4145. if ($md =~ m/^(HM-CC-TC|ROTO_ZEL-STG-RM-FWT)/){#controlMode different for RT
  4146. splice @a,1,3, ("regSet",$a[1],$a[2]);
  4147. push @postCmds,"++803F$id${dst}0204".sprintf("%02X",CUL_HM_secSince2000());
  4148. }
  4149. }
  4150. elsif($cmd eq "partyMode") { ################################################
  4151. my ($eH,$eM,$days,$prep) = ("","","","");
  4152. if ($a[2] =~ m/^(prep|exec)$/){
  4153. $prep = $a[2];
  4154. splice @a,2,1;#remove prep
  4155. }
  4156. $days = $a[3];
  4157. ($eH,$eM) = split(':',$a[2]);
  4158. my ($s,$m,$h) = localtime();
  4159. return "$eH:$eM passed at $h:$m. Please enter time in the feature"
  4160. if ($days == 0 && ($h+($m/60))>=($eH+($eM/60)) );
  4161. return "$eM illegal - use 00 or 30 minutes only" if ($eM !~ m/^(00|30)$/);
  4162. return "$eH illegal - hour must be between 0 and 23" if ($eH < 0 || $eH > 23);
  4163. return "$days illegal - days must be between 0 and 200" if ($days < 0 || $days > 200);
  4164. $eH += 128 if ($eM eq "30");
  4165. my $cHash = CUL_HM_id2Hash($dst."02");
  4166. $cHash->{helper}{partyReg} = sprintf("61%02X62%02X0000",$eH,$days);
  4167. $cHash->{helper}{partyReg} =~ s/(..)(..)/ $1:$2/g;
  4168. if ($cHash->{READINGS}{"RegL_06."}){#remove old settings
  4169. $cHash->{READINGS}{"RegL_06."}{VAL} =~ s/ 61:.*//;
  4170. $cHash->{READINGS}{"RegL_06."}{VAL} =~ s/ 00:00//;
  4171. $cHash->{READINGS}{"RegL_06."}{VAL} .= $cHash->{helper}{partyReg};
  4172. }
  4173. else{
  4174. $cHash->{READINGS}{"RegL_06."}{VAL} = $cHash->{helper}{partyReg};
  4175. }
  4176. CUL_HM_pushConfig($hash,$id,$dst,2,"000000","00",6,
  4177. sprintf("61%02X62%02X",$eH,$days),$prep);
  4178. splice @a,1,3, ("regSet","controlMode","party");
  4179. splice @a,2,0, ($prep) if ($prep);
  4180. push @postCmds,"++803F$id${dst}0204".sprintf("%02X",CUL_HM_secSince2000());
  4181. }
  4182. $cmd = $a[1];# get converted command
  4183. #if chn cmd is executed on device but refers to a channel?
  4184. my $chnHash = (!$roleC && $modules{CUL_HM}{defptr}{$dst."01"})?
  4185. $modules{CUL_HM}{defptr}{$dst."01"}:$hash;
  4186. my $devHash = CUL_HM_getDeviceHash($hash);
  4187. my $state = "set_".join(" ", @a[1..(int(@a)-1)]);
  4188. return "device on readonly. $cmd disabled"
  4189. if($activeCmds{$cmd} && CUL_HM_getAttrInt($name,"readOnly") );
  4190. if ($cmd eq "raw") { #####################################################
  4191. return "Usage: set $a[0] $cmd data [data ...]" if(@a < 3);
  4192. $state = "";
  4193. my $msg = $a[2];
  4194. foreach my $sub (@a[3..$#a]) {
  4195. last if ($sub !~ m/^[A-F0-9]*$/);
  4196. $msg .= $sub;
  4197. }
  4198. CUL_HM_PushCmdStack($hash, $msg);
  4199. }
  4200. elsif($cmd eq "reset") { ####################################################
  4201. CUL_HM_PushCmdStack($hash,"++".$flag."11".$id.$dst."0400");
  4202. }
  4203. elsif($cmd eq "burstXmit") { ################################################
  4204. $state = "";
  4205. $hash->{helper}{prt}{brstWu}=1;# start burst wakeup
  4206. CUL_HM_SndCmd($hash,"++B112$id$dst");
  4207. }
  4208. elsif($cmd eq "statusRequest") { ############################################
  4209. my @chnIdList = CUL_HM_getAssChnIds($name);
  4210. foreach my $channel (@chnIdList){
  4211. my $chnNo = substr($channel,6,2);
  4212. CUL_HM_PushCmdStack($hash,"++".$flag.'01'.$id.$dst.$chnNo.'0E');
  4213. }
  4214. $state = "";
  4215. }
  4216. elsif($cmd eq "getSerial") { ################################################
  4217. CUL_HM_PushCmdStack($hash,'++'.$flag.'01'.$id.$dst.'0009');
  4218. $state = "";
  4219. }
  4220. elsif($cmd eq "getDevInfo") { ###############################################
  4221. $state = "";
  4222. my $sn = ReadingsVal($name,"D-serialNr","");
  4223. return "serial number unknown" if (! $sn);
  4224. CUL_HM_PushCmdStack($hash,'++8401'.$id.'000000010A'.uc(unpack('H*', $sn)));
  4225. }
  4226. elsif($cmd eq "getConfig") { ################################################
  4227. CUL_HM_unQEntity($name,"qReqConf");
  4228. CUL_HM_getConfig($hash);
  4229. $state = "";
  4230. }
  4231. elsif($cmd eq "peerBulk") { #################################################
  4232. $state = "";
  4233. my $pL = $a[2];
  4234. return "unknown action: $a[3] - use set or unset"
  4235. if ($a[3] && $a[3] !~ m/^(set|unset)/);
  4236. my $set = ($a[3] && $a[3] eq "unset")?"02":"01";
  4237. foreach my $peer (grep(!/^self/,split(',',$pL))){
  4238. last if($peer =~ m/^#/);
  4239. my $pID = CUL_HM_peerChId($peer,$dst);
  4240. return "unknown peer".$peer if (length($pID) != 8);# peer only to channel
  4241. my $pCh1 = substr($pID,6,2);
  4242. my $pCh2 = $pCh1;
  4243. if(($culHmSubTypeSets->{$st} &&$culHmSubTypeSets->{$st}{peerChan} )||
  4244. ($culHmModelSets->{$md} &&$culHmModelSets->{$md}{peerChan} )||
  4245. ($culHmChanSets->{$md.$chn} &&$culHmChanSets->{$md.$chn}{peerChan}) ){
  4246. $pCh2 = "00"; # button behavior
  4247. }
  4248. CUL_HM_PushCmdStack($hash,'++'.$flag.'01'.$id.$dst.$chn.$set.
  4249. substr($pID,0,6).$pCh1.$pCh2);
  4250. }
  4251. CUL_HM_qAutoRead($name,3);
  4252. }
  4253. elsif($cmd =~ m/^(regBulk|getRegRaw)$/) { ############################### reg
  4254. my ($list,$addr,$data,$peerID);
  4255. $state = "";
  4256. if ($cmd eq "regBulk"){
  4257. $list = $a[2];
  4258. $list =~ s/[\.]?RegL_//;
  4259. ($list,$peerID) = split('\.',$list);
  4260. # return "unknown list Number:".$list if(hex($list)>6);
  4261. }
  4262. elsif ($cmd eq "getRegRaw"){
  4263. ($list,$peerID) = ($a[2],$a[3]);
  4264. return "Enter valid List0-6" if (!defined $list || $list !~ m/^List([0-6])$/);
  4265. $list ='0'.$1;
  4266. }
  4267. # as of now only hex value allowed check range and convert
  4268. $peerID = CUL_HM_peerChId(($peerID?$peerID:"00000000"),$dst);
  4269. my $peerChn = ((length($peerID) == 8)?substr($peerID,6,2):"01");# have to split chan and id
  4270. $peerID = substr($peerID,0,6);
  4271. if($cmd eq "getRegRaw"){
  4272. if ($list eq "00"){
  4273. CUL_HM_PushCmdStack($hash,'++'.$flag.'01'.$id.$dst.'00040000000000');
  4274. }
  4275. else{# other lists are per channel
  4276. my @chnIdList = CUL_HM_getAssChnIds($name);
  4277. foreach my $channel (@chnIdList){
  4278. my $chnNo = substr($channel,6,2);
  4279. if ($list =~ m/^0[34]$/){#getPeers to see if list3 is available
  4280. CUL_HM_PushCmdStack($hash,'++'.$flag.'01'.$id.$dst.$chnNo.'03');
  4281. my $chnHash = CUL_HM_id2Hash($channel);
  4282. $chnHash->{helper}{getCfgList} = $peerID.$peerChn;#list3 regs
  4283. $chnHash->{helper}{getCfgListNo} = int($list);
  4284. }
  4285. else{
  4286. CUL_HM_PushCmdStack($hash,'++'.$flag.'01'.$id.$dst.$chnNo.'04'
  4287. .$peerID.$peerChn.$list);
  4288. }
  4289. }
  4290. }
  4291. }
  4292. elsif($cmd eq "regBulk"){;
  4293. my @adIn = @a;
  4294. shift @adIn;shift @adIn;shift @adIn;
  4295. my $adList;
  4296. foreach my $ad ( @adIn){
  4297. last if($ad =~ m/^#/);
  4298. ($addr,$data) = split(":",$ad);
  4299. $adList .= sprintf("%02X%02X",hex($addr),hex($data)) if ($addr ne "00");
  4300. return "wrong addr or data:".$ad if (hex($addr)>255 || hex($data)>255);
  4301. }
  4302. $chn = 0 if ($list == 0);
  4303. CUL_HM_pushConfig($hash,$id,$dst,hex($chn),$peerID,$peerChn,$list,$adList);
  4304. }
  4305. }
  4306. elsif($cmd eq "regSet") { ############################################### reg
  4307. #set <name> regSet [prep] <regName> <value> [<peerChn>]
  4308. #prep is internal use only. It allowes to prepare shadowReg only but supress
  4309. #writing. Application necessarily needs to execute writing subsequent.
  4310. my $prep = "";
  4311. if ($a[2] =~ m/^(prep|exec)$/){
  4312. $prep = $a[2];
  4313. splice @a,2,1;#remove prep
  4314. }
  4315. my (undef,undef,$regName,$data,$peerChnIn) = @a;
  4316. $state = "";
  4317. my @regArr = CUL_HM_getRegN($st,$md,($roleD?"00":""),($roleC?$chn:""));
  4318. return "$regName failed: supported register are ".join(" ",sort @regArr)
  4319. if (!grep /^$regName$/,@regArr );
  4320. my $reg = $culHmRegDefine->{$regName};
  4321. my $conv = $reg->{c};
  4322. return $st." - ".$regName # give some help
  4323. .($conv eq "lit"? " literal:".join(",",keys%{$reg->{lit}})." "
  4324. : " range:". $reg->{min}." to ".$reg->{max}.$reg->{u}
  4325. .($reg->{lit}?" special:".join(",",keys%{$reg->{lit}})." "
  4326. :""
  4327. )
  4328. )
  4329. .(($reg->{p} eq 'y')?" peer required":"")." : ".$reg->{t}."\n"
  4330. if ($data eq "?");
  4331. if ( $conv ne 'lit'
  4332. && $reg->{lit}
  4333. && defined $reg->{lit}{$data} ){
  4334. $data = $reg->{lit}{$data};#conv special value past to calculation
  4335. }
  4336. return "value:$data out of range $reg->{min} to $reg->{max} for Reg \""
  4337. .$regName."\""
  4338. if (!($conv =~ m/^(lit|hex|min2time)$/)&&
  4339. ($data < $reg->{min} ||$data > $reg->{max})); # none number
  4340. return"invalid value. use:". join(",",sort keys%{$reg->{lit}})
  4341. if ($conv eq 'lit' && !defined($reg->{lit}{$data}));
  4342. if ($conv ne 'lit' && $reg->{lit} && $reg->{lit}{$data}){
  4343. $data = $reg->{lit}{$data}; #conv special value prior to calculation
  4344. }
  4345. $data *= $reg->{f} if($reg->{f});# obey factor befor possible conversion
  4346. if (!$conv){;# do nothing
  4347. }elsif($conv eq "fltCvT" ){$data = CUL_HM_fltCvT($data);
  4348. }elsif($conv eq "fltCvT60"){$data = CUL_HM_fltCvT60($data);
  4349. }elsif($conv eq "min2time"){$data = CUL_HM_time2min($data);
  4350. }elsif($conv eq "m10s3") {$data = $data*10-3;
  4351. }elsif($conv eq "hex") {$data = hex($data);
  4352. }elsif($conv eq "lit") {$data = $reg->{lit}{$data};
  4353. }else{return " conv undefined - please contact admin";
  4354. }
  4355. my $addr = int($reg->{a}); # bit location later
  4356. my $list = $reg->{l};
  4357. my $bit = ($reg->{a}*10)%10; # get fraction
  4358. my $dLen = $reg->{s}; # datalength in bit
  4359. $dLen = int($dLen)*8+(($dLen*10)%10);
  4360. # only allow it level if length less then one byte!!
  4361. return "partial Word error: ".$dLen if($dLen != 8*int($dLen/8) && $dLen>7);
  4362. no warnings qw(overflow portable);
  4363. my $mask = (0xffffffff>>(32-$dLen));
  4364. use warnings qw(overflow portable);
  4365. my $dataStr = substr(sprintf("%08X",($data & $mask) << $bit),
  4366. 8-int($reg->{s}+0.99)*2,);
  4367. my ($lChn,$peerId,$peerChn) = ($chn,"000000","00");
  4368. # if (($list == 3) ||($list == 4) # peer is necessary for list 3/4
  4369. if ($reg->{p} eq 'y' # peer is necessary
  4370. ||($peerChnIn)) {# and if requested by user
  4371. return "Peer not specified" if ($peerChnIn eq "");
  4372. $peerId = CUL_HM_peerChId($peerChnIn,$dst);
  4373. ($peerId,$peerChn) = unpack 'A6A2',$peerId.'01';
  4374. if ($list == 4 &&
  4375. !AttrVal($name,"peerIDs",undef)){####check this code#################
  4376. $peerChn = "00";
  4377. }
  4378. return "Peer not valid" if (length ($peerId) < 6);
  4379. }
  4380. elsif($list == 0){
  4381. $lChn = "00";
  4382. }
  4383. else{ #if($list == 1/5/6){
  4384. $lChn = "01" if ($chn eq "00"); #by default select chan 01 for device
  4385. }
  4386. my $addrData;
  4387. if ($dLen < 8){# fractional byte see whether we have stored the register
  4388. #read full 8 bit!!!
  4389. my $rName = CUL_HM_id2Name($dst.$lChn);
  4390. $rName =~ s/_chn-\d\d$//;
  4391. my $curVal = CUL_HM_getRegFromStore($rName,$addr,$list,$peerId.$peerChn);
  4392. if ($curVal !~ m/^(set_|)(\d+)$/){
  4393. return "peer required for $regName" if ($curVal =~ m/peer/);
  4394. return "cannot calculate value. Please issue set $name getConfig first - $curVal";
  4395. }
  4396. $curVal = $2; # we expect one byte in int, strap 'set_' possibly
  4397. $data = ($curVal & (~($mask<<$bit)))|($data<<$bit);
  4398. $addrData.=sprintf("%02X%02X",$addr,$data);
  4399. }
  4400. else{
  4401. for (my $cnt = 0;$cnt<int($reg->{s}+0.99);$cnt++){
  4402. $addrData.=sprintf("%02X",$addr+$cnt).substr($dataStr,$cnt*2,2);
  4403. }
  4404. }
  4405. my $cHash = CUL_HM_id2Hash($dst.($lChn eq '00'?"":$lChn));
  4406. $cHash = $hash if (!$cHash);
  4407. CUL_HM_pushConfig($cHash,$id,$dst,hex($lChn),$peerId,hex($peerChn),$list
  4408. ,$addrData,$prep);
  4409. CUL_HM_PushCmdStack($hash,$_) foreach(@postCmds);#ugly commands after regSet
  4410. }
  4411. elsif($cmd eq "level") { ####################################################
  4412. #level =>"<level> <relockDly> <speed>..."
  4413. my (undef,undef,$lvl,$rLocDly,$speed) = @a;
  4414. $rLocDly = 111600 if (!defined($rLocDly)||$rLocDly eq "ignore");# defaults
  4415. $speed = 30 if (!defined($speed));
  4416. $lvl = 127.5 if ($lvl eq "lock");
  4417. return "please enter level 0 to 100 or lock"
  4418. if ( !defined($lvl)
  4419. || $lvl !~ m/^\d*\.?\d?$/
  4420. || ($lvl > 100 && $lvl != 127.5));
  4421. return "reloclDelay range 0..65535 or ignore"
  4422. if ( $rLocDly > 111600 ||
  4423. ($rLocDly < 0.1 && $rLocDly ne '0' ));
  4424. return "select speed range 0 to 100" if ( $speed > 100);
  4425. $rLocDly = CUL_HM_encodeTime8($rLocDly);# calculate hex value
  4426. CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.'81'.$chn.
  4427. sprintf("%02X%02s%02X",$lvl*2,$rLocDly,$speed*2));
  4428. }
  4429. elsif($cmd =~ m/^(on|off|toggle)$/) { #######################################
  4430. my $lvlInv = (AttrVal($name, "param", "") =~ m/levelInverse/)?1:0;
  4431. $hash->{helper}{dlvl} = ( $cmd eq 'off'||
  4432. ($cmd eq 'toggle' &&CUL_HM_getChnLvl($name) != 0))
  4433. ? ($lvlInv?'C8':'00')
  4434. : ($lvlInv?'00':'C8');
  4435. my(undef,$lvlMax)=split",",AttrVal($name, "levelRange", "0,100");
  4436. $hash->{helper}{dlvl} = sprintf("%02X",$lvlMax*2) if ($hash->{helper}{dlvl} eq 'C8');
  4437. if ($md eq "HM-LC-Ja1PBU-FM"){ $hash->{helper}{dlvlCmd} = "++$flag"."11$id$dst"."80$chn$hash->{helper}{dlvl}"."CA";}
  4438. else{ $hash->{helper}{dlvlCmd} = "++$flag"."11$id$dst"."02$chn$hash->{helper}{dlvl}".'0000';}
  4439. CUL_HM_PushCmdStack($hash,$hash->{helper}{dlvlCmd});
  4440. $hash = $chnHash; # report to channel if defined
  4441. }
  4442. elsif($cmd eq "toggleDir") { ################################################
  4443. if ($hash->{helper}{dir}{cur} && $hash->{helper}{dir}{cur} ne "err"){
  4444. my $old = $hash->{helper}{dir}{cur};
  4445. $hash->{helper}{dir}{cur} = $hash->{helper}{dir}{cur} eq "stop" ?(($hash->{helper}{dir}{rct}
  4446. && $hash->{helper}{dir}{rct} eq "up")?"down"
  4447. :"up")
  4448. :"stop";
  4449. $hash->{helper}{dir}{rct} = $old;
  4450. }
  4451. else{
  4452. $hash->{helper}{dir}{rct} = "stop";
  4453. $hash->{helper}{dir}{cur} = "up";
  4454. }
  4455. if ($hash->{helper}{dir}{cur} eq "up" ){
  4456. $hash->{helper}{dlvl} = "C8";
  4457. $hash->{helper}{dlvlCmd} = "++$flag"."11$id$dst"."02$chn".'C80000';
  4458. CUL_HM_PushCmdStack($hash,$hash->{helper}{dlvlCmd});
  4459. }elsif ($hash->{helper}{dir}{cur} eq "down"){
  4460. $hash->{helper}{dlvl} = "00";
  4461. $hash->{helper}{dlvlCmd} = "++$flag"."11$id$dst"."02$chn".'000000';
  4462. CUL_HM_PushCmdStack($hash,$hash->{helper}{dlvlCmd});
  4463. }else {
  4464. delete $hash->{helper}{dlvl};
  4465. CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.'03'.$chn);
  4466. }
  4467. }
  4468. elsif($cmd =~ m/^(on-for-timer|on-till)$/) { ################################
  4469. my (undef,undef,$duration,$ramp) = @a; #date prepared extention to entdate
  4470. if ($cmd eq "on-till"){
  4471. # to be extended to handle end date as well
  4472. my ($info,$eH,$eM,$eSec) = GetTimeSpec($duration);
  4473. return "enter time: $info" if($info && $info !~ m/Wrong/);
  4474. $eSec += $eH*3600 + $eM*60;
  4475. my @lt = localtime;
  4476. my $ltSec = $lt[2]*3600+$lt[1]*60+$lt[0];# actually strip of date
  4477. $eSec += 3600*24 if ($ltSec > $eSec); # go for the next day
  4478. $duration = $eSec - $ltSec;
  4479. }
  4480. return "please enter the duration in seconds"
  4481. if (!defined $duration || $duration !~ m/^[+-]?\d+(\.\d+)?$/);
  4482. my $tval = CUL_HM_encodeTime16($duration);# onTime 0.0..85825945.6, 0=forever
  4483. # return "timer value to low" if ($tval eq "0000"); does it work for all if "0000"?
  4484. $tval = "" if ($tval eq "0000");
  4485. $ramp = ($ramp && $st eq "dimmer") ? CUL_HM_encodeTime16($ramp)
  4486. :($tval eq "" ? ""
  4487. : "0000");
  4488. delete $hash->{helper}{dlvl};#stop desiredLevel supervision
  4489. $hash->{helper}{stateUpdatDly} = ($duration>120)?$duration:120;
  4490. my(undef,$lvlMax)=split",",AttrVal($name, "levelRange", "0,100");
  4491. $lvlMax = sprintf("%02X",$lvlMax*2);
  4492. CUL_HM_PushCmdStack($hash,"++${flag}11$id${dst}02${chn}$lvlMax$ramp$tval");
  4493. $hash = $chnHash; # report to channel if defined
  4494. }
  4495. elsif($cmd eq "alarmLevel") { ###############################################
  4496. #level =>"[disarmed|armExtSens|armAll|armBlocked]"
  4497. my %lvlSet = (disarmed=>"00",armExtSens=>"32",armAll=>"C8",armBlocked=>"FF");
  4498. my (undef,undef,$lvl,$onTime) = (@a,0);#set ontime to 0 if not given.
  4499. $lvl = $lvlSet{$lvl};
  4500. return "please enter the onTime in seconds"
  4501. if ($onTime !~ m/^[+-]?\d+(\.\d+)?$/);
  4502. my $tval = CUL_HM_encodeTime16($onTime);# onTime 0.0..85825945.6, 0=forever
  4503. $tval = "" if ($tval eq "0000");
  4504. CUL_HM_PushCmdStack($hash,"++${flag}11$id${dst}02${chn}${lvl}0000$tval");
  4505. }
  4506. elsif($cmd eq "lock") { #####################################################
  4507. CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.'800100FF'); # LEVEL_SET
  4508. }
  4509. elsif($cmd eq "unlock") { ###################################################
  4510. my $tval = (@a > 2) ? int($a[2]) : 0;
  4511. my $delay = ($tval > 0) ? CUL_HM_encodeTime8($tval) : "FF"; # RELOCK_DELAY (FF=never)
  4512. CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.'800101'.$delay);# LEVEL_SET
  4513. }
  4514. elsif($cmd eq "open") { #####################################################
  4515. my $tval = (@a > 2) ? int($a[2]) : 0;
  4516. my $delay = ($tval > 0) ? CUL_HM_encodeTime8($tval) : "FF"; # RELOCK_DELAY (FF=never)
  4517. CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.'8001C8'.$delay);# OPEN
  4518. }
  4519. elsif($cmd eq "inhibit") { ##################################################
  4520. return "$a[2] is not on or off" if($a[2] !~ m/^(on|off)$/);
  4521. my $val = ($a[2] eq "on") ? "01" : "00";
  4522. CUL_HM_UpdtReadSingle($hash,"inhibit","set_$a[2]",1);
  4523. CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.$val.$chn); # SET_LOCK
  4524. }
  4525. elsif($cmd =~ m/^(up|down|pct|old)$/) { #####################################
  4526. my ($lvl,$tval,$rval,$duration) = (($cmd eq "old"?"old":($a[2]?$a[2]:0))
  4527. ,"","",0);
  4528. my($lvlMin,$lvlMax) = split",",AttrVal($name, "levelRange", "0,100");
  4529. my $lvlInv = (AttrVal($name, "param", "") =~ m/levelInverse/)?1:0;
  4530. if ($lvl eq "old"){#keep it - it means "old value"
  4531. }
  4532. else{
  4533. $lvl =~ s/(\d*\.?\d*).*/$1/;
  4534. return "level not given" if(!defined $lvl);
  4535. if ($cmd eq "pct"){
  4536. }
  4537. else{#dim [<changeValue>] ... [ontime] [ramptime]
  4538. $lvl = 10 if (!defined $a[2]); #set default step
  4539. $lvl = -1*$lvl if (($cmd eq "down" && !$lvlInv)||
  4540. ($cmd ne "down" && $lvlInv));
  4541. $lvl += CUL_HM_getChnLvl($name);
  4542. }
  4543. $lvl = $lvlMin + $lvl*($lvlMax-$lvlMin)/100; # relativ to range
  4544. $lvl = ($lvl > $lvlMax)?$lvlMax:(($lvl <= $lvlMin)?0:$lvl);
  4545. }
  4546. if ($st =~ m/^(dimmer|rgb)$/){# at least blind cannot stand ramp time...
  4547. if (!$a[3]){
  4548. $tval = "FFFF";
  4549. $duration = 0;
  4550. }
  4551. elsif ($a[3] =~ m/(..):(..):(..)/){
  4552. my ($eH,$eM,$eSec) = ($1,$2,$3);
  4553. $eSec += $eH*3600 + $eM*60;
  4554. my @lt = localtime;
  4555. my $ltSec = $lt[2]*3600+$lt[1]*60+$lt[0];# actually strip of date
  4556. $eSec += 3600*24 if ($ltSec > $eSec); # go for the next day
  4557. $duration = $eSec - $ltSec;
  4558. $tval = CUL_HM_encodeTime16($duration);
  4559. }
  4560. else{
  4561. $duration = $a[3];
  4562. $tval = CUL_HM_encodeTime16($duration);# onTime 0.05..85825945.6, 0=forever
  4563. }
  4564. $rval = CUL_HM_encodeTime16((@a > 4)?$a[4]:2.5);# rampTime 0.0..85825945.6, 0=immediate
  4565. $hash->{helper}{stateUpdatDly} = ($duration>120)?$duration:120;
  4566. }
  4567. # store desiredLevel in and its Cmd in case we have to repeat
  4568. my $plvl = ($lvl eq "old")?"C9"
  4569. :sprintf("%02X",(($lvlInv)?100-$lvl :$lvl)*2);
  4570. if (($tval ne "FFFF") || $lvl eq "old"){
  4571. delete $hash->{helper}{dlvl};#stop desiredLevel supervision
  4572. }
  4573. else{
  4574. $hash->{helper}{dlvl} = $plvl;
  4575. }
  4576. if ($md eq "HM-LC-Ja1PBU-FM"){ $hash->{helper}{dlvlCmd} = "++$flag"."11$id$dst"."80$chn$plvl"."CA";}
  4577. else{ $hash->{helper}{dlvlCmd} = "++$flag"."11$id$dst"."02$chn$plvl$rval$tval";}
  4578. CUL_HM_PushCmdStack($hash,$hash->{helper}{dlvlCmd});
  4579. $state = "set_".$lvl;
  4580. CUL_HM_UpdtReadSingle($hash,"level",$state,1);
  4581. }
  4582. elsif($cmd =~ m/^(pctSlat|pctLvlSlat)$/) { ##################################
  4583. # pctSlat =>"[0-100]|old|noChng"
  4584. # pctLvlSlat =>"-value-|old|noChng -slatValue-|old|noChng"
  4585. my ($lvl,$slat,$plvl,$pslat);
  4586. return "param missing " if (!defined $a[2]);
  4587. if ($cmd eq "pctSlat"){
  4588. $slat = $a[2];
  4589. $lvl = "noChng";
  4590. }
  4591. else{#"pctLvlSlat"
  4592. $slat = defined $a[3] ? $a[3] : "noChng";
  4593. $lvl = $a[2];
  4594. }
  4595. #--- calc slat----
  4596. if ($slat eq "old") {$pslat = "C9"}
  4597. elsif ($slat eq "noChng"){$pslat = "CA"}
  4598. else{ $slat =~ s/(\d*\.?\d*).*/$1/;
  4599. return "Value $a[2] not allowed for slat" if ($slat > 100);
  4600. $pslat = sprintf("%02X",$slat*2);
  4601. CUL_HM_UpdtReadSingle($hash,"levelSlat","set_".$slat,1);
  4602. }
  4603. #--- calc level----
  4604. if ($lvl eq "old") {$plvl = "C9"}
  4605. elsif ($lvl eq "noChng"){$plvl = "CA"}
  4606. else{
  4607. my $lvlInv = (AttrVal($name, "param", "") =~ m/levelInverse/) ? 1 : 0;
  4608. my($lvlMin,$lvlMax) = split",",AttrVal($name, "levelRange", "0,100");
  4609. $lvl = $lvlMin + $lvl*($lvlMax-$lvlMin)/100; # relativ to range
  4610. $lvl = ($lvl > $lvlMax) ? $lvlMax
  4611. : (($lvl <= $lvlMin)?0:$lvl);
  4612. $lvl =~ s/(\d*\.?\d*).*/$1/;
  4613. $plvl = sprintf("%02X",(($lvlInv) ? 100-$lvl : $lvl)*2);
  4614. CUL_HM_UpdtReadSingle($hash,"level","set_".$lvl,1);
  4615. $state = "set_".$lvl;
  4616. }
  4617. #--- execute----
  4618. CUL_HM_PushCmdStack($hash,"++$flag"."11$id$dst"."80${chn}$plvl$pslat");
  4619. }
  4620. elsif($cmd eq "stop") { #####################################################
  4621. delete $hash->{helper}{dlvl};#stop desiredLevel supervision
  4622. CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.'03'.$chn);
  4623. }
  4624. elsif($cmd eq "setRepeat") { ################################################
  4625. # setRepeat => "[no1..36] <sendName> <recName> [bdcast-yes|no]"}
  4626. $state = "";
  4627. my (undef,undef,$eNo,$sId,$rId,$bCst) = @a;
  4628. my ($pattern,$cnt);
  4629. my $repPeers = AttrVal($name,"repPeers",undef);
  4630. my @rPeer;
  4631. @rPeer = split ",",$repPeers;
  4632. if ($eNo eq "setAll"){
  4633. return " too many entries in repPeers" if (int(@rPeer) > 36);
  4634. return "setAll: attr repPeers undefined" if (!defined $repPeers);
  4635. my $entry = 0;
  4636. foreach my $repData (@rPeer){
  4637. $entry++;
  4638. my ($s,$d,$b) =split":",$repData;
  4639. $s = CUL_HM_name2Id($s);
  4640. $d = CUL_HM_name2Id($d);
  4641. return "attr repPeers entry $entry irregular:$repData"
  4642. if (!$s || !$d || !$b
  4643. || $s !~ m/(^[0-9A-F]{6})$/
  4644. || $d !~ m/(^[0-9A-F]{6})$/
  4645. || $b !~ m/^[yn]$/
  4646. );
  4647. $pattern .= $s.$d.(($b eq "n")?"00":"01");
  4648. }
  4649. while ($entry < 36){
  4650. $entry++;
  4651. $pattern .= "000000"."000000"."00";
  4652. }
  4653. $cnt = 1;# set first address byte
  4654. }
  4655. else{
  4656. return "entry must be between 1 and 36" if ($eNo < 1 || $eNo > 36);
  4657. my $sndID = CUL_HM_name2Id($sId);
  4658. my $recID = CUL_HM_name2Id($rId);
  4659. if ($sndID !~ m/(^[0-9A-F]{6})$/){$sndID = AttrVal($sId,"hmId","");};
  4660. if ($recID !~ m/(^[0-9A-F]{6})$/){$recID = AttrVal($rId,"hmId","");};
  4661. return "sender ID $sId unknown:".$sndID if ($sndID !~ m/(^[0-9A-F]{6})$/);
  4662. return "receiver ID $rId unknown:".$recID if ($recID !~ m/(^[0-9A-F]{6})$/);
  4663. return "broadcast must be yes or now" if ($bCst !~ m/^(yes|no)$/);
  4664. $pattern = $sndID.$recID.(($bCst eq "no")?"00":"01");
  4665. $cnt = ($eNo-1)*7+1;
  4666. $rPeer[$eNo-1] = "$sId:$rId:".(($bCst eq "no")?"n":"y");
  4667. $attr{$name}{repPeers} = join",",@rPeer;
  4668. }
  4669. my $addrData;
  4670. foreach ($pattern =~ /(.{2})/g){
  4671. $addrData .= sprintf("%02X%s",$cnt++,$_);
  4672. }
  4673. CUL_HM_pushConfig($hash, $id, $dst, 1,0,0,2, $addrData);
  4674. }
  4675. elsif($cmd eq "display") { ##################################################
  4676. my (undef,undef,undef,$t,$c,$u,$snd,$blk,$symb) = @_;
  4677. return "cmd only possible for device or its display channel"
  4678. if ($roleC && $chn ne "12");
  4679. my %symbol=(off => 0x0000,
  4680. bulb =>0x0100,switch =>0x0200,window =>0x0400,door=>0x0800,
  4681. blind=>0x1000,scene =>0x2000,phone =>0x4000,bell=>0x8000,
  4682. clock=>0x0001,arrowUp=>0x0002,arrowDown=>0x0004);
  4683. my %light=(off=>0,on=>1,slow=>2,fast=>3);
  4684. my %unit=(off =>0,Proz=>1,Watt=>2,x3=>3,C=>4,x5=>5,x6=>6,x7=>7,
  4685. F=>8,x9=>9,x10=>10,x11=>11,x12=>12,x13=>13,x14=>14,x15=>15);
  4686. my @symbList = split(',',$symb);
  4687. my $symbAdd = 0;
  4688. foreach my $symb (@symbList){
  4689. if (!defined($symbol{$symb})){# wrong parameter
  4690. return "'$symb ' unknown. Select one of ".join(" ",sort keys(%symbol));
  4691. }
  4692. $symbAdd |= $symbol{$symb};
  4693. }
  4694. return "$c not specified. Select one of [comma|no]"
  4695. if ($c ne "comma" && $c ne "no");
  4696. return "'$u' unknown. Select one of ".join(" ",sort keys(%unit))
  4697. if (!defined($unit{$u}));
  4698. return "'$snd' unknown. Select one of [off|1|2|3]"
  4699. if ($snd ne "off" && $snd > 3);
  4700. return "'$blk' unknown. Select one of ".join(" ",sort keys(%light))
  4701. if (!defined($light{$blk}));
  4702. my $beepBack = $snd | $light{$blk}*4;
  4703. $symbAdd |= 0x0008 if ($c eq "comma");
  4704. $symbAdd |= ($unit{$u} * 16);
  4705. my $text = sprintf("%5.5s",$t);#pad left with space
  4706. $text = uc(unpack("H*",$text));
  4707. CUL_HM_PushCmdStack($hash,sprintf("++%s11%s%s8012%s%04X%02X",
  4708. $flag,$id,$dst,$text,$symbAdd,$beepBack));
  4709. }
  4710. elsif($cmd =~ m/^(alarm|service)$/) { #######################################
  4711. return "$a[2] must be below 255" if ($a[2] >255 );
  4712. $chn = 18 if ($chn eq "01");
  4713. my $subtype = ($cmd eq "alarm")?"81":"82";
  4714. CUL_HM_PushCmdStack($hash,
  4715. sprintf("++%s11%s%s%s%s%02X",$flag,$id,$dst,$subtype,$chn, $a[2]));
  4716. }
  4717. elsif($cmd eq "led") { ######################################################
  4718. if ($md eq "HM-OU-LED16"){
  4719. my %color=(off=>0,red=>1,green=>2,orange=>3);
  4720. if ($roleD){# command called for a device, not a channel
  4721. my $col4all;
  4722. if (defined($color{$a[2]})){
  4723. $col4all = sprintf("%02X",$color{$a[2]}*85);#Color for 4 LEDS
  4724. $col4all = $col4all.$col4all.$col4all.$col4all;#and now for 16
  4725. }
  4726. elsif ($a[2] =~ m/^[A-Fa-f0-9]{1,8}$/i){
  4727. $col4all = sprintf("%08X",hex($a[2]));
  4728. }
  4729. else{
  4730. return "$a[2] unknown. use hex or: ".join(" ",sort keys(%color));
  4731. }
  4732. CUL_HM_UpdtReadBulk($hash,1,"color:".$col4all,
  4733. "state:set_".$col4all);
  4734. CUL_HM_PushCmdStack($hash,"++".$flag."11".$id.$dst."8100".$col4all);
  4735. }
  4736. else{# operating on a channel
  4737. return "$a[2] unknown. use: ".join(" ",sort keys(%color))
  4738. if (!defined($color{$a[2]}) );
  4739. CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.'80'.$chn.'0'.$color{$a[2]});
  4740. }
  4741. }
  4742. elsif($md =~ m/^HM-OU-CFM?-PL/){
  4743. my %color = (redL =>18,greenL =>34,orangeL =>50,
  4744. redS =>17,greenS =>33,orangeS =>49,
  4745. pause=>01);
  4746. my @itemList = split(',',$a[2]);
  4747. my $repeat = (defined $a[3] && $a[3] =~ m/^(\d+)$/)?$a[3]:1;
  4748. my $itemCnt = int(@itemList);
  4749. return "no more then 10 entries please" if ($itemCnt>10);
  4750. return "at least one entry must be entered" if ($itemCnt<1);
  4751. return "repetition $repeat out of range [1..255]"
  4752. if($repeat < 1 || $repeat > 255);
  4753. #<entries><multiply><MP3><MP3>
  4754. my $msgBytes = sprintf("01%02X",$repeat);
  4755. foreach my $led (@itemList){
  4756. if (!$color{$led} ){# wrong parameter
  4757. return "'$led' unknown. use: ".join(" ",sort keys(%color));
  4758. }
  4759. $msgBytes .= sprintf("%02X",$color{$led});
  4760. }
  4761. $msgBytes .= "01" if ($itemCnt == 1 && $repeat == 1);#add pause to term LED
  4762. # need to fill up empty locations for LED channel
  4763. $msgBytes = substr($msgBytes."000000000000000000",0,(10+2)*2);
  4764. CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.'80'.$chn.$msgBytes);
  4765. }
  4766. elsif($md =~ m/^HM-OU-CFM?-TW/){
  4767. my %color = (redL =>18,greenL =>34,yellowL =>50,blueL =>66, violettL => 82, cyanL => 98, whiteL =>114,
  4768. redS =>17,greenS =>33,yellowS =>49,blueS =>65, violettS => 81, cyanS => 97, whiteS =>113,
  4769. pause=>2);
  4770. my @itemList = split(',',$a[2]);
  4771. my $repeat = (defined $a[3] && $a[3] =~ m/^(\d+)$/)?$a[3]:1;
  4772. my $duration = (defined $a[4] && $a[4] =~ m/^(\d+)$/)?$a[4]:10800;
  4773. my $itemCnt = int(@itemList);
  4774. return "enter at least one and up to 10 items" if ($itemCnt < 1 || $itemCnt > 10);
  4775. return "repetition $repeat out of range [1..255]" if ($repeat < 1 || $repeat > 255);
  4776. return "duration $duration out of range [1..10800]" if ($duration < 1 || $duration > 10800);
  4777. my $msgBytes = sprintf("01%02X",$repeat);
  4778. foreach my $led (@itemList){
  4779. return "'$led' unknown. use: ".join(" ",sort keys(%color)) if (!$color{$led} );# wrong parameter;
  4780. $msgBytes .= sprintf("%02X",$color{$led});
  4781. }
  4782. $msgBytes .= "01" if ($itemCnt == 1 && $repeat == 1);#add pause to term LED
  4783. # need to fill up empty locations for LED channel
  4784. $msgBytes = substr($msgBytes."00000000000000000000",0,(10+2)*2);
  4785. if ($duration < 10800) {
  4786. $msgBytes .= sprintf("%02X%02X",($duration & 0x00ff), ($duration & 0xff00)>>8);
  4787. }
  4788. CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.'80'.$chn.$msgBytes);
  4789. }
  4790. else{
  4791. return "device for command cannot be identified";
  4792. }
  4793. }
  4794. elsif($cmd eq "brightCol") { ################################################
  4795. my (undef,undef,$bright,$colVal,$duration,$ramp) = @a; #date prepared extention to entdate
  4796. return "cmd requires brightness[0..100] step 0.5, color[0..100] step 0.5, duration, ramptime" if (!defined $ramp);
  4797. return "please enter the duration in seconds" if (!defined $duration || $duration !~ m/^[+-]?\d+(\.\d+)?$/);
  4798. ($bright,$colVal) = (int($bright*2),int($colVal*2));# convert percent to [0..200]
  4799. return "obey range for brightness[0..100] color[0..100]" if ( $bright < 0 or $bright > 200
  4800. or $colVal < 0 or $colVal > 200);
  4801. my $tval = CUL_HM_encodeTime16($duration);# onTime 0.0..85825945.6, 0=forever
  4802. $ramp = CUL_HM_encodeTime16($ramp);
  4803. CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.'80'.$chn.
  4804. sprintf("%02X%02X",$bright,$colVal).$ramp.$tval);
  4805. }
  4806. elsif($cmd eq "color") { ####################################################
  4807. my (undef,undef,$colVal) = @a; #date prepared extention to entdate
  4808. return "cmd requires color[0..100] step 0.5" if (!defined $colVal
  4809. ||$colVal < 0 ||$colVal > 100);
  4810. $colVal = int($colVal*2);# convert percent to [0..200]
  4811. CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.'02'.$chn.
  4812. sprintf("%02X",$colVal)."00A0");
  4813. }
  4814. elsif($cmd eq "brightAuto") { ###############################################
  4815. my (undef,undef,$bright,$colProg,$min,$max,$duration,$ramp) = @a; #date prepared extention to entdate
  4816. return "please enter the duration in seconds"
  4817. if (defined $duration && $duration !~ m/^[+-]?\d+(\.\d+)?$/);
  4818. return "at least bright and colorprogramm need to be set" if (!defined $colProg);
  4819. my $tval;
  4820. $tval = (!defined $duration) ? "" : CUL_HM_encodeTime16($duration);# onTime 0.0..85825945.6, 0=forever
  4821. $ramp = (!defined $ramp) ? "" : CUL_HM_encodeTime16($ramp) ;
  4822. $min = (!defined $min) ? "" : sprintf("%02X",$min) ;
  4823. $max = (!defined $max) ? "" : sprintf("%02X",$max) ;
  4824. CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.'81'.$chn.
  4825. sprintf("%02X%02X",$bright,$colProg).$min.$max.$ramp.$tval);
  4826. }
  4827. elsif($cmd eq "colProgram") { ###############################################
  4828. my (undef,undef,$colProg) = @a; #date prepared extention to entdate
  4829. return "cmd requires a colorProgram[0..255]" if (!defined $colProg
  4830. ||$colProg < 0 ||$colProg > 255);
  4831. CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.'02'.$chn.
  4832. sprintf("%02X",$colProg)."00A0");
  4833. }
  4834. elsif($cmd eq "playTone") { #################################################
  4835. my $msg;
  4836. if (!defined $a[2]){
  4837. return "please enter parameter";
  4838. }
  4839. elsif ($a[2] eq 'replay'){
  4840. $msg = ReadingsVal($chnHash->{NAME},".lastTone","");
  4841. }
  4842. else{
  4843. my @itemList = split(',',$a[2]);
  4844. my $repeat = (defined $a[3] && $a[3] =~ m/^(\d+)$/)?$a[3]:1;
  4845. my $itemCnt = int(@itemList);
  4846. my $volume = (defined $a[4] && $a[4] =~ m/^(\d+)$/)?$a[4]:10;
  4847. my $duration = (defined $a[5] && $a[5] =~ m/^(\d+\.)?\d+$/)?$a[5]:108000;
  4848. return "no more than 10 entries please" if ($itemCnt > 10);
  4849. return "repetition $repeat out of range [1..255]" if ($repeat < 1 || $repeat > 255);
  4850. return "volume $volume out of range [0..10]" if ($volume < 0 || $volume > 10);
  4851. return "duration $duration out of range [0.1..108000]" if ($duration < 0.1 || $duration > 108000);
  4852. #<volume><multiply><MP3><MP3>
  4853. my $msgBytes = sprintf("%02X%02X",$volume*20,$repeat);
  4854. foreach my $mp3 (@itemList){
  4855. return "input: $mp3 is not an integer below 255"
  4856. if (!defined $mp3 || $mp3 !~ /^[+-]?\d+$/ || $mp3 > 255);
  4857. $msgBytes .= sprintf("%02X",$mp3);
  4858. }
  4859. # need to fill up empty locations for MP3 numbers
  4860. $msgBytes = substr($msgBytes."00000000000000000000",0,(10+2)*2);
  4861. # add duration as a float value
  4862. #$duration = CUL_HM_decodeTime16($duration * 10);# need to be tested
  4863. my $exponent = 0;
  4864. my $mantisse = $duration * 10;
  4865. while ($mantisse >= 2048) {
  4866. $mantisse = $mantisse >> 1;
  4867. $exponent++;
  4868. }
  4869. $duration = $mantisse << 5 | $exponent;
  4870. $msgBytes .= sprintf("%04X", $duration);
  4871. $msg = '++'.$flag.'11'.$id.$dst.'80'.$chn.$msgBytes;
  4872. CUL_HM_UpdtReadSingle($chnHash,".lastTone",$msg,0);
  4873. }
  4874. CUL_HM_PushCmdStack($hash,$msg) if ($msg);
  4875. }
  4876. elsif($cmd eq "displayWM" ) { ###############################################
  4877. $state = "";
  4878. # textNo color icon
  4879. my $param = (scalar(@a)-2);
  4880. if ($a[2] eq "help"){
  4881. my $ret = "text :\n <text> max 2 char";
  4882. foreach (sort keys %disBtn){
  4883. my (undef,$ch,undef,$ln) = unpack('A3A2A1A1',$_);
  4884. $ch = sprintf("%02X",$ch);
  4885. $ret .= "\n $_ ->"
  4886. .ReadingsVal( InternalVal($devName,"channel_$ch","no")
  4887. ,"text$ln","unkown");
  4888. }
  4889. $ret .= "\n off nc(no change)"
  4890. ."\ncolor:".join(",",sort keys %disColor)
  4891. ."\n nc(no change)"
  4892. ."\nicon :".join(",",sort keys %disIcon)
  4893. ;
  4894. return $ret;
  4895. }
  4896. return "$a[2] not valid - choose short or long" if($a[2] !~ m/^(short|long)$/);
  4897. my $type = $a[2] eq "short"?"s":"l";
  4898. if(!defined $hash->{helper}{dispi}{$type}{"l1"}{d}){# setup if one is missing
  4899. $hash->{helper}{dispi}{$type}{"l$_"}{d}=1 foreach (1,2,3,4,5,6);
  4900. }
  4901. if($a[3] =~ m/^line(.)$/){
  4902. my $lnNr = $1;
  4903. return "line number wrong - use 1..6" if($lnNr !~ m/[1-6]/);
  4904. return "please add a text " if(!$a[4]);
  4905. my $lnRd = "disp_$a[2]_l$lnNr";# reading assotiated with this entry
  4906. my $dh = $hash->{helper}{dispi}{$type}{"l$lnNr"};
  4907. if ($a[4] eq "off"){ #no display in this line
  4908. delete $dh->{txt};
  4909. }
  4910. elsif($a[4] =~ m/^e:/){ # equation
  4911. $dh->{d} = 2; # mark as equation
  4912. $dh->{exe} = $a[4];
  4913. $dh->{exe} =~ s/^e://;
  4914. ($dh->{txt},$a[5],$a[6]) = eval $dh->{exe};
  4915. return "define eval must return 3 values:" if(!defined $a[6]);
  4916. }
  4917. elsif($a[4] ne "nc"){ # new text
  4918. return "text too long " .$a[4] if (length($a[4])>12);
  4919. $dh->{txt}=$a[4];
  4920. }
  4921. if($a[5]){ # set new color
  4922. if($a[5] eq "off"){ # set new color
  4923. delete $dh->{col};
  4924. }
  4925. elsif($a[5] ne "nc"){ # set new color
  4926. return "color wrong $a[5] use:".join(",",sort keys %disColor) if (!defined $disColor{$a[5]});
  4927. $dh->{col}=$a[5];
  4928. }
  4929. }
  4930. if($a[6]){ # new icon
  4931. if($a[6] eq "noIcon"){ # new icon
  4932. delete $dh->{icn};
  4933. }
  4934. elsif($a[6] ne "nc"){ # new icon
  4935. return "icon wrong $a[6] use:".join(",",sort keys %disIcon) if (!defined $disIcon {$a[6]});
  4936. $dh->{icn}=$a[6];
  4937. }
  4938. }
  4939. }
  4940. else{
  4941. return "not enough parameter - always use txtNo, color and icon in a set"
  4942. if(($param-1) %3);
  4943. for (my $cnt=3;$cnt<$param;$cnt+=3){
  4944. my $lnNr = int($cnt/3);
  4945. my $dh = $hash->{helper}{dispi}{$type}{"l$lnNr"};
  4946. return "color wrong ".$a[$cnt+1]." use:".join(",",sort keys %disColor) if (!defined $disColor{$a[$cnt+1]});
  4947. return "icon wrong " .$a[$cnt+2]." use:".join(",",sort keys %disIcon) if (!defined $disIcon {$a[$cnt+2]});
  4948. return "text too long " .$a[$cnt+0] if (length($a[$cnt+0])>12);
  4949. if ($a[$cnt+0] eq "nc") {} # nc = no change
  4950. elsif ($a[$cnt+0] eq "off"){ delete $dh->{txt} } # off = no text display
  4951. else {$dh->{txt} = $a[$cnt+0];} # nc = no change
  4952. $dh->{col} = $a[$cnt+1];
  4953. $dh->{icn} = $a[$cnt+2];
  4954. delete $dh->{icn} if ($a[$cnt+2] eq "noIcon");
  4955. }
  4956. }
  4957. foreach my $t (keys %{$hash->{helper}{dispi}}){ # prepare the messages
  4958. CUL_HM_calcDisWm($hash,$devName,$t);
  4959. }
  4960. }
  4961. elsif($cmd eq "displayEP" ) { ###############################################
  4962. $state = "displayEP";
  4963. RemoveInternalTimer($name.":reWriteDisplay");# just in case param reWriteDisplay used
  4964. my %disp_icons = (
  4965. off => '80', on => '81', open => '82', closed => '83'
  4966. ,error => '84', ok => '85', info => '86', newmsg => '87'
  4967. ,svcmsg => '88'
  4968. ,none => ''
  4969. );
  4970. my %disp_sounds = (
  4971. off => 'C0', longlong => 'C1', longshort => 'C2'
  4972. ,long2short => 'C3', short => 'C4', shortshort => 'C5'
  4973. ,long => 'C6'
  4974. );
  4975. my %disp_signals = (
  4976. off => 'F0', red => 'F1', green => 'F2', orange => 'F3'
  4977. );
  4978. # msg: 'text,icon;text,icon;text,icon'
  4979. my ($msg, $sound, $rep, $pause, $sig) = @a[2..$#a];
  4980. # set defaults
  4981. $msg = '' if (!defined ($msg));
  4982. $sound = 'off' if (!defined ($sound) || !exists ($disp_sounds{$sound}));
  4983. $sig = 'off' if (!defined ($sig) || !exists ($disp_signals{$sig} ));
  4984. $rep = (!defined ($rep)? 1 :
  4985. ($rep > 15 ? 15 :
  4986. ($rep == 0 ? 16 :
  4987. $rep)));
  4988. $pause = (!defined ($pause)?10 :
  4989. ($pause < 1 ?1 :
  4990. ($pause >160 ?160:
  4991. $pause)));
  4992. if($msg eq 'help'){ # display command info
  4993. return "command options:"
  4994. ."\n line1,icon1:line2,icon2:line3,icon3 sound repeat pause signal"
  4995. ."\n line: 12 char text to be dispalyed. No change if empty."
  4996. ."\n icon: icon per line: ".join(" ",keys(%disp_icons))
  4997. ."\n sound: ".join(" ",keys(%disp_sounds))
  4998. ."\n repeat: 1..16 default=1"
  4999. ."\n pause: 1..160 default=10"
  5000. ."\n signal: ".join(" ",keys(%disp_signals))
  5001. ."\n "
  5002. ."\n check for param reWriteDisplayxx: "
  5003. ."\n translate chars: "
  5004. ."\n [ => Ä"
  5005. ."\n # => Ö"
  5006. ."\n $ => Ü"
  5007. ."\n { => ä"
  5008. ."\n | => ö"
  5009. ."\n } => ü"
  5010. ."\n _ => ß"
  5011. ."\n ] => &"
  5012. ."\n ' => ="
  5013. ."\n @ => ∨"
  5014. ."\n > => ∧"
  5015. ."\n ; => Sandwatch"
  5016. ;
  5017. }
  5018. my $snd = '020A';
  5019. # Lines are separated by semicolon, empty lines are supported
  5020. my @disp_lines = (split (':', $msg.":::"),"","");# at least 3 entries - loop will use first 3
  5021. my $lineNr=1;
  5022. $evtDly = 1;
  5023. foreach my $line (@disp_lines[0..2]) {# only 3 lines
  5024. # Split line into text and icon part separated by comma
  5025. $snd .= '12';# start text indicator
  5026. my ($text, $icon); # add separator in case Icon is dismissed
  5027. if (!defined $line || $line eq '') {
  5028. $text = ReadingsVal($name,"line${lineNr}_text","");
  5029. $icon = ReadingsVal($name,"line${lineNr}_icon","off");
  5030. }
  5031. else{
  5032. ($text, $icon) = split (',', $line.","); # add separator in case Icon is dismissed
  5033. }
  5034. # Hex code
  5035. if ($text =~ /^0x[0-9A-F]{2}$/) {
  5036. $snd .= substr($text,2,2);
  5037. }
  5038. # Predefined text code text0-9
  5039. elsif ($text =~ /^text([0-9])$/) {
  5040. $snd .= sprintf ("8%1X", $1);
  5041. }
  5042. # Convert string to hex codes
  5043. else {
  5044. $text =~ s/\\_/ /g;
  5045. foreach my $ch (split ('', substr ($text, 0, 12))) {
  5046. $snd .= sprintf ("%02X", ord ($ch));
  5047. }
  5048. }
  5049. $snd .= '13'.$disp_icons{$icon} if ($disp_icons{$icon});
  5050. $snd .= '0A';
  5051. CUL_HM_UpdtReadBulk($hash,0,"line${lineNr}_text:$text"
  5052. ,"line${lineNr}_icon:$icon");
  5053. $lineNr++;
  5054. }
  5055. $snd .= '14'.$disp_sounds{$sound}.'1C'; # Sound
  5056. $snd .= sprintf ("%02X1D", 0xD0+$rep-1); # Repeat
  5057. $snd .= sprintf ("E%01X16", int(($pause-1)/10));# Pause
  5058. $snd .= $disp_signals{$sig}.'03'; # Signal
  5059. CUL_HM_UpdtReadBulk($hash,0,"signal:$sig");
  5060. CUL_HM_pushEvnts();
  5061. CUL_HM_PushCmdStack($hash,"++${flag}11$id${dst}80${chn}$_") foreach (unpack('(A28)*',$snd));
  5062. }
  5063. elsif($cmd =~ m/^(controlMode|controlManu|controlParty)$/) { ################
  5064. $state = "";
  5065. my $mode = $a[2];
  5066. if ($cmd ne "controlMode"){
  5067. $mode = substr($cmd,7);
  5068. $mode =~ s/^Manu$/manual/;
  5069. $a[2] = ($a[2] eq "off")?4.5:($a[2] eq "on"?30.5:$a[2]);
  5070. }
  5071. $mode = lc $mode;
  5072. return "invalid $mode:select of mode [auto|boost|day|night] or"
  5073. ." controlManu,controlParty"
  5074. if ($mode !~ m/^(auto|manual|party|boost|day|night)$/);
  5075. my ($temp,$party);
  5076. if ($mode =~ m/^(auto|boost|day|night)$/){
  5077. return "no additional params for $mode" if ($a[3]);
  5078. }
  5079. if($mode eq "manual"){
  5080. my $t = $a[2] ne "manual"?$a[2]:ReadingsVal($name,"desired-temp",18);
  5081. if ($md =~ m/CC-TC/){$t = ($t eq "off")?4.5:(($t eq "on" )?30.5:$t);}
  5082. else {$t = ($t eq "off")?5 :(($t eq "on" )?30 :$t);}
  5083. return "temperatur for manual 4.5 to 30.5 C"
  5084. if ($t < 4.5 || $t > 30.5);
  5085. $temp = $t*2;
  5086. }
  5087. elsif($mode eq "party"){
  5088. return "use party <temp> <from-time> <from-date> <to-time> <to-date>\n"
  5089. ."temperatur: 5 to 30 C\n"
  5090. ."date format: party 10 03.8.13 11:30 5.8.13 12:00"
  5091. if (!$a[2] || $a[2] < 5 || $a[2] > 30 || !$a[6] );
  5092. $temp = $a[2]*2;
  5093. # party format 03.8.13 11:30 5.8.13 12:00
  5094. my ($sd,$sm,$sy) = split('[\.-]',$a[3]);
  5095. my ($sh,$smin) = split(':' ,$a[4]);
  5096. my ($ed,$em,$ey) = split('[\.-]',$a[5]);
  5097. my ($eh,$emin) = split(':' ,$a[6]);
  5098. return "wrong start day $sd" if ($sd < 0 || $sd > 31);
  5099. return "wrong start month $sm" if ($sm < 0 || $sm > 12);
  5100. return "wrong start year $sy" if ($sy < 0 || $sy > 99);
  5101. return "wrong start hour $sh" if ($sh < 0 || $sh > 23);
  5102. return "wrong start minute $smin, ony 00 or 30" if ($smin != 0 && $smin != 30);
  5103. $sh = $sh * 2 + $smin/30;
  5104. return "wrong end day $ed" if ($ed < 0 || $ed > 31);
  5105. return "wrong end month $em" if ($em < 0 || $em > 12);
  5106. return "wrong end year $ey" if ($ey < 0 || $ey > 99);
  5107. return "wrong end hour $eh" if ($eh < 0 || $eh > 23);
  5108. return "wrong end minute $emin, ony 00 or 30" if ($emin != 0 && $emin != 30);
  5109. $eh = $eh * 2 + $emin/30;
  5110. $party = sprintf("%02X%02X%02X%02X%02X%02X%02X",
  5111. $sh,$sd,$sy,$eh,$ed,$ey,($sm*16+$em));
  5112. }
  5113. my %mCmd = (auto=>0,manual=>1,party=>2,boost=>3,day=>4,night=>5);
  5114. my $msg = '8'.($mCmd{$mode}).$chn;
  5115. $msg .= sprintf("%02X",$temp) if ($temp);
  5116. $msg .= $party if ($party);
  5117. my @teamList = ( split(",",AttrVal(CUL_HM_id2Name($dst."05"),"peerIDs","")) # peers of RT team
  5118. ,split(",",AttrVal(CUL_HM_id2Name($dst."02"),"peerIDs","")) # peers RT/TC team
  5119. ,CUL_HM_name2Id($name) # myself
  5120. );
  5121. foreach my $tId (@teamList){
  5122. my $teamC = CUL_HM_id2Name($tId);
  5123. $tId = substr($tId,0,6);
  5124. my $teamD = CUL_HM_id2Name($tId);
  5125. next if (!defined $defs{$teamC} );
  5126. CUL_HM_UpdtReadSingle($defs{$teamC},"controlMode","set_".$mode,1);
  5127. CUL_HM_PushCmdStack($defs{$teamD},'++'.$flag.'11'.$id.$tId.$msg);
  5128. if ( $tId ne $dst
  5129. && CUL_HM_getRxType($defs{$teamD}) & "02"){
  5130. # burst device - we need to send immediately
  5131. CUL_HM_SndCmd($defs{$teamD},"++B112$id".substr($tId,0,6));
  5132. }
  5133. }
  5134. }
  5135. elsif($cmd eq "desired-temp") { #############################################
  5136. if ($md =~ m/^(HM-CC-RT-DN|HM-TC-IT-WM-W-EU)/){
  5137. my $temp = ($a[2] eq "off")?9:($a[2] eq "on"?61:$a[2]*2);
  5138. return "invalid temp:$a[2]" if($temp <9 ||$temp > 61);
  5139. $temp = sprintf ("%02X",$temp);
  5140. CUL_HM_PushCmdStack($hash,'++'.$flag."11$id$dst"."8604$temp");
  5141. my $idTch = ($md =~ m/^HM-CC-RT-DN/ ? $dst."05" : $dst."02");
  5142. my @teamList = ( CUL_HM_name2Id($name) # myself
  5143. );
  5144. push @teamList,( split(",",AttrVal(CUL_HM_id2Name($dst."05"),"peerIDs","")) # peers of RT team
  5145. ,split(",",AttrVal(CUL_HM_id2Name($dst."02"),"peerIDs","")) # peers RT/TC team
  5146. ) if($md =~ m/^HM-CC-RT-DN/) ;
  5147. foreach my $tId (grep !/00000000/,@teamList){
  5148. $tId = substr($tId,0,6);
  5149. my $teamD = CUL_HM_id2Name($tId);
  5150. my $teamCh = (AttrVal($teamD,"model","") =~ m/HM-CC-RT-DN/) ? "04" #what is the controls channel of the peer?
  5151. : "02";
  5152. my $teamC = CUL_HM_id2Name($tId.$teamCh);
  5153. next if (!defined $defs{$teamC} );
  5154. CUL_HM_PushCmdStack($defs{$teamD},'++'.$flag."11$id$tId"."86$teamCh$temp");
  5155. CUL_HM_UpdtReadSingle($defs{$teamC},"state",$state,1);
  5156. if ( $tId ne $dst
  5157. && CUL_HM_getRxType($defs{$teamD}) & "02"){
  5158. # burst device - we need to send immediately
  5159. CUL_HM_SndCmd($defs{$teamD},"++B112$id".substr($tId,0,6));
  5160. }
  5161. }
  5162. }
  5163. else{
  5164. my $temp = CUL_HM_convTemp($a[2]);
  5165. return $temp if($temp =~ m/Invalid/);
  5166. CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.'0202'.$temp);
  5167. my $chnHash = CUL_HM_id2Hash($dst."02");
  5168. my $mode = ReadingsVal($chnHash->{NAME},"R-controlMode","");
  5169. $mode =~ s/set_//;#consider set as given
  5170. CUL_HM_UpdtReadSingle($chnHash,"desired-temp-cent",$a[2],1)
  5171. if($mode =~ m/central/);
  5172. }
  5173. }
  5174. elsif($cmd =~ m/^tempList(...)$/) { ##################################### reg
  5175. my $wd = $1;
  5176. $state= "";
  5177. my ($list,$addr,$prgChn);
  5178. if ($md =~ m/^(HM-CC-RT-DN|HM-TC-IT-WM-W-EU)/){
  5179. my %day2off = ( "Sat"=>"20", "Sun"=>"46", "Mon"=>"72", "Tue"=>"98",
  5180. "Wed"=>"124","Thu"=>"150","Fri"=>"176");
  5181. ($list,$addr,$prgChn) = (7,$day2off{$wd},0);
  5182. }
  5183. else{
  5184. my %day2off = ( "Sat"=>"5 0B", "Sun"=>"5 3B", "Mon"=>"5 6B",
  5185. "Tue"=>"5 9B", "Wed"=>"5 CB", "Thu"=>"6 01",
  5186. "Fri"=>"6 31");
  5187. ($list,$addr) = split(" ", $day2off{$wd},2);
  5188. $prgChn = 2;
  5189. $addr = hex($addr);
  5190. }
  5191. my $prep = "";
  5192. if (defined($a[2]) && $a[2] =~ m/^(prep|exec)$/){
  5193. $prep = $a[2];
  5194. splice @a,2,1;#remove prep
  5195. }
  5196. if ($md =~ m/^HM-TC-IT-WM-W-EU/ && $a[2] =~ m/^p([123])$/){
  5197. $list += $1 - 1;
  5198. splice @a,2,1;#remove list
  5199. }
  5200. return "To few arguments" if(@a < 4);
  5201. return "To many arguments, max 13 pairs" if(@a > 28 && $md =~ m/^(HM-CC-RT-DN|HM-TC-IT-WM-W-EU)/);
  5202. return "To many arguments, max 24 pairs" if(@a > 50 && $md !~ m/^(HM-CC-RT-DN|HM-TC-IT-WM-W-EU)/);
  5203. return "Bad format, use HH:MM TEMP ..." if(@a % 2);
  5204. return "Last time spec must be 24:00" if($a[@a-2] ne "24:00");
  5205. my ($data,$msg) = ("","");
  5206. for(my $idx = 2; $idx < @a; $idx += 2) {
  5207. return "$a[$idx] is not in HH:MM format"
  5208. if($a[$idx] !~ m/^([0-2]\d):([0-5]\d)/);
  5209. my ($h, $m) = ($1, $2);
  5210. my ($hByte,$lByte);
  5211. my $temp = $a[$idx+1];
  5212. if ($md =~ m/^(HM-CC-RT-DN|HM-TC-IT-WM-W-EU)/){
  5213. $temp = (int($temp*2)<<9) + ($h*12+($m/5));
  5214. $hByte = $temp>>8;
  5215. $lByte = $temp & 0xff;
  5216. }
  5217. else{
  5218. $temp = CUL_HM_convTemp($temp);
  5219. return $temp if($temp =~ m/Invalid/);
  5220. $hByte = $h*6+($m/10);
  5221. $lByte = hex($temp);
  5222. }
  5223. $data .= sprintf("%02X%02X%02X%02X", $addr, $hByte, $addr+1,$lByte);
  5224. $addr += 2;
  5225. $hash->{TEMPLIST}{$wd}{($idx-2)/2}{HOUR} = $h;
  5226. $hash->{TEMPLIST}{$wd}{($idx-2)/2}{MINUTE} = $m;
  5227. $hash->{TEMPLIST}{$wd}{($idx-2)/2}{TEMP} = $a[$idx+1];
  5228. $msg .= sprintf(" %02d:%02d %.1f", $h, $m, $a[$idx+1]);
  5229. }
  5230. CUL_HM_pushConfig($hash, $id, $dst, $prgChn,0,0,$list, $data,$prep);
  5231. }
  5232. elsif($cmd eq "sysTime") { ##################################################
  5233. $state = "";
  5234. my $s2000 = sprintf("%02X", CUL_HM_secSince2000());
  5235. CUL_HM_PushCmdStack($hash,"++803F$id${dst}0204$s2000");
  5236. }
  5237. elsif($cmd =~ m/^(valvePos|virtTemp|virtHum)$/) { ###########################
  5238. my $valu = $a[2];
  5239. my %lim = (valvePos =>{min=>0 ,max=>99 ,rd =>"valvePosTC" ,u =>" %"},
  5240. virtTemp =>{min=>-20,max=>50 ,rd =>"temperature",u =>"" },
  5241. virtHum =>{min=>0 ,max=>99 ,rd =>"humidity" ,u =>"" },);
  5242. if ($md eq "HM-CC-VD"){
  5243. return "level between $lim{$cmd}{min} and $lim{$cmd}{max} allowed"
  5244. if ($valu !~ m/^[+-]?\d+\.?\d+$/||
  5245. $valu > $lim{$cmd}{max}||$valu < $lim{$cmd}{min} );
  5246. CUL_HM_PushCmdStack($hash,'++A258'.$id.$dst
  5247. ."00".sprintf("%02X",($valu * 2.56)%256));
  5248. }
  5249. else{
  5250. my $u = $lim{$cmd}{u};
  5251. if ($valu eq "off"){
  5252. $u = "";
  5253. if ($cmd eq "virtHum") {$hash->{helper}{vd}{vinH} = "";}
  5254. else {$hash->{helper}{vd}{vin} = "";}
  5255. if ((!$hash->{helper}{vd}{vinH} || $hash->{helper}{vd}{vinH} eq "") &&
  5256. (!$hash->{helper}{vd}{vin} || $hash->{helper}{vd}{vin} eq "") ){
  5257. $state = "$cmd:stopped";
  5258. RemoveInternalTimer("valvePos:$dst$chn");# remove responsePending timer
  5259. RemoveInternalTimer("valveTmr:$dst$chn");# remove responsePending timer
  5260. delete($hash->{helper}{virtTC});
  5261. }
  5262. }
  5263. if ($hash->{helper}{virtTC} || $valu ne "off") {
  5264. if ($valu ne "off"){
  5265. return "level between $lim{$cmd}{min} and $lim{$cmd}{max} or 'off' allowed"
  5266. if ($valu !~ m/^[+-]?\d+\.?\d*$/||
  5267. $valu > $lim{$cmd}{max}||$valu < $lim{$cmd}{min} );
  5268. if ($cmd eq "virtHum") {$hash->{helper}{vd}{vinH} = $valu;}
  5269. else {$hash->{helper}{vd}{vin} = $valu;}
  5270. }
  5271. $attr{$devName}{msgRepeat} = 0;#force no repeat
  5272. if ($cmd eq "valvePos"){
  5273. my @pId = grep !/^$/,split(',',AttrVal($name,"peerIDs",""));
  5274. return "virtual TC support one VD only. Correct number of peers"
  5275. if (scalar @pId != 1);
  5276. my $ph = CUL_HM_id2Hash($pId[0]);
  5277. return "peerID $pId[0] is not assigned to a device " if (!$ph);
  5278. $hash->{helper}{vd}{typ} = 1; #valvePos
  5279. my $idDev = substr($pId[0],0,6);
  5280. $hash->{helper}{vd}{nDev} = CUL_HM_id2Name($idDev);
  5281. $hash->{helper}{vd}{id} = $modules{CUL_HM}{defptr}{$pId[0]}
  5282. ?$pId[0]
  5283. :$idDev;
  5284. $hash->{helper}{vd}{cmd} = "A258$dst$idDev";
  5285. CUL_HM_UpdtReadBulk($ph,1,
  5286. "state:set_$valu %",
  5287. "ValveDesired:$valu %");
  5288. $hash->{helper}{vd}{val} = sprintf("%02X",($valu * 2.56)%256);
  5289. $state = "ValveAdjust:$valu %";
  5290. }
  5291. else{#virtTemp || virtHum
  5292. $hash->{helper}{vd}{typ} = 2; #virtTemp
  5293. $hash->{helper}{vd}{cmd} = "8470$dst"."000000";
  5294. my $t = $hash->{helper}{vd}{vin}?$hash->{helper}{vd}{vin}:0;
  5295. $t *=10;
  5296. $t -= 0x8000 if ($t < 0);
  5297. $hash->{helper}{vd}{val} = sprintf("%04X", $t & 0x7fff);
  5298. $hash->{helper}{vd}{val} .= sprintf("%02X", $hash->{helper}{vd}{vinH})
  5299. if ($hash->{helper}{vd}{vinH} && $hash->{helper}{vd}{vinH} ne "");
  5300. }
  5301. $hash->{helper}{vd}{idh} = hex(substr($dst,2,2))*20077;
  5302. $hash->{helper}{vd}{idl} = hex(substr($dst,4,2))*256;
  5303. ($hash->{helper}{vd}{msgCnt},$hash->{helper}{vd}{next}) =
  5304. split(";",ReadingsVal($name,".next","0;".gettimeofday())) if(!defined $hash->{helper}{vd}{next});
  5305. if (!$hash->{helper}{virtTC}){
  5306. my $pn = CUL_HM_id2Name($hash->{helper}{vd}{id});
  5307. $hash->{helper}{vd}{ackT} = ReadingsTimestamp($pn, "ValvePosition", "")
  5308. if(!defined $hash->{helper}{vd}{ackT});
  5309. $hash->{helper}{vd}{miss} = 0 if(!defined $hash->{helper}{vd}{miss});
  5310. $hash->{helper}{vd}{msgRed} = 0 if(!defined $hash->{helper}{vd}{msgRed});
  5311. $hash->{helper}{virtTC} = ($cmd eq "valvePos")?"03":"00";
  5312. CUL_HM_UpdtReadSingle($hash,"valveCtrl","init",1)
  5313. if ($cmd eq "valvePos");
  5314. $hash->{helper}{vd}{next} = ReadingsVal($name,".next",gettimeofday())
  5315. if (!defined $hash->{helper}{vd}{next});
  5316. CUL_HM_valvePosUpdt("valvePos:$dst$chn");
  5317. }
  5318. $hash->{helper}{virtTC} = ($cmd eq "valvePos")?"03":"00";
  5319. }
  5320. CUL_HM_UpdtReadSingle($hash,$lim{$cmd}{rd},$valu.$u,1);
  5321. }
  5322. }
  5323. elsif($cmd eq "keydef") { ############################################### reg
  5324. if ( $a[3] eq "tilt") {CUL_HM_pushConfig($hash,$id,$dst,1,$id,$a[2],3,"0B220D838B228D83");#JT_ON/OFF/RAMPON/RAMPOFF short and long
  5325. } elsif ($a[3] eq "close") {CUL_HM_pushConfig($hash,$id,$dst,1,$id,$a[2],3,"0B550D838B558D83");#JT_ON/OFF/RAMPON/RAMPOFF short and long
  5326. } elsif ($a[3] eq "closed") {CUL_HM_pushConfig($hash,$id,$dst,1,$id,$a[2],3,"0F008F00"); #offLevel (also thru register)
  5327. } elsif ($a[3] eq "bolt") {CUL_HM_pushConfig($hash,$id,$dst,1,$id,$a[2],3,"0FFF8FFF"); #offLevel (also thru register)
  5328. } elsif ($a[3] eq "speedclose"){CUL_HM_pushConfig($hash,$id,$dst,1,$id,$a[2],3,sprintf("23%02XA3%02X",$a[4]*2,$a[4]*2));#RAMPOFFspeed (also in reg)
  5329. } elsif ($a[3] eq "speedtilt") {CUL_HM_pushConfig($hash,$id,$dst,1,$id,$a[2],3,sprintf("22%02XA2%02X",$a[4]*2,$a[4]*2));#RAMPOFFspeed (also in reg)
  5330. } else {return 'unknown argument '.$a[3];
  5331. }
  5332. }
  5333. elsif($cmd =~ m/^(teamCall|teamCallBat)$/) { ################################
  5334. $state = "";
  5335. my $sId = $roleV ? $dst : $id; # ID of cmd-source must not be a physical
  5336. # device. It can cause trouble with
  5337. # subsequent alarming
  5338. $hash->{TESTNR} = ($a[2] ? $a[2] : ($hash->{TESTNR} + 1))%255;
  5339. if ($fkt eq "sdLead1"){# ($md eq "HM-CC-SCD")
  5340. my $tstNo = sprintf("%02X",$hash->{TESTNR});
  5341. my $val = ($cmd eq "teamCallBat")? "80" : "00";
  5342. CUL_HM_PushCmdStack($hash, "++9440".$dst.$sId.$val.$tstNo);
  5343. CUL_HM_parseSDteam("40",$dst,$sId,$val.$tstNo);
  5344. }
  5345. else {#($md eq "HM-SEC-SD-2"){
  5346. # 96 switch on- others unknown
  5347. my $msg = CUL_HM_generateCBCsignature($hash,
  5348. sprintf("++1441$dst${sId}01%02X9600",$hash->{TESTNR}));
  5349. CUL_HM_PushCmdStack($hash, $msg) foreach (1..6);
  5350. CUL_HM_parseSDteam_2("41",$dst,$sId,substr($msg, 18));
  5351. }
  5352. }
  5353. elsif($cmd =~ m/^alarm(.*)/) { ##############################################
  5354. $state = "";
  5355. my $sId = $roleV ? $dst : $id; # ID of cmd-source must not be a physical
  5356. # device. It can cause trouble with
  5357. # subsequent alarming
  5358. if ($fkt eq "sdLead1"){# ($md eq "HM-CC-SCD")
  5359. my $p = (($1 eq "On")?"0BC8":"0C01");
  5360. my $msg = "++9441".$dst.$sId."01".$p;
  5361. CUL_HM_PushCmdStack($hash, $msg) foreach (1..3);# 3 reps fpr non-ack msg
  5362. CUL_HM_parseSDteam("41",$dst,$sId,"01".$p);
  5363. }
  5364. else{#($md eq "HM-SEC-SD-2"){
  5365. my $p = (($1 eq "On")?"C6":"00");
  5366. $hash->{TESTNR} = ($hash->{TESTNR} + 1)%255;
  5367. my $msg = CUL_HM_generateCBCsignature($hash,
  5368. sprintf("++1441$dst${sId}01%02X${p}00",$hash->{TESTNR}));
  5369. CUL_HM_PushCmdStack($hash, $msg) foreach (1..6);
  5370. CUL_HM_parseSDteam_2("41",$dst,$sId,substr($msg, 18));
  5371. }
  5372. }
  5373. elsif($cmd =~ m/^(press|event)(S|L)/) { #####################################
  5374. #press =>"-peer- [-repCount(long only)-] [-repDelay-] ..."
  5375. #event =>"-peer- -cond- [-repCount(long only)-] [-repDelay-] ..."
  5376. my ($trig,$type,$peer) = ($1,$2,$a[2]);
  5377. my ($cond,$repCnt,$repDly,$modeCode,$mode) = (0,0,0);
  5378. if ($st ne 'virtual'){
  5379. return "no peer defined" if (!defined $a[2]);
  5380. return "$peer not peered to $name" if (InternalVal($name,"peerList","") !~ m/$peer/);
  5381. }
  5382. else{
  5383. splice @a, 2, 0,"";# shift the array, similate a peer for virtuals
  5384. }
  5385. if ($trig eq "event"){
  5386. return "condition missing" if (!defined $a[3]);
  5387. ($cond,$repCnt,$repDly,$modeCode) = ($a[3],$a[4],$a[5],"41");
  5388. return "condition $cond out of range. Chooose 0...255" if ($cond < 0 || $cond > 255);
  5389. $cond = sprintf("%02X",$cond);
  5390. }
  5391. else{
  5392. ($cond,$repCnt,$repDly,$modeCode) = ("",$a[3],$a[4],"40");
  5393. }
  5394. if ($type eq "L"){
  5395. $mode = 64;
  5396. $repCnt = 1 if (!defined $repCnt );
  5397. $repDly = 0.25 if (!defined $repDly );
  5398. return "repeatCount $repCnt invalid. use value 1 - 255" if ($repCnt < 1 || $repCnt>255 );
  5399. return "repDelay $repDly invalid. use value 0.25 - 1.00" if ($repDly < 0.25 || $repDly>1 );
  5400. }
  5401. else{#short
  5402. ($repCnt,$repDly,$mode) = (0,0,0);
  5403. }
  5404. $hash->{helper}{count} = (!$hash->{helper}{count} ? 1
  5405. : $hash->{helper}{count}+1)%256;
  5406. if ($st eq 'virtual'){#serve all peers of virtual button
  5407. my @peerLchn = split(',',AttrVal($name,"peerIDs",""));
  5408. my @peerList = map{substr($_,0,6)} @peerLchn;
  5409. @peerList = grep !/000000/,grep !/^$/,CUL_HM_noDup(@peerList);
  5410. my $pc = sprintf("%02X%02X",hex($chn)+$mode,$hash->{helper}{count});# msg end
  5411. my $snd = 0;
  5412. foreach my $peer (sort @peerList){
  5413. my ($pHash,$peerFlag,$rxt);
  5414. $pHash = CUL_HM_id2Hash($peer);
  5415. next if ( !$pHash
  5416. || !$pHash->{helper}{role}
  5417. || !$pHash->{helper}{role}{prs});
  5418. $rxt = CUL_HM_getRxType($pHash);
  5419. $peerFlag = ($rxt & 0x02) ? "B4" : "A4"; #burst
  5420. CUL_HM_PushCmdStack($pHash,"++${peerFlag}$modeCode$dst$peer$pc");
  5421. $snd = 1;
  5422. foreach my $pCh(grep /$peer/,@peerLchn){
  5423. my $n = CUL_HM_id2Name($pCh);
  5424. next if (!$n);
  5425. $n =~ s/_chn-\d\d$//;
  5426. delete $defs{$n}{helper}{dlvl};#stop desiredLevel supervision
  5427. CUL_HM_stateUpdatDly($n,10);
  5428. }
  5429. if ($rxt & 0x80){#burstConditional
  5430. CUL_HM_SndCmd($pHash, "++B112$id".substr($peer,0,6));
  5431. }
  5432. else{
  5433. CUL_HM_ProcessCmdStack($pHash);
  5434. }
  5435. }
  5436. if(!$snd){# send 2 broadcast if no relevant peers
  5437. CUL_HM_SndCmd($hash,"++8440${dst}000000$pc");
  5438. }
  5439. }
  5440. else{#serve internal channels for actor
  5441. my ($pDev,$pCh) = unpack 'A6A2',CUL_HM_name2Id($peer,$devHash)."01";
  5442. return "button cannot be identified" if (!$pCh);
  5443. delete $hash->{helper}{dlvl};#stop desiredLevel supervision
  5444. my $msg = sprintf("3E%s%s%s%s%02X%02X",
  5445. $id,$dst,$pDev,$modeCode
  5446. ,hex($pCh)+$mode
  5447. ,$hash->{helper}{count}
  5448. ,$cond);
  5449. for (my $cnt = 1;$cnt < $repCnt; $cnt++ ){
  5450. CUL_HM_SndCmd($hash, "++80$msg"); # send direct Wont work for burst!
  5451. select(undef, undef, undef, $repDly);
  5452. }
  5453. CUL_HM_PushCmdStack($hash, "++${flag}$msg"); # send thru commandstack
  5454. CUL_HM_stateUpdatDly($name,10);#check status after 10 sec
  5455. }
  5456. }
  5457. elsif($cmd =~ m/^press(.*)/) { ##############################################
  5458. # [long|short] [<peer>] [<repCount(long only)>] [<repDelay>] [<forceTiming[0|1]>] ...
  5459. my ($repCnt,$repDly,$forceTiming,$mode) = (0,0,0,0);
  5460. if($cmd eq "pressL"){
  5461. splice @a,2,0, ("long");
  5462. }
  5463. elsif($cmd eq "pressS"){
  5464. splice @a,2,0, ("short");
  5465. }
  5466. if ($a[2]){
  5467. ##############################
  5468. if ($a[2] eq "long"){
  5469. $mode = 64;
  5470. splice @a,2,1;
  5471. (undef,undef,undef,$repCnt,$repDly,$forceTiming) = @a;
  5472. $repCnt = 1 if (!defined $repCnt );
  5473. $repDly = 0.25 if (!defined $repDly );
  5474. $forceTiming = 1 if (!defined $forceTiming);
  5475. return "repeatCount $repCnt invalid. use value 1 - 255" if ($repCnt < 1 || $repCnt>255 );
  5476. return "repDelay $repDly invalid. use value 0.25 - 1.00" if ($repDly < 0.25 || $repDly>1 );
  5477. return "forceTiming $forceTiming invalid. use value 0 or 1" if ($forceTiming ne "0" && $forceTiming ne "1" );
  5478. }
  5479. elsif($a[2] eq "short"){
  5480. splice @a,2,1;
  5481. }
  5482. }
  5483. my $vChn = $a[2]?$a[2]:"";
  5484. my $pressCnt = (!$hash->{helper}{count}?1:$hash->{helper}{count}+1)%256;
  5485. $hash->{helper}{count}=$pressCnt;# remember for next round
  5486. if ($st eq 'virtual'){#serve all peers of virtual button
  5487. my @peerLchn = split(',',AttrVal($name,"peerIDs",""));
  5488. my @peerList = map{substr($_,0,6)} @peerLchn;
  5489. @peerList = grep !/000000/,grep !/^$/,CUL_HM_noDup(@peerList);
  5490. my $pc = sprintf("%02X%02X",hex($chn)+$mode,$pressCnt);# msg end
  5491. my $snd = 0;
  5492. foreach my $peer (sort @peerList){
  5493. my ($pHash,$peerFlag,$rxt);
  5494. $pHash = CUL_HM_id2Hash($peer);
  5495. next if ( !$pHash
  5496. || !$pHash->{helper}{role}
  5497. || !$pHash->{helper}{role}{prs});
  5498. $rxt = CUL_HM_getRxType($pHash);
  5499. $peerFlag = ($rxt & 0x02)?"B4":"A4" if($vChn ne "noBurst");#burst
  5500. CUL_HM_PushCmdStack($pHash,"++${peerFlag}40$dst$peer$pc");
  5501. $snd = 1;
  5502. foreach my $pCh(grep /$peer/,@peerLchn){
  5503. my $n = CUL_HM_id2Name($pCh);
  5504. next if (!$n);
  5505. $n =~ s/_chn-\d\d$//;
  5506. delete $defs{$n}{helper}{dlvl};#stop desiredLevel supervision
  5507. CUL_HM_stateUpdatDly($n,10);
  5508. }
  5509. if ($rxt & 0x80){#burstConditional
  5510. CUL_HM_SndCmd($pHash, "++B112$id".substr($peer,0,6))
  5511. if($vChn ne "noBurst");
  5512. }
  5513. else{
  5514. CUL_HM_ProcessCmdStack($pHash);
  5515. }
  5516. }
  5517. if(!$snd){# send 2 broadcast if no relevant peers
  5518. CUL_HM_SndCmd($hash,"++8440${dst}000000$pc");
  5519. }
  5520. }
  5521. else{#serve internal channels for actor
  5522. #which button shall be simulated? We offer
  5523. # on/off: self button - on is even/off odd number. Obey channel
  5524. # name of peer
  5525. my $pId;
  5526. if ($vChn =~ m/^(on|off)$/ && $st =~ m/^(blindActuator|dimmer)$/){
  5527. $pId = $dst.sprintf("%02X",(($vChn eq "off")?-1:0) + $chn*2);
  5528. }
  5529. elsif($vChn){
  5530. $pId = CUL_HM_name2Id($vChn,$devHash)."01";#01 is default for devices
  5531. }
  5532. else{
  5533. $pId = $dst.sprintf("%02X",$chn);
  5534. }
  5535. my ($pDev,$pCh) = unpack 'A6A2',$pId;
  5536. return "button cannot be identified" if (!$pCh);
  5537. delete $hash->{helper}{dlvl};#stop desiredLevel supervision
  5538. my $msg = sprintf("3E%s%s%s40%02X%02X",
  5539. $id,$dst,$pDev,
  5540. hex($pCh)+$mode,
  5541. $pressCnt);
  5542. for (my $cnt = 1;$cnt < $repCnt; $cnt++ ){
  5543. CUL_HM_SndCmd($hash, "++80$msg"); # send direct Wont work for burst!
  5544. select(undef, undef, undef, $repDly);
  5545. }
  5546. CUL_HM_PushCmdStack($hash, "++${flag}$msg"); # send thru commandstack
  5547. }
  5548. }
  5549. elsif($cmd =~ m/^trg(Press|Event)(.)/) { ####################################
  5550. $state = "";
  5551. my ($trig,$type) = ($1,$2);
  5552. my $peer = $a[2];
  5553. return "$peer not defined" if(!defined$defs{$peer} && $peer ne "all" );
  5554. my @peers;
  5555. if ($peer eq "all"){
  5556. @peers = grep/./,split",",InternalVal($name,"peerList","");
  5557. }
  5558. else{
  5559. push @peers,$a[2];
  5560. }
  5561. if($trig eq "Event"){
  5562. return "no condition level defined" if (!defined $a[3]);
  5563. return "condition $a[3] out of range. limit to 0..255" if ($a[3]<0 || $a[3]>255);
  5564. }
  5565. foreach my $peerSet(@peers){
  5566. next if (!defined($peerSet) || !defined($defs{$peerSet}) );
  5567. if($trig eq "Event"){CUL_HM_Set($defs{$peerSet},$peerSet,"event$type",$name,$a[3]);}
  5568. else {CUL_HM_Set($defs{$peerSet},$peerSet,"press$type",$name);}
  5569. }
  5570. }
  5571. elsif($cmd eq "fwUpdate") { #################################################
  5572. if ($a[2] eq "onlyEnterBootLoader") {
  5573. Log3 $name,2,"CUL_HM entering bootloader for $name";
  5574. CUL_HM_SndCmd($hash, sprintf("%02X",$modules{CUL_HM}{helper}{updateNbr})
  5575. ."3011$id${dst}CA");
  5576. return ("",1);
  5577. }
  5578. return "no filename given" if (!$a[2]);
  5579. # return "only thru CUL " if (!$hash->{IODev}->{TYPE}
  5580. # ||($hash->{IODev}->{TYPE} ne "CUL"));
  5581. # todo add version checks of CUL
  5582. my ($fName,$pos,$enterBL) = ($a[2],0,($a[3] ? $a[3]+0 : 10));
  5583. my @imA; # image array: image[block][msg]
  5584. return "Illegal waitTime $enterBL - enter a value between 10 and 300"
  5585. if ($enterBL < 10 || $enterBL>300);
  5586. open(aUpdtF, $fName) || return("Can't open $fName: $!");
  5587. while(<aUpdtF>){
  5588. my $line = $_;
  5589. my $fs = length($line);
  5590. while ($fs>$pos){
  5591. my $bs = hex(substr($line,$pos,4))*2+4;
  5592. return "file corrupt. length:$fs expected:".($pos+$bs)
  5593. if ($fs<$pos+$bs);
  5594. my @msg = grep !/^$/,unpack '(A60)*',substr($line,$pos,$bs);
  5595. push @imA,\@msg; # image[block][msg]
  5596. $pos += $bs;
  5597. }
  5598. }
  5599. close(aUpdtF);
  5600. # --- we are prepared start update---
  5601. CUL_HM_protState($hash,"CMDs_FWupdate");
  5602. $modules{CUL_HM}{helper}{updating} = 1;
  5603. $modules{CUL_HM}{helper}{updatingName} = $name;
  5604. $modules{CUL_HM}{helper}{updateData} = \@imA;
  5605. $modules{CUL_HM}{helper}{updateStep} = 0;
  5606. $modules{CUL_HM}{helper}{updateDst} = $dst;
  5607. $modules{CUL_HM}{helper}{updateId} = $id;
  5608. $modules{CUL_HM}{helper}{updateNbr} = 10;
  5609. Log3 $name,2,"CUL_HM fwUpdate started for $name";
  5610. CUL_HM_SndCmd($hash, sprintf("%02X",$modules{CUL_HM}{helper}{updateNbr})
  5611. ."3011$id${dst}CA");
  5612. $hash->{helper}{io}{newChnFwUpdate} = $hash->{helper}{io}{newChn};#temporary hide init message
  5613. $hash->{helper}{io}{newChn} = "";
  5614. InternalTimer(gettimeofday()+$enterBL,"CUL_HM_FWupdateEnd","fail:notInBootLoader",0);
  5615. #InternalTimer(gettimeofday()+0.3,"CUL_HM_FWupdateSim",$dst."00000000",0);
  5616. }
  5617. elsif($cmd eq "postEvent") { ################################################
  5618. my (undef,undef,$cond) = @a;
  5619. my $cndNo;
  5620. if ($cond =~ m/[+-]?\d+/){
  5621. return "condition value:$cond above 255 illegal" if ($cond > 255);
  5622. $cndNo = $cond;
  5623. }
  5624. else{
  5625. my @keys;
  5626. if ($chnHash->{helper}{lm}){
  5627. foreach (keys %{$chnHash->{helper}{lm}}){
  5628. if ($chnHash->{helper}{lm}{$_} eq $cond){
  5629. $cndNo = $_;
  5630. last;
  5631. }
  5632. push @keys,$chnHash->{helper}{lm};
  5633. }
  5634. }
  5635. else{
  5636. foreach my $tp (keys %lvlStr){
  5637. foreach my $mk (keys %{$lvlStr{$tp}}){
  5638. foreach (keys %{$lvlStr{$tp}{$mk}}){
  5639. $cndNo = hex($_) if ($cond eq $lvlStr{$tp}{$mk}{$_});
  5640. push @keys,$lvlStr{$tp}{$mk}{$_};
  5641. }
  5642. }
  5643. }
  5644. }
  5645. return "cond:$cond not allowed. choose one of:[0..255],"
  5646. .join(",",sort @keys)
  5647. if (!defined $cndNo);
  5648. }
  5649. my $pressCnt = (!$hash->{helper}{count}?1:$hash->{helper}{count}+1)%256;
  5650. $hash->{helper}{count}=$pressCnt;# remember for next round
  5651. my @peerLChn = split(',',AttrVal($name,"peerIDs",""));
  5652. my @peerDev;
  5653. push (@peerDev,substr($_,0,6)) foreach (@peerLChn);
  5654. @peerDev = CUL_HM_noDup(@peerDev);#only once per device!
  5655. push @peerDev,'000000' if (!@peerDev);#send to broadcast if no peer
  5656. foreach my $peer (@peerDev){
  5657. my $pHash = CUL_HM_id2Hash($peer);
  5658. my $rxt = CUL_HM_getRxType($pHash);
  5659. my $peerFlag = ($rxt & 0x02)?"B4":"A4";#burst
  5660. CUL_HM_PushCmdStack($pHash, sprintf("++%s41%s%s%02X%02X%02X"
  5661. ,$peerFlag,$dst,$peer
  5662. ,hex($chn)
  5663. ,$pressCnt
  5664. ,$cndNo));
  5665. if ($rxt & 0x80){#burstConditional
  5666. CUL_HM_SndCmd($pHash, "++B112$id".substr($peer,0,6));
  5667. }
  5668. else{
  5669. CUL_HM_ProcessCmdStack($pHash);
  5670. }
  5671. }
  5672. foreach my $peer (@peerLChn){#inform each channel
  5673. my $pName = CUL_HM_id2Name($peer);
  5674. $pName = CUL_HM_id2Name(substr($peer,0,6)) if (!$defs{$pName});
  5675. next if (!$defs{$pName});
  5676. CUL_HM_UpdtReadBulk($defs{$pName},1
  5677. ,"trig_$name:$cond"
  5678. ,"trigLast:$name:$cond");
  5679. }
  5680. }
  5681. elsif($cmd eq "peerIODev") { ################################################
  5682. # peerIODev [IO] <chn> [set|unset]...
  5683. $state = "";
  5684. return "command requires parameter" if (!$a[2]);
  5685. my ($ioId,$ioCh,$set) = ($id,$a[2],'set'); #set defaults
  5686. if ($defs{$a[2]}){ #IO device given
  5687. $ioId = AttrVal($a[2],"hmId","");
  5688. return "$a[2] not valid, attribut hmid not set"
  5689. if($ioId !~ m/^[0-9A-F]{6}$/);
  5690. splice @a,2,1;
  5691. $ioCh = $a[2];
  5692. }
  5693. $set = $a[3] if ($a[3]);
  5694. $ioCh = sprintf("%02X",$ioCh);
  5695. return "No:$ioCh invalid. Number must be <=50" if (!$ioCh || $ioCh !~ m/^(\d*)$/ || $ioCh > 50);
  5696. return "option $set unknown - use set or unset" if ($set != m/^(set|unset)$/);
  5697. $set = ($set eq "set")?"01":"02";
  5698. CUL_HM_PushCmdStack($hash,"++${flag}01$id${dst}$chn$set$ioId${ioCh}00");
  5699. }
  5700. elsif($cmd eq "peerChan") { ############################################# reg
  5701. #peerChan <btnN> <device> ... [single|dual] [set|unset] [actor|remote|both]
  5702. my ($bNo,$peerN,$single,$set,$target) = ($a[2],$a[3],($a[4]?$a[4]:"dual"),
  5703. ($a[5]?$a[5]:"set"),
  5704. ($a[6]?$a[6]:"both"));
  5705. $state = "";
  5706. if ($roleD){
  5707. $bNo = 1 if ($bNo == 0 && $roleC); # role device and channel => button=1
  5708. return "$bNo is not a button number" if($bNo < 1);
  5709. }
  5710. my $peerId = CUL_HM_name2Id($peerN);
  5711. return "please enter peer" if(!$peerId);
  5712. return "peer is not a channel" if(!$defs{$peerN}{helper}{role}{chn});
  5713. $peerId .= "01" if( length($peerId) == 6);
  5714. my @pCh;
  5715. my ($peerHash,$dSet,$cmdB);
  5716. my $peerDst = substr($peerId,0,6);
  5717. my $pmd = AttrVal(CUL_HM_id2Name($peerDst), "model" , "");
  5718. if ($md =~ m/^HM-CC-RT-DN/ && $chn eq "05" ){# rt team peers cross from 05 to 04
  5719. @pCh = (undef,"04","05");
  5720. $chn = "04";
  5721. $single = "dual";
  5722. $dSet = 1;#Dual set - set 2 channels for "remote"
  5723. }
  5724. else{ # normal devices
  5725. $pCh[1] = $pCh[2] = substr($peerId,6,2);
  5726. }
  5727. $peerHash = $modules{CUL_HM}{defptr}{$peerDst.$pCh[1]}if ($modules{CUL_HM}{defptr}{$peerDst.$pCh[1]});
  5728. $peerHash = $modules{CUL_HM}{defptr}{$peerDst} if (!$peerHash);
  5729. return "$peerN not a CUL_HM device" if( ($target ne "remote")
  5730. && (!$peerHash || $peerHash->{TYPE} ne "CUL_HM")
  5731. && $defs{$devName}{IODev}->{NAME} ne $peerN);
  5732. return "$single must be single, dual or reverse" if($single !~ m/^(single|dual|reverse)$/);
  5733. return "$set must be set or unset" if($set !~ m/^(set|unset)$/);
  5734. return "$target must be [actor|remote|both]" if($target !~ m/^(actor|remote|both)$/);
  5735. return "use - single [set|unset] actor - for smoke detector" if( $st eq "smokeDetector" && ($single ne "single" || $target ne "actor"));
  5736. return "use - single - for ".$st if(($st =~ m/^(threeStateSensor|motionDetector)$/) && ($single ne "single"));
  5737. return "TC WindowRec only peers to channel 01 single" if( $pmd =~ m/^(HM-CC-TC|ROTO_ZEL-STG-RM-FWT)/ && $pCh[1] eq "03" && $chn ne "01" && $set eq "set");
  5738. my $pSt = CUL_HM_Get($peerHash,$peerHash->{NAME},"param","subType");
  5739. if ($set eq "unset"){$set = 0; $cmdB ="02";}
  5740. else {$set = 1; $cmdB ="01";}
  5741. my (@b,$nrCh2Pair);
  5742. $b[1] = ($roleD) ?(($single eq "single")?$bNo : ($bNo*2 - 1))
  5743. : hex($chn)
  5744. ;
  5745. if ($single eq "single"){
  5746. $b[2] = $b[1];
  5747. $b[1] = 0 if ($st eq "smokeDetector" ||$pSt eq "smokeDetector");
  5748. $nrCh2Pair = 1;
  5749. }
  5750. elsif($single eq "dual"){
  5751. $single = 0;
  5752. $b[2] = $b[1] + 1;
  5753. $nrCh2Pair = 2;
  5754. }
  5755. else{#($single eq "reverse")
  5756. $single = 0;
  5757. $b[2] = $b[1]++;
  5758. $nrCh2Pair = 2;
  5759. }
  5760. if ( $pSt eq "smokeDetector"){
  5761. $target = "both" if ($st eq "virtual");
  5762. }
  5763. # First the remote (one loop for on, one for off)
  5764. if ($target =~ m/^(remote|both)$/){
  5765. my $burst;
  5766. if ($culHmRegModel->{$md}{peerNeedsBurst}|| #peerNeedsBurst supported
  5767. $culHmRegType->{$st}{peerNeedsBurst}){
  5768. $burst = (CUL_HM_getRxType($peerHash) & 0x82) #burst |burstConditional
  5769. ?"0101"
  5770. :"0100";
  5771. }
  5772. for(my $i = 1; $i <= $nrCh2Pair; $i++) {
  5773. if ($st eq "virtual"){
  5774. my $btnName = $pSt eq "smokeDetector" ? $name :CUL_HM_id2Name($dst.sprintf("%02X",$b[$i]));
  5775. next if (!defined $attr{$btnName});
  5776. CUL_HM_ID2PeerList ($btnName,$peerDst.$pCh[$i],$set); #upd. peerlist
  5777. }
  5778. else{
  5779. my $bStr = sprintf("%02X",$b[$i]);
  5780. CUL_HM_PushCmdStack($hash,
  5781. "++".$flag."01${id}${dst}${bStr}$cmdB${peerDst}$pCh[$i]00");
  5782. # my ($hash,$src,$dst,$chn ,$peerAddr,$peerChn ,$list,$content,$prep) = @_;
  5783. CUL_HM_pushConfig($hash,$id, $dst,$b[$i],$peerDst ,hex($pCh[$i]),4 ,$burst)
  5784. if($burst && $cmdB eq "01"); # only if set
  5785. CUL_HM_qAutoRead($name,3);
  5786. }
  5787. }
  5788. # need to send data here- this is a 2 device command... thats why.
  5789. my $rxType = CUL_HM_getRxType($devHash);
  5790. if($rxType & 0x01){#allways
  5791. CUL_HM_ProcessCmdStack($devHash);
  5792. }
  5793. elsif($devHash->{cmdStack} &&
  5794. $devHash->{helper}{prt}{sProc} != 1 # not processing
  5795. ){
  5796. if($rxType & 0x02){# handle burst Access devices - add burst Bit
  5797. my ($pre,$tp,$tail) = unpack 'A2A2A*',$devHash->{cmdStack}[0];
  5798. $devHash->{cmdStack}[0] = sprintf("%s%02X%s",$pre,(hex($tp)|0x10),$tail);
  5799. CUL_HM_ProcessCmdStack($devHash);
  5800. }
  5801. elsif (CUL_HM_getAttrInt($name,"burstAccess")){ #burstConditional - have a try
  5802. $hash->{helper}{prt}{brstWu}=1;# start auto-burstWakeup
  5803. CUL_HM_SndCmd($devHash,"++B112$id$dst");
  5804. }
  5805. }
  5806. }
  5807. if ($target =~ m/^(actor|both)$/ ){
  5808. if ($modules{CUL_HM}{defptr}{$peerDst}){# is defined or ID only?
  5809. if ($pSt eq "virtual"){
  5810. CUL_HM_ID2PeerList ($peerN,$dst.sprintf("%02X",$b[2]),$set);
  5811. CUL_HM_ID2PeerList ($peerN,$dst.sprintf("%02X",$b[1]),$set)
  5812. if ($b[1] & !$single);
  5813. }
  5814. else{
  5815. my $peerFlag = 'A0';
  5816. if ($dSet){
  5817. CUL_HM_PushCmdStack($peerHash, sprintf("++%s01%s%s%s%s%s%02X00",$peerFlag,$id,$peerDst,$pCh[1],$cmdB,$dst,$b[1]));
  5818. CUL_HM_PushCmdStack($peerHash, sprintf("++%s01%s%s%s%s%s%02X00",$peerFlag,$id,$peerDst,$pCh[2],$cmdB,$dst,$b[2] ));
  5819. }
  5820. else{
  5821. CUL_HM_PushCmdStack($peerHash, sprintf("++%s01%s%s%s%s%s%02X%02X",$peerFlag,$id,$peerDst,$pCh[1],$cmdB,$dst,$b[2],$b[1] ));
  5822. }
  5823. if(CUL_HM_getRxType($peerHash) & 0x80){
  5824. my $pDevHash = CUL_HM_id2Hash($peerDst);#put on device
  5825. CUL_HM_pushConfig($pDevHash,$id,$peerDst,0,0,0,0,"0101");#set burstRx
  5826. }
  5827. CUL_HM_qAutoRead($peerHash->{NAME},3);
  5828. }
  5829. $devHash = CUL_HM_getDeviceHash($peerHash); # Exchange the hash, as the switch is always alive.
  5830. }
  5831. }
  5832. return ("",1) if ($target && $target eq "remote");#Nothing for actor
  5833. }
  5834. elsif($cmd =~ m/^(pair|getVersion)$/) { ####################################
  5835. $state = "";
  5836. my $serial = ReadingsVal($name, "D-serialNr", undef);
  5837. return "serial $serial - wrong length or Reading D-serialNr not present"
  5838. if(length($serial) != 10);
  5839. CUL_HM_PushCmdStack($hash,"++A401".$id."000000010A".uc( unpack("H*",$serial)));
  5840. $hash->{hmPairSerial} = $serial if ($cmd eq "pair");
  5841. }
  5842. elsif($cmd eq "hmPairForSec") { #############################################
  5843. $state = "";
  5844. my $arg = $a[2] ? $a[2] : "";
  5845. $arg = 60 if( $arg !~ m/^\d+$/);
  5846. CUL_HM_RemoveHMPair("hmPairForSec:$name");
  5847. $hash->{hmPair} = 1;
  5848. InternalTimer(gettimeofday()+$arg, "CUL_HM_RemoveHMPair", "hmPairForSec:$name", 1);
  5849. }
  5850. elsif($cmd eq "hmPairSerial") { #############################################
  5851. $state = "";
  5852. my $serial = $a[2]?$a[2]:"";
  5853. return "Usage: set $name hmPairSerial <10-character-serialnumber>"
  5854. if(length($serial) != 10);
  5855. CUL_HM_PushCmdStack($hash, "++8401${dst}000000010A".uc( unpack('H*', $serial)));
  5856. CUL_HM_RemoveHMPair("hmPairForSec:$name");
  5857. $hash->{hmPair} = 1;
  5858. $hash->{hmPairSerial} = $serial;
  5859. InternalTimer(gettimeofday()+30, "CUL_HM_RemoveHMPair", "hmPairForSec:$name", 1);
  5860. }
  5861. elsif($cmd eq "assignIO") { #################################################
  5862. $state = "";
  5863. my $io = $a[2];
  5864. return "use set of unset - $a[3] not allowed"
  5865. if ($a[3] && $a[3] != m/^(set|unset)$/);
  5866. my $set = ($a[3] && $a[3] eq "unset")?0:1;
  5867. if ($set){
  5868. CommandAttr(undef, "$io hmId $dst");
  5869. }
  5870. else{
  5871. CommandDeleteAttr(undef, "$io hmId");
  5872. }
  5873. CUL_HM_UpdtCentral($name);
  5874. }
  5875. elsif($cmd eq "assignHmKey") { ##############################################
  5876. $state = "";
  5877. my $oldKeyIdx = ReadingsVal($name, "aesKeyNbr", "00");
  5878. return "current key unknown" if (!defined $oldKeyIdx || $oldKeyIdx eq "");
  5879. my ($key1,$key2);
  5880. if (AttrVal($hash->{IODev}{NAME},"rfmode","") eq "HomeMatic" ) {
  5881. return "$cmd needs Crypt::Rijndael for updating keys with CUL"
  5882. if ($cryptFunc != 1);
  5883. my ($newKeyIdx, %keys) = CUL_HM_getKeys($hash);
  5884. my $oldKey = $keys{hex($oldKeyIdx)/2};
  5885. return "$cmd requires VCCU with hmKeys" if ($newKeyIdx == 0);
  5886. return "$cmd needs old key with index ".(hex($oldKeyIdx)/2) if (!defined($oldKey));
  5887. return "$cmd key with index ".$newKeyIdx." allready in use by device"
  5888. if ($newKeyIdx == (hex($oldKeyIdx)/2));
  5889. my $newKey = $keys{$newKeyIdx};
  5890. my $payload1 = pack("CCa8nN",1 #changekey?
  5891. ,$newKeyIdx*2 #index for first part of key
  5892. ,substr($newKey, 0, 8) #first 8 bytes of new key
  5893. ,rand(0xffff) #random
  5894. ,0x7e296fa5); #magic
  5895. my $payload2 = pack("CCa8nN",1 #changekey?
  5896. ,($newKeyIdx*2)+1 #index for second part of key
  5897. ,substr($newKey, 8, 8) #second 8 bytes of new key
  5898. ,rand(0xffff) #random
  5899. ,0x7e296fa5); #magic
  5900. my $cipher = Crypt::Rijndael->new($oldKey, Crypt::Rijndael::MODE_ECB());
  5901. Log3 $name,2,"CUL_HM $name assignHmKey index ".(hex($oldKeyIdx)/2)." to ".$newKeyIdx
  5902. ." Key1: ".unpack("H*", $payload1)
  5903. ." Key2: ".unpack("H*", $payload2);
  5904. $key1 = unpack("H*", $cipher->encrypt($payload1));
  5905. $key2 = unpack("H*", $cipher->encrypt($payload2));
  5906. }
  5907. else {
  5908. $key1 = sprintf("01%02X",$oldKeyIdx);
  5909. $key2 = sprintf("01%02X",($oldKeyIdx+1));
  5910. }
  5911. CUL_HM_PushCmdStack($hash,'++'.$flag.'04'.$id.$dst.$key1);
  5912. CUL_HM_PushCmdStack($hash,'++'.$flag.'04'.$id.$dst.$key2);
  5913. }
  5914. else{
  5915. return "$cmd not implemented - contact sysop";
  5916. }
  5917. CUL_HM_UpdtReadSingle($hash,"state",$state,1) if($state);
  5918. my $rxType = CUL_HM_getRxType($devHash);
  5919. Log3 $name,3,"CUL_HM set $name $act";
  5920. if($rxType & 0x01){#always
  5921. CUL_HM_ProcessCmdStack($devHash);
  5922. }
  5923. elsif($devHash->{cmdStack} &&
  5924. $devHash->{helper}{prt}{sProc} != 1 # not processing
  5925. ){
  5926. if($rxType & 0x02){# handle burst Access devices - add burst Bit
  5927. if($st eq "thermostat"){ # others do not support B112
  5928. CUL_HM_SndCmd($devHash,"++B112$id$dst");
  5929. }
  5930. else{# set burst flag
  5931. my ($pre,$tp,$tail) = unpack 'A2A2A*',$devHash->{cmdStack}[0];
  5932. $devHash->{cmdStack}[0] = sprintf("%s%02X%s",$pre,(hex($tp)|0x10),$tail);
  5933. CUL_HM_ProcessCmdStack($devHash);
  5934. }
  5935. }
  5936. elsif (CUL_HM_getAttrInt($name,"burstAccess")){ #burstConditional - have a try
  5937. $hash->{helper}{prt}{brstWu}=1;# start auto-burstWakeup
  5938. CUL_HM_SndCmd($devHash,"++B112$id$dst");
  5939. }
  5940. }
  5941. return ("",1);# no not generate trigger out of command
  5942. }
  5943. #+++++++++++++++++ set/get support subroutines+++++++++++++++++++++++++++++++++
  5944. sub CUL_HM_valvePosUpdt(@) {#update valve position periodically to please valve
  5945. my($in ) = @_;
  5946. my(undef,$vId) = split(':',$in);
  5947. my $hash = CUL_HM_id2Hash($vId);
  5948. my $hashVd = $hash->{helper}{vd};
  5949. my $name = $hash->{NAME};
  5950. my $msgCnt = $hashVd->{msgCnt};
  5951. my ($idl,$lo,$hi,$nextTimer);
  5952. my $tn = gettimeofday();
  5953. my $nextF = $hashVd->{next};
  5954. # int32_t result = (((_address << 8) | messageCounter) * 1103515245 + 12345) >> 16;
  5955. # 4e6d = 20077 12996205 = C64E6D
  5956. # return (result & 0xFF) + 480;
  5957. if ($tn > ($nextF + 3000)){# missed 20 periods;
  5958. Log3 $name,3,"CUL_HM $name virtualTC timer off by:".int($tn - $nextF);
  5959. $nextF = $tn;
  5960. }
  5961. while ($nextF < ($tn+0.05)) {# calculate next time from last successful
  5962. $msgCnt = ($msgCnt +1) %256;
  5963. $idl = $hashVd->{idl}+$msgCnt;
  5964. $lo = int(($idl*0x4e6d +12345)/0x10000);#&0xff;
  5965. $hi = ($hashVd->{idh}+$idl*198); #&0xff;
  5966. $nextTimer = (($lo+$hi)&0xff)/4 + 120;
  5967. $nextF += $nextTimer;
  5968. }
  5969. my $offset = AttrVal($name, "cyclicMsgOffset", 200) / 1000.0;
  5970. $nextTimer += $offset;
  5971. $nextF += $offset;
  5972. Log3 $name,5,"CUL_HM $name m:$hashVd->{msgCnt} ->$msgCnt t:$hashVd->{next}->$nextF M:$tn :$nextTimer";
  5973. $hashVd->{next} = $nextF;
  5974. $hashVd->{nextM} = $tn+$nextTimer;# new adjust if we will match
  5975. $hashVd->{msgCnt} = $msgCnt;
  5976. if ($hashVd->{cmd}){
  5977. if ($hashVd->{typ} == 1){
  5978. my $vc = ReadingsVal($name,"valveCtrl","init");
  5979. if ($vc eq 'restart'){
  5980. CUL_HM_UpdtReadSingle($hash,"valveCtrl","unknown",1);
  5981. my $pn = CUL_HM_id2Name($hashVd->{id});
  5982. $hashVd->{ackT} = ReadingsTimestamp($pn, "ValvePosition", "");
  5983. $hashVd->{msgSent} = 0;
  5984. }
  5985. elsif( ($vc ne "init" && $hashVd->{msgRed} <= $hashVd->{miss})
  5986. || $hash->{helper}{virtTC} ne "00") {
  5987. $hashVd->{msgSent} = 1;
  5988. CUL_HM_SndCmd($defs{$hashVd->{nDev}},sprintf("%02X%s%s%s"
  5989. ,$msgCnt
  5990. ,$hashVd->{cmd}
  5991. ,$hash->{helper}{virtTC}
  5992. ,$hashVd->{val}));
  5993. }
  5994. InternalTimer($tn+10,"CUL_HM_valvePosTmr","valveTmr:$vId",0);
  5995. $hashVd->{virtTC} = $hash->{helper}{virtTC};#save for repeat
  5996. $hash->{helper}{virtTC} = "00";
  5997. }
  5998. elsif ($hashVd->{typ} == 2){#send to broadcast
  5999. CUL_HM_PushCmdStack($hash,sprintf("%02X%s%s"
  6000. ,$msgCnt
  6001. ,$hashVd->{cmd}
  6002. ,$hashVd->{val}));
  6003. $hashVd->{next} = $hashVd->{nextM};
  6004. InternalTimer($hashVd->{next},"CUL_HM_valvePosUpdt","valvePos:$vId",0);
  6005. }
  6006. }
  6007. else{
  6008. delete $hash->{helper}{virtTC};
  6009. CUL_HM_UpdtReadSingle($hash,"state","stopped",1);
  6010. return;# terminate processing
  6011. }
  6012. CUL_HM_ProcessCmdStack($hash);
  6013. }
  6014. sub CUL_HM_valvePosTmr(@) {#calc next vd wakeup
  6015. my($in ) = @_;
  6016. my(undef,$vId) = split(':',$in);
  6017. my $hash = CUL_HM_id2Hash($vId);
  6018. my $hashVd = $hash->{helper}{vd};
  6019. my $name = $hash->{NAME};
  6020. my $vc = ReadingsVal($name,"valveCtrl","init");
  6021. my $vcn = $vc;
  6022. if ($hashVd->{msgSent}) {
  6023. my $pn = CUL_HM_id2Name($hashVd->{id});
  6024. my $ackTime = ReadingsTimestamp($pn, "ValvePosition", "");
  6025. if (!$ackTime || $ackTime eq $hashVd->{ackT} ){
  6026. $vcn = (++$hashVd->{miss} > 5) ? "lost"
  6027. :"miss_".$hashVd->{miss};
  6028. Log3 $name,5,"CUL_HM $name virtualTC use fail-timer";
  6029. }
  6030. else{#successful - store sendtime and msgCnt that calculated it
  6031. CUL_HM_UpdtReadSingle($hash,".next","$hashVd->{msgCnt};$hashVd->{nextM}",0);
  6032. $hashVd->{next} = $hashVd->{nextM};#use adjusted value if ack
  6033. $vcn = "ok";
  6034. $hashVd->{miss} = 0;
  6035. }
  6036. $hashVd->{msgSent} = 0;
  6037. $hashVd->{ackT} = $ackTime;
  6038. }
  6039. else {
  6040. $hash->{helper}{virtTC} = $hashVd->{virtTC} if($hash->{helper}{virtTC} eq "00" && $hashVd->{virtTC});
  6041. $hashVd->{miss}++;
  6042. }
  6043. CUL_HM_UpdtReadSingle($hash,"valveCtrl",$vcn,1) if($vc ne $vcn);
  6044. InternalTimer($hashVd->{next},"CUL_HM_valvePosUpdt","valvePos:$vId",0);
  6045. }
  6046. sub CUL_HM_weather(@) {#periodically send weather data
  6047. my($in ) = @_;
  6048. my(undef,$name) = split(':',$in);
  6049. my $hash = $defs{$name};
  6050. my $dName = CUL_HM_getDeviceName($name) ;
  6051. my $ioId = CUL_HM_IoId($defs{$dName});
  6052. CUL_HM_SndCmd($hash,"++8470".$ioId."00000000".$hash->{helper}{weather});
  6053. InternalTimer(gettimeofday()+150,"CUL_HM_weather","weather:$name",0);
  6054. }
  6055. sub CUL_HM_infoUpdtDevData($$$) {#autoread config
  6056. my($name,$hash,$p) = @_;
  6057. my($fw1,$fw2,$mId,$serNo,$stc,$devInfo) = unpack('A1A1A4A20A2A*', $p);
  6058. my $md = $culHmModel->{$mId}{name} ? $culHmModel->{$mId}{name}:"unknown";
  6059. my $serial = pack('H*',$serNo);
  6060. my $fw = sprintf("%d.%d", hex($fw1),hex($fw2));
  6061. $attr{$name}{model} = $md;
  6062. $attr{$name}{subType} = $culHmModel->{$mId}{st};
  6063. $attr{$name}{serialNr} = $serial; # to be removed from attributes
  6064. $attr{$name}{firmware} = $fw; # to be removed from attributes
  6065. # $attr{$name}{".devInfo"} = $devInfo; # to be removed from attributes
  6066. # $attr{$name}{".stc"} = $stc; # to be removed from attributes
  6067. CUL_HM_configUpdate($name) if(ReadingsVal($name,"D-firmware","") ne $fw
  6068. ||ReadingsVal($name,"D-serialNr","") ne $serial
  6069. ||ReadingsVal($name,".D-devInfo","") ne $devInfo
  6070. ||ReadingsVal($name,".D-stc" ,"") ne $stc
  6071. ) ;
  6072. CUL_HM_UpdtReadBulk($hash,1,"D-firmware:$fw",
  6073. "D-serialNr:$serial",
  6074. ".D-devInfo:$devInfo",
  6075. ".D-stc:$stc");
  6076. delete $hash->{helper}{rxType};
  6077. CUL_HM_getRxType($hash); #will update rxType
  6078. $mId = CUL_HM_getMId($hash);# set helper valiable and use result
  6079. # autocreate undefined channels
  6080. my @chanTypesList = split(',',$culHmModel->{$mId}{chn});
  6081. foreach my $chantype (@chanTypesList){
  6082. my ($chnTpName,$chnStart,$chnEnd) = split(':',$chantype);
  6083. my $chnNoTyp = 1;
  6084. for (my $chnNoAbs = $chnStart; $chnNoAbs <= $chnEnd;$chnNoAbs++){
  6085. my $chnId = $hash->{DEF}.sprintf("%02X",$chnNoAbs);
  6086. if (!$modules{CUL_HM}{defptr}{$chnId}){
  6087. my $chnName = $name."_".$chnTpName.(($chnStart == $chnEnd)?
  6088. '':'_'.sprintf("%02d",$chnNoTyp));
  6089. CommandDefine(undef,$chnName.' CUL_HM '.$chnId);
  6090. $attr{CUL_HM_id2Name($chnId)}{model} = $md;
  6091. }
  6092. $attr{CUL_HM_id2Name($chnId)}{model} = $md;
  6093. $chnNoTyp++;
  6094. }
  6095. }
  6096. if ($culHmModel->{$mId}{cyc}){
  6097. CUL_HM_ActAdd($hash->{DEF},AttrVal($name,"actCycle",
  6098. $culHmModel->{$mId}{cyc}));
  6099. }
  6100. }
  6101. sub CUL_HM_getConfig($){
  6102. my $hash = shift;
  6103. my $flag = 'A0';
  6104. my $id = CUL_HM_IoId($hash);
  6105. my $dst = substr($hash->{DEF},0,6);
  6106. my $name = $hash->{NAME};
  6107. CUL_HM_configUpdate($name);
  6108. delete $modules{CUL_HM}{helper}{cfgCmpl}{$name};
  6109. CUL_HM_complConfigTest($name);
  6110. CUL_HM_PushCmdStack($hash,'++'.$flag.'01'.$id.$dst.'00040000000000')
  6111. if ($hash->{helper}{role}{dev});
  6112. my @chnIdList = CUL_HM_getAssChnIds($name);
  6113. delete $hash->{READINGS}{$_}
  6114. foreach (grep /^[\.]?(RegL_)/,keys %{$hash->{READINGS}});
  6115. foreach my $channel (@chnIdList){
  6116. my $cHash = CUL_HM_id2Hash($channel);
  6117. my $chn = substr($channel,6,2);
  6118. delete $cHash->{READINGS}{$_}
  6119. foreach (grep /^[\.]?(RegL_)/,keys %{$cHash->{READINGS}});
  6120. my $lstAr = $culHmModel->{CUL_HM_getMId($cHash)}{lst};
  6121. if($lstAr){
  6122. my $pReq = 0; # Peer request not issued, do only once for channel
  6123. $cHash->{helper}{getCfgListNo}= "";
  6124. foreach my $listEntry (split(",",$lstAr)){#lists define for this channel
  6125. # e.g."1, 5:2.3p ,6:2"
  6126. my ($peerReq,$chnValid)= (0,0);
  6127. my ($listNo,$chnLst1) = split(":",$listEntry);
  6128. if (!$chnLst1){
  6129. $chnValid = 1; #if no entry go for all channels
  6130. $peerReq = 1 if($listNo eq 'p' || $listNo==3 ||$listNo==4); #default
  6131. }
  6132. else{
  6133. my @chnLst = split('\.',$chnLst1);
  6134. foreach my $lchn (@chnLst){
  6135. no warnings;#know that lchan may be followed by 'p' causing a warning
  6136. $chnValid = 1 if (int($lchn) == hex($chn));
  6137. use warnings;
  6138. $peerReq = 1 if ($chnValid && $lchn =~ m/p/);
  6139. last if ($chnValid);
  6140. }
  6141. }
  6142. if ($chnValid){# yes, we will go for a list
  6143. if ($peerReq){# need to get the peers first
  6144. if($listNo ne 'p'){# not if 'only peers'!
  6145. $cHash->{helper}{getCfgList} = "all";
  6146. $cHash->{helper}{getCfgListNo} .= ",".$listNo;
  6147. }
  6148. if (!$pReq){#get peers first, but only once per channel
  6149. CUL_HM_PushCmdStack($cHash,sprintf("++%s01%s%s%s03"
  6150. ,$flag,$id,$dst,$chn));
  6151. $pReq = 1;
  6152. }
  6153. }
  6154. else{
  6155. my $ln = sprintf("%02X",$listNo);
  6156. my $mch = CUL_HM_lstCh($cHash,$ln,$chn);
  6157. CUL_HM_PushCmdStack($cHash,"++$flag"."01$id$dst$mch"."0400000000$ln");
  6158. }
  6159. }
  6160. }
  6161. }
  6162. }
  6163. }
  6164. sub CUL_HM_calcDisWmSet($){
  6165. my $dh = shift;
  6166. my ($txt,$col,$icon) = eval $dh->{exe};
  6167. if ($txt eq "off") { delete $dh->{txt};}
  6168. elsif($txt ne "nc") { $dh->{txt} = substr($txt,0,12);}
  6169. if (!$col ||$col eq "off") { delete $dh->{col};}
  6170. elsif($col ne "nc"){
  6171. if (!defined $disColor{$col}){ delete $dh->{col};}
  6172. else { $dh->{col}=$col; }
  6173. }
  6174. if (!$icon ||$icon eq "noIcon"){delete $dh->{icn};}
  6175. elsif($icon ne "nc"){
  6176. if (!defined $disIcon {$icon}){delete $dh->{icn}}
  6177. else {$dh->{icn}=$icon;}
  6178. }
  6179. }
  6180. sub CUL_HM_calcDisWm($$$){
  6181. my ($hash,$devName,$t)= @_; # t = s or l
  6182. my $msg;
  6183. my $ts = $t eq "s"?"short":"long";
  6184. foreach my $l (sort keys %{$hash->{helper}{dispi}{$t}}){
  6185. my $dh = $hash->{helper}{dispi}{$t}{"$l"};
  6186. CUL_HM_calcDisWmSet($dh) if ($dh->{d} == 2);
  6187. my ($ch,$ln);
  6188. if($dh->{txt}){
  6189. (undef,$ch,undef,$ln) = unpack('A3A2A1A1',$dh->{txt});
  6190. $ch = sprintf("%02X",$ch) if ($ch =~ m/^\d+\d+$/);
  6191. my $rd = ($dh->{txt}?"$dh->{txt} ":"- ")
  6192. .($dh->{col}?"$dh->{col} ":"- ")
  6193. .($dh->{icn}?"$dh->{icn} ":"- ")
  6194. ;
  6195. $rd .= "->". ReadingsVal(InternalVal($devName,"channel_$ch","no")
  6196. ,"text$ln","unkown")
  6197. if (defined $disBtn{$dh->{txt} });
  6198. readingsSingleUpdate($hash,"disp_${ts}_$l"
  6199. ,$rd
  6200. ,0);
  6201. if (defined $disBtn{$dh->{txt} }){
  6202. $msg .= sprintf("12%02X",$disBtn{$dh->{txt} }+0x80);
  6203. }
  6204. else{
  6205. $msg .= "12";
  6206. $msg .= uc( unpack("H*",$dh->{txt})) if ($dh->{txt});
  6207. }
  6208. }
  6209. $msg .= sprintf("11%02X",$disColor{$dh->{col}}+0x80)if ($dh->{col});
  6210. $msg .= sprintf("13%02X",$disIcon{$dh->{icn} }+0x80)if ($dh->{icn});
  6211. $msg .= "0A";# end of line indicator
  6212. }
  6213. my $msgh = "800102";
  6214. $msg .= "03";
  6215. my @txtMsg2;
  6216. foreach (unpack('(A28)*',$msg)){
  6217. push @txtMsg2,$msgh.$_;
  6218. $msgh = "8001";
  6219. }
  6220. $hash->{helper}{disp}{$t} = \@txtMsg2;
  6221. }
  6222. sub CUL_HM_RemoveHMPair($) {####################################################
  6223. my($in ) = shift;
  6224. my(undef,$name) = split(':',$in);
  6225. RemoveInternalTimer("hmPairForSec:$name");
  6226. return if (!$name || !defined $defs{$name});
  6227. delete($defs{$name}{hmPair});
  6228. delete($defs{$name}{hmPairSerial});
  6229. }
  6230. #+++++++++++++++++ Protocol stack, sending, repeat+++++++++++++++++++++++++++++
  6231. sub CUL_HM_pushConfig($$$$$$$$@) {#generate messages to config data to register
  6232. my ($hash,$src,$dst,$chn,$peerAddr,$peerChn,$list,$content,$prep) = @_;
  6233. my $flag = 'A0';
  6234. my $tl = length($content);
  6235. $chn = sprintf("%02X",$chn);
  6236. $peerChn = sprintf("%02X",$peerChn);
  6237. $list = sprintf("%02X",$list);
  6238. $prep = "" if (!defined $prep);
  6239. # --store pending changes in shadow to handle bit manipulations cululativ--
  6240. $peerAddr = "000000" if(!$peerAddr);
  6241. my $peerN = ($peerAddr ne "000000")?CUL_HM_peerChName($peerAddr.$peerChn,$dst):"";
  6242. $peerN =~ s/broadcast//;
  6243. $peerN =~ s/ /_/g;#remote blanks
  6244. my $regLNp = "RegL_".$list.".".$peerN;
  6245. my $regPre = ($hash->{helper}{expert}{raw}?"":".");
  6246. my $regLN = $regPre.$regLNp;
  6247. #--- copy data from readings to shadow
  6248. my $chnhash = $modules{CUL_HM}{defptr}{$dst.$chn};
  6249. $chnhash = $hash if (!$chnhash);
  6250. my $sdH = CUL_HM_shH($chnhash,$list,$dst);
  6251. my $rRd = ReadingsVal($chnhash->{NAME},$regLN,"");
  6252. if (!$sdH->{helper}{shadowReg} ||
  6253. !$sdH->{helper}{shadowReg}{$regLNp}){
  6254. $sdH->{helper}{shadowReg}{$regLNp} = $rRd;
  6255. }
  6256. #--- update with ne value
  6257. my $regs = $sdH->{helper}{shadowReg}{$regLNp};
  6258. for(my $l = 0; $l < $tl; $l+=4) { #substitute changed bytes in shadow
  6259. my $addr = substr($content,$l,2);
  6260. my $data = substr($content,$l+2,2);
  6261. if(!$regs || !($regs =~ s/$addr:../$addr:$data/)){
  6262. $regs .= " ".$addr.":".$data;
  6263. }
  6264. }
  6265. $sdH->{helper}{shadowReg}{$regLNp} = $regs; # update shadow
  6266. my @changeList;
  6267. if ($prep eq "exec"){#update complete registerset
  6268. @changeList = keys%{$sdH->{helper}{shadowReg}};
  6269. }
  6270. elsif ($prep eq "prep"){
  6271. return; #prepare shadowReg only. More data expected.
  6272. }
  6273. else{
  6274. push @changeList,$regLNp;
  6275. }
  6276. my $changed = 0;# did we write
  6277. foreach my $nrn(@changeList){
  6278. my $change;
  6279. my $nrRd = ReadingsVal($chnhash->{NAME},$regPre.$nrn,"");
  6280. foreach (sort split " ",$sdH->{helper}{shadowReg}{$nrn}){
  6281. $change .= $_." " if ($nrRd !~ m/$_/);# filter only changes
  6282. }
  6283. next if (!$change);#no changes
  6284. $change =~ s/00:00//;
  6285. $change =~ s/(\ |:)//g;
  6286. if ($nrRd){
  6287. $chnhash->{READINGS}{$regPre.$nrn}{VAL} =~ s/00:00//; #mark incomplete as we go for a change;
  6288. }
  6289. my $pN;
  6290. $changed = 1;# yes, we did
  6291. ($list,$pN) = ($1,$2) if($nrn =~ m/RegL_(..)\.(.*)/);
  6292. if ($pN){($peerAddr,$peerChn) = unpack('A6A2', CUL_HM_name2Id($pN,$hash));}
  6293. else {($peerAddr,$peerChn) = ('000000','00');}
  6294. if (AttrVal($chnhash->{NAME},"peerIDs",0) =~ m/${peerAddr}00/){$peerChn = "00"}# if device we are not sure about device or channel. Check peers
  6295. CUL_HM_updtRegDisp($hash,$list,$peerAddr.$peerChn);
  6296. ############partition
  6297. # my @chSplit = unpack('(A28)*',$change);
  6298. my @chSplit = unpack('(A1120)*',$change);# makes max 40 lines, 280 byte
  6299. foreach my $chSpl(@chSplit){
  6300. my $mch = CUL_HM_lstCh($chnhash,$list,$chn);
  6301. CUL_HM_PushCmdStack($hash, "++".$flag.'01'.$src.$dst.$mch.'05'.
  6302. $peerAddr.$peerChn.$list);
  6303. $tl = length($chSpl);
  6304. for(my $l = 0; $l < $tl; $l+=28) {
  6305. my $ml = $tl-$l < 28 ? $tl-$l : 28;
  6306. CUL_HM_PushCmdStack($hash, "++A001".$src.$dst.$chn."08".
  6307. substr($chSpl,$l,$ml));
  6308. }
  6309. CUL_HM_PushCmdStack($hash,"++A001".$src.$dst.$mch."06");
  6310. }
  6311. #########
  6312. }
  6313. if ($changed){
  6314. CUL_HM_complConfig($hash->{NAME},1);
  6315. CUL_HM_qAutoRead($hash->{NAME},3) ;
  6316. }
  6317. }
  6318. sub CUL_HM_PushCmdStack($$) {
  6319. my ($chnhash, $cmd) = @_;
  6320. my @arr = ();
  6321. my $hash = CUL_HM_getDeviceHash($chnhash);
  6322. my $name = $hash->{NAME};
  6323. if(!$hash->{cmdStack}){# this is a new 'burst' of messages
  6324. $hash->{cmdStack} = \@arr;
  6325. $hash->{helper}{prt}{bErr}=0 if ($hash->{helper}{prt}{sProc} != 1);# not processing
  6326. }
  6327. push(@{$hash->{cmdStack}}, $cmd);
  6328. my $entries = scalar @{$hash->{cmdStack}};
  6329. $hash->{protCmdPend} = $entries." CMDs_pending";
  6330. CUL_HM_protState($hash,"CMDs_pending") if($hash->{helper}{prt}{sProc} != 1);# not processing
  6331. }
  6332. sub CUL_HM_ProcessCmdStack($) {
  6333. my ($chnhash) = @_;
  6334. my $hash = CUL_HM_getDeviceHash($chnhash);
  6335. if (!$hash->{helper}{prt}{rspWait}{cmd}){
  6336. if($hash->{cmdStack} && @{$hash->{cmdStack}}){
  6337. CUL_HM_SndCmd($hash, shift @{$hash->{cmdStack}});
  6338. }
  6339. elsif($hash->{helper}{prt}{sProc} != 0){
  6340. CUL_HM_protState($hash,"CMDs_done");
  6341. }
  6342. }
  6343. return;
  6344. }
  6345. sub CUL_HM_respWaitSu($@){ #setup response for multi-message response
  6346. # single commands
  6347. # cmd: single msg that needs to be ACKed
  6348. # mNo: number of message (needs to be in ACK)
  6349. # mNoWu: number of message if wakeup
  6350. # reSent: number of resends already done - usually init with 1
  6351. # wakeup: was wakeup message (burst devices)
  6352. #
  6353. # commands with multi-message answer
  6354. # PendCmd: command message
  6355. # Pending: type of answer we are awaiting
  6356. # forChn: which channel are we working on?
  6357. # forList: which list are we waiting for? (optional)
  6358. # forPeer: which peer are we waiting for? (optional)
  6359. my ($hash,@a)=@_;
  6360. my $mHsh = $hash->{helper}{prt};
  6361. $modules{CUL_HM}{prot}{rspPend}++ if(!$mHsh->{rspWait}{cmd});
  6362. foreach (@a){
  6363. next if (!$_);
  6364. my ($f,$d)=split ":=",$_;
  6365. $mHsh->{rspWait}{$f}=$d;
  6366. }
  6367. my $to = gettimeofday() + (($mHsh->{rspWait}{Pending})?rand(20)/10+4:
  6368. rand(40)/10+2);
  6369. if ($mHsh->{rspWait}{cmd}) {
  6370. my (undef,$mFlg) = unpack 'A6A2',$mHsh->{rspWait}{cmd};
  6371. $to += 1 if($mFlg && (hex($mFlg) & 0x10)); # burst wakeup
  6372. }
  6373. InternalTimer($to,"CUL_HM_respPendTout","respPend:$hash->{DEF}", 0);
  6374. }
  6375. sub CUL_HM_responseSetup($$) {#store all we need to handle the response
  6376. #setup repeatTimer and cmdStackControll
  6377. my ($hash,$cmd) = @_;
  6378. return if($hash->{helper}{prt}{sProc} == 3);#not relevant while FW update
  6379. my (undef,$mNo,$mFlg,$mTp,$src,$dst,$chn,$sTp,$dat) =
  6380. unpack 'A4A2A2A2A6A6A2A2A*',$cmd;
  6381. $mFlg = hex($mFlg);
  6382. if (($mFlg & 0x20) && ($dst ne '000000')){#msg wants ack
  6383. my $rss = $hash->{helper}{prt}{wuReSent}
  6384. ? $hash->{helper}{prt}{wuReSent}
  6385. :1;#resend count - may need preloaded for WU device
  6386. if ($mTp eq '01' && $sTp) {
  6387. if ($sTp eq "03"){ #PeerList-----------
  6388. #--- remember request params in device level
  6389. CUL_HM_respWaitSu ($hash,"Pending:=PeerList"
  6390. ,"cmd:=$cmd" ,"forChn:=$chn"
  6391. ,"mNo:=".hex($mNo)
  6392. ,"reSent:=$rss");
  6393. #--- remove readings in channel
  6394. my $chnhash = $modules{CUL_HM}{defptr}{"$dst$chn"};
  6395. $chnhash = $hash if (!$chnhash);
  6396. delete $chnhash->{READINGS}{peerList};#empty old list
  6397. delete $chnhash->{peerList};#empty old list
  6398. delete $chnhash->{helper}{peerIDsRaw};
  6399. $attr{$chnhash->{NAME}}{peerIDs} = '';
  6400. }
  6401. elsif($sTp eq "04"){ #RegisterRead-------
  6402. my ($peer, $list) = unpack 'A8A2',$dat;
  6403. $peer = ($peer ne "00000000")?CUL_HM_peerChName($peer,$dst):"";
  6404. #--- set messaging items
  6405. my $chnhash = $modules{CUL_HM}{defptr}{"$dst$chn"};
  6406. $chnhash = $hash if(!$chnhash);
  6407. my $fch = CUL_HM_shC($chnhash,$list,$chn);
  6408. CUL_HM_respWaitSu ($hash,"Pending:=RegisterRead"
  6409. ,"cmd:=$cmd" ,"forChn:=$fch"
  6410. ,"forList:=$list","forPeer:=$peer"
  6411. ,"mNo:=".hex($mNo)
  6412. ,"nAddr:=0"
  6413. ,"reSent:=$rss");
  6414. #--- remove channel entries that will be replaced
  6415. #empty val since reading will be cumulative
  6416. my $rlName = ($chnhash->{helper}{expert}{raw}?"":".")."RegL_".$list.".".$peer;
  6417. $chnhash->{READINGS}{$rlName}{VAL}="";
  6418. my $chnHash = $modules{CUL_HM}{defptr}{$dst.$chn};
  6419. delete ($chnhash->{READINGS}{$rlName}{TIME});
  6420. }
  6421. elsif($sTp eq "09"){ #SerialRead-------
  6422. CUL_HM_respWaitSu ($hash,"Pending:=SerialRead"
  6423. ,"cmd:=$cmd" ,"reSent:=$rss");
  6424. }
  6425. else{
  6426. CUL_HM_respWaitSu ($hash,"cmd:=$cmd","mNo:=$mNo","reSent:=$rss");
  6427. }
  6428. $hash->{helper}{cSnd} =~ s/.*,// if($hash->{helper}{cSnd});
  6429. $hash->{helper}{cSnd} .= ",".substr($cmd,8);
  6430. }
  6431. elsif($mTp eq '03') {#AES response - keep former wait and start timer again
  6432. #
  6433. if ($hash->{helper}{prt}{rspWait}){
  6434. RemoveInternalTimer("respPend:$hash->{DEF}");
  6435. my $mHsh = $hash->{helper}{prt};
  6436. my $to = gettimeofday() + (($mHsh->{helper}{prt}{rspWait}{Pending})?rand(20)/10+4:
  6437. rand(40)/10+2);
  6438. if ($mHsh->{rspWait}{cmd}) {
  6439. my (undef,$mFlg) = unpack 'A6A2',$mHsh->{rspWait}{cmd};
  6440. $to += 1 if($mFlg && (hex($mFlg) & 0x10)); # burst wakeup
  6441. }
  6442. InternalTimer($to,"CUL_HM_respPendTout","respPend:$hash->{DEF}", 0);
  6443. }
  6444. else{
  6445. # nothing - we dont know the origonal message
  6446. }
  6447. }
  6448. elsif($mTp eq '11') {
  6449. my $to = "";
  6450. if ($chn eq "02"){#!!! chn is subtype!!!
  6451. if ($dat =~ m/(..)....(....)/){#lvl ne 0 and timer on
  6452. # store Channel in this datafield.
  6453. # dimmer may answer with wrong virtual channel - then dont resent!
  6454. $hash->{helper}{tmdOn} = $sTp if ($1 ne "00" && $2 !~ m/(0000|FFFF)/);
  6455. $to = "timedOn:=1";
  6456. }
  6457. }
  6458. CUL_HM_respWaitSu ($hash,"cmd:=$cmd","mNo:=$mNo","reSent:=$rss",$to);
  6459. $hash->{helper}{cSnd} =~ s/.*,// if($hash->{helper}{cSnd});
  6460. $hash->{helper}{cSnd} .= ",".substr($cmd,8);
  6461. }
  6462. elsif($mTp eq '12' && $mFlg & 0x10){#wakeup with burst
  6463. # response setup - do not repeat, set counter to 250
  6464. CUL_HM_respWaitSu ($hash,"cmd:=$cmd","mNo:=$mNo","reSent:=$rss","brstWu:=1");
  6465. }
  6466. elsif($mTp !~ m/C./) {#
  6467. CUL_HM_respWaitSu ($hash,"cmd:=$cmd","mNo:=$mNo","reSent:=$rss");
  6468. }
  6469. CUL_HM_protState($hash,"CMDs_processing...");#if($mTp ne '03');
  6470. }
  6471. else{# no answer expected
  6472. if($hash->{cmdStack} && scalar @{$hash->{cmdStack}}){
  6473. if (!$hash->{helper}{prt}{sleeping}){
  6474. CUL_HM_protState($hash,"CMDs_processing...");
  6475. InternalTimer(gettimeofday()+.1, "CUL_HM_ProcessCmdStack", $hash, 0);
  6476. }
  6477. else{
  6478. delete $hash->{helper}{prt}{sleeping};
  6479. }
  6480. }
  6481. elsif(!$hash->{helper}{prt}{rspWait}{cmd}){
  6482. CUL_HM_protState($hash,"CMDs_done");
  6483. }
  6484. }
  6485. my $mmcS = $hash->{helper}{prt}{mmcS}?$hash->{helper}{prt}{mmcS}:0;
  6486. if ($mTp eq '01'){
  6487. my $oCmd = "++".substr($cmd,6);
  6488. if ($sTp eq "05"){
  6489. my @arr = ($oCmd);
  6490. $hash->{helper}{prt}{mmcA}=\@arr;
  6491. $hash->{helper}{prt}{mmcS} = 1;
  6492. }
  6493. elsif ($sTp =~ m/^(07|08)$/ && ($mmcS == 1||$mmcS == 2)){
  6494. push @{$hash->{helper}{prt}{mmcA}},$oCmd;
  6495. $hash->{helper}{prt}{mmcS} = 2;
  6496. }
  6497. elsif ($sTp eq "06" && ($mmcS == 2)){
  6498. push @{$hash->{helper}{prt}{mmcA}},$oCmd;
  6499. $hash->{helper}{prt}{mmcS} = 3;
  6500. }
  6501. elsif ($mmcS){ #
  6502. delete $hash->{helper}{prt}{mmcA};
  6503. delete $hash->{helper}{prt}{mmcS};
  6504. }
  6505. }
  6506. elsif($mmcS){
  6507. delete $hash->{helper}{prt}{mmcA};
  6508. delete $hash->{helper}{prt}{mmcS};
  6509. }
  6510. if($hash->{cmdStack} && scalar @{$hash->{cmdStack}}){
  6511. $hash->{protCmdPend} = scalar @{$hash->{cmdStack}}." CMDs pending";
  6512. }
  6513. else{
  6514. delete($hash->{protCmdPend});
  6515. }
  6516. }
  6517. sub CUL_HM_sndIfOpen($) {
  6518. my(undef,$io) = split(':',$_[0]);
  6519. RemoveInternalTimer("sndIfOpen:$io");# should not be necessary, but
  6520. my $ioHash = $defs{$io};
  6521. if ( ReadingsVal($io,"state","") !~ m/^(opened|Initialized)$/
  6522. ||(defined $ioHash->{XmitOpen} && $ioHash->{XmitOpen} != 1)
  6523. # ||$modules{CUL_HM}{prot}{rspPend}>=$maxPendCmds
  6524. ){#still no send allowed
  6525. if ( $modules{CUL_HM}{$io}{tmrStart} &&
  6526. ($modules{CUL_HM}{$io}{tmrStart} < gettimeofday() - $modules{CUL_HM}{hmIoMaxDly})){
  6527. # we need to clean up - this is way to long Stop delay
  6528. if ($modules{CUL_HM}{$io}{pendDev}) {
  6529. while(@{$modules{CUL_HM}{$io}{pendDev}}){
  6530. my $name = shift(@{$modules{CUL_HM}{$io}{pendDev}});
  6531. CUL_HM_eventP($defs{$name},"IOerr");
  6532. }
  6533. }
  6534. $modules{CUL_HM}{$io}{tmr} = 0;
  6535. }
  6536. else{
  6537. if ($modules{CUL_HM}{$io}{pendDev} && @{$modules{CUL_HM}{$io}{pendDev}}){
  6538. InternalTimer(gettimeofday()+$IOpoll,"CUL_HM_sndIfOpen",
  6539. "sndIfOpen:$io", 0);
  6540. }
  6541. }
  6542. }
  6543. else{
  6544. $modules{CUL_HM}{$io}{tmr} = 0;
  6545. if ($modules{CUL_HM}{$io}{pendDev} && @{$modules{CUL_HM}{$io}{pendDev}}){
  6546. my $name = shift(@{$modules{CUL_HM}{$io}{pendDev}});
  6547. CUL_HM_ProcessCmdStack($defs{$name});
  6548. if (@{$modules{CUL_HM}{$io}{pendDev}}){#tmr = 0, clearing queue slowly
  6549. InternalTimer(gettimeofday()+$IOpoll,"CUL_HM_sndIfOpen",
  6550. "sndIfOpen:$io", 0);
  6551. }
  6552. }
  6553. }
  6554. }
  6555. sub CUL_HM_SndCmd($$) {
  6556. my ($hash, $cmd) = @_;
  6557. $hash = CUL_HM_getDeviceHash($hash);
  6558. if( AttrVal($hash->{NAME},"ignore",0) != 0
  6559. || AttrVal($hash->{NAME},"dummy" ,0) != 0){
  6560. CUL_HM_eventP($hash,"dummy");
  6561. return;
  6562. }
  6563. CUL_HM_assignIO($hash) ;
  6564. if(!defined $hash->{IODev} ||!defined $hash->{IODev}{NAME}){
  6565. CUL_HM_eventP($hash,"IOerr");
  6566. CUL_HM_UpdtReadSingle($hash,"state","ERR_IOdev_undefined",1);
  6567. return;
  6568. }
  6569. my $io = $hash->{IODev};
  6570. my $ioName = $io->{NAME};
  6571. if ( ReadingsVal($ioName,"state","") !~ m/^(opened|Initialized)$/ # we need to queue
  6572. ||(hex substr($cmd,2,2) & 0x20) && ( # check for commands with resp-req
  6573. $modules{CUL_HM}{$ioName}{tmr} # queue already running
  6574. ||(defined $io->{XmitOpen} && $io->{XmitOpen} != 1)#overload, dont send
  6575. )
  6576. ){
  6577. # push device to list
  6578. if (!defined $modules{CUL_HM}{$ioName}{tmr}){
  6579. # some setup work for this timer
  6580. $modules{CUL_HM}{$ioName}{tmr} = 0;
  6581. if (!$modules{CUL_HM}{$ioName}{pendDev}){# generate if not exist
  6582. my @arr2 = ();
  6583. $modules{CUL_HM}{$ioName}{pendDev} = \@arr2;
  6584. }
  6585. }
  6586. # shall we delay commands if IO device is not present?
  6587. # it could cause trouble if light switches on after a long period
  6588. # repetition will be stopped after 1min forsecurity reason.
  6589. # so do: return cmd to queue and set state to pending again.
  6590. # device will be queued @ CUL_HM. Timer will perform cyclic check for IO to return.
  6591. #
  6592. my @arr = ();
  6593. $hash->{cmdStack} = \@arr if(!$hash->{cmdStack});
  6594. if( $hash->{helper}{prt}{rspWait} && $hash->{helper}{prt}{rspWait}{cmd}){
  6595. (undef,$cmd) = unpack 'A4A*',$hash->{helper}{prt}{rspWait}{cmd};
  6596. }
  6597. unshift (@{$hash->{cmdStack}}, $cmd);#pushback cmd, wait for opportunity
  6598. @{$modules{CUL_HM}{$ioName}{pendDev}} =
  6599. CUL_HM_noDup(@{$modules{CUL_HM}{$ioName}{pendDev}},$hash->{NAME});
  6600. CUL_HM_respPendRm($hash);#rm timer - we are out
  6601. CUL_HM_protState($hash,"CMDs_pending");
  6602. if ($modules{CUL_HM}{$ioName}{tmr} != 1){# need to start timer
  6603. my $tn = gettimeofday();
  6604. InternalTimer($tn+$IOpoll, "CUL_HM_sndIfOpen", "sndIfOpen:$ioName", 0);
  6605. $modules{CUL_HM}{$ioName}{tmr} = 1;
  6606. $modules{CUL_HM}{$ioName}{tmrStart} = $tn; # abort if to long
  6607. }
  6608. return;
  6609. }
  6610. $cmd =~ m/^(..)(.*)$/;
  6611. my ($mn, $cmd2) = unpack 'A2A*',$cmd;
  6612. if($mn eq "++") {
  6613. $mn = ($hash->{helper}{HM_CMDNR} + 1) & 0xff;
  6614. $hash->{helper}{HM_CMDNR} = $mn;
  6615. }
  6616. elsif($cmd =~ m/^[+-]/){; #continue pure
  6617. IOWrite($hash, "", $cmd);
  6618. return;
  6619. }
  6620. else {
  6621. $mn = hex($mn);
  6622. }
  6623. $cmd = sprintf("As%02X%02X%s", length($cmd2)/2+1, $mn, $cmd2);
  6624. IOWrite($hash, "", $cmd);
  6625. CUL_HM_statCnt($ioName,"s",hex(substr($cmd2,0,2)));
  6626. CUL_HM_eventP($hash,"Snd");
  6627. CUL_HM_eventP($hash,"SndB") if (hex(substr($cmd2,0,2)) & 0x10);
  6628. CUL_HM_responseSetup($hash,$cmd);
  6629. $cmd =~ m/^As(..)(..)(..)(..)(......)(......)(.*)/;
  6630. CUL_HM_DumpProtocol("SND", $io, ($1,$2,$3,$4,$5,$6,$7));
  6631. }
  6632. sub CUL_HM_statCnt(@) {# set msg statistics for (r)ecive (s)end or (u)pdate
  6633. my ($ioName,$dir,$typ) = @_;
  6634. my $stat = $modules{CUL_HM}{stat};
  6635. if (!$stat->{$ioName}){
  6636. foreach my $ud ("r","s","rb","sb"){
  6637. $stat->{$ud}{$ioName}{h}{$_} = 0 foreach(0..23);
  6638. $stat->{$ud}{$ioName}{d}{$_} = 0 foreach(0..6);
  6639. }
  6640. $stat->{$ioName}{last} = 0;
  6641. }
  6642. my @l = localtime(gettimeofday());
  6643. if ($l[2] != $stat->{$ioName}{last}){#next field
  6644. if ($l[2] < $stat->{$ioName}{last}){#next day
  6645. my $recentD = ($l[6]+5)%7;
  6646. foreach my $ud ("r","s","rb","sb"){
  6647. $stat->{$ud}{$ioName}{d}{$recentD} = 0;
  6648. $stat->{$ud}{$ioName}{d}{$recentD} += $stat->{$ud}{$ioName}{h}{$_} foreach (0..23);
  6649. }
  6650. }
  6651. foreach my $ud ("r","s","rb","sb"){
  6652. $stat->{$ud}{$ioName}{h}{$l[2]} = 0;
  6653. }
  6654. $stat->{$ioName}{last} = $l[2];
  6655. }
  6656. if ($dir ne "u"){
  6657. $stat->{$dir}{$ioName}{h}{$l[2]}++;
  6658. if (defined($typ) && ($typ & 0x10)){
  6659. $stat->{$dir."b"}{$ioName}{h}{$l[2]}++;
  6660. }
  6661. }
  6662. }
  6663. sub CUL_HM_statCntRfresh($) {# update statistic once a day
  6664. my ($ioName,$dir) = @_;
  6665. foreach (keys %{$modules{CUL_HM}{stat}{r}}){
  6666. if (!$defs{$ioName}){#IO device is deleted, clear counts
  6667. delete $modules{CUL_HM}{stat}{$ioName};
  6668. delete $modules{CUL_HM}{stat}{r}{$ioName};
  6669. delete $modules{CUL_HM}{stat}{s}{$ioName};
  6670. delete $modules{CUL_HM}{stat}{rb}{$ioName};
  6671. delete $modules{CUL_HM}{stat}{sb}{$ioName};
  6672. next;
  6673. }
  6674. CUL_HM_statCnt($_,"u") if ($_ ne "dummy");
  6675. }
  6676. RemoveInternalTimer("StatCntRfresh");
  6677. InternalTimer(gettimeofday()+3600*20,"CUL_HM_statCntRfresh","StatCntRfresh",0);
  6678. }
  6679. sub CUL_HM_trigLastEvent($$$$$){#set trigLast for central setting commands
  6680. my ($dst,$mTp,$p01,$p02,$chn) = @_;
  6681. my $name = CUL_HM_id2Name($dst.$chn);
  6682. return if (!defined $defs{$name});
  6683. CUL_HM_UpdtReadSingle($defs{$name},"trigLast","fhem:".$p01,1);
  6684. }
  6685. sub CUL_HM_respPendRm($) {#del response related entries in messageing entity
  6686. my ($hash) = @_;
  6687. return if (!defined($hash->{DEF}));
  6688. $modules{CUL_HM}{prot}{rspPend}-- if($hash->{helper}{prt}{rspWait}{cmd});
  6689. delete $hash->{helper}{prt}{rspWait};
  6690. delete $hash->{helper}{prt}{wuReSent};
  6691. delete $hash->{helper}{tmdOn};
  6692. # delete $hash->{helper}{prt}{mmcA};
  6693. # delete $hash->{helper}{prt}{mmcS};
  6694. RemoveInternalTimer($hash); # remove resend-timer
  6695. RemoveInternalTimer("respPend:$hash->{DEF}");# remove responsePending timer
  6696. $respRemoved = 1;
  6697. }
  6698. sub CUL_HM_respPendTout($) {
  6699. my ($HMidIn) = @_;
  6700. my(undef,$HMid) = split(":",$HMidIn,2);
  6701. my $hash = $modules{CUL_HM}{defptr}{$HMid};
  6702. my $pHash = $hash->{helper}{prt};#shortcut
  6703. if ($hash && $hash->{DEF} ne '000000'){# we know the device
  6704. my $name = $hash->{NAME};
  6705. $pHash->{awake} = 0 if (defined $pHash->{awake});# set to asleep
  6706. return if(!$pHash->{rspWait}{reSent}); # Double timer?
  6707. my $rxt = CUL_HM_getRxType($hash);
  6708. if ($pHash->{rspWait}{brstWu}){#burst-wakeup try failed (conditionalBurst)
  6709. CUL_HM_respPendRm($hash);# don't count problems, was just a try
  6710. $hash->{protCondBurst} = "off" if (!$hash->{protCondBurst}||
  6711. $hash->{protCondBurst} !~ m/forced/);;
  6712. $pHash->{brstWu} = 0;# finished
  6713. $pHash->{awake} = 0;# set to asleep
  6714. CUL_HM_protState($hash,"CMDs_pending");
  6715. # commandstack will be executed when device wakes up itself
  6716. }
  6717. elsif ($pHash->{try}){ #send try failed - revert, wait for wakeup
  6718. # device might still be busy with writing flash or similar
  6719. # we have to wait for next wakeup
  6720. unshift (@{$hash->{cmdStack}}, "++".substr($pHash->{rspWait}{cmd},6));
  6721. delete $pHash->{try};
  6722. CUL_HM_respPendRm($hash);# do not count problems with wakeup try, just wait
  6723. CUL_HM_protState($hash,"CMDs_pending");
  6724. }
  6725. elsif (ReadingsVal($hash->{IODev}->{NAME},"state","") !~ m/^(opened|Initialized)$/){#IO errors
  6726. CUL_HM_eventP($hash,"IOdly");
  6727. CUL_HM_ProcessCmdStack($hash) if($rxt & 0x03);#burst/all
  6728. }
  6729. elsif ($pHash->{rspWait}{reSent} > AttrVal($name,"msgRepeat",($rxt & 0x9B)?3:0)#too many
  6730. ){#config cannot retry
  6731. my $pendCmd = "MISSING ACK";
  6732. if ($pHash->{rspWait}{Pending}){
  6733. $pendCmd = "RESPONSE TIMEOUT:".$pHash->{rspWait}{Pending};
  6734. CUL_HM_complConfig($name,1);# check with delay
  6735. }
  6736. CUL_HM_eventP($hash,"ResndFail");
  6737. CUL_HM_UpdtReadSingle($hash,"state",$pendCmd,1);
  6738. }
  6739. else{# manage retries
  6740. $pHash->{rspWait}{reSent}++;
  6741. CUL_HM_eventP($hash,"Resnd");
  6742. Log3 $name,4,"CUL_HM_Resend: $name nr ".$pHash->{rspWait}{reSent};
  6743. if ($hash->{protCondBurst}&&$hash->{protCondBurst} eq "on" ){
  6744. #timeout while conditional burst was active. try re-wakeup
  6745. my $addr = CUL_HM_IoId($hash);
  6746. $pHash->{rspWaitSec}{$_} = $pHash->{rspWait}{$_}
  6747. foreach (keys%{$pHash->{rspWait}});
  6748. CUL_HM_SndCmd($hash,"++B112$addr$HMid");
  6749. $hash->{helper}{prt}{awake}=4;# start re-wakeup
  6750. }
  6751. elsif($rxt & 0x18){# wakeup/lazy devices
  6752. #need to fill back command to queue and wait for next wakeup
  6753. if ($pHash->{mmcA}){#fillback multi-message command
  6754. unshift @{$hash->{cmdStack}},$_ foreach (reverse@{$pHash->{mmcA}});
  6755. delete $pHash->{mmcA};
  6756. delete $pHash->{mmcS};
  6757. }
  6758. else{#fillback simple command
  6759. unshift (@{$hash->{cmdStack}},"++".substr($pHash->{rspWait}{cmd},6))
  6760. if (substr($pHash->{rspWait}{cmd},8,2) ne '12');# not wakeup
  6761. }
  6762. my $wuReSent = $pHash->{rspWait}{reSent};# save 'invalid' count
  6763. CUL_HM_respPendRm($hash);#clear
  6764. CUL_HM_protState($hash,"CMDs_pending");
  6765. $pHash->{wuReSent} = $wuReSent;# save 'invalid' count
  6766. }
  6767. else{# normal device resend
  6768. if ($rxt & 0x02){# type = burst - need to set burst-Bit for retry
  6769. if ($pHash->{mmcA}){#fillback multi-message command
  6770. unshift @{$hash->{cmdStack}},$_ foreach (reverse@{$pHash->{mmcA}});
  6771. delete $pHash->{mmcA};
  6772. delete $pHash->{mmcS};
  6773. my $cmd = shift @{$hash->{cmdStack}};
  6774. $cmd = sprintf("As%02X01%s", length($cmd)/2, substr($cmd,2));
  6775. $pHash->{rspWait}{cmd} = $cmd;
  6776. CUL_HM_responseSetup($hash,$cmd);
  6777. }
  6778. my ($pre,$tp,$tail) = unpack 'A6A2A*',$pHash->{rspWait}{cmd};
  6779. $pHash->{rspWait}{cmd} = sprintf("%s%02X%s",$pre,(hex($tp)|0x10),$tail);
  6780. }
  6781. IOWrite($hash, "", $pHash->{rspWait}{cmd});
  6782. CUL_HM_eventP($hash,"SndB") if(hex(substr($pHash->{rspWait}{cmd},6,2)) & 0x10);
  6783. CUL_HM_statCnt($hash->{IODev}{NAME},"s",hex(substr($pHash->{rspWait}{cmd},6,2)));
  6784. InternalTimer(gettimeofday()+rand(20)/10+4,"CUL_HM_respPendTout","respPend:$hash->{DEF}", 0);
  6785. }
  6786. }
  6787. }
  6788. }
  6789. sub CUL_HM_respPendToutProlong($) {#used when device sends part responses
  6790. my ($hash) = @_;
  6791. RemoveInternalTimer("respPend:$hash->{DEF}");
  6792. InternalTimer(gettimeofday()+2, "CUL_HM_respPendTout", "respPend:$hash->{DEF}", 0);
  6793. }
  6794. sub CUL_HM_FWupdateSteps($){#steps for FW update
  6795. my $mIn = shift;
  6796. my $step = $modules{CUL_HM}{helper}{updateStep};
  6797. my $name = $modules{CUL_HM}{helper}{updatingName};
  6798. my $dst = $modules{CUL_HM}{helper}{updateDst};
  6799. my $id = $modules{CUL_HM}{helper}{updateId};
  6800. my $mNo = $modules{CUL_HM}{helper}{updateNbr};
  6801. my $hash = $defs{$name};
  6802. my $mNoA = sprintf("%02X",$mNo);
  6803. return "" if ($mIn !~ m/$mNoA..02$dst${id}00/ && $mIn !~ m/..10${dst}00000000/);
  6804. if ($mIn =~ m/$mNoA..02$dst${id}00/){
  6805. $modules{CUL_HM}{helper}{updateRetry} = 0;
  6806. $modules{CUL_HM}{helper}{updateNbrPassed} = $mNo;
  6807. }
  6808. if ($step == 0){#check bootloader entered - now change speed
  6809. return "" if ($mIn =~ m/$mNoA..02$dst${id}00/);
  6810. Log3 $name,2,"CUL_HM fwUpdate $name entered mode. IO-speed: fast";
  6811. $mNo = (++$mNo)%256; $mNoA = sprintf("%02X",$mNo);
  6812. CUL_HM_SndCmd($hash,"${mNoA}00CB$id${dst}105B11F81547");
  6813. # CUL_HM_SndCmd($hash,"${mNoA}20CB$id${dst}105B11F815470B081A1C191D1BC71C001DB221B623EA");
  6814. select(undef, undef, undef, (0.04));
  6815. CUL_HM_FWupdateSpeed($name,100);
  6816. select(undef, undef, undef, (0.04));
  6817. $mNo = (++$mNo)%256; $mNoA = sprintf("%02X",$mNo);
  6818. $modules{CUL_HM}{helper}{updateStep} = $step = 1;
  6819. $modules{CUL_HM}{helper}{updateNbr} = $mNo;
  6820. RemoveInternalTimer("fail:notInBootLoader");
  6821. #InternalTimer(gettimeofday()+0.3,"CUL_HM_FWupdateSim","${dst}${id}00",0);
  6822. }
  6823. ##16.130 CUL_Parse: A 0A 30 0002 235EDB 255E91 00
  6824. ##16.338 CUL_Parse: A 0A 39 0002 235EDB 255E91 00
  6825. ##16.716 CUL_Parse: A 0A 42 0002 235EDB 255E91 00
  6826. ##17.093 CUL_Parse: A 0A 4B 0002 235EDB 255E91 00
  6827. ##17.471 CUL_Parse: A 0A 54 0002 235EDB 255E91 00
  6828. ##17.848 CUL_Parse: A 0A 5D 0002 235EDB 255E91 00
  6829. ##...
  6830. ##43.621 4: CUL_Parse: iocu1 A 0A 58 0002 235EDB 255E91 00
  6831. ##44.034 4: CUL_Parse: iocu1 A 0A 61 0002 235EDB 255E91 00
  6832. ##44.161 4: CUL_Parse: iocu1 A 1D 6A 20CA 255E91 235EDB 00121642446D1C3F45F240ED84DC5E7C1AB7554D
  6833. ##44.180 4: CUL_Parse: iocu1 A 0A 6A 0002 235EDB 255E91 00
  6834. ## one block = 10 messages in 200-1000ms
  6835. my $blocks = scalar(@{$modules{CUL_HM}{helper}{updateData}});
  6836. RemoveInternalTimer("respPend:$hash->{DEF}");
  6837. RemoveInternalTimer("fail:Block".($step-1));
  6838. if ($blocks < $step){#last block
  6839. CUL_HM_FWupdateEnd("done");
  6840. Log3 $name,2,"CUL_HM fwUpdate completed";
  6841. return "done";
  6842. }
  6843. else{# programming continue
  6844. my $bl = ${$modules{CUL_HM}{helper}{updateData}}[$step-1];
  6845. my $no = scalar(@{$bl});
  6846. Log3 $name,5,"CUL_HM fwUpdate write block $step of $blocks: $no messages";
  6847. foreach my $msgP (@{$bl}){
  6848. $mNo = (++$mNo)%256; $mNoA = sprintf("%02X",$mNo);
  6849. CUL_HM_SndCmd($hash, $mNoA.((--$no)?"00":"20")."CA$id$dst".$msgP);
  6850. # select(undef, undef, undef, (0.01));# no wait necessary - FHEM is slow anyway
  6851. }
  6852. $modules{CUL_HM}{helper}{updateStep}++;
  6853. $modules{CUL_HM}{helper}{updateNbr} = $mNo;
  6854. #InternalTimer(gettimeofday()+0.3,"CUL_HM_FWupdateSim","${dst}${id}00",0);
  6855. InternalTimer(gettimeofday()+5,"CUL_HM_FWupdateBTo","fail:Block$step",0);
  6856. return "";
  6857. }
  6858. }
  6859. sub CUL_HM_FWupdateBTo($){# FW update block timeout
  6860. my $in = shift;
  6861. $modules{CUL_HM}{helper}{updateRetry}++;
  6862. if ($modules{CUL_HM}{helper}{updateRetry} > 5){#retry exceeded
  6863. CUL_HM_FWupdateEnd($in);
  6864. }
  6865. else{# have a retry
  6866. $modules{CUL_HM}{helper}{updateStep}--;
  6867. $modules{CUL_HM}{helper}{updateNbr} = $modules{CUL_HM}{helper}{updateNbrPassed};
  6868. CUL_HM_FWupdateSteps("0010".$modules{CUL_HM}{helper}{updateDst}."00000000");
  6869. }
  6870. }
  6871. sub CUL_HM_FWupdateEnd($){#end FW update
  6872. my $in = shift;
  6873. my $hash = $defs{$modules{CUL_HM}{helper}{updatingName}};
  6874. CUL_HM_UpdtReadSingle($hash,"fwUpdate",$in,1);
  6875. CUL_HM_FWupdateSpeed($modules{CUL_HM}{helper}{updatingName},10);
  6876. $hash->{helper}{io}{newChn} = $hash->{helper}{io}{newChnFwUpdate}
  6877. if(defined $hash->{helper}{io}{newChnFwUpdate});#restore initMsg
  6878. delete $hash->{helper}{io}{newChnFwUpdate};
  6879. delete $defs{$modules{CUL_HM}{helper}{updatingName}}->{cmdStack};
  6880. delete $modules{CUL_HM}{helper}{updating};
  6881. delete $modules{CUL_HM}{helper}{updatingName};
  6882. delete $modules{CUL_HM}{helper}{updateData};
  6883. delete $modules{CUL_HM}{helper}{updateStep};
  6884. delete $modules{CUL_HM}{helper}{updateDst};
  6885. delete $modules{CUL_HM}{helper}{updateId};
  6886. delete $modules{CUL_HM}{helper}{updateNbr};
  6887. CUL_HM_respPendRm($hash);
  6888. CUL_HM_protState($hash,"CMDs_done_FWupdate");
  6889. Log3 $hash->{NAME},2,"CUL_HM fwUpdate $hash->{NAME} end. IO-speed: normal";
  6890. }
  6891. sub CUL_HM_FWupdateSpeed($$){#set IO speed
  6892. my ($name,$speed) = @_;
  6893. my $hash = $defs{$name};
  6894. if (AttrVal($hash->{IODev}{NAME},"rfmode","") ne "HomeMatic"){
  6895. my $msg = sprintf("G%02X",$speed);
  6896. IOWrite($hash, "cmd",$msg);
  6897. }
  6898. else{
  6899. IOWrite($hash, "cmd","speed".$speed);
  6900. }
  6901. }
  6902. sub CUL_HM_FWupdateSim($){#end FW Simulation
  6903. my $msg = shift;
  6904. my $ioName = $defs{$modules{CUL_HM}{helper}{updatingName}}->{IODev}->{NAME};
  6905. my $mNo = sprintf("%02X",$modules{CUL_HM}{helper}{updateNbr});
  6906. if (0 == $modules{CUL_HM}{helper}{updateStep}){
  6907. CUL_HM_Parse($defs{$ioName},"A00${mNo}0010$msg");
  6908. }
  6909. else{
  6910. Log3 "",5,"FWupdate simulate No:$mNo";
  6911. CUL_HM_Parse($defs{$ioName},"A00${mNo}8002$msg");
  6912. }
  6913. }
  6914. sub CUL_HM_eventP($$) {#handle protocol events
  6915. # Current Events are Rcv,NACK,IOerr,Resend,ResendFail,Snd
  6916. # additional variables are protCmdDel,protCmdPend,protState,protLastRcv
  6917. my ($hash, $evntType) = @_;
  6918. return if (!defined $hash);
  6919. if ($evntType eq "Rcv"){
  6920. $hash->{"protLastRcv"} = TimeNow();
  6921. CUL_HM_UpdtReadSingle($hash,".protLastRcv",$hash->{"protLastRcv"},0);
  6922. # return;
  6923. }
  6924. my $evnt = $hash->{"prot".$evntType} ? $hash->{"prot".$evntType} : "0";
  6925. my ($evntCnt,undef) = split(' last_at:',$evnt);
  6926. $hash->{"prot".$evntType} = ++$evntCnt." last_at:".TimeNow();
  6927. if ($evntType =~ m/^(Nack|ResndFail|IOerr|dummy)/){# unrecoverable Error
  6928. CUL_HM_UpdtReadSingle($hash,"state",$evntType,1);
  6929. $hash->{helper}{prt}{bErr}++;
  6930. $hash->{protCmdDel} = 0 if(!$hash->{protCmdDel});
  6931. $hash->{protCmdDel} += scalar @{$hash->{cmdStack}} + 1
  6932. if ($hash->{cmdStack});
  6933. CUL_HM_protState($hash,"CMDs_done");
  6934. CUL_HM_respPendRm($hash);
  6935. }
  6936. elsif($evntType eq "IOdly"){ # IO problem - will see whether it recovers
  6937. my $pHash = $hash->{helper}{prt};
  6938. if ($pHash->{mmcA}){
  6939. unshift @{$hash->{cmdStack}},$_ foreach (reverse@{$pHash->{mmcA}});
  6940. delete $pHash->{mmcA};
  6941. delete $pHash->{mmcS};
  6942. }
  6943. else{
  6944. unshift (@{$hash->{cmdStack}},"++".substr($pHash->{rspWait}{cmd},6));
  6945. # unshift @{$hash->{cmdStack}}, $pHash->{rspWait}{cmd};#pushback
  6946. }
  6947. CUL_HM_respPendRm($hash);
  6948. }
  6949. }
  6950. sub CUL_HM_protState($$){
  6951. my ($hash,$state) = @_;
  6952. if (!$hash || !$hash->{NAME}){#General remove when fixed
  6953. Log 4,"CUL_HM protstate undeviced hash to set $state";
  6954. return;
  6955. }
  6956. my $name = $hash->{NAME};
  6957. my $sProcIn = $hash->{helper}{prt}{sProc};
  6958. if ($sProcIn == 3){#FW update processing
  6959. # do not change state - commandstack is bypassed
  6960. return if ( $state !~ m/(Info_Cleared|_FWupdate)/);
  6961. }
  6962. if ($state =~ m/processing/) {
  6963. $hash->{helper}{prt}{sProc} = 1;
  6964. }
  6965. elsif($state =~ m/^CMDs_done/) {
  6966. $state .= ($hash->{helper}{prt}{bErr}?
  6967. ("_Errors:".$hash->{helper}{prt}{bErr})
  6968. :"");
  6969. delete($hash->{cmdStack});
  6970. delete($hash->{protCmdPend});
  6971. $hash->{helper}{prt}{bErr} = 0;
  6972. $hash->{helper}{prt}{sProc} = 0;
  6973. $hash->{helper}{prt}{awake} = 0 if (defined $hash->{helper}{prt}{awake});
  6974. }
  6975. elsif($state eq "Info_Cleared"){
  6976. $hash->{helper}{prt}{sProc} = 0;
  6977. $hash->{helper}{prt}{awake} = 0 if (defined $hash->{helper}{prt}{awake});
  6978. }
  6979. elsif($state eq "CMDs_pending"){
  6980. $hash->{helper}{prt}{sProc} = 2;
  6981. }
  6982. elsif($state eq "CMDs_FWupdate"){
  6983. $hash->{helper}{prt}{sProc} = 3;
  6984. }
  6985. $hash->{protState} = $state;
  6986. if (!$hash->{helper}{role}{chn}){
  6987. CUL_HM_UpdtReadSingle($hash,"state",$state,
  6988. ($hash->{helper}{prt}{sProc} == 1)?0:1);
  6989. }
  6990. Log3 $name,5,"CUL_HM $name protEvent:$state".
  6991. ($hash->{cmdStack}?" pending:".scalar @{$hash->{cmdStack}}:"");
  6992. CUL_HM_hmInitMsgUpdt($hash) if ( $hash->{helper}{prt}{sProc} != $sProcIn
  6993. && ( $hash->{helper}{prt}{sProc} < 2
  6994. ||($hash->{helper}{prt}{sProc} == 2 && $sProcIn == 0 )));
  6995. }
  6996. ###################-----------helper and shortcuts--------#####################
  6997. ################### Peer Handling ################
  6998. sub CUL_HM_ID2PeerList ($$$) {
  6999. my($name,$peerID,$set) = @_;
  7000. my $peerIDs = AttrVal($name,"peerIDs","");
  7001. return if (!$peerID && !$peerIDs); # nothing to do
  7002. my $hash = $defs{$name};
  7003. $peerIDs =~ s/$peerID//g; #avoid duplicate, support unset
  7004. $peerID =~ s/^000000../00000000/; #correct end detector
  7005. $peerIDs.= $peerID."," if($set);
  7006. my %tmpHash = map { $_ => 1 } split(",",$peerIDs);#remove duplicates
  7007. $peerIDs = ""; #clear list
  7008. my $peerNames = ""; #prepare names
  7009. my $dId = substr(CUL_HM_name2Id($name),0,6); #get own device ID
  7010. foreach my $pId (sort(keys %tmpHash)){
  7011. next if ($pId !~ m/^[0-9A-Fx]{8}$/); #ignore non-channel IDs
  7012. $peerIDs .= $pId.","; #append ID
  7013. next if ($pId eq "00000000"); # and end detection
  7014. $peerNames .= CUL_HM_peerChName($pId,$dId).",";
  7015. }
  7016. $attr{$name}{peerIDs} = $peerIDs; # make it public
  7017. my $dHash = CUL_HM_getDeviceHash($hash);
  7018. my $st = AttrVal($dHash->{NAME},"subType","");
  7019. my $md = AttrVal($dHash->{NAME},"model","");
  7020. my $chn = InternalVal($name,"chanNo","");
  7021. if ($peerNames){
  7022. $peerNames =~ s/_chn-01//g; # channel 01 is part of device
  7023. $peerNames =~ s/_chn-01//g; # channel 01 is part of device
  7024. CUL_HM_UpdtReadSingle($hash,"peerList",$peerNames,0);
  7025. $hash->{peerList} = $peerNames;
  7026. if ($st eq "virtual"){
  7027. #if any of the peers is an SD we are team master
  7028. my ($tMstr,$tcSim,$thSim) = (0,0,0);
  7029. foreach (split(",",$peerNames)){
  7030. if(AttrVal($_,"subType","") eq "smokeDetector"){#have smoke detector
  7031. $tMstr = AttrVal($_,"model","") eq "HM-SEC-SD-2"? 2:1;#differentiate SD and SD2
  7032. }
  7033. $tcSim = 1 if(AttrVal($_,"model","") =~ m/^(HM-CC-VD|ROTO_ZEL-STG-RM-FSA)/);
  7034. my $pch = (substr(CUL_HM_name2Id($_),6,2));
  7035. $thSim = 1 if(AttrVal($_,"model","") =~ m/^HM-CC-RT-DN/ && $pch eq "01");
  7036. }
  7037. if ($tMstr){
  7038. $hash->{helper}{fkt} = "sdLead".$tMstr;
  7039. $hash->{sdTeam} = "sdLead";
  7040. $hash->{TESTNR} = 1 if(!exists $hash->{TESTNR});#must be defined for all sdLead
  7041. CUL_HM_updtSDTeam($name);
  7042. }
  7043. elsif($tcSim){
  7044. $hash->{helper}{fkt}="vdCtrl";}
  7045. elsif($thSim){
  7046. $hash->{helper}{fkt}="virtThSens";}
  7047. else {
  7048. delete $hash->{helper}{fkt};}
  7049. if(!$tMstr) {delete $hash->{sdTeam};}
  7050. }
  7051. elsif ($st eq "smokeDetector"){
  7052. foreach (split(",",$peerNames)){
  7053. my $tn = ($_ =~ m/self/)?$name:$_;
  7054. next if (!$defs{$tn});
  7055. $defs{$tn}{helper}{fkt} = "sdLead".(AttrVal($name,"model","") eq "HM-SEC-SD-2"? 2:1);
  7056. $defs{$tn}{sdTeam} = "sdLead" ;
  7057. $defs{$tn}{TESTNR} = 1 if(!exists $defs{$tn}{TESTNR});#must be defined for all sdLead
  7058. }
  7059. if($peerNames !~ m/self/){
  7060. delete $hash->{sdTeam};
  7061. delete $hash->{helper}{fkt};
  7062. }
  7063. }
  7064. elsif( ($md =~ m/^HM-CC-RT-DN/ && $chn =~ m/^(02|05|04)$/)
  7065. ||($md eq "HM-TC-IT-WM-W-EU" && $chn eq "07")){
  7066. if ($chn eq "04"){
  7067. #if 04 is peered we are "teamed" -> set channel 05
  7068. my $ch05H = $modules{CUL_HM}{defptr}{$dHash->{DEF}."05"};
  7069. CUL_HM_UpdtReadSingle($ch05H,"state","peered",0) if($ch05H);
  7070. }
  7071. else{
  7072. CUL_HM_UpdtReadSingle($hash,"state","peered",0);
  7073. }
  7074. }
  7075. elsif( ($md =~ m/^HM-CC-RT-DN/ && $chn =~ m/^(03|06)$/)
  7076. ||($md eq "HM-TC-IT-WM-W-EU" && $chn =~ m/^(03|06)$/)){
  7077. if (AttrVal($hash,"state","unpeered") eq "unpeered"){
  7078. CUL_HM_UpdtReadSingle($hash,"state","unknown",0);
  7079. }
  7080. }
  7081. }
  7082. else{# no peer set - clean up: delete entries
  7083. delete $hash->{READINGS}{peerList};
  7084. delete $hash->{peerList};
  7085. if (($md =~ m/^HM-CC-RT-DN/ && $chn=~ m/^(02|03|04|05|06)$/)
  7086. ||($md eq "HM-TC-IT-WM-W-EU" && $chn=~ m/^(03|06|07)$/)){
  7087. if ($chn eq "04"){
  7088. my $ch05H = $modules{CUL_HM}{defptr}{$dHash->{DEF}."05"};
  7089. CUL_HM_UpdtReadSingle($ch05H,"state","unpeered") if($ch05H);
  7090. }
  7091. else{
  7092. CUL_HM_UpdtReadSingle($hash,"state","unpeered");
  7093. }
  7094. }
  7095. }
  7096. }
  7097. sub CUL_HM_peerChId($$) {# in:<IDorName> <deviceID>, out:channelID
  7098. my($pId,$dId)=@_;
  7099. return "" if (!$pId);
  7100. my $iId = CUL_HM_id2IoId($dId);
  7101. my ($pSc,$pScNo) = unpack 'A4A*',$pId; #helper for shortcut spread
  7102. return $dId.sprintf("%02X",'0'.$pScNo) if ($pSc eq 'self');
  7103. return $iId.sprintf("%02X",'0'.$pScNo) if ($pSc eq 'fhem');
  7104. return "all" if ($pId eq 'all');#used by getRegList
  7105. my $p = CUL_HM_name2Id($pId).'01';
  7106. return "" if (length($p)<8);
  7107. return substr(CUL_HM_name2Id($pId).'01',0,8);# default chan is 01
  7108. }
  7109. sub CUL_HM_peerChName($$) {#in:<IDorName> <deviceID>, out:name
  7110. my($pId,$dId)=@_;
  7111. my $iId = CUL_HM_id2IoId($dId);
  7112. my($pDev,$pChn) = unpack'A6A2',$pId;
  7113. return 'self'.$pChn if ($pDev eq $dId);
  7114. return 'fhem'.$pChn if ($pDev eq $iId && !defined $modules{CUL_HM}{defptr}{$pDev});
  7115. $pId = $pDev if($pChn =~ m/0[0x]/); # both means device directly. This may be used by remotes and pusdbuttons
  7116. return CUL_HM_id2Name($pId);
  7117. }
  7118. sub CUL_HM_getMId($) {#in: hash(chn or dev) out:model key (key for %culHmModel)
  7119. # Will store result in device helper
  7120. my $hash = shift;
  7121. $hash = CUL_HM_getDeviceHash($hash);
  7122. return "" if (!$hash->{NAME});
  7123. if (!defined $hash->{helper}{mId} || !$hash->{helper}{mId}){
  7124. my $model = AttrVal($hash->{NAME}, "model", "");
  7125. $hash->{helper}{mId} = "";
  7126. foreach my $mIdKey(keys%{$culHmModel}){
  7127. next if (!$culHmModel->{$mIdKey}{name} ||
  7128. $culHmModel->{$mIdKey}{name} ne $model);
  7129. $hash->{helper}{mId} = $mIdKey;
  7130. #--- mId is updated - now update the reglist
  7131. foreach(CUL_HM_getAssChnNames($hash->{NAME})){
  7132. $defs{$_}{helper}{regLst} = CUL_HM_getChnList($defs{$_});
  7133. }
  7134. last;
  7135. }
  7136. }
  7137. return $hash->{helper}{mId};
  7138. }
  7139. sub CUL_HM_getRxType($) { #in:hash(chn or dev) out:binary coded Rx type
  7140. # Will store result in device helper
  7141. my ($hash) = @_;
  7142. $hash = CUL_HM_getDeviceHash($hash);
  7143. no warnings; #convert regardless of content
  7144. my $rxtEntity = int($hash->{helper}{rxType});
  7145. use warnings;
  7146. if (!$rxtEntity){ #at least one bit must be set
  7147. my $MId = CUL_HM_getMId($hash);
  7148. my $rxtOfModel = ($MId && $culHmModel->{$MId}{rxt} ? $culHmModel->{$MId}{rxt} : "");
  7149. if ($rxtOfModel){
  7150. $rxtEntity |= ($rxtOfModel =~ m/b/)?0x02:0;#burst
  7151. $rxtEntity |= ($rxtOfModel =~ m/3/)?0x02:0;#tripple-burst todo currently unknown how it works
  7152. $rxtEntity |= ($rxtOfModel =~ m/c/)?0x04:0;#config
  7153. $rxtEntity |= ($rxtOfModel =~ m/w/)?0x08:0;#wakeup
  7154. $rxtEntity |= ($rxtOfModel =~ m/l/)?0x10:0;#lazyConfig
  7155. $rxtEntity |= ($rxtOfModel =~ m/f/)?0x80:0;#burstConditional
  7156. }
  7157. $rxtEntity = 1 if (!$rxtEntity);#always
  7158. $hash->{helper}{rxType} = $rxtEntity if ($MId);#store if ID is prooven
  7159. }
  7160. return $rxtEntity;
  7161. }
  7162. sub CUL_HM_getAssChnIds($) { #in: name out:ID list of assotiated channels
  7163. # if it is a channel only return itself
  7164. # if device and no channel
  7165. my ($name) = @_;
  7166. my @chnIdList;
  7167. if ($defs{$name}){
  7168. my $hash = $defs{$name};
  7169. foreach my $channel (grep /^channel_/, keys %{$hash}){
  7170. my $chnHash = $defs{$hash->{$channel}};
  7171. push @chnIdList,$chnHash->{DEF} if ($chnHash);
  7172. }
  7173. my $dId = CUL_HM_name2Id($name);
  7174. push @chnIdList,$dId."01" if (length($dId) == 6 && !$hash->{channel_01});
  7175. push @chnIdList,$dId if (length($dId) == 8);
  7176. }
  7177. return sort(@chnIdList);
  7178. }
  7179. sub CUL_HM_getAssChnNames($) { #in: name out:list of assotiated chan and device
  7180. my ($name) = @_;
  7181. my @chnN = ($name);
  7182. if ($defs{$name}){
  7183. my $hash = $defs{$name};
  7184. push @chnN,$defs{$name}{$_} foreach (grep /^channel_/, keys %{$defs{$name}});
  7185. }
  7186. return sort(@chnN);
  7187. }
  7188. sub CUL_HM_getKeys($) { #in: device-hash out:highest index, hash with keys
  7189. my ($hash) = @_;
  7190. my $highestIdx = 0;
  7191. my %keys = ();
  7192. $keys{0} = pack("H*", "A4E375C6B09FD185F27C4E96FC273AE4"); #index 0: eQ-3 default
  7193. my $vccu = $hash->{IODev}->{owner_CCU};
  7194. $vccu = $hash->{IODev}->{NAME} if(!defined($vccu) || !AttrVal($vccu,"hmKey",""));# if keys are not in vccu
  7195. if (defined($vccu)) {
  7196. foreach my $i (1..3){
  7197. my ($kNo,$k) = split(":",AttrVal($vccu,"hmKey".($i== 1?"":$i),""));
  7198. if (defined($kNo) && defined($k)) {
  7199. $kNo = hex($kNo);
  7200. $keys{$kNo} = pack("H*", $k);
  7201. $highestIdx = $kNo if ($kNo > $highestIdx);
  7202. }
  7203. }
  7204. }
  7205. return ($highestIdx, %keys);
  7206. }
  7207. sub CUL_HM_generateCBCsignature($$) { #in: device-hash,msg out: signed message
  7208. my ($hash,$msg) = @_;
  7209. my $oldcounter = ReadingsVal($hash->{NAME},"aesCBCCounter","000000");
  7210. my $counter = substr($oldcounter, 0, 4);
  7211. my ($mNo,$mFlg,$mTg,$mSnd,$mPay) = unpack 'A2A2A2A12A*',$msg;
  7212. my $devH = CUL_HM_getDeviceHash($hash);
  7213. if ($cryptFunc != 1) {
  7214. Log3 $hash,1,"CUL_HM $hash->{NAME} need Crypt::Rijndael to generate AES-CBC signature";
  7215. return $msg;
  7216. }
  7217. my ($kNo, %keys) = CUL_HM_getKeys($devH);
  7218. $kNo = AttrVal($devH->{NAME},"aesKey",$kNo);
  7219. if (!defined($keys{$kNo})) {
  7220. Log3 $hash,1,"CUL_HM $devH->{NAME} AES key ${kNo} not defined!";
  7221. return $msg;
  7222. }
  7223. #generate message number
  7224. if($mNo eq "++") {
  7225. $mNo = ($devH->{helper}{HM_CMDNR} + 1) & 0xff;
  7226. my $oldNo = hex(substr($oldcounter, 4, 2));
  7227. if ($mNo <= $oldNo && (($oldNo + 1) & 0xff) > $oldNo) {
  7228. $mNo = ($oldNo + 1) & 0xff;
  7229. }
  7230. $devH->{helper}{HM_CMDNR} = $mNo;
  7231. $mNo = sprintf("%02X", $mNo);
  7232. }
  7233. if (hex($counter.$mNo) <= hex($oldcounter)) {
  7234. $counter = sprintf("%04X", (hex($counter) + 1) & 0xffff);
  7235. }
  7236. push @evtEt,[$hash,1,"aesCBCCounter:".$counter.$mNo];
  7237. CUL_HM_pushEvnts();
  7238. my $cipher = Crypt::Rijndael->new($keys{$kNo}, Crypt::Rijndael::MODE_ECB());
  7239. my $iv = "49"
  7240. .$mSnd #sender receiver
  7241. .$counter #generation counter
  7242. .$mNo
  7243. ."000000000005";
  7244. my $ivC = $cipher->encrypt(pack("H32", $iv));
  7245. my $d = $mNo
  7246. .$mFlg #Flags
  7247. .$mPay; #payload
  7248. $d .= "00" x ((32 - length($d)) / 2) if (length($d) < 32);
  7249. my $cbc = $cipher->encrypt(pack("H32", $d) ^ $ivC);
  7250. #2016.09.04 06:52:54.227 3: CUL_HM
  7251. Log3 $hash,5, "CUL_HM $hash->{NAME} CBC IV: " . $iv
  7252. ."\n".(" "x30)."$hash->{NAME} CBC D: " . $d
  7253. ."\n".(" "x30)."$hash->{NAME} CBC E: " . unpack("H*", $cbc);
  7254. return uc( $mNo
  7255. . $mFlg.$mTg.$mSnd.$mPay
  7256. . $counter
  7257. . unpack("H8", substr($cbc, 12, 4)));
  7258. }
  7259. #+++++++++++++++++ Conversions names, hashes, ids++++++++++++++++++++++++++++++
  7260. #Performance opti: subroutines may consume up to 5 times the performance
  7261. #
  7262. #get Attr: $val = $attr{$hash->{NAME}}{$attrName}?$attr{$hash->{NAME}}{$attrName} :"";
  7263. # $val = $attr{$name}{$attrName} ?$attr{$name}{$attrName} :"";
  7264. #getRead: $val = $hash->{READINGS}{$rlName} ?$hash->{READINGS}{$rlName}{VAL} :"";
  7265. # $val = $defs{$name}{READINGS}{$rlName}?$defs{$name}{READINGS}{$rlName}{VAL} :"";
  7266. # $time = $hash->{READINGS}{$rlName} ?$hash->{READINGS}{$rlName}{time} :"";
  7267. sub CUL_HM_h2IoId($) { #in: ioHash out: ioHMid
  7268. my ($io) = @_;
  7269. return "000000" if (ref($io) ne 'HASH');
  7270. my $fhtid = defined($io->{FHTID}) ? $io->{FHTID} : "0000";
  7271. return AttrVal($io->{NAME},"hmId","F1$fhtid");
  7272. }
  7273. sub CUL_HM_IoId($) { #in: hash out: IO_id
  7274. my ($hash) = @_;
  7275. my $dHash = CUL_HM_getDeviceHash($hash);
  7276. my $ioHash = $dHash->{IODev};
  7277. return "" if (!$ioHash->{NAME});
  7278. my $fhtid = defined($ioHash->{FHTID}) ? $ioHash->{FHTID} : "0000";
  7279. return AttrVal($ioHash->{NAME},"hmId","F1$fhtid");
  7280. }
  7281. sub CUL_HM_id2IoId($) { #in: id, out:Id of assigned IO
  7282. my ($id) = @_;
  7283. ($id) = unpack 'A6',$id;#get device ID
  7284. return "" if (!$modules{CUL_HM}{defptr}{$id} ||
  7285. !$modules{CUL_HM}{defptr}{$id}->{IODev} ||
  7286. !$modules{CUL_HM}{defptr}{$id}->{IODev}->{NAME});
  7287. my $ioHash = $modules{CUL_HM}{defptr}{$id}->{IODev};
  7288. my $fhtid = defined($ioHash->{FHTID}) ? $ioHash->{FHTID} : "0000";
  7289. return AttrVal($ioHash->{NAME},"hmId","F1$fhtid");
  7290. }
  7291. sub CUL_HM_name2IoName($) { #in: hash out: IO_id
  7292. my ($name) = @_;
  7293. my $dHash = CUL_HM_getDeviceHash($defs{$name});
  7294. my $ioHash = $dHash->{IODev};
  7295. return $ioHash->{NAME} ? $ioHash->{NAME} : "";
  7296. }
  7297. sub CUL_HM_hash2Id($) { #in: id, out:hash
  7298. my ($hash) = @_;
  7299. return $hash->{DEF};
  7300. }
  7301. sub CUL_HM_hash2Name($) {#in: hash, out:name
  7302. my ($hash) = @_;
  7303. return $hash->{NAME};
  7304. }
  7305. sub CUL_HM_name2Hash($) {#in: name, out:hash
  7306. my ($name) = @_;
  7307. return $defs{$name};
  7308. }
  7309. sub CUL_HM_name2Id(@) { #in: name or HMid ==>out: HMid, "" if no match
  7310. my ($name,$idHash) = @_;
  7311. my $hash = $defs{$name};
  7312. return $hash->{DEF} if($hash && $hash->{TYPE} eq "CUL_HM");#name is entity
  7313. return $name if($name =~ m/^[A-F0-9]{6,8}$/i);#was already HMid
  7314. return $defs{$1}->{DEF}.$2 if($name =~ m/(.*)_chn-(..)$/); #<devname> chn-xx
  7315. return "000000" if($name eq "broadcast"); #broadcast
  7316. return substr($idHash->{DEF},0,6).sprintf("%02X",$1)
  7317. if($idHash && ($name =~ m/self(.*)/));
  7318. return CUL_HM_IoId($idHash).sprintf("%02X",$1)
  7319. if($idHash && ($name =~ m/fhem(.*)/));
  7320. return AttrVal($name,"hmId",""); # could be IO device
  7321. }
  7322. sub CUL_HM_id2Name($) { #in: name or HMid out: name
  7323. my ($p) = @_;
  7324. $p = "" if (!defined $p);
  7325. return $p if($defs{$p}||$p =~ m/_chn-\d\d$/
  7326. || $p !~ m/^[A-F0-9]{6,8}$/i);
  7327. my ($devId,$chn) = unpack 'A6A2',$p;
  7328. return "broadcast" if($devId eq "000000");
  7329. my $defPtr = $modules{CUL_HM}{defptr};
  7330. if (length($p) == 8 && $chn ne "00"){
  7331. return $defPtr->{$p}{NAME} if(defined $defPtr->{$p});#channel
  7332. return $defPtr->{$devId}{NAME}."_chn-$chn"
  7333. if($defPtr->{$devId});#dev, add chn
  7334. return $p; #not defined, return ID only
  7335. }
  7336. else{
  7337. return $defPtr->{$devId}{NAME} if($defPtr->{$devId});#device only
  7338. return $devId; #not defined, return ID only
  7339. }
  7340. }
  7341. sub CUL_HM_id2Hash($) { #in: id, out:hash
  7342. my ($id) = @_;
  7343. return $modules{CUL_HM}{defptr}{$id} if (defined $modules{CUL_HM}{defptr}{$id});
  7344. $id = substr($id,0,6);
  7345. return defined $modules{CUL_HM}{defptr}{$id}?($modules{CUL_HM}{defptr}{$id}):undef;
  7346. }
  7347. sub CUL_HM_getDeviceHash($) {#in: hash out: devicehash
  7348. my ($hash) = @_;
  7349. return $hash if(!$hash->{DEF});
  7350. my $devHash = $modules{CUL_HM}{defptr}{substr($hash->{DEF},0,6)};
  7351. return ($devHash)?$devHash:$hash;
  7352. }
  7353. sub CUL_HM_getDeviceName($) {#in: name out: name of device
  7354. my $name = shift;
  7355. return $name if(!$defs{$name});#unknown, return input
  7356. my $devHash = $modules{CUL_HM}{defptr}{substr($defs{$name}{DEF},0,6)};
  7357. return ($devHash)?$devHash->{NAME}:$name;
  7358. }
  7359. sub CUL_HM_shH($$$){
  7360. my ($h,$l,$d) = @_;
  7361. if ( $h->{helper}{shRegW}
  7362. && $h->{helper}{shRegW}{$l}
  7363. && $modules{CUL_HM}{defptr}{$d.$h->{helper}{shRegW}{$l}}){
  7364. return $modules{CUL_HM}{defptr}{$d.$h->{helper}{shRegW}{$l}};
  7365. }
  7366. return $h;
  7367. }
  7368. sub CUL_HM_shC($$$){
  7369. my ($h,$l,$c) = @_;
  7370. if ( $h->{helper}{shRegW}
  7371. && $h->{helper}{shRegW}{$l}){
  7372. return $h->{helper}{shRegW}{$l};
  7373. }
  7374. return $c;
  7375. }
  7376. sub CUL_HM_lstCh($$$){
  7377. my ($h,$l,$c) = @_;
  7378. if ( $h->{helper}{shRegR}
  7379. && $h->{helper}{shRegR}{$l}){
  7380. return $h->{helper}{shRegR}{$l};
  7381. }
  7382. return $c;
  7383. }
  7384. #+++++++++++++++++ debug ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  7385. sub CUL_HM_DumpProtocol($$@) {
  7386. my ($prefix, $iohash, $len,$cnt,$msgFlags,$mTp,$src,$dst,$p) = @_;
  7387. my $iname = $iohash->{NAME};
  7388. no warnings;# conv 2 number would cause a warning - which is ok
  7389. my $hmProtocolEvents = int (AttrVal(CUL_HM_id2Name($src), "hmProtocolEvents",
  7390. AttrVal(InternalVal($iname,"owner_CCU",$iname), "hmProtocolEvents", 0)));
  7391. use warnings;
  7392. my $p01 = substr($p,0,2);
  7393. my $p02 = substr($p,0,4);
  7394. my $p11 = (length($p) > 2 ? substr($p,2,2) : "");
  7395. CUL_HM_trigLastEvent($dst,$mTp,$p01,$p02,$p11) if ($mTp eq "11" && $p01 =~ m/(02|03|80|81)/ && $p11);
  7396. return if(!$hmProtocolEvents);
  7397. # decode message flags for printing
  7398. my $msgFlLong = "";
  7399. my $msgFlagsHex = hex($msgFlags);
  7400. for(my $i = 0; $i < @{$culHmCmdFlags}; $i++) {
  7401. $msgFlLong .= ",".${$culHmCmdFlags}[$i] if($msgFlagsHex & (1<<$i));
  7402. }
  7403. my $ps;
  7404. $ps = $culHmBits->{"$mTp;p11=$p11"} if(!$ps);
  7405. $ps = $culHmBits->{"$mTp;p01=$p01"} if(!$ps);
  7406. $ps = $culHmBits->{"$mTp;p02=$p02"} if(!$ps);
  7407. $ps = $culHmBits->{"$mTp"} if(!$ps);
  7408. my $txt = "";
  7409. if($ps) {
  7410. $txt = $ps->{txt};
  7411. if($ps->{params}) {
  7412. $ps = $ps->{params};
  7413. foreach my $k (sort {$ps->{$a} cmp $ps->{$b} } keys %{$ps}) {
  7414. my ($o,$l,$expr) = split(",", $ps->{$k}, 3);
  7415. last if(length($p) <= $o);
  7416. my $val = $l ? substr($p,$o,$l) : substr($p,$o);
  7417. eval $expr if($hmProtocolEvents > 1 && $expr);
  7418. $txt .= " $k:".(($hmProtocolEvents > 1 && $expr)?"":"0x")."$val";
  7419. }
  7420. }
  7421. $txt = " ($txt)" if($txt);
  7422. }
  7423. $src=CUL_HM_id2Name($src);
  7424. $dst=CUL_HM_id2Name($dst);
  7425. my $msg ="$prefix L:$len N:$cnt F:$msgFlags CMD:$mTp SRC:$src DST:$dst $p$txt ($msgFlLong)";
  7426. Log3 $iname,1,$msg;
  7427. DoTrigger($iname, $msg) if($hmProtocolEvents > 2);
  7428. }
  7429. #+++++++++++++++++ handling register updates ++++++++++++++++++++++++++++++++++
  7430. sub CUL_HM_getRegFromStore($$$$@) {#read a register from backup data
  7431. my($name,$regName,$list,$peerId,$regLN)=@_;
  7432. my $hash = $defs{$name};
  7433. my ($size,$pos,$conv,$factor,$unit) = (8,0,"",1,""); # default
  7434. my $addr = $regName;
  7435. my $reg = $culHmRegDefine->{$regName};
  7436. if ($reg) { # get the register's information
  7437. $addr = $reg->{a};
  7438. $pos = ($addr*10)%10;
  7439. $addr = int($addr);
  7440. $list = $reg->{l};
  7441. $size = $reg->{s};
  7442. $size = int($size)*8 + ($size*10)%10;
  7443. $conv = $reg->{c}; #unconvert formula
  7444. $factor = $reg->{f};
  7445. $unit = ($reg->{u}?" ".$reg->{u}:"");
  7446. }
  7447. else{
  7448. return "invalid:regname or address"
  7449. if($addr<1 ||$addr>255);
  7450. }
  7451. return "invalid:no peer for this register" if(($reg->{p} eq "n" && hex($peerId) != 0)
  7452. ||($reg->{p} eq "y" && hex($peerId) == 0));
  7453. my $dst = substr(CUL_HM_name2Id($name),0,6);
  7454. if(!$regLN){
  7455. $regLN = ($hash->{helper}{expert}{raw}?"":".")
  7456. .sprintf("RegL_%02X.",$list)
  7457. .($peerId?CUL_HM_peerChName($peerId,
  7458. $dst)
  7459. :"");
  7460. }
  7461. $regLN =~ s/broadcast//;
  7462. my $regLNp = $regLN;
  7463. $regLNp =~ s/^\.//; #remove leading '.' in case ..
  7464. my $sdH = CUL_HM_shH($hash,sprintf("%02X",$list),$dst);
  7465. my $sRL = ( $sdH->{helper}{shadowReg} # shadowregList
  7466. && $sdH->{helper}{shadowReg}{$regLNp})
  7467. ?$sdH->{helper}{shadowReg}{$regLNp}
  7468. :"";
  7469. my $rRL = ($hash->{READINGS}{$regLN}) #realRegList
  7470. ?$hash->{READINGS}{$regLN}{VAL}
  7471. :"";
  7472. my $data=0;
  7473. my $convFlg = "";# confirmation flag - indicates data not confirmed by device
  7474. for (my $size2go = $size;$size2go>0;$size2go -=8){
  7475. my $addrS = sprintf("%02X",$addr);
  7476. my ($dReadS,$dReadR) = (undef,"");
  7477. $dReadS = $1 if( $sRL =~ m/$addrS:(..)/);#shadowReg
  7478. $dReadR = $1 if( $rRL =~ m/$addrS:(..)/);#realReg
  7479. my $dRead = $dReadR;
  7480. if (defined $dReadS){
  7481. $convFlg = "set_" if ($dReadR ne $dReadS);
  7482. $dRead = $dReadS;
  7483. }
  7484. else{
  7485. return "invalid:peer missing" if (grep /$regLN../,keys %{$hash->{READINGS}} &&
  7486. !$peerId);
  7487. if (!defined($dRead) || $dRead eq ""){
  7488. return "invalid: not supported by FW version" if ($rRL =~ m/00:00/);#reglist is complete but still address cannot be found
  7489. return "invalid";
  7490. }
  7491. }
  7492. $data = ($data<< 8)+hex($dRead);
  7493. $addr++;
  7494. }
  7495. $data = ($data>>$pos) & (0xffffffff>>(32-$size));
  7496. if (!$conv){ ;# do nothing
  7497. } elsif($conv eq "lit" ){$data = defined $reg->{litInv}{$data}?$reg->{litInv}{$data}:"undef lit:$data";
  7498. } elsif($conv eq "fltCvT" ){$data = CUL_HM_CvTflt($data);
  7499. } elsif($conv eq "fltCvT60"){$data = CUL_HM_CvTflt60($data);
  7500. } elsif($conv eq "min2time"){$data = CUL_HM_min2time($data);
  7501. } elsif($conv eq "m10s3" ){$data = ($data+3)/10;
  7502. } elsif($conv eq "hex" ){$data = sprintf("0x%06X",$data);#06 only for paired to. Currently not used by others
  7503. } else { return " conv undefined - please contact admin";
  7504. }
  7505. $data /= $factor if ($factor);# obey factor after possible conversion
  7506. if ($conv ne "lit" && $reg->{litInv} && $reg->{litInv}{$data} ){
  7507. $data = $reg->{litInv}{$data};#conv special value past to calculation
  7508. $unit = "";
  7509. }
  7510. return $convFlg.$data.$unit;
  7511. }
  7512. sub CUL_HM_chgExpLvl($){# update visibility and set internal values for expert
  7513. my $tHash = shift;
  7514. my $exLvl = CUL_HM_getAttrInt($tHash->{NAME},"expert");
  7515. $tHash->{helper}{expert}{def} = (!($exLvl & 0x04))?1:0;#default register on
  7516. $tHash->{helper}{expert}{det} = ( ($exLvl & 0x01))?1:0;#detail register on
  7517. $tHash->{helper}{expert}{raw} = ( ($exLvl & 0x02))?1:0;#raw register on
  7518. $tHash->{helper}{expert}{tpl} = ( ($exLvl & 0x08))?1:0;#template on
  7519. my ($nTag,$grp);
  7520. if ($tHash->{helper}{expert}{def}){($nTag,$grp) = ("",".R-")}
  7521. else{ ($nTag,$grp) = (".","R-")}
  7522. foreach my $rdEntry (grep /^$grp/ ,keys %{$tHash->{READINGS}}){
  7523. my $reg = $rdEntry;
  7524. my $p = "";
  7525. $p = "-".$1 if($rdEntry =~ m/R-(.*)-(lg|sh)/);
  7526. $reg =~ s/^\.?R-(.*?-)?//;
  7527. next if(!$culHmRegDefine->{$reg} || $culHmRegDefine->{$reg}{d} eq '0');
  7528. $tHash->{READINGS}{$nTag."R$p-".$reg} = $tHash->{READINGS}{$rdEntry};
  7529. delete $tHash->{READINGS}{$rdEntry};
  7530. }
  7531. if ($tHash->{helper}{expert}{det}){($nTag,$grp) = ("",".R-")}
  7532. else{ ($nTag,$grp) = (".","R-")}
  7533. foreach my $rdEntry (grep /^$grp/ ,keys %{$tHash->{READINGS}}){
  7534. my $reg = $rdEntry;
  7535. my $p = "";
  7536. $p = "-".$1 if($rdEntry =~ m/R-(.*)-(lg|sh)/);
  7537. $reg =~ s/^\.?R-(.*-)?//;
  7538. next if(!$culHmRegDefine->{$reg} || $culHmRegDefine->{$reg}{d} eq '1');
  7539. $tHash->{READINGS}{$nTag."R$p-".$reg} = $tHash->{READINGS}{$rdEntry};
  7540. delete $tHash->{READINGS}{$rdEntry};
  7541. }
  7542. if ($tHash->{helper}{expert}{raw}){($nTag,$grp) = ("",".RegL_")}
  7543. else{ ($nTag,$grp) = (".","RegL_")}
  7544. foreach my $rdEntry (grep /^$grp/ ,keys %{$tHash->{READINGS}}){
  7545. my $reg = $rdEntry;
  7546. $reg =~ s/^\.//;
  7547. $tHash->{READINGS}{$nTag.$reg} = $tHash->{READINGS}{$rdEntry};
  7548. delete $tHash->{READINGS}{$rdEntry};
  7549. }
  7550. CUL_HM_setTmplDisp($tHash);
  7551. }
  7552. sub CUL_HM_setTmplDisp($){ # remove register if outdated
  7553. my $tHash = shift;
  7554. delete $tHash->{READINGS}{$_} foreach (grep /^tmpl_/ ,keys %{$tHash->{READINGS}});
  7555. if ($tHash->{helper}{expert}{tpl} && (%HMConfig::culHmTpl)){
  7556. foreach (keys %{$tHash->{helper}{tmpl}}){
  7557. my ($p,$t) = split(">",$_);
  7558. my @param;
  7559. if($tHash->{helper}{tmpl}{$_}){
  7560. @param = split(" ",$HMConfig::culHmTpl{$t}{p});
  7561. my @value = split(" ",$tHash->{helper}{tmpl}{$_});
  7562. for (my $i = 0; $i<scalar(@value); $i++){
  7563. $param[$i] .= ":".$value[$i];
  7564. }
  7565. $t .= ":".join(" ",@param);
  7566. }
  7567. $tHash->{READINGS}{"tmpl_".$p}{VAL} .= $t.",";#could be more than one!
  7568. $tHash->{READINGS}{"tmpl_".$p}{TIME} .= "-";# time does not make sense
  7569. }
  7570. }
  7571. }
  7572. sub CUL_HM_updtRegDisp($$$) {
  7573. my($hash,$list,$peerId)=@_;
  7574. my $listNo = $list+0;
  7575. my $name = $hash->{NAME};
  7576. my $devId = substr(CUL_HM_name2Id($name),0,6);
  7577. my $ioId = CUL_HM_IoId(CUL_HM_id2Hash($devId));
  7578. my $pReg = ($peerId && $peerId ne '00000000' )
  7579. ? CUL_HM_peerChName($peerId,$devId)."-"
  7580. : "";
  7581. $pReg =~ s/:/-/;
  7582. $pReg = "R-".$pReg;
  7583. my $devName =CUL_HM_getDeviceHash($hash)->{NAME};# devName as protocol entity
  7584. my $st = $attr{$devName}{subType} ?$attr{$devName}{subType} :"";
  7585. my $md = $attr{$devName}{model} ?$attr{$devName}{model} :"";
  7586. my $chn = $hash->{DEF};
  7587. $chn = (length($chn) == 8)?substr($chn,6,2):"";
  7588. my @regArr = CUL_HM_getRegN($st,$md,$chn);
  7589. my @changedRead;
  7590. my $regLN = ($hash->{helper}{expert}{raw}?"":".")
  7591. .sprintf("RegL_%02X.",$listNo)
  7592. .($peerId ? CUL_HM_peerChName($peerId,$devId) : "");
  7593. if (($md eq "HM-MOD-Re-8") && $listNo == 0){#handle Fw bug
  7594. CUL_HM_ModRe8($hash,$regLN);
  7595. }
  7596. foreach my $rgN (@regArr){
  7597. next if ($culHmRegDefine->{$rgN}->{l} ne $listNo);
  7598. my $rgVal = CUL_HM_getRegFromStore($name,$rgN,$list,$peerId,$regLN);
  7599. next if (!defined $rgVal || $rgVal =~ m/invalid/);
  7600. my $rdN = ($culHmRegDefine->{$rgN}->{d} ? ($hash->{helper}{expert}{def} ?"":".")
  7601. : ($hash->{helper}{expert}{det} ?"":"."))
  7602. .$pReg.$rgN;
  7603. push (@changedRead,$rdN.":".$rgVal)
  7604. if (ReadingsVal($name,$rdN,"") ne $rgVal);
  7605. }
  7606. CUL_HM_UpdtReadBulk($hash,1,@changedRead) if (@changedRead);
  7607. # --- handle specifics - Devices with abnormal or long register
  7608. if ($md =~ m/^(HM-CC-TC|ROTO_ZEL-STG-RM-FWT)/){#handle temperature readings
  7609. CUL_HM_TCtempReadings($hash) if (($list == 5 ||$list == 6) &&
  7610. substr($hash->{DEF},6,2) eq "02");
  7611. }
  7612. elsif ($md =~ m/^HM-CC-RT-DN/){#handle temperature readings
  7613. CUL_HM_TCITRTtempReadings($hash,$md,7) if ($list == 7 && $chn eq "04");
  7614. }
  7615. elsif ($md =~ m/^HM-TC-IT-WM-W-EU/){#handle temperature readings
  7616. CUL_HM_TCITRTtempReadings($hash,$md,$list) if ($list >= 7 && $chn eq "02");
  7617. }
  7618. elsif ($md =~ m/(^HM-PB-4DIS-WM|HM-Dis-WM55|HM-Dis-EP-WM55|HM-RC-Dis-H-x-EU|ROTO_ZEL-STG-RM-DWT-10)/){#add text
  7619. CUL_HM_4DisText($hash) if ($list == 1) ;
  7620. }
  7621. elsif ($st eq "repeater"){
  7622. CUL_HM_repReadings($hash) if ($list == 2);
  7623. }
  7624. elsif ($md eq "HM-SEC-SD-2"){
  7625. CUL_HM_SD_2($hash) if ($list == 0);
  7626. }
  7627. # CUL_HM_dimLog($hash) if(CUL_HM_Get($hash,$name,"param","subType") eq "dimmer");
  7628. }
  7629. sub CUL_HM_rmOldRegs($){ # remove register i outdated
  7630. #will remove register for deleted peers
  7631. my $name = shift;
  7632. my $hash = $defs{$name};
  7633. return if (!$hash->{peerList});# so far only peer-regs are removed
  7634. my @pList = split",",$hash->{peerList};
  7635. my @rpList;
  7636. foreach(grep /^R-(.*)-/,keys %{$hash->{READINGS}}){
  7637. push @rpList,$1 if ($_ =~ m/^R-(.*)-/);
  7638. }
  7639. @rpList = CUL_HM_noDup(@rpList);
  7640. return if (!@rpList);
  7641. foreach my $peer(@rpList){
  7642. next if($hash->{peerList} =~ m/\b$peer\b/);
  7643. delete $hash->{READINGS}{$_} foreach (grep /^R-$peer-/,keys %{$hash->{READINGS}})
  7644. }
  7645. }
  7646. sub CUL_HM_refreshRegs($){ # renew all register readings from Regl_
  7647. my $name = shift;
  7648. foreach(grep /\.?R-/,keys %{$defs{$name}{READINGS}}){
  7649. delete $defs{$name}{READINGS}{$_};
  7650. }
  7651. my $peers = ReadingsVal($name,"peerList","");
  7652. my $dH = CUL_HM_getDeviceHash($defs{$name});
  7653. foreach(grep /\.?RegL_/,keys %{$defs{$name}{READINGS}}){
  7654. my ($l,$p);
  7655. ($l,$p) = ($1,$2) if($_ =~ m/RegL_(..)\.(.*)/);
  7656. my $ps = $p;
  7657. $ps =~ s/_chn-\d\d$//;
  7658. if (!$p || $peers =~ m/$ps/){
  7659. CUL_HM_updtRegDisp($defs{$name},$l,CUL_HM_name2Id($p,$dH));
  7660. }
  7661. else{
  7662. delete $defs{$name}{READINGS}{$_};# peer for This List not found
  7663. }
  7664. }
  7665. }
  7666. #############################
  7667. #+++++++++++++++++ parameter cacculations +++++++++++++++++++++++++++++++++++++
  7668. my @culHmTimes8 = ( 0.1, 1, 5, 10, 60, 300, 600, 3600 );
  7669. sub CUL_HM_encodeTime8($) {#####################
  7670. my $v = shift;
  7671. return "00" if($v < 0.1);
  7672. for(my $i = 0; $i < @culHmTimes8; $i++) {
  7673. if($culHmTimes8[$i] * 32 > $v) {
  7674. for(my $j = 0; $j < 32; $j++) {
  7675. if($j*$culHmTimes8[$i] >= $v) {
  7676. return sprintf("%X", $i*32+$j);
  7677. }
  7678. }
  7679. }
  7680. }
  7681. return "FF";
  7682. }
  7683. sub CUL_HM_decodeTime8($) {#####################
  7684. my $v = hex(shift);
  7685. return "undef" if($v > 255);
  7686. my $v1 = int($v/32);
  7687. my $v2 = $v%32;
  7688. return $v2 * $culHmTimes8[$v1];
  7689. }
  7690. sub CUL_HM_encodeTime16($) {####################
  7691. my $v = shift;
  7692. return "0000" if($v < 0.05 || $v !~ m/^[+-]?\d+(\.\d+)?$/);
  7693. my $ret = "FFFF";
  7694. my $mul = 10;
  7695. for(my $i = 0; $i < 32; $i++) {
  7696. if($v*$mul < 0x7ff) {
  7697. $ret=sprintf("%04X", ((($v*$mul)<<5)+$i));
  7698. last;
  7699. }
  7700. $mul /= 2;
  7701. }
  7702. return ($ret);
  7703. }
  7704. sub CUL_HM_convTemp($) {########################
  7705. my ($val) = @_;
  7706. if(!($val eq "on" || $val eq "off" ||
  7707. ($val =~ m/^\d*\.?\d+$/ && $val >= 6 && $val <= 30))) {
  7708. my @list = map { ($_.".0", $_+0.5) } (6..30);
  7709. pop @list;
  7710. return "Invalid temperature $val, choose one of on off " . join(" ",@list);
  7711. }
  7712. $val = 100 if($val eq "on");
  7713. $val = 0 if($val eq "off");
  7714. return sprintf("%02X", $val*2);
  7715. }
  7716. sub CUL_HM_decodeTime16($) {####################
  7717. my $v = hex(shift);
  7718. my $m = int($v>>5);
  7719. my $e = $v & 0x1f;
  7720. return (2**$e)*$m*0.1;
  7721. }
  7722. sub CUL_HM_secSince2000() {#####################
  7723. # Calculate the local time in seconds from 2000.
  7724. my $t = time();
  7725. my @l = localtime($t);
  7726. my @g = gmtime($t);
  7727. my $t2 = $t + 60*(($l[2]-$g[2] + ((($l[5]<<9)|$l[7]) <=> (($g[5]<<9)|$g[7])) * 24) * 60 + $l[1]-$g[1])
  7728. # timezone and daylight saving...
  7729. - 946684800 # seconds between 01.01.2000, 00:00 and THE EPOCH (1970)
  7730. - 7200; # HM Special
  7731. return $t2;
  7732. }
  7733. sub CUL_HM_getChnLvl($){# in: name out: vit or phys level
  7734. my $name = shift;
  7735. my $curVal = ReadingsVal($name,"level",undef);
  7736. $curVal = ReadingsVal($name,".level",0)if (!defined $curVal);
  7737. $curVal =~ s/set_//;
  7738. $curVal =~ s/ .*//;#strip unit
  7739. return $curVal;
  7740. }
  7741. #--------------- Conversion routines for register settings---------------------
  7742. sub CUL_HM_initRegHash() { #duplicate short and long press register
  7743. my $mp = "$attr{global}{modpath}/FHEM";
  7744. opendir(DH, $mp) || return;
  7745. foreach my $m (grep /^HMConfig_(.*)\.pm$/,readdir(DH)) {
  7746. my $file = "${mp}/$m";
  7747. no strict "refs";
  7748. my $ret = do $file;
  7749. use strict "refs";
  7750. if(!$ret){ Log3 undef, 1, "Error loading file: $file:\n $@";}
  7751. else { Log3 undef, 3, "additional HM config file loaded: $file";}
  7752. }
  7753. closedir(DH);
  7754. }
  7755. my %fltCvT60 = (1=>127,60=>7620);
  7756. sub CUL_HM_fltCvT60($) { # float -> config time
  7757. my ($inValue) = @_;
  7758. my $exp = 0;
  7759. my $div2;
  7760. foreach my $div(sort{$a <=> $b} keys %fltCvT60){
  7761. $div2 = $div;
  7762. last if ($inValue < $fltCvT60{$div});
  7763. $exp++;
  7764. }
  7765. return ($exp << 7)+int($inValue/$div2+.1);
  7766. }
  7767. sub CUL_HM_CvTflt60($) { # config time -> float
  7768. my ($inValue) = @_;
  7769. return ($inValue & 0x7f)*((sort {$a <=> $b} keys(%fltCvT60))[$inValue >> 7]);
  7770. }
  7771. my %fltCvT = (0.1=>3.1,1=>31,5=>155,10=>310,60=>1860,300=>9300,
  7772. 600=>18600,3600=>111601);
  7773. sub CUL_HM_fltCvT($) { # float -> config time
  7774. my ($inValue) = @_;
  7775. my $exp = 0;
  7776. my $div2;
  7777. foreach my $div(sort{$a <=> $b} keys %fltCvT){
  7778. $div2 = $div;
  7779. last if ($inValue < $fltCvT{$div});
  7780. $exp++;
  7781. }
  7782. return ($exp << 5)+int($inValue/$div2+.1);
  7783. }
  7784. sub CUL_HM_CvTflt($) { # config time -> float
  7785. my ($inValue) = @_;
  7786. return ($inValue & 0x1f)*((sort {$a <=> $b} keys(%fltCvT))[$inValue >> 5]);
  7787. }
  7788. sub CUL_HM_flt6CvT($) { # float -> config time
  7789. my ($inValue) = @_;
  7790. my $exp = ($inValue>127)?1:0;
  7791. return ($exp << 7)+int($inValue/($exp?60:1));
  7792. }
  7793. sub CUL_HM_CvTflt6($) { # config time -> float
  7794. my ($inValue) = @_;
  7795. $inValue = 129 if ($inValue == 128);
  7796. return ($inValue & 0x7f)*(($inValue >> 7)?60:1);
  7797. }
  7798. sub CUL_HM_min2time($) { # minutes -> time
  7799. my $min = shift;
  7800. $min = $min * 30;
  7801. return sprintf("%02d:%02d",int($min/60),$min%60);
  7802. }
  7803. sub CUL_HM_time2min($) { # minutes -> time
  7804. my $time = shift;
  7805. my ($h,$m) = split ":",$time;
  7806. $m = ($h*60 + $m)/30;
  7807. $m = 0 if($m < 0);
  7808. $m = 47 if($m > 47);
  7809. return $m;
  7810. }
  7811. sub CUL_HM_getRegInfo($) { #
  7812. my ($name) = @_;
  7813. my $hash = $defs{$name};
  7814. my $devHash = CUL_HM_getDeviceHash($hash);
  7815. my $st = AttrVal ($devHash->{NAME},"subType", "" );
  7816. my $md = AttrVal ($devHash->{NAME},"model" , "" );
  7817. my $chn = InternalVal($hash->{NAME} ,"chanNo" ,"00");
  7818. my @regArr = CUL_HM_getRegN($st,$md,$chn);
  7819. my $roleD = $hash->{helper}{role}{dev};
  7820. my $roleC = $hash->{helper}{role}{chn};
  7821. my @rI;
  7822. foreach my $regName (@regArr){
  7823. my $reg = $culHmRegDefine->{$regName};
  7824. my $help = $reg->{t};
  7825. my ($min,$max) = ($reg->{min},"to ".$reg->{max});
  7826. if ($reg->{c} eq "lit"){
  7827. $help .= " options:".join(",",keys%{$reg->{lit}});
  7828. $min = "";
  7829. $max = "literal";
  7830. }
  7831. elsif (defined($reg->{lit})){
  7832. $help .= " special:".join(",",keys%{$reg->{lit}});
  7833. }
  7834. my $cp = $reg->{l}."p";
  7835. push @rI,sprintf("%4d: %-16s | %3s %-14s | %8s | %s\n",
  7836. $reg->{l},$regName
  7837. ,$min
  7838. ,$max.$reg->{u}
  7839. ,($reg->{p} eq 'y' ? "required" : "")
  7840. ,$help)
  7841. if (($roleD && $reg->{l} == 0)||
  7842. ($roleC && $reg->{l} != 0));
  7843. }
  7844. my $info = sprintf("list: %16s | %-18s | %-8s | %s\n",
  7845. "register","range","peer","description");
  7846. foreach(sort(@rI)){$info .= $_;}
  7847. return $info;
  7848. }
  7849. sub CUL_HM_getRegN($$@){ # get list of register for a model
  7850. my ($st,$md,@chn) = @_;
  7851. my @regArr = keys %{$culHmRegGeneral};
  7852. push @regArr, keys %{$culHmRegType->{$st}} if($culHmRegType->{$st});
  7853. push @regArr, keys %{$culHmRegModel->{$md}} if($culHmRegModel->{$md});
  7854. foreach (@chn){
  7855. push @regArr, keys %{$culHmRegChan->{$md.$_}} if($culHmRegChan->{$md.$_});
  7856. }
  7857. return @regArr;
  7858. }
  7859. sub CUL_HM_getChnList($){ # get reglist assotioted with a channel
  7860. my ($hash) = @_;
  7861. my $devHash = CUL_HM_getDeviceHash($hash);
  7862. my $chnN = hex(InternalVal($hash->{NAME},"chanNo","0"));
  7863. my @mLstA = split(",",$culHmModel->{$devHash->{helper}{mId}}{lst});
  7864. my $chRl = "";
  7865. if ($hash->{helper}{role}{dev}){
  7866. $chRl = ",0";
  7867. $chnN = ($hash->{helper}{role}{chn})? 1 # device is added. if we ar channel add this as well.
  7868. : "-";
  7869. }
  7870. foreach my $mLst(@mLstA){
  7871. my ($Lst,$cLst) = split(":",$mLst.":-");
  7872. $cLst = $chnN if ($cLst eq "-");
  7873. next if ($Lst eq "p" || $cLst eq "-");# no list, just peers
  7874. foreach my $aaa (grep /$chnN/,split('\.',$cLst)){
  7875. $Lst .= "p" if($Lst =~ m/^[34]$/ || $aaa =~ m/p/);
  7876. $Lst =~ s/ //g;
  7877. $chRl .= ",".$Lst;
  7878. }
  7879. }
  7880. return $chRl;
  7881. }
  7882. sub CUL_HM_4DisText($) { # convert text for 4dis
  7883. #text1: start at 54 (0x36) length 12 (0x0c)
  7884. #text2: start at 70 (0x46) length 12 (0x0c)
  7885. my ($hash)=@_;
  7886. my $name = $hash->{NAME};
  7887. my $regPre = ($hash->{helper}{expert}{raw}?"":".");
  7888. my $reg1 = ReadingsVal($name,$regPre."RegL_01." ,"");
  7889. my $pref = "";
  7890. if ($hash->{helper}{shadowReg}{"RegL_01."}){
  7891. $pref = "set_";
  7892. $reg1 = $hash->{helper}{shadowReg}{"RegL_01."};
  7893. }
  7894. my %txt;
  7895. foreach my $sAddr (54,70){
  7896. my $txtHex = $reg1; #one row
  7897. my $sStr = sprintf("%02X:",$sAddr);
  7898. $txtHex =~ s/.* $sStr//; #remove reg prior to string
  7899. $sStr = sprintf("%02X:",$sAddr+11);
  7900. $txtHex =~ s/$sStr(..).*/,$1/; #remove reg after string
  7901. $txtHex =~ s/ ..:/,/g; #remove addr
  7902. $txtHex =~ s/ //g; #remove space
  7903. $txtHex =~ s/,00.*//; #remove trailing string
  7904. $txt{$sAddr} = "";
  7905. my @ch = split(",",$txtHex,12);
  7906. foreach (@ch){$txt{$sAddr}.=chr(hex($_)) if (length($_)==2)};
  7907. }
  7908. CUL_HM_UpdtReadBulk($hash,1,"text1:".$pref.$txt{54},
  7909. "text2:".$pref.$txt{70});
  7910. return "text1:".$txt{54}."\n".
  7911. "text2:".$txt{70}."\n";
  7912. }
  7913. sub CUL_HM_TCtempReadings($) {# parse TC temperature readings
  7914. my ($hash) = @_;
  7915. my $name = $hash->{NAME};
  7916. my $regPre = ($hash->{helper}{expert}{raw}?"":".");
  7917. my $reg5 = ReadingsVal($name,$regPre."RegL_05." ,"");
  7918. my $reg6 = ReadingsVal($name,$regPre."RegL_06." ,"");
  7919. { #update readings in device - oldfashioned style, copy from Readings
  7920. my @histVals;
  7921. foreach my $var ("displayMode","displayTemp","controlMode","decalcDay","displayTempUnit","day-temp","night-temp","party-temp"){
  7922. my $varV = ReadingsVal($name,"R-".$var,"???");
  7923. foreach my $e( grep {${$_}[2] =~ m/$var/}# see if change is pending
  7924. grep {$hash eq ${$_}[0]}
  7925. grep {scalar(@{$_} == 3)}
  7926. @evtEt){
  7927. $varV = ${$e}[2];
  7928. $varV =~ s/^R-$var:// ;
  7929. }
  7930. push @histVals,"$var:$varV";
  7931. }
  7932. if (@histVals){
  7933. CUL_HM_UpdtReadBulk($hash,1,@histVals) ;
  7934. CUL_HM_UpdtReadBulk(CUL_HM_getDeviceHash($hash),1,@histVals);
  7935. }
  7936. }
  7937. if (ReadingsVal($name,"R-controlMode","") =~ m/^party/){
  7938. if ( $reg6 # ugly handling to add vanishing party register
  7939. && $reg6 !~ m/ 61:/
  7940. && $hash->{helper}{partyReg}){
  7941. $hash->{READINGS}{"RegL_06."}{VAL} =~ s/ 00:00/$hash->{helper}{partyReg}/;
  7942. }
  7943. }
  7944. else{
  7945. delete $hash->{helper}{partyReg};
  7946. }
  7947. my @days = ("Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri");
  7948. $reg5 =~ s/.* 0B://; #remove register up to addr 11 from list 5
  7949. my $tempRegs = $reg5.$reg6; #one row
  7950. $tempRegs =~ s/ 00:00/ /g; #remove regline termination
  7951. $tempRegs =~ s/ ..:/,/g; #remove addr Info
  7952. $tempRegs =~ s/ //g; #blank
  7953. my @Tregs = split(",",$tempRegs);
  7954. my @time = @Tregs[grep !($_ % 2), 0..$#Tregs]; # even-index =time
  7955. my @temp = @Tregs[grep $_ % 2, 0..$#Tregs]; # odd-index =data
  7956. my @changedRead;
  7957. my $setting;
  7958. if (scalar( @time )<168){
  7959. push (@changedRead,"R_tempList_State:incomplete");
  7960. $setting = "reglist incomplete\n" ;
  7961. }
  7962. else{
  7963. delete $hash->{READINGS}{$_}
  7964. foreach (grep !/_/,grep /tempList/,keys %{$hash->{READINGS}});
  7965. foreach (@time){$_=hex($_)*10};
  7966. foreach (@temp){$_=hex($_)/2};
  7967. push (@changedRead,"R_tempList_State:".
  7968. (($hash->{helper}{shadowReg}{"RegL_05."} ||
  7969. $hash->{helper}{shadowReg}{"RegL_06."} )?"set":"verified"));
  7970. for (my $day = 0; $day < 7; $day++){
  7971. my $tSpan = 0;
  7972. my $dayRead = "";
  7973. for (my $entry = 0;$entry<24;$entry++){
  7974. my $reg = $day *24 + $entry;
  7975. last if ($tSpan > 1430);
  7976. $tSpan = $time[$reg];
  7977. my $entry = sprintf("%02d:%02d %3.01f",($tSpan/60),($tSpan%60),$temp[$reg]);
  7978. $setting .= "Temp set: ${day}_".$days[$day]." ".$entry." C\n";
  7979. $dayRead .= " ".$entry;
  7980. $tSpan = $time[$reg];
  7981. }
  7982. push (@changedRead,"R_${day}_tempList$days[$day]:$dayRead");
  7983. }
  7984. }
  7985. CUL_HM_UpdtReadBulk($hash,1,@changedRead) if (@changedRead);
  7986. return $setting;
  7987. }
  7988. sub CUL_HM_SD_2($) {# parse SD2
  7989. my ($hash)=@_;
  7990. my $rep = CUL_HM_getRegFromStore($hash->{NAME},"devRepeatCntMax",0,"","");
  7991. if ($rep eq "1"){
  7992. CUL_HM_UpdtReadBulk($hash,1,"sdRepeat:on");
  7993. $hash->{sdRepeat} = "on";
  7994. }
  7995. elsif($rep eq "0"){
  7996. CUL_HM_UpdtReadBulk($hash,1,"sdRepeat:off");
  7997. }
  7998. else{
  7999. CUL_HM_UpdtReadBulk($hash,1,"sdRepeat:$rep");
  8000. }
  8001. }
  8002. sub CUL_HM_TCITRTtempReadings($$@) {# parse RT - TC-IT temperature readings
  8003. my ($hash,$md,@list)=@_;
  8004. my $name = $hash->{NAME};
  8005. my $regPre = ($hash->{helper}{expert}{raw}?"":".");
  8006. my @changedRead;
  8007. my $setting="";
  8008. my %idxN = (7=>"P1_",8=>"P2_",9=>"P3_");
  8009. $idxN{7} = "" if($md =~ m/CC-RT/);# not prefix for RT
  8010. my @days = ("Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri");
  8011. delete $hash->{READINGS}{hyst2pointWrite};
  8012. foreach my $lst (@list){
  8013. my @r1;
  8014. $lst +=0;
  8015. # cleanup old value formats
  8016. my $ln = length($idxN{$lst})?substr($idxN{$lst},0,2):"";
  8017. delete $hash->{READINGS}{$_}
  8018. foreach (grep !/_/,grep /tempList$ln/,keys %{$hash->{READINGS}});
  8019. my $tempRegs = ReadingsVal($name,$regPre."RegL_0$lst.","");
  8020. if ($tempRegs !~ m/00:00/){
  8021. # for (my $day = 0;$day<7;$day++){#leave days allone - state is incomplete should be enough
  8022. # push (@changedRead,"R_$idxN{$lst}${day}_tempList".$days[$day].":incomplete");
  8023. # }
  8024. push (@changedRead,"R_$idxN{$lst}tempList_State:incomplete");
  8025. CUL_HM_UpdtReadBulk($hash,1,@changedRead) if (@changedRead);
  8026. next;
  8027. }
  8028. foreach(split " ",$tempRegs){
  8029. my ($a,$d) = split ":",$_;
  8030. $r1[hex($a)] = $d;
  8031. }
  8032. if ($hash->{helper}{shadowReg}{"RegL_0$lst."}){
  8033. my $ch = 0;
  8034. foreach(split " ",$hash->{helper}{shadowReg}{"RegL_0$lst."}){
  8035. my ($a,$d) = split ":",$_;
  8036. $a = hex($a);
  8037. $ch = 1 if ((!$r1[$a] || $r1[$a] ne $d) && $a >= 20);
  8038. $r1[$a] = $d;
  8039. }
  8040. push (@changedRead,"R_$idxN{$lst}tempList_State:set") if ($ch);
  8041. }
  8042. else{
  8043. push (@changedRead,"R_$idxN{$lst}tempList_State:verified");
  8044. }
  8045. $tempRegs = join("",@r1[20..scalar@r1-1]);
  8046. for (my $day = 0;$day<7;$day++){
  8047. my $dayRead = "";
  8048. my @time;
  8049. my @temp;
  8050. if (length($tempRegs)<($day+1) *13*4) {
  8051. push (@changedRead,"R_$idxN{$lst}${day}_tempList$days[$day]:incomplete");
  8052. $setting .= "Temp set $idxN{$lst}: ${day}_".$days[$day]." incomplete\n";
  8053. }
  8054. else{
  8055. foreach (unpack '(A4)*',substr($tempRegs,$day *13*4,13*4)){
  8056. my $h = hex($_);
  8057. push @temp,($h >> 9)/2;
  8058. $h = ($h & 0x1ff) * 5;
  8059. $h = sprintf("%02d:%02d",int($h / 60),($h%60));
  8060. push @time,$h;
  8061. }
  8062. for (my $idx = 0;$idx<13;$idx++){
  8063. my $entry = sprintf(" %s %04.01f",$time[$idx],$temp[$idx]);
  8064. $setting .= "Temp set $idxN{$lst}: ${day}_".$days[$day].$entry." C\n";
  8065. $dayRead .= $entry;
  8066. last if ($time[$idx] eq "24:00");
  8067. }
  8068. push (@changedRead,"R_$idxN{$lst}${day}_tempList$days[$day]:$dayRead");
  8069. }
  8070. }
  8071. }
  8072. CUL_HM_UpdtReadBulk($hash,1,@changedRead) if (@changedRead);
  8073. return $setting;
  8074. }
  8075. sub CUL_HM_repReadings($) { # parse repeater
  8076. my ($hash)=@_;
  8077. my %pCnt;
  8078. my $cnt = 0;
  8079. return "" if (!$hash->{helper}{peerIDsRaw});
  8080. foreach my$pId(split',',$hash->{helper}{peerIDsRaw}){
  8081. next if (!$pId || $pId eq "00000000");
  8082. $pCnt{$pId.$cnt}{cnt}=$cnt++;
  8083. }
  8084. delete $hash->{repeater} foreach(devspec2array("TYPE=CUL_HM"
  8085. .":FILTER=DEF=......"
  8086. .":FILTER=repeater=$hash->{NAME}"));
  8087. my @pS;
  8088. my @pD;
  8089. my @pB;
  8090. foreach (split",",(AttrVal($hash->{NAME},"repPeers",undef))){
  8091. my ($s,$d,$b) = split":",$_;
  8092. push @pS,$s;
  8093. push @pD,$d;
  8094. push @pB,$b;
  8095. }
  8096. my @readList;
  8097. push @readList,"repPeer_".sprintf("%02d",$_+1).":undefined" for(0..35);#set default empty
  8098. my @retL;
  8099. my @repAttr;
  8100. push @repAttr," " for(0..35);
  8101. foreach my$pId(sort keys %pCnt){
  8102. my ($pdID,$bdcst,$no) = unpack('A6A2A2',$pId);
  8103. my $fNo = $no-1;#shorthand field number, often used
  8104. my $sName = CUL_HM_id2Name($pdID);
  8105. if ($sName eq $pdID && $pD[$fNo] && $defs{$pD[$fNo]}){
  8106. $sName = $defs{$pD[$fNo]}->{IODev}{NAME}
  8107. if($attr{$defs{$pD[$fNo]}->{IODev}{NAME}}{hmId} eq $pdID);
  8108. }
  8109. my $eS = sprintf("%02d:%-15s %-15s %-3s %-4s",
  8110. $no
  8111. ,$sName
  8112. ,((!$pS[$fNo] || $pS[$fNo] ne $sName)?"unknown":" dst>$pD[$fNo]")
  8113. ,($bdcst eq "01"?"yes":"no ")
  8114. ,($pB[$fNo] && ( ($bdcst eq "01" && $pB[$fNo] eq "y")
  8115. ||($bdcst eq "00" && $pB[$fNo] eq "n")) ?"ok":"fail")
  8116. );
  8117. $repAttr[$fNo] = "$sName:"
  8118. .((!$pS[$fNo] || $pS[$fNo] ne $sName)?"-":$pD[$fNo])
  8119. .":".($pB[$fNo]?$pB[$fNo]:"-");
  8120. my $dName = CUL_HM_getDeviceName($sName);
  8121. $defs{$dName}{repeater} = $hash->{NAME} if ($defs{$dName});
  8122. push @retL,$eS;
  8123. $readList[$fNo]="repPeer_".$eS;
  8124. }
  8125. $attr{$hash->{NAME}}->{repPeers} = join",",@repAttr;
  8126. CUL_HM_UpdtReadBulk($hash,0,@readList);
  8127. return "No Source Dest Bcast\n". join"\n", sort @retL;
  8128. }
  8129. sub CUL_HM_ModRe8($$) { # repair FW bug
  8130. #Register 18 may come with a wrong address - we will corrent that
  8131. my ($hash,$regN)=@_;
  8132. my $rl0 = ReadingsVal($hash->{NAME},$regN,"empty");
  8133. return if( $rl0 !~ m/00:00/ # not if List is incomplete
  8134. ||$rl0 =~ m/12:/ ); # reg 18 present, dont touch
  8135. foreach my $ad (split(" ",$rl0)){
  8136. my ($a,$d) = split(":",$ad);
  8137. my $ah = hex($a);
  8138. if ($ah & 0xe0 && (($ah & 0x1F) == 0x12)){
  8139. Log3 $hash,3,"CUL_HM replace address $a to 0x12";
  8140. $hash->{READINGS}{$regN}{VAL} =~ s/ $a:/ 12:/;
  8141. last;
  8142. }
  8143. }
  8144. }
  8145. sub CUL_HM_dimLog($) {# dimmer readings - support virtual chan - unused so far
  8146. my ($hash)=@_;
  8147. my $lComb = CUL_HM_Get($hash,$hash->{NAME},"reg","logicCombination");
  8148. return if (!$lComb);
  8149. my %logicComb=(
  8150. inactive=>{calc=>'$val=$val' ,txt=>'unused'},
  8151. or =>{calc=>'$val=$in>$val?$in:$val' ,txt=>'max(state,chan)'},
  8152. and =>{calc=>'$val=$in<$val?$in:$val' ,txt=>'min(state,chan)'},
  8153. xor =>{calc=>'$val=!($in!=0&&$val!=0)?($in>$val?$in:$val): 0' ,txt=>'0 if both are != 0, else max'},
  8154. nor =>{calc=>'$val=100-($in>$val?$in : $val)' ,txt=>'100-max(state,chan)'},
  8155. nand =>{calc=>'$val=100-($in<$val?$in : $val)' ,txt=>'100-min(state,chan)'},
  8156. orinv =>{calc=>'$val=(100-$in)>$val?(100-$in) : $val' ,txt=>'max((100-chn),state)'},
  8157. andinv =>{calc=>'$val=(100-$in)<$val?(100-$in) : $val' ,txt=>'min((100-chn),state)'},
  8158. plus =>{calc=>'$val=($in + $val)<100?($in + $val) : 100' ,txt=>'state + chan'},
  8159. minus =>{calc=>'$val=($in - $val)>0?($in + $val) : 0' ,txt=>'state - chan'},
  8160. mul =>{calc=>'$val=($in * $val)<100?($in + $val) : 100' ,txt=>'state * chan'},
  8161. plusinv =>{calc=>'$val=($val+100-$in)<100?($val+100-$in) : 100' ,txt=>'state + 100 - chan'},
  8162. minusinv=>{calc=>'$val=($val-100+$in)>0?($val-100+$in) : 0' ,txt=>'state - 100 + chan'},
  8163. mulinv =>{calc=>'$val=((100-$in)*$val)<100?(100-$in)*$val) : 100',txt=>'state * (100 - chan)'},
  8164. invPlus =>{calc=>'$val=(100-$val-$in)>0?(100-$val-$in) : 0' ,txt=>'100 - state - chan'},
  8165. invMinus=>{calc=>'$val=(100-$val+$in)<100?(100-$val-$in) : 100' ,txt=>'100 - state + chan'},
  8166. invMul =>{calc=>'$val=(100-$val*$in)>0?(100-$val*$in) : 0' ,txt=>'100 - state * chan'},
  8167. );
  8168. CUL_HM_UpdtReadBulk($hash,0,"R-logicCombTxt:".$logicComb{$lComb}{txt}
  8169. ,"R-logicCombCalc:".$logicComb{$lComb}{calc});
  8170. return "";
  8171. }
  8172. #+++++++++++++++++ Action Detector ++++++++++++++++++++++++++++++++++++++++++++
  8173. # verify that devices are seen in a certain period of time
  8174. # It will generate events if no message is seen sourced by the device during
  8175. # that period.
  8176. # ActionDetector will use the fixed HMid 000000
  8177. sub CUL_HM_ActGetCreateHash() {# get ActionDetector - create if necessary
  8178. if (!$modules{CUL_HM}{defptr}{"000000"}){
  8179. CommandDefine(undef,"ActionDetector CUL_HM 000000");
  8180. $attr{ActionDetector}{actCycle} = 600;
  8181. $attr{ActionDetector}{"event-on-change-reading"} = ".*";
  8182. }
  8183. my $actHash = $modules{CUL_HM}{defptr}{"000000"};
  8184. my $actName = ($actHash ? $actHash->{NAME} : "");
  8185. my $ac = AttrVal($actName,"actCycle",600);
  8186. if (!$actHash->{helper}{actCycle} ||
  8187. $actHash->{helper}{actCycle} != $ac){
  8188. $actHash->{helper}{actCycle} = $ac;
  8189. RemoveInternalTimer("ActionDetector");
  8190. $actHash->{STATE} = "active";
  8191. InternalTimer(gettimeofday()+$ac,"CUL_HM_ActCheck", "ActionDetector", 0);
  8192. }
  8193. return $actHash;
  8194. }
  8195. sub CUL_HM_time2sec($) {
  8196. my ($timeout) = @_;
  8197. my ($h,$m) = split(":",$timeout);
  8198. no warnings 'numeric';
  8199. $h = int($h);
  8200. $m = int($m);
  8201. use warnings 'numeric';
  8202. return ((sprintf("%03s:%02d",$h,$m)),((int($h)*60+int($m))*60));
  8203. }
  8204. sub CUL_HM_ActAdd($$) {# add an HMid to list for activity supervision
  8205. my ($devId,$timeout) = @_; #timeout format [hh]h:mm
  8206. $timeout = 0 if (!$timeout);
  8207. return $devId." is not an HM device - action detection cannot be added"
  8208. if (length($devId) != 6);
  8209. my ($cycleString,undef)=CUL_HM_time2sec($timeout);
  8210. my $devName = CUL_HM_id2Name($devId);
  8211. my $devHash = $defs{$devName};
  8212. $attr{$devName}{actCycle} = $cycleString;
  8213. $attr{$devName}{actStatus}=""; # force trigger
  8214. my $actHash = CUL_HM_ActGetCreateHash();
  8215. $actHash->{helper}{$devId}{start} = TimeNow();
  8216. $actHash->{helper}{peers} = CUL_HM_noDupInString(
  8217. ($actHash->{helper}{peers}?$actHash->{helper}{peers}:"")
  8218. .",$devId");
  8219. Log3 $actHash, 3,"Device ".$devName." added to ActionDetector with "
  8220. .$cycleString." time";
  8221. #run ActionDetector
  8222. RemoveInternalTimer("ActionDetector");
  8223. CUL_HM_ActCheck("add");
  8224. return;
  8225. }
  8226. sub CUL_HM_ActDel($) {# delete HMid for activity supervision
  8227. my ($devId) = @_;
  8228. my $devName = CUL_HM_id2Name($devId);
  8229. CUL_HM_setAttrIfCh($devName,"actStatus","deleted","Activity");#post trigger
  8230. delete $attr{$devName}{actCycle};
  8231. delete $attr{$devName}{actStatus};
  8232. my $actHash = CUL_HM_ActGetCreateHash();
  8233. delete ($actHash->{helper}{$devId});
  8234. my $peerIDs = $actHash->{helper}{peers};
  8235. $peerIDs =~ s/$devId//g if($peerIDs);
  8236. $actHash->{helper}{peers} = CUL_HM_noDupInString($peerIDs);
  8237. Log3 $actHash,3,"Device ".$devName." removed from ActionDetector";
  8238. RemoveInternalTimer("ActionDetector");
  8239. CUL_HM_ActCheck("del");
  8240. return;
  8241. }
  8242. sub CUL_HM_ActCheck($) {# perform supervision
  8243. my ($call) = @_;
  8244. my $actHash = CUL_HM_ActGetCreateHash();
  8245. my $tod = int(gettimeofday());
  8246. my $actName = $actHash->{NAME};
  8247. my $peerIDs = $actHash->{helper}{peers}?$actHash->{helper}{peers}:"";
  8248. my @event;
  8249. my ($cntUnkn,$cntAliv,$cntDead,$cnt_Off) =(0,0,0,0);
  8250. my $autoTry = CUL_HM_getAttrInt($actName,"actAutoTry",0);
  8251. foreach my $devId (split(",",$peerIDs)){
  8252. next if (!$devId);
  8253. my $devName = CUL_HM_id2Name($devId);
  8254. if(AttrVal($devName,"ignore",0)){
  8255. delete $actHash->{READINGS}{"status_".$devName};
  8256. next;
  8257. }
  8258. if(!$devName || !defined($attr{$devName}{actCycle})){
  8259. CUL_HM_ActDel($devId);
  8260. next;
  8261. }
  8262. my $state;
  8263. my $oldState = AttrVal($devName,"actStatus","unset");
  8264. my (undef,$tSec)=CUL_HM_time2sec($attr{$devName}{actCycle});
  8265. if ($tSec == 0){# detection switched off
  8266. $state = "switchedOff";
  8267. }
  8268. else{
  8269. my $tLast = ReadingsVal($devName,".protLastRcv",0);
  8270. my @t = localtime($tod - $tSec); #time since when a trigger is expected
  8271. my $tSince = sprintf("%04d-%02d-%02d %02d:%02d:%02d",
  8272. $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);
  8273. if (!$tLast #cannot determine time
  8274. || $tSince gt $tLast){ #no message received in window
  8275. if ($actHash->{helper}{$devId}{start} lt $tSince){
  8276. if($autoTry) { #try to send a statusRequest?
  8277. if (!$actHash->{helper}{$devId}{try} || $actHash->{helper}{$devId}{try} < 4){
  8278. $actHash->{helper}{$devId}{try} = $actHash->{helper}{$devId}{try}
  8279. ? ($actHash->{helper}{$devId}{try} + 1)
  8280. : 1;
  8281. my $cmds = CUL_HM_Set($defs{$devName},$devName,"help");
  8282. if ($cmds =~ m/(statusRequest|getSerial)/){
  8283. # send statusrequest if possible
  8284. CUL_HM_Set($defs{$devName},$devName,
  8285. ($cmds =~ m/statusRequest/ ? "statusRequest"
  8286. : "getSerial" ));
  8287. $state = $oldState eq "unset" ? "unknown"
  8288. : $oldState;
  8289. }
  8290. else{
  8291. $actHash->{helper}{$devId}{try} = 99;
  8292. $state = "dead";
  8293. }
  8294. }
  8295. else{
  8296. $state = "dead";
  8297. }
  8298. }
  8299. else{
  8300. $state = "dead";
  8301. }
  8302. }
  8303. else{
  8304. $state = "unknown";
  8305. }
  8306. }
  8307. else{ #message in time
  8308. $state = "alive";
  8309. delete $actHash->{helper}{$devId}{try};
  8310. }
  8311. }
  8312. if ($oldState ne $state){
  8313. CUL_HM_UpdtReadSingle($defs{$devName},"Activity",$state,1);
  8314. $attr{$devName}{actStatus} = $state;
  8315. Log3 $actHash,4,"Device ".$devName." is ".$state;
  8316. }
  8317. if ($state eq "unknown") {$cntUnkn++;}
  8318. elsif ($state eq "alive") {$cntAliv++;}
  8319. elsif ($state eq "dead") {$cntDead++;}
  8320. elsif ($state eq "switchedOff"){$cnt_Off++;}
  8321. push @event, "status_".$devName.":".$state;
  8322. }
  8323. push @event, "state:"."alive:".$cntAliv
  8324. ." dead:".$cntDead
  8325. ." unkn:".$cntUnkn
  8326. ." off:" .$cnt_Off;
  8327. my $allState = join " ",@event;# search and remove outdated readings
  8328. if ($call eq "ActionDetector"){#delete only in routine call
  8329. foreach (keys %{$actHash->{READINGS}}){
  8330. delete $actHash->{READINGS}{$_} if ($allState !~ m/$_:/);
  8331. }
  8332. }
  8333. CUL_HM_UpdtReadBulk($actHash,1,@event);
  8334. $actHash->{helper}{actCycle} = AttrVal($actName,"actCycle",600);
  8335. RemoveInternalTimer("ActionDetector");
  8336. InternalTimer(gettimeofday()+$actHash->{helper}{actCycle}
  8337. ,"CUL_HM_ActCheck", "ActionDetector", 0);
  8338. }
  8339. sub CUL_HM_ActInfo() {# print detailed status information
  8340. my $actHash = CUL_HM_ActGetCreateHash();
  8341. my $tod = int(gettimeofday());
  8342. my $peerIDs = $actHash->{helper}{peers}?$actHash->{helper}{peers}:"";
  8343. my @info;
  8344. foreach my $devId (split(",",$peerIDs)){
  8345. next if (!$devId);
  8346. my $devName = CUL_HM_id2Name($devId);
  8347. next if(!$devName || !defined($attr{$devName}{actCycle}));
  8348. next if(AttrVal($devName,"ignore",0));
  8349. my $state;
  8350. my (undef,$tSec)=CUL_HM_time2sec($attr{$devName}{actCycle});
  8351. if ($tSec != 0){
  8352. my $tLast = ReadingsVal($devName,".protLastRcv",0);
  8353. $tLast =~ /(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)/;
  8354. my $x = $2*30*24*3600 + $3*24*3600 + $4*3600 + $5*60 +$6;
  8355. my @t = localtime($tod - $tSec); #time since when a trigger is expected
  8356. my $y = $x -
  8357. (( $t[4]+1)*30*24*3600
  8358. + $t[3]*24*3600
  8359. + $t[2]*3600
  8360. + $t[1]*60
  8361. + $t[0]);
  8362. my $sign = "next ";
  8363. if ($y < 0){
  8364. $sign = "late -";
  8365. $y *= -1;
  8366. }
  8367. my @c;
  8368. $c[1] = int($y/3600);$y -= $c[1] * 3600;
  8369. $c[0] = int($y/60) ;$y -= $c[0] * 60;
  8370. $state .= sprintf("%-8s %s %s %3d:%02d:%02d %s"
  8371. ,ReadingsVal($devName,"Activity","")
  8372. ,$tLast,$sign,$c[1],$c[0],$y
  8373. ,$devName);
  8374. }
  8375. else{
  8376. $state = sprintf ("%-8s :%30s : "
  8377. ,ReadingsVal($devName,"Activity","")
  8378. ,$devName);
  8379. }
  8380. push @info,$state;
  8381. }
  8382. return sprintf ("%-8s %-19s %s %s\n\n","state"
  8383. ,"last"
  8384. ,"next h:mm:ss"
  8385. ,"name").
  8386. join("\n", sort @info);
  8387. }
  8388. #+++++++++++++++++ helper +++++++++++++++++++++++++++++++++++++++++++++++++++++
  8389. sub CUL_HM_UpdtReadBulk(@) { #update a bunch of readings and trigger the events
  8390. my ($hash,$doTrg,@readings) = @_;
  8391. return if (!@readings ||!defined $hash|| !defined $hash->{NAME} );
  8392. if($evtDly && $doTrg){#delay trigger if in parser and trigger ist requested
  8393. push @evtEt,[$hash,1,"$_"] foreach(@readings);
  8394. }
  8395. else{
  8396. readingsBeginUpdate($hash);
  8397. foreach my $rd (CUL_HM_noDup(@readings)){
  8398. next if (!$rd);
  8399. my ($rdName, $rdVal) = split(":",$rd, 2);
  8400. readingsBulkUpdate($hash,$rdName,
  8401. ((defined($rdVal) && $rdVal ne "")?$rdVal:"-"));
  8402. }
  8403. readingsEndUpdate($hash,$doTrg);
  8404. }
  8405. return $hash->{NAME};
  8406. }
  8407. sub CUL_HM_UpdtReadSingle(@) { #update single reading and trigger the event
  8408. my ($hash,$rName,$val,$doTrg) = @_;
  8409. return if (!defined $hash->{NAME});
  8410. if($evtDly && $doTrg){#delay trigger if in parser and trigger ist requested
  8411. push @evtEt,[$hash,1,"$rName:$val"];
  8412. }
  8413. else{
  8414. readingsSingleUpdate($hash,$rName,$val,$doTrg);
  8415. }
  8416. return $hash->{NAME};
  8417. }
  8418. sub CUL_HM_setAttrIfCh($$$$) {
  8419. my ($name,$att,$val,$trig) = @_;
  8420. if(AttrVal($name,$att,"") ne $val){
  8421. DoTrigger($name,$trig.":".$val) if($trig);
  8422. $attr{$name}{$att} = $val;
  8423. }
  8424. }
  8425. sub CUL_HM_noDup(@) {#return list with no duplicates
  8426. my %all;
  8427. return "" if (scalar(@_) == 0);
  8428. $all{$_}=0 foreach (grep {defined $_ && $_ !~ m/^$/} @_);
  8429. delete $all{""}; #remove empties if present
  8430. return (sort keys %all);
  8431. }
  8432. sub CUL_HM_noDupInString($) {#return string with no duplicates, comma separated
  8433. my ($str) = @_;
  8434. return join ",",CUL_HM_noDup(split ",",$str);
  8435. }
  8436. sub CUL_HM_storeRssi(@){
  8437. my ($name,$peerName,$val,$mNo) = @_;
  8438. return if (!$val || !$name|| !defined $defs{$name});
  8439. my $hash = $defs{$name};
  8440. if (AttrVal($peerName,"subType","") eq "virtual"){
  8441. my $h = InternalVal($name,"IODev",undef);#CUL_HM_name2IoName($peerName);
  8442. return if (!$h);
  8443. $peerName = $h->{NAME};
  8444. }
  8445. else{
  8446. return if (length($peerName) < 3);
  8447. }
  8448. if ($peerName =~ m/^at_/){
  8449. my $hhmrssi = $hash->{helper}{mRssi};
  8450. if ($hhmrssi->{mNo} ne $mNo){# new message
  8451. foreach my $n (keys %{$hhmrssi->{io}}) {
  8452. pop(@{$hhmrssi->{io}{$n}}); # take one from all IOs rssi
  8453. }
  8454. $hhmrssi->{mNo} = $mNo;
  8455. }
  8456. my ($mVal,$mPn) = ($val,substr($peerName,3));
  8457. if ($mPn =~ m/^rpt_(.*)/){# map repeater to io device, use max rssi
  8458. $mPn = $1;
  8459. $mVal = @{$hhmrssi->{io}{$mPn}}[0]
  8460. if( @{$hhmrssi->{io}{$mPn}}[0]
  8461. && @{$hhmrssi->{io}{$mPn}}[0] > $mVal);
  8462. }
  8463. if(CUL_HM_name2IoName($name) eq $mPn){
  8464. if ($mVal > -50 ) {$mVal += 8 ;}
  8465. elsif ($mVal > -60 ) {$mVal += 6 ;}
  8466. elsif ($mVal > -70 ) {$mVal += 4 ;}
  8467. else {$mVal += 2 ;}
  8468. }
  8469. @{$hhmrssi->{io}{$mPn}} = ($mVal,$mVal); # save last rssi twice
  8470. # -> allow tolerance for one missed reception even with good rssi
  8471. # -> reduce useless IO switching
  8472. }
  8473. $hash->{helper}{rssi}{$peerName}{lst} = $val;
  8474. my $rssiP = $hash->{helper}{rssi}{$peerName};
  8475. $rssiP->{min} = $val if (!$rssiP->{min} || $rssiP->{min} > $val);
  8476. $rssiP->{max} = $val if (!$rssiP->{max} || $rssiP->{max} < $val);
  8477. $rssiP->{cnt} ++;
  8478. $rssiP->{cnt} = 10000 if(!$rssiP->{cnt}); # avoid division by zero on overflow!
  8479. if ($rssiP->{cnt} == 1){
  8480. $rssiP->{avg} = $val;
  8481. }
  8482. else{
  8483. $rssiP->{avg} += ($val - $rssiP->{avg}) /$rssiP->{cnt};
  8484. }
  8485. my $rssi;
  8486. # foreach (keys %{$rssiP}){
  8487. foreach ("cnt","min","max","avg","lst"){
  8488. my $val = $rssiP->{$_}?$rssiP->{$_}:0;
  8489. $rssi .= $_.":".(int($val*100)/100)." ";
  8490. }
  8491. $hash->{"rssi_".$peerName} = $rssi;
  8492. CUL_HM_UpdtReadSingle($hash,"rssi_".$peerName,$val,1)
  8493. if (AttrVal($name,"rssiLog",undef));
  8494. return ;
  8495. }
  8496. sub CUL_HM_UpdtCentral($){
  8497. my $name = shift;
  8498. my $id = CUL_HM_name2Id($name);
  8499. return if(!$init_done || length($id) != 6);
  8500. foreach (keys %defs){# remove existing IO assignements
  8501. next if ( AttrVal($_,"hmId","") ne $id
  8502. && InternalVal($_,"owner_CCU","") ne $name);
  8503. delete $defs{$_}{owner_CCU};
  8504. }
  8505. $defs{$name}{assignedIOs} = join(",",devspec2array("hmId=$id"));
  8506. foreach my $ioN(split",",AttrVal($name,"IOList","")){# set parameter in IO
  8507. next if (!$defs{$ioN});
  8508. my $t = $defs{$ioN}{TYPE};
  8509. if ( $t =~ m/^(HMLAN|HMUARTLGW)$/){
  8510. ; # nothing special to do on device
  8511. }
  8512. elsif( $t =~ m/^(CUL|TSCUL|TSSTACKED|STACKABLE_CC)$/){ # nonsi: required for usage of TSCUL/TSSTACKED !!!
  8513. CommandAttr(undef, "$ioN rfmode HomeMatic") # set device to HomeMatic mode
  8514. if (AttrVal($ioN,"rfmode","") ne "HomeMatic");
  8515. }
  8516. else {
  8517. next;
  8518. }
  8519. CommandAttr(undef, "$ioN hmId $id")
  8520. if (AttrVal($ioN,"hmId","") ne $id);
  8521. $defs{$ioN}{owner_CCU} = $name;
  8522. }
  8523. # --- search for peers to CCU and potentially device this channel
  8524. foreach my $ccuBId (CUL_HM_noDup(grep /$id/ ,map{split ",",AttrVal($_,"peerIDs","")}keys %defs)){
  8525. next if (length($ccuBId) !=8);
  8526. # now for each ccu Channel, that ist peered with someone.
  8527. my $btn = hex(substr($ccuBId,6,2)) + 0;
  8528. next if (!$btn);
  8529. CommandDefine(undef,$name."_Btn$btn CUL_HM $ccuBId")
  8530. if (!$modules{CUL_HM}{defptr}{$ccuBId});
  8531. my $ccuChnName = $modules{CUL_HM}{defptr}{$ccuBId}{NAME};
  8532. foreach my $pn (grep !/^$/,
  8533. map{$_ if (AttrVal($_,"peerIDs","") =~ m/$ccuBId/)}
  8534. keys %defs){
  8535. CUL_HM_ID2PeerList ($ccuChnName,unpack('A8',CUL_HM_name2Id($pn)."01"),1);
  8536. }
  8537. }
  8538. my @ioList = grep /.+/,map{$defs{$_} ? $_ : ""} split(",",AttrVal($name,"IOList",""));
  8539. $defs{$name}{helper}{io}{ioList} = \@ioList;
  8540. my $io = AttrVal($name,"IODev","empty");# assign IODev to vccu
  8541. if (AttrVal($name,"IOList","") !~ m/$io/){
  8542. foreach(@ioList){
  8543. if ($defs{$_}){
  8544. $attr{$name}{IODev} = $_;
  8545. last;
  8546. }
  8547. }
  8548. }
  8549. CUL_HM_UpdtCentralState($name);
  8550. }
  8551. sub CUL_HM_UpdtCentralState($){
  8552. my $name = shift;
  8553. return if (!$defs{$name});
  8554. my $state = "";
  8555. my @IOl = split",",AttrVal($name,"IOList","");
  8556. foreach my $e (split",",$defs{$name}{assignedIOs}){
  8557. $state .= "$e:UAS," if (!grep /$e/,@IOl);
  8558. }
  8559. foreach my $ioN (@IOl){
  8560. next if (!defined($defs{$ioN})); # remove undefined IO devices
  8561. my $cnd = ReadingsVal($ioN,"cond","");
  8562. if ($cnd){ # covering all HMLAN/USB
  8563. $state .= "$ioN:$cnd,";
  8564. }
  8565. else{ # handling CUL
  8566. my $st = ReadingsVal($ioN,"state","unknown");
  8567. $state .= "$ioN:".($st ne "Initialized"?$st:"ok").",";
  8568. }
  8569. if (AttrVal($ioN,"hmId","") ne $defs{$name}{DEF}){
  8570. Log 1,"CUL_HM correct hmId for assigned IO $ioN";
  8571. $attr{$ioN}{hmId} = $defs{$name}{DEF};
  8572. }
  8573. };
  8574. $state = "IOs_ok" if (!$state);
  8575. CUL_HM_UpdtReadSingle($defs{$name},"state",$state,1);
  8576. }
  8577. sub CUL_HM_assignIO($){ #check and assign IO, returns 1 if IO changed
  8578. # assign IO device
  8579. my $hash = shift;
  8580. my $result = 0; # default: IO unchanged
  8581. if (!defined $hash->{helper}{prt}{sProc}
  8582. || ( $hash->{helper}{prt}{sProc} == 1
  8583. && defined $hash->{IODev}) #don't change while send in process
  8584. || defined($hash->{helper}{aesCommRq})) { #don't change while CUL aesCommReq in progress
  8585. return $result; # IO unchanged
  8586. }
  8587. my $oldIODev = ($hash->{IODev} && $hash->{IODev}{NAME}) ? $hash->{IODev} : undef;
  8588. my $newIODev;
  8589. my $ioCCU = $hash->{helper}{io}{vccu};
  8590. my $haveIOList = 0;
  8591. my @ioccu;
  8592. if ( $ioCCU
  8593. && defined $defs{$ioCCU}
  8594. && AttrVal($ioCCU,"model","") eq "CCU-FHEM"
  8595. && ref($defs{$ioCCU}{helper}{io}{ioList}) eq 'ARRAY'){
  8596. $haveIOList = 1;
  8597. @ioccu = @{$defs{$ioCCU}{helper}{io}{ioList}};
  8598. my @ios = ((sort {@{$hash->{helper}{mRssi}{io}{$b}}[0] <=>
  8599. @{$hash->{helper}{mRssi}{io}{$a}}[0] }
  8600. (grep { defined @{$hash->{helper}{mRssi}{io}{$_}}[0]} @ioccu))
  8601. ,(grep {!defined @{$hash->{helper}{mRssi}{io}{$_}}[0]} @ioccu));
  8602. unshift @ios,@{$hash->{helper}{io}{prefIO}} if ($hash->{helper}{io}{prefIO});# set prefIO to first choice
  8603. if ($hash->{helper}{io}{restoredIO}) { # set restoredIO to very first choice
  8604. unshift @ios,$hash->{helper}{io}{restoredIO};
  8605. delete ($hash->{helper}{io}{restoredIO}) if ($init_done); # we have a user choice, delete restore data
  8606. Log3 $hash->{NAME}, 0, "CUL_HM_assignIO ".$hash->{NAME}." autoassign restoredIO used";
  8607. }
  8608. foreach my $iom (@ios){
  8609. last if ($iom eq "none"); # if "none" is detected stop vccu auto assignment and try normal
  8610. next if ( !$defs{$iom}
  8611. || ReadingsVal($iom,"state","") eq "disconnected"
  8612. || InternalVal($iom,"XmitOpen",1) == 0); # HMLAN/HMUSB/TSCUL?
  8613. $newIODev = $defs{$iom};# suitable CCU IO found - continue to assign
  8614. last;
  8615. }
  8616. }
  8617. if (!defined $newIODev) {# not assigned thru CCU - try normal
  8618. return 0 if (!$oldIODev);# no IOdev by now - can't help
  8619. $newIODev = $oldIODev; # try keep the last one, if defined
  8620. my $dIo = AttrVal($hash->{NAME},"IODev",""); # if no VCCU is used, attr IODev is the first choice. But if VCCU is used, attr IODev must not be used for restore to work! Then it should be removed from attributes!
  8621. if ($defs{$dIo}) {
  8622. if ( !defined($oldIODev->{NAME})
  8623. || ($oldIODev->{NAME} ne $dIo) ) {
  8624. $newIODev = $defs{$dIo}; # assign according to Attribut
  8625. delete ($hash->{helper}{io}{restoredIO}) if ($init_done); # we have a user choice, delete restore data
  8626. Log3 $hash->{NAME}, 0, "CUL_HM_assignIO ".$hash->{NAME}." attr IODev used";
  8627. }
  8628. }
  8629. else {
  8630. if ($hash->{helper}{io}{restoredIO}) {
  8631. $newIODev = $defs{$hash->{helper}{io}{restoredIO}};
  8632. delete ($hash->{helper}{io}{restoredIO}) if ($init_done); # delete restore data
  8633. Log3 $hash->{NAME}, 0, "CUL_HM_assignIO ".$hash->{NAME}." restoredIO used";
  8634. }
  8635. else {
  8636. AssignIoPort($hash); #let kernal decide, but it is quite time consuming! Only to be used as very last chance!
  8637. $newIODev = $hash->{IODev};
  8638. Log3 $hash->{NAME}, 0, "CUL_HM_assignIO ".$hash->{NAME}." AssignIoPort used";
  8639. }
  8640. }
  8641. }
  8642. if ( defined($newIODev)
  8643. && ( !defined($oldIODev)
  8644. || ($oldIODev != $newIODev) ) ) {
  8645. my $ID = CUL_HM_hash2Id($hash);
  8646. if ($haveIOList) {
  8647. my $lIODevH;
  8648. foreach my $ioLd (@ioccu) { # remove on all unassigend IOs to ensure a consistant state of assignments in IO devices!
  8649. # IO has to keep track about and really remove just if required
  8650. $lIODevH = $defs{$ioLd};
  8651. next if ( !defined($lIODevH)
  8652. || ($lIODevH == $newIODev) );
  8653. if (ReadingsVal($ioLd,"state","") ne "disconnected") {
  8654. if ( $lIODevH->{helper}{VTS_AES} #if this unselected IO is TSCUL 0.14+ we have to remove the device from IO, as it starts with "historical" assignment data
  8655. || ( defined($oldIODev)
  8656. && ($lIODevH == $oldIODev) # HMLAN/HMUARTLGW always start with clean peerlist? At least it tries to.
  8657. && $lIODevH->{TYPE}
  8658. && $lIODevH->{TYPE} =~ m/^(HMLAN|HMUARTLGW)$/s
  8659. ) #if this unselected IO is HMLAN we have to remove the device from IO
  8660. ) {
  8661. $hash->{IODev} = $lIODevH; # temporary assignment for IOWrite to work on each IO!
  8662. IOWrite($hash, "", "remove:".$ID);
  8663. }
  8664. }
  8665. }
  8666. }
  8667. $hash->{IODev} = $newIODev; # finally assign IO
  8668. if ( ($newIODev->{TYPE} && $newIODev->{TYPE} =~ m/^(HMLAN|HMUARTLGW)$/)
  8669. || ( $newIODev->{helper}{VTS_AES})){
  8670. IOWrite($hash, "", "init:".$ID); # assign to new IO
  8671. }
  8672. $result = 1; # IO changed
  8673. }
  8674. return $result;
  8675. }
  8676. sub CUL_HM_stateUpdatDly($$){#delayed queue of status-request
  8677. my ($name,$time) = @_;
  8678. CUL_HM_unQEntity($name,"qReqStat");#remove requests, wait for me.
  8679. RemoveInternalTimer("sUpdt:$name");
  8680. InternalTimer(gettimeofday()+$time,"CUL_HM_qStateUpdatIfEnab","sUpdt:$name",0);
  8681. }
  8682. sub CUL_HM_qStateUpdatIfEnab($@){#in:name or id, queue stat-request
  8683. my ($name,$force) = @_;
  8684. $name = substr($name,6) if ($name =~ m/^sUpdt:/);
  8685. $name = CUL_HM_id2Name($name) if ($name =~ m/^[A-F0-9]{6,8}$/i);
  8686. $name =~ s/_chn-\d\d$//;
  8687. return if ( !$defs{$name} #device unknown, ignore
  8688. || CUL_HM_Set($defs{$name},$name,"help") !~ m/statusRequest/);
  8689. if ($force || ((CUL_HM_getAttrInt($name,"autoReadReg") & 0x0f) > 3)){
  8690. CUL_HM_qEntity($name,"qReqStat") ;
  8691. }
  8692. }
  8693. sub CUL_HM_qAutoRead($$){
  8694. my ($name,$lvl) = @_;
  8695. CUL_HM_configUpdate($name);
  8696. return if (!$defs{$name}
  8697. ||$lvl >= (0x07 & CUL_HM_getAttrInt($name,"autoReadReg")));
  8698. CUL_HM_qEntity($name,"qReqConf");
  8699. }
  8700. sub CUL_HM_unQEntity($$){# remove entity from q
  8701. my ($name,$q) = @_;
  8702. my $devN = CUL_HM_getDeviceName($name);
  8703. return if (AttrVal($devN,"subType","") eq "virtual");
  8704. my $dq = $defs{$devN}{helper}{q};
  8705. RemoveInternalTimer("sUpdt:$name") if ($q eq "qReqStat");#remove delayed
  8706. return if ($dq->{$q} eq "");
  8707. if ($devN eq $name){#all channels included
  8708. $dq->{$q}="";
  8709. }
  8710. else{
  8711. my @chns = split(",",$dq->{$q});
  8712. my $chn = substr(CUL_HM_name2Id($name),6,2);
  8713. @chns = grep !/$chn/,@chns;
  8714. @chns = grep !/00/,@chns;#remove device as well - just in case
  8715. $dq->{$q} = join",",@chns;
  8716. }
  8717. my $cq = (CUL_HM_getRxType($defs{$name}) & 0x1C)?($q."Wu") : $q;
  8718. return if( !$modules{CUL_HM}{helper}{$cq}
  8719. || scalar(@{$modules{CUL_HM}{helper}{$cq}}) == 0);
  8720. my $mQ = $modules{CUL_HM}{helper}{$cq};
  8721. return if(!$mQ || scalar(@{$mQ}) == 0);
  8722. @{$mQ} = grep !/^$devN$/,@{$mQ} if ($dq->{$q} eq "");
  8723. }
  8724. sub CUL_HM_qEntity($$){ # add to queue
  8725. my ($name,$q) = @_;
  8726. return if ($modules{CUL_HM}{helper}{hmManualOper});#no autoaction when manual
  8727. my $devN = CUL_HM_getDeviceName($name);
  8728. return if (AttrVal($devN,"subType","") eq "virtual");
  8729. $name = $devN if ($defs{$devN}{helper}{q}{$q} eq "00"); #already requesting all
  8730. if ($devN eq $name){#config for all device
  8731. $defs{$devN}{helper}{q}{$q}="00";
  8732. }
  8733. else{
  8734. $defs{$devN}{helper}{q}{$q} = CUL_HM_noDupInString(
  8735. $defs{$devN}{helper}{q}{$q}
  8736. .",".substr(CUL_HM_name2Id($name),6,2));
  8737. }
  8738. $q .= "Wu" if (!(CUL_HM_getRxType($defs{$name}) & 0x03));#normal or wakeup q?
  8739. $q = $modules{CUL_HM}{helper}{$q};
  8740. @{$q} = CUL_HM_noDup(@{$q},$devN); #we only q device - channels are stored in the device
  8741. my $wT = (@{$modules{CUL_HM}{helper}{qReqStat}})?
  8742. "1":
  8743. $modules{CUL_HM}{hmAutoReadScan};
  8744. RemoveInternalTimer("CUL_HM_procQs");
  8745. InternalTimer(gettimeofday()+ $wT,"CUL_HM_procQs","CUL_HM_procQs", 0);
  8746. }
  8747. sub CUL_HM_readStateTo($){#staterequest not working
  8748. my ($eN) = @_;
  8749. $eN = substr($eN,6) if ($eN =~ m/^sUpdt:/);
  8750. CUL_HM_UpdtReadSingle($defs{$eN},"state","unreachable",1);
  8751. CUL_HM_stateUpdatDly($eN,1800 );
  8752. }
  8753. sub CUL_HM_procQs($){#process non-wakeup queues
  8754. # --- verify send is possible
  8755. my $mq = $modules{CUL_HM}{helper};
  8756. foreach my $q ("qReqStat","qReqConf"){
  8757. if (@{$mq->{$q}}){
  8758. my $devN = ${$mq->{$q}}[0];
  8759. my $devH = $defs{$devN};
  8760. CUL_HM_assignIO($devH);
  8761. next if(!defined $devH->{IODev}{NAME});
  8762. my $ioName = $devH->{IODev}{NAME};
  8763. if ( ( ReadingsVal($ioName,"cond","") =~ m/^(ok|Overload-released|Warning-HighLoad|init)$/
  8764. && $q eq "qReqStat")
  8765. ||( CUL_HM_autoReadReady($ioName)
  8766. && !$devH->{cmdStack}
  8767. && $q eq "qReqConf")){
  8768. my $dq = $devH->{helper}{q};
  8769. my @chns = split(",",$dq->{$q});
  8770. my $nOpen = scalar @chns;
  8771. if (@chns > 1){$dq->{$q} = join ",",@chns[1..$nOpen-1];}
  8772. else{ $dq->{$q} = "";
  8773. @{$mq->{$q}} = grep !/^$devN$/,@{$mq->{$q}};
  8774. }
  8775. my $dId = CUL_HM_name2Id($devN);
  8776. my $eN=($chns[0] && $chns[0]ne "00")?CUL_HM_id2Name($dId.$chns[0]):$devN;
  8777. next if(!defined $defs{$eN});
  8778. if ($q eq "qReqConf"){
  8779. $mq->{autoRdActive} = $devN;
  8780. CUL_HM_Set($defs{$eN},$eN,"getConfig");
  8781. }
  8782. else{
  8783. CUL_HM_Set($defs{$eN},$eN,"statusRequest");
  8784. CUL_HM_unQEntity($eN,"qReqStat") if (!$dq->{$q});
  8785. InternalTimer(gettimeofday()+20,"CUL_HM_readStateTo","sUpdt:$eN",0);
  8786. }
  8787. }
  8788. last; # execute only one!
  8789. }
  8790. }
  8791. delete $mq->{autoRdActive}
  8792. if ($mq->{autoRdActive} &&
  8793. $defs{$mq->{autoRdActive}}{helper}{prt}{sProc} != 1);
  8794. my $next;# how long to wait for next timer
  8795. if (@{$mq->{qReqStat}}){$next = 1}
  8796. elsif (@{$mq->{qReqConf}}){$next = $modules{CUL_HM}{hmAutoReadScan}}
  8797. InternalTimer(gettimeofday()+$next,"CUL_HM_procQs","CUL_HM_procQs",0)
  8798. if ($next);
  8799. }
  8800. sub CUL_HM_appFromQ($$){#stack commands if pend in WuQ
  8801. my ($name,$reason) = @_;
  8802. my $devN = CUL_HM_getDeviceName($name);
  8803. my $dId = CUL_HM_name2Id($devN);
  8804. my $dq = $defs{$devN}{helper}{q};
  8805. if ($reason eq "cf"){# reason is config. add all since User has control
  8806. foreach my $q ("qReqStat","qReqConf"){
  8807. if ($dq->{$q} ne ""){# need update
  8808. my @eName;
  8809. if ($dq->{$q} eq "00"){
  8810. push @eName,$devN;
  8811. }
  8812. else{
  8813. my @chns = split(",",$dq->{$q});
  8814. push @eName,CUL_HM_id2Name($dId.$_)foreach (@chns);
  8815. }
  8816. $dq->{$q} = "";
  8817. @{$modules{CUL_HM}{helper}{$q."Wu"}} =
  8818. grep !/^$devN$/,@{$modules{CUL_HM}{helper}{$q."Wu"}};
  8819. foreach my $eN(@eName){
  8820. next if (!$eN);
  8821. CUL_HM_Set($defs{$eN},$eN,"getConfig") if ($q eq "qReqConf");
  8822. CUL_HM_Set($defs{$eN},$eN,"statusRequest") if ($q eq "qReqStat");
  8823. }
  8824. }
  8825. }
  8826. }
  8827. elsif($reason eq "wu"){#wakeup - just add one step
  8828. my $ioName = $defs{$devN}{IODev}{NAME};
  8829. return if (!CUL_HM_autoReadReady($ioName));# no sufficient performance
  8830. foreach my $q ("qReqStat","qReqConf"){
  8831. if ($dq->{$q} ne ""){# need update
  8832. my @chns = split(",",$dq->{$q});
  8833. my $nOpen = scalar @chns;
  8834. if ($nOpen > 1){$dq->{$q} = join ",",@chns[1..$nOpen-1];}
  8835. else{ $dq->{$q} = "";
  8836. @{$modules{CUL_HM}{helper}{$q."Wu"}} =
  8837. grep !/^$devN$/,@{$modules{CUL_HM}{helper}{$q."Wu"}};
  8838. }
  8839. my $eN=($chns[0]ne "00")?CUL_HM_id2Name($dId.$chns[0]):$devN;
  8840. CUL_HM_Set($defs{$eN},$eN,"getConfig") if ($q eq "qReqConf");
  8841. CUL_HM_Set($defs{$eN},$eN,"statusRequest") if ($q eq "qReqStat");
  8842. return;# Only one per step - very defensive.
  8843. }
  8844. }
  8845. }
  8846. }
  8847. sub CUL_HM_autoReadReady($){# capacity for autoread available?
  8848. my $ioName = shift;
  8849. my $mHlp = $modules{CUL_HM}{helper};
  8850. if ( $mHlp->{autoRdActive} # predecessor available
  8851. && $defs{$mHlp->{autoRdActive}}){
  8852. return 0 if ($defs{$mHlp->{autoRdActive}}{helper}{prt}{sProc} == 1); # predecessor still on
  8853. }
  8854. if ( !$ioName
  8855. || ReadingsVal($ioName,"cond","init") !~ m/^(ok|Overload-released|init)$/#default init for CUL
  8856. || ( defined $defs{$ioName}->{msgLoadCurrent}
  8857. && ( $defs{$ioName}->{msgLoadCurrent}>
  8858. (defined $defs{$ioName}{helper}{loadLvl}?$defs{$ioName}{helper}{loadLvl}{bl}:40)))){
  8859. return 0;
  8860. }
  8861. return 1;
  8862. }
  8863. sub CUL_HM_readValIfTO($){#
  8864. my ($name,$rd,$val) = split(":",shift);# uncertain:$name:$reading:$value
  8865. readingsSingleUpdate($defs{$name},$rd,$val,1);
  8866. }
  8867. sub CUL_HM_motionCheck($){#
  8868. my ($name) = split(":",shift);# uncertain:$name:$reading:$value
  8869. if (defined $defs{$name}{helper}{moStart}){
  8870. CUL_HM_UpdtReadBulk($defs{$name},1,"state:noMotion"
  8871. ,"motion:off"
  8872. ,"motionDuration:".(int(gettimeofday())-int($defs{$name}{helper}{moStart})));
  8873. delete $defs{$name}{helper}{moStart};
  8874. }
  8875. else{
  8876. CUL_HM_UpdtReadBulk($defs{$name},1,"state:noMotion"
  8877. ,"motion:off");
  8878. }
  8879. }
  8880. sub CUL_HM_reWriteDisplay($){
  8881. my ($name) = split(":",shift);# uncertain:$name:$reading:$value
  8882. CUL_HM_Set($defs{$name},$name,"displayEP",":::");
  8883. }
  8884. sub CUL_HM_getAttr($$$){#return attrValue - consider device if empty
  8885. my ($name,$attrName,$default) = @_;
  8886. my $val;
  8887. if($defs{$name}){
  8888. $val = (defined $attr{$name}{$attrName})
  8889. ? $attr{$name}{$attrName}
  8890. : undef;
  8891. if (!defined $val){
  8892. my $devN = $defs{$name}{device}?$defs{$name}{device}:$name;
  8893. $val = (defined $attr{$devN}{$attrName})
  8894. ? $attr{$devN}{$attrName}
  8895. : ($modules{CUL_HM}{AttrListDef} && $modules{CUL_HM}{AttrListDef}{$attrName})?$modules{CUL_HM}{AttrListDef}{$attrName}
  8896. :$default;
  8897. }
  8898. }
  8899. return $val;
  8900. }
  8901. sub CUL_HM_getAttrInt($@){#return attrValue as integer
  8902. my ($name,$attrName,$default) = @_;
  8903. $default = 0 if (!defined $default);
  8904. if($modules{CUL_HM}{AttrListDef} && $modules{CUL_HM}{AttrListDef}{$attrName}){
  8905. }
  8906. if($name && $defs{$name}){
  8907. my $devN = $defs{$name}{device}?$defs{$name}{device}:$name;
  8908. my $val = "0".AttrVal($name,$attrName
  8909. ,AttrVal($devN,$attrName
  8910. ,($modules{CUL_HM}{AttrListDef} && $modules{CUL_HM}{AttrListDef}{$attrName})?$modules{CUL_HM}{AttrListDef}{$attrName}
  8911. :$default));
  8912. $val =~ s/(\d*).*/$1/;
  8913. return int($val);
  8914. }
  8915. else{
  8916. return $default;
  8917. }
  8918. }
  8919. #+++++++++++++++++ external use +++++++++++++++++++++++++++++++++++++++++++++++
  8920. sub CUL_HM_peerUsed($) {# are peers expected?
  8921. # return 0: no peers expected
  8922. # 1: peers expected, list valid
  8923. # 2: peers expected, list invalid
  8924. # 3: peers possible (virtuall actor)
  8925. my $name = shift;
  8926. my $hash = $defs{$name};
  8927. return 0 if (!$hash->{helper}{role}{chn});#device has no channels
  8928. return 3 if ($hash->{helper}{role}{vrt});
  8929. my $mId = CUL_HM_getMId($hash);
  8930. my $cNo = hex(substr($hash->{DEF}."01",6,2))."p"; #default to channel 01
  8931. return 0 if (!$mId || !$culHmModel->{$mId});
  8932. foreach my $ls (split ",",$culHmModel->{$mId}{lst}){
  8933. my ($l,$c) = split":",$ls;
  8934. if ( ($l =~ m/^(p|3|4)$/ && !$c ) # 3,4,p without chanspec
  8935. ||($c && $c =~ m/$cNo/ )){
  8936. return (AttrVal($name,"peerIDs","") =~ m/00000000/?1:2);
  8937. }
  8938. }
  8939. }
  8940. sub CUL_HM_reglUsed($) {# provide data for HMinfo
  8941. my $name = shift;
  8942. my $hash = $defs{$name};
  8943. my ($devId,$chn) = unpack 'A6A2',$hash->{DEF}."01";
  8944. return undef if (AttrVal(CUL_HM_id2Name($devId),"subType","") eq "virtual");
  8945. my @pNames;
  8946. push @pNames,CUL_HM_peerChName($_,$devId)
  8947. foreach (grep !/(00000000|x)/,split(",",AttrVal($name,"peerIDs","")));#dont check 'x' peers
  8948. my @lsNo;
  8949. my $mId = CUL_HM_getMId($hash);
  8950. return undef if (!$mId || !$culHmModel->{$mId});
  8951. if ($hash->{helper}{role}{dev}){
  8952. push @lsNo,"0.";
  8953. }
  8954. if ($hash->{helper}{role}{chn}){
  8955. foreach my $ls (split ",",$culHmModel->{$mId}{lst}){
  8956. my ($l,$c) = split":",$ls;
  8957. if ($l ne "p"){# ignore peer-only entries
  8958. if ($c){
  8959. my $chNo = hex($chn);
  8960. if ($c =~ m/($chNo)p/){push @lsNo,"$l.$_" foreach (@pNames);}
  8961. elsif($c =~ m/$chNo/ ){push @lsNo,"$l.";}
  8962. }
  8963. else{
  8964. if ($l == 3 || $l == 4){push @lsNo,"$l.$_" foreach (@pNames);
  8965. }else{ push @lsNo,"$l." ;}
  8966. }
  8967. }
  8968. }
  8969. }
  8970. my $pre = $hash->{helper}{expert}{raw}?"":".";
  8971. $_ = $pre."RegL_0".$_ foreach (@lsNo);
  8972. return @lsNo;
  8973. }
  8974. sub CUL_HM_complConfigTest($){# Q - check register consistency some time later
  8975. my $name = shift;
  8976. return if ($modules{CUL_HM}{helper}{hmManualOper});#no autoaction when manual
  8977. push @{$modules{CUL_HM}{helper}{confCheckArr}},$name;
  8978. if (scalar @{$modules{CUL_HM}{helper}{confCheckArr}} == 1){
  8979. RemoveInternalTimer("CUL_HM_complConfigTO");
  8980. InternalTimer(gettimeofday()+ 1800,"CUL_HM_complConfigTO","CUL_HM_complConfigTO", 0);
  8981. }
  8982. }
  8983. sub CUL_HM_complConfigTestRm($){# Q - check register consistency some time later
  8984. my $name = shift;
  8985. my $mQ = $modules{CUL_HM}{helper}{confCheckArr};
  8986. @{$mQ} = grep !/^$name$/,@{$mQ};
  8987. }
  8988. sub CUL_HM_complConfigTO($) {# now perform consistency check of register
  8989. my @arr = @{$modules{CUL_HM}{helper}{confCheckArr}};
  8990. @{$modules{CUL_HM}{helper}{confCheckArr}} = ();
  8991. CUL_HM_complConfig($_) foreach (CUL_HM_noDup(@arr));
  8992. }
  8993. sub CUL_HM_complConfig($;$) {# read config if enabled and not complete
  8994. my ($name,$dly) = @_;
  8995. my $devN = CUL_HM_getDeviceName($name);
  8996. return if (AttrVal($devN,"subType","") eq "virtual");
  8997. return if ($modules{CUL_HM}{helper}{hmManualOper});#no autoaction when manual
  8998. return if ((CUL_HM_getAttrInt($name,"autoReadReg") & 0x07) < 5);
  8999. if ($defs{$devN}{helper}{prt}{sProc} != 0){# we wait till device is idle.
  9000. CUL_HM_complConfigTest($name); # requeue and wait patient
  9001. }
  9002. elsif (CUL_HM_peerUsed($name) == 2){
  9003. CUL_HM_qAutoRead($name,0) if(!$dly);
  9004. CUL_HM_complConfigTest($name);
  9005. delete $modules{CUL_HM}{helper}{cfgCmpl}{$name};
  9006. Log3 $name,5,"CUL_HM $name queue configRead, peers incomplete";
  9007. }
  9008. else{
  9009. my @regList = CUL_HM_reglUsed($name);
  9010. foreach (@regList){
  9011. if (ReadingsVal($name,$_,"") !~ m/00:00/){
  9012. CUL_HM_qAutoRead($name,0) if(!$dly);
  9013. CUL_HM_complConfigTest($name);
  9014. delete $modules{CUL_HM}{helper}{cfgCmpl}{$name};
  9015. Log3 $name,5,"CUL_HM $name queue configRead, register incomplete";
  9016. last;
  9017. }
  9018. }
  9019. $modules{CUL_HM}{helper}{cfgCmpl}{$name} = 1;#mark config as complete
  9020. }
  9021. }
  9022. sub CUL_HM_configUpdate($) {# mark entities with changed data
  9023. my $name = shift;
  9024. @{$modules{CUL_HM}{helper}{confUpdt}} =
  9025. CUL_HM_noDup(@{$modules{CUL_HM}{helper}{confUpdt}},$name);
  9026. }
  9027. sub CUL_HM_cleanShadowReg($){
  9028. # remove shadow-regs if those are identical to readings or
  9029. # the reading does not exist.
  9030. # return dirty "1" if some shadowregs still remain active
  9031. my ($name) = @_;
  9032. my $hash = $defs{$name};
  9033. my $dirty = 0;
  9034. foreach my $rLn (keys %{$hash->{helper}{shadowReg}}){
  9035. my $rLnP = ($hash->{helper}{expert}{raw} ? "" : ".").$rLn;
  9036. if ( !$hash->{READINGS}{$rLnP}
  9037. || !$hash->{helper}{shadowReg}{$rLn}
  9038. || $hash->{helper}{shadowReg}{$rLn} eq $hash->{READINGS}{$rLnP}{VAL}){
  9039. delete $hash->{helper}{shadowReg}{$rLn};
  9040. }
  9041. else{
  9042. $dirty = 1;
  9043. }
  9044. }
  9045. return $dirty;
  9046. }
  9047. #+++++++++++++++++ templates ++++++++++++++++++++++++++++++++++++++++++++++++++
  9048. sub CUL_HM_tempListTmpl(@) { ##################################################
  9049. # $name is comma separated list of names
  9050. # $template is formated <file>:template - file is optional
  9051. my ($name,$action,$template)=@_;
  9052. my %dl = (Sat=>0,Sun=>1,Mon=>2,Tue=>3,Wed=>4,Thu=>5,Fri=>6);
  9053. my %dlf = (1=>{Sat=>0,Sun=>0,Mon=>0,Tue=>0,Wed=>0,Thu=>0,Fri=>0},
  9054. 2=>{Sat=>0,Sun=>0,Mon=>0,Tue=>0,Wed=>0,Thu=>0,Fri=>0},
  9055. 3=>{Sat=>0,Sun=>0,Mon=>0,Tue=>0,Wed=>0,Thu=>0,Fri=>0});
  9056. return "unused" if ($template =~ m/^(none|0) *$/);
  9057. my $ret = "";
  9058. my @el = split",",$name;
  9059. my ($fName,$tmpl) = split":",$template;
  9060. if(!$tmpl){ # just a template - switch
  9061. $tmpl = $fName ? $fName: $name;
  9062. $fName = (eval "defined(&HMinfo_tempListDefFn)")
  9063. ? HMinfo_tempListDefFn()
  9064. : "./tempList.cfg";
  9065. }
  9066. my ($err,@RLines) = FileRead($fName);
  9067. return "file: $fName error:$err" if ($err);
  9068. # return "file: $fName for $name does not exist" if (!(-e $fName));
  9069. # open(aSave, "$fName") || return("Can't open $fName: $!");
  9070. my $found = 0;
  9071. my @entryFail = ();
  9072. my @exec = ();
  9073. if ($template =~ m/defaultWeekplan$/){
  9074. $found = 1;
  9075. foreach my $eN(@el){
  9076. if ($action eq "verify"){
  9077. my $val = "24:00 18.0";
  9078. foreach ( "R_0_tempListSat"
  9079. ,"R_1_tempListSun"
  9080. ,"R_2_tempListMon"
  9081. ,"R_3_tempListTue"
  9082. ,"R_4_tempListWed"
  9083. ,"R_5_tempListThu"
  9084. ,"R_6_tempListFri"){
  9085. my $nv = ReadingsVal($eN,$_,"empty");
  9086. $nv = join(" ",split(" ",$nv));
  9087. push @entryFail,$eN." :".$_." mismatch $val ne $nv ##" if ($val ne $nv);
  9088. }
  9089. $dlf{1}{Sat} = 1;
  9090. $dlf{1}{Sun} = 1;
  9091. $dlf{1}{Mon} = 1;
  9092. $dlf{1}{Tue} = 1;
  9093. $dlf{1}{Wed} = 1;
  9094. $dlf{1}{Thu} = 1;
  9095. $dlf{1}{Fri} = 1;
  9096. }
  9097. elsif($action eq "restore"){
  9098. foreach ( "tempListSat"
  9099. ,"tempListSun"
  9100. ,"tempListMon"
  9101. ,"tempListTue"
  9102. ,"tempListWed"
  9103. ,"tempListThu"
  9104. ,"tempListFri"){
  9105. my $x = CUL_HM_Set($defs{$eN},$eN,$_,"prep",split(" "," 24:00 18.0"));
  9106. push @entryFail,$eN." :".$_." respose:$x" if ($x ne "1");
  9107. push @exec,"$eN $_ exec 24:00 18.0";
  9108. }
  9109. }
  9110. }
  9111. }
  9112. else{
  9113. foreach(@RLines){
  9114. # while(<aSave>){
  9115. chomp;
  9116. my $line = $_;
  9117. $line =~ s/\r//g;
  9118. next if($line =~ m/#/);
  9119. if($line =~ m/^entities:/){
  9120. last if ($found != 0);
  9121. $line =~ s/.*://;
  9122. foreach my $eN (split(",",$line)){
  9123. $eN =~ s/ //g;
  9124. $found = 1 if ($eN eq $tmpl);
  9125. }
  9126. }
  9127. elsif($found == 1 && $line =~ m/(R_)?(P[123])?(_?._)?tempList[SMFWT].*\>/){
  9128. my ($prg,$tln,$val);
  9129. $prg = $1 if ($line =~ m/P(.)_/);
  9130. $prg = 1 if (!$prg);
  9131. ($tln,$val) = ($1,$2) if ($line =~ m/(.*)>(.*)/);
  9132. $tln =~ s/ //g;
  9133. $tln = "R_".$tln if($tln !~ m/^R_/);
  9134. my $dayTxt = ($tln =~ m/tempList(...)/ ? $1 : "");
  9135. if (!defined $dl{$dayTxt}){
  9136. push @entryFail," undefined daycode:$dayTxt";
  9137. next;
  9138. }
  9139. if ($dlf{$prg}{$dayTxt}){
  9140. push @entryFail," duplicate daycode:$dayTxt";
  9141. next;
  9142. }
  9143. $dlf{$prg}{$dayTxt} = 1;
  9144. my $day = $dl{$dayTxt};
  9145. $tln =~ s/tempList/${day}_tempList/ if ($tln !~ m/_[0-6]_/);
  9146. if (AttrVal($name,"model","") =~ m/^HM-TC-IT-WM-W/){
  9147. $tln =~ s/^R_/R_P1_/ if ($tln !~ m/^R_P/);# add P1 as default
  9148. }
  9149. else{
  9150. $tln =~ s/^R_P1_/R_/ if ($tln =~ m/^R_P/);# remove P1 default
  9151. }
  9152. $val =~ tr/ +/ /;
  9153. $val =~ s/^ //;
  9154. $val =~ s/ $//;
  9155. @exec = ();
  9156. foreach my $eN(@el){
  9157. if ($action eq "verify"){
  9158. $val = join(" ",map{(my $foo = $_) =~ s/^(.\.)/0$1/;$foo} split(" ",$val));
  9159. my $nv = ReadingsVal($eN,$tln,"empty");
  9160. $nv = join(" ",map{(my $foo = $_) =~ s/^(.\.)/0$1/;$foo} split(" ",$nv));
  9161. push @entryFail,$eN." :".$tln." mismatch $val ne $nv ##" if ($val ne $nv);
  9162. }
  9163. elsif($action eq "restore"){
  9164. $val = lc($1)." ".$val if ($tln =~ m/(P.)_._tempList/);
  9165. $tln =~ s/R_(P._)?._//;
  9166. my $x = CUL_HM_Set($defs{$eN},$eN,$tln,"prep",split(" ",$val));
  9167. push @entryFail,$eN." :".$tln." respose:$x" if ($x ne "1");
  9168. push @exec,"$eN $tln exec $val";
  9169. }
  9170. }
  9171. }
  9172. $ret = "failed Entries:\n " .join("\n ",@entryFail) if (scalar@entryFail);
  9173. }
  9174. }
  9175. if (!$found){
  9176. $ret .= "$tmpl not found in file $fName";
  9177. }
  9178. else{
  9179. if(CUL_HM_Get($defs{$name},$name,"param","model") ne "HM-TC-IT-WM-W-EU02"){
  9180. delete $dlf{2};
  9181. delete $dlf{3};
  9182. }
  9183. foreach my $p (keys %dlf){
  9184. my @unprg = grep !/^$/,map {$dlf{$p}{$_}?"":$_} keys %{$dlf{$p}};
  9185. my $cnt = scalar @unprg;
  9186. if ($cnt > 0 && $cnt < 7) {$ret .= "\n $name: incomplete template for prog $p days:".join(",",@unprg);}
  9187. elsif ($cnt == 7) {$ret .= "\n $name: unprogrammed prog $p ";}
  9188. else{
  9189. $ret .= "\n $name: tempList not verified " if (grep {$defs{$name}{READINGS}{$_}{VAL} ne "verified"}
  9190. grep /tempList_State/,
  9191. keys %{$defs{$name}{READINGS}});
  9192. }
  9193. }
  9194. }
  9195. foreach (@exec){
  9196. my @param = split(" ",$_);
  9197. CUL_HM_Set($defs{$param[0]},@param);
  9198. }
  9199. # close(aSave);
  9200. return $ret;
  9201. }
  9202. 1;
  9203. =pod
  9204. =item device
  9205. =item summary controls wireless homematic devices
  9206. =item summary_DE steuert HomeMatic devices auf Funk Basis
  9207. =begin html
  9208. <a name="CUL_HM"></a><h3>CUL_HM</h3>
  9209. <ul>
  9210. Support for eQ-3 HomeMatic devices via the <a href="#CUL">CUL</a> or the <a href="#HMLAN">HMLAN</a>.<br>
  9211. <br>
  9212. <a name="CUL_HMdefine"></a><b>Define</b>
  9213. <ul>
  9214. <code><B>define &lt;name&gt; CUL_HM &lt;6-digit-hex-code|8-digit-hex-code&gt;</B></code>
  9215. <br><br>
  9216. Correct device definition is the key for HM environment simple maintenance.
  9217. <br>
  9218. Background to define entities:<br>
  9219. HM devices has a 3 byte (6 digit hex value) HMid - which is key for
  9220. addressing. Each device hosts one or more channels. HMid for a channel is
  9221. the device's HMid plus the channel number (1 byte, 2 digit) in hex.
  9222. Channels should be defined for all multi-channel devices. Channel entities
  9223. cannot be defined if the hosting device does not exist<br> Note: FHEM
  9224. mappes channel 1 to the device if it is not defined explicitely. Therefore
  9225. it does not need to be defined for single channel devices.<br>
  9226. Note: if a device is deleted all assotiated channels will be removed as
  9227. well. <br> An example for a full definition of a 2 channel switch is given
  9228. below:<br>
  9229. <ul><code>
  9230. define livingRoomSwitch CUL_HM 123456<br>
  9231. define LivingroomMainLight CUL_HM 12345601<br>
  9232. define LivingroomBackLight CUL_HM 12345602<br><br></code>
  9233. </ul>
  9234. livingRoomSwitch is the device managing communication. This device is
  9235. defined prior to channels to be able to setup references. <br>
  9236. LivingroomMainLight is channel 01 dealing with status of light, channel
  9237. peers and channel assotiated register. If not defined channel 01 is covered
  9238. by the device entity.<br> LivingRoomBackLight is the second 'channel',
  9239. channel 02. Its definition is mandatory to operate this function.<br><br>
  9240. Sender specials: HM threats each button of remotes, push buttons and
  9241. similar as channels. It is possible (not necessary) to define a channel per
  9242. button. If all channels are defined access to pairing informatin is
  9243. possible as well as access to channel related register. Furthermore names
  9244. make the traces better readable.<br><br>
  9245. define may also be invoked by the <a href="#autocreate">autocreate</a>
  9246. module, together with the necessary subType attribute.
  9247. Usually you issue a <a href="#CULset">hmPairForSec</a> and press the
  9248. corresponding button on the device to be paired, or issue a <a
  9249. href="#CULset">hmPairSerial</a> set command if the device is a receiver
  9250. and you know its serial number. Autocreate will then create a fhem
  9251. device and set all necessary attributes. Without pairing the device
  9252. will not accept messages from fhem. fhem may create the device even if
  9253. the pairing is not successful. Upon a successful pairing you'll see a
  9254. CommandAccepted entry in the details section of the CUL_HM device.<br><br>
  9255. If you cannot use autocreate, then you have to specify:<br>
  9256. <ul>
  9257. <li>the &lt;6-digit-hex-code&gt;or HMid+ch &lt;8-digit-hex-code&gt;<br>
  9258. It is the unique, hardcoded device-address and cannot be changed (no,
  9259. you cannot choose it arbitrarily like for FS20 devices). You may
  9260. detect it by inspecting the fhem log.</li>
  9261. <li>the subType attribute<br>
  9262. which is one of switch dimmer blindActuator remote sensor swi
  9263. pushButton threeStateSensor motionDetector keyMatic winMatic
  9264. smokeDetector</li>
  9265. </ul>
  9266. Without these attributes fhem won't be able to decode device messages
  9267. appropriately. <br><br>
  9268. <b>Notes</b>
  9269. <ul>
  9270. <li>If the interface is a CUL device, the <a href="#rfmode">rfmode </a>
  9271. attribute of the corresponding CUL/CUN device must be set to HomeMatic.
  9272. Note: this mode is BidCos/Homematic only, you will <b>not</b> receive
  9273. FS20/HMS/EM/S300 messages via this device. Previously defined FS20/HMS
  9274. etc devices must be assigned to a different input device (CUL/FHZ/etc).
  9275. </li>
  9276. <li>Currently supported device families: remote, switch, dimmer,
  9277. blindActuator, motionDetector, smokeDetector, threeStateSensor,
  9278. THSensor, winmatic. Special devices: KS550, HM-CC-TC and the KFM100.
  9279. </li>
  9280. <li>Device messages can only be interpreted correctly if the device type is
  9281. known. fhem will extract the device type from a "pairing request"
  9282. message, even if it won't respond to it (see <a
  9283. href="#hmPairSerial">hmPairSerial</a> and <a
  9284. href="#hmPairForSec">hmPairForSec</a> to enable pairing).
  9285. As an alternative, set the correct subType and model attributes, for a
  9286. list of possible subType values see "attr hmdevice ?".</li>
  9287. <a name="HMAES"></a>
  9288. <li>The so called "AES-Encryption" is in reality a signing request: if it is
  9289. enabled, an actor device will only execute a received command, if a
  9290. correct answer to a request generated by the actor is received. This
  9291. means:
  9292. <ul>
  9293. <li>Reaction to commands is noticably slower, as 3 messages are sent
  9294. instead of one before the action is processed by the actor.</li>
  9295. <li>Every command and its final ack from the device is sent in clear,
  9296. so an outside observer will know the status of each device.</li>
  9297. <li>The firmware implementation is buggy: the "toggle" event is executed
  9298. <b>before</b> the answer for the signing request is received, at
  9299. least by some switches (HM-LC-Sw1-Pl and HM-LC-SW2-PB-FM).</li>
  9300. <li>The <a href="#HMLAN">HMLAN</a> configurator will answer signing
  9301. requests by itself, and if it is configured with the 3-byte address
  9302. of a foreign CCU which is still configurerd with the default
  9303. password, it is able to answer signing requests correctly.</li>
  9304. <li>AES-Encryption is useable with a HMLAN or a CUL. When using
  9305. a CUL, the perl-module Crypt::Rijndael needs to be installed.
  9306. Due to the issues above I do not recommend using Homematic
  9307. encryption at all.</li>
  9308. </ul>
  9309. </li>
  9310. </ul>
  9311. </ul><br>
  9312. <a name="CUL_HMset"></a><b>Set</b>
  9313. <ul>
  9314. Note: devices which are normally send-only (remote/sensor/etc) must be set
  9315. into pairing/learning mode in order to receive the following commands.
  9316. <br><br>
  9317. Universal commands (available to most hm devices):
  9318. <ul>
  9319. <li><B>assignHmKey</B><a name="CUL_HMassignHmKey"></a><br>
  9320. Initiates a key-exchange with the device, exchanging the old AES-key of the device with the key with the highest
  9321. index defined by the attribute hmKey* in the HMLAN or VCCU. The old key is determined by the reading aesKeyNbr,
  9322. which specifies the index of the old key when the reading is divided by 2.
  9323. </li>
  9324. <li><B>clear &lt;[rssi|readings|register|msgEvents|attack|all]&gt;</B><a name="CUL_HMclear"></a><br>
  9325. A set of variables can be removed.<br>
  9326. <ul>
  9327. readings: all readings will be deleted. Any new reading will be added usual. May be used to eliminate old data<br>
  9328. register: all captured register-readings in FHEM will be removed. This has NO impact to the values in the device.<br>
  9329. msgEvents: all message event counter will be removed. Also commandstack will be cleared. <br>
  9330. rssi: collected rssi values will be cleared. <br>
  9331. attack: information regarding an attack will be removed. <br>
  9332. all: all of the above. <br>
  9333. </ul>
  9334. </li>
  9335. <li><B>getConfig</B><a name="CUL_HMgetConfig"></a><br>
  9336. Will read major configuration items stored in the HM device. Executed
  9337. on a channel it will read pair Inforamtion, List0, List1 and List3 of
  9338. the 1st internal peer. Furthermore the peerlist will be retrieved for
  9339. teh given channel. If executed on a device the command will get the
  9340. above info or all assotated channels. Not included will be the
  9341. configuration for additional peers. <br> The command is a shortcut
  9342. for a selection of other commands.
  9343. </li>
  9344. <li><B>getRegRaw [List0|List1|List2|List3|List4|List5|List6]&lt;peerChannel&gt; </B><a name="CUL_HMgetRegRaw"></a><br>
  9345. Read registerset in raw format. Description of the registers is beyond
  9346. the scope of this documentation.<br>
  9347. Registers are structured in so called lists each containing a set of
  9348. registers.<br>
  9349. List0: device-level settings e.g. CUL-pairing or dimmer thermal limit
  9350. settings.<br>
  9351. List1: per channel settings e.g. time to drive the blind up and
  9352. down.<br>
  9353. List3: per 'link' settings - means per peer-channel. This is a lot of
  9354. data!. It controlls actions taken upon receive of a trigger from the
  9355. peer.<br>
  9356. List4: settings for channel (button) of a remote<br><br>
  9357. &lt;PeerChannel&gt; paired HMid+ch, i.e. 4 byte (8 digit) value like
  9358. '12345601'. It is mendatory for List 3 and 4 and can be left out for
  9359. List 0 and 1. <br>
  9360. 'all' can be used to get data of each paired link of the channel. <br>
  9361. 'selfxx' can be used to address data for internal channels (associated
  9362. with the build-in switches if any). xx is the number of the channel in
  9363. decimal.<br>
  9364. Note1: execution depends on the entity. If List1 is requested on a
  9365. device rather then a channel the command will retrieve List1 for all
  9366. channels assotiated. List3 with peerChannel = all will get all link
  9367. for all channel if executed on a device.<br>
  9368. Note2: for 'sender' see <a href="#CUL_HMremote">remote</a> <br>
  9369. Note3: the information retrieval may take a while - especially for
  9370. devices with a lot of channels and links. It may be necessary to
  9371. refresh the web interface manually to view the results <br>
  9372. Note4: the direct buttons on a HM device are hidden by default.
  9373. Nevertheless those are implemented as links as well. To get access to
  9374. the 'internal links' it is necessary to issue <br>
  9375. 'set &lt;name&gt; <a href="#CUL_HMregSet">regSet</a> intKeyVisib visib'<br>
  9376. or<br>
  9377. 'set &lt;name&gt; <a href="#CUL_HMregBulk">regBulk</a> RegL_0. 2:81'<br>
  9378. Reset it by replacing '81' with '01'<br> example:<br>
  9379. <ul><code>
  9380. set mydimmer getRegRaw List1<br>
  9381. set mydimmer getRegRaw List3 all <br>
  9382. </code></ul>
  9383. </li>
  9384. <li><B>getSerial</B><a name="CUL_HMgetSerial"></a><br>
  9385. Read serial number from device and write it to attribute serialNr.
  9386. </li>
  9387. <li><B>inhibit [on|off]</B><br>
  9388. Block / unblock all changes to the actor channel, i.e. actor state is frozen
  9389. until inhibit is set off again. Inhibit can be executed on any actor channel
  9390. but obviously not on sensors - would not make any sense.<br>
  9391. Practically it can be used to suspend any notifies as well as peered channel action
  9392. temporarily without the need to delete them. <br>
  9393. Examples:
  9394. <ul><code>
  9395. # Block operation<br>
  9396. set keymatic inhibit on <br><br>
  9397. </ul></code>
  9398. </li>
  9399. <li><B>pair</B><a name="CUL_HMpair"></a><br>
  9400. Pair the device with a known serialNumber (e.g. after a device reset)
  9401. to FHEM Central unit. FHEM Central is usualy represented by CUL/CUNO,
  9402. HMLAN,...
  9403. If paired, devices will report status information to
  9404. FHEM. If not paired, the device won't respond to some requests, and
  9405. certain status information is also not reported. Paring is on device
  9406. level. Channels cannot be paired to central separate from the device.
  9407. See also <a href="#CUL_HMgetpair">getPair</a> and
  9408. <a href="#CUL_HMunpair">unpair</a>.<br>
  9409. Don't confuse pair (to a central) with peer (channel to channel) with
  9410. <a href="#CUL_HMpeerChan">peerChan</a>.<br>
  9411. </li>
  9412. <li><B>peerBulk</B> &lt;peerch1,peerch2,...&gt; [set|unset]<a name="CUL_HMpeerBulk"></a><br>
  9413. peerBulk will add peer channels to the channel. All peers in the list will be added. <br>
  9414. with unset option the peers in the list will be subtracted from the device's peerList.<br>
  9415. peering sets the configuration of this link to its defaults. As peers are not
  9416. added in pairs default will be as defined for 'single' by HM for this device. <br>
  9417. More suffisticated funktionality is provided by
  9418. <a href="#CUL_HMpeerChan">peerChan</a>.<br>
  9419. peerBulk will not delete existing peers, just handle the given peerlist.
  9420. Other already installed peers will not be touched.<br>
  9421. peerBulk may be used to remove peers using <B>unset</B> option while default ist set.<br>
  9422. Main purpose of this command is to re-store data to a device.
  9423. It is recommended to restore register configuration utilising
  9424. <a href="#CUL_HMregBulk">regBulk</a> subsequent. <br>
  9425. Example:<br>
  9426. <ul><code>
  9427. set myChannel peerBulk 12345601,<br>
  9428. set myChannel peerBulk self01,self02,FB_Btn_04,FB_Btn_03,<br>
  9429. set myChannel peerBulk 12345601 unset # remove peer 123456 channel 01<br>
  9430. </code></ul>
  9431. </li>
  9432. <li><B>regBulk &lt;reg List&gt;.&lt;peer&gt; &lt;addr1:data1&gt; &lt;addr2:data2&gt;...</B><a name="CUL_HMregBulk"></a><br>
  9433. This command will replace the former regRaw. It allows to set register
  9434. in raw format. Its main purpose is to restore a complete register list
  9435. to values secured before. <br>
  9436. Values may be read by <a href="#CUL_HMgetConfig">getConfig</a>. The
  9437. resulting readings can be used directly for this command.<br>
  9438. &lt;reg List&gt; is the list data should be written to. Format could be
  9439. '00', 'RegL_00', '01'...<br>
  9440. &lt;peer&gt; is an optional adder in case the list requires a peer.
  9441. The peer can be given as channel name or the 4 byte (8 chars) HM
  9442. channel ID.<br>
  9443. &lt;addr1:data1&gt; is the list of register to be written in hex
  9444. format.<br>
  9445. Example:<br>
  9446. <ul><code>
  9447. set myChannel regBulk RegL_00. 02:01 0A:17 0B:43 0C:BF 15:FF 00:00<br>
  9448. RegL_03.FB_Btn_07
  9449. 01:00 02:00 03:00 04:32 05:64 06:00 07:FF 08:00 09:FF 0A:01 0B:44 0C:54 0D:93 0E:00 0F:00 11:C8 12:00 13:00 14:00 15:00 16:00 17:00 18:00 19:00 1A:00 1B:00 1C:00 1D:FF 1E:93 1F:00 81:00 82:00 83:00 84:32 85:64 86:00 87:FF 88:00 89:FF 8A:21 8B:44 8C:54 8D:93 8E:00 8F:00 91:C8 92:00 93:00 94:00 95:00 96:00 97:00 98:00 99:00 9A:00 9B:00 9C:00 9D:05 9E:93 9F:00 00:00<br>
  9450. set myblind regBulk 01 0B:10<br>
  9451. set myblind regBulk 01 0C:00<br>
  9452. </code></ul>
  9453. myblind will set the max drive time up for a blind actor to 25,6sec
  9454. </li>
  9455. <li><B>regSet [prep|exec] &lt;regName&gt; &lt;value&gt; &lt;peerChannel&gt;</B><a name="CUL_HMregSet"></a><br>
  9456. For some major register a readable version is implemented supporting
  9457. register names &lt;regName&gt; and value conversionsing. Only a subset
  9458. of register can be supproted.<br>
  9459. Optional parameter [prep|exec] allowes to pack the messages and therefore greatly
  9460. improve data transmission.
  9461. Usage is to send the commands with paramenter "prep". The data will be accumulated for send.
  9462. The last command must have the parameter "exec" in order to transmitt the information.<br>
  9463. &lt;value&gt; is the data in human readable manner that will be written
  9464. to the register.<br>
  9465. &lt;peerChannel&gt; is required if this register is defined on a per
  9466. 'peerChan' base. It can be set to '0' other wise.See <a
  9467. href="#CUL_HMgetRegRaw">getRegRaw</a> for full description<br>
  9468. Supported register for a device can be explored using<br>
  9469. <ul><code>set regSet ? 0 0</code></ul>
  9470. Condensed register description will be printed
  9471. using<br>
  9472. <ul><code>set regSet &lt;regname&gt; ? 0</code></ul>
  9473. </li>
  9474. <li><B>reset</B><a name="CUL_HMreset"></a><br>
  9475. Factory reset the device. You need to pair it again to use it with
  9476. fhem.
  9477. </li>
  9478. <li><B>sign [on|off]</B><a name="CUL_HMsign"></a><br>
  9479. Activate or deactivate signing (also called AES encryption, see the <a
  9480. href="#HMAES">note</a> above). Warning: if the device is attached via
  9481. a CUL, you need to install the perl-module Crypt::Rijndael to be
  9482. able to switch it (or deactivate signing) from fhem.
  9483. </li>
  9484. <li><B>statusRequest</B><a name="CUL_HMstatusRequest"></a><br>
  9485. Update device status. For multichannel devices it should be issued on
  9486. an per channel base
  9487. </li>
  9488. <li><B>unpair</B><a name="CUL_HMunpair"></a><br>
  9489. "Unpair" the device, i.e. make it available to pair with other master
  9490. devices. See <a href="#CUL_HMpair">pair</a> for description.</li>
  9491. <li><B>virtual &lt;number of buttons&gt;</B><a name="CUL_HMvirtual"></a><br>
  9492. configures a defined curcuit as virtual remote controll. Then number
  9493. of button being added is 1 to 255. If the command is issued a second
  9494. time for the same entity additional buttons will be added. <br>
  9495. Example for usage:
  9496. <ul><code>
  9497. define vRemote CUL_HM 100000 # the selected HMid must not be in use<br>
  9498. set vRemote virtual 20 # define 20 button remote controll<br>
  9499. set vRemote_Btn4 peerChan 0 &lt;actorchannel&gt; # peers Button 4 and 5 to the given channel<br>
  9500. set vRemote_Btn4 press<br>
  9501. set vRemote_Btn5 press long<br>
  9502. </code></ul>
  9503. see also <a href="#CUL_HMpress">press</a>
  9504. </li>
  9505. <li><B>deviceRename &lt;newName&gt;</B><a name="CUL_HMdeviceRename"></a><br>
  9506. rename the device and all its channels.
  9507. </li>
  9508. <li><B>fwUpdate [onlyEnterBootLoader] &lt;filename&gt; [&lt;waitTime&gt;]</B><br>
  9509. update Fw of the device. User must provide the appropriate file.
  9510. waitTime can be given optionally. In case the device needs to be set to
  9511. FW update mode manually this is the time the system will wait.<br>
  9512. "onlyEnterBootLoader" tells the device to enter the boot loader so it can be
  9513. flashed using the eq3 firmware update tool. Mainly useful for flush-mounted devices
  9514. in FHEM environments solely using HM-LAN adapters.
  9515. </li>
  9516. </ul>
  9517. <br>
  9518. <B>subType dependent commands:</B>
  9519. <ul>
  9520. <br>
  9521. <li>switch
  9522. <ul>
  9523. <li><B>on</B> <a name="CUL_HMon"> </a> - set level to 100%</li>
  9524. <li><B>off</B><a name="CUL_HMoff"></a> - set level to 0%</li>
  9525. <li><B>on-for-timer &lt;sec&gt;</B><a name="CUL_HMonForTimer"></a> -
  9526. set the switch on for the given seconds [0-85825945].<br> Note:
  9527. off-for-timer like FS20 is not supported. It may to be programmed
  9528. thru channel register.</li>
  9529. <li><B>on-till &lt;time&gt;</B><a name="CUL_HMonTill"></a> - set the switch on for the given end time.<br>
  9530. <ul><code>set &lt;name&gt; on-till 20:32:10<br></code></ul>
  9531. Currently a max of 24h is supported with endtime.<br>
  9532. </li>
  9533. <li><B>pressL &lt;peer&gt; [&lt;repCount&gt;] [&lt;repDelay&gt;] </B><a name="CUL_HMpressL"></a><br>
  9534. simulate a press of the local button or direct connected switch of the actor.<br>
  9535. <B>&lt;peer&gt;</B> allows to stimulate button-press of any peer of the actor.
  9536. i.e. if the actor is peered to any remote, virtual or io (HMLAN/CUL)
  9537. press can trigger the action defined. <br>
  9538. <B>&lt;repCount&gt;</B> number of automatic repetitions.<br>
  9539. <B>&lt;repDelay&gt;</B> timer between automatic repetitions. <br>
  9540. <B>Example:</B>
  9541. <code>
  9542. set actor pressL FB_Btn01 # trigger long peer FB button 01<br>
  9543. set actor pressL FB_chn-8 # trigger long peer FB button 08<br>
  9544. set actor pressL self01 # trigger short of internal peer 01<br>
  9545. set actor pressL fhem02 # trigger short of FHEM channel 2<br>
  9546. </code>
  9547. </li>
  9548. <li><B>pressS &lt;peer&gt;</B><a name="CUL_HMpressS"></a><br>
  9549. simulates a short press similar to long press
  9550. </li>
  9551. <li><B>eventL &lt;peer&gt; &lt;condition&gt; [&lt;repCount&gt;] [&lt;repDelay&gt;] </B><a name="CUL_HMeventL"></a><br>
  9552. simulate an event of an peer and stimulates the actor.<br>
  9553. <B>&lt;peer&gt;</B> allows to stimulate button-press of any peer of the actor.
  9554. i.e. if the actor is peered to any remote, virtual or io (HMLAN/CUL)
  9555. press can trigger the action defined. <br>
  9556. <B>&lt;codition&gt;</B> the level of the condition <br>
  9557. <B>Example:</B>
  9558. <code>
  9559. set actor eventL md 30 # trigger from motion detector with level 30<br>
  9560. </code>
  9561. </li>
  9562. <li><B>eventS &lt;peer&gt; &lt;condition&gt; </B><a name="CUL_HMeventS"></a><br>
  9563. simulates a short event from a peer of the actor. Typically sensor do not send long events.
  9564. </li>
  9565. <li><B>toggle</B><a name="CUL_HMtoggle"></a> - toggle the Actor. It will switch from any current
  9566. level to off or from off to 100%</li>
  9567. </ul>
  9568. <br>
  9569. </li>
  9570. <li>dimmer, blindActuator<br>
  9571. Dimmer may support virtual channels. Those are autocrated if applicable. Usually there are 2 virtual channels
  9572. in addition to the primary channel. Virtual dimmer channels are inactive by default but can be used in
  9573. in parallel to the primay channel to control light. <br>
  9574. Virtual channels have default naming SW&lt;channel&gt;_V&lt;no&gt;. e.g. Dimmer_SW1_V1 and Dimmer_SW1_V2.<br>
  9575. Dimmer virtual channels are completely different from FHEM virtual buttons and actors but
  9576. are part of the HM device. Documentation and capabilities for virtual channels is out of scope.<br>
  9577. <ul>
  9578. <li><B>0 - 100 [on-time] [ramp-time]</B><br>
  9579. set the actuator to the given value (in percent)
  9580. with a resolution of 0.5.<br>
  9581. Optional for dimmer on-time and ramp time can be choosen, both in seconds with 0.1s granularity.<br>
  9582. On-time is analog "on-for-timer".<br>
  9583. Ramp-time default is 2.5s, 0 means instantanous<br>
  9584. </li>
  9585. <li><B><a href="#CUL_HMon">on</a></B></li>
  9586. <li><B><a href="#CUL_HMoff">off</a></B></li>
  9587. <li><B><a href="#CUL_HMpress">press &lt;[short|long]&gt;&lt;[on|off]&gt;</a></B></li>
  9588. <li><B><a href="#CUL_HMtoggle">toggle</a></B></li>
  9589. <li><B>toggleDir</B><a name="CUL_HMtoggleDir"></a> - toggled drive direction between up/stop/down/stop</li>
  9590. <li><B><a href="#CUL_HMonForTimer">on-for-timer &lt;sec&gt;</a></B> - Dimmer only! <br></li>
  9591. <li><B><a href="#CUL_HMonTill">on-till &lt;time&gt;</a></B> - Dimmer only! <br></li>
  9592. <li><B>stop</B> - stop motion (blind) or dim ramp</li>
  9593. <li><B>old</B> - switch back to old value after a change. Dimmer only.</li>
  9594. <li><B>pct &lt;level&gt [&lt;ontime&gt] [&lt;ramptime&gt]</B> - set actor to a desired <B>absolut level</B>.<br>
  9595. Optional ontime and ramptime could be given for dimmer.<br>
  9596. ontime may be time in seconds. It may also be entered as end-time in format hh:mm:ss
  9597. </li>
  9598. <li><B>up [changeValue] [&lt;ontime&gt] [&lt;ramptime&gt]</B> dim up one step</li>
  9599. <li><B>down [changeValue] [&lt;ontime&gt] [&lt;ramptime&gt]</B> dim up one step<br>
  9600. changeValue is optional an gives the level to be changed up or down in percent. Granularity is 0.5%, default is 10%. <br>
  9601. ontime is optional an gives the duration of the level to be kept. '0' means forever and is default.<br>
  9602. ramptime is optional an defines the change speed to reach the new level. It is meaningful only for dimmer.
  9603. <br></li>
  9604. </ul>
  9605. <br>
  9606. </li>
  9607. <li>remotes, pushButton<a name="CUL_HMremote"></a><br>
  9608. This class of devices does not react on requests unless they are put
  9609. to learn mode. FHEM obeys this behavior by stacking all requests until
  9610. learn mode is detected. Manual interaction of the user is necessary to
  9611. activate learn mode. Whether commands are pending is reported on
  9612. device level with parameter 'protCmdPend'.
  9613. </li>
  9614. <ul>
  9615. <li><B>trgEventS [all|&lt;peer&gt;] &lt;condition&gt;</B><a name="CUL_HMtrgEventS"></a><br>
  9616. Issue eventS on the peer entity. If <B>all</B> is selected each of the peers will be triggered. See also <a href="CUL_HMeventS">eventS</a><br>
  9617. <B>&lt;condition&gt;</B>: is the condition being transmitted with the event. E.g. the brightness in case of a motion detector.
  9618. </li>
  9619. <li><B>trgEventL [all|&lt;peer&gt;] &lt;condition&gt;</B><a name="CUL_HMtrgEventL"></a><br>
  9620. Issue eventL on the peer entity. If <B>all</B> is selected each of the peers will be triggered. a normal device will not sent event long. See also <a href="CUL_HMeventL">eventL</a><br>
  9621. <B>&lt;condition&gt;</B>: is the condition being transmitted with the event. E.g. the brightness in case of a motion detector.
  9622. </li>
  9623. <li><B>trgPressS [all|&lt;peer&gt;] </B><a name="CUL_HMtrgPressS"></a><br>
  9624. Issue pressS on the peer entity. If <B>all</B> is selected each of the peers will be triggered. See also <a href="CUL_HMpressS">pressS</a><br>
  9625. </li>
  9626. <li><B>trgPressL [all|&lt;peer&gt;] </B><a name="CUL_HMtrgPressL"></a><br>
  9627. Issue pressL on the peer entity. If <B>all</B> is selected each of the peers will be triggered. See also <a href="CUL_HMpressL">pressL</a><br>
  9628. </li>
  9629. <li><B>peerIODev [IO] &lt;btn_no&gt; [<u>set</u>|unset]</B><a name="CUL_HMpeerIODev"></a><br>
  9630. The command is similar to <B><a href="#CUL_HMpeerChan">peerChan</a></B>.
  9631. While peerChan
  9632. is executed on a remote and peers any remote to any actor channel peerIODev is
  9633. executed on an actor channel and peer this to an channel of an FHEM IO device.<br>
  9634. An IO device according to eQ3 supports up to 50 virtual buttons. Those
  9635. will be peered/unpeerd to the actor. <a href="CUL_HMpress">press</a> can be
  9636. used to stimulate the related actions as defined in the actor register.
  9637. </li>
  9638. <li><B>peerChan &lt;btn_no&gt; &lt;actChan&gt; [single|<u>dual</u>|reverse][<u>set</u>|unset] [<u>both</u>|actor|remote]</B>
  9639. <a name="CUL_HMpeerChan"></a><br>
  9640. peerChan will establish a connection between a sender- <B>channel</B> and
  9641. an actuator-<B>channel</B> called link in HM nomenclatur. Peering must not be
  9642. confused with pairing.<br>
  9643. <B>Pairing</B> refers to assign a <B>device</B> to the central.<br>
  9644. <B>Peering</B> refers to virtally connect two <B>channels</B>.<br>
  9645. Peering allowes direkt interaction between sender and aktor without
  9646. the necessity of a CCU<br>
  9647. Peering a sender-channel causes the sender to expect an ack from -each-
  9648. of its peers after sending a trigger. It will give positive feedback (e.g. LED green)
  9649. only if all peers acknowledged.<br>
  9650. Peering an aktor-channel will setup a parameter set which defines the action to be
  9651. taken once a trigger from -this- peer arrived. In other words an aktor will <br>
  9652. - process trigger from peers only<br>
  9653. - define the action to be taken dedicated for each peer's trigger<br>
  9654. An actor channel will setup a default action upon peering - which is actor dependant.
  9655. It may also depend whether one or 2 buttons are peered <B>in one command</B>.
  9656. A swich may setup oen button for 'on' and the other for 'off' if 2 button are
  9657. peered. If only one button is peered the funktion will likely be 'toggle'.<br>
  9658. The funtion can be modified by programming the register (aktor dependant).<br>
  9659. Even though the command is executed on a remote or push-button it will
  9660. as well take effect on the actuator directly. Both sides' peering is
  9661. virtually independant and has different impact on sender and receiver
  9662. side.<br>
  9663. Peering of one actuator-channel to multiple sender-channel as
  9664. well as one sender-channel to multiple Actuator-channel is
  9665. possible.<br>
  9666. &lt;actChan&gt; is the actuator-channel to be peered.<br>
  9667. &lt;btn_no&gt; is the sender-channel (button) to be peered. If
  9668. 'single' is choosen buttons are counted from 1. For 'dual' btn_no is
  9669. the number of the Button-pair to be used. I.e. '3' in dual is the
  9670. 3rd button pair correcponding to button 5 and 6 in single mode.<br>
  9671. If the command is executed on a channel the btn_no is ignored.
  9672. It needs to be set, should be 0<br>
  9673. [single|dual]: this mode impacts the default behavior of the
  9674. Actuator upon using this button. E.g. a dimmer can be learned to a
  9675. single button or to a button pair. <br>
  9676. Defaults to dual.<br>
  9677. 'dual' (default) Button pairs two buttons to one actuator. With a
  9678. dimmer this means one button for dim-up and one for dim-down. <br>
  9679. 'reverse' identical to dual - but button order is reverse.<br>
  9680. 'single' uses only one button of the sender. It is useful for e.g. for
  9681. simple switch actuator to toggle on/off. Nevertheless also dimmer can
  9682. be learned to only one button. <br>
  9683. [set|unset]: selects either enter a peering or remove it.<br>
  9684. Defaults to set.<br>
  9685. 'set' will setup peering for the channels<br>
  9686. 'unset' will remove the peering for the channels<br>
  9687. [actor|remote|both] limits the execution to only actor or only remote.
  9688. This gives the user the option to redo the peering on the remote
  9689. channel while the settings in the actor will not be removed.<br>
  9690. Defaults to both.<br>
  9691. Example:
  9692. <ul><code>
  9693. set myRemote peerChan 2 mySwActChn single set #peer second button to an actuator channel<br>
  9694. set myRmtBtn peerChan 0 mySwActChn single set #myRmtBtn is a button of the remote. '0' is not processed here<br>
  9695. set myRemote peerChan 2 mySwActChn dual set #peer button 3 and 4<br>
  9696. set myRemote peerChan 3 mySwActChn dual unset #remove peering for button 5 and 6<br>
  9697. set myRemote peerChan 3 mySwActChn dual unset aktor #remove peering for button 5 and 6 in actor only<br>
  9698. set myRemote peerChan 3 mySwActChn dual set remote #peer button 5 and 6 on remote only. Link settings il mySwActChn will be maintained<br>
  9699. </code></ul>
  9700. </li>
  9701. </ul>
  9702. <li>virtual<a name="CUL_HMvirtual"></a><br>
  9703. <ul>
  9704. <li><B><a href="#CUL_HMpeerChan">peerChan</a></B> see remote</li>
  9705. <li><B><a name="CUL_HMpress"></a>press [long|short] [&lt;peer&gt;] [&lt;repCount&gt;] [&lt;repDelay&gt;] </B>
  9706. <ul>
  9707. simulates button press for an actor from a peered sensor.
  9708. will be sent of type "long".
  9709. <li>[long|short] defines whether long or short press shall be simulated. Defaults to short</li>
  9710. <li>[&lt;peer&gt;] define which peer's trigger shall be simulated.Defaults to self(channelNo).</li>
  9711. <li>[&lt;repCount&gt;] Valid for long press only. How long shall the button be pressed? Number of repetition of the messages is defined. Defaults to 1</li>
  9712. <li>[&lt;repDelay&gt;] Valid for long press only. defines wait time between the single messages. </li>
  9713. </ul>
  9714. </li>
  9715. <li><B>virtTemp &lt;[off -10..50]&gt;<a name="CUL_HMvirtTemp"></a></B>
  9716. simulates a thermostat. If peered to a device it periodically sends the
  9717. temperature until "off" is given. See also <a href="#CUL_HMvirtHum">virtHum</a><br>
  9718. </li>
  9719. <li><B>virtHum &lt;[off -10..50]&gt;<a name="CUL_HMvirtHum"></a></B>
  9720. simulates the humidity part of a thermostat. If peered to a device it periodically sends
  9721. the temperature and humidity until both are "off". See also <a href="#CUL_HMvirtTemp">virtTemp</a><br>
  9722. </li>
  9723. <li><B>valvePos &lt;[off 0..100]&gt;<a name="CUL_HMvalvePos"></a></B>
  9724. stimulates a VD<br>
  9725. </li>
  9726. </ul>
  9727. </li>
  9728. <li>smokeDetector<br>
  9729. Note: All these commands work right now only if you have more then one
  9730. smoekDetector, and you peered them to form a group. For issuing the
  9731. commands you have to use the master of this group, and currently you
  9732. have to guess which of the detectors is the master.<br>
  9733. smokeDetector can be setup to teams using
  9734. <a href="#CUL_HMpeerChan">peerChan</a>. You need to peer all
  9735. team-members to the master. Don't forget to also peerChan the master
  9736. itself to the team - i.e. peer it to itself! doing that you have full
  9737. controll over the team and don't need to guess.<br>
  9738. <ul>
  9739. <li><B>teamCall</B> - execute a network test to all team members</li>
  9740. <li><B>teamCallBat</B> - execute a network test simulate bat low</li>
  9741. <li><B>alarmOn</B> - initiate an alarm</li>
  9742. <li><B>alarmOff</B> - switch off the alarm</li>
  9743. </ul>
  9744. </li>
  9745. <li>4Dis (HM-PB-4DIS-WM|HM-RC-Dis-H-x-EU|ROTO_ZEL-STG-RM-DWT-10)
  9746. <ul>
  9747. <li><B>text &lt;btn_no&gt; [on|off] &lt;text1&gt; &lt;text2&gt;</B><br>
  9748. Set the text on the display of the device. To this purpose issue
  9749. this set command first (or a number of them), and then choose from
  9750. the teach-in menu of the 4Dis the "Central" to transmit the data.<br>
  9751. If used on a channel btn_no and on|off must not be given but only pure text.<br>
  9752. \_ will be replaced by blank character.<br>
  9753. Example:
  9754. <ul><code>
  9755. set 4Dis text 1 on On Lamp<br>
  9756. set 4Dis text 1 off Kitchen Off<br>
  9757. <br>
  9758. set 4Dis_chn4 text Kitchen Off<br>
  9759. </code></ul>
  9760. </li>
  9761. </ul>
  9762. <br></li>
  9763. <li>Climate-Control (HM-CC-TC)
  9764. <ul>
  9765. <li><B>desired-temp &lt;temp&gt;</B><br>
  9766. Set different temperatures. &lt;temp&gt; must be between 6 and 30
  9767. Celsius, and precision is half a degree.</li>
  9768. <li><B>tempListSat [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  9769. <li><B>tempListSun [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  9770. <li><B>tempListMon [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  9771. <li><B>tempListTue [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  9772. <li><B>tempListThu [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  9773. <li><B>tempListWed [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  9774. <li><B>tempListFri [prep|exec] HH:MM temp ... 24:00 temp</B><br>
  9775. Specify a list of temperature intervals. Up to 24 intervals can be
  9776. specified for each week day, the resolution is 10 Minutes. The
  9777. last time spec must always be 24:00.<br>
  9778. Example: until 6:00 temperature shall be 19, from then until 23:00 temperature shall be
  9779. 22.5, thereafter until midnight, 19 degrees celsius is desired.<br>
  9780. <code> set th tempListSat 06:00 19 23:00 22.5 24:00 19<br></code>
  9781. </li>
  9782. <br>
  9783. <li><B>tempListTmpl =>"[verify|restore] [[ &lt;file&gt; :]templateName] ...</B><br>
  9784. The tempList for one or more devices can be stored in a file. User can compare the
  9785. tempList in the file with the data read from the device. <br>
  9786. Restore will write the tempList to the device.<br>
  9787. Default opeartion is verify.<br>
  9788. Default file is tempList.cfg.<br>
  9789. Default templateName is the name of the actor<br>
  9790. Default for file and templateName can be set with attribut <B>tempListTmpl</B><br>
  9791. Example for templist file. room1 and room2 are the names of the template: <br>
  9792. <code>entities:room1
  9793. tempListSat>08:00 16.0 15:00 18.0 21:30 19.0 24:00 14.0
  9794. tempListSun>08:00 16.0 15:00 18.0 21:30 19.0 24:00 14.0
  9795. tempListMon>07:00 16.0 16:00 18.0 21:00 19.0 24:00 14.0
  9796. tempListTue>07:00 16.0 13:00 16.0 16:00 18.0 21:00 19.0 24:00 15.0
  9797. tempListWed>07:00 16.0 16:00 18.0 21:00 19.0 24:00 14.0
  9798. tempListThu>07:00 16.0 16:00 18.0 21:00 19.0 24:00 14.0
  9799. tempListFri>07:00 16.0 13:00 16.0 16:00 18.0 21:00 19.0 24:00 14.0
  9800. entities:room2
  9801. tempListSat>08:00 14.0 15:00 18.0 21:30 19.0 24:00 14.0
  9802. tempListSun>08:00 14.0 15:00 18.0 21:30 19.0 24:00 14.0
  9803. tempListMon>07:00 14.0 16:00 18.0 21:00 19.0 24:00 14.0
  9804. tempListTue>07:00 14.0 13:00 16.0 16:00 18.0 21:00 19.0 24:00 15.0
  9805. tempListWed>07:00 14.0 16:00 18.0 21:00 19.0 24:00 14.0
  9806. tempListThu>07:00 14.0 16:00 18.0 21:00 19.0 24:00 14.0
  9807. tempListFri>07:00 14.0 13:00 16.0 16:00 18.0 21:00 19.0 24:00 14.0
  9808. </code>
  9809. Specials:<br>
  9810. <li>none: template will be ignored</li>
  9811. <li>defaultWeekplan: as default each day is set to 18.0 degree.
  9812. useful if peered to a TC controller. Implicitely teh weekplan of TC will be used.</li>
  9813. </li>
  9814. <li><B>tempTmplSet =>"[[ &lt;file&gt; :]templateName]</B><br>
  9815. Set the attribut and apply the change to the device
  9816. </li>
  9817. <li><B>templateDel =>" &lt;template&gt; </B><br>
  9818. Delete templateentry for this entity
  9819. </li>
  9820. <li><B>partyMode &lt;HH:MM&gt;&lt;durationDays&gt;</B><br>
  9821. set control mode to party and device ending time. Add the time it ends
  9822. and the <b>number of days</b> it shall last. If it shall end next day '1'
  9823. must be entered<br></li>
  9824. <li><B>sysTime</B><br>
  9825. set time in climate channel to system time</li>
  9826. </ul><br>
  9827. </li>
  9828. <li>Climate-Control (HM-CC-RT-DN|HM-CC-RT-DN-BoM)
  9829. <ul>
  9830. <li><B>controlMode &lt;auto|boost|day|night&gt;</B><br></li>
  9831. <li><B>controlManu &lt;temp&gt;</B><br></li>
  9832. <li><B>controlParty &lt;temp&gt;&lt;startDate&gt;&lt;startTime&gt;&lt;endDate&gt;&lt;endTime&gt;</B><br>
  9833. set control mode to party, define temp and timeframe.<br>
  9834. example:<br>
  9835. <code>set controlParty 15 03-8-13 20:30 5-8-13 11:30</code></li>
  9836. <li><B>sysTime</B><br>
  9837. set time in climate channel to system time</li>
  9838. <li><B>desired-temp &lt;temp&gt;</B><br>
  9839. Set different temperatures. &lt;temp&gt; must be between 6 and 30
  9840. Celsius, and precision is half a degree.</li>
  9841. <li><B>tempListSat [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  9842. <li><B>tempListSun [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  9843. <li><B>tempListMon [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  9844. <li><B>tempListTue [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  9845. <li><B>tempListThu [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  9846. <li><B>tempListWed [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  9847. <li><B>tempListFri [prep|exec] HH:MM temp ... 24:00 temp</B><br>
  9848. Specify a list of temperature intervals. Up to 24 intervals can be
  9849. specified for each week day, the resolution is 10 Minutes. The
  9850. last time spec must always be 24:00.<br>
  9851. Optional parameter [prep|exec] allowes to pack the messages and therefore greatly
  9852. improve data transmission. This is especially helpful if device is operated in wakeup mode.
  9853. Usage is to send the commands with paramenter "prep". The data will be accumulated for send.
  9854. The last command must have the parameter "exec" in order to transmitt the information.<br>
  9855. Example: until 6:00 temperature shall be 19, from then until 23:00 temperature shall be
  9856. 22.5, thereafter until midnight, 19 degrees celsius is desired.<br>
  9857. <code> set th tempListSat 06:00 19 23:00 22.5 24:00 19<br></code>
  9858. <br>
  9859. <code> set th tempListSat prep 06:00 19 23:00 22.5 24:00 19<br>
  9860. set th tempListSun prep 06:00 19 23:00 22.5 24:00 19<br>
  9861. set th tempListMon prep 06:00 19 23:00 22.5 24:00 19<br>
  9862. set th tempListTue exec 06:00 19 23:00 22.5 24:00 19<br></code>
  9863. </li>
  9864. </ul><br>
  9865. </li>
  9866. <li>OutputUnit (HM-OU-LED16)
  9867. <ul>
  9868. <li><B>led [off|red|green|yellow]</B><br>
  9869. switches the LED of the channel to the color. If the command is
  9870. executed on a device it will set all LEDs to the specified
  9871. color.<br>
  9872. For Expert all LEDs can be set individual by providing a 8-digit hex number to the device.<br></li>
  9873. <li><B>ilum &lt;brightness&gt;&lt;duration&gt; </B><br>
  9874. &lt;brightness&gt; [0-15] of backlight.<br>
  9875. &lt;duration&gt; [0-127] in sec. 0 is permanent 'on'.<br></li>
  9876. </ul><br>
  9877. </li>
  9878. <li>OutputUnit (HM-OU-CFM-PL)
  9879. <ul>
  9880. <li><B>led &lt;color&gt;[,&lt;color&gt;..] [&lt;repeat&gt..]</B><br>
  9881. Possible colors are [redL|greenL|yellowL|redS|greenS|yellowS|pause]. A
  9882. sequence of colors can be given separating the color entries by ','.
  9883. White spaces must not be used in the list. 'S' indicates short and
  9884. 'L' long ilumination. <br>
  9885. <b>repeat</b> defines how often the sequence shall be executed. Defaults to 1.<br>
  9886. </li>
  9887. <li><B>playTone &lt;MP3No&gt[,&lt;MP3No&gt..] [&lt;repeat&gt;] [&lt;volume&gt;]</B><br>
  9888. Play a series of tones. List is to be entered separated by ','. White
  9889. spaces must not be used in the list.<br>
  9890. <b>replay</b> can be entered to repeat the last sound played once more.<br>
  9891. <b>repeat</b> defines how often the sequence shall be played. Defaults to 1.<br>
  9892. <b>volume</b> is defined between 0 and 10. 0 stops any sound currently playing. Defaults to 10 (100%).<br>
  9893. Example:
  9894. <ul><code>
  9895. # "hello" in display, symb bulb on, backlight, beep<br>
  9896. set cfm_Mp3 playTone 3 # MP3 title 3 once<br>
  9897. set cfm_Mp3 playTone 3 3 # MP3 title 3 3 times<br>
  9898. set cfm_Mp3 playTone 3,6,8,3,4 # MP3 title list 3,6,8,3,4 once<br>
  9899. set cfm_Mp3 playTone 3,6,8,3,4 255# MP3 title list 3,6,8,3,4 255 times<br>
  9900. set cfm_Mp3 playTone replay # repeat last sequence<br>
  9901. <br>
  9902. set cfm_Led led redL 4 # led red blink 3 times long<br>
  9903. set cfm_Led led redS,redS,redS,redL,redL,redL,redS,redS,redS 255 # SOS 255 times<br>
  9904. </ul></code>
  9905. </li>
  9906. </ul><br>
  9907. </li>
  9908. <li>HM-RC-19xxx
  9909. <ul>
  9910. <li><B>alarm &lt;count&gt;</B><br>
  9911. issue an alarm message to the remote<br></li>
  9912. <li><B>service &lt;count&gt;</B><br>
  9913. issue an service message to the remote<br></li>
  9914. <li><B>symbol &lt;symbol&gt; [set|unset]</B><br>
  9915. activate a symbol as available on the remote.<br></li>
  9916. <li><B>beep [off|1|2|3]</B><br>
  9917. activate tone<br></li>
  9918. <li><B>backlight [off|on|slow|fast]</B><br>
  9919. activate backlight<br></li>
  9920. <li><B>display &lt;text&gt; comma unit tone backlight &lt;symbol(s)&gt;
  9921. </B><br>
  9922. control display of the remote<br>
  9923. &lt;text&gt; : up to 5 chars <br>
  9924. comma : 'comma' activates the comma, 'no' leaves it off <br>
  9925. [unit] : set the unit symbols.
  9926. [off|Proz|Watt|x3|C|x5|x6|x7|F|x9|x10|x11|x12|x13|x14|x15]. Currently
  9927. the x3..x15 display is not tested. <br>
  9928. tone : activate one of the 3 tones [off|1|2|3]<br>
  9929. backlight: activate backlight flash mode [off|on|slow|fast]<br>
  9930. &lt;symbol(s)&gt; activate symbol display. Multople symbols can be
  9931. acticated at the same time, concatinating them comma separated. Don't
  9932. use spaces here. Possiblesymbols are
  9933. [bulb|switch|window|door|blind|scene|phone|bell|clock|arrowUp|arrowDown]<br><br>
  9934. Example:
  9935. <ul><code>
  9936. # "hello" in display, symb bulb on, backlight, beep<br>
  9937. set FB1 display Hello no off 1 on bulb<br>
  9938. # "1234,5" in display with unit 'W'. Symbols scene,phone,bell and
  9939. # clock are active. Backlight flashing fast, Beep is second tone<br>
  9940. set FB1 display 12345 comma Watt 2 fast scene,phone,bell,clock
  9941. </ul></code>
  9942. </li>
  9943. </ul><br>
  9944. </li>
  9945. <li>HM-Dis-WM55
  9946. <ul>
  9947. <li><B>displayWM help </B><br>
  9948. <B>displayWM [long|short] &lt;text1&gt; &lt;color1&gt; &lt;icon1&gt; ... &lt;text6&gt; &lt;color6&gt; &lt;icon6&gt;</B><br>
  9949. <B>displayWM [long|short] &lt;lineX&gt; &lt;text&gt; &lt;color&gt; &lt;icon&gt;</B><br>
  9950. up to 6 lines can be addressed.<br>
  9951. <B>lineX</B> line number that shall be changed. If this is set the 3 parameter of a line can be adapted. <br>
  9952. <B>textNo</B> is the text to be dispalyed in line No. The text is assotiated with the text defined for the buttons.
  9953. txt&lt;BtnNo&gt;_&lt;lineNo&gt; references channel 1 to 10 and their lines 1 or 2.
  9954. Alternaly a free text of up to 12 char can be used<br>
  9955. <B>color</B> is one white, red, orange, yellow, green, blue<br>
  9956. <B>icon</B> is one off, on, open, closed, error, ok, noIcon<br>
  9957. Example:
  9958. <ul><code>
  9959. set disp01 displayWM short txt02_2 green noIcon txt10_1 red error txt05_2 yellow closed txt02_2 orange open <br>
  9960. set disp01 displayWM long line3 txt02_2 green noIcon<br>
  9961. set disp01 displayWM long line2 nc yellow noIcon<br>
  9962. set disp01 displayWM long line6 txt02_2<br>
  9963. set disp01 displayWM long line1 nc nc closed<br>
  9964. </ul></code>
  9965. </li>
  9966. </ul><br>
  9967. </li>
  9968. <li>HM-Dis-EP-WM55
  9969. <ul>
  9970. <li><B>displayEP help </B><br>
  9971. <B>displayEP &lt;text1,icon1:text2,icon2:text3,icon3&gt; &lt;sound&gt; &lt;repetition&gt; &lt;pause&gt; &lt;signal&gt;</B><br>
  9972. up to 3 lines can be addressed.<br>
  9973. If help is given a <i><B>help</B></i> on the command is given. Options for all parameter will be given.<br>
  9974. <B>textx</B> 12 char text for the given line.
  9975. If empty the value as per reading will be transmittet - i.e. typically no change.
  9976. text0-9 will display predefined text of channels 4 to 8.
  9977. 0xHH allows to display a single char in hex format.<br>
  9978. <B>iconx</B> Icon for this line.
  9979. If empty the value as per reading will be transmittet - i.e. typically no change.<br>
  9980. <B>sound</B> sound to be played<br>
  9981. <B>repetition</B> 0..15 <br>
  9982. <B>pause</B> 1..160<br>
  9983. <B>signal</B> signal color to be displayed<br>
  9984. <br>
  9985. <B>Note: param reWriteDisplayxx</B> <br>
  9986. <li>
  9987. upon button press the device will overwrite the 3 middles lines. When set <br>
  9988. attr chan param reWriteDisplayxx<br>
  9989. the 3 lines will be rewritten to the latest value after xx seconds. xx is between 01 and 99<br>
  9990. </li>
  9991. </li>
  9992. </ul><br>
  9993. </li>
  9994. <li>keyMatic<br><br>
  9995. <ul>The Keymatic uses the AES signed communication. Control
  9996. of the Keymatic is possible with the HM-LAN adapter and the CUL.
  9997. To control the KeyMatic with a CUL, the perl-module Crypt::Rijndael
  9998. needs to be installed.</ul><br>
  9999. <ul>
  10000. <li><B>lock</B><br>
  10001. The lock bolt moves to the locking position<br></li>
  10002. <li><B>unlock [sec]</B><br>
  10003. The lock bolt moves to the unlocking position.<br>
  10004. [sec]: Sets the delay in seconds after the lock automatically locked again.<br>
  10005. 0 - 65535 seconds</li>
  10006. <li><B>open [sec]</B><br>
  10007. Unlocked the door so that the door can be opened.<br>
  10008. [sec]: Sets the delay in seconds after the lock automatically locked
  10009. again.<br>0 - 65535 seconds</li>
  10010. </ul>
  10011. </li>
  10012. <li>winMatic <br><br>
  10013. <ul>winMatic provides 2 channels, one for the window control and a second
  10014. for the accumulator.</ul><br>
  10015. <ul>
  10016. <li><B>level &lt;level&gt; &lt;relockDelay&gt; &lt;speed&gt;</B><br>
  10017. set the level. <br>
  10018. &lt;level&gt;: range is 0 to 100%<br>
  10019. &lt;relockDelay&gt;: range 0 to 65535 sec. 'ignore' can be used to igneore the value alternaly <br>
  10020. &lt;speed&gt;: range is 0 to 100%<br>
  10021. </li>
  10022. <li><B>stop</B><br>
  10023. stop movement<br>
  10024. </li>
  10025. </ul>
  10026. </li>
  10027. <li>CCU_FHEM<br>
  10028. <ul>
  10029. <li>defIgnUnknown<br>
  10030. define unknown devices which are present in the readings.
  10031. set attr ignore and remove the readingfrom the list. <br>
  10032. </li>
  10033. </ul>
  10034. </li>
  10035. <li>HM-Sys-sRP-Pl<br><br>
  10036. setup the repeater's entries. Up to 36entries can be applied.
  10037. <ul>
  10038. <li><B>setRepeat &lt;entry&gt; &lt;sender&gt; &lt;receiver&gt; &lt;broadcast&gt;</B><br>
  10039. &lt;entry&gt; [1..36] entry number in repeater table. The repeater can handle up to 36 entries.<br>
  10040. &lt;sender&gt; name or HMID of the sender or source which shall be repeated<br>
  10041. &lt;receiver&gt; name or HMID of the receiver or destination which shall be repeated<br>
  10042. &lt;broadcast&gt; [yes|no] determines whether broadcast from this ID shall be repeated<br>
  10043. <br>
  10044. short application: <br>
  10045. <code>setRepeat setAll 0 0 0<br></code>
  10046. will rewrite the complete list to the deivce. Data will be taken from attribut repPeers. <br>
  10047. attribut repPeers is formated:<br>
  10048. src1:dst1:[y/n],src2:dst2:[y/n],src2:dst2:[y/n],...<br>
  10049. <br>
  10050. Reading repPeer is formated:<br>
  10051. <ul>
  10052. Number src dst broadcast verify<br>
  10053. number: entry sequence number<br>
  10054. src: message source device - read from repeater<br>
  10055. dst: message destination device - assembled from attributes<br>
  10056. broadcast: shall broadcast be repeated for this source - read from repeater<br>
  10057. verify: do attributes and readings match?<br>
  10058. </ul>
  10059. </li>
  10060. </ul>
  10061. </li>
  10062. <br>
  10063. Debugging:
  10064. <ul>
  10065. <li><B>raw &lt;data&gt; ...</B><br>
  10066. Only needed for experimentation.
  10067. send a list of "raw" commands. The first command will be
  10068. immediately sent, the next one after the previous one is acked by
  10069. the target. The length will be computed automatically, and the
  10070. message counter will be incremented if the first two charcters are
  10071. ++. Example (enable AES):
  10072. <pre>
  10073. set hm1 raw ++A001F100001234560105000000001\
  10074. ++A001F10000123456010802010AF10B000C00\
  10075. ++A001F1000012345601080801\
  10076. ++A001F100001234560106</pre>
  10077. </li>
  10078. </ul>
  10079. </ul>
  10080. </ul>
  10081. <br>
  10082. <a name="CUL_HMget"></a><b>Get</b><br>
  10083. <ul>
  10084. <li><B>configSave &lt;filename&gt;</B><a name="CUL_HMconfigSave"></a><br>
  10085. Saves the configuration of an entity into a file. Data is stored in a
  10086. format to be executed from fhem command prompt.<br>
  10087. The file is located in the fhem home directory aside of fhem.cfg. Data
  10088. will be stored cumulative - i.e. new data will be appended to the
  10089. file. It is up to the user to avoid duplicate storage of the same
  10090. entity.<br>
  10091. Target of the data is ONLY the HM-device information which is located
  10092. IN the HM device. Explicitely this is the peer-list and the register.
  10093. With the register also the peering is included.<br>
  10094. The file is readable and editable by the user. Additionaly timestamps
  10095. are stored to help user to validate.<br>
  10096. Restrictions:<br>
  10097. Even though all data of the entity will be secured to the file FHEM
  10098. stores the data that is avalilable to FHEM at time of save!. It is up
  10099. to the user to read the data from the HM-hardware prior to execution.
  10100. See recommended flow below.<br>
  10101. This command will not store any FHEM attributes o device definitions.
  10102. This continues to remain in fhem.cfg.<br>
  10103. Furthermore the secured data will not automatically be reloaded to the
  10104. HM-hardware. It is up to the user to perform a restore.<br><br>
  10105. As with other commands also 'configSave' is best executed on a device
  10106. rather then on a channel. If executed on a device also the assotiated
  10107. channel data will be secured. <br><br>
  10108. <code>
  10109. Recommended work-order for device 'HMdev':<br>
  10110. set HMdev clear msgEvents # clear old events to better check flow<br>
  10111. set HMdev getConfig # read device & channel inforamtion<br>
  10112. # wait until operation is complete<br>
  10113. # protState should be CMDs_done<br>
  10114. # there shall be no warnings amongst prot... variables<br>
  10115. get configSave myActorFile<br>
  10116. </code>
  10117. </li>
  10118. <li><B>param &lt;paramName&gt;</B><br>
  10119. returns the content of the relevant parameter for the entity. <br>
  10120. Note: if this command is executed on a channel and 'model' is
  10121. requested the content hosting device's 'model' will be returned.
  10122. </li>
  10123. <li><B>reg &lt;addr&gt; &lt;list&gt; &lt;peerID&gt;</B><a name="CUL_HMget_reg"></a><br>
  10124. returns the value of a register. The data is taken from the storage in FHEM and not
  10125. read directly outof the device.
  10126. If register content is not present please use getConfig, getReg in advance.<br>
  10127. &lt;addr&gt; address in hex of the register. Registername can be used alternaly
  10128. if decoded by FHEM. "all" will return all decoded register for this entity in one list.<br>
  10129. &lt;list&gt; list from which the register is taken. If rgistername is used list
  10130. is ignored and can be set to 0.<br>
  10131. &lt;peerID&gt; identifies the registerbank in case of list3 and list4. It an be set to dummy if not used.<br>
  10132. </li>
  10133. <li><B>regVal &lt;addr&gt; &lt;list&gt; &lt;peerID&gt;</B><br>
  10134. returns the value of a register. It does the same as <a href="#CUL_HMget_reg">reg</a> but strips off units<br>
  10135. </li>
  10136. <li><B>regList</B><br>
  10137. returns a list of register that are decoded by FHEM for this device.<br>
  10138. Note that there could be more register implemented for a device.<br>
  10139. </li>
  10140. <li><B>saveConfig &lt;file&gt;</B><a name="CUL_HMsaveConfig"></a><br>
  10141. stores peers and register to the file.<br>
  10142. Stored will be the data as available in fhem. It is necessary to read the information from the device prior to the save.<br>
  10143. The command supports device-level action. I.e. if executed on a device also all related channel entities will be stored implicitely.<br>
  10144. Storage to the file will be cumulative.
  10145. If an entity is stored multiple times to the same file data will be appended.
  10146. User can identify time of storage in the file if necessary.<br>
  10147. Content of the file can be used to restore device configuration.
  10148. It will restore all peers and all register to the entity.<br>
  10149. Constrains/Restrictions:<br>
  10150. prior to rewrite data to an entity it is necessary to pair the device with FHEM.<br>
  10151. restore will not delete any peered channels, it will just add peer channels.<br>
  10152. </li>
  10153. <li><B>listDevice</B><br>
  10154. <ul>
  10155. <li>when used with ccu it returns a list of Devices using the ccu service to assign an IO.<br>
  10156. </li>
  10157. <li>when used with ActionDetector user will get a comma separated list of entities being assigned to the action detector<br>
  10158. get ActionDetector listDevice # returns all assigned entities<br>
  10159. get ActionDetector listDevice notActive# returns entities which habe not status alive<br>
  10160. get ActionDetector listDevice alive # returns entities with status alive<br>
  10161. get ActionDetector listDevice unknown # returns entities with status unknown<br>
  10162. get ActionDetector listDevice dead # returns entities with status dead<br>
  10163. </li>
  10164. </ul>
  10165. </li>
  10166. <li><B>info</B><br>
  10167. <ul>
  10168. <li>provides information about entities using ActionDetector<br>
  10169. </li>
  10170. </ul>
  10171. </li>
  10172. </ul><br>
  10173. <a name="CUL_HMattr"></a><b>Attributes</b>
  10174. <ul>
  10175. <li><a href="#eventMap">eventMap</a></li>
  10176. <li><a href="#do_not_notify">do_not_notify</a></li>
  10177. <li><a href="#ignore">ignore</a></li>
  10178. <li><a href="#dummy">dummy</a></li>
  10179. <li><a href="#showtime">showtime</a></li>
  10180. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  10181. <li><a name="CUL_HMaesCommReq">aesCommReq</a>
  10182. if set IO is forced to request AES signature before sending ACK to the device.<br>
  10183. </li>
  10184. <li><a name="#CUL_HMactAutoTry">actAutoTry</a>
  10185. actAutoTry 0_off,1_on<br>
  10186. setting this option enables Action Detector to send a statusrequest in case of a device is going to be marked dead.
  10187. The attribut may be useful in case a device is being checked that does not send messages regularely - e.g. an ordinary switch.
  10188. </li>
  10189. <li><a name="#CUL_HMactCycle">actCycle</a>
  10190. actCycle &lt;[hhh:mm]|off&gt;<br>
  10191. Supports 'alive' or better 'not alive' detection for devices. [hhh:mm] is the maximum silent time for the device.
  10192. Upon no message received in this period an event will be raised "&lt;device&gt; is dead".
  10193. If the device sends again another notification is posted "&lt;device&gt; is alive". <br>
  10194. This actiondetect will be autocreated for each device with build in cyclic status report.<br>
  10195. Controlling entity is a pseudo device "ActionDetector" with HMId "000000".<br>
  10196. Due to performance considerations the report latency is set to 600sec (10min).
  10197. It can be controlled by the attribute "actCycle" of "ActionDetector".<br>
  10198. Once entered to the supervision the HM device has 2 attributes:<br>
  10199. <ul>
  10200. actStatus: activity status of the device<br>
  10201. actCycle: detection period [hhh:mm]<br>
  10202. </ul>
  10203. The overall function can be viewed checking out the "ActionDetector" entity. The status of all entities is present in the READING section.<br>
  10204. Note: This function can be enabled for devices with non-cyclic messages as well. It is up to the user to enter a reasonable cycletime.
  10205. </li>
  10206. <li><a name="#CUL_HMautoReadReg">autoReadReg</a><br>
  10207. '0' autoReadReg will be ignored.<br>
  10208. '1' will execute a getConfig for the device automatically after each reboot of FHEM. <br>
  10209. '2' like '1' plus execute after power_on.<br>
  10210. '3' includes '2' plus updates on writes to the device<br>
  10211. '4' includes '3' plus tries to request status if it seems to be missing<br>
  10212. '5' checks reglist and peerlist. If reading seems incomplete getConfig will be scheduled<br>
  10213. '8_stateOnly' will only update status information but not configuration
  10214. data like register and peer<br>
  10215. Execution will be delayed in order to prevent congestion at startup. Therefore the update
  10216. of the readings and the display will be delayed depending on the size of the database.<br>
  10217. Recommendations and constrains upon usage:<br>
  10218. <ul>
  10219. use this attribute on the device or channel 01. Do not use it separate on each channel
  10220. of a multi-channel device to avoid duplicate execution<br>
  10221. usage on devices which only react to 'config' mode is not recommended since executen will
  10222. not start until config is triggered by the user<br>
  10223. usage on devices which support wakeup-mode is usefull. But consider that execution is delayed
  10224. until the device "wakes up".<br>
  10225. </ul>
  10226. </li>
  10227. <li><a name="#CUL_HMburstAccess">burstAccess</a><br>
  10228. can be set for the device entity if the model allowes conditionalBurst.
  10229. The attribut will switch off burst operations (0_off) which causes less message load
  10230. on HMLAN and therefore reduces the chance of HMLAN overload.<br>
  10231. Setting it on (1_auto) allowes shorter reaction time of the device. User does not
  10232. need to wait for the device to wake up. <br>
  10233. Note that also the register burstRx needs to be set in the device.</li>
  10234. <li><a name="#CUL_HMexpert">expert</a><br>
  10235. This attribut controls the visibility of the register readings. This attibute controls
  10236. the presentation of device parameter in the readings.<br>
  10237. it is a binary coded number with following presets:<br>
  10238. <ul>
  10239. 0_defReg : default register<br>
  10240. 1_allReg : all register<br>
  10241. 2_defReg+raw : default register and raw reading<br>
  10242. 3_allReg+raw : all register and raw reading<br>
  10243. 4_off : no register<br>
  10244. 8_templ+default: templates and default register<br>
  10245. 12_templOnly : templates only<br>
  10246. 251_anything : anything available<br>
  10247. </ul>
  10248. If expert is applied a device it is used for assotiated channels.
  10249. It can be overruled if expert attibute is also applied to the channel device.<br>
  10250. Make sure to check out attribut showInternalValues in the global values as well.
  10251. extert takes benefit of the implementation.
  10252. Nevertheless - by definition - showInternalValues overrules expert.
  10253. </li>
  10254. <li><a name="#CUL_HMreadOnly">readOnly</a><br>
  10255. restircts commands to read od observ only.
  10256. </li>
  10257. <li><a name="#CUL_HMIOgrp">IOgrp</a><br>
  10258. can be given to devices and shall point to a virtual CCU. As a consequence the
  10259. CCU will take care of the assignment to the best suitable IO. It is necessary that a
  10260. virtual CCU is defined and all relevant IO devices are assigned to it. Upon sending the CCU will
  10261. check which IO is operational and has the best RSSI performance for this device.<br>
  10262. Optional a prefered IO - perfIO can be given. In case this IO is operational it will be selected regardless
  10263. of rssi values. <br>
  10264. If none is detected in the prefIO list the mechanism is stopped and the IO as of IOdev is assigned<br>
  10265. Example:<br>
  10266. <ul><code>
  10267. attr myDevice1 IOgrp vccu<br>
  10268. attr myDevice2 IOgrp vccu:prefIO<br>
  10269. attr myDevice2 IOgrp vccu:prefIO1,prefIO2,prefIO3<br>
  10270. attr myDevice2 IOgrp vccu:prefIO1,prefIO2,none<br>
  10271. </code></ul>
  10272. </li>
  10273. <li><a name="#CUL_HMlevelRange">levelRange</a><br>
  10274. can be used with dimmer only. It defines the dimmable range to be used with this dimmer-channel.
  10275. It is meant to support e.g. LED light that starts at 10% and reaches maxbrightness at 40%.
  10276. levelrange will normalize the level to this range. I.e. set to 100% will physically set the
  10277. dimmer to 40%, 1% will set to 10% physically. 0% still switches physially off.<br>
  10278. Impacted are commands on, up, down, toggle and pct. <b>Not</b> effected is the off command
  10279. which still set physically 0%.<br>
  10280. To be considered:<br>
  10281. dimmer level set by peers and buttons is not impacted. Those are controlled by device register<br>
  10282. Readings level may go to negative or above 100%. This simply results from the calculation and reflects
  10283. physical level is above or below the given range.<br>
  10284. In case of virtual dimmer channels available present the attribut needs to be set for
  10285. each channel<br>
  10286. User should be careful to set min level other then '0'<br>
  10287. Example:<br>
  10288. <ul><code>
  10289. attr myChannel levelRange 0,40<br>
  10290. attr myChannel levelRange 10,80<br>
  10291. </code></ul>
  10292. </li>
  10293. <li><a name="#CUL_HMmodel">model</a>,
  10294. <a name="subType">subType</a><br>
  10295. These attributes are set automatically after a successful pairing.
  10296. They are not supposed to be set by hand, and are necessary in order to
  10297. correctly interpret device messages or to be able to send them.</li>
  10298. <li><a name="#CUL_HMmsgRepeat">msgRepeat</a><br>
  10299. defines number of repetitions if a device doesn't answer in time. <br>
  10300. Devices which donly support config mode no repeat ist allowed. <br>
  10301. For devices with wakeup mode the device will wait for next wakeup. Lonng delay might be
  10302. considered in this case. <br>
  10303. Repeat for burst devices will impact HMLAN transmission capacity.</li>
  10304. <li><a name="#CUL_HMparam">param</a><br>
  10305. param defines model specific behavior or functions. See <a href="#CUL_HMparams"><b>available parameter</b></a> for details</li>
  10306. <li><a name="#CUL_HMrawToReadable">rawToReadable</a><br>
  10307. Used to convert raw KFM100 values to readable data, based on measured
  10308. values. E.g. fill slowly your container, while monitoring the
  10309. values reported with <a href="#inform">inform</a>. You'll see:
  10310. <ul>
  10311. 10 (at 0%)<br>
  10312. 50 (at 20%)<br>
  10313. 79 (at 40%)<br>
  10314. 270 (at 100%)<br>
  10315. </ul>
  10316. Apply these values with: "attr KFM100 rawToReadable 10:0 50:20 79:40 270:100".
  10317. fhem will do a linear interpolation for values between the bounderies.
  10318. </li>
  10319. <li><a name="#CUL_HMrssiLog">rssiLog</a><br>
  10320. can be given to devices, denied for channels. If switched '1' each RSSI entry will be
  10321. written to a reading. User may use this to log and generate a graph of RSSI level.<br>
  10322. Due to amount of readings and events it is NOT RECOMMENDED to switch it on by default.
  10323. </li>
  10324. <li><a name="#CUL_HMtempListTmpl">tempListTmpl</a><br>
  10325. Sets the default template for a heating controller. If not given the detault template is taken from
  10326. file tempList.cfg using the enitity name as template name (e.g. ./tempLict.cfg:RT1_Clima <br>
  10327. To avoid template usage set this attribut to '0'.<br>
  10328. Format is &lt;file&gt;:&lt;templatename&gt;. lt
  10329. </li>
  10330. <li><a name="unit">unit</a><br>
  10331. set the reported unit by the KFM100 if rawToReadable is active. E.g.<br>
  10332. attr KFM100 unit Liter
  10333. </li>
  10334. <li><a name="cyclicMsgOffset">cyclicMsgOffset</a><br>
  10335. when calculating the timestamp for sending the next cyclic message (e.g. weather or valve data) then the value of this attribute<br>
  10336. in milliseconds is added to the result. So adjusting this might fix problems for example when weather messages of virtual devices are not received reliably
  10337. </li>
  10338. </ul> <br>
  10339. <a name="CUL_HMparams"><b>available parameter for attribut "param"</b></a>
  10340. <ul>
  10341. <li><B>HM-Sen-RD-O</B><br>
  10342. <B>offAtPon</B> heat channel only: force heating off after powerOn<br>
  10343. <B>onAtRain</B> heat channel only: force heating on while status changes to 'rain' and off when it changes to 'dry'<br>
  10344. </li>
  10345. <li><B>virtuals</B><br>
  10346. <B>noOnOff</B> virtual entity will not toggle state when trigger is received. If this parameter is
  10347. not given the entity will toggle its state between On and Off with each trigger<br>
  10348. <B>msgReduce:&lt;No&gt;</B> if channel is used for <a ref="CUL_HMvalvePos"></a> it skips every No message
  10349. in order to reduce transmit load. Numbers from 0 (no skip) up to 9 can be given.
  10350. VD will lose connection with more then 5 skips<br>
  10351. </li>
  10352. <li><B>blind</B><br>
  10353. <B>levelInverse</B> while HM considers 100% as open and 0% as closed this may not be
  10354. intuitive to all user. Ny default 100% is open and will be dislayed as 'on'. Setting this param the display will be inverted - 0% will be open and 100% is closed.<br>
  10355. NOTE: This will apply to readings and set commands. <B>It does not apply to any register. </B><br>
  10356. <B>ponRestoreSmart</B> upon powerup of the device the Blind will drive to expected closest endposition followed by driving to the pre-PON level<br>
  10357. <B>ponRestoreForce</B> upon powerup of the device the Blind will drive to level 0, then to level 100 followed by driving to the pre-PON level<br>
  10358. </li>
  10359. <li><B>sensRain</B><br>
  10360. <B>siren</B><br>
  10361. <B>powerMeter</B><br>
  10362. <B>switch</B><br>
  10363. <B>dimmer</B><br>
  10364. <B>rgb</B><br>
  10365. <B>showTimed</B> if timmed is running -till will be added to state.
  10366. This results eventually in state on-till which allowes better icon handling.<br>
  10367. </li>
  10368. </ul><br>
  10369. <a name="CUL_HMevents"><b>Generated events:</b></a>
  10370. <ul>
  10371. <li><B>general</B><br>
  10372. recentStateType:[ack|info] # cannot be used ti trigger notifies<br>
  10373. <ul>
  10374. <li>ack indicates that some statusinfo is derived from an acknowledge</li>
  10375. <li>info indicates an autonomous message from the device</li>
  10376. <li><a name="CUL_HMsabotageAttackId"><b>sabotageAttackId</b></a><br>
  10377. Alarming configuration access to the device from a unknown source<br></li>
  10378. <li><a name="CUL_HMsabotageAttack"><b>sabotageAttack</b></a><br>
  10379. Alarming configuration access to the device that was not issued by our system<br></li>
  10380. <li><a name="CUL_HMtrigDst"><b>trigDst_&lt;name&gt;: noConfig</b></a><br>
  10381. A sensor triggered a Device which is not present in its peerList. Obviously the peerList is not up to date<br></li>
  10382. </ul>
  10383. </li>
  10384. <li><B>HM-CC-TC,ROTO_ZEL-STG-RM-FWT</B><br>
  10385. T: $t H: $h<br>
  10386. battery:[low|ok]<br>
  10387. measured-temp $t<br>
  10388. humidity $h<br>
  10389. actuator $vp %<br>
  10390. desired-temp $dTemp<br>
  10391. desired-temp-manu $dTemp #temperature if switchen to manual mode<br>
  10392. desired-temp-cent $dTemp #temperature if switchen to central mode<br>
  10393. windowopen-temp-%d %.1f (sensor:%s)<br>
  10394. tempList$wd hh:mm $t hh:mm $t ...<br>
  10395. displayMode temp-[hum|only]<br>
  10396. displayTemp [setpoint|actual]<br>
  10397. displayTempUnit [fahrenheit|celsius]<br>
  10398. controlMode [auto|manual|central|party]<br>
  10399. tempValveMode [Auto|Closed|Open|unknown]<br>
  10400. param-change offset=$o1, value=$v1<br>
  10401. ValveErrorPosition_for_$dname $vep %<br>
  10402. ValveOffset_for_$dname : $of %<br>
  10403. ValveErrorPosition $vep %<br>
  10404. ValveOffset $of %<br>
  10405. time-request<br>
  10406. trig_&lt;src&gt; &lt;value&gt; #channel was triggered by &lt;src&gt; channel.
  10407. This event relies on complete reading of channels configuration, otherwise Data can be
  10408. incomplete or incorrect.<br>
  10409. trigLast &lt;channel&gt; #last receiced trigger<br>
  10410. </li>
  10411. <li><B>HM-CC-RT-DN and HM-CC-RT-DN-BoM</B><br>
  10412. state:T: $actTemp desired: $setTemp valve: $vp %<br>
  10413. motorErr: [ok|ValveTight|adjustRangeTooLarge|adjustRangeTooSmall|communicationERR|unknown|lowBat|ValveErrorPosition]
  10414. measured-temp $actTemp<br>
  10415. desired-temp $setTemp<br>
  10416. ValvePosition $vp %<br>
  10417. mode [auto|manual|party|boost]<br>
  10418. battery [low|ok]<br>
  10419. batteryLevel $bat V<br>
  10420. measured-temp $actTemp<br>
  10421. desired-temp $setTemp<br>
  10422. actuator $vp %<br>
  10423. time-request<br>
  10424. trig_&lt;src&gt; &lt;value&gt; #channel was triggered by &lt;src&gt; channel.
  10425. </li>
  10426. <li><B>HM-CC-VD,ROTO_ZEL-STG-RM-FSA</B><br>
  10427. $vp %<br>
  10428. battery:[critical|low|ok]<br>
  10429. motorErr:[ok|blocked|loose|adjusting range too small|opening|closing|stop]<br>
  10430. ValvePosition:$vp %<br>
  10431. ValveErrorPosition:$vep %<br>
  10432. ValveOffset:$of %<br>
  10433. ValveDesired:$vp % # set by TC <br>
  10434. operState:[errorTargetNotMet|onTarget|adjusting|changed] # operational condition<br>
  10435. operStateErrCnt:$cnt # number of failed settings<br>
  10436. </li>
  10437. <li><B>HM-CC-SCD</B><br>
  10438. [normal|added|addedStrong]<br>
  10439. battery [low|ok]<br>
  10440. </li>
  10441. <li><B>HM-SEC-SFA-SM</B><br>
  10442. powerError [on|off]<br>
  10443. sabotageError [on|off]<br>
  10444. battery: [critical|low|ok]<br>
  10445. </li>
  10446. <li><B>HM-LC-BL1-PB-FM</B><br>
  10447. motor: [opening|closing]<br>
  10448. </li>
  10449. <li><B>HM-LC-SW1-BA-PCB</B><br>
  10450. battery: [low|ok]<br>
  10451. </li>
  10452. <li><B>HM-OU-LED16</B><br>
  10453. color $value # hex - for device only<br>
  10454. $value # hex - for device only<br>
  10455. color [off|red|green|orange] # for channel <br>
  10456. [off|red|green|orange] # for channel <br>
  10457. </li>
  10458. <li><B>HM-OU-CFM-PL</B><br>
  10459. [on|off|$val]<br>
  10460. </li>
  10461. <li><B>HM-Sen-Wa-Od</B><br>
  10462. $level%<br>
  10463. level $level%<br>
  10464. </li>
  10465. <li><B>KFM100</B><br>
  10466. $v<br>
  10467. $cv,$unit<br>
  10468. rawValue:$v<br>
  10469. Sequence:$seq<br>
  10470. content:$cv,$unit<br>
  10471. </li>
  10472. <li><B>KS550/HM-WDS100-C6-O</B><br>
  10473. T: $t H: $h W: $w R: $r IR: $ir WD: $wd WDR: $wdr S: $s B: $b<br>
  10474. temperature $t<br>
  10475. humidity $h<br>
  10476. windSpeed $w<br>
  10477. windDirection $wd<br>
  10478. windDirRange $wdr<br>
  10479. rain $r<br>
  10480. isRaining $ir<br>
  10481. sunshine $s<br>
  10482. brightness $b<br>
  10483. unknown $p<br>
  10484. </li>
  10485. <li><B>HM-Sen-RD-O</B><br>
  10486. lastRain: timestamp # no trigger generated. Begin of previous Rain -
  10487. timestamp of the reading is the end of rain. <br>
  10488. </li>
  10489. <li><B>THSensor and HM-WDC7000</B><br>
  10490. T: $t H: $h AP: $ap<br>
  10491. temperature $t<br>
  10492. humidity $h<br>
  10493. airpress $ap #HM-WDC7000 only<br>
  10494. </li>
  10495. <li><B>dimmer</B><br>
  10496. overload [on|off]<br>
  10497. overheat [on|off]<br>
  10498. reduced [on|off]<br>
  10499. dim: [up|down|stop]<br>
  10500. </li>
  10501. <li><B>motionDetector</B><br>
  10502. brightness:$b<br>
  10503. alive<br>
  10504. motion on (to $dest)<br>
  10505. motionCount $cnt _next:$nextTr"-"[0x0|0x1|0x2|0x3|15|30|60|120|240|0x9|0xa|0xb|0xc|0xd|0xe|0xf]<br>
  10506. cover [closed|open] # not for HM-Sec-MDIR<br>
  10507. sabotageError [on|off] # only HM-Sec-MDIR<br>
  10508. battery [low|ok]<br>
  10509. devState_raw.$d1 $d2<br>
  10510. </li>
  10511. <li><B>remote/pushButton/outputUnit</B><br>
  10512. <ul> (to $dest) is added if the button is peered and does not send to broadcast<br>
  10513. Release is provided for peered channels only</ul>
  10514. Btn$x onShort<br>
  10515. Btn$x offShort<br>
  10516. Btn$x onLong $counter<br>
  10517. Btn$x offLong $counter<br>
  10518. Btn$x onLongRelease $counter<br>
  10519. Btn$x offLongRelease $counter<br>
  10520. Btn$x onShort (to $dest)<br>
  10521. Btn$x offShort (to $dest)<br>
  10522. Btn$x onLong $counter (to $dest)<br>
  10523. Btn$x offLong $counter (to $dest)<br>
  10524. Btn$x onLongRelease $counter (to $dest)<br>
  10525. Btn$x offLongRelease $counter (to $dest)<br>
  10526. </li>
  10527. <li><B>remote/pushButton</B><br>
  10528. battery [low|ok]<br>
  10529. trigger [Long|Short]_$no trigger event from channel<br>
  10530. </li>
  10531. <li><B>swi</B><br>
  10532. Btn$x Short<br>
  10533. Btn$x Short (to $dest)<br>
  10534. battery: [low|ok]<br>
  10535. </li>
  10536. <li><B>switch/dimmer/blindActuator</B><br>
  10537. $val<br>
  10538. powerOn [on|off|$val]<br>
  10539. [unknown|motor|dim] [up|down|stop]:$val<br>
  10540. timedOn [running|off]<br> # on is temporary - e.g. started with on-for-timer
  10541. </li>
  10542. <li><B>sensRain</B><br>
  10543. $val<br>
  10544. powerOn <br>
  10545. level &lt;val&ge;<br>
  10546. timedOn [running|off]<br> # on is temporary - e.g. started with on-for-timer
  10547. trigger [Long|Short]_$no trigger event from channel<br>
  10548. </li>
  10549. <li><B>smokeDetector</B><br>
  10550. [off|smoke-Alarm|alive] # for team leader<br>
  10551. [off|smoke-forward|smoke-alarm] # for team members<br>
  10552. [normal|added|addedStrong] #HM-CC-SCD<br>
  10553. SDteam [add|remove]_$dname<br>
  10554. battery [low|ok]<br>
  10555. smoke_detect [none|&lt;src&gt;]<br>
  10556. teamCall:from $src<br>
  10557. </li>
  10558. <li><B>threeStateSensor</B><br>
  10559. [open|tilted|closed]<br>
  10560. [wet|damp|dry] #HM-SEC-WDS only<br>
  10561. cover [open|closed] #HM-SEC-WDS and HM-Sec-RHS<br>
  10562. alive yes<br>
  10563. battery [low|ok]<br>
  10564. contact [open|tilted|closed]<br>
  10565. contact [wet|damp|dry] #HM-SEC-WDS only<br>
  10566. sabotageError [on|off] #HM-SEC-SC only<br>
  10567. </li>
  10568. <li><B>winMatic</B><br>
  10569. [locked|$value]<br>
  10570. motorErr [ok|TurnError|TiltError]<br>
  10571. direction [no|up|down|undefined]<br>
  10572. charge [trickleCharge|charge|dischange|unknown]<br>
  10573. airing [inactiv|$air]<br>
  10574. course [tilt|close]<br>
  10575. airing [inactiv|$value]<br>
  10576. contact tesed<br>
  10577. </li>
  10578. <li><B>keyMatic</B><br>
  10579. unknown:40<br>
  10580. battery [low|ok]<br>
  10581. uncertain [yes|no]<br>
  10582. error [unknown|motor aborted|clutch failure|none']<br>
  10583. lock [unlocked|locked]<br>
  10584. [unlocked|locked|uncertain]<br>
  10585. </li>
  10586. </ul>
  10587. <a name="CUL_HMinternals"><b>Internals</b></a>
  10588. <ul>
  10589. <li><B>aesCommToDev</B><br>
  10590. gives information about success or fail of AES communication between IO-device and HM-Device<br>
  10591. </li>
  10592. </ul><br>
  10593. <br>
  10594. </ul>
  10595. =end html
  10596. =begin html_DE
  10597. <a name="CUL_HM"></a><h3>CUL_HM</h3>
  10598. <ul>
  10599. Unterst&uuml;tzung f&uuml;r eQ-3 HomeMatic Ger&auml;te via <a href="#CUL">CUL</a> oder <a href="#HMLAN">HMLAN</a>.<br>
  10600. <br>
  10601. <a name="CUL_HMdefine"></a><b>Define</b>
  10602. <ul>
  10603. <code><B>define &lt;name&gt; CUL_HM &lt;6-digit-hex-code|8-digit-hex-code&gt;</B></code>
  10604. <br><br>
  10605. Eine korrekte Ger&auml;tedefinition ist der Schl&uuml;ssel zur einfachen Handhabung der HM-Umgebung.
  10606. <br>
  10607. Hintergrund zur Definition:<br>
  10608. HM-Ger&auml;te haben eine 3 Byte (6 stelliger HEX-Wert) lange HMid - diese ist Grundlage
  10609. der Adressierung. Jedes Ger&auml;t besteht aus einem oder mehreren Kan&auml;len. Die HMid f&uuml;r einen
  10610. Kanal ist die HMid des Ger&auml;tes plus die Kanalnummer (1 Byte, 2 Stellen) in
  10611. hexadezimaler Notation.
  10612. Kan&auml;le sollten f&uuml;r alle mehrkanaligen Ger&auml;te definiert werden. Eintr&auml;ge f&uuml;r Kan&auml;le
  10613. k&ouml;nnen nicht angelegt werden wenn das zugeh&ouml;rige Ger&auml;t nicht existiert.<br> Hinweis: FHEM
  10614. belegt das Ger&auml;t automatisch mit Kanal 1 falls dieser nicht explizit angegeben wird. Daher
  10615. ist bei einkanaligen Ger&auml;ten keine Definition n&ouml;tig.<br>
  10616. Hinweis: Wird ein Ger&auml;t gel&ouml;scht werden auch die zugeh&ouml;rigen Kan&auml;le entfernt. <br> Beispiel einer
  10617. vollst&auml;ndigen Definition eines Ger&auml;tes mit 2 Kan&auml;len:<br>
  10618. <ul><code>
  10619. define livingRoomSwitch CUL_HM 123456<br>
  10620. define LivingroomMainLight CUL_HM 12345601<br>
  10621. define LivingroomBackLight CUL_HM 12345602<br><br>
  10622. </code></ul>
  10623. livingRoomSwitch bezeichnet das zur Kommunikation verwendete Ger&auml;t. Dieses wird
  10624. vor den Kan&auml;len definiert um entsprechende Verweise einstellen zu k&ouml;nnen. <br>
  10625. LivingroomMainLight hat Kanal 01 und behandelt den Lichtstatus, Kanal-Peers
  10626. sowie zugeh&ouml;rige Kanalregister. Falls nicht definiert wird Kanal 01 durch die Ger&auml;teinstanz
  10627. abgedeckt.<br> LivingRoomBackLight ist der zweite "Kanal", Kanal 02. Seine
  10628. Definition ist verpflichtend um die Funktion ausf&uuml;hren zu k&ouml;nnen.<br><br>
  10629. Sonderfall Sender: HM behandelt jeden Knopf einer Fernbedienung, Drucktaster und
  10630. &auml;hnliches als Kanal . Es ist m&ouml;glich (nicht notwendig) einen Kanal pro Knopf zu
  10631. definieren. Wenn alle Kan&auml;le definiert sind ist der Zugriff auf Pairing-Informationen
  10632. sowie auf Kanalregister m&ouml;glich. Weiterhin werden Verkn&uuml;pfungen durch Namen besser
  10633. lesbar.<br><br>
  10634. define kann auch durch das <a href="#autocreate">autocreate</a>
  10635. Modul aufgerufen werden, zusammen mit dem notwendigen subType Attribut.
  10636. Normalerweise erstellt man <a href="#CULset">hmPairForSec</a> und dr&uuml;ckt dann den
  10637. zugeh&ouml;rigen Knopf am Ger&auml;t um die Verkn&uuml;pfung herzustellen oder man verwendet <a
  10638. href="#CULset">hmPairSerial</a> falls das Ger&auml;t ein Empf&auml;nger und die Seriennummer
  10639. bekannt ist. Autocreate wird dann ein FHEM-Ger&auml;t mit allen notwendigen Attributen anlegen.
  10640. Ohne Pairing wird das Ger&auml;t keine Befehle von FHEM akzeptieren. Selbst wenn das Pairing
  10641. scheitert legt FHEM m&ouml;glicherweise das Ger&auml;t an. Erfolgreiches Pairen wird
  10642. durch den Eintrag CommandAccepted in den Details zum CUL_HM Ger&auml;t angezeigt.<br><br>
  10643. Falls autocreate nicht verwendet werden kann muss folgendes spezifiziert werden:<br>
  10644. <ul>
  10645. <li>Der &lt;6-stellige-Hex-Code&gt;oder HMid+ch &lt;8-stelliger-Hex-Code&gt;<br>
  10646. Das ist eine einzigartige, festgelegte Ger&auml;teadresse die nicht ge&auml;ndert werden kann (nein,
  10647. man kann sie nicht willk&uuml;rlich ausw&auml;hlen wie z.B. bei FS20 Ger&auml;ten). Man kann sie feststellen
  10648. indem man das FHEM-Log durchsucht.</li>
  10649. <li>Das subType Attribut<br>
  10650. Dieses lautet: switch dimmer blindActuator remote sensor swi
  10651. pushButton threeStateSensor motionDetector keyMatic winMatic
  10652. smokeDetector</li>
  10653. <li>Das model Attribut<br>
  10654. ist entsprechend der HM Nomenklatur zu vergeben</li>
  10655. </ul>
  10656. Ohne diese Angaben kann FHEM nicht korrekt mit dem Ger&auml;t arbeiten.<br><br>
  10657. <b>Hinweise</b>
  10658. <ul>
  10659. <li>Falls das Interface ein Ger&auml;t vom Typ CUL ist muss <a href="#rfmode">rfmode </a>
  10660. des zugeh&ouml;rigen CUL/CUN Ger&auml;tes auf HomeMatic gesetzt werden.
  10661. Achtung: Dieser Modus ist nur f&uuml;r BidCos/Homematic. Nachrichten von FS20/HMS/EM/S300
  10662. werden durch diese Ger&auml;t <b>nicht</b> empfangen. Bereits definierte FS20/HMS
  10663. Ger&auml;te m&uuml;ssen anderen Eing&auml;ngen zugeordnet werden (CUL/FHZ/etc).
  10664. </li>
  10665. <li>Nachrichten eines Ger&auml;ts werden nur richtig interpretiert wenn der Ger&auml;tetyp
  10666. bekannt ist. FHEM erh&auml;lt den Ger&auml;tetyp aus einer"pairing request"
  10667. Nachricht, selbst wenn es darauf keine Antwort erh&auml;lt (siehe <a
  10668. href="#hmPairSerial">hmPairSerial</a> und <a
  10669. href="#hmPairForSec">hmPairForSec</a> um Parinig zu erm&ouml;glichen).
  10670. Alternativ, setzen des richtigen subType sowie Modelattributes, f&uuml;r eine Liste der
  10671. m&ouml;glichen subType-Werte siehe "attr hmdevice ?".</li>
  10672. <a name="HMAES"></a>
  10673. <li>Die sogenannte "AES-Verschl&uuml;sselung" ist eigentlich eine Signaturanforderung: Ist sie
  10674. aktiviert wird ein Aktor den erhaltenen Befehl nur ausf&uuml;hren falls er die korrekte
  10675. Antwort auf eine zuvor durch den Aktor gestellte Anfrage erh&auml;lt. Das bedeutet:
  10676. <ul>
  10677. <li>Die Reaktion auf Befehle ist merklich langsamer, da 3 Nachrichten anstatt einer &uuml;bertragen
  10678. werden bevor der Befehl vom Aktor ausgef&uuml;hrt wird.</li>
  10679. <li>Jeder Befehl sowie seine Best&auml;tigung durch das Ger&auml;t wird in Klartext &uuml;bertragen, ein externer
  10680. Beobachter kennt somit den Status jedes Ger&auml;ts.</li>
  10681. <li>Die eingebaute Firmware ist fehlerhaft: Ein "toggle" Befehl wir ausgef&uuml;hrt <b>bevor</b> die
  10682. entsprechende Antwort auf die Signaturanforderung empfangen wurde, zumindest bei einigen Schaltern
  10683. (HM-LC-Sw1-Pl und HM-LC-SW2-PB-FM).</li>
  10684. <li>Der <a href="#HMLAN">HMLAN</a> Konfigurator beantwortet Signaturanforderungen selbstst&auml;ndig,
  10685. ist dabei die 3-Byte-Adresse einer anderen CCU eingestellt welche noch immer das Standardpasswort hat,
  10686. kann dieser Signaturanfragen korrekt beantworten.</li>
  10687. <li>AES-Verschl&uuml;sselung wird durch HMLAN und CUL unterst&uuml;tzt. Bei Einsatz eines CUL
  10688. ist das Perl-Modul Crypt::Rijndael notwendig. Aufgrund dieser Einschr&auml;nkungen ist der
  10689. Einsatz der Homematic-Verschl&uuml;sselung nicht zu empfehlen!</li>
  10690. </ul>
  10691. </li>
  10692. </ul>
  10693. </ul><br>
  10694. <a name="CUL_HMset"></a><b>Set</b>
  10695. <ul>
  10696. Hinweis: Ger&auml;te die normalerweise nur senden (Fernbedienung/Sensor/etc.) m&uuml;ssen in den
  10697. Pairing/Lern-Modus gebracht werden um die folgenden Befehle zu empfangen.
  10698. <br>
  10699. <br>
  10700. Allgemeine Befehle (verf&uuml;gbar f&uuml;r die meisten HM-Ger&auml;te):
  10701. <ul>
  10702. <li><B>clear &lt;[rssi|readings|register|msgEvents|attack|all]&gt;</B><a name="CUL_HMclear"></a><br>
  10703. Eine Reihe von Variablen kann entfernt werden.<br>
  10704. <ul>
  10705. readings: Alle Messwerte werden gel&ouml;scht, neue Werte werden normal hinzugef&uuml;gt. Kann benutzt werden um alte Daten zu entfernen<br>
  10706. register: Alle in FHEM aufgezeichneten Registerwerte werden entfernt. Dies hat KEINEN Einfluss auf Werte im Ger&auml;t.<br>
  10707. msgEvents: Alle Anchrichtenz&auml;hler werden gel&ouml;scht. Ebenso wird der Befehlsspeicher zur&uuml;ckgesetzt. <br>
  10708. rssi: gesammelte RSSI-Werte werden gel&ouml;scht.<br>
  10709. attack: Einträge bezüglich einer Attack werden gelöscht.<br>
  10710. all: alles oben genannte.<br>
  10711. </ul>
  10712. </li>
  10713. <li><B>getConfig</B><a name="CUL_HMgetConfig"></a><br>
  10714. Liest die Hauptkonfiguration eines HM_Ger&auml;tes aus. Angewendet auf einen Kanal
  10715. erh&auml;lt man Pairing-Information, List0, List1 und List3 des ersten internen Peers.
  10716. Außerdem erh&auml;lt man die Liste der Peers f&uuml;r den gegebenen Kanal. Wenn auf ein Ger&auml;t
  10717. angewendet so bekommt man mit diesem Befehl die vorherigen Informationen f&uuml;r alle
  10718. zugeordneten Kan&auml;le. Ausgeschlossen davon sind Konfigurationen zus&auml;tzlicher Peers.
  10719. <br> Der Befehl ist eine Abk&uuml;rzung f&uuml;r eine Reihe anderer Befehle.
  10720. </li>
  10721. <li><B>getRegRaw [List0|List1|List2|List3|List4|List5|List6]&lt;peerChannel&gt; </B><a name="CUL_HMgetRegRaw"></a><br>
  10722. Auslesen der Rohdaten des Registersatzes. Eine Beschreibung der Register sprengt
  10723. den Rahmen dieses Dokuments.<br>
  10724. Die Register sind in sog. Listen strukturiert welche einen Satz Register enthalten.<br>
  10725. List0: Ger&auml;teeinstellungen z.B: Einstellungen f&uuml;r CUL-Pairing Temperaturlimit eines Dimmers.<br>
  10726. List1: Kanaleinstellungen z.B. ben&ouml;tigte Zeit um Rollo hoch und runter zu fahren.<br>
  10727. List3: "link" Einstellungen - d.h. Einstellungen f&uuml;r Peer-Kanal. Das ist eine große Datenmenge!
  10728. Steuert Aktionen bei Empfang eines Triggers vom Peer.<br>
  10729. List4: Einstellungen f&uuml;r den Kanal (Taster) einer Fernbedienung.<br><br>
  10730. &lt;PeerChannel&gt; verkn&uuml;pfte HMid+ch, z.B. 4 byte (8 stellige) Zahl wie
  10731. '12345601'. Ist verpflichtend f&uuml;r List3 und List4 und kann ausgelassen werden
  10732. f&uuml;r List0 und 1. <br>
  10733. 'all' kann verwendet werden um Daten von jedem mit einem Kanal verkn&uuml;pften Link zu bekommen. <br>
  10734. 'selfxx' wird verwendet um interne Kan&auml;le zu adressieren (verbunden mit den eingebauten Schaltern
  10735. falls vorhanden). xx ist die Kanalnummer in dezimaler Notation.<br>
  10736. Hinweis 1: Ausf&uuml;hrung ist abh&auml;ngig vom Entity. Wenn List1 f&uuml;r ein Ger&auml;t statt einem Kanal
  10737. abgefragt wird gibt der Befehl List1 f&uuml;r alle zugeh&ouml;rigen Kan&auml;le aus.
  10738. List3 mit 'peerChannel = all' gibt alle Verbindungen f&uuml;r alle Kan&auml;le eines Ger&auml;tes zur&uuml;ck.<br>
  10739. Hinweis 2: f&uuml;r 'Sender' siehe auch <a href="#CUL_HMremote">remote</a> <br>
  10740. Hinweis 3: Das Abrufen von Informationen kann dauern - besonders f&uuml;r Ger&auml;te
  10741. mit vielen Kan&auml;len und Verkn&uuml;pfungen. Es kann n&ouml;tig sein das Webinterface manuell neu zu laden
  10742. um die Ergebnisse angezeigt zu bekommen.<br>
  10743. Hinweis 4: Direkte Schalter eines HM-Ger&auml;ts sind standardm&auml;ßig ausgeblendet.
  10744. Dennoch sind sie genauso als Verkn&uuml;pfungen implemetiert. Um Zugriff auf 'internal links'
  10745. zu bekommen ist es notwendig folgendes zu erstellen:<br>
  10746. 'set &lt;name&gt; <a href="#CUL_HMregSet">regSet</a> intKeyVisib visib'<br>
  10747. oder<br>
  10748. 'set &lt;name&gt; <a href="#CUL_HMregBulk">regBulk</a> RegL_0. 2:81'<br>
  10749. Zur&uuml;cksetzen l&auml;sst es sich indem '81' mit '01' ersetzt wird.<br> example:<br>
  10750. <ul><code>
  10751. set mydimmer getRegRaw List1<br>
  10752. set mydimmer getRegRaw List3 all <br>
  10753. </code></ul>
  10754. </li>
  10755. <li><B>getSerial</B><a name="CUL_HMgetSerial"></a><br>
  10756. Auslesen der Seriennummer eines ger&auml;ts und speichern in Attribut serialNr.
  10757. </li>
  10758. <li><B>inhibit [on|off]</B><br>
  10759. Blockieren/Zulassen aller Kanal&auml;nderungen eines Aktors, d.h. Zustand des Aktors ist
  10760. eingefroren bis 'inhibit' wieder deaktiviert wird. 'Inhibit' kann f&uuml;r jeden Aktorkanal
  10761. ausgef&uuml;hrt werden aber nat&uuml;rlich nicht f&uuml;r Sensoren - w&uuml;rde auch keinen Sinn machen.<br>
  10762. Damit ist es praktischerweise m&ouml;glich Nachrichten ebenso wie verkn&uuml;pfte Kanalaktionen
  10763. tempor&auml;r zu unterdr&uuml;cken ohne sie l&ouml;schen zu m&uuml;ssen. <br>
  10764. Beispiele:
  10765. <ul><code>
  10766. # Ausf&uuml;hrung blockieren<br>
  10767. set keymatic inhibit on <br><br>
  10768. </ul></code>
  10769. </li>
  10770. <li><B>pair</B><a name="CUL_HMpair"></a><br>
  10771. Verbinden eines Ger&auml;ts bekannter Seriennummer (z.b. nach einem Reset)
  10772. mit einer FHEM-Zentrale. Diese Zentrale wird normalerweise durch CUL/CUNO,
  10773. HMLAN,... hergestellt.
  10774. Wenn verbunden melden Ger&auml;te ihren Status and FHEM.
  10775. Wenn nicht verbunden wird das Ger&auml;t auf bestimmte Anfragen nicht reagieren
  10776. und auch bestimmte Statusinformationen nicht melden. Pairing geschieht auf
  10777. Ger&auml;teebene. Kan&auml;le k&ouml;nnen nicht unabh&auml;ngig von einem Ger&auml;t mit der Zentrale
  10778. verbunden werden.
  10779. Siehe auch <a href="#CUL_HMgetpair">getPair</a> und
  10780. <a href="#CUL_HMunpair">unpair</a>.<br>
  10781. Nicht das Verbinden (mit einer Zentrale) mit verkn&uuml;pfen (Kanal zu Kanal) oder
  10782. <a href="#CUL_HMpeerChan">peerChan</a> verwechseln.<br>
  10783. </li>
  10784. <li><B>peerBulk</B> &lt;peerch1,peerch2,...&gt; [set|unset]<a name="CUL_HMpeerBulk"></a><br>
  10785. peerBulk f&uuml;gt Peer-Kan&auml;le zu einem Kanal hinzu. Alle Peers einer Liste werden
  10786. dabei hinzugef&uuml;gt.<br>
  10787. Peering setzt die Einstellungen einer Verkn&uuml;pfung auf Standardwerte. Da Peers nicht in Gruppen
  10788. hinzugef&uuml;gt werden werden sie durch HM standardm&auml;ßig als'single' f&uuml;r dieses Ger&auml;t
  10789. angelegt. <br>
  10790. Eine ausgekl&uuml;geltere Funktion wird gegeben durch
  10791. <a href="#CUL_HMpeerChan">peerChan</a>.<br>
  10792. peerBulk l&ouml;scht keine vorhandenen Peers sondern bearbeitet nur die Peerliste.
  10793. Andere bereits angelegt Peers werden nicht ver&auml;ndert.<br>
  10794. peerBulk kann verwendet werden um Peers zu l&ouml;schen indem die <B>unset</B> Option
  10795. mit Standardeinstellungen aufgerufen wird.<br>
  10796. Verwendungszweck dieses Befehls ist haupts&auml;chlich das Wiederherstellen
  10797. von Daten eines Ger&auml;ts.
  10798. Empfehlenswert ist das anschließende Wiederherstellen der Registereinstellung
  10799. mit <a href="#CUL_HMregBulk">regBulk</a>. <br>
  10800. Beispiel:<br>
  10801. <ul><code>
  10802. set myChannel peerBulk 12345601,<br>
  10803. set myChannel peerBulk self01,self02,FB_Btn_04,FB_Btn_03,<br>
  10804. set myChannel peerBulk 12345601 unset # entferne Peer 123456 Kanal 01<br>
  10805. </code></ul>
  10806. </li>
  10807. <li><B>regBulk &lt;reg List&gt;.&lt;peer&gt; &lt;addr1:data1&gt; &lt;addr2:data2&gt;...</B><a name="CUL_HMregBulk"></a><br>
  10808. Dieser Befehl ersetzt das bisherige regRaw. Er erlaubt Register mit Rohdaten zu
  10809. beschreiben. Hauptzweck ist das komplette Wiederherstellen eines zuvor gesicherten
  10810. Registers. <br>
  10811. Werte k&ouml;nnen mit <a href="#CUL_HMgetConfig">getConfig</a> ausgelesen werden. Die
  10812. zur&uuml;ckgegebenen Werte k&ouml;nnen direkt f&uuml;r diesen Befehl verwendet werden.<br>
  10813. &lt;reg List&gt; bezeichnet die Liste in die geschrieben werden soll. M&ouml;gliches Format
  10814. '00', 'RegL_00', '01'...<br>
  10815. &lt;peer&gt; ist eine optionale Angabe falls die Liste ein Peer ben&ouml;tigt.
  10816. Der Peer kann als Kanalname oder als 4-Byte (8 chars) HM-Kanal ID angegeben
  10817. werden.<br>
  10818. &lt;addr1:data1&gt; ist die Liste der Register im Hex-Format.<br>
  10819. Beispiel:<br>
  10820. <ul><code>
  10821. set myChannel regBulk RegL_00. 02:01 0A:17 0B:43 0C:BF 15:FF 00:00<br>
  10822. RegL_03.FB_Btn_07
  10823. 01:00 02:00 03:00 04:32 05:64 06:00 07:FF 08:00 09:FF 0A:01 0B:44 0C:54 0D:93 0E:00 0F:00 11:C8 12:00 13:00 14:00 15:00 16:00 17:00 18:00 19:00 1A:00 1B:00 1C:00 1D:FF 1E:93 1F:00 81:00 82:00 83:00 84:32 85:64 86:00 87:FF 88:00 89:FF 8A:21 8B:44 8C:54 8D:93 8E:00 8F:00 91:C8 92:00 93:00 94:00 95:00 96:00 97:00 98:00 99:00 9A:00 9B:00 9C:00 9D:05 9E:93 9F:00 00:00<br>
  10824. set myblind regBulk 01 0B:10<br>
  10825. set myblind regBulk 01 0C:00<br>
  10826. </code></ul>
  10827. myblind setzt die maximale Zeit f&uuml;r das Hochfahren der Rollos auf 25,6 Sekunden
  10828. </li>
  10829. <li><B>regSet [prep|exec] &lt;regName&gt; &lt;value&gt; &lt;peerChannel&gt;</B><a name="CUL_HMregSet"></a><br>
  10830. F&uuml;r einige Hauptregister gibt es eine lesbarere Version die Registernamen &lt;regName&gt;
  10831. und Wandlung der Werte enth&auml;lt. Nur ein Teil der Register wird davon unterst&uuml;tzt.<br>
  10832. Der optionale Parameter [prep|exec] erlaubt das Packen von Nachrichten und verbessert damit
  10833. deutlich die Daten&uuml;bertragung.
  10834. Benutzung durch senden der Befehle mit Parameter "prep". Daten werden dann f&uuml;r das Senden gesammelt.
  10835. Der letzte Befehl muss den Parameter "exec" habe um die Information zu &uuml;bertragen.<br>
  10836. &lt;value&gt; enth&auml;lt die Daten in menschenlesbarer Form die in das Register geschrieben werden.<br>
  10837. &lt;peerChannel&gt; wird ben&ouml;tigt falls das Register 'peerChan' basiert definiert wird.
  10838. Kann ansonsten auf '0' gesetzt werden. Siehe <a
  10839. href="#CUL_HMgetRegRaw">getRegRaw</a> f&uuml;r komplette Definition.<br>
  10840. Unterst&uuml;tzte Register eines Ger&auml;ts k&ouml;nnen wie folgt bestimmt werden:<br>
  10841. <ul><code>set regSet ? 0 0</code></ul>
  10842. Eine verk&uuml;rzte Beschreibung der Register wird zur&uuml;ckgegeben mit:<br>
  10843. <ul><code>set regSet &lt;regname&gt; ? 0</code></ul>
  10844. </li>
  10845. <li><B>reset</B><a name="CUL_HMreset"></a><br>
  10846. R&uuml;cksetzen des Ger&auml;ts auf Werkseinstellungen. Muss danach erneut verbunden werden um es
  10847. mit FHEM zu nutzen.
  10848. </li>
  10849. <li><B>sign [on|off]</B><a name="CUL_HMsign"></a><br>
  10850. Ein- oder ausschalten der Signierung (auch "AES-Verschl&uuml;sselung" genannt, siehe <a
  10851. href="#HMAES">note</a>). Achtung: Wird das Ger&auml;t &uuml;ber einen CUL eingebunden, ist schalten (oder
  10852. deaktivieren der Signierung) nur m&ouml;glich, wenn das Perl-Modul Crypt::Rijndael installiert ist.
  10853. </li>
  10854. <li><B>statusRequest</B><a name="CUL_HMstatusRequest"></a><br>
  10855. Aktualisieren des Ger&auml;testatus. F&uuml;r mehrkanalige Ger&auml;te sollte dies kanalbasiert
  10856. erfolgen.
  10857. </li>
  10858. <li><B>unpair</B><a name="CUL_HMunpair"></a><br>
  10859. Aufheben des "Pairings", z.B. um das verbinden mit einem anderen Master zu erm&ouml;glichen.
  10860. Siehe <a href="#CUL_HMpair">pair</a> f&uuml;r eine Beschreibung.</li>
  10861. <li><B>virtual &lt;Anzahl an Kn&ouml;pfen&gt;</B><a name="CUL_HMvirtual"></a><br>
  10862. Konfiguriert eine vorhandene Schaltung als virtuelle Fernbedienung. Die Anzahl der anlegbaren
  10863. Kn&ouml;pfe ist 1 - 255. Wird der Befehl f&uuml;r die selbe Instanz erneut aufgerufen werden Kn&ouml;pfe
  10864. hinzugef&uuml;gt. <br>
  10865. Beispiel f&uuml;r die Anwendung:
  10866. <ul><code>
  10867. define vRemote CUL_HM 100000 # die gew&auml;hlte HMid darf nicht in Benutzung sein<br>
  10868. set vRemote virtual 20 # definiere eine Fernbedienung mit 20 Kn&ouml;pfen<br>
  10869. set vRemote_Btn4 peerChan 0 &lt;actorchannel&gt; # verkn&uuml;pft Knopf 4 und 5 mit dem gew&auml;hlten Kanal<br>
  10870. set vRemote_Btn4 press<br>
  10871. set vRemote_Btn5 press long<br>
  10872. </code></ul>
  10873. siehe auch <a href="#CUL_HMpress">press</a>
  10874. </li>
  10875. <li><B>deviceRename &lt;newName&gt;</B><a name="CUL_HMdeviceRename"></a><br>
  10876. benennt das Device und alle seine Kan&auml;le um.
  10877. </li>
  10878. <li><B>fwUpdate [onlyEnterBootLoader] &lt;filename&gt; [&lt;waitTime&gt;]</B><br>
  10879. update Fw des Device. Der User muss das passende FW file bereitstellen.
  10880. waitTime ist optional. Es ist die Wartezeit, um das Device manuell in den FW-update-mode
  10881. zu versetzen.<br>
  10882. "onlyEnterBootLoader" schickt das Device in den Booloader so dass es vom eq3 Firmware Update
  10883. Tool geflashed werden kann. Haupts&auml;chlich f&uuml;r Unterputz-Aktoren in Verbindung mit
  10884. FHEM Installationen die ausschliesslich HM-LANs nutzen interessant.
  10885. </li>
  10886. </ul>
  10887. <br>
  10888. <B>subType abh&auml;ngige Befehle:</B>
  10889. <ul>
  10890. <br>
  10891. <li>switch
  10892. <ul>
  10893. <li><B>on</B> <a name="CUL_HMon"> </a> - setzt Wert auf 100%</li>
  10894. <li><B>off</B><a name="CUL_HMoff"></a> - setzt Wert auf 0%</li>
  10895. <li><B>on-for-timer &lt;sec&gt;</B><a name="CUL_HMonForTimer"></a> -
  10896. Schaltet das Ger&auml;t f&uuml;r die gew&auml;hlte Zeit in Sekunden [0-85825945] an.<br> Hinweis:
  10897. off-for-timer wie bei FS20 wird nicht unterst&uuml;tzt. Kann aber &uuml;ber Kanalregister
  10898. programmiert werden.</li>
  10899. <li><B>on-till &lt;time&gt;</B><a name="CUL_HMonTill"></a> - einschalten bis zum angegebenen Zeitpunkt.<br>
  10900. <ul><code>set &lt;name&gt; on-till 20:32:10<br></code></ul>
  10901. Das momentane Maximum f&uuml;r eine Endzeit liegt bei 24 Stunden.<br>
  10902. </li>
  10903. <li><B>pressL &lt;peer&gt; [&lt;repCount&gt;] [&lt;repDelay&gt;] </B><a name="CUL_HMpressL"></a><br>
  10904. simuliert einen Tastendruck eines lokalen oder anderen peers.<br>
  10905. <B>&lt;peer&gt;</B> peer auf den der Tastendruck bezogen wird. <br>
  10906. <B>&lt;repCount&gt;</B> automatische Wiederholungen des long press. <br>
  10907. <B>&lt;repDelay&gt;</B> timer zwischen den Wiederholungen. <br>
  10908. <B>Beispiel:</B>
  10909. <code>
  10910. set actor pressL FB_Btn01 # trigger long peer FB button 01<br>
  10911. set actor pressL FB_chn-8 # trigger long peer FB button 08<br>
  10912. set actor pressL self01 # trigger short des internen peers 01<br>
  10913. set actor pressL fhem02 # trigger short des FHEM channel 2<br>
  10914. </code>
  10915. </li>
  10916. <li><B>pressS &lt;peer&gt;</B><a name="CUL_HMpressS"></a><br>
  10917. simuliert einen kurzen Tastendruck entsprechend peerL
  10918. </li>
  10919. <li><B>eventL &lt;peer&gt; &lt;condition&gt; [&lt;repCount&gt;] [&lt;repDelay&gt;] </B><a name="CUL_HMeventL"></a><br>
  10920. simuliert einen Event mit zusätzlichem Wert.<br>
  10921. <B>&lt;peer&gt;</B> peer auf den der Tastendruck bezogen wird.<br>
  10922. <B>&lt;codition&gt;</B>wert des Events, 0..255 <br>
  10923. <B>Beispiel:</B>
  10924. <code>
  10925. set actor eventL md 30 # trigger vom Bewegungsmelder mit Wert 30<br>
  10926. </code>
  10927. </li>
  10928. <li><B>eventS &lt;peer&gt; &lt;condition&gt; </B><a name="CUL_HMeventS"></a><br>
  10929. simuliert einen kurzen Event eines Peers des actors. Typisch senden Sensoren nur short Events.
  10930. </li>
  10931. <br>
  10932. </ul>
  10933. </li>
  10934. <li>dimmer, blindActuator<br>
  10935. Dimmer k&ouml;nnen virtuelle Kan&auml;le unterst&uuml;tzen. Diese werden automatisch angelegt falls vorhanden.
  10936. Normalerweise gibt es 2 virtuelle Kan&auml;le zus&auml;tzlich zum prim&auml;ren Kanal. Virtuelle Dimmerkan&auml;le sind
  10937. standardm&auml;ßig deaktiviert, k&ouml;nnen aber parallel zum ersten Kanal benutzt werden um das Licht zu steuern. <br>
  10938. Die virtuellen Kan&auml;le haben Standardnamen SW&lt;channel&gt;_V&lt;nr&gt; z.B. Dimmer_SW1_V1 and Dimmer_SW1_V2.<br>
  10939. Virtuelle Dimmerkan&auml;le unterscheiden sich komplett von virtuellen Kn&ouml;pfen und Aktoren in FHEM, sind aber
  10940. Teil des HM-Ger&auml;ts. Dokumentation und M&ouml;glichkeiten w&uuml;rde hier aber zu weit f&uuml;hren.<br>
  10941. <ul>
  10942. <li><B>0 - 100 [on-time] [ramp-time]</B><br>
  10943. Setzt den Aktor auf den gegeben Wert (In Prozent)
  10944. mit einer Aufl&ouml;sung von 0.5.<br>
  10945. Bei Dimmern ist optional die Angabe von "on-time" und "ramp-time" m&ouml;glich, beide in Sekunden mit 0.1s Abstufung.<br>
  10946. "On-time" verh&auml;lt sich analog dem "on-for-timer".<br>
  10947. "Ramp-time" betr&auml;gt standardm&auml;ßig 2.5s, 0 bedeutet umgehend.<br>
  10948. </li>
  10949. <li><B><a href="#CUL_HMon">on</a></B></li>
  10950. <li><B><a href="#CUL_HMoff">off</a></B></li>
  10951. <li><B><a href="#CUL_HMpress">press &lt;[short|long]&gt;&lt;[on|off]&gt;</a></B></li>
  10952. <li><B><a href="#CUL_HMtoggle">toggle</a></B></li>
  10953. <li><B>toggleDir</B><a name="CUL_HMtoggleDir"></a> - toggelt die fahrtrichtung des Rollo-Aktors.
  10954. Es wird umgeschaltet zwischen auf/stop/ab/stop</li>
  10955. <li><B><a href="#CUL_HMonForTimer">on-for-timer &lt;sec&gt;</a></B> - Nur Dimmer! <br></li>
  10956. <li><B><a href="#CUL_HMonTill">on-till &lt;time&gt;</a></B> - Nur Dimmer! <br></li>
  10957. <li><B>stop</B> - Stopt Bewegung (Rollo) oder Dimmerrampe</li>
  10958. <li><B>old</B> - schaltet auf den vorigen Wert zurück. Nur dimmer. </li>
  10959. <li><B>pct &lt;level&gt [&lt;ontime&gt] [&lt;ramptime&gt]</B> - setzt Aktor auf gew&uuml;nschten <B>absolut Wert</B>.<br>
  10960. Optional k&ouml;nnen f&uuml;r Dimmer "ontime" und "ramptime" angegeben werden.<br>
  10961. "Ontime" kann dabei in Sekunden angegeben werden. Kann auch als Endzeit angegeben werden im Format hh:mm:ss
  10962. </li>
  10963. <li><B>up [changeValue] [&lt;ontime&gt] [&lt;ramptime&gt]</B> Einen Schritt hochdimmen.</li>
  10964. <li><B>down [changeValue] [&lt;ontime&gt] [&lt;ramptime&gt]</B> Einen Schritt runterdimmen.<br>
  10965. "changeValue" ist optional und gibt den zu &auml;ndernden Wert in Prozent an. M&ouml;gliche Abstufung dabei ist 0.5%, Standard ist 10%. <br>
  10966. "ontime" ist optional und gibt an wielange der Wert gehalten werden soll. '0' bedeutet endlos und ist Standard.<br>
  10967. "ramptime" ist optional und definiert die Zeit bis eine &auml;nderung den neuen Wert erreicht. Hat nur f&uuml;r Dimmer Bedeutung.
  10968. <br></li>
  10969. </ul>
  10970. <br>
  10971. </li>
  10972. <li>remotes, pushButton<a name="CUL_HMremote"></a><br>
  10973. Diese Ger&auml;teart reagiert nicht auf Anfragen, außer sie befinden sich im Lernmodus. FHEM reagiert darauf
  10974. indem alle Anfragen gesammelt werden bis der Lernmodus detektiert wird. Manuelles Eingreifen durch
  10975. den Benutzer ist dazu n&ouml;tig. Ob Befehle auf Ausf&uuml;hrung warten kann auf Ger&auml;teebene mit dem Parameter
  10976. 'protCmdPend' abgefragt werden.
  10977. <ul>
  10978. <li><B>trgEventS [all|&lt;peer&gt;] &lt;condition&gt;</B><a name="CUL_HMtrgEventS"></a><br>
  10979. Initiiert ein eventS fuer die peer entity. Wenn <B>all</B> ausgewählt ist wird das Kommando bei jedem der Peers ausgeführt. Siehe auch <a href="CUL_HMeventS">eventS</a><br>
  10980. <B>&lt;condition&gt;</B>: Ist der Wert welcher mit dem Event versendet wird. Bei einem Bewegungsmelder ist das bspw. die Helligkeit.
  10981. </li>
  10982. <li><B>trgEventL [all|&lt;peer&gt;] &lt;condition&gt;</B><a name="CUL_HMtrgEventL"></a><br>
  10983. Initiiert ein eventL fuer die peer entity. Wenn <B>all</B> ausgewählt ist wird das Kommando bei jedem der Peers ausgeführt. Siehe auch <a href="CUL_HMeventL">eventL</a><br>
  10984. <B>&lt;condition&gt;</B>: is the condition being transmitted with the event. E.g. the brightness in case of a motion detector.
  10985. </li>
  10986. <li><B>trgPressS [all|&lt;peer&gt;] </B><a name="CUL_HMtrgPressS"></a><br>
  10987. Initiiert ein pressS fuer die peer entity. Wenn <B>all</B> ausgewählt ist wird das Kommando bei jedem der Peers ausgeführt. Siehe auch <a href="CUL_HMpressS">pressS</a><br>
  10988. </li>
  10989. <li><B>trgPressL [all|&lt;peer&gt;] </B><a name="CUL_HMtrgPressL"></a><br>
  10990. Initiiert ein pressL fuer die peer entity. Wenn <B>all</B> ausgewählt ist wird das Kommando bei jedem der Peers ausgeführt. Siehe auch <a href="CUL_HMpressL">pressL</a><br>
  10991. </li>
  10992. <li><B>peerChan &lt;btn_no&gt; &lt;actChan&gt; [single|<u>dual</u>|reverse]
  10993. [<u>set</u>|unset] [<u>both</u>|actor|remote]</B><a name="CUL_HMpeerChan"></a><br>
  10994. "peerChan" richtet eine Verbindung zwischen Sender-<B>Kanal</B> und
  10995. Aktor-<B>Kanal</B> ein, bei HM "link" genannt. "Peering" darf dabei nicht
  10996. mit "pairing" verwechselt werden.<br>
  10997. <B>Pairing</B> bezeichnet das Zuordnen eines <B>Ger&auml;ts</B> zu einer Zentrale.<br>
  10998. <B>Peering</B> bezeichnet das faktische Verbinden von <B>Kan&auml;len</B>.<br>
  10999. Peering erlaubt die direkte Interaktion zwischen Sender und Aktor ohne den Einsatz einer CCU<br>
  11000. Peering eines Senderkanals veranlaßt den Sender nach dem Senden eines Triggers auf die
  11001. Best&auml;tigung eines - jeden - Peers zu warten. Positives Feedback (z.B. gr&uuml;ne LED)
  11002. gibt es dabei nur wenn alle Peers den Befehl best&auml;tigt haben.<br>
  11003. Peering eines Aktorkanals richtet dabei einen Satz von Parametern ein welche die auszuf&uuml;hrenden Aktionen
  11004. definieren wenn ein Trigger dieses Peers empfangen wird. Dies bedeutet: <br>
  11005. - nur Trigger von Peers werden ausgef&uuml;hrt<br>
  11006. - die auszuf&uuml;hrende Aktion muss f&uuml;r den zugeh&ouml;rigen Trigger eines Peers definiert werden<br>
  11007. Ein Aktorkanal richtet dabei eine Standardaktion beim Peering ein - diese h&auml;ngt vom Aktor ab.
  11008. Sie kann ebenfalls davon abh&auml;ngen ob ein oder zwei Tasten <B>ein einem Befehl</B> gepeert werden.
  11009. Peert man einen Schalter mit 2 Tasten kann eine Taste f&uuml;r 'on' und eine andere f&uuml;r 'off' angelegt werden.
  11010. Wenn nur eine Taste definiert wird ist die Funktion wahrscheinlich 'toggle'.<br>
  11011. Die Funktion kann durch programmieren des Register (vom Aktor abh&auml;ngig) ge&auml;ndert werden.<br>
  11012. Auch wenn der Befehl von einer Fernbedienung oder einem Taster kommt hat er direkten Effekt auf
  11013. den Aktor. Das Peering beider Seiten ist quasi unabh&auml;ngig und hat unterschiedlich Einfluss auf
  11014. Sender und Empf&auml;nger.<br>
  11015. Peering eines Aktorkanals mit mehreren Senderkan&auml;len ist ebenso m&ouml;glich wie das eines Senderkanals
  11016. mit mehreren Empf&auml;ngerkan&auml;len.<br>
  11017. &lt;actChan&gt; ist der zu verkn&uuml;pfende Aktorkanal.<br>
  11018. &lt;btn_no&gt; ist der zu verkn&uuml;pfende Senderkanal (Knopf). Wird
  11019. 'single' gew&auml;hlt werden die Tasten von 1 an gez&auml;hlt. F&uuml;r 'dual' ist btn_no
  11020. die Nummer des zu verwendenden Tasterpaares. Z.B. ist '3' iim Dualmodus das
  11021. dritte Tasterpaar welches mit Tasten 5 und 6 im Singlemodus &uuml;bereinstimmt.<br>
  11022. Wird der Befehl auf einen Kanal angewendet wird btn_no igroriert.
  11023. Muss gesetzt sein, sollte dabei 0 sein.<br>
  11024. [single|dual]: Dieser Modus bewirkt das Standardverhalten des Aktors bei Benutzung eines Tasters. Ein Dimmer
  11025. kann z.B. an einen einzelnen oder ein Paar von Tastern angelernt werden. <br>
  11026. Standardeinstellung ist "dual".<br>
  11027. 'dual' (default) Schalter verkn&uuml;pft zwei Taster mit einem Aktor. Bei einem Dimmer
  11028. bedeutet das ein Taster f&uuml;r hoch- und einer f&uuml;r runterdimmen. <br>
  11029. 'reverse' identisch zu dual - nur die Reihenfolge der Buttons ist gedreht<br>
  11030. 'single' benutzt nur einen Taster des Senders. Ist z.B. n&uuml;tzlich f&uuml;r einen einfachen Schalter
  11031. der nur zwischen an/aus toggled. Aber auch ein Dimmer kann an nur einen Taster angelernt werden.<br>
  11032. [set|unset]: W&auml;hlt aus ob Peer hinzugef&uuml;gt oder entfernt werden soll.<br>
  11033. Hinzuf&uuml;gen ist Standard.<br>
  11034. 'set' stellt Peers f&uuml;r einen Kanal ein.<br>
  11035. 'unset' entfernt Peer f&uuml;r einen Kanal.<br>
  11036. [actor|remote|both] beschr&auml;nkt die Ausf&uuml;hrung auf Aktor oder Fernbedienung.
  11037. Das erm&ouml;glicht dem Benutzer das entfernen des Peers vom Fernbedienungskanal ohne
  11038. die Einstellungen am Aktor zu entfernen.<br>
  11039. Standardm&auml;ßig gew&auml;hlt ist "both" f&uuml;r beides.<br>
  11040. Example:
  11041. <ul><code>
  11042. set myRemote peerChan 2 mySwActChn single set #Peer zweiten Knopf mit Aktorkanal<br>
  11043. set myRmtBtn peerChan 0 mySwActChn single set #myRmtBtn ist ein Knopf der Fernbedienung. '0' wird hier nicht verarbeitet<br>
  11044. set myRemote peerChan 2 mySwActChn dual set #Verkn&uuml;pfe Kn&ouml;pfe 3 und 4<br>
  11045. set myRemote peerChan 3 mySwActChn dual unset #Entferne Peering f&uuml;r Kn&ouml;pfe 5 und 6<br>
  11046. set myRemote peerChan 3 mySwActChn dual unset aktor #Entferne Peering f&uuml;r Kn&ouml;pfe 5 und 6 nur im Aktor<br>
  11047. set myRemote peerChan 3 mySwActChn dual set remote #Verkn&uuml;pfe Kn&ouml;pfe 5 und 6 nur mit Fernbedienung. Linkeinstellungen mySwActChn werden beibehalten.<br>
  11048. </code></ul>
  11049. </li>
  11050. </ul>
  11051. </li>
  11052. <li>virtual<a name="CUL_HMvirtual"></a><br>
  11053. <ul>
  11054. <li><B><a href="#CUL_HMpeerChan">peerChan</a></B> siehe remote</li>
  11055. <li><B><a name="CUL_HMpress"></a>press [long|short] [&lt;peer&gt;] [&lt;repCount&gt;] [&lt;repDelay&gt;] </B>
  11056. <ul>
  11057. Simuliert den Tastendruck am Aktor eines gepeerted Sensors
  11058. <li>[long|short] soll ein langer oder kurzer Taastendrucl simuliert werden? Default ist kurz. </li>
  11059. <li>[&lt;peer&gt;] legt fest, wessen peer's trigger simuliert werden soll.Default ist self(channelNo).</li>
  11060. <li>[&lt;repCount&gt;] nur gueltig fuer long. wie viele messages sollen gesendet werden? (Laenge des Button press). Default ist 1.</li>
  11061. <li>[&lt;repDelay&gt;] nur gueltig fuer long. definiert die Zeit zwischen den einzelnen Messages. </li>
  11062. </ul>
  11063. </li>
  11064. <li><B>virtTemp &lt;[off -10..50]&gt;<a name="CUL_HMvirtTemp"></a></B>
  11065. Simuliert ein Thermostat. Wenn mit einem Ger&auml;t gepeert wird periodisch eine Temperatur gesendet,
  11066. solange bis "off" gew&auml;hlt wird. Siehe auch <a href="#CUL_HMvirtHum">virtHum</a><br>
  11067. </li>
  11068. <li><B>virtHum &lt;[off -10..50]&gt;<a name="CUL_HMvirtHum"></a></B>
  11069. Simuliert den Feuchtigkeitswert eines Thermostats. Wenn mit einem Ger&auml;t verkn&uuml;pft werden periodisch
  11070. Luftfeuchtigkeit undTemperatur gesendet, solange bis "off" gew&auml;hlt wird. Siehe auch <a href="#CUL_HMvirtTemp">virtTemp</a><br>
  11071. </li>
  11072. <li><B>valvePos &lt;[off 0..100]&gt;<a name="CUL_HMvalvePos"></a></B>
  11073. steuert einen Ventilantrieb<br>
  11074. </li>
  11075. </ul>
  11076. </li>
  11077. <li>smokeDetector<br>
  11078. Hinweis: All diese Befehle funktionieren momentan nur wenn mehr als ein Rauchmelder
  11079. vorhanden ist, und diese gepeert wurden um eine Gruppe zu bilden. Um die Befehle abzusetzen
  11080. muss der Master dieser gruppe verwendet werden, und momentan muss man raten welcher der Master ist.<br>
  11081. smokeDetector kann folgendermaßen in Gruppen eingeteilt werden:
  11082. <a href="#CUL_HMpeerChan">peerChan</a>. Alle Mitglieder m&uuml;ssen mit dem Master verkn&uuml;pft werden. Auch der
  11083. Master muss mit peerChan zur Gruppe zugef&uuml;gt werden - z.B. mit sich selbst verkn&uuml;pft! Dadurch hat man volle
  11084. Kontrolle &uuml;ber die Gruppe und muss nicht raten.<br>
  11085. <ul>
  11086. <li><B>teamCall</B> - f&uuml;hrt einen Netzwerktest unter allen Gruppenmitgliedern aus</li>
  11087. <li><B>teamCallBat</B> - Simuliert einen low-battery alarm</li>
  11088. <li><B>alarmOn</B> - l&ouml;st einen Alarm aus</li>
  11089. <li><B>alarmOff</B> - schaltet den Alarm aus</li>
  11090. </ul>
  11091. </li>
  11092. <li>4Dis (HM-PB-4DIS-WM|HM-PB-4DIS-WM|HM-RC-Dis-H-x-EU|ROTO_ZEL-STG-RM-DWT-10)
  11093. <ul>
  11094. <li><B>text &lt;btn_no&gt; [on|off] &lt;text1&gt; &lt;text2&gt;</B><br>
  11095. Zeigt Text auf dem Display eines Ger&auml;ts an. F&uuml;r diesen Zweck muss zuerst ein set-Befehl
  11096. (oder eine Anzahl davon) abgegeben werden, dann k&ouml;nnen im "teach-in" Men&uuml; des 4Dis mit
  11097. "Central" Daten &uuml;bertragen werden.<br>
  11098. Falls auf einen Kanal angewendet d&uuml;rfen btn_no und on|off nicht verwendet werden, nur
  11099. reiner Text.<br>
  11100. \_ wird durch ein Leerzeichen ersetzt.<br>
  11101. Beispiel:
  11102. <ul>
  11103. <code>
  11104. set 4Dis text 1 on On Lamp<br>
  11105. set 4Dis text 1 off Kitchen Off<br>
  11106. <br>
  11107. set 4Dis_chn4 text Kitchen Off<br>
  11108. </code>
  11109. </ul>
  11110. </li>
  11111. </ul>
  11112. <br>
  11113. </li>
  11114. <li>Climate-Control (HM-CC-TC)
  11115. <ul>
  11116. <li><B>desired-temp &lt;temp&gt;</B><br>
  11117. Setzt verschiedene Temperaturen. &lt;temp&gt; muss zwischen 6°C und 30°C liegen, die Aufl&ouml;sung betr&auml;gt 0.5°C.</li>
  11118. <li><B>tempListSat [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  11119. <li><B>tempListSun [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  11120. <li><B>tempListMon [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  11121. <li><B>tempListTue [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  11122. <li><B>tempListThu [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  11123. <li><B>tempListWed [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  11124. <li><B>tempListFri [prep|exec] HH:MM temp ... 24:00 temp</B><br>
  11125. Gibt eine Liste mit Temperaturintervallen an. Bis zu 24 Intervall k&ouml;nnen pro Wochentag definiert werden, die
  11126. Aufl&ouml;sung dabei sind 10 Minuten. Die letzte Zeitangabe muss 24:00 Uhr sein.<br>
  11127. Beispiel: bis 6:00 soll die Temperatur 19°C sein, dann bis 23:00 Uhr 22.5°C, anschließend
  11128. werden bis Mitternacht 19°C gew&uuml;nscht.<br>
  11129. <code> set th tempListSat 06:00 19 23:00 22.5 24:00 19<br></code>
  11130. </li>
  11131. <li><B>partyMode &lt;HH:MM&gt;&lt;durationDays&gt;</B><br>
  11132. setzt die Steuerung f&uuml;r die angegebene Zeit in den Partymodus. Dazu ist die Endzeit sowie <b>Anzahl an Tagen</b>
  11133. die er dauern soll anzugeben. Falls er am n&auml;chsten Tag enden soll ist '1'
  11134. anzugeben<br></li>
  11135. <li><B>sysTime</B><br>
  11136. setzt Zeit des Klimakanals auf die Systemzeit</li>
  11137. </ul><br>
  11138. </li>
  11139. <li>Climate-Control (HM-CC-RT-DN|HM-CC-RT-DN-BoM)
  11140. <ul>
  11141. <li><B>controlMode &lt;auto|boost|day|night&gt;</B><br></li>
  11142. <li><B>controlManu &lt;temp&gt;</B><br></li>
  11143. <li><B>controlParty &lt;temp&gt;&lt;startDate&gt;&lt;startTime&gt;&lt;endDate&gt;&lt;endTime&gt;</B><br>
  11144. setzt die Steuerung in den Partymodus, definiert Temperatur und Zeitrahmen.<br>
  11145. Beispiel:<br>
  11146. <code>set controlParty 15 03-8-13 20:30 5-8-13 11:30</code></li>
  11147. <li><B>sysTime</B><br>
  11148. setzt Zeit des Klimakanals auf die Systemzeit</li>
  11149. <li><B>desired-temp &lt;temp&gt;</B><br>
  11150. Setzt verschiedene Temperaturen. &lt;temp&gt; muss zwischen 6°C und 30°C liegen, die Aufl&ouml;sung betr&auml;gt 0.5°C.</li>
  11151. <li><B>tempListSat [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  11152. <li><B>tempListSun [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  11153. <li><B>tempListMon [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  11154. <li><B>tempListTue [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  11155. <li><B>tempListThu [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  11156. <li><B>tempListWed [prep|exec] HH:MM temp ... 24:00 temp</B><br></li>
  11157. <li><B>tempListFri [prep|exec] HH:MM temp ... 24:00 temp</B><br>
  11158. Gibt eine Liste mit Temperaturintervallen an. Bis zu 24 Intervall k&ouml;nnen pro Wochentag definiert werden, die
  11159. Aufl&ouml;sung dabei sind 10 Minuten. Die letzte Zeitangabe muss immer 24:00 Uhr sein.<br>
  11160. Der optionale Parameter [prep|exec] erlaubt das packen der Nachrichten und verbessert damit deutlich
  11161. die Daten&uuml;bertragung. Besonders n&uuml;tzlich wenn das Ger&auml;t im "Wakeup"-modus betrieben wird.
  11162. Benutzung durch senden der Befehle mit Parameter "prep". Daten werden dann f&uuml;r das Senden gesammelt.
  11163. Der letzte Befehl muss den Parameter "exec" habe um die Information zu &uuml;bertragen.<br>
  11164. Beispiel: bis 6:00 soll die Temperatur 19°C sein, dann bis 23:00 Uhr 22.5°C, anschließend
  11165. werden bis Mitternacht 19°C gew&uuml;nscht.<br>
  11166. <code> set th tempListSat 06:00 19 23:00 22.5 24:00 19<br></code>
  11167. <br>
  11168. <code> set th tempListSat prep 06:00 19 23:00 22.5 24:00 19<br>
  11169. set th tempListSun prep 06:00 19 23:00 22.5 24:00 19<br>
  11170. set th tempListMon prep 06:00 19 23:00 22.5 24:00 19<br>
  11171. set th tempListTue exec 06:00 19 23:00 22.5 24:00 19<br></code>
  11172. </li>
  11173. <li><B>tempListTmpl =>"[verify|restore] [[&lt;file&gt;:]templateName] ...</B><br>
  11174. Die Temperaturlisten f&uuml;r ein oder mehrere Devices k&ouml;nnen in einem File hinterlegt
  11175. werden. Es wird ein template f&uuml;r eine Woche hinterlegt. Der User kann dieses
  11176. template in ein Device schreiben lassen (restore). Er kann auch pr&uuml;fen, ob das Device korrekt
  11177. nach dieser Templist programmiert ist (verify).
  11178. Default Opeartion ist verify.<br>
  11179. Default File ist tempList.cfg.<br>
  11180. Default templateName ist der name der Entity<br>
  11181. Default f&uuml;r file und templateName kann mit dem Attribut <B>tempListTmpl</B> gesetzt werden.<br>
  11182. Beispiel f&uuml;r ein templist File. room1 und room2 sind die Namen 2er Tempaltes:<br>
  11183. <code>entities:room1
  11184. tempListSat>08:00 16.0 15:00 18.0 21:30 19.0 24:00 14.0
  11185. tempListSun>08:00 16.0 15:00 18.0 21:30 19.0 24:00 14.0
  11186. tempListMon>07:00 16.0 16:00 18.0 21:00 19.0 24:00 14.0
  11187. tempListTue>07:00 16.0 13:00 16.0 16:00 18.0 21:00 19.0 24:00 15.0
  11188. tempListWed>07:00 16.0 16:00 18.0 21:00 19.0 24:00 14.0
  11189. tempListThu>07:00 16.0 16:00 18.0 21:00 19.0 24:00 14.0
  11190. tempListFri>07:00 16.0 13:00 16.0 16:00 18.0 21:00 19.0 24:00 14.0
  11191. entities:room2
  11192. tempListSat>08:00 14.0 15:00 18.0 21:30 19.0 24:00 14.0
  11193. tempListSun>08:00 14.0 15:00 18.0 21:30 19.0 24:00 14.0
  11194. tempListMon>07:00 14.0 16:00 18.0 21:00 19.0 24:00 14.0
  11195. tempListTue>07:00 14.0 13:00 16.0 16:00 18.0 21:00 19.0 24:00 15.0
  11196. tempListWed>07:00 14.0 16:00 18.0 21:00 19.0 24:00 14.0
  11197. tempListThu>07:00 14.0 16:00 18.0 21:00 19.0 24:00 14.0
  11198. tempListFri>07:00 14.0 13:00 16.0 16:00 18.0 21:00 19.0 24:00 14.0
  11199. </code>
  11200. Specials:<br>
  11201. <li>none: das Template wird ignoriert</li>
  11202. <li>defaultWeekplan: Es wird als Default jeden Tag 18.0 Grad eingestellt.
  11203. Sinnvoll nutzbar wenn man einen TC als Kontroller nutzt. Der Wochenplan des TC wird dann imlizit genutzt</li>
  11204. </li>
  11205. <li><B>tempTmplSet =>"[[ &lt;file&gt; :]templateName]</B><br>
  11206. Setzt das Attribut und sendet die Änderungen an das Device.
  11207. </li>
  11208. <li><B>templateDel =>" &lt;template&gt; </B><br>
  11209. Löscht eine Templateeintrag an dieser entity.
  11210. </li>
  11211. </ul><br>
  11212. </li>
  11213. <li>OutputUnit (HM-OU-LED16)
  11214. <ul>
  11215. <li><B>led [off|red|green|yellow]</B><br>
  11216. schaltet die LED des Kanals auf die gew&uuml;nschte Farbe. Wird der Befehl auf ein Ger&auml;t angewandt so
  11217. werden alle LEDs auf diese Farbe gesetzt.<br>
  11218. Experten k&ouml;nnen die LEDs separat durch eine 8-stellige Hex-Zahl ansteuern.<br></li>
  11219. <li><B>ilum &lt;Helligkeit&gt;&lt;Dauer&gt; </B><br>
  11220. &lt;Helligkeit&gt; [0-15] der Beleuchtung.<br>
  11221. &lt;Dauer&gt; [0-127] in Sekunden, 0 bedeutet dauernd an.<br></li>
  11222. </ul><br>
  11223. </li>
  11224. <li>OutputUnit (HM-OU-CFM-PL)
  11225. <ul>
  11226. <li><B>led &lt;color&gt;[,&lt;color&gt;..] [&lt;repeat&gt..]</B><br>
  11227. M&ouml;gliche Farben sind [redL|greenL|yellowL|redS|greenS|yellowS|pause]. Eine Folge von Farben
  11228. kann durch trennen der Farbeintr&auml;ge mit ',' eingestellt werden.
  11229. Leerzeichen d&uuml;rfen in der Liste nicht benutzt werden. 'S' bezeichnet kurze und
  11230. 'L' lange Beleuchtungsdauer. <br>
  11231. <b>repeat</b> definiert wie oft die Sequenz ausgef&uuml;hrt werden soll. Standard ist 1.<br>
  11232. </li>
  11233. <li><B>playTone &lt;MP3No&gt[,&lt;MP3No&gt;..] [&lt;repeat&gt;] [&lt;volume&gt;]</B><br>
  11234. Spielt eine Reihe von T&ouml;nen. Die Liste muss mit ',' getrennt werden. Leerzeichen
  11235. d&uuml;rfen in der Liste nicht benutzt werden.<br>
  11236. <b>replay</b> kann verwendet werden um den zuletzt gespielten Klang zu wiederholen.<br>
  11237. <b>repeat</b> definiert wie oft die Sequenz ausgef&uuml;hrt werden soll. Standard ist 1.<br>
  11238. <b>volume</b> kann im Bereich 0..10 liegen. 0 stoppt jeden aktuell gespielten Sound. Standard ist 10 (100%.<br>
  11239. Beispiel:
  11240. <ul><code>
  11241. set cfm_Mp3 playTone 3 # MP3 Titel 3 einmal<br>
  11242. set cfm_Mp3 playTone 3 3 # MP3 Titel 3 dreimal<br>
  11243. set cfm_Mp3 playTone 3 1 5 # MP3 Titel 3 mit halber Lautst&auml;rke<br>
  11244. set cfm_Mp3 playTone 3,6,8,3,4 # MP3 Titelfolge 3,6,8,3,4 einmal<br>
  11245. set cfm_Mp3 playTone 3,6,8,3,4 255# MP3 Titelfolge 3,6,8,3,4 255 mal<br>
  11246. set cfm_Mp3 playTone replay # Wiederhole letzte Sequenz<br>
  11247. <br>
  11248. set cfm_Led led redL 4 # rote LED dreimal lang blinken<br>
  11249. set cfm_Led led redS,redS,redS,redL,redL,redL,redS,redS,redS 255 # SOS 255 mal<br>
  11250. </ul></code>
  11251. </li>
  11252. </ul><br>
  11253. </li>
  11254. <li>HM-RC-19xxx
  11255. <ul>
  11256. <li><B>alarm &lt;count&gt;</B><br>
  11257. sendet eine Alarmnachricht an die Steuerung<br></li>
  11258. <li><B>service &lt;count&gt;</B><br>
  11259. sendet eine Servicenachricht an die Steuerung<br></li>
  11260. <li><B>symbol &lt;symbol&gt; [set|unset]</B><br>
  11261. aktiviert ein verf&uuml;gbares Symbol auf der Steuerung<br></li>
  11262. <li><B>beep [off|1|2|3]</B><br>
  11263. aktiviert T&ouml;ne<br></li>
  11264. <li><B>backlight [off|on|slow|fast]</B><br>
  11265. aktiviert Hintergrundbeleuchtung<br></li>
  11266. <li><B>display &lt;text&gt; comma unit tone backlight &lt;symbol(s)&gt;
  11267. </B><br>
  11268. Steuert das Display der Steuerung<br>
  11269. &lt;text&gt; : bis zu 5 Zeichen <br>
  11270. comma : 'comma' aktiviert das Komma, 'no' l&auml;ßt es aus <br>
  11271. [unit] : setzt Einheitensymbole.
  11272. [off|Proz|Watt|x3|C|x5|x6|x7|F|x9|x10|x11|x12|x13|x14|x15]. Momentan sind
  11273. x3..x15 nicht getestet. <br>
  11274. tone : aktiviert einen von 3 T&ouml;nen [off|1|2|3]<br>
  11275. backlight: l&auml;ßt die Hintergrundbeleuchtung aufblinken [off|on|slow|fast]<br>
  11276. &lt;symbol(s)&gt; aktiviert die Anzeige von Symbolen. Mehrere Symbole
  11277. k&ouml;nnen zu selben Zeit aktiv sein, Verkn&uuml;pfung erfolgt komma-getrennt. Dabei keine
  11278. Leerzeichen verwenden. M&ouml;gliche Symbole:
  11279. [bulb|switch|window|door|blind|scene|phone|bell|clock|arrowUp|arrowDown]<br><br>
  11280. Beispiel:
  11281. <ul><code>
  11282. # "Hello" auf dem Display, Symbol bulb an, Hintergrundbeleuchtung, Ton ausgeben<br>
  11283. set FB1 display Hello no off 1 on bulb<br>
  11284. # "1234,5" anzeigen mit Einheit 'W'. Symbole scene,phone,bell und
  11285. # clock sind aktiv. Hintergrundbeleuchtung blinikt schnell, Ausgabe von Ton 2<br>
  11286. set FB1 display 12345 comma Watt 2 fast scene,phone,bell,clock
  11287. </ul></code>
  11288. </li>
  11289. </ul><br>
  11290. </li>
  11291. <li>HM-Dis-WM55
  11292. <ul>
  11293. <li><B>displayWM help </B><br>
  11294. <B>displayWM [long|short] &lt;text1&gt; &lt;color1&gt; &lt;icon1&gt; ... &lt;text6&gt; &lt;color6&gt; &lt;icon6&gt;</B><br>
  11295. <B>displayWM [long|short] &lt;lineX&gt; &lt;text&gt; &lt;color&gt; &lt;icon&gt;</B><br>
  11296. es können bis zu 6 Zeilen programmiert werden.<br>
  11297. <B>lineX</B> legt die zu ändernde Zeilennummer fest. Es können die 3 Parameter der Zeile geändert werden.<br>
  11298. <B>textNo</B> ist der anzuzeigende Text. Der Inhalt des Texts wird in den Buttonds definiert.
  11299. txt&lt;BtnNo&gt;_&lt;lineNo&gt; referenziert den Button und dessn jeweiligen Zeile.
  11300. Alternativ kann ein bis zu 12 Zeichen langer Freitext angegeben werden<br>
  11301. <B>color</B> kann sein white, red, orange, yellow, green, blue<br>
  11302. <B>icon</B> kann sein off, on, open, closed, error, ok, noIcon<br>
  11303. Example:
  11304. <ul><code>
  11305. set disp01 displayWM short txt02_2 green noIcon txt10_1 red error txt05_2 yellow closed txt02_2 orange open <br>
  11306. set disp01 displayWM long line3 txt02_2 green noIcon<br>
  11307. set disp01 displayWM long line2 nc yellow noIcon<br>
  11308. set disp01 displayWM long line6 txt02_2<br>
  11309. set disp01 displayWM long line1 nc nc closed<br>
  11310. </ul></code>
  11311. </li>
  11312. </ul><br>
  11313. </li>
  11314. <li>HM-Dis-EP-WM55
  11315. <ul>
  11316. <li><B>displayEP help </B><br>
  11317. <B>displayEP &lt;text1,icon1:text2,icon2:text3,icon3&gt; &lt;sound&gt; &lt;repetition&gt; &lt;pause&gt; &lt;signal&gt;</B><br>
  11318. bis zu 3 Zeilen werden adressiert.<br>
  11319. Wenn help eingegeben wird wird eine <i><B>hilfe</B></i> zum Kommando ausgegeben. Optionen der Parameter werden ausgegeben.<br>
  11320. <B>textx</B> 12 char text für die Zeile.
  11321. Wenn leer wird der Wert gemäß Reading genutzt. Typisch bedeuted es, dass keine Änderung stattfindet.
  11322. text0-9 zeigt den vordefinierten Wert der Kanäle 4 bis 8 an.
  11323. 0xHH erlaubt die anzeige eines hex Zeichens.<br>
  11324. <B>iconx</B> Icon der Zeile.
  11325. Typisch bedeuted es, dass keine Änderung stattfindet.<br>
  11326. <B>sound</B> sound zum Abspielen.<br>
  11327. <B>repetition</B> 0..15 <br>
  11328. <B>pause</B> 1..160<br>
  11329. <B>signal</B> Signalfarbe zum Anzeigen<br>
  11330. <br>
  11331. <B>Note: param reWriteDisplayxx</B> <br>
  11332. <li>
  11333. Beim Druck einer Taste ueberschreibt das Geraet diemittleren 3 Zeilen. Wenn da Attribut <br>
  11334. attr chan param reWriteDisplayxx<br>
  11335. gesetzt ist werden die 3 Zeilen nach xx Sekunden auf den Orginalwert zurück geschrieben.<br>
  11336. </li>
  11337. </li>
  11338. </ul><br>
  11339. </li>
  11340. <li>keyMatic<br><br>
  11341. <ul>Keymatic verwendet eine AES-signierte Kommunikation. Die Steuerung von KeyMatic
  11342. ist mit HMLAN und mit CUL m&ouml;glich.
  11343. Um die Keymatic mit einem CUL zu steuern, muss das Perl-Modul Crypt::Rijndael
  11344. installiert sein.</ul><br>
  11345. <ul>
  11346. <li><B>lock</B><br>
  11347. Schließbolzen f&auml;hrt in Zu-Position<br></li>
  11348. <li><B>unlock [sec]</B><br>
  11349. Schließbolzen f&auml;hrt in Auf-Position.<br>
  11350. [sec]: Stellt die Verz&ouml;gerung ein nach der sich das Schloss automatisch wieder verschließt.<br>
  11351. 0 - 65535 Sekunden</li>
  11352. <li><B>open [sec]</B><br>
  11353. Entriegelt die T&uuml;r sodass diese ge&ouml;ffnet werden kann.<br>
  11354. [sec]: Stellt die Verz&ouml;gerung ein nach der sich das Schloss automatisch wieder
  11355. verschließt.<br>0 - 65535 Sekunden</li>
  11356. </ul>
  11357. </li>
  11358. <li>winMatic <br><br>
  11359. <ul>winMatic arbeitet mit 2 Kan&auml;len, einem f&uuml;r die Fenstersteuerung und einem f&uuml;r den Akku.</ul><br>
  11360. <ul>
  11361. <li><B>level &lt;level&gt; &lt;relockDelay&gt; &lt;speed&gt;</B><br>
  11362. stellt den Wert ein. <br>
  11363. &lt;level&gt;: Bereich ist 0% bis 100%<br>
  11364. &lt;relockDelay&gt;: Spanne reicht von 0 bis 65535 Sekunden. 'ignore' kann verwendet werden um den Wert zu ignorieren.<br>
  11365. &lt;speed&gt;: Bereich ist 0% bis 100%<br>
  11366. </li>
  11367. <li><B>stop</B><br>
  11368. stopt die Bewegung<br>
  11369. </li>
  11370. </ul>
  11371. </li>
  11372. <li>CCU_FHEM<br>
  11373. <ul>
  11374. <li>defIgnUnknown<br>
  11375. Definieren die unbekannten Devices und setze das Attribut ignore.
  11376. Ddann loesche die Readings. <br>
  11377. </li>
  11378. </ul>
  11379. </li>
  11380. <li>HM-Sys-sRP-Pl<br>
  11381. legt Eintr&auml;ge f&uuml;r den Repeater an. Bis zu 36 Eintr&auml;ge k&ouml;nnen angelegt werden.
  11382. <ul>
  11383. <li><B>setRepeat &lt;entry&gt; &lt;sender&gt; &lt;receiver&gt; &lt;broadcast&gt;</B><br>
  11384. &lt;entry&gt; [1..36] Nummer des Eintrags in der Tabelle.<br>
  11385. &lt;sender&gt; Name oder HMid des Senders oder der Quelle die weitergeleitet werden soll<br>
  11386. &lt;receiver&gt; Name oder HMid des Empf&auml;ngers oder Ziels an das weitergeleitet werden soll<br>
  11387. &lt;broadcast&gt; [yes|no] definiert ob Broadcasts von einer ID weitergeleitet werden sollen.<br>
  11388. <br>
  11389. Kurzanwendung: <br>
  11390. <code>setRepeat setAll 0 0 0<br></code>
  11391. schreibt die gesamte Liste der Ger&auml;te neu. Daten kommen vom Attribut repPeers. <br>
  11392. Das Attribut repPeers hat folgendes Format:<br>
  11393. src1:dst1:[y/n],src2:dst2:[y/n],src2:dst2:[y/n],...<br>
  11394. <br>
  11395. Formatierte Werte von repPeer:<br>
  11396. <ul>
  11397. Number src dst broadcast verify<br>
  11398. number: Nummer des Eintrags in der Liste<br>
  11399. src: Ursprungsger&auml;t der Nachricht - aus Repeater ausgelesen<br>
  11400. dst: Zielger&auml;t der Nachricht - aus den Attributen abgeleitet<br>
  11401. broadcast: sollen Broadcasts weitergeleitet werden - aus Repeater ausgelesen<br>
  11402. verify: stimmen Attribute und ausgelesen Werte &uuml;berein?<br>
  11403. </ul>
  11404. </li>
  11405. </ul>
  11406. </li>
  11407. </ul>
  11408. <br>
  11409. Debugging:
  11410. <ul>
  11411. <li><B>raw &lt;data&gt; ...</B><br>
  11412. nur f&uuml;r Experimente ben&ouml;tigt.
  11413. Sendet eine Liste von "Roh"-Befehlen. Der erste Befehl wird unmittelbar gesendet,
  11414. die folgenden sobald der vorherige best&auml;tigt wurde. Die L&auml;nge wird automatisch
  11415. berechnet und der Nachrichtenz&auml;hler wird erh&ouml;ht wenn die ersten beiden Zeichen ++ sind.
  11416. Beispiel (AES aktivieren):
  11417. <pre>
  11418. set hm1 raw ++A001F100001234560105000000001\
  11419. ++A001F10000123456010802010AF10B000C00\
  11420. ++A001F1000012345601080801\
  11421. ++A001F100001234560106
  11422. </pre>
  11423. </li>
  11424. </ul>
  11425. </ul>
  11426. <br>
  11427. <a name="CUL_HMget"></a><b>Get</b><br>
  11428. <ul>
  11429. <li><B>configSave &lt;filename&gt;</B><a name="CUL_HMconfigSave"></a><br>
  11430. Sichert die Einstellungen eines Eintrags in einer Datei. Die Daten werden in
  11431. einem von der FHEM-Befehlszeile ausf&uuml;hrbaren Format gespeichert.<br>
  11432. Die Datei liegt im FHEM Home-Verzeichnis neben der fhem.cfg. Gespeichert wird
  11433. kumulativ- d.h. neue Daten werden an die Datei angeh&auml;ngt. Es liegt am Benutzer das
  11434. doppelte speichern von Eintr&auml;gen zu vermeiden.<br>
  11435. Ziel der Daten ist NUR die Information eines HM-Ger&auml;tes welche IM Ger&auml;t gespeichert ist.
  11436. Im Deteil sind das nur die Peer-Liste sowie die Register.
  11437. Durch die Register wird also das Peering eingeschlossen.<br>
  11438. Die Datei ist vom Benutzer les- und editierbar. Zus&auml;tzlich gespeicherte Zeitstempel
  11439. helfen dem Nutzer bei der Validierung.<br>
  11440. Einschr&auml;nkungen:<br>
  11441. Auch wenn alle Daten eines Eintrags in eine Datei gesichert werden so sichert FHEM nur
  11442. die zum Zeitpunkt des Speicherns verf&uuml;gbaren Daten! Der Nutzer muss also die Daten
  11443. der HM-Hardware auslesen bevor dieser Befehl ausgef&uuml;hrt wird.
  11444. Siehe empfohlenen Ablauf unten.<br>
  11445. Dieser Befehl speichert keine FHEM-Attribute oder Ger&auml;tedefinitionen.
  11446. Diese verbleiben in der fhem.cfg.<br>
  11447. Desweiteren werden gesicherte Daten nicht automatisch zur&uuml;ck auf die HM-Hardware geladen.
  11448. Der Benutzer muss die Wiederherstellung ausl&ouml;sen.<br><br>
  11449. Ebenso wie ander Befehle wird 'configSave' am besten auf ein Ger&auml;t und nicht auf einen
  11450. Kanal ausgef&uuml;hrt. Wenn auf ein Ger&auml;t angewendet werden auch die damit verbundenen Kan&auml;le
  11451. gesichert. <br><br>
  11452. <code>
  11453. Empfohlene Arbeitsfolge f&uuml;r ein Ger&auml;t 'HMdev':<br>
  11454. set HMdev clear msgEvents # alte Events l&ouml;schen um Daten besser kontrollieren zu k&ouml;nnen<br>
  11455. set HMdev getConfig # Ger&auml;te- und Kanalinformation auslesen<br>
  11456. # warten bis Ausf&uuml;hrung abgeschlossen ist<br>
  11457. # "protState" sollte dann "CMDs_done" sein<br>
  11458. # es sollten keine Warnungen zwischen "prot" und den Variablen auftauchen<br>
  11459. get configSave myActorFile<br>
  11460. </code>
  11461. </li>
  11462. <li><B>param &lt;paramName&gt;</B><br>
  11463. Gibt den Inhalt der relevanten Parameter eines Eintrags zur&uuml;ck. <br>
  11464. Hinweis: wird der Befehl auf einen Kanal angewandt und 'model' abgefragt so wird das Model
  11465. des inhalteanbietenden Ger&auml;ts zur&uuml;ckgegeben.
  11466. </li>
  11467. <li><B>reg &lt;addr&gt; &lt;list&gt; &lt;peerID&gt;</B><br>
  11468. liefert den Wert eines Registers zur&uuml;ck. Daten werden aus dem Speicher von FHEM und nicht direkt vom Ger&auml;t geholt.
  11469. Falls der Registerinhalt nicht verf&uuml;gbar ist muss "getConfig" sowie anschließend "getReg" verwendet werden.<br>
  11470. &lt;addr&gt; Adresse des Registers in HEX. Registername kann alternativ verwendet werden falls in FHEM bekannt.
  11471. "all" gibt alle dekodierten Register eines Eintrags in einer Liste zur&uuml;ck.<br>
  11472. &lt;list&gt; Liste aus der das Register gew&auml;hlt wird. Wird der REgistername verwendet wird "list" ignoriert und kann auf '0' gesetzt werden.<br>
  11473. &lt;peerID&gt; identifiziert die Registerb&auml;nke f&uuml;r "list3" und "list4". Kann als Dummy gesetzt werden wenn nicht ben&ouml;tigt.<br>
  11474. </li>
  11475. <li><B>regList</B><br>
  11476. gibt eine Liste der von FHEM f&uuml;r dieses Ger&auml;t dekodierten Register zur&uuml;ck.<br>
  11477. Beachten dass noch mehr Register f&uuml;r ein Ger&auml;t implemetiert sein k&ouml;nnen.<br>
  11478. </li>
  11479. <li><B>saveConfig &lt;file&gt;</B><a name="CUL_HMsaveConfig"></a><br>
  11480. speichert Peers und Register in einer Datei.<br>
  11481. Gespeichert werden die Daten wie sie in FHEM verf&uuml;gbar sind. Es ist daher notwendig vor dem Speichern die Daten auszulesen.<br>
  11482. Der Befehl unterst&uuml;tzt Aktionen auf Ger&auml;teebene. D.h. wird der Befehl auf ein Ger&auml;t angewendet werden auch alle verbundenen Kanaleintr&auml;ge gesichert.<br>
  11483. Das Speichern der Datei erfolgt kumulativ. Wird ein Eintrag mehrfach in der selben Datei gespeichert so werden die Daten an diese angeh&auml;ngt.
  11484. Der Nutzer kann den Zeitpunkt des Speichern bei Bedarf auslesen.<br>
  11485. Der Inhalt der Datei kann verwendet werden um die Ger&auml;teeinstellungen wiederherzustellen. Er stellt alle Peers und Register des Eintrags wieder her.<br>
  11486. Zw&auml;nge/Beschr&auml;nkungen:<br>
  11487. vor dem zur&uuml;ckschreiben der Daten eines Eintrags muss das Ger&auml;t mit FHEM verbunden werden.<br>
  11488. "restore" l&ouml;scht keine verkn&uuml;pften Kan&auml;le, es f&uuml;gt nur neue Peers hinzu.<br>
  11489. </li>
  11490. <li><B>listDevice</B><br>
  11491. <ul>
  11492. <li>bei einer CCU gibt es eine Liste der Devices, welche den ccu service zum zuweisen der IOs zurück<br>
  11493. </li>
  11494. <li>beim ActionDetector wird eine Komma geteilte Liste der Entities zurückgegeben<br>
  11495. get ActionDetector listDevice # returns alle assigned entities<br>
  11496. get ActionDetector listDevice notActive# returns entities ohne status alive<br>
  11497. get ActionDetector listDevice alive # returns entities mit status alive<br>
  11498. get ActionDetector listDevice unknown # returns entities mit status unknown<br>
  11499. get ActionDetector listDevice dead # returns entities mit status dead<br>
  11500. </li>
  11501. </ul>
  11502. </li>
  11503. </ul><br>
  11504. <a name="CUL_HMattr"></a><b>Attribute</b>
  11505. <ul>
  11506. <li><a href="#eventMap">eventMap</a></li>
  11507. <li><a href="#do_not_notify">do_not_notify</a></li>
  11508. <li><a href="#ignore">ignore</a></li>
  11509. <li><a href="#dummy">dummy</a></li>
  11510. <li><a href="#showtime">showtime</a></li>
  11511. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  11512. <li><a name="CUL_HMaesCommReq">aesCommReq</a>
  11513. wenn gesetzt wird IO AES signature anfordern bevor ACK zum Device gesendet wird.<br>
  11514. </li>
  11515. <li><a name="#CUL_HMactAutoTry">actAutoTry</a>
  11516. actAutoTry 0_off,1_on<br>
  11517. setzen erlaubt dem ActionDetector ein statusrequest zu senden falls das Device dead markiert werden soll.
  11518. Das Attribut kann fuer Devices nützlich sein, welche sich nicht von selbst zyklisch melden.
  11519. </li>
  11520. <li><a href="#actCycle">actCycle</a>
  11521. actCycle &lt;[hhh:mm]|off&gt;<br>
  11522. Bietet eine 'alive' oder besser 'not alive' Erkennung f&uuml;r Ger&auml;te. [hhh:mm] ist die maximale Zeit ohne Nachricht eines Ger&auml;ts. Wenn innerhalb dieser Zeit keine Nachricht empfangen wird so wird das Event"&lt;device&gt; is dead" generiert.
  11523. Sendet das Ger&auml;t wieder so wird die Nachricht"&lt;device&gt; is alive" ausgegeben. <br>
  11524. Diese Erkennung wird durch 'autocreate' f&uuml;r jedes Ger&auml;t mit zyklischer Statusmeldung angelegt.<br>
  11525. Die Kontrollinstanz ist ein Pseudo-Ger&auml;t "ActionDetector" mit der HMId "000000".<br>
  11526. Aufgrund von Performance&uuml;berlegungen liegt die Antwortverz&ouml;gerung bei 600 Sekunden (10min). Kann &uuml;ber das Attribut "actCycle" des "ActionDetector" kontrolliert werden.<br>
  11527. Sobald die &Uuml;berwachung aktiviert wurde hat das HM-Ger&auml;t 2 Attribute:<br>
  11528. <ul>
  11529. actStatus: Aktivit&auml;tsstatus des Ger&auml;ts<br>
  11530. actCycle: Detektionsspanne [hhh:mm]<br>
  11531. </ul>
  11532. Die gesamte Funktion kann &uuml;ber den "ActionDetector"-Eintrag &uuml;berpr&uuml;ft werden. Der Status aller Instanzen liegt im READING-Bereich.<br>
  11533. Hinweis: Diese Funktion kann ebenfalls f&uuml;r Ger&auml;te ohne zyklische &Uuml;bertragung aktiviert werden. Es obliegt dem Nutzer eine vern&uuml;nftige Zeitspanne festzulegen.
  11534. </li>
  11535. <li><a name="#CUL_HMautoReadReg">autoReadReg</a><br>
  11536. '0' autoReadReg wird ignorert.<br>
  11537. '1' wird automatisch in getConfig ausgef&uuml;hrt f&uuml;r das Device nach jedem reboot von FHEM. <br>
  11538. '2' wie '1' plus nach Power on.<br>
  11539. '3' wie '2' plus update wenn auf das Device geschreiben wird.<br>
  11540. '4' wie '3' plus fordert Status an, wenn es nicht korrekt erscheint<br>
  11541. '5' pr&uuml;ft Registerlisten und peerlisten. Wenn diese nicht komplett sind wird ein update angefordert<br>
  11542. '8_stateOnly' es wird nur der Status gepr&uuml;ft, updates f&uuml;r Register werden nicht gemacht.<br>
  11543. Ausf&uuml;hrung wird verz&ouml;gert ausgef&uuml;hrt. Wenn das IO eine gewisse Last erreicht hat wird
  11544. das Kommando weiter verz&ouml;gert um eine &Uuml;berlast zu vermeiden.<br>
  11545. Empfohlene Zusammenh&auml;nge bei Nutzung:<br>
  11546. <ul>
  11547. Benutze das Attribut f&uuml;r das Device, nicht f&uuml;r jeden einzelnen Kanal<br>
  11548. Das Setzen auf Level 5 wird f&uuml;r alle Devices und Typen empfohlen, auch wakeup Devices.<br>
  11549. </ul>
  11550. </li>
  11551. <li><a name="CUL_HMburstAccess">burstAccess</a><br>
  11552. kann f&uuml;r eine Ger&auml;teinstanz gesetzt werden falls das Model bedingte Bursts erlaubt.
  11553. Das Attribut deaktiviert den Burstbetrieb (0_off) was die Nachrichtenmenge des HMLAN reduziert
  11554. und damit die Wahrscheinlichkeit einer &Uuml;berlast von HMLAN verringert.<br>
  11555. Einschalten (1_auto) erlaubt k&uuml;rzere Reaktionszeiten eines Ger&auml;ts. Der Nutzer muss nicht warten
  11556. bis das Ger&auml;t wach ist. <br>
  11557. Zu beacht ist dass das Register "burstRx" im Ger&auml;t ebenfalls gesetzt werden muss.
  11558. </li>
  11559. <li><a name="CUL_HMexpert">expert</a><br>
  11560. Dieses Attribut steuert die Sichtbarkeit der Register Readngs. Damit wird die Darstellung der Ger&auml;teparameter kontrolliert.<br>
  11561. Es handdelt sich um einen binaer kodierten Wert mit folgenden Empfehlungen:<br>
  11562. <ul>
  11563. 0_defReg : default Register<br>
  11564. 1_allReg : all Register<br>
  11565. 2_defReg+raw : default Register und raw Register<br>
  11566. 3_allReg+raw : alle Register und raw reading<br>
  11567. 4_off : no Register<br>
  11568. 8_templ+default: templates und default Register<br>
  11569. 12_templOnly : nur templates<br>
  11570. 251_anything : alles verfügbare<br>
  11571. </ul>
  11572. Wird 'expert' auf ein Ger&auml;t angewendet so gilt dies auch f&uuml;r alle verkn&uuml;pften Kan&auml;le.
  11573. Kann &uuml;bergangen werden indem das Attribut ' expert' auch f&uuml;r den Ger&auml;tekanal gesetzt wird.<br>
  11574. Das Attribut "showInternalValues" bei den globalen Werten muss ebenfalls &uuml;berpr&uuml;ft werden.
  11575. "expert" macht sich diese Implementierung zu Nutze.
  11576. Gleichwohl setzt "showInternalValues" - bei Definition - 'expert' außer Kraft .
  11577. </li>
  11578. <li><a name="#CUL_HMreadOnly">readOnly</a><br>
  11579. beschränkt kommandos auf Lesen und Beobachten.
  11580. </li>
  11581. <li><a name="#CUL_HMIOgrp">IOgrp</a><br>
  11582. kann an Devices vergeben werden udn zeigt auf eine virtuelle ccu. Danach wird die ccu
  11583. beim Senden das passende IO für das Device auswählen. Es ist notwendig, dass die virtuelle ccu
  11584. definiert und alle erlaubten IOs eingetragen sind. Beim Senden wird die ccu prüfen
  11585. welches IO operational ist und welches den besten rssi-faktor für das Device hat.<br>
  11586. Optional kann ein bevorzugtes IO definiert werden. In diesem Fall wird es, wenn operational,
  11587. genutzt - unabhängig von den rssi Werten.<br>
  11588. wenn kein prefIO verfügbar ist und none erkannt wird wird das IO aus IODev gewählt<br>
  11589. Beispiel:<br>
  11590. <ul><code>
  11591. attr myDevice1 IOgrp vccu<br>
  11592. attr myDevice2 IOgrp vccu:prefIO1,prefIO2,prefIO3<br>
  11593. attr myDevice2 IOgrp vccu:prefIO1,prefIO2,none<br>
  11594. </code></ul>
  11595. </li>
  11596. <li><a name="#CUL_HMlevelRange">levelRange</a><br>
  11597. nur f&uuml;r Dimmer! Der Dimmbereich wird eingeschr&auml;nkt.
  11598. Es ist gedacht um z.B. LED Lichter unterst&uuml;tzen welche mit 10% beginnen und bei 40% bereits das Maximum haben.
  11599. levelrange normalisiert den Bereich entsprechend. D.h. set 100 wird physikalisch den Dimmer auf 40%,
  11600. 1% auf 10% setzen. 0% schaltet physikalisch aus.<br>
  11601. Beeinflusst werdne Kommndos on, up, down, toggle und pct. <b>Nicht</b> beeinflusst werden Kommandos
  11602. die den Wert physikalisch setzen.<br>
  11603. Zu beachten:<br>
  11604. dimmer level von Peers gesetzt wird nicht beeinflusst. Dies wird durch Register konfiguriert.<br>
  11605. Readings level k&ouml;nnte negative werden oder &uuml;ber 100%. Das kommt daher, dass physikalisch der Bereich 0-100%
  11606. ist aber auf den logischen bereicht normiert wird.<br>
  11607. Sind virtuelle Dimmer Kan&auml;le verf&uuml;gbar muss das Attribut f&uuml;r jeden Kanal gesetzt werden<br>
  11608. Beispiel:<br>
  11609. <ul><code>
  11610. attr myChannel levelRange 0,40<br>
  11611. attr myChannel levelRange 10,80<br>
  11612. </code></ul>
  11613. </li>
  11614. <li><a name="#CUL_HMtempListTmpl">tempListTmpl</a><br>
  11615. Setzt das Default f&uuml;r Heizungskontroller. Ist es nicht gesetzt wird der default filename genutzt und der name
  11616. der entity als templatename. Z.B. ./tempList.cfg:RT_Clima<br>
  11617. Um das template nicht zu nutzen kann man es auf '0'setzen.<br>
  11618. Format ist &lt;file&gt;:&lt;templatename&gt;.
  11619. </li>
  11620. <li><a name="CUL_HMmodel">model</a>,
  11621. <a name="subType">subType</a><br>
  11622. Diese Attribute werden bei erfolgreichem Pairing automatisch gesetzt.
  11623. Sie sollten nicht per Hand gesetzt werden und sind notwendig um Ger&auml;tenachrichten
  11624. korrekt interpretieren oder senden zu k&ouml;nnen.</li>
  11625. <li><a name="param">param</a><br>
  11626. 'param' definiert modelspezifische Verhalten oder Funktionen. Siehe "models" f&uuml;r Details.</li>
  11627. <li><a name="CUL_HMmsgRepeat">msgRepeat</a><br>
  11628. Definiert die Nummer an Wiederholungen falls ein Ger&auml;t nicht rechtzeitig antwortet. <br>
  11629. F&uuml;r Ger&auml;te die nur den "Config"-Modus unterst&uuml;tzen sind Wiederholungen nicht erlaubt. <br>
  11630. Bei Ger&auml;te mit wakeup-Modus wartet das Ger&auml;t bis zum n&auml;chsten Aufwachen. Eine l&auml;ngere Verz&ouml;gerung
  11631. sollte in diesem Fall angedacht werden. <br>
  11632. Wiederholen von Bursts hat Auswirkungen auf die HMLAN &Uuml;bertragungskapazit&auml;t.</li>
  11633. <li><a name="rawToReadable">rawToReadable</a><br>
  11634. Wird verwendet um Rohdaten von KFM100 in ein lesbares Fomrat zu bringen, basierend auf
  11635. den gemessenen Werten. Z.B. langsames F&uuml;llen eines Tanks, w&auml;hrend die Werte mit <a href="#inform">inform</a>
  11636. angezeigt werden. Man sieht:
  11637. <ul>
  11638. 10 (bei 0%)<br>
  11639. 50 (bei 20%)<br>
  11640. 79 (bei 40%)<br>
  11641. 270 (bei 100%)<br>
  11642. </ul>
  11643. Anwenden dieser Werte: "attr KFM100 rawToReadable 10:0 50:20 79:40 270:100".
  11644. FHEM f&uuml;r damit eine lineare Interpolation der Werte in den gegebenen Grenzen aus.
  11645. </li>
  11646. <li><a name="unit">unit</a><br>
  11647. setzt die gemeldete Einheit des KFM100 falls 'rawToReadable' aktiviert ist. Z.B.<br>
  11648. attr KFM100 unit Liter
  11649. </li>
  11650. <li><a name="autoReadReg">autoReadReg</a><br>
  11651. '0' autoReadReg wird ignoriert.<br>
  11652. '1' f&uuml;hrt ein "getConfig" f&uuml;r ein Ger&auml;t automatisch nach jedem Neustart von FHEM aus. <br>
  11653. '2' verh&auml;lt sich wie '1',zus&auml;tzlich nach jedem power_on.<br>
  11654. '3' wie '2', zus&auml;tzlich bei jedem Schreiben auf das Ger&auml;t<br>
  11655. '4' wie '3' und versucht außerdem den Status abzufragen falls dieser nicht verf&uuml;gbar erscheint.<br>
  11656. '5' kontrolliert 'reglist' und 'peerlist'. Falls das Auslesen unvollst&auml;ndig ist wird 'getConfig' ausgef&uuml;hrt<br>
  11657. '8_stateOnly' aktualisiert nur Statusinformationen aber keine Konfigurationen wie Daten- oder
  11658. Peerregister.<br>
  11659. Ausf&uuml;hrung wird verz&ouml;gert um eine &Uuml;berlastung beim Start zu vermeiden . Daher werden Aktualisierung und Anzeige
  11660. von Werten abh&auml;ngig von der Gr&ouml;ße der Datenbank verz&ouml;gert geschehen.<br>
  11661. Empfehlungen und Einschr&auml;nkungen bei Benutzung:<br>
  11662. <ul>
  11663. Dieses Attribut nur auf ein Ger&auml;t oder Kanal 01 anwenden. Nicht auf einzelne Kan&auml;le eines mehrkanaligen
  11664. Ger&auml;ts anwenden um eine doppelte Ausf&uuml;hrung zu vermeiden.<br>
  11665. Verwendung bei Ger&auml;ten die nur auf den 'config'-Modus reagieren wird nicht empfohlen da die Ausf&uuml;hrung
  11666. erst starten wird wenn der Nutzer die Konfiguration vornimmt<br>
  11667. Anwenden auf Ger&auml;te mit 'wakeup'-Modus ist n&uuml;tzlich. Zu bedenken ist aber dass die Ausf&uuml;hrung
  11668. bis zm "aufwachen" verz&ouml;gert wird.<br>
  11669. </ul>
  11670. </li>
  11671. </ul> <br>
  11672. <a name="CUL_HMparams"><b>verf&uuml;gbare Parameter f&uuml;r "param"</b></a>
  11673. <ul>
  11674. <li><B>HM-Sen-RD-O</B><br>
  11675. offAtPon: nur Heizkan&auml;le: erzwingt Ausschalten der Heizung nach einem powerOn<br>
  11676. onAtRain: nur Heizkan&auml;le: erzwingt Einschalten der Heizung bei Status 'rain' und Ausschalten bei Status 'dry'<br>
  11677. </li>
  11678. <li><B>virtuals</B><br>
  11679. noOnOff: eine virtuelle Instanz wird den Status nicht &auml;ndern wenn ein Trigger empfangen wird. Ist dieser Paramter
  11680. nicht gegeben so toggled die Instanz ihren Status mit jedem trigger zwischen An und Aus<br>
  11681. msgReduce: falls gesetzt und der Kanal wird f&uuml;r <a ref="CUL_HMvalvePos"></a> genutzt wird jede Nachricht
  11682. außer die der Ventilstellung verworfen um die Nachrichtenmenge zu reduzieren<br>
  11683. </li>
  11684. <li><B>blind</B><br>
  11685. <B>levelInverse</B> w&auml;hrend HM 100% als offen und 0% als geschlossen behandelt ist dies evtl. nicht
  11686. intuitiv f&uuml;r den Nutzer. Defaut f&uuml;r 100% ist offen und wird als 'on'angezeigt.
  11687. Das Setzen des Parameters invertiert die Anzeige - 0% wird also offen und 100% ist geschlossen.<br>
  11688. ACHTUNG: Die Anpassung betrifft nur Readings und Kommandos. <B>Register sind nicht betroffen.</B><br>
  11689. <B>ponRestoreSmart</B> bei powerup des Device fährt das Rollo in die vermeintlich nächstgelegene Endposition und anschliessend in die ursprüngliche Position.<br>
  11690. <B>ponRestoreForce</B> bei powerup des Device fährt das Rollo auf Level 0, dann auf Level 100 und anschliessend in die ursprüngliche Position.<br>
  11691. </li>
  11692. <li><B>sensRain</B><br>
  11693. <B>siren</B><br>
  11694. <B>powerMeter</B><br>
  11695. <B>switch</B><br>
  11696. <B>dimmer</B><br>
  11697. <B>rgb</B><br>
  11698. <B>showTimed</B> wenn timedOn running ist wird -till an state gehängt. Dies führt dazu, dass ggf. on-till im State steht was das stateIcon handling verbessert.<br>
  11699. </li>
  11700. </ul><br>
  11701. <a name="CUL_HMevents"><b>Erzeugte Events:</b></a>
  11702. <ul>
  11703. <li><B>Allgemein</B><br>
  11704. recentStateType:[ack|info] # kann nicht verwendet werden um Nachrichten zu triggern<br>
  11705. <ul>
  11706. <li>ack zeigt an das eine Statusinformation aus einer Best&auml;tigung abgeleitet wurde</li>
  11707. <li>info zeigt eine automatische Nachricht eines Ger&auml;ts an</li>
  11708. <li><a name="CUL_HMsabotageAttackId"><b>sabotageAttackId</b></a><br>
  11709. Alarmiert bei Konfiguration des Ger&auml;ts durch unbekannte Quelle<br></li>
  11710. <li><a name="CUL_HMsabotageAttack"><b>sabotageAttack</b></a><br>
  11711. Alarmiert bei Konfiguration des Ger&auml;ts welche nicht durch das System ausgel&ouml;st wurde<br></li>
  11712. <li><a name="CUL_HMtrigDst"><b>trigDst_&lt;name&gt;: noConfig</b></a><br>
  11713. Ein Sensor triggert ein Device welches nicht in seiner Peerliste steht. Die Peerliste ist nicht akuell<br></li>
  11714. </ul>
  11715. </li>
  11716. <li><B>HM-CC-TC,ROTO_ZEL-STG-RM-FWT</B><br>
  11717. T: $t H: $h<br>
  11718. battery:[low|ok]<br>
  11719. measured-temp $t<br>
  11720. humidity $h<br>
  11721. actuator $vp %<br>
  11722. desired-temp $dTemp<br>
  11723. desired-temp-manu $dTemp #Temperatur falls im manuellen Modus<br>
  11724. desired-temp-cent $dTemp #Temperatur falls im Zentrale-Modus<br>
  11725. windowopen-temp-%d %.1f (sensor:%s)<br>
  11726. tempList$wd hh:mm $t hh:mm $t ...<br>
  11727. displayMode temp-[hum|only]<br>
  11728. displayTemp [setpoint|actual]<br>
  11729. displayTempUnit [fahrenheit|celsius]<br>
  11730. controlMode [auto|manual|central|party]<br>
  11731. tempValveMode [Auto|Closed|Open|unknown]<br>
  11732. param-change offset=$o1, value=$v1<br>
  11733. ValveErrorPosition_for_$dname $vep %<br>
  11734. ValveOffset_for_$dname : $of %<br>
  11735. ValveErrorPosition $vep %<br>
  11736. ValveOffset $of %<br>
  11737. time-request<br>
  11738. trig_&lt;src&gt; &lt;value&gt; #channel was triggered by &lt;src&gt; channel.
  11739. Dieses Event h&auml;ngt vom kompletten Auslesen der Kanalkonfiguration ab, anderenfalls k&ouml;nnen Daten
  11740. unvollst&auml;ndig oder fehlerhaft sein.<br>
  11741. trigLast &lt;channel&gt; #letzter empfangener Trigger<br>
  11742. </li>
  11743. <li><B>HM-CC-RT-DN and HM-CC-RT-DN-BoM</B><br>
  11744. state:T: $actTemp desired: $setTemp valve: $vp %<br>
  11745. motorErr: [ok|ValveTight|adjustRangeTooLarge|adjustRangeTooSmall|communicationERR|unknown|lowBat|ValveErrorPosition]
  11746. measured-temp $actTemp<br>
  11747. desired-temp $setTemp<br>
  11748. ValvePosition $vp %<br>
  11749. mode [auto|manual|party|boost]<br>
  11750. battery [low|ok]<br>
  11751. batteryLevel $bat V<br>
  11752. measured-temp $actTemp<br>
  11753. desired-temp $setTemp<br>
  11754. actuator $vp %<br>
  11755. time-request<br>
  11756. trig_&lt;src&gt; &lt;value&gt; #Kanal wurde durch &lt;src&gt; Kanal ausgel&ouml;ßt.
  11757. </li>
  11758. <li><B>HM-CC-VD,ROTO_ZEL-STG-RM-FSA</B><br>
  11759. $vp %<br>
  11760. battery:[critical|low|ok]<br>
  11761. motorErr:[ok|blocked|loose|adjusting range too small|opening|closing|stop]<br>
  11762. ValvePosition:$vp %<br>
  11763. ValveErrorPosition:$vep %<br>
  11764. ValveOffset:$of %<br>
  11765. ValveDesired:$vp % # durch Temperatursteuerung gesetzt <br>
  11766. operState:[errorTargetNotMet|onTarget|adjusting|changed] # operative Bedingung<br>
  11767. operStateErrCnt:$cnt # Anzahl fehlgeschlagener Einstellungen<br>
  11768. </li>
  11769. <li><B>HM-CC-SCD</B><br>
  11770. [normal|added|addedStrong]<br>
  11771. battery [low|ok]<br>
  11772. </li>
  11773. <li><B>HM-SEC-SFA-SM</B><br>
  11774. powerError [on|off]<br>
  11775. sabotageError [on|off]<br>
  11776. battery: [critical|low|ok]<br>
  11777. </li>
  11778. <li><B>HM-LC-BL1-PB-FM</B><br>
  11779. motor: [opening|closing]<br>
  11780. </li>
  11781. <li><B>HM-LC-SW1-BA-PCB</B><br>
  11782. battery: [low|ok]<br>
  11783. </li>
  11784. <li><B>HM-OU-LED16</B><br>
  11785. color $value # in Hex - nur f&uuml;r Ger&auml;t<br>
  11786. $value # in Hex - nur f&uuml;r Ger&auml;t<br>
  11787. color [off|red|green|orange] # f&uuml;r Kanal <br>
  11788. [off|red|green|orange] # f&uuml;r Kanal <br>
  11789. </li>
  11790. <li><B>HM-OU-CFM-PL</B><br>
  11791. [on|off|$val]<br>
  11792. </li>
  11793. <li><B>HM-Sen-Wa-Od</B><br>
  11794. $level%<br>
  11795. level $level%<br>
  11796. </li>
  11797. <li><B>KFM100</B><br>
  11798. $v<br>
  11799. $cv,$unit<br>
  11800. rawValue:$v<br>
  11801. Sequence:$seq<br>
  11802. content:$cv,$unit<br>
  11803. </li>
  11804. <li><B>KS550/HM-WDS100-C6-O</B><br>
  11805. T: $t H: $h W: $w R: $r IR: $ir WD: $wd WDR: $wdr S: $s B: $b<br>
  11806. temperature $t<br>
  11807. humidity $h<br>
  11808. windSpeed $w<br>
  11809. windDirection $wd<br>
  11810. windDirRange $wdr<br>
  11811. rain $r<br>
  11812. isRaining $ir<br>
  11813. sunshine $s<br>
  11814. brightness $b<br>
  11815. unknown $p<br>
  11816. </li>
  11817. <li><B>HM-Sen-RD-O</B><br>
  11818. lastRain: timestamp # kein Trigger wird erzeugt. Anfang des vorherigen Regen-Zeitstempels
  11819. des Messwerts ist Ende des Regens. <br>
  11820. </li>
  11821. <li><B>THSensor und HM-WDC7000</B><br>
  11822. T: $t H: $h AP: $ap<br>
  11823. temperature $t<br>
  11824. humidity $h<br>
  11825. airpress $ap #nur HM-WDC7000<br>
  11826. </li>
  11827. <li><B>dimmer</B><br>
  11828. overload [on|off]<br>
  11829. overheat [on|off]<br>
  11830. reduced [on|off]<br>
  11831. dim: [up|down|stop]<br>
  11832. </li>
  11833. <li><B>motionDetector</B><br>
  11834. brightness:$b<br>
  11835. alive<br>
  11836. motion on (to $dest)<br>
  11837. motionCount $cnt _next:$nextTr"-"[0x0|0x1|0x2|0x3|15|30|60|120|240|0x9|0xa|0xb|0xc|0xd|0xe|0xf]<br>
  11838. cover [closed|open] # nicht bei HM-Sec-MDIR<br>
  11839. sabotageError [on|off] # nur bei HM-Sec-MDIR<br>
  11840. battery [low|ok]<br>
  11841. devState_raw.$d1 $d2<br>
  11842. </li>
  11843. <li><B>remote/pushButton/outputUnit</B><br>
  11844. <ul> (to $dest) wird hinzugef&uuml;gt wenn der Knopf gepeert ist und keinen Broadcast sendet<br>
  11845. Freigabe ist nur f&uuml;r verkn&uuml;pfte Kan&auml;le verf&uuml;gbar</ul>
  11846. Btn$x onShort<br>
  11847. Btn$x offShort<br>
  11848. Btn$x onLong $counter<br>
  11849. Btn$x offLong $counter<br>
  11850. Btn$x onLongRelease $counter<br>
  11851. Btn$x offLongRelease $counter<br>
  11852. Btn$x onShort (to $dest)<br>
  11853. Btn$x offShort (to $dest)<br>
  11854. Btn$x onLong $counter (to $dest)<br>
  11855. Btn$x offLong $counter (to $dest)<br>
  11856. Btn$x onLongRelease $counter (to $dest)<br>
  11857. Btn$x offLongRelease $counter (to $dest)<br>
  11858. </li>
  11859. <li><B>remote/pushButton</B><br>
  11860. battery [low|ok]<br>
  11861. trigger [Long|Short]_$no trigger event from channel<br>
  11862. </li>
  11863. <li><B>swi</B><br>
  11864. Btn$x Short<br>
  11865. Btn$x Short (to $dest)<br>
  11866. battery: [low|ok]<br>
  11867. </li>
  11868. <li><B>switch/dimmer/blindActuator</B><br>
  11869. $val<br>
  11870. powerOn [on|off|$val]<br>
  11871. [unknown|motor|dim] [up|down|stop]:$val<br>
  11872. timedOn [running|off]<br> # "An" ist tempor&auml;r - z.B. mit dem 'on-for-timer' gestartet
  11873. </li>
  11874. <li><B>sensRain</B><br>
  11875. $val<br>
  11876. powerOn <br>
  11877. level &lt;val&ge;<br>
  11878. timedOn [running|off]<br> # "An" ist tempor&auml;r - z.B. mit dem 'on-for-timer' gestartet
  11879. trigger [Long|Short]_$no trigger event from channel<br>
  11880. </li>
  11881. <li><B>smokeDetector</B><br>
  11882. [off|smoke-Alarm|alive] # f&uuml;r Gruppen-Master<br>
  11883. [off|smoke-forward|smoke-alarm] # f&uuml;r Gruppenmitglieder<br>
  11884. [normal|added|addedStrong] #HM-CC-SCD<br>
  11885. SDteam [add|remove]_$dname<br>
  11886. battery [low|ok]<br>
  11887. smoke_detect [none|&lt;src&gt;]<br>
  11888. teamCall:from $src<br>
  11889. </li>
  11890. <li><B>threeStateSensor</B><br>
  11891. [open|tilted|closed]<br>
  11892. [wet|damp|dry] #nur HM-SEC-WDS<br>
  11893. cover [open|closed] #HM-SEC-WDS und HM-Sec-RHS<br>
  11894. alive yes<br>
  11895. battery [low|ok]<br>
  11896. contact [open|tilted|closed]<br>
  11897. contact [wet|damp|dry] #nur HM-SEC-WDS<br>
  11898. sabotageError [on|off] #nur HM-SEC-SC<br>
  11899. </li>
  11900. <li><B>winMatic</B><br>
  11901. [locked|$value]<br>
  11902. motorErr [ok|TurnError|TiltError]<br>
  11903. direction [no|up|down|undefined]<br>
  11904. charge [trickleCharge|charge|dischange|unknown]<br>
  11905. airing [inactiv|$air]<br>
  11906. course [tilt|close]<br>
  11907. airing [inactiv|$value]<br>
  11908. contact tesed<br>
  11909. </li>
  11910. <li><B>keyMatic</B><br>
  11911. unknown:40<br>
  11912. battery [low|ok]<br>
  11913. uncertain [yes|no]<br>
  11914. error [unknown|motor aborted|clutch failure|none']<br>
  11915. lock [unlocked|locked]<br>
  11916. [unlocked|locked|uncertain]<br>
  11917. </li>
  11918. </ul>
  11919. <a name="CUL_HMinternals"><b>Internals</b></a>
  11920. <ul>
  11921. <li><B>aesCommToDev</B><br>
  11922. Information über Erfolg und Fehler der AES Kommunikation zwischen IO-device und HM-Device<br>
  11923. </li>
  11924. </ul><br>
  11925. <br>
  11926. </ul>
  11927. =end html_DE
  11928. =cut