# $Id$ ############################################################################## # # 10_RESIDENTS.pm # An FHEM Perl module to ease resident administration. # # Copyright by Julian Pawlowski # e-mail: julian.pawlowski at gmail.com # # This file is part of fhem. # # Fhem is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # Fhem is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with fhem. If not, see . # ############################################################################## package main; use strict; use warnings; use Time::Local; use Data::Dumper; require RESIDENTStk; sub RESIDENTS_Set($@); sub RESIDENTS_Define($$); sub RESIDENTS_Notify($$); sub RESIDENTS_Attr(@); sub RESIDENTS_Undefine($$); ################################### sub RESIDENTS_Initialize($) { my ($hash) = @_; Log3 $hash, 5, "RESIDENTS_Initialize: Entering"; $hash->{SetFn} = "RESIDENTS_Set"; $hash->{DefFn} = "RESIDENTS_Define"; $hash->{NotifyFn} = "RESIDENTS_Notify"; $hash->{AttrFn} = "RESIDENTS_Attr"; $hash->{UndefFn} = "RESIDENTS_Undefine"; $hash->{AttrList} = "disable:1,0 rgr_showAllStates:0,1 rgr_states:multiple-strict,home,gotosleep,asleep,awoken,absent,gone rgr_lang:EN,DE rgr_noDuration:0,1 rgr_wakeupDevice " . $readingFnAttributes; } ################################### sub RESIDENTS_Define($$) { my ( $hash, $def ) = @_; my $name = $hash->{NAME}; my $name_attr; Log3 $name, 5, "RESIDENTS $name: called function RESIDENTS_Define()"; RESIDENTStk_findResidentSlaves($hash); # set default settings on first define if ( $init_done && !defined( $hash->{OLDDEF} ) ) { RESIDENTS_Attr( "init", $name, "rgr_lang" ); $attr{$name}{icon} = "control_building_filled"; $attr{$name}{room} = "Residents"; $attr{$name}{webCmd} = "state"; } # run timers InternalTimer( gettimeofday() + 15, "RESIDENTS_StartInternalTimers", $hash, 0 ); # Injecting AttrFn for use with RESIDENTS Toolkit if ( !defined( $modules{dummy}{AttrFn} ) ) { $modules{dummy}{AttrFn} = "RESIDENTStk_AttrFnDummy"; } elsif ( $modules{dummy}{AttrFn} ne "RESIDENTStk_AttrFnDummy" ) { Log3 $name, 5, "RESIDENTStk $name: concurrent AttrFn already defined for dummy module. Some attribute based functions like auto-creations will not be available."; } return undef; } ################################### sub RESIDENTS_Attr(@) { my ( $cmd, $name, $attribute, $value ) = @_; my $hash = $defs{$name}; my $prefix = "rgr_"; return unless ($init_done); Log3 $name, 5, "RESIDENTS $name: called function RESIDENTS_Attr()"; if ( $attribute eq "disable" ) { if ( $value and $value == 1 ) { $hash->{STATE} = "disabled"; RESIDENTS_StopInternalTimers($hash); } elsif ( $cmd eq "del" or !$value ) { evalStateFormat($hash); RESIDENTS_StartInternalTimers( $hash, 1 ); } } elsif ( $attribute eq $prefix . "noDuration" ) { if ($value) { delete $hash->{DURATIONTIMER} if ( $hash->{DURATIONTIMER} ); RESIDENTStk_RemoveInternalTimer( "DurationTimer", $hash ); } elsif ( !$value ) { RESIDENTS_DurationTimer($hash); } } elsif ( $attribute eq $prefix . "lang" ) { my $lang = $cmd eq "set" ? uc($value) : AttrVal( "global", "language", "EN" ); # for initial define, ensure fallback to EN $lang = "EN" if ( $cmd eq "init" && $lang !~ /^EN|DE$/i ); if ( $lang eq "DE" ) { $attr{$name}{alias} = "Bewohner" if ( !defined( $attr{$name}{alias} ) || $attr{$name}{alias} eq "Residents" ); $attr{$name}{group} = "Haus Status" if ( !defined( $attr{$name}{group} ) || $attr{$name}{group} eq "Home State" ); $attr{$name}{devStateIcon} = '.*zuhause:status_available:absent .*anwesend:status_available:absent .*abwesend:status_away_1:home .*verreist:status_standby:home .*keine:control_building_empty .*bettfertig:status_night:asleep .*schlaeft:status_night:awoken .*schläft:status_night:awoken .*aufgestanden:status_available:home .*:user_unknown:home'; $attr{$name}{eventMap} = "home:zuhause absent:abwesend gone:verreist none:keine gotosleep:bettfertig asleep:schläft awoken:aufgestanden"; $attr{$name}{widgetOverride} = "state:zuhause,bettfertig,schläft,aufgestanden,abwesend,verreist"; } elsif ( $lang eq "EN" ) { $attr{$name}{alias} = "Residents" if ( !defined( $attr{$name}{alias} ) || $attr{$name}{alias} eq "Bewohner" ); $attr{$name}{group} = "Home State" if ( !defined( $attr{$name}{group} ) || $attr{$name}{group} eq "Haus Status" ); $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 .*:user_unknown:home'; delete $attr{$name}{eventMap} if ( defined( $attr{$name}{eventMap} ) ); delete $attr{$name}{widgetOverride} if ( defined( $attr{$name}{widgetOverride} ) ); } else { return "Unsupported language $lang"; } evalStateFormat($hash); } return if ( IsDisabled($name) ); return; } ################################### sub RESIDENTS_Undefine($$) { my ( $hash, $name ) = @_; RESIDENTS_StopInternalTimers($hash); RESIDENTStk_findResidentSlaves($hash); # delete child roommates if ( defined( $hash->{ROOMMATES} ) && $hash->{ROOMMATES} ne "" ) { my @registeredRoommates = split( /,/, $hash->{ROOMMATES} ); foreach my $child (@registeredRoommates) { fhem( "delete " . $child ); Log3 $name, 3, "RESIDENTS $name: deleted device $child"; } } # delete child guests if ( defined( $hash->{GUESTS} ) && $hash->{GUESTS} ne "" ) { my @registeredGuests = split( /,/, $hash->{GUESTS} ); foreach my $child (@registeredGuests) { fhem( "delete " . $child ); Log3 $name, 3, "RESIDENTS $name: deleted device $child"; } } return undef; } ################################### sub RESIDENTS_Notify($$) { my ( $hash, $dev ) = @_; my $devName = $dev->{NAME}; my $hashName = $hash->{NAME}; return unless ( $devName ne $hashName ); # only foreign events return if ( IsDisabled($hashName) or IsDisabled($devName) ); return unless ( IsDevice( $devName, "ROOMMATE|GUEST|dummy" ) ); my @registeredRoommates = split( /,/, $hash->{ROOMMATES} ) if ( defined( $hash->{ROOMMATES} ) && $hash->{ROOMMATES} ne "" ); my @registeredGuests = split( /,/, $hash->{GUESTS} ) if ( defined( $hash->{GUESTS} ) && $hash->{GUESTS} ne "" ); my @registeredWakeupdevs = split( /,/, $attr{$hashName}{rgr_wakeupDevice} ) if ( defined( $attr{$hashName}{rgr_wakeupDevice} ) && $attr{$hashName}{rgr_wakeupDevice} ne "" ); # process only registered ROOMMATE or GUEST devices if ( ( @registeredRoommates && grep { /^$devName$/ } @registeredRoommates ) || ( @registeredGuests && grep { /^$devName$/ } @registeredGuests ) ) { return if ( !$dev->{CHANGED} ); # Some previous notify deleted the array. readingsBeginUpdate($hash); foreach my $change ( @{ $dev->{CHANGED} } ) { Log3 $hash, 5, "RESIDENTS " . $hashName . ": processing change $change"; # state changed if ( $change !~ /:/ || $change =~ /wayhome:/ || $change =~ /wakeup:/ ) { Log3 $hash, 4, "RESIDENTS " . $hashName . ": " . $devName . ": notify about change to $change"; RESIDENTS_UpdateReadings($hash); } # activity if ( $change !~ /:/ ) { # get user realname my $realname = AttrVal( $devName, AttrVal( $devName, "rr_realname", "group" ), $devName ); $realname = AttrVal( $devName, AttrVal( $devName, "rg_realname", "alias" ), $devName ) if ( $dev->{TYPE} eq "GUEST" ); # update statistics readingsBulkUpdate( $hash, "lastActivity", ReadingsVal( $devName, "state", $change ) ); readingsBulkUpdate( $hash, "lastActivityBy", $realname ); readingsBulkUpdate( $hash, "lastActivityByDev", $devName ); } } readingsEndUpdate( $hash, 1 ); return; } # if we have registered wakeup devices if (@registeredWakeupdevs) { # if this is a notification of a registered wakeup device if ( grep { /^$devName$/ } @registeredWakeupdevs ) { # Some previous notify deleted the array. return if ( !$dev->{CHANGED} ); foreach my $change ( @{ $dev->{CHANGED} } ) { RESIDENTStk_wakeupSet( $devName, $change ); } return; } # process sub-child notifies: *_wakeupDevice foreach my $wakeupDev (@registeredWakeupdevs) { # if this is a notification of a registered sub dummy device # of one of our wakeup devices if ( defined( $attr{$wakeupDev}{wakeupResetSwitcher} ) && $attr{$wakeupDev}{wakeupResetSwitcher} eq $devName && $defs{$devName}{TYPE} eq "dummy" ) { # Some previous notify deleted the array. return if ( !$dev->{CHANGED} ); foreach my $change ( @{ $dev->{CHANGED} } ) { RESIDENTStk_wakeupSet( $wakeupDev, $change ) if ( $change ne "off" ); } last; } } } return; } ################################### sub RESIDENTS_Set($@) { my ( $hash, @a ) = @_; my $name = $hash->{NAME}; my $state = ReadingsVal( $name, "state", "initialized" ); my $roommates = ( $hash->{ROOMMATES} ? $hash->{ROOMMATES} : "" ); my $guests = ( $hash->{GUESTS} ? $hash->{GUESTS} : "" ); return if ( IsDisabled($name) ); Log3 $name, 5, "RESIDENTS $name: called function RESIDENTS_Set()"; return "No Argument given" if ( !defined( $a[1] ) ); # depending on current FHEMWEB instance's allowedCommands, # restrict set commands if there is "set-user" in it my $adminMode = 1; my $FWallowedCommands = 0; $FWallowedCommands = AttrVal( $FW_wname, "allowedCommands", 0 ) if ( defined($FW_wname) ); if ( $FWallowedCommands && $FWallowedCommands =~ m/\bset-user\b/ ) { $adminMode = 0; return "Forbidden command: set " . $a[1] if ( lc( $a[1] ) eq "addroommate" || lc( $a[1] ) eq "addguest" || lc( $a[1] ) eq "removeroommate" || lc( $a[1] ) eq "removeguest" || lc( $a[1] ) eq "create" ); } # states my $states = ( defined( $attr{$name}{rgr_states} ) ? $attr{$name}{rgr_states} : ( defined( $attr{$name}{rgr_showAllStates} ) && $attr{$name}{rgr_showAllStates} == 1 ? "home,gotosleep,asleep,awoken,absent,gone" : "home,gotosleep,absent,gone" ) ); $states = $state . "," . $states if ( $state ne "initialized" && $states !~ /$state/ ); my $usage = "Unknown argument " . $a[1] . ", choose one of state:$states"; if ($adminMode) { $usage .= " addRoommate addGuest"; $usage .= " removeRoommate:" . $roommates if ( $roommates ne "" ); $usage .= " removeGuest:" . $guests if ( $guests ne "" ); $usage .= " create:wakeuptimer"; } # states if ( $a[1] eq "state" || $a[1] eq "home" || $a[1] eq "gotosleep" || $a[1] eq "asleep" || $a[1] eq "awoken" || $a[1] eq "absent" || $a[1] eq "gone" ) { my $newstate; my $presence = "absent"; # if not direct if ( $a[1] eq "state" && defined( $a[2] ) && ( $a[2] eq "home" || $a[2] eq "gotosleep" || $a[2] eq "asleep" || $a[2] eq "awoken" || $a[2] eq "absent" || $a[2] eq "gone" ) ) { $newstate = $a[2]; } elsif ( defined( $a[2] ) ) { return "Invalid 2nd argument, choose one of home gotosleep asleep awoken absent gone "; } else { $newstate = $a[1]; } Log3 $name, 2, "RESIDENTS set $name " . $newstate; # loop through every roommate if ( defined( $hash->{ROOMMATES} ) && $hash->{ROOMMATES} ne "" ) { my @registeredRoommates = split( /,/, $hash->{ROOMMATES} ); foreach my $roommate (@registeredRoommates) { fhem "set $roommate silentSet state $newstate" if ( ReadingsVal( $roommate, "state", "initialized" ) ne $newstate ); } } # loop through every guest if ( defined( $hash->{GUESTS} ) && $hash->{GUESTS} ne "" ) { $newstate = "none" if ( $newstate eq "gone" ); my @registeredGuests = split( /,/, $hash->{GUESTS} ); foreach my $guest (@registeredGuests) { fhem "set $guest silentSet state $newstate" if ( ReadingsVal( $guest, "state", "initialized" ) ne $newstate ); } } } # addRoommate elsif ( lc( $a[1] ) eq "addroommate" ) { Log3 $name, 2, "RESIDENTS set $name " . $a[1] . " " . $a[2] if ( defined( $a[2] ) ); my $rr_name; my $rr_name_attr; if ( $a[2] ne "" ) { $rr_name = "rr_" unless ( $a[2] =~ /^rr_/ ); $rr_name .= $a[2]; # define roommate fhem( "define " . $rr_name . " ROOMMATE " . $name ) unless ( IsDevice($rr_name) ); if ( IsDevice($rr_name) ) { return "Can't create, device $rr_name already existing." unless ( IsDevice( $rr_name, "ROOMMATE" ) ); my $lang = $a[3] ? uc( $a[3] ) : AttrVal( $rr_name, "rr_lang", AttrVal( $name, "rgr_lang", undef ) ); fhem( "attr " . $rr_name . " rr_lang " . $lang ) if ($lang); $attr{$rr_name}{comment} = "Auto-created by $name" unless ( defined( $attr{$rr_name}{comment} ) && $attr{$rr_name}{comment} eq "Auto-created by $name" ); fhem "set $rr_name silentSet state home"; Log3 $name, 3, "RESIDENTS $name: created new device $rr_name"; } } else { return "No Argument given, choose one of name "; } } # removeRoommate elsif ( lc( $a[1] ) eq "removeroommate" ) { Log3 $name, 2, "RESIDENTS set $name " . $a[1] . " " . $a[2] if ( defined( $a[2] ) ); if ( $a[2] ne "" ) { my $rr_name = $a[2]; # delete roommate if ( IsDevice($rr_name) ) { Log3 $name, 3, "RESIDENTS $name: deleted device $rr_name" if fhem( "delete " . $rr_name ); } } else { return "No Argument given, choose one of name "; } } # addGuest elsif ( lc( $a[1] ) eq "addguest" ) { Log3 $name, 2, "RESIDENTS set $name " . $a[1] . " " . $a[2] if ( defined( $a[2] ) ); my $rg_name; my $rg_name_attr; if ( $a[2] ne "" ) { $rg_name = "rg_" unless ( $a[2] =~ /^rg_/ ); $rg_name .= $a[2]; # define guest fhem( "define " . $rg_name . " GUEST " . $name ) unless ( IsDevice($rg_name) ); if ( IsDevice($rg_name) ) { return "Can't create, device $rg_name already existing." unless ( IsDevice( $rg_name, "GUEST" ) ); my $lang = $a[3] ? uc( $a[3] ) : AttrVal( $rg_name, "rg_lang", AttrVal( $name, "rgr_lang", undef ) ); fhem( "attr " . $rg_name . " rg_lang " . $lang ) if ($lang); $attr{$rg_name}{comment} = "Auto-created by $name" unless ( defined( $attr{$rg_name}{comment} ) && $attr{$rg_name}{comment} eq "Auto-created by $name" ); fhem "set $rg_name silentSet state home"; Log3 $name, 3, "RESIDENTS $name: created new device $rg_name"; } } else { return "No Argument given, choose one of name "; } } # removeGuest elsif ( lc( $a[1] ) eq "removeguest" ) { Log3 $name, 2, "RESIDENTS set $name " . $a[1] . " " . $a[2] if ( defined( $a[2] ) ); if ( $a[2] ne "" ) { my $rg_name = $a[2]; # delete guest if ( IsDevice($rg_name) ) { Log3 $name, 3, "RESIDENTS $name: deleted device $rg_name" if fhem( "delete " . $rg_name ); } } else { return "No Argument given, choose one of name "; } } # create elsif ( $a[1] eq "create" ) { if ( defined( $a[2] ) && $a[2] eq "wakeuptimer" ) { my $i = "1"; my $wakeuptimerName = $name . "_wakeuptimer" . $i; my $created = 0; until ($created) { if ( IsDevice($wakeuptimerName) ) { $i++; $wakeuptimerName = $name . "_wakeuptimer" . $i; } else { my $sortby = AttrVal( $name, "sortby", -1 ); $sortby++; # create new dummy device fhem "define $wakeuptimerName dummy"; fhem "attr $wakeuptimerName alias Wake-up Timer $i"; fhem "attr $wakeuptimerName comment Auto-created by RESIDENTS module for use with RESIDENTS Toolkit"; fhem "attr $wakeuptimerName devStateIcon OFF:general_aus\@red:reset running:general_an\@green:stop .*:general_an\@orange: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 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 trigger:noArg start:noArg stop:noArg end:noArg"; fhem "attr $wakeuptimerName userattr wakeupUserdevice"; fhem "attr $wakeuptimerName sortby " . $sortby if ($sortby); fhem "attr $wakeuptimerName wakeupUserdevice $name"; fhem "attr $wakeuptimerName webCmd nextRun"; # register slave device 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 nextRun OFF"; $created = 1; } } 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 "; } } # return usage hint else { return $usage; } return undef; } ############################################################################################################ # # Begin of helper functions # ############################################################################################################ sub RESIDENTS_UpdateReadings (@) { my ($hash) = @_; my $name = $hash->{NAME}; my $state = ReadingsVal( $name, "state", "none" ); my $presence = ReadingsVal( $name, "presence", "absent" ); my $state_home = 0; my $state_gotosleep = 0; my $state_asleep = 0; my $state_awoken = 0; my $state_absent = 0; my $state_gone = 0; my $state_total = 0; my $state_totalPresent = 0; my $state_totalAbsent = 0; my $state_totalGuests = 0; my $state_totalGuestsPresent = 0; my $state_totalGuestsAbsent = 0; my $state_totalRoommates = 0; my $state_totalRoommatesPresent = 0; my $state_totalRoommatesAbsent = 0; my $state_guestDev = 0; my $residentsDevs_home = "-"; my $residentsDevs_absent = "-"; my $residentsDevs_asleep = "-"; my $residentsDevs_awoken = "-"; my $residentsDevs_gone = "-"; my $residentsDevs_gotosleep = "-"; my $residentsDevs_wakeup = "-"; my $residentsDevs_wayhome = "-"; my $residentsDevs_wayhomeDelayed = "-"; my $residentsDevs_totalAbsent = "-"; my $residentsDevs_totalPresent = "-"; my $residentsDevs_totalAbsentGuest = "-"; my $residentsDevs_totalPresentGuest = "-"; my $residentsDevs_totalAbsentRoommates = "-"; my $residentsDevs_totalPresentRoommates = "-"; my $residents_home = "-"; my $residents_absent = "-"; my $residents_asleep = "-"; my $residents_awoken = "-"; my $residents_gone = "-"; my $residents_gotosleep = "-"; my $residents_wakeup = "-"; my $residents_wayhome = "-"; my $residents_wayhomeDelayed = "-"; my $residents_totalAbsent = "-"; my $residents_totalPresent = "-"; my $residents_totalAbsentGuest = "-"; my $residents_totalPresentGuest = "-"; my $residents_totalAbsentRoommates = "-"; my $residents_totalPresentRoommates = "-"; my $wayhome = 0; my $wayhomeDelayed = 0; my $wakeup = 0; my $newstate; my @registeredRoommates = split( /,/, $hash->{ROOMMATES} ) if ( defined( $hash->{ROOMMATES} ) && $hash->{ROOMMATES} ne "" ); my @registeredGuests = split( /,/, $hash->{GUESTS} ) if ( defined( $hash->{GUESTS} ) && $hash->{GUESTS} ne "" ); # count child states for ROOMMATE devices foreach my $roommate (@registeredRoommates) { $state_total++; $state_totalRoommates++; my $roommateName = AttrVal( $roommate, AttrVal( $roommate, "rr_realname", "group" ), "" ); Log3 $name, 5, "RESIDENTS $name: considering $roommate for state change"; if ( ReadingsVal( $roommate, "state", "initialized" ) eq "home" ) { $state_home++; $residentsDevs_home .= "," . $roommate if ( $residentsDevs_home ne "-" ); $residentsDevs_home = $roommate if ( $residentsDevs_home eq "-" ); $residents_home .= ", " . $roommateName if ( $roommateName ne "" && $residents_home ne "-" ); $residents_home = $roommateName if ( $roommateName ne "" && $residents_home eq "-" ); $state_totalPresent++; $state_totalRoommatesPresent++; $residentsDevs_totalPresent .= "," . $roommate if ( $residentsDevs_totalPresent ne "-" ); $residentsDevs_totalPresent = $roommate if ( $residentsDevs_totalPresent eq "-" ); $residentsDevs_totalPresentRoommates .= "," . $roommate if ( $residentsDevs_totalPresentRoommates ne "-" ); $residentsDevs_totalPresentRoommates = $roommate if ( $residentsDevs_totalPresentRoommates eq "-" ); $residents_totalPresent .= ", " . $roommateName if ( $roommateName ne "" && $residents_totalPresent ne "-" ); $residents_totalPresent = $roommateName if ( $roommateName ne "" && $residents_totalPresent eq "-" ); $residents_totalPresentRoommates .= ", " . $roommateName if ( $roommateName ne "" && $residents_totalPresentRoommates ne "-" ); $residents_totalPresentRoommates = $roommateName if ( $roommateName ne "" && $residents_totalPresentRoommates eq "-" ); } elsif ( ReadingsVal( $roommate, "state", "initialized" ) eq "gotosleep" ) { $state_gotosleep++; $residentsDevs_gotosleep .= "," . $roommate if ( $residentsDevs_gotosleep ne "-" ); $residentsDevs_gotosleep = $roommate if ( $residentsDevs_gotosleep eq "-" ); $residents_gotosleep .= ", " . $roommateName if ( $roommateName ne "" && $residents_gotosleep ne "-" ); $residents_gotosleep = $roommateName if ( $roommateName ne "" && $residents_gotosleep eq "-" ); $state_totalPresent++; $state_totalRoommatesPresent++; $residentsDevs_totalPresent .= "," . $roommate if ( $residentsDevs_totalPresent ne "-" ); $residentsDevs_totalPresent = $roommate if ( $residentsDevs_totalPresent eq "-" ); $residentsDevs_totalPresentRoommates .= "," . $roommate if ( $residentsDevs_totalPresentRoommates ne "-" ); $residentsDevs_totalPresentRoommates = $roommate if ( $residentsDevs_totalPresentRoommates eq "-" ); $residents_totalPresent .= ", " . $roommateName if ( $roommateName ne "" && $residents_totalPresent ne "-" ); $residents_totalPresent = $roommateName if ( $roommateName ne "" && $residents_totalPresent eq "-" ); $residents_totalPresentRoommates .= ", " . $roommateName if ( $roommateName ne "" && $residents_totalPresentRoommates ne "-" ); $residents_totalPresentRoommates = $roommateName if ( $roommateName ne "" && $residents_totalPresentRoommates eq "-" ); } elsif ( ReadingsVal( $roommate, "state", "initialized" ) eq "asleep" ) { $state_asleep++; $residentsDevs_asleep .= "," . $roommate if ( $residentsDevs_asleep ne "-" ); $residentsDevs_asleep = $roommate if ( $residentsDevs_asleep eq "-" ); $residents_asleep .= ", " . $roommateName if ( $roommateName ne "" && $residents_asleep ne "-" ); $residents_asleep = $roommateName if ( $roommateName ne "" && $residents_asleep eq "-" ); $state_totalPresent++; $state_totalRoommatesPresent++; $residentsDevs_totalPresent .= "," . $roommate if ( $residentsDevs_totalPresent ne "-" ); $residentsDevs_totalPresent = $roommate if ( $residentsDevs_totalPresent eq "-" ); $residentsDevs_totalPresentRoommates .= "," . $roommate if ( $residentsDevs_totalPresentRoommates ne "-" ); $residentsDevs_totalPresentRoommates = $roommate if ( $residentsDevs_totalPresentRoommates eq "-" ); $residents_totalPresent .= ", " . $roommateName if ( $roommateName ne "" && $residents_totalPresent ne "-" ); $residents_totalPresent = $roommateName if ( $roommateName ne "" && $residents_totalPresent eq "-" ); $residents_totalPresentRoommates .= ", " . $roommateName if ( $roommateName ne "" && $residents_totalPresentRoommates ne "-" ); $residents_totalPresentRoommates = $roommateName if ( $roommateName ne "" && $residents_totalPresentRoommates eq "-" ); } elsif ( ReadingsVal( $roommate, "state", "initialized" ) eq "awoken" ) { $state_awoken++; $residentsDevs_awoken .= "," . $roommate if ( $residentsDevs_awoken ne "-" ); $residentsDevs_awoken = $roommate if ( $residentsDevs_awoken eq "-" ); $residents_awoken .= ", " . $roommateName if ( $roommateName ne "" && $residents_awoken ne "-" ); $residents_awoken = $roommateName if ( $roommateName ne "" && $residents_awoken eq "-" ); $state_totalPresent++; $state_totalRoommatesPresent++; $residentsDevs_totalPresent .= "," . $roommate if ( $residentsDevs_totalPresent ne "-" ); $residentsDevs_totalPresent = $roommate if ( $residentsDevs_totalPresent eq "-" ); $residentsDevs_totalPresentRoommates .= "," . $roommate if ( $residentsDevs_totalPresentRoommates ne "-" ); $residentsDevs_totalPresentRoommates = $roommate if ( $residentsDevs_totalPresentRoommates eq "-" ); $residents_totalPresent .= ", " . $roommateName if ( $roommateName ne "" && $residents_totalPresent ne "-" ); $residents_totalPresent = $roommateName if ( $roommateName ne "" && $residents_totalPresent eq "-" ); $residents_totalPresentRoommates .= ", " . $roommateName if ( $roommateName ne "" && $residents_totalPresentRoommates ne "-" ); $residents_totalPresentRoommates = $roommateName if ( $roommateName ne "" && $residents_totalPresentRoommates eq "-" ); } elsif ( ReadingsVal( $roommate, "state", "initialized" ) eq "absent" ) { $state_absent++; $residentsDevs_absent .= "," . $roommate if ( $residentsDevs_absent ne "-" ); $residentsDevs_absent = $roommate if ( $residentsDevs_absent eq "-" ); $residents_absent .= ", " . $roommateName if ( $roommateName ne "" && $residents_absent ne "-" ); $residents_absent = $roommateName if ( $roommateName ne "" && $residents_absent eq "-" ); $state_totalAbsent++; $state_totalRoommatesAbsent++; $residentsDevs_totalAbsent .= "," . $roommate if ( $residentsDevs_totalAbsent ne "-" ); $residentsDevs_totalAbsent = $roommate if ( $residentsDevs_totalAbsent eq "-" ); $residentsDevs_totalAbsentRoommates .= "," . $roommate if ( $residentsDevs_totalAbsentRoommates ne "-" ); $residentsDevs_totalAbsentRoommates = $roommate if ( $residentsDevs_totalAbsentRoommates eq "-" ); $residents_totalAbsent .= ", " . $roommateName if ( $roommateName ne "" && $residents_totalAbsent ne "-" ); $residents_totalAbsent = $roommateName if ( $roommateName ne "" && $residents_totalAbsent eq "-" ); $residents_totalAbsentRoommates .= ", " . $roommateName if ( $roommateName ne "" && $residents_totalAbsentRoommates ne "-" ); $residents_totalAbsentRoommates = $roommateName if ( $roommateName ne "" && $residents_totalAbsentRoommates eq "-" ); } elsif ( ReadingsVal( $roommate, "state", "initialized" ) eq "gone" ) { $state_gone++; $residentsDevs_gone .= "," . $roommate if ( $residentsDevs_gone ne "-" ); $residentsDevs_gone = $roommate if ( $residentsDevs_gone eq "-" ); $residents_gone .= ", " . $roommateName if ( $roommateName ne "" && $residents_gone ne "-" ); $residents_gone = $roommateName if ( $roommateName ne "" && $residents_gone eq "-" ); $state_totalAbsent++; $state_totalRoommatesAbsent++; $residentsDevs_totalAbsent .= "," . $roommate if ( $residentsDevs_totalAbsent ne "-" ); $residentsDevs_totalAbsent = $roommate if ( $residentsDevs_totalAbsent eq "-" ); $residentsDevs_totalAbsentRoommates .= "," . $roommate if ( $residentsDevs_totalAbsentRoommates ne "-" ); $residentsDevs_totalAbsentRoommates = $roommate if ( $residentsDevs_totalAbsentRoommates eq "-" ); $residents_totalAbsent .= ", " . $roommateName if ( $roommateName ne "" && $residents_totalAbsent ne "-" ); $residents_totalAbsent = $roommateName if ( $roommateName ne "" && $residents_totalAbsent eq "-" ); $residents_totalAbsentRoommates .= ", " . $roommateName if ( $roommateName ne "" && $residents_totalAbsentRoommates ne "-" ); $residents_totalAbsentRoommates = $roommateName if ( $roommateName ne "" && $residents_totalAbsentRoommates eq "-" ); } if ( ReadingsVal( $roommate, "wakeup", "0" ) > 0 ) { $wakeup++; $residentsDevs_wakeup .= "," . $roommate if ( $residentsDevs_wakeup ne "-" ); $residentsDevs_wakeup = $roommate if ( $residentsDevs_wakeup eq "-" ); $residents_wakeup .= ", " . $roommateName if ( $roommateName ne "" && $residents_wakeup ne "-" ); $residents_wakeup = $roommateName if ( $roommateName ne "" && $residents_wakeup eq "-" ); } if ( ReadingsVal( $roommate, "wayhome", "0" ) > 0 ) { $wayhome++; $residentsDevs_wayhome .= "," . $roommate if ( $residentsDevs_wayhome ne "-" ); $residentsDevs_wayhome = $roommate if ( $residentsDevs_wayhome eq "-" ); $residents_wayhome .= ", " . $roommateName if ( $roommateName ne "" && $residents_wayhome ne "-" ); $residents_wayhome = $roommateName if ( $roommateName ne "" && $residents_wayhome eq "-" ); if ( ReadingsVal( $roommate, "wayhome", "0" ) == 2 ) { $wayhomeDelayed++; $residentsDevs_wayhomeDelayed .= "," . $roommate if ( $residentsDevs_wayhomeDelayed ne "-" ); $residentsDevs_wayhomeDelayed = $roommate if ( $residentsDevs_wayhomeDelayed eq "-" ); $residents_wayhomeDelayed .= ", " . $roommateName if ( $roommateName ne "" && $residents_wayhomeDelayed ne "-" ); $residents_wayhomeDelayed = $roommateName if ( $roommateName ne "" && $residents_wayhomeDelayed eq "-" ); } } } # count child states for GUEST devices foreach my $guest (@registeredGuests) { $state_guestDev++; my $guestName = AttrVal( $guest, AttrVal( $guest, "rg_realname", "alias" ), "" ); Log3 $name, 5, "RESIDENTS $name: considering $guest for state change"; if ( ReadingsVal( $guest, "state", "initialized" ) eq "home" ) { $state_home++; $state_totalPresent++; $state_totalGuestsPresent++; $state_totalGuests++; $state_total++; $residentsDevs_totalPresentGuest .= "," . $guest if ( $residentsDevs_totalPresentGuest ne "-" ); $residentsDevs_totalPresentGuest = $guest if ( $residentsDevs_totalPresentGuest eq "-" ); $residents_totalPresentGuest .= ", " . $guestName if ( $guestName ne "" && $residents_totalPresentGuest ne "-" ); $residents_totalPresentGuest = $guestName if ( $guestName ne "" && $residents_totalPresentGuest eq "-" ); $residentsDevs_totalPresent .= "," . $guest if ( $residentsDevs_totalPresent ne "-" ); $residentsDevs_totalPresent = $guest if ( $residentsDevs_totalPresent eq "-" ); $residents_totalPresent .= ", " . $guestName if ( $guestName ne "" && $residents_totalPresent ne "-" ); $residents_totalPresent = $guestName if ( $guestName ne "" && $residents_totalPresent eq "-" ); } elsif ( ReadingsVal( $guest, "state", "initialized" ) eq "gotosleep" ) { $state_gotosleep++; $state_totalPresent++; $state_totalGuestsPresent++; $state_totalGuests++; $state_total++; $residentsDevs_totalPresentGuest .= "," . $guest if ( $residentsDevs_totalPresentGuest ne "-" ); $residentsDevs_totalPresentGuest = $guest if ( $residentsDevs_totalPresentGuest eq "-" ); $residents_totalPresentGuest .= ", " . $guestName if ( $guestName ne "" && $residents_totalPresentGuest ne "-" ); $residents_totalPresentGuest = $guestName if ( $guestName ne "" && $residents_totalPresentGuest eq "-" ); $residentsDevs_totalPresent .= "," . $guest if ( $residentsDevs_totalPresent ne "-" ); $residentsDevs_totalPresent = $guest if ( $residentsDevs_totalPresent eq "-" ); $residents_totalPresent .= ", " . $guestName if ( $guestName ne "" && $residents_totalPresent ne "-" ); $residents_totalPresent = $guestName if ( $guestName ne "" && $residents_totalPresent eq "-" ); } elsif ( ReadingsVal( $guest, "state", "initialized" ) eq "asleep" ) { $state_asleep++; $state_totalPresent++; $state_totalGuestsPresent++; $state_totalGuests++; $state_total++; $residentsDevs_totalPresentGuest .= "," . $guest if ( $residentsDevs_totalPresentGuest ne "-" ); $residentsDevs_totalPresentGuest = $guest if ( $residentsDevs_totalPresentGuest eq "-" ); $residents_totalPresentGuest .= ", " . $guestName if ( $guestName ne "" && $residents_totalPresentGuest ne "-" ); $residents_totalPresentGuest = $guestName if ( $guestName ne "" && $residents_totalPresentGuest eq "-" ); $residentsDevs_totalPresent .= "," . $guest if ( $residentsDevs_totalPresent ne "-" ); $residentsDevs_totalPresent = $guest if ( $residentsDevs_totalPresent eq "-" ); $residents_totalPresent .= ", " . $guestName if ( $guestName ne "" && $residents_totalPresent ne "-" ); $residents_totalPresent = $guestName if ( $guestName ne "" && $residents_totalPresent eq "-" ); } elsif ( ReadingsVal( $guest, "state", "initialized" ) eq "awoken" ) { $state_awoken++; $state_totalPresent++; $state_totalGuestsPresent++; $state_totalGuests++; $state_total++; $residentsDevs_totalPresentGuest .= "," . $guest if ( $residentsDevs_totalPresentGuest ne "-" ); $residentsDevs_totalPresentGuest = $guest if ( $residentsDevs_totalPresentGuest eq "-" ); $residents_totalPresentGuest .= ", " . $guestName if ( $guestName ne "" && $residents_totalPresentGuest ne "-" ); $residents_totalPresentGuest = $guestName if ( $guestName ne "" && $residents_totalPresentGuest eq "-" ); $residentsDevs_totalPresent .= "," . $guest if ( $residentsDevs_totalPresent ne "-" ); $residentsDevs_totalPresent = $guest if ( $residentsDevs_totalPresent eq "-" ); $residents_totalPresent .= ", " . $guestName if ( $guestName ne "" && $residents_totalPresent ne "-" ); $residents_totalPresent = $guestName if ( $guestName ne "" && $residents_totalPresent eq "-" ); } elsif ( ReadingsVal( $guest, "state", "initialized" ) eq "absent" ) { $state_absent++; $state_totalAbsent++; $state_totalGuestsAbsent++; $state_totalGuests++; $state_total++; $residentsDevs_totalAbsentGuest .= "," . $guest if ( $residentsDevs_totalAbsentGuest ne "-" ); $residentsDevs_totalAbsentGuest = $guest if ( $residentsDevs_totalAbsentGuest eq "-" ); $residents_totalAbsentGuest .= ", " . $guestName if ( $guestName ne "" && $residents_totalAbsentGuest ne "-" ); $residents_totalAbsentGuest = $guestName if ( $guestName ne "" && $residents_totalAbsentGuest eq "-" ); $residentsDevs_totalAbsent .= "," . $guest if ( $residentsDevs_totalAbsent ne "-" ); $residentsDevs_totalAbsent = $guest if ( $residentsDevs_totalAbsent eq "-" ); $residents_totalAbsent .= ", " . $guestName if ( $guestName ne "" && $residents_totalAbsent ne "-" ); $residents_totalAbsent = $guestName if ( $guestName ne "" && $residents_totalAbsent eq "-" ); } if ( ReadingsVal( $guest, "wakeup", "0" ) > 0 ) { $wakeup++; $residentsDevs_wakeup .= "," . $guest if ( $residentsDevs_wakeup ne "-" ); $residentsDevs_wakeup = $guest if ( $residentsDevs_wakeup eq "-" ); $residents_wakeup .= ", " . $guestName if ( $guestName ne "" && $residents_wakeup ne "-" ); $residents_wakeup = $guestName if ( $guestName ne "" && $residents_wakeup eq "-" ); } if ( ReadingsVal( $guest, "wayhome", "0" ) > 0 ) { $wayhome++; $residents_wayhome .= "," . $guest if ( $residents_wayhome ne "-" ); $residents_wayhome = $guest if ( $residents_wayhome eq "-" ); $residents_wayhome .= ", " . $guestName if ( $guestName ne "" && $residents_wayhome ne "-" ); $residents_wayhome = $guestName if ( $guestName ne "" && $residents_wayhome eq "-" ); if ( ReadingsVal( $guest, "wayhome", "0" ) == 2 ) { $wayhomeDelayed++; $residentsDevs_wayhomeDelayed .= "," . $guest if ( $residentsDevs_wayhomeDelayed ne "-" ); $residentsDevs_wayhomeDelayed = $guest if ( $residentsDevs_wayhomeDelayed eq "-" ); $residents_wayhomeDelayed .= ", " . $guestName if ( $guestName ne "" && $residents_wayhomeDelayed ne "-" ); $residents_wayhomeDelayed = $guestName if ( $guestName ne "" && $residents_wayhomeDelayed eq "-" ); } } } # update counter readingsBulkUpdateIfChanged( $hash, "residentsTotal", $state_total ); readingsBulkUpdateIfChanged( $hash, "residentsTotalGuests", $state_totalGuests ); readingsBulkUpdateIfChanged( $hash, "residentsTotalGuestsPresent", $state_totalGuestsPresent ); readingsBulkUpdateIfChanged( $hash, "residentsTotalGuestsPresentDevs", $residentsDevs_totalPresentGuest ); readingsBulkUpdateIfChanged( $hash, "residentsTotalGuestsPresentNames", $residents_totalPresentGuest ); readingsBulkUpdateIfChanged( $hash, "residentsTotalGuestsAbsent", $state_totalGuestsAbsent ); readingsBulkUpdateIfChanged( $hash, "residentsTotalGuestsAbsentDevs", $residentsDevs_totalAbsentGuest ); readingsBulkUpdateIfChanged( $hash, "residentsTotalGuestsAbsentNames", $residents_totalAbsentGuest ); readingsBulkUpdateIfChanged( $hash, "residentsTotalRoommates", $state_totalRoommates ); readingsBulkUpdateIfChanged( $hash, "residentsTotalRoommatesPresent", $state_totalRoommatesPresent ); readingsBulkUpdateIfChanged( $hash, "residentsTotalRoommatesPresentDevs", $residentsDevs_totalPresentRoommates ); readingsBulkUpdateIfChanged( $hash, "residentsTotalRoommatesPresentNames", $residents_totalPresentRoommates ); readingsBulkUpdateIfChanged( $hash, "residentsTotalRoommatesAbsent", $state_totalRoommatesAbsent ); readingsBulkUpdateIfChanged( $hash, "residentsTotalRoommatesAbsentDevs", $residentsDevs_totalAbsentRoommates ); readingsBulkUpdateIfChanged( $hash, "residentsTotalRoommatesAbsentNames", $residents_totalAbsentRoommates ); readingsBulkUpdateIfChanged( $hash, "residentsTotalPresent", $state_totalPresent ); readingsBulkUpdateIfChanged( $hash, "residentsTotalPresentDevs", $residentsDevs_totalPresent ); readingsBulkUpdateIfChanged( $hash, "residentsTotalPresentNames", $residents_totalPresent ); readingsBulkUpdateIfChanged( $hash, "residentsTotalAbsent", $state_totalAbsent ); readingsBulkUpdateIfChanged( $hash, "residentsTotalAbsentDevs", $residentsDevs_totalAbsent ); readingsBulkUpdateIfChanged( $hash, "residentsTotalAbsentNames", $residents_totalAbsent ); readingsBulkUpdateIfChanged( $hash, "residentsHome", $state_home ); readingsBulkUpdateIfChanged( $hash, "residentsHomeDevs", $residentsDevs_home ); readingsBulkUpdateIfChanged( $hash, "residentsHomeNames", $residents_home ); readingsBulkUpdateIfChanged( $hash, "residentsGotosleep", $state_gotosleep ); readingsBulkUpdateIfChanged( $hash, "residentsGotosleepDevs", $residentsDevs_gotosleep ); readingsBulkUpdateIfChanged( $hash, "residentsGotosleepNames", $residents_gotosleep ); readingsBulkUpdateIfChanged( $hash, "residentsAsleep", $state_asleep ); readingsBulkUpdateIfChanged( $hash, "residentsAsleepDevs", $residentsDevs_asleep ); readingsBulkUpdateIfChanged( $hash, "residentsAsleepNames", $residents_asleep ); readingsBulkUpdateIfChanged( $hash, "residentsAwoken", $state_awoken ); readingsBulkUpdateIfChanged( $hash, "residentsAwokenDevs", $residentsDevs_awoken ); readingsBulkUpdateIfChanged( $hash, "residentsAwokenNames", $residents_awoken ); readingsBulkUpdateIfChanged( $hash, "residentsAbsent", $state_absent ); readingsBulkUpdateIfChanged( $hash, "residentsAbsentDevs", $residentsDevs_absent ); readingsBulkUpdateIfChanged( $hash, "residentsAbsentNames", $residents_absent ); readingsBulkUpdateIfChanged( $hash, "residentsGone", $state_gone ); readingsBulkUpdateIfChanged( $hash, "residentsGoneDevs", $residentsDevs_gone ); readingsBulkUpdateIfChanged( $hash, "residentsGoneNames", $residents_gone ); readingsBulkUpdateIfChanged( $hash, "residentsTotalWakeup", $wakeup ); readingsBulkUpdateIfChanged( $hash, "residentsTotalWakeupDevs", $residentsDevs_wakeup ); readingsBulkUpdateIfChanged( $hash, "residentsTotalWakeupNames", $residents_wakeup ); readingsBulkUpdateIfChanged( $hash, "residentsTotalWayhome", $wayhome ); readingsBulkUpdateIfChanged( $hash, "residentsTotalWayhomeDevs", $residentsDevs_wayhome ); readingsBulkUpdateIfChanged( $hash, "residentsTotalWayhomeNames", $residents_wayhome ); readingsBulkUpdateIfChanged( $hash, "residentsTotalWayhomeDelayed", $wayhomeDelayed ); readingsBulkUpdateIfChanged( $hash, "residentsTotalWayhomeDelayedDevs", $residentsDevs_wayhomeDelayed ); readingsBulkUpdateIfChanged( $hash, "residentsTotalWayhomeDelayedNames", $residents_wayhomeDelayed ); # # state calculation # # gotosleep if ( $state_home == 0 && $state_gotosleep > 0 && $state_asleep >= 0 && $state_awoken == 0 ) { $newstate = "gotosleep"; } # asleep elsif ($state_home == 0 && $state_gotosleep == 0 && $state_asleep > 0 && $state_awoken == 0 ) { $newstate = "asleep"; } # awoken elsif ($state_home == 0 && $state_gotosleep >= 0 && $state_asleep >= 0 && $state_awoken > 0 ) { $newstate = "awoken"; } # general presence elsif ($state_home > 0 || $state_gotosleep > 0 || $state_asleep > 0 || $state_awoken > 0 ) { $newstate = "home"; } # absent elsif ($state_absent > 0 && $state_home == 0 && $state_gotosleep == 0 && $state_asleep == 0 && $state_awoken == 0 ) { $newstate = "absent"; } # gone elsif ($state_gone > 0 && $state_absent == 0 && $state_home == 0 && $state_gotosleep == 0 && $state_asleep == 0 && $state_awoken == 0 ) { $newstate = "gone"; } # none elsif ($state_totalGuests == 0 && $state_totalRoommates == 0 && $state_gone == 0 && $state_absent == 0 && $state_home == 0 && $state_gotosleep == 0 && $state_asleep == 0 && $state_awoken == 0 ) { $newstate = "none"; } # unspecified; this should not happen else { $newstate = "unspecified"; } # calculate presence state my $newpresence = ( $newstate ne "none" && $newstate ne "gone" && $newstate ne "absent" ) ? "present" : "absent"; Log3 $name, 4, "RESIDENTS $name: calculation result - residentsTotal:$state_total residentsTotalRoommates:$state_totalRoommates residentsTotalRoommatesPresent:$state_totalRoommatesPresent residentsTotalRoommatesAbsent:$state_totalRoommatesAbsent residentsTotalGuests:$state_totalGuests residentsTotalGuestsPresent:$state_totalGuestsPresent residentsTotalGuestsAbsent:$state_totalGuestsAbsent residentsTotalPresent:$state_totalPresent residentsTotalAbsent:$state_totalAbsent residentsHome:$state_home residentsGotosleep:$state_gotosleep residentsAsleep:$state_asleep residentsAwoken:$state_awoken residentsAbsent:$state_absent residentsGone:$state_gone presence:$newpresence state:$newstate"; # safe current time my $datetime = FmtDateTime(time); # if state changed if ( $state ne $newstate ) { # stop any running wakeup-timers in case state changed my $wakeupState = AttrVal( $name, "wakeup", 0 ); if ($wakeupState) { my $wakeupDeviceList = AttrVal( $name, "rgr_wakeupDevice", 0 ); for my $wakeupDevice ( split /,/, $wakeupDeviceList ) { next if !$wakeupDevice; if ( IsDevice( $wakeupDevice, "dummy" ) ) { # forced-stop only if resident is not present anymore if ( $newpresence eq "present" ) { fhem "set $wakeupDevice:FILTER=running!=0 end"; } else { fhem "set $wakeupDevice:FILTER=running!=0 stop"; } } } } # if newstate is asleep, start sleep timer readingsBulkUpdate( $hash, "lastSleep", $datetime ) if ( $newstate eq "asleep" ); # if prior state was asleep, update sleep statistics if ( $state eq "asleep" && ReadingsVal( $name, "lastSleep", "" ) ne "" ) { readingsBulkUpdate( $hash, "lastAwake", $datetime ); readingsBulkUpdate( $hash, "lastDurSleep", RESIDENTStk_TimeDiff( $datetime, ReadingsVal( $name, "lastSleep", "" ) ) ); readingsBulkUpdate( $hash, "lastDurSleep_cr", RESIDENTStk_TimeDiff( $datetime, ReadingsVal( $name, "lastSleep", "" ), "min" ) ); } readingsBulkUpdate( $hash, "lastState", ReadingsVal( $name, "state", "initialized" ) ); readingsBulkUpdate( $hash, "state", $newstate ); } # if presence changed if ( $newpresence ne $presence ) { readingsBulkUpdate( $hash, "presence", $newpresence ); # update statistics if ( $newpresence eq "present" ) { readingsBulkUpdate( $hash, "lastArrival", $datetime ); # absence duration if ( ReadingsVal( $name, "lastDeparture", "-" ) ne "-" ) { readingsBulkUpdate( $hash, "lastDurAbsence", RESIDENTStk_TimeDiff( $datetime, ReadingsVal( $name, "lastDeparture", "-" ) ) ); readingsBulkUpdate( $hash, "lastDurAbsence_cr", RESIDENTStk_TimeDiff( $datetime, ReadingsVal( $name, "lastDeparture", "-" ), "min" ) ); } } else { readingsBulkUpdate( $hash, "lastDeparture", $datetime ); # presence duration if ( ReadingsVal( $name, "lastArrival", "-" ) ne "-" ) { readingsBulkUpdate( $hash, "lastDurPresence", RESIDENTStk_TimeDiff( $datetime, ReadingsVal( $name, "lastArrival", "-" ) ) ); readingsBulkUpdate( $hash, "lastDurPresence_cr", RESIDENTStk_TimeDiff( $datetime, ReadingsVal( $name, "lastArrival", "-" ), "min" ) ); } } } # calculate duration timers RESIDENTS_DurationTimer( $hash, 1 ); } sub RESIDENTS_DurationTimer($;$) { my ( $mHash, @a ) = @_; my $hash = ( $mHash->{HASH} ) ? $mHash->{HASH} : $mHash; my $name = $hash->{NAME}; my $silent = ( defined( $a[0] ) && $a[0] eq "1" ) ? 1 : 0; my $timestampNow = gettimeofday(); my $diff; my $durPresence = "0"; my $durAbsence = "0"; my $durSleep = "0"; my $noDuration = AttrVal( $name, "rgr_noDuration", 0 ); delete $hash->{DURATIONTIMER} if ( $hash->{DURATIONTIMER} ); RESIDENTStk_RemoveInternalTimer( "DurationTimer", $hash ); return if ( IsDisabled($name) || $noDuration ); # presence timer if ( ReadingsVal( $name, "presence", "absent" ) eq "present" && ReadingsVal( $name, "lastArrival", "-" ) ne "-" ) { $durPresence = $timestampNow - time_str2num( ReadingsVal( $name, "lastArrival", "" ) ); } # absence timer if ( ReadingsVal( $name, "presence", "present" ) eq "absent" && ReadingsVal( $name, "lastDeparture", "-" ) ne "-" ) { $durAbsence = $timestampNow - time_str2num( ReadingsVal( $name, "lastDeparture", "" ) ); } # sleep timer if ( ReadingsVal( $name, "state", "home" ) eq "asleep" && ReadingsVal( $name, "lastSleep", "-" ) ne "-" ) { $durSleep = $timestampNow - time_str2num( ReadingsVal( $name, "lastSleep", "" ) ); } my $durPresence_hr = ( $durPresence > 0 ) ? RESIDENTStk_sec2time($durPresence) : "00:00:00"; my $durPresence_cr = ( $durPresence > 60 ) ? int( $durPresence / 60 + 0.5 ) : 0; my $durAbsence_hr = ( $durAbsence > 0 ) ? RESIDENTStk_sec2time($durAbsence) : "00:00:00"; my $durAbsence_cr = ( $durAbsence > 60 ) ? int( $durAbsence / 60 + 0.5 ) : 0; my $durSleep_hr = ( $durSleep > 0 ) ? RESIDENTStk_sec2time($durSleep) : "00:00:00"; my $durSleep_cr = ( $durSleep > 60 ) ? int( $durSleep / 60 + 0.5 ) : 0; readingsBeginUpdate($hash) if ( !$silent ); readingsBulkUpdateIfChanged( $hash, "durTimerPresence_cr", $durPresence_cr ); readingsBulkUpdateIfChanged( $hash, "durTimerPresence", $durPresence_hr ); readingsBulkUpdateIfChanged( $hash, "durTimerAbsence_cr", $durAbsence_cr ); readingsBulkUpdateIfChanged( $hash, "durTimerAbsence", $durAbsence_hr ); readingsBulkUpdateIfChanged( $hash, "durTimerSleep_cr", $durSleep_cr ); readingsBulkUpdateIfChanged( $hash, "durTimerSleep", $durSleep_hr ); readingsEndUpdate( $hash, 1 ) if ( !$silent ); $hash->{DURATIONTIMER} = $timestampNow + 60; RESIDENTStk_InternalTimer( "DurationTimer", $hash->{DURATIONTIMER}, "RESIDENTS_DurationTimer", $hash, 1 ); return undef; } sub RESIDENTS_StartInternalTimers($$) { my ($hash) = @_; RESIDENTS_DurationTimer($hash); } sub RESIDENTS_StopInternalTimers($) { my ($hash) = @_; delete $hash->{DURATIONTIMER} if ( $hash->{DURATIONTIMER} ); RESIDENTStk_RemoveInternalTimer( "DurationTimer", $hash ); } 1; =pod =item helper =item summary combines ROOMMATE and GUEST devices to a residential community =item summary_de fasst ROOMMATE und GUEST Geräte zu einer Wohngemeinschaft zusammen =begin html

RESIDENTS

=end html =begin html_DE

RESIDENTS

=end html_DE =cut