################################################################# # $Id$ # # The module is a timer for executing actions with only one InternalTimer. # Github - FHEM Home Automation System # https://github.com/fhem/Timer # # FHEM Forum: Automatisierung # https://forum.fhem.de/index.php/board,20.0.html # https://forum.fhem.de/index.php/topic,103848.html | https://forum.fhem.de/index.php/topic,103986.0.html # # 2019 | 2020 - HomeAuto_User, elektron-bbs ################################################################# # notes: # - module mit package umsetzen ################################################################# package main; use strict; use warnings; use Time::HiRes qw(gettimeofday); my @action = ("on","off","DEF"); my @names; my @designations; my $description_all; my $cnt_attr_userattr = 0; my $language = uc(AttrVal("global", "language", "EN")); if ($language eq "DE") { @designations = ("Sonnenaufgang","Sonnenuntergang","lokale Zeit","Uhr","SA","SU"); $description_all = "alle"; # using in RegEx @names = ("Nr.","Jahr","Monat","Tag","Stunde","Minute","Sekunde","Gerät oder Bezeichnung","Aktion","Mo","Di","Mi","Do","Fr","Sa","So","aktiv",""); } else { @designations = ("Sunrise","Sunset","local time","","SR","SS"); $description_all = "all"; # using in RegEx @names = ("No.","Year","Month","Day","Hour","Minute","Second","Device or label","Action","Mon","Tue","Wed","Thu","Fri","Sat","Sun","active",""); } ########################## sub Timer_Initialize($) { my ($hash) = @_; $hash->{AttrFn} = "Timer_Attr"; $hash->{AttrList} = "disable:0,1 ". "Offset_Horizon:REAL,CIVIL,NAUTIC,ASTRONOMIC ". "Show_DeviceInfo:alias,comment ". "Timer_preselection:on,off ". "Table_Border_Cell:on,off ". "Table_Border:on,off ". "Table_Header_with_time:on,off ". "Table_Style:on,off ". "Table_Size_TextBox:15,20,25,30,35,40,45,50 ". "Table_View_in_room:on,off ". "stateFormat:textField-long "; $hash->{DefFn} = "Timer_Define"; $hash->{SetFn} = "Timer_Set"; $hash->{GetFn} = "Timer_Get"; $hash->{UndefFn} = "Timer_Undef"; $hash->{NotifyFn} = "Timer_Notify"; #$hash->{FW_summaryFn} = "Timer_FW_Detail"; # displays html instead of status icon in fhemweb room-view $hash->{FW_detailFn} = "Timer_FW_Detail"; $hash->{FW_deviceOverview} = 1; $hash->{FW_addDetailToSummary} = 1; # displays html in fhemweb room-view } ########################## # Predeclare Variables from other modules may be loaded later from fhem our $FW_wname; ########################## sub Timer_Define($$) { my ($hash, $def) = @_; my @arg = split("[ \t][ \t]*", $def); my $name = $arg[0]; ## Definitionsname, mit dem das Gerät angelegt wurde my $typ = $hash->{TYPE}; ## Modulname, mit welchem die Definition angelegt wurde my $filelogName = "FileLog_$name"; my ($cmd, $ret); my ($autocreateFilelog, $autocreateHash, $autocreateName, $autocreateDeviceRoom, $autocreateWeblinkRoom) = ('%L' . $name . '-%Y-%m.log', undef, 'autocreate', $typ, $typ); $hash->{NOTIFYDEV} = "global,TYPE=$typ"; return "Usage: define $name" if(@arg != 2); if ($init_done) { if (!defined(AttrVal($autocreateName, "disable", undef)) && !exists($defs{$filelogName})) { ### create FileLog ### $autocreateFilelog = AttrVal($autocreateName, "filelog", undef) if (defined AttrVal($autocreateName, "filelog", undef)); $autocreateFilelog =~ s/%NAME/$name/g; $cmd = "$filelogName FileLog $autocreateFilelog $name"; Log3 $filelogName, 2, "$name: define $cmd"; $ret = CommandDefine(undef, $cmd); if($ret) { Log3 $filelogName, 2, "$name: ERROR: $ret"; } else { ### Attributes ### CommandAttr($hash,"$filelogName room $autocreateDeviceRoom"); CommandAttr($hash,"$filelogName logtype text"); CommandAttr($hash,"$name room $autocreateDeviceRoom"); } } ### Attributes ### CommandAttr($hash,"$name room $typ") if (!defined AttrVal($name, "room", undef)); # set room, if only undef --> new def } ### default value´s ### readingsBeginUpdate($hash); readingsBulkUpdate($hash, "state" , "Defined"); readingsBulkUpdate($hash, "internalTimer" , "stop"); readingsEndUpdate($hash, 0); return undef; } ##################### sub Timer_Set($$$@) { my ( $hash, $name, @a ) = @_; return "no set value specified" if(int(@a) < 1); my $setList = "addTimer:noArg "; my $cmd = $a[0]; my $cmd2 = $a[1]; my $Timers_Count = 0; my $Timers_Count2; my $Timers_diff = 0; my $Timer_preselection = AttrVal($name,"Timer_preselection","off"); my $value; foreach my $d (sort keys %{$hash->{READINGS}}) { if ($d =~ /^Timer_(\d)+$/) { $Timers_Count++; $d =~ s/Timer_//; $setList.= "deleteTimer:" if ($Timers_Count == 1); $setList.= $d.","; } } if ($Timers_Count != 0) { $setList = substr($setList, 0, -1); # cut last , $setList.= " saveTimers:noArg"; } $setList.= " sortTimer:noArg" if ($Timers_Count > 1); Log3 $name, 4, "$name: Set | cmd=$cmd" if ($cmd ne "?"); if ($cmd eq "sortTimer") { my @timers_unsortet; my @userattr_values; my @attr_values_names; my $timer_nr_new; my $array_diff = 0; # difference, Timer can be sorted >= 1 my $array_diff_cnt1 = 0; # need to check 1 + 1 my $array_diff_cnt2 = 0; # need to check 1 + 1 foreach my $readingsName (sort keys %{$hash->{READINGS}}) { if ($readingsName =~ /^Timer_(\d+)$/) { my $value = ReadingsVal($name, $readingsName, 0); $value =~ /^.*\d{2},(.*),(on|off|DEF)/; push(@timers_unsortet,$1.",".ReadingsVal($name, $readingsName, 0).",$readingsName"); # unsort Reading Wert in Array $array_diff_cnt1++; $array_diff_cnt2 = substr($readingsName,-2) * 1; $array_diff = 1 if ($array_diff_cnt1 != $array_diff_cnt2 && $array_diff == 0); } } my @timers_sort = sort @timers_unsortet; # Timer in neues Array sortieren for (my $i=0; $i "."Timer_$timer_nr_new"."_set:textField-long"; push(@attr_values_names, "Timer_$timer_nr_new"."_set:textField-long"); } $timers_sort[$i] = substr( substr($timers_sort[$i],index($timers_sort[$i],",")+1) ,0,-9); readingsSingleUpdate($hash, "Timer_".$timer_nr_new , $timers_sort[$i], 1); } for (my $i=0; $i $attr_values_names[$i]"; addToDevAttrList($name,$attr_values_names[$i]); # added to userattr (new numbre) } addStructChange("modify", $name, "attr $name userattr"); # note with question mark if (scalar(@userattr_values) > 0) { # write userattr_values for (my $i=0; $i{READINGS}}) { if ($d =~ /^Timer_(\d+)$/) { $Timers_Count++; $Timers_Count2 = $1 * 1; if ($Timers_Count != $Timers_Count2 && $Timers_diff == 0) { # only for diff $Timers_diff++; last; } } } if ($Timer_preselection eq "on") { my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); $value = $year + 1900 .",".sprintf("%02s", ($mon + 1)).",".sprintf("%02s", $mday).",".sprintf("%02s", $hour).",".sprintf("%02s", $min).",00,,on,1,1,1,1,1,1,1,0"; } else { $value = "$description_all,$description_all,$description_all,$description_all,$description_all,00,,on,1,1,1,1,1,1,1,0"; } $Timers_Count = $Timers_Count + 1 if ($Timers_diff == 0); readingsSingleUpdate($hash, "Timer_".sprintf("%02s", $Timers_Count) , $value, 1); } if ($cmd eq "saveTimers") { open(SaveDoc, '>', "./FHEM/lib/$name"."_conf.txt") || return "ERROR: file $name"."_conf.txt can not open!"; foreach my $d (sort keys %{$hash->{READINGS}}) { print SaveDoc "Timer_".$1.",".$hash->{READINGS}->{$d}->{VAL}."\n" if ($d =~ /^Timer_(\d+)$/); } print SaveDoc "\n"; foreach my $e (sort keys %{$attr{$name}}) { my $LE = ""; $LE = "\n" if (AttrVal($name, $e, undef) !~ /\n$/); print SaveDoc $e.",".AttrVal($name, $e, undef).$LE if ($e =~ /^Timer_(\d+)_set$/); } close(SaveDoc); } if ($cmd eq "deleteTimer") { readingsDelete($hash, "Timer_".$cmd2); readingsBeginUpdate($hash); readingsBulkUpdate($hash, "state" , "Timer_$cmd2 deleted"); if ($Timers_Count == 0) { readingsBulkUpdate($hash, "internalTimer" , "stop",1); RemoveInternalTimer($hash, "Timer_Check"); } readingsEndUpdate($hash, 1); my $deleteTimer = "Timer_$cmd2"."_set:textField-long"; Timer_delFromUserattr($hash,$deleteTimer); Timer_PawList($hash); # list, Probably associated with addStructChange("modify", $name, "attr $name userattr Timer_$cmd2"); # note with question mark } return $setList if ( $a[0] eq "?"); return "Unknown argument $cmd, choose one of $setList" if (not grep /$cmd/, $setList); return undef; } ##################### sub Timer_Get($$$@) { my ( $hash, $name, $cmd, @a ) = @_; my $list = "loadTimers:no,yes"; my $cmd2 = $a[0]; my $Timer_cnt_name = -1; if ($cmd eq "loadTimers") { if ($cmd2 eq "no") { return ""; } if ($cmd2 eq "yes") { my $error = ""; my $line = 0; my @lines_readings; my @attr_values; my @attr_values_names; RemoveInternalTimer($hash, "Timer_Check"); open (InputFile,"<./FHEM/lib/$name"."_conf.txt") || return "ERROR: No file $name"."_conf.txt found in ./FHEM/lib directory from FHEM!"; while () { $line++; # Timer_04,alle,alle,alle,12,00,00,Update FHEM,Def,1,0,0,0,0,0,0,1 if ($_ =~ /^(.*),.*,.*,.*,.*,.*,.*,.*,(.*),.*,.*,.*,.*,.*,.*,.*,[0-1]$/) { chomp ($_); # Zeilenende entfernen $_ =~ s/,Def,/,DEF,/g if ($2 =~ /Def/); # Compatibility modify Def to DEF push(@lines_readings,$_); # lines in array my @values = split(",",$_); # split line in array to check #$error.= "- scalar arrray\n" if (scalar(@values) != 17); push(@attr_values_names, $values[0]."_set") if($values[8] eq "DEF"); for (my $i=0;$i<@values;$i++) { $error.= "- ".$values[0]." is no $name description" if ($i == 0 && $values[0] !~ /^Timer_\d{2}$/); $error.= "- ".$values[1]." invalid value" if ($i == 1 && $values[1] !~ /^\d{4}$|^$description_all$/); if ($i >= 2 && $i <= 5 && $values[$i] ne "$description_all") { $error.= "- ".$values[$i]." not decimal" if ($i >= 2 && $i <= 3 && $values[$i] !~ /^\d{2}$/); $error.= "- ".$values[2]." wrong value (allowed 1-12)" if ($i == 2 && ($values[2] * 1) < 1 && ($values[2] * 1) > 12); $error.= "- ".$values[3]." wrong value (allowed 1-31)" if ($i == 3 && ($values[3] * 1) < 1 && ($values[3] * 1) > 31); if ($i >= 4 && $i <= 5 && $values[$i] ne $designations[4] && $values[$i] ne $designations[5]) { # SA -> 4 SU -> 5 $error.= "- ".$values[$i]." not support" if ($i >= 4 && $i <= 5 && $values[$i] !~ /^\d{2}$/); $error.= "- ".$values[4]." wrong value (allowed 00-23)" if ($i == 4 && ($values[4] * 1) > 23); $error.= "- ".$values[5]." wrong value (allowed 00-59)" if ($i == 5 && ($values[5] * 1) > 59); } } $error.= "- ".$values[$i]." is no % 10\n" if ($i == 6 && $values[$i] % 10 != 0); $error.= "- ".$values[$i]." is no allowed action \n" if ($i == 8 && not grep { $values[$i] eq $_ } @action); $error.= "- ".$values[$i]." is not 0 or 1 \n" if ($i >= 9 && $values[$i] ne "0" && $values[$i] ne "1"); if ($error ne "") { close InputFile; Timer_Check($hash); $error.= "\nYour language is wrong! ($language)" if ($error =~ /-\s(all\s|alle\s|SA\s|SU\s|SR\s|SS\s)/); return "ERROR: your file is NOT valid!\n\nline $line\n$error"; } } } if ($_ =~ /^Timer_\d{2}_set,/) { $Timer_cnt_name++; push(@attr_values, substr($_,13)); } elsif ($_ !~ /^Timer_\d{2},/) { $attr_values[$Timer_cnt_name].= $_ if ($Timer_cnt_name >= 0); if ($_ =~ /.*}.*/){ # letzte } Klammer finden my $err = perlSyntaxCheck($attr_values[$Timer_cnt_name], ()); # check PERL Code if($err) { $err = "ERROR: your file is NOT valid! \n \n".$err; close InputFile; Timer_Check($hash); return $err; } } } } close InputFile; foreach my $d (sort keys %{$hash->{READINGS}}) { # delete all readings readingsDelete($hash, $d) if ($d =~ /^Timer_(\d+)$/); } foreach my $f (sort keys %{$attr{$name}}) { # delete all attributes Timer_xx_set ... CommandDeleteAttr($hash, $name." ".$f) if ($f =~ /^Timer_(\d+)_set$/); } my @userattr_values = split(" ", AttrVal($name, "userattr", "none")); for (my $i=0;$i<@userattr_values;$i++) { # delete userattr values Timer_xx_set:textField-long ... delFromDevAttrList($name, $userattr_values[$i]) if ($userattr_values[$i] =~ /^Timer_(\d+)_set:textField-long$/); } foreach my $e (@lines_readings) { # write new readings my $Timer_nr = substr($e,0,8); readingsSingleUpdate($hash, "$Timer_nr" , substr($e,9,length($e)-9), 1) if ($e =~ /^Timer_\d{2},/); } for (my $i=0;$i<@attr_values_names;$i++) { # write new userattr addToDevAttrList($name,$attr_values_names[$i].":textField-long"); } for (my $i=0;$i<@attr_values;$i++) { # write new attr value chomp ($attr_values[$i]); # Zeilenende entfernen CommandAttr($hash,"$name $attr_values_names[$i] $attr_values[$i]"); } Timer_PawList($hash); # list, Probably associated with readingsSingleUpdate($hash, "state" , "Timers loaded", 1); FW_directNotify("FILTER=(room=)?$name", "#FHEMWEB:WEB", "location.reload('true')", ""); Timer_Check($hash); return undef; } } return "Unknown argument $cmd, choose one of $list"; } ##################### sub Timer_Attr() { my ($cmd, $name, $attrName, $attrValue) = @_; my $hash = $defs{$name}; my $typ = $hash->{TYPE}; if ($cmd eq "set" && $init_done == 1 ) { Log3 $name, 5, "$name: Attr | set $attrName to $attrValue"; if ($attrName eq "disable") { if ($attrValue eq "1") { readingsSingleUpdate($hash, "internalTimer" , "stop",1); RemoveInternalTimer($hash, "Timer_Check"); } elsif ($attrValue eq "0") { Timer_Check($hash); } } if ($attrName =~ /^Timer_\d{2}_set$/) { my $err = perlSyntaxCheck($attrValue, ()); # check PERL Code InternalTimer(gettimeofday()+0.1, "Timer_PawList", $hash); return $err if($err); } } if ($cmd eq "del") { Log3 $name, 5, "$name: Attr | Attributes $attrName deleted"; if ($attrName eq "disable") { Timer_Check($hash); } if ($attrName eq "userattr") { if (defined AttrVal($FW_wname, "confirmDelete", undef) && AttrVal($FW_wname, "confirmDelete", undef) == 0) { $cnt_attr_userattr++; return "Please execute again if you want to force the attribute to delete!" if ($cnt_attr_userattr == 1); $cnt_attr_userattr = 0; } } if ($attrName =~ /^Timer_\d{2}_set$/) { Log3 $name, 3, "$name: Attr | Attributes $attrName deleted"; InternalTimer(gettimeofday()+0.1, "Timer_PawList", $hash); } } } ##################### sub Timer_Undef($$) { my ($hash, $arg) = @_; my $name = $hash->{NAME}; RemoveInternalTimer($hash, "Timer_Check"); Log3 $name, 4, "$name: Undef | device is delete"; return undef; } ##################### sub Timer_Notify($$) { my ($hash, $dev_hash) = @_; my $name = $hash->{NAME}; my $typ = $hash->{TYPE}; return "" if(IsDisabled($name)); # Return without any further action if the module is disabled my $devName = $dev_hash->{NAME}; # Device that created the events my $events = deviceEvents($dev_hash, 1); if($devName eq "global" && grep(m/^INITIALIZED|REREADCFG$/, @{$events}) && $typ eq "Timer") { Log3 $name, 5, "$name: Notify is running and starting $name"; ### Compatibility Check Def to DEF ### Log3 $name, 4, "$name: Notify Compatibility check"; foreach my $d (keys %{$hash->{READINGS}}) { if ( $d =~ /^Timer_\d+$/ && ReadingsVal($name, $d, "") =~ /,Def,/ ) { Log3 $name, 4, "$name: $d with ".ReadingsVal($name, $d, "")." must modify!"; my $ReadingsMod = ReadingsVal($name, $d, ""); $ReadingsMod =~ s/,Def,/,DEF,/g; readingsSingleUpdate($hash, $d , $ReadingsMod, 0); } } Timer_Check($hash); } return undef; } ##### HTML-Tabelle Timer-Liste erstellen ##### sub Timer_FW_Detail($$$$) { my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn. my $hash = $defs{$d}; my $name = $hash->{NAME}; my $html = ""; my $selected = ""; my $Timers_Count = 0; my $Table_Border = AttrVal($name,"Table_Border","off"); my $Table_Border_Cell = AttrVal($name,"Table_Border_Cell","off"); my $Table_Header_with_time = AttrVal($name,"Table_Header_with_time","off"); my $Table_Size_TextBox = AttrVal($name,"Table_Size_TextBox",20); my $Table_Style = AttrVal($name,"Table_Style","off"); my $Table_View_in_room = AttrVal($name,"Table_View_in_room","on"); my $style_code1 = ""; my $style_code2 = ""; my $time = FmtDateTime(time()); my $horizon = AttrVal($name,"Offset_Horizon","REAL"); my $FW_room_dupl = $FW_room; my @timer_nr; my $cnt_max = scalar(@names); return $html if((!AttrVal($name, "room", undef) && $FW_detail eq "") || ($Table_View_in_room eq "off" && $FW_detail eq "")); if ($Table_Style eq "on") { ### style via CSS for Checkbox ### $html.= ''; } Log3 $name, 5, "$name: attr2html is running"; foreach my $d (sort keys %{$hash->{READINGS}}) { if ($d =~ /^Timer_\d+$/) { $Timers_Count++; push(@timer_nr, substr($d,index($d,"_")+1)); } } $style_code2 = "border:2px solid #00FF00;" if($Table_Border eq "on"); $html.= "
$designations[0]: ".sunrise_abs($horizon)." $designations[3]  |  $designations[1]: ".sunset_abs($horizon)." $designations[3]  |  $designations[2]: ".TimeNow()." $designations[3]
" if($Table_Header_with_time eq "on"); $html.= "
"; # Timer Jahr Monat Tag Stunde Minute Sekunde Gerät Aktion Mo Di Mi Do Fr Sa So aktiv speichern # ------------------------------------------------------------------------------------------------- # 2019 09 03 18 15 00 Player on 0 0 0 0 0 0 0 0 # Spalte: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # ------------------------------------------------------------------------------------------------- # T 1 id: 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 ($id = timer_nr * 20 + $Spalte) # T 2 id: 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 ($id = timer_nr * 20 + $Spalte) ## Ueberschrift $html.= ""; #### $style_code1 = "border:1px solid #D8D8D8;" if($Table_Border_Cell eq "on"); for(my $spalte = 0; $spalte <= $cnt_max - 1; $spalte++) { $html.= "" if ($spalte == 0); # Timer-Nummer - auto Breite $html.= "" if ($spalte >= 1 && $spalte <= 6); # Dropdown-Listen - definierte Breite $html.= "" if ($spalte > 6 && $spalte < $cnt_max - 1); # auto Breite $html.= "" if ($spalte == $cnt_max - 1); # Button save - auto Breite } $html.= ""; for(my $zeile = 0; $zeile < $Timers_Count; $zeile++) { $html.= sprintf("", ($zeile & 1)?"odd":"even"); my $id = $timer_nr[$zeile] * 20; # id 20, 40, 60 ... # Log3 $name, 3, "$name: Zeile $zeile, id $id, Start"; my @select_Value = split(",", ReadingsVal($name, "Timer_".$timer_nr[$zeile], "$description_all,$description_all,$description_all,$description_all,$description_all,00,Lampe,on,0,0,0,0,0,0,0,0,,")); for(my $spalte = 1; $spalte <= $cnt_max; $spalte++) { $style_code1 .= "Padding-bottom:5px; " if ($zeile == $Timers_Count - 1); # letzte Zeile $html.= "" if ($spalte == 1); # Spalte Timer-Nummer if ($spalte >=2 && $spalte <= 7) { ## DropDown-Listen fuer Jahr, Monat, Tag, Stunde, Minute, Sekunde my $start = 0; # Stunde, Minute, Sekunde my $stop = 12; # Monat my $step = 1; # Jahr, Monat, Tag, Stunde, Minute $start = substr($time,0,4) if ($spalte == 2); # Jahr $stop = $start + 10 if ($spalte == 2); # Jahr $start = 1 if ($spalte == 3 || $spalte == 4); # Monat, Tag $stop = 31 if ($spalte == 4); # Tag $stop = 23 if ($spalte == 5); # Stunde $stop = 59 if ($spalte == 6); # Minute $stop = 50 if ($spalte == 7); # Sekunde $step = 10 if ($spalte == 7); # Sekunde $id++; # Log3 $name, 3, "$name: Zeile $zeile, id $id, select"; $html.= ""; } if ($spalte == 8) { ## Spalte Geraete $id ++; my $comment = ""; $comment = AttrVal($select_Value[$spalte-2],"alias","") if (AttrVal($name,"Show_DeviceInfo","") eq "alias"); $comment = AttrVal($select_Value[$spalte-2],"comment","") if (AttrVal($name,"Show_DeviceInfo","") eq "comment"); $html.= ""; } if ($spalte == 9) { ## DropDown-Liste Aktion $id ++; $html.= ""; } if ($spalte > 9 && $spalte < $cnt_max) { ## Spalte Wochentage + aktiv $id ++; $html.= "" if ($select_Value[$spalte-2] eq "0"); $html.= "" if ($select_Value[$spalte-2] eq "1"); } if ($spalte == $cnt_max) { ## Button Speichern $id ++; $html.= ""; # 🖫 💾 } Log3 $name, 5, "$name: attr2html | Timer=".$timer_nr[$zeile]." ".$names[$spalte-1]."=".$select_Value[$spalte-2]." cnt_max=$cnt_max ($spalte)" if ($spalte > 1 && $spalte < $cnt_max); } $html.= ""; ## Zeilenende } $html.= "
".$names[$spalte]."".$names[$spalte]."".$names[$spalte]."".$names[$spalte]."
".sprintf("%02s", $timer_nr[$zeile])."
$comment
"; ## Tabellenende ## Tabellenende + Script $html.= '
'; return $html; } ### for function from pushed_savebutton ### sub FW_pushed_savebutton { my $name = shift; my $hash = $defs{$name}; my $selected_buttons = shift; # neu,alle,alle,alle,alle,alle,00,Beispiel,on,0,0,0,0,0,0,0,0 my @selected_buttons = split("," , $selected_buttons); my $timer = $selected_buttons[0]; my $timers_count = 0; # Timer by counting my $timers_count2 = 0; # need to check 1 + 1 my $timers_diff = 0; # need to check 1 + 1 my $FW_room_dupl = shift; my $cnt_names = scalar(@selected_buttons); my $devicefound = 0; # to check device exists my $reload = 0; my $timestamp = TimeNow(); # Time now -> 2016-02-16 19:34:24 my @timestamp_values = split(/-|\s|:/ , $timestamp); # Time now splitted my ($sec, $min, $hour, $mday, $month, $year) = ($timestamp_values[5], $timestamp_values[4], $timestamp_values[3], $timestamp_values[2], $timestamp_values[1], $timestamp_values[0]); Log3 $name, 5, "$name: FW_pushed_savebutton is running"; return "ERROR: Comma not allowed in description!" if ($cnt_names > 17); foreach my $d (sort keys %{$hash->{READINGS}}) { if ($d =~ /^Timer_(\d+)$/) { $timers_count++; $timers_count2 = $1 * 1; if ($timers_count != $timers_count2 && $timers_diff == 0) { # only for diff $timer = $timers_count; $timers_diff = 1; } } } for(my $i = 0;$i < $cnt_names;$i++) { Log3 $name, 5, "$name: FW_pushed_savebutton | ".$names[$i]." -> ".$selected_buttons[$i]; ## to set time to check input ## SA -> pos 4 array | SU -> pos 5 array ## if ($i >= 1 && $i <=6 && ( $selected_buttons[$i] ne "$description_all" && $selected_buttons[$i] ne $designations[4] && $selected_buttons[$i] ne $designations[5] )) { $sec = $selected_buttons[$i] if ($i == 6); $min = $selected_buttons[$i] if ($i == 5); $hour = $selected_buttons[$i] if ($i == 4); $mday = $selected_buttons[$i] if ($i == 3); $month = $selected_buttons[$i]-1 if ($i == 2); $year = $selected_buttons[$i]-1900 if ($i == 1); } if ($i == 7) { Log3 $name, 5, "$name: FW_pushed_savebutton | check: exists device or name -> ".$selected_buttons[$i]; foreach my $d (sort keys %defs) { if (defined($defs{$d}{NAME}) && $defs{$d}{NAME} eq $selected_buttons[$i]) { $devicefound++; Log3 $name, 5, "$name: FW_pushed_savebutton | ".$selected_buttons[$i]." is checked and exists"; } } if ($devicefound == 0 && ($selected_buttons[$i+1] eq "on" || $selected_buttons[$i+1] eq "off")) { Log3 $name, 5, "$name: FW_pushed_savebutton | ".$selected_buttons[$i]." is NOT exists"; return "ERROR: device not exists or no description! NO timer saved!"; } } } return "ERROR: The time is in the past. Please set a time in the future!" if ((time() - fhemTimeLocal($sec, $min, $hour, $mday, $month - 1, $year)) > 0); return "ERROR: The next switching point is too small!" if ((fhemTimeLocal($sec, $min, $hour, $mday, $month - 1, $year) - time()) < 60); readingsDelete($hash,"Timer_".sprintf("%02s", $timer)."_set") if ($selected_buttons[8] ne "DEF" && ReadingsVal($name, "Timer_".sprintf("%02s", $timer)."_set", 0) ne "0"); my $oldValue = ReadingsVal($name,"Timer_".sprintf("%02s", $selected_buttons[0]) ,0); my $newValue = substr($selected_buttons,(index($selected_buttons,",") + 1)); $reload++ if ($oldValue ne $newValue && $FW_room_dupl); my @Value_split = split(/,/ , $oldValue); $oldValue = $Value_split[7]; @Value_split = split(/,/ , $newValue); $newValue = $Value_split[7]; if ($Value_split[6] eq "" && $Value_split[7] eq "DEF") { # standard name, if no name set in DEF option my $replace = "Timer_".sprintf("%02s", $selected_buttons[0]); $selected_buttons =~ s/,,/,$replace,/g; } readingsBeginUpdate($hash); readingsBulkUpdate($hash, "Timer_".sprintf("%02s", $selected_buttons[0]) , substr($selected_buttons,(index($selected_buttons,",") + 1))); my $state = "Timer_".sprintf("%02s", $selected_buttons[0])." saved"; my $userattrName = "Timer_".sprintf("%02s", $selected_buttons[0])."_set:textField-long"; my $popup = 0; if (($oldValue eq "on" || $oldValue eq "off") && $newValue eq "DEF") { $state = "Timer_".sprintf("%02s", $selected_buttons[0])." is saved and revised userattr. Please set DEF in attribute Timer_".sprintf("%02s", $selected_buttons[0])."_set !"; addToDevAttrList($name,$userattrName); addStructChange("modify", $name, "attr $name userattr"); # note with question mark $popup++; } if ($oldValue eq "DEF" && ($newValue eq "on" || $newValue eq "off")) { $state = "Timer_".sprintf("%02s", $selected_buttons[0])." is saved and deleted from userattr"; Timer_delFromUserattr($hash,$userattrName) if (AttrVal($name, "userattr", undef)); addStructChange("modify", $name, "attr $name userattr"); # note with question mark $reload++; } readingsBulkUpdate($hash, "state" , $state, 1); readingsEndUpdate($hash, 1); Timer_PawList($hash); # list, Probably associated with ## popup user message (jump to javascript) ## if ($popup != 0) { FW_directNotify("FILTER=(room=)?$name", "#FHEMWEB:WEB", "show_popup(".$selected_buttons[0].")", ""); $reload = 0 if ($reload != 0); # reset, need to right running } ## refresh site, need for userattr & right view checkboxes ## FW_directNotify("FILTER=(room=)?$name", "#FHEMWEB:WEB", "location.reload('true')", "") if ($reload != 0); Timer_Check($hash) if ($selected_buttons[16] eq "1" && ReadingsVal($name, "internalTimer", "stop") eq "stop"); return; } ### Popup DEF - Zusammenbau (Rueckgabe -> Javascript) ### sub Timer_Popup { my $name = shift; my $selected_button = shift; $selected_button = sprintf("%02s", $selected_button); my $ret = ""; Log3 $name, 5, "$name: Timer_Popup is running"; if ($language eq "DE") { $ret.= "

