From d483e42c9713be1fc614d1f13d83d17ec676285d Mon Sep 17 00:00:00 2001 From: jpawlowski Date: Sat, 14 Mar 2015 15:28:24 +0000 Subject: [PATCH] RESIDENTStk wakeuptimer: add macro example code, auto-cleanup of temp. at-devices, advanced readings, error handlings and many other things git-svn-id: https://svn.fhem.de/fhem/trunk@8208 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/10_RESIDENTS.pm | 175 ++++++-------- fhem/FHEM/20_GUEST.pm | 231 +++++------------- fhem/FHEM/20_ROOMMATE.pm | 230 +++++------------- fhem/FHEM/RESIDENTStk.pm | 486 ++++++++++++++++++++++++++++++-------- 4 files changed, 571 insertions(+), 551 deletions(-) diff --git a/fhem/FHEM/10_RESIDENTS.pm b/fhem/FHEM/10_RESIDENTS.pm index 5fc0d4c5b..0fecdd753 100644 --- a/fhem/FHEM/10_RESIDENTS.pm +++ b/fhem/FHEM/10_RESIDENTS.pm @@ -23,7 +23,7 @@ # along with fhem. If not, see . # # -# Version: 1.2.0 +# Version: 1.2.1 # # Major Version History: # - 1.2.0 - 2015-03-11 @@ -80,7 +80,7 @@ sub RESIDENTS_Define($$) { if ($init_done) { $attr{$name}{alias} = "Residents"; $attr{$name}{devStateIcon} = -'.*home:status_available:absent .*absent:status_away_1:home .*gone:status_standby:home .*none:control_building_empty .*gotosleep:status_night:asleep .*asleep:status_night:awoken .*awoken:status_available:home'; +'.*home:status_available:absent .*absent:status_away_1:home .*gone:status_standby:home .*none:control_building_empty .*gotosleep:status_night:asleep .*asleep:status_night:awoken .*awoken:status_available:home .*:user_unknown:home'; $attr{$name}{group} = "Home State"; $attr{$name}{icon} = "control_building_filled"; $attr{$name}{room} = "Residents"; @@ -166,7 +166,10 @@ sub RESIDENTS_Notify($$) { foreach my $change ( @{ $dev->{CHANGED} } ) { # state changed - if ( $change !~ /:/ || $change =~ /wayhome:/ ) { + if ( $change !~ /:/ + || $change =~ /wayhome:/ + || $change =~ /wakeup:/ ) + { Log3 $hash, 4, "RESIDENTS " . $hashName . ": " @@ -200,6 +203,8 @@ sub RESIDENTS_Notify($$) { readingsEndUpdate( $hash, 1 ); } } + + return; } # if we have registered wakeup devices @@ -213,28 +218,13 @@ sub RESIDENTS_Notify($$) { if ( !$dev->{CHANGED} ); foreach my $change ( @{ $dev->{CHANGED} } ) { - - # state changed - if ( $change =~ /OFF|([0-9]{2}:[0-9]{2})/ ) { - Log3 $hash, 4, - "RESIDENTS " - . $hashName . ": " - . $devName - . ": notify about change to $change"; - - RESIDENTStk_wakeupSet( $devName, $change ); - } - else { - Log3 $hash, 5, - "RESIDENTS " - . $hashName . ": " - . $devName - . ": received unhandled notify about change $change"; - } + RESIDENTStk_wakeupSet( $devName, $change ); } + + return; } - # stuff for every registered wakeupdev + # process sub-child notifies: *_wakeupDevice foreach my $wakeupDev (@registeredWakeupdevs) { # if this is a notification of a registered sub dummy device @@ -249,25 +239,11 @@ sub RESIDENTS_Notify($$) { if ( !$dev->{CHANGED} ); foreach my $change ( @{ $dev->{CHANGED} } ) { - - # state changed to on - if ( $change eq "auto" ) { - Log3 $hash, 4, - "RESIDENTS " - . $hashName . ": " - . $devName - . ": notify about change to $change"; - - RESIDENTStk_wakeupSet($wakeupDev); - } - else { - Log3 $hash, 5, - "RESIDENTS " - . $hashName . ": " - . $devName - . ": received unhandled notify about change $change"; - } + RESIDENTStk_wakeupSet( $wakeupDev, $change ) + if ( $change ne "off" ); } + + last; } } } @@ -580,6 +556,8 @@ sub RESIDENTS_Set($@) { $wakeuptimerName = $name . "_wakeuptimer" . $i; } else { + my $sortby = AttrVal( $name, "sortby", -1 ); + $sortby++; # create new dummy device fhem "define $wakeuptimerName dummy"; @@ -587,36 +565,41 @@ sub RESIDENTS_Set($@) { fhem "attr $wakeuptimerName comment Auto-created by RESIDENTS module for use with RESIDENTS Toolkit"; fhem -"attr $wakeuptimerName devStateIcon OFF:general_aus\@red .*:general_an\@green:OFF"; +"attr $wakeuptimerName devStateIcon OFF:general_aus\@red:reset running:general_an\@blue:stop .*:general_an\@green:nextRun%20OFF"; fhem "attr $wakeuptimerName group " . $attr{$name}{group} if ( defined( $attr{$name}{group} ) ); fhem "attr $wakeuptimerName icon time_timer"; fhem "attr $wakeuptimerName room " . $attr{$name}{room} if ( defined( $attr{$name}{room} ) ); fhem -"attr $wakeuptimerName setList state:OFF,00:00,00:15,00:30,00:45,01:00,01:15,01:30,01:45,02:00,02:15,02:30,02:45,03:00,03:15,03:30,03:45,04:00,04:15,04:30,04:45,05:00,05:15,05:30,05:45,06:00,06:15,06:30,06:45,07:00,07:15,07:30,07:45,08:00,08:15,08:30,08:45,09:00,09:15,09:30,09:45,10:00,10:15,10:30,10:45,11:00,11:15,11:30,11:45,12:00,12:15,12:30,12:45,13:00,13:15,13:30,13:45,14:00,14:15,14:30,14:45,15:00,15:15,15:30,15:45,16:00,16:15,16:30,16:45,17:00,17:15,17:30,17:45,18:00,18:15,18:30,18:45,19:00,19:15,19:30,19:45,20:00,20:15,20:30,20:45,21:00,21:15,21:30,21:45,22:00,22:15,22:30,22:45,23:00,23:15,23:30,23:45"; +"attr $wakeuptimerName setList nextRun:OFF,00:00,00:15,00:30,00:45,01:00,01:15,01:30,01:45,02:00,02:15,02:30,02:45,03:00,03:15,03:30,03:45,04:00,04:15,04:30,04:45,05:00,05:15,05:30,05:45,06:00,06:15,06:30,06:45,07:00,07:15,07:30,07:45,08:00,08:15,08:30,08:45,09:00,09:15,09:30,09:45,10:00,10:15,10:30,10:45,11:00,11:15,11:30,11:45,12:00,12:15,12:30,12:45,13:00,13:15,13:30,13:45,14:00,14:15,14:30,14:45,15:00,15:15,15:30,15:45,16:00,16:15,16:30,16:45,17:00,17:15,17:30,17:45,18:00,18:15,18:30,18:45,19:00,19:15,19:30,19:45,20:00,20:15,20:30,20:45,21:00,21:15,21:30,21:45,22:00,22:15,22:30,22:45,23:00,23:15,23:30,23:45 reset:noArg stop:noArg"; fhem "attr $wakeuptimerName userattr wakeupUserdevice"; + fhem "attr $wakeuptimerName sortby " . $sortby + if ($sortby); fhem "attr $wakeuptimerName wakeupUserdevice $name"; - fhem "attr $wakeuptimerName webCmd state"; + fhem "attr $wakeuptimerName webCmd nextRun"; # register slave device - if ( defined( $attr{$name}{rgr_wakeupDevice} ) ) { - fhem "attr $name rgr_wakeupDevice " - . $attr{$name}{rgr_wakeupDevice} - . ",$wakeuptimerName"; - } - else { + my $wakeupDevice = AttrVal( $name, "rgr_wakeupDevice", 0 ); + if ( !$wakeupDevice ) { fhem "attr $name rgr_wakeupDevice $wakeuptimerName"; } + elsif ( $wakeupDevice !~ /(.*,?)($wakeuptimerName)(.*,?)/ ) + { + fhem "attr $name rgr_wakeupDevice " + . $wakeupDevice + . ",$wakeuptimerName"; + } # trigger first update - fhem "set $wakeuptimerName OFF"; + fhem "set $wakeuptimerName nextRun OFF"; $created = 1; - return -"Dummy $wakeuptimerName and other pending devices created and pre-configured. You may edit Macro_$wakeuptimerName to define your wake-up actions and at_$wakeuptimerName for optional at-device adjustments."; } } + + return +"Dummy $wakeuptimerName and other pending devices created and pre-configured.\nYou may edit Macro_$wakeuptimerName to define your wake-up actions\nand at_$wakeuptimerName for optional at-device adjustments."; } else { return "Invalid 2nd argument, choose one of wakeuptimer "; @@ -657,6 +640,7 @@ sub RESIDENTS_UpdateReadings (@) { my $state_totalGuests = 0; my $state_guestDev = 0; my $wayhome = 0; + my $wakeup = 0; my $newstate; my $presence = "absent"; @@ -680,32 +664,36 @@ sub RESIDENTS_UpdateReadings (@) { $state_totalPresent++; } - if ( $defs{$roommate}{READINGS}{state}{VAL} eq "gotosleep" ) { + elsif ( $defs{$roommate}{READINGS}{state}{VAL} eq "gotosleep" ) { $state_gotosleep++; $state_totalPresent++; } - if ( $defs{$roommate}{READINGS}{state}{VAL} eq "asleep" ) { + elsif ( $defs{$roommate}{READINGS}{state}{VAL} eq "asleep" ) { $state_asleep++; $state_totalPresent++; } - if ( $defs{$roommate}{READINGS}{state}{VAL} eq "awoken" ) { + elsif ( $defs{$roommate}{READINGS}{state}{VAL} eq "awoken" ) { $state_awoken++; $state_totalPresent++; } - if ( $defs{$roommate}{READINGS}{state}{VAL} eq "absent" ) { + elsif ( $defs{$roommate}{READINGS}{state}{VAL} eq "absent" ) { $state_absent++; $state_totalAbsent++; } - if ( $defs{$roommate}{READINGS}{state}{VAL} eq "gone" ) { + elsif ( $defs{$roommate}{READINGS}{state}{VAL} eq "gone" ) { $state_gone++; $state_totalAbsent++; } } + if ( defined( $defs{$roommate}{READINGS}{wakeup}{VAL} ) ) { + $wakeup += $defs{$roommate}{READINGS}{wakeup}{VAL}; + } + if ( defined( $defs{$roommate}{READINGS}{wayhome}{VAL} ) ) { $wayhome += $defs{$roommate}{READINGS}{wayhome}{VAL}; } @@ -752,6 +740,10 @@ sub RESIDENTS_UpdateReadings (@) { } } + if ( defined( $defs{$guest}{READINGS}{wakeup}{VAL} ) ) { + $wakeup += $defs{$guest}{READINGS}{wakeup}{VAL}; + } + if ( defined( $defs{$guest}{READINGS}{wayhome}{VAL} ) ) { $wayhome += $defs{$guest}{READINGS}{wayhome}{VAL}; } @@ -801,6 +793,10 @@ sub RESIDENTS_UpdateReadings (@) { if ( !defined( $hash->{READINGS}{residentsGone}{VAL} ) || $hash->{READINGS}{residentsGone}{VAL} ne $state_gone ); + readingsBulkUpdate( $hash, "residentsTotalWakeup", $wakeup ) + if ( !defined( $hash->{READINGS}{residentsTotalWakeup}{VAL} ) + || $hash->{READINGS}{residentsTotalWakeup}{VAL} ne $wakeup ); + readingsBulkUpdate( $hash, "residentsTotalWayhome", $wayhome ) if ( !defined( $hash->{READINGS}{residentsTotalWayhome}{VAL} ) || $hash->{READINGS}{residentsTotalWayhome}{VAL} ne $wayhome ); @@ -911,14 +907,14 @@ sub RESIDENTS_UpdateReadings (@) { readingsBulkUpdate( $hash, "lastDurSleep", - RESIDENTS_TimeDiff( + RESIDENTStk_TimeDiff( $datetime, $hash->{READINGS}{lastSleep}{VAL} ) ); readingsBulkUpdate( $hash, "lastDurSleep_cr", - RESIDENTS_TimeDiff( + RESIDENTStk_TimeDiff( $datetime, $hash->{READINGS}{lastSleep}{VAL}, "min" ) ); @@ -945,14 +941,14 @@ sub RESIDENTS_UpdateReadings (@) { readingsBulkUpdate( $hash, "lastDurAbsence", - RESIDENTS_TimeDiff( + RESIDENTStk_TimeDiff( $datetime, $hash->{READINGS}{lastDeparture}{VAL} ) ); readingsBulkUpdate( $hash, "lastDurAbsence_cr", - RESIDENTS_TimeDiff( + RESIDENTStk_TimeDiff( $datetime, $hash->{READINGS}{lastDeparture}{VAL}, "min" ) @@ -969,14 +965,14 @@ sub RESIDENTS_UpdateReadings (@) { readingsBulkUpdate( $hash, "lastDurPresence", - RESIDENTS_TimeDiff( + RESIDENTStk_TimeDiff( $datetime, $hash->{READINGS}{lastArrival}{VAL} ) ); readingsBulkUpdate( $hash, "lastDurPresence_cr", - RESIDENTS_TimeDiff( + RESIDENTStk_TimeDiff( $datetime, $hash->{READINGS}{lastArrival}{VAL}, "min" ) @@ -989,49 +985,6 @@ sub RESIDENTS_UpdateReadings (@) { readingsEndUpdate( $hash, 1 ); } -################################### -sub RESIDENTS_TimeDiff($$;$) { - my ( $datetimeNow, $datetimeOld, $format ) = @_; - - my $timestampNow = RESIDENTS_Datetime2Timestamp($datetimeNow); - my $timestampOld = RESIDENTS_Datetime2Timestamp($datetimeOld); - my $timeDiff = $timestampNow - $timestampOld; - - # return seconds - return int( $timeDiff + 0.5 ) if ( defined($format) && $format eq "sec" ); - - # return minutes - return int( $timeDiff / 60 + 0.5 ) - if ( defined($format) && $format eq "min" ); - - # return human readable format - my $hours = ( $timeDiff < 3600 ? 0 : int( $timeDiff / 3600 ) ); - $timeDiff -= ( $hours == 0 ? 0 : ( $hours * 3600 ) ); - my $minutes = ( $timeDiff < 60 ? 0 : int( $timeDiff / 60 ) ); - my $seconds = $timeDiff % 60; - - $hours = "0" . $hours if ( $hours < 10 ); - $minutes = "0" . $minutes if ( $minutes < 10 ); - $seconds = "0" . $seconds if ( $seconds < 10 ); - - return "$hours:$minutes:$seconds"; -} - -################################### -sub RESIDENTS_Datetime2Timestamp($) { - my ($datetime) = @_; - - my ( $date, $time, $y, $m, $d, $hour, $min, $sec, $timestamp ); - - ( $date, $time ) = split( ' ', $datetime ); - ( $y, $m, $d ) = split( '-', $date ); - ( $hour, $min, $sec ) = split( ':', $time ); - $m -= 01; - $timestamp = timelocal( $sec, $min, $hour, $d, $m, $y ); - - return $timestamp; -} - 1; =pod @@ -1212,6 +1165,9 @@ sub RESIDENTS_Datetime2Timestamp($) {
  • residentsTotalPresent - number of all residents who are currently at home
  • +
  • + residentsTotalWakeup - number of all residents which currently have a wake-up program being executed +
  • residentsTotalWayhome - number of all active residents who are currently on their way back home
  • @@ -1256,7 +1212,7 @@ sub RESIDENTS_Datetime2Timestamp($) { wakeupResetdays - if wakeupDefaultTime is set you may restrict timer reset to specific days only. Mon=1,Tue=2,Wed=3,Thu=4,Fri=5,Sat=6,Sun=0 (optional)
  • - wakeupResetSwitcher - DUMMY device to quickly turn on/off reset function (optional, device will be auto-created/-deleted) + wakeupResetSwitcher - DUMMY device to quickly turn on/off reset function (optional, device will be auto-created)
  • wakeupUserdevice - backlink to RESIDENTS, ROOMMATE or GUEST device to check it's status (mandatory) @@ -1444,6 +1400,9 @@ sub RESIDENTS_Datetime2Timestamp($) {
  • residentsTotalPresent - Summe aller aktiven Bewohner, die momentan zu Hause sind
  • +
  • + residentsTotalWakeup - Summe aller Bewohner, bei denen aktuell ein Weckprogramm ausgeführt wird +
  • residentsTotalWayhome - Summe aller aktiven Bewohner, die momentan auf dem Weg zurück nach Hause sind
  • @@ -1488,7 +1447,7 @@ sub RESIDENTS_Datetime2Timestamp($) { wakeupResetdays - sofern wakeupDefaultTime gesetzt ist, kann der Reset hier auf betimmte Tage begrenzt werden. Mon=1,Di=2,Mi=3,Do=4,Fr=5,Sa=6,So=0 (optional)
  • - wakeupResetSwitcher - das DUMMY Device, welches zum schnellen ein/aus schalten der Resetfunktion verwendet wird (optional, Device wird automatisch angelegt/gelöscht) + wakeupResetSwitcher - das DUMMY Device, welches zum schnellen ein/aus schalten der Resetfunktion verwendet wird (optional, Device wird automatisch angelegt)
  • wakeupUserdevice - Backlink zum RESIDENTS, ROOMMATE oder GUEST Gerät, um dessen Status zu prüfen (notwendig) diff --git a/fhem/FHEM/20_GUEST.pm b/fhem/FHEM/20_GUEST.pm index 5aee26684..a2dc14654 100644 --- a/fhem/FHEM/20_GUEST.pm +++ b/fhem/FHEM/20_GUEST.pm @@ -23,7 +23,7 @@ # along with fhem. If not, see . # # -# Version: 1.2.0 +# Version: 1.2.1 # # Major Version History: # - 1.2.0 - 2015-03-11 @@ -141,7 +141,7 @@ sub GUEST_Define($$) { $attr{$name}{alias} = $aliasname; $attr{$name}{devStateIcon} = -".*home:user_available:absent .*absent:user_away:home .*none:control_building_empty:home .*gotosleep:scene_toilet:asleep .*asleep:scene_sleeping:awoken .*awoken:scene_sleeping_alternat:home .*:user_unknown"; +".*home:user_available:absent .*absent:user_away:home .*none:control_building_empty:home .*gotosleep:scene_toilet:asleep .*asleep:scene_sleeping:awoken .*awoken:scene_sleeping_alternat:home .*:user_unknown:home"; $attr{$name}{group} = "Guests"; $attr{$name}{icon} = "scene_visit_guests"; $attr{$name}{rg_realname} = "alias"; @@ -179,8 +179,8 @@ sub GUEST_Define($$) { sub GUEST_Undefine($$) { my ( $hash, $name ) = @_; - GUEST_RemoveInternalTimer( "AutoGone", $hash ); - GUEST_RemoveInternalTimer( "DurationTimer", $hash ); + RESIDENTStk_RemoveInternalTimer( "AutoGone", $hash ); + RESIDENTStk_RemoveInternalTimer( "DurationTimer", $hash ); if ( defined( $hash->{RESIDENTGROUPS} ) ) { my @registeredResidentgroups = @@ -225,28 +225,13 @@ sub GUEST_Notify($$) { if ( !$dev->{CHANGED} ); foreach my $change ( @{ $dev->{CHANGED} } ) { - - # state changed - if ( $change =~ /OFF|([0-9]{2}:[0-9]{2})/ ) { - Log3 $hash, 4, - "GUEST " - . $hashName . ": " - . $devName - . ": notify about change to $change"; - - RESIDENTStk_wakeupSet( $devName, $change ); - } - else { - Log3 $hash, 5, - "GUEST " - . $hashName . ": " - . $devName - . ": received unhandled notify about change $change"; - } + RESIDENTStk_wakeupSet( $devName, $change ); } + + return; } - # stuff for every registered wakeupdev + # process sub-child notifies: *_wakeupDevice foreach my $wakeupDev (@registeredWakeupdevs) { # if this is a notification of a registered sub dummy device @@ -261,25 +246,11 @@ sub GUEST_Notify($$) { if ( !$dev->{CHANGED} ); foreach my $change ( @{ $dev->{CHANGED} } ) { - - # state changed to on - if ( $change eq "auto" ) { - Log3 $hash, 4, - "GUEST " - . $hashName . ": " - . $devName - . ": notify about change to $change"; - - RESIDENTStk_wakeupSet($wakeupDev); - } - else { - Log3 $hash, 5, - "GUEST " - . $hashName . ": " - . $devName - . ": received unhandled notify about change $change"; - } + RESIDENTStk_wakeupSet( $wakeupDev, $change ) + if ( $change ne "off" ); } + + last; } } } @@ -468,14 +439,14 @@ sub GUEST_Set($@) { readingsBulkUpdate( $hash, "lastDurSleep", - GUEST_TimeDiff( + RESIDENTStk_TimeDiff( $datetime, $hash->{READINGS}{lastSleep}{VAL} ) ); readingsBulkUpdate( $hash, "lastDurSleep_cr", - GUEST_TimeDiff( + RESIDENTStk_TimeDiff( $datetime, $hash->{READINGS}{lastSleep}{VAL}, "min" ) ); @@ -545,14 +516,14 @@ sub GUEST_Set($@) { readingsBulkUpdate( $hash, "lastDurAbsence", - GUEST_TimeDiff( + RESIDENTStk_TimeDiff( $datetime, $hash->{READINGS}{lastDeparture}{VAL} ) ); readingsBulkUpdate( $hash, "lastDurAbsence_cr", - GUEST_TimeDiff( + RESIDENTStk_TimeDiff( $datetime, $hash->{READINGS}{lastDeparture}{VAL}, "min" ) @@ -569,14 +540,14 @@ sub GUEST_Set($@) { readingsBulkUpdate( $hash, "lastDurPresence", - GUEST_TimeDiff( + RESIDENTStk_TimeDiff( $datetime, $hash->{READINGS}{lastArrival}{VAL} ) ); readingsBulkUpdate( $hash, "lastDurPresence_cr", - GUEST_TimeDiff( + RESIDENTStk_TimeDiff( $datetime, $hash->{READINGS}{lastArrival}{VAL}, "min" ) @@ -641,7 +612,7 @@ sub GUEST_Set($@) { GUEST_AutoGone($hash); } elsif ( $state eq "absent" ) { - GUEST_RemoveInternalTimer( "AutoGone", $hash ); + RESIDENTStk_RemoveInternalTimer( "AutoGone", $hash ); } } } @@ -779,6 +750,8 @@ sub GUEST_Set($@) { $wakeuptimerName = $name . "_wakeuptimer" . $i; } else { + my $sortby = AttrVal( $name, "sortby", -1 ); + $sortby++; # create new dummy device fhem "define $wakeuptimerName dummy"; @@ -786,36 +759,41 @@ sub GUEST_Set($@) { fhem "attr $wakeuptimerName comment Auto-created by GUEST module for use with RESIDENTS Toolkit"; fhem -"attr $wakeuptimerName devStateIcon OFF:general_aus\@red .*:general_an\@green:OFF"; +"attr $wakeuptimerName devStateIcon OFF:general_aus\@red:reset running:general_an\@blue:stop .*:general_an\@green:nextRun%20OFF"; fhem "attr $wakeuptimerName group " . $attr{$name}{group} if ( defined( $attr{$name}{group} ) ); fhem "attr $wakeuptimerName icon time_timer"; fhem "attr $wakeuptimerName room " . $attr{$name}{room} if ( defined( $attr{$name}{room} ) ); fhem -"attr $wakeuptimerName setList state:OFF,00:00,00:15,00:30,00:45,01:00,01:15,01:30,01:45,02:00,02:15,02:30,02:45,03:00,03:15,03:30,03:45,04:00,04:15,04:30,04:45,05:00,05:15,05:30,05:45,06:00,06:15,06:30,06:45,07:00,07:15,07:30,07:45,08:00,08:15,08:30,08:45,09:00,09:15,09:30,09:45,10:00,10:15,10:30,10:45,11:00,11:15,11:30,11:45,12:00,12:15,12:30,12:45,13:00,13:15,13:30,13:45,14:00,14:15,14:30,14:45,15:00,15:15,15:30,15:45,16:00,16:15,16:30,16:45,17:00,17:15,17:30,17:45,18:00,18:15,18:30,18:45,19:00,19:15,19:30,19:45,20:00,20:15,20:30,20:45,21:00,21:15,21:30,21:45,22:00,22:15,22:30,22:45,23:00,23:15,23:30,23:45"; +"attr $wakeuptimerName setList nextRun:OFF,00:00,00:15,00:30,00:45,01:00,01:15,01:30,01:45,02:00,02:15,02:30,02:45,03:00,03:15,03:30,03:45,04:00,04:15,04:30,04:45,05:00,05:15,05:30,05:45,06:00,06:15,06:30,06:45,07:00,07:15,07:30,07:45,08:00,08:15,08:30,08:45,09:00,09:15,09:30,09:45,10:00,10:15,10:30,10:45,11:00,11:15,11:30,11:45,12:00,12:15,12:30,12:45,13:00,13:15,13:30,13:45,14:00,14:15,14:30,14:45,15:00,15:15,15:30,15:45,16:00,16:15,16:30,16:45,17:00,17:15,17:30,17:45,18:00,18:15,18:30,18:45,19:00,19:15,19:30,19:45,20:00,20:15,20:30,20:45,21:00,21:15,21:30,21:45,22:00,22:15,22:30,22:45,23:00,23:15,23:30,23:45 reset:noArg stop:noArg"; fhem "attr $wakeuptimerName userattr wakeupUserdevice"; + fhem "attr $wakeuptimerName sortby " . $sortby + if ($sortby); fhem "attr $wakeuptimerName wakeupUserdevice $name"; - fhem "attr $wakeuptimerName webCmd state"; + fhem "attr $wakeuptimerName webCmd nextRun"; # register slave device - if ( defined( $attr{$name}{rg_wakeupDevice} ) ) { - fhem "attr $name rg_wakeupDevice " - . $attr{$name}{rg_wakeupDevice} - . ",$wakeuptimerName"; - } - else { + my $wakeupDevice = AttrVal( $name, "rg_wakeupDevice", 0 ); + if ( !$wakeupDevice ) { fhem "attr $name rg_wakeupDevice $wakeuptimerName"; } + elsif ( $wakeupDevice !~ /(.*,?)($wakeuptimerName)(.*,?)/ ) + { + fhem "attr $name rg_wakeupDevice " + . $wakeupDevice + . ",$wakeuptimerName"; + } # trigger first update - fhem "set $wakeuptimerName OFF"; + fhem "set $wakeuptimerName nextRun OFF"; $created = 1; - return -"Dummy $wakeuptimerName and other pending devices created and pre-configured. You may edit Macro_$wakeuptimerName to define your wake-up actions and at_$wakeuptimerName for optional at-device adjustments."; } } + + return +"Dummy $wakeuptimerName and other pending devices created and pre-configured.\nYou may edit Macro_$wakeuptimerName to define your wake-up actions\nand at_$wakeuptimerName for optional at-device adjustments."; } else { return "Invalid 2nd argument, choose one of wakeuptimer "; @@ -842,7 +820,7 @@ sub GUEST_AutoGone($;$) { my $hash = ( $mHash->{HASH} ) ? $mHash->{HASH} : $mHash; my $name = $hash->{NAME}; - GUEST_RemoveInternalTimer( "AutoGone", $hash ); + RESIDENTStk_RemoveInternalTimer( "AutoGone", $hash ); if ( defined( $hash->{READINGS}{state}{VAL} ) && $hash->{READINGS}{state}{VAL} eq "absent" ) @@ -871,8 +849,8 @@ sub GUEST_AutoGone($;$) { else { my $runtime = $timestamp + $timeout * 3600; Log3 $name, 4, "GUEST $name: AutoGone timer scheduled: $runtime"; - GUEST_InternalTimer( "AutoGone", $runtime, "GUEST_AutoGone", $hash, - 1 ); + RESIDENTStk_InternalTimer( "AutoGone", $runtime, "GUEST_AutoGone", + $hash, 1 ); } } @@ -895,7 +873,7 @@ sub GUEST_DurationTimer($;$) { my $durAbsence = "0"; my $durSleep = "0"; - GUEST_RemoveInternalTimer( "DurationTimer", $hash ); + RESIDENTStk_RemoveInternalTimer( "DurationTimer", $hash ); if ( !defined( $attr{$name}{rg_noDuration} ) || $attr{$name}{rg_noDuration} == 0 ) @@ -910,7 +888,7 @@ sub GUEST_DurationTimer($;$) { { $durPresence = $timestampNow - - GUEST_Datetime2Timestamp( + RESIDENTStk_Datetime2Timestamp( $hash->{READINGS}{lastArrival}{VAL} ); } } @@ -926,7 +904,7 @@ sub GUEST_DurationTimer($;$) { { $durAbsence = $timestampNow - - GUEST_Datetime2Timestamp( + RESIDENTStk_Datetime2Timestamp( $hash->{READINGS}{lastDeparture}{VAL} ); } } @@ -940,20 +918,23 @@ sub GUEST_DurationTimer($;$) { { $durSleep = $timestampNow - - GUEST_Datetime2Timestamp( $hash->{READINGS}{lastSleep}{VAL} ); + RESIDENTStk_Datetime2Timestamp( + $hash->{READINGS}{lastSleep}{VAL} ); } } my $durPresence_hr = - ( $durPresence > 0 ) ? GUEST_sec2time($durPresence) : "00:00:00"; + ( $durPresence > 0 ) + ? RESIDENTStk_sec2time($durPresence) + : "00:00:00"; my $durPresence_cr = ( $durPresence > 60 ) ? int( $durPresence / 60 + 0.5 ) : 0; my $durAbsence_hr = - ( $durAbsence > 0 ) ? GUEST_sec2time($durAbsence) : "00:00:00"; + ( $durAbsence > 0 ) ? RESIDENTStk_sec2time($durAbsence) : "00:00:00"; my $durAbsence_cr = ( $durAbsence > 60 ) ? int( $durAbsence / 60 + 0.5 ) : 0; my $durSleep_hr = - ( $durSleep > 0 ) ? GUEST_sec2time($durSleep) : "00:00:00"; + ( $durSleep > 0 ) ? RESIDENTStk_sec2time($durSleep) : "00:00:00"; my $durSleep_cr = ( $durSleep > 60 ) ? int( $durSleep / 60 + 0.5 ) : 0; readingsBeginUpdate($hash) if ( !$silent ); @@ -978,115 +959,13 @@ sub GUEST_DurationTimer($;$) { readingsEndUpdate( $hash, 1 ) if ( !$silent ); } - GUEST_InternalTimer( "DurationTimer", $timestampNow + 60, + RESIDENTStk_InternalTimer( "DurationTimer", $timestampNow + 60, "GUEST_DurationTimer", $hash, 1 ) if ( $state ne "none" ); return undef; } -################################### -sub GUEST_TimeDiff ($$;$) { - my ( $datetimeNow, $datetimeOld, $format ) = @_; - - my $timestampNow = GUEST_Datetime2Timestamp($datetimeNow); - my $timestampOld = GUEST_Datetime2Timestamp($datetimeOld); - my $timeDiff = $timestampNow - $timestampOld; - - # return seconds - return int( $timeDiff + 0.5 ) if ( defined($format) && $format eq "sec" ); - - # return minutes - return int( $timeDiff / 60 + 0.5 ) - if ( defined($format) && $format eq "min" ); - - # return human readable format - my $hours = ( $timeDiff < 3600 ? 0 : int( $timeDiff / 3600 ) ); - $timeDiff -= ( $hours == 0 ? 0 : ( $hours * 3600 ) ); - my $minutes = ( $timeDiff < 60 ? 0 : int( $timeDiff / 60 ) ); - my $seconds = $timeDiff % 60; - - $hours = "0" . $hours if ( $hours < 10 ); - $minutes = "0" . $minutes if ( $minutes < 10 ); - $seconds = "0" . $seconds if ( $seconds < 10 ); - - return "$hours:$minutes:$seconds"; -} - -################################### -sub GUEST_Datetime2Timestamp($) { - my ($datetime) = @_; - - my ( $date, $time, $y, $m, $d, $hour, $min, $sec, $timestamp ); - - ( $date, $time ) = split( ' ', $datetime ); - ( $y, $m, $d ) = split( '-', $date ); - ( $hour, $min, $sec ) = split( ':', $time ); - $m -= 01; - $timestamp = timelocal( $sec, $min, $hour, $d, $m, $y ); - - return $timestamp; -} - -################################### -sub GUEST_sec2time($) { - my ($sec) = @_; - - # return human readable format - my $hours = ( $sec < 3600 ? 0 : int( $sec / 3600 ) ); - $sec -= ( $hours == 0 ? 0 : ( $hours * 3600 ) ); - my $minutes = ( $sec < 60 ? 0 : int( $sec / 60 ) ); - my $seconds = $sec % 60; - - $hours = "0" . $hours if ( $hours < 10 ); - $minutes = "0" . $minutes if ( $minutes < 10 ); - $seconds = "0" . $seconds if ( $seconds < 10 ); - - return "$hours:$minutes:$seconds"; -} - -################################### -sub GUEST_InternalTimer($$$$$) { - my ( $modifier, $tim, $callback, $hash, $waitIfInitNotDone ) = @_; - - my $mHash; - if ( $modifier eq "" ) { - $mHash = $hash; - } - else { - my $timerName = $hash->{NAME} . "_" . $modifier; - if ( exists( $hash->{TIMER}{$timerName} ) ) { - $mHash = $hash->{TIMER}{$timerName}; - } - else { - $mHash = { - HASH => $hash, - NAME => $hash->{NAME} . "_" . $modifier, - MODIFIER => $modifier - }; - $hash->{TIMER}{$timerName} = $mHash; - } - } - InternalTimer( $tim, $callback, $mHash, $waitIfInitNotDone ); -} - -################################### -sub GUEST_RemoveInternalTimer($$) { - my ( $modifier, $hash ) = @_; - - my $timerName = $hash->{NAME} . "_" . $modifier; - if ( $modifier eq "" ) { - RemoveInternalTimer($hash); - } - else { - my $mHash = $hash->{TIMER}{$timerName}; - if ( defined($mHash) ) { - delete $hash->{TIMER}{$timerName}; - RemoveInternalTimer($mHash); - } - } -} - ################################### sub GUEST_StartInternalTimers($$) { my ($hash) = @_; @@ -1360,6 +1239,9 @@ sub GUEST_StartInternalTimers($$) {
  • state - reflects the current state
  • +
  • + wakeup - becomes '1' while a wake-up program of this resident is being executed +
  • wayhome - depending on current location, it can become '1' if individual is on his/her way back home
  • @@ -1636,6 +1518,9 @@ sub GUEST_StartInternalTimers($$) {
  • state - gibt den aktuellen Status wieder
  • +
  • + wakeup - hat den Wert '1' während ein Weckprogramm dieses Bewohners ausgeführt wird +
  • wayhome - abhängig vom aktullen Aufenthaltsort, kann der Wert '1' werden, wenn die Person auf dem weg zurück nach Hause ist
  • diff --git a/fhem/FHEM/20_ROOMMATE.pm b/fhem/FHEM/20_ROOMMATE.pm index f64366619..867d108d1 100644 --- a/fhem/FHEM/20_ROOMMATE.pm +++ b/fhem/FHEM/20_ROOMMATE.pm @@ -23,7 +23,7 @@ # along with fhem. If not, see . # # -# Version: 1.2.0 +# Version: 1.2.1 # # Major Version History: # - 1.2.0 - 2015-03-11 @@ -143,7 +143,7 @@ sub ROOMMATE_Define($$) { $attr{$name}{alias} = "Status"; $attr{$name}{devStateIcon} = -".*home:user_available:absent .*absent:user_away:home .*gone:user_ext_away:home .*gotosleep:scene_toilet:asleep .*asleep:scene_sleeping:awoken .*awoken:scene_sleeping_alternat:home .*:user_unknown"; +".*home:user_available:absent .*absent:user_away:home .*gone:user_ext_away:home .*gotosleep:scene_toilet:asleep .*asleep:scene_sleeping:awoken .*awoken:scene_sleeping_alternat:home .*:user_unknown:home"; $attr{$name}{icon} = "people_sensor"; $attr{$name}{rr_realname} = "alias"; $attr{$name}{sortby} = "1"; @@ -184,8 +184,8 @@ sub ROOMMATE_Define($$) { sub ROOMMATE_Undefine($$) { my ( $hash, $name ) = @_; - ROOMMATE_RemoveInternalTimer( "AutoGone", $hash ); - ROOMMATE_RemoveInternalTimer( "DurationTimer", $hash ); + RESIDENTStk_RemoveInternalTimer( "AutoGone", $hash ); + RESIDENTStk_RemoveInternalTimer( "DurationTimer", $hash ); if ( defined( $hash->{RESIDENTGROUPS} ) ) { my @registeredResidentgroups = @@ -230,28 +230,13 @@ sub ROOMMATE_Notify($$) { if ( !$dev->{CHANGED} ); foreach my $change ( @{ $dev->{CHANGED} } ) { - - # state changed - if ( $change =~ /OFF|([0-9]{2}:[0-9]{2})/ ) { - Log3 $hash, 4, - "ROOMMATE " - . $hashName . ": " - . $devName - . ": notify about change to $change"; - - RESIDENTStk_wakeupSet( $devName, $change ); - } - else { - Log3 $hash, 5, - "ROOMMATE " - . $hashName . ": " - . $devName - . ": received unhandled notify about change $change"; - } + RESIDENTStk_wakeupSet( $devName, $change ); } + + return; } - # stuff for every registered wakeupdev + # process sub-child notifies: *_wakeupDevice foreach my $wakeupDev (@registeredWakeupdevs) { # if this is a notification of a registered sub dummy device @@ -266,25 +251,11 @@ sub ROOMMATE_Notify($$) { if ( !$dev->{CHANGED} ); foreach my $change ( @{ $dev->{CHANGED} } ) { - - # state changed to on - if ( $change eq "auto" ) { - Log3 $hash, 4, - "ROOMMATE " - . $hashName . ": " - . $devName - . ": notify about change to $change"; - - RESIDENTStk_wakeupSet($wakeupDev); - } - else { - Log3 $hash, 5, - "ROOMMATE " - . $hashName . ": " - . $devName - . ": received unhandled notify about change $change"; - } + RESIDENTStk_wakeupSet( $wakeupDev, $change ) + if ( $change ne "off" ); } + + last; } } } @@ -470,14 +441,14 @@ sub ROOMMATE_Set($@) { readingsBulkUpdate( $hash, "lastDurSleep", - ROOMMATE_TimeDiff( + RESIDENTStk_TimeDiff( $datetime, $hash->{READINGS}{lastSleep}{VAL} ) ); readingsBulkUpdate( $hash, "lastDurSleep_cr", - ROOMMATE_TimeDiff( + RESIDENTStk_TimeDiff( $datetime, $hash->{READINGS}{lastSleep}{VAL}, "min" ) ); @@ -547,14 +518,14 @@ sub ROOMMATE_Set($@) { readingsBulkUpdate( $hash, "lastDurAbsence", - ROOMMATE_TimeDiff( + RESIDENTStk_TimeDiff( $datetime, $hash->{READINGS}{lastDeparture}{VAL} ) ); readingsBulkUpdate( $hash, "lastDurAbsence_cr", - ROOMMATE_TimeDiff( + RESIDENTStk_TimeDiff( $datetime, $hash->{READINGS}{lastDeparture}{VAL}, "min" ) @@ -571,14 +542,14 @@ sub ROOMMATE_Set($@) { readingsBulkUpdate( $hash, "lastDurPresence", - ROOMMATE_TimeDiff( + RESIDENTStk_TimeDiff( $datetime, $hash->{READINGS}{lastArrival}{VAL} ) ); readingsBulkUpdate( $hash, "lastDurPresence_cr", - ROOMMATE_TimeDiff( + RESIDENTStk_TimeDiff( $datetime, $hash->{READINGS}{lastArrival}{VAL}, "min" ) @@ -621,7 +592,7 @@ sub ROOMMATE_Set($@) { ROOMMATE_AutoGone($hash); } elsif ( $state eq "absent" ) { - ROOMMATE_RemoveInternalTimer( "AutoGone", $hash ); + RESIDENTStk_RemoveInternalTimer( "AutoGone", $hash ); } } } @@ -761,6 +732,8 @@ sub ROOMMATE_Set($@) { $wakeuptimerName = $name . "_wakeuptimer" . $i; } else { + my $sortby = AttrVal( $name, "sortby", -1 ); + $sortby++; # create new dummy device fhem "define $wakeuptimerName dummy"; @@ -768,36 +741,41 @@ sub ROOMMATE_Set($@) { fhem "attr $wakeuptimerName comment Auto-created by ROOMMATE module for use with RESIDENTS Toolkit"; fhem -"attr $wakeuptimerName devStateIcon OFF:general_aus\@red .*:general_an\@green:OFF"; +"attr $wakeuptimerName devStateIcon OFF:general_aus\@red:reset running:general_an\@blue:stop .*:general_an\@green:nextRun%20OFF"; fhem "attr $wakeuptimerName group " . $attr{$name}{group} if ( defined( $attr{$name}{group} ) ); fhem "attr $wakeuptimerName icon time_timer"; fhem "attr $wakeuptimerName room " . $attr{$name}{room} if ( defined( $attr{$name}{room} ) ); fhem -"attr $wakeuptimerName setList state:OFF,00:00,00:15,00:30,00:45,01:00,01:15,01:30,01:45,02:00,02:15,02:30,02:45,03:00,03:15,03:30,03:45,04:00,04:15,04:30,04:45,05:00,05:15,05:30,05:45,06:00,06:15,06:30,06:45,07:00,07:15,07:30,07:45,08:00,08:15,08:30,08:45,09:00,09:15,09:30,09:45,10:00,10:15,10:30,10:45,11:00,11:15,11:30,11:45,12:00,12:15,12:30,12:45,13:00,13:15,13:30,13:45,14:00,14:15,14:30,14:45,15:00,15:15,15:30,15:45,16:00,16:15,16:30,16:45,17:00,17:15,17:30,17:45,18:00,18:15,18:30,18:45,19:00,19:15,19:30,19:45,20:00,20:15,20:30,20:45,21:00,21:15,21:30,21:45,22:00,22:15,22:30,22:45,23:00,23:15,23:30,23:45"; +"attr $wakeuptimerName setList nextRun:OFF,00:00,00:15,00:30,00:45,01:00,01:15,01:30,01:45,02:00,02:15,02:30,02:45,03:00,03:15,03:30,03:45,04:00,04:15,04:30,04:45,05:00,05:15,05:30,05:45,06:00,06:15,06:30,06:45,07:00,07:15,07:30,07:45,08:00,08:15,08:30,08:45,09:00,09:15,09:30,09:45,10:00,10:15,10:30,10:45,11:00,11:15,11:30,11:45,12:00,12:15,12:30,12:45,13:00,13:15,13:30,13:45,14:00,14:15,14:30,14:45,15:00,15:15,15:30,15:45,16:00,16:15,16:30,16:45,17:00,17:15,17:30,17:45,18:00,18:15,18:30,18:45,19:00,19:15,19:30,19:45,20:00,20:15,20:30,20:45,21:00,21:15,21:30,21:45,22:00,22:15,22:30,22:45,23:00,23:15,23:30,23:45 reset:noArg stop:noArg"; fhem "attr $wakeuptimerName userattr wakeupUserdevice"; + fhem "attr $wakeuptimerName sortby " . $sortby + if ($sortby); fhem "attr $wakeuptimerName wakeupUserdevice $name"; - fhem "attr $wakeuptimerName webCmd state"; + fhem "attr $wakeuptimerName webCmd nextRun"; # register slave device - if ( defined( $attr{$name}{rr_wakeupDevice} ) ) { - fhem "attr $name rr_wakeupDevice " - . $attr{$name}{rr_wakeupDevice} - . ",$wakeuptimerName"; - } - else { + my $wakeupDevice = AttrVal( $name, "rr_wakeupDevice", 0 ); + if ( !$wakeupDevice ) { fhem "attr $name rr_wakeupDevice $wakeuptimerName"; } + elsif ( $wakeupDevice !~ /(.*,?)($wakeuptimerName)(.*,?)/ ) + { + fhem "attr $name rr_wakeupDevice " + . $wakeupDevice + . ",$wakeuptimerName"; + } # trigger first update - fhem "set $wakeuptimerName OFF"; + fhem "set $wakeuptimerName nextRun OFF"; $created = 1; - return -"Dummy $wakeuptimerName and other pending devices created and pre-configured. You may edit Macro_$wakeuptimerName to define your wake-up actions and at_$wakeuptimerName for optional at-device adjustments."; } } + + return +"Dummy $wakeuptimerName and other pending devices created and pre-configured.\nYou may edit Macro_$wakeuptimerName to define your wake-up actions\nand at_$wakeuptimerName for optional at-device adjustments."; } else { return "Invalid 2nd argument, choose one of wakeuptimer "; @@ -824,7 +802,7 @@ sub ROOMMATE_AutoGone($;$) { my $hash = ( $mHash->{HASH} ) ? $mHash->{HASH} : $mHash; my $name = $hash->{NAME}; - ROOMMATE_RemoveInternalTimer( "AutoGone", $hash ); + RESIDENTStk_RemoveInternalTimer( "AutoGone", $hash ); if ( defined( $hash->{READINGS}{state}{VAL} ) && $hash->{READINGS}{state}{VAL} eq "absent" ) @@ -853,8 +831,8 @@ sub ROOMMATE_AutoGone($;$) { else { my $runtime = $timestamp + $timeout * 3600; Log3 $name, 4, "ROOMMATE $name: AutoGone timer scheduled: $runtime"; - ROOMMATE_InternalTimer( "AutoGone", $runtime, "ROOMMATE_AutoGone", - $hash, 1 ); + RESIDENTStk_InternalTimer( "AutoGone", $runtime, + "ROOMMATE_AutoGone", $hash, 1 ); } } @@ -873,7 +851,7 @@ sub ROOMMATE_DurationTimer($;$) { my $durAbsence = "0"; my $durSleep = "0"; - ROOMMATE_RemoveInternalTimer( "DurationTimer", $hash ); + RESIDENTStk_RemoveInternalTimer( "DurationTimer", $hash ); if ( !defined( $attr{$name}{rr_noDuration} ) || $attr{$name}{rr_noDuration} == 0 ) @@ -888,7 +866,7 @@ sub ROOMMATE_DurationTimer($;$) { { $durPresence = $timestampNow - - ROOMMATE_Datetime2Timestamp( + RESIDENTStk_Datetime2Timestamp( $hash->{READINGS}{lastArrival}{VAL} ); } } @@ -902,7 +880,7 @@ sub ROOMMATE_DurationTimer($;$) { { $durAbsence = $timestampNow - - ROOMMATE_Datetime2Timestamp( + RESIDENTStk_Datetime2Timestamp( $hash->{READINGS}{lastDeparture}{VAL} ); } } @@ -916,21 +894,23 @@ sub ROOMMATE_DurationTimer($;$) { { $durSleep = $timestampNow - - ROOMMATE_Datetime2Timestamp( + RESIDENTStk_Datetime2Timestamp( $hash->{READINGS}{lastSleep}{VAL} ); } } my $durPresence_hr = - ( $durPresence > 0 ) ? ROOMMATE_sec2time($durPresence) : "00:00:00"; + ( $durPresence > 0 ) + ? RESIDENTStk_sec2time($durPresence) + : "00:00:00"; my $durPresence_cr = ( $durPresence > 60 ) ? int( $durPresence / 60 + 0.5 ) : 0; my $durAbsence_hr = - ( $durAbsence > 0 ) ? ROOMMATE_sec2time($durAbsence) : "00:00:00"; + ( $durAbsence > 0 ) ? RESIDENTStk_sec2time($durAbsence) : "00:00:00"; my $durAbsence_cr = ( $durAbsence > 60 ) ? int( $durAbsence / 60 + 0.5 ) : 0; my $durSleep_hr = - ( $durSleep > 0 ) ? ROOMMATE_sec2time($durSleep) : "00:00:00"; + ( $durSleep > 0 ) ? RESIDENTStk_sec2time($durSleep) : "00:00:00"; my $durSleep_cr = ( $durSleep > 60 ) ? int( $durSleep / 60 + 0.5 ) : 0; readingsBeginUpdate($hash) if ( !$silent ); @@ -955,114 +935,12 @@ sub ROOMMATE_DurationTimer($;$) { readingsEndUpdate( $hash, 1 ) if ( !$silent ); } - ROOMMATE_InternalTimer( "DurationTimer", $timestampNow + 60, + RESIDENTStk_InternalTimer( "DurationTimer", $timestampNow + 60, "ROOMMATE_DurationTimer", $hash, 1 ); return undef; } -################################### -sub ROOMMATE_TimeDiff($$;$) { - my ( $datetimeNow, $datetimeOld, $format ) = @_; - - my $timestampNow = ROOMMATE_Datetime2Timestamp($datetimeNow); - my $timestampOld = ROOMMATE_Datetime2Timestamp($datetimeOld); - my $timeDiff = $timestampNow - $timestampOld; - - # return seconds - return int( $timeDiff + 0.5 ) if ( defined($format) && $format eq "sec" ); - - # return minutes - return int( $timeDiff / 60 + 0.5 ) - if ( defined($format) && $format eq "min" ); - - # return human readable format - my $hours = ( $timeDiff < 3600 ? 0 : int( $timeDiff / 3600 ) ); - $timeDiff -= ( $hours == 0 ? 0 : ( $hours * 3600 ) ); - my $minutes = ( $timeDiff < 60 ? 0 : int( $timeDiff / 60 ) ); - my $seconds = $timeDiff % 60; - - $hours = "0" . $hours if ( $hours < 10 ); - $minutes = "0" . $minutes if ( $minutes < 10 ); - $seconds = "0" . $seconds if ( $seconds < 10 ); - - return "$hours:$minutes:$seconds"; -} - -################################### -sub ROOMMATE_Datetime2Timestamp($) { - my ($datetime) = @_; - - my ( $date, $time, $y, $m, $d, $hour, $min, $sec, $timestamp ); - - ( $date, $time ) = split( ' ', $datetime ); - ( $y, $m, $d ) = split( '-', $date ); - ( $hour, $min, $sec ) = split( ':', $time ); - $m -= 01; - $timestamp = timelocal( $sec, $min, $hour, $d, $m, $y ); - - return $timestamp; -} - -################################### -sub ROOMMATE_sec2time($) { - my ($sec) = @_; - - # return human readable format - my $hours = ( $sec < 3600 ? 0 : int( $sec / 3600 ) ); - $sec -= ( $hours == 0 ? 0 : ( $hours * 3600 ) ); - my $minutes = ( $sec < 60 ? 0 : int( $sec / 60 ) ); - my $seconds = $sec % 60; - - $hours = "0" . $hours if ( $hours < 10 ); - $minutes = "0" . $minutes if ( $minutes < 10 ); - $seconds = "0" . $seconds if ( $seconds < 10 ); - - return "$hours:$minutes:$seconds"; -} - -################################### -sub ROOMMATE_InternalTimer($$$$$) { - my ( $modifier, $tim, $callback, $hash, $waitIfInitNotDone ) = @_; - - my $mHash; - if ( $modifier eq "" ) { - $mHash = $hash; - } - else { - my $timerName = $hash->{NAME} . "_" . $modifier; - if ( exists( $hash->{TIMER}{$timerName} ) ) { - $mHash = $hash->{TIMER}{$timerName}; - } - else { - $mHash = { - HASH => $hash, - NAME => $hash->{NAME} . "_" . $modifier, - MODIFIER => $modifier - }; - $hash->{TIMER}{$timerName} = $mHash; - } - } - InternalTimer( $tim, $callback, $mHash, $waitIfInitNotDone ); -} - -################################### -sub ROOMMATE_RemoveInternalTimer($$) { - my ( $modifier, $hash ) = @_; - - my $timerName = $hash->{NAME} . "_" . $modifier; - if ( $modifier eq "" ) { - RemoveInternalTimer($hash); - } - else { - my $mHash = $hash->{TIMER}{$timerName}; - if ( defined($mHash) ) { - delete $hash->{TIMER}{$timerName}; - RemoveInternalTimer($mHash); - } - } -} - ################################### sub ROOMMATE_StartInternalTimers($$) { my ($hash) = @_; @@ -1342,6 +1220,9 @@ sub ROOMMATE_StartInternalTimers($$) {
  • state - reflects the current state
  • +
  • + wakeup - becomes '1' while a wake-up program of this resident is being executed +
  • wayhome - depending on current location, it can become '1' if individual is on his/her way back home
  • @@ -1618,6 +1499,9 @@ sub ROOMMATE_StartInternalTimers($$) {
  • state - gibt den aktuellen Status wieder
  • +
  • + wakeup - hat den Wert '1' während ein Weckprogramm dieses Bewohners ausgeführt wird +
  • wayhome - abhängig vom aktullen Aufenthaltsort, kann der Wert '1' werden, wenn die Person auf dem weg zurück nach Hause ist
  • diff --git a/fhem/FHEM/RESIDENTStk.pm b/fhem/FHEM/RESIDENTStk.pm index 32c3a2d2c..1874ebda9 100644 --- a/fhem/FHEM/RESIDENTStk.pm +++ b/fhem/FHEM/RESIDENTStk.pm @@ -23,7 +23,7 @@ # along with fhem. If not, see . # # -# Version: 1.0.0 +# Version: 1.0.1 # # Version History: # - 1.0.0 - 2015-03-11 @@ -32,58 +32,158 @@ ############################################################################## ##################################### +# PRE-DEFINITION: wakeuptimer +#------------------------------------ +# + +# # Enslave DUMMY device to be used for alarm clock # -sub RESIDENTStk_wakeupSet($;$) { - my ( $NAME, $VALUE ) = @_; - my $userattr = AttrVal( $NAME, "userattr", 0 ); - my $autosave = AttrVal( $NAME, "wakeupAutosave", 0 ); - my $wakeupDefaultTime = AttrVal( $NAME, "wakeupDefaultTime", 0 ); - my $wakeupMacro = AttrVal( $NAME, "wakeupMacro", 0 ); - my $wakeupAtdevice = AttrVal( $NAME, "wakeupAtdevice", 0 ); - my $wakeupOffset = AttrVal( $NAME, "wakeupOffset", "0" ); - my $room = AttrVal( $NAME, "room", 0 ); - my $macroName = "Macro_" . $NAME; - my $atName = "at_" . $NAME; +sub RESIDENTStk_wakeupSet($$) { + my ( $NAME, $notifyValue ) = @_; + my $VALUE; - if ( !$VALUE ) { - if ($wakeupDefaultTime) { - Log3 $NAME, 4, - "RESIDENTStk $NAME: Resetting based on wakeupDefaultTime"; - fhem - "set $NAME:FILTER=state!=$wakeupDefaultTime $wakeupDefaultTime"; - } + # filter non-registered notifies + my @notify = split / /, $notifyValue; + if ( lc( $notify[0] ) !~ /off|nextrun|stop|reset|auto|([0-9]{2}:[0-9]{2})/ ) + { + Log3 $NAME, 5, + "RESIDENTStk $NAME: received unspecified notify '" + . $notify[0] + . "' - nothing to do"; return; } + elsif ( lc( $notify[0] ) eq "nextrun" ) { + return if ( !defined( $notify[1] ) ); + $VALUE = $notify[1]; + } + else { + $VALUE = $notify[0]; + } + + my $wakeupMacro = AttrVal( $NAME, "wakeupMacro", 0 ); + my $wakeupDefaultTime = AttrVal( $NAME, "wakeupDefaultTime", 0 ); + my $wakeupAtdevice = AttrVal( $NAME, "wakeupAtdevice", 0 ); + my $wakeupUserdevice = AttrVal( $NAME, "wakeupUserdevice", 0 ); + my $wakeupDays = AttrVal( $NAME, "wakeupDays", 0 ); + my $wakeupResetdays = AttrVal( $NAME, "wakeupResetdays", 0 ); + my $wakeupOffset = AttrVal( $NAME, "wakeupOffset", 0 ); + my $wakeupResetSwitcher = AttrVal( $NAME, "wakeupResetSwitcher", 0 ); + my $wakeupUserdevice = AttrVal( $NAME, "wakeupUserdevice", 0 ); + my $room = AttrVal( $NAME, "room", 0 ); + my $userattr = AttrVal( $NAME, "userattr", 0 ); + my $lastRun = ReadingsVal( $NAME, "lastRun", "07:00" ); + my $nextRun = ReadingsVal( $NAME, "nextRun", "07:00" ); + my $running = ReadingsVal( $NAME, "running", 0 ); + my $macroName = "Macro_" . $NAME; + my $atName = "at_" . $NAME; # check for required userattr attribute my $userattributes = -"wakeupOffset:slider,0,1,120 wakeupDefaultTime:time wakeupMacro wakeupUserdevice wakeupAtdevice wakeupResetSwitcher wakeupResetdays:multiple-strict,0,1,2,3,4,5,6 wakeupDays:multiple-strict,0,1,2,3,4,5,6 wakeupAutosave:1,0"; +"wakeupOffset:slider,0,1,120 wakeupDefaultTime:OFF,00:00,00:15,00:30,00:45,01:00,01:15,01:30,01:45,02:00,02:15,02:30,02:45,03:00,03:15,03:30,03:45,04:00,04:15,04:30,04:45,05:00,05:15,05:30,05:45,06:00,06:15,06:30,06:45,07:00,07:15,07:30,07:45,08:00,08:15,08:30,08:45,09:00,09:15,09:30,09:45,10:00,10:15,10:30,10:45,11:00,11:15,11:30,11:45,12:00,12:15,12:30,12:45,13:00,13:15,13:30,13:45,14:00,14:15,14:30,14:45,15:00,15:15,15:30,15:45,16:00,16:15,16:30,16:45,17:00,17:15,17:30,17:45,18:00,18:15,18:30,18:45,19:00,19:15,19:30,19:45,20:00,20:15,20:30,20:45,21:00,21:15,21:30,21:45,22:00,22:15,22:30,22:45,23:00,23:15,23:30,23:45 wakeupMacro wakeupUserdevice wakeupAtdevice wakeupResetSwitcher wakeupResetdays:multiple-strict,0,1,2,3,4,5,6 wakeupDays:multiple-strict,0,1,2,3,4,5,6"; if ( !$userattr || $userattr ne $userattributes ) { - Log3 $NAME, 3, + Log3 $NAME, 4, "RESIDENTStk $NAME: adjusting dummy device for required attribute userattr"; fhem "attr $NAME userattr $userattributes"; } # check for required userdevice attribute - if ( !AttrVal( $NAME, "wakeupUserdevice", 0 ) ) { + if ( !$wakeupUserdevice ) { Log3 $NAME, 3, -"RESIDENTStk $NAME: WARNING - set attribute wakeupUserdevice before running wakeup function"; +"RESIDENTStk $NAME: WARNING - set attribute wakeupUserdevice before running wakeup function!"; + } + elsif ( !defined( $defs{$wakeupUserdevice} ) ) { + Log3 $NAME, 3, +"RESIDENTStk $NAME: WARNING - user device $wakeupUserdevice does not exist!"; + } + elsif ($defs{$wakeupUserdevice}{TYPE} ne "RESIDENTS" + && $defs{$wakeupUserdevice}{TYPE} ne "ROOMMATE" + && $defs{$wakeupUserdevice}{TYPE} ne "GUEST" ) + { + Log3 $NAME, 3, +"RESIDENTStk $NAME: WARNING - defined user device '$wakeupUserdevice' is not a RESIDENTS, ROOMMATE or GUEST device!"; } # check for required wakeupMacro attribute if ( !$wakeupMacro ) { - Log3 $NAME, 3, + Log3 $NAME, 4, "RESIDENTStk $NAME: adjusting dummy device for required attribute wakeupMacro"; fhem "attr $NAME wakeupMacro $macroName"; $wakeupMacro = $macroName; } - - # check for existing macro notify device if ( !defined( $defs{$wakeupMacro} ) ) { + my $wakeUpMacroTemplate = "{\ +#\ +# This is an example wake-up program running within a period of 30 minutes:\ +# - drive shutters upwards slowly\ +# - light up a HUE bulb from 2000K to 6500K\ +# - have some voice notifications via SONOS\ +# - have some wake-up chill music via SONOS during program run\ +#\ +# Available wake-up variables:\ +# 1. \$EVTPART0 -> start or stop\ +# 2. \$EVTPART1 -> target wake-up time\ +# 2. \$EVTPART2 -> wake-up begin offset time\ +#\ +\ +#------------------------------------------------------------------------------------\ +# DELETE TEMP. AT-COMMANDS POTENTIALLY CREATED EARLIER BY THIS SCRIPT\ +# Executed for start to cleanup in case this wake-up automation is re-started.\ +# Executed for stop to cleanup in case the user ends this automation earlier.\ +#\ +for (my \$i=1;; \$i <= 10;; \$i++) {\ + if (defined(\$defs{\"atTmp_\".\$i.\"_\".\$NAME})) {\ + fhem \"delete atTmp_\".\$i.\"_\".\$NAME;;\ + }\ +}\ +\ +#------------------------------------------------------------------------------------\ +# BEGIN WAKE-UP PROGRAM\ +# Run first automation commands and create temp. at-devices for lagging actions.\ +#\ +if (\$EVTPART0 eq \"start\") {\ + Log3 \$NAME, 3, \"\$NAME: Wake-up program started for \".\$EVTPART1;;\ +\ + fhem \"set BR_FloorLamp pct 1 : ct 2000 : transitiontime 0;; set BR_FloorLamp pct 90 : ct 5600 : transitiontime 1770\";;\ +\ + fhem \"define atTmp_1_\$NAME at +00:10:00 set BR_Shutter pct 20\";;\ + fhem \"define atTmp_2_\$NAME at +00:20:00 set BR_Shutter pct 40\";;\ + fhem \"define atTmp_4_\$NAME at +00:30:00 set Sonos_Bedroom Speak 33 de |Hint| Es ist \".\$EVTPART1.\" Uhr, Zeit zum aufstehen!;;;; set BR_FloorLamp pct 100 60;;;; sleep 10;;;; set BR_Shutter pct 60;;;; set Sonos_Bedroom volume 10 10\";;\ +\ + # Working days only (Mon-Fri except bank holidays): do enforced wake-up actions\ + if (!\$we) {\ + Log (4, \"\$NAME: planning enforced wake-up\");;\ + fhem \"define atTmp_3_\$NAME at +00:25:00 set Sonos_Bedroom volume 2;;;; set Sonos_Bedroom Shuffle 1;;;; set Sonos_Bedroom StartFavourite Morning%%20Sounds;;;; sleep 4;;;; set Sonos_Bedroom volume 8 290\";;\ + }\ +}\ +\ +#------------------------------------------------------------------------------------\ +# END WAKE-UP PROGRAM (OPTIONAL)\ +# Put some post wake-up tasks here like reminders after the actual wake-up period.\ +#\ +if (\$EVTPART0 eq \"stop\") {\ + Log3 \$NAME, 3, \"\$NAME: Wake-up program ended for \".\$EVTPART1;;\ +\ + # Working days only (Mon-Fri except bank holidays): after only a small additional nap,\ + # get you out of bed :-)\ + # An additional notify for user state 'awoken' may take further actions\ + # and change to state 'home' afterwards.\ + if (!\$we) {\ + fhem \"define atTmp_5_\$NAME at +00:05:00 set rr_Julian:FILTER=STATE=asleep awoken\";;\ +\ + # At weekend and bank holidays: be jentle and just set user state to 'home' after some\ + # additional long nap time\ + } else {\ + fhem \"define atTmp_5_\$NAME at +01:30:00 set rr_Julian:FILTER=STATE=asleep home\";;\ + }\ +}\ +\ +}\ +"; + Log3 $NAME, 3, "RESIDENTStk $NAME: new notify macro device $wakeupMacro created"; - fhem "define $wakeupMacro notify $wakeupMacro {}"; + fhem "define $wakeupMacro notify $wakeupMacro $wakeUpMacroTemplate"; fhem "attr $wakeupMacro comment Macro auto-created by RESIDENTS Toolkit"; if ($room) { fhem "attr $wakeupMacro room $room" } @@ -95,66 +195,140 @@ sub RESIDENTStk_wakeupSet($;$) { # check for required wakeupAtdevice attribute if ( !$wakeupAtdevice ) { - Log3 $NAME, 3, + Log3 $NAME, 4, "RESIDENTStk $NAME: adjusting dummy device for required attribute wakeupAtdevice"; fhem "attr $NAME wakeupAtdevice $atName"; $wakeupAtdevice = $atName; } - - # check for existing at device if ( !defined( $defs{$wakeupAtdevice} ) ) { Log3 $NAME, 3, - "RESIDENTStk $NAME: new at device $wakeupAtdevice created"; + "RESIDENTStk $NAME: new at-device $wakeupAtdevice created"; fhem -"define $wakeupAtdevice at *08:00 { RESIDENTStk_wakeupRun(\"$NAME\") }"; +"define $wakeupAtdevice at *{RESIDENTStk_wakeupGetBegin(\"$NAME\")} { RESIDENTStk_wakeupRun(\"$NAME\") }"; fhem "attr $wakeupAtdevice comment Auto-created by RESIDENTS Toolkit"; if ($room) { fhem "attr $wakeupAtdevice room $room" } } + elsif ( $defs{$wakeupAtdevice}{TYPE} ne "at" ) { + Log3 $NAME, 3, +"RESIDENTStk $NAME: WARNING - defined at-device '$wakeupAtdevice' is not an at-device!"; + } -# Reset at device if wake-up timer was disabled and wakeupDefaultTime is present - if ( $VALUE eq "OFF" ) { - Log3 $NAME, 4, "RESIDENTStk $NAME: Wake-up timer disabled"; + # stop + # + if ( $VALUE eq "stop" && $running ) { + Log3 $NAME, 4, "RESIDENTStk $NAME: stopping wake-up program"; + fhem "setreading $NAME running 0"; + fhem "set $NAME nextRun $nextRun"; + + # trigger macro again so it may clean up it's stuff. + # use $EVTPART1 to check + if ( !$wakeupMacro ) { + Log3 $NAME, 2, "RESIDENTStk $NAME: missing attribute wakeupMacro"; + } + elsif ( !defined( $defs{$wakeupMacro} ) ) { + Log3 $NAME, 2, +"RESIDENTStk $NAME: notify macro $wakeupMacro not found - no wakeup actions defined!"; + } + elsif ( $defs{$wakeupMacro}{TYPE} ne "notify" ) { + Log3 $NAME, 2, + "RESIDENTStk $NAME: device $wakeupMacro is not of type notify"; + } + else { + if ( defined( $notify[1] ) ) { + Log3 $NAME, 4, +"RESIDENTStk $NAME: trigger $wakeupMacro (running=0,forced-stop=0)"; + fhem "trigger $wakeupMacro stop $lastRun $wakeupOffset"; + } + else { + Log3 $NAME, 4, +"RESIDENTStk $NAME: trigger $wakeupMacro (running=0,forced-stop=1)"; + fhem "trigger $wakeupMacro forced-stop $lastRun $wakeupOffset"; + } + fhem "setreading $wakeupUserdevice:FILTER=wakeup=1 wakeup 0"; + + my $wakeupStopAtdevice = $wakeupAtdevice . "_stop"; + if ( defined( $defs{$wakeupStopAtdevice} ) ) { + fhem "delete $wakeupStopAtdevice"; + } + } + return; + } + + # auto or reset + # + elsif ( $VALUE eq "auto" || $VALUE eq "reset" ) { + my $resetTime = ReadingsVal( $NAME, "lastRun", 0 ); if ($wakeupDefaultTime) { - $VALUE = $wakeupDefaultTime; + $resetTime = $wakeupDefaultTime; + } + + if ( $resetTime + && !( $VALUE eq "auto" && lc($resetTime) eq "off" ) ) + { + fhem "set $NAME:FILTER=state!=$resetTime nextRun $resetTime"; + } + elsif ( $VALUE eq "reset" ) { Log3 $NAME, 4, -"RESIDENTStk $NAME: Wake-up timer disabled and triggered at device reset"; - } - else { - Log3 $NAME, 4, "RESIDENTStk $NAME: Wake-up timer disabled"; +"RESIDENTStk $NAME: no default value specified in attribute wakeupDefaultTime, just keeping setting OFF"; + fhem "set $NAME:FILTER=state!=OFF nextRun OFF"; } + + return; } - # Recalculate new wake-up value - if ( $VALUE ne "OFF" ) { - my @time = split /:/, $VALUE; - my $time_sec = $time[0] * 3600 + $time[1] * 60; - my $begin = $time_sec - $wakeupOffset * 60; - my $hour = int( $begin / 3600 ); - my $leftover = $begin % 3600; - my $min = int( $leftover / 60 ); - if ( $time_sec < 1800 && $wakeupOffset > 0 ) { $hour = 23 } + # set new wakeup value + elsif (( lc($VALUE) eq "off" || $VALUE =~ /^([0-9]{2}:[0-9]{2})$/ ) + && defined( $defs{$wakeupAtdevice} ) + && $defs{$wakeupAtdevice}{TYPE} eq "at" ) + { + Log3 $NAME, 4, + "RESIDENTStk $NAME: New wake-up time: $VALUE"; - if ( $defs{$wakeupAtdevice}{TYPE} ne "at" ) { - Log3 $NAME, 3, -"RESIDENTStk $NAME: ERROR - defined device '$wakeupAtdevice' is not an at device!"; - } - else { - fhem "modify $wakeupAtdevice *" - . sprintf( "%02d:%02d", $hour, $min ); + readingsBeginUpdate( $defs{$NAME} ); + readingsBulkUpdate( $defs{$NAME}, "state", $VALUE ) + if ( ReadingsVal( $NAME, "state", 0 ) ne $VALUE ); + readingsBulkUpdate( $defs{$NAME}, "nextRun", $VALUE ) + if ( ReadingsVal( $NAME, "nextRun", 0 ) ne $VALUE ); + readingsEndUpdate( $defs{$NAME}, 1 ); - Log3 $NAME, 4, - "RESIDENTStk $NAME($wakeupAtdevice): Wake-up begin scheduled for " - . sprintf( "%02d:%02d", $hour, $min ); + fhem +"set $wakeupAtdevice modifyTimeSpec {RESIDENTStk_wakeupGetBegin(\"$NAME\")}"; + + if ( !$running ) { + fhem "setreading $wakeupUserdevice:FILTER=wakeup!=0 wakeup 0"; } } - # autosave - if ($autosave) { fhem "save" } - return undef; } -##################################### +# +# Get current wakeup begin +# +sub RESIDENTStk_wakeupGetBegin($) { + my ($NAME) = @_; + my $wakeupDefaultTime = AttrVal( $NAME, "wakeupDefaultTime", "07:00" ); + my $wakeupTime = ReadingsVal( $NAME, "nextRun", $wakeupDefaultTime ); + my $wakeupOffset = AttrVal( $NAME, "wakeupOffset", 0 ); + my $return; + + # Recalculate new wake-up value + if ( $wakeupTime =~ /^([0-9]{2}:[0-9]{2})$/ ) { + my @time = split /:/, $wakeupTime; + + my $seconds = $time[0] * 3600 + $time[1] * 60 - $wakeupOffset * 60; + if ( $seconds < 0 ) { $seconds = 86400 + $seconds } + + $return = RESIDENTStk_sec2time($seconds); + } + else { + $return = "07:00"; + } + + return $return; +} + +# # Use DUMMY device to run wakup event # sub RESIDENTStk_wakeupRun($) { @@ -162,10 +336,18 @@ sub RESIDENTStk_wakeupRun($) { my $wakeupMacro = AttrVal( $NAME, "wakeupMacro", 0 ); my $wakeupDefaultTime = AttrVal( $NAME, "wakeupDefaultTime", 0 ); + my $wakeupAtdevice = AttrVal( $NAME, "wakeupAtdevice", 0 ); my $wakeupUserdevice = AttrVal( $NAME, "wakeupUserdevice", 0 ); my $wakeupDays = AttrVal( $NAME, "wakeupDays", 0 ); my $wakeupResetdays = AttrVal( $NAME, "wakeupResetdays", 0 ); + my $wakeupOffset = AttrVal( $NAME, "wakeupOffset", 0 ); my $wakeupResetSwitcher = AttrVal( $NAME, "wakeupResetSwitcher", 0 ); + my $lastRun = ReadingsVal( $NAME, "lastRun", "07:00" ); + my $nextRun = ReadingsVal( $NAME, "nextRun", "07:00" ); + my $running = ReadingsVal( $NAME, "running", 0 ); + my $wakeupUserdeviceWakeup = ReadingsVal( $wakeupUserdevice, "wakeup", 0 ); + my $room = AttrVal( $NAME, "room", 0 ); + my $running = 0; my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime(time); @@ -181,58 +363,74 @@ sub RESIDENTStk_wakeupRun($) { } if ( !defined( $defs{$NAME} ) ) { - Log3 $NAME, 3, "RESIDENTStk $NAME: Non existing device"; return "$NAME: Non existing device"; } - elsif ( ReadingsVal( $NAME, "state", "OFF" ) eq "OFF" ) { + elsif ( lc($nextRun) eq "off" ) { Log3 $NAME, 4, - "RESIDENTStk $NAME: alarm set to OFF - not running any action"; +"RESIDENTStk $NAME: alarm set to OFF - not triggering wake-up program"; } elsif ( !$wakeupUserdevice ) { - Log3 $NAME, 4, "RESIDENTStk $NAME: missing attribute wakeupUserdevice"; return "$NAME: missing attribute wakeupUserdevice"; } elsif ( !defined( $defs{$wakeupUserdevice} ) ) { - Log3 $NAME, 4, - "RESIDENTStk $NAME: Non existing wakeupUserdevice $wakeupUserdevice"; return "$NAME: Non existing wakeupUserdevice $wakeupUserdevice"; } elsif ($defs{$wakeupUserdevice}{TYPE} ne "ROOMMATE" && $defs{$wakeupUserdevice}{TYPE} ne "GUEST" ) { - Log3 $NAME, 4, -"RESIDENTStk $NAME: device $wakeupUserdevice is not of type ROOMMATE or GUEST"; return "$NAME: device $wakeupUserdevice is not of type ROOMMATE or GUEST"; } elsif ( $defs{$wakeupUserdevice}{TYPE} eq "GUEST" && ReadingsVal( $wakeupUserdevice, "state", "" ) eq "none" ) { - fhem "set $NAME OFF"; + fhem "set $NAME nextRun OFF"; return; } elsif ($wday ~~ @days && ReadingsVal( $wakeupUserdevice, "state", "" ) ne "absent" - && ReadingsVal( $wakeupUserdevice, "state", "" ) ne "gone" ) + && ReadingsVal( $wakeupUserdevice, "state", "" ) ne "gone" + && ReadingsVal( $wakeupUserdevice, "state", "" ) ne "gotosleep" + && ReadingsVal( $wakeupUserdevice, "state", "" ) ne "awoken" ) { if ( !$wakeupMacro ) { - Log3 $NAME, 2, "RESIDENTStk $NAME: missing attribute wakeupMacro"; return "$NAME: missing attribute wakeupMacro"; } elsif ( !defined( $defs{$wakeupMacro} ) ) { - Log3 $NAME, 2, -"RESIDENTStk $NAME: notify macro $wakeupMacro not found - no wakeup actions defined!"; return "$NAME: notify macro $wakeupMacro not found - no wakeup actions defined!"; } elsif ( $defs{$wakeupMacro}{TYPE} ne "notify" ) { - Log3 $NAME, 2, - "RESIDENTStk $NAME: device $wakeupMacro is not of type notify"; return "$NAME: device $wakeupMacro is not of type notify"; } + elsif ($wakeupUserdeviceWakeup) { + Log3 $NAME, 3, + "RESIDENTStk $NAME: Another wake-up program is already being executed, won't trigger $wakeupMacro"; + } else { - Log3 $NAME, 4, "RESIDENTStk $NAME: trigger $wakeupMacro"; - fhem "trigger $wakeupMacro"; + Log3 $NAME, 4, + "RESIDENTStk $NAME: trigger $wakeupMacro (running=1)"; + fhem "trigger $wakeupMacro start $nextRun $wakeupOffset"; + fhem "setreading $wakeupUserdevice wakeup 1"; + fhem "setreading $NAME lastRun $nextRun"; + + if ( $wakeupOffset > 0 ) { + my $wakeupStopAtdevice = $wakeupAtdevice . "_stop"; + + if ( defined( $defs{$wakeupStopAtdevice} ) ) { + fhem "delete $wakeupStopAtdevice"; + } + + Log3 $NAME, 4, +"RESIDENTStk $NAME: created at-device $wakeupStopAtdevice to stop wake-up program in $wakeupOffset minutes"; + fhem "define $wakeupStopAtdevice at +" + . RESIDENTStk_sec2time( $wakeupOffset * 60 + 1 ) + . " set $NAME:FILTER=running=1 stop triggerpost"; + fhem +"attr $wakeupStopAtdevice comment Auto-created by RESIDENTS Toolkit"; + } + + $running = 1; } } @@ -248,19 +446,34 @@ sub RESIDENTStk_wakeupRun($) { if ( $wakeupDefaultTime && $wday ~~ @rdays && $doReset ) { Log3 $NAME, 4, "RESIDENTStk $NAME: Resetting based on wakeupDefaultTime"; - fhem "set $NAME:FILTER=state!=$wakeupDefaultTime $wakeupDefaultTime"; + fhem +"set $NAME:FILTER=state!=$wakeupDefaultTime nextRun $wakeupDefaultTime"; + } + + if ( $running && $wakeupOffset > 0 ) { + readingsBeginUpdate( $defs{$NAME} ); + readingsBulkUpdate( $defs{$NAME}, "running", "1" ) + if ( ReadingsVal( $NAME, "running", 0 ) ne "1" ); + readingsBulkUpdate( $defs{$NAME}, "state", "running" ) + if ( ReadingsVal( $NAME, "state", 0 ) ne "running" ); + readingsEndUpdate( $defs{$NAME}, 1 ); } return undef; } ##################################### +# FHEM CODE INJECTION +#------------------------------------ +# + +# # AttFn for enslaved dummy devices # sub RESIDENTStk_AttrFnDummy(@) { my ( $cmd, $name, $aName, $aVal ) = @_; - # set attribute + # set attribute if ( $cmd eq "set" ) { # wakeupResetSwitcher @@ -297,24 +510,103 @@ sub RESIDENTStk_AttrFnDummy(@) { } } - } - - # del attribute - elsif ( $cmd eq "del" ) { - - # wakeupResetSwitcher - if ( $aName eq "wakeupResetSwitcher" ) { - if ( defined( $defs{$aVal} ) && $defs{$aVal}{TYPE} eq "dummy" ) { - fhem "delete $aVal"; - - Log3 $name, 3, - "RESIDENTStk $name: slave dummy device $aVal deleted"; - } - } - } return undef; } +##################################### +# GENERAL FUNCTIONS USED IN RESIDENTS, ROOMMATE, GUEST +#------------------------------------ +# + +sub RESIDENTStk_TimeDiff ($$;$) { + my ( $datetimeNow, $datetimeOld, $format ) = @_; + + my $timestampNow = RESIDENTStk_Datetime2Timestamp($datetimeNow); + my $timestampOld = RESIDENTStk_Datetime2Timestamp($datetimeOld); + my $timeDiff = $timestampNow - $timestampOld; + + # return seconds + return int( $timeDiff + 0.5 ) + if ( defined($format) && $format eq "sec" ); + + # return minutes + return int( $timeDiff / 60 + 0.5 ) + if ( defined($format) && $format eq "min" ); + + # return human readable format + return RESIDENTStk_sec2time( int( $timeDiff + 0.5 ) ); +} + +sub RESIDENTStk_Datetime2Timestamp($) { + my ($datetime) = @_; + + my ( $date, $time, $y, $m, $d, $hour, $min, $sec, $timestamp ); + + ( $date, $time ) = split( ' ', $datetime ); + ( $y, $m, $d ) = split( '-', $date ); + ( $hour, $min, $sec ) = split( ':', $time ); + $m -= 01; + $timestamp = timelocal( $sec, $min, $hour, $d, $m, $y ); + + return $timestamp; +} + +sub RESIDENTStk_sec2time($) { + my ($sec) = @_; + + # return human readable format + my $hours = ( abs($sec) < 3600 ? 0 : int( abs($sec) / 3600 ) ); + $sec -= ( $hours == 0 ? 0 : ( $hours * 3600 ) ); + my $minutes = ( abs($sec) < 60 ? 0 : int( abs($sec) / 60 ) ); + my $seconds = abs($sec) % 60; + + $hours = "0" . $hours if ( $hours < 10 ); + $minutes = "0" . $minutes if ( $minutes < 10 ); + $seconds = "0" . $seconds if ( $seconds < 10 ); + + return "$hours:$minutes:$seconds"; +} + +sub RESIDENTStk_InternalTimer($$$$$) { + my ( $modifier, $tim, $callback, $hash, $waitIfInitNotDone ) = @_; + + my $mHash; + if ( $modifier eq "" ) { + $mHash = $hash; + } + else { + my $timerName = $hash->{NAME} . "_" . $modifier; + if ( exists( $hash->{TIMER}{$timerName} ) ) { + $mHash = $hash->{TIMER}{$timerName}; + } + else { + $mHash = { + HASH => $hash, + NAME => $hash->{NAME} . "_" . $modifier, + MODIFIER => $modifier + }; + $hash->{TIMER}{$timerName} = $mHash; + } + } + InternalTimer( $tim, $callback, $mHash, $waitIfInitNotDone ); +} + +sub RESIDENTStk_RemoveInternalTimer($$) { + my ( $modifier, $hash ) = @_; + + my $timerName = $hash->{NAME} . "_" . $modifier; + if ( $modifier eq "" ) { + RemoveInternalTimer($hash); + } + else { + my $mHash = $hash->{TIMER}{$timerName}; + if ( defined($mHash) ) { + delete $hash->{TIMER}{$timerName}; + RemoveInternalTimer($mHash); + } + } +} + 1;