############################################# # $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($); sub DOIFtoolsDeleteStatReadings; my @DOIFtools_we = [0,0,0,0,0,0,0,0]; my $DOIFtoolsJSfuncEM = <<'EOF'; EOF my $DOIFtoolsJSfuncStart = <<'EOF'; EOF ######################### 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 DOIFtoolsHideGetSet:1,0 DOIFtoolsMyShortcuts:textField-long DOIFtoolsMenuEntry:1,0 DOIFtoolsHideStatReadings:1,0 DOIFtoolsEventOnDeleted:1,0 DOIFtoolsEMbeforeReadings:1,0 DOIFtoolsNoLookUp:1,0 DOIFtoolsNoLookUpInDOIF:1,0 DOIFtoolsLogDir 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 @dtn = devspec2array("TYPE=DOIFtools"); my $ret = ""; # call DOIF_detailFn no strict "refs"; $ret .= &{ReadingsVal($dtn[0],".DOIF_detailFn","")}($FW_wname, $d, $room, $pageHash) if (ReadingsVal($dtn[0],".DOIF_detailFn","")); use strict "refs"; if (!$room) { # LookUp in probably associated with $ret .= $DOIFtoolsJSfuncStart if (!AttrVal($dtn[0],"DOIFtoolsNoLookUpInDOIF","")); # Event Monitor if (AttrVal($dtn[0],"DOIFtoolsEventMonitorInDOIF","")) { my $a0 = ReadingsVal($d,".eM", "off") eq "on" ? "off" : "on"; my $lang = AttrVal("global","language","EN"); $ret .= "
" if (ReadingsVal($dtn[0],".DOIF_detailFn","")); $ret .= "
Event monitor: toggle  "; $ret .= "
"; my $a = ""; if (ReadingsVal($d,".eM","off") eq "on") { $ret .= ""; my $filter = $a ? ($a eq "log" ? "global" : $a) : ".*"; $ret .= "
"; my $embefore = AttrVal($dtn[0],"DOIFtoolsEMbeforeReadings","0") ? "1" : ""; $ret .= "

"; $ret .= "Events (Filter: $filter) ". "  FHEM log ". "". "  ".($lang eq "DE" ? " Hinweis: Eventzeile markieren, Operanden auswählen, Definition ergänzen" : " Hint: select event line, choose operand, modify definition")."
\n"; $ret .= ""; $ret .= "
"; $ret .= $DOIFtoolsJSfuncEM; } } } return $ret ? $ret : undef; } ###################### # 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 top listing
"; my $suffix = "
jump to: the top top 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 = ""; # $ret .= ""; $ret .= $DOIFtoolsJSfuncStart if (!AttrVal($d,"DOIFtoolsNoLookUp","")); # 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 my $a0 = ReadingsVal($d,".eM", "off") eq "on" ? "off" : "on"; $ret .= "
"; $ret .= ""; if (AttrVal($d,"DOIFtoolsMyShortcuts","")) { $ret .= ""; } $ret .= "
Event monitor: toggle  "; if (!AttrVal($d,"DOIFtoolsHideModulShortcuts",0)) { $ret .= "Shortcuts: "; $ret .= "reload DOIFtools  " if(ReadingsVal($d,".debug","")); $ret .= "update check  "; $ret .= "update  "; $ret .= "shutdown restart  "; $ret .= "fheminfo send  "; } $ret .= "
"; 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 .= "
"; if (!AttrVal($d, "DOIFtoolsHideGetSet", 0)) { my $a1 = ReadingsVal($d,"doStatistics", "disabled") =~ "disabled|deleted" ? "enabled" : "disabled"; my $a2 = ReadingsVal($d,"specialLog", 0) ? 0 : 1; $ret .= ""; # set doStatistics enabled/disabled $ret .= ""; # set doStatistics deleted $ret .= ""; # set specialLog 0/1 $ret .= ""; $ret .= ""; # get statisticsReport $ret .= ""; # get checkDOIF $ret .= ""; # get runningTimerInDOIF $ret .= ""; $ret .= "
"; $ret .= FW_hidden("fwcsrf", $defs{$FW_wname}{CSRFTOKEN}) if($FW_CSRF); $ret .= "
 doStatistics $a1 
