| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231132321323313234132351323613237132381323913240132411324213243132441324513246132471324813249132501325113252132531325413255132561325713258132591326013261132621326313264132651326613267132681326913270132711327213273132741327513276132771327813279132801328113282132831328413285132861328713288132891329013291132921329313294132951329613297132981329913300133011330213303133041330513306133071330813309133101331113312133131331413315133161331713318133191332013321133221332313324133251332613327133281332913330133311333213333133341333513336133371333813339133401334113342133431334413345133461334713348133491335013351133521335313354133551335613357133581335913360133611336213363133641336513366133671336813369133701337113372133731337413375133761337713378133791338013381133821338313384133851338613387133881338913390133911339213393133941339513396133971339813399134001340113402134031340413405134061340713408134091341013411134121341313414134151341613417134181341913420134211342213423134241342513426134271342813429134301343113432134331343413435134361343713438134391344013441134421344313444134451344613447134481344913450134511345213453134541345513456134571345813459134601346113462134631346413465134661346713468134691347013471134721347313474134751347613477134781347913480134811348213483134841348513486134871348813489134901349113492134931349413495134961349713498134991350013501135021350313504135051350613507135081350913510135111351213513135141351513516135171351813519135201352113522135231352413525135261352713528135291353013531135321353313534135351353613537135381353913540135411354213543135441354513546135471354813549135501355113552135531355413555135561355713558135591356013561135621356313564135651356613567135681356913570135711357213573135741357513576135771357813579135801358113582135831358413585135861358713588135891359013591135921359313594135951359613597135981359913600136011360213603136041360513606136071360813609136101361113612136131361413615136161361713618136191362013621136221362313624136251362613627136281362913630136311363213633136341363513636136371363813639136401364113642136431364413645136461364713648136491365013651136521365313654136551365613657136581365913660136611366213663136641366513666136671366813669136701367113672136731367413675136761367713678136791368013681136821368313684136851368613687136881368913690136911369213693136941369513696136971369813699137001370113702137031370413705137061370713708137091371013711137121371313714137151371613717137181371913720137211372213723137241372513726137271372813729137301373113732137331373413735137361373713738137391374013741137421374313744137451374613747137481374913750137511375213753137541375513756137571375813759137601376113762137631376413765137661376713768137691377013771137721377313774137751377613777137781377913780137811378213783137841378513786137871378813789137901379113792137931379413795137961379713798137991380013801138021380313804138051380613807138081380913810138111381213813138141381513816138171381813819138201382113822138231382413825138261382713828138291383013831138321383313834138351383613837138381383913840138411384213843138441384513846138471384813849138501385113852138531385413855138561385713858138591386013861138621386313864138651386613867138681386913870138711387213873138741387513876138771387813879138801388113882138831388413885138861388713888138891389013891138921389313894138951389613897138981389913900139011390213903139041390513906139071390813909139101391113912139131391413915139161391713918139191392013921139221392313924139251392613927139281392913930139311393213933139341393513936139371393813939139401394113942139431394413945139461394713948139491395013951139521395313954139551395613957139581395913960139611396213963139641396513966139671396813969139701397113972139731397413975139761397713978139791398013981139821398313984139851398613987139881398913990139911399213993139941399513996139971399813999140001400114002140031400414005140061400714008140091401014011140121401314014140151401614017140181401914020140211402214023140241402514026140271402814029140301403114032140331403414035140361403714038140391404014041140421404314044140451404614047140481404914050140511405214053140541405514056140571405814059140601406114062140631406414065140661406714068140691407014071140721407314074140751407614077140781407914080140811408214083140841408514086140871408814089140901409114092140931409414095140961409714098140991410014101141021410314104141051410614107141081410914110141111411214113141141411514116141171411814119141201412114122141231412414125141261412714128141291413014131141321413314134141351413614137141381413914140141411414214143141441414514146141471414814149141501415114152141531415414155141561415714158141591416014161141621416314164141651416614167141681416914170141711417214173141741417514176141771417814179141801418114182141831418414185141861418714188141891419014191141921419314194141951419614197141981419914200142011420214203142041420514206142071420814209142101421114212142131421414215142161421714218142191422014221142221422314224142251422614227142281422914230142311423214233142341423514236142371423814239142401424114242142431424414245142461424714248142491425014251142521425314254142551425614257142581425914260142611426214263142641426514266142671426814269142701427114272142731427414275142761427714278142791428014281142821428314284142851428614287142881428914290142911429214293142941429514296142971429814299143001430114302143031430414305143061430714308143091431014311143121431314314143151431614317143181431914320143211432214323143241432514326143271432814329143301433114332143331433414335143361433714338143391434014341143421434314344143451434614347143481434914350143511435214353143541435514356143571435814359143601436114362143631436414365143661436714368143691437014371143721437314374143751437614377143781437914380143811438214383143841438514386143871438814389143901439114392143931439414395143961439714398143991440014401144021440314404144051440614407144081440914410144111441214413144141441514416144171441814419144201442114422144231442414425144261442714428144291443014431144321443314434144351443614437144381443914440144411444214443144441444514446144471444814449144501445114452144531445414455144561445714458144591446014461144621446314464144651446614467144681446914470144711447214473144741447514476144771447814479144801448114482144831448414485144861448714488144891449014491144921449314494144951449614497144981449914500145011450214503145041450514506145071450814509145101451114512145131451414515145161451714518145191452014521145221452314524145251452614527145281452914530145311453214533145341453514536145371453814539145401454114542145431454414545145461454714548145491455014551145521455314554145551455614557145581455914560145611456214563145641456514566145671456814569145701457114572145731457414575145761457714578145791458014581145821458314584145851458614587145881458914590145911459214593145941459514596145971459814599146001460114602146031460414605146061460714608146091461014611146121461314614146151461614617146181461914620146211462214623146241462514626146271462814629146301463114632146331463414635146361463714638146391464014641146421464314644146451464614647146481464914650146511465214653146541465514656146571465814659146601466114662146631466414665146661466714668146691467014671146721467314674146751467614677146781467914680146811468214683146841468514686146871468814689146901469114692146931469414695146961469714698146991470014701147021470314704147051470614707147081470914710147111471214713147141471514716147171471814719147201472114722147231472414725147261472714728147291473014731147321473314734147351473614737147381473914740147411474214743147441474514746147471474814749147501475114752147531475414755147561475714758147591476014761147621476314764147651476614767147681476914770147711477214773147741477514776147771477814779147801478114782147831478414785147861478714788147891479014791147921479314794147951479614797147981479914800148011480214803148041480514806148071480814809148101481114812148131481414815148161481714818148191482014821148221482314824148251482614827148281482914830148311483214833148341483514836148371483814839148401484114842148431484414845148461484714848148491485014851148521485314854148551485614857148581485914860148611486214863148641486514866148671486814869148701487114872148731487414875148761487714878148791488014881148821488314884148851488614887148881488914890148911489214893148941489514896148971489814899149001490114902149031490414905149061490714908149091491014911149121491314914149151491614917149181491914920149211492214923149241492514926149271492814929149301493114932149331493414935149361493714938149391494014941149421494314944149451494614947149481494914950149511495214953149541495514956149571495814959149601496114962149631496414965149661496714968149691497014971149721497314974149751497614977149781497914980149811498214983149841498514986149871498814989149901499114992149931499414995149961499714998149991500015001150021500315004150051500615007150081500915010150111501215013150141501515016150171501815019150201502115022150231502415025150261502715028150291503015031150321503315034150351503615037150381503915040150411504215043150441504515046150471504815049150501505115052150531505415055150561505715058150591506015061150621506315064150651506615067150681506915070150711507215073150741507515076150771507815079150801508115082150831508415085150861508715088150891509015091150921509315094150951509615097150981509915100151011510215103151041510515106151071510815109151101511115112151131511415115151161511715118151191512015121151221512315124151251512615127151281512915130151311513215133151341513515136151371513815139151401514115142151431514415145151461514715148151491515015151151521515315154151551515615157151581515915160151611516215163151641516515166151671516815169151701517115172151731517415175151761517715178151791518015181151821518315184151851518615187151881518915190151911519215193151941519515196151971519815199152001520115202152031520415205152061520715208152091521015211152121521315214152151521615217152181521915220152211522215223152241522515226152271522815229152301523115232152331523415235152361523715238152391524015241152421524315244152451524615247152481524915250152511525215253152541525515256152571525815259152601526115262152631526415265152661526715268152691527015271152721527315274152751527615277152781527915280152811528215283152841528515286152871528815289152901529115292152931529415295152961529715298152991530015301153021530315304153051530615307153081530915310153111531215313153141531515316153171531815319153201532115322153231532415325153261532715328153291533015331153321533315334153351533615337153381533915340153411534215343153441534515346153471534815349153501535115352153531535415355153561535715358153591536015361153621536315364153651536615367153681536915370153711537215373153741537515376153771537815379153801538115382153831538415385153861538715388153891539015391153921539315394153951539615397153981539915400154011540215403154041540515406154071540815409154101541115412154131541415415154161541715418154191542015421154221542315424154251542615427154281542915430154311543215433154341543515436154371543815439154401544115442154431544415445154461544715448154491545015451154521545315454154551545615457154581545915460154611546215463154641546515466154671546815469154701547115472154731547415475154761547715478154791548015481154821548315484154851548615487154881548915490154911549215493154941549515496154971549815499155001550115502155031550415505155061550715508155091551015511155121551315514155151551615517155181551915520155211552215523155241552515526155271552815529155301553115532155331553415535155361553715538155391554015541155421554315544155451554615547155481554915550155511555215553155541555515556155571555815559155601556115562155631556415565155661556715568155691557015571155721557315574155751557615577155781557915580155811558215583155841558515586155871558815589155901559115592155931559415595155961559715598155991560015601156021560315604156051560615607156081560915610156111561215613156141561515616156171561815619156201562115622156231562415625156261562715628156291563015631156321563315634156351563615637156381563915640156411564215643156441564515646156471564815649156501565115652156531565415655156561565715658156591566015661156621566315664156651566615667156681566915670156711567215673156741567515676156771567815679156801568115682156831568415685156861568715688156891569015691156921569315694156951569615697156981569915700157011570215703157041570515706157071570815709157101571115712157131571415715157161571715718157191572015721157221572315724157251572615727157281572915730157311573215733157341573515736157371573815739157401574115742157431574415745157461574715748157491575015751157521575315754157551575615757157581575915760157611576215763157641576515766157671576815769157701577115772157731577415775157761577715778157791578015781157821578315784157851578615787157881578915790157911579215793157941579515796157971579815799158001580115802158031580415805158061580715808158091581015811158121581315814158151581615817158181581915820158211582215823158241582515826158271582815829158301583115832158331583415835158361583715838158391584015841158421584315844158451584615847158481584915850158511585215853158541585515856158571585815859158601586115862158631586415865158661586715868158691587015871158721587315874158751587615877158781587915880158811588215883158841588515886158871588815889158901589115892158931589415895158961589715898158991590015901159021590315904159051590615907159081590915910159111591215913159141591515916159171591815919159201592115922159231592415925159261592715928159291593015931159321593315934159351593615937159381593915940159411594215943159441594515946159471594815949159501595115952159531595415955159561595715958159591596015961159621596315964159651596615967159681596915970159711597215973159741597515976159771597815979159801598115982159831598415985159861598715988159891599015991159921599315994159951599615997159981599916000160011600216003160041600516006160071600816009160101601116012160131601416015160161601716018160191602016021160221602316024160251602616027160281602916030160311603216033160341603516036160371603816039160401604116042160431604416045160461604716048160491605016051160521605316054160551605616057160581605916060160611606216063160641606516066160671606816069160701607116072160731607416075160761607716078160791608016081160821608316084160851608616087160881608916090160911609216093160941609516096160971609816099161001610116102161031610416105161061610716108161091611016111161121611316114161151611616117161181611916120161211612216123161241612516126161271612816129161301613116132161331613416135161361613716138161391614016141161421614316144161451614616147161481614916150161511615216153161541615516156161571615816159161601616116162161631616416165161661616716168161691617016171161721617316174161751617616177161781617916180161811618216183161841618516186161871618816189161901619116192161931619416195161961619716198161991620016201162021620316204162051620616207162081620916210162111621216213162141621516216162171621816219162201622116222162231622416225162261622716228162291623016231162321623316234162351623616237162381623916240162411624216243162441624516246162471624816249162501625116252162531625416255162561625716258162591626016261162621626316264162651626616267162681626916270162711627216273162741627516276162771627816279162801628116282162831628416285162861628716288162891629016291162921629316294162951629616297162981629916300163011630216303163041630516306163071630816309163101631116312163131631416315163161631716318163191632016321163221632316324163251632616327163281632916330163311633216333163341633516336163371633816339163401634116342163431634416345163461634716348163491635016351163521635316354163551635616357163581635916360163611636216363163641636516366163671636816369163701637116372163731637416375163761637716378163791638016381163821638316384163851638616387163881638916390163911639216393163941639516396163971639816399164001640116402164031640416405164061640716408164091641016411164121641316414164151641616417164181641916420164211642216423164241642516426164271642816429164301643116432164331643416435164361643716438164391644016441164421644316444164451644616447164481644916450164511645216453164541645516456164571645816459164601646116462164631646416465164661646716468164691647016471164721647316474164751647616477164781647916480164811648216483164841648516486164871648816489164901649116492164931649416495164961649716498164991650016501165021650316504165051650616507165081650916510165111651216513165141651516516165171651816519165201652116522165231652416525165261652716528165291653016531165321653316534165351653616537165381653916540165411654216543165441654516546165471654816549165501655116552165531655416555165561655716558165591656016561165621656316564165651656616567165681656916570165711657216573165741657516576165771657816579165801658116582165831658416585165861658716588165891659016591165921659316594165951659616597165981659916600166011660216603166041660516606166071660816609166101661116612166131661416615166161661716618166191662016621166221662316624166251662616627166281662916630166311663216633166341663516636166371663816639166401664116642166431664416645166461664716648166491665016651166521665316654166551665616657166581665916660166611666216663166641666516666166671666816669166701667116672166731667416675166761667716678166791668016681166821668316684166851668616687166881668916690166911669216693166941669516696166971669816699167001670116702167031670416705167061670716708167091671016711167121671316714167151671616717167181671916720167211672216723167241672516726167271672816729167301673116732167331673416735167361673716738167391674016741167421674316744167451674616747167481674916750167511675216753167541675516756167571675816759167601676116762167631676416765167661676716768167691677016771167721677316774167751677616777167781677916780167811678216783167841678516786167871678816789167901679116792167931679416795167961679716798167991680016801168021680316804168051680616807168081680916810168111681216813168141681516816168171681816819168201682116822168231682416825168261682716828168291683016831168321683316834168351683616837168381683916840168411684216843168441684516846168471684816849168501685116852168531685416855168561685716858168591686016861168621686316864168651686616867168681686916870168711687216873168741687516876168771687816879168801688116882168831688416885168861688716888168891689016891168921689316894168951689616897168981689916900169011690216903169041690516906169071690816909169101691116912169131691416915169161691716918169191692016921169221692316924169251692616927169281692916930169311693216933169341693516936169371693816939169401694116942169431694416945169461694716948169491695016951169521695316954169551695616957169581695916960169611696216963169641696516966169671696816969169701697116972169731697416975169761697716978169791698016981169821698316984169851698616987169881698916990169911699216993169941699516996169971699816999170001700117002170031700417005170061700717008170091701017011170121701317014170151701617017170181701917020170211702217023170241702517026170271702817029170301703117032170331703417035170361703717038170391704017041170421704317044170451704617047170481704917050170511705217053170541705517056170571705817059170601706117062170631706417065170661706717068170691707017071170721707317074170751707617077170781707917080170811708217083170841708517086170871708817089170901709117092170931709417095170961709717098170991710017101171021710317104171051710617107171081710917110171111711217113171141711517116171171711817119171201712117122171231712417125171261712717128171291713017131171321713317134171351713617137171381713917140171411714217143171441714517146171471714817149171501715117152171531715417155171561715717158171591716017161171621716317164171651716617167171681716917170171711717217173171741717517176171771717817179171801718117182171831718417185171861718717188171891719017191171921719317194171951719617197171981719917200172011720217203172041720517206172071720817209172101721117212172131721417215172161721717218172191722017221172221722317224172251722617227172281722917230172311723217233172341723517236172371723817239172401724117242172431724417245172461724717248172491725017251172521725317254172551725617257172581725917260172611726217263172641726517266172671726817269172701727117272172731727417275172761727717278172791728017281172821728317284172851728617287172881728917290172911729217293172941729517296172971729817299173001730117302173031730417305173061730717308173091731017311173121731317314173151731617317173181731917320173211732217323173241732517326173271732817329173301733117332173331733417335173361733717338173391734017341173421734317344173451734617347173481734917350173511735217353173541735517356173571735817359173601736117362173631736417365173661736717368173691737017371173721737317374173751737617377173781737917380173811738217383173841738517386173871738817389173901739117392173931739417395173961739717398173991740017401174021740317404174051740617407174081740917410174111741217413174141741517416174171741817419174201742117422174231742417425174261742717428174291743017431174321743317434174351743617437174381743917440174411744217443174441744517446174471744817449174501745117452174531745417455174561745717458174591746017461174621746317464174651746617467174681746917470174711747217473174741747517476174771747817479174801748117482174831748417485174861748717488174891749017491174921749317494174951749617497174981749917500175011750217503175041750517506175071750817509175101751117512175131751417515175161751717518175191752017521175221752317524175251752617527175281752917530175311753217533175341753517536175371753817539175401754117542175431754417545175461754717548175491755017551175521755317554175551755617557175581755917560175611756217563175641756517566175671756817569175701757117572175731757417575175761757717578175791758017581175821758317584175851758617587175881758917590175911759217593175941759517596175971759817599176001760117602176031760417605176061760717608176091761017611176121761317614176151761617617176181761917620176211762217623176241762517626176271762817629176301763117632176331763417635176361763717638176391764017641176421764317644176451764617647176481764917650176511765217653176541765517656176571765817659176601766117662176631766417665176661766717668176691767017671176721767317674176751767617677176781767917680176811768217683176841768517686176871768817689176901769117692176931769417695176961769717698176991770017701177021770317704177051770617707177081770917710177111771217713177141771517716177171771817719177201772117722177231772417725177261772717728177291773017731177321773317734177351773617737177381773917740177411774217743177441774517746177471774817749177501775117752177531775417755177561775717758177591776017761177621776317764177651776617767177681776917770177711777217773177741777517776177771777817779177801778117782177831778417785177861778717788177891779017791177921779317794177951779617797177981779917800178011780217803178041780517806178071780817809178101781117812178131781417815178161781717818178191782017821178221782317824178251782617827178281782917830178311783217833178341783517836178371783817839178401784117842178431784417845178461784717848178491785017851178521785317854178551785617857178581785917860178611786217863178641786517866178671786817869178701787117872178731787417875178761787717878178791788017881178821788317884178851788617887178881788917890178911789217893178941789517896178971789817899179001790117902179031790417905179061790717908179091791017911179121791317914179151791617917179181791917920179211792217923179241792517926179271792817929179301793117932179331793417935179361793717938179391794017941179421794317944179451794617947179481794917950179511795217953179541795517956179571795817959179601796117962179631796417965179661796717968179691797017971179721797317974179751797617977179781797917980179811798217983179841798517986179871798817989179901799117992179931799417995179961799717998179991800018001180021800318004180051800618007180081800918010180111801218013180141801518016180171801818019180201802118022180231802418025180261802718028180291803018031180321803318034180351803618037180381803918040180411804218043180441804518046180471804818049180501805118052180531805418055180561805718058180591806018061180621806318064180651806618067180681806918070180711807218073180741807518076180771807818079180801808118082180831808418085180861808718088180891809018091180921809318094180951809618097180981809918100181011810218103181041810518106181071810818109181101811118112181131811418115181161811718118181191812018121181221812318124181251812618127181281812918130181311813218133181341813518136181371813818139181401814118142181431814418145181461814718148181491815018151181521815318154181551815618157181581815918160181611816218163181641816518166181671816818169181701817118172181731817418175181761817718178181791818018181181821818318184181851818618187181881818918190181911819218193181941819518196181971819818199182001820118202182031820418205182061820718208182091821018211182121821318214182151821618217182181821918220182211822218223182241822518226182271822818229182301823118232182331823418235182361823718238182391824018241182421824318244182451824618247182481824918250182511825218253182541825518256182571825818259182601826118262182631826418265182661826718268182691827018271182721827318274182751827618277182781827918280182811828218283182841828518286182871828818289182901829118292182931829418295182961829718298182991830018301183021830318304183051830618307183081830918310183111831218313183141831518316183171831818319183201832118322183231832418325183261832718328183291833018331183321833318334183351833618337183381833918340183411834218343183441834518346183471834818349183501835118352183531835418355183561835718358183591836018361183621836318364183651836618367183681836918370183711837218373183741837518376183771837818379183801838118382183831838418385183861838718388183891839018391183921839318394183951839618397183981839918400184011840218403184041840518406184071840818409184101841118412184131841418415184161841718418184191842018421184221842318424184251842618427184281842918430184311843218433184341843518436184371843818439184401844118442184431844418445184461844718448184491845018451184521845318454184551845618457184581845918460184611846218463184641846518466184671846818469184701847118472184731847418475184761847718478184791848018481184821848318484184851848618487184881848918490184911849218493184941849518496184971849818499185001850118502185031850418505185061850718508185091851018511185121851318514185151851618517185181851918520185211852218523185241852518526185271852818529185301853118532185331853418535185361853718538185391854018541185421854318544185451854618547185481854918550185511855218553185541855518556185571855818559185601856118562185631856418565185661856718568185691857018571185721857318574185751857618577185781857918580185811858218583185841858518586185871858818589185901859118592185931859418595185961859718598185991860018601186021860318604186051860618607186081860918610186111861218613186141861518616186171861818619186201862118622186231862418625186261862718628186291863018631186321863318634186351863618637186381863918640186411864218643186441864518646186471864818649186501865118652186531865418655186561865718658186591866018661186621866318664186651866618667186681866918670186711867218673186741867518676186771867818679186801868118682186831868418685186861868718688186891869018691186921869318694186951869618697186981869918700187011870218703187041870518706187071870818709187101871118712187131871418715187161871718718187191872018721187221872318724187251872618727187281872918730187311873218733187341873518736187371873818739187401874118742187431874418745187461874718748187491875018751187521875318754187551875618757187581875918760187611876218763187641876518766187671876818769187701877118772187731877418775187761877718778187791878018781187821878318784187851878618787187881878918790187911879218793187941879518796187971879818799188001880118802188031880418805188061880718808188091881018811188121881318814188151881618817188181881918820188211882218823188241882518826188271882818829188301883118832188331883418835188361883718838188391884018841188421884318844188451884618847188481884918850188511885218853188541885518856188571885818859188601886118862188631886418865188661886718868188691887018871188721887318874188751887618877188781887918880188811888218883188841888518886188871888818889188901889118892188931889418895188961889718898188991890018901189021890318904189051890618907189081890918910189111891218913189141891518916189171891818919189201892118922189231892418925189261892718928189291893018931189321893318934189351893618937189381893918940189411894218943189441894518946189471894818949189501895118952189531895418955189561895718958189591896018961189621896318964189651896618967189681896918970189711897218973189741897518976189771897818979189801898118982189831898418985189861898718988189891899018991189921899318994189951899618997189981899919000190011900219003190041900519006190071900819009190101901119012190131901419015190161901719018190191902019021190221902319024190251902619027190281902919030190311903219033190341903519036190371903819039190401904119042190431904419045190461904719048190491905019051190521905319054190551905619057190581905919060190611906219063190641906519066190671906819069190701907119072190731907419075190761907719078190791908019081190821908319084190851908619087190881908919090190911909219093190941909519096190971909819099191001910119102191031910419105191061910719108191091911019111191121911319114191151911619117191181911919120191211912219123191241912519126191271912819129191301913119132191331913419135191361913719138191391914019141191421914319144191451914619147191481914919150191511915219153191541915519156191571915819159191601916119162191631916419165191661916719168191691917019171191721917319174191751917619177191781917919180191811918219183191841918519186191871918819189191901919119192191931919419195191961919719198191991920019201192021920319204192051920619207192081920919210192111921219213192141921519216192171921819219192201922119222192231922419225192261922719228192291923019231192321923319234192351923619237192381923919240192411924219243192441924519246192471924819249192501925119252192531925419255192561925719258192591926019261192621926319264192651926619267192681926919270192711927219273192741927519276192771927819279192801928119282192831928419285192861928719288192891929019291192921929319294192951929619297192981929919300193011930219303193041930519306193071930819309193101931119312193131931419315193161931719318193191932019321193221932319324193251932619327193281932919330193311933219333193341933519336193371933819339193401934119342193431934419345193461934719348193491935019351193521935319354193551935619357193581935919360193611936219363193641936519366193671936819369193701937119372193731937419375193761937719378193791938019381193821938319384193851938619387193881938919390193911939219393193941939519396193971939819399194001940119402194031940419405194061940719408194091941019411194121941319414194151941619417194181941919420194211942219423194241942519426194271942819429194301943119432194331943419435194361943719438194391944019441194421944319444194451944619447194481944919450194511945219453194541945519456194571945819459194601946119462194631946419465194661946719468194691947019471194721947319474194751947619477194781947919480194811948219483194841948519486194871948819489194901949119492194931949419495194961949719498194991950019501195021950319504195051950619507195081950919510195111951219513195141951519516195171951819519195201952119522195231952419525195261952719528195291953019531195321953319534195351953619537195381953919540195411954219543195441954519546195471954819549195501955119552195531955419555195561955719558195591956019561195621956319564195651956619567195681956919570195711957219573195741957519576195771957819579195801958119582195831958419585195861958719588195891959019591195921959319594195951959619597195981959919600196011960219603196041960519606196071960819609196101961119612196131961419615196161961719618196191962019621196221962319624196251962619627196281962919630196311963219633196341963519636196371963819639196401964119642196431964419645196461964719648196491965019651196521965319654196551965619657196581965919660196611966219663196641966519666196671966819669196701967119672196731967419675196761967719678196791968019681196821968319684196851968619687196881968919690196911969219693196941969519696196971969819699197001970119702197031970419705197061970719708197091971019711197121971319714197151971619717197181971919720197211972219723197241972519726197271972819729197301973119732197331973419735197361973719738197391974019741197421974319744197451974619747197481974919750197511975219753197541975519756197571975819759197601976119762197631976419765197661976719768197691977019771197721977319774197751977619777197781977919780197811978219783197841978519786197871978819789197901979119792197931979419795197961979719798197991980019801198021980319804198051980619807198081980919810198111981219813198141981519816198171981819819198201982119822198231982419825198261982719828198291983019831198321983319834198351983619837198381983919840198411984219843198441984519846198471984819849198501985119852198531985419855198561985719858198591986019861198621986319864198651986619867198681986919870198711987219873198741987519876198771987819879198801988119882198831988419885198861988719888198891989019891198921989319894198951989619897198981989919900199011990219903199041990519906199071990819909199101991119912199131991419915199161991719918199191992019921199221992319924199251992619927199281992919930199311993219933199341993519936199371993819939199401994119942199431994419945199461994719948199491995019951199521995319954199551995619957199581995919960199611996219963199641996519966199671996819969199701997119972199731997419975199761997719978199791998019981199821998319984199851998619987199881998919990199911999219993199941999519996199971999819999200002000120002200032000420005200062000720008200092001020011200122001320014200152001620017200182001920020200212002220023200242002520026200272002820029200302003120032200332003420035200362003720038200392004020041200422004320044200452004620047200482004920050200512005220053200542005520056200572005820059200602006120062200632006420065200662006720068200692007020071200722007320074200752007620077200782007920080200812008220083200842008520086200872008820089200902009120092200932009420095200962009720098200992010020101201022010320104201052010620107201082010920110201112011220113201142011520116201172011820119201202012120122201232012420125201262012720128201292013020131201322013320134201352013620137201382013920140201412014220143201442014520146201472014820149201502015120152201532015420155201562015720158201592016020161201622016320164201652016620167201682016920170201712017220173201742017520176201772017820179201802018120182201832018420185201862018720188201892019020191201922019320194201952019620197201982019920200202012020220203202042020520206202072020820209202102021120212202132021420215202162021720218202192022020221202222022320224202252022620227202282022920230202312023220233202342023520236202372023820239202402024120242202432024420245202462024720248202492025020251202522025320254202552025620257202582025920260202612026220263202642026520266202672026820269202702027120272202732027420275202762027720278202792028020281202822028320284202852028620287202882028920290202912029220293202942029520296202972029820299203002030120302203032030420305203062030720308203092031020311203122031320314203152031620317203182031920320203212032220323203242032520326203272032820329203302033120332203332033420335203362033720338203392034020341203422034320344203452034620347203482034920350203512035220353203542035520356203572035820359203602036120362203632036420365203662036720368203692037020371203722037320374203752037620377203782037920380203812038220383203842038520386203872038820389203902039120392203932039420395203962039720398203992040020401204022040320404204052040620407204082040920410204112041220413204142041520416204172041820419204202042120422204232042420425204262042720428204292043020431204322043320434204352043620437204382043920440204412044220443204442044520446204472044820449204502045120452204532045420455204562045720458204592046020461204622046320464204652046620467204682046920470204712047220473204742047520476204772047820479204802048120482204832048420485204862048720488204892049020491204922049320494204952049620497204982049920500205012050220503205042050520506205072050820509205102051120512205132051420515205162051720518 |
- ##############################################
- # $Id: 10_EnOcean.pm 16029 2018-01-28 18:43:46Z klaus.schauer $
- package main;
- use strict;
- use warnings;
- my $cryptFunc;
- my $xmlFunc;
- my $xml;
- eval "use Crypt::Rijndael";
- if ($@) {
- $cryptFunc = 0;
- } else {
- $cryptFunc = 1;
- }
- eval "use Crypt::Random qw(makerandom)";
- if ($@) {
- $cryptFunc = 0;
- } else {
- $cryptFunc = $cryptFunc == 1 ? 1 : 0;
- }
- eval "use XML::Simple";
- if ($@) {
- $xmlFunc = 0;
- } else {
- $xmlFunc = 1;
- $xml = new XML::Simple;
- }
- eval "use Data::Dumper";
- if ($@) {
- $xmlFunc = 0;
- } else {
- $xmlFunc = $xmlFunc == 1 ? 1 : 0;
- }
- use SetExtensions;
- sub EnOcean_Define($$);
- sub EnOcean_Initialize($);
- sub EnOcean_Parse($$);
- sub EnOcean_Get($@);
- sub EnOcean_Set($@);
- sub EnOcean_hvac_01Cmd($$$);
- sub EnOcean_roomCtrlPanel_00Snd($$$$$$$$);
- sub EnOcean_CheckSenderID($$$);
- sub EnOcean_SndRadio($$$$$$$$);
- sub EnOcean_ReadingScaled($$$$);
- sub EnOcean_TimerSet($);
- sub EnOcean_Undef($$);
- my %EnO_rorgname = (
- "A5" => "4BS", # 4BS, org 07
- "A6" => "ADT", # adressing destination telegram
- "A7" => "SMREC", # Smart Ack Relaim
- "B0" => "GPTI", # GP teach-in request
- "B1" => "GPTR", # GP teach-in response
- "B2" => "GPCD", # GP complete data
- "B3" => "GPSD", # GP selective data
- "C5" => "SYSEX", # remote management >> packet type 7 used
- "C6" => "SMLRNREQ", # Smart Ack Learn Request
- "C7" => "SMLRNANS", # Smart Ack Learn Answer
- "D0" => "SMMBOX", # Smart Ack Mail Box Functions
- "D1" => "MSC", # MSC
- "D2" => "VLD", # VLD
- "D4" => "UTE", # UTE
- "D5" => "contact", # 1BS, org 06
- "F6" => "switch", # RPS, org 05
- "30" => "SEC", # secure telegram
- "31" => "ENC", # secure telegram with encapsulation
- "32" => "SECD", # decrypted secure telegram
- "35" => "STE", # secure Teach-In
- "40" => "CDM", # chained data message
- );
- # switch commands
- my @EnO_ptm200btn = ("AI", "A0", "BI", "B0", "CI", "C0", "DI", "D0");
- my %EnO_ptm200btn;
- # switch.00 commands
- my %EnO_switch_00Btn = (
- "A0" => 14,
- "AI" => 13,
- "B0" => 12,
- "BI" => 11,
- "A0,B0" => 7,
- "A0,BI" => 10,
- "AI,B0" => 5,
- "AI,BI" => 9,
- "pressed" => 8,
- "pressed34" => 6,
- "released" => 15,
- "teachInSec" => 253,
- "teachOut" => 254,
- "teachIn" => 255
- );
- # gateway commands
- my @EnO_gwCmd = ("switching", "dimming", "setpointShift", "setpointBasic", "controlVar", "fanStage", "blindCmd");
- my %EnO_gwCmd = (
- "switching" => 1,
- "dimming" => 2,
- "setpointShift" => 3,
- "setpointBasic" => 4,
- "controlVar" => 5,
- "fanStage" => 6,
- "blindCmd" => 7,
- );
- # Some Manufacturers (e.g. Jaeger Direkt) also sell EnOcean products without an entry in the table below.
- my %EnO_manuf = (
- "000" => "Reserved",
- "001" => "Peha",
- "002" => "Thermokon",
- "003" => "Servodan",
- "004" => "EchoFlex Solutions",
- "005" => "AWAG Elektrotechnik AG (Omnio)",
- "006" => "Hardmeier electronics",
- "007" => "Regulvar Inc",
- "008" => "Ad Hoc Electronics",
- "009" => "Distech Controls",
- "00A" => "Kieback + Peter",
- "00B" => "EnOcean GmbH",
- "00C" => "Probare",
- "00D" => "Eltako",
- "00E" => "Leviton",
- "00F" => "Honeywell",
- "010" => "Spartan Peripheral Devices",
- "011" => "Siemens",
- "012" => "T-Mac",
- "013" => "Reliable Controls Corporation",
- "014" => "Elsner Elektronik GmbH",
- "015" => "Diehl Controls",
- "016" => "BSC Computer",
- "017" => "S+S Regeltechnik GmbH",
- "018" => "Masco Corporation",
- "019" => "Intesis Software SL",
- "01A" => "Viessmann",
- "01B" => "Lutuo Technology",
- "01C" => "Schneider Electric",
- "01D" => "Sauter",
- "01E" => "Boot-Up",
- "01F" => "Osram Sylvania",
- "020" => "Unotech",
- "021" => "Delta Controls Inc",
- "022" => "Unitronic AG",
- "023" => "NanoSense",
- "024" => "The S4 Group",
- "025" => "MSR Solutions",
- "026" => "GE",
- "027" => "Maico",
- "028" => "Ruskin Company",
- "029" => "Magnum Engery Solutions",
- "02A" => "KM Controls",
- "02B" => "Ecologix Controls",
- "02C" => "Trio 2 Sys",
- "02D" => "Afriso-Euro-Index",
- "030" => "NEC AccessTechnica Ltd",
- "031" => "ITEC Corporation",
- "032" => "Simix Co Ltd",
- "033" => "Permundo GmbH",
- "034" => "Eurotronic Technology GmbH",
- "035" => "Art Japan Co Ltd",
- "036" => "Tiansu Automation Control System Co Ltd",
- "038" => "Gruppo Giordano Idea Spa",
- "039" => "alphaEOS AG",
- "03A" => "Tag Technologies",
- "03C" => "Cloud Buildings Ltd",
- "03E" => "GIGA Concept",
- "03F" => "Sensortec",
- "040" => "Jaeger Direkt",
- "041" => "Air System Components Inc",
- "043" => "SODA GmbH",
- "045" => "Holter",
- "046" => "ID-RF",
- "049" => "Micropelt GmbH",
- "7FF" => "Multi user Manufacturer ID",
- );
- my %EnO_eepConfig = (
- "A5.02.01" => {attr => {subType => "tempSensor.01"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.02" => {attr => {subType => "tempSensor.02"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.03" => {attr => {subType => "tempSensor.03"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.04" => {attr => {subType => "tempSensor.04"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.05" => {attr => {subType => "tempSensor.05"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.06" => {attr => {subType => "tempSensor.06"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.07" => {attr => {subType => "tempSensor.07"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.08" => {attr => {subType => "tempSensor.08"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.09" => {attr => {subType => "tempSensor.09"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.0A" => {attr => {subType => "tempSensor.0A"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.0B" => {attr => {subType => "tempSensor.0B"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.10" => {attr => {subType => "tempSensor.10"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.11" => {attr => {subType => "tempSensor.11"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.12" => {attr => {subType => "tempSensor.12"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.13" => {attr => {subType => "tempSensor.13"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.14" => {attr => {subType => "tempSensor.14"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.15" => {attr => {subType => "tempSensor.15"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.16" => {attr => {subType => "tempSensor.16"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.17" => {attr => {subType => "tempSensor.17"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.18" => {attr => {subType => "tempSensor.18"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.19" => {attr => {subType => "tempSensor.19"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.1A" => {attr => {subType => "tempSensor.1A"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.1B" => {attr => {subType => "tempSensor.1B"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.20" => {attr => {subType => "tempSensor.20"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.02.30" => {attr => {subType => "tempSensor.30"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.04.01" => {attr => {subType => "roomSensorControl.01"}, GPLOT => "EnO_temp4humi6:Temp/Humi,"},
- "A5.04.02" => {attr => {subType => "tempHumiSensor.02"}, GPLOT => "EnO_temp4humi6:Temp/Humi,EnO_voltage4:Voltage,"},
- "A5.04.03" => {attr => {subType => "tempHumiSensor.03"}, GPLOT => "EnO_temp4humi6:Temp/Humi,"},
- "A5.05.01" => {attr => {subType => "baroSensor.01"}, GPLOT => "EnO_airPressure4:Airpressure,"},
- "A5.06.01" => {attr => {subType => "lightSensor.01"}, GPLOT => "EnO_brightness4:Brightness,EnO_voltage4:Voltage,"},
- "A5.06.02" => {attr => {subType => "lightSensor.02"}, GPLOT => "EnO_brightness4:Brightness,EnO_voltage4:Voltage,"},
- "A5.06.03" => {attr => {subType => "lightSensor.03"}, GPLOT => "EnO_brightness4:Brightness,"},
- "A5.06.04" => {attr => {subType => "lightSensor.04"}, GPLOT => "EnO_temp4brightness4:Temp/Brightness,"},
- "A5.06.05" => {attr => {subType => "lightSensor.05"}, GPLOT => "EnO_brightness4:Brightness,EnO_voltage4:Voltage,"},
- "A5.07.01" => {attr => {subType => "occupSensor.01"}, GPLOT => "EnO_motion:Motion,EnO_voltage4current4:Voltage/Current,"},
- "A5.07.02" => {attr => {subType => "occupSensor.02"}, GPLOT => "EnO_motion:Motion4brightness4:Motion/Brightness,EnO_voltage4:Voltage,"},
- "A5.07.03" => {attr => {subType => "occupSensor.03"}, GPLOT => "EnO_motion:Motion4brightness4:Motion/Brightness,EnO_voltage4:Voltage,"},
- "A5.08.01" => {attr => {subType => "lightTempOccupSensor.01"}, GPLOT => "EnO_temp4brightness4:Temp/Brightness,EnO_voltage4:Voltage,"},
- "A5.08.02" => {attr => {subType => "lightTempOccupSensor.02"}, GPLOT => "EnO_temp4brightness4:Temp/Brightness,EnO_voltage4:Voltage,"},
- "A5.08.03" => {attr => {subType => "lightTempOccupSensor.03"}, GPLOT => "EnO_temp4brightness4:Temp/Brightness,EnO_voltage4:Voltage,"},
- "A5.09.01" => {attr => {subType => "COSensor.01"}, GPLOT => "EnO_A5-09-01:CO/Temp,"},
- "A5.09.02" => {attr => {subType => "COSensor.02"}, GPLOT => "EnO_A5-09-02:CO/Temp,EnO_voltage4:Voltage,"},
- "A5.09.04" => {attr => {subType => "tempHumiCO2Sensor.01"}, GPLOT => "EnO_CO2:CO2,EnO_temp4humi6:Temp/Humi,"},
- "A5.09.05" => {attr => {subType => "vocSensor.01"}, GPLOT => "EnO_A5-09-05:Concentration,"},
- "A5.09.06" => {attr => {subType => "radonSensor.01"}, GPLOT => "EnO_A5-09-06:Radon,"},
- "A5.09.07" => {attr => {subType => "particlesSensor.01"}, GPLOT => "EnO_A5-09-07:Particles,"},
- "A5.09.08" => {attr => {subType => "CO2Sensor.01"}, GPLOT => "EnO_CO2:CO2,"},
- "A5.09.09" => {attr => {subType => "CO2Sensor.01"}, GPLOT => "EnO_CO2:CO2,"},
- "A5.09.0A" => {attr => {subType => "HSensor.01"}, GPLOT => "EnO_A5-09-0A:H/Temp,EnO_voltage4:Voltage,"},
- "A5.09.0B" => {attr => {subType => "radiationSensor.01"}, GPLOT => "EnO_radioactivity4/Radioactivity,EnO_voltage4:Voltage,"},
- "A5.09.0C" => {attr => {subType => "vocSensor.01"}, GPLOT => "EnO_A5-09-05:Concentration,"},
- "A5.10.01" => {attr => {subType => "roomSensorControl.05"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.10.02" => {attr => {subType => "roomSensorControl.05"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.10.03" => {attr => {subType => "roomSensorControl.05", comMode => "confirm", subDef => "getNextID"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.10.04" => {attr => {subType => "roomSensorControl.05"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.10.05" => {attr => {subType => "roomSensorControl.05"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.10.06" => {attr => {subType => "roomSensorControl.05"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.10.07" => {attr => {subType => "roomSensorControl.05"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.10.08" => {attr => {subType => "roomSensorControl.05"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.10.09" => {attr => {subType => "roomSensorControl.05"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.10.0A" => {attr => {subType => "roomSensorControl.05"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.10.0B" => {attr => {subType => "roomSensorControl.05"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.10.0C" => {attr => {subType => "roomSensorControl.05"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.10.0D" => {attr => {subType => "roomSensorControl.05"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.10.10" => {attr => {subType => "roomSensorControl.01"}, GPLOT => "EnO_temp4humi6:Temp/Humi,"},
- "A5.10.11" => {attr => {subType => "roomSensorControl.01"}, GPLOT => "EnO_temp4humi6:Temp/Humi,"},
- "A5.10.12" => {attr => {subType => "roomSensorControl.01"}, GPLOT => "EnO_temp4humi6:Temp/Humi,"},
- "A5.10.13" => {attr => {subType => "roomSensorControl.01"}, GPLOT => "EnO_temp4humi6:Temp/Humi,"},
- "A5.10.14" => {attr => {subType => "roomSensorControl.01"}, GPLOT => "EnO_temp4humi6:Temp/Humi,"},
- "A5.10.15" => {attr => {subType => "roomSensorControl.02"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.10.16" => {attr => {subType => "roomSensorControl.02"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.10.17" => {attr => {subType => "roomSensorControl.02"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.10.18" => {attr => {subType => "roomSensorControl.18"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.10.19" => {attr => {subType => "roomSensorControl.19"}, GPLOT => "EnO_temp4humi6:Temp/Humi,"},
- "A5.10.1A" => {attr => {subType => "roomSensorControl.1A"}, GPLOT => "EnO_temp4:Temp,EnO_voltage4:Voltage,"},
- "A5.10.1B" => {attr => {subType => "roomSensorControl.1B"}, GPLOT => "EnO_temp4:Temp,EnO_voltage4:Voltage,"},
- "A5.10.1C" => {attr => {subType => "roomSensorControl.1C"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.10.1D" => {attr => {subType => "roomSensorControl.1D"}, GPLOT => "EnO_temp4humi6:Temp/Humi"},
- "A5.10.1E" => {attr => {subType => "roomSensorControl.1B"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.10.1F" => {attr => {subType => "roomSensorControl.1F"}, GPLOT => "EnO_temp4:Temp,"},
- "A5.10.20" => {attr => {subType => "roomSensorControl.20"}, GPLOT => "EnO_temp4humi4:Temp/Humi,"},
- "A5.10.21" => {attr => {subType => "roomSensorControl.20"}, GPLOT => "EnO_temp4humi4:Temp/Humi,"},
- "A5.10.22" => {attr => {subType => "roomSensorControl.22"}, GPLOT => "EnO_temp4humi4:Temp/Humi,"},
- "A5.10.23" => {attr => {subType => "roomSensorControl.22"}, GPLOT => "EnO_temp4humi4:Temp/Humi,"},
- "A5.11.01" => {attr => {subType => "lightCtrlState.01"}, GPLOT => "EnO_A5-11-01:Dim/Brightness,"},
- "A5.11.02" => {attr => {subType => "tempCtrlState.01"}, GPLOT => "EnO_A5-11-02:SetpointTemp/ControlVar,"},
- "A5.11.03" => {attr => {subType => "shutterCtrlState.01", subDef => "getNextID", subTypeSet => "gateway", gwCmd => "blindCmd", webCmd => "opens:stop:closes:position"}, GPLOT => "EnO_A5-11-03:Position/AnglePos,"},
- "A5.11.04" => {attr => {subType => "lightCtrlState.02", subDef => "getNextID", subTypeSet => "lightCtrl.01", webCmd => "on:off:dim:rgb"}, GPLOT => "EnO_dimFFRGB:DimRGB,"},
- "A5.11.05" => {attr => {subType => "switch.05"}},
- "A5.12.00" => {attr => {subType => "autoMeterReading.00"}, GPLOT => "EnO_A5-12-00:Value/Counter,"},
- "A5.12.01" => {attr => {subType => "autoMeterReading.01"}, GPLOT => "EnO_power4energy4:Power/Energie,"},
- "A5.12.02" => {attr => {subType => "autoMeterReading.02"}, GPLOT => "EnO_A5-12-02:Flowrate/Consumption,"},
- "A5.12.03" => {attr => {subType => "autoMeterReading.03"}, GPLOT => "EnO_A5-12-03:Flowrate/Consumption,"},
- "A5.12.04" => {attr => {subType => "autoMeterReading.04"}, GPLOT => "EnO_A5-12-04:Weight,EnO_A5-12-04_2:Temperature/Battery,"},
- "A5.12.05" => {attr => {subType => "autoMeterReading.05"}, GPLOT => "EnO_A5-12-05:Amount,EnO_A5-12-05_2:Temperature/Battery,"},
- "A5.12.10" => {attr => {subType => "autoMeterReading.10"}, GPLOT => "EnO_A5-12-10:Current/Change,"},
- "A5.13.01" => {attr => {subType => "environmentApp"}, GPLOT => "EnO_A5-13-01:WindSpeed/Raining,EnO_temp4brightness4:Temp/Brightness,"},
- "A5.13.02" => {attr => {subType => "environmentApp"}, GPLOT => "EnO_A5-13-01:SunIntensity,"},
- "A5.13.03" => {attr => {subType => "environmentApp"}},
- "A5.13.04" => {attr => {subType => "environmentApp"}},
- "A5.13.05" => {attr => {subType => "environmentApp"}},
- "A5.13.06" => {attr => {subType => "environmentApp"}},
- "A5.13.07" => {attr => {subType => "windSensor.01"}, GPLOT => "EnO_A5-13-07:WindSpeed,"},
- "A5.13.08" => {attr => {subType => "rainSensor.01"}, GPLOT => "EnO_A5-13-08:Raining,"},
- "A5.13.10" => {attr => {subType => "environmentApp"}, GPLOT => "EnO_solarRadiation4:SolarRadiation,"},
- "A5.14.01" => {attr => {subType => "multiFuncSensor"}, GPLOT => "EnO_A5-14-xx:Voltage/Brightness,EnO_A5-14-xx_2:Contact/Vibration,"},
- "A5.14.02" => {attr => {subType => "multiFuncSensor"}, GPLOT => "EnO_A5-14-xx:Voltage/Brightness,EnO_A5-14-xx_2:Contact/Vibration,"},
- "A5.14.03" => {attr => {subType => "multiFuncSensor"}, GPLOT => "EnO_A5-14-xx:Voltage/Brightness,EnO_A5-14-xx_2:Contact/Vibration,"},
- "A5.14.04" => {attr => {subType => "multiFuncSensor"}, GPLOT => "EnO_A5-14-xx:Voltage/Brightness,EnO_A5-14-xx_2:Contact/Vibration,"},
- "A5.14.05" => {attr => {subType => "multiFuncSensor"}, GPLOT => "EnO_A5-14-xx:Voltage/Brightness,EnO_A5-14-xx_2:Contact/Vibration,"},
- "A5.14.06" => {attr => {subType => "multiFuncSensor"}, GPLOT => "EnO_A5-14-xx:Voltage/Brightness,EnO_A5-14-xx_2:Contact/Vibration,"},
- "A5.14.07" => {attr => {subType => "doorContact"}, GPLOT => "EnO_A5-14-xx:Voltage/Brightness,EnO_A5-14-xx_2:Contact/Vibration,"},
- "A5.14.08" => {attr => {subType => "doorContact"}, GPLOT => "EnO_A5-14-xx:Voltage/Brightness,EnO_A5-14-xx_2:Contact/Vibration,"},
- "A5.14.09" => {attr => {subType => "windowContact"}, GPLOT => "EnO_A5-14-xx:Voltage/Brightness,EnO_A5-14-xx_2:Contact/Vibration,"},
- "A5.14.0A" => {attr => {subType => "windowContact"}, GPLOT => "EnO_A5-14-xx:Voltage/Brightness,EnO_A5-14-xx_2:Contact/Vibration,"},
- "A5.20.01" => {attr => {subType => "hvac.01", webCmd => "setpointTemp"}, GPLOT => "EnO_A5-20-01:Temp/SetpointTemp/Setpoint,EnO_A5-20-01_2:PID,"},
- #"A5.20.02" => {attr => {subType => "hvac.02"}},
- #"A5.20.03" => {attr => {subType => "hvac.03"}},
- "A5.20.04" => {attr => {subType => "hvac.04", webCmd => "setpointTemp"}, GPLOT => "EnO_A5-20-04:Temp/FeedTemp,EnO_A5-20-04_2:SetpointTemp/Setpoint,EnO_A5-20-04_3:PID,"},
- "A5.20.10" => {attr => {subType => "hvac.10", comMode => "biDir", destinationID => "unicast", subDef => "getNextID"}, GPLOT => "EnO_A5-20-10:FanSpeed,"},
- "A5.20.11" => {attr => {subType => "hvac.11", comMode => "biDir", destinationID => "unicast", subDef => "getNextID"}},
- #"A5.20.12" => {attr => {subType => "hvac.12"}},
- "A5.30.01" => {attr => {subType => "digitalInput.01"}, GPLOT => "EnO_A5-30-01:Contact/Battery,"},
- "A5.30.02" => {attr => {subType => "digitalInput.02"}, GPLOT => "EnO_A5-30-02:Contact,"},
- "A5.30.03" => {attr => {subType => "digitalInput.03"}, GPLOT => "EnO_A5-30-03:Contact,EnO_temp4:Temp,"},
- "A5.30.04" => {attr => {subType => "digitalInput.04"}, GPLOT => "EnO_A5-30-04:Contact/Digital,"},
- "A5.30.05" => {attr => {subType => "digitalInput.05"}, GPLOT => "EnO_A5-30-05:Contact/Voltage,"},
- "A5.37.01" => {attr => {subType => "energyManagement.01", webCmd => "level:max"}, GPLOT => "EnO_A5-37-01:Level,"},
- "A5.38.08" => {attr => {subType => "gateway"}},
- "A5.38.09" => {attr => {subType => "lightCtrl.01"}, GPLOT => "EnO_dimFFRGB:DimRGB,"},
- "A5.3F.00" => {attr => {subType => "radioLinkTest", comMode => "biDir", destinationID => "unicast", subDef => "getNextID"}},
- "A5.3F.7F" => {attr => {subType => "manufProfile"}},
- "B0.00.00" => {attr => {subType => "genericProfile"}},
- "C5.00.00" => {attr => {subType => "remote", manufID => "7FF"}},
- "D2.01.00" => {attr => {subType => "actuator.01", defaultChannel => 0}, GPLOT => "EnO_power4energy4:Power/Energie,"},
- "D2.01.01" => {attr => {subType => "actuator.01", defaultChannel => 0}},
- "D2.01.02" => {attr => {subType => "actuator.01", defaultChannel => 0, webCmd => "on:off:dim"}, GPLOT => "EnO_dim4:Dim,EnO_power4energy4:Power/Energie,"},
- "D2.01.03" => {attr => {subType => "actuator.01", defaultChannel => 0, webCmd => "on:off:dim"}, GPLOT => "EnO_dim4:Dim,"},
- "D2.01.04" => {attr => {subType => "actuator.01", defaultChannel => 0, webCmd => "on:off:dim"}, GPLOT => "EnO_dim4:Dim,EnO_power4energy4:Power/Energie,"},
- "D2.01.05" => {attr => {subType => "actuator.01", defaultChannel => 0, webCmd => "on:off:dim"}, GPLOT => "EnO_dim4:Dim,EnO_power4energy4:Power/Energie,"},
- "D2.01.06" => {attr => {subType => "actuator.01", defaultChannel => 0}, GPLOT => "EnO_power4energy4:Power/Energie,"},
- "D2.01.07" => {attr => {subType => "actuator.01", defaultChannel => 0}},
- "D2.01.08" => {attr => {subType => "actuator.01", defaultChannel => 0}, GPLOT => "EnO_power4energy4:Power/Energie,"},
- "D2.01.09" => {attr => {subType => "actuator.01", defaultChannel => 0, webCmd => "on:off:dim"}, GPLOT => "EnO_dim4:Dim,EnO_power4energy4:Power/Energie,"},
- "D2.01.0A" => {attr => {subType => "actuator.01", defaultChannel => 0}},
- "D2.01.0B" => {attr => {subType => "actuator.01", defaultChannel => 0}, GPLOT => "EnO_power4energy4:Power/Energie,"},
- "D2.01.0C" => {attr => {subType => "actuator.01", defaultChannel => 0}, GPLOT => "EnO_power4energy4:Power/Energie,"},
- "D2.01.0D" => {attr => {subType => "actuator.01", defaultChannel => 0}},
- "D2.01.0E" => {attr => {subType => "actuator.01", defaultChannel => 0}, GPLOT => "EnO_power4energy4:Power/Energie,"},
- "D2.01.0F" => {attr => {subType => "actuator.01", defaultChannel => 0}},
- "D2.01.10" => {attr => {subType => "actuator.01", defaultChannel => 0}, GPLOT => "EnO_power4energy4:Power/Energie,"},
- "D2.01.11" => {attr => {subType => "actuator.01", defaultChannel => 0}},
- "D2.01.12" => {attr => {subType => "actuator.01", defaultChannel => 0}},
- "D2.01.13" => {attr => {subType => "actuator.01", defaultChannel => 0}},
- "D2.01.14" => {attr => {subType => "actuator.01", defaultChannel => 0}},
- "D2.03.00" => {attr => {subType => "switch.00"}},
- "D2.03.10" => {attr => {subType => "windowHandle.10"}, GPLOT => "EnO_windowHandle:WindowHandle,"},
- "D2.05.00" => {attr => {subType => "blindsCtrl.00", webCmd => "opens:stop:closes:position"}, GPLOT => "EnO_position4angle4:Position/AnglePos,"},
- "D2.05.01" => {attr => {subType => "blindsCtrl.01", webCmd => "opens:stop:closes:position"}},
- "D2.05.02" => {attr => {subType => "blindsCtrl.00", webCmd => "opens:stop:closes:position"}, GPLOT => "EnO_position4angle4:Position/AnglePos,"},
- "D2.06.01" => {attr => {subType => "multisensor.01"}, GPLOT => "EnO_temp4humi4:Temp/Humi,EnO_brightness4:Brightness,"},
- "D2.10.00" => {attr => {subType => "roomCtrlPanel.00", webCmd => "setpointTemp"}, GPLOT => "EnO_D2-10-xx:Temp/SPT/Humi,"},
- "D2.10.01" => {attr => {subType => "roomCtrlPanel.00", webCmd => "setpointTemp"}, GPLOT => "EnO_D2-10-xx:Temp/SPT/Humi,"},
- "D2.10.02" => {attr => {subType => "roomCtrlPanel.00", webCmd => "setpointTemp"}, GPLOT => "EnO_D2-10-xx:Temp/SPT/Humi,"},
- "D2.11.01" => {attr => {subType => "roomCtrlPanel.01", comMode => "biDir", webCmd => "setpointTemp"}, GPLOT => "EnO_D2-10-xx:Temp/SPT/Humi,"},
- "D2.11.02" => {attr => {subType => "roomCtrlPanel.01", comMode => "biDir", webCmd => "setpointTemp"}, GPLOT => "EnO_D2-10-xx:Temp/SPT/Humi,"},
- "D2.11.03" => {attr => {subType => "roomCtrlPanel.01", comMode => "biDir", webCmd => "setpointTemp"}, GPLOT => "EnO_D2-10-xx:Temp/SPT/Humi,"},
- "D2.11.04" => {attr => {subType => "roomCtrlPanel.01", comMode => "biDir", webCmd => "setpointTemp"}, GPLOT => "EnO_D2-10-xx:Temp/SPT/Humi,"},
- "D2.11.05" => {attr => {subType => "roomCtrlPanel.01", comMode => "biDir", webCmd => "setpointTemp"}, GPLOT => "EnO_D2-10-xx:Temp/SPT/Humi,"},
- "D2.11.06" => {attr => {subType => "roomCtrlPanel.01", comMode => "biDir", webCmd => "setpointTemp"}, GPLOT => "EnO_D2-10-xx:Temp/SPT/Humi,"},
- "D2.11.07" => {attr => {subType => "roomCtrlPanel.01", comMode => "biDir", webCmd => "setpointTemp"}, GPLOT => "EnO_D2-10-xx:Temp/SPT/Humi,"},
- "D2.11.08" => {attr => {subType => "roomCtrlPanel.01", comMode => "biDir", webCmd => "setpointTemp"}, GPLOT => "EnO_D2-10-xx:Temp/SPT/Humi,"},
- "D2.20.00" => {attr => {subType => "fanCtrl.00", webCmd => "fanSpeed"}, GPLOT => "EnO_fanSpeed4humi4:FanSpeed/Humi,"},
- "D2.32.00" => {attr => {subType => "currentClamp.00"}, GPLOT => "EnO_D2-32-xx:Current,"},
- "D2.32.01" => {attr => {subType => "currentClamp.01"}, GPLOT => "EnO_D2-32-xx:Current,"},
- "D2.32.02" => {attr => {subType => "currentClamp.02"}, GPLOT => "EnO_D2-32-xx:Current,"},
- "D2.40.00" => {attr => {subType => "ledCtrlState.00"}, GPLOT => "EnO_dim4:Dim,"},
- "D2.40.01" => {attr => {subType => "ledCtrlState.01"}, GPLOT => "EnO_dim4RGB:DimRGB,"},
- "D2.50.00" => {attr => {subType => "heatRecovery.00", webCmd => "ventilation"}, GPLOT => "EnO_D2-50-xx:Temp/AirQuality,EnO_D2-50-xx_2:AirFlow/FanSpeed,"},
- "D2.50.01" => {attr => {subType => "heatRecovery.00", webCmd => "ventilation"}, GPLOT => "EnO_D2-50-xx:Temp/AirQuality,EnO_D2-50-xx_2:AirFlow/FanSpeed,"},
- "D2.50.10" => {attr => {subType => "heatRecovery.00", webCmd => "ventilation"}, GPLOT => "EnO_D2-50-xx:Temp/AirQuality,EnO_D2-50-xx_2:AirFlow/FanSpeed,"},
- "D2.50.11" => {attr => {subType => "heatRecovery.00", webCmd => "ventilation"}, GPLOT => "EnO_D2-50-xx:Temp/AirQuality,EnO_D2-50-xx_2:AirFlow/FanSpeed,"},
- "D2.A0.01" => {attr => {subType => "valveCtrl.00", defaultChannel => 0, webCmd => "opens:closes"}, GPLOT => "EnO_valveCtrl:Valve,"},
- "D2.B0.51" => {attr => {subType => "liquidLeakage.51"}, GPLOT => "EnO_liquidLeakage:LiquidLeakage,"},
- "D5.00.01" => {attr => {subType => "contact", manufID => "7FF"}, GPLOT => "EnO_contact:Contact,"},
- "F6.01.01" => {attr => {subType => "switch", sensorMode => "pushbutton"}},
- "F6.02.01" => {attr => {subType => "switch"}},
- "F6.02.02" => {attr => {subType => "switch"}},
- "F6.02.03" => {attr => {subType => "switch"}},
- #"F6.02.04" => {attr => {subType => "switch.04"}},
- "F6.03.01" => {attr => {subType => "switch"}},
- "F6.03.02" => {attr => {subType => "switch"}},
- "F6.04.01" => {attr => {subType => "keycard"}, GPLOT => "EnO_keycard:Keycard,"},
- #"F6.04.02" => {attr => {subType => "keycard.02"}, GPLOT => "EnO_keycard:Keycard,"},
- "F6.05.00" => {attr => {subType => "windSpeed.00"}},
- "F6.05.01" => {attr => {subType => "liquidLeakage"}, GPLOT => "EnO_liquidLeakage:LiquidLeakage,"},
- "F6.05.02" => {attr => {subType => "smokeDetector.02"}},
- "F6.10.00" => {attr => {subType => "windowHandle"}, GPLOT => "EnO_windowHandle:WindowHandle,"},
- #"F6.10.01" => {attr => {subType => "windowHandle.01"}, GPLOT => "EnO_windowHandle:WindowHandle,"},
- "F6.3F.7F" => {attr => {subType => "switch.7F"}},
- # special profiles
- "G5.07.01" => {attr => {subType => "occupSensor.01", eep => "A5-07-01", manufID => "00D", model => 'tracker'}, GPLOT => "EnO_motion:Motion,EnO_voltage4current4:Voltage/Current,"},
- "G5.10.12" => {attr => {subType => "roomSensorControl.01", eep => "A5-10-12", manufID => "00D", scaleMax => 40, scaleMin => 0, scaleDecimals => 1}, GPLOT => "EnO_temp4humi6:Temp/Humi,"},
- "G5.38.08" => {attr => {subType => "gateway", eep => "A5-38-08", gwCmd => "dimming", manufID => "00D", webCmd => "on:off:dim"}, GPLOT => "EnO_dim4:Dim,"},
- "H5.38.08" => {attr => {subType => "gateway", comMode => "confirm", eep => "A5-38-08", gwCmd => "dimming", manufID => "00D", model => "Eltako_TF", teachMethod => "confirm", webCmd => "on:off:dim"}, GPLOT => "EnO_dim4:Dim,"},
- "G5.3F.7F" => {attr => {subType => "manufProfile", eep => "A5-3F-7F", manufID => "00D", webCmd => "opens:stop:closes"}},
- "H5.3F.7F" => {attr => {subType => "manufProfile", comMode => "confirm", eep => "A5-3F-7F", manufID => "00D", model => "Eltako_TF", sensorMode => 'pushbutton', settingAccuracy => "high", teachMethod => "confirm", webCmd => "opens:stop:closes"}},
- "M5.38.08" => {attr => {subType => "gateway", eep => "A5-38-08", gwCmd => "switching", manufID => "00D", webCmd => "on:off"}},
- "N5.38.08" => {attr => {subType => "gateway", comMode => "confirm", eep => "A5-38-08", gwCmd => "switching", manufID => "00D", model => "Eltako_TF", teachMethod => "confirm", webCmd => "on:off"}},
- "G5.ZZ.ZZ" => {attr => {subType => "PM101", manufID => "005"}, GPLOT => "EnO_motion:Motion,EnO_brightness4:Brightness,"},
- "L6.02.01" => {attr => {subType => "smokeDetector.02", eep => "F6-05-02", manufID => "00D"}},
- "ZZ.ZZ.ZZ" => {attr => {subType => "raw"}},
- );
- my %EnO_extendedRemoteFunctionCode = (
- 0x210 => "remoteLinkTableInfo", # get
- 0x211 => "remoteLinkTable", # get
- 0x212 => "remoteLinkTable", # set
- 0x213 => "remoteLinkTableGP", # get
- 0x214 => "remoteLinkTableGP", # set
- 0x220 => "remoteLearnMode", # set
- 0x221 => "remoteTeach", # set
- 0x224 => "remoteReset", # set
- 0x225 => "remoteRLT", # set
- 0x226 => "remoteApplyChanges", # set
- 0x227 => "remoteProductID", # get
- 0x230 => "remoteDevCfg", # get
- 0x231 => "remoteDevCfg", # set
- 0x232 => "remoteLinkCfg", # get
- 0x233 => "remoteLinkCfg", # set
- 0x240 => "remoteAck", # parse
- 0x250 => "remoteRepeater", # get
- 0x251 => "remoteRepeater", # set
- 0x252 => "remoteRepeaterFilter" # set
- );
- my %EnO_models = (
- "Eltako_FAE14" => {attr => {manufID => "00D"}},
- "Eltako_FHK14" => {attr => {manufID => "00D"}},
- "Eltako_FHK61" => {attr => {manufID => "00D"}},
- "Eltako_FSA12" => {attr => {manufID => "00D"}},
- "Eltako_FSB14" => {attr => {manufID => "00D"}},
- "Eltako_FSB61" => {attr => {manufID => "00D"}},
- "Eltako_FSB70" => {attr => {manufID => "00D"}},
- "Eltako_FSM12" => {attr => {manufID => "00D"}},
- "Eltako_FSM61" => {attr => {manufID => "00D"}},
- "Eltako_FT55" => {attr => {manufID => "00D"}},
- "Eltako_FTS12" => {attr => {manufID => "00D"}},
- "Eltako_TF"=> {attr => {manufID => "00D"}},
- "Holter_OEM" => {attr => {pidCtrl => "off"}},
- "Micropelt_MVA004" => {attr => {remoteCode => "FFFFFFFE", remoteEEP => "A5-20-01", remoteID => "getNextID", remoteManagement => "manager"}, xml => {productID => "0x004900000000", xmlDescrLocation => "/FHEM/lib/EnO_ReCom_Device_Descr.xml"}},
- other => {},
- tracker => {}
- );
- my @EnO_defaultChannel = ("all", "input", 0..29);
- my %wakeUpCycle = (
- 10 => 0,
- 60 => 1,
- 90 => 2,
- 120 => 3,
- 150 => 4,
- 180 => 5,
- 210 => 6,
- 240 => 7,
- 270 => 8,
- 300 => 9,
- 330 => 10,
- 360 => 11,
- 390 => 12,
- 420 => 13,
- 450 => 14,
- 480 => 15,
- 510 => 16,
- 540 => 17,
- 570 => 18,
- 600 => 19,
- 630 => 20,
- 660 => 21,
- 690 => 22,
- 720 => 23,
- 750 => 24,
- 780 => 25,
- 810 => 26,
- 840 => 27,
- 870 => 28,
- 900 => 29,
- 930 => 30,
- 960 => 31,
- 990 => 32,
- 1020 => 33,
- 1050 => 34,
- 1080 => 35,
- 1110 => 36,
- 1140 => 37,
- 1170 => 38,
- 1200 => 39,
- 1230 => 40,
- 1260 => 41,
- 1290 => 42,
- 1320 => 43,
- 1350 => 44,
- 1380 => 45,
- 1410 => 46,
- 1440 => 47,
- 1470 => 48,
- 1500 => 49,
- 10800 => 50,
- 21600 => 51,
- 32400 => 52,
- 43200 => 53,
- 54000 => 54,
- 64800 => 55,
- 75600 => 56,
- 86400 => 57,
- 97200 => 58,
- 108000 => 59,
- 118800 => 60,
- 129600 => 61,
- 140400 => 62,
- 151200 => 63,
- );
- my %wakeUpCycleInv = (
- 0 => 10,
- 1 => 60,
- 2 => 90,
- 3 => 120,
- 4 => 150,
- 5 => 180,
- 6 => 210,
- 7 => 240,
- 8 => 270,
- 9 => 300,
- 10 => 330,
- 11 => 360,
- 12 => 390,
- 13 => 420,
- 14 => 450,
- 15 => 480,
- 16 => 510,
- 17 => 540,
- 18 => 570,
- 19 => 600,
- 20 => 630,
- 21 => 660,
- 22 => 690,
- 23 => 720,
- 24 => 750,
- 25 => 780,
- 26 => 810,
- 27 => 840,
- 28 => 870,
- 29 => 900,
- 30 => 930,
- 31 => 960,
- 32 => 990,
- 33 => 1020,
- 34 => 1050,
- 35 => 1080,
- 36 => 1110,
- 37 => 1140,
- 38 => 1170,
- 39 => 1200,
- 40 => 1230,
- 41 => 1260,
- 42 => 1290,
- 43 => 1320,
- 44 => 1350,
- 45 => 1380,
- 46 => 1410,
- 47 => 1440,
- 48 => 1470,
- 49 => 1500,
- 50 => 10800,
- 51 => 21600,
- 52 => 32400,
- 53 => 43200,
- 54 => 54000,
- 55 => 64800,
- 56 => 75600,
- 57 => 86400,
- 58 => 97200,
- 59 => 108000,
- 60 => 118800,
- 61 => 129600,
- 62 => 140400,
- 63 => 151200,
- );
- my @EnO_resolution = (1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32);
- my @EnO_scaling = (0, 1, 10, 100, 1000, 10000, 100000, 1e6, 1e7, 0.1, 0.01, 0.001, 1e-6, 1e-9);
- my %EnO_gpValueEnum = (
- 1 => {name => "multipurpose"},
- 2 => {name => "buildingMode", enum => {0 => "in_use", 1 => "not_used", 2 => "protection"},
- enumInv => {"in_use" => 0, "not_used" => 1, "protection" => 2}},
- 3 => {name => "occupanyMode", enum => {0 => "occupied", 1 => "standby", 2 => "not_occupied"},
- enumInv => {"occupied" => 0,"standby" => 1,"not_occupied" => 2}},
- 4 => {name => "hvacMode", enum => {0 => "auto", 1 => "comfort", 2 => "standby", 3 => "economy", 4 => "building_protection"},
- enumInv => {"auto" => 0, "comfort" => 1, "standby" => 2, "economy" => 3, "building_protection" => 4}},
- 5 => {name => "changeoverMode", enum => {0 => "auto", 1 => "cooling", 2 => "heating"},
- enumInv => {"auto" => 0,"cooling" => 1,"heating" => 2}},
- );
- my %EnO_gpValueFlag = (
- 1 => {name => "auto/man", flag => {0 => "man", 1 => "auto"},
- flagInv => {"man" => 0,"auto" => 1}},
- 2 => {name => "buttonPressed", flag => {0 => "not_pressed", 1 => "pressed"},
- flagInv => {"not_pressed" => 0,"pressed" => 1}},
- 3 => {name => "buttonChanged", flag => {0 => "no_change", 1 => "change"},
- flagInv => {"no_change" => 0,"change" => 1}},
- 4 => {name => "day/night", flag => {0 => "night", 1 => "day"},
- flagInv => {"night" => 0,"day" => 1}},
- 5 => {name => "down", flag => {0 => "no_change", 1 => "down"},
- flagInv => {"no_change" => 0,"down" => 1}},
- 6 => {name => "generalAlarm", flag => {0 => "manual", 1 => "alarm"},
- flagInv => {"manual" => 0,"alarm" => 1}},
- 7 => {name => "heat/cool", flag => {0 => "cool", 1 => "heat"},
- flagInv => {"cool" => 0,"heat" => 1}},
- 8 => {name => "high/low", flag => {0 => "low", 1 => "high"},
- flagInv => {"low" => 0,"high" => 1}},
- 9 => {name => "occupancy", flag => {0 => "unoccupied", 1 => "occupied"},
- flagInv => {"unoccupied" => 0,"occupied" => 1}},
- 10 => {name => "on/off", flag => {0 => "off", 1 => "on"},
- flagInv => {"off" => 0,"on" => 1}},
- 11 => {name => "open/closed", flag => {0 => "closed", 1 => "open"},
- flagInv => {"closed" => 0,"open" => 1}},
- 12 => {name => "powerAlarm", flag => {0 => "no_change", 1 => "alarm"},
- flagInv => {"no_change" => 0,"alarm" => 1}},
- 13 => {name => "start/stop", flag => {0 => "stop", 1 => "start"},
- flagInv => {"stop" => 0,"start" => 1}},
- 14 => {name => "up", flag => {0 => "no_change", 1 => "up"},
- flagInv => {"no_change" => 0,"up" => 1}},
- );
- my %EnO_gpValueData = (
- 1 => {name => "acceleration", unit => "m/s2"},
- 2 => {name => "angle", unit => "deg"},
- 3 => {name => "angular_velocity", unit => "rad/s"},
- 4 => {name => "area", unit => "m²"},
- 5 => {name => "concentration", unit => "ppm"},
- 6 => {name => "current", unit => "A"},
- 7 => {name => "distance", unit => "m"},
- 8 => {name => "electric_field_strength", unit => "V/m"},
- 9 => {name => "energy", unit => "J"},
- 10 => {name => "number", unit => "N/A"},
- 11 => {name => "force", unit => "N"},
- 12 => {name => "frequency", unit => "Hz"},
- 13 => {name => "heat_flux_density", unit => "W/m2"},
- 14 => {name => "impulse", unit => "Ns"},
- 15 => {name => "luminance_intensity", unit => "lux"},
- 16 => {name => "magnetic_field_strength", unit => "A/m"},
- 17 => {name => "mass", unit => "kg"},
- 18 => {name => "mass_density", unit => "kg/m2"},
- 19 => {name => "mass_flow", unit => "kg/s"},
- 20 => {name => "power", unit => "W"},
- 21 => {name => "pressure", unit => "Pa"},
- 22 => {name => "relative_humidity", unit => "%"},
- 23 => {name => "resistance", unit => "Ohm"},
- 24 => {name => "temperature", unit => "C"},
- 25 => {name => "time", unit => "s"},
- 26 => {name => "torque", unit => "Nm"},
- 27 => {name => "velocity", unit => "m/s"},
- 28 => {name => "voltage", unit => "V"},
- 29 => {name => "volume", unit => "m3"},
- 30 => {name => "volumetric_flow", unit => "m3/s"},
- );
- # Initialize
- sub
- EnOcean_Initialize($)
- {
- my ($hash) = @_;
- my %subTypeList;
- my @subTypeList;
- foreach my $eep (keys %EnO_eepConfig){
- push @subTypeList, $EnO_eepConfig{$eep}{attr}{subType};
- }
- my $subTypeList = join(",", sort grep { !$subTypeList{$_}++ } @subTypeList);
- $hash->{AutoCreate} = {"EnO.*" => {ATTR => "creator:autocreate", FILTER => "%NAME"}};
- $hash->{noAutocreatedFilelog} = 1;
- $hash->{Match} = "^EnOcean:";
- $hash->{DefFn} = "EnOcean_Define";
- $hash->{DeleteFn} = "EnOcean_Delete";
- $hash->{UndefFn} = "EnOcean_Undef";
- $hash->{ParseFn} = "EnOcean_Parse";
- $hash->{SetFn} = "EnOcean_Set";
- #$hash->{StateFn} = "EnOcean_State";
- $hash->{GetFn} = "EnOcean_Get";
- $hash->{NotifyFn} = "EnOcean_Notify";
- $hash->{AttrFn} = "EnOcean_Attr";
- $hash->{AttrList} = "IODev do_not_notify:1,0 ignore:0,1 dummy:0,1 " .
- "showtime:1,0 " .
- "actualTemp angleMax:slider,-180,20,180 alarmAction " .
- "angleMin:slider,-180,20,180 " .
- "angleTime setCmdTrigger:man,refDev blockUnknownMSC:no,yes blockMotion:no,yes " .
- "blockTemp:no,yes blockDisplay:no,yes blockDateTime:no,yes " .
- "blockTimeProgram:no,yes blockOccupancy:no,yes blockSetpointTemp:no,yes " .
- "blockFanSpeed:no,yes blockKey:no,yes comMode:confirm,biDir,uniDir creator:autocreate,manual " .
- "daylightSavingTime:supported,not_supported dataEnc:VAES,AES-CBC " .
- "defaultChannel:" . join(",", @EnO_defaultChannel) . " " .
- "demandRespAction demandRespRefDev demandRespMax:A0,AI,B0,BI,C0,CI,D0,DI ".
- "demandRespMin:A0,AI,B0,BI,C0,CI,D0,DI demandRespRandomTime " .
- "demandRespThreshold:slider,0,1,15 demandRespTimeoutLevel:max,last destinationID " .
- "devChannel devMode:master,slave devUpdate:off,auto,demand,polling,interrupt " .
- "dimMax dimMin dimValueOn disable:0,1 disabledForIntervals " .
- "displayContent:default,humidity,off,setpointTemp,tempertureExtern,temperatureIntern,time,no_change " .
- "displayOrientation:0,90,180,270 " .
- "eep gpDef gwCmd:" . join(",", sort @EnO_gwCmd) . " humitity humidityRefDev " .
- "keyRcv keySnd macAlgo:no,3,4 measurementCtrl:disable,enable " .
- "manufID:" . join(",", sort keys %EnO_manuf) . " " .
- "model:" . join(",", sort keys %EnO_models) . " " .
- "observe:on,off observeCmdRepetition:1,2,3,4,5 observeErrorAction observeInterval observeLogic:and,or " .
- #observeCmds observeExeptions
- "observeRefDev pidActorErrorAction:errorPos,freeze pidActorCallBeforeSetting pidActorErrorPos " .
- "pidActorLimitLower pidActorLimitUpper pidCtrl:on,off pidDeltaTreshold pidFactor_D pidFactor_I " .
- "pidFactor_P pidIPortionCallBeforeSetting pidSensorTimeout " .
- "pollInterval postmasterID productID rampTime rcvRespAction ".
- "releasedChannel:A,B,C,D,I,0,auto repeatingAllowed:yes,no remoteCode remoteEEP remoteID remoteManufID " .
- "remoteManagement:client,manager,off rlcAlgo:no,2++,3++ rlcRcv rlcSnd rlcTX:true,false " .
- "reposition:directly,opens,closes rltRepeat:16,32,64,128,256 rltType:1BS,4BS " .
- "scaleDecimals:0,1,2,3,4,5,6,7,8,9 scaleMax scaleMin secMode:rcv,snd,bidir " .
- "secLevel:encapsulation,encryption,off sendDevStatus:no,yes sensorMode:switch,pushbutton " .
- "serviceOn:no,yes settingAccuracy:high,low setpointRefDev setpointSummerMode:slider,0,5,100 " .
- "setpointTempRefDev shutTime shutTimeCloses subDef " .
- "subDef0 subDefI subDefA subDefB subDefC subDefD subDefH subDefW " .
- "subType:$subTypeList subTypeSet:$subTypeList subTypeReading:$subTypeList " .
- "summerMode:off,on switchMode:switch,pushbutton " .
- "switchHysteresis switchType:direction,universal,channel,central " .
- "teachMethod:1BS,4BS,confirm,GP,RPS,smartAck,STE,UTE temperatureRefDev " .
- "temperatureScale:C,F,default,no_change timeNotation:12,24,default,no_change " .
- "timeProgram1 timeProgram2 timeProgram3 timeProgram4 trackerWakeUpCycle:10,20,30,40,60,120,180,240,3600,86400 updateState:default,yes,no " .
- "uteResponseRequest:yes,no " .
- "wakeUpCycle:" . join(",", keys %wakeUpCycle) . " " .
- $readingFnAttributes;
- for (my $i = 0; $i < @EnO_ptm200btn; $i++) {
- $EnO_ptm200btn{$EnO_ptm200btn[$i]} = "$i:30";
- }
- $EnO_ptm200btn{released} = "0:20";
- if ($cryptFunc == 1){
- Log3 undef, 2, "EnOcean Cryptographic functions available.";
- } else {
- Log3 undef, 2, "EnOcean Cryptographic functions are not available.";
- }
- if ($xmlFunc == 1){
- Log3 undef, 2, "EnOcean XML functions available.";
- } else {
- Log3 undef, 2, "EnOcean XML functions are not available.";
- }
- return undef;
- }
- # Define
- sub EnOcean_Define($$) {
- my ($hash, $def) = @_;
- my @a = split("[ \t][ \t]*", $def);
- my $name = $hash->{NAME};
- my ($autocreateFilelog, $autocreateHash, $autocreateName, $autocreateDeviceRoom, $autocreateWeblinkRoom) =
- ('./log/' . $name . '-%Y.log', undef, 'autocreate', 'EnOcean', 'Plots');
- my ($cmd, $eep, $ret);
- my $filelogName = "FileLog_$name";
- $def = "00000000";
- if(@a > 2 && @a < 5) {
- # find autocreate device
- while (($autocreateName, $autocreateHash) = each(%defs)) {
- last if ($defs{$autocreateName}{TYPE} eq "autocreate");
- }
- $autocreateDeviceRoom = AttrVal($autocreateName, "device_room", $autocreateDeviceRoom) if (defined $autocreateName);
- $autocreateDeviceRoom = 'EnOcean' if ($autocreateDeviceRoom eq '%TYPE');
- $autocreateDeviceRoom = $name if ($autocreateDeviceRoom eq '%NAME');
- $autocreateDeviceRoom = AttrVal($name, "room", $autocreateDeviceRoom);
- if ($init_done) {
- Log3 $name, 2, "EnOcean define " . join(' ', @a);
- if (!defined(AttrVal($autocreateName, "disable", undef)) && !exists($defs{$filelogName})) {
- # create FileLog
- $autocreateFilelog = $attr{$autocreateName}{filelog} if (exists $attr{$autocreateName}{filelog});
- $autocreateFilelog =~ s/%NAME/$name/g;
- $cmd = "$filelogName FileLog $autocreateFilelog $name";
- Log3 $filelogName, 2, "EnOcean define $cmd";
- $ret = CommandDefine(undef, $cmd);
- if($ret) {
- Log3 $filelogName, 2, "EnOcean ERROR: $ret";
- } else {
- $attr{$filelogName}{room} = $autocreateDeviceRoom;
- $attr{$filelogName}{logtype} = 'text';
- }
- }
- }
- if ($a[2] eq "getNextID") {
- AssignIoPort($hash) if (!exists $hash->{IODev});
- if (exists $hash->{OLDDEF}) {
- delete $modules{EnOcean}{defptr}{$hash->{OLDDEF}};
- }
- $hash->{DEF} = $def;
- $def = EnOcean_CheckSenderID("getNextID", $hash->{IODev}{NAME}, "00000000");
- $hash->{DEF} = $def;
- $modules{EnOcean}{defptr}{$def} = $hash;
- $attr{$name}{manufID} = "7FF" if (!exists $attr{$name}{manufID});
- $attr{$name}{room} = $autocreateDeviceRoom;
- $attr{$name}{subType} = "raw" if (!exists $attr{$name}{subType});
- } elsif ($a[2] =~ m/^[A-Fa-f0-9]{8}$/i) {
- # DestinationID
- $def = uc($a[2]);
- $hash->{DEF} = $def;
- if (defined($a[3]) && $a[3] =~ m/^([A-Za-z0-9]{2})-([A-Za-z0-9]{2})-([A-Za-z0-9]{2})$/i) {
- # EEP
- my ($rorg, $func, $type) = (uc($1), uc($2), uc($3));
- $rorg = "F6" if ($rorg eq "05");
- $rorg = "D5" if ($rorg eq "06");
- $rorg = "A5" if ($rorg eq "07");
- $eep = "$rorg.$func.$type";
- if (exists $EnO_eepConfig{$eep}) {
- if ($eep eq 'A5.3F.00') {
- my ($rltHash, $rltName);
- foreach my $dev (keys %defs) {
- next if ($defs{$dev}{TYPE} ne 'EnOcean');
- next if (!exists($attr{$dev}{subType}));
- next if ($attr{$dev}{subType} ne 'radioLinkTest');
- $rltHash = $defs{$dev};
- $rltName = $rltHash->{NAME};
- last;
- }
- return "Radio Link Test device already defined, use $rltName" if ($rltHash);
- }
- AssignIoPort($hash) if (!exists $hash->{IODev});
- if (exists $hash->{OLDDEF}) {
- delete $modules{EnOcean}{defptr}{$hash->{OLDDEF}};
- }
- $modules{EnOcean}{defptr}{$def} = $hash;
- if (exists($attr{$name}{eep}) && $attr{$name}{eep} ne "$rorg-$func-$type") {
- delete $attr{$name};
- $attr{$filelogName}{logtype} = $EnO_eepConfig{$eep}{GPLOT} . 'text'
- if (exists $attr{$filelogName}{logtype});
- # delete SVG devices
- my ($weblinkName, $weblinkHash);
- while (($weblinkName, $weblinkHash) = each(%defs)) {
- if ($weblinkName =~ /^SVG_$name.*/) {
- CommandDelete(undef, $weblinkName);
- Log3 $hash->{NAME}, 2, "EnOcean $weblinkName deleted";
- }
- }
- }
- $attr{$name}{eep} = "$rorg-$func-$type";
- $attr{$name}{manufID} = "7FF" if (!exists $attr{$name}{manufID});
- $attr{$name}{room} = $autocreateDeviceRoom;
- foreach my $attrCntr (keys %{$EnO_eepConfig{$eep}{attr}}) {
- if ($attrCntr eq "subDef") {
- if (!exists $attr{$name}{$attrCntr}) {
- $attr{$name}{$attrCntr} = EnOcean_CheckSenderID($EnO_eepConfig{$eep}{attr}{$attrCntr}, $hash->{IODev}{NAME}, "00000000");
- }
- } else {
- $attr{$name}{$attrCntr} = $EnO_eepConfig{$eep}{attr}{$attrCntr};
- }
- }
- EnOcean_CreateSVG(undef, $hash, $a[3]);
- } else {
- return "EEP $rorg-$func-$type not supported";
- }
- } elsif (defined($a[3]) && $a[3] =~ m/^EnOcean:.*/) {
- # autocreate: parse received device telegram
- AssignIoPort($hash) if (!exists $hash->{IODev});
- $modules{EnOcean}{defptr}{$def} = $hash;
- my @msg = split(':', $a[3]);
- my $packetType = hex $msg[1];
- if ($packetType == 1) {
- my ($data, $rorg, $status);
- #EnOcean:PacketType:RORG:MessageData:SourceID:Status:OptionalData
- (undef, undef, $rorg, $data, undef, $status, undef) = @msg;
- $attr{$name}{subType} = $EnO_rorgname{$rorg};
- if ($attr{$name}{subType} eq "switch") {
- my $nu = (hex($status) & 0x10) >> 4;
- my $t21 = (hex($status) & 0x20) >> 5;
- $attr{$name}{manufID} = "7FF";
- if ($t21 && $nu) {
- $attr{$name}{eep} = "F6-02-01";
- readingsSingleUpdate($hash, "teach", "RPS teach-in accepted EEP F6-02-01 Manufacturer: no ID", 1);
- $attr{$name}{teachMethod} = 'RPS';
- Log3 $name, 2, "EnOcean $name teach-in EEP F6-02-01 Manufacturer: no ID";
- } elsif (!$t21 && $nu) {
- $attr{$name}{eep} = "F6-03-01";
- readingsSingleUpdate($hash, "teach", "RPS teach-in accepted EEP F6-03-01 Manufacturer: no ID", 1);
- $attr{$name}{teachMethod} = 'RPS';
- Log3 $name, 2, "EnOcean $name teach-in EEP F6-03-01 Manufacturer: no ID";
- } elsif ($t21 && !$nu) {
- $attr{$name}{subType} = "windowHandle";
- $attr{$name}{eep} = "F6-10-00";
- readingsSingleUpdate($hash, "teach", "RPS teach-in accepted EEP F6-10-00 Manufacturer: no ID", 1);
- $attr{$name}{teachMethod} = 'RPS';
- Log3 $name, 2, "EnOcean $name teach-in EEP F6-10-00 Manufacturer: no ID";
- }
- } elsif ($attr{$name}{subType} eq "contact" && hex($data) & 8) {
- $attr{$name}{eep} = "D5-00-01";
- $attr{$name}{manufID} = "7FF";
- readingsSingleUpdate($hash, "teach", "1BS teach-in accepted EEP D5-00-01 Manufacturer: no ID", 1);
- $attr{$name}{teachMethod} = '1BS';
- Log3 $name, 2, "EnOcean $name teach-in EEP D5-00-01 Manufacturer: no ID";
- } elsif ($attr{$name}{subType} eq "4BS" && hex(substr($data, 6, 2)) & 8) {
- $hash->{helper}{teachInWait} = "4BS";
- readingsSingleUpdate($hash, "teach", "4BS teach-in is missing", 1);
- Log3 $name, 2, "EnOcean $name 4BS teach-in is missing";
- } elsif ($attr{$name}{subType} eq "UTE") {
- $hash->{helper}{teachInWait} = "UTE";
- } elsif ($attr{$name}{subType} eq "VLD") {
- $hash->{helper}{teachInWait} = "UTE";
- readingsSingleUpdate($hash, "teach", "UTE teach-in is missing", 1);
- Log3 $name, 2, "EnOcean $name UTE teach-in is missing";
- } elsif ($attr{$name}{subType} eq "MSC") {
- readingsSingleUpdate($hash, "teach", "MSC not supported", 1);
- Log3 $name, 2, "EnOcean $name MSC not supported";
- } elsif ($attr{$name}{subType} =~ m/^SEC|ENC$/) {
- $hash->{helper}{teachInWait} = "STE";
- readingsSingleUpdate($hash, "teach", "STE teach-in is missing", 1);
- Log3 $name, 2, "EnOcean $name STE teach-in is missing";
- } elsif ($attr{$name}{subType} =~ m/^GPCD|GPSD$/) {
- $hash->{helper}{teachInWait} = "GPTI";
- readingsSingleUpdate($hash, "teach", "GP teach-in is missing", 1);
- Log3 $name, 2, "EnOcean $name GP teach-in is missing";
- }
- } elsif ($packetType == 4) {
- $hash->{helper}{smartAckLearnWait} = $name;
- } elsif ($packetType == 7) {
- # remote management
- #EnOcean:PacketType:RORG:MessageData:SourceID:DestinationID:FunctionNumber:ManufacturerID:RSSI:Delay
- if (hex($msg[6]) < 0x600) {
- $attr{$name}{remoteManagement} = 'client';
- # unlock device to send acknowledgment telegram
- $hash->{RemoteClientUnlock} = 1;
- my %functionHash = (hash => $hash, param => 'RemoteClientUnlock');
- RemoveInternalTimer(\%functionHash);
- InternalTimer(gettimeofday() + 1, 'EnOcean_cdmClearHashVal', \%functionHash, 0);
- } else {
- $attr{$name}{remoteManagement} = 'manager';
- }
- $attr{$name}{eep} = 'C5-00-00';
- $attr{$name}{manufID} = substr($msg[7], 1);
- $attr{$name}{remoteEEP} = 'C5-00-00';
- $attr{$name}{remoteID} = $msg[4];
- $attr{$name}{remoteManufID} = substr($msg[7], 1);
- $attr{$name}{subType} = 'remote';
- $modules{EnOcean}{defptr}{$msg[4]} = $hash;
- }
- EnOcean_Parse($hash, $a[3]);
- if (exists $attr{$name}{eep}) {
- $attr{$name}{eep} =~ m/^([A-Za-z0-9]{2})-([A-Za-z0-9]{2})-([A-Za-z0-9]{2})$/i;
- $eep = uc("$1.$2.$3");
- if (exists($attr{$filelogName}{logtype}) && exists($EnO_eepConfig{$eep}{GPLOT})) {
- $attr{$filelogName}{logtype} = $EnO_eepConfig{$eep}{GPLOT} . 'text';
- EnOcean_CreateSVG(undef, $hash, undef);
- }
- }
- } else {
- # no device infos
- AssignIoPort($hash) if (!exists $hash->{IODev});
- # assign defptr
- if (exists $hash->{OLDDEF}) {
- delete $modules{EnOcean}{defptr}{$hash->{OLDDEF}};
- }
- $modules{EnOcean}{defptr}{$def} = $hash;
- $attr{$name}{manufID} = "7FF" if (!exists $attr{$name}{manufID});
- $attr{$name}{room} = $autocreateDeviceRoom;
- $attr{$name}{subType} = "raw" if (!exists $attr{$name}{subType});
- }
- } elsif ($a[2] =~ m/^([A-Za-z0-9]{2})-([A-Za-z0-9]{2})-([A-Za-z0-9]{2})$/i) {
- # EEP
- my ($rorg, $func, $type) = (uc($1), uc($2), uc($3));
- $rorg = "F6" if ($rorg eq "05");
- $rorg = "D5" if ($rorg eq "06");
- $rorg = "A5" if ($rorg eq "07");
- $eep = "$rorg.$func.$type";
- if (exists $EnO_eepConfig{$eep}) {
- if ($eep eq 'A5.3F.00') {
- # radio link test device
- my ($rltHash, $rltName);
- foreach my $dev (keys %defs) {
- next if ($defs{$dev}{TYPE} ne 'EnOcean');
- next if (!exists($attr{$dev}{subType}));
- next if ($attr{$dev}{subType} ne 'radioLinkTest');
- $rltHash = $defs{$dev};
- $rltName = $rltHash->{NAME};
- last;
- }
- return "Radio Link Test device already defined, use $rltName" if ($rltHash);
- }
- AssignIoPort($hash) if (!exists $hash->{IODev});
- if (exists($hash->{OLDDEF}) && $hash->{OLDDEF} =~ m/^[A-Fa-f0-9]{8}$/i) {
- delete $modules{EnOcean}{defptr}{$hash->{OLDDEF}};
- if ($hash->{DEF} =~ m/^([A-Za-z0-9]{2})-([A-Za-z0-9]{2})-([A-Za-z0-9]{2})$/i) {
- $def = $hash->{OLDDEF};
- $hash->{DEF} = $hash->{OLDDEF};
- }
- } else {
- $hash->{DEF} = $def;
- if ($eep eq 'A5.3F.00') {
- $attr{$name}{subDef} = EnOcean_CheckSenderID("getNextID", $hash->{IODev}{NAME}, "00000000");
- } elsif ($eep eq 'C5.00.00') {
- # remote management
- } else {
- $def = EnOcean_CheckSenderID("getNextID", $hash->{IODev}{NAME}, "00000000");
- $hash->{DEF} = $def;
- }
- }
- $modules{EnOcean}{defptr}{$def} = $hash;
- if (exists($attr{$name}{eep}) && $attr{$name}{eep} ne "$rorg-$func-$type") {
- delete $attr{$name};
- if (exists $attr{$filelogName}{logtype}) {
- if (exists$EnO_eepConfig{$eep}{GPLOT}) {
- $attr{$filelogName}{logtype} = $EnO_eepConfig{$eep}{GPLOT} . 'text';
- } else {
- $attr{$filelogName}{logtype} = 'text';
- }
- }
- }
- $attr{$name}{eep} = "$rorg-$func-$type";
- $attr{$name}{manufID} = "7FF" if (!exists $attr{$name}{manufID});
- $attr{$name}{room} = $autocreateDeviceRoom;
- foreach my $attrCntr (keys %{$EnO_eepConfig{$eep}{attr}}) {
- if ($attrCntr ne "subDef") {
- $attr{$name}{$attrCntr} = $EnO_eepConfig{$eep}{attr}{$attrCntr};
- }
- }
- EnOcean_CreateSVG('del', $hash, $a[2]);
- } else {
- return "EEP $rorg-$func-$type not supported";
- }
- } else {
- return "wrong syntax: define <name> EnOcean <8-digit-hex-code> [<EEP>]|getNextID|<EEP>";
- }
- } else {
- return "wrong syntax: define <name> EnOcean <8-digit-hex-code> [<EEP>]|getNextID|<EEP>";
- }
- # device specific actions
- if (exists($attr{$name}{subType}) && $attr{$name}{subType} =~ m/^hvac\.0(1|4)$/) {
- # pid parameter
- @{$hash->{helper}{calcPID}} = (undef, $hash, 'defined');
- $hash->{helper}{stopped} = 0;
- #$hash->{helper}{adjust} = '';
- }
- # all notifys needed
- #$hash->{NOTIFYDEV} = "global";
- Log3 $name, 5, "EnOcean_define for device $name executed.";
- return undef;
- }
- # Get
- sub EnOcean_Get($@)
- {
- my ($hash, @a) = @_;
- return "no get value specified" if (@a < 2);
- my $name = $hash->{NAME};
- if (IsDisabled($name)) {
- Log3 $name, 4, "EnOcean $name get commands disabled.";
- return;
- }
- my $cmdID;
- my $cmdList = "";
- my $data;
- my $destinationID = AttrVal($name, "destinationID", undef);
- if (AttrVal($name, "comMode", "uniDir") eq "biDir") {
- $destinationID = defined(AttrVal($name, "subDef", undef)) ? $hash->{DEF} : "FFFFFFFF";
- $destinationID = "FFFFFFFF" if (uc(AttrVal($name, "subDef", $hash->{DEF})) eq uc($hash->{DEF}));
- } elsif (!defined $destinationID || $destinationID eq "multicast") {
- $destinationID = "FFFFFFFF";
- } elsif ($destinationID eq "unicast") {
- $destinationID = defined(AttrVal($name, "subDef", undef)) ? $hash->{DEF} : "FFFFFFFF";
- $destinationID = "FFFFFFFF" if (uc(AttrVal($name, "subDef", $hash->{DEF})) eq uc($hash->{DEF}));
- } elsif ($destinationID !~ m/^[\dA-Fa-f]{8}$/) {
- return "DestinationID $destinationID wrong, choose <8-digit-hex-code>.";
- }
- $destinationID = uc($destinationID);
- my $eep = uc(AttrVal($name, "eep", "00-00-00"));
- if ($eep =~ m/^([A-Fa-f0-9]{2})-([A-Fa-f0-9]{2})-([A-Fa-f0-9]{2})$/i) {
- $eep = (((hex($1) << 6) | hex($2)) << 7) | hex($3);
- } else {
- $eep = (((hex("FF") << 6) | hex("3F")) << 7) | hex("7F");
- }
- my $manufID = uc(AttrVal($name, "manufID", ""));
- my $model = AttrVal($name, "model", "");
- my $packetType = 1;
- $packetType = 0x0A if (ReadingsVal($hash->{IODev}{NAME}, "mode", "00") eq "01");
- my $remoteID = AttrVal($name, "remoteID", undef);
- my $remoteManufID = uc(AttrVal($name, "remoteManufID", AttrVal($name, "manufID", "")));
- my $rorg;
- my $status = '00';
- my $st = AttrVal($name, "subType", "");
- my $stSet = AttrVal($name, "subTypeSet", undef);
- if (defined $stSet) {$st = $stSet;}
- my $subDef = uc(AttrVal($name, "subDef", $hash->{DEF}));
- if ($subDef !~ m/^[\dA-F]{8}$/) {return "SenderID $subDef wrong, choose <8-digit-hex-code>.";}
- my $timeNow = TimeNow();
- if (AttrVal($name, "remoteManagement", "off") eq "manager") {
- # Remote Management
- $cmdList = "remoteDevCfg remoteFunctions:noArg remoteID:noArg remoteLinkCfg remoteLinkTableInfo:noArg remoteLinkTable remoteLinkTableGP remotePing:noArg remoteProductID:noArg remoteRepeater:noArg remoteStatus:noArg ";
- }
- # control get actions
- # $updateState = -1: no get commands available e. g. sensors
- # 0: execute get commands
- # 1: execute get commands and and update reading state
- # 2: execute get commands delayed
- my $updateState = 1;
- #Log3 $name, 5, "EnOcean $name EnOcean_Get command: " . join(" ", @a);
- shift @a;
- for (my $i = 0; $i < @a; $i++) {
- my $cmd = $a[$i];
- if ($cmd eq "remoteID") {
- $cmdID = 4;
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- shift(@a);
- $data = sprintf "0004%04X%06X", $manufID, ($eep << 3);
- $destinationID = "FFFFFFFF";
- $status = '0F';
- $hash->{IODev}{helper}{remoteAnswerWait}{0x604}{hash} = $hash;
- my %functionHash = (hash => $hash, param => 0x604);
- RemoveInternalTimer(\%functionHash);
- InternalTimer(gettimeofday() + 2.5, 'EnOcean_cdmClearRemoteWait', \%functionHash, 0);
- Log3 $name, 3, "EnOcean get $name $cmd";
- } elsif ($cmd eq "remotePing") {
- return "Attribute remoteID is missing, please define it." if (!defined $remoteID);
- $cmdID = 6;
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- shift(@a);
- $data = sprintf "0006%04X", $manufID;
- $destinationID = $remoteID;
- $status = '0F';
- $hash->{IODev}{helper}{remoteAnswerWait}{0x606}{hash} = $hash;
- my %functionHash = (hash => $hash, param => 0x606);
- RemoveInternalTimer(\%functionHash);
- InternalTimer(gettimeofday() + 2.5, 'EnOcean_cdmClearRemoteWait', \%functionHash, 0);
- Log3 $name, 3, "EnOcean get $name $cmd";
- } elsif ($cmd eq "remoteFunctions") {
- return "Attribute remoteID is missing, please define it." if (!defined $remoteID);
- $cmdID = 7;
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- shift(@a);
- $data = sprintf "0007%04X", $manufID;
- $destinationID = $remoteID;
- $status = '0F';
- $hash->{IODev}{helper}{remoteAnswerWait}{0x607}{hash} = $hash;
- my %functionHash = (hash => $hash, param => 0x607);
- RemoveInternalTimer(\%functionHash);
- InternalTimer(gettimeofday() + 2.5, 'EnOcean_cdmClearRemoteWait', \%functionHash, 0);
- Log3 $name, 3, "EnOcean get $name $cmd";
- } elsif ($cmd eq "remoteStatus") {
- $cmdID = 8;
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- shift(@a);
- $data = sprintf "0008%04X", $manufID;
- if (defined $remoteID) {
- $destinationID = $remoteID;
- } else {
- $destinationID = 'F' x 8;
- }
- $status = '0F';
- $hash->{IODev}{helper}{remoteAnswerWait}{0x608}{hash} = $hash;
- my %functionHash = (hash => $hash, param => 0x608);
- RemoveInternalTimer(\%functionHash);
- InternalTimer(gettimeofday() + 2.5, 'EnOcean_cdmClearRemoteWait', \%functionHash, 0);
- Log3 $name, 3, "EnOcean get $name $cmd";
- } elsif ($cmd eq "remoteLinkTableInfo") {
- return "Attribute remoteID is missing, please define it." if (!defined $remoteID);
- $cmdID = 0x210;
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- shift(@a);
- $data = sprintf "%04X%04X", $cmdID, $manufID;
- $destinationID = $remoteID;
- $status = '0F';
- $hash->{IODev}{helper}{remoteAnswerWait}{0x810}{hash} = $hash;
- my %functionHash = (hash => $hash, param => 0x810);
- RemoveInternalTimer(\%functionHash);
- InternalTimer(gettimeofday() + 2.5, 'EnOcean_cdmClearRemoteWait', \%functionHash, 0);
- Log3 $name, 3, "EnOcean get $name $cmd";
- } elsif ($cmd eq "remoteLinkTable") {
- return "Attribute remoteID is missing, please define it." if (!defined $remoteID);
- $cmdID = 0x211;
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- my $startRef;
- my $endRef;
- my $direction;
- if (defined($a[1]) && defined($a[2]) && defined($a[3]) && $a[1] =~ m/^in|out$/ && $a[2] =~ m/^[\dA-Fa-f]{2}$/ && $a[3] =~ m/^[\dA-Fa-f]{2}$/) {
- shift(@a);
- $direction = shift(@a);
- $direction = $direction eq 'out' ? '80' : '00';
- $startRef = uc(shift(@a));
- $endRef = uc(shift(@a));
- ($startRef, $endRef) = ($endRef, $startRef) if (hex($startRef) > hex($endRef));
- } else {
- return "Wrong parameter or direction/startRef/endRef not defined.";
- }
- $data = sprintf "%04X%04X%2s%2s%2s", $cmdID, $manufID, $direction, $startRef, $endRef;
- $destinationID = $remoteID;
- $status = '0F';
- $hash->{IODev}{helper}{remoteAnswerWait}{0x811}{hash} = $hash;
- my %functionHash = (hash => $hash, param => 0x811);
- RemoveInternalTimer(\%functionHash);
- InternalTimer(gettimeofday() + 2.5, 'EnOcean_cdmClearRemoteWait', \%functionHash, 0);
- Log3 $name, 3, "EnOcean get $name $cmd";
- } elsif ($cmd eq "remoteLinkTableGP") {
- return "Attribute remoteID is missing, please define it." if (!defined $remoteID);
- $cmdID = 0x213;
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- my $direction;
- my $index;
- if (defined($a[1]) && defined($a[2]) && $a[1] =~ m/^in|out$/ && $a[2] =~ m/^[\dA-Fa-f]{2}$/) {
- shift(@a);
- $direction = shift(@a);
- $direction = $direction eq 'out' ? '80' : '00';
- $index = uc(shift(@a));
- } else {
- return "Wrong parameter or direction/index not defined.";
- }
- $data = sprintf "%04X%04X%2s%2s", $cmdID, $manufID, $direction, $index;
- $destinationID = $remoteID;
- $status = '0F';
- $hash->{IODev}{helper}{remoteAnswerWait}{0x813}{hash} = $hash;
- my %functionHash = (hash => $hash, param => 0x813);
- RemoveInternalTimer(\%functionHash);
- InternalTimer(gettimeofday() + 2.5, 'EnOcean_cdmClearRemoteWait', \%functionHash, 0);
- Log3 $name, 3, "EnOcean get $name $cmd";
- } elsif ($cmd eq "remoteProductID") {
- return "Attribute remoteID is missing, please define it." if (!defined $remoteID);
- $cmdID = 0x227;
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- shift(@a);
- $data = sprintf "%04X%04X", $cmdID, $manufID;
- $destinationID = $remoteID;
- $status = '0F';
- $hash->{IODev}{helper}{remoteAnswerWait}{0x827}{hash} = $hash;
- my %functionHash = (hash => $hash, param => 0x827);
- RemoveInternalTimer(\%functionHash);
- InternalTimer(gettimeofday() + 5.5, 'EnOcean_cdmClearRemoteWait', \%functionHash, 0);
- Log3 $name, 3, "EnOcean get $name $cmd";
- } elsif ($cmd eq "remoteDevCfg") {
- return "Attribute remoteID is missing, please define it." if (!defined $remoteID);
- $cmdID = 0x230;
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- my $startRef;
- my $endRef;
- my $paraLen = '00';
- if (defined($a[1]) && defined($a[2]) && $a[1] =~ m/^[\dA-Fa-f]{4}$/ && $a[2] =~ m/^[\dA-Fa-f]{4}$/) {
- shift(@a);
- $startRef = uc(shift(@a));
- $endRef = uc(shift(@a));
- ($startRef, $endRef) = ($endRef, $startRef) if (hex($startRef) > hex($endRef));
- if (defined($a[0]) && $a[0] =~ m/^[\dA-Fa-f]{2}$/) {
- $paraLen = uc(shift(@a));
- }
- } else {
- return "Wrong parameter or startRef/endRef not defined.";
- }
- $data = sprintf "%04X%04X%4s%4s%2s", $cmdID, $manufID, $startRef, $endRef, $paraLen;
- $destinationID = $remoteID;
- $status = '0F';
- $hash->{IODev}{helper}{remoteAnswerWait}{0x830}{hash} = $hash;
- my %functionHash = (hash => $hash, param => 0x830);
- RemoveInternalTimer(\%functionHash);
- InternalTimer(gettimeofday() + 2.5, 'EnOcean_cdmClearRemoteWait', \%functionHash, 0);
- Log3 $name, 3, "EnOcean get $name $cmd";
- } elsif ($cmd eq "remoteLinkCfg") {
- return "Attribute remoteID is missing, please define it." if (!defined $remoteID);
- $cmdID = 0x232;
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- my $direction;
- my $idx;
- my $startRef;
- my $endRef;
- my $paraLen = '00';
- if (defined($a[1]) && defined($a[2]) && defined($a[3]) && defined($a[4])
- && $a[1] =~ m/^in|out$/ && $a[2] =~ m/^[\dA-Fa-f]{2}$/ && $a[3] =~ m/^[\dA-Fa-f]{4}$/ && $a[4] =~ m/^[\dA-Fa-f]{4}$/) {
- shift(@a);
- $direction = shift(@a);
- $direction = $direction eq 'out' ? '80' : '00';
- $idx = uc(shift(@a));
- $startRef = uc(shift(@a));
- $endRef = uc(shift(@a));
- ($startRef, $endRef) = ($endRef, $startRef) if (hex($startRef) > hex($endRef));
- if (defined($a[0]) && $a[0] =~ m/^[\dA-Fa-f]{2}$/) {
- $paraLen = uc(shift(@a));
- }
- } else {
- return "Wrong parameter or startRef/endRef not defined.";
- }
- $data = sprintf "%04X%04X%2s%2s%4s%4s%2s", $cmdID, $manufID, $direction, $idx, $startRef, $endRef, $paraLen;
- $destinationID = $remoteID;
- $status = '0F';
- $hash->{IODev}{helper}{remoteAnswerWait}{0x832}{hash} = $hash;
- my %functionHash = (hash => $hash, param => 0x832);
- RemoveInternalTimer(\%functionHash);
- InternalTimer(gettimeofday() + 2.5, 'EnOcean_cdmClearRemoteWait', \%functionHash, 0);
- Log3 $name, 3, "EnOcean get $name $cmd";
- } elsif ($cmd eq "remoteRepeater") {
- return "Attribute remoteID is missing, please define it." if (!defined $remoteID);
- $cmdID = 0x250;
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- shift(@a);
- $data = sprintf "%04X%04X", $cmdID, $manufID;
- $destinationID = $remoteID;
- $status = '0F';
- $hash->{IODev}{helper}{remoteAnswerWait}{0x850}{hash} = $hash;
- my %functionHash = (hash => $hash, param => 0x850);
- RemoveInternalTimer(\%functionHash);
- InternalTimer(gettimeofday() + 5.5, 'EnOcean_cdmClearRemoteWait', \%functionHash, 0);
- Log3 $name, 3, "EnOcean get $name $cmd";
- } elsif ($st eq "switch.05") {
- # Dual Channel Switch Actuator
- # (A5-11-05)
- $rorg = "A5";
- shift(@a);
- $updateState = 0;
- if ($cmd eq "state") {
- # query state
- Log3 $name, 3, "EnOcean get $name $cmd";
- $data = "00000008";
- } else {
- $cmdList .= "state:noArg";
- return "Unknown argument $cmd, choose one of $cmdList";
- }
- } elsif ($st eq "lightCtrl.01") {
- # Central Command, Extended Lighting-Control
- # (A5-38-09)
- $rorg = "A5";
- shift(@a);
- $updateState = 0;
- if ($cmd eq "state") {
- # query state
- Log3 $name, 3, "EnOcean get $name $cmd";
- $data = "00000008";
- } else {
- $cmdList .= "state:noArg";
- return "Unknown argument $cmd, choose one of $cmdList";
- }
- } elsif ($st eq "actuator.01") {
- # Electronic switches and dimmers with Energy Measurement and Local Control
- # (D2-01-00 - D2-01-12)
- $rorg = "D2";
- shift(@a);
- my $channel;
- if ($cmd !~ m/^roomCtrlMode$/) {
- $channel = shift(@a);
- $channel = AttrVal($name, "defaultChannel", AttrVal($name, "devChannel", undef)) if (!defined $channel);
- if (!defined $channel || $channel eq "all") {
- $channel = 30;
- } elsif ($channel eq "input" || $channel + 0 == 31) {
- $channel = 31;
- } elsif ($channel + 0 >= 30) {
- $channel = 30;
- } elsif ($channel + 0 >= 0 && $channel + 0 <= 29) {
- } else {
- return "$cmd <channel> wrong, choose 0...29|all|input.";
- }
- }
- if ($cmd eq "state") {
- $cmdID = 3;
- Log3 $name, 3, "EnOcean get $name $cmd $channel";
- $data = sprintf "%02X%02X", $cmdID, $channel;
- } elsif ($cmd eq "measurement") {
- $cmdID = 6;
- my $query = shift(@a);
- if (!defined $query) {
- return "$cmd <channel> <query> wrong, choose 0...30|all|input energy|power.";
- } elsif ($query eq "energy") {
- $query = 0;
- } elsif ($query eq "power") {
- $query = 1;
- } else {
- return "$cmd <channel> <query> wrong, choose 0...30|all|input energy|power.";
- }
- Log3 $name, 3, "EnOcean get $name $cmd $channel $query";
- $data = sprintf "%02X%02X", $cmdID, $query << 5 | $channel;
- } elsif ($cmd eq "roomCtrlMode") {
- Log3 $name, 3, "EnOcean get $name $cmd";
- $data = '09';
- } elsif ($cmd eq "settings") {
- $cmdID = 10;
- Log3 $name, 3, "EnOcean get $name $cmd $channel";
- $data = sprintf "%02X%02X", $cmdID, $channel;
- } elsif ($cmd eq "special") {
- $rorg = "D1";
- my $query = shift(@a);
- if ($manufID eq "033") {
- if (!defined $query) {
- return "$cmd <channel> <query> wrong, choose health|load|voltage|serialNumber.";
- } elsif ($query eq "health") {
- $query = 7;
- } elsif ($query eq "load") {
- $query = 8;
- } elsif ($query eq "voltage") {
- $query = 9;
- } elsif ($query eq "serialNumber") {
- $query = 0x81;
- } else {
- return "$cmd <channel> <query> wrong, choose health|load|voltage|serialNumber.";
- }
- $data = sprintf "0331%02X", $query;
- Log3 $name, 3, "EnOcean get $name $cmd $channel $query";
- readingsSingleUpdate($hash, "getParam", $query, 0);
- } elsif ($manufID eq "046") {
- if (!defined $query) {
- return "$cmd <channel> <query> wrong, choose reset|firmwareVersion|taughtInDevNum|taughtInDevID.";
- } elsif ($query eq "reset") {
- $query = 1;
- $data = sprintf "0046%02X", $query;
- } elsif ($query eq "firmwareVersion") {
- $query = 2;
- $data = sprintf "0046%02X", $query;
- } elsif ($query eq "taughtInDevNum") {
- $query = 4;
- $data = sprintf "0046%02X", $query;
- } elsif ($query eq "taughtInDevID") {
- $query = 6;
- my $taughtInDevNum = shift(@a);
- if (defined $taughtInDevNum && $taughtInDevNum =~ m/^\d+$/ && $taughtInDevNum >= 0 && $taughtInDevNum <= 23) {
- $data = sprintf "0046%02X%02X", $query, $taughtInDevNum;
- } else {
- return "Usage: taughtInDevID is not numeric or out of range";
- }
- } else {
- return "$cmd <channel> <query> wrong, choose reset|firmwareVersion|taughtInDevNum|taughtInDevID.";
- }
- Log3 $name, 3, "EnOcean get $name $cmd $channel $query";
- readingsSingleUpdate($hash, "getParam", $query, 0);
- }
- } else {
- if ($manufID =~ m/^033|046$/) {
- return "Unknown argument $cmd, choose one of " . $cmdList . "state measurement roomCtrlMode:noArg special settings";
- } else {
- return "Unknown argument $cmd, choose one of " . $cmdList . "state measurement roomCtrlMode:noArg settings";
- }
- }
- } elsif ($st =~ m/^blindsCtrl\.0[01]$/) {
- # Blinds Control for Position and Angle
- # (D2-05-xx)
- $rorg = "D2";
- shift(@a);
- $updateState = 0;
- if ($cmd eq "position") {
- # query position and angle
- $cmdID = 3;
- my $channel = shift(@a);
- if (!defined $channel) {
- $channel = AttrVal($name, "defaultChannel", 'all');
- $channel = $channel eq "all" ? 15 : $channel - 1;
- } elsif ($channel =~ m/^all$/) {
- $channel = 15;
- } elsif ($channel =~ m/^[1234]$/) {
- $channel -= 1;
- } else {
- return "$cmd parameter wrong, choose one of 1|2|3|4|all";
- }
- Log3 $name, 3, "EnOcean get $name $cmd";
- $data = sprintf "%02X", $channel << 4 | $cmdID;
- } else {
- $cmdList .= "position";
- return "Unknown argument $cmd, choose one of $cmdList";
- }
- } elsif ($st eq "multisensor.01") {
- #####
- # Multisensor Windows Handle
- # (D2-06-01)
- $rorg = "D2";
- shift(@a);
- $updateState = 2;
- my $waitingCmds = ReadingsVal($name, "waitingCmds", undef);
- if (defined $waitingCmds) {
- # check presence state
- $waitingCmds = ReadingsVal($name, "presence", "present") eq "absent" ? $waitingCmds & 0xDF | 32 : $waitingCmds & 0xDF;
- } else {
- $waitingCmds = ReadingsVal($name, "presence", "present") eq "absent" ? 32 : 0;
- }
- if ($cmd eq "config") {
- # query config
- readingsSingleUpdate($hash, "waitingCmds", $waitingCmds | 0x80, 0);
- Log3 $name, 3, "EnOcean get $name $cmd";
- } elsif ($cmd eq "log") {
- # query log
- readingsSingleUpdate($hash, "waitingCmds", $waitingCmds | 0x40, 0);
- Log3 $name, 3, "EnOcean get $name $cmd";
- } else {
- $cmdList .= "config:noArg log:noArg";
- return "Unknown argument $cmd, choose one of $cmdList";
- }
- } elsif ($st eq "roomCtrlPanel.00") {
- # Room Control Panel
- # (D2-10-00 - D2-10-02)
- $rorg = "D2";
- shift(@a);
- $updateState = 2;
- if ($cmd eq "data") {
- # data request
- readingsSingleUpdate($hash, "waitingCmds", ReadingsVal($name, "waitingCmds", 0) | 1, 0);
- Log3 $name, 3, "EnOcean get $name $cmd";
- } elsif ($cmd eq "config") {
- # configuration request
- readingsSingleUpdate($hash, "waitingCmds", ReadingsVal($name, "waitingCmds", 0) | 32, 0);
- Log3 $name, 3, "EnOcean get $name $cmd";
- } elsif ($cmd eq "roomCtrl") {
- # room control setup request
- readingsSingleUpdate($hash, "waitingCmds", ReadingsVal($name, "waitingCmds", 0) | 128, 0);
- Log3 $name, 3, "EnOcean get $name $cmd";
- } elsif ($cmd eq "timeProgram") {
- # time program request
- readingsSingleUpdate($hash, "waitingCmds", ReadingsVal($name, "waitingCmds", 0) | 256, 0);
- Log3 $name, 3, "EnOcean get $name $cmd";
- } else {
- $cmdList .= "data:noArg config:noArg roomCtrl:noArg timeProgram:noArg";
- return "Unknown argument $cmd, choose one of $cmdList";
- }
- } elsif ($st eq "fanCtrl.00") {
- # Fan Control
- # (D2-20-00 - D2-20-02)
- $rorg = "D2";
- shift(@a);
- $updateState = 0;
- if ($cmd eq "state") {
- # query position and angle
- $data = "F6FFFFFF";
- Log3 $name, 3, "EnOcean get $name $cmd DATA: $data";
- } else {
- $cmdList .= "state:noArg";
- return "Unknown argument $cmd, choose one of $cmdList";
- }
- } elsif ($st eq "heatRecovery.00") {
- # heat recovery ventilation
- # (D2-50-00)
- $rorg = "D2";
- shift(@a);
- $updateState = 0;
- if ($cmd eq "basicState") {
- # query switch state
- $data = "00";
- Log3 $name, 3, "EnOcean get $name $cmd";
- } elsif ($cmd eq "extendedState") {
- # query switch state
- $data = "01";
- Log3 $name, 3, "EnOcean get $name $cmd";
- } else {
- $cmdList .= "basicState:noArg extendedState:noArg";
- return "Unknown argument $cmd, choose one of $cmdList";
- }
- } elsif ($st eq "valveCtrl.00" && AttrVal($name, "devMode", "master") eq "master") {
- # Valve Control
- # (D2-A0-01)
- $rorg = "D2";
- shift(@a);
- $updateState = 0;
- if ($cmd eq "state") {
- # query switch state
- $data = "00";
- readingsSingleUpdate($hash, "state", $cmd, 1);
- Log3 $name, 3, "EnOcean get $name $cmd";
- } else {
- $cmdList .= "state:noArg";
- return "Unknown argument $cmd, choose one of $cmdList";
- }
- } elsif ($st eq "remote") {
- return "Unknown argument $cmd, choose one of $cmdList";
- } else {
- # subtype does not support get commands
- if (AttrVal($name, "remoteManagement", "off") eq "manager") {
- return "Unknown argument $cmd, choose one of $cmdList";
- } else {
- return;
- }
- }
- if($updateState != 2) {
- EnOcean_SndRadio(undef, $hash, $packetType, $rorg, $data, $subDef, $status, $destinationID);
- }
- }
- }
- # Set
- sub EnOcean_Set($@)
- {
- my ($hash, @a) = @_;
- return "no set value specified" if (@a < 2);
- my $name = $hash->{NAME};
- if (IsDisabled($name)) {
- Log3 $name, 4, "EnOcean $name set commands disabled.";
- return;
- }
- my $cmdID;
- my $cmdList = "";
- my @cmdObserve = @a;
- my ($ctrl, $data, $err, $logLevel, $response);
- my $destinationID = AttrVal($name, "destinationID", undef);
- if (AttrVal($name, "comMode", "uniDir") eq "biDir") {
- $destinationID = defined(AttrVal($name, "subDef", undef)) ? $hash->{DEF} : "FFFFFFFF";
- $destinationID = "FFFFFFFF" if (uc(AttrVal($name, "subDef", $hash->{DEF})) eq uc($hash->{DEF}));
- } elsif (!defined $destinationID || $destinationID eq "multicast") {
- $destinationID = "FFFFFFFF";
- } elsif ($destinationID eq "unicast") {
- $destinationID = defined(AttrVal($name, "subDef", undef)) ? $hash->{DEF} : "FFFFFFFF";
- $destinationID = "FFFFFFFF" if (uc(AttrVal($name, "subDef", $hash->{DEF})) eq uc($hash->{DEF}));
- } elsif ($destinationID !~ m/^[\dA-Fa-f]{8}$/) {
- return "DestinationID $destinationID wrong, choose <8-digit-hex-code>.";
- }
- $destinationID = uc($destinationID);
- my $IODev = $hash->{IODev}{NAME};
- my $IOHash = $defs{$IODev};
- my $manufID = uc(AttrVal($name, "manufID", ""));
- my $model = AttrVal($name, "model", "");
- my $packetType = 1;
- $packetType = 0x0A if (ReadingsVal($IODev, "mode", "00") eq "01");
- my $remoteID = AttrVal($name, "remoteID", undef);
- my $remoteManufID = uc(AttrVal($name, "remoteManufID", AttrVal($name, "manufID", "")));
- my $rorg;
- my $sendCmd = 1;
- my $status = "00";
- my $st = AttrVal($name, "subType", "");
- my $stSet = AttrVal($name, "subTypeSet", undef);
- if (defined $stSet) {$st = $stSet;}
- my $subDef = uc(AttrVal($name, "subDef", $hash->{DEF}));
- if ($subDef !~ m/^[\dA-F]{8}$/) {return "SenderID $subDef wrong, choose <8-digit-hex-code>.";}
- my $switchMode = AttrVal($name, "switchMode", "switch");
- my $timeNow = TimeNow();
- my $remoteManagement = AttrVal($name, "remoteManagement", "off");
- if ($remoteManagement eq "manager") {
- # Remote Management Manager
- $cmdList = "remoteAction:noArg remoteApplyChanges:devCfg,linkTable,no_change remoteDevCfg remoteLinkCfg remoteLock:noArg remoteLearnMode remoteLinkTable remoteLinkTableGP remoteRepeater remoteRepeaterFilter remoteReset:devCfg,linkTableIn,linkTableOut,no_change remoteRLT remoteSetCode:noArg remoteTeach remoteUnlock:noArg ";
- } elsif ($remoteManagement eq "client" || $st eq "remote") {
- # Remote Management Client
- $cmdList .= "remoteLock:noArg remoteUnlock ";
- }
- # control set actions
- # $updateState = -1: no set commands available e. g. sensors
- # 0: execute set commands
- # 1: execute set commands and and update reading state
- # 2: execute set commands delayed
- # 3: internal command
- my $updateState = AttrVal($name, "comMode", "uniDir") eq "uniDir" ? 1 : 0;
- my $updateStateAttr = AttrVal($name, "updateState", "default");
- shift @a;
- for (my $i = 0; $i < @a; $i++) {
- my $cmd = $a[$i];
- my ($cmd1, $cmd2);
- if ($cmd eq "remoteUnlock") {
- if ($remoteManagement eq "manager") {
- # manager
- $cmdID = 1;
- my $remoteCode = AttrVal($name, "remoteCode", undef);
- return "Security Code not defined, set attr $name remoteCode <00000001 ... FFFFFFFE>!" if (!defined($remoteCode));
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- $data = sprintf "%04X%04X%s", $cmdID, $manufID, uc($remoteCode);
- if (defined $remoteID) {
- $destinationID = $remoteID;
- } else {
- $destinationID = 'F' x 8;
- }
- $updateState = 0;
- } else {
- # client
- my $remoteUnlockPeriod = 1800;
- if (defined $a[1]) {
- if ($a[1] =~ m/^\d+$/ && $a[1] >= 0 && $a[1] <= 1800) {
- $remoteUnlockPeriod = $a[1];
- shift(@a);
- } else {
- return "Usage: $a[1] is not numeric or out of range";
- }
- }
- $hash->{RemoteClientUnlock} = 1;
- my %functionHash = (hash => $hash, param => 'RemoteClientUnlock');
- RemoveInternalTimer(\%functionHash);
- InternalTimer(gettimeofday() + $remoteUnlockPeriod, 'EnOcean_cdmClearHashVal', \%functionHash, 0);
- $updateState = 3;
- }
- Log3 $name, 3, "EnOcean set $name $cmd";
- } elsif ($cmd eq "remoteLock") {
- if ($remoteManagement eq "manager") {
- # manager
- $cmdID = 2;
- my $remoteCode = AttrVal($name, "remoteCode", undef);
- return "Security Code not defined, set attr $name remoteCode <00000001 ... FFFFFFFE>!" if (!defined($remoteCode));
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- $data = sprintf "%04X%04X%s", $cmdID, $manufID, uc($remoteCode);
- if (defined $remoteID) {
- $destinationID = $remoteID;
- } else {
- $destinationID = 'F' x 8;
- }
- $updateState = 0;
- } else {
- # client
- delete $hash->{RemoteClientUnlock};
- my %functionHash = (hash => $hash, param => 'RemoteClientUnlock');
- RemoveInternalTimer(\%functionHash);
- $updateState = 3;
- }
- Log3 $name, 3, "EnOcean set $name $cmd";
- } elsif ($cmd eq "remoteSetCode") {
- $cmdID = 3;
- my $remoteCode = AttrVal($name, "remoteCode", undef);
- return "Security Code not defined, set attr $name remoteCode <00000001 ... FFFFFFFE>!" if (!defined($remoteCode));
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- $data = sprintf "%04X%04X%s", $cmdID, $manufID, uc($remoteCode);
- if (defined $remoteID) {
- $destinationID = $remoteID;
- } else {
- $destinationID = 'F' x 8;
- }
- Log3 $name, 3, "EnOcean set $name $cmd";
- $updateState = 0;
- } elsif ($cmd eq "remoteAction") {
- $cmdID = 5;
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- $data = sprintf "%04X%04X", $cmdID, $manufID;
- if (defined $remoteID) {
- $destinationID = $remoteID;
- } else {
- $destinationID = 'F' x 8;
- }
- Log3 $name, 3, "EnOcean set $name $cmd";
- $updateState = 0;
- } elsif ($cmd eq "remoteLinkTable") {
- return "Attribute remoteID is missing, please define it." if (!defined $remoteID);
- if (defined $a[1] && defined $a[2] && defined $a[3] && defined $a[4] && defined $a[5] &&
- $a[1] =~ m/^in|out$/ && $a[2] =~ m/^[\dA-Fa-f]{2}$/ && $a[3] =~ m/^[\dA-Fa-f]{8}$/ && $a[4] =~ m/^[\dA-Fa-f]{2}-[\dA-Fa-f]{2}-[\dA-Fa-f]{2}$/ && $a[5] =~ m/^[\dA-Fa-f]{2}$/) {
- $cmdID = 0x212;
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- $a[4] =~ m/^(..)-(..)-(..)$/;
- $data = sprintf "%04X%04X%s%s%s%s%s%s%s", $cmdID, $manufID, $a[1] eq 'out' ? '80' : '00', uc($a[2]), uc($a[3]), uc($1), uc($2), uc($3), uc($a[5]);
- splice(@a,0,5);
- Log3 $name, 3, "EnOcean set $name $cmd";
- $updateState = 0;
- } else {
- return "Usage: $cmd arguments needed or wrong.";
- }
- } elsif ($cmd eq "remoteLinkTableGP") {
- # gpDef example: ch2:O:1:24:1:7:-40:1:40:1
- return "Attribute remoteID is missing, please define it." if (!defined $remoteID);
- if (defined $a[1] && defined $a[2] && defined $a[3] && $a[1] =~ m/^in|out$/ && $a[2] =~ m/^[\dA-Fa-f]{2}$/) {
- $cmdID = 0x214;
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- my $direction = $a[1] eq 'out' ? '80' : '00';
- my $gpDef;
- my ($channelName, $channelDir, $channelType, $signalType, $valueType, $resolution, $engMin, $scalingMin, $engMax, $scalingMax) =
- split(':', $a[3]);
- if ($channelDir eq "O" && $a[1] eq 'out' || $channelDir eq "I" && $a[1] eq 'in') {
- # add channel-, signal- and valuetype
- $gpDef .= substr(unpack('B8', pack('C', $channelType)), 6) .
- unpack('B8', pack('C', $signalType)) .
- substr(unpack('B8', pack('C', $valueType)), 6);
- if ($channelType == 1 || $channelType == 3) {
- # data, enumeration: add resolution
- $gpDef .= substr(unpack('B8', pack('C', $resolution)), 4);
- }
- if ($channelType == 1) {
- # data: add engineering and scaling
- $gpDef .= unpack('B8', pack('c', $engMin)) .
- substr(unpack('B8', pack('C', $scalingMin)), 4) .
- unpack('B8', pack('c', $engMax)) .
- substr(unpack('B8', pack('C', $scalingMax)), 4);
- }
- } else {
- return "Usage: Link Table GP Entry Direction wrong.";
- }
- if (length($gpDef) % 8) {
- # fill with trailing zeroes to x bytes
- $gpDef .= 0 x (8 - length($gpDef) % 8);
- }
- $data = sprintf "%04X%04X%s%s%s", $cmdID, $manufID, $a[1] eq 'out' ? '80' : '00', uc($a[2]), EnOcean_convBitToHex($gpDef);
- splice(@a,0,3);
- Log3 $name, 3, "EnOcean set $name $cmd";
- $updateState = 0;
- } else {
- return "Usage: $cmd arguments needed or wrong.";
- }
- } elsif ($cmd eq "remoteLearnMode") {
- return "Attribute remoteID is missing, please define it." if (!defined $remoteID);
- if (defined $a[1]) {
- $cmdID = 0x220;
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- $destinationID = $remoteID;
- my $learnMode;
- my $cmdVal = 0;
- if ($a[1] eq 'in') {
- $learnMode = '00';
- shift(@a);
- } elsif ($a[1] eq 'out') {
- $learnMode = '40';
- shift(@a);
- } elsif ($a[1] eq 'off') {
- $learnMode = '80';
- shift(@a);
- } else {
- return "Usage: $cmd $a[1] argument unknown.";
- }
- if (defined $a[1] && $a[1] =~ m/^[\dA-Fa-f]{2}$/) {
- $cmdVal = $a[1];
- shift(@a);
- } else {
- return "Usage: $cmd <learnMode> argument needed or wrong.";
- }
- $data = sprintf "%04X%04X%s%s", $cmdID, $manufID, $learnMode, $cmdVal;
- Log3 $name, 3, "EnOcean set $name $cmd";
- $updateState = 0;
- }
- } elsif ($cmd eq "remoteTeach") {
- return "Attribute remoteID is missing, please define it." if (!defined $remoteID);
- if (defined $a[1] && $a[1] =~ m/^[\dA-Fa-f]{2}$/) {
- $cmdID = 0x221;
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- $destinationID = $remoteID;
- $data = sprintf "%04X%04X%s", $cmdID, $manufID, $a[1];
- shift(@a);
- Log3 $name, 3, "EnOcean set $name $cmd";
- $updateState = 0;
- if (!exists($hash->{IODev}{Teach})) {
- # enable teach-in receiving for 3 sec
- $hash->{IODev}{Teach} = 1;
- my %functionHash = (hash => $IOHash, param => 'Teach');
- RemoveInternalTimer(\%functionHash);
- InternalTimer(gettimeofday() + 3, 'EnOcean_cdmClearHashVal', \%functionHash, 0);
- }
- } else {
- return "Usage: $cmd argument needed or wrong.";
- }
- } elsif ($cmd eq "remoteReset") {
- return "Attribute remoteID is missing, please define it." if (!defined $remoteID);
- if (defined $a[1] && $a[1] =~ m/^devCfg|linkTableIn|linkTableOut|no_change$/) {
- $cmdID = 0x224;
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- $destinationID = $remoteID;
- my %changeCmd = ('devCfg' => '80', 'linkTableIn' => '40', 'linkTableOut' => '20', 'no_change' => '00');
- $data = sprintf "%04X%04X%s", $cmdID, $manufID, $changeCmd{$a[1]};
- shift(@a);
- Log3 $name, 3, "EnOcean set $name $cmd";
- $updateState = 0;
- } else {
- return "Usage: $cmd argument needed or wrong.";
- }
- } elsif ($cmd eq "remoteRLT") {
- return "Attribute remoteID is missing, please define it." if (!defined $remoteID);
- if (defined $a[1] && defined $a[2] && $a[1] =~ m/^off|on$/ && $a[2] =~ m/^[0-7][1-9A-Fa-f]$/) {
- $cmdID = 0x225;
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- $destinationID = $remoteID;
- my $rltMode = hex $a[2];
- $rltMode |= 0x80 if ($a[1] eq 'on');
- $data = sprintf "%04X%04X%02X", $cmdID, $manufID, $rltMode;
- splice(@a,0,2);
- Log3 $name, 3, "EnOcean set $name $cmd";
- $updateState = 0;
- }
- } elsif ($cmd eq "remoteApplyChanges") {
- return "Attribute remoteID is missing, please define it." if (!defined $remoteID);
- if (defined $a[1] && $a[1] =~ m/^devCfg|linkTable|no_change$/) {
- $cmdID = 0x226;
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- $destinationID = $remoteID;
- my %changeCmd = ('devCfg' => '40', 'linkTable' => '80', 'no_change' => '00');
- $data = sprintf "%04X%04X%s", $cmdID, $manufID, $changeCmd{$a[1]};
- shift(@a);
- Log3 $name, 3, "EnOcean set $name $cmd";
- $updateState = 0;
- } else {
- return "Usage: $cmd argument needed or wrong.";
- }
- } elsif ($cmd eq "remoteDevCfg") {
- return "Attribute remoteID is missing, please define it." if (!defined $remoteID);
- if (defined $a[1] && defined $a[2] && $a[1] =~ m/^[\dA-Fa-f]{4}$/ && $a[2] =~ m/^[\dA-Fa-f]{2}[\dA-Fa-f]*$/ && length($a[2]) % 2 == 0) {
- $cmdID = 0x231;
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- $destinationID = $remoteID;
- $data = sprintf "%04X%04X%s%02X%s", $cmdID, $manufID, uc($a[1]), length($a[2]) / 2, uc($a[2]);
- splice(@a,0,2);
- Log3 $name, 3, "EnOcean set $name $cmd";
- $updateState = 0;
- } else {
- return "Usage: $cmd arguments needed or wrong.";
- }
- } elsif ($cmd eq "remoteLinkCfg") {
- return "Attribute remoteID is missing, please define it." if (!defined $remoteID);
- if (defined $a[1] && defined $a[2] && defined $a[3] && defined $a[4] &&
- $a[1] =~ m/^in|out$/ && $a[2] =~ m/^[\dA-Fa-f]{2}$/ && $a[3] =~ m/^[\dA-Fa-f]{4}$/ &&
- $a[4] =~ m/^[\dA-Fa-f]{2}[\dA-Fa-f]*$/ && length($a[4]) % 2 == 0) {
- $cmdID = 0x233;
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- my $direction = $a[1] eq 'out' ? '80' : '00';
- $destinationID = $remoteID;
- $data = sprintf "%04X%04X%s%s%s%02X%s", $cmdID, $manufID, $direction, uc($a[2]), uc($a[3]), length($a[4]) / 2, uc($a[4]);
- splice(@a,0,4);
- Log3 $name, 3, "EnOcean set $name $cmd";
- $updateState = 0;
- } else {
- return "Usage: $cmd arguments needed or wrong.";
- }
- } elsif ($cmd eq "remoteRepeater") {
- return "Attribute remoteID is missing, please define it." if (!defined $remoteID);
- if (defined $a[1] && defined $a[2] && defined $a[3] &&
- $a[1] =~ m/^on|off|filter$/ && $a[2] =~ m/^[1-2]$/ && $a[3] =~ m/^AND|OR$/) {
- $cmdID = 0x251;
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- my %repFunc = ('on' => 0x40, 'off' => 0, 'filter' => 0x80);
- my $cmdVal = $repFunc{$a[1]} | ($a[2] == 2 ? 0x20 : 0x10) | ($a[3] eq 'OR' ? 8 : 0);
- $destinationID = $remoteID;
- $data = sprintf "%04X%04X%02X", $cmdID, $manufID, $cmdVal;
- splice(@a,0,3);
- Log3 $name, 3, "EnOcean set $name $cmd";
- $updateState = 0;
- } else {
- return "Usage: $cmd arguments needed or wrong.";
- }
- } elsif ($cmd eq "remoteRepeaterFilter") {
- return "Attribute remoteID is missing, please define it." if (!defined $remoteID);
- if (defined $a[1] && defined $a[2] &&
- $a[1] =~ m/^apply|block|delete|deleteAll$/ && $a[2] =~ m/^destinationID|sourceID|rorg|rssi$/) {
- my %repFilterCtrl = ('deleteAll' => 0x30, 'delete' => 0x20, 'apply' => 0x10, 'block' => 0);
- my $cmdVal = $repFilterCtrl{$a[1]};
- my $cmdAttr;
- if (defined $a[3]) {
- if ($a[2] =~ m/^destinationID$/ && $a[3] =~ m/^[\dA-Fa-f]{8}$/) {
- $cmdVal |= 3;
- $cmdAttr = $a[3];
- } elsif ($a[2] =~ m/^sourceID$/ && $a[3] =~ m/^[\dA-Fa-f]{8}$/) {
- $cmdVal |= 0;
- $cmdAttr = $a[3];
- } elsif ($a[2] =~ m/^rorg$/ && $a[3] =~ m/^[\dA-Fa-f]{2}$/) {
- $cmdVal |= 1;
- $cmdAttr = '0' x 6 . $a[3];
- } elsif ($a[2] =~ m/^rssi$/ && $a[3] =~ m/^-?\d+$/ && abs($a[3]) >= 0 && abs($a[3]) <= 255) {
- $cmdVal |= 2;
- $cmdAttr = sprintf "000000%02X", abs($a[3]);
- } else {
- return "Usage: $cmd $a[2] argument wrong.";
- }
- } else {
- return "Usage: $cmd $a[2] argument needed.";
- }
- $cmdID = 0x252;
- $manufID = 0x7FF;
- $packetType = 7;
- $rorg = "C5";
- $destinationID = $remoteID;
- $data = sprintf "%04X%04X%02X%s", $cmdID, $manufID, $cmdVal, $cmdAttr;
- splice(@a,0,3);
- Log3 $name, 3, "EnOcean set $name $cmd";
- $updateState = 0;
- } else {
- return "Usage: $cmd arguments needed or wrong.";
- }
- } elsif ($st eq "switch") {
- # Rocker Switch, simulate a PTM200 switch module
- # separate first and second action
- ($cmd1, $cmd2) = split(",", $cmd, 2);
- # check values
- if (!defined($EnO_ptm200btn{$cmd1}) || ($cmd2 && !defined($EnO_ptm200btn{$cmd2}))) {
- $cmdList .= join(":noArg ", sort keys %EnO_ptm200btn) . ':noArg';
- return SetExtensions($hash, $cmdList, $name, @a);
- }
- my $channelA = ReadingsVal($name, "channelA", undef);
- my $channelB = ReadingsVal($name, "channelB", undef);
- my $channelC = ReadingsVal($name, "channelC", undef);
- my $channelD = ReadingsVal($name, "channelD", undef);
- my $lastChannel = ReadingsVal($name, ".lastChannel", "A");
- my $releasedChannel = AttrVal($name, "releasedChannel", "auto");
- my $subDefA = AttrVal($name, "subDefA", $subDef);
- my $subDefB = AttrVal($name, "subDefB", $subDef);
- my $subDefC = AttrVal($name, "subDefC", $subDef);
- my $subDefD = AttrVal($name, "subDefD", $subDef);
- my $subDefI = AttrVal($name, "subDefI", $subDef);
- my $subDef0 = AttrVal($name, "subDef0", $subDef);
- my $switchType = AttrVal($name, "switchType", "direction");
- # first action
- if ($cmd1 eq "released") {
- if ($switchType eq "central") {
- if ($releasedChannel eq "auto") {
- if ($lastChannel =~ m/0|I/) {
- $releasedChannel = $lastChannel;
- } else {
- $releasedChannel = 0;
- }
- } elsif ($releasedChannel !~ m/0|I/) {
- $releasedChannel = 0;
- }
- } elsif ($switchType eq "channel") {
- if ($releasedChannel eq "auto") {
- if ($lastChannel =~ m/A|B|C|D/) {
- $releasedChannel = $lastChannel;
- } else {
- $releasedChannel = "A";
- }
- } elsif ($releasedChannel !~ m/A|B|C|D/) {
- $releasedChannel = "A";
- }
- }
- $subDef = AttrVal($name, "subDef" . $releasedChannel, $subDef);
- } elsif ($switchType eq "central") {
- if ($cmd1 =~ m/.0/) {
- $subDef = $subDef0;
- $lastChannel = 0;
- } elsif ($cmd1 =~ m/.I/) {
- $subDef = $subDefI;
- $lastChannel = "I";
- }
- } elsif ($switchType eq "channel") {
- $lastChannel = substr($cmd1, 0, 1);
- $subDef = AttrVal($name, "subDef" . $lastChannel, $subDefA);
- } else {
- $lastChannel = substr($cmd1, 0, 1);
- }
- if ($switchType eq "universal") {
- if ($cmd1 =~ m/A./ && (!defined($channelA) || $cmd1 ne $channelA)) {
- $cmd1 = "A0";
- } elsif ($cmd1 =~ m/B./ && (!defined($channelB) || $cmd1 ne $channelB)) {
- $cmd1 = "B0";
- } elsif ($cmd1 =~ m/C./ && (!defined($channelC) || $cmd1 ne $channelC)) {
- $cmd1 = "C0";
- } elsif ($cmd1 =~ m/D./ && (!defined($channelD) || $cmd1 ne $channelD)) {
- $cmd1 = "D0";
- } elsif ($cmd1 eq "released") {
- } else {
- $sendCmd = undef;
- }
- }
- # second action
- if ($cmd2 && $switchType eq "universal") {
- if ($cmd2 =~ m/A./ && (!defined($channelA) || $cmd2 ne $channelA)) {
- $cmd2 = "A0";
- } elsif ($cmd2 =~ m/B./ && (!defined($channelB) || $cmd2 ne $channelB)) {
- $cmd2 = "B0";
- } elsif ($cmd2 =~ m/C./ && (!defined($channelC) || $cmd2 ne $channelC)) {
- $cmd2 = "C0";
- } elsif ($cmd2 =~ m/D./ && (!defined($channelD) || $cmd2 ne $channelD)) {
- $cmd2 = "D0";
- } else {
- $cmd2 = undef;
- }
- if ($cmd2 && undef($sendCmd)) {
- # only second action has changed, send as first action
- $cmd1 = $cmd2;
- $cmd2 = undef;
- $sendCmd = 1;
- }
- }
- # convert and send first and second command
- my $switchCmd;
- ($switchCmd, $status) = split(':', $EnO_ptm200btn{$cmd1}, 2);
- # reset T21 status flag if 4 rocker
- $status = '10' if ($switchCmd > 3);
- $switchCmd <<= 5;
- if ($cmd1 ne "released") {
- # set the pressed flag
- $switchCmd |= 0x10 ;
- }
- if($cmd2) {
- # execute second action
- if ($switchType =~ m/^central|channel$/) {
- # second action not supported
- $cmd = $cmd1;
- } else {
- my ($d2, undef) = split(':', $EnO_ptm200btn{$cmd2}, 2);
- # reset T21 status flag if 4 rocker
- $status = '10' if ($d2 > 3);
- $switchCmd |= ($d2 << 1) | 0x01;
- }
- }
- if (defined $sendCmd) {
- $data = sprintf "%02X", $switchCmd;
- $rorg = "F6";
- #####
- SetExtensionsCancel($hash);
- Log3 $name, 3, "EnOcean set $name $cmd";
- if ($updateState) {
- readingsSingleUpdate($hash, "channel" . $1, $cmd1, 1) if ($cmd1 =~ m/^([A-D])./);
- readingsSingleUpdate($hash, "channel" . $1, $cmd2, 1) if ($cmd2 && $cmd2 =~ m/^([A-D])./);
- }
- readingsSingleUpdate($hash, ".lastChannel", $lastChannel, 0);
- }
- } elsif ($st eq "switch.00") {
- my $switchCmd = join(",", sort split(",", $cmd, 2));
- ($cmd1, $cmd2) = split(",", $switchCmd, 2);
- $cmd = $switchCmd;
- if ((!defined($switchCmd)) || (!defined($EnO_switch_00Btn{$switchCmd}))) {
- # check values
- $cmdList .= join(":noArg ", keys %EnO_switch_00Btn);
- return SetExtensions($hash, $cmdList, $name, @a);
- } elsif ($switchCmd eq "teachIn") {
- ($err, $rorg, $data) = EnOcean_sndUTE(undef, $hash, AttrVal($name, "comMode", "uniDir"),
- AttrVal($name, "uteResponseRequest", "no"), "in", 0, "D2-03-00");
- $updateState = 0;
- } elsif ($switchCmd eq "teachInSec") {
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDef", "confirm");
- ($err, $response, $logLevel) = EnOcean_sec_createTeachIn(undef, $hash, "uniDir", "VAES", "D2-03-00", 3,
- "2++", "false", "encryption", $subDef, $destinationID);
- if ($err) {
- Log3 $name, $logLevel, "EnOcean $name Error: $err";
- return $err;
- } else {
- EnOcean_CommandSave(undef, undef);
- Log3 $name, $logLevel, "EnOcean $name $response";
- readingsSingleUpdate($hash, "teach", "STE teach-in sent", 1);
- return(undef);
- }
- } elsif ($switchCmd eq "teachOut") {
- ($err, $rorg, $data) = EnOcean_sndUTE(undef, $hash, AttrVal($name, "comMode", "uniDir"),
- AttrVal($name, "uteResponseRequest", "no"), "out", 0, "D2-03-00");
- $updateState = 0;
- } else {
- $data = sprintf "%02X", $EnO_switch_00Btn{$switchCmd};
- $rorg = "D2";
- #####
- SetExtensionsCancel($hash);
- }
- Log3 $name, 3, "EnOcean set $name $switchCmd";
- } elsif ($st eq "roomSensorControl.01") {
- # Room Sensor and Control Unit (EEP A5-04-01, A5-10-10 ... A5-10-14)
- # [Thermokon SR04 * rH, Thanus SR *, untested]
- # $db[3] is the setpoint where 0x00 = min ... 0xFF = max
- # $db[2] is the humidity where 0x00 = 0%rH ... 0xFA = 100%rH
- # $db[1] is the temperature where 0x00 = 0°C ... 0xFA = +40°C
- # $db[0] bit D0 is the occupy button, pushbutton or slide switch
- $rorg = "A5";
- # primarily temperature from the reference device then the attribute actualTemp is read
- my $temperatureRefDev = AttrVal($name, "temperatureRefDev", undef);
- my $actualTemp = AttrVal($name, "actualTemp", 20);
- $actualTemp = ReadingsVal($temperatureRefDev, "temperature", 20) if (defined $temperatureRefDev);
- $actualTemp = 20 if ($actualTemp !~ m/^[+-]?\d+(\.\d+)?$/);
- $actualTemp = 0 if ($actualTemp < 0);
- $actualTemp = 40 if ($actualTemp > 40);
- $actualTemp = sprintf "%0.1f", $actualTemp;
- # primarily humidity from the reference device then the attribute humidity is read
- my $humidityRefDev = AttrVal($name, "humidityRefDev", undef);
- my $humidity = AttrVal($name, "humidity", 0);
- $humidity = ReadingsVal($humidityRefDev, "humidity", 0) if (defined $humidityRefDev);
- $humidity = 0 if ($humidity !~ m/^\d+(\.\d+)?$/);
- $humidity = 0 if ($humidity < 0);
- $humidity = 100 if ($humidity > 100);
- $humidity = sprintf "%d", $humidity;
- my $setCmd = 8;
- my $setpoint = ReadingsVal($name, "setpoint", 125);
- my $setpointScaled = ReadingsVal($name, "setpointScaled", undef);
- my $switch = ReadingsVal($name, "switch", "off");
- $setCmd |= 1 if ($switch eq "on");
- if ($cmd eq "teach") {
- if ($manufID eq "00D") {
- # teach-in EEP A5-10-12, Manufacturer "Eltako"
- $data = "40900D80";
- $attr{$name}{eep} = "A5-10-12";
- } else {
- # teach-in EEP A5-10-10, Manufacturer "Multi user Manufacturer ID"
- $data = "4087FF80";
- $attr{$name}{eep} = "A5-10-10";
- }
- CommandDeleteReading(undef, "$name .*");
- readingsSingleUpdate($hash, "teach", "4BS teach-in sent", 1);
- $updateState = 0;
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDef", "confirm");
- } elsif ($cmd eq "setpoint") {
- #
- if (defined $a[1]) {
- if (($a[1] =~ m/^[+-]?\d+(\.\d+)?$/) && ($a[1] >= 0) && ($a[1] <= 255)) {
- $setpoint = $a[1];
- shift(@a);
- if (defined $setpointScaled) {
- $setpointScaled = EnOcean_ReadingScaled($hash, $setpoint, 0, 255);
- }
- } else {
- return "Usage: $a[1] is not numeric or out of range";
- }
- }
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "temperature", $actualTemp);
- readingsBulkUpdate($hash, "humidity", $humidity);
- readingsBulkUpdate($hash, "setpointScaled", $setpointScaled) if (defined $setpointScaled);
- readingsBulkUpdate($hash, "setpoint", $setpoint);
- readingsBulkUpdate($hash, "switch", $switch);
- readingsBulkUpdate($hash, "state", "T: $actualTemp H: $humidity SP: $setpoint SW: $switch");
- readingsEndUpdate($hash, 1);
- $actualTemp = $actualTemp / 40 * 250;
- $humidity = $humidity / 100 * 250;
- $updateState = 0;
- $data = sprintf "%02X%02X%02X%02X", $setpoint, $humidity, $actualTemp, $setCmd;
- } elsif ($cmd eq "setpointScaled") {
- #
- if (defined $a[1]) {
- my $scaleMin = AttrVal($name, "scaleMin", undef);
- my $scaleMax = AttrVal($name, "scaleMax", undef);
- my ($rangeMin, $rangeMax);
- if (defined $scaleMax && defined $scaleMin &&
- $scaleMax =~ m/^[+-]?\d+(\.\d+)?$/ && $scaleMin =~ m/^[+-]?\d+(\.\d+)?$/) {
- if ($scaleMin > $scaleMax) {
- ($rangeMin, $rangeMax)= ($scaleMax, $scaleMin);
- } else {
- ($rangeMin, $rangeMax)= ($scaleMin, $scaleMax);
- }
- } else {
- return "Usage: Attributes scaleMin and/or scaleMax not defined or not numeric.";
- }
- if ($a[1] =~ m/^[+-]?\d+(\.\d+)?$/ && $a[1] >= $rangeMin && $a[1] <= $rangeMax) {
- $setpointScaled = $a[1];
- shift(@a);
- $setpoint = sprintf "%d", 255 * $scaleMin/($scaleMin-$scaleMax) - 255/($scaleMin-$scaleMax) * $setpointScaled;
- } else {
- return "Usage: $a[1] is not numeric or out of range";
- }
- }
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "temperature", $actualTemp);
- readingsBulkUpdate($hash, "humidity", $humidity);
- readingsBulkUpdate($hash, "setpointScaled", $setpointScaled) if (defined $setpointScaled);
- readingsBulkUpdate($hash, "setpoint", $setpoint);
- readingsBulkUpdate($hash, "switch", $switch);
- readingsBulkUpdate($hash, "state", "T: $actualTemp H: $humidity SP: $setpoint SW: $switch");
- readingsEndUpdate($hash, 1);
- $actualTemp = $actualTemp / 40 * 250;
- $humidity = $humidity / 100 * 250;
- $updateState = 0;
- $data = sprintf "%02X%02X%02X%02X", $setpoint, $humidity, $actualTemp, $setCmd;
- } elsif ($cmd eq "switch") {
- #
- if (defined $a[1]) {
- if ($a[1] eq "on") {
- $switch = $a[1];
- $setCmd |= 1;
- shift(@a);
- } elsif ($a[1] eq "off"){
- $switch = $a[1];
- shift(@a);
- } else {
- return "Usage: $a[1] is unknown";
- }
- }
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "temperature", $actualTemp);
- readingsBulkUpdate($hash, "humidity", $humidity);
- readingsBulkUpdate($hash, "setpointScaled", $setpointScaled) if (defined $setpointScaled);
- readingsBulkUpdate($hash, "setpoint", $setpoint);
- readingsBulkUpdate($hash, "switch", $switch);
- readingsBulkUpdate($hash, "state", "T: $actualTemp H: $humidity SP: $setpoint SW: $switch");
- readingsEndUpdate($hash, 1);
- $actualTemp = $actualTemp / 40 * 250;
- $humidity = $humidity / 100 * 250;
- $updateState = 0;
- $data = sprintf "%02X%02X%02X%02X", $setpoint, $humidity, $actualTemp, $setCmd;
- } else {
- return "Unknown argument " . $cmd . ", choose one of " . $cmdList . " setpoint:slider,0,1,255 setpointScaled switch:on,off teach:noArg"
- }
- Log3 $name, 3, "EnOcean set $name $cmd";
- } elsif ($st eq "roomSensorControl.05") {
- # Room Sensor and Control Unit (EEP A5-10-01 ... A5-10-0D)
- # [Eltako FTR55D, FTR55H, Thermokon SR04 *, Thanos SR *, untested]
- # $db[3] is the fan speed or night reduction for Eltako
- # $db[2] is the setpoint where 0x00 = min ... 0xFF = max or
- # reference temperature for Eltako where 0x00 = 0°C ... 0xFF = 40°C
- # $db[1] is the temperature where 0x00 = +40°C ... 0xFF = 0°C
- # $db[1]_bit_1 is blocking the aditional Room Sensor and Control Unit for Eltako FVS
- # $db[0]_bit_0 is the slide switch
- $rorg = "A5";
- # primarily temperature from the reference device then the attribute actualTemp is read
- my $temperatureRefDev = AttrVal($name, "temperatureRefDev", undef);
- my $actualTemp = AttrVal($name, "actualTemp", 20);
- $actualTemp = ReadingsVal($temperatureRefDev, "temperature", 20) if (defined $temperatureRefDev);
- $actualTemp = 20 if ($actualTemp !~ m/^[+-]?\d+(\.\d+)?$/);
- $actualTemp = 0 if ($actualTemp < 0);
- $actualTemp = 40 if ($actualTemp > 40);
- $actualTemp = sprintf "%0.1f", $actualTemp;
- my $setCmd = 8;
- if ($manufID eq "00D") {
- # EEP A5-10-06 plus DB3 [Eltako FVS]
- my $setpointTemp = ReadingsVal($name, "setpointTemp", 20);
- my $nightReduction = ReadingsVal($name, "nightReduction", 0);
- my $block = ReadingsVal($name, "block", "unlock");
- if ($cmd eq "teach") {
- # teach-in EEP A5-10-06 plus "FVS", Manufacturer "Eltako"
- $data = "40300D85";
- $attr{$name}{eep} = "A5-10-06";
- CommandDeleteReading(undef, "$name .*");
- readingsSingleUpdate($hash, "teach", "4BS teach-in sent", 1);
- $updateState = 0;
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDef", "confirm");
- } elsif ($cmd eq "desired-temp" || $cmd eq "setpointTemp") {
- #
- if (defined $a[1]) {
- if (($a[1] =~ m/^[+-]?\d+(\.\d+)?$/) && ($a[1] >= 0) && ($a[1] <= 40)) {
- $setpointTemp = sprintf "%0.1f", $a[1];
- shift(@a);
- } else {
- return "Usage: $a[1] is not numeric or out of range";
- }
- }
- if (defined $a[1]) {
- if (($a[1] =~ m/^(lock|unlock)$/) ) {
- $block = $a[1];
- shift(@a);
- } else {
- return "Usage: $a[1] is unknown";
- }
- }
- readingsSingleUpdate($hash, "temperature", $actualTemp, 1);
- readingsSingleUpdate($hash, "setpointTemp", $setpointTemp, 1);
- readingsSingleUpdate($hash, "nightReduction", $nightReduction, 1);
- readingsSingleUpdate($hash, "block", $block, 1);
- readingsSingleUpdate($hash, "state", "T: $actualTemp SPT: $setpointTemp NR: $nightReduction", 1);
- if ($nightReduction == 5) {
- $nightReduction = 31;
- } elsif ($nightReduction == 4) {
- $nightReduction = 25;
- } elsif ($nightReduction == 3) {
- $nightReduction = 19;
- } elsif ($nightReduction == 2) {
- $nightReduction = 12;
- } elsif ($nightReduction == 1) {
- $nightReduction = 6;
- } else {
- $nightReduction = 0;
- }
- $actualTemp = (40 - $actualTemp) / 40 * 255;
- $setpointTemp = $setpointTemp * 255 / 40;
- # control of the aditional Room Sensor and Control Unit
- if ($block eq "lock") {
- # temperature setting is locked
- $setCmd = 0x0D;
- } else {
- # setpointTemp may be subject to change at +/-3 K
- $setCmd = 0x0F;
- }
- $updateState = 0;
- $data = sprintf "%02X%02X%02X%02X", $nightReduction, $setpointTemp, $actualTemp, $setCmd;
- } elsif ($cmd eq "nightReduction") {
- #
- if (defined $a[1]) {
- if ($a[1] =~ m/^[0-5]$/) {
- $nightReduction = $a[1];
- shift(@a);
- } else {
- return "Usage: $a[1] is not numeric or out of range";
- }
- }
- if (defined $a[1]) {
- if (($a[1] =~ m/^(lock|unlock)$/) ) {
- $block = $a[1];
- shift(@a);
- } else {
- return "Usage: $a[1] is unknown";
- }
- }
- readingsSingleUpdate($hash, "temperature", $actualTemp, 1);
- readingsSingleUpdate($hash, "setpointTemp", $setpointTemp, 1);
- readingsSingleUpdate($hash, "nightReduction", $nightReduction, 1);
- readingsSingleUpdate($hash, "block", $block, 1);
- readingsSingleUpdate($hash, "state", "T: $actualTemp SPT: $setpointTemp NR: $nightReduction", 1);
- if ($nightReduction == 5) {
- $nightReduction = 31;
- } elsif ($nightReduction == 4) {
- $nightReduction = 25;
- } elsif ($nightReduction == 3) {
- $nightReduction = 19;
- } elsif ($nightReduction == 2) {
- $nightReduction = 12;
- } elsif ($nightReduction == 1) {
- $nightReduction = 6;
- } else {
- $nightReduction = 0;
- }
- $actualTemp = (40 - $actualTemp) / 40 * 254;
- $setpointTemp = $setpointTemp * 254 / 40;
- # control of the aditional Room Sensor and Control Unit
- if ($block eq "lock") {
- # temperature setting is locked
- $setCmd = 0x0D;
- } else {
- # setpointTemp may be subject to change at +/-3 K
- $setCmd = 0x0F;
- }
- $updateState = 0;
- $data = sprintf "%02X%02X%02X%02X", $nightReduction, $setpointTemp, $actualTemp, $setCmd;
- } else {
- return "Unknown argument " . $cmd . ", choose one of setpointTemp:slider,0,1,40 desired-temp nightReduction:0,1,2,3,4,5 teach:noArg"
- }
- } else {
- # EEP A5-10-02 or EEP A5-10-03
- my $setpoint = ReadingsVal($name, "setpoint", 128);
- my $setpointScaled = ReadingsVal($name, "setpointScaled", undef);
- my $fanStage = ReadingsVal($name, "fanStage", "auto");
- my $switch = ReadingsVal($name, "switch", "off");
- $setCmd |= 1 if ($switch eq "on");
- if ($cmd eq "teach") {
- if ($manufID eq "019") {
- # teach-in EEP A5-10-03, Manufacturer "Multi user Manufacturer ID"
- $data = "401FFF80";
- $attr{$name}{eep} = "A5-10-03";
- } else {
- # teach-in EEP A5-10-02, Manufacturer "Multi user Manufacturer ID"
- $data = "4017FF80";
- $attr{$name}{eep} = "A5-10-02";
- }
- CommandDeleteReading(undef, "$name .*");
- readingsSingleUpdate($hash, "teach", "4BS teach-in sent", 1);
- $updateState = 0;
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDef", "confirm");
- } elsif ($cmd eq "fanStage") {
- #
- if (defined $a[1] && ($a[1] =~ m/^[0-3]$/ || $a[1] eq "auto")) {
- $fanStage = $a[1];
- shift(@a);
- readingsSingleUpdate($hash, "temperature", $actualTemp, 1);
- readingsSingleUpdate($hash, "setpointScaled", $setpointScaled, 1) if (defined $setpointScaled);
- readingsSingleUpdate($hash, "setpoint", $setpoint, 1);
- readingsSingleUpdate($hash, "fanStage", $fanStage, 1);
- readingsSingleUpdate($hash, "switch", $switch, 1);
- readingsSingleUpdate($hash, "state", "T: $actualTemp SP: $setpoint F: $fanStage SW: $switch", 1);
- if ($fanStage eq "auto"){
- $fanStage = 255;
- } elsif ($fanStage == 0) {
- $fanStage = 209;
- } elsif ($fanStage == 1) {
- $fanStage = 189;
- } elsif ($fanStage == 2) {
- $fanStage = 164;
- } else {
- $fanStage = 144;
- }
- } else {
- return "Usage: $a[1] is not numeric, out of range or unknown";
- }
- $actualTemp = (40 - $actualTemp) / 40 * 254;
- $updateState = 0;
- $data = sprintf "%02X%02X%02X%02X", $fanStage, $setpoint, $actualTemp, $setCmd;
- } elsif ($cmd eq "setpoint") {
- #
- if (defined $a[1]) {
- if (($a[1] =~ m/^[+-]?\d+(\.\d+)?$/) && ($a[1] >= 0) && ($a[1] <= 255)) {
- $setpoint = $a[1];
- shift(@a);
- if (defined $setpointScaled) {
- $setpointScaled = EnOcean_ReadingScaled($hash, $setpoint, 0, 255);
- }
- } else {
- return "Usage: $a[1] is not numeric or out of range";
- }
- }
- readingsSingleUpdate($hash, "temperature", $actualTemp, 1);
- readingsSingleUpdate($hash, "setpointScaled", $setpointScaled, 1) if (defined $setpointScaled);
- readingsSingleUpdate($hash, "setpoint", $setpoint, 1);
- readingsSingleUpdate($hash, "fanStage", $fanStage, 1);
- readingsSingleUpdate($hash, "switch", $switch, 1);
- readingsSingleUpdate($hash, "state", "T: $actualTemp SP: $setpoint F: $fanStage SW: $switch", 1);
- if ($fanStage eq "auto"){
- $fanStage = 255;
- } elsif ($fanStage == 0) {
- $fanStage = 209;
- } elsif ($fanStage == 1) {
- $fanStage = 189;
- } elsif ($fanStage == 2) {
- $fanStage = 164;
- } else {
- $fanStage = 144;
- }
- $actualTemp = (40 - $actualTemp) / 40 * 255;
- $updateState = 0;
- $data = sprintf "%02X%02X%02X%02X", $fanStage, $setpoint, $actualTemp, $setCmd;
- } elsif ($cmd eq "setpointScaled") {
- #
- if (defined $a[1]) {
- my $scaleMin = AttrVal($name, "scaleMin", undef);
- my $scaleMax = AttrVal($name, "scaleMax", undef);
- my ($rangeMin, $rangeMax);
- if (defined $scaleMax && defined $scaleMin &&
- $scaleMax =~ m/^[+-]?\d+(\.\d+)?$/ && $scaleMin =~ m/^[+-]?\d+(\.\d+)?$/) {
- if ($scaleMin > $scaleMax) {
- ($rangeMin, $rangeMax)= ($scaleMax, $scaleMin);
- } else {
- ($rangeMin, $rangeMax)= ($scaleMin, $scaleMax);
- }
- } else {
- return "Usage: Attributes scaleMin and/or scaleMax not defined or not numeric.";
- }
- if ($a[1] =~ m/^[+-]?\d+(\.\d+)?$/ && $a[1] >= $rangeMin && $a[1] <= $rangeMax) {
- $setpointScaled = $a[1];
- shift(@a);
- $setpoint = sprintf "%d", 255 * $scaleMin/($scaleMin-$scaleMax) - 255/($scaleMin-$scaleMax) * $setpointScaled;
- } else {
- return "Usage: $a[1] is not numeric or out of range";
- }
- }
- readingsSingleUpdate($hash, "temperature", $actualTemp, 1);
- readingsSingleUpdate($hash, "setpointScaled", $setpointScaled, 1);
- readingsSingleUpdate($hash, "setpoint", $setpoint, 1);
- readingsSingleUpdate($hash, "fanStage", $fanStage, 1);
- readingsSingleUpdate($hash, "switch", $switch, 1);
- readingsSingleUpdate($hash, "state", "T: $actualTemp SP: $setpoint F: $fanStage SW: $switch", 1);
- if ($fanStage eq "auto"){
- $fanStage = 255;
- } elsif ($fanStage == 0) {
- $fanStage = 209;
- } elsif ($fanStage == 1) {
- $fanStage = 189;
- } elsif ($fanStage == 2) {
- $fanStage = 164;
- } else {
- $fanStage = 144;
- }
- $actualTemp = (40 - $actualTemp) / 40 * 255;
- $updateState = 0;
- $data = sprintf "%02X%02X%02X%02X", $fanStage, $setpoint, $actualTemp, $setCmd;
- } elsif ($cmd eq "switch") {
- #
- if (defined $a[1]) {
- if ($a[1] eq "on") {
- $switch = $a[1];
- $setCmd |= 1;
- shift(@a);
- } elsif ($a[1] eq "off"){
- $switch = $a[1];
- shift(@a);
- } else {
- return "Usage: $a[1] is unknown";
- }
- }
- readingsSingleUpdate($hash, "temperature", $actualTemp, 1);
- readingsSingleUpdate($hash, "setpointScaled", $setpointScaled, 1) if (defined $setpointScaled);
- readingsSingleUpdate($hash, "setpoint", $setpoint, 1);
- readingsSingleUpdate($hash, "fanStage", $fanStage, 1);
- readingsSingleUpdate($hash, "switch", $switch, 1);
- readingsSingleUpdate($hash, "state", "T: $actualTemp SP: $setpoint F: $fanStage SW: $switch", 1);
- if ($fanStage eq "auto"){
- $fanStage = 255;
- } elsif ($fanStage == 0) {
- $fanStage = 209;
- } elsif ($fanStage == 1) {
- $fanStage = 189;
- } elsif ($fanStage == 2) {
- $fanStage = 164;
- } else {
- $fanStage = 144;
- }
- $actualTemp = (40 - $actualTemp) / 40 * 255;
- $updateState = 0;
- $data = sprintf "%02X%02X%02X%02X", $fanStage, $setpoint, $actualTemp, $setCmd;
- } else {
- return "Unknown argument " . $cmd . ", choose one of " . $cmdList . "setpoint:slider,0,1,255 fanStage:auto,0,1,2,3 setpointScaled switch:on,off teach:noArg"
- }
- }
- Log3 $name, 3, "EnOcean set $name $cmd";
- } elsif ($st eq "hvac.01" || $st eq "MD15") {
- # Battery Powered Actuator (EEP A5-20-01)
- # [Kieback&Peter MD15-FTL-xx]
- $rorg = "A5";
- my $setpointTemp = ReadingsVal($name, "setpointTemp", 20);
- my $temperature = ReadingsVal($name, "temperature", 20);
- if ($cmd eq "setpoint") {
- if (defined $a[1] && $a[1] =~ m/^\d+$/ && $a[1] >= 0 && $a[1] <= 100) {
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "setpointSet", $a[1]);
- readingsBulkUpdate($hash, "waitingCmds", $cmd);
- readingsEndUpdate($hash, 0);
- # stop PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- CommandDeleteReading(undef, "$name setpointTempSet");
- Log3 $name, 3, "EnOcean set $name $cmd $a[1]";
- shift(@a);
- } else {
- return "Usage: $cmd value wrong.";
- }
- $updateState = 2;
- } elsif ($cmd =~ m/^desired-temp|setpointTemp$/) {
- if (defined $a[1] && $a[1] =~ m/^\d+(\.\d)?$/ && $a[1] >= 10 && $a[1] <= 30) {
- $cmd = "setpointTemp";
- $setpointTemp = $a[1];
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "setpointTempSet", sprintf("%0.1f", $setpointTemp));
- readingsBulkUpdate($hash, "waitingCmds", "setpointTemp");
- readingsEndUpdate($hash, 0);
- # PID regulator active
- my $activatePID = AttrVal($name, 'pidCtrl', 'off') eq 'on' ? 'start' : 'stop';
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, $activatePID, '');
- CommandDeleteReading(undef, "$name setpointSet");
- Log3 $name, 3, "EnOcean set $name $cmd $setpointTemp";
- shift(@a);
- } else {
- return "Usage: $cmd value wrong.";
- }
- $updateState = 2;
- } elsif ($cmd =~ m/^liftSet|runInit|valveOpens|valveCloses$/) {
- # unattended?
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "waitingCmds", $cmd);
- readingsEndUpdate($hash, 0);
- # stop PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- CommandDeleteReading(undef, "$name setpointSet");
- CommandDeleteReading(undef, "$name setpointTempSet");
- Log3 $name, 3, "EnOcean set $name $cmd";
- $updateState = 2;
- } else {
- $cmdList .= "setpointTemp:slider,0,1,40 " if (AttrVal($name, "pidCtrl", 'on') eq 'on' || AttrVal($name, "manufID", '7FF') ne '049');
- $cmdList .= "setpoint:slider,0,5,100 desired-temp liftSet:noArg runInit:noArg valveOpens:noArg valveCloses:noArg";
- return "Unknown command " . $cmd . ", choose one of " . $cmdList;
- }
- } elsif ($st eq "hvac.04") {
- # heating radiator valve actuating drive (EEP A5-20-04)
- $rorg = "A5";
- my $setpointTemp = ReadingsVal($name, "setpointTemp", 20);
- my $temperature = ReadingsVal($name, "temperature", 20);
- if ($cmd eq "setpoint") {
- if (defined $a[1] && $a[1] =~ m/^\d+$/ && $a[1] >= 0 && $a[1] <= 100) {
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "setpointSet", $a[1]);
- readingsBulkUpdate($hash, "waitingCmds", $cmd);
- readingsEndUpdate($hash, 0);
- # stop PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- CommandDeleteReading(undef, "$name setpointTempSet");
- Log3 $name, 3, "EnOcean set $name $cmd $a[1]";
- shift(@a);
- } else {
- return "Usage: $cmd value wrong.";
- }
- $updateState = 2;
- } elsif ($cmd =~ m/^desired-temp|setpointTemp$/) {
- if (defined $a[1] && $a[1] =~ m/^\d+(\.\d)?$/ && $a[1] >= 10 && $a[1] <= 30) {
- $cmd = "setpointTemp";
- $setpointTemp = $a[1];
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "setpointTempSet", sprintf("%0.1f", $setpointTemp));
- readingsBulkUpdate($hash, "waitingCmds", "setpointTemp");
- readingsEndUpdate($hash, 0);
- # PID regulator active
- #####
- my $activatePID = AttrVal($name, 'pidCtrl', 'on') eq 'on' ? 'start' : 'stop';
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, $activatePID, '');
- CommandDeleteReading(undef, "$name setpointSet");
- Log3 $name, 3, "EnOcean set $name $cmd $setpointTemp";
- shift(@a);
- } else {
- return "Usage: $cmd value wrong.";
- }
- $updateState = 2;
- } elsif ($cmd =~ m/^runInit|valveOpens|valveCloses$/) {
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "waitingCmds", $cmd);
- readingsEndUpdate($hash, 0);
- # stop PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- CommandDeleteReading(undef, "$name setpointSet");
- CommandDeleteReading(undef, "$name setpointTempSet");
- Log3 $name, 3, "EnOcean set $name $cmd";
- $updateState = 2;
- } else {
- $cmdList .= "setpointTemp:slider,10,1,30 " if (AttrVal($name, "pidCtrl", 'on') eq 'on' || AttrVal($name, "model", '') eq 'Holter_OEM');
- $cmdList .= "setpoint:slider,0,5,100 " if (AttrVal($name, "pidCtrl", 'on') eq 'off' && AttrVal($name, "model", '') ne 'Holter_OEM');
- $cmdList .= "runInit:noArg valveCloses:noArg valveOpens:noArg";
- return "Unknown command " . $cmd . ", choose one of " . $cmdList;
- }
- } elsif ($st eq "hvac.10") {
- # Generic HVAC Interface (EEP A5-20-10)
- $rorg = "A5";
- my %ctrlFunc = (
- "off" => 1,
- "on" => 2,
- "occupancy" => 3,
- "ctrl" => 4,
- "fanSpeed" => 5,
- "vanePosition" => 6,
- "mode" => 7,
- "teach" => 255,
- );
- my %mode = (
- "auto" => 0,
- "heat" => 1,
- "morning_warmup" => 2,
- "cool" => 3,
- "night_purge" => 4,
- "precool" => 5,
- "off" => 6,
- "test" => 7,
- "emergency_heat" => 8,
- "fan_only" => 9,
- "free_cool" => 10,
- "ice" => 11,
- "max_heat" => 12,
- "eco" => 13,
- "dehumidification" => 14,
- "calibration" => 15,
- "emergency_cool" => 16,
- "emergency_stream" => 17,
- "max_cool" => 18,
- "hvc_load" => 19,
- "no_load" => 20,
- "auto_heat" => 31,
- "auto_cool" => 32,
- );
- my %vanePosition = (
- "auto" => 0,
- "horizontal" => 1,
- "position_2" => 2,
- "position_3" => 3,
- "position_4" => 4,
- "vertical" => 5,
- "swing" => 6,
- "vertical_swing" => 11,
- "horizontal_swing" => 12,
- "hor_vert_swing" => 13,
- "stop_swing" => 14,
- );
- my $ctrlFuncID;
- if (defined $ctrlFunc{$cmd}) {
- $ctrlFuncID = $ctrlFunc{$cmd};
- } else {
- $cmdList .= "ctrl mode:" . join(",", sort keys %mode) . " fanSpeed:auto,1,2,3,4,5,6,7,8,9,10,11,12,13,14 " .
- "occupancy:occupied,off,standby,unoccupied on:noArg off:noArg teach:noArg vanePosition:" .
- join(",", sort keys %vanePosition);
- return SetExtensions ($hash, $cmdList, $name, @a);
- }
- $ctrl = ReadingsVal($name, "ctrl", "auto");
- my $fanSpeed = ReadingsVal($name, "fanSpeed", "auto");
- my $mode = ReadingsVal($name, "mode", "off");
- my $occupancy = ReadingsVal($name, "occupancy", "off");
- my $powerSwitch = ReadingsVal($name, "powerSwitch", "off");
- my $vanePosition = ReadingsVal($name, "vanePosition", "auto");
- my ($ctrlParam1, $ctrlParam2, $ctrlParam3, $setCmd) = (0, 0, 0, 8);
- if($ctrlFuncID == 255) {
- # teach-in EEP A5-20-10, Manufacturer "Multi user Manufacturer ID"
- $ctrlParam1 = 0x80;
- $ctrlParam2 = 0x87;
- $ctrlParam3 = 0xFF;
- $setCmd = 0x80;
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDef", "biDir");
- EnOcean_4BSRespWait(undef, $hash, $subDef);
- readingsSingleUpdate($hash, "teach", "4BS teach-in sent, response requested", 1);
- $updateState = 0;
- } elsif ($ctrlFuncID == 1) {
- # off
- $powerSwitch = "off";
- $updateState = 0;
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "powerSwitch", "off");
- readingsBulkUpdate($hash, "state", "off");
- readingsEndUpdate($hash, 0);
- #####
- SetExtensionsCancel($hash);
- } elsif ($ctrlFuncID == 2) {
- # on
- $powerSwitch = "on";
- $updateState = 0;
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "powerSwitch", "on");
- readingsBulkUpdate($hash, "state", "off");
- readingsEndUpdate($hash, 0);
- #####
- SetExtensionsCancel($hash);
- } elsif ($ctrlFuncID == 3) {
- # occupancy
- if (defined $a[1] && $a[1] =~ m/^occupied|standby|unoccupied|off$/) {
- $occupancy = $a[1];
- readingsSingleUpdate($hash, "occupancy", $a[1], 0);
- shift(@a);
- } else {
- return "Usage: $cmd value wrong.";
- }
- $updateState = 0;
- } elsif ($ctrlFuncID == 4) {
- # ctrl
- if (defined $a[1] && ($a[1] =~ m/^auto$/ || $a[1] =~ m/^\d+$/ && $a[1] >= 0 && $a[1] <= 100)) {
- $ctrl = $a[1];
- readingsSingleUpdate($hash, "ctrl", $a[1], 0);
- shift(@a);
- } else {
- return "Usage: $cmd value wrong.";
- }
- $updateState = 0;
- } elsif ($ctrlFuncID == 5) {
- # fanSpeed
- if (defined $a[1] && ($a[1] =~ m/^auto$/ || $a[1] =~ m/^\d+$/ && $a[1] > 0 && $a[1] < 15)) {
- $fanSpeed = $a[1];
- readingsSingleUpdate($hash, "fanSpeed", $a[1], 0);
- shift(@a);
- } else {
- return "Usage: $cmd value wrong.";
- }
- $updateState = 0;
- } elsif ($ctrlFuncID == 6) {
- # vanePosition
- my $vanePositionValues = join("|", keys %vanePosition);
- if (defined $a[1] && $a[1] =~ m/^$vanePositionValues$/) {
- $vanePosition = $a[1];
- readingsSingleUpdate($hash, "vanePosition", $a[1], 0);
- shift(@a);
- } else {
- return "Usage: $cmd value wrong.";
- }
- $updateState = 0;
- } elsif ($ctrlFuncID == 7) {
- # mode
- my $modeValues = join("|", keys %mode);
- if (defined $a[1] && $a[1] =~ m/^$modeValues$/) {
- $mode = $a[1];
- readingsSingleUpdate($hash, "mode", $a[1], 0);
- shift(@a);
- } else {
- return "Usage: $cmd value wrong.";
- }
- $updateState = 0;
- }
- if ($cmd ne "teach") {
- $ctrlParam1 = $mode{$mode};
- $vanePosition = $vanePosition{$vanePosition};
- $fanSpeed = 0 if ($fanSpeed eq "auto");
- $ctrlParam2 = $vanePosition << 4 || $fanSpeed;
- $ctrl = 255 if ($ctrl eq "auto");
- $ctrlParam3 = $ctrl;
- if ($occupancy eq "occupied") {
- $occupancy = 0;
- } elsif ($occupancy eq "standby") {
- $occupancy = 1;
- } elsif ($occupancy eq "unoccupied") {
- $occupancy = 2;
- } else {
- $occupancy = 3;
- }
- $powerSwitch = $powerSwitch eq "on" ? 1 : 0;
- $setCmd |= $occupancy << 1 | $powerSwitch;
- }
- $data = sprintf "%02X%02X%02X%02X", $ctrlParam1, $ctrlParam2, $ctrlParam3, $setCmd;
- Log3 $name, 3, "EnOcean set $name $cmd";
- } elsif ($st eq "hvac.11") {
- # Generic HVAC Interface - Error Control (EEP A5-20-11)
- $rorg = "A5";
- my %ctrlFunc = (
- "externalDisable" => 1,
- "remoteCtrl" => 2,
- "window" => 3,
- "teach" => 255,
- );
- my $ctrlFuncID;
- if (defined $ctrlFunc{$cmd}) {
- $ctrlFuncID = $ctrlFunc{$cmd};
- } else {
- $cmdList .= "externalDisable:disabled,enabled remoteCtrl:disabled,enabled teach:noArg window:closed,opened";
- return "Unknown argument " . $cmd . ", choose one of " . $cmdList;
- }
- my $extern = ReadingsVal($name, "externalDisable", "enable");
- my $remote = ReadingsVal($name, "remoteCtrl", "enable");
- my $window = ReadingsVal($name, "window", "closed");
- my ($ctrlParam1, $ctrlParam2, $ctrlParam3, $setCmd) = (0, 0, 0, 8);
- if($ctrlFuncID == 255) {
- # teach-in EEP A5-20-11, Manufacturer "Multi user Manufacturer ID"
- $ctrlParam1 = 0x80;
- $ctrlParam2 = 0x8F;
- $ctrlParam3 = 0xFF;
- $setCmd = 0x80;
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDef", "biDir");
- EnOcean_4BSRespWait(undef, $hash, $subDef);
- readingsSingleUpdate($hash, "teach", "4BS teach-in sent, response requested", 1);
- $updateState = 0;
- } elsif ($ctrlFuncID == 1) {
- # external disablement
- if (defined $a[1] && $a[1] =~ m/^disabled|enabled$/) {
- $extern = $a[1];
- readingsSingleUpdate($hash, "externalDisable", $a[1], 0);
- shift(@a);
- } else {
- return "Usage: $cmd value wrong.";
- }
- $updateState = 0;
- } elsif ($ctrlFuncID == 2) {
- # disable remote controller
- if (defined $a[1] && $a[1] =~ m/^disabled|enabled$/) {
- $remote = $a[1];
- readingsSingleUpdate($hash, "remoteCtrl", $a[1], 0);
- shift(@a);
- } else {
- return "Usage: $cmd value wrong.";
- }
- $updateState = 0;
- } elsif ($ctrlFuncID == 3) {
- # window contact
- if (defined $a[1] && $a[1] =~ m/^closed|opened$/) {
- $window = $a[1];
- readingsSingleUpdate($hash, "window", $a[1], 0);
- shift(@a);
- } else {
- return "Usage: $cmd value wrong.";
- }
- $updateState = 0;
- }
- if ($cmd ne "teach") {
- $ctrlParam3 = $extern eq "disabled" ? 1 : 0;
- $setCmd |= ($remote eq "disabled" ? 1 : 0) << 2 | ($window eq "closed" ? 1 : 0) << 1;
- }
- $data = sprintf "%02X%02X%02X%02X", $ctrlParam1, $ctrlParam2, $ctrlParam3, $setCmd;
- Log3 $name, 3, "EnOcean set $name $cmd";
- } elsif ($st eq "gateway") {
- # Gateway (EEP A5-38-08)
- # select Command from attribute gwCmd or command line
- my $gwCmd = AttrVal($name, "gwCmd", undef);
- if ($gwCmd && $EnO_gwCmd{$gwCmd}) {
- # command from attribute gwCmd
- if ($EnO_gwCmd{$cmd}) {
- # shift $cmd
- $cmd = $a[1];
- shift(@a);
- }
- } elsif ($EnO_gwCmd{$cmd}) {
- # command from command line
- $gwCmd = $cmd;
- $cmd = $a[1];
- shift(@a);
- } else {
- return "Unknown Gateway command " . $cmd . ", choose one of " . $cmdList . join(" ", sort keys %EnO_gwCmd);
- }
- my $gwCmdID;
- $rorg = "A5";
- my $setCmd = 0;
- my $time = 0;
- if ($gwCmd eq "switching") {
- # Switching
- $gwCmdID = 1;
- if($cmd eq "teach") {
- # teach-in EEP A5-38-08, Manufacturer "Multi user Manufacturer ID"
- #$data = sprintf "%02X000000", $gwCmdID;
- if ($model =~ m/TF$/) {
- $data = "E0400D80";
- } else {
- $data = "E047FF80";
- }
- $attr{$name}{eep} = "A5-38-08";
- CommandDeleteReading(undef, "$name .*");
- readingsSingleUpdate($hash, "teach", "4BS teach-in sent", 1);
- $updateState = 0;
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDef", "confirm");
- EnOcean_setTeachConfirmWaitHash(undef, $hash);
- } elsif ($cmd eq "on") {
- $setCmd = 9;
- readingsSingleUpdate($hash, "block", "unlock", 1);
- if ($a[1]) {
- return "Usage: $cmd [lock|unlock]" if (($a[1] ne "lock") && ($a[1] ne "unlock"));
- if ($a[1] eq "lock") {
- $setCmd = $setCmd | 4;
- readingsSingleUpdate($hash, "block", "lock", 1);
- }
- shift(@a);
- }
- #$updateState = 0;
- #####
- SetExtensionsCancel($hash);
- $data = sprintf "%02X%04X%02X", $gwCmdID, $time, $setCmd;
- } elsif ($cmd eq "off") {
- if ($model =~ m/FSA12$/) {
- $setCmd = 0x0E;
- } else {
- $setCmd = 8;
- }
- readingsSingleUpdate($hash, "block", "unlock", 1);
- if ($a[1]) {
- return "Usage: $cmd [lock|unlock]" if (($a[1] ne "lock") && ($a[1] ne "unlock"));
- if ($a[1] eq "lock") {
- $setCmd = $setCmd | 4;
- readingsSingleUpdate($hash, "block", "lock", 1);
- }
- shift(@a);
- }
- #$updateState = 0;
- #####
- SetExtensionsCancel($hash);
- $data = sprintf "%02X%04X%02X", $gwCmdID, $time, $setCmd;
- } elsif ($cmd eq "local") {
- if ($a[1]) {
- return "Usage: $cmd [learn]" if ($a[1] ne "learn");
- if ($a[1] eq "learn") {
- $cmd = 'off';
- $setCmd = $setCmd | 0x28;
- readingsSingleUpdate($hash, "block", "unlock", 1);
- }
- shift(@a);
- }
- #$updateState = 0;
- $data = sprintf "%02X%04X%02X", $gwCmdID, $time, $setCmd;
- } else {
- my $cmdList = "local:learn on:noArg off:noArg teach:noArg";
- return SetExtensions ($hash, $cmdList, $name, @a);
- }
- } elsif ($gwCmd eq "dimming") {
- # Dimming
- $gwCmdID = 2;
- my $dimMax = AttrVal($name, "dimMax", 255);
- my $dimMin = AttrVal($name, "dimMin", "off");
- if ($dimMax =~ m/^\d+$/ && $dimMin =~ m/^\d+$/ && $dimMin > $dimMax) {
- ($dimMax, $dimMin) = ($dimMin , $dimMax);
- }
- my $dimVal = ReadingsVal($name, "dim", undef);
- my $rampTime = AttrVal($name, "rampTime", 1);
- my $sendDimCmd = 0;
- $setCmd = 9;
- if ($cmd =~ m/^\d+$/) {
- # interpretive numeric value as dimming
- unshift(@a, 'dim');
- $cmd = 'dim';
- }
- if ($cmd eq "teach") {
- # teach-in EEP A5-38-08, Manufacturer "Multi user Manufacturer ID"
- #$data = "E047FF80";
- # teach-in Eltako
- if ($model =~ m/TF$/) {
- $data = "E0400D80";
- } else {
- $data = "02000000";
- }
- $attr{$name}{eep} = "A5-38-08";
- CommandDeleteReading(undef, "$name .*");
- readingsSingleUpdate($hash, "teach", "4BS teach-in sent", 1);
- $updateState = 0;
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDef", "confirm");
- EnOcean_setTeachConfirmWaitHash(undef, $hash);
- } elsif ($cmd eq "dim") {
- return "Usage: $cmd dim/% [rampTime/s lock|unlock]"
- if(@a < 2 || $a[1] !~ m/^\d+$/ || $a[1] < 0 || $a[1] > 100);
- # for eltako relative (0-100) (but not compliant to EEP because DB0.2 is 0)
- # >> if manufID needed: set DB2.0
- $dimVal = $a[1];
- if ($dimVal > 0) {
- readingsSingleUpdate ($hash, "dimValueStored", $dimVal, 1);
- }
- shift(@a);
- if (defined($a[1])) {
- return "Usage: $cmd dim/% [rampTime/s lock|unlock]" if ($a[1] !~ m/^\d+$/);
- $rampTime = $a[1];
- shift(@a);
- }
- $sendDimCmd = 1;
- } elsif ($cmd eq "dimup") {
- return "Usage: $cmd dim/% [rampTime/s lock|unlock]"
- if(@a < 2 || $a[1] !~ m/^\d+$/ || $a[1] < 0 || $a[1] > 100);
- $dimVal += $a[1];
- if ($dimVal > 0) {
- readingsSingleUpdate ($hash, "dimValueStored", $dimVal, 1);
- }
- shift(@a);
- if (defined($a[1])) {
- return "Usage: $cmd dim/% [rampTime/s lock|unlock]" if ($a[1] !~ m/^\d+$/);
- $rampTime = $a[1];
- shift(@a);
- }
- $sendDimCmd = 1;
- } elsif ($cmd eq "dimdown") {
- return "Usage: $cmd dim/% [rampTime/s lock|unlock]"
- if(@a < 2 || $a[1] !~ m/^\d+$/ || $a[1] < 0 || $a[1] > 100);
- $dimVal -= $a[1];
- if ($dimVal > 0) {
- readingsSingleUpdate ($hash, "dimValueStored", $dimVal, 1);
- }
- shift(@a);
- if (defined($a[1])) {
- return "Usage: $cmd dim/% [rampTime/s lock|unlock]" if ($a[1] !~ m/^\d+$/);
- $rampTime = $a[1];
- shift(@a);
- }
- $sendDimCmd = 1;
- } elsif ($cmd eq "on") {
- $rampTime = 1;
- my $dimValueOn = AttrVal($name, "dimValueOn", 100);
- if ($dimValueOn eq "stored") {
- $dimVal = ReadingsVal($name, "dimValueStored", 100);
- if ($dimVal < 1) {
- $dimVal = 100;
- readingsSingleUpdate ($hash, "dimValueStored", $dimVal, 1);
- }
- } elsif ($dimValueOn eq "last") {
- $dimVal = ReadingsVal ($name, "dimValueLast", 100);
- if ($dimVal < 1) { $dimVal = 100; }
- } else {
- if ($dimValueOn !~ m/^\d+$/) {
- $dimVal = 100;
- } elsif ($dimValueOn > 100) {
- $dimVal = 100;
- } elsif ($dimValueOn < 1) {
- $dimVal = 1;
- } else {
- $dimVal = $dimValueOn;
- }
- }
- $sendDimCmd = 1;
- } elsif ($cmd eq "off") {
- $dimVal = 0;
- $rampTime = 1;
- $setCmd = 8;
- $sendDimCmd = 1;
- } elsif ($cmd eq "local") {
- if ($a[1]) {
- return "Usage: $cmd [learn]" if ($a[1] ne "learn");
- if ($a[1] eq "learn") {
- $cmd = 'off';
- $dimVal = 0;
- $rampTime = 1;
- $setCmd = 0x2C;
- readingsSingleUpdate($hash, "block", "lock", 1);
- }
- shift(@a);
- }
- #$updateState = 0;
- #####
- SetExtensionsCancel($hash);
- $data = sprintf "%02X%02X%02X%02X", $gwCmdID, $dimVal, $rampTime, $setCmd;
- } else {
- my $cmdList = "dim:slider,0,1,100 local:learn on:noArg off:noArg teach:noArg";
- return SetExtensions ($hash, $cmdList, $name, @a);
- }
- if ($sendDimCmd) {
- readingsSingleUpdate($hash, "block", "unlock", 1);
- if (defined $a[1]) {
- return "Usage: $cmd dim/% [rampTime/s lock|unlock]" if ($a[1] ne "lock" && $a[1] ne "unlock");
- # Eltako devices: lock dimming value
- if ($manufID eq "00D" && $a[1] eq "lock" ) {
- $setCmd = $setCmd | 4;
- readingsSingleUpdate($hash, "block", "lock", 1);
- }
- shift(@a);
- } else {
- # Dimming value relative
- if ($manufID ne "00D") {$setCmd = $setCmd | 4;}
- }
- if ($cmd eq "off" && $dimMin =~ m/^\d+$/ && $dimMin == 0) {
- # switch off
- } elsif ($cmd eq "off" && $dimMin =~ m/^\d+$/) {
- $dimVal = $dimMin;
- $setCmd = 9;
- } elsif ($dimMax eq "off" || $dimVal == 0 && $dimMin eq "off" || $dimVal < 0) {
- # switch off
- $dimVal = 0;
- $setCmd = 8;
- } elsif ($dimMin eq "off") {
- } elsif ($dimVal < $dimMin) {
- $dimVal = $dimMin;
- }
- $dimVal = $dimMax if ($dimVal > $dimMax);
- $dimVal = 100 if ($dimVal > 100);
- $rampTime = 0 if ($rampTime < 0);
- $rampTime = 255 if ($rampTime > 255);
- #$updateState = 0;
- readingsSingleUpdate ($hash, "dim", $dimVal, 1);
- $data = sprintf "%02X%02X%02X%02X", $gwCmdID, $dimVal, $rampTime, $setCmd;
- }
- } elsif ($gwCmd eq "setpointShift") {
- $gwCmdID = 3;
- if ($cmd eq "teach") {
- # teach-in EEP A5-38-08, Manufacturer "Multi user Manufacturer ID"
- $data = "E047FF80";
- $attr{$name}{eep} = "A5-38-08";
- CommandDeleteReading(undef, "$name .*");
- readingsSingleUpdate($hash, "teach", "4BS teach-in sent", 1);
- $updateState = 0;
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDef", "confirm");
- EnOcean_setTeachConfirmWaitHash(undef, $hash);
- } elsif ($cmd eq "shift") {
- if (($a[1] =~ m/^[+-]?\d+(\.\d+)?$/) && ($a[1] >= -12.7) && ($a[1] <= 12.8)) {
- #$updateState = 0;
- $data = sprintf "%02X00%02X08", $gwCmdID, ($a[1] + 12.7) * 10;
- shift(@a);
- } else {
- return "Usage: $a[1] is not numeric or out of range";
- }
- } else {
- return "Unknown argument $cmd, choose one of teach:noArg shift";
- }
- } elsif ($gwCmd eq "setpointBasic") {
- $gwCmdID = 4;
- if($cmd eq "teach") {
- # teach-in EEP A5-38-08, Manufacturer "Multi user Manufacturer ID"
- $data = "E047FF80";
- $attr{$name}{eep} = "A5-38-08";
- CommandDeleteReading(undef, "$name .*");
- readingsSingleUpdate($hash, "teach", "4BS teach-in sent", 1);
- $updateState = 0;
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDef", "confirm");
- EnOcean_setTeachConfirmWaitHash(undef, $hash);
- } elsif ($cmd eq "basic") {
- if (($a[1] =~ m/^[+-]?\d+(\.\d+)?$/) && ($a[1] >= 0) && ($a[1] <= 51.2)) {
- #$updateState = 0;
- $data = sprintf "%02X00%02X08", $gwCmdID, $a[1] * 5;
- shift(@a);
- } else {
- return "Usage: $cmd parameter is not numeric or out of range.";
- }
- } else {
- return "Unknown argument $cmd, choose one of teach:noArg basic";
- }
- } elsif ($gwCmd eq "controlVar") {
- $gwCmdID = 5;
- my $controlVar = ReadingsVal($name, "controlVar", 0);
- if($cmd eq "teach") {
- # teach-in EEP A5-38-08, Manufacturer "Multi user Manufacturer ID"
- $data = "E047FF80";
- $attr{$name}{eep} = "A5-38-08";
- CommandDeleteReading(undef, "$name .*");
- readingsSingleUpdate($hash, "teach", "4BS teach-in sent", 1);
- $updateState = 0;
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDef", "confirm");
- EnOcean_setTeachConfirmWaitHash(undef, $hash);
- } elsif ($cmd eq "presence") {
- if ($a[1] eq "standby") {
- $setCmd = 0x0A;
- } elsif ($a[1] eq "absent") {
- $setCmd = 9;
- } elsif ($a[1] eq "present") {
- $setCmd = 8;
- } else {
- return "Usage: $cmd parameter unknown.";
- }
- shift(@a);
- $data = sprintf "%02X00%02X%02X", $gwCmdID, $controlVar, $setCmd;
- } elsif ($cmd eq "energyHoldOff") {
- if ($a[1] eq "normal") {
- $setCmd = 8;
- } elsif ($a[1] eq "holdoff") {
- $setCmd = 0x0C;
- } else {
- return "Usage: $cmd parameter unknown.";
- }
- shift(@a);
- $data = sprintf "%02X00%02X%02X", $gwCmdID, $controlVar, $setCmd;
- } elsif ($cmd eq "controllerMode") {
- if ($a[1] eq "auto") {
- $setCmd = 8;
- } elsif ($a[1] eq "heating") {
- $setCmd = 0x28;
- } elsif ($a[1] eq "cooling") {
- $setCmd = 0x48;
- } elsif ($a[1] eq "off" || $a[1] eq "BI") {
- $setCmd = 0x68;
- } else {
- return "Usage: $cmd parameter unknown.";
- }
- shift(@a);
- $data = sprintf "%02X00%02X%02X", $gwCmdID, $controlVar, $setCmd;
- } elsif ($cmd eq "controllerState") {
- if ($a[1] eq "auto") {
- $setCmd = 8;
- } elsif ($a[1] eq "override") {
- $setCmd = 0x18;
- if (defined $a[2] && ($a[2] =~ m/^[+-]?\d+$/) && ($a[2] >= 0) && ($a[2] <= 100) ) {
- $controlVar = $a[2] * 255;
- shift(@a);
- } else {
- return "Usage: Control Variable Override is not numeric or out of range.";
- }
- } else {
- return "Usage: $cmd parameter unknown.";
- }
- shift(@a);
- #$updateState = 0;
- $data = sprintf "%02X00%02X%02X", $gwCmdID, $controlVar, $setCmd;
- } else {
- return "Unknown argument, choose one of teach:noArg presence:absent,present,standby energyHoldOff:holdoff,normal controllerMode:cooling,heating,off controllerState:auto,override";
- }
- } elsif ($gwCmd eq "fanStage") {
- $gwCmdID = 6;
- if($cmd eq "teach") {
- # teach-in EEP A5-38-08, Manufacturer "Multi user Manufacturer ID"
- $data = "E047FF80";
- $attr{$name}{eep} = "A5-38-08";
- CommandDeleteReading(undef, "$name .*");
- readingsSingleUpdate($hash, "teach", "4BS teach-in sent", 1);
- $updateState = 0;
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDef", "confirm");
- EnOcean_setTeachConfirmWaitHash(undef, $hash);
- } elsif ($cmd eq "stage") {
- if ($a[1] eq "auto") {
- #$updateState = 0;
- $data = sprintf "%02X00%02X08", $gwCmdID, 255;
- } elsif ($a[1] && $a[1] =~ m/^[0-3]$/) {
- #$updateState = 0;
- $data = sprintf "%02X00%02X08", $gwCmdID, $a[1];
- } else {
- return "Usage: $cmd parameter is not numeric or out of range"
- }
- shift(@a);
- } else {
- return "Unknown argument, choose one of teach:noArg stage:auto,0,1,2,3";
- }
- } elsif ($gwCmd eq "blindCmd") {
- $gwCmdID = 7;
- my %blindFunc = (
- "status" => 0,
- "stop" => 1,
- "opens" => 2,
- "closes" => 3,
- "position" => 4,
- "up" => 5,
- "down" => 6,
- "runtimeSet" => 7,
- "angleSet" => 8,
- "positionMinMax" => 9,
- "angleMinMax" => 10,
- "positionLogic" => 11,
- "teach" => 255,
- );
- my @blindFunc = (
- "position:slider,0,1,100",
- "opens:noArg",
- "closes:noArg",
- "up",
- "down",
- "stop:noArg",
- "status:noArg",
- "runtimeSet",
- "angleSet",
- "positionMinMax",
- "angleMinMax",
- "positionLogic:normal,inverse",
- "teach:noArg",
- );
- my $blindFuncID;
- if (defined $blindFunc {$cmd}) {
- $blindFuncID = $blindFunc {$cmd};
- } elsif ($cmd =~ m/^\d+$/) {
- # interpretive numeric value as position
- unshift(@a, 'position');
- $cmd = 'position';
- $blindFuncID = 4;
- } else {
- return "Unknown Gateway Blind Central Function " . $cmd . ", choose one of ". join(" ", @blindFunc);
- }
- my $blindParam1 = 0;
- my $blindParam2 = 0;
- $setCmd = $blindFuncID << 4 | 8;
- if($blindFuncID == 255) {
- # teach-in EEP A5-38-08, Manufacturer "Multi user Manufacturer ID"
- $gwCmdID = 0xE0;
- $blindParam1 = 0x47;
- $blindParam2 = 0xFF;
- $setCmd = 0x80;
- $attr{$name}{eep} = "A5-38-08";
- CommandDeleteReading(undef, "$name .*");
- readingsSingleUpdate($hash, "teach", "4BS teach-in sent", 1);
- $updateState = 0;
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDef", "confirm");
- } elsif ($blindFuncID == 0) {
- # status
- $updateState = 0;
- } elsif ($blindFuncID == 1) {
- # stop
- $updateState = 0;
- } elsif ($blindFuncID == 2) {
- # opens
- $updateState = 0;
- } elsif ($blindFuncID == 3) {
- # closes
- $updateState = 0;
- } elsif ($blindFuncID == 4) {
- # position
- if (defined $a[1] && $a[1] =~ m/^[+-]?\d+$/ && $a[1] >= 0 && $a[1] <= 100) {
- $blindParam1 = $a[1];
- shift(@a);
- if (defined $a[1]) {
- if ($a[1] =~ m/^[+-]?\d+$/ && $a[1] >= -180 && $a[1] <= 180) {
- # set angle
- $blindParam2 = abs($a[1]) / 2;
- if ($a[1] < 0) {$blindParam2 |= 0x80;}
- shift(@a);
- } else {
- return "Usage: $cmd variable is not numeric or out of range.";
- }
- } else {
- # set angle defaults
- my $positionLogic = ReadingsVal($name, 'positionLogic', 'normal');
- my $angleMin = ReadingsVal($name, 'angleMin', -180);
- my $angleMax = ReadingsVal($name, 'angleMax', 180);
- if ($blindParam1 == 0) {
- $blindParam2 = $positionLogic eq 'normal' ? $angleMax : $angleMin;
- } elsif ($blindParam1 == 100) {
- $blindParam2 = $positionLogic eq 'normal' ? $angleMin : $angleMax;
- } else {
- $blindParam2 = $angleMin + ($angleMax - $angleMin) / 2;
- }
- if ($blindParam2 < 0) {
- $blindParam2 = abs($blindParam2) / 2;
- $blindParam2 |= 0x80;
- } else {
- $blindParam2 = $blindParam2 / 2;
- }
- }
- } else {
- return "Usage: $cmd variable is not numeric or out of range.";
- }
- # angle und position value available
- $setCmd |= 2;
- $updateState = 0;
- } elsif ($blindFuncID == 5 || $blindFuncID == 6) {
- # up / down
- if (defined $a[1] && $a[1] =~ m/^[+-]?\d+$/ && $a[1] >= 0 && $a[1] <= 255) {
- $blindParam1 = $a[1];
- if (defined $a[2] && $a[2] =~ m/^[+-]?\d+(\.\d+)?$/ && $a[2] >= 0 && $a[2] <= 25.5) {
- $blindParam2 = $a[2] * 10;
- shift(@a);
- } else {
- return "Usage: $cmd variable is not numeric or out of range.";
- }
- shift(@a);
- } else {
- return "Usage: $cmd variable is not numeric or out of range.";
- }
- $updateState = 0;
- } elsif ($blindFuncID == 7) {
- # runtimeSet
- if (defined $a[1] && $a[1] =~ m/^[+-]?\d+$/ && $a[1] >= 0 && $a[1] <= 255) {
- $blindParam1 = $a[1];
- if (defined $a[2] && $a[2] =~ m/^[+-]?\d+$/ && $a[2] >= 0 && $a[2] <= 255) {
- $blindParam2 = $a[2];
- shift(@a);
- } else {
- return "Usage: $cmd variable is not numeric or out of range.";
- }
- shift(@a);
- } else {
- return "Usage: $cmd variable is not numeric or out of range.";
- }
- readingsSingleUpdate($hash, "runTimeUp", $blindParam1, 1);
- readingsSingleUpdate($hash, "runTimeDown", $blindParam2, 1);
- $updateState = 0;
- } elsif ($blindFuncID == 8) {
- # angleSet
- if (defined $a[1] && $a[1] =~ m/^[+-]?\d+(\.\d+)?$/ && $a[1] >= 0 && $a[1] <= 25.5) {
- $blindParam1 = $a[1] * 10;
- ##
- readingsSingleUpdate($hash, "angleTime", (sprintf "%0.1f", $a[1]), 1);
- shift(@a);
- } else {
- return "Usage: $cmd variable is not numeric or out of range.";
- }
- $updateState = 0;
- } elsif ($blindFuncID == 9) {
- # positionMinMax
- if (defined $a[1] && $a[1] =~ m/^[+-]?\d+$/ && $a[1] >= 0 && $a[1] <= 100) {
- $blindParam1 = $a[1];
- if (defined $a[2] && $a[2] =~ m/^[+-]?\d+$/ && $a[2] >= 0 && $a[2] <= 100) {
- $blindParam2 = $a[2];
- shift(@a);
- } else {
- return "Usage: $cmd variable is not numeric or out of range.";
- }
- # angle und position value available
- $setCmd |= 2;
- shift(@a);
- } else {
- return "Usage: $cmd variable is not numeric or out of range.";
- }
- if ($blindParam1 > $blindParam2) {($blindParam1, $blindParam2) = ($blindParam2, $blindParam1);}
- readingsSingleUpdate($hash, "positionMin", $blindParam1, 1);
- readingsSingleUpdate($hash, "positionMax", $blindParam2, 1);
- $updateState = 0;
- } elsif ($blindFuncID == 10) {
- # angleMinMax
- if (defined $a[1] && $a[1] =~ m/^[+-]?\d+$/ && $a[1] >= -180 && $a[1] <= 180) {
- if (!defined $a[2] || $a[2] !~ m/^[+-]?\d+$/ || $a[2] < -180 || $a[2] > 180) {
- return "Usage: $cmd variable is not numeric or out of range.";
- }
- if ($a[1] > $a[2]) {($a[1], $a[2]) = ($a[2], $a[1]);}
- $blindParam1 = abs($a[1]) / 2;
- if ($a[1] < 0) {$blindParam1 |= 0x80;}
- $blindParam2 = abs($a[2]) / 2;
- if ($a[2] < 0) {$blindParam2 |= 0x80;}
- # angle und position value available
- $setCmd |= 2;
- } else {
- return "Usage: $cmd variable is not numeric or out of range.";
- }
- readingsSingleUpdate($hash, "angleMin", $a[1], 1);
- readingsSingleUpdate($hash, "angleMax", $a[2], 1);
- splice (@a, 0, 2);
- shift(@a);
- $updateState = 0;
- } elsif ($blindFuncID == 11) {
- # positionLogic
- if ($a[1] eq "normal") {
- $blindParam1 = 0;
- } elsif ($a[1] eq "inverse") {
- $blindParam1 = 1;
- } else {
- return "Usage: $cmd variable is unknown.";
- }
- readingsSingleUpdate($hash, "positionLogic", $a[1], 1);
- shift(@a);
- $updateState = 0;
- } else {
- }
- $setCmd |= 4 if (AttrVal($name, "sendDevStatus", "no") eq "yes");
- $setCmd |= 1 if (AttrVal($name, "serviceOn", "no") eq "yes");
- $data = sprintf "%02X%02X%02X%02X", $gwCmdID, $blindParam1, $blindParam2, $setCmd;
- } else {
- return "Unknown Gateway command " . $cmd . ", choose one of ". $cmdList . join(" ", sort keys %EnO_gwCmd);
- }
- Log3 $name, 3, "EnOcean set $name $cmd";
- } elsif ($st eq "energyManagement.01") {
- # Energy Management, Demand Response (A5-37-01)
- $rorg = "A5";
- $updateState = 0;
- my $drLevel = 15;
- my $powerUsage = 100;
- my $powerUsageLevel = 1;
- my $powerUsageScale = 0;
- my $randomStart = 0;
- my $randomEnd = 0;
- my $randomTime = rand(AttrVal($name, "demandRespRandomTime", 1));
- my $setpoint = 255;
- my $timeout = 0;
- my $threshold = AttrVal($name, "demandRespThreshold", 8);
- if($cmd eq "teach") {
- # teach-in EEP A5-37-01, Manufacturer "Multi user Manufacturer ID"
- $data = "DC0FFF80";
- $attr{$name}{eep} = "A5-37-01";
- CommandDeleteReading(undef, "$name .*");
- readingsSingleUpdate($hash, "teach", "4BS teach-in sent", 1);
- $updateState = 0;
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDef", "confirm");
- Log3 $name, 3, "EnOcean set $name $cmd";
- } elsif ($cmd eq "level") {
- return "Usage: $cmd 0...15 [max|rel [yes|no [yes|no [timeout/min]]]]"
- if(@a < 2 || $a[1] !~ m/^\d+$/ || $a[1] < 0 || $a[1] > 15 );
- $drLevel = $a[1];
- $powerUsage = $a[1] / 15 * 100;
- $powerUsageLevel = $drLevel >= $threshold ? 1 : 0;
- $setpoint = $a[1] * 17;
- shift(@a);
- } elsif ($cmd eq "max") {
- } elsif ($cmd eq "min") {
- $drLevel = 0;
- $powerUsage = 0;
- $powerUsageLevel = 0;
- $setpoint = 0;
- } elsif ($cmd eq "power") {
- return "Usage: $cmd 0...100 [max|rel [yes|no [yes|no [timeout/min]]]]"
- if(@a < 2 || $a[1] !~ m/^\d+$/ || $a[1] < 0 || $a[1] > 100);
- $drLevel = $a[1] / 100 * 15;
- $powerUsage = $a[1];
- $powerUsageLevel = $drLevel >= $threshold ? 1 : 0;
- $setpoint = $a[1] * 2.55;
- shift(@a);
- } elsif ($cmd eq "setpoint") {
- return "Usage: $cmd 0...255 [max|rel [yes|no [yes|no [timeout/min]]]]"
- if(@a < 2 || $a[1] !~ m/^\d+$/ || $a[1] < 0 || $a[1] > 255 );
- $drLevel = $a[1] / 255 * 15 ;
- $powerUsage = $a[1] / 255 * 100;
- $powerUsageLevel = $drLevel >= $threshold ? 1 : 0;
- $setpoint = $a[1];
- shift(@a);
- } else {
- return "Unknown argument " . $cmd . ", choose one of " . $cmdList . "level:slider,0,1,15 max:noArg min:noArg power:slider,0,5,100 setpoint:slider,0,5,255 teach:noArg"
- }
- if ($cmd ne "teach") {
- if (@a > 1) {
- return "Usage: $cmd [<cmdValue>] [max|rel [yes|no [yes|no [timeout/min]]]]" if($a[1] !~ m/^max|rel$/);
- $powerUsageScale = $a[1] eq "rel" ? 0x80 : 0;
- shift(@a);
- }
- if (@a > 1) {
- return "Usage: $cmd [<cmdValue>] [max|rel [yes|no [yes|no [timeout/min]]]]" if($a[1] !~ m/^yes|no$/);
- $randomStart = $a[1] eq "yes" ? 4 : 0;
- shift(@a);
- }
- if (@a > 1) {
- return "Usage: $cmd [<cmdValue>] [max|rel [yes|no [yes|no [timeout/min]]]]" if($a[1] !~ m/^yes|no$/);
- $randomEnd = $a[1] eq "yes" ? 2 : 0;
- shift(@a);
- }
- if (@a > 1) {
- return "Usage: $cmd [<cmdValue>] [max|rel [yes|no [yes|no [timeout/min]]]]"
- if($a[1] !~ m/^\d+$/ || $a[1] < 0 || $a[1] > 3825);
- $timeout = int($a[1] / 15);
- shift(@a);
- }
- $data = sprintf "%02X%02X%02X%02X", $setpoint, $powerUsageScale | $powerUsage, $timeout,
- $drLevel << 4 | $randomStart | $randomEnd | $powerUsageLevel | 8;
- my @db = ($drLevel << 4 | $randomStart | $randomEnd | $powerUsageLevel | 8,
- $timeout, $powerUsageScale | $powerUsage, $setpoint);
- EnOcean_energyManagement_01Parse($hash, @db);
- }
- Log3 $name, 3, "EnOcean set $name $cmd";
- } elsif ($st eq "lightCtrl.01") {
- # Central Command, Extended Lighting-Control (EEP A5-38-09)
- $rorg = "A5";
- my %ctrlFunc = (
- "off" => 1,
- "on" => 2,
- "dimup" => 3,
- "dimdown" => 4,
- "stop" => 5,
- "dim" => 6,
- "rgb" => 7,
- "scene" => 8,
- "dimMinMax" => 9,
- "lampOpHours" => 10,
- "block" => 11,
- "meteringValue" => 12,
- "teach" => 255,
- );
- my $ctrlFuncID;
- if (exists $ctrlFunc{$cmd}) {
- $ctrlFuncID = $ctrlFunc{$cmd};
- } elsif ($cmd =~ m/^\d+$/) {
- # interpretive numeric value as dimming
- unshift(@a, 'dim');
- $cmd = 'dim';
- $ctrlFuncID = 6;
- } else {
- $cmdList .= "dim:slider,0,5,255 dimup:noArg dimdown:noArg on:noArg off:noArg stop:noArg rgb:colorpicker,RGB scene dimMinMax lampOpHours block meteringValue teach:noArg";
- return SetExtensions ($hash, $cmdList, $name, @a);
- }
- my ($ctrlParam1, $ctrlParam2, $ctrlParam3) = (0, 0, 0);
- my $setCmd = $ctrlFuncID << 4 | 8;
- if($ctrlFuncID == 255) {
- # teach-in EEP A5-38-09, Manufacturer "Multi user Manufacturer ID"
- $ctrlParam1 = 0xE1;
- $ctrlParam2 = 0xC7;
- $ctrlParam3 = 0xFF;
- $setCmd = 0x80;
- $attr{$name}{eep} = "A5-38-09";
- CommandDeleteReading(undef, "$name .*");
- readingsSingleUpdate($hash, "teach", "4BS teach-in sent", 1);
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDef", "confirm");
- $updateState = 0;
- } elsif ($ctrlFuncID == 1) {
- # off
- CommandDeleteReading(undef, "$name scene");
- #####
- SetExtensionsCancel($hash);
- $updateState = 0;
- } elsif ($ctrlFuncID == 2) {
- # on
- CommandDeleteReading(undef, "$name scene");
- #####
- SetExtensionsCancel($hash);
- $updateState = 0;
- } elsif ($ctrlFuncID == 3 || $ctrlFuncID == 4) {
- # dimup / dimdown
- my $rampTime = $a[1];
- if (defined $a[1]) {
- if ($a[1] =~ m/^\d+?$/ && $a[1] >= 0 && $a[1] <= 65535) {
- shift(@a);
- } else {
- return "Usage: $cmd ramping time value is not numeric or out of range.";
- }
- } else {
- $rampTime = AttrVal($name, "rampTime", 1);
- }
- $ctrlParam3 = $rampTime & 0xFF;
- $ctrlParam2 = ($rampTime & 0xFF00) >> 8;
- readingsSingleUpdate($hash, "rampTime", $rampTime, 1);
- CommandDeleteReading(undef, "$name scene");
- #####
- SetExtensionsCancel($hash);
- $updateState = 0;
- } elsif ($ctrlFuncID == 5) {
- # stop
- CommandDeleteReading(undef, "$name scene");
- #####
- SetExtensionsCancel($hash);
- $updateState = 0;
- } elsif ($ctrlFuncID == 6) {
- # dim
- if (defined $a[1] && $a[1] =~ m/^\d+$/ && $a[1] >= 0 && $a[1] <= 255) {
- $ctrlParam1 = $a[1];
- shift(@a);
- } else {
- return "Usage: $cmd dimming value is not numeric or out of range.";
- }
- my $rampTime = $a[1];
- if (defined $a[1]) {
- if ($a[1] =~ m/^\d+?$/ && $a[1] >= 0 && $a[1] <= 65535) {
- shift(@a);
- } else {
- return "Usage: $cmd ramping time value is not numeric or out of range.";
- }
- } else {
- $rampTime = AttrVal($name, "rampTime", 1);
- }
- $ctrlParam3 = $rampTime & 0xFF;
- $ctrlParam2 = ($rampTime & 0xFF00) >> 8;
- CommandDeleteReading(undef, "$name scene");
- readingsSingleUpdate($hash, "rampTime", $rampTime, 1);
- #####
- SetExtensionsCancel($hash);
- $updateState = 0;
- } elsif ($ctrlFuncID == 7) {
- # RGB
- if (@a > 1) {
- if ($a[1] =~ m/^[\dA-Fa-f]{6}$/) {
- # red
- $ctrlParam1 = hex substr($a[1], 0, 2);
- # green
- $ctrlParam2 = hex substr($a[1], 2, 2);
- # blue
- $ctrlParam3 = hex substr($a[1], 4, 2);
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "red", $ctrlParam1);
- readingsBulkUpdate($hash, "green", $ctrlParam2);
- readingsBulkUpdate($hash, "blue", $ctrlParam3);
- readingsBulkUpdate($hash, "rgb", uc($a[1]));
- readingsEndUpdate($hash, 0);
- shift(@a);
- } else {
- return "Usage: $cmd value is not hexadecimal or out of range.";
- }
- } else {
- return "Usage: $cmd values are missing";
- }
- $updateState = 0;
- } elsif ($ctrlFuncID == 8) {
- # scene
- if (@a > 2) {
- if ($a[2] =~ m/^\d+?$/ && $a[2] >= 0 && $a[2] <= 15) {
- $ctrlParam3 = $a[2];
- } else {
- return "Usage: $cmd number is not numeric or out of range.";
- }
- if ($a[1] eq "drive") {
- readingsSingleUpdate($hash, "scene", $ctrlParam3, 1);
- splice(@a, 0, 2);
- } elsif ($a[1] eq "store") {
- $ctrlParam3 |= 0x80;
- splice(@a, 0, 2);
- } else {
- return "Usage: $cmd parameter is wrong.";
- }
- } else {
- return "Usage: $cmd values are missing";
- }
- $updateState = 0;
- } elsif ($ctrlFuncID == 9) {
- # dimMinMax
- if (@a > 2) {
- if ($a[1] =~ m/^\d+$/ && $a[1] >= 0 && $a[1] <= 255) {
- # dimming limit min
- $ctrlParam1 = $a[1];
- shift(@a);
- } else {
- return "Usage: $cmd value is not numeric or out of range.";
- }
- if ($a[1] =~ m/^\d+$/ && $a[1] >= 0 && $a[1] <= 255) {
- # dimming limit max
- $ctrlParam2 = $a[1];
- shift(@a);
- } else {
- return "Usage: $cmd value is not numeric or out of range.";
- }
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "dimMin", $ctrlParam1);
- readingsBulkUpdate($hash, "dimMax", $ctrlParam2);
- readingsEndUpdate($hash, 1);
- } else {
- return "Usage: $cmd values are missing";
- }
- $updateState = 0;
- } elsif ($ctrlFuncID == 10) {
- # set operating hours of the lamp
- if (defined $a[1] && $a[1] =~ m/^\d+$/ && $a[1] >= 0 && $a[1] <= 65535) {
- $ctrlParam2 = $a[1] & 0xFF;
- $ctrlParam1 = ($a[1] & 0xFF00) >> 8;
- shift(@a);
- } else {
- return "Usage: $cmd variable is not numeric or out of range.";
- }
- $updateState = 0;
- } elsif ($ctrlFuncID == 11) {
- # block
- if (!defined $a[1]) {
- return "Usage: $cmd values are missing";
- } elsif ($a[1] eq "unlock") {
- $ctrlParam3 = 0;
- } elsif ($a[1] eq "on") {
- $ctrlParam3 = 1;
- } elsif ($a[1] eq "off") {
- $ctrlParam3 = 2;
- } elsif ($a[1] eq "local") {
- $ctrlParam3 = 3;
- } else {
- return "Usage: $cmd variable is unknown.";
- }
- readingsSingleUpdate($hash, "block", $a[1], 1);
- shift(@a);
- $updateState = 0;
- } elsif ($ctrlFuncID == 12) {
- # meteringValues
- if (@a > 2) {
- my %unitEnum = (
- "mW" => 0,
- "W" => 1,
- "kW" => 2,
- "MW" => 3,
- "Wh" => 4,
- "kWh" => 5,
- "MWh" => 6,
- "GWh" => 7,
- "mA" => 8,
- "A" => 9,
- "mV" => 10,
- "V" => 11,
- );
- if (exists $unitEnum{$a[2]}) {
- $ctrlParam3 = $unitEnum{$a[2]};
- } else {
- return "Unknown metering value choose one of " . join(" ", sort keys %unitEnum);
- }
- if ($ctrlParam3 == 9 || $ctrlParam3 == 11) {
- if ($a[1] =~ m/^\d+(\.\d+)?$/ && $a[1] >= 0 && $a[1] <= 6553.5) {
- $ctrlParam2 = int($a[1] * 10) & 0xFF;
- $ctrlParam1 = (int($a[1] * 10) & 0xFF00) >> 8;
- } else {
- return "Usage: $cmd value is not numeric or out of range.";
- }
- } else {
- if ($a[1] =~ m/^\d+$/ && $a[1] >= 0 && $a[1] <= 65535) {
- $ctrlParam2 = $a[1] & 0xFF;
- $ctrlParam1 = ($a[1] & 0xFF00) >> 8;
- } else {
- return "Usage: $cmd value is not numeric or out of range.";
- }
- }
- splice(@a, 0, 2);
- } else {
- return "Usage: $cmd values are missing";
- }
- $updateState = 0;
- }
- $setCmd |= 4 if (AttrVal($name, "sendDevStatus", "yes") eq "no");
- $setCmd |= 1 if (AttrVal($name, "serviceOn", "no") eq "yes");
- $data = sprintf "%02X%02X%02X%02X", $ctrlParam1, $ctrlParam2, $ctrlParam3, $setCmd;
- Log3 $name, 3, "EnOcean set $name $cmd";
- } elsif ($st eq "radioLinkTest") {
- # Radio Link Test (A5-3F-00)
- $rorg = "A5";
- $updateState = 3;
- if($cmd =~ m/^standby|stop$/) {
- @{$hash->{helper}{rlt}{param}} = ($cmd, $hash, undef, $subDef, 'master', 0);
- EnOcean_RLT($hash->{helper}{rlt}{param});
- } else {
- return "Unknown argument " . $cmd . ", choose one of " . $cmdList . "standby:noArg stop:noArg"
- }
- Log3 $name, 3, "EnOcean set $name $cmd";
- } elsif ($st eq "manufProfile") {
- if ($manufID eq "00D") {
- # Eltako Shutter
- my $angleMax = AttrVal($name, "angleMax", 90);
- my $angleMin = AttrVal($name, "angleMin", -90);
- my $anglePos = ReadingsVal($name, "anglePos", undef);
- my $anglePosStart;
- my $angleTime = AttrVal($name, "angleTime", 0);
- my $position = ReadingsVal($name, "position", undef);
- my $positionStart;
- my $setCmd = 8;
- my $settingAccuracy = 1;
- if (AttrVal($name, 'settingAccuracy', 'low') eq 'high') {
- $setCmd = 0x0A;
- $settingAccuracy = 10;
- }
- if ($cmd eq "?" || $cmd eq "stop") {
- } else {
- # check actual shutter position
- my $actualState = ReadingsVal($name, "state", undef);
- if (defined $actualState) {
- if ($actualState eq "open") {
- $position = 0;
- $anglePos = 0;
- } elsif ($actualState eq "closed") {
- $position = 100;
- $anglePos = $angleMax;
- }
- }
- $anglePosStart = $anglePos;
- $positionStart = $position;
- readingsSingleUpdate($hash, ".anglePosStart", $anglePosStart, 0);
- readingsSingleUpdate($hash, ".positionStart", $positionStart, 0);
- }
- $rorg = "A5";
- my $shutTime = AttrVal($name, "shutTime", 255);
- my $shutTimeCloses = AttrVal($name, "shutTimeCloses", $shutTime);
- $shutTimeCloses = $shutTime if ($shutTimeCloses < $shutTime);
- my $shutCmd = 0;
- $angleMax = 90 if ($angleMax !~ m/^[+-]?\d+$/);
- $angleMax = 180 if ($angleMax > 180);
- $angleMax = -180 if ($angleMax < -180);
- $angleMin = -90 if ($angleMin !~ m/^[+-]?\d+$/);
- $angleMin = 180 if ($angleMin > 180);
- $angleMin = -180 if ($angleMin < -180);
- ($angleMax, $angleMin) = ($angleMin, $angleMax) if ($angleMin > $angleMax);
- $angleMax ++ if ($angleMin == $angleMax);
- $angleTime = 6 if ($angleTime !~ m/^[+-]?\d+$/);
- $angleTime = 6 if ($angleTime > 6);
- $angleTime = 0 if ($angleTime < 0);
- $shutTime = 255 if ($shutTime !~ m/^[+-]?\d+$/);
- $shutTime = 255 if ($shutTime > 255);
- $shutTime = 1 if ($shutTime < 1);
- if ($cmd =~ m/^\d+$/) {
- # interpretive numeric value as position
- unshift(@a, 'position');
- $cmd = 'position';
- }
- if ($cmd eq "teach") {
- # teach-in EEP A5-3F-7F, Manufacturer "Eltako"
- $data = "FFF80D80";
- $attr{$name}{eep} = "A5-3F-7F";
- CommandDeleteReading(undef, "$name .*");
- readingsSingleUpdate($hash, "teach", "4BS teach-in sent", 1);
- $updateState = 0;
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDef", "confirm");
- EnOcean_setTeachConfirmWaitHash(undef, $hash);
- } elsif ($cmd eq "stop") {
- # stop
- # delete readings, as they are undefined
- CommandDeleteReading(undef, "$name anglePos");
- CommandDeleteReading(undef, "$name position");
- readingsSingleUpdate($hash, "endPosition", "not_reached", 1);
- readingsSingleUpdate($hash, "state", "stop", 1);
- $shutCmd = 0;
- } elsif ($cmd eq "opens") {
- # opens >> B0
- $anglePos = 0;
- $position = 0;
- readingsSingleUpdate($hash, "anglePos", $anglePos, 1);
- readingsSingleUpdate($hash, "position", $position, 1);
- readingsSingleUpdate($hash, "endPosition", "open", 1);
- $cmd = "open";
- $shutTime = $shutTimeCloses;
- $shutCmd = 1;
- #$updateState = 0;
- } elsif ($cmd eq "closes") {
- # closes >> BI
- $anglePos = $angleMax;
- $position = 100;
- readingsSingleUpdate($hash, "anglePos", $anglePos, 1);
- readingsSingleUpdate($hash, "position", $position, 1);
- readingsSingleUpdate($hash, "endPosition", "closed", 1);
- $cmd = "closed";
- $shutTime = $shutTimeCloses;
- $shutCmd = 2;
- #$updateState = 0;
- } elsif ($cmd eq "up") {
- # up
- if (defined $a[1]) {
- if ($a[1] =~ m/^[+-]?\d+$/ && $a[1] >= 0 && $a[1] <= 255) {
- $position = $positionStart - $a[1] / $shutTime * 100;
- if ($angleTime) {
- $anglePos = $anglePosStart - ($angleMax - $angleMin) * $a[1] / $angleTime;
- if ($anglePos < $angleMin) {
- $anglePos = $angleMin;
- }
- } else {
- $anglePos = $angleMin;
- }
- if ($position <= 0) {
- $anglePos = 0;
- $position = 0;
- readingsSingleUpdate($hash, "endPosition", "open", 1);
- $cmd = "open";
- } else {
- readingsSingleUpdate($hash, "endPosition", "not_reached", 1);
- $cmd = "not_reached";
- }
- $shutTime = $a[1];
- shift(@a);
- } else {
- return "Usage: $a[1] is not numeric or out of range";
- }
- } else {
- $anglePos = 0;
- $position = 0;
- readingsSingleUpdate($hash, "endPosition", "open", 1);
- $cmd = "open";
- }
- readingsSingleUpdate($hash, "anglePos", sprintf("%d", $anglePos), 1);
- readingsSingleUpdate($hash, "position", sprintf("%d", $position), 1);
- $shutCmd = 1;
- } elsif ($cmd eq "down") {
- # down
- if (defined $a[1]) {
- if ($a[1] =~ m/^[+-]?\d+$/ && $a[1] >= 0 && $a[1] <= 255) {
- $position = $positionStart + $a[1] / $shutTime * 100;
- if ($angleTime) {
- $anglePos = $anglePosStart + ($angleMax - $angleMin) * $a[1] / $angleTime;
- if ($anglePos > $angleMax) {
- $anglePos = $angleMax;
- }
- } else {
- $anglePos = $angleMax;
- }
- if($position >= 100) {
- $anglePos = $angleMax;
- $position = 100;
- readingsSingleUpdate($hash, "endPosition", "closed", 1);
- $cmd = "closed";
- } else {
- readingsSingleUpdate($hash, "endPosition", "not_reached", 1);
- $cmd = "not_reached";
- }
- $shutTime = $a[1];
- shift(@a);
- } else {
- return "Usage: $a[1] is not numeric or out of range";
- }
- } else {
- $anglePos = $angleMax;
- $position = 100;
- readingsSingleUpdate($hash, "endPosition", "closed", 1);
- $cmd = "closed";
- }
- readingsSingleUpdate($hash, "anglePos", sprintf("%d", $anglePos), 1);
- readingsSingleUpdate($hash, "position", sprintf("%d", $position), 1);
- $shutCmd = 2;
- } elsif ($cmd eq "position") {
- if (!defined $positionStart) {
- return "Position unknown, please first opens the blinds completely."
- } elsif ($angleTime > 0 && !defined $anglePosStart){
- return "Slats angle position unknown, please first opens the blinds completely."
- } else {
- my $shutTimeSet = $shutTime;
- if (defined $a[2]) {
- if ($a[2] =~ m/^[+-]?\d+$/ && $a[2] >= $angleMin && $a[2] <= $angleMax) {
- $anglePos = $a[2];
- } else {
- return "Usage: $a[1] $a[2] is not numeric or out of range";
- }
- splice(@a,2,1);
- } else {
- $anglePos = $angleMax;
- }
- if ($positionStart <= $angleTime * $angleMax / ($angleMax - $angleMin) / $shutTimeSet * 100) {
- $anglePosStart = $angleMax;
- }
- if (defined $a[1] && $a[1] =~ m/^[+-]?\d+$/ && $a[1] >= 0 && $a[1] <= 100) {
- if ($positionStart < $a[1]) {
- # down
- $angleTime = $angleTime * ($angleMax - $anglePos) / ($angleMax - $angleMin);
- $shutTime = $shutTime * ($a[1] - $positionStart) / 100 + $angleTime;
- # round up
- $angleTime = int($angleTime) + 1 if ($settingAccuracy == 1 && $angleTime > int($angleTime));
- $shutTime = int($shutTime) + 1 if ($settingAccuracy == 1 && $shutTime > int($shutTime));
- $position = $a[1] + $angleTime / $shutTimeSet * 100;
- if ($position >= 100) {
- $position = 100;
- }
- $shutCmd = 2;
- if ($angleTime) {
- my @timerCmd = ($name, "up", $angleTime);
- my %par = (hash => $hash, timerCmd => \@timerCmd);
- InternalTimer(gettimeofday() + $shutTime + 1, "EnOcean_TimerSet", \%par, 0);
- }
- } elsif ($positionStart > $a[1]) {
- # up
- $angleTime = $angleTime * ($anglePos - $angleMin) /($angleMax - $angleMin);
- $shutTime = $shutTime * ($positionStart - $a[1]) / 100 + $angleTime;
- # round up
- $angleTime = int($angleTime) + 1 if ($settingAccuracy == 1 && $angleTime > int($angleTime));
- $shutTime = int($shutTime) + 1 if ($settingAccuracy == 1 && $shutTime > int($shutTime));
- $position = $a[1] - $angleTime / $shutTimeSet * 100;
- if ($position <= 0) {
- $position = 0;
- $anglePos = 0;
- }
- $shutCmd = 1;
- if ($angleTime && $a[1] > 0) {
- my @timerCmd = ($name, "down", $angleTime);
- my %par = (hash => $hash, timerCmd => \@timerCmd);
- InternalTimer(gettimeofday() + $shutTime + 1, "EnOcean_TimerSet", \%par, 0);
- }
- } else {
- if ($anglePosStart > $anglePos) {
- # up >> reduce slats angle
- $shutTime = $angleTime * ($anglePosStart - $anglePos)/($angleMax - $angleMin);
- # round up
- $shutTime = int($shutTime) + 1 if ($settingAccuracy == 1 && $shutTime > int($shutTime));
- $shutCmd = 1;
- } elsif ($anglePosStart < $anglePos) {
- # down >> enlarge slats angle
- $shutTime = $angleTime * ($anglePos - $anglePosStart) /($angleMax - $angleMin);
- # round up
- $shutTime = int($shutTime) + 1 if ($settingAccuracy == 1 && $shutTime > int($shutTime));
- $shutCmd = 2;
- } else {
- # position and slats angle ok
- $data = '00000008';
- $shutCmd = 0;
- $updateState = 3;
- }
- }
- if ($position == 0) {
- readingsSingleUpdate($hash, "endPosition", "open", 1);
- $cmd = "open";
- } elsif ($position == 100) {
- readingsSingleUpdate($hash, "endPosition", "closed", 1);
- $cmd = "closed";
- } else {
- readingsSingleUpdate($hash, "endPosition", "not_reached", 1);
- $cmd = "not_reached";
- }
- readingsSingleUpdate($hash, "anglePos", sprintf("%d", $anglePos), 1);
- readingsSingleUpdate($hash, "position", sprintf("%d", $position), 1);
- shift(@a);
- } else {
- return "Usage: $a[1] is not numeric or out of range";
- }
- }
- } elsif ($cmd eq "anglePos") {
- if (!defined $positionStart) {
- return "Position unknown, please first opens the blinds completely."
- } elsif ($angleTime > 0 && !defined $anglePosStart){
- return "Slats angle position unknown, please first opens the blinds completely."
- } else {
- if (defined $a[1]) {
- if ($a[1] =~ m/^[+-]?\d+$/ && $a[1] >= $angleMin && $a[1] <= $angleMax) {
- $anglePos = $a[1];
- shift(@a);
- if ($anglePosStart > $anglePos) {
- # up >> reduce slats angle
- $shutTime = $angleTime * ($anglePosStart - $anglePos)/($angleMax - $angleMin);
- # round up
- $shutTime = int($shutTime) + 1 if ($settingAccuracy == 1 && $shutTime > int($shutTime));
- $shutCmd = 1;
- } elsif ($anglePosStart < $anglePos) {
- # down >> enlarge slats angle
- $shutTime = $angleTime * ($anglePos - $anglePosStart) /($angleMax - $angleMin);
- # round up
- $shutTime = int($shutTime) + 1 if ($settingAccuracy == 1 && $shutTime > int($shutTime));
- $shutCmd = 2;
- } else {
- # slats angle ok
- $data = '00000008';
- $shutCmd = 0;
- $updateState = 3;
- }
- readingsSingleUpdate($hash, "anglePos", sprintf("%d", $anglePos), 1);
- } else {
- return "Usage: $a[1] is not numeric or out of range";
- }
- } else {
- return "Usage: $cmd values are missing";
- }
- }
- } elsif ($cmd eq "local") {
- if ($a[1]) {
- return "Usage: $cmd [learn]" if ($a[1] ne "learn");
- if ($a[1] eq "learn") {
- $setCmd = $setCmd | 0x20;
- }
- shift(@a);
- }
- $updateState = 0;
- $data = sprintf "%04X%02X%02X", int($shutTime * $settingAccuracy), $shutCmd, $setCmd;
- } else {
- return "Unknown argument " . $cmd . ", choose one of " . $cmdList . "position:slider,0,5,100 anglePos:slider,-180,5,180 closes:noArg down local:learn opens:noArg stop:noArg teach:noArg up"
- }
- if ($shutCmd || $cmd eq "stop") {
- #$updateState = 0;
- $data = sprintf "%04X%02X%02X", int($shutTime * $settingAccuracy), $shutCmd, $setCmd;
- }
- Log3 $name, 3, "EnOcean set $name $cmd";
- }
- } elsif ($st eq "actuator.01") {
- # Electronic switches and dimmers with Energy Measurement and Local Control
- # (D2-01-00 - D2-01-12)
- $rorg = "D2";
- #$updateState = 0;
- my $cmdID;
- my $channel;
- my $dimValTimer = 0;
- my $outputVal;
- if ($cmd =~ m/^\d+$/) {
- # interpretive numeric value as dimming
- unshift(@a, 'dim');
- $cmd = 'dim';
- }
- if ($cmd eq "on") {
- shift(@a);
- $cmdID = 1;
- my $dimValueOn = AttrVal($name, "dimValueOn", 100);
- if ($dimValueOn eq "stored") {
- $outputVal = ReadingsVal($name, "dimValueStored", 100);
- if ($outputVal < 1) {
- $outputVal = 100;
- readingsSingleUpdate ($hash, "dimValueStored", $outputVal, 1);
- }
- } elsif ($dimValueOn eq "last") {
- $outputVal = ReadingsVal ($name, "dimValueLast", 100);
- if ($outputVal < 1) { $outputVal = 100; }
- } else {
- if ($dimValueOn !~ m/^[+-]?\d+$/) {
- $outputVal = 100;
- } elsif ($dimValueOn > 100) {
- $outputVal = 100;
- } elsif ($dimValueOn < 1) {
- $outputVal = 1;
- } else {
- $outputVal = $dimValueOn;
- }
- }
- $channel = shift(@a);
- $channel = AttrVal($name, "defaultChannel", AttrVal($name, "devChannel", undef)) if (!defined $channel);
- if (!defined $channel || $channel eq "all") {
- CommandDeleteReading(undef, "$name channel.*");
- CommandDeleteReading(undef, "$name dim.*");
- readingsSingleUpdate($hash, "channelAll", "on", 1);
- readingsSingleUpdate($hash, "dim", $outputVal, 1);
- $channel = 30;
- } elsif ($channel eq "input" || $channel + 0 == 31) {
- readingsSingleUpdate($hash, "channelInput", "on", 1);
- readingsSingleUpdate($hash, "dimInput", $outputVal, 1);
- $channel = 31;
- } elsif ($channel + 0 >= 30) {
- CommandDeleteReading(undef, "$name channel.*");
- CommandDeleteReading(undef, "$name dim.*");
- readingsSingleUpdate($hash, "channelAll", "on", 1);
- readingsSingleUpdate($hash, "dim", $outputVal, 1);
- $channel = 30;
- } elsif ($channel + 0 >= 0 && $channel + 0 <= 29) {
- readingsSingleUpdate($hash, "channel" . $channel, "on", 1);
- readingsSingleUpdate($hash, "dim" . $channel, $outputVal, 1);
- } else {
- return "$cmd $channel wrong, choose 0...29|all|input.";
- }
- #readingsSingleUpdate($hash, "state", "on", 1);
- $data = sprintf "%02X%02X%02X", $cmdID, $dimValTimer << 5 | $channel, $outputVal;
- } elsif ($cmd eq "off") {
- shift(@a);
- $cmdID = 1;
- $outputVal = 0;
- $channel = shift(@a);
- $channel = AttrVal($name, "defaultChannel", AttrVal($name, "devChannel", undef)) if (!defined $channel);
- if (!defined $channel || $channel eq "all") {
- CommandDeleteReading(undef, "$name channel.*");
- CommandDeleteReading(undef, "$name dim.*");
- readingsSingleUpdate($hash, "channelAll", "off", 1);
- readingsSingleUpdate($hash, "dim", $outputVal, 1);
- $channel = 30;
- } elsif ($channel eq "input" || $channel + 0 == 31) {
- readingsSingleUpdate($hash, "channelInput", "off", 1);
- readingsSingleUpdate($hash, "dimInput", $outputVal, 1);
- $channel = 31;
- } elsif ($channel + 0 >= 30) {
- CommandDeleteReading(undef, "$name channel.*");
- CommandDeleteReading(undef, "$name dim.*");
- readingsSingleUpdate($hash, "channelAll", "off", 1);
- readingsSingleUpdate($hash, "dim", $outputVal, 1);
- $channel = 30;
- } elsif ($channel >= 0 && $channel <= 29) {
- readingsSingleUpdate($hash, "channel" . $channel, "off", 1);
- readingsSingleUpdate($hash, "dim" . $channel, $outputVal, 1);
- } else {
- return "$cmd $channel wrong, choose 0...39|all|input.";
- }
- #readingsSingleUpdate($hash, "state", "off", 1);
- $data = sprintf "%02X%02X%02X", $cmdID, $dimValTimer << 5 | $channel, $outputVal;
- } elsif ($cmd eq "dim") {
- shift(@a);
- $cmdID = 1;
- $outputVal = shift(@a);
- if (!defined $outputVal || $outputVal !~ m/^[+-]?\d+$/ || $outputVal < 0 || $outputVal > 100) {
- return "Usage: $cmd variable is not numeric or out of range.";
- }
- $channel = shift(@a);
- $channel = AttrVal($name, "defaultChannel", AttrVal($name, "devChannel", undef)) if (!defined $channel);
- if (!defined $channel) {
- CommandDeleteReading(undef, "$name channel.*");
- CommandDeleteReading(undef, "$name dim.*");
- if ($outputVal == 0) {
- readingsSingleUpdate($hash, "channelAll", "off", 1);
- } else {
- readingsSingleUpdate($hash, "channelAll", "on", 1);
- }
- readingsSingleUpdate($hash, "dim", $outputVal, 1);
- $channel = 30;
- } else {
- if ($channel eq "all") {
- CommandDeleteReading(undef, "$name channel.*");
- CommandDeleteReading(undef, "$name dim.*");
- if ($outputVal == 0) {
- readingsSingleUpdate($hash, "channelAll", "off", 1);
- } else {
- readingsSingleUpdate($hash, "channelAll", "on", 1);
- }
- readingsSingleUpdate($hash, "dim", $outputVal, 1);
- $channel = 30;
- } elsif ($channel eq "input" || $channel + 0 == 31) {
- if ($outputVal == 0) {
- readingsSingleUpdate($hash, "channelInput", "off", 1);
- } else {
- readingsSingleUpdate($hash, "channelInput", "on", 1);
- }
- readingsSingleUpdate($hash, "dimInput", $outputVal, 1);
- $channel = 31;
- } elsif ($channel + 0 >= 30) {
- CommandDeleteReading(undef, "$name channel.*");
- CommandDeleteReading(undef, "$name dim.*");
- if ($outputVal == 0) {
- readingsSingleUpdate($hash, "channelAll", "off", 1);
- } else {
- readingsSingleUpdate($hash, "channelAll", "on", 1);
- }
- readingsSingleUpdate($hash, "dim", $outputVal, 1);
- $channel = 30;
- } elsif ($channel >= 0 && $channel <= 29) {
- if ($outputVal == 0) {
- readingsSingleUpdate($hash, "channel" . $channel, "off", 1);
- } else {
- readingsSingleUpdate($hash, "channel" . $channel, "on", 1);
- }
- readingsSingleUpdate($hash, "dim" . $channel, $outputVal, 1);
- } else {
- return "Usage: $cmd $channel wrong, choose 0...39|all|input.";
- }
- $dimValTimer = shift(@a);
- if (defined $dimValTimer) {
- if ($dimValTimer eq "switch") {
- $dimValTimer = 0;
- } elsif ($dimValTimer eq "stop") {
- $dimValTimer = 4;
- } elsif ($dimValTimer =~ m/^[1-3]$/) {
- } else {
- return "Usage: $cmd <channel> $dimValTimer wrong, choose 1..3|switch|stop.";
- }
- } else {
- $dimValTimer = 0;
- }
- }
- if ($outputVal == 0) {
- $cmd = "off";
- #readingsSingleUpdate($hash, "state", "off", 1);
- } else {
- $cmd = "on";
- #readingsSingleUpdate($hash, "state", "on", 1);
- }
- $data = sprintf "%02X%02X%02X", $cmdID, $dimValTimer << 5 | $channel, $outputVal;
- } elsif ($cmd eq "local") {
- shift(@a);
- $updateState = 0;
- $cmdID = 2;
- # same configuration for all channels
- $channel = 30;
- my $dayNight = ReadingsVal($name, "dayNight", "day");
- my $dayNightCmd = ($dayNight eq "night")? 1:0;
- my $defaultState = ReadingsVal($name, "defaultState", "off");
- my $defaultStateCmd;
- if ($defaultState eq "off") {
- $defaultStateCmd = 0;
- } elsif ($defaultState eq "on") {
- $defaultStateCmd = 1;
- } elsif ($defaultState eq "last") {
- $defaultStateCmd = 2;
- } else {
- $defaultStateCmd = 0;
- }
- my $localControl = ReadingsVal($name, "localControl", "disabled");
- my $localControlCmd = ($localControl eq "enabled")? 1:0;
- my $overCurrentShutdown = ReadingsVal($name, "overCurrentShutdown", "off");
- my $overCurrentShutdownCmd = ($overCurrentShutdown eq "restart")? 1:0;
- my $overCurrentShutdownReset = "not_active";
- my $overCurrentShutdownResetCmd = 0;
- my $rampTime1 = ReadingsVal($name, "rampTime1", 0);
- my $rampTime1Cmd = $rampTime1 * 2;
- if ($rampTime1Cmd <= 0) {
- $rampTime1Cmd = 0;
- } elsif ($rampTime1Cmd >= 15) {
- $rampTime1Cmd = 15;
- }
- my $rampTime2 = ReadingsVal($name, "rampTime2", 0);
- my $rampTime2Cmd = $rampTime2 * 2;
- if ($rampTime2Cmd <= 0) {
- $rampTime2Cmd = 0;
- } elsif ($rampTime2Cmd >= 15) {
- $rampTime2Cmd = 15;
- }
- my $rampTime3 = ReadingsVal($name, "rampTime3", 0);
- my $rampTime3Cmd = $rampTime3 * 2;
- if ($rampTime3Cmd <= 0) {
- $rampTime3Cmd = 0;
- } elsif ($rampTime3Cmd >= 15) {
- $rampTime3Cmd = 15;
- }
- my $teachInDev = ReadingsVal($name, "teachInDev", "disabled");
- my $teachInDevCmd = ($teachInDev eq "enabled")? 1:0;
- my $localCmd = shift(@a);
- my $localCmdVal = shift(@a);
- if ($localCmd eq "dayNight") {
- if ($localCmdVal eq "day") {
- $dayNight = "day";
- $dayNightCmd = 0;
- } elsif ($localCmdVal eq "night") {
- $dayNight = "night";
- $dayNightCmd = 1;
- } else {
- return "Usage: $cmd $localCmd <value> wrong, choose day night.";
- }
- } elsif ($localCmd eq "defaultState"){
- if ($localCmdVal eq "off") {
- $defaultState = "off";
- $defaultStateCmd = 0;
- } elsif ($localCmdVal eq "on") {
- $defaultState = "on";
- $defaultStateCmd = 1;
- } elsif ($localCmdVal eq "last") {
- $defaultState = "last";
- $defaultStateCmd = 2;
- } else {
- return "Usage: $cmd $localCmd <value> wrong, choose on off last.";
- }
- } elsif ($localCmd eq "localControl"){
- if ($localCmdVal eq "disabled") {
- $localControl = "disabled";
- $localControlCmd = 0;
- } elsif ($localCmdVal eq "enabled") {
- $localControl = "enabled";
- $localControlCmd = 1;
- } else {
- return "Usage: $cmd $localCmd <value> wrong, choose disabled enabled.";
- }
- } elsif ($localCmd eq "overCurrentShutdown"){
- if ($localCmdVal eq "off") {
- $overCurrentShutdown = "off";
- $overCurrentShutdownCmd = 0;
- } elsif ($localCmdVal eq "restart") {
- $overCurrentShutdown = "restart";
- $overCurrentShutdownCmd = 1;
- } else {
- return "Usage: $cmd $localCmd <value> wrong, choose off restart.";
- }
- } elsif ($localCmd eq "overCurrentShutdownReset"){
- if ($localCmdVal eq "not_active") {
- $overCurrentShutdownReset = "not_active";
- $overCurrentShutdownResetCmd = 0;
- } elsif ($localCmdVal eq "trigger") {
- $overCurrentShutdownReset = "trigger";
- $overCurrentShutdownResetCmd = 1;
- } else {
- return "Usage: $cmd $localCmd <value> wrong, choose not_active trigger.";
- }
- } elsif ($localCmd eq "rampTime1"){
- if ($localCmdVal >= 0 || $localCmdVal <= 7.5) {
- $rampTime1 = $localCmdVal;
- $rampTime1Cmd = $localCmdVal * 2;
- } else {
- return "Usage: $cmd $localCmd <value> wrong, choose 0, 0.5, ..., 7, 7.5";
- }
- } elsif ($localCmd eq "rampTime2"){
- if ($localCmdVal >= 0 || $localCmdVal <= 7.5) {
- $rampTime2 = $localCmdVal;
- $rampTime2Cmd = $localCmdVal * 2;
- } else {
- return "Usage: $cmd $localCmd <value> wrong, choose 0, 0.5, ..., 7, 7.5";
- }
- } elsif ($localCmd eq "rampTime3"){
- if ($localCmdVal >= 0 || $localCmdVal <= 7.5) {
- $rampTime3 = $localCmdVal;
- $rampTime3Cmd = $localCmdVal * 2;
- } else {
- return "Usage: $cmd $localCmd <value> wrong, choose 0, 0.5, ..., 7, 7.5";
- }
- } elsif ($localCmd eq "teachInDev"){
- if ($localCmdVal eq "disabled") {
- $teachInDev = "disabled";
- $teachInDevCmd = 0;
- } elsif ($localCmdVal eq "enabled") {
- $teachInDev = "enabled";
- $teachInDevCmd = 1;
- } else {
- return "Usage: $cmd $localCmd <value> wrong, choose disabled enabled.";
- }
- } else {
- return "Usage: $cmd <localCmd> wrong, choose dayNight|defaultState|localControl|" .
- "overCurrentShutdown|overCurrentShutdownReset|rampTime1|rampTime2|rampTime3|teachInDev.";
- }
- readingsSingleUpdate($hash, "dayNight", $dayNight, 1);
- readingsSingleUpdate($hash, "defaultState", $defaultState, 1);
- readingsSingleUpdate($hash, "localControl", $localControl, 1);
- readingsSingleUpdate($hash, "overCurrentShutdown", $overCurrentShutdown, 1);
- readingsSingleUpdate($hash, "overCurrentShutdownReset", $overCurrentShutdownReset, 1);
- readingsSingleUpdate($hash, "rampTime1", $rampTime1, 1);
- readingsSingleUpdate($hash, "rampTime2", $rampTime2, 1);
- readingsSingleUpdate($hash, "rampTime3", $rampTime3, 1);
- readingsSingleUpdate($hash, "teachInDev", $teachInDev, 1);
- $data = sprintf "%02X%02X%02X%02X", $teachInDevCmd << 7 | $cmdID,
- $overCurrentShutdownCmd << 7 | $overCurrentShutdownResetCmd << 6 | $localControlCmd << 5 | $channel,
- int($rampTime2Cmd) << 4 | int($rampTime3Cmd),
- $dayNightCmd << 7 | $defaultStateCmd << 4 | int($rampTime1Cmd);
- } elsif ($cmd eq "measurement") {
- shift(@a);
- $updateState = 0;
- $cmdID = 5;
- # same configuration for all channels
- $channel = 30;
- my $measurementMode = ReadingsVal($name, "measurementMode", "energy");
- my $measurementModeCmd = ($measurementMode eq "power")? 0:1;
- my $measurementReport = ReadingsVal($name, "measurementReport", "query");
- my $measurementReportCmd = ($measurementReport eq "auto")? 0:1;
- my $measurementReset = "not_active";
- my $measurementResetCmd = 0;
- my $measurementDelta = int(ReadingsVal($name, "measurementDelta", 0));
- if ($measurementDelta <= 0) {
- $measurementDelta = 0;
- } elsif ($measurementDelta >= 4095) {
- $measurementDelta = 4095;
- }
- my $unit = ReadingsVal($name, "measurementUnit", "Ws");
- my $unitCmd;
- if ($unit eq "Ws") {
- $unitCmd = 0;
- } elsif ($unit eq "Wh") {
- $unitCmd = 1;
- } elsif ($unit eq "KWh") {
- $unitCmd = 2;
- } elsif ($unit eq "W") {
- $unitCmd = 3;
- } elsif ($unit eq "KW") {
- $unitCmd = 4;
- } else {
- $unitCmd = 0;
- }
- my $responseTimeMax = ReadingsVal($name, "responseTimeMax", 10);
- my $responseTimeMaxCmd = $responseTimeMax / 10;
- if ($responseTimeMaxCmd <= 1) {
- $responseTimeMaxCmd = 1;
- } elsif ($responseTimeMaxCmd >= 255) {
- $responseTimeMaxCmd = 255;
- }
- my $responseTimeMin = ReadingsVal($name, "responseTimeMin", 1);
- if ($responseTimeMin <= 1) {
- $responseTimeMin = 1;
- } elsif ($responseTimeMin >= 255) {
- $responseTimeMin = 255;
- }
- my $measurementCmd = shift(@a);
- my $measurementCmdVal = shift(@a);
- if (!defined $measurementCmdVal) {
- return "Usage: $cmd $measurementCmd <value> needed.";
- }
- if (!defined $measurementCmd) {
- return "Usage: $cmd <measurementCmd> wrong, choose mode|report|" .
- "reset|delta|unit|responseTimeMax|responseTimeMin.";
- } elsif ($measurementCmd eq "mode") {
- if ($measurementCmdVal eq "energy") {
- $measurementMode = "energy";
- $measurementModeCmd = 0;
- } elsif ($measurementCmdVal eq "power") {
- $measurementMode = "power";
- $measurementModeCmd = 1;
- } else {
- return "Usage: $cmd $measurementCmd <value> wrong, choose energy power.";
- }
- } elsif ($measurementCmd eq "report"){
- if ($measurementCmdVal eq "query") {
- $measurementReport = "query";
- $measurementReportCmd = 0;
- } elsif ($measurementCmdVal eq "auto") {
- $measurementReport = "auto";
- $measurementReportCmd = 1;
- } else {
- return "Usage: $cmd $measurementCmd <value> wrong, choose query auto.";
- }
- } elsif ($measurementCmd eq "reset"){
- if ($measurementCmdVal eq "not_active") {
- $measurementReset = "not_active";
- $measurementResetCmd = 0;
- } elsif ($measurementCmdVal eq "trigger") {
- $measurementReset = "trigger";
- $measurementResetCmd = 1;
- } else {
- return "Usage: $cmd $measurementCmd <value> wrong, choose not_active trigger.";
- }
- } elsif ($measurementCmd eq "unit"){
- if ($measurementCmdVal eq "Ws") {
- $unit = "Ws";
- $unitCmd = 0;
- } elsif ($measurementCmdVal eq "Wh") {
- $unit = "Wh";
- $unitCmd = 1;
- } elsif ($measurementCmdVal eq "KWh") {
- $unit = "KWh";
- $unitCmd = 2;
- } elsif ($measurementCmdVal eq "W") {
- $unit = "W";
- $unitCmd = 3;
- } elsif ($measurementCmdVal eq "KW") {
- $unit = "KW";
- $unitCmd = 4;
- } else {
- return "Usage: $cmd $measurementCmd <value> wrong, choose Ws Wh KWh W KW.";
- }
- } elsif ($measurementCmd eq "delta"){
- if ($measurementCmdVal >= 0 || $measurementCmdVal <= 4095) {
- $measurementDelta = int($measurementCmdVal);
- } else {
- return "Usage: $cmd $measurementCmd <value> wrong, choose 0 ... 4095";
- }
- } elsif ($measurementCmd eq "responseTimeMax"){
- if ($measurementCmdVal >= 10 || $measurementCmdVal <= 2550) {
- $responseTimeMax = int($measurementCmdVal);
- $responseTimeMaxCmd = int($measurementCmdVal) / 10;
- } else {
- return "Usage: $cmd $measurementCmd <value> wrong, choose 10 ... 2550";
- }
- } elsif ($measurementCmd eq "responseTimeMin"){
- if ($measurementCmdVal >= 1 || $measurementCmdVal <= 255) {
- $responseTimeMin = int($measurementCmdVal);
- } else {
- return "Usage: $cmd $measurementCmd <value> wrong, choose 1 ... 255";
- }
- } else {
- return "Usage: $cmd <measurementCmd> wrong, choose mode|report|" .
- "reset|delta|unit|responseTimeMax|responseTimeMin.";
- }
- readingsSingleUpdate($hash, "measurementMode", $measurementMode, 1);
- readingsSingleUpdate($hash, "measurementReport", $measurementReport, 1);
- readingsSingleUpdate($hash, "measurementReset", $measurementReset, 1);
- readingsSingleUpdate($hash, "measurementDelta", $measurementDelta, 1);
- readingsSingleUpdate($hash, "measurementUnit", $unit, 1);
- readingsSingleUpdate($hash, "responseTimeMax", $responseTimeMax, 1);
- readingsSingleUpdate($hash, "responseTimeMin", $responseTimeMin, 1);
- $data = sprintf "%02X%02X%02X%02X%02X%02X", $cmdID,
- $measurementReportCmd << 7 | $measurementResetCmd << 6 | $measurementModeCmd << 5 | $channel,
- ($measurementDelta | 0x0F) << 4 | $unitCmd, ($measurementDelta | 0xFF00) >> 8,
- $responseTimeMax, $responseTimeMin;
- } elsif ($cmd eq "roomCtrlMode") {
- shift(@a);
- $updateState = 0;
- $cmdID = 8;
- my $roomCtrlModeCmd = shift(@a);
- return "$cmd <channel> <roomCtrlMode> is missing, choose off|comfort|comfort-1|comfort-2|economy|buildingProtection." if (!defined $roomCtrlModeCmd);
- if ($roomCtrlModeCmd eq "off") {
- $roomCtrlModeCmd = 0;
- } elsif ($roomCtrlModeCmd eq "comfort") {
- $roomCtrlModeCmd = 1;
- } elsif ($roomCtrlModeCmd eq "economy") {
- $roomCtrlModeCmd = 2;
- } elsif ($roomCtrlModeCmd eq "buildingProtection") {
- $roomCtrlModeCmd = 3;
- } elsif ($roomCtrlModeCmd eq "comfort-1") {
- $roomCtrlModeCmd = 4;
- } elsif ($roomCtrlModeCmd eq "comfort-2") {
- $roomCtrlModeCmd = 5;
- } else {
- return "$cmd <channel> <roomCtrlMode> wrong, choose off|comfort|comfort-1|comfort-2|economy|buildingProtection.";
- }
- $data = sprintf "%02X%02X", $cmdID, $roomCtrlModeCmd;
- } elsif ($cmd eq "autoOffTime") {
- shift(@a);
- $updateState = 0;
- $cmdID = 0x0B;
- $outputVal = int(shift(@a) * 10);
- if (!defined $outputVal || $outputVal !~ m/^[+-]?\d+$/ || $outputVal < 0 || $outputVal > 65534) {
- return "Usage: $cmd variable is not numeric or out of range.";
- }
- $channel = shift(@a);
- $channel = AttrVal($name, "defaultChannel", AttrVal($name, "devChannel", undef)) if (!defined $channel);
- if (!defined $channel || $channel eq "all") {
- CommandDeleteReading(undef, "$name autoOffTime.*");
- $channel = 30;
- } elsif ($channel eq "input" || $channel + 0 == 31) {
- $channel = 31;
- } elsif ($channel + 0 >= 30) {
- CommandDeleteReading(undef, "$name autoOffTime.*");
- $channel = 30;
- } elsif ($channel >= 0 && $channel <= 29) {
- } else {
- return "$cmd $channel wrong, choose 0...31|all|input.";
- }
- my $extSwitchMode = ReadingsVal($name, "extSwitchMode", 'unavailable');
- my %extSwitchMode = (
- "unavailable" => 0,
- "switch" => 1,
- "pushbutton" => 2,
- "auto" => 3
- );
- if (exists $extSwitchMode{$extSwitchMode}) {
- $extSwitchMode = $extSwitchMode{$extSwitchMode};
- } else {
- $extSwitchMode = 0;
- }
- my $extSwitchType = ReadingsVal($name, "extSwitchType", 'toggle') eq 'direction' ? 1 : 0;
- $data = sprintf "%02X%02X%04XFFFF%02X", $cmdID, $channel, $outputVal, $extSwitchMode << 6 | $extSwitchType << 5;
- } elsif ($cmd eq "delayOffTime") {
- shift(@a);
- $updateState = 0;
- $cmdID = 0x0B;
- $outputVal = int(shift(@a) * 10);
- if (!defined $outputVal || $outputVal !~ m/^[+-]?\d+$/ || $outputVal < 0 || $outputVal > 65534) {
- return "Usage: $cmd variable is not numeric or out of range.";
- }
- $channel = shift(@a);
- $channel = AttrVal($name, "defaultChannel", AttrVal($name, "devChannel", undef)) if (!defined $channel);
- if (!defined $channel || $channel eq "all") {
- CommandDeleteReading(undef, "$name delayOffTime.*");
- $channel = 30;
- } elsif ($channel eq "input" || $channel + 0 == 31) {
- $channel = 31;
- } elsif ($channel + 0 >= 30) {
- CommandDeleteReading(undef, "$name delayOffTime.*");
- $channel = 30;
- } elsif ($channel >= 0 && $channel <= 29) {
- } else {
- return "$cmd $channel wrong, choose 0...31|all|input.";
- }
- my $extSwitchMode = ReadingsVal($name, "extSwitchMode", 'unavailable');
- my %extSwitchMode = (
- "unavailable" => 0,
- "switch" => 1,
- "pushbutton" => 2,
- "auto" => 3
- );
- if (exists $extSwitchMode{$extSwitchMode}) {
- $extSwitchMode = $extSwitchMode{$extSwitchMode};
- } else {
- $extSwitchMode = 0;
- }
- my $extSwitchType = ReadingsVal($name, "extSwitchType", 'toggle') eq 'direction' ? 1 : 0;
- $data = sprintf "%02X%02XFFFF%04X%02X", $cmdID, $channel, $outputVal, $extSwitchMode << 6 | $extSwitchType << 5;
- } elsif ($cmd eq "extSwitchMode") {
- shift(@a);
- $updateState = 0;
- $cmdID = 0x0B;
- my $extSwitchMode = shift(@a);
- return "Usage: $cmd variable is missing, choose unavailable|switch|pushbutton|auto." if (!defined $extSwitchMode);
- my %extSwitchMode = (
- "unavailable" => 0,
- "switch" => 1,
- "pushbutton" => 2,
- "auto" => 3
- );
- if (exists $extSwitchMode{$extSwitchMode}) {
- $extSwitchMode = $extSwitchMode{$extSwitchMode};
- } else {
- return "Usage: $cmd variable wrong, choose unavailable|switch|pushbutton|auto.";
- }
- $channel = shift(@a);
- $channel = AttrVal($name, "defaultChannel", AttrVal($name, "devChannel", undef)) if (!defined $channel);
- if (!defined $channel || $channel eq "all") {
- CommandDeleteReading(undef, "$name extSwitchMode.*");
- $channel = 30;
- } elsif ($channel eq "input" || $channel + 0 == 31) {
- $channel = 31;
- } elsif ($channel + 0 >= 30) {
- CommandDeleteReading(undef, "$name extSwitchMode.*");
- $channel = 30;
- } elsif ($channel >= 0 && $channel <= 29) {
- } else {
- return "$cmd $channel wrong, choose 0...31|all|input.";
- }
- my $extSwitchType = ReadingsVal($name, "extSwitchType", 'toggle') eq 'direction' ? 1 : 0;
- $data = sprintf "%02X%02XFFFFFFFF%02X", $cmdID, $channel, $extSwitchMode << 6 | $extSwitchType << 5;
- } elsif ($cmd eq "extSwitchType") {
- shift(@a);
- $updateState = 0;
- $cmdID = 0x0B;
- my $extSwitchType = shift(@a);
- return "Usage: $cmd variable is missing, choose toggle|direction." if (!defined $extSwitchType);
- my %extSwitchType = (
- "toggle" => 0,
- "direction" => 1,
- );
- if (exists $extSwitchType{$extSwitchType}) {
- $extSwitchType = $extSwitchType{$extSwitchType};
- } else {
- return "Usage: $cmd variable wrong, choose toggle|direction.";
- }
- $channel = shift(@a);
- $channel = AttrVal($name, "defaultChannel", AttrVal($name, "devChannel", undef)) if (!defined $channel);
- if (!defined $channel || $channel eq "all") {
- CommandDeleteReading(undef, "$name extSwitchMode.*");
- $channel = 30;
- } elsif ($channel eq "input" || $channel + 0 == 31) {
- $channel = 31;
- } elsif ($channel + 0 >= 30) {
- CommandDeleteReading(undef, "$name extSwitchMode.*");
- $channel = 30;
- } elsif ($channel >= 0 && $channel <= 29) {
- } else {
- return "$cmd $channel wrong, choose 0...31|all|input.";
- }
- my $extSwitchMode = ReadingsVal($name, "extSwitchMode", 'unavailable');
- my %extSwitchMode = (
- "unavailable" => 0,
- "switch" => 1,
- "pushbutton" => 2,
- "auto" => 3
- );
- if (exists $extSwitchMode{$extSwitchMode}) {
- $extSwitchMode = $extSwitchMode{$extSwitchMode};
- } else {
- $extSwitchMode = 0;
- }
- $data = sprintf "%02X%02XFFFFFFFF%02X", $cmdID, $channel, $extSwitchMode << 6 | $extSwitchType << 5;
- } elsif ($cmd eq "special") {
- $rorg = "D1";
- shift(@a);
- $updateState = 0;
- my $repeaterActive = 0;
- my $repeaterLevel = 0;
- my $specialCmd = shift(@a);
- if ($manufID eq "046") {
- if (!defined $specialCmd) {
- return "$cmd <command> wrong, choose repeaterLevel.";
- } elsif ($specialCmd eq "repeaterLevel") {
- $cmdID = 8;
- my $repeaterLevel = shift(@a);
- if (defined $repeaterLevel && $repeaterLevel =~ m/^off|1|2$/) {
- if ($repeaterLevel eq "off") {
- $repeaterLevel = 0;
- } else {
- $repeaterActive = 1;
- }
- } else {
- return "Usage: repeaterLevel is wrong";
- }
- } else {
- return "$cmd $specialCmd <arg> wrong, choose repeaterLevel off|1|2.";
- }
- }
- $data = sprintf "0046%02X%02X%02X", $cmdID, $repeaterActive, $repeaterLevel;
- } else {
- if ($manufID =~ m/^046$/) {
- $cmdList .= "dim:slider,0,1,100 on off autoOffTime delayOffTime extSwitchMode extSwitchType local measurement roomCtrlMode:off,buildingProtection,economy,comfort-2,comfort-1,comfort special";
- } else {
- $cmdList .= "dim:slider,0,1,100 on off autoOffTime delayOffTime extSwitchMode extSwitchType local measurement roomCtrlMode:off,buildingProtection,economy,comfort-2,comfort-1,comfort";
- }
- return SetExtensions ($hash, $cmdList, $name, @a);
- }
- Log3 $name, 3, "EnOcean set $name $cmd";
- } elsif ($st =~ m/^blindsCtrl\.0[01]$/) {
- # Blinds Control for Position and Angle
- # (D2-05-xx)
- $rorg = "D2";
- $updateState = 0;
- my $cmdID;
- my $channel = AttrVal($name, "defaultChannel", 'all');
- my $position = 127;
- my $angle = 127;
- my $repo = AttrVal($name, "reposition", "directly");
- if ($repo eq "directly") {
- $repo = 0;
- } elsif ($repo eq "opens") {
- $repo = 1;
- } elsif ($repo eq "closes") {
- $repo = 2;
- } else {
- $repo = 0;
- }
- my $lock = 0;
- if ($cmd =~ m/^\d+$/) {
- # interpretive numeric value as position
- unshift(@a, 'position');
- $cmd = 'position';
- }
- if ($cmd eq "position") {
- $cmdID = 1;
- shift(@a);
- if (ReadingsVal($name, "block", "unlock") ne "unlock") {
- return "Attention: Device locked";
- }
- if (defined $a[0]) {
- # position value
- if (($a[0] =~ m/^\d+$/) && ($a[0] >= 0) && ($a[0] <= 100)) {
- $position = shift(@a);
- if (defined $a[0]) {
- # angle value
- if (($a[0] =~ m/^\d+$/) && ($a[0] >= 0) && ($a[0] <= 100)) {
- $angle = shift(@a);
- if (defined $a[0]) {
- # channel
- $channel = shift(@a);
- if ($channel =~ m/^all$/) {
- $channel = 15;
- } elsif ($channel =~ m/^[1234]$/) {
- $channel -= 1;
- } else {
- return "Usage: $position $angle $channel argument unknown, choose one of 1|2|3|4|all";
- }
- } else {
- $channel = 15;
- }
- if (defined $a[0]) {
- # reposition value
- $repo = shift(@a);
- if ($repo eq "directly") {
- $repo = 0;
- } elsif ($repo eq "opens") {
- $repo = 1;
- } elsif ($repo eq "closes") {
- $repo = 2;
- } else {
- return "Usage: $position $angle $channel $repo argument unknown, choose one of directly opens closes";
- }
- }
- readingsSingleUpdate($hash, "anglePos", $angle, 1);
- } else {
- return "Usage: $position $a[0] is not numeric or out of range";
- }
- } else {
- $channel = 15;
- }
- readingsSingleUpdate($hash, "state", "in_motion", 1);
- } else {
- return "Usage: $a[0] is not numeric or out of range";
- }
- } else {
- return "Usage: set <name> position <position> [<angle> [<repo>]]";
- }
- $data = sprintf "%02X%02X%02X%02X", $position, $angle, $repo << 4 | $lock, $channel << 4 | $cmdID;
- } elsif ($cmd eq "anglePos") {
- $cmdID = 1;
- shift(@a);
- if (ReadingsVal($name, "block", "unlock") ne "unlock") {
- return "Attention: Device locked";
- }
- if (defined $a[0]) {
- if (($a[0] =~ m/^\d+$/) && ($a[0] >= 0) && ($a[0] <= 100)) {
- $angle = shift(@a);
- if (defined $a[0]) {
- # channel
- $channel = shift(@a);
- if ($channel =~ m/^all$/) {
- $channel = 15;
- } elsif ($channel =~ m/^[1234]$/) {
- $channel -= 1;
- } else {
- return "Usage: $angle $channel argument unknown, choose one of 1|2|3|4|all";
- }
- } else {
- $channel = 15;
- }
- readingsSingleUpdate($hash, "state", "in_motion", 1);
- } else {
- return "Usage: $a[0] is not numeric or out of range";
- }
- } else {
- return "Usage: set <name> anglePos <angle>";
- }
- $repo = 0;
- $data = sprintf "%02X%02X%02X%02X", $position, $angle, $repo << 4 | $lock, $channel << 4 | $cmdID;
- } elsif ($cmd eq "stop") {
- $cmdID = 2;
- shift(@a);
- if (defined $a[0]) {
- # channel
- $channel = shift(@a);
- if ($channel =~ m/^all$/) {
- $channel = 15;
- } elsif ($channel =~ m/^[1234]$/) {
- $channel -= 1;
- } else {
- return "Usage: stop $channel argument unknown, choose one of 1|2|3|4|all";
- }
- } else {
- $channel = 15;
- }
- readingsSingleUpdate($hash, "state", "stopped", 1);
- $data = sprintf "%02X", $channel << 4 | $cmdID;
- } elsif ($cmd eq "opens") {
- $cmdID = 1;
- shift(@a);
- if (ReadingsVal($name, "block", "unlock") ne "unlock") {
- return "Attention: Device locked";
- }
- if (defined $a[0]) {
- # channel
- $channel = shift(@a);
- if ($channel =~ m/^all$/) {
- $channel = 15;
- } elsif ($channel =~ m/^[1234]$/) {
- $channel -= 1;
- } else {
- return "Usage: opens $channel argument unknown, choose one of 1|2|3|4|all";
- }
- } else {
- $channel = 15;
- }
- $position = 0;
- $repo = 0;
- $data = sprintf "%02X%02X%02X%02X", $position, $angle, $repo << 4 | $lock, $channel << 4 | $cmdID;
- } elsif ($cmd eq "closes") {
- $cmdID = 1;
- shift(@a);
- if (ReadingsVal($name, "block", "unlock") ne "unlock") {
- return "Attention: Device locked";
- }
- if (defined $a[0]) {
- # channel
- $channel = shift(@a);
- if ($channel =~ m/^all$/) {
- $channel = 15;
- } elsif ($channel =~ m/^[1234]$/) {
- $channel -= 1;
- } else {
- return "Usage: closes $channel argument unknown, choose one of 1|2|3|4|all";
- }
- } else {
- $channel = 15;
- }
- $position = 100;
- $repo = 0;
- $data = sprintf "%02X%02X%02X%02X", $position, $angle, $repo << 4 | $lock, $channel << 4 | $cmdID;
- } elsif ($cmd eq "unlock") {
- $cmdID = 1;
- shift(@a);
- if (defined $a[0]) {
- # channel
- $channel = shift(@a);
- if ($channel =~ m/^all$/) {
- $channel = 15;
- } elsif ($channel =~ m/^[1234]$/) {
- $channel -= 1;
- } else {
- return "Usage: unlock $channel argument unknown, choose one of 1|2|3|4|all";
- }
- } else {
- $channel = 15;
- }
- $repo = 0;
- $lock = 7;
- $data = sprintf "%02X%02X%02X%02X", $position, $angle, $repo << 4 | $lock, $channel << 4 | $cmdID;
- } elsif ($cmd eq "lock") {
- $cmdID = 1;
- shift(@a);
- if (defined $a[0]) {
- # channel
- $channel = shift(@a);
- if ($channel =~ m/^all$/) {
- $channel = 15;
- } elsif ($channel =~ m/^[1234]$/) {
- $channel -= 1;
- } else {
- return "Usage: lock $channel argument unknown, choose one of 1|2|3|4|all";
- }
- } else {
- $channel = 15;
- }
- $repo = 0;
- $lock = 1;
- $data = sprintf "%02X%02X%02X%02X", $position, $angle, $repo << 4 | $lock, $channel << 4 | $cmdID;
- } elsif ($cmd eq "alarm") {
- $cmdID = 1;
- shift(@a);
- if (defined $a[0]) {
- # channel
- $channel = shift(@a);
- if ($channel =~ m/^all$/) {
- $channel = 15;
- } elsif ($channel =~ m/^[1234]$/) {
- $channel -= 1;
- } else {
- return "Usage: alarm $channel argument unknown, choose one of 1|2|3|4|all";
- }
- } else {
- $channel = 15;
- }
- $repo = 0;
- $lock = 2;
- $data = sprintf "%02X%02X%02X%02X", $position, $angle, $repo << 4 | $lock, $channel << 4 | $cmdID;
- } else {
- $cmdList .= "position:slider,0,1,100 anglePos:slider,0,1,100 stop opens closes lock unlock alarm";
- return "Unknown argument $cmd, choose one of $cmdList";
- }
- Log3 $name, 3, "EnOcean set $name $cmd";
- } elsif ($st eq "multisensor.01") {
- #####
- # Multisensor Windows Handle
- # (D2-06-01)
- $rorg = "D2";
- $updateState = 2;
- my $waitingCmds = ReadingsVal($name, "waitingCmds", undef);
- if (defined $waitingCmds) {
- # check presence state
- $waitingCmds = ReadingsVal($name, "presence", "present") eq "absent" ? $waitingCmds & 0xDF | 32 : $waitingCmds & 0xDF;
- } else {
- $waitingCmds = ReadingsVal($name, "presence", "present") eq "absent" ? 32 : 0;
- }
- if ($cmd eq "presence") {
- # set presence
- if (defined $a[1]) {
- if ($a[1] =~ m/^absent$/) {
- readingsSingleUpdate($hash, "presence", $a[1], 1);
- Log3 $name, 3, "EnOcean set $name $cmd $a[1]";
- shift(@a);
- readingsSingleUpdate($hash, "waitingCmds", $waitingCmds & 0xDF | 32, 0);
- } elsif ($a[1] =~ m/^present$/) {
- readingsSingleUpdate($hash, "presence", $a[1], 1);
- Log3 $name, 3, "EnOcean set $name $cmd $a[1]";
- shift(@a);
- readingsSingleUpdate($hash, "waitingCmds", $waitingCmds & 0xDF, 0);
- } else {
- return "Usage: $a[1] is not numeric or out of range";
- }
- }
- } elsif ($cmd eq "handleClosedClick") {
- # set battery closed click
- if (defined $a[1]) {
- if ($a[1] =~ m/^disable$/) {
- readingsSingleUpdate($hash, "handleClosedClick", $a[1] . 'd', 1);
- Log3 $name, 3, "EnOcean set $name $cmd $a[1]";
- shift(@a);
- readingsSingleUpdate($hash, "waitingCmds", $waitingCmds & 0xE7 | 8, 0);
- } elsif ($a[1] =~ m/^enable$/) {
- readingsSingleUpdate($hash, "handleClosedClick", $a[1] . 'd', 1);
- Log3 $name, 3, "EnOcean set $name $cmd $a[1]";
- shift(@a);
- readingsSingleUpdate($hash, "waitingCmds", $waitingCmds & 0xE7 | 16, 0);
- } else {
- return "Usage: $a[1] is not numeric or out of range";
- }
- }
- } elsif ($cmd eq "batteryLowClick") {
- # set battery click low
- if (defined $a[1]) {
- if ($a[1] =~ m/^disable$/) {
- readingsSingleUpdate($hash, "batteryLowClick", $a[1] . 'd', 1);
- Log3 $name, 3, "EnOcean set $name $cmd $a[1]";
- shift(@a);
- readingsSingleUpdate($hash, "waitingCmds", $waitingCmds & 0xF9 | 2, 0);
- } elsif ($a[1] =~ m/^enable$/) {
- readingsSingleUpdate($hash, "batteryLowClick", $a[1] . 'd', 1);
- Log3 $name, 3, "EnOcean set $name $cmd $a[1]";
- shift(@a);
- readingsSingleUpdate($hash, "waitingCmds", $waitingCmds & 0xF9 | 4, 0);
- } else {
- return "Usage: $a[1] is not numeric or out of range";
- }
- }
- } elsif ($cmd eq "updateInterval") {
- # set update interval
- if (defined $a[1]) {
- if ($a[1] =~ m/^\d+$/ && $a[1] >= 5 && $a[1] <= 65535) {
- readingsSingleUpdate($hash, "updateInterval", $a[1], 1);
- readingsSingleUpdate($hash, "updateIntervalSet", $a[1], 0);
- Log3 $name, 3, "EnOcean set $name $cmd $a[1]";
- shift(@a);
- readingsSingleUpdate($hash, "waitingCmds", $waitingCmds | 1, 0);
- } else {
- return "Usage: $a[1] is not numeric or out of range";
- }
- }
- } elsif ($cmd eq "blinkInterval") {
- # set blick interval
- if (defined $a[1]) {
- if ($a[1] =~ m/^\d+$/ && $a[1] >= 3 && $a[1] <= 255) {
- readingsSingleUpdate($hash, "blinkInterval", $a[1], 1);
- readingsSingleUpdate($hash, "blinkIntervalSet", $a[1], 0);
- Log3 $name, 3, "EnOcean set $name $cmd $a[1]";
- shift(@a);
- readingsSingleUpdate($hash, "waitingCmds", $waitingCmds | 1, 0);
- } else {
- return "Usage: $a[1] is not numeric or out of range";
- }
- }
- } elsif ($cmd eq "teachSlave") {
- # teach slave
- $updateState = 0;
- $destinationID = "FFFFFFFF";
- if (defined $a[1]) {
- if ($a[1] =~ m/^contact$/) {
- $rorg = "D5";
- $data = '00';
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDefW", "biDir");
- readingsSingleUpdate($hash, "teachSlave", '1BS teach-in sent', 1);
- Log3 $name, 3, "EnOcean set $name $cmd $a[1]";
- shift(@a);
- } elsif ($a[1] =~ m/^windowHandleOpen$/) {
- $rorg = "F6";
- $data = 'E0';
- $status = '20';
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDefH", "biDir");
- readingsSingleUpdate($hash, "teachSlave", 'RPS teach-in sent', 1);
- Log3 $name, 3, "EnOcean set $name $cmd $a[1]";
- shift(@a);
- } elsif ($a[1] =~ m/^windowHandleClosed$/) {
- $rorg = "F6";
- $data = 'F0';
- $status = '20';
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDefH", "biDir");
- readingsSingleUpdate($hash, "teachSlave", 'RPS teach-in sent', 1);
- Log3 $name, 3, "EnOcean set $name $cmd $a[1]";
- shift(@a);
- } elsif ($a[1] =~ m/^windowHandleTilted$/) {
- $rorg = "F6";
- $data = 'D0';
- $status = '20';
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDefH", "biDir");
- readingsSingleUpdate($hash, "teachSlave", 'RPS teach-in sent', 1);
- Log3 $name, 3, "EnOcean set $name $cmd $a[1]";
- shift(@a);
- } else {
- return "Usage: $a[1] is wrong";
- }
- }
- } else {
- $cmdList .= "presence:absent,present handleClosedClick:enable,disable batteryLowClick:enable,disable " .
- "updateInterval blinkInterval teachSlave:contact,windowHandleClosed,windowHandleOpen,windowHandleTilted";
- return "Unknown argument $cmd, choose one of $cmdList";
- }
- } elsif ($st eq "roomCtrlPanel.00") {
- # Room Control Panel
- # (D2-10-00 - D2-10-02)
- $rorg = "D2";
- $updateState = 2;
- if ($cmd eq "desired-temp"|| $cmd eq "setpointTemp") {
- if (defined $a[1]) {
- if (($a[1] =~ m/^[+-]?\d+(\.\d+)?$/) && ($a[1] >= 0) && ($a[1] <= 40)) {
- $a[1] = sprintf "%0.1f", $a[1];
- readingsSingleUpdate($hash, "setpointTemp", $a[1], 1);
- readingsSingleUpdate($hash, "setpointTempSet", $a[1], 0);
- Log3 $name, 3, "EnOcean set $name setpointTemp $a[1]";
- shift(@a);
- readingsSingleUpdate($hash, "waitingCmds", ReadingsVal($name, "waitingCmds", 0) | 2, 0);
- } else {
- return "Usage: $a[1] is not numeric or out of range";
- }
- }
- } elsif ($cmd eq "economyTemp" || $cmd eq "preComfortTemp" || $cmd eq "buildingProtectionTemp" || $cmd eq "comfortTemp") {
- if (defined $a[1]) {
- if (($a[1] =~ m/^[+-]?\d+(\.\d+)?$/) && ($a[1] >= 0) && ($a[1] <= 40)) {
- Log3 $name, 3, "EnOcean set $name $cmd $a[1]";
- $cmd =~ s/(\b)([a-z])/$1\u$2/g;
- $a[1] = sprintf "%0.1f", $a[1];
- readingsSingleUpdate($hash, "setpoint" . $cmd, $a[1], 1);
- readingsSingleUpdate($hash, "setpoint" . $cmd . "Set", $a[1], 0);
- shift(@a);
- readingsSingleUpdate($hash, "waitingCmds", ReadingsVal($name, "waitingCmds", 0) | 8, 0);
- } else {
- return "Usage: $a[1] is not numeric or out of range";
- }
- }
- } elsif ($cmd eq "fanSpeed") {
- if (defined $a[1]) {
- if ($a[1] >= 0 && $a[1] <= 100) {
- readingsSingleUpdate($hash, "fanSpeed", $a[1], 1);
- readingsSingleUpdate($hash, "fanSpeedSet", $a[1], 0);
- Log3 $name, 3, "EnOcean set $name fanSpeed $a[1]";
- shift(@a);
- readingsSingleUpdate($hash, "waitingCmds", ReadingsVal($name, "waitingCmds", 0) | 2, 0);
- } else {
- return "Usage: $a[1] is wrong.";
- }
- }
- } elsif ($cmd eq "fanSpeedMode") {
- if (defined $a[1]) {
- if ($a[1] =~ m/^(central|local)$/) {
- readingsSingleUpdate($hash, "fanSpeedMode", $a[1], 1);
- readingsSingleUpdate($hash, "fanSpeedModeSet", $a[1], 0);
- Log3 $name, 3, "EnOcean set $name fanSpeedMode $a[1]";
- shift(@a);
- readingsSingleUpdate($hash, "waitingCmds", ReadingsVal($name, "waitingCmds", 0) | 2, 0);
- } else {
- return "Usage: $a[1] is wrong.";
- }
- }
- } elsif ($cmd eq "cooling") {
- if (defined $a[1]) {
- if ($a[1] =~ m/^(on|off|auto|no_change)$/) {
- readingsSingleUpdate($hash, "cooling", $a[1], 1);
- readingsSingleUpdate($hash, "coolingSet", $a[1], 0);
- Log3 $name, 3, "EnOcean set $name cooling $a[1]";
- shift(@a);
- readingsSingleUpdate($hash, "waitingCmds", ReadingsVal($name, "waitingCmds", 0) | 2, 0);
- } else {
- return "Usage: $a[1] is wrong.";
- }
- }
- } elsif ($cmd eq "heating") {
- if (defined $a[1]) {
- if ($a[1] =~ m/^(on|off|auto|no_change)$/) {
- readingsSingleUpdate($hash, "heating", $a[1], 1);
- readingsSingleUpdate($hash, "heatingSet", $a[1], 0);
- Log3 $name, 3, "EnOcean set $name heating $a[1]";
- shift(@a);
- readingsSingleUpdate($hash, "waitingCmds", ReadingsVal($name, "waitingCmds", 0) | 2, 0);
- } else {
- return "Usage: $a[1] is wrong.";
- }
- }
- } elsif ($cmd eq "roomCtrlMode") {
- if (defined $a[1]) {
- if ($a[1] =~ m/^(comfort|preComfort|economy|buildingProtection)$/) {
- readingsSingleUpdate($hash, "roomCtrlMode", $a[1], 1);
- readingsSingleUpdate($hash, "roomCtrlModeSet", $a[1], 0);
- Log3 $name, 3, "EnOcean set $name roomCtrlMode $a[1]";
- shift(@a);
- readingsSingleUpdate($hash, "waitingCmds", ReadingsVal($name, "waitingCmds", 0) | 2, 0);
- } else {
- return "Usage: $a[1] is wrong.";
- }
- }
- } elsif ($cmd eq "config") {
- readingsSingleUpdate($hash, "waitingCmds", ReadingsVal($name, "waitingCmds", 0) | 64, 0);
- Log3 $name, 3, "EnOcean set $name $cmd";
- } elsif ($cmd eq "timeProgram") {
- # delete remote and send new time program
- delete $hash->{helper}{4}{telegramWait};
- for (my $messagePartCntr = 1; $messagePartCntr <= 4; $messagePartCntr ++) {
- if (defined AttrVal($name, "timeProgram" . $messagePartCntr, undef)) {
- $hash->{helper}{4}{telegramWait}{$messagePartCntr} = 1;
- Log3 $name, 3, "EnOcean $name EnOcean_Set timeProgram" . $messagePartCntr . " set";
- }
- }
- readingsSingleUpdate($hash, "waitingCmds", ReadingsVal($name, "waitingCmds", 0) | 528, 0);
- Log3 $name, 3, "EnOcean set $name $cmd";
- } elsif ($cmd eq "deleteTimeProgram") {
- readingsSingleUpdate($hash, "waitingCmds", ReadingsVal($name, "waitingCmds", 0) | 512, 0);
- Log3 $name, 3, "EnOcean set $name $cmd";
- } elsif ($cmd eq "time") {
- readingsSingleUpdate($hash, "waitingCmds", ReadingsVal($name, "waitingCmds", 0) | 4, 0);
- Log3 $name, 3, "EnOcean set $name $cmd";
- } elsif ($cmd eq "window") {
- if (defined $a[1]) {
- if ($a[1] =~ m/^closed|open$/) {
- readingsSingleUpdate($hash, "window", $a[1], 1);
- readingsSingleUpdate($hash, "windowSet", $a[1], 0);
- Log3 $name, 3, "EnOcean set $name window $a[1]";
- shift(@a);
- readingsSingleUpdate($hash, "waitingCmds", ReadingsVal($name, "waitingCmds", 0) | 2, 0);
- } else {
- return "Usage: $a[1] is wrong.";
- }
- }
- } elsif ($cmd eq "clearCmds") {
- CommandDeleteReading(undef, "$name waitingCmds");
- Log3 $name, 3, "EnOcean set $name $cmd";
- } else {
- $cmdList .= "cooling:auto,off,on,no_change desired-temp setpointTemp:slider,0,1,40 " .
- "comfortTemp deleteTimeProgram:noArg " .
- "economyTemp preComfortTemp buildingProtectionTemp config:noArg " .
- "clearCmds:noArg fanSpeed:slider,0,1,100 heating:auto,off,on,no_change " .
- "fanSpeedMode:central,local time:noArg " .
- "roomCtrlMode:comfort,economy,preComfort,buildingProtection timeProgram:noArg window:closed,open";
- return "Unknown argument $cmd, choose one of $cmdList";
- }
- } elsif ($st eq "roomCtrlPanel.01") {
- # Room Control Panel
- # (D2-11-01 - D2-11-08)
- $rorg = "D2";
- $updateState = 0;
- my $cooling = ReadingsVal($name, "colling", 'off');
- my $fanSpeed = ReadingsVal($name, "fanSpeed", 'auto');
- my $heating = ReadingsVal($name, "heating", 'off');
- my $humidity = ReadingsVal($name, "humidity", 0);
- my $occupancy = ReadingsVal($name, "occupancy", 'unoccupied');
- my $setpointBase = ReadingsVal($name, "setpointBase", 20);
- my $setpointTemp = ReadingsVal($name, "setpointTemp", 20);
- my $setpointShift = ReadingsVal($name, "setpointShift", 0);
- my $setpointShiftMax = ReadingsVal($name, "setpointShiftMax", 10);
- my $setpointType = ReadingsVal($name, "setpointType", 'setpointShift');
- my $temperature = ReadingsVal($name, "temperature", 20);
- my $waitingCmds = ReadingsVal($name, "waitingCmds", 0);
- my $window = ReadingsVal($name, "window", 'closed');
- if ($cmd eq "desired-temp"|| $cmd eq "setpointTemp") {
- if (defined $a[1]) {
- if ($a[1] =~ m/^\d+$/ && $a[1] >= 5 && $a[1] <= 40) {
- readingsBeginUpdate($hash);
- if ($a[1] < 15) {
- $setpointBase = 15;
- $setpointShift = $a[1] - $setpointBase;
- $setpointShiftMax = $setpointBase - $a[1] if ($setpointShiftMax < -$setpointShift);
- readingsBulkUpdate($hash, "setpointShiftMax", $setpointShiftMax);
- } elsif ($a[1] > 30) {
- $setpointBase = 30;
- $setpointShift = $a[1] - $setpointBase;
- $setpointShiftMax = $a[1] - $setpointBase if ($setpointShiftMax < $setpointShift);
- readingsBulkUpdate($hash, "setpointShiftMax", $setpointShiftMax);
- } else {
- $setpointBase = $a[1];
- $setpointShift = 0;
- }
- readingsBulkUpdate($hash, "setpointShift", $setpointShift);
- readingsBulkUpdate($hash, "setpointBase", $setpointBase);
- $setpointTemp = $a[1];
- readingsBulkUpdate($hash, "setpointTemp", $a[1]);
- readingsBulkUpdate($hash, "state", "T: $temperature H: $humidity SPT: $setpointTemp F: $fanSpeed");
- readingsBulkUpdate($hash, "waitingCmds", $waitingCmds |= 1);
- readingsEndUpdate($hash, 1);
- CommandDeleteReading(undef, "$name smartAckMailbox");
- Log3 $name, 3, "EnOcean set $name setpointTemp $a[1]";
- shift(@a);
- } else {
- return "Usage: $a[1] is not numeric or out of range";
- }
- } else {
- return "Usage: set <name> setpointTemp 5...40";
- }
- } elsif ($cmd eq "setpointShiftMax") {
- if (defined $a[1]) {
- if ($a[1] =~ m/^\d+$/ && $a[1] >= 1 && $a[1] <= 10) {
- readingsBeginUpdate($hash);
- if ($setpointTemp < 15 - $a[1]) {
- $setpointShiftMax = 15 - $setpointTemp;
- } elsif ($setpointTemp > 30 + $a[1]) {
- $setpointShiftMax = $setpointTemp - 30;
- } else {
- $setpointShiftMax = $a[1];
- }
- readingsBulkUpdate($hash, "setpointShiftMax", $setpointShiftMax);
- readingsBulkUpdate($hash, "waitingCmds", $waitingCmds |= 2);
- readingsEndUpdate($hash, 1);
- CommandDeleteReading(undef, "$name smartAckMailbox");
- Log3 $name, 3, "EnOcean set $name setpointShiftMax $a[1]";
- shift(@a);
- } else {
- return "Usage: $a[1] is not numeric or out of range";
- }
- } else {
- return "Usage: set <name> setpointShiftMax 1...10";
- }
- } elsif ($cmd eq "setpointType") {
- if (defined $a[1]) {
- if ($a[1] =~ m/^setpointTemp|setpointShift$/) {
- readingsBeginUpdate($hash);
- $setpointType = $a[1];
- readingsBulkUpdate($hash, "setpointType", $setpointType);
- readingsBulkUpdate($hash, "waitingCmds", $waitingCmds |= 0x80);
- readingsEndUpdate($hash, 1);
- CommandDeleteReading(undef, "$name smartAckMailbox");
- Log3 $name, 3, "EnOcean set $name setpointType $a[1]";
- shift(@a);
- } else {
- return "Usage: $a[1] is wrong.";
- }
- } else {
- return "Usage: set <name> setpointType setpointTemp|setpointShift";
- }
- } elsif ($cmd eq 'fanSpeed') {
- if (defined $a[1]) {
- if ($a[1] =~ m/^auto|off|1|2|3$/) {
- $fanSpeed = $a[1];
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "fanSpeed", $a[1]);
- readingsBulkUpdate($hash, "state", "T: $temperature H: $humidity SPT: $setpointTemp F: $fanSpeed");
- readingsBulkUpdate($hash, "waitingCmds", $waitingCmds |= 4);
- readingsEndUpdate($hash, 1);
- CommandDeleteReading(undef, "$name smartAckMailbox");
- Log3 $name, 3, "EnOcean set $name fanSpeed $a[1]";
- shift(@a);
- } else {
- return "Usage: $a[1] is wrong.";
- }
- } else {
- return "Usage: set <name> fanspeed 1...3|auto|off";
- }
- } elsif ($cmd eq "cooling") {
- if (defined $a[1]) {
- if ($a[1] =~ m/^on|off$/) {
- $cooling = $a[1];
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "cooling", $a[1]);
- if ($a[1] eq 'on') {
- $heating = 'off';
- readingsBulkUpdate($hash, "heating", 'off');
- }
- readingsBulkUpdate($hash, "waitingCmds", $waitingCmds |= 0x20);
- readingsEndUpdate($hash, 1);
- CommandDeleteReading(undef, "$name smartAckMailbox");
- Log3 $name, 3, "EnOcean set $name cooling $a[1]";
- shift(@a);
- } else {
- return "Usage: $a[1] is wrong.";
- }
- } else {
- return "Usage: set <name> cooling on|off";
- }
- } elsif ($cmd eq "heating") {
- if (defined $a[1]) {
- if ($a[1] =~ m/^on|off$/) {
- $heating = $a[1];
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "heating", $a[1]);
- if ($a[1] eq 'on') {
- $cooling = 'off';
- readingsBulkUpdate($hash, "cooling", 'off');
- }
- readingsBulkUpdate($hash, "waitingCmds", $waitingCmds |= 0x40);
- readingsEndUpdate($hash, 1);
- CommandDeleteReading(undef, "$name smartAckMailbox");
- Log3 $name, 3, "EnOcean set $name heating $a[1]";
- shift(@a);
- } else {
- return "Usage: $a[1] is wrong.";
- }
- } else {
- return "Usage: set <name> heating on|off";
- }
- } elsif ($cmd eq "occupancy") {
- if (defined $a[1]) {
- if ($a[1] =~ m/^occupied|unoccupied$/) {
- $occupancy = $a[1];
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "occupancy", $a[1]);
- readingsBulkUpdate($hash, "waitingCmds", $waitingCmds |= 8);
- readingsEndUpdate($hash, 1);
- CommandDeleteReading(undef, "$name smartAckMailbox");
- Log3 $name, 3, "EnOcean set $name occupancy $a[1]";
- shift(@a);
- } else {
- return "Usage: $a[1] is wrong.";
- }
- } else {
- return "Usage: set <name> occupancy occupied|unoccupied";
- }
- } elsif ($cmd eq "window") {
- if (defined $a[1]) {
- if ($a[1] =~ m/^closed|open$/) {
- $window = $a[1];
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "window", $a[1]);
- readingsBulkUpdate($hash, "waitingCmds", $waitingCmds |= 0x10);
- readingsEndUpdate($hash, 1);
- CommandDeleteReading(undef, "$name smartAckMailbox");
- Log3 $name, 3, "EnOcean set $name window $a[1]";
- shift(@a);
- } else {
- return "Usage: $a[1] is wrong.";
- }
- } else {
- return "Usage: set <name> window open|closed";
- }
- } else {
- $cmdList .= "setpointTemp:slider,5,1,40 cooling:off,on desired-temp " .
- "fanSpeed:auto,off,1,2,3 heating:off,on occupancy:occupied,unoccupied " .
- "setpointShiftMax:slider,1,1,10 setpointType:setpointShift,setpointTemp window:closed,open";
- return "Unknown argument $cmd, choose one of $cmdList";
- }
- $setpointType = $setpointType eq 'setpointTemp' ? 0x80 : 0;
- $heating = $heating eq 'on' ? 0x40 : 0;
- $cooling = $cooling eq 'on' ? 0x20 : 0;
- $window = $window eq 'open' ? 0x10 : 0;
- $setpointShift = int(($setpointShift + $setpointShiftMax) * 255 / ($setpointShiftMax * 2));
- #$setpointShift = unpack('C', pack('c', $setpointShift));
- my %fanSpeed = ('auto' => 0, 'off' =>1, 1 => 2, 2 => 3, 3 => 4);
- $occupancy = $occupancy eq 'occupied' ? 1 : 0;
- $data = sprintf "%02X%02X%02X%02X", $setpointType |$heating | $cooling | $window | 1,
- $setpointShift, $setpointBase,
- $setpointShiftMax << 4 | $fanSpeed{$fanSpeed} << 1 | $occupancy;
- } elsif ($st eq "fanCtrl.00") {
- # Fan Control
- # (D2-20-00 - D2-20-02)
- $rorg = "D2";
- #$updateState = 0;
- my ($fanSpeed, $humiThreshold, $roomSize, $roomSizeRef, $humidityCtrl, $tempLevel, $opMode) =
- (255, 255, 15, 3, 3, 3, 15);
- my $messageType = 0;
- my @roomSizeTbl = (25, 50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300, 325, 350);
- if ($cmd eq "fanSpeed") {
- $updateState = 0;
- if (defined $a[1]) {
- if ($a[1] =~ m/^\d+$/ && $a[1] >= 0 && $a[1] <= 100) {
- $fanSpeed = $a[1];
- readingsSingleUpdate($hash, "fanSpeed", $a[1], 1);
- Log3 $name, 3, "EnOcean set $name fanSpeed $a[1]";
- shift(@a);
- } elsif ($a[1] eq "auto") {
- $fanSpeed = 253;
- readingsSingleUpdate($hash, "fanSpeed", $a[1], 1);
- Log3 $name, 3, "EnOcean set $name fanSpeed $a[1]";
- shift(@a);
- } elsif ($a[1] eq "default") {
- $fanSpeed = 254;
- readingsSingleUpdate($hash, "fanSpeed", $a[1], 1);
- Log3 $name, 3, "EnOcean set $name fanSpeed $a[1]";
- shift(@a);
- } else {
- return "Usage: $a[1] is wrong.";
- }
- } else {
- return "Usage: set <name> fanspeed 0...100|auto|default";
- }
- shift(@a);
- } elsif ($cmd eq "on") {
- $opMode = 1;
- Log3 $name, 3, "EnOcean set $name $a[0]";
- shift(@a);
- } elsif ($cmd eq "off") {
- $opMode = 0;
- Log3 $name, 3, "EnOcean set $name $a[0]";
- shift(@a);
- } elsif ($cmd eq "desired-temp" || $cmd eq "setpointTemp") {
- $updateState = 0;
- my $temperatureRefDev = AttrVal($name, "temperatureRefDev", undef);
- return "attribute missing: attr $name temperatureRefDev <refDev> must be defined" if (!defined $temperatureRefDev);
- my $temperature = ReadingsVal($temperatureRefDev, "temperature", 20);
- my $setpointTemp = ReadingsVal($name, "setpointTemp", 20);
- my $switchHysteresis = AttrVal($name, "switchHysteresis", 1);
- if (defined $a[1] && $a[1] =~ m/^\d+$/ && $a[1] >= 0 && $a[1] <= 40) {
- $setpointTemp = $a[1];
- shift(@a);
- }
- if ($setpointTemp > $temperature + $switchHysteresis / 2) {
- $tempLevel = 2;
- } elsif ($setpointTemp < $temperature - $switchHysteresis / 2) {
- $tempLevel = 0;
- } else {
- $tempLevel = 1;
- }
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "setpointTemp", sprintf("%.1f", $setpointTemp));
- readingsBulkUpdate($hash, "temperature", sprintf("%.1f", $temperature));
- readingsEndUpdate($hash, 1);
- Log3 $name, 3, "EnOcean set $name $cmd $setpointTemp";
- shift(@a);
- } elsif ($cmd eq "humidityThreshold") {
- $updateState = 0;
- if (defined $a[1]) {
- if ($a[1] =~ m/^\d+$/ && $a[1] >= 0 && $a[1] <= 100) {
- $humidityCtrl = 1;
- $humiThreshold = $a[1];
- readingsSingleUpdate($hash, "humidityThreshold", $a[1], 1);
- Log3 $name, 3, "EnOcean set $name humidityThreshold $a[1]";
- shift(@a);
- } elsif ($a[1] eq "auto") {
- $humidityCtrl = 1;
- $humiThreshold = 253;
- readingsSingleUpdate($hash, "humidityThreshold", $a[1], 1);
- Log3 $name, 3, "EnOcean set $name humidityThreshold $a[1]";
- shift(@a);
- } elsif ($a[1] eq "default") {
- $humidityCtrl = 2;
- $humiThreshold = 254;
- readingsSingleUpdate($hash, "humidityThreshold", $a[1], 1);
- Log3 $name, 3, "EnOcean set $name humidityThreshold $a[1]";
- shift(@a);
- } elsif ($a[1] eq "disabled") {
- $humidityCtrl = 0;
- $humiThreshold = 255;
- readingsSingleUpdate($hash, "humidityThreshold", $a[1], 1);
- Log3 $name, 3, "EnOcean set $name humidityThreshold $a[1]";
- shift(@a);
- } else {
- return "Usage: $a[1] is wrong.";
- }
- } else {
- return "Usage: set <name> humidityThreshold 0...100|auto|default|disabled";
- }
- shift(@a);
- } elsif ($cmd eq "roomSize") {
- $updateState = 0;
- if (defined $a[1]) {
- if ($a[1] =~ m/^\d+$/) {
- readingsSingleUpdate($hash, "roomSize", $a[1], 1);
- Log3 $name, 3, "EnOcean set $name roomSize $a[1]";
- $roomSize = 14;
- for (my $i = 0; $i < @roomSizeTbl; $i++) {
- if ($a[1] <= $roomSizeTbl[$i]) {
- $roomSize = $i;
- last;
- }
- }
- $roomSizeRef = 0;
- shift(@a);
- } elsif ($a[1] eq "not_used") {
- $roomSizeRef = 1;
- $roomSize = 15;
- readingsSingleUpdate($hash, "roomSize", $a[1], 1);
- Log3 $name, 3, "EnOcean set $name roomSize $a[1]";
- shift(@a);
- } elsif ($a[1] eq "default") {
- $roomSizeRef = 2;
- $roomSize = 15;
- readingsSingleUpdate($hash, "roomSize", $a[1], 1);
- Log3 $name, 3, "EnOcean set $name roomSize $a[1]";
- shift(@a);
- } elsif ($a[1] eq "max") {
- $roomSizeRef = 0;
- $roomSize = 14;
- readingsSingleUpdate($hash, "roomSize", $a[1], 1);
- Log3 $name, 3, "EnOcean set $name roomSize $a[1]";
- shift(@a);
- } else {
- return "Usage: $a[1] is wrong.";
- }
- } else {
- return "Usage: set <name> roomSize 0...350|max|not_used|default";
- }
- shift(@a);
- } else {
- $cmdList .= "off:noArg on:noArg desired-temp fanSpeed setpointTemp:slider,0,1,40 " .
- "humidityThreshold roomSize";
- return "Unknown argument $cmd, choose one of $cmdList";
- }
- $data = sprintf "%02X%02X%02X%02X", ($opMode << 4) | ($tempLevel << 1),
- ($humidityCtrl << 6) | ($roomSizeRef << 4) | $roomSize,
- $humiThreshold, $fanSpeed;
- } elsif ($st eq "heatRecovery.00") {
- # heat recovery ventilation
- # (D2-50-00)
- $rorg = "D2";
- $updateState = 0;
- my $airQuatityThreshold = ReadingsVal($name, "airQuatityThreshold", 'default');
- $airQuatityThreshold = $airQuatityThreshold eq 'default' ? 127 : $airQuatityThreshold;
- my $CO2Threshold = ReadingsVal($name, "CO2Threshold", 'default');
- $CO2Threshold = $CO2Threshold eq 'default' ? 127 : $CO2Threshold;
- my $heatExchangerBypass = 0;
- my $humidityThreshold = ReadingsVal($name, "humidityThreshold", 'default');
- $humidityThreshold = $humidityThreshold eq 'default' ? 127 : $humidityThreshold;
- my $startTimerMode = 0;
- my $tempThreshold = ReadingsVal($name, "roomTempSet", 'default');
- $tempThreshold = $tempThreshold eq 'default' ? 0 : $tempThreshold;
- my $ventilation = 15;
- if ($cmd eq "ventilation") {
- $ventilation = $a[1];
- return "Usage: $cmd variable is missing, choose off|1...4|auto|demand|supplyAir|exhaustAir." if (!defined $ventilation);
- shift(@a);
- my %ventilation = (
- "off" => 0,
- 1 => 1,
- 2 => 2,
- 3 => 3,
- 4 => 4,
- "auto" => 11,
- "demand" => 12,
- "supplyAir" => 13,
- "exhaustAir" => 14
- );
- if (exists $ventilation{$ventilation}) {
- $ventilation = $ventilation{$ventilation};
- } else {
- return "Usage: $cmd variable wrong, choose off|1...4|auto|demand|supplyAir|exhaustAir.";
- }
- } elsif ($cmd eq "heatExchangerBypass") {
- $heatExchangerBypass = $a[1];
- return "Usage: $cmd variable is missing, choose opens|closes." if (!defined $heatExchangerBypass);
- shift(@a);
- my %heatExchangerBypass = (
- "closes" => 1,
- "opens" => 2
- );
- if (exists $heatExchangerBypass{$heatExchangerBypass}) {
- $heatExchangerBypass = $heatExchangerBypass{$heatExchangerBypass};
- } else {
- return "Usage: $cmd variable wrong, choose opens|closes.";
- }
- } elsif ($cmd eq "startTimerMode") {
- $startTimerMode = 1;
- } elsif ($cmd eq "CO2Threshold") {
- $CO2Threshold = $a[1];
- if (defined $CO2Threshold) {
- if ($CO2Threshold eq 'default') {
- $CO2Threshold = 127;
- readingsSingleUpdate($hash, "CO2Threshold", 'default', 1);
- } elsif ($CO2Threshold =~ m/^\d+$/ && $CO2Threshold >= 0 && $CO2Threshold <= 100) {
- readingsSingleUpdate($hash, "CO2Threshold", $CO2Threshold, 1);
- } else {
- return "Usage: $cmd variable is wrong, choose default|0 ... 100." ;
- }
- } else {
- return "Usage: $cmd variable is wrong, choose default|0 ... 100." ;
- }
- shift(@a);
- } elsif ($cmd eq "humidityThreshold") {
- $humidityThreshold = $a[1];
- if (defined $humidityThreshold) {
- if ($humidityThreshold eq 'default') {
- $humidityThreshold = 127;
- readingsSingleUpdate($hash, "humidityThreshold", 'default', 1);
- } elsif ($humidityThreshold =~ m/^\d+$/ && $humidityThreshold >= 0 && $humidityThreshold <= 100) {
- readingsSingleUpdate($hash, "humidityThreshold", $humidityThreshold, 1);
- } else {
- return "Usage: $cmd variable is wrong, choose default|0 ... 100." ;
- }
- } else {
- return "Usage: $cmd variable is wrong, choose default|0 ... 100." ;
- }
- shift(@a);
- } elsif ($cmd eq "airQuatityThreshold") {
- $airQuatityThreshold = $a[1];
- if (defined $airQuatityThreshold) {
- if ($airQuatityThreshold eq 'default') {
- $airQuatityThreshold = 127;
- readingsSingleUpdate($hash, "airQuatityThreshold", 'default', 1);
- } elsif ($airQuatityThreshold =~ m/^\d+$/ && $airQuatityThreshold >= 0 && $airQuatityThreshold <= 100) {
- readingsSingleUpdate($hash, "airQuatityThreshold", $airQuatityThreshold, 1);
- } else {
- return "Usage: $cmd variable is wrong, choose default|0 ... 100." ;
- }
- } else {
- return "Usage: $cmd variable is wrong, choose default|0 ... 100." ;
- }
- shift(@a);
- } elsif ($cmd eq "roomTemp") {
- $tempThreshold = $a[1];
- if (defined $tempThreshold) {
- if ($tempThreshold eq 'default') {
- $tempThreshold = 0;
- readingsSingleUpdate($hash, "roomTempSet", 'default', 1);
- } elsif ($tempThreshold =~ m/^[+-]?\d+$/ && $tempThreshold >= - 63 && $tempThreshold <= 63) {
- readingsSingleUpdate($hash, "roomTempSet", $tempThreshold, 1);
- $tempThreshold += 64;
- #$tempThreshold = abs($tempThreshold) | 0x40 if ($tempThreshold < 0);
- } else {
- return "Usage: $cmd variable is wrong, choose default|-63 ... 63." ;
- }
- } else {
- return "Usage: $cmd variable is wrong, choose default|-63 ... 63." ;
- }
- shift(@a);
- } else {
- $cmdList .= "ventilation:off,1,2,3,4,auto,demand,supplyAir,exhaustAir roomTemp heatExchangerBypass:closes,opens " .
- "startTimerMode:noArg CO2Threshold humidityThreshold airQuatityThreshold";
- return "Unknown argument $cmd, choose one of $cmdList";
- }
- $data = sprintf "%02X%02X%02X%02X%02X%02X", 32 | $ventilation, $heatExchangerBypass << 4, $startTimerMode << 7 | $CO2Threshold,
- $humidityThreshold, $airQuatityThreshold, $tempThreshold;
- Log3 $name, 3, "EnOcean set $name $cmd";
- } elsif ($st eq "valveCtrl.00") {
- # Valve Control
- # (D2-A0-01)
- if (AttrVal($name, "devMode", "master") eq "slave") {
- # devNode slave
- if ($cmd eq "closed") {
- $rorg = "D2";
- $data = "01";
- } elsif ($cmd eq "open") {
- $rorg = "D2";
- $data = "02";
- } elsif ($cmd eq "teachIn") {
- $updateState = 0;
- ($err, $rorg, $data) = EnOcean_sndUTE(undef, $hash, "biDir",
- AttrVal($name, "uteResponseRequest", "yes"), "in", 0, "D2-A0-01");
- } elsif ($cmd eq "teachOut") {
- $updateState = 0;
- ($err, $rorg, $data) = EnOcean_sndUTE(undef, $hash, "biDir",
- AttrVal($name, "uteResponseRequest", "yes"), "out", 0, "D2-A0-01");
- } else {
- return "Unknown argument $cmd, choose one of " . $cmdList . "open:noArg closed:noArg teachIn:noArg teachOut:noArg";
- }
- } else {
- # devMode master
- if ($cmd eq "closes") {
- $rorg = "D2";
- $data = "01";
- } elsif ($cmd eq "opens") {
- $rorg = "D2";
- $data = "02";
- } else {
- return "Unknown argument $cmd, choose one of " . $cmdList . "opens:noArg closes:noArg";
- }
- }
- Log3 $name, 3, "EnOcean set $name $cmd";
- } elsif ($st eq "contact") {
- # 1BS Telegram
- # Single Input Contact (EEP D5-00-01)
- $rorg = "D5";
- my $setCmd;
- if ($cmd eq "teach") {
- $attr{$name}{eep} = "D5-00-01";
- CommandDeleteReading(undef, "$name .*");
- readingsSingleUpdate($hash, "teach", "1BS teach-in sent", 1);
- $setCmd = 0;
- $updateState = 0;
- } elsif ($cmd eq "closed") {
- $setCmd = 9;
- } elsif ($cmd eq "open") {
- $setCmd = 8;
- } else {
- return "Unknown argument $cmd, choose one of " . $cmdList . "open:noArg closed:noArg teach:noArg";
- }
- $data = sprintf "%02X", $setCmd;
- Log3 $name, 3, "EnOcean set $name $cmd";
- } elsif ($st eq "genericProfile") {
- # Generic Profile
- my $channel = 0;
- my $devMode = AttrVal($name, "devMode", "master");
- my $header = 1;
- my ($setChannel, $setChannelName) = split(/-|:/, $cmd, 2);
- my ($channelName, $channelDir, $channelType, $signalType, $valueType, $resolution, $engMin, $scalingMin, $engMax, $scalingMax);
- my ($readingFormat, $readingName, $readingType, $readingUnit, $readingValue);
- my $gpDef = AttrVal($name, "gpDef", undef);
- return "Usage: Channel definition is missing" if (!defined $gpDef);
- my @gpDef = split("[ \t][ \t]*", $gpDef);
- if ($cmd eq "channelName") {
- # rename channel name
- ($setChannel, $setChannelName) = split(/-|:/, $a[1], 2);
- if (defined $gpDef[$setChannel]) {
- ($channelName, $channelDir, $channelType, $signalType, $valueType, $resolution, $engMin, $scalingMin, $engMax, $scalingMax) =
- split(':', $gpDef[$setChannel]);
- # remove spaces und tabs
- #$channelName =~ tr/ \t//d;
- if ($setChannelName eq "?") {
- $channelName = "none";
- if ($channelType == 1 && defined $EnO_gpValueData{$signalType}{name}) {
- $channelName = $EnO_gpValueData{$signalType}{name};
- } elsif ($channelType == 2 && defined $EnO_gpValueFlag{$signalType}{name}) {
- $channelName = $EnO_gpValueFlag{$signalType}{name};
- } elsif ($channelType == 3 && defined $EnO_gpValueEnum{$signalType}{name}) {
- $channelName = $EnO_gpValueEnum{$signalType}{name};
- }
- } else {
- $channelName = $setChannelName;
- }
- $resolution = '' if (!defined $resolution);
- $engMin = '' if (!defined $engMin);
- $scalingMin = '' if (!defined $scalingMin);
- $engMax = '' if (!defined $engMax);
- $scalingMax = '' if (!defined $scalingMax);
- $gpDef[$setChannel] = $channelName . ':' . $channelDir . ':' . $channelType . ':' . $signalType . ':' . $valueType .
- ':' . $resolution . ':' . $engMin . ':' . $scalingMin . ':' . $engMax . ':' . $scalingMax;
- $attr{$name}{gpDef} = join(' ', @gpDef);
- shift @a;
- $updateState = 3;
- $channelType = 255;
- EnOcean_CommandSave(undef, undef);
- } else {
- return "Wrong parameter, channel $setChannel not defined.";
- }
- } elsif ($cmd eq "teachIn" && $devMode eq "slave") {
- # teach-in generic profile
- $rorg = "B0";
- my ($gpDefO, $gpDefI, $formatPattern, $teachInInfo);
- my $channelDirSeq = "--";
- my $comMode = 0;
- $attr{$name}{comMode} = "uniDir";
- $attr{$name}{eep} = "B0-00-00";
- # multicast teach-in
- $destinationID = "FFFFFFFF";
- my $productID = AttrVal($name, "productID", undef);
- if (defined $productID) {
- $productID = '0000000010' . EnOcean_convHexToBit($productID);
- } else {
- $productID = '';
- }
- while ($gpDef[$channel]) {
- ($channelName, $channelDir, $channelType, $signalType, $valueType, $resolution, $engMin, $scalingMin, $engMax, $scalingMax) =
- split(':', $gpDef[$channel]);
- if ($channelDir eq "O") {
- #Log3 $name, 3, "EnOcean set $name channel: $channel channelDir: $channelDir seq: $channelDirSeq";
- #Log3 $name, 3, "EnOcean set $name channel: $channel channelType: $channelType signalType: $signalType valueType: $valueType";
- return "Usage: attr $name gpDef: O/I sequence error" if $channelDirSeq =~ m/.I$/;
- $channelDirSeq = "O-";
- # add channel-, signal- and valuetype
- $gpDefO .= substr(unpack('B8', pack('C', $channelType)), 6) .
- unpack('B8', pack('C', $signalType)) .
- substr(unpack('B8', pack('C', $valueType)), 6);
- if ($channelType == 1 || $channelType == 3) {
- # data, enumeration: add resolution
- $gpDefO .= substr(unpack('B8', pack('C', $resolution)), 4);
- }
- if ($channelType == 1) {
- # data: add engineering and scaling
- $gpDefO .= unpack('B8', pack('c', $engMin)) .
- substr(unpack('B8', pack('C', $scalingMin)), 4) .
- unpack('B8', pack('c', $engMax)) .
- substr(unpack('B8', pack('C', $scalingMax)), 4);
- }
- } elsif ($channelDir eq "I") {
- #Log3 $name, 3, "EnOcean set $name channel: $channel channelDir: $channelDir seq: $channelDirSeq";
- return "Usage: attr $name gpDef: O/I sequence error" if $channelDirSeq !~ m/^O./;
- $channelDirSeq = "OI";
- $gpDefI .= substr(unpack('B8', pack('C', $channelType)), 6) .
- unpack('B8', pack('C', $signalType)) .
- substr(unpack('B8', pack('C', $valueType)), 6);
- if ($channelType == 1 || $channelType == 3) {
- # data, enumeration: add resolution
- $gpDefI .= substr(unpack('B8', pack('C', $resolution)), 4);
- }
- if ($channelType == 1) {
- # data: add engineering and scaling
- $gpDefI .= unpack('B8', pack('c', $engMin)) .
- substr(unpack('B8', pack('C', $scalingMin)), 4) .
- unpack('B8', pack('c', $engMax)) .
- substr(unpack('B8', pack('C', $scalingMax)), 4);
- }
- }
- $channel ++;
- }
- if ($channelDirSeq eq "OI") {
- # await teach-in response
- $comMode = 1;
- $attr{$name}{comMode} = "biDir";
- # set flag for response request
- $hash->{IODev}{helper}{gpRespWait}{AttrVal($name, "subDef", $hash->{DEF})}{teachInReq} = "in";
- $hash->{IODev}{helper}{gpRespWait}{AttrVal($name, "subDef", $hash->{DEF})}{hash} = $hash;
- if (!exists($hash->{IODev}{Teach})) {
- # enable teach-in receiving for 3 sec
- $hash->{IODev}{Teach} = 1;
- my %timeoutHash = (hash => $IOHash, function => "gpRespTimeout", helper => "gpRespWait");
- RemoveInternalTimer(\%timeoutHash);
- InternalTimer(gettimeofday() + 3, "EnOcean_RespTimeout", \%timeoutHash, 0);
- }
- }
- $header = (0x7FF << 1 | $comMode) << 4;
- if ($channelDirSeq =~ m/.I$/) {
- # create teach-in information
- if (length($gpDefI) % 8) {
- # fill with trailing zeroes to x bytes
- $gpDefI .= 0 x (8 - length($gpDefI) % 8);
- }
- $teachInInfo = '0000000001' . unpack('B8', pack('C', length($gpDefI) / 4));
- }
- #Log3 $name, 3, "EnOcean set $name header: $header O: $gpDefO Info: $teachInInfo I: $gpDefI";
- # DophinView GP profile error if Product ID sent
- $data = $productID . $gpDefO . $teachInInfo . $gpDefI;
- #$data = $gpDefO . $teachInInfo . $gpDefI;
- if (length($data) % 8) {
- # fill with trailing zeroes to x bytes
- $data .= 0 x (8 - length($data) % 8);
- }
- $channelType = 0;
- EnOcean_CommandSave(undef, undef);
- $data = sprintf '%04X%s', $header, EnOcean_convBitToHex($data);
- my $teachInState = $comMode == 1 ? "teach-in sent, response requested" : "teach-in sent";
- readingsSingleUpdate($hash, "teach", "GP $teachInState", 1);
- } elsif ($cmd eq "teachOut" && $devMode eq "slave") {
- # teach out generic profile
- $rorg = "B0";
- $channelType = 0;
- my $comMode = 0;
- if (AttrVal($name, "comMode", "uniDir") eq "biDir") {
- $comMode = 1;
- $hash->{IODev}{helper}{gpRespWait}{AttrVal($name, "subDef", $hash->{DEF})}{teachInReq} = "out";
- $hash->{IODev}{helper}{gpRespWait}{AttrVal($name, "subDef", $hash->{DEF})}{hash} = $hash;
- if (!exists($hash->{IODev}{Teach})) {
- # enable teach-in receiving for 3 sec
- $hash->{IODev}{Teach} = 1;
- my %timeoutHash = (hash => $IOHash, function => "gpRespTimeout", helper => "gpRespWait");
- RemoveInternalTimer(\%timeoutHash);
- InternalTimer(gettimeofday() + 3, "EnOcean_RespTimeout", \%timeoutHash, 0);
- }
- }
- $data = sprintf '%04X', (0x7FF << 1 | $comMode) << 4 | 4;
- my $teachInState = $comMode == 1 ? "teach-in deletion sent, response requested" : "teach-in deletion sent";
- readingsSingleUpdate($hash, "teach", "GP $teachInState", 1);
- } elsif ($setChannel =~ m/^\d+$/ && defined $gpDef[$setChannel]) {
- # send selective data (GPSD)
- $rorg = "B3";
- # select channel
- ($channelName, $channelDir, $channelType, $signalType, $valueType, $resolution, $engMin, $scalingMin, $engMax, $scalingMax) =
- split(':', $gpDef[$setChannel]);
- if ($channelName eq $setChannelName && $channelDir eq "O") {
- $channel = $setChannel;
- } else {
- return "Channel name wrong or no output channel";
- }
- } else {
- # command error
- my $channelCntr = 0;
- my @cmdList;
- while (defined $gpDef[$channelCntr]) {
- ($channelName, $channelDir, $channelType, $signalType, $valueType, $resolution, $engMin, $scalingMin, $engMax, $scalingMax) =
- split(':', $gpDef[$channelCntr]);
- if ($channelDir ne "O") {
- $channelCntr ++;
- next;
- }
- if ($channelType == 1) {
- # data
- push @cmdList, sprintf('%02d', $channelCntr) . "-" . $channelName;
- } elsif ($channelType == 2) {
- # flag
- push @cmdList, sprintf('%02d', $channelCntr) . "-" . $channelName . ':' .
- $EnO_gpValueFlag{$signalType}{flag}{0} . "," .
- $EnO_gpValueFlag{$signalType}{flag}{1};
- } elsif ($channelType == 3) {
- # enumeration
- my $cmdListEnum = "";
- my $enum = 0;
- while (defined $EnO_gpValueEnum{$signalType}{enum}{$enum}) {
- $cmdListEnum .= $EnO_gpValueEnum{$signalType}{enum}{$enum} . ",";
- $enum ++;
- }
- push @cmdList, sprintf('%02d', $channelCntr) . "-" . $channelName . ':' . substr($cmdListEnum, 0, -1);
- }
- $channelCntr ++;
- }
- if (defined $cmdList[0] && $devMode eq "slave") {
- return "Unknown argument $cmd, choose one of " . $cmdList . 'channelName teachIn:noArg teachOut:noArg ' . join(" ", @cmdList);
- } elsif (defined $cmdList[0] && $devMode eq "master") {
- return "Unknown argument $cmd, choose one of " . $cmdList . 'channelName ' . join(" ", @cmdList);
- } else {
- return "Unknown argument $cmd, choose one of " . $cmdList . 'channelName';
- }
- }
- #Log3 $name, 3, "EnOcean set $name header: $header channel: $channel";
- if ($channelType == 1) {
- # data
- if ($engMin * $EnO_scaling[$scalingMin] > $engMax * $EnO_scaling[$scalingMax]) {
- return "Usage: numerical value is missing" if (!defined $a[1]);
- if ($a[1] =~ m/^[-+]?\d*\.?\d+([eE][-+]?\d+)?$/ &&
- $a[1] >= $engMax * $EnO_scaling[$scalingMax] &&
- $a[1] <= $engMin * $EnO_scaling[$scalingMin]) {
- $data = int(2**$EnO_resolution[$resolution] * ($a[1] - $engMin * $EnO_scaling[$scalingMin]) /
- ($engMax * $EnO_scaling[$scalingMax] - $engMin * $EnO_scaling[$scalingMin]));
- #Log3 $name, 3, "EnOcean set $name header: $header channel: $channel resolution: " . $EnO_resolution[$resolution] . " data: $data";
- if ($data >= 2**$EnO_resolution[$resolution]) {
- $data = 2**$EnO_resolution[$resolution] - 1;
- #Log3 $name, 3, "EnOcean set $name header: $header channel: $channel resolution: " . $EnO_resolution[$resolution] . " data: $data";
- }
- shift @a;
- } else {
- return "Usage: $a[1] is not numeric or out of range";
- }
- } else {
- return "Usage: numerical value is missing" if (!defined $a[1]);
- if ($a[1] =~ m/^[-+]?\d*\.?\d+([eE][-+]?\d+)?$/ &&
- $a[1] >= $engMin * $EnO_scaling[$scalingMin] &&
- $a[1] <= $engMax * $EnO_scaling[$scalingMax]) {
- $data = int(2**$EnO_resolution[$resolution] * ($a[1] - $engMin * $EnO_scaling[$scalingMin]) /
- ($engMax * $EnO_scaling[$scalingMax] - $engMin * $EnO_scaling[$scalingMin]));
- #Log3 $name, 3, "EnOcean set $name header: $header channel: $channel resolution: " . $EnO_resolution[$resolution] . " data: $data";
- if ($data >= 2**$EnO_resolution[$resolution]) {
- $data = 2**$EnO_resolution[$resolution] - 1;
- #Log3 $name, 3, "EnOcean set $name header: $header channel: $channel resolution: " . $EnO_resolution[$resolution] . " data: $data";
- }
- shift @a;
- } else {
- return "Usage: $a[1] is not numeric or out of range";
- }
- }
- ($err, $logLevel, $response, $readingFormat, $readingName, $readingType, $readingUnit, $readingValue, $valueType) =
- EnOcean_gpConvDataToValue (undef, $hash, $channel, $data, $gpDef[$channel]);
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, $readingName, sprintf("$readingFormat", $readingValue));
- readingsBulkUpdate($hash, $readingName . "Unit", $readingUnit);
- readingsBulkUpdate($hash, $readingName . "ValueType", $valueType);
- readingsBulkUpdate($hash, $readingName . "ChannelType", $readingType);
- readingsEndUpdate($hash, 1);
- $data = EnOcean_gpConvSelDataToSndData($header, $channel, $EnO_resolution[$resolution], $data);
- } elsif ($channelType == 2) {
- # flag
- if (defined $EnO_gpValueFlag{$signalType}{flagInv}{$a[1]}) {
- $data = $EnO_gpValueFlag{$signalType}{flagInv}{$a[1]};
- #Log3 $name, 3, "EnOcean set $name header: $header channel: $channel data: " . $EnO_gpValueFlag{$signalType}{flagInv}{$a[1]};
- shift @a;
- } else {
- return "Usage: $a[1] is unknown";
- }
- #Log3 $name, 3, "EnOcean set $name header: $header channel: $channel data: $data";
- ($err, $logLevel, $response, $readingFormat, $readingName, $readingType, $readingUnit, $readingValue, $valueType)=
- EnOcean_gpConvDataToValue (undef, $hash, $channel, $data, $gpDef[$channel]);
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, $readingName, sprintf("$readingFormat", $readingValue));
- readingsBulkUpdate($hash, $readingName . "Unit", $readingUnit);
- readingsBulkUpdate($hash, $readingName . "ValueType", $valueType);
- readingsBulkUpdate($hash, $readingName . "ChannelType", $readingType);
- readingsEndUpdate($hash, 1);
- $data = sprintf '%04X', (($header << 6 | $channel) << 1 | $data) << 5;
- } elsif ($channelType == 3) {
- # enumeration
- if (defined $EnO_gpValueEnum{$signalType}{enumInv}{$a[1]}) {
- $data = $EnO_gpValueEnum{$signalType}{enumInv}{$a[1]};
- #Log3 $name, 3, "EnOcean set $name header: $header channel: $channel data: $data";
- shift @a;
- } else {
- return "Usage: $a[1] is unknown";
- }
- ($err, $logLevel, $response, $readingFormat, $readingName, $readingType, $readingUnit, $readingValue, $valueType)=
- EnOcean_gpConvDataToValue (undef, $hash, $channel, $data, $gpDef[$channel]);
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, $readingName, sprintf("$readingFormat", $readingValue));
- readingsBulkUpdate($hash, $readingName . "Unit", $readingUnit);
- readingsBulkUpdate($hash, $readingName . "ValueType", $valueType);
- readingsBulkUpdate($hash, $readingName . "ChannelType", $readingType);
- readingsEndUpdate($hash, 1);
- $data = EnOcean_gpConvSelDataToSndData($header, $channel, $EnO_resolution[$resolution], $data);
- }
- Log3 $name, 3, "EnOcean set $name $cmd";
- } elsif ($st eq "raw") {
- # sent raw data
- if ($cmd eq "4BS"){
- # 4BS Telegram
- if ($a[1] && $a[1] =~ m/^[\dA-Fa-f]{8}$/) {
- $data = uc($a[1]);
- $rorg = "A5";
- } else {
- return "Wrong parameter, choose 4BS <data 4 Byte hex> [status 1 Byte hex]";
- }
- } elsif ($cmd eq "1BS") {
- # 1BS Telegram
- if ($a[1] && $a[1] =~ m/^[\dA-Fa-f]{2}$/) {
- $data = uc($a[1]);
- $rorg = "D5";
- } else {
- return "Wrong parameter, choose 1BS <data 1 Byte hex> [status 1 Byte hex]";
- }
- } elsif ($cmd eq "RPS") {
- # RPS Telegram
- if ($a[1] && $a[1] =~ m/^[\dA-Fa-f]{2}$/) {
- $data = uc($a[1]);
- $rorg = "F6";
- } else {
- return "Wrong parameter, choose RPS <data 1 Byte hex> [status 1 Byte hex]";
- }
- } elsif ($cmd eq "MSC") {
- # MSC Telegram
- if ($a[1] && $a[1] =~ m/^[\dA-Fa-f]{2,28}$/ && !(length($a[1]) % 2)) {
- $data = uc($a[1]);
- $rorg = "D1";
- } else {
- return "Wrong parameter, choose MSC <data 1 ... 14 Byte hex> [status 1 Byte hex]";
- }
- } elsif ($cmd eq "UTE") {
- # UTE Telegram
- if ($a[1] && $a[1] =~ m/^[\dA-Fa-f]{14}$/) {
- $data = uc($a[1]);
- $rorg = "D4";
- } else {
- return "Wrong parameter, choose UTE <data 7 Byte hex> [status 1 Byte hex]";
- }
- } elsif ($cmd eq "VLD") {
- # VLD Telegram
- if ($a[1] && $a[1] =~ m/^[\dA-Fa-f]{2,28}$/ && !(length($a[1]) % 2)) {
- $data = uc($a[1]);
- $rorg = "D2";
- } else {
- return "Wrong parameter, choose VLD <data 1 ... 14 Byte hex> [status 1 Byte hex]";
- }
- } elsif ($cmd eq "GPTI") {
- # GP teach-in request telegram
- if ($a[1] && $a[1] =~ m/^[\dA-Fa-f]{2,1024}$/ && !(length($a[1]) % 2)) {
- $data = uc($a[1]);
- $rorg = "B0";
- } else {
- return "Wrong parameter, choose GPTI <data 1 ... 512 Byte hex> [status 1 Byte hex]";
- }
- } elsif ($cmd eq "GPTR") {
- # GP teach-in response telegram
- if ($a[1] && $a[1] =~ m/^[\dA-Fa-f]{2,1024}$/ && !(length($a[1]) % 2)) {
- $data = uc($a[1]);
- $rorg = "B1";
- } else {
- return "Wrong parameter, choose GPTR <data 1 ... 512 Byte hex> [status 1 Byte hex]";
- }
- } elsif ($cmd eq "GPCD") {
- # GP complete date telegram
- if ($a[1] && $a[1] =~ m/^[\dA-Fa-f]{2,1024}$/ && !(length($a[1]) % 2)) {
- $data = uc($a[1]);
- $rorg = "B2";
- } else {
- return "Wrong parameter, choose GPCD <data 1 ... 512 Byte hex> [status 1 Byte hex]";
- }
- } elsif ($cmd eq "GPSD") {
- # GP selective data telegram
- if ($a[1] && $a[1] =~ m/^[\dA-Fa-f]{2,1024}$/ && !(length($a[1]) % 2)) {
- $data = uc($a[1]);
- $rorg = "B3";
- } else {
- return "Wrong parameter, choose GPSD <data 1 ... 512 Byte hex> [status 1 Byte hex]";
- }
- } elsif ($cmd eq "SEC") {
- # secure telegram
- if ($a[1] && $a[1] =~ m/^[\dA-Fa-f]{2,28}$/ && !(length($a[1]) % 2)) {
- $data = uc($a[1]);
- $rorg = "30";
- } else {
- return "Wrong parameter, choose SEC <data 1 ... 14 Byte hex> [status 1 Byte hex]";
- }
- } elsif ($cmd eq "ENC") {
- # secure telegram with encapsulation
- if ($a[1] && $a[1] =~ m/^[\dA-Fa-f]{2,28}$/ && !(length($a[1]) % 2)) {
- $data = uc($a[1]);
- $rorg = "31";
- } else {
- return "Wrong parameter, choose ENC <data 1 ... 14 Byte hex> [status 1 Byte hex]";
- }
- } elsif ($cmd eq "STE") {
- # secure Teach-In
- if ($a[1] && $a[1] =~ m/^[\dA-Fa-f]{2,28}$/ && !(length($a[1]) % 2)) {
- $data = uc($a[1]);
- $rorg = "35";
- } else {
- return "Wrong parameter, choose STE <data 1 ... 14 Byte hex> [status 1 Byte hex]";
- }
- } else {
- return "Unknown argument $cmd, choose one of 1BS 4BS ENC GPCD GPSD GPTI GPTR MSC RPS SEC STE UTE VLD";
- }
- if ($a[2]) {
- if ($a[2] !~ m/^[\dA-Fa-f]{2}$/) {
- return "Wrong status parameter, choose $cmd $a[1] [status 1 Byte hex]";
- }
- $status = uc($a[2]);
- splice(@a,2,1);
- }
- $updateState = 0;
- readingsSingleUpdate($hash, "RORG", $cmd, 1);
- readingsSingleUpdate($hash, "dataSent", $data, 1);
- readingsSingleUpdate($hash, "statusSent", $status, 1);
- Log3 $name, 3, "EnOcean set $name $cmd $data $status";
- shift(@a);
- } elsif ($st eq "remote") {
- return "Unknown argument $cmd, choose one of $cmdList";
- } else {
- # subtype does not support set commands
- $updateState = -1;
- if (AttrVal($name, "remoteManagement", "off") eq "manager") {
- return "Unknown argument $cmd, choose one of $cmdList";
- } else {
- return;
- }
- }
- # set reading state if confirmation telegram is not expected
- if($updateState == 1 && $updateStateAttr =~ m/^default|yes$/) {
- readingsSingleUpdate($hash, "state", $cmd, 1);
- } elsif ($updateState == 0 && $updateStateAttr eq "yes") {
- readingsSingleUpdate($hash, "state", $cmd, 1);
- #} elsif ($updateState == 3) {
- #internal command
- #} else {
- # readingsSingleUpdate($hash, ".info", "await_confirm", 0);
- }
- # send commands
- if($updateState >= -1 && $updateState <= 1) {
- EnOcean_SndCdm(undef, $hash, $packetType, $rorg, $data, $subDef, $status, $destinationID);
- EnOcean_observeInit(1, $hash, @cmdObserve);
- if ($switchMode eq "pushbutton" && $cmd1 ne "released") {
- my @timerCmd = ($name, "released");
- my %par = (hash => $hash, timerCmd => \@timerCmd);
- InternalTimer(gettimeofday() + 0.1, "EnOcean_TimerSet", \%par, 0);
- }
- }
- #Log3 $name, 3, "EnOcean set $name remainder of CmdArg: " . join(' ' ,@a) if (@a > 0);
- }
- return undef;
- }
- # parse and display the incoming telegrams
- sub EnOcean_Parse($$)
- {
- my ($iohash, $msg) = @_;
- my $IODev = $iohash->{NAME};
- my ($hash, $name, $filelogName, $rorgname);
- my ($ctrl, $err, $logLevel, $response);
- Log3 $IODev, 4, "EnOcean received via $IODev: $msg";
- my @msg = split(':', $msg);
- my ($rorg, $data, $senderID, $status, $odata, $subDef, $destinationID, $funcNumber, $manufID, $RSSI, $delay, $subTelNum);
- my $packetType = hex($msg[1]);
- my @event;
- if ($packetType == 1) {
- # packet type RADIO
- #EnOcean:PacketType:RORG:MessageData:SourceID:Status:OptionalData
- (undef, undef, $rorg, $data, $senderID, $status, $odata) = @msg;
- $odata =~ m/^(..)(........)(..)(..)$/;
- ($subTelNum, $destinationID, $RSSI) = (hex($1), $2, hex($3));
- if (exists $modules{EnOcean}{defptr}{$senderID}) {
- $hash = $modules{EnOcean}{defptr}{$senderID};
- } elsif ($destinationID ne 'FFFFFFFF') {
- $hash = $modules{EnOcean}{defptr}{$destinationID};
- }
- $rorgname = $EnO_rorgname{$rorg};
- if (!$rorgname) {
- if($hash) {
- Log3 $hash->{NAME}, 2, "EnOcean $hash->{NAME} RORG $rorg unknown.";
- } else {
- Log3 undef, 2, "EnOcean RORG $rorg received from $senderID unknown.";
- }
- return "";
- }
- if ($rorg eq "40") {
- # chained data message (CDM)
- $data =~ m/^(..)(.*)$/;
- # SEQ evaluation?
- my ($seq, $idx) = (hex($1) & 0xC0, hex($1) & 0x3F);
- $data = $2;
- if ($idx == 0) {
- # first message part
- delete $iohash->{helper}{cdm};
- $data =~ m/^(....)(..)(.*)$/;
- $iohash->{helper}{cdm}{len} = hex($1);
- $iohash->{helper}{cdm}{rorg} = $2;
- $iohash->{helper}{cdm}{data}{$idx} = $3;
- $iohash->{helper}{cdm}{lenCounter} = length($3) / 2;
- my %functionHash = (hash => $iohash, function => "cdm");
- RemoveInternalTimer(\%functionHash);
- InternalTimer(gettimeofday() + 1, "EnOcean_helperClear", \%functionHash, 0);
- #Log3 $IODev, 3, "EnOcean $IODev CDM timer started";
- } else {
- $iohash->{helper}{cdm}{data}{$idx} = $data;
- $iohash->{helper}{cdm}{lenCounter} += length($data) / 2;
- }
- if ($iohash->{helper}{cdm}{lenCounter} >= $iohash->{helper}{cdm}{len}) {
- # data message complete
- # reconstruct RORG, DATA
- my ($idx, $dataPart, @data);
- while (($idx, $dataPart) = each(%{$iohash->{helper}{cdm}{data}})) {
- $data[$idx] = $iohash->{helper}{cdm}{data}{$idx};
- }
- $data = join('', @data);
- $msg[3] = $data;
- $rorg = $iohash->{helper}{cdm}{rorg};
- $msg[2] = $rorg;
- $msg = join(':', @msg);
- $rorgname = $EnO_rorgname{$rorg};
- if (!$rorgname) {
- Log3 undef, 2, "EnOcean RORG $rorg received from $senderID unknown.";
- return "";
- }
- delete $iohash->{helper}{cdm};
- my %functionHash = (hash => $iohash, function => "cdm");
- RemoveInternalTimer(\%functionHash);
- #Log3 $IODev, 3, "EnOcean $IODev CDM concatenated DATA $data";
- } else {
- # wait for next data message part
- return $IODev;
- }
- }
- if($hash) {
- $name = $hash->{NAME};
- if ($rorg eq 'A5' && !(hex(substr($data, 6, 2)) & 8) &&
- hex(substr($data, 0, 2)) >> 2 == 0x3F &&
- (hex(substr($data, 0, 4)) >> 3 & 0x7F) == 0) {
- # find Radio Link Test device
- my $rltHash;
- foreach my $dev (keys %defs) {
- next if ($defs{$dev}{TYPE} ne 'EnOcean');
- next if (!exists($attr{$dev}{subType}));
- next if ($attr{$dev}{subType} ne 'radioLinkTest');
- $rltHash = $defs{$dev};
- last;
- }
- if (defined $rltHash) {
- if ($rltHash != $hash) {
- if (ReadingsVal($rltHash->{NAME}, 'state', '') =~ m/^standby$/) {
- # device is temporarily subType radioLinkTest
- @{$rltHash->{helper}{rlt}{oldDev}} = ($hash, $name, $hash->{DEF});
- CommandModify(undef, "$name 00000000");
- delete $modules{EnOcean}{defptr}{$hash->{DEF}};
- $hash = $rltHash;
- $name = $hash->{NAME};
- CommandModify(undef, "$name $senderID");
- $modules{EnOcean}{defptr}{$senderID} = $hash;
- } else {
- # Radio Link Test Devices not ready at the moment
- return '';
- }
- }
- Log3 $name, 4, "EnOcean received RLT Query messsage DATA: $data from DeviceID: $senderID";
- } else {
- Log3 $name, 4, "EnOcean received RLT Query messsage DATA: $data from DeviceID: $senderID";
- }
- } else {
- Log3 $name, 4, "EnOcean $name received PacketType: $packetType RORG: $rorg DATA: $data SenderID: $senderID STATUS: $status";
- }
- $manufID = uc(AttrVal($name, "manufID", ""));
- $subDef = uc(AttrVal($name, "subDef", $hash->{DEF}));
- $filelogName = "FileLog_$name";
- #if ($IODev ne $hash->{IODev}{NAME}) {
- # transceiver wrong
- # Log3 $name, 4, "EnOcean $name locked telegram via $IODev PacketType: $packetType RORG: $rorg DATA: $data SenderID: $senderID STATUS: $status";
- # return "";
- #}
- } else {
- # SenderID unknown, created new device
- Log3 undef, 5, "EnOcean received PacketType: $packetType RORG: $rorg DATA: $data SenderID: $senderID STATUS: $status";
- my $learningMode = AttrVal($IODev, "learningMode", "demand");
- my $ret = "UNDEFINED EnO_$senderID EnOcean $senderID $msg";
- if ($rorgname =~ m/^GPCD|GPSD|SMLRNANS|SMREC|SMMBOX$/) {
- Log3 undef, 4, "EnOcean Received $rorgname telegram to the unknown device with SenderID $senderID.";
- return '';
- } elsif ($rorg eq 'A5' &&
- hex(substr($data, 0, 2)) >> 2 == 0x3F &&
- (hex(substr($data, 0, 4)) >> 3 & 0x7F) == 0 &&
- !(hex(substr($data, 6, 2)) & 8)) {
- # find Radio Link Test device
- my $rltHash;
- foreach my $dev (keys %defs) {
- next if ($defs{$dev}{TYPE} ne 'EnOcean');
- next if (!exists($attr{$dev}{subType}));
- next if ($attr{$dev}{subType} ne 'radioLinkTest');
- $rltHash = $defs{$dev};
- last;
- }
- if ($rltHash) {
- return '' if (ReadingsVal($rltHash->{NAME}, 'state', '') !~ m/^standby$/);
- $hash = $rltHash;
- $name = $hash->{NAME};
- CommandModify(undef, "$name $senderID");
- $modules{EnOcean}{defptr}{$senderID} = $hash;
- $filelogName = "FileLog_$name";
- Log3 $name, 4, "EnOcean RLT Query messsage DATA: $data from SenderID: $senderID received";
- $manufID = uc(AttrVal($name, "manufID", ""));
- $subDef = uc(AttrVal($name, "subDef", $hash->{DEF}));
- } else {
- Log3 undef, 1, "EnOcean Unknown device with SenderID $senderID and RLT Query message, please define it.";
- return $ret;
- }
- } elsif (exists $iohash->{helper}{teachConfirmWaitHash}) {
- # teach-in response with confirm telegram, assign remote device
- $hash = $iohash->{helper}{teachConfirmWaitHash};
- $name = $hash->{NAME};
- # substitute subDef with DEF
- delete $modules{EnOcean}{defptr}{$hash->{DEF}};
- $modules{EnOcean}{defptr}{$senderID} = $hash;
- $attr{$name}{subDef} = $hash->{DEF};
- $subDef = $attr{$name}{subDef};
- $hash->{DEF} = $senderID;
- $attr{$name}{comMode} = "confirm";
- $manufID = uc(AttrVal($name, "manufID", ""));
- $filelogName = "FileLog_$name";
- # clear teach-in request
- delete $iohash->{helper}{teachConfirmWaitHash};
- # store changes
- EnOcean_CommandSave(undef, undef);
- push @event, "3:teach:4BS teach-in accepted";
- Log3 $name, 2, "EnOcean $name remote device with SenderID $senderID assigned";
- } elsif ($learningMode eq "demand" && $iohash->{Teach}) {
- Log3 undef, 1, "EnOcean Unknown device with SenderID $senderID and $rorgname telegram, please define it.";
- return $ret;
- } elsif ($learningMode eq "nearfield" && $iohash->{Teach} && $RSSI <= 60) {
- Log3 undef, 1, "EnOcean Unknown device with SenderID $senderID and $rorgname telegram, please define it.";
- return $ret;
- } elsif ($learningMode eq "always") {
- if ($rorgname =~ m/^UTE|GPTI|GPTR$/) {
- if ($iohash->{Teach}) {
- Log3 undef, 1, "EnOcean Unknown device with SenderID $senderID and $rorgname telegram, please define it.";
- return $ret;
- } else {
- Log3 undef, 1, "EnOcean Unknown device with SenderID $senderID and $rorgname telegram, activate learning mode.";
- return "";
- }
- } elsif ($rorgname =~ m/^SMLRNREQ$/) {
- if ($iohash->{SmartAckLearn}) {
- Log3 undef, 1, "EnOcean Unknown device with SenderID $senderID and $rorgname telegram, please define it.";
- return $ret;
- } else {
- Log3 undef, 1, "EnOcean Unknown device with SenderID $senderID and $rorgname telegram, activate learning mode.";
- return "";
- }
- } else {
- Log3 undef, 1, "EnOcean Unknown device with SenderID $senderID and $rorgname telegram, please define it.";
- return $ret;
- }
- } else {
- Log3 undef, 4, "EnOcean Unknown device with SenderID $senderID and $rorgname telegram, activate learning mode.";
- return "";
- }
- }
- } elsif ($packetType == 2) {
- # packet type RESPONSE
- #EnOcean:PacketType:ResposeCode:MessageData:OptionalData
- (undef, undef, $funcNumber, $data, $odata) = @msg;
- $data = defined($data) ? $data : '';
- $odata = defined($odata) ? $odata : '';
- my %codes = (
- "00" => "OK",
- "01" => "ERROR",
- "02" => "NOT_SUPPORTED",
- "03" => "WRONG_PARAM",
- "04" => "OPERATION_DENIED",
- "82" => "FLASH_HW_ERROR",
- "90" => "BASEID_OUT_OF_RANGE",
- "91" => "BASEID_MAX_REACHED",
- );
- my $rcTxt = $codes{$funcNumber} if($codes{$funcNumber});
- if($hash) {
- $name = $hash->{NAME};
- $funcNumber = hex($funcNumber);
- Log3 $name, $funcNumber == 0 ? 5 : 2, "EnOcean $name RESPONSE: $rcTxt DATA: $data ODATA: $odata";
- return $name;
- } else {
- Log3 undef, $funcNumber == 0 ? 5 : 2, "EnOcean RESPONSE: $rcTxt DATA: $data ODATA: $odata";
- return "";
- }
- } elsif ($packetType == 4) {
- # packet type EVENT
- #EnOcean:PacketType:EventCode:MessageData
- (undef, undef, $funcNumber, $data) = @msg;
- Log3 undef, 5, "EnOcean EventCode: $funcNumber DATA: $data";
- $funcNumber = hex($funcNumber);
- if ($funcNumber == 2) {
- # smart Ack confirm learn
- my ($priority, $rorg, $func, $type, $postmasterID, $hopCount);
- my $responseTime = 150;
- my $sendData = '';
- my $sendHash = defined($hash) ? $hash : $iohash;
- my $sendName = $sendHash->{NAME};
- $data =~ m/^(..)(....)(..)(..)(..)(..)(........)(........)(..)$/;
- ($priority, $manufID, $rorg, $func, $type, $RSSI, $postmasterID, $senderID, $hopCount) = (hex($1), $2, $3, $4, $5, hex($6), $7, $8, $9);
- #Log3 undef, 2, "EnOcean IOHASH: $iohash PRIORITY: $priority SmartAckLearn: " . (exists($iohash->{SmartAckLearn}) ? 1 : 0) .
- # " SmartAckLearnWait: " . (exists($iohash->{helper}{smartAckLearnWait}) ? $iohash->{helper}{smartAckLearnWait} : '');
- if ($iohash->{SmartAckLearn} ||
- exists($iohash->{helper}{smartAckLearnWait}) && $iohash->{helper}{smartAckLearnWait} eq $sendName) {
- my $subType = "$rorg.$func.$type";
- if (exists $EnO_eepConfig{$subType}) {
- # EEP supported
- if (exists $modules{EnOcean}{defptr}{$senderID}) {
- $hash = $modules{EnOcean}{defptr}{$senderID};
- }
- $rorgname = $EnO_rorgname{$rorg};
- if($hash) {
- delete $iohash->{helper}{smartAckLearnWait};
- $name = $hash->{NAME};
- $subDef = uc(AttrVal($name, "subDef", $hash->{DEF}));
- if (($priority & 15) == 15) {
- # device exists, learn OUT
- # send response, to delete mailbox
- $rorg = substr(AttrVal($name, "eep", ' '), 0, 2);
- $sendData = sprintf "00%04X20", $responseTime;
- EnOcean_SndRadio(undef, $hash, 2, $rorg, $sendData, $subDef, '00', $hash->{DEF});
- Log3 $name, 2, "EnOcean $name Smart Ack teach-out send";
- Log3 $name, 2, "EnOcean $name device $name deleted";
- CommandDelete(undef, $name);
- EnOcean_CommandSave(undef, undef);
- return '';
- } else {
- # config device
- Log3 $name, 5, "EnOcean $name received PacketType: $packetType EventCode: $funcNumber DATA: $data";
- $attr{$name}{subType} = $EnO_eepConfig{$subType}{attr}{subType};
- $attr{$name}{eep} = "$rorg-$func-$type";
- $manufID = sprintf "%03X", hex($manufID) & 0x7FF;
- $attr{$name}{manufID} = $manufID;
- $manufID = $EnO_manuf{$manufID} if(exists $EnO_manuf{$manufID});
- $attr{$name}{postmasterID} = $postmasterID;
- $hash->{SmartAckRSSI} = $RSSI;
- $attr{$name}{teachMethod} = 'smartAck';
- foreach my $attrCntr (keys %{$EnO_eepConfig{$subType}{attr}}) {
- if ($attrCntr ne "subDef") {
- $attr{$name}{$attrCntr} = $EnO_eepConfig{$subType}{attr}{$attrCntr};
- }
- }
- #if (defined AttrVal($name, 'subDef', undef)) {
- # $subDef = $attr{$name}{subDef};
- #} else {
- #$subDef = $postmasterID;
- #$attr{$name}{subDef} = $postmasterID;
- #}
- $subDef = '0' x 8;
- $attr{$name}{subDef} = '0' x 8;
- # sent response to create mailbox
- $sendData = sprintf "00%04X00", $responseTime;
- EnOcean_SndRadio(undef, $hash, 2, $rorg, $sendData, $subDef, '00', $hash->{DEF});
- readingsSingleUpdate($hash, "teach", "Smart Ack teach-in accepted EEP $rorg-$func-$type Manufacturer: $manufID", 1);
- Log3 $name, 2, "EnOcean $name Smart Ack teach-in accepted EEP $rorg-$func-$type Manufacturer: $manufID";
- EnOcean_CommandSave(undef, undef);
- return $name;
- }
- } else {
- # device unknown
- if (($priority & 5) == 5) {
- # Smart Ack priority ok (place for mailbox, good RSSI, Local)
- Log3 undef, 1, "EnOcean Unknown device with SenderID $senderID and Smart Ack learn In message, please define it.";
- return "UNDEFINED EnO_$senderID EnOcean $senderID $msg";
- } elsif (($priority & 5) == 1) {
- # Smart Ack no place for mailbox
- $sendData = sprintf "00%04X12", $responseTime;
- EnOcean_SndRadio(undef, $sendHash, 2, $rorg, $sendData, $postmasterID, '00', $senderID);
- Log3 $sendName, 2, "EnOcean $sendName Smart Ack learn in from SenderID $senderID Discard learn in, postmaster has no place for further mailbox";
- return '';
- } else {
- # Smart Ack priority to low
- $sendData = sprintf "00%04X13", $responseTime;
- EnOcean_SndRadio(undef, $sendHash, 2, $rorg, $sendData, $postmasterID, '00', $senderID);
- Log3 $sendName, 2, "EnOcean $sendName Smart Ack learn in from SenderID $senderID Discard learn in, priority to low";
- return '';
- }
- }
- } else {
- # EEP not supported
- # sent response
- $sendData = sprintf "00%04X11", $responseTime;
- EnOcean_SndRadio(undef, $sendHash, 2, $rorg, $sendData, $postmasterID, '00', $senderID);
- Log3 $sendName, 2, "EnOcean $sendName Smart Ack learn in from SenderID $senderID with EEP $rorg-$func-$type not supported";
- return '';
- }
- } else {
- # smart ack learn not activated
- $sendData = sprintf "00%04XFF", $responseTime;
- EnOcean_SndRadio(undef, $sendHash, 2, $rorg, $sendData, $postmasterID, '00', $senderID);
- Log3 $sendName, 2, "EnOcean $sendName Smart Ack learn in from SenderID $senderID received, activate learning";
- return '';
- }
- }
- } elsif ($packetType == 6) {
- # packet type SMART ACK
- #EnOcean:PacketType:SmartAckCode:MessageData
- (undef, undef, $funcNumber, $data) = @msg;
- if($hash) {
- $name = $hash->{NAME};
- $subDef = uc(AttrVal($name, "subDef", $hash->{DEF}));
- Log3 $name, 2, "EnOcean $name received PacketType: $packetType Function Number: $funcNumber DATA: $data";
- } else {
- Log3 undef, 2, "EnOcean received PacketType: $packetType Function Number: $funcNumber DATA: $data";
- return "";
- }
- $funcNumber = hex($funcNumber);
- } elsif ($packetType == 7) {
- # packet type REMOTE_MAN_COMMAND
- #EnOcean:PacketType:RORG:MessageData:SourceID:DestinationID:FunctionNumber:ManufacturerID:RSSI:Delay
- (undef, undef, $rorg, $data, $senderID, $destinationID, $funcNumber, $manufID, $RSSI, $delay) = @msg;
- if (exists $modules{EnOcean}{defptr}{$senderID}) {
- $hash = $modules{EnOcean}{defptr}{$senderID};
- $name = $hash->{NAME};
- if (!exists $attr{$name}{remoteID}) {
- $attr{$name}{remoteID} = $senderID;
- Log3 $name, 2, "EnOcean $name remoteID $senderID assigned";
- if (exists($iohash->{helper}{remoteAnswerWait}{hex($funcNumber)}{hash}) &&
- $iohash->{helper}{remoteAnswerWait}{hex($funcNumber)}{hash} == $hash) {
- delete $iohash->{helper}{remoteAnswerWait}{hex($funcNumber)}{hash};
- }
- }
- } elsif (exists($iohash->{helper}{remoteAnswerWait}{hex($funcNumber)}{hash})) {
- # the remoteID is assigned to the requesting device
- $hash = $iohash->{helper}{remoteAnswerWait}{hex($funcNumber)}{hash};
- $name = $hash->{NAME};
- $subDef = '0' x 8;
- $attr{$name}{remoteID} = $senderID;
- $modules{EnOcean}{defptr}{$senderID} = $hash;
- delete $iohash->{helper}{remoteAnswerWait}{hex($funcNumber)}{hash};
- Log3 $name, 2, "EnOcean $name remoteID $senderID assigned";
- #EnOcean_CommandSave(undef, undef);
- } elsif ($destinationID ne 'FFFFFFFF') {
- $hash = $modules{EnOcean}{defptr}{$destinationID};
- }
- #$funcNumber = substr($funcNumber, 1);
- if($hash) {
- $name = $hash->{NAME};
- $manufID = substr($manufID, 1);
- $rorgname = $EnO_rorgname{$rorg};
- $subDef = '0' x 8;
- Log3 $name, 4, "EnOcean $name received PacketType: $packetType RORG: $rorg DATA: $data SenderID: $senderID
- DestinationID $destinationID Function Number: " . substr($funcNumber, 1) . " ManufacturerID: $manufID";
- $delay = hex($delay);
- $funcNumber = hex($funcNumber);
- $RSSI = hex($RSSI);
- } else {
- if (hex($funcNumber) == 4) {
- #Log3 undef, 1, "EnOcean received remote management query ID from unknown SenderID $senderID, please define device.";
- #return "UNDEFINED EnO_$senderID EnOcean $senderID $msg";
- return '';
- } else {
- return '';
- }
- }
- }
- my $eep = AttrVal($name, "eep", undef);
- my $smartAckLearn = $hash->{IODev}{SmartAckLearn};
- my $teach = $hash->{IODev}{Teach};
- my ($deleteDevice, $oldDevice);
- if (AttrVal($name, "secLevel", "off") =~ m/^encapsulation|encryption$/ &&
- AttrVal($name, "secMode", "") =~ m/^rcv|biDir$/) {
- if ($rorg eq "30" || $rorg eq "31") {
- Log3 $name, 5, "EnOcean $name secure data RORG: $rorg DATA: $data SenderID: $senderID STATUS: $status";
- ($err, $rorg, $data) = EnOcean_sec_convertToNonsecure($hash, $rorg, $data);
- if (defined $err) {
- Log3 $name, 2, "EnOcean $name security ERROR: $err";
- return "";
- }
- } elsif ($rorg eq "35") {
- # pass second teach-in telegram
- } else {
- Log3 $name, 2, "EnOcean $name unsecure telegram locked";
- return "";
- }
- if ($rorg eq "32") {
- if (defined $eep) {
- # reconstruct RORG
- $rorg = substr($eep, 0, 2);
- Log3 $name, 5, "EnOcean $name decrypted data RORG: 32 >> $rorg DATA: $data SenderID: $senderID STATUS: $status";
- } else {
- # Teach-In telegram expected
- #####
- # telegram analyse needed >> 1BS, 4BS, UTE
- if (length($data) == 14) {
- $rorg = "D4";
- } else {
- Log3 $name, 2, "EnOcean $name security teach-in failed, UTE message is missing";
- return "";
- }
- }
- }
- }
- if ($rorg eq "A6") {
- # addressing destination telegram (ADT)
- # reconstruct RORG, DATA
- $data =~ m/^(..)(.*)(........)$/;
- ($rorg, $data) = ($1, $2);
- $rorgname = $EnO_rorgname{$rorg};
- if (!$rorgname) {
- Log3 undef, 1, "EnOcean RORG $rorg received from $senderID unknown.";
- return "";
- }
- if ($destinationID ne $3) {
- Log3 $name, 1, "EnOcean $name ADT DestinationID wrong.";
- return "";
- }
- Log3 $name, 1, "EnOcean $name ADT decapsulation RORG: $rorg DATA: $data DestinationID: $3";
- }
- # compute data
- # extract data bytes $db[x] ... $db[0]
- my @db;
- my $dbCntr = 0;
- for (my $strCntr = length($data) / 2 - 1; $strCntr >= 0; $strCntr --) {
- $db[$dbCntr] = hex substr($data, $strCntr * 2, 2);
- $dbCntr ++;
- }
- my $model = AttrVal($name, "model", "");
- my $st = AttrVal($name, "subType", "");
- my $subtypeReading = AttrVal($name, "subTypeReading", undef);
- $st = $subtypeReading if (defined $subtypeReading);
- if ($rorg eq "F6") {
- # RPS Telegram
- my $event = "state";
- my $nu = (hex($status) & 0x10) >> 4;
- # unused flags (AFAIK)
- #push @event, "1:T21:".((hex($status) & 0x20) >> 5);
- #push @event, "1:NU:$nu";
- if ($st eq "FRW" || $st eq "smokeDetector.02") {
- # smoke detector
- if (!exists($hash->{helper}{lastEvent}) || $hash->{helper}{lastEvent} != $db[0] || ReadingsVal($name, 'alarm', '') eq 'dead_sensor') {
- if ($db[0] == 0x30) {
- push @event, "3:alarm:off";
- push @event, "3:battery:low";
- $msg = ReadingsVal($name, 'state', 'off');
- } elsif ($db[0] == 0x10) {
- push @event, "3:battery:ok";
- push @event, "3:alarm:smoke-alarm";
- $msg = "smoke-alarm";
- } elsif ($db[0] == 0) {
- push @event, "3:alarm:off";
- push @event, "3:battery:ok";
- $msg = "off";
- }
- push @event, "3:$event:$msg";
- $hash->{helper}{lastEvent} = $db[0];
- }
- @{$hash->{helper}{alarmTimer}} = ($hash, 'alarm', 'dead_sensor', 1, 5);
- RemoveInternalTimer($hash->{helper}{alarmTimer});
- InternalTimer(gettimeofday() + 1320, 'EnOcean_readingsSingleUpdate', $hash->{helper}{alarmTimer}, 0);
- } elsif ($st eq "windSpeed.00") {
- # wind speed threshold detector
- if (!exists($hash->{helper}{lastEvent}) || $hash->{helper}{lastEvent} != $db[0] || ReadingsVal($name, 'alarm', '') eq 'dead_sensor') {
- push @event, "3:alarm:off";
- if ($db[0] == 0x30) {
- push @event, "3:battery:low";
- $msg = ReadingsVal($name, 'state', 'off');
- } elsif ($db[0] == 0x10) {
- push @event, "3:windSpeed:on";
- push @event, "3:battery:ok";
- $msg = "on";
- } elsif ($db[0] == 0) {
- push @event, "3:windSpeed:off";
- push @event, "3:battery:ok";
- $msg = "off";
- }
- push @event, "3:$event:$msg";
- $hash->{helper}{lastEvent} = $db[0];
- }
- @{$hash->{helper}{alarmTimer}} = ($hash, 'alarm', 'dead_sensor', 1, 5);
- RemoveInternalTimer($hash->{helper}{alarmTimer});
- InternalTimer(gettimeofday() + 1320, 'EnOcean_readingsSingleUpdate', $hash->{helper}{alarmTimer}, 0);
- } elsif ($model =~ m/FAE14|FHK14|FHK61$/) {
- # heating/cooling relay FAE14, FHK14, untested
- $event = "controllerMode";
- if ($db[0] == 0x30) {
- # night reduction 2 K
- push @event, "3:energyHoldOff:holdoff";
- $msg = "auto";
- } elsif ($db[0] == 0x10) {
- # off
- push @event, "3:energyHoldOff:normal";
- $msg = "off";
- } elsif ($db[0] == 0x70) {
- # on
- push @event, "3:energyHoldOff:normal";
- $msg = "auto";
- } elsif ($db[0] == 0x50) {
- # night reduction 4 K
- push @event, "3:energyHoldOff:holdoff";
- $msg = "auto";
- }
- push @event, "3:$event:$msg";
- } elsif ($st eq "gateway") {
- # Eltako switching, dimming
- if ($db[0] == 0x70) {
- $msg = "on";
- } elsif ($db[0] == 0x50) {
- $msg = "off";
- } elsif ($db[0] == 0x30) {
- $event = 'alert';
- $msg = "on";
- } elsif ($db[0] == 0x10) {
- $event = 'alert';
- $msg = "off";
- }
- push @event, "3:$event:$msg";
- } elsif ($st eq "manufProfile" && $manufID eq "00D") {
- # Eltako shutter
- if ($db[0] == 0x70) {
- # open
- push @event, "3:endPosition:open_ack";
- $msg = "open_ack";
- } elsif ($db[0] == 0x50) {
- # closed
- push @event, "3:position:100";
- push @event, "3:anglePos:" . AttrVal($name, "angleMax", 90);
- push @event, "3:endPosition:closed";
- $msg = "closed";
- } elsif ($db[0] == 0) {
- # not reached or not available
- push @event, "3:endPosition:not_reached";
- $msg = "not_reached";
- } elsif ($db[0] == 1) {
- # up
- push @event, "3:endPosition:not_reached";
- $msg = "up";
- } elsif ($db[0] == 2) {
- # down
- push @event, "3:endPosition:not_reached";
- $msg = "down";
- }
- push @event, "3:$event:$msg";
- } elsif ($st eq "occupSensor.01" && $model eq "tracker") {
- # tracker
- push @event, "3:button:" . ($db[0] & 0x70 ? "pressed" : "released");
- } elsif ($st eq "switch.7F" && $manufID eq "00D") {
- $msg = $EnO_ptm200btn[($db[0] & 0xE0) >> 5];
- $msg .= "," . $EnO_ptm200btn[($db[0] & 0x0E) >> 1] if ($db[0] & 1);
- $msg .= " released" if (!($db[0] & 0x10));
- push @event, "3:buttons:" . ($db[0] & 0x10 ? "pressed" : "released");
- if ($msg =~ m/A0/) {push @event, "3:channelA:A0";}
- if ($msg =~ m/AI/) {push @event, "3:channelA:AI";}
- if ($msg =~ m/B0/) {push @event, "3:channelB:B0";}
- if ($msg =~ m/BI/) {push @event, "3:channelB:BI";}
- if ($msg =~ m/C0/) {push @event, "3:channelC:C0";}
- if ($msg =~ m/CI/) {push @event, "3:channelC:CI";}
- if ($msg =~ m/D0/) {push @event, "3:channelD:D0";}
- if ($msg =~ m/DI/) {push @event, "3:channelD:DI";}
- push @event, "3:$event:$msg";
- } else {
- if ($nu) {
- if ($st eq "keycard") {
- # Key Card, not tested
- $msg = "keycard_inserted" if ($db[0] == 112);
- } elsif ($st eq "liquidLeakage") {
- # liquid leakage sensor, not tested
- $msg = "wet" if ($db[0] == 0x11);
- } else {
- # Theoretically there can be a released event with some of the A0, BI
- # pins set, but with the plastic cover on this wont happen.
- $msg = $EnO_ptm200btn[($db[0] & 0xE0) >> 5];
- $msg .= "," . $EnO_ptm200btn[($db[0] & 0x0E) >> 1] if ($db[0] & 1);
- $msg .= " released" if (!($db[0] & 0x10));
- push @event, "3:buttons:" . ($db[0] & 0x10 ? "pressed" : "released");
- if ($msg =~ m/A0/) {push @event, "3:channelA:A0";}
- if ($msg =~ m/AI/) {push @event, "3:channelA:AI";}
- if ($msg =~ m/B0/) {push @event, "3:channelB:B0";}
- if ($msg =~ m/BI/) {push @event, "3:channelB:BI";}
- if ($msg =~ m/C0/) {push @event, "3:channelC:C0";}
- if ($msg =~ m/CI/) {push @event, "3:channelC:CI";}
- if ($msg =~ m/D0/) {push @event, "3:channelD:D0";}
- if ($msg =~ m/DI/) {push @event, "3:channelD:DI";}
- }
- } else {
- if ($db[0] & 0xC0) {
- # Only a Mechanical Handle is setting these bits when NU = 0
- $msg = "closed" if ($db[0] == 0xF0);
- $msg = "open" if ($db[0] == 0xE0);
- $msg = "tilted" if ($db[0] == 0xD0);
- $msg = "open_from_tilted" if ($db[0] == 0xC0);
- } elsif ($st eq "keycard") {
- $msg = "keycard_removed";
- } elsif ($st eq "liquidLeakage") {
- $msg = "dry";
- } else {
- $msg = (($db[0] & 0x10) ? "pressed" : "released");
- push @event, "3:buttons:" . ($db[0] & 0x10 ? "pressed" : "released");
- }
- }
- # released events are disturbing when using a remote, since it overwrites
- # the "real" state immediately. In the case of an Eltako FSB14, FSB61 ...
- # the state should remain released.
- if ($msg =~ m/released$/ &&
- AttrVal($name, "sensorMode", "switch") ne "pushbutton" &&
- $model !~ m/(FT55|FSB14|FSB61|FSB70|FSM12|FSM61|FTS12)$/) {
- $event = "buttons";
- $msg = "released";
- } else {
- push @event, "3:$event:$msg";
- }
- }
- } elsif ($rorg eq "D5") {
- # 1BS telegram
- if ($st eq "radioLinkTest") {
- # Radio Link Test (EEP A5-3F-00)
- @{$hash->{helper}{rlt}{param}} = ('parse', $hash, $data, $subDef, 'master', $RSSI);
- EnOcean_RLT($hash->{helper}{rlt}{param});
- } else {
- # Single Input Contact (EEP D5-00-01)
- # [Eltako FTK, STM-250]
- push @event, "3:state:" . ($db[0] & 1 ? "closed" : "open");
- if (!($db[0] & 8)) {
- # teach-in
- $attr{$name}{eep} = "D5-00-01";
- $attr{$name}{manufID} = "7FF";
- $attr{$name}{subType} = "contact";
- push @event, "3:teach:1BS teach-in accepted EEP D5-00-01 Manufacturer: no ID";
- Log3 $name, 2, "EnOcean $name teach-in EEP D5-00-01 Manufacturer: no ID";
- # store attr subType, manufID ...
- EnOcean_CommandSave(undef, undef);
- }
- }
- } elsif ($rorg eq "A5") {
- # 4BS telegram
- if (($db[0] & 8) == 0) {
- # Teach-In telegram
- if ($teach || AttrVal($hash->{IODev}{NAME}, "learningMode", "demand") eq "always" || $st eq "radioLinkTest") {
- if ($db[0] & 0x80) {
- # 4BS Teach-In telegram with EEP and manufacturer ID
- my $func = sprintf "%02X", ($db[3] >> 2);
- my $type = sprintf "%02X", ((($db[3] & 3) << 5) | ($db[2] >> 3));
- my $mid = sprintf "%03X", ((($db[2] & 7) << 8) | $db[1]);
- # manufID to account for vendor-specific features
- $attr{$name}{manufID} = $mid;
- $mid = $EnO_manuf{$mid} if($EnO_manuf{$mid});
- my $st = "A5.$func.$type";
- $attr{$name}{eep} = "A5-$func-$type";
- if ($db[0] & 0x10) {
- # 4BS teach-in bidirectional response received
- Log3 $name, 5, "EnOcean $name 4BS teach-in response message from $senderID received";
- if ($teach && exists($hash->{IODev}{helper}{"4BSRespWait"}{$destinationID})) {
- if ($db[0] & 0x40) {
- # EEP supported
- if ($db[0] & 0x20) {
- # SenderID stored
- Log3 $name, 2, "EnOcean $name 4BS teach-in accepted by $senderID";
- push @event, "3:teach:4BS teach-in accepted EEP $rorg-$func-$type Manufacturer: $mid";
- $attr{$name}{comMode} = "biDir";
- $attr{$name}{destinationID} = "unicast";
- # substitute subDef with DEF
- $attr{$name}{subDef} = $hash->{DEF};
- $hash->{DEF} = $senderID;
- $modules{EnOcean}{defptr}{$senderID} = $hash;
- delete $modules{EnOcean}{defptr}{$destinationID};
- # clear teach-in request
- delete $hash->{IODev}{helper}{"4BSRespWait"}{$destinationID};
- # store attr subType, manufID ...
- EnOcean_CommandSave(undef, undef);
- } else {
- # SenderID not stored / deleted
- Log3 $name, 2, "EnOcean $name 4BS request not accepted by $senderID";
- push @event, "3:teach:4BS request not accepted EEP $rorg-$func-$type Manufacturer: $mid";
- }
- } else {
- # EEP not suppported
- Log3 $name, 2, "EnOcean $name 4BS EEP not supported by $senderID";
- push @event, "3:teach:4BS EEP not supported EEP $rorg-$func-$type Manufacturer: $mid";
- }
- } else {
- Log3 $name, 2, "EnOcean $name 4BS teach-in response message from $senderID ignored";
- }
- } else {
- # 4BS teach-in query
- $attr{$name}{teachMethod} = '4BS';
- if(exists $EnO_eepConfig{$st}{attr}) {
- push @event, "3:teach:4BS teach-in accepted EEP A5-$func-$type Manufacturer: $mid";
- Log3 $name, 2, "EnOcean $name 4BS teach-in accepted EEP A5-$func-$type Manufacturer: $mid";
- foreach my $attrCntr (keys %{$EnO_eepConfig{$st}{attr}}) {
- if ($attrCntr eq "subDef") {
- if (!defined AttrVal($name, $attrCntr, undef)) {
- $attr{$name}{$attrCntr} = EnOcean_CheckSenderID($EnO_eepConfig{$st}{attr}{$attrCntr}, $hash->{IODev}{NAME}, "00000000");
- }
- } else {
- $attr{$name}{$attrCntr} = $EnO_eepConfig{$st}{attr}{$attrCntr};
- }
- }
- if (exists($hash->{helper}{teachInWait}) && $hash->{helper}{teachInWait} =~ m/^4BS|STE$/) {
- $attr{$filelogName}{logtype} = $EnO_eepConfig{$st}{GPLOT} . 'text'
- if (exists $attr{$filelogName}{logtype});
- EnOcean_CreateSVG(undef, $hash, undef);
- delete $hash->{helper}{teachInWait};
- }
- $st = $EnO_eepConfig{$st}{attr}{subType};
- } else {
- push @event, "3:teach:4BS EEP not supported EEP A5-$func-$type Manufacturer: $mid";
- Log3 $name, 2, "EnOcean $name 4BS EEP not supported EEP A5-$func-$type Manufacturer: $mid";
- $attr{$name}{subType} = "raw";
- $st = "raw";
- }
- if ($teach || $st eq "radioLinkTest") {
- # bidirectional 4BS teach-in
- if ($st eq "hvac.01" || $st eq "MD15") {
- # EEP A5-20-01
- $attr{$name}{comMode} = "biDir";
- $attr{$name}{destinationID} = "unicast";
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDef", "biDir");
- # teach-in response
- EnOcean_SndRadio(undef, $hash, $packetType, $rorg, "800FFFF0", $subDef, "00", $hash->{DEF});
- Log3 $name, 2, "EnOcean $name 4BS teach-in response sent to " . $hash->{DEF};
- readingsSingleUpdate($hash, 'operationMode', 'setpointTemp', 0);
- #####
- #EnOcean_hvac_01Cmd($hash, $packetType, 128); # 128 == 20 degree C
- } elsif ($st eq "hvac.02") {
- # EEP A5-20-02 not supported
- # teach-in response
- $data = sprintf "%06X90", (hex($func) << 7 | hex($type)) << 11 | 0x7FF;
- EnOcean_SndRadio(undef, $hash, $packetType, $rorg, $data, "00000000", "00", $hash->{DEF});
- Log3 $name, 2, "EnOcean $name 4BS teach-in response sent to " . $hash->{DEF};
- } elsif ($st eq "hvac.03") {
- # EEP A5-20-03 not supported
- # teach-in response
- $data = sprintf "%06X90", (hex($func) << 7 | hex($type)) << 11 | 0x7FF;
- EnOcean_SndRadio(undef, $hash, $packetType, $rorg, $data, "00000000", "00", $hash->{DEF});
- Log3 $name, 2, "EnOcean $name 4BS teach-in response sent to " . $hash->{DEF};
- } elsif ($st eq "hvac.04") {
- # heating radiator valve actuating drive (EEP A5-20-04)
- $attr{$name}{comMode} = "biDir";
- $attr{$name}{destinationID} = "unicast";
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDef", "biDir");
- # teach-in response
- $data = sprintf "%06XF0", (hex($func) << 7 | hex($type)) << 11 | 0x7FF;
- EnOcean_SndRadio(undef, $hash, $packetType, $rorg, $data, $subDef, "00", $hash->{DEF});
- Log3 $name, 2, "EnOcean $name 4BS teach-in response sent to " . $hash->{DEF};
- readingsSingleUpdate($hash, 'operationMode', 'setpointTemp', 0);
- } elsif ($st eq "radioLinkTest") {
- # Radio Link Test (EEP A5-3F-00)
- if (ReadingsVal($name, "state", 'standby') eq 'standby') {
- $attr{$name}{comMode} = "biDir";
- $attr{$name}{destinationID} = "unicast";
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDef", "biDir");
- # teach-in response, SenderID not stored
- $data = sprintf "%06XD0", (hex($func) << 7 | hex($type)) << 11 | hex($attr{$name}{manufID});
- #$data = sprintf "%06XD0", (hex($func) << 7 | hex($type)) << 11 | 0x7FF;
- #$data = sprintf "%06XF0", (hex($func) << 7 | hex($type)) << 11 | 0x7FF;
- EnOcean_SndRadio(undef, $hash, $packetType, $rorg, $data, $subDef, "00", $hash->{DEF});
- Log3 $name, 2, "EnOcean $name 4BS teach-in response sent to " . $hash->{DEF};
- @{$hash->{helper}{rlt}{param}} = ('start', $hash, undef, $subDef, 'master', $RSSI);
- EnOcean_RLT($hash->{helper}{rlt}{param});
- }
- #} elsif ($st =~ m/^hvac\.1[0-1]$/) {
- # EEP A5-20-10, A5-20-11
- # teach-in response
- # $data = sprintf "%06XF0", (hex($func) << 7 | hex($type)) << 11 | 0x7FF;
- # EnOcean_SndRadio(undef, $hash, $packetType, $rorg, $data, "00000000", "00", $hash->{DEF});
- # Log3 $name, 2, "EnOcean $name 4BS teach-in response sent to " . $hash->{DEF};
- } elsif (AttrVal($name, "comMode", "") =~ m/^confirm|biDir$/) {
- # confirm telegram requested, teach-in response sent
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDef", "biDir");
- $data = sprintf "%06XF0", (hex($func) << 7 | hex($type)) << 11 | 0x7FF;
- EnOcean_SndRadio(undef, $hash, $packetType, $rorg, $data, $subDef, "00", $hash->{DEF});
- Log3 $name, 2, "EnOcean $name 4BS teach-in response sent to " . $hash->{DEF};
- #} else {
- # EEP not supported
- # teach-in response
- # $data = sprintf "%06X90", (hex($func) << 7 | hex($type)) << 11 | 0x7FF;
- # EnOcean_SndRadio(undef, $hash, $packetType, $rorg, $data, "00000000", "00", $hash->{DEF});
- # Log3 $name, 2, "EnOcean $name 4BS teach-in response sent to " . $hash->{DEF};
- }
- }
- }
- # store attr subType, manufID ...
- EnOcean_CommandSave(undef, undef);
- # delete standard readings
- CommandDeleteReading(undef, "$name sensor[0-9]");
- CommandDeleteReading(undef, "$name D[0-9]");
- } else {
- # 4BS Teach-In without EEP and manufacturer ID
- push @event, "3:teach:4BS teach-in accepted: No EEP profile identifier and no Manufacturer ID";
- Log3 $name, 2, "EnOcean $name 4BS teach-in accepted. No EEP profile identifier and no Manufacturer ID";
- $attr{$name}{subType} = "raw";
- $st = "raw";
- }
- } else {
- Log3 $name, 4, "EnOcean $name teach-in with subType $st locked, set transceiver in teach mode.";
- return "";
- }
- } elsif ($st eq "hvac.01" || $st eq "MD15") {
- # Battery Powered Actuator (EEP A5-20-01)
- # [Kieback&Peter MD15-FTL-xx]
- push @event, "3:energyInput:" . (($db[2] & 0x40) ? "enabled" : "disabled");
- my $battery = ($db[2] & 0x10) ? "ok" : "low";
- my $energyStorage;
- if ($db[2] & 0x20) {
- $energyStorage = 'charged';
- $battery = 'ok';
- } else {
- $energyStorage = 'empty';
- }
- push @event, "3:battery:$battery";
- push @event, "3:energyStorage:$energyStorage";
- my $roomTemp = ReadingsVal($name, "roomTemp", 20);
- if ($db[2] & 4) {
- CommandDeleteReading(undef, "$name roomTemp");
- } else {
- $roomTemp = $db[1] * 40 / 255;
- push @event, "3:roomTemp:" . sprintf "%0.1f", $roomTemp;
- }
- my $setpoint = $db[3];
- push @event, "3:setpoint:$setpoint";
- my $maintenanceMode = ReadingsVal($name, "maintenanceMode", ($db[2] & 0x80) ? 'on' : 'off');
- # if ($db[2] & 0x80) {
- # $maintenanceMode = 'on' if (!defined($maintenanceMode));
- # } else {
- # $maintenanceMode = 'off';
- # }
- push @event, "3:cover:" . (($db[2] & 8) ? "open" : "closed");
- push @event, "3:window:" . (($db[2] & 2) ? "open" : "closed");
- push @event, "3:actuatorState:". (($db[2] & 1) ? "obstructed" : "ok");
- push @event, "3:selfCtrl:" . (($db[0] & 4) ? "on" : "off");
- my $functionSelect = 0;
- my $setpointSelect = 0;
- my $setpointSet = ReadingsVal($name, "setpointSet", $setpoint);
- my $setpointTemp = ReadingsVal($name, "setpointTemp", 20);
- my $setpointTempSet = ReadingsVal($name, "setpointTempSet", $setpointTemp);
- my $temperature = ReadingsVal($name, 'temperature', $roomTemp);
- if (!defined(AttrVal($name, "temperatureRefDev", undef))) {
- if ($db[2] & 4) {
- CommandDeleteReading(undef, "$name temperature");
- } else {
- $temperature = $roomTemp;
- readingsSingleUpdate($hash, 'temperature', sprintf("%0.1f", $temperature), 1);
- }
- }
- Log3 $name, 5, "EnOcean $name EnOcean_parse SPT: $setpointTemp SPTS: $setpointTempSet";
- my $operationMode = ReadingsVal($name, "operationMode", 'setpointTemp');
- my $setpointSummerMode = AttrVal($name, "setpointSummerMode", 0);
- my $summerMode = AttrVal($name, "summerMode", "off");
- my $timeDiff = EnOcean_TimeDiff(ReadingsTimestamp($name, 'wakeUpCycle', undef));
- my $waitingCmds = ReadingsVal($name, "waitingCmds", "no_change");
- my $wakeUpCycle = 600;
- # calc wakeup cycle
- if ($summerMode eq 'off') {
- $summerMode = 0;
- if ($timeDiff == 0) {
- $wakeUpCycle = 1200;
- } elsif ($timeDiff < 120) {
- $wakeUpCycle = 120;
- } elsif ($timeDiff > 1200) {
- $wakeUpCycle = 1200;
- } else {
- $wakeUpCycle = int($timeDiff);
- }
- } else {
- $summerMode = 8;
- # ignore all commands
- if ($waitingCmds ne "summerMode") {
- $waitingCmds = "no_change";
- CommandDeleteReading(undef, "$name waitingCmds");
- }
- if ($manufID eq '049') {
- $wakeUpCycle = 28800;
- } else {
- $wakeUpCycle = 3600;
- }
- }
- readingsSingleUpdate($hash, 'wakeUpCycle', $wakeUpCycle, 1);
- # set alarm timer
- CommandDeleteReading(undef, "$name alarm");
- @{$hash->{helper}{alarmTimer}} = ($hash, 'alarm', 'no_response_from_actuator', 1, 3);
- RemoveInternalTimer($hash->{helper}{alarmTimer});
- InternalTimer(gettimeofday() + $wakeUpCycle * 1.1, "EnOcean_readingsSingleUpdate", $hash->{helper}{alarmTimer}, 0);
- my $actionCmd = AttrVal($name, "rcvRespAction", undef);
- if (defined $actionCmd) {
- my %specials = ("%ACTUATORSTATE" => (($db[2] & 1) ? "obstructed" : "ok"),
- "%BATTERY" => $battery,
- "%COVER" => (($db[2] & 8) ? "open" : "closed"),
- "%ENERGYINPUT" => (($db[2] & 0x40) ? "enabled" : "disabled"),
- "%ENERGYSTORAGE" => $energyStorage,
- "%MAINTENANCEMODE" => $maintenanceMode,
- "%NAME" => $name,
- "%OPERATIONMODE" => $operationMode,
- "%ROOMTEMP" => $roomTemp,
- "%SELFCTRL" => (($db[0] & 4) ? "on" : "off"),
- "%SETPOINT" => $setpoint,
- "%SETPOINTTEMP" => $setpointTemp,
- "%SUMMERMODE" => $summerMode,
- "%TEMPERATURE" => $temperature,
- "%WINDOW" => (($db[2] & 2) ? "open" : "closed"),
- );
- # action exec
- $actionCmd = EvalSpecials($actionCmd, %specials);
- my $ret = AnalyzeCommandChain(undef, $actionCmd);
- Log3 $name, 2, "Encean $name rcvRespAction ERROR: $ret" if($ret);
- $maintenanceMode = ReadingsVal($name, "maintenanceMode", ($db[2] & 0x80) ? 'on' : 'off');
- $operationMode = ReadingsVal($name, "operationMode", 'setpointTemp');
- $setpointSet = ReadingsVal($name, "setpointSet", $setpoint);
- $setpointTempSet = ReadingsVal($name, "setpointTempSet", $setpointTemp);
- $temperature = ReadingsVal($name, 'temperature', $roomTemp);
- $waitingCmds = ReadingsVal($name, "waitingCmds", "no_change");
- }
- if ($waitingCmds eq "valveOpens") {
- # deactivate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- $setpointSet = 100;
- $db[2] = 0x20;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- push @event, "3:maintenanceMode:valveOpend:runInit";
- push @event, "3:operationMode:off";
- CommandDeleteReading(undef, "$name setpointSet");
- CommandDeleteReading(undef, "$name setpointTemp");
- CommandDeleteReading(undef, "$name setpointTempSet");
- CommandDeleteReading(undef, "$name waitingCmds");
- $functionSelect = 1;
- $waitingCmds = 0x20;
- } elsif ($waitingCmds eq "valveCloses") {
- if ($maintenanceMode eq "valveOpend:runInit") {
- $setpointSet = 100;
- $db[2] = 0x20;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- push @event, "3:maintenanceMode:runInit";
- push @event, "3:operationMode:off";
- $functionSelect = 1;
- $waitingCmds = 0x80;
- } else {
- $setpointSet = 0;
- $db[2] = 0x20;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- push @event, "3:maintenanceMode:valveClosed";
- push @event, "3:operationMode:off";
- CommandDeleteReading(undef, "$name waitingCmds");
- $functionSelect = 1;
- $waitingCmds = 0x10;
- }
- # stop PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- CommandDeleteReading(undef, "$name setpointSet");
- CommandDeleteReading(undef, "$name setpointTemp");
- CommandDeleteReading(undef, "$name setpointTempSet");
- } elsif ($waitingCmds eq "runInit") {
- # deactivate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- $setpointSet = 0;
- $db[2] = 0x20;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- push @event, "3:maintenanceMode:runInit";
- push @event, "3:operationMode:off";
- push @event, "3:waitingCmds:$operationMode";
- #CommandDeleteReading(undef, "$name setpointSet");
- #CommandDeleteReading(undef, "$name setpointTemp");
- #CommandDeleteReading(undef, "$name setpointTempSet");
- #CommandDeleteReading(undef, "$name waitingCmds");
- $functionSelect = 1;
- $waitingCmds = 0x80;
- } elsif ($waitingCmds eq "liftSet") {
- # deactivate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- $setpointSet = 0;
- $db[2] = 0x20;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- push @event, "3:maintenanceMode:listSet";
- push @event, "3:operationMode:off";
- push @event, "3:waitingCmds:$operationMode";
- #CommandDeleteReading(undef, "$name setpointSet");
- #CommandDeleteReading(undef, "$name setpointTemp");
- #CommandDeleteReading(undef, "$name setpointTempSet");
- #CommandDeleteReading(undef, "$name waitingCmds");
- $functionSelect = 1;
- $waitingCmds = 0x40;
- } elsif ($waitingCmds eq "setpoint") {
- # deactivate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- if ($maintenanceMode eq "valveOpend:runInit") {
- $setpointSet = 100;
- $db[2] = 0x20;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- push @event, "3:maintenanceMode:runInit";
- push @event, "3:operationMode:off";
- $functionSelect = 1;
- $waitingCmds = 0x80;
- } else {
- $db[2] = (40 - $temperature) * 255 / 40;
- push @event, "3:maintenanceMode:off";
- push @event, "3:operationMode:setpoint";
- CommandDeleteReading(undef, "$name setpointTemp");
- CommandDeleteReading(undef, "$name setpointTempSet");
- CommandDeleteReading(undef, "$name waitingCmds");
- $waitingCmds = 0;
- }
- } elsif ($waitingCmds eq "setpointTemp") {
- if ($maintenanceMode eq "valveOpend:runInit") {
- # deactivate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- $setpointSet = 100;
- $db[2] = 0x20;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- push @event, "3:maintenanceMode:runInit";
- push @event, "3:operationMode:off";
- $functionSelect = 1;
- $waitingCmds = 0x80;
- } else {
- if (AttrVal($name, "pidCtrl", 'on') eq 'on') {
- # activate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'actuator', '');
- $setpointSet = ReadingsVal($name, "setpointSet", $setpoint);
- } else {
- # deactivate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- # setpoint temperature
- $setpointSet = $setpointTempSet * 255 / 40;
- $setpointSelect = 4;
- }
- $setpointTemp = $setpointTempSet;
- $db[2] = (40 - $temperature) * 255 / 40;
- push @event, "3:setpointTemp:" . sprintf("%0.1f", $setpointTemp);
- push @event, "3:maintenanceMode:off";
- push @event, "3:operationMode:setpointTemp";
- CommandDeleteReading(undef, "$name waitingCmds");
- $waitingCmds = 0;
- }
- } elsif ($waitingCmds eq "summerMode") {
- # deactivate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- $setpointSet = $setpointSummerMode;
- $db[2] = (40 - $temperature) * 255 / 40;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- push @event, "3:maintenanceMode:off";
- push @event, "3:operationMode:summerMode";
- #CommandDeleteReading(undef, "$name setpointSet");
- CommandDeleteReading(undef, "$name setpointTemp");
- CommandDeleteReading(undef, "$name setpointTempSet");
- CommandDeleteReading(undef, "$name waitingCmds");
- $waitingCmds = 0;
- } elsif ($operationMode eq "setpoint") {
- # deactivate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- if ($maintenanceMode eq "valveOpend:runInit") {
- $setpointSet = 100;
- $db[2] = 0x20;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- push @event, "3:maintenanceMode:off";
- push @event, "3:operationMode:setpoint";
- $functionSelect = 1;
- $waitingCmds = 0x80;
- } else {
- $db[2] = (40 - $temperature) * 255 / 40;
- push @event, "3:maintenanceMode:off";
- push @event, "3:operationMode:setpoint";
- $waitingCmds = 0;
- }
- } elsif ($operationMode eq "setpointTemp") {
- if ($maintenanceMode eq "valveOpend:runInit") {
- # deactivate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- $setpointSet = 100;
- $db[2] = 0x20;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- push @event, "3:maintenanceMode:off";
- push @event, "3:operationMode:setpointTemp";
- $functionSelect = 1;
- $waitingCmds = 0x80;
- } else {
- if (AttrVal($name, "pidCtrl", 'on') eq 'on') {
- # activate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'actuator', '');
- $setpointSet = ReadingsVal($name, "setpointSet", $setpointSet);
- } else {
- # deactivate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- # setpoint temperature
- $setpointSet = $setpointTempSet * 255 / 40;
- $setpointSelect = 4;
- }
- $db[2] = (40 - $temperature) * 255 / 40;
- $setpointTemp = $setpointTempSet;
- push @event, "3:setpointTemp:" . sprintf("%.1f", $setpointTemp);
- push @event, "3:maintenanceMode:off";
- push @event, "3:operationMode:setpointTemp";
- $waitingCmds = 0;
- }
- } elsif ($operationMode eq "summerMode") {
- # deactivate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- $setpointSet = $setpointSummerMode;
- $db[2] = (40 - $temperature) * 255 / 40;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- push @event, "3:maintenanceMode:off";
- push @event, "3:operationMode:summerMode";
- $waitingCmds = 0;
- } elsif ($maintenanceMode eq "valveOpend:runInit") {
- # deactivate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- $setpointSet = 100;
- $db[2] = 0x20;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- push @event, "3:maintenanceMode:valveOpend:runInit";
- push @event, "3:operationMode:off";
- #CommandDeleteReading(undef, "$name setpointSet");
- #CommandDeleteReading(undef, "$name setpointTemp");
- #CommandDeleteReading(undef, "$name setpointTempSet");
- #CommandDeleteReading(undef, "$name waitingCmds");
- $functionSelect = 1;
- $waitingCmds = 0x20;
- } elsif ($maintenanceMode eq "valveClosed") {
- # stop PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- $setpointSet = 0;
- $db[2] = 0x20;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- push @event, "3:maintenanceMode:valveClosed";
- push @event, "3:operationMode:off";
- #CommandDeleteReading(undef, "$name setpointSet");
- #CommandDeleteReading(undef, "$name setpointTemp");
- #CommandDeleteReading(undef, "$name setpointTempSet");
- #CommandDeleteReading(undef, "$name waitingCmds");
- $functionSelect = 1;
- $waitingCmds = 0x10;
- } else {
- $db[2] = (40 - $temperature) * 255 / 40;
- $waitingCmds = 0;
- }
- push @event, "3:state:T: " . sprintf("%0.1f", $temperature) . " SPT: " . sprintf("%.1f", $setpointTemp) . " SP: $setpoint";
- # sent message to the actuator
- $data = sprintf "%02X%02X%02X08", $setpointSet, $db[2], $waitingCmds | $summerMode | $setpointSelect | $functionSelect;
- EnOcean_SndRadio(undef, $hash, $packetType, "A5", $data, $subDef, "00", $hash->{DEF});
- } elsif ($st eq "hvac.04") {
- # heating radiator valve actuating drive EEP A5-20-04)
- my %failureCode = (
- 17 => "measurement_error",
- 18 => "battery_empty",
- 20 => "frost_protection",
- 33 => "blocked_valve",
- 36 => "end_point_detection_error",
- 40 => "no_valve",
- 49 => "not_taught_in",
- 53 => "no_response_from_controller",
- 54 => "teach-in_error"
- );
- my $battery = "ok";
- my %displayOrientation = (0 => 0, 90 => 1, 180 => 2, 270 => 3);
- my $feedTemp = ReadingsVal($name, "feedTemp", 20);
- my $roomTemp = ReadingsVal($name, "roomTemp", 20);
- my $setpoint = $db[3];
- push @event, "3:setpoint:$setpoint";
- my $setpointSet = ReadingsVal($name, "setpointSet", $setpoint);
- my $setpointTemp = ReadingsVal($name, "setpointTemp", 20);
- my $setpointTempSet = ReadingsVal($name, "setpointTempSet", $setpointTemp);
- my $temperature = ReadingsVal($name, "temperature", $roomTemp);
- if ($db[0] & 2) {
- if ($setpointTemp == $setpointTempSet) {
- $setpointTemp = sprintf "%0.1f", ($db[2] * 20 / 255 + 10);
- if ($setpointTemp != $setpointTempSet) {
- # setpointTempSet has been changed by actuator
- $setpointTempSet = $setpointTemp;
- readingsSingleUpdate($hash, 'setpointTempSet', $setpointTempSet, 1);
- }
- } else {
- # setpointTempSet has been changed by Fhem
- $setpointTemp = sprintf "%0.1f", ($db[2] * 20 / 255 + 10);
- }
- push @event, "3:setpointTemp:$setpointTemp";
- } else {
- if ($db[0] & 0x80) {
- # temperature measurement inactive
- CommandDeleteReading(undef, "$name feedTemp");
- } else {
- $feedTemp = $db[2] * 60 / 255 + 20;
- push @event, "3:feedTemp:" . sprintf("%0.1f", $feedTemp);
- }
- }
- if ($db[0] & 1) {
- # failure code
- if (exists $failureCode{$db[1]}) {
- push @event, "3:alarm:" . $failureCode{$db[1]};
- $battery = "empty" if ($db[1] == 18);
- } else {
- CommandDeleteReading(undef, "$name alarm");
- }
- } else {
- if ($db[0] & 0x80) {
- # temperature measurement inactive
- CommandDeleteReading(undef, "$name roomTemp");
- } else {
- # room temperature
- $roomTemp = sprintf("%0.1f", ($db[1] * 20 / 255 + 10));
- push @event, "3:roomTemp:$roomTemp";
- CommandDeleteReading(undef, "$name alarm");
- }
- }
- if (!defined(AttrVal($name, "temperatureRefDev", undef))) {
- if ($db[0] & 0x80) {
- # temperature measurement needed, activate temperature measurement
- $attr{$name}{measurementCtrl} = 'enable';
- EnOcean_CommandSave(undef, undef);
- } else {
- $temperature = $roomTemp;
- readingsSingleUpdate($hash, 'temperature', $temperature, 1);
- #push @event, "3:temperature:$temperature";
- }
- }
- push @event, "3:measurementState:" . ($db[0] & 0x80 ? "inactive" : "active");
- push @event, "3:blockKey:" . ($db[0] & 4 ? "yes" : "no");
- push @event, "3:battery:$battery";
- #push @event, "3:state:T: $temperature SPT: $setpointTemp SP: $setpoint";
- if ($db[0] & 0x40) {
- # status request
- # action needed?
- }
- Log3 $name, 5, "EnOcean $name EnOcean_parse SPT: $setpointTemp SPTS: $setpointTempSet";
- my $activatePID = AttrVal($name, 'pidCtrl', 'on') eq 'on' ? 'actuator' : 'stop';
- my $blockKey = ((AttrVal($name, "blockKey", 'no') eq 'yes') ? 1 : 0) << 2;
- my $displayOrientation = $displayOrientation{AttrVal($name, "displayOrientation", 0)} << 4;
- my $maintenanceMode = ReadingsVal($name, "maintenanceMode", "off");
- my $measurementCtrl = (AttrVal($name, 'measurementCtrl', 'enable') eq 'enable') ? 0 : 0x40;
- #my $operationMode = ReadingsVal($name, "operationMode", "off");
- my $operationMode = ReadingsVal($name, "operationMode", ((AttrVal($name, 'pidCtrl', 'on') eq 'on') ? 'setpointTemp' : 'setpoint'));
- my $summerMode = AttrVal($name, "summerMode", "off");
- my $waitingCmds = ReadingsVal($name, "waitingCmds", "no_change");
- my $wakeUpCycle = $wakeUpCycle{AttrVal($name, "wakeUpCycle", 300)};
- if ($summerMode eq 'off' && $wakeUpCycle >= 50) {
- # set default Wake-up Cycle (300 s)
- $wakeUpCycle = 9;
- } elsif ($summerMode eq 'on') {
- if ($waitingCmds ne "summerMode") {
- $waitingCmds = "no_change";
- CommandDeleteReading(undef, "$name waitingCmds");
- }
- $setpointSet = 100;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- $wakeUpCycle = 50 if ($wakeUpCycle < 50);
- }
- my $actionCmd = AttrVal($name, "rcvRespAction", undef);
- if (defined $actionCmd) {
- my %specials = ("%BATTERY" => $battery,
- "%FEEDTEMP" => $feedTemp,
- "%MAINTENANCEMODE" => $maintenanceMode,
- "%NAME" => $name,
- "%OPERATIONMODE" => $operationMode,
- "%ROOMTEMP" => $roomTemp,
- "%SETPOINT" => $setpoint,
- "%SETPOINTTEMP" => $setpointTemp,
- "%SUMMERMODE" => $summerMode,
- "%TEMPERATURE" => $temperature,
- );
- # action exec
- $actionCmd = EvalSpecials($actionCmd, %specials);
- my $ret = AnalyzeCommandChain(undef, $actionCmd);
- Log3 $name, 2, "EnOcean $name rcvRespAction ERROR: $ret" if($ret);
- $maintenanceMode = ReadingsVal($name, "maintenanceMode", 'off');
- $operationMode = ReadingsVal($name, "operationMode", ((AttrVal($name, 'pidCtrl', 'on') eq 'on') ? 'setpointTemp' : 'setpoint'));
- $setpointSet = ReadingsVal($name, "setpointSet", $setpoint);
- $setpointTempSet = ReadingsVal($name, "setpointTempSet", $setpointTemp);
- $temperature = ReadingsVal($name, 'temperature', $roomTemp);
- $waitingCmds = ReadingsVal($name, "waitingCmds", "no_change");
- }
- if ($waitingCmds eq "valveOpens") {
- # deactivate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- $setpointSet = 100;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- push @event, "3:maintenanceMode:valveOpend:runInit";
- push @event, "3:operationMode:off";
- CommandDeleteReading(undef, "$name setpointSet");
- CommandDeleteReading(undef, "$name setpointTemp");
- CommandDeleteReading(undef, "$name setpointTempSet");
- CommandDeleteReading(undef, "$name waitingCmds");
- $waitingCmds = 1;
- } elsif ($waitingCmds eq "valveCloses") {
- if ($maintenanceMode eq "valveOpend:runInit") {
- $setpointSet = 100;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- push @event, "3:maintenanceMode:runInit";
- push @event, "3:operationMode:off";
- $waitingCmds = 2;
- } else {
- $setpointSet = 0;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- push @event, "3:maintenanceMode:valveClosed";
- push @event, "3:operationMode:off";
- CommandDeleteReading(undef, "$name waitingCmds");
- $waitingCmds = 3;
- }
- # stop PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- CommandDeleteReading(undef, "$name setpointSet");
- CommandDeleteReading(undef, "$name setpointTemp");
- CommandDeleteReading(undef, "$name setpointTempSet");
- } elsif ($waitingCmds eq "runInit") {
- # deactivate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- $setpointSet = 100;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- push @event, "3:maintenanceMode:runInit";
- push @event, "3:operationMode:off";
- CommandDeleteReading(undef, "$name setpointSet");
- CommandDeleteReading(undef, "$name setpointTemp");
- CommandDeleteReading(undef, "$name setpointTempSet");
- CommandDeleteReading(undef, "$name waitingCmds");
- $waitingCmds = 2;
- } elsif ($waitingCmds eq "setpoint") {
- # deactivate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- if ($maintenanceMode eq "valveOpend:runInit") {
- $setpointSet = 100;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- push @event, "3:maintenanceMode:runInit";
- push @event, "3:operationMode:off";
- $waitingCmds = 2;
- } else {
- push @event, "3:maintenanceMode:off";
- push @event, "3:operationMode:setpoint";
- #CommandDeleteReading(undef, "$name setpointSet");
- CommandDeleteReading(undef, "$name setpointTemp");
- CommandDeleteReading(undef, "$name setpointTempSet");
- CommandDeleteReading(undef, "$name waitingCmds");
- $waitingCmds = 0;
- }
- } elsif ($waitingCmds eq "setpointTemp") {
- if ($maintenanceMode eq "valveOpend:runInit") {
- # deactivate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- $setpointSet = 100;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- push @event, "3:maintenanceMode:runInit";
- push @event, "3:operationMode:off";
- $waitingCmds = 2;
- } else {
- # activate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, $activatePID, '');
- $setpointSet = ReadingsVal($name, "setpointSet", $setpoint);
- $setpointTemp = $setpointTempSet;
- push @event, "3:setpointTemp:$setpointTemp";
- push @event, "3:maintenanceMode:off";
- push @event, "3:operationMode:setpointTemp";
- #CommandDeleteReading(undef, "$name setpointSet");
- #CommandDeleteReading(undef, "$name setpointTempSet");
- CommandDeleteReading(undef, "$name waitingCmds");
- $waitingCmds = 0;
- }
- } elsif ($waitingCmds eq "summerMode") {
- # deactivate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- $setpointSet = 100;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- push @event, "3:maintenanceMode:off";
- push @event, "3:operationMode:summerMode";
- #CommandDeleteReading(undef, "$name setpointSet");
- CommandDeleteReading(undef, "$name setpointTemp");
- CommandDeleteReading(undef, "$name setpointTempSet");
- CommandDeleteReading(undef, "$name waitingCmds");
- $waitingCmds = 0;
- } elsif ($operationMode eq "setpoint") {
- # deactivate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- if ($maintenanceMode eq "valveOpend:runInit") {
- $setpointSet = 100;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- push @event, "3:maintenanceMode:off";
- push @event, "3:operationMode:setpoint";
- $waitingCmds = 2;
- } else {
- push @event, "3:maintenanceMode:off";
- push @event, "3:operationMode:setpoint";
- #CommandDeleteReading(undef, "$name setpointSet");
- #CommandDeleteReading(undef, "$name setpointTemp");
- #CommandDeleteReading(undef, "$name setpointTempSet");
- #CommandDeleteReading(undef, "$name waitingCmds");
- $waitingCmds = 0;
- }
- } elsif ($operationMode eq "setpointTemp") {
- if ($maintenanceMode eq "valveOpend:runInit") {
- # deactivate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- $setpointSet = 100;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- push @event, "3:maintenanceMode:off";
- push @event, "3:operationMode:setpointTemp";
- $waitingCmds = 2;
- } else {
- # activate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, $activatePID, '');
- $setpointSet = ReadingsVal($name, "setpointSet", $setpointSet);
- push @event, "3:setpointTemp:$setpointTemp";
- push @event, "3:maintenanceMode:off";
- push @event, "3:operationMode:setpointTemp";
- #CommandDeleteReading(undef, "$name setpointSet");
- #CommandDeleteReading(undef, "$name setpointTempSet");
- #CommandDeleteReading(undef, "$name waitingCmds");
- $waitingCmds = 0;
- }
- } elsif ($operationMode eq "summerMode") {
- # deactivate PID regulator
- ($err, $logLevel, $response) = EnOcean_setPID(undef, $hash, 'stop', '');
- $setpointSet = 100;
- readingsSingleUpdate($hash, 'setpointSet', $setpointSet, 1);
- push @event, "3:maintenanceMode:off";
- push @event, "3:operationMode:summerMode";
- #CommandDeleteReading(undef, "$name setpointSet");
- #CommandDeleteReading(undef, "$name setpointTemp");
- #CommandDeleteReading(undef, "$name setpointTempSet");
- #CommandDeleteReading(undef, "$name waitingCmds");
- $waitingCmds = 0;
- } else {
- $waitingCmds = 0;
- }
- push @event, "3:state:T: $temperature SPT: $setpointTemp SP: $setpoint";
- # sent message to the actuator
- $data = sprintf "%02X%02X%02X%02X", $setpointSet,
- ($setpointTempSet - 10) / 20 * 255,
- (AttrVal($name, 'pidCtrl', 'on') eq 'on' ? 0 : 0x80) | $measurementCtrl | $wakeUpCycle,
- $displayOrientation | 8 | $blockKey | $waitingCmds;
- EnOcean_SndRadio(undef, $hash, $packetType, "A5", $data, $subDef, "00", $hash->{DEF});
- } elsif ($st eq "hvac.10") {
- # Generic HVAC Interface (EEP A5-20-10)
- my %mode = (
- 0 => "auto",
- 1 => "heat",
- 2 => "morning_warmup",
- 3 => "cool",
- 4 => "night_purge",
- 5 => "precool",
- 6 => "off",
- 7 => "test",
- 8 => "emergency_heat",
- 9 => "fan_only",
- 10 => "free_cool",
- 11 => "ice",
- 12 => "max_heat",
- 13 => "eco",
- 14 => "dehumidification",
- 15 => "calibration",
- 16 => "emergency_cool",
- 17 => "emergency_stream",
- 18 => "max_cool",
- 19 => "hvc_load",
- 20 => "no_load",
- 31 => "auto_heat",
- 32 => "auto_cool",
- );
- if (exists $mode{$db[3]}) {
- push @event, "3:mode:$mode{$db[3]}";
- } else {
- push @event, "3:mode:unknown";
- }
- my %vanePosition = (
- 0 => "auto",
- 1 => "horizontal",
- 2 => "position_2",
- 3 => "position_3",
- 4 => "position_4",
- 5 => "vertical",
- 6 => "swing",
- 11 => "vertical_swing",
- 12 => "horizontal_swing",
- 13 => "hor_vert_swing",
- 14 => "stop_swing",
- );
- my $vanePosition = $db[2] >> 4;
- if (exists $vanePosition{$vanePosition}) {
- push @event, "3:vanePosition:$vanePosition{$vanePosition}";
- } else {
- push @event, "3:vanePosition:unknown";
- }
- my $fanSpeed = $db[2] & 0x0F;
- if ($fanSpeed == 0) {
- push @event, "3:fanSpeed:auto";
- } elsif ($fanSpeed > 0 && $fanSpeed < 15) {
- push @event, "3:fanSpeed:" . $fanSpeed;
- } else {
- push @event, "3:fanSpeed:unknown";
- }
- if ($db[1] == 255) {
- push @event, "3:ctrl:auto";
- } elsif ($db[1] >= 0 && $db[1] <= 100) {
- push @event, "3:ctrl:" . $db[1];
- } else {
- push @event, "3:ctrl:unknown";
- }
- my $occupancy = ($db[0] & 6) >> 1;
- if ($occupancy == 0) {
- push @event, "3:occupancy:occupied";
- } elsif ($occupancy == 1) {
- push @event, "3:occupancy:standby";
- } elsif ($occupancy == 2) {
- push @event, "3:occupancy:unoccupied";
- } else {
- push @event, "3:occupancy:off";
- }
- push @event, "3:powerSwitch:" . ($db[0] & 1 ? "on" : "off");
- push @event, "3:state:" . ($db[0] & 1 ? "on" : "off");
- } elsif ($st eq "hvac.11") {
- # Generic HVAC Interface - Error Control (EEP A5-20-11)
- push @event, "3:errorCode:" . hex(substr($data, 0, 4));
- push @event, "3:otherDisable:" . ($db[1] & 8 ? "disabled" : "enabled");
- push @event, "3:windowDisable:" . ($db[1] & 4 ? "disabled" : "enabled");
- push @event, "3:keyCardDisable:" . ($db[1] & 2 ? "disabled" : "enabled");
- push @event, "3:externalDisable:" . ($db[1] & 1 ? "disabled" : "enabled");
- push @event, "3:remoteCtrl:" . ($db[0] & 4 ? "disabled" : "enabled");
- push @event, "3:window:" . ($db[0] & 2 ? "closed" : "opened");
- push @event, "3:alarm:" . ($db[0] & 1 ? "error" : "ok");
- push @event, "3:state:" . ($db[0] & 1 ? "error" : "ok");
- } elsif ($st =~ m/^tempSensor/) {
- # Temperature Sensor with with different ranges (EEP A5-02-01 ... A5-02-1B)
- # $db[1] is the temperature where 0x00 = max °C ... 0xFF = min °C
- my $temp;
- $temp = sprintf "%0.1f", 0 - $db[1] / 6.375 if ($st eq "tempSensor.01");
- $temp = sprintf "%0.1f", 10 - $db[1] / 6.375 if ($st eq "tempSensor.02");
- $temp = sprintf "%0.1f", 20 - $db[1] / 6.375 if ($st eq "tempSensor.03");
- $temp = sprintf "%0.1f", 30 - $db[1] / 6.375 if ($st eq "tempSensor.04");
- $temp = sprintf "%0.1f", 40 - $db[1] / 6.375 if ($st eq "tempSensor.05");
- $temp = sprintf "%0.1f", 50 - $db[1] / 6.375 if ($st eq "tempSensor.06");
- $temp = sprintf "%0.1f", 60 - $db[1] / 6.375 if ($st eq "tempSensor.07");
- $temp = sprintf "%0.1f", 70 - $db[1] / 6.375 if ($st eq "tempSensor.08");
- $temp = sprintf "%0.1f", 80 - $db[1] / 6.375 if ($st eq "tempSensor.09");
- $temp = sprintf "%0.1f", 90 - $db[1] / 6.375 if ($st eq "tempSensor.0A");
- $temp = sprintf "%0.1f", 100 - $db[1] / 6.375 if ($st eq "tempSensor.0B");
- $temp = sprintf "%0.1f", 20 - $db[1] / 3.1875 if ($st eq "tempSensor.10");
- $temp = sprintf "%0.1f", 30 - $db[1] / 3.1875 if ($st eq "tempSensor.11");
- $temp = sprintf "%0.1f", 40 - $db[1] / 3.1875 if ($st eq "tempSensor.12");
- $temp = sprintf "%0.1f", 50 - $db[1] / 3.1875 if ($st eq "tempSensor.13");
- $temp = sprintf "%0.1f", 60 - $db[1] / 3.1875 if ($st eq "tempSensor.14");
- $temp = sprintf "%0.1f", 70 - $db[1] / 3.1875 if ($st eq "tempSensor.15");
- $temp = sprintf "%0.1f", 80 - $db[1] / 3.1875 if ($st eq "tempSensor.16");
- $temp = sprintf "%0.1f", 90 - $db[1] / 3.1875 if ($st eq "tempSensor.17");
- $temp = sprintf "%0.1f", 100 - $db[1] / 3.1875 if ($st eq "tempSensor.18");
- $temp = sprintf "%0.1f", 110 - $db[1] / 3.1875 if ($st eq "tempSensor.19");
- $temp = sprintf "%0.1f", 120 - $db[1] / 3.1875 if ($st eq "tempSensor.1A");
- $temp = sprintf "%0.1f", 130 - $db[1] / 3.1875 if ($st eq "tempSensor.1B");
- $temp = sprintf "%0.2f", 41.2 - (($db[2] << 8) | $db[1]) / 20 if ($st eq "tempSensor.20");
- $temp = sprintf "%0.1f", 62.3 - (($db[2] << 8) | $db[1]) / 10 if ($st eq "tempSensor.30");
- push @event, "3:temperature:$temp";
- push @event, "3:state:$temp";
- } elsif ($st eq "COSensor.01") {
- # Gas Sensor, CO Sensor (EEP A5-09-01)
- # [untested]
- # $db[3] is the CO concentration where 0x00 = 0 ppm ... 0xFF = 255 ppm
- # $db[1] is the temperature where 0x00 = 0 °C ... 0xFF = 255 °C
- # $db[0] bit D1 temperature sensor available 0 = no, 1 = yes
- my $coChannel1 = $db[3];
- push @event, "3:CO:$coChannel1";
- if ($db[0] & 2) {
- my $temp = $db[1];
- push @event, "3:temperature:$temp";
- }
- push @event, "3:state:$coChannel1";
- } elsif ($st eq "COSensor.02") {
- # Gas Sensor, CO Sensor (EEP A5-09-02)
- # [untested]
- # $db[3] is the voltage where 0x00 = 0 V ... 0xFF = 5.1 V
- # $db[2] is the CO concentration where 0x00 = 0 ppm ... 0xFF = 1020 ppm
- # $db[1] is the temperature where 0x00 = 0 °C ... 0xFF = 51 °C
- # $db[0]_bit_1 temperature sensor available 0 = no, 1 = yes
- my $coChannel1 = $db[2] << 2;
- my $voltage = sprintf "%0.1f", $db[3] * 0.02;
- push @event, "3:CO:$coChannel1";
- if ($db[0] & 2) {
- my $temp = sprintf "%0.1f", $db[1] * 0.2;
- push @event, "3:temperature:$temp";
- }
- push @event, "3:voltage:$voltage";
- push @event, "3:state:$coChannel1";
- } elsif ($st eq "tempHumiCO2Sensor.01") {
- # Gas Sensor, CO2 Sensor (EEP A5-09-04)
- # [Thermokon SR04 CO2 *, Eltako FCOTF63, untested]
- # $db[3] is the humidity where 0x00 = 0 %rH ... 0xC8 = 100 %rH
- # $db[2] is the CO2 concentration where 0x00 = 0 ppm ... 0xFF = 2500 ppm
- # $db[1] is the temperature where 0x00 = 0°C ... 0xFF = +51 °C
- # $db[0] bit D2 humidity sensor available 0 = no, 1 = yes
- # $db[0] bit D1 temperature sensor available 0 = no, 1 = yes
- my $humi = "-";
- my $temp = "-";
- my $airQuality;
- if ($db[0] & 4) {
- $humi = $db[3] >> 1;
- push @event, "3:humidity:$humi";
- }
- my $co2 = sprintf "%d", $db[2] * 10;
- push @event, "3:CO2:$co2";
- if ($db[0] & 2) {
- $temp = sprintf "%0.1f", $db[1] * 51 / 255 ;
- push @event, "3:temperature:$temp";
- }
- if ($co2 <= 400) {
- $airQuality = "high";
- } elsif ($co2 <= 600) {
- $airQuality = "mean";
- } elsif ($co2 <= 1000) {
- $airQuality = "moderate";
- } else {
- $airQuality = "low";
- }
- push @event, "3:airQuality:$airQuality";
- push @event, "3:state:T: $temp H: $humi CO2: $co2 AQ: $airQuality";
- } elsif ($st eq "radonSensor.01") {
- # Gas Sensor, Radon Sensor (EEP A5-09-06)
- # [untested]
- # $db[3]_bit_7 ... $db[2]_bit_6 is the radon activity where 0 = 0 Bq/m3 ... 1023 = 1023 Bq/m3
- my $rn = $db[3] << 2 | $db[2] >> 6;
- push @event, "3:Rn:$rn";
- push @event, "3:state:$rn";
- } elsif ($st eq "vocSensor.01") {
- # Gas Sensor, VOC Sensor (EEP A5-09-05, A5-09-0C)
- # [untested]
- # $db[3]_bit_7 ... $db[2]_bit_0 is the VOC concentration where 0 = 0 ppb ... 65535 = 65535 ppb
- # $db[1] is the VOC identification
- # $db[0]_bit_1 ... $db[0]_bit_0 is the scale multiplier
- my $vocSCM = $db[0] & 3;
- if ($vocSCM == 3) {
- $vocSCM = 10;
- } elsif ($vocSCM == 2) {
- $vocSCM = 1;
- } elsif ($vocSCM == 1) {
- $vocSCM = 0.1;
- } else {
- $vocSCM = 0.01;
- }
- my $vocConc = sprintf "%f", ($db[3] << 8 | $db[2]) * $vocSCM;
- my %vocID = (
- 0 => "VOCT",
- 1 => "Formaldehyde",
- 2 => "Benzene",
- 3 => "Styrene",
- 4 => "Toluene",
- 5 => "Tetrachloroethylene",
- 6 => "Xylene",
- 7 => "n-Hexane",
- 8 => "n-Octane",
- 9 => "Cyclopentane",
- 10 => "Methanol",
- 11 => "Ethanol",
- 12 => "1-Pentanol",
- 13 => "Acetone",
- 14 => "Ethylene Oxide",
- 15 => "Acetaldehyde ue",
- 16 => "Acetic Acid",
- 17 => "Propionice Acid",
- 18 => "Valeric Acid",
- 19 => "Butyric Acid",
- 20 => "Ammoniac",
- 22 => "Hydrogen Sulfide",
- 23 => "Dimethylsulfide",
- 24 => "2-Butanol",
- 25 => "2-Methylpropanol",
- 26 => "Diethyl Ether",
- 27 => "Naphthalene",
- 28 => "4-Phenylcyclohexene",
- 29 => "Limonene",
- 30 => "Tricloroethylene",
- 31 => "Isovaleric Acid",
- 32 => "Indole",
- 33 => "Cadaverine",
- 34 => "Putrescine",
- 35 => "Caproic Acid",
- 255 => "Ozone",
- );
- if (exists $vocID{$db[1]}) {
- push @event, "3:vocName:$vocID{$db[1]}";
- } else {
- push @event, "3:vocName:unknown";
- }
- push @event, "3:concentration:$vocConc";
- push @event, "3:concentrationUnit:" . $db[0] & 4 ? 'ug/m3' : 'ppb';
- push @event, "3:state:$vocConc";
- } elsif ($st eq "particlesSensor.01") {
- # Gas Sensor, Particles Sensor (EEP A5-09-07)
- # [untested]
- # $db[3]_bit_7 ... $db[2]_bit_7 is the particle concentration < 10 µm
- # where 0 = 0 µg/m3 ... 511 = 511 µg/m3
- # $db[2]_bit_6 ... $db[1]_bit_6 is the particle concentration < 2.5 µm
- # where 0 = 0 µg/m3 ... 511 = 511 µg/m3
- # $db[1]_bit_5 ... $db[0]_bit_5 is the particle concentration < 1 µm
- # where 0 = 0 µg/m3 ... 511 = 511 µg/m3
- # $db[0]_bit_2 = 1 = Sensor PM10 active
- # $db[0]_bit_1 = 1 = Sensor PM2_5 active
- # $db[0]_bit_0 = 1 = Sensor PM1 active
- my $pm_10 = "inactive";
- my $pm_2_5 = "inactive";
- my $pm_1 = "inactive";
- if ($db[0] & 4) {$pm_10 = $db[3] << 1 | $db[2] >> 7;}
- if ($db[0] & 2) {$pm_2_5 = ($db[2] & 0x7F) << 1 | $db[1] >> 7;}
- if ($db[0] & 1) {$pm_1 = ($db[1] & 0x3F) << 3 | $db[0] >> 5;}
- push @event, "3:particles_10:$pm_10";
- push @event, "3:particles_2_5:$pm_2_5";
- push @event, "3:particles_1:$pm_1";
- push @event, "3:state:PM10: $pm_10 PM2_5: $pm_2_5 PM1: $pm_1";
- } elsif ($st eq "CO2Sensor.01") {
- # CO2 Sensor (EEP A5-09-08, A5-09-09)
- # [untested]
- # $db[1] is the CO2 concentration where 0x00 = 0 ppm ... 0xFF = 2000 ppm
- # $db[0]_bit_2 is power failure detection
- my $co2 = $db[1] / 255 * 2000;
- push @event, "3:powerFailureDetection:" . ($db[0] & 4 ? "detected" : "not_detected");
- push @event, "3:CO2:$co2";
- push @event, "3:state:$co2";
- } elsif ($st eq "HSensor.01") {
- # H Sensor (EEP A5-09-0A)
- # [untested]
- # $db[3]_$db[2] is the H concentration where 0x00 = 0 ppm ... 0xFFFF = 2000 ppm
- my $hydro = ($db[3] << 8 | $db[2]) / 65535 * 2000;
- push @event, "3:voltage:" . sprintf("%0.1f", (($db[0] & 0xF0) >> 4) / 15 * 3 + 2) if ($db[0] & 1);
- push @event, "3:temperature:" . sprintf("%0.1f", $db[1] / 255 * 80 - 20) if ($db[0] & 2);
- push @event, "3:H:" . sprintf "%0.2f", $hydro;
- push @event, "3:state:" . sprintf "%0.2f", $hydro;
- } elsif ($st eq "radiationSensor.01") {
- # Radiation Sensor (EEP A5-09-0B)
- # [untested]
- # $db[2]_$db[1] is the radioactivity where 0x00 = 0 ... 0xFFFF = 65535
- my %scaleMulti = (
- 0 => 0.001,
- 1 => 0.01,
- 2 => 0.1,
- 3 => 1,
- 4 => 10,
- 5 => 100,
- 6 => 1000,
- 7 => 10000,
- 8 => 100000
- );
- my $scaleMulti = ($db[0] & 0xF0) >> 4;
- my $scaleDecimals;
- if ($scaleMulti <= 2) {
- $scaleDecimals = "%0." . (3 - $scaleMulti) . "f";
- } else {
- $scaleDecimals = "%d"
- }
- $scaleMulti = $scaleMulti{$scaleMulti} if (exists $scaleMulti{$scaleMulti});
- my %unit = (
- 0 => "uSv/h",
- 1 => "cpm",
- 2 => "Bq/L",
- 3 => "Bq/kg"
- );
- my $unit = ($db[0] & 6) >> 1;
- $unit = $unit{$unit};
- my $radioactivity = $db[2] << 8 | $db[1];
- push @event, "3:radioactivity:" . sprintf "$scaleDecimals", $radioactivity * $scaleMulti;
- push @event, "3:radioactivityUnit:$unit";
- push @event, "3:voltage:" . sprintf("%0.1f", (($db[3] & 0xF0) >> 4) / 15 * 3 + 2) if ($db[0] & 1);
- push @event, "3:state:" . sprintf "$scaleDecimals", $radioactivity * $scaleMulti;
- } elsif ($st eq "roomSensorControl.05") {
- # Room Sensor and Control Unit (EEP A5-10-01 ... A5-10-0D)
- # [Eltako FTR55D, FTR55H, Thermokon SR04 *, Thanos SR *, untested]
- # $db[3] is the fan speed or night reduction for Eltako
- # $db[2] is the setpoint where 0x00 = min ... 0xFF = max or
- # reference temperature for Eltako where 0x00 = 0°C ... 0xFF = 40°C
- # $db[1] is the temperature where 0x00 = +40°C ... 0xFF = 0°C
- # $db[0]_bit_0 is the occupy button, pushbutton or slide switch
- my $temp = sprintf "%0.1f", 40 - $db[1] / 6.375;
- if ($manufID eq "00D") {
- my $nightReduction = 0;
- $nightReduction = 1 if ($db[3] == 0x06);
- $nightReduction = 2 if ($db[3] == 0x0C);
- $nightReduction = 3 if ($db[3] == 0x13);
- $nightReduction = 4 if ($db[3] == 0x19);
- $nightReduction = 5 if ($db[3] == 0x1F);
- my $setpointTemp = sprintf "%0.1f", $db[2] / 6.375;
- push @event, "3:state:T: $temp SPT: $setpointTemp NR: $nightReduction";
- push @event, "3:nightReduction:$nightReduction";
- push @event, "3:setpointTemp:$setpointTemp";
- } else {
- my $fspeed = 3;
- $fspeed = 2 if ($db[3] >= 145);
- $fspeed = 1 if ($db[3] >= 165);
- $fspeed = 0 if ($db[3] >= 190);
- $fspeed = "auto" if ($db[3] >= 210);
- my $switch = $db[0] & 1 ? "on" : "off";
- push @event, "3:state:T: $temp SP: $db[2] F: $fspeed SW: $switch";
- push @event, "3:fanStage:$fspeed";
- push @event, "3:switch:$switch";
- push @event, "3:setpoint:$db[2]";
- my $setpointScaled = EnOcean_ReadingScaled($hash, $db[2], 0, 255);
- if (defined $setpointScaled) {
- push @event, "3:setpointScaled:" . $setpointScaled;
- }
- }
- push @event, "3:temperature:$temp";
- } elsif ($st eq "roomSensorControl.01") {
- # Room Sensor and Control Unit (EEP A5-04-01, A5-10-10 ... A5-10-14)
- # [Thermokon SR04 * rH, Thanus SR *, untested]
- # $db[3] is the setpoint where 0x00 = min ... 0xFF = max
- # $db[2] is the humidity where 0x00 = 0%rH ... 0xFA = 100%rH
- # $db[1] is the temperature where 0x00 = 0°C ... 0xFA = +40°C
- # $db[0] bit D0 is the occupy button, pushbutton or slide switch
- my $temp = sprintf "%0.1f", $db[1] * 40 / 250;
- my $humi = sprintf "%d", $db[2] / 2.5;
- my $switch = $db[0] & 1;
- push @event, "3:humidity:$humi";
- push @event, "3:temperature:$temp";
- if ($manufID eq "039") {
- my $brightness = sprintf "%d", $db[3] * 117;
- push @event, "3:brightness:$brightness";
- push @event, "3:state:T: $temp H: $humi B: $brightness";
- } else {
- push @event, "3:setpoint:$db[3]";
- push @event, "3:state:T: $temp H: $humi SP: $db[3] SW: $switch";
- push @event, "3:switch:$switch";
- my $setpointScaled = EnOcean_ReadingScaled($hash, $db[3], 0, 255);
- if (defined $setpointScaled) {
- push @event, "3:setpointScaled:" . $setpointScaled;
- }
- }
- } elsif ($st eq "roomSensorControl.02") {
- # Room Sensor and Control Unit (A5-10-15 ... A5-10-17)
- # [untested]
- # $db[2] bit D7 ... D2 is the setpoint where 0 = min ... 63 = max
- # $db[2] bit D1 ... $db[1] bit D0 is the temperature where 0 = -10°C ... 1023 = +41.2°C
- # $db[0]_bit_0 is Occupany Button where 0 = pressed, 1 = released
- my $temp = sprintf "%0.2f", -10 + ((($db[2] & 3) << 8) | $db[1]) / 19.98;
- my $setpoint = ($db[2] & 0xFC) >> 2;
- my $presence = $db[0] & 1 ? "absent" : "present";
- push @event, "3:state:T: $temp SP: $setpoint P: $presence";
- push @event, "3:presence:$presence";
- push @event, "3:setpoint:$setpoint";
- push @event, "3:temperature:$temp";
- my $setpointScaled = EnOcean_ReadingScaled($hash, $db[2], 0, 255);
- if (defined $setpointScaled) {
- push @event, "3:setpointScaled:" . $setpointScaled;
- }
- } elsif ($st eq "roomSensorControl.18") {
- # Room Sensor and Control Unit (A5-10-18)
- # [untested]
- # $db[3] is the illuminance where min 0x00 = 0 lx, max 0xFA = 1000 lx
- # $db[2] is the setpoint where 250 = 0 °C ... 0 = 40 °C
- # $db[1] is the temperature where 250 = 0 °C ... 0 = 40 °C
- # $db[0]_bit_6 ... $db[0]_bit_4 is the fan speed
- # $db[0]_bit_1 is Occupany enable where 0 = enabled, 1 = disabled
- # $db[0]_bit_0 is Occupany Button where 0 = pressed, 1 = released
- my $lux = $db[3] << 2;
- if ($db[3] == 251) {$lux = "over range";}
- my $setpoint = sprintf "%0.1f", 40 - $db[2] * 40 / 250;
- my $temp = sprintf "%0.1f", 40 - $db[1] * 40 / 250;
- my $fanSpeed;
- if ((($db[0] & 0x70) >> 4) == 0) {
- $fanSpeed = "auto";
- } elsif ((($db[0] & 0x70) >> 4) == 7) {
- $fanSpeed = "off";
- } else {
- $fanSpeed = (($db[0] & 0x70) >> 4) - 1;
- }
- my $presence;
- if ($db[0] & 2) {
- $presence = "disabled";
- } else {
- $presence = $db[0] & 1 ? "absent" : "present";
- }
- push @event, "3:brightness:$lux";
- push @event, "3:fan:$fanSpeed";
- push @event, "3:presence:$presence";
- push @event, "3:setpoint:$setpoint";
- push @event, "3:temperature:$temp";
- push @event, "3:state:T: $temp B: $lux F: $fanSpeed SP: $setpoint P: $presence";
- } elsif ($st eq "roomSensorControl.19") {
- # Room Sensor and Control Unit (A5-10-19)
- # [untested]
- # $db[3] is the humidity where min 0x00 = 0 %rH, max 0xFA = 10 %rH
- # $db[2] is the setpoint where 250 = 0 °C ... 0 = 40 °C
- # $db[1] is the temperature where 250 = 0 °C ... 0 = 40 °C
- # $db[0]_bit_6 ... $db[0]_bit_4 is the fan speed
- # $db[0]_bit_1 is Occupany Button where 0 = pressed, 1 = released
- # $db[0]_bit_0 is Occupany enable where 0 = enabled, 1 = disabled
- my $humi = $db[3] / 2.5;
- my $setpoint = sprintf "%0.1f", 40 - $db[2] * 40 / 250;
- my $temp = sprintf "%0.1f", 40 - $db[1] * 40 / 250;
- my $fanSpeed;
- if ((($db[0] & 0x70) >> 4) == 0) {
- $fanSpeed = "auto";
- } elsif ((($db[0] & 0x70) >> 4) == 7) {
- $fanSpeed = "off";
- } else {
- $fanSpeed = (($db[0] & 0x70) >> 4) - 1;
- }
- my $presence;
- if ($db[0] & 1) {
- $presence = "disabled";
- } else {
- $presence = $db[0] & 2 ? "absent" : "present";
- }
- push @event, "3:fan:$fanSpeed";
- push @event, "3:humidity:$humi";
- push @event, "3:presence:$presence";
- push @event, "3:setpoint:$setpoint";
- push @event, "3:temperature:$temp";
- push @event, "3:state:T: $temp H: $humi F: $fanSpeed SP: $setpoint P: $presence";
- } elsif ($st eq "roomSensorControl.1A") {
- # Room Sensor and Control Unit (A5-10-1A)
- # [untested]
- # $db[3] is the voltage where 0x00 = 0 V ... 0xFA = 5.0 V
- # $db[3] > 0xFA is error code
- # $db[2] is the setpoint where 250 = 0 °C ... 0 = 40 °C
- # $db[1] is the temperature where 250 = 0 °C ... 0 = 40 °C
- # $db[0]_bit_6 ... $db[0]_bit_4 is the fan speed
- # $db[0]_bit_1 is Occupany enable where 0 = enabled, 1 = disabled
- # $db[0]_bit_0 is Occupany Button where 0 = pressed, 1 = released
- my $voltage = sprintf "%0.1f", $db[3] * 0.02;
- if ($db[3] > 250) {push @event, "3:errorCode:$db[3]";}
- my $setpoint = sprintf "%0.1f", 40 - $db[2] * 40 / 250;
- my $temp = sprintf "%0.1f", 40 - $db[1] * 40 / 250;
- my $fanSpeed;
- if ((($db[0] & 0x70) >> 4) == 0) {
- $fanSpeed = "auto";
- } elsif ((($db[0] & 0x70) >> 4) == 7) {
- $fanSpeed = "off";
- } else {
- $fanSpeed = (($db[0] & 0x70) >> 4) - 1;
- }
- my $presence;
- if ($db[0] & 2) {
- $presence = "disabled";
- } else {
- $presence = $db[0] & 1 ? "absent" : "present";
- }
- push @event, "3:fan:$fanSpeed";
- push @event, "3:presence:$presence";
- push @event, "3:setpoint:$setpoint";
- push @event, "3:temperature:$temp";
- push @event, "3:voltage:$voltage";
- push @event, "3:state:T: $temp F: $fanSpeed SP: $setpoint P: $presence U: $voltage";
- } elsif ($st eq "roomSensorControl.1B") {
- # Room Sensor and Control Unit (A5-10-1B)
- # [untested]
- # $db[3] is the voltage where 0x00 = 0 V ... 0xFA = 5.0 V
- # $db[3] > 0xFA is error code
- # $db[2] is the illuminance where min 0x00 = 0 lx, max 0xFA = 1000 lx
- # $db[1] is the temperature where 250 = 0 °C ... 0 = 40 °C
- # $db[0]_bit_6 ... $db[0]_bit_4 is the fan speed
- # $db[0]_bit_1 is Occupany enable where 0 = enabled, 1 = disabled
- # $db[0]_bit_0 is Occupany Button where 0 = pressed, 1 = released
- my $voltage = sprintf "%0.1f", $db[3] * 0.02;
- if ($db[3] > 250) {push @event, "3:errorCode:$db[3]";}
- my $lux = $db[2] << 2;
- if ($db[2] == 251) {$lux = "over range";}
- my $temp = sprintf "%0.1f", 40 - $db[1] * 40 / 250;
- my $fanSpeed;
- if ((($db[0] & 0x70) >> 4) == 0) {
- $fanSpeed = "auto";
- } elsif ((($db[0] & 0x70) >> 4) == 7) {
- $fanSpeed = "off";
- } else {
- $fanSpeed = (($db[0] & 0x70) >> 4) - 1;
- }
- my $presence;
- if ($db[0] & 2) {
- $presence = "disabled";
- } else {
- $presence = $db[0] & 1 ? "absent" : "present";
- }
- push @event, "3:brightness:$lux";
- push @event, "3:fan:$fanSpeed";
- push @event, "3:presence:$presence";
- push @event, "3:temperature:$temp";
- push @event, "3:voltage:$voltage";
- push @event, "3:state:T: $temp B: $lux F: $fanSpeed P: $presence U: $voltage";
- } elsif ($st eq "roomSensorControl.1C") {
- # Room Sensor and Control Unit (A5-10-1C)
- # [untested]
- # $db[3] is the illuminance where min 0x00 = 0 lx, max 0xFA = 1000 lx
- # $db[2] is the illuminance setpoint where min 0x00 = 0 lx, max 0xFA = 1000 lx
- # $db[1] is the temperature where 250 = 0 °C ... 0 = 40 °C
- # $db[0]_bit_6 ... $db[0]_bit_4 is the fan speed
- # $db[0]_bit_1 is Occupany enable where 0 = enabled, 1 = disabled
- # $db[0]_bit_0 is Occupany Button where 0 = pressed, 1 = released
- my $lux = $db[3] << 2;
- if ($db[3] == 251) {$lux = "over range";}
- my $setpoint = $db[2] << 2;
- my $temp = sprintf "%0.1f", 40 - $db[1] * 40 / 250;
- my $fanSpeed;
- if ((($db[0] & 0x70) >> 4) == 0) {
- $fanSpeed = "auto";
- } elsif ((($db[0] & 0x70) >> 4) == 7) {
- $fanSpeed = "off";
- } else {
- $fanSpeed = (($db[0] & 0x70) >> 4) - 1;
- }
- my $presence;
- if ($db[0] & 2) {
- $presence = "disabled";
- } else {
- $presence = $db[0] & 1 ? "absent" : "present";
- }
- push @event, "3:brightness:$lux";
- push @event, "3:fan:$fanSpeed";
- push @event, "3:presence:$presence";
- push @event, "3:setpoint:$setpoint";
- push @event, "3:temperature:$temp";
- push @event, "3:state:T: $temp B: $lux F: $fanSpeed SP: $setpoint P: $presence";
- } elsif ($st eq "roomSensorControl.1D") {
- # Room Sensor and Control Unit (A5-10-1D)
- # [untested]
- # $db[3] is the humidity where min 0x00 = 0 %rH, max 0xFA = 10 %rH
- # $db[2] is the humidity setpoint where min 0x00 = 0 %rH, max 0xFA = 10 %rH
- # $db[1] is the temperature where 250 = 0 °C ... 0 = 40 °C
- # $db[0]_bit_6 ... $db[0]_bit_4 is the fan speed
- # $db[0]_bit_1 is Occupany enable where 0 = enabled, 1 = disabled
- # $db[0]_bit_0 is Occupany Button where 0 = pressed, 1 = released
- my $humi = sprintf "%d", $db[3] / 2.5;
- my $setpoint = $db[2] / 2.5;
- my $temp = sprintf "%0.1f", 40 - $db[1] * 40 / 250;
- my $fanSpeed;
- if ((($db[0] & 0x70) >> 4) == 0) {
- $fanSpeed = "auto";
- } elsif ((($db[0] & 0x70) >> 4) == 7) {
- $fanSpeed = "off";
- } else {
- $fanSpeed = (($db[0] & 0x70) >> 4) - 1;
- }
- my $presence;
- if ($db[0] & 2) {
- $presence = "disabled";
- } else {
- $presence = $db[0] & 1 ? "absent" : "present";
- }
- push @event, "3:fan:$fanSpeed";
- push @event, "3:humidity:$humi";
- push @event, "3:presence:$presence";
- push @event, "3:setpoint:$setpoint";
- push @event, "3:temperature:$temp";
- push @event, "3:state:T: $temp H: $humi F: $fanSpeed SP: $setpoint P: $presence";
- } elsif ($st eq "roomSensorControl.1F") {
- # Room Sensor and Control Unit (A5-10-1F)
- # [untested]
- # $db[3] is the fan speed
- # $db[2] is the setpoint where 0 = 0 ... 255 = 255
- # $db[1] is the temperature where 250 = 0 °C ... 0 = 40 °C
- # $db[0]_bit_6 ... $db[0]_bit_4 is the fan speed
- # $db[0]_bit_6 ... $db[0]_bit_4 are flags
- # $db[0]_bit_1 is Occupany enable where 0 = enabled, 1 = disabled
- # $db[0]_bit_0 is Occupany Button where 0 = pressed, 1 = released
- my $fanSpeed = "unknown";
- if ($db[0] & 0x10) {
- $fanSpeed = 3;
- $fanSpeed = 2 if ($db[3] >= 145);
- $fanSpeed = 1 if ($db[3] >= 165);
- $fanSpeed = 0 if ($db[3] >= 190);
- $fanSpeed = "auto" if ($db[3] >= 210);
- }
- my $setpoint = "unknown";
- $setpoint = $db[2] if ($db[0] & 0x20);
- my $temp = "unknown";
- $temp = sprintf "%0.1f", 40 - $db[1] * 40 / 250 if ($db[0] & 0x40);
- my $presence = "unknown";
- $presence = "absent" if (!($db[0] & 2));
- $presence = "present" if (!($db[0] & 1));
- push @event, "3:fan:$fanSpeed";
- push @event, "3:presence:$presence";
- push @event, "3:setpoint:$setpoint";
- push @event, "3:temperature:$temp";
- push @event, "3:state:T: $temp F: $fanSpeed SP: $setpoint P: $presence";
- my $setpointScaled = EnOcean_ReadingScaled($hash, $db[2], 0, 255);
- if (defined $setpointScaled) {
- push @event, "3:setpointScaled:" . $setpointScaled;
- }
- } elsif ($st eq "roomSensorControl.20") {
- # Room Operation Panel (A5-10-20, A5-10-21)
- # [untested]
- # $db[3] is the setpoint where 0 = 0 ... 255 = 255
- # $db[2] is the humidity setpoint where min 0x00 = 0 %rH, max 0xFA = 100 %rH
- # $db[1] is the temperature where 250 = 0 °C ... 0 = 40 °C
- # $db[0]_bit_6 ... $db[0]_bit_5 is setpoint mode
- # $db[0]_bit_4 is battery state 0 = ok, 1 = low
- # $db[0]_bit_0 is user activity where 0 = no, 1 = yes
- my $humi = sprintf "%d", $db[2] / 2.5;
- my $setpoint = $db[3];
- my $temp = sprintf "%0.1f", 40 - $db[1] * 40 / 250;
- my $setpointMode;
- if ((($db[0] & 0x60) >> 5) == 3) {
- $setpointMode = "reserved";
- } elsif ((($db[0] & 0x60) >> 5) == 2) {
- $setpointMode = "auto";
- } elsif ((($db[0] & 0x60) >> 1) == 1){
- $setpointMode = "frostProtection";
- } else {
- $setpointMode = "setpoint";
- }
- my $battery = ($db[0] & 0x10) ? "low" : "ok";
- push @event, "3:activity:" . ($db[0] & 1 ? "yes" : "no");
- push @event, "3:battery:$battery";
- push @event, "3:humidity:$humi";
- push @event, "3:setpoint:$setpoint";
- push @event, "3:setpointMode:$setpointMode";
- push @event, "3:temperature:$temp";
- push @event, "3:state:T: $temp H: $humi SP: $setpoint B: $battery";
- my $setpointScaled = EnOcean_ReadingScaled($hash, $db[3], 0, 255);
- if (defined $setpointScaled) {
- push @event, "3:setpointScaled:" . $setpointScaled;
- }
- } elsif ($st eq "roomSensorControl.22") {
- # Room Operation Panel (A5-10-22, A5-10-23)
- my $setpoint = $db[3];
- my $humi = sprintf "%d", $db[2] / 2.5;
- my $temp = sprintf "%0.1f", $db[1] * 40 / 250;
- my $fanSpeed;
- if ((($db[0] & 0xE0) >> 5) == 4) {
- $fanSpeed = 3;
- } elsif ((($db[0] & 0xE0) >> 5) == 3) {
- $fanSpeed = 2;
- } elsif ((($db[0] & 0xE0) >> 5) == 2) {
- $fanSpeed = 1;
- } elsif ((($db[0] & 0xE0) >> 5) == 1){
- $fanSpeed = "off";
- } else {
- $fanSpeed = "auto";
- }
- my $occupancy = ($db[0] & 1) ? "occupied" : "unoccupied";
- push @event, "3:occupancy:$occupancy";
- push @event, "3:humidity:$humi";
- push @event, "3:setpoint:$setpoint";
- push @event, "3:fanSpeed:$fanSpeed";
- push @event, "3:temperature:$temp";
- push @event, "3:state:T: $temp H: $humi SP: $setpoint F: $fanSpeed O: $occupancy";
- my $setpointScaled = EnOcean_ReadingScaled($hash, $db[3], 0, 255);
- if (defined $setpointScaled) {
- push @event, "3:setpointScaled:" . $setpointScaled;
- }
- } elsif ($st eq "tempHumiSensor.02") {
- # Temperatur and Humidity Sensor(EEP A5-04-02)
- # [Eltako FAFT60, FIFT63AP]
- # $db[3] is the voltage where 0x59 = 2.5V ... 0x9B = 4V, only at Eltako
- # $db[2] is the humidity where 0x00 = 0%rH ... 0xFA = 100%rH
- # $db[1] is the temperature where 0x00 = -20°C ... 0xFA = +60°C
- my $humi = sprintf "%d", $db[2] / 2.5;
- my $temp = sprintf "%0.1f", -20 + $db[1] * 80 / 250;
- my $battery = "unknown";
- if ($manufID eq "00D") {
- # Eltako sensor
- my $voltage = sprintf "%0.1f", $db[3] * 6.58 / 255;
- my $energyStorage = "unknown";
- if ($db[3] <= 0x58) {
- $energyStorage = "empty";
- $battery = "low";
- }
- elsif ($db[3] <= 0xDC) {
- $energyStorage = "charged";
- $battery = "ok";
- }
- else {
- $energyStorage = "full";
- $battery = "ok";
- }
- push @event, "3:battery:$battery";
- push @event, "3:energyStorage:$energyStorage";
- push @event, "3:voltage:$voltage";
- }
- push @event, "3:state:T: $temp H: $humi B: $battery";
- push @event, "3:humidity:$humi";
- push @event, "3:temperature:$temp";
- } elsif ($st eq "tempHumiSensor.03") {
- # Temperatur and Humidity Sensor(EEP A5-04-03)
- # [untested]
- # $db[3] is the humidity where 0x00 = 0%rH ... 0xFF = 100%rH
- # $db[2] .. $db[1] is the temperature where 0x00 = -20°C ... 0x3FF = +60°C
- my $humi = sprintf "%d", $db[3] / 2.55;
- my $temp = sprintf "%0.1f", -20 + ($db[2] << 8 | $db[1]) * 80 / 1023;
- push @event, "3:state:T: $temp H: $humi";
- push @event, "3:humidity:$humi";
- push @event, "3:temperature:$temp";
- push @event, "3:telegramType:" . ($db[0] & 1 ? "event" : "heartbeat");
- } elsif ($st eq "baroSensor.01") {
- # Barometric Sensor(EEP A5-04-03)
- # [untested]
- # $db[3] .. $db[2] is the barometric where 0x00 = 500 hPa ... 0x3FF = 1150 hPa
- my $baro = sprintf "%d", 500 + ($db[2] << 8 | $db[1]) * 650 / 1023;
- push @event, "3:state:$baro";
- push @event, "3:airPressure:$baro";
- push @event, "3:telegramType:" . ($db[0] & 1 ? "event" : "heartbeat");
- } elsif ($st eq "lightSensor.01") {
- # Light Sensor (EEP A5-06-01)
- # [Eltako FAH60, FAH63, FIH63, Thermokon SR65 LI, untested]
- # $db[3] is the voltage where 0x00 = 0 V ... 0xFF = 5.1 V
- # $db[3] is the low illuminance for Eltako devices where
- # min 0x00 = 0 lx, max 0xFF = 100 lx, if $db[2] = 0
- # $db[2] is the illuminance (ILL2) where min 0x00 = 300 lx, max 0xFF = 30000 lx
- # $db[1] is the illuminance (ILL1) where min 0x00 = 600 lx, max 0xFF = 60000 lx
- # $db[0]_bit_0 is Range select where 0 = ILL1, 1 = ILL2
- my $lux;
- my $voltage = "unknown";
- if ($manufID eq "00D") {
- if($db[2] == 0) {
- $lux = sprintf "%d", $db[3] * 100 / 255;
- } else {
- $lux = sprintf "%d", $db[2] * 116.48 + 300;
- }
- } else {
- $voltage = sprintf "%0.1f", $db[3] * 0.02;
- if($db[0] & 1) {
- $lux = sprintf "%d", $db[2] * 116.48 + 300;
- } else {
- $lux = sprintf "%d", $db[1] * 232.94 + 600;
- }
- push @event, "3:voltage:$voltage";
- }
- push @event, "3:brightness:$lux";
- push @event, "3:state:$lux";
- } elsif ($st eq "lightSensor.02") {
- # Light Sensor (EEP A5-06-02)
- # $db[3] is the voltage where 0x00 = 0 V ... 0xFF = 5.1 V
- # $db[2] is the illuminance (ILL2) where min 0x00 = 0 lx, max 0xFF = 510 lx
- # $db[1] is the illuminance (ILL1) where min 0x00 = 0 lx, max 0xFF = 1020 lx
- # $db[0]_bit_0 is Range select where 0 = ILL1, 1 = ILL2
- my $lux;
- my $voltage = sprintf "%0.1f", $db[3] * 0.02;
- if($db[0] & 1) {
- $lux = $db[2] << 1;
- } else {
- $lux = $db[1] << 2;
- }
- push @event, "3:voltage:$voltage";
- push @event, "3:brightness:$lux";
- push @event, "3:state:$lux";
- } elsif ($st eq "lightSensor.03") {
- # Light Sensor (EEP A5-06-03)
- # $db[3] is the voltage where 0x00 = 0 V ... 0xFA = 5.0 V
- # $db[3] > 0xFA is error code
- # $db[2]_bit_7 ... $db[1]_bit_6 is the illuminance where min 0x000 = 0 lx, max 0x3E8 = 1000 lx
- my $lux = $db[2] << 2 | $db[1] >> 6;
- if ($lux == 1001) {$lux = "over range";}
- my $voltage = sprintf "%0.1f", $db[3] * 0.02;
- if ($db[3] > 250) {push @event, "3:errorCode:$db[3]";}
- push @event, "3:voltage:$voltage";
- push @event, "3:brightness:$lux";
- push @event, "3:state:$lux";
- } elsif ($st eq "lightSensor.04") {
- # Light Sensor (EEP A5-06-04)
- my $temperature;
- if ($db[0] & 2) {
- $temperature = sprintf "%0.1f", $db[3] * 80 / 255 - 20;
- push @event, "3:temperature:$temperature";
- } else {
- $temperature = '-';
- CommandDeleteReading(undef, "$name temperature");
- }
- my $brightness;
- if ($db[0] & 1) {
- $brightness = $db[2] << 8 | $db[1];
- push @event, "3:brightness:$brightness";
- } else {
- $brightness = '-';
- CommandDeleteReading(undef, "$name brightness");
- }
- my $energyStorage = sprintf "%d", ($db[0] >> 4) * 100 / 15;
- my $battery;
- if ($energyStorage <= 6) {
- $battery = 'low';
- push @event, "3:battery:low";
- push @event, "3:energyStorage:$energyStorage";
- } else {
- $battery = 'ok';
- push @event, "3:battery:ok";
- push @event, "3:energyStorage:$energyStorage";
- }
- push @event, "3:state:T: $temperature E: $brightness B: $battery";
- } elsif ($st eq "lightSensor.05") {
- # Light Sensor (EEP A5-06-05)
- # $db[3] is the voltage where 0x00 = 0 V ... 0xFF = 5.1 V
- # $db[2] is the illuminance (ILL2) where min 0x00 = 0 lx, max 0xFF = 5100 lx
- # $db[1] is the illuminance (ILL1) where min 0x00 = 0 lx, max 0xFF = 1020000 lx
- # $db[0]_bit_0 is Range select where 0 = ILL1, 1 = ILL2
- my $lux;
- if($db[0] & 1) {
- $lux = sprintf "%d", $db[2] * 20;
- } else {
- $lux = sprintf "%d", $db[1] * 40;
- }
- push @event, "3:voltage:" . sprintf "%0.1f", $db[3] * 0.02;
- push @event, "3:brightness:$lux";
- push @event, "3:state:$lux";
- } elsif ($st eq "occupSensor.01") {
- # Occupancy Sensor (EEP A5-07-01)
- # $db[3] is the voltage where 0x00 = 0 V ... 0xFA = 5.0 V
- # $db[3] > 0xFA is error code
- # $db[2] is solar panel current where =0 uA ... 0xFF = 127 uA
- # $db[1] is PIR Status (motion) where 0 ... 127 = off, 128 ... 255 = on
- my $motion = "off";
- if ($db[1] >= 128) {$motion = "on";}
- if ($db[0] & 1) {push @event, "3:voltage:" . sprintf "%0.1f", $db[3] * 0.02;}
- if ($db[3] > 250) {push @event, "3:errorCode:$db[3]";}
- if ($manufID eq "00B") {
- push @event, "3:current:" . sprintf "%0.1f", $db[2] / 2;
- if ($db[0] & 2) {
- push @event, "3:sensorType:ceiling";
- } else {
- push @event, "3:sensorType:wall";
- }
- }
- if ($model eq "tracker") {
- @{$hash->{helper}{motionTimer}} = ($hash, 'motion', 'off', 1, 5);
- @{$hash->{helper}{stateTimer}} = ($hash, 'state', 'off', 1, 5);
- RemoveInternalTimer($hash->{helper}{motionTimer});
- RemoveInternalTimer($hash->{helper}{stateTimer});
- InternalTimer(gettimeofday() + AttrVal($name, 'trackerWakeUpCycle', 30) * 1.1, 'EnOcean_readingsSingleUpdate', $hash->{helper}{motionTimer}, 0);
- InternalTimer(gettimeofday() + AttrVal($name, 'trackerWakeUpCycle', 30) * 1.1, 'EnOcean_readingsSingleUpdate', $hash->{helper}{stateTimer}, 0);
- }
- if (!exists($hash->{helper}{lastVoltage}) || $hash->{helper}{lastVoltage} != $db[3]) {
- push @event, "3:battery:" . ($db[3] * 0.02 > 2.8 ? "ok" : "low");
- $hash->{helper}{lastVoltage} = $db[3];
- }
- push @event, "3:button:" . ($db[0] & 4 ? "released" : "pressed") if ($manufID eq "7FF");
- push @event, "3:motion:$motion";
- push @event, "3:state:$motion";
- } elsif ($st eq "occupSensor.02") {
- # Occupancy Sensor (EEP A5-07-02)
- # $db[3] is the voltage where 0x00 = 0 V ... 0xFA = 5.0 V
- # $db[3] > 0xFA is error code
- # $db[0]_bit_7 is PIR Status (motion) where 0 = off, 1 = on
- my $motion = $db[0] >> 7 ? "on" : "off";
- if ($db[3] > 250) {push @event, "3:errorCode:$db[3]";}
- push @event, "3:battery:" . ($db[3] * 0.02 > 2.9 ? "ok" : "low");
- push @event, "3:motion:$motion";
- push @event, "3:voltage:" . sprintf "%0.1f", $db[3] * 0.02;
- push @event, "3:state:$motion";
- } elsif ($st eq "occupSensor.03") {
- # Occupancy Sensor (EEP A5-07-03)
- # $db[3] is the voltage where 0x00 = 0 V ... 0xFA = 5.0 V
- # $db[3] > 0xFA is error code
- # $db[2]_bit_7 ... $db[1]_bit_6 is the illuminance where min 0x000 = 0 lx, max 0x3E8 = 1000 lx
- # $db[0]_bit_7 is PIR Status (motion) where 0 = off, 1 = on
- my $motion = $db[0] >> 7 ? "on" : "off";
- my $lux = $db[2] << 2 | $db[1] >> 6;
- if ($lux == 1001) {$lux = "over range";}
- my $voltage = sprintf "%0.1f", $db[3] * 0.02;
- if ($db[3] > 250) {push @event, "3:errorCode:$db[3]";}
- push @event, "3:battery:" . ($db[3] * 0.02 > 2.9 ? "ok" : "low");
- push @event, "3:brightness:$lux";
- push @event, "3:motion:$motion";
- push @event, "3:voltage:$voltage";
- push @event, "3:state:M: $motion E: $lux U: $voltage";
- } elsif ($st =~ m/^lightTempOccupSensor/) {
- # Light, Temperatur and Occupancy Sensor (EEP A5-08-01 ... A5-08-03)
- # $db[3] is the voltage where 0x00 = 0 V ... 0xFF = 5.1 V
- # $db[2] is the illuminance where min 0x00 = 0 lx, max 0xFF = 510 lx, 1020 lx, (2048 lx)
- # $db[1] is the temperature whrere 0x00 = 0 °C ... 0xFF = 51 °C or -30 °C ... 50°C
- # $db[0]_bit_1 is PIR Status (motion) where 0 = on, 1 = off
- # $db[0]_bit_0 is Occupany Button where 0 = pressed, 1 = released
- my $lux;
- my $temp;
- my $voltage = sprintf "%0.1f", $db[3] * 0.02;
- my $motion = $db[0] & 2 ? "off" : "on";
- my $presence = $db[0] & 1 ? "absent" : "present";
- if ($st eq "lightTempOccupSensor.01") {
- # Light, Temperatur and Occupancy Sensor (EEP A5-08-01)
- # [Eltako FABH63, FBH55, FBH63, FIBH63]
- if ($manufID eq "00D") {
- $lux = sprintf "%d", $db[2] * 2048 / 255;
- push @event, "3:state:M: $motion E: $lux";
- } else {
- $lux = $db[2] << 1;
- $temp = sprintf "%0.1f", $db[1] * 0.2;
- push @event, "3:state:M: $motion E: $lux P: $presence T: $temp U: $voltage";
- push @event, "3:presence:$presence";
- push @event, "3:temperature:$temp";
- push @event, "3:voltage:$voltage";
- }
- } elsif ($st eq "lightTempOccupSensor.02") {
- # Light, Temperatur and Occupancy Sensor (EEP A5-08-02)
- $lux = $db[2] << 2;
- $temp = sprintf "%0.1f", $db[1] * 0.2;
- push @event, "3:state:M: $motion E: $lux P: $presence T: $temp U: $voltage";
- push @event, "3:presence:$presence";
- push @event, "3:temperature:$temp";
- push @event, "3:voltage:$voltage";
- } elsif ($st eq "lightTempOccupSensor.03") {
- # Light, Temperatur and Occupancy Sensor (EEP A5-08-03)
- $lux = $db[2] * 6;
- $temp = sprintf "%0.1f", -30 + $db[1] * 80 / 255;
- push @event, "3:state:M: $motion E: $lux P: $presence T: $temp U: $voltage";
- push @event, "3:presence:$presence";
- push @event, "3:temperature:$temp";
- push @event, "3:voltage:$voltage";
- }
- push @event, "3:brightness:$lux";
- push @event, "3:motion:$motion";
- } elsif ($st eq "lightCtrlState.01") {
- # Lighting Controller State (EEP A5-11-01)
- # $db[3] is the illumination where 0x00 = 0 lx ... 0xFF = 510 lx
- # $db[2] is the illumination Setpoint where 0x00 = 0 ... 0xFF = 255
- # $db[1] is the Dimming Output Level where 0x00 = 0 ... 0xFF = 255
- # $db[0]_bit_7 is the Repeater state where 0 = disabled, 1 = enabled
- # $db[0]_bit_6 is the Power Relay Timer state where 0 = disabled, 1 = enabled
- # $db[0]_bit_5 is the Daylight Harvesting state where 0 = disabled, 1 = enabled
- # $db[0]_bit_4 is the Dimming mode where 0 = switching, 1 = dimming
- # $db[0]_bit_2 is the Magnet Contact state where 0 = open, 1 = closed
- # $db[0]_bit_1 is the Occupancy (prensence) state where 0 = absent, 1 = present
- # $db[0]_bit_0 is the Power Relay state where 0 = off, 1 = on
- push @event, "3:brightness:" . ($db[3] << 1);
- push @event, "3:illum:$db[2]";
- push @event, "3:dim:$db[1]";
- push @event, "3:powerRelayTimer:" . ($db[0] & 0x80 ? "enabled" : "disabled");
- push @event, "3:repeater:" . ($db[0] & 0x40 ? "enabled" : "disabled");
- push @event, "3:daylightHarvesting:" . ($db[0] & 0x20 ? "enabled" : "disabled");
- push @event, "3:mode:" . ($db[0] & 0x10 ? "dimming" : "switching");
- push @event, "3:contact:" . ($db[0] & 4 ? "closed" : "open");
- push @event, "3:presence:" . ($db[0] & 2 ? "present" : "absent");
- push @event, "3:powerSwitch:" . ($db[0] & 1 ? "on" : "off");
- push @event, "3:state:" . ($db[0] & 1 ? "on" : "off");
- } elsif ($st eq "tempCtrlState.01") {
- # Temperature Controller Output (EEP A5-11-02)
- # $db[3] is the Control Variable where 0x00 = 0 % ... 0xFF = 100 %
- # $db[2] is the Fan Stage
- # $db[1] is the Actual Setpoint where 0x00 = 0 °C ... 0xFF = 51.2 °C
- # $db[0]_bit_7 is the Alarm state where 0 = no, 1 = yes
- # $db[0]_bit_6 ... $db[0]_bit_5 is the Controller Mode
- # $db[0]_bit_4 is the Controller State where 0 = auto, 1 = override
- # $db[0]_bit_2 is the Energy hold-off where 0 = normal, 1 = hold-off
- # $db[0]_bit_1 ... $db[0]_bit_0is the Occupancy (prensence) state where 0 = present
- # 1 = absent, 3 = standby, 4 = frost
- push @event, "3:controlVar:" . sprintf "%d", $db[3] * 100 / 255;
- if (($db[2] & 3) == 0) {
- push @event, "3:fan:0";
- } elsif (($db[2] & 3) == 1){
- push @event, "3:fan:1";
- } elsif (($db[2] & 3) == 2){
- push @event, "3:fan:2";
- } elsif (($db[2] & 3) == 3){
- push @event, "3:fan:3";
- } elsif ($db[2] == 255){
- push @event, "3:fan:unknown";
- }
- push @event, "3:fanMode:" . ($db[2] & 0x10 ? "auto" : "manual");
- my $setpointTemp = sprintf "%0.1f", $db[1] * 0.2;
- push @event, "3:setpointTemp:$setpointTemp";
- push @event, "3:alarm:" . ($db[0] & 1 ? "on" : "off");
- my $controllerMode = ($db[0] & 0x60) >> 5;
- if ($controllerMode == 0) {
- push @event, "3:controllerMode:auto";
- } elsif ($controllerMode == 1) {
- push @event, "3:controllerMode:heating";
- } elsif ($controllerMode == 2) {
- push @event, "3:controllerMode:cooling";
- } elsif ($controllerMode == 3) {
- push @event, "3:controllerMode:off";
- }
- push @event, "3:controllerState:" . ($db[0] & 0x10 ? "override" : "auto");
- push @event, "3:energyHoldOff:" . ($db[0] & 4 ? "holdoff" : "normal");
- if (($db[0] & 3) == 0) {
- push @event, "3:presence:present";
- } elsif (($db[0] & 3) == 1){
- push @event, "3:presence:absent";
- } elsif (($db[0] & 3) == 2){
- push @event, "3:presence:standby";
- } elsif (($db[0] & 3) == 3){
- push @event, "3:presence:frost";
- }
- push @event, "3:state:$setpointTemp";
- } elsif ($st eq "shutterCtrlState.01") {
- # Blind Status (EEP A5-11-03)
- # $db[3] is the Shutter Position where 0 = 0 % ... 100 = 100 %
- # $db[2]_bit_7 is the Angle sign where 0 = positive, 1 = negative
- # $db[2]_bit_6 ... $db[2]_bit_0 where 0 = 0° ... 90 = 180°
- # $db[1]_bit_7 is the Position Value Flag where 0 = no available, 1 = available
- # $db[1]_bit_6 is the Angle Value Flag where 0 = no available, 1 = available
- # $db[1]_bit_5 ... $db[1]_bit_4 is the Error State (alarm)
- # $db[1]_bit_3 ... $db[1]_bit_2 is the End-position State
- # $db[1]_bit_1 ... $db[1]_bit_0 is the Shutter State
- # $db[0]_bit_7 is the Service Mode where 0 = no, 1 = yes
- # $db[0]_bit_6 is the Position Mode where 0 = normal, 1 = inverse
- if ($db[1] & 0x80) {
- push @event, "3:position:" . $db[3];
- }
- my $anglePos = ($db[2] & 0x7F) << 1;
- if ($db[2] & 0x80) {$anglePos *= -1;}
- if ($db[1] & 0x40) {
- push @event, "3:anglePos:" . $anglePos;
- }
- my $alarm = ($db[1] & 0x30) >> 4;
- if ($alarm == 0) {
- push @event, "3:alarm:off";
- } elsif ($alarm == 1){
- push @event, "3:alarm:no_endpoints_defined";
- } elsif ($alarm == 2){
- push @event, "3:alarm:on";
- } elsif ($alarm == 3){
- push @event, "3:alarm:not_used";
- }
- my $endPosition = ($db[1] & 0x0C) >> 2;
- if ($endPosition == 0) {
- push @event, "3:endPosition:not_available";
- push @event, "3:state:not_available";
- } elsif ($endPosition == 1) {
- push @event, "3:endPosition:not_reached";
- push @event, "3:state:not_reached";
- } elsif ($endPosition == 2) {
- push @event, "3:endPosition:open";
- push @event, "3:state:open";
- } elsif ($endPosition == 3){
- push @event, "3:endPosition:closed";
- push @event, "3:state:closed";
- }
- my $shutterState = $db[1] & 3;
- if (($db[1] & 3) == 0) {
- push @event, "3:shutterState:not_available";
- } elsif (($db[1] & 3) == 1) {
- push @event, "3:shutterState:stopped";
- } elsif (($db[1] & 3) == 2){
- push @event, "3:shutterState:opens";
- } elsif (($db[1] & 3) == 3){
- push @event, "3:shutterState:closes";
- }
- push @event, "3:serviceOn:" . ($db[0] & 0x80 ? "yes" : "no");
- push @event, "3:positionMode:" . ($db[0] & 0x40 ? "inverse" : "normal");
- } elsif ($st eq "lightCtrlState.02") {
- # Extended Lighting Status (EEP A5-11-04)
- # $db[3] the contents of the variable depends on the parameter mode
- # $db[2] the contents of the variable depends on the parameter mode
- # $db[1] the contents of the variable depends on the parameter mode
- # $db[0]_bit_7 is the Service Mode where 0 = no, 1 = yes
- # $db[0]_bit_6 is the operating hours flag where 0 = not_available, 1 = available
- # $db[0]_bit_5 ... $db[0]_bit_4 is the Error State (alarm)
- # $db[0]_bit_2 ... $db[0]_bit_1 is the parameter mode
- # $db[0]_bit_0 is the lighting status where 0 = off, 1 = on
- push @event, "3:serviceOn:" . ($db[1] & 0x80 ? "yes" : "no");
- my $alarm = ($db[0] & 0x30) >> 4;
- if ($alarm == 0) {
- push @event, "3:alarm:off";
- } elsif ($alarm == 1){
- push @event, "3:alarm:lamp_failure";
- } elsif ($alarm == 2){
- push @event, "3:alarm:internal_failure";
- } elsif ($alarm == 3){
- push @event, "3:alarm:external_periphery_failure";
- }
- my $mode = ($db[0] & 6) >> 1;
- if ($mode == 0) {
- # dimmer value and lamp operating hours
- push @event, "3:dim:$db[3]";
- if ($db[0] & 40) {
- push @event, "3:lampOpHours:" . ($db[2] << 8 | $db[1]);
- } else {
- push @event, "3:lampOpHours:unknown";
- }
- } elsif ($mode == 1){
- # RGB value
- push @event, "3:red:$db[3]";
- push @event, "3:green:$db[2]";
- push @event, "3:blue:$db[1]";
- push @event, "3:rgb:" . substr($data, 0, 6);
- } elsif ($mode == 2){
- # energy metering value
- my @measureUnit = ("mW", "W", "kW", "MW", "Wh", "kWh", "MWh", "GWh",
- "mA", "A", "mV", "V");
- if ($db[1] < 4) {
- push @event, "3:power:" . ($db[3] << 8 | $db[2]);
- push @event, "3:powerUnit:" . $measureUnit[$db[1]];
- } elsif ($db[1] < 8) {
- push @event, "3:energy:" . ($db[3] << 8 | $db[2]);
- push @event, "3:energyUnit:" . $measureUnit[$db[1]];
- } elsif ($db[1] == 8) {
- push @event, "3:current:" . ($db[3] << 8 | $db[2]);
- push @event, "3:currentUnit:" . $measureUnit[$db[1]];
- } elsif ($db[1] == 9) {
- push @event, "3:current:" . sprintf "%0.1f", ($db[3] << 8 | $db[2]) / 10;
- push @event, "3:currentUnit:" . $measureUnit[$db[1]];
- } elsif ($db[1] == 10) {
- push @event, "3:voltage:" . ($db[3] << 8 | $db[2]);
- push @event, "3:voltageUnit:" . $measureUnit[$db[1]];
- } elsif ($db[1] == 11) {
- push @event, "3:voltage:" . sprintf "%0.1f", ($db[3] << 8 | $db[2]) / 10;
- push @event, "3:voltageUnit:" . $measureUnit[$db[1]];
- } else {
- push @event, "3:measuredValue:" . ($db[3] << 8 | $db[2]);
- push @event, "3:measureUnit:unknown";
- }
- } elsif ($mode == 3){
- # not used
- }
- push @event, "3:powerSwitch:" . ($db[0] & 1 ? "on" : "off");
- push @event, "3:state:" . ($db[0] & 1 ? "on" : "off");
- } elsif ($st eq "switch.05") {
- # Dual Channel Switch Actuator
- # (A5-11-05)
- if ($db[0] & 1) {
- push @event, "3:workingMode:" . (($db[0] & 0x70) >> 4);
- push @event, "3:channel1:" . ($db[0] & 2 ? "on" : "off");
- push @event, "3:channel2:" . ($db[0] & 4 ? "on" : "off");
- push @event, "3:state:1: " . ($db[0] & 2 ? "on" : "off") . " 2: " . ($db[0] & 4 ? "on" : "off");
- }
- } elsif ($st =~ m/^autoMeterReading\.0[0-3]$/ || $st eq "actuator.01" && $manufID eq "033") {
- # Automated meter reading (AMR) (EEP A5-12-00 ... A5-12-03)
- # $db[3] (MSB) + $db[2] + $db[1] (LSB) is the Meter reading
- # $db[0]_bit_7 ... $db[0]_bit_4 is the Measurement channel
- # $db[0]_bit_2 is the Data type where 0 = cumulative value, 1 = current value
- # $db[0]_bit_1 ... $db[0]_bit_0 is the Divisor where 0 = x/1, 1 = x/10,
- # 2 = x/100, 3 = x/1000
- my $dataType = ($db[0] & 4) >> 2;
- my $divisor = $db[0] & 3;
- my $meterReading;
- if ($divisor == 3) {
- $meterReading = sprintf "%.3f", ($db[3] << 16 | $db[2] << 8 | $db[1]) / 1000;
- } elsif ($divisor == 2) {
- $meterReading = sprintf "%.2f", ($db[3] << 16 | $db[2] << 8 | $db[1]) / 100;
- } elsif ($divisor == 1) {
- $meterReading = sprintf "%.1f", ($db[3] << 16 | $db[2] << 8 | $db[1]) / 10;
- } else {
- $meterReading = $db[3] << 16 | $db[2] << 8 | $db[1];
- }
- my $channel = $db[0] >> 4;
- if ($st eq "autoMeterReading.00") {
- # Automated meter reading (AMR), Counter (EEP A5-12-01)
- # [Thermokon SR-MI-HS, untested]
- if ($dataType == 1) {
- # current value
- push @event, "3:currentValue" . sprintf('%02d', $channel) . ":$meterReading";
- push @event, "3:state:$meterReading";
- } else {
- # cumulative counter
- push @event, "3:counter" . sprintf('%02d', $channel) . ":$meterReading";
- }
- } elsif ($st eq "autoMeterReading.01" || $st eq "actuator.01" && $manufID eq "033") {
- # Automated meter reading (AMR), Electricity (EEP A5-12-01)
- # [Eltako FSS12, FWZ12, DSZ14DRS, DSZ14WDRS, DWZ61]
- # $db[0]_bit_7 ... $db[0]_bit_4 is the Tariff info
- # $db[0]_bit_2 is the Data type where 0 = cumulative value kWh,
- # 1 = current value W
- if ($db[0] == 0x8F && $manufID eq "00D") {
- # Eltako, read meter serial number
- my $serialNumber;
- if ($db[1] == 0) {
- # first 2 digits of the serial number
- $serialNumber = substr(ReadingsVal($name, "serialNumber", "S-------"), 4, 4);
- $serialNumber = sprintf "S-%01x%01x%4s", $db[3] >> 4, $db[3] & 0x0F, $serialNumber;
- } else {
- # last 4 digits of the serial number
- $serialNumber = substr(ReadingsVal($name, "serialNumber", "S---"), 0, 4);
- $serialNumber = sprintf "%4s%01x%01x%01x%01x", $serialNumber,
- $db[2] >> 4, $db[2] & 0x0F, $db[3] >> 4, $db[3] & 0x0F;
- }
- push @event, "3:serialNumber:$serialNumber";
- } elsif ($dataType == 1) {
- # momentary power
- push @event, "3:power:$meterReading";
- if (!($st eq "actuator.01" && $manufID eq "033")) {
- push @event, "3:state:$meterReading";
- }
- } else {
- # power consumption
- push @event, "3:energy$channel:$meterReading";
- push @event, "3:currentTariff:$channel";
- }
- } elsif ($st eq "autoMeterReading.02" || $st eq "autoMeterReading.03") {
- # Automated meter reading (AMR), Gas, Water (EEP A5-12-02, A5-12-03)
- if ($dataType == 1) {
- # current value
- push @event, "3:flowrate:$meterReading";
- push @event, "3:state:$meterReading";
- } else {
- # cumulative counter
- push @event, "3:consumption$channel:$meterReading";
- push @event, "3:currentTariff:$channel";
- }
- }
- } elsif ($st =~ m/^autoMeterReading\.0[45]$/) {
- # $db[1] is the temperature 0 .. 0xFF >> -40 ... 40
- # $db[0]_bit_1 ... $db[0]_bit_0 is the battery level
- my $temperature = sprintf "%0.1f", ($db[1] / 255 * 80) - 40;
- my $battery = $db[0] & 3;
- if ($battery == 3) {
- $battery = "empty";
- } elsif ($battery == 2) {
- $battery = "low";
- } elsif ($battery == 1) {
- $battery = "ok";
- } else {
- $battery = "full";
- }
- push @event, "3:battery:$battery";
- push @event, "3:temperature:$temperature";
- if ($st eq "autoMeterReading.04") {
- # Automated meter reading (AMR), Temperature, Load (EEP A5-12-04)
- # $db[3] ... $db[2]_bit_2 is the Current Value in gram
- my $weight = $db[3] << 6 | $db[2];
- push @event, "3:weight:$weight";
- push @event, "3:state:T: $temperature W: $weight B: $battery";
- } elsif ($st eq "autoMeterReading.05") {
- # Automated meter reading (AMR), Temperature, Container (EEP A5-12-05)
- # $db[3] ... $db[2]_bit_6 is position sensor
- my @sp;
- $sp[0] = ($db[3] & 128) >> 7;
- $sp[1] = ($db[3] & 64) >> 6;
- $sp[2] = ($db[3] & 32) >> 5;
- $sp[3] = ($db[3] & 16) >> 4;
- $sp[4] = ($db[3] & 8) >> 3;
- $sp[5] = ($db[3] & 4) >> 2;
- $sp[6] = ($db[3] & 2) >> 1;
- $sp[7] = $db[3] & 1;
- $sp[8] = ($db[2] & 128) >> 7;
- $sp[9] = ($db[2] & 64) >> 6;
- my $amount = 0;
- for (my $spCntr = 0; $spCntr <= 9; $spCntr ++) {
- push @event, "3:location" . $spCntr . ":" . ($sp[$spCntr] ? "possessed" : "not_possessed");
- $amount += $sp[$spCntr];
- }
- push @event, "3:amount:$amount";
- push @event, "3:state:T: $temperature L: " . $sp[0] . $sp[1] . " " . $sp[2] . $sp[3] .
- " " . $sp[4] . $sp[5] . " " . $sp[6] . $sp[7] . " " . $sp[8] . $sp[9] . " B: $battery";
- }
- } elsif ($st eq "autoMeterReading.10") {
- # Automated meter reading (AMR), current meter 16 channels (EEP A5-12-10)
- my $meterReading = hex(substr($data, 0, 6)) / 10 ** ($db[0] & 3);
- my $channel = sprintf '%02d', $db[0] & 0xF0 >> 4;
- my $scaleDecimals = "%0." . ($db[0] & 3) . "f";
- push @event, "3:currentTariff:$channel";
- if ($db[0] & 4) {
- # current
- push @event, "3:current$channel:" . sprintf "$scaleDecimals", $meterReading;
- push @event, "3:state:" . sprintf "$scaleDecimals", $meterReading;
- } else {
- # electric charge
- push @event, "3:electricChange$channel:" . sprintf "$scaleDecimals", $meterReading;
- }
- } elsif ($st eq "environmentApp") {
- # Environmental Applications (EEP A5-13-01 ... EEP A5-13-06, EEP A5-13-10)
- # [Eltako FWS61]
- # $db[0]_bit_7 ... $db[0]_bit_4 is the Identifier
- my $identifier = $db[0] >> 4;
- if ($identifier == 1) {
- # Weather Station (EEP A5-13-01)
- # $db[3] is the dawn sensor where 0x00 = 0 lx ... 0xFF = 999 lx
- # $db[2] is the temperature where 0x00 = -40 °C ... 0xFF = 80 °C
- # $db[1] is the wind speed where 0x00 = 0 m/s ... 0xFF = 70 m/s
- # $db[0]_bit_2 is day / night where 0 = day, 1 = night
- # $db[0]_bit_1 is rain indication where 0 = no (no rain), 1 = yes (rain)
- my $dawn = sprintf "%d", $db[3] * 999 / 255;
- my $temp = sprintf "%0.1f", -40 + $db[2] * 120 / 255;
- my $windSpeed = sprintf "%0.1f", $db[1] * 70 / 255;
- my $dayNight = $db[0] & 4 ? "night" : "day";
- my $isRaining = $db[0] & 2 ? "yes" : "no";
- push @event, "3:brightness:$dawn";
- push @event, "3:dayNight:$dayNight";
- push @event, "3:isRaining:$isRaining";
- push @event, "3:temperature:$temp";
- push @event, "3:windSpeed:$windSpeed";
- push @event, "3:state:T: $temp B: $dawn W: $windSpeed IR: $isRaining";
- } elsif ($identifier == 2) {
- # Sun Intensity (EEP A5-13-02)
- # $db[3] is the sun exposure west where 0x00 = 1 lx ... 0xFF = 150 klx
- # $db[2] is the sun exposure south where 0x00 = 1 lx ... 0xFF = 150 klx
- # $db[1] is the sun exposure east where 0x00 = 1 lx ... 0xFF = 150 klx
- # $db[0]_bit_2 is hemisphere where 0 = north, 1 = south
- push @event, "3:hemisphere:" . ($db[0] & 4 ? "south" : "north");
- push @event, "3:sunWest:" . sprintf "%d", 1 + $db[3] * 149999 / 255;
- push @event, "3:sunSouth:" . sprintf "%d", 1 + $db[2] * 149999 / 255;
- push @event, "3:sunEast:" . sprintf "%d", 1 + $db[1] * 149999 / 255;
- } elsif ($identifier == 3) {
- # Date exchange (EEP A5-13-03)
- push @event, "3:date:" . ($db[1] + 2000) . '-' . $db[2] . '-' . $db[3];
- } elsif ($identifier == 4) {
- # Time und Day exchange (EEP A5-13-04)
- my @day = ('', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday');
- my $day = ($db[3] & 0xF0) >> 4;
- push @event, "3:weekday:" . $day[$day];
- if ($db[0] & 4) {
- # 12 h time format
- push @event, "3:time:" . ($db[3] & 0x0F) . ':' . $db[2] . ':' . $db[1] . ' ' . ($db[1] & 2 ? 'PM' : 'AM');
- } else {
- push @event, "3:time:" . ($db[3] & 0x0F) . ':' . $db[2] . ':' . $db[1];
- }
- push @event, "3:timeSource:" . ($db[0] & 1 ? 'GPS' : 'RTC');
- } elsif ($identifier == 5) {
- # Direction exchange (EEP A5-13-05)
- push @event, "3:elevation:" . ($db[3] - 90);
- push @event, "3:azimuth:" . hex(substr($data, 2, 4));
- } elsif ($identifier == 6) {
- # Geographic Position exchange (EEP A5-13-06)
- push @event, "3:latitude:" . sprintf("%0.3f", hex(substr($data, 0, 1) . substr($data, 2, 2)) / 22.75 - 90);
- push @event, "3:longitude:" . sprintf("%0.3f", hex(substr($data, 1, 1) . substr($data, 4, 2)) / 13.375 - 180);
- } elsif ($identifier == 7) {
- # Sun Position and Radiation (EEP A5-13-10)
- # $db[3]_bit_7 ... $db[3]_bit_1 is Sun Elevation where 0 = 0 ° ... 90 = 90 °
- # $db[3]_bit_0 is day / night where 0 = day, 1 = night
- # $db[2] is Sun Azimuth where 0 = -90 ° ... 180 = 90 °
- # $db[1] and $db[0]_bit_2 ... $db[0]_bit_0 is Solar Radiation where
- # 0 = 0 W/m2 ... 2000 = 2000 W/m2
- my $sunElev = $db[3] >> 1;
- my $sunAzim = $db[2] - 90;
- my $solarRad = $db[1] << 3 | $db[0] & 7;
- push @event, "3:dayNight:" . ($db[3] & 1 ? "night" : "day");
- push @event, "3:solarRadiation:$solarRad";
- push @event, "3:sunAzimuth:$sunAzim";
- push @event, "3:sunElevation:$sunElev";
- push @event, "3:state:SRA: $solarRad SNA: $sunAzim SNE: $sunElev";
- } else {
- # EEP A5-13-03 ... EEP A5-13-06 not implemented
- }
- } elsif ($st eq "windSensor.01") {
- # Wind Sensor (EEP A5-13-07)
- my @windDirection = ('NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW', 'N');
- push @event, "3:battery:" . ($db[0] & 1 ? 'low' : 'ok');
- push @event, "3:windDirection:" . $windDirection[$db[3]];
- push @event, "3:windSpeedAverage:" . $db[2] / 1.275;
- push @event, "3:windSpeedMax:" . $db[1] / 1.275;
- push @event, "3:state:" . $db[2] / 1.275;
- } elsif ($st eq "rainSensor.01") {
- # Rain Sensor (EEP A5-13-08)
- my $ras = ($db[3] & 0x40) >> 6;
- my $rfa = ($db[3] & 0x3F) / 10;
- my $rfc = hex(substr($data, 2, 4));
- my $rain = $rfc * 0.6875 * (1 + $ras * $rfa / 100);
- push @event, "3:battery:" . ($db[0] & 1 ? 'low' : 'ok');
- push @event, "3:rain:$rain";
- push @event, "3:state:$rain";
- } elsif ($st eq "multiFuncSensor") {
- # Multi-Func Sensor (EEP A5-14-01 ... A5-14-06)
- # $db[3] is the voltage where 0x00 = 0 V ... 0xFA = 5.0 V
- # $db[3] > 0xFA is error code
- # $db[2] is the illuminance where min 0x00 = 0 lx, max 0xFA = 1000 lx
- # $db[0]_bit_1 is Vibration where 0 = off, 1 = on
- # $db[0]_bit_0 is Contact where 0 = closed, 1 = open
- my $lux = $db[2] << 2;
- if ($db[2] == 251) {$lux = "over range";}
- my $voltage = sprintf "%0.1f", $db[3] * 0.02;
- if ($db[3] > 250) {push @event, "3:errorCode:$db[3]";}
- my $vibration = $db[0] & 2 ? "on" : "off";
- my $contact = $db[0] & 1 ? "open" : "closed";
- push @event, "3:brightness:$lux";
- push @event, "3:contact:$contact";
- push @event, "3:vibration:$vibration";
- push @event, "3:voltage:$voltage";
- push @event, "3:state:C: $contact V: $vibration E: $lux U: $voltage";
- } elsif ($st eq "doorContact") {
- # dual door contact (EEP A5-14-07, A5-14-08)
- if (!exists($hash->{helper}{lastEvent}) || $hash->{helper}{lastEvent} ne $data) {
- my $voltage = sprintf "%0.1f", $db[3] * 0.02;
- my $doorContact = $db[0] & 4 ? 'open' : 'closed';
- my $lockContact = $db[0] & 2 ? 'unlocked' : 'locked';
- my $vibration = $db[0] & 1 ? 'on' : 'off';
- push @event, "3:voltage:$voltage";
- push @event, "3:contact:$doorContact";
- push @event, "3:block:$lockContact";
- push @event, "3:vibration:$vibration";
- push @event, "3:state:C: $doorContact B: $lockContact V: $vibration U: $voltage";
- $hash->{helper}{lastEvent} = $data;
- }
- CommandDeleteReading(undef, "$name alarm");
- @{$hash->{helper}{alarmTimer}} = ($hash, 'alarm', 'dead_sensor', 1, 5);
- RemoveInternalTimer($hash->{helper}{alarmTimer});
- InternalTimer(gettimeofday() + 66, 'EnOcean_readingsSingleUpdate', $hash->{helper}{alarmTimer}, 0);
- } elsif ($st eq "windowContact") {
- # window contact (EEP A5-14-09, A5-14-0A)
- if (!exists($hash->{helper}{lastEvent}) || $hash->{helper}{lastEvent} ne $data) {
- my $voltage = sprintf "%0.1f", $db[3] * 0.02;
- my %window = (0 => 'closed', 1 => 'tilt', 2 => 'reserved', 3 => 'open');
- my $window = $window{(($db[0] & 6) >> 1)};
- my $vibration = $db[0] & 1 ? 'on' : 'off';
- push @event, "3:voltage:$voltage";
- push @event, "3:window:$window";
- push @event, "3:vibration:$vibration";
- push @event, "3:state:W: $window V: $vibration U: $voltage";
- $hash->{helper}{lastEvent} = $data;
- }
- CommandDeleteReading(undef, "$name alarm");
- @{$hash->{helper}{alarmTimer}} = ($hash, 'alarm', 'dead_sensor', 1, 5);
- RemoveInternalTimer($hash->{helper}{alarmTimer});
- InternalTimer(gettimeofday() + 66, 'EnOcean_readingsSingleUpdate', $hash->{helper}{alarmTimer}, 0);
- } elsif ($st =~ m/^digitalInput\.0[12]$/) {
- # Digital Input (EEP A5-30-01, A5-30-02)
- my $contact;
- if ($st eq "digitalInput.01") {
- # Single Input Contact, Batterie Monitor (EEP A5-30-01)
- # [Thermokon SR65 DI, untested]
- # $db[2] is the supply voltage, if >= 121 = battery ok
- # $db[1] is the input state, if <= 195 = contact closed
- my $battery = $db[2] >= 121 ? "ok" : "low";
- $contact = $db[1] <= 195 ? "closed" : "open";
- push @event, "3:battery:$battery";
- } else {
- # Single Input Contact (EEP A5-30-02)
- # $db[0]_bit_0 is the input state where 0 = closed, 1 = open
- $contact = $db[0] & 1 ? "open" : "closed";
- }
- push @event, "3:contact:$contact";
- push @event, "3:state:$contact";
- } elsif ($st eq "digitalInput.03") {
- # 4 digital inputs, wake, temperature (EEP A5-30-03)
- my $in0 = $db[1] & 1;
- my $in1 = ($db[1] & 2) > 1;
- my $in2 = ($db[1] & 4) > 2;
- my $in3 = ($db[1] & 8) > 3;
- my $wake = $db[1] & 16 ? 'high' : 'low';
- my $temperature = sprintf "%0.1f", 40 - $db[2] * 40 / 255;
- push @event, "3:in0:$in0";
- push @event, "3:in1:$in1";
- push @event, "3:in2:$in2";
- push @event, "3:in3:$in3";
- push @event, "3:wake:$wake";
- push @event, "3:temperature:$temperature";
- push @event, "3:state:T: $temperature I: " . $in0 . $in1 . $in2 . $in3 . " W: " . $wake;
- } elsif ($st eq "digitalInput.04") {
- # 3 digital inputs, 1 digital input 8 bit (EEP A5-30-04)
- my $in0 = $db[0] & 1;
- my $in1 = ($db[0] & 2) > 1;
- my $in2 = ($db[0] & 4) > 2;
- my $in3 = $db[1];
- push @event, "3:in0:$in0";
- push @event, "3:in1:$in1";
- push @event, "3:in2:$in2";
- push @event, "3:in3:$in3";
- push @event, "3:state:" . $in0 . $in1 . $in2 . " " . $in3;
- } elsif ($st eq "digitalInput.05") {
- # single input contact, retransmission, battery monitor (EEP A5-30-05)
- my $signalIdx = $db[1] & 0x7F;
- my $signalIdxLast = ReadingsVal($name, "signalIdx", undef);
- my $signalType = $db[1] & 0x80 ? "heartbeat" : "event";
- push @event, "3:voltage:" . sprintf "%0.1f", $db[2] / 255 * 3.3;
- push @event, "3:signalIdx:$signalIdx";
- push @event, "3:telegramType:$signalType";
- if (defined $signalIdxLast) {
- if ($signalIdx == $signalIdxLast + 1 || $signalIdx == 0 && $signalIdxLast == 127) {
- push @event, "3:state:$signalType";
- } else {
- push @event, "3:state:error";
- }
- } else {
- push @event, "3:state:$signalType";
- }
- } elsif ($st eq "gateway") {
- # Gateway (EEP A5-38-08)
- # $db[3] is the command ID ($gwCmdID)
- # Eltako devices not send teach-in telegrams
- if(($db[0] & 8) == 0) {
- # teach-in, identify and store command type in attr gwCmd
- my $gwCmd = AttrVal($name, "gwCmd", undef);
- if (!$gwCmd) {
- $gwCmd = $EnO_gwCmd[$db[3] - 1];
- $attr{$name}{gwCmd} = $gwCmd;
- }
- }
- if ($db[3] == 1) {
- # Switching
- # Eltako devices not send A5 telegrams
- push @event, "3:executeTime:" . sprintf "%0.1f", (($db[2] << 8) | $db[1]) / 10;
- push @event, "3:block:" . ($db[0] & 4 ? "lock" : "unlock");
- push @event, "3:executeType" . ($db[0] & 2 ? "delay" : "duration");
- push @event, "3:state:" . ($db[0] & 1 ? "on" : "off");
- } elsif ($db[3] == 2) {
- # Dimming
- # $db[0]_bit_2 is store final value, not used, because
- # dimming value is always stored
- push @event, "3:rampTime:$db[1]";
- push @event, "3:state:" . ($db[0] & 0x01 ? "on" : "off");
- if ($db[0] & 4) {
- # Relative Dimming Range
- push @event, "3:dim:" . sprintf "%d", $db[2] * 100 / 255;
- } else {
- push @event, "3:dim:$db[2]";
- }
- push @event, "3:dimValueLast:$db[2]" if ($db[2] > 0);
- } elsif ($db[3] == 3) {
- # Setpoint shift
- # $db1 is setpoint shift where 0 = -12.7 K ... 255 = 12.8 K
- my $setpointShift = sprintf "%0.1f", -12.7 + $db[1] / 10;
- push @event, "3:setpointShift:$setpointShift";
- push @event, "3:state:$setpointShift";
- } elsif ($db[3] == 4) {
- # Basic Setpoint
- # $db1 is setpoint where 0 = 0 °C ... 255 = 51.2 °C
- my $setpoint = sprintf "%0.1f", $db[1] / 5;
- push @event, "3:setpoint:$setpoint";
- push @event, "3:state:$setpoint";
- } elsif ($db[3] == 5) {
- # Control variable
- # $db1 is control variable override where 0 = 0 % ... 255 = 100 %
- push @event, "3:controlVar:" . sprintf "%d", $db[1] * 100 / 255;
- my $controllerMode = ($db[0] & 0x60) >> 5;
- if ($controllerMode == 0) {
- push @event, "3:controllerMode:auto";
- push @event, "3:state:auto";
- } elsif ($controllerMode == 1) {
- push @event, "3:controllerMode:heating";
- push @event, "3:state:heating";
- } elsif ($controllerMode == 2){
- push @event, "3:controllerMode:cooling";
- push @event, "3:state:cooling";
- } elsif ($controllerMode == 3){
- push @event, "3:controllerMode:off";
- push @event, "3:state:off";
- }
- push @event, "3:controllerState:" . ($db[0] & 0x10 ? "override" : "auto");
- push @event, "3:energyHoldOff:" . ($db[0] & 4 ? "holdoff" : "normal");
- my $occupancy = $db[0] & 3;
- if ($occupancy == 0) {
- push @event, "3:presence:present";
- } elsif ($occupancy == 1){
- push @event, "3:presence:absent";
- } elsif ($occupancy == 2){
- push @event, "3:presence:standby";
- }
- } elsif ($db[3] == 6) {
- # Fan stage
- if ($db[1] == 0) {
- push @event, "3:fan:0";
- push @event, "3:state:0";
- } elsif ($db[1] == 1) {
- push @event, "3:fan:1";
- push @event, "3:state:1";
- } elsif ($db[1] == 2) {
- push @event, "3:fan:2";
- push @event, "3:state:2";
- } elsif ($db[1] == 3) {
- push @event, "3:fan:3";
- push @event, "3:state:3";
- } elsif ($db[1] == 255) {
- push @event, "3:fan:auto";
- push @event, "3:state:auto";
- }
- } else {
- push @event, "3:state:Gateway Command ID $db[3] unknown.";
- }
- } elsif ($st eq "energyManagement.01") {
- # Energy Management, Demand Response
- # (A5-37-01)
- EnOcean_energyManagement_01Parse($hash, @db);
- } elsif ($st eq "manufProfile") {
- # Manufacturer Specific Applications (EEP A5-3F-7F)
- if ($manufID eq "002") {
- # [Thermokon SR65 3AI, untested]
- # $db[3] is the input 3 where 0x00 = 0 V ... 0xFF = 10 V
- # $db[2] is the input 2 where 0x00 = 0 V ... 0xFF = 10 V
- # $db[1] is the input 1 where 0x00 = 0 V ... 0xFF = 10 V
- my $input3 = sprintf "%0.2f", $db[3] * 10 / 255;
- my $input2 = sprintf "%0.2f", $db[2] * 10 / 255;
- my $input1 = sprintf "%0.2f", $db[1] * 10 / 255;
- push @event, "3:input1:$input1";
- push @event, "3:input2:$input2";
- push @event, "3:input3:$input3";
- push @event, "3:state:I1: $input1 I2: $input2 I3: $input3";
- } elsif ($manufID eq "005") {
- # omnio
- if ($db[0] == 0x0C) {
- # omnio UPH 2330/1x
- my $channel = ($db[1] & 0xF0) > 4;
- my $emergencyMode = $db[1] & 8 ? "on" : "off";
- my $window = $db[1] & 4 ? "open" : "closed";
- my $nightReduction = $db[1] & 2 ? "on" : "off";
- my $state = $db[1] & 1 ? "on" : "off";
- my $temperature = sprintf "%0.1f", $db[2] * 40 / 250;
- my $setpointTemp = sprintf "%0.1f", $db[3] * 40 / 250;
- push @event, "3:emergencyMode" . $channel . ":$emergencyMode";
- push @event, "3:window" . $channel . ":$window";
- push @event, "3:nightReduction" . $channel . ":$nightReduction";
- push @event, "3:temperature" . $channel . ":$temperature";
- push @event, "3:setpointTemp" . $channel . ":$setpointTemp";
- push @event, "3:state:$state";
- }
- } elsif ($manufID eq "00D") {
- # [Eltako shutter]
- my $angleMax = AttrVal($name, "angleMax", 90);
- my $angleMin = AttrVal($name, "angleMin", -90);
- my $anglePos = ReadingsVal($name, ".anglePosStart", undef);
- my $angleTime = AttrVal($name, "angleTime", 0);
- my $position = ReadingsVal($name, ".positionStart", undef);
- my $shutTime = AttrVal($name, "shutTime", 255);
- my $shutTimeStop = ($db[3] << 8 | $db[2]) * 0.1;
- my $state;
- $angleMax = 90 if ($angleMax !~ m/^[+-]?\d+$/);
- $angleMax = 180 if ($angleMax > 180);
- $angleMax = -180 if ($angleMax < -180);
- $angleMin = -90 if ($angleMin !~ m/^[+-]?\d+$/);
- $angleMin = 180 if ($angleMin > 180);
- $angleMin = -180 if ($angleMin < -180);
- ($angleMax, $angleMin) = ($angleMin, $angleMax) if ($angleMin > $angleMax);
- $angleMax ++ if ($angleMin == $angleMax);
- $angleTime = 0 if ($angleTime !~ m/^[+-]?\d+$/);
- $angleTime = 6 if ($angleTime > 6);
- $angleTime = 0 if ($angleTime < 0);
- $shutTime = 255 if ($shutTime !~ m/^[+-]?\d+$/);
- $shutTime = 255 if ($shutTime > 255);
- $shutTime = 1 if ($shutTime < 1);
- if ($db[0] == 0x0A) {
- push @event, "3:block:unlock";
- } elsif ($db[0] == 0x0E) {
- push @event, "3:block:lock";
- }
- if (defined $position) {
- if ($db[1] == 1) {
- # up
- $position -= $shutTimeStop / $shutTime * 100;
- if ($angleTime) {
- $anglePos -= ($angleMax - $angleMin) * $shutTimeStop / $angleTime;
- if ($anglePos < $angleMin) {
- $anglePos = $angleMin;
- }
- } else {
- $anglePos = $angleMin;
- }
- if ($position <= 0) {
- $anglePos = 0;
- $position = 0;
- push @event, "3:endPosition:open";
- $state = "open";
- } else {
- push @event, "3:endPosition:not_reached";
- $state = "stop";
- }
- push @event, "3:anglePos:" . sprintf("%d", $anglePos);
- push @event, "3:position:" . sprintf("%d", $position);
- push @event, "3:.anglePosStart:" . sprintf("%d", $anglePos);
- push @event, "3:.positionStart:" . sprintf("%d", $position);
- } elsif ($db[1] == 2) {
- # down
- $position += $shutTimeStop / $shutTime * 100;
- if ($angleTime) {
- $anglePos += ($angleMax - $angleMin) * $shutTimeStop / $angleTime;
- if ($anglePos > $angleMax) {
- $anglePos = $angleMax;
- }
- } else {
- $anglePos = $angleMax;
- }
- if($position > 100) {
- $anglePos = $angleMax;
- $position = 100;
- push @event, "3:endPosition:closed";
- $state = "closed";
- } else {
- push @event, "3:endPosition:not_reached";
- $state = "stop";
- }
- push @event, "3:anglePos:" . sprintf("%d", $anglePos);
- push @event, "3:position:" . sprintf("%d", $position);
- push @event, "3:.anglePosStart:" . sprintf("%d", $anglePos);
- push @event, "3:.positionStart:" . sprintf("%d", $position);
- } else {
- $state = "not_reached";
- }
- push @event, "3:state:$state";
- }
- } else {
- # Unknown Application
- push @event, "3:state:Manufacturer Specific Application unknown";
- }
- } elsif ($st eq "PM101") {
- # Light and Presence Sensor [Omnio Ratio eagle-PM101]
- # The sensor also sends switching commands (RORG F6) with the senderID-1
- # $db[2] is the illuminance where 0x00 = 0 lx ... 0xFF = 1000 lx
- my $channel2 = $db[0] & 2 ? "on" : "off";
- push @event, "3:brightness:" . ($db[2] << 2);
- push @event, "3:channel1:" . ($db[0] & 1 ? "on" : "off");
- push @event, "3:channel2:" . $channel2;
- push @event, "3:motion:" . $channel2;
- push @event, "3:state:" . $channel2;
- } elsif ($st eq "radioLinkTest") {
- # Radio Link Test (EEP A5-3F-00)
- @{$hash->{helper}{rlt}{param}} = ('parse', $hash, $data, $subDef, 'master', $RSSI);
- EnOcean_RLT($hash->{helper}{rlt}{param});
- } elsif ($st eq "raw") {
- # raw
- push @event, "3:state:RORG: $rorg DATA: $data STATUS: $status ODATA: $odata";
- } else {
- # unknown devices
- push @event, "3:state:$db[3]";
- push @event, "3:sensor1:$db[3]";
- push @event, "3:sensor2:$db[2]";
- push @event, "3:sensor3:$db[1]";
- push @event, "3:D3:" . ($db[0] & 8 ? 1 : 0);
- push @event, "3:D2:" . ($db[0] & 4 ? 1 : 0);
- push @event, "3:D1:" . ($db[0] & 2 ? 1 : 0);
- push @event, "3:D0:" . ($db[0] & 1 ? 1 : 0);
- }
- } elsif ($rorg eq "D2") {
- # VLD telegram
- if ($st eq "roomCtrlPanel.00") {
- # EEP D2-10-01 - D2-10-03
- my ($key, $val);
- # message identifier
- my $mid = hex(substr($data, 0, 2)) >> 5;
- # message continuation flag
- my $mcf = hex(substr($data, 0, 2)) & 3;
- my ($irc, $fbc, $gmt);
- if ($mcf == 0) {
- # message complete
- # read stored telegrams
- if (!defined($hash->{helper}{$mid}{messagePart})) {
- $hash->{helper}{$mid}{messagePart} = 1;
- $hash->{helper}{$mid}{data}{1} = $data;
- } else {
- $hash->{helper}{$mid}{messagePart} += 1;
- $hash->{helper}{$mid}{data}{$hash->{helper}{$mid}{messagePart}} = $data;
- }
- if ($mid == 4) {
- CommandDeleteAttr(undef, "$name timeProgram1");
- CommandDeleteAttr(undef, "$name timeProgram2");
- CommandDeleteAttr(undef, "$name timeProgram3");
- CommandDeleteAttr(undef, "$name timeProgram4");
- }
- for (my $partCntr = $hash->{helper}{$mid}{messagePart}; $partCntr > 0; $partCntr --) {
- $data = $hash->{helper}{$mid}{data}{$partCntr};
- delete $hash->{helper}{$mid}{data}{$partCntr};
- if ($partCntr == 1) {
- delete $hash->{helper}{$mid}{messagePart};
- } else {
- $hash->{helper}{$mid}{messagePart} --;
- }
- $dbCntr = 0;
- for (my $strCntr = length($data) / 2 - 1; $strCntr >= 0; $strCntr --) {
- $db[$dbCntr] = hex substr($data, $strCntr * 2, 2);
- $dbCntr++;
- }
- #Log3 $name, 2, "EnOcean $name EnOcean_Parse write MID $mid DATA $data to part $partCntr";
- #Log3 $name, 2, "EnOcean $name EnOcean_Parse write 1 MID $mid DATA $data to " . sprintf "%02X%02X%02X%02X%02X%02X", $db[5], $db[4], $db[3], $db[2], $db[1], $db[0];
- if ($mid == 0) {
- # general message
- $irc = ($db[0] & 56) >> 3;
- $fbc = ($db[0] & 6) >> 1;
- $gmt = $db[0] & 1;
- #push @event, "3:general:$data";
- } elsif ($mid == 1) {
- # data message
- my $temperature = "-";
- $temperature = sprintf "%.1f", $db[0] / 254 * 40 if ($db[2] & 1);
- push @event, "3:temperature:$temperature";
- my $setpointTemp = "-";
- $setpointTemp = sprintf "%.1f", $db[1] / 254 * 40 if ($db[2] & 2);
- push @event, "3:setpointTemp:$setpointTemp";
- my $roomCtrlMode = ($db[2] & 12) >> 2;
- if ($roomCtrlMode == 3) {
- $roomCtrlMode = "buildingProtection";
- } elsif ($roomCtrlMode == 2) {
- $roomCtrlMode = "preComfort";
- } elsif ($roomCtrlMode == 1) {
- $roomCtrlMode = "economy";
- } else{
- $roomCtrlMode = "comfort";
- }
- push @event, "3:roomCtrlMode:$roomCtrlMode";
- my $heating = ($db[2] & 48) >> 4;
- if ($heating == 3) {
- $heating = "auto";
- } elsif ($heating == 2) {
- $heating = "off";
- } elsif ($heating == 1) {
- $heating = "on";
- } else{
- $heating = "-";
- }
- if ($heating ne "-") {
- push @event, "3:heating:$heating";
- }
- my $cooling = ($db[2] & 192) >> 6;
- if ($cooling == 3) {
- $cooling = "auto";
- } elsif ($cooling == 2) {
- $cooling = "off";
- } elsif ($cooling == 1) {
- $cooling = "on";
- } else{
- $cooling = "-";
- }
- if ($cooling ne "-") {
- push @event, "3:cooling:$cooling";
- }
- my $occupancy = $db[3] & 3;
- if ($occupancy == 3) {
- $occupancy = "reserved";
- } elsif ($occupancy == 2) {
- $occupancy = "absent";
- } elsif ($occupancy == 1) {
- $occupancy = "present";
- } else{
- $occupancy = "-";
- }
- if ($occupancy eq "-") {
- $occupancy = ReadingsVal($name, "occupancy", "-");
- } else {
- push @event, "3:occupancy:$occupancy";
- }
- my $motion = ($db[3] & 12) >> 2;
- if ($motion == 3) {
- $motion = "reserved";
- } elsif ($motion == 2) {
- $motion = "on";
- } elsif ($motion == 1) {
- $motion = "off";
- } else{
- $motion = "-";
- }
- if ($motion eq "-") {
- $motion = ReadingsVal($name, "motion", "-");
- } else {
- push @event, "3:motion:$motion";
- }
- push @event, "3:solarPowered:" . ($db[3] & 16 ? "no" : "yes");
- my $battery = ($db[3] & 96) >> 5;
- if ($battery == 3) {
- $battery = "empty";
- } elsif ($battery == 2) {
- $battery = "low";
- } elsif ($battery == 1) {
- $battery = "ok";
- } else{
- $battery = "-";
- }
- if ($battery ne "-") {
- push @event, "3:battery:$battery";
- }
- my $window = $db[4] & 3;
- if ($window == 3) {
- $window = "reserved";
- } elsif ($window == 2) {
- $window = "open";
- } elsif ($window == 1) {
- $window = "closed";
- } else{
- $window = "-";
- }
- if ($window ne "-") {
- push @event, "3:window:$window";
- }
- push @event, "3:moldWarning:" . ($db[4] & 4 ? "on" : "off");
- push @event, "3:customWarning1:" . ($db[4] & 8 ? "on" : "off");
- push @event, "3:customWarning2:" . ($db[4] & 16 ? "on" : "off");
- push @event, "3:fanSpeedMode:" . ($db[4] & 64 ? "local" : "central");
- my $fanSpeed = 0;
- $fanSpeed = sprintf "%d", $db[5] & 127 if ($db[4] & 128);
- push @event, "3:fanSpeed:$fanSpeed";
- my $humi = "-";
- $humi = sprintf "%d", $db[6] / 2.55 if ($db[5] & 128);
- push @event, "3:humidity:$humi";
- push @event, "3:state:T: $temperature H: $humi F: $fanSpeed SPT: $setpointTemp O: $occupancy M: $motion";
- } elsif ($mid == 2) {
- # configuration message
- $attr{$name}{blockFanSpeed} = $db[6] & 1 ? "no" : "yes";
- $attr{$name}{blockSetpointTemp} = $db[6] & 2 ? "no" : "yes";
- $attr{$name}{blockOccupancy} = $db[6] & 4 ? "no" : "yes";
- $attr{$name}{blockTimeProgram} = $db[6] & 8 ? "no" : "yes";
- $attr{$name}{blockDateTime} = $db[6] & 16 ? "no" : "yes";
- $attr{$name}{blockDisplay} = $db[6] & 32 ? "no" : "yes";
- $attr{$name}{blockTemp} = $db[6] & 64 ? "no" : "yes";
- $attr{$name}{blockMotion} = $db[6] & 128 ? "no" : "yes";
- my $pollInterval = $db[5] >> 2;
- if ($pollInterval == 63) {
- $attr{$name}{pollInterval} = 1440;
- } elsif ($pollInterval == 62) {
- $attr{$name}{pollInterval} = 720;
- } elsif ($pollInterval == 61) {
- $attr{$name}{pollInterval} = 180;
- } else {
- $attr{$name}{pollInterval} = $pollInterval;
- }
- $attr{$name}{blockKey} = $db[5] & 2 ? "no" : "yes";
- my $displayContent = $db[4] >> 5;
- my %displayContent = (7 => "humidity",
- 6 => "off",
- 5 => "setpointTemp",
- 4 => "tempertureExtern",
- 3 => "temperatureIntern",
- 2 => "time",
- 1 => "default",
- 0 => "no_change"
- );
- while (($key, $val) = each(%displayContent)) {
- $attr{$name}{displayContent} = $val if ($key == $displayContent);
- }
- my $temperatureScale = ($db[4] & 24) >> 3;
- my %temperatureScale = (3 => "F",
- 2 => "C",
- 1 => "default",
- 0 => "no_change"
- );
- while (($key, $val) = each(%temperatureScale)) {
- $attr{$name}{temperatureScale} = $val if ($key == $temperatureScale);
- }
- $attr{$name}{daylightSavingTime} = $db[4] & 4 ? "not_supported" : "supported";
- my $timeNotation = $db[4] & 3;
- if ($timeNotation == 0) {
- $attr{$name}{timeNotation} = "no_change";
- } elsif ($timeNotation == 1) {
- $attr{$name}{timeNotation} = "default";
- } elsif ($timeNotation == 2) {
- $attr{$name}{timeNotation} = 24;
- } elsif ($timeNotation == 3) {
- $attr{$name}{timeNotation} = 12;
- }
- EnOcean_CommandSave(undef, undef);
- } elsif ($mid == 3) {
- # room control setup
- my $setpointComfort = "-";
- $setpointComfort = sprintf "%.1f", $db[1] / 254 * 40 if ($db[0] & 1);
- push @event, "3:setpointComfortTemp:$setpointComfort";
- my $setpointEconomy = "-";
- $setpointEconomy = sprintf "%.1f", $db[2] / 254 * 40 if ($db[0] & 2);
- push @event, "3:setpointEconomyTemp:$setpointEconomy";
- my $setpointPreComfort = "-";
- $setpointPreComfort = sprintf "%.1f", $db[3] / 254 * 40 if ($db[0] & 4);
- push @event, "3:setpointPreComfortTemp:$setpointPreComfort";
- my $setpointBuildingProtection = "-";
- $setpointBuildingProtection = sprintf "%.1f", $db[3] / 254 * 40 if ($db[0] & 8);
- push @event, "3:setpointBuildingProtectionTemp:$setpointBuildingProtection";
- } elsif ($mid == 4) {
- # time program setup
- my $timeProgram = "timeProgram" . $partCntr;
- my $period = $db[0] >> 4;
- my $periodVal = "";
- my %period = (15 => "FrMo",
- 14 => "FrSu",
- 13 => "ThFr",
- 12 => "WeFr",
- 11 => "TuTh",
- 10 => "MoWe",
- 9 => "Su",
- 8 => "Sa",
- 7 => "Fr",
- 6 => "Th",
- 5 => "We",
- 4 => "Tu",
- 3 => "Mo",
- 2 => "SaSu",
- 1 => "MoFr",
- 0 => "MoSu"
- );
- while (($key, $val) = each(%period)) {
- $periodVal = $val if ($key == $period);
- }
- my $roomCtrlMode = ($db[0] & 12) >> 2;
- if ($roomCtrlMode == 3) {
- $roomCtrlMode = "buildingProtection";
- } elsif ($roomCtrlMode == 2) {
- $roomCtrlMode = "preComfort";
- } elsif ($roomCtrlMode == 1) {
- $roomCtrlMode = "economy";
- } else{
- $roomCtrlMode = "comfort";
- }
- my ($startHour, $startMinute, $endHour, $endMinute) = ($db[1], $db[2], $db[3], $db[4]);
- $startHour = $startHour < 10 ? $startHour = "0" . $startHour : $startHour;
- $startMinute = $startMinute < 10 ? $startMinute = "0" . $startMinute : $startMinute;
- $endHour = $endHour < 10 ? $endHour = "0" . $endHour : $endHour;
- $endMinute = $endMinute < 10 ? $endMinute = "0" . $endMinute : $endMinute;
- #Log3 $name, 2, "EnOcean $name EnOcean_Parse write 2 MID $mid DATA $data to " . sprintf "%02X%02X%02X%02X%02X%02X", $db[5], $db[4], $db[3], $db[2], $db[1], $db[0];
- #Log3 $name, 2, "EnOcean $name EnOcean_Parse write 3 MID $mid DATA $data to $timeProgram VAL: $periodVal $startHour:$startMinute $endHour:$endMinute $roomCtrlMode";
- $attr{$name}{$timeProgram} = "$periodVal $startHour:$startMinute $endHour:$endMinute $roomCtrlMode";
- #Log3 $name, 2, "EnOcean $name EnOcean_Parse write 4 MID $mid DATA $data to $timeProgram VAL: $attr{$name}{$timeProgram}";
- EnOcean_CommandSave(undef, undef);
- }
- }
- ($err, $response) = EnOcean_roomCtrlPanel_00Snd(undef, $hash, $packetType, $mid, $mcf, $irc, $fbc, $gmt);
- } elsif ($mcf == 1) {
- # message incomplete
- if (!defined($hash->{helper}{$mid}{messagePart})) {
- $hash->{helper}{$mid}{messagePart} = 1;
- } elsif ($hash->{helper}{$mid}{messagePart} >= 4) {
- # max 4 message parts stored
- for (my $partCntr = 1; $partCntr < $hash->{helper}{$mid}{messagePart}; $partCntr ++) {
- $hash->{helper}{$mid}{data}{$partCntr} = $hash->{helper}{$mid}{data}{$partCntr + 1};
- }
- } else {
- $hash->{helper}{$mid}{messagePart} += 1;
- }
- $hash->{helper}{$mid}{data}{$hash->{helper}{$mid}{messagePart}} = $data;
- #Log3 $name, 2, "EnOcean $name EnOcean_Parse store MID $mid DATA $data to messagePart $hash->{helper}{$mid}{messagePart}";
- ($err, $response) = EnOcean_roomCtrlPanel_00Snd(undef, $hash, $packetType, $mid, $mcf, undef, undef, undef);
- }
- } elsif ($st eq "actuator.01") {
- # Electronic switches and dimmers with Energy Measurement and Local Control
- # (D2-01-00 - D2-01-12)
- my $channel = (hex substr($data, 2, 2)) & 0x1F;
- if ($channel == 31) {$channel = "Input";}
- my $cmd = hex substr($data, 1, 1);
- if ($cmd == 4) {
- # actuator status response
- my $overCurrentOff;
- my $error;
- my $localControl;
- my $dim;
- push @event, "3:powerFailure" . $channel . ":" . ($db[2] & 0x80 ? "enabled" : "disabled");
- push @event, "3:powerFailureDetection" . $channel . ":" . ($db[2] & 0x40 ? "detected" : "not_detected");
- if (($db[1] & 0x80) == 0) {
- $overCurrentOff = "ready";
- } else {
- $overCurrentOff = "executed";
- }
- push @event, "3:overCurrentOff" . $channel . ":" . $overCurrentOff;
- if ((($db[1] & 0x60) >> 5) == 1) {
- $error = "warning";
- } elsif ((($db[1] & 0x60) >> 5) == 2) {
- $error = "failure";
- } elsif ((($db[1] & 0x60) >> 5) == 3) {
- $error = "not_supported";
- } else {
- $error = "ok";
- }
- push @event, "3:error" . $channel . ":" . $error;
- if (($db[0] & 0x80) == 0) {
- $localControl = "disabled";
- } else {
- $localControl = "enabled";
- }
- push @event, "3:localControl" . $channel . ":" . $localControl;
- my $dimValue = $db[0] & 0x7F;
- if ($dimValue == 0) {
- push @event, "3:channel" . $channel . ":off";
- push @event, "3:state:off";
- } else {
- push @event, "3:channel" . $channel . ":on";
- push @event, "3:state:on";
- }
- if ($channel ne "Input") {
- push @event, "3:dim:" . $dimValue;
- push @event, "3:dim" . $channel . ":" . $dimValue;
- } else {
- push @event, "3:dim" . $channel . ":" . $dimValue;
- }
- } elsif ($cmd == 7) {
- # actuator measurement response
- my $unit = $db[4] >> 5;
- if ($unit == 1) {
- #$unit = "Wh";
- $unit = "KWh";
- push @event, "3:energyUnit" . $channel . ":" . $unit;
- push @event, "3:energy" . $channel . ":" . sprintf("%.3f", (hex substr($data, 4, 8)) / 1000);
- } elsif ($unit == 2) {
- $unit = "KWh";
- push @event, "3:energyUnit" . $channel . ":" . $unit;
- push @event, "3:energy" . $channel . ":" . hex substr($data, 4, 8);
- } elsif ($unit == 3) {
- $unit = "W";
- push @event, "3:powerUnit" . $channel . ":" . $unit;
- push @event, "3:power" . $channel . ":" . hex substr($data, 4, 8);
- } elsif ($unit == 4) {
- $unit = "KW";
- push @event, "3:powerUnit" . $channel . ":" . $unit;
- push @event, "3:power" . $channel . ":" . hex substr($data, 4, 8);
- } else {
- $unit = "Ws";
- push @event, "3:energyUnit" . $channel . ":" . $unit;
- push @event, "3:energy" . $channel . ":" . hex substr($data, 4, 8);
- }
- } elsif ($cmd == 10) {
- # pilot wire mode response
- my $roomCtrlMode = $db[0] & 3;
- my %roomCtrlMode = (
- 0 => "off",
- 1 => "comfort",
- 2 => "economy",
- 3 => "buildingProtection",
- 4 => "comfort-1",
- 5 => "comfort-2"
- );
- push @event, "3:roomCtrlMode" . $roomCtrlMode{$roomCtrlMode} if (exists $roomCtrlMode{$roomCtrlMode});
- } elsif ($cmd == 13) {
- # external interface settings
- push @event, "3:autoOffTime" . $channel . ":" . sprintf("%0.1f", hex(substr($data, 4, 4)) * 0.1);
- push @event, "3:delayOffTime" . $channel . ":" . sprintf("%0.1f", hex(substr($data, 8, 4)) * 0.1);
- my $extSwitchMode = ($db[0] & 0xC0) >> 6;
- my %extSwitchMode = (
- 0 => "unavailable",
- 1 => "switch",
- 2 => "pushbutton",
- 3 => "auto"
- );
- push @event, "3:extSwitchMode" . $extSwitchMode{$extSwitchMode} if (exists $extSwitchMode{$extSwitchMode});
- push @event, "3:extSwitchType" . ($db[0] & 0x20 ? 'direction' : 'toggle');
- } else {
- # unknown response
- }
- } elsif ($st eq "switch.00" || $st eq "windowHandle.10") {
- if ($db[0] == 1) {
- push @event, "3:state:open_from_tilted";
- } elsif ($db[0] == 2) {
- push @event, "3:state:closed";
- } elsif ($db[0] == 3) {
- push @event, "3:state:open";
- } elsif ($db[0] == 4) {
- push @event, "3:state:tilted";
- } elsif ($db[0] == 5) {
- push @event, "3:state:AI,B0";
- push @event, "3:channelA:AI";
- push @event, "3:channelB:B0";
- push @event, "3:energyBow:pressed";
- } elsif ($db[0] == 6) {
- CommandDeleteReading(undef, "$name channel.*");
- push @event, "3:state:pressed34";
- push @event, "3:energyBow:pressed";
- } elsif ($db[0] == 7) {
- push @event, "3:state:A0,B0";
- push @event, "3:channelA:A0";
- push @event, "3:channelB:B0";
- push @event, "3:energyBow:pressed";
- } elsif ($db[0] == 8) {
- if (AttrVal($name, "sensorMode", "switch") eq "pushbutton") {
- push @event, "3:state:pressed";
- }
- push @event, "3:energyBow:pressed";
- } elsif ($db[0] == 9) {
- push @event, "3:state:AI,BI";
- push @event, "3:channelA:AI";
- push @event, "3:channelB:BI";
- push @event, "3:energyBow:pressed";
- } elsif ($db[0] == 10) {
- push @event, "3:state:A0,BI";
- push @event, "3:channelA:A0";
- push @event, "3:channelB:BI";
- push @event, "3:energyBow:pressed";
- } elsif ($db[0] == 11) {
- push @event, "3:state:BI";
- push @event, "3:channelB:BI";
- push @event, "3:energyBow:pressed";
- } elsif ($db[0] == 12) {
- push @event, "3:state:B0";
- push @event, "3:channelB:B0";
- push @event, "3:energyBow:pressed";
- } elsif ($db[0] == 13) {
- push @event, "3:state:AI";
- push @event, "3:channelA:AI";
- push @event, "3:energyBow:pressed";
- } elsif ($db[0] == 14) {
- push @event, "3:state:A0";
- push @event, "3:channelA:A0";
- push @event, "3:energyBow:pressed";
- } elsif ($db[0] == 15) {
- if (AttrVal($name, "sensorMode", "switch") eq "pushbutton") {
- push @event, "3:state:released";
- }
- push @event, "3:energyBow:released";
- }
- } elsif ($st =~ m/^blindsCtrl\.0[01]$/) {
- # EEP D2-05-0x
- my $channel = (($db[0] & 0xF0) >> 4) + 1;
- my $cmd = $db[0] & 0x0F;
- if ($cmd == 4) {
- # actuator status response
- if ($db[3] == 0) {
- push @event, "3:state:open";
- push @event, "3:endPosition" . sprintf('%02d', $channel) . ":open";
- push @event, "3:position" . sprintf('%02d', $channel) . ":" . $db[3];
- push @event, "3:position:" . $db[3];
- } elsif ($db[3] == 100) {
- push @event, "3:state:closed";
- push @event, "3:endPosition" . sprintf('%02d', $channel) . ":closed";
- push @event, "3:position" . sprintf('%02d', $channel) . ":" . $db[3];
- push @event, "3:position:" . $db[3];
- } elsif ($db[3] == 127) {
- push @event, "3:state:unknown";
- push @event, "3:endPosition" . sprintf('%02d', $channel) . ":unknown";
- push @event, "3:position" . sprintf('%02d', $channel) . ":unknown";
- push @event, "3:position:" . $db[3];
- } else {
- push @event, "3:state:" . $db[3];
- push @event, "3:endPosition" . sprintf('%02d', $channel) . ":not_reached";
- push @event, "3:position" . sprintf('%02d', $channel) . ":" . $db[3];
- push @event, "3:position:" . $db[3];
- }
- if ($db[2] == 127) {
- push @event, "3:anglePos" . sprintf('%02d', $channel) . ":unknown";
- } else {
- push @event, "3:anglePos" . sprintf('%02d', $channel) . ":" . $db[2];
- push @event, "3:anglePos:" . $db[2];
- }
- if ($db[1] == 0) {
- push @event, "3:block" . sprintf('%02d', $channel) . ":unlock";
- } elsif ($db[1] == 1) {
- push @event, "3:block" . sprintf('%02d', $channel) . ":lock";
- } elsif ($db[1] == 2) {
- push @event, "3:block" . sprintf('%02d', $channel) . ":alarm";
- } else {
- push @event, "3:block" . sprintf('%02d', $channel) . ":reserved";
- }
- } else {
- # unknown response
- }
- } elsif ($st eq "multisensor.01") {
- #####
- # Multisensor Windows Handle
- # (D2-06-01)
- # message type
- my $msgType = hex(substr($data, 0, 2));
- my $blinkInterval = 0;
- my $updateInterval = 0;
- my $waitingCmds = ReadingsVal($name, "waitingCmds", undef);
- if ($msgType == 0) {
- # sensor values
- my %onOffTrigger = (
- 0 => "off",
- 1 => "on",
- 14 => "invalid",
- 15 => "not_supported",
- );
- my $onOffTrigger = $db[8] >> 4;
- if (exists $onOffTrigger{$onOffTrigger}) {
- push @event, "3:burglaryAlarm:$onOffTrigger{$onOffTrigger}";
- } else {
- push @event, "3:burglaryAlarm:unknown";
- }
- $onOffTrigger = $db[8] & 15;
- if (exists $onOffTrigger{$onOffTrigger}) {
- push @event, "3:protectionAlarm:$onOffTrigger{$onOffTrigger}";
- } else {
- push @event, "3:protectionAlarm:unknown";
- }
- my %handlePosition = (
- 0 => "unknown",
- 1 => "up",
- 2 => "down",
- 3 => "left",
- 4 => "right",
- 14 => "invalid",
- 15 => "not_supported"
- );
- my %handleRPS = (
- 1 => 'D0',
- 2 => 'F0',
- 3 => 'E0',
- 4 => 'E0'
- );
- my $handlePosition = $db[7] >> 4;
- if (exists $handlePosition{$handlePosition}) {
- push @event, "3:handle:$handlePosition{$handlePosition}";
- } else {
- push @event, "3:handle:unknown";
- }
- # forward handle position (RPS telegam)
- if (exists($handleRPS{$handlePosition}) && defined(AttrVal($name, "subDefH", undef))) {
- EnOcean_SndRadio(undef, $hash, $packetType, "F6", $handleRPS{$handlePosition}, AttrVal($name, "subDefH", "00000000"), '20', 'FFFFFFFF');
- }
- my %windowState = (
- 0 => "undef",
- 1 => "not_tilted",
- 2 => "tilted",
- 14 => "invalid",
- 15 => "not_supported"
- );
- my %window1BS = (
- 1 => '09',
- 2 => '08'
- );
- my $windowState = $db[7] & 15;
- if (exists $windowState{$windowState}) {
- push @event, "3:window:$windowState{$windowState}";
- } else {
- push @event, "3:window:unknown";
- }
- # forward window state (1BS telegam)
- if (exists($window1BS{$windowState}) && defined(AttrVal($name, "subDefW", undef))) {
- EnOcean_SndRadio(undef, $hash, $packetType, "D5", $window1BS{$windowState}, AttrVal($name, "subDefW", "00000000"), '00', 'FFFFFFFF');
- }
- my %button = (
- 0 => "no_change",
- 1 => "pressed",
- 2 => "released",
- 14 => "invalid",
- 15 => "not_supported"
- );
- my $button = $db[6] >> 4;
- if (exists $button{$button}) {
- if ($button == 0 && ReadingsVal($name, "buttonRight", 'unknown') eq 'unknown') {
- push @event, "3:buttonRight:unknown";
- } elsif ($button == 0) {
- } else {
- push @event, "3:buttonRight:$button{$button}";
- }
- } else {
- push @event, "3:buttonRight:unknown";
- }
- $button = $db[6] & 15;
- if (exists $button{$button}) {
- if ($button == 0 && ReadingsVal($name, "buttonLeft", 'unknown') eq 'unknown') {
- push @event, "3:buttonLeft:unknown";
- } elsif ($button == 0) {
- } else {
- push @event, "3:buttonLeft:$button{$button}";
- }
- } else {
- push @event, "3:buttonLeft:unknown";
- }
- my $motion = $db[5] >> 4;
- if (exists $onOffTrigger{$motion}) {
- push @event, "3:motion:$onOffTrigger{$motion}";
- } else {
- push @event, "3:motion:unknown";
- }
- my %vacation = (
- 0 => "no_change",
- 1 => "absent",
- 2 => "present",
- 14 => "invalid",
- 15 => "not_supported"
- );
- my $vacation = $db[5] & 15;
- if (exists $vacation{$vacation}) {
- if ($vacation == 0 && ReadingsVal($name, "presence", 'unknown') eq 'unknown') {
- push @event, "3:presence:unknown";
- } elsif ($vacation == 0) {
- } else {
- push @event, "3:presence:$vacation{$vacation}";
- }
- } else {
- push @event, "3:presence:unknown";
- }
- my $temperature;
- if ($db[4] <= 250) {
- $temperature = sprintf "%0.1f", $db[4] * 80 / 250 - 20;
- push @event, "3:temperature:$temperature";
- } elsif ($db[4] <= 254) {
- $temperature = '-';
- push @event, "3:temperature:invalid";
- } elsif ($db[4] <= 255) {
- $temperature = '-';
- push @event, "3:temperature:not_supported";
- } else {
- $temperature = '-';
- push @event, "3:temperature:unknown";
- }
- my $humidity;
- if ($db[3] <= 200) {
- $humidity = $db[3] / 2;
- push @event, "3:humidity:$humidity";
- } elsif ($db[3] <= 254) {
- $humidity = '-';
- push @event, "3:humidity:invalid";
- } elsif ($db[3] <= 255) {
- $humidity = '-';
- push @event, "3:humidity:not_supported";
- } else {
- $humidity = '-';
- push @event, "3:humidity:unknown";
- }
- my $brightness = hex(substr($data, 14, 4));
- if ($brightness <= 60000) {
- push @event, "3:brightness:$brightness";
- } elsif ($brightness == 60001) {
- $brightness = '-';
- push @event, "3:brightness:over_range";
- } elsif ($brightness == 65534) {
- $brightness = '-';
- push @event, "3:brightness:invalid";
- } elsif ($brightness == 65535) {
- $brightness = '-';
- push @event, "3:brightness:not_supported";
- } else {
- $brightness = '-';
- push @event, "3:brightness:unknown";
- }
- my $energyStorage = ($db[0] >> 3) * 5;
- my $battery;
- if ($energyStorage <= 5) {
- $battery = 'low';
- push @event, "3:battery:low";
- push @event, "3:energyStorage:$energyStorage";
- } elsif ($energyStorage <= 100) {
- $battery = 'ok';
- push @event, "3:battery:ok";
- push @event, "3:energyStorage:$energyStorage";
- } else {
- $battery = '-';
- push @event, "3:battery:-";
- $energyStorage = '-';
- push @event, "3:energyStorage:unknown";
- }
- push @event, "3:state:T: $temperature H: $humidity E: $brightness M: $onOffTrigger{$motion}";
- } elsif ($msgType == 0x10) {
- # configuration report
- if (defined $waitingCmds) {
- $waitingCmds = $db[3] & 0x80 ? $waitingCmds & 0xDF | 32 : $waitingCmds & 0xDF;
- }
- push @event, "3:presence:" . ($db[3] & 0x80 ? 'absent' : 'present');
- push @event, "3:handleClosedClick:" . ($db[3] & 0x40 ? 'enabled' : 'disabled');
- push @event, "3:batteryLowClick:" . ($db[3] & 0x20 ? 'enabled' : 'disabled');
- $updateInterval = hex(substr($data, 4, 4));
- if ($updateInterval <= 4) {
- $updateInterval = '-';
- push @event, "3:updateInterval:unknown";
- } else {
- push @event, "3:updateInterval:$updateInterval";
- }
- $blinkInterval = $db[0];
- if ($blinkInterval <= 2) {
- $blinkInterval = '-';
- push @event, "3:blinkInterval:unknown";
- } else {
- push @event, "3:blinkInterval:$blinkInterval";
- }
- } elsif ($msgType == 0x20) {
- # log data 01
- push @event, "3:powerOns:" . substr($data, 2, 8);
- push @event, "3:alarms:" . substr($data, 10, 8);
- } elsif ($msgType == 0x21) {
- # log data 02
- push @event, "3:handleMoveClosed:" . substr($data, 2, 8);
- push @event, "3:handleMoveOpend:" . substr($data, 10, 8);
- push @event, "3:handleMoveTilted:" . substr($data, 18, 8);
- } elsif ($msgType == 0x22) {
- # log data 03
- push @event, "3:windowTilts:" . substr($data, 2, 8);
- } elsif ($msgType == 0x23) {
- # log data 04
- push @event, "3:buttonRightPresses:" . substr($data, 2, 8);
- push @event, "3:buttonLeftPresses:" . substr($data, 10, 8);
- } else {
- }
- if (defined $waitingCmds) {
- $updateInterval = 0;
- $blinkInterval = 0;
- if ($waitingCmds & 1) {
- $waitingCmds &= 0xFE;
- $updateInterval = ReadingsVal($name, "updateIntervalSet", 0);
- $updateInterval = $updateInterval =~ m/^\d+$/ ? $updateInterval : 0;
- $blinkInterval = ReadingsVal($name, "blinkIntervalSet", 0);
- $blinkInterval = $blinkInterval =~ m/^\d+$/ ? $blinkInterval : 0;
- CommandDeleteReading(undef, "$name .*Set");
- }
- CommandDeleteReading(undef, "$name waitingCmds");
- $data = sprintf "80%02X%04X%02X", $waitingCmds, $updateInterval, $blinkInterval;
- EnOcean_SndRadio(undef, $hash, $packetType, "D2", $data, AttrVal($name, "subDef", "00000000"), "00", $hash->{DEF});
- #EnOcean_multisensor_01Snd($crtl, $hash, $packetType);
- }
- } elsif ($st eq "roomCtrlPanel.01") {
- # Room Control Panel
- # (D2-11-01 - D2-11-08)
- my $msgType = hex(substr($data, 1, 1));
- my $setpointType = ReadingsVal($name, "setpointType", 'setpointShift');
- my $waitingCmds = ReadingsVal($name, "waitingCmds", 0);
- if (($waitingCmds & 0x80) == 0) {
- $setpointType = hex(substr($data, 0, 1)) & 8 ? 'setpointTemp' : 'setpointShift';
- push @event, "3:setpointType:$setpointType";
- }
- if ($msgType == 2) {
- my $trigger = ($db[5] & 0x60) >> 5;
- my %trigger = (0 => 'heartbeat', 1 => 'sensor', 2 => 'input');
- push @event, "3:trigger:" . $trigger{$trigger};
- my $temperature = sprintf "%0.1f", $db[4] / 255 * 40;
- push @event, "3:temperature:$temperature";
- my $humidity = sprintf "%d", $db[3] / 2.5;
- push @event, "3:humidity:$humidity";
- my $setpointShiftMax = ($db[0] & 0xF0) >> 4;
- push @event, "3:setpointShiftMax:$setpointShiftMax";
- my $setpointShift = int(0.5 + $db[2] * $setpointShiftMax / 128 * 10) / 10 - $setpointShiftMax;
- push @event, "3:setpointShift:" . sprintf "%0.1f", $setpointShift;
- push @event, "3:setpointBase:$db[1]";
- push @event, "3:setpointTemp:" . sprintf "%0.1f", ($db[1] + $setpointShift);
- my %fanSpeed = (0 => 'auto', 1 => 'off', 2 => 1, 3 => 2, 4 => 3);
- my $fanSpeed = ($db[0] & 0xE) >> 1;
- push @event, "3:fanSpeed:" . $fanSpeed{$fanSpeed};
- push @event, "3:occupancy:" . ($db[0] & 1 ? 'occupied' : 'unoccupied');
- push @event, "3:state:T: $temperature H: $humidity SPT: " . ($db[1] + $setpointShift) . " F: " . $fanSpeed{$fanSpeed};
- }
- CommandDeleteReading(undef, "$name waitingCmds");
- } elsif ($st eq "fanCtrl.00") {
- # Fan Control
- # (D2-20-00 - D2-20-02)
- if ($db[3] & 1) {
- # fan status message
- my $fanSpeed = $db[0];
- my $humidity = $db[1];
- my $roomSize = $db[2] & 15;
- my $roomSizeRef = ($db[2] & 0x30) >> 4;
- my $humidityCtrl = ($db[2] & 0xC0) >> 6;
- my $serviceInfo = ($db[3] & 14) >> 1;
- my $opMode = ($db[3] & 0xF0) >> 4;
- my %roomSizeTbl = (
- 0 => 25,
- 1 => 50,
- 2 => 75,
- 3 => 100,
- 4 => 125,
- 5 => 150,
- 6 => 175,
- 7 => 200,
- 8 => 225,
- 9 => 250,
- 10 => 275,
- 11 => 300,
- 12 => 325,
- 13 => 350,
- 14 => "max",
- 15 => "no_change"
- );
- if ($opMode == 0) {
- push @event, "3:state:off";
- } elsif ($opMode == 1) {
- push @event, "3:state:on";
- } elsif ($opMode == 15) {
- push @event, "3:state:not_supported";
- }
- if ($serviceInfo == 0) {
- push @event, "3:error:ok";
- } elsif ($serviceInfo == 1) {
- push @event, "3:error:air_filter";
- } elsif ($serviceInfo == 2) {
- push @event, "3:error:hardware";
- } elsif ($serviceInfo == 7) {
- push @event, "3:error:not_supported";
- }
- if ($humidityCtrl == 0) {
- push @event, "3:humidityCtrl:disabled";
- } elsif ($humidityCtrl == 1) {
- push @event, "3:humidityCtrl:enabled";
- } elsif ($humidityCtrl == 3) {
- push @event, "3:humidityCtrl:not_supported";
- }
- if ($roomSizeRef == 0) {
- push @event, "3:roomSizeRef:used";
- } elsif ($roomSizeRef == 1) {
- push @event, "3:roomSizeRef:not_used";
- } elsif ($roomSizeRef == 3) {
- push @event, "3:roomSizeRef:not_supported";
- }
- if ($roomSize < 15) {
- push @event, "3:roomSize:" . $roomSizeTbl{$roomSize};
- }
- if ($humidity >= 0 && $humidity <= 100) {
- push @event, "3:humidity:$humidity";
- } elsif ($humidity == 255) {
- push @event, "3:humidity:not_supported";
- }
- if ($fanSpeed >= 0 && $fanSpeed <= 100) {
- push @event, "3:fanSpeed:$fanSpeed";
- } elsif ($fanSpeed == 255) {
- push @event, "3:fanSpeed:not_supported";
- }
- }
- } elsif ($st eq "currentClamp.00") {
- # AC current clamp (EEP D2-32-00)
- if ($db[2] & 0x80) {
- # power fail
- push @event, "3:current1:0";
- push @event, "3:state:I1: 0";
- } else {
- my $current1 = hex(substr($data, 2, 3));
- if ($db[2] & 0x40) {
- # divisor = 1/10
- $current1 = $current1 / 10;
- push @event, "3:current1:" . sprintf "%0.1f", $current1;
- push @event, "3:state:I1: " . sprintf "%0.1f", $current1;
- } else {
- push @event, "3:current1:" . $current1;
- push @event, "3:state:I1: " . $current1;
- }
- }
- } elsif ($st eq "currentClamp.01") {
- # AC current clamp (EEP D2-32-01)
- if ($db[3] & 0x80) {
- # power fail
- push @event, "3:current1:0";
- push @event, "3:current2:0";
- push @event, "3:state:I1: 0 I2: 0";
- } else {
- my $current1 = hex(substr($data, 2, 3));
- my $current2 = hex(substr($data, 5, 3));
- if ($db[3] & 0x40) {
- # divisor = 1/10
- $current1 = $current1 / 10;
- $current2 = $current2 / 10;
- push @event, "3:current1:" . sprintf "%0.1f", $current1;
- push @event, "3:current2:" . sprintf "%0.1f", $current2;
- push @event, "3:state:I1: " . sprintf("%0.1f", $current1) . " I2: " . sprintf("%0.1f", $current2);
- } else {
- push @event, "3:current1:" . $current1;
- push @event, "3:current2:" . $current2;
- push @event, "3:state:I1: $current1 I2: $current2";
- }
- }
- } elsif ($st eq "currentClamp.02") {
- # AC current clamp (EEP D2-32-02)
- if ($db[5] & 0x80) {
- # power fail
- push @event, "3:current1:0";
- push @event, "3:current2:0";
- push @event, "3:current3:0";
- push @event, "3:state:I1: 0 I2: 0 I3: 0";
- } else {
- my $current1 = hex(substr($data, 2, 3));
- my $current2 = hex(substr($data, 5, 3));
- my $current3 = hex(substr($data, 8, 3));
- if ($db[5] & 0x40) {
- # divisor = 1/10
- $current1 = $current1 / 10;
- $current2 = $current2 / 10;
- $current3 = $current3 / 10;
- push @event, "3:current1:" . sprintf "%0.1f", $current1;
- push @event, "3:current2:" . sprintf "%0.1f", $current2;
- push @event, "3:current3:" . sprintf "%0.1f", $current3;
- push @event, "3:state:I1: " . sprintf("%0.1f", $current1) . " I2: " . sprintf("%0.1f", $current2) . " I3: " . sprintf("%0.1f", $current3);
- } else {
- push @event, "3:current1:" . $current1;
- push @event, "3:current2:" . $current2;
- push @event, "3:current3:" . $current3;
- push @event, "3:state:I1: $current1 I2: $current2 I3: $current3";
- }
- }
- } elsif ($st eq "ledCtrlState.00") {
- # LED Controller Status (EEP D2-40-00)
- if ($db[1] & 0x80) {
- # powerSwitch
- push @event, "3:powerSwitch:on";
- push @event, "3:state:on";
- } else {
- push @event, "3:powerSwitch:off";
- push @event, "3:state:off";
- }
- if ($db[1] & 0x40) {
- # Demand Response Mode
- push @event, "3:demandResp:on";
- } else {
- push @event, "3:demandResp:off";
- }
- if ($db[1] & 0x20) {
- # Daylight Harvesting
- push @event, "3:daylightHarvesting:on";
- } else {
- push @event, "3:daylightHarvesting:off";
- }
- my $occupancy = ($db[1] & 0x18) >> 3;
- if ($occupancy == 0) {
- push @event, "3:occupany:unoccupied";
- } elsif ($occupancy == 1) {
- push @event, "3:occupany:occupied";
- } else {
- push @event, "3:occupany:unknown";
- }
- push @event, "3:telegramType:" . ($db[1] & 4 ? "event" : "heartbeat");
- if ($db[0] >= 0 && $db[0] <= 200) {
- push @event, "3:dim:" . sprintf "%0.1f", $db[0] / 2;
- }
- } elsif ($st eq "ledCtrlState.01") {
- # LED Controller Status (EEP D2-40-01)
- if ($db[3] & 0x80) {
- # powerSwitch
- push @event, "3:powerSwitch:on";
- push @event, "3:state:on";
- } else {
- push @event, "3:powerSwitch:off";
- push @event, "3:state:off";
- }
- if ($db[3] & 0x40) {
- # Demand Response Mode
- push @event, "3:demandResp:on";
- } else {
- push @event, "3:demandResp:off";
- }
- if ($db[3] & 0x20) {
- # Daylight Harvesting
- push @event, "3:daylightHarvesting:on";
- } else {
- push @event, "3:daylightHarvesting:off";
- }
- my $occupancy = ($db[3] & 0x18) >> 3;
- if ($occupancy == 0) {
- push @event, "3:occupany:unoccupied";
- } elsif ($occupancy == 1) {
- push @event, "3:occupany:occupied";
- } else {
- push @event, "3:occupany:unknown";
- }
- push @event, "3:telegramType:" . ($db[3] & 4 ? "event" : "heartbeat");
- my ($red, $green, $blue) = ('00', '00', '00');
- if ($db[2] >= 0 && $db[2] <= 200) {
- push @event, "3:red:" . sprintf "%0.1f", $db[2] / 2;
- $red = sprintf "%02X", abs($db[2] / 200 * 255);
- }
- if ($db[1] >= 0 && $db[1] <= 200) {
- push @event, "3:green:" . sprintf "%0.1f", $db[1] / 2;
- $green = sprintf "%02X", abs($db[1] / 200 * 255);
- }
- if ($db[0] >= 0 && $db[0] <= 200) {
- push @event, "3:blue:" . sprintf "%0.1f", $db[0] / 2;
- $blue = sprintf "%02X", abs($db[0] / 200 * 255);
- }
- push @event, "3:rgb:" . $red . $green . $blue;
- } elsif ($st eq "heatRecovery.00") {
- # heat recovery ventilation
- # (D2-50-00)
- my $msgType = hex(substr($data, 0, 1)) >> 1;
- if ($msgType == 2) {
- my $ventilation = 'unknown';
- my %ventilation = (
- 0 => "off",
- 1 => 1,
- 2 => 2,
- 3 => 3,
- 4 => 4,
- 11 => "auto",
- 12 => "demand",
- 13 => "supplyAir",
- 14 => "exhaustAir"
- );
- $ventilation = $db[13] & 15;
- $ventilation = $ventilation{$ventilation} if (exists $ventilation{$ventilation});
- push @event, "3:ventilation:$ventilation";
- push @event, "3:fireplaceSafetyMode:" . ($db[12] & 8 ? 'enabled' : 'disabled');
- push @event, "3:heatExchangerBypass:" . ($db[12] & 4 ? 'opened' : 'closed');
- push @event, "3:supplyAirFlap:" . ($db[12] & 2 ? 'opened' : 'closed');
- push @event, "3:exhaustAirFlap:" . ($db[12] & 1 ? 'opened' : 'closed');
- push @event, "3:defrost:" . ($db[11] & 0x80 ? 'on' : 'off');
- push @event, "3:coolingProtection:" . ($db[11] & 0x40 ? 'on' : 'off');
- push @event, "3:outdoorAirHeater:" . ($db[11] & 0x20 ? 'on' : 'off');
- push @event, "3:supplyAirHeater:" . ($db[11] & 0x10 ? 'on' : 'off');
- push @event, "3:drainHeater:" . ($db[11] & 8 ? 'on' : 'off');
- push @event, "3:timerMode:" . ($db[11] & 4 ? 'on' : 'off');
- push @event, "3:filterMaintenance:" . ($db[11] & 2 ? 'required' : 'not_required');
- push @event, "3:weeklyTimer:" . ($db[11] & 1 ? 'on' : 'off');
- push @event, "3:roomTempCtrl:" . ($db[10] & 0x80 ? 'on' : 'off');
- my $airQuatity = $db[10] & 0x7F;
- if ($airQuatity <= 100) {
- push @event, "3:airQuality1:$airQuatity";
- } else {
- CommandDeleteReading(undef, "$name airQuality1");
- }
- push @event, "3:deviceMode:" . ($db[9] & 0x80 ? 'slave' : 'master');
- $airQuatity = $db[9] & 0x7F;
- if ($airQuatity <= 100) {
- push @event, "3:airQuality2:$airQuatity";
- } else {
- CommandDeleteReading(undef, "$name airQuality2");
- }
- my $outdoorTemp = ($db[8] & 0xFE) >> 1;
- #$outdoorTemp -= $outdoorTemp if ($outdoorTemp & 0x40);
- $outdoorTemp -= 64;
- push @event, "3:outdoorTemp:$outdoorTemp";
- my $supplyTemp = ($db[8] & 1) << 6 | ($db[7] & 0xFC) >> 2;
- #$supplyTemp -= $supplyTemp if ($supplyTemp & 0x40);
- $supplyTemp -= 64;
- push @event, "3:supplyTemp:$supplyTemp";
- my $roomTemp = ($db[7] & 3) << 5 | ($db[6] & 0xF8) >> 3;
- #$roomTemp -= $roomTemp if ($roomTemp & 0x40);
- $roomTemp -= 64;
- push @event, "3:roomTemp:$roomTemp";
- my $exhaustTemp = ($db[6] & 7) << 4 | ($db[5] & 0xF0) >> 4;
- #$exhaustTemp -= $exhaustTemp if ($exhaustTemp & 0x40);
- $exhaustTemp -= 64;
- push @event, "3:exhaustTemp:$exhaustTemp";
- push @event, "3:supplyAirFlow:". (($db[5] & 0x0F) << 2 | ($db[4] & 0xFC) >> 2);
- push @event, "3:exhaustAirFlow:" . (($db[4] & 3) << 8 | $db[3]);
- push @event, "3:supplyFanSpeed:". ($db[2] << 4 | ($db[1] & 0xF0) >> 4);
- push @event, "3:exhaustFanSpeed:" . (($db[1] & 0x0F) << 8 | $db[0]);
- push @event, "3:state:$ventilation";
- } elsif ($msgType == 3) {
- push @event, "3:SWVersion:" . (($db[13] & 0x0F) << 8 | $db[12]);
- push @event, "3:operationHours:" . (($db[11] << 8 | $db[10]) * 3);
- push @event, "3:input:" . sprintf("%02b %02b", $db[9], $db[8]);
- push @event, "3:output:" . sprintf("%02b %02b", $db[7], $db[6]);
- push @event, "3:info:" . sprintf("%02b %02b", $db[5], $db[4]);
- push @event, "3:fault:" . sprintf("%02b %02b %02b %02b", $db[3], $db[2], $db[1], $db[0]);
- }
- } elsif ($st eq "valveCtrl.00") {
- # Valve Control
- # (D2-A0-01)
- $db[0] &= 3;
- if (AttrVal($name, "devMode", "master") eq "slave") {
- # devMode slave
- if ($db[0] == 1 || $db[0] == 3) {
- push @event, "3:state:closes";
- } elsif ($db[0] == 2) {
- push @event, "3:state:opens";
- } elsif ($db[0] == 0) {
- my $state = ReadingsVal($name, "state", "-");
- if ($state eq "closed" || $state eq "closes") {
- $state = "01";
- } elsif ($state eq "open" || $state eq "opens") {
- $state = "02";
- } else {
- $state = "00";
- }
- EnOcean_SndRadio(undef, $hash, 1, "D2", $state, AttrVal($name, "subDef", "00000000"), "00", $hash->{DEF});
- }
- } else {
- #devMode master
- if ($db[0] == 1) {
- push @event, "3:state:closed";
- } elsif ($db[0] == 2) {
- push @event, "3:state:open";
- } elsif ($db[0] == 0 || $db[0] == 3) {
- push @event, "3:state:-";
- }
- }
- } elsif ($st eq "liquidLeakage.51") {
- # liquid leakage sensor
- push @event, "3:state:" . $db[0] & 3 ? 'wet' : 'dry';
- } elsif ($st eq "raw") {
- # raw
- push @event, "3:state:RORG: $rorg DATA: $data STATUS: $status ODATA: $odata";
- # display data bytes $db[0] ... $db[x]
- for (my $dbCntr = 0; $dbCntr <= $#db; $dbCntr++) {
- push @event, "3:DB_" . $dbCntr . ":" . $db[$dbCntr];
- }
- } else {
- # unknown devices
- push @event, "3:state:$data";
- }
- } elsif ($rorg eq "D1") {
- # MSC telegram
- if ($st eq "actuator.01") {
- if ($manufID eq "033") {
- if (substr($data, 3, 1) == 4) {
- my $getParam = ReadingsVal($name, "getParam", 0);
- if ($getParam == 8) {
- push @event, "3:loadClassification:no";
- push @event, "3:loadLink:" . ($db[1] & 16 ? "connected" : "disconnected");
- push @event, "3:loadOperation:3-wire";
- push @event, "3:loadState:" . ($db[1] & 64 ? "on" : "off");
- CommandDeleteReading(undef, "$name getParam");
- } elsif ($getParam == 7) {
- if ($db[0] & 4) {
- push @event, "3:devTempState:warning";
- } elsif ($db[0] & 2) {
- push @event, "3:devTempState:max";
- } else {
- push @event, "3:devTempState:ok";
- }
- push @event, "3:mainsPower:" . ($db[0] & 8 ? "failure" : "ok");
- if ($db[1] == 0xFF) {
- push @event, "3:devTemp:invalid";
- } else {
- push @event, "3:devTemp:" . $db[1];
- }
- CommandDeleteReading(undef, "$name getParam");
- } elsif ($getParam == 9) {
- push @event, "3:voltage:" . sprintf("%.2f", (hex(substr($data, 4, 4)) * 0.01));
- CommandDeleteReading(undef, "$name getParam");
- } elsif ($getParam == 0x81) {
- $hash->{READINGS}{serialNumber}{VAL} = substr($data, 4, 4);
- $hash->{READINGS}{getParam}{VAL} = 0x82;
- EnOcean_SndRadio(undef, $hash, $packetType, "D1", "033182", AttrVal($name, "subDef", "00000000"), "00", $hash->{DEF});
- } elsif ($getParam == 0x82) {
- push @event, "3:serialNumber:" . $hash->{READINGS}{serialNumber}{VAL} . substr($data, 4, 4);
- CommandDeleteReading(undef, "$name getParam");
- }
- }
- } elsif ($manufID eq "046") {
- my $cmd = hex(substr($data, 4, 2));
- if ($cmd == 3) {
- push @event, "3:firmwareVersion:" . substr($data, 6, 6);
- } elsif ($cmd == 5) {
- push @event, "3:taughtInDevNum:$db[0]";
- } elsif ($cmd == 7) {
- CommandDeleteReading(undef, "$name taughtInDevID.*");
- push @event, "3:taughtInDevID" . sprintf('%02d', $db[4]) . ":" . substr($data, 8, 8);
- }
- }
- } elsif ($st eq "raw") {
- # raw
- push @event, "3:state:RORG: $rorg DATA: $data STATUS: $status ODATA: $odata";
- push @event, "3:manufID:" . substr($data, 0, 3);
- # display data bytes $db[0] ... $db[x]
- for (my $dbCntr = 0; $dbCntr <= $#db; $dbCntr++) {
- push @event, "3:DB_" . $dbCntr . ":" . $db[$dbCntr];
- }
- } else {
- # unknown devices
- if(AttrVal($name, "blockUnknownMSC", "no") eq "yes") {
- push @event, "3:MSC:$data";
- }
- }
- } elsif ($rorg eq "D0") {
- my %smartAckMailbox = (0 => 'empty', 1 => 'not_exits', 2 => 'reset');
- push @event, "3:smartAckMailbox:" . $smartAckMailbox{$db[0]};
- } elsif ($rorg eq "B2") {
- # GP complete data (GPCD)
- my ($channel, $channelName, $value, $gpDef, $resolution) = (0, undef, undef, AttrVal($name, 'gpDef', undef), undef);
- return "generic profil not defined" if (!defined $gpDef);
- my @gpDef = split("[ \t][ \t]*", $gpDef);
- my ($readingFormat, $readingName, $readingType, $readingUnit, $readingValue, $valueType);
- $data = EnOcean_convHexToBit($data);
- while (defined $gpDef[$channel]) {
- ($channelName, undef, undef, undef, undef, $resolution, undef, undef, undef, undef) = split(':', $gpDef[$channel]);
- $resolution = 0 if (!defined $resolution);
- $data =~ m/^(.{$EnO_resolution[$resolution]})(.*)$/;
- $value = hex(unpack('H8', pack('B32', '0' x (32 - $EnO_resolution[$resolution]) . $1)));
- $data = $2;
- ($err, $logLevel, $response, $readingFormat, $readingName, $readingType, $readingUnit, $readingValue, $valueType)=
- EnOcean_gpConvDataToValue (undef, $hash, $channel, $value, $gpDef[$channel]);
- $channel ++;
- next if (defined $err);
- push @event, "3:$readingName:" . sprintf("$readingFormat", $readingValue);
- push @event, "3:" . $readingName . "Unit:$readingUnit";
- push @event, "3:" . $readingName . "ValueType:$valueType";
- push @event, "3:" . $readingName . "ChannelType:$readingType";
- }
- } elsif ($rorg eq "B3") {
- # GP selective data (GPSD)
- my $gpDef = AttrVal($name, "gpDef", undef);
- return "generic profil not defined" if (!defined $gpDef);
- my @gpDef = split("[ \t][ \t]*", $gpDef);
- my ($channel, $channelName, $resolution, $value);
- my ($readingFormat, $readingName, $readingType, $readingUnit, $readingValue, $valueType);
- #Log3 $name, 2, "EnOcean $name parse GPSD data: $data start";
- $data =~ m/^(.)(.*)$/;
- my $header = hex $1;
- $data = substr(EnOcean_convHexToBit($data), 4);
- #Log3 $name, 2, "EnOcean $name parse GPSD header: $header data: $data start";
- for (my $cntr = 1; $cntr <= $header; $cntr ++) {
- $data =~ m/^(.{6})(.*)$/;
- ($channel, $data) = (unpack('C', pack('B8', '00' . $1)), $2);
- #Log3 $name, 2, "EnOcean $name parse GPSD channel: $channel data: $data";
- if (defined $gpDef[$channel]) {
- ($channelName, undef, undef, undef, undef, $resolution, undef, undef, undef, undef) = split(':', $gpDef[$channel]);
- $resolution = 0 if (!defined $resolution || $resolution eq '');
- $data =~ m/^(.{$EnO_resolution[$resolution]})(.*)$/;
- $value = hex(unpack('H8', pack('B32', '0' x (32 - $EnO_resolution[$resolution]) . $1)));
- $data = $2;
- #Log3 $name, 2, "EnOcean $name parse GPSD channel: $channel value: " . $value . " data: $data";
- ($err, $logLevel, $response, $readingFormat, $readingName, $readingType, $readingUnit, $readingValue, $valueType) =
- EnOcean_gpConvDataToValue(undef, $hash, $channel, $value, $gpDef[$channel]);
- push @event, "3:$readingName:" . sprintf("$readingFormat", $readingValue);
- push @event, "3:" . $readingName . "Unit:$readingUnit";
- push @event, "3:" . $readingName . "ValueType:$valueType";
- push @event, "3:" . $readingName . "ChannelType:$readingType";
- }
- }
- } elsif ($rorg eq "B0" && $teach) {
- # GP teach in request (GPTI)
- #
- $data =~ m/^(....)(.*)$/;
- my $header = hex($1);
- $data = $2;
- my $purpose = ($header & 12) >> 2;
- if ($purpose == 0 || ($purpose == 2 && AttrVal($name, "subType", "") ne "genericProfile")) {
- # teach-in request
- $attr{$name}{comMode} = $header & 16 ? "biDir" : "uniDir";
- $attr{$name}{eep} = "B0-00-00";
- $attr{$name}{manufID} = sprintf "%03X", ($header & 0xFFE0) >> 5;
- $attr{$name}{subType} = "genericProfile";
- $attr{$name}{teachMethod} = 'GP';
- my $channel = 0;
- my $channelDir = "I";
- my $channelDef;
- my $channelName;
- my $channelType;
- my $cntr = 0;
- my $dataOutboundDefLen = 0;
- my @gpDef;
- my $signalType;
- #Log3 $name, 2, "EnOcean $name parse GPTI header: $header data: $data start";
- $data = EnOcean_convHexToBit($data);
- #Log3 $name, 2, "EnOcean $name parse GPTI data: $data start";
- while (length($data) >= 12) {
- last if ($cntr > 64);
- $cntr ++;
- $data =~ m/^(..)(.{8})(.*)$/;
- $channelType = unpack('C', pack('B8', '000000' . $1));
- $signalType = unpack('C', pack('B8', $2));
- $data = $3;
- #Log3 $name, 2, "EnOcean $name parse GPTI channel: $channel channelType: $channelType signalType: $signalType data: $data";
- if ($channelType == 0) {
- # teach-in information
- if ($signalType == 1) {
- # outbound channel description
- $channelDir = "O";
- $data =~ m/^(.{8})(.*)$/;
- $dataOutboundDefLen = unpack('C', pack('B8', $1));
- $data = $2;
- } elsif ($signalType == 2) {
- # produkt ID
- $data =~ m/^(.{32})(.*)$/;
- $attr{$name}{productID} = EnOcean_convBitToHex($1);
- $data = $2;
- }
- } elsif ($channelType == 1) {
- # data
- $data =~ m/^(..)(....)(.{8})(....)(.{8})(....)(.*)$/;
- $channelDef = $channelDir . ':' . $channelType . ':' . $signalType . ':' .
- unpack('C', pack('B8', '000000' . $1)) . ':' . unpack('C', pack('B8', '0000' . $2)) . ':' .
- unpack('c', pack('B8', $3)) . ':' . unpack('C', pack('B8', '0000' . $4)) . ':' .
- unpack('c', pack('B8', $5)) . ':' . unpack('C', pack('B8', '0000' . $6));
- $data = $7;
- if (defined $EnO_gpValueData{$signalType}{name}) {
- $channelName = $EnO_gpValueData{$signalType}{name};
- } else {
- $channelName = "none";
- }
- $gpDef[$channel] = $channelName . ':' . $channelDef;
- $channel ++;
- } elsif ($channelType == 2) {
- # flag
- $data =~ m/^(..)(.*)$/;
- $channelDef = $channelDir . ':' .$channelType . ':' . $signalType . ':' .
- unpack('C', pack('B8', '000000' . $1));
- $data = $2;
- if (defined $EnO_gpValueFlag{$signalType}{name}) {
- $channelName = $EnO_gpValueFlag{$signalType}{name};
- } else {
- $channelName = "none";
- }
- $gpDef[$channel] = $channelName . ':' . $channelDef;
- $channel ++;
- } elsif ($channelType == 3) {
- # enumeration
- $data =~ m/^(..)(....)(.*)$/;
- $channelDef = $channelDir . ':' .$channelType . ':' . $signalType . ':' .
- unpack('C', pack('B8', '000000' . $1)) . ':' . unpack('C', pack('B8', '0000' . $2));
- $data = $3;
- if (defined $EnO_gpValueEnum{$signalType}{name}) {
- $channelName = $EnO_gpValueEnum{$signalType}{name};
- } else {
- $channelName = "none";
- }
- $gpDef[$channel] = $channelName . ':' . $channelDef;
- $channel ++;
- }
- }
- $attr{$name}{gpDef} = join(' ', @gpDef);
- if (AttrVal($name, "comMode", "uniDir") eq "biDir") {
- # send GP Teach-In Response message
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDef", "biDir");
- $data = sprintf "%04X", ((hex(AttrVal($name, "manufID", "7FF")) << 5) | 8);
- EnOcean_SndCdm(undef, $hash, $packetType, "B1", $data, $subDef, "00", $senderID);
- Log3 $name, 2, "EnOcean $name GP teach-in response sent to $senderID";
- }
- my $mid = $attr{$name}{manufID};
- $mid = $EnO_manuf{$mid} if($EnO_manuf{$mid});
- push @event, "3:teach:GP teach-in accepted Manufacturer: $mid";
- Log3 $name, 2, "EnOcean $name GP teach-in accepted Manufacturer: $mid";
- # store attr subType, manufID, gpDef ...
- EnOcean_CommandSave(undef, undef);
- } elsif ($purpose == 1 || ($purpose == 2 && AttrVal($name, "subType", "") eq "genericProfile")) {
- # teach-in deletion request
- $deleteDevice = $name;
- if (AttrVal($name, "comMode", "uniDir") eq "biDir") {
- # send GP Teach-In Deletion Response message
- $data = sprintf "%04X", (hex(AttrVal($name, "manufID", "7FF")) << 5) | 16;
- EnOcean_SndCdm(undef, $hash, $packetType, "B1", $data, AttrVal($name, "subDef", "00000000"), "00", $senderID);
- Log3 $name, 2, "EnOcean $name GP teach-in deletion response send to $senderID";
- }
- Log3 $name, 2, "EnOcean $name GP teach-in delete request executed";
- }
- } elsif ($rorg eq "B1" && $teach) {
- # GP teach-in response (GPTR)
- $data =~ m/^(....)(.*)$/;
- my $header = hex($1);
- $data = $2;
- my $mid = sprintf "%03X", ($header & 0xFFE0) >> 5;
- my $purpose = ($header & 24) >> 3;
- if (exists $hash->{IODev}{helper}{gpRespWait}{$destinationID}) {
- if ($purpose == 0) {
- # teach-in rejected generally
- if ($hash->{IODev}{helper}{gpRespWait}{$destinationID}{teachInReq} eq "in") {
- push @event, "3:teach:GP teach-in rejected";
- Log3 $name, 2, "EnOcean $name GP teach-in rejected by $senderID";
- }
- # clear teach-in request
- delete $hash->{IODev}{helper}{gpRespWait}{$destinationID};
- } elsif ($purpose == 1) {
- # teach-in accepted
- if ($hash->{IODev}{helper}{gpRespWait}{$destinationID}{teachInReq} eq "in") {
- $attr{$name}{manufID} = $mid;
- # substitute subDef with DEF
- $attr{$name}{subDef} = $hash->{DEF};
- $hash->{DEF} = $senderID;
- $modules{EnOcean}{defptr}{$senderID} = $hash;
- delete $modules{EnOcean}{defptr}{$destinationID};
- EnOcean_CommandSave(undef, undef);
- $mid = $EnO_manuf{$mid} if($EnO_manuf{$mid});
- push @event, "3:teach:GP teach-in accepted Manufacturer: $mid";
- Log3 $name, 2, "EnOcean $name GP teach-in accepted by $senderID";
- }
- # clear teach-in request
- delete $hash->{IODev}{helper}{gpRespWait}{$destinationID};
- } elsif ($purpose == 2) {
- # teach-out accepted
- if ($hash->{IODev}{helper}{gpRespWait}{$destinationID}{teachInReq} eq "out") {
- if (defined $attr{$name}{subDef}) {
- delete $modules{EnOcean}{defptr}{$hash->{DEF}};
- $hash->{DEF} = $attr{$name}{subDef};
- $modules{EnOcean}{defptr}{$hash->{DEF}} = $hash;
- delete $attr{$name}{subDef};
- EnOcean_CommandSave(undef, undef);
- }
- push @event, "3:teach:GP teach-out accepted";
- Log3 $name, 2, "EnOcean $name GP teach-out accepted";
- }
- # clear teach-in request
- delete $hash->{IODev}{helper}{gpRespWait}{$destinationID};
- } else {
- if ($hash->{IODev}{helper}{gpRespWait}{$destinationID}{teachInReq} eq "in") {
- # rejected channels outbound or inbound, sent teach-in response with teach-out
- $data = sprintf "%04X", (hex(AttrVal($name, "manufID", "7FF")) << 5) | 16;
- EnOcean_SndCdm(undef, $hash, $packetType, "B1", $data, $destinationID, "00", $senderID);
- push @event, "3:teach:GP teach-in channels rejected, sent teach-out";
- Log3 $name, 2, "EnOcean $name GP teach-in channels rejected, sent teach-out to $senderID";
- }
- # clear teach-in request
- delete $hash->{IODev}{helper}{gpRespWait}{$destinationID};
- }
- } else {
- Log3 $name, 2, "EnOcean $name GP teach-in response from $senderID received, teach-in request unknown";
- }
- } elsif ($rorg eq "D4" && $teach) {
- # UTE - Universal Uni- and Bidirectional Teach-In / Teach-Out
- #
- Log3 $name, 5, "EnOcean $name UTE teach-in received from $senderID";
- my $rorg = sprintf "%02X", $db[0];
- my $func = sprintf "%02X", $db[1];
- my $type = sprintf "%02X", $db[2];
- my $mid = sprintf "%03X", ((($db[3] & 7) << 8) | $db[4]);
- my $devChannel = $db[5];
- my $comMode = $db[6] & 0x80 ? "biDir" : "uniDir";
- my $comModeUTE = AttrVal($hash->{IODev}{NAME}, "comModeUTE", "auto");
- $comMode = $comModeUTE if ($comModeUTE ne 'auto');
- my $subType = "$rorg.$func.$type";
- if (($db[6] & 0xF) == 0) {
- # Teach-In Query telegram received
- my $teachInReq = ($db[6] & 0x30) >> 4;
- if ($teachInReq == 0 || $teachInReq == 2) {
- # Teach-In Request
- $attr{$name}{teachMethod} = 'UTE';
- if(exists $EnO_eepConfig{$subType}) {
- # Teach-In EEP supported
- foreach my $attrCntr (keys %{$EnO_eepConfig{$subType}{attr}}) {
- $attr{$name}{$attrCntr} = $EnO_eepConfig{$subType}{attr}{$attrCntr};
- }
- $attr{$name}{manufID} = $mid;
- $attr{$name}{devChannel} = $devChannel;
- $attr{$name}{comMode} = $comMode;
- $mid = $EnO_manuf{$mid} if($EnO_manuf{$mid});
- $attr{$name}{eep} = "$rorg-$func-$type";
- if (exists($hash->{helper}{teachInWait}) && $hash->{helper}{teachInWait} =~ m/^UTE|STE$/) {
- $attr{$filelogName}{logtype} = $EnO_eepConfig{$subType}{GPLOT} . 'text'
- if (exists $attr{$filelogName}{logtype});
- EnOcean_CreateSVG(undef, $hash, undef);
- delete $hash->{helper}{teachInWait};
- }
- $subType = $EnO_eepConfig{$subType}{attr}{subType};
- #$attr{$name}{subType} = $subType;
- push @event, "3:teach:UTE teach-in accepted EEP $rorg-$func-$type Manufacturer: $mid";
- if (!($db[6] & 0x40)) {
- # UTE Teach-In-Response expected
- # send UTE Teach-In Response message
- $data = (sprintf "%02X", $db[6] & 0x80 | 0x11) . substr($data, 2, 12);
- if ($comMode eq "biDir") {
- ($err, $subDef) = EnOcean_AssignSenderID(undef, $hash, "subDef", $comMode);
- } else {
- $subDef = "00000000";
- }
- EnOcean_SndRadio(undef, $hash, $packetType, "D4", $data, $subDef, "00", $senderID);
- Log3 $name, 2, "EnOcean $name UTE teach-in response send to $senderID";
- }
- Log3 $name, 2, "EnOcean $name UTE teach-in accepted EEP $rorg-$func-$type Manufacturer: $mid";
- } else {
- # Teach-In EEP not supported
- $attr{$name}{subType} = "raw";
- $attr{$name}{manufID} = $mid;
- $attr{$name}{devChannel} = $devChannel;
- $attr{$name}{comMode} = $comMode;
- $mid = $EnO_manuf{$mid} if($EnO_manuf{$mid});
- push @event, "3:teach:UTE teach-in accepted EEP $rorg-$func-$type not supported Manufacturer: $mid";
- # send EEP Teach-In Response message
- if (!($db[6] & 0x40)) {
- # UTE Teach-In-Response expected
- # send UTE Teach-In Response message
- $data = (sprintf "%02X", $db[6] & 0x80 | 0x31) . substr($data, 2, 12);
- EnOcean_SndRadio(undef, $hash, $packetType, "D4", $data, "00000000", "00", $senderID);
- Log3 $name, 2, "EnOcean $name UTE teach-in response send to $senderID";
- }
- Log3 $name, 2, "EnOcean $name UTE teach-in accepted EEP $rorg-$func-$type not supported Manufacturer: $mid";
- }
- # store attr subType, manufID ...
- EnOcean_CommandSave(undef, undef);
- } elsif ($teachInReq == 1) {
- # Teach-In Deletion Request
- $deleteDevice = $name;
- if (!($db[6] & 0x40)) {
- # UTE Teach-In Deletion Response expected
- # send UTE Teach-In Deletion Response message
- $data = (sprintf "%02X", $db[6] & 0x80 | 0x21) . substr($data, 2, 12);
- EnOcean_SndRadio(undef, $hash, $packetType, "D4", $data, AttrVal($name, "subDef", "00000000"), "00", $senderID);
- Log3 $name, 2, "EnOcean $name UTE teach-in deletion response send to $senderID";
- }
- Log3 $name, 2, "EnOcean $name UTE teach-in delete request executed";
- }
- } else {
- # Teach-In Respose telegram received
- my $teachInAccepted = ($db[6] & 0x30) >> 4;
- Log3 $name, 5, "EnOcean $name UTE teach-in response message from $senderID received";
- if (exists $hash->{IODev}{helper}{UTERespWait}{$destinationID}) {
- if ($comMode eq "uniDir") {
- $attr{$name}{manufID} = $mid;
- if ($teachInAccepted == 0) {
- $teachInAccepted = "request not accepted";
- } elsif ($teachInAccepted == 1){
- $teachInAccepted = "teach-in accepted";
- } elsif ($teachInAccepted == 2){
- $teachInAccepted = "teach-out accepted";
- } else {
- $teachInAccepted = "EEP not supported";
- }
- $mid = $EnO_manuf{$mid} if($EnO_manuf{$mid});
- push @event, "3:teach:UTE $teachInAccepted EEP $rorg-$func-$type Manufacturer: $mid";
- Log3 $name, 2, "EnOcean $name UTE $teachInAccepted EEP $rorg-$func-$type Manufacturer: $mid";
- } else {
- if ($hash->{IODev}{helper}{UTERespWait}{$destinationID}{teachInReq} eq "in") {
- # Teach-In Request
- if ($teachInAccepted == 0) {
- $teachInAccepted = "request not accepted";
- } elsif ($teachInAccepted == 1){
- $teachInAccepted = "teach-in accepted";
- $attr{$name}{subDef} = $hash->{DEF};
- $hash->{DEF} = $senderID;
- $modules{EnOcean}{defptr}{$senderID} = $hash;
- delete $modules{EnOcean}{defptr}{$destinationID};
- $attr{$name}{manufID} = $mid;
- # store attr subType, manufID ...
- EnOcean_CommandSave(undef, undef);
- } elsif ($teachInAccepted == 2){
- $teachInAccepted = "teach-out accepted";
- } else {
- $teachInAccepted = "EEP not supported";
- }
- $mid = $EnO_manuf{$mid} if($EnO_manuf{$mid});
- push @event, "3:teach:UTE $teachInAccepted EEP $rorg-$func-$type Manufacturer: $mid";
- Log3 $name, 2, "EnOcean $name UTE $teachInAccepted EEP $rorg-$func-$type Manufacturer: $mid";
- } elsif ($hash->{IODev}{helper}{UTERespWait}{$destinationID}{teachInReq} eq "out") {
- # Teach-In Deletion Request
- if ($teachInAccepted == 0) {
- $teachInAccepted = "request not accepted";
- } elsif ($teachInAccepted == 1){
- $teachInAccepted = "teach-in accepted";
- } elsif ($teachInAccepted == 2){
- $teachInAccepted = "teach-out accepted";
- if (defined $attr{$name}{subDef}) {
- delete $modules{EnOcean}{defptr}{$hash->{DEF}};
- $hash->{DEF} = $attr{$name}{subDef};
- $modules{EnOcean}{defptr}{$hash->{DEF}} = $hash;
- delete $attr{$name}{subDef};
- EnOcean_CommandSave(undef, undef);
- }
- } else {
- $teachInAccepted = "EEP not supported";
- }
- $mid = $EnO_manuf{$mid} if($EnO_manuf{$mid});
- push @event, "3:teach:UTE $teachInAccepted EEP $rorg-$func-$type Manufacturer: $mid";
- Log3 $name, 2, "EnOcean $name UTE $teachInAccepted EEP $rorg-$func-$type Manufacturer: $mid";
- }
- }
- # clear teach-in request
- delete $hash->{IODev}{helper}{UTERespWait}{$destinationID};
- } else {
- # teach-in request unknown, delete response device, no action
- $deleteDevice = $name;
- Log3 $name, 2, "EnOcean $name UTE teach-in request unknown";
- }
- }
- } elsif ($rorg eq "35" && $teach) {
- # Secure Teach-In
- ($err, $msg) = EnOcean_sec_parseTeachIn($hash, $data, $subDef, $destinationID);
- if (defined $err) {
- Log3 $name, 2, "EnOcean $name secure teach-in ERROR: $err";
- return "";
- }
- Log3 $name, 3, "EnOcean $name secure teach-in $msg";
- EnOcean_CommandSave(undef, undef);
- return "";
- } elsif ($rorg eq "C6" && $smartAckLearn) {
- # Smart Ack Learn Request
- #####
- $data =~ m/^(....)(..)(..)(..)(..)(........)$/;
- my $subType = "$2.$3.$4";
- my $postmasterID = '0' x 8;
- my $mid = '7FF';
- my $responseTime = 150;
- my $sendData = '';
- if (exists $EnO_eepConfig{$subType}) {
- if (($db[9] & 0xF8) == 0xF8) {
- # Smart Ack send by sensor
- $attr{$name}{subType} = $EnO_eepConfig{$subType}{attr}{subType};
- $attr{$name}{eep} = "$3-$4-$5";
- $mid = substr(sprintf("%04X", hex($1) & 0x7FF), 1);
- $attr{$name}{manufID} = $mid;
- $mid = $EnO_manuf{$mid} if(exists $EnO_manuf{$mid});
- $attr{$name}{repeaterID} = $6;
- $postmasterID = $6;
- $attr{$name}{postmasterID} = $postmasterID;
- $attr{$name}{teachMethod} = 'smartAck';
- $hash->{SmartAckRSSI} = - hex($db[4]);
- foreach my $attrCntr (keys %{$EnO_eepConfig{$subType}{attr}}) {
- if ($attrCntr ne "subDef") {
- $attr{$name}{$attrCntr} = $EnO_eepConfig{$subType}{attr}{$attrCntr};
- }
- }
- if (defined AttrVal($name, 'subDef', undef)) {
- $subDef = $attr{$name}{subDef};
- } else {
- $subDef = EnOcean_CheckSenderID('getNextID', $hash->{IODev}{NAME}, "00000000");
- $attr{$name}{subDef} = $subDef;
- }
- # create mailbox
- $sendData = sprintf "03%04X00%04X%04X", $responseTime, $postmasterID, $hash->{DEF};
- EnOcean_SndRadio(undef, $hash, 6, $rorg, $sendData, $subDef, '00', $hash->{DEF});
- # next commands will be sent with a delay
- #usleep($responseTime * 1000);
- # send learn reply
- $sendData = sprintf "01%04X00%04X", $responseTime, $hash->{DEF};
- EnOcean_SndRadio(undef, $hash, 1, 'C7', $sendData, $subDef, '00', $hash->{DEF});
- push @event, "3:teach:Smart Ack teach-in accepted EEP " . $attr{$name}{eep} . " Manufacturer: $mid";
- Log3 $name, 2, "EnOcean $name Smart Ack teach-in accepted EEP " . $attr{$name}{eep} . " Manufacturer: $mid";
- EnOcean_CommandSave(undef, undef);
- }
- } else {
- # EEP not supported
- # send learn reply
- $sendData = sprintf "01%04X10%04X", $responseTime, $hash->{DEF};
- EnOcean_SndRadio(undef, $hash, 1, 'C7', $sendData, '0' x 8, '00', $hash->{DEF});
- CommandDelete(undef, $name);
- Log3 $name, 2, "EnOcean $name Smart Ack teach-in not accepted EEP " . $attr{$name}{eep} . " Manufacturer: $mid";
- }
- } elsif ($packetType == 7) {
- # packet type REMOTE_MAN_COMMAND
- my $remoteCode = AttrVal($name, 'remoteCode', '0' x 8);
- my $remoteLastStatusReturnCode = $manufID eq '7FF' ? '00' : '04';
- my $remoteManagement = AttrVal($name, "remoteManagement", "off");
- my $sendData = '';
- push @event, "3:remoteLastFunctionNumber:" . sprintf("%03X", $funcNumber);
- if ($funcNumber == 1 && $remoteManagement eq 'client') {
- # unlock
- if ($data eq uc($remoteCode) && !exists($hash->{RemoteClientUnlockFailed})) {
- $hash->{RemoteClientUnlock} = 1;
- my %functionHash = (hash => $hash, param => 'RemoteClientUnlock');
- RemoveInternalTimer(\%functionHash);
- InternalTimer(gettimeofday() + 1800, 'EnOcean_cdmClearHashVal', \%functionHash, 0);
- Log3 $name, 2, "EnOcean $name RMCC unlock request executed.";
- } else {
- $remoteLastStatusReturnCode = '02';
- $hash->{RemoteClientUnlockFailed} = 1;
- my %functionHash = (hash => $hash, param => 'RemoteClientUnlockFailed');
- RemoveInternalTimer(\%functionHash);
- InternalTimer(gettimeofday() + 30, 'EnOcean_cdmClearHashVal', \%functionHash, 0);
- Log3 $name, 2, "EnOcean $name RMCC unlock request not executed, remote Code $data wrong.";
- }
- push @event, "3:remoteLastStatusReturnCode:$remoteLastStatusReturnCode";
- } elsif ($funcNumber == 2 && $remoteManagement eq 'client') {
- # lock
- if ($hash->{RemoteClientUnlock} && $data eq uc($remoteCode)) {
- delete $hash->{RemoteClientUnlock};
- my %functionHash = (hash => $hash, param => 'RemoteClientUnlock');
- RemoveInternalTimer(\%functionHash);
- Log3 $name, 2, "EnOcean $name RMCC lock request executed.";
- } else {
- $remoteLastStatusReturnCode = '02';
- Log3 $name, 2, "EnOcean $name RMCC lock request not executed.";
- }
- push @event, "3:remoteLastStatusReturnCode:$remoteLastStatusReturnCode";
- } elsif ($funcNumber == 3 && $remoteManagement eq 'client') {
- # set code
- if ($hash->{RemoteClientUnlock} && $data =~ m/^[A-Fa-f0-9]{8}$/ && uc($data) ne 'FFFFFFFF') {
- $attr{$name}{remoteCode} = $data;
- EnOcean_CommandSave(undef, undef);
- Log3 $name, 2, "EnOcean $name RMCC set code request executed.";
- } else {
- $remoteLastStatusReturnCode = '05';
- Log3 $name, 2, "EnOcean $name RMCC set code request not executed.";
- }
- push @event, "3:remoteLastStatusReturnCode:$remoteLastStatusReturnCode";
- } elsif ($funcNumber == 4 && $remoteManagement eq 'client') {
- # query ID
- if ($hash->{RemoteClientUnlock}) {
- my $eepRcv = hex(substr($data, 0, 6)) >> 3;
- my $rorg = sprintf "%02X", ($eepRcv >> 13);
- my $func = sprintf "%02X", (($eepRcv & 0x1F80) >> 7);
- my $type = sprintf "%02X", ($eepRcv & 127);
- $eepRcv = "$rorg-$func-$type";
- if ($hash->{RemoteClientUnlock} && $eep eq $eepRcv) {
- $sendData = '06040' . AttrVal($name, 'manufID', '7FF') . substr($data, 0, 4) . sprintf("%02X", hex(substr($data, 4, 2)) & 0xF8);
- EnOcean_SndRadio(undef, $hash, $packetType, $rorg, $sendData, '0' x 8, '0F', $senderID);
- Log3 $name, 2, "EnOcean $name RMCC query ID answer sent.";
- } else {
- $remoteLastStatusReturnCode = '03';
- Log3 $name, 2, "EnOcean $name RMCC query ID request not executed, EEP $eepRcv wrong or client locked.";
- }
- } else {
- $remoteLastStatusReturnCode = '07';
- Log3 $name, 2, "EnOcean $name RMCC query ID request not executed.";
- }
- push @event, "3:remoteLastStatusReturnCode:$remoteLastStatusReturnCode";
- } elsif ($funcNumber == 5 && $remoteManagement eq 'client') {
- # action
- if ($hash->{RemoteClientUnlock}) {
- Log3 $name, 2, "EnOcean $name RMCC action request executed";
- } else {
- $remoteLastStatusReturnCode = '01';
- Log3 $name, 2, "EnOcean $name RMCC action request not executed.";
- }
- push @event, "3:remoteLastStatusReturnCode:$remoteLastStatusReturnCode";
- } elsif ($funcNumber == 6 && $remoteManagement eq 'client') {
- # ping
- my $eep = AttrVal($name, "eep", "C5-00-00");
- if ($eep =~ m/^([A-Fa-f0-9]{2})-([A-Fa-f0-9]{2})-([A-Fa-f0-9]{2})$/i) {
- $eep = (((hex($1) << 6) | hex($2)) << 7) | hex($3);
- } else {
- $eep = (((hex("C5") << 6) | hex("00")) << 7) | hex("00");
- }
- push @event, "3:remoteLastStatusReturnCode:$remoteLastStatusReturnCode";
- push @event, "3:remoteRSSI:" . -$RSSI;
- $sendData = '06060' . AttrVal($name, 'manufID', '7FF') . sprintf("%04X%02X", $eep << 3, $RSSI);
- EnOcean_SndRadio(undef, $hash, $packetType, $rorg, $sendData, '0' x 8, '0F', $senderID);
- Log3 $name, 2, "EnOcean $name RMCC ping answer executed";
- } elsif ($funcNumber == 7 && $remoteManagement eq 'client') {
- # query function
- if ($hash->{RemoteClientUnlock}) {
- $sendData = '06070' . AttrVal($name, 'manufID', '7FF') . '020107FF';
- EnOcean_SndRadio(undef, $hash, $packetType, $rorg, $sendData, '0' x 8, '0F', $senderID);
- Log3 $name, 2, "EnOcean $name RMCC query function answer sent.";
- } else {
- $remoteLastStatusReturnCode = '07';
- Log3 $name, 2, "EnOcean $name RMCC query function request not executed.";
- }
- push @event, "3:remoteLastStatusReturnCode:$remoteLastStatusReturnCode";
- } elsif ($funcNumber == 8 && $remoteManagement eq 'client') {
- # query status
- if ($hash->{RemoteClientUnlock}) {
- $sendData = '06080' . AttrVal($name, 'manufID', '7FF') . '000' . ReadingsVal($name, "remoteLastFunctionNumber", "000") .
- ReadingsVal($name, "$remoteLastStatusReturnCode", '00');
- EnOcean_SndRadio(undef, $hash, $packetType, $rorg, $sendData, '0' x 8, '0F', $senderID);
- Log3 $name, 2, "EnOcean $name RMCC query status answer sent.";
- } else {
- $remoteLastStatusReturnCode = '07';
- Log3 $name, 2, "EnOcean $name RMCC query status request not executed.";
- }
- push @event, "3:remoteLastStatusReturnCode:$remoteLastStatusReturnCode";
- } elsif ($funcNumber == 0x201) {
- $data =~ m/^(..)(..)(..)(..)$/;
- my ($rorg, $func, $type, $flag) = ($1, $2, $3, hex($4));
- if ($flag == 1) {
- # learn in
- if (exists $EnO_eepConfig{"$rorg.$func.$type"}) {
- $attr{$name}{eep} = "$rorg-$func-$type";
- #$attr{$name}{remoteID} = $remoteID if (defined $remoteID);
- $attr{$name}{remoteManagement} = "client";
- $attr{$name}{teachMethod} = 'RPC';
- foreach my $attrCntr (keys %{$EnO_eepConfig{"$rorg.$func.$type"}{attr}}) {
- if ($attrCntr eq "subDef") {
- if (!exists $attr{$name}{$attrCntr}) {
- $attr{$name}{$attrCntr} = EnOcean_CheckSenderID($EnO_eepConfig{"$rorg.$func.$type"}{attr}{$attrCntr}, $hash->{IODev}{NAME}, "00000000");
- }
- } else {
- $attr{$name}{$attrCntr} = $EnO_eepConfig{"$rorg.$func.$type"}{attr}{$attrCntr};
- }
- }
- EnOcean_CreateSVG(undef, $hash, $attr{$name}{eep});
- push @event, "3:teach:RPC teach-in accepted EEP $rorg-$func-$type Manufacturer: " .
- (exists($EnO_manuf{$manufID}) ? $EnO_manuf{$manufID} : $manufID);
- Log3 $name, 2, "EnOcean $name RPC teach-in with EEP $rorg-$func-$type ManufacturerID: $manufID accepted.";
- } else {
- Log3 $name, 2, "EnOcean $name RPC teach-in with EEP $rorg-$func-$type ManufacturerID: $manufID not supported.";
- }
- } elsif ($flag == 3) {
- # learn out
- #Log3 $name, 2, "EnOcean $name device $name deleted";
- CommandDelete(undef, $name);
- Log3 $name, 2, "EnOcean $name RPC teach-out with EEP $rorg-$func-$type ManufacturerID: $manufID executed.";
- EnOcean_CommandSave(undef, undef);
- } else {
- Log3 $name, 2, "EnOcean $name RPC learn function $flag not supported";
- }
- push @event, "3:remoteLastStatusReturnCode:$remoteLastStatusReturnCode";
- } elsif ($funcNumber == 0x240 && $remoteManagement eq 'manager') {
- # acknowledge
- $remoteLastStatusReturnCode = '00';
- delete $iohash->{helper}{remoteAnswerWait}{$funcNumber}{hash};
- push @event, "3:remoteLastStatusReturnCode:$remoteLastStatusReturnCode";
- Log3 $name, 2, "EnOcean $name RPC acknowledge received";
- } elsif ($funcNumber == 0x604 && $remoteManagement eq 'manager') {
- # query id answer
- my $eep = hex(substr($data, 0, 6)) >> 3;
- my $rorg = sprintf "%02X", ($eep >> 13);
- my $func = sprintf "%02X", (($eep & 0x1F80) >> 7);
- my $type = sprintf "%02X", ($eep & 127);
- $attr{$name}{remoteManufID} = $manufID;
- $manufID = $EnO_manuf{$manufID} if($EnO_manuf{$manufID});
- $attr{$name}{remoteEEP} = "$rorg-$func-$type";
- my $subType = "$rorg.$func.$type";
- #$attr{$name}{subType} = $EnO_eepConfig{$subType}{attr}{subType} if($EnO_manuf{$subType});
- $remoteLastStatusReturnCode = '00';
- delete $iohash->{helper}{remoteAnswerWait}{$funcNumber}{hash};
- push @event, "3:remoteLastStatusReturnCode:$remoteLastStatusReturnCode";
- Log3 $name, 2, "EnOcean $name RMCC query ID answer received EEP $rorg-$func-$type Manufacturer: $manufID";
- EnOcean_CommandSave(undef, undef);
- } elsif ($funcNumber == 0x606 && $remoteManagement eq 'manager') {
- # ping answer
- my $eep = hex(substr($data, 0, 6)) >> 3;
- my $rorg = sprintf "%02X", ($eep >> 13);
- my $func = sprintf "%02X", (($eep & 0x1F80) >> 7);
- my $type = sprintf "%02X", ($eep & 127);
- $attr{$name}{remoteManufID} = $manufID;
- $manufID = $EnO_manuf{$manufID} if($EnO_manuf{$manufID});
- $attr{$name}{remoteEEP} = "$rorg-$func-$type";
- my $subType = "$rorg.$func.$type";
- #$attr{$name}{subType} = $EnO_eepConfig{$subType}{attr}{subType} if($EnO_manuf{$subType});
- push @event, "3:remoteRSSI:" . -$RSSI;
- $remoteLastStatusReturnCode = '00';
- delete $iohash->{helper}{remoteAnswerWait}{$funcNumber}{hash};
- push @event, "3:remoteLastStatusReturnCode:$remoteLastStatusReturnCode";
- Log3 $name, 2, "EnOcean $name RMCC ping answer received EEP $rorg-$func-$type Manufacturer: $manufID";
- EnOcean_CommandSave(undef, undef);
- } elsif ($funcNumber == 0x607 && $remoteManagement eq 'manager') {
- # query function answer
- CommandDeleteReading(undef, "$name remoteFunction.*");
- my $count = 1;
- my $len = length($data);
- while ($len > 0) {
- $data =~ m/^.(...).(...)(.*)$/;
- push @event, "3:remoteFunction" . sprintf("%02d", $count) . ":$1:$2:" . (exists($EnO_extendedRemoteFunctionCode{hex($1)}) ? $EnO_extendedRemoteFunctionCode{hex($1)} : '-');
- $count ++;
- $data = $3;
- $len -= 8;
- }
- $remoteLastStatusReturnCode = '00';
- delete $iohash->{helper}{remoteAnswerWait}{$funcNumber}{hash};
- push @event, "3:remoteLastStatusReturnCode:$remoteLastStatusReturnCode";
- Log3 $name, 2, "EnOcean $name RMCC query function answer received";
- } elsif ($funcNumber == 0x608 && $remoteManagement eq 'manager') {
- # query status answer
- delete $iohash->{helper}{remoteAnswerWait}{$funcNumber}{hash};
- push @event, "3:remoteLastFunctionNumber:" . substr($data, 3, 3);
- push @event, "3:remoteLastStatusReturnCode:" . substr($data, 6, 2);
- Log3 $name, 2, "EnOcean $name RMCC query status answer received LastFunction: " . substr($data, 3, 3) .
- " LastFunctionCode: " . substr($data, 6, 2);
- } elsif ($funcNumber == 0x810 && $remoteManagement eq 'manager') {
- # teach-in supported link tables response
- delete $iohash->{helper}{remoteAnswerWait}{$funcNumber}{hash};
- push @event, "3:remoteLastStatusReturnCode:$remoteLastStatusReturnCode";
- my $supportFlags = hex(substr($data, 0, 2));
- my $linkTableInCurrent = substr($data, 6, 2) && $supportFlags & 0x10 ? substr($data, 6, 2) : '00';
- my $linkTableInMax = substr($data, 8, 2) && $supportFlags & 0x10 ? substr($data, 8, 2) : '00';
- my $linkTableOutCurrent = substr($data, 2, 2) && $supportFlags & 0x20 ? substr($data, 2, 2) : '00';
- my $linkTableOutMax = substr($data, 4, 2) && $supportFlags & 0x20 ? substr($data, 4, 2) : '00';
- push @event, "3:remoteLearn:" . ($supportFlags & 0x80 ? 'supported' : 'not_supported');
- push @event, "3:remoteLinkTableIn:" . ($supportFlags & 0x10 ? 'supported' : 'not_supported');
- push @event, "3:remoteLinkTableOut:" . ($supportFlags & 0x20 ? 'supported' : 'not_supported');
- push @event, "3:remoteLinkTableInCurrent:" . $linkTableInCurrent if ($supportFlags & 0x10);
- push @event, "3:remoteLinkTableInMax:" . $linkTableInMax if ($supportFlags & 0x10);
- push @event, "3:remoteLinkTableOutCurrent:" . $linkTableOutCurrent if ($supportFlags & 0x20);
- push @event, "3:remoteLinkTableOutMax:" . $linkTableOutMax if ($supportFlags & 0x20);
- push @event, "3:remoteTeach:" . ($supportFlags & 0x40 ? 'supported' : 'not_supported');
- Log3 $name, 2, "EnOcean $name RPC teach-in supported link tables response received Data: $data";
- # request outbound table
- #$hash->{IODev}{helper}{remoteAnswerWait}{0x821}{hash} = $hash;
- #$hash->{IODev}{helper}{remoteLinkTableStartRef} = 0;
- #my %functionHash = (hash => $hash, param => 0x821);
- #RemoveInternalTimer(\%functionHash);
- #InternalTimer(gettimeofday() + 2.5, 'EnOcean_cdmClearRemoteWait', \%functionHash, 0);
- #$sendData = '022107FF';
- #EnOcean_SndRadio(undef, $hash, $packetType, $rorg, $sendData, '0' x 8, '0F', $senderID);
- #Log3 $name, 2, "EnOcean $name RPC request teach-in outbound table";
- } elsif ($funcNumber == 0x811 && $remoteManagement eq 'manager') {
- # link table response
- CommandDeleteReading(undef, "$name remoteLinkTableDesc.*");
- $data =~ m/^(..)(.*)$/;
- my $direction = hex($1) & 0x80 ? 'Out' : 'In';
- $data = $2;
- while (length($data) > 0) {
- $data =~ m/^(..)(........)(..)(..)(..)(..)(.*)$/;
- push @event, "3:remoteLinkTableDesc" . $direction . "$1:S2:S3-S4-$5:$6";
- $data = $7;
- }
- $remoteLastStatusReturnCode = '00';
- delete $iohash->{helper}{remoteAnswerWait}{$funcNumber}{hash};
- push @event, "3:remoteLastStatusReturnCode:$remoteLastStatusReturnCode";
- Log3 $name, 2, "EnOcean $name RPC link table response received";
- } elsif ($funcNumber == 0x813 && $remoteManagement eq 'manager') {
- # link table GP response
- CommandDeleteReading(undef, "$name remoteLinkTableGPDesc.*");
- $data =~ m/^(..)(.*)$/;
- my $direction = hex($1) & 0x80 ? 'Out' : 'In';
- $data = $2;
- my $channel = 0;
- my $channelDir = hex($1) & 0x80 ? 'O' : 'I';
- my $channelDef;
- my $channelName;
- my $channelType;
- my $dataOutboundDefLen = 0;
- my $gpData;
- my @gpDef;
- my $gpIdx;
- my $signalType;
- while (length($data) > 0) {
- $data =~ m/^(..)(.{12})(.*)$/;
- $gpIdx = $1;
- $gpData = EnOcean_convHexToBit($2);
- $data = $3;
- $gpData =~ m/^(..)(.{8})(.*)$/;
- $channelType = unpack('C', pack('B8', '000000' . $1));
- $signalType = unpack('C', pack('B8', $2));
- $data = $3;
- #Log3 $name, 2, "EnOcean $name parse RPC link table GP idx: $gpIdx channelType: $channelType signalType: $signalType data: $data";
- if ($channelType == 0) {
- # teach-in information
- if ($signalType == 1) {
- # outbound channel description
- } elsif ($signalType == 2) {
- # produkt ID
- }
- } elsif ($channelType == 1) {
- # data
- $gpData =~ m/^(..)(....)(.{8})(....)(.{8})(....)(.*)$/;
- $channelDef = $channelDir . ':' . $channelType . ':' . $signalType . ':' .
- unpack('C', pack('B8', '000000' . $1)) . ':' . unpack('C', pack('B8', '0000' . $2)) . ':' .
- unpack('c', pack('B8', $3)) . ':' . unpack('C', pack('B8', '0000' . $4)) . ':' .
- unpack('c', pack('B8', $5)) . ':' . unpack('C', pack('B8', '0000' . $6));
- if (defined $EnO_gpValueData{$signalType}{name}) {
- $channelName = $EnO_gpValueData{$signalType}{name};
- } else {
- $channelName = "none";
- }
- $gpDef[$channel] = $channelName . ':' . $channelDef;
- } elsif ($channelType == 2) {
- # flag
- $gpData =~ m/^(..)(.*)$/;
- $channelDef = $channelDir . ':' .$channelType . ':' . $signalType . ':' .
- unpack('C', pack('B8', '000000' . $1));
- if (defined $EnO_gpValueFlag{$signalType}{name}) {
- $channelName = $EnO_gpValueFlag{$signalType}{name};
- } else {
- $channelName = "none";
- }
- $gpDef[$channel] = $channelName . ':' . $channelDef;
- } elsif ($channelType == 3) {
- # enumeration
- $gpData =~ m/^(..)(....)(.*)$/;
- $channelDef = $channelDir . ':' .$channelType . ':' . $signalType . ':' .
- unpack('C', pack('B8', '000000' . $1)) . ':' . unpack('C', pack('B8', '0000' . $2));
- if (defined $EnO_gpValueEnum{$signalType}{name}) {
- $channelName = $EnO_gpValueEnum{$signalType}{name};
- } else {
- $channelName = "none";
- }
- $gpDef[$channel] = $channelName . ':' . $channelDef;
- }
- push @event, "3:remoteLinkTableGPDesc" . $direction . "$gpIdx:$gpDef[0]";
- }
- $remoteLastStatusReturnCode = '00';
- delete $iohash->{helper}{remoteAnswerWait}{$funcNumber}{hash};
- push @event, "3:remoteLastStatusReturnCode:$remoteLastStatusReturnCode";
- Log3 $name, 2, "EnOcean $name RPC link table GP response received";
- } elsif ($funcNumber == 0x827 && $remoteManagement eq 'manager') {
- # product ID answer
- $manufID = substr($data, 1, 3);
- $attr{$name}{remoteManufID} = $manufID;
- $manufID = $EnO_manuf{$manufID} if($EnO_manuf{$manufID});
- $remoteLastStatusReturnCode = '00';
- delete $iohash->{helper}{remoteAnswerWait}{$funcNumber}{hash};
- $sendData = '024007FF';
- EnOcean_SndRadio(undef, $hash, $packetType, $rorg, $sendData, '0' x 8, '0F', $senderID);
- push @event, "3:remoteProductID:" . substr($data, 4, 8);
- push @event, "3:remoteLastStatusReturnCode:$remoteLastStatusReturnCode";
- Log3 $name, 2, "EnOcean $name RPC Product ID answer received ProductID: " . substr($data, 4, 8) . " Manufacturer: $manufID";
- Log3 $name, 2, "EnOcean $name RPC acknowledge sent";
- EnOcean_CommandSave(undef, undef);
- } elsif ($funcNumber == 0x830 && $remoteManagement eq 'manager') {
- # device config response
- CommandDeleteReading(undef, "$name remoteDevCfg.*");
- my $idx;
- my $valueLen;
- while (length($data) > 0) {
- $data =~ m/^(....)(..)(.*)$/;
- $idx = $1;
- $valueLen = hex($2) * 2;
- $data = $3;
- $data =~ m/^(.{$valueLen})(.*)$/;
- push @event, "3:remoteDevCfg$idx:S1";
- $data = $2;
- }
- $remoteLastStatusReturnCode = '00';
- delete $iohash->{helper}{remoteAnswerWait}{$funcNumber}{hash};
- push @event, "3:remoteLastStatusReturnCode:$remoteLastStatusReturnCode";
- Log3 $name, 2, "EnOcean $name RPC device configuration response received";
- } elsif ($funcNumber == 0x832 && $remoteManagement eq 'manager') {
- # link based configuration response
- $data =~ m/^(..)(..)(.*)$/;
- my $direction = hex($1) & 0x80 ? 'Out' : 'In';
- my $linkTableIdx = $2;
- CommandDeleteReading(undef, "$name remoteLinkCfg$direction$linkTableIdx.*");
- $data = $3;
- my $idx;
- my $valueLen;
- while (length($data) > 0) {
- $data =~ m/^(....)(..)(.*)$/;
- $idx = $1;
- $valueLen = hex($2) * 2;
- $data = $3;
- $data =~ m/^(.{$valueLen})(.*)$/;
- push @event, "3:remoteLinkCfg$direction$linkTableIdx:$idx:S1";
- $data = $2;
- }
- $remoteLastStatusReturnCode = '00';
- delete $iohash->{helper}{remoteAnswerWait}{$funcNumber}{hash};
- push @event, "3:remoteLastStatusReturnCode:$remoteLastStatusReturnCode";
- Log3 $name, 2, "EnOcean $name RPC link table response received";
- } elsif ($funcNumber == 0x850 && $remoteManagement eq 'manager') {
- # query status answer
- delete $iohash->{helper}{remoteAnswerWait}{$funcNumber}{hash};
- my $repeaterFunction = 'off';
- if (($db[0] & 0xC0) == 0) {
- $repeaterFunction = 'off';
- } elsif (($db[0] & 0xC0) == 0x40) {
- $repeaterFunction = 'on';
- } elsif (($db[0] & 0xC0) == 0x80) {
- $repeaterFunction = 'filter';
- }
- push @event, "3:remoteRepeaterFunction:$repeaterFunction";
- push @event, "3:remoteRepeaterLevel:" . (($db[0] & 0x30) == 0x20 ? 2 : 1);
- push @event, "3:remoteRepeaterFilter:" . ($db[0] & 8 ? 'OR' : 'AND');
- Log3 $name, 2, "EnOcean $name RPC repeater functions response received";
- } else {
- Log3 $name, 2, "EnOcean $name RMCC/RPC function number " . sprintf("%03X", $funcNumber) . " not supported.";
- }
- } elsif ($rorg eq "C5" && $packetType == 1) {
- # remote management >> packetType = 7
- return $name;
- }
- readingsBeginUpdate($hash);
- for(my $i = 0; $i < int(@event); $i++) {
- # Flag & 1: reading, Flag & 2: changed. Currently ignored.
- my ($flag, $vn, $vv) = split(':', $event[$i], 3);
- readingsBulkUpdate($hash, $vn, $vv);
- my @cmdObserve = ($name, $vn, $vv);
- EnOcean_observeParse(2, $hash, @cmdObserve);
- }
- readingsEndUpdate($hash, 1);
- if (defined $deleteDevice) {
- # delete device and save config
- CommandDelete(undef, $deleteDevice);
- Log3 $name, 2, "EnOcean $name device $deleteDevice deleted";
- if (defined $oldDevice) {
- Log3 $name, 2, "EnOcean $name renamed $oldDevice to $deleteDevice";
- CommandRename(undef, "$oldDevice $deleteDevice");
- EnOcean_CommandSave(undef, undef);
- return $deleteDevice;
- } else {
- EnOcean_CommandSave(undef, undef);
- return '';
- }
- }
- return $name;
- }
- sub EnOcean_Attr(@)
- {
- my ($cmd, $name, $attrName, $attrVal) = @_;
- my $hash = $defs{$name};
- # return if attribute list is incomplete
- return undef if (!$init_done);
- my $err;
- my $loglevel = 2;
- my $waitingCmds = AttrVal($name, "waitingCmds", 0);
- if ($attrName eq "angleTime") {
- my $channel;
- my $data;
- if (!defined $attrVal) {
- if (AttrVal($name, "subType", "") =~ m/^blindsCtrl\.0[01]$/) {
- # no rotation
- $data = "7FFF0007F5";
- EnOcean_SndRadio(undef, $hash, 1, "D2", $data, AttrVal($name, "subDef", "00000000"), "00", $hash->{DEF});
- }
- } elsif (AttrVal($name, "subType", "") =~ m/^blindsCtrl\.0[01]$/) {
- my @attrVal = split(':', $attrVal);
- for (my $channel = 0; $channel <= $#attrVal && $channel < 4; $channel ++) {
- if ($attrVal[$channel] =~ m/^\d+(\.\d+)?$/ && $attrVal[$channel] >= 0 && $attrVal[$channel] <= 2.54) {
- if ($attrVal[$channel] < 0.01) {
- $attrVal[$channel] = 0;
- } else {
- $attrVal[$channel] = int($attrVal[$channel] * 100);
- }
- $data = sprintf "7FFF%02X07%02X", $attrVal[$channel], $channel << 4 | 5;
- EnOcean_SndRadio(0.2 + $channel * 0.5, $hash, 1, "D2", $data, AttrVal($name, "subDef", "00000000"), "00", $hash->{DEF});
- } else {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- }
- } elsif (AttrVal($name, "subType", "") eq "manufProfile" && AttrVal($name, "manufID", "") eq "00D" &&
- $attrVal =~ m/^[+-]?\d+?$/ && $attrVal >= 1 && $attrVal <= 6) {
- } else {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- #Log3 $name, 2, "EnOcean $name attribute-value [$attrName] = $attrVal wrong";
- #CommandDeleteAttr(undef, "$name $attrName");
- }
- } elsif ($attrName eq "alarmAction") {
- my $data;
- if (!defined $attrVal) {
- if (AttrVal($name, "subType", "") =~ m/^blindsCtrl\.0[01]$/) {
- # no alarm action
- $data = "7FFFFF00F5";
- EnOcean_SndRadio(undef, $hash, 1, "D2", $data, AttrVal($name, "subDef", "00000000"), "00", $hash->{DEF});
- }
- } elsif (AttrVal($name, "subType", "") =~ m/^blindsCtrl\.0[01]$/) {
- my @attrVal = split(':', $attrVal);
- for (my $channel = 0; $channel <= $#attrVal && $channel < 4; $channel ++) {
- if ($attrVal =~ m/no|stop|opens|closes$/) {
- my $alarmAction = 0;
- if ($attrVal[$channel] eq "no") {
- $alarmAction = 0;
- } elsif ($attrVal[$channel] eq "stop") {
- $alarmAction = 1;
- } elsif ($attrVal[$channel] eq "opens") {
- $alarmAction = 2;
- } elsif ($attrVal[$channel] eq "closes") {
- $alarmAction = 3;
- } else {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- $data = sprintf "7FFFFF%02X%02X", $alarmAction, $channel << 4 | 5;
- EnOcean_SndRadio(0.2 + $channel * 0.5, $hash, 1, "D2", $data, AttrVal($name, "subDef", "00000000"), "00", $hash->{DEF});
- } else {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- }
- } else {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName =~ m/^block.*/) {
- if (!defined $attrVal){
- } elsif ($attrVal =~ m/^(no|yes)$/) {
- if (AttrVal($name, "subType", "") eq "roomCtrlPanel.00") {
- $waitingCmds |= 64;
- readingsSingleUpdate($hash, "waitingCmds", $waitingCmds, 0);
- }
- } else {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "comMode") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^biDir|uniDir|confirm$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "creator") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^autocreate|manual$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "dataEnc") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^VAES|AES-CBC$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "daylightSavingTime") {
- if (!defined $attrVal){
- } elsif ($attrVal =~ m/^(supported|not_supported)$/) {
- if (AttrVal($name, "subType", "") eq "roomCtrlPanel.00") {
- $waitingCmds |= 64;
- readingsSingleUpdate($hash, "waitingCmds", $waitingCmds, 0);
- }
- } else {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "defaultChannel") {
- my $defaultChannel = '';
- my $subType = AttrVal($name, "subType", "");
- if ($subType eq "actuator.01") {
- $defaultChannel = join("|", @EnO_defaultChannel);
- } elsif ($subType =~ m/^blindsCtrl\.0[01]$/) {
- $defaultChannel = '[1234]|all';
- }
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^($defaultChannel)$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "demandRespAction") {
- if (!defined $attrVal){
- } else {
- my %specials = ("%TARGETNAME" => $name,
- "%NAME" => $name,
- "%TARGETTYPE" => '',
- "%TYPE" => '',
- "%LEVEL" => '',
- "%SETPOINT" => '',
- "%POWERUSAGE" => '',
- "%POWERUSAGESCALE" => '',
- "%POWERUSAGELEVEL" => '',
- "%TARGETSTATE" => '',
- "%STATE" => ''
- );
- $err = perlSyntaxCheck($attrVal, %specials);
- }
- } elsif ($attrName eq "demandRespRandomTime") {
- if (!defined $attrVal) {
- } elsif ($attrVal !~ m/^\d+?$/ || $attrVal < 1) {
- $err = "attribute-value [$attrName] = $attrVal is not a integer number or not valid";
- }
- } elsif ($attrName =~ m/^(demandRespMax|demandRespMin)$/) {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^(A0|AI|B0|BI|C0|CI|D0|DI)$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- CommandDeleteAttr(undef, "$name $attrName");
- }
- } elsif ($attrName eq "demandRespThreshold") {
- if (!defined $attrVal) {
- } elsif ($attrVal !~ m/^\d+?$/ || $attrVal < 0 || $attrVal > 15) {
- $err = "attribute-value [$attrName] = $attrVal is not a integer number or not valid";
- }
- } elsif ($attrName eq "devChannel") {
- if (!defined $attrVal){
- } elsif ($attrVal =~ m/^[\dA-Fa-f]{2}$/) {
- # actions see EnOcean_Notify, global ATTR
- } elsif ($attrVal =~ m/^\d+$/ && $attrVal >= 0 && $attrVal <= 255) {
- } else {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "devMode") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^master|slave$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "devUpdate") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^(off|auto|demand|polling|interrupt)$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName =~ m/^dimMax|dimMin$/) {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^off|[\d+]$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "displayContent") {
- if (!defined $attrVal){
- } elsif ($attrVal =~ m/^(humidity|off|setpointTemp|tempertureExtern|temperatureIntern|time|default|no_change)$/) {
- if (AttrVal($name, "subType", "") eq "roomCtrlPanel.00") {
- $waitingCmds |= 64;
- readingsSingleUpdate($hash, "waitingCmds", $waitingCmds, 0);
- }
- } else {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "displayOrientation") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^0|90|180|270$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "eep" || $attrName eq "remoteEEP") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^[\dA-Fa-f]{2}-[\dA-Fa-f]{2}-[\dA-Fa-f]{2}$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "model") {
- if (!defined $attrVal){
- } else {
- # set model specific attributes
- foreach my $attrCntr (keys %{$EnO_models{$attrVal}{attr}}) {
- if ($attrCntr eq "remoteID") {
- if (exists $hash->{DEF}) {
- $attr{$name}{$attrCntr} = $hash->{DEF};
- } else {
- $attr{$name}{$attrCntr} = EnOcean_CheckSenderID($EnO_models{$attrVal}{attr}{$attrCntr}, $hash->{IODev}{NAME}, "00000000");
- }
- } else {
- $attr{$name}{$attrCntr} = $EnO_models{$attrVal}{attr}{$attrCntr};
- }
- }
- if (exists $EnO_models{$attrVal}{xml}) {
- # read xml device description to $hash->{helper}
- if ($xmlFunc == 1) {
- my $xmlFile = $attr{global}{modpath} . $EnO_models{$attrVal}{xml}{xmlDescrLocation};
- if (-e -f -r $xmlFile) {
- $hash->{helper} = $xml->XMLin($xmlFile);
- if (exists $hash->{helper}{Device}) {
- } else {
- Log3 $name, 2, "EnOcean $name <attr> Device Description not defined";
- }
- } else {
- Log3 $name, 2, "EnOcean $name <attr> Device Description file $xmlFile not exists";
- }
- } else {
- Log3 $name, 2, "EnOcean $name <attr> XML functions are not available";
- }
- }
- }
- } elsif ($attrName eq "gpDef") {
- if (!defined $attrVal){
- } else {
- my @gpDef = split("[ \t][ \t]*", $attrVal);
- my ($channelName, $channelDir, $channelType, $signalType, $valueType, $resolution, $engMin, $scalingMin, $engMax, $scalingMax);
- for (my $channel = 0; $channel < @gpDef; $channel ++) {
- my @err;
- ($channelName, $channelDir, $channelType, $signalType, $valueType, $resolution, $engMin, $scalingMin, $engMax, $scalingMax) =
- split(':', $gpDef[$channel]);
- push(@err, "channelName") if (!defined $channelName);
- push(@err, "channelDir") if (!defined($channelDir) || $channelDir !~ m/^O|I$/);
- push(@err, "channelType") if (!defined($channelType) || $channelType !~ m/^\d+$/ || $channelType > 3);
- push(@err, "signalType") if (!defined($signalType) || $signalType !~ m/^\d+$/ || $signalType > 255);
- push(@err, "valueType") if (!defined($valueType) || $valueType !~ m/^\d+$/ || $valueType > 3);
- if ($channelType == 1 || $channelType == 3) {
- push(@err, "resolution") if (!defined($resolution) || $resolution !~ m/^\d+$/ || $resolution > 12);
- }
- if ($channelType == 1) {
- push(@err, "engMin") if (!defined($engMin) || $engMin !~ m/^[+-]?\d+$/ || $engMin < -128 || $engMin > 127);
- push(@err, "scalingMin") if (!defined($scalingMin) || $scalingMin !~ m/^\d+$/ || $scalingMin < 1 || $scalingMin > 13);
- push(@err, "engMax") if (!defined($engMax) || $engMax !~ m/^[+-]?\d+$/ || $engMax < -128 || $engMax > 127);
- push(@err, "scalingMax") if (!defined($scalingMax) || $scalingMax !~ m/^\d+$/ || $scalingMax < 1 || $scalingMax > 13);
- }
- $err = "attribute-value $attrName/channel " . sprintf('%02d', $channel) . ": " .
- join(', ', @err) . " wrong" if (defined $err[0]);
- }
- }
- } elsif ($attrName eq "humidity") {
- if (!defined $attrVal) {
- } elsif ($attrVal =~ m/^\d+$/ && $attrVal >= 0 && $attrVal <= 100) {
- } else {
- #RemoveInternalTimer($hash);
- $err = "attribute-value [$attrName] = $attrVal is not a integer number or not valid";
- }
- } elsif ($attrName =~ m/^key/) {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^[\dA-Fa-f]{32}$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "macAlgo") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^no|[3-4]$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "manufID" || $attrName eq "remoteManufID") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^[0-7][\dA-Fa-f]{2}$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "measurementCtrl") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^disable|enable$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "observe") {
- if (!defined $attrVal){
- } elsif (lc($attrVal) !~ m/^(off|on)$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "observeCmdRepetition") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^[1-5]$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "observeErrorAction") {
- if (!defined $attrVal){
- } else {
- my %specials = ("%NAME" => $name,
- "%FAILEDDEV" => '',
- "%TYPE" => '',
- "%EVENT" => ''
- );
- $err = perlSyntaxCheck($attrVal, %specials);
- }
- } elsif ($attrName eq "observeInterval") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^\d+?$/ || $attrVal < 1) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "observeLogic") {
- if (!defined $attrVal){
- } elsif (lc($attrVal) !~ m/^and|or$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "pidActorErrorAction") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^errorPos|freeze$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "pidCtrl") {
- if (!defined $attrVal){
- } elsif (lc($attrVal) eq "on") {
- EnOcean_setPID(undef, $hash, 'start', ReadingsVal($name, "setpoint", ''));
- } elsif (lc($attrVal) eq "off") {
- EnOcean_setPID(undef, $hash, 'stop', '');
- } else {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "pidActorErrorPos") {
- if (!defined $attrVal) {
- } elsif ($attrVal !~ m/^\d+?$/ || $attrVal < 0 || $attrVal > 100) {
- $err = "attribute-value [$attrName] = $attrVal is not a integer number or not valid";
- }
- } elsif ($attrName eq "pidDeltaTreshold") {
- my $reFloatpos = '^([\\+]?\\d+\\.?\d*$)';
- if (!defined $attrVal) {
- } elsif ($attrVal !~ m/$reFloatpos/) {
- $err = "attribute-value [$attrName] = $attrVal is not a integer number or not valid";
- }
- } elsif ($attrName eq "pidSensorTimeout") {
- if (!defined $attrVal) {
- } elsif ($attrVal !~ m/^\d+?$/) {
- $err = "attribute-value [$attrName] = $attrVal is not a integer number or not valid";
- }
- } elsif ($attrName =~ m/^pidActorLimitLower|pidActorLimitUpper$/) {
- if (!defined $attrVal) {
- } elsif ($attrVal !~ m/^\d+?$/ || $attrVal < 0 || $attrVal > 100) {
- $err = "attribute-value [$attrName] = $attrVal is not a integer number or not valid";
- }
- } elsif ($attrName =~ m/^pidFactor_.$/) {
- my $reFloatpos = '^([\\+]?\\d+\\.?\d*$)';
- if (!defined $attrVal) {
- } elsif ($attrVal !~ m/$reFloatpos/) {
- $err = "attribute-value [$attrName] = $attrVal is not a integer number or not valid";
- }
- } elsif ($attrName eq "pollInterval") {
- if (!defined $attrVal) {
- } elsif ($attrVal =~ m/^\d+?$/) {
- if (AttrVal($name, "subType", "") eq "roomCtrlPanel.00") {
- $waitingCmds |= 64;
- readingsSingleUpdate($hash, "waitingCmds", $waitingCmds, 0);
- }
- } else {
- #RemoveInternalTimer($hash);
- $err = "attribute-value [$attrName] = $attrVal is not a integer number or not valid";
- }
- } elsif ($attrName eq "productID") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^[\dA-Fa-f]{8}$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "rampTime") {
- if (!defined $attrVal){
- } elsif ($attrVal =~ m/^\d+?$/) {
- if (AttrVal($name, "subType", "") eq "gateway") {
- if ($attrVal < 0 || $attrVal > 255) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif (AttrVal($name, "subType", "") eq "lightCtrl.01") {
- if ($attrVal < 0 || $attrVal > 65535) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } else {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } else {
- $err = "attribute $attrName not supported for subType " . AttrVal($name, "subType", "");
- }
- } elsif ($attrName eq "releasedChannel") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^A|B|C|D|I|0|auto$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "rcvRespAction") {
- if (!defined $attrVal){
- } elsif (AttrVal($name, "subType", "") eq "hvac.01") {
- my %specials = ("%ACTUATORSTATE" => '',
- "%BATTERY" => '',
- "%COVER" => '',
- "%ENERGYINPUT" => '',
- "%ENERGYSTORAGE" => '',
- "%MAINTENANCEMODE" => '',
- "%NAME" => $name,
- "%OPERATIONMODE" => '',
- "%ROOMTEMP" => '',
- "%SELFCTRL" => '',
- "%SETPOINT" => '',
- "%SETPOINTTEMP" => '',
- "%SUMMERMODE" => '',
- "%TEMPERATURE" => '',
- "%WINDOW" => ''
- );
- $err = perlSyntaxCheck($attrVal, %specials);
- } elsif (AttrVal($name, "subType", "") eq "hvac.04") {
- my %specials = ("%BATTERY" => '',
- "%FEEDTEMP" => '',
- "%MAINTENANCEMODE" => '',
- "%NAME" => $name,
- "%OPERATIONMODE" => '',
- "%ROOMTEMP" => '',
- "%SETPOINT" => '',
- "%SETPOINTTEMP" => '',
- "%SUMMERMODE" => '',
- "%TEMPERATURE" => ''
- );
- $err = perlSyntaxCheck($attrVal, %specials);
- }
- } elsif ($attrName eq "remoteID") {
- if (!defined $attrVal){
- # delete old pointer
- delete $modules{EnOcean}{defptr}{$attr{$name}{$attrName}} if (exists($attr{$name}{$attrName}) && $attr{$name}{$attrName} ne $hash->{DEF});
- } elsif ($attrVal =~ m/^[\dA-F]{8}$/) {
- # delete old pointer
- delete $modules{EnOcean}{defptr}{$attr{$name}{$attrName}} if (exists($attr{$name}{$attrName}) && $attr{$name}{$attrName} ne $hash->{DEF});
- $modules{EnOcean}{defptr}{$attrVal} = $hash;
- } else {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "remoteManagement") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^client|manager|off$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "reposition") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^directly|opens|closes$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName =~ m/^rlcRcv|rlcSnd$/) {
- if (!defined $attrVal){
- } elsif (AttrVal($name, "rlcAlgo", "") eq "2++" && $attrVal =~ m/^[\dA-Fa-f]{4}$/) {
- } elsif (AttrVal($name, "rlcAlgo", "") eq "3++" && $attrVal =~ m/^[\dA-Fa-f]{6}$/) {
- } else {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "rlcAlgo") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^no|2++|3++$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "rlcTX") {
- if (!defined $attrVal){
- } elsif (lc($attrVal) !~ m/^true|false$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "rltType") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^1BS|4BS$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "rltRepeat") {
- if (!defined $attrVal){
- } elsif ($attrVal =~ m/^\d+?$/ && $attrVal >= 16 && $attrVal <= 256) {
- } else {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "remoteCode") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^[\dA-Fa-f]{8}$/ || $attrVal eq "00000000" || uc($attrVal) eq "FFFFFFFF") {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "secLevel") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^encapsulation|encryption|off$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "secMode") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^rcv|snd|bidir$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "sendDevStatus") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^no|yes$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "serviceOn") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^(no|yes)$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "setCmdTrigger") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^man|refDev$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "setpointSummerMode") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^\d+$/ && $attrVal >= 0 && $attrVal <= 100) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "settingAccuracy") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^high|low$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "shutTime") {
- my $data;
- if (!defined $attrVal) {
- if (AttrVal($name, "subType", "") =~ m/^blindsCtrl\.0[01]$/) {
- # set shutTime to max
- $data = "7530FF07F5";
- EnOcean_SndRadio(undef, $hash, 1, "D2", $data, AttrVal($name, "subDef", "00000000"), "00", $hash->{DEF});
- }
- } elsif (AttrVal($name, "subType", "") =~ m/^blindsCtrl\.0[01]$/) {
- my @attrVal = split(':', $attrVal);
- for (my $channel = 0; $channel <= $#attrVal && $channel < 4; $channel ++) {
- if ($attrVal[$channel] =~ m/^\d+$/ && $attrVal[$channel] >= 5 && $attrVal[$channel] <= 300) {
- $attrVal[$channel] = int($attrVal[$channel] * 100);
- $data = sprintf "%04XFF07%02X", $attrVal[$channel], $channel << 4 | 5;
- EnOcean_SndRadio(0.2 + $channel * 0.5, $hash, 1, "D2", $data, AttrVal($name, "subDef", "00000000"), "00", $hash->{DEF});
- } else {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- }
- } elsif (AttrVal($name, "subType", "") eq "manufProfile" && AttrVal($name, "manufID", "") eq "00D" &&
- $attrVal =~ m/^[+-]?\d+$/ && $attrVal >= 1 && $attrVal <= 255) {
- } else {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "summerMode") {
- if (!defined $attrVal){
- } elsif ($attrVal eq 'on') {
- if (AttrVal($name, 'subType', '') =~ m/^hvac\.0(1|4)$/ && AttrVal($name, 'summerMode', 'off') eq 'off') {
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, 'waitingCmds', 'summerMode');
- readingsBulkUpdate($hash, 'operationModeRestore', ReadingsVal($name, 'operationMode', 'setpoint'));
- readingsBulkUpdate($hash, 'setpointTempRestore', ReadingsVal($name, 'setpointTemp', 20));
- readingsBulkUpdate($hash, 'operationMode', 'setpoint');
- readingsEndUpdate($hash, 0);
- } else {
- # attr not changed
- }
- } elsif ($attrVal eq 'off') {
- if (AttrVal($name, 'subType', '') =~ m/^hvac\.0(1|4)$/ && AttrVal($name, 'summerMode', 'off') eq 'on') {
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, 'waitingCmds', 'runInit');
- readingsBulkUpdate($hash, 'operationMode', ReadingsVal($name, 'operationModeRestore', 'setpoint'));
- readingsBulkUpdate($hash, 'setpointTemp', ReadingsVal($name, 'setpointTempRestore', 20));
- readingsEndUpdate($hash, 0);
- CommandDeleteReading(undef, "$name .*Restore");
- } else {
- # attr not changed
- }
- } else {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName =~ m/^subDef.?|postmasterID/) {
- if (!defined $attrVal){
- } elsif ($attrVal eq "getNextID") {
- # actions see EnOcean_Notify, global ATTR
- } elsif ($attrVal !~ m/^[\dA-Fa-f]{8}$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "switchHysteresis") {
- if (!defined $attrVal) {
- } elsif ($attrVal =~ m/^\d+(\.\d+)?$/ && $attrVal >= 0.1) {
- } else {
- #RemoveInternalTimer($hash);
- $err = "attribute-value [$attrName] = $attrVal is not a valid number";
- }
- } elsif ($attrName eq "teachMethod") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^1BS|4BS|confirm|GP|RPS|smartAck|STE|UTE$$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "temperatureScale") {
- if (!defined $attrVal){
- } elsif ($attrVal =~ m/^(C|F|default|no_change)$/) {
- if (AttrVal($name, "subType", "") eq "roomCtrlPanel.00") {
- $waitingCmds |= 64;
- readingsSingleUpdate($hash, "waitingCmds", $waitingCmds, 0);
- }
- } else {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "timeNotation") {
- if (!defined $attrVal){
- } elsif ($attrVal =~ m/^(12|24|default|no_change)$/) {
- if (AttrVal($name, "subType", "") eq "roomCtrlPanel.00") {
- $waitingCmds |= 64;
- readingsSingleUpdate($hash, "waitingCmds", $waitingCmds, 0);
- }
- } else {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName =~ m/^timeProgram[1-4]$/) {
- if (!defined $attrVal){
- } elsif ($attrVal =~ m/^(FrMo|FrSu|ThFr|WeFr|TuTh|MoWe|SaSu|MoFr|MoSu|Su|Sa|Fr|Th|We|Tu|Mo)\s+(\d*?):(00|15|30|45)\s+(\d*?):(00|15|30|45)\s+(comfort|economy|preComfort|buildingProtection)$/) {
- if (AttrVal($name, "subType", "") eq "roomCtrlPanel.00") {
- # delete remote and send new time program
- delete $hash->{helper}{4}{telegramWait};
- $hash->{helper}{4}{telegramWait}{substr($attrName,-1,1) + 0} = 1;
- for (my $messagePartCntr = 1; $messagePartCntr <= 4; $messagePartCntr ++) {
- if (defined AttrVal($name, "timeProgram" . $messagePartCntr, undef)) {
- $hash->{helper}{4}{telegramWait}{$messagePartCntr} = 1;
- }
- }
- $waitingCmds |= 528;
- readingsSingleUpdate($hash, "waitingCmds", $waitingCmds, 0);
- }
- } else {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "trackerWakeUpCycle") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^10|20|30|40|60|120|180|240|3600|86400$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "updateState") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^default|yes|no$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "uteResponseRequest") {
- if (!defined $attrVal){
- } elsif ($attrVal !~ m/^(yes|no)$/) {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- } elsif ($attrName eq "wakeUpCycle") {
- my $wakeUpCycle = join("|", keys %wakeUpCycle);
- if (!defined $attrVal){
- } elsif ($attrVal =~ m/^$wakeUpCycle$/) {
- if ($attrVal >= 1500 && AttrVal($name, 'wakeUpCycle', 300) < 1500) {
- # switch to summer mode
- $attr{$name}{summerMode} = 'on';
- readingsSingleUpdate($hash, 'waitingCmds', 'summerMode', 0);
- } elsif ($attrVal < 1500 && AttrVal($name, 'wakeUpCycle', 300) >= 1500) {
- # runInit necessary before switch to operation mode
- $attr{$name}{summerMode} = 'off';
- readingsSingleUpdate($hash, 'waitingCmds', 'runInit', 0);
- }
- } else {
- $err = "attribute-value [$attrName] = $attrVal wrong";
- }
- }
- return $err;
- }
- sub EnOcean_Notify(@)
- {
- my ($hash, $dev) = @_;
- my $name = $hash->{NAME};
- my $devName = $dev->{NAME};
- return undef if (AttrVal($name ,"disable", 0) > 0);
- my $max = int(@{$dev->{CHANGED}});
- for (my $i = 0; $i < $max; $i++) {
- my $s = $dev->{CHANGED}[$i];
- $s = "" if(!defined($s));
- if ($devName eq $name) {
- my @parts = split(/: | /, $s);
- #####
- if (exists($hash->{helper}{stopped}) && !$hash->{helper}{stopped} && $parts[0] eq "temperature") {
- # PID regulator: calc gradient for delta as base for d-portion calculation
- my $setpointTemp = ReadingsVal($name, "setpointTemp", undef);
- my $temperature = $parts[1];
- # ---- build difference current - old value
- # calc difference of delta/deltaOld
- my $delta = $setpointTemp - $temperature if (defined($setpointTemp));
- my $deltaOld = ($hash->{helper}{deltaOld} + 0) if (defined($hash->{helper}{deltaOld}));
- my $deltaDiff = ($delta - $deltaOld) if (defined($delta) && defined($deltaOld));
- # ----- build difference of timestamps
- my $deltaOldTsStr = $hash->{helper}{deltaOldTS};
- my $deltaOldTsNum = time_str2num($deltaOldTsStr) if (defined($deltaOldTsStr));
- my $nowTsNum = gettimeofday();
- my $tsDiff = ($nowTsNum - $deltaOldTsNum)
- if (defined($deltaOldTsNum) && (($nowTsNum - $deltaOldTsNum) > 0));
- # ----- calculate gradient of delta
- my $deltaGradient = $deltaDiff / $tsDiff
- if (defined($deltaDiff) && defined($tsDiff) && ($tsDiff > 0 ));
- $deltaGradient = 0 if ( !defined($deltaGradient) );
- # ----- store results
- $hash->{helper}{deltaGradient} = $deltaGradient;
- $hash->{helper}{deltaOld} = $delta;
- $hash->{helper}{deltaOldTS} = TimeNow();
- Log3 $name, 5, "EnOcean $name <notify> $devName $s";
- }
- } elsif ($devName eq "global" && $s =~ m/^RENAMED ([^ ]*) ([^ ]*)$/) {
- if (defined AttrVal($name, "temperatureRefDev", undef)) {
- if (AttrVal($name, "temperatureRefDev", undef) eq $1) {
- CommandAttr(undef, "$name temperatureRefDev $2");
- }
- } elsif (defined AttrVal($name, "setpointRefDev", undef)) {
- if (AttrVal($name, "setpointRefDev", undef) eq $1) {
- CommandAttr(undef, "$name setpointRefDev $2");
- }
- } elsif (defined AttrVal($name, "setpointTempRefDev", undef)) {
- if (AttrVal($name, "setpointTempRefDev", undef) eq $1) {
- CommandAttr(undef, "$name setpointTempRefDev $2");
- }
- } elsif (defined AttrVal($name, "humidityRefDev", undef)) {
- if (AttrVal($name, "humidityRefDev", undef) eq $1) {
- CommandAttr(undef, "$name humidityRefDev $2");
- }
- } elsif (defined AttrVal($name, "observeRefDev", undef)) {
- if (AttrVal($name, "observeRefDev", undef) eq $1) {
- CommandAttr(undef, "$name observeRefDev $2");
- }
- } elsif (defined AttrVal($name, "demandRespRefDev", undef)) {
- if (AttrVal($name, "demandRespRefDev", undef) eq $1) {
- CommandAttr(undef, "$name demandRespRefDev $2");
- }
- }
- #Log3($name, 5, "EnOcean $name <notify> RENAMED old: $1 new: $2");
- } elsif ($devName eq "global" && $s =~ m/^DELETED ([^ ]*)$/) {
- # delete attribute *RefDev
- if (defined AttrVal($name, "temperatureRefDev", undef)) {
- if (AttrVal($name, "temperatureRefDev", undef) eq $1) {
- CommandDeleteAttr(undef, "$name temperatureRefDev");
- }
- } elsif (defined AttrVal($name, "setpointRefDev", undef)) {
- if (AttrVal($name, "setpointRefDev", undef) eq $1) {
- CommandDeleteAttr(undef, "$name setpointRefDev");
- }
- } elsif (defined AttrVal($name, "setpointTempRefDev", undef)) {
- if (AttrVal($name, "setpointTempRefDev", undef) eq $1) {
- CommandDeleteAttr(undef, "$name setpointTempRefDev");
- }
- } elsif (defined AttrVal($name, "humidityRefDev", undef)) {
- if (AttrVal($name, "humidityRefDev", undef) eq $1) {
- CommandDeleteAttr(undef, "$name humidityRefDev");
- }
- } elsif (defined AttrVal($name, "observeRefDev", undef)) {
- if (AttrVal($name, "observeRefDev", undef) eq $1) {
- CommandDeleteAttr(undef, "$name observeRefDev");
- }
- } elsif (defined AttrVal($name, "demandRespRefDev", undef)) {
- if (AttrVal($name, "demandRespRefDev", undef) eq $1) {
- CommandDeleteAttr(undef, "$name demandRespRefDev");
- }
- }
- #Log3($name, 5, "EnOcean $name <notify> DELETED $1");
- } elsif ($devName eq "global" && $s =~ m/^DEFINED ([^ ]*)$/) {
- my $definedName = $1;
- if ($name eq $definedName) {
- if (exists($attr{$name}{subType}) && $attr{$name}{subType} =~ m/^hvac\.0(1|4)$/) {
- # control PID regulatior
- if (AttrVal($name, 'pidCtrl', 'on') eq 'on' && ReadingsVal($name, 'maintenanceMode', 'off') eq 'off') {
- EnOcean_setPID(undef, $hash, 'start', ReadingsVal($name, "setpoint", ''));
- } else {
- EnOcean_setPID(undef, $hash, 'stop', '');
- }
- }
- }
- # teach-in response actions
- # delete temporary teach-in response device, see V9333_02
- #Log3($name, 2, "EnOcean $name <notify> DEFINED $definedName");
- } elsif ($devName eq "global" && $s =~ m/^INITIALIZED$/) {
- # assign remote management defptr
- if (exists $attr{$name}{remoteID}) {
- $modules{EnOcean}{defptr}{$attr{$name}{remoteID}} = $hash;
- }
- if (AttrVal($name ,"subType", "") eq "roomCtrlPanel.00") {
- CommandDeleteReading(undef, "$name waitingCmds");
- }
- if (exists($attr{$name}{subType}) && $attr{$name}{subType} =~ m/^hvac\.0(1|4)$/) {
- # control PID regulatior
- if (AttrVal($name, 'pidCtrl', 'on') eq 'on' && ReadingsVal($name, 'maintenanceMode', 'off') eq 'off') {
- EnOcean_setPID(undef, $hash, 'start', ReadingsVal($name, "setpoint", ''));
- } else {
- EnOcean_setPID(undef, $hash, 'stop', '');
- }
- }
- if (exists($attr{$name}{subType}) && $attr{$name}{subType} eq "switch.05") {
- my @getCmd = ($name, 'state');
- EnOcean_Get($hash, @getCmd);
- }
- EnOcean_ReadDevDesc(undef, $hash);
- #Log3($name, 2, "EnOcean $name <notify> INITIALIZED");
- } elsif ($devName eq "global" && $s =~ m/^REREADCFG$/) {
- # assign remote management defptr
- if (exists $attr{$name}{remoteID}) {
- $modules{EnOcean}{defptr}{$attr{$name}{remoteID}} = $hash;
- }
- if (AttrVal($name ,"subType", "") eq "roomCtrlPanel.00") {
- CommandDeleteReading(undef, "$name waitingCmds");
- }
- if (exists($attr{$name}{subType}) && $attr{$name}{subType} =~ m/^hvac\.0(1|4)$/) {
- # control PID regulatior
- if (AttrVal($name, 'pidCtrl', 'on') eq 'on' && ReadingsVal($name, 'maintenanceMode', 'off') eq 'off') {
- EnOcean_setPID(undef, $hash, 'start', ReadingsVal($name, "setpoint", ''));
- } else {
- EnOcean_setPID(undef, $hash, 'stop', '');
- }
- }
- EnOcean_ReadDevDesc(undef, $hash);
- #Log3($name, 2, "EnOcean $name <notify> REREADCFG");
- } elsif ($devName eq "global" && $s =~ m/^ATTR ([^ ]*) ([^ ]*) ([^ ]*)$/) {
- my ($sdev, $attrName, $attrVal) = ($1, $2, $3);
- #Log3 $name, 5, "EnOcean $name <notify> ATTR $1 $2 $3";
- if ($name eq $sdev && $attrName =~ m/^subDef.?/ && $attrVal eq "getNextID") {
- $attr{$name}{$attrName} = '0' x 8;
- $attr{$name}{$attrName} = EnOcean_CheckSenderID("getNextID", $defs{$name}{IODev}{NAME}, "00000000");
- Log3 $name, 2, "EnOcean set $name attribute $attrName to " . $attr{$name}{$attrName};
- } elsif ($attrName eq "devChannel" && $attrVal =~ m/^[\dA-Fa-f]{2}$/) {
- # convert old format
- $attr{$name}{$attrName} = hex $attrVal;
- }
- } elsif ($devName eq "global" && $s =~ m/^DELETEATTR ([^ ]*)$/) {
- #Log3($name, 5, "EnOcean $name <notify> DELETEATTR $1");
- } elsif ($devName eq "global" && $s =~ m/^MODIFIED ([^ ]*)$/) {
- #Log3($name, 5, "EnOcean $name <notify> MODIFIED");
- } elsif ($devName eq "global" && $s =~ m/^SAVE$/) {
- #Log3($name, 5, "EnOcean $name <notify> SAVE");
- } elsif ($devName eq "global" && $s =~ m/^SHUTDOWN$/) {
- #Log3($name, 5, "EnOcean $name <notify> SHUTDOWN");
- } else {
- my @parts = split(/: | /, $s);
- if (defined AttrVal($name, "observeRefDev", undef)) {
- my @observeRefDev = split("[ \t][ \t]*", AttrVal($name, "observeRefDev", undef));
- if (grep /^$devName$/, @observeRefDev) {
- my ($reading, $value) = ("", "");
- $reading = shift @parts;
- if (!defined($parts[0]) || @parts > 1) {
- $value = $s;
- $reading = "state";
- } else {
- $value = $parts[0];
- }
- my @cmdObserve = ($devName, $reading, $value);
- EnOcean_observeParse(2, $hash, @cmdObserve);
- #Log3($name, 5, "EnOcean $name <notify> observeRefDev: $devName $reading: $value");
- }
- }
- if (defined AttrVal($name, "demandRespRefDev", undef)) {
- my @demandRespRefDev = split("[ \t][ \t]*", AttrVal($name, "demandRespRefDev", undef));
- if (grep /^$devName$/, @demandRespRefDev) {
- my @cmdDemandResponse;
- my $actionCmd = AttrVal($name, "demandRespAction", undef);
- if (defined $actionCmd) {
- if ($parts[0] =~ m/^on|off$/) {
- #Log3($name, 3, "EnOcean $name <notify> demandRespRefDev: $devName Cmd: set $parts[0]");
- my %specials = ("%TARGETNAME" => $name,
- "%NAME" => $devName,
- "%TARGETTYPE" => $hash->{TYPE},
- "%TYPE" => $dev->{TYPE},
- "%LEVEL" => ReadingsVal($devName, "level", 15),
- "%SETPOINT" => ReadingsVal($devName, "setpoint", 255),
- "%POWERUSAGE" => ReadingsVal($devName, "powerUsage", 100),
- "%POWERUSAGESCALE" => ReadingsVal($devName, "powerUsageScale", "max"),
- "%POWERUSAGELEVEL" => ReadingsVal($devName, "powerUsageLevel", "max"),
- "%TARGETSTATE" => ReadingsVal($name, "state", ""),
- "%STATE" => ReadingsVal($devName, "state", "off")
- );
- # action exec
- $actionCmd = EvalSpecials($actionCmd, %specials);
- my $ret = AnalyzeCommandChain(undef, $actionCmd);
- Log3 $name, 2, "EnOcean $name demandRespAction ERROR: $ret" if($ret);
- }
- } elsif (AttrVal($name, "subType", "") =~ m/^switch.*$/ || AttrVal($name, "subTypeSet", "") =~ m/^switch.*$/) {
- if ($parts[0] eq "powerUsageLevel" && $parts[1] eq "max") {
- @cmdDemandResponse = ($name, AttrVal($name, "demandRespMax", "B0"));
- #Log3($name, 3, "EnOcean $name <notify> demandRespRefDev: $devName Cmd: set " . join(" ", @cmdDemandResponse));
- EnOcean_Set($hash, @cmdDemandResponse);
- } elsif ($parts[0] eq "powerUsageLevel" && $parts[1] eq "min") {
- @cmdDemandResponse = ($name, AttrVal($name, "demandRespMin", "BI"));
- #Log3($name, 3, "EnOcean $name <notify> demandRespRefDev: $devName Cmd: set " . join(" ", @cmdDemandResponse));
- EnOcean_Set($hash, @cmdDemandResponse);
- }
- } elsif (AttrVal($name, "subType", "") eq "gateway" && AttrVal($name, "gwCmd", "") eq "switching") {
- if ($parts[0] eq "powerUsageLevel" && $parts[1] eq "max") {
- @cmdDemandResponse = ($name, "on");
- #Log3($name, 3, "EnOcean $name <notify> demandRespRefDev: $devName Cmd: set " . join(" ", @cmdDemandResponse));
- EnOcean_Set($hash, @cmdDemandResponse);
- } elsif ($parts[0] eq "powerUsageLevel" && $parts[1] eq "min") {
- @cmdDemandResponse = ($name, "off");
- #Log3($name, 3, "EnOcean $name <notify> demandRespRefDev: $devName Cmd: set " . join(" ", @cmdDemandResponse));
- EnOcean_Set($hash, @cmdDemandResponse);
- }
- } elsif ((AttrVal($name, "subType", "") eq "gateway" && AttrVal($name, "gwCmd", "") eq "dimming")
- || AttrVal($name, "subType", "") eq "actuator.01") {
- if ($parts[0] eq "powerUsage") {
- if (ReadingsVal($devName, "powerUsageScale", "max") eq "rel") {
- @cmdDemandResponse = ($name, "dim", $parts[1] * ReadingsVal($name, "dimValueLast", 100) / 100);
- } else {
- @cmdDemandResponse = ($name, "dim", $parts[1]);
- }
- #Log3($name, 3, "EnOcean $name <notify> demandRespRefDev: $devName Cmd: set " . join(" ", @cmdDemandResponse));
- EnOcean_Set($hash, @cmdDemandResponse);
- }
- } elsif ((AttrVal($name, "subType", "") eq "roomSensorControl.05" && AttrVal($name, "manufID", "") eq "00D")) {
- if ($parts[0] eq "level") {
- @cmdDemandResponse = ($name, "nightReduction", int(5 - 1/3 * $parts[1]));
- #Log3($name, 3, "EnOcean $name <notify> demandRespRefDev: $devName Cmd: set " . join(" ", @cmdDemandResponse));
- EnOcean_Set($hash, @cmdDemandResponse);
- }
- } elsif (AttrVal($name, "subType", "") eq "lightCtrl.01") {
- if ($parts[0] eq "setpoint") {
- @cmdDemandResponse = ($name, "dim", $parts[1]);
- #Log3($name, 3, "EnOcean $name <notify> demandRespRefDev: $devName Cmd: set " . join(" ", @cmdDemandResponse));
- EnOcean_Set($hash, @cmdDemandResponse);
- }
- } elsif (AttrVal($name, "subType", "") eq "roomSensorControl.01") {
- if ($parts[0] eq "setpoint") {
- @cmdDemandResponse = ($name, "setpoint", $parts[1]);
- #Log3($name, 3, "EnOcean $name <notify> demandRespRefDev: $devName Cmd: set " . join(" ", @cmdDemandResponse));
- EnOcean_Set($hash, @cmdDemandResponse);
- }
- } elsif (AttrVal($name, "subType", "") eq "roomSensorControl.05") {
- if ($parts[0] eq "setpoint") {
- @cmdDemandResponse = ($name, $parts[0], $parts[1]);
- #Log3($name, 3, "EnOcean $name <notify> demandRespRefDev: $devName Cmd: set " . join(" ", @cmdDemandResponse));
- EnOcean_Set($hash, @cmdDemandResponse);
- }
- } elsif (AttrVal($name, "subType", "") eq "roomCtrlPanel.00") {
- if ($parts[0] eq "powerUsageLevel" && $parts[1] eq "max") {
- @cmdDemandResponse = ($name, "roomCtrlMode", "comfort");
- #Log3($name, 3, "EnOcean $name <notify> demandRespRefDev: $devName Cmd: set " . join(" ", @cmdDemandResponse));
- EnOcean_Set($hash, @cmdDemandResponse);
- } elsif ($parts[0] eq "powerUsageLevel" && $parts[1] eq "min") {
- @cmdDemandResponse = ($name, "roomCtrlMode", "economy");
- #Log3($name, 3, "EnOcean $name <notify> demandRespRefDev: $devName Cmd: set " . join(" ", @cmdDemandResponse));
- EnOcean_Set($hash, @cmdDemandResponse);
- }
- }
- }
- }
- if (defined(AttrVal($name, "humidityRefDev", undef)) && AttrVal($name, "setCmdTrigger", "man") eq "refDev") {
- if ($devName eq AttrVal($name, "humidityRefDev", "")) {
- if ($parts[0] eq "humidity") {
- if (AttrVal($name, "subType", "") eq "roomSensorControl.01") {
- my @setCmd = ($name, "setpoint");
- EnOcean_Set($hash, @setCmd);
- }
- }
- }
- #Log3 $name, 2, "EnOcean $name <notify> $devName $s";
- }
- if (defined(AttrVal($name, "temperatureRefDev", undef)) && AttrVal($name, "setCmdTrigger", "man") eq "refDev") {
- # sent a setpoint or setpointTemp telegram
- if ($devName eq AttrVal($name, "temperatureRefDev", "")) {
- if ($parts[0] eq "temperature") {
- if (AttrVal($name, "subType", "") eq "roomSensorControl.05" && AttrVal($name, "manufID", "") eq "00D") {
- my @setCmd = ($name, "setpointTemp");
- EnOcean_Set($hash, @setCmd);
- } elsif (AttrVal($name, "subType", "") eq "fanCtrl.00") {
- my @setCmd = ($name, "setpointTemp");
- EnOcean_Set($hash, @setCmd);
- } elsif (AttrVal($name, "subType", "") eq "roomSensorControl.01") {
- my @setCmd = ($name, "setpoint");
- EnOcean_Set($hash, @setCmd);
- } elsif (AttrVal($name, "subType", "") eq "roomSensorControl.05") {
- my @setCmd = ($name, "setpoint");
- EnOcean_Set($hash, @setCmd);
- }
- }
- }
- #Log3 $name, 2, "EnOcean $name <notify> $devName $s";
- }
- if (defined(AttrVal($name, "temperatureRefDev", undef)) &&
- $devName eq AttrVal($name, "temperatureRefDev", "") &&
- $parts[0] eq "temperature") {
- if (AttrVal($name, "subType", "") eq "hvac.01") {
- readingsSingleUpdate($hash, "temperature", $parts[1], 1);
- #Log3 $name, 2, "EnOcean $name <notify> $devName $s";
- }
- if (AttrVal($name, "subType", "") eq "hvac.04" && AttrVal($name, "measurementCtrl", "enable") eq 'disable') {
- readingsSingleUpdate($hash, "temperature", $parts[1], 1);
- #Log3 $name, 2, "EnOcean $name <notify> $devName $s";
- }
- }
- if (defined(AttrVal($name, "setpointRefDev", undef)) &&
- $devName eq AttrVal($name, "setpointRefDev", "") &&
- $parts[0] eq "setpoint") {
- if (AttrVal($name, "subType", '') =~ m/^hvac\.0(1|4)$/) {
- my @setCmd = ($name, "setpoint", $parts[1]);
- EnOcean_Set($hash, @setCmd);
- #Log3 $name, 2, "EnOcean $name <notify> $devName $s";
- }
- }
- if (defined(AttrVal($name, "setpointTempRefDev", undef)) &&
- $devName eq AttrVal($name, "setpointTempRefDev", "") &&
- $parts[0] eq "setpointTemp") {
- if (AttrVal($name, "subType", '') =~ m/^hvac\.0(1|4)$/) {
- if (ReadingsVal($name, "setpointTemp", 0) != $parts[1]) {
- my @setCmd = ($name, "setpointTemp", $parts[1]);
- EnOcean_Set($hash, @setCmd);
- #Log3 $name, 2, "EnOcean $name <notify> $devName $s";
- }
- }
- }
- }
- }
- return undef;
- }
- # ADT encapsulation
- sub
- EnOcean_Encapsulation($$$$)
- {
- my ($packetType, $rorg, $data, $destinationID) = @_;
- if ($destinationID eq "FFFFFFFF") {
- return ($rorg, $data);
- } else {
- $data = $rorg . $data;
- return ("A6", $data);
- }
- }
- # set PID regulator
- sub EnOcean_setPID($$$) {
- my ($crtl, $hash, $cmd, $adjust) = @_;
- my $name = $hash->{NAME};
- my ($err, $response, $logLevel) = (undef, 'start', 5);
- @{$hash->{helper}{calcPID}} = (undef, $hash, $cmd);
- if ($cmd eq 'stop' || AttrVal($name, 'pidCtrl', 'on') eq 'off') {
- $hash->{helper}{stopped} = 1;
- readingsSingleUpdate($hash, "pidState", 'stopped', 0);
- RemoveInternalTimer($hash->{helper}{calcPID});
- $response = 'stopped';
- } elsif ($cmd eq 'start' || $cmd eq 'actuator') {
- $hash->{helper}{stopped} = 0;
- $hash->{helper}{adjust} = $adjust;
- RemoveInternalTimer($hash->{helper}{calcPID});
- ($err, $logLevel, $response) = EnOcean_calcPID($hash->{helper}{calcPID});
- }
- return ($err, $logLevel, $response);
- }
- # calc valve setpoint (PID regulator)
- sub EnOcean_calcPID($) {
- my ($pidParam) = @_;
- my ($crtl, $hash, $cmd) = @$pidParam;
- my $name = $hash->{NAME};
- my ($err, $response, $logLevel, $setpoint) = (undef, $cmd, 5, 0);
- #####
- my $reUINT = '^([\\+]?\\d+)$'; # uint without whitespaces
- my $re01 = '^([0,1])$'; # only 0,1
- my $reINT = '^([\\+,\\-]?\\d+$)'; # int
- my $reFloatpos = '^([\\+]?\\d+\\.?\d*$)'; # gleitpunkt positiv float
- my $reFloat = '^([\\+,\\-]?\\d+\\.?\d*$)'; # float
- my $sensor = $name;
- my $reading = 'temperature';
- my $regexp = $reFloat;
- my $DEBUG_Sensor = AttrVal( $name, 'pidDebugSensor', '0' ) eq '1';
- my $DEBUG_Actuation = AttrVal( $name, 'pidDebugActuation', '0' ) eq '1';
- my $DEBUG_Delta = AttrVal( $name, 'pidDebugDelta', '0' ) eq '1';
- my $DEBUG_Calc = AttrVal( $name, 'pidDebugCalc', '0' ) eq '1';
- my $DEBUG_Update = AttrVal( $name, 'pidDebugUpdate', '0' ) eq '1';
- my $DEBUG = $DEBUG_Sensor || $DEBUG_Actuation || $DEBUG_Calc || $DEBUG_Delta || $DEBUG_Update;
- my $actuation = "";
- my $actuationDone = ReadingsVal( $name, 'setpointSet', ReadingsVal( $name, 'setpoint', ""));
- my $actuationCalc = ReadingsVal( $name, 'setpointCalc', "" );
- my $actuationCalcOld = $actuationCalc;
- my $actorTimestamp =
- ( $hash->{helper}{actorTimestamp} )
- ? $hash->{helper}{actorTimestamp}
- : FmtDateTime( gettimeofday() - 3600 * 24 );
- my $desired = '';
- my $sensorStr = ReadingsVal($name, 'temperature',"");
- my $sensorValue = "";
- my $sensorTS = ReadingsTimestamp($name, 'temperature', undef);
- my $sensorIsAlive = 0;
- my $iPortion = ReadingsVal( $name, 'p_i', 0 );
- my $pPortion = ReadingsVal( $name, 'p_p', "" );
- my $dPortion = ReadingsVal( $name, 'p_d', "" );
- my $stateStr = "";
- CommandDeleteReading(undef, "$name pidAlarm");
- my $deltaOld = ReadingsVal( $name, 'delta', 0 );
- my $delta = "";
- my $deltaGradient = ( $hash->{helper}{deltaGradient} ) ? $hash->{helper}{deltaGradient} : 0;
- my $calcReq = 0;
- my $readingUpdateReq = '';
- # ---------------- check conditions
- while (1)
- {
- # --------------- retrive values from attributes
- my $wakeUpCycle = AttrVal($name, 'wakeUpCycle', ReadingsVal($name, 'wakeUpCycle', 300));
- my $pidCycle = $wakeUpCycle / 3;
- $pidCycle = 10 if ($pidCycle < 10);
- $hash->{helper}{actorInterval} = 10;
- $hash->{helper}{actorThreshold} = 0;
- $hash->{helper}{actorKeepAlive} = $pidCycle;
- $hash->{helper}{actorValueDecPlaces} = 0;
- $hash->{helper}{actorErrorAction} = AttrVal($name, 'pidActorErrorAction', 'freeze');
- $hash->{helper}{actorErrorPos} = AttrVal($name, 'pidActorErrorPos', 0);
- $hash->{helper}{calcInterval} = $pidCycle;
- $hash->{helper}{deltaTreshold} = AttrVal($name, 'pidDeltaTreshold', 0);
- if (AttrVal($name, 'measurementCtrl', 'enable') eq 'enable') {
- $hash->{helper}{sensorTimeout} = $wakeUpCycle * 4;
- } else {
- $hash->{helper}{sensorTimeout} = AttrVal($name, 'pidSensorTimeout', 3600);
- }
- $hash->{helper}{reverseAction} = 0;
- $hash->{helper}{updateInterval} = $pidCycle;
- $hash->{helper}{actorLimitLower} = AttrVal($name, 'pidActorLimitLower', 0);
- my $actorLimitLower = $hash->{helper}{actorLimitLower};
- $hash->{helper}{actorLimitUpper} = AttrVal($name, 'pidActorLimitUpper', 100);
- my $actorLimitUpper = $hash->{helper}{actorLimitUpper};
- $hash->{helper}{factor_P} = AttrVal($name, 'pidFactor_P', 25);
- $hash->{helper}{factor_I} = AttrVal($name, 'pidFactor_I', 0.25);
- $hash->{helper}{factor_D} = AttrVal($name, 'pidFactor_D', 0);
- if ($hash->{helper}{stopped}) {
- $stateStr = "stopped";
- last;
- }
- $desired = ReadingsVal( $name, 'setpointTempSet', ReadingsVal($name, 'setpointTemp', ""));
- #my $desired = ReadingsVal( $name, $hash->{helper}{desiredName}, "" );
- # sensor found
- #PID20_Log $hash, 2, "--------------------------" if ($DEBUG);
- #PID20_Log $hash, 2, "S1 sensorStr:$sensorStr sensorTS:$sensorTS" if ($DEBUG_Sensor);
- if ( !$sensorStr && !$stateStr ) {
- $stateStr = "alarm";
- $err = 'no_temperature_value';
- }
- # sensor alive
- if ( $sensorStr && $sensorTS )
- {
- my $timeDiff = EnOcean_TimeDiff($sensorTS);
- $sensorIsAlive = 1 if ( $timeDiff <= $hash->{helper}{sensorTimeout} );
- $sensorStr =~ m/$regexp/;
- $sensorValue = $1;
- $sensorValue = "" if ( !defined($sensorValue) );
- #PID20_Log $hash, 2,
- # "S2 timeOfDay:"
- # . gettimeofday()
- # . " timeDiff:$timeDiff sensorTimeout:"
- # . $hash->{helper}{sensorTimeout}
- # . " --> sensorIsAlive:$sensorIsAlive"
- # if ($DEBUG_Sensor);
- }
- # sensor dead
- if (!$sensorIsAlive && !$stateStr) {
- $stateStr = "alarm";
- $err = 'dead_sensor';
- }
- # missing desired
- if ($desired eq "" && !$stateStr) {
- $stateStr = "alarm";
- $err = 'setpoint_device_missing';
- }
- # check delta threshold
- $delta = ( $desired ne "" && $sensorValue ne "" ) ? $desired - $sensorValue : "";
- $calcReq = 1 if ( !$stateStr && $delta ne "" && ( abs($delta) >= abs( $hash->{helper}{deltaTreshold} ) ) );
- #PID20_Log $hash, 2,
- # "D1 desired[" . ( $desired ne "" ) ? sprintf( "%.1f", $desired )
- # : "" . "] - sensorValue: [" . ( $sensorValue ne "" ) ? sprintf( "%.1f", $sensorValue )
- # : "" . "] = delta[" . ( $delta ne "" ) ? sprintf( "%.2f", $delta )
- # : "" . "] calcReq:$calcReq"
- # if ($DEBUG_Delta);
- #request for calculation
- # ---------------- calculation request
- if ($calcReq)
- {
- # reverse action requested
- my $workDelta = ( $hash->{helper}{reverseAction} == 1 ) ? -$delta : $delta;
- my $deltaOld = -$deltaOld if ( $hash->{helper}{reverseAction} == 1 );
- # calc p-portion
- $pPortion = $workDelta * $hash->{helper}{factor_P};
- # calc d-Portion
- $dPortion = ($deltaGradient) * $hash->{helper}{calcInterval} * $hash->{helper}{factor_D};
- # calc i-portion respecting windUp
- # freeze i-portion if windUp is active
- my $isWindup = $actuationCalcOld
- && ( ( $workDelta > 0 && $actuationCalcOld > $actorLimitUpper )
- || ( $workDelta < 0 && $actuationCalcOld < $actorLimitLower ) );
- if ( $hash->{helper}{adjust} ne "" )
- {
- $iPortion = $hash->{helper}{adjust} - ( $pPortion + $dPortion );
- $iPortion = $actorLimitUpper if ( $iPortion > $actorLimitUpper );
- $iPortion = $actorLimitLower if ( $iPortion < $actorLimitLower );
- #PID20_Log $hash, 5, "adjust request with:" . $hash->{helper}{adjust} . " ==> p_i:$iPortion";
- $hash->{helper}{adjust} = "";
- } elsif ( !$isWindup ) # integrate only if no windUp
- {
- # normalize the intervall to minute=60 seconds
- $iPortion = $iPortion + $workDelta * $hash->{helper}{factor_I} * $hash->{helper}{calcInterval} / 60;
- $hash->{helper}{isWindUP} = 0;
- }
- $hash->{helper}{isWindUP} = $isWindup;
- # check callback for iPortion
- my $iportionCallBeforeSetting = AttrVal( $name, 'pidIPortionCallBeforeSetting', undef );
- if ( defined($iportionCallBeforeSetting) && exists &$iportionCallBeforeSetting )
- {
- #PID20_Log $hash, 5, 'start callback ' . $iportionCallBeforeSetting . ' with iPortion:' . $iPortion;
- no strict "refs";
- $iPortion = &$iportionCallBeforeSetting( $name, $iPortion );
- use strict "refs";
- #PID20_Log $hash, 5, 'return value of ' . $iportionCallBeforeSetting . ':' . $iPortion;
- }
- # calc actuation
- $actuationCalc = $pPortion + $iPortion + $dPortion;
- #PID20_Log $hash, 2, "P1 delta:" . sprintf( "%.2f", $delta ) . " isWindup:$isWindup" if ($DEBUG_Calc);
- #PID20_Log $hash, 2,
- # "P2 pPortion:"
- # . sprintf( "%.2f", $pPortion )
- # . " iPortion:"
- # . sprintf( "%.2f", $iPortion )
- # . " dPortion:"
- # . sprintf( "%.2f", $dPortion )
- # . " actuationCalc:"
- # . sprintf( "%.2f", $actuationCalc )
- # if ($DEBUG_Calc);
- }
- $readingUpdateReq = 1; # in each case update readings
- # ---------------- acutation request
- my $noTrouble = ( $desired ne "" && $sensorIsAlive );
- # check actor fallback in case of sensor fault
- if (!$sensorIsAlive && ($hash->{helper}{actorErrorAction} eq "errorPos")) {
- #$stateStr .= "- force pid-output to errorPos";
- $err .= ':actuator_in_errorPos';
- $actuationCalc = $hash->{helper}{actorErrorPos};
- $actuationCalc = "" if ( !defined($actuationCalc) );
- }
- # check acutation diff
- $actuation = $actuationCalc;
- # limit $actuation
- $actuation = $actorLimitUpper if ( $actuation ne "" && ( $actuation > $actorLimitUpper ) );
- $actuation = $actorLimitLower if ( $actuation ne "" && ( $actuation < $actorLimitLower ) );
- # check if round request
- my $fmt = "%." . $hash->{helper}{actorValueDecPlaces} . "f";
- $actuation = sprintf( $fmt, $actuation ) if ( $actuation ne "" );
- my $actuationDiff = abs( $actuation - $actuationDone )
- if ( $actuation ne "" && $actuationDone ne "" );
- #PID20_Log $hash, 2,
- # "A1 act:$actuation actDone:$actuationDone "
- # . " actThreshold:"
- # . $hash->{helper}{actorThreshold}
- # . " actDiff:$actuationDiff"
- # if ($DEBUG_Actuation);
- # check threshold-condition for actuation
- my $rsTS = $actuationDone ne "" && $actuationDiff >= $hash->{helper}{actorThreshold};
- # ...... special handling if acutation is in the black zone between actorLimit and (actorLimit - actorThreshold)
- # upper range
- my $rsUp =
- $actuationDone ne ""
- && $actuation > $actorLimitUpper - $hash->{helper}{actorThreshold}
- && $actuationDiff != 0
- && $actuation >= $actorLimitUpper;
- # low range
- my $rsDown =
- $actuationDone ne ""
- && $actuation < $actorLimitLower + $hash->{helper}{actorThreshold}
- && $actuationDiff != 0
- && $actuation <= $actorLimitLower;
- # upper or lower limit are exceeded
- my $rsLimit = $actuationDone ne "" && ( $actuationDone < $actorLimitLower || $actuationDone > $actorLimitUpper );
- my $actuationByThreshold = ( ( $rsTS || $rsUp || $rsDown ) && $noTrouble );
- #PID20_Log $hash, 2, "A2 rsTS:$rsTS rsUp:$rsUp rsDown:$rsDown noTrouble:$noTrouble"
- # if ($DEBUG_Actuation);
- # check time condition for actuation
- my $actTimeDiff = EnOcean_TimeDiff($actorTimestamp); # $actorTimestamp is valid in each case
- my $actuationByTime = ($noTrouble) && ( $actTimeDiff > $hash->{helper}{actorInterval} );
- #PID20_Log $hash, 2,
- # "A3 actTS:$actorTimestamp"
- # . " actTimeDiff:"
- # . sprintf( "%.2f", $actTimeDiff )
- # . " actInterval:"
- # . $hash->{helper}{actorInterval}
- # . "-->actByTime:$actuationByTime "
- # if ($DEBUG_Actuation);
- # check keep alive condition for actuation
- my $actuationKeepAliveReq = ( $actTimeDiff >= $hash->{helper}{actorKeepAlive} )
- if ( defined($actTimeDiff) && $actuation ne "" );
- # build total actuation request
- my $actuationReq = (
- ( $actuationByThreshold && $actuationByTime )
- || $actuationKeepAliveReq # request by keep alive
- || $rsLimit # upper or lower limit are exceeded
- || $actuationDone eq "" # startup condition
- ) && $actuation ne ""; # acutation is initialized
- #PID20_Log $hash, 2,
- # "A4 (actByTh:$actuationByThreshold && actByTime:$actuationByTime)"
- # . "||actKeepAlive:$actuationKeepAliveReq"
- # . "||rsLimit:$rsLimit=actnReq:$actuationReq"
- # if ($DEBUG_Actuation);
- # ................ perform output to actor
- if ($actuationReq)
- {
- $readingUpdateReq = 1; # update the readings
- # check calback for actuation
- my $actorCallBeforeSetting = AttrVal( $name, 'pidActorCallBeforeSetting', undef );
- if ( defined($actorCallBeforeSetting) && exists &$actorCallBeforeSetting )
- {
- #PID20_Log $hash, 5, 'start callback ' . $actorCallBeforeSetting . ' with actuation:' . $actuation;
- no strict "refs";
- $actuation = &$actorCallBeforeSetting( $name, $actuation );
- use strict "refs";
- #PID20_Log $hash, 5, 'return value of ' . $actorCallBeforeSetting . ':' . $actuation;
- }
- #build command for fhem
- #PID20_Log $hash, 5,
- # "actor:"
- # . $hash->{helper}{actor}
- # . " actorCommand:"
- # . $hash->{helper}{actorCommand}
- # . " actuation:"
- # . $actuation;
- #my $cmd = sprintf( "set %s %s %g", $hash->{helper}{actor}, $hash->{helper}{actorCommand}, $actuation );
- # execute command
- my $ret;
- #$ret = fhem $cmd;
- $setpoint = $actuation;
- $actuationDone = $actuation;
- # note timestamp
- $hash->{helper}{actorTimestamp} = TimeNow();
- my $retStr = "";
- $retStr = " with return-value:" . $ret if ( defined($ret) && ( $ret ne '' ) );
- #PID20_Log $hash, 3, "<$cmd> " . $retStr;
- }
- my $updateAlive = ( $actuation ne "" )
- && EnOcean_TimeDiff( ReadingsTimestamp( $name, 'setpointSet', gettimeofday() ) ) >= $hash->{helper}{updateInterval};
- # my $updateReq = ( ( $actuationReq || $updateAlive ) && $actuation ne "" );
- # PID20_Log $hash, 2, "U1 actReq:$actuationReq updateAlive:$updateAlive --> updateReq:$updateReq" if ($DEBUG_Update);
- # ---------------- update request
- if ($readingUpdateReq)
- {
- readingsBeginUpdate($hash);
- #readingsBulkUpdate( $hash, $hash->{helper}{desiredName}, $desired ) if ( $desired ne "" );
- #readingsBulkUpdate( $hash, $hash->{helper}{measuredName}, $sensorValue ) if ( $sensorValue ne "" );
- readingsBulkUpdate( $hash, 'p_p', $pPortion ) if ( $pPortion ne "" );
- readingsBulkUpdate( $hash, 'p_d', $dPortion ) if ( $dPortion ne "" );
- readingsBulkUpdate( $hash, 'p_i', $iPortion ) if ( $iPortion ne "" );
- readingsBulkUpdate( $hash, 'setpointSet', $actuationDone) if ($actuationDone ne "");
- readingsBulkUpdate( $hash, 'setpointCalc', $actuationCalc ) if ( $actuationCalc ne "" );
- readingsBulkUpdate( $hash, 'delta', $delta ) if ( $delta ne "" );
- readingsEndUpdate( $hash, 1 );
- #PID20_Log $hash, 5, "readings updated";
- }
- last;
- } # end while
- # ........ update statePID.
- $stateStr = 'idle' if ($stateStr eq '' && !$calcReq);
- $stateStr = 'processing' if ($stateStr eq '' && $calcReq);
- #PID20_Log $hash, 2, "C1 stateStr:$stateStr calcReq:$calcReq" if ($DEBUG_Calc);
- #......... timer setup
- #my $next = gettimeofday() + $hash->{helper}{calcInterval};
- #RemoveInternalTimer($name); # prevent multiple timers for same hash
- #InternalTimer( $next, "PID20_Calc", $name, 1 );
- #PID20_Log $hash, 2, "InternalTimer next:".FmtDateTime($next)." PID20_Calc name:$name DEBUG_Calc:$DEBUG_Calc";
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, 'pidState', $stateStr);
- readingsBulkUpdate($hash, 'pidAlarm', $err) if (defined $err);
- readingsEndUpdate($hash, 1);
- Log3($name, 5, "EnOcean $name EnOcean_calcPID Cmd: $cmd pidState: $stateStr T: $sensorValue SP: $setpoint SPT: $desired");
- @{$hash->{helper}{calcPID}} = (undef, $hash, 'periodic');
- RemoveInternalTimer($hash->{helper}{calcPID});
- InternalTimer(gettimeofday() + $hash->{helper}{calcInterval} * 1.02, "EnOcean_calcPID", $hash->{helper}{calcPID}, 0);
- return ($err, $logLevel, $response);
- }
- # sent message to Multisnesor Window Handle (EEP D2-06-01)
- sub
- EnOcean_multisensor_01Snd($$$)
- {
- my ($crtl, $hash, $packetType) = @_;
- my $name = $hash->{NAME};
- my ($data, $err, $response, $logLevel);
- EnOcean_SndRadio(undef, $hash, $packetType, "D2", $data, AttrVal($name, "subDef", "00000000"), "00", $hash->{DEF});
- return ($err, $response);
- }
- # sent message to Room Control Panel (EEP D2-10-xx)
- sub
- EnOcean_roomCtrlPanel_00Snd($$$$$$$$)
- {
- my ($crtl, $hash, $packetType, $mid, $mcf, $irc, $fbc, $gmt) = @_;
- my $name = $hash->{NAME};
- my ($data, $err, $response, $logLevel);
- my $messagePart = 1;
- if ($mid == 0) {
- # general massage
- ($err, $response, $data, $logLevel) = EnOcean_roomCtrlPanel_00Cmd(undef, $hash, $mcf, $messagePart);
- EnOcean_SndRadio(undef, $hash, $packetType, "D2", $data, AttrVal($name, "subDef", "00000000"), "00", $hash->{DEF});
- if ($err) {
- Log3 $name, $logLevel, "EnOcean $name Error: $err";
- } else {
- Log3 $name, $logLevel, "EnOcean $name $response";
- }
- if (!defined($irc)) {
- } elsif ($irc == 0) {
- # acknowledge request
- } elsif ($irc == 1) {
- # data request
- } elsif ($irc == 2) {
- # configuration request
- } elsif ($irc == 3) {
- # room control request
- } elsif ($irc == 4) {
- # time program request
- }
- if (!defined($fbc)) {
- } elsif ($fbc == 0) {
- # acknowledge / heartbeat
- } elsif ($fbc == 1) {
- # telegram repetition request
- } elsif ($fbc == 2) {
- # message repetition request
- } elsif ($fbc == 3) {
- # reserved
- }
- if (!defined($gmt)) {
- } elsif ($gmt == 0) {
- # information request
- } elsif ($gmt == 1) {
- # feetback
- }
- } elsif ($mid == 1) {
- # data message
- ($err, $response, $data, $logLevel) = EnOcean_roomCtrlPanel_00Cmd(undef, $hash, $mcf, $messagePart);
- EnOcean_SndRadio(undef, $hash, $packetType, "D2", $data, AttrVal($name, "subDef", "00000000"), "00", $hash->{DEF});
- if ($err) {
- Log3 $name, $logLevel, "EnOcean $name Error: $err";
- } else {
- Log3 $name, $logLevel, "EnOcean $name $response";
- }
- } elsif ($mid == 2) {
- # configuration message
- ($err, $response, $data, $logLevel) = EnOcean_roomCtrlPanel_00Cmd(undef, $hash, $mcf, $messagePart);
- EnOcean_SndRadio(undef, $hash, $packetType, "D2", $data, AttrVal($name, "subDef", "00000000"), "00", $hash->{DEF});
- if ($err) {
- Log3 $name, $logLevel, "EnOcean $name Error: $err";
- } else {
- Log3 $name, $logLevel, "EnOcean $name $response";
- }
- } elsif ($mid == 3) {
- # room control setup
- ($err, $response, $data, $logLevel) = EnOcean_roomCtrlPanel_00Cmd(undef, $hash, $mcf, $messagePart);
- EnOcean_SndRadio(undef, $hash, $packetType, "D2", $data, AttrVal($name, "subDef", "00000000"), "00", $hash->{DEF});
- if ($err) {
- Log3 $name, $logLevel, "EnOcean $name Error: $err";
- } else {
- Log3 $name, $logLevel, "EnOcean $name $response";
- }
- } elsif ($mid == 4) {
- # time program setup
- ($err, $response, $data, $logLevel) = EnOcean_roomCtrlPanel_00Cmd(undef, $hash, $mcf, $messagePart);
- EnOcean_SndRadio(undef, $hash, $packetType, "D2", $data, AttrVal($name, "subDef", "00000000"), "00", $hash->{DEF});
- if ($err) {
- Log3 $name, $logLevel, "EnOcean $name Error: $err";
- } else {
- Log3 $name, $logLevel, "EnOcean $name $response";
- }
- }
- return ($err, $response);
- }
- # generate command to Room Control Panel (EEP D2-10-xx)
- sub
- EnOcean_roomCtrlPanel_00Cmd($$$$)
- {
- my ($crtl, $hash, $mcf, $messagePart) = @_;
- my $name = $hash->{NAME};
- my $data = "0000";
- my $err;
- my $response = "acknowledge send";
- my $logLevel = 4;
- # Waitings Commands (waitingCmds)
- # 1 = sent data request
- # 2 = sent data message
- # 4 = sent configuration message (time)
- # 8 = sent room control setup
- # 16 = sent time program setup
- # 32 = sent configuration request
- # 64 = sent configuration message
- # 128 = sent room control setup request
- # 256 = sent time program request
- # 512 = sent delete time program
- my $waitingCmds = ReadingsVal($name, "waitingCmds", 0);
- if ($mcf == 0) {
- # message complete
- if ($waitingCmds & 8) {
- # room control setup waiting
- my ($db5, $db4, $db3, $db2, $db1, $db0) = (96, 0, 0, 0, 0, 0, 0);
- if (defined ReadingsVal($name, "setpointComfortTempSet", undef)) {
- $db1 = ReadingsVal($name, "setpointComfortTempSet", 0) * 255 / 40;
- $db0 = $db0 | 1;
- CommandDeleteReading(undef, "$name setpointComfortTempSet");
- }
- if (defined ReadingsVal($name, "setpointEconomyTempSet", undef)) {
- $db2 = ReadingsVal($name, "setpointEconomyTempSet", 0) * 255 / 40;
- $db0 = $db0 | 2;
- CommandDeleteReading(undef, "$name setpointEconomyTempSet");
- }
- if (defined ReadingsVal($name, "setpointPreComfortTempSet", undef)) {
- $db3 = ReadingsVal($name, "setpointPreComfortTempSet", 0) * 255 / 40;
- $db0 = $db0 | 4;
- CommandDeleteReading(undef, "$name setpointPreComfortTempSet");
- }
- if (defined ReadingsVal($name, "setpointBuildingProtectionTempSet", undef)) {
- $db4 = ReadingsVal($name, "setpointBuildingProtectionTempSet", 0) * 255 / 40;
- $db0 = $db0 | 8;
- CommandDeleteReading(undef, "$name setpointBuildingProtectionTempSet");
- }
- $data = sprintf "%02X%02X%02X%02X%02X%02X", $db5, $db4, $db3, $db2, $db1, $db0;
- # clear command
- $waitingCmds = $waitingCmds & 247 + 0xFF00;
- $response = "room control setup send $data";
- $logLevel = 2;
- } elsif ($waitingCmds & 64 || $waitingCmds & 4) {
- # configuration message waiting
- my ($sec, $min, $hour, $day, $month, $year) = localtime();
- my ($key, $val);
- $month += 1;
- $year += 1900;
- my ($db7, $db6, $db5, $db4, $db1, $db0) = (64, 0, 0, 0, $min << 2, $hour << 3);
- $db6 |= 1 if (AttrVal($name, "blockFanSpeed", "no") ne "yes" );
- $db6 |= 2 if (AttrVal($name, "blockSetpointTemp", "no") ne "yes" );
- $db6 |= 4 if (AttrVal($name, "blockOccupancy", "no") ne "yes" );
- $db6 |= 8 if (AttrVal($name, "blockTimeProgram", "no") ne "yes" );
- $db6 |= 16 if (AttrVal($name, "blockDateTime", "no") ne "yes" );
- $db6 |= 32 if (AttrVal($name, "blockDisplay", "no") ne "yes" );
- $db6 |= 64 if (AttrVal($name, "blockTemp", "no") ne "yes" );
- $db6 |= 128 if (AttrVal($name, "blockMotion", "no") ne "yes" );
- $db5 = AttrVal($name, "pollInterval", 10);
- if ($db5 > 60 && $db5 <= 180) {
- $db5 = 61;
- } elsif ($db5 > 180 && $db5 <= 720) {
- $db5 = 62;
- } elsif ($db5 > 720) {
- $db5 = 63;
- }
- $db5 = $db5 << 2;
- $db5 |= 2 if (AttrVal($name, "blockKey", "no") ne "yes" );
- my $displayContent = AttrVal($name, "displayContent", "no_change");
- my $displayContentVal = 0;
- my %displayContent = ("humidity" => 7,
- "off" => 6,
- "setpointTemp" => 5,
- "tempertureExtern" => 4,
- "temperatureIntern" => 3,
- "time" => 2,
- "default" => 1,
- "no_change" => 0
- );
- while (($key, $val) = each(%displayContent)) {
- $displayContentVal = $val if ($key eq $displayContent);
- }
- my $temperatureScale = AttrVal($name, "temperatureScale", "no_change");
- my $temperatureScaleVal = 0;
- my %temperatureScale = ("F" => 3,
- "C" => 2,
- "default" => 1,
- "no_change" => 0
- );
- while (($key, $val) = each(%temperatureScale)) {
- $temperatureScaleVal = $val if ($key eq $temperatureScale);
- }
- my $daylightSavingTimeVal = 0;
- $daylightSavingTimeVal = 1 if (AttrVal($name, "daylightSavingTime", "supported") eq "not_supported");
- my $timeNotation = AttrVal($name, "timeNotation", "no_change");
- my $timeNotationVal = 0;
- if ($timeNotation eq "no_change") {
- $timeNotationVal = 0;
- } elsif ($timeNotation eq "default") {
- $timeNotationVal = 1;
- } elsif ($timeNotation == 24) {
- $timeNotationVal = 2;
- } elsif ($timeNotation == 12) {
- $timeNotationVal = 3;
- }
- $db4 = (($displayContentVal << 2 | $temperatureScaleVal) << 1 | $daylightSavingTimeVal) << 2 | $timeNotationVal;
- my $db32 = ($day << 4 | $month) << 7 | $year - 2000;
- if ($waitingCmds & 4) {
- $db0 |= 1;
- # clear time command
- $waitingCmds &= 251 + 0xFF00;
- }
- if ($waitingCmds & 64) {
- # clear config command
- $waitingCmds &= 191 + 0xFF00;
- }
- $data = sprintf "%02X%02X%02X%02X%04X%02X%02X", $db7, $db6, $db5, $db4, $db32, $db1, $db0;
- $response = "configuration message send $data";
- $logLevel = 2;
- } elsif ($waitingCmds & 2) {
- # data message waiting
- my ($db7, $db6, $db5, $db4, $db3, $db2, $db1, $db0) = (32, 0, 0, 0, 0, 0, 0, 0, 0);
- if (defined ReadingsVal($name, "setpointTempSet", undef)) {
- $db1 = ReadingsVal($name, "setpointTempSet", 0) * 255 / 40;
- $db2 |= 2;
- CommandDeleteReading(undef, "$name setpointTempSet");
- }
- my $heatingSet;
- if (defined ReadingsVal($name, "heatingSet", undef)) {
- $heatingSet = ReadingsVal($name, "heatingSet", 0);
- } else {
- $heatingSet = ReadingsVal($name, "heating", 0);
- }
- if ($heatingSet eq "no_change") {
- $heatingSet = 0;
- } elsif ($heatingSet eq "on") {
- $heatingSet = 1;
- } elsif ($heatingSet eq "off") {
- $heatingSet = 2;
- } elsif ($heatingSet eq "auto") {
- $heatingSet = 3;
- }
- $db2 |= $heatingSet << 4;
- CommandDeleteReading(undef, "$name heatingSet");
- my $coolingSet;
- if (defined ReadingsVal($name, "coolingSet", undef)) {
- $coolingSet = ReadingsVal($name, "coolingSet", 0);
- } else {
- $coolingSet = ReadingsVal($name, "cooling", 0);
- }
- if ($coolingSet eq "no_change") {
- $coolingSet = 0;
- } elsif ($coolingSet eq "on") {
- $coolingSet = 1;
- } elsif ($coolingSet eq "off") {
- $coolingSet = 2;
- } elsif ($coolingSet eq "auto") {
- $coolingSet = 3;
- }
- $db2 |= $coolingSet << 6;
- CommandDeleteReading(undef, "$name coolingSet");
- my $roomCtrlModeSet;
- if (defined ReadingsVal($name, "roomCtrlModeSet", undef)) {
- $roomCtrlModeSet = ReadingsVal($name, "roomCtrlModeSet", 0);
- } else {
- $roomCtrlModeSet = ReadingsVal($name, "roomCtrlMode", 0);
- }
- if ($roomCtrlModeSet eq "comfort") {
- $roomCtrlModeSet = 0;
- } elsif ($roomCtrlModeSet eq "economy") {
- $roomCtrlModeSet = 1;
- } elsif ($roomCtrlModeSet eq "preComfort") {
- $roomCtrlModeSet = 2;
- } elsif ($roomCtrlModeSet eq "buildingProtection") {
- $roomCtrlModeSet = 3;
- }
- $db2 |= $roomCtrlModeSet << 2;
- CommandDeleteReading(undef, "$name roomCtrlModeSet");
- my $windowSet;
- if (defined ReadingsVal($name, "windowSet", undef)) {
- $windowSet = ReadingsVal($name, "windowSet", 0);
- } else {
- $windowSet = ReadingsVal($name, "window", 0);
- }
- if ($windowSet eq "no_change") {
- $windowSet = 0;
- } elsif ($windowSet eq "closed") {
- $windowSet = 1;
- } elsif ($windowSet eq "open") {
- $windowSet = 2;
- } elsif ($windowSet eq "reserved") {
- $windowSet = 3;
- }
- $db4 |= $windowSet;
- CommandDeleteReading(undef, "$name windowSet");
- my $fanSpeedModeSet;
- if (defined ReadingsVal($name, "fanSpeedModeSet", undef)) {
- $fanSpeedModeSet = ReadingsVal($name, "fanSpeedModeSet", 0);
- } else {
- $fanSpeedModeSet = ReadingsVal($name, "fanSpeedMode", 0);
- }
- $db4 |= 64 if ($fanSpeedModeSet eq "local");
- CommandDeleteReading(undef, "$name fanSpeedModeSet");
- if (defined ReadingsVal($name, "fanSpeedSet", undef)) {
- $db5 = ReadingsVal($name, "fanSpeedSet", 0);
- $db4 |= 128;
- CommandDeleteReading(undef, "$name fanSpeedSet");
- }
- $data = sprintf "%02X%02X%02X%02X%02X%02X%02X%02X", $db7, $db6, $db5, $db4, $db3, $db2, $db1, $db0;
- # clear command
- $waitingCmds = $waitingCmds & 253 + 0xFF00;
- $response = "data message send";
- $logLevel = 2;
- } elsif ($waitingCmds & 512) {
- # delete time program command waiting
- $data = "800000000001";
- # clear command
- $waitingCmds = $waitingCmds & 0xFDFF;
- $response = "delete time program send";
- $logLevel = 2;
- } elsif ($waitingCmds & 16) {
- # time program setup waiting
- my ($db5, $endMinute, $endHour, $startMinute, $startHour, $db0) = (128, 0, 0, 0, 0, 0, 0);
- my ($key, $val);
- my $messagePartCntr;
- for ($messagePartCntr = 4; $messagePartCntr >= 1; $messagePartCntr --) {
- if ($hash->{helper}{4}{telegramWait}{$messagePartCntr}) {
- $hash->{helper}{4}{telegramWait}{$messagePartCntr} = 0;
- my $timeProgram = AttrVal($name, "timeProgram" . $messagePartCntr, undef);
- my @timeProgram = split("[ \t][ \t]*", $timeProgram);
- my ($period, $roomCtrlMode) = ($timeProgram[0], $timeProgram[3]);
- ($startHour, $startMinute) = split(':', $timeProgram[1]);
- ($endHour, $endMinute) = split(':', $timeProgram[2]);
- my $periodVal = 0;
- my %period = ("FrMo" => 15,
- "FrSu" => 14,
- "ThFr" => 13,
- "WeFr" => 12,
- "TuTh" => 11,
- "MoWe" => 10,
- "Su" => 9,
- "Sa" => 8,
- "Fr" => 7,
- "Th" => 6,
- "We" => 5,
- "Tu" => 4,
- "Mo" => 3,
- "SaSu" => 2,
- "MoFr" => 1,
- "MoSu" => 0
- );
- while (($key, $val) = each(%period)) {
- $periodVal = $val if ($key eq $period);
- }
- if ($roomCtrlMode eq "buildingProtection") {
- $roomCtrlMode = 3;
- } elsif ($roomCtrlMode eq "preComfort") {
- $roomCtrlMode = 2;
- } elsif ($roomCtrlMode eq "economy") {
- $roomCtrlMode = 1;
- } else{
- $roomCtrlMode = 0;
- }
- if ($messagePartCntr > 1) {
- # set mcf flag
- $db5 |= 1;
- } else {
- # clear command
- $waitingCmds = $waitingCmds & 239 + 0xFF00;
- }
- $data = sprintf "%02X%02X%02X%02X%02X%02X", $db5, $endMinute, $endHour, $startMinute, $startHour, $periodVal << 4 | $roomCtrlMode << 2;
- $response = "time program setup send";
- $logLevel = 2;
- last;
- }
- }
- } elsif ($waitingCmds & 1) {
- # data request waiting
- $data = "0009";
- # clear command
- $waitingCmds = $waitingCmds & 254 + 0xFF00;
- $response = "data request send";
- $logLevel = 2;
- } elsif ($waitingCmds & 32) {
- # configuration request waiting
- $data = "0011";
- # clear command
- $waitingCmds = $waitingCmds & 223 + 0xFF00;
- $response = "configuration request send";
- $logLevel = 2;
- } elsif ($waitingCmds & 128) {
- # room control setup request waiting
- $data = "0019";
- # clear command
- $waitingCmds = $waitingCmds & 127 + 0xFF00;
- $response = "room control setup request send";
- $logLevel = 2;
- } elsif ($waitingCmds & 256) {
- # time program request waiting
- $data = "0021";
- # clear command
- $waitingCmds = $waitingCmds & 0xFEFF;
- $response = "time program request send";
- $logLevel = 2;
- }
- if ($waitingCmds == 0) {
- CommandDeleteReading(undef, "$name waitingCmds");
- } else {
- readingsSingleUpdate($hash, "waitingCmds", $waitingCmds, 0);
- }
- } elsif ($mcf == 1) {
- # message incomplete
- $response = "acknowledge send, wating for next part of the message";
- $logLevel = 2;
- } elsif ($mcf == 2) {
- # automatic message control
- $response = "acknowledge send, automatic message control";
- $logLevel = 2;
- } elsif ($mcf == 3) {
- # reserved
- }
- return ($err, $response, $data, $logLevel);
- }
- # create SVG devices
- sub EnOcean_CreateSVG($$$)
- {
- my ($ctrl, $hash, $eepSVG) = @_;
- my $name = $hash->{NAME};
- my ($autocreateHash, $autocreateName, $autocreateDeviceRoom, $autocreateWeblinkRoom) =
- (undef, 'autocreate', 'EnOcean', 'Plots');
- my $filelogName = "FileLog_$name";
- my ($cmd, $eep, $weblinkName, $weblinkHash, $ret);
- if (defined($eepSVG) && $eepSVG =~ m/^([A-Za-z0-9]{2})-([A-Za-z0-9]{2})-([A-Za-z0-9]{2})$/i) {
- $eep = uc("$1.$2.$3");
- } elsif (exists($attr{$name}{eep}) && $attr{$name}{eep} =~ m/^([A-Za-z0-9]{2})-([A-Za-z0-9]{2})-([A-Za-z0-9]{2})$/i) {
- $eep = uc("$1.$2.$3");
- } else {
- return undef;
- }
- # find autocreate device
- while (($autocreateName, $autocreateHash) = each(%defs)) {
- last if ($defs{$autocreateName}{TYPE} eq "autocreate");
- }
- # delete old SVG devices
- if (defined($ctrl) && $ctrl eq 'del' || !exists($defs{$filelogName})) {
- while (($weblinkName, $weblinkHash) = each(%defs)) {
- if ($weblinkName =~ /^SVG_$name.*/) {
- CommandDelete(undef, $weblinkName);
- Log3 $hash->{NAME}, 5, "EnOcean_CreateSVG: device $weblinkName deleted";
- }
- }
- }
- if (!defined(AttrVal($autocreateName, "disable", undef)) && exists($defs{$filelogName})) {
- if (exists $EnO_eepConfig{$eep}{GPLOT}) {
- # add GPLOT parameters
- $attr{$filelogName}{logtype} = $EnO_eepConfig{$eep}{GPLOT} . $attr{$filelogName}{logtype}
- if (!exists($attr{$filelogName}{logtype}) || $attr{$filelogName}{logtype} eq 'text');
- if (AttrVal($autocreateName, "weblink", 1)) {
- $autocreateWeblinkRoom = $attr{$autocreateName}{weblink_room} if (exists $attr{$autocreateName}{weblink_room});
- $autocreateWeblinkRoom = 'EnOcean' if ($autocreateWeblinkRoom eq '%TYPE');
- $autocreateWeblinkRoom = $name if ($autocreateWeblinkRoom eq '%NAME');
- $autocreateWeblinkRoom = $attr{$name}{room} if (exists $attr{$name}{room});
- my $wnr = 1;
- #create SVG devices
- foreach my $wdef (split(/,/, $EnO_eepConfig{$eep}{GPLOT})) {
- next if(!$wdef);
- my ($gplotfile, $stuff) = split(/:/, $wdef);
- next if(!$gplotfile);
- $weblinkName = "SVG_$name";
- $weblinkName .= "_$wnr" if($wnr > 1);
- $wnr++;
- next if (exists $defs{$weblinkName});
- $cmd = "$weblinkName SVG $filelogName:$gplotfile:CURRENT";
- Log3 $weblinkName, 2, "EnOcean define $cmd";
- $ret = CommandDefine(undef, $cmd);
- if($ret) {
- Log3 $weblinkName, 2, "EnOcean ERROR: define $cmd: $ret";
- last;
- }
- $attr{$weblinkName}{room} = $autocreateWeblinkRoom;
- $attr{$weblinkName}{title} = '"' . $name . ' Min $data{min1}, Max $data{max1}, Last $data{currval1}"';
- $ret = CommandSet(undef, "$weblinkName copyGplotFile");
- if($ret) {
- Log3 $weblinkName, 2, "EnOcean ERROR: set $weblinkName copyGplotFile: $ret";
- last;
- }
- }
- }
- }
- }
- return undef;
- }
- #CommandSave
- sub EnOcean_CommandSave($$)
- {
- my ($ctrl, $param) = @_;
- # find autocreate device
- my ($autocreateHash, $autocreateName);
- while (($autocreateName, $autocreateHash) = each(%defs)) {
- last if ($defs{$autocreateName}{TYPE} eq "autocreate");
- }
- my $autosave = AttrVal($autocreateName, "autosave", undef);
- if (!defined $autosave) {
- CommandSave($ctrl, $param) if (AttrVal("global", "autosave", 1));
- } elsif ($autosave) {
- CommandSave($ctrl, $param);
- }
- return;
- }
- sub EnOcean_readingsSingleUpdate($) {
- my ($readingParam) = @_;
- my ($hash, $readingName, $readingVal, $ctrl, $log) = @$readingParam;
- if (defined $hash) {
- readingsSingleUpdate($hash, $readingName, $readingVal, $ctrl) ;
- Log3 $hash->{NAME}, $log, "EnOcean " . $hash->{NAME} . " EVENT $readingName: $readingVal" if ($log);
- }
- return;
- }
- sub EnOcean_4BSRespWait($$$) {
- my ($ctrl, $hash, $subDef) = @_;
- my $IODev = $hash->{IODev}{NAME};
- my $IOHash = $defs{$IODev};
- $hash->{IODev}{helper}{"4BSRespWait"}{$subDef}{teachInReq} = "out";
- $hash->{IODev}{helper}{"4BSRespWait"}{$subDef}{hash} = $hash;
- if (!exists($hash->{IODev}{Teach})) {
- # enable teach-in receiving for 3 sec
- $hash->{IODev}{Teach} = 1;
- my %timeoutHash = (hash => $IOHash, function => "4BSRespTimeout", helper => "4BSRespWait");
- RemoveInternalTimer(\%timeoutHash);
- InternalTimer(gettimeofday() + 3, "EnOcean_RespTimeout", \%timeoutHash, 0);
- }
- return;
- }
- # Check SenderIDs
- sub EnOcean_CheckSenderID($$$)
- {
- my ($ctrl, $IODev, $senderID) = @_;
- if (!defined $IODev) {
- my (@listIODev, %listIODev);
- foreach my $dev (keys %defs) {
- next if ($defs{$dev}{TYPE} ne "EnOcean");
- push(@listIODev, $defs{$dev}{IODev}{NAME});
- }
- @listIODev = sort grep(!$listIODev{$_}++, @listIODev);
- if (@listIODev == 1) {
- $IODev = $listIODev[0];
- }
- }
- my $unusedID = 0;
- $unusedID = hex($defs{$IODev}{BaseID}) if ($defs{$IODev}{BaseID});
- my $IDCntr1;
- my $IDCntr2;
- if ($unusedID == 0) {
- $IDCntr1 = 0;
- $IDCntr2 = 0;
- } else {
- $IDCntr1 = $unusedID + 1;
- $IDCntr2 = $unusedID + 127;
- }
- if ($ctrl eq "getBaseID") {
- # get TCM BaseID of the EnOcean device
- if ($defs{$IODev}{BaseID}) {
- $senderID = $defs{$IODev}{BaseID}
- } else {
- $senderID = "0" x 8;
- }
- } elsif ($ctrl eq "getUsedID") {
- # find and sort used SenderIDs
- my @listID;
- my %listID;
- foreach my $dev (keys %defs) {
- next if ($defs{$dev}{TYPE} ne "EnOcean");
- push(@listID, grep(hex($_) >= $IDCntr1 && hex($_) <= $IDCntr2, $defs{$dev}{DEF}));
- push(@listID, $attr{$dev}{subDef}) if ($attr{$dev}{subDef});
- push(@listID, $attr{$dev}{subDefA}) if ($attr{$dev}{subDefA});
- push(@listID, $attr{$dev}{subDefB}) if ($attr{$dev}{subDefB});
- push(@listID, $attr{$dev}{subDefC}) if ($attr{$dev}{subDefC});
- push(@listID, $attr{$dev}{subDefD}) if ($attr{$dev}{subDefD});
- push(@listID, $attr{$dev}{subDefI}) if ($attr{$dev}{subDefI});
- push(@listID, $attr{$dev}{subDefH}) if ($attr{$dev}{subDefH});
- push(@listID, $attr{$dev}{subDefW}) if ($attr{$dev}{subDefW});
- push(@listID, $attr{$dev}{subDef0}) if ($attr{$dev}{subDef0});
- }
- $senderID = join(" ", sort grep(!$listID{$_}++, @listID));
- } elsif ($ctrl eq "getFreeID") {
- # find and sort free SenderIDs
- my (@freeID, @listID, %listID, @intersection, @difference, %count, $element);
- for (my $IDCntr = $IDCntr1; $IDCntr <= $IDCntr2; $IDCntr++) {
- push(@freeID, sprintf "%08X", $IDCntr);
- }
- foreach my $dev (keys %defs) {
- next if ($defs{$dev}{TYPE} ne "EnOcean");
- push(@listID, grep(hex($_) >= $IDCntr1 && hex($_) <= $IDCntr2, $defs{$dev}{DEF}));
- push(@listID, $attr{$dev}{subDef}) if ($attr{$dev}{subDef} && $attr{$dev}{subDef} ne "00000000");
- push(@listID, $attr{$dev}{subDefA}) if ($attr{$dev}{subDefA} && $attr{$dev}{subDefA} ne "00000000");
- push(@listID, $attr{$dev}{subDefB}) if ($attr{$dev}{subDefB} && $attr{$dev}{subDefB} ne "00000000");
- push(@listID, $attr{$dev}{subDefC}) if ($attr{$dev}{subDefC} && $attr{$dev}{subDefC} ne "00000000");
- push(@listID, $attr{$dev}{subDefD}) if ($attr{$dev}{subDefD} && $attr{$dev}{subDefD} ne "00000000");
- push(@listID, $attr{$dev}{subDefI}) if ($attr{$dev}{subDefI} && $attr{$dev}{subDefI} ne "00000000");
- push(@listID, $attr{$dev}{subDefH}) if ($attr{$dev}{subDefH} && $attr{$dev}{subDefH} ne "00000000");
- push(@listID, $attr{$dev}{subDefW}) if ($attr{$dev}{subDefW} && $attr{$dev}{subDefW} ne "00000000");
- push(@listID, $attr{$dev}{subDef0}) if ($attr{$dev}{subDef0} && $attr{$dev}{subDef0} ne "00000000");
- }
- @listID = sort grep(!$listID{$_}++, @listID);
- foreach $element (@listID, @freeID) {
- $count{$element}++
- }
- foreach $element (keys %count) {
- push @{$count{$element} > 1 ? \@intersection : \@difference }, $element;
- }
- $senderID = ':' . join(" ", sort @difference);
- } elsif ($ctrl eq "getNextID") {
- # get next free SenderID
- my (@freeID, @listID, %listID, @intersection, @difference, %count, $element);
- for (my $IDCntr = $IDCntr1; $IDCntr <= $IDCntr2; $IDCntr++) {
- push(@freeID, sprintf "%08X", $IDCntr);
- }
- foreach my $dev (keys %defs) {
- next if ($defs{$dev}{TYPE} ne "EnOcean");
- push(@listID, grep(hex($_) >= $IDCntr1 && hex($_) <= $IDCntr2, $defs{$dev}{DEF}));
- push(@listID, $attr{$dev}{subDef}) if ($attr{$dev}{subDef} && $attr{$dev}{subDef} ne "00000000");
- push(@listID, $attr{$dev}{subDefA}) if ($attr{$dev}{subDefA} && $attr{$dev}{subDefA} ne "00000000");
- push(@listID, $attr{$dev}{subDefB}) if ($attr{$dev}{subDefB} && $attr{$dev}{subDefB} ne "00000000");
- push(@listID, $attr{$dev}{subDefC}) if ($attr{$dev}{subDefC} && $attr{$dev}{subDefC} ne "00000000");
- push(@listID, $attr{$dev}{subDefD}) if ($attr{$dev}{subDefD} && $attr{$dev}{subDefD} ne "00000000");
- push(@listID, $attr{$dev}{subDefI}) if ($attr{$dev}{subDefI} && $attr{$dev}{subDefI} ne "00000000");
- push(@listID, $attr{$dev}{subDefH}) if ($attr{$dev}{subDefH} && $attr{$dev}{subDefH} ne "00000000");
- push(@listID, $attr{$dev}{subDefW}) if ($attr{$dev}{subDefW} && $attr{$dev}{subDefW} ne "00000000");
- push(@listID, $attr{$dev}{subDef0}) if ($attr{$dev}{subDef0} && $attr{$dev}{subDef0} ne "00000000");
- }
- @listID = sort grep(!$listID{$_}++, @listID);
- foreach $element (@listID, @freeID) {
- $count{$element}++
- }
- foreach $element (keys %count) {
- push @{$count{$element} > 1 ? \@intersection : \@difference }, $element;
- }
- @difference = sort @difference;
- if (defined $difference[0]) {
- $senderID = $difference[0];
- } else {
- $senderID = "0" x 8;
- Log3 $IODev, 2, "EnOcean $IODev no free senderIDs available";
- }
- } else {
- }
- return $senderID;
- }
- # assign next free SenderID
- sub EnOcean_AssignSenderID($$$$)
- {
- my ($ctrl, $hash, $attrName, $comMode) = @_;
- my $def = $hash->{DEF};
- my $err;
- my $name = $hash->{NAME};
- my $IODev = $hash->{IODev}{NAME};
- my $senderID = AttrVal($name, $attrName, "");
- # SenderID valid
- return ($err, $senderID) if ($senderID =~ m/^[\dA-Fa-f]{8}$/);
- return ("no IODev", $def) if (!defined $IODev);
- # DEF is SenderID
- if (hex($def) >= hex($defs{$IODev}{BaseID}) && hex($def) <= hex($defs{$IODev}{BaseID}) + 127) {
- if ($comMode eq "biDir") {
- $attr{$name}{comMode} = $comMode;
- } else {
- $attr{$name}{comMode} = "uniDir";
- }
- return ($err, $def);
- } else {
- if ($comMode eq "biDir") {
- $attr{$name}{comMode} = $comMode;
- } else {
- $attr{$name}{comMode} = "confirm";
- }
- $senderID = EnOcean_CheckSenderID("getNextID", $IODev, "00000000");
- }
- #Log3 $name, 2, "EnOcean $name SenderID: $senderID assigned";
- #CommandAttr(undef, "$name $attrName $senderID");
- $attr{$name}{$attrName} = $senderID;
- return ($err, $senderID);
- }
- # split chained data message
- sub EnOcean_SndCdm($$$$$$$$)
- {
- my ($ctrl, $hash, $packetType, $rorg, $data, $senderID, $status, $destinationID) = @_;
- if (!defined $data) {
- Log3 $hash->{NAME}, 5, "EnOcean $hash->{NAME} EnOcean_SndCDM SenderID: $senderID DestinationID: $destinationID " .
- "PacketType: $packetType RORG: $rorg DATA: undef STATUS: $status";
- return;
- }
- my ($seq, $idx, $len, $dataPart, $dataPartLen) = (0, 0, length($data) / 2, undef, 14);
- # split telelegram with optional data
- $dataPartLen = 9 if ($destinationID ne "FFFFFFFF");
- if ($packetType == 1 && $len > $dataPartLen) {
- # first CDM telegram
- if ($dataPartLen == 14) {
- $data =~ m/^(....................)(.*)$/;
- $dataPart = (sprintf "%02X", $seq << 6 | $idx) . (sprintf "%04X", $len) . $rorg . $1;
- $data = $2;
- } else {
- $data =~ m/^(..........)(.*)$/;
- $dataPart = (sprintf "%02X", $seq << 6 | $idx) . (sprintf "%04X", $len) . $rorg . $1;
- $data = $2;
- }
- $idx ++;
- $len -= $dataPartLen - 5;
- EnOcean_SndRadio($ctrl, $hash, $packetType, "40", $dataPart, $senderID, $status, $destinationID);
- while ($len > 0) {
- if ($len > $dataPartLen - 2) {
- if ($dataPartLen == 14) {
- $data =~ m/^(..........................)(.*)$/;
- $dataPart = (sprintf "%02X", $seq << 6 | $idx) . $1;
- $data = $2;
- } else {
- $data =~ m/^(................)(.*)$/;
- $dataPart = (sprintf "%02X", $seq << 6 | $idx) . $1;
- $data = $2;
- }
- $idx ++;
- $len -= $dataPartLen - 2;
- } else {
- $dataPart = (sprintf "%02X", $seq << 6 | $idx) . $data;
- $len = 0;
- }
- EnOcean_SndRadio($ctrl, $hash, $packetType, "40", $dataPart, $senderID, $status, $destinationID);
- }
- } else {
- # not necessary to split
- EnOcean_SndRadio($ctrl, $hash, $packetType, $rorg, $data, $senderID, $status, $destinationID);
- }
- return;
- }
- # send ESP3 Packet Type Radio
- sub EnOcean_SndRadio($$$$$$$$)
- {
- my ($ctrl, $hash, $packetType, $rorg, $data, $senderID, $status, $destinationID) = @_;
- if (!defined $data) {
- Log3 $hash->{NAME}, 5, "EnOcean $hash->{NAME} EnOcean_SndRadio SenderID: $senderID DestinationID: $destinationID " .
- "PacketType: $packetType RORG: $rorg DATA: undef STATUS: $status";
- return;
- }
- my ($err, $response, $loglevel);
- my $header;
- my $odata = "";
- my $odataLength = 0;
- if ($packetType == 1) {
- ($err, $rorg, $data, $response, $loglevel) = EnOcean_sec_convertToSecure($hash, $packetType, $rorg, $data);
- if (defined $err) {
- Log3 $hash->{NAME}, $loglevel, "EnOcean $hash->{NAME} Error: $err";
- return "";
- }
- if (AttrVal($hash->{NAME}, "repeatingAllowed", "yes") eq "no") {
- $status = substr($status, 0, 1) . "F";
- }
- if ($rorg eq "A6") {
- # ADT telegram
- $data .= $destinationID;
- } elsif ($destinationID ne "FFFFFFFF") {
- # SubTelNum = 03, DestinationID:8, RSSI = FF, secLevel = 00
- $odata = sprintf "03%sFF00", $destinationID;
- $odataLength = 7;
- }
- # Data Length:4 Optional Length:2 Packet Type:2
- $header = sprintf "%04X%02X%02X", (length($data) / 2 + 6), $odataLength, $packetType;
- Log3 $hash->{NAME}, 4, "EnOcean $hash->{NAME} sent PacketType: $packetType RORG: $rorg DATA: $data SenderID: $senderID STATUS: $status ODATA: $odata";
- $data = $rorg . $data . $senderID . $status . $odata;
- } elsif ($packetType == 2 || $packetType == 6) {
- # smart ack commands
- # Data Length:4 Optional Length:2 Packet Type:2
- $header = sprintf "%04X%02X%02X", length($data) / 2, 0, $packetType;
- Log3 $hash->{NAME}, 4, "EnOcean $hash->{NAME} sent PacketType: $packetType DATA: $data";
- } elsif ($packetType == 7) {
- my $delay = 0;
- $delay = 1 if ($destinationID eq "FFFFFFFF");
- $senderID = "00000000";
- #$senderID = "00000000" if ($destinationID eq "FFFFFFFF");
- $odata = sprintf "%s%sFF%02X", $destinationID, $senderID, $delay;
- $odataLength = 10;
- # Data Length:4 Optional Length:2 Packet Type:2
- $header = sprintf "%04X%02X%02X", (length($data) / 2), $odataLength, $packetType;
- Log3 $hash->{NAME}, 4, "EnOcean $hash->{NAME} sent PacketType: $packetType DATA: $data ODATA: $odata";
- $data .= $odata;
- }
- if (defined $ctrl) {
- # sent telegram delayed
- my @param = ($hash, $header, $data);
- InternalTimer(gettimeofday() + $ctrl, 'EnOcean_IOWriteTimer', \@param, 0);
- } else {
- IOWrite($hash, $header, $data);
- }
- return;
- }
- #
- sub EnOcean_IOWriteTimer($)
- {
- my ($ioParam) = @_;
- my ($hash, $header, $data) = @$ioParam;
- IOWrite($hash, $header, $data);
- return;
- }
- # Scale Readings
- sub EnOcean_ReadingScaled($$$$)
- {
- my ($hash, $readingVal, $readingMin, $readingMax) = @_;
- my $name = $hash->{NAME};
- my $valScaled;
- my $scaleDecimals = AttrVal($name, "scaleDecimals", undef);
- my $scaleMin = AttrVal($name, "scaleMin", undef);
- my $scaleMax = AttrVal($name, "scaleMax", undef);
- if (defined $scaleMax && defined $scaleMin &&
- $scaleMax =~ m/^[+-]?\d+(\.\d+)?$/ && $scaleMin =~ m/^[+-]?\d+(\.\d+)?$/) {
- $valScaled = ($readingMin*$scaleMax-$scaleMin*$readingMax)/
- ($readingMin-$readingMax)+
- ($scaleMin-$scaleMax)/($readingMin-$readingMax)*$readingVal;
- }
- if (defined $scaleDecimals && $scaleDecimals =~ m/^[0-9]?$/) {
- $scaleDecimals = "%0." . $scaleDecimals . "f";
- $valScaled = sprintf "$scaleDecimals", $valScaled;
- }
- return $valScaled;
- }
- # Reorganize Strings
- sub EnOcean_ReorgList($)
- {
- my ($list) = @_;
- my @list = split("[ \t][ \t]*", $list);
- my %list;
- @list = sort grep(!$list{$_}++, @list);
- $list = join(" ", @list) . " ";
- return $list;
- }
- # EnOcean_Set called from sub InternalTimer()
- sub EnOcean_TimerSet($)
- {
- my ($par) = @_;
- EnOcean_Set($par->{hash}, @{$par->{timerCmd}});
- }
- #
- sub EnOcean_InternalTimer($$$$$)
- {
- my ($modifier, $tim, $callback, $hash, $waitIfInitNotDone) = @_;
- my $mHash = {};
- my $timerName = "$hash->{NAME}_$modifier";
- if ($modifier eq "") {
- $mHash = $hash;
- } else {
- if (exists ($hash->{helper}{TIMER}{$timerName})) {
- $mHash = $hash->{helper}{TIMER}{$timerName};
- Log3 $hash->{NAME}, 5, "EnOcean_InternalTimer setting mHash with stored $timerName";
- } else {
- $mHash = {HASH => $hash, NAME => $timerName, MODIFIER => $modifier};
- $hash->{helper}{TIMER}{$timerName} = $mHash;
- Log3 $hash->{NAME}, 5, "EnOcean_InternalTimer setting mHash with $timerName";
- }
- }
- InternalTimer($tim, $callback, $mHash, $waitIfInitNotDone);
- Log3 $hash->{NAME}, 5, "EnOcean setting timer $timerName at " . strftime("%Y-%m-%d %H:%M:%S", localtime($tim));
- return;
- }
- #
- sub EnOcean_RemoveInternalTimer($$)
- {
- my ($modifier, $hash) = @_;
- my $mHash = {};
- my $timerName = "$hash->{NAME}_$modifier";
- if ($modifier eq "") {
- RemoveInternalTimer($hash);
- } else {
- $mHash = $hash->{helper}{TIMER}{$timerName};
- if (defined($mHash)) {
- delete $hash->{helper}{TIMER}{$timerName};
- RemoveInternalTimer($mHash);
- }
- }
- Log3 $hash->{NAME}, 5, "EnOcean removing timer $timerName";
- return;
- }
- #
- sub EnOcean_observeInit($$@)
- {
- #init observe
- my ($ctrl, $hash, @cmdValue) = @_;
- my ($err, $name) = (undef, $hash->{NAME});
- return (undef, $ctrl) if (lc(AttrVal($name, "observe", "off")) eq "off");
- return (undef, $ctrl) if (defined($hash->{helper}{observeCntr}) && $hash->{helper}{observeCntr} > 0);
- $hash->{helper}{observeCntr} = 1;
- #my @observeExeptions = split("[ \t][ \t]*", AttrVal($name, "observeExeptions", ""));
- my @observeRefDev = split("[ \t][ \t]*", AttrVal($name, "observeRefDev", $name));
- $hash->{helper}{observeRefDev} = \@observeRefDev;
- Log3 $name, 4, "EnOcean $name < observeRefDev " . join(" ", @{$hash->{helper}{observeRefDev}}) . " (init)";
- $hash->{helper}{lastCmdFunction} = "set";
- # @cmdValue = (<name>, <cmd>, <param1>, <param2>, ...)
- $hash->{helper}{lastCmdValue} = \@cmdValue;
- my $observeCmds = AttrVal($name, "observeCmds", undef);
- my @observeCmds;
- #my %observeCmds;
- my ($cmdPair, $cmdSent, $cmdReceived);
- if (defined $observeCmds) {
- @observeCmds = split("[ \t][ \t]*", $observeCmds);
- foreach my $cmdPair (@observeCmds) {
- ($cmdSent, $cmdReceived) = split(':', $cmdPair);
- $hash->{helper}{observeCmds}{$cmdSent} = $cmdReceived;
- }
- } else {
- $hash->{helper}{observeCmds}{$cmdValue[1]} = $cmdValue[1];
- }
- $hash->{helper}{observeCntr} = 1;
- #CommandDeleteReading(undef, "$name observeFailedDev");
- readingsSingleUpdate($hash, "observeFailedDev", "", 0);
- my %functionHash = (hash => $hash, function => "observe");
- RemoveInternalTimer(\%functionHash);
- InternalTimer(gettimeofday() + AttrVal($name, "observeInterval", 1), "EnOcean_observeRepeat", \%functionHash, 0);
- Log3 $name, 4, "EnOcean set " . join(" ", @cmdValue) . " observing started";
- return ($err, $ctrl);
- }
- #
- sub EnOcean_observeParse($$@)
- {
- # observe acknowledge
- my ($ctrl, $hash, @cmdValue) = @_;
- my ($err, $name) = (undef, $hash->{NAME});
- return (undef, $ctrl) if (lc(AttrVal($name, "observe", "off")) eq "off");
- # observing disabled or ignore second and following acknowledgment telegrams
- return (undef, $ctrl) if (!exists($hash->{helper}{observeRefDev}) || @{$hash->{helper}{observeRefDev}} == 0);
- my $devName = shift @cmdValue;
- my $cmd = shift @cmdValue;
- $cmd = shift @cmdValue if ($cmd eq "state");
- my ($observeRefDevIdx) = grep{$hash->{helper}{observeRefDev}[$_] eq $devName} 0..$#{$hash->{helper}{observeRefDev}};
- # device not observed
- return (undef, $ctrl) if (!defined $observeRefDevIdx);
- Log3 $name, 4, "EnOcean $name < observeRefDev " . join(" ", @{$hash->{helper}{observeRefDev}}) . " parsed";
- Log3 $name, 4, "EnOcean $name < $devName $cmd " . join(" ", @cmdValue) . " received";
- if (@{$hash->{helper}{observeRefDev}} == 1) {
- # last acknowledgment telegram stops observing
- delete $hash->{helper}{observeCmds};
- delete $hash->{helper}{observeCntr};
- delete $hash->{helper}{observeRefDev};
- delete $hash->{helper}{lastCmdFunction};
- delete $hash->{helper}{lastCmdValue};
- my %functionHash = (hash => $hash, function => "observe");
- RemoveInternalTimer(\%functionHash);
- Log3 $name, 4, "EnOcean $name < $devName $cmd " . join(" ", @cmdValue) . " observing stopped";
- } else {
- # remove the device that has sent a telegram
- Log3 $name, 4, "EnOcean $name < observeRefDev " . $hash->{helper}{observeRefDev}[$observeRefDevIdx] . " removed";
- splice(@{$hash->{helper}{observeRefDev}}, $observeRefDevIdx, 1);
- if (lc(AttrVal($name, "observeLogic", "or")) eq "and") {
- # AND logic: remove the device that has sent a telegram and await further telegrams
- Log3 $name, 4, "EnOcean $name < observeRefDev " . join(" ", @{$hash->{helper}{observeRefDev}}) . " observing continued";
- } else {
- # OR logic: last acknowledgment telegram stops observing
- #shift @{$hash->{helper}{observeRefDev}};
- delete $hash->{helper}{observeCmds};
- delete $hash->{helper}{observeCntr};
- delete $hash->{helper}{observeRefDev};
- delete $hash->{helper}{lastCmdFunction};
- delete $hash->{helper}{lastCmdValue};
- my %functionHash = (hash => $hash, function => "observe");
- RemoveInternalTimer(\%functionHash);
- Log3 $name, 4, "EnOcean $name < $devName $cmd " . join(" ", @cmdValue) . " observing stopped";
- }
- }
- # if (defined($hash->{helper}{lastCmdValue}[1]) && $hash->{helper}{lastCmdValue}[1] eq $cmd) {
- # acknowledgment telegram ok, cancel the repetition of the command
- # } else {
- # acknowledgment telegram does not match the telegram sent, cancel the repetition of the command
- # }
- return ($err, $ctrl);
- }
- #
- sub EnOcean_observeRepeat($)
- {
- #timer expires without acknowledgment telegram, repeat command
- my ($functionHash) = @_;
- my $hash = $functionHash->{hash};
- my $name = $hash->{NAME};
- return if (AttrVal($name, "observe", "off") eq "off" || !defined($hash->{helper}{observeCntr}));
- #my @observeExeptions = split("[ \t][ \t]*", AttrVal($name, "observeExeptions", ""));
- if ($hash->{helper}{observeCntr} <= AttrVal($name, "observeCmdRepetition", 2)) {
- #repeat last command
- $hash->{helper}{observeCntr} += 1;
- Log3 $name, 4, "EnOcean set " . join(" ", @{$hash->{helper}{lastCmdValue}}) . " repeated";
- EnOcean_Set($hash, @{$hash->{helper}{lastCmdValue}});
- RemoveInternalTimer($functionHash);
- InternalTimer(gettimeofday() + AttrVal($name, "observeInterval", 1), "EnOcean_observeRepeat", $functionHash, 0);
- } else {
- # reached the maximum number of retries, clear last command
- Log3 $name, 2, "EnOcean set " . join(" ", @{$hash->{helper}{lastCmdValue}}) .
- " observing " . join(" ", @{$hash->{helper}{observeRefDev}}) . " failed";
- #splice(@{$hash->{helper}{observeRefDev}}, 0);
- my $actionCmd = AttrVal($name, "observeErrorAction", undef);
- if (defined $actionCmd) {
- # error action exec
- my %specials = ("%NAME" => shift(@{$hash->{helper}{lastCmdValue}}),
- "%FAILEDDEV" => join(" ", @{$hash->{helper}{observeRefDev}}),
- "%TYPE" => $hash->{TYPE},
- "%EVENT" => ReplaceEventMap($name, join(" ", @{$hash->{helper}{lastCmdValue}}), 1)
- );
- $actionCmd = EvalSpecials($actionCmd, %specials);
- my $ret = AnalyzeCommandChain(undef, $actionCmd);
- Log3 $name, 2, "EnOcean $name observeErrorAction ERROR: $ret" if($ret);
- }
- readingsSingleUpdate($hash, "observeFailedDev", join(" ", @{$hash->{helper}{observeRefDev}}), 1);
- delete $hash->{helper}{observeCmds};
- delete $hash->{helper}{observeCntr};
- delete $hash->{helper}{observeRefDev};
- delete $hash->{helper}{lastCmdFunction};
- delete $hash->{helper}{lastCmdValue};
- RemoveInternalTimer($functionHash);
- }
- return;
- }
- sub EnOcean_RLT($) {
- # Radio Link Test
- my ($rltParam) = @_;
- my ($ctrl, $hash, $dataRx, $subDef, $rltMode, $rssiMaster) = @$rltParam;
- my $name = $hash->{NAME};
- my $def = $hash->{DEF};
- my ($err, $logLevel, $response, $dataTx, $rorg, $msgID) = (undef, 5, undef, undef, undef, 0);
- my $rltCntrMax = AttrVal($name, 'rltRepeat', 16);
- my $rltType = AttrVal($name, 'rltType', '4BS');
- if ($rltType eq '1BS') {
- $msgID = $hash->{helper}{rlt}{cntr} & 0x3F if (exists($hash->{helper}{rlt}{cntr}));
- $dataTx = sprintf("%02X", ($msgID & 0x3C) << 2 | 8 | ($msgID & 3) << 1);
- $rorg = 'D5';
- } else {
- $dataTx = '0000000C';
- $rorg = 'A5';
- }
- if ($ctrl eq 'start') {
- RemoveInternalTimer($hash->{helper}{rlt}{param});
- $hash->{helper}{rlt}{cntr} = 0;
- $hash->{helper}{rlt}{param}[0] = 'periodic';
- readingsSingleUpdate($hash, 'state', 'active', 1);
- EnOcean_SndRadio(undef, $hash, 1, $rorg, $dataTx, $subDef, "00", $def);
- InternalTimer(gettimeofday() + 0.15, "EnOcean_RLT", $hash->{helper}{rlt}{param}, 0);
- } elsif ($ctrl eq 'parse') {
- $hash->{helper}{rlt}{param}[0] = 'periodic';
- if ($hash->{helper}{rlt}{cntr} < $rltCntrMax) {
- # store received RLT data from slave
- $hash->{helper}{rlt}{dataRx}[$hash->{helper}{rlt}{cntr}] = $dataRx;
- $hash->{helper}{rlt}{rssiMaster}[$hash->{helper}{rlt}{cntr}] = $rssiMaster;
- if ($hash->{helper}{rlt}{cntr} < $rltCntrMax - 1) {
- RemoveInternalTimer($hash->{helper}{rlt}{param});
- $hash->{helper}{rlt}{cntr} ++;
- EnOcean_SndRadio(undef, $hash, 1, $rorg, $dataTx, $subDef, "00", $def);
- InternalTimer(gettimeofday() + 0.15, "EnOcean_RLT", $hash->{helper}{rlt}{param}, 0);
- }
- } else {
- RemoveInternalTimer($hash->{helper}{rlt}{param});
- readingsSingleUpdate($hash, 'state', 'stopped', 1);
- EnOcean_RLTResult(undef, $hash, $rltType, $rltCntrMax);
- if (exists $hash->{helper}{rlt}{oldDev}) {
- # activate old device subType
- my $oldHash = $hash->{helper}{rlt}{oldDev}[0];
- my $oldName = $hash->{helper}{rlt}{oldDev}[1];
- my $oldDef = $hash->{helper}{rlt}{oldDev}[2];
- CommandModify(undef, "$oldName $oldDef");
- $modules{EnOcean}{defptr}{$oldDef} = $oldHash;
- }
- delete $hash->{helper}{rlt};
- # delete deviceID
- CommandModify(undef, "$name 00000000");
- delete $modules{EnOcean}{defptr}{$def};
- }
- } elsif ($ctrl eq 'periodic') {
- RemoveInternalTimer($hash->{helper}{rlt}{param});
- if ($hash->{helper}{rlt}{cntr} < $rltCntrMax - 1) {
- # no RLT_SlaveTest telegram received from slave
- $hash->{helper}{rlt}{param}[0] = 'periodic';
- $hash->{helper}{rlt}{cntr} ++;
- EnOcean_SndRadio(undef, $hash, 1, $rorg, $dataTx, $subDef, "00", $def);
- InternalTimer(gettimeofday() + 0.11, "EnOcean_RLT", $hash->{helper}{rlt}{param}, 0);
- } else {
- # waiting for last RLT_SlaveTest telegram
- $hash->{helper}{rlt}{param}[0] = 'waiting';
- InternalTimer(gettimeofday() + 0.15, "EnOcean_RLT", $hash->{helper}{rlt}{param}, 0);
- }
- } elsif ($ctrl eq 'waiting') {
- readingsSingleUpdate($hash, 'state', 'stopped', 1);
- EnOcean_RLTResult(undef, $hash, $rltType, $rltCntrMax);
- if (exists $hash->{helper}{rlt}{oldDev}) {
- # activate old device subType
- my $oldHash = $hash->{helper}{rlt}{oldDev}[0];
- my $oldName = $hash->{helper}{rlt}{oldDev}[1];
- my $oldDef = $hash->{helper}{rlt}{oldDev}[2];
- CommandModify(undef, "$oldName $oldDef");
- $modules{EnOcean}{defptr}{$oldDef} = $oldHash;
- }
- delete $hash->{helper}{rlt};
- # delete deviceID
- CommandModify(undef, "$name 00000000");
- delete $modules{EnOcean}{defptr}{$def};
- } elsif ($ctrl eq 'standby') {
- RemoveInternalTimer($hash->{helper}{rlt}{param});
- delete $hash->{helper}{rlt};
- readingsSingleUpdate($hash, 'state', 'standby', 1);
- # delete deviceID
- CommandModify(undef, "$name 00000000");
- delete $modules{EnOcean}{defptr}{$def};
- } elsif ($ctrl eq 'stop') {
- RemoveInternalTimer($hash->{helper}{rlt}{param});
- if (exists $hash->{helper}{rlt}{oldDev}) {
- # activate old device subType
- my $oldHash = $hash->{helper}{rlt}{oldDev}[0];
- my $oldName = $hash->{helper}{rlt}{oldDev}[1];
- my $oldDef = $hash->{helper}{rlt}{oldDev}[2];
- CommandModify(undef, "$oldName $oldDef");
- $modules{EnOcean}{defptr}{$oldDef} = $oldHash;
- }
- delete $hash->{helper}{rlt};
- readingsSingleUpdate($hash, 'state', 'stopped', 1);
- # delete deviceID
- CommandModify(undef, "$name 00000000");
- delete $modules{EnOcean}{defptr}{$def};
- }
- return ($err, $logLevel, $response);
- }
- sub EnOcean_RLTResult($$$$) {
- # show RLT results
- my ($ctrl, $hash, $rltType, $rltCntrMax) = @_;
- my $name = $hash->{NAME};
- my ($err, $logLevel, $response, $data, $msgCntr, $msgID, $subTelNum, $rssiMaster, $rssiMasterAvg, $rssiSlave, $rssiLevel1, $rssiLevel2, $rssiNonEnOcean) =
- (undef, 5, undef, undef, 0, 0, 0, 0, 0, 0, 0, 0, 0);
- my %rssi = (0 => '-', 1 => '>= -31', 2 => '-32', 0x3F => '<= -93');
- my %rssiNonEnOcean =
- (0 => '-',
- 1 => '>= -31',
- 2 => '-32 ... -37',
- 3 => '-38 ... -43',
- 4 => '-44 ... -49',
- 5 => '-50 ... -55',
- 6 => '-56 ... -61',
- 7 => '-62 ... -67',
- 8 => '-68 ... -73',
- 9 => '-74 ... -79',
- 10 => '-80 ... -85',
- 11 => '<= -92');
- for (my $cntr = 0; $cntr < $rltCntrMax; $cntr++) {
- $data = $hash->{helper}{rlt}{dataRx}[$cntr];
- if (defined $data) {
- $msgCntr ++;
- $rssiMaster = -$hash->{helper}{rlt}{rssiMaster}[$cntr];
- $rssiMasterAvg += $rssiMaster;
- if ($rltType eq '1BS') {
- $msgID = (hex($data) & 0xF0) >> 6 | (hex($data) & 6) >> 1;
- Log3 $name, 2, "EnOcean RLT DeviceID: " . $hash->{DEF} . " msgCntr: ". ($cntr + 1) . " msgID: $msgID RSSI Master: $rssiMaster response received";
- } else {
- $subTelNum = (hex(substr($data, 0, 2)) & 0xC0) >> 6;
- $rssiLevel2 = hex(substr($data, 0, 2)) & 0x3F;
- $rssiLevel2 = $rssi{$rssiLevel2} if (exists $rssi{$rssiLevel2});
- $rssiLevel1 = hex(substr($data, 2, 2));
- $rssiLevel1 = $rssi{$rssiLevel1} if (exists $rssi{$rssiLevel1});
- $rssiSlave = hex(substr($data, 4, 2));
- $rssiSlave = $rssi{$rssiSlave} if (exists $rssi{$rssiSlave});
- $rssiNonEnOcean = (hex(substr($data, 6, 2)) & 0xF0) >> 4;
- $rssiNonEnOcean = $rssiNonEnOcean{$rssiNonEnOcean} if (exists $rssiNonEnOcean{$rssiNonEnOcean});
- $msgID = (hex(substr($data, 6, 2)) & 6) >> 1;
- Log3 $name, 2, "EnOcean RLT DeviceID: " . $hash->{DEF} . " msgCntr: ". ($cntr + 1) . " subTelNum: $subTelNum " .
- "RSSI Master: $rssiMaster RSSI Slave: $rssiSlave RSSI Level 1: $rssiLevel1 RSSI Level 2: $rssiLevel2 " .
- "RSSI non EnOcean: $rssiNonEnOcean";
- }
- } else {
- Log3 $name, 2, "EnOcean RLT DeviceID: " . $hash->{DEF} . " msgCntr: ". ($cntr + 1) . " no answer";
- }
- }
- if ($msgCntr > 0) {
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, 'msgLost', (sprintf "%0.1f", ($rltCntrMax - $msgCntr) / $rltCntrMax * 100));
- readingsBulkUpdate($hash, 'rssiMasterAvg', (sprintf "%0.1f", $rssiMasterAvg / $msgCntr));
- readingsEndUpdate($hash, 1);
- } else {
- CommandDeleteReading(undef, "$name msgLost");
- CommandDeleteReading(undef, "$name rssiMasterAvg");
- }
- return ($err, $logLevel, $response);
- }
- #
- sub EnOcean_energyManagement_01Parse($@)
- {
- my ($hash, @db) = @_;
- my $name = $hash->{NAME};
- # [drLevel] = 15 : no requests for reduction in power consumptions
- my $drLevel = ($db[0] & 0xF0) >> 4;
- my $powerUsage = $db[2] & 0x7F;
- my $powerUsageLevel = $db[0] & 1 ? "max" : "min";
- my $powerUsageScale = $db[2] & 0x80 ? "rel" : "max";
- my $randomStart = $db[0] & 4 ? "yes" : "no";
- my $randomEnd = $db[0] & 2 ? "yes" : "no";
- my $randomTime = rand(AttrVal($name, "demandRespRandomTime", 1));
- my $setpoint = $db[3];
- my $timeout = $db[1] * 15 * 60;
- $randomTime = $randomTime > $timeout && $timeout > 0 ? rand($timeout) : rand($randomTime);
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "randomEnd", $randomEnd);
- readingsBulkUpdate($hash, "randomStart", $randomStart);
- my %timeoutHash = (hash => $hash, function => "demandResponseTimeout");
- my %functionHash = (hash => $hash,
- function => "demandResponseExec",
- drLevel => $drLevel,
- powerUsage => $powerUsage,
- powerUsageLevel => $powerUsageLevel,
- powerUsageScale => $powerUsageScale,
- setpoint => $setpoint
- );
- RemoveInternalTimer(\%timeoutHash);
- RemoveInternalTimer(\%functionHash);
- if ($timeout > 0 && $drLevel < 15) {
- # timeout timer
- InternalTimer(gettimeofday() + $timeout, "EnOcean_demandResponseTimeout", \%timeoutHash, 0);
- my ($sec, $min, $hour, $day, $month, $year) = localtime(time + $timeout);
- $month += 1;
- $year += 1900;
- $min = $min < 10 ? $min = "0" . $min : $min;
- $hour = $hour < 10 ? $hour = "0" . $hour : $hour;
- $day = $day < 10 ? $day = "0" . $day : $day;
- $month = $month < 10 ? $ month = "0". $month : $month;
- readingsBulkUpdate($hash, "timeout", "$year-$month-$day $hour:$min:$sec");
- } else {
- CommandDeleteReading(undef, "$name timeout");
- }
- if ($randomStart eq "yes" && ReadingsVal($name, "level", 15) == 15) {
- readingsBulkUpdate($hash, "state", "waiting_for_start");
- Log3 $name, 3, "EnOcean set $name demand response waiting for start";
- InternalTimer(gettimeofday() + $randomTime, "EnOcean_demandResponseExec", \%functionHash, 0);
- } elsif ($randomEnd eq "yes" && ReadingsVal($name, "level", 15) < 15) {
- readingsBulkUpdate($hash, "state", "waiting_for_stop");
- Log3 $name, 3, "EnOcean set $name demand response waiting for stop";
- InternalTimer(gettimeofday() + $randomTime, "EnOcean_demandResponseExec", \%functionHash, 0);
- } else {
- EnOcean_demandResponseExec(\%functionHash);
- }
- readingsEndUpdate($hash, 1);
- return;
- }
- #
- sub EnOcean_demandResponseExec($)
- {
- my ($functionHash) = @_;
- my $function = $functionHash->{function};
- my $hash = $functionHash->{hash};
- my $drLevel = $functionHash->{drLevel};
- my $powerUsage = $functionHash->{powerUsage};
- my $powerUsageLevel = $functionHash->{powerUsageLevel};
- my $powerUsageScale = $functionHash->{powerUsageScale};
- my $setpoint = $functionHash->{setpoint};
- my $name = $hash->{NAME};
- my $actionCmd = AttrVal($name, "demandRespAction", undef);
- # save old values
- $hash->{helper}{drLevel} = ReadingsVal($name, "level", 15);
- $hash->{helper}{powerUsage} = ReadingsVal($name, "powerUsage", 100);
- $hash->{helper}{powerUsageLevel} = ReadingsVal($name, "powerUsageLevel", "max");
- $hash->{helper}{powerUsageScale} = ReadingsVal($name, "powerUsageScale", "max");
- $hash->{helper}{setpoint} = ReadingsVal($name, "setpoint", 255);
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "level", $drLevel);
- readingsBulkUpdate($hash, "powerUsage", $powerUsage);
- readingsBulkUpdate($hash, "powerUsageLevel", $powerUsageLevel);
- readingsBulkUpdate($hash, "powerUsageScale", $powerUsageScale);
- readingsBulkUpdate($hash, "setpoint", $setpoint);
- if ($drLevel == 15) {
- readingsBulkUpdate($hash, "state", "off");
- Log3 $name, 3, "EnOcean set $name demand response off";
- } else {
- readingsBulkUpdate($hash, "state", "on");
- Log3 $name, 3, "EnOcean set $name demand response on";
- }
- readingsEndUpdate($hash, 1);
- if (defined $actionCmd) {
- # action exec
- my %specials= ("%NAME" => $name,
- "%TYPE" => $hash->{TYPE},
- "%LEVEL" => $drLevel,
- "%SETPOINT" => $setpoint,
- "%POWERUSAGE" => $powerUsage,
- "%POWERUSAGESCALE" => $powerUsageScale,
- "%POWERUSAGELEVEL" => $powerUsageLevel,
- "%STATE" => ReadingsVal($name, "state", "off")
- );
- $actionCmd = EvalSpecials($actionCmd, %specials);
- my $ret = AnalyzeCommandChain(undef, $actionCmd);
- Log3 $name, 2, "EnOcean $name demandRespAction ERROR: $ret" if($ret);
- }
- return;
- }
- #
- sub EnOcean_demandResponseTimeout($)
- {
- my ($functionHash) = @_;
- my $function = $functionHash->{function};
- my $hash = $functionHash->{hash};
- my $name = $hash->{NAME};
- my $actionCmd = AttrVal($name, "demandRespAction", undef);
- my $data;
- my $timeoutLevel = AttrVal($name, "demandRespTimeoutLevel", "max");
- RemoveInternalTimer($functionHash);
- CommandDeleteReading(undef, "$name timeout");
- my $drLevel = 15;
- my $powerUsage = 100;
- my $powerUsageLevel = "max";
- my $powerUsageScale = "max";
- my $setpoint = 255;
- my $timeout = 0;
- if ($timeoutLevel eq "last" && defined($hash->{helper}{drLevel})) {
- # restore old values
- $drLevel = $hash->{helper}{drLevel};
- $powerUsage = $hash->{helper}{powerUsage};
- $powerUsageScale = $hash->{helper}{powerUsageLevel};
- $powerUsageLevel = $hash->{helper}{powerUsageScale};
- $setpoint = $hash->{helper}{setpoint};
- }
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "level", $drLevel);
- readingsBulkUpdate($hash, "powerUsage", $powerUsage);
- readingsBulkUpdate($hash, "powerUsageLevel", $powerUsageLevel);
- readingsBulkUpdate($hash, "powerUsageScale", $powerUsageScale);
- readingsBulkUpdate($hash, "setpoint", $setpoint);
- if ($drLevel == 15) {
- readingsBulkUpdate($hash, "state", "off");
- Log3 $name, 3, "EnOcean set $name demand response off";
- } else {
- readingsBulkUpdate($hash, "state", "on");
- Log3 $name, 3, "EnOcean set $name demand response on";
- }
- readingsEndUpdate($hash, 1);
- my %specials= ("%NAME" => $name,
- "%TYPE" => $hash->{TYPE},
- "%LEVEL" => $drLevel,
- "%SETPOINT" => $setpoint,
- "%POWERUSAGE" => $powerUsage,
- "%POWERUSAGESCALE" => $powerUsageScale,
- "%POWERUSAGELEVEL" => $powerUsageLevel,
- "%STATE" => ReadingsVal($name, "state", "off")
- );
- $powerUsageLevel = $powerUsageLevel eq "max" ? 1 : 0;
- $powerUsageScale = $powerUsageScale eq "rel" ? 0x80 : 0;
- my $randomStart = ReadingsVal($name, "randomStart", "no") eq "yes" ? 4 : 0;
- my $randomEnd = ReadingsVal($name, "randomEnd", "no") eq "yes" ? 2 : 0;
- $data = sprintf "%02X%02X%02X%02X", $setpoint, $powerUsageScale | $powerUsage, $timeout,
- $drLevel << 4 | $randomStart | $randomEnd | $powerUsageLevel | 8;
- EnOcean_SndRadio(undef, $hash, 1, "A5", $data, AttrVal($name, "subDef", $hash->{DEF}), "00", "FFFFFFFF");
- if (defined $actionCmd) {
- # action exec
- $actionCmd = EvalSpecials($actionCmd, %specials);
- my $ret = AnalyzeCommandChain(undef, $actionCmd);
- Log3 $name, 2, "EnOcean $name demandRespAction ERROR: $ret" if($ret);
- }
- return;
- }
- # Send UTE Teach-In Telegrams
- sub EnOcean_sndUTE($$$$$$$) {
- my ($ctrl, $hash, $comMode, $responseRequest, $teachInReq, $devChannel, $eep) = @_;
- my $name = $hash->{NAME};
- my ($err, $data) = (undef, "");
- my $IODev = $hash->{IODev}{NAME};
- my $IOHash = $defs{$IODev};
- my @db = (undef, undef, undef, "07", "FF", $devChannel);
- if ($eep =~ m/^(..)-(..)-(..)$/) {
- ($db[0], $db[1], $db[2]) = ($1, $2, $3);
- } else {
- return (1, undef, undef);
- }
- # set unidir/bidir operation
- $db[6] = $comMode eq "biDir" ? 0x80 : 0;
- $attr{$name}{comMode} = $comMode;
- # set teach mode
- if ($teachInReq eq "out") {
- $db[6] |= 0x10;
- } elsif ($teachInReq eq "inout") {
- $db[6] |= 0x20;
- }
- # set response message mode
- if ($responseRequest eq "no") {
- $db[6] |= 0x40;
- readingsSingleUpdate($hash, "teach", "EEP $eep UTE query sent", 1);
- } else {
- # set flag for response request,
- if ($teachInReq eq "in") {
- $hash->{IODev}{helper}{UTERespWait}{$hash->{DEF}}{teachInReq} = $teachInReq;
- $hash->{IODev}{helper}{UTERespWait}{$hash->{DEF}}{hash} = $hash;
- } elsif ($teachInReq eq "out") {
- $hash->{IODev}{helper}{UTERespWait}{AttrVal($name, "subDef", $hash->{DEF})}{teachInReq} = $teachInReq;
- $hash->{IODev}{helper}{UTERespWait}{AttrVal($name, "subDef", $hash->{DEF})}{hash} = $hash;
- } elsif ($teachInReq eq "inout") {
- $hash->{IODev}{helper}{UTERespWait}{$hash->{DEF}}{teachInReq} = $teachInReq;
- $hash->{IODev}{helper}{UTERespWait}{$hash->{DEF}}{hash} = $hash;
- }
- readingsSingleUpdate($hash, "teach", "EEP $eep UTE query sent, response requested", 1);
- if (!exists($hash->{IODev}{Teach})) {
- # enable teach-in receiving for 3 sec
- $hash->{IODev}{Teach} = 1;
- my %timeoutHash = (hash => $IOHash, function => "UTERespTimeout", helper => "UTERespWait");
- RemoveInternalTimer(\%timeoutHash);
- InternalTimer(gettimeofday() + 3, "EnOcean_RespTimeout", \%timeoutHash, 0);
- }
- }
- $attr{$name}{devChannel} = $devChannel;
- $attr{$name}{eep} = $eep;
- $attr{$name}{manufID} = "7FF";
- $data = sprintf "%02X%02X%s%s%s%s%s", $db[6], $db[5], $db[4], $db[3], $db[2], $db[1], $db[0];
- return ($err, "D4", $data);
- }
- #
- sub EnOcean_RespTimeout($) {
- my ($functionHash) = @_;
- my $function = $functionHash->{function};
- my $hash = $functionHash->{hash};
- my $helper = $functionHash->{helper};
- my $name = $hash->{NAME};
- delete $hash->{helper}{$helper};
- delete $hash->{Teach};
- return;
- }
- #
- sub EnOcean_setTeachConfirmWaitHash($) {
- my ($ctrl, $hash) = @_;
- if (AttrVal($hash->{NAME}, "teachMethod", "") eq 'confirm') {
- $hash->{IODev}{helper}{teachConfirmWaitHash} = $hash;
- my %functionHash = (hash => $hash->{IODev}, function => "teachConfirmWaitHash");
- RemoveInternalTimer(\%functionHash);
- InternalTimer(gettimeofday() + 5, "EnOcean_helperClear", \%functionHash, 0);
- }
- return;
- }
- #
- sub EnOcean_ReadDevDesc($$) {
- # read xml device description to $hash->{helper}
- my ($ctrl, $hash) = @_;
- my $name = $hash->{NAME};
- if ($xmlFunc == 0) {
- Log3 $name, 2, "EnOcean $name XML functions are not available";
- return;
- }
- if (exists($hash->{TYPE}) && $hash->{TYPE} eq 'EnOcean' && exists($attr{$name}{model})) {
- if (exists $EnO_models{$attr{$name}{model}}) {
- if (exists $EnO_models{$attr{$name}{model}}{xml}{xmlDescrLocation}) {
- my $xmlFile = $attr{global}{modpath} . $EnO_models{$attr{$name}{model}}{xml}{xmlDescrLocation};
- if (-e -f -r $xmlFile) {
- my $xmlData = $xml->XMLin($xmlFile);
- $hash->{helper} = $xmlData;
- if (exists $xmlData->{Device}) {
- Log3 $name, 5, "EnOcean $name Beginn Device Description";
- Log3 $name, 5, "###";
- Log3 $name, 5, Dumper($xmlData);
- Log3 $name, 5, "###";
- Log3 $name, 5, "EnOcean $name End Device Description";
- } else {
- Log3 $name, 2, "EnOcean $name Device Description not defined";
- }
- } else {
- Log3 $name, 2, "EnOcean $name Device Description file $xmlFile not exists";
- }
- }
- }
- }
- return;
- }
- #
- sub EnOcean_helperClear($) {
- my ($functionHash) = @_;
- my $function = $functionHash->{function};
- my $hash = $functionHash->{hash};
- delete $hash->{helper}{$function};
- return;
- }
- #
- sub EnOcean_cdmClearRemoteWait($) {
- my ($functionHash) = @_;
- my $hash = $functionHash->{hash};
- my $param = $functionHash->{param};
- delete $hash->{IODev}{helper}{remoteAnswerWait}{$param}{hash};
- #Log3 $hash->{NAME}, 3, "EnOcean $hash->{NAME} EnOcean_cdmClearRemoteWait executed.";
- return;
- }
- #
- sub EnOcean_cdmClearHashVal($) {
- my ($functionHash) = @_;
- my $hash = $functionHash->{hash};
- my $param = $functionHash->{param};
- delete $hash->{$param};
- #Log3 $hash->{NAME}, 3, "EnOcean $hash->{NAME} EnOcean_cdmClearHashVal executed.";
- return;
- }
- #
- sub EnOcean_CommandDelete($) {
- my ($functionHash) = @_;
- my $deleteDevice = $functionHash->{deleteDevice};
- my $function = $functionHash->{function};
- my $hash = $functionHash->{hash};
- my $name = $hash->{NAME};
- my $oldDevice = $functionHash->{oldDevice};
- CommandDelete(undef, $deleteDevice);
- if (defined $oldDevice) {
- Log3 $name, 2, "EnOcean $name: $oldDevice renamed to $deleteDevice";
- CommandRename(undef, "$oldDevice $deleteDevice");
- CommandSave(undef, undef);
- } else {
- Log3 $name, 2, "EnOcean $name: $deleteDevice deleted";
- CommandSave(undef, undef);
- }
- return;
- }
- #
- sub EnOcean_convBitToHex($) {
- # convert bit string to hex string
- my ($bitStr) = @_;
- my $hexStr = '';
- while(length($bitStr) > 0) {
- $bitStr =~ m/^(.*)(.{8})$/;
- $bitStr = $1;
- $hexStr = unpack('H2', pack('B8', $2)) . $hexStr;
- }
- return uc($hexStr);
- }
- #
- sub EnOcean_convHexToBit($) {
- # convert unsign hex string to bit string
- my ($hexstr) = @_;
- my $bitstr = '';
- while (length($hexstr) > 0) {
- $hexstr =~ m/^(.*)(..)$/;
- $hexstr = $1;
- $bitstr = unpack('B8', pack('H2', $2)) . $bitstr;
- }
- return $bitstr;
- }
- #
- sub EnOcean_convIntToBit($$) {
- # convert unsign number to bitstring
- my ($data, $resolution) = @_;
- Log3 undef, 3, "EnOcean EnOcean_convIntToBitstr input: $data";
- if ($data > 0xFFFF) {
- # unsigned long (32 bit)
- Log3 undef, 3, "EnOcean EnOcean_convIntToBitstr pack L: " . pack('L', $data);
- #$data = unpack('B32', pack('L', $data));
- $data = unpack('B32', $data);
- } elsif ($data > 0xFF) {
- # unsigned short (16 bit)
- Log3 undef, 3, "EnOcean EnOcean_convIntToBitstr pack S: " . pack('S', $data);
- #$data = '0' x 16 . unpack('B16', pack('S', $data));
- $data = '0' x 16 . unpack('B16', $data);
- } else {
- # unsigned char (8 bit)
- Log3 undef, 3, "EnOcean EnOcean_convIntToBitstr pack C: " . pack('C', $data);
- $data = '0' x 24 . unpack('B8', pack('C', $data));
- }
- Log3 undef, 3, "EnOcean EnOcean_convIntToBitstr pack B32: " . $data;
- $data = substr($data, 32 - $resolution);
- return $data;
- }
- sub EnOcean_gpConvSelDataToSndData($$$$) {
- # Generic Profiles, make selective data in hex
- my ($header, $channel, $resolution, $data) = @_;
- my $resolutionPattern = '%04B%06B%0' . $resolution . 'B';
- $data = sprintf "$resolutionPattern", $header, $channel, $data;
- if (($resolution + 10) % 8) {
- # fill with trailing zeroes to x bytes
- $data = $data . '0' x (8 - (10 + $resolution) % 8);
- }
- #Log3 undef, 3, "EnOcean EnOcean_gpConvSelDataToSndData header: $header channel: $channel data: $data";
- $data = EnOcean_convBitToHex($data);
- #Log3 undef, 3, "EnOcean EnOcean_gpConvSelDataToSndData header: $header channel: $channel data: $data";
- return $data;
- }
- #
- sub EnOcean_gpConvDataToValue($$$$$) {
- # Generic Profiles, convert data to value
- my ($ctrl, $hash, $channel, $data, $dataDescr) = @_;
- my $name = $hash->{NAME};
- my ($err, $logLevel, $msg, $readingFormat, $readingType, $readingUnit, $readingValue, $valueType) =
- (undef, 5, 'ok', '%d', 'data', 'N/A', $data, 'value');
- my @channelTypeList = ("teachIn", "data", "flag", "enum");
- my @signalTypeList;
- my @valueTypeList = ("res", "value", "setpointAbs", "setpointRel");
- # extract channel definition
- my ($channelName, $channelDir, $channelType, $signalType, $resolution, $engMin, $scalingMin, $engMax, $scalingMax);
- ($channelName, $channelDir, $channelType, $signalType, $valueType, $resolution, $engMin, $scalingMin, $engMax, $scalingMax) =
- split(':', $dataDescr);
- my $readingName = sprintf('%02d', $channel) . '-' . $channelName;
- $readingType = $channelTypeList[$channelType];
- $valueType = $valueTypeList[$valueType];
- if ($channelType == 3) {
- # enumeration
- if (defined $EnO_gpValueEnum{$signalType}{enum}{$data}) {
- $readingValue = $EnO_gpValueEnum{$signalType}{enum}{$data};
- $readingFormat = '%s';
- }
- } elsif ($channelType == 2) {
- # flag
- if (defined $EnO_gpValueFlag{$signalType}{flag}{$data}) {
- $readingValue = $EnO_gpValueFlag{$signalType}{flag}{$data};
- $readingFormat = '%s';
- }
- } elsif ($channelType == 1) {
- # data
- if (defined $EnO_gpValueData{$signalType}{unit}) {
- $readingUnit = $EnO_gpValueData{$signalType}{unit};
- }
- my @decimalDigits = (0, 1, 1, 2, 2, 2, 3, 4 , 4, 5, 7, 8, 10);
- $readingValue = $data / 2**$EnO_resolution[$resolution] *
- ($engMax * $EnO_scaling[$scalingMax] - $engMin * $EnO_scaling[$scalingMin]) + $engMin * $EnO_scaling[$scalingMin];
- if ($readingValue =~ m/^[+-]?\d+$/) {
- } elsif ($readingValue < 1000) {
- $readingFormat = '%0.' . ($decimalDigits[$resolution] - 1) . 'f';
- } else {
- $readingFormat = '%.' . ($decimalDigits[$resolution] - 1) . 'g';
- }
- } else {
- # teach-in info ... not used
- my @valueList = ("res", "channelsDescription", "productID");
- if (defined $valueList[$signalType]) {
- $readingValue = $valueList[$signalType];
- $readingFormat = '%s';
- }
- }
- return ($err, $logLevel, $msg, $readingFormat, $readingName, $readingType, $readingUnit, $readingValue, $valueType);
- }
- # Parse Secure Teach-In Telegrams
- sub EnOcean_sec_parseTeachIn($$$$) {
- my ($hash, $telegram, $subDef, $destinationID) = @_;
- my $name = $hash->{NAME};
- my ($err, $response, $logLevel);
- my $rlc; # Rolling code
- my $key1; # First part of private key
- my $key2; # Second part of private key
- # Extract byte fields from telegram
- # TEACH_IN_INFO, SLF, RLC/KEY/variable
- $telegram =~ /^(..)(..)(.*)/; # TODO Parse error handling?
- my $teach_bin = unpack('B8',pack('H2', $1)); # Parse as ASCII HEX, unpack to bitstring
- my $slf_bin = unpack('B8',pack('H2', $2)); # Parse as ASCII HEX, unpack to bitstring
- my $crypt = $3;
- # Extract bit fields from teach-in info field
- # IDX, CNT, PSK, TYPE, INFO
- $teach_bin =~ /(..)(..)(.)(.)(..)/; # TODO Parse error handling?
- my $idx = unpack('C',pack('B8', '000000'.$1)); # Padd to byte, parse as unsigned char
- my $cnt = unpack('C',pack('B8', '000000'.$2)); # Padd to byte, parse as unsigned char
- my $psk = $3;
- my $type = $4;
- my $info = unpack('C',pack('B8', '000000'.$5)); # Padd to byte, parse as unsigned char
- # Extract bit fields from SLF field
- # RLC_ALGO, RLC_TX, MAC_ALGO, DATA_ENC
- $slf_bin =~ /(..)(.)(..)(...)/; # TODO Parse error handling?
- my $rlc_algo = unpack('C',pack('B8', '000000'.$1)); # Padd to byte, parse as unsigned char
- my $rlc_tx = $2;
- my $mac_algo = unpack('C',pack('B8', '000000'.$3)); # Padd to byte, parse as unsigned char
- my $data_enc = unpack('C',pack('B8', '00000'.$4)); # Padd to byte, parse as unsigned char
- # The teach-in information is split in two telegrams due to the ERP1 limitations on telegram length
- # So we should get a telegram with index 0 and count 2 with the first half of the infos needed
- if ($idx == 0 && $cnt == 2) {
- # First part of the teach in message
- # Decode teach in type
- if ($type == 0) {
- # 1BS, 4BS, UTE or GP teach-in expected
- if ($info == 0) {
- $attr{$name}{comMode} = "uniDir";
- $attr{$name}{secMode} = "rcv";
- } else {
- $attr{$name}{comMode} = "biDir";
- $attr{$name}{secMode} = "biDir";
- }
- $hash->{helper}{teachInWait} = "STE";
- } else {
- # switch teach-in
- $attr{$name}{teachMethod} = 'STE';
- if ($info == 0) {
- $attr{$name}{comMode} = "uniDir";
- $attr{$name}{eep} = "D2-03-00";
- $attr{$name}{manufID} = "7FF";
- $attr{$name}{secMode} = "rcv";
- foreach my $attrCntr (keys %{$EnO_eepConfig{"D2.03.00"}{attr}}) {
- $attr{$name}{$attrCntr} = $EnO_eepConfig{"D2.03.00"}{attr}{$attrCntr};
- }
- readingsSingleUpdate($hash, "teach", "STE teach-in accepted EEP D2-03-00 Manufacturer: " . $EnO_manuf{"7FF"}, 1);
- Log3 $name, 2, "EnOcean $name STE teach-in accepted EEP D2-03-00 Rocker A Manufacturer: " . $EnO_manuf{"7FF"};
- } else {
- $attr{$name}{comMode} = "uniDir";
- $attr{$name}{eep} = "D2-03-00";
- $attr{$name}{manufID} = "7FF";
- $attr{$name}{secMode} = "rcv";
- foreach my $attrCntr (keys %{$EnO_eepConfig{"D2.03.00"}{attr}}) {
- $attr{$name}{$attrCntr} = $EnO_eepConfig{"D2.03.00"}{attr}{$attrCntr};
- }
- readingsSingleUpdate($hash, "teach", "STE teach-in accepted EEP D2-03-00 Manufacturer: " . $EnO_manuf{"7FF"}, 1);
- Log3 $name, 2, "EnOcean $name STE teach-in accepted EEP D2-03-00 Rocker B Manufacturer: " . $EnO_manuf{"7FF"};
- }
- }
- # Decode RLC algorithm and extract RLC and private key (only first part most likely)
- if ($rlc_algo == 0) {
- # No RLC used in telegram or internally in memory, use case untested
- return ("Secure modes without RLC not tested or supported", undef);
- } elsif ($rlc_algo == 1) {
- # "RLC= 2-byte long. RLC algorithm consists on incrementing in +1 the previous RLC value
- # Extract RLC and KEY fields from data trailing SLF field
- # RLC, KEY, ID, STATUS
- $crypt =~ /^(....)(.*)$/;
- $rlc = $1;
- $key1 = $2;
- #print "RLC: $rlc\n";
- #print "Part 1 of KEY: $key1\n";
- # Store in device hash
- $attr{$name}{rlcAlgo} = '2++';
- readingsSingleUpdate($hash, ".rlcRcv", $rlc, 0);
- # storing backup copy
- $attr{$name}{rlcRcv} = $rlc;
- $attr{$name}{keyRcv} = $key1;
- } elsif ($rlc_algo == 2) {
- # RLC= 3-byte long. RLC algorithm consists on incrementing in +1 the previous RLC value
- # Extract RLC and KEY fields from data trailing SLF field
- # RLC, KEY, ID, STATUS
- $crypt =~ /^(......)(.*)$/;
- $rlc = $1;
- $key1 = $2;
- #print "RLC: $rlc\n";
- #print "Part 1 of KEY: $key1\n";
- # Store in device hash
- $attr{$name}{rlcAlgo} = '3++';
- readingsSingleUpdate($hash, ".rlcRcv", $rlc, 0);
- # storing backup copy
- $attr{$name}{rlcRcv} = $rlc;
- $attr{$name}{keyRcv} = $key1;
- } else {
- # Undefined RLC algorithm
- return ("Undefined RLC algorithm $rlc_algo", undef);
- }
- # RLC Transmission
- if ($rlc_tx == 0 ) {
- # Secure operation mode telegrams do not contain RLC, we store and track it ourself
- $attr{$name}{rlcTX} = 'false';
- } else {
- # Secure operation mode messages contain RLC, CAUTION untested
- $attr{$name}{rlcTX} = 'true';
- }
- # Decode MAC Algorithm
- if ($mac_algo == 0) {
- # No MAC included in the secure telegram
- # Doesn't make sense for RLC senders like the PTM215, as we can't verify the RLC then...
- #$attr{$name}{macAlgo} = 'no';
- return ("Secure mode without MAC algorithm unsupported", undef);
- } elsif ($mac_algo == 1) {
- # CMAC is a 3-byte-long code
- $attr{$name}{macAlgo} = '3';
- } elsif ($mac_algo == 2) {
- # MAC is a 4-byte-long code
- $attr{$name}{macAlgo} = '4';
- } else {
- # Undefined MAC algorith;
- # Nothing we can do either...
- #$attr{$name}{macAlgo} = 'no';
- return ("Undefined MAC algorithm $mac_algo", undef);
- }
- # Decode data encryption algorithm
- if ($data_enc == 0) {
- # Data not encrypted? Right now we will handle this like an error, concrete use case untested
- #$attr{$name}{secLevel} = 'encapsulation';
- return ("Secure mode message without data encryption unsupported", undef);
- } elsif ($data_enc == 1) {
- # Unspecified
- return ("Undefined data encryption algorithm $data_enc", undef);
- } elsif ($data_enc == 2) {
- # Unspecified
- return ("Undefined data encryption algorithm $data_enc", undef);
- } elsif ($data_enc == 3) {
- # Data will be encrypted/decrypted XORing with a string obtained from a AES128 encryption
- $attr{$name}{dataEnc} = 'VAES';
- $attr{$name}{secLevel} = 'encryption';
- } elsif ($data_enc == 4) {
- # Data will be encrypted/decrypted using the AES128 algorithm in CBC mode
- # Might be used in the future right now untested
- #$attr{$name}{dataEnc} = 'AES-CBC';
- #$attr{$name}{secLevel} = 'encryption';
- return ("Secure mode message with AES-CBC data encryption unsupported", undef);
- } else {
- # Something went horribly wrong
- return ("Could not parse data encryption information, $data_enc", undef);
- }
- $hash->{helper}{teachInSTE} = $cnt - 1;
- # Ok we got a lots of infos and the first part of the private key
- return (undef, "part1: $name");
- } elsif ($idx == 1 && exists($hash->{helper}{teachInSTE})) {
- # Second part of the teach-in telegrams
- # Extract byte fields from telegram
- # Don't care about info fields, KEY, ID, don't care about status
- $telegram =~ /^..(.*)$/; # TODO Parse error handling?
- $key2 = $1;
- # We already should have gathered the infos from the first teach-in telegram
- if (!defined($attr{$name}{keyRcv})) {
- # We have missed the first telegram
- return ("Missing first teach-in telegram", undef);
- }
- # Append second part of private key to first part of private key
- $attr{$name}{keyRcv} .= $key2;
- if ($attr{$name}{secMode} eq "biDir") {
- # bidirectional secure teach-in
- if (!defined $subDef) {
- $subDef = EnOcean_CheckSenderID("getNextID", $defs{$name}{IODev}{NAME}, "00000000");
- $attr{$name}{subDef} = $subDef;
- }
- ($err, $response, $logLevel) = EnOcean_sec_createTeachIn(undef, $hash, $attr{$name}{comMode},
- $attr{$name}{dataEnc}, $attr{$name}{eep},
- $attr{$name}{macAlgo}, $attr{$name}{rlcAlgo},
- $attr{$name}{rlcTX}, $attr{$name}{secLevel},
- $subDef, $destinationID);
- if ($err) {
- Log3 $name, $logLevel, "EnOcean $name Error: $err";
- return $err;
- } else {
- Log3 $name, $logLevel, "EnOcean $name $response";
- }
- }
- delete $hash->{helper}{teachInSTE};
- return (undef, "part2: $name");
- }
- # Sequence error?
- return ("teach-in sequence error IDC: $idx CNT: $cnt", undef);
- }
- # Do VAES decyrption
- # All parameters need to be passed as byte strings
- #
- # Parameter 1: Current rolling code, 16 bytes
- # Parameter 2: Private key, 16bytes
- # Paremeter 3: Encrypted data, 16bytes
- #
- # Returns: Decrypted data, 16 bytes
- #
- # Decryption of more than 16bytes of data is currently unsupported
- #
- sub EnOcean_sec_decodeVAES($$$) {
- my $rlc = $_[0];
- my $private_key = $_[1];
- my $data_enc = $_[2];
- # Public key according to EnOcean Security specification
- my $public_key = pack('H32', '3410de8f1aba3eff9f5a117172eacabd');
- # Input for VAES
- my $aes_in = $public_key ^ $rlc;
- #print "--\n";
- #print "Public Key ".unpack('H32', $public_key)."\n";
- #print "RLC ".unpack('H32', $rlc)."\n";
- #print "AES input ".unpack('H32', $aes_in)."\n";
- #print "--\n";
- #print "Private Key ".unpack('H32', $private_key)."\n";
- my $cipher = Crypt::Rijndael->new( $private_key );
- my $aes_out = $cipher->encrypt($aes_in);
- #print "AES output ".unpack('H32', $aes_out)."\n";
- #print "Data_enc: ".unpack('H32', $data_enc)."\n";
- my $data_dec = $data_enc ^ $aes_out;
- #print "Data_dec: ".unpack('H32', $data_dec)."\n";
- return $data_dec;
- }
- # Returns current RLC in hex format and increments the stored RLC
- # Checks the boundaries of the RLC for roll-over
- #
- # Parameter 1: Sender ID in hexadecimal format for lookup in receivers hash
- #
- # Affects: receivers hash
- #
- # Returns: RLC in hexadecimal format
- #
- sub EnOcean_sec_getRLC($$) {
- my $hash = $_[0];
- my $rlcVar = $_[1];
- my $name = $hash->{NAME};
- # Fetch newest RLC from receiver hash
- my $old_rlc = ReadingsVal($name, "." . $rlcVar, $attr{$name}{$rlcVar});
- if (hex($old_rlc) < hex($attr{$name}{$rlcVar})) {
- $old_rlc = $attr{$name}{$rlcVar};
- }
- #print "RLC old: $old_rlc\n";
- Log3 $name, 5, "EnOcean $name EnOcean_sec_getRLC RLC old: $old_rlc";
- # Advance RLC by one
- my $new_rlc = hex($old_rlc) + 1;
- # Boundary check
- if ($attr{$name}{rlcAlgo} eq '2++') {
- if ($new_rlc > 65535) {
- #print "RLC rollover\n";
- Log3 $name, 5, "EnOcean $name EnOcean_sec_getRLC RLC rollover";
- $new_rlc = 0;
- $attr{$name}{$rlcVar} = "0000";
- EnOcean_CommandSave(undef, undef);
- }
- readingsSingleUpdate($hash, "." . $rlcVar, uc(unpack('H4',pack('n', $new_rlc))), 0);
- $attr{$name}{$rlcVar} = uc(unpack('H4',pack('n', $new_rlc)));
- } elsif ($attr{$name}{rlcAlgo} eq '3++') {
- if ($new_rlc > 16777215) {
- #print "RLC rollover\n";
- Log3 $name, 5, "EnOcean $name EnOcean_sec_getRLC RLC rollover";
- $new_rlc = 0;
- $attr{$name}{$rlcVar} = "000000";
- EnOcean_CommandSave(undef, undef);
- }
- readingsSingleUpdate($hash, "." . $rlcVar, uc(unpack('H6',pack('N', $new_rlc))), 0);
- $attr{$name}{$rlcVar} = uc(unpack('H6',pack('N', $new_rlc)));
- }
- #print "RLC new: ".$attr{$name}{rlc}."\n";
- Log3 $name, 5, "EnOcean $name EnOcean_sec_getRLC RLC new: ". $attr{$name}{$rlcVar};
- return $old_rlc;
- }
- # Generate MAC of data
- #
- # Parameter 1: private key as byte string, 16bytes
- # Parameter 2: data fro which mac should be calculated in hexadecimal format, len variable
- # Parameter 3: length of MAC to be generated in bytes
- #
- # Returns: MAC in hexadecimal format
- #
- # This function currently supports data with lentgh of less then 16bytes,
- # MAC for longer data is untested but specified
- #
- sub EnOcean_sec_generateMAC($$$) {
- my $private_key = $_[0];
- my $data = $_[1];
- my $cmac_len = $_[2];
- #print "Calculating MAC for data $data\n";
- Log3 undef, 5, "EnOcean_sec_generateMAC Calculating MAC for data $data";
- Log3 undef, 5, "EnOcean_sec_generateMAC private key ".unpack('H32', $private_key);
- # Pack data to 16byte byte string, padd with 10..0 binary
- my $data_expanded = pack('H32', $data.'80');
- #print "Exp. data ".unpack('H32', $data_expanded)."\n";
- # Constants according to specification
- my $const_zero = pack('H32','00');
- my $const_rb = pack('H32', '00000000000000000000000000000087');
- # Encrypt zero data with private key to get L
- my $cipher = Crypt::Rijndael->new($private_key);
- my $l = $cipher->encrypt($const_zero);
- #print "L ".unpack('H32', $l)."\n";
- #print "L ".unpack('B128', $l)."\n";
- # Expand L to 128bit string
- my $l_bit = unpack('B128', $l);
- # K1 and K2 stored as 128bit string
- my $k1_bit;
- my $k2_bit;
- # K1 and K2 as binary
- my $k1;
- my $k2;
- # Store L << 1 in K1
- $l_bit =~ /^.(.{127})/;
- $k1_bit = $1.'0';
- $k1 = pack('B128', $k1_bit);
- # If MSB of L == 1, K1 = K1 XOR const_Rb
- if($l_bit =~ m/^1/) {
- #print "MSB of L is set\n";
- $k1 = $k1 ^ $const_rb;
- $k1_bit = unpack('B128', $k1);
- } else {
- #print "MSB of L is unset\n";
- }
- # Store K1 << 1 in K2
- $k1_bit =~ /^.(.{127})/;
- $k2_bit = $1.'0';
- $k2 = pack('B128', $k2_bit);
- # If MSB of K1 == 1, K2 = K2 XOR const_Rb
- if($k1_bit =~ m/^1/) {
- #print "MSB of K1 is set\n";
- $k2 = $k2 ^ $const_rb;
- } else {
- #print "MSB of K1 is unset\n";
- }
- # XOR data with K2
- $data_expanded ^= $k2;
- # Encrypt data
- my $cmac = $cipher->encrypt($data_expanded);
- #print "CMAC ".unpack('H32', $cmac)."\n";
- Log3 undef, 5, "EnOcean_sec_generateMAC CMAC ".unpack('H32', $cmac);
- # Extract specified len of MAC
- my $cmac_pattern = '^(.{'.($cmac_len * 2).'})';
- unpack('H32', $cmac) =~ /$cmac_pattern/;
- Log3 undef, 5, "EnOcean_sec_generateMAC cutted CMAC ".unpack('H32', $1);
- # Return MAC in hexadecimal format
- return uc($1);
- }
- # Verify (MAC) and decode/decrypt secure mode message
- #
- # Parameter 1: content of radio telegram in hexadecimal format
- #
- # Returns: "ERROR-" + error description, "OK-" + EEP D2-03-00 telegram in hexadecimal format
- #
- # Right now we only decode PTM215 telegrams which are transmitted as RORG 30 and without
- # encapsulation. Encapsulation of other telegrams is possible and specified but untested due to the
- # lack of hardware suporting this.
- #
- sub EnOcean_sec_convertToNonsecure($$$) {
- my ($hash, $rorg, $crypt_data) = @_;
- my $name = $hash->{NAME};
- if ($cryptFunc == 0) {
- return ("Cryptographic functions are not available", undef, undef);
- }
- my $private_key;
- # Prefix of pattern to extract the different cryptographic infos
- my $crypt_pattern = "^(.*)";;
- # Flags and infos for fields to expect
- my $expect_rlc = 0;
- my $expect_mac = 0;
- my $mac_len;
- my $expect_enc = 0;
- # Check if RLC is transmitted and when, which length to expect
- if($attr{$name}{rlcTX} eq 'true') {
- # Message should contain RLC
- if ($attr{$name}{rlcAlgo} eq '2++') {
- $crypt_pattern .= "(....)";
- $expect_rlc = 1;
- } elsif ($attr{$name}{rlcAlgo} eq '3++') {
- $crypt_pattern .= "(......)";
- $expect_rlc = 1;
- } else {
- # RLC_TX but no info on RLC length
- return ("RLC_TX and RLC_ALGO inconsistent", undef, undef);
- }
- }
- # Check what length of MAC to expect
- if($attr{$name}{macAlgo} eq '3') {
- $crypt_pattern .= "(......)";
- $mac_len = 3;
- $expect_mac = 1;
- } elsif ($attr{$name}{macAlgo} eq '4') {
- $crypt_pattern .= "(........)";
- $mac_len = 4;
- $expect_mac = 1;
- } else {
- # According to the specification it's possible to transmit no MAC, bt we don't implement this for now
- return ("Secure mode messages without MAC unsupported", undef, undef);
- }
- # Suffix for crypt pattern
- $crypt_pattern .= '$';
- # Extract byte fields from message payload
- $crypt_data =~ /$crypt_pattern/;
- my $data_enc = $1;
- my $dataLength = length($data_enc);
- return ("Telegrams with a length of more than 16 bytes are not supported", undef, undef) if ($dataLength > 32);
- my $rlc;
- my $mac;
- if ($expect_rlc == 1 && $expect_mac == 1) {
- $rlc = $2;
- $mac = $3;
- } elsif ($expect_rlc == 0 && $expect_mac == 1) {
- $mac = $2;
- }
- Log3 $name, 5, "EnOcean $name EnOcean_sec_convertToNonsecure RORG: $rorg DATA_ENC: $data_enc";
- if ($expect_rlc == 1) {
- Log3 $name, 5, "EnOcean $name EnOcean_sec_convertToNonsecure RLC: $rlc";
- };
- Log3 $name, 5, "EnOcean $name EnOcean_sec_convertToNonsecure MAC: $mac";
- # TODO RLC could be transmitted with data, could not test this
- #if(!defined($rlc)) {
- # print "No RLC in message, using stored value\n";
- # $rlc = getRLC($senderID);
- #}
- # Maximum RLC search window is 128
- foreach my $rlc_window (0..128) {
- #print "Trying RLC offset $rlc_window\n";
- # Fetch stored RLC
- $rlc = EnOcean_sec_getRLC($hash, "rlcRcv");
- # Fetch private Key for VAES
- if ($attr{$name}{keyRcv} =~ /[\dA-F]{32}/) {
- $private_key = pack('H32',$attr{$name}{keyRcv});
- } else {
- return ("private key wrong, please teach-in the device new", undef, undef);
- }
- # Generate and check MAC over RORG+DATA+RLC fields
- if ($mac eq EnOcean_sec_generateMAC($private_key, $rorg.$data_enc.$rlc, $mac_len)) {
- #print "RLC verfified\n";
- # Expand RLC to 16byte
- my $rlc_expanded = pack('H32',$rlc);
- # Expand data to 16byte
- my $data_expanded = pack('H32',$data_enc);
- # Decode data using VAES
- my $data_dec = EnOcean_sec_decodeVAES($rlc_expanded, $private_key, $data_expanded);
- my $data_end = unpack('H32', $data_dec);
- if ($rorg eq '30') {
- # Extract one nibble of data
- $data_end =~ /^.(.)/;
- return (undef, '32', "0" . uc($1));
- } else {
- $dataLength -= 2;
- $data_end =~ /^(..)(.{$dataLength})/;
- Log3 $name, 5, "EnOcean $name EnOcean_sec_convertToNonsecure RORG: " . uc($1) . " DATA: " . uc($2);
- return (undef, uc($1), uc($2));
- }
- }
- }
- # Couldn't verify or decrypt message in RLC window
- return ("Can't verify or decrypt telegram", undef, undef);
- }
- #
- sub EnOcean_sec_createTeachIn($$$$$$$$$$$)
- {
- my ($ctrl, $hash, $comMode, $dataEnc, $eep, $macAlgo, $rlcAlgo, $rlcTX, $secLevel, $subDef, $destinationID) = @_;
- my $name = $hash->{NAME};
- my ($data, $err, $response, $loglevel);
- # THIS IS A BASIC IMPLEMENTATION WITH HARDCODED VALUES FOR
- # THE SECURITY PARAMETERS, WILL BE CUSTOMIZABLE IN FUTURE
- if ($cryptFunc == 0) {
- return ("Cryptographic functions are not available", undef, 2);
- }
- # generate random private key
- my $pKey;
- for (my $i = 1; $i < 5; $i++) {
- $pKey .= uc(unpack('H8', pack('L', makerandom(Size => 32, Strength => 1))));
- }
- $attr{$name}{keySnd} = AttrVal($name, "keySnd", $pKey);
- #generate random rlc, save to fhem.cfg and update readings
- my $rlc = ReadingsVal($name, ".rlcSnd", uc(unpack('H4', pack('n', makerandom(Size => 16, Strength => 1)))));
- readingsSingleUpdate($hash, ".rlcSnd", $rlc, 0);
- $attr{$name}{rlcSnd} = $rlc;
- $attr{$name}{comMode} = AttrVal($name, "comMode", $comMode);
- $attr{$name}{dataEnc} = AttrVal($name, "dataEnc", $dataEnc);
- $attr{$name}{eep} = $eep;
- $attr{$name}{macAlgo} = AttrVal($name, "macAlgo", $macAlgo);
- $attr{$name}{manufID} = "7FF";
- $attr{$name}{rlcAlgo} = AttrVal($name, "rlcAlgo", $rlcAlgo);
- $attr{$name}{rlcTX} = AttrVal($name, "rlcTX", $rlcTX);
- $attr{$name}{secLevel} = AttrVal($name, "secLevel", $secLevel);
- if (AttrVal($name, "secMode", "") =~ m/^rcv|bidir$/) {
- $attr{$name}{secMode} = "biDir";
- } else {
- $attr{$name}{secMode} = "snd";
- }
- # prepare 1st telegram
- #RORG = 35, TEACH_IN_INFO_0, SLF, RLC, KEY, ID, STATUS as defined in Security_of_EnOcean_Radio_Networks.pdf page 17
- #set TEACH_IN_INFO = 25 -> 0001.0101 -> IDX =0, CNT = 2, PSK = 0, TYPE = 1, INFO = 1
- #set SLF = 4B -> 0100.1011 -> RLC_ALGO=16bit, RLC-TX=0, MAC-ALGO = AES3BYTE, DATA_ENC = VAES128
- #save the fixed security parameters to fhem.cfg
- #get first 5 bytes of private key
- #data 1: 25 4B r1 r2 k1 k2 k3 k4 k5
- $data = "254B" . $rlc . substr($attr{$name}{keySnd}, 0, 5*2);
- EnOcean_SndRadio(undef, $hash, 1, "35", $data, $subDef, "00", $destinationID);
- # prepare 2nd telegram
- #RORG = 35, TEACH_IN_INFO_1, KEY, ID, STATUS as defined in Security_of_EnOcean_Radio_Networks.pdf page 17
- #set TEACH_IN_INFO = 40 -> 0100.0000 -> IDX =1, CNT = 0, PSK = 0, TYPE = 0, INFO = 0
- #get 2nd 11 bytes of private key
- #data 2: 40 k6 k7 k8 k9 k10 k11 k12 k13 k14 k15 k16
- $data = "40" . substr($attr{$name}{keySnd}, 10, 11*2);
- EnOcean_SndRadio(undef, $hash, 1, "35", $data, $subDef, "00", $destinationID);
- return (undef, "secure teach-in", 2);
- }
- #
- sub EnOcean_sec_convertToSecure($$$$)
- {
- my ($hash, $packetType, $rorg, $data) = @_;
- my ($err, $response, $loglevel);
- my $name = $hash->{NAME};
- my $secLevel = AttrVal($name, "secLevel", "off");
- # encryption needed?
- return ($err, $rorg, $data, $response, 5) if ($rorg =~ m/^F6|35$/ || $secLevel !~ m/^encapsulation|encryption$/);
- return ("Cryptographic functions are not available", undef, undef, $response, 2) if ($cryptFunc == 0);
- my $dataEnc = AttrVal($name, "dataEnc", undef);
- my $subType = AttrVal($name, "subType", "");
- # subType specific actions
- if ($subType eq "switch.00" || $subType eq "windowHandle.10") {
- # securemode for D2-03-00 and D2-03-10
- if (hex($data) > 15) {
- return("wrong data byte", $rorg, $data, $response, 2);
- }
- # set rorg to secure telegram
- $rorg = "30";
- } else {
- return("Cryptographic functions for $subType not available", $rorg, $data, $response, 2);
- }
- #Get and update RLC
- my $rlc = EnOcean_sec_getRLC($hash, "rlcSnd");
- #Log3 $hash->{NAME}, 5, "EnOcean_sec_convertToSecure: Got actual RLC: $rlc";
- #Get key of device
- my $pKey = AttrVal($name, "keySnd", undef);
- return("private key not defined", $rorg, $data, $response, 2) if (!defined $pKey);
- $pKey = pack('H32', $pKey);
- #Log3 $hash->{NAME}, 5, "EnOcean_sec_convertToSecure: key: " . AttrVal($name, "keySnd", "");
- #prepare data
- my $rlc_expanded = pack('H32', $rlc);
- my $data_expanded = pack('H32', $data);
- my $data_dec;
- if ($dataEnc eq "VAES") {
- $data_dec = EnOcean_sec_decodeVAES($rlc_expanded, $pKey, $data_expanded);
- } else {
- return("Cryptographic functions not available", $rorg, $data, $response, 2);
- }
- my $data_end = unpack('H32', $data_dec);
- #get the correct nibble
- $data_end =~ /^.(.)/;
- $data_end = uc("0$1");
- #Log3 $hash->{NAME}, 5, "EnOcean_sec_convertToSecure: Crypted Data: $data_end";
- # calc MAC
- my $macAlgo = AttrVal($name, "macAlgo", undef);
- return("MAC Algorithm not defined", $rorg, $data, $response, 2) if (!defined $macAlgo);
- my $mac = EnOcean_sec_generateMAC($pKey, $rorg . $data_end . $rlc, $macAlgo);
- # combine message
- $data = $data_end . uc($mac);
- #Log3 $hash->{NAME}, 5, "EnOcean_sec_convertToSecure: Crypted Payload: $data";
- return(undef, $rorg, $data, $response, 5);
- }
- #
- sub
- EnOcean_NumericSort
- {
- if ($a < $b) {
- return -1;
- } elsif ($a == $b) {
- return 0;
- } else {
- return 1;
- }
- }
- sub EnOcean_TimeDiff($)
- {
- my ($strTS) = @_;
- if (defined $strTS) {
- my $timeDiff = gettimeofday() - ($strTS eq "" ? gettimeofday() : time_str2num($strTS));
- $timeDiff = 0 if ($timeDiff < 0);
- return $timeDiff;
- } else {
- return 0;
- }
- }
- # Undef
- sub
- EnOcean_Undef($$)
- {
- my ($hash, $name) = @_;
- delete $hash->{helper};
- delete $modules{EnOcean}{defptr}{uc($hash->{DEF})};
- delete $modules{EnOcean}{defptr}{uc($attr{$name}{remoteID})} if (exists $attr{$name}{remoteID});
- if (AttrVal($name, "remoteManagement", "off") eq "client") {
- delete $hash->{RemoteClientUnlock};
- my %functionHash = (hash => $hash, param => 'RemoteClientUnlock');
- RemoveInternalTimer(\%functionHash);
- }
- return undef;
- }
- # Delete
- sub
- EnOcean_Delete($$)
- {
- my ($hash, $name) = @_;
- my $logName = "FileLog_$name";
- my ($count, $gplotFile, $logFile, $weblinkName, $weblinkHash);
- Log3 $name, 2, "EnOcean $name deleted";
- # delete FileLog device and log files
- if (exists $defs{$logName}) {
- $logFile = $defs{$logName}{logfile};
- $logFile =~ /^(.*)($name).*\.(.*)$/;
- $logFile = $1 . $2 . "*." . $3;
- CommandDelete(undef, "FileLog_$name");
- Log3 $name, 2, "EnOcean FileLog_$name deleted";
- $count = unlink glob $logFile;
- Log3 $name, 2, "EnOcean $logFile >> $count files deleted";
- }
- # delete SVG devices and gplot files
- while (($weblinkName, $weblinkHash) = each(%defs)) {
- if ($weblinkName =~ /^SVG_$name.*/) {
- $gplotFile = "./www/gplot/" . $defs{$weblinkName}{GPLOTFILE} . "*.gplot";
- CommandDelete(undef, $weblinkName);
- Log3 $name, 2, "EnOcean $weblinkName deleted";
- $count = unlink glob $gplotFile;
- Log3 $name, 2, "EnOcean $gplotFile >> $count files deleted";
- }
- }
- return undef;
- }
- 1;
- =pod
- =item device
- =item summary EnOcean Gateway and Actor
- =item summary_DE EnOcean Gateway und Aktor
- =begin html
- <a name="EnOcean"></a>
- <h3>EnOcean</h3>
- <ul><br>
- <b>Quick Links</b>
- <ul>
- <li><a href="#EnOceanget">Get Commands</a></li>
- <li><a href="#EnOceanset">Set Commands</a></li>
- <li><a href="#EnOceanattr">Attributes</a></li>
- <li><a href="#EnOceanevents">Generated Events</a></li>
- </ul><br><br>
- EnOcean devices are sold by numerous hardware vendors (e.g. Eltako, Peha, etc),
- using the RF Protocol provided by the EnOcean Alliance.<br><br>
- Depending on the function of the device an specific device profile is used, called
- EnOcean Equipment Profile (EEP). The specific definition of a device is referenced by
- the EEP (RORG-FUNC-TYPE). Basically four groups (RORG) will be differed, e. g.
- RPS (switches), 1BS (contacts), 4BS, VLD (sensors and controller). Some manufacturers use
- additional proprietary extensions. RORG MSC is not supported except for few exceptions.
- Further technical information can be found at the
- <a href="http://www.enocean-alliance.org/de/enocean_standard/">EnOcean Alliance</a>,
- see in particular the
- <a href="http://www.enocean-alliance.org/eep/">EnOcean Equipment Profiles (EEP)</a>
- <br><br>
- The supplementary Generic Profiles approach instead defines a language to communicate the
- transmitted data types and ranges. The devices becomes self describing on their data
- structures in communication. The Generic Profiles include a language definition with
- a parameter selection that covers every possible measured value to be transmitted.
- Therefore, the approach does not only define parameters for the value recalculation algorithm
- but also includes specific signal definition. (e.g. physical units). Further technical
- information can be found at the
- <a href="https://www.enocean-alliance.org/fileadmin/redaktion/enocean_alliance/pdf/GenericProfiles_V1_Extract.pdf">Generic Profiles 1.0 Abstract</a>
- <br><br>
- Smart Acknowledge (Smart Ack) enables a special bidirectional communication. The communication is managed by a
- Controller that responds to the devices telegrams with acknowledges. Smart Ack is a bidirectional communication
- protocol between two actors. At least one actor must be an energy autarkic Sensor, and at least one must be a line
- powered Controller (Fhem). A sensor sends its data and expects the answer telegram in a predefined very short
- time slot. In this time Sensors receiver is active. For this purpose we declare a Post Master with Mail Boxes.
- A Mail Box is like a letter box for a Sensor and it specific to a single sender. Telegrams from Fhem are collected
- into the Mail Box. A Sensor can reclaim telegrams that are in his Mail Box.
- <br><br>
- Fhem recognizes a number of devices automatically. In order to teach-in, for
- some devices the sending of confirmation telegrams has to be turned on.
- Some equipment types and/or device models must be manually specified.
- Do so using the <a href="#EnOceanattr">attributes</a>
- <a href="#subType">subType</a> and <a href="#model">model</a>, see chapter
- <a href="#EnOceanset">Set</a> and
- <a href="#EnOceanevents">Generated events</a>. With the help of additional
- <a href="#EnOceanattr">attributes</a>, the behavior of the devices can be
- changed separately.
- <br><br>
- Fhem and the EnOcean devices must be trained with each other. To this, Fhem
- must be in the learning mode, see <a href="#EnOcean_teach-in">Teach-In / Teach-Out</a>,
- <a href="#EnOcean_smartAck">Smart Ack Learning</a> and <a href="#TCM_learningMode">learningMode</a>.
- The teach-in procedure depends on the type of the devices.
- <br><br>
- Switches (EEP RPS) and contacts (EEP 1BS) are recognized when receiving the first message.
- Contacts can also send a teach-in telegram. Fhem not need this telegram.
- Sensors (EEP 4BS) has to send a teach-in telegram. The profile-less
- 4BS teach-in procedure transfers no EEP profile identifier and no manufacturer
- ID. In this case Fhem does not recognize the device automatically. The proper
- device type must be set manually, use the <a href="#EnOceanattr">attributes</a>
- <a href="#subType">subType</a>, <a href="#manufID">manufID</a> and/or
- <a href="#model">model</a>. If the EEP profile identifier and the manufacturer
- ID are sent the device is clearly identifiable. Fhem automatically assigns
- these devices to the correct profile.
- <br><br>
- 4BS devices can also be taught in special cases by using of confirmation telegrams. This method
- is used for the EnOcean Tipp-Funk devices. The function is activated via the attribute [<a href="#EnOcean_teachMethod">teachMethod</a>] = confirm.<br>
- For example the remote device Eltako TF100D can be learned as follows
- <ul><br>
- <code>define <name> EnOcean H5-38-08</code><br>
- set TF100D in learning mode<br>
- <code>set <name> teach</code>
- </ul>
- <br>
- Some 4BS, VLD or MSC devices must be paired bidirectional,
- see <a href="#EnOcean_teach-in">Teach-In / Teach-Out</a>.
- <br><br>
- Devices that communicate encrypted, has to taught-in through specific procedures.
- <br><br>
- Smart Ack Learning is a futher process where devices exchange information about each
- other in order to create the logical links in the EnOcean network and a Post Master Mail Box.
- It can result in Learn In or Learn Out, see <a href="#EnOcean_smartAck">Smart Ack Learning</a>.
- <br><br>
- Fhem supports many of most common EnOcean profiles and manufacturer-specific
- devices. Additional profiles and devices can be added if required.
- <br><br>
- In order to enable communication with EnOcean remote stations a
- <a href="#TCM">TCM</a> module is necessary.
- <br><br>
- Please note that EnOcean repeaters also send Fhem data telegrams again.
- Use the TCM <code>attr <name> <a href="#blockSenderID">blockSenderID</a> own</code>
- to block receiving telegrams with a TCM SenderIDs.
- <br><br>
- <b>Observing Functions</b><br>
- <ul>
- Interference or overloading of the radio transmission can prevent the reception of Fhem
- commands at the receiver. With the help of the observing function Fhem checks the reception
- of the acknowledgment telegrams of the actuator. If within one second no acknowledgment
- telegram is received, the last set command is sent again.
- The set command is repeated a maximum of 5 times. The maximum number can be specified in the attribute
- <a href="#EnOcean_observeCmdRepetition">observeCmdRepetition</a>.<br>
- The function can only be used if the actuator immediately after the reception of
- the set command sends an acknowledgment message.<br>
- The observing function is turned on by the Attribute <a href="#EnOcean_observe">observe.</a>
- In addition, further devices can be monitored. The names of this devices can be entered in the
- <a href="#EnOcean_observeRefDev">observeRefDev</a> attribute. If additional device are specified,
- the monitoring is stopped as soon as the first acknowledgment telegram of one of the devices was received (OR logic).
- If the <a href="#EnOcean_observeLogic">observeLogic</a> attribute is set to "and", the monitoring is stopped when a telegram
- was received by all devices (AND logic). Please note that the name of the own device has also to be entered in the
- <a href="#EnOcean_observeRefDev">observeRefDev</a> if required.<br>
- If the maximum number of retries is reached and still no all acknowledgment telegrams has been received, the reading
- "observeFailedDev" shows the faulty devices and the command can be executed, that is stored in the
- <a href="#EnOcean_observeErrorAction">observeErrorAction</a> attribute.
- <br><br>
- </ul>
- <b>Energy Management</b><br>
- <ul>
- <li><a href="#demand_response">Demand Response</a> (EEP A5-37-01)</li>
- Demand Response (DR) is a standard to allow utility companies to send requests for reduction in power
- consumption during peak usage times. It is also used as a means to allow users to reduce overall power
- comsumption as energy prices increase. The EEP was designed with a very flexible setting for the level
- (0...15) as well as a default level whereby the transmitter can specify a specific level for all
- controllers to use (0...100 % of either maximum or current power output, depending on the load type).
- The profile also includes a timeout setting to indicate how long the DR event should last if the
- DR transmitting device does not send heartbeats or subsequent new DR levels.<br>
- The DR actor controls the target actuators such as switches, dimmers etc. The DR actor
- is linked to the FHEM target actors via the attribute <a href="#EnOcean_demandRespRefDev">demandRespRefDev</a>.<br>
- <ul>
- <li>Standard actions are available for the following profiles:</li>
- <ul>
- <li>switch (setting the switching command for min, max by the attribute <a href="#EnOcean_demandRespMin">demandRespMin</a>,
- <a href="#EnOcean_demandRespMax">demandRespMax</a>)</li>
- <li>gateway/switching (on, off)</li>
- <li>gateway/dimming (dim 0...100, relative to the max or current set value)</li>
- <li>lightCtrl.01 (dim 0...255)</li>
- <li>actuator.01 (dim 0...100)</li>
- <li>roomSensorControl.01 (setpoint 0...255)</li>
- <li>roomSensorControl.05 (setpoint 0...255 or nightReduction 0...5 for Eltako devices)</li>
- <li>roomCtrlPanel.00 (roomCtrlMode comfort|economy)</li>
- </ul>
- <li>On the target actuator can be specified alternatively a freely definable command.
- The command sequence is stored in the attribute <a href="#EnOcean_demandRespAction">demandRespAction</a>.
- The command sequence can be designed similar to "notify". For the command sequence predefined variables can be used,
- eg. $LEVEL. This actions can be executed very flexible depending on the given energy
- reduction levels.
- </li>
- <li>Alternatively or additionally, a custom command sequence in the DR profile itself
- can be stored.
- </li>
- </ul>
- The profile has a master and slave mode.
- <ul>
- <li>In slave mode, demand response data telegrams received eg a control unit of the power utility,
- evaluated and the corresponding commands triggered on the linked target actuators. The behavior in
- slave mode can be changed by multiple attributes.
- </li>
- <li>In master mode, the demand response level is set by set commands and thus sends a corresponding
- data telegram and the associated target actuators are controlled. The demand response control
- value are specified by "level", "power", "setpoint" "max" or "min". Each other settings are
- calculated proportionally. In normal operation, ie without power reduction, the control value (level)
- is 15. Through the optional parameters "powerUsageScale", "randomStart", "randomEnd" and "timeout"
- the control behavior can be customized. The threshold at which the reading "powerUsageLevel"
- between "min" and "max" switch is specified with the attribute
- <a href="#EnOcean_demandRespThreshold">demandRespThreshold</a>.
- </li>
- </ul>
- Additional information about the profile itself can be found in the EnOcean EEP documentation.
- <br><br>
- </ul>
- <b>Remote Management</b><br>
- <ul>
- Remote Management allows EnOcean devices to be configured and maintained over the air.
- Thanks to Remote Management, sensors or switches IDs, for instance, can be stored or deleted from
- already installed actuators or gateways which are hard to access. Remote Management also allows querying
- debug information from the Remote Device and calling some manufacturer implemented functions.<br>
- Remote Management is performed by the Remote Manager, operated by the actor, on the
- managed Remote Device (Sensor, Gateway). The management is done through a series of
- commands and responding answers. Actor sends the commands to the Remote Device. Remote
- Device sends answers to the actor. The commands indicate the Remote Device what to do.
- Remote Device answers if requested by the command. The commands belong to one of the
- main use case categories, which are:
- <ul>
- <li>Security</li>
- <li>Locate / indentify remote device</li>
- <li>Get status</li>
- <li>Extended function</li>
- </ul>
- The management is often done with a group of Remote Devices. Commands are sent as
- addressed unicast telegrams, usually. In special cases broadcast transmission is also available.
- To avoid telegram collisions the Remote Devices respond to broadcast commands with a
- random delay.<br>
- The Security, Locate, and Get Status options provide to the actor basic operability of Remote
- management. Their purpose is to ensure the proper work of Remote Management when
- operating with several Remote Devices. These functions behave in the same way on every
- Remote Device. Every product that supports Remote Management provides these options.<br>
- Extended functions provide the real benefit of Remote Management. They vary from Remote
- Device to Remote Device. They depend on how and where the Remote Device is used.
- Therefore, not every Remote Device provides every extended function. It depends on the
- programmer / customer what extended functions he wants to add. There is a list of specified
- commands, but the manufacturer can also add manufacturer specific extended functions. These
- functions are identified by the manufacturer ID.<br>
- More information can be found on the <a href="http://www.enocean.com">EnOcean websites</a>.<br><ber>
- Fhem operates primarily as a remote manager. For tests but also a client device can be created.
- <br><br>
- The remote manager function must be activated for the desired device by
- <ul><br>
- <code>attr <remote device name> remote manager</code><br>
- </ul>
- <br><br>
- The remote client device must be defined as follows<br>
- <ul><br>
- <code>define <client name> EnOcean C5-00-00</code><br>
- </ul><br>
- and has to by unlocked for t seconds
- <ul><br>
- <code>set <client name> unlock <t/s></code><br>
- </ul><br>
- Only one remote management client device should be defined.<br><br>
- For security reasons the remote management commands can only be accessed in the unlock
- period. The period can be entered in two cases:
- <ul>
- <li>Within 30min after device power-up if no CODE is set</li>
- <li>Within 30min after an unlock command with a correct 32bit security code is received</li>
- </ul>
- The unlock/lock period can be accessed only with the security code. The security code can be
- set whenever the Remote Device accepts remote management commands.<br>
- When the Remote Device is locked it does not respond to any command, but unlock and ping.
- When a wrong security code is received the Remote Device does not process unlock commands
- for a security period of 30 seconds.<br>
- Security code=0x000000 is the default value and has to be interpreted as: no CODE has been
- set. The actor can also set the security code to 0x000000 from a previously set value. If no
- security code is set, unlock after the unlock period is not processed. Only ping will be
- processed. Remote Management is not available until next power up. 0xFFFFFFFF is reserved
- and can not be used as security code.<br><br>
- To administrate a remote device whose Remote ID must be known. The Remote ID can be determined
- as follows:
- <ul><br>
- <code>attr <name> remote manager</code><br>
- power-up the remote device<br>
- <code>get <name> remoteID</code><br><br>
- </ul>
- All commands are described in the remote management chapters of the <a href="#EnOcean_remoteSet">set</a>-
- and <a href="#EnOcean_remoteGet">get</a>-commands.<br><br>
- The Remote Management Function is configured using the following attributes:<br>
- <ul>
- <li><a href="#EnOcean_remoteCode">remoteCode</a></li>
- <li><a href="#EnOcean_remoteEEP">remoteEEP</a></li>
- <li><a href="#EnOcean_remoteID">remoteID</a></li>
- <li><a href="#EnOcean_remoteManagement">remoteManagement</a></li>
- <li><a href="#EnOcean_remoteManufID">remoteManufID</a></li>
- </ul><br>
- The content of events is described in the chapter <a href="#EnOcean_remoteEvents">Remote Management Events</a><br><br>.
- The following extended functions are supported:
- <ul>
- <li>210:remoteLinkTableInfo</li>
- <li>211:remoteLinkTable</li>
- <li>212:remoteLinkTable</li>
- <li>213:remoteLinkTableGP</li>
- <li>214:remoteLinkTableGP</li>
- <li>220:remoteLearnMode</li>
- <li>221:remoteTeach</li>
- <li>224:remoteReset</li>
- <li>225:remoteRLT</li>
- <li>226:remoteApplyChanges</li>
- <li>227:remoteProductID</li>
- <li>230:remoteDevCfg</li>
- <li>231:remoteDevCfg</li>
- <li>232:remoteLinkCfg</li>
- <li>233:remoteLinkCfg</li>
- <li>240:remoteAck</li>
- <li>250:remoteRepeater</li>
- <li>251:remoteRepeater</li>
- <li>252:remoteRepeaterFilter</li>
- </ul>
- <br><br>
- </ul>
- <b>Radio Link Test</b><br>
- <ul>
- Units supporting the Radio Link Test (RLT) shall offer a functionality that allows for radio link testing between them
- (Position A to Position B, point-to-point only). Fhem support at least 1BS and 4BS test messages. When two units
- perform radio link testing one unit needs to act in a mode called RLT Master and the other unit needs to act in
- a mode called RLT Slave. Fhem acts as RLT Master (subType radioLinkTest).<br>
- The Radio Link Test device must be defined as follows<br>
- <ul><br>
- <code>define <name> EnOcean A5-3F-00</code><br>
- </ul><br>
- and has to by activated
- <ul><br>
- <code>set <name> standby</code><br>
- </ul><br>
- Alternatively, the device can also be created automatically by autocreate. Only one RLT device may be defined in FHEM.<br>
- After activation the RLT Master listens for RLT Query messages. On reception of at least one RLT Query messsage the
- RLT Master responds and starts transmission of RLT MasterTest messages. After that the RLT Master awaits the response
- from the RLT Slave.<br>
- A radio link test communication consits of a minimum of 16 and a maximum of 256 RLT MasterTest messages. When the
- radio link test communication is completed the RLT Master gets deactivated automatically. The test results can be
- found in the log file.
- <br><br>
- </ul>
- <b>Security features</b><br>
- <ul>
- The receiving and sending of encrypted messages is supported. This module currently allows the secure operating mode of PTM 215
- based switches.<br>
- To receive secured telegrams, you first have to start the teach in mode via<br><br>
- <code>set <IODev> teach <t/s></code><br><br>
- and then doing the following on the PTM 215 module:<br>
- <ul>
- <li>Remove the switch cover of the module</li>
- <li>Press both buttons of one rocker side (A0 & A1 or B0 & B1)</li>
- <li>While keeping the buttons pressed actuate the energy bow twice.</li><br>
- </ul>
- This generates two teach-in telegrams which create a Fhem device with the subType "switch.00" and synchronize the Fhem with
- the PTM 215. Both the Fhem and the PTM 215 now maintain a counter which is used to generate a rolling code encryption scheme.
- Also during teach-in, a private key is transmitted to the Fhem. The counter value is allowed to desynchronize for a maximum of
- 128 counts, to allow compensating for missed telegrams, if this value is crossed you need to teach-in the PTM 215 again. Also
- if your Fhem installation gets erased including the state information, you need to teach in the PTM 215 modules again (which
- you would need to do anyway).<br><br>
- To send secured telegrams, you first have send a secure teach-in to the remode device<br><br>
- <ul>
- <code>set <name> teachInSec</code><br><br>
- </ul>
- As for the security of this solution, if someone manages to capture the teach-in telegrams, he can extract the private key,
- so the added security isn't perfect but relies on the fact, that none listens to you setting up your installation.
- <br><br>
- The cryptographic functions need the additional Perl modules Crypt/Rijndael and Crypt/Random. The module must be installed manually.
- With the help of CPAN at the operating system level, for example,<br><br>
- <ul>
- <code>/usr/bin/perl -MCPAN -e 'install Crypt::Rijndael'</code><br>
- <code>/usr/bin/perl -MCPAN -e 'install Crypt::Random'</code>
- </ul>
- <br><br>
- </ul>
- <a name="EnOceandefine"></a>
- <b>Define</b>
- <ul>
- <code>define <name> EnOcean <DEF> [<EEP>]|getNextID|<EEP></code>
- <br><br>
- Define an EnOcean device, connected via a <a href="#TCM">TCM</a> modul. The
- <DEF> is the SenderID/DestinationID of the device (8 digit hex number), for example
- <ul><br>
- <code>define switch1 EnOcean FFC54500</code><br>
- </ul><br>
- In order to control devices, you cannot reuse the SenderIDs/
- DestinationID of other devices (like remotes), instead you have to create
- your own, which must be in the allowed SenderID range of the underlying Fhem
- IO device, see <a href="#TCM">TCM</a> BaseID, LastID. For this first query the
- <a href="#TCM">TCM</a> with the <code>get <tcm> baseID</code> command
- for the BaseID. You can use up to 128 IDs starting with the BaseID shown there.
- If you are using an Fhem SenderID outside of the allowed range, you will see an
- ERR_ID_RANGE message in the Fhem log.<br>
- FHEM can assign a free SenderID alternatively, for example
- <ul><br>
- <code>define switch1 EnOcean getNextID</code><br>
- </ul><br>
- If the EEP is known, the appropriate device can be created with the basic parameters, for example
- <ul><br>
- <code>define sensor1 EnOcean FFC54500 A5-02-05</code><br>
- </ul><br>
- or
- <ul><br>
- <code>define sensor1 EnOcean A5-02-05</code><br>
- </ul><br>
- Inofficial EEP for special devices
- <ul>
- <li>G5-07-01 PioTek-Tracker<br></li>
- <li>G5-10-12 Room Sensor and Control Unit [Eltako FUTH65D]<br></li>
- <li>G5-38-08 Gateway, Dimming [Eltako FSG, FUD]<br></li>
- <li>H5-38-08 Gateway, Dimming [Eltako TF61D, TF100D]<br></li>
- <li>M5-38-08 Gateway, Switching [Eltako FSR14]<br></li>
- <li>N5-38-08 Gateway, Switching [Eltako TF61L, TF61R, TF100A, TF100L]<br></li>
- <li>G5-3F-7F Shutter [Eltako FSB]<br></li>
- <li>H5-3F-7F Shutter [Eltako TF61J]<br></li>
- <li>L6-02-01 Smoke Detector [Eltako FRW]<br></li>
- <li>G5-ZZ-ZZ Light and Presence Sensor [Omnio Ratio eagle-PM101]<br></li>
- <li>ZZ-ZZ-ZZ EnOcean RAW profile<br></li>
- <br><br>
- </ul>
- The <a href="#autocreate">autocreate</a> module may help you if the actor or sensor send
- acknowledge messages or teach-in telegrams. In order to control this devices e. g. switches with
- additional SenderIDs you can use the attributes <a href="#subDef">subDef</a>,
- <a href="#subDef0">subDef0</a> and <a href="#subDefI">subDefI</a>.<br>
- Fhem communicates unicast, if bidirectional 4BS or UTE teach-in is used, see
- <a href="#EnOcean_teach-in"> Bidirectional Teach-In / Teach-Out</a>. In this case
- Fhem send unicast telegrams with its SenderID and the DestinationID of the device.
- <br><br>
- </ul>
- <a name="EnOceaninternals"></a>
- <b>Internals</b>
- <ul>
- <li>DEF: 0000000 ... FFFFFFFF|<EEP><br>
- EnOcean DestinationID or SenderID<br>
- If the attributes subDef* are set, this values are used as EnOcean SenderID.<br>
- For an existing device, the device can be re-parameterized by entering the EEP.<br>
- </li>
- <li><IODev>_DestinationID: 0000000 ... FFFFFFFF<br>
- Received destination address, Broadcast radio: FFFFFFFF<br>
- </li>
- <li><IODev>_RSSI: LP/dBm<br>
- Received signal strength indication (best value of all received subtelegrams)<br>
- </li>
- <li><IODev>_ReceivingQuality: excellent|good|bad<br>
- excellent: RSSI >= -76 dBm (internal standard antenna sufficiently)<br>
- good: RSSI < -76 dBm and RSSI >= -87 dBm (good antenna necessary)<br>
- bad: RSSI < -87 dBm (repeater required)<br>
- </li>
- <li><IODev>_RepeatingCounter: 0...2<br>
- Number of forwardings by repeaters<br>
- </li>
- <br><br>
- </ul>
- <a name="EnOceanset"></a>
- <b>Set</b>
- <ul>
- <li><a name="EnOcean_teach-in">Teach-In / Teach-Out</a>
- <ul>
- <li>Teach-in remote devices</li>
- <br>
- <code>set <IODev> teach <t/s></code>
- <br><br>
- Set Fhem in the learning mode.<br>
- A device, which is then also put in this state is to paired with
- Fhem. Bidirectional Teach-In / Teach-Out is used for some 4BS, VLD and MSC devices,
- e. g. EEP 4BS, RORG A5-20-01 (Battery Powered Actuator).<br>
- Bidirectional Teach-In for 4BS, UTE and Generic Profiles are supported.<br>
- <code>IODev</code> is the name of the TCM Module.<br>
- <code>t/s</code> is the time for the learning period.
- <br><br>
- Types of learning modes see <a href="#TCM_learningMode">learningMode</a>
- <br><br>
- Example:
- <ul><code>set TCM_0 teach 600</code></ul>
- <br>
- <li>RPS profiles Teach-In (switches)</li>
- <br>
- <code>set <name> A0|AI|B0|BI|C0|CI|D0|DI</code>
- <br><br>
- Send teach-in telegram to remote device.
- <br><br>
- <li>1BS profiles Teach-In (contact)</li>
- <br>
- <code>set <name> teach</code>
- <br><br>
- Send teach-in telegram to remote device.
- <br><br>
- <li>4BS profiles Teach-In (sensors, dimmer, room controller etc.)</li>
- <br>
- <code>set <name> teach</code>
- <br><br>
- Send teach-in telegram to remote device.<br>
- If no SenderID (attr subDef) was assigned before a learning telegram is sent for the first time, a free SenderID
- is assigned automatically.
- <br><br>
- <li>UTE - Universal Uni- and Bidirectional Teach-In</li>
- <br>
- <code>set <name> teachIn|teachOut</code>
- <br><br>
- Send teach-in telegram to remote device.<br>
- If no SenderID (attr subDef) was assigned before a learning telegram is sent for the first time, a free SenderID
- is assigned automatically.
- <br><br>
- <li>Generic Profiles Teach-In</li>
- <br>
- <code>set <name> teachIn|teachOut</code>
- <br><br>
- Send teach-in telegram to remote device.<br>
- If no SenderID (attr subDef) was assigned before a learning telegram is sent for the first time, a free SenderID
- is assigned automatically.
- <br><br>
- <li>Secure Devices Teach-In</li>
- <br>
- <code>set <name> teachInSec</code>
- <br><br>
- Secure teach-in to the remode device.<br>
- If no SenderID (attr subDef) was assigned before a learning telegram is sent for the first time, a free SenderID
- is assigned automatically.
- <br><br>
- </ul>
- </li>
- <li><a name="EnOcean_smartAck">Smart Ack Learning</a>
- <ul>
- <li>Teach-in remote Smart Ack devices</li>
- <br>
- <code>set <IODev> smartAckLearn <t/s></code>
- <br><br>
- Set Fhem in the Smart Ack learning mode.<br>
- The post master fuctionality must be activated using the command <code>smartAckMailboxMax</code> in advance.<br>
- The simple learnmode is supported, see <a href="#TCM_smartAckLearnMode">smartAckLearnMode</a><br>
- A device, which is then also put in this state is to paired with
- Fhem. Bidirectional learn in for 4BS, UTE and Generic Profiles are supported.<br>
- <code>IODev</code> is the name of the TCM Module.<br>
- <code>t/s</code> is the time for the learning period.
- <br><br>
- Example:
- <ul><code>set TCM_0 smartAckLearn 600</code></ul>
- <br>
- </ul>
- </li>
- <li><a name="EnOcean_remoteSet">Remote Management</a>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>remoteAction<br>
- sent action command to perfoms an action, depending on the functionality of the device</li>
- <li>remoteApplyChanges devCfg|linkTable|no_change<br>
- apply changes</li>
- <li>remoteDevCfg <index> <value><br>
- set configuration</li>
- <li>remoteLinkTable in|out <index> <ID> <EEP> <channel><br>
- set link table content</li>
- <li>remoteLinkCfg in|out <index> <data index> <value><br>
- set link based configuration</li>
- <li>remoteLinkTableGP in|out <index> <GP channel description><br>
- set link table content</li>
- <li>remoteLock<br>
- locks the remote device or local client</li>
- <li>remoteLearnMode in|out|off <index><br>
- initiate remote learn-in or learn-out of inbound index</li>
- <li>remoteReset devCfg|linkTableIn|linkTableOut|no_change<br>
- reset to defaults</li>
- <li>remoteRLT on|off <number of RLT slaves><br>
- reset to defaults</li>
- <li>remoteRepeater on|off|filter <level> <filter structure><br>
- set repeater functions</li>
- <li>remoteRepeaterFilter apply|block|delete|deleteAll destinationID|sourceID|rorg|rssi <filter value><br>
- set repeater functions</li>
- <li>remoteSetCode<br>
- set the remote security code</li>
- <li>remoteTeach <channel><br>
- request teach-in telegram from channel 00..FF</li>
- <li>remoteUnlock [1...1800]<br>
- unlocks the remote device or local client<br>
- The unlock period can be set in the client mode between 1s and 1800 s.</li>
- <br>
- [<channel>] = 00...FF<br>
- [<EEP>] = <RORG>-<function>-<type><br>
- [<filter structure>] = AND|OR<br>
- [<filter value>] = <destinationID>|<sourceID>|<RORG>|<LP/dBm><br>
- [<GP channel description>] = <name of channel 00>:<O|I>:<channel type>:<signal type>:<value type>[:<resolution>[:<engineering min>:<scaling min>:<engineering max>:<scaling max>]]<br>
- [<ID>] = 00000001...FFFFFFFE<br>
- [<index>] = 00...FF<br>
- [<number of RLT slaves>] = 01..7F<br>
- [<level>] = 1|2<br>
- [<data index>] = 0000...FFFF<br>
- [<value>] = n x 00...FF<br>
- </ul><br>
- </li><br>
- <li>Switch, Pushbutton Switch (EEP F6-02-01 ... F6-03-02)<br>
- RORG RPS [default subType]
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is one of A0, AI, B0, BI, C0, CI, D0, DI,
- combinations of these and released. First and second action can be sent
- simultaneously. Separate first and second action with a comma.<br>
- In fact we are trying to emulate a PT200 type remote.<br>
- If you define an <a href="#eventMap">eventMap</a> attribute with on/off,
- then you will be able to easily set the device from the <a
- href="#FHEMWEB">WEB</a> frontend.<br>
- <a href="#setExtensions">set extensions</a> are supported, if the corresponding
- <a href="#eventMap">eventMap</a> specifies the <code>on</code> and <code>off</code>
- mappings, for example <code>attr <name> eventMap on-till:on-till AI:on A0:off</code>.<br>
- With the help of additional <a href="#EnOceanattr">attributes</a>, the
- behavior of the devices can be adapt.<br>
- The attr subType must be switch. This is done if the device was created by autocreate.
- <br><br>
- Example:
- <ul><code>
- set switch1 BI<br>
- set switch1 B0,CI<br>
- attr eventMap BI:on B0:off<br>
- set switch1 on<br>
- </code></ul><br>
- </ul>
- </li>
- <li>Staircase off-delay timer (EEP F6-02-01 ... F6-02-02)<br>
- RORG RPS [Eltako FTN14, tested with Eltako FTN14 only]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>on<br>
- issue switch on command</li>
- <li>released<br>
- start timer</li>
- </ul><br>
- Set attr eventMap to B0:on BI:off, attr subType to switch, attr
- webCmd to on:released and if needed attr switchMode to pushbutton manually.<br>
- The attr subType must be switch. This is done if the device was created by autocreate.<br>
- Use the sensor type "Schalter" for Eltako devices. The Staircase
- off-delay timer is switched on when pressing "on" and the time will be started
- when pressing "released". "released" immediately after "on" is sent if
- the attr switchMode is set to "pushbutton".
- </li>
- <br><br>
- <li>Pushbutton Switch (EEP D2-03-00)<br>
- RORG VLD [EnOcean PTM 215 Modul]
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>teachIn<br>
- initiate UTE teach-in</li>
- <li>teachInSec<br>
- initiate secure teach-in</li>
- <li>teachOut<br>
- initiate UTE teach-out</li>
- <li>A0|AI|B0|BI<br>
- issue switch command</li>
- <li>A0,B0|A0,AI|AI,B0|AI,BI<br>
- issue switch command</li>
- <li>pressed<br>
- energy bow pressed</li>
- <li>pressed34<br>
- 3 or 4 buttons and energy bow pressed</li>
- <li>released<br>
- energy bow released</li><br>
- </ul>
- First and second action can be sent simultaneously. Separate first and second action with a comma.<br>
- If you define an <a href="#eventMap">eventMap</a> attribute with on/off,
- then you will be able to easily set the device from the <a href="#FHEMWEB">WEB</a> frontend.<br>
- <a href="#setExtensions">set extensions</a> are supported, if the corresponding
- <a href="#eventMap">eventMap</a> specifies the <code>on</code> and <code>off</code>
- mappings, for example <code>attr <name> eventMap on-till:on-till AI:on A0:off</code>.<br>
- If <a href="#EnOcean_comMode">comMode</a> is set to biDir the device can be controlled bidirectionally.<br>
- With the help of additional <a href="#EnOceanattr">attributes</a>, the behavior of the devices can be adapt.<br>
- The attr subType must be switch.00. This is done if the device was created by autocreate.
- <br><br>
- <ul>
- Example:
- <ul><code>
- set switch1 BI<br>
- set switch1 B0,CI<br>
- attr eventMap BI:on B0:off<br>
- set switch1 on<br>
- </code></ul><br>
- </ul>
- </li>
- <li>Single Input Contact, Door/Window Contact (EEP D5-00-01)<br>
- RORG 1BS [tested with Eltako FSR14]
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>closed<br>
- issue closed command</li>
- <li>open<br>
- issue open command</li>
- <li>teach<br>
- initiate teach-in</li>
- </ul><br>
- The attr subType must be contact. The attribute must be set manually.
- </li><br><br>
- <li>Room Sensor and Control Unit (EEP A5-10-02)<br>
- [Thermokon SR04 PTS]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>teach<br>
- initiate teach-in</li>
- <li>setpoint [0 ... 255]<br>
- Set the actuator to the specifed setpoint.</li>
- <li>setpointScaled [<floating-point number>]<br>
- Set the actuator to the scaled setpoint.</li>
- <li>fanStage [auto|0|1|2|3]<br>
- Set fan stage</li>
- <li>switch [on|off]<br>
- Set switch</li>
- </ul><br>
- The actual temperature will be taken from the temperature reported by
- a temperature reference device <a href="#temperatureRefDev">temperatureRefDev</a>
- primarily or from the attribute <a href="#actualTemp">actualTemp</a> if it is set.<br>
- If the attribute <a href="#EnOcean_setCmdTrigger">setCmdTrigger</a> is set to "refDev", a setpoint
- command is sent when the reference device is updated.<br>
- The scaling of the setpoint adjustment is device- and vendor-specific. Set the
- attributes <a href="#scaleMax">scaleMax</a>, <a href="#scaleMin">scaleMin</a> and
- <a href="#scaleDecimals">scaleDecimals</a> for the additional scaled setting
- setpointScaled.<br>
- The attr subType must be roomSensorControl.05. The attribute must be set manually.
- </li>
- <br><br>
- <li>Room Sensor and Control Unit (EEP A5-10-03)<br>
- [used for IntesisBox PA-AC-ENO-1i]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>teach<br>
- initiate teach-in</li>
- <li>setpoint [0 ... 255]<br>
- Set the actuator to the specifed setpoint.</li>
- <li>setpointScaled [<floating-point number>]<br>
- Set the actuator to the scaled setpoint.</li>
- <li>fanStage [auto|0|1|2|3]<br>
- Set fan stage</li>
- <li>switch [on|off]<br>
- Set switch</li>
- </ul><br>
- The actual temperature will be taken from the temperature reported by
- a temperature reference device <a href="#temperatureRefDev">temperatureRefDev</a>
- primarily or from the attribute <a href="#actualTemp">actualTemp</a> if it is set.<br>
- If the attribute <a href="#EnOcean_setCmdTrigger">setCmdTrigger</a> is set to "refDev", a setpoint
- command is sent when the reference device is updated.<br>
- The scaling of the setpoint adjustment is device- and vendor-specific. Set the
- attributes <a href="#scaleMax">scaleMax</a>, <a href="#scaleMin">scaleMin</a> and
- <a href="#scaleDecimals">scaleDecimals</a> for the additional scaled setting
- setpointScaled.<br>
- The attr subType must be roomSensorControl.05 and attr manufID must be 019. The attribute must be set manually.
- </li>
- <br><br>
- <li>Room Sensor and Control Unit (A5-10-06 plus night reduction)<br>
- [Eltako FTR65DS, FTR65HS]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>teach<br>
- initiate teach-in</li>
- <li>desired-temp [t/°C [lock|unlock]]<br>
- Set the desired temperature.</li>
- <li>nightReduction [t/K [lock|unlock]]<br>
- Set night reduction</li>
- <li>setpointTemp [t/°C [lock|unlock]]<br>
- Set the desired temperature</li>
- </ul><br>
- The actual temperature will be taken from the temperature reported by
- a temperature reference device <a href="#temperatureRefDev">temperatureRefDev</a>
- primarily or from the attribute <a href="#actualTemp">actualTemp</a> if it is set.<br>
- If the attribute <a href="#EnOcean_setCmdTrigger">setCmdTrigger</a> is set to "refDev", a setpointTemp
- command is sent when the reference device is updated.<br>
- This profil can be used with a further Room Sensor and Control Unit Eltako FTR55*
- to control a heating/cooling relay FHK12, FHK14 or FHK61. If Fhem and FTR55*
- is teached in, the temperature control of the FTR55* can be either blocked
- or to a setpoint deviation of +/- 3 K be limited. For this use the optional parameter
- [block] = lock|unlock, unlock is default.<br>
- The attr subType must be roomSensorControl.05 and attr manufID must be 00D.
- The attributes must be set manually.
- </li>
- <br><br>
- <li>Room Sensor and Control Unit (EEP A5-10-10)<br>
- [Thermokon SR04 * rH, Thanos SR *]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>teach<br>
- initiate teach-in</li>
- <li>setpoint [0 ... 255]<br>
- Set the actuator to the specifed setpoint.</li>
- <li>setpointScaled [<floating-point number>]<br>
- Set the actuator to the scaled setpoint.</li>
- <li>switch [on|off]<br>
- Set switch</li>
- </ul><br>
- The actual temperature will be taken from the temperature reported by
- a temperature reference device <a href="#temperatureRefDev">temperatureRefDev</a>
- primarily or from the attribute <a href="#actualTemp">actualTemp</a> if it is set.<br>
- The actual humidity will be taken from the humidity reported by
- a humidity reference device <a href="#EnOcean_humidityRefDev">humidityRefDev</a>
- primarily or from the attribute <a href="#EnOcean_humidity">humidity</a> if it is set.<br>
- If the attribute <a href="#EnOcean_setCmdTrigger">setCmdTrigger</a> is set to "refDev", a setpoint
- command is sent when the reference device is updated.<br>
- The scaling of the setpoint adjustment is device- and vendor-specific. Set the
- attributes <a href="#scaleMax">scaleMax</a>, <a href="#scaleMin">scaleMin</a> and
- <a href="#scaleDecimals">scaleDecimals</a> for the additional scaled setting
- setpointScaled.<br>
- The attr subType must be roomSensorControl.01. The attribute must be set manually.
- </li>
- <br><br>
- <li>Room Sensor and Control Unit (EEP A5-10-12)<br>
- [Eltako FUTH65D]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>teach<br>
- initiate teach-in</li>
- <li>setpoint [0 ... 255]<br>
- Set the actuator to the specifed setpoint.</li>
- <li>setpointScaled [<floating-point number>]<br>
- Set the actuator to the scaled setpoint.</li>
- <li>switch [on|off]<br>
- Set switch</li>
- </ul><br>
- The actual temperature will be taken from the temperature reported by
- a temperature reference device <a href="#temperatureRefDev">temperatureRefDev</a>
- primarily or from the attribute <a href="#actualTemp">actualTemp</a> if it is set.<br>
- If the attribute <a href="#EnOcean_setCmdTrigger">setCmdTrigger</a> is set to "refDev", a setpoint
- command is sent when the reference device is updated.<br>
- The attr subType must be roomSensorControl.01 and attr manufID must be 00D. The attribute must be set manually.
- </li>
- <br><br>
- <li>Battery Powered Actuator (EEP A5-20-01)<br>
- [Kieback&Peter MD15-FTL-xx]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>setpoint setpoint/%<br>
- Set the actuator to the specifed setpoint (0...100). The setpoint can also be set by the
- <a href="#EnOcean_setpointRefDev">setpointRefDev</a> device if it is set.</li>
- <li>setpointTemp t/°C<br>
- Set the actuator to the specifed temperature setpoint. The temperature setpoint can also be set by the
- <a href="#EnOcean_setpointTempRefDev">setpointTempRefDev</a> device if it is set.<br>
- The FHEM PID controller calculates the actuator setpoint based on the temperature setpoint. The controller's
- operation can be set via the PID parameters <a href="#EnOcean_pidFactor_P">pidFactor_P</a>,
- <a href="#EnOcean_pidFactor_I">pidFactor_I</a> and <a href="#EnOcean_pidFactor_D">pidFactor_D</a>.<br>
- If the attribute pidCtrl is set to off, the PI controller of the actuator is used (selfCtrl mode). Please
- read the instruction manual of the device, whether the device has an internal PI controller.<br></li>
- <li>runInit<br>
- Maintenance Mode: Run init sequence</li>
- <li>valveOpens<br>
- Maintenance Mode: Valve opens<br>
- After the valveOpens command, the valve remains open permanently and can no longer be controlled by Fhem.
- By pressing the button on the device itself, the actuator is returned to its normal operating state.</li>
- <li>valveCloses<br>
- Maintenance Mode: Valve closes</li>
- </ul><br>
- The Heating Radiator Actuating Drive is configured using the following attributes:<br>
- <ul>
- <li><a href="#EnOcean_pidActorCallBeforeSetting">pidActorCallBeforeSetting</a></li>
- <li><a href="#EnOcean_pidActorErrorAction">pidActorErrorAction</a></li>
- <li><a href="#EnOcean_pidActorErrorPos">pidActorErrorPos</a></li>
- <li><a href="#EnOcean_pidActorLimitLower">pidActorLimitLower</a></li>
- <li><a href="#EnOcean_pidActorLimitUpper">pidActorLimitUpper</a></li>
- <li><a href="#EnOcean_pidCtrl">pidCtrl</a></li>
- <li><a href="#EnOcean_pidDeltaTreshold">pidDeltaTreshold</a></li>
- <li><a href="#EnOcean_pidFactor_P">pidFactor_P</a></li>
- <li><a href="#EnOcean_pidFactor_I">pidFactor_I</a></li>
- <li><a href="#EnOcean_pidFactor_D">pidFactor_D</a></li>
- <li><a href="#EnOcean_pidIPortionCallBeforeSetting">pidIPortionCallBeforeSetting</a></li>
- <li><a href="#EnOcean_pidSensorTimeout">pidSensorTimeout</a></li>
- <li><a href="#EnOcean_rcvRespAction">rcvRespAction</a></li>
- <li><a href="#EnOcean_setpointRefDev">setpointRefDev</a></li>
- <li><a href="#EnOcean_setpointSummerMode">setpointSummerMode</a></li>
- <li><a href="#EnOcean_setpointTempRefDev">setpointTempRefDev</a></li>
- <li><a href="#temperatureRefDev">temperatureRefDev</a></li>
- <li><a href="#EnOcean_summerMode">summerMode</a></li>
- </ul>
- The actual temperature will be reported by the Heating Radiator Actuating Drive or by the
- <a href="#temperatureRefDev">temperatureRefDev</a> if it is set. The internal temperature sensor
- of the Micropelt iTRV is not suitable as an actual temperature value for the PID controller.
- An external room thermostat is required.<br>
- The attr event-on-change-reading .* shut not by set. The PID controller expects periodic events.
- If these are missing, a communication alarm is signaled.<br>
- The attr subType must be hvac.01. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired,
- see <a href="#EnOcean_teach-in">Teach-In / Teach-Out</a>.<br>
- The command is not sent until the device wakes up and sends a message, usually
- every 10 minutes.
- </li>
- <br><br>
- <li>Heating Radiator Actuating Drive (EEP A5-20-04)<br>
- [Holter SmartDrive MX]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>setpoint setpoint/%<br>
- Set the actuator to the specifed setpoint (0...100). The setpoint can also be set by the
- <a href="#EnOcean_setpointRefDev">setpointRefDev</a> device if it is set.</li>
- <li>setpointTemp t/°C<br>
- Set the actuator to the specifed temperature setpoint. The temperature setpoint can also be set by the
- <a href="#EnOcean_setpointTempRefDev">setpointTempRefDev</a> device if it is set.<br>
- The FHEM PID controller calculates the actuator setpoint based on the temperature setpoint. The controller's
- operation can be set via the PID parameters <a href="#EnOcean_pidFactor_P">pidFactor_P</a>,
- <a href="#EnOcean_pidFactor_I">pidFactor_I</a> and <a href="#EnOcean_pidFactor_D">pidFactor_D</a>.</li>
- <li>runInit<br>
- Maintenance Mode: Run init sequence</li>
- <li>valveOpens<br>
- Maintenance Mode: Valve opens<br>
- After the valveOpens command, the valve remains open permanently and can no longer be controlled by Fhem.
- By pressing the button on the device itself, the actuator is returned to its normal operating state.</li>
- <li>valveCloses<br>
- Maintenance Mode: Valve closes</li>
- </ul><br>
- The Heating Radiator Actuating Drive is configured using the following attributes:<br>
- <ul>
- <li><a href="#EnOcean_blockKey">blockKey</a></li>
- <li><a href="#EnOcean_displayOrientation">displayOrientation</a></li>
- <li><a href="#EnOcean_measurementCtrl">measurementCtrl</a></li>
- <li><a href="#model">model</a></li>
- <li><a href="#EnOcean_pidActorCallBeforeSetting">pidActorCallBeforeSetting</a></li>
- <li><a href="#EnOcean_pidActorErrorAction">pidActorErrorAction</a></li>
- <li><a href="#EnOcean_pidActorErrorPos">pidActorErrorPos</a></li>
- <li><a href="#EnOcean_pidActorLimitLower">pidActorLimitLower</a></li>
- <li><a href="#EnOcean_pidActorLimitUpper">pidActorLimitUpper</a></li>
- <li><a href="#EnOcean_pidCtrl">pidCtrl</a></li>
- <li><a href="#EnOcean_pidDeltaTreshold">pidDeltaTreshold</a></li>
- <li><a href="#EnOcean_pidFactor_P">pidFactor_P</a></li>
- <li><a href="#EnOcean_pidFactor_I">pidFactor_I</a></li>
- <li><a href="#EnOcean_pidFactor_D">pidFactor_D</a></li>
- <li><a href="#EnOcean_pidIPortionCallBeforeSetting">pidIPortionCallBeforeSetting</a></li>
- <li><a href="#EnOcean_pidSensorTimeout">pidSensorTimeout</a></li>
- <li><a href="#EnOcean_setpointRefDev">setpointRefDev</a></li>
- <li><a href="#EnOcean_setpointTempRefDev">setpointTempRefDev</a></li>
- <li><a href="#temperatureRefDev">temperatureRefDev</a></li>
- <li><a href="#EnOcean_summerMode">summerMode</a></li>
- <li><a href="#EnOcean_wakeUpCycle">wakeUpCycle</a></li>
- </ul>
- The actual temperature will be reported by the Heating Radiator Actuating Drive or by the
- <a href="#temperatureRefDev">temperatureRefDev</a> if it is set.<br>
- The attr event-on-change-reading .* shut not by set. The PID controller expects periodic events.
- If these are missing, a communication alarm is signaled.<br>
- The attr subType must be hvac.04. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired,
- see <a href="#EnOcean_teach-in">Teach-In / Teach-Out</a>.<br>
- The OEM version of the Holter SmartDrive MX has an internal PID controller. This function is activated by
- attr <device> model Holter_OEM and attr <device> pidCtrl off.<br>
- The command is not sent until the device wakes up and sends a message, usually
- every 5 minutes.
- </li>
- <br><br>
- <li>Generic HVAC Interface (EEP A5-20-10)<br>
- [IntesisBox PA-AC-ENO-1i]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>ctrl auto|0...100<br>
- Set control variable</li>
- <li>fanSpeed auto|1...14<br>
- Set fan speed</li>
- <li>occupancy occupied|off|standby|unoccupied<br>
- Set room occupancy</li>
- <li>on<br>
- Set on</li>
- <li>off<br>
- Set off</li>
- <li>mode auto|heat|morning_warmup|cool|night_purge|precool|off|test|emergency_heat|fan_only|free_cool|ice|max_heat|eco|dehumidification|calibration|emergency_cool|emergency_stream|max_cool|hvc_load|no_load|auto_heat|auto_cool<br>
- Set mode</li>
- <li>teach<br>
- Teach-in</li>
- <li>vanePosition auto|horizontal|position_2|position_3|position_4|vertical|swing|vertical_swing|horizontal_swing|hor_vert_swing|stop_swing<br>
- Set vane position</li>
- </ul><br>
- The attr subType must be hvac.10. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired,
- see <a href="#EnOcean_teach-in">Teach-In / Teach-Out</a>.
- </li>
- <br><br>
- <li>Generic HVAC Interface - Error Control (EEP A5-20-11)<br>
- [IntesisBox PA-AC-ENO-1i]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>externalDisable disable|enable<br>
- Set external disablement</li>
- <li>remoteCtrl disable|enable<br>
- Dieable/enable remote controller</li>
- <li>teach<br>
- Teach-in</li>
- <li>window closed|opened<br>
- Set window state</li>
- </ul><br>
- The attr subType must be hvac.11. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired,
- see <a href="#EnOcean_teach-in">Teach-In / Teach-Out</a>.
- </li>
- <br><br>
- <li>Energy management, <a name="demand_response">demand response</a> (EEP A5-37-01)<br>
- demand response master commands<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>level 0...15 [<powerUsageScale> [<randomStart> [<randomEnd> [timeout]]]]<br>
- set demand response level</li>
- <li>max [<powerUsageScale> [<randomStart> [<randomEnd> [timeout]]]]<br>
- set power usage level to max</li>
- <li>min [<powerUsageScale> [<randomStart> [<randomEnd> [timeout]]]]<br>
- set power usage level to min</li>
- <li>power power/% [<powerUsageScale> [<randomStart> [<randomEnd> [timeout]]]]<br>
- set power</li>
- <li>setpoint 0...255 [<powerUsageScale> [<randomStart> [<randomEnd> [timeout]]]]<br>
- set setpoint</li>
- <li>teach<br>
- initiate teach-in</li>
- </ul><br>
- [<powerUsageScale>] = max|rel, [<powerUsageScale>] = max is default<br>
- [<randomStart>] = yes|no, [<randomStart>] = no is default<br>
- [<randomEnd>] = yes|no, [<randomEnd>] = no is default<br>
- [timeout] = 0/min | 15/min ... 3825/min, [timeout] = 0 is default<br>
- The attr subType must be energyManagement.01.<br>
- This is done if the device was created by autocreate.<br>
- </li>
- <br><br>
- <li><a name="Gateway">Gateway</a> (EEP A5-38-08)<br>
- The Gateway profile include 7 different commands (Switching, Dimming,
- Setpoint Shift, Basic Setpoint, Control variable, Fan stage, Blind Central Command).
- The commands can be selected by the attribute gwCmd or command line. The attribute
- entry has priority.<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li><gwCmd> <cmd> [subCmd]<br>
- initiate Gateway commands by command line</li>
- <li><cmd> [subCmd]<br>
- initiate Gateway commands if attribute gwCmd is set.</li>
- </ul><br>
- The attr subType must be gateway. Attribute gwCmd can also be set to
- switching|dimming|setpointShift|setpointBasic|controlVar|fanStage|blindCmd.<br>
- This is done if the device was created by autocreate.<br>
- For Eltako devices attributes must be set manually.
- </li>
- <br><br>
- <li>Gateway (EEP A5-38-08)<br>
- Switching<br>
- [Eltako FLC61, FSA12, FSR14]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>teach<br>
- initiate teach-in mode</li>
- <li>on [lock|unlock]<br>
- issue switch on command</li>
- <li>off [lock|unlock]<br>
- issue switch off command</li>
- <li><a href="#setExtensions">set extensions</a> are supported.</li>
- </ul><br>
- The attr subType must be gateway and gwCmd must be switching. This is done if the device was
- created by autocreate.<br>
- For Eltako devices attributes must be set manually. For Eltako FSA12 attribute model must be set
- to Eltako_FSA12.
- </li>
- <br><br>
- <li>Gateway (EEP A5-38-08)<br>
- Dimming<br>
- [Eltako FUD12, FUD14, FUD61, FUD70, FSG14, ...]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>dim/% [rampTime/s [lock|unlock]]<br>
- issue dim command</li>
- <li>teach<br>
- initiate teach-in mode</li>
- <li>on [lock|unlock]<br>
- issue switch on command</li>
- <li>off [lock|unlock]<br>
- issue switch off command</li>
- <li>dim dim/% [rampTime/s [lock|unlock]]<br>
- issue dim command</li>
- <li>dimup dim/% [rampTime/s [lock|unlock]]<br>
- issue dim command</li>
- <li>dimdown dim/% [rampTime/s [lock|unlock]]<br>
- issue dim command</li>
- <li><a href="#setExtensions">set extensions</a> are supported.</li>
- </ul><br>
- rampTime Range: t = 1 s ... 255 s or 0 if no time specified,
- for Eltako: t = 1 = fast dimming ... 255 = slow dimming or 0 = dimming speed on the dimmer used<br>
- The attr subType must be gateway and gwCmd must be dimming. This is done if the device was
- created by autocreate.<br>
- For Eltako devices attributes must be set manually. Use the sensor type "PC/FVS" for Eltako devices.
- </li>
- <br><br>
- <li>Gateway (EEP A5-38-08)<br>
- Dimming of fluorescent lamps<br>
- [Eltako FSG70, tested with Eltako FSG70 only]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>on<br>
- issue switch on command</li>
- <li>off<br>
- issue switch off command</li>
- <li><a href="#setExtensions">set extensions</a> are supported.</li>
- </ul><br>
- The attr subType must be gateway and gwCmd must be dimming. Set attr eventMap to B0:on BI:off,
- attr subTypeSet to switch and attr switchMode to pushbutton manually.<br>
- Use the sensor type "Richtungstaster" for Eltako devices.
- </li>
- <br><br>
- <li>Gateway (EEP A5-38-08)<br>
- Setpoint shift<br>
- [untested]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>teach<br>
- initiate teach-in mode</li>
- <li>shift 1/K <br>
- issue Setpoint shift</li>
- </ul><br>
- Shift Range: T = -12.7 K ... 12.8 K<br>
- The attr subType must be gateway and gwCmd must be setpointShift.
- This is done if the device was created by autocreate.<br>
- </li>
- <br><br>
- <li>Gateway (EEP A5-38-08)<br>
- Basic Setpoint<br>
- [untested]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>teach<br>
- initiate teach-in mode</li>
- <li>basic t/°C<br>
- issue Basic Setpoint</li>
- </ul><br>
- Setpoint Range: t = 0 °C ... 51.2 °C<br>
- The attr subType must be gateway and gwCmd must be setpointBasic.
- This is done if the device was created by autocreate.<br>
- </li>
- <br><br>
- <li>Gateway (EEP A5-38-08)<br>
- Control variable<br>
- [untested]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>teach<br>
- initiate teach-in mode</li>
- <li>presence present|absent|standby<br>
- issue Room occupancy</li>
- <li>energyHoldOff normal|holdoff<br>
- issue Energy hold off</li>
- <li>controllerMode auto|heating|cooling|off<br>
- issue Controller mode</li>
- <li>controllerState auto|override <0 ... 100> <br>
- issue Control variable override</li>
- </ul><br>
- Override Range: cvov = 0 % ... 100 %<br>
- The attr subType must be gateway and gwCmd must be controlVar.
- This is done if the device was created by autocreate.<br>
- </li>
- <br><br>
- <li>Gateway (EEP A5-38-08)<br>
- Fan stage<br>
- [untested]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>teach<br>
- initiate teach-in mode</li>
- <li>stage 0 ... 3|auto<br>
- issue Fan Stage override</li>
- </ul><br>
- The attr subType must be gateway and gwCmd must be fanStage.
- This is done if the device was created by autocreate.<br>
- </li>
- <br><br>
- <li>Gateway (EEP A5-38-08)<br>
- <a name="Blind Command Central">Blind Command Central</a><br>
- [not fully tested]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>position/% [α/°]<br>
- drive blinds to position with angle value</li>
- <li>teach<br>
- initiate teach-in mode</li>
- <li>status<br>
- Status request</li>
- <li>opens<br>
- issue blinds opens command</li>
- <li>up tu/s ta/s<br>
- issue roll up command</li>
- <li>closes<br>
- issue blinds closes command</li>
- <li>down td/s ta/s<br>
- issue roll down command</li>
- <li>position position/% [α/°]<br>
- drive blinds to position with angle value</li>
- <li>stop<br>
- issue blinds stops command</li>
- <li>runtimeSet tu/s td/s<br>
- set runtime parameter</li>
- <li>angleSet ta/s<br>
- set angle configuration</li>
- <li>positionMinMax positionMin/% positionMax/%<br>
- set min, max values for position</li>
- <li>angleMinMax αo/° αs/°<br>
- set slat angle for open and shut position</li>
- <li>positionLogic normal|inverse<br>
- set position logic</li>
- </ul><br>
- Runtime Range: tu|td = 0 s ... 255 s<br>
- Select a runtime up and a runtime down that is at least as long as the
- shading element or roller shutter needs to move from its end position to
- the other position.<br>
- Position Range: position = 0 % ... 100 %<br>
- Angle Time Range: ta = 0 s ... 25.5 s<br>
- Runtime value for the sunblind reversion time. Select the time to revolve
- the sunblind from one slat angle end position to the other end position.<br>
- Slat Angle: α|αo|αs = -180 ° ... 180 °<br>
- Position Logic, normal: Blinds fully opens corresponds to Position = 0 %<br>
- Position Logic, inverse: Blinds fully opens corresponds to Position = 100 %<br>
- The attr subType must be gateway and gwCmd must be blindCmd.<br>
- See also attributes <a href="#EnOcean_sendDevStatus">sendDevStatus and <a href="#EnOcean_serviceOn">serviceOn</a></a><br>
- The profile is linked with controller profile, see <a href="#Blind Status">Blind Status</a>.<br>
- </li>
- <br><br>
- <li>Extended Lighting Control (EEP A5-38-09)<br>
- [untested]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>teach<br>
- initiate remote teach-in</li>
- <li>on<br>
- issue switch on command</li>
- <li>off<br>
- issue switch off command</li>
- <li>dim dim [rampTime/s]<br>
- issue dim command</li>
- <li>dimup rampTime/s<br>
- issue dim command</li>
- <li>dimdown rampTime/s<br>
- issue dim command</li>
- <li>stop<br>
- stop dimming</li>
- <li>rgb <red color value><green color value><blue color value><br>
- issue color value command</li>
- <li>scene drive|store 0..15<br>
- store actual value in the scene or drive to scene value</li>
- <li>dimMinMax <min value> <max value><br>
- set minimal and maximal dimmer value</li>
- <li>lampOpHours 0..65535<br>
- set the operation hours of the lamp</li>
- <li>block unlock|on|off|local<br>
- locking local operations</li>
- <li>meteringValues 0..65535 mW|W|kW|MW|Wh|kWh|MWh|GWh|mA|mV<br>
- set a new value for the energy metering (overwrite the actual value with the selected unit)</li>
- <li>meteringValues 0..6553.5 A|V<br>
- set a new value for the energy metering (overwrite the actual value with the selected unit)</li>
- <li><a href="#setExtensions">set extensions</a> are supported.</li>
- </ul><br>
- color values: 00 ... FF hexadecimal<br>
- rampTime Range: t = 1 s ... 65535 s or 1 if no time specified, ramping time can be set by attribute
- <a href="#EnOcean_rampTime">rampTime</a><br>
- The attr subType or subTypSet must be lightCtrl.01. This is done if the device was created by autocreate.<br>
- The subType is associated with the subtype lightCtrlState.02.
- </li>
- <br><br>
- <li><a name="Manufacturer Specific Applications">Manufacturer Specific Applications</a> (EEP A5-3F-7F)<br>
- Shutter<br>
- [Eltako FSB12, FSB14, FSB61, FSB70, tested with Eltako devices only]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>position/% [α/°]<br>
- drive blinds to position with angle value</li>
- <li>anglePos α/°<br>
- drive blinds to angle value</li>
- <li>closes<br>
- issue blinds closes command</li>
- <li>down td/s<br>
- issue roll down command</li>
- <li>opens<br>
- issue blinds opens command</li>
- <li>position position/% [α/°]<br>
- drive blinds to position with angle value</li>
- <li>stop<br>
- issue stop command</li>
- <li>teach<br>
- initiate teach-in mode</li>
- <li>up tu/s<br>
- issue roll up command</li>
- </ul><br>
- Run-time Range: tu|td = 1 s ... 255 s<br>
- Position Range: position = 0 % ... 100 %<br>
- Slat Angle Range: α = -180 ° ... 180 °<br>
- Angle Time Range: ta = 0 s ... 6 s<br>
- The devive can only fully controlled if the attributes <a href="#angleMax">angleMax</a>,
- <a href="#angleMin">angleMin</a>, <a href="#angleTime">angleTime</a>,
- <a href="#shutTime">shutTime</a> and <a href="#shutTimeCloses">shutTimeCloses</a>,
- are set correctly.
- If <a href="#EnOcean_settingAccuracy">settingAccuracy</a> is set to high, the run-time is sent in 1/10 increments.<br>
- Set attr subType to manufProfile, manufID to 00D and attr model to Eltako_FSB14|FSB61|FSB70|TF manually.<br>
- Use the sensor type "Szenentaster/PC" for Eltako devices.
- </li>
- <br><br>
- <li>Electronic switches and dimmers with Energy Measurement and Local Control (D2-01-00 - D2-01-12)<br>
- [Telefunken Funktionsstecker, PEHA Easyclick, AWAG Elektrotechnik AG Omnio UPS 230/xx,UPD 230/xx, NodOn in-wall module, smart plug]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>autoOffTime t/s [<channel>]<br>
- set auto Off timer</li>
- <li>delayOffTime t/s [<channel>]<br>
- set delay Off timer</li>
- <li>dim/% [<channel> [<rampTime>]]<br>
- issue dimming command</li>
- <li>extSwitchMode unavailable|switch|pushbutton|auto [<channel>]<br>
- set external interface mode</li>
- <li>extSwitchType toggle|direction [<channel>]<br>
- set external interface type</li>
- <li>on [<channel>]<br>
- issue switch on command</li>
- <li>off [<channel>]<br>
- issue switch off command</li>
- <li>dim dim/% [<channel> [<rampTime>]]<br>
- issue dimming command</li>
- <li>local dayNight day|night, day is default<br>
- set the user interface indication</li>
- <li>local defaultState on|off|last, off is default<br>
- set the default setting of the output channels when switch on</li>
- <li>local localControl enabled|disabled, disabled is default<br>
- enable the local control of the device</li>
- <li>local overCurrentShutdown off|restart, off is default<br>
- set the behavior after a shutdown due to an overcurrent</li>
- <li>local overCurrentShutdownReset not_active|trigger, not_active is default<br>
- trigger a reset after an overcurrent</li>
- <li>local rampTime<1...3> 0/s, 0.5/s ... 7/s, 7.5/s, 0 is default<br>
- set the dimming time of timer 1 ... 3</li>
- <li>local teachInDev enabled|disabled, disabled is default<br>
- enable the taught-in devices with different EEP</li>
- <li>measurement delta 0/s ... 4095/s, 0 is default<br>
- define the difference between two displayed measurements </li>
- <li>measurement mode energy|power, energy is default<br>
- define the measurand</li>
- <li>measurement report query|auto, query is default<br>
- specify the measurement method</li>
- <li>measurement reset not_active|trigger, not_active is default<br>
- resetting the measured values</li>
- <li>measurement responseTimeMax 10/s ... 2550/s, 10 is default<br>
- set the maximum time between two outputs of measured values</li>
- <li>measurement responseTimeMin 1/s ... 255/s, 1 is default<br>
- set the minimum time between two outputs of measured values</li>
- <li>measurement unit Ws|Wh|KWh|W|KW, Ws is default<br>
- specify the measurement unit</li>
- <li>roomCtrlMode off|comfort|comfort-1|comfort-2|economy|buildingProtection<br>
- set pilot wire mode</li>
- <li>special repeater off|1|2<br>
- set repeater level of device (additional NodOn command)
- </li>
- </ul><br>
- [autoOffTime] = 0 s ... 0.1 s ... 6553.4 s<br>
- [delayOffTime] = 0 s ... 0.1 s ... 6553.4 s<br>
- [channel] = 0...29|all|input, all is default<br>
- The default channel can be specified with the attr <a href="#EnOcean_defaultChannel">defaultChannel</a>.<br>
- [rampTime] = 1..3|switch|stop, switch is default<br>
- The attr subType must be actuator.01. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired,
- see <a href="#EnOcean_teach-in">Bidirectional Teach-In / Teach-Out</a>.
- </li>
- <br><br>
- <li>Blind Control for Position and Angle (D2-05-00 - D2-05-01)<br>
- [AWAG Elektrotechnik AG OMNIO UPJ 230/12, REGJ12/04M ]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>opens [<channel>]<br>
- issue blinds opens command</li>
- <li>closes [<channel>]<br>
- issue blinds closes command</li>
- <li>position position/% [[α/%] [[<channel>] [directly|opens|closes]]]<br>
- drive blinds to position with angle value</li>
- <li>anglePos α/% [<channel>]<br>
- drive blinds to angle value</li>
- <li>stop [<channel>]<br>
- issue stop command</li>
- <li>alarm [<channel>]<br>
- set actuator to the "alarm" mode. When the actuator ist set to the "alarm" mode neither local
- nor central positioning and configuration commands will be executed. Before entering the "alarm"
- mode, the actuator will execute the "alarm action" as configured by the attribute <a href="#EnOcean_alarmAction">alarmAction</a>
- </li>
- <li>lock [<channel>]<br>
- set actuator to the "blockade" mode. When the actuator ist set to the "blockade" mode neither local
- nor central positioning and configuration commands will be executed.
- </li>
- <li>unlock [<channel>]<br>
- issue unlock command</li>
- </ul><br>
- Channel Range: 1 ... 4|all, default is all<br>
- Position Range: position = 0 % ... 100 %<br>
- Slat Angle Range: α = 0 % ... 100 %<br>
- The devive can only fully controlled if the attributes <a href="#EnOcean_alarmAction">alarmAction</a>,
- <a href="#angleTime">angleTime</a>, <a href="#EnOcean_reposition">reposition</a> and <a href="#shutTime">shutTime</a>
- are set correctly.<br>
- With the attribute <a name="EnOcean_defaultChannel">defaultChannel</a> the default channel can be specified.<br>
- The attr subType must be blindsCtrl.00 or blindsCtrl.01. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired,
- see <a href="#EnOcean_teach-in">Bidirectional Teach-In / Teach-Out</a>.
- </li>
- <br><br>
- <li>Multisensor Windows Handle (D2-06-01)<br>
- [Soda GmbH]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>presence absent|present<br>
- set vacation mode</li>
- <li>handleClosedClick disable|enable<br>
- set handle closed click feature</li>
- <li>batteryLowClick disable|enable<br>
- set battery low click feature</li>
- <li>teachSlave contact|windowHandleClosed|windowHandleOpen|windowHandleTilted<br>
- sent teach-in to the slave devices (contact: EEP: D5-00-01, windowHandle: EEP F6-10-00)<br>
- The events window or handle will get forwarded once a slave-device contact or windowHandle is taught in.
- </li>
- <li>updateInterval t/s<br>
- set sensor update interval</li>
- <li>blinkInterval t/s<br>
- set vacation blink interval</li>
- </ul><br>
- sensor update interval Range: updateInterval = 5 ... 65535<br>
- vacation blick interval Range: blinkInterval = 3 ... 255<br>
- The multisensor window handle is configured using the following attributes:<br>
- <ul>
- <li><a href="#EnOcean_subDefH">subDefH</a></li>
- <li><a href="#EnOcean_subDefW">subDefW</a></li>
- </ul>
- The attr subType must be multisensor.01. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Room Control Panels (D2-10-00 - D2-10-02)<br>
- [Kieback & Peter RBW322-FTL]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>buildingProtectionTemp t/°C<br>
- set building protection temperature</li>
- <li>clearCmds [<channel>]<br>
- clear waiting commands</li>
- <li>comfortTemp t/°C<br>
- set comfort temperature</li>
- <li>config<br>
- Setting the configuration of the room controller, the configuration parameters are set using attributes.</li>
- <li>cooling auto|off|on|no_change<br>
- switch cooling</li>
- <li>deleteTimeProgram<br>
- delete time programs of the room controller</li>
- <li>desired-temp t/°C<br>
- set setpoint temperature</li>
- <li>economyTemp t/°C<br>
- set economy temperature</li>
- <li>fanSpeed fanspeed/%<br>
- set fan speed</li>
- <li>fanSpeedMode central|local<br>
- set fan speed mode</li>
- <li>heating auto|off|on|no_change<br>
- switch heating</li>
- <li>preComfortTemp t/°C<br>
- set pre comfort temperature</li>
- <li>roomCtrlMode buildingProtectionTemp|comfortTemp|economyTemp|preComfortTemp<br>
- select setpoint temperature</li>
- <li>setpointTemp t/°C<br>
- set current setpoint temperature</li>
- <li>time<br>
- set time and date of the room controller </li>
- <li>timeProgram<br>
- set time programms of the room contoller</li>
- <li>window closed|open<br>
- put the window state</li>
- </ul><br>
- Setpoint Range: t = 0 °C ... 40 °C<br>
- The room controller is configured using the following attributes:<br>
- <ul>
- <li><a href="#EnOcean_blockDateTime">blockDateTime</a></li>
- <li><a href="#EnOcean_blockDisplay">blockDisplay</a></li>
- <li><a href="#EnOcean_blockFanSpeed">blockFanSpeed</a></li>
- <li><a href="#EnOcean_blockMotion">blockMotion</a></li>
- <li><a href="#EnOcean_blockProgram">blockProgram</a></li>
- <li><a href="#EnOcean_blockOccupany">blockOccupancy</a></li>
- <li><a href="#EnOcean_blockTemp">blockTemp</a></li>
- <li><a href="#EnOcean_blockTimeProgram">blockTimeProgram</a></li>
- <li><a href="#EnOcean_blockSetpointTemp">blockSetpointTemp</a></li>
- <li><a href="#EnOcean_daylightSavingTime">daylightSavingTime</a></li>
- <li><a href="#EnOcean_displayContent">displayContent</a></li>
- <li><a href="#EnOcean_pollInterval">pollInterval</a></li>
- <li><a href="#EnOcean_temperatureScale">temperatureScale</a></li>
- <li><a href="#EnOcean_timeNotation">timeNotation</a></li>
- <li><a href="#EnOcean_timeProgram[1-4]">timeProgram[1-4]</a></li>
- </ul>
- The attr subType must be roomCtrlPanel.00. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired,
- see <a href="#EnOcean_teach-in">Bidirectional Teach-In / Teach-Out</a>.
- </li>
- <br><br>
- <li>Room Control Panels (D2-11-01 - D2-11-08)<br>
- [Thermokon EasySens SR06 LCD-2T/-2T rh -4T/-4T rh]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>cooling on|off, default [colling] = off<br>
- set cooling symbol at the display</li>
- <li>desired-temp t/°C<br>
- set setpoint temperature</li>
- <li>fanSpeed auto|off|1|2|3<br>
- set fan speed</li>
- <li>heating on|off, default [heating] = off<br>
- set heating symbol at the display</li>
- <li>occupancy occupied|unoccupied<br>
- set occupancy state</li>
- <li>setpointTemp t/°C<br>
- set current setpoint temperature</li>
- <li>setpointShiftMax t/K<br>
- set setpoint shift max</li>
- <li>setpointType setpointTemp|setpointShift<br>
- set setpoint type</li>
- <li>window closed|open, default [window] = closed<br>
- set window open symbol at the display</li>
- </ul><br>
- Setpoint Range: t = 5 °C ... 40 °C<br>
- Setpoint Shift Max Range: t = 0 K ... 10 K<br>
- The attr subType must be roomCtrlPanel.01. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired by Smart Ack,
- see <a href="#EnOcean_smartAck">SmartAck Learning</a>.
- </li>
- <br><br>
- <li>Fan Control (D2-20-00 - D2-20-02)<br>
- [Maico ECA x RC/RCH, ER 100 RC, untested]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>on<br>
- fan on</li>
- <li>off<br>
- fan off</li>
- <li>desired-temp [t/°C]<br>
- set setpoint temperature</li>
- <li>fanSpeed fanspeed/%|auto|default<br>
- set fan speed</li>
- <li>humidityThreshold rH/%<br>
- set humidity threshold</li>
- <li>roomSize 0...350/m<sup>2</sup>|default|not_used<br>
- set room size</li>
- <li>setpointTemp [t/°C]<br>
- set current setpoint temperature</li>
- </ul><br>
- Setpoint Range: t = 0 °C ... 40 °C<br>
- The fan controller is configured using the following attributes:<br>
- <ul>
- <li><a href="#EnOcean_setCmdTrigger">setCmdTrigger</a></li>
- <li><a href="#EnOcean_switchHysteresis">switchHysteresis</a></li>
- <li><a href="#temperatureRefDev">temperatureRefDev</a></li>
- </ul>
- The attr subType must be fanCtrl.00. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired,
- see <a href="#EnOcean_teach-in">Bidirectional Teach-In / Teach-Out</a>. The profile
- behaves like a master. Only one fan can be taught as a slave.
- </li>
- <br><br>
- <li>Heat Recovery Ventilation (D2-50-00 - D2-50-11)<br>
- [untested]<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>ventilation off|1...4|auto|demand|supplyAir|exhaustAir<br>
- select ventilation mode/level</li>
- <li>heatExchangerBypass opens|closes<br>
- override of automatic heat exchanger bypass control</li>
- <li>startTimerMode<br>
- enable timer operation mode</li>
- <li>CO2Threshold default|1/%<br>
- override CO2 threshold for CO2 control in automatic mode</li>
- <li>humidityThreshold default|rH/%<br>
- override humidity threshold for humidity control in automatic mode</li>
- <li>airQuatityThreshold default|1/%<br>
- override air qualidity threshold for air qualidity control in automatic mode</li>
- <li>roomTemp default|t/°C<br>
- override room temperature threshold for room temperature control mode</li>
- </ul><br>
- roomTemp Range: t = -63 °C ... 63 °C<br>
- xThreshold Range: 0 % ... 100 %<br>
- The attr subType must be heatRecovery.00. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired,
- see <a href="#EnOcean_teach-in">Bidirectional Teach-In / Teach-Out</a>.
- </li>
- <br><br>
- <li>Valve Control (EEP D2-A0-01)<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>closes<br>
- issue closes command (master)</li>
- <li>opens<br>
- issue opens command (master)</li>
- <li>closed<br>
- issue closed command (slave)</li>
- <li>open<br>
- issue open command (slave)</li>
- <li>teachIn<br>
- initiate UTE teach-in (slave)</li>
- <li>teachOut<br>
- initiate UTE teach-out (slave)</li>
- </ul><br>
- The valve controller is configured using the following attributes:<br>
- <ul>
- <li><a href="#EnOcean_devMode">devMode</a></li>
- </ul>
- The attr subType must be valveCtrl.00. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired,
- see <a href="#EnOcean_teach-in">Bidirectional Teach-In / Teach-Out</a>. The profile
- behaves like a master or slave, see <a href="#EnOcean_devMode">devMode</a>.
- </li>
- <br><br>
- <li>Generic Profiles<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li><00 ... 64>-<channel name> <value><br>
- set channel value</li>
- <li>channelName <channel number>-<channel name><br>
- rename channel</li>
- <li>teachIn<br>
- sent teach-in telegram</li>
- <li>teachOut<br>
- sent teach-out telegram</li>
- </ul><br>
- The generic profile device is configured using the following attributes:<br>
- <ul>
- <li><a href="#EnOcean_comMode">comMode</a></li>
- <li><a href="#EnOcean_devMode">devMode</a></li>
- <li><a href="#EnOcean_gpDef">gpDef</a></li>
- <li><a href="#EnOcean_manufID">manufID</a></li>
- </ul>
- The attr subType must be genericProfile. This is done if the device was
- created by autocreate. If the profile in slave mode is operated, especially the channel
- definition in the gpDef attributes must be entered manually.
- </li>
- <br><br>
- <li>RAW Command<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>1BS|4BS|GPCD|GPSD|GPTI|GPTR|MSC|RPS|UTE|VLD data [status]<br>
- sent data telegram</li>
- </ul><br>
- [data] = <1-byte hex ... 512-byte hex><br>
- [status] = 0x00 ... 0xFF<br>
- With the help of this command data messages in hexadecimal format can be sent.
- Telegram types (RORG) 1BS, 4BS, RPS, MSC, UTE, VLD, GPCD, GPSD, GPTI and GPTR are supported.
- For further information, see <a href="http://www.enocean-alliance.org/eep/">EnOcean Equipment Profiles (EEP)</a> and
- Generic Profiles.
- </li>
- <br><br>
- <li>Radio Link Test<br>
- <ul>
- <code>set <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>standby|stop<br>
- set RLT Master state
- </li>
- </ul><br>
- The Radio Link Test device is configured using the following attributes:<br>
- <ul>
- <li><a href="#EnOcean_rltRepeat">rltRepeat</a></li>
- <li><a href="#EnOcean_rltType">rltType</a></li>
- </ul>
- The attr subType must be readioLinkTest. This is done if the device was
- created by autocreate or manually by <code>define <name> EnOcean A5-3F-00</code><br>.
- </li>
- <br><br>
- </ul></ul>
- <a name="EnOceanget"></a>
- <b>Get</b>
- <ul>
- <li><a name="EnOcean_remoteGet">Remote Management</a>
- <ul>
- <code>get <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>remoteDevCfg <start data index> <end data index><br>
- get device configuration between start index and end index</li>
- <li>remoteFunctions<br>
- get a list of the supported extended functions</li>
- <li>remoteID<br>
- get the remote device ID</li>
- <li>remoteLinkTableInfo<br>
- query supported link table info</li>
- <li>remoteLinkCfg in|out <index> <start data index> <end data index> <length><br>
- get link table between start index and end index</li>
- <li>remoteLinkTable in|out <start index> <end index><br>
- get link table between start index and end index</li>
- <li>remoteLinkTableGP in|out <index><br>
- get link table GP entry with index</li>
- <li>remotePing<br>
- get a ping response from the remote device</li>
- <li>remoteProductID<br>
- query product ID</li>
- <li>remoteRepeater<br>
- asks for the repeater status of the remote device</li>
- <li>remoteStatus<br>
- asks for the status info of the remote device</li>
- <br>
- [<data index>] = 0000...FFFF<br>
- [<index>] = 00...FF<br>
- [<length>] = n x 00...FF<br>
- </ul>
- </li><br><br>
- <li>Dual Channel Switch Actuator (EEP A5-11-059)<br>
- [untested]<br>
- <ul>
- <code>get <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>state<br>
- status request</li>
- </ul><br>
- The attr subType or subTypSet must be switch.05. This is done if the device was created by autocreate.
- </li>
- <br><br>
- <li>Extended Lighting Control (EEP A5-38-09)<br>
- [untested]<br>
- <ul>
- <code>get <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>state<br>
- status request</li>
- </ul><br>
- The attr subType or subTypSet must be lightCtrl.01. This is done if the device was created by autocreate.<br>
- The subType is associated with the subtype lightCtrlState.02.
- </li>
- <br><br>
- <li>Electronic switches and dimmers with Energy Measurement and Local Control (D2-01-00 - D2-01-12)<br>
- [Telefunken Funktionsstecker, PEHA Easyclick, AWAG Elektrotechnik AG Omnio UPS 230/xx,UPD 230/xx, NodOn in-wall module, smart plug]<br>
- <ul>
- <code>get <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>roomCtrlMode<br>
- get pilot wire mode</li>
- <li>settings [<channel>]<br>
- get external interface settings</li>
- <li>state [<channel>]<br>
- </li>
- <li>measurement <channel> energy|power<br>
- </li>
- <li>special <channel> health|load|voltage|serialNumber<br>
- additional Permondo SmartPlug PSC234 commands
- </li>
- <li>special <channel> firmwareVersion|reset|taughtInDevID|taughtInDevNum<br>
- additional NodOn commands
- </li>
- </ul><br>
- The default channel can be specified with the attr <a href="#EnOcean_defaultChannel">defaultChannel</a>.<br>
- The attr subType must be actuator.01. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired,
- see <a href="#EnOcean_teach-in">Bidirectional Teach-In / Teach-Out</a>.
- </li>
- <br><br>
- <li>Blind Control for Position and Angle (D2-05-00)<br>
- [AWAG Elektrotechnik AG OMNIO UPJ 230/12, REGJ12/04M]<br>
- <ul>
- <code>get <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>position [<channel>]<br>
- query position and angle value</li>
- </ul><br>
- Channel Range: 1 ... 4|all, default is all<br>
- The devive can only fully controlled if the attributes <a href="#EnOcean_alarmAction">alarmAction</a>,
- <a href="#angleTime">angleTime</a>, <a href="#EnOcean_reposition">reposition</a> and <a href="#shutTime">shutTime</a>
- are set correctly.<br>
- With the attribute <a name="EnOcean_defaultChannel">defaultChannel</a> the default channel can be specified.<br>
- The attr subType must be blindsCtrl.00 or blindsCrtl.01. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired,
- see <a href="#EnOcean_teach-in">Bidirectional Teach-In / Teach-Out</a>.
- </li>
- <br><br>
- <li>Multisensor Windows Handle (D2-06-01)<br>
- [Soda GmbH]<br>
- <ul>
- <code>get <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>config<br>
- get configuration settings</li>
- <li>log<br>
- get log data</li>
- </ul><br>
- The multisensor window handle is configured using the following attributes:<br>
- <ul>
- <li><a href="#EnOcean_subDefH">subDefH</a></li>
- <li><a href="#EnOcean_subDefW">subDefW</a></li>
- </ul>
- The attr subType must be multisensor.01. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Room Control Panels (D2-10-00 - D2-10-02)<br>
- [Kieback & Peter RBW322-FTL]<br>
- <ul>
- <code>get <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>config<br>
- get the configuration of the room controler</li>
- <li>data<br>
- get data</li>
- <li>roomCtrl<br>
- get the parameter of the room controler</li>
- <li>timeProgram<br>
- get the time program</li>
- </ul><br>
- The attr subType must be roomCtrlPanel.00. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired,
- see <a href="#EnOcean_teach-in">Bidirectional Teach-In / Teach-Out</a>.
- </li>
- <br><br>
- <li>Fan Control (D2-20-00 - D2-20-02)<br>
- [Maico ECA x RC/RCH, ER 100 RC, untested]<br>
- <ul>
- <code>get <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>state<br>
- get the state of the room controler</li>
- </ul><br>
- The attr subType must be fanCtrl.00. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired,
- see <a href="#EnOcean_teach-in">Bidirectional Teach-In / Teach-Out</a>.
- </li>
- <br><br>
- <li>Heat Recovery Ventilation (D2-50-00 - D2-50-11)<br>
- [untested]<br>
- <ul>
- <code>get <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>basicState<br>
- get the basic state</li>
- <li>extendedState<br>
- get the extended state</li>
- </ul><br>
- The attr subType must be heatRecovery.00. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired,
- see <a href="#EnOcean_teach-in">Bidirectional Teach-In / Teach-Out</a>.
- </li>
- <br><br>
- <li>Valve Control (EEP D2-A0-01)<br>
- <ul>
- <code>get <name> <value></code>
- <br><br>
- where <code>value</code> is
- <li>state<br>
- get the state of the valve controler (master)</li>
- </ul><br>
- The attr subType must be valveCtrl.00. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired,
- see <a href="#EnOcean_teach-in">Bidirectional Teach-In / Teach-Out</a>. The profile
- behaves like a master or slave, see <a href="#EnOcean_devMode">devMode</a>.
- </li>
- <br><br>
- </ul><br>
- <a name="EnOceanattr"></a>
- <b>Attributes</b>
- <ul>
- <ul>
- <li><a name="actualTemp">actualTemp</a> t/°C<br>
- The value of the actual temperature, used by a Room Sensor and Control Unit
- or when controlling HVAC components e. g. Battery Powered Actuators (MD15 devices). Should by
- filled via a notify from a distinct temperature sensor.<br>
- If absent, the reported temperature from the HVAC components is used.
- </li>
- <li><a name="EnOcean_alarmAction">alarmAction</a> <channel1>[:<channel2>[:<channel3>[:<channel4>]]]<br>
- [alarmAction] = no|stop|opens|closes, default is no<br>
- Action that is executed before the actuator is entering the "alarm" mode.<br>
- Notice subType blindsCrtl.00, blindsCrtl.01: The attribute can only be set while the actuator is online.
- </li>
- <li><a name="angleMax">angleMax</a> αs/°, [αs] = -180 ... 180, 90 is default.<br>
- Slat angle end position maximum.<br>
- angleMax is supported for shutter.
- </li>
- <li><a name="angleMin">angleMin</a> αo/°, [αo] = -180 ... 180, -90 is default.<br>
- Slat angle end position minimum.<br>
- angleMin is supported for shutter.
- </li>
- <li><a name="angleTime">angleTime</a> <channel1>[:<channel2>[:<channel3>[:<channel4>]]]<br>
- subType blindsCtrl.00, blindsCtrl.01: [angleTime] = 0|0.01 .. 2.54, 0 is default.<br>
- subType manufProfile: [angleTime] = 0 ... 6, 0 is default.<br>
- Runtime value for the sunblind reversion time. Select the time to revolve
- the sunblind from one slat angle end position to the other end position.<br>
- Notice subType blindsCrtl.00: The attribute can only be set while the actuator is online.
- </li>
- <li><a name="EnOcean_blockDateTime">blockDateTime</a> yes|no, [blockDateTime] = no is default.<br>
- blockDateTime is supported for roomCtrlPanel.00.
- </li>
- <li><a name="EnOcean_blockDisplay">blockDisplay</a> yes|no, [blockDisplay] = no is default.<br>
- blockDisplay is supported for roomCtrlPanel.00.
- </li>
- <li><a name="EnOcean_blockFanSpeed">blockFanSpeed</a> yes|no, [blockFanSpeed] = no is default.<br>
- blockFanSpeed is supported for roomCtrlPanel.00.
- </li>
- <li><a name="EnOcean_blockKey">blockKey</a> yes|no, [blockKey] = no is default.<br>
- blockKey is supported for roomCtrlPanel.00 and hvac.04.
- </li>
- <li><a name="EnOcean_blockMotion">blockMotion</a> yes|no, [blockMotion] = no is default.<br>
- blockMotion is supported for roomCtrlPanel.00.
- </li>
- <li><a name="EnOcean_blockOccupany">blockOccupancy</a> yes|no, [blockOccupancy] = no is default.<br>
- blockOccupancy is supported for roomCtrlPanel.00.
- </li>
- <li><a name="EnOcean_blockTemp">blockTemp</a> yes|no, [blockTemp] = no is default.<br>
- blockTemp is supported for roomCtrlPanel.00.
- </li>
- <li><a name="EnOcean_blockTimeProgram">blockTimeProgram</a> yes|no, [blockTimeProgram] = no is default.<br>
- blockTimeProgram is supported for roomCtrlPanel.00.
- </li>
- <li><a name="EnOcean_blockSetpointTemp">blockSetpointTemp</a> yes|no, [blockSetpointTemp] = no is default.<br>
- blockSetPointTemp is supported for roomCtrlPanel.00.
- </li>
- <li><a name="EnOcean_blockUnknownMSC">blockUnknownMSC</a> yes|no,
- [blockUnknownMSC] = no is default.<br>
- If the structure of the MSC telegrams can not interpret the raw data to be output. Setting this attribute to yes,
- the output can be suppressed.
- </li>
- <li><a name="EnOcean_comMode">comMode</a> biDir|confirm|uniDir, [comMode] = uniDir is default.<br>
- Communication Mode between an enabled EnOcean device and Fhem.<br>
- Unidirectional communication means a point-to-multipoint communication
- relationship. The EnOcean device e. g. sensors does not know the unique
- Fhem SenderID.<br>
- If the attribute is set to confirm Fhem awaits confirmation telegrams from the remote device.<br>
- Bidirectional communication means a point-to-point communication
- relationship between an enabled EnOcean device and Fhem. It requires all parties
- involved to know the unique Sender ID of their partners. Bidirectional communication
- needs a teach-in / teach-out process, see <a href="#EnOcean_teach-in">Bidirectional Teach-In / Teach-Out</a>.
- </li>
- <li><a name="EnOcean_dataEnc">dataEnc</a> VAES|AES-CBC, [dataEnc] = VAES is default<br>
- Data encryption algorithm
- </li>
- <li><a name="EnOcean_defaultChannel">defaultChannel</a> <channel>
- subType actuator.01: [defaultChannel] = all|input|0 ... 29, all is default.<br>
- subType blindsCtrl.00, blindsCtrl.01: [defaultChannel] = all|1 ... 4, all is default.<br>
- Default device channel
- </li>
- <li><a name="EnOcean_daylightSavingTime">daylightSavingTime</a> supported|not_supported, [daylightSavingTime] = supported is default.<br>
- daylightSavingTime is supported for roomCtrlPanel.00.
- </li>
- <li><a name="EnOcean_demandRespAction">demandRespAction</a> <command><br>
- Command being executed after an demand response command is set. If <command> is enclosed in {},
- then it is a perl expression, if it is enclosed in "", then it is a shell command,
- else it is a "plain" fhem.pl command (chain). In the <command> you can access the demand response
- readings $TYPE, $NAME, $LEVEL, $SETPOINT, $POWERUSAGE, $POWERUSAGESCALE, $POWERUSAGELEVEL, $STATE. In addition,
- the variables $TARGETNAME, $TARGETTYPE, $TARGETSTATE can be used if the action is executed
- on the target device. This data is available as a local variable in perl, as environment variable for shell
- scripts, and will be textually replaced for Fhem commands.
- </li>
- <li><a name="EnOcean_demandRespMax">demandRespMax</a> A0|AI|B0|BI|C0|CI|D0|DI, [demandRespMax] = B0 is default<br>
- Switch command which is executed if the demand response switches to a maximum.
- </li>
- <li><a name="EnOcean_demandRespMin">demandRespMin</a> A0|AI|B0|BI|C0|CI|D0|DI, [demandRespMax] = BI is default<br>
- Switch command which is executed if the demand response switches to a minimum.
- </li>
- <li><a name="EnOcean_demandRespRefDev">demandRespRefDev</a> <name><br>
- </li>
- <li><a name="EnOcean_demandRespRandomTime">demandRespRandomTime</a> t/s [demandRespRandomTime] = 1 is default<br>
- Maximum length of the random delay at the start or end of a demand respose event in slave mode.
- </li>
- <li><a name="EnOcean_demandRespThreshold">demandRespThreshold</a> 0...15 [demandRespTheshold] = 8 is default<br>
- Threshold for switching the power usage level between minimum and maximum in the master mode.
- </li>
- <li><a name="EnOcean_demandRespTimeoutLevel">demandRespTimeoutLevel</a> max|last [demandRespTimeoutLevel] = max is default<br>
- Demand response timeout level in slave mode.
- </li>
- <li><a name="devChannel">devChannel</a> 00 ... FF, [devChannel] = FF is default<br>
- Number of the individual device channel, FF = all channels supported by the device
- </li>
- <li><a name="destinationID">destinationID</a> multicast|unicast|00000001 ... FFFFFFFF,
- [destinationID] = multicast is default<br>
- Destination ID, special values: multicast = FFFFFFFF, unicast = [DEF]
- </li>
- <li><a name="EnOcean_devMode">devMode</a> master|slave, [devMode] = master is default.<br>
- device operation mode.
- </li>
- <li><a href="#devStateIcon">devStateIcon</a></li>
- <li><a name="EnOcean_dimMax">dimMax</a> dim/%|off, [dimMax] = 255 is default.<br>
- maximum brightness value<br>
- dimMax is supported for the profile gateway/dimming.
- </li>
- <li><a name="EnOcean_dimMin">dimMin</a> dim/%|off, [dimMax] = off is default.<br>
- minimum brightness value<br>
- If [dimMax] = off, then the actuator takes down the ramp time set there.
- dimMin is supported for the profile gateway/dimming.
- </li>
- <li><a name="dimValueOn">dimValueOn</a> dim/%|last|stored,
- [dimValueOn] = 100 is default.<br>
- Dim value for the command "on".<br>
- The dimmer switched on with the value 1 % ... 100 % if [dimValueOn] =
- 1 ... 100.<br>
- The dimmer switched to the last dim value received from the
- bidirectional dimmer if [dimValueOn] = last.<br>
- The dimmer switched to the last Fhem dim value if [dimValueOn] =
- stored.<br>
- dimValueOn is supported for the profile gateway/dimming.
- </li>
- <li><a href="#EnOcean_disable">disable</a> 0|1<br>
- If applied set commands will not be executed.
- </li>
- <li><a href="#EnOcean_disabledForIntervals">disabledForIntervals</a> HH:MM-HH:MM HH:MM-HH-MM...<br>
- Space separated list of HH:MM tupels. If the current time is between
- the two time specifications, set commands will not be executed. Instead of
- HH:MM you can also specify HH or HH:MM:SS. To specify an interval
- spawning midnight, you have to specify two intervals, e.g.:
- <ul>
- 23:00-24:00 00:00-01:00
- </ul>
- </li>
- <li><a name="EnOcean_displayContent">displayContent</a>
- humidity|off|setpointTemp|temperatureExtern|temperatureIntern|time|default|no_change, [displayContent] = no_change is default.<br>
- displayContent is supported for roomCtrlPanel.00.
- </li>
- <li><a name="EnOcean_displayOrientation">displayOrientation</a> rad/°, [displayOrientation] = 0|90|180|270, 0 is default.<br>
- Display orientation of the actuator
- </li>
- <li><a href="#do_not_notify">do_not_notify</a></li>
- <li><a name="EnOcean_eep">eep</a> <00...FF>-<00...3F>-<00...7F><br>
- EnOcean Equipment Profile (EEP)
- <li><a href="#eventMap">eventMap</a></li>
- </li>
- <li><a name="EnOcean_gpDef">gpDef</a> <name of channel 00>:<O|I>:<channel type>:<signal type>:<value type>[:<resolution>[:<engineering min>:<scaling min>:<engineering max>:<scaling max>]] ...
- <name of channel 64>:<O|I>:<channel type>:<signal type>:<value type>[:<resolution>[:<engineering min>:<scaling min>:<engineering max>:<scaling max>]]
- <br>
- Generic Profiles channel definitions are set automatically in master mode. If the profile in slave mode is operated, the channel
- definition must be entered manually. For each channel, the channel definitions are to be given in ascending order. The channel
- parameters to be specified in decimal. First, the outgoing channels (direction = O) are to be defined, then the incoming channels
- (direction = I) should be described. The channel numbers are assigned automatically starting with 00th.
- </li>
- <li><a name="gwCmd">gwCmd</a> switching|dimming|setpointShift|setpointBasic|controlVar|fanStage|blindCmd<br>
- Gateway Command Type, see <a href="#Gateway">Gateway</a> profile
- </li>
- <li><a name="EnOcean_humidity">humidity</a> rH/%<br>
- The value of the actual humidity, used by a Room Sensor and Control Unit. Should by
- filled via a notify from a distinct humidity sensor.
- </li>
- <li><a name="EnOcean_humidityRefDev">humidityRefDev</a> <name><br>
- Name of the device whose reference value is read. The reference values is
- the reading humidity.
- </li>
- <li><a href="#ignore">ignore</a></li>
- <li><a href="#IODev">IODev</a></li>
- <li><a name="EnOcean_keyRcv">keyRcv</a> <private key 16 byte hex><br>
- Private Key for receive direction
- </li>
- <li><a name="EnOcean_keySnd">keySnd</a> <private key 16 byte hex><br>
- Private Key for send direction
- </li>
- <li><a name="EnOcean_macAlgo">macAlgo</a> no|3|4<br>
- MAC Algorithm
- </li>
- <li><a name="EnOcean_manufID">manufID</a> <000 ... 7FF><br>
- Manufacturer ID number
- </li>
- <li><a name="EnOcean_measurementCtrl">measurementCtrl</a> enable|disable<br>
- Enable or disable the temperature measurements (room and feed temperature) of the actuator. If the temperature
- measurements are turned off, an external temperature sensor must be exists, see attribute
- <a href="#temperatureRefDev">temperatureRefDev</a>.
- </li>
- <li><a href="#model">model</a></li>
- <li><a name="EnOcean_observe">observe</a> off|on, [observe] = off is default.<br>
- Observing and repeating the execution of set commands
- </li>
- <li><a name="EnOcean_observeCmdRepetition">observeCmdRepetition</a> 1..5, [observeCmdRepetition] = 2 is default.<br>
- Maximum number of command retries
- </li>
- <li><a name="EnOcean_observeErrorAction">observeErrorAction</a> <command><br>
- Command being executed after an error. If <command> is enclosed in {},
- then it is a perl expression, if it is enclosed in "", then it is a shell command,
- else it is a "plain" fhem.pl command (chain). In the <command> you can access the set
- command. $TYPE, $NAME, $FAILEDDEV, $EVENT, $EVTPART0, $EVTPART1, $EVTPART2, etc. contains the space separated
- set parts. The <a href="#eventMap">eventMap</a> replacements are taken into account. This data
- is available as a local variable in perl, as environment variable for shell
- scripts, and will be textually replaced for Fhem commands.
- </li>
- <li><a name="EnOcean_observeInterval">observeInterval</a> 1/s ... 255/s, [observeInterval] = 1 is default.<br>
- Interval between two observations
- </li>
- <li><a name="EnOcean_observeLogic">observeLogic</a> and|or, [observeLogic] = or is default.<br>
- Observe logic
- </li>
- <li><a name="EnOcean_observeRefDev">observeRefDev</a> <name> [<name> [<name>]],
- [observeRefDev] = <name of the own device> is default<br>
- Names of the devices to be observed. The list must be separated by spaces.
- </li>
- <li><a name="EnOcean_pidActorCallBeforeSetting">pidActorCallBeforeSetting</a>,
- [pidActorCallBeforeSetting] = not defined is default<br>
- Callback-function, which can manipulate the actorValue. Further information see modul PID20.
- </li>
- <li><a name="EnOcean_pidActorErrorAction">pidActorErrorAction</a> freeze|errorPos,
- [pidActorErrorAction] = freeze is default<br>
- required action on error
- </li>
- <li><a name="EnOcean_pidActorErrorPos">pidActorErrorPos</a> valvePos/%,
- [pidActorErrorPos] = 0...100, 0 is default<br>
- actor's position to be used in case of error
- </li>
- <li><a name="EnOcean_pidActorLimitLower">pidActorLimitLower</a> valvePos/%,
- [pidActorLimitLower] = 0...100, 0 is default<br>
- lower limit for actor
- </li>
- <li><a name="EnOcean_pidActorLimitUpper">pidActorLimitUpper</a> valvePos/%,
- [pidActorLimitUpper] = 0...100, 100 is default<br>
- upper limit for actor
- </li>
- <li><a name="EnOcean_pidCtrl">pidCtrl</a> on|off,
- [pidCtrl] = on is default<br>
- Activate the Fhem PID regulator
- </li>
- <li><a name="EnOcean_pidDeltaTreshold">pidDeltaTreshold</a> <floating-point number>,
- [pidDeltaTreshold] = 0 is default<br>
- if delta < delta-threshold the pid will enter idle state
- </li>
- <li><a name="EnOcean_pidFactor_P">pidFactor_P</a> <floating-point number>,
- [pidFactor_P] = 25 is default<br>
- P value for PID
- </li>
- <li><a name="EnOcean_pidFactor_I">pidFactor_I</a> <floating-point number>,
- [pidFactor_I] = 0.25 is default<br>
- I value for PID
- </li>
- <li><a name="EnOcean_pidFactor_D">pidFactor_D</a> <floating-point number>,
- [pidFactor_D] = 0 is default<br>
- D value for PID
- </li>
- <li><a name="EnOcean_pidIPortionCallBeforeSetting">pidIPortionCallBeforeSetting</a>
- [pidIPortionCallBeforeSetting] = not defined is default<br>
- Callback-function, which can manipulate the value of I-Portion. Further information see modul PID20.
- </li>
- <li><a name="EnOcean_pidSensorTimeout">pidSensorTimeout t/s</a>
- [pidSensorTimeout] = 3600 is default<br>
- number of seconds to wait before sensor <a href="#temperatureRefDev">temperatureRefDev</a> will be recognized n/a
- </li>
- <li><a name="EnOcean_pollInterval">pollInterval</a> t/s, [pollInterval] = 10 is default.<br>
- [pollInterval] = 1 ... 1440.<br>
- pollInterval is supported for roomCtrlPanel.00.
- </li>
- <li><a name="EnOcean_rampTime">rampTime</a> t/s or relative, [rampTime] = 1 is default.<br>
- No ramping or for Eltako dimming speed set on the dimmer if [rampTime] = 0.<br>
- Gateway/dimmung: Ramping time 1 s to 255 s or relative fast to low dimming speed if [rampTime] = 1 ... 255.<br>
- lightCtrl.01: Ramping time 1 s to 65535 s<br>
- rampTime is supported for gateway, command dimming and lightCtrl.01.
- </li>
- <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
- <li><a name="EnOcean_rcvRespAction">rcvRespAction</a> <command><br>
- Command being executed after an message from the aktor is received and before an response message is sent.
- If <command> is enclosed in {}, then it is a perl expression, if it is enclosed in "", then it is a shell command,
- else it is a "plain" fhem.pl command (chain). In the <command> you can access the name of the device by using $NAME
- and the current readings $ACTUATORSTATE, $BATTERY, $COVER, $ENERGYINPUT, $ENERGYSTORAGE, $MAINTENANCEMODE, $OPERATIONMODE,
- $ROOMTEMP, $SELFCTRL, $SETPOINT, $SETPOINTTEMP, $SUMMERMODE, $TEMPERATURE, $WINDOW for the subType hvac.01 and $NAME,
- $BATTERY, $FEEDTEMP, $MAINTENANCEMODE, $OPERATIONMODE, $ROOMTEMP, $SETPOINT, $SETPOINTTEMP, $SUMMERMODE, $TEMPERATURE
- for the subType hvac.04.
- This data is available as a local variable in perl, as environment variable for shell
- scripts, and will be textually replaced for Fhem commands.
- </li>
- <li><a name="EnOcean_remoteCode">remoteCode</a> <00000000...FFFFFFFE><br>
- Remote Management Security Code, 00000000 is interpreted as on code has been set.
- </li>
- <li><a name="EnOcean_remoteEEP">remoteEEP</a> <00...FF>-<00...3F>-<00...7F><br>
- Remote Management EnOcean Equipment Profile (EEP)
- </li>
- <li><a name="EnOcean_remoteID">remoteID</a> <00000001...FFFFFFFE><br>
- Remote Management Remote Device ID
- </li>
- <li><a name="EnOcean_remoteManagement">remoteManagement</a> client|manager|off,
- [remoteManagement] = off is default.<br>
- Enable Remote Management for the device.
- </li>
- <li><a name="EnOcean_remoteManufID">remoteManufID</a> <000...7FF><br>
- Remote Management Manufacturer ID
- </li>
- <li><a name="repeatingAllowed">repeatingAllowed</a> yes|no,
- [repeatingAllowed] = yes is default.<br>
- EnOcean Repeater in the transmission range of Fhem may forward data messages
- of the device, if the attribute is set to yes.
- </li>
- <li><a name="EnOcean_releasedChannel">releasedChannel</a> A|B|C|D|I|0|auto, [releasedChannel] = auto is default.<br>
- Attribute releasedChannel determines via which SenderID (subDefA ... subDef0) the command released is sent.
- If [releasedChannel] = auto, the SenderID the last command A0, AI, B0, BI, C0, CI, D0 or DI is used.
- Attribute releasedChannel is supported for attr switchType = central and attr switchType = channel.
- </li>
- <li><a name="EnOcean_reposition">reposition</a> directly|opens|closes, [reposition] = directly is default.<br>
- Attribute reposition specifies how to adjust the internal positioning tracker before going to the new position.
- </li>
- <li><a name="EnOcean_rlcAlgo">rlcAlgo</a> 2++|3++<br>
- RLC Algorithm
- </li>
- <li><a name="EnOcean_rlcRcv">rlcRcv</a> <rolling code 2 or 3 byte hex><br>
- Rolling Code for receive direction
- </li>
- <li><a name="EnOcean_rlcSnd">rlcSnd</a> <rolling code 2 or 3 byte hex><br>
- Rolling Code for send direction
- </li>
- <li><a name="EnOcean_rlcTX">rlcTX</a> false|true<br>
- Rolling Code is expected in the received telegram
- </li>
- <li><a name="EnOcean_rltRepeat">rltRepeat</a> 16|32|64|128|256,
- [rltRepeat] = 16 is default.<br>
- Number of RLT MasterTest messages sent
- </li>
- <li><a name="EnOcean_rltType">rltType</a> 1BS|4BS,
- [rltType] = 4BS is default.<br>
- Type of RLT MasterTest message
- </li>
- <li><a name="scaleDecimals">scaleDecimals</a> 0 ... 9<br>
- Decimal rounding with x digits of the scaled reading setpoint
- </li>
- <li><a name="EnOcean_teachMethod">teachMethod</a> 1BS|4B|confirm|GP|RPS|smartAck|STE|UTE<br>
- teach-in method
- </li>
- <li><a name="scaleMax">scaleMax</a> <floating-point number><br>
- Scaled maximum value of the reading setpoint
- </li>
- <li><a name="scaleMin">scaleMin</a> <floating-point number><br>
- Scaled minimum value of the reading setpoint
- </li>
- <li><a name="EnOcean_secLevel">secLevel</a> encapsulation|encryption|off, [secLevel] = off is default<br>
- Security level of the data
- </li>
- <li><a name="EnOcean_secMode">secMode</a> rcv|snd|bidir<br>
- Telegram direction, which is secured
- </li>
- <li><a name="EnOcean_sendDevStatus">sendDevStatus</a> no|yes, [sendDevStatus] = no is default.<br>
- Send new status of the device.
- </li>
- <li><a name="sensorMode">sensorMode</a> switch|pushbutton,
- [sensorMode] = switch is default.<br>
- The status "released" will be shown in the reading state if the
- attribute is set to "pushbutton".
- </li>
- <li><a name="EnOcean_serviceOn">serviceOn</a> no|yes,
- [serviceOn] = no is default.<br>
- Device in Service Mode.
- </li>
- <li><a name="EnOcean_setCmdTrigger">setCmdTrigger</a> man|refDev, [setCmdTrigger] = man is default.<br>
- Operation mode to send set commands<br>
- If the attribute is set to "refDev", a device-specific set command is sent when the reference device is updated.
- For the subType "roomSensorControl.05" and "fanCrtl.00" the reference "temperatureRefDev" is supported.<br>
- For the subType "roomSensorControl.01" the references "humidityRefDev" and "temperatureRefDev" are supported.<br>
- </li>
- <li><a name="EnOcean_setpointRefDev">setpointRefDev</a> <name><br>
- Name of the device whose reference value is read. The reference values is
- the reading setpoint.
- </li>
- <li><a name="EnOcean_setpointSummerMode">setpointSummerMode</a> valvePos/%,
- [setpointSummerMode] = 0...100, 0 is default<br>
- Valve position in summer operation
- </li>
- <li><a name="EnOcean_setpointTempRefDev">setpointTempRefDev</a> <name><br>
- Name of the device whose reference value is read. The reference values is
- the reading setpointTemp.
- </li>
- <li><a name="EnOcean_settingAccuracy">settingAccuracy</a> high|low,
- [settingAccuracy] = low is default.<br>
- set setting accurancy.
- </li>
- <li><a href="#showtime">showtime</a></li>
- <li><a name="shutTime">shutTime</a> <channel1>[:<channel2>[:<channel3>[:<channel4>]]]<br>
- subType blindsCtrl.00, blindsCtrl.01: [shutTime] = 5 ... 300, 300 is default.<br>
- subType manufProfile: [shutTime] = 1 ... 255, 255 is default.<br>
- Use the attr shutTime to set the time delay to the position "Halt" in
- seconds. Select a delay time that is at least as long as the shading element
- or roller shutter needs to move from its end position to the other position.<br>
- Notice subType blindsCrtl.00: The attribute can only be set while the actuator is online.
- </li>
- <li><a name="shutTimeCloses">shutTimeCloses</a> t/s, [shutTimeCloses] = 1 ... 255,
- [shutTimeCloses] = [shutTime] is default.<br>
- Set the attr shutTimeCloses to define the runtime used by the commands opens and closes.
- Select a runtime that is at least as long as the value set by the delay switch of the actuator.
- <br>
- shutTimeCloses is supported for shutter.
- </li>
- <li><a name="subDef">subDef</a> <EnOcean SenderID>,
- [subDef] = [DEF] is default.<br>
- SenderID (<a href="#TCM">TCM</a> BaseID + offset) to control a bidirectional switch or actor.<br>
- In order to control devices that send acknowledge telegrams, you cannot reuse the ID of this
- devices, instead you have to create your own, which must be in the
- allowed ID-Range of the underlying IO device. For this first query the
- <a href="#TCM">TCM</a> with the "<code>get <tcm> idbase</code>" command. You can use
- up to 128 IDs starting with the base shown there.<br>
- If [subDef] = getNextID FHEM can assign a free SenderID alternatively. The system configuration
- needs to be reloaded. The assigned SenderID will only displayed after the system configuration
- has been reloaded, e.g. Fhem command rereadcfg.
- </li>
- <li><a name="subDefA">subDefA</a> <EnOcean SenderID>,
- [subDefA] = [subDef] is default.<br>
- SenderID (<a href="#TCM">TCM</a> BaseID + offset) for [value] = A0|AI|released<br>
- Used with switch type "channel". Set attr switchType to channel.<br>
- subDefA is supported for switches.<br>
- Second action is not sent.<br>
- If [subDefA] = getNextID FHEM can assign a free SenderID alternatively. The assigned SenderID will only
- displayed after the system configuration has been reloaded, e.g. Fhem command rereadcfg.
- </li>
- <li><a name="subDefB">subDefB</a> <EnOcean SenderID>,
- [subDefB] = [subDef] is default.<br>
- SenderID (<a href="#TCM">TCM</a> BaseID + offset) for [value] = B0|BI|released<br>
- Used with switch type "channel". Set attr switchType to channel.<br>
- subDefB is supported for switches.<br>
- Second action is not sent.<br>
- If [subDefB] = getNextID FHEM can assign a free SenderID alternatively. The assigned SenderID will only
- displayed after the system configuration has been reloaded, e.g. Fhem command rereadcfg.
- </li>
- <li><a name="subDefC">subDefC</a> <EnOcean SenderID>,
- [subDefC] = [subDef] is default.<br>
- SenderID (<a href="#TCM">TCM</a> BaseID + offset) for [value] = C0|CI|released<br>
- Used with switch type "channel". Set attr switchType to channel.<br>
- subDefC is supported for switches.<br>
- Second action is not sent.<br>
- If [subDefC] = getNextID FHEM can assign a free SenderID alternatively. The assigned SenderID will only
- displayed after the system configuration has been reloaded, e.g. Fhem command rereadcfg.
- </li>
- <li><a name="subDefD">subDefD</a> <EnOcean SenderID>,
- [subDefD] = [subDef] is default.<br>
- SenderID (<a href="#TCM">TCM</a> BaseID + offset) for [value] = D0|DI|released<br>
- Used with switch type "channel". Set attr switchType to channel.<br>
- subDefD is supported for switches.<br>
- Second action is not sent.<br>
- If [subDefD] = getNextID FHEM can assign a free SenderID alternatively. The assigned SenderID will only
- displayed after the system configuration has been reloaded, e.g. Fhem command rereadcfg.
- </li>
- <li><a name="subDef0">subDef0</a> <EnOcean SenderID>,
- [subDef0] = [subDef] is default.<br>
- SenderID (<a href="#TCM">TCM</a> BaseID + offset) for [value] = A0|B0|C0|D0|released<br>
- Used with switch type "central". Set attr switchType to central.<br>
- Use the sensor type "zentral aus/ein" for Eltako devices.<br>
- subDef0 is supported for switches.<br>
- Second action is not sent.<br>
- If [subDef0] = getNextID FHEM can assign a free SenderID alternatively. The assigned SenderID will only
- displayed after the system configuration has been reloaded, e.g. Fhem command rereadcfg.
- </li>
- <li><a name="subDefI">subDefI</a> <EnOcean SenderID>,
- [subDefI] = [subDef] is default.<br>
- SenderID (<a href="#TCM">TCM</a> BaseID + offset) for [value] = AI|BI|CI|DI<br>
- Used with switch type "central". Set attr switchType to central.<br>
- Use the sensor type "zentral aus/ein" for Eltako devices.<br>
- subDefI is supported for switches.<br>
- Second action is not sent.<br>
- If [subDefI] = getNextID FHEM can assign a free SenderID alternatively. The assigned SenderID will only
- displayed after the system configuration has been reloaded, e.g. Fhem command rereadcfg.
- </li>
- <li><a name="EnOcean_subDefH">subDefH</a> <EnOcean SenderID>,
- [subDefH] = undef is default.<br>
- SenderID (<a href="#TCM">TCM</a> BaseID + offset)<br>
- Used with subType "multisensor.00". If the attribute subDefH is set, the position of the window handle as EEP F6-10-00
- (windowHandle) telegram is forwarded.<br>
- If [subDefH] = getNextID FHEM can assign a free SenderID alternatively.
- </li>
- <li><a name="EnOcean_subDefW">subDefW</a> <EnOcean SenderID>,
- [subDefW] = undef is default.<br>
- SenderID (<a href="#TCM">TCM</a> BaseID + offset)<br>
- Used with subType "multisensor.00". If the attribute subDefW is set, the window state as EEP D5-00-01
- (contact) telegram is forwarded.<br>
- If [subDefW] = getNextID FHEM can assign a free SenderID alternatively.
- </li>
- <li><a href="#subType">subType</a></li>
- <li><a name="subTypeSet">subTypeSet</a> <type of device>, [subTypeSet] = [subType] is default.<br>
- Type of device (EEP Profile) used for sending commands. Set the Attribute manually.
- The profile has to fit their basic profile. More information can be found in the basic profiles.
- </li>
- <li><a name="EnOcean_summerMode">summerMode</a> off|on,
- [summerMode] = off is default.<br>
- Put Battery Powered Actuator (hvac.01) or Heating Radiator Actuating Drive (hvac.04) in summer operation
- to reduce energy consumption. If [summerMode] = on, the set commands are not executed.
- </li>
- <li><a name="EnOcean_switchHysteresis">switchHysteresis</a> <value>,
- [switchHysteresis] = 1 is default.<br>
- Switch Hysteresis
- </li>
- <li><a name="switchMode">switchMode</a> switch|pushbutton,
- [switchMode] = switch is default.<br>
- The set command "released" immediately after <value> is sent if the
- attribute is set to "pushbutton".
- </li>
- <li><a name="switchType">switchType</a> direction|universal|central|channel,
- [switchType] = direction is default.<br>
- EnOcean Devices support different types of sensors, e. g. direction
- switch, universal switch or pushbutton, central on/off.<br>
- For Eltako devices these are the sensor types "Richtungstaster",
- "Universalschalter" or "Universaltaster", "Zentral aus/ein".<br>
- With the sensor type <code>direction</code> switch on/off commands are
- accepted, e. g. B0, BI, released. Fhem can control an device with this
- sensor type unique. This is the default function and should be
- preferred.<br>
- Some devices only support the <code>universal switch
- </code> or <code>pushbutton</code>. With a Fhem command, for example,
- B0 or BI is switched between two states. In this case Fhem cannot
- control this device unique. But if the Attribute <code>switchType
- </code> is set to <code>universal</code> Fhem synchronized with
- a bidirectional device and normal on/off commands can be used.
- If the bidirectional device response with the channel B
- confirmation telegrams also B0 and BI commands are to be sent,
- e g. channel A with A0 and AI. Also note that confirmation telegrams
- needs to be sent.<br>
- Partly for the switchType <code>central</code> two different SenderID
- are required. In this case set the Attribute <code>switchType</code> to
- <code>central</code> and define the Attributes
- <a href="#subDef0">subDef0</a> and <a href="#subDefI">subDefI</a>.<br>
- Furthermore, SenderIDs can be used depending on the channel A, B, C or D.
- In this case set the Attribute switchType to <code>channel</code> and define
- the Attributes <a href="#subDefA">subDefA</a>, <a href="#subDefB">subDefB</a>,
- <a href="#subDefC">subDefC</a>, or <a href="#subDefD">subDefD</a>.
- </li>
- <li><a name="temperatureRefDev">temperatureRefDev</a> <name><br>
- Name of the device whose reference value is read. The reference values is
- the reading temperature.
- </li>
- <li><a name="EnOcean_temperatureScale">temperatureScale</a> F|C|default|no_change, [temperatureScale] = no_change is default.<br>
- temperatureScale is supported for roomCtrlPanel.00.
- </li>
- <li><a name="EnOcean_timeNotation">timeNotation</a> 12|24|default|no_change, [timeNotation] = no_change is default.<br>
- timeNotation is supported for roomCtrlPanel.00.
- </li>
- <li><a name="EnOcean_timeProgram[1-4]">timeProgram[1-4]</a> <period> <starttime> <endtime> <roomCtrlMode>, [timeProgam[1-4]] = <none> is default.<br>
- [period] = FrMo|FrSu|ThFr|WeFr|TuTh|MoWe|SaSu|MoFr|MoSu|Su|Sa|Fr|Th|We|Tu|Mo<br>
- [starttime] = [00..23]:[00|15|30|45]<br>
- [endtime] = [00..23]:[00|15|30|45]<br>
- [roomCtrlMode] = buildingProtection|comfort|economy|preComfort<br>
- The Room Control Panel Kieback & Peter RBW322-FTL supports only [roomCtrlMode] = comfort.<br>
- timeProgram is supported for roomCtrlPanel.00.
- </li>
- <li><a name="EnOcean_trackerWakeUpCycle">trackerWakeUpCycle</a> t/s, [wakeUpCycle] =10 s, 20 s, 30 s, 40 s, 60 s, 120 s, 180 s, 240 s, 3600, 86400 s, 30 s is default.<br>
- Transmission cycle of the tracker.
- </li>
- <li><a name="EnOcean_updateState">updateState</a> default|yes|no, [updateState] = default is default.<br>
- update reading state after set commands
- </li>
- <li><a name="EnOcean_uteResponseRequest">uteResponseRequest</a> yes|no<br>
- request UTE teach-in/teach-out response message, the standard value depends on the EEP profil
- </li>
- <li><a href="#verbose">verbose</a></li>
- <li><a name="EnOcean_wakeUpCycle">wakeUpCycle</a> t/s, [wakeUpCycle] = 10 s ... 151200 s, 300 s is default.<br>
- Transmission cycle of the actuator.
- </li>
- <li><a href="#webCmd">webCmd</a></li>
- </ul>
- </ul>
- <br>
- <a name="EnOceanevents"></a>
- <b>Generated events</b>
- <ul>
- <ul>
- <li><a name="EnOcean_remoteEvents">Remote Management</a><br>
- <ul>
- <li>remoteDevCfg<0000...FFFF>: <device config></li>
- <li>remoteFunction<01...99>: <remote function number>:<remote manufacturer ID>:<explanation></li>
- <li>remoteLastFunctionNumber: 001...FFF</li>
- <li>remoteLastStatusReturnCode: 00...FF</li>
- <li>remoteLearn: not_supported|supported</li>
- <li>remoteLinkCfg<in|out><00...FF>: <data index>:<device config></li>
- <li>remoteLinkTableDesc<in|out><00...FF>: <DeviceID>:<EEP>:<channel></li>
- <li>remoteLinkTableGPDesc<in|out><00...FF>: <name of channel 00>:<O|I>:<channel type>:<signal type>:<value type>[:<resolution>[:<engineering min>:<scaling min>:<engineering max>:<scaling max>]]</li>
- <li>remoteProductID: 00000000...FFFFFFFF</li>
- <li>remoteRepeaterFilter: AND|OR</li>
- <li>remoteRepeaterFunction: on|off|filter</li>
- <li>remoteRepeaterLevel: 1|2</li>
- <li>remoteTeach: not_supported|supported</li>
- <li>remoteRSSI: LP/dBm</li>
- <li>teach: <result of teach procedure></li>
- </ul>
- </li>
- <br><br>
- <li>Switch (EEP F6-02-01 ... F6-03-02)<br>
- <ul>
- <li>A0</li>
- <li>AI</li>
- <li>B0</li>
- <li>BI</li>
- <li>C0</li>
- <li>CI</li>
- <li>D0</li>
- <li>DI</li>
- <li><BtnX,BtnY> First and second action where BtnX and BtnY is
- one of the above, e.g. A0 BI or D0 CI</li>
- <li>buttons: pressed|released</li>
- <li>state: <BtnX>[,<BtnY>]</li>
- </ul><br>
- Switches (remote controls) or actors with more than one
- (pair) keys may have multiple channels e. g. B0/BI, A0/AI with one
- SenderID or with separate addresses.
- </li>
- <br><br>
- <li>Pushbutton Switch, Pushbutton Input Module (EEP F6-02-01 ... F6-02-02, F6-01-01)<br>
- [Eltako FT55, FSM12, FSM61, FTS12]<br>
- <ul>
- <li>A0</li>
- <li>AI</li>
- <li>B0</li>
- <li>BI</li>
- <li>C0</li>
- <li>CI</li>
- <li>D0</li>
- <li>DI</li>
- <li><BtnX,BtnY> First and second action where BtnX and BtnY is
- one of the above, e.g. A0,BI or D0,CI</li>
- <li>released</li>
- <li>buttons: pressed|released</li>
- <li>state: <BtnX>[,<BtnY>] [released]</li>
- </ul><br>
- The status of the device may become "released", this is not the case for a normal switch.<br>
- Set attr model to Eltako_FT55|FSM12|FSM61|FTS12 or attr sensorMode to pushbutton manually.
- </li>
- <br><br>
- <li>Pushbutton Switch (EEP F6-3F-7F)<br>
- [Eltako FGW14/FAM14 with internal decryption and RS-485 communication]<br>
- <ul>
- <li>A0</li>
- <li>AI</li>
- <li>B0</li>
- <li>BI</li>
- <li>C0</li>
- <li>CI</li>
- <li>D0</li>
- <li>DI</li>
- <li><BtnX,BtnY> First and second action where BtnX and BtnY is
- one of the above, e.g. A0,BI or D0,CI</li>
- <li>released</li>
- <li>buttons: pressed|released</li>
- <li>state: <BtnX>[,<BtnY>] [released]</li>
- </ul><br>
- Set attr subType to switch.7F and manufID to 00D.<br>
- The status of the device may become "released", this is not the case for
- a normal switch. Set attr sensorMode to pushbutton manually.
- </li>
- <br><br>
- <li>Pushbutton Switch (EEP D2-03-00)<br>
- [EnOcean PTM 215 Modul]<br>
- <ul>
- <li>A0</li>
- <li>AI</li>
- <li>B0</li>
- <li>BI</li>
- <li><BtnX,BtnY> First and second action where BtnX and BtnY is
- one of the above, e.g. A0,BI</li>
- <li>pressed</li>
- <li>released</li>
- <li>teach: <result of teach procedure></li>
- <li>energyBow: pressed|released</li>
- <li>state: <BtnX>|<BtnX>,<BtnY>|released|pressed|teachIn|teachOut</li>
- </ul><br>
- The attr subType must be switch.00. This is done if the device was
- created by autocreate. Set attr sensorMode to pushbutton manually if needed.
- </li>
- <br><br>
- <li>Heating/Cooling Relay (EEP F6-02-01 ... F6-02-02)<br>
- [Eltako FAE14, FHK14, untested]<br>
- <ul>
- <li>controllerMode: auto|off</li>
- <li>energyHoldOff: normal|holdoff</li>
- <li>buttons: pressed|released</li>
- </ul><br>
- Set attr subType to switch and model to Eltako_FAE14|FHK14 manually. In addition
- every telegram received from a teached-in temperature sensor (e.g. FTR55H)
- is repeated as a confirmation telegram from the Heating/Cooling Relay
- FAE14, FHK14. In this case set attr subType to e. g. roomSensorControl.05
- and attr manufID to 00D.
- </li>
- <br><br>
- <li>Key Card Activated Switch (EEP F6-04-01)<br>
- [Eltako FKC, FKF, FZS, untested]<br>
- <ul>
- <li>keycard_inserted</li>
- <li>keycard_removed</li>
- <li>state: keycard_inserted|keycard_removed</li>
- </ul><br>
- Set attr subType to keycard manually.
- </li>
- <br><br>
- <li>Wind Speed Threshold Detector (EEP F6-05-00)<br>
- <ul>
- <li>on</li>
- <li>off</li>
- <li>alarm: dead_sensor|off</li>
- <li>windSpeed: dead_sensor|on|off</li>
- <li>battery: low|ok</li>
- <li>state: on|off</li>
- </ul><br>
- Set attr subType to windSpeed.00 manually.
- </li>
- <br><br>
- <li>Liquid Leakage Sensor (EEP F6-05-01)<br>
- [untested]<br>
- <ul>
- <li>dry</li>
- <li>wet</li>
- <li>state: dry|wet</li>
- </ul><br>
- Set attr subType to liquidLeakage manually.
- </li>
- <br><br>
- <li>Smoke Detector (EEP F6-05-02)<br>
- [Eltako FRW]<br>
- <ul>
- <li>smoke-alarm</li>
- <li>off</li>
- <li>alarm: dead_sensor|smoke-alarm|off</li>
- <li>battery: low|ok</li>
- <li>state: smoke-alarm|off</li>
- </ul><br>
- Set attr subType to smokeDetector.02 manually.
- </li>
- <br><br>
- <li>Window Handle (EEP F6-10-00, D2-03-10)<br>
- [HOPPE SecuSignal, Eltako FHF, Eltako FTKE]<br>
- <ul>
- <li>closed</li>
- <li>open</li>
- <li>tilted</li>
- <li>open_from_tilted</li>
- <li>state: closed|open|tilted|open_from_tilted</li>
- </ul><br>
- The device windowHandle or windowHandle.10 should be created by autocreate.
- </li>
- <br><br>
- <li>Single Input Contact, Door/Window Contact<br>
- 1BS Telegram (EEP D5-00-01)<br>
- [EnOcean STM 320, STM 329, STM 250, Eltako FTK, Peha D 450 FU]
- <ul>
- <li>closed</li>
- <li>open</li>
- <li>teach: <result of teach procedure></li>
- <li>state: open|closed</li>
- </ul></li>
- The device should be created by autocreate.
- <br><br>
- <li>Temperature Sensors with with different ranges (EEP A5-02-01 ... A5-02-30)<br>
- [EnOcean STM 330, Eltako FTF55, Thermokon SR65 ...]<br>
- <ul>
- <li>t/°C</li>
- <li>temperature: t/°C (Sensor Range: t = <t min> °C ... <t max> °C)</li>
- <li>state: t/°C</li>
- </ul><br>
- The attr subType must be tempSensor.01 ... tempSensor.30. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Temperatur and Humidity Sensor (EEP A5-04-02)<br>
- [Eltako FAFT60, FIFT63AP]<br>
- <ul>
- <li>T: t/°C H: rH/% B: unknown|low|ok</li>
- <li>battery: unknown|low|ok</li>
- <li>energyStorage: unknown|empty|charged|full</li>
- <li>humidity: rH/% (Sensor Range: rH = 0 % ... 100 %)</li>
- <li>temperature: t/°C (Sensor Range: t = -20 °C ... 60 °C)</li>
- <li>voltage: U/V</li> (Sensor Range: U = 0 V ... 6.6 V)
- <li>state: T: t/°C H: rH/% B: unknown|low|ok</li>
- </ul><br>
- The attr subType must be tempHumiSensor.02 and attr
- manufID must be 00D for Eltako Devices. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Temperatur and Humidity Sensor (EEP A5-04-03)<br>
- [untsted]<br>
- <ul>
- <li>T: t/°C H: rH/%</li>
- <li>humidity: rH/% (Sensor Range: rH = 0 % ... 100 %)</li>
- <li>telegramType: heartbeat|event</li>
- <li>temperature: t/°C (Sensor Range: t = -20 °C ... 60 °C)</li>
- <li>state: T: t/°C H: rH/%</li>
- </ul><br>
- The attr subType must be tempHumiSensor.03. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Barometric Sensor (EEP A5-05-01)<br>
- [untested]<br>
- <ul>
- <li>P/hPa</li>
- <li>airPressure: P/hPa (Sensor Range: P = 500 hPa ... 1150 hPa</li>
- <li>telegramType: heartbeat|event</li>
- <li>state: P/hPa</li>
- </ul><br>
- The attr subType must be baroSensor.01. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Light Sensor (EEP A5-06-01)<br>
- [Eltako FAH60, FAH63, FIH63, Thermokon SR65 LI]<br>
- <ul>
- <li>E/lx</li>
- <li>brightness: E/lx (Sensor Range: 300 lx ... 30 klx, 600 lx ... 60 klx
- , Sensor Range for Eltako: E = 0 lx ... 100 lx, 300 lx ... 30 klx)</li>
- <li>voltage: U/V</li> (Sensor Range: U = 0 V ... 5.1 V)
- <li>state: E/lx</li>
- </ul><br>
- Eltako devices only support Brightness.<br>
- The attr subType must be lightSensor.01 and attr manufID must be 00D
- for Eltako Devices. This is done if the device was created by
- autocreate.
- </li>
- <br><br>
- <li>Light Sensor (EEP A5-06-02)<br>
- [untested]<br>
- <ul>
- <li>E/lx</li>
- <li>brightness: E/lx (Sensor Range: 0 lx ... 1020 lx</li>
- <li>voltage: U/V (Sensor Range: U = 0 V ... 5.1 V)</li>
- <li>state: E/lx</li>
- </ul><br>
- The attr subType must be lightSensor.02. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Light Sensor (EEP A5-06-03)<br>
- [untested]<br>
- <ul>
- <li>E/lx</li>
- <li>brightness: E/lx (Sensor Range: E = 0 lx ... 1000 lx, over range)</li>
- <li>errorCode: 251 ... 255</li>
- <li>state: E/lx</li>
- </ul><br>
- The attr subType must be lightSensor.03. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Light Sensor (EEP A5-06-04)<br>
- [untested]<br>
- <ul>
- <li>T: t/°C E: E/lx B: ok|low</li>
- <li>battery: ok|low</li>
- <li>brightness: E/lx (Sensor Range: E = 0 lx ... 65535 lx)</li>
- <li>energyStorage: 1/%</li>
- <li>temperature: t/°C (Sensor Range: t = -20 °C ... 60 °C)</li>
- <li>state: T: t/°C E: E/lx B: ok|low</li>
- </ul><br>
- The attr subType must be lightSensor.04. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Light Sensor (EEP A5-06-05)<br>
- [untested]<br>
- <ul>
- <li>E/lx</li>
- <li>brightness: E/lx (Sensor Range: 0 lx ... 10200 lx</li>
- <li>voltage: U/V (Sensor Range: U = 0 V ... 5.1 V)</li>
- <li>state: E/lx</li>
- </ul><br>
- The attr subType must be lightSensor.05. This is done if the device was created by autocreate.
- </li>
- <br><br>
- <li>Occupancy Sensor (EEP A5-07-01, A5-07-02)<br>
- [EnOcean EOSW]<br>
- <ul>
- <li>on|off</li>
- <li>battery: ok|low</li>
- <li>button: pressed|released</li>
- <li>current: I/µA (Sensor Range: I = 0 V ... 127.0 µA)</li>
- <li>errorCode: 251 ... 255</li>
- <li>motion: on|off</li>
- <li>sensorType: ceiling|wall</li>
- <li>voltage: U/V (Sensor Range: U = 0 V ... 5.0 V)</li>
- <li>state: on|off</li>
- </ul><br>
- The attr subType must be occupSensor.<01|02>. This is done if the device was
- created by autocreate. Current is the solar panel current. Some values are
- displayed only for certain types of devices.
- </li>
- <br><br>
- <li>Eltako/PioTek-Tracker TF-TTB (EEP A5-07-01)<br>
- <ul>
- <li>on|off</li>
- <li>battery: ok|low</li>
- <li>button: pressed|released</li>
- <li>motion: on|off</li>
- <li>voltage: U/V (Sensor Range: U = 0 V ... 5.0 V)</li>
- <li>state: on|off</li>
- </ul><br>
- The attr subType must be occupSensor.01. This is done if the device was
- created by autocreate. The attr model has to be set manually to tracker.
- Alternatively, the profile will be defined with inofficial EEP G5-07-01.<br>
- The transmission cycle is set using the attribute <a href="#EnOcean_trackerWakeUpCycle">trackerWakeUpCycle</a>.
- </li>
- <br><br>
- <li>Occupancy Sensor (EEP A5-07-03)<br>
- [untested]<br>
- <ul>
- <li>M: on|off E: E/lx U: U/V</li>
- <li>battery: ok|low</li>
- <li>brightness: E/lx (Sensor Range: E = 0 lx ... 1000 lx, over range)</li>
- <li>errorCode: 251 ... 255</li>
- <li>motion: on|off</li>
- <li>voltage: U/V (Sensor Range: U = 0 V ... 5.0 V)</li>
- <li>state: M: on|off E: E/lx U: U/V</li>
- </ul><br>
- The attr subType must be occupSensor.03. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Light, Temperatur and Occupancy Sensor (EEP A5-08-01 ... A5-08-03)<br>
- [Eltako FABH63, FBH55, FBH63, FIBH63, Thermokon SR-MDS, PEHA 482 FU-BM DE]<br>
- <ul>
- <li>M: on|off E: E/lx P: absent|present T: t/°C U: U/V</li>
- <li>brightness: E/lx (Sensor Range: E = 0 lx ... 510, 1020, 1530 or 2048 lx)</li>
- <li>motion: on|off</li>
- <li>presence: absent|present</li>
- <li>temperature: t/°C (Sensor Range: t = 0 °C ... 51 °C or -30 °C ... 50 °C)</li>
- <li>voltage: U/V</li> (Sensor Range: U = 0 V ... 5.1 V)
- <li>state: M: on|off E: E/lx P: absent|present T: t/°C U: U/V</li>
- </ul><br>
- Eltako and PEHA devices only support Brightness and Motion.<br>
- The attr subType must be lightTempOccupSensor.<01|02|03> and attr
- manufID must be 00D for Eltako Devices. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Gas Sensor, CO Sensor (EEP A5-09-01)<br>
- [untested]<br>
- <ul>
- <li>CO: c/ppm (Sensor Range: c = 0 ppm ... 255 ppm)</li>
- <li>temperature: t/°C (Sensor Range: t = 0 °C ... 255 °C)</li>
- <li>state: c/ppm</li>
- </ul><br>
- The attr subType must be COSensor.01. This is done if the device was created by autocreate.
- </li>
- <br><br>
- <li>Gas Sensor, CO Sensor (EEP A5-09-02)<br>
- [untested]<br>
- <ul>
- <li>CO: c/ppm (Sensor Range: c = 0 ppm ... 1020 ppm)</li>
- <li>temperature: t/°C (Sensor Range: t = 0 °C ... 51.0 °C)</li>
- <li>voltage: U/V</li> (Sensor Range: U = 0 V ... 5.1 V)
- <li>state: c/ppm</li>
- </ul><br>
- The attr subType must be COSensor.02. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Gas Sensor, CO2 Sensor (EEP A5-09-04)<br>
- [Thermokon SR04 CO2 *, Eltako FCOTF63, untested]<br>
- <ul>
- <li>airQuality: high|mean|moderate|low (Air Quality Classes DIN EN 13779)</li>
- <li>CO2: c/ppm (Sensor Range: c = 0 ppm ... 2550 ppm)</li>
- <li>humidity: rH/% (Sensor Range: rH = 0 % ... 100 %)</li>
- <li>temperature: t/°C (Sensor Range: t = 0 °C ... 51 °C)</li>
- <li>state: T: t/°C H: rH/% CO2: c/ppm AQ: high|mean|moderate|low</li>
- </ul><br>
- The attr subType must be tempHumiCO2Sensor.01. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Gas Sensor, Volatile organic compounds (VOC) Sensor (EEP A5-09-05, A5-09-0C)<br>
- [untested]<br>
- <ul>
- <li>concentration: c/[unit] (Sensor Range: c = 0 ... 655350</li>
- <li>concentrationUnit: ppb|μ/m3</li>
- <li>vocName: Name of last measured VOC</li>
- <li>state: c/[unit]</li>
- </ul><br>
- The attr subType must be vocSensor.01. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Gas Sensor, Radon Sensor (EEP A5-09-06)<br>
- [untested]<br>
- <ul>
- <li>Rn: A m3/Bq (Sensor Range: A = 0 Bq/m3 ... 1023 Bq/m3)</li>
- <li>state: A m3/Bq</li>
- </ul><br>
- The attr subType must be radonSensor.01. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Gas Sensor, Particles Sensor (EEP A5-09-07)<br>
- [untested]<br>
- Three channels with particle sizes of up to 10 μm, 2.5 μm and 1 μm are supported<br>.
- <ul>
- <li>particles_10: p m3/μg | inactive (Sensor Range: p = 0 μg/m3 ... 511 μg/m3)</li>
- <li>particles_2_5: p m3/μg | inactive (Sensor Range: p = 0 μg/m3 ... 511 μg/m3)</li>
- <li>particles_1: p m3/μg | inactive (Sensor Range: p = 0 μg/m3 ... 511 μg/m3)</li>
- <li>state: PM10: p m3/μg PM2_5: p m3/μg PM1: p m3/μg</li>
- </ul><br>
- The attr subType must be particlesSensor.01. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>CO2 Sensor (EEP A5-09-08, A5-09-09)<br>
- [untested]<br>
- <ul>
- <li>CO2: c/ppm (Sensor Range: c = 0 ppm ... 2000 ppm)</li>
- <li>powerFailureDetection: detected|not_detected</li>
- <li>state: c/ppm</li>
- </ul><br>
- The attr subType must be CO2Sensor.01. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>H Sensor (EEP A5-09-0A)<br>
- [untested]<br>
- <ul>
- <li>c/ppm</li>
- <li>voltage: U/V (Sensor Range: U = 2 V ... 5 V)</li>
- <li>H: c/ppm (Sensor Range: c = 0 ppm ... 2000 ppm)</li>
- <li>temperature: t/°C (Sensor Range: t = -20 °C ... 60 °C)</li>
- <li>state: c/ppm</li>
- </ul><br>
- The attr subType must be HSensor.01. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Radiation Sensor (EEP A5-09-0B)<br>
- [untested]<br>
- <ul>
- <li>1/[unit]</li>
- <li>radioactivity: 1/[unit] (Sensor Range: c = 0 [unit] ... 65535 [unit])</li>
- <li>unit: uSv/h|cpm|Bq/L|Bq/kg</li>
- <li>voltage: U/V (Sensor Range: U = 2 V ... 5 V)</li>
- <li>state: 1/[unit]</li>
- </ul><br>
- The attr subType must be radiationSensor.01. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Room Sensor and Control Unit (EEP A5-10-01 ... A5-10-0D)<br>
- [Eltako FTR55*, Thermokon SR04 *, Thanos SR *]<br>
- <ul>
- <li>T: t/°C SP: 0 ... 255 F: 0|1|2|3|auto SW: 0|1</li>
- <li>fanStage: 0|1|2|3|auto</li>
- <li>switch: on|off</li>
- <li>setpoint: 0 ... 255</li>
- <li>setpointScaled: <floating-point number></li>
- <li>temperature: t/°C (Sensor Range: t = 0 °C ... 40 °C)</li>
- <li>state: T: t/°C SP: 0 ... 255 F: 0|1|2|3|auto SW: on|off</li><br>
- Alternatively for Eltako devices
- <li>T: t/°C SPT: t/°C NR: t/°C</li>
- <li>block: lock|unlock</li>
- <li>nightReduction: t/K</li>
- <li>setpointTemp: t/°C</li>
- <li>temperature: t/°C (Sensor Range: t = 0 °C ... 40 °C)</li>
- <li>state: T: t/°C SPT: t/°C NR: t/K</li><br>
- </ul><br>
- The scaling of the setpoint adjustment is device- and vendor-specific. Set the
- attributes <a href="#scaleMax">scaleMax</a>, <a href="#scaleMin">scaleMin</a> and
- <a href="#scaleDecimals">scaleDecimals</a> for the additional scaled reading
- setpointScaled. Use attribut <a href="#userReadings">userReadings</a> to
- adjust the scaling alternatively.<br>
- The attr subType must be roomSensorControl.05 and attr
- manufID must be 00D for Eltako Devices. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Room Sensor and Control Unit (EEP A5-04-01, A5-10-10 ... A5-10-14)<br>
- [Eltako FUTH65D, Thermokon SR04 * rH, Thanos SR *]<br>
- <ul>
- <li>T: t/°C H: rH/% SP: 0 ... 255 SW: 0|1</li>
- <li>humidity: rH/% (Sensor Range: rH = 0 % ... 100 %)</li>
- <li>switch: 0|1</li>
- <li>temperature: t/°C (Sensor Range: t = 0 °C ... 40 °C)</li>
- <li>setpoint: 0 ... 255</li>
- <li>setpointScaled: <floating-point number></li>
- <li>state: T: t/°C H: rH/% SP: 0 ... 255 SW: 0|1</li>
- </ul><br>
- The scaling of the setpoint adjustment is device- and vendor-specific. Set the
- attributes <a href="#scaleMax">scaleMax</a>, <a href="#scaleMin">scaleMin</a> and
- <a href="#scaleDecimals">scaleDecimals</a> for the additional scaled reading
- setpointScaled. Use attribut <a href="#userReadings">userReadings</a> to
- adjust the scaling alternatively.<br>
- The attr subType must be roomSensorControl.01 and attr
- manufID must be 00D for Eltako Devices. This is
- done if the device was created by autocreate.
- </li>
- <br><br>
- <li>Room Sensor and Control Unit (EEP A5-10-15 ... A5-10-17)<br>
- [untested]<br>
- <ul>
- <li>T: t/°C SP: 0 ... 63 P: absent|present</li>
- <li>presence: absent|present</li>
- <li>temperature: t/°C (Sensor Range: t = -10 °C ... 41.2 °C)</li>
- <li>setpoint: 0 ... 63</li>
- <li>setpointScaled: <floating-point number></li>
- <li>state: T: t/°C SP: 0 ... 63 P: absent|present</li>
- </ul><br>
- The scaling of the setpoint adjustment is device- and vendor-specific. Set the
- attributes <a href="#scaleMax">scaleMax</a>, <a href="#scaleMin">scaleMin</a> and
- <a href="#scaleDecimals">scaleDecimals</a> for the additional scaled reading
- setpointScaled. Use attribut <a href="#userReadings">userReadings</a> to
- adjust the scaling alternatively.<br>
- The attr subType must be roomSensorControl.02. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Room Sensor and Control Unit (EEP A5-10-18)<br>
- [untested]<br>
- <ul>
- <li>T: t/°C B: E/lx F: 0|1|2|3|4|5|auto|off SP: t/°C P: absent|present|disabled</li>
- <li>brightness: E/lx (Sensor Range: E = 0 lx ... 1000 lx, over range)</li>
- <li>fan: 0|1|2|3|4|5|auto|off</li>
- <li>presence: absent|present|disabled</li>
- <li>temperature: t/°C (Sensor Range: t = 0 °C ... 40 °C)</li>
- <li>setpoint: t/°C (Sensor Range: t = 0 °C ... 40 °C)</li>
- <li>state: T: t/°C B: E/lx F: 0|1|2|3|4|5|auto|off SP: t/°C P: absent|present|disabled</li>
- </ul><br>
- The attr subType must be roomSensorControl.18. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Room Sensor and Control Unit (EEP A5-10-19)<br>
- [untested]<br>
- <ul>
- <li>T: t/°C H: rH/% F: 0|1|2|3|4|5|auto|off SP: t/°C P: absent|present|disabled</li>
- <li>fan: 0|1|2|3|4|5|auto|off</li>
- <li>humidity: rH/% (Sensor Range: rH = 0 % ... 100 %)</li>
- <li>presence: absent|present|disabled</li>
- <li>setpoint: t/°C (Sensor Range: t = 0 °C ... 40 °C)</li>
- <li>temperature: t/°C (Sensor Range: t = 0 °C ... 40 °C)</li>
- <li>state: T: t/°C H: rH/% F: 0|1|2|3|4|5|auto|off SP: t/°C P: absent|present|disabled</li>
- </ul><br>
- The attr subType must be roomSensorControl.19. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Room Sensor and Control Unit (EEP A5-10-1A)<br>
- [untested]<br>
- <ul>
- <li>T: t/°C F: 0|1|2|3|4|5|auto|off SP: t/°C P: absent|present|disabled U: U/V</li>
- <li>errorCode: 251 ... 255</li>
- <li>fan: 0|1|2|3|4|5|auto|off</li>
- <li>presence: absent|present|disabled</li>
- <li>setpoint: t/°C (Sensor Range: t = 0 °C ... 40 °C)</li>
- <li>temperature: t/°C (Sensor Range: t = 0 °C ... 40 °C)</li>
- <li>voltage: U/V (Sensor Range: U = 0 V ... 5.0 V)</li>
- <li>state: T: t/°C F: 0|1|2|3|4|5|auto|off SP: t/°C P: absent|present|disabled U: U/V</li>
- </ul><br>
- The attr subType must be roomSensorControl.1A. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Room Sensor and Control Unit (EEP A5-10-1B)<br>
- [untested]<br>
- <ul>
- <li>T: t/°C B: E/lx F: 0|1|2|3|4|5|auto|off P: absent|present|disabled U: U/V</li>
- <li>brightness: E/lx (Sensor Range: E = 0 lx ... 1000 lx, over range)</li>
- <li>errorCode: 251 ... 255</li>
- <li>fan: 0|1|2|3|4|5|auto|off</li>
- <li>presence: absent|present|disabled</li>
- <li>temperature: t/°C (Sensor Range: t = 0 °C ... 40 °C)</li>
- <li>voltage: U/V (Sensor Range: U = 0 V ... 5.0 V)</li>
- <li>state: T: t/°C B: E/lx F: 0|1|2|3|4|5|auto|off P: absent|present|disabled U: U/V</li>
- </ul><br>
- The attr subType must be roomSensorControl.1B. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Room Sensor and Control Unit (EEP A5-10-1C)<br>
- [untested]<br>
- <ul>
- <li>T: t/°C B: E/lx F: 0|1|2|3|4|5|auto|off SP: E/lx P: absent|present|disabled</li>
- <li>brightness: E/lx (Sensor Range: E = 0 lx ... 1000 lx, over range)</li>
- <li>fan: 0|1|2|3|4|5|auto|off</li>
- <li>presence: absent|present|disabled</li>
- <li>setpoint: E/lx (Sensor Range: E = 0 lx ... 1000 lx, over range)</li>
- <li>temperature: t/°C (Sensor Range: t = 0 °C ... 40 °C)</li>
- <li>state: T: t/°C B: E/lx F: 0|1|2|3|4|5|auto|off SP: E/lx P: absent|present|disabled</li>
- </ul><br>
- The attr subType must be roomSensorControl.1C. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Room Sensor and Control Unit (EEP A5-10-1D)<br>
- [untested]<br>
- <ul>
- <li>T: t/°C H: rH/% F: 0|1|2|3|4|5|auto|off SP: rH/% P: absent|present|disabled</li>
- <li>humidity: rH/% (Sensor Range: rH = 0 % ... 100 %)</li>
- <li>fan: 0|1|2|3|4|5|auto|off</li>
- <li>presence: absent|present|disabled</li>
- <li>setpoint: rH/% (Sensor Range: rH = 0 % ... 100 %)</li>
- <li>temperature: t/°C (Sensor Range: t = 0 °C ... 40 °C)</li>
- <li>state: T: t/°C H: rH/% F: 0|1|2|3|4|5|auto|off SP: rH/% P: absent|present|disabled</li>
- </ul><br>
- The attr subType must be roomSensorControl.1D. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Room Sensor and Control Unit (EEP A5-10-1F)<br>
- [untested]<br>
- <ul>
- <li>T: t/°C F: 0|1|2|3|auto SP: 0 ... 255 P: absent|present|disabled</li>
- <li>fan: 0|1|2|3|auto</li>
- <li>presence: absent|present|disabled</li>
- <li>setpoint: 0 ... 255</li>
- <li>setpointScaled: <floating-point number></li>
- <li>temperature: t/°C (Sensor Range: t = 0 °C ... 40 °C)</li>
- <li>state: T: t/°C F: 0|1|2|3|auto SP: 0 ... 255 P: absent|present|disabled</li>
- </ul><br>
- The scaling of the setpoint adjustment is device- and vendor-specific. Set the
- attributes <a href="#scaleMax">scaleMax</a>, <a href="#scaleMin">scaleMin</a> and
- <a href="#scaleDecimals">scaleDecimals</a> for the additional scaled reading
- setpointScaled. Use attribut <a href="#userReadings">userReadings</a> to
- adjust the scaling alternatively.<br>
- The attr subType must be roomSensorControl.1F. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Room Operation Panel (EEP A5-10-20, A5-10-21)<br>
- [untested]<br>
- <ul>
- <li>T: t/°C H: rH/% SP: 0 ... 255 B: ok|low</li>
- <li>activity: yes|no</li>
- <li>battery: ok|low</li>
- <li>humidity: rH/% (Sensor Range: rH = 0 % ... 100 %)</li>
- <li>setpoint: 0 ... 255</li>
- <li>setpointMode: auto|frostProtect|setpoint</li>
- <li>setpointScaled: <floating-point number></li>
- <li>temperature: t/°C (Sensor Range: t = 0 °C ... 40 °C)</li>
- <li>state: t/°C H: rH/% SP: 0 ... 255 B: ok|low</li>
- </ul><br>
- The scaling of the setpoint adjustment is device- and vendor-specific. Set the
- attributes <a href="#scaleMax">scaleMax</a>, <a href="#scaleMin">scaleMin</a> and
- <a href="#scaleDecimals">scaleDecimals</a> for the additional scaled reading
- setpointScaled. Use attribut <a href="#userReadings">userReadings</a> to
- adjust the scaling alternatively.<br>
- The attr subType must be roomSensorControl.20. This is done if the device was created by autocreate.
- </li>
- <br><br>
- <li>Room Operation Panel (EEP A5-10-22, A5-10-23)<br>
- [untested]<br>
- <ul>
- <li>T: t/°C H: rH/% SP: 0 ... 255 F: auto|off|1|2|3 O: occupied|unoccupied</li>
- <li>fanSpeed: auto|off|1|2|3</li>
- <li>humidity: rH/% (Sensor Range: rH = 0 % ... 100 %)</li>
- <li>occupancy: occupied|unoccupied</li>
- <li>setpoint: 0 ... 255</li>
- <li>setpointScaled: <floating-point number></li>
- <li>temperature: t/°C (Sensor Range: t = 0 °C ... 40 °C)</li>
- <li>state: t/°C H: rH/% SP: 0 ... 255 F: auto|off|1|2|3 O: occupied|unoccupied</li>
- </ul><br>
- The scaling of the setpoint adjustment is device- and vendor-specific. Set the
- attributes <a href="#scaleMax">scaleMax</a>, <a href="#scaleMin">scaleMin</a> and
- <a href="#scaleDecimals">scaleDecimals</a> for the additional scaled reading
- setpointScaled. Use attribut <a href="#userReadings">userReadings</a> to
- adjust the scaling alternatively.<br>
- The attr subType must be roomSensorControl.22.
- This is done if the device was created by autocreate.
- </li>
- <br><br>
- <li>Lighting Controller State (EEP A5-11-01)<br>
- [untested]<br>
- <ul>
- <li>on|off</li>
- <li>brightness: E/lx (Sensor Range: E = 0 lx ... 510 lx)</li>
- <li>contact: open|closed</li>
- <li>daylightHarvesting: enabled|disabled</li>
- <li>dim: 0 ... 255</li>
- <li>presence: absent|present</li>
- <li>illum: 0 ... 255</li>
- <li>mode: switching|dimming</li>
- <li>powerRelayTimer: enabled|disabled</li>
- <li>powerSwitch: on|off</li>
- <li>repeater: enabled|disabled</li>
- <li>state: on|off</li>
- </ul><br>
- The attr subType must be lightCtrlState.01 This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Temperature Controller Output (EEP A5-11-02)<br>
- [untested]<br>
- <ul>
- <li>t/°C</li>
- <li>alarm: on|off</li>
- <li>controlVar: cvar (Sensor Range: cvar = 0 % ... 100 %)</li>
- <li>controllerMode: auto|heating|cooling|off</li>
- <li>controllerState: auto|override</li>
- <li>energyHoldOff: normal|holdoff</li>
- <li>fan: 0 ... 3|auto</li>
- <li>presence: present|absent|standby|frost</li>
- <li>setpointTemp: t/°C (Sensor Range: t = 0 °C ... 51.2 °C)</li>
- <li>state: t/°C</li>
- </ul><br>
- The attr subType must be tempCtrlState.01 This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li><a name="Blind Status">Blind Status</a> (EEP A5-11-03)<br>
- [untested, experimental status]<br>
- <ul>
- <li>open|closed|not_reached|not_available</li>
- <li>alarm: on|off|no endpoints defined|not used</li>
- <li>anglePos: α/° (Sensor Range: α = -180 ° ... 180 °)</li>
- <li>endPosition: open|closed|not_reached|not_available</li>
- <li>position: pos/% (Sensor Range: pos = 0 % ... 100 %)</li>
- <li>positionMode: normal|inverse</li>
- <li>serviceOn: yes|no</li>
- <li>shutterState: opens|closes|stopped|not_available</li>
- <li>state: open|closed|not_reached|not_available</li>
- </ul><br>
- The attr subType must be shutterCtrlState.01 This is done if the device was
- created by autocreate.<br>
- The profile is linked with <a href="#Blind Command Central">Blind Command Central</a>.
- The profile <a href="#Blind Command Central">Blind Command Central</a>
- controls the devices centrally. For that the attributes subDef, subTypeSet
- and gwCmd have to be set manually.
- </li>
- <br><br>
- <li>Extended Lighting Status (EEP A5-11-04)<br>
- [untested]<br>
- <ul>
- <li>on|off</li>
- <li>alarm: off|lamp_failure|internal_failure|external_periphery_failure</li>
- <li>blue: 0 ... 255</li>
- <li>current: <formula symbol>/<unit> (Sensor range: <formula symbol> = 0 ... 65535 <unit></li>
- <li>currentUnit: mA|A</li>
- <li>dim: 0 ... 255</li>
- <li>energy: <formula symbol>/<unit> (Sensor range: <formula symbol> = 0 ... 65535 <unit></li>
- <li>energyUnit: Wh|kWh|MWh|GWh</li>
- <li>green: 0 ... 255</li>
- <li>measuredValue: <formula symbol>/<unit> (Sensor range: <formula symbol> = 0 ... 65535 <unit></li>
- <li>measureUnit: unknown</li>
- <li>lampOpHours: t/h |unknown (Sensor range: t = 0 h ... 65535 h)</li>
- <li>power: <formula symbol>/<unit> (Sensor range: <formula symbol> = 0 ... 65535 <unit></li>
- <li>powerSwitch: on|off</li>
- <li>powerUnit: mW|W|kW|MW</li>
- <li>red: 0 ... 255</li>
- <li>rgb: RRGGBB (red (R), green (G) or blue (B) color component values: 00 ... FF)</li>
- <li>serviceOn: yes|no</li>
- <li>voltage: <formula symbol>/<unit> (Sensor range: <formula symbol> = 0 ... 65535 <unit></li>
- <li>voltageUnit: mV|V</li>
- <li>state: on|off</li>
- </ul><br>
- The attr subType must be lightCtrlState.02 This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Dual Channel Switch Actuator (EEP A5-11-05)<br>
- [untested]<br>
- <ul>
- <li>1: on|off 2: on|off</li>
- <li>channel1: on|off</li>
- <li>channel2: on|off</li>
- <li>workingMode: 1 ... 4</li>
- <li>state: 1: on|off 2: on|off</li>
- </ul><br>
- The attr subType must be switch.05. This is done if the device was created by autocreate.
- </li>
- <br><br>
- <li>Automated meter reading (AMR), Counter (EEP A5-12-00)<br>
- [Thermokon SR-MI-HS, untested]<br>
- <ul>
- <li>1/s</li>
- <li>currentValue<00 ... 15>: 1/s</li>
- <li>counter<00 ... 15>: 0 ... 16777215</li>
- <li>state: 1/s</li>
- </ul><br>
- The attr subType must be autoMeterReading.00. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Automated meter reading (AMR), Electricity (EEP A5-12-01)<br>
- [Eltako FSS12, DSZ14DRS, DSZ14WDRS, Thermokon SR-MI-HS, untested]<br>
- [Eltako FWZ12-16A tested]<br>
- <ul>
- <li>P/W</li>
- <li>power: P/W</li>
- <li>energy<0 ... 15>: E/kWh</li>
- <li>currentTariff: 0 ... 15</li>
- <li>serialNumber: S-<nnnnnn></li>
- <li>state: P/W</li>
- </ul><br>
- The attr subType must be autoMeterReading.01 and attr
- manufID must be 00D for Eltako Devices. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Automated meter reading (AMR), Gas, Water (EEP A5-12-02, A5-12-03)<br>
- [untested]<br>
- <ul>
- <li>Vs/l</li>
- <li>flowrate: Vs/l</li>
- <li>consumption<0 ... 15>: V/m3</li>
- <li>currentTariff: 0 ... 15</li>
- <li>state: Vs/l</li>
- </ul><br>
- The attr subType must be autoMeterReading.02|autoMeterReading.03.
- This is done if the device was created by autocreate.
- </li>
- <br><br>
- <li>Automated meter reading (AMR), Temperatur, Load (EEP A5-12-04)<br>
- [untested]<br>
- <ul>
- <li>T: t/°C W: m/g B: full|ok|low|empty</li>
- <li>battery: full|ok|low|empty</li>
- <li>temperature: t/°C (Sensor Range: t = -40 °C ... 40 °C)</li>
- <li>weight: m/g</li>
- <li>state: T: t/°C W: m/g B: full|ok|low|empty</li>
- </ul><br>
- The attr subType must be autoMeterReading.04.
- This is done if the device was created by autocreate.
- </li>
- <br><br>
- <li>Automated meter reading (AMR), Temperatur, Container Sensor (EEP A5-12-05)<br>
- [untested]<br>
- <ul>
- <li>T: t/°C L: <location0 ... location9> B: full|ok|low|empty</li>
- <li>amount: 0 ... 10</li>
- <li>battery: full|ok|low|empty</li>
- <li>location<0 ... 9>: possessed|not_possessed</li>
- <li>temperature: t/°C (Sensor Range: t = -40 °C ... 40 °C)</li>
- <li>state: T: t/°C L: <location0 ... location9> B: full|ok|low|empty</li>
- </ul><br>
- The attr subType must be autoMeterReading.05.
- This is done if the device was created by autocreate.
- </li>
- <br><br>
- <li>Automated meter reading (AMR), Current Meter 16 Channels (EEP A5-12-10)<br>
- [untested]<br>
- <ul>
- <li>I/mA</li>
- <li>current<00 ... 15>: I/mA (Sensor Range: I = 0 mA ... 16777215 mA)</li>
- <li>electricChange<00 ... 15>: Q/Ah (Sensor Range: Q = 0 Ah ... 16777215 Ah)</li>
- <li>currentTariff: 00 ... 15</li>
- <li>state: I/mA</li>
- </ul><br>
- The attr subType must be autoMeterReading.10. This is done if the device was created by autocreate.
- </li>
- <br><br>
- <li>Environmental Applications<br>
- Weather Station (EEP A5-13-01)<br>
- Sun Intensity (EEP A5-13-02)<br>
- [Eltako FWS61]<br>
- <ul>
- <li>T: t/°C B: E/lx W: Vs/m IR: yes|no</li>
- <li>brightness: E/lx (Sensor Range: E = 0 lx ... 999 lx)</li>
- <li>dayNight: day|night</li>
- <li>hemisphere: north|south</li>
- <li>isRaining: yes|no</li>
- <li>sunEast: E/lx (Sensor Range: E = 1 lx ... 150 klx)</li>
- <li>sunSouth: E/lx (Sensor Range: E = 1 lx ... 150 klx)</li>
- <li>sunWest: E/lx (Sensor Range: E = 1 lx ... 150 klx)</li>
- <li>temperature: t/°C (Sensor Range: t = -40 °C ... 80 °C)</li>
- <li>windSpeed: Vs/m (Sensor Range: V = 0 m/s ... 70 m/s)</li>
- <li>state:T: t/°C B: E/lx W: Vs/m IR: yes|no</li>
- </ul><br>
- Brightness is the strength of the dawn light. SunEast,
- sunSouth and sunWest are the solar radiation from the respective
- compass direction. IsRaining is the rain indicator.<br>
- The attr subType must be environmentApp and attr manufID must be 00D
- for Eltako Devices. This is done if the device was created by
- autocreate.<br>
- The Eltako Weather Station FWS61 supports not the day/night indicator
- (dayNight).<br>
- </li>
- <br><br>
- <li>Environmental Applications<br>
- Data Exchange (EEP A5-13-03)<br>
- Time and Day Exchange (EEP A5-13-04)<br>
- Direction Exchange (EEP A5-13-05)<br>
- Geographic Exchange (EEP A5-13-06)<br>
- <ul>
- <li>azimuth: α/° (Sensor Range: α = 0 ° ... 359 °)</li>
- <li>date: JJJJ-MM-DD</li>
- <li>elevation: β/° (Sensor Range: β = -90 ° ... 90 °)</li>
- <li>latitude: φ/° (Sensor Range: φ = -90 ° ... 90 °)</li>
- <li>longitude: λ/° (Sensor Range: λ = -180 ° ... 180 °)</li>
- <li>time: hh:mm:ss [AM|PM]</li>
- <li>timeSource: GPS|RTC</li>
- <li>weekday: Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday</li>
- </ul><br>
- The attr subType must be environmentApp. This is done if the device was created by
- autocreate.
- </li>
- <br><br>
- <li>Environmental Applications<br>
- Sun Position and Radiation (EEP A5-13-10)<br>
- [untested]<br>
- <ul>
- <li>SRA: E m2/W SNA: α/° SNE: β/°</li>
- <li>dayNight: day|night</li>
- <li>solarRadiation: E m2/W (Sensor Range: E = 0 W/m2 ... 2000 W/m2)</li>
- <li>sunAzimuth: α/° (Sensor Range: α = -90 ° ... 90 °)</li>
- <li>sunElevation: β/° (Sensor Range: β = 0 ° ... 90 °)</li>
- <li>state:SRA: E m2/W SNA: α/° SNE: β/°</li>
- </ul><br>
- The attr subType must be environmentApp. This is done if the device was created by
- autocreate.
- </li>
- <br><br>
- <li>Wind Sensor (EEP A5-13-07)<br>
- [Hideki, untested]<br>
- <ul>
- <li>Vh/km (Sensor Range: V = 0 km/h ... 199.9 km/h)</li>
- <li>battery: ok|low</li>
- <li>windSpeedAverage: Vh/km (Sensor Range: V = 0 km/h ... 199.9 km/h)</li>
- <li>windSpeedDirection: NNE|NE|ENE|E|ESE|SE|SSE|S|SSW|SW|WSW|W|WNW|NW|NNW|N</li>
- <li>windSpeedMax: Vh/km (Sensor Range: V = 0 km/h ... 199.9 km/h)</li>
- <li>state:Vh/km (Sensor Range: V = 0 km/h ... 199.9 km/h)</li>
- </ul><br>
- The attr subType must be windSensor.01. This is done if the device was created by
- autocreate.<br>
- </li>
- <br><br>
- <li>Rain Sensor (EEP A5-13-08)<br>
- [Hideki, untested]<br>
- <ul>
- <li>H/mm</li>
- <li>battery: ok|low</li>
- <li>rain: H/mm</li>
- <li>state:H/mm</li>
- </ul><br>
- The amount of rainfall is calculated at intervals of 183 s.<br>
- The attr subType must be rainSensor.01. This is done if the device was created by
- autocreate.<br>
- </li>
- <br><br>
- <li>Multi-Func Sensor (EEP A5-14-01 ... A5-14-06)<br>
- [untested]<br>
- <ul>
- <li>C: open|closed V: on|off E: E/lx U: U/V</li>
- <li>brightness: E/lx (Sensor Range: E = 0 lx ... 1000 lx, over range)</li>
- <li>contact: open|closed</li>
- <li>errorCode: 251 ... 255</li>
- <li>vibration: on|off</li>
- <li>voltage: U/V (Sensor Range: U = 0 V ... 5.0 V)</li>
- <li>state: C: open|closed V: on|off E: E/lx U: U/V</li>
- </ul><br>
- The attr subType must be multiFuncSensor. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Dual Door Contact (EEP A5-14-07, A5-14-08)<br>
- [untested]<br>
- <ul>
- <li>C: open|closed B: unlocked|locked V: on|off U: U/V</li>
- <li>alarm: dead_sensor</li>
- <li>block: unlocked|locked</li>
- <li>contact: open|closed</li>
- <li>vibration: on|off</li>
- <li>voltage: U/V (Sensor Range: U = 0 V ... 5.0 V)</li>
- <li>state: C: open|closed B: unlocked|locked V: on|off U: U/V</li>
- </ul><br>
- The attr subType must be doorContact. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Window/Door Contact (EEP A5-14-09, A5-14-0A)<br>
- [untested]<br>
- <ul>
- <li>W: open|tilt|closed B: unlocked|locked V: on|off U: U/V</li>
- <li>alarm: dead_sensor</li>
- <li>vibration: on|off</li>
- <li>voltage: U/V (Sensor Range: U = 0 V ... 5.0 V)</li>
- <li>window: open|tilt|closed</li>
- <li>state: W: open|tilt|closed V: on|off U: U/V</li>
- </ul><br>
- The attr subType must be windowContact. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Battery Powered Actuator (EEP A5-20-01)<br>
- [Kieback&Peter MD15-FTL-xx]<br>
- <ul>
- <li>T: t/°C SPT: t/°C SP: setpoint/%</li>
- <li>actuatorState: obstructed|ok</li>
- <li>alarm: no_response_from_actuator</li>
- <li>battery: ok|low</li>
- <li>cover: open|closed</li>
- <li>delta: <floating-point number></li>
- <li>energyInput: enabled|disabled</li>
- <li>energyStorage: charged|empty</li>
- <li>maintenanceMode: off|runInit|valveClosed|valveOpend:runInit</li>
- <li>operationMode: off|setpoint|setpointTemp</li>
- <li>p_d: <floating-point number></li>
- <li>p_i: <floating-point number></li>
- <li>p_p: <floating-point number></li>
- <li>pidAlarm: actuator_in_errorPos|dead_sensor|no_temperature_value|setpoint_device_missing</li>
- <li>pidState: alarm|idle|processing|start|stop|</li>
- <li>roomTemp: t/°C</li>
- <li>selfCtl: on|off</li>
- <li>setpoint: setpoint/%</li>
- <li>setpointSet: setpoint/%</li>
- <li>setpointCalc: setpoint/%</li>
- <li>setpointTemp: t/°C</li>
- <li>setpointTempSet: t/°C</li>
- <li>teach: <result of teach procedure></li>
- <li>temperature: t/°C</li>
- <li>waitingCmds: no_change|runInit|setpoint|setpointTemp|valveCloses|valveOpens</li>
- <li>wakeUpCycle: t/s</li>
- <li>window: open|closed</li>
- <li>state: T: t/°C SPT: t/°C SP: setpoint/%</li>
- </ul><br>
- The internal temperature sensor (roomTemp) of the Micropelt iTRV is not suitable as
- a room thermostat.<br>
- The attr subType must be hvac.01. This is done if the device was created by
- autocreate.
- </li>
- <br><br>
- <li>Heating Radiator Actuating Drive (EEP A5-20-04)<br>
- [Holter SmartDrive MX]<br>
- <ul>
- <li>T: t/°C SPT: t/°C SP: setpoint/%</li>
- <li>alarm: measurement_error|battery_empty|frost_protection|blocked_valve|end_point_detection_error|no_valve|not_taught_in|no_response_from_controller|teach-in_error</li>
- <li>battery: ok|empty</li>
- <li>blockKey: yes|no</li>
- <li>delta: <floating-point number></li>
- <li>feedTemp: t/°C</li>
- <li>maintenanceMode: off|runInit|valveClosed|valveOpend:runInit</li>
- <li>measurementState: active|inactive</li>
- <li>operationMode: off|setpoint|setpointTemp</li>
- <li>p_d: <floating-point number></li>
- <li>p_i: <floating-point number></li>
- <li>p_p: <floating-point number></li>
- <li>pidAlarm: actuator_in_errorPos|dead_sensor|no_temperature_value|setpoint_device_missing</li>
- <li>pidState: alarm|idle|processing|start|stop|</li>
- <li>roomTemp: t/°C</li>
- <li>setpoint: setpoint/%</li>
- <li>setpointSet: setpoint/%</li>
- <li>setpointCalc: setpoint/%</li>
- <li>setpointTemp: t/°C</li>
- <li>setpointTempSet: t/°C</li>
- <li>teach: <result of teach procedure></li>
- <li>temperature: t/°C</li>
- <li>waitingCmds: no_change|runInit|setpoint|setpointTemp|valveCloses|valveOpens</li>
- <li>state: T: t/°C SPT: t/°C SP: setpoint/%</li>
- </ul><br>
- The attr subType must be hvac.04. This is done if the device was created by
- autocreate.
- </li>
- <br><br>
- <li>Generic HVAC Interface (EEP A5-20-10)<br>
- [IntesisBox PA-AC-ENO-1i]<br>
- <ul>
- <li>on|off</li>
- <li>ctrl: auto|0...100</li>
- <li>fanSpeed: auto|1...14</li>
- <li>occupancy: occupied|off|standby|unoccupied</li>
- <li>mode: auto|heat|morning_warmup|cool|night_purge|precool|off|test|emergency_heat|fan_only|free_cool|ice|max_heat|eco|dehumidification|calibration|emergency_cool|emergency_stream|max_cool|hvc_load|no_load|auto_heat|auto_cool</li>
- <li>vanePosition: auto|horizontal|position_2|position_3|position_4|vertical|swing|vertical_swing|horizontal_swing|hor_vert_swing|stop_swing</li>
- <li>powerSwitch: on|off</li>
- <li>teach: <result of teach procedure></li>
- <li>state: on|off</li>
- </ul><br>
- The attr subType must be hvac.10. This is done if the device was created by
- autocreate.
- </li>
- <br><br>
- <li>Generic HVAC Interface - Error Control (EEP A5-20-11)<br>
- [IntesisBox PA-AC-ENO-1i]<br>
- <ul>
- <li>error|ok</li>
- <li>alarm: error|ok</li>
- <li>errorCode: 0...65535</li>
- <li>externalDisable: disable|enable</li>
- <li>keyCardDisable: disable|enable</li>
- <li>otherDisable: disable|enable</li>
- <li>powerSwitch: on|off</li>
- <li>remoteCtrl: disable|enable</li>
- <li>teach: <result of teach procedure></li>
- <li>window: closed|opened</li>
- <li>windowDisable: disable|enable</li>
- <li>state: error|ok</li>
- </ul><br>
- The attr subType must be hvac.11. This is done if the device was created by
- autocreate.
- </li>
- <br><br>
- <li>Digital Input (EEP A5-30-01, A5-30-02)<br>
- [Thermokon SR65 DI, untested]<br>
- <ul>
- <li>open|closed</li>
- <li>battery: ok|low (only EEP A5-30-01)</li>
- <li>contact: open|closed</li>
- <li>teach: <result of teach procedure></li>
- <li>state: open|closed</li>
- </ul><br>
- The attr subType must be digitalInput.01 or digitalInput.02. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Digital Input (EEP A5-30-03)<br>
- 4 digital Inputs, Wake, Temperature [untested]<br>
- <ul>
- <li>T: t/°C I: 0|1 0|1 0|1 0|1 W: 0|1</li>
- <li>in0: 0|1</li>
- <li>in1: 0|1</li>
- <li>in2: 0|1</li>
- <li>in3: 0|1</li>
- <li>teach: <result of teach procedure></li>
- <li>temperature: t/°C (Sensor Range: t = 0 °C ... 40 °C)</li>
- <li>wake: high|low</li>
- <li>state: T: t/°C I: 0|1 0|1 0|1 0|1 W: high|low</li>
- </ul><br>
- The attr subType must be digitalInput.03. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Digital Input (EEP A5-30-04)<br>
- 3 digital Inputs, 1 digital Input 8 bits [untested]<br>
- <ul>
- <li>0|1 0|1 0|1 0...255</li>
- <li>in0: 0|1</li>
- <li>in1: 0|1</li>
- <li>in2: 0|1</li>
- <li>in3: 0...255</li>
- <li>teach: <result of teach procedure></li>
- <li>state: 0|1 0|1 0|1 0...255</li>
- </ul><br>
- The attr subType must be digitalInput.04. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Digital Input, single input contact, retransmission, battery monitor (EEP A5-30-05)<br>
- [untested]<br>
- <ul>
- <li>error|event|heartbeat</li>
- <li>battery: U/V (Range: U = 0 V ... 3.3 V</li>
- <li>signalIdx: 0 ... 127</li>
- <li>teach: <result of teach procedure></li>
- <li>telegramType: event|heartbeat</li>
- <li>state: error|event|heartbeat</li>
- </ul><br>
- The attr subType must be digitalInput.05. This is done if the device was
- created by autocreate.
- </li>
- <li>Energy management, demand response (EEP A5-37-01)<br>
- <br>
- <ul>
- <li>on|off|waiting_for_start|waiting_for_stop</li>
- <li>level: 0...15</li>
- <li>powerUsage: powerUsage/%</li>
- <li>powerUsageLevel: max|min</li>
- <li>powerUsageScale: rel|max</li>
- <li>randomEnd: yes|no</li>
- <li>randomStart: yes|no</li>
- <li>setpoint: 0...255</li>
- <li>teach: <result of teach procedure></li>
- <li>timeout: yyyy-mm-dd hh:mm:ss</li>
- <li>state: on|off|waiting_for_start|waiting_for_stop</li>
- </ul><br>
- The attr subType must be energyManagement.01. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Gateway (EEP A5-38-08)<br>
- Switching<br>
- [Eltako FLC61, FSA12, FSR14]<br>
- <ul>
- <li>on</li>
- <li>off</li>
- <li>executeTime: t/s (Sensor Range: t = 0.1 s ... 6553.5 s or 0 if no time specified)</li>
- <li>executeType: duration|delay</li>
- <li>block: lock|unlock</li>
- <li>teach: <result of teach procedure></li>
- <li>state: on|off</li>
- </ul><br>
- The attr subType must be gateway and gwCmd must be switching. This is done if the device was
- created by autocreate.<br>
- For Eltako devices attributes must be set manually. Eltako devices only send on/off.
- </li>
- <br><br>
- <li>Gateway (EEP A5-38-08)<br>
- Dimming<br>
- [Eltako FUD14, FUD61, FUD70, FSG14, ...]<br>
- <ul>
- <li>on</li>
- <li>off</li>
- <li>block: lock|unlock</li>
- <li>dim: dim/% (Sensor Range: dim = 0 % ... 100 %)</li>
- <li>dimValueLast: dim/%<br>
- Last value received from the bidirectional dimmer.</li>
- <li>dimValueStored: dim/%<br>
- Last value saved by <code>set <name> dim <value></code>.</li>
- <li>rampTime: t/s (Sensor Range: t = 1 s ... 255 s or 0 if no time specified,
- for Eltako: t = 1 = fast dimming ... 255 = slow dimming or 0 = dimming speed on the dimmer used)</li>
- <li>teach: <result of teach procedure></li>
- <li>state: on|off</li>
- </ul><br>
- The attr subType must be gateway, gwCmd must be dimming and attr manufID must be 00D
- for Eltako Devices. This is done if the device was created by autocreate.<br>
- For Eltako devices attributes must be set manually. Eltako devices only send on/off and dim.
- </li>
- <br><br>
- <li>Gateway (EEP A5-38-08)<br>
- Setpoint shift<br>
- [untested]<br>
- <ul>
- <li>1/K</li>
- <li>setpointShift: 1/K (Sensor Range: T = -12.7 K ... 12.8 K)</li>
- <li>teach: <result of teach procedure></li>
- <li>state: 1/K</li>
- </ul><br>
- The attr subType must be gateway, gwCmd must be setpointShift.
- This is done if the device was created by autocreate.
- </li>
- <br><br>
- <li>Gateway (EEP A5-38-08)<br>
- Basic Setpoint<br>
- [untested]<br>
- <ul>
- <li>t/°C</li>
- <li>setpoint: t/°C (Sensor Range: t = 0 °C ... 51.2 °C)</li>
- <li>teach: <result of teach procedure></li>
- <li>state: t/°C</li>
- </ul><br>
- The attr subType must be gateway, gwCmd must be setpointBasic.
- This is done if the device was created by autocreate.
- </li>
- <br><br>
- <li>Gateway (EEP A5-38-08)<br>
- Control variable<br>
- [untested]<br>
- <ul>
- <li>auto|heating|cooling|off</li>
- <li>controlVar: cvov (Sensor Range: cvov = 0 % ... 100 %)</li>
- <li>controllerMode: auto|heating|cooling|off</li>
- <li>controllerState: auto|override</li>
- <li>energyHoldOff: normal|holdoff</li>
- <li>presence: present|absent|standby</li>
- <li>teach: <result of teach procedure></li>
- <li>state: auto|heating|cooling|off</li>
- </ul><br>
- The attr subType must be gateway, gwCmd must be controlVar.
- This is done if the device was created by autocreate.
- </li>
- <br><br>
- <li>Gateway (EEP A5-38-08)<br>
- Fan stage<br>
- [untested]<br>
- <ul>
- <li>0 ... 3|auto</li>
- <li>teach: <result of teach procedure></li>
- <li>state: 0 ... 3|auto</li>
- </ul><br>
- The attr subType must be gateway, gwCmd must be fanStage.
- This is done if the device was created by autocreate.
- </li>
- <br><br>
- <li>Extended Lighting Control (EEP A5-38-09)<br>
- [untested]<br>
- <ul>
- <li>on</li>
- <li>off</li>
- <li>block: unlock|on|off|local</li>
- <li>blue: <blue channel value> (Range: blue = 0 ... 255)</li>
- <li>dimMax: <maximum dimming value> (Range: dim = 0 ... 255)</li>
- <li>dimMin: <minimum dimming value> (Range: dim = 0 ... 255)</li>
- <li>green: <green channel value> (Range: green = 0 ... 255)</li>
- <li>rampTime: t/s (Range: t = 0 s ... 65535 s)</li>
- <li>red: <red channel value> (Range: red = 0 ... 255)</li>
- <li>rgb: RRGGBB (red (R), green (G) or blue (B) color component values: 00 ... FF)</li>
- <li>teach: <result of teach procedure></li>
- <li>state: on|off</li>
- </ul><br>
- Another readings, see subtype lightCtrlState.02.<br>
- The attr subType or subTypSet must be lightCtrl.01. This is done if the device was created by autocreate.<br>
- The subType is associated with the subtype lightCtrlState.02.
- </li>
- <br><br>
- <li>Manufacturer Specific Applications (EEP A5-3F-7F)<br><br>
- Wireless Analog Input Module<br>
- [Thermokon SR65 3AI, untested]<br>
- <ul>
- <li>I1: U/V I2: U/V I3: U/V</li>
- <li>input1: U/V (Sensor Range: U = 0 V ... 10 V)</li>
- <li>input2: U/V (Sensor Range: U = 0 V ... 10 V)</li>
- <li>input3: U/V (Sensor Range: U = 0 V ... 10 V)</li>
- <li>teach: <result of teach procedure></li>
- <li>state: I1: U/V I2: U/V I3: U/V</li>
- </ul><br>
- The attr subType must be manufProfile and attr manufID must be 002
- for Thermokon Devices. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Manufacturer Specific Applications (EEP A5-3F-7F)<br><br>
- Thermostat Actuator<br>
- [AWAG omnio UPH230/1x]<br>
- <ul>
- <li>on|off</li>
- <li>emergencyMode<channel>: on|off</li>
- <li>nightReduction<channel>: on|off</li>
- <li>setpointTemp<channel>: t/°C</li>
- <li>teach: <result of teach procedure></li>
- <li>temperature<channel>: t/°C</li>
- <li>window<channel>: on|off</li>
- <li>state: on|off</li>
- </ul><br>
- The attr subType must be manufProfile and attr manufID must be 005
- for AWAG omnio Devices. This is done if the device was created by autocreate.
- </li>
- <br><br>
- <li>Manufacturer Specific Applications (EEP A5-3F-7F)<br><br>
- Shutter (EEP F6-02-01 ... F6-02-02)<br>
- [Eltako FSB12, FSB14, FSB61, FSB70]<br>
- <ul>
- <li>open|open_ack<br>
- The status of the device will become "open" after the TOP endpoint is
- reached, or it has finished an "opens" or "position 0" command.</li>
- <li>closed<br>
- The status of the device will become "closed" if the BOTTOM endpoint is
- reached</li>
- <li>stop<br>
- The status of the device become "stop" if stop command is sent.</li>
- <li>not_reached<br>
- The status of the device become "not_reached" between one of the endpoints.</li>
- <li>anglePos: α/° (Sensor Range: α = -180 ° ... 180 °)</li>
- <li>endPosition: open|open_ack|closed|not_reached|not_available</li>
- <li>position: pos/% (Sensor Range: pos = 0 % ... 100 %)</li>
- <li>teach: <result of teach procedure></li>
- <li>state: open|open_ack|closed|not_reached|stop|teach</li>
- </ul><br>
- The values of the reading position and anglePos are updated automatically,
- if the command position is sent or the reading state was changed
- manually to open or closed.<br>
- Set attr subType file, attr manufID to 00D and attr model to
- Eltako_FSB14|FSB61|FSB70 manually.
- </li>
- <br><br>
- <li>Electronic switches and dimmers with Energy Measurement and Local Control (D2-01-00 - D2-01-12)<br>
- [Telefunken Funktionsstecker, PEHA Easyclick, AWAG Elektrotechnik AG Omnio UPS 230/xx,UPD 230/xx]<br>
- <ul>
- <li>on</li>
- <li>off</li>
- <li>autoOffTime<1...29|All|Input>: 1/s</li>
- <li>channel<0...29|All|Input>: on|off</li>
- <li>delayOffTime<1...29|All|Input>: 1/s</li>
- <li>dayNight: day|night</li>
- <li>defaultState: on|off|last</li>
- <li>devTemp: t/°C|invalid</li>
- <li>devTempState: ok|max|warning</li>
- <li>dim<0...29|Input>: dim/% (Sensor Range: dim = 0 % ... 100 %)</li>
- <li>energy<channel>: E/[Ws|Wh|KWh]</li>
- <li>energyUnit<channel>: Ws|Wh|KWh</li>
- <li>error<channel>: ok|warning|failure|not_supported</li>
- <li>extSwitchMode<1...29|All|Input>: unavailable|switch|pushbutton|auto</li>
- <li>extSwitchType<1...29|All|Input>: toggle|direction</li>
- <li>firmwareVersion: [000000 ... FFFFFF]</li>
- <li>loadClassification: no</li>
- <li>localControl<channel>: enabled|disabled</li>
- <li>loadLink: connected|disconnected</li>
- <li>loadOperation: 3-wire</li>
- <li>loadState: on|off</li>
- <li>measurementMode: energy|power</li>
- <li>measurementReport: auto|query</li>
- <li>measurementReset: not_active|trigger</li>
- <li>measurementDelta: E/[Ws|Wh|KWh|W|KW]</li>
- <li>measurementUnit: Ws|Wh|KWh|W|KW</li>
- <li>overCurrentOff<channel>: executed|ready</li>
- <li>overCurrentShutdown<channel>: off|restart</li>
- <li>overCurrentShutdownReset<channel>: not_active|trigger</li>
- <li>power<channel>: P/[W|KW]</li>
- <li>powerFailure<channel>: enabled|disabled</li>
- <li>powerFailureDetection<channel>: detected|not_detected</li>
- <li>powerUnit<channel>: W|KW</li>
- <li>rampTime<1...3l>: 1/s</li>
- <li>responseTimeMax: 1/s</li>
- <li>responseTimeMin: 1/s</li>
- <li>roomCtrlMode: off|comfort|comfort-1|comfort-2|economy|buildingProtection</li>
- <li>serialNumber: [00000000 ... FFFFFFFF]</li>
- <li>taughtInDevID<00...23>: [00000001 ... FFFFFFFE]</li>
- <li>taughtInDevNum: [0 ... 23]</li>
- <li>teach: <result of teach procedure></li>
- <li>teachInDev: enabled|disabled</li>
- <li>state: on|off</li>
- </ul>
- <br>
- The attr subType must be actuator.01. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired,
- see <a href="#EnOcean_teach-in">Bidirectional Teach-In / Teach-Out</a>.
- </li>
- <br><br>
- <li>Blind Control for Position and Angle (D2-05-00)<br>
- [AWAG Elektrotechnik AG OMNIO UPJ 230/12]<br>
- <ul>
- <li>open<br>
- The status of the device will become "open" after the TOP endpoint is
- reached, or it has finished an "opens" or "position 0" command.</li>
- <li>closed<br>
- The status of the device will become "closed" if the BOTTOM endpoint is
- reached</li>
- <li>stop<br>
- The status of the device become "stop" if stop command is sent.</li>
- <li>not_reached<br>
- The status of the device become "not_reached" between one of the endpoints.</li>
- <li>pos/% (Sensor Range: pos = 0 % ... 100 %)</li>
- <li>anglePos<channel>: α/% (Sensor Range: α = 0 % ... 100 %)</li>
- <li>block<channel>: unlock|lock|alarm</li>
- <li>endPosition<channel>: open|closed|not_reached|unknown</li>
- <li>position<channel>: unknown|pos/% (Sensor Range: pos = 0 % ... 100 %)</li>
- <li>teach: <result of teach procedure></li>
- <li>state: open|closed|in_motion|stopped|pos/% (Sensor Range: pos = 0 % ... 100 %)</li>
- </ul>
- <br>
- The attr subType must be blindsCtrl.00 or blindsCtrl.01. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired,
- see <a href="#EnOcean_teach-in">Bidirectional Teach-In / Teach-Out</a>.
- </li>
- <br><br>
- <li>Multisensor Window Handle (D2-06-01)<br>
- [Soda GmbH]<br>
- <ul>
- <li>T: t/°C H: -|rH/% E: -|E/lx M: off|on|invalid|not_supported|unknown</li>
- <li>alarms: <alarms> (Range: alarms = 00000000 ... FFFFFFFF)</li>
- <li>battery: ok|low</li>
- <li>batteryLowClick: enabled|disabled</li>
- <li>burglaryAlarm: off|on|invalid|not_supported|unknown</li>
- <li>handle: up|down|left|right|invalid|not_supported|unknown</li>
- <li>blinkInterval: t/s|unknown (Range: t = 3 s ... 255 s)</li>
- <li>blinkIntervalSet: t/s|unknown (Range: t = 3 s ... 255 s)</li>
- <li>brightness: E/lx|over_range|invalid|not_supported|unknown (Sensor Range: E = 0 lx ... 60000 lx)</li>
- <li>buttonLeft: pressed|released|invalid|not_supported|unknown</li>
- <li>buttonLeftPresses: <buttonLeftPresses> (Range: buttonLeftPresses = 00000000 ... FFFFFFFF)</li>
- <li>buttonRight: pressed|released|invalid|not_supported|unknown</li>
- <li>buttonRightPresses: <buttonRightPresses> (Range: buttonRightPresses = 00000000 ... FFFFFFFF)</li>
- <li>energyStorage: 1/%|unknown</li>
- <li>handleClosedClick: enabled|disabled</li>
- <li>handleMoveClosed: <handleMoveClosed> (Range: handleMoveClosed = 00000000 ... FFFFFFFF)</li>
- <li>handleMoveOpend: <handleMoveOpend> (Range: handleMoveOpend = 00000000 ... FFFFFFFF)</li>
- <li>handleMoveTilted: <handleMoveTilted> (Range: handleMoveTilted = 00000000 ... FFFFFFFF)</li>
- <li>humidity: rH/%|invalid|not_supported|unknown</li>
- <li>motion: off|on|invalid|not_supported|unknown</li>
- <li>powerOns: <powerOns> (Range: powerOns = 00000000 ... FFFFFFFF)</li>
- <li>presence: absent|present|invalid|not_supported|unknown</li>
- <li>protectionAlarm: off|on|invalid|not_supported|unknown</li>
- <li>temperature: t/°C|invalid|not_supported|unknown (Sensor Range: t = -20 °C ... 60 °C)</li>
- <li>updateInterval: t/s|unknown (Range: t = 5 s ... 65535 s)</li>
- <li>updateIntervalSet: t/s|unknown (Range: t = 5 s ... 65535 s)</li>
- <li>waitingCmds: <integer number></li>
- <li>window: undef|not_tilted|tilted|invalid|not_supported|unknown</li>
- <li>windowTilts: <windowTilts> (Range: windowTilts = 00000000 ... FFFFFFFF)</li>
- <li>state: T: t/°C H: -|rH/% E: -|E/lx M: off|on|invalid|not_supported|unknown</li>
- </ul><br>
- The attr subType must be multisensor.01. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired,
- see <a href="#EnOcean_teach-in">Bidirectional Teach-In / Teach-Out</a>.
- </li>
- <br><br>
- <li>Room Control Panels (D2-10-00 - D2-10-02)<br>
- [Kieback & Peter RBW322-FTL]<br>
- <ul>
- <li>T: t/°C H: -|rH/% F: 0 ... 100/% SPT: t/°C O: -|absent|present M: -|on|off</li>
- <li>battery: ok|low|empty|-</li>
- <li>cooling: auto|on|off|-</li>
- <li>customWarning[1|2]: on|off</li>
- <li>fanSpeed: 0 ... 100/%</li>
- <li>fanSpeedMode: central|local</li>
- <li>heating: auto|on|off|-</li>
- <li>humidity: -|rH/%</li>
- <li>moldWarning: on|off</li>
- <li>motion: on|off|-</li>
- <li>occupancy: -|absent|present</li>
- <li>roomCtrlMode: buildingProtection|comfort|economy|preComfort</li>
- <li>setpointBuildingProtectionTemp: -|t/°C (Range: t = 0 °C ... 40 °C)</li>
- <li>setpointComfortTemp: -|t/°C (Range: t = 0 °C ... 40 °C)</li>
- <li>setpointEconomyTemp: -|t/°C (Range: t = 0 °C ... 40 °C)</li>
- <li>setpointPreComfortTemp: -|t/°C (Range: t = 0 °C ... 40 °C)</li>
- <li>setpointTemp: t/°C (Range: t = 0 °C ... 40 °C)</li>
- <li>solarPowered: yes|no</li>
- <li>temperature: t/°C (Sensor Range: t = 0 °C ... 40 °C)</li>
- <li>window: closed|open</li>
- <li>state: T: t/°C H: -|rH/% F: 0 ... 100/% SPT: t/°C O: -|absent|present M: -|on|off</li>
- </ul><br>
- The attr subType must be roomCtrlPanel.00. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired,
- see <a href="#EnOcean_teach-in">Bidirectional Teach-In / Teach-Out</a>.
- </li>
- <br><br>
- <li>Room Control Panels (D2-11-01 - D2-11-08)<br>
- [Thermokon EasySens SR06 LCD-2T/-2T rh -4T/-4T rh]<br>
- <ul>
- <li>T: t/°C H: rH/% SPT: t/°C F: auto|off|1|2|3</li>
- <li>cooling: on|off</li>
- <li>fanSpeed: auto|off|1|2|3</li>
- <li>heating: on|off</li>
- <li>humidity: rH/%</li>
- <li>occupancy: occupied|unoccupied</li>
- <li>setpointBase: t/°C (Range: t = 15 °C ... 30 °C)</li>
- <li>setpointShift: t/K (Range: t = -10 K ... 10 K)</li>
- <li>setpointShiftMax: t/K (Range: t = 0 K ... 10 K)</li>
- <li>setpointTemp: t/°C (Range: t = 5 °C ... 40 °C)</li>
- <li>setpointType: setpointTemp|setpointShift</li>
- <li>temperature: t/°C (Sensor Range: t = 0 °C ... 40 °C)</li>
- <li>trigger: heartbeat|sensor|input</li>
- <li>window: closed|open</li>
- <li>state: T: t/°C H: rH/% SPT: t/°C F: auto|off|1|2|3</li>
- </ul><br>
- The attr subType must be roomCtrlPanel.01. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired by Smart Ack,
- see <a href="#EnOcean_smartAck">SmartAck Learning</a>.
- </li>
- <br><br>
- <li>Fan Control (D2-20-00 - D2-20-02)<br>
- [Maico ECA x RC/RCH, ER 100 RC, untested]<br>
- <ul>
- <li>on|off|not_supported</li>
- <li>fanSpeed: 0 ... 100/%</li>
- <li>error: ok|air_filter|hardware|not_supported</li>
- <li>humidity: rH/%|not_supported</li>
- <li>humidityCtrl: disabled|enabled|not_supported</li>
- <li>roomSize: 0...350/m<sup>2</sup>|max</li>
- <li>roomSizeRef: unsed|not_used|not_supported</li>
- <li>setpointTemp: t/°C (Range: t = 0 °C ... 40 °C)</li>
- <li>teach: <result of teach procedure></li>
- <li>temperature: t/°C (Sensor Range: t = 0 °C ... 40 °C)</li>
- <li>state: on|off|not_supported</li>
- </ul><br>
- The attr subType must be fanCtrl.00. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired,
- see <a href="#EnOcean_teach-in">Bidirectional Teach-In / Teach-Out</a>.
- </li>
- <br><br>
- <li>AC Current Clamp (D2-32-00 - D2-32-02)<br>
- [untested]<br>
- <ul>
- <li>I1: I/A I2: I/A I3: I/A</li>
- <li>current1: I/A (Range: I = 0 A ... 4095 A)</li>
- <li>current2: I/A (Range: I = 0 A ... 4095 A)</li>
- <li>current3: I/A (Range: I = 0 A ... 4095 A)</li>
- <li>teach: <result of teach procedure></li>
- <li>state: I1: I/A I2: I/A I3: I/A</li>
- </ul><br>
- The attr subType must be currentClamp.00|currentClamp.01|currentClamp.02. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>LED Controller Status (EEP D2-40-00 - D2-40-01)<br>
- [untested]<br>
- <ul>
- <li>on|off</li>
- <li>blue: 0 % ... 100 %</li>
- <li>daylightHarvesting: on|off</li>
- <li>demandResp: on|off</li>
- <li>dim: 0 % ... 100 %</li>
- <li>green: 0 % ... 100 %</li>
- <li>occupany: unoccupied|occupied|unknown</li>
- <li>powerSwitch: on|off</li>
- <li>red: 0 % ... 100 %</li>
- <li>rgb: RRGGBB (red (R), green (G) or blue (B) color component values: 00 ... FF)</li>
- <li>teach: <result of teach procedure></li>
- <li>telegramType: event|heartbeat</li>
- <li>state: on|off</li>
- </ul><br>
- The attr subType must be ledCtrlState.00|ledCtrlState.01 This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Heat Recovery Ventilation (D2-50-00 - D2-50-11)<br>
- [untested]<br>
- <ul>
- <li>off|1...4|auto|demand|supplyAir|exhaustAir</li>
- <li>airQualidity1: 1/%</li>
- <li>airQualidity2: 1/%</li>
- <li>airQualidityThreshold: default|1/%</li>
- <li>CO2Threshold: default|1/%</li>
- <li>coolingProtection: on|off</li>
- <li>defrost: on|off</li>
- <li>deviceMode: master|slave</li>
- <li>drainHeater: on|off</li>
- <li>exhaustAirFlap: closed|opend</li>
- <li>exhaustAirFlow: h/m3 (Sensor Range: Q = 0 m3/h ... 1023 m3/h)</li>
- <li>exhaustFanSpeed: min (Sensor Range: n = 0 / min ... 4095 / min)</li>
- <li>exhaustTemp: t/°C (Sensor Range: t = -63 °C ... 63 °C)</li>
- <li>fault: bbbbbbbb bbbbbbbb bbbbbbbb bbbbbbbb (b = 0|1)</li>
- <li>filterMaintenance: required|not_required</li>
- <li>fireplaceSafetyMode: disabled|enabled</li>
- <li>heatExchangerBypass: closed|opend</li>
- <li>humidityThreshold: default|rH/%</li>
- <li>info: bbbbbbbb bbbbbbbb (b = 0|1)</li>
- <li>input: bbbbbbbb bbbbbbbb (b = 0|1)</li>
- <li>operationHours: [0 ... 589815]</li>
- <li>output: bbbbbbbb bbbbbbbb (b = 0|1)</li>
- <li>outdoorAirHeater: on|off</li>
- <li>outdoorTemp: t/°C (Sensor Range: t = -63 °C ... 63 °C)</li>
- <li>roomTemp: t/°C (Sensor Range: t = -63 °C ... 63 °C)</li>
- <li>roomTempCtrl: on|off</li>
- <li>roomTempSet: default|t/°C (Sensor Range: t = -63 °C ... 63 °C)</li>
- <li>supplyAirFlow: h/m3 (Sensor Range: Q = 0 m3/h ... 1023 m3/h)</li>
- <li>supplyAirFlap: closed|opend</li>
- <li>supplyAirHeater: on|off</li>
- <li>supplyFanSpeed: min (Sensor Range: n = 0 / min ... 4095 / min)</li>
- <li>supplyTemp: t/°C (Sensor Range: t = -63 °C ... 63 °C)</li>
- <li>SWVersion: [0 ... 4095]</li>
- <li>timerMode: on|off</li>
- <li>weeklyTimer: on|off</li>
- <li>state: off|1...4|auto|demand|supplyAir|exhaustAir</li>
- </ul><br>
- The attr subType must be heatRecovery.00. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired,
- see <a href="#EnOcean_teach-in">Bidirectional Teach-In / Teach-Out</a>.
- </li>
- <br><br>
- <li>Valve Control (EEP D2-A0-01)<br>
- <ul>
- <li>opens</li>
- <li>open</li>
- <li>closes</li>
- <li>closed</li>
- <li>teach: <result of teach procedure></li>
- <li>state: opens|open|closes|closed|teachIn|teachOut</li>
- </ul><br>
- The attr subType must be valveCtrl.00. This is done if the device was
- created by autocreate. To control the device, it must be bidirectional paired,
- see <a href="#EnOcean_teach-in">Bidirectional Teach-In / Teach-Out</a>.
- </li>
- <br><br>
- <li>Liquid Leakage Sensor (EEP D2-B0-51)<br>
- [untested]<br>
- <ul>
- <li>dry</li>
- <li>wet</li>
- <li>state: dry|wet</li>
- </ul><br>
- The attr subType must be liquidLeakage.51. This is done if the device was
- created by autocreate.
- </li>
- <br><br>
- <li>Generic Profiles<br>
- <ul>
- <li><00...64>-<channel name>: <value></li>
- <li><00...64>-<channel name>Unit: <value></li>
- <li><00...64>-<channel name>ValueType: value|setpointAbs|setpointRel</li>
- <li><00...64>-<channel name>ChannelType: teachIn|data|flag|enum</li>
- <li>teach: <result of teach procedure></li>
- </ul><br>
- The attr subType must be genericProfile. This is done if the device was
- created by autocreate. If the profile in slave mode is operated, especially the channel
- definition in the gpDef attributes must be entered manually.
- </li>
- <br><br>
- <li>RAW Command<br>
- <ul>
- <li>RORG: 1BS|4BS|ENC|MCS|RPS|SEC|STE|UTE|VLD</li>
- <li>dataSent: data (Range: 1 Byte hex ... 512 Byte hex)</li>
- <li>statusSent: status (Range: 0x00 ... 0xFF)</li>
- <li>state: RORG: rorg DATA: data STATUS: status ODATA: odata</li>
- </ul><br>
- With the help of this command data messages in hexadecimal format can be sent and received.
- The telegram types (RORG) 1BS and RPS are always received protocol-specific.
- For further information, see
- <a href="http://www.enocean-alliance.org/eep/">EnOcean Equipment Profiles (EEP)</a>.
- <br>
- Set attr subType to raw manually.
- </li>
- <br><br>
- <li>Light and Presence Sensor<br>
- [Omnio Ratio eagle-PM101]<br>
- <ul>
- <li>on</li>
- <li>off</li>
- <li>brightness: E/lx (Sensor Range: E = 0 lx ... 1000 lx)</li>
- <li>channel1: on|off<br>
- Motion message in depending on the brightness threshold</li>
- <li>channel2: on|off<br>
- Motion message</li>
- <li>motion: on|off<br>
- Channel 2</li>
- <li>state: on|off<br>
- Channel 2</li>
- </ul><br>
- The sensor also sends switching commands (RORG F6) with the SenderID-1.<br>
- Set attr subType to PM101 manually. Automatic teach-in is not possible,
- since no EEP and manufacturer ID are sent.
- </li>
- <br><br>
- <li>Radio Link Test<br>
- <ul>
- <li>standby|active|stopped</li>
- <li>msgLost: msgLost/%</li>
- <li>rssiMasterAvg: LP/dBm</li>
- <li>state: standby|active|stopped<br></li>
- </ul><br>
- The attr subType must be readioLinkTest. This is done if the device was
- created by autocreate or manually by <code>define <name> EnOcean A5-3F-00</code><br>.
- </li>
- </ul>
- </ul>
- =end html
- =cut
|