# $Id$ ######################################################################################## # # 98_alarmclock.pm # Fhem Modul to set up a Alarmclock # # 2017 Florian Zetlmeisl # # Parts of the holiday and vacation identification # are written by Prof. Dr. Peter A. Henning # # This programm 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. # # The GNU General Public License can be found at # http://www.gnu.org/copyleft/gpl.html. # A copy is found in the textfile GPL.txt and important notices to the license # from the author is found in LICENSE.txt distributed with these scripts. # # This script 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. # ######################################################################################## package main; use strict; use warnings; ######################################################################################## # # Globale Variablen # ######################################################################################## my %alarmclock_sets = ( "AlarmTime1_Monday" => "09:00", "AlarmTime2_Tuesday" => "09:00", "AlarmTime3_Wednesday" => "09:00", "AlarmTime4_Thursday" => "09:00", "AlarmTime5_Friday" => "09:00", "AlarmTime6_Saturday" => "10:00", "AlarmTime7_Sunday" => "10:00", "AlarmTime8_Holiday" => "10:00", "AlarmTime9_Vacation" => "10:00", "AlarmOff" => "NONE", "AlarmTime_Weekdays" => "09:00", "AlarmTime_Weekend" => "09:00", "stop" => "NONE", "skip" => "NONE", "save" => "NONE", "load" => "NONE", "disable" => "0" ); my %alarmday = ( "1" => "AlarmTime1_Monday", "2" => "AlarmTime2_Tuesday", "3" => "AlarmTime3_Wednesday", "4" => "AlarmTime4_Thursday", "5" => "AlarmTime5_Friday", "6" => "AlarmTime6_Saturday", "0" => "AlarmTime7_Sunday", "8" => "AlarmTime8_Holiday", "9" => "AlarmTime9_Vacation" ); my @mapping_attrs = qw( AlarmRoutine:textField-long AlarmRoutineOff:textField-long PreAlarmRoutine:textField-long OffRoutine:textField-long HardAlarmRoutine:textField-long SnoozeRoutine:textField-long RepRoutine1:textField-long RepRoutine2:textField-long RepRoutine3:textField-long ); ######################################################################################## # # Alarmclock Initialize # ######################################################################################## sub alarmclock_Initialize($) { my ($hash) = @_; $hash->{DefFn} = "alarmclock_Define"; $hash->{UndefFn} = "alarmclock_Undefine"; $hash->{SetFn} = "alarmclock_Set"; $hash->{AttrFn} = "alarmclock_Attr"; $hash->{NotifyFn} = "alarmclock_Notify"; $hash->{AttrList} = " " . join( " ", @mapping_attrs ) . " PreAlarmRoutine" . " AlarmRoutine" . " AlarmRoutineOff" . " PreAlarmTimeInSec" . " EventForSnooze" . " EventForAlarmOff" . " SnoozeTimeInSec" . " OffDefaultTime" . " OffRoutine" . " HardAlarmTimeInSec" . " HardAlarmRoutine" . " MaxAlarmDurationInSec" . " SnoozeRoutine" . " HolidayDevice" . " HolidayCheck:1,0" . " HolidayDays" . " VacationDevice" . " VacationCheck:1,0" . " VacationDays" . " PresenceDevice" . " PresenceCheck:1,0" . " RepRoutine1" . " RepRoutine1WaitInSec" . " RepRoutine1Repeats" . " RepRoutine1Mode:Alarm,PreAlarm,off" . " RepRoutine1Stop:Snooze,off" . " RepRoutine2" . " RepRoutine2WaitInSec" . " RepRoutine2Repeats" . " RepRoutine2Mode:Alarm,PreAlarm,off" . " RepRoutine2Stop:Snooze,off" . " RepRoutine3" . " RepRoutine3WaitInSec" . " RepRoutine3Repeats" . " RepRoutine3Mode:Alarm,PreAlarm,off" . " RepRoutine3Stop:Snooze,off" . " WeekprofileName" . " disable:1,0" . " $readingFnAttributes"; } ######################################################################################## # # Alarmclock Define # ######################################################################################## sub alarmclock_Define($$) { my ($hash, $def) = @_; my @a = split("[ \t][ \t]*", $def); my ($name, $type) = @a; return "Wrong syntax: use define alarmclock" if(int(@a) != 2); $hash->{helper}{Repeat1} = 0; $hash->{helper}{Repeat2} = 0; $hash->{helper}{Repeat3} = 0; $hash->{helper}{Today} = 0; $hash->{helper}{Tomorrow} = 0; return undef; } ######################################################################################## # # Alarmclock Undefine # ######################################################################################## sub alarmclock_Undefine($$) { my ($hash,$arg) = @_; RemoveInternalTimer($hash); return undef; } ######################################################################################## # # Alarmclock Set # ######################################################################################## sub alarmclock_Set($$) { my ($hash, @param) = @_; my $name = shift @param; my $opt = shift @param; my $value = join("", @param); my $Weekprofile = AttrVal($hash->{NAME},"WeekprofileName","Weekprofile_1,Weekprofile_2,Weekprofile_3,Weekprofile_4,Weekprofile_5"); if(!defined($alarmclock_sets{$opt})) { my $list = " AlarmTime1_Monday" ." AlarmTime2_Tuesday" ." AlarmTime3_Wednesday" ." AlarmTime4_Thursday" ." AlarmTime5_Friday" ." AlarmTime6_Saturday" ." AlarmTime7_Sunday" ." AlarmTime8_Holiday" ." AlarmTime9_Vacation" ." AlarmOff:1_Monday,2_Tuesday,3_Wednesday,4_Thursday,5_Friday,6_Saturday,7_Sunday,8_Holiday,9_Vacation,Weekdays,Weekend,All" ." AlarmTime_Weekdays" ." AlarmTime_Weekend" ." stop:Alarm" ." skip:NextAlarm,None" ." save:$Weekprofile" ." load:$Weekprofile" ." disable:1,0"; return "Unknown argument $opt, choose one of $list"; } ### AlarmTime ### if ($opt =~ /^AlarmTime(1_Monday|2_Tuesday|3_Wednesday|4_Thursday|5_Friday|6_Saturday|7_Sunday|8_Holiday|9_Vacation)/) { if ($value =~ /^([0-9]|0[0-9]|1?[0-9]|2[0-3]):[0-5]?[0-9]$/) { readingsSingleUpdate( $hash, $opt, $value, 1 ); alarmclock_createtimer($hash); } elsif (!($value =~ /^([0-9]|0[0-9]|1?[0-9]|2[0-3]):[0-5]?[0-9]$/)) { return "Please Set $opt HH:MM" ; } } ### AlarmOff ### if ($opt eq "AlarmOff") { if ($value =~ /^(1_Monday|2_Tuesday|3_Wednesday|4_Thursday|5_Friday|6_Saturday|7_Sunday|8_Holiday|9_Vacation)$/) { readingsSingleUpdate( $hash, "AlarmTime$value", "off", 1 ); alarmclock_createtimer($hash); } elsif ($value eq "Weekdays") { readingsBeginUpdate($hash); readingsBulkUpdate( $hash, "AlarmTime1_Monday", "off"); readingsBulkUpdate( $hash, "AlarmTime2_Tuesday", "off"); readingsBulkUpdate( $hash, "AlarmTime3_Wednesday", "off"); readingsBulkUpdate( $hash, "AlarmTime4_Thursday", "off"); readingsBulkUpdate( $hash, "AlarmTime5_Friday", "off"); readingsEndUpdate($hash,1); alarmclock_createtimer($hash); } elsif ($value eq "Weekend") { readingsBeginUpdate($hash); readingsBulkUpdate( $hash, "AlarmTime6_Saturday", "off"); readingsBulkUpdate( $hash, "AlarmTime7_Sunday", "off"); readingsEndUpdate($hash,1); alarmclock_createtimer($hash); } elsif ($value eq "All") { readingsBeginUpdate($hash); readingsBulkUpdate( $hash, "AlarmTime1_Monday", "off"); readingsBulkUpdate( $hash, "AlarmTime2_Tuesday", "off"); readingsBulkUpdate( $hash, "AlarmTime3_Wednesday", "off"); readingsBulkUpdate( $hash, "AlarmTime4_Thursday", "off"); readingsBulkUpdate( $hash, "AlarmTime5_Friday", "off"); readingsBulkUpdate( $hash, "AlarmTime6_Saturday", "off"); readingsBulkUpdate( $hash, "AlarmTime7_Sunday", "off"); readingsBulkUpdate( $hash, "AlarmTime8_Holiday", "off"); readingsBulkUpdate( $hash, "AlarmTime9_Vacation", "off"); readingsEndUpdate($hash,1); alarmclock_createtimer($hash); } elsif (!($value =~ /^(1_Monday|2_Tuesday|3_Wednesday|4_Thursday|5_Friday|6_Saturday|7_Sunday|8_Holiday|9_Vacation|Weekdays|Weekend|All)$/)) { return "Please Set $opt (1_Monday|2_Tuesday|3_Wednesday|4_Thursday|5_Friday|6_Saturday|7_Sunday|8_Holiday|9_Vacation|Weekdays|Weekend|All)"; } } ### AlarmTime_Weekdays ### if ($opt eq "AlarmTime_Weekdays") { if ($value =~ /^([0-9]|0[0-9]|1?[0-9]|2[0-3]):[0-5]?[0-9]$/) { readingsBeginUpdate($hash); readingsBulkUpdate( $hash, "AlarmTime1_Monday", $value); readingsBulkUpdate( $hash, "AlarmTime2_Tuesday", $value); readingsBulkUpdate( $hash, "AlarmTime3_Wednesday", $value); readingsBulkUpdate( $hash, "AlarmTime4_Thursday", $value); readingsBulkUpdate( $hash, "AlarmTime5_Friday", $value); readingsEndUpdate($hash,1); alarmclock_createtimer($hash); } elsif (!($value =~ /^([0-9]|0[0-9]|1?[0-9]|2[0-3]):[0-5]?[0-9]$/)) { return "Please Set $opt HH:MM" ; } } ### AlarmTime_Weekend ### if ($opt eq "AlarmTime_Weekend") { if ($value =~ /^([0-9]|0[0-9]|1?[0-9]|2[0-3]):[0-5]?[0-9]$/) { readingsBeginUpdate($hash); readingsBulkUpdate( $hash, "AlarmTime6_Saturday", $value); readingsBulkUpdate( $hash, "AlarmTime7_Sunday", $value); readingsEndUpdate($hash,1); alarmclock_createtimer($hash); } elsif (!($value =~ /^([0-9]|0[0-9]|1?[0-9]|2[0-3]):[0-5]?[0-9]$/)) { return "Please Set $opt HH:MM" ; } } ### stop Alarm ### if ($opt eq "stop") { if (($value eq "Alarm") && ((ReadingsVal($hash->{NAME},"state",0)) =~ /^(Alarm is running|PreAlarm is running|Snooze for.*)/)) { alarmclock_alarmroutine_stop($hash); } } ### save Weekprofile ### if ($opt eq "save") { my $time1 = ReadingsVal($hash->{NAME},"AlarmTime1_Monday","off"); my $time2 = ReadingsVal($hash->{NAME},"AlarmTime2_Tuesday","off"); my $time3 = ReadingsVal($hash->{NAME},"AlarmTime3_Wednesday","off"); my $time4 = ReadingsVal($hash->{NAME},"AlarmTime4_Thursday","off"); my $time5 = ReadingsVal($hash->{NAME},"AlarmTime5_Friday","off"); my $time6 = ReadingsVal($hash->{NAME},"AlarmTime6_Saturday","off"); my $time7 = ReadingsVal($hash->{NAME},"AlarmTime7_Sunday","off"); readingsSingleUpdate( $hash, $value,"$time1,$time2,$time3,$time4,$time5,$time6,$time7", 1 ); } ### load Weekprofile ### if ($opt eq "load") { my @time = split(/,/, ReadingsVal($hash->{NAME}, $value,"")); readingsBeginUpdate($hash); readingsBulkUpdate( $hash, "AlarmTime1_Monday", $time[0]); readingsBulkUpdate( $hash, "AlarmTime2_Tuesday", $time[1]); readingsBulkUpdate( $hash, "AlarmTime3_Wednesday", $time[2]); readingsBulkUpdate( $hash, "AlarmTime4_Thursday", $time[3]); readingsBulkUpdate( $hash, "AlarmTime5_Friday", $time[4]); readingsBulkUpdate( $hash, "AlarmTime6_Saturday", $time[5]); readingsBulkUpdate( $hash, "AlarmTime7_Sunday", $time[6]); readingsEndUpdate($hash,1); alarmclock_createtimer($hash); } ### skip ### if ($opt eq "skip") { if ($value eq "NextAlarm") { readingsBeginUpdate($hash); readingsBulkUpdate( $hash, "state", "skip next Alarm"); readingsBulkUpdate( $hash, "skip", "next Alarm"); readingsEndUpdate($hash,1); alarmclock_createtimer($hash); } if ($value eq "None") { readingsSingleUpdate( $hash, "skip", "none", 1 ); alarmclock_createtimer($hash); } } ### disable ### if ($opt eq "disable") { if ($value eq "1") { RemoveInternalTimer($hash); readingsSingleUpdate( $hash,"state", "deactivated", 1 ); Log3 $hash->{NAME}, 3, "alarmclock: $hash->{NAME} - deactivated"; } if ($value eq "0") { readingsSingleUpdate( $hash,"state", "activated", 1 ); alarmclock_createtimer($hash); Log3 $hash->{NAME}, 3, "alarmclock: $hash->{NAME} - activated"; } } return undef; } ######################################################################################## # # Alarmclock Attr # ######################################################################################## sub alarmclock_Attr(@) { my ($cmd,$name,$attr_name,$attrVal) = @_; my $hash = $defs{$name}; if(($attr_name eq "OffDefaultTime") && ($attrVal =~ /^([0-9]|0[0-9]|1?[0-9]|2[0-3]):([0-5]?[0-9])$/)) { InternalTimer(gettimeofday()+1, "alarmclock_createtimer", $hash, 0); } if(($attr_name eq "PreAlarmTimeInSec") && ($attrVal =~ /^([0-9]?[0-9]?[0-9]?[0-9])$/)) { InternalTimer(gettimeofday()+1, "alarmclock_createtimer", $hash, 0); } ###disable### if($attr_name eq "disable") { if( $cmd eq "set" ) { if($attrVal eq "1") { RemoveInternalTimer($hash); readingsSingleUpdate( $hash,"state", "deactivated", 1 ); Log3 $hash->{NAME}, 3, "alarmclock: $hash->{NAME} - deactivated"; } elsif($attrVal eq "0") { InternalTimer(gettimeofday()+1, "alarmclock_createtimer", $hash, 0); Log3 $hash->{NAME}, 3, "alarmclock: $hash->{NAME} - activated"; } } elsif( $cmd eq "del" ) { InternalTimer(gettimeofday()+1, "alarmclock_createtimer", $hash, 0); Log3 $hash->{NAME}, 3, "alarmclock: $hash->{NAME} - activated"; } } ###Holiday### if($attr_name eq "HolidayCheck") { RemoveInternalTimer($hash); InternalTimer(gettimeofday()+1, "alarmclock_createtimer", $hash, 0); } ###Vacation### if($attr_name eq "VacationCheck") { RemoveInternalTimer($hash); InternalTimer(gettimeofday()+1, "alarmclock_createtimer", $hash, 0); } return undef; } ######################################################################################## # # Timer für die heutige Alarmzeit wird erstellt # ######################################################################################## sub alarmclock_createtimer($) { my ($hash) = @_; my ($SecNow, $MinNow, $HourNow, $DayNow, $MonthNow, $YearNow, $WDayNow, $YDNow, $SumTimeNow) = localtime(time); $hash->{helper}{Today} = $WDayNow; if ($WDayNow =~ /^(0|1|2|3|4|5)/) { $hash->{helper}{Tomorrow} = $WDayNow + 1; } else { $hash->{helper}{Tomorrow} = 0; } my $HourinSec = $HourNow * 3600; my $MininSec = $MinNow * 60; my $NowinSec = $HourinSec + $MininSec + $SecNow; if ((AttrVal($hash->{NAME}, "disable", 0 ) ne "1" ) && (ReadingsVal($hash->{NAME},"state","activated") ne "deactivated")) { ### Vacation ### alarmclock_vacation_check($hash); ### Holiday ### alarmclock_holiday_check($hash); my $alarmtimetoday = $alarmday{$hash->{helper}{Today}}; my $alarmtimetomorrow = $alarmday{$hash->{helper}{Tomorrow}}; if ((ReadingsVal($hash->{NAME},$alarmtimetoday,"NONE")) =~ /^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/) { ReadingsVal($hash->{NAME},$alarmtimetoday,"NONE") =~ /^([0-9]|0[0-9]|1?[0-9]|2[0-3]):([0-5]?[0-9])$/; my $AlarmHour = 0; my $AlarmMin = 0; $AlarmHour = $1; $AlarmMin = $2; my $AlarmHourinSec = $AlarmHour * 3600; my $AlarmMininSec = $AlarmMin * 60; my $AlarminSec = $AlarmHourinSec + $AlarmMininSec; if(($NowinSec < $AlarminSec) && (ReadingsVal($hash->{NAME},"skip","none") eq "none")) { my $AlarmIn = $AlarminSec - $NowinSec; RemoveInternalTimer($hash); InternalTimer(gettimeofday()+$AlarmIn, "alarmclock_alarmroutine_start", $hash, 0); readingsSingleUpdate( $hash,"state", "next Alarm at $AlarmHour:$AlarmMin", 1 ); Log3 $hash->{NAME}, 5, "alarmclock: $hash->{NAME} - alarm-timer created with $AlarmIn sec"; ### PreAlarm ### if((AttrVal($hash->{NAME},"PreAlarmTimeInSec","NONE")) =~ /^([0-9]?[0-9]?[0-9]?[0-9])$/) { my $PreAlarmTime = AttrVal($hash->{NAME},"PreAlarmTimeInSec","NONE"); if($NowinSec < $AlarminSec - $PreAlarmTime) { my $PreAlarmIn = $AlarmIn - $PreAlarmTime; InternalTimer(gettimeofday()+$PreAlarmIn, "alarmclock_prealarmroutine_start", $hash, 0); Log3 $hash->{NAME}, 5, "alarmclock: $hash->{NAME} - pre-alarm timer created with $PreAlarmIn sec"; } else { Log3 $hash->{NAME}, 3, "alarmclock: $hash->{NAME} - pre-alarm time has been in the past"; } } else { Log3 $hash->{NAME}, 4, "alarmclock: $hash->{NAME} - no PreAlarmTimeInSec is set"; } ### End PreAlarm ### } ### skip next Alarmtime ### elsif(($NowinSec < $AlarminSec) && (ReadingsVal($hash->{NAME},"skip","none") ne "none")) { my $AlarmIn2 = $AlarminSec - $NowinSec; RemoveInternalTimer($hash); InternalTimer(gettimeofday()+$AlarmIn2, "alarmclock_skip", $hash, 0); Log3 $hash->{NAME}, 3, "alarmclock: $hash->{NAME} - skip next Alarm"; } ### End skip next Alarmtime ### else { alarmclock_midnight_timer($hash); Log3 $hash->{NAME}, 5, "alarmclock: $hash->{NAME} - alarm time today has been in the past => midnight-timer started."; } } ### OffDefaultTime ### elsif((ReadingsVal($hash->{NAME},$alarmtimetoday,"NONE")) eq "off") { if((AttrVal($hash->{NAME},"OffDefaultTime","NONE")) =~ /^([0-9]|0[0-9]|1?[0-9]|2[0-3]):([0-5]?[0-9])$/) { my $OffDefaultTimeHour = 0; my $OffDefaultTimeMin = 0; $OffDefaultTimeHour = $1; $OffDefaultTimeMin = $2; my $OffDefaultTimeHourinSec = $OffDefaultTimeHour * 3600; my $OffDefaultTimeMininSec = $OffDefaultTimeMin * 60; my $OffDefaultTimeinSec = $OffDefaultTimeHourinSec + $OffDefaultTimeMininSec; if($NowinSec < $OffDefaultTimeinSec) { my $OffDefaultTimeIn = $OffDefaultTimeinSec - $NowinSec; RemoveInternalTimer($hash); InternalTimer(gettimeofday()+$OffDefaultTimeIn, "alarmclock_offroutine_start", $hash, 0); readingsSingleUpdate( $hash,"state", "next OffRoutine at $OffDefaultTimeHour:$OffDefaultTimeMin", 1 ); Log3 $hash->{NAME}, 5, "alarmclock: $hash->{NAME} - off-routine-timer created with $OffDefaultTimeIn sec"; } else { alarmclock_midnight_timer($hash); Log3 $hash->{NAME}, 3, "alarmclock: $hash->{NAME} - OffDefaultTime has been in the past"; } } else { alarmclock_midnight_timer($hash); Log3 $hash->{NAME}, 4, "alarmclock: $hash->{NAME} - no OffDefaultTime is set"; } } ### End OffDefaultTime ### else { alarmclock_midnight_timer($hash); Log3 $hash->{NAME}, 5, "alarmclock: $hash->{NAME} - no alarm today => midnight-timer started"; } ### Alarm Reading ### my $AlarmToday = ReadingsVal($hash->{NAME},$alarmtimetoday," "); my $AlarmTomorrow = ReadingsVal($hash->{NAME},$alarmtimetomorrow," "); readingsBeginUpdate($hash); readingsBulkUpdate( $hash, "AlarmToday", $AlarmToday); readingsBulkUpdate( $hash, "AlarmTomorrow", $AlarmTomorrow); readingsEndUpdate($hash,1); } } ######################################################################################## # # Midnight-timer + 5 seconds # ######################################################################################## sub alarmclock_midnight_timer($) { my ($hash) = @_; my ($SecNow, $MinNow, $HourNow, $DayNow, $MonthNow, $YearNow, $WDayNow, $YDNow, $SumTimeNow) = localtime(time); my $HourinSec = $HourNow * 3600; my $MininSec = $MinNow * 60; my $NowinSec = $HourinSec + $MininSec + $SecNow; my $SectoMidnight = 86405 - $NowinSec; if ((AttrVal($hash->{NAME}, "disable", 0 ) ne "1" ) && (ReadingsVal($hash->{NAME},"state","activated") ne "deactivated")) { RemoveInternalTimer($hash); InternalTimer(gettimeofday()+$SectoMidnight, "alarmclock_createtimer", $hash, 0); Log3 $hash->{NAME}, 5, "alarmclock: $hash->{NAME} - midnight-timer created with $SectoMidnight sec."; if (ReadingsVal($hash->{NAME},"skip","none") ne "none") { readingsSingleUpdate( $hash, "state", "skip next Alarm", 1); } else { readingsSingleUpdate( $hash,"state", "OK", 1 ); } } } ######################################################################################## # # Alarm-Routine start # ######################################################################################## sub alarmclock_alarmroutine_start($) { my ($hash) = @_; my $Mode = "Alarm"; if (alarmclock_presence_check($hash)) { fhem("".AttrVal($hash->{NAME},"AlarmRoutine","")); readingsSingleUpdate( $hash,"state", "Alarm is running", 1 ); alarmclock_hardalarm_timer($hash); alarmclock_maxalarmduration_timer($hash); alarmclock_reproutine($hash, $Mode); Log3 $hash->{NAME}, 3, "alarmclock: $hash->{NAME} - AlarmRoutine started."; } else { alarmclock_createtimer($hash); } } ######################################################################################## # # Alarm-Routine stop # ######################################################################################## sub alarmclock_alarmroutine_stop($) { my ($hash) = @_; fhem("".AttrVal($hash->{NAME},"AlarmRoutineOff","")); readingsSingleUpdate( $hash,"state", "Alarm stopped", 1 ); Log3 $hash->{NAME}, 3, "alarmclock: $hash->{NAME} - alarmroutine stopped."; RemoveInternalTimer($hash); alarmclock_midnight_timer($hash); } ######################################################################################## # # Pre-Alarm-Routine start # ######################################################################################## sub alarmclock_prealarmroutine_start($) { my ($hash) = @_; my $Mode = "PreAlarm"; if (alarmclock_presence_check($hash)) { fhem("".AttrVal($hash->{NAME},"PreAlarmRoutine","")); readingsSingleUpdate( $hash,"state", "PreAlarm is running", 1 ); alarmclock_reproutine($hash, $Mode); Log3 $hash->{NAME}, 3, "alarmclock: $hash->{NAME} - PreAlarmRoutine started."; } } ######################################################################################## # # Alarm Snooze # ######################################################################################## sub alarmclock_snooze_start($) { my ($hash) = @_; my $Mode = "Snooze"; if((AttrVal($hash->{NAME},"SnoozeTimeInSec","NONE")) =~ /^([0-9]?[0-9]?[0-9]?[0-9])$/) { my $SnoozeTime = AttrVal($hash->{NAME},"SnoozeTimeInSec",""); fhem("".AttrVal($hash->{NAME},"SnoozeRoutine","")); InternalTimer(gettimeofday()+$SnoozeTime, "alarmclock_alarmroutine_start", $hash, 0); readingsSingleUpdate( $hash,"state", "Snooze for $SnoozeTime sec", 1 ); RemoveInternalTimer($hash, "alarmclock_hardalarmroutine_start"); RemoveInternalTimer($hash, "alarmclock_alarmroutine_stop"); alarmclock_reproutine_stop($hash, $Mode); Log3 $hash->{NAME}, 5, "alarmclock: $hash->{NAME} - snooze-timer created with $SnoozeTime sec."; } else { Log3 $hash->{NAME}, 3, "alarmclock: $hash->{NAME} - no SnoozeTimeInSec is set."; } } ######################################################################################## # # Alarm OffRoutine # ######################################################################################## sub alarmclock_offroutine_start($) { my ($hash) = @_; fhem("".AttrVal($hash->{NAME},"OffRoutine","")); readingsSingleUpdate( $hash,"state", "OffRoutine is running", 1 ); alarmclock_createtimer($hash); Log3 $hash->{NAME}, 3, "alarmclock: $hash->{NAME} - OffRoutine started."; } ######################################################################################## # # HardAlarm Timer # ######################################################################################## sub alarmclock_hardalarm_timer($) { my ($hash) = @_; if((AttrVal($hash->{NAME},"HardAlarmTimeInSec","NONE")) =~ /^([0-9]?[0-9]?[0-9]?[0-9])$/) { my $HardAlarmTime = AttrVal($hash->{NAME},"HardAlarmTimeInSec",""); InternalTimer(gettimeofday()+$HardAlarmTime, "alarmclock_hardalarmroutine_start", $hash, 0); Log3 $hash->{NAME}, 5, "alarmclock: $hash->{NAME} - HardAlarm created with $HardAlarmTime sec."; } else { Log3 $hash->{NAME}, 4, "alarmclock: $hash->{NAME} - no HardAlarmTimeInSec is set."; } } ######################################################################################## # # MaxAlarmDuration # ######################################################################################## sub alarmclock_maxalarmduration_timer($) { my ($hash) = @_; if((AttrVal($hash->{NAME},"MaxAlarmDurationInSec","NONE")) =~ /^([0-9]?[0-9]?[0-9]?[0-9])$/) { my $MaxAlarmDuration = AttrVal($hash->{NAME},"MaxAlarmDurationInSec",""); InternalTimer(gettimeofday()+$MaxAlarmDuration, "alarmclock_alarmroutine_stop", $hash, 0); Log3 $hash->{NAME}, 5, "alarmclock: $hash->{NAME} - MaxAlarmDuration created with $MaxAlarmDuration sec."; } } ######################################################################################## # # HardAlarm start # ######################################################################################## sub alarmclock_hardalarmroutine_start($) { my ($hash) = @_; fhem("".AttrVal($hash->{NAME},"HardAlarmRoutine","")); Log3 $hash->{NAME}, 3, "alarmclock: $hash->{NAME} - HardAlarmRoutine started."; } ######################################################################################## # # RepRoutine # ######################################################################################## sub alarmclock_reproutine($$) { my ($hash, $Mode) = @_; if(((AttrVal($hash->{NAME},"RepRoutine1","NONE")) ne "NONE") && (AttrVal($hash->{NAME},"RepRoutine1Mode","off")) eq $Mode) { $hash->{helper}{Repeat1} = 0; my $WaitTime1 = AttrVal($hash->{NAME},"RepRoutine1WaitInSec","10"); InternalTimer(gettimeofday()+$WaitTime1, "alarmclock_reproutine1_start", $hash, 0); } if(((AttrVal($hash->{NAME},"RepRoutine2","NONE")) ne "NONE") && (AttrVal($hash->{NAME},"RepRoutine2Mode","off")) eq $Mode) { $hash->{helper}{Repeat2} = 0; my $WaitTime2 = AttrVal($hash->{NAME},"RepRoutine2WaitInSec","10"); InternalTimer(gettimeofday()+$WaitTime2, "alarmclock_reproutine2_start", $hash, 0); } if(((AttrVal($hash->{NAME},"RepRoutine3","NONE")) ne "NONE") && (AttrVal($hash->{NAME},"RepRoutine3Mode","off")) eq $Mode) { $hash->{helper}{Repeat3} = 0; my $WaitTime3 = AttrVal($hash->{NAME},"RepRoutine3WaitInSec","10"); InternalTimer(gettimeofday()+$WaitTime3, "alarmclock_reproutine3_start", $hash, 0); } } ######################################################################################## # # RepRoutine stop # ######################################################################################## sub alarmclock_reproutine_stop($$) { my ($hash, $Mode) = @_; if((AttrVal($hash->{NAME},"RepRoutine1Stop","Snooze")) eq $Mode) { RemoveInternalTimer($hash, "alarmclock_reproutine1_start"); } if((AttrVal($hash->{NAME},"RepRoutine2Stop","Snooze")) eq $Mode) { RemoveInternalTimer($hash, "alarmclock_reproutine2_start"); } if((AttrVal($hash->{NAME},"RepRoutine3Stop","Snooze")) eq $Mode) { RemoveInternalTimer($hash, "alarmclock_reproutine3_start"); } } ######################################################################################## # # RepRoutine1 start # ######################################################################################## sub alarmclock_reproutine1_start($) { my ($hash) = @_; my $WaitTime = AttrVal($hash->{NAME},"RepRoutine1WaitInSec","10"); my $Repeats = AttrVal($hash->{NAME},"RepRoutine1Repeats","2"); my $RNow = $hash->{helper}{Repeat1}; if ($Repeats > $RNow) { my $RNext = $RNow + 1; $hash->{helper}{Repeat1} = $RNext; fhem("".AttrVal($hash->{NAME},"RepRoutine1","")); InternalTimer(gettimeofday()+$WaitTime, "alarmclock_reproutine1_start", $hash, 0); } } ######################################################################################## # # RepRoutine2 start # ######################################################################################## sub alarmclock_reproutine2_start($) { my ($hash) = @_; my $WaitTime = AttrVal($hash->{NAME},"RepRoutine2WaitInSec","10"); my $Repeats = AttrVal($hash->{NAME},"RepRoutine2Repeats","2"); my $RNow = $hash->{helper}{Repeat2}; if ($Repeats > $RNow) { my $RNext = $RNow + 1; $hash->{helper}{Repeat2} = $RNext; fhem("".AttrVal($hash->{NAME},"RepRoutine2","")); InternalTimer(gettimeofday()+$WaitTime, "alarmclock_reproutine2_start", $hash, 0); } } ######################################################################################## # # RepRoutine3 start # ######################################################################################## sub alarmclock_reproutine3_start($) { my ($hash) = @_; my $WaitTime = AttrVal($hash->{NAME},"RepRoutine3WaitInSec","10"); my $Repeats = AttrVal($hash->{NAME},"RepRoutine3Repeats","2"); my $RNow = $hash->{helper}{Repeat3}; if ($Repeats > $RNow) { my $RNext = $RNow + 1; $hash->{helper}{Repeat3} = $RNext; fhem("".AttrVal($hash->{NAME},"RepRoutine3","")); InternalTimer(gettimeofday()+$WaitTime, "alarmclock_reproutine3_start", $hash, 0); } } ######################################################################################## # # Presence Check # ######################################################################################## sub alarmclock_presence_check($) { my ($hash) = @_; if ((AttrVal($hash->{NAME}, "PresenceDevice", "NONE" ) ne "NONE" ) && (AttrVal($hash->{NAME}, "PresenceCheck", "1" ) ne "0" )) { my @Presence = split(/\|/, AttrVal($hash->{NAME},"PresenceDevice","")); my $a = 0; my $b = scalar(@Presence); while ($a < $b) { my @PresenceDevice = split(/:/,$Presence[$a]); if (scalar(@PresenceDevice) eq "1") { if (ReadingsVal($PresenceDevice[0],"state","present") ne "present") { Log3 $hash->{NAME}, 1, "alarmclock: $hash->{NAME} - absent"; return 0; } } elsif (scalar(@PresenceDevice) eq "2") { if (ReadingsVal($PresenceDevice[0],"state","NONE") eq $PresenceDevice[1]) { Log3 $hash->{NAME}, 1, "alarmclock: $hash->{NAME} - absent"; return 0; } } elsif (scalar(@PresenceDevice) eq "3") { my $PresenceEvent = $PresenceDevice[2]; $PresenceEvent =~ s/ //g; if (ReadingsVal($PresenceDevice[0],$PresenceDevice[1],"NONE") eq $PresenceEvent) { Log3 $hash->{NAME}, 1, "alarmclock: $hash->{NAME} - absent"; return 0; } } $a ++; } } return 1; } ######################################################################################## # # Holiday Check # ######################################################################################## sub alarmclock_holiday_check($) { my ($hash) = @_; my ($SecNow, $MinNow, $HourNow, $DayNow, $MonthNow, $YearNow, $WDayNow, $YDNow, $SumTimeNow) = localtime(time); my $WDayToday = $WDayNow; my $WDayTomorrow = $WDayNow + 1; if ($WDayToday == 0) { $WDayToday = 7; } if ((AttrVal($hash->{NAME}, "HolidayDevice", "NONE" ) ne "NONE" ) && (AttrVal($hash->{NAME}, "HolidayCheck", "1" ) ne "0" )) { my @HolidayDays = split(/\|/, AttrVal($hash->{NAME},"HolidayDays","1|2|3|4|5|6|7")); my $DayToday = grep {$_==$WDayToday;} @HolidayDays; my $DayTomorrow = grep {$_==$WDayTomorrow;} @HolidayDays; my @Holiday = split(/\|/, AttrVal($hash->{NAME},"HolidayDevice","")); my $a = 0; my $b = scalar(@Holiday); while ($a < $b) { my @HolidayDevice = split(/:/,$Holiday[$a]); if (scalar(@HolidayDevice) eq "1") { if( IsDevice( $HolidayDevice[0], "holiday" )) { my $today = strftime("%2m-%2d", localtime(time)); my $tomorrow = strftime("%2m-%2d", localtime(time+86400)); my $todayevent = holiday_refresh($HolidayDevice[0],$today); if (($todayevent ne "none") && ($DayToday == 1)) { Log3 $hash->{NAME}, 1, "alarmclock: $hash->{NAME} - holiday => $HolidayDevice[0] - $todayevent"; $hash->{helper}{Today} = 8; } my $tomorrowevent = holiday_refresh($HolidayDevice[0],$tomorrow); if (($tomorrowevent ne "none") && ($DayTomorrow == 1)) { $hash->{helper}{Tomorrow} = 8; } } elsif( IsDevice($HolidayDevice[0], "Calendar" )) { my $stoday = strftime("%2d.%2m.20%2y", localtime(time)); my $stomorrow = strftime("%2d.%2m.20%2y", localtime(time+86400)); my $line = Calendar_Get($defs{$HolidayDevice[0]},"get","events","format:text"); if ($line) { chomp($line); my @lines = split('\n',$line); foreach $line (@lines) { chomp($line); my $date = substr($line,0,10); if (($date eq $stoday) && ($DayToday == 1)) { my $todaydesc = substr($line,15); Log3 $hash->{NAME}, 1, "alarmclock: $hash->{NAME} - holiday => $HolidayDevice[0] - $todaydesc"; $hash->{helper}{Today} = 8; } if (($date eq $stomorrow) && ($DayTomorrow == 1)) { $hash->{helper}{Tomorrow} = 8; } } } } } elsif (scalar(@HolidayDevice) eq "2") { if ((ReadingsVal($HolidayDevice[0],"state","NONE") eq $HolidayDevice[1]) && ($DayToday == 1)) { Log3 $hash->{NAME}, 1, "alarmclock: $hash->{NAME} - holiday => $HolidayDevice[0] - $HolidayDevice[1]"; $hash->{helper}{Today} = 8; } } elsif (scalar(@HolidayDevice) eq "3") { my $HolidayEvent = $HolidayDevice[2]; $HolidayEvent =~ s/ //g; if ((ReadingsVal($HolidayDevice[0],$HolidayDevice[1],"NONE") eq $HolidayEvent) && ($DayToday == 1)) { Log3 $hash->{NAME}, 1, "alarmclock: $hash->{NAME} - holiday => $HolidayDevice[0] - $HolidayDevice[1] - $HolidayEvent"; $hash->{helper}{Today} = 8; } } $a ++; } } } ######################################################################################## # # Vacation Check # ######################################################################################## sub alarmclock_vacation_check($) { my ($hash) = @_; my ($SecNow, $MinNow, $HourNow, $DayNow, $MonthNow, $YearNow, $WDayNow, $YDNow, $SumTimeNow) = localtime(time); my $WDayToday = $WDayNow; my $WDayTomorrow = $WDayNow + 1; if ($WDayToday == 0) { $WDayToday = 7; } if ((AttrVal($hash->{NAME}, "VacationDevice", "NONE" ) ne "NONE" ) && (AttrVal($hash->{NAME}, "VacationCheck", "1" ) ne "0" )) { my @VacationDays = split(/\|/, AttrVal($hash->{NAME},"VacationDays","1|2|3|4|5|6|7")); my $DayToday = grep {$_==$WDayToday;} @VacationDays; my $DayTomorrow = grep {$_==$WDayTomorrow;} @VacationDays; my @Vacation = split(/\|/, AttrVal($hash->{NAME},"VacationDevice","")); my $a = 0; my $b = scalar(@Vacation); while ($a < $b) { my @VacationDevice = split(/:/,$Vacation[$a]); if (scalar(@VacationDevice) eq "1") { if( IsDevice( $VacationDevice[0], "holiday" )) { my $today = strftime("%2m-%2d", localtime(time)); my $tomorrow = strftime("%2m-%2d", localtime(time+86400)); my $todayevent = holiday_refresh($VacationDevice[0],$today); if (($todayevent ne "none") && ($DayToday == 1)) { Log3 $hash->{NAME}, 1, "alarmclock: $hash->{NAME} - vacation => $VacationDevice[0] - $todayevent"; $hash->{helper}{Today} = 9; } my $tomorrowevent = holiday_refresh($VacationDevice[0],$tomorrow); if (($tomorrowevent ne "none") && ($DayTomorrow == 1)) { $hash->{helper}{Tomorrow} = 9; } } elsif( IsDevice($VacationDevice[0], "Calendar" )) { my $stoday = strftime("%2d.%2m.%2y", localtime(time)); my $stomorrow = strftime("%2d.%2m.%2y", localtime(time+86400)); my @tday = split('\.',$stoday); my @tmor = split('\.',$stomorrow); my $fline = Calendar_Get($defs{$VacationDevice[0]},"get","events","format:full"); my @lines = split('\n',$fline); foreach $fline (@lines) { chomp($fline); my @chunks = split(' ',$fline); my @sday = split('\.',$chunks[2]); my @eday = split('\.',substr($chunks[3],6,10)); my $rets = ($sday[2]-$tday[2]-2000)*365+($sday[1]-$tday[1])*31+($sday[0]-$tday[0]); my $rete = ($eday[2]-$tday[2]-2000)*365+($eday[1]-$tday[1])*31+($eday[0]-$tday[0]); if (($rete>=0) && ($rets<=0) && ($DayToday == 1)) { my $todaydesc = $chunks[5]; Log3 $hash->{NAME}, 1, "alarmclock: $hash->{NAME} - vacation => $VacationDevice[0] - $todaydesc"; $hash->{helper}{Today} = 9; } $rets = ($sday[2]-$tmor[2]-2000)*365+($sday[1]-$tmor[1])*31+($sday[0]-$tmor[0]); $rete = ($eday[2]-$tmor[2]-2000)*365+($eday[1]-$tmor[1])*31+($eday[0]-$tmor[0]); if (($rete>=0) && ($rets<=0) && ($DayTomorrow == 1)) { $hash->{helper}{Tomorrow} = 9; } } } } elsif (scalar(@VacationDevice) eq "2") { if ((ReadingsVal($VacationDevice[0],"state","NONE") eq $VacationDevice[1]) && ($DayToday == 1)) { Log3 $hash->{NAME}, 1, "alarmclock: $hash->{NAME} - vacation => $VacationDevice[0] - $VacationDevice[1]"; $hash->{helper}{Today} = 9; } } elsif (scalar(@VacationDevice) eq "3") { my $VacationEvent = $VacationDevice[2]; $VacationEvent =~ s/ //g; if ((ReadingsVal($VacationDevice[0],$VacationDevice[1],"NONE") eq $VacationEvent)&& ($DayToday == 1)) { Log3 $hash->{NAME}, 1, "alarmclock: $hash->{NAME} - vacation => $VacationDevice[0] - $VacationDevice[1] - $VacationEvent"; $hash->{helper}{Today} = 9; } } $a ++; } } } ######################################################################################## # # skip # ######################################################################################## sub alarmclock_skip($) { my ($hash) = @_; readingsSingleUpdate( $hash, "skip", "none", 1 ); alarmclock_createtimer($hash); } ######################################################################################## # # Notify # ######################################################################################## sub alarmclock_Notify($$) { my ($hash, $devhash) = @_; return "" if(IsDisabled($hash->{NAME})); # Return without any further action if the module is disabled my $devName = $devhash->{NAME}; # Device that created the events my $events = deviceEvents($devhash,0); return if( !$events ); ### alarmclock_createtimer wird nach dem start von fhem aufgerufen ### if($devName eq "global" && grep(m/^INITIALIZED|REREADCFG$/, @{$events})) { alarmclock_createtimer($hash); } ### Notify Alarm off ### if((ReadingsVal($hash->{NAME},"state",0)) =~ /^(Alarm is running|PreAlarm is running|Snooze for.*)/) { if(my @AlarmOffDevice = split(/\|/, AttrVal($hash->{NAME},"EventForAlarmOff",""))) { foreach my $AlarmOffDevice(@AlarmOffDevice) { my @AlarmOffDevicePart = split(/:/, $AlarmOffDevice,2); if(($devName eq $AlarmOffDevicePart[0]) && (grep { $AlarmOffDevicePart[1] eq $_ } @{$events})) { alarmclock_alarmroutine_stop($hash); } } } } ### Notify Snooze ### if((ReadingsVal($hash->{NAME},"state",0)) eq "Alarm is running") { if(my @SnoozeDevice = split(/\|/, AttrVal($hash->{NAME},"EventForSnooze",""))) { foreach my $SnoozeDevice(@SnoozeDevice) { my @SnoozeDevicePart = split(/:/, $SnoozeDevice,2); if(($devName eq $SnoozeDevicePart[0]) && (grep { $SnoozeDevicePart[1] eq $_ } @{$events})) { alarmclock_snooze_start($hash); } } } } } 1; =pod =item helper =item summary Fhem Modul to set up a Alarmclock =item summary_DE Fhem Weckermodul =begin html

alarmclock

=end html =cut