fhem-mirror/FHEM/88_Timer.pm
HomeAuto_User a5333e367f 88_Timer.pm: revised code PERL::Critic
git-svn-id: https://svn.fhem.de/fhem/trunk/fhem@24841 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2021-08-09 20:01:27 +00:00

1284 lines
60 KiB
Perl
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#################################################################
# $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]&nbsp;&nbsp;|&nbsp;&nbsp;$designations[1]: ".sunset_abs($horizon)." $designations[3]&nbsp;&nbsp;|&nbsp;&nbsp;$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=\"&#128190;\"/></td>"; # &#128427; &#128190;
}
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.= "&#8226; FHEM Kommando: <code>set TYPE=IT:FILTER=room=03_Stube.*:FILTER=state=on off</code><br>";
$ret.= "&#8226; PERL Code: <code>{ Log 1, \"$name: schaltet jetzt\" }</code><br><br>";
$ret.= "<i>Weitere Beispiele oder Intervallschaltungen finden Sie in der ger&auml;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.= "&#8226; FHEM command: <code>set TYPE=IT:FILTER=room=03_Stube.*:FILTER=state=on off</code><br>";
$ret.= "&#8226; 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 &lt;NAME&gt; 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) ."&amp;nbsp;- ". ReadingsVal($name, "state", "none");}</code><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- 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&ouml;nnen Sie neue Zeitpunkte und Aktionen definieren. Die kleinstm&ouml;gliche Definition einer Aktion ist ein 10 Sekunden Intervall.<br>
Mittels der Dropdown Men&uuml;s k&ouml;nnen Sie die Einstellungen f&uuml;r den Zeitschaltpunkt vornehmen. Erst nach dem dr&uuml;cken auf den <code>Speichern</code> Knopf wird die Einstellung &uuml;bernommen.<br><br>
In der DropDown-Liste stehen jeweils die Zahlenwerte f&uuml;r Jahr / Monat / Tag / Stunde / Minute / Sekunde zur Auswahl.<br>
Zus&auml;tzlich k&ouml;nnen Sie in der Spalte Stunde und Minute die Auswahl <code>SA</code> und <code>SU</code> nutzen. Diese K&uuml;rzel stehen f&uuml;r den Zeitpunkt Sonnenaufgang und Sonnenuntergang.<br>
Wenn sie Beispielsweise bei Minute <code>SU</code> ausw&auml;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&uuml;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&auml;nde m&uuml;ssen von dem zu schaltenden Device unterst&uuml;tzt werden</li>
<li><code>DEF</code> - f&uuml;r einen PERL-Code oder ein FHEM Kommando <font color="red">*</font color> <br><br>
<ul><u>Beispiele f&uuml;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&uuml;r hinterlegen Sie den auszuf&uuml;hrenden PERL-Code in das jeweilige Attribut. Bsp.: <code>Timer_03_set</code>
<br><br>
<u>Eine Intervallschaltung des Timer ist nur m&ouml;glich in folgenden Varianten:</u><br>
<ul>
<li>min&uuml;tlich, Sekunde definieren und alle anderen Werte (Minute, Stunde, Tag, Monat, Jahr) auf <code>alle</code> setzen</li>
<li>st&uuml;ndlich, Sekunde + Minute definieren und alle anderen Werte (Stunde, Tag, Monat, Jahr) auf <code>alle</code> setzen</li>
<li>t&auml;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&auml;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&ouml;nnen definiert werden, in dem im zugeh&ouml;rigen Timer-Attribut z.B. folgender Perl-Code eingef&uuml;gt wird:<br>
<code>{if ($min % 5 == 0) {fhem("set FS10_6_11 toggle");}}</code><br>
Dieser Timer w&uuml;rde dann aller 5 Minuten ausgef&uuml;hrt, wenn der Timer wie beschrieben auf min&uuml;tliches Ausf&uuml;hren konfiguriert ist.<br><br>
Folgende Variablen f&uuml;r Zeit- und Datumsangaben stehen zur Verf&uuml;gung:<br>
<code>$sec, $min, $hour, $mday, $month, $year, $wday, $yday, $isdst, $hms, $we, $today</code><br>
Damit ist es m&ouml;glich, einen Timer beispielsweise nur jeden Sonntag um 15:30:00 Uhr etwas ausf&uuml;hren zu lassen.
<br><br>
<b>Define</b><br>
<ul><code>define &lt;NAME&gt; 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&uuml;gt einen neuen Timer hinzu.</li><a name=""></a>
<a name="deleteTimer"></a>
<li>deleteTimer: L&ouml;scht den ausgew&auml;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&auml;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) ."&amp;nbsp;- ". ReadingsVal($name, "state", "none");}</code><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- 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 &uuml;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&auml;nge der Textbox f&uuml;r die Ger&auml;tenamen / Bezeichung zu ver&auml;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&uuml;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&uuml;r die Berechnung der Zeiten von Sonnenaufgang und Sonnenuntergang werden verschiedene H&ouml;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&auml;chsten Aufruf)</li><br><br></ul>
<b><i><u>Hinweise:</u></i></b><br>
<ul><li>Eintr&auml;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