34_ESPEasy.pm 168 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465
  1. # $Id: 34_ESPEasy.pm 17666 2018-11-03 12:23:51Z dev0 $
  2. ################################################################################
  3. #
  4. # 34_ESPEasy.pm is a FHEM Perl module to control ESP82xx/ESP32 /w ESPEasy
  5. #
  6. # Copyright 2018 by dev0
  7. # FHEM forum: https://forum.fhem.de/index.php?action=profile;u=7465
  8. #
  9. # This file is part of FHEM.
  10. #
  11. # Fhem is free software: you can redistribute it and/or modify
  12. # it under the terms of the GNU General Public License as published by
  13. # the Free Software Foundation, either version 2 of the License, or
  14. # (at your option) any later version.
  15. #
  16. # Fhem is distributed in the hope that it will be useful,
  17. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. # GNU General Public License for more details.
  20. #
  21. # You should have received a copy of the GNU General Public License
  22. # along with fhem. If not, see <http://www.gnu.org/licenses/>.
  23. #
  24. ################################################################################
  25. package main;
  26. # ------------------------------------------------------------------------------
  27. # required perl/fhem modules
  28. # ------------------------------------------------------------------------------
  29. use strict;
  30. use warnings;
  31. use Data::Dumper;
  32. use MIME::Base64;
  33. use TcpServerUtils;
  34. use HttpUtils;
  35. use Color;
  36. use SetExtensions;
  37. my $module_version = "2.01"; # Version of this module
  38. # ------------------------------------------------------------------------------
  39. # modul version and required ESP Easy firmware / JSON lib version
  40. # ------------------------------------------------------------------------------
  41. my $minEEBuild = 128; # informational
  42. my $minJsonVersion = 1.02; # checked in received data
  43. # ------------------------------------------------------------------------------
  44. # default values
  45. # ------------------------------------------------------------------------------
  46. my $d_Interval = 300; # interval
  47. my $d_httpReqTimeout = 10; # timeout http req
  48. my $d_colorpickerCTww = 2000; # color temp for ww (kelvin)
  49. my $d_colorpickerCTcw = 6000; # color temp for cw (kelvin)
  50. my $d_maxHttpSessions = 3; # concurrent connects to a single esp
  51. my $d_maxQueueSize = 250; # max queue size,
  52. my $d_resendFailedCmd = 0; # do no00t resend failed http requests
  53. my $d_displayTextEncode = 1; # urlEncode Text for Displays
  54. my $d_displayTextWidth = 0; # display width, 0 => disable formating
  55. my $d_bridgePort = 8383; # bridge port if none specified
  56. my $d_disableLogin = 0; # Disable login if HTTP Code 302
  57. # ------------------------------------------------------------------------------
  58. # defaults for user defined cmds
  59. # ------------------------------------------------------------------------------
  60. my $d_args = 0; # min number of required arguments
  61. my $d_urlPlg = "/control?cmd="; # plugin command URL
  62. my $d_urlSys = "/?cmd="; # system command URL
  63. my $d_widget = ""; # widget defaults
  64. my $d_usage = ""; # usage defaults
  65. # ------------------------------------------------------------------------------
  66. # IP ranges that are allowed to connect to ESPEasy without attr allowedIPs set.
  67. # defined as regexp beause it's quicker than check against IP ranges...
  68. # ------------------------------------------------------------------------------
  69. my $d_allowedIPs = "192.168.0.0/16,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,"
  70. . "fe80::/10,fc00::/7,::1";
  71. my $d_localIPs = "^(127|192.168|172.(1[6-9]|2[0-9]|3[01])|10|169.254)\\.|"
  72. . "^(f(e[89ab]|[cd])|::1)";
  73. # ------------------------------------------------------------------------------
  74. # some mappings
  75. # ------------------------------------------------------------------------------
  76. my %ee_map = (
  77. build => { # ESP Easy build versions
  78. 1 => { type => "ESP Easy", ver => "STD" },
  79. 17 => { type => "ESP Easy Mega", ver => "STD" },
  80. 33 => { type => "ESP Easy 32", ver => "STD" },
  81. 65 => { type => "ARDUINO Easy", ver => "STD" },
  82. 81 => { type => "NANO Easy", ver => "STD" }
  83. },
  84. pins => { # Arduino pin names, keys must be upper case here
  85. # ESP82xx / ESP32
  86. D0 => 16, D1 => 5, D2 => 4, D3 => 0, D4 => 2, D5 => 14, D6 => 12,
  87. D7 => 13, D8 => 15, D9 => 3, D10 => 1, RX => 3, TX => 1, SD2 => 9,
  88. SD3 => 10,
  89. # ESP32
  90. TOUCH0 => 4, TOUCH1 => 0, TOUCH2 => 21, TOUCH3 => 15, TOUCH4 => 13,
  91. TOUCH5 => 12, TOUCH6 => 14, TOUCH7 => 27, TOUCH8 => 33, TOUCH9 => 32,
  92. # ESP32
  93. ADC1_0 => 36, ADC1_1 => 37, ADC1_2 => 38, ADC1_3 => 39, ADC1_4 => 32,
  94. ADC1_5 => 33, ADC1_6 => 34, ADC1_7 => 35, ADC2_0 => 4, ADC2_1 => 0,
  95. ADC2_2 => 21, ADC2_3 => 15, ADC2_4 => 13, ADC2_5 => 12, ADC2_6 => 14,
  96. ADC2_7 => 27, ADC2_8 => 25, ADC2_9 => 26
  97. },
  98. rst => { # readingSwitchText => {
  99. 10 => { # vType => {
  100. 1 => { 0 => "off", 1 => "on" }, # attr_rst => {org => new, ...},
  101. 2 => { 0 => "on", 1 => "off" } # attr_rst => {org => new, ...}
  102. } # }
  103. },
  104. onOff => { # on/off mappings within setFn
  105. on => 1,
  106. off => 0
  107. }
  108. );
  109. # ------------------------------------------------------------------------------
  110. # get commands
  111. # ------------------------------------------------------------------------------
  112. my %ee_gets = (
  113. bridge => {
  114. queuesize => {widget => "noArg", fn => ""},
  115. queuecontent => {widget => "", fn => ""},
  116. pinmap => {widget => "noArg", fn => ""},
  117. user => {widget => "noArg", fn => ""},
  118. pass => {widget => "noArg", fn => ""},
  119. },
  120. device => {
  121. pinmap => {widget => "noArg", fn => ""},
  122. setcmds => {widget => "noArg", fn => ""},
  123. adminpassword => {widget => "noArg", fn => ""}
  124. }
  125. );
  126. # ------------------------------------------------------------------------------
  127. # attributes
  128. # ------------------------------------------------------------------------------
  129. my %ee_attr = (
  130. all => {
  131. disable => { widget => "1,0" },
  132. disabledForIntervals => { widget => "" },
  133. do_not_notify => { widget => "0,1" },
  134. },
  135. bridge => {
  136. allowedIPs => { widget => "" },
  137. authentication => { widget => "1,0" },
  138. autocreate => { widget => "1,0" },
  139. autosave => { widget => "1,0" },
  140. combineDevices => { widget => "" },
  141. deniedIPs => { widget => "" },
  142. httpReqTimeout => { widget => "" },
  143. maxQueueSize => { widget => "10,25,50,100,250,500,1000,2500,5000,10000,25000,50000,100000" },
  144. maxHttpSessions => { widget => "0,1,2,3,4,5,6,7,8,9" },
  145. resendFailedCmd => { widget => "" },
  146. },
  147. device => {
  148. adjustValue => { widget => "" },
  149. disableRiskyCmds => { widget => "" },
  150. displayTextEncode => { widget => "1,0" },
  151. displayTextWidth => { widget => "" },
  152. IODev => { widget => "" },
  153. Interval => { widget => "" },
  154. mapLightCmds => { widget => "lights,nfx" },
  155. parseCmdResponse => { widget => "" },
  156. pollGPIOs => { widget => "" },
  157. presenceCheck => { widget => "1,0" },
  158. readingPrefixGPIO => { widget => "" },
  159. readingSuffixGPIOState => { widget => "" },
  160. readingSwitchText => { widget => "1,0,2" },
  161. setState => { widget => "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,25,50,100" },
  162. userSetCmds => { widget => "textField-long" },
  163. useSetExtensions => { widget => "0,1"},
  164. rgbGPIOs => { widget => "" },
  165. wwcwGPIOs => { widget => "" },
  166. colorpicker => { widget => "RGB,HSV,HSVp" },
  167. },
  168. # attr_rgbGPIOs => {
  169. # colorpicker => { widget => "RGB,HSV,HSVp" },
  170. # },
  171. attr_wwcwGPIOs => {
  172. colorpickerCTcw => { widget => "" },
  173. colorpickerCTww => { widget => "" },
  174. ctCW_reducedRange => { widget => "" },
  175. ctWW_reducedRange => { widget => "" },
  176. wwcwMaxBri => { widget => "0,1" },
  177. }
  178. );
  179. # ------------------------------------------------------------------------------
  180. # - get available set cmds based on attributes
  181. # - available cmds can be found in $data{ESPEasy}{device}{sets}...
  182. # - will be called from notifyFN() on INITIALIZED, REREADCFG and some attr changes
  183. # ------------------------------------------------------------------------------
  184. sub ESPEasy_initDevSets($)
  185. {
  186. my ($hash) = @_;
  187. my $name = $hash->{NAME};
  188. my $subtype = $hash->{SUBTYPE};
  189. # define colorpickers for use below
  190. my $cp_pct = "colorpicker,BRI,0,1,100";
  191. my $cp_bri = "colorpicker,BRI,0,1,255";
  192. my $cp_ct = "colorpicker,CT," . AttrVal($name,"ctWW_reducedRange",AttrVal($name,"colorpickerCTww",$d_colorpickerCTww)) . ",10," . AttrVal($name,"ctCW_reducedRange",AttrVal($name,"colorpickerCTcw",$d_colorpickerCTcw));
  193. my $cp_rgb = "colorpicker,".AttrVal($name,"colorpicker","HSVp");
  194. my %ee_sets = (
  195. bridge => { # bridge commands
  196. user => { args => 0, url => "", widget => "", usage => "<username>" },
  197. pass => { args => 0, url => "", widget => "", usage => "<password>" },
  198. clearqueue => { args => 0, url => "", widget => "noArg", usage => "" },
  199. active => { args => 0, url => "", widget => "noArg", usage => "" },
  200. inactive => { args => 0, url => "", widget => "noArg", usage => "" },
  201. reopen => { args => 0, url => "", widget => "noArg", usage => "" }
  202. },
  203. device => { # known ESP Easy plugin commands
  204. gpio => { args => 2, url => $d_urlPlg, widget => "", usage => "<pin> <0|1|off|on>" },
  205. pwm => { args => 2, url => $d_urlPlg, widget => "", usage => "<pin> <level>" },
  206. pwmfade => { args => 3, url => $d_urlPlg, widget => "", usage => "<pin> <target> <duration>" },
  207. pulse => { args => 3, url => $d_urlPlg, widget => "", usage => "<pin> <0|1|off|on> <duration>" },
  208. longpulse => { args => 3, url => $d_urlPlg, widget => "", usage => "<pin> <0|1|off|on> <duration>" },
  209. longpulse_ms => { args => 3, url => $d_urlPlg, widget => "", usage => "<pin> <0|1|off|on> <duration>" },
  210. servo => { args => 3, url => $d_urlPlg, widget => "", usage => "<servoNo> <pin> <position>" },
  211. lcd => { args => 3, url => $d_urlPlg, widget => "", usage => "<row> <col> <text>" },
  212. lcdcmd => { args => 1, url => $d_urlPlg, widget => "", usage => "<on|off|clear>" },
  213. mcpgpio => { args => 2, url => $d_urlPlg, widget => "", usage => "<port> <0|1|off|on>" },
  214. mcppulse => { args => 3, url => $d_urlPlg, widget => "", usage => "<port> <0|1|off|on> <duration>" },
  215. mcplongpulse => { args => 3, url => $d_urlPlg, widget => "", usage => "<port> <0|1|off|on> <duration>" },
  216. oled => { args => 3, url => $d_urlPlg, widget => "", usage => "<row> <col> <text>" },
  217. oledcmd => { args => 1, url => $d_urlPlg, widget => "", usage => "<on|off|clear>" },
  218. pcapwm => { args => 2, url => $d_urlPlg, widget => "", usage => "<pin> <Level>" },
  219. pcfgpio => { args => 2, url => $d_urlPlg, widget => "", usage => "<pin> <0|1|off|on>" },
  220. pcfpulse => { args => 3, url => $d_urlPlg, widget => "", usage => "<pin> <0|1|off|on> <duration>" },
  221. pcflongpulse => { args => 3, url => $d_urlPlg, widget => "", usage => "<pin> <0|1|off|on> <duration>" },
  222. irsend => { args => 3, url => $d_urlPlg, widget => "", usage => "<RAW> <B32 raw code> <frequenz> <pulse length> <blank length> | irsend <NEC|JVC|RC5|RC6|SAMSUNG|SONY|PANASONIC> <code> <bits>" }, #_P035_IRTX.ino
  223. status => { args => 2, url => $d_urlPlg, widget => "", usage => "<device> <pin>" },
  224. lights => { args => 1, url => $d_urlPlg, widget => "", usage => "<rgb|ct|pct|on|off|toggle> [color] [fading time] [pct]" },
  225. dots => { args => 1, url => $d_urlPlg, widget => "", usage => "<params>" },
  226. tone => { args => 3, url => $d_urlPlg, widget => "", usage => "<pin> <freq> <duration>" },
  227. rtttl => { args => 1, url => $d_urlPlg, widget => "", usage => "<RTTTL>" },
  228. dmx => { args => 1, url => $d_urlPlg, widget => "", usage => "<ON|OFF|LOG|value|channel=value[,value][...]>" },
  229. motorshieldcmd => { args => 5, url => $d_urlPlg, widget => "", usage => "<DCMotor|Stepper> <Motornumber> <Forward|Backward|Release> <Speed|Steps> <SINGLE|DOUBLE|INTERLEAVE|MICROSTEP>" },
  230. candle => { args => 0, url => $d_urlPlg, widget => "", usage => ":<FlameType>:<Color>:<Brightness>" }, # params are splited by ":" not " "
  231. neopixel => { args => 4, url => $d_urlPlg, widget => "", usage => "<led_nr> <red 0-255> <green 0-255> <blue 0-255>" },
  232. neopixelall => { args => 3, url => $d_urlPlg, widget => "", usage => "<red 0-255> <green 0-255> <blue 0-255>" },
  233. neopixelline => { args => 5, url => $d_urlPlg, widget => "", usage => "<start_led_nr> <end_led_nr> <red 0-255> <green 0-255> <blue 0-255>" },
  234. oledframedcmd => { args => 1, url => $d_urlPlg, widget => "", usage => "<on|off>" },
  235. serialsend => { args => 1, url => $d_urlPlg, widget => "", usage => "<string>" }, #_P020_Ser2Net.ino
  236. buzzer => { args => 0, url => $d_urlPlg, widget => "", usage => "" },
  237. inputswitchstate => { args => 0, url => $d_urlPlg, widget => "", usage => "" },
  238. nfx => { args => 1, url => $d_urlPlg, widget => "", usage => "<off|on|dim|line|one|all|rgb|fade|colorfade|rainbow|kitt|comet|theatre|scan|dualscan|twinkle|twinklefade|sparkle|wipe|fire|stop> <parameter>" },
  239. event => { args => 1, url => $d_urlPlg, widget => "", usage => "<string>" }, #changed url to sys-url;
  240. # rules related commands
  241. deepsleep => { args => 1, url => $d_urlSys, widget => "", usage => "<duration in s>" },
  242. publish => { args => 2, url => $d_urlSys, widget => "", usage => "<topic> <value>" },
  243. notify => { args => 0, url => $d_urlSys, widget => "", usage => "<notify nr> <message>" },
  244. reboot => { args => 0, url => $d_urlSys, widget => "noArg", usage => "" },
  245. rules => { args => 1, url => $d_urlSys, widget => "", usage => "<0|1|off|on>" }, #enable/disable use of rules
  246. sendto => { args => 2, url => $d_urlSys, widget => "", usage => "<unit nr> <command>" },
  247. sendtohttp => { args => 3, url => $d_urlSys, widget => "", usage => "<ip> <port> <url>" },
  248. sendtoudp => { args => 3, url => $d_urlSys, widget => "", usage => "<ip> <port> <url>" },
  249. taskvalueset => { args => 3, url => $d_urlSys, widget => "", usage => "<task/device nr> <value nr> <value/formula>" },
  250. taskvaluesetandrun => {args=> 3, url => $d_urlSys, widget => "", usage => "<task/device nr> <value nr> <value/formula>" },
  251. taskrun => { args => 1, url => $d_urlSys, widget => "", usage => "<task/device nr>" },
  252. timerset => { args => 2, url => $d_urlSys, widget => "", usage => "<timer nr> <duration in s>" },
  253. # dummies
  254. raw => { args => 1, url => $d_urlPlg, widget => "", usage => "<esp_comannd> [args]" },
  255. rawsystem => { args => 1, url => $d_urlSys, widget => "", usage => "<esp_comannd> [args]" },
  256. # internal cmds
  257. statusrequest => { args => 0, url => "", widget => "noArg", usage => "" },
  258. adminpassword => { args => 0, url => "", widget => "", usage => "<password>" },
  259. clearreadings => { args => 0, url => "", widget => "noArg", usage => "" },
  260. active => { args => 0, url => "", widget => "noArg", usage => "" },
  261. inactive => { args => 0, url => "", widget => "noArg", usage => "" }
  262. },
  263. system => { # system commands (another url)
  264. erase => { args => 0, url => $d_urlSys, widget => "noArg", usage => "" },
  265. reset => { args => 0, url => $d_urlSys, widget => "noArg", usage => "" },
  266. resetflashwritecounter => { args => 0, url => $d_urlSys, widget => "noArg", usage => "" },
  267. },
  268. attr_rgbGPIOs => {
  269. rgb => { args => 1, url => $d_urlPlg, widget => $cp_rgb, usage => "<rrggbb> [fadetime] [delay +/-ms]" },
  270. pct => { args => 1, url => $d_urlPlg, widget => $cp_pct, usage => "<pct> [fadetime]" },
  271. on => { args => 0, url => $d_urlPlg, widget => "noArg", usage => "" },
  272. off => { args => 0, url => $d_urlPlg, widget => "noArg", usage => "" },
  273. toggle => { args => 0, url => $d_urlPlg, widget => "noArg", usage => "" },
  274. },
  275. attr_wwcwGPIOs => {
  276. pct => { args => 1, url => $d_urlPlg, widget => $cp_pct, usage => "<pct> [fadetime]" },
  277. ct => { args => 1, url => $d_urlPlg, widget => $cp_ct, usage => "<ct> [fadetime] [pct bri]" },
  278. on => { args => 0, url => $d_urlPlg, widget => "noArg", usage => "" },
  279. off => { args => 0, url => $d_urlPlg, widget => "noArg", usage => "" },
  280. toggle => { args => 0, url => $d_urlPlg, widget => "noArg", usage => "" },
  281. },
  282. attr_lights => { # Lights
  283. rgb => { args => 1, url => $d_urlPlg, widget => $cp_rgb, usage => "<rrggbb> [fadetime] [delay +/-ms]" },
  284. pct => { args => 1, url => $d_urlPlg, widget => $cp_pct, usage => "<pct> [fadetime]" },
  285. ct => { args => 1, url => $d_urlPlg, widget => $cp_ct, usage => "<ct> [fadetime] [pct bri]" },
  286. on => { args => 0, url => $d_urlPlg, widget => "", usage => "[fadetime]" },
  287. off => { args => 0, url => $d_urlPlg, widget => "", usage => "[fadetime]" },
  288. toggle => { args => 0, url => $d_urlPlg, widget => "", usage => "[fadetime]" },
  289. },
  290. attr_nfx => { # nfx commands - Forum #73949
  291. rgb => { args => 1, url => $d_urlPlg, widget => $cp_rgb, usage => "<rrggbb> [fadetime] [delay +/-ms]" },
  292. pct => { args => 1, url => $d_urlPlg, widget => $cp_pct, usage => "<pct> [fadetime]" },
  293. ct => { args => 1, url => $d_urlPlg, widget => $cp_ct, usage => "<ct> [fadetime] [pct bri]" },
  294. on => { args => 0, url => $d_urlPlg, widget => "", usage => "[fadetime] [delay +/-ms]" },
  295. off => { args => 0, url => $d_urlPlg, widget => "", usage => "[fadetime] [delay +/-ms]" },
  296. toggle => { args => 0, url => $d_urlPlg, widget => "", usage => "[fadetime]" },
  297. all => { args => 1, url => $d_urlPlg, widget => $cp_rgb, usage => "<rrggbb> [fadetime] [delay +/-ms]" },
  298. bgcolor => { args => 1, url => $d_urlPlg, widget => $cp_rgb, usage => "<rrggbb>" },
  299. colorfade => { args => 2, url => $d_urlPlg, widget => "", usage => "<rrggbb_start> <rrggbb_end> [startpixel] [endpixel]" },
  300. comet => { args => 1, url => $d_urlPlg, widget => $cp_rgb, usage => "<rrggbb> [speed +/- 0-50]" },
  301. dim => { args => 1, url => $d_urlPlg, widget => $cp_bri, usage => "<value 0-255>" },
  302. dualscan => { args => 1, url => $d_urlPlg, widget => $cp_rgb, usage => "<rrggbb> [rrggbb background] [speed 0-50]" },
  303. fade => { args => 1, url => $d_urlPlg, widget => $cp_rgb, usage => "<rrggbb> [fadetime ms] [delay +/-ms]" },
  304. fire => { args => 0, url => $d_urlPlg, widget => "", usage => "[fps] [brightness 0-255] [cooling 20-100] [sparking 50-200]" },
  305. kitt => { args => 1, url => $d_urlPlg, widget => "", usage => "<rrggbb> [speed 0-50]" },
  306. line => { args => 3, url => $d_urlPlg, widget => "", usage => "<startpixel> <endpixel> <rrggbb>" },
  307. one => { args => 2, url => $d_urlPlg, widget => "", usage => "<pixel> <rrggbb>" },
  308. scan => { args => 1, url => $d_urlPlg, widget => $cp_rgb, usage => "<rrggbb> [rrggbb background] [speed 0-50]" },
  309. sparkle => { args => 1, url => $d_urlPlg, widget => $cp_rgb, usage => "<rrggbb> [rrggbb background] [speed 0-50]" },
  310. stop => { args => 0, url => $d_urlPlg, widget => "noArg", usage => "" },
  311. theatre => { args => 1, url => $d_urlPlg, widget => $cp_rgb, usage => "<rrggbb> [rrggbb background] [speed +/- 0-50]" },
  312. twinkle => { args => 1, url => $d_urlPlg, widget => $cp_rgb, usage => "<rrggbb> [rrggbb background] [speed 0-50]" },
  313. twinklefade => { args => 1, url => $d_urlPlg, widget => $cp_rgb, usage => "<rrggbb> [number of pixels] [speed 0-50]" },
  314. wipe => { args => 1, url => $d_urlPlg, widget => $cp_rgb, usage => "<rrggbb> [rrggbb dot] [speed +/- 0-50]" },
  315. faketv => { args => 0, url => $d_urlPlg, widget => "", usage => "[startpixel] [endpixel]" },
  316. simpleclock => { args => 0, url => $d_urlPlg, widget => "", usage => "[bigtickcolor] [smalltickcolor] [hourcolor] [minutecolor] [secondcolor]" },
  317. count => { args => 1, url => $d_urlPlg, widget => "slider,1,1,50", usage => "<value>" },
  318. fadedelay => { args => 1, url => $d_urlPlg, widget => "slider,-5000,10,5000", usage => "<value in +/-ms>" },
  319. fadetime => { args => 1, url => $d_urlPlg, widget => "slider,0,100,10000", usage => "<value in ms>" },
  320. rainbow => { args => 0, url => $d_urlPlg, widget => "slider,-10,1,10", usage => "[speed +/- 0-50]" },
  321. speed => { args => 1, url => $d_urlPlg, widget => "slider,-50,1,50", usage => "<value 0-50>" },
  322. }
  323. ); # hash %ee_sets
  324. # gather required categories
  325. my @categories;
  326. my $mapLightsCmd = lc AttrVal($name,"mapLightCmds",0);
  327. push (@categories, $subtype);
  328. if ($subtype eq "device") {
  329. push (@categories, "system") if !AttrVal("$name","disableRiskyCmds",0);
  330. push (@categories, "attr_".$mapLightsCmd) if $mapLightsCmd && defined $ee_sets{"attr_$mapLightsCmd"};
  331. push (@categories, "attr_rgbGPIOs") if AttrVal("$name","rgbGPIOs",0);
  332. push (@categories, "attr_wwcwGPIOs") if AttrVal("$name","wwcwGPIOs",0);
  333. }
  334. # build hash of avail commands
  335. # todo: with hashref copy, see https://perlmaven.com/how-to-insert-a-hash-in-another-hash ???
  336. my %activeSets;
  337. foreach my $cat (@categories) {
  338. foreach my $cmd (keys %{ $ee_sets{$cat} } ) {
  339. $activeSets{$cmd} = $ee_sets{$cat}{$cmd};
  340. }
  341. }
  342. # write all mapped subcms in $hash->{helper}{mapLightCmds}, will be used in SetFn;
  343. delete $hash->{helper}{mapLightCmds};
  344. if ($mapLightsCmd) {
  345. foreach (keys %{$ee_sets{"attr_$mapLightsCmd"}}) {
  346. $hash->{helper}{mapLightCmds}{$_} = $mapLightsCmd;
  347. }
  348. }
  349. # user cmds/maps
  350. my $userSetCmds = AttrVal($name,"userSetCmds",0);
  351. if ($userSetCmds) {
  352. my %ua = eval($userSetCmds);
  353. if ($@) {
  354. Log3 $name, 2, "An error occourred while building user defined cmds/maps: $@";
  355. return $@;
  356. }
  357. foreach my $plugin (keys %ua) {
  358. my $p = lc($plugin);
  359. # use reverse order to be sure plugin's url is set before subcmds.
  360. my @keys = reverse sort keys %{ $ua{$plugin} };
  361. foreach my $key (@keys) {
  362. # key is a mapped subcmd
  363. if ( ref($ua{$plugin}{$key}) eq "HASH" ) {
  364. foreach my $subcmd (keys %{ $ua{$plugin}{$key} }) {
  365. my $sc = lc($subcmd);
  366. $activeSets{$sc} = $ua{$plugin}{$key}{$subcmd};
  367. # write all mapped subcms in $hash->{helper}{mapLightCmds}, will be used in SetFn;
  368. $hash->{helper}{mapLightCmds}{$sc} = $p;
  369. # Set defaults for mapped cmds and be sure all keys are defined in following fns
  370. $activeSets{$sc}{args} = $d_args if !defined $activeSets{$sc}{args};
  371. $activeSets{$sc}{widget} = $d_widget if !defined $activeSets{$sc}{widget};
  372. $activeSets{$sc}{usage} = $d_usage if !defined $activeSets{$sc}{usage};
  373. # use plugin's url, if not defined use default
  374. $activeSets{$sc}{url} = defined $activeSets{$p}{url} ? $activeSets{$p}{url} : $d_urlPlg
  375. if !defined $activeSets{$sc}{url};
  376. }
  377. }
  378. # key is param for plugin cmd
  379. else {
  380. $activeSets{$p}{$key} = $ua{$plugin}{$key};
  381. }
  382. }
  383. # Set defaults for plugin cmds and be sure all keys are defined in following fns
  384. $activeSets{$p}{args} = $d_args if !defined $activeSets{$p}{args};
  385. $activeSets{$p}{url} = $d_urlPlg if !defined $activeSets{$p}{url};
  386. $activeSets{$p}{widget} = $d_widget if !defined $activeSets{$p}{widget};
  387. $activeSets{$p}{usage} = $d_usage if !defined $activeSets{$p}{usage};
  388. }
  389. }
  390. # add help command
  391. $activeSets{help} = { args => 1, widget => join(",",sort keys %activeSets), url => "", usage => "<".join(",",sort keys %activeSets).">" };
  392. # reference to all available cmds
  393. $data{ESPEasy}{$name}{sets} = \%activeSets;
  394. Log3 $name, 4, "ESPEasy $name: Available set cmds/maps (re)initialized.";
  395. }
  396. # ------------------------------------------------------------------------------
  397. # enable colorpicker etc. only if attrs (rgb|wwcw)GPIOs|mapLightCmds are set
  398. # called by NotifyFn
  399. # ------------------------------------------------------------------------------
  400. sub ESPEasy_initDevAttrs($) {
  401. my ($hash) = @_;
  402. my ($name, $subtype) = ($hash->{NAME}, $hash->{SUBTYPE});
  403. # add attr_.* categories if corresponding attr is in use
  404. my @cats = ($subtype, "all");
  405. foreach (keys %ee_attr) {
  406. if (m/^attr_(\w+)$/) {
  407. push(@cats, "attr_".$1) if defined AttrVal($name, $1, undef) || defined AttrVal($name,"mapLightCmds",undef);
  408. }
  409. }
  410. # push attributes from selected categories in array @attrs
  411. my @attrs;
  412. foreach my $cat (@cats) {
  413. foreach my $attr (sort keys %{ $ee_attr{$cat} }) {
  414. my $w = $ee_attr{$cat}{$attr}{widget};
  415. # push attrs with corresponding widget
  416. push(@attrs, $attr . ($w ne "" ? ":$w" : ""));
  417. }
  418. }
  419. push (@attrs, $readingFnAttributes);
  420. setDevAttrList($name, join(" ", sort @attrs));
  421. Log3 $name, 4, "ESPEasy $name: Available attributes (re)initialized.";
  422. }
  423. # ------------------------------------------------------------------------------
  424. sub ESPEasy_Initialize($)
  425. {
  426. my ($hash) = @_;
  427. #common
  428. $hash->{DefFn} = "ESPEasy_Define";
  429. $hash->{GetFn} = "ESPEasy_Get";
  430. $hash->{SetFn} = "ESPEasy_Set";
  431. $hash->{AttrFn} = "ESPEasy_Attr";
  432. $hash->{UndefFn} = "ESPEasy_Undef";
  433. $hash->{ShutdownFn} = "ESPEasy_Shutdown";
  434. $hash->{DeleteFn} = "ESPEasy_Delete";
  435. $hash->{RenameFn} = "ESPEasy_Rename";
  436. $hash->{NotifyFn} = "ESPEasy_Notify";
  437. $hash->{StateFn} = "ESPEasy_State";
  438. #provider
  439. $hash->{ReadFn} = "ESPEasy_Read"; # ESP http request will be parsed here
  440. $hash->{WriteFn} = "ESPEasy_Write"; # called from logical module's IOWrite
  441. $hash->{Clients} = ":ESPEasy:"; # used by dispatch,$hash->{TYPE} of receiver
  442. my %matchList = ( "1:ESPEasy" => ".*" );
  443. $hash->{MatchList} = \%matchList;
  444. #consumer
  445. $hash->{ParseFn} = "ESPEasy_dispatchParse";
  446. $hash->{Match} = ".+";
  447. # add all attributes to hash, unnecessary attributes will be removed in
  448. # ESPEasy_initDevAttrs called from NotifyFn
  449. my @attr;
  450. foreach my $subtype (keys %ee_attr) {
  451. foreach my $attr ( keys %{ $ee_attr{$subtype} } ) {
  452. push (
  453. @attr, $attr . (
  454. $ee_attr{$subtype}{$attr}{widget} ne ""
  455. ? ":" . $ee_attr{$subtype}{$attr}{widget}
  456. : ""
  457. ) # ternary if
  458. ) # push
  459. } # foreach $attr
  460. } # foreach $subtype
  461. push (@attr, $readingFnAttributes);
  462. $hash->{AttrList} = join(" ",sort @attr);
  463. # for the next release...
  464. # $hash->{AttrRenameMap} = { "ctCW_reducedRange" => "ctCWreducedRange",
  465. # "ctWW_reducedRange" => "ctWWreducedRange",
  466. # "colorpickerCTcw" => "ctCWColorpicker"
  467. # "colorpickerCTww" => "ctWWcolorpicker"
  468. # "wwcwMaxBri" => "ctMaxBri"
  469. # };
  470. }
  471. # ------------------------------------------------------------------------------
  472. sub ESPEasy_Define($$) # only called when defined, not on reload.
  473. {
  474. my ($hash, $def) = @_;
  475. my @a = split("[ \t][ \t]*", $def);
  476. my $usage = "\nUse 'define <name> ESPEasy <bridge> <PORT>"
  477. . "\nUse 'define <name> ESPEasy <ip|fqdn> <PORT> <IODev> <IDENT>";
  478. return "Wrong syntax: $usage" if(int(@a) < 3);
  479. my $name = $a[0];
  480. my $type = $a[1];
  481. my $host = $a[2];
  482. my $port;
  483. $port = $a[3] if defined $a[3];
  484. $port = 8383 if !defined $port && $host eq "bridge";
  485. my $iodev = $a[4] if defined $a[4];
  486. my $ident = $a[5] if defined $a[5];
  487. my $ipv = $port =~ m/^IPV6:/ ? 6 : 4;
  488. return "ERROR: only 1 ESPEasy bridge can be defined!"
  489. if($host eq "bridge" && $modules{ESPEasy}{defptr}{BRIDGE}{$ipv});
  490. return "ERROR: missing arguments for subtype device: $usage"
  491. if ($host ne "bridge" && !(defined $a[4]) && !(defined $a[5]));
  492. return "ERROR: too much arguments for a bridge: $usage"
  493. if ($host eq "bridge" && defined $a[4]);
  494. (ESPEasy_isIPv4($host) || ESPEasy_isFqdn($host) || $host eq "bridge")
  495. ? $hash->{HOST} = $host
  496. : return "ERROR: invalid IPv4 address, fqdn or keyword bridge: '$host'";
  497. # check fhem.pl version (req. setDevAttrList Forum # 85868, 86010)
  498. AttrVal('global','version','') =~ m/^fhem.pl:(\d+)\/.*$/;
  499. return "ERROR: fhem.pl is too old to use $type module. "
  500. ."Version 16453/2018-03-21 is required at least."
  501. if (not(defined $1) || $1 < 16453);
  502. $hash->{PORT} = $port if defined $port;
  503. $hash->{IDENT} = $ident if defined $ident;
  504. $hash->{VERSION} = $module_version;
  505. $hash->{NOTIFYDEV} = "global";
  506. #--- BRIDGE -------------------------------------------------
  507. if ($hash->{HOST} eq "bridge") {
  508. $hash->{SUBTYPE} = "bridge";
  509. $hash->{IPV} = $ipv;
  510. $modules{ESPEasy}{defptr}{BRIDGE}{$ipv} = $hash;
  511. if ($init_done && !defined($hash->{OLDDEF})) {
  512. CommandAttr(undef,"$name room $type");
  513. CommandAttr(undef,"$name group $type Bridge");
  514. CommandAttr(undef,"$name authentication 0");
  515. CommandAttr(undef,"$name combineDevices 0");
  516. }
  517. $hash->{".bau"} = getKeyValue($type."_".$name."-user");
  518. $hash->{".bap"} = getKeyValue($type."_".$name."-pass");
  519. # only informational
  520. $hash->{MAX_HTTP_SESSIONS} = $d_maxHttpSessions;
  521. $hash->{MAX_QUEUE_SIZE} = $d_maxQueueSize;
  522. # Check OS IPv6 support
  523. if ($ipv == 6) {
  524. use constant HAS_AF_INET6 => defined eval { Socket::AF_INET6() };
  525. Log3 $name, 2, "$type $name: WARNING: Your system seems to have no IPv6 support." if !HAS_AF_INET6;
  526. }
  527. }
  528. #--- DEVICE -------------------------------------------------
  529. else {
  530. $hash->{INTERVAL} = $d_Interval;
  531. $hash->{SUBTYPE} = "device";
  532. $hash->{sec}{admpwd} = getKeyValue($type."_".$name."-admpwd");
  533. AssignIoPort($hash,$iodev) if !defined $hash->{IODev};
  534. InternalTimer(gettimeofday()+5+rand(5), "ESPEasy_statusRequest", $hash);
  535. readingsSingleUpdate($hash, 'state', 'Initialized',1);
  536. my $io = (defined($hash->{IODev}{NAME})) ? $hash->{IODev}{NAME} : "none";
  537. Log3 $hash->{NAME}, 4, "$type $name: Opened for $ident $host:$port using bridge $io";
  538. }
  539. ESPEasy_initDevSets($hash);
  540. ESPEasy_loadRequiredModules($hash);
  541. return undef;
  542. }
  543. # ------------------------------------------------------------------------------
  544. sub ESPEasy_Get(@)
  545. {
  546. my ($hash, $name, $cmd, @args) = @_;
  547. return "argument is missing" if !$cmd;
  548. my $subtype = $hash->{SUBTYPE};
  549. $cmd = lc $cmd;
  550. my $ret;
  551. if( !grep( m/^$cmd$/, keys %{ $ee_gets{$subtype} } ) || $cmd eq "?") {
  552. my @clist;
  553. foreach my $c ( sort keys %{ $ee_gets{$subtype} } ) {
  554. my $w = $ee_gets{$subtype}{$c}{widget} ? ":".$ee_gets{$subtype}{$c}{widget} : "";
  555. push(@clist, $c.$w);
  556. }
  557. return "Unknown argument $cmd, choose one of ". join(" ",@clist);
  558. }
  559. # lookup sub fn to be executed or use "ESPEasy_Get_$cmd"
  560. my $fn = $ee_gets{$subtype}{$cmd}{fn};
  561. $fn = $fn ne "" ? $fn : "ESPEasy_Get_$cmd";
  562. # exec $fn
  563. return &{\&{ $fn }}(@_);
  564. }
  565. # ------------------------------------------------------------------------------
  566. # GetFn subs, called by reference to $cmd name or global $gets{$cmd}{fn}
  567. # ------------------------------------------------------------------------------
  568. # ------------------------------------------------------------------------------
  569. # get username or password that is being used
  570. # ------------------------------------------------------------------------------
  571. sub ESPEasy_Get_user(@)
  572. {
  573. my ($hash, $name, $cmd, @args) = @_;
  574. return defined $hash->{".bau"} ? $hash->{".bau"} : "username is not defined, yet.";
  575. }
  576. # ------------------------------------------------------------------------------
  577. # get username or password that is being used
  578. # ------------------------------------------------------------------------------
  579. sub ESPEasy_Get_pass(@)
  580. {
  581. my ($hash, $name, $cmd, @args) = @_;
  582. return defined $hash->{".bap"} ? $hash->{".bap"} : "password is not defined, yet.";
  583. }
  584. # ------------------------------------------------------------------------------
  585. # get arduino pin mappings that can be used
  586. # ------------------------------------------------------------------------------
  587. sub ESPEasy_Get_adminpassword(@)
  588. {
  589. my ($hash, $name, $cmd, @args) = @_;
  590. return defined $hash->{sec}{admpwd} ? $hash->{sec}{admpwd} : "password is not defined, yet.";
  591. }
  592. # ------------------------------------------------------------------------------
  593. # get formated list of available commands
  594. # ------------------------------------------------------------------------------
  595. sub ESPEasy_Get_setcmds(@)
  596. {
  597. my ($hash) = @_;
  598. my ($type, $name) = ($hash->{TYPE}, $hash->{NAME});
  599. my ($args, $url, $widget, $usage);
  600. my $line = "-" x 79 . "\n";
  601. $line .= "plugin / mapped cmd |mapped to plugin |args|url |widget |\n";
  602. $line .= "-" x 79 . "\n";
  603. foreach my $cmd (sort keys %{ $data{$type}{$name}{sets} }) {
  604. next if $cmd =~ m/^(help|clearreadings|statusrequest)$/;
  605. my $plugin = defined $hash->{helper}{mapLightCmds}{$cmd} ? $hash->{helper}{mapLightCmds}{$cmd} : "-";
  606. $line .= substr( $cmd . " " x (21 - length($cmd)) ,0,21 ) ."|";
  607. $line .= substr( $plugin . " " x (17 - length($plugin)) ,0,17 ) ."|";
  608. my $c = $data{$type}{$name}{sets}{$cmd}; # just a little bit shorter...
  609. $line .= substr( $c->{args} . " " x(4 - length($c->{args})) ,0,4 ) ."|";
  610. $line .= substr( $c->{url} . " " x(14 - length($c->{url})) ,0,14 ) ."|";
  611. $line .= substr( $c->{widget} . " " x(18 - length($c->{widget})) ,0,18 ) ."|\n";
  612. }
  613. # replace lace braces for FHEMWEB
  614. if ($hash->{CL}{TYPE} eq "FHEMWEB") {
  615. $line =~ s/</&lt;/g;
  616. $line =~ s/>/&gt;/g;
  617. }
  618. return $line;
  619. }
  620. # ------------------------------------------------------------------------------
  621. # get arduino pin mappings that can be used
  622. # ------------------------------------------------------------------------------
  623. sub ESPEasy_Get_pinmap(@)
  624. {
  625. my $ret .= "\nAlias => GPIO\n";
  626. $ret .= "---------------\n";
  627. foreach (sort keys %{$ee_map{pins}}) {
  628. $ret .= $_." " x (8-length $_ ) ."=> $ee_map{pins}{$_}\n";
  629. }
  630. return $ret;
  631. }
  632. # ------------------------------------------------------------------------------
  633. # simple get queue sizes
  634. # ------------------------------------------------------------------------------
  635. sub ESPEasy_Get_queuesize(@)
  636. {
  637. my ($hash, $name, $cmd, @args) = @_;
  638. my $ret;
  639. foreach (keys %{ $hash->{helper}{queue} }) {
  640. $ret .= "$_:".scalar @{$hash->{helper}{queue}{"$_"}}." ";
  641. }
  642. return $ret ? $ret : "No queues in use.";
  643. }
  644. # ------------------------------------------------------------------------------
  645. # get queue content of all/selected queues
  646. # ------------------------------------------------------------------------------
  647. sub ESPEasy_Get_queuecontent(@)
  648. {
  649. my ($hash, $name, $cmd, @args) = @_;
  650. my $host = $args[0];
  651. my $ret; my $i = 0; my $j = 0;
  652. my $mseclog = AttrVal("global","mseclog",0);
  653. if (defined $hash->{helper}{queue}) {
  654. my $Xspace = " "x ($mseclog ? 20 : 16); # different spacer if attr/global/mseclog
  655. my $Xdash = ("-"x80)."\n"; # just a few dashes;
  656. foreach my $q (sort keys %{ $hash->{helper}{queue} }) {
  657. next if $host ne "" && $q !~ m/^$host$/;
  658. $ret .= "\nQueue for host $q:\n";
  659. $ret .= $Xdash."Time:".$Xspace."Cmd:\n".$Xdash; # queue title
  660. $i = 0;
  661. foreach my $qe (@{ $hash->{helper}{queue}{$q} }) {
  662. my ($s,$ms) = split(/\./,$qe->{ts}); # get secs + mSecs, see WriteFn
  663. my $ts = FmtDateTime($s); # format time string as FHEM does
  664. $ts .= sprintf(".%03d", $ms/1000) if $mseclog; # add .msecs if attr/global/mseclog
  665. $ret .= $ts ." " .$qe->{cmd} ." " .join(",",@{$qe->{cmdArgs}})."\n";
  666. $i++ # single queue counter
  667. }
  668. $ret .= "=> $i entries\n"; # single queue counter
  669. $j += $i; # add single counter to overall counter
  670. }
  671. }
  672. return $ret ? $ret."\n==> Number of all requested queue entries: $j entries"
  673. : "No specified queues active.";
  674. }
  675. # ------------------------------------------------------------------------------
  676. sub ESPEasy_Set($$@)
  677. {
  678. my ($hash, $name, $cmd, @params) = @_;
  679. my ($type, $subtype) = ($hash->{TYPE}, $hash->{SUBTYPE});
  680. # case insensitive
  681. $cmd = lc($cmd) if $cmd;
  682. # get current cmd list if cmd is __unknown__
  683. my $clist = ESPEasy_isCmdAvailable($hash,$cmd);
  684. if (defined $clist) {
  685. if (AttrVal($name,"useSetExtensions",0)) {
  686. Log3 $name, 3, "$type $name: set $name $cmd ".join(" ",@params)." (use set extensions)"
  687. if $cmd =~ m/^(o(n|ff)-(for-timer|till(-overnight)?)|blink|intervals|toggle)$/ ;
  688. return SetExtensions($hash, $clist, $name, $cmd, @params);
  689. }
  690. my $err = "Unknown argument $cmd, choose one of $clist";
  691. return "Unknown argument $cmd, choose one of $clist";
  692. }
  693. SetExtensionsCancel($hash); # Forum #53137
  694. return if (IsDisabled $name) && $cmd !~ m/^(in)?active$/;
  695. # Log set command
  696. Log3 $name, 3, "$type $name: set $name $cmd ".join(" ",@params) if $cmd !~ m/^(\?|user|pass|help|active|inactive)$/;
  697. # check if there are all required arguments
  698. my $set = $data{ESPEasy}{$name}{sets}{$cmd};
  699. if($set->{args} && scalar @params < $set->{args}) {
  700. Log3 $name, 2, "$type $name: Missing argument: 'set $name $cmd ".join(" ",@params)."'" if $cmd ne "help";
  701. return "Missing argument: $cmd needs at least $set->{args} argument" . ($set->{args} < 2 ? "" : "s")."\n"
  702. . "Usage: 'set $name $cmd $set->{usage}'";
  703. }
  704. # if (defined &{\&{ "ESPEasy_Set_$cmd" }}) {
  705. # return &{\&{ "ESPEasy_Set_$cmd" }}(@_);
  706. # }
  707. # Internal cmds
  708. if ($cmd eq "help") {
  709. my $usage = $data{ESPEasy}{$name}{sets}{$params[0]}{usage};
  710. return $usage ? "Usage: set $name $params[0] $usage"
  711. : "Note: '$params[0]' is not registered as an ESPEasy command. "
  712. . "See attribute userSetCmds to register your own or unsupported commands.";
  713. }
  714. elsif ($cmd =~ m/^clearqueue$/i) {
  715. delete $hash->{helper}{queue};
  716. Log3 $name, 3, "$type $name: Queues erased.";
  717. return undef;
  718. }
  719. elsif ($cmd =~ m/^user|pass$/ ) {
  720. setKeyValue($hash->{TYPE}."_".$hash->{NAME}."-".$cmd,$params[0]);
  721. $cmd eq "user" ? $hash->{".bau"} = $params[0] : $hash->{".bap"} = $params[0];
  722. return undef;
  723. }
  724. elsif ($cmd eq "reopen" ) {
  725. ESPEasy_TcpServer_Close($hash);
  726. ESPEasy_TcpServer_Open($hash);
  727. return undef;
  728. }
  729. elsif ($cmd =~ m/^(in)?active$/) {
  730. if (AttrVal($name, "disable", 0) == 1) {
  731. my $msg = "Set cmd '$cmd' ignored, attribute 'disable' is enabled.";
  732. Log3 $name, 3, "$type $name: $msg";
  733. return $msg;
  734. }
  735. elsif ($cmd eq "inactive") {
  736. if ($subtype eq "bridge") {
  737. ESPEasy_TcpServer_Close($hash);
  738. }
  739. else { # device
  740. }
  741. readingsSingleUpdate($hash, "state", "inactive", 1);
  742. Log3 $name, 3, "$type $name: Device disabled";
  743. }
  744. elsif ($cmd eq "active") {
  745. if (ReadingsVal($name, "state", "") ne "Initialized") {
  746. if ($subtype eq "bridge") {
  747. ESPEasy_TcpServer_Open($hash);
  748. }
  749. else {
  750. readingsSingleUpdate($hash, "state", "Initialized", 1);
  751. ESPEasy_setState($hash);
  752. Log3 $name, 3, "$type $name: Device enabled";
  753. }
  754. return undef;
  755. }
  756. }
  757. return undef;
  758. }
  759. # Device cmds
  760. elsif ($cmd eq "statusrequest") {
  761. ESPEasy_statusRequest($hash);
  762. return undef;
  763. }
  764. elsif ($cmd eq "clearreadings") {
  765. ESPEasy_clearReadings($hash);
  766. return undef;
  767. }
  768. elsif ($cmd =~ m/^adminpassword$/ ) {
  769. setKeyValue($hash->{TYPE}."_".$hash->{NAME}."-admpwd", $params[0]);
  770. $hash->{sec}{admpwd} = $params[0];
  771. return undef;
  772. }
  773. return undef if $subtype eq "bridge";
  774. # urlEncode <text> parameter
  775. @params = ESPEasy_urlEncodeDisplayText($hash,$cmd,@params);
  776. # pin mapping (eg. D8 -> 15), <pin> parameter
  777. my $pp = ESPEasy_paramPos($hash,$cmd,'<pin>');
  778. if ($pp && $params[$pp-1] =~ m/^[a-zA-Z]/) {
  779. Log3 $name, 5, "$type $name: Pin mapping ". uc $params[$pp-1] .
  780. " => ".$ee_map{pins}{uc $params[$pp-1]};
  781. $params[$pp-1] = $ee_map{pins}{uc $params[$pp-1]};
  782. }
  783. # onOff mapping (on/off -> 1/0), <0|1|off|on> parameter
  784. $pp = ESPEasy_paramPos($hash,$cmd,'<0|1|off|on>');
  785. if ($pp) {
  786. my $ooArg = lc($params[$pp-1]);
  787. my $ooVal = defined $ee_map{onOff}{$ooArg} ? $ee_map{onOff}{$ooArg} : undef;
  788. if (defined $ooVal) {
  789. Log3 $name, 5, "$type $name: onOff mapping ". $params[$pp-1]." => $ooVal";
  790. $params[$pp-1] = $ooVal;
  791. }
  792. }
  793. # re-map cmds if necessary
  794. if (defined $hash->{helper}{mapLightCmds} && defined $hash->{helper}{mapLightCmds}{$cmd}) {
  795. unshift @params, $cmd;
  796. $cmd = $hash->{helper}{mapLightCmds}{$cmd};
  797. }
  798. # special handling for attrs wwcwGPIOs & rgbGPIOs
  799. else {
  800. # enable ct|pct commands if attr wwcwGPIOs is set
  801. if (AttrVal($name,"wwcwGPIOs",0) && $cmd =~ m/^(ct|pct)$/i) {
  802. my $ret = ESPEasy_setCT($hash,$cmd,@params);
  803. return $ret if ($ret);
  804. }
  805. # enable rgb related commands if attr rgbGPIOs is set
  806. if (AttrVal($name,"rgbGPIOs",0) && $cmd =~ m/^(rgb|on|off|toggle)$/i) {
  807. my $ret = ESPEasy_setRGB($hash,$cmd,@params);
  808. return $ret if ($ret);
  809. }
  810. }
  811. # Log device set cmd with all mappings
  812. Log3 $name, 5, "$type $name: set $name $cmd ".join(" ",@params). " (mappings done)"
  813. if $cmd !~ m/^(\?|user|pass|help)$/;
  814. Log3 $name, 5, "$type $name: IOWrite ( \$defs{$name}, \$defs{$name}, $cmd, (\"".join("\",\"",@params)."\") )";
  815. Log3 $name, 2, "$type $name: Device seems to be in sleep mode, sending command nevertheless."
  816. if (defined $hash->{SLEEP} && $hash->{SLEEP} ne "0");
  817. # send cmd with required args to IO Device
  818. my $parseCmd = ESPEasy_isParseCmd($hash,$cmd); # should response be parsed and dispatched
  819. IOWrite($hash, $hash, $parseCmd, $cmd, @params);
  820. return undef;
  821. }
  822. ## ------------------------------------------------------------------------------
  823. #sub ESPEasy_Set_help(@) {
  824. # my ($hash, $name, $cmd, @params) = @_;
  825. # my $usage = $data{ESPEasy}{$name}{sets}{$params[0]}{usage};
  826. # return $usage ? "Usage: set $name $params[0] $usage"
  827. # : "Note: '$params[0]' is not registered as an ESPEasy command. "
  828. # . "See attribute userSetCmds to register your own or unsupported commands.";
  829. #}
  830. # ------------------------------------------------------------------------------
  831. sub ESPEasy_Read($) {
  832. my ($hash) = @_; #hash of temporary child instance
  833. my $name = $hash->{NAME};
  834. my $ipv = $hash->{IPV} ? $hash->{IPV} : ($hash->{PEER} =~ m/:/ ? 6 : 4);
  835. my $bhash = $modules{ESPEasy}{defptr}{BRIDGE}{$ipv}; #hash of original instance
  836. my $bname = $bhash->{NAME};
  837. my $btype = $bhash->{TYPE};
  838. # Accept and create a child
  839. if( $hash->{SERVERSOCKET} ) {
  840. # Levering new TcpServerUtils security feature, use our own TcpServer_Accept()
  841. my $aRet = ESPEasy_TcpServer_Accept($hash,"ESPEasy");
  842. return;
  843. }
  844. # use received IP instead of configured one (NAT/PAT could have modified)
  845. my $peer = $hash->{PEER};
  846. # Read max 9000 bytes, return num of read bytes
  847. my $buf;
  848. my $ret = sysread($hash->{CD}, $buf, 9000); # accept jumbo frames
  849. # Delete temporary device
  850. if( !defined($ret ) || $ret <= 0 ) {
  851. CommandDelete( undef, $hash->{NAME} );
  852. return;
  853. }
  854. # Check allowed IPs
  855. if ( !( ESPEasy_isPeerAllowed($peer,AttrVal($bname,"allowedIPs", $d_allowedIPs)) &&
  856. !ESPEasy_isPeerAllowed($peer,AttrVal($bname,"deniedIPs",0)) ) ) {
  857. Log3 $bname, 2, "$btype $name: Peer address rejected";
  858. return;
  859. }
  860. Log3 $bname, 4, "$btype $name: Peer address $peer accepted";
  861. # check content-length header (Forum #87607)
  862. $hash->{PARTIAL} .= $buf;
  863. my @data = split( '\R\R', $hash->{PARTIAL} );
  864. (my $ldata = $hash->{PARTIAL}) =~ s/Authorization: Basic [\w=]+/Authorization: Basic *****/;
  865. if(scalar @data < 2) { #header not complete
  866. Log3 $bname, 5, "$btype $name: Incomplete or no header, awaiting more data: \n$ldata";
  867. #start timer
  868. return;
  869. }
  870. my $header = ESPEasy_header2Hash($data[0]);
  871. if(!defined $header->{"Content-Length"}) {
  872. Log3 $bname, 2, "$btype $name: Missing content-length header: \n$ldata";
  873. ESPEasy_sendHttpClose($hash,"400 Bad Request","");
  874. #delete temp bridge device
  875. return;
  876. }
  877. my $len = length($data[1]);
  878. if($header->{"Content-Length"} > $len) {
  879. Log3 $bname, 5, "$btype $name: Received content too small, awaiting more content: $header->{'Content-Length'}:$len \n$ldata";
  880. #start timer
  881. return;
  882. }
  883. elsif($header->{"Content-Length"} < $len) {
  884. Log3 $bname, 2, "$btype $name: Received content too large, skip processing data: $header->{'Content-Length'}:$len \n$ldata";
  885. ESPEasy_sendHttpClose($hash,"400 Bad Request","");
  886. #delete temp bridge device
  887. return;
  888. }
  889. Log3 $name, 4, "$btype $name: Received content length ok";
  890. # mask password in authorization header with ****
  891. my $logHeader = { %$header };
  892. # public IPs
  893. if (!defined $logHeader->{Authorization} && $peer !~ m/$d_localIPs/) {
  894. Log3 $bname, 2, "$btype $name: No basic auth set while using a public IP "
  895. . "address. $peer rejected.";
  896. return;
  897. }
  898. $logHeader->{Authorization} =~ s/Basic\s.*\s/Basic ***** / if defined $logHeader->{Authorization};
  899. # Dump logHeader
  900. Log3 $bname, 5, "$btype $name: Received header: ".ESPEasy_dumpSingleLine($logHeader)
  901. if (defined $logHeader);
  902. # Dump content
  903. Log3 $bname, 5, "$btype $name: Received content: $data[1]" if defined $data[1];
  904. # check authorization
  905. if (!defined ESPEasy_isAuthenticated($hash,$header->{Authorization})) {
  906. ESPEasy_sendHttpClose($hash,"401 Unauthorized","");
  907. return;
  908. }
  909. # No error occurred, send http respose OK to ESP
  910. ESPEasy_sendHttpClose($hash,"200 OK",""); #if !grep(/"sleep":1/, $data[1]);
  911. # JSON received...
  912. my $json;
  913. if (defined $data[1] && $data[1] =~ m/"module":"ESPEasy"/) {
  914. # perl module JSON not installed
  915. if ( !$bhash->{helper}{pm}{JSON} ) {
  916. Log3 $bname, 2, "$btype $bname: Perl module 'JSON' is not installed. Can't process received data from $peer.";
  917. return;
  918. }
  919. # use encode_utf8 if available else replace any disturbing chars
  920. $bhash->{helper}{pm}{Encode}
  921. ? ( eval { $json = decode_json( encode_utf8($data[1]) ); 1; } )
  922. : ( eval { $json = decode_json( $data[1] =~ s/[^\x20-\x7E]/_/gr ); 1; } );
  923. if ($@) {
  924. Log3 $bname, 2, "$btype $name: WARNING: Invalid JSON received. "
  925. . "Check your ESP configuration ($peer).\n$@";
  926. return;
  927. }
  928. # check that ESPEasy software is new enough
  929. return if ESPEasy_checkVersion($bhash,$peer,$json->{data}{ESP}{build},$json->{version});
  930. # should never happen, but who knows what some JSON module versions do...
  931. $json->{data}{ESP}{name} = "" if !defined $json->{data}{ESP}{name};
  932. $json->{data}{SENSOR}{0}{deviceName} = "" if !defined $json->{data}{SENSOR}{0}{deviceName};
  933. # remove illegal chars from ESP name for further processing and assign to new var
  934. (my $espName = $json->{data}{ESP}{name}) =~ s/[^A-Za-z\d_\.]/_/g;
  935. (my $espDevName = $json->{data}{SENSOR}{0}{deviceName}) =~ s/[^A-Za-z\d_\.]/_/g;
  936. # check that 'ESP name' or 'device name' is set
  937. if ($espName eq "" && $espDevName eq "") {
  938. Log3 $bname, 2, "$btype $name: WARNIING 'ESP name' and 'device name' "
  939. ."missing ($peer). Check your ESP config. Skip processing data.";
  940. Log3 $bname, 2, "$btype $name: Data: $data[1]";
  941. return;
  942. }
  943. my $cd = ESPEasy_isCombineDevices($peer,$espName,AttrVal($bname,"combineDevices",0));
  944. my $ident = $cd
  945. ? $espName ne "" ? $espName : $peer
  946. : $espName.($espName ne "" && $espDevName ne "" ? "_" : "").$espDevName;
  947. my $d0;
  948. Log3 $bname, 4, "$btype $name: Src:'$json->{data}{ESP}{name}'/'"
  949. . (!defined $json->{data}{SENSOR}{0}{deviceName} || $json->{data}{SENSOR}{0}{deviceName} eq ""
  950. ? "<undefined>"
  951. : $json->{data}{SENSOR}{0}{deviceName} )
  952. ."' => ident:$ident dev:"
  953. . ( ($d0=(devspec2array("i:IDENT=$ident:FILTER=i:TYPE=$btype"))[0])
  954. ? $d0
  955. : "<undefined>" )
  956. . " combinedDevice:".$cd;
  957. # push internals in @values
  958. my @values;
  959. my @intVals = qw(unit sleep build build_git build_notes version node_type_id);
  960. foreach my $intVal (@intVals) {
  961. next if !defined $json->{data}{ESP}{$intVal} || $json->{data}{ESP}{$intVal} eq "";
  962. push(@values,"i||".$intVal."||".$json->{data}{ESP}{$intVal}."||0");
  963. }
  964. # push sensor value in @values
  965. foreach my $vKey (keys %{$json->{data}{SENSOR}}) {
  966. if(ref $json->{data}{SENSOR}{$vKey} eq ref {}
  967. && exists $json->{data}{SENSOR}{$vKey}{value}) {
  968. # remove illegal chars
  969. $json->{data}{SENSOR}{$vKey}{valueName} =~ s/[^A-Za-z\d_\.\-\/]/_/g;
  970. my $dmsg = "r||".$json->{data}{SENSOR}{$vKey}{valueName}
  971. ."||".$json->{data}{SENSOR}{$vKey}{value}
  972. ."||".$json->{data}{SENSOR}{$vKey}{type};
  973. if ($dmsg =~ m/(\|\|\|\|)|(\|\|$)/) { #detect an empty value
  974. Log3 $bname, 2, "$btype $name: WARNING: value name or value is "
  975. ."missing ($peer). Skip processing this value.";
  976. Log3 $bname, 2, "$btype $name: Data: $data[1]";
  977. next; #skip further processing for this value only
  978. }
  979. push(@values,$dmsg);
  980. }
  981. }
  982. ESPEasy_dispatch($hash,$ident,$peer,@values);
  983. } #$data[1] =~ m/"module":"ESPEasy"/
  984. else {
  985. Log3 $bname, 2, "$btype $name: WARNING: Wrong controller configured or "
  986. ."ESPEasy Version is too old.";
  987. Log3 $bname, 2, "$btype $name: WARNING: ESPEasy version R"
  988. .$minEEBuild." or later required.";
  989. }
  990. # session will not be close immediately if ESP goes to sleep after http send
  991. # needs further investigation?
  992. if ($hash->{TEMPORARY} && $json->{data}{ESP}{sleep}) {
  993. CommandDelete(undef, $name);
  994. }
  995. return;
  996. }
  997. # ------------------------------------------------------------------------------
  998. sub ESPEasy_Write($$$@) #called from logical's IOWrite (end of SetFn)
  999. {
  1000. my ($hash,$dhash,$parseCmd,$cmd,@params) = @_;
  1001. my ($name,$type) = ($hash->{NAME},$hash->{TYPE});
  1002. my ($dname,$dtype) = ($dhash->{NAME},$dhash->{TYPE});
  1003. if (IsDisabled($name)) {
  1004. Log3 $name, 4, "$type $name: cmd 'set $dname $cmd' ignored, bridge is disabled or inactive.";
  1005. # if ($cmd ne "statusrequest");
  1006. return undef;
  1007. }
  1008. if ($cmd eq "cleanup") {
  1009. delete $hash->{helper}{received};
  1010. return undef;
  1011. }
  1012. elsif ($cmd eq "statusrequest") {
  1013. ESPEasy_statusRequest($hash);
  1014. return undef;
  1015. }
  1016. my $retry = 0;
  1017. # a hash is more easy to handle in the following subs...
  1018. my $cmdHash = {
  1019. name => $dhash->{NAME},
  1020. ident => $dhash->{IDENT},
  1021. port => $dhash->{PORT},
  1022. host => $dhash->{HOST},
  1023. parseCmd => $parseCmd,
  1024. retry => 0,
  1025. cmd => $cmd,
  1026. cmdArgs => [ @params ],
  1027. ts => ESPEasy_timeStamp(),
  1028. authRetry => 0,
  1029. admpwd => $dhash->{sec}{admpwd},
  1030. };
  1031. ESPEasy_httpReq($hash, $cmdHash);
  1032. }
  1033. # ------------------------------------------------------------------------------
  1034. # Global events only ( $hash->{NOTIFYDEV}=global )
  1035. # ------------------------------------------------------------------------------
  1036. sub ESPEasy_Notify($$)
  1037. {
  1038. my ($hash,$dev) = @_;
  1039. my $name = $hash->{NAME};
  1040. my $type = $hash->{TYPE};
  1041. return if(!grep(m/^(DELETE)?ATTR $name |^INITIALIZED$|^REREADCFG$|^DEFINED/, @{$dev->{CHANGED}}));
  1042. foreach (@{$dev->{CHANGED}}) {
  1043. if (m/^(DELETE)?ATTR ($name) (\w+)\s?(.*)?$/s) { # modifier 's' is important multiline attrs like userSetCmds, ...
  1044. Log3 $name, 5, "$type $name: received event: $_";
  1045. if ($3 eq "disable") { # attr disable = 0 or deleted
  1046. # --- enable ---
  1047. if (defined $1 || (defined $4 && $4 eq "0")) {
  1048. # device (trigger only if state is disabled)
  1049. if ($hash->{SUBTYPE} eq "device" && ReadingsVal($name, "state", "") eq "disabled") {
  1050. ESPEasy_resetTimer($hash) ;
  1051. readingsSingleUpdate($hash, 'state', 'Initialized',1);
  1052. Log3 $name, 3, "$type $name: Device enabled";
  1053. }
  1054. # bridge (trigger only if file descriptor is defined)
  1055. elsif ($hash->{SUBTYPE} eq "bridge" && !defined $hash->{FD}) {
  1056. ESPEasy_TcpServer_Close($hash,1); #close silently
  1057. ESPEasy_TcpServer_Open($hash);
  1058. }
  1059. }
  1060. # --- disable ---
  1061. else {
  1062. # device (trigger only if state != disabled)
  1063. if ($hash->{SUBTYPE} eq "device" && ReadingsVal($name, "state", "") ne "disabled") {
  1064. ESPEasy_clearReadings($hash);
  1065. ESPEasy_resetTimer($hash,"stop");
  1066. readingsSingleUpdate($hash, "state", "disabled",1);
  1067. Log3 $name, 3, "$type $name: Device disabled";
  1068. }
  1069. # bridge (trigger only if state != disabled)
  1070. elsif ($hash->{SUBTYPE} eq "bridge" && ReadingsVal($name, "state", "") ne "disabled") {
  1071. ESPEasy_TcpServer_Close($hash);
  1072. readingsSingleUpdate($hash, "state", "disabled",1);
  1073. Log3 $name, 3, "$type $name: Device disabled";
  1074. }
  1075. }
  1076. }
  1077. elsif ($3 eq "Interval") {
  1078. if (defined $1) {
  1079. $hash->{INTERVAL} = $d_Interval;
  1080. }
  1081. elsif (defined $4 && $4 eq "0") {
  1082. $hash->{INTERVAL} = "disabled";
  1083. ESPEasy_resetTimer($hash,"stop");
  1084. CommandDeleteReading(undef, "$name presence")
  1085. if defined $hash->{READINGS}{presence};
  1086. }
  1087. else { # Interval > 0
  1088. $hash->{INTERVAL} = $4;
  1089. ESPEasy_resetTimer($hash);
  1090. }
  1091. }
  1092. elsif ($3 eq "setState") {
  1093. if (defined $1 || (defined $4 && $4 > 0)) {
  1094. ESPEasy_setState($hash);
  1095. }
  1096. else { #setState == 0
  1097. CommandSetReading(undef,"$name state Initialized");
  1098. }
  1099. }
  1100. elsif ($3 =~ /^(mapLightCmds)$/) {
  1101. ESPEasy_initDevSets($hash);
  1102. ESPEasy_initDevAttrs($hash);
  1103. }
  1104. elsif ($3 =~ /^(rgbGPIOs|wwcwGPIOs)$/) {
  1105. ESPEasy_initDevAttrs($hash);
  1106. }
  1107. elsif ($3 =~ /^(mapLightCmds|colorpicker(CT[cw]w)?|ct[CW]W_reducedRange|disableRiskyCmds|userSetCmds|userSetMaps|userSets)$/) {
  1108. ESPEasy_initDevSets($hash);
  1109. }
  1110. else {
  1111. #Log 5, "$type $name: Attribute $3 not handeled by NotifyFn ";
  1112. }
  1113. } # if (m/^(DELETE)?ATTR ($name) (\w+)\s?(.*)?$/s)
  1114. elsif (m/^(INITIALIZED|REREADCFG|DEFINED $name)$/) {
  1115. ESPEasy_initDevSets($hash);
  1116. ESPEasy_initDevAttrs($hash);
  1117. ESPEasy_TcpServer_Open($hash) if $hash->{SUBTYPE} eq "bridge";
  1118. }
  1119. else { #should never be reached
  1120. #Log 5, "$type $name: WARNING: unexpected event received by NotifyFn: $_";
  1121. }
  1122. }
  1123. return undef;
  1124. }
  1125. # ------------------------------------------------------------------------------
  1126. sub ESPEasy_Rename() {
  1127. my ($new,$old) = @_;
  1128. my $i = 0;
  1129. my $type = $defs{"$new"}->{TYPE};
  1130. my $name = $defs{"$new"}->{NAME};
  1131. my $subtype = $defs{"$new"}->{SUBTYPE};
  1132. my @am;
  1133. # copy values from old to new device
  1134. setKeyValue($type."_".$new."-user",getKeyValue($type."_".$old."-user"));
  1135. setKeyValue($type."_".$new."-pass",getKeyValue($type."_".$old."-pass"));
  1136. setKeyValue($type."_".$new."-admpwd",getKeyValue($type."_".$old."-admpwd"));
  1137. # delete old entries
  1138. setKeyValue($type."_".$old."-user",undef);
  1139. setKeyValue($type."_".$old."-pass",undef);
  1140. setKeyValue($type."_".$old."-firstrun",undef);
  1141. setKeyValue($type."_".$old."-admpwd",undef);
  1142. # sets/maps
  1143. $data{$type}{$new} = $data{$type}{$old};
  1144. delete $data{$type}{$old};
  1145. # replace IDENT in devices if bridge name changed
  1146. if ($subtype eq "bridge") {
  1147. foreach my $ldev (devspec2array("TYPE=$type")) {
  1148. my $dhash = $defs{$ldev};
  1149. my $dsubtype = $dhash->{SUBTYPE};
  1150. next if ($dsubtype eq "bridge");
  1151. my $dname = $dhash->{NAME};
  1152. my $ddef = $dhash->{DEF};
  1153. my $oddef = $dhash->{DEF};
  1154. $ddef =~ s/ $old / $new /;
  1155. if ($oddef ne $ddef){
  1156. $i = $i+2;
  1157. CommandModify(undef, "$dname $ddef");
  1158. CommandAttr(undef,"$dname IODev $new");
  1159. push (@am,$dname);
  1160. }
  1161. }
  1162. }
  1163. Log3 $name, 2, "$type $name: Device $old renamed to $new";
  1164. Log3 $name, 2, "$type $name: Attribute IODev set to '$name' in these "
  1165. ."devices: ".join(", ",@am) if $subtype eq "bridge";
  1166. if (AttrVal($name,"autosave",AttrVal("global","autosave",1)) && $i>0) {
  1167. CommandSave(undef,undef);
  1168. Log3 $type, 2, "$type $name: $i structural changes saved "
  1169. ."(autosave is enabled)";
  1170. }
  1171. elsif ($i>0) {
  1172. Log3 $type, 2, "$type $name: There are $i structural changes. "
  1173. ."Don't forget to save chages.";
  1174. }
  1175. return undef;
  1176. }
  1177. # ------------------------------------------------------------------------------
  1178. sub ESPEasy_Attr(@)
  1179. {
  1180. my ($cmd,$name,$aName,$aVal) = @_;
  1181. my $hash = $defs{$name};
  1182. my ($type, $subtype) = ($hash->{TYPE}, $hash->{SUBTYPE});
  1183. my $revSubType = $subtype eq "bridge" ? "device" : "bridge";
  1184. my $ret;
  1185. if ($cmd eq "set" && !defined $aVal) {
  1186. Log3 $name, 2, "$type $name: attr $name $aName '': value must not be empty";
  1187. return "$name: attr $aName: value must not be empty";
  1188. }
  1189. elsif ($aName eq "readingSwitchText") {
  1190. $ret = "0,1,2" if ($cmd eq "set" && not $aVal =~ m/^(0|1|2)$/)
  1191. }
  1192. elsif ($aName eq "combineDevices") {
  1193. $ret = "0 | 1 | ESPname | ip[/netmask][,ip[/netmask]][,...]"
  1194. if $cmd eq "set" && !(ESPEasy_isAttrCombineDevices($aVal) || $aVal =~ m/^[01]$/ )
  1195. }
  1196. elsif ($aName =~ m/^(allowedIPs|deniedIPs)$/) {
  1197. $ret = "[comma separated list of] ip[/netmask] or a regexp"
  1198. if $cmd eq "set" && !ESPEasy_isIPv64Range($aVal,"regexp")
  1199. }
  1200. elsif ($aName =~ m/^(pollGPIOs|rgbGPIOs|wwcwGPIOs)$/) {
  1201. $ret = "GPIO_No[,GPIO_No][...]"
  1202. if $cmd eq "set" && $aVal !~ m/^[a-zA-Z]{0,2}[0-9]+(,[a-zA-Z]{0,2}[0-9]+)*$/
  1203. }
  1204. elsif ($aName eq "colorpicker") {
  1205. $ret = "RGB | HSV | HSVp"
  1206. if ($cmd eq "set" && not $aVal =~ m/^(RGB|HSV|HSVp)$/)
  1207. }
  1208. elsif ($aName =~ m/^(colorpickerCTww|colorpickerCTcw)$/) {
  1209. $ret = "1000..10000"
  1210. if $cmd eq "set" && ($aVal < 1000 || $aVal > 10000)
  1211. }
  1212. elsif ($aName eq "parseCmdResponse") {
  1213. my $cmds = lc join("|",keys %{ $data{ESPEasy}{$name}{sets} });
  1214. $ret = "cmd[,cmd][...] #cmd must be a registered ESPEasy cmd"
  1215. if $init_done && $cmd eq "set" && lc($aVal) !~ m/^($cmds){1}(,($cmds))*$/
  1216. }
  1217. elsif ($aName eq "mapLightCmds") {
  1218. my $cmds = lc join("|",keys %{ $data{ESPEasy}{$name}{sets} });
  1219. $ret = "ESPEasy cmd"
  1220. if $init_done && $cmd eq "set" && lc($aVal) !~ m/^($cmds){1}(,($cmds))*$/}
  1221. elsif ($aName =~ m/^(setState|resendFailedCmd)$/) {
  1222. $ret = "integer"
  1223. if ($cmd eq "set" && not $aVal =~ m/^(\d+)$/)}
  1224. elsif ($aName eq "displayTextWidth") {
  1225. $ret = "number of charaters per line"
  1226. if ($cmd eq "set" && not $aVal =~ m/^(\d+)$/)}
  1227. elsif ($aName eq "readingPrefixGPIO") {
  1228. $ret = "[a-zA-Z0-9._-/]+"
  1229. if ($cmd eq "set" && $aVal !~ m/^[A-Za-z\d_\.\-\/]+$/)}
  1230. elsif ($aName eq "readingSuffixGPIOState") {
  1231. $ret = "[a-zA-Z0-9._-/]+"
  1232. if ($cmd eq "set" && $aVal !~ m/^[A-Za-z\d_\.\-\/]+$/)}
  1233. elsif ($aName eq "httpReqTimeout") {
  1234. $ret = "3..60 (default: $d_httpReqTimeout)"
  1235. if $cmd eq "set" && ($aVal < 3 || $aVal > 60)}
  1236. elsif ($aName eq "maxHttpSessions") {
  1237. ($cmd eq "set" && ($aVal !~ m/^[0-9]+$/))
  1238. ? ($ret = ">= 0 (default: $d_maxHttpSessions, 0: disable queuing)")
  1239. : ($hash->{MAX_HTTP_SESSIONS} = $aVal);
  1240. if ($cmd eq "del") {$hash->{MAX_HTTP_SESSIONS} = $d_maxHttpSessions}
  1241. }
  1242. elsif ($aName eq "maxQueueSize") {
  1243. ($cmd eq "set" && ($aVal !~ m/^[1-9][0-9]+$/))
  1244. ? ($ret = ">=10 (default: $d_maxQueueSize)")
  1245. : ($hash->{MAX_QUEUE_SIZE} = $aVal);
  1246. if ($cmd eq "del") {$hash->{MAX_QUEUE_SIZE} = $d_maxQueueSize}
  1247. }
  1248. elsif ($aName eq "Interval") {
  1249. ($cmd eq "set" && ($aVal !~ m/^(\d)+$/ || $aVal <10 && $aVal !=0))
  1250. ? ($ret = "0 or >=10")
  1251. : ($hash->{INTERVAL} = $aVal)
  1252. }
  1253. elsif ($aName eq "userSetCmds") {
  1254. $ret = ESPEasy_Attr_userSetCmds($hash, $cmd, $aName, $aVal);
  1255. $ret = "a perl hash. See command reference for details.\n\n"
  1256. . "Error: ".chomp($ret)
  1257. . "\n\nExample:\n"
  1258. . "(\n"
  1259. ." plugin_X => { cmd_1 => {}, cmd_2 => {} },\n"
  1260. ." plugin_Y => {\n"
  1261. ." rgb => { args => 1, url => \"/myUrl\", widget => \"colorpicker,RGB\", usage => \"<rrggbb> [fadetime]\" },\n"
  1262. ." ct => { args => 1, url => \"/myUrl\", widget => \"colorpicker,CT,2000,100,4500\", usage => \"<colortemp>\" }\n"
  1263. ." }\n"
  1264. .")\n"
  1265. if $ret;
  1266. }
  1267. if (!$init_done) {
  1268. if ($aName =~ /^disable$/ && $aVal == 1) {
  1269. readingsSingleUpdate($hash, "state", "disabled",1);
  1270. }
  1271. }
  1272. if (defined $ret) {
  1273. return "$name: Attribut '$aName' must be: $ret";
  1274. }
  1275. return undef;
  1276. }
  1277. # ------------------------------------------------------------------------------
  1278. # check attr userSetCmds | userSetMaps
  1279. # ------------------------------------------------------------------------------
  1280. sub ESPEasy_Attr_userSetCmds(@) {
  1281. my ($hash, $cmd, $aName, $aVal) = @_;
  1282. my %user;
  1283. my $ret;
  1284. if ($cmd eq "set") {
  1285. my %ua = eval($aVal);
  1286. return $@ if $@;
  1287. foreach my $plugin (keys %ua) {
  1288. foreach my $key ( keys %{ $ua{$plugin} } ) {
  1289. return "Unknown key '$key' in $plugin => { $key => ... }" if ($key !~ m/^(args|url|widget|usage|cmds)$/);
  1290. next if $key =~ m/^(args|url|widget|usage)$/ && !ref($ua{$plugin}{$key});
  1291. if ($key eq "cmds") {
  1292. if (ref($ua{$plugin}{$key}) eq "HASH") {
  1293. foreach my $subcmd (keys %{ $ua{$plugin}{$key} }) {
  1294. foreach my $subkey (keys %{ $ua{$plugin}{$key}{$subcmd} }) {
  1295. my $where = "$plugin => { $key => { $subcmd => { $subkey => ... } } }";
  1296. return "Unknown key '$subkey' in $where. Mistyped?" if ($subkey !~ m/^(args|url|widget|usage)$/);
  1297. return "Value of '$subkey' in $where must be a string." if ref($ua{$plugin}{$key}{$subcmd}{$subkey});
  1298. }
  1299. }
  1300. }
  1301. else {
  1302. return "Value of key '$key' in $plugin => { $key => ... } must be a hash.";
  1303. }
  1304. } # key eq "cmds"
  1305. } # foreach key
  1306. } # foreach plugin
  1307. } # set attr
  1308. # Delete Attribute, afterwards notifyFn will build new cmdhash in $data{ESPEasy}{$name}{sets}...
  1309. else {
  1310. # do nothing
  1311. }
  1312. # eval() above accepts single string expressions...
  1313. my $reHash = '\s*\w+\s*=>\s*\{.*}\s*,*\s*';
  1314. return "Wrong Syntax: '$aVal'" if $aVal !~ m/^\s*\($reHash(,$reHash)*\)\s*$/s;
  1315. return undef;
  1316. }
  1317. # ------------------------------------------------------------------------------
  1318. #UndefFn: called while deleting device (delete-command) or while rereadcfg
  1319. sub ESPEasy_Undef($$)
  1320. {
  1321. my ($hash, $arg) = @_;
  1322. my ($name,$type,$port) = ($hash->{NAME},$hash->{TYPE},$hash->{PORT});
  1323. # close server and return if it is a child process for incoming http requests
  1324. if (defined $hash->{TEMPORARY} && $hash->{TEMPORARY} == 1) {
  1325. my $ipv = $hash->{PEER} =~ m/:/ ? 6 : 4;
  1326. my $bhash = $modules{ESPEasy}{defptr}{BRIDGE}{$ipv};
  1327. Log3 $bhash->{NAME}, 4, "$type $name: Closing tcp session.";
  1328. ESPEasy_TcpServer_Close($hash);
  1329. return undef
  1330. };
  1331. HttpUtils_Close($hash);
  1332. RemoveInternalTimer($hash);
  1333. if($hash->{SUBTYPE} && $hash->{SUBTYPE} eq "bridge") {
  1334. my $ipv = $hash->{IPV};
  1335. delete $modules{ESPEasy}{defptr}{BRIDGE}{$ipv}
  1336. if(defined($modules{ESPEasy}{defptr}{BRIDGE}{$ipv}));
  1337. ESPEasy_TcpServer_Close($hash);
  1338. Log3 $name, 2, "$type $name: Socket on port tcp/$port closed";
  1339. }
  1340. else {
  1341. IOWrite($hash, $hash, undef, "cleanup", undef );
  1342. }
  1343. return undef;
  1344. }
  1345. # ------------------------------------------------------------------------------
  1346. #ShutdownFn: called before fhem's shutdown command
  1347. sub ESPEasy_Shutdown($)
  1348. {
  1349. my ($hash) = @_;
  1350. HttpUtils_Close($hash);
  1351. Log3 $hash->{NAME}, 4, "$hash->{TYPE} $hash->{NAME}: Shutdown requested";
  1352. return undef;
  1353. }
  1354. # ------------------------------------------------------------------------------
  1355. #DeleteFn: called while deleting device (delete-command) but after UndefFn
  1356. sub ESPEasy_Delete($$)
  1357. {
  1358. my ($hash, $arg) = @_;
  1359. my ($name, $type) = ($hash->{NAME}, $hash->{TYPE});
  1360. # return if it is a child process for incoming http requests
  1361. if (!defined $hash->{TEMPORARY}) {
  1362. setKeyValue($type."_".$name."-user",undef);
  1363. setKeyValue($type."_".$name."-pass",undef);
  1364. setKeyValue($type."_".$name."-firstrun",undef);
  1365. setKeyValue($type."_".$name."-admpwd",undef);
  1366. delete $data{$type}{$name};
  1367. Log3 $hash->{NAME}, 4, "$type $name: $hash->{NAME} deleted";
  1368. }
  1369. return undef;
  1370. }
  1371. # ------------------------------------------------------------------------------
  1372. sub ESPEasy_dispatch($$$@) #called by bridge -> send to logical devices
  1373. {
  1374. my($hash,$ident,$host,@values) = @_;
  1375. my $name = $hash->{NAME};
  1376. return if (IsDisabled $name);
  1377. my $type = $hash->{TYPE};
  1378. my $ipv = $host =~ m/:/ ? 6 : 4;
  1379. my $bhash = $modules{ESPEasy}{defptr}{BRIDGE}{$ipv};
  1380. my $bname = $bhash->{NAME};
  1381. my $ui = 1; #can be removed later
  1382. my $as = (AttrVal($bname,"autosave",AttrVal("global","autosave",1))) ? 1 : 0;
  1383. my $ac = (AttrVal($bname,"autocreate",AttrVal("global","autoload_undefined_devices",1))) ? 1 : 0;
  1384. my $msg = $ident."::".$host."::".$ac."::".$as."::".$ui."::".join("|||",@values);
  1385. # Log3 $bname, 5, "$type $name: Dispatch: $msg";
  1386. Dispatch($bhash, $msg, undef);
  1387. return undef;
  1388. }
  1389. # ------------------------------------------------------------------------------
  1390. sub ESPEasy_State($$$$)
  1391. {
  1392. my ($hash, $time, $reading, $val) = @_;
  1393. if($reading eq "state" && $val eq "inactive") {
  1394. readingsSingleUpdate($hash, "state", "inactive", 1);
  1395. }
  1396. return undef;
  1397. }
  1398. # ------------------------------------------------------------------------------
  1399. sub ESPEasy_dispatchParse($$$) # called by logical device (defined by
  1400. { # $hash->{ParseFn})
  1401. # we are called from dispatch() from the ESPEasy bridge device
  1402. # we never come here if $msg does not match $hash->{MATCH} in the first place
  1403. my ($IOhash, $msg) = @_; # IOhash points to the ESPEasy bridge, not device
  1404. my $IOname = $IOhash->{NAME};
  1405. my $type = $IOhash->{TYPE};
  1406. # 1:ident 2:ip 3:autocreate 4:autosave 5:uniqIDs 6:value(s)
  1407. my ($ident,$ip,$ac,$as,$ui,$v) = split("::",$msg);
  1408. return "" if !$ident || $ident eq "";
  1409. my $name;
  1410. my @v = split("\\|\\|\\|",$v);
  1411. # look in each $defs{$d}{IDENT} for $ident to get device name.
  1412. foreach my $d (keys %defs) {
  1413. next if($defs{$d}{TYPE} ne "ESPEasy");
  1414. if (InternalVal($defs{$d}{NAME},"IDENT","") eq "$ident") {
  1415. $name = $defs{$d}{NAME} ;
  1416. last;
  1417. }
  1418. }
  1419. # autocreate device if no device has $ident asigned.
  1420. if (!($name) && $ac eq "1") {
  1421. $name = ESPEasy_autocreate($IOhash,$ident,$ip,$as);
  1422. # cleanup helper
  1423. delete $IOhash->{helper}{autocreate}{$ident}
  1424. if defined $IOhash->{helper}{autocreate}{$ident};
  1425. delete $IOhash->{helper}{autocreate}
  1426. if scalar keys %{$IOhash->{helper}{autocreate}} == 0;
  1427. }
  1428. # autocreate is disabled
  1429. elsif (!($name) && $ac eq "0") {
  1430. Log3 $IOname, 2, "$type $IOname: autocreate is disabled (ident: $ident)"
  1431. if not defined $IOhash->{helper}{autocreate}{$ident};
  1432. $IOhash->{helper}{autocreate}{$ident} = "disabled";
  1433. return $ident;
  1434. }
  1435. return $name if (IsDisabled $name);
  1436. my $hash = $defs{$name};
  1437. Log3 $name, 5, "$type $name: Received: $msg";
  1438. if (defined $hash && $hash->{TYPE} eq "ESPEasy" && $hash->{SUBTYPE} eq "device") {
  1439. my @logInternals;
  1440. foreach (@v) {
  1441. my ($cmd,$reading,$value,$vType) = split("\\|\\|",$_);
  1442. # reading prefix replacement (useful if we poll values)
  1443. my $replace = '"'.AttrVal($name,"readingPrefixGPIO","GPIO").'"';
  1444. $reading =~ s/^GPIO/$replace/ee;
  1445. # --- setReading ----------------------------------------------
  1446. if ($cmd eq "r") {
  1447. # reading suffix replacement only for setreading
  1448. $replace = '"'.AttrVal($name,"readingSuffixGPIOState","").'"';
  1449. $reading =~ s/_state$/$replace/ee;
  1450. # map value to on/off if device is a switch
  1451. my $rst = AttrVal($name,"readingSwitchText",1);
  1452. $value = $ee_map{rst}{$vType}{$rst}{$value}
  1453. if defined $ee_map{rst}{$vType} && defined $ee_map{rst}{$vType}{$rst}
  1454. && defined $ee_map{rst}{$vType}{$rst}{$value}
  1455. && !AttrVal($name,"rgbGPIOs",0); # special treatment if attr rgbGPIOs is set
  1456. # delete ignored reading and helper
  1457. if (defined ReadingsVal($name,".ignored_$reading",undef)) {
  1458. delete $hash->{READINGS}{".ignored_$reading"};
  1459. delete $hash->{helper}{received}{".ignored_$reading"};
  1460. }
  1461. # delete warning if there is any (send from httpRequestParse before)
  1462. if (exists ($hash->{"WARNING"})) {
  1463. if (defined $hash->{"WARNING"}) {
  1464. Log3 $name, 2, "$type $name: RESOLVED: ".$hash->{"WARNING"};
  1465. }
  1466. delete $hash->{"WARNING"};
  1467. }
  1468. # attr adjustValue
  1469. my $orgVal = $value;
  1470. $value = ESPEasy_adjustValue($hash,$reading,$value);
  1471. if (!defined $value) {
  1472. Log3 $name, 4, "$type $name: $reading: $orgVal [ignored]";
  1473. $reading = ".ignored_$reading";
  1474. $value = $orgVal;
  1475. }
  1476. readingsSingleUpdate($hash, $reading, $value, 1);
  1477. my $adj = ($orgVal ne $value) ? " [adjusted]" : "";
  1478. Log3 $name, 4, "$type $name: $reading: $value".$adj
  1479. if defined $value && $reading !~ m/^\./; #no leading dot
  1480. # used for presence detection
  1481. $hash->{helper}{received}{$reading} = time();
  1482. # recalc RGB reading if a PWM channel has changed
  1483. if (AttrVal($name,"rgbGPIOs",0) && $reading =~ m/\d$/i) {
  1484. my ($r,$g,$b) = ESPEasy_gpio2RGB($hash);
  1485. if (($r ne "" && uc ReadingsVal($name,"rgb","") ne uc $r.$g.$b) ) {
  1486. readingsSingleUpdate($hash, "rgb", $r.$g.$b, 1);
  1487. }
  1488. }
  1489. }
  1490. # --- Internals -----------------------------------------------
  1491. elsif ($cmd eq "i") {
  1492. # add human readable text to node_type_id
  1493. $value .= defined $ee_map{build}{$value}{type}
  1494. ? ": " . $ee_map{build}{$value}{type}
  1495. : ": unknown node type id"
  1496. if $reading eq "node_type_id";
  1497. # no value given
  1498. $value = "<undefined>" if !defined $value || $value eq "";
  1499. # set internal
  1500. $hash->{"ESP_".uc($reading)} = $value;
  1501. # add to log
  1502. push(@logInternals,"$reading:$value");
  1503. }
  1504. # --- Error ---------------------------------------------------
  1505. elsif ($cmd eq "e") {
  1506. if (!defined $hash->{"WARNING"} || $hash->{"WARNING"} ne $value) {
  1507. Log3 $name, 2, "$type $name: WARNING: $value";
  1508. $hash->{"WARNING"} = $value;
  1509. # CommandTrigger(undef, "$name ....");
  1510. }
  1511. #readingsSingleUpdate($hash, $reading, $value, 1);
  1512. }
  1513. # --- Notice (just log) ---------------------------------------
  1514. elsif ($cmd eq "n") {
  1515. Log3 $name, $vType, "$type $name: $reading: $value";
  1516. }
  1517. # --- DeleteReading -------------------------------------------
  1518. elsif ($cmd eq "dr") {
  1519. CommandDeleteReading(undef, "$name $reading");
  1520. Log3 $name, 4, "$type $name: Reading $reading deleted";
  1521. }
  1522. else {
  1523. Log3 $name, 2, "$type $name: Unknown internal command code received via dispatch. Report to maintainer, please.";
  1524. }
  1525. } # foreach @v
  1526. Log3 $name, 5, "$type $name: Internals: ".join(" ",@logInternals)
  1527. if scalar @logInternals > 0;
  1528. ESPEasy_checkPresence($hash) if ReadingsVal($name,"presence","") ne "present";
  1529. ESPEasy_setState($hash);
  1530. }
  1531. else { #autocreate failed
  1532. Log3 undef, 2, "ESPEasy: Device $name not defined";
  1533. }
  1534. return $name; # must be != undef. else msg will processed further -> help me!
  1535. }
  1536. # ------------------------------------------------------------------------------
  1537. sub ESPEasy_autocreate($$$$)
  1538. {
  1539. my ($IOhash,$ident,$ip,$autosave) = @_;
  1540. my $IOname = $IOhash->{NAME};
  1541. my $IOtype = $IOhash->{TYPE};
  1542. my $devname = "ESPEasy_".$ident;
  1543. my $define = "$devname ESPEasy $ip 80 $IOhash->{NAME} $ident";
  1544. Log3 undef, 2, "$IOtype $IOname: Autocreate $define";
  1545. my $cmdret= CommandDefine(undef,$define);
  1546. if(!$cmdret) {
  1547. $cmdret= CommandAttr(undef, "$devname room $IOhash->{TYPE}");
  1548. $cmdret= CommandAttr(undef, "$devname group $IOhash->{TYPE} Device");
  1549. $cmdret= CommandAttr(undef, "$devname setState 3");
  1550. $cmdret= CommandAttr(undef, "$devname Interval $d_Interval");
  1551. $cmdret= CommandAttr(undef, "$devname presenceCheck 1");
  1552. $cmdret= CommandAttr(undef, "$devname readingSwitchText 1");
  1553. if (AttrVal($IOname,"autosave",AttrVal("global","autosave",1))) {
  1554. CommandSave(undef,undef);
  1555. Log3 undef, 2, "$IOtype $IOname: Structural changes saved.";
  1556. }
  1557. else {
  1558. Log3 undef, 2, "$IOtype $IOname: Autosave is disabled: "
  1559. ."Do not forget to save changes.";
  1560. }
  1561. }
  1562. else {
  1563. Log3 undef, 1, "$IOtype $IOname: WARNING: an error occurred "
  1564. ."while creating device for $ident: $cmdret";
  1565. }
  1566. return $devname;
  1567. }
  1568. # ------------------------------------------------------------------------------
  1569. sub ESPEasy_httpReq(@)
  1570. {
  1571. my ($hash, $cmdHash) = @_;
  1572. my ($name, $type) = ($hash->{NAME},$hash->{TYPE});
  1573. my ($host, $port, $ident, $dname) = ($cmdHash->{host}, $cmdHash->{port}, $cmdHash->{ident}, $cmdHash->{name});
  1574. my ($cmd, @cmdArgs) = ($cmdHash->{cmd}, @{$cmdHash->{cmdArgs}}) ;
  1575. my $url;
  1576. # queue http requests or continue if there are no queued cmds
  1577. return undef if ESPEasy_httpReqQueue($hash, $cmdHash);
  1578. $cmdHash->{retry}++;
  1579. $hash->{helper}{sessions}{$host}++; # increment http session counter
  1580. my $path = $data{ESPEasy}{$dname}{sets}{$cmd}{url}; # build http url
  1581. # raw/rawsystem is used for commands not implemented right now
  1582. if ($cmd =~ m/^raw|rawsystem$/) {
  1583. $cmd = $cmdArgs[0];
  1584. splice(@cmdArgs,0,1);
  1585. }
  1586. if (defined $cmdHash->{dologin} && $cmdHash->{dologin} == 1) {
  1587. $url = "http://$host:$port/login?password=$cmdHash->{admpwd}";
  1588. }
  1589. else {
  1590. my $plist = join(",",@cmdArgs); # join cmd params into a string to be used in http url
  1591. $plist = ",".$plist if @cmdArgs; # add leading comma if defined
  1592. $url = "http://".$host.":".$port.$path.$cmd.$plist; # build full url
  1593. }
  1594. my $httpParams = {
  1595. url => $url,
  1596. timeout => AttrVal($name,"httpReqTimeout",$d_httpReqTimeout),
  1597. keepalive => 0,
  1598. httpversion => "1.0",
  1599. hideurl => ($url =~ m/password/ ? 1 : 0),
  1600. method => "GET",
  1601. ignoreredirects => 1,
  1602. callback => \&ESPEasy_httpReqParse,
  1603. hash => $hash, # pass throght to ESPEasy_httpReqParse()
  1604. cmdHash => $cmdHash # pass throght to ESPEasy_httpReqParse()
  1605. };
  1606. (my $logUrl = $url) =~ s/password=.*/password=*****/;
  1607. Log3 $name, 4, "$type $name: httpReq device:$dname ident:$ident timeout:$httpParams->{timeout} url:$logUrl" if ($cmd !~ m/^(status)/);
  1608. HttpUtils_NonblockingGet($httpParams);
  1609. return undef;
  1610. }
  1611. # ------------------------------------------------------------------------------
  1612. sub ESPEasy_httpReqParse($$$)
  1613. {
  1614. my ($param, $err, $data) = @_;
  1615. my $hash = $param->{hash};
  1616. my ($name,$type) = ($hash->{NAME},$hash->{TYPE});
  1617. my $cmdHash = $param->{cmdHash};
  1618. my ($host, $ident, $dname) = ($cmdHash->{host}, $cmdHash->{ident}, $cmdHash->{name});
  1619. my ($retry, $parseCmd, $cmd) = ($cmdHash->{retry}, $cmdHash->{parseCmd}, $cmdHash->{cmd});
  1620. my ($port, $pass) = ($cmdHash->{port}, $cmdHash->{admpwd});
  1621. my @cmdArgs = @{ $cmdHash->{cmdArgs} }; # used for queueing
  1622. my $plist = join(",",@cmdArgs); # used in Log entries
  1623. my @values;
  1624. # command queue
  1625. $hash->{helper}{sessions}{$host}--;
  1626. if ($err ne "") {
  1627. push(@values, "e||_lastError||$err||0"); # dispatch $err to logical device
  1628. $hash->{"WARNING_$host"} = $err; # keep in helper for support reason
  1629. #Log3 $name, 2, "$type $name: httpReq failed: $host $ident '$cmd $plist' ";
  1630. #Log3 $name, 2, "$type $name: set $dname $cmd". ($plist ne "" ?" $plist": "")." failed: $err" ;
  1631. Log3 $name, 2, "$type $name: $err [set $dname $cmd". ($plist ne ""?" $plist":"") ."]";
  1632. # unshift command back to queue (resend) if retry not reached
  1633. my $maxRetry = AttrVal($name,"resendFailedCmd",$d_resendFailedCmd);
  1634. if ($retry <= $maxRetry && $hash->{MAX_HTTP_SESSIONS} ) {
  1635. unshift @{$hash->{helper}{queue}{$host}}, $cmdHash;
  1636. Log3 $name, 4, "$type $name: Requeuing: $host $ident '$cmd $plist' (".scalar @{$hash->{helper}{queue}{$host}}.")";
  1637. }
  1638. }
  1639. # ESPEasy's firmware command is unknown
  1640. elsif ($data =~ m/^(Unknown or restricted command)!/) {
  1641. my $n = $1. ": '" .($cmd !~m/^raw(system)?/ ? $cmd : $cmdArgs[0]). "'";
  1642. push(@values, "n||Warning||$n||3");
  1643. }
  1644. # Authorization not send or failed.
  1645. elsif ($data =~ m/^(HTTP\/1.1 302)\s?\r\nLocation: \/login/s) {
  1646. if (!defined $pass || $pass eq "") {
  1647. my $n = "Command \'$cmd\' requires authentication but no adminpassword ist set.";
  1648. push(@values, "n||Warning||$n||2");
  1649. }
  1650. else {
  1651. # queue command, send credentials
  1652. if ($cmdHash->{authRetry} == 0) {
  1653. my $n = "Wrong URL or authorization required for \'$cmd $plist\'. Queueing command, sending credentials first.";
  1654. push(@values, "n||Notice||$n||4");
  1655. $cmdHash->{authRetry} = 1;
  1656. unshift @{$hash->{helper}{queue}{$host}}, $cmdHash;
  1657. my $loginHash = {
  1658. name => $dname, ident => $ident, port => $port, host => $host,
  1659. parseCmd => 1, retry => 0, cmd => $cmd, cmdArgs => [ ],
  1660. authRetry => 0, admpwd => $pass, dologin => 1, ts => ESPEasy_timeStamp()
  1661. };
  1662. unshift @{$hash->{helper}{queue}{$host}}, $loginHash;
  1663. }
  1664. # credentials send but still 302...
  1665. else {
  1666. my $n = "Authorization failed. Discarding command \'$cmd $plist\'.";
  1667. push(@values, "n||Error||$n||2");
  1668. }
  1669. }
  1670. }
  1671. # check that response from cmd should be parsed (client attr parseCmdResponse)
  1672. elsif ($data ne "" && !$parseCmd) {
  1673. ESPEasy_httpReqDequeue($hash, $host);
  1674. return undef;
  1675. }
  1676. elsif ($data ne "") { # no error occurred
  1677. # command queue
  1678. delete $hash->{"WARNING_$host"};
  1679. (my $logData = $data) =~ s/\n//sg;
  1680. Log3 $name, 5, "$type $name: http response for ident:$ident cmd:'$cmd,$plist' => '$logData'";
  1681. # This json data are response from plugin. Lights and nfx plugin use it.
  1682. # Also status command (polling) send infos that will be evaluate (deprecated)
  1683. if ($data =~ m/^\{/) { #it could be json...
  1684. my $res;
  1685. # return here if PM JSON is not installed.
  1686. if ( !$hash->{helper}{pm}{JSON} ) {
  1687. Log3 $name, 2, "$type $name: Perl module JSON missing, can't process data.";
  1688. return undef;
  1689. }
  1690. $hash->{helper}{pm}{Encode} # use encode_utf8 if available else replace any disturbing chars
  1691. ? ( eval { $res = decode_json( encode_utf8($data) ); 1; } )
  1692. : ( eval { $res = decode_json( $data =~ s/[^\x20-\x7E]/_/gr ); 1; } );
  1693. # is there an json decode error?
  1694. if ($@) {
  1695. Log3 $name, 2, "$type $name: WARNING: deformed JSON data received from $host requested by $ident.";
  1696. Log3 $name, 2, "$type $name: $@";
  1697. push(@values, "n||Error||$@||2");
  1698. }
  1699. # json decode worked fine...
  1700. else {
  1701. # maps plugin type (answer for set state/gpio) to SENSOR_TYPE_SWITCH (vType:10)
  1702. my $vType = (defined $res->{plugin} && $res->{plugin} eq "1") ? "10" : "0";
  1703. # Plugins lights:123 nfx:124
  1704. if (defined $res->{plugin} && $res->{plugin} =~ m/^(123|124)$/) {
  1705. foreach my $key (keys %{ $res }) {
  1706. push @values, "r||$key||".$res->{$key}."||".$vType
  1707. if $res->{$key} ne "" && $key ne "plugin";
  1708. }
  1709. }
  1710. # all other plugins...
  1711. else {
  1712. push @values, "r||GPIO".$res->{pin}."_mode||".$res->{mode}."||".$vType;
  1713. push @values, "r||GPIO".$res->{pin}."_state||".$res->{state}."||".$vType;
  1714. push @values, "r||_lastAction||".$res->{log}."||".$vType if $res->{log} ne "";
  1715. }
  1716. } # json decode worked fine...
  1717. } #if ($data =~ m/^\{/)
  1718. # no json returned => unknown state
  1719. else {
  1720. Log3 $name, 5, "$type $name: No json fmt: ident:$ident $cmd $plist => $data";
  1721. if (defined $param->{cmd} && $param->{cmd} eq "status" && defined $param->{plist} && $param->{plist} =~ m/^gpio,(\d+)$/i) {
  1722. # push values/cmds in @values
  1723. if (defined $1) {
  1724. push @values, "r||GPIO".$1."_mode||"."?"."||0";
  1725. push @values, "r||GPIO".$1."_state||".$data."||0";
  1726. }
  1727. }
  1728. }
  1729. } # ($data ne "")
  1730. else {
  1731. }
  1732. ESPEasy_dispatch($hash,$ident,$host,@values);
  1733. ESPEasy_httpReqDequeue($hash, $host);
  1734. return undef;
  1735. }
  1736. # ------------------------------------------------------------------------------
  1737. # Queue cmd if max_sessions reached and queueSize is not reached,
  1738. # else discard cmd
  1739. # ------------------------------------------------------------------------------
  1740. sub ESPEasy_httpReqQueue(@)
  1741. {
  1742. my ($hash, $cmdHash) = @_;
  1743. my ($name, $type) = ($hash->{NAME}, $hash->{TYPE});
  1744. my $cmd = $cmdHash->{cmd};
  1745. my @cmdArgs = @{ $cmdHash->{cmdArgs} };
  1746. my $cmdArgs = join(",",@cmdArgs);
  1747. my $host = $cmdHash->{host};
  1748. my $queueSize = defined $hash->{helper}{queue} && defined $hash->{helper}{queue}{$host}
  1749. ? scalar @{$hash->{helper}{queue}{$host}} : 0;
  1750. $hash->{helper}{sessions}{$host} = 0 if !defined $hash->{helper}{sessions}{$host};
  1751. # is queueing enabled?
  1752. if ($hash->{MAX_HTTP_SESSIONS}) {
  1753. # do queueing if max sessions are already in use
  1754. if ($hash->{helper}{sessions}{$host} >= $hash->{MAX_HTTP_SESSIONS} ) {
  1755. # max queue size reached
  1756. if ($queueSize < $hash->{MAX_QUEUE_SIZE}) {
  1757. push(@{$hash->{helper}{queue}{$host}}, $cmdHash);
  1758. Log3 $name, 4, "$type $name: Queuing: $host $cmdHash->{ident} '$cmd $cmdArgs' ($queueSize)";
  1759. return 1;
  1760. }
  1761. else {
  1762. Log3 $name, 2, "$type $name: set $cmd $cmdArgs (skipped due to queue size exceeded: $hash->{MAX_QUEUE_SIZE})";
  1763. return 1;
  1764. }
  1765. }
  1766. }
  1767. return 0;
  1768. }
  1769. # ------------------------------------------------------------------------------
  1770. # De-Queue set cmds and delete $hash->{helper}{queue}.. if empty
  1771. # ------------------------------------------------------------------------------
  1772. sub ESPEasy_httpReqDequeue($$)
  1773. {
  1774. my ($hash,$host) = @_;
  1775. my ($name,$type) = ($hash->{NAME},$hash->{TYPE});
  1776. if ( defined $hash->{helper}{queue}
  1777. && defined $hash->{helper}{queue}{$host}
  1778. && scalar @{$hash->{helper}{queue}{$host}} ) {
  1779. my $cmdHash = shift @{ $hash->{helper}{queue}{$host} };
  1780. Log3 $name, 4, "$type $name: Dequeuing: $host $cmdHash->{ident} "
  1781. . "'$cmdHash->{cmd} " . join(",",@{$cmdHash->{cmdArgs}})."'"
  1782. . " (".scalar @{$hash->{helper}{queue}{$host}}.")";
  1783. # delete queue if empty
  1784. delete $hash->{helper}{queue}{$host} if defined $hash->{helper}{queue} && defined $hash->{helper}{queue}{$host} && scalar @{$hash->{helper}{queue}{$host}} == 0;
  1785. delete $hash->{helper}{queue} if defined $hash->{helper}{queue} && scalar keys %{ $hash->{helper}{queue} } == 0;
  1786. ESPEasy_httpReq($hash, $cmdHash);
  1787. }
  1788. return undef;
  1789. }
  1790. # ------------------------------------------------------------------------------
  1791. sub ESPEasy_statusRequest($) #called by device
  1792. {
  1793. my ($hash) = @_;
  1794. my ($name, $type) = ($hash->{NAME},$hash->{TYPE});
  1795. unless (IsDisabled $name) {
  1796. Log3 $name, 4, "$type $name: set statusRequest";
  1797. ESPEasy_pollGPIOs($hash);
  1798. ESPEasy_checkPresence($hash);
  1799. ESPEasy_setState($hash);
  1800. }
  1801. ESPEasy_resetTimer($hash);
  1802. return undef;
  1803. }
  1804. # ------------------------------------------------------------------------------
  1805. sub ESPEasy_pollGPIOs($) #called by device
  1806. {
  1807. my ($hash) = @_;
  1808. my $name = $hash->{NAME};
  1809. my $type = $hash->{TYPE};
  1810. my $sleep = $hash->{SLEEP};
  1811. my $a = AttrVal($name,'pollGPIOs',undef);
  1812. if (!defined $a) {
  1813. # do nothing, just return
  1814. }
  1815. elsif (defined $sleep && $sleep eq "1") {
  1816. Log3 $name, 2, "$type $name: Polling of GPIOs is not possible as long as deep sleep mode is active.";
  1817. }
  1818. else {
  1819. my @gpios = split(",",$a);
  1820. foreach my $gpio (@gpios) {
  1821. if ($gpio =~ m/^[a-zA-Z]/) { # pin mapping (eg. D8 -> 15)
  1822. Log3 $name, 5, "$type $name: Pin mapping ".uc $gpio." => $ee_map{pins}{uc $gpio}";
  1823. $gpio = $ee_map{pins}{uc $gpio};
  1824. }
  1825. Log3 $name, 5, "$type $name: IOWrite(\$defs{$name}, $hash, 1, status, gpio,".$gpio.")";
  1826. IOWrite($hash, $hash, 1, "status", "gpio,".$gpio);
  1827. }
  1828. }
  1829. return undef;
  1830. }
  1831. # ------------------------------------------------------------------------------
  1832. sub ESPEasy_resetTimer($;$)
  1833. {
  1834. my ($hash,$sig) = @_;
  1835. my $name = $hash->{NAME};
  1836. my $type = $hash->{TYPE};
  1837. $sig = "" if !$sig;
  1838. if ($init_done == 1) {
  1839. RemoveInternalTimer($hash, "ESPEasy_statusRequest");
  1840. }
  1841. if ($sig eq "stop") {
  1842. Log3 $name, 5, "$type $name: internalTimer stopped";
  1843. return undef;
  1844. }
  1845. return undef if AttrVal($name,"Interval",$d_Interval) == 0;
  1846. unless(IsDisabled($name)) {
  1847. my $s = AttrVal($name,"Interval",$d_Interval) + rand(5);
  1848. my $ts = $s + gettimeofday();
  1849. Log3 $name, 5, "$type $name: Start internalTimer +".int($s)." => ".FmtDateTime($ts);
  1850. InternalTimer($ts, "ESPEasy_statusRequest", $hash);
  1851. }
  1852. return undef;
  1853. }
  1854. # ------------------------------------------------------------------------------
  1855. # Loaned from TcpServerUtils, but no IPv4 fallback, modified logging and state
  1856. sub ESPEasy_TcpServer_Open($)
  1857. {
  1858. my ($hash) = @_;
  1859. my ($name, $type) = ($hash->{NAME}, $hash->{TYPE});
  1860. my $port = $hash->{PORT};
  1861. my $dp = "[TCP:".($hash->{IPV}==4?'IPV4:':'')."$port]";
  1862. if($port =~ m/^IPV6:(\d+)$/i) {
  1863. $port = $1;
  1864. eval "require IO::Socket::INET6; use Socket6;";
  1865. if($@) {
  1866. readingsSingleUpdate($hash, 'state', 'error', 1);
  1867. Log3 $hash, 1, "$type $name: Error: Can't load INET6";
  1868. Log3 $hash, 1, "$type $name: $@";
  1869. return "error";
  1870. } else {
  1871. $hash->{IPV6} = 1;
  1872. }
  1873. }
  1874. my @opts = (
  1875. Domain => ($hash->{IPV6} ? AF_INET6() : AF_UNSPEC), # Linux bug
  1876. LocalHost => undef,
  1877. LocalPort => $port,
  1878. Listen => 32, # For Windows
  1879. Blocking => ($^O =~ /Win/ ? 1 : 0), # Needed for .WRITEBUFFER@darwin
  1880. ReuseAddr => 1
  1881. );
  1882. $hash->{SERVERSOCKET} = $hash->{IPV6}
  1883. ? IO::Socket::INET6->new(@opts)
  1884. : IO::Socket::INET->new(@opts);
  1885. if(!$hash->{SERVERSOCKET}) {
  1886. Log3 $hash, 1, "$type $name: Error: Can't open server port $dp";
  1887. Log3 $hash, 1, "$type $name: $!";
  1888. readingsSingleUpdate($hash, 'state', 'error', 1);
  1889. return "error";
  1890. }
  1891. $hash->{FD} = $hash->{SERVERSOCKET}->fileno();
  1892. $hash->{PORT} = $hash->{SERVERSOCKET}->sockport();
  1893. $selectlist{"$name.$port"} = $hash;
  1894. readingsSingleUpdate($hash, 'state', 'Initialized', 1);
  1895. Log3 $hash, 3, "$type $name: Bridge v$module_version port $dp opened.";
  1896. return undef;
  1897. }
  1898. # ------------------------------------------------------------------------------
  1899. sub ESPEasy_TcpServer_Close($;$) # 1:hash 2:silent
  1900. {
  1901. my ($hash, $silent) = @_;
  1902. TcpServer_Close($hash);
  1903. if (!defined $hash->{TEMPORARY} && !$silent) {
  1904. my ($name, $type) = ($hash->{NAME}, $hash->{TYPE});
  1905. my $dp = "[TCP:".($hash->{IPV}==4?'IPV4:':'').$hash->{PORT}."]";
  1906. Log3 $hash, 3, "$type $name: Bridge v$module_version port $dp closed.";
  1907. }
  1908. return undef;
  1909. }
  1910. # ------------------------------------------------------------------------------
  1911. # Duplicated sub from TcpServerUtils as a workaround for new security feature:
  1912. # https://forum.fhem.de/index.php/topic,72717.0.html
  1913. sub ESPEasy_TcpServer_Accept($$)
  1914. {
  1915. my ($hash, $type) = @_;
  1916. my $name = $hash->{NAME};
  1917. my @clientinfo = $hash->{SERVERSOCKET}->accept();
  1918. if(!@clientinfo) {
  1919. Log3 $name, 1, "Accept failed ($name: $!)" if($! != EAGAIN);
  1920. return undef;
  1921. }
  1922. $hash->{CONNECTS}++;
  1923. my ($port, $iaddr) = $hash->{IPV6} ?
  1924. sockaddr_in6($clientinfo[1]) :
  1925. sockaddr_in($clientinfo[1]);
  1926. my $caddr = $hash->{IPV6} ?
  1927. inet_ntop(AF_INET6(), $iaddr) :
  1928. inet_ntoa($iaddr);
  1929. # ------------------------------------------------------------------------------
  1930. # Removed from sub because we have our own access control that works in a more
  1931. # readable and flexible way (network ranges with allow/deny and regexps).
  1932. # Our new allowed ranges default are also now:
  1933. # 127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,fc00::/7,fe80::/10,::1
  1934. # ------------------------------------------------------------------------------
  1935. #
  1936. # my $af = $attr{$name}{allowfrom};
  1937. # if(!$af) {
  1938. # my $re = "^(127|192.168|172.(1[6-9]|2[0-9]|3[01])|10|169.254)\\.|".
  1939. # "^(fe[89ab]|::1)";
  1940. # if($caddr !~ m/$re/) {
  1941. # my %empty;
  1942. # $hash->{SNAME} = $hash->{NAME};
  1943. # my $auth = Authenticate($hash, \%empty);
  1944. # delete $hash->{SNAME};
  1945. # if($auth == 0) {
  1946. # Log3 $name, 1,
  1947. # "Connection refused from the non-local address $caddr:$port, ".
  1948. # "as there is no working allowed instance defined for it";
  1949. # close($clientinfo[0]);
  1950. # return undef;
  1951. # }
  1952. # }
  1953. # }
  1954. #
  1955. # if($af) {
  1956. # if($caddr !~ m/$af/) {
  1957. # my $hostname = gethostbyaddr($iaddr, AF_INET);
  1958. # if(!$hostname || $hostname !~ m/$af/) {
  1959. # Log3 $name, 1, "Connection refused from $caddr:$port";
  1960. # close($clientinfo[0]);
  1961. # return undef;
  1962. # }
  1963. # }
  1964. # }
  1965. #$clientinfo[0]->blocking(0); # Forum #24799
  1966. if($hash->{SSL}) {
  1967. # Forum #27565: SSLv23:!SSLv3:!SSLv2', #35004: TLSv12:!SSLv3
  1968. my $sslVersion = AttrVal($hash->{NAME}, "sslVersion",
  1969. AttrVal("global", "sslVersion", "TLSv12:!SSLv3"));
  1970. # Certs directory must be in the modpath, i.e. at the same level as the
  1971. # FHEM directory
  1972. my $mp = AttrVal("global", "modpath", ".");
  1973. my $ret = IO::Socket::SSL->start_SSL($clientinfo[0], {
  1974. SSL_server => 1,
  1975. SSL_key_file => "$mp/certs/server-key.pem",
  1976. SSL_cert_file => "$mp/certs/server-cert.pem",
  1977. SSL_version => $sslVersion,
  1978. SSL_cipher_list => 'HIGH:!RC4:!eNULL:!aNULL',
  1979. Timeout => 4,
  1980. });
  1981. my $err = $!;
  1982. if( !$ret
  1983. && $err != EWOULDBLOCK
  1984. && $err ne "Socket is not connected") {
  1985. $err = "" if(!$err);
  1986. $err .= " ".($SSL_ERROR ? $SSL_ERROR : IO::Socket::SSL::errstr());
  1987. Log3 $name, 1, "$type SSL/HTTPS error: $err"
  1988. if($err !~ m/error:00000000:lib.0.:func.0.:reason.0./); #Forum 56364
  1989. close($clientinfo[0]);
  1990. return undef;
  1991. }
  1992. }
  1993. my $cname = "${name}_${caddr}_${port}";
  1994. my %nhash;
  1995. $nhash{NR} = $devcount++;
  1996. $nhash{NAME} = $cname;
  1997. $nhash{PEER} = $caddr;
  1998. $nhash{PORT} = $port;
  1999. $nhash{FD} = $clientinfo[0]->fileno();
  2000. $nhash{CD} = $clientinfo[0]; # sysread / close won't work on fileno
  2001. $nhash{TYPE} = $type;
  2002. $nhash{SSL} = $hash->{SSL};
  2003. $nhash{STATE} = "Connected";
  2004. $nhash{SNAME} = $name;
  2005. $nhash{TEMPORARY} = 1; # Don't want to save it
  2006. $nhash{BUF} = "";
  2007. $attr{$cname}{room} = "hidden";
  2008. $defs{$cname} = \%nhash;
  2009. $selectlist{$nhash{NAME}} = \%nhash;
  2010. my $ret = $clientinfo[0]->setsockopt(SOL_SOCKET, SO_KEEPALIVE, 1);
  2011. Log3 $name, 4, "Connection accepted from $nhash{NAME}";
  2012. return \%nhash;
  2013. }
  2014. # ------------------------------------------------------------------------------
  2015. sub ESPEasy_header2Hash($) {
  2016. my ($string) = @_;
  2017. my %header = ();
  2018. foreach my $line (split("\r\n", $string)) {
  2019. my ($key,$value) = split(": ", $line,2);
  2020. next if !$value;
  2021. $value =~ s/^ //;
  2022. $header{$key} = $value;
  2023. }
  2024. return \%header;
  2025. }
  2026. # ------------------------------------------------------------------------------
  2027. sub ESPEasy_isCmdAvailable($$@)
  2028. {
  2029. my ($hash,$cmd) = @_;
  2030. my $name = $hash->{NAME};
  2031. if (!defined $data{ESPEasy}{$name}{sets}{$cmd}) {
  2032. my $clist;
  2033. foreach my $c (sort keys %{ $data{ESPEasy}{$name}{sets} } ) {
  2034. $clist .= $c . ($data{ESPEasy}{$name}{sets}{$c}{widget} eq ""
  2035. ? " " : ":$data{ESPEasy}{$name}{sets}{$c}{widget} ");
  2036. }
  2037. return $clist;
  2038. }
  2039. return undef;
  2040. }
  2041. # ------------------------------------------------------------------------------
  2042. sub ESPEasy_isParseCmd($$) #called by device
  2043. {
  2044. my ($hash,$cmd) = @_;
  2045. my $name = $hash->{NAME};
  2046. my $doParse = 0;
  2047. my @cmds = split(",",AttrVal($name,"parseCmdResponse","status"));
  2048. foreach (@cmds) {
  2049. if (lc($_) eq lc($cmd)) {
  2050. $doParse = 1;
  2051. last;
  2052. }
  2053. }
  2054. return $doParse;
  2055. }
  2056. # ------------------------------------------------------------------------------
  2057. sub ESPEasy_sendHttpClose($$$) {
  2058. my ($hash,$code,$response) = @_;
  2059. my ($name,$type,$con) = ($hash->{NAME},$hash->{TYPE},$hash->{CD});
  2060. my $ipv = $hash->{PEER} =~ m/:/ ? 6 : 4;
  2061. my $bhash = $modules{ESPEasy}{defptr}{BRIDGE}{$ipv};
  2062. my $bname = $bhash->{NAME};
  2063. print $con "HTTP/1.1 ".$code."\r\n",
  2064. "Content-Type: text/plain\r\n",
  2065. "Connection: close\r\n",
  2066. "Content-Length: ".length($response)."\r\n\r\n",
  2067. $response;
  2068. Log3 $bname, 4, "$type $name: Send http close '$code'";
  2069. return undef;
  2070. }
  2071. # ------------------------------------------------------------------------------
  2072. sub ESPEasy_paramPos($$$)
  2073. {
  2074. my ($hash,$cmd,$search) = @_;
  2075. my $name = $hash->{NAME};
  2076. my @usage = split(" ",$data{ESPEasy}{$name}{sets}{$cmd}{usage});
  2077. my $pos = 0;
  2078. my $i = 0;
  2079. foreach (@usage) {
  2080. if ($_ eq $search) {
  2081. $pos = $i+1;
  2082. last;
  2083. }
  2084. $i++;
  2085. }
  2086. return $pos; # return 0 if no match, else position
  2087. }
  2088. # ------------------------------------------------------------------------------
  2089. sub ESPEasy_paramCount($)
  2090. {
  2091. return () = $_[0] =~ m/\s/g # count \s in a string
  2092. }
  2093. # ------------------------------------------------------------------------------
  2094. sub ESPEasy_clearReadings($)
  2095. {
  2096. my ($hash) = @_;
  2097. my $name = $hash->{NAME};
  2098. my $type = $hash->{TYPE};
  2099. my @dr;
  2100. foreach (keys %{$hash->{READINGS}}) {
  2101. CommandDeleteReading(undef, "$name $_");
  2102. push(@dr,$_);
  2103. }
  2104. if (scalar @dr >= 1) {
  2105. delete $hash->{helper}{received};
  2106. delete $hash->{helper}{fpc}; # used in checkPresence
  2107. Log3 $name, 3, "$type $name: Readings [".join(",",@dr)."] wiped out";
  2108. }
  2109. ESPEasy_setState($hash);
  2110. return undef
  2111. }
  2112. # ------------------------------------------------------------------------------
  2113. sub ESPEasy_checkVersion($$$$)
  2114. {
  2115. my ($hash,$dev,$ve,$vj) = @_;
  2116. my ($type,$name) = ($hash->{TYPE},$hash->{NAME});
  2117. my $ov = "_OUTDATED_ESP_VER_$dev";
  2118. if ($vj < $minJsonVersion) {
  2119. $hash->{$ov} = "R".$ve."/J".$vj;
  2120. Log3 $name, 2, "$type $name: WARNING: no data processed. ESPEasy plugin "
  2121. ."'FHEM HTTP' is too old [$dev: R".$ve." J".$vj."]. ".
  2122. "Use ESPEasy R$minEEBuild at least.";
  2123. return 1;
  2124. }
  2125. else{
  2126. delete $hash->{$ov} if exists $hash->{$ov};
  2127. return 0;
  2128. }
  2129. }
  2130. # ------------------------------------------------------------------------------
  2131. sub ESPEasy_checkPresence($)
  2132. {
  2133. my ($hash,$isPresent) = @_;
  2134. my $name = $hash->{NAME};
  2135. my $type = $hash->{TYPE};
  2136. my $interval = AttrVal($name,'Interval',$d_Interval);
  2137. my $addTime = 10; # if there is extreme heavy system load
  2138. return undef if AttrVal($name,'presenceCheck',1) == 0;
  2139. return undef if $interval == 0;
  2140. my $presence = "absent";
  2141. # check each received reading
  2142. foreach my $reading (keys %{$hash->{helper}{received}}) {
  2143. if (ReadingsAge($name,$reading,0) < $interval+$addTime) {
  2144. #dev is present if any reading is newer than INTERVAL+$addTime
  2145. $presence = "present";
  2146. last;
  2147. }
  2148. }
  2149. # update presence only if FirstPrecenceCheck is $interval seconds ago.
  2150. $hash->{helper}{fpc} = time() if (!defined $hash->{helper}{fpc});
  2151. if ($presence eq "present" || (time() - $hash->{helper}{fpc}) > $interval) {
  2152. readingsSingleUpdate($hash,"presence",$presence,1);
  2153. Log3 $name, 4, "$type $name: presence: $presence";
  2154. }
  2155. return undef;
  2156. }
  2157. # ------------------------------------------------------------------------------
  2158. sub ESPEasy_setState($)
  2159. {
  2160. my ($hash) = @_;
  2161. my $name = $hash->{NAME};
  2162. my $type = $hash->{TYPE};
  2163. return undef if not AttrVal($name,"setState",1);
  2164. if (AttrVal($name,"rgbGPIOs",0)) {
  2165. my ($r,$g,$b) = ESPEasy_gpio2RGB($hash);
  2166. if ($r ne "") {
  2167. readingsSingleUpdate($hash,"state", "R: $r G: $g B: $b", 1)
  2168. }
  2169. }
  2170. else {
  2171. my $interval = AttrVal($name,"Interval",$d_Interval);
  2172. my $addTime = 3;
  2173. my @ret;
  2174. foreach my $reading (sort keys %{$hash->{helper}{received}}) {
  2175. next if $reading =~ m/^(\.ignored_.*|state|presence|_lastAction|_lastError|\w+_mode)$/;
  2176. next if $interval && ReadingsAge($name,$reading,1) > $interval+$addTime;
  2177. push(@ret, substr($reading,0,AttrVal($name,"setState",3))
  2178. .": ".ReadingsVal($name,$reading,""));
  2179. }
  2180. my $oState = ReadingsVal($name, "state", "");
  2181. my $presence = ReadingsVal($name, "presence", "Initialized");
  2182. if ($presence eq "absent" && $oState ne "absent") {
  2183. readingsSingleUpdate($hash,"state","absent", 1 );
  2184. delete $hash->{helper}{received};
  2185. }
  2186. else {
  2187. my $nState = (scalar @ret >= 1) ? join(" ",@ret) : $presence;
  2188. readingsSingleUpdate($hash,"state",$nState, 1 ); # if ($oState ne $nState);
  2189. }
  2190. }
  2191. return undef;
  2192. }
  2193. # ------------------------------------------------------------------------------
  2194. sub ESPEasy_setRGB($$@)
  2195. {
  2196. my ($hash,$cmd,@p) = @_;
  2197. my ($type,$name) = ($hash->{TYPE},$hash->{NAME});
  2198. my ($rg,$gg,$bg) = split(",",AttrVal($name,"rgbGPIOs",""));
  2199. my ($r,$g,$b);
  2200. my $rgb = $p[0] if $cmd =~ m/^rgb$/i;
  2201. # return undef if !defined $rgb;
  2202. $rg = $ee_map{pins}{uc $rg} if defined $ee_map{pins}{uc $rg};
  2203. $gg = $ee_map{pins}{uc $gg} if defined $ee_map{pins}{uc $gg};
  2204. $bg = $ee_map{pins}{uc $bg} if defined $ee_map{pins}{uc $bg};
  2205. if ($cmd =~ m/^(1|on)$/ || ($cmd =~ m/^rgb$/i && $rgb =~ m/^(1|on)$/)) {
  2206. $rgb = "FFFFFF" }
  2207. elsif ($cmd =~ m/^(0|off)$/ || ($cmd =~ m/^rgb$/i && $rgb =~ m/^(0|off)$/)) {
  2208. $rgb = "000000" }
  2209. elsif ($cmd =~ m/^toggle$/i || ($cmd =~ m/^rgb$/i && $rgb =~ m/^toggle$/i)) {
  2210. $rgb = ReadingsVal($name,"rgb","000000") ne "000000" ? "000000" : "FFFFFF"
  2211. }
  2212. if ($rgb =~ m/^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/) {
  2213. ($r,$g,$b) = (hex($1), hex($2), hex($3));
  2214. }
  2215. else {
  2216. Log3 $name, 2, "$type $name: set $name $cmd $rgb: "
  2217. ."'$rgb' is not a valid RGB value.";
  2218. return "'$rgb' is not a valid RGB value.";
  2219. }
  2220. ESPEasy_Set($hash, $name, "pwm", ("$rg", $r*4));
  2221. ESPEasy_Set($hash, $name, "pwm", ("$gg", $g*4));
  2222. ESPEasy_Set($hash, $name, "pwm", ("$bg", $b*4));
  2223. readingsSingleUpdate($hash, "rgb", uc $rgb, 1);
  2224. return undef;
  2225. }
  2226. # ------------------------------------------------------------------------------
  2227. sub ESPEasy_setCT($$@)
  2228. {
  2229. my ($hash,$cmd,@p) = @_;
  2230. my ($type,$name) = ($hash->{TYPE},$hash->{NAME});
  2231. my ($gww,$gcw) = split(",",AttrVal($name,"wwcwGPIOs",""));
  2232. my ($ww,$cw);
  2233. my ($pct,$ct);
  2234. my $ctWW = AttrVal($name,"colorpickerCTww",$d_colorpickerCTww);
  2235. my $ctCW = AttrVal($name,"colorpickerCTcw",$d_colorpickerCTcw);
  2236. my $ctWW_lim = AttrVal($name,"ctWW_reducedRange",undef);
  2237. my $ctCW_lim = AttrVal($name,"ctCW_reducedRange",undef);
  2238. $gww = $ee_map{pins}{uc $gww} if defined $ee_map{pins}{uc $gww};
  2239. $gcw = $ee_map{pins}{uc $gcw} if defined $ee_map{pins}{uc $gcw};
  2240. readingsSingleUpdate($hash, $cmd, $p[0], 1);
  2241. if ($cmd eq "ct") {
  2242. $ct = $p[0];
  2243. $pct = ReadingsVal($name,"pct",50);
  2244. }
  2245. elsif ($cmd eq "pct") {
  2246. $pct = $p[0];
  2247. $ct = ReadingsVal($name,"ct",3000);
  2248. }
  2249. # are we out of range?
  2250. $pct = 100 if $pct > 100;
  2251. $pct = 0 if $pct < 0;
  2252. $ct = $ctWW if $ct < $ctWW;
  2253. $ct = $ctCW if $ct > $ctCW;
  2254. #Log 1, "pct:$pct ct:$ct ctWW:$ctWW ctCW:$ctCW ctWW_lim:$ctWW_lim ctCW_lim:$ctCW_lim";
  2255. my $wwcwMaxBri = AttrVal($name,"wwcwMaxBri",0);
  2256. my ($fww,$fcw) = ESPEasy_ct2wwcw($ct, $ctWW, $ctCW, $wwcwMaxBri, $ctWW_lim, $ctCW_lim);
  2257. ESPEasy_Set($hash, $name, "pwm", ($gww, int $pct*10.23*$fww));
  2258. ESPEasy_Set($hash, $name, "pwm", ($gcw, int $pct*10.23*$fcw));
  2259. return undef;
  2260. }
  2261. # ------------------------------------------------------------------------------
  2262. # ct2wwcw with constant brightness over temp range (or max bri if $maxBri == 1).
  2263. # "used range" can be set to reduce temp range to get a lighter leds with constant
  2264. # bri over reduced temp range.
  2265. # 1: temp to set 2:led-ww-temp 3:led-cw-temp 4:maxBri 5:used range ww 6:used range cw
  2266. sub ESPEasy_ct2wwcw($$$;$$$)
  2267. {
  2268. my ($t,$tww,$tcw,$maxBri,$tww_ur,$tcw_ur) = @_;
  2269. my $maxBriFactor;
  2270. $tcw -= $tww;
  2271. $t -= $tww;
  2272. my $fcw = $t / $tcw;
  2273. my $fww = 1 - $fcw;
  2274. if ($maxBri // $maxBri) {
  2275. $maxBriFactor = ($fcw > $fww) ? 1/$fcw : 1/$fww;
  2276. #Log 1, "maxBriFactor: $maxBriFactor (maxBri)";
  2277. }
  2278. else {
  2279. $tww_ur = $tww if !(defined $tww_ur) || $tww_ur < $tww || $tww_ur >= $tcw;
  2280. $tcw_ur = $tcw if !(defined $tcw_ur) || $tcw_ur > $tcw || $tcw_ur <= $tww;
  2281. $tww_ur -= $tww;
  2282. $tcw_ur -= $tww;
  2283. my $t = ($tww_ur < $tcw - $tcw_ur) ? $tww_ur : $tcw - $tcw_ur;
  2284. my $fcw = $t / $tcw;
  2285. my $fww = 1 - $fcw;
  2286. $maxBriFactor = ($fcw > $fww) ? 1/$fcw : 1/$fww;
  2287. #Log 1, "maxBriFactor: $maxBriFactor (constBri)";
  2288. }
  2289. return ( $fww * $maxBriFactor, $fcw * $maxBriFactor );
  2290. }
  2291. # ------------------------------------------------------------------------------
  2292. sub ESPEasy_gpio2RGB($)
  2293. {
  2294. my ($hash) = @_;
  2295. my $name = $hash->{NAME};
  2296. my ($r,$g,$b,$rgb);
  2297. my $a = AttrVal($name,"rgbGPIOs",undef);
  2298. return undef if !defined $a;
  2299. my ($gr,$gg,$gb) = split(",",AttrVal($name,"rgbGPIOs",""));
  2300. $gr = $ee_map{pins}{uc $gr} if defined $ee_map{pins}{uc $gr};
  2301. $gg = $ee_map{pins}{uc $gg} if defined $ee_map{pins}{uc $gg};
  2302. $gb = $ee_map{pins}{uc $gb} if defined $ee_map{pins}{uc $gb};
  2303. my $rr = AttrVal($name,"readingPrefixGPIO","GPIO").$gr;
  2304. my $rg = AttrVal($name,"readingPrefixGPIO","GPIO").$gg;
  2305. my $rb = AttrVal($name,"readingPrefixGPIO","GPIO").$gb;
  2306. $r = ReadingsVal($name,$rr,undef);
  2307. $g = ReadingsVal($name,$rg,undef);
  2308. $b = ReadingsVal($name,$rb,undef);
  2309. return ("","","") if !defined $r || !defined $g || !defined $b
  2310. || $r !~ m/^\d+$/ || $g !~ m/^\d+$/i || $b !~ m/^\d+$/i;
  2311. return (sprintf("%2.2X",$r/4), sprintf("%2.2X",$g/4), sprintf("%2.2X",$b/4));
  2312. }
  2313. # ------------------------------------------------------------------------------
  2314. # attr <dev> devStateIcon { ESPEasy_devStateIcon($name) }
  2315. sub ESPEasy_devStateIcon($)
  2316. {
  2317. my $ret = Color::devStateIcon($_[0],"rgb","rgb");
  2318. $ret =~ m/^.*:on@#(..)(..)(..):toggle$/;
  2319. return undef if !defined $1;
  2320. my $symP = int((hex($1)+hex($2)+hex($3))/76.5)*10;
  2321. $symP = "00" if $symP == 0;
  2322. my $icon = "light_light_dim_".$symP;
  2323. $ret =~ s/:on@#/:$icon@#/;
  2324. return $ret;
  2325. }
  2326. # ------------------------------------------------------------------------------
  2327. sub ESPEasy_adjustValue($$$)
  2328. {
  2329. my ($hash,$r,$v) = @_;
  2330. my $name = $hash->{NAME};
  2331. my $type = $hash->{TYPE};
  2332. my $a = AttrVal($name,"adjustValue",undef);
  2333. return $v if !defined $a;
  2334. my ($VALUE,$READING,$NAME) = ($v,$r,$name); #capital vars for use in attribute
  2335. my @a = split(" ",$a);
  2336. foreach (@a) {
  2337. my ($regex,$formula) = split(":",$_);
  2338. if ($r =~ m/^$regex$/) {
  2339. no warnings;
  2340. my $adjVal = $formula =~ m/\$VALUE/ ? eval($formula) : eval($v.$formula);
  2341. use warnings;
  2342. if ($@) {
  2343. Log3 $name, 2, "$type $name: WARNING: attribute 'adjustValue': "
  2344. ."mad expression '$formula'";
  2345. Log3 $name, 2, "$type $name: $@";
  2346. }
  2347. else {
  2348. my $rText = (defined $adjVal) ? $adjVal : "'undef'";
  2349. Log3 $name, 5, "$type $name: Adjusted reading $r: $v => $formula = $rText";
  2350. return $adjVal;
  2351. }
  2352. #last; #disabled to be able to match multiple readings
  2353. }
  2354. }
  2355. return $v;
  2356. }
  2357. # ------------------------------------------------------------------------------
  2358. sub ESPEasy_urlEncodeDisplayText($$@)
  2359. {
  2360. my ($hash, $cmd, @params) = @_;
  2361. my $name = $hash->{NAME};
  2362. my $enc = AttrVal($name, "displayTextEncode", $d_displayTextEncode);
  2363. my $pp = ESPEasy_paramPos($hash,$cmd,'<text>');
  2364. if ($enc && $pp) {
  2365. my (@p, @t);
  2366. my $c = scalar @params;
  2367. # leading parameters
  2368. for (my $i=0; $i<$pp-1; $i++) {
  2369. push( @p, $params[$i] )
  2370. }
  2371. # collect all texts parameters
  2372. for (my $i=$pp-1; $i<$c; $i++) {
  2373. $params[$i] =~ s/,/./g; # comma is ESPEasy parameter splitter, can't be used
  2374. push @t, $params[$i];
  2375. }
  2376. my $text = join(" ", @t);
  2377. # fill line with leading/trailing spaces
  2378. my $width = AttrVal($name,"displayTextWidth", $d_displayTextWidth);
  2379. if ($width) {
  2380. $text = " " x ($p[1]-1) .$text. " " x ($width - length($text) - $p[1]+1);
  2381. $text = substr($text, 0, $width);
  2382. $p[1] = 1;
  2383. }
  2384. push(@p, urlEncode($text));
  2385. return @p;
  2386. }
  2387. return @params;
  2388. }
  2389. # ------------------------------------------------------------------------------
  2390. sub ESPEasy_loadRequiredModules($)
  2391. {
  2392. my ($hash) = @_;
  2393. foreach ("JSON", "Encode") {
  2394. eval "use $_; 1;";
  2395. if (!$@) {
  2396. $hash->{helper}{pm}{$_} = 1;
  2397. }
  2398. else {
  2399. $hash->{helper}{pm}{$_} = 0;
  2400. if ($init_done || $hash->{SUBTYPE} eq "bridge") {
  2401. my ($name,$type) = ($hash->{NAME},$hash->{TYPE});
  2402. Log3 $name, 1, "$type $name: WARNING: Perl module $_ is not installed. "
  2403. . "Reduced functionality!";
  2404. Log3 $name, 2, "$type $name: $@" if $init_done;
  2405. }
  2406. }
  2407. }
  2408. return undef;
  2409. }
  2410. # ------------------------------------------------------------------------------
  2411. sub ESPEasy_isAttrCombineDevices($)
  2412. {
  2413. return 0 if !defined $_[0];
  2414. my @ranges = split(/,| /,$_[0]);
  2415. foreach (@ranges) {
  2416. if (!($_ =~ m/^([A-Za-z0-9_\.]|[A-Za-z0-9_\.][A-Za-z0-9_\.]*[A-Za-z0-9\._])$/
  2417. || ESPEasy_isIPv64Range($_)))
  2418. {
  2419. return 0
  2420. }
  2421. }
  2422. return 1;
  2423. }
  2424. # ------------------------------------------------------------------------------
  2425. # check if $peer is covered by $allowed (eg. 10.1.2.3 is included in 10.0.0.0/8)
  2426. # 1:peer address 2:allowed range
  2427. # ------------------------------------------------------------------------------
  2428. sub ESPEasy_isCombineDevices($$$)
  2429. {
  2430. my ($peer,$espName,$allowed) = @_;
  2431. return $allowed if $allowed =~ m/^[01]$/;
  2432. my @allowed = split(/,| /,$allowed);
  2433. foreach (@allowed) { return 1 if $espName eq $_ }
  2434. return 1 if ESPEasy_isPeerAllowed($peer,$allowed);
  2435. return 0;
  2436. }
  2437. # ------------------------------------------------------------------------------
  2438. # check param to be a valid ip64 address or fqdn or hostname
  2439. # ------------------------------------------------------------------------------
  2440. sub ESPEasy_isAuthenticated($$)
  2441. {
  2442. my ($hash,$ah) = @_;
  2443. my ($name,$type) = ($hash->{NAME},$hash->{TYPE});
  2444. my $ipv = $hash->{PEER} =~ m/:/ ? 6 : 4;
  2445. my $bhash = $modules{ESPEasy}{defptr}{BRIDGE}{$ipv};
  2446. my ($bname,$btype) = ($bhash->{NAME},$bhash->{TYPE});
  2447. my $u = $bhash->{".bau"};
  2448. my $p = $bhash->{".bap"};
  2449. my $attr = AttrVal($bname,"authentication",0);
  2450. if (!defined $u || !defined $p || $attr == 0) {
  2451. if (defined $ah){
  2452. Log3 $bname, 2, "$type $name: No basic authentication active but ".
  2453. "credentials received";
  2454. }
  2455. else {
  2456. Log3 $bname, 4, "$type $name: No basic authentication required";
  2457. }
  2458. return "not required";
  2459. }
  2460. elsif (defined $ah) {
  2461. my ($a,$v) = split(" ",$ah);
  2462. if ($a eq "Basic" && decode_base64($v) eq $u.":".$p) {
  2463. Log3 $bname, 4, "$type $name: Basic authentication accepted";
  2464. return "accepted";
  2465. }
  2466. else {
  2467. Log3 $bname, 2, "$type $name: Basic authentication rejected";
  2468. }
  2469. }
  2470. else {
  2471. Log3 $bname, 2, "$type $name: Basic authentication active but ".
  2472. "no credentials received";
  2473. }
  2474. return undef;
  2475. }
  2476. # ------------------------------------------------------------------------------
  2477. sub ESPEasy_isValidPeer($)
  2478. {
  2479. my ($addr) = @_;
  2480. return 0 if !defined $addr;
  2481. my @ranges = split(/,| /,$addr);
  2482. foreach (@ranges) {
  2483. return 0 if !( ESPEasy_isIPv64Range($_)
  2484. || ESPEasy_isFqdn($_) || ESPEasy_isHostname($_) );
  2485. }
  2486. return 1;
  2487. }
  2488. # ------------------------------------------------------------------------------
  2489. # check if given ip or ip range is guilty
  2490. # argument can be:
  2491. # - ipv4, ipv4/CIDR, ipv4/dotted, ipv6, ipv6/CIDR (or a regexp if opt. argument
  2492. # $regexChk is set)
  2493. # - space or comma separated list of above.
  2494. # ------------------------------------------------------------------------------
  2495. sub ESPEasy_isIPv64Range($;$)
  2496. {
  2497. my ($addr,$regexChk) = @_;
  2498. return 0 if !defined $addr;
  2499. my @ranges = split(/,| /,$addr);
  2500. foreach (@ranges) {
  2501. my ($ip,$nm) = split("/",$_);
  2502. if (ESPEasy_isIPv4($ip)) {
  2503. return 0 if defined $nm && !( ESPEasy_isNmDotted($nm)
  2504. || ESPEasy_isNmCIDRv4($nm) );
  2505. }
  2506. elsif (ESPEasy_isIPv6($ip)) {
  2507. return 0 if defined $nm && !ESPEasy_isNmCIDRv6($nm);
  2508. }
  2509. elsif (defined $regexChk && !defined $nm) {
  2510. return 0 if $ip =~ m/^\*/ || $ip =~ m/^\d+\.\d+\.\d+\.\d+$/; # faulty regexp/ip
  2511. eval { "Hallo" =~ m/^$ip$/ };
  2512. return $@ ? 0 : 1;
  2513. }
  2514. else {
  2515. return 0;
  2516. }
  2517. }
  2518. return 1;
  2519. }
  2520. # ------------------------------------------------------------------------------
  2521. # check if $peer is covered by $allowed (eg. 10.1.2.3 is included in 10.0.0.0/8)
  2522. # 1:peer address 2:allowed range
  2523. # ------------------------------------------------------------------------------
  2524. sub ESPEasy_isPeerAllowed($$)
  2525. {
  2526. my ($peer,$allowed) = @_;
  2527. return $allowed if $allowed =~ m/^[01]$/;
  2528. #return 1 if $allowed =~ /^0.0.0.0\/0(.0.0.0)?$/; # not necessary but faster
  2529. my $binPeer = ESPEasy_ip2bin($peer);
  2530. my @a = split(/,| /,$allowed);
  2531. foreach (@a) {
  2532. return 1 if $peer =~ m/^$_$/; # a regexp is been used
  2533. next if !ESPEasy_isIPv64Range($_); # needed for combinedDevices
  2534. my ($addr,$ip,$mask) = ESPEasy_addrToCIDR($_);
  2535. return 0 if !defined $ip || !defined $mask; # return if ip or mask !guilty
  2536. my $binAllowed = ESPEasy_ip2bin($addr);
  2537. my $binPeerCut = substr($binPeer,0,$mask);
  2538. return 1 if ($binAllowed eq $binPeerCut);
  2539. }
  2540. return 0;
  2541. }
  2542. # ------------------------------------------------------------------------------
  2543. # convert IPv64 address to binary format and return network part of binary, only
  2544. # ------------------------------------------------------------------------------
  2545. sub ESPEasy_ip2bin($)
  2546. {
  2547. my ($addr) = @_;
  2548. my ($ip,$mask) = split("/",$addr);
  2549. my @bin;
  2550. if (ESPEasy_isIPv4($ip)) {
  2551. $mask = 32 if !defined $mask;
  2552. @bin = map substr(unpack("B32",pack("N",$_)),-8), split(/\./,$ip);
  2553. }
  2554. elsif (ESPEasy_isIPv6($ip)) {
  2555. $ip = ESPEasy_expandIPv6($ip);
  2556. $mask = 128 if !defined $mask;
  2557. @bin = map {unpack('B*',pack('H*',$_))} split(/:/, $ip);
  2558. }
  2559. else {
  2560. return undef;
  2561. }
  2562. my $bin = join('', @bin);
  2563. my $binMask = substr($bin, 0, $mask);
  2564. return $binMask; # return network part of $bin
  2565. }
  2566. # ------------------------------------------------------------------------------
  2567. # expand IPv6 address to 8 full blocks
  2568. # Advantage of IO::Socket : already installed and it seems to be the fastest way
  2569. # http://stackoverflow.com/questions/4800691/perl-ipv6-address-expansion-parsing
  2570. # ------------------------------------------------------------------------------
  2571. sub ESPEasy_expandIPv6($)
  2572. {
  2573. my ($ipv6) = @_;
  2574. use Socket qw(inet_pton AF_INET6);
  2575. return join(":", unpack("H4H4H4H4H4H4H4H4",inet_pton(AF_INET6, $ipv6)));
  2576. }
  2577. # ------------------------------------------------------------------------------
  2578. # convert IPv64 address or range into CIDR notion
  2579. # return undef if addreess or netmask is not valid
  2580. # ------------------------------------------------------------------------------
  2581. sub ESPEasy_addrToCIDR($)
  2582. {
  2583. my ($addr) = @_;
  2584. my ($ip,$mask) = split("/",$addr);
  2585. # no nm specified
  2586. return (ESPEasy_isIPv4($ip) ? ("$ip/32",$ip,32) : ("$ip/128",$ip,128)) if !defined $mask;
  2587. # netmask is already in CIDR format and all values are valid
  2588. return ("$ip/$mask",$ip,$mask)
  2589. if (ESPEasy_isIPv4($ip) && ESPEasy_isNmCIDRv4($mask))
  2590. || (ESPEasy_isIPv6($ip) && ESPEasy_isNmCIDRv6($mask));
  2591. $mask = ESPEasy_dottedNmToCIDR($mask);
  2592. return (undef,undef,undef) if !defined $mask;
  2593. return ("$ip/$mask",$ip,$mask);
  2594. }
  2595. # ------------------------------------------------------------------------------
  2596. # convert dotted decimal netmask to CIDR format
  2597. # return undef if nm is not in dotted decimal format
  2598. # ------------------------------------------------------------------------------
  2599. sub ESPEasy_dottedNmToCIDR($)
  2600. {
  2601. my ($mask) = @_;
  2602. return undef if !ESPEasy_isNmDotted($mask);
  2603. # dotted decimal to CIDR
  2604. my ($byte1, $byte2, $byte3, $byte4) = split(/\./, $mask);
  2605. my $num = ($byte1 * 16777216) + ($byte2 * 65536) + ($byte3 * 256) + $byte4;
  2606. my $bin = unpack("B*", pack("N", $num));
  2607. my $count = ($bin =~ tr/1/1/);
  2608. return $count; # return number of netmask bits
  2609. }
  2610. # ------------------------------------------------------------------------------
  2611. sub ESPEasy_isIPv4($)
  2612. {
  2613. return 0 if !defined $_[0];
  2614. return 1 if($_[0]
  2615. =~ m/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/);
  2616. return 0;
  2617. }
  2618. # ------------------------------------------------------------------------------
  2619. sub ESPEasy_isIPv6($)
  2620. {
  2621. return 0 if !defined $_[0];
  2622. return 1 if ($_[0]
  2623. =~ m/^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/);
  2624. return 0;
  2625. }
  2626. # ------------------------------------------------------------------------------
  2627. sub ESPEasy_isIPv64($)
  2628. {
  2629. return 0 if !defined $_[0];
  2630. return 1 if ESPEasy_isIPv4($_[0]) || ESPEasy_isIPv6($_[0]);
  2631. return 0;
  2632. }
  2633. # ------------------------------------------------------------------------------
  2634. sub ESPEasy_isNmDotted($)
  2635. {
  2636. return 0 if !defined $_[0];
  2637. return 1 if ($_[0]
  2638. =~ m/^(255|254|252|248|240|224|192|128|0)\.0\.0\.0|255\.(255|254|252|248|240|224|192|128|0)\.0\.0|255\.255\.(255|254|252|248|240|224|192|128|0)\.0|255\.255\.255\.(255|254|252|248|240|224|192|128|0)$/);
  2639. return 0;
  2640. }
  2641. # ------------------------------------------------------------------------------
  2642. sub ESPEasy_isNmCIDRv4($)
  2643. {
  2644. return 0 if !defined $_[0];
  2645. return 1 if ($_[0] =~ m/^([0-2]?[0-9]|3[0-2])$/);
  2646. return 0;
  2647. }
  2648. # ------------------------------------------------------------------------------
  2649. sub ESPEasy_isNmCIDRv6($)
  2650. {
  2651. return 0 if !defined $_[0];
  2652. return 1 if ($_[0] =~ m/^([0-9]?[0-9]|1([0-1][0-9]|2[0-8]))$/);
  2653. return 0;
  2654. }
  2655. # ------------------------------------------------------------------------------
  2656. sub ESPEasy_isFqdn($)
  2657. {
  2658. return 0 if !defined $_[0];
  2659. return 1 if ($_[0]
  2660. =~ m/^(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{1,63}(?<!-)\.)+[a-zA-Z]{2,63}$)$/);
  2661. return 0;
  2662. }
  2663. # ------------------------------------------------------------------------------
  2664. sub ESPEasy_isHostname($)
  2665. {
  2666. return 0 if !defined $_[0];
  2667. return 1 if ($_[0] =~ m/^([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/)
  2668. && !(ESPEasy_isIPv4($_[0]) || ESPEasy_isIPv6($_[0]));
  2669. return 0;
  2670. }
  2671. # ------------------------------------------------------------------------------
  2672. # get accurate time
  2673. # ------------------------------------------------------------------------------
  2674. sub ESPEasy_timeStamp() {
  2675. my ($s,$ms) = gettimeofday();
  2676. return "$s.$ms";
  2677. }
  2678. # ------------------------------------------------------------------------------
  2679. sub ESPEasy_dumpSingleLine($)
  2680. {
  2681. my $saveIndent = $Data::Dumper::Indent; my $saveTerse = $Data::Dumper::Terse ;
  2682. $Data::Dumper::Indent = 0; $Data::Dumper::Terse = 1;
  2683. my $ret = Dumper($_[0]);
  2684. $Data::Dumper::Indent = $saveIndent; $Data::Dumper::Terse = $saveTerse;
  2685. return $ret;
  2686. }
  2687. 1;
  2688. =pod
  2689. =item device
  2690. =item summary Control and access to ESPEasy (Espressif ESP8266/ESP32 WLAN-SoC)
  2691. =item summary_DE Steuerung und Zugriff auf ESPEasy (Espressif ESP8266/ESP32 WLAN-SoC)
  2692. =begin html
  2693. <a name="ESPEasy"></a>
  2694. <h3>ESPEasy</h3>
  2695. <ul>
  2696. <p>Provides access and control to Espressif ESP8266/ESP32 WLAN-SoC w/ ESPEasy</p>
  2697. Notes:
  2698. <ul>
  2699. <li>You have to define a bridge device before any logical device can be
  2700. (automatically) defined.
  2701. </li>
  2702. <li>You have to configure your ESP to use "FHEM HTTP" controller protocol.
  2703. Furthermore ESP Easy controller IP must match FHEM's IP address. ESP controller
  2704. port and the FHEM ESPEasy bridge port must be the same.
  2705. </li>
  2706. <li>
  2707. Max. 2 ESPEasy bridges can be defined in the same FHEM instance: 1 for IPv4 and 1 for IPv6
  2708. </li>
  2709. <li>Further information about this module is available here:
  2710. <a href="https://forum.fhem.de/index.php/topic,55728.0.html">Forum #55728</a>
  2711. or in this <a href="https://wiki.fhem.de/wiki/ESPEasy">wiki article</a>.
  2712. </li>
  2713. <li>For security reasons: if one or more of your ESPEasy device uses a
  2714. public IP address then you have to enable this explicitly or the device(s)
  2715. will be ignored/rejected:
  2716. </li>
  2717. <ul>
  2718. <li>
  2719. Enable all ESPEasy device IP addresses/subnets/regexs with the help of
  2720. bridge attributes
  2721. <a href="#ESPEasy_bridge_attr_allowedips">allowedIPs</a> /
  2722. <a href="#ESPEasy_bridge_attr_deniedips">deniedIPs</a>.
  2723. </li>
  2724. <li>
  2725. Enable authentication: see attribute
  2726. <a href="#ESPEasy_bridge_attr_authentication">authentication</a> and
  2727. bridge set <a href="#ESPEasy_bridge_set_user">user</a>
  2728. / <a href="#ESPEasy_bridge_set_pass">pass</a> commands.
  2729. </li>
  2730. </ul>
  2731. <br>
  2732. </ul>
  2733. Requirements:
  2734. <ul>
  2735. <li>
  2736. ESPEasy build &gt;= <a href="https://github.com/ESP8266nu/ESPEasy"
  2737. target="_new">R128</a> (self compiled) or an ESPEasy precompiled image
  2738. &gt;= <a href="http://www.letscontrolit.com/wiki/index.php/ESPEasy#Loading_firmware" target="_new">R140_RC3</a><br>
  2739. </li>
  2740. <li>perl module JSON<br>
  2741. Use "cpan install JSON" or operating system's package manager to install
  2742. Perl JSON Modul. Depending on your os the required package is named:
  2743. libjson-perl or perl-JSON.
  2744. </li>
  2745. </ul>
  2746. <h4>ESPEasy Bridge</h4>
  2747. <a name="ESPEasy_bridge_define"></a>
  2748. <b>Define </b>(bridge)<br><br>
  2749. <ul>
  2750. <code>define &lt;name&gt; ESPEasy bridge &lt;[IPV6:]port&gt;</code><br><br>
  2751. <li>
  2752. <a name=""><code>&lt;name&gt;</code></a><br>
  2753. Specifies a device name of your choise.<br>
  2754. example: <code>ESPBridge</code>
  2755. </li><br>
  2756. <li>
  2757. <a name=""><code>&lt;port&gt;</code></a><br>
  2758. Specifies TCP port for incoming ESPEasy http requests. This port must
  2759. <u>not</u> be used by any other application or daemon on your system and
  2760. must be in the range 1024..65535 unless you run your FHEM installation
  2761. with root permissions (not recommanded).<br>
  2762. If you want to define an IPv4 and an IPv6 bridge on the same TCP port
  2763. (recommanded) then it might be necessary on (some?) Linux
  2764. distributions to activate IPV6_V6ONLY socket option. Use <code>"echo
  2765. 1>/proc/sys/net/ipv6/bindv6only"</code> or systemctl for that purpose.<br>
  2766. eg. <code>8383</code><br>
  2767. eg. <code>IPV6:8383</code><br>
  2768. Example:<br>
  2769. <code>define ESPBridge ESPEasy bridge 8383</code></li><br>
  2770. </ul>
  2771. <br><a name="ESPEasy_bridge_get"></a>
  2772. <b>Get </b>(bridge)<br><br>
  2773. <ul>
  2774. <li><a name="ESPEasy_bridge_get_reading">&lt;reading&gt;</a><br>
  2775. returns the value of the specified reading</li>
  2776. <br>
  2777. <li><a name="ESPEasy_bridge_get_queueSize">queueSize</a><br>
  2778. returns number of entries for currently used queue.
  2779. </li><br>
  2780. <li><a name="ESPEasy_bridge_get_queueContent">queueContent</a><br>
  2781. returns queues content.
  2782. <ul>
  2783. <li>arguments: <code>IP address</code> (can be a regex or omitted to display all queues)</li>
  2784. </ul>
  2785. </li><br>
  2786. <li><a name="ESPEasy_bridge_get_user">user</a><br>
  2787. returns username used by basic authentication for incoming requests.
  2788. </li><br>
  2789. <li><a name="ESPEasy_bridge_get_pass">pass</a><br>
  2790. returns password used by basic authentication for incoming requests.
  2791. </li><br>
  2792. </ul>
  2793. <br><a name="ESPEasy_bridge_set"></a>
  2794. <b>Set </b>(bridge)<br><br>
  2795. <ul>
  2796. <li><a name="ESPEasy_bridge_set_active">active</a><br>
  2797. Activates the current device if it was set
  2798. <a href="#ESPEasy_bridge_set_inactive">inactive</a> before.
  2799. Set active/inactive will be mostly used in scripts without the side
  2800. effect that the 'red question mark' will be displayed in FHEMWEB that
  2801. indicates unsaved configuration changes.
  2802. If attribute <a href="ESPEasy_bridge_attr_disable"">disabled</a> is
  2803. enabled (set to '1') then this set command will be ignored.
  2804. </li><br>
  2805. <li><a name="ESPEasy_bridge_set_inactive">inactive</a><br>
  2806. Opposite of set command <a href="#ESPEasy_bridge_set_active">activate</a>
  2807. </li><br>
  2808. <li><a name="ESPEasy_bridge_set_clearqueue">clearQueue</a><br>
  2809. Used to erase all command queues.<br>
  2810. required value: <code>&lt;none&gt;</code><br>
  2811. eg. : <code>set ESPBridge clearQueue</code></li><br>
  2812. <li><a name="ESPEasy_bridge_set_help">help</a><br>
  2813. Shows set command usage<br>
  2814. required values: <code>help|pass|user|clearQueue</code></li><br>
  2815. <li><a name="ESPEasy_bridge_set_pass">pass</a><br>
  2816. Specifies password used by basic authentication for incoming requests.<br>
  2817. Note that attribute <a href="#ESPEasy_bridge_attr_authentication">authentication</a>
  2818. must be set to enable basic authentication, too.<br>
  2819. required value: <code>&lt;password&gt;</code><br>
  2820. eg. : <code>set ESPBridge pass secretpass</code></li><br>
  2821. <li><a name="ESPEasy_bridge_set_reopen">reopen</a><br>
  2822. Reopen TCP/IP server port for incoming connections from ESPs.</li><br>
  2823. <li><a name="ESPEasy_bridge_set_user">user</a><br>
  2824. Specifies username used by basic authentication for incoming requests.<br>
  2825. Note that attribute <a href="#ESPEasy_bridge_attr_authentication">authentication</a>
  2826. must be set to enable basic authentication, too.<br>
  2827. required value: <code>&lt;username&gt;</code><br>
  2828. eg. : <code>set ESPBridge user itsme</code></li><br>
  2829. </ul>
  2830. <br><a name="ESPEasy_bridge_attr"></a>
  2831. <b>Attributes </b>(bridge)<br><br>
  2832. <ul>
  2833. <li><a name="ESPEasy_bridge_attr_allowedips">allowedIPs</a><br>
  2834. Used to limit IPs or IP ranges of ESPs which are allowed to commit data.
  2835. <br>
  2836. Specify IP, IP/netmask, regexp or a comma separated list of these values.
  2837. Netmask can be written as bitmask or dotted decimal. Domain names for dns
  2838. lookups are not supported.<br>
  2839. Possible values: IPv64 address, IPv64/netmask, regexp<br>
  2840. Default: 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16,
  2841. fe80::/10, fc00::/7, ::1
  2842. <br><br>
  2843. Examles:<br>
  2844. <table><tr><td>
  2845. 10.68.30.147
  2846. </td><td>
  2847. =&gt; IPv4 single address
  2848. </td></tr><tr><td>
  2849. 10.68.30.0/25
  2850. </td><td>
  2851. =&gt; IPv4 CIDR network 192.168.30.0-127
  2852. </td></tr><tr><td>
  2853. 10.68.30.8/255.255.248.0
  2854. </td><td>
  2855. =&gt; IPv4 CIDR network 192.168.30.8-15
  2856. </td></tr><tr><td>
  2857. 192.168.30.1([0-4][0-9]|50)
  2858. </td><td>
  2859. =&gt; IPv4 range w/ regexp: 192.168.30.100-150
  2860. </td></tr><tr><td>
  2861. 2001:1a59:50a9::aaaa
  2862. </td><td>
  2863. =&gt; IPv6 single address
  2864. </td></tr><tr><td>
  2865. 2001:1a59:50a9::/48
  2866. </td><td>
  2867. =&gt; IPv6 network 2001:1a59:50a9::/48
  2868. </td></tr><tr><td>
  2869. 2001:1a59:50a9::01[0-4][0-9]
  2870. </td><td>
  2871. =&gt; IPv6 range w/ regexp: 2001:1a59:50a9::0100-0149
  2872. </tr></td>
  2873. </table>
  2874. <span style="font-size:small;">Note that short IPv6 notation (::) must be
  2875. used in conjunction with regexps.</span>
  2876. </li><br>
  2877. <li><a name="ESPEasy_bridge_attr_authentication">authentication</a><br>
  2878. Used to enable basic authentication for incoming requests.<br>
  2879. Note that user, pass and authentication attribute must be set to activate
  2880. basic authentication<br>
  2881. Possible values: 0,1<br>
  2882. Default: 0</li><br>
  2883. <li><a name="ESPEasy_bridge_attr_autocreate">autocreate</a><br>
  2884. Used to overwrite global autocreate setting.<br>
  2885. Global autocreate setting will be detected by global attribut
  2886. 'autoload_undefined_devices'<br>
  2887. Possible values: 0,1<br>
  2888. Default: 0 (disabled)</li><br>
  2889. <li><a name="ESPEasy_bridge_attr_autosave">autosave</a><br>
  2890. Used to overwrite global autosave setting.<br>
  2891. Global autosave setting will be detected by global attribut 'autosave'.
  2892. <br>
  2893. Possible values: 0,1<br>
  2894. Default: 0 (disabled)</li><br>
  2895. <li><a name="ESPEasy_bridge_attr_combinedevices">combineDevices</a><br>
  2896. Used to gather all ESP devices of a single ESP into 1 FHEM device even if
  2897. different ESP devices names are used.<br>
  2898. Possible values: 0, 1, IPv64 address, IPv64/netmask, ESPname or a comma
  2899. separated list consisting of these values.<br>
  2900. Netmasks can be written as bitmask or dotted decimal. Domain names for dns
  2901. lookups are not supported.<br>
  2902. Default: 0 (disabled for all ESPs)<br>
  2903. Eg. 1 (globally enabled)<br>
  2904. Eg. ESP01,ESP02<br>
  2905. Eg. 10.68.30.1,10.69.0.0/16,ESP01,2002:1a59:50a9::/48</li><br>
  2906. <li><a name="ESPEasy_bridge_attr_deniedips">deniedIPs</a><br>
  2907. Used to define IPs or IP ranges of ESPs which are denied to commit data.
  2908. <br>
  2909. Syntax see <a href="#ESPEasy_bridge_attr_allowedips">allowedIPs</a>.<br>
  2910. This attribute will overwrite any IP or range defined by
  2911. <a href="#ESPEasy_bridge_attr_allowedips">allowedIPs</a>.<br>
  2912. Default: none (no IPs are denied)</li><br>
  2913. <li><a name="ESPEasy_bridge_attr_disable">disable</a><br>
  2914. Used to disable device. <a href="#ESPEasy_bridge_set_inactive">inactive</a>
  2915. state will be overwritten.<br>
  2916. Possible values: 0,1<br>
  2917. Default: 0 (eanble)</li><br>
  2918. <li><a name="ESPEasy_bridge_attr_httpreqtimeout">httpReqTimeout</a><br>
  2919. Specifies seconds to wait for a http answer from ESP8266 device.<br>
  2920. Possible values: 4..60<br>
  2921. Default: 10 seconds</li><br>
  2922. <li><a name="ESPEasy_bridge_attr_maxhttpsessions">maxHttpSessions</a><br>
  2923. Limit maximal concurrent outgoing http sessions to a single ESP.<br>
  2924. Set to 0 to disable this feature. At the moment (ESPEasy R147) it seems
  2925. to be possible to send 5 "concurrent" requests if nothing else keeps the
  2926. esp working.<br>
  2927. Possible values: 0..9<br>
  2928. Default: 3</li><br>
  2929. <li><a name="ESPEasy_bridge_attr_maxqueuesize">maxQueueSize</a><br>
  2930. Limit maximal queue size (number of commands in queue) for outgoing http
  2931. requests.<br>
  2932. If command queue size is reached (eg. ESP is offline) any further
  2933. command will be ignored and discard.<br>
  2934. Possible values: >10<br>
  2935. Default: 250</li><br>
  2936. <li><a name="ESPEasy_bridge_attr_resendfailedcmd">resendFailedCmd</a><br>
  2937. Used to define number of command resends to the ESP if there is an error
  2938. in transmission on network layer (eg. unreachable wifi device).<br>
  2939. Possible values: a positive number<br>
  2940. Default: 0 (disabled: no resending of commands)</li><br>
  2941. <li><a name="ESPEasy_bridge_attr_uniqids">uniqIDs</a><br>
  2942. This attribute has been removed.</li><br>
  2943. <li><a href="#readingFnAttributes">readingFnAttributes</a>
  2944. </li><br>
  2945. </ul>
  2946. <h4>ESPEasy Device</h4>
  2947. <a name="ESPEasy_device_define"></a>
  2948. <b>Define </b>(logical device)<br><br>
  2949. <ul>
  2950. Note 1: Logical devices will be created automatically if any values are
  2951. received by the bridge device and autocreate is not disabled. If you
  2952. configured your ESP in a way that no data is send independently then you
  2953. have to define logical devices. At least wifi rssi value could be defined
  2954. to use autocreate and presence detection.<br><br>
  2955. <code>define &lt;name&gt; ESPEasy &lt;ip|fqdn&gt; &lt;port&gt; &lt;IODev&gt; &lt;identifier&gt;</code><br><br>
  2956. <li>
  2957. <a name=""><code>&lt;name&gt;</code></a><br>
  2958. Specifies a device name of your choise.<br>
  2959. example: <code>ESPxx</code>
  2960. </li><br>
  2961. <li>
  2962. <a name=""><code>&lt;ip|fqdn&gt;</code></a><br>
  2963. Specifies ESP IP address or hostname.<br>
  2964. example: <code>172.16.4.100</code><br>
  2965. example: <code>espxx.your.domain.net</code>
  2966. </li><br>
  2967. <li>
  2968. <a name=""><code>&lt;port&gt;</code></a><br>
  2969. Specifies http port to be used for outgoing request to your ESP. Should be 80<br>
  2970. example: <code>80</code>
  2971. </li><br>
  2972. <li>
  2973. <a name=""><code>&lt;IODev&gt;</code></a><br>
  2974. Specifies your ESP bridge device. See above.<br>
  2975. example: <code>ESPBridge</code>
  2976. </li><br>
  2977. <li>
  2978. <a name=""><code>&lt;identifier&gt;</code></a><br>
  2979. Specifies an identifier that will bind your ESP to this device.<br>
  2980. This identifier must be specified in this form:<br>
  2981. &lt;esp name&gt;_&lt;esp device name&gt;.<br>
  2982. If bridge attribute <a href="#ESPEasy_bridge_attr_combinedevices">combineDevices</a> is used then &lt;esp name&gt; is used, only.<br>
  2983. ESP name and device name can be found here:<br>
  2984. &lt;esp name&gt;: =&gt; ESP GUI =&gt; Config =&gt; Main Settings =&gt; Name<br>
  2985. &lt;esp device name&gt;: =&gt; ESP GUI =&gt; Devices =&gt; Edit =&gt; Task Settings =&gt; Name<br>
  2986. example: <code>ESPxx_DHT22</code><br>
  2987. example: <code>ESPxx</code>
  2988. </li><br>
  2989. <li>Example:<br>
  2990. <code>define ESPxx ESPEasy 172.16.4.100 80 ESPBridge EspXX_SensorXX</code>
  2991. </li><br>
  2992. </ul>
  2993. <br><a name="ESPEasy_device_get"></a>
  2994. <b>Get </b>(logical device)<br><br>
  2995. <ul>
  2996. <li><a name="ESPEasy_device_get_adminpassword">adminPassword</a><br>
  2997. returns the admin password. For details see
  2998. <a href="#ESPEasy_device_set_adminpassword">set adminPassword</a>
  2999. </li><br>
  3000. <li><a name="ESPEasy_device_get_pinmap">pinMap</a><br>
  3001. returns possible alternative pin names that can be used in commands
  3002. </li><br>
  3003. <li><a name="ESPEasy_device_get_setcmds">setCmds</a><br>
  3004. returns formatted table of registered ESP commands/mappings.
  3005. </li><br>
  3006. </ul>
  3007. <br><a name="ESPEasy_device_set"></a>
  3008. <b>Set </b>(logical device)
  3009. <br><br>
  3010. <ul>
  3011. Notes:<br>
  3012. - Commands are case insensitive.<br>
  3013. - Users of Wemos D1 mini or NodeMCU can use Arduino pin names instead of
  3014. GPIO numbers.<br>
  3015. &nbsp;&nbsp;D1 =&gt; GPIO5, D2 =&gt; GPIO4, ...,TX =&gt; GPIO1 (see: get
  3016. <a href="#ESPEasy_bridge_get_pinmap">pinMap</a>)<br>
  3017. - low/high state can be written as 0/1 or on/off
  3018. <br><br>
  3019. <b>ESPEasy module internal commands:</b><br><br>
  3020. <li><a name="ESPEasy_device_set_active">active</a><br>
  3021. Works in the same way as bridge set command <a href="#ESPEasy_device_set_active">active</a>.
  3022. </li><br>
  3023. <li><a name="ESPEasy_device_set_inactive">inactive</a><br>
  3024. Works in the same way as bridge set command <a href="#ESPEasy_device_set_inactive">inactive</a>.
  3025. </li><br>
  3026. <li><a name="ESPEasy_device_set_adminpassword">adminPassword</a><br>
  3027. The ESP Easy 'Admin Password" is used to protect some ESP Easy commands
  3028. against unauthorized access. When this feature is enabled on your ESPs
  3029. you should deposit this password. If an ESP Easy command will require this
  3030. authorization the password will be sent to the ESP. Keep in mind that this
  3031. feature works quite slow on your ESP Easy nodes.
  3032. </li><br>
  3033. <li><a name="ESPEasy_device_set_clearreadings">clearReadings</a><br>
  3034. Delete all readings that are auto created by received sensor values
  3035. since last FHEM restart.<br>
  3036. <ul>
  3037. <li>arguments: <code>none</code></li>
  3038. <li>example: set &lt;esp&gt; clearReadings</li>
  3039. </ul>
  3040. </li><br>
  3041. <li><a name="ESPEasy_device_set_help">help</a><br>
  3042. Shows set command usage.<br>
  3043. <ul>
  3044. <li>arguments: <code>&lt;a valid set command&gt;</code></li>
  3045. <li>example: <code>set &lt;esp&gt; help gpio</code></li>
  3046. </ul>
  3047. </li><br>
  3048. <li><a name="ESPEasy_device_set_raw">raw</a><br>
  3049. Can be used for own ESP plugins or new ESPEasy commands that are not
  3050. considered by this module at the moment. Any argument will be sent
  3051. directly to the ESP. Used URL is: "/control?cmd="
  3052. <ul>
  3053. <li>arguments: raw &lt;cmd&gt; [&lt;arg1&gt;] [&lt;arg2&gt;] [&lt;...&gt;]</li>
  3054. <li>example: set &lt;esp&gt; raw myCommand p1 p2 p3</li>
  3055. </ul>
  3056. </li><br>
  3057. <li><a name="ESPEasy_device_set_rawsystem">rawsystem</a><br>
  3058. The same as set command <a href="">raw</a> but this command uses the URL
  3059. "/?cmd=" (command.ino) instead of "/control?cmd=" (ESPEasy plugins).
  3060. </li><br>
  3061. <li><a name="ESPEasy_device_set_statusrequest">statusRequest</a><br>
  3062. Trigger a statusRequest for configured GPIOs (see attribut pollGPIOs)
  3063. and do a presence check<br>
  3064. <ul>
  3065. <li>arguments: <code>n/a</code></li>
  3066. <li>example: <code>set &lt;esp&gt; statusRequest</code></li>
  3067. </ul><br>
  3068. </li><br>
  3069. <i><b>Note:</b> The following commands are built-in ESPEasy Software commands
  3070. that are send directly to the ESP after passing a syntax check and more...
  3071. A detailed description can be found here:
  3072. <a href="http://www.letscontrolit.com/wiki/index.php/ESPEasy_Command_Reference"
  3073. target="_NEW">ESPEasy Command Reference</a></i><br><br>
  3074. <b>ESP Easy generic I/O commands:</b><br><br>
  3075. <li><a name="ESPEasy_device_set_gpio">GPIO</a><br>
  3076. Switch output pins to high/low<br>
  3077. <ul>
  3078. <li>arguments: <code>&lt;pin&gt; &lt;0|1|off|on&gt;</code></li>
  3079. <li>example: <code>set &lt;esp&gt; gpio 14 on</code></li>
  3080. </ul>
  3081. </li><br>
  3082. <li><a name="ESPEasy_device_set_pwm">PWM</a><br>
  3083. Direct PWM control of output pins<br>
  3084. <ul>
  3085. <li>arguments: <code>&lt;pin&gt; &lt;level&gt;</code></li>
  3086. <li>example: <code>set &lt;esp&gt; pwm 14 512</code></li>
  3087. </ul>
  3088. </li><br>
  3089. <li><a name="ESPEasy_device_set_pwmfade">PWMFADE</a><br>
  3090. Fade output pins to a pwm value<br>
  3091. <ul>
  3092. <li>arguments: <code>&lt;pin&gt; &lt;target pwm&gt; &lt;duration 1-30s&gt;</code></li>
  3093. <li>example: <code>set &lt;esp&gt; pwmfade 14 1023 10</code></li>
  3094. </ul>
  3095. </li><br>
  3096. <li><a name="ESPEasy_device_set_pulse">Pulse</a><br>
  3097. Direct pulse control of output pins<br>
  3098. <ul>
  3099. <li>arguments: <code>&lt;pin&gt; &lt;0|1|off|on&gt; &lt;duration&gt;</code></li>
  3100. <li>example: <code>set &lt;esp&gt; pulse 14 on 10</code></li>
  3101. </ul>
  3102. </li><br>
  3103. <li><a name="ESPEasy_device_set_logpulse">LongPulse</a><br>
  3104. Direct pulse control of output pins (duration in s)<br>
  3105. <ul>
  3106. <li>arguments: <code>&lt;pin&gt; &lt;0|1|off|on&gt; &lt;duration&gt;</code></li>
  3107. <li>example: <code>set &lt;esp&gt; longpulse 14 on 10</code></li>
  3108. </ul>
  3109. </li><br>
  3110. <li><a name="ESPEasy_device_set_logpulse_ms">LongPulse_ms</a><br>
  3111. Direct pulse control of output pins (duration in ms)<br>
  3112. <ul>
  3113. <li>arguments: <code>&lt;pin&gt; &lt;0|1|off|on&gt; &lt;duration&gt;</code></li>
  3114. <li>example: <code>set &lt;esp&gt; longpulse_ms 14 on 10000</code></li>
  3115. </ul>
  3116. </li><br>
  3117. <li><a name="ESPEasy_device_set_pcfgpio">PCFGpio</a><br>
  3118. Control PCF8574 (8-bit I/O expander for I2C-bus)<br>
  3119. <ul>
  3120. <li>arguments: <code>&lt;port&gt; &lt;0|1|off|on&gt;</code></li>
  3121. <li>example: <code>set &lt;esp&gt; PCFGpio 128 on</code></li>
  3122. </ul>
  3123. Port numbering see:
  3124. <a href="https://www.letscontrolit.com/wiki/index.php/PCF8574#Input">
  3125. ESPEasy Wiki PCF8574</a>
  3126. </li><br>
  3127. <li><a name="ESPEasy_device_set_pcfpulse">PCFPulse</a><br>
  3128. Control PCF8574 (8-bit I/O expander for I2C-bus)
  3129. <ul>
  3130. <li>arguments: <code>&lt;port&gt; &lt;0|1|off|on&gt; &lt;duration&gt;</code></li>
  3131. <li>example: <code>set &lt;esp&gt; PCFPulse 128 on 10</code></li>
  3132. </ul>
  3133. Port numbering see:
  3134. <a href="https://www.letscontrolit.com/wiki/index.php/PCF8574#Input">
  3135. ESPEasy Wiki PCF8574</a>
  3136. </li><br>
  3137. <li><a name="ESPEasy_device_set_pcflongpulse">PCFLongPulse</a><br>
  3138. Control on PCF8574 (8-bit I/O expander for I2C-bus)
  3139. <ul>
  3140. <li>arguments: <code>&lt;port&gt; &lt;0|1|off|on&gt; &lt;duration&gt;</code></li>
  3141. <li>example: <code>set &lt;esp&gt; PCFLongPulse 128 on 10</code></li>
  3142. </ul>
  3143. Port numbering see:
  3144. <a href="https://www.letscontrolit.com/wiki/index.php/PCF8574#Input">
  3145. ESPEasy Wiki PCF8574</a>
  3146. </li><br>
  3147. <li><a name="ESPEasy_device_set_mcpgpio">MCPGPIO</a><br>
  3148. Control MCP23017 output pins (16-Bit I/O Expander with Serial Interface)<br>
  3149. <ul>
  3150. <li>arguments: <code>&lt;port&gt; &lt;0|1|off|on&gt;</code></li>
  3151. <li>example: <code>set &lt;esp&gt; MCPGPIO 48 on</code></li>
  3152. </ul>
  3153. Port numbering see:
  3154. <a href="https://www.letscontrolit.com/wiki/index.php/MCP23017#Input">
  3155. ESPEasy Wiki MCP23017</a>
  3156. </li><br>
  3157. <li><a name="ESPEasy_device_set_mcppulse">MCPPulse</a><br>
  3158. Pulse control on MCP23017 output pins (duration in ms)<br>
  3159. <ul>
  3160. <li>arguments: <code>&lt;port&gt; &lt;0|1|off|on&gt; &lt;duration&gt;</code></li>
  3161. <li>example: <code>set &lt;esp&gt; MCPPulse 48 on 100</code></li>
  3162. </ul>
  3163. </li><br>
  3164. <li><a name="ESPEasy_device_set_mcplongpulse">MCPLongPulse</a><br>
  3165. Longpulse control on MCP23017 output pins (duration in s)<br>
  3166. <ul>
  3167. <li>arguments: <code>&lt;port&gt; &lt;0|1|off|on&gt; &lt;duration&gt;</code></li>
  3168. <li>example: <code>set &lt;esp&gt; MCPLongPulse 48 on 2</code></li>
  3169. </ul>
  3170. </li><br>
  3171. <li><a name="ESPEasy_device_set_pcapwm">pcapwm</a><br>
  3172. Control PCA9685 (16-channel / 12-bit PWM I2C-bus controller)<br>
  3173. <ul>
  3174. <li>arguments: <code>&lt;pin 0-15&gt; &lt;level 0-4095&gt;</code></li>
  3175. <li>example: <code>set &lt;esp&gt; pcapwm 15 4095</code></li>
  3176. </ul>
  3177. </li><br>
  3178. <b>ESP Easy motor control commands:</b><br><br>
  3179. <li><a name="ESPEasy_device_set_servo">Servo</a><br>
  3180. Direct control of servo motors<br>
  3181. <ul>
  3182. <li>arguments: <code>&lt;servoNo&gt; &lt;pin&gt; &lt;position&gt;</code></li>
  3183. <li>example: <code>set &lt;esp&gt; servo 1 14 100</code></li>
  3184. </ul>
  3185. </li><br>
  3186. <li><a name="ESPEasy_device_set_motorshieldcmd">MotorShieldCMD</a><br>
  3187. Control a DC motor or stepper<br>
  3188. <ul>
  3189. <li>
  3190. arguments: <code>DCMotor &lt;motornumber&gt; &lt;forward|backward|release&gt; &lt;speed&gt;</code><br>
  3191. arguments: <code>Stepper &lt;motornumber&gt; &lt;forward|backward|release&gt; &lt;steps&gt; &lt;single|double|interleave|microstep&gt;</code>
  3192. </li>
  3193. <li>
  3194. example: <code>set &lt;esp&gt; MotorShieldCMD DCMotor 1 forward 10</code><br>
  3195. example: <code>set &lt;esp&gt; MotorShieldCMD Stepper 1 backward 25 single</code>
  3196. </li>
  3197. </ul>
  3198. </li><br>
  3199. <b>ESP Easy display related commands:</b><br><br>
  3200. <li><a name="ESPEasy_device_set_lcd">lcd</a><br>
  3201. Write text messages to LCD screen<br>
  3202. Pay attention to attributes
  3203. <a href="#ESPEasy_device_attr_displaytextencode">displayTextEncode</a> and
  3204. <a href="#ESPEasy_device_attr_displaytextwidth">displayTextWidth</a>.<br>
  3205. <ul>
  3206. <li>arguments: <code>&lt;row&gt; &lt;col&gt; &lt;text&gt;</code></li>
  3207. <li>example: <code>set &lt;esp&gt; lcd 1 1 Test a b c</code></li>
  3208. </ul>
  3209. </li><br>
  3210. <li><a name="ESPEasy_device_set_lcdcmd">lcdcmd</a><br>
  3211. Control LCD screen<br>
  3212. <ul>
  3213. <li>arguments: <code>&lt;on|off|clear&gt;</code></li>
  3214. <li>example: <code>set &lt;esp&gt; lcdcmd clear</code></li>
  3215. </ul>
  3216. </li><br>
  3217. <li><a name="ESPEasy_device_set_oled">oled</a><br>
  3218. Write text messages to OLED screen<br>
  3219. Pay attention to attributes
  3220. <a href="#ESPEasy_device_attr_displaytextencode">displayTextEncode</a> and
  3221. <a href="#ESPEasy_device_attr_displaytextwidth">displayTextWidth</a>.<br>
  3222. <ul>
  3223. <li>arguments: <code>&lt;row&gt; &lt;col&gt; &lt;text&gt;</code></li>
  3224. <li>example: <code>set &lt;esp&gt; oled 1 1 Test a b c</code></li>
  3225. </ul>
  3226. </li><br>
  3227. <li><a name="ESPEasy_device_set_oledcmd">oledcmd</a><br>
  3228. Control OLED screen<br>
  3229. <ul>
  3230. <li>arguments: <code>&lt;on|off|clear&gt;</code></li>
  3231. <li>example: <code>set &lt;esp&gt; oledcmd clear</code></li>
  3232. </ul>
  3233. </li><br>
  3234. <li><a name="ESPEasy_device_set_oledframedcmd">oledframedcmd</a><br>
  3235. Switch oledframed on/off<br>
  3236. <ul>
  3237. <li>arguments: <code>&lt;on|off&gt;</code></li>
  3238. <li>example: <code>set &lt;esp&gt; oledframedcmd on</code></li>
  3239. </ul>
  3240. </li><br>
  3241. <b>ESP Easy DMX related commands:</b><br><br>
  3242. <li><a name="ESPEasy_device_set_dmx">dmx</a><br>
  3243. Send DMX commands to a device<br>
  3244. <ul>
  3245. <li>arguments: <code>&lt;on|off|log|value|channel=value[,value][...]&gt;</code></li>
  3246. <li>example: <code>set &lt;esp&gt; dmx 1=255,2=127</code></li>
  3247. </ul>
  3248. </li><br>
  3249. <b>ESP Easy LED/Lights related commands:</b><br><br>
  3250. <li><a name="ESPEasy_device_set_lights">Lights</a> (plugin can be found <a
  3251. href="https://github.com/ddtlabs/ESPEasy-Plugin-Lights target="_NEW">here</a>)<br>
  3252. Control a rgb or ct light<br>
  3253. <ul>
  3254. <li>arguments: <code>&lt;rgb|ct|pct|on|off|toggle&gt; [&lt;hex-rgb|color-temp|pct-value&gt;] [&lt;fading time&gt;]</code></li>
  3255. <li>examples:<br>
  3256. <code>set &lt;esp&gt; lights rgb aa00aa</code><br>
  3257. <code>set &lt;esp&gt; lights rgb aa00aa 10</code><br>
  3258. <code>set &lt;esp&gt; lights ct 3200</code><br>
  3259. <code>set &lt;esp&gt; lights ct 3200 10</code><br>
  3260. <code>set &lt;esp&gt; lights pct 50</code><br>
  3261. <code>set &lt;esp&gt; lights on</code><br>
  3262. <code>set &lt;esp&gt; lights off</code><br>
  3263. <code>set &lt;esp&gt; lights toggle</code><br>
  3264. </li>
  3265. </ul>
  3266. </li><br>
  3267. <li><a name="ESPEasy_device_set_nfx">nfx</a> (plugin can be found
  3268. <a target="_blank" href="https://github.com/djcysmic/NeopixelBusFX">here</a>)<br>
  3269. Control nfx plugin. Note: To use FHEMWEB's colorpicker and slider widgets you have to set
  3270. Attribut <a href="ESPEasy_device_attr_maplightscmds">mapLightCmds</a> to nfx.
  3271. <ul>
  3272. <li>arguments: <code>
  3273. <table>
  3274. <tr><td>all</td> <td>&lt;rrggbb&gt; [fadetime] [delay +/-ms]</td></tr>
  3275. <tr><td>bgcolor</td> <td>&lt;rrggbb&gt;</td></tr>
  3276. <tr><td>ct</td> <td>&lt;ct&gt; [fadetime] [pct bri]</td></tr>
  3277. <tr><td>colorfade</td> <td>&lt;rrggbb_start&gt; &lt;rrggbb_end&gt; [startpixel] [endpixel]</td></tr>
  3278. <tr><td>comet</td> <td>&lt;rrggbb&gt; [speed +/- 0-50]</td></tr>
  3279. <tr><td>count</td> <td>&lt;value&gt;</td></tr>
  3280. <tr><td>dim</td> <td>&lt;value 0-255&gt;</td></tr>
  3281. <tr><td>dualscan</td> <td>&lt;rrggbb&gt; [rrggbb background] [speed 0-50]</td></tr>
  3282. <tr><td>fade</td> <td>&lt;rrggbb&gt; [fadetime ms] [delay +/-ms]</td></tr>
  3283. <tr><td>fadedelay</td> <td>&lt;value in +/-ms&gt;</td></tr>
  3284. <tr><td>fadetime</td> <td>&lt;value in ms&gt;</td></tr>
  3285. <tr><td>faketv</td> <td>[startpixel] [endpixel]</td></tr>
  3286. <tr><td>fire</td> <td>[fps] [brightness 0-255] [cooling 20-100] [sparking 50-200]</td></tr>
  3287. <tr><td>kitt</td> <td>&lt;rrggbb&gt; [speed 0-50]</td></tr>
  3288. <tr><td>line</td> <td>&lt;startpixel&gt; &lt;endpixel&gt; &lt;rrggbb&gt;</td></tr>
  3289. <tr><td>off</td> <td>[fadetime] [delay +/-ms]</td></tr>
  3290. <tr><td>on</td> <td>[fadetime] [delay +/-ms]</td></tr>
  3291. <tr><td>one</td> <td>&lt;pixel&gt; &lt;rrggbb&gt;</td></tr>
  3292. <tr><td>pct</td> <td>&lt;pct&gt; [fadetime]</td></tr>
  3293. <tr><td>rainbow</td> <td>[speed +/- 0-50]</td></tr>
  3294. <tr><td>rgb</td> <td>&lt;rrggbb&gt; [fadetime] [delay +/-ms]</td></tr>
  3295. <tr><td>scan</td> <td>&lt;rrggbb&gt; [rrggbb background] [speed 0-50]</td></tr>
  3296. <tr><td>simpleclock</td> <td>[bigtickcolor] [smalltickcolor] [hourcolor] [minutecolor] [secondcolor]</td></tr>
  3297. <tr><td>sparkle</td> <td>&lt;rrggbb&gt; [rrggbb background] [speed 0-50]</td></tr>
  3298. <tr><td>speed</td> <td>&lt;value 0-50&gt;</td></tr>
  3299. <tr><td>stop</td> <td></td></tr>
  3300. <tr><td>theatre</td> <td>&lt;rrggbb&gt; [rrggbb background] [speed +/- 0-50]</td></tr>
  3301. <tr><td>toggle</td> <td>[fadetime]</td></tr>
  3302. <tr><td>twinkle</td> <td>&lt;rrggbb&gt; [rrggbb background] [speed 0-50]</td></tr>
  3303. <tr><td>twinklefade</td> <td>&lt;rrggbb&gt; [number of pixels] [speed 0-50]</td></tr>
  3304. <tr><td>wipe</td> <td>&lt;rrggbb&gt; [rrggbb dot] [speed +/- 0-50]</td></tr>
  3305. </table>
  3306. </code></li>
  3307. <li>examples:<br>
  3308. <code>
  3309. set &lt;esp&gt; nfx all 00ff00 100<br>
  3310. set &lt;esp&gt; nfx rgb aa00ff 1000 10<br>
  3311. set &lt;esp&gt; nfx line 0 100 f0f0f0c<br>
  3312. </code>
  3313. </li>
  3314. <li>examples with attribut mapLightCmds set to nfx:<br>
  3315. <code>
  3316. set &lt;esp&gt; all 00ff00 100<br>
  3317. set &lt;esp&gt; rgb aa00ff 1000 10<br>
  3318. set &lt;esp&gt; line 0 100 f0f0f0c<br>
  3319. </code>
  3320. </li>
  3321. </ul>
  3322. </li><br>
  3323. <li><a name="ESPEasy_device_set_candle">candle</a><br>
  3324. Control candle rgb plugin<br>
  3325. <ul>
  3326. <li>arguments:
  3327. <code>CANDLE:&lt;FlameType&gt;:&lt;Color&gt;:&lt;Brightness&gt;</code></li>
  3328. <li>example: <code>set &lt;esp&gt; CANDLE:4:FF0000:200</code></li>
  3329. </ul>
  3330. </li><br>
  3331. <li><a name="ESPEasy_device_set_neopixel">neopixel</a><br>
  3332. Control neopixel plugin (single LED)<br>
  3333. <ul>
  3334. <li>arguments: <code>&lt;led nr&gt; &lt;red 0-255&gt; &lt;green 0-255&gt; &lt;blue 0-255&gt;</code></li>
  3335. <li>example: <code>set &lt;esp&gt; neopixel 1 255 255 255</code></li>
  3336. </ul>
  3337. </li><br>
  3338. <li><a name="ESPEasy_device_set_neopixelall">neopixelall</a><br>
  3339. Control neopixel plugin (all together)<br>
  3340. <ul>
  3341. <li>arguments: <code>&lt;red 0-255&gt; &lt;green 0-255&gt; &lt;blue 0-255&gt;</code></li>
  3342. <li>example: <code>set &lt;esp&gt; neopixelall 255 255 255</code></li>
  3343. </ul>
  3344. </li><br>
  3345. <li><a name="ESPEasy_device_set_neopixelline">neopixelline</a><br>
  3346. Control neopixel plugin (line)<br>
  3347. <ul>
  3348. <li>arguments: <code>&lt;start led no&gt; &lt;stop led no&gt; &lt;red 0-255&gt; &lt;green 0-255&gt; &lt;blue 0-255&gt;</code></li>
  3349. <li>example: <code>set &lt;esp&gt; neopixelline 1 5 0 127 255</code></li>
  3350. </ul>
  3351. </li><br>
  3352. <b>ESP Easy sound related commands:</b><br><br>
  3353. <li><a name="ESPEasy_device_set_tone">tone</a><br>
  3354. Play a tone on a pin via a speaker or piezo element (ESPEasy &gt;=
  3355. 2.0.0-dev6)
  3356. <br>
  3357. <ul>
  3358. <li>arguments: <code>&lt;pin&gt; &lt;freq Hz&gt; &lt;duration s&gt;</code></li>
  3359. <li>example: <code>set &lt;esp&gt; tone 14 4000 1</code></li>
  3360. </ul>
  3361. </li><br>
  3362. <li><a name="ESPEasy_device_set_rtttl">rtttl</a><br>
  3363. Play melodies via <a target="_NEW"
  3364. href="https://en.wikipedia.org/wiki/Ring_Tone_Transfer_Language#Technical_specification">RTTTL</a>
  3365. (ESPEasy &gt;= 2.0.0-dev6)
  3366. <br>
  3367. <ul>
  3368. <li>arguments: &lt;rtttl&gt; &lt;pin&gt;:&lt;rtttl codes&gt;</li>
  3369. <li>example: <code>set &lt;esp&gt; rtttl 14:d=10,o=6,b=180,c,e,g</code></li>
  3370. </ul>
  3371. </li><br>
  3372. <li><a name="ESPEasy_device_set_buzzer">buzzer</a><br>
  3373. Beep a short time<br>
  3374. <ul>
  3375. <li>arguments: <code>none</code></li>
  3376. <li>example: <code>set &lt;esp&gt; buzzer</code></li>
  3377. </ul>
  3378. </li><br>
  3379. <b>ESP Easy miscellaneous commands:</b><br><br>
  3380. <li><a name="ESPEasy_device_set_irsend">irsend</a><br>
  3381. Send ir codes via "Infrared Transmit" Plugin<br>
  3382. Supported protocols are: NEC, JVC, RC5, RC6, SAMSUNG, SONY, PANASONIC at
  3383. the moment. As long as official documentation is missing you can find
  3384. some details here:
  3385. <a href="http://www.letscontrolit.com/forum/viewtopic.php?f=5&amp;t=328" target="_NEW">
  3386. IR Transmitter thread #1</a> and
  3387. <a
  3388. href="https://www.letscontrolit.com/forum/viewtopic.php?t=328&amp;start=61" target="_NEW">
  3389. IR Transmitter thread #61</a>.<br>
  3390. <ul>
  3391. <li>
  3392. arguments: <code>&lt;NEC|JVC|RC5|RC6|SAMSUNG|SONY|PANASONIC&gt; &lt;hex code&gt; &lt;bit length&gt;</code><br>
  3393. arguments: <code>&lt;RAW&gt; &lt;B32 raw&gt; &lt;frequenz&gt; &lt;pulse length&gt; &lt;blank length&gt;</code>
  3394. </li>
  3395. <li>
  3396. example: <code>set &lt;esp&gt; irsend NEC 7E81542B 32</code><br>
  3397. example: <code>set &lt;esp&gt; irsend RAW 3U0GGL8AGGK588A22K58ALALALAGL1A22LAK45ALALALALALALALALAL1AK5 38 512 256</code>
  3398. </li>
  3399. </ul>
  3400. </li><br>
  3401. <li><a name="ESPEasy_device_set_reboot">reboot</a><br>
  3402. Used to reboot your ESP<br>
  3403. <ul>
  3404. <li>arguments: <code>none</code></li>
  3405. <li>example: <code>set &lt;esp&gt; reboot</code></li>
  3406. </ul>
  3407. </li><br>
  3408. <li><a name="ESPEasy_device_set_serialsend">serialsend</a><br>
  3409. Used for ser2net plugin<br>
  3410. <ul>
  3411. <li>arguments: <code>&lt;string&gt;</code></li>
  3412. <li>example: <code>set &lt;esp&gt; serialsend test</code></li>
  3413. </ul>
  3414. </li><br>
  3415. <b>ESP Easy administrative commands</b> (be careful !!!):<br><br>
  3416. <li><a name="ESPEasy_device_set_erase">erase</a><br>
  3417. Wipe out ESP flash memory<br>
  3418. <ul>
  3419. <li>arguments: <code>none</code></li>
  3420. <li>example: <code>set &lt;esp&gt; erase</code></li>
  3421. </ul>
  3422. </li><br>
  3423. <li><a name="ESPEasy_device_set_reset">reset</a><br>
  3424. Do a factory reset on the ESP<br>
  3425. <ul>
  3426. <li>arguments: <code>none</code></li>
  3427. <li>example: <code>set &lt;esp&gt; reset</code></li>
  3428. </ul>
  3429. </li><br>
  3430. <li><a name="ESPEasy_device_set_resetflashwritecounter">resetflashwritecounter</a><br>
  3431. Used to reset flash write counter<br>
  3432. <ul>
  3433. <li>arguments: <code>none</code></li>
  3434. <li>example: <code>set &lt;esp&gt; resetflashwritecounter</code></li>
  3435. </ul>
  3436. </li><br>
  3437. <b>ESP Easy rules related commands</b> (Note: These commands may be protected with the ESP Easy 'Admin Passsword'.
  3438. See <a href="#ESPEasy_device_set_adminpassword">set adminpassword</a> for
  3439. details.)<br><br>
  3440. <li><a name="ESPEasy_device_set_deepsleep">deepsleep</a><br>
  3441. Ask ESP to go into deepsleep mode.<br>
  3442. <ul>
  3443. <li>arguments: <code>&lt;duration in is&gt;</code></li>
  3444. </ul>
  3445. </li><br>
  3446. <li><a name="ESPEasy_device_set_event">event</a><br>
  3447. Trigger an ESP event. Such events can be used in ESP Easy rules.<br>
  3448. <ul>
  3449. <li>arguments: <code>&lt;string&gt;</code></li>
  3450. <li>example: <code>set &lt;esp&gt; event testevent</code></li>
  3451. </ul>
  3452. </li><br>
  3453. <li><a name="ESPEasy_device_set_notify">notify</a><br>
  3454. Send a notify message<br>
  3455. <ul>
  3456. <li>arguments: <code>&lt;notify nr&gt; &lt;message&gt;</code></li>
  3457. </ul>
  3458. </li><br>
  3459. <li><a name="ESPEasy_device_set_publish">publish</a><br>
  3460. Publish a value via MQTT<br>
  3461. <ul>
  3462. <li>arguments: <code>&lt;topic&gt; &lt;value&gt;</code></li>
  3463. </ul>
  3464. </li><br>
  3465. <li><a name="ESPEasy_device_set_rules">rules</a><br>
  3466. Enable/disable rule processing<br>
  3467. <ul>
  3468. <li>arguments: <code>&lt;0|1|off|on&gt;</code></li>
  3469. </ul>
  3470. </li><br>
  3471. <li><a name="ESPEasy_device_set_sendto">sendto</a><br>
  3472. Send a command to another ESP<br>
  3473. <ul>
  3474. <li>arguments: <code>&lt;unit nr&gt; &lt;command&gt;</code></li>
  3475. </ul>
  3476. </li><br>
  3477. <li><a name="ESPEasy_device_set_sendtohttp">sendtohttp</a><br>
  3478. Used to tigger a HTTP URL call<br>
  3479. <ul>
  3480. <li>arguments: <code>none</code></li>
  3481. </ul>
  3482. </li><br>
  3483. <li><a name="ESPEasy_device_set_sendtoudp">sendtoudp</a><br>
  3484. Used to tigger a UDP call<br>
  3485. <ul>
  3486. <li>arguments: <code>&lt;ip&gt; &lt;port&gt; &lt;url&gt;</code></li>
  3487. </ul>
  3488. </li><br>
  3489. <li><a name="ESPEasy_device_set_taskrun">taskrun</a><br>
  3490. Used trigger a taskrun command<br>
  3491. <ul>
  3492. <li>arguments: <code>&lt;task/device nr&gt;</code></li>
  3493. </ul>
  3494. </li><br>
  3495. <li><a name="ESPEasy_device_set_taskvalueset">taskvalueset</a><br>
  3496. Used to set taskvalueset<br>
  3497. <ul>
  3498. <li>arguments: <code>&lt;task/device nr&gt; &lt;value nr&gt; &lt;value/formula&gt;</code></li>
  3499. </ul>
  3500. </li><br>
  3501. <li><a name="ESPEasy_device_set_taskvaluesetandrun">taskvaluesetandrun</a><br>
  3502. Used to set taskvaluesetandrun<br>
  3503. <ul>
  3504. <li>arguments: <code>&lt;task/device nr&gt; &lt;value nr&gt; &lt;value/formula&gt;</code></li>
  3505. </ul>
  3506. </li><br>
  3507. <li><a name="ESPEasy_device_set_timerset">timerset</a><br>
  3508. Set an ESP Easy timer<br>
  3509. <ul>
  3510. <li>arguments: <code>&lt;timer nr&gt; &lt;duration in s&gt;</code></li>
  3511. </ul>
  3512. </li><br>
  3513. <b>ESP Easy experimental commands:</b> (The following commands can be changed or removed at any time)<br><br>
  3514. <li><a name="ESPEasy_device_set_rgb">rgb</a><br>
  3515. Used to control a rgb light wo/ an ESPEasy plugin.<br>
  3516. You have to set attribute <a href="#ESPEasy_device_attr_rgbgpios">rgbGPIOs</a> to
  3517. enable this feature. Default colorpicker mode is HSVp but can be adjusted
  3518. with help of attribute <a href="#ESPEasy_device_attr_colorpicker">colorpicker</a>
  3519. to HSV or RGB. Set attribute <a href="#webCmd">webCmd</a> to rgb to
  3520. display a colorpicker in FHEMWEB room view and on detail page.<br>
  3521. <ul>
  3522. <li>
  3523. arguments: <code>&lt;rrggbb&gt;|on|off|toggle</code>
  3524. </li>
  3525. <li>
  3526. examples:<br>
  3527. <code>set &lt;esp&gt; rgb 00FF00</code><br>
  3528. <code>set &lt;esp&gt; rgb on</code><br>
  3529. <code>set &lt;esp&gt; rgb off</code><br>
  3530. <code>set &lt;esp&gt; rgb toggle</code><br>
  3531. </li>
  3532. <li>Full featured example:<br>
  3533. attr &lt;ESP&gt; colorpicker HSVp<br>
  3534. attr &lt;ESP&gt; devStateIcon { ESPEasy_devStateIcon($name) }<br>
  3535. attr &lt;ESP&gt; Interval 30<br>
  3536. attr &lt;ESP&gt; parseCmdResponse status,pwm<br>
  3537. attr &lt;ESP&gt; pollGPIOs D6,D7,D8<br>
  3538. attr &lt;ESP&gt; rgbGPIOs D6,D7,D8<br>
  3539. attr &lt;ESP&gt; webCmd rgb:rgb ff0000:rgb 00ff00:rgb 0000ff:toggle:on:off
  3540. </li>
  3541. </ul>
  3542. </li><br>
  3543. <b>ESP Easy deprecated commands:</b> (will be removed in a later version)<br>
  3544. <br>
  3545. <li><a name="ESPEasy_device_set_status">status</a><br>
  3546. Request esp device status (eg. gpio)<br>
  3547. See attributes: parseCmdResponse, readingPrefixGPIO, readingSuffixGPIOState
  3548. <ul>
  3549. <li>arguments: <code>&lt;pin&gt;</code></li>
  3550. <li>example: <code>set &lt;esp&gt; status 14</code></li>
  3551. </ul>
  3552. </li><br>
  3553. </ul>
  3554. <br><a name="ESPEasy_device_attr"></a>
  3555. <b>Attributes</b> (logical device)<br><br>
  3556. <ul>
  3557. <li><a name="ESPEasy_device_attr_adjustvalue">adjustValue</a><br>
  3558. Used to adjust sensor values<br>
  3559. Must be a space separated list of &lt;reading&gt;:&lt;formula&gt;.
  3560. Reading can be a regexp. Formula can be an arithmetic expression like
  3561. 'round(($VALUE-32)*5/9,2)'.
  3562. If $VALUE is omitted in formula then it will be added to the beginning of
  3563. the formula. So you can simple write 'temp:+20' or '.*:*4'<br>
  3564. Modified or ignored values are marked in the system log (verbose 4). Use
  3565. verbose 5 logging to see more details.<br>
  3566. If the used sub function returns 'undef' then the value will be ignored.
  3567. <br>
  3568. The following variables can be used if necessary:
  3569. <ul>
  3570. <li>$VALUE contains the original value</li>
  3571. <li>$READING contains the reading name</li>
  3572. <li>$NAME contains the device name</li>
  3573. </ul>
  3574. Default: none<br>
  3575. Eg. <code>attr ESPxx adjustValue humidity:+0.1
  3576. temperature*:($VALUE-32)*5/9</code><br>
  3577. Eg. <code>attr ESPxx adjustValue
  3578. .*:my_OwnFunction($NAME,$READING,$VALUE)</code><br>
  3579. <br>
  3580. Sample function to ignore negative values:<br>
  3581. <code>
  3582. sub my_OwnFunction($$$) {<br>
  3583. &nbsp;&nbsp;my ($name,$reading,$value) = @_;<br>
  3584. &nbsp;&nbsp;return ($value < 0) ? undef : $value;<br>
  3585. }<br>
  3586. </code>
  3587. </li><br>
  3588. <li><a name="ESPEasy_device_attr_colorpicker">colorpicker</a><br>
  3589. Used to select colorpicker mode<br>
  3590. Possible values: RGB,HSV,HSVp<br>
  3591. Default: HSVp
  3592. </li><br>
  3593. <li><a name="ESPEasy_device_attr_colorpickerctcw">colorpickerCTcw</a><br>
  3594. Used to select ct colorpicker's cold white color temperature in Kelvin<br>
  3595. Possible values: &gt; colorpickerCTww<br>
  3596. Default: 6000
  3597. </li><br>
  3598. <li><a name="ESPEasy_device_attr_colorpickerctww">colorpickerCTww</a><br>
  3599. Used to select ct colorpicker's warm white color temperature in Kelvin<br>
  3600. Possible values: &lt; colorpickerCTcw<br>
  3601. Default: 2000
  3602. </li><br>
  3603. <li><a name="ESPEasy_device_attr_disable">disable</a><br>
  3604. Used to disable device<br>
  3605. Possible values: 0,1<br>
  3606. Default: 0
  3607. </li><br>
  3608. <li><a name="ESPEasy_device_attr_disableRiskyCmds">disableRiskyCmds</a><br>
  3609. Used to disable supposed dangerous set cmds: erase, reset, resetflashwritecounter<br>
  3610. Possible values: 0,1<br>
  3611. Default: 0
  3612. </li><br>
  3613. <li><a name="ESPEasy_device_attr_displaytextencode">displayTextEncode</a><br>
  3614. Used to disable url encoding for text that is send to oled/lcd displays.
  3615. Useful if you want to encode the text by yourself.<br>
  3616. Possible values: 0,1<br>
  3617. Default: 1 (enabled)
  3618. </li><br>
  3619. <li><a name="ESPEasy_device_attr_displaytextwidth">displayTextWidth</a><br>
  3620. Used to specify number of characters per display line.<br>
  3621. If set then all characters before and after the text on the same line will
  3622. be overwritten with spaces. Attribute
  3623. <a href="#ESPEasy_device_attr_displaytextencode">displayTextEncode</a> must not be
  3624. disabled to use this feature. A 128x64px display has 16 characters per
  3625. line if you are using a 8px font.<br>
  3626. Possible values: integer<br>
  3627. Default: 0 (disabled)
  3628. </li><br>
  3629. <li><a name="ESPEasy_device_attr_interval">Interval</a><br>
  3630. Used to set polling interval for presence check and GPIOs polling in
  3631. seconds. 0 will disable this feature.<br>
  3632. Possible values: secs &gt; 10.<br>
  3633. Default: 300
  3634. </li><br>
  3635. <li><a href="#IODev">IODev</a><br>
  3636. Used to select I/O device (ESPEasy Bridge).
  3637. </li><br>
  3638. <li><a name="ESPEasy_device_attr_maplightscmds">mapLightCmds</a><br>
  3639. Enable the following commands and map them to the specified ESPEasy
  3640. command: rgb, ct, pct, on, off, toggle, dim, line, one, all, fade,
  3641. colorfade, rainbow, kitt, comet, theatre, scan, dualscan, twinkle,
  3642. twinklefade, sparkle, wipe, fire, stop, fadetime, fadedelay, count, speed,
  3643. bgcolor. Ask the ESPEasy maintainer to add more if required.<br>
  3644. Needed to use FHEM's colorpicker or slider widgets to control a
  3645. rgb/ct/effect/... plugin.<br>
  3646. required values: <code>a valid set command</code><br>
  3647. eg. <code>attr &lt;esp&gt; mapLightCmds Lights</code>
  3648. </li><br>
  3649. <li><a name="ESPEasy_device_attr_presencecheck">presenceCheck</a><br>
  3650. Used to enable/disable presence check for ESPs<br>
  3651. Presence check determines the presence of a device by readings age. If any
  3652. reading of a device is newer than <a href="#ESPEasy_device_attr_interval">interval</a>
  3653. seconds then it is marked as being present. This kind of check works for
  3654. ESP devices in deep sleep too but require at least 1 reading that is
  3655. updated regularly. Therefore the ESP must send the corresponding data
  3656. regularly (ESP device option "delay").<br>
  3657. Possible values: 0,1<br>
  3658. Default: 1 (enabled)
  3659. </li><br>
  3660. <li>
  3661. <a href="#readingFnAttributes">readingFnAttributes</a>
  3662. </li><br>
  3663. <li><a name="ESPEasy_device_attr_readingswitchtext">readingSwitchText</a><br>
  3664. Map values for readings to on/off instead 0/1 if ESP device is a switch.<br>
  3665. Possible values:<br>
  3666. 0: disable mapping.<br>
  3667. 1: enable mapping 0-&gt;off / 1-&gt;on<br>
  3668. 2: enable inverse mapping 0-&gt;on / 1-&gt;off<br>
  3669. Default: 1
  3670. </li><br>
  3671. <li><a name="ESPEasy_device_attr_rgbgpios">rgbGPIOs</a><br>
  3672. Use to define GPIOs your lamp is conneted to. Must be set to be able to
  3673. use <a href="#ESPEasy_device_set_rgb">rgb</a> set command.<br>
  3674. Possible values: Comma separated tripple of ESP pin numbers or arduino pin
  3675. names<br>
  3676. Eg: 12,13,15<br>
  3677. Eg: D6,D7,D8<br>
  3678. Default: none
  3679. </li><br>
  3680. <li><a name="ESPEasy_device_attr_setstate">setState</a><br>
  3681. Summarize received values in state reading.<br>
  3682. A positive number determines the number of characters used for abbreviated
  3683. reading names. Only readings with an age less than
  3684. <a href="#ESPEasy_device_attr_interval">interval</a> will be considered. If your are
  3685. not satisfied with format or behavior of setState then disable this
  3686. attribute (set to 0) and use global attributes userReadings and/or
  3687. stateFormat to get what you want.<br>
  3688. Possible values: integer &gt;=0<br>
  3689. Default: 3 (enabled with 3 characters abbreviation)
  3690. </li><br>
  3691. <li><a name="ESPEasy_device_attr_userSetCmds">userSetCmds</a><br>
  3692. Can be used to:
  3693. <ul>
  3694. <li>
  3695. Define new, own or unconsidered ESP Easy commands. Note: alternatively
  3696. the set commands <a href="#ESPEasy_device_set_raw">raw</a> or
  3697. <a href="#ESPEasy_device_set_rawsystem">rawsystem</a> can also be used to it.<br>
  3698. </li>
  3699. <li>
  3700. Mapping of secondary commands as primary ones to be able to use FHEM
  3701. widgets or FHEM's <a href="#setExtensions">set extentions</a>.
  3702. </li>
  3703. <li>
  3704. Redefine built-in commands.
  3705. </li>
  3706. </ul><br>
  3707. Argument must be a <a href="https://perldoc.perl.org/perldsc.html#Declaration-of-a-HASH-OF-HASHES">perl hash</a>.
  3708. The following hash keys can be used. An omitted key will be replaced with the appropriate default value.<br>
  3709. <ul>
  3710. <li><code>args:</code> minimum number of required arguments. Default: 0</li>
  3711. <li><code>url:</code> ESPEasy URL to be called. Default: "/control?cmd="</li>
  3712. <li><code>widget:</code> <a href="#widgetOverride">FHEM widget</a> to be
  3713. used for this set command. Default: none
  3714. </li>
  3715. <li><code>cmds:</code> Sub command(s) of specified plugin that will be
  3716. mapped as regular command(s). Must also be a perl hash. Default: none
  3717. </li>
  3718. <li><code>usage:</code> Possible command line arguments. Used in help command and
  3719. syntax check. Required arguments should be enclosed in curly brackets,
  3720. optional arguments in square brackets. Both should be separated by
  3721. spaces. Default: none</li>
  3722. The following usage strings have a special meaning and effect:
  3723. <ul>
  3724. <li>&lt;0|1|off|on&gt;: "on" or "off" will be replaced with "0" or "1"
  3725. in commands send to the ESPEasy device. See attribute
  3726. <a href="#ESPEasy_device_attr_readingswitchtext">readingSwitchText</a>
  3727. for details.</li>
  3728. <li>&lt;pin&gt;: GPIO pin numbers can also be written as
  3729. Arduino/NodeMCU pin names. See get pinMap command.</li>
  3730. <li>&lt;text&gt;: Text will be encoded for use with oled/lcd commands
  3731. to be able to use special characters.</li>
  3732. </ul>
  3733. </ul><br>
  3734. Define new commands:<br>
  3735. <ul>
  3736. <li><code>( myCmd1 =&gt; {} )</code></li>
  3737. <li><code>( myCmd1 =&gt; {}, myCmd2 =&gt; {} )</code></li>
  3738. <li><code>( myCmd3 =&gt; {args =&gt; 2, url =&gt; "/?cmd=", widget=&gt; "",
  3739. usage =&gt; "&lt;param1&gt; &lt;param2&gt;"} )</code></li>
  3740. </ul>
  3741. <br>
  3742. Define new commands with mapped sub commands:<br>
  3743. This example registers the new commands plugin_a and plugin_b. Both
  3744. commands can be used like any other ESP Easy command (eg. set dev plugin_b on).
  3745. Sub commands rgb, ct, on, off and bri can also be used as regular commands.
  3746. The advantage is that FHEM's <a href="#widgetOverride">widgets</a> and/or
  3747. <a href="#setExtensions">set extentions</a> can be used for these sub
  3748. commands right now.
  3749. <ul><li>
  3750. <pre>(
  3751. plugin_a =&gt; {
  3752. args =&gt; 2,
  3753. url =&gt; "/control?cmd=",
  3754. usage =&gt; "&lt;rgb|ct&gt; <rrggbb|colortemp>",
  3755. cmds =&gt; {
  3756. rgb =&gt; { args =&gt; 1, usage =&gt; "&lt;rrggbb&gt;", widget =&gt; "colorpicker,HSV" },
  3757. ct =&gt; { args =&gt; 1, usage =&gt; "&lt;colortemp&gt;", widget =&gt; "colorpicker,CT,2000,10,4000" }
  3758. }
  3759. },
  3760. plugin_b =&gt; {
  3761. args =&gt; 1,
  3762. url =&gt; "/foo?bar",
  3763. usage =&gt; "&lt;on|off|bri&gt; [bri_value]",
  3764. cmds =&gt; {
  3765. on =&gt; { widget =&gt; "noArg" },
  3766. off =&gt; { widget =&gt; "noArg" },
  3767. bri =&gt; { widget =&gt; "knob,min:1,max:100,step:1,linecap:round", usage =&gt; "&lt;0..255&gt;", args =&gt; 1 }
  3768. }
  3769. }
  3770. )</pre>
  3771. </li>
  3772. </ul>
  3773. </li>
  3774. <li><a name="ESPEasy_device_attr_useSetExtensions">useSetExtensions</a><br>
  3775. If set to 1 and on/off commands are available (use
  3776. <a href="#ESPEasy_device_attr_userSetCmds">userSetCmds</a> or
  3777. <a href="#eventMap">eventMap</a> if not) then the
  3778. <a href="#setExtensions">set extensions</a> are supported.<br>
  3779. Default: 0 (disabled)<br>
  3780. Eg. attr ESPxx useSetExtensions 1
  3781. </li><br>
  3782. <b>Deprecated attributes:</b><br>
  3783. <br>
  3784. <li><a name="ESPEasy_device_attr_parsecmdresponse">parseCmdResponse</a> (deprecated, may be removed in later versions)<br>
  3785. Used to parse response of commands like GPIO, PWM, STATUS, ...<br>
  3786. Specify a module command or comma separated list of commands as argument.
  3787. Commands are case insensitive.<br>
  3788. Only necessary if ESPEasy software plugins do not send their data
  3789. independently. Useful for commands like STATUS, PWM, ...<br>
  3790. Possible values: &lt;set cmd&gt;[,&lt;set cmd&gt;][,...]<br>
  3791. Default: status<br>
  3792. Eg. <code>attr ESPxx parseCmdResponse status,pwm</code>
  3793. </li><br>
  3794. <li><a name="ESPEasy_device_attr_pollgpios">pollGPIOs</a> (deprecated, may be removed in later versions)<br>
  3795. Used to enable polling for GPIOs status. This polling will do same as
  3796. command 'set ESPxx status &lt;device&gt; &lt;pin&gt;'<br>
  3797. Possible values: GPIO number or comma separated GPIO number list<br>
  3798. Default: none<br>
  3799. Eg. <code>attr ESPxx pollGPIOs 13,D7,D2</code>
  3800. </li>
  3801. <br>
  3802. The following two attributes control naming of readings that are
  3803. generated by help of parseCmdResponse and pollGPIOs (see above)
  3804. <br><br>
  3805. <li><a name="ESPEasy_device_attr_readingprefixgpio">readingPrefixGPIO</a> (deprecated, may be removed in later versions)<br>
  3806. Specifies a prefix for readings based on GPIO numbers. For example:
  3807. "set ESPxx pwm 13 512" will switch GPIO13 into pwm mode and set pwm to
  3808. 512. If attribute readingPrefixGPIO is set to PIN and attribut
  3809. <a href="#ESPEasy_device_attr_parsecmdresponse">parseCmdResponse</a> contains pwm
  3810. command then the reading name will be PIN13.<br>
  3811. Possible Values: <code>string</code><br>
  3812. Default: GPIO
  3813. </li><br>
  3814. <li><a name="ESPEasy_device_attr_readingsuffixgpiostate">readingSuffixGPIOState</a> (deprecated, may be removed in later versions)<br>
  3815. Specifies a suffix for the state-reading of GPIOs (see Attribute
  3816. <a href="#ESPEasy_device_attr_pollgpios">pollGPIOs</a>)<br>
  3817. Possible Values: <code>string</code><br>
  3818. Default: no suffix<br>
  3819. Eg. attr ESPxx readingSuffixGPIOState _state
  3820. </li><br>
  3821. </ul>
  3822. </ul>
  3823. =end html
  3824. =cut