mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-04-28 05:01:57 +00:00
98_Heating_Control, 98_WeekdayTimer, 59_Twilight: big refactoring update with some minor improvements
git-svn-id: https://svn.fhem.de/fhem/trunk@8305 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
3131a14ac2
commit
76df7c129b
@ -364,7 +364,7 @@ sub Twilight_WeatherCallback(@) {
|
||||
################################################################################
|
||||
sub Twilight_StandardTimerSet($) {
|
||||
my ($hash) = @_;
|
||||
my $midnight = time() - Twilight_midnight_seconds(time()) + 24*3600 + 30;
|
||||
my $midnight = time() - Twilight_midnight_seconds(time()) + 24*3600 + 1;
|
||||
|
||||
myRemoveInternalTimer ("Midnight", $hash);
|
||||
myInternalTimer ("Midnight", $midnight, "Twilight_Midnight", $hash, 0);
|
||||
|
@ -3,7 +3,6 @@
|
||||
#
|
||||
# 98_Heating_Control.pm
|
||||
# written by Dietmar Ortmann
|
||||
# modified by Tobias Faust
|
||||
#
|
||||
# This file is part of fhem.
|
||||
#
|
||||
@ -26,27 +25,25 @@ package main;
|
||||
use strict;
|
||||
use warnings;
|
||||
use POSIX;
|
||||
use Time::Local 'timelocal_nocheck';
|
||||
|
||||
#####################################
|
||||
########################################################################
|
||||
sub Heating_Control_Initialize($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
||||
if(!$modules{Twilight}{LOADED} && -f "$attr{global}{modpath}/FHEM/59_Twilight.pm") {
|
||||
my $ret = CommandReload(undef, "59_Twilight");
|
||||
if(!$modules{WeekdayTimer}{LOADED} && -f "$attr{global}{modpath}/FHEM/98_WeekdayTimer.pm") {
|
||||
my $ret = CommandReload(undef, "98_WeekdayTimer");
|
||||
Log3 undef, 1, $ret if($ret);
|
||||
}
|
||||
|
||||
# Consumer
|
||||
$hash->{SetFn} = "Heating_Control_Set";
|
||||
$hash->{AttrFn} = "Heating_Control_Attr";
|
||||
$hash->{DefFn} = "Heating_Control_Define";
|
||||
$hash->{UndefFn} = "Heating_Control_Undef";
|
||||
$hash->{GetFn} = "Heating_Control_Get";
|
||||
$hash->{AttrFn} = "Heating_Control_Attr";
|
||||
$hash->{UpdFn} = "Heating_Control_Update";
|
||||
$hash->{AttrList}= "disable:0,1 delayedExecutionCond windowSensor ".
|
||||
$readingFnAttributes;
|
||||
$readingFnAttributes;
|
||||
}
|
||||
################################################################################
|
||||
sub Heating_Control_Set($@) {
|
||||
@ -67,519 +64,32 @@ sub Heating_Control_Set($@) {
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
################################################################################
|
||||
sub Heating_Control_Get($@)
|
||||
{
|
||||
my ($hash, @a) = @_;
|
||||
return "argument is missing" if(int(@a) != 2);
|
||||
|
||||
$hash->{LOCAL} = 1;
|
||||
delete $hash->{LOCAL};
|
||||
my $reading= $a[1];
|
||||
my $value;
|
||||
|
||||
if(defined($hash->{READINGS}{$reading})) {
|
||||
$value= $hash->{READINGS}{$reading}{VAL};
|
||||
} else {
|
||||
return "no such reading: $reading";
|
||||
}
|
||||
return "$a[0] $reading => $value";
|
||||
########################################################################
|
||||
sub Heating_Control_Get($@) {
|
||||
return WeekdayTimer_Get($@);
|
||||
}
|
||||
################################################################################
|
||||
sub Heating_Control_Define($$)
|
||||
{
|
||||
########################################################################
|
||||
sub Heating_Control_Define($$){
|
||||
my ($hash, $def) = @_;
|
||||
|
||||
my %longDays = ( "de" => ["Sonntag", "Montag","Dienstag","Mittwoch", "Donnerstag","Freitag", "Samstag" ],
|
||||
"en" => ["Sunday", "Monday","Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
|
||||
"fr" => ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi","Samedi" ]);
|
||||
my %shortDays = ( "de" => ["so","mo","di","mi","do","fr","sa"],
|
||||
"en" => ["su","mo","tu","we","th","fr","sa"],
|
||||
"fr" => ["di","lu","ma","me","je","ve","sa"]);
|
||||
|
||||
my @a = split("[ \t]+", $def);
|
||||
|
||||
return "Usage: define <name> $hash->{TYPE} <device> <language> <switching times> <condition|command>"
|
||||
if(@a < 4);
|
||||
|
||||
my $name = shift @a;
|
||||
my $type = shift @a;
|
||||
my $device = shift @a;
|
||||
|
||||
my @switchingtimes;
|
||||
my $conditionOrCommand = "";
|
||||
|
||||
# ggf. language optional Parameter
|
||||
my $language = shift @a;
|
||||
my $langRegExp = "(";
|
||||
foreach my $l (keys(%shortDays)) {
|
||||
$langRegExp .= $l . "|";
|
||||
}
|
||||
$langRegExp =~ s/\|$//g;
|
||||
$langRegExp .= ")";
|
||||
if ($language =~ m/^$langRegExp$/g) {
|
||||
$hash->{LANGUAGE} = $language;
|
||||
} else {
|
||||
Log3 $hash, 3, "[$name] illegal language: $language, use one of $langRegExp" if (length($language) == 2);
|
||||
unshift (@a,$language) if (length($language) != 2) ;
|
||||
$hash->{LANGUAGE} = "de";
|
||||
}
|
||||
$language = $hash->{LANGUAGE};
|
||||
|
||||
# test if device is defined
|
||||
Log3 $hash, 3, "[$name] invalid device, <$device> not found" if(!$defs{$device});
|
||||
|
||||
#fuer den modify Altlasten bereinigen
|
||||
delete($hash->{TIME_AS_PERL}) if($hash->{TIME_AS_PERL});
|
||||
delete($hash->{helper}{CONDITION}) if($hash->{helper}{CONDITION});
|
||||
delete($hash->{helper}{COMMAND}) if($hash->{helper}{COMMAND});
|
||||
delete($hash->{helper}{SWITCHINGTIMES}) if($hash->{helper}{SWITCHINGTIMES});
|
||||
delete($hash->{helper}{SWITCHINGTIME}) if($hash->{helper}{SWITCHINGTIME});
|
||||
foreach my $l (keys(%shortDays)) {
|
||||
for (my $w=0; $w<7; $w++) {
|
||||
delete($hash->{"PROFILE ".($w).": ".$longDays{$l}[$w]}) if($hash->{"PROFILE ".($w).": ".$longDays{$l}[$w]});
|
||||
}
|
||||
}
|
||||
|
||||
for(my $i=0; $i<@a; $i++) {
|
||||
#pruefen auf Angabe eines Schaltpunktes
|
||||
my @t = split(/\|/, $a[$i]);
|
||||
my $anzahl = @t;
|
||||
if ( $anzahl >= 2 && $anzahl <= 3) {
|
||||
push(@switchingtimes, $a[$i]);
|
||||
} else {
|
||||
#der Rest ist das auzufuehrende Kommando/condition
|
||||
$conditionOrCommand = trim(join(" ", @a[$i..@a-1]));
|
||||
last;
|
||||
}
|
||||
}
|
||||
# wenn keine switchintime angegeben ist, dann Fehler
|
||||
Log3 $hash, 3, "no Switchingtime found in <$conditionOrCommand>, check first parameter" if (@switchingtimes == 0);
|
||||
|
||||
$hash->{NAME} = $name;
|
||||
$hash->{DEVICE} = $device;
|
||||
$modules{$hash->{TYPE}}{defptr}{$hash->{NAME}} = $hash;
|
||||
$hash->{helper}{SWITCHINGTIMES} = join(" ", @switchingtimes);
|
||||
if($conditionOrCommand =~ m/^\(.*\)$/g) { #condition (*)
|
||||
$hash->{helper}{CONDITION} = $conditionOrCommand;
|
||||
} elsif(length($conditionOrCommand) > 0 ) {
|
||||
$hash->{helper}{COMMAND} = $conditionOrCommand;
|
||||
}
|
||||
|
||||
# jetzt die switchingtimes und Tagesangaben verarbeiten.
|
||||
if (!Heating_Control_ParseSwitchingProfile($hash, \@switchingtimes, \$shortDays{$language})) {
|
||||
return;
|
||||
}
|
||||
|
||||
# Profile sortiert aufbauen
|
||||
for (my $d=0; $d<=6; $d++) {
|
||||
foreach my $st (sort (keys %{ $hash->{helper}{SWITCHINGTIME}{$d} })) {
|
||||
my $para = $hash->{helper}{SWITCHINGTIME}{$d}{$st};
|
||||
$hash->{"PROFILE ".($d).": ".$longDays{$language}[$d]} .= sprintf("%s %s, ", substr ($st,0,5), $para);
|
||||
}
|
||||
}
|
||||
|
||||
my $now = time();
|
||||
if ($hash->{TIME_AS_PERL} ) {
|
||||
Heating_Control_UpdatePerlTime_TimerSet($hash);
|
||||
}
|
||||
|
||||
$hash->{PERLTIMEUPDATEMODE} = 0 if (!defined($hash->{PERLTIMEUPDATEMODE}));
|
||||
|
||||
myRemoveInternalTimer("Update", $hash);
|
||||
myInternalTimer ("Update", $now+1, "$hash->{TYPE}_Update", $hash, 0);
|
||||
|
||||
readingsBeginUpdate ($hash);
|
||||
readingsBulkUpdate ($hash, "nextUpdate", strftime("Heute, %H:%M:%S",localtime($now+30)));
|
||||
readingsBulkUpdate ($hash, "nextValue", "???");
|
||||
readingsBulkUpdate ($hash, "state", "waiting...");
|
||||
readingsEndUpdate ($hash, defined($hash->{LOCAL} ? 0 : 1));
|
||||
|
||||
return undef;
|
||||
my $ret = WeekdayTimer_Define($hash, $def);
|
||||
$hash->{helper}{DESIRED_TEMP_READING} = "";
|
||||
return $ret;
|
||||
}
|
||||
################################################################################
|
||||
sub Heating_Control_ParseSwitchingProfile($$$) {
|
||||
my ($hash, $switchingtimes, $shortDays) = @_;
|
||||
|
||||
my $name = $hash->{NAME};
|
||||
my $language = $hash->{LANGUAGE};
|
||||
|
||||
my %dayNumber=();
|
||||
my $daysRegExp = "(";
|
||||
for(my $idx=0; $idx<7; $idx++) {
|
||||
my $day = @{$$shortDays}[$idx];
|
||||
$dayNumber{$day} = $idx;
|
||||
$daysRegExp .= $day . "|";
|
||||
}
|
||||
$daysRegExp =~ s/\|$//g;
|
||||
$daysRegExp .= ")";
|
||||
|
||||
my (@st, @days, $daylist, $time, $timeString, $para);
|
||||
for(my $i=0; $i<@{$switchingtimes}; $i++) {
|
||||
|
||||
@st = split(/\|/, @{$switchingtimes}[$i]);
|
||||
if ( @st == 2) {
|
||||
$daylist = "1234567"; #jeden Tag/Woche ist vordefiniert
|
||||
$time = $st[0];
|
||||
$para = $st[1];
|
||||
} elsif ( @st == 3) {
|
||||
$daylist = lc($st[0]);
|
||||
$time = $st[1];
|
||||
$para = $st[2];
|
||||
}
|
||||
|
||||
my %hdays=();
|
||||
|
||||
# Angaben der Tage verarbeiten
|
||||
# Aufzaehlung 1234 ...
|
||||
if ( $daylist =~ m/^(\d){0,7}$/g) {
|
||||
|
||||
$daylist =~ s/7/0/g;
|
||||
@days = split("", $daylist);
|
||||
@hdays{@days}=1;
|
||||
|
||||
# Aufzaehlung Sa,So,... | Mo-Di,Do,Fr-Mo
|
||||
} elsif ($daylist =~ m/^($daysRegExp(,|-|$)){0,7}$/g ) {
|
||||
|
||||
my $oldDay = "", my $oldDel = "";
|
||||
for (;length($daylist);) {
|
||||
my $day = substr($daylist,0,2,"");
|
||||
my $del = substr($daylist,0,1,"");
|
||||
my @subDays;
|
||||
if ($oldDel eq "-" ){
|
||||
# von bis Angabe: Mo-Di
|
||||
my $low = $dayNumber{$oldDay};
|
||||
my $high = $dayNumber{$day};
|
||||
if ($low <= $high) {
|
||||
@subDays = ($low .. $high);
|
||||
} else {
|
||||
#@subDays = ($dayNumber{so} .. $high, $low .. $dayNumber{sa});
|
||||
@subDays = ( 00 .. $high, $low .. 06);
|
||||
}
|
||||
@hdays{@subDays}=1;
|
||||
} else {
|
||||
#einzelner Tag: Sa
|
||||
$hdays{$dayNumber{$day}} = 1;
|
||||
}
|
||||
$oldDay = $day;
|
||||
$oldDel = $del;
|
||||
}
|
||||
} else{
|
||||
Log3 $hash, 1, "invalid daylist in $name <$daylist> use one of 123...|Sa,So,...|Mo-Di,Do,Fr|Su-Th,We|Lu-Me";
|
||||
return 0;
|
||||
}
|
||||
|
||||
@days = sort(SortNumber keys %hdays);
|
||||
|
||||
my $TIME_AS_PERL = 0;
|
||||
if($time =~ m/^\{.*\}$/g) {
|
||||
$TIME_AS_PERL = 1;
|
||||
$hash->{TIME_AS_PERL} |= 1;
|
||||
}
|
||||
|
||||
my $now = time();
|
||||
my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime($now);
|
||||
my $listOfDays = "";
|
||||
for (my $d=0; $d<@days; $d++) {
|
||||
|
||||
# Zeitangabe verarbeiten.
|
||||
if ($TIME_AS_PERL) { # Perlausdruck {*}
|
||||
my $date = $now+($d-$wday)*86400;
|
||||
$timeString = '{ my $date='."$date;" .$time."}";
|
||||
$timeString = eval( $timeString ); # must deliver HH:MM[:SS]
|
||||
$@ =~ s/\n/ /g; Log3 ($hash, 3, "[$hash->{NAME}] " . $@) if ($@);
|
||||
} else {
|
||||
$timeString = $time;
|
||||
}
|
||||
|
||||
if ($timeString =~ m/^[0-2][0-9]:[0-5][0-9]$/g) { # HH:MM
|
||||
$timeString .= ":00"; # HH:MM:SS erzeugen
|
||||
} elsif ($timeString =~ m/^[0-2][0-9](:[0-5][0-9]){2,2}$/g) { # HH:MM:SS
|
||||
; # ok.
|
||||
} else {
|
||||
Log3 $hash, 1, "[$name] invalid time <$timeString> HH:MM[:SS]";
|
||||
#return 0;
|
||||
}
|
||||
|
||||
$listOfDays .= @{$$shortDays}[$days[$d]] . ",";
|
||||
$hash->{helper}{SWITCHINGTIME}{$days[$d]}{$timeString} = $para;
|
||||
}
|
||||
$listOfDays =~ s/,$//g;
|
||||
Log3 $hash, 5, "[$name] Switchingtime: @{$switchingtimes}[$i] : $listOfDays -> $time -> $para ";
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
################################################################################
|
||||
sub Heating_Control_Undef($$) {
|
||||
########################################################################
|
||||
sub Heating_Control_Undef($$){
|
||||
my ($hash, $arg) = @_;
|
||||
|
||||
myRemoveInternalTimer("Update", $hash);
|
||||
myRemoveInternalTimer("UpdatePerlTime", $hash);
|
||||
|
||||
delete $modules{$hash->{TYPE}}{defptr}{$hash->{NAME}};
|
||||
return undef;
|
||||
}
|
||||
################################################################################
|
||||
sub Heating_Control_UpdatePerlTime_TimerSet($) {
|
||||
my ($hash) = @_;
|
||||
|
||||
my $now = time();
|
||||
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($now);
|
||||
my $secToMidnight = 24*3600 -(3600*$hour + 60*$min + $sec) + 10*60;
|
||||
#my $secToMidnight = + 01*60;
|
||||
|
||||
myRemoveInternalTimer("UpdatePerlTime", $hash);
|
||||
myInternalTimer ("UpdatePerlTime", $now+$secToMidnight, "$hash->{TYPE}_UpdatePerlTime", $hash, 0);
|
||||
|
||||
}
|
||||
################################################################################
|
||||
sub Heating_Control_UpdatePerlTime($) {
|
||||
my ($myHash) = @_;
|
||||
my $hash = myGetHashIndirekt($myHash, (caller(0))[3]);
|
||||
return if (!defined($hash));
|
||||
|
||||
if (defined($hash->{TIME_AS_PERL})) {
|
||||
$hash->{PERLTIMEUPDATEMODE} = 1;
|
||||
Heating_Control_Define($hash, $hash->{NAME} . " " . $hash->{TYPE} . " " . $hash->{DEF} );
|
||||
}
|
||||
return WeekdayTimer_Undef($hash, $arg);
|
||||
}
|
||||
########################################################################
|
||||
sub Heating_Control_Update($) {
|
||||
my ($myHash) = @_;
|
||||
my $hash = myGetHashIndirekt($myHash, (caller(0))[3]);
|
||||
return if (!defined($hash));
|
||||
|
||||
my $mod = "[".$hash->{NAME} ."] "; ###
|
||||
my $name = $hash->{NAME};
|
||||
my $now = time() + 5; # garantiert > als die eingestellte Schlatzeit
|
||||
|
||||
# Schaltparameter ermitteln
|
||||
my ($nowSwitch,$nextSwitch,$newParam,$nextParam) = Heating_Control_akt_next_param($now, $hash);
|
||||
|
||||
# Fenserkontakte abfragen - wenn einer im Status closed, dann Schaltung um 60 Sekunden verzögern
|
||||
if (Heating_Control_FensterOffen($hash, $nowSwitch, $newParam)) {
|
||||
return;
|
||||
}
|
||||
|
||||
# ggf. Device schalten
|
||||
Heating_Control_Device_Schalten($hash, $now, $nowSwitch, $newParam);
|
||||
$hash->{PERLTIMEUPDATEMODE} = 0;
|
||||
|
||||
Log3 $hash, 4, $mod .strftime('Next switch %d.%m.%Y %H:%M:%S',localtime($nextSwitch));
|
||||
|
||||
|
||||
# Timer und Readings setzen.
|
||||
myRemoveInternalTimer("Update", $hash);
|
||||
myInternalTimer ("Update", $nextSwitch, "$hash->{TYPE}_Update", $hash, 0);
|
||||
|
||||
my $active = 1;
|
||||
if (defined $hash->{helper}{CONDITION}) {
|
||||
$active = AnalyzeCommandChain(undef, "{".$hash->{helper}{CONDITION}."}");
|
||||
}
|
||||
|
||||
readingsBeginUpdate($hash);
|
||||
readingsBulkUpdate ($hash, "nextUpdate", strftime("%d.%m.%Y %H:%M:%S",localtime($nextSwitch)));
|
||||
readingsBulkUpdate ($hash, "nextValue", $nextParam);
|
||||
readingsBulkUpdate ($hash, "state", $active ? $newParam : "inactive" );
|
||||
readingsEndUpdate ($hash, defined($hash->{LOCAL} ? 0 : 1));
|
||||
|
||||
return 1;
|
||||
}
|
||||
########################################################################
|
||||
sub Heating_Control_FensterOffen ($$$) {
|
||||
my ($hash, $tim, $event) = @_;
|
||||
my $mod = "[".$hash->{NAME} ."]";
|
||||
|
||||
my $verzoegerteAusfuehrungCond = AttrVal($hash->{NAME}, "delayedExecutionCond", "0");
|
||||
|
||||
my %specials= (
|
||||
"%HEATING_CONTROL" => $hash->{NAME},
|
||||
"%WEEKDAYTIMER" => $hash->{NAME},
|
||||
"%TIME" => $tim,
|
||||
"%NAME" => $hash->{DEVICE},
|
||||
"%EVENT" => $event
|
||||
);
|
||||
$verzoegerteAusfuehrungCond = EvalSpecials($verzoegerteAusfuehrungCond, %specials);
|
||||
my $verzoegerteAusfuehrung = eval($verzoegerteAusfuehrungCond);
|
||||
|
||||
if ($verzoegerteAusfuehrung) {
|
||||
if (!defined($hash->{VERZOEGRUNG})) {
|
||||
Log3 $hash, 3, "$mod switch of $hash->{DEVICE} delayed - $verzoegerteAusfuehrungCond is TRUE";
|
||||
}
|
||||
myRemoveInternalTimer("Update", $hash);
|
||||
myInternalTimer ("Update", time()+60, "$hash->{TYPE}_Update", $hash, 0);
|
||||
$hash->{VERZOEGRUNG} = 1;
|
||||
return 1
|
||||
}
|
||||
|
||||
my %contacts = ( "CUL_FHTTK" => { "READING" => "Window", "STATUS" => "(Open)", "MODEL" => "r" },
|
||||
"CUL_HM" => { "READING" => "state", "STATUS" => "(open|tilted)", "MODEL" => "r" },
|
||||
"MAX" => { "READING" => "state", "STATUS" => "(open)", "MODEL" => "r" },
|
||||
"WeekdayTimer" => { "READING" => "delayedExecution","STATUS" => "^1\$", "MODEL" => "a" },
|
||||
"Heating_Control" => { "READING" => "delayedExecution","STATUS" => "^1\$", "MODEL" => "a" }
|
||||
);
|
||||
|
||||
my $fensterKontakte = AttrVal($hash->{NAME}, "windowSensor", "")." ".$hash->{NAME};
|
||||
$fensterKontakte =~ s/^\s+//;
|
||||
$fensterKontakte =~ s/\s+$//;
|
||||
|
||||
Log3 $hash, 5, "$mod list of senors found: '$fensterKontakte'";
|
||||
if ($fensterKontakte ne "" ) {
|
||||
my @kontakte = split("[ \t]+", $fensterKontakte);
|
||||
foreach my $fk (@kontakte) {
|
||||
if(!$defs{$fk}) {
|
||||
Log3 $hash, 3, "$mod sensor <$fk> not found - check name.";
|
||||
} else {
|
||||
my $fk_hash = $defs{$fk};
|
||||
my $fk_typ = $fk_hash->{TYPE};
|
||||
if (!defined($contacts{$fk_typ})) {
|
||||
Log3 $hash, 3, "$mod TYPE '$fk_typ' of $fk not yet supported, $fk ignored - inform maintainer";
|
||||
} else {
|
||||
|
||||
my $reading = $contacts{$fk_typ}{READING};
|
||||
my $statusReg = $contacts{$fk_typ}{STATUS};
|
||||
my $model = $contacts{$fk_typ}{MODEL};
|
||||
|
||||
my $windowStatus;
|
||||
if ($model eq "r") { ### Reading, sonst Attribut
|
||||
$windowStatus = ReadingsVal($fk,$reading,"nF");
|
||||
}else{
|
||||
$windowStatus = AttrVal ($fk,$reading,"nF");
|
||||
}
|
||||
|
||||
if ($windowStatus eq "nF") {
|
||||
Log3 $hash, 3, "$mod Reading/Attribute '$reading' of $fk not found, $fk ignored - inform maintainer" if ($model eq "r");
|
||||
} else {
|
||||
Log3 $hash, 5, "$mod sensor '$fk' Reading/Attribute '$reading' is '$windowStatus'";
|
||||
|
||||
if ($windowStatus =~ m/^$statusReg$/g) {
|
||||
if (!defined($hash->{VERZOEGRUNG})) {
|
||||
Log3 $hash, 3, "$mod switch of $hash->{DEVICE} delayed - sensor '$fk' Reading/Attribute '$reading' is '$windowStatus'";
|
||||
}
|
||||
myRemoveInternalTimer("Update", $hash);
|
||||
myInternalTimer ("Update", time()+60, "$hash->{TYPE}_Update", $hash, 0);
|
||||
$hash->{VERZOEGRUNG} = 1;
|
||||
return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($hash->{VERZOEGRUNG}) {
|
||||
Log3 $hash, 3, "$mod delay of switching $hash->{DEVICE} stopped.";
|
||||
}
|
||||
delete $hash->{VERZOEGRUNG};
|
||||
return 0;
|
||||
}
|
||||
########################################################################
|
||||
sub Heating_Control_akt_next_param($$) {
|
||||
my ($now, $hash) = @_;
|
||||
|
||||
my $mod = "[".$hash->{NAME} ."] "; ###
|
||||
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($now);
|
||||
|
||||
my ($nextParam, $next, $nextSwitch, $nowSwitch, $newParam) = (0,0,0,0,0);
|
||||
# aktuellen und nächsten Schaltzeitpunkt ermitteln.
|
||||
my $startIdx;
|
||||
for (my $d=-1; $d>=-7; $d--) {
|
||||
my $wd = ($d+$wday) % 7;
|
||||
my $anzSwitches = keys %{ $hash->{helper}{SWITCHINGTIME}{$wd} };
|
||||
$startIdx = $d;
|
||||
last if ($anzSwitches > 0);
|
||||
}
|
||||
|
||||
for (my $d=$startIdx; $d<=7; $d++) {
|
||||
#ueber jeden Tag
|
||||
last if ($nextSwitch > 0);
|
||||
my $wd = ($d+$wday) % 7;
|
||||
foreach my $st (sort (keys %{ $hash->{helper}{SWITCHINGTIME}{$wd} })) {
|
||||
|
||||
# Tagediff + Sekunden des Tages addieren
|
||||
my @t = split(/:/, $st); # HH MM SS
|
||||
#my $secondsToSwitch = $d*24*3600 + 3600*($t[0] - $hour) + 60*($t[1] - $min) + $t[2] - $sec;
|
||||
my $next = zeitErmitteln ($now, $t[0], $t[1], $t[2], $d);
|
||||
my $secondsToSwitch = $next - $now;
|
||||
|
||||
if ($secondsToSwitch<=10 && $secondsToSwitch>=-20) {
|
||||
Log3 $hash, 4, $mod."Jetzt:".strftime('%d.%m.%Y %H:%M:%S',localtime($now))." -> Next: ".strftime('%d.%m.%Y %H:%M:%S',localtime($next))." -> Param: $hash->{helper}{SWITCHINGTIME}{$wd}{$st} ".$secondsToSwitch;
|
||||
}
|
||||
if ($secondsToSwitch<=0) {
|
||||
$newParam = $hash->{helper}{SWITCHINGTIME}{$wd}{$st};
|
||||
$nowSwitch = $next;
|
||||
} else {
|
||||
$nextParam = $hash->{helper}{SWITCHINGTIME}{$wd}{$st};
|
||||
$nextSwitch = $next;
|
||||
last;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if ($now > $nextSwitch) {
|
||||
$nextSwitch = max ($now+60,$nextSwitch);
|
||||
}
|
||||
return ($nowSwitch,$nextSwitch,$newParam,$nextParam);
|
||||
sub Heating_Control_Update($){
|
||||
my ($hash) = @_;
|
||||
return WeekdayTimer_Update($hash);
|
||||
}
|
||||
################################################################################
|
||||
sub Heating_Control_Device_Schalten($$$$) {
|
||||
my ($hash, $now, $nowSwitch, $newParam) = @_;
|
||||
|
||||
my $command = "";
|
||||
my $mod = "[".$hash->{NAME} ."] "; ###
|
||||
|
||||
#modifier des Zieldevices auswaehlen
|
||||
my $setModifier = Heating_Control_isHeizung($hash);
|
||||
|
||||
# Kommando aufbauen
|
||||
if (defined $hash->{helper}{CONDITION}) {
|
||||
$command = '{ fhem("set @ '. $setModifier .' %") if' . $hash->{helper}{CONDITION} . '}';
|
||||
} elsif (defined $hash->{helper}{COMMAND}) {
|
||||
$command = $hash->{helper}{COMMAND};
|
||||
} else {
|
||||
$command = '{ fhem("set @ '. $setModifier .' %") }';
|
||||
}
|
||||
|
||||
my $isHeating = $setModifier gt "";
|
||||
my $aktParam = ReadingsVal($hash->{DEVICE}, $setModifier, 0);
|
||||
$aktParam = sprintf("%.1f", $aktParam) if ($isHeating && $aktParam =~ m/^[0-9]{1,3}$/i);
|
||||
$newParam = sprintf("%.1f", $newParam) if ($isHeating && $newParam =~ m/^[0-9]{1,3}$/i);
|
||||
|
||||
Log3 $hash, 4, $mod .strftime('%d.%m.%Y %H:%M:%S',localtime($nowSwitch))." ; aktParam: $aktParam ; newParam: $newParam";
|
||||
|
||||
my $disabled = AttrVal($hash->{NAME}, "disable", 0);
|
||||
my $disabled_txt = $disabled ? " " : " not";
|
||||
Log3 $hash, 4, $mod . "is$disabled_txt disabled";
|
||||
|
||||
#Kommando ausführen
|
||||
my $secondsSinceSwitch = $nowSwitch - $now;
|
||||
|
||||
if ($hash->{PERLTIMEUPDATEMODE} == 1) {
|
||||
Log3 $hash, 5, $mod."no switch of device in PERLTIMEUPDATEMODE at 00:10 o'clock";
|
||||
return;
|
||||
}
|
||||
|
||||
my $setAllTempMode = defined ($hash->{setAllTempMode});
|
||||
if (defined $hash->{helper}{COMMAND} || ($nowSwitch gt "" && $aktParam ne $newParam )) {
|
||||
if (!$setAllTempMode && !$setModifier && $secondsSinceSwitch < -60) {
|
||||
Log3 $hash, 5, $mod."no switch in the yesterdays because of the devices type($hash->{DEVICE} is not a heating).";
|
||||
} else {
|
||||
if ($command && !$disabled) {
|
||||
$newParam =~ s/:/ /g;
|
||||
|
||||
$command = SemicolonEscape($command);
|
||||
my %specials= (
|
||||
"%NAME" => $hash->{DEVICE},
|
||||
"%EVENT" => $newParam,
|
||||
);
|
||||
$command= EvalSpecials($command, %specials);
|
||||
|
||||
Log3 $hash, 4, $mod."command: $command executed";
|
||||
my $ret = AnalyzeCommandChain(undef, $command);
|
||||
Log3 ($hash, 3, $ret) if($ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
sub Heating_Control_SetTimerOfDay($) {
|
||||
my ($hash) = @_;
|
||||
return WeekdayTimer_SetTimerOfDay($hash);
|
||||
}
|
||||
########################################################################
|
||||
sub Heating_Control_Attr($$$) {
|
||||
@ -592,90 +102,16 @@ sub Heating_Control_Attr($$$) {
|
||||
return undef;
|
||||
}
|
||||
########################################################################
|
||||
sub Heating_Control_isHeizung($) {
|
||||
my ($hash) = @_;
|
||||
|
||||
my %setmodifiers =
|
||||
("FHT" => "desired-temp",
|
||||
"PID20" => "desired",
|
||||
"EnOcean" => { "subTypeReading" => "subType", "setModifier" => "desired-temp",
|
||||
"roomSensorControl.05" => 1,
|
||||
"hvac.01" => 1 },
|
||||
"MAX" => { "subTypeReading" => "type", "setModifier" => "desiredTemperature",
|
||||
"HeatingThermostatPlus" => 1,
|
||||
"HeatingThermostat" => 1,
|
||||
"WallMountedThermostat" => 1 },
|
||||
"CUL_HM" => { "subTypeReading" => "model","setModifier" => "desired-temp",
|
||||
"HM-CC-TC" => 1,
|
||||
"HM-TC-IT-WM-W-EU" => 1,
|
||||
"HM-CC-RT-DN" => 1 } );
|
||||
my $dHash = $defs{$hash->{DEVICE}}; ###
|
||||
my $dType = $dHash->{TYPE};
|
||||
return "" if (!defined($dType));
|
||||
|
||||
my $setModifier = $setmodifiers{$dType};
|
||||
$setModifier = "" if (!defined($setModifier));
|
||||
if (ref($setModifier)) {
|
||||
|
||||
my $subTypeReading = $setmodifiers{$dType}{subTypeReading};
|
||||
|
||||
my $model;
|
||||
if ($subTypeReading eq "type" ) {
|
||||
$model = $dHash->{type};
|
||||
} else {
|
||||
$model = AttrVal($hash->{DEVICE}, $subTypeReading, "nF");
|
||||
}
|
||||
|
||||
if (defined($setmodifiers{$dType}{$model})) {
|
||||
$setModifier = $setmodifiers{$dType}{setModifier}
|
||||
} else {
|
||||
$setModifier = "";
|
||||
}
|
||||
}
|
||||
return $setModifier;
|
||||
}
|
||||
|
||||
################################################################################
|
||||
sub Heating_Control_SetAllTemps() { # {Heating_Control_SetAllTemps()}
|
||||
sub Heating_Control_SetAllTemps() { # {Heating_Control_SetAllTemps()}
|
||||
|
||||
foreach my $hc ( sort keys %{$modules{Heating_Control}{defptr}} ) {
|
||||
my $hash = $modules{Heating_Control}{defptr}{$hc};
|
||||
|
||||
if($hash->{helper}{CONDITION}) {
|
||||
if (!(eval ($hash->{helper}{CONDITION}))) {
|
||||
readingsSingleUpdate ($hash, "state", "inactive", 1);
|
||||
next;
|
||||
}
|
||||
}
|
||||
|
||||
my $myHash->{HASH}=$hash;
|
||||
$hash->{setAllTempMode} = 1;
|
||||
Heating_Control_Update($myHash);
|
||||
delete $hash->{setAllTempMode};
|
||||
Log3 undef, 3, "Heating_Control_Update() for $hash->{NAME} done!";
|
||||
Heating_Control_SetTimer($hash);
|
||||
Log3 undef, 3, "Heating_Control_SetTimer() for $hash->{NAME} done!";
|
||||
}
|
||||
Log3 undef, 3, "Heating_Control_SetAllTemps() done!";
|
||||
}
|
||||
########################################################################
|
||||
sub zeitErmitteln ($$$$$) {
|
||||
my ($now, $hour, $min, $sec, $days) = @_;
|
||||
|
||||
my @jetzt_arr = localtime($now);
|
||||
#Stunden Minuten Sekunden
|
||||
$jetzt_arr[2] = $hour; $jetzt_arr[1] = $min; $jetzt_arr[0] = $sec;
|
||||
$jetzt_arr[3] += $days;
|
||||
my $next = timelocal_nocheck(@jetzt_arr);
|
||||
return $next;
|
||||
}
|
||||
########################################################################
|
||||
sub SortNumber {
|
||||
if($a < $b)
|
||||
{ return -1; }
|
||||
elsif($a == $b)
|
||||
{ return 0; }
|
||||
else
|
||||
{ return 1; }
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
@ -1004,3 +440,4 @@ sub SortNumber {
|
||||
|
||||
=end html_DE
|
||||
=cut
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#
|
||||
# 98_WeekdayTimer.pm
|
||||
# written by Dietmar Ortmann
|
||||
# modified by Tobias Faust
|
||||
#
|
||||
# This file is part of fhem.
|
||||
#
|
||||
@ -25,25 +26,41 @@ package main;
|
||||
use strict;
|
||||
use warnings;
|
||||
use POSIX;
|
||||
########################################################################
|
||||
sub WeekdayTimer_Initialize($)
|
||||
{
|
||||
|
||||
use Time::Local 'timelocal_nocheck';
|
||||
|
||||
use Data::Dumper;
|
||||
$Data::Dumper::Sortkeys = 1;
|
||||
|
||||
################################################################################
|
||||
sub WeekdayTimer_Initialize($){
|
||||
my ($hash) = @_;
|
||||
|
||||
if(!$modules{Heating_Control}{LOADED} && -f "$attr{global}{modpath}/FHEM/98_Heating_Control.pm") {
|
||||
my $ret = CommandReload(undef, "98_Heating_Control");
|
||||
if(!$modules{Twilight}{LOADED} && -f "$attr{global}{modpath}/FHEM/59_Twilight.pm") {
|
||||
my $ret = CommandReload(undef, "59_Twilight");
|
||||
Log3 undef, 1, $ret if($ret);
|
||||
}
|
||||
|
||||
# Consumer
|
||||
$hash->{SetFn} = "WeekdayTimer_Set";
|
||||
$hash->{AttrFn} = "WeekdayTimer_Attr";
|
||||
$hash->{DefFn} = "WeekdayTimer_Define";
|
||||
$hash->{UndefFn} = "WeekdayTimer_Undef";
|
||||
$hash->{GetFn} = "WeekdayTimer_Get";
|
||||
$hash->{AttrFn} = "WeekdayTimer_Attr";
|
||||
$hash->{UpdFn} = "WeekdayTimer_Update";
|
||||
$hash->{AttrList}= "disable:0,1 delayedExecutionCond ".
|
||||
$readingFnAttributes;
|
||||
$readingFnAttributes;
|
||||
}
|
||||
################################################################################
|
||||
sub WeekdayTimer_InitHelper($) {
|
||||
my ($hash) = @_;
|
||||
|
||||
$hash->{longDays} = { "de" => ["Sonntag", "Montag","Dienstag","Mittwoch", "Donnerstag","Freitag", "Samstag", "Wochenende", "Werktags" ],
|
||||
"en" => ["Sunday", "Monday","Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "weekend", "weekdays" ],
|
||||
"fr" => ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi","Samedi", "weekend", "jours de la semaine"]};
|
||||
$hash->{shortDays} = { "de" => ["so", "mo", "di", "mi", "do", "fr", "sa", '$we', '!$we' ],
|
||||
"en" => ["su", "mo", "tu", "we", "th", "fr", "sa", '$we', '!$we' ],
|
||||
"fr" => ["di", "lu", "ma", "me", "je", "ve", "sa", '$we', '!$we' ]};
|
||||
}
|
||||
################################################################################
|
||||
sub WeekdayTimer_Set($@) {
|
||||
@ -64,34 +81,657 @@ sub WeekdayTimer_Set($@) {
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
########################################################################
|
||||
################################################################################
|
||||
sub WeekdayTimer_Get($@) {
|
||||
return Heating_Control_Get($@);
|
||||
}
|
||||
########################################################################
|
||||
sub WeekdayTimer_Define($$){
|
||||
my ($hash, $def) = @_;
|
||||
my ($hash, @a) = @_;
|
||||
return "argument is missing" if(int(@a) != 2);
|
||||
|
||||
my $ret = Heating_Control_Define($hash, $def);
|
||||
$hash->{helper}{DESIRED_TEMP_READING} = "";
|
||||
return $ret;
|
||||
$hash->{LOCAL} = 1;
|
||||
delete $hash->{LOCAL};
|
||||
my $reading= $a[1];
|
||||
my $value;
|
||||
|
||||
if(defined($hash->{READINGS}{$reading})) {
|
||||
$value= $hash->{READINGS}{$reading}{VAL};
|
||||
} else {
|
||||
return "no such reading: $reading";
|
||||
}
|
||||
return "$a[0] $reading => $value";
|
||||
}
|
||||
########################################################################
|
||||
sub WeekdayTimer_Undef($$){
|
||||
################################################################################
|
||||
sub WeekdayTimer_Undef($$) {
|
||||
my ($hash, $arg) = @_;
|
||||
return Heating_Control_Undef($hash, $arg);
|
||||
|
||||
foreach my $time (keys %{$hash->{profil}}) {
|
||||
myRemoveInternalTimer($time, $hash);
|
||||
}
|
||||
myRemoveInternalTimer("SetTimerOfDay", $hash);
|
||||
delete $modules{$hash->{TYPE}}{defptr}{$hash->{NAME}};
|
||||
return undef;
|
||||
}
|
||||
########################################################################
|
||||
sub WeekdayTimer_UpdatePerlTime($) {
|
||||
my ($hash) = @_;
|
||||
Heating_Control_UpdatePerlTime($hash);
|
||||
################################################################################
|
||||
sub WeekdayTimer_Define($$) {
|
||||
my ($hash, $def) = @_;
|
||||
WeekdayTimer_InitHelper($hash);
|
||||
|
||||
my @a = split("[ \t]+", $def);
|
||||
|
||||
return "Usage: define <name> $hash->{TYPE} <device> <language> <switching times> <condition|command>"
|
||||
if(@a < 4);
|
||||
|
||||
#fuer den modify Altlasten bereinigen
|
||||
delete($hash->{helper});
|
||||
|
||||
my $name = shift @a;
|
||||
my $type = shift @a;
|
||||
my $device = shift @a;
|
||||
|
||||
WeekdayTimer_DeleteTimer($hash);
|
||||
my $delVariables = "(CONDITION|COMMAND|profile|Profil)";
|
||||
map { delete $hash->{$_} if($_=~ m/^$delVariables.*/g) } keys %{$hash};
|
||||
|
||||
my $language = WeekdayTimer_Language ($hash, \@a);
|
||||
|
||||
my $idx = 0;
|
||||
$hash->{dayNumber} = {map {$_ => $idx++} @{$hash->{shortDays}{$language}}};
|
||||
$hash->{helper}{daysRegExp} = '(' . join ("|", @{$hash->{shortDays}{$language}}) . ")";
|
||||
$hash->{helper}{daysRegExpMessage} = $hash->{helper}{daysRegExp};
|
||||
|
||||
$hash->{helper}{daysRegExp} =~ s/\$/\\\$/g;
|
||||
$hash->{helper}{daysRegExp} =~ s/\!/\\\!/g;
|
||||
|
||||
WeekdayTimer_GlobalDaylistSpec ($hash, \@a);
|
||||
|
||||
my @switchingtimes = WeekdayTimer_gatherSwitchingTimes (\@a);
|
||||
my $conditionOrCommand = join (" ", @a);
|
||||
|
||||
# test if device is defined
|
||||
Log3 ($hash, 3, "[$name] invalid device, <$device> not found") if(!$defs{$device});
|
||||
|
||||
# wenn keine switchintime angegeben ist, dann Fehler
|
||||
Log3 ($hash, 3, "[$name] no valid Switchingtime found in <$conditionOrCommand>, check first parameter") if (@switchingtimes == 0);
|
||||
|
||||
$hash->{TYPE} = $type;
|
||||
$hash->{NAME} = $name;
|
||||
$hash->{DEVICE} = $device;
|
||||
$hash->{SWITCHINGTIMES} = \@switchingtimes;
|
||||
|
||||
$modules{$hash->{TYPE}}{defptr}{$hash->{NAME}} = $hash;
|
||||
|
||||
if($conditionOrCommand =~ m/^\(.*\)$/g) { #condition (*)
|
||||
$hash->{CONDITION} = $conditionOrCommand;
|
||||
} elsif(length($conditionOrCommand) > 0 ) {
|
||||
$hash->{COMMAND} = $conditionOrCommand;
|
||||
}
|
||||
|
||||
#WeekdayTimer_DeleteTimer($hash); am Anfang dieser Routine
|
||||
WeekdayTimer_Profile ($hash);
|
||||
WeekdayTimer_SetTimer ($hash);
|
||||
|
||||
WeekdayTimer_SetTimerForMidnightUpdate( { HASH => $hash} );
|
||||
|
||||
return undef;
|
||||
}
|
||||
########################################################################
|
||||
sub WeekdayTimer_Update($){
|
||||
my ($hash) = @_;
|
||||
return Heating_Control_Update($hash);
|
||||
################################################################################
|
||||
sub WeekdayTimer_Profile($) {
|
||||
my $hash = shift;
|
||||
|
||||
my $nochZuAendern = 0; # $d
|
||||
my $language = $hash->{LANGUAGE};
|
||||
my %longDays = %{$hash->{longDays}};
|
||||
|
||||
delete $hash->{profil};
|
||||
my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time());
|
||||
|
||||
my $now = time();
|
||||
# ------------------------------------------------------------------------------
|
||||
foreach my $st (@{$hash->{SWITCHINGTIMES}}) {
|
||||
my ($tage,$time,$parameter) = WeekdayTimer_SwitchingTime ($hash, $st);
|
||||
|
||||
foreach my $d (@{$tage}) {
|
||||
|
||||
my $dayOfEchteZeit = $d;
|
||||
if ($d==7) { # Weekend
|
||||
$dayOfEchteZeit = ($wday ~~ [1..5]) ? 6 : $wday; # ggf. Samstag
|
||||
} elsif ($d==8) { # day of Week
|
||||
$dayOfEchteZeit = ($wday ~~ [0..6]) ? 1 : $wday; # ggf. Montag
|
||||
}
|
||||
|
||||
my $echtZeit = WeekdayTimer_EchteZeit($hash, $dayOfEchteZeit, $time);
|
||||
$hash->{profile}{$d}{$echtZeit} = $parameter;
|
||||
}
|
||||
}
|
||||
# ------------------------------------------------------------------------------
|
||||
foreach my $st (@{$hash->{SWITCHINGTIMES}}) {
|
||||
my ($tage,$time,$parameter) = WeekdayTimer_SwitchingTime ($hash, $st);
|
||||
my $echtZeit = WeekdayTimer_EchteZeit ($hash, $wday, $time);
|
||||
my ($stunde, $minute, $sekunde) = split (":",$echtZeit);
|
||||
|
||||
$hash->{profil} {$echtZeit}{PARA} = $parameter;
|
||||
$hash->{profil} {$echtZeit}{TIM} = WeekdayTimer_zeitErmitteln ($now, $stunde, $minute, $sekunde, 0);
|
||||
$hash->{profil} {$echtZeit}{TAGE} = $tage;
|
||||
}
|
||||
# ------------------------------------------------------------------------------
|
||||
Log3 $hash, 4, "[$hash->{NAME}] " . sunrise_abs() . " " . sunset_abs() . " " . $longDays{$language}[$wday];
|
||||
foreach my $d (sort keys %{$hash->{profile}}) {
|
||||
my $profiltext = "";
|
||||
foreach my $t (sort keys %{$hash->{profile}{$d}}) {
|
||||
$profiltext .= "$t " . $hash->{profile}{$d}{$t} . ", ";
|
||||
}
|
||||
my $profilKey = "Profil $d: $longDays{$language}[$d]";
|
||||
$profiltext =~ s/, $//;
|
||||
$hash->{$profilKey} = $profiltext;
|
||||
Log3 $hash, 4, "[$hash->{NAME}] $profiltext ($profilKey)";
|
||||
}
|
||||
delete $hash->{profile};
|
||||
}
|
||||
########################################################################
|
||||
################################################################################
|
||||
sub WeekdayTimer_SwitchingTime($$) {
|
||||
my ($hash, $switchingtime) = @_;
|
||||
|
||||
my $name = $hash->{NAME};
|
||||
my $globalDaylistSpec = $hash->{GlobalDaylistSpec};
|
||||
my @tageGlobal = @{WeekdayTimer_daylistAsArray($hash, $globalDaylistSpec)};
|
||||
|
||||
my (@st, $daylist, $time, $timeString, $para);
|
||||
@st = split(/\|/, $switchingtime);
|
||||
|
||||
if ( @st == 2) {
|
||||
$daylist = ($globalDaylistSpec gt "") ? $globalDaylistSpec : "0123456";
|
||||
$time = $st[0];
|
||||
$para = $st[1];
|
||||
} elsif ( @st == 3) {
|
||||
$daylist = $st[0];
|
||||
$time = $st[1];
|
||||
$para = $st[2];
|
||||
}
|
||||
|
||||
my @tage = @{WeekdayTimer_daylistAsArray($hash, $daylist)};
|
||||
my $tage=@tage;
|
||||
if ( $tage==0 ) {
|
||||
Log3 ($hash, 1, "[$name] invalid daylist in $name <$daylist> use one of 012345678 or $hash->{helper}{daysRegExpMessage}");
|
||||
}
|
||||
|
||||
my %hdays=();
|
||||
@hdays{@tageGlobal} = undef;
|
||||
@hdays{@tage} = undef;
|
||||
@tage = sort keys %hdays;
|
||||
|
||||
#Log3 $hash, 3, "Tage: " . Dumper \@tage;
|
||||
return (\@tage,$time,$para);
|
||||
}
|
||||
|
||||
################################################################################
|
||||
sub WeekdayTimer_daylistAsArray($$){
|
||||
my ($hash, $daylist) = @_;
|
||||
|
||||
my $name = $hash->{NAME};
|
||||
my @days;
|
||||
|
||||
my %hdays=();
|
||||
|
||||
$daylist = lc($daylist);
|
||||
# Angaben der Tage verarbeiten
|
||||
# Aufzaehlung 1234 ...
|
||||
if ( $daylist =~ m/^[0-8]{0,9}$/g) {
|
||||
|
||||
Log3 ($hash, 3, "[$name] " . '"7" in daylist now means $we(weekend) - see dokumentation!!!' )
|
||||
if (index($daylist, '7') != -1);
|
||||
|
||||
@days = split("", $daylist);
|
||||
@hdays{@days} = undef;
|
||||
|
||||
# Aufzaehlung Sa,So,... | Mo-Di,Do,Fr-Mo
|
||||
} elsif ($daylist =~ m/^($hash->{helper}{daysRegExp}(,|-|$)){0,7}$/g ) {
|
||||
my @subDays;
|
||||
my @aufzaehlungen = split (",", $daylist);
|
||||
foreach my $einzelAufzaehlung (@aufzaehlungen) {
|
||||
my @days = split ("-", $einzelAufzaehlung);
|
||||
my $days = @days;
|
||||
if ($days == 1) {
|
||||
#einzelner Tag: Sa
|
||||
$hdays{$hash->{dayNumber}{$days[0]}} = undef;
|
||||
} else {
|
||||
# von bis Angabe: Mo-Di
|
||||
my $von = $hash->{dayNumber}{$days[0]};
|
||||
my $bis = $hash->{dayNumber}{$days[1]};
|
||||
if ($von <= $bis) {
|
||||
@subDays = ($von .. $bis);
|
||||
} else {
|
||||
#@subDays = ($dayNumber{so} .. $bis, $von .. $dayNumber{sa});
|
||||
@subDays = ( 00 .. $bis, $von .. 06);
|
||||
}
|
||||
@hdays{@subDays}=undef;
|
||||
}
|
||||
}
|
||||
} else{
|
||||
%hdays = ();
|
||||
}
|
||||
|
||||
my @tage = sort keys %hdays;
|
||||
return \@tage;
|
||||
}
|
||||
################################################################################
|
||||
sub WeekdayTimer_EchteZeit($$$) {
|
||||
my ($hash, $d, $time) = @_;
|
||||
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
my $now = time();
|
||||
my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime($now);
|
||||
|
||||
my $listOfDays = "";
|
||||
|
||||
# Zeitangabe verarbeiten.
|
||||
$time = '"' . "$time" . '"' if($time !~ m/^\{.*\}$/g);
|
||||
my $date = $now+($d-$wday)*86400;
|
||||
my $timeString = '{ my $date='."$date;" .$time."}";
|
||||
my $eTimeString = eval( $timeString ); # must deliver HH:MM[:SS]
|
||||
if ($@) {
|
||||
$@ =~ s/\n/ /g;
|
||||
Log3 ($hash, 3, "[$name] " . $@ . ">>>$timeString<<<");
|
||||
$eTimeString = "00:00:00";
|
||||
}
|
||||
|
||||
if ($eTimeString =~ m/^[0-2][0-9]:[0-5][0-9]$/g) { # HH:MM
|
||||
$eTimeString .= ":00"; # HH:MM:SS erzeugen
|
||||
} elsif ($eTimeString =~ m/^[0-2][0-9](:[0-5][0-9]){2,2}$/g) { # HH:MM:SS
|
||||
; # ok.
|
||||
} else {
|
||||
Log3 ($hash, 1, "[$name] invalid time <$eTimeString> HH:MM[:SS]");
|
||||
$eTimeString = "00:00:00";
|
||||
}
|
||||
return $eTimeString;
|
||||
}
|
||||
################################################################################
|
||||
sub WeekdayTimer_zeitErmitteln ($$$$$) {
|
||||
my ($now, $hour, $min, $sec, $days) = @_;
|
||||
|
||||
my @jetzt_arr = localtime($now);
|
||||
#Stunden Minuten Sekunden
|
||||
$jetzt_arr[2] = $hour; $jetzt_arr[1] = $min; $jetzt_arr[0] = $sec;
|
||||
$jetzt_arr[3] += $days;
|
||||
my $next = timelocal_nocheck(@jetzt_arr);
|
||||
return $next;
|
||||
}
|
||||
################################################################################
|
||||
sub WeekdayTimer_gatherSwitchingTimes {
|
||||
my $a = shift;
|
||||
|
||||
my @switchingtimes = ();
|
||||
my $conditionOrCommand;
|
||||
|
||||
# switchingtime einsammeln
|
||||
while (@$a > 0) {
|
||||
|
||||
#pruefen auf Angabe eines Schaltpunktes
|
||||
my $element = shift @$a;
|
||||
my @t = split(/\|/, $element);
|
||||
my $anzahl = @t;
|
||||
if ( $anzahl >= 2 && $anzahl <= 3) {
|
||||
push(@switchingtimes, $element);
|
||||
} else {
|
||||
unshift @$a, $element;
|
||||
last;
|
||||
}
|
||||
}
|
||||
return (@switchingtimes);
|
||||
}
|
||||
################################################################################
|
||||
sub WeekdayTimer_Language {
|
||||
my ($hash, $a) = @_;
|
||||
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
# ggf. language optional Parameter
|
||||
my $langRegExp = "(" . join ("|", keys(%{$hash->{shortDays}})) . ")";
|
||||
my $language = shift @$a;
|
||||
|
||||
if ($language =~ m/^$langRegExp$/g) {
|
||||
} else {
|
||||
Log3 ($hash, 3, "[$name] language: $language not recognized, use one of $langRegExp") if (length($language) == 2);
|
||||
unshift @$a, $language;
|
||||
$language = "de";
|
||||
}
|
||||
$hash->{LANGUAGE} = $language;
|
||||
|
||||
$language = $hash->{LANGUAGE};
|
||||
return ($langRegExp, $language);
|
||||
}
|
||||
################################################################################
|
||||
sub WeekdayTimer_GlobalDaylistSpec {
|
||||
my ($hash, $a) = @_;
|
||||
|
||||
my $daylist = shift @$a;
|
||||
|
||||
my @tage = @{WeekdayTimer_daylistAsArray($hash, $daylist)};
|
||||
my $tage = @tage;
|
||||
if ($tage > 0) {
|
||||
;
|
||||
} else {
|
||||
unshift (@$a,$daylist);
|
||||
$daylist = "";
|
||||
}
|
||||
|
||||
$hash->{GlobalDaylistSpec} = $daylist;
|
||||
}
|
||||
################################################################################
|
||||
sub WeekdayTimer_SetTimerForMidnightUpdate($) {
|
||||
my ($myHash) = @_;
|
||||
my $hash = myGetHashIndirekt($myHash, (caller(0))[3]);
|
||||
return if (!defined($hash));
|
||||
|
||||
my $now = time();
|
||||
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($now);
|
||||
my $secToMidnight = 24*3600 -(3600*$hour + 60*$min + $sec) + 5;
|
||||
#my $secToMidnight = + 01*60;
|
||||
|
||||
myRemoveInternalTimer("SetTimerOfDay", $hash);
|
||||
myInternalTimer ("SetTimerOfDay", $now+$secToMidnight, "$hash->{TYPE}_SetTimerOfDay", $hash, 0);
|
||||
|
||||
}
|
||||
################################################################################
|
||||
sub WeekdayTimer_SetTimerOfDay($) {
|
||||
my ($myHash) = @_;
|
||||
my $hash = myGetHashIndirekt($myHash, (caller(0))[3]);
|
||||
return if (!defined($hash));
|
||||
|
||||
WeekdayTimer_DeleteTimer($hash);
|
||||
WeekdayTimer_Profile ($hash);
|
||||
WeekdayTimer_SetTimer ($hash);
|
||||
|
||||
WeekdayTimer_SetTimerForMidnightUpdate( { HASH => $hash} );
|
||||
}
|
||||
################################################################################
|
||||
sub WeekdayTimer_SetTimer($) {
|
||||
my $hash = shift;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
my $now = time();
|
||||
|
||||
my $switchedInThePast = 0;
|
||||
my $isHeating = WeekdayTimer_isHeizung($hash);
|
||||
my $grenzSeconds = $isHeating ? -24*3600 : -5;
|
||||
|
||||
my @switches = sort keys %{$hash->{profil}};
|
||||
if ($#switches < 0) {
|
||||
Log3 $hash, 3, "[$name] no switches to send, due to possible errors.";
|
||||
return;
|
||||
}
|
||||
|
||||
my $nextSwitch = $switches[0];
|
||||
my $nextPara = $hash->{profil}{$switches[0]}{PARA};
|
||||
|
||||
my @reverseSwitches = ((reverse @switches), $switches[$#switches]);
|
||||
for(my $i=0; $i<=$#reverseSwitches; $i++) {
|
||||
my $time = $reverseSwitches[$i];
|
||||
|
||||
$hash->{profil}{$time}{NEXTPARA} = $nextPara;
|
||||
$hash->{profil}{$time}{NEXTSWITCH} = $nextSwitch;
|
||||
|
||||
my $timToSwitch = $hash->{profil}{$time}{TIM};
|
||||
$nextPara = $hash->{profil}{$time}{PARA};
|
||||
$nextSwitch = $time;
|
||||
|
||||
$timToSwitch -= 24*3600 if ($i == $#reverseSwitches);
|
||||
my $secondsToSwitch = $timToSwitch - $now;
|
||||
|
||||
if ($secondsToSwitch>$grenzSeconds && !$switchedInThePast) {
|
||||
myInternalTimer ("$time", $timToSwitch, "$hash->{TYPE}_Update", $hash, 0);
|
||||
$switchedInThePast = ($secondsToSwitch<0);
|
||||
}
|
||||
}
|
||||
}
|
||||
################################################################################
|
||||
sub WeekdayTimer_DeleteTimer($) {
|
||||
my $hash = shift;
|
||||
map {myRemoveInternalTimer ($_, $hash)} keys %{$hash->{profil}};
|
||||
}
|
||||
################################################################################
|
||||
sub WeekdayTimer_Update($) {
|
||||
my ($myHash) = @_;
|
||||
my $hash = myGetHashIndirekt($myHash, (caller(0))[3]);
|
||||
return if (!defined($hash));
|
||||
|
||||
my $name = $hash->{NAME};
|
||||
my $time = $myHash->{MODIFIER};
|
||||
my $now = time();
|
||||
|
||||
# Schaltparameter ermitteln
|
||||
my $tage = $hash->{profil}{$time}{TAGE};
|
||||
my $newParam = $hash->{profil}{$time}{PARA};
|
||||
my $nextSwitch = $hash->{profil}{$time}{NEXTSWITCH};
|
||||
my $nextParam = $hash->{profil}{$time}{NEXTPARA};
|
||||
|
||||
# Fenserkontakte abfragen - wenn einer im Status closed, dann Schaltung um 60 Sekunden verzögern
|
||||
if (WeekdayTimer_FensterOffen($hash, $newParam, $time)) {
|
||||
return;
|
||||
}
|
||||
|
||||
my $active = 1;
|
||||
my $condition = WeekdayTimer_Condition ($hash, $tage);
|
||||
if ($condition) {
|
||||
$active = AnalyzeCommandChain(undef, "{". $condition ."}");
|
||||
}
|
||||
Log3 $hash, 4, "[$name] seems to be active: $condition" if($active);
|
||||
|
||||
# ggf. Device schalten
|
||||
WeekdayTimer_Device_Schalten($hash, $newParam, $tage);
|
||||
|
||||
readingsBeginUpdate($hash);
|
||||
readingsBulkUpdate ($hash, "nextUpdate", $nextSwitch);
|
||||
readingsBulkUpdate ($hash, "nextUpdate", $nextSwitch);
|
||||
readingsBulkUpdate ($hash, "nextValue", $nextParam);
|
||||
readingsBulkUpdate ($hash, "state", $active ? $newParam : "inactive" );
|
||||
readingsEndUpdate ($hash, defined($hash->{LOCAL} ? 0 : 1));
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
################################################################################
|
||||
sub WeekdayTimer_isHeizung($) {
|
||||
my ($hash) = @_;
|
||||
|
||||
my %setmodifiers =
|
||||
("FHT" => "desired-temp",
|
||||
"PID20" => "desired",
|
||||
"EnOcean" => { "subTypeReading" => "subType", "setModifier" => "desired-temp",
|
||||
"roomSensorControl.05" => 1,
|
||||
"hvac.01" => 1 },
|
||||
"MAX" => { "subTypeReading" => "type", "setModifier" => "desiredTemperature",
|
||||
"HeatingThermostatPlus" => 1,
|
||||
"HeatingThermostat" => 1,
|
||||
"WallMountedThermostat" => 1 },
|
||||
"CUL_HM" => { "subTypeReading" => "model","setModifier" => "desired-temp",
|
||||
"HM-CC-TC" => 1,
|
||||
"HM-TC-IT-WM-W-EU" => 1,
|
||||
"HM-CC-RT-DN" => 1 } );
|
||||
my $dHash = $defs{$hash->{DEVICE}};
|
||||
my $dType = $dHash->{TYPE};
|
||||
return "" if (!defined($dType));
|
||||
|
||||
my $setModifier = $setmodifiers{$dType};
|
||||
$setModifier = "" if (!defined($setModifier));
|
||||
if (ref($setModifier)) {
|
||||
|
||||
my $subTypeReading = $setmodifiers{$dType}{subTypeReading};
|
||||
|
||||
my $model;
|
||||
if ($subTypeReading eq "type" ) {
|
||||
$model = $dHash->{type};
|
||||
} else {
|
||||
$model = AttrVal($hash->{DEVICE}, $subTypeReading, "nF");
|
||||
}
|
||||
|
||||
if (defined($setmodifiers{$dType}{$model})) {
|
||||
$setModifier = $setmodifiers{$dType}{setModifier}
|
||||
} else {
|
||||
$setModifier = "";
|
||||
}
|
||||
}
|
||||
return $setModifier;
|
||||
}
|
||||
################################################################################
|
||||
#
|
||||
sub WeekdayTimer_FensterOffen ($$$) {
|
||||
my ($hash, $event, $time) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
my $verzoegerteAusfuehrungCond = AttrVal($hash->{NAME}, "delayedExecutionCond", "0");
|
||||
|
||||
my %specials= (
|
||||
"%HEATING_CONTROL" => $hash->{NAME},
|
||||
"%WEEKDAYTIMER" => $hash->{NAME},
|
||||
"%NAME" => $hash->{DEVICE},
|
||||
"%EVENT" => $event
|
||||
);
|
||||
$verzoegerteAusfuehrungCond = EvalSpecials($verzoegerteAusfuehrungCond, %specials);
|
||||
my $verzoegerteAusfuehrung = eval($verzoegerteAusfuehrungCond);
|
||||
|
||||
if ($verzoegerteAusfuehrung) {
|
||||
if (!defined($hash->{VERZOEGRUNG})) {
|
||||
Log3 $hash, 3, "[$name] switch of $hash->{DEVICE} delayed - $verzoegerteAusfuehrungCond is TRUE";
|
||||
}
|
||||
myRemoveInternalTimer("Update", $hash);
|
||||
myInternalTimer ("$time", time()+60, "$hash->{TYPE}_Update", $hash, 0);
|
||||
$hash->{VERZOEGRUNG} = 1;
|
||||
return 1
|
||||
}
|
||||
|
||||
my %contacts = ( "CUL_FHTTK" => { "READING" => "Window", "STATUS" => "(Open)", "MODEL" => "r" },
|
||||
"CUL_HM" => { "READING" => "state", "STATUS" => "(open|tilted)", "MODEL" => "r" },
|
||||
"MAX" => { "READING" => "state", "STATUS" => "(open)", "MODEL" => "r" },
|
||||
"WeekdayTimer" => { "READING" => "delayedExecution","STATUS" => "^1\$", "MODEL" => "a" },
|
||||
"Heating_Control" => { "READING" => "delayedExecution","STATUS" => "^1\$", "MODEL" => "a" }
|
||||
);
|
||||
|
||||
my $fensterKontakte = AttrVal($hash->{NAME}, "windowSensor", "")." ".$hash->{NAME};
|
||||
$fensterKontakte =~ s/^\s+//;
|
||||
$fensterKontakte =~ s/\s+$//;
|
||||
|
||||
Log3 $hash, 5, "[$name] list of window sensors found: '$fensterKontakte'";
|
||||
if ($fensterKontakte ne "" ) {
|
||||
my @kontakte = split("[ \t]+", $fensterKontakte);
|
||||
foreach my $fk (@kontakte) {
|
||||
if(!$defs{$fk}) {
|
||||
Log3 $hash, 3, "[$name] sensor <$fk> not found - check name.";
|
||||
} else {
|
||||
my $fk_hash = $defs{$fk};
|
||||
my $fk_typ = $fk_hash->{TYPE};
|
||||
if (!defined($contacts{$fk_typ})) {
|
||||
Log3 $hash, 3, "[$name] TYPE '$fk_typ' of $fk not yet supported, $fk ignored - inform maintainer";
|
||||
} else {
|
||||
|
||||
my $reading = $contacts{$fk_typ}{READING};
|
||||
my $statusReg = $contacts{$fk_typ}{STATUS};
|
||||
my $model = $contacts{$fk_typ}{MODEL};
|
||||
|
||||
my $windowStatus;
|
||||
if ($model eq "r") { ### Reading, sonst Attribut
|
||||
$windowStatus = ReadingsVal($fk,$reading,"nF");
|
||||
}else{
|
||||
$windowStatus = AttrVal ($fk,$reading,"nF");
|
||||
}
|
||||
|
||||
if ($windowStatus eq "nF") {
|
||||
Log3 $hash, 3, "[$name] Reading/Attribute '$reading' of $fk not found, $fk ignored - inform maintainer" if ($model eq "r");
|
||||
} else {
|
||||
Log3 $hash, 5, "[$name] sensor '$fk' Reading/Attribute '$reading' is '$windowStatus'";
|
||||
|
||||
if ($windowStatus =~ m/^$statusReg$/g) {
|
||||
if (!defined($hash->{VERZOEGRUNG})) {
|
||||
Log3 $hash, 3, "[$name] switch of $hash->{DEVICE} delayed - sensor '$fk' Reading/Attribute '$reading' is '$windowStatus'";
|
||||
}
|
||||
myRemoveInternalTimer("Update", $hash);
|
||||
#myInternalTimer ("Update", time()+60, "$hash->{TYPE}_Update", $hash, 0);
|
||||
myInternalTimer ("$time", time()+60, "$hash->{TYPE}_Update", $hash, 0);
|
||||
$hash->{VERZOEGRUNG} = 1;
|
||||
return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($hash->{VERZOEGRUNG}) {
|
||||
Log3 $hash, 3, "[$name] delay of switching $hash->{DEVICE} stopped.";
|
||||
}
|
||||
delete $hash->{VERZOEGRUNG};
|
||||
return 0;
|
||||
}
|
||||
################################################################################
|
||||
sub WeekdayTimer_Device_Schalten($$$) {
|
||||
my ($hash, $newParam, $tage) = @_;
|
||||
|
||||
my ($command, $condition) = "";
|
||||
my $name = $hash->{NAME}; ###
|
||||
|
||||
my $now = time();
|
||||
#modifier des Zieldevices auswaehlen
|
||||
my $setModifier = WeekdayTimer_isHeizung($hash);
|
||||
|
||||
$command = '{ fhem("set @ '. $setModifier .' %") }';
|
||||
$command = $hash->{COMMAND} if (defined $hash->{COMMAND});
|
||||
|
||||
$condition = WeekdayTimer_Condition($hash, $tage);
|
||||
|
||||
$command = "{ if " .$condition . " " . $command . "}";
|
||||
|
||||
my $isHeating = $setModifier gt "";
|
||||
my $aktParam = ReadingsVal($hash->{DEVICE}, $setModifier, "");
|
||||
$aktParam = sprintf("%.1f", $aktParam) if ($isHeating && $aktParam =~ m/^[0-9]{1,3}$/i);
|
||||
$newParam = sprintf("%.1f", $newParam) if ($isHeating && $newParam =~ m/^[0-9]{1,3}$/i);
|
||||
|
||||
my $disabled = AttrVal($hash->{NAME}, "disable", 0);
|
||||
my $disabled_txt = $disabled ? " " : " not";
|
||||
Log3 $hash, 5, "[$name] aktParam:$aktParam newParam:$newParam - is $disabled_txt disabled";
|
||||
|
||||
#Kommando ausführen
|
||||
if ($command && !$disabled && $aktParam ne $newParam) {
|
||||
$newParam =~ s/:/ /g;
|
||||
|
||||
$command = SemicolonEscape($command);
|
||||
my %specials= (
|
||||
"%NAME" => $hash->{DEVICE},
|
||||
"%EVENT" => $newParam,
|
||||
);
|
||||
$command= EvalSpecials($command, %specials);
|
||||
|
||||
Log3 $hash, 4, "[$name] command: $command executed";
|
||||
my $ret = AnalyzeCommandChain(undef, $command);
|
||||
Log3 ($hash, 3, $ret) if($ret);
|
||||
}
|
||||
}
|
||||
################################################################################
|
||||
sub WeekdayTimer_Condition($$) {
|
||||
my ($hash, $tage) = @_;
|
||||
|
||||
my $condition = "( ";
|
||||
$condition .= (defined $hash->{CONDITION}) ? $hash->{CONDITION} : 1 ;
|
||||
$condition .= " && " . WeekdayTimer_TageAsCondition($tage);
|
||||
$condition .= ")";
|
||||
|
||||
return $condition;
|
||||
|
||||
}
|
||||
################################################################################
|
||||
sub WeekdayTimer_TageAsCondition ($) {
|
||||
my $tage = shift;
|
||||
|
||||
my %days = map {$_ => 1} @$tage;
|
||||
|
||||
my $we = $days{7}; delete $days{7}; # $we
|
||||
my $notWe = $days{8}; delete $days{8}; #!$we
|
||||
|
||||
my $tageExp = '($wday ~~ [' . join (",", sort keys %days) . "]";
|
||||
$tageExp .= ' || $we' if defined $we;
|
||||
$tageExp .= ' || !$we' if defined $notWe;
|
||||
$tageExp .= ')';
|
||||
|
||||
return $tageExp;
|
||||
|
||||
}
|
||||
################################################################################
|
||||
sub WeekdayTimer_Attr($$$) {
|
||||
my ($cmd, $name, $attrName, $attrVal) = @_;
|
||||
|
||||
@ -101,23 +741,14 @@ sub WeekdayTimer_Attr($$$) {
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
########################################################################
|
||||
sub WeekdayTimer_SetAllParms() { # {WeekdayTimer_SetAllParms()}
|
||||
################################################################################
|
||||
sub WeekdayTimer_SetAllParms() { # {WeekdayTimer_SetAllParms()}
|
||||
|
||||
foreach my $hc ( sort keys %{$modules{WeekdayTimer}{defptr}} ) {
|
||||
my $hash = $modules{WeekdayTimer}{defptr}{$hc};
|
||||
|
||||
if($hash->{helper}{CONDITION}) {
|
||||
if (!(eval ($hash->{helper}{CONDITION}))) {
|
||||
readingsSingleUpdate ($hash, "state", "inactive", 1);
|
||||
next;
|
||||
}
|
||||
}
|
||||
my $myHash->{HASH}=$hash;
|
||||
$hash->{setAllTempMode} = 1;
|
||||
WeekdayTimer_Update($myHash);
|
||||
delete $hash->{setAllTempMode};
|
||||
Log3 undef, 3, "WeekdayTimer_Update() for $hash->{NAME} done!";
|
||||
WeekdayTimer_SetTimer($hash);
|
||||
Log3 undef, 3, "WeekdayTimer_SetAllParms() for $hash->{NAME} done!";
|
||||
}
|
||||
Log3 undef, 3, "WeekdayTimer_SetAllParms() done!";
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user