98_DOIFtools.pm 79 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536
  1. #############################################
  2. # $Id: 98_DOIFtools.pm 13412 2017-02-14 16:29:19Z Ellert $
  3. #
  4. # This file is part of fhem.
  5. #
  6. # Fhem is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 2 of the License, or
  9. # (at your option) any later version.
  10. # Fhem is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with fhem. If not, see <http://www.gnu.org/licenses/>.
  17. #
  18. ###############################################
  19. package main;
  20. use strict;
  21. use warnings;
  22. use Time::Local;
  23. sub DOIFtools_Initialize($);
  24. sub DOIFtools_Set($@);
  25. sub DOIFtools_Get($@);
  26. sub DOIFtools_Undef;
  27. sub DOIFtools_Define($$$);
  28. sub DOIFtools_Attr(@);
  29. sub DOIFtools_Notify($$);
  30. sub DOIFtoolsRg;
  31. sub DOIFtoolsNxTimer;
  32. sub DOIFtoolsNextTimer;
  33. sub DOIFtoolsGetAssocDev;
  34. sub DOIFtoolsCheckDOIF;
  35. sub DOIFtoolsCheckDOIFcoll;
  36. sub DOIFtools_fhemwebFn($$$$);
  37. sub DOIFtools_eM($$$$);
  38. sub DOIFtools_dO ($$$$);
  39. sub DOIFtoolsSetNotifyDev;
  40. sub DOIFtools_logWrapper($);
  41. sub DOIFtoolsCounterReset($);
  42. sub DOIFtoolsDeleteStatReadings;
  43. my @DOIFtools_we =();
  44. my $DOIFtoolsJSfuncEM = <<'EOF';
  45. <script type="text/javascript">
  46. //functions
  47. function doiftoolsCopyToClipboard() {
  48. var txtarea = document.getElementById('console');
  49. var start = txtarea.selectionStart;
  50. var finish = txtarea.selectionEnd;
  51. var txt = txtarea.value.substring(start, finish);
  52. var hlp = "Please highlight exactly one complete event line";
  53. if(!txt)
  54. return FW_okDialog(hlp);
  55. var re = /^....-..-..\s..:..:..(\....)?\s([^\s]+)\s([^\s]+)\s([^\s]+:\s)?(.*)([\r\n]*)?$/;
  56. var ret = txt.match(re);
  57. if(!ret)
  58. return FW_okDialog(hlp);
  59. var dev = ret[3];
  60. var ret1;
  61. var red ="";
  62. var val ="";
  63. if (ret[4]) {
  64. ret1 = ret[4].match(/(.*):\s$/);
  65. red = ret1[1];
  66. }
  67. val = ret[5];
  68. val = val.replace(/\s/g, ".");
  69. re1 = "["+dev+(red ? ":"+red : "")+"]";
  70. re2 = "["+dev+(red ? ":"+red : "")+"] eq \""+val+"\"";
  71. re3 = "[\"^"+dev+(red ? "$:^"+red+": " : "$:")+"\"]";
  72. re4 = "[\"^"+dev+(red ? "$:^"+red+": " : "$:^")+val+"$\"]";
  73. var txt = "Copy &amp; paste it to your DOIF definition<br><br>";
  74. txt += "<div><ul>";
  75. txt += "<li>event as [&lt;device&gt;:&lt;reading&gt;] representation:<br><code>"+re1+"</code></li><br>";
  76. txt += "<li>event as [&lt;device&gt;:&lt;reading&gt;] representation with comparison:<br><code>"+re2+"</code></li><br>";
  77. txt += "<li>event as <i>regular expression</i>:<br><code>"+re3+"</code></li><br>";
  78. txt += "<li>event as <i>regular expression</i> with value:<br><code>"+re4+"</code></li><br>";
  79. txt += "</ul></div>";
  80. return FW_okDialog(txt);
  81. }
  82. function delbutton() {
  83. var r = $("head").attr("root");
  84. var myFW_root = FW_root;
  85. if(r)
  86. myFW_root = r;
  87. FW_cmd(myFW_root+"?cmd={my @d = devspec2array('TYPE=DOIFtools');;return $d[0] ? $d[0] : ''}&XHR=1", function(data){
  88. if (data) {
  89. var dn = data;
  90. FW_cmd(myFW_root+"?cmd={AttrVal(\""+dn+"\",\"DOIFtoolsEMbeforeReadings\",\"0\")}&XHR=1", function(data){
  91. if (data == 1) {
  92. var ins = document.getElementsByClassName('makeTable wide readings');
  93. var del = document.getElementById('doiftoolscons');
  94. if (del) {
  95. ins[0].parentNode.insertBefore(del,ins[0]);
  96. }
  97. }
  98. });
  99. }
  100. });
  101. var del = document.getElementById('addRegexpPart');
  102. if (del) {
  103. removeEventListener ('DOMNodeInserted', delbutton);
  104. del.parentNode.removeChild(del);
  105. }
  106. }
  107. //execute
  108. var ins = document.getElementById('doiftoolsdel');
  109. addEventListener ('DOMNodeInserted', delbutton, false);
  110. var ins = document.getElementById('console');
  111. ins.addEventListener ('select', doiftoolsCopyToClipboard, false);
  112. </script>
  113. EOF
  114. my $DOIFtoolsJSfuncStart = <<'EOF';
  115. <script type="text/javascript">
  116. //functions
  117. function doiftoolsRemoveLookUp () {
  118. $('#addLookUp').dialog( "close" );
  119. }
  120. function doiftoolsAddLookUp () {
  121. var tn = $(this).text();
  122. var target = this;
  123. var txt = "Internals<table class='block wide internals' style='font-size:12px'>";
  124. FW_cmd(FW_root+"?cmd=jsonlist2 "+tn+"&XHR=1", function(data){
  125. var devList = JSON.parse(data);
  126. var dev = devList.Results[0];
  127. var row = 0;
  128. for (item in dev.Internals) {
  129. if (item == "DEF") {dev.Internals[item] = "<pre>"+dev.Internals[item]+"</pre>"}
  130. var cla = ((row++&1)?"odd":"even");
  131. txt += "<tr class='"+cla+"'><td>"+item+"</td><td>"+dev.Internals[item].replace(/\n/g,"<br>")+"</td></tr>\n";
  132. }
  133. txt += "</table>Readings<table class='block wide readings' style='font-size:12px'><br>";
  134. row = 0;
  135. for (item in dev.Readings) {
  136. var cla = ((row++&1)?"odd":"even");
  137. txt += "<tr class='"+cla+"'><td>"+item+"</td><td>"+dev.Readings[item].Value+"</td><td>"+dev.Readings[item].Time+"</td></tr>\n";
  138. }
  139. txt += "</table>Attributes<table class='block wide attributes' style='font-size:12px'><br>";
  140. row = 0;
  141. for (item in dev.Attributes) {
  142. if (item.match(/(userReadings|wait|setList)/) ) {dev.Attributes[item] = "<pre>"+dev.Attributes[item]+"</pre>"}
  143. var cla = ((row++&1)?"odd":"even");
  144. txt += "<tr class='"+cla+"'><td>"+item+"</td><td>"+dev.Attributes[item]+"</td></tr>\n";
  145. }
  146. txt += "</table>";
  147. $('#addLookUp').html(txt);
  148. $('#addLookUp').dialog("open");
  149. });
  150. }
  151. $(document).ready(function(){
  152. $('body').append('<div id="addLookUp" style="display:none"></div>');
  153. $('#addLookUp').dialog({
  154. width:"60%",
  155. height:"auto",
  156. maxHeight:900,
  157. modal: false,
  158. position: { at: "right"},
  159. collusion: "fit fit",
  160. buttons: [
  161. {
  162. text: "Ok",
  163. style:"margin-right: 100%",
  164. click: function() {
  165. $( this ).dialog( "close" );
  166. }
  167. }
  168. ]
  169. });
  170. $('#addLookUp').dialog( "close" );
  171. $(".assoc").find("a:even").each(function() {
  172. $(this).on("mouseover",doiftoolsAddLookUp);
  173. // $(this).on("mouseleave",doiftoolsRemoveLookUp);
  174. });
  175. });
  176. </script>
  177. EOF
  178. #########################
  179. sub DOIFtools_Initialize($)
  180. {
  181. my ($hash) = @_;
  182. $hash->{DefFn} = "DOIFtools_Define";
  183. $hash->{SetFn} = "DOIFtools_Set";
  184. $hash->{GetFn} = "DOIFtools_Get";
  185. $hash->{UndefFn} = "DOIFtools_Undef";
  186. $hash->{AttrFn} = "DOIFtools_Attr";
  187. $hash->{NotifyFn} = "DOIFtools_Notify";
  188. $hash->{FW_detailFn} = "DOIFtools_fhemwebFn";
  189. $data{FWEXT}{"/DOIFtools_logWrapper"}{CONTENTFUNC} = "DOIFtools_logWrapper";
  190. my $oldAttr = "target_room:noArg target_group:noArg executeDefinition:noArg executeSave:noArg eventMonitorInDOIF:noArg readingsPrefix:noArg";
  191. $hash->{AttrList} = "DOIFtoolsExecuteDefinition:1,0 DOIFtoolsTargetRoom DOIFtoolsTargetGroup DOIFtoolsExecuteSave:1,0 DOIFtoolsReadingsPrefix DOIFtoolsEventMonitorInDOIF:1,0 DOIFtoolsHideModulShortcuts:1,0 DOIFtoolsHideGetSet:1,0 DOIFtoolsMyShortcuts:textField-long DOIFtoolsMenuEntry:1,0 DOIFtoolsHideStatReadings:1,0 DOIFtoolsEventOnDeleted:1,0 DOIFtoolsEMbeforeReadings:1,0 DOIFtoolsNoLookUp:1,0 DOIFtoolsNoLookUpInDOIF:1,0 disabledForIntervals ".$oldAttr;
  192. }
  193. sub DOIFtools_dO ($$$$){return "";}
  194. # FW_detailFn for DOIF injecting event monitor
  195. sub DOIFtools_eM($$$$) {
  196. my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn.
  197. my @dtn = devspec2array("TYPE=DOIFtools");
  198. my $ret = "";
  199. $ret .= $DOIFtoolsJSfuncStart if (!AttrVal($dtn[0],"DOIFtoolsNoLookUpInDOIF",""));
  200. # Event Monitor
  201. my $a0 = ReadingsVal($d,".eM", "off") eq "on" ? "off" : "on";
  202. $ret .= "<div class=\"dval\"><br><span title=\"toggle to switch event monitor on/off\">Event monitor: <a href=\"$FW_ME?detail=$d&amp;cmd.$d=setreading $d .eM $a0\">toggle</a>&nbsp;&nbsp;</span>";
  203. $ret .= "</div>";
  204. my $a = "";
  205. if (ReadingsVal($d,".eM","off") eq "on") {
  206. $ret .= "<script type=\"text/javascript\" src=\"$FW_ME/pgm2/console.js\"></script>";
  207. my $filter = $a ? ($a eq "log" ? "global" : $a) : ".*";
  208. $ret .= "<div id='doiftoolscons'>";
  209. $ret .= "<div><br>";
  210. $ret .= "Events (Filter: <a href=\"#\" id=\"eventFilter\">$filter</a>) ".
  211. "&nbsp;&nbsp;<span id=\"doiftoolsdel\" class='fhemlog'>FHEM log ".
  212. "<input id='eventWithLog' type='checkbox'".
  213. ($a && $a eq "log" ? " checked":"")."></span>".
  214. "&nbsp;&nbsp;<button id='eventReset'>Reset</button></div>\n";
  215. $ret .= "<textarea id=\"console\" style=\"width:99%; top:.1em; bottom:1em; position:relative;\" readonly=\"readonly\" rows=\"25\" cols=\"60\" title=\"selecting an event line displays example operands for DOIFs definition\" ></textarea>";
  216. $ret .= "</div>";
  217. $ret .= $DOIFtoolsJSfuncEM;
  218. }
  219. return $ret;
  220. }
  221. ######################
  222. # Show the content of the log (plain text), or an image and offer a link
  223. # to convert it to an SVG instance
  224. # If text and no reverse required, try to return the data as a stream;
  225. sub DOIFtools_logWrapper($) {
  226. my ($cmd) = @_;
  227. my $d = $FW_webArgs{dev};
  228. my $type = $FW_webArgs{type};
  229. my $file = $FW_webArgs{file};
  230. my $ret = "";
  231. if(!$d || !$type || !$file) {
  232. FW_pO '<div id="content">DOIFtools_logWrapper: bad arguments</div>';
  233. return 0;
  234. }
  235. if(defined($type) && $type eq "text") {
  236. $defs{$d}{logfile} =~ m,^(.*)/([^/]*)$,; # Dir and File
  237. my $path = "$1/$file";
  238. $path =~ s/%L/$attr{global}{logdir}/g
  239. if($path =~ m/%/ && $attr{global}{logdir});
  240. $path = AttrVal($d,"archivedir","") . "/$file" if(!-f $path);
  241. FW_pO "<div id=\"content\">";
  242. FW_pO "<div class=\"tiny\">" if($FW_ss);
  243. FW_pO "<pre class=\"log\"><b>jump to: <a name='top'></a><a href=\"#end_of_file\">the end</a> <a href=\"#listing\">first listing</a></b><br>";
  244. my $suffix = "<br/><b>jump to: <a name='end_of_file'></a><a href='#top'>the top</a> <a href=\"#listing\">first listing</a></b><br/></pre>".($FW_ss ? "</div>" : "")."</div>";
  245. my $reverseLogs = AttrVal($FW_wname, "reverseLogs", 0);
  246. if(!$reverseLogs) {
  247. $suffix .= "</body></html>";
  248. return FW_returnFileAsStream($path, $suffix, "text/html", 0, 0);
  249. }
  250. if(!open(FH, $path)) {
  251. FW_pO "<div id=\"content\">$path: $!</div></body></html>";
  252. return 0;
  253. }
  254. my $cnt = join("", reverse <FH>);
  255. close(FH);
  256. # $cnt = FW_htmlEscape($cnt);
  257. FW_pO $cnt;
  258. FW_pO $suffix;
  259. return 1;
  260. }
  261. return 0;
  262. }
  263. sub DOIFtools_fhemwebFn($$$$) {
  264. my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn.
  265. my $ret = "";
  266. # $ret .= "<script type=\"text/javascript\" src=\"$FW_ME/pgm2/myfunction.js\"></script>";
  267. $ret .= $DOIFtoolsJSfuncStart if (!AttrVal($d,"DOIFtoolsNoLookUp",""));
  268. # Logfile Liste
  269. if($FW_ss && $pageHash) {
  270. $ret.= "<div id=\"$d\" align=\"center\" class=\"FileLog col2\">".
  271. "$defs{$d}{STATE}</div>";
  272. } else {
  273. my $row = 0;
  274. $ret .= sprintf("<table class=\"FileLog %swide\">",
  275. $pageHash ? "" : "block ");
  276. foreach my $f (FW_fileList($defs{$d}{logfile})) {
  277. my $class = (!$pageHash ? (($row++&1)?"odd":"even") : "");
  278. $ret .= "<tr class=\"$class\">";
  279. $ret .= "<td><div class=\"dname\">$f</div></td>";
  280. my $idx = 0;
  281. foreach my $ln (split(",", AttrVal($d, "logtype", "text"))) {
  282. if($FW_ss && $idx++) {
  283. $ret .= "</tr><tr class=\"".(($row++&1)?"odd":"even")."\"><td>";
  284. }
  285. my ($lt, $name) = split(":", $ln);
  286. $name = $lt if(!$name);
  287. $ret .= FW_pH("$FW_ME/DOIFtools_logWrapper&dev=$d&type=$lt&file=$f",
  288. "<div class=\"dval\">$name</div>", 1, "dval", 1);
  289. }
  290. }
  291. $ret .= "</table>";
  292. }
  293. # Event Monitor
  294. my $a0 = ReadingsVal($d,".eM", "off") eq "on" ? "off" : "on";
  295. $ret .= "<div class=\"dval\"><br><span title=\"toggle to switch event monitor on/off\">Event monitor: <a href=\"$FW_ME?detail=$d&amp;cmd.$d=setreading $d .eM $a0\">toggle</a>&nbsp;&nbsp;</span>";
  296. $ret .= "Shortcuts: " if (!AttrVal($d,"DOIFtoolsHideModulShortcuts",0) or AttrVal($d,"DOIFtoolsMyShortcuts",""));
  297. if (!AttrVal($d,"DOIFtoolsHideModulShortcuts",0)) {
  298. $ret .= "<a href=\"$FW_ME?detail=$d&amp;cmd.$d=reload 98_DOIFtools.pm\">reload DOIFtools</a>&nbsp;&nbsp;" if(ReadingsVal($d,".debug",""));
  299. $ret .= "<a href=\"$FW_ME?detail=$d&amp;cmd.$d=update check\">update check</a>&nbsp;&nbsp;";
  300. $ret .= "<a href=\"$FW_ME?detail=$d&amp;cmd.$d=update\">update</a>&nbsp;&nbsp;" if(!ReadingsVal($d,".debug",""));
  301. $ret .= "<a href=\"$FW_ME?detail=$d&amp;cmd.$d=set%20update_du:FILTER=state=0%201\">update</a>&nbsp;&nbsp;" if(ReadingsVal($d,".debug",""));
  302. $ret .= "<a href=\"$FW_ME?detail=$d&amp;cmd.$d=shutdown restart\">shutdown restart</a>&nbsp;&nbsp;";
  303. $ret .= "<a href=\"$FW_ME?detail=$d&amp;cmd.$d=fheminfo send\">fheminfo send</a>&nbsp;&nbsp;";
  304. }
  305. if (AttrVal($d,"DOIFtoolsMyShortcuts","")) {
  306. my @sc = split(",",AttrVal($d,"DOIFtoolsMyShortcuts",""));
  307. for (my $i = 0; $i < @sc; $i+=2) {
  308. if ($sc[$i] =~ m/^\#\#(.*)/) {
  309. $ret .= "$1&nbsp;&nbsp;";
  310. } else {
  311. $ret .= "<a href=\"/$sc[$i+1]\">$sc[$i]</a>&nbsp;&nbsp;" if($sc[$i] and $sc[$i+1]);
  312. }
  313. }
  314. }
  315. if (!AttrVal($d, "DOIFtoolsHideGetSet", 0)) {
  316. $ret .= "<br><br>";
  317. my $a1 = ReadingsVal($d,"doStatistics", "disabled") =~ "disabled|deleted" ? "enabled" : "disabled";
  318. my $a2 = ReadingsVal($d,"specialLog", 0) ? 0 : 1;
  319. # set doStatistics enabled/disabled
  320. $ret .= "<form method=\"post\" action=\"$FW_ME\" autocomplete=\"off\"><input name=\"detail\" value=\"$d\" type=\"hidden\">
  321. <input name=\"dev.set$d\" value=\"$d\" type=\"hidden\">
  322. <input name=\"cmd.set$d\" value=\"set\" class=\"set\" type=\"submit\">
  323. <div class=\"set downText\">&nbsp;doStatistics $a1&emsp;</div>
  324. <div style=\"display:none\" class=\"noArg_widget\" informid=\"$d-doStatistics\">
  325. <input name=\"val.set$d\" value=\"doStatistics $a1\" type=\"hidden\">
  326. </div></form>";
  327. # set doStatistics deleted
  328. $ret .= "<form method=\"post\" action=\"$FW_ME\" autocomplete=\"off\"><input name=\"detail\" value=\"$d\" type=\"hidden\">
  329. <input name=\"dev.set$d\" value=\"$d\" type=\"hidden\">
  330. <input name=\"cmd.set$d\" value=\"set\" class=\"set\" type=\"submit\">
  331. <div class=\"set downText\">&nbsp;doStatistics deleted&emsp;</div>
  332. <div style=\"display:none\" class=\"noArg_widget\" informid=\"$d-doStatistics\">
  333. <input name=\"val.set$d\" value=\"doStatistics deleted\" type=\"hidden\">
  334. </div></form>";
  335. # set specialLog 0/1
  336. $ret .= "<form method=\"post\" action=\"$FW_ME\" autocomplete=\"off\"><input name=\"detail\" value=\"$d\" type=\"hidden\">
  337. <input name=\"dev.set$d\" value=\"$d\" type=\"hidden\">
  338. <input name=\"cmd.set$d\" value=\"set\" class=\"set\" type=\"submit\">
  339. <div class=\"set downText\">&nbsp;specialLog $a2&emsp;</div>
  340. <div style=\"display:none\" class=\"noArg_widget\" informid=\"$d-doStatistics\">
  341. <input name=\"val.set$d\" value=\"specialLog $a2\" type=\"hidden\">
  342. </div></form>";
  343. $ret .= "<br><br>";
  344. # get statisticsReport
  345. $ret .= "<form method=\"post\" action=\"$FW_ME\" autocomplete=\"off\">
  346. <input name=\"detail\" value=\"$d\" type=\"hidden\">
  347. <input name=\"dev.get$d\" value=\"$d\" type=\"hidden\">
  348. <input name=\"cmd.get$d\" value=\"get\" class=\"get\" type=\"submit\">
  349. <div class=\"get downText\">&nbsp;statisticsReport&emsp;</div>
  350. <div style=\"display:none\" class=\"noArg_widget\" informid=\"$d-statisticsReport\">
  351. <input name=\"val.get$d\" value=\"statisticsReport\" type=\"hidden\">
  352. </div></form>";
  353. # get checkDOIF
  354. $ret .= "<form method=\"post\" action=\"$FW_ME\" autocomplete=\"off\">
  355. <input name=\"detail\" value=\"$d\" type=\"hidden\">
  356. <input name=\"dev.get$d\" value=\"$d\" type=\"hidden\">
  357. <input name=\"cmd.get$d\" value=\"get\" class=\"get\" type=\"submit\">
  358. <div class=\"get downText\">&nbsp;checkDOIF&emsp;</div>
  359. <div style=\"display:none\" class=\"noArg_widget\" informid=\"$d-checkDOIF\">
  360. <input name=\"val.get$d\" value=\"checkDOIF\" type=\"hidden\">
  361. </div></form>";
  362. # get runningTimerInDOIF
  363. $ret .= "<form method=\"post\" action=\"$FW_ME\" autocomplete=\"off\">
  364. <input name=\"detail\" value=\"$d\" type=\"hidden\">
  365. <input name=\"dev.get$d\" value=\"$d\" type=\"hidden\">
  366. <input name=\"cmd.get$d\" value=\"get\" class=\"get\" type=\"submit\">
  367. <div class=\"get downText\">&nbsp;runningTimerInDOIF&emsp;</div>
  368. <div style=\"display:none\" class=\"noArg_widget\" informid=\"$d-runningTimerInDOIF\">
  369. <input name=\"val.get$d\" value=\"runningTimerInDOIF\" type=\"hidden\">
  370. </div></form>";
  371. }
  372. $ret .= "</div><br>";
  373. my $a = "";
  374. if (ReadingsVal($d,".eM","off") eq "on") {
  375. $ret .= "<script type=\"text/javascript\" src=\"$FW_ME/pgm2/console.js\"></script>";
  376. # $ret .= "<script type=\"text/javascript\" src=\"$FW_ME/pgm2/doiftools.js\"></script>";
  377. my $filter = $a ? ($a eq "log" ? "global" : $a) : ".*";
  378. $ret .= "<div><br>";
  379. $ret .= "Events (Filter: <a href=\"#\" id=\"eventFilter\">$filter</a>) ".
  380. "&nbsp;&nbsp;<span id=\"doiftoolsdel\" class='fhemlog'>FHEM log ".
  381. "<input id='eventWithLog' type='checkbox'".
  382. ($a && $a eq "log" ? " checked":"")."></span>".
  383. "&nbsp;&nbsp;<button id='eventReset'>Reset</button></div>\n";
  384. $ret .= "<div>";
  385. $ret .= "<textarea id=\"console\" style=\"width:99%; top:.1em; bottom:1em; position:relative;\" readonly=\"readonly\" rows=\"25\" cols=\"60\" title=\"selecting an event line displays example operands for DOIFs definition\"></textarea>";
  386. $ret .= "</div>";
  387. $ret .= $DOIFtoolsJSfuncEM;
  388. }
  389. return $ret;
  390. }
  391. sub DOIFtools_Notify($$) {
  392. my ($hash, $source) = @_;
  393. my $pn = $hash->{NAME};
  394. my $sn = $source->{NAME};
  395. my $events = deviceEvents($source,1);
  396. return if( !$events );
  397. # \@DOIFtools_we aktualisieren
  398. if ($sn eq AttrVal("global","holiday2we","")) {
  399. my $we;
  400. my $val;
  401. my $a;
  402. my $b;
  403. for (my $i = 0; $i < 8; $i++) {
  404. $DOIFtools_we[$i] = 0;
  405. $val = CommandGet(undef,"get $sn days $i");
  406. if($val) {
  407. ($a, $b) = ReplaceEventMap($sn, [$sn, $val], 0);
  408. $DOIFtools_we[$i] = 1 if($b ne "none");
  409. }
  410. }
  411. }
  412. my $ldi = ReadingsVal($pn,"specialLog","") ? ReadingsVal($pn,"doif_to_log","") : "";
  413. foreach my $event (@{$events}) {
  414. $event = "" if(!defined($event));
  415. # add list to DOIFtoolsLog
  416. if ($ldi and $ldi =~ "$sn" and $event =~ m/(^cmd: \d+(\.\d+)?|^wait_timer: \d\d.*)/) {
  417. $hash->{helper}{counter}{0}++;
  418. my $trig = "<a name=\"list$hash->{helper}{counter}{0}\"><a name=\"listing\">";
  419. $trig .= "</a><strong>\[$hash->{helper}{counter}{0}\] +++++ Listing $sn:$1 +++++</strong>\n";
  420. my $prev = $hash->{helper}{counter}{0} - 1;
  421. my $next = $hash->{helper}{counter}{0} + 1;
  422. $trig .= $prev ? "<b>jump to: <a href=\"#list$prev\">prev</a>&nbsp;&nbsp;<a href=\"#list$next\">next</a> Listing</b><br>" : "<b>jump to: prev&nbsp;&nbsp;<a href=\"#list$next\">next</a> Listing</b><br>";
  423. $trig .= "DOIF-Version: ".ReadingsVal($pn,"DOIF_version","n/a")."<br>";
  424. $trig .= CommandList(undef,$sn);
  425. foreach my $itm (keys %defs) {
  426. $trig =~ s,([\[\" ])$itm([\"\:\] ]),$1<a href="$FW_ME?detail=$itm">$itm</a>$2,g;
  427. }
  428. CommandTrigger(undef,"$hash->{TYPE}Log $trig");
  429. }
  430. # DOIFtools DEF addition
  431. if ($sn eq "global" and $event =~ "MODIFIED|INITIALIZED|DEFINED|DELETED|RENAMED|UNDEFINED") {
  432. my @doifList = devspec2array("TYPE=DOIF");
  433. $hash->{DEF} = "associated DOIF: ".join(" ",sort @doifList);
  434. readingsSingleUpdate($hash,"DOIF_version",fhem("version 98_DOIF.pm noheader",1),0);
  435. }
  436. # get DOIF version, FHEM revision and default values
  437. if ($sn eq "global" and $event =~ "INITIALIZED|MODIFIED $pn") {
  438. readingsBeginUpdate($hash);
  439. readingsBulkUpdate($hash,"DOIF_version",fhem("version 98_DOIF.pm noheader",1));
  440. readingsBulkUpdate($hash,"FHEM_revision",fhem("version revision noheader",1));
  441. readingsBulkUpdate($hash,"sourceAttribute","readingList") unless ReadingsVal($pn,"sourceAttribute","");
  442. readingsBulkUpdate($hash,"recording_target_duration",0) unless ReadingsVal($pn,"recording_target_duration","0");
  443. readingsBulkUpdate($hash,"doStatistics","disabled") unless ReadingsVal($pn,"doStatistics","");
  444. readingsBulkUpdate($hash,".eM", ReadingsVal($pn,".eM","off"));
  445. readingsBulkUpdate($hash,"statisticsDeviceFilterRegex", ".*") unless ReadingsVal($pn,"statisticsDeviceFilterRegex","");
  446. readingsEndUpdate($hash,0);
  447. $defs{$pn}{VERSION} = fhem("version 98_DOIFtools.pm noheader",1);
  448. DOIFtoolsSetNotifyDev($hash,1,1);
  449. #set new attributes and delete old ones
  450. CommandAttr(undef,"$pn DOIFtoolsExecuteDefinition ".AttrVal($pn,"executeDefinition","")) if (AttrVal($pn,"executeDefinition",""));
  451. CommandDeleteAttr(undef,"$pn executeDefinition") if (AttrVal($pn,"executeDefinition",""));
  452. CommandAttr(undef,"$pn DOIFtoolsExecuteSave ".AttrVal($pn,"executeSave","")) if (AttrVal($pn,"executeSave",""));
  453. CommandDeleteAttr(undef,"$pn executeSave") if (AttrVal($pn,"executeSave",""));
  454. CommandAttr(undef,"$pn DOIFtoolsTargetRoom ".AttrVal($pn,"target_room","")) if (AttrVal($pn,"target_room",""));
  455. CommandDeleteAttr(undef,"$pn target_room") if (AttrVal($pn,"target_room",""));
  456. CommandAttr(undef,"$pn DOIFtoolsTargetGroup ".AttrVal($pn,"target_group","")) if (AttrVal($pn,"target_group",""));
  457. CommandDeleteAttr(undef,"$pn target_group") if (AttrVal($pn,"target_group",""));
  458. CommandAttr(undef,"$pn DOIFtoolsReadingsPrefix ".AttrVal($pn,"readingsPrefix","")) if (AttrVal($pn,"readingsPrefix",""));
  459. CommandDeleteAttr(undef,"$pn readingsPrefix") if (AttrVal($pn,"readingsPrefix",""));
  460. CommandAttr(undef,"$pn DOIFtoolsEventMonitorInDOIF ".AttrVal($pn,"eventMonitorInDOIF","")) if (AttrVal($pn,"eventMonitorInDOIF",""));
  461. CommandDeleteAttr(undef,"$pn eventMonitorInDOIF") if (AttrVal($pn,"eventMonitorInDOIF",""));
  462. CommandSave(undef,undef);
  463. }
  464. # Event monitor in DOIF
  465. if ($modules{DOIF}{LOADED} and !defined $modules{DOIF}->{FW_detailFn} and $sn eq "global" and $event =~ "INITIALIZED" and AttrVal($pn,"DOIFtoolsEventMonitorInDOIF","")) {
  466. $modules{DOIF}->{FW_detailFn} = "DOIFtools_eM" if (!defined $modules{DOIF}->{FW_detailFn});
  467. readingsSingleUpdate($hash,".DOIFdO",$modules{DOIF}->{FW_deviceOverview},0);
  468. $modules{DOIF}->{FW_deviceOverview} = 1;
  469. }
  470. # Statistics event recording
  471. if (ReadingsVal($pn,"doStatistics","disabled") eq "enabled" and !IsDisabled($pn) and $sn ne "global" and (ReadingsVal($pn,"statisticHours",0) <= ReadingsVal($pn,"recording_target_duration",0) or !ReadingsVal($pn,"recording_target_duration",0))) {
  472. my $st = AttrVal($pn,"DOIFtoolsHideStatReadings","") ? ".stat_" : "stat_";
  473. readingsSingleUpdate($hash,"$st$sn",ReadingsVal($pn,"$st$sn",0)+1,0);
  474. }
  475. }
  476. #statistics time counter updating
  477. if (ReadingsVal($pn,"doStatistics","disabled") eq "enabled" and !IsDisabled($pn) and $sn ne "global") {
  478. if (!ReadingsVal($pn,"recording_target_duration",0) or ReadingsVal($pn,"statisticHours",0) <= ReadingsVal($pn,"recording_target_duration",0)) {
  479. my $t = gettimeofday();
  480. my $te = ReadingsVal($pn,".te",gettimeofday()) + $t - ReadingsVal($pn,".t0",gettimeofday());
  481. my $tH = int($te*100/3600 +.5)/100;
  482. readingsBeginUpdate($hash);
  483. readingsBulkUpdate($hash,".te",$te);
  484. readingsBulkUpdate($hash,".t0",$t);
  485. readingsBulkUpdate($hash,"statisticHours",sprintf("%.2f",$tH));
  486. readingsEndUpdate($hash,0);
  487. } else {
  488. DOIFtoolsSetNotifyDev($hash,1,0);
  489. readingsBeginUpdate($hash);
  490. readingsBulkUpdate($hash,"Action","event recording target duration reached");
  491. readingsBulkUpdate($hash,"doStatistics","disabled");
  492. readingsEndUpdate($hash,0);
  493. }
  494. }
  495. return undef;
  496. }
  497. sub DOIFtoolsRg
  498. {
  499. my ($hash,$arg) = @_;
  500. my $pn = $hash->{NAME};
  501. my $pnRg= "rg_$arg";
  502. my $ret = "";
  503. my @ret;
  504. my $defRg = "";
  505. my @defRg;
  506. my $cL = "";
  507. my @rL = split(/ /,AttrVal($arg,"readingList",""));
  508. for (my $i=0; $i<@rL; $i++) {
  509. $defRg .= ",<$rL[$i]>,$rL[$i]";
  510. $cL .= "\"$rL[$i]\"=>\"$rL[$i]:\",";
  511. }
  512. push @defRg, "$pnRg readingsGroup $arg:+STATE$defRg";
  513. my $rooms = AttrVal($pn,"DOIFtoolsTargetRoom","") ? AttrVal($pn,"DOIFtoolsTargetRoom","") : AttrVal($arg,"room","");
  514. push @defRg, "$pnRg room $rooms" if($rooms);
  515. my $groups = AttrVal($pn,"DOIFtoolsTargetGroup","") ? AttrVal($pn,"DOIFtoolsTargetGroup","") : AttrVal($arg,"group","");
  516. push @defRg, "$pnRg group $groups" if($groups);
  517. push @defRg, "$pnRg commands {$cL}" if ($cL);
  518. push @defRg, "$pnRg noheading 1";
  519. $defRg = "defmod $defRg[0]\rattr ".join("\rattr ",@defRg[1..@defRg-1]);
  520. if (AttrVal($pn,"DOIFtoolsExecuteDefinition","")) {
  521. $ret = CommandDefMod(undef,$defRg[0]);
  522. push @ret, $ret if ($ret);
  523. for (my $i = 1; $i < @defRg; $i++) {
  524. $ret = CommandAttr(undef,$defRg[$i]);
  525. push @ret, $ret if ($ret);
  526. }
  527. if (@ret) {
  528. $ret = join("\n", @ret);
  529. return $ret;
  530. } else {
  531. $ret = "Created device <b>$pnRg</b>.\n";
  532. $ret .= CommandSave(undef,undef) if (AttrVal($pn,"DOIFtoolsExecuteSave",""));
  533. return $ret;
  534. }
  535. } else {
  536. $defRg =~ s/</&lt;/g;
  537. $defRg =~ s/>/&gt;/g;
  538. return $defRg;
  539. }
  540. }
  541. # calculate real date in userReadings
  542. sub DOIFtoolsNextTimer {
  543. my ($timer_str) = @_;
  544. $timer_str =~ /(\d\d).(\d\d).(\d\d\d\d) (\d\d):(\d\d):(\d\d)\|([0-8]+)/;
  545. my $tdays = $7;
  546. return "$1.$2.$3 $4:$5:$6" if (length($7)==0);
  547. my $timer = timelocal($6,$5,$4,$1,$2-1,$3);
  548. my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($timer);
  549. my $ilook = 0;
  550. my $we;
  551. for (my $iday = $wday; $iday < 7; $iday++) {
  552. $we = (($iday==0 || $iday==6) ? 1 : 0);
  553. if(!$we) {
  554. $we = $DOIFtools_we[$ilook + 1];
  555. }
  556. if ($tdays =~ /$iday/ or ($tdays =~ /7/ and $we) or ($tdays =~ /8/ and !$we)) {
  557. return strftime("%d.%m.%Y %H:%M:%S",localtime($timer + $ilook * 86400));
  558. }
  559. $ilook++;
  560. }
  561. for (my $iday = 0; $iday < $wday; $iday++) {
  562. $we = (($iday==0 || $iday==6) ? 1 : 0);
  563. if(!$we) {
  564. $we = $DOIFtools_we[$ilook + 1];
  565. }
  566. if ($tdays =~ /$iday/ or ($tdays =~ /7/ and $we) or ($tdays =~ /8/ and !$we)) {
  567. return strftime("%d.%m.%Y %H:%M:%S",localtime($timer + $ilook * 86400));
  568. }
  569. $ilook++;
  570. }
  571. }
  572. sub DOIFtoolsNxTimer {
  573. my ($hash,$arg) = @_;
  574. my $pn = $hash->{NAME};
  575. my $tn= $arg;
  576. my $thash = $defs{$arg};
  577. my $ret = "";
  578. my @ret;
  579. foreach my $key (keys %{$thash->{READINGS}}) {
  580. if ($key =~ m/^timer_\d\d_c\d\d/ && $thash->{READINGS}{$key}{VAL} =~ m/.*\|[0-8]+/) {
  581. $ret = AttrVal($pn,"DOIFtoolsReadingsPrefix","N_")."$key:$key.* \{DOIFtoolsNextTimer(ReadingsVal(\"$tn\",\"$key\",\"none\"))\}";
  582. push @ret, $ret if ($ret);
  583. }
  584. }
  585. if (@ret) {
  586. $ret = join(",", @ret);
  587. if (!AttrVal($tn,"userReadings","")) {
  588. CommandAttr(undef,"$tn userReadings $ret");
  589. $ret = "Created userReadings for <b>$tn</b>.\n";
  590. $ret .= CommandSave(undef,undef) if (AttrVal($pn,"DOIFtoolsExecuteSave",""));
  591. return $ret;
  592. } else {
  593. $ret = "A userReadings attribute already exists, adding is not implemented, try it manually.\r\r $ret\r";
  594. return $ret;
  595. }
  596. }
  597. return join("\n", @ret);
  598. }
  599. sub DOIFtoolsGetAssocDev {
  600. my ($hash,$arg) = @_;
  601. my $pn = $hash->{NAME};
  602. my $tn= $arg;
  603. my $thash = $defs{$arg};
  604. my $ret = "";
  605. my @ret = ();
  606. push @ret ,$arg;
  607. $ret .= $thash->{devices}{all} if ($thash->{devices}{all});
  608. $ret =~ s/^\s|\s$//;
  609. push @ret, split(/ /,$ret);
  610. push @ret, getPawList($tn);
  611. return @ret;
  612. }
  613. sub DOIFtoolsCheckDOIFcoll {
  614. my ($hash,$tn) = @_;
  615. my $ret = "";
  616. my $tail = $defs{$tn}{DEF};
  617. if (!$tail) {
  618. $tail="";
  619. } else {
  620. $tail =~ s/(##.*\n)|(##.*$)|\n/ /g;
  621. }
  622. return("") if ($tail =~ /^ *$/);
  623. $ret .= $tn if ($tail =~ m/(DOELSEIF )/ and !($tail =~ m/(DOELSE )/) and AttrVal($tn,"do","") !~ "always");
  624. return $ret;
  625. }
  626. sub DOIFtoolsCheckDOIF {
  627. my ($hash,$tn) = @_;
  628. my $ret = "";
  629. my $tail = $defs{$tn}{DEF};
  630. if (!$tail) {
  631. $tail="";
  632. } else {
  633. $tail =~ s/(##.*\n)|(##.*$)|\n/ /g;
  634. }
  635. return("") if ($tail =~ /^ *$/);
  636. my $DE = AttrVal("global", "language", "") eq "DE" ? 1 : 0;
  637. if ($DE) {
  638. $ret .= "<li>ersetze <b>DOIF name</b> durch <b>\$SELF</b> (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#DOIF_Ereignissteuerung_ueber_Auswertung_von_Events\">Auswertung von Events</a>)</li>\n" if ($tail =~ m/[\[|\?]($tn)/);
  639. $ret .= "<li>ersetze <b>ReadingsVal(...)</b> durch <b>[</b>name<b>:</b>reading<b>,</b>default value<b>]</b>, wenn es nicht in einem <b><a href=\"https://fhem.de/commandref.html#IF\">IF-Befehl</a></b> verwendet wird, dort ist es nicht anders möglich einen Default-Wert anzugeben. (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#DOIF_Ereignissteuerung\">Steuerung durch Events</a>)</li>\n" if ($tail =~ m/(ReadingsVal)/);
  640. $ret .= "<li>ersetze <b>ReadingsNum(...)</b> durch <b>[</b>name<b>:</b>reading<b>:d,</b>default value]</b>, wenn es nicht in einem <b><a href=\"https://fhem.de/commandref.html#IF\">IF-Befehl</a></b> verwendet wird, dort ist es nicht anders möglich einen Default-Wert anzugeben. (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#DOIF_Filtern_nach_Zahlen\">Filtern nach Zahlen</a>)</li>\n" if ($tail =~ m/(ReadingsNum)/);
  641. $ret .= "<li>ersetze <b>InternalVal(...)</b> durch <b>[</b>name<b>:</b>&amp;internal,</b>default value<b>]</b>, wenn es nicht in einem <b><a href=\"https://fhem.de/commandref.html#IF\">IF-Befehl</a></b> verwendet wird, dort ist es nicht anders möglich einen Default-Wert anzugeben. (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#DOIF_Ereignissteuerung\">Steuerung durch Events</a>)</li>\n" if ($tail =~ m/(InternalVal)/);
  642. $ret .= "<li>ersetze <b>$1...\")}</b> durch <b>$2...</b> (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#command\">FHEM-Befehl</a>)</li>\n" if ($tail =~ m/(\{\s*fhem.*?\"\s*(set|get))/);
  643. $ret .= "<li>ersetze <b>{system \"</b>&lt;SHELL-Befehl&gt;<b>\"}</b> durch <b>\"</b>\&lt;SHELL-Befehl&gt;<b>\"</b> (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#command\">FHEM SHELL-Befehl, nicht blockierend</a>)</li>\n" if ($tail =~ m/(\{\s*system.*?\})/);
  644. $ret .= "<li><b>sleep</b> im DOIF zu nutzen, wird nicht empfohlen, nutze das Attribut <b>wait</b> für (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#DOIF_wait\">Verzögerungen</a>)</li>\n" if ($tail =~ m/(sleep\s\d+\.?\d+\s*[;|,]?)/);
  645. $ret .= "<li>ersetze <b>[</b>name<b>:?</b>regex<b>]</b> durch <b>[</b>name<b>:\"</b>regex<b>\"]</b> (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#DOIF_Ereignissteuerung_ueber_Auswertung_von_Events\">Vermeidung veralteter Syntax</a>)</li>\n" if ($tail =~ m/(\[.*?[^"]?:[^"]?\?.*?\])/);
  646. $ret .= "<li>der erste <b>Befehl</b> nach <b>DOELSE</b> scheint eine <b>Bedingung</b> zu sein, weil <b>$2</b> enthalten ist, bitte prüfen.</li>\n" if ($tail =~ m/(DOELSE .*?\]\s*?(\!\S|\=\~|\!\~|and|or|xor|not|\|\||\&\&|\=\=|\!\=|ne|eq|lt|gt|le|ge)\s*?).*?\)/);
  647. my @wait = SplitDoIf(":",AttrVal($tn,"wait",""));
  648. my @sub0 = ();
  649. my @tmp = ();
  650. if (@wait and !AttrVal($tn,"timerWithWait","")) {
  651. for (my $i = 0; $i < @wait; $i++) {
  652. ($sub0[$i],@tmp) = SplitDoIf(",",$wait[$i]);
  653. $sub0[$i] =~ s/\s// if($sub0[$i]);
  654. }
  655. if (defined $defs{$tn}{timeCond}) {
  656. foreach my $key (sort keys %{$defs{$tn}{timeCond}}) {
  657. if (defined($defs{$tn}{timeCond}{$key}) and $defs{$tn}{timeCond}{$key} and $sub0[$defs{$tn}{timeCond}{$key}]) {
  658. $ret .= "<li><b>Timer</b> in der <b>Bedingung</b> and <b>Wait-Timer</b> für <b>Befehle</b> im selben <b>DOIF-Zweig</b>.<br>Wenn ein unerwartetes Verhalten beobachtet wird, nutze das Attribut <b>timerWithWait</b> (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#DOIF_timerWithWait\">Verzögerung von Timern</a>)</li>\n";
  659. last;
  660. }
  661. }
  662. }
  663. }
  664. my $wait = AttrVal($tn,"wait","");
  665. if ($wait) {
  666. $ret .= "<li>Mindestens ein <b>indirekter Timer</b> im Attribut <b>wait</b> bezieht sich auf den <b>DOIF-Namen</b> ( $tn ) und hat keinen <b>Default-Wert</b>, er sollte angegeben werden.</b>. (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#DOIF_notexist\">Default-Wert</a>)</li>\n"
  667. if($wait =~ m/(\[(\$SELF|$tn).*?(\,.*?)?\])/ and $2 and !$3);
  668. }
  669. if (defined $defs{$tn}{time}) {
  670. foreach my $key (sort keys %{$defs{$tn}{time}}) {
  671. if (defined $defs{$tn}{time}{$key} and $defs{$tn}{time}{$key} =~ m/(\[(\$SELF|$tn).*?(\,.*?)?\])/ and $2 and !$3) {
  672. $ret .= "<li>Mindestens ein <b>indirekter Timer</b> in einer <b>Bedingung</b> bezieht sich auf den <b>DOIF-Namen</b> ( $tn ) und hat keinen <b>Default-Wert</b>, er sollte angegeben werden. (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#DOIF_notexist\">Default-Wert</a>)</li>\n";
  673. last;
  674. }
  675. }
  676. }
  677. if (defined $defs{$tn}{devices}{all}) {
  678. @tmp = ();
  679. my $devi = $defs{$tn}{devices}{all};
  680. $devi =~ s/^ | $//g;
  681. my @devi = split(/ /,$defs{$tn}{devices}{all});
  682. foreach my $key (@devi) {
  683. push @tmp, $key if (defined $defs{$key} and $defs{$key}{TYPE} eq "dummy");
  684. }
  685. if (@tmp) {
  686. @tmp = keys %{{ map { $_ => 1 } @tmp}};
  687. my $tmp = join(" ",sort @tmp);
  688. $ret .= "<li>Dummy-Geräte ( $tmp ) in der Bedingung von DOIF $tn können durch <b>benutzerdefinierte Readings des DOIF</b> ersetzt werden, wenn sie als Frontend-Elemente genutzt werden. (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#https://fhem.de/commandref_DE.html#DOIF_setList__readingList\">readingList, setList, webCmd</a>)</li>\n";
  689. }
  690. }
  691. if (defined $defs{$tn}{do}) {
  692. @tmp = ();
  693. foreach my $key (keys %{$defs{$tn}{do}}) {
  694. foreach my $subkey (keys %{$defs{$tn}{do}{$key}}) {
  695. push @tmp, $1 if ($defs{$tn}{do}{$key}{$subkey} =~ m/set (.*?) / and defined $defs{$1} and $defs{$1}{TYPE} eq "dummy");
  696. }
  697. }
  698. if (@tmp) {
  699. @tmp = keys %{{ map { $_ => 1 } @tmp}};
  700. my $tmp = join(" ",sort @tmp);
  701. $ret .= "<li>Statt Dummys ( $tmp ) zu setzen, könnte ggf. der Status des DOIF $tn zur Anzeige im Frontend genutzt werden. (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#https://fhem.de/commandref_DE.html#DOIF_cmdState\">DOIF-Status ersetzen</a>)</li>\n";
  702. }
  703. }
  704. } else {
  705. $ret .= "<li>replace <b>DOIF name</b> with <b>\$SELF</b> (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#DOIF_Ereignissteuerung_ueber_Auswertung_von_Events\">utilization of events</a>)</li>\n" if ($tail =~ m/[\[|\?]($tn)/);
  706. $ret .= "<li>replace <b>ReadingsVal(...)</b> with <b>[</b>name<b>:</b>reading<b>,</b>default value<b>]</b>, if not used in an <b><a href=\"https://fhem.de/commandref.html#IF\">IF command</a></b>, otherwise there is no possibility to use a default value (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#DOIF_Ereignissteuerung\">controlling by events</a>)</li>\n" if ($tail =~ m/(ReadingsVal)/);
  707. $ret .= "<li>replace <b>ReadingsNum(...)</b> with <b>[</b>name<b>:</b>reading<b>:d,</b>default value]</b>, if not used in an <b><a href=\"https://fhem.de/commandref.html#IF\">IF command</a></b>, otherwise there is no possibility to use a default value (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#DOIF_Filtern_nach_Zahlen\">filtering numbers</a>)</li>\n" if ($tail =~ m/(ReadingsNum)/);
  708. $ret .= "<li>replace <b>InternalVal(...)</b> with <b>[</b>name<b>:</b>&amp;internal,</b>default value<b>]</b>, if not used in an <b><a href=\"https://fhem.de/commandref.html#IF\">IF command</a></b>, otherwise there is no possibility to use a default value (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#DOIF_Ereignissteuerung\">controlling by events</a>)</li>\n" if ($tail =~ m/(InternalVal)/);
  709. $ret .= "<li>replace <b>$1...\")}</b> with <b>$2...</b> (<a target=\"_blank\" href=\"https://fhem.de/commandref.html#command\">plain FHEM command</a>)</li>\n" if ($tail =~ m/(\{\s*fhem.*?\"\s*(set|get))/);
  710. $ret .= "<li>replace <b>{system \"</b>&lt;shell command&gt;<b>\"}</b> with <b>\"</b>\&lt;shell command&gt;<b>\"</b> (<a target=\"_blank\" href=\"https://fhem.de/commandref.html#command\">plain FHEM shell command, non blocking</a>)</li>\n" if ($tail =~ m/(\{\s*system.*?\})/);
  711. $ret .= "<li><b>sleep</b> is not recommended in DOIF, use attribute <b>wait</b> for (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#DOIF_wait\">delay</a>)</li>\n" if ($tail =~ m/(sleep\s\d+\.?\d+\s*[;|,]?)/);
  712. $ret .= "<li>replace <b>[</b>name<b>:?</b>regex<b>]</b> by <b>[</b>name<b>:\"</b>regex<b>\"]</b> (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#DOIF_Ereignissteuerung_ueber_Auswertung_von_Events\">avoid old syntax</a>)</li>\n" if ($tail =~ m/(\[.*?[^"]?:[^"]?\?.*?\])/);
  713. $ret .= "<li>the first <b>command</b> after <b>DOELSE</b> seems to be a <b>condition</b> indicated by <b>$2</b>, check it.</li>\n" if ($tail =~ m/(DOELSE .*?\]\s*?(\!\S|\=\~|\!\~|and|or|xor|not|\|\||\&\&|\=\=|\!\=|ne|eq|lt|gt|le|ge)\s*?).*?\)/);
  714. my @wait = SplitDoIf(":",AttrVal($tn,"wait",""));
  715. my @sub0 = ();
  716. my @tmp = ();
  717. if (@wait and !AttrVal($tn,"timerWithWait","")) {
  718. for (my $i = 0; $i < @wait; $i++) {
  719. ($sub0[$i],@tmp) = SplitDoIf(",",$wait[$i]);
  720. $sub0[$i] =~ s/\s// if($sub0[$i]);
  721. }
  722. if (defined $defs{$tn}{timeCond}) {
  723. foreach my $key (sort keys %{$defs{$tn}{timeCond}}) {
  724. if (defined($defs{$tn}{timeCond}{$key}) and $defs{$tn}{timeCond}{$key} and $sub0[$defs{$tn}{timeCond}{$key}]) {
  725. $ret .= "<li><b>Timer</b> in <b>condition</b> and <b>wait timer</b> for <b>commands</b> in the same <b>DOIF branch</b>.<br>If you observe unexpected behaviour, try attribute <b>timerWithWait</b> (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#DOIF_timerWithWait\">delay of Timer</a>)</li>\n";
  726. last;
  727. }
  728. }
  729. }
  730. }
  731. my $wait = AttrVal($tn,"wait","");
  732. if ($wait) {
  733. $ret .= "<li>At least one <b>indirect timer</b> in attribute <b>wait</b> is referring <b>DOIF's name</b> ( $tn ) and has no <b>default value</b>, you should add <b>default values</b>. (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#DOIF_notexist\">default value</a>)</li>\n"
  734. if($wait =~ m/(\[(\$SELF|$tn).*?(\,.*?)?\])/ and $2 and !$3);
  735. }
  736. if (defined $defs{$tn}{time}) {
  737. foreach my $key (sort keys %{$defs{$tn}{time}}) {
  738. if (defined $defs{$tn}{time}{$key} and $defs{$tn}{time}{$key} =~ m/(\[(\$SELF|$tn).*?(\,.*?)?\])/ and $2 and !$3) {
  739. $ret .= "<li>At least one <b>indirect timer</b> in <b>condition</b> is referring <b>DOIF's name</b> ( $tn ) and has no <b>default value</b>, you should add <b>default values</b>. (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#DOIF_notexist\">default value</a>)</li>\n";
  740. last;
  741. }
  742. }
  743. }
  744. if (defined $defs{$tn}{devices}{all}) {
  745. @tmp = ();
  746. my $devi = $defs{$tn}{devices}{all};
  747. $devi =~ s/^ | $//g;
  748. my @devi = split(/ /,$defs{$tn}{devices}{all});
  749. foreach my $key (@devi) {
  750. push @tmp, $key if (defined $defs{$key} and $defs{$key}{TYPE} eq "dummy");
  751. }
  752. if (@tmp) {
  753. my $tmp = join(" ",sort @tmp);
  754. $ret .= "<li>dummy devices in DOIF $tn condition could replaced by <b>user defined readings</b> in DOIF, if they are used as frontend elements. (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#https://fhem.de/commandref_DE.html#DOIF_setList__readingList\">readingList, setList, webCmd</a>)</li>\n";
  755. }
  756. }
  757. if (defined $defs{$tn}{do}) {
  758. @tmp = ();
  759. foreach my $key (keys %{$defs{$tn}{do}}) {
  760. foreach my $subkey (keys %{$defs{$tn}{do}{$key}}) {
  761. push @tmp, $1 if ($defs{$tn}{do}{$key}{$subkey} =~ m/set (.*?) / and defined $defs{$1} and $defs{$1}{TYPE} eq "dummy");
  762. }
  763. }
  764. if (@tmp) {
  765. my $tmp = join(" ",sort @tmp);
  766. $ret .= "<li>The state of DOIF $tn could be eventually used as display element in frontend, instead of setting a dummy device ( $tmp ). (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#https://fhem.de/commandref_DE.html#DOIF_cmdState\">replace DOIF state</a>)</li>\n";
  767. }
  768. }
  769. }
  770. $ret = $ret ? "$tn\n<ul>$ret</ul> " : "";
  771. return $ret;
  772. }
  773. # param: $hash, doif_to_log, statisticsTypes as 1 or 0
  774. sub DOIFtoolsSetNotifyDev {
  775. my ($hash,@a) = @_;
  776. my $pn = $hash->{NAME};
  777. $hash->{NOTIFYDEV} = "global";
  778. $hash->{NOTIFYDEV} .= ",$attr{global}{holiday2we}" if ($attr{global}{holiday2we});
  779. $hash->{NOTIFYDEV} .= ",".ReadingsVal($pn,"doif_to_log","") if ($a[0] and ReadingsVal($pn,"doif_to_log","") and ReadingsVal($pn,"specialLog",0));
  780. $hash->{NOTIFYDEV} .= ",TYPE=".ReadingsVal($pn,"statisticsTYPEs","") if ($a[1] and ReadingsVal($pn,"statisticsTYPEs","") and ReadingsVal($pn,"doStatistics","deleted") eq "enabled");
  781. return undef;
  782. }
  783. sub DOIFtoolsCounterReset($) {
  784. my ($pn) = @_;
  785. RemoveInternalTimer($pn,"DOIFtoolsCounterReset");
  786. $defs{$pn}->{helper}{counter}{0} = 0;
  787. my $nt = gettimeofday();
  788. my @lt = localtime($nt);
  789. $nt -= ($lt[2]*3600+$lt[1]*60+$lt[0]); # Midnight
  790. $nt += 86400 + 3; # Tomorrow
  791. InternalTimer($nt, "DOIFtoolsCounterReset", $pn, 0);
  792. return undef;
  793. }
  794. sub DOIFtoolsDeleteStatReadings {
  795. my ($hash, @a) = @_;
  796. my $pn = $hash->{NAME};
  797. my $st = AttrVal($pn,"DOIFtoolsHideStatReadings","") ? ".stat_" : "stat_"; readingsBeginUpdate($hash);
  798. readingsBulkUpdate($hash,"Action","event recording stopped and data deleted");
  799. readingsBulkUpdate($hash,"doStatistics","disabled");
  800. readingsBulkUpdate($hash,"statisticHours","0.00");
  801. readingsBulkUpdate($hash,".t0",gettimeofday());
  802. readingsBulkUpdate($hash,".te",0);
  803. readingsEndUpdate($hash,0);
  804. if (AttrVal($pn,"DOIFtoolsEventOnDeleted","")){
  805. readingsBeginUpdate($hash);
  806. foreach my $key (keys %{$hash->{READINGS}}) {
  807. readingsBulkUpdate($hash,"stat_$1",ReadingsVal($pn,"$key",0)) if ($key =~ m/^$st(.*)/);
  808. }
  809. readingsEndUpdate($hash,1);
  810. }
  811. foreach my $key (keys %{$hash->{READINGS}}) {
  812. delete $hash->{READINGS}{$key} if ($key =~ "^(stat_|\.stat_)");
  813. }
  814. }
  815. #################################
  816. sub DOIFtools_Define($$$)
  817. {
  818. my ($hash, $def) = @_;
  819. my ($pn, $type, $cmd) = split(/[\s]+/, $def, 3);
  820. my @Liste = devspec2array("TYPE=DOIFtools");
  821. if (@Liste > 1) {
  822. CommandDelete(undef,$pn);
  823. CommandSave(undef,undef);
  824. return "Only one instance of DOIFtools is allowed per FHEM installation. Delete the old one first.";
  825. }
  826. $hash->{STATE} = "initialized";
  827. $hash->{logfile} = AttrVal("global","logdir","./log/")."$hash->{TYPE}Log-%Y-%j.log";
  828. DOIFtoolsCounterReset($pn);
  829. return undef;
  830. }
  831. sub DOIFtools_Attr(@)
  832. {
  833. my @a = @_;
  834. my $cmd = $a[0];
  835. my $pn = $a[1];
  836. my $attr = $a[2];
  837. my $value = (defined $a[3]) ? $a[3] : "";
  838. my $hash = $defs{$pn};
  839. my $ret="";
  840. if ($init_done and $attr eq "DOIFtoolsEventMonitorInDOIF") {
  841. if (!defined $modules{DOIF}->{FW_detailFn} and $cmd eq "set" and $value) {
  842. $modules{DOIF}->{FW_detailFn} = "DOIFtools_eM";
  843. readingsSingleUpdate($hash,".DOIFdO",$modules{DOIF}->{FW_deviceOverview},0);
  844. $modules{DOIF}->{FW_deviceOverview} = "DOIFtools_dO";
  845. } elsif ($modules{DOIF}->{FW_detailFn} eq "DOIFtools_eM" and ($cmd eq "del" or !$value)) {
  846. delete $modules{DOIF}->{FW_detailFn};
  847. $modules{DOIF}->{FW_deviceOverview} = ReadingsVal($pn,"DOIFtools_dO","");
  848. }
  849. } elsif ($init_done and $attr eq "DOIFtoolsMenuEntry") {
  850. if ($cmd eq "set" and $value) {
  851. if (!(AttrVal($FW_wname, "menuEntries","") =~ m/(DOIFtools\,$FW_ME\?detail\=DOIFtools\,)/)) {
  852. CommandAttr(undef, "$FW_wname menuEntries DOIFtools,$FW_ME?detail=DOIFtools,".AttrVal($FW_wname, "menuEntries",""));
  853. CommandSave(undef, undef);
  854. }
  855. } elsif ($init_done and $cmd eq "del" or !$value) {
  856. if (AttrVal($FW_wname, "menuEntries","") =~ m/(DOIFtools\,$FW_ME\?detail\=DOIFtools\,)/) {
  857. my $me = AttrVal($FW_wname, "menuEntries","");
  858. $me =~ s/DOIFtools\,$FW_ME\?detail\=DOIFtools\,//;
  859. CommandAttr(undef, "$FW_wname menuEntries $me");
  860. CommandSave(undef, undef);
  861. }
  862. }
  863. } elsif ($init_done and $attr eq "DOIFtoolsHideStatReadings") {
  864. DOIFtoolsSetNotifyDev($hash,1,0);
  865. DOIFtoolsDeleteStatReadings($hash);
  866. } elsif ($init_done and $cmd eq "set" and
  867. $attr =~ m/^(executeDefinition|executeSave|target_room|target_group|readingsPrefix|eventMonitorInDOIF)$/) {
  868. $ret .= "\n$1 is an old attribute name use a new one beginning with DOIFtools...";
  869. return $ret;
  870. }
  871. return undef;
  872. }
  873. sub DOIFtools_Undef
  874. {
  875. my ($hash, $pn) = @_;
  876. $hash->{DELETED} = 1;
  877. if (devspec2array("TYPE=DOIFtools") <=1 and defined($modules{DOIF}->{FW_detailFn}) and $modules{DOIF}->{FW_detailFn} eq "DOIFtools_eM") {
  878. delete $modules{DOIF}->{FW_detailFn};
  879. $modules{DOIF}->{FW_deviceOverview} = ReadingsVal($pn,"DOIFtools_dO","");
  880. }
  881. if (AttrVal($pn,"DOIFtoolsMenuEntry","")) {
  882. CommandDeleteAttr(undef, "$pn DOIFtoolsMenuEntry");
  883. }
  884. RemoveInternalTimer($pn,"DOIFtoolsCounterReset");
  885. return undef;
  886. }
  887. sub DOIFtools_Set($@)
  888. {
  889. my ($hash, @a) = @_;
  890. my $pn = $hash->{NAME};
  891. my $arg = $a[1];
  892. my $value = (defined $a[2]) ? $a[2] : "";
  893. my $ret = "";
  894. my @ret = ();
  895. my @doifList = devspec2array("TYPE=DOIF");
  896. my @deviList = devspec2array("TYPE!=DOIF");
  897. my @ntL =();
  898. my $dL = join(",",sort @doifList);
  899. my $deL = join(",",sort @deviList);
  900. my $st = AttrVal($pn,"DOIFtoolsHideStatReadings","") ? ".stat_" : "stat_";
  901. my %types = ();
  902. foreach my $d (keys %defs ) {
  903. next if(IsIgnored($d));
  904. my $t = $defs{$d}{TYPE};
  905. $types{$t} = "";
  906. }
  907. my $tL = join(",",sort keys %types);
  908. if ($arg eq "sourceAttribute") {
  909. readingsSingleUpdate($hash,"sourceAttribute",$value,0);
  910. return $ret;
  911. } elsif ($arg eq "targetDOIF") {
  912. readingsSingleUpdate($hash,"targetDOIF",$value,0);
  913. } elsif ($arg eq "deleteReadingsInTargetDOIF") {
  914. if ($value) {
  915. my @i = split(",",$value);
  916. foreach my $i (@i) {
  917. $ret = CommandDeleteReading(undef,ReadingsVal($pn,"targetDOIF","")." $i");
  918. push @ret, $ret if($ret);
  919. }
  920. $ret = join("\n", @ret);
  921. readingsSingleUpdate($hash,"targetDOIF","",0);
  922. return $ret;
  923. } else {
  924. readingsSingleUpdate($hash,"targetDOIF","",0);
  925. return "no reading selected.";
  926. }
  927. } elsif ($arg eq "targetDevice") {
  928. readingsSingleUpdate($hash,"targetDevice",$value,0);
  929. } elsif ($arg eq "deleteReadingsInTargetDevice") {
  930. if ($value) {
  931. my @i = split(",",$value);
  932. foreach my $i (@i) {
  933. $ret = CommandDeleteReading(undef,ReadingsVal($pn,"targetDevice","")." $i");
  934. push @ret, $ret if($ret);
  935. }
  936. $ret = join("\n", @ret);
  937. readingsSingleUpdate($hash,"targetDevice","",0);
  938. return $ret;
  939. } else {
  940. readingsSingleUpdate($hash,"targetDevice","",0);
  941. return "no reading selected.";
  942. }
  943. } elsif ($arg eq "doStatistics") {
  944. if ($value eq "deleted") {
  945. DOIFtoolsSetNotifyDev($hash,1,0);
  946. DOIFtoolsDeleteStatReadings($hash);
  947. } elsif ($value eq "disabled") {
  948. readingsBeginUpdate($hash);
  949. readingsBulkUpdate($hash,"Action","event recording paused");
  950. readingsBulkUpdate($hash,"doStatistics","disabled");
  951. readingsEndUpdate($hash,0);
  952. DOIFtoolsSetNotifyDev($hash,1,0);
  953. } elsif ($value eq "enabled") {
  954. readingsBeginUpdate($hash);
  955. readingsBulkUpdate($hash,"Action","<html><div style=\"color:red;\" >recording events</div></html>");
  956. readingsBulkUpdate($hash,"doStatistics","enabled");
  957. readingsBulkUpdate($hash,".t0",gettimeofday());
  958. readingsEndUpdate($hash,0);
  959. DOIFtoolsSetNotifyDev($hash,1,1);
  960. }
  961. } elsif ($arg eq "statisticsTYPEs") {
  962. $value =~ s/\,/|/g;
  963. readingsBeginUpdate($hash);
  964. readingsBulkUpdate($hash,"statisticsTYPEs",$value);
  965. readingsEndUpdate($hash,0);
  966. DOIFtoolsDeleteStatReadings($hash);
  967. DOIFtoolsSetNotifyDev($hash,1,0);
  968. } elsif ($arg eq "recording_target_duration") {
  969. $value =~ m/(\d+)/;
  970. readingsSingleUpdate($hash,"recording_target_duration",$1 ? $1 : 0,0);
  971. } elsif ($arg eq "statisticsShowRate_ge") {
  972. $value =~ m/(\d+)/;
  973. readingsSingleUpdate($hash,"statisticsShowRate_ge",$1 ? $1 : 0,0);
  974. } elsif ($arg eq "specialLog") {
  975. if ($value) {
  976. readingsSingleUpdate($hash,"specialLog",1,0);
  977. DOIFtoolsSetNotifyDev($hash,1,1);
  978. } else {
  979. readingsSingleUpdate($hash,"specialLog",0,0);
  980. DOIFtoolsSetNotifyDev($hash,0,1);
  981. }
  982. } elsif ($arg eq "statisticsDeviceFilterRegex") {
  983. $ret = "Bad regexp: starting with *" if($value =~ m/^\*/);
  984. eval { "Hallo" =~ m/^$value$/ };
  985. $ret .= "\nBad regexp: $@" if($@);
  986. if ($ret or !$value) {
  987. readingsSingleUpdate($hash,"statisticsDeviceFilterRegex", ".*",0);
  988. return "$ret\nRegexp is set to: .*";
  989. } else {
  990. readingsSingleUpdate($hash,"statisticsDeviceFilterRegex", $value,0);
  991. }
  992. } else {
  993. my $hardcoded = "doStatistics:disabled,enabled,deleted specialLog:0,1";
  994. my $retL = "unknown argument $arg for $pn, choose one of statisticsTYPEs:multiple-strict,.*,$tL sourceAttribute:readingList targetDOIF:$dL targetDevice:$deL recording_target_duration:0,1,6,12,24,168 statisticsDeviceFilterRegex statisticsShowRate_ge ".(AttrVal($pn,"DOIFtoolsHideGetSet",0) ? $hardcoded :"");
  995. if (ReadingsVal($pn,"targetDOIF","")) {
  996. my $tn = ReadingsVal($pn,"targetDOIF","");
  997. my @rL = ();
  998. foreach my $key (keys %{$defs{$tn}->{READINGS}}) {
  999. push @rL, $key if ($key !~ "^(Device|state|error|cmd|e_|timer_|wait_|matched_|last_cmd|mode|\.eM)");
  1000. }
  1001. $retL .= " deleteReadingsInTargetDOIF:multiple-strict,".join(",", sort @rL);
  1002. }
  1003. if (ReadingsVal($pn,"targetDevice","")) {
  1004. my $tn = ReadingsVal($pn,"targetDevice","");
  1005. my @rL = ();
  1006. my $rx = ReadingsVal($pn,".debug","") ? "^(state)" : "^(state|[.])";
  1007. foreach my $key (keys %{$defs{$tn}->{READINGS}}) {
  1008. push @rL, $key if ($key !~ $rx);
  1009. }
  1010. $retL .= " deleteReadingsInTargetDevice:multiple-strict,".join(",", sort @rL);
  1011. }
  1012. return $retL;
  1013. }
  1014. return $ret;
  1015. }
  1016. sub DOIFtools_Get($@)
  1017. {
  1018. my ($hash, @a) = @_;
  1019. my $pn = $hash->{NAME};
  1020. my $arg = $a[1];
  1021. my $value = (defined $a[2]) ? $a[2] : "";
  1022. my $ret="";
  1023. my @ret=();
  1024. my @doifList = devspec2array("TYPE=DOIF");
  1025. my @ntL =();
  1026. my $dL = join(",",sort @doifList);
  1027. my $DE = AttrVal("global", "language", "") eq "DE" ? 1 : 0;
  1028. foreach my $i (@doifList) {
  1029. foreach my $key (keys %{$defs{$i}{READINGS}}) {
  1030. if ($key =~ m/^timer_\d\d_c\d\d/ && $defs{$i}{READINGS}{$key}{VAL} =~ m/.*\|[0-8]+/) {
  1031. push @ntL, $i;
  1032. last;
  1033. }
  1034. }
  1035. }
  1036. my $ntL = join(",",@ntL);
  1037. my %types = ();
  1038. foreach my $d (keys %defs ) {
  1039. next if(IsIgnored($d));
  1040. my $t = $defs{$d}{TYPE};
  1041. $types{$t} = "";
  1042. }
  1043. if ($arg eq "readingsGroup_for") {
  1044. foreach my $i (split(",",$value)) {
  1045. push @ret, DOIFtoolsRg($hash,$i);
  1046. }
  1047. $ret .= join("\n",@ret);
  1048. $ret = "<b>Definition for a simple readingsGroup prepared for import with \"Raw definition\":</b>\r--->\r$ret\r<---\r\r";
  1049. $ret = "<b>Die Definition einer einfachen readingsGroup ist für den Import mit \"Raw definition\"</b> vorbereitet:\r--->\r$ret\r<---\r\r" if ($DE);
  1050. Log3 $pn, 3, $ret if($ret);
  1051. return $ret;
  1052. } elsif ($arg eq "DOIF_to_Log") {
  1053. my @regex = ();
  1054. my $regex = "";
  1055. my $pnLog = "$hash->{TYPE}Log";
  1056. push @regex, $pnLog;
  1057. readingsSingleUpdate($hash,"doif_to_log",$value,0);
  1058. readingsSingleUpdate($hash,"specialLog",0,0) if (!$value);
  1059. DOIFtoolsSetNotifyDev($hash,0,1);
  1060. # return unless($value);
  1061. foreach my $i (split(",",$value)) {
  1062. push @regex, DOIFtoolsGetAssocDev($hash,$i);
  1063. }
  1064. @regex = keys %{{ map { $_ => 1 } @regex}};
  1065. $regex = join("|",@regex).":.*";
  1066. if (AttrVal($pn,"DOIFtoolsExecuteDefinition","")) {
  1067. push @ret, "Create device <b>$pnLog</b>.\n";
  1068. $ret = CommandDefMod(undef,"$pnLog FileLog ".AttrVal("global","logdir","./log/")."$pnLog-%Y-%j.log $regex");
  1069. push @ret, $ret if($ret);
  1070. $ret = CommandAttr(undef,"$pnLog mseclog ".AttrVal($pnLog,"mseclog","1"));
  1071. push @ret, $ret if($ret);
  1072. $ret = CommandAttr(undef,"$pnLog nrarchive ".AttrVal($pnLog,"nrarchive","3"));
  1073. push @ret, $ret if($ret);
  1074. $ret = CommandAttr(undef,"$pnLog disable ".($value ? "0" : "1"));
  1075. push @ret, $ret if($ret);
  1076. $ret = CommandSave(undef,undef) if (AttrVal($pn,"DOIFtoolsExecuteSave",""));
  1077. push @ret, $ret if($ret);
  1078. $ret = join("\n", @ret);
  1079. Log3 $pn, 3, $ret if($ret);
  1080. return $ret;
  1081. } else {
  1082. $ret = "<b>Definition for a FileLog prepared for import with \"Raw definition\":</b>\r--->\r";
  1083. $ret = "<b>Die FileLog-Definition ist zum Import mit \"Raw definition\"</b>vorbereitet:\r--->\r" if ($DE);
  1084. $ret .= "defmod $pnLog FileLog ".AttrVal("global","logdir","./log/")."$pnLog-%Y-%j.log $regex\r";
  1085. $ret .= "attr $pnLog mseclog 1\r<---\r\r";
  1086. return $ret;
  1087. }
  1088. } elsif ($arg eq "userReading_nextTimer_for") {
  1089. foreach my $i (split(",",$value)) {
  1090. push @ret, DOIFtoolsNxTimer($hash,$i);
  1091. }
  1092. $ret .= join("\n",@ret);
  1093. Log3 $pn, 3, $ret if($ret);
  1094. return $ret;
  1095. } elsif ($arg eq "statisticsReport") {
  1096. # event statistics
  1097. my $regex = ReadingsVal($pn,"statisticsDeviceFilterRegex",".*");
  1098. my $evtsum = 0;
  1099. my $evtlen = 15 + 2;
  1100. my $rate = 0;
  1101. my $typsum = 0;
  1102. my $typlen = 10 + 2;
  1103. my $typerate = 0;
  1104. my $allattr = "";
  1105. my $rx = AttrVal($pn,"DOIFtoolsHideStatReadings","") ? "\.stat_" : "stat_";
  1106. my $te = ReadingsVal($pn,".te",0)/3600;
  1107. my $compRate = ReadingsNum($pn,"statisticsShowRate_ge",0);
  1108. foreach my $typ ( keys %types) {
  1109. $typlen = length($typ)+2 > $typlen ? length($typ)+2 : $typlen;
  1110. }
  1111. foreach my $key (sort keys %{$defs{$pn}->{READINGS}}) {
  1112. $rate = ($te ? int($hash->{READINGS}{$key}{VAL}/$te + 0.5) : 0) if ($key =~ m/^$rx($regex)/);
  1113. if ($key =~ m/^$rx($regex)/ and $rate >= $compRate) {
  1114. $evtlen = length($1)+2 > $evtlen ? length($1)+2 : $evtlen;
  1115. }
  1116. }
  1117. $ret = "<b>".sprintf("%-".$typlen."s","TYPE").sprintf("%-".$evtlen."s","NAME").sprintf("%-12s","Number").sprintf("%-8s","Rate").sprintf("%-12s","<a href=\"https://wiki.fhem.de/wiki/Event#Beschr.C3.A4nken_von_Events\">Restriction</a>")."</b>\n";
  1118. $ret = "<b>".sprintf("%-".$typlen."s","TYPE").sprintf("%-".$evtlen."s","NAME").sprintf("%-12s","Anzahl").sprintf("%-8s","Rate").sprintf("%-12s","<a href=\"https://wiki.fhem.de/wiki/Event#Beschr.C3.A4nken_von_Events\">Begrenzung</a>")."</b>\n" if ($DE);
  1119. $ret .= sprintf("%-".$typlen."s","").sprintf("%-".$evtlen."s","").sprintf("%-12s","Events").sprintf("%-8s","1/h").sprintf("%-12s","event-on...")."\n";
  1120. $ret .= sprintf("-"x($typlen+$evtlen+33))."\n";
  1121. my $i = 0;
  1122. my $t = 0;
  1123. foreach my $typ (sort keys %types) {
  1124. $typsum = 0;
  1125. $t=0;
  1126. foreach my $key (sort keys %{$defs{$pn}->{READINGS}}) {
  1127. $rate = ($te ? int($hash->{READINGS}{$key}{VAL}/$te + 0.5) : 0) if ($key =~ m/^$rx($regex)/ and defined($defs{$1}) and $defs{$1}->{TYPE} eq $typ);
  1128. if ($key =~ m/^$rx($regex)/ and defined($defs{$1}) and $defs{$1}->{TYPE} eq $typ and $rate >= $compRate) {
  1129. $evtsum += $hash->{READINGS}{$key}{VAL};
  1130. $typsum += $hash->{READINGS}{$key}{VAL};
  1131. $allattr = " ".join(" ",keys %{$attr{$1}});
  1132. $ret .= sprintf("%-".$typlen."s",$typ).sprintf("%-".$evtlen."s",$1).sprintf("%-12s",$hash->{READINGS}{$key}{VAL}).sprintf("%-8s",$rate).sprintf("%-12s",($DE ? ($allattr =~ " event-on" ? "ja" : "nein") : ($allattr =~ " event-on" ? "yes" : "no")))."\n";
  1133. $i++;
  1134. $t++;
  1135. }
  1136. }
  1137. if ($t) {
  1138. $typerate = $te ? int($typsum/$te + 0.5) : 0;
  1139. if($typerate >= $compRate) {
  1140. $ret .= sprintf("%".($typlen+$evtlen+10)."s","="x10).sprintf("%2s"," ").sprintf("="x6)."\n";
  1141. if ($DE) {
  1142. $ret .= sprintf("%".($typlen+$evtlen)."s","Summe: ").sprintf("%-10s",$typsum).sprintf("%2s","&empty;:").sprintf("%-8s",$typerate)."\n";
  1143. $ret .= sprintf("%".($typlen+$evtlen+1)."s","Geräte: ").sprintf("%-10s",$t)."\n";
  1144. $ret .= sprintf("%".($typlen+$evtlen+1)."s","Events/Gerät: ").sprintf("%-10s",int($typsum/$t + 0.5))."\n";
  1145. } else {
  1146. $ret .= sprintf("%".($typlen+$evtlen)."s","Total: ").sprintf("%-10s",$typsum).sprintf("%2s","&empty;:").sprintf("%-8s",$typerate)."\n";
  1147. $ret .= sprintf("%".($typlen+$evtlen)."s","Devices: ").sprintf("%-10s",$t)."\n";
  1148. $ret .= sprintf("%".($typlen+$evtlen)."s","Events/device: ").sprintf("%-10s",int($typsum/$t + 0.5))."\n";
  1149. }
  1150. $ret .= "<div style=\"color:#d9d9d9\" >".sprintf("-"x($typlen+$evtlen+33))."</div>";
  1151. }
  1152. }
  1153. }
  1154. if ($DE) {
  1155. $ret .= sprintf("%".($typlen+$evtlen+10)."s","="x10).sprintf("%2s"," ").sprintf("="x6)."\n";
  1156. $ret .= sprintf("%".($typlen+$evtlen)."s","Summe: ").sprintf("%-10s",$evtsum).sprintf("%2s","&empty;:").sprintf("%-8s",$te ? int($evtsum/$te + 0.5) : "")."\n";
  1157. $ret .= sprintf("%".($typlen+$evtlen)."s","Dauer: ").sprintf("%d:%02d",int($te),int(($te-int($te))*60+.5))."\n";
  1158. $ret .= sprintf("%".($typlen+$evtlen+1)."s","Geräte: ").sprintf("%-10s",$i)."\n";
  1159. $ret .= sprintf("%".($typlen+$evtlen+1)."s","Events/Gerät: ").sprintf("%-10s",int($evtsum/$i + 0.5))."\n\n" if ($i);
  1160. fhem("count",1) =~ m/(\d+)/;
  1161. $ret .= sprintf("%".($typlen+$evtlen+1)."s","Geräte total: ").sprintf("%-10s","$1\n\n");
  1162. $ret .= sprintf("%".($typlen+$evtlen+1)."s","<u>Filter</u>\n");
  1163. $ret .= sprintf("%".($typlen+$evtlen)."s","TYPE: ").sprintf("%-10s",ReadingsVal($pn,"statisticsTYPEs","")."\n");
  1164. $ret .= sprintf("%".($typlen+$evtlen-7)."s","NAME: ").sprintf("%-10s",ReadingsVal($pn,"statisticsDeviceFilterRegex",".*")."\n");
  1165. $ret .= sprintf("%".($typlen+$evtlen-7)."s","Rate: ").sprintf("%-10s","&gt;= $compRate\n\n");
  1166. } else {
  1167. $ret .= sprintf("%".($typlen+$evtlen+10)."s","="x10).sprintf("%2s"," ").sprintf("="x6)."\n";
  1168. $ret .= sprintf("%".($typlen+$evtlen)."s","Total: ").sprintf("%-10s",$evtsum).sprintf("%2s","&empty;:").sprintf("%-8s",$te ? int($evtsum/$te + 0.5) : "")."\n";
  1169. $ret .= sprintf("%".($typlen+$evtlen)."s","Duration: ").sprintf("%d:%02d",int($te),int(($te-int($te))*60+.5))."\n";
  1170. $ret .= sprintf("%".($typlen+$evtlen)."s","Devices: ").sprintf("%-10s",$i)."\n";
  1171. $ret .= sprintf("%".($typlen+$evtlen)."s","Events/device: ").sprintf("%-10s",int($evtsum/$i + 0.5))."\n\n" if ($i);
  1172. fhem("count",1) =~ m/(\d+)/;
  1173. $ret .= sprintf("%".($typlen+$evtlen)."s","Devices total: ").sprintf("%-10s","$1\n\n");
  1174. $ret .= sprintf("%".($typlen+$evtlen+1)."s","<u>Filter</u>\n");
  1175. $ret .= sprintf("%".($typlen+$evtlen)."s","TYPE: ").sprintf("%-10s",ReadingsVal($pn,"statisticsTYPEs","")."\n");
  1176. $ret .= sprintf("%".($typlen+$evtlen-7)."s","NAME: ").sprintf("%-10s",ReadingsVal($pn,"statisticsDeviceFilterRegex",".*")."\n");
  1177. $ret .= sprintf("%".($typlen+$evtlen-7)."s","Rate: ").sprintf("%-10s","&gt;= $compRate\n\n");
  1178. }
  1179. $ret .= "<div style=\"color:#d9d9d9\" >".sprintf("-"x($typlen+$evtlen+33))."</div>";
  1180. # attibute statistics
  1181. if ($DE) {
  1182. $ret .= "<b>".sprintf("%-30s","genutzte Attribute in DOIF").sprintf("%-12s","Anzahl")."</b>\n";
  1183. } else {
  1184. $ret .= "<b>".sprintf("%-30s","used attributes in DOIF").sprintf("%-12s","Number")."</b>\n";
  1185. }
  1186. $ret .= sprintf("-"x42)."\n";
  1187. my %da = ();
  1188. foreach my $di (@doifList) {
  1189. foreach my $dia (keys %{$attr{$di}}) {
  1190. if ($modules{DOIF}{AttrList} =~ m/(^|\s)$dia(:|\s)/) {
  1191. if ($dia =~ "do|selftrigger|checkall") {
  1192. $dia = "* $dia ".AttrVal($di,$dia,"");
  1193. $da{$dia} = ($da{$dia} ? $da{$dia} : 0) + 1;
  1194. } else {
  1195. $dia = "* $dia";
  1196. $da{$dia} = ($da{$dia} ? $da{$dia} : 0) + 1;
  1197. }
  1198. } else {
  1199. $da{$dia} = ($da{$dia} ? $da{$dia} : 0) + 1;
  1200. }
  1201. }
  1202. }
  1203. foreach $i (sort keys %da) {
  1204. $ret .= sprintf("%-30s","$i").sprintf("%-12s","$da{$i}")."\n";
  1205. }
  1206. } elsif ($arg eq "checkDOIF") {
  1207. my @coll = ();
  1208. my $coll = "";
  1209. foreach my $di (@doifList) {
  1210. $coll = DOIFtoolsCheckDOIFcoll($hash,$di);
  1211. push @coll, $coll if($coll);
  1212. }
  1213. $ret .= join(" ",@coll);
  1214. if ($DE) {
  1215. $ret .= "\n<ul><li><b>DOELSIF</b> ohne <b>DOELSE</b> ist o.k., wenn der Status wechselt, bevor die selbe Bedingung wiederholt wahr wird,<br> andernfalls sollte <b>do always</b> genutzt werden (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#DOIF_do_always\">Steuerung durch Events</a>, <a target=\"_blank\" href=\"https://wiki.fhem.de/wiki/DOIF/Einsteigerleitfaden,_Grundfunktionen_und_Erl%C3%A4uterungen#Verhaltensweise_ohne_steuernde_Attribute\">Verhalten ohne Attribute</a>)</li></ul> \n" if (@coll);
  1216. } else {
  1217. $ret .= "\n<ul><li><b>DOELSIF</b> without <b>DOELSE</b> is o.k., if state changes between, the same condition becomes true again,<br>otherwise use attribute <b>do always</b> (<a target=\"_blank\" href=\"https://fhem.de/commandref_DE.html#DOIF_do_always\">controlling by events</a>, <a target=\"_blank\" href=\"https://wiki.fhem.de/wiki/DOIF/Einsteigerleitfaden,_Grundfunktionen_und_Erl%C3%A4uterungen#Verhaltensweise_ohne_steuernde_Attribute\">behaviour without attributes</a>)</li></ul> \n" if (@coll);
  1218. }
  1219. foreach my $di (@doifList) {
  1220. $ret .= DOIFtoolsCheckDOIF($hash,$di);
  1221. }
  1222. $ret = $DE ? ($ret ? "Empfehlung gefunden für:\n\n$ret" : "Keine Empfehlung gefunden.") : ($ret ? "Found recommendation for:\n\n$ret" : "No recommendation found.");
  1223. return $ret;
  1224. } elsif ($arg eq "runningTimerInDOIF") {
  1225. my $erg ="";
  1226. foreach my $di (@doifList) {
  1227. push @ret, sprintf("%-28s","$di").sprintf("%-40s",ReadingsVal($di,"wait_timer",""))."\n" if (ReadingsVal($di,"wait_timer","no timer") ne "no timer");
  1228. }
  1229. $ret .= join("",@ret);
  1230. $ret = $ret ? "Found running wait_timer for:\n\n$ret" : "No running wait_timer found.";
  1231. return $ret;
  1232. } else {
  1233. my $hardcoded = "checkDOIF:noArg statisticsReport:noArg runningTimerInDOIF:noArg";
  1234. return "unknown argument $arg for $pn, choose one of readingsGroup_for:multiple-strict,$dL DOIF_to_Log:multiple-strict,$dL userReading_nextTimer_for:multiple-strict,$ntL ".(AttrVal($pn,"DOIFtoolsHideGetSet",0) ? $hardcoded :"");
  1235. }
  1236. return $ret;
  1237. }
  1238. 1;
  1239. =pod
  1240. =item helper
  1241. =item summary tools to support DOIF
  1242. =item summary_DE Werkzeuge zur Unterstützung von DOIF
  1243. =begin html
  1244. <a name="DOIFtools"></a>
  1245. <h3>DOIFtools</h3>
  1246. <ul>
  1247. DOIFtools contains tools to support DOIF.<br>
  1248. <br>
  1249. <ul>
  1250. <li>create readingsGroup definitions for labeling frontend widgets.</li>
  1251. <li>create a debug logfile for some DOIF and quoted devices with optional device listing each state or wait timer update.</li>
  1252. <li>optional device listing in debug logfile each state or wait timer update.</li>
  1253. <li>navigation between device listings in logfile if opened via DOIFtools.</li>
  1254. <li>create userReadings in DOIF devices displaying real dates for weekday restricted timer.</li>
  1255. <li>delete user defined readings in DOIF devices with multiple choice.</li>
  1256. <li>delete visible readings in other devices with multiple choice, but not <i>state</i>.</li>
  1257. <li>record statistics data about events.</li>
  1258. <li>limitting recordig duration.</li>
  1259. <li>generate a statistics report.</li>
  1260. <li>lists every DOIF definition in <i>probably associated with</i>.</li>
  1261. <li>access to DOIFtools from any DOIF device via <i>probably associated with</i></li>
  1262. <li>access from DOIFtools to existing DOIFtoolsLog logfiles</li>
  1263. <li>show event monitor in device overview and optionally DOIF</li>
  1264. <li>check definitions and offer recommendations</li>
  1265. <li>create shortcuts</li>
  1266. <li>optionally create a menu entry</li>
  1267. <li>show a list of running wait timer</li>
  1268. </ul>
  1269. <br>
  1270. Just one definition per FHEM-installation is allowed. <a href="#DOIFtools"More in the german section.</a>
  1271. <br>
  1272. </ul>
  1273. =end html
  1274. =begin html_DE
  1275. <a name="DOIFtools"></a>
  1276. <h3>DOIFtools</h3>
  1277. <ul>
  1278. DOIFtools stellt Funktionen zur Unterstützung von DOIF-Geräten bereit.<br>
  1279. <br>
  1280. <ul>
  1281. <li>erstellen von readingsGroup Definitionen, zur Beschriftung von Frontendelementen.</li>
  1282. <li>erstellen eines Debug-Logfiles, in dem mehrere DOIF und zugehörige Geräte geloggt werden.</li>
  1283. <li>optionales DOIF-Listing bei jeder Status und Wait-Timer Aktualisierung im Debug-Logfile.</li>
  1284. <li>Navigation zwischen den DOIF-Listings im Logfile, wenn es über DOIFtools geöffnet wird.</li>
  1285. <li>erstellen von userReadings in DOIF-Geräten zur Anzeige des realen Datums bei Wochentag behafteten Timern.</li>
  1286. <li>löschen von benutzerdefinierten Readings in DOIF-Definitionen über eine Mehrfachauswahl.</li>
  1287. <li>löschen von Readings in anderen Geräten über eine Mehrfachauswahl, nicht <i>state</i>.</li>
  1288. <li>erfassen statistischer Daten über Events.</li>
  1289. <li>Begrenzung der Datenaufzeichnungsdauer.</li>
  1290. <li>erstellen eines Statistikreports.</li>
  1291. <li>Liste aller DOIF-Definitionen in <i>probably associated with</i>.</li>
  1292. <li>Zugriff auf DOIFtools aus jeder DOIF-Definition über die Liste in <i>probably associated with</i>.</li>
  1293. <li>Zugriff aus DOIFtools auf vorhandene DOIFtoolsLog-Logdateien.</li>
  1294. <li>zeigt den Event Monitor in der Detailansicht von DOIFtools.</li>
  1295. <li>ermöglicht den Zugriff auf den Event Monitor in der Detailansicht von DOIF.</li>
  1296. <li>prüfen der DOIF Definitionen mit Empfehlungen.</li>
  1297. <li>erstellen von Shortcuts</li>
  1298. <li>optionalen Menüeintrag erstellen</li>
  1299. <li>Liste der laufenden Wait-Timer anzeigen</li>
  1300. </ul>
  1301. <br>
  1302. <a name="DOIFtoolsBedienungsanleitung"></a>
  1303. <b>Bedienungsanleitung</b>
  1304. <br>
  1305. <ul>
  1306. Eine <a href="https://wiki.fhem.de/wiki/DOIFtools">Bedienungsanleitung für DOIFtools</a> gibt es im FHEM-Wiki.
  1307. </ul>
  1308. <br>
  1309. <a name="DOIFtoolsDefinition"></a>
  1310. <b>Definition</b>
  1311. <br>
  1312. <ul>
  1313. <code>define &lt;name&gt; DOIFtools</code><br>
  1314. Es ist nur eine Definition pro FHEM Installation möglich. Die Definition wird mit den vorhanden DOIF-Namen ergänzt, daher erscheinen alle DOIF-Geräte in der Liste <i>probably associated with</i>. Zusätzlich wird in jedem DOIF-Gerät in dieser Liste auf das DOIFtool verwiesen.<br>
  1315. <br>
  1316. <u>Definitionsvorschlag</u> zum Import mit <a href="https://wiki.fhem.de/wiki/DOIF/Import_von_Code_Snippets">Raw definition</a>:<br>
  1317. <code>
  1318. defmod DOIFtools DOIFtools<br>
  1319. attr DOIFtools DOIFtoolsEventMonitorInDOIF 1<br>
  1320. attr DOIFtools DOIFtoolsEMbeforeReadings 1<br>
  1321. attr DOIFtools DOIFtoolsExecuteDefinition 1<br>
  1322. attr DOIFtools DOIFtoolsExecuteSave 1<br>
  1323. attr DOIFtools DOIFtoolsMenuEntry 1<br>
  1324. attr DOIFtools DOIFtoolsMyShortcuts ##&lt;br&gt;My Shortcuts:,,list DOIFtools,fhem?cmd=list DOIFtools<br>
  1325. </code>
  1326. </ul>
  1327. <br>
  1328. <a name="DOIFtoolsSet"></a>
  1329. <b>Set</b>
  1330. <br>
  1331. <ul>
  1332. <code>set &lt;name&gt; deleteReadingInTargetDOIF &lt;readings to delete name&gt;</code><br>
  1333. <b>deleteReadingInTargetDOIF</b> löscht die benutzerdefinierten Readings im Ziel-DOIF<br>
  1334. <br>
  1335. <code>set &lt;name&gt; targetDOIF &lt;target name&gt;</code><br>
  1336. <b>targetDOIF</b> vor dem Löschen der Readings muss das Ziel-DOIF gesetzt werden.<br>
  1337. <br>
  1338. <code>set &lt;name&gt; deleteReadingInTargetDevice &lt;readings to delete name&gt;</code><br>
  1339. <b>deleteReadingInTargetDevice</b> löscht sichtbare Readings, ausser <i>state</i> im Ziel-Gerät. Bitte den Gefahrenhinweis zum Befehl <a href="https://fhem.de/commandref_DE.html#deletereading">deletereading</a> beachten!<br>
  1340. <br>
  1341. <code>set &lt;name&gt; targetDevice &lt;target name&gt;</code><br>
  1342. <b>targetDevice</b> vor dem Löschen der Readings muss das Ziel-Gerät gesetzt werden.<br>
  1343. <br>
  1344. <code>set &lt;name&gt; sourceAttribute &lt;readingList&gt; </code><br>
  1345. <b>sourceAttribute</b> vor dem Erstellen einer ReadingsGroup muss das Attribut gesetzt werden aus dem die Readings gelesen werden, um die ReadingsGroup zu erstellen und zu beschriften. <b>Default, readingsList</b><br>
  1346. <br>
  1347. <code>set &lt;name&gt; statisticsDeviceFilterRegex &lt;regular expression as device filter&gt;</code><br>
  1348. <b>statisticsDeviceFilterRegex</b> setzt einen Filter auf Gerätenamen, nur die gefilterten Geräte werden im Bericht ausgewertet. <b>Default, ".*"</b>.<br>
  1349. <br>
  1350. <code>set &lt;name&gt; statisticsTYPEs &lt;List of TYPE used for statistics generation&gt;</code><br>
  1351. <b>statisticsTYPEs</b> setzt eine Liste von TYPE für die Statistikdaten erfasst werden, bestehende Statistikdaten werden gelöscht. <b>Default, ""</b>.<br>
  1352. <br>
  1353. <code>set &lt;name&gt; statisticsShowRate_ge &lt;integer value for event rate&gt;</code><br>
  1354. <b>statisticsShowRate_ge</b> setzt eine Event-Rate, ab der ein Gerät in die Auswertung einbezogen wird. <b>Default, 0</b>.<br>
  1355. <br>
  1356. <code>set &lt;name&gt; specialLog &lt;0|1&gt;</code><br>
  1357. <b>specialLog</b> <b>1</b> DOIF-Listing bei Status und Wait-Timer Aktualisierung im Debug-Logfile. <b>Default, 0</b>.<br>
  1358. <br>
  1359. <code>set &lt;name&gt; doStatistics &lt;enabled|disabled|deleted&gt;</code><br>
  1360. <b>doStatistics</b><br>
  1361. &emsp;<b>deleted</b> setzt die Statistik zurück und löscht alle <i>stat_</i> Readings.<br>
  1362. &emsp;<b>disabled</b> pausiert die Statistikdatenerfassung.<br>
  1363. &emsp;<b>enabled</b> startet die Statistikdatenerfassung.<br>
  1364. <br>
  1365. <code>set &lt;name&gt; recording_target_duration &lt;hours&gt;</code><br>
  1366. <b>recording_target_duration</b> gibt an wie lange Daten erfasst werden sollen. <b>Default, 0</b> die Dauer ist nicht begrenzt.<br>
  1367. <br>
  1368. </ul>
  1369. <a name="DOIFtoolsGet"></a>
  1370. <b>Get</b>
  1371. <br>
  1372. <ul>
  1373. <code>get &lt;name&gt; DOIF_to_Log &lt;DOIF names for logging&gt;</code><br>
  1374. <b>DOIF_to_Log</b> erstellt eine FileLog-Definition, die für alle angegebenen DOIF-Definitionen loggt. Der <i>Reguläre Ausdruck</i> wird aus den, direkt in den DOIF-Greräte angegebenen und den wahrscheinlich verbundenen Geräten, ermittelt.<br>
  1375. <br>
  1376. <code>get &lt;name&gt; checkDOIF</code><br>
  1377. <b>checkDOIF</b> führt eine einfache Syntaxprüfung durch und empfiehlt Änderungen.<br>
  1378. <br>
  1379. <code>get &lt;name&gt; readingsGroup_for &lt;DOIF names to create readings groups&gt;</code><br>
  1380. <b>readingsGroup_for</b> erstellt readingsGroup-Definitionen für die angegebenen DOIF-namen. <b>sourceAttribute</b> verweist auf das Attribut, dessen Readingsliste als Basis verwendet wird. Die Eingabeelemente im Frontend werden mit den Readingsnamen beschriftet.<br>
  1381. <br>
  1382. <code>get &lt;name&gt; userReading_nextTimer_for &lt;DOIF names where to create real date timer readings&gt;</code><br>
  1383. <b>userReading_nextTimer_for</b> erstellt userReadings-Attribute für Timer-Readings mit realem Datum für Timer, die mit Wochentagangaben angegeben sind, davon ausgenommen sind indirekte Wochentagsangaben.<br>
  1384. <br>
  1385. <code>get &lt;name&gt; statisticsReport </code><br>
  1386. <b>statisticsReport</b> erstellt einen Bericht aus der laufenden Datenerfassung.<br><br>Die Statistik kann genutzt werden, um Geräte mit hohen Ereignisaufkommen zu erkennen. Bei einer hohen Rate, sollte im Interesse der Systemperformance geprüft werden, ob die <a href="https://wiki.fhem.de/wiki/Event">Events</a> eingeschränkt werden können. Werden keine Events eines Gerätes weiterverarbeitet, kann das Attribut <i>event-on-change-reading</i> auf <i>none</i> oder eine andere Zeichenfolge, die im Gerät nicht als Readingname vorkommt, gesetzt werden.<br>
  1387. <br>
  1388. <code>get &lt;name&gt; runningTimerInDOIF</code><br>
  1389. <b>runningTimerInDOIF</b> zeigt eine Liste der laufenden Timer. Damit kann entschieden werden, ob bei einem Neustart wichtige Timer gelöscht werden und der Neustart ggf. verschoben werden sollte.<br>
  1390. <br>
  1391. </ul>
  1392. <a name="DOIFtoolsAttribute"></a>
  1393. <b>Attribute</b><br>
  1394. <ul>
  1395. <code>attr &lt;name&gt; DOIFtoolsExecuteDefinition &lt;0|1&gt;</code><br>
  1396. <b>DOIFtoolsExecuteDefinition</b> <b>1</b> führt die erzeugten Definitionen aus. <b>Default 0</b>, zeigt die erzeugten Definitionen an, sie können mit <i>Raw definition</i> importiert werden.<br>
  1397. <br>
  1398. <code>attr &lt;name&gt; DOIFtoolsExecuteSave &lt;0|1&gt;</code><br>
  1399. <b>DOIFtoolsExecuteSave</b> <b>1</b>, die Definitionen werden automatisch gespeichert. <b>Default 0</b>, der Benutzer kann die Definitionen speichern.<br>
  1400. <br>
  1401. <code>attr &lt;name&gt; DOIFtoolsTargetGroup &lt;group names for target&gt;</code><br>
  1402. <b>DOIFtoolsTargetGroup</b> gibt die Gruppen für die zu erstellenden Definitionen an. <b>Default</b>, die Gruppe der Ursprungs Definition.<br>
  1403. <br>
  1404. <code>attr &lt;name&gt; DOIFtoolsTargetRoom &lt;room names for target&gt;</code><br>
  1405. <b>DOIFtoolsTargetRoom</b> gibt die Räume für die zu erstellenden Definitionen an. <b>Default</b>, der Raum der Ursprungs Definition.<br>
  1406. <br>
  1407. <code>attr &lt;name&gt; DOIFtoolsReadingsPrefix &lt;user defined prefix&gt;</code><br>
  1408. <b>DOIFtoolsReadingsPrefix</b> legt den Präfix der benutzerdefinierten Readingsnamen für die Zieldefinition fest. <b>Default</b>, DOIFtools bestimmt den Präfix.<br>
  1409. <br>
  1410. <code>attr &lt;name&gt; DOIFtoolsEventMonitorInDOIF &lt;1|0&gt;</code><br>
  1411. <b>DOIFtoolsEventMonitorInDOIF</b> <b>1</b>, die Anzeige des Event-Monitors wird in DOIF ermöglicht. <b>Default 0</b>, kein Zugriff auf den Event-Monitor im DOIF.<br>
  1412. <br>
  1413. <code>attr &lt;name&gt; DOIFtoolsEMbeforeReadings &lt;1|0&gt;</code><br>
  1414. <b>DOIFtoolsEMbeforeReading</b> <b>1</b>, die Anzeige des Event-Monitors wird in DOIF direkt über den Readings angezeigt. <b>Default 0</b>, anzeige des Event-Monitors über den Internals.<br>
  1415. <br>
  1416. <code>attr &lt;name&gt; DOIFtoolsHideGetSet &lt;0|1&gt;</code><br>
  1417. <b>DOIFtoolsHideModulGetSet</b> <b>1</b>, verstecken der Set- und Get-Shortcuts. <b>Default 0</b>.<br>
  1418. <br>
  1419. <code>attr &lt;name&gt; DOIFtoolsNoLookUp &lt;0|1&gt;</code><br>
  1420. <b>DOIFtoolsNoLookUp</b> <b>1</b>, es werden keine Lookup-Fenster in DOIFtools geöffnet. <b>Default 0</b>.<br>
  1421. <br>
  1422. <code>attr &lt;name&gt; DOIFtoolsNoLookUpInDOIF &lt;0|1&gt;</code><br>
  1423. <b>DOIFtoolsNoLookUpInDOIF</b> <b>1</b>, es werden keine Lookup-Fenster in DOIF geöffnet. <b>Default 0</b>.<br>
  1424. <br>
  1425. <code>attr &lt;name&gt; DOIFtoolsHideModulShortcuts &lt;0|1&gt;</code><br>
  1426. <b>DOIFtoolsHideModulShortcuts</b> <b>1</b>, verstecken der DOIFtools Shortcuts. <b>Default 0</b>.<br>
  1427. <br>
  1428. <code>attr &lt;name&gt; DOIFtoolsHideStatReadings &lt;0|1&gt;</code><br>
  1429. <b>DOIFtoolsHideStatReadings</b> <b>1</b>, verstecken der <i>stat_</i> Readings. Das Ändern des Attributs löscht eine bestehende Event-Aufzeichnung. <b>Default 0</b>.<br>
  1430. <br>
  1431. <code>attr &lt;name&gt; DOIFtoolsEventOnDeleted &lt;0|1&gt;</code><br>
  1432. <b>DOIFtoolsEventOnDeleted</b> <b>1</b>, es werden Events für alle <i>stat_</i> erzeugt, bevor sie gelöscht werden. Damit könnten die erfassten Daten geloggt werden. <b>Default 0</b>.<br>
  1433. <br>
  1434. <code>attr &lt;name&gt; DOIFtoolsMyShortcuts &lt;shortcut name&gt,&lt;command&gt;, ...</code><br>
  1435. <b>DOIFtoolsMyShortcuts</b> &lt;Bezeichnung&gt;<b>,</b>&lt;Befehl&gt;<b>,...</b> anzeigen eigener Shortcuts, siehe globales Attribut <a href="#menuEntries">menuEntries</a>.<br>
  1436. Zusätzlich gilt, wenn ein Eintrag mit ## beginnt und mit ,, endet, wird er als HTML interpretiert.<br>
  1437. <u>Beispiel:</u><br>
  1438. <code>attr DOIFtools DOIFtoolsMyShortcuts ##&lt;br&gt;My Shortcuts:,,list DOIFtools,fhem?cmd=list DOIFtools</code><br>
  1439. <br>
  1440. <code>attr &lt;name&gt; DOIFtoolsMenuEntry &lt;0|1&gt;</code><br>
  1441. <b>DOIFtoolsMenuEntry</b> <b>1</b>, erzeugt einen Menüeintrag im FHEM-Menü. <b>Default 0</b>.<br>
  1442. <br>
  1443. <a href="#disabledForIntervals"><b>disabledForIntervals</b></a> pausiert die Statistikdatenerfassung.<br>
  1444. <br>
  1445. </ul>
  1446. <a name="DOIFtoolsReadings"></a>
  1447. <b>Readings</b>
  1448. <br>
  1449. <ul>
  1450. DOIFtools erzeugt bei der Aktualisierung von Readings keine Events, daher muss die Seite im Browser aktualisiert werden, um aktuelle Werte zu sehen.<br>
  1451. <br>
  1452. <li><b>Action</b> zeigt den Status der Event-Aufzeichnung an.</li>
  1453. <li><b>DOIF_version</b> zeigt die Version des DOIF an.</li>
  1454. <li><b>FHEM_revision</b> zeigt die Revision von FHEM an.</li>
  1455. <li><b>doStatistics</b> zeigt den Status der Statistikerzeugung an</li>
  1456. <li><b>logfile</b> gibt den Pfad und den Dateinamen mit Ersetzungszeichen an.</li>
  1457. <li><b>recording_target_duration</b> gibt an wie lange Daten erfasst werden sollen.</li>
  1458. <li><b>stat_</b>&lt;<b>devicename</b>&gt; zeigt die Anzahl der gezählten Ereignisse, die das jeweilige Gerät erzeugt hat.</li>
  1459. <li><b>statisticHours</b> zeigt die kumulierte Zeit für den Status <i>enabled</i> an, während der, Statistikdaten erfasst werden.</li>
  1460. <li><b>statisticShowRate_ge</b> zeigt die Event-Rate, ab der Geräte in die Auswertung einbezogen werden.</li>
  1461. <li><b>statisticsDeviceFilterRegex</b> zeigt den aktuellen Gerätefilterausdruck an.</li>
  1462. <li><b>statisticsTYPEs</b> zeigt eine Liste von <i>TYPE</i> an, für deren Geräte die Statistik erzeugt wird.</li>
  1463. <li><b>targetDOIF</b> zeigt das Ziel-DOIF, bei dem Readings gelöscht werden sollen.</li>
  1464. <li><b>targetDevice</b> zeigt das Ziel-Gerät, bei dem Readings gelöscht werden sollen.</li>
  1465. </ul>
  1466. </br>
  1467. <a name="DOIFtoolsLinks"></a>
  1468. <b>Links</b>
  1469. <br>
  1470. <ul>
  1471. <a href="https://forum.fhem.de/index.php/topic,63938.0.html">DOIFtools im FHEM-Forum</a><br>
  1472. <a href="https://wiki.fhem.de/wiki/DOIFtools">DOIFtools im FHEM-Wiki</a>
  1473. </ul>
  1474. </ul>
  1475. =end html_DE
  1476. =cut