diff --git a/fhem/CHANGED b/fhem/CHANGED
index feb2187a2..868f0bc8f 100644
--- a/fhem/CHANGED
+++ b/fhem/CHANGED
@@ -1,10 +1,11 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it.
+ - changed: 98_WeekdayTimer: remove Twilight dependency
- feature: 74_AMADautomagicflowset: add support for Android 9 Pie
- feature: 88_HMCCU: New commands and bug fixes
- changed: 93_DbLog: CommandRef revised
- - feature: 98_HTTPMOD: starting with featurelevel >5.9 enableCookies,
- enableControlSet, handleRedirects and
+ - feature: 98_HTTPMOD: starting with featurelevel >5.9 enableCookies,
+ enableControlSet, handleRedirects and
enforceGoodReadingNames will default to 1
- feature: 49_SSCam: new streaming device type "lastsnap", minor fixes
- change: 93_Log2Syslog: version numbering
@@ -15,7 +16,7 @@
- feature: 12_HProtocolTank: added Product to calculate 15 degrees volume
- new: 70_ZoneMinder: fetching Event-Details via API after event received
- new: 71_ZM_Monitor: fetching Event-Details via API after event received
- - bugfix: 49_SSCam: V8.8.1, fix need attr snapGalleryBoost when sending a
+ - bugfix: 49_SSCam: V8.8.1, fix need attr snapGalleryBoost when sending a
snapshot by telegram
- new: 39_gassistant: Google Assistant support
- bugfix: 70_BOTVAC: vendor name is no longer case sensitive
@@ -35,21 +36,21 @@
- bugfix: 49_SSCam: fix version numbering
- bugfix: 93_DbRep: fix version numbering
- feature: 98_livetracking: added events for OwnTrack zones
- - feature: 93_DbRep: command exportToFile or attribute "expimpfile" accept
+ - feature: 93_DbRep: command exportToFile or attribute "expimpfile" accept
option "MAXLINES="
- bugfix: 09_CUL_FHTTK: removed low batt information from state, moved to
reading batteryState
- feature: 59_Weather: add limit forecast, selection between daily and hourly
forecast
- - feature: 49_SSCam: V8.6.1, new attribute snapReadingRotate, time format in
- readings and galleries depends from global language
+ - feature: 49_SSCam: V8.6.1, new attribute snapReadingRotate, time format in
+ readings and galleries depends from global language
attribute, minor bug fixes
- - feature: 98_freezemon: New freeze statistics
+ - feature: 98_freezemon: New freeze statistics
- bugfix: 14_CUL_TCM97001: Fix uninitialized value warning
- - bugfix: 14_CUL_REDIRECT: Fix uninitialized value warning
+ - bugfix: 14_CUL_REDIRECT: Fix uninitialized value warning
- feature: 93_DbRep: sqlCmd can handle SQL session variables, Forum:#96082
- bugfix: 98_DOIFtools: remove Log in DOIFtoolsNextTimer
- - bugfix: 93_DbRep: V8.9.10, fix warnings Malformed UTF-8 character during
+ - bugfix: 93_DbRep: V8.9.10, fix warnings Malformed UTF-8 character during
importFromFile, Forum:#96056
- change: 59_Weather: add Internal MODEL for statistic
- feature: 70_BRAVIA: new set command openUrl
@@ -60,7 +61,7 @@
- bugfix: DarkSkyAPI: fix reading values, add API Version
- bugfix: 82_LGTV_WebOS: add SkyOnline at set commands
- feature: 49_SSCam: 8.5.0, SVS device has "snapCams" command to take
- snapshots of all cameras and optionally send them
+ snapshots of all cameras and optionally send them
alltogether by Email
- change: 59_Weather: Codestyle, add attribut model to API
- bugfix: 73_AutoShuttersControl: fix drive up then roommate asleep
@@ -77,7 +78,7 @@
- bugfix: DarkSkyAPI: OpenWeatherMapAPI, Weather - fix bugs, change
day_of_week formated, delete deprecated readings
- bugfixe: DarkSkyAPI: fix hourly wind at factor 3.6
- - new: 44_ROLLO: Universal module to precisely control shutters/blinds
+ - new: 44_ROLLO: Universal module to precisely control shutters/blinds
which support only open/close/stop
- bugfix: 59_Weather: DarkSky and OpenWeatherMapAPI change formated output,
add hourly support for DarkSky - thanks to Lippie,
diff --git a/fhem/FHEM/98_WeekdayTimer.pm b/fhem/FHEM/98_WeekdayTimer.pm
index 9812ad4a3..91b2a7869 100644
--- a/fhem/FHEM/98_WeekdayTimer.pm
+++ b/fhem/FHEM/98_WeekdayTimer.pm
@@ -1,115 +1,94 @@
+# Id ##########################################################################
# $Id$
-##############################################################################
+
+# copyright ###################################################################
#
-# 98_WeekdayTimer.pm
-# written by Dietmar Ortmann
-# modified by Tobias Faust
-# Maintained by igami since 02-2018
+# 98_WeekdayTimer.pm
+# written by Dietmar Ortmann
+# modified by Tobias Faust
+# Maintained by igami since 2018 - Thanks Dietmar for all you did for FHEM, RIP
#
-# This file is part of fhem.
+# This file is part of FHEM.
#
-# Fhem is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
+# FHEM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
#
-# Fhem is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
+# FHEM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with fhem. If not, see .
-#
-##############################################################################
+# You should have received a copy of the GNU General Public License
+# along with FHEM. If not, see .
+
+# packages ####################################################################
package main;
-use strict;
-use warnings;
-use POSIX;
+ use strict;
+ use warnings;
+ use POSIX;
-use Time::Local 'timelocal_nocheck';
+ use Time::Local 'timelocal_nocheck';
+ use Data::Dumper;
+ $Data::Dumper::Sortkeys = 1;
-use Data::Dumper;
-$Data::Dumper::Sortkeys = 1;
+# forward declarations ########################################################
+sub WeekdayTimer_Initialize($);
-################################################################################
+sub WeekdayTimer_Define($$);
+sub WeekdayTimer_Undef($$);
+sub WeekdayTimer_Set($@);
+sub WeekdayTimer_Get($@);
+sub WeekdayTimer_Attr($$$$);
+sub WeekdayTimer_Update($);
+
+sub WeekdayTimer_GetHashIndirekt($$);
+sub WeekdayTimer_InternalTimer($$$$$);
+sub WeekdayTimer_RemoveInternalTimer($$);
+
+sub WeekdayTimer_Condition($$);
+sub WeekdayTimer_daylistAsArray($$);
+sub WeekdayTimer_delayedTimerInPast($);
+sub WeekdayTimer_DeleteTimer($);
+sub WeekdayTimer_Device_Schalten($$$);
+sub WeekdayTimer_EchteZeit($$$);
+sub WeekdayTimer_evalAndcleanupParam($$$$);
+sub WeekdayTimer_FensterOffen($$$);
+sub WeekdayTimer_gatherSwitchingTimes;
+sub WeekdayTimer_getListeDerTage($$);
+sub WeekdayTimer_GlobalDaylistSpec;
+sub WeekdayTimer_InitHelper($);
+sub WeekdayTimer_isAnActiveTimer($$$);
+sub WeekdayTimer_isHeizung($);
+sub WeekdayTimer_Language;
+sub WeekdayTimer_Profile($);
+sub WeekdayTimer_searchAktNext($$);
+sub WeekdayTimer_SetAllParms();
+sub WeekdayTimer_SetParm($);
+sub WeekdayTimer_SetTimer($);
+sub WeekdayTimer_SetTimerForMidnightUpdate($);
+sub WeekdayTimer_SetTimerOfDay($);
+sub WeekdayTimer_SwitchingTime($$);
+sub WeekdayTimer_TageAsCondition($);
+sub WeekdayTimer_tageAsHash($$);
+sub WeekdayTimer_zeitErmitteln($$$$$);
+
+# initialize ##################################################################
sub WeekdayTimer_Initialize($){
my ($hash) = @_;
+ my $TYPE = "WeekdayTimer";
- 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->{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 switchInThePast:0,1 commandTemplate ".
- $readingFnAttributes;
+ $hash->{DefFn} = $TYPE."_Define";
+ $hash->{UndefFn} = $TYPE."_Undef";
+ $hash->{SetFn} = $TYPE."_Set";
+ $hash->{GetFn} = $TYPE."_Get";
+ $hash->{AttrFn} = $TYPE."_Attr";
+ $hash->{UpdFn} = $TYPE."_Update";
+ $hash->{AttrList}= "disable:0,1 delayedExecutionCond switchInThePast:0,1 commandTemplate $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($@) {
- my ($hash, @a) = @_;
-
- return "no set value specified" if(int(@a) < 2);
- return "Unknown argument $a[1], choose one of enable disable " if($a[1] eq "?");
-
- my $name = shift @a;
- my $v = join(" ", @a);
-
- Log3 $hash, 3, "[$name] set $name $v";
-
- if ($v eq "enable") {
- fhem("attr $name disable 0");
- } elsif ($v eq "disable") {
- fhem("attr $name disable 1");
- }
- return undef;
-}
-################################################################################
-sub WeekdayTimer_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 WeekdayTimer_Undef($$) {
- my ($hash, $arg) = @_;
-
- foreach my $idx (keys %{$hash->{profil}}) {
- myRemoveInternalTimer($idx, $hash);
- }
- myRemoveInternalTimer("SetTimerOfDay", $hash);
- delete $modules{$hash->{TYPE}}{defptr}{$hash->{NAME}};
- return undef;
-}
-################################################################################
+# regular Fn ##################################################################
sub WeekdayTimer_Define($$) {
my ($hash, $def) = @_;
WeekdayTimer_InitHelper($hash);
@@ -181,532 +160,74 @@ sub WeekdayTimer_Define($$) {
return undef;
}
-################################################################################
-sub WeekdayTimer_Profile($) {
- my $hash = shift;
- my $language = $hash->{LANGUAGE};
- my %longDays = %{$hash->{longDays}};
+sub WeekdayTimer_Undef($$) {
+ my ($hash, $arg) = @_;
- delete $hash->{profil};
- my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time());
-
- my $now = time();
-# ---- Zeitpunkte den Tagen zuordnen -----------------------------------
- my $idx = 0;
- foreach my $st (@{$hash->{SWITCHINGTIMES}}) {
- my ($tage,$time,$parameter) = WeekdayTimer_SwitchingTime ($hash, $st);
-
- $idx++;
- foreach my $d (@{$tage}) {
-
- my @listeDerTage = ($d);
- push (@listeDerTage, WeekdayTimer_getListeDerTage($d, $time)) if ($d>=7);
-
- map { my $day = $_;
- my $dayOfEchteZeit = $day;
- $dayOfEchteZeit = ($wday>=1&&$wday<=5) ? 6 : $wday if ($day==7); # ggf. Samstag $wday ~~ [1..5]
- $dayOfEchteZeit = ($wday==0||$wday==6) ? 1 : $wday if ($day==8); # ggf. Montag $wday ~~ [0, 6]
- my $echtZeit = WeekdayTimer_EchteZeit($hash, $dayOfEchteZeit, $time);
- $hash->{profile} {$day}{$echtZeit} = $parameter;
- $hash->{profile_IDX}{$day}{$echtZeit} = $idx;
- } @listeDerTage;
- }
+ foreach my $idx (keys %{$hash->{profil}}) {
+ WeekdayTimer_RemoveInternalTimer($idx, $hash);
}
-# ---- Zeitpunkte des aktuellen Tages mit EPOCH ermitteln --------------
- $idx = 0;
- 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);
+ WeekdayTimer_RemoveInternalTimer("SetTimerOfDay", $hash);
+ delete $modules{$hash->{TYPE}}{defptr}{$hash->{NAME}};
+ return undef;
+}
- $idx++;
- $hash->{profil}{$idx}{TIME} = $time;
- $hash->{profil}{$idx}{PARA} = $parameter;
- $hash->{profil}{$idx}{EPOCH} = WeekdayTimer_zeitErmitteln ($now, $stunde, $minute, $sekunde, 0);
- $hash->{profil}{$idx}{TAGE} = $tage;
+sub WeekdayTimer_Set($@) {
+ my ($hash, @a) = @_;
+
+ return "no set value specified" if(int(@a) < 2);
+ return "Unknown argument $a[1], choose one of enable disable " if($a[1] eq "?");
+
+ my $name = shift @a;
+ my $v = join(" ", @a);
+
+ Log3 $hash, 3, "[$name] set $name $v";
+
+ if ($v eq "enable") {
+ fhem("attr $name disable 0");
+ } elsif ($v eq "disable") {
+ fhem("attr $name disable 1");
}
-# ---- Texte Readings aufbauen -----------------------------------------
- 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)";
- }
-
- # für logProxy umhaengen
- $hash->{helper}{SWITCHINGTIME} = $hash->{profile};
- delete $hash->{profile};
-}
-################################################################################
-sub WeekdayTimer_getListeDerTage($$) {
- my ($d, $time) = @_;
-
- my %hdays=();
- @hdays{(0, 6)} = undef if ($d==7); # sa,so ( $we)
- @hdays{(1..5)} = undef if ($d==8); # mo-fr (!$we)
-
- my $wday;
- my $now = time();
- my ($sec,$min,$hour,$mday,$mon,$year,$nowWday,$yday,$isdst) = localtime($now);
-
- my @realativeWdays = (0..6);
- for (my $i=0;$i<=6;$i++) {
-
- my $relativeDay = $i-$nowWday;
- #Log 3, "relativeDay------------>$relativeDay";
- my ($stunde, $minute, $sekunde) = split (":",$time);
-
- my $echteZeit = WeekdayTimer_zeitErmitteln ($now, $stunde, $minute, $sekunde, $relativeDay);
- #Log 3, "echteZeit---$i---->>>$relativeDay<<<----->".FmtDateTime($echteZeit);
- ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($echteZeit);
- my $h2we = $attr{global}{holiday2we};
- if($h2we) {
- my $ergebnis = fhem("get $h2we ".sprintf("%02d-%02d",$mon+1,$mday),1);
- if ($ergebnis ne "none") {
- #Log 3, "ergebnis-------$i----->$ergebnis";
- $hdays{$i} = undef if ($d==7); # $we Tag aufnehmen
- delete $hdays{$i} if ($d==8); # !$we Tag herausnehmen
- }
- }
- }
-
- #Log 3, "result------------>" . join (" ", sort keys %hdays);
- return keys %hdays;
-}
-################################################################################
-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);
+ return undef;
}
-################################################################################
-sub WeekdayTimer_daylistAsArray($$){
- my ($hash, $daylist) = @_;
+sub WeekdayTimer_Get($@) {
+ my ($hash, @a) = @_;
+ return "argument is missing" if(int(@a) != 2);
- my $name = $hash->{NAME};
- my @days;
+ $hash->{LOCAL} = 1;
+ delete $hash->{LOCAL};
+ my $reading= $a[1];
+ my $value;
- 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 $hash = shift;
- my $a = shift;
-
- my $name = $hash->{NAME};
- my @switchingtimes = ();
- my $conditionOrCommand;
-
- # switchingtime einsammeln
- while (@$a > 0) {
-
- #pruefen auf Angabe eines Schaltpunktes
- my $element = "";
- my @restoreElements = ();
-E: while (@$a > 0) {
-
- my $actualElement = shift @$a;
- push @restoreElements, $actualElement;
- $element = $element . $actualElement . " ";
- Log3 $hash, 5, "[$name] $element - trying to accept as a switchtime";
-
- # prüfen ob Anführungszeichen paarig sind
- my @quotes = ('"', "'" );
- foreach my $quote (@quotes){
- my $balancedSign = eval "((\$element =~ tr/$quote//))";
- if ($balancedSign % 2) { # ungerade Anzahl quotes, dann verlängern
- Log3 $hash, 5, "[$name] $element - unbalanced quotes: $balancedSign $quote found";
- next E;
- }
- }
-
- # prüfen ob öffnende/schliessende Klammern paarig sind
- my %signs = ('('=>')', '{'=>'}');
- foreach my $signOpened (keys(%signs)) {
- my $signClosed = $signs{$signOpened};
- my $balancedSign = eval "((\$element =~ tr/$signOpened//) - (\$element =~ tr/$signClosed//))";
- if ($balancedSign) { # öffnende/schließende Klammern nicht gleich, dann verlängern
- Log3 $hash, 5, "[$name] $element - unbalanced brackets $signOpened$signClosed:$balancedSign";
- next E;
- }
- }
- last;
- }
-
- # ein space am Ende wieder abschneiden
- $element = substr ($element, 0, length($element)-1);
- my @t = split(/\|/, $element);
- my $anzahl = @t;
-
- if ( ($anzahl == 2 || $anzahl == 3) && $t[0] gt "" && $t[1] gt "" ) {
- Log3 $hash, 4, "[$name] $element - accepted";
- push(@switchingtimes, $element);
- } else {
- Log3 $hash, 4, "[$name] $element - NOT accepted, must be command or condition";
- unshift @$a, @restoreElements;
- 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) {
+ if(defined($hash->{READINGS}{$reading})) {
+ $value= $hash->{READINGS}{$reading}{VAL};
} else {
- Log3 ($hash, 3, "[$name] language: $language not recognized, use one of $langRegExp") if (length($language) == 2);
- unshift @$a, $language;
- $language = "de";
+ return "no such reading: $reading";
}
- $hash->{LANGUAGE} = $language;
-
- $language = $hash->{LANGUAGE};
- return ($langRegExp, $language);
+ return "$a[0] $reading => $value";
}
-################################################################################
-sub WeekdayTimer_GlobalDaylistSpec {
- my ($hash, $a) = @_;
- my $daylist = shift @$a;
+sub WeekdayTimer_Attr($$$$) {
+ my ($cmd, $name, $attrName, $attrVal) = @_;
- my @tage = @{WeekdayTimer_daylistAsArray($hash, $daylist)};
- my $tage = @tage;
- if ($tage > 0) {
- ;
- } else {
- unshift (@$a,$daylist);
- $daylist = "";
+ $attrVal = 0 if(!defined $attrVal);
+
+ my $hash = $defs{$name};
+ if( $attrName eq "disable" ) {
+ readingsSingleUpdate ($hash, "disabled", $attrVal, 1);
+ } elsif ( $attrName eq "enable" ) {
+ WeekdayTimer_SetTimerOfDay({ HASH => $hash});
+ } elsif ( $attrName eq "switchInThePast" ) {
+ $attr{$name}{$attrName} = $attrVal;
+ WeekdayTimer_SetTimerOfDay({ HASH => $hash});
}
-
- $hash->{GlobalDaylistSpec} = $daylist;
+ return undef;
}
-################################################################################
-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 $midnightPlus5Seconds = WeekdayTimer_zeitErmitteln ($now, 0, 0, 5, 1);
- #Log3 $hash, 3, "midnightPlus5Seconds------------>".FmtDateTime($midnightPlus5Seconds);
-
- myRemoveInternalTimer("SetTimerOfDay", $hash);
- my $newMyHash = myInternalTimer ("SetTimerOfDay", $midnightPlus5Seconds, "$hash->{TYPE}_SetTimerOfDay", $hash, 0);
- $newMyHash->{SETTIMERATMIDNIGHT} = 1;
-
-}
-################################################################################
-sub WeekdayTimer_SetTimerOfDay($) {
- my ($myHash) = @_;
- my $hash = myGetHashIndirekt($myHash, (caller(0))[3]);
- return if (!defined($hash));
-
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time());
- my $secSinceMidnight = 3600*$hour + 60*$min + $sec;
-
- $hash->{SETTIMERATMIDNIGHT} = $myHash->{SETTIMERATMIDNIGHT};
- WeekdayTimer_DeleteTimer($hash);
- WeekdayTimer_Profile ($hash);
- WeekdayTimer_SetTimer ($hash);
- delete $hash->{SETTIMERATMIDNIGHT};
-
- WeekdayTimer_SetTimerForMidnightUpdate( { HASH => $hash} );
-}
-################################################################################
-sub WeekdayTimer_SetTimer($) {
- my $hash = shift;
- my $name = $hash->{NAME};
-
- my $now = time();
-
- my $isHeating = WeekdayTimer_isHeizung($hash);
- my $swip = AttrVal($name, "switchInThePast", 0);
- my $switchInThePast = ($swip || $isHeating);
-
- Log3 $hash, 4, "[$name] Heating recognized - switch in the past activated" if ($isHeating);
- Log3 $hash, 4, "[$name] no switch in the yesterdays because of the devices type($hash->{DEVICE} is not recognized as heating) - use attr switchInThePast" if (!$switchInThePast && !defined $hash->{SETTIMERATMIDNIGHT});
-
- my @switches = sort keys %{$hash->{profil}};
- if ($#switches < 0) {
- Log3 $hash, 3, "[$name] no switches to send, due to possible errors.";
- return;
- }
-
- readingsSingleUpdate ($hash, "state", "inactive", 1) if (!defined $hash->{SETTIMERATMIDNIGHT});
- for(my $i=0; $i<=$#switches; $i++) {
-
- my $idx = $switches[$i];
-
- my $time = $hash->{profil}{$idx}{TIME};
- my $timToSwitch = $hash->{profil}{$idx}{EPOCH};
- my $tage = $hash->{profil}{$idx}{TAGE};
- my $para = $hash->{profil}{$idx}{PARA};
-
- my $secondsToSwitch = $timToSwitch - $now;
-
- my $isActiveTimer = WeekdayTimer_isAnActiveTimer ($hash, $tage, $para);
- readingsSingleUpdate ($hash, "state", "active", 1)
- if (!defined $hash->{SETTIMERATMIDNIGHT} && $isActiveTimer);
-
- if ($secondsToSwitch>-5 || defined $hash->{SETTIMERATMIDNIGHT} ) {
- if($isActiveTimer) {
- Log3 $hash, 4, "[$name] setTimer - timer seems to be active today: ".join("",@$tage)."|$time|$para";
- } else {
- Log3 $hash, 4, "[$name] setTimer - timer seems to be NOT active today: ".join("",@$tage)."|$time|$para ". $hash->{CONDITION};
- }
- myRemoveInternalTimer("$idx", $hash);
- myInternalTimer ("$idx", $timToSwitch, "$hash->{TYPE}_Update", $hash, 0);
- }
- }
-
- if (defined $hash->{SETTIMERATMIDNIGHT}) {
- return;
- }
-
- my ($aktIdx,$aktTime,$aktParameter,$nextTime,$nextParameter) =
- WeekdayTimer_searchAktNext($hash, time()+5);
- if(!defined $aktTime) {
- Log3 $hash, 3, "[$name] can not compute past switching time";
- }
-
- readingsSingleUpdate ($hash, "nextUpdate", FmtDateTime($nextTime), 1);
- readingsSingleUpdate ($hash, "nextValue", $nextParameter, 1);
- readingsSingleUpdate ($hash, "currValue", $aktParameter, 1); # HB
-
- if ($switchInThePast && defined $aktTime) {
- # Fensterkontakte abfragen - wenn einer im Status closed, dann Schaltung um 60 Sekunden verzögern
- if (WeekdayTimer_FensterOffen($hash, $aktParameter, $aktIdx)) {
- return;
- }
-
- # alle in der Vergangenheit liegenden Schaltungen sammeln und
- # nach 5 Sekunden in der Reihenfolge der Schaltzeiten
- # durch WeekdayTimer_delayedTimerInPast() als Timer einstellen
- # die Parameter merken wir uns kurzzeitig im hash
- # modules{WeekdayTimer}{timerInThePast}
- my $device = $hash->{DEVICE};
- Log3 $hash, 4, "[$name] past timer on $hash->{DEVICE} at ". FmtDateTime($aktTime). " with $aktParameter activated";
-
- my $parameter = $modules{WeekdayTimer}{timerInThePast}{$device}{$aktTime};
- $parameter = [] if (!defined $parameter);
- push (@$parameter,["$aktIdx", $aktTime, "$hash->{TYPE}_Update", $hash, 0]);
- $modules{WeekdayTimer}{timerInThePast}{$device}{$aktTime} = $parameter;
-
- my $tipHash = $modules{WeekdayTimer}{timerInThePastHash};
- $tipHash = $hash if (!defined $tipHash);
- $modules{WeekdayTimer}{timerInThePastHash} = $tipHash;
-
- myRemoveInternalTimer("delayed", $tipHash);
- myInternalTimer ("delayed", time()+5, "WeekdayTimer_delayedTimerInPast", $tipHash, 0);
-
- }
-}
-################################################################################
-sub WeekdayTimer_delayedTimerInPast($) {
- my ($myHash) = @_;
- my $hash = myGetHashIndirekt($myHash, (caller(0))[3]);
- return if (!defined($hash));
-
- my $tim = time();
- my $tipIpHash = $modules{WeekdayTimer}{timerInThePast};
-
- foreach my $device ( keys %$tipIpHash ) {
- foreach my $time ( sort keys %{$tipIpHash->{$device}} ) {
- Log3 $hash, 4, "$device ".FmtDateTime($time)." ".($tim-$time)."s ";
-
- foreach my $para ( @{$tipIpHash->{$device}{$time}} ) {
- myRemoveInternalTimer(@$para[0], @$para[3]);
- my $mHash =myInternalTimer (@$para[0],@$para[1],@$para[2],@$para[3],@$para[4]);
- $mHash->{immerSchalten} = 1;
- }
- }
- }
- delete $modules{WeekdayTimer}{timerInThePast};
- delete $modules{WeekdayTimer}{timerInThePastHash}
-}
-################################################################################
-sub WeekdayTimer_searchAktNext($$) {
- my ($hash, $now) = @_;
- my $name = $hash->{NAME};
-
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($now);
- #Log3 $hash, 3, "[$name] such--->".FmtDateTime($now);
-
- my ($oldTag, $oldTime, $oldPara , $oldIdx);
- my ($nextTag, $nextTime, $nextPara, $nextIdx);
-
- my $language = $hash->{LANGUAGE};
- my %shortDays = %{$hash->{shortDays}};
-
- my @realativeWdays = ($wday..6,0..$wday-1,$wday..6,0..6);
- for (my $i=0;$i<=$#realativeWdays;$i++) {
-
- my $relativeDay = $i-7;
- my $relWday = $realativeWdays[$i];
-
- foreach my $time (sort keys %{$hash->{helper}{SWITCHINGTIME}{$relWday}}) {
- my ($stunde, $minute, $sekunde) = split (":",$time);
-
- $oldTime = $nextTime;
- $oldPara = $nextPara;
- $oldIdx = $nextIdx;
- $oldTag = $nextTag;
-
- $nextTime = WeekdayTimer_zeitErmitteln ($now, $stunde, $minute, $sekunde, $relativeDay);
- $nextPara = $hash->{helper}{SWITCHINGTIME}{$relWday}{$time};
- $nextIdx = $hash->{profile_IDX}{$relWday}{$time};
- $nextTag = $relWday;
-
- #Log3 $hash, 3, $shortDays{$language}[$nextTag]." ".FmtDateTime($nextTime)." ".$nextPara." ".$nextIdx;
-
- if ($nextTime >= $now) {
- #Log3 $hash, 3, "oldIdx------------->$oldIdx";
- #Log3 $hash, 3, "oldTime------------>".FmtDateTime($oldTime);
- #Log3 $hash, 3, "oldPara------------>$oldPara";
- return ($oldIdx, $oldTime, $oldPara, $nextTime, $nextPara);
- }
- }
- }
- return (undef,undef,undef,undef);
-}
-################################################################################
-sub WeekdayTimer_DeleteTimer($) {
- my $hash = shift;
- map {myRemoveInternalTimer ($_, $hash)} keys %{$hash->{profil}};
-}
-################################################################################
sub WeekdayTimer_Update($) {
my ($myHash) = @_;
- my $hash = myGetHashIndirekt($myHash, (caller(0))[3]);
+ my $hash = WeekdayTimer_GetHashIndirekt($myHash, (caller(0))[3]);
return if (!defined($hash));
my $name = $hash->{NAME};
@@ -764,53 +285,250 @@ sub WeekdayTimer_Update($) {
return 1;
}
-################################################################################
-sub WeekdayTimer_isAnActiveTimer ($$$) {
- my ($hash, $tage, $newParam) = @_;
- my $name = $hash->{NAME};
- my %specials = ( "%NAME" => $hash->{DEVICE}, "%EVENT" => $newParam);
+# Twilight Fn #################################################################
+sub WeekdayTimer_GetHashIndirekt($$) {
+ my ($myHash, $function) = @_;
- my $condition = WeekdayTimer_Condition ($hash, $tage);
- my $tageAsHash = WeekdayTimer_tageAsHash($hash, $tage);
- my $xPression = "{".$tageAsHash.";;".$condition ."}";
- $xPression = EvalSpecials($xPression, %specials);
- Log3 $hash, 5, "[$name] condition: $xPression";
-
- my $ret = AnalyzeCommandChain(undef, $xPression);
- #Log3 $hash, 3, "[$name] condition:>>>$ret<<< $xPression";
- Log3 $hash, 5, "[$name] result of condition:$ret";
- return $ret;
+ if (!defined($myHash->{HASH})) {
+ Log 3, "[$function] myHash not valid";
+ return undef;
+ };
+ return $myHash->{HASH};
}
-################################################################################
-# {WeekdayTimer_isHeizung($defs{HeizungKueche_an_wt})}
-sub WeekdayTimer_isHeizung($) {
- my ($hash) = @_;
+
+sub WeekdayTimer_InternalTimer($$$$$) {
+ my ($modifier, $tim, $callback, $hash, $waitIfInitNotDone) = @_;
+
+ my $timerName = "$hash->{NAME}_$modifier";
+ my $mHash = { HASH=>$hash, NAME=>"$hash->{NAME}_$modifier", MODIFIER=>$modifier};
+ if (defined($hash->{TIMER}{$timerName})) {
+ Log3 $hash, 1, "[$hash->{NAME}] possible overwriting of timer $timerName - please delete first";
+ stacktrace();
+ } else {
+ $hash->{TIMER}{$timerName} = $mHash;
+ }
+
+ Log3 $hash, 5, "[$hash->{NAME}] setting Timer: $timerName " . FmtDateTime($tim);
+ InternalTimer($tim, $callback, $mHash, $waitIfInitNotDone);
+ return $mHash;
+}
+
+sub WeekdayTimer_RemoveInternalTimer($$) {
+ my ($modifier, $hash) = @_;
+
+ my $timerName = "$hash->{NAME}_$modifier";
+ my $myHash = $hash->{TIMER}{$timerName};
+ if (defined($myHash)) {
+ delete $hash->{TIMER}{$timerName};
+ Log3 $hash, 5, "[$hash->{NAME}] removing Timer: $timerName";
+ RemoveInternalTimer($myHash);
+ }
+}
+
+# module Fn ###################################################################
+sub WeekdayTimer_Condition($$) {
+ my ($hash, $tage) = @_;
my $name = $hash->{NAME};
+ Log3 $hash, 4, "[$name] condition:$hash->{CONDITION} - Tage:".join(",",@$tage);
- my $dHash = $defs{$hash->{DEVICE}};
- return "" if (!defined $dHash); # vorzeitiges Ende wenn das device nicht existiert
+ my $condition = "( ";
+ $condition .= ($hash->{CONDITION} gt "") ? $hash->{CONDITION} : 1 ;
+ $condition .= " && " . WeekdayTimer_TageAsCondition($tage);
+ $condition .= ")";
- my $dType = $dHash->{TYPE};
- return "" if (!defined($dType) || $dType eq "dummy" );
+ return $condition;
- my $dName = $dHash->{NAME};
+}
- my @tempSet = ("desired-temp", "desiredTemperature", "desired", "thermostatSetpointSet");
- my $allSets = getAllSets($dName);
+sub WeekdayTimer_daylistAsArray($$) {
+ my ($hash, $daylist) = @_;
- foreach my $ts (@tempSet) {
- if ($allSets =~ m/$ts/) {
- Log3 $hash, 4, "[$name] device type heating recognized, setModifier:$ts";
- return $ts
+ 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_delayedTimerInPast($) {
+ my ($myHash) = @_;
+ my $hash = WeekdayTimer_GetHashIndirekt($myHash, (caller(0))[3]);
+ return if (!defined($hash));
+
+ my $tim = time();
+ my $tipIpHash = $modules{WeekdayTimer}{timerInThePast};
+
+ foreach my $device ( keys %$tipIpHash ) {
+ foreach my $time ( sort keys %{$tipIpHash->{$device}} ) {
+ Log3 $hash, 4, "$device ".FmtDateTime($time)." ".($tim-$time)."s ";
+
+ foreach my $para ( @{$tipIpHash->{$device}{$time}} ) {
+ WeekdayTimer_RemoveInternalTimer(@$para[0], @$para[3]);
+ my $mHash =WeekdayTimer_InternalTimer (@$para[0],@$para[1],@$para[2],@$para[3],@$para[4]);
+ $mHash->{immerSchalten} = 1;
+ }
}
}
-
+ delete $modules{WeekdayTimer}{timerInThePast};
+ delete $modules{WeekdayTimer}{timerInThePastHash}
}
-################################################################################
-#
-sub WeekdayTimer_FensterOffen ($$$) {
+
+sub WeekdayTimer_DeleteTimer($) {
+ my $hash = shift;
+ map {WeekdayTimer_RemoveInternalTimer ($_, $hash)} keys %{$hash->{profil}};
+}
+
+sub WeekdayTimer_Device_Schalten($$$) {
+ my ($hash, $newParam, $tage) = @_;
+
+ my ($command, $condition, $tageAsHash) = "";
+ my $name = $hash->{NAME}; ###
+ my $dummy = "";
+
+ my $now = time();
+ #modifier des Zieldevices auswaehlen
+ my $setModifier = WeekdayTimer_isHeizung($hash);
+
+ $attr{$name}{commandTemplate} =
+ 'set $NAME '. $setModifier .' $EVENT' if (!defined $attr{$name}{commandTemplate});
+
+ $command = AttrVal($hash->{NAME}, "commandTemplate", "commandTemplate not found");
+ $command = $hash->{COMMAND} if ($hash->{COMMAND} gt "");
+
+ my $activeTimer = 1;
+
+ 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 $aktParam = WeekdayTimer_evalAndcleanupParam($hash,$dummy,ReadingsVal($hash->{DEVICE}, $setModifier, ""),$isHeating);
+ # newParam is already processed by evalAndcleanupParam()
+
+ my $disabled = AttrVal($hash->{NAME}, "disable", 0);
+ my $disabled_txt = $disabled ? " " : " not";
+ Log3 $hash, 4, "[$name] aktParam:$aktParam newParam:$newParam - is $disabled_txt disabled";
+
+ #Kommando ausführen
+ if ($command && !$disabled && $activeTimer
+ && $aktParam ne $newParam
+ ) {
+ $newParam =~ s/\\:/|/g;
+ $newParam =~ s/:/ /g;
+ $newParam =~ s/\|/:/g;
+
+ my %specials = ( "%NAME" => $hash->{DEVICE}, "%EVENT" => $newParam);
+ $command= EvalSpecials($command, %specials);
+
+ Log3 $hash, 4, "[$name] command: '$command' executed with ".join(",", map { "$_=>$specials{$_}" } keys %specials);
+ my $ret = AnalyzeCommandChain(undef, $command);
+ Log3 ($hash, 3, $ret) if($ret);
+ }
+}
+
+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_evalAndcleanupParam($$$$) {
+ my ($hash,$time,$param,$isHeating) = @_;
+
+ my $name = $hash->{DEVICE} ;
+ my $wdName = $hash->{NAME};
+
+ my $newParam = $param;
+ if ($param =~ m/^{.*}$/) {
+
+ Log3 $hash, 4, "[$wdName] calculating dynamic param before all: ....... $newParam";
+
+ $newParam =~ s/\$NAME/$hash->{DEVICE}/g;
+ $newParam =~ s/\$TIME/$time/g;
+ Log3 $hash, 4, "[$wdName] calculating dynamic param after substitutions: $newParam";
+
+ $newParam = eval $newParam;
+ if ($@ || not defined $newParam) {
+ Log3 $hash, 1, "[$wdName] problem calculating dynamic param: ........... $param";
+ Log3 $hash, 1, "[$wdName] $@";
+ } else {
+ Log3 $hash, 4, "[$wdName] calculating dynamic param after eval: ........ $newParam";
+ }
+
+ }elsif($isHeating && $param =~ m/^\d{1,3}$/){
+ $newParam = sprintf("%.1f", $param);
+ }
+ return $newParam;
+}
+
+sub WeekdayTimer_FensterOffen($$$) {
my ($hash, $event, $time) = @_;
my $name = $hash->{NAME};
@@ -852,11 +570,11 @@ sub WeekdayTimer_FensterOffen ($$$) {
}
if (defined($hash->{VERZOEGRUNG_IDX}) && $hash->{VERZOEGRUNG_IDX}!=$time) {
Log3 $hash, 3, "[$name] timer at $hash->{profil}{$hash->{VERZOEGRUNG_IDX}}{TIME} skiped by new timer at $hash->{profil}{$time}{TIME}";
- myRemoveInternalTimer($hash->{VERZOEGRUNG_IDX},$hash);
+ WeekdayTimer_RemoveInternalTimer($hash->{VERZOEGRUNG_IDX},$hash);
}
$hash->{VERZOEGRUNG_IDX} = $time;
- myRemoveInternalTimer("$time", $hash);
- myInternalTimer ("$time", $nextRetry, "$hash->{TYPE}_Update", $hash, 0);
+ WeekdayTimer_RemoveInternalTimer("$time", $hash);
+ WeekdayTimer_InternalTimer ("$time", $nextRetry, "$hash->{TYPE}_Update", $hash, 0);
$hash->{VERZOEGRUNG} = 1;
return 1;
}
@@ -909,11 +627,11 @@ sub WeekdayTimer_FensterOffen ($$$) {
}
if (defined($hash->{VERZOEGRUNG_IDX}) && $hash->{VERZOEGRUNG_IDX}!=$time) {
Log3 $hash, 3, "[$name] timer at $hash->{profil}{$hash->{VERZOEGRUNG_IDX}}{TIME} skiped by new timer at $hash->{profil}{$time}{TIME}";
- myRemoveInternalTimer($hash->{VERZOEGRUNG_IDX},$hash);
+ WeekdayTimer_RemoveInternalTimer($hash->{VERZOEGRUNG_IDX},$hash);
}
$hash->{VERZOEGRUNG_IDX} = $time;
- myRemoveInternalTimer("$time", $hash);
- myInternalTimer ("$time", $nextRetry, "$hash->{TYPE}_Update", $hash, 0);
+ WeekdayTimer_RemoveInternalTimer("$time", $hash);
+ WeekdayTimer_InternalTimer ("$time", $nextRetry, "$hash->{TYPE}_Update", $hash, 0);
$hash->{VERZOEGRUNG} = 1;
return 1
}
@@ -929,108 +647,482 @@ sub WeekdayTimer_FensterOffen ($$$) {
delete $hash->{VERZOEGRUNG_IDX} if defined($hash->{VERZOEGRUNG_IDX});
return 0;
}
-################################################################################
-sub WeekdayTimer_evalAndcleanupParam($$$$) {
- my ($hash,$time,$param,$isHeating) = @_;
- my $name = $hash->{DEVICE} ;
- my $wdName = $hash->{NAME};
-
- my $newParam = $param;
- if ($param =~ m/^{.*}$/) {
-
- Log3 $hash, 4, "[$wdName] calculating dynamic param before all: ....... $newParam";
-
- $newParam =~ s/\$NAME/$hash->{DEVICE}/g;
- $newParam =~ s/\$TIME/$time/g;
- Log3 $hash, 4, "[$wdName] calculating dynamic param after substitutions: $newParam";
-
- $newParam = eval $newParam;
- if ($@ || not defined $newParam) {
- Log3 $hash, 1, "[$wdName] problem calculating dynamic param: ........... $param";
- Log3 $hash, 1, "[$wdName] $@";
- } else {
- Log3 $hash, 4, "[$wdName] calculating dynamic param after eval: ........ $newParam";
- }
-
- }elsif($isHeating && $param =~ m/^\d{1,3}$/){
- $newParam = sprintf("%.1f", $param);
- }
- return $newParam;
-}
-################################################################################
-sub WeekdayTimer_Device_Schalten($$$) {
- my ($hash, $newParam, $tage) = @_;
-
- my ($command, $condition, $tageAsHash) = "";
- my $name = $hash->{NAME}; ###
- my $dummy = "";
-
- my $now = time();
- #modifier des Zieldevices auswaehlen
- my $setModifier = WeekdayTimer_isHeizung($hash);
-
- $attr{$name}{commandTemplate} =
- 'set $NAME '. $setModifier .' $EVENT' if (!defined $attr{$name}{commandTemplate});
-
- $command = AttrVal($hash->{NAME}, "commandTemplate", "commandTemplate not found");
- $command = $hash->{COMMAND} if ($hash->{COMMAND} gt "");
-
- my $activeTimer = 1;
-
- 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 $aktParam = WeekdayTimer_evalAndcleanupParam($hash,$dummy,ReadingsVal($hash->{DEVICE}, $setModifier, ""),$isHeating);
- # newParam is already processed by evalAndcleanupParam()
-
- my $disabled = AttrVal($hash->{NAME}, "disable", 0);
- my $disabled_txt = $disabled ? " " : " not";
- Log3 $hash, 4, "[$name] aktParam:$aktParam newParam:$newParam - is $disabled_txt disabled";
-
- #Kommando ausführen
- if ($command && !$disabled && $activeTimer
- && $aktParam ne $newParam
- ) {
- $newParam =~ s/\\:/|/g;
- $newParam =~ s/:/ /g;
- $newParam =~ s/\|/:/g;
-
- my %specials = ( "%NAME" => $hash->{DEVICE}, "%EVENT" => $newParam);
- $command= EvalSpecials($command, %specials);
-
- Log3 $hash, 4, "[$name] command: '$command' executed with ".join(",", map { "$_=>$specials{$_}" } keys %specials);
- my $ret = AnalyzeCommandChain(undef, $command);
- Log3 ($hash, 3, $ret) if($ret);
- }
-}
-################################################################################
-sub WeekdayTimer_tageAsHash($$) {
- my ($hash, $tage) = @_;
-
- my %days = map {$_ => 1} @$tage;
- map {delete $days{$_}} (7,8);
-
- return 'my $days={};map{$days->{$_}=1}'.'('.join (",", sort keys %days).')';
-}
-################################################################################
-sub WeekdayTimer_Condition($$) {
- my ($hash, $tage) = @_;
+sub WeekdayTimer_gatherSwitchingTimes {
+ my $hash = shift;
+ my $a = shift;
my $name = $hash->{NAME};
- Log3 $hash, 4, "[$name] condition:$hash->{CONDITION} - Tage:".join(",",@$tage);
+ my @switchingtimes = ();
+ my $conditionOrCommand;
- my $condition = "( ";
- $condition .= ($hash->{CONDITION} gt "") ? $hash->{CONDITION} : 1 ;
- $condition .= " && " . WeekdayTimer_TageAsCondition($tage);
- $condition .= ")";
+ # switchingtime einsammeln
+ while (@$a > 0) {
- return $condition;
+ #pruefen auf Angabe eines Schaltpunktes
+ my $element = "";
+ my @restoreElements = ();
+ while (@$a > 0) {
+
+ my $actualElement = shift @$a;
+ push @restoreElements, $actualElement;
+ $element = $element . $actualElement . " ";
+ Log3 $hash, 5, "[$name] $element - trying to accept as a switchtime";
+
+ # prüfen ob Anführungszeichen paarig sind
+ my @quotes = ('"', "'" );
+ foreach my $quote (@quotes){
+ my $balancedSign = eval "((\$element =~ tr/$quote//))";
+ if ($balancedSign % 2) { # ungerade Anzahl quotes, dann verlängern
+ Log3 $hash, 5, "[$name] $element - unbalanced quotes: $balancedSign $quote found";
+ next E;
+ }
+ }
+
+ # prüfen ob öffnende/schliessende Klammern paarig sind
+ my %signs = ('('=>')', '{'=>'}');
+ foreach my $signOpened (keys(%signs)) {
+ my $signClosed = $signs{$signOpened};
+ my $balancedSign = eval "((\$element =~ tr/$signOpened//) - (\$element =~ tr/$signClosed//))";
+ if ($balancedSign) { # öffnende/schließende Klammern nicht gleich, dann verlängern
+ Log3 $hash, 5, "[$name] $element - unbalanced brackets $signOpened$signClosed:$balancedSign";
+ next E;
+ }
+ }
+ last;
+ }
+
+ # ein space am Ende wieder abschneiden
+ $element = substr ($element, 0, length($element)-1);
+ my @t = split(/\|/, $element);
+ my $anzahl = @t;
+
+ if ( ($anzahl == 2 || $anzahl == 3) && $t[0] gt "" && $t[1] gt "" ) {
+ Log3 $hash, 4, "[$name] $element - accepted";
+ push(@switchingtimes, $element);
+ } else {
+ Log3 $hash, 4, "[$name] $element - NOT accepted, must be command or condition";
+ unshift @$a, @restoreElements;
+ last;
+ }
+ }
+ return (@switchingtimes);
+}
+
+sub WeekdayTimer_getListeDerTage($$) {
+ my ($d, $time) = @_;
+
+ my %hdays=();
+ @hdays{(0, 6)} = undef if ($d==7); # sa,so ( $we)
+ @hdays{(1..5)} = undef if ($d==8); # mo-fr (!$we)
+
+ my $wday;
+ my $now = time();
+ my ($sec,$min,$hour,$mday,$mon,$year,$nowWday,$yday,$isdst) = localtime($now);
+
+ my @realativeWdays = (0..6);
+ for (my $i=0;$i<=6;$i++) {
+
+ my $relativeDay = $i-$nowWday;
+ #Log 3, "relativeDay------------>$relativeDay";
+ my ($stunde, $minute, $sekunde) = split (":",$time);
+
+ my $echteZeit = WeekdayTimer_zeitErmitteln ($now, $stunde, $minute, $sekunde, $relativeDay);
+ #Log 3, "echteZeit---$i---->>>$relativeDay<<<----->".FmtDateTime($echteZeit);
+ ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($echteZeit);
+ my $h2we = $attr{global}{holiday2we};
+ if($h2we) {
+ my $ergebnis = fhem("get $h2we ".sprintf("%02d-%02d",$mon+1,$mday),1);
+ if ($ergebnis ne "none") {
+ #Log 3, "ergebnis-------$i----->$ergebnis";
+ $hdays{$i} = undef if ($d==7); # $we Tag aufnehmen
+ delete $hdays{$i} if ($d==8); # !$we Tag herausnehmen
+ }
+ }
+ }
+
+ #Log 3, "result------------>" . join (" ", sort keys %hdays);
+ return keys %hdays;
+}
+
+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_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_isAnActiveTimer($$$) {
+ my ($hash, $tage, $newParam) = @_;
+
+ my $name = $hash->{NAME};
+ my %specials = ( "%NAME" => $hash->{DEVICE}, "%EVENT" => $newParam);
+
+ my $condition = WeekdayTimer_Condition ($hash, $tage);
+ my $tageAsHash = WeekdayTimer_tageAsHash($hash, $tage);
+ my $xPression = "{".$tageAsHash.";;".$condition ."}";
+ $xPression = EvalSpecials($xPression, %specials);
+ Log3 $hash, 5, "[$name] condition: $xPression";
+
+ my $ret = AnalyzeCommandChain(undef, $xPression);
+ #Log3 $hash, 3, "[$name] condition:>>>$ret<<< $xPression";
+ Log3 $hash, 5, "[$name] result of condition:$ret";
+ return $ret;
+}
+
+sub WeekdayTimer_isHeizung($) {
+ my ($hash) = @_;
+
+ my $name = $hash->{NAME};
+
+ my $dHash = $defs{$hash->{DEVICE}};
+ return "" if (!defined $dHash); # vorzeitiges Ende wenn das device nicht existiert
+
+ my $dType = $dHash->{TYPE};
+ return "" if (!defined($dType) || $dType eq "dummy" );
+
+ my $dName = $dHash->{NAME};
+
+ my @tempSet = ("desired-temp", "desiredTemperature", "desired", "thermostatSetpointSet");
+ my $allSets = getAllSets($dName);
+
+ foreach my $ts (@tempSet) {
+ if ($allSets =~ m/$ts/) {
+ Log3 $hash, 4, "[$name] device type heating recognized, setModifier:$ts";
+ return $ts
+ }
+ }
}
-################################################################################
-sub WeekdayTimer_TageAsCondition ($) {
+
+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_Profile($) {
+ my $hash = shift;
+
+ 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();
+ # ---- Zeitpunkte den Tagen zuordnen -----------------------------------
+ my $idx = 0;
+ foreach my $st (@{$hash->{SWITCHINGTIMES}}) {
+ my ($tage,$time,$parameter) = WeekdayTimer_SwitchingTime ($hash, $st);
+
+ $idx++;
+ foreach my $d (@{$tage}) {
+
+ my @listeDerTage = ($d);
+ push (@listeDerTage, WeekdayTimer_getListeDerTage($d, $time)) if ($d>=7);
+
+ map { my $day = $_;
+ my $dayOfEchteZeit = $day;
+ $dayOfEchteZeit = ($wday>=1&&$wday<=5) ? 6 : $wday if ($day==7); # ggf. Samstag $wday ~~ [1..5]
+ $dayOfEchteZeit = ($wday==0||$wday==6) ? 1 : $wday if ($day==8); # ggf. Montag $wday ~~ [0, 6]
+ my $echtZeit = WeekdayTimer_EchteZeit($hash, $dayOfEchteZeit, $time);
+ $hash->{profile} {$day}{$echtZeit} = $parameter;
+ $hash->{profile_IDX}{$day}{$echtZeit} = $idx;
+ } @listeDerTage;
+ }
+ }
+ # ---- Zeitpunkte des aktuellen Tages mit EPOCH ermitteln --------------
+ $idx = 0;
+ 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);
+
+ $idx++;
+ $hash->{profil}{$idx}{TIME} = $time;
+ $hash->{profil}{$idx}{PARA} = $parameter;
+ $hash->{profil}{$idx}{EPOCH} = WeekdayTimer_zeitErmitteln ($now, $stunde, $minute, $sekunde, 0);
+ $hash->{profil}{$idx}{TAGE} = $tage;
+ }
+ # ---- Texte Readings aufbauen -----------------------------------------
+ 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)";
+ }
+
+ # für logProxy umhaengen
+ $hash->{helper}{SWITCHINGTIME} = $hash->{profile};
+ delete $hash->{profile};
+}
+
+sub WeekdayTimer_searchAktNext($$) {
+ my ($hash, $now) = @_;
+ my $name = $hash->{NAME};
+
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($now);
+ #Log3 $hash, 3, "[$name] such--->".FmtDateTime($now);
+
+ my ($oldTag, $oldTime, $oldPara , $oldIdx);
+ my ($nextTag, $nextTime, $nextPara, $nextIdx);
+
+ my $language = $hash->{LANGUAGE};
+ my %shortDays = %{$hash->{shortDays}};
+
+ my @realativeWdays = ($wday..6,0..$wday-1,$wday..6,0..6);
+ for (my $i=0;$i<=$#realativeWdays;$i++) {
+
+ my $relativeDay = $i-7;
+ my $relWday = $realativeWdays[$i];
+
+ foreach my $time (sort keys %{$hash->{helper}{SWITCHINGTIME}{$relWday}}) {
+ my ($stunde, $minute, $sekunde) = split (":",$time);
+
+ $oldTime = $nextTime;
+ $oldPara = $nextPara;
+ $oldIdx = $nextIdx;
+ $oldTag = $nextTag;
+
+ $nextTime = WeekdayTimer_zeitErmitteln ($now, $stunde, $minute, $sekunde, $relativeDay);
+ $nextPara = $hash->{helper}{SWITCHINGTIME}{$relWday}{$time};
+ $nextIdx = $hash->{profile_IDX}{$relWday}{$time};
+ $nextTag = $relWday;
+
+ #Log3 $hash, 3, $shortDays{$language}[$nextTag]." ".FmtDateTime($nextTime)." ".$nextPara." ".$nextIdx;
+
+ if ($nextTime >= $now) {
+ #Log3 $hash, 3, "oldIdx------------->$oldIdx";
+ #Log3 $hash, 3, "oldTime------------>".FmtDateTime($oldTime);
+ #Log3 $hash, 3, "oldPara------------>$oldPara";
+ return ($oldIdx, $oldTime, $oldPara, $nextTime, $nextPara);
+ }
+ }
+ }
+ return (undef,undef,undef,undef);
+}
+
+sub WeekdayTimer_SetAllParms() {
+
+ my @wdNamen = sort keys %{$modules{WeekdayTimer}{defptr}};
+ foreach my $wdName ( @wdNamen ) {
+ WeekdayTimer_SetParm($wdName);
+ }
+ Log3 undef, 3, "WeekdayTimer_SetAllParms() done on: ".join(" ",@wdNamen );
+}
+
+sub WeekdayTimer_SetParm($) {
+ my ($name) = @_;
+
+ my $hash = $modules{WeekdayTimer}{defptr}{$name};
+ if(defined $hash) {
+ WeekdayTimer_DeleteTimer($hash);
+ WeekdayTimer_SetTimer($hash);
+ }
+}
+
+sub WeekdayTimer_SetTimer($) {
+ my $hash = shift;
+ my $name = $hash->{NAME};
+
+ my $now = time();
+
+ my $isHeating = WeekdayTimer_isHeizung($hash);
+ my $swip = AttrVal($name, "switchInThePast", 0);
+ my $switchInThePast = ($swip || $isHeating);
+
+ Log3 $hash, 4, "[$name] Heating recognized - switch in the past activated" if ($isHeating);
+ Log3 $hash, 4, "[$name] no switch in the yesterdays because of the devices type($hash->{DEVICE} is not recognized as heating) - use attr switchInThePast" if (!$switchInThePast && !defined $hash->{SETTIMERATMIDNIGHT});
+
+ my @switches = sort keys %{$hash->{profil}};
+ if ($#switches < 0) {
+ Log3 $hash, 3, "[$name] no switches to send, due to possible errors.";
+ return;
+ }
+
+ readingsSingleUpdate ($hash, "state", "inactive", 1) if (!defined $hash->{SETTIMERATMIDNIGHT});
+ for(my $i=0; $i<=$#switches; $i++) {
+
+ my $idx = $switches[$i];
+
+ my $time = $hash->{profil}{$idx}{TIME};
+ my $timToSwitch = $hash->{profil}{$idx}{EPOCH};
+ my $tage = $hash->{profil}{$idx}{TAGE};
+ my $para = $hash->{profil}{$idx}{PARA};
+
+ my $secondsToSwitch = $timToSwitch - $now;
+
+ my $isActiveTimer = WeekdayTimer_isAnActiveTimer ($hash, $tage, $para);
+ readingsSingleUpdate ($hash, "state", "active", 1)
+ if (!defined $hash->{SETTIMERATMIDNIGHT} && $isActiveTimer);
+
+ if ($secondsToSwitch>-5 || defined $hash->{SETTIMERATMIDNIGHT} ) {
+ if($isActiveTimer) {
+ Log3 $hash, 4, "[$name] setTimer - timer seems to be active today: ".join("",@$tage)."|$time|$para";
+ } else {
+ Log3 $hash, 4, "[$name] setTimer - timer seems to be NOT active today: ".join("",@$tage)."|$time|$para ". $hash->{CONDITION};
+ }
+ WeekdayTimer_RemoveInternalTimer("$idx", $hash);
+ WeekdayTimer_InternalTimer ("$idx", $timToSwitch, "$hash->{TYPE}_Update", $hash, 0);
+ WeekdayTimer_InternalTimer ("$idx", $timToSwitch, "$hash->{TYPE}_Update", $hash, 0);
+ }
+ }
+
+ if (defined $hash->{SETTIMERATMIDNIGHT}) {
+ return;
+ }
+
+ my ($aktIdx,$aktTime,$aktParameter,$nextTime,$nextParameter) =
+ WeekdayTimer_searchAktNext($hash, time()+5);
+ if(!defined $aktTime) {
+ Log3 $hash, 3, "[$name] can not compute past switching time";
+ }
+
+ readingsSingleUpdate ($hash, "nextUpdate", FmtDateTime($nextTime), 1);
+ readingsSingleUpdate ($hash, "nextValue", $nextParameter, 1);
+ readingsSingleUpdate ($hash, "currValue", $aktParameter, 1); # HB
+
+ if ($switchInThePast && defined $aktTime) {
+ # Fensterkontakte abfragen - wenn einer im Status closed, dann Schaltung um 60 Sekunden verzögern
+ if (WeekdayTimer_FensterOffen($hash, $aktParameter, $aktIdx)) {
+ return;
+ }
+
+ # alle in der Vergangenheit liegenden Schaltungen sammeln und
+ # nach 5 Sekunden in der Reihenfolge der Schaltzeiten
+ # durch WeekdayTimer_delayedTimerInPast() als Timer einstellen
+ # die Parameter merken wir uns kurzzeitig im hash
+ # modules{WeekdayTimer}{timerInThePast}
+ my $device = $hash->{DEVICE};
+ Log3 $hash, 4, "[$name] past timer on $hash->{DEVICE} at ". FmtDateTime($aktTime). " with $aktParameter activated";
+
+ my $parameter = $modules{WeekdayTimer}{timerInThePast}{$device}{$aktTime};
+ $parameter = [] if (!defined $parameter);
+ push (@$parameter,["$aktIdx", $aktTime, "$hash->{TYPE}_Update", $hash, 0]);
+ $modules{WeekdayTimer}{timerInThePast}{$device}{$aktTime} = $parameter;
+
+ my $tipHash = $modules{WeekdayTimer}{timerInThePastHash};
+ $tipHash = $hash if (!defined $tipHash);
+ $modules{WeekdayTimer}{timerInThePastHash} = $tipHash;
+
+ WeekdayTimer_RemoveInternalTimer("delayed", $tipHash);
+ WeekdayTimer_InternalTimer ("delayed", time()+5, "WeekdayTimer_delayedTimerInPast", $tipHash, 0);
+
+ }
+}
+
+sub WeekdayTimer_SetTimerForMidnightUpdate($) {
+ my ($myHash) = @_;
+ my $hash = WeekdayTimer_GetHashIndirekt($myHash, (caller(0))[3]);
+ return if (!defined($hash));
+
+ my $now = time();
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($now);
+
+ my $midnightPlus5Seconds = WeekdayTimer_zeitErmitteln ($now, 0, 0, 5, 1);
+ #Log3 $hash, 3, "midnightPlus5Seconds------------>".FmtDateTime($midnightPlus5Seconds);
+
+ WeekdayTimer_RemoveInternalTimer("SetTimerOfDay", $hash);
+ my $newMyHash = WeekdayTimer_InternalTimer ("SetTimerOfDay", $midnightPlus5Seconds, "$hash->{TYPE}_SetTimerOfDay", $hash, 0);
+ $newMyHash->{SETTIMERATMIDNIGHT} = 1;
+
+}
+
+sub WeekdayTimer_SetTimerOfDay($) {
+ my ($myHash) = @_;
+ my $hash = WeekdayTimer_GetHashIndirekt($myHash, (caller(0))[3]);
+ return if (!defined($hash));
+
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time());
+ my $secSinceMidnight = 3600*$hour + 60*$min + $sec;
+
+ $hash->{SETTIMERATMIDNIGHT} = $myHash->{SETTIMERATMIDNIGHT};
+ WeekdayTimer_DeleteTimer($hash);
+ WeekdayTimer_Profile ($hash);
+ WeekdayTimer_SetTimer ($hash);
+ delete $hash->{SETTIMERATMIDNIGHT};
+
+ WeekdayTimer_SetTimerForMidnightUpdate( { HASH => $hash} );
+}
+
+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_TageAsCondition($) {
my $tage = shift;
my %days = map {$_ => 1} @$tage;
@@ -1046,45 +1138,30 @@ sub WeekdayTimer_TageAsCondition ($) {
return $tageExp;
}
-################################################################################
-sub WeekdayTimer_Attr($$$$) {
- my ($cmd, $name, $attrName, $attrVal) = @_;
- $attrVal = 0 if(!defined $attrVal);
+sub WeekdayTimer_tageAsHash($$) {
+ my ($hash, $tage) = @_;
- my $hash = $defs{$name};
- if( $attrName eq "disable" ) {
- readingsSingleUpdate ($hash, "disabled", $attrVal, 1);
- } elsif ( $attrName eq "enable" ) {
- WeekdayTimer_SetTimerOfDay({ HASH => $hash});
- } elsif ( $attrName eq "switchInThePast" ) {
- $attr{$name}{$attrName} = $attrVal;
- WeekdayTimer_SetTimerOfDay({ HASH => $hash});
- }
- return undef;
+ my %days = map {$_ => 1} @$tage;
+ map {delete $days{$_}} (7,8);
+
+ return 'my $days={};map{$days->{$_}=1}'.'('.join (",", sort keys %days).')';
}
-########################################################################
-sub WeekdayTimer_SetParm($) {
- my ($name) = @_;
- my $hash = $modules{WeekdayTimer}{defptr}{$name};
- if(defined $hash) {
- WeekdayTimer_DeleteTimer($hash);
- WeekdayTimer_SetTimer($hash);
- }
-}
-################################################################################
-sub WeekdayTimer_SetAllParms() { # {WeekdayTimer_SetAllParms()}
+sub WeekdayTimer_zeitErmitteln($$$$$) {
+ my ($now, $hour, $min, $sec, $days) = @_;
- my @wdNamen = sort keys %{$modules{WeekdayTimer}{defptr}};
- foreach my $wdName ( @wdNamen ) {
- WeekdayTimer_SetParm($wdName);
- }
- Log3 undef, 3, "WeekdayTimer_SetAllParms() done on: ".join(" ",@wdNamen );
+ 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;
}
1;
+# commandref ##################################################################
=pod
=item device
=item summary sends parameter to devices at defined times