34_ESPEasy.pm 132 KB

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