39_Talk2Fhem.pm 74 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244
  1. ################################################################
  2. #
  3. # Copyright notice
  4. #
  5. # (c) 2018 Oliver Georgi
  6. #
  7. # This script is free software; you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation; either version 2 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # The GNU General Public License can be found at
  13. # http://www.gnu.org/copyleft/gpl.html.
  14. # A copy is found in the textfile GPL.txt and important notices to the license
  15. # from the author is found in LICENSE.txt distributed with these scripts.
  16. #
  17. # This script is distributed in the hope that it will be useful,
  18. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. # GNU General Public License for more details.
  21. #
  22. # This copyright notice MUST APPEAR in all copies of the script!
  23. #
  24. ################################################################
  25. # $Id: 39_Talk2Fhem.pm 16337 2018-03-05 22:30:22Z Phill $
  26. #
  27. # 13.12.2017 0.0.2 diverses
  28. #
  29. # 16.12.2017 0.0.3 neuer pass check: else in @ sowie %
  30. # nicht alle pass keys werden durchlaufen sondern nur die geforderten
  31. # pass ^()$ ergänzt
  32. #
  33. # 19.12.2017 0.1.0 FHEM-Modul Funktionen eingefügt
  34. # Attribute T2F_keywordlist und T2F_modwordlist erzeugt
  35. ## 30.12.2017 0.2.0 Umgebungssuchen in regexp unterstützt
  36. # # Kommentierung ermöglicht
  37. # Syntaxcheck der Definition
  38. # Multiple Datumsangaben korrigiert
  39. # Wochentagsangaben korrigiert
  40. # Syntaxvereinfachung
  41. # Regexp-Listen erweitert
  42. # Multilingual DE EN
  43. # 02.01.2018 0.2.1 Liste der erase Wörter erweitert
  44. # problem bei wordlist ergänzung
  45. # multideviceable
  46. # Regexp in HASH auswertung
  47. # Automatische Umlautescaping
  48. # komma in wordlists
  49. # 26.01.2018 0.3.2 extra word search && in phrase
  50. # reihenfolge der DEF wird berücksichtigt
  51. # FHEM helper hash für globale verwendet
  52. # Code aufgeräumt
  53. # including definition files
  54. # zugriff auf Umgebungsmuster und Zeitphrasen
  55. # Phraseindikator ? und !
  56. # $n@ for keylistsaccess added
  57. # Zahlenwörter in Zeitphrase konvertieren
  58. # Eventgesteurte Befehle
  59. # Leerer set parameter löst den letzten befehl nochmal aus
  60. # 10.02.2018 0.4.0
  61. # set CLEARTRIGGERS
  62. # wieder Erkennung verbessert
  63. # Logikfehler bei verschachtelten Sätzen mit "und" behoben
  64. # Neue pass checks float, numeral
  65. # Extraktion des Klammernarrays auch bei Keylistenselector $n@
  66. # Bug on none existing namelists
  67. # Fhem $_ modifikations bug behoben
  68. # Log ausgaben verbessert
  69. # Neues Attribut T2F_if
  70. # Neues Attribut T2F_origin
  71. # Neuer GET standardfilter
  72. # Neuer GET log
  73. # Neue Variable $IF
  74. # Errormessages detaliert
  75. # Neuer Get @keylist @modlist
  76. # 12.02.2018 0.4.1
  77. # Community Notes
  78. # Nested Modifikations
  79. # Neuer Get modificationtypes
  80. # 19.02.2018 0.4.2
  81. # pass word changed
  82. # english adjust
  83. # nested bracket crash in arraymod fixed
  84. # syntaxissues on extendet commands fixed
  85. # umlautfix
  86. # 20.02.2018 0.4.3
  87. # Bug that not load attributes at bootup fixed
  88. # Function normalication updated
  89. # 21.02.2018 0.4.4
  90. # bracket extraction bug fixed
  91. # def not load at bootup fixed
  92. # 03.03.2018 0.4.5
  93. # timephrase modified
  94. # recognize order in hash replacement
  95. # Commandref fix unwanted characters
  96. # possibility of nested brackets in modifiacator
  97. # stabitility fixes in user regexp
  98. # replace ; to ;; in timecommands
  99. # Add 1 day if timecode is in past in hour phrases
  100. # Added async warning if keywordlist is unkown
  101. # 04.03.2018 0.4.6
  102. # Breacket decoding bug fixed
  103. # 04.02.2018 0.5.0
  104. # Feature: Object radiusing
  105. # 05.02.2018 0.5.1
  106. # Semicolon adding concretized
  107. # Problem with first letter umlaut in type modificator fixed
  108. # Fixed problems Attributes not ready
  109. # Matched/Unmatched corrected
  110. # Else in arraymodifikation expanded
  111. ################################################################
  112. # TODO:
  113. #
  114. # device verundung durch regexp klammern? eher durch try and error
  115. # answerx
  116. #
  117. # klammern in keywordlists sollen die $n nummerierung nicht beeinflussen
  118. # in keywordlists sind vermutlich nur maximal eine klammerebene möglich direkte regex arrays sind endlos verschachtelbar
  119. # zusätzlich unmodifizierte zeit greifbar machen
  120. # timephrase kombies morgen früh um 9 uhr ist unzuverlässig. evtl order einführen, dann kann auch die splittung weg
  121. # viertel zeitphrasen
  122. # - regexp
  123. #vordefinierte regex zu verfügung stellen
  124. # (i[nm]|vor|auf|unter|hinter)? ?(de[rmn]|die|das)? ?
  125. package main;
  126. use strict;
  127. use warnings;
  128. use POSIX;
  129. use Data::Dumper;
  130. use Time::Local;
  131. use Text::ParseWords;
  132. use Text::Balanced qw(extract_multiple extract_bracketed);
  133. use Encode qw(decode encode);
  134. my %Talk2Fhem_globals;
  135. $Talk2Fhem_globals{version}="0.5.1";
  136. $Talk2Fhem_globals{EN}{erase} = ['\bplease\b', '\balso\b', '^msgtext:'];
  137. $Talk2Fhem_globals{EN}{numbers} = {
  138. 'zero' => 0
  139. ,'^one\S*' => 1
  140. ,'^(two|twice)' => 2
  141. ,'^(three|third)' => 3
  142. ,'^four\S*' => 4
  143. ,'^five\S*' => 5
  144. ,'^six\S*' => 6
  145. ,'^seven\S*' => 7
  146. ,'^eight\S*' => 8
  147. ,'^nine\S*' => 9
  148. ,'^ten\S*' => 10
  149. ,'^eleven\S*' => 11
  150. ,'^twelve\S*' => 12
  151. };
  152. $Talk2Fhem_globals{DE}{numberre} = join("|", ('\d+', keys %{$Talk2Fhem_globals{DE}{numbers}}));
  153. $Talk2Fhem_globals{EN}{pass} = {
  154. true => '^(yes|1|true|on|open|up|bright.*)$',
  155. false => '^(no|0|false|off|close|down|dark.*)$',
  156. numeral => {re=>"($Talk2Fhem_globals{EN}{numberre})",fc=>sub{
  157. return ($_[0]) if $_[0] =~ /\d+/;
  158. my $v = $_[0];
  159. foreach ( keys %{$Talk2Fhem_globals{EN}{numbers}} ) {
  160. my $tmp = Talk2Fhem_escapeumlauts($_);
  161. last if ($v =~ s/$tmp/$Talk2Fhem_globals{EN}{numbers}{$_}/i);
  162. }
  163. return($v);}
  164. },
  165. integer => '\b(\d+)\b',
  166. float => {re=>'\b(\d+)(\s*[,.])?(\s*(\d+))?\b',fc=>'"$1".("$4"?".$4":"")'},
  167. word => '\b(\w{4,})\b',
  168. empty => '^\s*$'
  169. };
  170. $Talk2Fhem_globals{EN}{datephrase} = {
  171. 'tomorrow'=> {days=>1}
  172. , 'day after tomorrow'=> {days=>2}
  173. , 'yesterday'=> {days=>-1}
  174. , 'the day before yesterday'=> {days=>-2}
  175. , 'in (\d+) week\S?'=> {days=>'(7*$1)'}
  176. , 'in (\d+) month(\S\S)?'=> {month=>'"$1"'}
  177. , 'in (\d+) year(\S\S)?'=> {year=>'"$1"'}
  178. , 'next week'=> {days=>7}
  179. , 'next month'=> {month=>1}
  180. , 'next year'=> {year=>1}
  181. , '(on )?sunday'=> {wday=>0}
  182. , '(on )?monday'=> {wday=>1}
  183. , '(on )?tuesday'=> {wday=>2}
  184. , '(on )?Wednesday'=> {wday=>3}
  185. , '(on )?thursday'=> {wday=>4}
  186. , '(on )?friday'=> {wday=>5}
  187. , '(on )?saturday'=> {wday=>6}
  188. , 'in (\d+) days?'=> {days=>'"$1"'}
  189. , 'on (\d\S*(\s\d+)?)'=> {date=>'"$1"'}
  190. };
  191. $Talk2Fhem_globals{EN}{timephrase} = {
  192. '(in|and|after)? (\d+) hours?' => {hour=>'"$2"'}
  193. , '(in|and|after)? (\d+) minutes?' => {min=>'"$2"'}
  194. , '(in|and|after)? (\d+) seconds?' => {sec=>'"$2"'}
  195. , 'now' => {min=>3}
  196. , 'after' => {min=>30}
  197. , 'later' => {hour=>1}
  198. , 'right now' => {unix=>'time'}
  199. , 'immediately' => {unix=>'time'}
  200. , 'by (\d+) (o.clock)?' => {time=>'"$1"'}
  201. , 'at (\d+) (o.clock)?' => {time=>'"$1"'}
  202. , 'morning' => {time=>'"09:00"'}
  203. , 'evening' => {time=>'"18:00"'}
  204. , 'afternoon' => {time=>'"16:00"'}
  205. , 'morning' => {time=>'"10:30"'}
  206. , 'noon' => {time=>'"12:00"'}
  207. , 'at lunchtime' => {time=>'"12:00"'}
  208. , 'today' => {time=>'"12:00"'}
  209. };
  210. #$Talk2Fhem_globals{DE}{erase} = ['\bbitte\b', '\bauch\b', '\smachen\b', '\sschalten\b', '\sfahren\b', '\bkann\b', '\bsoll\b', '\bnach\b', '^msgtext:'];
  211. $Talk2Fhem_globals{DE}{erase} = ['\bbitte\b', '\bauch\b','\bkann\b', '\bsoll\b'];
  212. # true => '^(ja|1|true|wahr|ein|eins.*|auf.*|öffnen|an.*|rauf.*|hoch.*|laut.*|hell.*)$',
  213. # false => '^(nein|0|false|falsch|aus.*|null|zu.*|schlie\S\S?en|runter.*|ab.*|leise.*|dunk.*)$',
  214. $Talk2Fhem_globals{DE}{numbers} = {
  215. 'null' => 0
  216. ,'(ein\S*|erste\S*)' => 1
  217. ,'zwei\S*' => 2
  218. ,'(drei\S*|dritt\S*)' => 3
  219. ,'vier\S*' => 4
  220. ,'fünf\S*' => 5
  221. ,'sechs\S*' => 6
  222. ,'sieb\S*' => 7
  223. ,'acht\S*' => 8
  224. ,'neun\S*' => 9
  225. ,'zehn\S*' => 10
  226. ,'elf\S*' => 11
  227. ,'zwölf\S*' => 12
  228. };
  229. $Talk2Fhem_globals{DE}{numberre} = join("|", ('\d+', keys %{$Talk2Fhem_globals{DE}{numbers}}));
  230. $Talk2Fhem_globals{DE}{pass} = {
  231. true => '\b(ja|1|true|wahr|ein|eins.*|auf.*|\S*ffnen|an.*|rauf.*|hoch.*|laut.*|hell.*|start.*|(ab)?spiele\S?)\b',
  232. false => '\b(nein|0|false|falsch|aus.*|null|zu.*|schlie\S\S?en|runter.*|ab.*|leise.*|dunk.*|stop.*|beende\S?)\b',
  233. numeral => {re=>"($Talk2Fhem_globals{DE}{numberre})",fc=>sub{
  234. return ($_[0]) if $_[0] =~ /\d+/;
  235. my $v = $_[0];
  236. foreach ( keys %{$Talk2Fhem_globals{DE}{numbers}} ) {
  237. my $tmp = Talk2Fhem_escapeumlauts($_);
  238. last if ($v =~ s/$tmp/$Talk2Fhem_globals{DE}{numbers}{$_}/i);
  239. }
  240. return($v);}
  241. },
  242. integer => '\b(\d+)\b',
  243. float => {re=>'\b(\d+)(\s*[,.])?(\s*(\d+))?\b',fc=>'"$1".("$4"?".$4":"")'},
  244. word => '\b(\w{4,})\b',
  245. empty => '^\s*$'
  246. };
  247. $Talk2Fhem_globals{DE}{datephrase} = {
  248. 'morgen'=> {days=>1}
  249. , 'übermorgen'=> {days=>2}
  250. , 'gestern'=> {days=>-1}
  251. , 'vorgestern'=> {days=>-2}
  252. , 'in ('.$Talk2Fhem_globals{DE}{numberre}.') woche\S?'=> {days=>'(7*$1)'}
  253. , 'in ('.$Talk2Fhem_globals{DE}{numberre}.') monat(\S\S)?'=> {month=>'"$1"'}
  254. , 'in ('.$Talk2Fhem_globals{DE}{numberre}.') jahr(\S\S)?'=> {year=>'"$1"'}
  255. , 'nächste.? woche'=> {days=>7}
  256. , 'nächste.? monat'=> {month=>1}
  257. , 'nächste.? jahr'=> {year=>1}
  258. , '(am )?sonntag'=> {wday=>0}
  259. , '(am )?montag'=> {wday=>1}
  260. , '(am )?dienstag'=> {wday=>2}
  261. , '(am )?mittwoch'=> {wday=>3}
  262. , '(am )?donnerstag'=> {wday=>4}
  263. , '(am )?freitag'=> {wday=>5}
  264. , '(am )?samstag'=> {wday=>6}
  265. , 'in ('.$Talk2Fhem_globals{DE}{numberre}.') tag(\S\S)?'=> {days=>'"$1"'}
  266. , 'am (\d\S*(\s\d+)?)'=> {date=>'"$1"'}
  267. };
  268. # fc modify time. $_[0] = ermittelte zeit. Zugriff auf $1 $2 usw
  269. $Talk2Fhem_globals{DE}{timephrase} = {
  270. '(in|und|nach)? ('.$Talk2Fhem_globals{DE}{numberre}.') stunde.?' => {hour=>'"$2"'}
  271. , '(in|und|nach)? ('.$Talk2Fhem_globals{DE}{numberre}.') minute.?' => {min=>'"$2"'}
  272. , '(in|und|nach)? ('.$Talk2Fhem_globals{DE}{numberre}.') sekunde.?' => {sec=>'"$2"'}
  273. , 'gleich' => {min=>3}
  274. , 'nachher' => {min=>30}
  275. , 'später' => {hour=>1}
  276. , 'jetzt' => {unix=>'time'}
  277. , 'sofort' => {unix=>'time'}
  278. , 'um (\d+\s?\:\s?\d+|'.$Talk2Fhem_globals{DE}{numberre}.') (uhr)?' => {
  279. time=>'"$1"',
  280. fc=>sub () {(($_[0] + 3600) < time) ? ($_[0]+3600*24) : $_[0] }
  281. }
  282. , 'um ('.$Talk2Fhem_globals{DE}{numberre}.') uhr ('.$Talk2Fhem_globals{DE}{numberre}.')' => {
  283. hour=>'"$1"',
  284. min=>'"$1"',
  285. fc=>sub () {(($_[0] + 3600) < time) ? ($_[0]+3600*24) : $_[0] }
  286. } ############ ZU TESTEN
  287. , 'früh' => {time=>'"09:00"'}
  288. , 'abends?' => {time=>'"18:00"'}
  289. , 'nachmittags?' => {time=>'"16:00"'}
  290. , 'vormittags?' => {time=>'"10:30"'}
  291. , 'mittags?' => {time=>'"12:00"'}
  292. , 'heute' => {time=>'"12:00"'}
  293. };
  294. sub Talk2Fhem_Initialize($);
  295. sub Talk2Fhem_Define($$);
  296. sub Talk2Fhem_Undef($$);
  297. sub Talk2Fhem_Delete($$);
  298. sub Talk2Fhem_Notify($$);
  299. sub Talk2Fhem_Set($@);
  300. sub Talk2Fhem_addND($);
  301. sub Talk2Fhem_UpdND($);
  302. sub Talk2Fhem_Get($$@);
  303. sub Talk2Fhem_Attr(@);
  304. sub Talk2Fhem_Loadphrase($$$);
  305. sub Talk2Fhem_parseParams($);
  306. sub Talk2Fhem_realtrim($);
  307. sub Talk2Fhem_normalize($);
  308. sub Talk2Fhem_parseArray($;$$);
  309. sub Talk2Fhem_loadList($$;$);
  310. sub Talk2Fhem_language($);
  311. sub Talk2Fhem_mkattime($$);
  312. sub Talk2Fhem_exec($$$);
  313. sub T2FL($$$);
  314. sub Talk2Fhem_Initialize($)
  315. {
  316. my ($hash) = @_;
  317. $hash->{DefFn} = "Talk2Fhem_Define";
  318. $hash->{UndefFn} = "Talk2Fhem_Undef";
  319. # $hash->{DeleteFn} = "X_Delete";
  320. $hash->{SetFn} = "Talk2Fhem_Set";
  321. $hash->{GetFn} = "Talk2Fhem_Get";
  322. # $hash->{ReadFn} = "X_Read";
  323. # $hash->{ReadyFn} = "X_Ready";
  324. $hash->{AttrFn} = "Talk2Fhem_Attr";
  325. $hash->{NotifyFn} = "Talk2Fhem_Notify";
  326. # $hash->{RenameFn} = "X_Rename";
  327. # $hash->{ShutdownFn} = "X_Shutdown";
  328. $hash->{AttrList} =
  329. "disable:0,1 T2F_disableumlautescaping:0,1 T2F_origin T2F_filter T2F_if:textField-long T2F_keywordlist:textField-long T2F_modwordlist:textField-long T2F_language:EN,DE";
  330. }
  331. sub Talk2Fhem_Define($$)
  332. {
  333. my ( $hash, $def ) = @_;
  334. $hash->{STATE} = "Loading";
  335. if ($def =~ /^\S+ Talk2Fhem$/) {
  336. $hash->{DEF} = "# <regex> = <command>\n# Examples:\n# timer (löschen|zurück)\t= set \$NAME cleartimers\n# ereignis\\S* (löschen|zurück)\t= set \$NAME cleartriggers";
  337. return;
  338. }
  339. my $error = undef;
  340. my @def = split(/ /, $def);
  341. my $name = shift(@def);
  342. my $dev = shift(@def);
  343. $hash->{STATE} = "Initialized";
  344. if ($init_done) {
  345. ($_ = Talk2Fhem_loadList($hash, "T2F_keywordlist")) && return;
  346. ($_ = Talk2Fhem_loadList($hash, "T2F_modwordlist")) && return;
  347. $error = Talk2Fhem_Loadphrase($hash, "phrase", "@def");
  348. T2FL($name, 1, $error) if $error;
  349. # T2FL($name, 5, "T2F Phrasehash:\n".Dumper($Talk2Fhem_phrase{$name})) unless $error;
  350. T2FL($name, 5, "T2F Phrasehash:\n".Dumper($hash->{helper}{phrase})) unless $error;
  351. $hash->{STATE} = "Ready";
  352. }
  353. return $error;
  354. }
  355. sub Talk2Fhem_Loadphrase($$$) {
  356. my $hash = shift;
  357. my $target = shift;
  358. my $text = "@_";
  359. my @h = Talk2Fhem_parseParams($text);
  360. return ("Error while parsing Definition.\n$h[0]"."\n\n$text" ) unless(ref($h[0]) eq "HASH");
  361. # Not ready yet
  362. return unless $hash->{helper};
  363. my $disu =AttrVal($hash, "T2F_disableumlautescaping", 0);
  364. my %keylist = %{$hash->{helper}{T2F_keywordlist}} if $hash->{helper}{T2F_keywordlist};
  365. my $i=0;
  366. while ($i <= $#h) {
  367. my $elmnt = $h[$i];
  368. my @a = $$elmnt{key}=~/(?<!\\)\(/g;
  369. my @b = $$elmnt{key}=~/(?<!\\)\)/g;
  370. return("Error while parsing Definition HASH.\nOdd numbers of brackets ():\n".$$elmnt{key}) unless $#a eq $#b;
  371. if ($$elmnt{key} eq '$include') {
  372. T2FL $hash->{NAME}, 4, "Loading Configfile $$elmnt{val}";
  373. # open(my $fh, '<:encoding(UTF-8)', $$elmnt{val})
  374. # open fh, "<", $$elmnt{val}
  375. # or return "Could not open file '$$elmnt{val}' $!";
  376. my ($error, @content) = FileRead($$elmnt{val});
  377. return "$error '$$elmnt{val}'" if $error;
  378. #local $/;
  379. my @file = Talk2Fhem_parseParams(join("\n",@content));
  380. #close("fh");
  381. return ("Error while parsing File $$elmnt{val}.\n$file[0]"."\n\n$text" ) unless(ref($file[0]) eq "HASH");
  382. splice @h, $i, 1;
  383. splice @h, $i, 0, @file;
  384. # push(@h, @file);
  385. next;
  386. }
  387. if ($$elmnt{val} =~ /^\((.*)\)/) {
  388. #my %r = eval($$elmnt{val});
  389. #Log 1, "Hallo: ".$1;
  390. my %r;
  391. my $harr = Talk2Fhem_parseArray($1, undef, 1);
  392. for my $el (@$harr) {
  393. my @test = split(/[\s\t]*=>[\t\s\n]*/,$el,2);
  394. my $t = $test[1] =~ /^[^"']/;
  395. #Log 1, Dumper @test;
  396. #Log 1, Dumper $t;
  397. my $h = Talk2Fhem_parseArray($el, '\s*=>[\t\s\n]*', $t);
  398. #my @arr = /(.*?)=>(.*)/;
  399. #$h = Talk2Fhem_parseArray($_, "=>", 1) if $$h[0]=~ /answer/;
  400. $r{$$h[0]} = $$h[1];
  401. }
  402. return("Error while parsing Definition HASH.\n".$$elmnt{val}."\n\n$text") unless (%r);
  403. $$elmnt{val} = \%r;
  404. }elsif ($$elmnt{val} =~ /^\(.*[^)]$/) {
  405. return("Error while parsing Definition HASH.\nDid you forget closing ')' in:\n".$$elmnt{val}."\n\n$text");
  406. } else {
  407. my $tmp=$$elmnt{val};
  408. $$elmnt{val} = undef;
  409. $$elmnt{val}{($target eq "phrase") ? "cmd" : $target} = $tmp;
  410. }
  411. #alternative syntax wenn nur ein value
  412. # elsif ($$elmnt{key} =~ /^\$if.*?\s+(.*)/) {
  413. #return("Syntax Error. Can't locate IF condition.") unless $1;
  414. #return("Syntax Error. Can't locate IF regexp.") unless $$elmnt{val};
  415. #$hash->{helper}{ifs} = { IF=>$$elmnt{val}, regexp=>"$1" };
  416. #splice @h, $i, 1;
  417. #next;
  418. # }
  419. $i++;
  420. # Regexp Auflösung und Analyse
  421. my $d=0;
  422. my @hitnokeylist=(AttrVal($hash->{NAME}, "T2F_origin", undef));
  423. my @phrs = map { Talk2Fhem_realtrim($_) } split(/[\t\s]*\&\&[\t\s]*/, $$elmnt{key});
  424. for my $phr (@phrs) {
  425. my $keylistname;
  426. my $tmp = $phr;
  427. # klammern zählen die nicht geslasht sind und kein spezialklammern sind (?
  428. while ($tmp =~ /(?<!\\)\((?!\?)(@(\w+))?/g) {
  429. $d++;
  430. if ($1) {
  431. $keylistname = $2;
  432. unless ($keylist{$keylistname}) {
  433. asyncOutput($hash->{CL}, "Warning: Unkown keywordlist $1. In phrase: $phr");
  434. next;
  435. #return(T2FL($hash, 1, "Unkown keywordlist $1. In phrase: $phr"));
  436. }
  437. my $re = join("|", @{$keylist{$keylistname}});
  438. $phr =~ s/@(\w+)/$re/;
  439. #speichern welcher array in welcher klammer steht
  440. $hitnokeylist[$d] = $keylistname;
  441. }
  442. }
  443. push(@{$$elmnt{regexps}}, Talk2Fhem_escapeumlauts($phr, $disu));
  444. $$elmnt{hitnokeylist} = \@hitnokeylist;
  445. }
  446. }
  447. # for (@h) {
  448. # next unless ($$_{val}{if});
  449. # my $test = AnalyzeCommandChain ($hash, "IF ((".$$_{val}{if}.")) ({1})");
  450. # if ($test and $test ne "1") {
  451. # T2FL $hash, 1, "Condition ".$$_{val}{if}." failed: ".$test;
  452. # return($test."\n\n".$text);
  453. # }
  454. # }
  455. $hash->{helper}{$target} = \@h;
  456. return(undef);
  457. }
  458. sub Talk2Fhem_Undef($$)
  459. {
  460. my ( $hash, $name) = @_;
  461. $hash->{helper} = undef;
  462. return undef;
  463. }
  464. sub Talk2Fhem_Delete($$)
  465. {
  466. my ( $hash, $name ) = @_;
  467. return undef;
  468. }
  469. sub Talk2Fhem_Notify($$)
  470. {
  471. my ($own_hash, $dev_hash) = @_;
  472. my $ownName = $own_hash->{NAME};
  473. my $devName; # Device that created the events
  474. for (@{$$own_hash{helper}{notifiers}}) {
  475. $devName = $dev_hash->{NAME} if $_ eq $dev_hash->{NAME};
  476. }
  477. return "" unless $devName;
  478. my $events = deviceEvents($dev_hash, 1);
  479. my @nots = @{$$own_hash{helper}{notifies}};
  480. my $i=0;
  481. # for my $i (0 .. $#nots) {
  482. while ($i <= $#{$$own_hash{helper}{notifies}}) {
  483. my $not = ${$$own_hash{helper}{notifies}}[$i];
  484. if (grep { $devName eq $_ } (@{$$not{devs}})) {
  485. T2FL $own_hash, 4, "Event detected ".$$not{if};
  486. my $res = fhem($$not{if});
  487. T2FL $own_hash, 5, "Result: ".$res;
  488. if ($res == 1) {
  489. T2FL $own_hash, 3, "Execute command: ".$$not{cmd};
  490. my $fhemres = fhem($$not{cmd});
  491. readingsSingleUpdate($own_hash, "response", $fhemres, 1);
  492. splice(@{$$own_hash{helper}{notifies}}, $i--, 1);
  493. Talk2Fhem_UpdND($own_hash);
  494. } elsif ($res) {
  495. T2FL $own_hash, 1, "Error on condition ($$not{if}): $res";
  496. readingsSingleUpdate($own_hash, "response", $res, 1);
  497. splice(@{$$own_hash{helper}{notifies}}, $i--, 1);
  498. Talk2Fhem_UpdND($own_hash);
  499. }
  500. }
  501. $i++;
  502. }
  503. return "" if(IsDisabled($ownName));
  504. if($devName eq "global" && grep(m/^INITIALIZED|REREADCFG$/, @{$events}))
  505. {
  506. #Talk2Fhem_parseKeys($own_hash);
  507. }
  508. }
  509. sub Talk2Fhem_Set($@)
  510. {
  511. my ( $hash, $name, @args ) = @_;
  512. (return "\"set $name\" needs at least one argument") unless(scalar(@args));
  513. (return "Unknown argument ?, choose one of ! cleartriggers:noArg cleartimers:noArg") if($args[0] eq "?");
  514. if ($hash->{STATE} ne "Ready") {
  515. #Fülle nur cmds array
  516. } elsif ($args[0] eq "cleartimers") {
  517. AnalyzeCommand($hash->{CL}, "delete at_".$name."_.*");
  518. } elsif ($args[0] eq "cleartriggers") {
  519. $$hash{helper}{notifies} = [];
  520. Talk2Fhem_UpdND($hash);
  521. } else {
  522. $hash->{STATE} = "Loading";
  523. shift @args if $args[0] eq "!";
  524. @args = ReadingsVal($name, "set", undef) unless(scalar(@args));
  525. #my $txt = s/[^\x00-\xFF]//g;
  526. #my $txt = decode("utf8", "@args");
  527. my $txt = "@args";
  528. Talk2Fhem_loadList($hash, "T2F_keywordlist") unless $hash->{helper}{T2F_keywordlist};
  529. Talk2Fhem_loadList($hash, "T2F_modwordlist") unless $hash->{helper}{T2F_modwordlist};
  530. Talk2Fhem_Loadphrase($hash, "phrase", $hash->{DEF}) unless $hash->{helper}{phrase};
  531. Talk2Fhem_Loadphrase($hash, "if", AttrVal($name, "T2F_if","")) if (AttrVal($name, "T2F_if",0) and ! $hash->{helper}{if});
  532. readingsSingleUpdate($hash, "set", "$txt", 1);
  533. $hash->{STATE} = "Ready";
  534. $hash->{STATE} = "Working";
  535. my %res = Talk2Fhem_exec("$txt", $hash, $name);
  536. if (%res && ! $res{err} && $res{cmds}) {
  537. #Ausführen
  538. if ($res{cmds}) {
  539. for my $h (@{$res{cmds}}) {
  540. my $fhemcmd = ($$h{at}?Talk2Fhem_mkattime($name, $$h{at})." ":"").($$h{at}?($$h{cmd} =~ s/;/;;/gr):$$h{cmd});
  541. unless ($$h{ifs}) { # kein IF
  542. T2FL $name, 5, "Executing Command: ".$fhemcmd;
  543. my $fhemres = AnalyzeCommandChain ($hash->{CL}, $fhemcmd) unless (IsDisabled($name));
  544. $$h{"fhemcmd"} = $fhemcmd;
  545. push(@{$res{fhemres}}, $fhemres) if ($fhemres);
  546. T2FL $name, 5, "Pushed: ".$fhemcmd;
  547. } else { # If
  548. #Event erstellen
  549. my %r;
  550. $r{hash} = $hash;
  551. $r{if} = "IF ((".(join(") and (", @{$$h{ifs}})).")) ({1})";
  552. my $test = AnalyzeCommandChain ($hash->{CL}, $r{if});
  553. if ($test and $test ne "1") {
  554. T2FL $name, 1, "Condition $r{if} failed: ".$test;
  555. push(@{$res{fhemres}}, $test);
  556. next;
  557. } my %s = (); # make it unique
  558. push(@{$r{devs}}, grep { ! $s{$_}++ } map {/\[(.*?)[:\]]/g} @{$$h{ifs}});
  559. $r{cmd} = $fhemcmd;
  560. Talk2Fhem_addND(\%r);
  561. }
  562. }
  563. }
  564. } else {
  565. # Nothing to do
  566. T2FL $name, 1, "Nothing to do: ".$txt;
  567. }
  568. #push(@{$res{err}}, "FHEM: ".$fhemres) if $fhemres;
  569. my $status;
  570. if ($res{fhemres}) { $status = "response" }
  571. elsif (IsDisabled($name)) {$status = "disabled"}
  572. elsif ($res{err}) {$status = "err"}
  573. elsif ($res{answers}) {$status = "answers"}
  574. else {$status = "done"}
  575. readingsBeginUpdate($hash);
  576. #T2FL($hash, 1, "CL:\n".Dumper($hash->{CL}));
  577. #readingsBulkUpdate($hash, "client", $hash->{CL}{NAME});
  578. readingsBulkUpdate($hash, "ifs", join(" and ", @{$res{ifs}})) if $res{ifs};
  579. #readingsBulkUpdate($hash, "cmds", join(";\n", map { ($$_{at}?Talk2Fhem_mkattime($name, $$_{at})." ":"").$$_{cmd} } @{$res{cmds}})) if $res{cmds};
  580. readingsBulkUpdate($hash, "cmds", join(";\n", map { $$_{"fhemcmd"} } @{$res{cmds}})) if $res{cmds};
  581. readingsBulkUpdate($hash, "answers", join(" und ", @{$res{answers}})) if $res{answers};
  582. readingsBulkUpdate($hash, "err", join("\n", @{$res{err}})) if $res{err};
  583. readingsBulkUpdate($hash, "response", join("\n", @{$res{fhemres}})) if $res{fhemres};
  584. readingsBulkUpdate($hash, "status", $status);
  585. ### in done könnte
  586. readingsEndUpdate($hash, 1);
  587. }
  588. $hash->{STATE} = "Ready";
  589. return;
  590. }
  591. sub Talk2Fhem_addND($) {
  592. #Log 1, Dumper $_[0]{cmds};
  593. my $hash = $_[0]{hash};
  594. unless(IsDisabled($$hash{NAME})) {
  595. my %h;
  596. for (keys %{$_[0]}) {
  597. next if /hash/;
  598. $h{$_} = $_[0]{$_};
  599. }
  600. push(@{$$hash{helper}{notifies}}, \%h);
  601. Talk2Fhem_UpdND($hash);
  602. }
  603. }
  604. sub Talk2Fhem_UpdND($) {
  605. my ($hash) = @_;
  606. my %s = (); # make it unique
  607. my @ntfs = @{$$hash{helper}{notifies}};
  608. @{$$hash{helper}{notifiers}} = grep { ! $s{$_}++ } map { @{$$_{devs}} } @ntfs;
  609. #$$hash{NOTIFYDEV} = join ",",@{$$hash{helper}{notifiers}};
  610. notifyRegexpChanged($hash, join "|",@{$$hash{helper}{notifiers}});
  611. readingsSingleUpdate($hash, "notifies", join( "\\n", map {$$_{if}} @ntfs), 1);
  612. T2FL $hash, 4, "Updated NotifyDev: ".join( "|", @{$$hash{helper}{notifiers}});
  613. T2FL $hash, 5, "Updated NotifyDev: ".Dumper @ntfs;
  614. }
  615. sub Talk2Fhem_Get($$@)
  616. {
  617. my ( $hash, $name, $opt, @args ) = @_;
  618. my $lang = Talk2Fhem_language($hash);
  619. return "\"get $name\" needs at least one argument" unless(defined($opt));
  620. if($opt eq "keylistno")
  621. {
  622. my $res;
  623. my $keylist = Talk2Fhem_parseParams(AttrVal($name, "T2F_keywordlist", ""));
  624. foreach (keys %$keylist) {
  625. $res .= $_.":\n";
  626. my $arr = Talk2Fhem_parseArray($$keylist{$_});
  627. for (my $i=0;$i<=$#$arr;$i++) {
  628. $res .= ($i+1).": ".$arr->[$i]."\n";
  629. }
  630. }
  631. return $res;
  632. }
  633. elsif($opt =~ /^\@/)
  634. {
  635. my $keylist = Talk2Fhem_parseParams(AttrVal($name, "T2F_keywordlist", ""));
  636. my $modlist = Talk2Fhem_parseParams(AttrVal($name, "T2F_modwordlist", ""));
  637. my $r;
  638. (my $kwl = $opt) =~ s/^\@//;
  639. (my $mwl = $args[0]) =~ s/^\@//;
  640. my $kw = Talk2Fhem_parseArray($$keylist{$kwl});
  641. my $mw = Talk2Fhem_parseArray($$modlist{$mwl});
  642. my $l=11;
  643. map { $l = length($_) if length($_) > $l } (@$kw);
  644. $r .= "Keywordlist".(" " x ($l-11))." : "."Modwordlist\n";
  645. $r .= $opt.(" " x ($l-length($opt)))." : ".$args[0]."\n\n";
  646. for my $i (0..$#$kw) {
  647. $r .= ($$kw[$i]//"").(" " x ($l-length(($$kw[$i]//""))))." : ".($$mw[$i]//"")."\n";
  648. }
  649. return($r);
  650. }
  651. elsif($opt eq "standardfilter")
  652. {
  653. my $atr=AttrVal($name, "T2F_filter", 0);
  654. my $filter = join(',',@{$Talk2Fhem_globals{Talk2Fhem_language($name)}{erase}});
  655. if ($atr) {
  656. return("Attribute T2F_filter is not empty please delete it.");
  657. } else {
  658. fhem("attr $name T2F_filter $filter");
  659. return("Filterattribute set to standard.");
  660. }
  661. }
  662. elsif($opt eq "log")
  663. {
  664. return($hash->{helper}{LOG});
  665. }
  666. elsif($opt eq "modificationtypes")
  667. {
  668. my $res = ref $Talk2Fhem_globals{$lang}{pass}{$args[0]} && $Talk2Fhem_globals{$lang}{pass}{$args[0]}{re} || $Talk2Fhem_globals{$lang}{pass}{$args[0]};
  669. return(($lang eq "DE" ? "Folgende RegExp wird erwartet:\n" : "The following regexp is expected:\n").$res);
  670. }
  671. elsif($opt eq "datedefinitions")
  672. {
  673. return(Dumper %{$Talk2Fhem_globals{$lang}{datephrase}});
  674. }
  675. elsif($opt eq "timedefinitions")
  676. {
  677. return(Dumper %{$Talk2Fhem_globals{$lang}{timephrase}});
  678. }
  679. elsif($opt eq "version")
  680. {
  681. return(Dumper $Talk2Fhem_globals{version});
  682. }
  683. # ...
  684. else
  685. {
  686. my $keylist = Talk2Fhem_parseParams(AttrVal($name, "T2F_keywordlist", ""));
  687. my $modlist = Talk2Fhem_parseParams(AttrVal($name, "T2F_modwordlist", ""));
  688. return "Unknown argument $opt, choose one of keylistno:noArg log:noArg standardfilter:noArg version:noArg".
  689. " @".join(" @",map { $_.":@".join(",@", sort keys %$modlist) } sort keys %$keylist).
  690. " modificationtypes:".join(",", sort keys %{$Talk2Fhem_globals{$lang}{pass}}).
  691. " datedefinitions:noArg timedefinitions:noArg";
  692. }
  693. }
  694. sub Talk2Fhem_Attr(@)
  695. {
  696. my ( $cmd, $name, $attrName, $attrValue ) = @_;
  697. # $cmd - Vorgangsart - kann die Werte "del" (löschen) oder "set" (setzen) annehmen
  698. # $name - Gerätename
  699. # $attrName/$attrValue sind Attribut-Name und Attribut-Wert
  700. return unless $init_done;
  701. #Log 1, Dumper @_;
  702. if ($attrName eq "T2F_keywordlist" or $attrName eq "T2F_modwordlist") {
  703. $defs{$name}{helper}{phrase} = undef;
  704. $defs{$name}{helper}{if} = undef;
  705. if ($cmd eq "set") {
  706. T2FL $name, 4, "Attribute checking!";
  707. return Talk2Fhem_loadList($defs{$name}, $attrName, $attrValue);
  708. } else {
  709. delete $defs{$name}{helper}{$attrName};
  710. }
  711. }
  712. if ($attrName eq "T2F_if") {
  713. if ($cmd eq "set") {
  714. return(Talk2Fhem_Loadphrase($defs{$name}, "if", $attrValue));
  715. } else {
  716. delete $defs{$name}{helper}{if};
  717. }
  718. }
  719. #elsif ($attrName eq "T2F_filter") {
  720. #Log 1, "HALLO".$defs{global}{STATE};
  721. #my $preattr = AttrVal($name, "T2F_filter", "");
  722. #if ($preattr eq "") {
  723. # $_[3] = join(",", @{$Talk2Fhem_globals{Talk2Fhem_language($name)}{erase}}).",".$attrValue;
  724. #}}
  725. return undef;
  726. }
  727. sub Talk2Fhem_parseParams_old($)
  728. {
  729. my ($val) = @_;
  730. my %res; my $i=0;
  731. foreach my $v (split(/\n/,$val)) {
  732. # if ($v =~ /^[ \t]*(?!#)(.*?)[ \t]+=[ \t]+(.*?)[ \t]*$/) {
  733. $i++;
  734. $v =~ s/#.*//;
  735. next unless $v;
  736. if ($v =~ /^[ \t]*(.*?)[ \t]+=[ \t]+(.*?)[ \t]*$/) {
  737. return ("#$i Missing REGEXP '$v'") unless ($1);
  738. return ("#$i Missing Command '$v'") unless ($2);
  739. $res{$1} = $2;
  740. } else {
  741. return ("#$i Syntaxerror. '$v'\nDid you forget whitespace before or after '='");
  742. }
  743. }
  744. return(\%res);
  745. }
  746. sub Talk2Fhem_realtrim($)
  747. {
  748. my $string = shift;
  749. $string =~ s/^[\s\t\n]*|[\s\t\n]*$//g;
  750. # $string =~ s/^[\s\t\n]*|[\s\t\n]*$//g;
  751. return $string;
  752. }
  753. sub Talk2Fhem_normalize($)
  754. {
  755. my $string = shift;
  756. #mach probleme bei "ue"
  757. # $string =~ s/\s{2,}|\b\w\b|\t|\n|['".,;:\!\?]/ /g;
  758. $string =~ s/\s{2,}|\t|\n|['".,;\!\?]/ /g;
  759. return $string;
  760. }
  761. sub Talk2Fhem_parseParams($)
  762. {
  763. my ($def) = @_;
  764. my $val = $def;
  765. my $i=0;
  766. my %hres;
  767. my @res;
  768. while ($val =~ /(.*?)[ \t]+=[ \t\n]+((.|\n)*?)(?=(\n.*?[ \t]+=[ \t\n]|$))/) {
  769. my $pre = Talk2Fhem_realtrim($`);
  770. if ($pre) {
  771. return ("Syntaxerror: $pre") if ($pre !~ /^#/);
  772. }
  773. $val = $';
  774. next if (Talk2Fhem_realtrim($1) =~ /^#/);
  775. my $key = $1;
  776. my $val = $2; my $r;
  777. $key = Talk2Fhem_realtrim($key);
  778. foreach my $line (split("\n", $val)) {
  779. $line =~ s/#.*//;
  780. $line = Talk2Fhem_realtrim($line);
  781. $r .= $line;
  782. }
  783. if ( wantarray ) {
  784. push(@res, {key => $key, val => $r});
  785. } else {
  786. $hres{$key} = $r;
  787. }
  788. }
  789. return ("Syntaxerror: $val") if (Talk2Fhem_realtrim($val));
  790. return(@res) if ( wantarray );
  791. return(\%hres);
  792. }
  793. sub Talk2Fhem_parseArray($;$$)
  794. {
  795. my ($val, $split, $keep) = @_;
  796. $split = "," unless $split;
  797. my @r = map {Talk2Fhem_realtrim($_)} quotewords($split, $keep, $val);
  798. return(\@r);
  799. }
  800. sub Talk2Fhem_loadList($$;$)
  801. {
  802. my $hash = shift;
  803. my $type = shift;
  804. my $list = (shift || AttrVal($hash->{NAME}, $type, ""));
  805. $list = Talk2Fhem_parseParams($list);
  806. return ("Error while parsing Keywordlist.\n$list" ) unless(ref($list) eq "HASH");
  807. delete $hash->{helper}{T2F_andwordlist} if $type eq "T2F_keywordlist";
  808. delete $hash->{helper}{$type};
  809. foreach (keys %$list) {
  810. # $$list{$_} = Talk2Fhem_parseArray($$list{$_});
  811. $hash->{helper}{T2F_andwordlist}{$_} = Talk2Fhem_parseArray($$list{$_}) if /^\&/;
  812. $hash->{helper}{$type}{s/^\&//r} = Talk2Fhem_parseArray($$list{$_});
  813. }
  814. # my $modlist = Talk2Fhem_parseParams(AttrVal($name, "T2F_modwordlist", ""));;
  815. # return ("Error while parsing Modwordlist.\n$modlist" ) unless(ref($modlist) eq "HASH");
  816. # foreach (keys %$modlist) {
  817. ## $$modlist{$_} = Talk2Fhem_parseArray($$modlist{$_});
  818. # $hash->{helper}{modlist}{$_} = Talk2Fhem_parseArray($$modlist{$_});
  819. # }
  820. }
  821. sub Talk2Fhem_language($)
  822. {
  823. my ($name) = @_;
  824. my $lang = AttrVal($name, "T2F_language", AttrVal("global", "language", "DE"));
  825. $lang=uc($lang);
  826. $lang = "DE" unless $lang =~ /DE|EN/;
  827. return($lang);
  828. }
  829. sub Talk2Fhem_mkattime($$) {
  830. my $myname = $_[0];
  831. my $i = $_[1];
  832. my @ltevt = localtime($i);
  833. my $d=0; my $dev="at_".$myname."_".$i."_".$d;
  834. while ($defs{$dev}) {$dev = "at_".$myname."_".$i."_".++$d}
  835. return("define at_".$myname."_".$i."_".$d." at "
  836. .($ltevt[5]+1900)
  837. ."-".sprintf("%02d", ($ltevt[4]+1))
  838. ."-".sprintf("%02d", $ltevt[3])
  839. ."T".sprintf("%02d", $ltevt[2])
  840. .":".sprintf("%02d", $ltevt[1])
  841. .":".sprintf("%02d", $ltevt[0]));
  842. }
  843. sub Talk2Fhem_exec($$$) {
  844. my %assires;
  845. my %lastcmd;
  846. sub Talk2Fhem_get_time_by_phrase($$$$$%);
  847. sub Talk2Fhem_addevt($$$$;$$);
  848. sub Talk2Fhem_err($$$;$);
  849. sub Talk2Fhem_filter($$);
  850. sub Talk2Fhem_escapeumlauts($;$);
  851. sub Talk2Fhem_test($$);
  852. my ($txt, $me, $myname) = @_;
  853. $me->{helper}{LOG}="";
  854. #my $kl = $me->{helper}{T2F_keywordlist};
  855. #my $ml = $me->{helper}{T2F_modwordlist};
  856. (Talk2Fhem_err($myname, "No Text given!",\%assires,1) && return(%assires)) unless $txt;
  857. my $lang = Talk2Fhem_language($myname);
  858. my %Talk2Fhem = %{$Talk2Fhem_globals{$lang}};
  859. T2FL($myname, 5, "Talk2Fhem Version: ".$Talk2Fhem_globals{version});
  860. T2FL($myname, 3, "Decoding Text: ".$txt);
  861. my $t2ffilter = AttrVal($myname,"T2F_filter",0);
  862. T2FL($me, 5, "Using User Filter: ".$t2ffilter) if $t2ffilter;
  863. my $lastevt;
  864. my $lastif;
  865. my $lastifmatch;
  866. my $origin = AttrVal($myname, "T2F_origin", "");
  867. $txt =~ s/$origin//;
  868. $origin = $&;
  869. $txt = Talk2Fhem_normalize(Talk2Fhem_realtrim($txt));
  870. readingsSingleUpdate($me, "origin", $origin, 1);
  871. #:START
  872. #Zeiten könnten auch ein und enthalten deswegen nicht wenn auf und eine Zahl folgt
  873. my @cmds = split(/ und (?!$Talk2Fhem_globals{DE}{numberre})/, $txt);
  874. # CHECK if $cmd[0] hit Talk2Fhem_test. Unless dont split. And make a deeper analysis.
  875. #if ($#cmd)
  876. # before test remove time and if phrases simple
  877. # unless (Talk2Fhem_test($me, $cmds[0]))
  878. # Nun schauen wir mal was vor und nach dem und ist.
  879. # könnte phrasentreffer auf kompletten satz ausschluss geben
  880. @cmds = grep { $_ } @cmds;
  881. # Tiefe UND analyse
  882. # Präposition und Artikel
  883. my $art = '(([ai][nm]|beim?|auf|unter|hinter|über|vo[rnm]) )?((de[rmns]|die|das) )?';
  884. my $uart = '(eine?[smnr]?)';
  885. my @regtargets;
  886. for (keys %{$me->{helper}{T2F_andwordlist}}) { push(@regtargets, \@{$me->{helper}{T2F_andwordlist}{$_}}) };
  887. my $reg = '\b'.${art}.'('.join("|",map { @$_ } @regtargets).')\b';
  888. Log 1, $reg;
  889. my @andlockinfo; #my @sets;
  890. for (@regtargets) {
  891. my $regtarget = join("|", @$_);
  892. Log 1, $regtarget;
  893. # d Satzteil x Teilposition y cmds Position
  894. my $d=0; my $x=0; my $y=0; my $prepost; my $prepre;
  895. for (@cmds) {
  896. # erkenne target
  897. if (/\s?\b(${art})(${regtarget})\b/i)
  898. {
  899. my ($pre,$post,$target) = ($`,$',$&);
  900. my @targets=($target);
  901. # weitere targets. Bei Auflistungen ohne und
  902. while ($post =~ s/(,|\s)+(${art}(${regtarget}))\b//i) {
  903. push(@targets, $2);
  904. };
  905. my $xx = -1;
  906. # Fülle andlockinfo hash
  907. for (@targets) {
  908. my $s = $andlockinfo[$d]{count}++;
  909. my $ref = \%{$andlockinfo[$d]{part}[$x+(++$xx)]};
  910. # füge targets hinzu mit korregierten leerzeichen
  911. $$ref{target} .= ((($$ref{target}||"")!~/\s$/ and $_!~/^\s/)?" ":"").$_;
  912. $$ref{no} = $y;
  913. $$ref{pre} = $pre || undef;
  914. if ($post) {
  915. #entferne Targets anderer Kategorien
  916. $post =~ s/\s?$reg\s?/ /gi;
  917. $post = undef if $post =~ /^\s*$/;
  918. $$ref{post} = $post || undef;
  919. }
  920. }
  921. $x += $xx if $xx ne -1;
  922. $andlockinfo[$d]{pre} = ($pre || $prepre) if $pre || $prepre;# and !$andlockinfo[$d]{pre};
  923. $andlockinfo[$d]{post} = ($post || $prepost) if $post || $prepost;# and !$andlockinfo[$d]{post};
  924. $prepost=undef; $prepre=undef;
  925. # wenn es ein post existiert fange neuen Satzteil an und speichere pre und post
  926. if ($andlockinfo[$d]{count} > 1 and $post) {
  927. $prepost = $andlockinfo[$d]{post};
  928. $prepre = $andlockinfo[$d]{pre};
  929. $d++;
  930. $x=-1;
  931. }
  932. }
  933. else {
  934. $d++; $x=-1;
  935. }
  936. $x++; $y++;
  937. }
  938. }
  939. #print Dumper @andlockinfo;
  940. #Bilde komplette Sätze und füge nicht berücksichtigte sätze an richtiger position ein
  941. my @ncmds; my $o=0;
  942. for (my $i=0;$i<=$#andlockinfo;$i++) {
  943. my $a = $andlockinfo[$i];
  944. if (ref $a) {
  945. for ($o..($$a{part}[0]{no}-1)) {
  946. push(@ncmds, $cmds[$_]);
  947. }
  948. for my $p (@{$$a{part}}) {
  949. push(@ncmds, ($$p{pre}//$$a{pre}||"").$$p{target}.($$p{post}//$$a{post}||""));
  950. $o = $$p{no}+1;
  951. }
  952. }
  953. }
  954. push(@ncmds, splice(@cmds,$o));
  955. @cmds = @ncmds ? @ncmds : @cmds;
  956. T2FL($myname, 4, "After correction:\n".(join("\n", @cmds)));
  957. foreach (@cmds) {
  958. next unless $_;
  959. my $cmd = $_;
  960. my $specials;
  961. $$specials{origin} = $origin;
  962. T2FL($myname, 4, "Command part: '$cmd'");
  963. my $rawcmd = $cmd;
  964. my $time = time;
  965. ### wieder und dann/danach am Anfang legen die zeit auf das vorherige event
  966. if ($lastevt and ($cmd =~ /\bwieder |^(dann|danach).*/i)) {
  967. T2FL($myname, 5, "Word again found. Reusing timeevent. ".localtime($lastevt));
  968. $time = $lastevt;
  969. }
  970. my $evtime = Talk2Fhem_get_time_by_phrase($myname, $time, $time, \$cmd, \$specials, %{$Talk2Fhem{datephrase}});
  971. $evtime = Talk2Fhem_get_time_by_phrase($myname, $evtime, $time, \$cmd, \$specials, %{$Talk2Fhem{timephrase}});
  972. #T2FL($myname, 4, "Extracted Timephrase. '$$specials{timephrase}'") if $$specials{timephrase};
  973. T2FL($myname, 4, "Extracted Timephrase. '$$specials{timephrase}'") if $$specials{timephrase};
  974. T2FL($myname, 5, "Commandpart after datedecoding. '$cmd'") if $cmd ne $rawcmd;
  975. unless($evtime) {
  976. Talk2Fhem_err($myname, "Error while time calculating: $rawcmd",\%assires,1);
  977. next;
  978. }
  979. $cmd = Talk2Fhem_filter($myname, $cmd);
  980. if ($time < $evtime) {
  981. T2FL($myname, 4, "Eventtime found: ".localtime($evtime));
  982. $lastevt=$evtime;
  983. } elsif ($time-10 > $evtime) {
  984. T2FL($myname, 3, "Time is in past: $time $evtime");
  985. $lastevt=0;
  986. } elsif ($lastevt) {$lastevt++}
  987. foreach my $phr (@{$me->{helper}{if}}) {
  988. my $sc = Talk2Fhem_addevt($myname, $phr, $lastevt, $cmd, \%assires, $specials);
  989. }
  990. push(@{$$specials{ifs}} , @{$lastif}) if ($lastif);
  991. $lastif = $$specials{ifs};
  992. $lastifmatch .= ($lastifmatch ? " und " : " ").$$specials{match};
  993. $$specials{ifmatch} = $lastifmatch;
  994. $cmd = Talk2Fhem_normalize(Talk2Fhem_realtrim($cmd));
  995. # Maximal 2 Wörter vor dem wieder, ansonsten wird von einem neuen Kommando ausgegangen.
  996. # dann wird nach der letzten Zahl, wort länger als 3 buchstaben oder wahr falsch wörter gesucht.
  997. #if ($cmd =~ /^.?(\S+\s){0,2}wieder.* (\S+)$/i) {
  998. if (%lastcmd and
  999. ( $cmd =~ /wieder\b.*($Talk2Fhem{pass}{float})/i ||
  1000. $cmd =~ /wieder\b.*($Talk2Fhem{pass}{integer})/i ||
  1001. $cmd =~ /wieder\b.*($Talk2Fhem{pass}{word})/i ||
  1002. $cmd =~ /wieder\b.*($Talk2Fhem{pass}{true})/i ||
  1003. $cmd =~ /wieder\b.*($Talk2Fhem{pass}{false})/i ||
  1004. $cmd =~ /wieder\b.*($Talk2Fhem{numberre})/i)) {
  1005. $$specials{dir} = $1;
  1006. # hier erfolgt ein hitcheck, damit erkannt wird ob das kommando ohne wieder ein eigenständiger befehl ist.
  1007. # frage ist ob zusätzlich über specials eine rückgabe gegeben werden soll ob die konfig "wieder" fähig ist. z.b. überhaupt ein $n vorhanden ist.
  1008. # ist der 2 wörter check noch notwendig?
  1009. unless (Talk2Fhem_test($me, $cmd =~ s/\s?wieder\s/ /r)) {
  1010. #Vorhiges Kommando mit letztem wort als "direction"
  1011. # Log 1, Dumper Talk2Fhem_test($me, $_ =~ s/\s?wieder\s/ /r);
  1012. T2FL($myname, 4, "Word again with direction ($$specials{dir}) found. Using last command. ${$lastcmd{phr}}{key}");
  1013. Talk2Fhem_addevt($myname, $lastcmd{phr}, $lastevt, $lastcmd{cmd}, \%assires, $specials);
  1014. next;
  1015. } else {
  1016. T2FL($myname, 3, "Again word ignored because Command matches own Phrase!");
  1017. $$specials{dir} = undef;
  1018. }
  1019. }
  1020. #wieder wird nicht mehr benötigt
  1021. $cmd =~ s/\bwieder\b|^(dann|danach) / /g;
  1022. $cmd = Talk2Fhem_normalize(Talk2Fhem_realtrim(Talk2Fhem_filter($myname, $cmd)));
  1023. T2FL($myname, 4, "Command left: '$cmd'") if $rawcmd ne $cmd;
  1024. my $sc;
  1025. #foreach my $phr (keys(%{$Talk2Fhem_phrase{$myname}})) {
  1026. foreach my $phr (@{$me->{helper}{phrase}}) {
  1027. #Teste Phrasenregex
  1028. $lastcmd{phr} = $phr;
  1029. $lastcmd{cmd} = $cmd;
  1030. $sc = Talk2Fhem_addevt($myname, $phr, $lastevt, $cmd, \%assires, $specials);
  1031. # undef nicht gefunden, 0 fehler beim umwandeln, 1 erfolgreich
  1032. last if defined($sc);
  1033. }
  1034. unless ($sc) {
  1035. unless(defined($sc)) {
  1036. # undef
  1037. Talk2Fhem_err($myname, "No match: '$rawcmd'",\%assires,1);
  1038. } else {
  1039. # 0
  1040. Talk2Fhem_err($myname, "Error on Command: '$rawcmd'",\%assires,1) unless $assires{err};
  1041. last;
  1042. }
  1043. }
  1044. # eventuell ganz abbrechen bei fehler, jetzt wird noch das nächste und ausgewertet
  1045. next;
  1046. }
  1047. return(%assires);
  1048. sub Talk2Fhem_filter($$) {
  1049. my ($name, $cmd) = @_;
  1050. my $filter = AttrVal($name,"T2F_filter",$Talk2Fhem_globals{Talk2Fhem_language($name)}{erase});
  1051. unless (ref($filter) eq "ARRAY") {
  1052. $filter = Talk2Fhem_parseArray($filter);
  1053. };
  1054. for (@$filter) {
  1055. $cmd =~ s/$_/ /gi;
  1056. }
  1057. $cmd =~ s/\s{2,}/ /g;
  1058. return(Talk2Fhem_realtrim($cmd));
  1059. }
  1060. sub Talk2Fhem_get_time_by_phrase($$$$$%) {
  1061. my ($myname, $evt, $now, $cmd, $spec, %tp) = @_;
  1062. #T2FL($myname, 5, "get_time_by_phrase. Using eventtime: ".localtime($evt)." now: ".localtime($now)." command: ".$$cmd);
  1063. return(0) unless ($evt);
  1064. my @lt = localtime($evt);
  1065. my @now = localtime($now);
  1066. my $disu = AttrVal($myname, "T2F_disableumlautescaping", 0);
  1067. foreach my $key (keys(%tp)) {
  1068. my $esckey = Talk2Fhem_escapeumlauts($key, $disu);
  1069. my @opt = ($$cmd =~ /\b$esckey\b/i);
  1070. while ($$cmd =~ s/\b$esckey\b/ /i) {
  1071. $$$spec{timephrase} .= $&." ";
  1072. my %tf = %{$tp{$key}};
  1073. T2FL($myname, 4, "Timephrase found: =~ s/\\b$key\\b/");
  1074. foreach my $datemod (keys(%tf)) {
  1075. next if $datemod eq "fc";
  1076. # Suche Ersetzungsvariablen
  1077. my $dmstore = $tf{$datemod};
  1078. while ($tf{$datemod} =~ /\$(\d+)/) {
  1079. my $d=$1;
  1080. my $v = $opt[($d-1)];
  1081. if ($v !~ /^\d+$/) {
  1082. #Log 1, "KEINE Zahl ".$v;
  1083. foreach ( keys %{$Talk2Fhem_globals{DE}{numbers}} ) {
  1084. my $tmp = Talk2Fhem_escapeumlauts($_, $disu);
  1085. last if ($v =~ s/$tmp/$Talk2Fhem_globals{DE}{numbers}{$_}/i);
  1086. }
  1087. }
  1088. $tf{$datemod} =~ s/\$\d+/$v/;
  1089. }
  1090. $tf{$datemod} = eval($tf{$datemod});
  1091. T2FL($myname, 5, "TIMEPHRASEDATA mod: '$datemod' raw: '$dmstore' result: '$tf{$datemod}' opt: '@opt'");
  1092. if ($datemod eq "days") {
  1093. $evt = POSIX::mktime(0,0,12,($lt[3]+$tf{days}),$lt[4],$lt[5]) || 0;
  1094. } elsif ($datemod eq "wday") {
  1095. $evt = POSIX::mktime(0,0,12,($lt[3]-$lt[6]+$tf{wday}+(( $tf{wday} <= $lt[6] )?7:0)),$lt[4],$lt[5]) || 0;
  1096. } elsif ($datemod eq "year") {
  1097. $evt = POSIX::mktime(0,0,12,$lt[3],$lt[4],($lt[5]+$tf{year})) || 0;
  1098. } elsif ($datemod eq "month") {
  1099. $evt = POSIX::mktime(0,0,12,$lt[3],($lt[4]+$tf{month}),$lt[5]) || 0;
  1100. } elsif ($datemod eq "sec") {
  1101. $evt = POSIX::mktime(($now[0]+$tf{sec}),$now[1],$now[2],$lt[3],$lt[4],$lt[5]) || 0;
  1102. } elsif ($datemod eq "min") {
  1103. $evt = POSIX::mktime($now[0],($now[1]+$tf{min}),$now[2],$lt[3],$lt[4],$lt[5]) || 0;
  1104. } elsif ($datemod eq "hour") {
  1105. $evt = POSIX::mktime($now[0],$now[1],($now[2]+$tf{hour}),$lt[3],$lt[4],$lt[5]) || 0;
  1106. } elsif ($datemod eq "time") {
  1107. my @t = map { s/\s//gr } split(":", $tf{time});
  1108. $evt = POSIX::mktime($t[2] || 0,$t[1] || 0,$t[0],$lt[3],$lt[4],$lt[5]) || 0;
  1109. } elsif ($datemod eq "date") {
  1110. my @t = split(/\.|\s/, $tf{date});
  1111. if ($t[1]) {$t[1]--} else {$t[1] = $now[4]+1}
  1112. if ($t[2]) {if (length($t[2]) eq 2) { $t[2] = "20".$t[2] }; $t[2]=$t[2]-1900} else {$t[2] = $now[5]}
  1113. $evt = POSIX::mktime(0,0,12,$t[0], $t[1], $t[2]) || 0;
  1114. } elsif ($datemod eq "unix") {
  1115. $evt = localtime($tf{unix});
  1116. }
  1117. @now = localtime($evt);
  1118. }
  1119. @lt = localtime($evt);
  1120. }
  1121. if ($tp{$key}{fc}) {
  1122. if (ref $tp{$key}{fc} eq "CODE") {
  1123. my $lock = $evt;
  1124. $evt = &{$tp{$key}{fc}}($evt);
  1125. T2FL($myname, 4, "Time modified by function. ".$evt) if $lock != $evt;
  1126. }
  1127. }
  1128. }
  1129. return($evt);
  1130. }
  1131. sub Talk2Fhem_test($$) {
  1132. my ($hash, $cmd) = @_;
  1133. foreach my $phr (@{$hash->{helper}{phrase}}) {
  1134. my $r = Talk2Fhem_addevt($hash->{NAME}, $phr, undef, $cmd);
  1135. return $r if $r;
  1136. }
  1137. }
  1138. sub Talk2Fhem_addevt($$$$;$$) {
  1139. #print Dumper @_;
  1140. my ($myname, $phr, $lastevt, $cmd, $res, $spec) = @_;
  1141. my $success;
  1142. my $rawcmd = $cmd;
  1143. my $cmdref = \$_[3];
  1144. my $disu =AttrVal($myname, "T2F_disableumlautescaping", 0);
  1145. my %keylist = %{$defs{$myname}{helper}{T2F_keywordlist}} if $defs{$myname}{helper}{T2F_keywordlist};
  1146. my %modlist = %{$defs{$myname}{helper}{T2F_modwordlist}} if $defs{$myname}{helper}{T2F_modwordlist};
  1147. #T2FL($me, 5, "Using lists:\n".Dumper(%keylist, %modlist));
  1148. # my @phrs = map { Talk2Fhem_realtrim($_) } split(/[\t\s]*\&\&[\t\s]*/, $$phr{key});
  1149. my @hitnokeylist = @{$$phr{hitnokeylist}};
  1150. my @fphrs = @{$$phr{regexps}};
  1151. my $pmatch;
  1152. my $punmatch = $cmd;
  1153. my @dir = ($$spec{origin});
  1154. T2FL($myname, 5, "$myname Evaluate search:\n$cmd =~ /$$phr{key}/i") if ref $res;
  1155. for my $fphr (@fphrs) {
  1156. # if (my @d = ($cmd =~ qr/$fphr/i))
  1157. if ($fphr =~ s/^\?//){
  1158. my @d = ($cmd =~ /$fphr/i);
  1159. my $m = $&;
  1160. #Log 1, "A: ".$fphr;
  1161. #Log 1, "A: ".Dumper $m;
  1162. #Log 1, "B: ".Dumper @d;
  1163. my $b = () = $fphr =~ m/(?<!\\)\((?!\?)/g;
  1164. #Log 1, "C: $#d".Dumper $b;
  1165. # Wenn die klammer kein erfolg hat wird auch @d nicht gefüllt und muß manuell gefüllt werden
  1166. if ($#d == -1) {
  1167. for (1..$b) {
  1168. push(@d, undef);
  1169. # push(@d, "");
  1170. }
  1171. }
  1172. push(@dir, @d);
  1173. next if $m eq "?";
  1174. $pmatch .= $m;
  1175. $punmatch =~ s/$m//gi;
  1176. #$cmd =~ s/$m//gi;
  1177. } elsif ($fphr =~ /^\!/) {
  1178. return if (eval { $cmd =~ /$'/i });
  1179. } elsif (my @d = ($cmd =~ /$fphr/i ) ){
  1180. my $m = $&;
  1181. $pmatch .= $m;
  1182. $punmatch =~ s/$m//gi;
  1183. #$cmd =~ s/$m//gi;
  1184. # Klammerinhalt speichern wenn Klammer vorhanden
  1185. push(@dir, @d) if $fphr =~ /(?<!\\)\((?!\?)/;
  1186. # push(@dir, @d) if $fphr =~ /\((?!\?)/;
  1187. } else {
  1188. #T2FL($myname, 5, "$myname No hit with:\n$cmd =~ /$fphr/i");
  1189. return;
  1190. }
  1191. }
  1192. $$spec{match} = $pmatch;
  1193. $$spec{unmatch} = $punmatch;
  1194. return(1) unless ref $res;
  1195. T2FL($myname, 5, "Command after Phrasecheck: ".$cmd) if $cmd ne $rawcmd;
  1196. T2FL($myname, 5, "Keylists: ".Dumper @hitnokeylist);
  1197. T2FL($myname, 5, "Filled lists: ".Dumper @fphrs);
  1198. T2FL($myname, 5, "Words: ".Dumper @dir);
  1199. #$pmatch=Talk2Fhem_realtrim($pmatch);
  1200. $punmatch=Talk2Fhem_realtrim($punmatch);
  1201. T2FL($myname, 5, "Match: ".$pmatch);
  1202. T2FL($myname, 5, "Unmatch: ".$punmatch);
  1203. #$success;
  1204. T2FL($myname, 4, "Hit with phrase: qr/$$phr{key}/i");
  1205. my %react;
  1206. %react=%{$$phr{val}};
  1207. ####### TODO:
  1208. ####### $[1..n,n,...] für multiple hit auswahl !!! Was ist das trennzeichen in der ausgabe?
  1209. my %exec;
  1210. my @types = ("if", "cmd","answer");
  1211. my $mainbracket;
  1212. foreach my $type (@types) {
  1213. my $raw = $react{$type};
  1214. next unless $raw;
  1215. my $mainbracket = (sort { $b <=> $a } ($raw =~ /\$(\d+)/g))[0] unless ($mainbracket);
  1216. my $do = $raw;
  1217. my $dirbracket = $react{offset};
  1218. T2FL($myname, 5, "Handle reaction $type: $raw");
  1219. if ($raw) {
  1220. # Suche Ersetzungsvariablen
  1221. $do =~ s/\!\$\&/$punmatch/g;
  1222. $do =~ s/\$\&/$pmatch/g;
  1223. $do =~ s/\$DATE/$$spec{timephrase}/g;
  1224. my $tagain = ($$spec{dir} ? "wieder" : "");
  1225. $do =~ s/\$AGAIN/$tagain/g;
  1226. $do =~ s/\$TIME/$lastevt/g;
  1227. $do =~ s/\$NAME/$myname/g;
  1228. # $do =~ s/\$ORIGIN/$$spec{origin}/g;
  1229. $do =~ s/\$IF/$$spec{ifmatch}/g;
  1230. while ($do =~ /\$(\d+)\@/) {
  1231. my $no = $1;
  1232. my @keywords;
  1233. # wenn kein @array in klammer clipno
  1234. unless ($hitnokeylist[$no]) {
  1235. T2FL($myname, 5, "Clipnumber $no is no array! Try to extract by seperator '|'");
  1236. my @cs = map { my @t = split('\|', $_ =~ s/^\(|\)$//gr); \@t } $$phr{key} =~ /(?<!\\)\((?!\?).*?\)/g;
  1237. @keywords = @{$cs[($no-1)]};
  1238. #wenn keine Liste in Klammer ist
  1239. if ($#keywords == -1) {
  1240. Talk2Fhem_err($myname, T2FL($myname, 1, "Clipnumber $no includes no array in '$$phr{key}!"),$res,1);
  1241. return(0);
  1242. }
  1243. } elsif ($hitnokeylist[$no]) {
  1244. @keywords = map { Talk2Fhem_escapeumlauts($_, $disu) } @{$keylist{$hitnokeylist[$no]}};
  1245. }
  1246. my $i;
  1247. for($i=0;$i<=$#keywords;$i++){
  1248. last if $dir[$no] =~ /^$keywords[$i]$/i;
  1249. }
  1250. my $k = ($$spec{dir} and ($no) == $mainbracket) ? $$spec{dir} : $keywords[$i];
  1251. T2FL($myname, 5, "Simple bracket selection (No. $no) with Keyword $i: '$k'");
  1252. $do =~ s/\$$no\@(?!(\[|\{|\(|\d))/$k/;
  1253. }
  1254. # Einfache Variablenersetzung ohne Array oder Hash
  1255. while ($do =~ /\$(\d+)(?!(\[|\{|\(|\d))/) {
  1256. my $r = ($$spec{dir} and ($1) == $mainbracket) ? $$spec{dir} : $dir[$1];
  1257. T2FL($myname, 5, "Simple bracket selection (No. $1): '$r'") if $r;
  1258. $do =~ s/\$$1(?!(\[|\{|\(|\d))/${r}/;
  1259. }
  1260. T2FL($myname, 4, "Replaced bracket: $raw -> $do") if $raw ne $do;
  1261. # while ($do =~ s/(.*)\$(\d+)(\[|\{|\()(.*?)(?3)/$1###/) {
  1262. # while ($do =~ s/(.*)\$(\d+)(\[|\{|\()(.*?)(\]|\}|\))/$1###/) {
  1263. while ($do =~ /(.*)\$(\d+)(?=\[|\{|\()/) {
  1264. my $pre = $1;
  1265. my $clipno = $2;
  1266. my $post = $';
  1267. my ($found, $rest) = extract_bracketed( $post, '{}[]()' );
  1268. unless ($found) {
  1269. Talk2Fhem_err($myname, T2FL($myname, 1, "'$raw': Fehler in Kommandoteilmodifikator Nr. '\$$clipno' nach: '$pre'"),$res,1);
  1270. return(0);
  1271. }
  1272. #Klammer aus Value in Hash überführen
  1273. $do = $pre."###".$rest;
  1274. $found =~ /(.)(.*)./;
  1275. my $utype = $1;
  1276. my $uhash = $2;
  1277. T2FL($myname, 4, "Advanced bracket replacement. \$$clipno$uhash = $do");
  1278. if ($uhash =~ /@(\w+)/) {
  1279. if ($modlist{$1}) {
  1280. $uhash = $`.'"'.Talk2Fhem_escapeumlauts(join('","', @{$modlist{$1}}), $disu).'"'.$' ;
  1281. #ersetze ,, durch "","",
  1282. # zwei mal weil immer eins zu weit geschoben wird
  1283. #### ist noch notwendig???
  1284. $uhash =~ s/([\[,])([,\]])/$1""$2/g;
  1285. $uhash =~ s/([\[,])([,\]])/$1""$2/g;
  1286. T2FL($myname, 5, "Adding modlist: ".$uhash);
  1287. } else {
  1288. Talk2Fhem_err($myname, T2FL($myname, 1, "Unbekannte modwordlist in '$$phr{key}' \@$1"),$res,1);
  1289. return(0);
  1290. }
  1291. }
  1292. my $hash;
  1293. if ($utype eq "[") {
  1294. $hash = Talk2Fhem_parseArray($uhash)
  1295. } elsif ($utype eq "{") {
  1296. #$hash = eval($uhash)
  1297. my $harr = Talk2Fhem_parseArray($uhash); my $i=0;
  1298. for (@$harr) {
  1299. my $h = Talk2Fhem_parseArray($_, "=>");
  1300. $$hash{$$h[0]} = {val=>$$h[1],order=>$i++};
  1301. }
  1302. } elsif ($utype eq "(") {
  1303. ##### klappt nicht weil in while regex nicht bis zur schließenden klammer getriggert wird wenn vorher ein } oder ] kommt
  1304. #$hash = eval($uhash);
  1305. T2FL($myname, 1, '$n() has no function at this moment. Possible worng Syntax: '.$$phr{key});
  1306. next;
  1307. } else {
  1308. #sollte eigentlich nie eintreffen weil auf die zeichen explizit gesucht wird
  1309. T2FL($myname, 1, "Unkown modwordtype ($utype) in '$$phr{key}'");
  1310. next;
  1311. }
  1312. #aktuelles Wort im Key auswählen
  1313. if (($clipno-1) > $#dir) {
  1314. T2FL($myname, 1, "Not enough clips in phrase '$$phr{key} =~ $raw'");
  1315. next;
  1316. }
  1317. my $d = ($$spec{dir} and ($clipno) == $mainbracket) ? $$spec{dir} : $dir[$clipno];
  1318. T2FL($myname, 4, "Keyword (".($clipno)."): '$d'");
  1319. # Wort übersetzen
  1320. if (ref($hash) eq "HASH") {
  1321. T2FL($myname, 5, "HASH evaluation:\n".Dumper($hash));
  1322. #my $passed=0;
  1323. foreach my $h (sort {$$hash{$a}{order} <=> $$hash{$b}{order} } keys(%$hash)) {
  1324. #sollte eigentlich in den syntaxcheck
  1325. unless (defined $$hash{$h}{val}) {
  1326. T2FL($myname, 1, "Empty replacementstring! $h");
  1327. #return(0);
  1328. next;
  1329. };
  1330. next if ($h eq "else");
  1331. unless ($h =~ /^\/.*\/$/ or defined ${$Talk2Fhem{pass}}{$h}) {
  1332. T2FL($myname, 1, "Replacementtype unkown! $h");
  1333. #return(0);
  1334. next;
  1335. };
  1336. #$passed=1;
  1337. next if ($h eq "empty");
  1338. next unless $d;
  1339. my $re;
  1340. my $fc;
  1341. if ($h =~ /^\/(.*)\/$/) {
  1342. $re = $1;
  1343. } else {
  1344. $re = ${$Talk2Fhem{pass}}{$h};
  1345. if (ref($re) eq "HASH") {
  1346. $fc=$$re{fc};
  1347. $re=$$re{re};
  1348. }
  1349. }
  1350. $re = Talk2Fhem_escapeumlauts($re, $disu);
  1351. if ($d =~ qr/$re/i) {
  1352. my $rp = $$hash{$h}{val};
  1353. if (ref $fc eq "CODE") {
  1354. T2FL($myname,5,"Functionmod '$fc' $rp");
  1355. my @res = $d =~ qr/$re/i;
  1356. $rp = &$fc(@res);
  1357. } elsif ($fc) {
  1358. T2FL($myname,5,"Functionmod '$$fc' $rp");
  1359. my $ev = eval($fc);
  1360. $rp =~ s/$re/$ev/gi;
  1361. }
  1362. T2FL($myname, 5, "Word found ($h): '$d' replace with '$rp'");
  1363. $do =~ s/###/$rp/;
  1364. last;
  1365. }
  1366. }
  1367. # empty != undef
  1368. # if (defined($d) and $d =~ qr/${$Talk2Fhem{pass}}{empty}/ and ($$hash{empty}{val} or (! $$hash{empty}{val} and $$hash{else}{val}))) {
  1369. # empty undef
  1370. if (! defined($d) or $d =~ qr/${$Talk2Fhem{pass}}{empty}/) {
  1371. #$d existiert nicht
  1372. my $e = ($$hash{empty}{val} || $$hash{else}{val});
  1373. T2FL($myname, 5, "Empty word replace with '$e'");
  1374. $do =~ s/###/$e/;
  1375. }
  1376. #########
  1377. if ($do =~ /###/) {
  1378. #Vergleich fehlgeschlagen
  1379. if ($$hash{else}{val}) {
  1380. T2FL($myname, 5, "Unkown word '$d' replace with '$$hash{else}{val}'");
  1381. $do =~ s/###/$$hash{else}{val}/;
  1382. } else {
  1383. T2FL($myname, 1, "HASH Replacement Failed! $do");
  1384. #%$res = undef;
  1385. #return();
  1386. }
  1387. }
  1388. }
  1389. if (ref($hash) eq "ARRAY") {
  1390. my $else="";
  1391. my $empty="";
  1392. # keywords else und empty löschen und nächsten wert als parameter nehmen
  1393. @$hash = grep {
  1394. if ("$_" eq "else") { $else = " "; 0 }
  1395. else { if ($else eq " ") { $else = $_; 0 }
  1396. else { 1 } } } @$hash;
  1397. @$hash = grep {
  1398. if ("$_" eq "empty") { $empty = " "; 0 }
  1399. else { if ($empty eq " ") { $empty = $_; 0 }
  1400. else { 1 } } } @$hash;
  1401. T2FL($myname, 5, "ARRAY evaluation: else: $else empty: $empty\narray: @$hash");
  1402. # if (($d =~ qr/${$Talk2Fhem{pass}}{empty}/) and defined($d)) {
  1403. my $intd = $d;
  1404. foreach ( keys %{$Talk2Fhem_globals{Talk2Fhem_language($myname)}{numbers}} ) {
  1405. my $tmp = Talk2Fhem_escapeumlauts($_, $disu);
  1406. if ($d =~ /$tmp/i) {
  1407. $intd = $Talk2Fhem_globals{Talk2Fhem_language($myname)}{numbers}{$_};
  1408. last;
  1409. };
  1410. }
  1411. T2FL($myname, 5, "Numeral word found. '$d' converted to; $intd");
  1412. if (($d =~ qr/${$Talk2Fhem{pass}}{empty}/) or ! defined($d)) {
  1413. T2FL($myname, 5, "Empty word replace with! $empty");
  1414. $do =~ s/###/$empty/;
  1415. } elsif (IsInt($intd)) {
  1416. unless ($$hash[$intd]) {
  1417. my $err = T2FL($myname, 3, "Field #$intd doesn't exist in Array!");
  1418. if ($else eq "") {
  1419. Talk2Fhem_err($myname, $err, $res,1);
  1420. return(0);
  1421. }
  1422. } else {
  1423. T2FL($myname, 5, "Integer ($intd) used for array selection! $$hash[$intd]");
  1424. $do =~ s/###/$$hash[$intd]/ if $$hash[$intd];
  1425. }
  1426. } elsif ($d) {
  1427. my @keywords;
  1428. # wenn kein @array in klammer clipno
  1429. unless (defined($hitnokeylist[$clipno])) {
  1430. T2FL($myname, 5, "Clipnumber $clipno is no array! Try to extract by seperator '|'");
  1431. # my @cs = map { my @t = split('\|', $_ =~ s/^\(|\)$//gr); \@t } $$phr{key} =~ /(?<!\\)\((?!\?).*?\)/g;
  1432. # my @cs = map { my @t = split('\|', $_ =~ s/^\(|\)$//gr); \@t } $$phr{key} =~ /(?<! \\ ) \( (?! \? ) (?: (?R) | [^()]+ )+ \) /xg;
  1433. # Klammern extrahieren
  1434. my @cs = ("onlysometext".$$phr{key});
  1435. #unshift(@cs, undef) if $$phr{key} =~ /^\(/;
  1436. for (my $i=0; $i<=$#cs; $i++) {
  1437. my $c = $cs[$i];
  1438. #T2FL($myname, 5, "C: ".Dumper $c);
  1439. if ($c =~ /^\(\?.*\)$/) {
  1440. # Perl Special bracket delete it.
  1441. splice(@cs, $i, 1);
  1442. $c = $cs[$i];
  1443. }
  1444. (my $locked = $c) =~ s/^\((.*)\)$/$1/g;
  1445. my @add = extract_multiple($locked, [sub { extract_bracketed($_[0], '()') }],undef, 1);
  1446. splice(@cs, ($i+1) ,0 , @add);
  1447. last if $i > 10;
  1448. }
  1449. #T2FL($myname, 5, "CS: ".Dumper @cs);
  1450. # @keywords = @{$cs[($clipno-1)]};
  1451. # Log 1, Dumper @cs;
  1452. # @cs = grep { /^\(/ } @cs;
  1453. # Log 1, Dumper @cs;
  1454. # Log 1, "-----> ".$cs[($clipno-1)];
  1455. (my $clip = $cs[($clipno)]) =~ s/^\(|\)$//g;
  1456. #T2FL($myname, 5, "clip: ".Dumper $clip);
  1457. # push(@keywords, split('\|', $clip) extract_bracketed($clip, '()'));
  1458. my @extract;
  1459. for (extract_multiple($clip, [sub { extract_bracketed($_[0], '()') }])) {
  1460. #T2FL($myname, 5, "EM: ".Dumper $_);
  1461. if ($_ =~ /^\(/) {
  1462. push (@extract, "") if ($#extract eq -1);
  1463. $extract[$#extract] .= $_;
  1464. next;}
  1465. if (s/^\|// or /^[^(]/) {
  1466. if ($_ ne "") {
  1467. push(@extract, split('\|', $_));
  1468. } else {
  1469. push(@extract, "");
  1470. }
  1471. } else {
  1472. push (@extract, "") if ($#extract eq -1);
  1473. $extract[$#extract] .= $_;
  1474. }
  1475. }
  1476. #T2FL($myname, 5, "A: ".Dumper @extract);
  1477. #@keywords = map { /^\(/ ? $_ : split('\|', $_=~s/^\||\|$//gr) } extract_multiple($clip, [sub { extract_bracketed($_[0], '()') }]);
  1478. @keywords = @extract;
  1479. #T2FL($myname, 5, "keywords: ".Dumper @keywords);
  1480. # @keywords = split('\|',);
  1481. #Log 1, Dumper @keywords;
  1482. #wenn keine Liste in Klammer ist
  1483. my $err = T2FL($myname, 1, "Clipnumber $clipno includes no array or integer in '$$phr{key}!");
  1484. if ($#keywords == -1 and $else eq "") {
  1485. Talk2Fhem_err($myname, $err,$res,1);
  1486. return(0);
  1487. }
  1488. } else {
  1489. @keywords = @{$keylist{$hitnokeylist[$clipno]}};
  1490. }
  1491. # T2FL($myname, 4, "Searching position of $d in @keywords");
  1492. @keywords = map { Talk2Fhem_escapeumlauts($_, $disu) } @keywords;
  1493. T2FL($myname, 4, "Searching position of '$d' in '@keywords'");
  1494. my $i=0;
  1495. foreach (@keywords) {
  1496. # if ($d =~ /^\Q$_\E$/i) {
  1497. if (eval{$d =~ /^$_$/i}) {
  1498. unless (defined($$hash[$i])) {
  1499. my $err = T2FL($myname, 1, "Not enough elements in modwordlist! Position $i in (@$hash) doesn't exist.");
  1500. if ($else eq "") {
  1501. Talk2Fhem_err($myname, $err, $res,1);
  1502. return(0);
  1503. }
  1504. } else {
  1505. T2FL($myname, 5, "Found '$d' at position $i");
  1506. $do =~ s/###/$$hash[$i]/;
  1507. }
  1508. }
  1509. $i++;
  1510. }
  1511. }
  1512. if ($do =~ /###/) {
  1513. if ($else ne "") {
  1514. T2FL($myname, 5, "Unkown word '$d' replace with '$else'");
  1515. $do =~ s/###/$else/;
  1516. } else {
  1517. T2FL($myname, 1, "ARRAY Replacement Failed! $do");
  1518. }
  1519. }
  1520. }
  1521. }
  1522. if ($do and ($do !~ /###/)) {
  1523. my $result;
  1524. #2016-01-25T02:02:00
  1525. if ($type eq "if") {
  1526. push(@{$$spec{ifs}}, $do);
  1527. #push(@{$exec{$type}}, $do);
  1528. $$cmdref = $punmatch;
  1529. T2FL($myname, 3, "New Command after IF: ".$$cmdref);
  1530. } elsif ($type eq "cmd") {
  1531. my $at;
  1532. # $at=Talk2Fhem_mkattime($myname, ($react{offset}) ? ($lastevt+$react{offset}) : $lastevt) if ($lastevt);
  1533. $$result{cmd} = $do;
  1534. $$result{at} = (($react{offset}) ? ($lastevt+$react{offset}) : $lastevt) if ($lastevt);
  1535. $$result{ifs} = $$spec{ifs} if $$spec{ifs};
  1536. #$$spec{ifs} = undef;
  1537. $success = 1;
  1538. } elsif ($type eq "answer") {
  1539. T2FL($myname, 4, "Answer eval: $do");
  1540. my $answ = eval("$do");
  1541. if (defined($answ)) {
  1542. $result = $answ;
  1543. #$exec{$type} = $answ;
  1544. $success = 1;
  1545. } else {
  1546. Talk2Fhem_err($myname, T2FL($myname, 1, "Error in answer eval: ".$do),$res,1);
  1547. return(0);
  1548. }
  1549. } elsif ($type eq "offset") {
  1550. } else {
  1551. T2FL($myname, 1, "Unkown KEY $type in Commandhash");
  1552. }
  1553. T2FL($myname, 3, "Result of $type: ".Dumper $result);
  1554. $exec{$type."s"} = $result if ($result);
  1555. #push(@{$$res{$type."s"}}, $result) if ($result);
  1556. } else {
  1557. T2FL($myname, 1, "No hit on advanced bracket selection: ".($do || $raw));
  1558. #%{$res} = undef;
  1559. $success = undef;
  1560. last;
  1561. }
  1562. }
  1563. }
  1564. #Hier Befehle ausführen.
  1565. if ($success) {
  1566. for (keys %exec) {
  1567. push(@{$$res{$_}}, $exec{$_});
  1568. }
  1569. }
  1570. return($success);
  1571. }
  1572. sub Talk2Fhem_err($$$;$) {
  1573. my ($myname, $t, $res, $v) = @_;
  1574. $v = 1 unless $v;
  1575. T2FL($myname, $v, $t);
  1576. push(@{${$res}{err}}, $t);
  1577. }
  1578. sub Talk2Fhem_escapeumlauts($;$) {
  1579. my ($cmd, $disable) = @_;
  1580. return($cmd) if $disable;
  1581. (my $res = $cmd) =~ s/[äöüß]/\\S\\S?/gi;
  1582. #Umlaute sind Arschlöcher
  1583. $res =~ s/(\\S\\S\?){2}/\\S\\S?/g;
  1584. return($res);
  1585. }
  1586. }
  1587. sub T2FL($$$) {
  1588. Log3($_[0], $_[1], $_[2]);
  1589. my $h = $_[0];
  1590. $h = ref $h && $h || $defs{$h} || return;
  1591. if ($defs{$h->{NAME}}) {
  1592. $h->{helper}{LOG} .= $_[2]."\n";
  1593. }
  1594. return($_[2]);
  1595. }
  1596. 1;
  1597. # Beginn der Commandref
  1598. =pod
  1599. =item helper
  1600. =item summary A RegExp based language control module
  1601. =item summary_DE Ein auf RegExp basierendes Sprachsteuerung Modul
  1602. =begin html
  1603. <a name="Talk2Fhem"></a>
  1604. <h3>Talk2Fhem</h3>
  1605. <ul>
  1606. The module <i>Talk2Fhem</i> is a connection between natural language and FHEM commands.
  1607. The configuration is carried out conveniently via the FHEM web frontend.<br>
  1608. For a more detailed description and further examples see <a href="http://wiki.fhem.de/wiki/Modul_Talk2Fhem">Talk2Fhem Wiki</a>.
  1609. <br><br>
  1610. <a name="Talk2Fhemdefine"></a>
  1611. <b>Define</b>
  1612. <ul>
  1613. <code>define &lt;name&gt; Talk2Fhem</code>
  1614. <br><br>
  1615. Example: <code>define talk Talk2Fhem</code>
  1616. <br><br>
  1617. The actual configuration should first be done on the FHEM side.
  1618. <br><br>
  1619. The individual language phrases are configured line by line. A configuration
  1620. always starts by the regular expression, followed by at least one space or tab
  1621. from an equal sign. <br>
  1622. The command part begins after the equals sign with a space, tab, or newline. <br> <br>
  1623. <code>&lt;regexp&gt; = &lt;command&gt;</code>
  1624. <br><br>
  1625. <b>Short refernce:</b>
  1626. <br>
  1627. <code>&lt;RegExpPart&gt; [&amp;&amp; [?!]&lt;RegExpPart_n&gt;] = [ &lt;FHEM command&gt; | { &lt;Perl code&gt; } | (&lt;option&gt; =&gt; '&lt;wert&gt;' , ... ) ]</code>
  1628. <br><br>
  1629. Example: <code>helo world = {Log 1, Helo World}</code>
  1630. <br><br>
  1631. Everything after a hashtag '#' is ignored until the end of the line.
  1632. <br><br>
  1633. &lt;regexp&gt;
  1634. <ul>Regular expression describing the text at which the command should be executed</ul>
  1635. <br><br>
  1636. &lt;command&gt;
  1637. <ul>
  1638. The executive part. The following formats are allowed:
  1639. <li>FHEM Command</li>
  1640. <li>{Perlcode}</li>
  1641. <li>(&lt;option&gt; =&gt; '&lt;value&gt;' , ... )</li>
  1642. <ul>
  1643. <br><i>&lt;option&gt;</i><br>
  1644. <li><b>cmd</b><br>FHEM command as above</li>
  1645. <li><b>offset</b><br>Integer value in seconds that is added at the time</li>
  1646. <li><b>answer</b><br>Perl code whose return is written in the Reading answer</li>
  1647. </ul>
  1648. </ul>
  1649. <br>
  1650. Bracket transfer:
  1651. <ul>
  1652. Brackets set in the regular expression can be transferred to the command section with $1, $2, [...], $n and
  1653. be modified. The following modification options are available here.
  1654. <li>$n <br>Get the word straight without change.</li>
  1655. <li>$n{&lt;type&gt; =&gt; &lt;value&gt;}<br>
  1656. Types are:<br>
  1657. true, false, integer, empty, else<br>
  1658. true, false, integer, float, numeral, /&lt;regexp&gt;/, word, empty, else<br>
  1659. <b>true</b> corresponds to: ja|1|true|wahr|ein|eins.*|auf.*|..?ffnen|an.*|rauf.*|hoch.*|laut.*|hell.*<br>
  1660. <b>false</b> corresponds to: nein|0|false|falsch|aus.*|null|zu.*|schlie..?en|runter.*|ab.*|leise.*|dunk.*<br>
  1661. <b>integer</b> Word is an integer<br>
  1662. <b>float</b> Word is a float number<br>
  1663. <b>numeral</b> Word is numeral or an integer<br>
  1664. <b>/&lt;regexp&gt;/</b> Word is matching &lt;regexp&gt;<br>
  1665. <b>word</b> Word contains 4 or more letters<br>
  1666. <b>empty</b> Word Contains an empty string<br>
  1667. <b>else</b> If none of the cases apply<br>
  1668. If a &lt;type&gt; is identified for $n the &lt;value&gt; is beeing used.
  1669. Example: <code>light (\S*) = set light $1{true =&gt; on,false =&gt; off}</code>
  1670. </li>
  1671. <li>$n[&lt;list&gt;]<br>
  1672. Comma separated list: [value1,value2,...,[else,value], [empty,value]] or [@modwordlist]<br>
  1673. If $n is a number, the word at that position in &lt;list&gt; is selected.<br><br>
  1674. If $n is a text, it searches for a list in its parenthesis in the &lt;regexp&gt; part. (a|b|c) or (@keywordlist)
  1675. In this list, $n is searched for and successively positioned in &lt;list&gt; chosen for $n.
  1676. <br>Example: <code>light .* (kitchen|corridor|bad) (\S*) on = set $1[dev_a,dev_b,dev_c] $2{true =&gt; on,false =&gt; off}</code>
  1677. </li>
  1678. <li>$n@<br>The word is adopted as it is written in the list in the &lt;regexp&gt;-part.</li>
  1679. </ul>
  1680. <br>
  1681. Environment variables::
  1682. <ul>
  1683. There are a number of variables that can be accessed in the &lt;command&gt;-part.
  1684. <li><b>$&amp;</b> Contains all found words </li>
  1685. <li><b>!$&amp;</b> Contains the rest that was not included by RegExp</li>
  1686. <li><b>$DATE</b> Contains the time and date text of the voice </li>
  1687. <li><b>$AGAIN</b> Contains the word again if it is a command again</li>
  1688. <li><b>$TIME</b> Contains the found time.</li>
  1689. <li><b>$NAME</b> Contains the devicename.</li>
  1690. <li><b>$IF</b> Contains the text of the detected T2F_if configuration.</li>
  1691. <li><b>$0</b> Contains the text of the detected T2F_origin regexp.</li>
  1692. </ul>
  1693. </ul>
  1694. <br>
  1695. <a name="Talk2Fhemset"></a>
  1696. <b>Set</b><br>
  1697. <ul>
  1698. <code>set &lt;name&gt; [!]&lt;text&gt;</code>
  1699. <br><br>
  1700. The text is sent to the module via the <i>set</i> command.
  1701. See <a href="http://fhem.de/commandref.html#set">commandref#set</a> for more help.
  1702. <li>cleartimers</li> Removes the pending time-related commands
  1703. <li>cleartriggers</li> Removes the pending event-related commands
  1704. </ul>
  1705. <br>
  1706. <a name="Talk2Fhemget"></a>
  1707. <b>Get</b><br>
  1708. <code>get &lt;name&gt; &lt;option&gt;</code>
  1709. <br><br>
  1710. Information can be read from the module via <i>get</i>.
  1711.         See <a href="http://fhem.de/commandref.html#get">commandref#get</a> for more information on "get". <br><br>
  1712. &lt;option&gt;
  1713. <ul>
  1714. <li><i>@keywordlist</i> <i>@modwordlist</i><br>
  1715. Compare the two lists word by word.</li>
  1716. <li><i>keylistno</i><br>
  1717. A list of the configured "keyword" lists. For easier positioning of "modword" lists </li>
  1718. <li><i>log</i><br>
  1719. Shows the log entries of the last command </li>
  1720. <li><i>modificationtypes</i><br>
  1721. Shows the regexp of the modificationtypes. </li>
  1722. <li><i>standardfilter</i><br>
  1723. Load the standartfilter and print it in the Attribute T2F_filter if its empty </li>
  1724. <li><i>version</i><br>
  1725. The module version</li>
  1726. </ul>
  1727. <br>
  1728. <a name="Talk2Fhemreadings"></a>
  1729. <b>Readings</b>
  1730. <ul>
  1731. <li><i>set</i><br>
  1732. Contains the last text sent via "set".
  1733. </li>
  1734. <li><i>cmds</i><br>
  1735. Contains the last executed command. Is also set with disable = 1.
  1736. </li>
  1737. <li><i>answer</i><br>
  1738. Contains the response text of the last command.
  1739. </li>
  1740. <li><i>err</i><br>
  1741. Contains the last error message. <br>
  1742. "No match" match with no RegExp. <br>
  1743. "Error on Command" see FHEM log.
  1744. </li>
  1745. <li><i>response</i><br>
  1746. Got the response of the fhem Command.
  1747. </li>
  1748. <li><i>origin</i><br>
  1749. Contains the found string of the RegExp defined in the attribute T2F_origin.
  1750. </li>
  1751. <li><i>status</i><br>
  1752. Got the status of the request.
  1753. response, disabled, err, answers, done
  1754. </li>
  1755. <li><i>ifs</i><br>
  1756. Contains the conditions at which the command will be executed.
  1757. </li>
  1758. <li><i>notifies</i><br>
  1759. Contains a list of the devices that are relevant for the currently waiting conditional commands. There is an internal notify on these devices.
  1760. </li>
  1761. </ul>
  1762. <br>
  1763. <a name="Talk2Fhemattr"></a>
  1764. <b>Attribute</b>
  1765. <ul>
  1766. <code>attr &lt;name&gt; &lt;attribute&gt; &lt;value&gt;</code>
  1767. <br><br>
  1768. See <a href="http://fhem.de/commandref.html#attr">commandref#attr</a> for more information about the attributes.
  1769. <br><br>
  1770. Attributes:
  1771. <ul>
  1772. <li><i>T2F_keywordlist</i> &lt;name&gt; = &lt;list&gt;<br>
  1773. A comma-separated list of keywords such as: rooms, names, colors, etc ... <br>
  1774. In other words, things named with a natural name. </li>
  1775. <li><i>T2F_modwordlist</i> &lt;name&gt; = &lt;list&gt;<br>
  1776. A comma seperated list of substitution words used for the keywords.
  1777. For example: device names in FHEM <br> </li>
  1778. <li><i>T2F_if</i><br>
  1779. A collection of event-driven configurations. The syntax is that of the definition. Command part is an IF condition. <br>
  1780. z.B.: (when|if) .*?door = [door] eq "open"
  1781. </li>
  1782. <li><i>T2F_filter</i><br>
  1783. Comma-separated list of RegExp generally removed. <br>
  1784. Standard: \bplease\b,\balso\b
  1785. </li>
  1786. <li><i>T2F_origin</i><br>
  1787. A RegExp which is generally removed and whose output can be accessed via $0. <br>
  1788. Can be used for user mapping.</li>
  1789. <li><i>T2F_language</i>DE|EN<br>
  1790. The used language can be set via the global attribute "language". Or overwritten with this attribute.
  1791. </li>
  1792. <li><i>T2F_disableumlautescaping</i> &lt;0|1&gt;<br>
  1793. Disable convertimg umlauts to "\S\S?"</li>
  1794. <li><i>disable</i> &lt;0|1&gt;<br>
  1795. Can be used for test purposes. If the attribute is set to 1, the FHEM command is not executed
  1796. but written in reading cmds.
  1797. </li>
  1798. </ul>
  1799. </ul>
  1800. </ul>
  1801. =end html
  1802. =begin html_DE
  1803. <a name="Talk2Fhem"></a>
  1804. <h3>Talk2Fhem</h3>
  1805. <ul>
  1806. Das Modul <i>Talk2Fhem</i> stellt eine Verbindung zwischen nat&uuml;rlicher Sprache und FHEM Befehlen her.
  1807. Die Konfiguration erfolgt dabei komfortabel &uuml;ber das FHEM Webfrontend.<br>
  1808. F&uuml;r eine genauere Beschreibung und weiterf&uuml;hrende Beispiele siehe <a href="http://wiki.fhem.de/wiki/Modul_Talk2Fhem">Talk2Fhem Wiki</a>.
  1809. <br><br>
  1810. <a name="Talk2Fhemdefine"></a>
  1811. <b>Define</b>
  1812. <ul>
  1813. <code>define &lt;name&gt; Talk2Fhem</code>
  1814. <br><br>
  1815. Beispiel: <code>define talk Talk2Fhem</code>
  1816. <br><br>
  1817. Die eigentliche Konfigration sollte erst auf der FHEM Seite erfolgen.
  1818. <br><br>
  1819. Die einzelnen Sprachphrasen werden Zeile f&uuml;r Zeile konfiguriert. Hierbei f&auml;ngt eine Konfiguration
  1820. immer mit dem Regul&auml;rem Ausdruck an, gefolgt von mindestens einem Leerzeichen oder Tabulator gefolgt
  1821. von einem Gleichheitszeichen.<br>
  1822. Der Kommandoteil f&auml;ngt nach dem Gleichheitszeichen mit einem Leerzeichen, Tabulator oder Zeilenumbruch an.<br><br>
  1823. <code>&lt;regexp&gt; = &lt;command&gt;</code>
  1824. <br><br>
  1825. <b>Kurzreferenz:</b>
  1826. <br>
  1827. <code>&lt;RegExpPart&gt; [&amp;&amp; [?!]&lt;RegExpPart_n&gt;] = [ &lt;FHEM command&gt; | { &lt;Perl code&gt; } | (&lt;option&gt; =&gt; '&lt;wert&gt;' , ... ) ]</code>
  1828. <br><br>
  1829. Beispiel: <code>hallo welt = {Log 1, Hallo Welt}</code>
  1830. <br><br>
  1831. Alles nach einem Hashtag '#' wird bis zum Zeilenende ignoriert.
  1832. <br><br>
  1833. &lt;regexp&gt;
  1834. <ul>Regul&auml;rer Ausdruck der den Text beschreibt, bei dem das Kommando ausgef&uuml;hrt werden soll</ul>
  1835. <br><br>
  1836. &lt;command&gt;
  1837. <ul>
  1838. Der ausf&uuml;hrende Teil. Folgende Formate sind Zul&auml;ssig:
  1839. <li>FHEM Kommando</li>
  1840. <li>{Perlcode}</li>
  1841. <li>(&lt;option&gt; =&gt; '&lt;wert&gt;' , ... )</li>
  1842. <ul>
  1843. <br><i>&lt;option&gt;</i><br>
  1844. <li><b>cmd</b><br>FHEM Kommando wie oben</li>
  1845. <li><b>offset</b><br>Ganzzahliger Wert in Sekunden der auf den Zeitpunkt addiert wird</li>
  1846. <li><b>answer</b><br>Perl Code dessen R&uuml;ckgabe in das Reading answer geschrieben wird</li>
  1847. </ul>
  1848. </ul>
  1849. <br>
  1850. Klammer&uuml;berf&uuml;hrung:
  1851. <ul>
  1852. Im Regul&auml;rem Ausdruck gesetzte Klammern k&ouml;nnen in den Kommandoteil mit $1, $2, [...], $n &uuml;berf&uuml;hrt und
  1853. modifiziert werden. Folgende Modifizierungsm&ouml;glichkeiten stehen hierbei zur Verf&uuml;gung.
  1854. <li>$n<br>Ohne &Auml;nderung direkt das Wort &uuml;berf&uuml;hren.</li>
  1855. <li>$n{&lt;typ&gt; =&gt; &lt;wert&gt;}<br>
  1856. Die Typen sind:<br>
  1857. true, false, integer, float, numeral, /&lt;regexp&gt;/, word, empty, else<br>
  1858. <b>true</b> entspricht: ja|1|true|wahr|ein|eins.*|auf.*|..?ffnen|an.*|rauf.*|hoch.*|laut.*|hell.*<br>
  1859. <b>false</b> entspricht: nein|0|false|falsch|aus.*|null|zu.*|schlie..?en|runter.*|ab.*|leise.*|dunk.*<br>
  1860. <b>integer</b> Wort enth&auml;lt eine Zahl
  1861. <b>float</b> Wort enth&auml;lt eine Gleitkommazahl
  1862. <b>numeral</b> Word ist ein Zahlenwort oder Zahl <br>
  1863. <b>/&lt;regexp&gt;/</b> Wort entspricht der &lt;regexp&gt;
  1864. <b>word</b> Wort enth&auml;lt gleich oder mehr als 4 Zeichen
  1865. <b>empty</b> Wort enth&auml;lt eine Leere Zeichenkette
  1866. <b>else</b> Falls keines der F&auml;lle zutrifft
  1867. Wird ein &lt;typ&gt; identifiziert wird f&uuml;r $n der &lt;wert&gt; eingesetzt<br>
  1868. Beispiel: <code>licht (\S*) = set light $1{true =&gt; on,false =&gt; off}</code>
  1869. </li>
  1870. <li>$n[&lt;list&gt;]<br>
  1871. Kommaseparierte Liste: [wert1,wert2,...,[else,value], [empty,value]] oder [@modwordlist]<br>
  1872. Ist $n eine Zahl, wird das Wort das an dieser Position in &lt;list&gt; steht gew&auml;hlt.<br><br>
  1873. Ist $n ein Text wird in der zugeh&ouml;rigen Klammer im &lt;regexp&gt;-Teil nach einer Liste gesucht. (a|b|c) oder (@keywordlist)
  1874. In dieser Liste, wird nach $n gesucht und bei erfolg dessen Position in &lt;list&gt; f&uuml;r $n gew&auml;hlt.
  1875. <br>Beispiel: <code>licht .* (k&uuml;che|flur|bad) (\S*) an = set $1[dev_a,dev_b,dev_c] $2{true =&gt; on,false =&gt; off}</code>
  1876. </li>
  1877. <li>$n@<br>Das Wort wird so &uuml;bernommen wie es in der Liste im &lt;regexp&gt;-Teil steht.</li>
  1878. </ul>
  1879. <br>
  1880. Umgebungsvariablen:
  1881. <ul>
  1882. Es stehen eine Reihe von Variablen zur Verf&uuml;gung auf die im &lt;command&gt;-Teil zugegriffen werden k&ouml;nnen.
  1883. <li><b>$&amp;</b> Enth&auml;lt alle gefundenen W&ouml;rter</li>
  1884. <li><b>!$&amp;</b> Enth&auml;lt den Rest der nicht von der RegExp eingeschlossen wurde</li>
  1885. <li><b>$DATE</b> Enth&auml;lt den Zeit und Datumstext des Sprachbefehls</li>
  1886. <li><b>$AGAIN</b> Enth&auml;lt das Wort wieder wenn es sich um ein wieder Kommando handelt</li>
  1887. <li><b>$TIME</b> Enth&auml;lt die erkannte Zeit.</li>
  1888. <li><b>$NAME</b> Enth&auml;lt den Devicenamen.</li>
  1889. <li><b>$IF</b> Enth&auml;lt den Text der erkannten T2F_if Konfiguration.</li>
  1890. <li><b>$0</b> Enth&auml;lt den Text der erkannten T2F_origin RegExp.</li>
  1891. </ul>
  1892. </ul>
  1893. <br>
  1894. <a name="Talk2Fhemset"></a>
  1895. <b>Set</b><br>
  1896. <ul>
  1897. <code>set &lt;name&gt; [!]&lt;text&gt;</code>
  1898. <br><br>
  1899. &Uuml;ber das <i>set</i> Kommando wird der zu interpretierende Text an das Modul gesendet.
  1900. Schaue unter <a href="http://fhem.de/commandref.html#set">commandref#set</a> f&uuml;r weiterf&uuml;hrende Hilfe.
  1901. <li>cleartimers</li> Entfernt die wartenden zeitbezogenen Kommandos
  1902. <li>cleartriggers</li> Entfernt die wartenden ereignisbezogenen Kommandos
  1903. </ul>
  1904. <br>
  1905. <a name="Talk2Fhemget"></a>
  1906. <b>Get</b><br>
  1907. <code>get &lt;name&gt; &lt;option&gt;</code>
  1908. <br><br>
  1909. &Uuml;ber <i>get</i> lassen sich Informationen aus dem Modul auslesen.
  1910. Siehe <a href="http://fhem.de/commandref.html#get">commandref#get</a> f&uuml;r weitere Informationen zu "get".
  1911. <br><br>
  1912. &lt;option&gt;
  1913. <ul>
  1914. <li><i>@keywordlist</i> <i>@modwordlist</i><br>
  1915. Vergleich der zwei Listen Wort f&uuml;r Wort</li>
  1916. <li><i>keylistno</i><br>
  1917. Eine Auflistung der Konfigurierten "Keyword"-Listen. Zur einfacheren Positionierung der "Modword"-Listen</li>
  1918. <li><i>log</i><br>
  1919. Zeigt die Logeintr&auml;ge des letzten Kommandos</li>
  1920. <li><i>modificationtypes</i><br>
  1921. Zeigt die RegExp der Modifikationstypen. </li>
  1922. <li><i>standardfilter</i><br>
  1923. L&auml;dt den Standardfilter und schreibt ihn in das Attribut T2F_filter wenn er leer ist</li>
  1924. <li><i>version</i><br>
  1925. Die Modulversion</li>
  1926. </ul>
  1927. <br>
  1928. <a name="Talk2Fhemreadings"></a>
  1929. <b>Readings</b>
  1930. <ul>
  1931. <li><i>set</i><br>
  1932. Enth&auml;lt den zuletzt &uuml;ber "set" gesendeten Text.
  1933. </li>
  1934. <li><i>cmds</i><br>
  1935. Enth&auml;lt das zuletzt ausgef&uuml;hrte Kommando. Wird auch bei disable=1 gesetzt.
  1936. </li>
  1937. <li><i>answer</i><br>
  1938. Enth&auml;lt den Antworttext des letzten Befehls.
  1939. </li>
  1940. <li><i>err</i><br>
  1941. Enth&auml;lt die letzte Fehlermeldung.<br>
  1942. "No match" &Uuml;bereinstimmung mit keiner RegExp.<br>
  1943. "Error on Command" siehe FHEM log.
  1944. </li>
  1945. <li><i>response</i><br>
  1946. Enth&auml;llt die R&uuml;ckgabe des FHEM Befhels.
  1947. </li>
  1948. <li><i>origin</i><br>
  1949. Enth&auml;lt die gefundene Zeichenkette der in dem Attribut T2F_origin definierten RegExp.
  1950. </li>
  1951. <li><i>status</i><br>
  1952. Enth&auml;lt den Status der Ausgabe.
  1953. response, disabled, err, answers, done
  1954. </li>
  1955. <li><i>ifs</i><br>
  1956. Enth&auml;lt die Bedingungen bei denen das Kommando ausgef&uuml;hrt werden wird.
  1957. </li>
  1958. <li><i>notifies</i><br>
  1959. Enth&auml;lt eine Auflistung der Devices die f&uuml;r die aktuell wartenden bedingten Kommandos relevant sind. Auf diesen Devices liegt ein internes notify.
  1960. </li>
  1961. </ul>
  1962. <br>
  1963. <a name="Talk2Fhemattr"></a>
  1964. <b>Attribute</b>
  1965. <ul>
  1966. <code>attr &lt;name&gt; &lt;attribute&gt; &lt;value&gt;</code>
  1967. <br><br>
  1968. Siehe <a href="http://fhem.de/commandref.html#attr">commandref#attr</a> f&uuml;r weitere Informationen zu den Attributen.
  1969. <br><br>
  1970. Attribute:
  1971. <ul>
  1972. <li><i>T2F_keywordlist</i> &lt;name&gt; = &lt;list&gt;<br>
  1973. Eine Komma seperierte Liste von Schl&uuml;sselw&ouml;rtern wie z.B.: R&auml;umen, Namen, Farben usw...<br>
  1974. Mit anderen Worten, mit nat&uuml;rlichem Namen benannte Sachen.
  1975. </li>
  1976. <li><i>T2F_modwordlist</i> &lt;name&gt; = &lt;list&gt;<br>
  1977. Eine Komma seperierte Liste von Ersetzungsw&ouml;rten die f&uuml;r die Schl&uuml;sselw&ouml;rter eingesetzt werden.
  1978. z.B.: Ger&auml;tenamen in FHEM<br>
  1979. </li>
  1980. <li><i>T2F_if</i><br>
  1981. Eine Auflistung von ereignisgesteuerten Konfigurationen. Die Syntax ist die der Definition. Kommandoteil ist eine IF Bedingung.<br>
  1982. z.B.: wenn .*?t&uuml;r = [door] eq "open"
  1983. </li>
  1984. <li><i>T2F_filter</i><br>
  1985. Kommaseparierte Liste von RegExp die generell entfernt werden.<br>
  1986. Standard: \bbitte\b,\bauch\b,\bkann\b,\bsoll\b
  1987. </li>
  1988. <li><i>T2F_origin</i><br>
  1989. Eine RegExp die generell entfernt wird und deren Ausgabe &uuml;ber $0 angesprochen werden kann.<br>
  1990. Kann f&uuml;r eine Benutzerzuordnung verwendet werden.
  1991. </li>
  1992. <li><i>T2F_language</i>DE|EN<br>
  1993. Die verwendete Sprache kann &uuml;ber das globale Attribut "language" gesetzt werden. Oder &uuml;ber dieses Attribut &uuml;berschrieben werden.
  1994. </li>
  1995. <li><i>T2F_disableumlautescaping</i> &lt;0|1&gt;<br>
  1996. Deaktiviert das Konvertieren der Umlaute in "\S\S?"</li>
  1997. <li><i>disable</i> &lt;0|1&gt;<br>
  1998. Kann zu Testzwecken verwendet werden. Steht das Attribut auf 1, wird das FHEM-Kommando nicht ausgef&uuml;hrt
  1999. aber in das Reading cmds geschrieben.
  2000. </li>
  2001. </ul>
  2002. </ul>
  2003. </ul>
  2004. =end html_DE
  2005. =cut