############################################# # $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; sub DOIF_cmd ($$$$); sub DOIF_delTimer($) { my ($hash) = @_; RemoveInternalTimer($hash); foreach my $key (keys %{$hash->{triggertime}}) { RemoveInternalTimer (\$hash->{triggertime}{$key}); } } sub DOIF_delAll($) { my ($hash) = @_; delete ($hash->{helper}); delete ($hash->{condition}); delete ($hash->{do}); delete ($hash->{devices}); delete ($hash->{time}); delete ($hash->{timer}); delete ($hash->{timers}); delete ($hash->{itimer}); delete ($hash->{timeCond}); delete ($hash->{realtime}); delete ($hash->{localtime}); delete ($hash->{days}); delete ($hash->{readings}); delete ($hash->{internals}); delete ($hash->{trigger}); delete ($hash->{triggertime}); delete ($hash->{interval}); delete ($hash->{regexp}); delete ($hash->{state}); delete ($defs{$hash->{NAME}}{READINGS}); } ######################### sub DOIF_Initialize($) { my ($hash) = @_; $hash->{DefFn} = "DOIF_Define"; $hash->{SetFn} = "DOIF_Set"; $hash->{UndefFn} = "DOIF_Undef"; $hash->{AttrFn} = "DOIF_Attr"; $hash->{NotifyFn} = "DOIF_Notify"; $hash->{AttrList} = "disable:0,1 loglevel:0,1,2,3,4,5,6 wait do:always,resetwait cmdState state initialize repeatsame repeatcmd waitsame waitdel cmdpause timerWithWait:1,0 notexist selftrigger:wait,all timerevent:1,0 checkReadingEvent:1,0 addStateEvent:1,0 ".$readingFnAttributes; } sub GetBlockDoIf ($$) { my ($cmd,$match) = @_; my $count=0; my $first_pos=0; my $last_pos=0; my $err=""; while($cmd =~ /$match/g) { if (substr($cmd,pos($cmd)-1,1) eq substr($match,2,1)) { $count++; $first_pos=pos($cmd) if ($count == 1); } elsif (substr($cmd,pos($cmd)-1,1) eq substr($match,4,1)) { $count--; } if ($count < 0) { $err="right bracket without left bracket"; return ("",substr($cmd,pos($cmd)-1),$err,""); } if ($count == 0) { $last_pos=pos($cmd); last; } } if ($count > 0) { $err="no right bracket"; return ("",substr($cmd,$first_pos-1),$err); } if ($first_pos) { return (substr($cmd,0,$first_pos-1),substr($cmd,$first_pos,$last_pos-$first_pos-1),"",substr($cmd,$last_pos)); } else { return ($cmd,"","",""); } } sub GetCommandDoIf ($$) { my ($separator,$tailBlock) = @_; my $char; my $beginning; my $currentBlock; my $err; my $cmd=""; while ($tailBlock=~ /^([^$separator^"^\[^\{^\(]*)/g) { $char=substr($tailBlock,pos($tailBlock),1); if ($char eq $separator) { $cmd=$cmd.substr($tailBlock,0,pos($tailBlock)); $tailBlock=substr($tailBlock,pos($tailBlock)+1); return($cmd,$tailBlock,""); } elsif ($char eq '{') { ($beginning,$currentBlock,$err,$tailBlock)=GetBlockDoIf($tailBlock,'[\{\}]'); return ($currentBlock,$tailBlock,$err) if ($err); $cmd=$cmd.$beginning."{$currentBlock}"; } elsif ($char eq '(') { ($beginning,$currentBlock,$err,$tailBlock)=GetBlockDoIf($tailBlock,'[\(\)]'); return ($currentBlock,$tailBlock,$err) if ($err); $cmd=$cmd.$beginning."($currentBlock)"; } elsif ($char eq '[') { ($beginning,$currentBlock,$err,$tailBlock)=GetBlockDoIf($tailBlock,'[\[\]]'); return ($currentBlock,$tailBlock,$err) if ($err); $cmd=$cmd.$beginning."[$currentBlock]"; } elsif ($char eq '"') { if ($tailBlock =~ /(^[^"]*"[^"]*")(.*)/) { $cmd=$cmd.$1; $tailBlock=$2; } } } if ($cmd eq "") { $cmd=$tailBlock; } else { $cmd=$cmd.$tailBlock } return ($cmd,"",""); } sub EvalValueDoIf($$$) { my ($hash,$attr,$value)=@_; return undef if (!defined($value)); my $err=""; my $pn=$hash->{NAME}; ($value,$err)=ReplaceAllReadingsDoIf($hash,$value,-1,1); if ($err) { my $error="$pn: error in $attr: $err"; Log3 $pn,4 , $error; readingsSingleUpdate ($hash, "error", $error,0); $value=0; } else { my $ret = eval $value; if ($@) { my $error="$pn: error in $attr: $value"; Log3 $pn,4 , $error; readingsSingleUpdate ($hash, "error", $error,0); $value=0; } else { $value=$ret; } } return ($value); } sub EvalCmdStateDoIf($$) { my ($hash,$state)=@_; my $err; my $pn=$hash->{NAME}; ($state,$err)=ReplaceAllReadingsDoIf($hash,$state,-1,1); if ($err) { Log3 $pn,4 , "$pn: error in state: $err" if ($err); $state=$err; } else { ($state,$err)=EvalAllDoIf($hash, $state); if ($err) { Log3 $pn,4 , "$pn: error in state: $err" if ($err); $state=$err; } } return($state) } sub SplitDoIf($$) { my ($separator,$tailBlock)=@_; my @commands; my $cmd; my $err; if (defined $tailBlock) { while ($tailBlock ne "") { ($cmd,$tailBlock,$err)=GetCommandDoIf($separator,$tailBlock); #return (@commands,$err) if ($err); push(@commands,$cmd); } } return(@commands); } sub EventCheckDoif($$$$) { my ($n,$dev,$eventa,$NotifyExp)=@_; my $found=0; my $s; return 0 if ($dev ne $n); return 0 if(!$eventa); my $max = int(@{$eventa}); my $ret = 0; if ($NotifyExp eq "") { return 1 ; } for (my $i = 0; $i < $max; $i++) { $s = $eventa->[$i]; $s = "" if(!defined($s)); $found = ($s =~ m/$NotifyExp/); if ($found) { return 1; } } return 0; } sub EventDoIf($$$$) { my ($n,$hash,$NotifyExp,$check)=@_; my $dev=$hash->{helper}{triggerDev}; my $eventa=$hash->{helper}{triggerEvents}; if ($check) { return 0 if ($dev ne $n); } else { return 0 if ($n and $dev !~ /$n/); } return 0 if(!$eventa); my $max = int(@{$eventa}); my $ret = 0; if ($NotifyExp eq "") { return 1 ; } for (my $i = 0; $i < $max; $i++) { my $s = $eventa->[$i]; $s = "" if(!defined($s)); my $found = ($s =~ m/$NotifyExp/); if ($found) { $hash->{helper}{event}=$s; return 1; } #if(!$found && AttrVal($n, "eventMap", undef)) { # my @res = ReplaceEventMap($n, [$n,$s], 0); # shift @res; # $s = join(" ", @res); # $found = ("$n:$s" =~ m/^$re$/); } return 0; } sub InternalDoIf($$$$$$) { my ($hash,$name,$internal,$regExp,$output,$default)=@_; my $r=""; my $element; return ($default =~ /^"(.*)"$/) ? $1 : $default if (!defined $defs{$name}); return ($default =~ /^"(.*)"$/) ? $1 : $default if (!defined $defs{$name}{$internal}); $r=$defs{$name}{$internal}; if ($regExp) { $element = ($r =~ /$regExp/) ? $1 : ""; if ($output) { $element= eval $output; if ($@) { Log3 ($hash->{NAME},4 , "$hash->{NAME}: $@"); readingsSingleUpdate ($hash, "error", $@,0); return(undef); } } } else { $element=$r; } return($element); } sub ReadingSecDoIf($$) { my ($name,$reading)=@_; my ($seconds, $microseconds) = gettimeofday(); return ($seconds - time_str2num(ReadingsTimestamp($name, $reading, "1970-01-01 01:00:00"))); } sub ReadingValDoIf($$$$$$) { my ($hash,$name,$reading,$regExp,$output,$default)=@_; my $r; my $element; return ($default =~ /^"(.*)"$/) ? $1 : $default if (!defined $defs{$name}); return ($default =~ /^"(.*)"$/) ? $1 : $default if (!defined $defs{$name}{READINGS}{$reading}{VAL}); $r=$defs{$name}{READINGS}{$reading}{VAL}; $r="" if (!defined($r)); if ($regExp) { $element = ($r =~ /$regExp/) ? $1 : ""; if ($output) { $element= eval $output; if ($@) { Log3 ($hash->{NAME},4 , "$hash->{NAME}: $@"); readingsSingleUpdate ($hash, "error", $@,0); return(undef); } } } else { $element=$r; } return($element); } sub EvalAllDoIf($$) { my ($hash,$tailBlock)= @_; my $eval=""; my $beginning; my $err; my $cmd=""; my $ret=""; my $eventa=$hash->{helper}{triggerEvents}; my $device=$hash->{helper}{triggerDev}; my $event=$hash->{helper}{event}; my $events=""; if ($eventa) { $events=join(",",@{$eventa}); } while ($tailBlock ne "") { ($beginning,$eval,$err,$tailBlock)=GetBlockDoIf($tailBlock,'[\{\}]'); return ($eval,$err) if ($err); if ($eval) { if (substr($eval,0,1) eq "(") { my $ret = eval $eval; return($eval." ",$@) if ($@); $eval=$ret; } else { $eval="{".$eval."}"; } } $cmd.=$beginning.$eval; } return ($cmd,""); } sub ReplaceReadingDoIf($) { my ($element) = @_; my $beginning; my $tailBlock; my $err; my $regExp=""; my $name; my $reading; my $format; my $output=""; #my ($name,$reading,$format)=split(":",$element); my $internal=""; my $notifyExp=""; if ($element =~ /^([^:]*):(".*")/) { $name=$1; $reading=$2; } elsif ($element =~ /^([^:]*)(?::([^:]*)(?::(.*))?)?/) { $name=$1; $reading=$2; $format=$3; } if ($name) { if ($reading) { if (substr($reading,0,1) eq "\?") { $notifyExp=substr($reading,1); return("EventDoIf('$name',".'$hash,'."'$notifyExp',1)","",$name,undef,undef); } elsif ($reading =~ /^"(.*)"$/g) { $notifyExp=$1; return("EventDoIf('$name',".'$hash,'."'$notifyExp',1)","",$name,undef,undef); } $internal = substr($reading,1) if (substr($reading,0,1) eq "\&"); if ($format) { if ($format eq "sec") { return("ReadingSecDoIf('$name','$reading')","",$name,$reading,undef); } elsif (substr($format,0,1) eq '[') { #old Syntax ($beginning,$regExp,$err,$tailBlock)=GetBlockDoIf($format,'[\[\]]'); return ($regExp,$err) if ($err); return ($regExp,"no round brackets in regular expression") if ($regExp !~ /.*\(.*\)/); } elsif ($format =~ /^"([^"]*)"(?::(.*))?/){ $regExp=$1; $output=$2; return ($regExp,"no round brackets in regular expression") if ($regExp !~ /.*\(.*\)/); } elsif ($format =~ /^d[^:]*(?::(.*))?/) { $regExp = '(-?\d+(\.\d+)?)'; $output=$1; } else { return($format,"unknown expression format"); } } $output="" if (!defined($output)); if ($internal) { return("InternalDoIf(".'$hash'.",'$name','$internal','$regExp','$output',".'AttrVal($hash->{NAME},'."'notexist',undef))","",$name,undef,$internal); } else { return("ReadingValDoIf(".'$hash'.",'$name','$reading','$regExp','$output',".'AttrVal($hash->{NAME},'."'notexist',undef))","",$name,$reading,undef); } } else { return("InternalDoIf(".'$hash'.",'$name','STATE','$regExp','$output',".'AttrVal($hash->{NAME},'."'notexist',undef))","",$name,undef,'STATE'); } } } sub ReplaceReadingEvalDoIf($$$) { my ($hash,$element,$eval) = @_; my ($block,$err,$device,$reading,$internal)=ReplaceReadingDoIf($element); return ($block,$err) if ($err); if ($eval) { if (!AttrVal($hash->{NAME},'notexist',undef)) { return ($device,"device does not exist: $device") if(!$defs{$device}); return ($block,"reading does not exist: $device:$reading") if (defined ($reading) and !defined($defs{$device}{READINGS}{$reading})); return ($block,"internal does not exist: $device:$internal") if (defined ($internal) and !defined($defs{$device}{$internal})); } my $ret = eval $block; return($block." ",$@) if ($@); $block=$ret; } return ($block,"",$device,$reading,$internal); } sub AddItemDoIf($$) { my ($items,$item)=@_; if (!$items) { $items=" $item "; } elsif ($items !~ / $item /) { $items.="$item "; } return $items; } sub AddRegexpTriggerDoIf($$$) { my ($hash,$regexp,$condition)= @_; my $max_regexp=keys %{$hash->{regexp}{$condition}}; for (my $i=0; $i<$max_regexp;$i++) { if ($hash->{regexp}{$condition}{$i} eq $regexp) { return; } } $hash->{regexp}{$condition}{$max_regexp}=$regexp; $max_regexp=keys %{$hash->{regexp}{all}}; for (my $i=0; $i<$max_regexp;$i++) { if ($hash->{regexp}{all}{$i} eq $regexp) { return; } } $hash->{regexp}{all}{$max_regexp}=$regexp; } sub ReplaceAllReadingsDoIf($$$$) { my ($hash,$tailBlock,$condition,$eval)= @_; my $block=""; my $beginning; my $err; my $cmd=""; my $ret=""; my $device=""; my $nr; my $timer=""; my $event=0; my $definition=$tailBlock; my $reading; my $internal; my $trigger=1; my $nameExp; my $notifyExp; if (!defined $tailBlock) { return ("",""); } while ($tailBlock ne "") { $trigger=1; ($beginning,$block,$err,$tailBlock)=GetBlockDoIf($tailBlock,'[\[\]]'); return ($block,$err) if ($err); if ($block ne "") { if ($block =~ /^\"(.*)\"$/){ my $exp=$1; if ($exp =~ /([^\:]*):(.*)/) { $nameExp=$1; $notifyExp=$2; } else { $nameExp=$exp; } #my ($nameExp,$notifyExp)=split (":",$1); if ($condition >= 0) { $nameExp="" if (!$nameExp); $notifyExp="" if (!$notifyExp); $block="EventDoIf('$nameExp',".'$hash,'."'$notifyExp',0)"; AddRegexpTriggerDoIf($hash,$exp,$condition); $event=1; } } else { if (substr($block,0,1) eq "?") { $block=substr($block,1); $trigger=0; } $trigger=0 if (substr($block,0,1) eq "\$"); if ($block =~ /^\$?[a-z0-9._]*[a-z._]+[a-z0-9._]*($|:.+$)/i) { ($block,$err,$device,$reading,$internal)=ReplaceReadingEvalDoIf($hash,$block,$eval); return ($block,$err) if ($err); if ($condition >= 0) { if ($trigger) { $hash->{devices}{$condition} = AddItemDoIf($hash->{devices}{$condition},$device); $hash->{devices}{all} = AddItemDoIf($hash->{devices}{all},$device); $event=1; } $hash->{readings}{$condition} = AddItemDoIf($hash->{readings}{$condition},"$device:$reading") if (defined ($reading)); $hash->{internals}{$condition} = AddItemDoIf($hash->{internals}{$condition},"$device:$internal") if (defined ($internal)); $hash->{readings}{all} = AddItemDoIf($hash->{readings}{all},"$device:$reading") if (defined ($reading)); $hash->{internals}{all} = AddItemDoIf($hash->{internals}{all},"$device:$internal") if (defined ($internal)); $hash->{trigger}{all} = AddItemDoIf($hash->{trigger}{all},"$device") if (!defined ($internal) and !defined($reading)); } elsif ($condition == -2) { $hash->{state}{device} = AddItemDoIf($hash->{state}{device},$device) if ($device ne $hash->{NAME}); } elsif ($condition == -3) { $hash->{itimer}{all} = AddItemDoIf($hash->{itimer}{all},$device); } } elsif ($condition >= 0) { ($timer,$err)=DOIF_CheckTimers($hash,$block,$condition,$trigger); return($timer,$err) if ($err); if ($timer) { $block=$timer; $event=1 if ($trigger); } } else { $block="[".$block."]"; } } } $cmd.=$beginning.$block; } return ($definition,"no trigger in condition") if ($condition >=0 and $event == 0); return ($cmd,""); } sub ParseCommandsDoIf($$$) { my($hash,$tailBlock,$eval) = @_; my $pn=$hash->{NAME}; my $currentBlock=""; my $beginning=""; my $err=""; my $pos=0; my $last_error=""; my $ifcmd; my $ret; my $eventa=$hash->{helper}{triggerEvents}; my $device=$hash->{helper}{triggerDev}; my $event=$hash->{helper}{event}; my $events=""; if ($eventa) { $events=join(",",@{$eventa}); } while ($tailBlock ne "") { if ($tailBlock=~ /^\s*\{/) { # perl block ($beginning,$currentBlock,$err,$tailBlock)=GetBlockDoIf($tailBlock,'[\{\}]'); return ($currentBlock,$err) if ($err); if ($currentBlock ne "") { ($currentBlock,$err)=ReplaceAllReadingsDoIf($hash,$currentBlock,-1,$eval); return ($currentBlock,$err) if ($err); if ($eval) { ($currentBlock,$err)=EvalAllDoIf($hash,$currentBlock); return ($currentBlock,$err) if ($err); } } $currentBlock="{".$currentBlock."}"; } elsif ($tailBlock =~ /^\s*IF/) { my $ifcmd=""; ($beginning,$currentBlock,$err,$tailBlock)=GetBlockDoIf($tailBlock,'[\(\)]'); #condition return ($currentBlock,$err) if ($err); $ifcmd.=$beginning."(".$currentBlock.")"; ($beginning,$currentBlock,$err,$tailBlock)=GetBlockDoIf($tailBlock,'[\(\)]'); #if case return ($currentBlock,$err) if ($err); $ifcmd.=$beginning."(".$currentBlock.")"; if ($tailBlock =~ /^\s*ELSE/) { ($beginning,$currentBlock,$err,$tailBlock)=GetBlockDoIf($tailBlock,'[\(\)]'); #else case return ($currentBlock,$err) if ($err); $ifcmd.=$beginning."(".$currentBlock.")"; } $currentBlock=$ifcmd; } else { if ($tailBlock =~ /^\s*\(/) { # remove bracket ($beginning,$currentBlock,$err,$tailBlock)=GetBlockDoIf($tailBlock,'[\(\)]'); return ($currentBlock,$err) if ($err); #$tailBlock=substr($tailBlock,pos($tailBlock)) if ($tailBlock =~ /^\s*,/g); } else { ($currentBlock,$tailBlock)=GetCommandDoIf(',',$tailBlock); } if ($currentBlock ne "") { ($currentBlock,$err)=ReplaceAllReadingsDoIf($hash,$currentBlock,-1,$eval); return ($currentBlock,$err) if ($err); if ($eval) { ($currentBlock,$err)=EvalAllDoIf($hash, $currentBlock); return ($currentBlock,$err) if ($err); } } } if ($eval) { if ($ret = AnalyzeCommandChain(undef,$currentBlock)) { Log3 $pn,2 , "$pn: $currentBlock: $ret"; $last_error.="$currentBlock: $ret "; } } $tailBlock=substr($tailBlock,pos($tailBlock)) if ($tailBlock =~ /^\s*,/g); } return("",$last_error); } sub DOIF_CheckTimers($$$$) { my $i=0; my @nrs; my @times; my $nr=0; my $days=""; my $err; my $beginning; my $pos; my $time; my $block; my $result; my ($hash,$timer,$condition,$trigger)=@_; $timer =~ s/\s//g; while ($timer ne "") { if ($timer=~ /^\+\(/) { ($beginning,$time,$err,$timer)=GetBlockDoIf($timer,'[\(\)]'); return ($time,$err) if ($err); $time="+(".$time.")"; ($result,$err)=ReplaceAllReadingsDoIf($hash,$time,-3,0); return ($time,$err) if ($err); } elsif ($timer=~ /^\(/) { ($beginning,$time,$err,$timer)=GetBlockDoIf($timer,'[\(\)]'); return ($time,$err) if ($err); $time="(".$time.")"; ($result,$err)=ReplaceAllReadingsDoIf($hash,$time,-3,0); return ($time,$err) if ($err); } elsif ($timer=~ /^\{/) { ($beginning,$time,$err,$timer)=GetBlockDoIf($timer,'[\{\}]'); return ($time,$err) if ($err); $time="{".$time."}"; } elsif ($timer=~ m/^\+\[([0-9]+)\]:([0-5][0-9])/g) { $pos=pos($timer); $time=substr($timer,0,$pos); $timer=substr($timer,$pos); } elsif ($timer=~ /^\+\[/) { ($beginning,$time,$err,$timer)=GetBlockDoIf($timer,'[\[\]]'); return ($time,$err) if ($err); $time="+[".$time."]"; ($result,$err)=ReplaceAllReadingsDoIf($hash,$time,-3,0); return ($time,$err) if ($err); } elsif ($timer=~ /^\[/) { ($beginning,$time,$err,$timer)=GetBlockDoIf($timer,'[\[\]]'); return ($time,$err) if ($err); $time="[".$time."]"; ($result,$err)=ReplaceAllReadingsDoIf($hash,$time,-3,0); return ($time,$err) if ($err); } elsif ($timer =~ /-/g) { $pos=pos($timer)-1; $time=substr($timer,0,$pos); $timer=substr($timer,$pos); } else { ($time,$days)=split(/\|/,$timer); $timer=""; } $times[$i]=$time; $nrs[$i++]=$hash->{helper}{last_timer}++; if ($timer) { if ($timer =~ /\-/g) { $timer=substr($timer,pos($timer)); } elsif ($timer =~ /\|/g) { $days=substr($timer,pos($timer)); $timer=""; } else { return ($timer,"wrong time format"); } } } $days = "" if (!defined ($days)); for (my $j=0; $j<$i;$j++) { $nr=$nrs[$j]; $time=$times[$j]; $time .=":00" if ($time =~ m/^[0-9][0-9]:[0-5][0-9]$/); $hash->{timer}{$nr}=0; $hash->{time}{$nr}=$time; $hash->{timeCond}{$nr}=$condition; $hash->{days}{$nr}=$days if ($days ne ""); if ($init_done) { $err=(DOIF_SetTimer($hash,"DOIF_TimerTrigger",$nr)); return($hash->{time}{$nr},$err) if ($err); } $hash->{timers}{$condition}.=" $nr " if ($trigger); } if ($i == 2) { $block='DOIF_time($hash,$hash->{realtime}{'.$nrs[0].'},$hash->{realtime}{'.$nrs[1].'},$wday,$hms,"'.$days.'")'; $hash->{interval}{$nrs[0]}=-1; $hash->{interval}{$nrs[1]}=$nrs[0]; } else { $block='DOIF_time_once($hash,$hash->{timer}{'.$nrs[0].'},$wday,"'.$days.'")'; } return ($block,""); } sub DOIF_time($$$$$$) { my $ret=0; my ($hash,$begin,$end,$wday,$hms,$days)=@_; my $err; ($days,$err)=ReplaceAllReadingsDoIf($hash,$days,-1,1); if ($err) { my $errmsg="error in days: $err"; Log3 ($hash->{NAME},4 , "$hash->{NAME}: $errmsg"); readingsSingleUpdate ($hash, "error", $errmsg,0); return 0; } my $we=DOIF_we($wday); if ($end gt $begin) { if ($hms ge $begin and $hms lt $end) { $ret=1; } } else { if ($hms ge $begin) { $ret=1; } elsif ($hms lt $end) { $wday=6 if ($wday-- == 0); $we=DOIF_we($wday); $ret=1; } } if ($ret == 1) { return 1 if ($days eq "" or $days =~ /$wday/ or ($days =~ /7/ and $we) or ($days =~ /8/ and !$we)); } return 0; } sub DOIF_time_once($$$$) { my ($hash,$flag,$wday,$days)=@_; my $err; ($days,$err)=ReplaceAllReadingsDoIf($hash,$days,-1,1); if ($err) { my $errmsg="error in days: $err"; Log3 ($hash->{NAME},4 , "$hash->{NAME}: $errmsg"); readingsSingleUpdate ($hash, "error", $errmsg,0); return 0; } my $we=DOIF_we($wday); if ($flag) { return 1 if ($days eq "" or $days =~ /$wday/ or ($days =~ /7/ and $we) or ($days =~ /8/ and !$we)); } return 0; } ############################ sub DOIF_SetState($$$$$) { my ($hash,$nr,$subnr,$event,$last_error)=@_; my $pn=$hash->{NAME}; my $cmdNr=""; my $cmd=""; my $err=""; my $attr=AttrVal($hash->{NAME},"cmdState",""); my $state=AttrVal($hash->{NAME},"state",""); my @cmdState=SplitDoIf('|',$attr); return undef if (AttrVal($hash->{NAME},"disable","")); $nr=ReadingsVal($pn,"cmd_nr",0)-1 if (!$event); if ($nr!=-1) { $cmdNr=$nr+1; my @cmdSubState=SplitDoIf(',',$cmdState[$nr]); if (defined $cmdSubState[$subnr]) { $cmd=EvalCmdStateDoIf($hash,$cmdSubState[$subnr]); } else { if (defined $hash->{do}{$nr}{$subnr+1}) { $cmd="cmd_".$cmdNr."_".($subnr+1); } else { if (defined ($cmdState[$nr]) and defined $cmdSubState[$subnr]) { $cmd=EvalCmdStateDoIf($hash,$cmdState[$nr]); } else { $cmd="cmd_$cmdNr"; } } } } if ($cmd =~ /^"(.*)"$/) { $cmd=$1; } readingsBeginUpdate ($hash); if ($event) { readingsBulkUpdate($hash,"cmd_nr",$cmdNr); if (defined $hash->{do}{$nr}{1}) { readingsBulkUpdate($hash,"cmd_seqnr",$subnr+1); readingsBulkUpdate($hash,"cmd",$cmdNr.".".($subnr+1)); } else { delete ($defs{$hash->{NAME}}{READINGS}{cmd_seqnr}); readingsBulkUpdate($hash,"cmd",$cmdNr); } readingsBulkUpdate($hash,"cmd_event",$event); if ($last_error) { readingsBulkUpdate($hash,"error",$last_error); } else { delete ($defs{$hash->{NAME}}{READINGS}{error}); } } # if ($state and !defined $hash->{do}{$nr}{$subnr+1}) { if ($state) { my $stateblock='\['.$pn.'\]'; $state =~ s/$stateblock/$cmd/g; $state=EvalCmdStateDoIf($hash,$state); } else { $state=$cmd; } readingsBulkUpdate($hash, "state", $state); readingsEndUpdate ($hash, 1); } sub DOIF_we($) { my ($wday)=@_; my $we = (($wday==0 || $wday==6) ? 1 : 0); if(!$we) { my $h2we = $attr{global}{holiday2we}; if($h2we && Value($h2we)) { my ($a, $b) = ReplaceEventMap($h2we, [$h2we, Value($h2we)], 0); $we = 1 if($b ne "none"); } } return $we; } sub DOIF_CheckCond($$) { my ($hash,$condition) = @_; my $err=""; my ($seconds, $microseconds) = gettimeofday(); my ($sec,$min,$hour,$mday,$month,$year,$wday,$yday,$isdst) = localtime($seconds); my $hms = sprintf("%02d:%02d:%02d", $hour, $min, $sec); my $hm = sprintf("%02d:%02d", $hour, $min); my $dev; my $reading; my $internal; my $we=DOIF_we($wday); my $eventa=$hash->{helper}{triggerEvents}; my $device=$hash->{helper}{triggerDev}; my $event=$hash->{helper}{event}; my $events=""; my $cmd=ReadingsVal($hash->{NAME},"cmd",0); if ($eventa) { $events=join(",",@{$eventa}); } $month++; $year+=1900; if (defined ($hash->{readings}{$condition})) { foreach my $devReading (split(/ /,$hash->{readings}{$condition})) { $devReading=~ s/\$DEVICE/$hash->{helper}{triggerDev}/g if ($devReading); if (!AttrVal($hash->{NAME},'notexist',undef)) { ($dev,$reading)=(split(":",$devReading)); return (-1,"device does not exist: [$dev:$reading]") if ($devReading and !defined ($defs{$dev})); return (-1,"reading does not exist: [$dev:$reading]") if ($devReading and !defined($defs{$dev}{READINGS}{$reading}{VAL})); } } } if (defined ($hash->{internals}{$condition})) { foreach my $devInternal (split(/ /,$hash->{internals}{$condition})) { $devInternal=~ s/\$DEVICE/$hash->{helper}{triggerDev}/g if ($devInternal); if (!AttrVal($hash->{NAME},'notexist',undef)) { ($dev,$internal)=(split(":",$devInternal)); return (-1,"device does not exist: [$dev:$internal]") if ($devInternal and !defined ($defs{$dev})); return (-1,"internal does not exist: [$dev:$internal]") if ($devInternal and !defined($defs{$dev}{$internal})); } } } my $command=$hash->{condition}{$condition}; if ($command) { my $eventa=$hash->{helper}{triggerEvents}; my $events=""; if ($eventa) { $events=join(",",@{$eventa}); } $command =~ s/\$DEVICE/$hash->{helper}{triggerDev}/g; $command =~ s/\$EVENTS/$events/g; $command =~ s/\$EVENT/$hash->{helper}{event}/g; #my $idx = 0; #my $evt; #foreach my $part (split(" ", $hash->{helper}{event})) { # $evt='\$EVTPART'.$idx; # $command =~ s/$evt/$part/g; # $idx++; #} } my $ret = eval $command; if($@){ $err = "perl error in condition: $hash->{condition}{$condition}: $@"; $ret = 0; } return ($ret,$err); } sub DOIF_cmd ($$$$) { my ($hash,$nr,$subnr,$event)=@_; my $pn = $hash->{NAME}; my $ret; my $cmd; my $err=""; my $repeatnr; my $last_cmd=ReadingsVal($pn,"cmd_nr",0)-1; my @cmdpause=SplitDoIf(':',AttrVal($pn,"cmdpause","")); my @sleeptimer=SplitDoIf(':',AttrVal($pn,"repeatcmd","")); my ($seconds, $microseconds) = gettimeofday(); my $cmdpauseValue=EvalValueDoIf($hash,"cmdpause",$cmdpause[$nr]); if ($cmdpauseValue and $subnr==0) { return undef if ($seconds - time_str2num(ReadingsTimestamp($pn, "state", "1970-01-01 01:00:00")) < $cmdpauseValue); } if (AttrVal($pn,"repeatsame","")) { my @repeatsame=SplitDoIf(':',AttrVal($pn,"repeatsame","")); my $repeatsameValue=EvalValueDoIf($hash,"repeatsame",$repeatsame[$nr]); if ($subnr == 0) { if ($repeatsameValue) { $repeatnr=ReadingsVal($pn,"cmd_count",0); if ($last_cmd == $nr) { if ($repeatnr < $repeatsameValue) { $repeatnr++; } else { delete ($defs{$hash->{NAME}}{READINGS}{cmd_count}) if (defined ($sleeptimer[$nr]) and (AttrVal($pn,"do","") eq "always" or AttrVal($pn,"do","") eq "resetwait")); return undef; } } else { $repeatnr=1; } readingsSingleUpdate ($hash, "cmd_count", $repeatnr,1); } else { return undef if ($last_cmd == $nr and $subnr==0 and (AttrVal($pn,"do","") ne "always" and AttrVal($pn,"do","") ne "resetwait")); delete ($defs{$hash->{NAME}}{READINGS}{cmd_count}); } } } if (AttrVal($pn,"waitsame","")) { my @waitsame=SplitDoIf(':',AttrVal($pn,"waitsame","")); my $waitsameValue=EvalValueDoIf($hash,"waitsame",$waitsame[$nr]); if ($subnr == 0) { if ($waitsameValue) { my $cmd_nr="cmd_".($nr+1); if (ReadingsVal($pn,"waitsame","") eq $cmd_nr) { if ($seconds - time_str2num(ReadingsTimestamp($pn, "waitsame", "1970-01-01 01:00:00")) > $waitsameValue) { readingsSingleUpdate ($hash, "waitsame", $cmd_nr,1); return undef; } } else { readingsSingleUpdate ($hash, "waitsame", $cmd_nr,1); return undef; } } delete ($defs{$hash->{NAME}}{READINGS}{waitsame}); } } if ($hash->{do}{$nr}{$subnr}) { $cmd=$hash->{do}{$nr}{$subnr}; my $eventa=$hash->{helper}{triggerEvents}; my $events=""; if ($eventa) { $events=join(",",@{$eventa}); } $cmd =~ s/\$DEVICE/$hash->{helper}{triggerDev}/g; $cmd =~ s/\$EVENTS/$events/g; $cmd =~ s/\$EVENT/$hash->{helper}{event}/g; #my $idx = 0; #my $evt; #foreach my $part (split(" ", $hash->{helper}{event})) { # $evt='\$EVTPART'.$idx; # $cmd =~ s/$evt/$part/g; # $idx++; #} #readingsSingleUpdate ($hash, "Event",$hash->{helper}{event},0); ($cmd,$err)=ParseCommandsDoIf($hash,$cmd,1); } DOIF_SetState ($hash,$nr,$subnr,$event,$err); if (defined $hash->{do}{$nr}{++$subnr}) { my $last_cond=ReadingsVal($pn,"cmd_nr",0)-1; if (DOIF_SetSleepTimer($hash,$last_cond,$nr,$subnr,$event,-1,undef)) { DOIF_cmd ($hash,$nr,$subnr,$event); } } else { if (defined ($sleeptimer[$nr])) { my $last_cond=ReadingsVal($pn,"cmd_nr",0)-1; if (DOIF_SetSleepTimer($hash,$last_cond,$nr,0,$event,-1,$sleeptimer[$nr])) { DOIF_cmd ($hash,$nr,$subnr,$event); } } } #delete $hash->{helper}{cur_cmd_nr}; return undef; } sub CheckiTimerDoIf($$$) { my ($device,$itimer,$eventa)=@_; my $max = int(@{$eventa}); my $found; return 1 if ($itimer =~ /\[$device\]/); for (my $j = 0; $j < $max; $j++) { if ($eventa->[$j] =~ "^(.+): ") { $found = ($itimer =~ /\[$device:$1[:\]]/); if ($found) { return 1; } } } return 0; } sub CheckReadingDoIf($$) { my ($readings,$eventa)=@_; my $max = int(@{$eventa}); my $s; my $found=0; my $device; my $reading; if (!defined $readings) { return 1; } foreach my $item (split(/ /,$readings)) { ($device,$reading)=(split(":",$item)); if (defined $reading) { for (my $j = 0; $j < $max; $j++) { $s = $eventa->[$j]; $s = "" if(!defined($s)); $found = ($s =~ m/^$reading: /); if ($found) { return 1; } } } } return 0; } sub CheckRegexpDoIf($$$$) { my ($hash,$name,$eventa,$condition)=@_; my $cond=($condition == -1) ? "all" : $condition; my $max_regexp=keys %{$hash->{regexp}{$cond}}; my $c; my $nameExp; my $notifyExp; for (my $i=0; $i<$max_regexp;$i++) { if ($hash->{regexp}{$cond}{$i} =~ /([^\:]*):(.*)/) { $nameExp=$1; $notifyExp=$2; } else { $nameExp=$hash->{regexp}{$cond}{$i}; } $nameExp="" if (!$nameExp); $notifyExp="" if (!$notifyExp); if ($nameExp eq "" or $name =~ /$nameExp/) { #my $eventa = $hash->{helper}{triggerEvents}; my $events=""; if ($eventa) { $events=join(",",@{$eventa}); } if ($notifyExp eq "") { if ($cond ne "all") { $c=$cond+1; readingsSingleUpdate ($hash, "matched_event_c".$c."_".($i+1),"$events",0); } return 1; } my $max = int(@{$eventa}); my $s; my $found; for (my $j = 0; $j < $max; $j++) { $s = $eventa->[$j]; $s = "" if(!defined($s)); $found = ($s =~ m/$notifyExp/); if ($found) { if ($cond ne "all") { $c=$cond+1; readingsSingleUpdate ($hash, "matched_event_c".$c."_".($i+1),$s,0); } return 1 } } } } return 0; } sub DOIF_Trigger ($$) { my ($hash,$device)= @_; my $timerNr=-1; my $ret; my $err; my $doelse=0; my $event; my $pn=$hash->{NAME}; my $max_cond=keys %{$hash->{condition}}; my $last_cond=ReadingsVal($pn,"cmd_nr",0)-1; my $j; my @triggerEvents; for (my $i=0; $i<$max_cond;$i++) { if ($device eq "") {# timer next if (!defined ($hash->{timers}{$i})); my $found=0; foreach $j (split(" ",$hash->{timers}{$i})) { if ($hash->{timer}{$j} == 1) { $found=1; $timerNr=$j; last; } } next if (!$found); $event="timer_".($timerNr+1); @triggerEvents=($event); $hash->{helper}{triggerEvents}=\@triggerEvents; $hash->{helper}{triggerDev}=""; $hash->{helper}{event}=$event; } else { #event if (!CheckRegexpDoIf($hash, $device, $hash->{helper}{triggerEvents}, $i)) { next if (!defined ($hash->{devices}{$i})); next if ($hash->{devices}{$i} !~ / $device /); next if (AttrVal($pn, "checkReadingEvent", 0) and !CheckReadingDoIf ($hash->{readings}{$i},$hash->{helper}{triggerEventsState}) and (defined $hash->{internals}{$i} ? $hash->{internals}{$i} !~ / $device:.+ /:1)) } $event="$device"; } if (($ret,$err)=DOIF_CheckCond($hash,$i)) { if ($err) { Log3 $hash->{Name},4,"$hash->{NAME}: $err" if ($ret != -1); readingsSingleUpdate ($hash, "error", $err,0); return undef; } if ($ret) { $hash->{helper}{timerevents}=$hash->{helper}{triggerEvents}; $hash->{helper}{timereventsState}=$hash->{helper}{triggerEventsState}; $hash->{helper}{timerevent}=$hash->{helper}{event}; $hash->{helper}{timerdev}=$hash->{helper}{triggerDev}; if (DOIF_SetSleepTimer($hash,$last_cond,$i,0,$device,$timerNr,undef)) { DOIF_cmd ($hash,$i,0,$event); return 1; } else { return undef; } } else { $doelse = 1; } } } if ($doelse) { #DOELSE if (defined ($hash->{do}{$max_cond}{0}) or ($max_cond == 1 and !(AttrVal($pn,"do","") or AttrVal($pn,"repeatsame","")))) { #DOELSE $hash->{helper}{timerevents}=$hash->{helper}{triggerEvents}; $hash->{helper}{timereventsState}=$hash->{helper}{triggerEventsState}; $hash->{helper}{timerevent}=$hash->{helper}{event}; $hash->{helper}{timerdev}=$hash->{helper}{triggerDev}; if (DOIF_SetSleepTimer($hash,$last_cond,$max_cond,0,$device,$timerNr,undef)) { DOIF_cmd ($hash,$max_cond,0,$event) ; return 1; } } } return undef; } sub DOIF_Notify($$) { my ($hash, $dev) = @_; my $pn = $hash->{NAME}; return "" if($attr{$pn} && $attr{$pn}{disable}); return "" if (!$dev->{NAME}); my $device; my $reading; my $internal; my $ret; my $err; my $eventa; my $eventas; $eventa = deviceEvents($dev, AttrVal($pn, "addStateEvent", 0)); $eventas = deviceEvents($dev, 1); if ($dev->{NAME} eq "global" and (EventCheckDoif($dev->{NAME},"global",$eventa,"INITIALIZED") or EventCheckDoif($dev->{NAME},"global",$eventa,"REREADCFG"))) { $hash->{helper}{globalinit}=1; if ($hash->{helper}{last_timer} > 0){ for (my $j=0; $j<$hash->{helper}{last_timer};$j++) { DOIF_SetTimer($hash,"DOIF_TimerTrigger",$j); } } } if (($hash->{itimer}{all}) and $hash->{itimer}{all} =~ / $dev->{NAME} /) { for (my $j=0; $j<$hash->{helper}{last_timer};$j++) { if (AttrVal($pn, "checkReadingEvent", 0) and CheckiTimerDoIf ($dev->{NAME},$hash->{time}{$j},$eventas) or !AttrVal($pn, "checkReadingEvent", 0) and $hash->{time}{$j} =~ /\[$dev->{NAME}\]|\[$dev->{NAME}:/) { DOIF_SetTimer($hash,"DOIF_TimerTrigger",$j); } } } return "" if (ReadingsVal($pn,"mode","") eq "disabled"); return "" if (!$hash->{helper}{globalinit}); return "" if (defined $hash->{helper}{cur_cmd_nr}); return "" if (!$hash->{devices}{all} and !$hash->{state}{device} and !$hash->{regexp}{all}); if ((($hash->{devices}{all}) and $hash->{devices}{all} =~ / $dev->{NAME} /) or CheckRegexpDoIf($hash,$dev->{NAME},$eventa,-1)){ $hash->{helper}{cur_cmd_nr}="Trigger $dev->{NAME}" if (AttrVal($hash->{NAME},"selftrigger","") ne "all"); readingsSingleUpdate ($hash, "Device",$dev->{NAME},0); #my $events = deviceEvents($dev, AttrVal($dev->{NAME}, "addStateEvent", 0)); #readingsSingleUpdate ($hash, "Event","@{$events}",0); if ($hash->{readings}{all}) { foreach my $item (split(/ /,$hash->{readings}{all})) { ($device,$reading)=(split(":",$item)); if ($item and $device eq $dev->{NAME} and defined ($defs{$device}{READINGS}{$reading})) { if (!AttrVal($pn, "checkReadingEvent", 0) or CheckReadingDoIf ("$item",$eventas)) { readingsSingleUpdate ($hash, "e_".$dev->{NAME}."_".$reading,$defs{$device}{READINGS}{$reading}{VAL},0); } } } } if ($hash->{internals}{all}) { foreach my $item (split(/ /,$hash->{internals}{all})) { ($device,$internal)=(split(":",$item)); readingsSingleUpdate ($hash, "e_".$dev->{NAME}."_".$internal,$defs{$device}{$internal},0) if ($item and $device eq $dev->{NAME} and defined ($defs{$device}{$internal})); } } if ($hash->{trigger}{all}) { foreach my $item (split(/ /,$hash->{trigger}{all})) { readingsSingleUpdate ($hash, "e_".$dev->{NAME}."_events",join(",",@{$eventa}),0); } } $hash->{helper}{triggerEvents}=$eventa; $hash->{helper}{triggerEventsState}=$eventas; $hash->{helper}{triggerDev}=$dev->{NAME}; $hash->{helper}{event}=join(",",@{$eventa}); $ret=DOIF_Trigger($hash,$dev->{NAME}); } if (($hash->{state}{device}) and $hash->{state}{device} =~ / $dev->{NAME} / and !$ret) { $hash->{helper}{cur_cmd_nr}="Trigger $dev->{NAME}" if (AttrVal($hash->{NAME},"selftrigger","") ne "all"); $hash->{helper}{triggerEvents}=$eventa; $hash->{helper}{triggerEventsState}=$eventas; $hash->{helper}{triggerDev}=$dev->{NAME}; $hash->{helper}{event}=join(",",@{$eventa}); DOIF_SetState($hash,"",0,"",""); } delete $hash->{helper}{cur_cmd_nr}; return undef; } sub DOIF_TimerTrigger ($) { my ($timer)=@_; my $hash=${$timer}->{hash}; my $pn = $hash->{NAME}; my $localtime=${$timer}->{localtime}; delete $hash->{triggertime}{$localtime}; my $ret; $hash->{helper}{cur_cmd_nr}="timer $localtime" if (AttrVal($hash->{NAME},"selftrigger","") ne "all"); #$hash->{helper}{cur_cmd_nr}="timer $localtime"; for (my $j=0; $j<$hash->{helper}{last_timer};$j++) { if ($hash->{localtime}{$j} == $localtime) { $hash->{timer}{$j}=1; } } $ret=DOIF_Trigger ($hash,"") if (ReadingsVal($pn,"mode","") ne "disabled"); for (my $j=0; $j<$hash->{helper}{last_timer};$j++) { if ($hash->{timer}{$j} == 1) { $hash->{timer}{$j}=0; if (!AttrVal($hash->{NAME},"disable","")) { if (defined ($hash->{interval}{$j})) { if ($hash->{interval}{$j} != -1) { DOIF_SetTimer($hash,"DOIF_TimerTrigger",$hash->{interval}{$j}) ; DOIF_SetTimer($hash,"DOIF_TimerTrigger",$j) ; } } else { DOIF_SetTimer($hash,"DOIF_TimerTrigger",$j) ; } } } } delete ($hash->{helper}{cur_cmd_nr}); return undef; #return($ret); } sub DOIF_DetTime($) { my ($timeStr) = @_; my $rel=0; my $align; my $hr=0; my $err; my $h=0; my $m=0; my $s=0; my $fn; if (substr($timeStr,0,1) eq "+") { $timeStr=substr($timeStr,1); $rel=1; } my ($now, $microseconds) = gettimeofday(); my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($now); if($timeStr =~ m/^\[([0-9]+)\]:([0-5][0-9])$/) { $hr=$1; $rel=0; $align=$2; } elsif ($timeStr =~ m/^:([0-5][0-9])$/) { $align=$1; } elsif ($timeStr =~ m/^(\-?([0-9]+))$/) { $s=$1; } else { if ($timeStr =~ m/^\$hms$/) { $timeStr = sprintf("%02d:%02d:%02d", $hour, $min, $sec); } elsif ($timeStr =~ m/^\$hm$/) { $timeStr = sprintf("%02d:%02d", $hour, $min); } ($err, $h, $m, $s, $fn) = GetTimeSpec($timeStr); return $err if ($err); } if (defined ($align)) { if ($rel) { if ($align > 0) { $m = (int($min/$align)+1)*$align; if ($m>=60) { $h = $hour+1; $m = 0; } else { $h = $hour; } } $rel=0; } else { $m=$align; if ($hr > 1) { $h = (int($hour/$hr)+1)*$hr; $h = 0 if ($h >=24); } else { if ($m <= $min) { $h = $hour+1; } else { $h = $hour; } } } } my $second = $h*3600+$m*60+$s; if ($second == 0 and $rel) { $err = "null is not allowed on a relative time"; } return ($err, ($rel and !defined ($align)), $second); } sub DOIF_CalcTime($$) { my ($hash,$block)= @_; my $tailBlock; my $beginning; my $err; my $cmd=""; my $rel=""; my $relGlobal=0; my $reading; my $internal; my $device; my $pos; my $ret; if ($block=~ m/^\+\[([0-9]+)\]:([0-5][0-9])$/) { ($err,$rel,$block)=DOIF_DetTime($block); return ($block,$err,$rel); } elsif ($block =~ /^\+\(/ or $block =~ /^\+\[/) { $relGlobal=1; #$pos=pos($block); $block=substr($block,1); } if ($block =~ /^\(/) { ($beginning,$tailBlock,$err,$tailBlock)=GetBlockDoIf($block,'[\(\)]'); return ($tailBlock,$err) if ($err); } else { if ($block =~ /^\[/) { ($beginning,$block,$err,$tailBlock)=GetBlockDoIf($block,'[\[\]]'); return ($block,$err) if ($err); ($block,$err,$device,$reading,$internal)=ReplaceReadingEvalDoIf($hash,$block,1); return ($block,$err) if ($err); } ($err,$rel,$block)=DOIF_DetTime($block); $rel=1 if ($relGlobal); return ($block,$err,$rel); } $tailBlock=$block; while ($tailBlock ne "") { ($beginning,$block,$err,$tailBlock)=GetBlockDoIf($tailBlock,'[\[\]]'); return ($block,$err) if ($err); if ($block ne "") { if ($block =~ /^\??[a-z0-9._]*[a-z._]+[a-z0-9._]*($|:.+$)/i) { ($block,$err,$device,$reading,$internal)=ReplaceReadingEvalDoIf($hash,$block,1); return ($block,$err) if ($err); } ($err,$rel,$block)=DOIF_DetTime($block); return ($block,$err) if ($err); } $cmd.=$beginning.$block; } $tailBlock=$cmd; $cmd=""; while ($tailBlock ne "") { ($beginning,$block,$err,$tailBlock)=GetBlockDoIf($tailBlock,'[\{\}]'); return ($block,$err) if ($err); if ($block ne "") { $ret = eval $block; return($block." ",$@) if ($@); $block=$ret; ($err,$rel,$block)=DOIF_DetTime($block); return ($block,$err) if ($err); } $cmd.=$beginning.$block; } $ret = eval $cmd; return($cmd." ",$@) if ($@); return ($ret,"null is not allowed on a relative time",$relGlobal) if ($ret == 0 and $relGlobal); return ($ret,"",$relGlobal); } sub DOIF_SetTimer($$$) { my ($hash, $func, $nr) = @_; my $timeStr=$hash->{time}{$nr}; my $cond=$hash->{timeCond}{$nr}; my $next_time; my ($second,$err, $rel)=DOIF_CalcTime($hash,$timeStr); if ($err) { readingsSingleUpdate ($hash,"timer_".($nr+1)."_c".($cond+1),"error: ".$err,AttrVal($hash->{NAME},"timerevent","")?1:0); Log3 $hash->{NAME},4 , "$hash->{NAME} timer_".($nr+1)."_c".($cond+1)." error: ".$err; #RemoveInternalTimer($timer); $hash->{realtime}{$nr}="00:00:00"; return $err; } if ($second < 0 and $rel) { readingsSingleUpdate ($hash,"timer_".($nr+1)."_c".($cond+1),"time offset: $second, negativ offset is not allowed",AttrVal($hash->{NAME},"timerevent","")?1:0); return("timer_".($nr+1)."_c".($cond+1),"time offset: $second, negativ offset is not allowed"); } my ($now, $microseconds) = gettimeofday(); my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($now); my $isdst_now=$isdst; my $sec_today = $hour*3600+$min*60+$sec; my $midnight = $now-$sec_today; if ($rel) { $next_time =$now+$second; } else { $next_time = $midnight+$second; } if ($second <= $sec_today and !$rel) { $next_time+=86400; ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($next_time); if ($isdst_now != $isdst) { if ($isdst_now == 1) { $next_time+=3600 if ($isdst == 0); } else { $next_time-=3600 if ($second>=3*3600 or $second <= $sec_today and $second<2*3600); } } } #if ($next_time < $now and $isdst_now == $isdst) { # readingsSingleUpdate ($hash,"timer_".($nr+1)."_c".($cond+1),"back to the past is not allowed",AttrVal($hash->{NAME},"timerevent","")?1:0); # return("timer_".($nr+1)."_c".($cond+1),"back to the past is not allowed"); #} else { my $next_time_str=strftime("%d.%m.%Y %H:%M:%S",localtime($next_time)); $next_time_str.="\|".$hash->{days}{$nr} if (defined ($hash->{days}{$nr})); readingsSingleUpdate ($hash,"timer_".($nr+1)."_c".($cond+1),$next_time_str,AttrVal($hash->{NAME},"timerevent","")?1:0); $hash->{realtime}{$nr}=strftime("%H:%M:%S",localtime($next_time)); if (defined ($hash->{localtime}{$nr})) { my $old_lt=$hash->{localtime}{$nr}; my $found=0; delete ($hash->{localtime}{$nr}); foreach my $lt (keys %{$hash->{localtime}}) { if ($hash->{localtime}{$lt} == $old_lt) { $found=1; last; } } if (!$found) { RemoveInternalTimer(\$hash->{triggertime}{$old_lt}); delete ($hash->{triggertime}{$old_lt}); } } $hash->{localtime}{$nr}=$next_time; if (!defined ($hash->{triggertime}{$next_time})) { $hash->{triggertime}{$next_time}{hash}=$hash; $hash->{triggertime}{$next_time}{localtime}=$next_time; InternalTimer($next_time, $func, \$hash->{triggertime}{$next_time}, 0); } return undef; } sub DOIF_SetSleepTimer($$$$$$$) { my ($hash,$last_cond,$nr,$subnr,$device,$timerNr,$repeatcmd)=@_; my $pn = $hash->{NAME}; my $sleeptimer=$hash->{helper}{sleeptimer}; my @waitdel=SplitDoIf(':',AttrVal($pn,"waitdel","")); my @waitdelsubnr=SplitDoIf(',',defined $waitdel[$sleeptimer] ? $waitdel[$sleeptimer] : ""); my $err; if ($sleeptimer != -1 and (($sleeptimer != $nr or AttrVal($pn,"do","") eq "resetwait") or ($sleeptimer == $nr and $waitdelsubnr[$subnr]))) { RemoveInternalTimer($hash); #delete ($defs{$hash->{NAME}}{READINGS}{wait_timer}); readingsSingleUpdate ($hash, "wait_timer", "no timer",1); $hash->{helper}{sleeptimer}=-1; $subnr=$hash->{helper}{sleepsubtimer} if ($hash->{helper}{sleepsubtimer}!=-1 and $sleeptimer == $nr); return 0 if ($sleeptimer == $nr and $waitdelsubnr[$subnr]); } if ($timerNr >= 0 and !AttrVal($pn,"timerWithWait","")) {#Timer if ($last_cond != $nr or AttrVal($pn,"do","") eq "always" or AttrVal($pn,"repeatsame","")) { return 1; } else { return 0; } } if ($hash->{helper}{sleeptimer} == -1 and ($last_cond != $nr or $subnr > 0 or AttrVal($pn,"do","") eq "always" or AttrVal($pn,"do","") eq "resetwait" or AttrVal($pn,"repeatsame","") or defined($repeatcmd))) { my $sleeptime=0; if (defined ($repeatcmd)) { $sleeptime=$repeatcmd; } else { my @sleeptimer=SplitDoIf(':',AttrVal($pn,"wait","")); if ($waitdelsubnr[$subnr]) { $sleeptime = $waitdelsubnr[$subnr]; } else { my @sleepsubtimer=SplitDoIf(',',defined $sleeptimer[$nr]? $sleeptimer[$nr]: ""); if ($sleepsubtimer[$subnr]) { $sleeptime=$sleepsubtimer[$subnr]; } } } $sleeptime=EvalValueDoIf($hash,"wait",$sleeptime); if ($sleeptime) { my $seconds = gettimeofday(); my $next_time = $seconds+$sleeptime; $hash->{helper}{sleeptimer}=$nr; $hash->{helper}{sleepsubtimer}=$subnr; $device="timer_".($timerNr+1) if ($timerNr >= 0); $hash->{helper}{sleepdevice}=$device; my $cmd_nr=$nr+1; if (defined $hash->{do}{$nr}{1}) { my $cmd_subnr=$subnr+1; readingsSingleUpdate ($hash,"wait_timer",strftime("%d.%m.%Y %H:%M:%S cmd_$cmd_nr"."_$cmd_subnr $device",localtime($next_time)),1); } else { readingsSingleUpdate ($hash,"wait_timer",strftime("%d.%m.%Y %H:%M:%S cmd_$cmd_nr $device",localtime($next_time)),1); } InternalTimer($next_time, "DOIF_SleepTrigger",$hash, 0); return 0; } elsif (defined($repeatcmd)){ return 0; } else { return 1; } } else { return 0; } } sub DOIF_SleepTrigger ($) { my ($hash)=@_; my $sleeptimer=$hash->{helper}{sleeptimer}; my $sleepsubtimer=$hash->{helper}{sleepsubtimer}; $hash->{helper}{sleeptimer}=-1; $hash->{helper}{sleepsubtimer}=-1; my $pn = $hash->{NAME}; $hash->{helper}{cur_cmd_nr}="wait_timer" if (!AttrVal($hash->{NAME},"selftrigger","")); $hash->{helper}{triggerEvents}=$hash->{helper}{timerevents}; $hash->{helper}{triggerEventsState}=$hash->{helper}{timereventsState}; $hash->{helper}{event}=$hash->{helper}{timerevent}; $hash->{helper}{triggerDev}=$hash->{helper}{timerdev}; readingsSingleUpdate ($hash, "wait_timer", "no timer",1); # if (!AttrVal($hash->{NAME},"disable","")) { if (ReadingsVal($pn,"mode","") ne "disabled") { DOIF_cmd ($hash,$sleeptimer,$sleepsubtimer,$hash->{helper}{sleepdevice}); } delete $hash->{helper}{cur_cmd_nr}; return undef; } ############################# sub CmdDoIf($$) { my ($hash, $tail) = @_; my $cond=""; my $err=""; my $if_cmd=""; my $if_cmd_ori=""; my $else_cmd=""; my $else_cmd_ori=""; my $tailBlock; my $eval=""; my $beginning; my $i=0; my $j=0; my $last_do; if (!$tail) { $tail=""; } else { $tail =~ s/(##.*\n)|(##.*$)|\n/ /g; $tail =~ s/\$SELF/$hash->{NAME}/g; } # if (defined $hash->{helper}) #def modify if ($init_done) { DOIF_delTimer($hash); DOIF_delAll ($hash); readingsBeginUpdate($hash); readingsBulkUpdate($hash,"cmd",0); readingsBulkUpdate($hash,"state","initialized"); readingsEndUpdate($hash, 1); $hash->{helper}{globalinit}=1; } #$hash->{STATE} = 'initialized'; $hash->{helper}{last_timer}=0; $hash->{helper}{sleeptimer}=-1; # if ($init_done) { # $hash->{helper}{globalinit}=1; # } while ($tail ne "") { return($tail, "no left bracket of condition") if ($tail !~ /^ *\(/); #condition ($beginning,$cond,$err,$tail)=GetBlockDoIf($tail,'[\(\)]'); return ($cond,$err) if ($err); ($cond,$err)=ReplaceAllReadingsDoIf($hash,$cond,$i,0); return ($cond,$err) if ($err); return ($tail,"no condition") if ($cond eq ""); $hash->{condition}{$i}=$cond; #DOIF $if_cmd_ori=""; $j=0; while ($tail =~ /^\s*\(/) { ($beginning,$if_cmd_ori,$err,$tail)=GetBlockDoIf($tail,'[\(\)]'); return ($if_cmd_ori,$err) if ($err); ($if_cmd,$err)=ParseCommandsDoIf($hash,$if_cmd_ori,0); return ($if_cmd,$err) if ($err); #return ($tail,"no commands") if ($if_cmd eq ""); $hash->{do}{$i}{$j++}=$if_cmd_ori; } $hash->{do}{$i}{0}=$if_cmd_ori if ($j==0); #do without brackets $last_do=$i; $tail =~ s/^\s*$//g; if (length($tail)) { $tail =~ /^\s*DOELSEIF/g; if (pos($tail)) { $tail=substr($tail,pos($tail)); if (!length($tail)) { return ($tail,"no DOELSEIF block"); } } else { last if ($tail =~ /^\s*DOELSE/); return ($tail,"expected DOELSEIF or DOELSE"); } } $i++; } #DOELSE if (length($tail)) { $tail =~ /^\s*DOELSE/g; if (pos($tail)) { $tail=substr($tail,pos($tail)); } else { return ($tail,"expected DOELSE"); } $j=0; while ($tail =~ /^\s*\(/) { ($beginning,$else_cmd_ori,$err,$tail)=GetBlockDoIf($tail,'[\(\)]'); return ($else_cmd_ori,$err) if ($err); ($else_cmd,$err)=ParseCommandsDoIf($hash,$else_cmd_ori,0); return ($else_cmd,$err) if ($err); $hash->{do}{$last_do+1}{$j++}=$else_cmd_ori; } $hash->{do}{$last_do+1}{0}=$else_cmd_ori if ($j==0); #doelse without brackets } return("","") } sub DOIF_Define($$$) { my ($hash, $def) = @_; my ($name, $type, $cmd) = split(/[\s]+/, $def, 3); return undef if (AttrVal($hash->{NAME},"disable","")); my ($msg,$err)=CmdDoIf($hash,$cmd); if ($err ne "") { $msg=$cmd if (!$msg); my $errmsg="$name $type: $err: $msg"; return $errmsg; } else { return undef; } } ################################# sub DOIF_Attr(@) { my @a = @_; my $hash = $defs{$a[1]}; my $ret=""; if (($a[0] eq "set" and $a[2] eq "disable" and ($a[3] eq "0")) or (($a[0] eq "del" and $a[2] eq "disable"))) { my $cmd = $defs{$hash->{NAME}}{DEF}; my ($msg,$err)=CmdDoIf($hash,$cmd); if ($err ne "") { $msg=$cmd if (!$msg); return ("$err: $msg"); } } elsif($a[0] eq "set" and $a[2] eq "disable" and $a[3] eq "1") { DOIF_delTimer($hash); DOIF_delAll ($hash); readingsSingleUpdate ($hash,"state","deactivated",1); } elsif($a[0] eq "set" && $a[2] eq "state") { delete ($hash->{state}{device}); my ($block,$err)=ReplaceAllReadingsDoIf($hash,$a[3],-2,0); return $err if ($err); } elsif($a[0] eq "set" && $a[2] eq "wait") { RemoveInternalTimer($hash); #delete ($defs{$hash->{NAME}}{READINGS}{wait_timer}); readingsSingleUpdate ($hash, "wait_timer", "no timer",1); $hash->{helper}{sleeptimer}=-1; } elsif($a[0] eq "set" && $a[2] eq "initialize") { readingsBeginUpdate($hash); readingsBulkUpdate ($hash,"state",$a[3]); readingsBulkUpdate ($hash,"cmd_nr","0"); readingsBulkUpdate ($hash,"cmd",0); readingsEndUpdate($hash, 1); } elsif($a[0] eq "del" && $a[2] eq "repeatsame") { delete ($defs{$hash->{NAME}}{READINGS}{cmd_count}); } elsif($a[0] eq "del" && $a[2] eq "waitsame") { delete ($defs{$hash->{NAME}}{READINGS}{waitsame}); } return undef; } sub DOIF_Undef { my ($hash, $name) = @_; $hash->{DELETED} = 1; DOIF_delTimer($hash); return undef; } sub DOIF_Set($@) { my ($hash, @a) = @_; my $pn = $hash->{NAME}; my $arg = $a[1]; my $value = (defined $a[2]) ? $a[2] : ""; my $ret=""; if ($arg eq "disable" or $arg eq "initialize") { if (AttrVal($hash->{NAME},"disable","")) { return ("modul ist deactivated by disable attribut, delete disable attribut first"); } } if ($arg eq "disable") { readingsBeginUpdate ($hash); readingsBulkUpdate($hash, "state", "disabled"); readingsBulkUpdate($hash, "mode", "disabled"); readingsEndUpdate ($hash, 1); } elsif ($arg eq "initialize" ) { delete ($defs{$hash->{NAME}}{READINGS}{mode}); delete ($defs{$hash->{NAME}}{READINGS}{cmd_nr}); delete ($defs{$hash->{NAME}}{READINGS}{cmd_event}); readingsSingleUpdate($hash, "state","initialize",1); } else { return "$pn: unknown argument $a[1], choose one of disable initialize" } return $ret; } 1; =pod =begin html

DOIF

=end html =begin html_DE

DOIF

=end html_DE =cut