From d844928f195b220a103c9be7d0bafe79e13fab1a Mon Sep 17 00:00:00 2001 From: Ellert Date: Tue, 17 Jan 2017 23:34:49 +0000 Subject: [PATCH] DOIFtools:a new module mainly supporting DOIF by providing tools git-svn-id: https://svn.fhem.de/fhem/trunk@13131 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/98_DOIFtools.pm | 1011 +++++++++++++++++++++++++++++++++++++ 1 file changed, 1011 insertions(+) create mode 100644 fhem/FHEM/98_DOIFtools.pm diff --git a/fhem/FHEM/98_DOIFtools.pm b/fhem/FHEM/98_DOIFtools.pm new file mode 100644 index 000000000..690261c42 --- /dev/null +++ b/fhem/FHEM/98_DOIFtools.pm @@ -0,0 +1,1011 @@ +############################################# +# $Id$ +# +# This file is part of fhem. +# +# Fhem is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# Fhem is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with fhem. If not, see . +# +############################################### + + +package main; +use strict; +use warnings; +use Time::Local; + +sub DOIFtools_Initialize($); +sub DOIFtools_Set($@); +sub DOIFtools_Get($@); +sub DOIFtools_Undef; +sub DOIFtools_Define($$$); +sub DOIFtools_Attr(@); +sub DOIFtools_Notify($$); +sub DOIFtoolsRg; +sub DOIFtoolsNxTimer; +sub DOIFtoolsNextTimer; +sub DOIFtoolsGetAssocDev; +sub DOIFtoolsCheckDOIF; +sub DOIFtoolsCheckDOIFcoll; +sub DOIFtools_fhemwebFn($$$$); +sub DOIFtools_eM($$$$); +sub DOIFtools_dO ($$$$); +sub DOIFtoolsSetNotifyDev; +sub DOIFtools_logWrapper($); +sub DOIFtoolsCounterReset($); + +my @DOIFtools_we =(); + + +######################### +sub DOIFtools_Initialize($) +{ + my ($hash) = @_; + $hash->{DefFn} = "DOIFtools_Define"; + $hash->{SetFn} = "DOIFtools_Set"; + $hash->{GetFn} = "DOIFtools_Get"; + $hash->{UndefFn} = "DOIFtools_Undef"; + $hash->{AttrFn} = "DOIFtools_Attr"; + $hash->{NotifyFn} = "DOIFtools_Notify"; + + $hash->{FW_detailFn} = "DOIFtools_fhemwebFn"; + + $data{FWEXT}{"/DOIFtools_logWrapper"}{CONTENTFUNC} = "DOIFtools_logWrapper"; + + my $oldAttr = "target_room:noArg target_group:noArg executeDefinition:noArg executeSave:noArg eventMonitorInDOIF:noArg readingsPrefix:noArg"; + $hash->{AttrList} = "DOIFtoolsExecuteDefinition:1,0 DOIFtoolsTargetRoom DOIFtoolsTargetGroup DOIFtoolsExecuteSave:1,0 DOIFtoolsReadingsPrefix DOIFtoolsEventMonitorInDOIF:1,0 DOIFtoolsHideModulShortcuts:1,0 DOIFtoolsMyShortcuts DOIFtoolsMenuEntry:1,0 disabledForIntervals ".$oldAttr; +} + + +sub DOIFtools_dO ($$$$){return "";} +# FW_detailFn for DOIF injecting event monitor +sub DOIFtools_eM($$$$) { + my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn. + my $ret = ""; + # Event Monitor + $ret .= "

Event monitor: on  "; + $ret .= "off"; + $ret .= "
"; + + my $a = ""; + if (ReadingsVal($d,".eM","off") eq "on") { + $ret .= ""; + my $filter = $a ? ($a eq "log" ? "global" : $a) : ".*"; + $ret .= "

"; + $ret .= "Events (Filter: $filter) ". + "  FHEM log ". + "". + "  
\n"; + $ret .= "
"; + $ret .= ""; + $ret .= "
"; + } + return $ret; +} +###################### +# Show the content of the log (plain text), or an image and offer a link +# to convert it to an SVG instance +# If text and no reverse required, try to return the data as a stream; +sub DOIFtools_logWrapper($) { + my ($cmd) = @_; + + my $d = $FW_webArgs{dev}; + my $type = $FW_webArgs{type}; + my $file = $FW_webArgs{file}; + my $ret = ""; + + if(!$d || !$type || !$file) { + FW_pO '
DOIFtools_logWrapper: bad arguments
'; + return 0; + } + + if(defined($type) && $type eq "text") { + $defs{$d}{logfile} =~ m,^(.*)/([^/]*)$,; # Dir and File + my $path = "$1/$file"; + $path =~ s/%L/$attr{global}{logdir}/g + if($path =~ m/%/ && $attr{global}{logdir}); + $path = AttrVal($d,"archivedir","") . "/$file" if(!-f $path); + + FW_pO "
"; + FW_pO "
" if($FW_ss); + FW_pO "
jump to: the end first listing
"; + my $suffix = "
jump to: the top first listing
".($FW_ss ? "
" : "")."
"; + + my $reverseLogs = AttrVal($FW_wname, "reverseLogs", 0); + if(!$reverseLogs) { + $suffix .= ""; + return FW_returnFileAsStream($path, $suffix, "text/html", 0, 0); + } + + if(!open(FH, $path)) { + FW_pO "
$path: $!
"; + return 0; + } + my $cnt = join("", reverse ); + close(FH); +# $cnt = FW_htmlEscape($cnt); + FW_pO $cnt; + FW_pO $suffix; + return 1; + } + return 0; +} + +sub DOIFtools_fhemwebFn($$$$) { + my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn. + my $ret = ""; + # Logfile Liste + if($FW_ss && $pageHash) { + $ret."
". + "$defs{$d}{STATE}
"; + } else { + my $row = 0; + $ret .= sprintf("", + $pageHash ? "" : "block "); + foreach my $f (FW_fileList($defs{$d}{logfile})) { + my $class = (!$pageHash ? (($row++&1)?"odd":"even") : ""); + $ret .= ""; + $ret .= ""; + my $idx = 0; + foreach my $ln (split(",", AttrVal($d, "logtype", "text"))) { + if($FW_ss && $idx++) { + $ret .= "
$f
"; + } + my ($lt, $name) = split(":", $ln); + $name = $lt if(!$name); + $ret .= FW_pH("$FW_ME/DOIFtools_logWrapper&dev=$d&type=$lt&file=$f", + "
$name
", 1, "dval", 1); + } + } + $ret .= "
"; + } + # Event Monitor + $ret .= "

