############################################# # $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; use Color; use vars qw(%FW_rooms %FW_groups); 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,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; #DOIFtoolsForceGet:true } 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 $lang = AttrVal("global","language","EN"); my $ret = ""; # call DOIF_detailFn no strict "refs"; my $retfn = &{ReadingsVal($dtn[0],".DOIF_detailFn","")}($FW_wname, $d, $room, $pageHash) if (ReadingsVal($dtn[0],".DOIF_detailFn","")); $ret .= $retfn if ($retfn); 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"; $ret .= "
" if (ReadingsVal($dtn[0],".DOIF_detailFn","")); $ret .= ""; $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 endtop listing
"; my $suffix = "
jump to: the toptop 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 ($DOIFtoolsJSfuncStart && !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  "; # Shortcuts 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 ((",".AttrVal("global","holiday2we","").",") =~ /\,$sn\,/) { @DOIFtools_we = (0,0,0,0,0,0,0,0,0); foreach my $item (split(",",AttrVal("global","holiday2we",""))) { my $val; my $a; my $b; for (my $i = 0; $i < 9; $i++) { $val = CommandGet(undef,"$item days $i"); if($val) { ($a, $b) = ReplaceEventMap($item, [$item, $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")."
"; my $trigtmp = CommandList(undef,$sn); $trigtmp =~ s/\n|\r/
/g; $trig .= $trigtmp; 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; } # DOIFtoolsLinColorGrad(start_color,end_color,percent|[$min,max,current]) # start_color, end_color: 6 hexadecimal values as string with or without leading # # percent: from 0 to 1 # min: minmal value # max: maximal value # current: current value # return: 6 hexadecimal value as string, prefix depends on input sub DOIFtoolsLinColorGrad { my ($sc,$ec,$pct,$max,$cur) = @_; $pct = ($cur-$pct)/($max-$pct) if (@_ == 5); my $prefix = ""; $prefix = "#" if ("$sc $ec"=~"#"); $sc =~ s/^#//; $ec =~ s/^#//; $pct = $pct > 1 ? 1 : $pct; $pct = $pct < 0 ? 0 : $pct; $sc =~/([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})/; my @sc = (hex($1),hex($2),hex($3)); $ec =~/([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})/; my @ec = (hex($1),hex($2),hex($3)); my @rgb; for (0..2) { $rgb[$_] = sprintf("%02X", int(($ec[$_] - $sc[$_])*$pct + $sc[$_] + .5)); } return $prefix.join("",@rgb); } sub DOIFtoolsHsvColorGrad { my ($cur,$min,$max,$min_s,$max_s,$s,$v)=@_; my $m=($max_s-$min_s)/($max-$min); my $n=$min_s-$min*$m; if ($cur>$max) { $cur=$max; } elsif ($cur<$min) { $cur=$min; } my $h=$cur*$m+$n; $h /=360; $s /=100; $v /=100; my($r,$g,$b)=Color::hsv2rgb ($h,$s,$v); $r *= 255; $g *= 255; $b *= 255; return sprintf("#%02X%02X%02X", $r+0.5, $g+0.5, $b+0.5); } 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 (!$7 && length($7) == 0); my $timer = timelocal($6,$5,$4,$1,$2-1,$3); my $weekd = $7; if ($weekd =~ s/\[(.*):(.*)\]//) { $weekd .= ReadingsVal($1,length($2)>0?$2:"state","") if($1); } my $tdays = ""; $tdays = $tn ? DOIF_weekdays($defs{$tn},$weekd) : $weekd; $tdays =~/([0-9])/; return $tstr if (length($1) == 0); my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($timer); my $ilook = 0; my $we; my $twe; for (my $iday = $wday; $iday < 7; $iday++) { $we = (($iday==0 || $iday==6) ? 1 : 0); if(!$we) { $we = $DOIFtools_we[$ilook + 1]; } $twe = (($iday==5 || $iday==6) ? 1 : 0); if(!$twe) { $twe = $DOIFtools_we[$ilook + 2]; } if ($tdays =~ /$iday/ or ($tdays =~ /7/ and $we) or ($tdays =~ /8/ and !$we) or ($tdays =~ /9/ and $twe)) { 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]; } $twe = (($iday==5 || $iday==6) ? 1 : 0); if(!$twe) { $twe = $DOIFtools_we[$ilook + 2]; } if ($tdays =~ /$iday/ or ($tdays =~ /7/ and $we) or ($tdays =~ /8/ and !$we) or ($tdays =~ /9/ and $twe)) { 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 .= "
  • nach DOELSE ist möglicherweise eine Bedingung angegeben, weil $2 gefunden wurde, bitte prüfen (ignorieren, wenn der Operator zu einem Befehl gehört).
  • \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 .= "
  • $2 found after DOELSE, it seems to be a condition, check it (ignore if it's part of a command).
  • \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); return "Only one instance of DOIFtools is allowed per FHEM installation. Delete the old one first."; } $hash->{logfile} = AttrVal($pn,"DOIFtoolsLogDir",AttrVal("global","logdir","./log/"))."$hash->{TYPE}Log-%Y-%j.log"; DOIFtoolsCounterReset($pn); readingsSingleUpdate($hash,"state","initialized",0); 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 "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); FW_directNotify("#FHEMWEB:$FW_wname", "location.reload('".AttrVal($pn,"DOIFtoolsForceGet","")."')", ""); } 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); FW_directNotify("#FHEMWEB:$FW_wname", "location.reload('".AttrVal($pn,"DOIFtoolsForceGet","")."')", ""); } 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 @doifListFHEM = devspec2array("TYPE=DOIF" and "MODEL=FHEM"); my @doifListPerl = devspec2array("TYPE=DOIF" and "MODEL=Perl"); 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))."
    "; # model statistics if ($DE) { $ret .= "".sprintf("%-30s","DOIF-Modelle").sprintf("%-12s","Anzahl")."\n"; } else { $ret .= "".sprintf("%-30s","Models of DOIF").sprintf("%-12s","Number")."\n"; } $ret .= sprintf("-"x42)."\n"; $ret .= sprintf("%-30s","FHEM").sprintf("%-12s","".@doifListFHEM)."\n"; $ret .= sprintf("%-30s","Perl").sprintf("%-12s","".@doifListPerl)."\n\n"; # 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 (@doifListFHEM) { $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 (@doifListFHEM) { $ret .= DOIFtoolsCheckDOIF($hash,$di); } $ret = $DE ? ($ret ? "Empfehlung gefunden für MODEL FHEM:\n\n$ret" : "Keine Empfehlung gefunden.") : ($ret ? "Found recommendation for MODEL FHEM:\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."; $ret .= "\n\n".fhem("blockinginfo",1); return $ret; } elsif ($arg eq "SetAttrIconForDOIF") { $ret .= CommandAttr(undef,"$value icon helper_doif"); $ret .= CommandSave(undef,undef) if (AttrVal($pn,"DOIFtoolsExecuteSave","")); return $ret; } elsif ($arg eq "linearColorGradient") { my ($sc,$ec,$min,$max,$step) = split(",",$value); if ($value && $sc =~ /[0-9A-F]{6}/ && $ec =~ /[0-9A-F]{6}/ && $min =~ /(-?\d+(\.\d+)?)/ && $max =~ /(-?\d+(\.\d+)?)/ && $step =~ /(-?\d+(\.\d+)?)/) { $ret .= "
    "; $ret .= ""; $ret .= ""; $ret .= ""; for (my $i=$min;$i<=$max;$i+=$step) { my $col = DOIFtoolsLinColorGrad($sc,$ec,$min,$max,$i); $col =~ /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/; $ret .= ""; } $ret .= "
    Color Table
    "; for (my $i=0;$i<=127;$i++) { my $col = DOIFtoolsLinColorGrad($sc,$ec,0,127,$i); $ret .= " "; } $ret .= "
    Value Color Number RGB values Color
    ".sprintf("%.1f",$i)."$col ".hex($1).",".hex($2).",".hex($3)."       
    ";
            
           return $ret;
          } else {
            $ret = $DE ? "
    Falsche Eingabe: $value
    Syntax: <Startfarbnummer>,<Endfarbnummer>,<Minimalwert>,<Maximalwert>,<Schrittweite>
    Beispielangabe: #0000FF,#FF0000,7,30,1
    ":"
    Wrong input: $value
    Syntax: <start color number>,<end color number>,<minimal value>,<maximal value>,<step width>
    Example specification: #0000FF,#FF0000,7,30,1
    ";
            return $ret
          }
      } elsif ($arg eq "hsvColorGradient") {
          my ($min_s,$max_s,$min,$max,$step,$s,$v)=split(",",$value);
          if ($value && $s >= 0 && $s <= 100 && $v >= 0 && $v <= 100  && $min_s >= 0 && $min_s <= 360 && $max_s >= 0 && $max_s <= 360) {
            $ret .= "
    "; $ret .= ""; $ret .= ""; $ret .= ""; for (my $i=$min;$i<=$max;$i+=$step) { my $col = DOIFtoolsHsvColorGrad($i,$min,$max,$min_s,$max_s,$s,$v); $col =~ /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/; $ret .= ""; } $ret .= "
    Color Table
    "; for (my $i=0;$i<=127;$i++) { my $col = DOIFtoolsHsvColorGrad($i,0,127,$min_s,$max_s,$s,$v); $ret .= " "; } $ret .= "
    Value Color Number RGB values Color
    ".sprintf("%.1f",$i)."$col ".hex($1).",".hex($2).",".hex($3)."       
    ";
            
           return $ret;
          } else {
            $ret = $DE ? "
    Falsche Eingabe: $value
    Syntax: <HUE-Startwert>,<HUE-Endwert>,<Minimalwert>,<Maximalwert>,<Schrittweite>,<Sättigung>,<Hellwert>
    Beispielangabe: 240,360,7,30,1,80,80
    ":"
    Wrong input: $value
    Syntax: <HUE start value>,<HUE end value>,<minimal value>,<maximal value>,<step width>,<saturation>,<lightness>
    Example specification: 240,360,7,30,1,80,80
    ";
            return $ret
          }
      } elsif ($arg eq "modelColorGradient") {
        my $err_ret = $DE ? "
    Falsche Eingabe: $value
    Syntax: <Minimalwert>,<Zwischenwert>,<Maximalwert>,<Schrittweite><Farbmodel>
    Beispiele:
    30,60,100,5,[255,255,0,127,255,0,0,255,0,0,255,255,0,127,255], z.B. Luftfeuchte
    7,20,30,1,[0,0,255,63,0,192,127,0,127,192,0,63,255,0,0], z.B. Temperatur
    0,2.6,5.2,0.0625,[192,0,0,208,63,0,224,127,0,240,192,0,255,255,0], z.B. Exponent der Helligkeit
    7,20,30,1,0
    ":"
    Wrong input: $value
    Syntax: <minimal value>,<middle value>,<maximal value>,<step width>,<color model>
    Example specifications:
    0,50,100,5,[255,255,0,127,255,0,0,255,0,0,255,255,0,127,255] e.g. humidity
    7,20,30,1,[0,0,255,63,0,192,127,0,127,192,0,63,255,0,0], e.g. temperature
    0,2.6,5.2,0.0625,[192,0,0,208,63,0,224,127,0,240,192,0,255,255,0], e.g. brightness exponent
    7,20,30,1,0
    ";
        return $err_ret if (!$value);
        my ($min,$mid,$max,$step,$colors);
        my $err = "";
        $value =~ s/,(\[.*\])//;
        if ($1) {
          $colors = eval($1);
          if ($@) {
            $err="Error eval 1567: $@\n".$err_ret;
            Log3 $hash->{NAME},3,"modelColorGradient \n".$err; 
            return $err;
          }
          ($min,$mid,$max,$step) = split(",",$value);
        } else {
          ($min,$mid,$max,$step,$colors) = split(",",$value);
        }
        return $err_ret if ($min>=$mid or $mid >= $max or $step <= 0 or (ref($colors) ne "ARRAY" && $colors !~ "0|1|2"));
        my $erg=eval("\"".Color::pahColor($min,$mid,$max,$min+$step,$colors)."\"");
        if ($@) {
          $err="Error eval 1577: $@\n".$err_ret;
          Log3 $hash->{NAME},3,"modelColorGradient \n".$err; 
        return $err;
        }
        $ret .= "
    "; $ret .= ""; $ret .= ""; $ret .= ""; for (my $i=$min;$i<=$max;$i+=$step) { my $col = eval("\"".Color::pahColor($min,$mid,$max,$i,$colors)."\""); if ($@) { $err="Error eval 1567: $@\n".$err_ret; Log3 $hash->{NAME},3,"modelColorGradient \n".$err; return $err; } $col = "#".substr($col,0,6); $col =~ /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/; $ret .= ""; } $ret .= "
    Color Table
    "; for (my $i=0;$i<=127;$i++) { my $col = eval("\"".Color::pahColor($min,$mid,$max,$min+$i*($max-$min)/127,$colors)."\""); if ($@) { $err="Error eval 1567: $@\n".$err_ret; Log3 $hash->{NAME},3,"modelColorGradient \n".$err; return $err; } $col = "#".substr($col,0,6); $ret .= " "; } $ret .= "
    Value Color Number RGB values Color
    ".sprintf("%.1f",$i)."$col ".hex($1).",".hex($2).",".hex($3)."       
    ";
        
        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 :"")." linearColorGradient:textField modelColorGradient:textField hsvColorGradient:textField";
      } 
    
      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.
      • delete visible readings in other devices with multiple choice, but not state.
      • 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 detail view and optionally in DOIFs detail view
      • convert events to DOIF operands, a selected operand is copied to clipboard and the DEF editor will open
      • check definitions and offer recommendations for DOIF MODEL FHEM
      • create shortcuts
      • optionally create a menu entry
      • show a list of running wait timer
      • scale values to color numbers and RGB values for coloration

      Just one definition per FHEM-installation is allowed. More in the german section.
    =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.
      • löschen von Readings in anderen Geräten über eine Mehrfachauswahl, nicht state.
      • 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.
      • erzeugt DOIF-Operanden aus einer Event-Zeile des Event-Monitors.
        • Ist der Event-Monitor in DOIF geöffnet, dann kann die Definition des DOIF geändert werden.
        • Ist der Event-Monitor in DOIFtools geöffnet, dann kann die Definition eines DOIF erzeugt werden.
      • prüfen der Definitionen mit Empfehlungen für DOIF-Modus FHEM.
      • erstellen von Shortcuts
      • optionalen Menüeintrag erstellen
      • Liste der laufenden Wait-Timer anzeigen
      • skaliert Werte zu Farbnummern und RGB Werten zum Einfärben, z.B. von Icons.

      Inhalt

      Bedienungsanleitung

      Definition
        define <name> DOIFtools
        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 probably associated with. Zusätzlich wird in jedem DOIF-Gerät in dieser Liste auf das DOIFtool verwiesen.

        Definitionsvorschlag zum Import mit Raw definition:
        defmod DOIFtools DOIFtools
        attr DOIFtools DOIFtoolsEventMonitorInDOIF 1
        attr DOIFtools DOIFtoolsExecuteDefinition 1
        attr DOIFtools DOIFtoolsExecuteSave 1
        attr DOIFtools DOIFtoolsMenuEntry 1
        attr DOIFtools DOIFtoolsMyShortcuts ##My Shortcuts:,,list DOIFtools,fhem?cmd=list DOIFtools,remove_DOIFtoolsLog,fhem?cmd=delete DOIFtoolsLog;%22rm ./log/DOIFtoolsLog*.log%22

      Set
      • set <name> deleteReadingInTargetDOIF <readings to delete name>
        deleteReadingInTargetDOIF löscht die benutzerdefinierten Readings im Ziel-DOIF

      • set <name> targetDOIF <target name>
        targetDOIF vor dem Löschen der Readings muss das Ziel-DOIF gesetzt werden.

      • set <name> deleteReadingInTargetDevice <readings to delete name>
        deleteReadingInTargetDevice löscht sichtbare Readings, ausser state im Ziel-Gerät. Bitte den Gefahrenhinweis zum Befehl deletereading beachten ! Commandref#deletereading

      • set <name> targetDevice <target name>
        targetDevice vor dem Löschen der Readings muss das Ziel-Gerät gesetzt werden.

      • set <name> sourceAttribute <readingList>
        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

      • set <name> statisticsDeviceFilterRegex <regular expression as device filter>
        statisticsDeviceFilterRegex setzt einen Filter auf Gerätenamen, nur die gefilterten Geräte werden im Bericht ausgewertet. Default, ".*".

      • set <name> statisticsTYPEs <List of TYPE used for statistics generation>
        statisticsTYPEs setzt eine Liste von TYPE für die Statistikdaten erfasst werden, bestehende Statistikdaten werden gelöscht. Default, "".

      • set <name> statisticsShowRate_ge <integer value for event rate>
        statisticsShowRate_ge setzt eine Event-Rate, ab der ein Gerät in die Auswertung einbezogen wird. Default, 0.

      • set <name> specialLog <0|1>
        specialLog 1 DOIF-Listing bei Status und Wait-Timer Aktualisierung im Debug-Logfile. Default, 0.

      • set <name> doStatistics <enabled|disabled|deleted>
        doStatistics
        deleted setzt die Statistik zurück und löscht alle stat_ Readings.
        disabled pausiert die Statistikdatenerfassung.
        enabled startet die Statistikdatenerfassung.

      • set <name> recording_target_duration <hours>
        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>
        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.

      • get <name> checkDOIF
        checkDOIF führt eine einfache Syntaxprüfung durch und empfiehlt Änderungen für DOIF-Modus FHEM.

      • get <name> readingsGroup_for <DOIF names to create readings groups>
        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.

      • get <name> userReading_nextTimer_for <DOIF names where to create real date timer readings>
        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.

      • get <name> statisticsReport
        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.FHEM-Wiki: Events

      • get <name> runningTimerInDOIF
        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. Zeigt nachrichtlich das Ergebnis von blockinginfo an.

      • get <name> SetAttrIconForDOIF <DOIF names for setting the attribute icon to helper_doif>
        SetAttrIconForDOIF setzt für die ausgewählten DOIF das Attribut icon auf helper_doif.

      • get <name> linearColorGradient <start color number>,<end color number>,<minimal value>,<maximal value>,<step width>
        linearColorGradient erzeugt eine Tabelle mit linear abgestuften Farbnummern und RGB-Werten.
        <start color number>, ist eine HTML-Farbnummer, Beispiel: #0000FF für Blau.
        <end color number>, , ist eine HTML-Farbnummer, Beispiel: #FF0000 für Rot.
        <minimal value>, der Minimalwert auf den die Startfarbnummer skaliert wird, Beispiel: 7.
        <maximal value>, der Maximalwert auf den die Endfarbnummer skaliert wird, Beispiel: 30.
        <step width>, für jeden Schritt wird ein Farbwert erzeugt, Beispiel: 0.5.
        Beispiel: get DOIFtools linearColorGradient #0000FF,#FF0000,7,30,0.5

      • get <name> modelColorGradient <minimal value>,<middle value>,<maximal value>,<step width>,<color model>
        modelColorGradient erzeugt eine Tabelle mit modellbedingt abgestuften Farbnummern und RGB-Werten, siehe FHEM-Wiki Farbskala mit Color::pahColor
        <minimal value>, der Minimalwert auf den die Startfarbnummer skaliert wird, Beispiel: 7.
        <middle value>, der Mittenwert ist ein Fixpunkt zwischen Minimal- u. Maximalwert, Beispiel: 20.
        <maximal value>, der Maximalwert auf den die Endfarbnummer skaliert wird, Beispiel: 30.
        <step width>, für jeden Schritt wird ein Farbwert erzeugt, Beispiel: 1.
        <color model>, die Angabe eines vordefinierten Modells <0|1|2> oder fünf RGB-Werte als Array [r1,g1,b1,r2,g2,b2,r3,g3,b3,r4,g4,b4,r5,g5,b5] für ein eigenes Model.

        Beispiele:
        get DOIFtools modelColorGradient 7,20,30,1,0
        get DOIFtools modelColorGradient 0,50,100,5,[255,255,0,127,255,0,0,255,0,0,255,255,0,127,255]
        Farbskala mit Color::pahColor

      • get <name> hsvColorGradient <HUE start value>,<HUE end value>,<minimal value>,<maximal value>,<step width>,<saturation>,<lightness>
        hsvColorGradient erzeugt eine Tabelle über HUE-Werte abgestufte Farbnummern und RGB-Werten.
        <Hue start value>, der HUE-Startwert, Beispiel: 240 für Blau.
        <HUE end value>, der HUE-Endwert, Beispiel: 360 für Rot.
        <minimal value>, der Minimalwert auf den der HUE-Startwert skaliert wird, Beispiel: 7.
        <maximal value>, der Maximalwert auf den der HUE-Endwert skaliert wird, Beispiel: 30.
        <step width>, für jeden Schritt wird ein Farbwert erzeugt, Beispiel: 1.
        <saturation>, die Angabe eines Wertes für die Farbsättigung <0-100>, Beispiel 80.
        <lightness>, die Angabe eines Wertes für die Helligkeit <0-100>, Beispiel 80.

        Beispiele:
        get DOIFtools hsvColorGradient 240,360,7,30,1,80,80

      Attribute
      • attr <name> DOIFtoolsExecuteDefinition <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.

      • attr <name> DOIFtoolsExecuteSave <0|1>
        DOIFtoolsExecuteSave 1, die Definitionen werden automatisch gespeichert. Default 0, der Benutzer kann die Definitionen speichern.

      • attr <name> DOIFtoolsTargetGroup <group names for target>
        DOIFtoolsTargetGroup gibt die Gruppen für die zu erstellenden Definitionen an. Default, die Gruppe der Ursprungs Definition.

      • attr <name> DOIFtoolsTargetRoom <room names for target>
        DOIFtoolsTargetRoom gibt die Räume für die zu erstellenden Definitionen an. Default, der Raum der Ursprungs Definition.

      • attr <name> DOIFtoolsReadingsPrefix <user defined prefix>
        DOIFtoolsReadingsPrefix legt den Präfix der benutzerdefinierten Readingsnamen für die Zieldefinition fest. Default, DOIFtools bestimmt den Präfix.

      • attr <name> DOIFtoolsEventMonitorInDOIF <1|0>
        DOIFtoolsEventMonitorInDOIF 1, die Anzeige des Event-Monitors wird in DOIF ermöglicht. Default 0, kein Zugriff auf den Event-Monitor im DOIF.

      • attr <name> DOIFtoolsEMbeforeReadings <1|0>
        DOIFtoolsEMbeforeReading 1, die Anzeige des Event-Monitors wird in DOIF direkt über den Readings angezeigt. Default 0, anzeige des Event-Monitors über den Internals.

      • attr <name> DOIFtoolsHideGetSet <0|1>
        DOIFtoolsHideGetSet 1, verstecken der Set- und Get-Shortcuts. Default 0.

      • attr <name> DOIFtoolsNoLookUp <0|1>
        DOIFtoolsNoLookUp 1, es werden keine Lookup-Fenster in DOIFtools geöffnet. Default 0.

      • attr <name> DOIFtoolsNoLookUpInDOIF <0|1>
        DOIFtoolsNoLookUpInDOIF 1, es werden keine Lookup-Fenster in DOIF geöffnet. Default 0.

      • attr <name> DOIFtoolsHideModulShortcuts <0|1>
        DOIFtoolsHideModulShortcuts 1, verstecken der DOIFtools Shortcuts. Default 0.

      • attr <name> DOIFtoolsHideStatReadings <0|1>
        DOIFtoolsHideStatReadings 1, verstecken der stat_ Readings. Das Ändern des Attributs löscht eine bestehende Event-Aufzeichnung. Default 0.

      • attr <name> DOIFtoolsEventOnDeleted <0|1>
        DOIFtoolsEventOnDeleted 1, es werden Events für alle stat_ erzeugt, bevor sie gelöscht werden. Damit könnten die erfassten Daten geloggt werden. Default 0.

      • attr <name> DOIFtoolsMyShortcuts <shortcut name>,<command>, ...
        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 ##<br>My Shortcuts:,,list DOIFtools,fhem?cmd=list DOIFtools
        menuEntries
      • attr <name> DOIFtoolsMenuEntry <0|1>
        DOIFtoolsMenuEntry 1, erzeugt einen Menüeintrag im FHEM-Menü. Default 0.

      • attr <name> DOIFtoolsLogDir <path to DOIFtools logfile>
        DOIFtoolsLogDir <path>, gibt den Pfad zum Logfile an Default ./log oder der Pfad aus dem Attribut global logdir.

        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.

      • Action zeigt den Status der Event-Aufzeichnung an.
      • 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.
      • statisticShowRate_ge zeigt die Event-Rate, ab der Geräte in die Auswertung einbezogen werden.
      • statisticsDeviceFilterRegex zeigt den aktuellen Gerätefilterausdruck an.
      • statisticsTYPEs zeigt eine Liste von TYPE an, für deren Geräte die Statistik erzeugt wird.
      • targetDOIF zeigt das Ziel-DOIF, bei dem Readings gelöscht werden sollen.
      • targetDevice zeigt das Ziel-Gerät, bei dem Readings gelöscht werden sollen.

      Links
    =end html_DE =cut