10_ZWave.pm 229 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883
  1. ##############################################
  2. # $Id: 10_ZWave.pm 17186 2018-08-20 20:10:55Z rudolfkoenig $
  3. # See ZWDongle.pm for inspiration
  4. package main;
  5. use strict;
  6. use warnings;
  7. use SetExtensions;
  8. use Compress::Zlib;
  9. use Time::HiRes qw( gettimeofday );
  10. use HttpUtils;
  11. use ZWLib;
  12. sub ZWave_Cmd($$@);
  13. sub ZWave_Get($@);
  14. sub ZWave_Parse($$@);
  15. sub ZWave_Set($@);
  16. sub ZWave_SetClasses($$$$);
  17. sub ZWave_addToSendStack($$$);
  18. sub ZWave_secStart($);
  19. sub ZWave_secEnd($);
  20. sub ZWave_configParseModel($;$);
  21. sub ZWave_callbackId($);
  22. sub ZWave_setEndpoints($);
  23. our ($FW_ME,$FW_tp,$FW_ss);
  24. our %zwave_id2class;
  25. my %zwave_class = (
  26. NO_OPERATION => { id => '00' }, # lowlevel
  27. ZWAVE => { id => '01', # lowlevel
  28. set => { zwaveAssignId => "03%02x%08x" },
  29. parse => { "..0101(....)..(..)..(.*)" =>
  30. '"zwaveNIF:baseClass:$2 flags:$1 classes:$3"',
  31. "..0103(..)(........)" =>
  32. '"zwaveAssignId:homeId:$2 nodeIdHex:$1"',
  33. "..0104(.*)"=> '"zwaveFindNodesInRange:$1"',
  34. "..0105" => '"zwaveGetNodesInRange:noarg"',
  35. "..0106(.*)"=> '"zwaveNodeRangeInfo:$1"',
  36. "..0107(.*)"=> '"zwaveCommandComplete:$1"',
  37. "..010801" => '"zwaveTransferPresentation"',
  38. "..010c(.*)"=> '"zwaveAssignRoute:$1"'
  39. }},
  40. BASIC => { id => '20', # V1, V2
  41. set => { basicValue => "01%02x", # V1, V2
  42. basicSet => "01%02x" }, # Alias, Forum #38200
  43. get => { basicStatus => "02", }, # V1, V2
  44. parse => { "..2001(.*)"=> '"basicSet:".hex($1)', # Forum #36980
  45. "..2002" => "basicGet:request", # sent by the remote
  46. "032003(..)"=> '"basicReport:".hex($1)', # V1
  47. "052003(..)(..)(..)" => 'ZWave_basicReport($1,$2,$3)',
  48. }},
  49. CONTROLLER_REPLICATION => { id => '21' },
  50. APPLICATION_STATUS => { id => '22', # V1
  51. parse => { "..2201(..)(..)" =>
  52. 'ZWave_APPLICATION_STATUS_01_Report($hash, $1, $2)', # V1
  53. "03220200" => "applicationStatus:cmdRejected", # V1
  54. }},
  55. ZIP_SERVICES => { id => '23' },
  56. ZIP_SERVER => { id => '24' },
  57. SWITCH_BINARY => { id => '25',
  58. set => { off => "0100",
  59. on => "01FF",
  60. "off-for-timer" => 'ZWave_switchBinary_timer($hash,"00","%s")',
  61. "on-for-timer" => 'ZWave_switchBinary_timer($hash,"FF","%s")' },
  62. get => { swbStatus => "02", },
  63. parse => { "..250300" => "state:off",
  64. "..2503ff" => "state:on",
  65. "052503(..)(..)(..)" => 'sprintf("swbStatus:%s target %s '.
  66. 'duration %s", hex($1), hex($2),ZWave_byte2time($3))',
  67. "03250100" => "state:setOff",
  68. "032501ff" => "state:setOn" } } ,
  69. SWITCH_MULTILEVEL => { id => '26',
  70. set => { off => "0100",
  71. on => "01FF",
  72. dim => "01%02x",
  73. dimWithDuration => 'ZWave_switchMultilevel_Set($hash, 0, "%s")',
  74. dimUpDown => 'ZWave_switchMultilevel_Set($hash, 1, "%s")',
  75. dimUpDownWithDuration =>
  76. 'ZWave_switchMultilevel_Set($hash, 2, "%s")',
  77. dimUpDownIncDecWithDuration =>
  78. 'ZWave_switchMultilevel_Set($hash, 3, "%s")',
  79. stop => "05" },
  80. get => { swmStatus => "02",
  81. swmSupported=> "06" },
  82. parse => { "..2603(.*)"=> '($1 eq "00" ? "state:off" :
  83. ($1 eq "ff" ? "state:on" :
  84. "state:dim ".hex($1)))',
  85. "052603(..)(..)(..)" => 'sprintf("swmStatus:%s target %s '.
  86. 'duration %s", hex($1), hex($2), ZWave_duration($3))', # V4
  87. "..260100.."=> "state:setOff",
  88. "..2601ff.."=> "state:setOn",
  89. "..260420" => "state:swmBeginUp",
  90. "..260460" => "state:swmBeginDown",
  91. "..2604(..)(..)(..)(..)" => 'ZWave_swmParse($1,$2,$3,$4)',
  92. "..2605" => "state:swmEnd",
  93. "..2607(..)(..)" =>
  94. 'sprintf("swmSwitchType:primary %s secondary %s",
  95. Zwave_switchType($1), Zwave_switchType($2))', }}, #V3
  96. SWITCH_ALL => { id => '27',
  97. set => { swaIncludeNone => "0100",
  98. swaIncludeOff => "0101",
  99. swaIncludeOn => "0102",
  100. swaIncludeOnOff => "01ff",
  101. swaOn => "04",
  102. swaOff => "05" },
  103. get => { swaInclude => "02" },
  104. parse => { "03270300" => "swa:none",
  105. "03270301" => "swa:off",
  106. "03270302" => "swa:on",
  107. "032703ff" => "swa:on off" } },
  108. SWITCH_TOGGLE_BINARY => { id => '28' },
  109. SWITCH_TOGGLE_MULTILEVEL => { id => '29' },
  110. CHIMNEY_FAN => { id => '2a' },
  111. SCENE_ACTIVATION => { id => '2b',
  112. set => { sceneActivate => "01%02x%02x" },
  113. parse => { "042b01(..)(..)" => '"scene_".hex($1).":".hex($2)',
  114. "042b01(..)ff" => 'ZWave_sceneParse($1)'} },
  115. SCENE_ACTUATOR_CONF => { id => '2c',
  116. set => { sceneConfig => "01%02x%02x80%02x" },
  117. get => { sceneConfig => "02%02x" },
  118. parse => { "052c03(..)(..)(..)" =>
  119. '"scene_".hex($1).":level ".hex($2)."duration ".hex($3)' } },
  120. SCENE_CONTROLLER_CONF => { id => '2d',
  121. set => { sceneConfig => "01%02x%02x%02x" },
  122. get => { sceneConfig => "02%02x" },
  123. parse => { "052d03(..)(..)(..)" =>
  124. '"group_".hex($1).":scene ".hex($2)."duration ".hex($3)' } },
  125. ZIP_CLIENT => { id => '2e' },
  126. ZIP_ADV_SERVICES => { id => '2f' },
  127. SENSOR_BINARY => { id => '30',
  128. get => { sbStatus => "02" },
  129. parse => { "03300300" => "state:closed",
  130. "033003ff" => "state:open",
  131. "043003(..)(..)"=> 'ZWave_sensorbinaryV2Parse($1,$2)' } },
  132. SENSOR_MULTILEVEL => { id => '31',
  133. get => { smStatus => "04" },
  134. parse => { "..3105(..)(..)(.*)" => 'ZWave_multilevelParse($1,$2,$3)'} },
  135. METER => { id => '32',
  136. set => { meterReset => "05" },
  137. get => { meter => 'ZWave_meterGet("%s")',
  138. meterSupported => "03" },
  139. parse => { "..3202(.*)"=> 'ZWave_meterParse($hash, $1)',
  140. "..3204(.*)"=> 'ZWave_meterSupportedParse($hash, $1)' } },
  141. COLOR_CONTROL => { id => '33',
  142. get => { ccCapability=> '01', # no more args
  143. ccStatus => '03%02x' },
  144. set => { # Forum #36050
  145. rgb => '05050000010002%02x03%02x04%02x', # Forum #44014
  146. wcrgb => '050500%02x01%02x02%02x03%02x04%02x' },
  147. parse => { "043302(..)(..)"=> 'ZWave_ccCapability($1,$2)',
  148. "043304(..)(.*)"=> '"ccStatus_".hex($1).":".hex($2)' } },
  149. ZIP_ADV_CLIENT => { id => '34' },
  150. METER_PULSE => { id => '35' },
  151. BASIC_TARIFF_INFO => { id => '36' },
  152. HRV_STATUS => { id => '37',
  153. get => { hrvStatus => "01%02x",
  154. hrvStatusSupported => "03" },
  155. parse => { "0637020042(....)" =>
  156. 'sprintf("outdoorTemperature: %0.1f C", s2Hex($1)/100)',
  157. "0637020142(....)" =>
  158. 'sprintf("supplyAirTemperature: %0.1f C", s2Hex($1)/100)',
  159. "0637020242(....)" =>
  160. 'sprintf("exhaustAirTemperature: %0.1f C", s2Hex($1)/100)',
  161. "0637020342(....)" =>
  162. 'sprintf("dischargeAirTemperature: %0.1f C",s2Hex($1)/100)',
  163. "0637020442(....)" =>
  164. 'sprintf("indoorTemperature: %0.1f C", s2Hex($1)/100)',
  165. "0537020501(..)" =>
  166. 'sprintf("indoorHumidity: %s %%", hex($1))',
  167. "0537020601(..)" =>
  168. 'sprintf("remainingFilterLife: %s %%", hex($1))',
  169. "033704(..)" =>
  170. 'sprintf("supportedStatus: %s", ZWave_HrvStatus($1))',
  171. } },
  172. THERMOSTAT_HEATING => { id => '38' },
  173. HRV_CONTROL => { id => '39',
  174. set => { bypassOff => "0400",
  175. bypassOn => "04FF",
  176. ventilationRate => "07%02x" },
  177. get => { bypass => "05",
  178. ventilationRate => "08" },
  179. parse => { "033906(..)"=> '($1 eq "00" ? "bypass:off" : '.
  180. '($1 eq "ff" ? "bypass:on" : '.
  181. '"bypass:dim ".hex($1)))',
  182. "033909(..)"=> 'sprintf("ventilationRate: %s",hex($1))' } },
  183. DCP_CONFIG => { id => '3a' },
  184. DCP_MONITOR => { id => '3b' },
  185. METER_TBL_CONFIG => { id => '3c' },
  186. METER_TBL_MONITOR => { id => '3d' },
  187. METER_TBL_PUSH => { id => '3e' },
  188. PREPAYMENT => { id => '3f' },
  189. THERMOSTAT_MODE => { id => '40',
  190. set => { tmOff => "0100",
  191. tmHeating => "0101",
  192. tmCooling => "0102",
  193. tmAuto => "0103",
  194. tmFan => "0106",
  195. tmEnergySaveHeating => "010b",
  196. tmFullPower => "010f",
  197. tmManual => "011f" },
  198. get => { thermostatMode => "02" },
  199. parse => { "0.400300" => "thermostatMode:off",
  200. "0.400301" => "thermostatMode:heating",
  201. "0.400302" => "thermostatMode:cooling",
  202. "0.400303" => "thermostatMode:auto",
  203. "0.400306" => "thermostatMode:fanOnly",
  204. "0.40030b" => "thermostatMode:energySaveHeating",
  205. "0.40030f" => "thermostatMode:fullPower",
  206. "0.40031f" => "thermostatMode:manual",
  207. "0.400100" => "thermostatMode:setTmOff",
  208. "0.400101" => "thermostatMode:setTmHeating",
  209. "0.400103" => "thermostatMode:auto",
  210. "0.40010b" => "thermostatMode:setTmEnergySaveHeating",
  211. "0.40011f" => "thermostatMode:setTmManual",
  212. } } ,
  213. PREPAYMENT_ENCAPSULATION => { id => '41' },
  214. THERMOSTAT_OPERATING_STATE=>{ id => '42' ,
  215. get => { thermostatOperatingState => "02" },
  216. parse => { "03420300" => "thermostatOperatingState:idle",
  217. "03420301" => "thermostatOperatingState:heating",
  218. "03420302" => "thermostatOperatingState:cooling",
  219. "03420303" => "thermostatOperatingState:fanOnly",
  220. "03420304" => "thermostatOperatingState:pendingHeat",
  221. "03420305" => "thermostatOperatingState:pendingCool",
  222. "03420306" => "thermostatOperatingState:ventEconomizer",
  223. "03420307" => "thermostatOperatingState:auxHeating",
  224. "03420308" => "thermostatOperatingState:2ndStageHeating",
  225. "03420309" => "thermostatOperatingState:2ndStageCooling",
  226. "0342030a" => "thermostatOperatingState:2ndStageAuxHeat",
  227. "0342030b" => "thermostatOperatingState:3rdStageAuxHeat",
  228. } },
  229. THERMOSTAT_SETPOINT => { id => '43',
  230. set => { setpointHeating => "010101%02x",
  231. setpointCooling => "010201%02x",
  232. thermostatSetpointSet
  233. => 'ZWave_thermostatSetpointSet($hash, "%s")',
  234. "desired-temp" => # alias
  235. => 'ZWave_thermostatSetpointSet($hash, "%s")'},
  236. get => { setpoint => 'ZWave_thermostatSetpointGet("%s")',
  237. thermostatSetpointSupported => '04' },
  238. parse => { "..4303(.*)" => 'ZWave_thermostatSetpointParse($hash, $1)',
  239. "..4305(.*)" =>
  240. 'ZWave_thermostatSetpointSupportedParse($hash, $1)' } },
  241. THERMOSTAT_FAN_MODE => { id => '44',
  242. set => { fanAutoLow => "0100",
  243. fanLow => "0101",
  244. fanAutoHigh => "0102",
  245. fanHigh => "0103",
  246. fanAutoMedium => "0104",
  247. fanMedium => "0105" },
  248. get => { fanMode => "02" },
  249. parse => { "03440300" => "fanMode:fanAutoLow",
  250. "03440301" => "fanMode:fanLow",
  251. "03440302" => "fanMode:fanAutoHigh",
  252. "03440303" => "fanMode:fanHigh",
  253. "03440304" => "fanMode:fanAutoMedium",
  254. "03440305" => "fanMode:fanMedium"
  255. } } ,
  256. THERMOSTAT_FAN_STATE => { id => '45',
  257. get => { fanState => "02" },
  258. parse => { "03450300" => "fanState:off",
  259. "03450301" => "fanState:low",
  260. "03450302" => "fanState:high",
  261. "03450303" => "fanState:medium",
  262. "03450304" => "fanState:circulation",
  263. "03450305" => "fanState:humidityCirc",
  264. "03450306" => "fanState:rightLeftCirc",
  265. "03450307" => "fanState:upDownCirc",
  266. "03450308" => "fanState:quietCirc"
  267. } } ,
  268. CLIMATE_CONTROL_SCHEDULE => { id => '46',
  269. set => { ccs => 'ZWave_ccsSet("%s")' ,
  270. ccsOverride => 'ZWave_ccsSetOverride("%s")'},
  271. get => { ccs => 'ZWave_ccsGet("%s")',
  272. ccsAll => 'ZWave_ccsAllGet($hash)',
  273. ccsChanged => "04",
  274. ccsOverride => "07" },
  275. parse => { "..46(..)(.*)" => 'ZWave_ccsParse($1,$2)' }},
  276. THERMOSTAT_SETBACK => { id => '47' },
  277. RATE_TBL_CONFIG => { id => '48' },
  278. RATE_TBL_MONITOR => { id => '49' },
  279. TARIFF_CONFIG => { id => '4a' },
  280. TARIFF_TBL_MONITOR => { id => '4b' },
  281. DOOR_LOCK_LOGGING => { id => '4c',
  282. get => { doorLockLoggingRecordsSupported => '01',
  283. doorLockLoggingRecord => '03%02x' },
  284. parse => { "034c02(.*)" => '"doorLockLoggingRecordsSupported:".hex($val)',
  285. "..4c04(.*)" => 'ZWave_doorLLRParse($hash, $1)'} },
  286. NETWORK_MANAGEMANT_BASIC => { id => '4d' },
  287. SCHEDULE_ENTRY_LOCK => { id => '4e', # V1, V2, V3
  288. get => { scheduleEntryLockTypeSupported => '09',
  289. scheduleEntryLockWeekDay => "04%02x%02x",
  290. scheduleEntryLockYearDay => "07%02x%02x",
  291. scheduleEntryLockDailyRepeating => "0e%02x%02x",
  292. scheduleEntryLockTimeOffset => '0b'
  293. },
  294. set => { scheduleEntryLockSet =>
  295. 'ZWave_scheduleEntryLockSet($hash, "%s")',
  296. scheduleEntryLockAllSet =>
  297. 'ZWave_scheduleEntryLockAllSet($hash, "%s")',
  298. scheduleEntryLockWeekDaySet =>
  299. 'ZWave_scheduleEntryLockWeekDaySet($hash, "%s")',
  300. scheduleEntryLockYearDaySet =>
  301. 'ZWave_scheduleEntryLockYearDaySet($hash, "%s")',
  302. scheduleEntryLockTimeOffsetSet =>
  303. 'ZWave_scheduleEntryLockTimeOffsetSet($hash, "%s")',
  304. scheduleEntryLockYearDailyRepeatingSet =>
  305. 'ZWave_scheduleEntryLockDailyRepeatingSet($hash, "%s")'
  306. },
  307. parse => { "..4e0a(.*)" =>
  308. 'ZWave_scheduleEntryLockTypeSupportedParse($hash, $1)',
  309. "..4e05(.{14})" =>
  310. 'ZWave_scheduleEntryLockWeekDayParse($hash, $1)',
  311. "..4e08(.{24})" =>
  312. 'ZWave_scheduleEntryLockYearDayParse($hash, $1)',
  313. "..4e0c(.{6})" =>
  314. 'ZWave_scheduleEntryLockTimeOffsetParse($hash, $1)',
  315. "..4e0f(.{14})" =>
  316. 'ZWave_scheduleEntryLockDailyRepeatingParse($hash, $1)'
  317. }},
  318. ZI_6LOWPAN => { id => '4f' },
  319. BASIC_WINDOW_COVERING => { id => '50',
  320. set => { coveringClose => "0140",
  321. coveringOpen => "0100",
  322. coveringStop => "02" },
  323. parse => { "03500140" => "covering:close",
  324. "03500100" => "covering:open",
  325. "03500200" => "covering:stop",
  326. "03500240" => "covering:stop" } },
  327. MTP_WINDOW_COVERING => { id => '51' },
  328. NETWORK_MANAGEMENT_PROXY => { id => '52' },
  329. NETWORK_SCHEDULE => { id => '53', # V1, Schedule
  330. get => { scheduleSupported => "01",
  331. schedule => "04%02x",
  332. scheduleState => "08"},
  333. set => { scheduleRemove => "06%02x",
  334. schedule => 'ZWave_scheduleSet($hash, "%s")',
  335. scheduleState => "07%02x%02x"},
  336. parse => { "..5302(.*)" => 'ZWave_scheduleSupportedParse($hash, $1)',
  337. "..5305(.*)" => 'ZWave_scheduleParse($hash, $1)',
  338. "..5309(.*)" => 'ZWave_scheduleStateParse($hash, $1)' } },
  339. NETWORK_MANAGEMENT_PRIMARY=>{ id => '54' },
  340. TRANSPORT_SERVICE => { id => '55' },
  341. CRC_16_ENCAP => { id => '56' }, # Parse is handled in the code
  342. APPLICATION_CAPABILITY => { id => '57' },
  343. ZIP_ND => { id => '58' },
  344. ASSOCIATION_GRP_INFO => { id => '59',
  345. get => { associationGroupName => "01%02x",
  346. associationGroupCmdList => "0500%02x" },
  347. parse => { "..5902(..)(.*)"=> '"assocGroupName_".hex($1).":".pack("H*",$2)',
  348. "..5906(..)..(.*)"=> 'ZWave_assocGroupCmdList($1,$2)' } },
  349. DEVICE_RESET_LOCALLY => { id => '5a',
  350. parse => { "025a01" => "deviceResetLocally:yes" } },
  351. CENTRAL_SCENE => { id => '5b',
  352. parse => { "055b03...0(..)" => '"cSceneSet:".hex($1)',
  353. "055b03...1(..)" => '"cSceneDimEnd:".hex($1)',
  354. "055b03...2(..)" => '"cSceneDim:".hex($1)',
  355. "055b03...3(..)" => '"cSceneDouble:".hex($1)',
  356. "055b03...([4-6])(..)" => '"cSceneMultiple_".
  357. (hex($1)-1).":".hex($2)'} },
  358. IP_ASSOCIATION => { id => '5c' },
  359. ANTITHEFT => { id => '5d' },
  360. ZWAVEPLUS_INFO => { id => '5e',
  361. get => { zwavePlusInfo=>"01"},
  362. parse => { "095e02(..)(..)(..)(....)(....)"
  363. => 'ZWave_plusInfoParse($1,$2,$3,$4,$5)'} },
  364. ZIP_GATEWAY => { id => '5f' },
  365. MULTI_CHANNEL => { id => '60', # Version 2, aka MULTI_INSTANCE
  366. set => { mcCreateAll => 'ZWave_mcCreateAll($hash,"")' },
  367. get => { mcEndpoints => "07",
  368. mcCapability=> "09%02x"},
  369. parse => { "^0[45]6008(..)(..)" => '"mcEndpoints:total ".hex($2).'.
  370. '(hex($1)&0x80 ? ", dynamic":"").'.
  371. '(hex($1)&0x40 ? ", identical":", different")',
  372. "^..600a(.*)"=> 'ZWave_mcCapability($hash, $1)' },
  373. init => { ORDER=>49, CMD => '"set $NAME mcCreateAll"' } },
  374. ZIP_PORTAL => { id => '61' },
  375. DOOR_LOCK => { id => '62', # V2
  376. set => { doorLockOperation => 'ZWave_DoorLockOperationSet($hash, "%s")',
  377. doorLockConfiguration =>
  378. 'ZWave_DoorLockConfigSet($hash, "%s")' },
  379. get => { doorLockOperation => '02',
  380. doorLockConfiguration => '05'},
  381. parse => { "..6203(.*)" => 'ZWave_DoorLockOperationReport($hash, $1)',
  382. "..6206(.*)" => 'ZWave_DoorLockConfigReport($hash, $1)'} },
  383. USER_CODE => { id => '63',
  384. set => { userCode => 'ZWave_userCodeSet("%s")' },
  385. get => { userCode => "02%02x" ,
  386. userCodeUsersNumber => "04"},
  387. parse => { "..6303(..)(..)(.*)" =>
  388. 'sprintf("userCode:id %d status %d code %s", $1, $2, $3)' ,
  389. "..6305(..)" => '"userCodeUsersNumber:".hex($1)'}
  390. },
  391. APPLIANCE => { id => '64' },
  392. DMX => { id => '65' },
  393. BARRIER_OPERATOR => { id => '66',
  394. set => { barrierClose=> "0100",
  395. barrierOpen => "01ff" },
  396. get => { barrierState=> "02" },
  397. parse => { "..6603(..)"=> '($1 eq "00" ? "barrierState:closed" :
  398. ($1 eq "fc" ? "barrierState:closing" :
  399. ($1 eq "fd" ? "barrierState:stopped" :
  400. ($1 eq "fe" ? "barrierState:opening" :
  401. ($1 eq "ff" ? "barrierState:open" :
  402. "barrierState:".hex($1))))))'} },
  403. NETWORK_MANAGEMENT_INSTALL=> { id => '67' },
  404. ZIP_NAMING => { id => '68' },
  405. MAILBOX => { id => '69' },
  406. WINDOW_COVERING => { id => '6a' },
  407. IRRIGATION => { id => '6b' },
  408. SUPERVISION => { id => '6c' },
  409. ENTRY_CONTROL => { id => '6f' },
  410. CONFIGURATION => { id => '70',
  411. set => { configDefault=>"04%02x80",
  412. configByte => "04%02x01%02x",
  413. configWord => "04%02x02%04x",
  414. configLong => "04%02x04%08x" },
  415. get => { config => "05%02x",
  416. configAll => 'ZWave_configAllGet($hash)' },
  417. parse => { "^..70..(..)(..)(.*)" => 'ZWave_configParse($hash,$1,$2,$3)'} },
  418. ALARM => { id => '71',
  419. set => {
  420. alarmnotification => 'ZWave_ALARM_06_Set("%s")', # >=V2
  421. },
  422. get => {
  423. alarmEventSupported => 'ZWave_ALARM_01_Get("%s")', # >=V3
  424. alarm => 'ZWave_ALARM_04_Get(1, "%s")', # >=V1
  425. alarmWithType => 'ZWave_ALARM_04_Get(2, "%s")', # >=V2
  426. alarmWithTypeEvent => 'ZWave_ALARM_04_Get(3, "%s")', # >=V3
  427. alarmTypeSupported => "07", # >=V2
  428. },
  429. parse => {
  430. "..7102(.*)" => 'ZWave_ALARM_02_Report($1)', # >=V3
  431. "..7105(..)(..)(.*)" =>
  432. 'ZWave_ALARM_05_Report($hash, $1, $2, $3)', # >=V1
  433. "..7108(.*)" => 'ZWave_ALARM_08_Report($1)', # >=V2
  434. },
  435. },
  436. MANUFACTURER_SPECIFIC => { id => '72',
  437. get => { model => "04" },
  438. parse => { "0[8a]7205(....)(....)(....)(.*)"
  439. => 'ZWave_mfsParse($hash,$1,$2,$3,0)',
  440. "0[8a]7205(....)(....)(.{4})(.*)"
  441. => 'ZWave_mfsParse($hash,$1,$2,$3,1)',
  442. "0[8a]7205(....)(.{4})(.{4})(.*)"
  443. => 'ZWave_mfsParse($hash,$1,$2,$3,2)' },
  444. init => { ORDER=>49, CMD => '"get $NAME model"' } },
  445. POWERLEVEL => { id => '73',
  446. set => { powerlevel => "01%02x%02x",
  447. powerlevelTest => "04%02x%02x%04x" },
  448. get => { powerlevel => "02",
  449. powerlevelTest => "05" },
  450. parse => { "047303(..)(..)" =>
  451. '"powerlvl:current ".hex($1)." remain ".hex($2)',
  452. "067306(..)(..)(....)" =>
  453. '"powerlvlTest:node ".hex($1)." status ".hex($2).
  454. " frameAck ".hex($3)',} },
  455. PROTECTION => { id => '75',
  456. set => { protectionOff => "0100",
  457. protectionSeq => "0101",
  458. protectionOn => "0102",
  459. protectionBytes => "01%02x%02x" },
  460. get => { protection => "02" },
  461. parse => { "03750300" => "protection:off",
  462. "03750301" => "protection:seq",
  463. "03750302" => "protection:on",
  464. "047503(..)(..)" => 'ZWave_protectionParse($1, $2)'} },
  465. LOCK => { id => '76' },
  466. NODE_NAMING => { id => '77',
  467. set => { name => '(undef, "0100".unpack("H*", "%s"))',
  468. location => '(undef, "0400".unpack("H*", "%s"))' },
  469. get => { name => '02',
  470. location => '05' },
  471. parse => { '..770300(.*)' => '"name:".pack("H*", $1)',
  472. '..770600(.*)' => '"location:".pack("H*", $1)' } },
  473. FIRMWARE_UPDATE_MD => { id => '7a' },
  474. GROUPING_NAME => { id => '7b' },
  475. REMOTE_ASSOCIATION_ACTIVATE=>{id => '7c' },
  476. REMOTE_ASSOCIATION => { id => '7d' },
  477. BATTERY => { id => '80',
  478. get => { battery => "02" },
  479. parse => { "0.8003(..)"=> 'ZWave_battery($1)'} } ,
  480. CLOCK => { id => '81',
  481. get => { clock => "05" },
  482. set => { clock => 'ZWave_clockSet()' },
  483. parse => { "028105" => "clock:get",
  484. "048106(..)(..)"=> 'ZWave_clockParse($1,$2)' }},
  485. HAIL => { id => '82',
  486. parse => { "028201" => "hail:01"}},
  487. WAKE_UP => { id => '84',
  488. set => { wakeupInterval => "04%06x%02x",
  489. wakeupNoMoreInformation => "08" },
  490. get => { wakeupInterval => "05",
  491. wakeupIntervalCapabilities => "09" },
  492. parse => { "..8404(.*)"=> '"cmdSet:wakeupInterval $1"',
  493. "..8405" => 'cmdGet:wakeupInterval',
  494. "..8406(......)(..)" =>
  495. '"wakeupReport:interval ".hex($1)." target ".hex($2)',
  496. "..8407" => 'wakeup:notification',
  497. "..8408" => 'cmdSet:wakeupNoMoreInformation',
  498. "..8409" => 'cmdGet:wakeupIntervalCapabilities',
  499. "..840a(......)(......)(......)(......)" =>
  500. '"wakeupIntervalCapabilitiesReport:min ".hex($1).'.
  501. '" max ".hex($2)." default ".hex($3)." step ".hex($4)'},
  502. init => { ORDER=>11, CMD => '"set $NAME wakeupInterval 86400 $CTRLID"' } },
  503. ASSOCIATION => { id => '85',
  504. set => { associationAdd => "01%02x%02x*",
  505. associationDel => "04%02x%02x*" },
  506. get => { association => "02%02x",
  507. associationGroups => "05",
  508. associationAll => 'ZWave_associationAllGet($hash,"")' },
  509. parse => { "..8503(..)(..)..(.*)" => 'ZWave_assocGroup($homeId,$1,$2,$3)',
  510. "..8506(..)" => '"assocGroups:".hex($1)' },
  511. init => { ORDER=>10, CMD=> '"set $NAME associationAdd 1 $CTRLID"' } },
  512. VERSION => { id => '86',
  513. get => { version => "11",
  514. versionClass => 'ZWave_versionClassGet($hash, "%s")',
  515. versionClassAll => 'ZWave_versionClassAllGet($hash)'},
  516. parse => { "028611" => "cmdGet:version",
  517. "078612(..........)" => 'sprintf("version:Lib %d Prot '.
  518. '%d.%02d App %d.%d", unpack("C*",pack("H*","$1")))',
  519. "098612(..............)" => 'sprintf("version:Lib %d Prot '.
  520. '%d.%02d App %d.%d HW %d FWCounter %d",'.
  521. 'unpack("C*",pack("H*","$1")))',
  522. "0b8612(..................)" => 'sprintf("version:Lib %d Prot '.
  523. '%d.%02d App %d.%d HW %d FWCounter %d FW %d.%d",'.
  524. 'unpack("C*",pack("H*","$1")))',
  525. "048614(..)(..)" => '"versionClass_".hex($1).":".hex($2)' },
  526. init => { ORDER=> 1, CMD => '"get $NAME versionClassAll"' } },
  527. INDICATOR => { id => '87',
  528. set => { indicatorOff => "0100",
  529. indicatorOn => "01FF",
  530. indicatorDim => "01%02x" },
  531. get => { indicatorStatus => "02", },
  532. parse => { "038703(..)" => '($1 eq "00" ? "indState:off" :
  533. ($1 eq "ff" ? "indState:on" :
  534. "indState:dim ".hex($1)))'} },
  535. PROPRIETARY => { id => '88' },
  536. LANGUAGE => { id => '89' },
  537. TIME => { id => '8a' ,
  538. set => { timeOffset => 'ZWave_timeOffsetSet($hash, "%s")' },
  539. get => { time => "01",
  540. date => "03",
  541. timeOffset => "06" },
  542. parse => { "..8a04(.*)" => 'ZWave_dateReport($hash,$1)',
  543. "..8a02(.*)" => 'ZWave_timeReport($hash,$1)',
  544. "..8a07(.*)" => 'ZWave_timeOffsetReport($hash,$1)'} },
  545. TIME_PARAMETERS => { id => '8b',
  546. set => { timeParameters => 'ZWave_timeParametersSet($hash, "%s")'},
  547. get => { timeParameters => "02"},
  548. parse => { "..8b02" => 'timeParametersGet',
  549. "..8b03(.*)" => 'ZWave_timeParametersReport($hash, $1)' } },
  550. GEOGRAPHIC_LOCATION => { id => '8c' },
  551. COMPOSITE => { id => '8d' },
  552. MULTI_CHANNEL_ASSOCIATION=> { id => '8e', # aka MULTI_INSTANCE_ASSOCIATION
  553. set => { mcaAdd => "01%02x%02x*",
  554. mcaDel => "04%02x*" },
  555. get => { mca => "02%02x",
  556. mcaGroupings=> "05",
  557. mcaAll => 'ZWave_mcaAllGet($hash,"")' },
  558. parse => { "..8e03(..)(..)..(.*)" => 'ZWave_mcaReport($homeId,$1,$2,$3)',
  559. "..8e06(.*)"=> '"mcaGroups:".hex($1)' } },
  560. MULTI_CMD => { id => '8f' }, # Handled in Parse
  561. ENERGY_PRODUCTION => { id => '90' },
  562. MANUFACTURER_PROPRIETARY => { id => '91' }, # see also zwave_deviceSpecial
  563. SCREEN_MD => { id => '92' },
  564. SCREEN_ATTRIBUTES => { id => '93' },
  565. SIMPLE_AV_CONTROL => { id => '94' },
  566. AV_CONTENT_DIRECTORY_MD => { id => '95' },
  567. AV_RENDERER_STATUS => { id => '96' },
  568. AV_CONTENT_SEARCH_MD => { id => '97' },
  569. SECURITY => { id => '98',
  570. set => { "secSupportedReport" => 'ZWave_sec($hash, "02")', },
  571. parse => { "..9803(.*)"=> 'ZWave_secSupported($hash, $1)',
  572. "..9840" => 'ZWave_secNonceRequestReceived($hash)',
  573. "..9880(.*)"=> 'ZWave_secNonceReceived($hash, $1)',
  574. "..9881(.*)"=> 'ZWave_secDecrypt($hash, $1, 0)',
  575. "..98c1(.*)"=> 'ZWave_secDecrypt($hash, $1, 1)' } },
  576. AV_TAGGING_MD => { id => '99' },
  577. IP_CONFIGURATION => { id => '9a' },
  578. ASSOCIATION_COMMAND_CONFIGURATION
  579. => { id => '9b' },
  580. SENSOR_ALARM => { id => '9c',
  581. get => { alarm => "01%02x" },
  582. parse => { "..9c02(..)(..)(..)(....)" =>
  583. '"alarm_type_$2:level ".hex($3)." node ".hex($1)." seconds ".hex($4)'} },
  584. SILENCE_ALARM => { id => '9d' },
  585. SENSOR_CONFIGURATION => { id => '9e' },
  586. SECURITY_S2 => { id => '9f' },
  587. MARK => { id => 'ef' },
  588. NON_INTEROPERABLE => { id => 'f0' },
  589. );
  590. my %zwave_classVersion = (
  591. alarmEventSupported => { min => 3 },
  592. alarmTypeSupported => { min => 2 },
  593. alarmWithType => { min => 2 },
  594. alarmWithTypeEvent => { min => 3 },
  595. alarmnotification => { min => 2 },
  596. dimWithDuration => { min => 2 },
  597. dimUpDownWithDuration => { min => 2 },
  598. dimUpDownIncDecWithDuration => { min => 3 },
  599. meterReset => { min => 2 },
  600. meterSupported => { min => 2 },
  601. "on-for-timer" => { min => 2 },
  602. "off-for-timer" => { min => 2 },
  603. swmSupported => { min => 3 },
  604. tmEnergySaveHeating => { min => 2 },
  605. tmFullPower => { min => 3 },
  606. tmManual => { min => 3 },
  607. wakeupIntervalCapabilities => { min => 2 },
  608. );
  609. my %zwave_cmdArgs = (
  610. set => {
  611. dim => "slider,0,1,99",
  612. indicatorDim => "slider,0,1,99",
  613. rgb => "colorpicker,RGB",
  614. configRGBLedColorForTesting => "colorpicker,RGB", # Aeon SmartSwitch 6
  615. },
  616. get => {
  617. },
  618. parse => {
  619. }
  620. );
  621. use vars qw(%zwave_parseHook);
  622. #my %zwave_parseHook; # nodeId:regexp => fn, used by assocRequest
  623. my %zwave_modelConfig;
  624. my %zwave_modelIdAlias = ( "0175-0004-000a" => "devolo_Siren",
  625. "010f-0301-1001" => "Fibaro_FGRM222",
  626. "010f-0302-1000" => "Fibaro_FGRM222", # FGR 222
  627. "010f-0203-1000" => "Fibaro_FGS223",
  628. "0108-0004-000a" => "Philio_PSE02", # DLink DCH-Z510
  629. "013c-0004-000a" => "Philio_PSE02", # Zipato Siren
  630. "0115-0100-0102" => "ZME_KFOB" );
  631. # Patching certain devices.
  632. our %zwave_deviceSpecial;
  633. %zwave_deviceSpecial = (
  634. devolo_Siren => {
  635. ALARM => {
  636. set => { alarmSmokeOn =>"050000000001010000",
  637. alarmEmergencyOn=>"050000000007010000",
  638. alarmFireOn =>"05000000000a020000",
  639. alarmAmbulanceOn=>"05000000000a030000",
  640. alarmPoliceOn =>"05000000000a010000",
  641. alarmSilentOn =>"05000000000afe0000",
  642. alarmDoorchimeOn=>"050000000006160000",
  643. alarmArmOn =>"050000000006030000",
  644. alarmDisarmOn =>"050000000006040000",
  645. alarmBeepOn =>"05000000000a050000" } } },
  646. Fibaro_FGRM222 => {
  647. MANUFACTURER_PROPRIETARY => {
  648. set => { positionSlat=>"010f26010100%02x",
  649. positionBlinds=>"010f260102%02x00"},
  650. get => { position=>"010f2602020000", },
  651. parse => { "0891010f260303(..)(..)" =>
  652. 'sprintf("position:Blind %d Slat %d",hex($1),hex($2))',
  653. "0891010f260302(..)00" =>'"position:".hex($1)' } } },
  654. Fibaro_FGS223 => {
  655. ASSOCIATION => {
  656. init => {ORDER=>50, CMD => '"set $NAME associationDel 1 $CTRLID"'} },
  657. MULTI_CHANNEL_ASSOCIATION => {
  658. init => {ORDER=>51, CMD => '"set $NAME mcaAdd 1 0 $CTRLID 1"'} } },
  659. Philio_PSE02 => {
  660. ALARM => {
  661. set => { alarmEmergencyOn=>"050000000007010000",
  662. alarmFireOn =>"05000000000a020000",
  663. alarmAmbulanceOn=>"05000000000a030000",
  664. alarmPoliceOn =>"05000000000a010000",
  665. alarmDoorchimeOn=>"050000000006160000",
  666. alarmBeepOn =>"05000000000a050000" } } },
  667. ZME_KFOB => {
  668. ZWAVEPLUS_INFO => {
  669. # Example only. ORDER must be >= 50
  670. init => { ORDER=>50, CMD => '"get $NAME zwavePlusInfo"' } } }
  671. );
  672. my $zwave_cryptRijndael = 0;
  673. my $zwave_lastHashSent;
  674. my (%zwave_link, %zwave_img);
  675. my $zwave_activeHelpSites = "alliance,pepper"; # pepper alive again (aka zobie)
  676. my $zwave_allHelpSites = "alliance,pepper";
  677. # standard definitions for regular expression
  678. # naming scheme: p<number of returned groups>_name
  679. my $p1_m = "([0-5][0-9])"; # mm 00-59
  680. my $p2_hm = "([01][0-9]|2[0-3]):([0-5][0-9])"; # hh:mm
  681. my $p3_hms = "([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])"; # hh:mm:ss
  682. my $p1_b = "(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])"; # byte:0-255, 1-3 digits
  683. my $p1_wd = "(mon|tue|wed|thu|fri|sat|sun)"; # 3 letter weekday
  684. # ymd: yyyy-mm-dd, yyyy 4 digits, mm 2 digits 01-12, dd 2 digits 01-31
  685. my $p3_ymd = "([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])";
  686. sub
  687. ZWave_Initialize($)
  688. {
  689. my ($hash) = @_;
  690. $hash->{Match} = "^[0-9A-Fa-f]+\$";
  691. $hash->{SetFn} = "ZWave_Set";
  692. $hash->{GetFn} = "ZWave_Get";
  693. $hash->{DefFn} = "ZWave_Define";
  694. $hash->{UndefFn} = "ZWave_Undef";
  695. $hash->{AttrFn} = "ZWave_Attr";
  696. $hash->{ParseFn} = "ZWave_Parse";
  697. no warnings 'qw';
  698. my @attrList = qw(
  699. IODev
  700. WNMI_delay
  701. classes
  702. disable:0,1
  703. disabledForIntervals
  704. do_not_notify:noArg
  705. dummy:noArg
  706. eventForRaw
  707. extendedAlarmReadings:0,1,2
  708. ignore:noArg
  709. ignoreDupMsg:noArg
  710. neighborListPos
  711. noExplorerFrames:noArg
  712. noWakeupForApplicationUpdate:noArg
  713. secure_classes
  714. showtime:noArg
  715. vclasses
  716. useMultiCmd:noArg
  717. useCRC16:noArg
  718. zwaveRoute
  719. );
  720. use warnings 'qw';
  721. $hash->{AttrList} = join(" ", @attrList)." ".$readingFnAttributes;
  722. map { $zwave_id2class{lc($zwave_class{$_}{id})} = $_ } keys %zwave_class;
  723. $hash->{FW_detailFn} = "ZWave_fhemwebFn";
  724. $hash->{FW_deviceOverview} = 1;
  725. eval { require Crypt::Rijndael; };
  726. if($@) {
  727. Log 3, "ZWave: cannot load Crypt::Rijndael, SECURITY class disabled";
  728. } else {
  729. $zwave_cryptRijndael = 1;
  730. }
  731. ################
  732. # Read in the pepper/alliance translation table
  733. for my $n (split(",", $zwave_allHelpSites)) {
  734. my $fn = $attr{global}{modpath}."/FHEM/lib/zwave_${n}links.csv.gz";
  735. my $gz = gzopen($fn, "rb");
  736. if($gz) {
  737. my $line;
  738. while($gz->gzreadline($line)) {
  739. chomp($line);
  740. my @a = split(",",$line);
  741. $zwave_link{$n}{lc($a[0])} = $a[1];
  742. $zwave_img{$n}{lc($a[0])} = $a[2];
  743. }
  744. $gz->gzclose();
  745. } else {
  746. Log 3, "Can't open $fn: $!";
  747. }
  748. }
  749. # Create cache directory
  750. my $fn = $attr{global}{modpath}."/www/deviceimages";
  751. if(! -d $fn) { mkdir($fn) || Log 3, "Can't create $fn"; }
  752. $fn .= "/zwave";
  753. if(! -d $fn) { mkdir($fn) || Log 3, "Can't create $fn"; }
  754. }
  755. #############################
  756. my $zw_init_ordered;
  757. sub
  758. ZWave_Define($$)
  759. {
  760. my ($hash, $def) = @_;
  761. my @a = split("[ \t][ \t]*", $def);
  762. my $name = shift @a;
  763. my $type = shift @a; # always ZWave
  764. my $u = "wrong syntax for $name: define <name> ZWave homeId id [classes]";
  765. return $u if(int(@a) < 2 || int(@a) > 3);
  766. my $homeId = lc(shift @a);
  767. my $id = shift @a;
  768. return "define $name: wrong homeId ($homeId): need an 8 digit hex value"
  769. if( ($homeId !~ m/^[a-f0-9]{8}$/i) );
  770. return "define $name: wrong id ($id): need a number"
  771. if( ($id !~ m/^\d+$/i) );
  772. $hash->{ZWaveSubDevice} = ($id > 255 ? "yes" : "no");
  773. $id = sprintf("%0*x", ($id > 255 ? 4 : 2), $id);
  774. $hash->{homeId} = $homeId;
  775. $hash->{nodeIdHex} = $id;
  776. $modules{ZWave}{defptr}{"$homeId $id"} = $hash;
  777. my $proposed;
  778. if($init_done) { # Use the right device while inclusion is running
  779. for my $p (devspec2array("TYPE=ZWDongle|ZWCUL|FHEM2FHEM")) {
  780. $proposed = $p if($defs{$p}{homeId} && $defs{$p}{homeId} eq $homeId);
  781. }
  782. }
  783. AssignIoPort($hash, $proposed);
  784. $hash->{".vclasses"} = {};
  785. if(@a) { # Autocreate: set the classes, execute the init calls
  786. asyncOutput($hash->{IODev}{addCL}, "created $name") if($hash->{IODev});
  787. ZWave_SetClasses($homeId, $id, undef, $a[0]);
  788. }
  789. if($init_done) {
  790. ZWave_setEndpoints($hash);
  791. } else {
  792. if(!$zw_init_ordered) {
  793. $zw_init_ordered = 1;
  794. InternalTimer(1, "ZWave_setEndpoints", $hash, 0);
  795. }
  796. }
  797. return undef;
  798. }
  799. sub
  800. ZWave_setEndpoints($)
  801. {
  802. my $mp = $modules{ZWave}{defptr};
  803. for my $k (sort keys %{$mp}) {
  804. my $h = $mp->{$k};
  805. delete($h->{endpointParent});
  806. delete($h->{endpointChildren});
  807. }
  808. for my $k (sort keys %{$mp}) {
  809. my $h = $mp->{$k};
  810. next if($h->{nodeIdHex} !~ m/(..)(..)/);
  811. my ($root, $lid) = ($1, $2);
  812. my $rd = $mp->{$h->{homeId}." ".$root};
  813. $h->{endpointParent} = ($rd ? $rd->{NAME} : "unknown");
  814. $h->{".vclasses"} = ($rd ? $rd->{".vclasses"} : {} );
  815. if($rd) {
  816. if($rd->{endpointChildren}) {
  817. $rd->{endpointChildren} .= ",".$h->{NAME};
  818. } else {
  819. $rd->{endpointChildren} = $h->{NAME};
  820. }
  821. }
  822. }
  823. }
  824. sub
  825. ZWave_initFromModelfile($$$)
  826. {
  827. my ($name, $ctrlId, $cfg) = @_;
  828. my @res;
  829. ZWave_configParseModel($cfg) if(!$zwave_modelConfig{$cfg});
  830. my $mc = $zwave_modelConfig{$cfg};
  831. return @res if(!$mc);
  832. for my $grp (keys %{$mc->{group}}) {
  833. next if($grp eq '1');
  834. next if($mc->{group}{$grp} !~ m/auto="true"/);
  835. push @res, "set $name associationAdd $grp $ctrlId";
  836. }
  837. return @res;
  838. }
  839. sub
  840. ZWave_execInits($$;$)
  841. {
  842. my ($hash, $min, $cfg) = @_; # min = 50 for model-specific stuff
  843. my $cl = $attr{$hash->{NAME}}{classes};
  844. return "" if(!$cl);
  845. my @clList = split(" ", $cl);
  846. my (@initList, %seen);
  847. foreach my $cl (@clList) {
  848. next if($seen{$cl});
  849. $seen{$cl} = 1;
  850. my $ptr = ZWave_getHash($hash, $cl, "init");
  851. $ptr->{CC} = $cl if($ptr);
  852. push @initList, $ptr if($ptr && $ptr->{ORDER} >= $min);
  853. }
  854. my $NAME = $hash->{NAME};
  855. my $iodev = $hash->{IODev};
  856. my $homeReading = ReadingsVal($iodev->{NAME}, "homeId", "") if($iodev);
  857. my $CTRLID=hex($1) if($homeReading && $homeReading =~ m/CtrlNodeIdHex:(..)/);
  858. my @cmd;
  859. foreach my $i (sort { $a->{ORDER}<=>$b->{ORDER} } @initList) {
  860. my $version = $hash->{".vclasses"}{$i->{CC}};
  861. push @cmd, eval $i->{CMD} if(!defined($version) || $version > 0);
  862. }
  863. push @cmd, ZWave_initFromModelfile($hash->{NAME}, $CTRLID, $cfg)
  864. if($cfg); # model specific init, called from "get model"
  865. foreach my $cmd (@cmd) {
  866. my $ret = AnalyzeCommand(undef, $cmd);
  867. Log 1, "ZWAVE INIT: $cmd: $ret" if ($ret);
  868. }
  869. return "";
  870. }
  871. sub
  872. ZWave_neighborList($)
  873. {
  874. my ($hash) = @_;
  875. my $id = $hash->{nodeIdHex};
  876. # GET_ROUTING_TABLE_LINE, no dead links, include routing neighbors
  877. IOWrite($hash, "", "0080${id}0100");
  878. no strict "refs";
  879. my $iohash = $hash->{IODev};
  880. my $fn = $modules{$iohash->{TYPE}}{ReadAnswerFn};
  881. my ($err, $data) = &{$fn}($iohash, "neighborList", "^0180") if($fn);
  882. use strict "refs";
  883. return $err if($err);
  884. $data =~ s/^0180//;
  885. $data = zwlib_parseNeighborList($iohash, $data);
  886. readingsSingleUpdate($hash, "neighborList", $data, 1) if($hash);
  887. return $data;
  888. }
  889. ###################################
  890. sub
  891. ZWave_Cmd($$@)
  892. {
  893. my ($type, $hash, @a) = @_;
  894. return "no $type argument specified" if(int(@a) < 2);
  895. my $name = shift(@a);
  896. my $cmd = shift(@a);
  897. # Collect the commands from the distinct classes
  898. my %cmdList;
  899. my $classes = AttrVal($name, "classes", "");
  900. foreach my $cl (split(" ", $classes)) {
  901. my $ptr = ZWave_getHash($hash, $cl, $type);
  902. next if(!$ptr);
  903. foreach my $k (keys %{$ptr}) {
  904. if(!$cmdList{$k}) {
  905. $cmdList{$k}{fmt} = $ptr->{$k};
  906. $cmdList{$k}{id} = $zwave_class{$cl}{id};
  907. }
  908. }
  909. }
  910. my $id = $hash->{nodeIdHex};
  911. if($id !~ m/(....)/) { # not multiChannel
  912. if($type eq "set") {
  913. $cmdList{neighborUpdate} = { fmt=>"48$id", id=>"", ctrlCmd=>1 };
  914. $cmdList{returnRouteAdd} = { fmt=>"46$id%02x", id=>"", ctrlCmd=>1 };
  915. $cmdList{returnRouteDel} = { fmt=>"47$id", id=>"", ctrlCmd=>1 };
  916. my $iohash = $hash->{IODev};
  917. if($iohash && ReadingsVal($iohash->{NAME}, "sucNodeId","no") ne "no") {
  918. $cmdList{sucRouteAdd} = { fmt=>"51$id", id=>"", ctrlCmd=>1 };
  919. $cmdList{sucRouteDel} = { fmt=>"55$id", id=>"", ctrlCmd=>1 };
  920. }
  921. }
  922. $cmdList{neighborList}{fmt} = "x" if($type eq "get"); # Add meta command
  923. }
  924. if($type eq "set" && $cmd eq "rgb") {
  925. if($a[0] && $a[0] =~ m/^[0-9A-F]+$/i && $a[0] =~ /^(..)(..)(..)$/) {
  926. @a = (hex($1), hex($2), hex($3));
  927. } else {
  928. return "set rgb: a 6-digit hex number is required";
  929. }
  930. }
  931. if(!$cmdList{$cmd}) {
  932. my @list;
  933. my $mc = ReadingsVal($hash->{NAME}, "modelConfig", "");
  934. foreach my $lcmd (sort keys %cmdList) {
  935. if($mc && $zwave_cmdArgs{$type}{"$mc$lcmd"}) {
  936. push @list, "$lcmd:".$zwave_cmdArgs{$type}{"$mc$lcmd"};
  937. } elsif($zwave_cmdArgs{$type}{$lcmd}) {
  938. push @list, "$lcmd:$zwave_cmdArgs{$type}{$lcmd}";
  939. } elsif($cmdList{$lcmd}{fmt} !~ m/%/) {
  940. push @list, "$lcmd:noArg";
  941. } else {
  942. push @list, $lcmd;
  943. }
  944. }
  945. my $list = join(" ",@list);
  946. if($type eq "set") {
  947. unshift @a, $name, $cmd;
  948. return SetExtensions($hash, $list, @a);
  949. } else {
  950. return "Unknown argument $cmd, choose one of $list";
  951. }
  952. }
  953. SetExtensionsCancel($hash) if($type eq "set");
  954. return "" if(IsDisabled($name));
  955. return ZWave_neighborList($hash) if($cmd eq "neighborList");
  956. ################################
  957. # ZW_SEND_DATA,nodeId,CMD,ACK|AUTO_ROUTE
  958. my $cmdFmt = $cmdList{$cmd}{fmt};
  959. my $cmdId = $cmdList{$cmd}{id};
  960. # 0x05=AUTO_ROUTE+ACK, 0x20: ExplorerFrames
  961. my $nArg = 0;
  962. if($cmdFmt =~ m/%/) {
  963. my @ca = split("%", $cmdFmt);
  964. $nArg = int(@ca)-1;
  965. }
  966. my $parTxt = ($nArg == 0 ? "no parameter" :
  967. ($nArg == 1 ? "one parameter" :
  968. "$nArg parameters"));
  969. if($cmdFmt =~ m/^(.*)\*$/) {
  970. $cmdFmt = $1;
  971. return "$type $cmd needs at least $parTxt" if($nArg > int(@a));
  972. $cmdFmt .= ("%02x" x (int(@a)-$nArg));
  973. } elsif($cmdFmt =~ m/%s/) { # vararg for functions
  974. $nArg = 0 if(!@a);
  975. @a = (join(" ", @a));
  976. } else {
  977. return "$type $cmd needs $parTxt" if($nArg != int(@a));
  978. }
  979. if($cmdFmt !~ m/%s/ && $cmd !~ m/^config/) {
  980. for(my $i1 = 0; $i1<int(@a); $i1++) {
  981. return "Error: $a[$i1] is not a decimal number"
  982. if($a[$i1] !~ m/^[-\d]+$/);
  983. }
  984. }
  985. if($cmd =~ m/^config/ && $cmd ne "configAll") {
  986. my ($err, $lcmd) =
  987. ZWave_configCheckParam($hash, $type, $cmd, $cmdFmt, @a);
  988. return $err if($err);
  989. $cmdFmt = $lcmd;
  990. } else {
  991. $cmdFmt = sprintf($cmdFmt, @a) if($nArg);
  992. $@ = undef;
  993. my ($err, $ncmd) = eval($cmdFmt) if($cmdFmt !~ m/^\d/);
  994. return $err if($err);
  995. return $@ if($@);
  996. $cmdFmt = $ncmd if(defined($ncmd));
  997. return "" if($ncmd && $ncmd eq "EMPTY"); # configAll
  998. }
  999. Log3 $name, 3, "ZWave $type $name $cmd ".join(" ", @a);
  1000. my ($baseClasses, $baseHash) = ($classes, $hash);
  1001. delete($hash->{lastChannelUsed});
  1002. if($id =~ m/(..)(..)/) { # Multi-Channel, encapsulate
  1003. my ($baseId,$ch) = ($1, $2);
  1004. $id = $baseId;
  1005. $cmdFmt = "0d00$ch$cmdId$cmdFmt";
  1006. $cmdId = "60"; # MULTI_CHANNEL
  1007. $baseHash = $modules{ZWave}{defptr}{"$hash->{homeId} $baseId"};
  1008. $baseClasses = AttrVal($baseHash->{NAME}, "classes", "");
  1009. $baseHash->{lastChannelUsed} = $name;
  1010. }
  1011. my $data;
  1012. if(!$cmdId) {
  1013. $data = $cmdFmt;
  1014. $data .= ZWave_callbackId($baseHash);
  1015. } else {
  1016. my $len = sprintf("%02x", length($cmdFmt)/2+1);
  1017. my $cmdEf = (AttrVal($name, "noExplorerFrames", 0) == 0 ? "25" : "05");
  1018. $data = "13$id$len$cmdId${cmdFmt}$cmdEf"; # 13==SEND_DATA
  1019. $data .= ZWave_callbackId($baseHash);
  1020. }
  1021. if($type eq "get" && $hash->{CL} && !ZWave_isWakeUp($hash)) {
  1022. if(!$hash->{asyncGet}) { # Wait for the result for frontend cmd
  1023. my $tHash = { hash=>$hash, CL=>$hash->{CL}, re=>"^000400${id}..$cmdId"};
  1024. $hash->{asyncGet} = $tHash;
  1025. InternalTimer(gettimeofday()+4, sub {
  1026. asyncOutput($tHash->{CL}, "Timeout reading answer for $cmd");
  1027. delete($hash->{asyncGet});
  1028. }, $tHash, 0);
  1029. }
  1030. }
  1031. if ($data =~ m/(......)(....)(.*)(....)/) {
  1032. my $cc_cmd=$2;
  1033. my $payload=$3;
  1034. #check message here for needed encryption (SECURITY)
  1035. if(ZWave_secIsSecureClass($hash, $cc_cmd)) {
  1036. ZWave_secStart($hash);
  1037. # message stored in hash, will be processed when nonce arrives
  1038. my $cmd2 = "$type $name $cmd";
  1039. $cmd2 .= " ".join(" ", @a) if(@a);
  1040. ZWave_secPutMsg($hash, $cc_cmd . $payload, $cmd2);
  1041. ZWave_secAddToSendStack($baseHash, '9840');
  1042. return;
  1043. }
  1044. }
  1045. my $r = ZWave_addToSendStack($baseHash, $type, $data);
  1046. return (AttrVal($name,"verbose",3) > 2 ? $r : undef) if($r);
  1047. if($type ne "get") {
  1048. if(!$cmdId) {
  1049. ZWave_processSendStack($baseHash, "next");
  1050. }
  1051. $cmd .= " ".join(" ", @a) if(@a);
  1052. my $iohash = $hash->{IODev};
  1053. my $withSet = ($iohash->{showSetInState} && !$cmdList{$cmd}{ctrlCmd});
  1054. readingsSingleUpdate($hash, "state", $withSet ? "set_$cmd" : $cmd, 1);
  1055. }
  1056. return undef;
  1057. }
  1058. sub
  1059. ZWave_SCmd($$@)
  1060. {
  1061. my ($type, $hash, @a) = @_;
  1062. if($hash->{secInProgress} && !(@a < 2 || $a[1] eq "?")) {
  1063. my %h = ( T => $type, A => \@a );
  1064. push @{$hash->{secStack}}, \%h;
  1065. return ($type eq "get" ?
  1066. "Secure operation in progress, executing in background" : "");
  1067. }
  1068. return ZWave_Cmd($type, $hash, @a);
  1069. }
  1070. sub ZWave_Set($@) { return ZWave_SCmd("set", shift, @_); }
  1071. sub ZWave_Get($@) { return ZWave_SCmd("get", shift, @_); }
  1072. # returns supported Parameters by hrvStatus
  1073. sub
  1074. ZWave_HrvStatus($)
  1075. {
  1076. my ($p) = @_;
  1077. $p = hex($p);
  1078. my @hrv_status = ( "outdoorTemperature", "supplyAirTemperature",
  1079. "exhaustAirTemperature", "dischargeAirTemperature",
  1080. "indoorTemperature", "indoorHumidity",
  1081. "remainingFilterLife" );
  1082. my @l;
  1083. for(my $i=0; $i < 7; $i++) {
  1084. push @l, "$i = $hrv_status[$i]" if($p & (1<<$i));
  1085. }
  1086. return join("\n", @l);
  1087. }
  1088. sub
  1089. ZWave_ccCapability($$)
  1090. {
  1091. my ($l,$h) = @_;
  1092. my @names = ("WarmWhite","ColdWhite","Red","Green",
  1093. "Blue","Amber","Cyan","Purple","Indexed");
  1094. my $x = hex($l)+256*hex($h);
  1095. my @ret = "ccCapability:";
  1096. for(my $i=0; $i<int(@names); $i++) {
  1097. push @ret,$names[$i] if($x & (1<<$i));
  1098. }
  1099. return join(" ",@ret);
  1100. }
  1101. sub
  1102. ZWave_scheduleEntryLockSet ($$)
  1103. {
  1104. my ($hash, $arg) = @_;
  1105. my $name = $hash->{NAME};
  1106. return ("wrong format, see commandref", "")
  1107. if($arg !~ m/([0-2]?[0-9]?[0-9]) ((en|dis)abled)/);
  1108. return ("User Identifier: only 0-255 allowed", "") if (($1<0) || ($1>255));
  1109. my $rt = sprintf("01%02x%02x", $1, ($2 eq "enabled") ? "01" : "02");
  1110. return ("",$rt);
  1111. }
  1112. sub
  1113. ZWave_scheduleEntryLockAllSet ($$)
  1114. {
  1115. my ($hash, $arg) = @_;
  1116. my $name = $hash->{NAME};
  1117. return ("wrong format, see commandref", "")
  1118. if($arg !~ m/((en|dis)abled)/);
  1119. my $rt = sprintf("02%02x", ($1 eq "enabled") ? "01" : "02");
  1120. return ("",$rt);
  1121. }
  1122. sub
  1123. ZWave_scheduleEntryLockWeekDayParse ($$)
  1124. {
  1125. my ($hash, $val) = @_;
  1126. return if($val !~ m/^(..)(..)(..)(..)(..)(..)(..)/);
  1127. my $userId = sprintf("userID: %d", hex($1));
  1128. my $scheduleSlotId = sprintf("slotID: %d", hex($2));
  1129. # Attention! scheduleEntryLock use different definition of weekday!
  1130. my @dow = ("sun","mon","tue","wed","thu","fri","sat");
  1131. my $dayOfWeek = (hex($3) < 7) ? $dow[hex($3)] : "invalid";
  1132. my $start = sprintf("%02d:%02d", hex($4), hex($5));
  1133. my $end = sprintf("%02d:%02d", hex($6), hex($7));
  1134. my $rt1 = sprintf ("weekDaySchedule_%d:", hex($1));
  1135. $rt1 .= "$userId $scheduleSlotId $dayOfWeek $start $end";
  1136. return ($rt1);
  1137. }
  1138. sub
  1139. ZWave_scheduleEntryLockWeekDaySet ($$)
  1140. {
  1141. my ($hash, $arg) = @_;
  1142. my $name = $hash->{NAME};
  1143. if($arg !~
  1144. m/([01]) $p1_b $p1_b $p1_wd $p2_hm $p2_hm/) {
  1145. return ("wrong format, see commandref", "");
  1146. }
  1147. if (($2>255) || ($3>255)) {
  1148. return ("values our of range, see commandref", "");
  1149. }
  1150. # Attention! scheduleEntryLock use different definition of weekday!
  1151. my @dow = ("sun","mon","tue","wed","thu","fri","sat");
  1152. my $wd;
  1153. map { $wd=sprintf("%02x",$_) if($dow[$_] eq $4) }(0..int($#dow));
  1154. return ("Unknown weekday $4, use one of ".join(" ", @dow), "")
  1155. if(!defined($wd));
  1156. my $rt = sprintf("03%02x%02x%02x%02x%02x%02x%02x%02x",
  1157. $1,$2,$3,$wd,$5,$6,$7,$8);
  1158. return ("",$rt);
  1159. }
  1160. sub
  1161. ZWave_scheduleEntryLockDailyRepeatingParse ($$)
  1162. {
  1163. my ($hash, $val) = @_;
  1164. return if($val !~ m/^(..)(..)(..)(..)(..)(..)(..)/);
  1165. my $userID = sprintf ("userID: %d", hex($1));
  1166. my $scheduleID = sprintf ("slotID: %d", hex($2));
  1167. # bit field for week day: b7 reserved, b6 sat, b5 fri, ..., b0 sun
  1168. my $w = hex($3);
  1169. #my $wd = sprintf("weekDaySchedule: %07b ", $w);
  1170. my $wd = "weekDaySchedule: ";
  1171. my $nu = "...";
  1172. $wd .= (($w & 0x02) == 0x02) ? "mon" : $nu;
  1173. $wd .= (($w & 0x04) == 0x04) ? "tue" : $nu;
  1174. $wd .= (($w & 0x08) == 0x08) ? "wed" : $nu;
  1175. $wd .= (($w & 0x10) == 0x10) ? "thu" : $nu;
  1176. $wd .= (($w & 0x20) == 0x20) ? "fri" : $nu;
  1177. $wd .= (($w & 0x40) == 0x40) ? "sat" : $nu;
  1178. $wd .= (($w & 0x01) == 0x01) ? "sun" : $nu;
  1179. my $start = sprintf("Start: %02d:%02d" , hex($4), hex($5));
  1180. my $duration = sprintf("Duration: %02d:%02d" , hex($6), hex($7));
  1181. my $rt1 = sprintf ("scheduleEntryLockDailyRepeating_%d:", hex($1));
  1182. $rt1 .= "$userID $scheduleID $wd $start $duration";
  1183. return ($rt1);
  1184. }
  1185. sub
  1186. ZWave_scheduleEntryLockDailyRepeatingSet ($$)
  1187. {
  1188. my ($hash, $arg) = @_;
  1189. my $name = $hash->{NAME};
  1190. if($arg !~
  1191. m/([01]) $p1_b $p1_b ((?:[\.a-zA-Z]{3}){1,7}) $p2_hm $p2_hm/) {
  1192. return ("wrong format, see commandref", "");
  1193. }
  1194. my $rt1 = sprintf("%02x%02x%02x", $1, $2, $3);
  1195. my $rt2 = sprintf("%02x%02x%02x%02x", $5, $6, $7, $8);
  1196. my $w = $4;
  1197. my $wd = 0x00;
  1198. $wd |= 0x02 if ($w =~ /mon/i);
  1199. $wd |= 0x04 if ($w =~ /tue/i);
  1200. $wd |= 0x08 if ($w =~ /wed/i);
  1201. $wd |= 0x10 if ($w =~ /thu/i);
  1202. $wd |= 0x20 if ($w =~ /fri/i);
  1203. $wd |= 0x40 if ($w =~ /sat/i);
  1204. $wd |= 0x01 if ($w =~ /sun/i);
  1205. my $rt = sprintf("10$rt1%02x$rt2", $wd);
  1206. return ("",$rt);
  1207. }
  1208. sub
  1209. ZWave_scheduleEntryLockYearDayParse ($$)
  1210. {
  1211. my ($hash, $val) = @_;
  1212. return if($val !~ m/^(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)/);
  1213. my $userID = sprintf ("userID: %d", hex($1));
  1214. my $scheduleID = sprintf ("slotID: %d", hex($2));
  1215. my $startdate = sprintf ("start: %4d-%02d-%02d %02d:%02d",
  1216. 2000+hex($3),hex($4),hex($5),hex($6),hex($7));
  1217. my $enddate = sprintf ("end: %4d-%02d-%02d %02d:%02d",
  1218. 2000+hex($8),hex($9),hex($10),hex($11),hex($12));
  1219. my $rt1 = sprintf ("yearDaySchedule_%d:", hex($1));
  1220. $rt1 .= "$userID $scheduleID $startdate $enddate";
  1221. return ($rt1);
  1222. }
  1223. sub
  1224. ZWave_scheduleEntryLockYearDaySet ($$)
  1225. {
  1226. my ($hash, $arg) = @_;
  1227. my $name = $hash->{NAME};
  1228. if($arg !~
  1229. m/([01]) $p1_b $p1_b $p3_ymd $p2_hm $p3_ymd $p2_hm/) {
  1230. return ("wrong format, see commandref", "");
  1231. }
  1232. my $sy = $4-2000;
  1233. my $ey = $9-2000;
  1234. if (($sy < 0) || ($sy > 255) || ($ey < 0) || ($ey > 255)) {
  1235. Log3 $name, 1, "$name: year out of range";
  1236. return ("wrong value for year, only 2000-2255 allowed","");
  1237. }; # no sanity check of date (e.g. 2016-02-31 will pass)
  1238. my $rt = sprintf("06%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
  1239. $1, $2, $3, $sy, $5, $6, $7, $8, $ey, $10, $11, $12, $13);
  1240. return ("",$rt);
  1241. }
  1242. sub
  1243. ZWave_scheduleEntryLockTimeOffsetParse ($$)
  1244. {
  1245. my ($hash, $val) = @_;
  1246. return if($val !~ m/^(..)(..)(..)/);
  1247. my $sTZO = ((hex($1) & 0x80) == 0x80) ? '-' : '+';
  1248. my $TZO = sprintf("TZO: %s%02d:%02d", $sTZO, (hex($1) & 0x7f), hex($2));
  1249. my $sDST = ((hex($3) & 0x80) == 0x80) ? '-' : '+';
  1250. my $DST = sprintf("DST: %s%02d", $sDST, (hex($3) & 0x7f));
  1251. my $rt1 = "scheduleEntryLockTimeOffset: $TZO $DST";
  1252. return ($rt1);
  1253. }
  1254. sub
  1255. ZWave_scheduleEntryLockTimeOffsetSet ($$)
  1256. {
  1257. my ($hash, $arg) = @_;
  1258. my $name = $hash->{NAME};
  1259. if($arg !~ m/([\+\-])$p2_hm ([\+\-])$p1_b/) {
  1260. return ("wrong format, see commandref", "");
  1261. }
  1262. my $hTZO = $2 | (($1 eq '-') ? 0x80 : 0x00);
  1263. my $mTZO = $3;
  1264. my $mDST = $5 | (($4 eq '-') ? 0x80 : 0x00);
  1265. my $rt = sprintf("0d%02x%02x%02x", $hTZO, $mTZO, $mDST);
  1266. return ("",$rt);
  1267. }
  1268. sub
  1269. ZWave_scheduleEntryLockTypeSupportedParse ($$)
  1270. {
  1271. my ($hash, $val) = @_;
  1272. return if($val !~ m/^(..)(..)(.*)/);
  1273. my $numWDslots = sprintf ("WeekDaySlots: %d", hex($1));
  1274. my $numYDslots = sprintf ("YearDaySlots: %d", hex($2));
  1275. # only for V3
  1276. my $numDailySlots = '';
  1277. $numDailySlots = sprintf (" DailyRepeatingSlots: %d", hex($3)) if ($3);
  1278. my $rt1 = "scheduleEntryLockEntryTypeSupported:"
  1279. ." $numWDslots $numYDslots$numDailySlots";
  1280. return ($rt1);
  1281. }
  1282. sub
  1283. ZWave_thermostatSetpointGet($)
  1284. {
  1285. my ($type) = @_;
  1286. $type = (($type eq "%s") ? "1" : $type);
  1287. return ("wrong format, only 1-15 allowed","")
  1288. if ($type !~ m/^(1[0-5]?|[0]?[1-9])$/);
  1289. return("",sprintf('02%02x', $type & 0x0f));
  1290. }
  1291. sub
  1292. ZWave_thermostatSetpointSet ($$)
  1293. {
  1294. my ($hash, $arg) = @_;
  1295. my $name = $hash->{NAME};
  1296. my $p1_temp = "([\+\-]?(?:(?:[0-9]+(?:\.[0-9]*)?)|(?:\.[0-9]+)))";
  1297. my $p1_scale = "([cC|fF])";
  1298. my $p1_type = "(1[0-5]?|[0]?[1-9])";
  1299. my $p1_prec = "([0-7])";
  1300. my $p1_size = "([124])";
  1301. if($arg !~
  1302. m/$p1_temp+ ?$p1_scale?+ ?$p1_type?+ ?$p1_prec?+ ?$p1_size?+/) {
  1303. return ("wrong format, see commandref", "");
  1304. }
  1305. my $temp = $1;
  1306. my $scale = (defined $2) ? $2 : "c"; # default to c = celsius
  1307. $scale = (lc($scale) eq "f") ? 1 : 0;
  1308. my $type = ((defined $3) ? $3 : 1) & 0x0f; # default to 1 = heating
  1309. my $prec = ((defined $4) ? $4 : 1) & 0x07; # default to 1 decimal
  1310. my $size = ((defined $5) ? $5 : 2) & 0x07; # default to 2 byte size
  1311. my $sp = int($temp * (10 ** $prec));
  1312. my $max = (2 ** (8*$size-1)) - 1;
  1313. my $min = -1 * (2 ** (8*$size-1));
  1314. if (($sp > $max) || ($sp < $min)) {
  1315. my $rt = sprintf("temperature value out of range for given "
  1316. ."size and precision [%0.*f, %0.*f]",
  1317. $prec, $min/(10**$prec), $prec, $max/(10**$prec));
  1318. return ($rt, "");
  1319. }
  1320. $sp = ($sp < 0) ? $sp + 2**(8*$size) : $sp;
  1321. $sp = sprintf("%0*x", 2*$size, $sp);
  1322. $type = sprintf("%02x", $type);
  1323. my $precScaleSize = sprintf("%02x", ($size | ($scale<<3) | ($prec<<5)));
  1324. my $rt = "01$type$precScaleSize$sp";
  1325. return ("",$rt);
  1326. }
  1327. sub
  1328. ZWave_thermostatSetpointParse ($$)
  1329. {
  1330. my ($hash, $val) = @_;
  1331. my $name = $hash->{NAME};
  1332. return if($val !~ m/^(..)(..)(.*)/);
  1333. my @setpointtype = ( # definition from V3
  1334. "notSupported",
  1335. "heating",
  1336. "cooling",
  1337. "notSupported1",
  1338. "notSupported2",
  1339. "notSupported3",
  1340. "notSupported4",
  1341. "furnance",
  1342. "dryAir",
  1343. "moistAir",
  1344. "autoChangeover",
  1345. "energySaveHeating",
  1346. "energySaveCooling",
  1347. "awayHeating",
  1348. "awayCooling",
  1349. "fullPower"
  1350. );
  1351. my $type = $setpointtype[(hex($1) & 0x0f)];
  1352. my $prec = (hex($2) & 0xe0)>>5;
  1353. my $scale = (((hex($2) & 0x18)>>3) == 1) ? "F": "C";
  1354. my $size = (hex($2) & 0x07);
  1355. if (length($3) != $size*2) {
  1356. Log3 $name, 1, "$name: THERMOSTAT_SETPOINT_REPORT "
  1357. ."wrong number of bytes received";
  1358. return;
  1359. }
  1360. my $sp = hex($3);
  1361. $sp -= (2 ** ($size*8)) if $sp >= (2 ** ($size*8-1));
  1362. $sp = $sp / (10 ** $prec);
  1363. # output temperature with variable decimals as reported (according to $prec)
  1364. my $rt = sprintf("setpointTemp:%0.*f %s %s", $prec, $sp, $scale, $type);
  1365. return ($rt);
  1366. }
  1367. sub
  1368. ZWave_thermostatSetpointSupportedParse ($$)
  1369. {
  1370. # only defined for version >= V3
  1371. my ($hash, $val) = @_;
  1372. my $name = $hash->{NAME};
  1373. return if($val !~ m/^(.*)/);
  1374. my $n = length($val)/2;
  1375. my @supportedType = ( # definition from V3
  1376. "none", # 0x00
  1377. "heating", # 0x01
  1378. "cooling", # 0x02
  1379. "furnance", # 0x07 !type 3 to 6 left out!
  1380. "dryAir", # 0x08
  1381. "moistAir", # 0x09
  1382. "autoChangeover", # 0x0a
  1383. "energySaveHeating", # 0x0b
  1384. "energySaveCooling", # 0x0c
  1385. "awayHeating", # 0x0d
  1386. "awayCooling", # 0x0e
  1387. "fullPower" # 0x0f
  1388. );
  1389. my $numTypes = @supportedType;
  1390. my $delimeter = '';
  1391. my $rt = "thermostatSetpointSupported:";
  1392. for (my $i=0; $i<$n; $i++) {
  1393. # loop over all supplied bytes
  1394. my $supported = hex(substr($val, $i*2, 2));
  1395. for (my $j=$i*8; $j<$i*8+8; $j++) {
  1396. # loop over all bits
  1397. last if $j >= $numTypes;
  1398. if ($supported & (2 ** ($j-$i*8))) {
  1399. $rt .= sprintf("$delimeter$supportedType[$j]");
  1400. $delimeter = " ";
  1401. }
  1402. }
  1403. }
  1404. return ($rt);
  1405. }
  1406. sub
  1407. ZWave_scheduleSupportedParse ($$)
  1408. {
  1409. my ($hash, $val) = @_;
  1410. return if($val !~ m/^(..)(..)(..)(.*)(..)/);
  1411. my $numSupported = sprintf("num: %d", hex($1));
  1412. my $sTimeSupport = sprintf("startTimeSupport: %06b", (hex($2) & 0x3f));
  1413. my $fbSupport = sprintf("fallbackSupport: %1b", (hex($2) & 0x40));
  1414. my $sEnaDis = sprintf("enableDisableSupport: %1b", (hex($2) & 0x80));
  1415. my $numSupportedCC = sprintf("numCCs: %d", hex($3));
  1416. my $OverrideTypes = sprintf("overrideTypes: %07b", (hex($5) & 0x7f));
  1417. my $overrideSupport = sprintf("overrideSupport: %1b", (hex($5) & 0x80));
  1418. my $supportedCCs = "";
  1419. if (hex($3)>0) {
  1420. $val = $4;
  1421. for (my $i=0;$i<hex($3); $i++) {
  1422. $val =~ m/(..)(..)(.*)/;
  1423. my $supportedCC = sprintf ("CC_%d: %d CCname_%d: %s",
  1424. $i+1, hex($1), $i+1, $zwave_id2class{lc($1)});
  1425. my $supportedCCmask = sprintf (" CCmask_%d: %02b",
  1426. $i+1, (hex($2) & 0x03));
  1427. $supportedCCs .= " " if $i >0;
  1428. $supportedCCs .= $supportedCC . $supportedCCmask;
  1429. $val = $3;
  1430. }
  1431. }
  1432. my $rt1 = "scheduleEntryLockScheduleSupported:"
  1433. ."$numSupported $sTimeSupport $fbSupport "
  1434. ."$sEnaDis $numSupportedCC $OverrideTypes $overrideSupport";
  1435. my $rt2 = "scheduleEntryLockScheduleSupportedCC:$supportedCCs";
  1436. return ($rt1, $rt2);
  1437. }
  1438. sub
  1439. ZWave_scheduleStateSet ($$)
  1440. {
  1441. my ($hash, $arg) = @_;
  1442. my $name = $hash->{NAME};
  1443. return ("wrong format, see commandref", "") if($arg !~ m/(.*?) (.*?)/);
  1444. my $rt = sprintf("07%02x%02x", $1, $2);
  1445. return ("",$rt);
  1446. }
  1447. sub
  1448. ZWave_scheduleSet ($$)
  1449. {
  1450. my ($hash, $arg) = @_;
  1451. my $name = $hash->{NAME};
  1452. if($arg !~
  1453. # 1 2 3 4 5 6 7 8 9 10 11 12
  1454. m/(.*?) (.*?) (....)-(..)-(..) (.*?) (.*?) (..):(..) (.*?) (.*?) (.*)/) {
  1455. return ("wrong format, see commandref", "");
  1456. }
  1457. my $ID = sprintf("%02x", $1);
  1458. my $uID = sprintf("%02x", $2);
  1459. my $sYear = sprintf("%02x", $3 - 2000);
  1460. my $sMonth = sprintf("%02x", $4 & 0x0f);
  1461. my $sDay = sprintf("%02x", $5 & 0x1f);
  1462. my $sWDay = sprintf("%02x", $6 & 0x7f);
  1463. my $durationType = ($7<<5) & 0xe0;
  1464. my $sHour = sprintf("%02x", (($8 & 0x1f) | $durationType));
  1465. my $sMinute = sprintf("%02x", $9 & 0x3f);
  1466. my $duration = sprintf("%04x", $10);
  1467. my $numReports = sprintf("%02x", $11);
  1468. my $cmdgroup ="";
  1469. my @param;
  1470. if (length($12)>0) { # cmd(s) given
  1471. @param = split (" ", $12);
  1472. Log3 $name, 1, "$name: param: $#param $12";
  1473. for (my $i=0; $i<=$#param; $i++) {
  1474. $cmdgroup .= sprintf("%02x%s", length($param[$i])/2, $param[$i]);
  1475. }
  1476. }
  1477. my $numCmd = sprintf("%02x", $#param+1);
  1478. my $rt = "03" .$ID .$uID .$sYear .$sMonth .$sDay .$sWDay;
  1479. $rt .= $sHour .$sMinute .$duration .$numReports .$numCmd .$cmdgroup;
  1480. #~ Log3 $name, 1, "$name: $rt";
  1481. return ("",$rt);
  1482. }
  1483. sub
  1484. ZWave_scheduleParse($$)
  1485. {
  1486. my ($hash, $val) = @_;
  1487. return if($val !~ m/^(..)(..)(..)(..)(..)(..)(..)(..)(....)(..)(..)(..)(.*)/);
  1488. my $scheduleID = sprintf ("ID: %d", hex($1));
  1489. my $userID = sprintf ("userID: %d", hex($2));
  1490. my $startYear = sprintf ("sYear: %d", 2000 + hex($3));
  1491. my $startMonth = sprintf ("sMonth: %d", (hex($4) & 0x0f));
  1492. my $activeID = sprintf ("activeID: %d", (hex($4) & 0xf0)>>4);
  1493. my $startDay = sprintf ("sDay: %d", (hex($5) & 0x1f));
  1494. my $sWeekDay = sprintf ("sWeekDay: %d", (hex($6) & 0x7f));
  1495. my $startHour = sprintf ("sHour: %d", (hex($7) & 0x1f));
  1496. my $durationType = sprintf ("durationType: %d", (hex($7) & 0x1f)>>5);
  1497. my $startMinute = sprintf ("sMinute: %d", (hex($8) & 0x3f));
  1498. my $duration = sprintf ("duration: %d", hex($9));
  1499. my $numReports = sprintf ("numReportsToFollow: %d", hex($10));
  1500. my $numCmds = sprintf ("numCmds: %d", hex ($11));
  1501. my $cmdlen = sprintf ("cmdLen: %d", hex($12));
  1502. my $cmd = sprintf ("cmd: %s", $13);
  1503. my $rt1 = sprintf ("schedule_%d:", hex($1));
  1504. $rt1 .= "$scheduleID $userID $startYear $startMonth $activeID ".
  1505. "$startDay $sWeekDay $startHour $durationType $startMinute ".
  1506. "$duration $numReports $numCmds $cmdlen $cmd";
  1507. return ($rt1);
  1508. }
  1509. sub
  1510. ZWave_scheduleStateParse($$)
  1511. {
  1512. my ($hash, $val) = @_;
  1513. return if($val !~ m/^(..)(..)(..)(..)/);
  1514. my $numSupportedIDs = sprintf ("numIDs: %d", hex($1));
  1515. my $override = sprintf ("overried: %b", (hex($2) & 0x01));
  1516. my $numReports = sprintf ("numReportsToFollow: %d", (hex($2) & 0xfe)>>1);
  1517. my $ID1 = sprintf ("ID1: %d", (hex($3) & 0x0f));
  1518. my $ID2 = sprintf ("ID2: %d", (hex($3) & 0xf0)>>4);
  1519. my $ID3 = sprintf ("ID3: %d", (hex($4) & 0x0f));
  1520. my $IDN = sprintf ("IDn: %d", (hex($4) & 0xf0)>>4);
  1521. my $rt1 .= "scheduleState:$numSupportedIDs $override $numReports ".
  1522. "$ID1 $ID2 $ID3 $IDN";
  1523. return ($rt1);
  1524. }
  1525. sub
  1526. ZWave_assocGroupCmdList($$)
  1527. {
  1528. my ($grp, $txt) = @_;
  1529. $txt =~ s/(..)(..)/
  1530. ($zwave_id2class{$1} ? $zwave_id2class{$1}:"UNKNOWN_$1").":$2 "/ge;
  1531. return "assocGroupCmdList_".hex($grp).":$txt";
  1532. }
  1533. my %zwm_unit = (
  1534. energy=> ["kWh", "kVAh", "W", "pulseCount", "V", "A", "PowerFactor"],
  1535. gas => ["m3", "feet3", "undef", "pulseCount"],
  1536. water => ["m3", "feet3", "USgallons", "pulseCount"]
  1537. );
  1538. sub
  1539. ZWave_meterParse($$)
  1540. {
  1541. my ($hash,$val) = @_;
  1542. return if($val !~ m/^(..)(..)(.*)$/);
  1543. my ($v1, $v2, $v3) = (hex($1), hex($2), $3);
  1544. my $name = $hash->{NAME};
  1545. # rate_type currently not used / not reported
  1546. my $rate_type = ($v1 >> 5) & 0x3;
  1547. my @rate_type_text =("undef","consumed", "produced");
  1548. my $rate_type_text = ($rate_type > $#rate_type_text ?
  1549. "undef" : $rate_type_text[$rate_type]);
  1550. my $meter_type = ($v1 & 0x1f);
  1551. my @meter_type_text =("undef", "energy", "gas", "water", "undef");
  1552. my $meter_type_text = ($meter_type > $#meter_type_text ?
  1553. "undef" : $meter_type_text[$meter_type]);
  1554. my $precision = ($v2>>5) & 0x7; # 3 bits
  1555. my $scale = ($v2>>3) & 0x3; # 2 bits, meaning unit
  1556. my $size = $v2 & 0x7; # 3 bits
  1557. $scale |= (($v1 & 0x80) >> 5);
  1558. my $unit_text = ($meter_type_text eq "undef" ?
  1559. "undef" : $zwm_unit{$meter_type_text}[$scale]);
  1560. $meter_type_text = "power" if ($unit_text eq "W");
  1561. $meter_type_text = "voltage" if ($unit_text eq "V");
  1562. $meter_type_text = "current" if ($unit_text eq "A");
  1563. my $mv = hex(substr($v3, 0, 2*$size));
  1564. $mv = $mv / (10 ** $precision);
  1565. $mv -= (2 ** ($size*8)) if $mv >= (2 ** ($size*8-1));
  1566. $v3 = substr($v3, 2*$size, length($v3)-(2*$size));
  1567. if (length($v3) < 4) { # V1 report
  1568. return "$meter_type_text: $mv $unit_text";
  1569. } else { # V2 or greater report
  1570. my $delta_time = hex(substr($v3, 0, 4));
  1571. $v3 = substr($v3, 4, length($v3)-4);
  1572. if ($delta_time == 0) { # no previous meter value
  1573. return "$meter_type_text: $mv $unit_text";
  1574. } else { # previous meter value present
  1575. my $pmv = hex(substr($v3, 0, 2*$size));
  1576. $pmv = $pmv / (10 ** $precision);
  1577. $pmv -= (2 ** ($size*8)) if $pmv >= (2 ** ($size*8-1));
  1578. if ($delta_time == 65535) {
  1579. $delta_time = "unknown";
  1580. } else {
  1581. $delta_time .= " s";
  1582. };
  1583. return "$meter_type_text: $mv $unit_text previous: $pmv delta_time: ".
  1584. "$delta_time"; # V2 report
  1585. }
  1586. }
  1587. }
  1588. sub
  1589. ZWave_meterGet($)
  1590. {
  1591. my ($scale) = @_;
  1592. if ($scale eq "%s") { # no parameter specified, use V1 get without scale
  1593. return("", "01");
  1594. };
  1595. if (($scale < 0) || ($scale > 6)) {
  1596. return("argument must be one of: 0 to 6","");
  1597. } else {
  1598. $scale = $scale << 3;
  1599. return("",sprintf('01%02x', $scale));
  1600. };
  1601. }
  1602. #V2: 1b7:reset 1b65:resrvd, 1b4-0:type, 2b7-4:resrvd, 2b3-0:scale
  1603. #V3: 1b7:reset 1b65:resrvd, 1b4-0:type, 2b:scale
  1604. #V4: 1b7:reset 1b65:rate, 1b4-0:type, 2b7:mst, 2b6-0:scale1, 3b:#scaleBytes,...
  1605. # No V4 support...
  1606. sub
  1607. ZWave_meterSupportedParse($$)
  1608. {
  1609. my ($hash,$val) = @_;
  1610. return if($val !~ m/^(..)(..)/);
  1611. my ($v1, $v2) = (hex($1), hex($2));
  1612. my $name = $hash->{NAME};
  1613. my $meter_reset = $v1 & 0x80;
  1614. my $meter_reset_text = $meter_reset ? "yes" : "no";
  1615. my $meter_type = ($v1 & 0x1f);
  1616. my @meter_type_text =("undef", "energy", "gas", "water", "undef");
  1617. my $meter_type_text = ($meter_type > $#meter_type_text ?
  1618. "undef" : $meter_type_text[$meter_type]);
  1619. my $scale = $v2 & 0x7f;
  1620. my $unit_text="";
  1621. for (my $i=0; $i <= 6; $i++) {
  1622. if ($scale & 2**$i) {
  1623. $unit_text .= ", " if (length($unit_text)>0);
  1624. $unit_text .= $i.":".$zwm_unit{$meter_type_text}[$i];
  1625. };
  1626. };
  1627. return "meterSupported: type: $meter_type_text scales: $unit_text resetable:".
  1628. " $meter_reset_text";
  1629. }
  1630. sub
  1631. ZWave_versionClassGet($$)
  1632. {
  1633. my ($hash, $class) = @_;
  1634. delete $zwave_parseHook{"$hash->{nodeIdHex}:..8614"} if ($hash->{CL});
  1635. return("", sprintf('13%02x', $class))
  1636. if($class =~ m/^\d+$/);
  1637. return("", sprintf('13%02x', hex($zwave_class{$class}{id})))
  1638. if($zwave_class{$class});
  1639. return ("versionClass needs a class as parameter", "") if($class eq "%s");
  1640. return ("Unknown class $class", "");
  1641. }
  1642. sub
  1643. ZWave_versionClassAllGet($@)
  1644. {
  1645. my ($hash, $data) = @_;
  1646. my $name = $hash->{NAME};
  1647. if(!$data) { # called by the user
  1648. delete($hash->{CL});
  1649. my %h = map { $_=>1 } split(" ", AttrVal($name, "classes", ""));
  1650. foreach my $c (sort keys %h) {
  1651. next if($c eq "MARK");
  1652. ZWave_Get($hash, $name, "versionClass", $c);
  1653. }
  1654. $zwave_parseHook{"$hash->{nodeIdHex}:..8614"} = \&ZWave_versionClassAllGet;
  1655. return(ZWave_WibMsg($hash).", check the vclasses attribute", "EMPTY");
  1656. }
  1657. $zwave_parseHook{"$hash->{nodeIdHex}:..8614"} = \&ZWave_versionClassAllGet;
  1658. my %h = map { $_=>1 } split(" ", AttrVal($name, "vclasses", ""));
  1659. return 0 if($data !~ m/^048614(..)(..)$/i); # ??
  1660. if($zwave_id2class{lc($1)}) {
  1661. $h{$zwave_id2class{lc($1)}.":".hex($2)} = 1;
  1662. $hash->{".vclasses"}{$zwave_id2class{lc($1)}} = hex($2);
  1663. $attr{$name}{vclasses} = join(" ", sort keys %h);
  1664. }
  1665. return !$hash->{asyncGet}; # "veto" for parseHook/getAll
  1666. }
  1667. sub
  1668. ZWave_multilevelParse($$$)
  1669. {
  1670. my ($type,$fl,$arg) = @_;
  1671. my %ml_tbl = (
  1672. '01' => { n => 'temperature', st => ['C', 'F'] },
  1673. '02' => { n => 'generalPurpose', st => ['%', ''] },
  1674. '03' => { n => 'luminance', st => ['%', 'Lux'] },
  1675. '04' => { n => 'power', st => ['W', 'Btu/h'] },
  1676. '05' => { n => 'humidity', st => ['%'] },
  1677. '06' => { n => 'velocity', st => ['m/s', 'mph'] },
  1678. '07' => { n => 'direction', st => [] },
  1679. '08' => { n => 'atmosphericPressure', st => ['kPa', 'inchHg'] },
  1680. '09' => { n => 'barometricPressure', st => ['kPa', 'inchHg'] },
  1681. '0a' => { n => 'solarRadiation', st => ['W/m2'] },
  1682. '0b' => { n => 'dewpoint', st => ['C', 'F'] },
  1683. '0c' => { n => 'rain', st => ['mm/h', 'in/h'] },
  1684. '0d' => { n => 'tideLevel', st => ['m', 'feet'] },
  1685. '0e' => { n => 'weight', st => ['kg', 'pound'] },
  1686. '0f' => { n => 'voltage', st => ['V', 'mV'] },
  1687. '10' => { n => 'current', st => ['A', 'mA'] },
  1688. '11' => { n => 'CO2-level', st => ['ppm']},
  1689. '12' => { n => 'airFlow', st => ['m3/h', 'cfm'] },
  1690. '13' => { n => 'tankCapacity', st => ['l', 'cbm', 'usgal'] },
  1691. '14' => { n => 'distance', st => ['m', 'cm', 'feet'] },
  1692. '15' => { n => 'anglePosition', st => ['%', 'relN', 'relS'] },
  1693. '16' => { n => 'rotation', st => ['rpm', 'Hz'] },
  1694. '17' => { n => 'waterTemperature', st => ['C', 'F'] },
  1695. '18' => { n => 'soilTemperature', st => ['C', 'F'] },
  1696. '19' => { n => 'seismicIntensity', st => ['mercalli', 'EU macroseismic',
  1697. 'liedu', 'shindo'] },
  1698. '1a' => { n => 'seismicMagnitude', st => ['local', 'moment',
  1699. 'surface wave', 'body wave'] },
  1700. '1b' => { n => 'ultraviolet', st => ['UV'] },
  1701. '1c' => { n => 'electricalResistivity',st => ['ohm'] },
  1702. '1d' => { n => 'electricalConductivity',st => ['siemens/m'] },
  1703. '1e' => { n => 'loudness', st => ['dB', 'dBA'] },
  1704. '1f' => { n => 'moisture', st => ['%', 'content', 'k ohms',
  1705. 'water activity'] },
  1706. '20' => { n => 'frequency', st => ['Hz', 'kHz'] },
  1707. '21' => { n => 'time', st => ['seconds'] },
  1708. '22' => { n => 'targetTemperature', st => ['C', 'F'] },
  1709. '23' => { n => 'particulateMatter', st => ['mol/m3', 'micro-g/m3'] },
  1710. '24' => { n => 'formaldehydeLevel', st => ['mol/m3'] },
  1711. '25' => { n => 'radonConcentration', st => ['bq/m3', 'pCi/L'] },
  1712. '26' => { n => 'methaneDensity', st => ['mol/m3'] },
  1713. '27' => { n => 'volatileOrganicCompound',st => ['mol/m3'] },
  1714. '28' => { n => 'carbonMonoxide', st => ['mol/m3'] },
  1715. '29' => { n => 'soilHumidity', st => ['%'] },
  1716. '2a' => { n => 'soilReactivity', st => ['pH'] },
  1717. '2b' => { n => 'soilSalinity', st => ['mol/m3'] },
  1718. '2c' => { n => 'heartRate', st => ['Bpm'] },
  1719. '2d' => { n => 'bloodPressure', st => ['Systolic mmHg',
  1720. 'Diastolic mmHg'] },
  1721. '2e' => { n => 'muscleMass', st => ['Kg'] },
  1722. '2f' => { n => 'fatMass', st => ['Kg'] },
  1723. '30' => { n => 'boneMass', st => ['Kg'] },
  1724. '31' => { n => 'totalBodyWater', st => ['Kg'] },
  1725. '32' => { n => 'basicMetabolicRate', st => ['J'] },
  1726. '33' => { n => 'bodyMassIndex', st => ['BMI'] },
  1727. );
  1728. my $pr = (hex($fl)>>5)&0x07; # precision
  1729. my $sc = (hex($fl)>>3)&0x03; # scale
  1730. my $bc = (hex($fl)>>0)&0x07; # bytecount
  1731. $arg = substr($arg, 0, 2*$bc);
  1732. my $msb = (hex($arg)>>8*$bc-1); # most significant bit ( 0 = pos, 1 = neg )
  1733. my $val = $msb ? -( 2 ** (8 * $bc) - hex($arg) ) : hex($arg); # 2's complement
  1734. my $ml = $ml_tbl{$type};
  1735. return "UNKNOWN multilevel type: $type fl: $fl arg: $arg" if(!$ml);
  1736. return sprintf("%s:%.*f %s", $ml->{n}, $pr, $val/(10**$pr),
  1737. int(@{$ml->{st}}) > $sc ? $ml->{st}->[$sc] : "");
  1738. }
  1739. sub
  1740. ZWave_timeParametersReport($$)
  1741. {
  1742. my ($hash, $arg) = @_;
  1743. my $name = $hash->{NAME};
  1744. if($arg !~ m/(....)(..)(..)(..)(..)(..)/) {
  1745. Log3 $name,1,"$name: timeParametersReport with wrong format received: $arg";
  1746. return;
  1747. }
  1748. return
  1749. sprintf("timeParameters:date: %04d-%02d-%02d time(UTC): %02d:%02d:%02d",
  1750. hex($1), hex($2), hex($3), hex($4), (hex$5), hex($6));
  1751. }
  1752. sub
  1753. ZWave_timeParametersSet($$)
  1754. {
  1755. my ($hash, $arg) = @_;
  1756. my $name = $hash->{NAME};
  1757. return ("wrong format, see commandref", "")
  1758. if($arg !~ m/(....)-(..)-(..) (..):(..):(..)/);
  1759. my $rt = sprintf("%04x%02x%02x%02x%02x%02x", $1, $2, $3, $4, $5, $6);
  1760. return ("", sprintf("01%s", $rt));
  1761. }
  1762. sub
  1763. ZWave_dateReport($$)
  1764. {
  1765. my ($hash, $arg) = @_;
  1766. my $name = $hash->{NAME};
  1767. if ($arg !~ m/(....)(..)(..)/) {
  1768. Log3 $name, 1, "$name: dateReport with wrong format received: $arg";
  1769. return;
  1770. }
  1771. return (sprintf("date:%04d-%02d-%02d", hex($1), hex($2), hex($3)));
  1772. }
  1773. sub
  1774. ZWave_timeReport($$)
  1775. {
  1776. my ($hash, $arg) = @_;
  1777. my $name = $hash->{NAME};
  1778. if ($arg !~ m/(..)(..)(..)/) {
  1779. Log3 $name, 1, "$name: timeReport with wrong format received: $arg";
  1780. return;
  1781. }
  1782. return (sprintf("time:%02d:%02d:%02d RTC: %s",
  1783. (hex($1) & 0x1f), hex($2), hex($3),
  1784. (hex($1) & 0x80) ? "failed" : "working"));
  1785. }
  1786. sub
  1787. ZWave_timeOffsetReport($$)
  1788. {
  1789. my ($hash, $arg) = @_;
  1790. my $name = $hash->{NAME};
  1791. if ($arg !~ m/(..)(..)(..)(..)(..)(..)(..)(..)(..)/) {
  1792. Log3 $name, 1, "$name: timeOffsetReport with wrong format received: $arg";
  1793. return;
  1794. }
  1795. my $hourTZO = hex($1) & 0x7f;
  1796. my $signTZO = hex($1) & 0x80;
  1797. my $minuteTZO = hex($2);
  1798. my $minuteOffsetDST = hex($3) & 0x7f;
  1799. my $signOffsetDST = hex($3) & 0x80;
  1800. my $monthStartDST = hex($4);
  1801. my $dayStartDST = hex($5);
  1802. my $hourStartDST = hex($6);
  1803. my $monthEndDST = hex($7);
  1804. my $dayEndDST = hex($8);
  1805. my $hourEndDST = hex($9);
  1806. my $UTCoffset = "UTC-Offset: ";
  1807. $UTCoffset .= ($signTZO ? "-" : "+");
  1808. $UTCoffset .= sprintf ("%02d:%02d", $hourTZO, $minuteTZO);
  1809. my $DSToffset = "DST-Offset(minutes): ";
  1810. $DSToffset .= ($signOffsetDST ? "-" : "+");
  1811. $DSToffset .= sprintf ("%02d", $minuteOffsetDST);
  1812. my $startDST = "DST-Start: ";
  1813. $startDST .= sprintf ("%02d-%02d_%02d:00",
  1814. $monthStartDST, $dayStartDST, $hourStartDST);
  1815. my $endDST = "DST-End: ";
  1816. $endDST .= sprintf ("%02d-%02d_%02d:00",
  1817. $monthEndDST, $dayEndDST, $hourEndDST);
  1818. return (sprintf("timeOffset:$UTCoffset $DSToffset $startDST $endDST"));
  1819. }
  1820. sub
  1821. ZWave_timeOffsetSet($$)
  1822. {
  1823. my ($hash, $arg) = @_;
  1824. my $name = $hash->{NAME};
  1825. if($arg !~
  1826. m/([+-])(..):(..) ([+-])(..) (..)-(..)_(..):00 (..)-(..)_(..):00/) {
  1827. return ("wrong format $arg, see commandref", "");
  1828. }
  1829. my $signTZO = $1;
  1830. my $hourTZO = $2;
  1831. my $minuteTZO = $3;
  1832. my $signOffsetDST = $4;
  1833. my $minuteOffsetDST = $5;
  1834. my $monthStartDST = $6;
  1835. my $dayStartDST = $7;
  1836. my $hourStartDST = $8;
  1837. my $monthEndDST = $9;
  1838. my $dayEndDST = $10;
  1839. my $hourEndDST = $11;
  1840. my $rt = sprintf("%02x%02x",
  1841. ($hourTZO | ($signTZO eq "-" ? 0x01 : 0x00)), $minuteTZO);
  1842. $rt .= sprintf("%02x",
  1843. ($minuteOffsetDST | ($signOffsetDST eq "-" ? 0x01 : 0x00)));
  1844. $rt .= sprintf("%02x%02x%02x",
  1845. $monthStartDST, $dayStartDST, $hourStartDST);
  1846. $rt .= sprintf("%02x%02x%02x", $monthEndDST, $dayEndDST, $hourEndDST);
  1847. return ("", sprintf("05%s", $rt));
  1848. }
  1849. sub
  1850. ZWave_DoorLockOperationReport($$)
  1851. {
  1852. my ($hash, $arg) = @_;
  1853. my $name = $hash->{NAME};
  1854. if ($arg !~ m/(..)(..)(..)(..)(..)/) {
  1855. Log3 $name, 1, "$name: doorLockOperationReport with wrong ".
  1856. "format received: $arg";
  1857. return;
  1858. }
  1859. my $DLM = hex($1); # DoorLockMode
  1860. my $DLHM = hex($2); # DoorLockHandleModes
  1861. my $DC = hex($3); # DoorCondition
  1862. my $DLTM = hex($4); # DoorLockTimeoutMinutes
  1863. my $DLTS = hex($5); # DoorLockTimeoutSeconds
  1864. my $DLMtext = "mode: ";
  1865. if ($DLM == 0xff) {
  1866. $DLMtext .= "secured";
  1867. } elsif ($DLM == 0xfe) {
  1868. $DLMtext .= "lockStateUnknown";
  1869. }
  1870. else {
  1871. $DLMtext .= "unsecured";
  1872. $DLMtext .= (($DLM & 0x10) ? "_inside" :"");
  1873. $DLMtext .= (($DLM & 0x20) ? "_outside" :"");
  1874. $DLMtext .= (($DLM & 0x01) ? "_withTimeout" :"");
  1875. };
  1876. my $odlhm = sprintf ("outsideHandles: %04b", ($DLHM & 0xf0)>>4);
  1877. my $idlhm = sprintf ("insideHandles: %04b", ($DLHM & 0x0f));
  1878. my $dc_door = "door: " . (($DC & 0x01) ? "closed" : "open");
  1879. my $dc_bolt = "bolt: " . (($DC & 0x02) ? "unlocked" : "locked");
  1880. my $dc_latch = "latch: " . (($DC & 0x04) ? "closed" : "open");
  1881. my $to = "timeoutSeconds: ";
  1882. if (($DLTM == 0xfe) && ($DLTS == 0xfe)) {
  1883. $to .= 'not_supported';
  1884. } else {
  1885. $to .= sprintf ("%d", ($DLTM * 60 + $DLTS));
  1886. }
  1887. return "doorLockOperation:$DLMtext $odlhm $idlhm $dc_door ".
  1888. "$dc_bolt $dc_latch $to";
  1889. }
  1890. sub
  1891. ZWave_DoorLockConfigReport($$)
  1892. {
  1893. my ($hash, $arg) = @_;
  1894. my $name = $hash->{NAME};
  1895. if ($arg !~ m/(..)(..)(..)(..)/) {
  1896. Log3 $name, 1, "$name: doorLockOperationReport with wrong ".
  1897. "format received: $arg";
  1898. return;
  1899. }
  1900. my $OpMode = $1; # OperationMode
  1901. my $DLHS = hex($2); # DoorLockHandleStates
  1902. my $DLTM = hex($3); # DoorLockTimeoutMinutes
  1903. my $DLTS = hex($4); # DoorLockTimeoutSeconds
  1904. my $ot = "mode: ";
  1905. if ($OpMode eq '01') {
  1906. $ot .= "constant";
  1907. } elsif ($OpMode eq '02') {
  1908. $ot .= "timed";
  1909. } else {
  1910. $ot .= "unknown";
  1911. }
  1912. my $odlhs = sprintf ("outsideHandles: %04b", ($DLHS & 0xf0)>>4);
  1913. my $idlhs = sprintf ("insideHandles: %04b", ($DLHS & 0x0f));
  1914. my $to = "timeoutSeconds: ";
  1915. if (($DLTM == 0xfe) && ($DLTS == 0xfe)) {
  1916. $to .= 'not_supported';
  1917. } else {
  1918. $to .= sprintf ("%d", ($DLTM * 60 + $DLTS));
  1919. }
  1920. return "doorLockConfiguration: $ot $odlhs $idlhs $to";
  1921. }
  1922. sub
  1923. ZWave_DoorLockOperationSet($$)
  1924. {
  1925. my ($hash, $arg) = @_;
  1926. my $name = $hash->{NAME};
  1927. my $rt;
  1928. $rt = ($arg eq 'open') ? "00" :
  1929. ($arg eq 'close') ? "FF" :
  1930. ($arg eq "00") ? "00" :
  1931. ($arg eq "01") ? "01" :
  1932. ($arg eq "10") ? "10" :
  1933. ($arg eq "11") ? "11" :
  1934. ($arg eq "20") ? "20" :
  1935. ($arg eq "21") ? "21" :
  1936. ($arg eq "FF") ? "FF" : "";
  1937. return ("DoorLockOperationSet: wrong parameter, see commandref")
  1938. if ($rt eq "");
  1939. return ("", "01".$rt);
  1940. }
  1941. sub
  1942. ZWave_DoorLockConfigSet($$)
  1943. {
  1944. # 0x62 V1, V2
  1945. # userinput: operationType, ohandles, ihandles, seconds_dez
  1946. my ($hash, $arg) = @_;
  1947. my $name = $hash->{NAME};
  1948. if ($arg !~ m/\b(.+)\b \b([01]{4})\b \b([01]{4})\b \b([0-9]+)$/) {
  1949. #~ Log3 $name, 1, "$name: doorLockConfigurationSet wrong ".
  1950. #~ "format, see commandref: $arg";
  1951. return ("doorLockConfigurationSet: wrong format, see commandref","");
  1952. }
  1953. my $oT;
  1954. if (lc($1) eq "constant") {
  1955. $oT = 1;
  1956. } elsif (lc($1) eq "timed") {
  1957. $oT = 2;
  1958. } else {
  1959. return ("wrong operationType: only [constant|timed] is allowed","");
  1960. }
  1961. my $handles = ((oct("0b".$2))<<4 | oct("0b".$3));
  1962. if (($4 < 1) || ($4) > 15239) { # max. 253 * 60 + 59 seconds
  1963. return ("doorLockConfigurationSet: 1-15238 seconds allowed","");
  1964. }
  1965. return ("", sprintf("04%02x%02x%02x%02x",
  1966. $oT,$handles, int($4 / 60) ,($4 % 60)));
  1967. }
  1968. sub
  1969. ZWave_SetClasses($$$$)
  1970. {
  1971. my ($homeId, $id, $type6, $classes) = @_;
  1972. my $def = $modules{ZWave}{defptr}{"$homeId $id"};
  1973. if(!$def) {
  1974. $type6 = $zw_type6{$type6} if($type6 && $zw_type6{lc($type6)});
  1975. $id = hex($id);
  1976. return "UNDEFINED ZWave_${type6}_$id ZWave $homeId $id $classes"
  1977. }
  1978. my @classes;
  1979. for my $classId (grep /../, split(/(..)/, lc($classes))) {
  1980. push @classes, $zwave_id2class{lc($classId)} ?
  1981. $zwave_id2class{lc($classId)} : "UNKNOWN_".lc($classId);
  1982. }
  1983. my $name = $def->{NAME};
  1984. $attr{$name}{classes} = join(" ", @classes)
  1985. if(@classes && !$attr{$name}{classes});
  1986. $def->{DEF} = "$homeId ".hex($id);
  1987. return "";
  1988. }
  1989. sub
  1990. ZWave_swmParse($$$$)
  1991. {
  1992. my ($fl, $sl, $dur, $step)=@_;
  1993. my $fl1 = (hex($fl) & 0x18)>>3;
  1994. my $fl2 = (hex($fl) & 0xc0)>>6;
  1995. $fl = ($fl1==0 ? "Increment": $fl1==1 ? "Decrement" : "")." ".
  1996. ($fl2==0 ? "Up": $fl1==1 ? "Down" : "");
  1997. return sprintf("state:swm %s Start: %d Duration: %d Step: %d",
  1998. $fl, hex($sl), hex($dur), hex($step));
  1999. }
  2000. sub
  2001. ZWave_sceneParse($)
  2002. {
  2003. my ($p)=@_;
  2004. my @arg = ("unknown", "on", "off",
  2005. "dim up start", "dim down start", "dim up end", "dim down end");
  2006. return sprintf("sceneEvent%s:%s", int(hex($p)/10), $arg[hex($p)%10]);
  2007. }
  2008. sub
  2009. ZWave_mcCapability($$)
  2010. {
  2011. my ($hash, $caps) = @_;
  2012. #Caps:= channelId,genericDeviceClass,specificDeviceClass,Class1,Class2,...
  2013. my $name = $hash->{NAME};
  2014. my $iodev = $hash->{IODev};
  2015. return "Missing IODev for $name" if(!$iodev);
  2016. my $homeId = $iodev->{homeId};
  2017. my @l = grep /../, split(/(..)/, lc($caps));
  2018. my $chid = sprintf("%02x", hex(shift(@l)) & 0x7f); # Forum #50176
  2019. my $id = $hash->{nodeIdHex};
  2020. my @classes;
  2021. my $type6 = shift(@l);
  2022. $type6 = $zw_type6{$type6} if($type6 && $zw_type6{lc($type6)});
  2023. my $specificClass = shift(@l);
  2024. for my $classId (@l) {
  2025. push @classes, $zwave_id2class{lc($classId)} ?
  2026. $zwave_id2class{lc($classId)} : "UNKNOWN_".uc($classId);
  2027. }
  2028. return "mcCapability_$chid:no classes" if(!@classes);
  2029. if($chid ne "00" && !$modules{ZWave}{defptr}{"$homeId $id$chid"}) {
  2030. my $lid = hex("$id$chid");
  2031. my $lcaps = substr($caps, 6);
  2032. $id = hex($id);
  2033. DoTrigger("global",
  2034. "UNDEFINED ZWave_${type6}_$id.$chid ZWave $homeId $lid $lcaps", 1);
  2035. }
  2036. return "mcCapability_$chid:".join(" ", @classes);
  2037. }
  2038. sub
  2039. ZWave_mcCreateAll($$)
  2040. {
  2041. my ($hash, $data) = @_;
  2042. if(!$data) { # called by the user
  2043. $zwave_parseHook{"$hash->{nodeIdHex}:0[45]6008...."} = \&ZWave_mcCreateAll;
  2044. ZWave_Get($hash, $hash->{NAME}, "mcEndpoints");
  2045. return("", "EMPTY");
  2046. }
  2047. $data =~ m/^0[45]6008(..)(..)/; # 4 vs. 5: Forum #50895
  2048. my $nGrp = hex($2);
  2049. for(my $c = 1; $c <= $nGrp; $c++) {
  2050. ZWave_Get($hash, $hash->{NAME}, "mcCapability", $c);
  2051. }
  2052. return undef;
  2053. }
  2054. sub
  2055. ZWave_mfsAddClasses($$)
  2056. {
  2057. my ($hash, $cfgFile) = @_;
  2058. my $name = $hash->{NAME};
  2059. my $attr = $attr{$name}{classes};
  2060. return if(!$cfgFile || !$attr);
  2061. my $changed;
  2062. ZWave_configParseModel($cfgFile);
  2063. my $ci = $zwave_modelConfig{$cfgFile}{classInfo};
  2064. foreach my $id (keys %{$ci}) {
  2065. my $v = $ci->{$id};
  2066. if($v =~ m/action="add"/) {
  2067. $id = sprintf("%02x", $id);
  2068. my $cn = $zwave_id2class{$id};
  2069. next if($attr =~ m/$cn/);
  2070. $attr .= " $cn";
  2071. $changed = 1;
  2072. }
  2073. }
  2074. return if(!$changed);
  2075. addStructChange("attr", $name, "$name classes $attr");
  2076. $attr{$name}{classes} = $attr;
  2077. }
  2078. sub
  2079. ZWave_mfsParse($$$$$)
  2080. {
  2081. my ($hash, $mf, $prod, $id, $config) = @_;
  2082. if($config == 2) {
  2083. setReadingsVal($hash, "modelId", "$mf-$prod-$id", TimeNow());
  2084. return "modelId:$mf-$prod-$id";
  2085. }
  2086. my $xml = $attr{global}{modpath}.
  2087. "/FHEM/lib/openzwave_manufacturer_specific.xml";
  2088. ($mf, $prod, $id) = (lc($mf), lc($prod), lc($id)); # Just to make it sure
  2089. if(open(FH, $xml)) {
  2090. my ($lastMf, $mName, $ret) = ("","");
  2091. while(my $l = <FH>) {
  2092. if($l =~ m/<Manufacturer.*id="([^"]*)".*name="([^"]*)"/) {
  2093. $lastMf = lc($1);
  2094. $mName = $2;
  2095. next;
  2096. }
  2097. if($l =~ m/<Product type\s*=\s*"([^"]*)".*id\s*=\s*"([^"]*)".*name\s*=\s*"([^"]*)"/) {
  2098. if($mf eq $lastMf && $prod eq lc($1) && $id eq lc($2)) {
  2099. if($config) {
  2100. $ret = (($l =~ m/config\s*=\s*"([^"]*)"/) ? $1 : "unknown");
  2101. ZWave_mfsAddClasses($hash, $1);
  2102. # execInits needs the modelId
  2103. setReadingsVal($hash, "modelId", "$mf-$prod-$id", TimeNow());
  2104. ZWave_execInits($hash, 50, $ret);
  2105. return "modelConfig:$ret";
  2106. } else {
  2107. $ret = "model:$mName $3";
  2108. }
  2109. last;
  2110. }
  2111. }
  2112. }
  2113. close(FH);
  2114. return $ret if($ret);
  2115. } else {
  2116. Log 1, "can't open $xml: $!";
  2117. }
  2118. return sprintf("model:0x%s 0x%s 0x%s", $mf, $prod, $id);
  2119. }
  2120. sub
  2121. ZWave_doorLLRParse($$)
  2122. {
  2123. my ($hash, $val) = @_;
  2124. my $result;
  2125. my $name = $hash->{NAME};
  2126. my @eventTypeHash = ( 'none', #not defined
  2127. 'Lock Command: Keypad access code verified lock command', #evtT 1
  2128. 'Unlock Command: Keypad access code verified unlock command', #evtT 2
  2129. 'Lock Command: Keypad lock button pressed', #evtT 3
  2130. 'Unlock command: Keypad unlock button pressed', #evtT 4
  2131. 'Lock Command: Keypad access code out of schedule', #evtT 5
  2132. 'Unlock Command: Keypad access code out of schedule', #evtT 6
  2133. 'Keypad illegal access code entered', #evtT 7
  2134. 'Key or latch operation locked (manual)', #evtT 8
  2135. 'Key or latch operation unlocked (manual)', #evtT 9
  2136. 'Auto lock operation', #evtT 10
  2137. 'Auto unlock operation', #evtT 11
  2138. 'Lock Command: Z-Wave access code verified', #evtT 12
  2139. 'Unlock Command: Z-Wave access code verified', #evtT 13
  2140. 'Lock Command: Z-Wave (no code)', #evtT 14
  2141. 'Unlock Command: Z-Wave (no code)', #evtT 15
  2142. 'Lock Command: Z-Wave access code out of schedule', #evtT 16
  2143. 'Unlock Command Z-Wave access code out of schedule', #evtT 17
  2144. 'Z-Wave illegal access code entered', #evtT 18
  2145. 'Key or latch operation locked (manual)', #evtT 19
  2146. 'Key or latch operation unlocked (manual)', #evtT 20
  2147. 'Lock secured', #evtT 21
  2148. 'Lock unsecured', #evtT 22
  2149. 'User code added', #evtT 23
  2150. 'User code deleted', #evtT 24
  2151. 'All user codes deleted', #evtT 25
  2152. 'Master code changed', #evtT 26
  2153. 'User code changed', #evtT 27
  2154. 'Lock reset', #evtT 28
  2155. 'Configuration changed', #evtT 29
  2156. 'Low battery', #evtT 30
  2157. 'New Battery installed', #evtT 31
  2158. );
  2159. # |- $recordNumber
  2160. # | |- $timestampYear
  2161. # | | |- $timestampMonth
  2162. # | | | |- $timestampDay
  2163. # | | | | |- $recordStatusTimestampHour
  2164. # | | | | | |- $timestampMinute
  2165. # | | | | | | |- $timestampSecond
  2166. # | | | | | | | |- $eventType
  2167. # | | | | | | | | |- $userIdentifier
  2168. # | | | | | | | | | |- $userCodeLength
  2169. # 20 07e1 07 1f 34 28 2b 08 00 00
  2170. if( $val =~ /^(..)(....)(..)(..)(..)(..)(..)(..)(..)(..)/ ) {
  2171. my $recordNumber = hex($1);
  2172. my $timestampYear = hex($2);
  2173. my $timestampMonth = hex($3);
  2174. my $timestampDay = hex($4);
  2175. my $recordStatusTimestampHour = sprintf( "%b", hex($5) );
  2176. my $recordStatus = substr $recordStatusTimestampHour, 0, 1;
  2177. $recordStatus = oct( "0b$recordStatus" );
  2178. my $timestampHour = substr $recordStatusTimestampHour, 1, 5;
  2179. $timestampHour = oct( "0b$timestampHour" );
  2180. my $timestampMinute = hex($6);
  2181. my $timestampSecond = hex($7);
  2182. my $eventTypeNumber = hex($8);
  2183. my $userIdentifier = hex($9);
  2184. my $userCodeLength = hex($10);
  2185. my $timestamp = sprintf ("%4d-%02d-%02d %02d:%02d:%02d",
  2186. $timestampYear,$timestampMonth,$timestampDay,$timestampHour,
  2187. $timestampMinute,$timestampSecond);
  2188. $result = "doorLockLoggingRecord:".
  2189. "recordNr: $recordNumber ".
  2190. "recordStatus: $recordStatus ".
  2191. "eventType: $eventTypeHash[$eventTypeNumber] ".
  2192. "userIdentifier: $userIdentifier ".
  2193. "userCodeLength: $userCodeLength ".
  2194. "timestamp: $timestamp";
  2195. }
  2196. # |- $userCode
  2197. # 20 07e1 07 1f 34 28 2b 08 00 00 3132333435
  2198. if( $val =~ /^(..)(....)(..)(..)(..)(..)(..)(..)(..)(..)(.+)/ ) {
  2199. my $userCode = "recieved but not saved.";
  2200. $result .= " USER_CODE: $userCode";
  2201. }
  2202. if( $result ne '' ) {
  2203. return $result;
  2204. } else {
  2205. return "doorLockLoggingRecord:$val";
  2206. }
  2207. }
  2208. my @zwave_wd = ("none","mon","tue","wed","thu","fri","sat","sun");
  2209. sub
  2210. ZWave_ccsSet($)
  2211. {
  2212. my ($spec) = @_;
  2213. my @arg = split(/[ ,]/, $spec);
  2214. my $usage = "wrong arg, need: <weekday> HH:MM relTemp HH:MM relTemp ...";
  2215. return ($usage,"") if(@arg < 1 || int(@arg) > 19 || (int(@arg)-1)%2 != 0);
  2216. my $wds = shift(@arg);
  2217. my $ret;
  2218. map { $ret=sprintf("%02x",$_) if($zwave_wd[$_] eq $wds) }(1..int($#zwave_wd));
  2219. return ("Unknown weekday $wds, use one of ".join(" ", @zwave_wd), "")
  2220. if(!defined($ret));
  2221. for(my $i=0; $i<@arg; $i+=2) {
  2222. return ($usage, "") if($arg[$i] !~ m/^(\d+):(\d\d)$/);
  2223. $ret .= sprintf("%02x%02x", $1, $2);
  2224. return ($usage, "") if($arg[$i+1] !~ m/^([-.\d]+)$/ || $1 < -12 || $1 > 12);
  2225. $ret .= sprintf("%02x", $1 < 0 ? (255+$1*10) : ($1*10));
  2226. }
  2227. for(my $i=@arg; $i<18; $i+=2) {
  2228. $ret .= "00007f";
  2229. }
  2230. return ("", "01$ret");
  2231. }
  2232. sub
  2233. ZWave_ccsSetOverride($)
  2234. {
  2235. my ($arg) = @_;
  2236. my $override_type;
  2237. my $override_state;
  2238. my $err = "wrong arguments, see commandref for details";
  2239. if ($arg =~ m/(no|temporary|permanent) (.*)/) {
  2240. $override_type = (lc($1) eq "no" ? 0 : (lc($1) eq "temporary" ? 1 : 2));
  2241. my $state = $2;
  2242. if ($state =~ m/(frost|energy)/) {
  2243. $override_state = (lc($state) eq "frost" ? 0x79 : 0x7a);
  2244. } elsif ($state =~ m/[-+]?[0-9]*\.?[0-9]+/) {
  2245. $state *= 10;
  2246. $state = 120 if ($state > 120);
  2247. $state = -128 if ($state < -128);
  2248. $state += 256 if ($state < 0);
  2249. $override_state = $state;
  2250. } else {
  2251. return($err, "");
  2252. }
  2253. return ("", sprintf("06%02x%02x", $override_type, $override_state));
  2254. }
  2255. return($err, "");
  2256. }
  2257. sub
  2258. ZWave_ccsGet($)
  2259. {
  2260. my ($wds, $wdn) = @_;
  2261. $wds = "" if($wds eq "%s"); # No parameter specified
  2262. map { $wdn = $_ if($zwave_wd[$_] eq $wds) } (1..int($#zwave_wd));
  2263. return ("Unknown weekday $wds, use one of ".join(" ", @zwave_wd), "")
  2264. if(!$wdn);
  2265. return ("", sprintf("02%02x", $wdn));
  2266. }
  2267. sub
  2268. ZWave_ccsAllGet ($)
  2269. {
  2270. my ($hash) = @_;
  2271. foreach my $idx (1..int($#zwave_wd)) {
  2272. ZWave_Get($hash, $hash->{NAME}, "ccs", $zwave_wd[$idx]);
  2273. }
  2274. return (ZWave_WibMsg($hash),"EMPTY");
  2275. }
  2276. sub
  2277. ZWave_ccsParse($$)
  2278. {
  2279. my ($t, $p) = @_;
  2280. return "ccsChanged:$p" if($t == "05");
  2281. if($t == "08" && $p =~ m/^(..)(..)$/) {
  2282. my $ret = ($1 eq "00" ? "no" : ($1 eq "01" ? "temporary" : "permanent"));
  2283. $ret .= ", ". ($2 eq "79" ? "frost protection" :
  2284. ($2 eq "7a" ? "energy saving" : "unused"));
  2285. return "ccsOverride:$ret";
  2286. }
  2287. if($t == "03") {
  2288. $p =~ /^(..)(.*$)/;
  2289. my $n = "ccs_".$zwave_wd[hex($1)];
  2290. $p = $2;
  2291. my @v;
  2292. while($p =~ m/^(..)(..)(..)(.*)$/) {
  2293. last if($3 eq "7f"); # unused
  2294. $p = $4;
  2295. my $t = hex($3);
  2296. $t = ($t == 0x7a ? "energySave" : $t >= 0x80 ? -(255-$t)/10 : $t/10);
  2297. push @v, sprintf("%02d:%02d %0.1f", hex($1), hex($2), $t);
  2298. }
  2299. return "$n:".(@v ? join(" ",@v) : "N/A");
  2300. }
  2301. return "ccs: UNKNOWN $t$p";
  2302. }
  2303. sub
  2304. ZWave_userCodeSet($)
  2305. {
  2306. my ($spec) = @_;
  2307. my @arg = split(" ", $spec);
  2308. return ("wrong arg, need: id status usercode","")
  2309. if(@arg != 3 || $spec !~ m/^[A-F0-9 ]*$/i);
  2310. return ("", sprintf("01%02x%02x%s", $arg[0],$arg[1],$arg[2]));
  2311. }
  2312. sub
  2313. ZWave_clockAdjust($$)
  2314. {
  2315. my ($hash, $d) = @_;
  2316. return $d if($d !~ m/^13(..)048104....$/);
  2317. my ($err, $nd) = ZWave_clockSet();
  2318. my $cmdEf = (AttrVal($hash->{NAME},"noExplorerFrames",0) == 0 ? "25" : "05");
  2319. return "13${1}0481${nd}${cmdEf}${1}";
  2320. }
  2321. sub
  2322. ZWave_clockSet()
  2323. {
  2324. my @l = localtime();
  2325. return ("", sprintf("04%02x%02x", (($l[6] ? $l[6]:7)<<5)|$l[2], $l[1]));
  2326. }
  2327. sub
  2328. ZWave_clockParse($$)
  2329. {
  2330. my ($p1,$p2) = @_;
  2331. $p1 = hex($p1); $p2 = hex($p2);
  2332. return sprintf("clock:%s %02d:%02d", $zwave_wd[$p1>>5], $p1 & 0x1f, $p2);
  2333. }
  2334. sub
  2335. ZWave_cleanString($$$)
  2336. {
  2337. my ($c, $postfix, $isValue) = @_;
  2338. my $shortened = 0;
  2339. $c =~ s/^[0-9.]+ //g if(!$isValue);
  2340. $c =~ s/Don.t/Dont/g; # Bugfix
  2341. if($c =~ m/^(.+)\.(.+)$/ && $2 !~ m/^[ \d]+$/) { # delete second sentence
  2342. $c = $1; $shortened++;
  2343. }
  2344. $c =~ s/[^A-Z0-9]+/ /ig;
  2345. $c =~ s/ *$//g;
  2346. $c =~ s/ (.)/uc($1)/gei;
  2347. while(length($c) > 32 && $shortened < 999) {
  2348. $c =~ s/[A-Z][^A-Z]*$//;
  2349. $shortened++;
  2350. }
  2351. $c .= $postfix if($shortened);
  2352. return ($c, $shortened);;
  2353. }
  2354. ###################################
  2355. # Poor mans XML-Parser
  2356. sub
  2357. ZWave_configParseModel($;$)
  2358. {
  2359. my ($cfg, $my) = @_;
  2360. return if(!$my && ZWave_configParseModel($cfg, 1));
  2361. my $fn = $attr{global}{modpath}."/FHEM/lib/".($my ? "fhem_":"open").
  2362. "zwave_deviceconfig.xml.gz";
  2363. my $gz = gzopen($fn, "rb");
  2364. if(!$gz) {
  2365. Log 3, "Can't open $fn: $!" if(!$my);
  2366. return 0;
  2367. }
  2368. my ($ret, $line, $class, %hash, $cmdName, %classInfo, %group, $origName);
  2369. while($gz->gzreadline($line)) { # Search the "file" entry
  2370. if($line =~ m/^\s*<Product.*sourceFile="$cfg"/) {
  2371. $ret = 1;
  2372. last;
  2373. }
  2374. }
  2375. my $partial="";
  2376. while($gz->gzreadline($line)) {
  2377. last if($line =~ m+^\s*</Product>+);
  2378. if($line =~ m/^\s*<CommandClass.*id="([^"]*)"(.*)$/) {
  2379. $class = $1;
  2380. $classInfo{$class} = $2;
  2381. }
  2382. $group{$1} = $line if($line =~ m/^\s*<Group.*index="([^"]*)".*$/);
  2383. next if(!$class || $class ne "112");
  2384. if($line =~ m/^\s*<Value /) {
  2385. my %h;
  2386. $h{type} = $1 if($line =~ m/type="([^"]*)"/i);
  2387. $h{size} = $1 if($line =~ m/size="([^"]*)"/i);
  2388. $h{genre} = $1 if($line =~ m/genre="([^"]*)"/i); # config, user
  2389. $h{label} = $1 if($line =~ m/label="([^"]*)"/i);
  2390. $h{min} = $1 if($line =~ m/min="([^"]*)"/i);
  2391. $h{max} = $1 if($line =~ m/max="([^"]*)"/i);
  2392. $h{value} = $1 if($line =~ m/value="([^"]*)"/i);
  2393. $h{index} = $1 if($line =~ m/index="([^"]*)"/i); # 1, 2, etc
  2394. $h{read_only} = $1 if($line =~ m/read_only="([^"]*)"/i); # true,false
  2395. $h{write_only} = $1 if($line =~ m/write_only="([^"]*)"/i); # true,false
  2396. my ($cmd,$shortened) = ZWave_cleanString($h{label}, $h{index}, 0);
  2397. $origName = "config$cmd";
  2398. $cmdName = $origName;
  2399. my $index = 1;
  2400. while($hash{$cmdName}) {
  2401. $cmdName = $origName."_".(++$index);
  2402. }
  2403. $h{Help} = "";
  2404. $h{Help} .= "Full text for $cmdName is: $h{label}<br>"
  2405. if($shortened || $origName ne $cmdName);
  2406. $hash{$cmdName} = \%h;
  2407. }
  2408. if($line =~ m,<Help>, && $line !~ m,</Help>,) { # Multiline Help
  2409. $partial = $line;
  2410. next;
  2411. }
  2412. if($partial) {
  2413. if($line =~ m,</Help>,) {
  2414. $line = $partial.$line;
  2415. $line =~ s/[\r\n]//gs;
  2416. $partial = "";
  2417. } else {
  2418. $partial .= $line;
  2419. next;
  2420. }
  2421. }
  2422. $hash{$cmdName}{Help} .= "$1<br>" if($line =~ m+<Help>(.*)</Help>+s);
  2423. if($line =~ m/^\s*<Item/) {
  2424. my $label = $1 if($line =~ m/label="([^"]*)"/i);
  2425. my $value = $1 if($line =~ m/value="([^"]*)"/i);
  2426. my ($item, $shortened) = ZWave_cleanString($label, $value, 1);
  2427. $hash{$cmdName}{Item}{$item} = $value;
  2428. $hash{$cmdName}{type} = "list"; # Forum #42604
  2429. $hash{$cmdName}{Help} .= "Full text for $item is $label<br>"
  2430. if($shortened);
  2431. }
  2432. }
  2433. $gz->gzclose();
  2434. my %mc = (set=>{}, get=>{}, config=>{},classInfo=>\%classInfo,group=>\%group);
  2435. foreach my $cmd (keys %hash) {
  2436. my $h = $hash{$cmd};
  2437. my $arg = ($h->{type} eq "button" ? "a" : "a%b");
  2438. $mc{set}{$cmd} = $arg if(!$h->{read_only} || $h->{read_only} ne "true");
  2439. $mc{get}{$cmd} ="noArg" if(!$h->{write_only} || $h->{write_only} ne "true");
  2440. $mc{config}{$cmd} = $h;
  2441. my $caName = "$cfg$cmd";
  2442. $zwave_cmdArgs{set}{$caName} = join(",", keys %{$h->{Item}}) if($h->{Item});
  2443. $zwave_cmdArgs{set}{$caName} = "noArg" if($h->{type} eq "button");
  2444. $zwave_cmdArgs{get}{$caName} = "noArg";
  2445. }
  2446. $zwave_modelConfig{$cfg} = \%mc;
  2447. Log 3, "ZWave got config for $cfg from $fn" if($ret);
  2448. return $ret;
  2449. }
  2450. ###################################
  2451. sub
  2452. ZWave_configGetHash($)
  2453. {
  2454. my ($hash) = @_;
  2455. return undef if(!$hash);
  2456. my $mc = ReadingsVal($hash->{NAME}, "modelConfig", "");
  2457. ZWave_configParseModel($mc) if($mc && !$zwave_modelConfig{$mc});
  2458. return $zwave_modelConfig{$mc};
  2459. }
  2460. sub
  2461. ZWave_configCheckParam($$$$@)
  2462. {
  2463. my ($hash, $type, $cmd, $fmt, @arg) = @_;
  2464. my $mc = ZWave_configGetHash($hash);
  2465. return ("", sprintf($fmt, @arg)) if(!$mc);
  2466. my $h = $mc->{config}{$cmd};
  2467. return ("", sprintf($fmt, @arg)) if(!$h);
  2468. return ("", sprintf("05%02x", $h->{index})) if($type eq "get");
  2469. if($cmd eq "configRGBLedColorForTesting") {
  2470. return ("6 digit hext number needed","") if($arg[0] !~ m/^[0-9a-f]{6}$/i);
  2471. return ("", sprintf("04%02x03%s", $h->{index}, $arg[0]));
  2472. }
  2473. my $t = $h->{type};
  2474. if($t eq "list") {
  2475. my $v = $h->{Item}{$arg[0]};
  2476. return ("Unknown parameter $arg[0] for $cmd, use one of ".
  2477. join(",", keys %{$h->{Item}}), "") if(!defined($v));
  2478. my $len = ($v > 65535 ? 8 : ($v > 255 ? 4 : 2));
  2479. return ("", sprintf("04%02x%02d%0*x", $h->{index}, $len/2, $len, $v));
  2480. }
  2481. if($t eq "button") {
  2482. return ("", sprintf("04%02x01%02x", $h->{index}, $h->{value}));
  2483. }
  2484. return ("Parameter is not decimal", "") if($arg[0] !~ m/^-?[0-9]+$/);
  2485. if($h->{size}) { # override type by size
  2486. $t = ($h->{size} eq "1" ? "byte" : ($h->{size} eq "2" ? "short" : "int"));
  2487. }
  2488. my $len = ($t eq "int" ? 8 : ($t eq "short" ? 4 : 2));
  2489. $arg[0] += 2**($len==8 ? 32 : ($len==4 ? 16 : 8)) if($arg[0] < 0); #F:41709
  2490. return ("", sprintf("04%02x%02x%0*x", $h->{index}, $len/2, $len, $arg[0]));
  2491. }
  2492. sub
  2493. ZWave_basicReport ($$$)
  2494. {
  2495. my ($value, $target, $duration) = @_;;
  2496. my $time = hex($duration);
  2497. if (($time > 0x7F) && ($time <= 0xFD)) {
  2498. $time = (hex($duration) - 0x7F) * 60;
  2499. };
  2500. $time .= " seconds";
  2501. $time = "unknown" if (hex($duration) == 0xFE);
  2502. $time = "255" if (hex($duration) == 0xFF);
  2503. my $rt = "basicReport:".hex($value)
  2504. ." target: ".hex($target)
  2505. ." duration: ".$time;
  2506. return $rt;
  2507. }
  2508. sub
  2509. ZWave_APPLICATION_STATUS_01_Report($$$)
  2510. { # 0x2101 ApplicationStatus ApplicationBusyReport >= V1
  2511. my ($hash, $st, $wTime) = @_;
  2512. my $name = $hash->{NAME};
  2513. my $status = hex($st);
  2514. my $rt .= $status==0 ? "tryAgainLater " :
  2515. $status==1 ? "tryAgainInWaitTimeSeconds " :
  2516. $status==2 ? "RequestQueued " : "unknownStatusCode ";
  2517. $rt .= sprintf("waitTime: %d", hex($wTime));
  2518. return ("applicationBusy:$rt");
  2519. }
  2520. ##############################################
  2521. ### START: 0x71 ALARM (NOTIFICATION)
  2522. ### Renamed from ALARM to NOTIFICATION in V3 specification,
  2523. ### for backward compatibility the name ALARM will be used
  2524. ### regardless of the version of the class.
  2525. my %zwave_alarmType = (
  2526. "01"=>"Smoke",
  2527. "02"=>"CO",
  2528. "03"=>"CO2",
  2529. "04"=>"Heat",
  2530. "05"=>"Water",
  2531. "06"=>"AccessControl",
  2532. "07"=>"HomeSecurity",
  2533. "08"=>"PowerManagement",
  2534. "09"=>"System",
  2535. "0a"=>"Emergency",
  2536. "0b"=>"Clock",
  2537. "0c"=>"Appliance",
  2538. "0d"=>"HomeHealth"
  2539. );
  2540. my %zwave_alarmEvent = ( # no comma allowed in strings
  2541. "0101"=>"detected",
  2542. "0102"=>"detected - Unknown Location",
  2543. "0103"=>"Alarm Test",
  2544. "0201"=>"detected",
  2545. "0202"=>"detected - Unknown Location",
  2546. "0301"=>"detected",
  2547. "0302"=>"detected - Unknown Location",
  2548. "0401"=>"Overheat detected",
  2549. "0402"=>"Overheat detected - Unknown Location",
  2550. "0403"=>"Rapid Temperature Rise",
  2551. "0404"=>"Rapid Temperature Rise - Unknown Location",
  2552. "0405"=>"Underheat detected",
  2553. "0406"=>"Underheat detected - Unknown Location",
  2554. "0501"=>"Leak detected",
  2555. "0502"=>"Leak detected - Unknown Location",
  2556. "0503"=>"Level Dropped",
  2557. "0504"=>"Level Dropped - Unknown Location",
  2558. "0505"=>"Replace Filter",
  2559. "0601"=>"Manual Lock Operation",
  2560. "0602"=>"Manual Unlock Operation",
  2561. "0603"=>"RF Lock Operation",
  2562. "0604"=>"RF Unlock Operation",
  2563. "0605"=>"Keypad Lock Operation",
  2564. "0606"=>"Keypad Unlock Operation",
  2565. "0607"=>"Manual Not Fully Locked Operation",
  2566. "0608"=>"RF Not Fully Locked Operation",
  2567. "0609"=>"Auto Lock Locked Operation",
  2568. "060a"=>"Auto Lock Not Fully Operation",
  2569. "060b"=>"Lock Jammed",
  2570. "060c"=>"All user codes deleted",
  2571. "060d"=>"Single user code deleted",
  2572. "060e"=>"New user code added",
  2573. "060f"=>"New user code not added due to duplicate code",
  2574. "0610"=>"Keypad temporary disabled",
  2575. "0611"=>"Keypad busy",
  2576. "0612"=>"New Program code Entered - Unique code for lock configuration",
  2577. "0613"=>"Manually Enter user Access code exceeds code limit",
  2578. "0614"=>"Unlock By RF with invalid user code",
  2579. "0615"=>"Locked by RF with invalid user codes",
  2580. "0616"=>"Window/Door is open",
  2581. "0617"=>"Window/Door is closed",
  2582. "0640"=>"Barrier performing Initialization process",
  2583. "0641"=>"Barrier operation (Open / Close) force has been exceeded.",
  2584. "0642"=>"Barrier motor has exceeded manufacturer's operational time limit",
  2585. "0643"=>"Barrier operation has exceeded physical mechanical limits.",
  2586. "0644"=>"Barrier unable to perform requested operation due to UL requirements.",
  2587. "0645"=>"Barrier Unattended operation has been disabled per UL requirements.",
  2588. "0646"=>"Barrier failed to perform Requested operation, device malfunction",
  2589. "0647"=>"Barrier Vacation Mode",
  2590. "0648"=>"Barrier Safety Beam Obstacle",
  2591. "0649"=>"Barrier Sensor Not Detected / Supervisory Error",
  2592. "064a"=>"Barrier Sensor Low Battery Warning",
  2593. "064b"=>"Barrier detected short in Wall Station wires",
  2594. "064c"=>"Barrier associated with non-Z-wave remote control.",
  2595. "0700"=>"Previous Events cleared",
  2596. "0701"=>"Intrusion",
  2597. "0702"=>"Intrusion - Unknown Location",
  2598. "0703"=>"Tampering - product covering removed",
  2599. "0704"=>"Tampering - Invalid Code",
  2600. "0705"=>"Glass Breakage",
  2601. "0706"=>"Glass Breakage - Unknown Location",
  2602. "0707"=>"Motion Detection",
  2603. "0708"=>"Motion Detection - Unknown Location",
  2604. "0800"=>"Previous Events cleared",
  2605. "0801"=>"Power has been applied",
  2606. "0802"=>"AC mains disconnected",
  2607. "0803"=>"AC mains re-connected",
  2608. "0804"=>"Surge Detection",
  2609. "0805"=>"Voltage Drop/Drift",
  2610. "0806"=>"Over-current detected",
  2611. "0807"=>"Over-voltage detected",
  2612. "0808"=>"Over-load detected",
  2613. "0809"=>"Load error",
  2614. "080a"=>"Replace battery soon",
  2615. "080b"=>"Replace battery now",
  2616. "080c"=>"Battery is charging",
  2617. "080d"=>"Battery is fully charged",
  2618. "080e"=>"Charge battery soon",
  2619. "080f"=>"Charge battery now!",
  2620. "0901"=>"hardware failure",
  2621. "0902"=>"software failure",
  2622. "0903"=>"hardware failure with OEM proprietary failure code",
  2623. "0904"=>"software failure with OEM proprietary failure code",
  2624. "0a01"=>"Contact Police",
  2625. "0a02"=>"Contact Fire Service",
  2626. "0a03"=>"Contact Medical Service",
  2627. "0b01"=>"Wake Up Alert",
  2628. "0b02"=>"Timer Ended",
  2629. "0b03"=>"Time remaining",
  2630. "0c01"=>"Program started",
  2631. "0c02"=>"Program in progress",
  2632. "0c03"=>"Program completed",
  2633. "0c04"=>"Replace main filter",
  2634. "0c05"=>"Failure to set target temperature",
  2635. "0c06"=>"Supplying water",
  2636. "0c07"=>"Water supply failure",
  2637. "0c08"=>"Boiling",
  2638. "0c09"=>"Boiling failure",
  2639. "0c0a"=>"Washing",
  2640. "0c0b"=>"Washing failure",
  2641. "0c0c"=>"Rinsing",
  2642. "0c0d"=>"Rinsing failure",
  2643. "0c0e"=>"Draining",
  2644. "0c0f"=>"Draining failure",
  2645. "0c10"=>"Spinning",
  2646. "0c11"=>"Spinning failure",
  2647. "0c12"=>"Drying",
  2648. "0c13"=>"Drying failure",
  2649. "0c14"=>"Fan failure",
  2650. "0c15"=>"Compressor failure",
  2651. "0d00"=>"Previous Events cleared",
  2652. "0d01"=>"Leaving Bed",
  2653. "0d02"=>"Sitting on bed",
  2654. "0d03"=>"Lying on bed",
  2655. "0d04"=>"Posture changed",
  2656. "0d05"=>"Sitting on edge of bed",
  2657. "0d06"=>"Volatile Organic Compound level"
  2658. );
  2659. sub
  2660. ZWave_ALARM_01_Get($) {
  2661. # 0x7101 Alarm/Notification EventSupportedGet >= V3
  2662. # alarmEventSupported
  2663. my ($arg) = @_;
  2664. foreach my $t (keys %zwave_alarmType) {
  2665. if (lc($zwave_alarmType{$t}) eq lc($arg)) {
  2666. return ("", "01".$t);
  2667. }
  2668. }
  2669. return ("unknown notification type entered, see commandref", "");
  2670. }
  2671. sub
  2672. ZWave_ALARM_02_Report($) {
  2673. # 0x7102 Alarm/Notification EventSupportedReport >= V3
  2674. # additional flagname "Appliance" in V4
  2675. my ($arg) = @_;
  2676. if($arg !~ m/(..)(..)(.*)/) {
  2677. return ("wrong format received");
  2678. }
  2679. my $t = $1;
  2680. my $rt = "alarmEventSupported_";
  2681. $rt .= ($zwave_alarmType{$t}) ? $zwave_alarmType{$t}.":" :
  2682. hex($t)."_unknown:";
  2683. my $numBitMasks = $2 & 0x1f;
  2684. #my $res = ($2 & 0xe0) >> 5; # reserved field
  2685. my $e = "";
  2686. my $val = $3;
  2687. my $delimeter = "";
  2688. for (my $i=0; $i<$numBitMasks; $i++) {
  2689. my $supported = hex(substr($val, $i*2, 2));
  2690. for (my $j=$i*8; $j<$i*8+8; $j++) {
  2691. if ($supported & (2 ** ($j-$i*8))) {
  2692. $e = sprintf("%s%02x", $t, $j);
  2693. $rt .= $delimeter;
  2694. $rt .= "(" . $j . ") ";
  2695. if (!$zwave_alarmEvent{$e}) {
  2696. $rt .= "unknown event";
  2697. } else {
  2698. $rt .= $zwave_alarmEvent{$e};
  2699. }
  2700. $delimeter = ", ";
  2701. }
  2702. }
  2703. }
  2704. return $rt;
  2705. }
  2706. sub
  2707. ZWave_ALARM_04_Get($$)
  2708. {
  2709. # 0x7104 Alarm/Notification alarmGet >= V1
  2710. # alarm (V1), alarmWithType (V2), alarmWithTypeEvent (>=V3)
  2711. my ($v, $arg) = @_;
  2712. my $rt = "04";
  2713. if ($v == 1) {
  2714. if($arg !~ m/^$p1_b$/) {
  2715. return ("wrong format, see commandref", "");
  2716. }
  2717. $rt .= sprintf("%02x", $1);
  2718. return ("",$rt);
  2719. } elsif ($v == 2) {
  2720. if($arg !~ m/(.*)/) {
  2721. return ("wrong format, see commandref", "");
  2722. }
  2723. foreach my $t (keys %zwave_alarmType) {
  2724. if (lc($zwave_alarmType{$t}) eq lc($1)) {
  2725. $rt .= sprintf("00%s", $t);
  2726. return ("", $rt);
  2727. }
  2728. }
  2729. } elsif ($v >= 3) { # V3=V4
  2730. if($arg !~ m/(.*) $p1_b/) {
  2731. return ("wrong format, see commandref", "");
  2732. }
  2733. foreach my $t (keys %zwave_alarmType) {
  2734. if (lc($zwave_alarmType{$t}) eq lc($1)) {
  2735. $rt .= sprintf("00%s%02x", $t, $2);
  2736. return ("", $rt);
  2737. }
  2738. }
  2739. }
  2740. }
  2741. sub
  2742. ZWave_ALARM_05_Report($$$$)
  2743. {
  2744. # 0x7105 Alarm/Notification Report >= V1
  2745. # additional parameter in V2 and V3
  2746. my ($hash, $t, $l, $r) = @_;
  2747. my $name = $hash->{NAME};
  2748. my $rt0 = "";
  2749. my $rt1 = "";
  2750. my $at = "";
  2751. my $level = "";
  2752. my $eventname = "";
  2753. my $ar = AttrVal($name, "extendedAlarmReadings",0);
  2754. if (!$r) { #V1 Answer
  2755. $at = $zwave_alarmType{$t} ? $zwave_alarmType{$t} :
  2756. sprintf("%d_unknown", hex($t));
  2757. $rt0 = "alarm_type_$t:level $l";
  2758. $rt1 = "alarm_$at:level $l";
  2759. } elsif ($r =~ m/(..)(..)(..)(..)(..)(.*)/) {
  2760. my ($zid, $ns, $t2, $e, $prop, $opt) = ($1, $2, $3, $4, $5, $6);
  2761. if ($e ne "00") {
  2762. if (($t ne "00") && ($l eq "00")) {
  2763. $level = "Event cleared: ";
  2764. }
  2765. } else {
  2766. $level = "Event cleared: ";
  2767. my $len = hex($prop & 0x1f);
  2768. if (($len >= 1) && ($opt =~ m/(..)(.*)/)) {
  2769. $e = $1;
  2770. }
  2771. }
  2772. $eventname = $zwave_alarmEvent{"$t2$e"} ? $zwave_alarmEvent{"$t2$e"}
  2773. : sprintf("unknown event %d", hex($e));
  2774. $at = $zwave_alarmType{$t2} ? $zwave_alarmType{$t2} :
  2775. sprintf("%d_unknown", hex($t2));
  2776. my $nst = ($ns eq "ff") ? "notificationIsOn" : "notificationIsOff";
  2777. my $o = ($opt && ($e ne "00")) ? ", arg $prop$opt" : "";
  2778. $rt0 = "alarm:$at: $level$eventname$o";
  2779. $rt1 = "alarm_$at:$level$eventname$o, $nst";
  2780. }
  2781. if ($ar == 0) {
  2782. return ($rt0);
  2783. } elsif ($ar == 1) {
  2784. return ($rt1);
  2785. } elsif ($ar == 2) {
  2786. return ($rt0, $rt1);
  2787. }
  2788. }
  2789. sub
  2790. ZWave_ALARM_06_Set($) {
  2791. # 0x7106 Alarm/Notification alarmSet (>=V2)
  2792. # alarmnotification
  2793. my ($arg) = @_;
  2794. if($arg !~ m/^(.*) (on|off)$/i) {
  2795. return ("wrong format, see commandref", "");
  2796. }
  2797. my $status = (lc($2) eq "on") ? "FF" : "00";
  2798. foreach my $t (keys %zwave_alarmType) {
  2799. if (lc($zwave_alarmType{$t}) eq lc($1)) {
  2800. return ("", "06".$t.$status);
  2801. }
  2802. }
  2803. return ("alarm/notification type not defined","");
  2804. }
  2805. sub
  2806. ZWave_ALARM_08_Report($) {
  2807. # 0x7108 Alarm/Notification TypeSupportedReport >= V2
  2808. # additional flagname "Appliance" in V4
  2809. my ($arg) = @_;
  2810. if($arg !~ m/(..)(.*)/) {
  2811. return ("wrong format received");
  2812. }
  2813. my $numBitMasks = $1 & 0x1f;
  2814. #my $res = ($1 & 0x60) >> 5; # reserved field
  2815. my $alarm_v1 = $1 & 0x80; # 0:standard types, 1:V1 non-standard
  2816. if ($alarm_v1) {
  2817. return ("alarmTypeSupported:non-standard V1 Alarm Types used");
  2818. } else {
  2819. my $rt = "alarmTypeSupported:";
  2820. my $t = "";
  2821. my $val = $2;
  2822. my $delimeter = "";
  2823. for (my $i=0; $i<$numBitMasks; $i++) {
  2824. my $supported = hex(substr($val, $i*2, 2));
  2825. for (my $j=$i*8; $j<$i*8+8; $j++) {
  2826. if ($supported & (2 ** ($j-$i*8))) {
  2827. $t = sprintf("%02x", $j);
  2828. $rt .= sprintf("$delimeter$zwave_alarmType{$t}");
  2829. $delimeter = " ";
  2830. }
  2831. }
  2832. }
  2833. return $rt;
  2834. }
  2835. }
  2836. ### END: 0x71 ALARM/NOTIFICATION
  2837. ##############################################
  2838. sub
  2839. ZWave_protectionParse($$)
  2840. {
  2841. my ($lp, $rp) = @_;
  2842. my $lpt = "Local: ". ($lp eq "00" ? "unprotected" :
  2843. ($lp eq "01" ? "by sequence" : "No operation possible"));
  2844. my $rpt = "RF: ". ($rp eq "00" ? "unprotected" :
  2845. ($rp eq "01" ? "No control" : "No response"));
  2846. return "protection:$lpt $rpt";
  2847. }
  2848. sub
  2849. ZWave_battery($) # Forum #87575
  2850. {
  2851. my ($val) = @_;
  2852. my @ret;
  2853. push @ret, "battery:".($val eq "ff" ? "low":hex($val)." %");
  2854. push @ret, "batteryState:".($val eq "ff" ? "low":"ok");
  2855. push @ret, "batteryPercent:".hex($val) if($val ne "ff");
  2856. return @ret;
  2857. }
  2858. sub
  2859. ZWave_configParse($$$$)
  2860. {
  2861. my ($hash, $cmdId, $size, $val) = @_;
  2862. $val = substr($val, 0, 2*$size);
  2863. $val = hex($val);
  2864. $cmdId = hex($cmdId);
  2865. my $mc = ZWave_configGetHash($hash);
  2866. return "config_$cmdId:$val" if(!$mc);
  2867. my $h = $mc->{config};
  2868. foreach my $cmd (keys %{$h}) {
  2869. if($h->{$cmd}{index} == $cmdId) {
  2870. my $hi = $h->{$cmd}{Item};
  2871. if($hi) {
  2872. foreach my $item (keys %{$hi}) {
  2873. return "$cmd:$item" if($hi->{$item} == $val);
  2874. }
  2875. }
  2876. return "$cmd:$val";
  2877. }
  2878. }
  2879. return "config_$cmdId:$val";
  2880. }
  2881. sub
  2882. ZWave_WibMsg($)
  2883. {
  2884. my ($hash) = @_;
  2885. return ZWave_isWakeUp($hash) ?
  2886. "Scheduled get requests for sending after WAKEUP" :
  2887. "working in the background";
  2888. }
  2889. sub
  2890. ZWave_configAllGet($)
  2891. {
  2892. my ($hash) = @_;
  2893. my $mc = ZWave_configGetHash($hash);
  2894. return ("configAll: no model specific configs found", undef)
  2895. if(!$mc || !$mc->{config});
  2896. delete($hash->{CL});
  2897. foreach my $c (sort keys %{$mc->{get}}) {
  2898. ZWave_Get($hash, $hash->{NAME}, $c);
  2899. }
  2900. return (ZWave_WibMsg($hash),"EMPTY");
  2901. }
  2902. sub
  2903. ZWave_associationAllGet($$)
  2904. {
  2905. my ($hash, $data) = @_;
  2906. if(!$data) { # called by the user
  2907. $zwave_parseHook{"$hash->{nodeIdHex}:..85"} = \&ZWave_associationAllGet;
  2908. delete($hash->{CL});
  2909. ZWave_Get($hash, $hash->{NAME}, "associationGroups");
  2910. return(ZWave_WibMsg($hash), "EMPTY");
  2911. }
  2912. my $nGrp = ($data =~ m/..8506(..)/ ? hex($1) :
  2913. ReadingsVal($hash->{NAME}, "assocGroups", 0));
  2914. my $grp = 0;
  2915. $grp = hex($1) if($data =~ m/..8503(..)/);
  2916. return if($grp >= $nGrp);
  2917. $zwave_parseHook{"$hash->{nodeIdHex}:..85"} = \&ZWave_associationAllGet;
  2918. ZWave_Get($hash, $hash->{NAME}, "association", $grp+1);
  2919. return;
  2920. }
  2921. sub
  2922. ZWave_mcaAllGet($$)
  2923. {
  2924. my ($hash, $data) = @_;
  2925. if(!$data) { # called by the user
  2926. $zwave_parseHook{"$hash->{nodeIdHex}:..8e"} = \&ZWave_mcaAllGet;
  2927. delete($hash->{CL});
  2928. ZWave_Get($hash, $hash->{NAME}, "mcaGroupings");
  2929. return(ZWave_WibMsg($hash), "EMPTY");
  2930. }
  2931. my $nGrp = ($data =~ m/..8e06(..)/ ? hex($1) :
  2932. ReadingsVal($hash->{NAME}, "mcaGroups", 0));
  2933. my $grp = 0;
  2934. $grp = hex($1) if($data =~ m/..8e03(..)/);
  2935. return if($grp >= $nGrp);
  2936. $zwave_parseHook{"$hash->{nodeIdHex}:..8e"} = \&ZWave_mcaAllGet;
  2937. ZWave_Get($hash, $hash->{NAME}, "mca", $grp+1);
  2938. return;
  2939. }
  2940. my %zwave_roleType = (
  2941. "00"=>"CentralStaticController",
  2942. "01"=>"SubStaticController",
  2943. "02"=>"PortableController",
  2944. "03"=>"PortableReportingController",
  2945. "04"=>"PortableSlave",
  2946. "05"=>"AlwaysOnSlave",
  2947. "06"=>"SleepingReportingSlave",
  2948. "07"=>"SleepingListeningSlave"
  2949. );
  2950. my %zwave_nodeType = (
  2951. "00"=>"Z-Wave+Node",
  2952. "01"=>"Z-Wave+IpRouter",
  2953. "02"=>"Z-Wave+IpGateway",
  2954. "03"=>"Z-Wave+IpClientAndIpNode",
  2955. "04"=>"Z-Wave+IpClientAndZwaveNode"
  2956. );
  2957. sub
  2958. ZWave_plusInfoParse($$$$$)
  2959. {
  2960. my ($version, $roleType, $nodeType, $installerIconType, $userIconType) = @_;
  2961. return "zwavePlusInfo: " .
  2962. "version:" . $version .
  2963. " role:" .
  2964. ($zwave_roleType{"$roleType"} ? $zwave_roleType{"$roleType"} :"unknown") .
  2965. " node:" .
  2966. ($zwave_nodeType{"$nodeType"} ? $zwave_nodeType{"$nodeType"} :"unknown") .
  2967. " installerIcon:". $installerIconType .
  2968. " userIcon:". $userIconType;
  2969. }
  2970. my %zwave_sensorBinaryTypeV2 = (
  2971. "01"=>"generalPurpose",
  2972. "02"=>"smoke",
  2973. "03"=>"CO",
  2974. "04"=>"CO2",
  2975. "05"=>"heat",
  2976. "06"=>"water",
  2977. "07"=>"freeze",
  2978. "08"=>"tamper",
  2979. "09"=>"aux",
  2980. "0a"=>"doorWindow",
  2981. "0b"=>"tilt",
  2982. "0c"=>"motion",
  2983. "0d"=>"glassBreak"
  2984. );
  2985. sub
  2986. ZWave_sensorbinaryV2Parse($$)
  2987. {
  2988. my ($value, $sensorType) = @_;
  2989. return ($zwave_sensorBinaryTypeV2{"$sensorType"} ?
  2990. $zwave_sensorBinaryTypeV2{"$sensorType"} :"unknown") .
  2991. ":".($value eq "00" ? "off" : ($value eq "ff" ? "on" : hex($value)));
  2992. }
  2993. sub
  2994. ZWave_assocGroup($$$$)
  2995. {
  2996. my ($homeId, $gId, $max, $nodes) = @_;
  2997. my %list = map { $defs{$_}{nodeIdHex} => $_ }
  2998. grep { $defs{$_}{homeId} &&
  2999. $defs{$_}{nodeIdHex} &&
  3000. $defs{$_}{homeId} eq $homeId }
  3001. keys %defs;
  3002. $nodes = join(" ",
  3003. map { $list{$_} ? $list{$_} : "UNKNOWN_".hex($_); }
  3004. ($nodes =~ m/../g));
  3005. return sprintf("assocGroup_%d:Max %d Nodes %s", hex($gId),hex($max), $nodes);
  3006. }
  3007. sub
  3008. ZWave_mcaReport($$$$)
  3009. {
  3010. my ($homeId, $gId, $max, $arg) = @_;
  3011. my %list = map { $defs{$_}{nodeIdHex} => $_ }
  3012. grep { $defs{$_}{homeId} &&
  3013. $defs{$_}{nodeIdHex} &&
  3014. $defs{$_}{homeId} eq $homeId }
  3015. keys %defs;
  3016. my ($ret, $step) = ("", 1);
  3017. my @arg = ($arg =~ m/../g);
  3018. for(my $idx = 0; $idx < @arg; $idx += $step) {
  3019. my $a = $arg[$idx];
  3020. if($step == 1 && $a eq "00") {
  3021. $step = 2;
  3022. $idx--;
  3023. next;
  3024. }
  3025. my $n = ($list{$a} ? $list{$a} : sprintf("UNKNOWN_%d", hex($a)));
  3026. $ret .= " ".($step == 1 ? $n : ($n.":".hex($arg[$idx+1])));
  3027. }
  3028. return sprintf("mca_%d:Max %d %s",
  3029. hex($gId), hex($max), ($ret ? "Nodes$ret" : ""));
  3030. }
  3031. sub
  3032. ZWave_CRC16($)
  3033. {
  3034. my ($msg) = @_;
  3035. my $buf = pack 'H*', $msg;
  3036. my $len = length($buf);
  3037. my $poly = 0x1021; #CRC-CCITT (CRC-16) x16 + x12 + x5 + 1
  3038. my $crc16 = 0x1D0F; #Startvalue
  3039. for(my $i=0; $i<$len; $i++) {
  3040. my $byte = ord(substr($buf, $i, 1));
  3041. $byte = $byte * 0x100; # expand to 16 Bit
  3042. for(0..7) {
  3043. if(($byte & 0x8000) ^ ($crc16 & 0x8000)) { # if Hi-bits are different
  3044. $crc16 <<= 1;
  3045. $crc16 ^= $poly;
  3046. } else {
  3047. $crc16 <<= 1;
  3048. }
  3049. $crc16 &= 0xFFFF;
  3050. $byte <<= 1;
  3051. $byte &= 0xFFFF;
  3052. }
  3053. }
  3054. return sprintf "%04x", $crc16;
  3055. }
  3056. ##############################################
  3057. # SECURITY (start)
  3058. ##############################################
  3059. sub
  3060. ZWave_secIncludeStart($$)
  3061. {
  3062. my ($hash, $iodev) = @_;
  3063. my $name = $hash->{NAME};
  3064. my $ioName = $iodev->{NAME};
  3065. readingsSingleUpdate($hash, "SECURITY",
  3066. "INITIALIZING (starting secure inclusion)", 0);
  3067. my $classes = AttrVal($name, "classes", "");
  3068. if($classes =~ m/SECURITY/) {
  3069. if ($zwave_cryptRijndael == 1) {
  3070. my $key = AttrVal($ioName, "networkKey", "");
  3071. if($key) {
  3072. $iodev->{secInitName} = $name;
  3073. Log3 $ioName, 2, "ZWAVE Starting secure init for $name";
  3074. #~ ZWave_secIncludeStart($dh);
  3075. #~ return "";
  3076. # starting secure inclusion by setting the scheme
  3077. $zwave_parseHook{"$hash->{nodeIdHex}:..9805"} = \&ZWave_secSchemeReport;
  3078. ZWave_secAddToSendStack($hash, "980400"); # only scheme "00" is defined
  3079. return 1;
  3080. } else {
  3081. Log3 $ioName,1,"No secure inclusion as $ioName has no networkKey";
  3082. readingsSingleUpdate($hash, "SECURITY",
  3083. 'DISABLED (Networkkey not found)', 0);
  3084. Log3 $name, 1, "$name: SECURITY disabled, ".
  3085. "networkkey not found";
  3086. }
  3087. } else {
  3088. readingsSingleUpdate($hash, "SECURITY",
  3089. 'DISABLED (Module Crypt::Rijndael not found)', 0);
  3090. Log3 $name, 1, "$name: SECURITY disabled, module ".
  3091. "Crypt::Rijndael not found";
  3092. }
  3093. } else {
  3094. readingsSingleUpdate($hash, "SECURITY",
  3095. 'DISABLED (SECURITY not supported by device)', 0);
  3096. Log3 $name, 1, "$name: secure inclusion failed, ".
  3097. "SECURITY disabled, device does not support SECURITY command class";
  3098. }
  3099. return 0;
  3100. }
  3101. sub
  3102. ZWave_secSchemeReport($$) # only called by zwave_parseHook during Include
  3103. {
  3104. my ($hash, $arg) = @_;
  3105. my $name = $hash->{NAME};
  3106. if ($arg =~ m/..9805(..)/) { # 03980500 is expected
  3107. if ($1 == 0) { # supported SECURITY-Scheme, prepare for setting networkkey
  3108. $zwave_parseHook{"$hash->{nodeIdHex}:..9880"} = \&ZWave_secNetworkkeySet;
  3109. ZWave_secAddToSendStack($hash, '9840');
  3110. #ZWave_Cmd("set", $hash, $name, "secNonceReport");
  3111. } else {
  3112. Log3 $name, 1, "$name: Unsupported SECURITY-Scheme received: $1"
  3113. .", please report";
  3114. }
  3115. } else {
  3116. Log3 $name, 1, "$name: Unknown SECURITY-SchemeReport received: $arg";
  3117. }
  3118. return 1; # "veto" for parseHook
  3119. }
  3120. sub
  3121. ZWave_secNetworkkeySet($$) # only called by zwave_parseHook during Include
  3122. {
  3123. my ($hash, $arg) = @_;
  3124. my $name = $hash->{NAME};
  3125. my $iodev = $hash->{IODev};
  3126. my $r_nonce_hex;
  3127. if ($arg =~ m/..9880(................)/) {
  3128. $r_nonce_hex = $1;
  3129. } else {
  3130. Log3 $name, 1, "$name: Unknown SECURITY-NonceReport received: $arg";
  3131. }
  3132. my $key_hex = AttrVal($iodev->{NAME}, "networkKey", "");
  3133. my $mynonce_hex = substr (ZWave_secCreateNonce($hash), 2, 16);
  3134. my $cryptedNetworkKeyMsg = ZWave_secNetworkkeyEncode($r_nonce_hex,
  3135. $mynonce_hex, $key_hex, $hash->{nodeIdHex});
  3136. $zwave_parseHook{"$hash->{nodeIdHex}:..9807"} = \&ZWave_secNetWorkKeyVerify;
  3137. ZWave_secAddToSendStack($hash, '98'.$cryptedNetworkKeyMsg);
  3138. readingsSingleUpdate($hash, "SECURITY", 'INITIALIZING (Networkkey sent)',0);
  3139. Log3 $name, 5, "$name: SECURITY initializing, networkkey sent";
  3140. # start timer here to check state if networkkey was not verified
  3141. $hash->{networkkeyTimer} = { hash => $hash };
  3142. InternalTimer(gettimeofday()+25, "ZWave_secTestNetworkkeyVerify",
  3143. $hash->{networkkeyTimer}, 0);
  3144. return 1; # "veto" for parseHook
  3145. }
  3146. sub
  3147. ZWave_secNetWorkKeyVerify ($$) # only called by zwave_parseHook during Include
  3148. {
  3149. my ($hash, $arg) = @_;
  3150. my $name = $hash->{NAME};
  3151. my $iodev = $hash->{IODev};
  3152. if (!ZWave_secIsEnabled($hash)) {
  3153. return 1;
  3154. }
  3155. RemoveInternalTimer($hash->{networkkeyTimer});
  3156. delete $hash->{networkkeyTimer};
  3157. readingsSingleUpdate($hash, "SECURITY", 'ENABLED', 0);
  3158. Log3 $name, 3, "$name: SECURITY enabled, networkkey was verified";
  3159. $zwave_parseHook{"$hash->{nodeIdHex}:..9803"} = \&ZWave_secIncludeFinished;
  3160. ZWave_Cmd("set", $hash, $name, ("secSupportedReport"));
  3161. return 1; # "veto" for parseHook
  3162. }
  3163. sub
  3164. ZWave_secIncludeFinished($$) # only called by zwave_parseHook during Include
  3165. {
  3166. my ($hash, $arg) = @_;
  3167. my $iodev = $hash->{IODev};
  3168. my $name = $hash->{NAME};
  3169. if ($arg =~ m/..9803(.*)/) {
  3170. ZWave_secSupported($hash, $1);
  3171. } else {
  3172. Log3 $name, 2, "$name: unknown answer for secSupported received";
  3173. }
  3174. if ($iodev->{secInitName}) {
  3175. # Secure inclusion is finished, remove readings and execute "normal" init
  3176. delete $iodev->{secInitName};
  3177. ZWave_execInits($hash, 0);
  3178. }
  3179. return 1; # "veto" for parseHook -> secSupported to be called before execInits
  3180. }
  3181. sub
  3182. ZWave_secTestNetworkkeyVerify ($)
  3183. {
  3184. my ($p) = @_;
  3185. my $hash = $p->{hash};
  3186. my $name = $hash->{NAME};
  3187. my $sec_status = ReadingsVal($name, "SECURITY", undef);
  3188. delete $hash->{networkkeyTimer};
  3189. if ($sec_status !~ m/ENABLED/) {
  3190. readingsSingleUpdate($hash, "SECURITY",
  3191. 'DISABLED (networkkey not verified and timer expired)', 0);
  3192. Log3 $name, 1, "$name: SECURITY disabled, networkkey was not verified ".
  3193. "and timer expired";
  3194. }
  3195. }
  3196. sub
  3197. ZWave_secAddToSendStack($$)
  3198. {
  3199. my ($hash, $cmd) = @_;
  3200. my $name = $hash->{NAME};
  3201. my $id = $hash->{nodeIdHex};
  3202. my $len = sprintf("%02x", (length($cmd)-2)/2+1);
  3203. my $cmdEf = (AttrVal($name, "noExplorerFrames", 0) == 0 ? "25" : "05");
  3204. my $data = "13$id$len$cmd$cmdEf" . ZWave_callbackId($hash);
  3205. ZWave_addToSendStack($hash, "set", $data);
  3206. }
  3207. sub
  3208. ZWave_secStart($)
  3209. {
  3210. my ($hash) = @_;
  3211. my $dt = gettimeofday();
  3212. $hash = $defs{$hash->{endpointParent}} if($hash->{endpointParent});
  3213. $hash->{secTime} = $dt;
  3214. $hash->{secTimer} = { hash => $hash };
  3215. InternalTimer($dt+7, "ZWave_secUnlock", $hash->{secTimer}, 0);
  3216. return if($hash->{secInProgress});
  3217. $hash->{secInProgress} = 1;
  3218. my @empty;
  3219. $hash->{secStack} = \@empty;
  3220. }
  3221. sub
  3222. ZWave_secUnlock($)
  3223. {
  3224. my ($p) = @_;
  3225. my $hash= $p->{hash};
  3226. my $dt = gettimeofday();
  3227. if (($hash->{secInProgress}) && ($dt > ($hash->{secTime} + 6))) {
  3228. Log3 $hash->{NAME}, 3, "$hash->{NAME}: secStart older than "
  3229. ."6 seconds detected, secUnlock will call Zwave_secEnd";
  3230. ZWave_secEnd($hash);
  3231. }
  3232. }
  3233. sub
  3234. ZWave_secEnd($)
  3235. {
  3236. my ($hash) = @_;
  3237. return if(!$hash->{secInProgress});
  3238. RemoveInternalTimer($hash->{secTimer});
  3239. my $secStack = $hash->{secStack};
  3240. delete $hash->{secInProgress};
  3241. delete $hash->{secStack};
  3242. delete $hash->{secTimer};
  3243. foreach my $cmd (@{$secStack}) {
  3244. ZWave_SCmd($cmd->{T}, $hash, @{$cmd->{A}});
  3245. }
  3246. }
  3247. sub
  3248. ZWave_secIsSecureClass($$)
  3249. {
  3250. my ($hash, $cc_cmd) = @_;
  3251. my $name = $hash->{NAME};
  3252. if ($cc_cmd =~m/(..)(..)/) {
  3253. my ($cc, $cmd) = ($1, $2);
  3254. my $cc_name = $zwave_id2class{lc($cc)};
  3255. my $sec_classes = AttrVal($hash->{endpointParent} ?
  3256. $hash->{endpointParent} : $name, "secure_classes", "");
  3257. if (($sec_classes =~ m/$cc_name/) && ($cc_name ne 'SECURITY')){
  3258. Log3 $name, 5, "$name: $cc_name is a secured class!";
  3259. return 1;
  3260. }
  3261. # some SECURITY commands need to be encrypted allways
  3262. if ($cc eq '98') {
  3263. if ($cmd eq '02' || # SupportedGet
  3264. $cmd eq '06' || # NetworkKeySet
  3265. $cmd eq '08' ){ # SchemeInherit
  3266. Log3 $name, 5, "$name: Security commands will be encrypted!";
  3267. return 1;
  3268. }
  3269. }
  3270. # these SECURITY commands should not be encrypted
  3271. # SchemeGet = 0x04, NonceGet = 0x40, NonceReport = 0x80
  3272. # MessageEncap = 0x81 is already encrypted
  3273. # MessageEncapNonceGet = 0xc1 is already encrypted
  3274. }
  3275. return 0;
  3276. }
  3277. sub
  3278. ZWave_secSupported($$)
  3279. {
  3280. my ($hash, $arg) = @_;
  3281. my $name = $hash->{NAME};
  3282. my $id = $hash->{nodeIdHex};
  3283. if (!ZWave_secIsEnabled($hash)) {
  3284. return;
  3285. };
  3286. Log3 $name, 5, "$name: Secured Classes Supported: $arg";
  3287. if ($arg =~ m/(..)(.*)/) {
  3288. if ($1 ne '00') {
  3289. Log3 $name, 1, "$name: Multi part message report for secure classes ".
  3290. "can not be handled!";
  3291. }
  3292. my @sec_classes;
  3293. my $sec_classes = $2;
  3294. for my $sec_classId (grep /../, split(/(..)/, lc($sec_classes))) {
  3295. push @sec_classes, $zwave_id2class{lc($sec_classId)} ?
  3296. $zwave_id2class{lc($sec_classId)} : "UNKNOWN_".lc($sec_classId);
  3297. }
  3298. $attr{$name}{secure_classes} = join(" ", @sec_classes)
  3299. if (@sec_classes);
  3300. }
  3301. # Add new secure_classes to classes if not already present
  3302. # Needed for classes that are only supported with SECURITY
  3303. if ($attr{$name}{secure_classes} && $attr{$name}{classes}) {
  3304. my $classes = $attr{$name}{classes};
  3305. my $secure_classes = $attr{$name}{secure_classes};
  3306. my $c1;
  3307. my $c2;
  3308. my $s1;
  3309. my $s2;
  3310. my $classname;
  3311. if ($classes =~ m/(.*)(MARK)(.*)/) {
  3312. ($c1, $c2) = ($1, $2 . $3);
  3313. } else {
  3314. ($c1, $c2) = ($classes, "");
  3315. }
  3316. if ($secure_classes =~ m/(.*)(MARK)(.*)/) {
  3317. ($s1, $s2) = ($1, $2 . $3);
  3318. } else {
  3319. ($s1, $s2) = ($secure_classes, "");
  3320. }
  3321. foreach $classname (split(" ", $s1)) {
  3322. if ($c1 !~ m/\b$classname\b/) {
  3323. $c1 = join (" ", $c1, $classname);
  3324. }
  3325. }
  3326. foreach $classname (split(" ", $s2)) {
  3327. if ($c2 !~ m/\b$classname\b/) {
  3328. $c2 = join (" ", $c2, $classname);
  3329. }
  3330. }
  3331. $classes = join (" ", $c1, $c2);
  3332. $classes =~ s/ +/ /gs;
  3333. $attr{$name}{classes} = $classes;
  3334. }
  3335. }
  3336. sub
  3337. ZWave_secNonceReceived($$)
  3338. {
  3339. my ($hash, $r_nonce_hex) = @_;
  3340. my $iodev = $hash->{IODev};
  3341. my $name = $hash->{NAME};
  3342. if (!ZWave_secIsEnabled($hash))
  3343. {
  3344. return;
  3345. }
  3346. # if nonce is received, we should have stored a message for encryption
  3347. my $getSecMsg = ZWave_secGetMsg($hash);
  3348. my @secArr = split / /, $getSecMsg, 4;
  3349. my $secMsg = $secArr[0];
  3350. my $type = $secArr[1];
  3351. my $cmd = $secArr[3];
  3352. if (!$secMsg) {
  3353. Log3 $name, 1, "$name: Error, nonce reveived but no stored command for ".
  3354. "encryption found";
  3355. return undef;
  3356. }
  3357. my $enc = ZWave_secEncrypt($hash, $r_nonce_hex, $secMsg);
  3358. ZWave_secAddToSendStack($hash, '98'.$enc);
  3359. if ($type eq "set" && $cmd && $cmd !~ m/^config.*request$/) {
  3360. readingsSingleUpdate($hash, "state", $cmd, 1);
  3361. Log3 $name, 5, "$name: type=$type, cmd=$cmd ($getSecMsg)";
  3362. ZWave_secEnd($hash) if ($type eq "set");
  3363. }
  3364. return undef;
  3365. }
  3366. sub
  3367. ZWave_secPutMsg ($$$)
  3368. {
  3369. my ($hash, $s, $cmd) = @_;
  3370. my $name = $hash->{NAME};
  3371. $hash = $defs{$hash->{endpointParent}} if($hash->{endpointParent});
  3372. if (!$hash->{secMsg}) {
  3373. my @arr = ();
  3374. $hash->{secMsg} = \@arr;
  3375. }
  3376. push @{$hash->{secMsg}}, $s . " ". $cmd;
  3377. Log3 $name, 5, "$name SECURITY: $s stored for encryption";
  3378. }
  3379. sub
  3380. ZWave_secGetMsg ($)
  3381. {
  3382. my ($hash) = @_;
  3383. my $name = $hash->{NAME};
  3384. my $secMsg = $hash->{secMsg};
  3385. if ($secMsg && @{$secMsg}) {
  3386. my $ret = shift(@{$secMsg});
  3387. if ($ret) {
  3388. Log3 $name, 5, "$name SECURITY: $ret retrieved for encryption";
  3389. return $ret;
  3390. }
  3391. }
  3392. Log3 $name, 1, "$name: no stored commands in Internal secMsg found";
  3393. return "";
  3394. }
  3395. sub
  3396. ZWave_secNonceRequestReceived ($)
  3397. {
  3398. my ($hash) = @_;
  3399. if (!ZWave_secIsEnabled($hash)) {
  3400. return;
  3401. }
  3402. ZWave_secStart($hash);
  3403. ZWave_secAddToSendStack($hash, '98'.ZWave_secCreateNonce($hash));
  3404. }
  3405. sub
  3406. ZWave_secIsEnabled ($)
  3407. {
  3408. my ($hash) = @_;
  3409. my $secStatus = ReadingsVal($hash->{NAME}, "SECURITY", "DISABLED");
  3410. if ($secStatus =~ m/DISABLED/) {
  3411. Log3 $hash->{NAME}, 1, "$hash->{NAME} SECURITY $secStatus (command dropped)";
  3412. return (0);
  3413. }
  3414. return (1);
  3415. }
  3416. sub
  3417. ZWave_sec ($$)
  3418. {
  3419. my ($hash, $arg) = @_;
  3420. return (ZWave_secIsEnabled($hash) ? ("", $arg) : ("",'00'));
  3421. }
  3422. sub
  3423. ZWave_secCreateNonce($)
  3424. {
  3425. my ($hash) = @_;
  3426. if (ZWave_secIsEnabled($hash)) {
  3427. my $nonce;
  3428. my $nonce_id;
  3429. my $n=0;
  3430. $hash->{secNonce} = {} if (!$hash->{secNonce});
  3431. foreach my $id (sort keys %{$hash->{secNonce}}) {
  3432. $n++;
  3433. }
  3434. my $time = gettimeofday();
  3435. if ($n>50) { #clean up only if more than 50 nonce are active
  3436. foreach my $id (sort keys %{$hash->{secNonce}}) {
  3437. my $dt = $time - $hash->{secNonce}{$id}{timeStamp};
  3438. if ($dt > 10) {
  3439. Log3 $hash->{NAME}, 3, "$hash->{NAME}: SECURITY: nonce "
  3440. .$hash->{secNonce}{$id}{nonce} ."is too old ("
  3441. .sprintf("%d seconds", $dt)
  3442. .") and is removed from list";
  3443. delete($hash->{secNonce}{$id});
  3444. $n--;
  3445. }
  3446. }
  3447. }
  3448. if ($n >10) {
  3449. Log3 $hash->{NAME}, 2, "$hash->{NAME}: SECURITY: multiple nonce warning, "
  3450. ."there are $n nonce active";
  3451. }
  3452. $n=0;
  3453. do {
  3454. $nonce = ZWave_secGetNonce();
  3455. $nonce_id = substr($nonce, 0, 2);
  3456. $n++;
  3457. } until ((!$hash->{secNonce}{$nonce_id}) || $n>512);
  3458. if ($n >512) {
  3459. Log3 $hash->{NAME}, 1, "$hash->{NAME}: SECURITY: could not generate "
  3460. ."unique nonce, ignoring request!";
  3461. return ('00');
  3462. }
  3463. my $id = substr($nonce, 0 ,2);
  3464. $hash->{secNonce} = {} if (!$hash->{secNonce});
  3465. $hash->{secNonce}{$id}{nonce} = $nonce;
  3466. $hash->{secNonce}{$id}{timeStamp} = gettimeofday();
  3467. return ('80'.$nonce);
  3468. } else {
  3469. return ('00');
  3470. }
  3471. }
  3472. sub
  3473. ZWave_secRetrieveNonce($$)
  3474. {
  3475. my ($hash, $s_nonce_id_hex) = @_;
  3476. my $s_nonce_hex;
  3477. my $n=0;
  3478. return undef if (!$hash->{secNonce});
  3479. # remove old (>10 seconds) entries BEFORE trying to retrieve nonce
  3480. my $time = gettimeofday();
  3481. foreach my $id (sort keys %{$hash->{secNonce}}) {
  3482. $n++;
  3483. my $dt = $time - $hash->{secNonce}{$id}{timeStamp};
  3484. if ($dt > 10) {
  3485. Log3 $hash->{NAME}, 5, "$hash->{NAME}: SECURITY: nonce "
  3486. .$hash->{secNonce}{$id}{nonce} ."is too old ("
  3487. .sprintf("%d seconds", $dt)
  3488. .") and is removed from list";
  3489. delete($hash->{secNonce}{$id});
  3490. $n--;
  3491. }
  3492. }
  3493. if ($n >1) {
  3494. Log3 $hash->{NAME}, 5, "$hash->{NAME}: SECURITY: multiple nonce warning, "
  3495. ."there are $n nonce active";
  3496. }
  3497. if ($hash->{secNonce}{$s_nonce_id_hex}) {
  3498. $s_nonce_hex = $hash->{secNonce}{$s_nonce_id_hex}{nonce};
  3499. delete($hash->{secNonce}{$s_nonce_id_hex});
  3500. return $s_nonce_hex;
  3501. } else {
  3502. return undef;
  3503. }
  3504. }
  3505. sub
  3506. ZWave_secGetNonce()
  3507. {
  3508. my $nonce='';
  3509. for (my $i = 0; $i <8; $i++) {
  3510. $nonce .= sprintf "%02x",int(rand(256));
  3511. }
  3512. return $nonce;
  3513. }
  3514. sub
  3515. ZWave_secEncrypt($$$)
  3516. {
  3517. my ($hash, $r_nonce_hex, $plain) = @_;
  3518. my $name = $hash->{NAME};
  3519. my $iodev = $hash->{IODev};
  3520. my $id = $hash->{nodeIdHex};
  3521. my $init_enc_key = pack 'H*', 'a' x 32;
  3522. my $init_auth_key = pack 'H*', '5' x 32;
  3523. my $s_nonce_hex = ZWave_secGetNonce();
  3524. my $iv = pack 'H*', $s_nonce_hex . $r_nonce_hex;
  3525. my $key = pack 'H*', AttrVal($iodev->{NAME}, "networkKey", "");
  3526. my $enc_key = ZWave_secEncryptECB($key, $init_enc_key);
  3527. my $auth_key = ZWave_secEncryptECB($key, $init_auth_key);
  3528. my $seq = '00'; # Sequencebyte -> need to be calculated for longer messages
  3529. my $msg_hex = $seq . $plain;
  3530. my $out_hex = ZWave_secEncryptOFB ($enc_key, $iv, $msg_hex);
  3531. my $auth_msg_hex = '8101';
  3532. $auth_msg_hex .= sprintf "%02x", hex($hash->{nodeIdHex});
  3533. $auth_msg_hex .= sprintf "%02x", (length ($out_hex))/2;
  3534. $auth_msg_hex .= $out_hex;
  3535. Log3 $name, 5, "$name: secEncrypt plain:$msg_hex enc:$out_hex";
  3536. my $auth_code_hex = ZWave_secGenerateAuth($auth_key, $iv, $auth_msg_hex);
  3537. my $enc_msg_hex = '81' . $s_nonce_hex . $out_hex . substr($r_nonce_hex, 0, 2)
  3538. . $auth_code_hex;
  3539. return $enc_msg_hex;
  3540. }
  3541. sub
  3542. ZWave_secDecrypt($$$)
  3543. {
  3544. my ($hash, $data, $newnonce) = @_;
  3545. my $name = $hash->{NAME};
  3546. my $iodev = $hash->{IODev};
  3547. if (!ZWave_secIsEnabled($hash)) {
  3548. return;
  3549. }
  3550. my $init_enc_key = pack 'H*', 'a' x 32;
  3551. my $init_auth_key = pack 'H*', '5' x 32;
  3552. my $key = pack 'H*', AttrVal($iodev->{NAME}, "networkKey", "");
  3553. my $enc_key = ZWave_secEncryptECB($key, $init_enc_key);
  3554. my $auth_key = ZWave_secEncryptECB($key, $init_auth_key);
  3555. # encrypted message format:
  3556. # data= bcb328fe5d924a402b2901fc2699cc3bcacd30e0
  3557. # bcb328fe5d924a40 = 8 byte r_nonce
  3558. # 2b2901 = encrypted message, variable length
  3559. # fc = s_nonce-Id (= first byte of s_nonce)
  3560. # 2699cc3bcacd30e0 = 8 byte authentification code
  3561. if ($data !~ m/^(................)(.*)(..)(................)$/) {
  3562. Log3 $name, 1, "$name: Error, wrong format of encrypted msg";
  3563. ZWave_secEnd($hash);
  3564. return "";
  3565. }
  3566. my ($r_nonce_hex, $msg_hex, $s_nonce_id_hex, $auth_code_hex) = ($1, $2, $3, $4);
  3567. my $s_nonce_hex = ZWave_secRetrieveNonce($hash, $s_nonce_id_hex);
  3568. if (!$s_nonce_hex) {
  3569. Log3 $name, 1, "$name: Error, no send_nonce to decrypt message available";
  3570. ZWave_secEnd($hash);
  3571. return "";
  3572. }
  3573. Log3 $name, 5, "$name: secDecrypt: send_nonce $s_nonce_hex with "
  3574. ."nonce_id $s_nonce_id_hex retrieved";
  3575. my $iv = pack 'H*', $r_nonce_hex . $s_nonce_hex;
  3576. my $out_hex = ZWave_secEncryptOFB ($enc_key, $iv, $msg_hex);
  3577. Log3 $name, 5, "$name: secDecrypt: decrypted cmd $out_hex";
  3578. # decoding sequence information
  3579. # bit 76 reseved
  3580. # bit 5 second frame (0x20)
  3581. # bit 4 sequenced (0x10)
  3582. # bit 3210 sequenceCounter (0x0f)
  3583. my $seq = hex(substr ($out_hex, 0,2));
  3584. my $sequenced = (($seq & 0x10) ? 1 : 0);
  3585. my $secondFrame = (($seq & 0x20) ? 1 : 0);
  3586. my $sequenceCounter = sprintf "%02x", ($seq & 0x0f);
  3587. Log3 $name, 5, "$name: secDecrypt: Sequencebyte $seq, sequenced ".
  3588. "$sequenced, secondFrame $secondFrame, sequenceCounter $sequenceCounter";
  3589. # Rebuild message for authentification check
  3590. # 81280103 '81' . <from-id> . <to-id> . <len> . <encrMsg>
  3591. my $my_msg_hex = ($newnonce ? 'c1' : '81');
  3592. $my_msg_hex .= sprintf "%02x", hex($hash->{nodeIdHex});
  3593. $my_msg_hex .= '01';
  3594. $my_msg_hex .= sprintf "%02x", (length ($msg_hex))/2;
  3595. $my_msg_hex .= $msg_hex;
  3596. my $my_auth_code_hex = ZWave_secGenerateAuth($auth_key, $iv, $my_msg_hex);
  3597. Log3 $name, 5, "$name: secDecrypt: calculated Authentication code ".
  3598. "$my_auth_code_hex";
  3599. $out_hex = substr($out_hex, 2,length($out_hex)-2);
  3600. if ($auth_code_hex eq $my_auth_code_hex) {
  3601. if ($sequenced && !$secondFrame){ # first frame of sequence message
  3602. ZWave_secStoreFirstFrame($hash, $sequenceCounter, $out_hex);
  3603. } else { # not first frame or not sequenced
  3604. if ($sequenced && $secondFrame){
  3605. my $firstFrame = ZWave_secRetrieveFirstFrame ($hash, $sequenceCounter);
  3606. if ($firstFrame) {
  3607. $out_hex = $firstFrame . $out_hex;
  3608. } else {
  3609. Log3 $name, 1, "$name: secDecrypt: first frame of message (sequence ".
  3610. "$sequenceCounter) for decryption not found!";
  3611. }
  3612. }
  3613. my $decryptedCmd = '000400';
  3614. $decryptedCmd .= sprintf "%02x", hex($hash->{nodeIdHex});
  3615. $decryptedCmd .= sprintf "%02x", (length ($out_hex))/2;
  3616. $decryptedCmd .= $out_hex;
  3617. Log3 $name, 5, "$name: secDecrypt: parsing $decryptedCmd";
  3618. ZWave_Parse($iodev, $decryptedCmd, undef);
  3619. ZWave_secEnd($hash);
  3620. }
  3621. } else {
  3622. Log3 $name, 1, "$name: secDecrypt: Authentification code not verified, "
  3623. ."command $out_hex will be dropped!";
  3624. if ($sequenced && $secondFrame){
  3625. ZWave_secRetrieveFirstFrame ($hash, $sequenceCounter);
  3626. }
  3627. ZWave_secEnd($hash);
  3628. }
  3629. if ($newnonce == 1) {
  3630. ZWave_secAddToSendStack($hash, '98'.ZWave_secCreateNonce($hash));
  3631. #ZWave_Cmd("set", $hash, $hash->{NAME}, "secNonce");
  3632. }
  3633. return "";
  3634. }
  3635. sub
  3636. ZWave_secStoreFirstFrame ($$$) {
  3637. my ($hash, $seqcnt, $framedata) = @_;
  3638. my $framename = "Frame_$seqcnt";
  3639. $hash->{$framename} = $framedata;
  3640. }
  3641. sub
  3642. ZWave_secRetrieveFirstFrame ($$) {
  3643. my ($hash, $seqcnt) = @_;
  3644. my $framename = "Frame_$seqcnt";
  3645. if ($hash->{$framename}) {
  3646. my $ret = $hash->{$framename};
  3647. if ($ret) {
  3648. $hash->{$framename} = undef;
  3649. return $ret;
  3650. }
  3651. }
  3652. Log3 $hash->{NAME}, 1, "$hash->{NAME}: first frame of message (sequence ".
  3653. "$seqcnt) for decryption not found!";
  3654. return undef;
  3655. }
  3656. sub
  3657. ZWave_secEncryptECB ($$)
  3658. {
  3659. my ($key, $iv) = @_;
  3660. # $key and $iv as 'packed' hex-strings
  3661. my $cipher_ecb = Crypt::Rijndael->new ($key, Crypt::Rijndael::MODE_ECB() );
  3662. return $cipher_ecb->encrypt($iv);
  3663. }
  3664. sub
  3665. ZWave_secEncryptOFB ($$$)
  3666. {
  3667. my ($key, $iv, $in_hex) = @_;
  3668. # $key and $iv as 'packed' hex-strings, $in_hex as hex-string
  3669. my $cipher_ofb = Crypt::Rijndael->new($key,
  3670. Crypt::Rijndael::MODE_OFB() );
  3671. $cipher_ofb->set_iv($iv);
  3672. # make sure that the blocksize is 16 bytes / 32 characters
  3673. my $in_len = length($in_hex);
  3674. if ($in_len % 32) {
  3675. $in_hex .= '0' x (32 - ($in_len % 32));
  3676. }
  3677. my $out_hex = unpack 'H*', $cipher_ofb->encrypt(pack 'H*', $in_hex);
  3678. return (substr ($out_hex, 0, $in_len));
  3679. }
  3680. sub
  3681. ZWave_secGenerateAuth ($$$)
  3682. {
  3683. my ($key, $iv, $msg_hex) = @_;
  3684. my $cipher_ecb = Crypt::Rijndael->new ($key, Crypt::Rijndael::MODE_ECB() );
  3685. my $enc_iv = ZWave_secEncryptECB($key, $iv);
  3686. # make sure that the blocksize is 16 bytes / 32 characters
  3687. my $msg_len = length($msg_hex);
  3688. if ($msg_len % 32) {
  3689. $msg_hex .= '0' x (32 - ($msg_len % 32));
  3690. }
  3691. my $temp=0;
  3692. my $buff=0;
  3693. my $buff_hex="";
  3694. # xOR first block with encrypted iv
  3695. # encrypt the result, repeat for all blocks using encrypted output
  3696. # as input for xOR of next block
  3697. for (my $i = 0; $i < (length($msg_hex)/32); $i++) {
  3698. $buff_hex = substr($msg_hex, $i*32, 32);
  3699. $buff = pack 'H*', $buff_hex;
  3700. $temp = $buff ^ $enc_iv;
  3701. $enc_iv = $cipher_ecb->encrypt($temp);
  3702. };
  3703. # only 8 byte used for message authentification code
  3704. return unpack 'H16', $enc_iv;
  3705. }
  3706. sub
  3707. ZWave_secNetworkkeyEncode ($$$$)
  3708. {
  3709. my ($nonce_hex, $mynonce_hex, $key_plain_hex, $id_hex) = @_;
  3710. #my $name ="ZWave_secNetworkkeySet";
  3711. # The NetworkKeySet command message will be encrcpted and authentificated
  3712. # with temporary keys that are created with the networkkey and default
  3713. # keys for encryption and authentification as given below.
  3714. my $init_enc_key = pack 'H*', 'a' x 32;
  3715. my $init_auth_key = pack 'H*', '5' x 32;
  3716. my $key_zero = pack 'H*', '0' x 32;
  3717. my $nonce = pack 'H*', $nonce_hex;
  3718. my $mynonce = pack 'H*', $mynonce_hex;
  3719. my $enc_key = ZWave_secEncryptECB($key_zero, $init_enc_key);
  3720. my $auth_key = ZWave_secEncryptECB($key_zero, $init_auth_key);
  3721. my $iv = pack 'H*', $mynonce_hex . $nonce_hex;
  3722. # build 'plain-text' message to be encrypted
  3723. # 0x00 = sequence byte -> only one frame
  3724. # 0x98 = Security Class
  3725. # 0x06 = NetworkKeySet
  3726. $key_plain_hex = '009806'.$key_plain_hex;
  3727. my $out_hex = ZWave_secEncryptOFB($enc_key, $iv, $key_plain_hex);
  3728. ############ MAC generation ############################
  3729. # build message for encryption
  3730. # command, source-id, target-id
  3731. # 0x81="Security_Message_Encapsulation" 0x01=Souce-ID (Controller = 0x01)
  3732. my $in_hex = '8101' . $id_hex;
  3733. $in_hex .= sprintf "%02x", length($out_hex)/2; # length of command
  3734. $in_hex .= $out_hex; # encrypted network key
  3735. my $auth_hex = ZWave_secGenerateAuth ($auth_key, $iv, $in_hex);
  3736. # build encrypted message
  3737. # Command Class will be added during sending -> do not prepend
  3738. # 0x81 = Security_Message_Encapsulation
  3739. $out_hex = '81' . $mynonce_hex . $out_hex . substr($nonce_hex, 0, 2) .
  3740. $auth_hex;
  3741. return $out_hex;
  3742. }
  3743. ##############################################
  3744. # SECURITY (end)
  3745. ##############################################
  3746. sub
  3747. ZWave_getHash($$$)
  3748. {
  3749. my ($hash, $cl, $type) = @_;
  3750. my $ptr; # must be standalone, as there is a $ptr in the calling fn.
  3751. $ptr = $zwave_class{$cl}{$type}
  3752. if($zwave_class{$cl} && $zwave_class{$cl}{$type});
  3753. if($cl eq "CONFIGURATION" && $type ne "parse") {
  3754. my $mc = ZWave_configGetHash($hash);
  3755. if($mc) {
  3756. my $mcp = $mc->{$type};
  3757. if($mcp) {
  3758. my %nptr = ();
  3759. map({$nptr{$_} = $ptr->{$_}} keys %{$ptr});
  3760. map({$nptr{$_} = $mcp->{$_}} keys %{$mcp});
  3761. $ptr = \%nptr;
  3762. }
  3763. }
  3764. }
  3765. my $modelId = ReadingsVal($hash->{NAME}, "modelId", "");
  3766. $modelId = $zwave_modelIdAlias{$modelId} if($zwave_modelIdAlias{$modelId});
  3767. my $p = $zwave_deviceSpecial{$modelId};
  3768. if($p && $p->{$cl}) {
  3769. $ptr = $p->{$cl}{$type} if($p->{$cl}{$type});
  3770. my $add = $p->{$cl}{$type."_ADD"};
  3771. $ptr = {} if($add && !$ptr);
  3772. map { $ptr->{$_} = $add->{$_} } keys %{$add} if($add);
  3773. }
  3774. if($type eq "get" || $type eq "set") {
  3775. my %h;
  3776. my $version = $hash->{".vclasses"}{$cl};
  3777. $version = $defs{$hash->{endpointParent}}{".vclasses"}{$cl}
  3778. if(!$version &&
  3779. $hash->{endpointParent} && $defs{$hash->{endpointParent}});
  3780. $version = 0 if(!defined($version));
  3781. map {
  3782. my $zv = $zwave_classVersion{$_};
  3783. if(!$zv || ((!$zv->{min} || $zv->{min} <= $version) &&
  3784. (!$zv->{max} || $zv->{max} >= $version))) {
  3785. $h{$_} = $ptr->{$_};
  3786. }
  3787. } keys %{$ptr};
  3788. $ptr = \%h;
  3789. }
  3790. return $ptr;
  3791. }
  3792. my $zwave_cbid = 0;
  3793. my %zwave_cbid2dev;
  3794. sub
  3795. ZWave_callbackId($)
  3796. {
  3797. my ($p) = @_;
  3798. if(ref($p) eq "HASH") {
  3799. $zwave_cbid = 0 if($zwave_cbid == 255);
  3800. $zwave_cbid2dev{++$zwave_cbid} = $p;
  3801. return sprintf("%02x", $zwave_cbid);
  3802. }
  3803. return $zwave_cbid2dev{hex($p)};
  3804. }
  3805. sub
  3806. ZWave_wakeupTimer($$)
  3807. {
  3808. my ($hash, $direct) = @_;
  3809. my $now = gettimeofday();
  3810. my $wnmi_delay = AttrVal($hash->{NAME}, "WNMI_delay", 2);
  3811. if(!$hash->{wakeupAlive}) {
  3812. $hash->{wakeupAlive} = 1;
  3813. $hash->{lastMsgSent} = $now;
  3814. InternalTimer($now+0.1, "ZWave_wakeupTimer", $hash, 0);
  3815. } elsif(!$direct && $now - $hash->{lastMsgSent} > $wnmi_delay) {
  3816. if(!$hash->{SendStack}) {
  3817. my $nodeId = $hash->{nodeIdHex};
  3818. my $cmdEf = (AttrVal($hash->{NAME},"noExplorerFrames",0)==0 ? "25":"05");
  3819. # wakeupNoMoreInformation
  3820. IOWrite($hash,
  3821. $hash->{homeId}.($hash->{route} ? ",".$hash->{route} : ""),
  3822. "0013${nodeId}028408${cmdEf}".ZWave_callbackId($hash));
  3823. $hash->{lastMsgSent} = $now;
  3824. }
  3825. delete $hash->{wakeupAlive};
  3826. } else {
  3827. return if($direct);
  3828. InternalTimer($now+0.1, "ZWave_wakeupTimer", $hash, 0);
  3829. }
  3830. }
  3831. sub
  3832. ZWave_isWakeUp($)
  3833. {
  3834. my ($h) = @_;
  3835. $h->{isWakeUp} = (index(AttrVal($h->{NAME}, "classes", ""), "WAKE_UP") >= 0)
  3836. if(!defined($h->{isWakeUp}));
  3837. return $h->{isWakeUp};
  3838. }
  3839. sub
  3840. ZWave_addCRC16($)
  3841. {
  3842. my ($msg) = @_;
  3843. return $msg if($msg !~ m/13(..)(..)(.*)(....)$/);
  3844. my ($tgt, $olen, $omsg, $sfx) = ($1, $2, $3, $4);
  3845. $msg = sprintf("13%s%02x5601%s%s%s", $tgt, length($omsg)/2+4,
  3846. $omsg, uc(ZWave_CRC16("5601$omsg")), $sfx);
  3847. return $msg;
  3848. }
  3849. # stack contains type:hexcode
  3850. # type is: set / sentset, get / sentget / sentackget
  3851. # sentset will be discarded after ack, sentget needs ack (->sentackget) then msg
  3852. # next discards either state.
  3853. # acktpye: retry, next, ack or msg
  3854. sub
  3855. ZWave_processSendStack($$;$)
  3856. {
  3857. my ($hash,$ackType, $omsg) = @_;
  3858. delete($hash->{delayedProcessing});
  3859. my $ss = $hash->{SendStack};
  3860. if(!$ss) {
  3861. readingsSingleUpdate($hash, "timeToAck",
  3862. sprintf("%0.3f", gettimeofday()-$hash->{lastMsgSent}), 0)
  3863. if($ackType eq "ack" && $hash->{lastMsgSent});
  3864. return;
  3865. }
  3866. #Log 1, "pSS: $hash->{NAME}, $ackType $ss->[0]".($omsg ? " omsg:$omsg" : "");
  3867. if ($ackType eq "next") {
  3868. if($ss->[0] =~ m/^sent(.*?):13....98(.*)$/) { # only for security commands
  3869. my $secMsg = $hash->{secMsg};
  3870. if ($secMsg && @{$secMsg}) {
  3871. Log3 $hash->{NAME}, 1, "$hash->{NAME}: NO_ACK received during secured "
  3872. ."command: $secMsg->[0], command will be removed from security stack";
  3873. shift(@{$secMsg});
  3874. ZWave_secEnd($hash);
  3875. }
  3876. }
  3877. }
  3878. if($ackType eq "retry") {
  3879. $ss->[0] =~ m/^(.*)(set|get):(.*)$/;
  3880. $ss->[0] = "$2:$3";
  3881. return;
  3882. }
  3883. my $now = gettimeofday();
  3884. if($ss->[0] =~ m/^sent(.*?):(.*)(..)$/) {
  3885. my ($stype,$smsg, $cbid) = ($1,$2,$3);
  3886. if($ackType eq "ack") {
  3887. if($cbid ne $omsg) {
  3888. Log 4, "ZWave: wrong callbackid $omsg received, expecting $cbid";
  3889. return;
  3890. }
  3891. readingsSingleUpdate($hash, "timeToAck",
  3892. sprintf("%0.3f", $now-$hash->{lastMsgSent}), 0)
  3893. if($hash->{lastMsgSent});
  3894. if($stype eq "get") {
  3895. $ss->[0] = "sentackget:$smsg$cbid";
  3896. return;
  3897. }
  3898. } elsif($stype eq "ackget" && $ackType eq "msg") {# compare answer class
  3899. $smsg = "13xxxx$1" if($smsg =~ /^......600d....(.*)/);
  3900. my $cs = substr($smsg, 6, 2);
  3901. my $cg = substr($omsg, 2, 2);
  3902. return if($cs ne $cg);
  3903. }
  3904. shift @{$ss};
  3905. $hash->{cmdsPending} = int(@{$ss});
  3906. RemoveInternalTimer($hash) if(!ZWave_isWakeUp($hash));
  3907. }
  3908. if(@{$ss} == 0) {
  3909. delete $hash->{SendStack};
  3910. return;
  3911. }
  3912. $ss->[0] =~ m/^([^:]*?):(.*)$/;
  3913. my ($type, $msg) = ($1, $2);
  3914. my $iomsg = $hash->{useCRC16} ? ZWave_addCRC16($msg) : $msg;
  3915. IOWrite($hash,
  3916. $hash->{homeId}.($hash->{route}?",".$hash->{route}:""),
  3917. "00$iomsg");
  3918. $ss->[0] = "sent$type:$msg";
  3919. $hash->{lastMsgSent} = $now;
  3920. $zwave_lastHashSent = $hash;
  3921. if(!ZWave_isWakeUp($hash)) {
  3922. InternalTimer($hash->{lastMsgSent}+5, sub { # zme generates NO_ACK after 4s
  3923. Log 2, "ZWave: No ACK from $hash->{NAME} after 5s for $ss->[0]";
  3924. ZWave_processSendStack($hash, "next");
  3925. }, $hash, 0);
  3926. }
  3927. }
  3928. # packs multiple gets into a MULTI_CMD-get, may reorder
  3929. sub
  3930. ZWave_packSendStack($)
  3931. {
  3932. my ($hash) = @_;
  3933. my (@ns, @ms, $sfx, $cmd, $id);
  3934. my $ncmd = 0;
  3935. for my $se (@{$hash->{SendStack}}) {
  3936. if($se =~ m/^get:13(..)(...*)(....)$/) {
  3937. ($id, $cmd, $sfx) = ($1, $2, $3);
  3938. if($cmd =~ m/^..8f01(..)(.*)/i) { # already Multi-cmd
  3939. $ncmd += $1; $cmd = $2;
  3940. } else {
  3941. $ncmd++;
  3942. }
  3943. push(@ms, $cmd);
  3944. } else {
  3945. push(@ns, $se);
  3946. }
  3947. }
  3948. return if($ncmd < 2 || @ms < 2);
  3949. $cmd = join("", @ms);
  3950. push @ns, sprintf("get:13$id%02x8f01%02x%s%s",
  3951. length($cmd)/2+3, $ncmd, $cmd, $sfx);
  3952. $hash->{SendStack} = \@ns;
  3953. $hash->{cmdsPending} = int(@ns);
  3954. }
  3955. sub
  3956. ZWave_addToSendStack($$$)
  3957. {
  3958. my ($hash, $type, $cmd) = @_;
  3959. if(!$hash->{SendStack}) {
  3960. my @empty;
  3961. $hash->{SendStack} = \@empty;
  3962. }
  3963. my $ss = $hash->{SendStack};
  3964. push @{$ss}, "$type:$cmd";
  3965. $hash->{cmdsPending} = int(@{$ss});
  3966. #Log 1, "aTSS: $hash->{NAME}, $type, $cmd / L:".int(@{$ss});
  3967. if(ZWave_isWakeUp($hash)) {
  3968. # SECURITY XXX
  3969. if($cmd =~ m/^......988[01].*/) {
  3970. Log3 $hash->{NAME}, 5, "$hash->{NAME}: Sendstack bypassed for $cmd";
  3971. } else {
  3972. if($hash->{useMultiCmd}) {
  3973. ZWave_packSendStack($hash);
  3974. if($hash->{INTRIGGER}) { # Allow repacking of multiple gets on WUN
  3975. if(!$hash->{delayedProcessing}) {
  3976. $hash->{delayedProcessing} = 1;
  3977. InternalTimer(1, sub(){ZWave_processSendStack($hash, "next");}, 0);
  3978. }
  3979. return;
  3980. }
  3981. }
  3982. return "Scheduled for sending after WAKEUP" if(!$hash->{wakeupAlive});
  3983. }
  3984. } else { # clear commands without 0113 and 0013
  3985. my $now = gettimeofday();
  3986. if(@{$ss} > 1 && $now-$hash->{lastMsgSent} > 5) {
  3987. Log3 $hash, 2,
  3988. "ERROR: $hash->{NAME}: cleaning commands without ack after 5s";
  3989. delete $hash->{SendStack};
  3990. $hash->{cmdsPending} = 0;
  3991. return ZWave_addToSendStack($hash, $type, $cmd);
  3992. }
  3993. }
  3994. ZWave_processSendStack($hash, "next") if(@{$ss} == 1);
  3995. return undef;
  3996. }
  3997. ###################################
  3998. # 0004000a03250300 (sensor binary off for id 11)
  3999. # { ZWave_Parse($defs{zd}, "0004000c028407", "") }
  4000. sub
  4001. ZWave_Parse($$@)
  4002. {
  4003. my ($iodev, $msg, $srcCmd) = @_;
  4004. my $homeId = $iodev->{homeId};
  4005. my $ioName = $iodev->{NAME};
  4006. if(!$homeId) {
  4007. Log3 $ioName, 1, "ERROR: $ioName homeId is not set!"
  4008. if(!$iodev->{errReported});
  4009. $iodev->{errReported} = 1;
  4010. return "";
  4011. }
  4012. if($msg =~ m/^01(..)(..*)/) { # 01==ANSWER from the ZWDongle
  4013. my ($cmd, $arg) = ($1, $2);
  4014. $cmd = $zw_func_id{$cmd} if($zw_func_id{$cmd});
  4015. if($cmd eq "ZW_SEND_DATA") { # 011301: data was sent.
  4016. if($arg != 1) {
  4017. if($zwave_lastHashSent) {
  4018. my $hash = $zwave_lastHashSent;
  4019. readingsSingleUpdate($hash, "SEND_DATA", "failed:$arg", 1);
  4020. $arg = "transmit queue overflow" if($arg == 0);
  4021. Log3 $ioName, 2, "ERROR: cannot SEND_DATA to $hash->{NAME}: $arg";
  4022. ZWave_processSendStack($hash, "retry");
  4023. } else {
  4024. Log3 $ioName, 2, "ERROR: cannot SEND_DATA: $arg (unknown device)";
  4025. }
  4026. }
  4027. return "";
  4028. }
  4029. if($cmd eq "SERIAL_API_SET_TIMEOUTS" && $arg =~ m/(..)(..)/) {
  4030. Log3 $ioName, 2, "SERIAL_API_SET_TIMEOUTS: ACK:$1 BYTES:$2";
  4031. return "";
  4032. }
  4033. if($cmd eq "ZW_REMOVE_FAILED_NODE_ID" ||
  4034. $cmd eq "ZW_REPLACE_FAILED_NODE") {
  4035. my $retval;
  4036. if($arg eq "00") { $retval = 'failedNodeRemoveStarted';
  4037. } elsif($arg eq "02") { $retval = 'notPrimaryController';
  4038. } elsif($arg eq "04") { $retval = 'noCallbackFunction';
  4039. } elsif($arg eq "08") { $retval = 'failedNodeNotFound';
  4040. } elsif($arg eq "10") { $retval = 'failedNodeRemoveProcessBusy';
  4041. } elsif($arg eq "20") { $retval = 'failedNodeRemoveFail';
  4042. } else { $retval = 'unknown_'.$arg; # should never happen
  4043. }
  4044. DoTrigger($ioName, "$cmd $retval");
  4045. return "";
  4046. }
  4047. if($cmd eq "ZW_REQUEST_NETWORK_UPDATE") {
  4048. my $retval;
  4049. if($arg eq "00") { $retval = 'selfOrNoSUC';
  4050. } elsif($arg eq "01") { $retval = 'started';
  4051. } else { $retval = 'unknown_'.$arg; # should never happen
  4052. }
  4053. DoTrigger($ioName, "$cmd $retval");
  4054. return "";
  4055. }
  4056. if($cmd eq "ZW_ASSIGN_SUC_RETURN_ROUTE" ||
  4057. $cmd eq "ZW_DELETE_SUC_RETURN_ROUTE" ||
  4058. $cmd eq "ZW_ASSIGN_RETURN_ROUTE" ||
  4059. $cmd eq "ZW_DELETE_RETURN_ROUTE" ||
  4060. $cmd eq "ZW_SEND_SUC_ID") {
  4061. my $retval;
  4062. if($arg eq "00") { $retval = 'alreadyActive';
  4063. } elsif($arg eq "01") { $retval = 'started';
  4064. } else { $retval = 'unknown_'.$arg; # should never happen
  4065. }
  4066. DoTrigger($ioName, "$cmd $retval");
  4067. return "";
  4068. }
  4069. if($cmd eq "ZW_SET_SUC_NODE_ID") {
  4070. my $retval;
  4071. if($arg eq "00") { $retval = 'failed';
  4072. } elsif($arg eq "01") { $retval = 'ok';
  4073. } else { $retval = 'unknown_'.$arg; # should never happen
  4074. }
  4075. DoTrigger($ioName, "$cmd $retval");
  4076. return "";
  4077. }
  4078. if($cmd eq "CLEAR_NETWORK_STATS") {
  4079. my $retval;
  4080. if($arg eq "01") { $retval = 'ok';
  4081. } else { $retval = 'unknown_'.$arg; # should never happen
  4082. }
  4083. DoTrigger($ioName, "$cmd $retval");
  4084. return "";
  4085. }
  4086. if($cmd eq "ZW_SET_PRIORITY_ROUTE" && $arg =~ m/(..)(..)/) {
  4087. DoTrigger($ioName, "$cmd node $1 result $2");
  4088. return "";
  4089. }
  4090. foreach my $h (keys %zwave_parseHook) {
  4091. if("$cmd:$arg" =~ m/$h/) {
  4092. my $fn = $zwave_parseHook{$h};
  4093. delete $zwave_parseHook{$h};
  4094. $fn->($arg);
  4095. return "";
  4096. }
  4097. }
  4098. Log3 $ioName, 4, "$ioName unhandled ANSWER: $cmd $arg";
  4099. return "";
  4100. }
  4101. if($msg =~ m/^00(..)(..*)/) { # 00==REQUEST from the ZWDongle
  4102. my ($cmd, $arg) = ($1, $2);
  4103. $cmd = $zw_func_id{$cmd} if($zw_func_id{$cmd});
  4104. if($cmd eq "ZW_SET_DEFAULT") {
  4105. my $retval;
  4106. if($arg eq "01") { $retval = 'done';
  4107. } else { $retval = 'unknown_'.$arg; # should never happen
  4108. }
  4109. DoTrigger($ioName, "$cmd $retval");
  4110. return "";
  4111. }
  4112. }
  4113. if($msg !~ m/^00(..)(..)(..)(.*)/) { # 00=REQUEST
  4114. Log3 $ioName, 4, "$ioName: UNKNOWN msg $msg";
  4115. return "";
  4116. }
  4117. my ($cmd, $callbackid, $id, $arg) = ($1, $2, $3, $4);
  4118. $cmd = $zw_func_id{$cmd} if($zw_func_id{$cmd});
  4119. #####################################
  4120. # Controller commands
  4121. my $evt;
  4122. my $rawMsg = "CMD:$cmd ID:$id ARG:$arg"; # No fmt change, Forum #49165
  4123. Log3 $ioName, 4, $rawMsg ." CB:$callbackid";
  4124. my $hash = ZWave_callbackId($callbackid);
  4125. if($cmd eq 'ZW_ADD_NODE_TO_NETWORK' ||
  4126. $cmd eq 'ZW_REMOVE_NODE_FROM_NETWORK') {
  4127. my @vals = ("learnReady", "nodeFound", "slave",
  4128. "controller", "protocolDone", "done", "failed");
  4129. $evt = ($id eq "00" || hex($id)>@vals+1) ? "unknownArg" : $vals[hex($id)-1];
  4130. if(($evt eq "slave" || $evt eq "controller") &&
  4131. $arg =~ m/(..)....(..)..(.*)$/) {
  4132. my ($id,$type6,$classes) = ($1, $2, $3);
  4133. return ZWave_SetClasses($homeId, $id, $type6, $classes)
  4134. if($cmd eq 'ZW_ADD_NODE_TO_NETWORK');
  4135. }
  4136. if($evt eq "protocolDone" && $arg =~ m/(..)../) {
  4137. my $dh = $modules{ZWave}{defptr}{"$homeId $1"};
  4138. return "" if(!$dh);
  4139. my $addSecure = $iodev->{addSecure}; # addNode off deletes it
  4140. AnalyzeCommand(undef, "set $ioName addNode off")
  4141. if($cmd eq 'ZW_ADD_NODE_TO_NETWORK');
  4142. ZWave_wakeupTimer($dh, 1) if(ZWave_isWakeUp($dh));
  4143. return "" if($addSecure && ZWave_secIncludeStart($dh, $iodev) == 1);
  4144. return ZWave_execInits($dh, 0);
  4145. }
  4146. # addNode off generates ZW_ADD_NODE_TO_NETWORK:done sometimes (#51411)
  4147. if($evt eq "failed" && $cmd eq 'ZW_ADD_NODE_TO_NETWORK') {
  4148. asyncOutput($iodev->{addCL}, "addNode failed");
  4149. AnalyzeCommand(undef, "set $ioName addNode off")
  4150. }
  4151. } elsif($cmd eq "ZW_APPLICATION_UPDATE") {
  4152. if($arg =~ m/....(..)..(.*)$/) {
  4153. my ($type6,$classes) = ($1, $2);
  4154. my $ret = ZWave_SetClasses($homeId, $id, $type6, $classes);
  4155. $hash = $modules{ZWave}{defptr}{"$homeId $id"};
  4156. if($hash) {
  4157. if(!AttrVal($hash->{NAME}, "noWakeupForApplicationUpdate", 0)) { # 50090
  4158. if(ZWave_isWakeUp($hash)) {
  4159. ZWave_wakeupTimer($hash, 1);
  4160. ZWave_processSendStack($hash, "next");
  4161. }
  4162. }
  4163. if(!$ret) {
  4164. readingsSingleUpdate($hash, "CMD", $cmd, 1); # forum:20884
  4165. return $hash->{NAME};
  4166. }
  4167. } else {
  4168. InternalTimer(1, sub(){ # execInits for createNode
  4169. $hash = $modules{ZWave}{defptr}{"$homeId $id"};
  4170. ZWave_execInits($hash, 0) if($hash);
  4171. }, 0);
  4172. }
  4173. return $ret;
  4174. } elsif($callbackid eq "10") {
  4175. $evt = 'sucId '.hex($id);
  4176. DoTrigger($ioName, "$cmd $evt");
  4177. Log3 $ioName, 4, "$ioName $cmd $evt";
  4178. return "";
  4179. } elsif($callbackid eq "20") {
  4180. $evt = 'deleteDone '.hex($id);
  4181. DoTrigger($ioName, "$cmd $evt");
  4182. Log3 $ioName, 4, "$ioName $cmd $evt";
  4183. return "";
  4184. } elsif($callbackid eq "40") {
  4185. $evt = 'addDone '.hex($id);
  4186. DoTrigger($ioName, "$cmd $evt");
  4187. Log3 $ioName, 4, "$ioName $cmd $evt";
  4188. return "";
  4189. } elsif($callbackid eq "81") {
  4190. Log3 $ioName, 2, "ZW_REQUEST_NODE_INFO failed ".hex($id);
  4191. return "";
  4192. } else {
  4193. Log3 $ioName, 2, "ZW_APPLICATION_UPDATE unknown $callbackid";
  4194. return "";
  4195. }
  4196. } elsif($cmd eq "ZW_SEND_DATA") { # 0013cb00....
  4197. my %msg = ('00'=>'OK', '01'=>'NO_ACK', '02'=>'FAIL',
  4198. '03'=>'NOT_IDLE', '04'=>'NOROUTE' );
  4199. my $lmsg = ($msg{$id} ? $msg{$id} : "UNKNOWN_ERROR");
  4200. Log3 $ioName, ($id eq "00" ? 4 : 2),
  4201. "$ioName transmit $lmsg for CB $callbackid, target ".
  4202. ($hash ? $hash->{NAME} : "unknown");
  4203. if($id eq "00") {
  4204. my $name="";
  4205. if($hash) {
  4206. ZWave_processSendStack($hash, "ack", $callbackid);
  4207. readingsSingleUpdate($hash, "transmit", $lmsg, 0);
  4208. if($iodev->{showSetInState}) {
  4209. my $lCU = $hash->{lastChannelUsed};
  4210. my $lname = $lCU ? $lCU : $hash->{NAME};
  4211. my $state = ReadingsVal($lname, "state", "");
  4212. if($state =~ m/^set_(.*)$/) {
  4213. readingsSingleUpdate($defs{$lname}, "state", $1, 1);
  4214. $name = $lname;
  4215. }
  4216. }
  4217. }
  4218. delete($hash->{lastChannelUsed});
  4219. return $name;
  4220. } else { # Wait for the retry timer to remove this cmd from the stack.
  4221. return "" if(!$hash);
  4222. #readingsSingleUpdate($hash, "state", "TRANSMIT_$lmsg", 1); #Forum #57781
  4223. readingsSingleUpdate($hash, "transmit", $lmsg, 1);
  4224. return $hash->{NAME};
  4225. }
  4226. } elsif($cmd eq "ZW_SET_LEARN_MODE") {
  4227. if($id eq "01") { $evt = 'started';
  4228. } elsif($id eq "06") { $evt = 'done'; # $arg = new NodeId
  4229. } elsif($id eq "07") { $evt = 'failed';
  4230. } elsif($id eq "80") { $evt = 'deleted';
  4231. } else { $evt = 'unknown'; # should never happen
  4232. }
  4233. } elsif($cmd eq "ZW_REQUEST_NODE_NEIGHBOR_UPDATE") {
  4234. if($id eq "21") { $evt = 'started';
  4235. } elsif($id eq "22") { $evt = 'done';
  4236. } elsif($id eq "23") { $evt = 'failed';
  4237. } else { $evt = 'unknown'; # should never happen
  4238. }
  4239. if($hash) {
  4240. readingsSingleUpdate($hash, "neighborUpdate", $evt, 1);
  4241. return $hash->{NAME};
  4242. }
  4243. } elsif($cmd eq "ZW_REMOVE_FAILED_NODE_ID") {
  4244. if($id eq "00") { $evt = 'nodeOk';
  4245. } elsif($id eq "01") { $evt = 'failedNodeRemoved';
  4246. } elsif($id eq "02") { $evt = 'failedNodeNotRemoved';
  4247. } else { $evt = 'unknown_'.$id; # should never happen
  4248. }
  4249. } elsif($cmd eq "ZW_REPLACE_FAILED_NODE") {
  4250. if($id eq "00") { $evt = 'nodeOk';
  4251. } elsif($id eq "03") { $evt = 'failedNodeReplace';
  4252. } elsif($id eq "04") { $evt = 'failedNodeReplaceDone';
  4253. } elsif($id eq "05") { $evt = 'failedNodeRemoveFailed';
  4254. } else { $evt = 'unknown_'.$id; # should never happen
  4255. }
  4256. } elsif($cmd eq "ZW_ASSIGN_SUC_RETURN_ROUTE" ||
  4257. $cmd eq "ZW_DELETE_SUC_RETURN_ROUTE" ||
  4258. $cmd eq "ZW_ASSIGN_RETURN_ROUTE" ||
  4259. $cmd eq "ZW_DELETE_RETURN_ROUTE" ||
  4260. $cmd eq "ZW_SEND_SUC_ID") {
  4261. if($id eq "00") { $evt = 'transmitOk';
  4262. } elsif($id eq "01") { $evt = 'transmitNoAck';
  4263. } elsif($id eq "02") { $evt = 'transmitFail';
  4264. } elsif($id eq "03") { $evt = 'transmitFailNotIdle';
  4265. } elsif($id eq "04") { $evt = 'transmitNoRoute';
  4266. } else { $evt = 'unknown_'.$id; # should never happen
  4267. }
  4268. } elsif($cmd eq "ZW_REQUEST_NETWORK_UPDATE") {
  4269. if($id eq "00") { $evt = 'done';
  4270. } elsif($id eq "01") { $evt = 'abort';
  4271. } elsif($id eq "02") { $evt = 'wait';
  4272. } elsif($id eq "03") { $evt = 'disabled';
  4273. } elsif($id eq "04") { $evt = 'overflow';
  4274. } else { $evt = 'unknown_'.$id; # should never happen
  4275. }
  4276. } elsif($cmd eq "ZW_SET_SUC_NODE_ID") {
  4277. if($id eq "05") { $evt = 'callbackSucceeded';
  4278. } elsif($id eq "06") { $evt = 'callbackFailed';
  4279. } else { $evt = 'unknown_'.$id; # do not know
  4280. }
  4281. } elsif($cmd eq "ZW_CONTROLLER_CHANGE" ||
  4282. $cmd eq "ZW_CREATE_NEW_PRIMARY") {
  4283. my @vals = ("learnReady", "nodeFound", "slave","controller", "protocolDone",
  4284. "done", "failed"); # slave should never happen
  4285. $evt = ($id eq "00" || hex($id)>@vals+1) ? "unknownArg" : $vals[hex($id)-1];
  4286. if($cmd eq "ZW_CREATE_NEW_PRIMARY" && $evt eq "protocolDone") {
  4287. AnalyzeCommand(undef, "set $ioName createNewPrimary stop");
  4288. }
  4289. if($cmd eq "ZW_CONTROLLER_CHANGE" && $evt eq "protocolDone") {
  4290. AnalyzeCommand(undef, "set $ioName addNode off");
  4291. }
  4292. }
  4293. if($evt) {
  4294. return "$cmd $evt" if($srcCmd);
  4295. DoTrigger($ioName, "$cmd $evt");
  4296. Log3 $ioName, 4, "$ioName $cmd $evt";
  4297. return "";
  4298. }
  4299. ######################################
  4300. # device messages
  4301. if($cmd ne "APPLICATION_COMMAND_HANDLER") {
  4302. Log3 $ioName, 4, "$ioName unhandled command $cmd";
  4303. return ""
  4304. }
  4305. if($arg =~ m/^(..)(.*)/) {
  4306. my $l1 = hex($1);
  4307. my $l2 = length($2)/2;
  4308. if($l1 > $l2) {
  4309. Log3 $ioName, $2, "Packet with short length ($arg)";
  4310. return "";
  4311. }
  4312. $arg = substr($arg, 0, ($l1+1)*2) if($l1 < $l2); #79659
  4313. }
  4314. if($arg =~ m/^(..)(..)(.*)/ && $2 eq "c6") { # Danfoss Living Strangeness
  4315. Log3 $ioName, 4, "Class mod for Danfoss ($2)";
  4316. $arg = sprintf("%s%02x%s", $1, hex($2) & 0x7f, $3);
  4317. }
  4318. if($arg =~ /^..5601(.*)(....)/) { # CRC_16_ENCAP: Unwrap encapsulated command
  4319. #Log3 $ioName, 4, "CRC FIX, MSG: ($1)"; # see Forum #23494
  4320. my $crc16 = ZWave_CRC16("5601".$1);
  4321. if (lc($2) eq lc($crc16)) {
  4322. $arg = sprintf("%02x$1", length($1)/2);
  4323. } else {
  4324. Log3 $ioName, 4, "$ioName CRC_16 checksum mismatch, received $2," .
  4325. " calculated $crc16";
  4326. return "";
  4327. }
  4328. }
  4329. my ($baseHash, $baseId, $ep) = ("",$id,"");
  4330. if($arg =~ /^..6006(..)(.*)/) { # MULTI_CHANNEL CMD_ENCAP, V1, Forum #36126
  4331. $ep = $1;
  4332. $baseHash = $modules{ZWave}{defptr}{"$homeId $id"};
  4333. $id = "$id$ep";
  4334. $arg = sprintf("%02x$2", length($2)/2);
  4335. }
  4336. if($arg =~ /^..600d(..)(..)(.*)/) { # MULTI_CHANNEL CMD_ENCAP, V2
  4337. $ep = sprintf("%02x", hex($1 ne "00" ? $1 : $2) & 0x7f); # Forum #50176
  4338. if($ep ne "00") {
  4339. $baseHash = $modules{ZWave}{defptr}{"$homeId $id"};
  4340. $id = "$id$ep";
  4341. }
  4342. $arg = sprintf("%02x$3", length($3)/2);
  4343. }
  4344. $hash = $modules{ZWave}{defptr}{"$homeId $id"};
  4345. $baseHash = $hash if(!$baseHash);
  4346. if(!$hash) {
  4347. if(!$baseHash) {
  4348. Log3 $ioName, 4, "ZWave: unknown message $msg for ID $id";
  4349. return "";
  4350. }
  4351. # autocreate the device when pressing the remote button (Forum #43261)
  4352. $id=hex($id); $baseId=hex($baseId); $ep=hex($ep);
  4353. my $nn = "ZWave_Node_$baseId".($ep eq "0" ? "" : ".$ep");
  4354. my $ret = "UNDEFINED $nn ZWave $homeId $id";
  4355. Log3 $ioName, 3, "$ret, please define it. Triggered by $msg.";
  4356. DoTrigger("global", $ret);
  4357. return "";
  4358. }
  4359. my $name = $hash->{NAME};
  4360. my @event;
  4361. my @args = ($arg); # MULTI_CMD handling
  4362. while(@args) {
  4363. $arg = shift(@args);
  4364. return if($arg !~ m/^..(..)/);
  4365. my $class = $1;
  4366. my $className = $zwave_id2class{lc($class)} ?
  4367. $zwave_id2class{lc($class)} : "UNKNOWN_".uc($class);
  4368. if($className eq "MULTI_CMD") {
  4369. my ($ncmd, $off) = (0, 4);
  4370. while(length($arg) > $off*2) {
  4371. my $l = hex(substr($arg, $off*2, 2))+1;
  4372. push @args, substr($arg, $off*2, $l*2);
  4373. $off += $l;
  4374. }
  4375. next;
  4376. }
  4377. my $ptr = ZWave_getHash($hash, $className, "parse");
  4378. if(!$ptr) {
  4379. push @event, "UNPARSED:$className $arg";
  4380. next;
  4381. }
  4382. my $hookVeto = 0;
  4383. foreach my $h (keys %zwave_parseHook) {
  4384. if("$id:$arg" =~ m/$h/) {
  4385. my $fn = $zwave_parseHook{$h};
  4386. delete $zwave_parseHook{$h};
  4387. $hookVeto++ if($fn->($hash, $arg));
  4388. }
  4389. }
  4390. if(!$hookVeto) {
  4391. my $matched = 0;
  4392. foreach my $k (keys %{$ptr}) {
  4393. if($arg =~ m/^$k/) {
  4394. my $val = $ptr->{$k};
  4395. my @val = ($val);
  4396. @val = eval $val if(index($val, '$') >= 0);
  4397. push @event, @val if(defined($val[0]));
  4398. $matched++;
  4399. }
  4400. }
  4401. push @event, "UNPARSED:$className $arg" if(!$matched);
  4402. }
  4403. }
  4404. if($arg =~ m/^028407/) { # wakeup:notification (WUN)
  4405. if($hash->{ignoreDupMsg} && $hash->{wakeupAlive}) { # Ignore 2nd WUN
  4406. Log3 $name, 3, "$name: ignore duplicate WUN";
  4407. return ""
  4408. }
  4409. ZWave_wakeupTimer($baseHash, 1);
  4410. ZWave_processSendStack($baseHash, "next");
  4411. } else {
  4412. if($hash->{ignoreDupMsg} && $callbackid ne "00") {
  4413. my $hp = hex($callbackid);
  4414. if($zwave_cbid2dev{$hp}) {
  4415. delete $zwave_cbid2dev{$hp};
  4416. } else {
  4417. Log3 $name, 3, "$name: ignore duplicate answer $event[0]";
  4418. return "";
  4419. }
  4420. }
  4421. ZWave_processSendStack($baseHash, "msg", $arg)
  4422. if(!ZWave_isWakeUp($hash) || $hash->{wakeupAlive});
  4423. }
  4424. return "" if(!@event);
  4425. readingsBeginUpdate($hash);
  4426. for(my $i = 0; $i < int(@event); $i++) {
  4427. next if($event[$i] eq "");
  4428. my ($vn, $vv) = split(":", $event[$i], 2);
  4429. readingsBulkUpdate($hash, $vn, $vv);
  4430. readingsBulkUpdate($hash, "reportedState", $vv)
  4431. if($vn eq "state"); # different from set
  4432. }
  4433. readingsBulkUpdate($hash, "rawMsg", $rawMsg)
  4434. if(AttrVal($name, "eventForRaw", undef));
  4435. readingsEndUpdate($hash, 1);
  4436. if($hash->{asyncGet} && $msg =~ m/$hash->{asyncGet}{re}/) {
  4437. RemoveInternalTimer($hash->{asyncGet});
  4438. asyncOutput($hash->{asyncGet}{CL}, join("\n", @event));
  4439. delete($hash->{asyncGet});
  4440. }
  4441. return join("\n", @event) if($srcCmd);
  4442. return $name;
  4443. }
  4444. #####################################
  4445. sub
  4446. ZWave_Undef($$)
  4447. {
  4448. my ($hash, $arg) = @_;
  4449. my $homeId = $hash->{homeId};
  4450. my $id = $hash->{nodeIdHex};
  4451. delete $modules{ZWave}{defptr}{"$homeId $id"};
  4452. return undef;
  4453. }
  4454. #####################################
  4455. sub
  4456. ZWave_computeRoute($;$)
  4457. {
  4458. my ($spec,$attrVal) = @_;
  4459. for my $dev (devspec2array($spec)) {
  4460. my $av = AttrVal($dev, "zwaveRoute", $attrVal);
  4461. next if(!$av);
  4462. my @h;
  4463. for my $r (split(" ", $av)) {
  4464. if(!$defs{$r} || $defs{$r}{TYPE} ne "ZWave") {
  4465. my $msg = "zwaveRoute: $r is not a ZWave device";
  4466. Log 1, $msg;
  4467. return $msg;
  4468. }
  4469. push(@h, $defs{$r}{nodeIdHex});
  4470. }
  4471. $defs{$dev}{route} = join("",@h);
  4472. }
  4473. return undef;
  4474. }
  4475. sub
  4476. ZWave_Attr(@)
  4477. {
  4478. my ($type, $devName, $attrName, $param) = @_;
  4479. my $hash = $defs{$devName};
  4480. if($attrName eq "zwaveRoute") {
  4481. if($type eq "del") {
  4482. delete $hash->{route};
  4483. return undef;
  4484. }
  4485. return ZWave_computeRoute($devName, $param) if($init_done);
  4486. InternalTimer(1, "ZWave_computeRoute", "TYPE=ZWave", 0);
  4487. return undef;
  4488. } elsif($attrName eq "ignoreDupMsg") {
  4489. if($type eq "del") {
  4490. delete $hash->{ignoreDupMsg};
  4491. } else {
  4492. $hash->{ignoreDupMsg} = (defined($param) ? $param : 1);
  4493. }
  4494. return undef;
  4495. } elsif($attrName eq "vclasses") {
  4496. if($type eq "del") {
  4497. $hash->{".vclasses"} = {};
  4498. return undef;
  4499. }
  4500. my %h = map { split(":", $_) } split(" ", $param);
  4501. $hash->{".vclasses"} = \%h;
  4502. return undef;
  4503. } elsif($attrName eq "useMultiCmd") {
  4504. if($type eq "del") {
  4505. delete $hash->{useMultiCmd};
  4506. return undef;
  4507. }
  4508. my $a = ($attr{$devName} ? $attr{$devName}{classes} : "");
  4509. return "useMultiCmd: unsupported device, see help ZWave for details"
  4510. if(!$a || !($a =~ m/MULTI_CMD/ && $a =~ m/WAKE_UP/));
  4511. $hash->{useMultiCmd} = 1;
  4512. return undef;
  4513. } elsif($attrName eq "useCRC16") {
  4514. if($type eq "del") {
  4515. delete $hash->{useCRC16};
  4516. return undef;
  4517. }
  4518. my $a = ($attr{$devName} ? $attr{$devName}{classes} : "");
  4519. return "useCRC16: unsupported device, see help ZWave for details"
  4520. if(!$a || $a !~ m/CRC_16_ENCAP/ ||
  4521. ReadingsVal($devName, "SECURITY", "") eq "ENABLED");
  4522. $hash->{useCRC16} = 1;
  4523. return undef;
  4524. }
  4525. return undef;
  4526. }
  4527. sub
  4528. ZWave_switchBinary_timer($$$)
  4529. {
  4530. my ($hash, $on, $time) = @_;
  4531. return (undef, "01$on".ZWave_time2byte($hash, $time));
  4532. }
  4533. sub
  4534. ZWave_switchMultilevel_Set($$$)
  4535. {
  4536. my ($hash, $cmdType, $arg) = @_;
  4537. my $n = $hash->{NAME};
  4538. $arg = uc($arg);
  4539. my ($regexp, $duration);
  4540. if($cmdType == 0) { # dimWithDuration
  4541. return "$n dimWithDuration: wrong format for $arg"
  4542. if($arg !~ m/$p1_b (\d+)/);
  4543. return ("", sprintf("01%02x%s", $1, ZWave_time2byte($hash,$2)));
  4544. } elsif ($cmdType == 1) { # dimUpDown
  4545. $regexp = "(UP|DOWN) (IGNORE|USE) $p1_b";
  4546. } elsif ($cmdType == 2) { # dimUpDownWithDuration
  4547. $regexp = "(UP|DOWN) (IGNORE|USE) $p1_b $p1_b";
  4548. } elsif ($cmdType == 3) { # dimUpDownIncDecWithDuration
  4549. $regexp = "(UP|DOWN|NOMOTION) (IGNORE|USE) $p1_b "
  4550. ."$p1_b (INC|DEC|NOINCDEC) $p1_b";
  4551. }
  4552. return "$n: wrong format, see commandref" if($arg !~ m/$regexp/);
  4553. my $bitfield = 0;
  4554. $bitfield |= ($2 eq "IGNORE") ? 0x01<<5 : 0x00;
  4555. $bitfield |= ($1 eq "DOWN") ? 0x01<<6 :
  4556. ($1 eq "NOMOTION") ? 0x03<<6 : 0x00;
  4557. if ($cmdType == 3) {
  4558. $bitfield |= ($5 eq "DEC") ? 0x01<<3 :
  4559. ($5 eq "NOINCDEC") ? 0x03<<3 : 0x00;
  4560. }
  4561. $duration = ZWave_time2byte($hash,$4) if ($cmdType >=2);
  4562. my $rt = sprintf("04%02x%02x", $bitfield, $3);
  4563. $rt .= sprintf("%s", $duration) if ($cmdType == 2);
  4564. $rt .= sprintf("%s%02x", $duration, $6) if ($cmdType == 3);
  4565. return ("", $rt);
  4566. }
  4567. my %zwave_switchType = (
  4568. "00"=>"Undefined",
  4569. "01"=>"On/Off",
  4570. "02"=>"Up/Down",
  4571. "03"=>"Open/Close",
  4572. "04"=>"Clockwise/Counter-Clockwise",
  4573. "05"=>"Right/Left",
  4574. "06"=>"Forward/Reverse",
  4575. "07"=>"Push/Pull",
  4576. );
  4577. sub
  4578. Zwave_switchType($)
  4579. {
  4580. my ($t) = @_;
  4581. $t = sprintf("%02x", hex($t)&0x1f); # clear reserved bits
  4582. return (($zwave_switchType{$t}) ? $zwave_switchType{$t} : "Unknown");
  4583. }
  4584. sub
  4585. ZWave_time2byte($$)
  4586. {
  4587. my ($hash, $txt) = @_;
  4588. if($txt !~ m/^[0-9]+$/) {
  4589. Log 1, "ZWave_time2byte: wrong duration $txt, replacing it with 0";
  4590. return "00";
  4591. }
  4592. my $b = ($txt <= 0x7f ? $txt : int($txt/60)+0x7f);
  4593. $b = 0xfe if($b > 0xfe);
  4594. my $b2 = $b > 0x7f ? ($b - 0x7f) * 60 : $b;
  4595. my $n = ($hash ? $hash->{NAME} : "unknown");
  4596. Log3 $n, 2, "$n: changing *for-timeout to $b2 from $txt" if($b2 != $txt);
  4597. return sprintf("%02x", $b);
  4598. }
  4599. sub
  4600. ZWave_byte2time($)
  4601. {
  4602. my ($duration) = @_;
  4603. my $time = hex($duration);
  4604. $time = ($time - 0x7f) * 60 if($time>0x7f && $time<0xff);
  4605. return (lc($duration) eq "ff" ? "factoryDefault" : "$time seconds");
  4606. }
  4607. #####################################
  4608. # Show the help from the device.xml, if the correct entry is selected
  4609. sub
  4610. ZWave_helpFn($$)
  4611. {
  4612. my ($d,$cmd) = @_;
  4613. my $mc = ZWave_configGetHash($defs{$d});
  4614. return "" if(!$mc);
  4615. my $h = $mc->{config}{$cmd};
  4616. return "" if(!$h || !$h->{Help});
  4617. $cmd .= " (numeric code $h->{index})" if(defined($h->{index}));
  4618. my $ret = "Help for $cmd:<br>".$h->{Help};
  4619. my $hi = $h->{Item};
  4620. $ret .= "Possible values: ".
  4621. join(", ", map {"$_ ($hi->{$_})"} sort keys %{$hi})."<br>"
  4622. if($hi);
  4623. return $ret;
  4624. }
  4625. sub
  4626. ZWave_getPic($$)
  4627. {
  4628. my ($iodev, $model) = @_;
  4629. my $hs = AttrVal($iodev, "helpSites", $zwave_allHelpSites);
  4630. for my $n (split(",", $hs)) {
  4631. my $img = $zwave_img{$n}{$model};
  4632. next if(!$img);
  4633. my $fn = $attr{global}{modpath}."/www/deviceimages/zwave/$img";
  4634. if(!-f $fn) { # Cache the picture
  4635. my $url = $n eq "alliance" ?
  4636. "http://products.z-wavealliance.org/ProductImages/Index?productName=":
  4637. "http://fhem.de/deviceimages/zwave/";
  4638. Log 3, "ZWave: downloading $url/$img for $model";
  4639. my $data = GetFileFromURL("$url/$img");
  4640. if($data && open(FH,">$fn")) {
  4641. binmode(FH);
  4642. print FH $data;
  4643. close(FH)
  4644. }
  4645. }
  4646. return "$FW_ME/deviceimages/zwave/$img";
  4647. }
  4648. return "";
  4649. }
  4650. sub
  4651. ZWave_fhemwebFn($$$$)
  4652. {
  4653. my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn.
  4654. my $pl = ""; # Pepper link and image
  4655. my $model = ReadingsVal($d, "modelId", "");
  4656. return '' if (!$model);
  4657. my $iodev = $defs{$d}{IODev}{NAME};
  4658. my $hs = AttrVal($iodev, "helpSites", $zwave_activeHelpSites);
  4659. for my $n (split(",", $hs)) {
  4660. my $link = $zwave_link{$n}{lc($model)};
  4661. next if(!$link);
  4662. $pl .= "<div class='detLink ZWPepper'>";
  4663. my $url = ($n eq "alliance" ?
  4664. "http://products.z-wavealliance.org/products/" :
  4665. "http://devel.pepper1.net/zwavedb/device/");
  4666. $pl .= "<a target='_blank' href='$url/$link'>Details in $n DB</a>";
  4667. $pl .= "</div>";
  4668. }
  4669. my $img = ZWave_getPic($iodev, lc($model));
  4670. if($img && !$FW_ss) {
  4671. $pl .= "<div class='img'".($FW_tp?"":" style='float:right'").">";
  4672. $pl .= "<img style='max-width:96;max-height:96px;' src='$img'>";
  4673. $pl .= "</div>";
  4674. }
  4675. return
  4676. "<div id='ZWHelp' class='makeTable help'></div>$pl".
  4677. '<script type="text/javascript">'.
  4678. "var zwaveDevice='$d', FW_tp='$FW_tp';" . <<'JSEND'
  4679. $(document).ready(function() {
  4680. $("div#ZWHelp").insertBefore("div.makeTable.internals"); // Move
  4681. $("div.detLink.ZWPepper").insertAfter("div.detLink.devSpecHelp");
  4682. if(FW_tp) $("div.img.ZWPepper").appendTo("div#menu");
  4683. $("select.set,select.get").each(function(){
  4684. $(this).get(0).setValueFn = function(val) {
  4685. $("div#ZWHelp").html(val);
  4686. }
  4687. $(this).change(function(){
  4688. FW_queryValue('{ZWave_helpFn("'+zwaveDevice+'","'+$(this).val()+'")}',
  4689. $(this).get(0));
  4690. });
  4691. });
  4692. });
  4693. </script>
  4694. JSEND
  4695. }
  4696. #####################################
  4697. # 2-byte signed hex
  4698. sub
  4699. s2Hex($)
  4700. {
  4701. my ($p) = @_;
  4702. $p = hex($p);
  4703. return ($p > 32767 ? -(65536-$p) : $p);
  4704. }
  4705. 1;
  4706. =pod
  4707. =item summary devices communicating via the ZWave protocol
  4708. =item summary_DE Anbindung von ZWave Ger&auml;ten
  4709. =begin html
  4710. <a name="ZWave"></a>
  4711. <h3>ZWave</h3>
  4712. <ul>
  4713. This module is used to control ZWave devices via FHEM, see <a
  4714. href="http://www.z-wave.com">www.z-wave.com</a> for details for this device
  4715. family. The full specification of ZWave command classes can be found here:
  4716. <a href="http://zwavepublic.com/specifications"
  4717. title="website with the full specification of ZWave command classes">
  4718. http://zwavepublic.com/specifications</a>.
  4719. This module is a client of the <a href="#ZWDongle">ZWDongle</a>
  4720. module, which is directly attached to the controller via USB or TCP/IP. To
  4721. use the SECURITY features, the Crypt-Rijndael perl module is needed.
  4722. <br><br>
  4723. <a name="ZWavedefine"></a>
  4724. <b>Define</b>
  4725. <ul>
  4726. <code>define &lt;name&gt; ZWave &lt;homeId&gt; &lt;id&gt; [classes]</code>
  4727. <br>
  4728. <br>
  4729. &lt;homeId&gt; is the homeId of the controller node, and id is the id of the
  4730. slave node in the network of this controller.<br>
  4731. classes is a hex-list of ZWave device classes. This argument is usually
  4732. specified by autocreate when creating a device. If you wish to manually
  4733. create a device, use the classes attribute instead, see below for details.
  4734. Defining a ZWave device the first time is usually done by autocreate.
  4735. <br>
  4736. Example:
  4737. <ul>
  4738. <code>define lamp ZWave 00ce2074 9</code><br>
  4739. <code>attr lamp classes SWITCH_BINARY BASIC MANUFACTURER_SPECIFIC VERSION
  4740. SWITCH_ALL ASSOCIATION METER CONFIGURATION ALARM</code><br>
  4741. </ul>
  4742. </ul>
  4743. <br>
  4744. Note: the sets/gets/generated events of a given node depend on the classes
  4745. supported by this node. If a node supports 3 classes, then the union of
  4746. these sets/gets/events will be available for this node.<br>
  4747. Commands for battery operated nodes will be queued internally, and sent when
  4748. the node sends a message. Answers to get commands appear then as events, the
  4749. corresponding readings will be updated.
  4750. <br><br>
  4751. <a name="ZWaveset"></a>
  4752. <b>Set</b>
  4753. <ul>
  4754. <br>
  4755. <b>Notes</b>:
  4756. <ul>
  4757. <li>devices with on/off functionality support the <a
  4758. href="#setExtensions"> set extensions</a>.</li>
  4759. <li>A set command does not automatically update the corresponding reading,
  4760. you have to execute a get for this purpose. This can be automatically
  4761. done via a notify, although this is not recommended in all cases.</li>
  4762. </ul>
  4763. <br><br><b>All</b>
  4764. <li>neighborUpdate<br>
  4765. Requests controller to update its routing table which is based on
  4766. slave's neighbor list. The update may take significant time to complete.
  4767. With the event "done" or "failed" ZWDongle will notify the end of the
  4768. update process. To read node's neighbor list see neighborList get
  4769. below.</li>
  4770. <li>returnRouteAdd &lt;decimal nodeId&gt;<br>
  4771. Assign up to 4 static return routes to the routing/enhanced slave to
  4772. allow direct communication to &lt;decimal nodeId&gt;. (experts only)</li>
  4773. <li>returnRouteDel<br>
  4774. Delete all static return routes. (experts only)</li>
  4775. <li>sucRouteAdd<br>
  4776. Inform the routing/enhanced slave of the presence of a SUC/SIS. Assign
  4777. up to 4 static return routes to SUC/SIS.</li>
  4778. <li>sucRouteDel<br>
  4779. Delete static return routes to SUC/SIS node.</li>
  4780. <br><br><b>Class ALARM</b>
  4781. <li>alarmnotification &lt;alarmType&gt; (on|off)<br>
  4782. Enable (on) or disable (off) the sending of unsolicited reports for
  4783. the alarm type &lt;alarmType&gt;. A list of supported alarm types of the
  4784. device can be obtained with the alarmTypeSupported command. The
  4785. name of the alarm type is case insensitive. Sending of an unsolicited
  4786. notification only works for associated nodes, broadcasting is not
  4787. allowed, so associations have to be set up. This command is
  4788. specified in version 2.</li>
  4789. <li> Note:<br>
  4790. The name of the class ALARM was renamed to NOTIFICATION in
  4791. version 3 of the Zwave specification. Due to backward compatibility
  4792. reasons the class will be always referenced as ALARM in FHEM,
  4793. regardless of the version.</li>
  4794. <br><br><b>Class ASSOCIATION</b>
  4795. <li>associationAdd groupId nodeId ...<br>
  4796. Add the specified list of nodeIds to the association group groupId.<br> Note:
  4797. upon creating a FHEM-device for the first time FHEM will automatically add
  4798. the controller to the first association group of the node corresponding to
  4799. the FHEM device, i.e it issues a "set name associationAdd 1
  4800. controllerNodeId"</li>
  4801. <li>associationDel groupId nodeId ...<br>
  4802. Remove the specified list of nodeIds from the association group groupId.</li>
  4803. <br><br><b>Class BASIC</b>
  4804. <li>basicValue value<br>
  4805. Send value (0-255) to this device. The interpretation is device dependent,
  4806. e.g. for a SWITCH_BINARY device 0 is off and anything else is on.</li>
  4807. <li>basicValue value<br>
  4808. Alias for basicValue, to make mapping from the incoming events easier.
  4809. </li>
  4810. <br><br><b>Class BARRIER_OPERATOR</b>
  4811. <li>barrierClose<br>
  4812. start closing the barrier.</li>
  4813. <li>barrierOpen<br>
  4814. start opening the barrier.
  4815. </li>
  4816. <br><br><b>Class BASIC_WINDOW_COVERING</b>
  4817. <li>coveringClose<br>
  4818. Starts closing the window cover. Moving stops if blinds are fully closed or
  4819. a coveringStop command was issued.
  4820. </li>
  4821. <li>coveringOpen<br>
  4822. Starts opening the window cover. Moving stops if blinds are fully open or
  4823. a coveringStop command was issued.
  4824. </li>
  4825. <li>coveringStop<br>
  4826. Stop moving the window cover. Blinds are partially open (closed).
  4827. </li>
  4828. <br><br><b>Class CLIMATE_CONTROL_SCHEDULE</b>
  4829. <li>ccs [mon|tue|wed|thu|fri|sat|sun] HH:MM tempDiff HH:MM tempDiff ...<br>
  4830. set the climate control schedule for the given day.<br>
  4831. Up to 9 pairs of HH:MM tempDiff may be specified.<br>
  4832. HH:MM must occur in increasing order.
  4833. tempDiff is relative to the setpoint temperature, and may be between -12
  4834. and 12, with one decimal point, measured in Kelvin (or Centigrade).<br>
  4835. If only a weekday is specified without any time and tempDiff, then the
  4836. complete schedule for the specified day is removed and marked as unused.
  4837. </li>
  4838. <li>ccsOverride (no|temporary|permanent) (frost|energy|$tempOffset) <br>
  4839. set the override state<br>
  4840. no: switch the override off<br>
  4841. temporary: override the current schedule only<br>
  4842. permanent: override all schedules<br>
  4843. frost/energy: set override mode to frost protection or energy saving<br>
  4844. $tempOffset: the temperature setback (offset to setpoint) in 1/10 degrees
  4845. range from -12.8 to 12.0, values will be limited to this range.
  4846. </li>
  4847. <br><br><b>Class CLOCK</b>
  4848. <li>clock<br>
  4849. set the clock to the current date/time (no argument required)
  4850. </li>
  4851. <br><br><b>Class COLOR_CONTROL</b>
  4852. <li>rgb<br>
  4853. Set the color of the device as a 6 digit RGB Value (RRGGBB), each color is
  4854. specified with a value from 00 to ff.</li>
  4855. <li>wcrgb<br>
  4856. Used for sending warm white, cold white, red, green and blue values
  4857. to device. Values must be decimal (0 - 255) and separated by blanks.
  4858. <ul>
  4859. set &lt;name&gt; wcrgb 0 255 0 0 0 (setting full cold white)<br>
  4860. </ul>
  4861. </li>
  4862. <br><br><b>Class CONFIGURATION</b>
  4863. <li>configByte cfgAddress 8bitValue<br>
  4864. configWord cfgAddress 16bitValue<br>
  4865. configLong cfgAddress 32bitValue<br>
  4866. Send a configuration value for the parameter cfgAddress. cfgAddress and
  4867. value are node specific.<br>
  4868. Note: if the model is set (see MANUFACTURER_SPECIFIC get), then more
  4869. specific config commands are available.</li>
  4870. <li>configDefault cfgAddress<br>
  4871. Reset the configuration parameter for the cfgAddress parameter to its
  4872. default value. See the device documentation to determine this value.</li>
  4873. <br><br><b>Class DOOR_LOCK, V2</b>
  4874. <li>doorLockOperation DOOR_LOCK_MODE<br>
  4875. Set the operation mode of the door lock.<br>
  4876. DOOR_LOCK_MODE:<br>
  4877. open = Door unsecured<br>
  4878. close = Door secured<br>
  4879. 00 = Door unsecured<br>
  4880. 01 = Door unsecured with timeout<br>
  4881. 10 = Door unsecured for inside door handles<br>
  4882. 11 = Door unsecured for inside door handles with timeout<br>
  4883. 20 = Door unsecured for outside door handles<br>
  4884. 21 = Door unsecured for outside door handles with timeout<br>
  4885. FF = Door secured<br>
  4886. Note: open/close can be used as an alias for 00/FF.
  4887. </li>
  4888. <li>doorLockConfiguration operationType outsidehandles
  4889. insidehandles timeoutSeconds<br>
  4890. Set the configuration for the door lock.<br>
  4891. operationType: [constant|timed]<br>
  4892. outsidehandle/insidehandle: 4-bit binary field for handle 1-4,
  4893. bit=0:handle disabled, bit=1:handle enabled, highest bit is for
  4894. handle 4, lowest bit for handle 1. Example 0110 0001
  4895. = outside handles 3 and 2 are active, inside handle 1 is active<br>
  4896. timeoutSeconds: time out for timed operation (in seconds) [1-15239].
  4897. </li>
  4898. <br><br><b>Class INDICATOR</b>
  4899. <li>indicatorOn<br>
  4900. switch the indicator on</li>
  4901. <li>indicatorOff<br>
  4902. switch the indicator off</li>
  4903. <li>indicatorDim value<br>
  4904. takes values from 1 to 99.
  4905. If the indicator does not support dimming, it is interpreted as on.</li>
  4906. <br><br><b>Class MANUFACTURER_PROPRIETARY</b>
  4907. <br>Fibaro FGR(M)-222 only:
  4908. <li>positionBlinds<br>
  4909. drive blinds to position %</li>
  4910. <li>positionSlat<br>
  4911. drive slat to position %</li>
  4912. <br>D-Link DCH-Z510, Philio PSE02, Zipato Indoor Siren only:<br>
  4913. switch alarm on with selected sound (to stop use: set &lt;device&gt; off)
  4914. <li>alarmEmergencyOn</li>
  4915. <li>alarmFireOn</li>
  4916. <li>alarmAmbulanceOn</li>
  4917. <li>alarmPoliceOn</li>
  4918. <li>alarmDoorchimeOn</li>
  4919. <li>alarmBeepOn</li>
  4920. <br><br><b>Class METER</b>
  4921. <li>meterReset<br>
  4922. Reset all accumulated meter values.<br>
  4923. Note: see meterSupported command and its output to detect if resetting the
  4924. value is supported by the device.<br>
  4925. The command will reset ALL accumulated values, it is not possible to
  4926. choose a single value.</li>
  4927. <br><br><b>Class MULTI_CHANNEL</b>
  4928. <li>mcCreateAll<br>
  4929. Create a FHEM device for all channels. This command is executed after
  4930. inclusion of a new device.
  4931. </li>
  4932. <br><br><b>Class MULTI_CHANNEL_ASSOCIATION</b>
  4933. <li>mcaAdd groupId node1 node2 ... 0 node1 endPoint1 node2 endPoint2 ...<br>
  4934. Add a list of node or node:endpoint associations. The latter can be used to
  4935. create channels on remotes. E.g. to configure the button 1,2,... on the
  4936. zwave.me remote, use:
  4937. <ul>
  4938. set remote mcaAdd 2 0 1 2<br>
  4939. set remote mcaAdd 3 0 1 3<br>
  4940. ....
  4941. </ul>
  4942. For each button a separate FHEM device will be generated.
  4943. </li>
  4944. <li>mcaDel groupId node1 node2 ... 0 node1 endPoint1 node2 endPoint2 ...<br>
  4945. delete node or node:endpoint associations.
  4946. Special cases: just specifying the groupId will delete everything for this
  4947. groupId. Specifying 0 for groupId will delete all associations.
  4948. </li>
  4949. <br><br><b>Class NETWORK_SCHEDULE (SCHEDULE), V1</b>
  4950. <li>schedule ID USER_ID YEAR-MONTH-DAY WDAY ACTIVE_ID DURATION_TYPE
  4951. HOUR:MINUTE DURATION NUM_REPORTS CMD ... CMD<br>
  4952. Set a schedule for a user. Due to lack of documentation,
  4953. details for some parameters are not available. Command Class is
  4954. used together with class USER_CODE.<br>
  4955. <ul>
  4956. ID: id of schedule, refer to maximum number of supported schedules
  4957. reported by the scheduleSupported command.<br>
  4958. USER_ID: id of user, starting from 1 up to the number of supported
  4959. users, refer also to the USER_CODE class description.<br>
  4960. YEAR-MONTH-DAY: start of schedule in the format yyyy-mm-dd.<br>
  4961. WDAY: weekday, 1=Monday, 7=Sunday.<br>
  4962. ACTIVE_ID: unknown parameter.<br>
  4963. DURATION_TYPE: unknown parameter.<br>
  4964. HOUR:MINUTE: start of schedule in the format hh:mm.<br>
  4965. DURATION: unknown parameter.<br>
  4966. NUM_REPORTS: number of reports to follow, must be 0.<br>
  4967. CMD: command(s) (as hexcode sequence) that the schedule executes,
  4968. see report of scheduleSupported command for supported command
  4969. class and mask. A list of space separated commands can be
  4970. specified.<br>
  4971. </ul>
  4972. </li>
  4973. <li>scheduleRemove ID<br>
  4974. Remove the schedule with the id ID</li>
  4975. <li>scheduleState ID STATE<br>
  4976. Set the STATE of the schedule with the id ID. Description for
  4977. parameter STATE is not available.</li>
  4978. <br><br><b>Class NODE_NAMING</b>
  4979. <li>name NAME<br>
  4980. Store NAME in the EEPROM. Note: only ASCII is supported.</li>
  4981. <li>location LOCATION<br>
  4982. Store LOCATION in the EEPROM. Note: only ASCII is supported.</li>
  4983. <br><br><b>Class POWERLEVEL</b>
  4984. <li>Class is only used in an installation or test situation</li>
  4985. <li>powerlevel level timeout/s<br>
  4986. set powerlevel to level [0-9] for timeout/s [1-255].<br>
  4987. level 0=normal, level 1=-1dBm, .., level 9=-9dBm.</li>
  4988. <li>powerlevelTest nodeId level frames <br>
  4989. send number of frames [1-65535] to nodeId with level [0-9].</li>
  4990. <br><br><b>Class PROTECTION</b>
  4991. <li>protectionOff<br>
  4992. device is unprotected</li>
  4993. <li>protectionOn<br>
  4994. device is protected</li>
  4995. <li>protectionSeq<br>
  4996. device can be operated, if a certain sequence is keyed.</li>
  4997. <li>protectionBytes LocalProtectionByte RFProtectionByte<br>
  4998. for commandclass PROTECTION V2 - see devicemanual for supported
  4999. protectionmodes</li>
  5000. <br><br><b>Class SCENE_ACTIVATION</b>
  5001. <li>sceneConfig<br>
  5002. activate settings for a specific scene.
  5003. Parameters are: sceneId, dimmingDuration (0..255)
  5004. </li>
  5005. <br><br><b>Class SCENE_ACTUATOR_CONF</b>
  5006. <li>sceneConfig<br>
  5007. set configuration for a specific scene.
  5008. Parameters are: sceneId, dimmingDuration, finalValue (0..255)
  5009. </li>
  5010. <br><br><b>Class SCENE_CONTROLLER_CONF</b>
  5011. <li>groupConfig<br>
  5012. set configuration for a specific scene.
  5013. Parameters are: groupId, sceneId, dimmingDuration.
  5014. </li>
  5015. <br><br><b>Class SCHEDULE_ENTRY_LOCK, V1, V2, V3</b>
  5016. <li>scheduleEntryLockSet USER_ID ENABLED<br>
  5017. enables or disables schedules for a specified user ID (V1)<br>
  5018. <ul>
  5019. USER_ID: id of user, starting from 1 up to the number of supported
  5020. users, refer also to the USER_CODE class description.<br>
  5021. ENABLED: 0=disabled, 1=enabled<br>
  5022. </ul>
  5023. </li>
  5024. <li>scheduleEntryLockAllSet ENABLED<br>
  5025. enables or disables schedules for all users (V1)<br>
  5026. <ul>
  5027. ENABLED: 0=disabled, 1=enabled<br>
  5028. </ul>
  5029. </li>
  5030. <li>scheduleEntryLockWeekDaySet ACTION USER_ID SCHEDULE_ID WEEKDAY
  5031. STARTTIME ENDTIME<br>
  5032. erase or set a week day schedule for a specified user ID (V1)<br>
  5033. <ul>
  5034. ACTION: 0=erase schedule slot, 1=modify the schedule slot for the
  5035. user<br>
  5036. USER_ID: id of user, starting from 1 up to the number of supported
  5037. users, refer also to the USER_CODE class description.<br>
  5038. SCHEDULE_ID: schedule slot id (from 1 up to number of supported
  5039. schedule slots)<br>
  5040. WEEKDAY: day of week, choose one of:
  5041. "sun","mon","tue","wed","thu","fri","sat"<br>
  5042. STARTTIME: time of schedule start, in the format HH:MM
  5043. (leading 0 is mandatory)<br>
  5044. ENDTIME: time of schedule end in the format HH:MM
  5045. (leading 0 is mandatory)<br>
  5046. </ul>
  5047. </li>
  5048. <li>scheduleEntryLockYearDaySet ACTION USER_ID SCHEDULE_ID
  5049. STARTDATE STARTTIME ENDDATE ENDTIME<br>
  5050. erase or set a year day schedule for a specified user ID (V1)<br>
  5051. <ul>
  5052. ACTION: 0=erase schedule slot, 1=modify the schedule slot for the
  5053. user<br>
  5054. USER_ID: id of user, starting from 1 up to the number of supported
  5055. users, refer also to the USER_CODE class description.<br>
  5056. SCHEDULE_ID: schedule slot id (from 1 up to number of supported
  5057. schedule slots)<br>
  5058. STARTDATE: date of schedule start in the format YYYY-MM-DD<br>
  5059. STARTTIME: time of schedule start in the format HH:MM
  5060. (leading 0 is mandatory)<br>
  5061. ENDDATE: date of schedule end in the format YYYY-MM-DD<br>
  5062. ENDTIME: time of schedule end in the format HH:MM
  5063. (leading 0 is mandatory)<br>
  5064. </ul>
  5065. </li>
  5066. <li>scheduleEntryLockTimeOffsetSet TZO DST<br>
  5067. set the TZO and DST (V2)<br>
  5068. <ul>
  5069. TZO: current local time zone offset in the format (+|-)HH:MM
  5070. (sign and leading 0 is mandatory)<br>
  5071. DST: daylight saving time offset in the format (+|-)[[m]m]m
  5072. (sign is mandatory, minutes: 0 to 127, 1-3 digits)<br>
  5073. </ul>
  5074. </li>
  5075. <li>scheduleEntryLockDailyRepeatingSet ACTION USER_ID SCHEDULE_ID
  5076. WEEKDAYS STARTTIME DURATION<br>
  5077. set a daily repeating schedule for the specified user (V3)<br>
  5078. <ul>
  5079. ACTION: 0=erase schedule slot, 1=modify the schedule slot for the
  5080. user<br>
  5081. USER_ID: id of user, starting from 1 up to the number of supported
  5082. users, refer also to the USER_CODE class description.<br>
  5083. SCHEDULE_ID: schedule slot id (from 1 up to number of supported
  5084. schedule slots)<br>
  5085. WEEKDAYS: concatenated string of weekdays (choose from:
  5086. "sun","mon","tue","wed","thu","fri","sat");
  5087. e.g. "montuewedfri" or "satsun", unused days can be
  5088. specified as "..."<br>
  5089. STARTTIME: time of schedule start in the format HH:MM
  5090. (leading 0 is mandatory)<br>
  5091. DURATION: duration of schedule in the format HH:MM
  5092. (leading 0 is mandatory)<br>
  5093. </ul>
  5094. </li>
  5095. <br><br><b>Class SWITCH_ALL</b>
  5096. <li>swaIncludeNone<br>
  5097. the device does not react to swaOn and swaOff commands</li>
  5098. <li>swaIncludeOff<br>
  5099. the device reacts to the swaOff command
  5100. but does not react to the swaOn command</li>
  5101. <li>swaIncludeOn<br>
  5102. the device reacts to the swaOn command
  5103. but does not react to the swaOff command</li>
  5104. <li>swaIncludeOnOff<br>
  5105. the device reacts to the swaOn and swaOff commands</li>
  5106. <li>swaOn<br>
  5107. sends the all on command to the device</li>
  5108. <li>swaOff<br>
  5109. sends the all off command to the device.</li>
  5110. <br><br><b>Class SWITCH_BINARY</b>
  5111. <li>on<br>
  5112. switch the device on</li>
  5113. <li>off<br>
  5114. switch the device off</li>
  5115. <li>on-for-timer seconds<br>
  5116. off-for-timer seconds<br>
  5117. For version 2 of this class the ZWave implementation of this command is
  5118. used, else the SetExtensions emulation with a delayed on/off. The native
  5119. implementation has a second resolution for up to 127 seconds, and a minute
  5120. resolution up to 7620 seconds (i.e. 127 minutes). The specified value will
  5121. be rounded to the required resolution and/or limited to the maximum allowed
  5122. value. If the value has to be changed, then this will be logged in the
  5123. logfile.<br>
  5124. If the SetExtensions emulation without such limitations is preferred, then
  5125. the vclasses attribute should be modified.
  5126. </li>
  5127. <br><br><b>Class SWITCH_MULTILEVEL</b>
  5128. <li>on, off<br>
  5129. the same as for SWITCH_BINARY.</li>
  5130. <li>dim &lt;value&gt;<br>
  5131. dim/jump to the requested &lt;value&gt; (0..99)<br>
  5132. Note: ZWave defines the range for &lt;value&gt; to 0..99, with 99
  5133. representing "on".</li>
  5134. <li>dimUpDown (UP|DOWN) (IGNORE|USE) &lt;startlevel&gt;<br>
  5135. starts dimming up or down, starting at a &lt;startlevel&gt; if USE
  5136. is specified. If IGNORE is specified, the startlevel will be
  5137. ignored.<br>
  5138. Note: All keywords must be specified, even in the case of IGNORE.
  5139. The device SHOULD respect the &lt;startlevel&gt;, however,
  5140. most devices will ignore the specified startlevel.</li>
  5141. <li>dimUpDownIncDecWithDuration (UP|DOWN|NOMOTION) (IGNORE|USE)
  5142. &lt;startlevel&gt; &lt;duration&gt; (INC|DEC|NOINCDEC) &lt;stepsize&gt;<br>
  5143. similar to dimUpDownWithDuration, adding support for secondary switch types
  5144. (e.g. shutter and blind control) (class version V3).<br>
  5145. The dimming/movement of the primary switch type can be inhibited by
  5146. specifying NOMOTION, so that only the secondary switch type is used. The
  5147. secondary switch type is controlled by the keywords INC, DEC and NOINCDEC,
  5148. where NOINCDEC will inhibit the dimming/movement of the secondary switch.
  5149. If NOINCDEC is used, the &lt;stepsize&gt; must be specified as 0.<br>
  5150. &lt;stepsize&gt; can be 0..99 or 255.<br>
  5151. Note: &lt;stepsize&gt; will be interpreted by the device, most likely to
  5152. represent a time or position, e.g. to turn blinds.<br>
  5153. &lt;duration&gt; can be 0..7620 seconds (127 minutes). Up to a duration of
  5154. 127 seconds, the (internal) resolution is 1 second, for longer durations
  5155. the resolution is 60 seconds.<br>
  5156. Note: A device SHOULD respect the given duration, however, most devices
  5157. will use their internal default duration and ignore the specified duration.
  5158. </li>
  5159. <li>dimUpDownWithDuration (up|down) (ignore|use) &lt;startlevel&gt;
  5160. &lt;duration&gt;<br>
  5161. similar to dimUpDown, adding a &lt;duration&gt; time for the
  5162. transition (V2)<br>
  5163. &lt;duration&gt; can be 0..7620 seconds (127 minutes). Up to a duration of
  5164. 127 seconds, the (internal) resolution is 1 second, for longer durations
  5165. the resolution is 60 seconds.<br>
  5166. Note: A device SHOULD respect the given duration, however, most devices
  5167. will use their internal default duration and ignore the specified duration.
  5168. </li>
  5169. <li>dimWithDuration &lt;value&gt; &lt;duration&gt;<br>
  5170. dim to the requested &lt;value&gt; (0..99) in &lt;duration&gt; seconds.
  5171. (V2) <br>
  5172. &lt;duration&gt; can be 0..7620 seconds (127 minutes). Up to a duration of
  5173. 127 seconds, the (internal) resolution is 1 second, for longer durations
  5174. the resolution is 60 seconds.<br>
  5175. Note: A device SHOULD respect the given duration, however, most devices
  5176. will use their internal default duration and ignore the specified duration.
  5177. </li>
  5178. <li>stop<br>
  5179. stop dimming/operation</li>
  5180. <br><br><b>Class THERMOSTAT_FAN_MODE</b>
  5181. <li>fanAutoLow</li>
  5182. <li>fanLow</li>
  5183. <li>fanAutoMedium</li>
  5184. <li>fanMedium</li>
  5185. <li>fanAutoHigh</li>
  5186. <li>fanHigh<br>
  5187. set the fan mode.</li>
  5188. <br><br><b>Class THERMOSTAT_MODE</b>
  5189. <li>tmOff</li>
  5190. <li>tmHeating</li>
  5191. <li>tmCooling</li>
  5192. <li>tmAuto</li>
  5193. <li>tmFan</li>
  5194. <li>V2:</li>
  5195. <li>tmEnergySaveHeating</li>
  5196. <li>V3:</li>
  5197. <li>tmFullPower</li>
  5198. <li>tmManual<br>
  5199. set the thermostat mode.</li>
  5200. <br><br><b>Class THERMOSTAT_SETPOINT</b>
  5201. <li>setpointHeating value<br>
  5202. set the thermostat to heat to the given value.
  5203. The value is an integer and read as celsius.<br>
  5204. See thermostatSetpointSet for a more enhanced method.
  5205. </li>
  5206. <li>setpointCooling value<br>
  5207. set the thermostat to cool down to the given value.
  5208. The value is an integer and read as celsius.<br>
  5209. See thermostatSetpointSet for a more enhanced method.
  5210. </li>
  5211. <li>thermostatSetpointSet TEMP [SCALE [TYPE [PREC [SIZE]]]]<br>
  5212. set the setpoint of the thermostat to the given value.<br>
  5213. <ul>
  5214. TEMP: setpoint temperature value, by default the value is used
  5215. with 1 decimal, see PREC<br>
  5216. SCALE: (optional) scale of temperature; [cC]=celsius,
  5217. [fF]=fahrenheit, defaults to celsius<br>
  5218. TYPE: (optional) setpoint type; [1, 15], defaults to 1=heating<br>
  5219. <ul>
  5220. 1=heating,
  5221. 2=cooling,
  5222. 7=furnance,
  5223. 8=dryAir,
  5224. 9=moistAir,
  5225. 10=autoChangeover,
  5226. 11=energySaveHeating,
  5227. 12=energySaveCooling,
  5228. 13=awayHeating,
  5229. 14=awayCooling,
  5230. 15=fullPower
  5231. </ul>
  5232. PREC: (optional) number of decimals to be used, [1-7], defaults
  5233. to 1<br>
  5234. SIZE: (optional) number of bytes used, [1, 2, 4], defaults to 2<br>
  5235. Note: optional parameters can be ommitted and are used with there
  5236. default values. If you need or want to specify an optional
  5237. parameter, ALL parameters in front of this parameter need
  5238. to be also specified!<br>
  5239. Note: the number of decimals (defined by PREC) and the number of
  5240. bytes (defined by SIZE) used for the setpoint influence the usable
  5241. range for the temperature. Some device do not support all possible
  5242. values/combinations for PREC/SIZE.<br>
  5243. <ul>
  5244. 1 byte: 0 decimals [-128, 127], 1 decimal [-12.8, 12.7], ...<br>
  5245. 2 byte: 0 decimals [-32768, 32767], 1 decimal [-3276.8, 3276.7],
  5246. ...<br>
  5247. 4 byte: 0 decimals [-2147483648, 2147483647], ...<br>
  5248. </ul>
  5249. </ul>
  5250. </li>
  5251. <li>desired-temp value<br>
  5252. same as thermostatSetpoint, used to make life easier for helper-modules
  5253. </li>
  5254. <br><br><b>Class TIME, V2</b>
  5255. <li>timeOffset TZO DST_Offset DST_START DST_END<br>
  5256. Set the time offset for the internal clock of the device.<br>
  5257. TZO: Offset of time zone to UTC in format [+|-]hh:mm.<br>
  5258. DST_OFFSET: Offset for daylight saving time (DST) in minutes
  5259. in the format [+|-]mm.<br>
  5260. DST_START / DST_END: Start and end of daylight saving time in the
  5261. format MM-DD_HH:00.<br>
  5262. Note: Sign for both offsets must be specified!<br>
  5263. Note: Minutes for DST_START and DST_END must be specified as "00"!
  5264. </li>
  5265. <br><br><b>Class TIME_PARAMETERS, V1</b>
  5266. <li>timeParametersGet<br>
  5267. The device request time parameters. Right now the user should define a
  5268. notify with a "set timeParameters" command.
  5269. </li>
  5270. <li>timeParameters DATE TIME<br>
  5271. Set the time (UTC) to the internal clock of the device.<br>
  5272. DATE: Date in format YYYY-MM-DD.<br>
  5273. TIME: Time (UTC) in the format hh:mm:ss.<br>
  5274. Note: Time zone offset to UTC must be set with command class TIME.
  5275. </li>
  5276. <br><br><b>Class USER_CODE</b>
  5277. <li>userCode id status code</br>
  5278. set code and status for the id n. n ist starting at 1, status is 0 for
  5279. available (deleted) and 1 for set (occupied). code is a hexadecimal string.
  5280. </li>
  5281. <br><br><b>Class WAKE_UP</b>
  5282. <li>wakeupInterval value nodeId<br>
  5283. Set the wakeup interval of battery operated devices to the given value in
  5284. seconds. Upon wakeup the device sends a wakeup notification to nodeId.</li>
  5285. <li>wakeupNoMoreInformation<br>
  5286. put a battery driven device into sleep mode. </li>
  5287. </ul>
  5288. <br>
  5289. <a name="ZWaveget"></a>
  5290. <b>Get</b>
  5291. <ul>
  5292. <br><br><b>All</b>
  5293. <li>neighborList<br>
  5294. returns the list of neighbors. Provides insights to actual network
  5295. topology. List includes dead links and non-routing neighbors.
  5296. Since this information is stored in the dongle, the information will be
  5297. returned directly even for WAKE_UP devices.</li>
  5298. <br><br><b>Class ALARM</b>
  5299. <li>alarm &lt;alarmId&gt;<br>
  5300. return the value for the (decimal) alarmId. The value is device
  5301. specific. This command is specified in version 1 and should only
  5302. be used with old devices that only support version 1.</li>
  5303. <li>alarmWithType &lt;alarmType&gt;<br>
  5304. return the event for the specified alarm type. This command is
  5305. specified in version 2.
  5306. </li>
  5307. <li>alarmWithTypeEvent &lt;alarmType&gt; &lt;eventnumber&gt;<br>
  5308. return the event details for the specified alarm type and
  5309. eventnumber. This command is specified in version 3. The eventnumber
  5310. is specific for each alarm type, a list of the supported
  5311. eventnumbers can be obtained by the "alarmEventSupported" command,
  5312. refer also to the documentation of the device.
  5313. </li>
  5314. <li>alarmTypeSupported<br>
  5315. Returns a list of the supported alarm types of the device which are
  5316. used as parameters in the "alarmWithType" and "alarmWithTypeEvent"
  5317. commands. This command is specified in version 2.</li>
  5318. <li>alarmEventSupported &lt;alarmType&gt;<br>
  5319. Returns a list of the supported events for the specified alarm type.
  5320. The number of the events can be used as parameter in the
  5321. "alarmWithTypeEvent" command. This command is specified in
  5322. version 3.</li>
  5323. <br><br><b>Class ASSOCIATION</b>
  5324. <li>association groupId<br>
  5325. return the list of nodeIds in the association group groupId in the form:<br>
  5326. assocGroup_X:Max Y, Nodes id,id...
  5327. </li>
  5328. <li>associationGroups<br>
  5329. return the number of association groups<br>
  5330. </li>
  5331. <li>associationAll<br>
  5332. request association info for all possible groups.</li>
  5333. <br><br><b>Class ASSOCIATION_GRP_INFO</b>
  5334. <li>associationGroupName groupId<br>
  5335. return the name of association groups
  5336. </li>
  5337. <li>associationGroupCmdList groupId<br>
  5338. return Command Classes and Commands that will be sent to associated
  5339. devices in this group<br>
  5340. </li>
  5341. <br><br><b>Class BARRIER_OPERATOR</b>
  5342. <li>barrierState<br>
  5343. request state of the barrier.
  5344. </li>
  5345. <br><br><b>Class BASIC</b>
  5346. <li>basicStatus<br>
  5347. return the status of the node as basicReport:XY. The value (XY) depends on
  5348. the node, e.g. a SWITCH_BINARY device reports 00 for off and FF (255) for on.
  5349. Devices with version 2 (or greater) can return two additional values, the
  5350. 'target value' and 'duration'. The 'duration' is reported in seconds,
  5351. as "unknown duration" (value 0xFE = 253) or as "255 (reserved value)"
  5352. (value 0xFF = 255).
  5353. </li>
  5354. <br><br><b>Class BATTERY</b>
  5355. <li>battery<br>
  5356. return the state and charge of the battery, see below the events
  5357. </li>
  5358. <br><br><b>CLASS DOOR_LOCK_LOGGING, V1 (deprecated)</b>
  5359. <li>doorLockLoggingRecordsSupported<br>
  5360. Gives back the number of records that can be stored by the device.
  5361. </li>
  5362. <li>doorLockLoggingRecord n<br>
  5363. Requests and reports the logging record number n.<br>
  5364. You will get a reading with the requested record number, the record status,
  5365. the event type, user identifier, user's code length and the timestamp of
  5366. the event in the form of yyyy-mm-dd hh:mm:ss. Although the request does
  5367. report the user code, the user typed in, it is dropped for security
  5368. reasons, so it does not get logged in clear text.<br>
  5369. If the report could not get parsed correctly, it does report the raw
  5370. message.<br>
  5371. The event types can be looked up in the "Software Design Specification -
  5372. Z-Wave Application Command Class Specification" at page 150 from SIGMA
  5373. DESIGNS in the version of 2017-07-10.</li>
  5374. <br><br><b>Class CLIMATE_CONTROL_SCHEDULE</b>
  5375. <li>ccsOverride<br>
  5376. request the climate control schedule override report
  5377. </li>
  5378. <li>ccs [mon|tue|wed|thu|fri|sat|sun]<br>
  5379. request the climate control schedule for the given day.
  5380. </li>
  5381. <li>ccsAll<br>
  5382. request the climate control schedule for all days. (runs in background)
  5383. </li>
  5384. <br><br><b>Class CLOCK</b>
  5385. <li>clock<br>
  5386. request the clock data
  5387. </li>
  5388. <br><br><b>Class COLOR_CONTROL</b>
  5389. <li>ccCapability<br>
  5390. return capabilities.</li>
  5391. <li>ccStatus channelId<br>
  5392. return status of channel ChannelId.
  5393. </li>
  5394. <br><br><b>Class CONFIGURATION</b>
  5395. <li>config cfgAddress<br>
  5396. return the value of the configuration parameter cfgAddress. The value is
  5397. device specific.<br>
  5398. Note: if the model is set (see MANUFACTURER_SPECIFIC get), then more
  5399. specific config commands are available.
  5400. </li>
  5401. <li>configAll<br>
  5402. If the model of a device is set, and configuration descriptions are
  5403. available from the database for this device, then request the value of all
  5404. known configuration parameters.</li>
  5405. <br><br><b>Class DOOR_LOCK, V2</b>
  5406. <li>doorLockConfiguration<br>
  5407. Request the configuration report from the door lock.
  5408. </li>
  5409. <li>doorLockOperation<br>
  5410. Request the operconfiguration report from the door lock.
  5411. </li>
  5412. <br><br><b>Class HRV_STATUS</b>
  5413. <li>hrvStatus<br>
  5414. report the current status (temperature, etc.)
  5415. </li>
  5416. <li>hrvStatusSupported<br>
  5417. report the supported status fields as a bitfield.
  5418. </li>
  5419. <br><br><b>Class INDICATOR</b>
  5420. <li>indicatorStatus<br>
  5421. return the indicator status of the node, as indState:on, indState:off or
  5422. indState:dim value.
  5423. </li>
  5424. <br><br><b>Class MANUFACTURER_PROPRIETARY</b>
  5425. <li>position<br>
  5426. Fibaro FGRM-222 only: return the blinds position and slat angle.
  5427. </li>
  5428. <br><br><b>Class MANUFACTURER_SPECIFIC</b>
  5429. <li>model<br>
  5430. return the manufacturer specific id (16bit),
  5431. the product type (16bit)
  5432. and the product specific id (16bit).<br>
  5433. Note: if the openzwave xml files are installed, then return the name of the
  5434. manufacturer and of the product. This call is also necessary to decode more
  5435. model specific configuration commands and parameters.
  5436. </li>
  5437. <br><br><b>Class METER</b>
  5438. <li>meter scale<br>
  5439. return the meter report for the requested scale.<br>
  5440. Note: protocol V1 does not support the scale parameter, the parameter
  5441. will be ignored and the default scale will be returned.<br>
  5442. For protocol V2 and higher, scale is supported and depends on the
  5443. type of the meter (energy, gas or water).<br>
  5444. The device may not support all scales, see the meterSupported
  5445. command and its output. If the scale parameter is omitted, the
  5446. default unit will be reported.<br>
  5447. Example: For an electric meter, meter 0 will report energy in kWh,
  5448. meter 2 will report power in W and meter 6 will report current in A
  5449. (if these scales are supported).<br>
  5450. </li>
  5451. <li>meterSupported<br>
  5452. request the type of the meter, the supported scales and the
  5453. capability to reset the accumulated value.<br>
  5454. Note: The output contains the decimal numbers of the supported
  5455. scales that can be used as parameter for the meter command.
  5456. </li>
  5457. <br><br><b>Class MULTI_CHANNEL</b>
  5458. <li>mcEndpoints<br>
  5459. return the list of endpoints available, e.g.:<br>
  5460. mcEndpoints: total 2, identical
  5461. </li>
  5462. <li>mcCapability chid<br>
  5463. return the classes supported by the endpoint/channel chid. If the channel
  5464. does not exist, create a FHEM node for it. Example:<br>
  5465. mcCapability_02:SWITCH_BINARY<br>
  5466. <b>Note:</b> This is the best way to create the secondary nodes of a
  5467. MULTI_CHANNEL device. The device is only created for channel 2 or greater.
  5468. </li>
  5469. <br><br><b>Class MULTI_CHANNEL_ASSOCIATION</b>
  5470. <li>mca groupid<br>
  5471. return the associations for the groupid. for the syntax of the returned
  5472. data see the mcaAdd command above.</li>
  5473. <li>mcaAll<br>
  5474. request association info for all possible groupids.
  5475. </li>
  5476. <br><br><b>Class NETWORK_SCHEDULE (SCHEDULE), V1</b>
  5477. <li>scheduleSupported<br>
  5478. Request the supported features, e.g. number of supported schedules.
  5479. Due to the lack of documentation, details for some fields in the
  5480. report are not available.</li>
  5481. <li>schedule ID<br>
  5482. Request the details for the schedule with the id ID. Due to the
  5483. lack of documentation, details for some fields in the report are
  5484. not available.</li>
  5485. <li>scheduleState<br>
  5486. Request the details for the schedule state. Due to the lack of
  5487. documentation, details for some fields in the report are not
  5488. available.</li>
  5489. <br><br><b>Class NODE_NAMING</b>
  5490. <li>name<br>
  5491. Get the name from the EEPROM. Note: only ASCII is supported.</li>
  5492. <li>location<br>
  5493. Get the location from the EEPROM. Note: only ASCII is supported.</li>
  5494. <br><br><b>Class POWERLEVEL</b>
  5495. <li>powerlevel<br>
  5496. Get the current powerlevel and remaining time in this level.</li>
  5497. <li>powerlevelTest<br>
  5498. Get the result of last powerlevelTest.</li>
  5499. <br><br><b>Class PROTECTION</b>
  5500. <li>protection<br>
  5501. returns the protection state. It can be on, off or seq.</li>
  5502. <br><br><b>Class SCENE_ACTUATOR_CONF</b>
  5503. <li>sceneConfig<br>
  5504. returns the settings for a given scene. Parameter is sceneId
  5505. </li>
  5506. <br><br><b>Class SCENE_CONTROLLER_CONF</b>
  5507. <li>groupConfig<br>
  5508. returns the settings for a given group. Parameter is groupId
  5509. </li>
  5510. <br><br><b>Class SCHEDULE_ENTRY_LOCK, V1, V2, V3</b>
  5511. <li>scheduleEntryLockTypeSupported<br>
  5512. returns the number of available slots for week day and year day
  5513. schedules (V1), in V3 the number of available slots for the daily
  5514. repeating schedule is reported additionally
  5515. </li>
  5516. <li>scheduleEntryLockWeekDay USER_ID SCHEDULE_ID<br>
  5517. returns the specified week day schedule for the specified user
  5518. (day of week, start time, end time) (V1)<br>
  5519. <ul>
  5520. USER_ID: id of user, starting from 1 up to the number of supported
  5521. users, refer also to the USER_CODE class description.<br>
  5522. SCHEDULE_ID: schedule slot id (from 1 up to number of supported
  5523. schedule slots)<br>
  5524. </ul>
  5525. </li>
  5526. <li>scheduleEntryLockYearDay USER_ID SCHEDULE_ID<br>
  5527. returns the specified year day schedule for the specified user
  5528. (start date, start time, end date, end time) (V1)<br>
  5529. <ul>
  5530. USER_ID: id of user, starting from 1 up to the number of supported
  5531. users, refer also to the USER_CODE class description.<br>
  5532. SCHEDULE_ID: schedule slot id (from 1 up to number of supported
  5533. schedule slots)<br>
  5534. </ul>
  5535. </li>
  5536. <li>scheduleEntryLockDailyRepeating USER_ID SCHEDULE_ID<br>
  5537. returns the specified daily schedule for the specified user
  5538. (weekdays, start date, duration) (V3)<br>
  5539. <ul>
  5540. USER_ID: id of user, starting from 1 up to the number of supported
  5541. users, refer also to the USER_CODE class description.<br>
  5542. SCHEDULE_ID: schedule slot id (from 1 up to number of supported
  5543. schedule slots)<br>
  5544. </ul>
  5545. </li>
  5546. <li>scheduleEntryLockTimeOffset<br>
  5547. returns the time zone offset TZO and the daylight saving time
  5548. offset (V2)
  5549. </li>
  5550. <br><br><b>Class SECURITY</b>
  5551. <li>secSupportedReport<br>
  5552. (internaly used to) request the command classes that are supported
  5553. with SECURITY
  5554. </li>
  5555. <li>Notes:<br>
  5556. This class needs the installation of the perl module Crypt::Rijndael and
  5557. a defined networkkey in the attributes of the ZWDongle device<br>
  5558. Currently a secure inclusion can only be started from the command input
  5559. with "set &lt;ZWDongle_device_name&gt; addNode [onSec|onNwSec]"<br>
  5560. These commands are only described here for completeness of the
  5561. documentation, but are not intended for manual usage. These commands
  5562. will be removed from the interface in future version.</li>
  5563. <br><br><b>Class SENSOR_ALARM</b>
  5564. <li>alarm alarmType<br>
  5565. return the nodes alarm status of the requested alarmType. 00 = GENERIC,
  5566. 01 = SMOKE, 02 = CO, 03 = CO2, 04 = HEAT, 05 = WATER, 255 = returns the
  5567. nodes first supported alarm type.
  5568. </li>
  5569. <br><br><b>Class SENSOR_BINARY</b>
  5570. <li>sbStatus<br>
  5571. return the status of the node.
  5572. </li>
  5573. <br><br><b>Class SENSOR_MULTILEVEL</b>
  5574. <li>smStatus<br>
  5575. request data from the node (temperature/humidity/etc)
  5576. </li>
  5577. <br><br><b>Class SWITCH_ALL</b>
  5578. <li>swaInclude<br>
  5579. return the switch-all mode of the node.
  5580. </li>
  5581. <br><br><b>Class SWITCH_BINARY</b>
  5582. <li>swbStatus<br>
  5583. return the status of the node, as state:on or state:off.
  5584. </li>
  5585. <br><br><b>Class SWITCH_MULTILEVEL</b>
  5586. <li>swmStatus<br>
  5587. return the status of the node, as state:on, state:off or state:dim value.
  5588. </li>
  5589. <li>swmSupported<br>
  5590. return the supported switch types (class version 3)
  5591. </li>
  5592. <br><br><b>Class THERMOSTAT_FAN_MODE</b>
  5593. <li>fanMode<br>
  5594. request the mode
  5595. </li>
  5596. <br><br><b>Class THERMOSTAT_FAN_STATE</b>
  5597. <li>fanMode<br>
  5598. request the state
  5599. </li>
  5600. <br><br><b>Class THERMOSTAT_MODE</b>
  5601. <li>thermostatMode<br>
  5602. request the mode
  5603. </li>
  5604. <br><br><b>Class THERMOSTAT_OPERATING_STATE</b>
  5605. <li>thermostatOperatingState<br>
  5606. request the operating state
  5607. </li>
  5608. <br><br><b>Class THERMOSTAT_SETPOINT</b>
  5609. <li>setpoint [TYPE]<br>
  5610. request the setpoint<br>
  5611. TYPE: (optional) setpoint type; [1, 15], defaults to 1=heating<br>
  5612. <ul>
  5613. 1=heating,
  5614. 2=cooling,
  5615. 7=furnance,
  5616. 8=dryAir,
  5617. 9=moistAir,
  5618. 10=autoChangeover,
  5619. 11=energySaveHeating,
  5620. 12=energySaveCooling,
  5621. 13=awayHeating,
  5622. 14=awayCooling,
  5623. 15=fullPower
  5624. </ul>
  5625. </li>
  5626. <li>thermostatSetpointSupported<br>
  5627. requests the list of supported setpoint types
  5628. </li>
  5629. <br><br><b>Class TIME, V2</b>
  5630. <li>time<br>
  5631. Request the (local) time from the internal clock of the device.
  5632. </li>
  5633. <li>date<br>
  5634. Request the (local) date from the internal clock of the device.
  5635. </li>
  5636. <li>timeOffset<br>
  5637. Request the report for the time offset and DST settings from the
  5638. internal clock of the device.
  5639. </li>
  5640. <br><br><b>Class TIME_PARAMETERS, V1</b>
  5641. <li>time<br>
  5642. Request the date and time (UTC) from the internal clock of the device.
  5643. </li>
  5644. <br><br><b>Class USER_CODE</b>
  5645. <li>userCode n</br>
  5646. request status and code for the id n
  5647. </li>
  5648. <br><br><b>Class VERSION</b>
  5649. <li>version<br>
  5650. return the version information of this node in the form:<br>
  5651. Lib A Prot x.y App a.b
  5652. </li>
  5653. <li>versionClass classId or className<br>
  5654. return the supported command version for the requested class
  5655. </li>
  5656. <li>versionClassAll<br>
  5657. executes "get devicename versionClass class" for each class from the
  5658. classes attribute in the background without generating events, and sets the
  5659. vclasses attribute at the end.
  5660. </li>
  5661. <br><br><b>Class WAKE_UP</b>
  5662. <li>wakeupInterval<br>
  5663. return the wakeup interval in seconds, in the form<br>
  5664. wakeupReport:interval seconds target id
  5665. </li>
  5666. <li>wakeupIntervalCapabilities (V2 only)<br>
  5667. return the wake up interval capabilities in seconds, in the form<br>
  5668. wakeupIntervalCapabilitiesReport:min seconds max seconds default seconds
  5669. step seconds
  5670. </li>
  5671. <br><br><b>Class ZWAVEPLUS_INFO</b>
  5672. <li>zwavePlusInfo<br>
  5673. request the zwavePlusInfo
  5674. </li>
  5675. </ul>
  5676. <br>
  5677. <a name="ZWaveattr"></a>
  5678. <b>Attributes</b>
  5679. <ul>
  5680. <li><a href="#IODev">IODev</a></li>
  5681. <li><a name="WNMI_delay">WNMI_delay</a><br>
  5682. This attribute sets the time delay between the last message sent to an
  5683. WakeUp device and the sending of the WNMI Message
  5684. (WakeUpNoMoreInformation) that will set the device to sleep mode. Value
  5685. is in seconds, subseconds my be specified. Values outside of 0.2-5.0 are
  5686. probably harmful.
  5687. </li>
  5688. <li><a name="classes">classes</a><br>
  5689. This attribute is needed by the ZWave module, as the list of the possible
  5690. set/get commands depends on it. It contains a space separated list of
  5691. class names (capital letters).
  5692. </li>
  5693. <li><a href="#disable">disable</a></li>
  5694. <li><a href="#disabledForIntervals">disabledForIntervals</a></li>
  5695. <li><a href="#dummy">dummy</a></li>
  5696. <li><a href="#do_not_notify">do_not_notify</a></li>
  5697. <li><a href="#dummy">dummy</a></li>
  5698. <li><a name="eventForRaw">eventForRaw</a><br>
  5699. Generate an an additional event for the RAW message. Can be used if
  5700. someone fears that critical notifies won't work, if FHEM changes the event
  5701. text after an update. </li>
  5702. <li><a name="extendedAlarmReadings">extendedAlarmReadings</a><br>
  5703. Some devices support more than one alarm type, this attribute
  5704. selects which type of reading is used for the reports of the ALARM
  5705. (or NOTIFICATION) class:<br>
  5706. A value of "0" selects a combined, single reading ("alarm") for
  5707. all alarm types of the device. Subsequent reports of different
  5708. alarm types will overwrite each other. This is the default setting
  5709. and the former behavior.<br>
  5710. A value of "1" selects separate alarm readings for each alarm type
  5711. of the device. The readings are named "alarm_&lt;alarmtype&gt;.
  5712. This can also be selected if only one alarmtype is supported by
  5713. the device. This reading also contains the status of the
  5714. alarm notification. For compatibility reasons this is currently
  5715. not supported with the combined reading.<br>
  5716. A value of "2" selects both of the above and creates the combined and
  5717. the seperate readings at the same time, this should only be used
  5718. if really needed as duplicate events are generated.
  5719. </li>
  5720. <li><a href="#ignore">ignore</a></li>
  5721. <li><a name="ignoreDupMsg">ignoreDupMsg</a><br>
  5722. Experimental: if set (to 1), ignore duplicate wakeup messages, or
  5723. multiple responses to a single get due to missing lowlevel ACK.
  5724. </li>
  5725. <li><a href="#neighborListPos">neighborListPos</a></li>
  5726. <li><a name="noExplorerFrames">noExplorerFrames</a><br>
  5727. turn off the use of Explorer Frames
  5728. </li>
  5729. <li><a name="noWakeupForApplicationUpdate">noWakeupForApplicationUpdate</a>
  5730. <br>
  5731. some devices (notable the Aeotec Multisensor 6) are only awake after an
  5732. APPLICATION UPDATE telegram for a very short time. If this attribute is
  5733. set (recommended for the Aeotec Multisensor 6), the WakeUp-Stack is not
  5734. processed after receiving such a message.
  5735. </li>
  5736. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  5737. <li><a name="secure_classes">secure_classes</a><br>
  5738. This attribute is the result of the "set DEVICE secSupportedReport"
  5739. command. It contains a space seperated list of the the command classes
  5740. that are supported with SECURITY.
  5741. </li>
  5742. <li><a href="#showtime">showtime</a></li>
  5743. <li><a name="vclasses">vclasses</a><br>
  5744. This is the result of the "get DEVICE versionClassAll" command, and
  5745. contains the version information for each of the supported classes.
  5746. </li>
  5747. <li><a name="useCRC16">useCRC16</a><br>
  5748. Experimental: if a device supports CRC_16_ENCAP, then add CRC16 to the
  5749. command. Note: this is not available to SECURITY ENABLED devices, as
  5750. security has its own CRC.
  5751. </li>
  5752. <li><a name="useMultiCmd">useMultiCmd</a><br>
  5753. Experimental: if a device supports MULTI_CMD and WAKE_UP, then pack
  5754. multiple get messages on the SendStack into a single MULTI_CMD to save
  5755. radio transmissions.
  5756. </li>
  5757. <li><a name="zwaveRoute">zwaveRoute</a><br>
  5758. space separated list of (ZWave) device names. They will be used in the
  5759. given order to route messages from the controller to this device. Specify
  5760. them in the order from the controller to the device. Do not specify the
  5761. controller and the device itself, only the routers inbetween. Used only
  5762. if the IODev is a ZWCUL device. </li>
  5763. </ul>
  5764. <br>
  5765. <a name="ZWaveevents"></a>
  5766. <b>Generated events:</b>
  5767. <ul>
  5768. <br><b>neighborUpdate</b>
  5769. <li>ZW_REQUEST_NODE_NEIGHBOR_UPDATE [started|done|failed]</li>
  5770. <br><b>returnRouteAdd</b>
  5771. <li>ZW_ASSIGN_RETURN_ROUTE [started|alreadyActive|transmitOk|
  5772. transmitNoAck|transmitFail|transmitNotIdle|
  5773. transmitNoRoute]</li>
  5774. <br><b>returnRouteDel</b>
  5775. <li>ZW_DELETE_RETURN_ROUTE [started|alreadyActive|transmitOk|
  5776. transmitNoAck|transmitFail|transmitNotIdle|
  5777. transmitNoRoute]</li>
  5778. <br><b>sucRouteAdd</b>
  5779. <li>ZW_ASSIGN_SUC_RETURN_ROUTE [started|alreadyActive|transmitOk|
  5780. transmitNoAck|transmitFail|transmitNotIdle|
  5781. transmitNoRoute]</li>
  5782. <br><b>sucRouteDel</b>
  5783. <li>ZW_DELETE_SUC_RETURN_ROUTE [started|alreadyActive|transmitOk|
  5784. transmitNoAck|transmitFail|transmitNotIdle|
  5785. transmitNoRoute]</li>
  5786. <br><b>Class ALARM</b>
  5787. <li>Note:<br>
  5788. Depending on the setting of the attribute "extendedAlarmReadings"
  5789. the generated events differ slightly. With a value of "0" or "2" a
  5790. combined reading for all alarm types of the device with the name
  5791. "alarm" will be used. With a value of "1" or "2" separate readings
  5792. for each supported alarm type will be generated with names
  5793. "alarm_&lt;alarmType&gt;.</li>
  5794. <li>Devices with class version 1 support: alarm_type_X:level Y</li>
  5795. <li>For higher class versions more detailed events with 100+ different
  5796. strings in the form alarm:&lt;string&gt;
  5797. (or alarm_&lt;alarmType&gt;:&lt;string&gt;) are generated.<br>
  5798. For the combined reading, the name of the alarm type is part of
  5799. the reading event, for separate readings it is part of the
  5800. reading name.<br>
  5801. If a cleared event can be identified, the string "Event cleared:"
  5802. is reported before the event details.<br>
  5803. The seperate readings also contain the status of the
  5804. alarm / notification. For compatibility reasons this is currently
  5805. not supported with the combined reading. </li>
  5806. <br><b>Class APPLICATION_STATUS</b>
  5807. <li>applicationStatus: [cmdRejected]</li>
  5808. <li>applicationBusy: [tryAgainLater|tryAgainInWaitTimeSeconds|
  5809. RequestQueued|unknownStatusCode] $waitTime</li>
  5810. <br><br><b>Class ASSOCIATION</b>
  5811. <li>assocGroup_X:Max Y Nodes A,B,...</li>
  5812. <li>assocGroups:X</li>
  5813. <br><br><b>Class ASSOCIATION_GRP_INFO</b>
  5814. <li>assocGroupName_X:name</li>
  5815. <li>assocGroupCmdList_X:Class1:Cmd1 Class2:Cmd ...</li>
  5816. <br><br><b>Class BARRIER_OPERATOR</b>
  5817. <li>barrierState:[ closed | [%] | closing | stopped | opening | open ]</li>
  5818. <br><br><b>Class BASIC</b>
  5819. <li>basicReport:X (for class version 1)<br>
  5820. basicReport:X target y duration z (for class version 2 or greater)</li>
  5821. <li>basicGet:request</li>
  5822. <li>basicSet:X</li>
  5823. <br><br><b>Class BASIC_WINDOW_COVERING</b>
  5824. <li>covering:[open|close|stop]</li>
  5825. <br><br><b>Class BATTERY</b>
  5826. <li>battery:{low|chargelevel %}</li>
  5827. <li>batteryState:{ok|low}</li>
  5828. <li>batteryPercent:&lt;value&gt;</li>
  5829. <br><br><b>Class CENTRAL_SCENE</b>
  5830. <li>cSceneSet:X</li>
  5831. <li>cSceneDim:X</li>
  5832. <li>cSceneDimEnd:X</li>
  5833. <li>cSceneDouble:X</li>
  5834. <li>cSceneMultiple_N:X<br>where N is 3, 4 or 5 (multiple presses)</li>
  5835. <br><br><b>Class CLIMATE_CONTROL_SCHEDULE</b>
  5836. <li>ccsOverride:[no|temporary|permanent],
  5837. [frost protection|energy saving|unused]</li>
  5838. <li>ccsChanged:&lt;number&gt;</li>
  5839. <li>ccs_[mon|tue|wed|thu|fri|sat|sun]:HH:MM temp HH:MM temp...</li>
  5840. <br><br><b>Class CLOCK</b>
  5841. <li>clock:get</li>
  5842. <li>clock:[mon|tue|wed|thu|fri|sat|sun] HH:MM</li>
  5843. <br><br><b>Class COLOR_CONTROL</b>
  5844. <li>ccCapability:XY</li>
  5845. <li>ccStatus_X:Y</li>
  5846. <br><br><b>Class CONFIGURATION</b>
  5847. <li>config_X:Y<br>
  5848. Note: if the model is set (see MANUFACTURER_SPECIFIC get), then more
  5849. specific config messages are available.</li>
  5850. <br><br><b>Class DEVICE_RESET_LOCALLY</b>
  5851. <li>deviceResetLocally:yes<br></li>
  5852. <br><br><b>Class DOOR_LOCK, V2</b>
  5853. <li>doorLockConfiguration: mode: [constant|timed] outsideHandles:
  5854. $outside_mode(4 bit field) insideHandles: $inside_mode(4 bit field)
  5855. timeoutSeconds: [not_supported|$seconds]</li>
  5856. <li>doorLockOperation: mode: $mode outsideHandles:
  5857. $outside_mode(4 bit field) insideHandles: $inside_mode(4 bit field)
  5858. door: [open|closed] bolt: [locked|unlocked] latch: [open|closed]
  5859. timeoutSeconds: [not_supported|$time]<br>
  5860. $mode = [unsecured|unsecured_withTimeout|unsecured_inside|
  5861. unsecured_inside_withTimeout|unsecured_outside|
  5862. unsecured_outside_withTimeout|secured</li>
  5863. <br><br><b>Class HAIL</b>
  5864. <li>hail:01<br></li>
  5865. <br><br><b>Class HRV_STATUS</b>
  5866. <li>outdoorTemperature: %0.1f C</li>
  5867. <li>supplyAirTemperature: %0.1f C</li>
  5868. <li>exhaustAirTemperature: %0.1f C</li>
  5869. <li>dischargeAirTemperature: %0.1f C</li>
  5870. <li>indoorTemperature: %0.1f C</li>
  5871. <li>indoorHumidity: %s %</li>
  5872. <li>remainingFilterLife: %s %</li>
  5873. <li>supportedStatus: &lt;list of supported stati&gt;</li>
  5874. <br><br><b>Class INDICATOR</b>
  5875. <li>indState:[on|off|dim value]</li>
  5876. <br><br><b>Class MANUFACTURER_PROPRIETARY</b>
  5877. <li>Fibaro FGRM-222 with ReportsType Fibar CC only:</li>
  5878. <li>position:Blind [%] Slat [%]<br>
  5879. (VenetianBlindMode)</li>
  5880. <li>position:[%]<br>
  5881. (RollerBlindMode)</li>
  5882. <br><br><b>Class MANUFACTURER_SPECIFIC</b>
  5883. <li>modelId:hexValue hexValue hexValue</li>
  5884. <li>model:manufacturerName productName</li>
  5885. <li>modelConfig:configLocation</li>
  5886. <br><br><b>Class METER</b>
  5887. <li>energy:val [kWh|kVAh|pulseCount|powerFactor]</li>
  5888. <li>gas:val [m3|feet3|pulseCount]</li>
  5889. <li>water:val [m3|feet3|USgallons|pulseCount]</li>
  5890. <li>power:val W</li>
  5891. <li>voltage:val V</li>
  5892. <li>current:val A</li>
  5893. <li>meterSupported:type:[meter_type] scales:[list of supported scales]
  5894. resetable:[yes|no]</li>
  5895. <br><br><b>Class MULTI_CHANNEL</b>
  5896. <li>endpoints:total X $dynamic $identical</li>
  5897. <li>mcCapability_X:class1 class2 ...</li>
  5898. <br><br><b>Class NETWORK_SCHEDULE (SCHEDULE), V1</b>
  5899. <li>schedule_&lt;id&gt;: ID: $schedule_id userID: $user_id sYear:
  5900. $starting_year sMonth: $starting_month activeID: $active_id
  5901. sDay: $starting_day sWeekDay: $starting_weekday sHour:
  5902. $starting_hour durationType: $duration_type sMinute:
  5903. $starting_minute duration: $duration numReportsToFollow:
  5904. $number_of_reports_to_follow numCmds: $number_of_commands
  5905. cmdLen: $length_of_command cmd: $commandsequence(hex)</li>
  5906. <li>scheduleSupported: num: $number_of_supported_schedules
  5907. startTimeSupport: $start_time_support(6 bit field) fallbackSupport:
  5908. $fallback_support enableDisableSupport: $ena_dis_support
  5909. numCCs: $number_of_supported_command_classes
  5910. overrideTypes: $override_types(7 bit field) overrideSupport:
  5911. $override_support</li>
  5912. <li>scheduleSupportedCC: CC_&lt;x&gt;: $number_of_command_class
  5913. CCname_&lt;x&gt;: $name_of_command_class]CCmask_&lt;x&gt;:
  5914. $mask_for_command(2 bit)</li>
  5915. <br><br><b>Class NODE_NAMING</b>
  5916. <li>name:NAME</li>
  5917. <li>location:LOCATION</li>
  5918. <br><br><b>Class POWERLEVEL</b>
  5919. <li>powerlvl:current x remain y<br>
  5920. NOTE: "current 0 remain 0" means normal mode without timeout</li>
  5921. <li>powerlvlTest:node x status y frameAck z<br>
  5922. NOTE: status 0=failed, 1=success (at least one ACK), 2=in progress</li>
  5923. <br><br><b>Class PROTECTION</b>
  5924. <li>protection:[on|off|seq]</li>
  5925. <br><br><b>Class SCENE_ACTIVATION</b>
  5926. <li>scene_Id:level finalValue</li>
  5927. <br><br><b>Class SCENE_ACTUATOR_CONF</b>
  5928. <li>scene_Id:level dimmingDuration finalValue</li>
  5929. <br><br><b>Class SCENE_CONTROLLER_CONF</b>
  5930. <li>group_Id:scene dimmingDuration</li>
  5931. <br><br><b>Class SCHEDULE_ENTRY_LOCK</b>
  5932. <li>scheduleEntryLockEntryTypeSupported:WeekDaySlots: $value
  5933. YearDaySlots: $value</li>
  5934. <li>weekDaySchedule_$userId:userID: $value slotID: $value $weekday
  5935. $starthour:$startminute $endhour:$endminute</li>
  5936. <li>yearDaySchedule_$userId:userID: $value slotID: $value
  5937. start: $year-$month-$day $hour:$minute
  5938. end: $year-$month-$day $hour:$minute</li>
  5939. <li>scheduleEntryLockDailyRepeating_$userId:userID: $value $weekdays
  5940. $hour:$minute $durationhour:$durationminute<br>
  5941. Note: $weekdays is a concatenated string with weekdaynames
  5942. ("sun","mon","tue","wed","thu","fri","sat") where inactive
  5943. weekdays are represented by "...", e.g. montue...wedfri</li>
  5944. <li>scheduleEntryLockTimeOffset:TZO: $sign$hour:$minute DST:
  5945. $sign$minutes</li>
  5946. <br><br><b>Class SECURITY</b>
  5947. <li>none<br>
  5948. Note: the class security should work transparent to the sytem and is not
  5949. intended to generate events</li>
  5950. <br><br><b>Class SENSOR_ALARM</b>
  5951. <li>alarm_type_X:level Y node $nodeID seconds $seconds</li>
  5952. <br><br><b>Class SENSOR_BINARY</b>
  5953. <li>SENSORY_BINARY V1:</li>
  5954. <li>state:open</li>
  5955. <li>state:closed</li>
  5956. <li>SENSORY_BINARY V2:</li>
  5957. <li>unknown:[off|on]</li>
  5958. <li>generalPurpose:[off|on]</li>
  5959. <li>smoke:[off|on]</li>
  5960. <li>CO:[off|on]</li>
  5961. <li>CO2:[off|on]</li>
  5962. <li>heat:[off|on]</li>
  5963. <li>water:[off|on]</li>
  5964. <li>freeze:[off|on]</li>
  5965. <li>tamper:[off|on]</li>
  5966. <li>aux:[off|on]</li>
  5967. <li>doorWindow:[off|on]</li>
  5968. <li>tilt:[off|on]</li>
  5969. <li>motion:[off|on]</li>
  5970. <li>glassBreak:[off|on]</li>
  5971. <br><br><b>Class SENSOR_MULTILEVEL</b>
  5972. <li>temperature $val [C|F]</li>
  5973. <li>generalPurpose $val %</li>
  5974. <li>luminance $val [%|Lux]</li>
  5975. <li>power $val [W|Btu/h]</li>
  5976. <li>humidity $val %</li>
  5977. <li>velocity $val [m/s|mph]</li>
  5978. <li>direction $val</li>
  5979. <li>atmosphericPressure $val [kPa|inchHg]</li>
  5980. <li>barometricPressure $val [kPa|inchHg]</li>
  5981. <li>solarRadiation $val W/m2</li>
  5982. <li>dewpoint $val [C|F]</li>
  5983. <li>rain $val [mm/h|in/h]</li>
  5984. <li>tideLevel $val [m|feet]</li>
  5985. <li>weight $val [kg|pound]</li>
  5986. <li>voltage $val [V|mV]</li>
  5987. <li>current $val [A|mA]</li>
  5988. <li>CO2-level $val ppm</li>
  5989. <li>airFlow $val [m3/h|cfm]</li>
  5990. <li>tankCapacity $val [l|cbm|usgal]</li>
  5991. <li>distance $val [m|cm|feet]</li>
  5992. <li>anglePosition $val [%|relN|relS]</li>
  5993. <li>rotation $val [rpm|Hz]</li>
  5994. <li>waterTemperature $val [C|F]</li>
  5995. <li>soilTemperature $val [C|F]</li>
  5996. <li>seismicIntensity $val [mercalli|EU macroseismic|liedu|shindo]</li>
  5997. <li>seismicMagnitude $val [local|moment|surface wave|body wave]</li>
  5998. <li>ultraviolet $val [UV]</li>
  5999. <li>electricalResistivity $val [ohm]</li>
  6000. <li>electricalConductivity $val [siemens/m]</li>
  6001. <li>loudness $val [dB|dBA]</li>
  6002. <li>moisture $val [%|content|k ohms|water activity]</li>
  6003. <li>frequency $val [Hz|kHz]</li>
  6004. <li>time $val [seconds]</li>
  6005. <li>targetTemperature $val [C|F]</li>
  6006. <li>particulateMatter $val [mol/m3|micro-g/m3]</li>
  6007. <li>formaldehydeLevel $val [mol/m3]</li>
  6008. <li>radonConcentration $val [bq/m3|pCi/L]</li>
  6009. <li>methaneDensity $val [mol/m3]</li>
  6010. <li>volatileOrganicCompound $val [mol/m3]</li>
  6011. <li>carbonMonoxide $val [mol/m3]</li>
  6012. <li>soilHumidity $val [%]</li>
  6013. <li>soilReactivity $val [pH]</li>
  6014. <li>soilSalinity $val [mol/m3]</li>
  6015. <li>heartRate $val [Bpm]</li>
  6016. <li>bloodPressure $val [Systolic mmHg|Diastolic mmHg]</li>
  6017. <li>muscleMass $val [Kg]</li>
  6018. <li>fatMass $val [Kg]</li>
  6019. <li>boneMass $val [Kg]</li>
  6020. <li>totalBodyWater $val [Kg]</li>
  6021. <li>basicMetabolicRate $val [J]</li>
  6022. <li>bodyMassIndex $val [BMI]</li>
  6023. <br><br><b>Class SWITCH_ALL</b>
  6024. <li>swa:[ none | on | off | on off ]</li>
  6025. <br><br><b>Class SWITCH_BINARY</b>
  6026. <li>state:on</li>
  6027. <li>state:off</li>
  6028. <li>state:setOn</li>
  6029. <li>state:setOff</li>
  6030. <li>swbState:$current target $target duration [$time seconds|unknown]</li>
  6031. <br><br><b>Class SWITCH_MULTILEVEL</b>
  6032. <li>state:on</li>
  6033. <li>state:off</li>
  6034. <li>state:setOn</li>
  6035. <li>state:setOff</li>
  6036. <li>state:dim value</li>
  6037. <li>state:swmBeginUp</li>
  6038. <li>state:swmBeginDown</li>
  6039. <li>state:swm [ Decrement | Increment ] [ Up | Down ]
  6040. Start: $sl Duration: $dur Step: $step</li>
  6041. <li>state:swmEnd</li>
  6042. <li>swmStatus:$value target &target duration $duration</li>
  6043. <br><br><b>Class THERMOSTAT_FAN_MODE</b>
  6044. <li>fanMode:[ fanAutoLow | fanLow | fanAutoHigh | fanHigh | fanAutoMedium |
  6045. fanMedium ]
  6046. </li>
  6047. <br><br><b>Class THERMOSTAT_FAN_STATE</b>
  6048. <li>fanState:[ off | low | high | medium | circulation | humidityCirc |
  6049. fanrightLeftCirc | upDownCirc | quietCirc ]</li>
  6050. <br><br><b>Class THERMOSTAT_MODE</b>
  6051. <li>thermostatMode:[ off | cooling | heating | fanOnly | auto |
  6052. energySaveHeating | manual | setTmOff | setTmHeating |
  6053. setTmEnergySaveHeating | setTmManual ]</li>
  6054. <br><br><b>Class THERMOSTAT_OPERATING_STATE</b>
  6055. <li>thermostatOperatingState:[ idle | heating | cooling | fanOnly |
  6056. pendingHeat | pendingCooling | ventEconomizer | auxHeating |
  6057. 2ndStageHeating | 2ndStageCooling | 2ndStageAuxHeat |
  6058. 3rdStageAuxHeat ]</li>
  6059. <br><br><b>Class THERMOSTAT_SETPOINT</b>
  6060. <li>setpointTemp:$temp $scale $type<br>
  6061. <ul>
  6062. $temp: setpoint temperature with number of decimals as reported
  6063. by the device<br>
  6064. $scale: [C|F]; C=Celsius scale, F=Fahrenheit scale<br>
  6065. $type: setpoint type, one of:<br>
  6066. <ul>
  6067. heating,
  6068. cooling,
  6069. furnance,
  6070. dryAir,
  6071. moistAir,
  6072. autoChangeover,
  6073. energySaveHeating,
  6074. energySaveCooling,
  6075. awayHeating,
  6076. awayCooling,
  6077. fullPower
  6078. </ul>
  6079. </ul>
  6080. </li>
  6081. <br><br><b>Class TIME, V2</b>
  6082. <li>time:$time RTC: [failed|working]</li>
  6083. <li>date:$date</li>
  6084. <li>timeOffset: UTC-Offset: $utco DST-Offset(minutes): $dsto DST-Start:
  6085. $start DST-End: $end</li>
  6086. <br><br><b>Class TIME_PARAMETERS, V1</b>
  6087. <li>timeParameters: date: $date time(UTC): $time</li>
  6088. <br><br><b>Class USER_CODE</b>
  6089. <li>userCode:id x status y code z</li>
  6090. <br><br><b>Class VERSION</b>
  6091. <li>V1:</li>
  6092. <li>version:Lib A Prot x.y App a.b</li>
  6093. <li>V2:</li>
  6094. <li>version:Lib A Prot x.y App a.b HW B FWCounter C FW c.d</li>
  6095. <li>V1 and V2:</li>
  6096. <li>versionClass_$classId:$version</li>
  6097. <br><br><b>Class WAKE_UP</b>
  6098. <li>wakeup:notification</li>
  6099. <li>wakeupReport:interval:X target:Y</li>
  6100. <li>wakeupIntervalCapabilitiesReport:min W max X default Y step Z</li>
  6101. <br><br><b>Class ZWAVEPLUS_INFO</b>
  6102. <li>zwavePlusInfo:version: V role: W node: X installerIcon: Y userIcon: Z</li>
  6103. </ul>
  6104. </ul>
  6105. =end html
  6106. =cut