Hinweis:

"; $ret.= "Das Attribut userattr wurde automatisch angepasst.
"; $ret.= "Um DEF zu definieren, geben Sie bitte das FHEM Kommando oder den PERL Code in das Attribut Timer_$selected_button"."_set ein.
"; $ret.= "attr $name Timer_$selected_button"."_set \"DEF Code\" (ohne \" \")

"; $ret.= "Die Eingabe entspricht der FHEM-Befehlszeile im Browser.
"; $ret.= "

DEF Beispiele:

"; $ret.= "• FHEM Kommando: set TYPE=IT:FILTER=room=03_Stube.*:FILTER=state=on off
"; $ret.= "• PERL Code: { Log 1, \"$name: schaltet jetzt\" }

"; $ret.= "Weitere Beispiele oder Intervallschaltungen finden Sie in der gerätespezifischen Hilfe.

"; $ret.= "Klicken Sie auf close, um fortzufahren."; } else { $ret.= "

Note:

"; $ret.= "The attribute userattr has been automatically revised.
"; $ret.= "To define DEF, please input the FHEM command or PERL code in attribute Timer_$selected_button"."_set.
"; $ret.= "attr $name Timer_$selected_button"."_set \"DEF code\" (without \" \")

"; $ret.= "The input is made equal to the FHEM command line in the browser.
"; $ret.= "

DEF examples:

"; $ret.= "• FHEM command: set TYPE=IT:FILTER=room=03_Stube.*:FILTER=state=on off
"; $ret.= "• PERL code: { Log 1, \"$name: switch now\" }

"; $ret.= "For more examples or interval switching please look into the Device specific help.

"; $ret.= "Click close to continue."; } return $ret; } ### for delete Timer value from userattr ### sub Timer_delFromUserattr($$) { my $hash = shift; my $deleteTimer = shift; my $name = $hash->{NAME}; if (AttrVal($name, "userattr", undef) =~ /$deleteTimer/) { delFromDevAttrList($name, $deleteTimer); Log3 $name, 5, "$name: delete $deleteTimer from userattr Attributes"; } } ### for Check ### sub Timer_Check($) { my ($hash) = @_; my $name = $hash->{NAME}; my @timestamp_values = split(/-|\s|:/ , TimeNow()); # Time now (2016-02-16 19:34:24) splitted in array my $dayOfWeek = strftime('%w', localtime); # Wochentag $dayOfWeek = 7 if ($dayOfWeek eq "0"); # Sonntag nach hinten (Position 14 im Array) my $intervall = 60; # Intervall to start new InternalTimer (standard) my $cnt_activ = 0; # counter for activ timers my ($seconds, $microseconds) = gettimeofday(); my $horizon = AttrVal($name,"Offset_Horizon","REAL"); my @sunriseValues = split(":" , sunrise_abs($horizon)); # Sonnenaufgang (06:34:24) splitted in array my @sunsetValues = split(":" , sunset_abs($horizon)); # Sonnenuntergang (19:34:24) splitted in array my $state;; Log3 $name, 5, "$name: Check is running, Sonnenaufgang $sunriseValues[0]:$sunriseValues[1]:$sunriseValues[2], Sonnenuntergang $sunsetValues[0]:$sunsetValues[1]:$sunsetValues[2]"; Log3 $name, 4, "$name: Check is running, drift $microseconds microSeconds"; foreach my $d (keys %{$hash->{READINGS}}) { if ($d =~ /^Timer_\d+$/) { my @values = split("," , $hash->{READINGS}->{$d}->{VAL}); #Jahr Monat Tag Stunde Minute Sekunde Gerät Aktion Mo Di Mi Do Fr Sa So aktiv #alle, alle, alle, alle, alle, 00, BlueRay_Player_LG, on, 0, 0, 0, 0, 0, 0, 0, 0 #0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 my $set = 1; if ($values[15] == 1) { # Timer aktiv $cnt_activ++; $values[3] = $sunriseValues[0] if $values[3] eq $designations[4]; # Stunde | Sonnenaufgang -> pos 4 array $values[4] = $sunriseValues[1] if $values[4] eq $designations[4]; # Minute | Sonnenaufgang -> pos 4 array $values[3] = $sunsetValues[0] if $values[3] eq $designations[5]; # Stunde | Sonnenuntergang -> pos 5 array $values[4] = $sunsetValues[1] if $values[4] eq $designations[5]; # Stunde | Sonnenuntergang -> pos 5 array for (my $i = 0;$i < 5;$i++) { # Jahr, Monat, Tag, Stunde, Minute $set = 0 if ($values[$i] ne "$description_all" && $values[$i] ne $timestamp_values[$i]); } $set = 0 if ($values[(($dayOfWeek*1) + 7)] eq "0"); # Wochentag $set = 0 if ($values[5] eq "00" && $timestamp_values[5] ne "00"); # Sekunde (Intervall 60) $set = 0 if ($values[5] ne "00" && $timestamp_values[5] ne $values[5]); # Sekunde (Intervall 10) $intervall = 10 if ($values[5] ne "00"); Log3 $name, 5, "$name: $d - set=$set intervall=$intervall dayOfWeek=$dayOfWeek column array=".(($dayOfWeek*1) + 7)." (".$values[($dayOfWeek*1) + 7].") $values[0]-$values[1]-$values[2] $values[3]:$values[4]:$values[5]"; if ($set == 1) { Log3 $name, 4, "$name: $d - set $values[6] $values[7] ($dayOfWeek, $values[0]-$values[1]-$values[2] $values[3]:$values[4]:$values[5])"; CommandSet($hash, $values[6]." ".$values[7]) if ($values[7] ne "DEF"); # $state = "$d set $values[6] $values[7] accomplished"; readingsSingleUpdate($hash, "state" , "$d set $values[6] $values[7] accomplished", 1); if ($values[7] eq "DEF") { if (AttrVal($name, $d."_set", undef)) { Log3 $name, 5, "$name: $d - exec at command: ".AttrVal($name, $d."_set", undef); my $ret = AnalyzeCommandChain(undef, SemicolonEscape(AttrVal($name, $d."_set", undef))); Log3 $name, 3, "$name: $d\_set - ERROR: $ret" if($ret); } else { $state = "$d missing userattr to work!"; } } } } } } readingsBeginUpdate($hash); if ($intervall == 60) { if ($timestamp_values[5] != 0 && $cnt_activ > 0) { $intervall = 60 - $timestamp_values[5]; readingsBulkUpdate($hash, "internalTimer" , $intervall, 1); Log3 $name, 3, "$name: time difference too large! interval=$intervall, Sekunde=$timestamp_values[5]"; } } ## calculated from the starting point at 00 10 20 30 40 50 if Seconds interval active ## if ($intervall == 10) { if ($timestamp_values[5] % 10 != 0 && $cnt_activ > 0) { $intervall = $intervall - ($timestamp_values[5] % 10); readingsBulkUpdate($hash, "internalTimer" , $intervall, 1); Log3 $name, 3, "$name: time difference too large! interval=$intervall, Sekunde=$timestamp_values[5]"; } } $intervall = ($intervall - $microseconds / 1000000); # Korrektur Zeit wegen Drift RemoveInternalTimer($hash); InternalTimer(gettimeofday()+$intervall, "Timer_Check", $hash, 0) if ($cnt_activ > 0); $state = "no timer active" if ($cnt_activ == 0 && ReadingsVal($name, "internalTimer", "stop") ne "stop"); readingsBulkUpdate($hash, "state" , "$state", 1) if defined($state); readingsBulkUpdate($hash, "internalTimer" , "stop") if ($cnt_activ == 0 && ReadingsVal($name, "internalTimer", "stop") ne "stop"); readingsBulkUpdate($hash, "internalTimer" , $intervall, 0) if($cnt_activ > 0); readingsEndUpdate($hash, 1); } ### list, Probably associated with ### sub Timer_PawList($) { my ($hash) = @_; my $name = $hash->{NAME}; my $associatedWith = ""; Log3 $name, 5, "$name: Timer_PawList is running"; foreach my $d (keys %{$hash->{READINGS}}) { if ($d =~ /^Timer_(\d+)$/) { my @values = split("," , ReadingsVal($name, $d, "")); ### clear value, "Probably associated with" ne DEF if ($values[7] ne "DEF") { if (not grep /$values[6]/, $associatedWith) { $associatedWith = $associatedWith eq "" ? $values[6] : $associatedWith.",".$values[6]; } ### Self-administration test, "Probably associated with" for DEF } elsif ($values[7] eq "DEF") { my $Timer_set_attr = AttrVal($name, $d."_set", ""); if ($Timer_set_attr ne "") { Log3 $name, 5, "$name: Timer_PawList | look at DEF: ".$Timer_set_attr; $Timer_set_attr =~ /(get|set)\s(\w+)\s/; if ($2) { Log3 $name, 5, "$name: Timer_PawList | found in DEF: ".$2; if (not grep /$2/, $associatedWith) { $associatedWith = $associatedWith eq "" ? $2 : $associatedWith.",".$2; } } } } ### END ### } } Log3 $name, 5, "$name: Timer_PawList | Reading .associatedWith is: ".$associatedWith; if ($associatedWith ne "") { CommandSetReading(undef, "$name .associatedWith $associatedWith"); } else { readingsDelete($hash,".associatedWith") if(ReadingsVal($name, ".associatedWith", undef)); } ## current list "Probably associated with" finish ## } ########################################## # Eval-Rückgabewert für erfolgreiches # Laden des Moduls 1; # Beginn der Commandref =pod =item [helper] =item summary Programmable timer =item summary_DE Programmierbare Zeitschaltuhr =begin html

Timer Modul

    The timer module is a programmable timer.

    In Frontend you can define new times and actions. The smallest possible definition of an action is a 10 second interval.
    You can use the dropdown menu to make the settings for the time switch point. Only after clicking on the Save button will the setting be taken over. In the drop-down list, the numerical values ​​for year / month / day / hour / minute / second are available for selection.

    In addition, you can use the selection SR and SS in the hour and minute column. These rumps represent the time of sunrise and sunset.
    For example, if you select at minute SS , you have set the minutes of the sunset as the value. As soon as you set the value to SS at hour and minute the timer uses the calculated sunset time at your location. (For this calculation the FHEM global attributes latitude and longitude are necessary!)

    Programmable actions are currently:
    • on | off - The states must be supported by the device
    • DEF - for a PERL code or a FHEM command *

        example for DEF:
      • { Log 1, "Timer: now switch" } (PERL code)
      • update (FHEM command)
      • trigger Timer state:ins Log geschrieben (FHEM-command)
      • set TYPE=IT:FILTER=room=Stube.*:FILTER=state=on off (FHEM-command)

    * To do this, enter the code to be executed in the respective attribute. example: Timer_03_set

    Interval switching of the timer is only possible in the following variants:
    • minute, define second and set all other values ​​(minute, hour, day, month, year) to all
    • hourly, define second + minute and set all other values ​​(hour, day, month, year) to all
    • daily, define second + minute + hour and set all other values ​​(day, month, year) to all
    • monthly, define second + minute + hour + day and set all other values ​​(month, year) to all
    • annually, define second + minute + hour + day + month and set the value (year) to all
    • sunrise, define second & define minute + hour with SR and set all other values ​​(day, month, year) to all
    • sunset, define second & define minute + hour with SS and set all other values ​​(day, month, year) to all

    Any interval circuits can be defined in which in the associated timer attribute e.g. the following Perl code is inserted:
    {if ($min % 5 == 0) {fhem("set FS10_6_11 toggle");}}
    This timer would run every 5 minutes if the timer is configured to run in a minute as described.
    The following variables for time and date are available:
    $sec, $min, $hour, $mday, $month, $year, $wday, $yday, $isdst, $week, $hms, $hm, $md, $ymd, $we, $twe
    This makes it possible, for example, to have a timer run every Sunday at 15:30:00.

    Define
      define <NAME> Timer

      example:
        define timer Timer

    Set
    • addTimer: Adds a new timer
    • deleteTimer: Deletes the selected timer
    • saveTimers: Saves the settings in file Timers.txt on directory ./FHEM/lib.
    • sortTimer: Sorts the saved timers alphabetically.


    Get
    • loadTimers: Loads a saved configuration from file Timers.txt from directory ./FHEM/lib.


    Attribute

    • stateFormat
      It is used to format the value state
      example: { ReadingsTimestamp($name, "state", 0) ."&nbsp;- ". ReadingsVal($name, "state", "none");}
                     - will format to output: 2019-09-19 17:51:44 - Timer_04 saved



    • Table_Header_with_time
      Shows or hides the sunrise and sunset with the local time above the table. (on | off, standard off)

    • Table_Size_TextBox
      Correction value to change the length of the text box for the device name / designation. (default 20)

    • Table_Style
      Turns on the defined table style. (on | off, default off)

    • Table_View_in_room
      Toggles the tables UI in the room view on or off. (on | off, standard on)
      In the room Unsorted the table UI is always switched off!

    • Timer_preselection
      Sets the input values ​​for a new timer to the current time. (on | off = default)

    • Timer_xx_set
      Location for the FHEM command or PERL code of the timer (xx has the numerical value of 01-99). WITHOUT this attribute, which only appears if action DEF is set , The module does not process FHEM command or PERL code from the user. *

    • Offset_Horizon
      Different elevation angles are used to calculate sunrise and sunset times.
      (REAL = 0°, CIVIL = -6°, NAUTIC = -12°, ASTRONOMIC = -18°, default REAL)

    • Show_DeviceInfo
      Shows the additional information (alias | comment, default off)


    Generated readings
    • Timer_xx
      Memory values ​​of the individual timer

    • internalTimer
      State of the internal timer (stop or Interval until the next call)


    Hints:
    • Entries in the system logfile like: 2019.09.20 22:15:01 3: Timer: time difference too large! interval=59, Sekunde=01 say that the timer has recalculated the time.
=end html =begin html_DE

Timer Modul

    Das Timer Modul ist eine programmierbare Schaltuhr.

    Im Frontend können Sie neue Zeitpunkte und Aktionen definieren. Die kleinstmögliche Definition einer Aktion ist ein 10 Sekunden Intervall.
    Mittels der Dropdown Menüs können Sie die Einstellungen für den Zeitschaltpunkt vornehmen. Erst nach dem drücken auf den Speichern Knopf wird die Einstellung übernommen.

    In der DropDown-Liste stehen jeweils die Zahlenwerte für Jahr / Monat / Tag / Stunde / Minute / Sekunde zur Auswahl.
    Zusätzlich können Sie in der Spalte Stunde und Minute die Auswahl SA und SU nutzen. Diese Kürzel stehen für den Zeitpunkt Sonnenaufgang und Sonnenuntergang.
    Wenn sie Beispielsweise bei Minute SU auswählen, so haben Sie die Minuten des Sonnenuntergang als Wert gesetzt. Sobald Sie bei Stunde und Minute den Wert auf SU stellen, so nutzt der Timer den errechnenten Zeitpunkt Sonnenuntergang an Ihrem Standort. (Für diese Berechnung sind die FHEM globalen Attribute latitude und longitude notwendig!)

    Programmierbare Aktionen sind derzeit:
    • on | off - Die Zustände müssen von dem zu schaltenden Device unterstützt werden
    • DEF - für einen PERL-Code oder ein FHEM Kommando *

        Beispiele für DEF:
      • { Log 1, "Timer: schaltet jetzt" } (PERL-Code)
      • update (FHEM-Kommando)
      • trigger Timer state:ins Log geschrieben (FHEM-Kommando)
      • set TYPE=IT:FILTER=room=Stube.*:FILTER=state=on off (FHEM-Kommando)

    * Hierfür hinterlegen Sie den auszuführenden PERL-Code in das jeweilige Attribut. Bsp.: Timer_03_set

    Eine Intervallschaltung des Timer ist nur möglich in folgenden Varianten:
    • minütlich, Sekunde definieren und alle anderen Werte (Minute, Stunde, Tag, Monat, Jahr) auf alle setzen
    • stündlich, Sekunde + Minute definieren und alle anderen Werte (Stunde, Tag, Monat, Jahr) auf alle setzen
    • täglich, Sekunde + Minute + Stunde definieren und alle anderen Werte (Tag, Monat, Jahr) auf alle setzen
    • monatlich, Sekunde + Minute + Stunde + Tag definieren und alle anderen Werte (Monat, Jahr) auf alle setzen
    • jährlich, Sekunde + Minute + Stunde + Tag + Monat definieren und den Wert (Jahr) auf alle setzen
    • Sonnenaufgang, Sekunde definieren & Minute + Stunde definieren mit SA und alle anderen Werte (Tag, Monat, Jahr) auf alle setzen
    • Sonnenuntergang, Sekunde definieren & Minute + Stunde definieren mit SU und alle anderen Werte (Tag, Monat, Jahr) auf alle setzen

    Beliebige Intervallschaltungen können definiert werden, in dem im zugehörigen Timer-Attribut z.B. folgender Perl-Code eingefügt wird:
    {if ($min % 5 == 0) {fhem("set FS10_6_11 toggle");}}
    Dieser Timer würde dann aller 5 Minuten ausgeführt, wenn der Timer wie beschrieben auf minütliches Ausführen konfiguriert ist.
    Folgende Variablen für Zeit- und Datumsangaben stehen zur Verfügung:
    $sec, $min, $hour, $mday, $month, $year, $wday, $yday, $isdst, $week, $hms, $hm, $md, $ymd, $we, $twe
    Damit ist es möglich, einen Timer beispielsweise nur jeden Sonntag um 15:30:00 Uhr etwas ausführen zu lassen.

    Define
      define <NAME> Timer

      Beispiel:
        define Schaltuhr Timer

    Set
    • addTimer: Fügt einen neuen Timer hinzu.
    • deleteTimer: Löscht den ausgewählten Timer.
    • saveTimers: Speichert die eingestellten Timer in der Datei Timers.txt im Verzeichnis ./FHEM/lib.
    • sortTimer: Sortiert die gespeicherten Timer alphabetisch.


    Get
    • loadTimers: Läd eine gespeicherte Konfiguration aus der Datei Timers.txt aus dem Verzeichnis ./FHEM/lib.


    Attribute

    • stateFormat
      Es dient zur Formatierung des Wertes state
      Beispiel: { ReadingsTimestamp($name, "state", 0) ."&nbsp;- ". ReadingsVal($name, "state", "none");}
                     - wird zur formatieren Ausgabe: 2019-09-19 17:51:44 - Timer_04 saved

    • Table_Border
      Blendet den Tabellenrahmen ein. (on | off, standard off)


    • Table_Header_with_time
      Blendet den Sonnenauf und Sonnenuntergang mit der lokalen Zeit über der Tabelle ein oder aus. (on | off, standard off)

    • Table_Size_TextBox
      Korrekturwert um die Länge der Textbox für die Gerätenamen / Bezeichung zu verändern. (standard 20)

    • Table_Style
      Schaltet den definierten Tabellen-Style ein. (on | off, standard off)

    • Table_View_in_room
      Schaltet das Tabellen UI in der Raumansicht an oder aus. (on | off, standard on)
      Im Raum Unsorted ist das Tabellen UI immer abgeschalten!

    • Timer_preselection
      Setzt die Eingabewerte bei einem neuen Timer auf die aktuelle Zeit. (on | off, standard off)

    • Timer_xx_set
      Speicherort für das FHEM-Kommando oder den PERL-Code des Timers (xx hat den Zahlenwert von 01-99). OHNE dieses Attribut, welches nur erscheint wenn die Aktion DEF eingestellt ist, verarbeitet das Modul kein Kommando oder PERL-Code vom Benutzer. *

    • Offset_Horizon
      Für die Berechnung der Zeiten von Sonnenaufgang und Sonnenuntergang werden verschiedene Höhenwinkel verwendet.
      (REAL = 0°, CIVIL = -6°, NAUTIC = -12°, ASTRONOMIC = -18°, Standard REAL)

    • Show_DeviceInfo
      Blendet die Zusatzinformation ein. (alias | comment, standard off)


    Generierte Readings
    • Timer_xx
      Speicherwerte des einzelnen Timers

    • internalTimer
      Zustand des internen Timers (stop oder Intervall bis zum nächsten Aufruf)


    Hinweise:
    • Einträge im Systemlogfile wie: 2019.09.20 22:15:01 3: Timer: time difference too large! interval=59, Sekunde=01 sagen aus, das der Timer die Zeit neu berechnet hat.
=end html_DE # Ende der Commandref =cut