39_Talk2Fhem.pm 78 KB

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