Event monitor: on  "; + $ret .= "off  "; + $ret .= "Shortcuts: " if (!AttrVal($d,"DOIFtoolsHideModulShortcuts",0) or AttrVal($d,"DOIFtoolsMyShortcuts","")); + if (!AttrVal($d,"DOIFtoolsHideModulShortcuts",0)) { + $ret .= "reload DOIFtools  " if(ReadingsVal($d,".debug","")); + $ret .= "update check  "; + $ret .= "update  " if(!ReadingsVal($d,".debug","")); + $ret .= "update  " if(ReadingsVal($d,".debug","")); + $ret .= "shutdown restart  "; + $ret .= "fheminfo send  "; + } + if (AttrVal($d,"DOIFtoolsMyShortcuts","")) { + my @sc = split(",",AttrVal($d,"DOIFtoolsMyShortcuts","")); + for (my $i = 0; $i < @sc; $i+=2) { + if ($sc[$i] =~ m/^\#\#(.*)/) { + $ret .= "$1  "; + } else { + $ret .= "$sc[$i]  " if($sc[$i] and $sc[$i+1]); + } + } + } + $ret .= "
"; + my $a = ""; + if (ReadingsVal($d,".eM","off") eq "on") { + $ret .= ""; + my $filter = $a ? ($a eq "log" ? "global" : $a) : ".*"; + $ret .= "

