mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-02-25 09:55:38 +00:00
1284 lines
60 KiB
Perl
1284 lines
60 KiB
Perl
#################################################################
|
||
# $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 - ... HomeAuto_User, elektron-bbs
|
||
#################################################################
|
||
# notes:
|
||
# - module mit package umsetzen
|
||
#################################################################
|
||
|
||
|
||
package main;
|
||
|
||
use strict;
|
||
use warnings;
|
||
use Time::HiRes qw(gettimeofday);
|
||
|
||
our $VERSION = '2021-01-20';
|
||
|
||
my @action = qw(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
|
||
|
||
return;
|
||
}
|
||
|
||
##########################
|
||
# 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";
|
||
|
||
if (@arg != 2) { return "Usage: define <name> $name"; }
|
||
|
||
if ($init_done) {
|
||
if (!defined(AttrVal($autocreateName, 'disable', undef)) && !exists($defs{$filelogName})) {
|
||
### create FileLog ###
|
||
if (defined AttrVal($autocreateName, 'filelog', undef)) { $autocreateFilelog = AttrVal($autocreateName, 'filelog', undef); }
|
||
$autocreateFilelog =~ s/%NAME/$name/gxms;
|
||
$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 ###
|
||
if (!defined AttrVal($name, 'room', undef)) { CommandAttr($hash,"$name room $typ"); } # set room, if only undef --> new def
|
||
}
|
||
|
||
$hash->{versionModule} = $VERSION;
|
||
|
||
### default value´s ###
|
||
readingsBeginUpdate($hash);
|
||
readingsBulkUpdate($hash, 'state' , 'Defined');
|
||
readingsBulkUpdate($hash, 'internalTimer' , 'stop');
|
||
readingsEndUpdate($hash, 0);
|
||
return;
|
||
}
|
||
|
||
#####################
|
||
sub Timer_Set {
|
||
my ( $hash, $name, @a ) = @_;
|
||
if (int(@a) < 1) { return 'no set value specified'; }
|
||
|
||
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)+$/xms) {
|
||
$Timers_Count++;
|
||
$d =~ s/Timer_//ms;
|
||
if ($Timers_Count == 1) { $setList.= 'deleteTimer:'; }
|
||
$setList.= $d.',';
|
||
}
|
||
}
|
||
|
||
if ($Timers_Count != 0) {
|
||
$setList = substr($setList, 0, -1); # cut last ,
|
||
$setList.= ' saveTimers:noArg';
|
||
}
|
||
|
||
if ($Timers_Count > 1) { $setList.= ' sortTimer:noArg'; }
|
||
if ($cmd ne '?') { Log3 $name, 4, "$name: Set | cmd=$cmd"; }
|
||
|
||
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+)$/xms) {
|
||
$value = ReadingsVal($name, $readingsName, 0);
|
||
$value =~ /^.*\d{2},(.*),(on|off|DEF)/xms;
|
||
push(@timers_unsortet,$1.','.ReadingsVal($name, $readingsName, 0).",$readingsName"); # unsort Reading Wert in Array
|
||
$array_diff_cnt1++;
|
||
$array_diff_cnt2 = substr($readingsName,-2) * 1;
|
||
if ($array_diff_cnt1 != $array_diff_cnt2 && $array_diff == 0) { $array_diff = 1; }
|
||
}
|
||
}
|
||
|
||
my @timers_sort = sort @timers_unsortet; # Timer in neues Array sortieren
|
||
|
||
for (my $i=0; $i<scalar(@timers_unsortet); $i++) {
|
||
if ($timers_unsortet[$i] ne $timers_sort[$i]) { $array_diff++; }
|
||
}
|
||
|
||
if ($array_diff != 0) { RemoveInternalTimer($hash, 'Timer_Check'); }
|
||
if ($array_diff == 0) { return 'cancellation! No sorting necessary.'; } # check, need action continues
|
||
|
||
for (my $i=0; $i<scalar(@timers_sort); $i++) {
|
||
readingsDelete($hash, substr($timers_sort[$i],-8)); # Readings Timer loeschen
|
||
}
|
||
|
||
for (my $i=0; $i<scalar(@timers_sort); $i++) {
|
||
$timer_nr_new = sprintf("%02s",$i + 1); # neue Timer-Nummer
|
||
if ($timers_sort[$i] =~ /^.*\d{2},(.*),(DEF),.*,(Timer_\d+)/xms) { # filtre DEF values - Perl Code (DEF must in S2 - Timer nr old $3)
|
||
Log3 $name, 4, "$name: Set | $cmd: ".$timers_sort[$i];
|
||
if (defined AttrVal($name, $3.'_set', undef)) {
|
||
Log3 $name, 4, "$name: Set | $cmd: ".$3.' remember values';
|
||
push(@userattr_values,"Timer_$timer_nr_new".','.AttrVal($name, $3.'_set',0)); # userattr value in Array with new numbre
|
||
}
|
||
Timer_delFromUserattr($hash,$3.'_set:textField-long'); # delete from userattr (old numbre)
|
||
Log3 $name, 4, "$name: Set | $cmd: added to array attr_values_names -> "."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<scalar(@attr_values_names); $i++) {
|
||
Log3 $name, 4, "$name: Set | $cmd: from array to attrib userattr -> $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<scalar(@userattr_values); $i++) {
|
||
my $timer_nr = substr($userattr_values[$i],0,8).'_set';
|
||
my $value_attr = substr($userattr_values[$i],index($userattr_values[$i],',')+1);
|
||
CommandAttr($hash,"$name $timer_nr $value_attr");
|
||
}
|
||
}
|
||
Timer_Check($hash);
|
||
}
|
||
|
||
if ($cmd eq 'addTimer') {
|
||
$Timers_Count = 0;
|
||
foreach my $d (sort keys %{$hash->{READINGS}}) {
|
||
if ($d =~ /^Timer_(\d+)$/xms) {
|
||
$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";
|
||
}
|
||
|
||
if ($Timers_diff == 0) { $Timers_Count = $Timers_Count + 1; }
|
||
readingsSingleUpdate($hash, 'Timer_'.sprintf("%02s", $Timers_Count) , $value, 1);
|
||
}
|
||
|
||
if ($cmd eq 'saveTimers') {
|
||
open(my $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+)$/xms);
|
||
}
|
||
|
||
print $SaveDoc "\n";
|
||
|
||
foreach my $e (sort keys %{$attr{$name}}) {
|
||
my $LE = '';
|
||
if (AttrVal($name, $e, undef) !~ /\n$/xms) { $LE = "\n"; }
|
||
if ($e =~ /^Timer_(\d+)_set$/xms) { print $SaveDoc $e.','.AttrVal($name, $e, undef).$LE; }
|
||
}
|
||
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
|
||
}
|
||
|
||
if ($a[0] eq '?') { return $setList; }
|
||
if (not grep { /$cmd/xms } $setList) { return "Unknown argument $cmd, choose one of $setList"; }
|
||
|
||
return;
|
||
}
|
||
|
||
#####################
|
||
sub Timer_Get {
|
||
my ( $hash, $name, $cmd, @a ) = @_;
|
||
my $list = 'loadTimers:no,yes';
|
||
my $cmd2 = $a[0];
|
||
my $Timer_cnt_name = -1;
|
||
my $room = AttrVal($name, 'room', 'Unsorted');
|
||
|
||
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 (my $InputFile, '<' ,"./FHEM/lib/$name".'_conf.txt') || return "ERROR: No file $name".'_conf.txt found in ./FHEM/lib directory from FHEM!';
|
||
while (<$InputFile>) {
|
||
$line++;
|
||
# Timer_04,alle,alle,alle,12,00,00,Update FHEM,Def,1,0,0,0,0,0,0,1
|
||
if ($_ =~ /^(.*),.*,.*,.*,.*,.*,.*,.*,(.*),.*,.*,.*,.*,.*,.*,.*,[0-1]$/xms) {
|
||
chomp ($_); # Zeilenende entfernen
|
||
$_ =~ s/,Def,/,DEF,/gxms if ($2 =~ /Def/xms); # 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++) {
|
||
if ($i == 0 && $values[0] !~ /^Timer_\d{2}$/xms) { $error.= '- '.$values[0]." is no $name description"; }
|
||
if ($i == 1 && $values[1] !~ /^\d{4}$|^$description_all$/xms) { $error.= '- '.$values[1].' invalid value'; }
|
||
|
||
if ($i >= 2 && $i <= 5 && $values[$i] ne $description_all) {
|
||
if ($i >= 2 && $i <= 3 && $values[$i] !~ /^\d{2}$/xms) { $error.= '- '.$values[$i].' not decimal'; }
|
||
if ($i == 2 && ($values[2] * 1) < 1 && ($values[2] * 1) > 12) { $error.= '- '.$values[2].' wrong value (allowed 1-12)'; }
|
||
if ($i == 3 && ($values[3] * 1) < 1 && ($values[3] * 1) > 31) { $error.= '- '.$values[3].' wrong value (allowed 1-31)'; }
|
||
|
||
if ($i >= 4 && $i <= 5 && $values[$i] ne $designations[4] && $values[$i] ne $designations[5]) { # SA -> 4 SU -> 5
|
||
if ($i >= 4 && $i <= 5 && $values[$i] !~ /^\d{2}$/xms) { $error.= '- '.$values[$i].' not support'; }
|
||
if ($i == 4 && ($values[4] * 1) > 23) { $error.= '- '.$values[4].' wrong value (allowed 00-23)'; }
|
||
if ($i == 5 && ($values[5] * 1) > 59) { $error.= '- '.$values[5].' wrong value (allowed 00-59)'; }
|
||
}
|
||
}
|
||
|
||
if ($i == 6 && $values[$i] % 10 != 0) { $error.= '- '.$values[$i]." is no % 10\n"; }
|
||
if ($i == 8 && not grep { $values[$i] eq $_ } @action) { $error.= '- '.$values[$i]." is no allowed action \n"; }
|
||
if ($i >= 9 && $values[$i] ne '0' && $values[$i] ne '1') { $error.= '- '.$values[$i]." is not 0 or 1 \n"; }
|
||
|
||
if ($error ne '') {
|
||
close $InputFile;
|
||
Timer_Check($hash);
|
||
if ($error =~ /-\s(all\s|alle\s|SA\s|SU\s|SR\s|SS\s)/xms) { $error.= "\nYour language is wrong! ($language)"; }
|
||
return "ERROR: your file is NOT valid!\n\nline $line\n$error";
|
||
}
|
||
}
|
||
}
|
||
|
||
if ($_ =~ /^Timer_\d{2}_set,/xms) {
|
||
$Timer_cnt_name++;
|
||
push(@attr_values, substr($_,13));
|
||
} elsif ($_ !~ /^Timer_\d{2},/xms) {
|
||
if ($Timer_cnt_name >= 0) { $attr_values[$Timer_cnt_name].= $_; }
|
||
|
||
if ($_ =~ /.*}.*/xms){ # 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
|
||
if ($d =~ /^Timer_(\d+)$/xms) { readingsDelete($hash, $d); }
|
||
}
|
||
|
||
foreach my $f (sort keys %{$attr{$name}}) { # delete all attributes Timer_xx_set ...
|
||
if ($f =~ /^Timer_(\d+)_set$/xms) { CommandDeleteAttr($hash, $name.' '.$f); }
|
||
}
|
||
|
||
my @userattr_values = split(' ', AttrVal($name, 'userattr', 'none'));
|
||
for (my $i=0;$i<@userattr_values;$i++) { # delete userattr values Timer_xx_set:textField-long ...
|
||
if ($userattr_values[$i] =~ /^Timer_(\d+)_set:textField-long$/xms) { delFromDevAttrList($name, $userattr_values[$i]); }
|
||
}
|
||
|
||
foreach my $e (@lines_readings) { # write new readings
|
||
if ($e =~ /^Timer_\d{2},/xms) { readingsSingleUpdate($hash, substr($e,0,8) , substr($e,9,length($e)-9), 1); }
|
||
}
|
||
|
||
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=$room|$name)", "#FHEMWEB:WEB", "location.reload('true')", '');
|
||
Timer_Check($hash);
|
||
|
||
return;
|
||
}
|
||
}
|
||
|
||
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$/xms) {
|
||
my $err = perlSyntaxCheck($attrValue, ()); # check PERL Code
|
||
InternalTimer(gettimeofday()+0.1, 'Timer_PawList', $hash);
|
||
if ($err) { return $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++;
|
||
if ($cnt_attr_userattr == 1) { return 'Please execute again if you want to force the attribute to delete!'; }
|
||
$cnt_attr_userattr = 0;
|
||
}
|
||
}
|
||
|
||
if ($attrName =~ /^Timer_\d{2}_set$/xms) {
|
||
Log3 $name, 3, "$name: Attr | Attributes $attrName deleted";
|
||
InternalTimer(gettimeofday()+0.1, 'Timer_PawList', $hash);
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
#####################
|
||
sub Timer_Undef {
|
||
my ($hash, $arg) = @_;
|
||
my $name = $hash->{NAME};
|
||
|
||
RemoveInternalTimer($hash, 'Timer_Check');
|
||
Log3 $name, 4, "$name: Undef | device is delete";
|
||
|
||
return;
|
||
}
|
||
|
||
#####################
|
||
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$/xms, @{$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+$/xms && ReadingsVal($name, $d, '') =~ /,Def,/xms ) {
|
||
Log3 $name, 4, "$name: $d with ".ReadingsVal($name, $d, '').' must modify!';
|
||
my $ReadingsMod = ReadingsVal($name, $d, '');
|
||
$ReadingsMod =~ s/,Def,/,DEF,/gxms;
|
||
readingsSingleUpdate($hash, $d , $ReadingsMod, 0);
|
||
}
|
||
}
|
||
Timer_Check($hash);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
##### 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);
|
||
|
||
if ((!AttrVal($name, 'room', undef) && $FW_detail eq '') || ($Table_View_in_room eq 'off' && $FW_detail eq '')) { return $html; }
|
||
|
||
if ($Table_Style eq 'on') {
|
||
### style via CSS for Checkbox ###
|
||
$html.= '<style>
|
||
/* Labels for checked inputs */
|
||
input:checked {
|
||
}
|
||
|
||
/* Checkbox element, when checked */
|
||
input[type="checkbox"]:checked {
|
||
box-shadow: 2px -2px 1px #13ab15;
|
||
-moz-box-shadow: 2px -2px 1px #13ab15;
|
||
-webkit-box-shadow: 2px -2px 1px #13ab15;
|
||
-o-box-shadow: 2px -2px 1px #13ab15;
|
||
}
|
||
|
||
/* Checkbox element, when NO checked */
|
||
input[type="checkbox"] {
|
||
box-shadow: 2px -2px 1px #b5b5b5;
|
||
-moz-box-shadow: 2px -2px 1px #b5b5b5;
|
||
-webkit-box-shadow: 2px -2px 1px #b5b5b5;
|
||
-o-box-shadow: 2px -2px 1px #b5b5b5;
|
||
}
|
||
|
||
/* Checkbox element, when checked and hover */
|
||
input:hover[type="checkbox"]{
|
||
box-shadow: 2px -2px 1px red;
|
||
-moz-box-shadow: 2px -2px 1px red;
|
||
-webkit-box-shadow: 2px -2px 1px red;
|
||
-o-box-shadow: 2px -2px 1px red;
|
||
}
|
||
|
||
/* Save element */
|
||
input[type="reset"] {
|
||
border-radius:4px;
|
||
}
|
||
|
||
</style>';
|
||
}
|
||
|
||
Log3 $name, 5, "$name: attr2html is running";
|
||
|
||
foreach my $d (sort keys %{$hash->{READINGS}}) {
|
||
if ($d =~ /^Timer_\d+$/xms) {
|
||
$Timers_Count++;
|
||
push(@timer_nr, substr($d,index($d,'_')+1));
|
||
}
|
||
}
|
||
|
||
if ($Table_Border eq 'on') { $style_code2 = "border:2px solid #00FF00;"; }
|
||
|
||
$html.= "<div style=\"text-align: center; font-size:medium; padding: 0px 0px 6px 0px;\">$designations[0]: ".sunrise_abs($horizon)." $designations[3] | $designations[1]: ".sunset_abs($horizon)." $designations[3] | $designations[2]: ".TimeNow()." $designations[3]</div>" if($Table_Header_with_time eq 'on');
|
||
$html.= "<div id=\"table\"><table class=\"block wide\" style=\"$style_code2\">";
|
||
|
||
# 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.= "<tr class=\"odd\">";
|
||
####
|
||
|
||
if ($Table_Border_Cell eq 'on') { $style_code1 = "border:1px solid #D8D8D8;"; }
|
||
|
||
for(my $spalte = 0; $spalte <= $cnt_max - 1; $spalte++) {
|
||
if ($spalte == 0) { $html.= "<td align=\"center\" style=\"$style_code1 Padding-top:3px; Padding-left:5px; text-decoration:underline\">".$names[$spalte]."</td>"; } # Timer-Nummer - auto Breite
|
||
if ($spalte >= 1 && $spalte <= 6) { $html.= "<td align=\"center\" width=70 style=\"$style_code1 Padding-top:3px; text-decoration:underline\">".$names[$spalte]."</td>"; } # Dropdown-Listen - definierte Breite
|
||
if ($spalte > 6 && $spalte < $cnt_max - 1) { $html.= "<td align=\"center\" style=\"$style_code1 Padding-top:3px; text-decoration:underline\">".$names[$spalte]."</td>"; } # auto Breite
|
||
if ($spalte == $cnt_max - 1) { $html.= "<td align=\"center\" style=\"$style_code1 Padding-top:3px; Padding-right:5px; text-decoration:underline\">".$names[$spalte]."</td>"; } # Button save - auto Breite
|
||
}
|
||
$html.= "</tr>";
|
||
|
||
for(my $zeile = 0; $zeile < $Timers_Count; $zeile++) {
|
||
$html.= sprintf("<tr class=\"%s\">", ($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++) {
|
||
if ($zeile == $Timers_Count - 1) { $style_code1 .= "Padding-bottom:5px; "; } # letzte Zeile
|
||
|
||
if ($spalte == 1) { $html.= "<td align=\"center\" style=\"$style_code1\">".sprintf("%02s", $timer_nr[$zeile])."</td>"; } # 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
|
||
|
||
if ($spalte == 2) { $start = substr($time,0,4); } # Jahr
|
||
if ($spalte == 2) { $stop = $start + 10; } # Jahr
|
||
if ($spalte == 3 || $spalte == 4) { $start = 1; } # Monat, Tag
|
||
if ($spalte == 4) { $stop = 31; } # Tag
|
||
if ($spalte == 5) { $stop = 23; } # Stunde
|
||
if ($spalte == 6) { $stop = 59; } # Minute
|
||
|
||
if ($spalte == 7) { $stop = 50; } # Sekunde
|
||
if ($spalte == 7) { $step = 10 ; } # Sekunde
|
||
$id++;
|
||
|
||
# Log3 $name, 3, "$name: Zeile $zeile, id $id, select";
|
||
$html.= "<td align=\"center\" style=\"$style_code1\"><select id=\"".$id."\">"; # id need for java script
|
||
if ($spalte <= 6) { $html.= "<option>$description_all</option>"; } # Jahr, Monat, Tag, Stunde, Minute
|
||
if ($spalte == 5 || $spalte == 6) { # Stunde, Minute
|
||
$selected = $select_Value[$spalte-2] eq $designations[4] ? "selected=\"selected\"" : '';
|
||
$html.= "<option $selected value=\"".$designations[4]."\">".$designations[4]."</option>"; # Sonnenaufgang -> pos 4 array
|
||
$selected = $select_Value[$spalte-2] eq $designations[5] ? "selected=\"selected\"" : '';
|
||
$html.= "<option $selected value=\"".$designations[5]."\">".$designations[5]."</option>"; # Sonnenuntergang -> pos 5 array
|
||
}
|
||
for(my $k = $start ; $k <= $stop ; $k += $step) {
|
||
$selected = $select_Value[$spalte-2] eq sprintf("%02s", $k) ? "selected=\"selected\"" : '';
|
||
$html.= "<option $selected value=\"" . sprintf("%02s", $k) . "\">" . sprintf("%02s", $k) . "</option>";
|
||
}
|
||
$html.="</select></td>";
|
||
}
|
||
|
||
if ($spalte == 8) { ## Spalte Geraete
|
||
$id ++;
|
||
my $comment = '';
|
||
if (AttrVal($name,'Show_DeviceInfo','') eq 'alias') { $comment = AttrVal($select_Value[$spalte-2],'alias',''); }
|
||
if (AttrVal($name,'Show_DeviceInfo','') eq 'comment') { $comment = AttrVal($select_Value[$spalte-2],'comment',''); }
|
||
$html.= "<td align=\"center\" style=\"$style_code1\"><input size=\"$Table_Size_TextBox\" type=\"text\" placeholder=\"Timer_".($zeile + 1)."\" id=\"".$id."\" value=\"".$select_Value[$spalte-2]."\"><br><small>$comment</small></td>";
|
||
}
|
||
|
||
if ($spalte == 9) { ## DropDown-Liste Aktion
|
||
$id ++;
|
||
$html.= "<td align=\"center\" style=\"$style_code1\"><select id=\"".$id."\">"; # id need for java script
|
||
foreach (@action) {
|
||
if ($select_Value[$spalte-2] ne $_) { $html.= "<option> $_ </option>"; }
|
||
if ($select_Value[$spalte-2] eq $_) { $html.= "<option selected=\"selected\">".$select_Value[$spalte-2]."</option>"; }
|
||
}
|
||
$html.="</select></td>";
|
||
}
|
||
|
||
if ($spalte > 9 && $spalte < $cnt_max) { ## Spalte Wochentage + aktiv
|
||
$id ++;
|
||
if ($select_Value[$spalte-2] eq '0') { $html.= "<td align=\"center\" style=\"$style_code1\"><input type=\"checkbox\" name=\"days\" id=\"".$id."\" value=\"0\" onclick=\"Checkbox(".$id.")\"></td>"; }
|
||
if ($select_Value[$spalte-2] eq '1') { $html.= "<td align=\"center\" style=\"$style_code1\"><input type=\"checkbox\" name=\"days\" id=\"".$id."\" value=\"1\" onclick=\"Checkbox(".$id.")\" checked></td>"; }
|
||
}
|
||
|
||
if ($spalte == $cnt_max) { ## Button Speichern
|
||
$id ++;
|
||
$html.= "<td align=\"center\" style=\"$style_code1 Padding-right:5px\"> <INPUT type=\"reset\" onclick=\"pushed_savebutton(".$id.")\" value=\"💾\"/></td>"; # 🖫 💾
|
||
}
|
||
if ($spalte > 1 && $spalte < $cnt_max) { Log3 $name, 5, "$name: attr2html | Timer=".$timer_nr[$zeile]." ".$names[$spalte-1].'='.$select_Value[$spalte-2]." cnt_max=$cnt_max ($spalte)"; }
|
||
}
|
||
$html.= "</tr>"; ## Zeilenende
|
||
}
|
||
$html.= "</table>"; ## Tabellenende
|
||
|
||
## Tabellenende + Script
|
||
$html.= '</div>
|
||
|
||
<script>
|
||
/* checkBox Werte von Checkboxen Wochentage */
|
||
function Checkbox(id) {
|
||
var checkBox = document.getElementById(id);
|
||
if (checkBox.checked) {
|
||
checkBox.value = 1;
|
||
} else {
|
||
checkBox.value = 0;
|
||
}
|
||
}
|
||
|
||
/* Aktion wenn Speichern */
|
||
function pushed_savebutton(id) {
|
||
var allVals = [];
|
||
var timerNr = (id - 17) / 20;
|
||
allVals.push(timerNr);
|
||
var start = id - 17 + 1;
|
||
for(var i=start; i<id; i++) {
|
||
allVals.push(document.getElementById(i).value);
|
||
}
|
||
|
||
/* check Semicolon description */
|
||
var n = allVals[7].search(";"); /* return -1 if not found */
|
||
if (n != -1) {
|
||
FW_errmsg("ERROR: Semicolon not allowed in description!", 4000);
|
||
return;
|
||
}
|
||
FW_cmd(FW_root+ \'?XHR=1"'.$FW_CSRF.'"&cmd={FW_pushed_savebutton("'.$name.'","\'+allVals+\'","'.$FW_room_dupl.'")}\');
|
||
}
|
||
|
||
/* Popup DEF - Zusammenbau (PERL -> Rueckgabe -> Javascript) */
|
||
function show_popup(button) {
|
||
FW_cmd(FW_root+\'?cmd={Timer_Popup("'.$name.'","\'+button+\'")}&XHR=1"'.$FW_CSRF.'"\', function(data){popup_return(data)});
|
||
}
|
||
|
||
/* Popup DEF - Aktionen */
|
||
function popup_return(txt) {
|
||
var div = $("<div id=\"popup_return\">");
|
||
$(div).html(txt);
|
||
$("body").append(div);
|
||
var oldPos = $("body").scrollTop();
|
||
|
||
$(div).dialog({
|
||
dialogClass:"no-close", modal:true, width:"auto", closeOnEscape:false,
|
||
maxHeight:$(window).height()*0.95,
|
||
title: " infobox for user ",
|
||
buttons: [
|
||
{text:"close", click:function(){
|
||
$(this).dialog("close");
|
||
$(div).remove();
|
||
location.reload();
|
||
}}]
|
||
});
|
||
}
|
||
</script>';
|
||
|
||
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 $room = AttrVal($name, 'room', 'Unsorted');
|
||
|
||
my $timestamp = TimeNow(); # Time now -> 2016-02-16 19:34:24
|
||
my @timestamp_values = split(/-|\s|:/xms , $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";
|
||
|
||
if ($cnt_names > 17) { return 'ERROR: Comma not allowed in description!'; }
|
||
|
||
foreach my $d (sort keys %{$hash->{READINGS}}) {
|
||
if ($d =~ /^Timer_(\d+)$/xms) {
|
||
$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] )) {
|
||
if ($i == 6) { $sec = $selected_buttons[$i]; }
|
||
if ($i == 5) { $min = $selected_buttons[$i]; }
|
||
if ($i == 4) { $hour = $selected_buttons[$i]; }
|
||
if ($i == 3) { $mday = $selected_buttons[$i]; }
|
||
if ($i == 2) { $month = $selected_buttons[$i]-1; }
|
||
if ($i == 1) { $year = $selected_buttons[$i]-1900; }
|
||
}
|
||
|
||
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!';
|
||
}
|
||
}
|
||
}
|
||
|
||
if ((time() - fhemTimeLocal($sec, $min, $hour, $mday, $month - 1, $year)) > 0) { return 'ERROR: The time is in the past. Please set a time in the future!'; }
|
||
if ((fhemTimeLocal($sec, $min, $hour, $mday, $month - 1, $year) - time()) < 60) { return 'ERROR: The next switching point is too small!'; }
|
||
|
||
if ($selected_buttons[8] ne 'DEF' && ReadingsVal($name, 'Timer_'.sprintf("%02s", $timer).'_set', 0) ne '0') { readingsDelete($hash,'Timer_'.sprintf("%02s", $timer).'_set'); }
|
||
|
||
my $oldValue = ReadingsVal($name,'Timer_'.sprintf("%02s", $selected_buttons[0]) ,0);
|
||
my $newValue = substr($selected_buttons,(index($selected_buttons,',') + 1));
|
||
if ($oldValue ne $newValue && $FW_room_dupl) { $reload++; }
|
||
|
||
my @Value_split = split(/,/xms , $oldValue);
|
||
$oldValue = $Value_split[7];
|
||
|
||
@Value_split = split(/,/xms , $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,/gxms;
|
||
}
|
||
|
||
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';
|
||
if (AttrVal($name, 'userattr', undef)) { Timer_delFromUserattr($hash,$userattrName); }
|
||
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=$room|$name)", "#FHEMWEB:WEB", "show_popup(".$selected_buttons[0].")", '');
|
||
if ($reload != 0) { $reload = 0; } # reset, need to right running
|
||
}
|
||
|
||
## refresh site, need for userattr & right view checkboxes ##
|
||
FW_directNotify("FILTER=(room=$room|$name)", "#FHEMWEB:WEB", "location.reload('true')", '') if ($reload != 0);
|
||
|
||
if ($selected_buttons[16] eq '1' && ReadingsVal($name, 'internalTimer', 'stop') eq 'stop') { Timer_Check($hash); }
|
||
|
||
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.= "<p style=\"text-decoration-line: underline;\">Hinweis:</p>";
|
||
$ret.= "Das Attribut <code>userattr</code> wurde automatisch angepasst.<br>";
|
||
$ret.= "Um <code>DEF</code> zu definieren, geben Sie bitte das FHEM Kommando oder den PERL Code in das Attribut <code>Timer_$selected_button"."_set</code> ein.<br>";
|
||
$ret.= "<small><code>attr $name Timer_$selected_button"."_set \"DEF Code\"</code> (ohne \" \")</small><br><br>";
|
||
$ret.= "Die Eingabe entspricht der FHEM-Befehlszeile im Browser.<br>";
|
||
$ret.= "<p style=\"text-decoration-line: underline;\">DEF Beispiele:</p>";
|
||
$ret.= "• FHEM Kommando: <code>set TYPE=IT:FILTER=room=03_Stube.*:FILTER=state=on off</code><br>";
|
||
$ret.= "• PERL Code: <code>{ Log 1, \"$name: schaltet jetzt\" }</code><br><br>";
|
||
$ret.= "<i>Weitere Beispiele oder Intervallschaltungen finden Sie in der gerätespezifischen Hilfe.</i><br><br>";
|
||
$ret.= "Klicken Sie auf close, um fortzufahren.";
|
||
} else {
|
||
$ret.= "<p style=\"text-decoration-line: underline;\">Note:</p>";
|
||
$ret.= "The attribute <code>userattr</code> has been automatically revised.<br>";
|
||
$ret.= "To define <code>DEF</code>, please input the FHEM command or PERL code in attribute <code>Timer_$selected_button"."_set</code>.<br>";
|
||
$ret.= "<small><code>attr $name Timer_$selected_button"."_set \"DEF code\"</code> (without \" \")</small><br><br>";
|
||
$ret.= "The input is made equal to the FHEM command line in the browser.<br>";
|
||
$ret.= "<p style=\"text-decoration-line: underline;\">DEF examples:</p>";
|
||
$ret.= "• FHEM command: <code>set TYPE=IT:FILTER=room=03_Stube.*:FILTER=state=on off</code><br>";
|
||
$ret.= "• PERL code: <code>{ Log 1, \"$name: switch now\" }</code><br><br>";
|
||
$ret.= "<i>For more examples or interval switching please look into the Device specific help.</i><br><br>";
|
||
$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/xms) {
|
||
delFromDevAttrList($name, $deleteTimer);
|
||
Log3 $name, 5, "$name: delete $deleteTimer from userattr Attributes";
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
### for Check ###
|
||
sub Timer_Check {
|
||
my ($hash) = @_;
|
||
my $name = $hash->{NAME};
|
||
my @timestamp_values = split(/-|\s|:/xms , TimeNow()); # Time now (2016-02-16 19:34:24) splitted in array
|
||
my $dayOfWeek = strftime('%w', localtime); # Wochentag
|
||
|
||
if ($dayOfWeek eq '0') { $dayOfWeek = 7; } # 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+$/xms) {
|
||
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++;
|
||
if ($values[3] eq $designations[4]) { $values[3] = $sunriseValues[0]; } # Stunde | Sonnenaufgang -> pos 4 array
|
||
if ($values[4] eq $designations[4]) { $values[4] = $sunriseValues[1]; } # Minute | Sonnenaufgang -> pos 4 array
|
||
if ($values[3] eq $designations[5]) { $values[3] = $sunsetValues[0]; } # Stunde | Sonnenuntergang -> pos 5 array
|
||
if ($values[4] eq $designations[5]) { $values[4] = $sunsetValues[1]; } # Stunde | Sonnenuntergang -> pos 5 array
|
||
|
||
for (my $i = 0;$i < 5;$i++) { # Jahr, Monat, Tag, Stunde, Minute
|
||
if ($values[$i] ne $description_all && $values[$i] ne $timestamp_values[$i]) { $set = 0; }
|
||
}
|
||
|
||
if ($values[(($dayOfWeek*1) + 7)] eq '0') { $set = 0; } # Wochentag
|
||
if ($values[5] eq '00' && $timestamp_values[5] ne '00') { $set = 0; } # Sekunde (Intervall 60)
|
||
if ($values[5] ne '00' && $timestamp_values[5] ne $values[5]) { $set = 0; } # Sekunde (Intervall 10)
|
||
if ($values[5] ne '00') { $intervall = 10; }
|
||
|
||
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])";
|
||
if ($values[7] ne 'DEF') { CommandSet($hash, $values[6].' '.$values[7]); }
|
||
# $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)));
|
||
if ($ret) { Log3 $name, 3, "$name: $d\_set - ERROR: $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);
|
||
if ($cnt_activ > 0) { InternalTimer(gettimeofday()+$intervall, 'Timer_Check', $hash, 0); }
|
||
|
||
if ($cnt_activ == 0 && ReadingsVal($name, 'internalTimer', 'stop') ne 'stop') { $state = 'no timer active'; }
|
||
if (defined $state) { readingsBulkUpdate($hash, 'state' , "$state", 1); }
|
||
if ($cnt_activ == 0 && ReadingsVal($name, 'internalTimer', 'stop') ne 'stop') { readingsBulkUpdate($hash, 'internalTimer' , 'stop'); }
|
||
if ($cnt_activ > 0) { readingsBulkUpdate($hash, 'internalTimer' , $intervall, 0); }
|
||
readingsEndUpdate($hash, 1);
|
||
|
||
return;
|
||
}
|
||
|
||
### 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+)$/xms) {
|
||
my @values = split(',' , ReadingsVal($name, $d, ''));
|
||
### clear value, "Probably associated with" ne DEF
|
||
if ($values[7] ne 'DEF') {
|
||
if (not grep { /$values[6]/xms } $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/xms;
|
||
if ($2) {
|
||
Log3 $name, 5, "$name: Timer_PawList | found in DEF: ".$2;
|
||
if (not grep { /$2/xms } $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 {
|
||
if(ReadingsVal($name, '.associatedWith', undef)) { readingsDelete($hash,'.associatedWith'); }
|
||
}
|
||
## current list "Probably associated with" finish ##
|
||
return;
|
||
}
|
||
|
||
##########################################
|
||
|
||
# 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
|
||
|
||
<a name="Timer"></a>
|
||
<h3>Timer Modul</h3>
|
||
<ul>
|
||
The timer module is a programmable timer.<br><br>
|
||
In Frontend you can define new times and actions. The smallest possible definition of an action is a 10 second interval.<br>
|
||
You can use the dropdown menu to make the settings for the time switch point. Only after clicking on the <code> Save </code> 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.<br><br>
|
||
In addition, you can use the selection <code> SR </code> and <code> SS </code> in the hour and minute column. These rumps represent the time of sunrise and sunset.<br>
|
||
For example, if you select at minute <code> SS </code>, you have set the minutes of the sunset as the value. As soon as you set the value to <code> SS </code> at hour and minute
|
||
the timer uses the calculated sunset time at your location. <u><i>(For this calculation the FHEM global attributes latitude and longitude are necessary!)</u></i>
|
||
|
||
<br><br>
|
||
<u>Programmable actions are currently:</u><br>
|
||
<ul>
|
||
<li><code> on | off</code> - The states must be supported by the device</li>
|
||
<li><code>DEF</code> - for a PERL code or a FHEM command <font color="red">*</font color> <br><br>
|
||
<ul><u>example for DEF:</u>
|
||
<li><code>{ Log 1, "Timer: now switch" }</code> (PERL code)</li>
|
||
<li><code>update</code> (FHEM command)</li>
|
||
<li><code>trigger Timer state:ins Log geschrieben</code> (FHEM-command)</li>
|
||
<li><code>set TYPE=IT:FILTER=room=Stube.*:FILTER=state=on off</code> (FHEM-command)</li></ul>
|
||
</li>
|
||
</ul>
|
||
<br>
|
||
|
||
<b><font color="red">*</font color></b> To do this, enter the code to be executed in the respective attribute. example: <code>Timer_03_set</code>
|
||
|
||
<br><br>
|
||
<u>Interval switching of the timer is only possible in the following variants:</u><br>
|
||
<ul>
|
||
<li>minute, define second and set all other values (minute, hour, day, month, year) to <code>all</code></li>
|
||
<li>hourly, define second + minute and set all other values (hour, day, month, year) to <code>all</code></li>
|
||
<li>daily, define second + minute + hour and set all other values (day, month, year) to <code>all</code></li>
|
||
<li>monthly, define second + minute + hour + day and set all other values (month, year) to <code>all</code></li>
|
||
<li>annually, define second + minute + hour + day + month and set the value (year) to <code>all</code></li>
|
||
<li>sunrise, define second & define minute + hour with <code>SR</code> and set all other values (day, month, year) to <code>all</code></li>
|
||
<li>sunset, define second & define minute + hour with <code>SS</code> and set all other values (day, month, year) to <code>all</code></li>
|
||
</ul>
|
||
<br>
|
||
Any interval circuits can be defined in which in the associated timer attribute e.g. the following Perl code is inserted:<br>
|
||
<code>{if ($min % 5 == 0) {fhem("set FS10_6_11 toggle");}}</code><br>
|
||
This timer would run every 5 minutes if the timer is configured to run in a minute as described.<br><br>
|
||
The following variables for time and date are available:<br>
|
||
<code>$sec, $min, $hour, $mday, $month, $year, $wday, $yday, $isdst, $hms, $we, $today</code><br>
|
||
This makes it possible, for example, to have a timer run every Sunday at 15:30:00.
|
||
<br><br>
|
||
|
||
<b>Define</b><br>
|
||
<ul><code>define <NAME> Timer</code><br><br>
|
||
<u>example:</u>
|
||
<ul>
|
||
define timer Timer
|
||
</ul>
|
||
</ul><br>
|
||
|
||
<b>Set</b><br>
|
||
<ul>
|
||
<a name="addTimer"></a>
|
||
<li>addTimer: Adds a new timer</li><a name=""></a>
|
||
<a name="deleteTimer"></a>
|
||
<li>deleteTimer: Deletes the selected timer</li><a name=""></a>
|
||
<a name="saveTimers"></a>
|
||
<li>saveTimers: Saves the settings in file <code>Timers.txt</code> on directory <code>./FHEM/lib</code>.</li>
|
||
<a name="sortTimer"></a>
|
||
<li>sortTimer: Sorts the saved timers alphabetically.</li>
|
||
</ul><br><br>
|
||
|
||
<b>Get</b><br>
|
||
<ul>
|
||
<a name="loadTimers"></a>
|
||
<li>loadTimers: Loads a saved configuration from file <code>Timers.txt</code> from directory <code>./FHEM/lib</code>.</li><a name=""></a>
|
||
</ul><br><br>
|
||
|
||
<b>Attribute</b><br>
|
||
<ul><li><a href="#disable">disable</a></li></ul><br>
|
||
<ul><li><a name="stateFormat">stateFormat</a><br>
|
||
It is used to format the value <code>state</code><br>
|
||
<u>example:</u> <code>{ ReadingsTimestamp($name, "state", 0) ."&nbsp;- ". ReadingsVal($name, "state", "none");}</code><br>
|
||
- will format to output: <code>2019-09-19 17:51:44 - Timer_04 saved</code></li><a name=" "></a></ul><br>
|
||
<ul><li><a name="Table_Border">Table_Border</a><br>
|
||
Shows the table border. (on | off = default)</li><a name=" "></a></ul><br>
|
||
<ul><li><a name="Table_Border_Cell">Table_Border_Cell</a><br>
|
||
Shows the cell frame. (on | off = default)</li><a name=" "></a></ul><br>
|
||
<ul><li><a name="Table_Header_with_time">Table_Header_with_time</a><br>
|
||
Shows or hides the sunrise and sunset with the local time above the table. (on | off, standard off)</li><a name=" "></a></ul><br>
|
||
<ul><li><a name="Table_Size_TextBox">Table_Size_TextBox</a><br>
|
||
Correction value to change the length of the text box for the device name / designation. (default 20)</li><a name=" "></a></ul><br>
|
||
<ul><li><a name="Table_Style">Table_Style</a><br>
|
||
Turns on the defined table style. (on | off, default off)</li><a name=" "></a></ul><br>
|
||
<ul><li><a name="Table_View_in_room">Table_View_in_room</a><br>
|
||
Toggles the tables UI in the room view on or off. (on | off, standard on)<br>
|
||
<small><i>In the room <code> Unsorted </code> the table UI is always switched off!</i></small></li><a name=" "></a></ul><br>
|
||
<ul><li><a name="Timer_preselection">Timer_preselection</a><br>
|
||
Sets the input values for a new timer to the current time. (on | off = default)</li><a name=" "></a></ul><br>
|
||
<ul><li><a name="Timer_xx_set">Timer_xx_set</a><br>
|
||
Location for the FHEM command or PERL code of the timer (xx has the numerical value of 01-99). WITHOUT this attribute, which <b> only appears if action <code> DEF </code> is set </b>,
|
||
The module does not process FHEM command or PERL code from the user. <code><font color="red">*</font color> </code></li><a name=" "></a></ul><br>
|
||
<ul><li><a name="Offset_Horizon">Offset_Horizon</a><br>
|
||
Different elevation angles are used to calculate sunrise and sunset times.<br>
|
||
(REAL = 0°, CIVIL = -6°, NAUTIC = -12°, ASTRONOMIC = -18°, default REAL)</li><a name=" "></a></ul><br>
|
||
<ul><li><a name="Show_DeviceInfo">Show_DeviceInfo</a><br>
|
||
Shows the additional information (alias | comment, default off)</li><a name=" "></a></ul><br>
|
||
<br>
|
||
|
||
<b><i>Generated readings</i></b><br>
|
||
<ul><li>Timer_xx<br>
|
||
Memory values of the individual timer</li><br>
|
||
<li>internalTimer<br>
|
||
State of the internal timer (stop or Interval until the next call)</li><br><br></ul>
|
||
|
||
<b><i><u>Hints:</u></i></b><br>
|
||
<ul><li>Entries in the system logfile like: <code>2019.09.20 22:15:01 3: Timer: time difference too large! interval=59, Sekunde=01</code> say that the timer has recalculated the time.</li></ul>
|
||
|
||
</ul>
|
||
=end html
|
||
|
||
|
||
=begin html_DE
|
||
|
||
<a name="Timer"></a>
|
||
<h3>Timer Modul</h3>
|
||
<ul>
|
||
Das Timer Modul ist eine programmierbare Schaltuhr.<br><br>
|
||
Im Frontend können Sie neue Zeitpunkte und Aktionen definieren. Die kleinstmögliche Definition einer Aktion ist ein 10 Sekunden Intervall.<br>
|
||
Mittels der Dropdown Menüs können Sie die Einstellungen für den Zeitschaltpunkt vornehmen. Erst nach dem drücken auf den <code>Speichern</code> Knopf wird die Einstellung übernommen.<br><br>
|
||
In der DropDown-Liste stehen jeweils die Zahlenwerte für Jahr / Monat / Tag / Stunde / Minute / Sekunde zur Auswahl.<br>
|
||
Zusätzlich können Sie in der Spalte Stunde und Minute die Auswahl <code>SA</code> und <code>SU</code> nutzen. Diese Kürzel stehen für den Zeitpunkt Sonnenaufgang und Sonnenuntergang.<br>
|
||
Wenn sie Beispielsweise bei Minute <code>SU</code> auswählen, so haben Sie die Minuten des Sonnenuntergang als Wert gesetzt. Sobald Sie bei Stunde und Minute den Wert auf <code>SU</code>
|
||
stellen, so nutzt der Timer den errechnenten Zeitpunkt Sonnenuntergang an Ihrem Standort. <u><i>(Für diese Berechnung sind die FHEM globalen Attribute latitude und longitude notwendig!)</u></i>
|
||
|
||
<br><br>
|
||
<u>Programmierbare Aktionen sind derzeit:</u><br>
|
||
<ul>
|
||
<li><code> on | off</code> - Die Zustände müssen von dem zu schaltenden Device unterstützt werden</li>
|
||
<li><code>DEF</code> - für einen PERL-Code oder ein FHEM Kommando <font color="red">*</font color> <br><br>
|
||
<ul><u>Beispiele für DEF:</u>
|
||
<li><code>{ Log 1, "Timer: schaltet jetzt" }</code> (PERL-Code)</li>
|
||
<li><code>update</code> (FHEM-Kommando)</li>
|
||
<li><code>trigger Timer state:ins Log geschrieben</code> (FHEM-Kommando)</li>
|
||
<li><code>set TYPE=IT:FILTER=room=Stube.*:FILTER=state=on off</code> (FHEM-Kommando)</li></ul>
|
||
</li>
|
||
</ul>
|
||
<br>
|
||
|
||
<b><font color="red">*</font color></b> Hierfür hinterlegen Sie den auszuführenden PERL-Code in das jeweilige Attribut. Bsp.: <code>Timer_03_set</code>
|
||
|
||
<br><br>
|
||
<u>Eine Intervallschaltung des Timer ist nur möglich in folgenden Varianten:</u><br>
|
||
<ul>
|
||
<li>minütlich, Sekunde definieren und alle anderen Werte (Minute, Stunde, Tag, Monat, Jahr) auf <code>alle</code> setzen</li>
|
||
<li>stündlich, Sekunde + Minute definieren und alle anderen Werte (Stunde, Tag, Monat, Jahr) auf <code>alle</code> setzen</li>
|
||
<li>täglich, Sekunde + Minute + Stunde definieren und alle anderen Werte (Tag, Monat, Jahr) auf <code>alle</code> setzen</li>
|
||
<li>monatlich, Sekunde + Minute + Stunde + Tag definieren und alle anderen Werte (Monat, Jahr) auf <code>alle</code> setzen</li>
|
||
<li>jährlich, Sekunde + Minute + Stunde + Tag + Monat definieren und den Wert (Jahr) auf <code>alle</code> setzen</li>
|
||
<li>Sonnenaufgang, Sekunde definieren & Minute + Stunde definieren mit <code>SA</code> und alle anderen Werte (Tag, Monat, Jahr) auf <code>alle</code> setzen</li>
|
||
<li>Sonnenuntergang, Sekunde definieren & Minute + Stunde definieren mit <code>SU</code> und alle anderen Werte (Tag, Monat, Jahr) auf <code>alle</code> setzen</li>
|
||
</ul>
|
||
<br>
|
||
Beliebige Intervallschaltungen können definiert werden, in dem im zugehörigen Timer-Attribut z.B. folgender Perl-Code eingefügt wird:<br>
|
||
<code>{if ($min % 5 == 0) {fhem("set FS10_6_11 toggle");}}</code><br>
|
||
Dieser Timer würde dann aller 5 Minuten ausgeführt, wenn der Timer wie beschrieben auf minütliches Ausführen konfiguriert ist.<br><br>
|
||
Folgende Variablen für Zeit- und Datumsangaben stehen zur Verfügung:<br>
|
||
<code>$sec, $min, $hour, $mday, $month, $year, $wday, $yday, $isdst, $hms, $we, $today</code><br>
|
||
Damit ist es möglich, einen Timer beispielsweise nur jeden Sonntag um 15:30:00 Uhr etwas ausführen zu lassen.
|
||
<br><br>
|
||
|
||
<b>Define</b><br>
|
||
<ul><code>define <NAME> Timer</code><br><br>
|
||
<u>Beispiel:</u>
|
||
<ul>
|
||
define Schaltuhr Timer
|
||
</ul>
|
||
</ul><br>
|
||
|
||
<b>Set</b><br>
|
||
<ul>
|
||
<a name="addTimer"></a>
|
||
<li>addTimer: Fügt einen neuen Timer hinzu.</li><a name=""></a>
|
||
<a name="deleteTimer"></a>
|
||
<li>deleteTimer: Löscht den ausgewählten Timer.</li><a name=""></a>
|
||
<a name="saveTimers"></a>
|
||
<li>saveTimers: Speichert die eingestellten Timer in der Datei <code>Timers.txt</code> im Verzeichnis <code>./FHEM/lib</code>. </li>
|
||
<a name="sortTimer"></a>
|
||
<li>sortTimer: Sortiert die gespeicherten Timer alphabetisch.</li>
|
||
</ul><br><br>
|
||
|
||
<b>Get</b><br>
|
||
<ul>
|
||
<a name="loadTimers"></a>
|
||
<li>loadTimers: Läd eine gespeicherte Konfiguration aus der Datei <code>Timers.txt</code> aus dem Verzeichnis <code>./FHEM/lib</code>.</li><a name=""></a>
|
||
</ul><br><br>
|
||
|
||
<b>Attribute</b><br>
|
||
<ul><li><a href="#disable">disable</a></li></ul><br>
|
||
<ul><li><a name="stateFormat">stateFormat</a><br>
|
||
Es dient zur Formatierung des Wertes <code>state</code><br>
|
||
<u>Beispiel:</u> <code>{ ReadingsTimestamp($name, "state", 0) ."&nbsp;- ". ReadingsVal($name, "state", "none");}</code><br>
|
||
- wird zur formatieren Ausgabe: <code>2019-09-19 17:51:44 - Timer_04 saved</code></li><a name=" "></a></ul><br>
|
||
<ul><li><a name="Table_Border">Table_Border</a><br>
|
||
Blendet den Tabellenrahmen ein. (on | off, standard off)</li><a name=" "></a></ul><br>
|
||
<ul><li><a name="Table_Border_Cell">Table_Border_Cell</a><br>
|
||
Blendet den Cellrahmen ein. (on | off, standard off)</li><a name=" "></a></ul><br>
|
||
<ul><li><a name="Table_Header_with_time">Table_Header_with_time</a><br>
|
||
Blendet den Sonnenauf und Sonnenuntergang mit der lokalen Zeit über der Tabelle ein oder aus. (on | off, standard off)</li><a name=" "></a></ul><br>
|
||
<ul><li><a name="Table_Size_TextBox">Table_Size_TextBox</a><br>
|
||
Korrekturwert um die Länge der Textbox für die Gerätenamen / Bezeichung zu verändern. (standard 20)</li><a name=" "></a></ul><br>
|
||
<ul><li><a name="Table_Style">Table_Style</a><br>
|
||
Schaltet den definierten Tabellen-Style ein. (on | off, standard off)</li><a name=" "></a></ul><br>
|
||
<ul><li><a name="Table_View_in_room">Table_View_in_room</a><br>
|
||
Schaltet das Tabellen UI in der Raumansicht an oder aus. (on | off, standard on)<br>
|
||
<small><i>Im Raum <code>Unsorted</code> ist das Tabellen UI immer abgeschalten!</i></small></li><a name=" "></a></ul><br>
|
||
<ul><li><a name="Timer_preselection">Timer_preselection</a><br>
|
||
Setzt die Eingabewerte bei einem neuen Timer auf die aktuelle Zeit. (on | off, standard off)</li><a name=" "></a></ul><br>
|
||
<ul><li><a name="Timer_xx_set">Timer_xx_set</a><br>
|
||
Speicherort für das FHEM-Kommando oder den PERL-Code des Timers (xx hat den Zahlenwert von 01-99). OHNE dieses Attribut, welches <b>nur erscheint wenn die Aktion <code> DEF </code> eingestellt </b> ist,
|
||
verarbeitet das Modul kein Kommando oder PERL-Code vom Benutzer. <code> <font color="red">*</font color> </code></li><a name=" "></a></ul><br>
|
||
<ul><li><a name="Offset_Horizon">Offset_Horizon</a><br>
|
||
Für die Berechnung der Zeiten von Sonnenaufgang und Sonnenuntergang werden verschiedene Höhenwinkel verwendet.<br>
|
||
(REAL = 0°, CIVIL = -6°, NAUTIC = -12°, ASTRONOMIC = -18°, Standard REAL)</li><a name=" "></a></ul><br>
|
||
<ul><li><a name="Show_DeviceInfo">Show_DeviceInfo</a><br>
|
||
Blendet die Zusatzinformation ein. (alias | comment, standard off)</li><a name=" "></a></ul><br>
|
||
<br>
|
||
|
||
<b><i>Generierte Readings</i></b><br>
|
||
<ul><li>Timer_xx<br>
|
||
Speicherwerte des einzelnen Timers</li><br>
|
||
<li>internalTimer<br>
|
||
Zustand des internen Timers (stop oder Intervall bis zum nächsten Aufruf)</li><br><br></ul>
|
||
|
||
<b><i><u>Hinweise:</u></i></b><br>
|
||
<ul><li>Einträge im Systemlogfile wie: <code>2019.09.20 22:15:01 3: Timer: time difference too large! interval=59, Sekunde=01</code> sagen aus, das der Timer die Zeit neu berechnet hat.</li></ul>
|
||
|
||
</ul>
|
||
=end html_DE
|
||
|
||
# Ende der Commandref
|
||
=cut |