"; $ret .= FW_hidden("fwcsrf", $defs{$FW_wname}{CSRFTOKEN}) if($FW_CSRF); $ret .= "
 doStatistics deleted 
"; $ret .= FW_hidden("fwcsrf", $defs{$FW_wname}{CSRFTOKEN}) if($FW_CSRF); $ret .= "
 specialLog $a2 
 statisticsReport 
 checkDOIF 
 runningTimerInDOIF 
"; } $ret .= "
"; my $a = ""; if (ReadingsVal($d,".eM","off") eq "on") { my $lang = AttrVal("global","language","EN"); $ret .= ""; # $ret .= ""; my $filter = $a ? ($a eq "log" ? "global" : $a) : ".*"; $ret .= "
"; $ret .= "Events (Filter: $filter) ". "  FHEM log ". "". "  ".($lang eq "DE" ? " Hinweis: Eventzeile markieren, Operanden auswählen, neue Definition erzeugen" : " Hint: select event line, choose operand, create definition")."
\n"; my $embefore = AttrVal($d,"DOIFtoolsEMbeforeReadings","0") ? "1" : ""; $ret .= "
"; $ret .= ""; $ret .= "
"; $ret .= $DOIFtoolsJSfuncEM; } 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,"$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: prev  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 =~ "^INITIALIZED\$|^MODIFIED|^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, FHEM revision and default values 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)); readingsBulkUpdate($hash,"sourceAttribute","readingList") unless ReadingsVal($pn,"sourceAttribute",""); readingsBulkUpdate($hash,"recording_target_duration",0) unless ReadingsVal($pn,"recording_target_duration","0"); readingsBulkUpdate($hash,"doStatistics","disabled") unless ReadingsVal($pn,"doStatistics",""); readingsBulkUpdate($hash,".eM", ReadingsVal($pn,".eM","off")); readingsBulkUpdate($hash,"statisticsDeviceFilterRegex", ".*") unless ReadingsVal($pn,"statisticsDeviceFilterRegex",""); 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 FW_detailFn if ($modules{DOIF}{LOADED} and (!$modules{DOIF}->{FW_detailFn} or $modules{DOIF}->{FW_detailFn} and $modules{DOIF}->{FW_detailFn} ne "DOIFtools_eM") and $sn eq "global" and $event =~ "^INITIALIZED\$" ) { readingsBeginUpdate($hash); readingsBulkUpdate($hash,".DOIF_detailFn",$modules{DOIF}->{FW_detailFn}); $modules{DOIF}->{FW_detailFn} = "DOIFtools_eM"; readingsBulkUpdate($hash,".DOIFdO",$modules{DOIF}->{FW_deviceOverview}); $modules{DOIF}->{FW_deviceOverview} = 1; readingsEndUpdate($hash,0); } # 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) or !ReadingsVal($pn,"recording_target_duration",0))) { my $st = AttrVal($pn,"DOIFtoolsHideStatReadings","") ? ".stat_" : "stat_"; readingsSingleUpdate($hash,"$st$sn",ReadingsVal($pn,"$st$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,"recording_target_duration",0) or ReadingsVal($pn,"statisticHours",0) <= ReadingsVal($pn,"recording_target_duration",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); readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Action","event recording target duration reached"); readingsBulkUpdate($hash,"doStatistics","disabled"); readingsEndUpdate($hash,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,$tn) = @_; $timer_str =~ /(\d\d).(\d\d).(\d\d\d\d) (\d\d):(\d\d):(\d\d)\|?(.*)/; my $tstr = "$1.$2.$3 $4:$5:$6"; return $tstr if (length($7) == 0); my $timer = timelocal($6,$5,$4,$1,$2-1,$3); my $tdays = ""; $tdays = $tn ? DOIF_weekdays($defs{$tn},$7) : $7; $tdays =~/([0-8])/; return $tstr if (length($1) == 0); 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++; } return "no timer next 7 days"; } 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/\d\d.\d\d.\d\d\d\d \d\d:\d\d:\d\d\|.*/) { $ret = AttrVal($pn,"DOIFtoolsReadingsPrefix","N_")."$key:$key.* \{DOIFtoolsNextTimer(ReadingsVal(\"$tn\",\"$key\",\"none\"),\"$tn\")\}"; 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 =~ /^ *$/); my $DE = AttrVal("global", "language", "") eq "DE" ? 1 : 0; if ($DE) { $ret .= "
  • ersetze DOIF name durch \$SELF (Auswertung von Events)
  • \n" if ($tail =~ m/[\[|\?]($tn)/); $ret .= "
  • ersetze ReadingsVal(...) durch [name:reading,default value], wenn es nicht in einem IF-Befehl verwendet wird, dort ist es nicht anders möglich einen Default-Wert anzugeben. (Steuerung durch Events)
  • \n" if ($tail =~ m/(ReadingsVal)/); $ret .= "
  • ersetze ReadingsNum(...) durch [name:reading:d,default value], wenn es nicht in einem IF-Befehl verwendet wird, dort ist es nicht anders möglich einen Default-Wert anzugeben. (Filtern nach Zahlen)
  • \n" if ($tail =~ m/(ReadingsNum)/); $ret .= "
  • ersetze InternalVal(...) durch [name:&internal,default value], wenn es nicht in einem IF-Befehl verwendet wird, dort ist es nicht anders möglich einen Default-Wert anzugeben. (Steuerung durch Events)
  • \n" if ($tail =~ m/(InternalVal)/); $ret .= "
  • ersetze $1...\")} durch $2... (FHEM-Befehl)
  • \n" if ($tail =~ m/(\{\s*fhem.*?\"\s*(set|get))/); $ret .= "
  • ersetze {system \"<SHELL-Befehl>\"} durch \"\<SHELL-Befehl>\" (FHEM SHELL-Befehl, nicht blockierend)
  • \n" if ($tail =~ m/(\{\s*system.*?\})/); $ret .= "
  • sleep im DOIF zu nutzen, wird nicht empfohlen, nutze das Attribut wait für (Verzögerungen)
  • \n" if ($tail =~ m/(sleep\s\d+\.?\d+\s*[;|,]?)/); $ret .= "
  • ersetze [name:?regex] durch [name:\"regex\"] (Vermeidung veralteter Syntax)
  • \n" if ($tail =~ m/(\[.*?[^"]?:[^"]?\?.*?\])/); $ret .= "
  • der erste Befehl nach DOELSE scheint eine Bedingung zu sein, weil $2 enthalten ist, bitte prüfen.
  • \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]); } if (defined $defs{$tn}{timeCond}) { foreach my $key (sort keys %{$defs{$tn}{timeCond}}) { if (defined($defs{$tn}{timeCond}{$key}) and $defs{$tn}{timeCond}{$key} and $sub0[$defs{$tn}{timeCond}{$key}]) { $ret .= "
  • Timer in der Bedingung and Wait-Timer für Befehle im selben DOIF-Zweig.
    Wenn ein unerwartetes Verhalten beobachtet wird, nutze das Attribut timerWithWait (Verzögerung von Timern)
  • \n"; last; } } } } my $wait = AttrVal($tn,"wait",""); if ($wait) { $ret .= "
  • Mindestens ein indirekter Timer im Attribut wait bezieht sich auf den DOIF-Namen ( $tn ) und hat keinen Default-Wert, er sollte angegeben werden.. (Default-Wert)
  • \n" if($wait =~ m/(\[(\$SELF|$tn).*?(\,.*?)?\])/ and $2 and !$3); } if (defined $defs{$tn}{time}) { foreach my $key (sort keys %{$defs{$tn}{time}}) { if (defined $defs{$tn}{time}{$key} and $defs{$tn}{time}{$key} =~ m/(\[(\$SELF|$tn).*?(\,.*?)?\])/ and $2 and !$3) { $ret .= "
  • Mindestens ein indirekter Timer in einer Bedingung bezieht sich auf den DOIF-Namen ( $tn ) und hat keinen Default-Wert, er sollte angegeben werden. (Default-Wert)
  • \n"; last; } } } if (defined $defs{$tn}{devices}{all}) { @tmp = (); my $devi = $defs{$tn}{devices}{all}; $devi =~ s/^ | $//g; my @devi = split(/ /,$defs{$tn}{devices}{all}); foreach my $key (@devi) { push @tmp, $key if (defined $defs{$key} and $defs{$key}{TYPE} eq "dummy"); } if (@tmp) { @tmp = keys %{{ map { $_ => 1 } @tmp}}; my $tmp = join(" ",sort @tmp); $ret .= "
  • Dummy-Geräte ( $tmp ) in der Bedingung von DOIF $tn können durch benutzerdefinierte Readings des DOIF ersetzt werden, wenn sie als Frontend-Elemente genutzt werden. (readingList, setList, webCmd)
  • \n"; } } if (defined $defs{$tn}{do}) { @tmp = (); foreach my $key (keys %{$defs{$tn}{do}}) { foreach my $subkey (keys %{$defs{$tn}{do}{$key}}) { push @tmp, $1 if ($defs{$tn}{do}{$key}{$subkey} =~ m/set (.*?) / and defined $defs{$1} and $defs{$1}{TYPE} eq "dummy"); } } if (@tmp) { @tmp = keys %{{ map { $_ => 1 } @tmp}}; my $tmp = join(" ",sort @tmp); $ret .= "
  • Statt Dummys ( $tmp ) zu setzen, könnte ggf. der Status des DOIF $tn zur Anzeige im Frontend genutzt werden. (DOIF-Status ersetzen)
  • \n"; } } } else { $ret .= "
  • replace DOIF name with \$SELF (utilization of events)
  • \n" if ($tail =~ m/[\[|\?]($tn)/); $ret .= "
  • replace ReadingsVal(...) with [name:reading,default value], if not used in an IF command, otherwise there is no possibility to use a default value (controlling by events)
  • \n" if ($tail =~ m/(ReadingsVal)/); $ret .= "
  • replace ReadingsNum(...) with [name:reading:d,default value], if not used in an IF command, otherwise there is no possibility to use a default value (filtering numbers)
  • \n" if ($tail =~ m/(ReadingsNum)/); $ret .= "
  • replace InternalVal(...) with [name:&internal,default value], if not used in an IF command, otherwise there is no possibility to use a default value (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]); } if (defined $defs{$tn}{timeCond}) { foreach my $key (sort keys %{$defs{$tn}{timeCond}}) { if (defined($defs{$tn}{timeCond}{$key}) and $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; } } } } my $wait = AttrVal($tn,"wait",""); if ($wait) { $ret .= "
  • At least one indirect timer in attribute wait is referring DOIF's name ( $tn ) and has no default value, you should add default values. (default value)
  • \n" if($wait =~ m/(\[(\$SELF|$tn).*?(\,.*?)?\])/ and $2 and !$3); } if (defined $defs{$tn}{time}) { foreach my $key (sort keys %{$defs{$tn}{time}}) { if (defined $defs{$tn}{time}{$key} and $defs{$tn}{time}{$key} =~ m/(\[(\$SELF|$tn).*?(\,.*?)?\])/ and $2 and !$3) { $ret .= "
  • At least one indirect timer in condition is referring DOIF's name ( $tn ) and has no default value, you should add default values. (default value)
  • \n"; last; } } } if (defined $defs{$tn}{devices}{all}) { @tmp = (); my $devi = $defs{$tn}{devices}{all}; $devi =~ s/^ | $//g; my @devi = split(/ /,$defs{$tn}{devices}{all}); foreach my $key (@devi) { push @tmp, $key if (defined $defs{$key} and $defs{$key}{TYPE} eq "dummy"); } if (@tmp) { my $tmp = join(" ",sort @tmp); $ret .= "
  • dummy devices in DOIF $tn condition could replaced by user defined readings in DOIF, if they are used as frontend elements. (readingList, setList, webCmd)
  • \n"; } } if (defined $defs{$tn}{do}) { @tmp = (); foreach my $key (keys %{$defs{$tn}{do}}) { foreach my $subkey (keys %{$defs{$tn}{do}{$key}}) { push @tmp, $1 if ($defs{$tn}{do}{$key}{$subkey} =~ m/set (.*?) / and defined $defs{$1} and $defs{$1}{TYPE} eq "dummy"); } } if (@tmp) { my $tmp = join(" ",sort @tmp); $ret .= "
  • The state of DOIF $tn could be eventually used as display element in frontend, instead of setting a dummy device ( $tmp ). (replace DOIF state)
  • \n"; } } } $ret = $ret ? "$tn\n " : ""; 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 DOIFtoolsDeleteStatReadings { my ($hash, @a) = @_; my $pn = $hash->{NAME}; my $st = AttrVal($pn,"DOIFtoolsHideStatReadings","") ? ".stat_" : "stat_"; readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Action","event recording stopped and data deleted"); readingsBulkUpdate($hash,"doStatistics","disabled"); readingsBulkUpdate($hash,"statisticHours","0.00"); readingsBulkUpdate($hash,".t0",gettimeofday()); readingsBulkUpdate($hash,".te",0); readingsEndUpdate($hash,0); if (AttrVal($pn,"DOIFtoolsEventOnDeleted","")){ readingsBeginUpdate($hash); foreach my $key (keys %{$hash->{READINGS}}) { readingsBulkUpdate($hash,"stat_$1",ReadingsVal($pn,"$key",0)) if ($key =~ m/^$st(.*)/); } readingsEndUpdate($hash,1); } foreach my $key (keys %{$hash->{READINGS}}) { delete $hash->{READINGS}{$key} if ($key =~ "^(stat_|\.stat_)"); } } ################################# 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($pn,"DOIFtoolsLogDir",AttrVal("global","logdir","./log/"))."$hash->{TYPE}Log-%Y-%j.log"; 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 ($init_done and $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} = 1; # } 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,".DOIFdO",""); # } } elsif ($init_done and $attr eq "DOIFtoolsMenuEntry") { if ($cmd eq "set" and $value) { if (!(AttrVal($FW_wname, "menuEntries","") =~ m/(DOIFtools\,$FW_ME\?detail\=DOIFtools\,)/)) { CommandAttr(undef, "$FW_wname menuEntries DOIFtools,$FW_ME?detail=DOIFtools,".AttrVal($FW_wname, "menuEntries","")); CommandSave(undef, undef); } } elsif ($init_done and $cmd eq "del" or !$value) { if (AttrVal($FW_wname, "menuEntries","") =~ m/(DOIFtools\,$FW_ME\?detail\=DOIFtools\,)/) { my $me = AttrVal($FW_wname, "menuEntries",""); $me =~ s/DOIFtools\,$FW_ME\?detail\=DOIFtools\,//; CommandAttr(undef, "$FW_wname menuEntries $me"); CommandSave(undef, undef); } } } elsif ($init_done and $attr eq "DOIFtoolsLogDir") { if ($cmd eq "set") { if ($value and -d $value) { $value =~ m,^(.*)/$,; return "Path \"$value\" needs a final slash." if (!$1); $hash->{logfile} = "$value$hash->{TYPE}Log-%Y-%j.log"; } else { return "\"$value\" is not a valid directory"; } } elsif ($cmd eq "del" or !$value) { $hash->{logfile} = AttrVal("global","logdir","./log/")."$hash->{TYPE}Log-%Y-%j.log"; } } elsif ($init_done and $attr eq "DOIFtoolsHideStatReadings") { DOIFtoolsSetNotifyDev($hash,1,0); DOIFtoolsDeleteStatReadings($hash); } elsif ($init_done and $cmd eq "set" and $attr =~ m/^(executeDefinition|executeSave|target_room|target_group|readingsPrefix|eventMonitorInDOIF)$/) { $ret .= "\n$1 is an old attribute name use a new one beginning with DOIFtools..."; return $ret; } 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") { $modules{DOIF}->{FW_detailFn} = ReadingsVal($pn,".DOIF_detailFn",""); $modules{DOIF}->{FW_deviceOverview} = ReadingsVal($pn,".DOIFdO",""); } if (AttrVal($pn,"DOIFtoolsMenuEntry","")) { CommandDeleteAttr(undef, "$pn DOIFtoolsMenuEntry"); } 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 @deviList = devspec2array("TYPE!=DOIF"); my @ntL =(); my $dL = join(",",sort @doifList); my $deL = join(",",sort @deviList); my $st = AttrVal($pn,"DOIFtoolsHideStatReadings","") ? ".stat_" : "stat_"; 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); readingsSingleUpdate($hash,"targetDOIF","",0); return $ret; } else { readingsSingleUpdate($hash,"targetDOIF","",0); return "no reading selected."; } } elsif ($arg eq "targetDevice") { readingsSingleUpdate($hash,"targetDevice",$value,0); } elsif ($arg eq "deleteReadingsInTargetDevice") { if ($value) { my @i = split(",",$value); foreach my $i (@i) { $ret = CommandDeleteReading(undef,ReadingsVal($pn,"targetDevice","")." $i"); push @ret, $ret if($ret); } $ret = join("\n", @ret); readingsSingleUpdate($hash,"targetDevice","",0); return $ret; } else { readingsSingleUpdate($hash,"targetDevice","",0); return "no reading selected."; } } elsif ($arg eq "doStatistics") { if ($value eq "deleted") { DOIFtoolsSetNotifyDev($hash,1,0); DOIFtoolsDeleteStatReadings($hash); } elsif ($value eq "disabled") { readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Action","event recording paused"); readingsBulkUpdate($hash,"doStatistics","disabled"); readingsEndUpdate($hash,0); DOIFtoolsSetNotifyDev($hash,1,0); } elsif ($value eq "enabled") { readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Action","
    recording events
    "); 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); readingsEndUpdate($hash,0); DOIFtoolsDeleteStatReadings($hash); 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 "statisticsShowRate_ge") { $value =~ m/(\d+)/; readingsSingleUpdate($hash,"statisticsShowRate_ge",$1 ? $1 : 0,0); } elsif ($arg eq "specialLog") { if ($value) { readingsSingleUpdate($hash,"specialLog",1,0); DOIFtoolsSetNotifyDev($hash,1,1); } else { readingsSingleUpdate($hash,"specialLog",0,0); DOIFtoolsSetNotifyDev($hash,0,1); } } elsif ($arg eq "statisticsDeviceFilterRegex") { $ret = "Bad regexp: starting with *" if($value =~ m/^\*/); eval { "Hallo" =~ m/^$value$/ }; $ret .= "\nBad regexp: $@" if($@); if ($ret or !$value) { readingsSingleUpdate($hash,"statisticsDeviceFilterRegex", ".*",0); return "$ret\nRegexp is set to: .*"; } else { readingsSingleUpdate($hash,"statisticsDeviceFilterRegex", $value,0); } } else { my $hardcoded = "doStatistics:disabled,enabled,deleted specialLog:0,1"; 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 :""); 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|\.eM)"); } $retL .= " deleteReadingsInTargetDOIF:multiple-strict,".join(",", sort @rL); } if (ReadingsVal($pn,"targetDevice","")) { my $tn = ReadingsVal($pn,"targetDevice",""); my @rL = (); my $rx = ReadingsVal($pn,".debug","") ? "^(state)" : "^(state|[.])"; foreach my $key (keys %{$defs{$tn}->{READINGS}}) { push @rL, $key if ($key !~ $rx); } $retL .= " deleteReadingsInTargetDevice:multiple-strict,".join(",", sort @rL); } return $retL; } 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); my $DE = AttrVal("global", "language", "") eq "DE" ? 1 : 0; 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/\d\d.\d\d.\d\d\d\d \d\d:\d\d:\d\d\|.*/) { 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"; $ret = "Die Definition einer einfachen readingsGroup ist für den Import mit \"Raw definition\" vorbereitet:\r--->\r$ret\r<---\r\r" if ($DE); 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); readingsSingleUpdate($hash,"specialLog",0,0) if (!$value); DOIFtoolsSetNotifyDev($hash,0,1); # 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 ".InternalVal($pn,"logfile","./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 = CommandAttr(undef,"$pnLog disable ".($value ? "0" : "1")); 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 = "Die FileLog-Definition ist zum Import mit \"Raw definition\"vorbereitet:\r--->\r" if ($DE); $ret .= "defmod $pnLog FileLog ".InternalVal($pn,"logfile","./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 $regex = ReadingsVal($pn,"statisticsDeviceFilterRegex",".*"); my $evtsum = 0; my $evtlen = 15 + 2; my $rate = 0; my $typsum = 0; my $typlen = 10 + 2; my $typerate = 0; my $allattr = ""; my $rx = AttrVal($pn,"DOIFtoolsHideStatReadings","") ? "\.stat_" : "stat_"; my $te = ReadingsVal($pn,".te",0)/3600; my $compRate = ReadingsNum($pn,"statisticsShowRate_ge",0); foreach my $typ ( keys %types) { $typlen = length($typ)+2 > $typlen ? length($typ)+2 : $typlen; } foreach my $key (sort keys %{$defs{$pn}->{READINGS}}) { $rate = ($te ? int($hash->{READINGS}{$key}{VAL}/$te + 0.5) : 0) if ($key =~ m/^$rx($regex)/); if ($key =~ m/^$rx($regex)/ and $rate >= $compRate) { $evtlen = length($1)+2 > $evtlen ? length($1)+2 : $evtlen; } } $ret = "".sprintf("%-".$typlen."s","TYPE").sprintf("%-".$evtlen."s","NAME").sprintf("%-12s","Number").sprintf("%-8s","Rate").sprintf("%-12s","Restriction")."\n"; $ret = "".sprintf("%-".$typlen."s","TYPE").sprintf("%-".$evtlen."s","NAME").sprintf("%-12s","Anzahl").sprintf("%-8s","Rate").sprintf("%-12s","Begrenzung")."\n" if ($DE); $ret .= sprintf("%-".$typlen."s","").sprintf("%-".$evtlen."s","").sprintf("%-12s","Events").sprintf("%-8s","1/h").sprintf("%-12s","event-on...")."\n"; $ret .= sprintf("-"x($typlen+$evtlen+33))."\n"; my $i = 0; my $t = 0; foreach my $typ (sort keys %types) { $typsum = 0; $t=0; foreach my $key (sort keys %{$defs{$pn}->{READINGS}}) { $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); if ($key =~ m/^$rx($regex)/ and defined($defs{$1}) and $defs{$1}->{TYPE} eq $typ and $rate >= $compRate) { $evtsum += $hash->{READINGS}{$key}{VAL}; $typsum += $hash->{READINGS}{$key}{VAL}; $allattr = " ".join(" ",keys %{$attr{$1}}); $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"; $i++; $t++; } } if ($t) { $typerate = $te ? int($typsum/$te + 0.5) : 0; if($typerate >= $compRate) { $ret .= sprintf("%".($typlen+$evtlen+10)."s","="x10).sprintf("%2s"," ").sprintf("="x6)."\n"; if ($DE) { $ret .= sprintf("%".($typlen+$evtlen)."s","Summe: ").sprintf("%-10s",$typsum).sprintf("%2s","∅:").sprintf("%-8s",$typerate)."\n"; $ret .= sprintf("%".($typlen+$evtlen+1)."s","Geräte: ").sprintf("%-10s",$t)."\n"; $ret .= sprintf("%".($typlen+$evtlen+1)."s","Events/Gerät: ").sprintf("%-10s",int($typsum/$t + 0.5))."\n"; } else { $ret .= sprintf("%".($typlen+$evtlen)."s","Total: ").sprintf("%-10s",$typsum).sprintf("%2s","∅:").sprintf("%-8s",$typerate)."\n"; $ret .= sprintf("%".($typlen+$evtlen)."s","Devices: ").sprintf("%-10s",$t)."\n"; $ret .= sprintf("%".($typlen+$evtlen)."s","Events/device: ").sprintf("%-10s",int($typsum/$t + 0.5))."\n"; } $ret .= "
    ".sprintf("-"x($typlen+$evtlen+33))."
    "; } } } if ($DE) { $ret .= sprintf("%".($typlen+$evtlen+10)."s","="x10).sprintf("%2s"," ").sprintf("="x6)."\n"; $ret .= sprintf("%".($typlen+$evtlen)."s","Summe: ").sprintf("%-10s",$evtsum).sprintf("%2s","∅:").sprintf("%-8s",$te ? int($evtsum/$te + 0.5) : "")."\n"; $ret .= sprintf("%".($typlen+$evtlen)."s","Dauer: ").sprintf("%d:%02d",int($te),int(($te-int($te))*60+.5))."\n"; $ret .= sprintf("%".($typlen+$evtlen+1)."s","Geräte: ").sprintf("%-10s",$i)."\n"; $ret .= sprintf("%".($typlen+$evtlen+1)."s","Events/Gerät: ").sprintf("%-10s",int($evtsum/$i + 0.5))."\n\n" if ($i); fhem("count",1) =~ m/(\d+)/; $ret .= sprintf("%".($typlen+$evtlen+1)."s","Geräte total: ").sprintf("%-10s","$1\n\n"); $ret .= sprintf("%".($typlen+$evtlen+1)."s","Filter\n"); $ret .= sprintf("%".($typlen+$evtlen)."s","TYPE: ").sprintf("%-10s",ReadingsVal($pn,"statisticsTYPEs","")."\n"); $ret .= sprintf("%".($typlen+$evtlen-7)."s","NAME: ").sprintf("%-10s",ReadingsVal($pn,"statisticsDeviceFilterRegex",".*")."\n"); $ret .= sprintf("%".($typlen+$evtlen-7)."s","Rate: ").sprintf("%-10s",">= $compRate\n\n"); } else { $ret .= sprintf("%".($typlen+$evtlen+10)."s","="x10).sprintf("%2s"," ").sprintf("="x6)."\n"; $ret .= sprintf("%".($typlen+$evtlen)."s","Total: ").sprintf("%-10s",$evtsum).sprintf("%2s","∅:").sprintf("%-8s",$te ? int($evtsum/$te + 0.5) : "")."\n"; $ret .= sprintf("%".($typlen+$evtlen)."s","Duration: ").sprintf("%d:%02d",int($te),int(($te-int($te))*60+.5))."\n"; $ret .= sprintf("%".($typlen+$evtlen)."s","Devices: ").sprintf("%-10s",$i)."\n"; $ret .= sprintf("%".($typlen+$evtlen)."s","Events/device: ").sprintf("%-10s",int($evtsum/$i + 0.5))."\n\n" if ($i); fhem("count",1) =~ m/(\d+)/; $ret .= sprintf("%".($typlen+$evtlen)."s","Devices total: ").sprintf("%-10s","$1\n\n"); $ret .= sprintf("%".($typlen+$evtlen+1)."s","Filter\n"); $ret .= sprintf("%".($typlen+$evtlen)."s","TYPE: ").sprintf("%-10s",ReadingsVal($pn,"statisticsTYPEs","")."\n"); $ret .= sprintf("%".($typlen+$evtlen-7)."s","NAME: ").sprintf("%-10s",ReadingsVal($pn,"statisticsDeviceFilterRegex",".*")."\n"); $ret .= sprintf("%".($typlen+$evtlen-7)."s","Rate: ").sprintf("%-10s",">= $compRate\n\n"); } $ret .= "
    ".sprintf("-"x($typlen+$evtlen+33))."
    "; # attibute statistics if ($DE) { $ret .= "".sprintf("%-30s","genutzte Attribute in DOIF").sprintf("%-12s","Anzahl")."\n"; } else { $ret .= "".sprintf("%-30s","used attributes in DOIF").sprintf("%-12s","Number")."\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("%-30s","$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); if ($DE) { $ret .= "\n \n" if (@coll); } else { $ret .= "\n \n" if (@coll); } foreach my $di (@doifList) { $ret .= DOIFtoolsCheckDOIF($hash,$di); } $ret = $DE ? ($ret ? "Empfehlung gefunden für:\n\n$ret" : "Keine Empfehlung gefunden.") : ($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; } elsif ($arg eq "SetAttrIconForDOIF") { $ret .= CommandAttr(undef,"$value icon helper_doif"); $ret .= CommandSave(undef,undef) if (AttrVal($pn,"DOIFtoolsExecuteSave","")); return $ret; } else { my $hardcoded = "checkDOIF:noArg statisticsReport:noArg runningTimerInDOIF:noArg"; return "unknown argument $arg for $pn, choose one of readingsGroup_for:multiple-strict,$dL DOIF_to_Log:multiple-strict,$dL SetAttrIconForDOIF:multiple-strict,$dL userReading_nextTimer_for:multiple-strict,$ntL ".(AttrVal($pn,"DOIFtoolsHideGetSet",0) ? $hardcoded :""); } return $ret; } 1; =pod =item helper =item summary tools to support DOIF =item summary_DE Werkzeuge zur Unterstützung von DOIF =begin html

    DOIFtools

    =end html =begin html_DE

    DOIFtools

    =end html_DE =cut