"; + $ret .= "Events (Filter: $filter) ". + "  FHEM log ". + "". + "  
\n"; + $ret .= "
"; + $ret .= ""; + $ret .= "
"; + } + return $ret; +} +sub DOIFtools_Notify($$) { + my ($hash, $source) = @_; + my $pn = $hash->{NAME}; + my $sn = $source->{NAME}; + my $events = deviceEvents($source,1); + return if( !$events ); + # \@DOIFtools_we aktualisieren + if ($sn eq AttrVal("global","holiday2we","")) { + my $we; + my $val; + my $a; + my $b; + for (my $i = 0; $i < 8; $i++) { + $DOIFtools_we[$i] = 0; + $val = CommandGet(undef,"get $sn days $i"); + if($val) { + ($a, $b) = ReplaceEventMap($sn, [$sn, $val], 0); + $DOIFtools_we[$i] = 1 if($b ne "none"); + } + } + } + my $ldi = ReadingsVal($pn,"specialLog","") ? ReadingsVal($pn,"doif_to_log","") : ""; + foreach my $event (@{$events}) { + $event = "" if(!defined($event)); + # add list to DOIFtoolsLog + if ($ldi and $ldi =~ "$sn" and $event =~ m/(^cmd: \d+(\.\d+)?|^wait_timer: \d\d.*)/) { + $hash->{helper}{counter}{0}++; + my $trig = "{helper}{counter}{0}\">"; + $trig .= "\[$hash->{helper}{counter}{0}\] +++++ Listing $sn:$1 +++++\n"; + my $prev = $hash->{helper}{counter}{0} - 1; + my $next = $hash->{helper}{counter}{0} + 1; + $trig .= $prev ? "jump to: prev  next Listing
" : "jump to: next Listing
"; + $trig .= "DOIF-Version: ".ReadingsVal($pn,"DOIF_version","n/a")."
"; + $trig .= CommandList(undef,$sn); + foreach my $itm (keys %defs) { + $trig =~ s,([\[\" ])$itm([\"\:\] ]),$1$itm$2,g; + } + CommandTrigger(undef,"$hash->{TYPE}Log $trig"); + } + # DOIFtools DEF addition + if ($sn eq "global" and $event =~ "MODIFIED|INITIALIZED|DEFINED|DELETED|RENAMED|UNDEFINED") { + my @doifList = devspec2array("TYPE=DOIF"); + $hash->{DEF} = "associated DOIF: ".join(" ",sort @doifList); + readingsSingleUpdate($hash,"DOIF_version",fhem("version 98_DOIF.pm noheader",1),0); + } + # get DOIF version and FHEM revision + if ($sn eq "global" and $event =~ "INITIALIZED|MODIFIED $pn") { + readingsBeginUpdate($hash); + readingsBulkUpdate($hash,"DOIF_version",fhem("version 98_DOIF.pm noheader",1)); + readingsBulkUpdate($hash,"FHEM_revision",fhem("version revision noheader",1)); + readingsEndUpdate($hash,0); + $defs{$pn}{VERSION} = fhem("version 98_DOIFtools.pm noheader",1); + DOIFtoolsSetNotifyDev($hash,1,1); + #set new attributes and delete old ones + CommandAttr(undef,"$pn DOIFtoolsExecuteDefinition ".AttrVal($pn,"executeDefinition","")) if (AttrVal($pn,"executeDefinition","")); + CommandDeleteAttr(undef,"$pn executeDefinition") if (AttrVal($pn,"executeDefinition","")); + CommandAttr(undef,"$pn DOIFtoolsExecuteSave ".AttrVal($pn,"executeSave","")) if (AttrVal($pn,"executeSave","")); + CommandDeleteAttr(undef,"$pn executeSave") if (AttrVal($pn,"executeSave","")); + CommandAttr(undef,"$pn DOIFtoolsTargetRoom ".AttrVal($pn,"target_room","")) if (AttrVal($pn,"target_room","")); + CommandDeleteAttr(undef,"$pn target_room") if (AttrVal($pn,"target_room","")); + CommandAttr(undef,"$pn DOIFtoolsTargetGroup ".AttrVal($pn,"target_group","")) if (AttrVal($pn,"target_group","")); + CommandDeleteAttr(undef,"$pn target_group") if (AttrVal($pn,"target_group","")); + CommandAttr(undef,"$pn DOIFtoolsReadingsPrefix ".AttrVal($pn,"readingsPrefix","")) if (AttrVal($pn,"readingsPrefix","")); + CommandDeleteAttr(undef,"$pn readingsPrefix") if (AttrVal($pn,"readingsPrefix","")); + CommandAttr(undef,"$pn DOIFtoolsEventMonitorInDOIF ".AttrVal($pn,"eventMonitorInDOIF","")) if (AttrVal($pn,"eventMonitorInDOIF","")); + CommandDeleteAttr(undef,"$pn eventMonitorInDOIF") if (AttrVal($pn,"eventMonitorInDOIF","")); + CommandSave(undef,undef); + } + # Event monitor in DOIF + if ($modules{DOIF}{LOADED} and !defined $modules{DOIF}->{FW_detailFn} and $sn eq "global" and $event =~ "INITIALIZED" and AttrVal($pn,"DOIFtoolsEventMonitorInDOIF","")) { + $modules{DOIF}->{FW_detailFn} = "DOIFtools_eM" if (!defined $modules{DOIF}->{FW_detailFn}); + readingsSingleUpdate($hash,".DOIFdO",$modules{DOIF}->{FW_deviceOverview},0); + $modules{DOIF}->{FW_deviceOverview} = 1; + } + # Statistics event recording + 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)) { + readingsSingleUpdate($hash,"stat_$sn",ReadingsVal($pn,"stat_$sn",0)+1,0); + } + } + #statistics time counter updating + if (ReadingsVal($pn,"doStatistics","disabled") eq "enabled" and !IsDisabled($pn) and $sn ne "global") { + if (ReadingsVal($pn,"statisticHours",0) <= ReadingsVal($pn,"recording_target_duration",0) or ReadingsVal($pn,"recording_target_duration",0) == 0) { + my $t = gettimeofday(); + my $te = ReadingsVal($pn,".te",gettimeofday()) + $t - ReadingsVal($pn,".t0",gettimeofday()); + my $tH = int($te*100/3600 +.5)/100; + readingsBeginUpdate($hash); + readingsBulkUpdate($hash,".te",$te); + readingsBulkUpdate($hash,".t0",$t); + readingsBulkUpdate($hash,"statisticHours",sprintf("%.2f",$tH)); + readingsEndUpdate($hash,0); + } else { + DOIFtoolsSetNotifyDev($hash,1,0); + readingsSingleUpdate($hash,"doStatistics","disabled",0); + } + } + return undef; +} +sub DOIFtoolsRg +{ + my ($hash,$arg) = @_; + my $pn = $hash->{NAME}; + my $pnRg= "rg_$arg"; + my $ret = ""; + my @ret; + my $defRg = ""; + my @defRg; + my $cL = ""; + my @rL = split(/ /,AttrVal($arg,"readingList","")); + for (my $i=0; $i<@rL; $i++) { + $defRg .= ",<$rL[$i]>,$rL[$i]"; + $cL .= "\"$rL[$i]\"=>\"$rL[$i]:\","; + } + push @defRg, "$pnRg readingsGroup $arg:+STATE$defRg"; + my $rooms = AttrVal($pn,"DOIFtoolsTargetRoom","") ? AttrVal($pn,"DOIFtoolsTargetRoom","") : AttrVal($arg,"room",""); + push @defRg, "$pnRg room $rooms" if($rooms); + my $groups = AttrVal($pn,"DOIFtoolsTargetGroup","") ? AttrVal($pn,"DOIFtoolsTargetGroup","") : AttrVal($arg,"group",""); + push @defRg, "$pnRg group $groups" if($groups); + push @defRg, "$pnRg commands {$cL}" if ($cL); + push @defRg, "$pnRg noheading 1"; + $defRg = "defmod $defRg[0]\rattr ".join("\rattr ",@defRg[1..@defRg-1]); + if (AttrVal($pn,"DOIFtoolsExecuteDefinition","")) { + $ret = CommandDefMod(undef,$defRg[0]); + push @ret, $ret if ($ret); + for (my $i = 1; $i < @defRg; $i++) { + $ret = CommandAttr(undef,$defRg[$i]); + push @ret, $ret if ($ret); + } + if (@ret) { + $ret = join("\n", @ret); + return $ret; + } else { + $ret = "Created device $pnRg.\n"; + $ret .= CommandSave(undef,undef) if (AttrVal($pn,"DOIFtoolsExecuteSave","")); + return $ret; + } + } else { + $defRg =~ s//>/g; + return $defRg; + } +} +# calculate real date in userReadings +sub DOIFtoolsNextTimer { + my ($timer_str) = @_; + $timer_str =~ /(\d\d).(\d\d).(\d\d\d\d) (\d\d):(\d\d):(\d\d)\|([0-8]+)/; + my $tdays = $7; + return "$1.$2.$3 $4:$5:$6" if (length($7)==0); + my $timer = timelocal($6,$5,$4,$1,$2-1,$3); + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($timer); + my $ilook = 0; + my $we; + for (my $iday = $wday; $iday < 7; $iday++) { + $we = (($iday==0 || $iday==6) ? 1 : 0); + if(!$we) { + $we = $DOIFtools_we[$ilook + 1]; + } + if ($tdays =~ /$iday/ or ($tdays =~ /7/ and $we) or ($tdays =~ /8/ and !$we)) { + return strftime("%d.%m.%Y %H:%M:%S",localtime($timer + $ilook * 86400)); + } + $ilook++; + } + for (my $iday = 0; $iday < $wday; $iday++) { + $we = (($iday==0 || $iday==6) ? 1 : 0); + if(!$we) { + $we = $DOIFtools_we[$ilook + 1]; + } + if ($tdays =~ /$iday/ or ($tdays =~ /7/ and $we) or ($tdays =~ /8/ and !$we)) { + return strftime("%d.%m.%Y %H:%M:%S",localtime($timer + $ilook * 86400)); + } + $ilook++; + } +} + +sub DOIFtoolsNxTimer { + my ($hash,$arg) = @_; + my $pn = $hash->{NAME}; + my $tn= $arg; + my $thash = $defs{$arg}; + my $ret = ""; + my @ret; + foreach my $key (keys %{$thash->{READINGS}}) { + if ($key =~ m/^timer_\d\d_c\d\d/ && $thash->{READINGS}{$key}{VAL} =~ m/.*\|[0-8]+/) { + $ret = AttrVal($pn,"DOIFtoolsReadingsPrefix","N_")."$key:$key.* \{DOIFtoolsNextTimer(ReadingsVal(\"$tn\",\"$key\",\"none\"))\}"; + push @ret, $ret if ($ret); + } + } + if (@ret) { + $ret = join(",", @ret); + if (!AttrVal($tn,"userReadings","")) { + CommandAttr(undef,"$tn userReadings $ret"); + $ret = "Created userReadings for $tn.\n"; + $ret .= CommandSave(undef,undef) if (AttrVal($pn,"DOIFtoolsExecuteSave","")); + return $ret; + } else { + $ret = "A userReadings Attribute already exists, adding is not implemented, try it manually.\r\r $ret\r"; + return $ret; + } + } + return join("\n", @ret); +} + +sub DOIFtoolsGetAssocDev { + my ($hash,$arg) = @_; + my $pn = $hash->{NAME}; + my $tn= $arg; + my $thash = $defs{$arg}; + my $ret = ""; + my @ret = (); + push @ret ,$arg; + $ret .= $thash->{devices}{all} if ($thash->{devices}{all}); + $ret =~ s/^\s|\s$//; + push @ret, split(/ /,$ret); + push @ret, getPawList($tn); + return @ret; +} + +sub DOIFtoolsCheckDOIFcoll { + my ($hash,$tn) = @_; + my $ret = ""; + my $tail = $defs{$tn}{DEF}; + if (!$tail) { + $tail=""; + } else { + $tail =~ s/(##.*\n)|(##.*$)|\n/ /g; + } + return("") if ($tail =~ /^ *$/); + $ret .= $tn if ($tail =~ m/(DOELSEIF )/ and !($tail =~ m/(DOELSE )/) and AttrVal($tn,"do","") !~ "always"); + return $ret; +} + +sub DOIFtoolsCheckDOIF { + my ($hash,$tn) = @_; + my $ret = ""; + my $tail = $defs{$tn}{DEF}; + if (!$tail) { + $tail=""; + } else { + $tail =~ s/(##.*\n)|(##.*$)|\n/ /g; + } + return("") if ($tail =~ /^ *$/); + $ret .= "
  • replace DOIF name with \$SELF (utilization of events)
  • \n" if ($tail =~ m/[\[|\?]($tn)/); + $ret .= "
  • replace ReadingsVal(...) with [name:reading] (controlling by events)
  • \n" if ($tail =~ m/(ReadingsVal)/); + $ret .= "
  • replace ReadingsNum(...) with [name:reading:d] (filtering numbers)
  • \n" if ($tail =~ m/(ReadingsNum)/); + $ret .= "
  • replace InternalVal(...) with [name:&internal] (controlling by events)
  • \n" if ($tail =~ m/(InternalVal)/); + $ret .= "
  • replace $1...\")} with $2... (plain FHEM command)
  • \n" if ($tail =~ m/(\{\s*fhem.*?\"\s*(set|get))/); + $ret .= "
  • replace {system \"<shell command>\"} with \"\<shell command>\" (plain FHEM shell command, non blocking)
  • \n" if ($tail =~ m/(\{\s*system.*?\})/); + $ret .= "
  • sleep is not recommended in DOIF, use attribute wait for (delay)
  • \n" if ($tail =~ m/(sleep\s\d+\.?\d+\s*[;|,]?)/); + $ret .= "
  • replace [name:?regex] by [name:\"regex\"] (avoid old syntax)
  • \n" if ($tail =~ m/(\[.*?[^"]?:[^"]?\?.*?\])/); + + $ret .= "
  • the first command after DOELSE seems to be a condition indicated by $2, check it.
  • \n" if ($tail =~ m/(DOELSE .*?\]\s*?(\!\S|\=\~|\!\~|and|or|xor|not|\|\||\&\&|\=\=|\!\=|ne|eq|lt|gt|le|ge)\s*?).*?\)/); + my @wait = SplitDoIf(":",AttrVal($tn,"wait","")); + my @sub0 = (); + my @tmp = (); + if (@wait and !AttrVal($tn,"timerWithWait","")) { + for (my $i = 0; $i < @wait; $i++) { + ($sub0[$i],@tmp) = SplitDoIf(",",$wait[$i]); + $sub0[$i] =~ s/\s// if($sub0[$i]); + } + foreach my $key (sort keys %{$defs{$tn}{timeCond}}) { + if ($defs{$tn}{timeCond}{$key} and $sub0[$defs{$tn}{timeCond}{$key}]) { + $ret .= "
  • Timer in condition and wait timer for commands in the same DOIF branch.
    If you observe unexpected behaviour, try attribute timerWithWait (delay of Timer)
  • \n"; + last; + } + } + } + $ret = $ret ? "$tn\n
      $ret
    " : ""; + return $ret; +} + +# param: $hash, doif_to_log, statisticsTypes as 1 or 0 +sub DOIFtoolsSetNotifyDev { + my ($hash,@a) = @_; + my $pn = $hash->{NAME}; + $hash->{NOTIFYDEV} = "global"; + $hash->{NOTIFYDEV} .= ",$attr{global}{holiday2we}" if ($attr{global}{holiday2we}); + $hash->{NOTIFYDEV} .= ",".ReadingsVal($pn,"doif_to_log","") if ($a[0] and ReadingsVal($pn,"doif_to_log","") and ReadingsVal($pn,"specialLog",0)); + $hash->{NOTIFYDEV} .= ",TYPE=".ReadingsVal($pn,"statisticsTYPEs","") if ($a[1] and ReadingsVal($pn,"statisticsTYPEs","") and ReadingsVal($pn,"doStatistics","deleted") eq "enabled"); + return undef; +} +sub DOIFtoolsCounterReset($) { + my ($pn) = @_; + RemoveInternalTimer($pn,"DOIFtoolsCounterReset"); + $defs{$pn}->{helper}{counter}{0} = 0; + my $nt = gettimeofday(); + my @lt = localtime($nt); + $nt -= ($lt[2]*3600+$lt[1]*60+$lt[0]); # Midnight + $nt += 86400 + 3; # Tomorrow + InternalTimer($nt, "DOIFtoolsCounterReset", $pn, 0); + return undef; +} +################################# +sub DOIFtools_Define($$$) +{ + my ($hash, $def) = @_; + my ($pn, $type, $cmd) = split(/[\s]+/, $def, 3); + my @Liste = devspec2array("TYPE=DOIFtools"); + if (@Liste > 1) { + CommandDelete(undef,$pn); + CommandSave(undef,undef); + return "Only one instance of DOIFtools is allowed per FHEM installation. Delete the old one first."; + } + $hash->{STATE} = "initialized"; + $hash->{logfile} = AttrVal("global","logdir","./log/")."$hash->{TYPE}Log-%Y-%j.log"; + readingsBeginUpdate($hash); + readingsBulkUpdate($hash,"sourceAttribute","readingList") unless ReadingsVal($pn,"sourceAttribute",""); + readingsBulkUpdate($hash,"doStatistics","disabled") unless ReadingsVal($pn,"doStatistics",""); + readingsBulkUpdate($hash,".eM", ReadingsVal($pn,".eM","off")); + readingsEndUpdate($hash, 0); + DOIFtoolsCounterReset($pn); + return undef; +} + +sub DOIFtools_Attr(@) +{ + my @a = @_; + my $cmd = $a[0]; + my $pn = $a[1]; + my $attr = $a[2]; + my $value = (defined $a[3]) ? $a[3] : ""; + my $hash = $defs{$pn}; + my $ret=""; + if ($attr eq "DOIFtoolsEventMonitorInDOIF") { + if (!defined $modules{DOIF}->{FW_detailFn} and $cmd eq "set" and $value) { + $modules{DOIF}->{FW_detailFn} = "DOIFtools_eM"; + readingsSingleUpdate($hash,".DOIFdO",$modules{DOIF}->{FW_deviceOverview},0); + $modules{DOIF}->{FW_deviceOverview} = "DOIFtools_dO"; + } elsif ($modules{DOIF}->{FW_detailFn} eq "DOIFtools_eM" and ($cmd eq "del" or !$value)) { + delete $modules{DOIF}->{FW_detailFn}; + $modules{DOIF}->{FW_deviceOverview} = ReadingsVal($pn,"DOIFtools_dO",""); + } + } elsif ($attr eq "DOIFtoolsMenuEntry") { + if ($cmd eq "set" and $value) { + if (!(AttrVal($FW_wname, "menuEntries","") =~ m/(DOIFtools\,\/fhem\?detail\=DOIFtools\,)/)) { + CommandAttr(undef, "$FW_wname menuEntries DOIFtools,/fhem?detail=DOIFtools,".AttrVal($FW_wname, "menuEntries","")); + CommandSave(undef, undef); + } + } elsif ($cmd eq "del" or !$value) { + if (AttrVal($FW_wname, "menuEntries","") =~ m/(DOIFtools\,\/fhem\?detail\=DOIFtools\,)/) { + my $me = AttrVal($FW_wname, "menuEntries",""); + $me =~ s/DOIFtools\,\/fhem\?detail\=DOIFtools\,//; + CommandAttr(undef, "$FW_wname menuEntries $me"); + CommandSave(undef, undef); + } + + } + } + return undef; +} + +sub DOIFtools_Undef +{ + my ($hash, $pn) = @_; + $hash->{DELETED} = 1; + if (devspec2array("TYPE=DOIFtools") <=1 and defined($modules{DOIF}->{FW_detailFn}) and $modules{DOIF}->{FW_detailFn} eq "DOIFtools_eM") { + delete $modules{DOIF}->{FW_detailFn}; + $modules{DOIF}->{FW_deviceOverview} = ReadingsVal($pn,"DOIFtools_dO",""); + } + RemoveInternalTimer($pn,"DOIFtoolsCounterReset"); + return undef; +} + +sub DOIFtools_Set($@) +{ + my ($hash, @a) = @_; + my $pn = $hash->{NAME}; + my $arg = $a[1]; + my $value = (defined $a[2]) ? $a[2] : ""; + my $ret = ""; + my @ret = (); + my @doifList = devspec2array("TYPE=DOIF"); + my @ntL =(); + my $dL = join(",",sort @doifList); + + my %types = (); + foreach my $d (keys %defs ) { + next if(IsIgnored($d)); + my $t = $defs{$d}{TYPE}; + $types{$t} = ""; + } + my $tL = join(",",sort keys %types); + + if ($arg eq "sourceAttribute") { + readingsSingleUpdate($hash,"sourceAttribute",$value,0); + return $ret; + } elsif ($arg eq "targetDOIF") { + readingsSingleUpdate($hash,"targetDOIF",$value,0); + } elsif ($arg eq "deleteReadingsInTargetDOIF") { + if ($value) { + my @i = split(",",$value); + foreach my $i (@i) { + $ret = CommandDeleteReading(undef,ReadingsVal($pn,"targetDOIF","")." $i"); + push @ret, $ret if($ret); + } + $ret = join("\n", @ret); + Log3 $pn, 3, $ret if($ret); + return $ret; + } else { + return "no reading selected."; + } + } elsif ($arg eq "doStatistics") { + if ($value eq "deleted") { + DOIFtoolsSetNotifyDev($hash,1,0); + readingsBeginUpdate($hash); + readingsBulkUpdate($hash,"doStatistics","disabled"); + readingsBulkUpdate($hash,"statisticHours","0.00"); + readingsBulkUpdate($hash,".t0",gettimeofday()); + readingsBulkUpdate($hash,".te",0); + readingsEndUpdate($hash,0); + foreach my $key (keys %{$defs{$pn}->{READINGS}}) { + delete $defs{$pn}->{READINGS}{$key} if ($key =~ "^stat_"); + } + } elsif ($value eq "disabled") { + readingsSingleUpdate($hash,"doStatistics","disabled",0); + DOIFtoolsSetNotifyDev($hash,1,0); + } elsif ($value eq "enabled") { + readingsBeginUpdate($hash); + readingsBulkUpdate($hash,"doStatistics","enabled"); + readingsBulkUpdate($hash,".t0",gettimeofday()); + readingsEndUpdate($hash,0); + DOIFtoolsSetNotifyDev($hash,1,1); + } + } elsif ($arg eq "statisticsTYPEs") { + $value =~ s/\,/|/g; + readingsBeginUpdate($hash); + readingsBulkUpdate($hash,"statisticsTYPEs",$value); + readingsBulkUpdate($hash,"doStatistics","disabled"); + readingsBulkUpdate($hash,".te",0); + readingsBulkUpdate($hash,".t0",gettimeofday()); + readingsBulkUpdate($hash,"statisticHours","0.00"); + readingsEndUpdate($hash,0); + foreach my $key (keys %{$defs{$pn}->{READINGS}}) { + delete $defs{$pn}->{READINGS}{$key} if ($key =~ "^stat_"); + } + DOIFtoolsSetNotifyDev($hash,1,0); + } elsif ($arg eq "recording_target_duration") { + $value =~ m/(\d+)/; + readingsSingleUpdate($hash,"recording_target_duration",$1 ? $1 : 0,0); + } elsif ($arg eq "specialLog") { + $hash->{helper}{counter}{1} = 0; + $hash->{helper}{counter}{2} = 0; + if ($value) { + readingsSingleUpdate($hash,"specialLog",1,0); + DOIFtoolsSetNotifyDev($hash,1,1); + } else { + readingsSingleUpdate($hash,"specialLog",0,0); + DOIFtoolsSetNotifyDev($hash,0,1); + } + } else { + if (ReadingsVal($pn,"targetDOIF","")) { + my $tn = ReadingsVal($pn,"targetDOIF",""); + my @rL = (); + foreach my $key (keys %{$defs{$tn}->{READINGS}}) { + push @rL, $key if ($key !~ "^(Device|state|error|cmd|e_|timer_|wait_|matched_|last_cmd|mode)"); + } + my $rL = join(",",@rL); + return "unknown argument $arg for $pn, choose one of statisticsTYPEs:multiple-strict,.*,$tL doStatistics:disabled,enabled,deleted sourceAttribute:readingList targetDOIF:$dL deleteReadingsInTargetDOIF:multiple-strict,$rL recording_target_duration:0,1,6,12,24,168 specialLog:0,1 "; + } else { + return "unknown argument $arg for $pn, choose one of statisticsTYPEs:multiple-strict,.*,$tL doStatistics:disabled,enabled,deleted sourceAttribute:readingList targetDOIF:$dL recording_target_duration:0,1,6,12,24,168 specialLog:0,1 "; + } + } +return $ret; +} + +sub DOIFtools_Get($@) +{ + my ($hash, @a) = @_; + my $pn = $hash->{NAME}; + my $arg = $a[1]; + my $value = (defined $a[2]) ? $a[2] : ""; + my $ret=""; + my @ret=(); + my @doifList = devspec2array("TYPE=DOIF"); + my @ntL =(); + my $dL = join(",",sort @doifList); + + foreach my $i (@doifList) { + foreach my $key (keys %{$defs{$i}{READINGS}}) { + if ($key =~ m/^timer_\d\d_c\d\d/ && $defs{$i}{READINGS}{$key}{VAL} =~ m/.*\|[0-8]+/) { + push @ntL, $i; + last; + } + } + } + my $ntL = join(",",@ntL); + + my %types = (); + foreach my $d (keys %defs ) { + next if(IsIgnored($d)); + my $t = $defs{$d}{TYPE}; + $types{$t} = ""; + } + + if ($arg eq "readingsGroup_for") { + foreach my $i (split(",",$value)) { + push @ret, DOIFtoolsRg($hash,$i); + } + $ret .= join("\n",@ret); + $ret = "Definition for a simple readingsGroup prepared for import with \"Raw definition\":\r--->\r$ret\r<---\r\r"; + Log3 $pn, 3, $ret if($ret); + return $ret; + } elsif ($arg eq "DOIF_to_Log") { + my @regex = (); + my $regex = ""; + my $pnLog = "$hash->{TYPE}Log"; + push @regex, $pnLog; + readingsSingleUpdate($hash,"doif_to_log",$value,0); + return unless($value); + + foreach my $i (split(",",$value)) { + push @regex, DOIFtoolsGetAssocDev($hash,$i); + } + @regex = keys %{{ map { $_ => 1 } @regex}}; + $regex = join("|",@regex).":.*"; + if (AttrVal($pn,"DOIFtoolsExecuteDefinition","")) { + push @ret, "Create device $pnLog.\n"; + $ret = CommandDefMod(undef,"$pnLog FileLog ".AttrVal("global","logdir","./log/")."$pnLog-%Y-%j.log $regex"); + push @ret, $ret if($ret); + $ret = CommandAttr(undef,"$pnLog mseclog ".AttrVal($pnLog,"mseclog","1")); + push @ret, $ret if($ret); + $ret = CommandAttr(undef,"$pnLog nrarchive ".AttrVal($pnLog,"nrarchive","3")); + push @ret, $ret if($ret); + $ret = CommandSave(undef,undef) if (AttrVal($pn,"DOIFtoolsExecuteSave","")); + push @ret, $ret if($ret); + $ret = join("\n", @ret); + Log3 $pn, 3, $ret if($ret); + return $ret; + } else { + $ret = "Definition for a FileLog prepared for import with \"Raw definition\":\r--->\r"; + $ret .= "defmod $pnLog FileLog ".AttrVal("global","logdir","./log/")."$pnLog-%Y-%j.log $regex\r"; + $ret .= "attr $pnLog mseclog 1\r<---\r\r"; + return $ret; + } + } elsif ($arg eq "userReading_nextTimer_for") { + foreach my $i (split(",",$value)) { + push @ret, DOIFtoolsNxTimer($hash,$i); + } + $ret .= join("\n",@ret); + Log3 $pn, 3, $ret if($ret); + return $ret; + } elsif ($arg eq "statisticsReport") { + # event statistics + my $evtsum = 0; + my $typsum = 0; + my $allattr = ""; + $ret = "".sprintf("%-17s","TYPE").sprintf("%-25s","Name").sprintf("%-12s","Anzahl").sprintf("%-8s","Rate").sprintf("%-12s","Begrenzung")."\n"; + $ret .= sprintf("%-17s","").sprintf("%-25s","").sprintf("%-12s","Events").sprintf("%-8s","1/h").sprintf("%-12s","event-on...")."\n"; + $ret .= sprintf("-"x76)."\n"; + my $te = ReadingsVal($pn,".te",0)/3600; + my $i = 0; + my $t = 0; + foreach my $typ (sort keys %types) { + $typsum = 0; + $t=0; + foreach my $key (sort keys %{$defs{$pn}->{READINGS}}) { + if ($key =~ m/^stat_(.*)/ and $defs{$1}->{TYPE} eq $typ) { + $evtsum += $hash->{READINGS}{$key}{VAL}; + $typsum += $hash->{READINGS}{$key}{VAL}; + $allattr = " ".join(" ",keys %{$attr{$1}}); + $ret .= sprintf("%-17s",$typ).sprintf("%-25s",$1).sprintf("%-12s",$hash->{READINGS}{$key}{VAL}).sprintf("%-8s",$te ? int($hash->{READINGS}{$key}{VAL}/$te + 0.5) : "").sprintf("%-12s",($allattr =~ " event-on") ? "ja" : "nein")."\n"; + $i++; + $t++; + } + } + if ($t) { + $ret .= sprintf("%52s","="x10).sprintf("%2s"," ").sprintf("="x6)."\n"; + $ret .= sprintf("%42s","Summe: ").sprintf("%-10s",$typsum).sprintf("%2s","∅:").sprintf("%-8s",$te ? int($typsum/$te + 0.5) : "")."\n"; + $ret .= sprintf("%43s","Geräte: ").sprintf("%-10s",$t)."\n"; + $ret .= sprintf("%43s","Events/Gerät: ").sprintf("%-10s",int($typsum/$t + 0.5))."\n"; + $ret .= "
    ".sprintf("-"x71)."
    "; + } + } + $ret .= sprintf("%52s","="x10).sprintf("%2s"," ").sprintf("="x6)."\n"; + $ret .= sprintf("%42s","Summe: ").sprintf("%-10s",$evtsum).sprintf("%2s","∅:").sprintf("%-8s",$te ? int($evtsum/$te + 0.5) : "")."\n"; + $ret .= sprintf("%42s","Dauer: ").sprintf("%d:%02d",int($te),int(($te-int($te))*60+.5))."\n"; + $ret .= sprintf("%43s","Geräte: ").sprintf("%-10s",$i)."\n"; + $ret .= sprintf("%43s","Events/Gerät: ").sprintf("%-10s",int($evtsum/$i + 0.5))."\n\n" if ($i); + fhem("count",1) =~ m/(\d+)/; + $ret .= sprintf("%43s","Geräte total: ").sprintf("%-10s","$1\n\n"); + $ret .= "
    ".sprintf("-"x71)."
    "; + # attibute statistics + $ret .= "".sprintf("%-30s","gesetzte Attribute in DOIF").sprintf("%-12s","Anzahl")."\n"; + $ret .= sprintf("-"x42)."\n"; + my %da = (); + foreach my $di (@doifList) { + foreach my $dia (keys %{$attr{$di}}) { + if ($modules{DOIF}{AttrList} =~ m/(^|\s)$dia(:|\s)/) { + if ($dia =~ "do|selftrigger|checkall") { + $dia = "* $dia ".AttrVal($di,$dia,""); + $da{$dia} = ($da{$dia} ? $da{$dia} : 0) + 1; + } else { + $dia = "* $dia"; + $da{$dia} = ($da{$dia} ? $da{$dia} : 0) + 1; + } + } else { + $da{$dia} = ($da{$dia} ? $da{$dia} : 0) + 1; + } + } + } + foreach $i (sort keys %da) { + $ret .= sprintf("%-28s","$i").sprintf("%-12s","$da{$i}")."\n"; + } + } elsif ($arg eq "checkDOIF") { + my @coll = (); + my $coll = ""; + foreach my $di (@doifList) { + $coll = DOIFtoolsCheckDOIFcoll($hash,$di); + push @coll, $coll if($coll); + } + $ret .= join(" ",@coll); + $ret .= "\n \n" if (@coll); + foreach my $di (@doifList) { + $ret .= DOIFtoolsCheckDOIF($hash,$di); + } + + $ret = $ret ? "Found recommendation for:\n\n$ret" : "No recommendation found."; + return $ret; + + } elsif ($arg eq "runningTimerInDOIF") { + my $erg =""; + foreach my $di (@doifList) { + push @ret, sprintf("%-28s","$di").sprintf("%-40s",ReadingsVal($di,"wait_timer",""))."\n" if (ReadingsVal($di,"wait_timer","no timer") ne "no timer"); + } + $ret .= join("",@ret); + $ret = $ret ? "Found running wait_timer for:\n\n$ret" : "No running wait_timer found."; + return $ret; + + } else { + return "unknown argument $arg for $pn, choose one of checkDOIF:noArg statisticsReport:noArg readingsGroup_for:multiple-strict,$dL DOIF_to_Log:multiple-strict,$dL userReading_nextTimer_for:multiple-strict,$ntL runningTimerInDOIF:noArg "; + } + + return $ret; +} + + +1; + +=pod +=item helper +=item summary tools to support DOIF +=item summary_DE Werkzeuge zur Unterstützung von DOIF +=begin html + + +

    DOIFtools

    +
      +DOIFtools contains tools to support DOIF.
      +
      +
        +
      • create readingsGroup definitions for labeling frontend widgets.
      • +
      • create a debug logfile for some DOIF and quoted devices with optional device listing each state or wait timer update.
      • +
      • optional device listing in debug logfile each state or wait timer update.
      • +
      • navigation between device listings in logfile if opened via DOIFtools.
      • +
      • create userReadings in DOIF devices displaying real dates for weekday restricted timer.
      • +
      • delete user defined readings in DOIF devices with multiple choice.
      • +
      • record statistics data about events.
      • +
      • limitting recordig duration.
      • +
      • generate a statistics report.
      • +
      • lists every DOIF definition in probably associated with.
      • +
      • access to DOIFtools from any DOIF device via probably associated with
      • +
      • access from DOIFtools to existing DOIFtoolsLog logfiles
      • +
      • show event monitor in device overview and optionally DOIF
      • +
      • create shortcuts
      • +
      • optionally create a menu entry
      • +
      +
      +Just one definition per FHEM-installation is necessary. +
      +
    +=end html +=begin html_DE + + +

    DOIFtools

    +
      +DOIFtools stellt Funktionen zur Unterstützung von DOIF-Geräten bereit.
      +
      +
        +
      • erstellen von readingsGroup Definitionen, zur Beschriftung von Frontendelementen.
      • +
      • erstellen eines Debug-Logfiles, in dem mehrere DOIF und zugehörige Geräte geloggt werden.
      • +
      • optionales DOIF-Listing bei jeder Status und Wait-Timer Aktualisierung im Debug-Logfile.
      • +
      • Navigation zwischen den DOIF-Listings im Logfile, wenn es über DOIFtools geöffnet wird.
      • +
      • erstellen von userReadings in DOIF-Geräten zur Anzeige des realen Datums bei Wochentag behafteten Timern.
      • +
      • löschen von benutzerdefinierten Readings in DOIF-Definitionen über eine Mehrfachauswahl.
      • +
      • erfassen statistischer Daten über Events.
      • +
      • Begrenzung der Datenaufzeichnungsdauer.
      • +
      • erstellen eines Statistikreports.
      • +
      • Liste aller DOIF-Definitionen in probably associated with.
      • +
      • Zugriff auf DOIFtools aus jeder DOIF-Definition über die Liste in probably associated with.
      • +
      • Zugriff aus DOIFtools auf vorhandene DOIFtoolsLog-Logdateien.
      • +
      • zeigt den Event Monitor in der Detailansicht von DOIFtools.
      • +
      • ermöglicht den Zugriff auf den Event Monitor in der Detailansicht von DOIF.
      • +
      • erstellen von Shortcuts
      • +
      • optionalen Memüeintrag erstellen
      • +
      +
      + + +Definition
      +
      +
      define <name> DOIFtools
      +
      Es ist nur eine Definition pro FHEM Installation notwendig. Die Definition wird mit den vorhanden DOIF-Namen ergänzt, daher erscheinen alle DOIF-Geräte in der Liste probably associated with. Zusätzlich wird in jedem DOIF-Gerät in dieser Liste auf das DOIFtool verwiesen.
      +
      + + +Set
      +
      +
      set <name> deleteReadingInTargetDOIF <readings to delete name>|targetDOIF <target name>|sourceAttribute <readingList> |statisticsTYPEs <List of TYPE used for statistics generation>|doStatistics <enabled|disabled|deleted>

      +
      deleteReadingInTargetDOIF löscht die benutzerdefinierten Readings im Ziel-DOIF
      +
      + targetDOIF vor dem Löschen der Readings muss das Ziel-DOIF gesetzt werden.
      +
      + sourceAttribute vor dem Erstellen einer ReadingsGroup muss das Attribut gesetzt werden aus dem die Readings gelesen werden, um die ReadingsGroup zu erstellen und zu beschriften. Default, readingsList
      +
      + statisticsTYPEs setzt eine Liste von TYPE für die Statistikdaten erfasst werden, bestehende Statistikdaten werden gelöscht. Default, "".
      +
      + specialLog 1 DOIF-Listing bei Status und Wait-Timer Aktualisierung im Debug-Logfile. Default, 0.
      +
      + doStatistics deleted setzt die Statistik zurück und löscht alle stat_ Readings.
      + disabled pausiert die Statistikdatenerfassung.
      + enabled startet die Statistikdatenerfassung.
      +
      + recording_target_duration gibt an wie lange Daten erfasst werden sollen. Default, 0 die Dauer ist nicht begrenzt.
      +
      +
      +
      + + +Get
      + +
      get <name> DOIF_to_Log <DOIF names for logging> |readingsGroup_for <DOIF names to create readings groups> |userReading_nextTimer_for <DOIF names where to create real date timer readings> |statisticsReport

      +
      + DOIF_to_Log erstellt eine FileLog-Definition, die für alle angegebenen DOIF-Definitionen loggt. Der Reguläre Ausdruck wird aus den, direkt in den DOIF-Greräte angegebenen und den wahrscheinlich verbundenen Geräten, ermittelt.
      +
      + checkDOIF führt eine einfache Syntaxprüfung durch und empfiehlt Änderungen.
      +
      + readingsGroup_for erstellt readingsGroup-Definitionen für die angegebenen DOIF-namen. sourceAttribute verweist auf das Attribut, dessen Readingsliste als Basis verwendet wird. Die Eingabeelemente im Frontend werden mit den Readingsnamen beschriftet.
      +
      + userReading_nextTimer_for erstellt userReadings-Attribute für Timer-Readings mit realem Datum für Timer, die mit Wochentagangaben angegeben sind, davon ausgenommen sind indirekte Wochentagsangaben.
      +
      + statisticsReport erstellt einen Bericht aus der laufenden Datenerfassung.

      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 Events eingeschränkt werden können. Werden keine Events eines Gerätes weiterverarbeitet, kann das Attribut event-on-change-reading auf none oder eine andere Zeichenfolge, die im Gerät nicht als Readingname vorkommt, gesetzt werden.
      +
      + runningTimerInDOIF 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.
      +
      +
      +
      + + +Attribute
      + +
      attr <name> DOIFtoolsExecuteDefinition <0|1> |DOIFtoolsExecuteSave <0|1> |DOIFtoolsTargetRoom <room names for target> |DOIFtoolsTargetGroup <group names for target> |DOIFtoolsEventMonitorInDOIF <1|0> |DOIFtoolsReadingsPrefix <user defined prefix> |DOIFtoolsHideModulShortcuts <0|1> |DOIFtoolsMyShortcuts <shortcut name>,<command>, ... |DOIFtoolsMenuEntry <0|1>

      +
      DOIFtoolsExecuteDefinition 1 führt die erzeugten Definitionen aus. Default 0, zeigt die erzeugten Definitionen an, sie können mit Raw definition importiert werden.
      +
      + DOIFtoolsExecuteSave 1, die Definitionen werden automatisch gespeichert. Default 0, der Benutzer kann die Definitionen speichern.
      +
      + DOIFtoolsTargetGroup gibt die Gruppen für die zu erstellenden Definitionen an. Default, die Gruppe der Ursprungs Definition.
      +
      + DOIFtoolsTargetRoom gibt die Räume für die zu erstellenden Definitionen an. Default, der Raum der Ursprungs Definition.
      +
      + DOIFtoolsReadingsPrefix legt den Präfix der benutzerdefinierten Readingsnamen für die Zieldefinition fest. Default, DOIFtools bestimmt den Präfix.
      +
      + DOIFtoolsEventMonitorInDOIF 1, die Anzeige des Event-Monitors wird in DOIF ermöglicht. Default 0, kein Zugriff auf den Event-Monitor im DOIF.
      +
      + DOIFtoolsHideModulShortcuts 1, verstecken der DOIFtools Shortcuts. Default 0.
      +
      + DOIFtoolsMyShortcuts <Bezeichnung>,<Befehl>,... anzeigen eigener Shortcuts, siehe globales Attribut menuEntries.
      + Zusätzlich gilt, wenn ein Eintrag mit ## beginnt und mit ,, endet, wird er als HTML interpretiert.
      + Beispiel: attr DOIFtools DOIFtoolsMyShortcuts ##
      My Shortcuts:,,list DOIFtools,fhem?cmd=list DOIFtools

      +
      + DOIFtoolsMenuEntry 1, erzeugt einen Menüeintrag im FHEM-Menü. Default 0.
      +
      + disabledForIntervals pausiert die Statistikdatenerfassung.
      +
      +
      + +Readings
      +
      +
        + DOIFtools erzeugt bei der Aktualisierung von Readings keine Events, daher muss die Seite im Browser aktualisiert werden, um aktuelle Werte zu sehen.
        +
        +
      • DOIF_version zeigt die Version des DOIF an.
      • +
      • FHEM_revision zeigt die Revision von FHEM an.
      • +
      • doStatistics zeigt den Status der Statistikerzeugung an
      • +
      • logfile gibt den Pfad und den Dateinamen mit Ersetzungszeichen an.
      • +
      • recording_target_duration gibt an wie lange Daten erfasst werden sollen.
      • +
      • stat_<devicename> zeigt die Anzahl der gezählten Ereignisse, die das jeweilige Gerät erzeugt hat.
      • +
      • statisticHours zeigt die kumulierte Zeit für den Status enabled an, während der, Statistikdaten erfasst werden.
      • +
      • statisticsTYPEs zeigt eine Liste von TYPE an, für deren Geräte die Statistik erzeugt wird.
      • +
      • specialLog zeigt an ob DOIF-Listing im Log eingeschaltet ist.
      • +
      +
      + +Links
      +
      + +
      +
    +=end html_DE +=cut