############################################# # $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}); foreach my $key (keys %{$defs{$hash->{NAME}}{READINGS}}) { delete $defs{$hash->{NAME}}{READINGS}{$key} if ($key =~ "^(Device|state|error|warning|cmd|e_|timer_|wait_|matched_|last_cmd|mode)"); } } ######################### 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 checkall:event,timer,all weekdays setList:textField-long readingList ".$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 "" if (!defined($value) or $value eq ""); my $err=""; my $pn=$hash->{NAME}; $value =~ s/\$SELF/$pn/g; ($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 AggrIntDoIf { my ($hash,$modeType,$device,$reading,$cond,$default)=@_; my $num=0; my $value=""; my $sum=0; my $average; my $extrem; my $name; my $devname; my $err; my $ret; my $result; my @devices; my $group; my $room; my $STATE; my $TYPE; my $warning=0; my $mode=substr($modeType,0,1); my $type; my $format; my $place; my $number; if ($modeType =~ /.(sum|average|max|min)?[:]?(?:(a|d)?(\d)?)?/) { $type = (defined $1)? $1 : ""; $format= (defined $2)? $2 : ""; $place= $3; } if (defined $default) { if ($default =~ /^"(.*)"$/) { $default = $1; } else { $default=EvalValueDoIf($hash,"default",$default); } } foreach my $name (($device eq "") ? keys %defs:grep {/$device/} keys %defs) { next if($attr{$name} && $attr{$name}{ignore}); $value=""; $number=""; if ($reading) { if (defined $defs{$name}{READINGS}{$reading}) { $value=$defs{$name}{READINGS}{$reading}{VAL}; $number = ($value =~ /(-?\d+(\.\d+)?)/ ? $1 : 0); } else { next; } } if ($cond) { if ($cond =~ /^"(.*)"$/) { if (defined $defs{$name}{READINGS}{$reading}) { $ret=($value =~ /$1/); } } else { $_=$value; $STATE=Value($name); $TYPE=$defs{$name}{TYPE}; $group=AttrVal($name,"group",""); $room=AttrVal($name,"room",""); $lastWarningMsg=""; $ret = eval $cond; if ($@) { $@ =~ s/^(.*) at \(eval.*\)(.*)$/$1,$2/; if (defined $hash) { Log3 ($hash->{NAME},3 , "$hash->{NAME}: aggregate function: error in condition: $cond, $@"); } return("error in aggregate function: ".$@); } if ($lastWarningMsg) { $warning=1; $lastWarningMsg =~ s/^(.*) at \(eval.*$/$1/; Log3 ($hash->{NAME},3 , "$hash->{NAME}: aggregate function: warning in condition: $cond, Device: $name"); readingsSingleUpdate ($hash, "warning_aggr", "condition: $cond , device: $name, $lastWarningMsg",0); } $lastWarningMsg=""; } } else { $ret=1; } if ($format eq "a") { $devname=AttrVal($name,"alias",$name); } else { $devname=$name; } if ($ret) { if ($type eq ""){ $num++; push (@devices,$devname); } elsif (defined $value) { if ($type eq "sum" or $type eq "average") { $num++; push (@devices,$devname); $sum+=$number; } elsif ($type eq "max") { if (!defined $extrem or $number>$extrem) { $extrem=$number; @devices=($devname); } } elsif ($type eq "min") { if (!defined $extrem or $number<$extrem) { $extrem=$number; @devices=($devname); } } } } } delete ($defs{$hash->{NAME}}{READINGS}{warning_aggr}) if (defined $hash and $warning==0); if ($type eq "max" or $type eq "min") { $extrem=0 if (!defined $extrem); $result=$extrem; } elsif ($type eq "sum") { $result= $sum; } elsif ($type eq "average") { if ($num>0) { $result=($sum/$num) } } else { $result=$num; } if ($mode eq "#") { if ($format eq "d") { $result = ($result =~ /(-?\d+(\.\d+)?)/ ? $1 : 0); $result = round ($result,$place) if (defined $place); } if ($num==0 and defined $default) { return ($default); } else { return ($result); } } elsif ($mode eq "@") { if ($num==0 and defined $default) { @devices =($default); } return (sort @devices); } return 0; } sub AggrDoIf { my ($modeType,$device,$reading,$cond,$default)=@_; return (AggrIntDoIf(undef,$modeType,$device,$reading,$cond,$default)); } sub AggregateDoIf { my ($hash,$modeType,$device,$reading,$cond,$default)=@_; my $mode=substr($modeType,0,1); my $type=substr($modeType,1); my $splittoken=","; if ($modeType =~ /.(?:sum|average|max|min)?[:]?[^s]*(?:s\((.*)\))?/) { $splittoken=$1 if (defined $1); } if ($mode eq "#") { return (AggrIntDoIf($hash,$modeType,$device,$reading,$cond,$default)); } elsif ($mode eq "@") { return (join ($splittoken,AggrIntDoIf($hash,$modeType,$device,$reading,$cond,$default))); } return (""); } sub EventDoIf { my ($n,$hash,$NotifyExp,$check,$filter,$output,$default)=@_; my $dev=$hash->{helper}{triggerDev}; my $eventa=$hash->{helper}{triggerEvents}; if ($check) { if ($dev ne $n) { if (defined $filter) { return ($default) } else { return 0; } } } else { if ($n and $dev !~ /$n/) { if (defined $filter) { return ($default) } else { return 0; } } } return 0 if(!$eventa); my $max = int(@{$eventa}); my $ret = 0; if ($NotifyExp eq "") { return 1 if (!defined $filter); } my $s; my $found; my $element; for (my $i = 0; $i < $max; $i++) { $s = $eventa->[$i]; $s = "" if(!defined($s)); $found = ($s =~ m/$NotifyExp/); if ($found or $NotifyExp eq "") { $hash->{helper}{event}=$s; if (defined $filter) { $element = ($s =~ /$filter/) ? $1 : ""; if ($element) { if ($output ne "") { $element= eval $output; if ($@) { Log3 ($hash->{NAME},4 , "$hash->{NAME}: $@"); readingsSingleUpdate ($hash, "error", $@,0); return(undef); } } return ($element); } } else { 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$/); } if (defined $filter) { return ($default); } else { return 0; } } sub InternalDoIf { my ($hash,$name,$internal,$default,$regExp,$output)=@_; $default=AttrVal($hash->{NAME},'notexist','') if (!defined $default); $regExp='' if (!defined $regExp); $output='' if (!defined $output); if ($default =~ /^"(.*)"$/) { $default = $1; } else { $default=EvalValueDoIf($hash,"default",$default); } my $r=""; my $element; return ($default) if (!defined $defs{$name}); return ($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,$default,$regExp,$output)=@_; $default=AttrVal($hash->{NAME},'notexist','') if (!defined $default); $output='' if (!defined $output); $regExp='' if (!defined $regExp); if ($default =~ /^"(.*)"$/) { $default = $1; } else { $default=EvalValueDoIf($hash,"default",$default); } my $r; my $element; return ($default) if (!defined $defs{$name}); return ($default) if (!defined $defs{$name}{READINGS}); return ($default) if (!defined $defs{$name}{READINGS}{$reading}); $r=$defs{$name}{READINGS}{$reading}{VAL}; $r="" if (!defined($r)); if ($regExp) { if ($regExp =~ /^d(\d)?/) { my $round=$1; $r = ($r =~ /(-?\d+(\.\d+)?)/ ? $1 : 0); $r = round ($r,$round) if (defined $round); $regExp="(.*)"; } "" =~ /()()()()()()()()()/; #reset $1, $2... $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 "(") { $eval=$1 if ($eval =~/^\((.*)\)$/); my $ret = eval $eval; return($eval." ",$@) if ($@); $eval=$ret; } else { $eval="{".$eval."}"; } } $cmd.=$beginning.$eval; } return ($cmd,""); } sub ReplaceAggregateDoIf($$$) { my ($hash,$block,$condition) = @_; my $exp; my $nameExp; my $notifyExp; my $match; my $reading; my $aggrType; my $default; ($block,$default)=SplitDoIf(",",$block); if ($block =~ /^([^"]*)(.*)/) { $aggrType=$1; $block=$2; } ($exp,$reading,$match)=SplitDoIf(":",$block); if ($exp =~ /^"(.*)"/){ $exp=$1; if ($exp =~ /([^\:]*):(.*)/) { $nameExp=$1; $notifyExp=$2; } else { $nameExp=$exp; } } $nameExp="" if (!defined $nameExp); $notifyExp="" if (!defined $notifyExp); if (defined $default) { $match="" if (!defined $match); $block="AggregateDoIf(".'$hash'.",'$aggrType','$nameExp','$reading','$match','$default')"; } elsif (defined $match) { $block="AggregateDoIf(".'$hash'.",'$aggrType','$nameExp','$reading','$match')"; } elsif (defined $reading) { $block="AggregateDoIf(".'$hash'.",'$aggrType','$nameExp','$reading')"; } else { $block="AggregateDoIf(".'$hash'.",'$aggrType','$nameExp')"; } if (!($condition >= 0)) { my $ret = eval $block; return($block." ",$@) if ($@); $block=$ret; } return ($block,undef); } sub ReplaceEventDoIf($) { my ($block) = @_; my $exp; my $exp2; my $nameExp; my $notifyExp; my $default; my $filter; my $output; ($exp,$default)=SplitDoIf(",",$block); ($exp2,$filter,$output)=SplitDoIf(":",$exp); if ($exp2 =~ /^"(.*)"/){ $exp2=$1; if ($exp2 =~ /([^\:]*):(.*)/) { $nameExp=$1; $notifyExp=$2; } else { $nameExp=$exp2; } } $nameExp="" if (!defined $nameExp); $notifyExp="" if (!defined $notifyExp); $output="" if (!defined $output); if (defined $default) { if ($default =~ /"(.*)"/) { $default = $1; } if (defined $filter) { if ($filter =~ /"(.*)"/) { $filter=$1; } else { return ($filter,"wrong filter Regex") } } else { $filter='[^\:]*: (.*)'; } } else { if (defined $filter) { return ($block,"default value must be defined") } else { $block="EventDoIf('$nameExp',".'$hash,'."'$notifyExp',0)"; return ($block,undef); } } $block="EventDoIf('$nameExp',".'$hash,'."'$notifyExp',0,'$filter','$output','$default')"; return ($block,undef); } sub ReplaceReadingDoIf($) { my ($element) = @_; my $beginning; my $tailBlock; my $err; my $regExp=""; my $name; my $reading; my $format; my $output=""; my $exp; my $default; my $param=""; ($exp,$default)=SplitDoIf(",",$element); $default="" if (!defined($default)); my $internal=""; my $notifyExp=""; if ($exp =~ /^([^:]*):(".*")/) { $name=$1; $reading=$2; } elsif ($exp =~ /^([^:]*)(?::([^:]*)(?::(.*))?)?/) { $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 =$1; $output=$2; } else { return($format,"unknown expression format"); } } $output="" if (!defined($output)); if ($output) { $param=",'$default','$regExp','$output'"; } elsif ($regExp) { $param=",'$default','$regExp'"; } elsif ($default) { $param=",'$default'"; } if ($internal) { return("InternalDoIf(".'$hash'.",'$name','$internal'".$param.")","",$name,undef,$internal); } else { return("ReadingValDoIf(".'$hash'.",'$name','$reading'".$param.")","",$name,$reading,undef); } } else { if ($default) { $param=",'$default'"; } return("InternalDoIf(".'$hash'.",'$name','STATE'".$param.")","",$name,undef,'STATE'); } } } sub ReplaceReadingEvalDoIf($$$) { my ($hash,$element,$eval) = @_; my ($block,$err,$device,$reading,$internal)=ReplaceReadingDoIf($element); return ($block,$err) if ($err); if ($eval) { # return ("[".$element."]","") if(!$defs{$device}); # return ("[".$element."]","") if (defined ($reading) and !defined($defs{$device}{READINGS}{$reading})); # return ("[".$element."]","") 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 AddRegexpStateDoIf($$) { my ($hash,$regexp)= @_; my $max_regexp=keys %{$hash->{state}{STATE}}; for (my $i=0; $i<$max_regexp;$i++) { if ($hash->{state}{STATE}{$i} eq $regexp) { return; } } $hash->{state}{STATE}{$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; if (!defined $tailBlock) { return ("",""); } $tailBlock =~ s/\$SELF/$hash->{NAME}/g; while ($tailBlock ne "") { ($beginning,$block,$err,$tailBlock)=GetBlockDoIf($tailBlock,'[\[\]]'); return ($block,$err) if ($err); if ($block ne "") { if (substr($block,0,1) eq "?") { $block=substr($block,1); $trigger=0; } else { $trigger=1; } if ($block =~ /^(?:(?:#|@)[^"]*)"([^"]*)"/) { ($block,$err)=ReplaceAggregateDoIf($hash,$block,$condition); return ($block,$err) if ($err); if ($trigger) { if ($condition >= 0) { AddRegexpTriggerDoIf($hash,$1,$condition); $event=1; } elsif ($condition == -2) { AddRegexpStateDoIf($hash,$1); } } } elsif ($block =~ /^"([^"]*)"/) { if ($condition>=0) { ($block,$err)=ReplaceEventDoIf($block); return ($block,$err) if ($err); AddRegexpTriggerDoIf($hash,$1,$condition); $event=1; } else { $block="[".$block."]"; } } else { $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) and $trigger); $hash->{internals}{$condition} = AddItemDoIf($hash->{internals}{$condition},"$device:$internal") if (defined ($internal)); $hash->{readings}{all} = AddItemDoIf($hash->{readings}{all},"$device:$reading") if (defined ($reading) and $trigger); $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 ($currentBlock =~ /^{.*}$/) { $ret = AnalyzePerlCommand(undef,$currentBlock); } else { $ret = AnalyzeCommandChain(undef,$currentBlock); } if ($ret) { Log3 $pn,2 , "$pn: $currentBlock: $ret"; $last_error.="$currentBlock: $ret "; } } $tailBlock=substr($tailBlock,pos($tailBlock)) if ($tailBlock =~ /^\s*,/g); } return("",$last_error); } sub DOIF_weekdays($$) { my ($hash,$weekdays)=@_; my @days=SplitDoIf(',',AttrVal($hash->{NAME},"weekdays","So,Mo,Di,Mi,Do,Fr,Sa,WE,AT")); for (my $i=0;$i<@days;$i++) { $weekdays =~ s/$days[$i]/$i/; } return($weekdays); } 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) { DOIF_SetTimer($hash,"DOIF_TimerTrigger",$nr); #$err=(DOIF_SetTimer($hash,"DOIF_TimerTrigger",$nr)); #return($hash->{time}{$nr},$err) if ($err); } $hash->{timers}{$condition}.=" $nr " if ($trigger); } if ($i == 2) { if ($days eq "") { $block='DOIF_time($hash,'.$nrs[0].','.$nrs[1].',$wday,$hms)'; } else { $block='DOIF_time($hash,'.$nrs[0].','.$nrs[1].',$wday,$hms,"'.$days.'")'; } $hash->{interval}{$nrs[0]}=-1; $hash->{interval}{$nrs[1]}=$nrs[0]; } else { if ($days eq "") { $block='DOIF_time_once($hash,'.$nrs[0].',$wday)'; } else { $block='DOIF_time_once($hash,'.$nrs[0].',$wday,"'.$days.'")'; } } return ($block,""); } sub DOIF_time { my $ret=0; my ($hash,$b,$e,$wday,$hms,$days)=@_; $days="" if (!defined ($days)); return 0 if (!defined $hash->{realtime}{$b}); return 0 if (!defined $hash->{realtime}{$e}); my $begin=$hash->{realtime}{$b}; my $end=$hash->{realtime}{$e}; my $err; return 0 if ($begin eq $end); ($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; } $days=DOIF_weekdays($hash,$days); 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,$nr,$wday,$days)=@_; $days="" if (!defined ($days)); my $flag=$hash->{timer}{$nr}; 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; } $days=DOIF_weekdays($hash,$days); 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",""); $state =~ s/\$SELF/$pn/g; 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); $month++; $year+=1900; my $week=strftime ('%W', localtime($seconds)); my $hms = sprintf("%02d:%02d:%02d", $hour, $min, $sec); my $hm = sprintf("%02d:%02d", $hour, $min); my $ymd = sprintf("%02d-%02d-%02d", $year, $month,$mday); my $md = sprintf("%02d-%02d",$month,$mday); 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}); } 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++; #} } $cmdFromAnalyze="$hash->{NAME}: ".sprintf("warning in condition c%02d",($condition+1)); $lastWarningMsg=""; my $ret = eval $command; if($@){ $@ =~ s/^(.*) at \(eval.*\)(.*)$/$1,$2/; $err = sprintf("condition c%02d",($condition+1)).": $@"; $ret = 0; } if ($lastWarningMsg) { $lastWarningMsg =~ s/^(.*) at \(eval.*$/$1/; readingsSingleUpdate ($hash, "warning", sprintf("condition c%02d",($condition+1)).": $lastWarningMsg",0); } else { delete ($defs{$hash->{NAME}}{READINGS}{warning}); } $lastWarningMsg=""; $cmdFromAnalyze = undef; 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,$cond)=@_; my $type; if ($cond eq "STATE") { $type="state"; } else { $type="regexp"; } my $max_regexp=keys %{$hash->{$type}{$cond}}; my $c; my $nameExp; my $notifyExp; for (my $i=0; $i<$max_regexp;$i++) { if ($hash->{$type}{$cond}{$i} =~ /([^\:]*):(.*)/) { $nameExp=$1; $notifyExp=$2; } else { $nameExp=$hash->{$type}{$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" and $cond ne "STATE") { $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" and $cond ne "STATE") { $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="$device"; my $pn=$hash->{NAME}; my $max_cond=keys %{$hash->{condition}}; my $last_cond=ReadingsVal($pn,"cmd_nr",0)-1; my $j; my @triggerEvents; if (AttrVal($pn, "checkall", 0) =~ "1|all|timer" and $device eq "") { for ($j=0; $j<$hash->{helper}{last_timer};$j++) { if ($hash->{timer}{$j}==1) { $timerNr=$j; #first timer last; } } } for (my $i=0; $i<$max_cond;$i++) { if ($device eq "") {# timer my $found=0; if (defined ($hash->{timers}{$i})) { foreach $j (split(" ",$hash->{timers}{$i})) { if ($hash->{timer}{$j} == 1) { $found=1; $timerNr=$j; last; } } } next if (!$found and AttrVal($pn, "checkall", 0) !~ "1|all|timer"); $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)) { if (AttrVal($pn, "checkall", 0) !~ "1|all|event") { 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; # delete old timer-readings foreach my $key (keys %{$defs{$hash->{NAME}}{READINGS}}) { delete $defs{$hash->{NAME}}{READINGS}{$key} if ($key =~ "^timer_"); } if ($hash->{helper}{last_timer} > 0){ for (my $j=0; $j<$hash->{helper}{last_timer};$j++) { DOIF_SetTimer($hash,"DOIF_TimerTrigger",$j); } } if (AttrVal($pn,"initialize",0) and !AttrVal($pn,"disable",0)) { readingsBeginUpdate($hash); readingsBulkUpdate ($hash,"state",AttrVal($pn,"initialize",0)); readingsBulkUpdate ($hash,"cmd_nr","0"); readingsBulkUpdate ($hash,"cmd",0); readingsEndUpdate($hash, 0); } } return "" if (!$hash->{helper}{globalinit}); return "" if (!$hash->{itimer}{all} and !$hash->{devices}{all} and !$hash->{state}{device} and !keys %{$hash->{state}{STATE}} and !keys %{$hash->{regexp}{all}}); if (($hash->{itimer}{all}) and $hash->{itimer}{all} =~ / $dev->{NAME} /) { for (my $j=0; $j<$hash->{helper}{last_timer};$j++) { if (CheckiTimerDoIf ($dev->{NAME},$hash->{time}{$j},$eventas)) { DOIF_SetTimer($hash,"DOIF_TimerTrigger",$j); } } } return "" if (defined $hash->{helper}{cur_cmd_nr}); return "" if (ReadingsVal($pn,"mode","") eq "disabled"); if ((($hash->{devices}{all}) and $hash->{devices}{all} =~ / $dev->{NAME} /) or CheckRegexpDoIf($hash,$dev->{NAME},$eventa,"all") or CheckRegexpDoIf($hash,$dev->{NAME},$eventa,"STATE")){ $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} / or CheckRegexpDoIf($hash,$dev->{NAME},$eventa,"STATE")) 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; my ($now, $microseconds) = gettimeofday(); my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($now); $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 (defined $hash->{localtime}{$j} and $hash->{localtime}{$j} == $localtime) { if (defined ($hash->{interval}{$j})) { if ($hash->{interval}{$j} != -1) { if (defined $hash->{realtime}{$j} eq $hash->{realtime}{$hash->{interval}{$j}}) { $hash->{timer}{$hash->{interval}{$j}}=0; next; } } } $hash->{timer}{$j}=1; if (!DOIF_time_once($hash,$j,$wday,$hash->{days}{$j})) {#check days $hash->{timer}{$j}=0; } } } $ret=DOIF_Trigger ($hash,"") if (ReadingsVal($pn,"mode","") ne "disabled"); for (my $j=0; $j<$hash->{helper}{last_timer};$j++) { $hash->{timer}{$j}=0; if (defined $hash->{localtime}{$j} and $hash->{localtime}{$j} == $localtime) { 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 ($hash, $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 { ($timeStr,$err)=ReplaceAllReadingsDoIf($hash,$timeStr,-3,1); return ($err) if ($err); ($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($hash,$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($hash, $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 "") { # $ret = eval $block; # return($block." ",$@) if ($@); # $block=$ret; ($err,$rel,$block)=DOIF_DetTime($hash,"{".$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 "") { 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($hash,$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; if (defined ($hash->{localtime}{$nr})) { my $old_lt=$hash->{localtime}{$nr}; my $found=0; delete ($hash->{localtime}{$nr}); delete ($hash->{realtime}{$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}); } } my ($second,$err, $rel)=DOIF_CalcTime($hash,$timeStr); my $timernr=sprintf("timer_%02d_c%02d",($nr+1),($cond+1)); if ($err) { readingsSingleUpdate ($hash,$timernr,"error: ".$err,AttrVal($hash->{NAME},"timerevent","")?1:0); Log3 $hash->{NAME},4 , "$hash->{NAME} ".$timernr." error: ".$err; #RemoveInternalTimer($timer); #$hash->{realtime}{$nr} = "00:00:00" if (!defined $hash->{realtime}{$nr}); return $err; } if ($second < 0 and $rel) { readingsSingleUpdate ($hash,$timernr,"time offset: $second, negativ offset is not allowed",AttrVal($hash->{NAME},"timerevent","")?1:0); return($timernr,"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); } } } 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,$timernr,$next_time_str,AttrVal($hash->{NAME},"timerevent","")?1:0); $hash->{realtime}{$nr}=strftime("%H:%M:%S",localtime($next_time)); $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; } #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->{helper}{last_timer}=0; $hash->{helper}{sleeptimer}=-1; return("","") if ($tail =~ /^ *$/); 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*(\(|\{)/) { if ($tail =~ /^\s*\(/) { ($beginning,$if_cmd_ori,$err,$tail)=GetBlockDoIf($tail,'[\(\)]'); return ($if_cmd_ori,$err) if ($err); } elsif ($tail =~ /^\s*\{/) { ($beginning,$if_cmd_ori,$err,$tail)=GetBlockDoIf($tail,'[\{\}]'); return ($if_cmd_ori,$err) if ($err); $if_cmd_ori="{".$if_cmd_ori."}"; } ($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}; my ($block,$err)=ReplaceAllReadingsDoIf($hash,$a[3],-2,0); return $err if ($err); } elsif($a[0] eq "del" && $a[2] eq "state") { delete $hash->{state}; } 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 "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" or $arg eq "enable") { if (AttrVal($hash->{NAME},"disable","")) { return ("modul ist deactivated by disable attribut, delete disable attribut first"); } } if ($arg eq "disable") { readingsBeginUpdate ($hash); readingsBulkUpdate($hash,"last_cmd",ReadingsVal($pn,"state","")); 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}); delete ($defs{$hash->{NAME}}{READINGS}{cmd_seqnr}); delete ($defs{$hash->{NAME}}{READINGS}{cmd_event}); readingsSingleUpdate($hash, "state","initialize",1); } elsif ($arg eq "enable" ) { #delete ($defs{$hash->{NAME}}{READINGS}{mode}); readingsSingleUpdate ($hash,"state",ReadingsVal($pn,"last_cmd",""),0) if (ReadingsVal($pn,"last_cmd","") ne ""); delete ($defs{$hash->{NAME}}{READINGS}{last_cmd}); readingsSingleUpdate ($hash,"mode","enable",1) } elsif ($arg =~ /^cmd_(.*)/ ) { if (ReadingsVal($pn,"mode","") ne "disabled") { if ($hash->{helper}{sleeptimer} != -1) { RemoveInternalTimer($hash); readingsSingleUpdate ($hash, "wait_timer", "no timer",1); $hash->{helper}{sleeptimer}=-1; } DOIF_cmd ($hash,$1-1,0,"set_cmd_".$1); } } elsif ($arg eq "?") { my $setList = AttrVal($pn, "setList", " "); $setList =~ s/\n/ /g; my $cmdList=""; #my @cmdState=SplitDoIf('|',AttrVal($hash->{NAME},"cmdState","")); #my @cmdSubState; my $max_cond=keys %{$hash->{condition}}; $max_cond++ if (defined ($hash->{do}{$max_cond}{0}) or ($max_cond == 1 and !(AttrVal($pn,"do","") or AttrVal($pn,"repeatsame","")))); for (my $i=0; $i <$max_cond;$i++) { #@cmdSubState=SplitDoIf(',',$cmdState[$i]); $cmdList.="cmd_".($i+1).":noArg "; #$cmdList.=EvalCmdStateDoIf($hash,$cmdSubState[0]).":noArg " if defined ($cmdState[$i]); } return "unknown argument ? for $pn, choose one of disable:noArg initialize:noArg enable:noArg $cmdList $setList"; } else { my @rl = split(" ", AttrVal($pn, "readingList", "")); my $doRet; eval { if(@rl && grep /\b$arg\b/, @rl) { my $v = shift @a; $v = shift @a; readingsSingleUpdate($hash, $v, join(" ",@a), 1); $doRet = 1; } }; return if($doRet); if (ReadingsVal($pn,"mode","") ne "disabled") { my @cmdState=SplitDoIf('|',AttrVal($hash->{NAME},"cmdState","")); my @cmdSubState; for (my $i=0; $i < @cmdState;$i++) { @cmdSubState=SplitDoIf(',',$cmdState[$i]); if ($arg eq EvalCmdStateDoIf($hash,$cmdSubState[0])) { if ($hash->{helper}{sleeptimer} != -1) { RemoveInternalTimer($hash); readingsSingleUpdate ($hash, "wait_timer", "no timer",1); $hash->{helper}{sleeptimer}=-1; } DOIF_cmd ($hash,$i,0,"set_".$arg."_cmd_".($i+1)); last; } } } #return "unknown argument $arg for $pn, choose one of disable:noArg initialize:noArg enable:noArg cmd $setList"; } return $ret; } 1; =pod =item helper =item summary universal module, it works event- and time-controlled =item summary_DE universelles Modul, welches ereignis- und zeitgesteuert Anweisungen ausführt =begin html

DOIF

=end html =begin html_DE

DOIF

=end html_DE =cut