mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-04-22 02:10:32 +00:00
RESIDENTS.*: consolidate code base
git-svn-id: https://svn.fhem.de/fhem/trunk@14034 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
b2b86afb5a
commit
5e648fdf8e
@ -12,581 +12,18 @@ require RESIDENTStk;
|
|||||||
sub RESIDENTS_Initialize($) {
|
sub RESIDENTS_Initialize($) {
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
|
|
||||||
$hash->{DefFn} = "RESIDENTS_Define";
|
$hash->{DefFn} = "RESIDENTStk_Define";
|
||||||
$hash->{UndefFn} = "RESIDENTS_Undefine";
|
$hash->{UndefFn} = "RESIDENTStk_Undefine";
|
||||||
$hash->{SetFn} = "RESIDENTS_Set";
|
$hash->{SetFn} = "RESIDENTStk_Set";
|
||||||
$hash->{AttrFn} = "RESIDENTS_Attr";
|
$hash->{AttrFn} = "RESIDENTStk_Attr";
|
||||||
$hash->{NotifyFn} = "RESIDENTS_Notify";
|
$hash->{NotifyFn} = "RESIDENTStk_Notify";
|
||||||
|
|
||||||
$hash->{AttrList} =
|
$hash->{AttrList} =
|
||||||
"disable:1,0 disabledForIntervals do_not_notify: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 "
|
"disable:1,0 disabledForIntervals do_not_notify:1,0 "
|
||||||
|
. "rgr_states:multiple-strict,home,gotosleep,asleep,awoken,absent,gone rgr_lang:EN,DE rgr_noDuration:0,1 rgr_showAllStates:0,1 rgr_wakeupDevice "
|
||||||
. $readingFnAttributes;
|
. $readingFnAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
# regular Fn ##################################################################
|
|
||||||
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,
|
|
||||||
"RESIDENTStk_RG_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_Undefine($$) {
|
|
||||||
my ( $hash, $name ) = @_;
|
|
||||||
|
|
||||||
RESIDENTStk_RG_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_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] !~ /^(wakeuptimer)$/i ) {
|
|
||||||
return "Invalid 2nd argument, choose one of wakeuptimer ";
|
|
||||||
}
|
|
||||||
elsif ( $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 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 wakeupResetdays:multiple-strict,0,1,2,3,4,5,6 wakeupDays:multiple-strict,0,1,2,3,4,5,6 wakeupHolidays:,andHoliday,orHoliday,andNoHoliday,orNoHoliday wakeupEnforced:0,1,2,3";
|
|
||||||
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
|
|
||||||
RESIDENTStk_findDummySlaves($name);
|
|
||||||
fhem "sleep 1;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.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# return usage hint
|
|
||||||
else {
|
|
||||||
return $usage;
|
|
||||||
}
|
|
||||||
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub RESIDENTS_Attr(@) {
|
|
||||||
my ( $cmd, $name, $attribute, $value ) = @_;
|
|
||||||
my $hash = $defs{$name};
|
|
||||||
my $prefix = "rgr_";
|
|
||||||
|
|
||||||
Log3 $name, 5, "RESIDENTS $name: called function RESIDENTS_Attr()";
|
|
||||||
|
|
||||||
if ( $attribute eq "rgr_wakeupDevice" ) {
|
|
||||||
return "Value for $attribute has invalid format"
|
|
||||||
unless ( $value =~ /^([a-zA-Z\d._]+,?)([a-zA-Z\d._]+,?)*$/ );
|
|
||||||
|
|
||||||
RESIDENTStk_findResidentSlaves( $hash, $value );
|
|
||||||
}
|
|
||||||
|
|
||||||
elsif ( !$init_done ) {
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
|
|
||||||
elsif ( $attribute eq "disable" ) {
|
|
||||||
if ( $value and $value == 1 ) {
|
|
||||||
$hash->{STATE} = "disabled";
|
|
||||||
RESIDENTStk_RG_StopInternalTimers($hash);
|
|
||||||
}
|
|
||||||
elsif ( $cmd eq "del" or !$value ) {
|
|
||||||
evalStateFormat($hash);
|
|
||||||
RESIDENTStk_RG_StartInternalTimers( $hash, 1 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
elsif ( $attribute eq $prefix . "noDuration" ) {
|
|
||||||
if ($value) {
|
|
||||||
delete $hash->{DURATIONTIMER} if ( $hash->{DURATIONTIMER} );
|
|
||||||
RESIDENTStk_RemoveInternalTimer( "DurationTimer", $hash );
|
|
||||||
}
|
|
||||||
elsif ( !$value ) {
|
|
||||||
RESIDENTStk_RG_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 undef;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub RESIDENTS_Notify($$) {
|
|
||||||
my ( $hash, $dev ) = @_;
|
|
||||||
my $name = $hash->{NAME};
|
|
||||||
my $prefix = RESIDENTStk_GetPrefixFromType($name);
|
|
||||||
my $devName = $dev->{NAME};
|
|
||||||
my $devType = GetType($devName);
|
|
||||||
return "" if ( IsDisabled($name) or IsDisabled($devName) );
|
|
||||||
|
|
||||||
# process only ROOMMATE or GUEST devices
|
|
||||||
if ( $devType =~ /^ROOMMATE|GUEST$/ ) {
|
|
||||||
|
|
||||||
my $events = deviceEvents( $dev, 1 );
|
|
||||||
return "" unless ($events);
|
|
||||||
|
|
||||||
readingsBeginUpdate($hash);
|
|
||||||
|
|
||||||
foreach my $event ( @{$events} ) {
|
|
||||||
next unless ( defined($event) );
|
|
||||||
|
|
||||||
Log3 $hash, 5, "RESIDENTS " . $name . ": processing event - $event";
|
|
||||||
|
|
||||||
# state changed
|
|
||||||
if ( $event =~ /^state:/
|
|
||||||
|| $event =~ /^wayhome:/
|
|
||||||
|| $event =~ /^wakeup:/ )
|
|
||||||
{
|
|
||||||
RESIDENTS_UpdateReadings($hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
# activity
|
|
||||||
if ( $event =~ /^state:/ ) {
|
|
||||||
|
|
||||||
# get user realname
|
|
||||||
my $aliasAttr = "group";
|
|
||||||
$aliasAttr = "alias" if ( $prefix eq "rg_" );
|
|
||||||
my $realname =
|
|
||||||
AttrVal( $devName,
|
|
||||||
AttrVal( $devName, $prefix . "realname", $aliasAttr ),
|
|
||||||
$devName );
|
|
||||||
|
|
||||||
# update statistics
|
|
||||||
readingsBulkUpdate( $hash, "lastActivity",
|
|
||||||
ReadingsVal( $devName, "state", $event ) );
|
|
||||||
readingsBulkUpdate( $hash, "lastActivityBy", $realname );
|
|
||||||
readingsBulkUpdate( $hash, "lastActivityByDev", $devName );
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
readingsEndUpdate( $hash, 1 );
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
delete $dev->{CHANGEDWITHSTATE};
|
|
||||||
my $events = deviceEvents( $dev, 1 );
|
|
||||||
return "" unless ($events);
|
|
||||||
|
|
||||||
# process wakeup devices
|
|
||||||
my @registeredWakeupdevs =
|
|
||||||
split( ',', AttrVal( $name, $prefix . "wakeupDevice", "" ) );
|
|
||||||
if (@registeredWakeupdevs) {
|
|
||||||
|
|
||||||
# if this is a notification of a registered wakeup device
|
|
||||||
if ( grep { m/^$devName$/ } @registeredWakeupdevs ) {
|
|
||||||
|
|
||||||
foreach my $event ( @{$events} ) {
|
|
||||||
next unless ( defined($event) );
|
|
||||||
RESIDENTStk_wakeupSet( $devName, $event );
|
|
||||||
}
|
|
||||||
|
|
||||||
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 ( AttrVal( $wakeupDev, "wakeupResetSwitcher", "" ) eq $devName
|
|
||||||
&& IsDevice( $devName, "dummy" ) )
|
|
||||||
{
|
|
||||||
foreach my $event ( @{$events} ) {
|
|
||||||
next unless ( defined($event) );
|
|
||||||
RESIDENTStk_wakeupSet( $wakeupDev, $event )
|
|
||||||
unless ( $event =~ /^(?:state:\s*)?off$/i );
|
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
# module Fn ####################################################################
|
# module Fn ####################################################################
|
||||||
sub RESIDENTS_UpdateReadings (@) {
|
sub RESIDENTS_UpdateReadings (@) {
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
@ -1454,7 +891,7 @@ sub RESIDENTS_UpdateReadings (@) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# calculate duration timers
|
# calculate duration timers
|
||||||
RESIDENTStk_RG_DurationTimer( $hash, 1 );
|
RESIDENTStk_DurationTimer( $hash, 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
@ -7,905 +7,26 @@ use Data::Dumper;
|
|||||||
use Time::Local;
|
use Time::Local;
|
||||||
|
|
||||||
require RESIDENTStk;
|
require RESIDENTStk;
|
||||||
|
our (@RESIDENTStk_attr);
|
||||||
|
|
||||||
# initialize ##################################################################
|
# initialize ##################################################################
|
||||||
sub GUEST_Initialize($) {
|
sub GUEST_Initialize($) {
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
|
|
||||||
$hash->{DefFn} = "GUEST_Define";
|
$hash->{DefFn} = "RESIDENTStk_Define";
|
||||||
$hash->{UndefFn} = "GUEST_Undefine";
|
$hash->{UndefFn} = "RESIDENTStk_Undefine";
|
||||||
$hash->{SetFn} = "GUEST_Set";
|
$hash->{SetFn} = "RESIDENTStk_Set";
|
||||||
$hash->{AttrFn} = "RESIDENTStk_RG_Attr";
|
$hash->{AttrFn} = "RESIDENTStk_Attr";
|
||||||
$hash->{NotifyFn} = "RESIDENTStk_RG_Notify";
|
$hash->{NotifyFn} = "RESIDENTStk_Notify";
|
||||||
|
|
||||||
$hash->{AttrList} =
|
$hash->{AttrList} =
|
||||||
"disable:1,0 disabledForIntervals do_not_notify:1,0 rg_locationHome rg_locationWayhome rg_locationUnderway rg_autoGoneAfter:0,12,16,24,26,28,30,36,48,60 rg_showAllStates:0,1 rg_realname:group,alias rg_states:multiple-strict,home,gotosleep,asleep,awoken,absent,gone rg_locations rg_moods rg_moodDefault rg_moodSleepy rg_noDuration:0,1 rg_wakeupDevice rg_geofenceUUIDs rg_presenceDevices rg_lang:EN,DE "
|
"disable:1,0 disabledForIntervals do_not_notify:1,0 "
|
||||||
|
. "rg_states:multiple-strict,home,gotosleep,asleep,awoken,absent,none "
|
||||||
. $readingFnAttributes;
|
. $readingFnAttributes;
|
||||||
}
|
|
||||||
|
|
||||||
# regular Fn ##################################################################
|
foreach (@RESIDENTStk_attr) {
|
||||||
sub GUEST_Define($$) {
|
$hash->{AttrList} .= " rg_" . $_;
|
||||||
my ( $hash, $def ) = @_;
|
|
||||||
my @a = split( "[ \t][ \t]*", $def );
|
|
||||||
my $name = $hash->{NAME};
|
|
||||||
my $name_attr;
|
|
||||||
|
|
||||||
Log3 $name, 5, "GUEST $name: called function GUEST_Define()";
|
|
||||||
|
|
||||||
if ( int(@a) < 2 ) {
|
|
||||||
my $msg = "Wrong syntax: define <name> GUEST [RESIDENTS-DEVICE-NAMES]";
|
|
||||||
Log3 $name, 4, $msg;
|
|
||||||
return $msg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$hash->{NOTIFYDEV} = "";
|
|
||||||
|
|
||||||
$hash->{RESIDENTGROUPS} = defined( $a[2] ) ? $a[2] : "";
|
|
||||||
if ( defined( $hash->{RESIDENTGROUPS} ) ) {
|
|
||||||
foreach ( split( /,/, $hash->{RESIDENTGROUPS} ) ) {
|
|
||||||
RESIDENTStk_findResidentSlaves( $defs{$_} )
|
|
||||||
if ( IsDevice( $_, "RESIDENTS" ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# set reverse pointer
|
|
||||||
$modules{GUEST}{defptr}{$name} = \$hash;
|
|
||||||
|
|
||||||
readingsBeginUpdate($hash);
|
|
||||||
|
|
||||||
# set default settings on first define
|
|
||||||
if ( $init_done && !defined( $hash->{OLDDEF} ) ) {
|
|
||||||
GUEST_Attr( "init", $name, "rg_lang" );
|
|
||||||
|
|
||||||
my $aliasname = $name;
|
|
||||||
$aliasname =~ s/^rg_//;
|
|
||||||
$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";
|
|
||||||
$attr{$name}{group} = "Guests";
|
|
||||||
$attr{$name}{icon} = "scene_visit_guests";
|
|
||||||
$attr{$name}{rg_realname} = "alias";
|
|
||||||
$attr{$name}{sortby} = "1";
|
|
||||||
$attr{$name}{webCmd} = "state";
|
|
||||||
|
|
||||||
my $firstRgr = $hash->{RESIDENTGROUPS};
|
|
||||||
$firstRgr =~ s/,[\s\S]*$//gmi;
|
|
||||||
$attr{$name}{room} = $attr{$firstRgr}{room}
|
|
||||||
if ( exists( $attr{$firstRgr}{room} ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
# trigger for modified objects
|
|
||||||
readingsBulkUpdateIfChanged( $hash, "state",
|
|
||||||
ReadingsVal( $name, "state", "" ) );
|
|
||||||
|
|
||||||
readingsEndUpdate( $hash, 1 );
|
|
||||||
|
|
||||||
# run timers
|
|
||||||
InternalTimer(
|
|
||||||
gettimeofday() + 15,
|
|
||||||
"RESIDENTStk_RG_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, 4,
|
|
||||||
"RESIDENTStk $name: concurrent AttrFn already defined for dummy module ("
|
|
||||||
. $modules{dummy}{AttrFn}
|
|
||||||
. "). Some attribute based functions like auto-creations will not be available.";
|
|
||||||
}
|
|
||||||
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub GUEST_Undefine($$) {
|
|
||||||
my ( $hash, $name ) = @_;
|
|
||||||
|
|
||||||
RESIDENTStk_RG_StopInternalTimers($hash);
|
|
||||||
|
|
||||||
if ( defined( $hash->{RESIDENTGROUPS} ) ) {
|
|
||||||
my $old = $hash->{RESIDENTGROUPS};
|
|
||||||
delete $hash->{RESIDENTGROUPS};
|
|
||||||
foreach ( split( /,/, $old ) ) {
|
|
||||||
RESIDENTStk_findResidentSlaves( $defs{$_} )
|
|
||||||
if ( IsDevice( $_, "RESIDENTS" ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# release reverse pointer
|
|
||||||
delete $modules{GUEST}{defptr}{$name};
|
|
||||||
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub GUEST_Set($@);
|
|
||||||
|
|
||||||
sub GUEST_Set($@) {
|
|
||||||
my ( $hash, @a ) = @_;
|
|
||||||
my $name = $hash->{NAME};
|
|
||||||
my $state = ReadingsVal( $name, "state", "initialized" );
|
|
||||||
my $presence = ReadingsVal( $name, "presence", "undefined" );
|
|
||||||
my $mood = ReadingsVal( $name, "mood", "-" );
|
|
||||||
my $location = ReadingsVal( $name, "location", "undefined" );
|
|
||||||
my $silent = 0;
|
|
||||||
|
|
||||||
return if ( IsDisabled($name) );
|
|
||||||
|
|
||||||
Log3 $name, 5, "GUEST $name: called function GUEST_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 "create" );
|
|
||||||
}
|
|
||||||
|
|
||||||
# states
|
|
||||||
my $states = (
|
|
||||||
defined( $attr{$name}{rg_states} ) ? $attr{$name}{rg_states}
|
|
||||||
: (
|
|
||||||
defined( $attr{$name}{rg_showAllStates} )
|
|
||||||
&& $attr{$name}{rg_showAllStates} == 1
|
|
||||||
? "home,gotosleep,asleep,awoken,absent,none"
|
|
||||||
: "home,gotosleep,absent,none"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
$states = $state . "," . $states if ( $states !~ /$state/ );
|
|
||||||
$states =~ s/ /,/g;
|
|
||||||
|
|
||||||
# moods
|
|
||||||
my $moods = (
|
|
||||||
defined( $attr{$name}{rg_moods} )
|
|
||||||
? $attr{$name}{rg_moods} . ",toggle"
|
|
||||||
: "calm,relaxed,happy,excited,lonely,sad,bored,stressed,uncomfortable,sleepy,angry,toggle"
|
|
||||||
);
|
|
||||||
$moods = $mood . "," . $moods if ( $moods !~ /$mood/ );
|
|
||||||
$moods =~ s/ /,/g;
|
|
||||||
|
|
||||||
# locations
|
|
||||||
my $locations = (
|
|
||||||
defined( $attr{$name}{rg_locations} )
|
|
||||||
? $attr{$name}{rg_locations}
|
|
||||||
: ""
|
|
||||||
);
|
|
||||||
if ( $locations !~ /$location/
|
|
||||||
&& $locations ne "" )
|
|
||||||
{
|
|
||||||
$locations = ":" . $location . "," . $locations;
|
|
||||||
}
|
|
||||||
elsif ( $locations ne "" ) {
|
|
||||||
$locations = ":" . $locations;
|
|
||||||
}
|
|
||||||
$locations =~ s/ /,/g;
|
|
||||||
|
|
||||||
my $usage = "Unknown argument " . $a[1] . ", choose one of state:$states";
|
|
||||||
$usage .= " mood:$moods";
|
|
||||||
$usage .= " location$locations";
|
|
||||||
if ($adminMode) {
|
|
||||||
$usage .= " create:wakeuptimer";
|
|
||||||
$usage .= ",locationMap"
|
|
||||||
if ( ReadingsVal( $name, "locationLat", "-" ) ne "-"
|
|
||||||
&& ReadingsVal( $name, "locationLong", "-" ) ne "-" );
|
|
||||||
}
|
|
||||||
|
|
||||||
# silentSet
|
|
||||||
if ( $a[1] eq "silentSet" ) {
|
|
||||||
$silent = 1;
|
|
||||||
my $first = shift @a;
|
|
||||||
$a[0] = $first;
|
|
||||||
}
|
|
||||||
|
|
||||||
# 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 "none"
|
|
||||||
|| $a[1] eq "gone" )
|
|
||||||
{
|
|
||||||
my $newstate;
|
|
||||||
|
|
||||||
# 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 "none"
|
|
||||||
|| $a[2] eq "gone" )
|
|
||||||
)
|
|
||||||
{
|
|
||||||
$newstate = $a[2];
|
|
||||||
}
|
|
||||||
elsif ( defined( $a[2] ) ) {
|
|
||||||
return
|
|
||||||
"Invalid 2nd argument, choose one of home gotosleep asleep awoken absent none ";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$newstate = $a[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
$newstate = "none" if ( $newstate eq "gone" );
|
|
||||||
|
|
||||||
Log3 $name, 2, "GUEST set $name " . $newstate if ( !$silent );
|
|
||||||
|
|
||||||
# if state changed
|
|
||||||
if ( $state ne $newstate ) {
|
|
||||||
readingsBeginUpdate($hash);
|
|
||||||
|
|
||||||
readingsBulkUpdate( $hash, "lastState", $state );
|
|
||||||
readingsBulkUpdate( $hash, "state", $newstate );
|
|
||||||
|
|
||||||
my $datetime = TimeNow();
|
|
||||||
|
|
||||||
# reset mood
|
|
||||||
my $mood_default =
|
|
||||||
( defined( $attr{$name}{"rg_moodDefault"} ) )
|
|
||||||
? $attr{$name}{"rg_moodDefault"}
|
|
||||||
: "calm";
|
|
||||||
my $mood_sleepy =
|
|
||||||
( defined( $attr{$name}{"rg_moodSleepy"} ) )
|
|
||||||
? $attr{$name}{"rg_moodSleepy"}
|
|
||||||
: "sleepy";
|
|
||||||
|
|
||||||
if (
|
|
||||||
$mood ne "-"
|
|
||||||
&& ( $newstate eq "gone"
|
|
||||||
|| $newstate eq "none"
|
|
||||||
|| $newstate eq "absent"
|
|
||||||
|| $newstate eq "asleep" )
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Log3 $name, 4,
|
|
||||||
"GUEST $name: implicit mood change caused by state "
|
|
||||||
. $newstate;
|
|
||||||
GUEST_Set( $hash, $name, "silentSet", "mood", "-" );
|
|
||||||
}
|
|
||||||
|
|
||||||
elsif ( $mood ne $mood_sleepy
|
|
||||||
&& ( $newstate eq "gotosleep" || $newstate eq "awoken" ) )
|
|
||||||
{
|
|
||||||
Log3 $name, 4,
|
|
||||||
"GUEST $name: implicit mood change caused by state "
|
|
||||||
. $newstate;
|
|
||||||
GUEST_Set( $hash, $name, "silentSet", "mood", $mood_sleepy );
|
|
||||||
}
|
|
||||||
|
|
||||||
elsif ( ( $mood eq "-" || $mood eq $mood_sleepy )
|
|
||||||
&& $newstate eq "home" )
|
|
||||||
{
|
|
||||||
Log3 $name, 4,
|
|
||||||
"GUEST $name: implicit mood change caused by state "
|
|
||||||
. $newstate;
|
|
||||||
GUEST_Set( $hash, $name, "silentSet", "mood", $mood_default );
|
|
||||||
}
|
|
||||||
|
|
||||||
# if state 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"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
# calculate presence state
|
|
||||||
my $newpresence =
|
|
||||||
( $newstate ne "none"
|
|
||||||
&& $newstate ne "gone"
|
|
||||||
&& $newstate ne "absent" )
|
|
||||||
? "present"
|
|
||||||
: "absent";
|
|
||||||
|
|
||||||
# stop any running wakeup-timers in case state changed
|
|
||||||
my $wakeupState = ReadingsVal( $name, "wakeup", 0 );
|
|
||||||
if ( $wakeupState > 0 ) {
|
|
||||||
my $wakeupDeviceList = AttrVal( $name, "rg_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" ) {
|
|
||||||
Log3 $name, 4,
|
|
||||||
"ROOMMATE $name: ending wakeup-timer $wakeupDevice";
|
|
||||||
fhem "set $wakeupDevice:FILTER=running!=0 end";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Log3 $name, 4,
|
|
||||||
"ROOMMATE $name: stopping wakeup-timer $wakeupDevice";
|
|
||||||
fhem "set $wakeupDevice:FILTER=running!=0 stop";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# if presence changed
|
|
||||||
if ( $newpresence ne $presence ) {
|
|
||||||
readingsBulkUpdate( $hash, "presence", $newpresence );
|
|
||||||
|
|
||||||
# update location
|
|
||||||
my @location_home =
|
|
||||||
split( ' ', AttrVal( $name, "rg_locationHome", "home" ) );
|
|
||||||
my @location_underway =
|
|
||||||
split( ' ',
|
|
||||||
AttrVal( $name, "rg_locationUnderway", "underway" ) );
|
|
||||||
my @location_wayhome =
|
|
||||||
split( ' ',
|
|
||||||
AttrVal( $name, "rg_locationWayhome", "wayhome" ) );
|
|
||||||
my $searchstring = quotemeta($location);
|
|
||||||
|
|
||||||
if ( $newpresence eq "present" ) {
|
|
||||||
if ( !grep( m/^$searchstring$/, @location_home )
|
|
||||||
&& $location ne $location_home[0] )
|
|
||||||
{
|
|
||||||
Log3 $name, 4,
|
|
||||||
"GUEST $name: implicit location change caused by state "
|
|
||||||
. $newstate;
|
|
||||||
GUEST_Set( $hash, $name, "silentSet", "location",
|
|
||||||
$location_home[0] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if ( !$silent
|
|
||||||
&& !grep( m/^$searchstring$/, @location_underway )
|
|
||||||
&& $location ne $location_underway[0] )
|
|
||||||
{
|
|
||||||
Log3 $name, 4,
|
|
||||||
"GUEST $name: implicit location change caused by state "
|
|
||||||
. $newstate;
|
|
||||||
GUEST_Set( $hash, $name, "silentSet", "location",
|
|
||||||
$location_underway[0] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# reset wayhome
|
|
||||||
readingsBulkUpdateIfChanged( $hash, "wayhome", "0" );
|
|
||||||
|
|
||||||
# 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"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# adjust linked objects
|
|
||||||
if ( defined( $attr{$name}{"rg_passPresenceTo"} )
|
|
||||||
&& $attr{$name}{"rg_passPresenceTo"} ne "" )
|
|
||||||
{
|
|
||||||
my @linkedObjects =
|
|
||||||
split( ' ', $attr{$name}{"rg_passPresenceTo"} );
|
|
||||||
|
|
||||||
foreach my $object (@linkedObjects) {
|
|
||||||
if ( IsDevice( $object, "ROOMMATE|GUEST" )
|
|
||||||
&& $defs{$object} ne $name
|
|
||||||
&& ReadingsVal( $object, "state", "" ) ne "gone"
|
|
||||||
&& ReadingsVal( $object, "state", "" ) ne "none" )
|
|
||||||
{
|
|
||||||
fhem("set $object $newstate");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# clear readings if guest is gone
|
|
||||||
if ( $newstate eq "none" ) {
|
|
||||||
readingsBulkUpdate( $hash, "lastArrival", "-" )
|
|
||||||
if ( ReadingsVal( $name, "lastArrival", "-" ) ne "-" );
|
|
||||||
readingsBulkUpdate( $hash, "lastAwake", "-" )
|
|
||||||
if ( ReadingsVal( $name, "lastAwake", "-" ) ne "-" );
|
|
||||||
readingsBulkUpdate( $hash, "lastDurAbsence", "-" )
|
|
||||||
if ( ReadingsVal( $name, "lastDurAbsence", "-" ) ne "-" );
|
|
||||||
readingsBulkUpdate( $hash, "lastDurSleep", "-" )
|
|
||||||
if ( ReadingsVal( $name, "lastDurSleep", "-" ) ne "-" );
|
|
||||||
readingsBulkUpdate( $hash, "lastLocation", "-" )
|
|
||||||
if ( ReadingsVal( $name, "lastLocation", "-" ) ne "-" );
|
|
||||||
readingsBulkUpdate( $hash, "lastSleep", "-" )
|
|
||||||
if ( ReadingsVal( $name, "lastSleep", "-" ) ne "-" );
|
|
||||||
readingsBulkUpdate( $hash, "lastMood", "-" )
|
|
||||||
if ( ReadingsVal( $name, "lastMood", "-" ) ne "-" );
|
|
||||||
readingsBulkUpdate( $hash, "location", "-" )
|
|
||||||
if ( ReadingsVal( $name, "location", "-" ) ne "-" );
|
|
||||||
readingsBulkUpdate( $hash, "mood", "-" )
|
|
||||||
if ( ReadingsVal( $name, "mood", "-" ) ne "-" );
|
|
||||||
}
|
|
||||||
|
|
||||||
# calculate duration timers
|
|
||||||
RESIDENTStk_RG_DurationTimer( $hash, $silent );
|
|
||||||
|
|
||||||
readingsEndUpdate( $hash, 1 );
|
|
||||||
|
|
||||||
# enable or disable AutoGone timer
|
|
||||||
if ( $newstate eq "absent" ) {
|
|
||||||
RESIDENTStk_RG_AutoGone($hash);
|
|
||||||
}
|
|
||||||
elsif ( $state eq "absent" ) {
|
|
||||||
delete $hash->{AUTOGONE} if ( $hash->{AUTOGONE} );
|
|
||||||
RESIDENTStk_RemoveInternalTimer( "AutoGone", $hash );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# mood
|
|
||||||
elsif ( $a[1] eq "mood" ) {
|
|
||||||
if ( defined( $a[2] ) && $a[2] ne "" ) {
|
|
||||||
Log3 $name, 2, "GUEST set $name mood " . $a[2] if ( !$silent );
|
|
||||||
readingsBeginUpdate($hash) if ( !$silent );
|
|
||||||
|
|
||||||
if ( $a[2] eq "toggle"
|
|
||||||
&& ReadingsVal( $name, "lastMood", "" ) ne "" )
|
|
||||||
{
|
|
||||||
readingsBulkUpdate( $hash, "mood",
|
|
||||||
ReadingsVal( $name, "lastMood", "" ) );
|
|
||||||
readingsBulkUpdate( $hash, "lastMood", $mood );
|
|
||||||
}
|
|
||||||
elsif ( $mood ne $a[2] ) {
|
|
||||||
readingsBulkUpdate( $hash, "lastMood", $mood )
|
|
||||||
if ( $mood ne "-" );
|
|
||||||
readingsBulkUpdate( $hash, "mood", $a[2] );
|
|
||||||
}
|
|
||||||
|
|
||||||
readingsEndUpdate( $hash, 1 ) if ( !$silent );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return "Invalid 2nd argument, choose one of mood toggle";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# location
|
|
||||||
elsif ( $a[1] eq "location" ) {
|
|
||||||
if ( defined( $a[2] ) && $a[2] ne "" ) {
|
|
||||||
Log3 $name, 2, "GUEST set $name location " . $a[2] if ( !$silent );
|
|
||||||
|
|
||||||
if ( $location ne $a[2] ) {
|
|
||||||
my $searchstring;
|
|
||||||
|
|
||||||
readingsBeginUpdate($hash) if ( !$silent );
|
|
||||||
|
|
||||||
# read attributes
|
|
||||||
my @location_home =
|
|
||||||
split( ' ', AttrVal( $name, "rg_locationHome", "home" ) );
|
|
||||||
my @location_underway =
|
|
||||||
split( ' ',
|
|
||||||
AttrVal( $name, "rg_locationUnderway", "underway" ) );
|
|
||||||
my @location_wayhome =
|
|
||||||
split( ' ',
|
|
||||||
AttrVal( $name, "rg_locationWayhome", "wayhome" ) );
|
|
||||||
|
|
||||||
$searchstring = quotemeta($location);
|
|
||||||
readingsBulkUpdate( $hash, "lastLocation", $location )
|
|
||||||
if ( $location ne "wayhome"
|
|
||||||
&& !grep( m/^$searchstring$/, @location_underway ) );
|
|
||||||
readingsBulkUpdate( $hash, "location", $a[2] )
|
|
||||||
if ( $a[2] ne "wayhome" );
|
|
||||||
|
|
||||||
# wayhome detection
|
|
||||||
$searchstring = quotemeta($location);
|
|
||||||
if (
|
|
||||||
(
|
|
||||||
$a[2] eq "wayhome"
|
|
||||||
|| grep( m/^$searchstring$/, @location_wayhome )
|
|
||||||
)
|
|
||||||
&& ( $presence eq "absent" )
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Log3 $name, 3,
|
|
||||||
"GUEST $name: on way back home from $location";
|
|
||||||
readingsBulkUpdateIfChanged( $hash, "wayhome", "1" );
|
|
||||||
}
|
|
||||||
|
|
||||||
readingsEndUpdate( $hash, 1 ) if ( !$silent );
|
|
||||||
|
|
||||||
# auto-updates
|
|
||||||
$searchstring = quotemeta( $a[2] );
|
|
||||||
if (
|
|
||||||
(
|
|
||||||
$a[2] eq "home"
|
|
||||||
|| grep( m/^$searchstring$/, @location_home )
|
|
||||||
)
|
|
||||||
&& $state ne "home"
|
|
||||||
&& $state ne "gotosleep"
|
|
||||||
&& $state ne "asleep"
|
|
||||||
&& $state ne "awoken"
|
|
||||||
&& $state ne "initialized"
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Log3 $name, 4,
|
|
||||||
"GUEST $name: implicit state change caused by location "
|
|
||||||
. $a[2];
|
|
||||||
GUEST_Set( $hash, $name, "silentSet", "state", "home" );
|
|
||||||
}
|
|
||||||
elsif (
|
|
||||||
(
|
|
||||||
$a[2] eq "underway"
|
|
||||||
|| grep( m/^$searchstring$/, @location_underway )
|
|
||||||
)
|
|
||||||
&& $state ne "gone"
|
|
||||||
&& $state ne "none"
|
|
||||||
&& $state ne "absent"
|
|
||||||
&& $state ne "initialized"
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Log3 $name, 4,
|
|
||||||
"GUEST $name: implicit state change caused by location "
|
|
||||||
. $a[2];
|
|
||||||
GUEST_Set( $hash, $name, "silentSet", "state", "absent" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return "Invalid 2nd argument, choose one of location ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# create
|
|
||||||
elsif ( lc( $a[1] ) eq "create" ) {
|
|
||||||
if ( !defined( $a[2] ) || $a[2] !~ /^(wakeuptimer|locationMap)$/i ) {
|
|
||||||
return
|
|
||||||
"Invalid 2nd argument, choose one of wakeuptimer locationMap ";
|
|
||||||
}
|
|
||||||
elsif ( lc( $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 GUEST 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 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 wakeupResetdays:multiple-strict,0,1,2,3,4,5,6 wakeupDays:multiple-strict,0,1,2,3,4,5,6 wakeupHolidays:,andHoliday,orHoliday,andNoHoliday,orNoHoliday wakeupEnforced:0,1,2,3";
|
|
||||||
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, "rg_wakeupDevice", 0 );
|
|
||||||
if ( !$wakeupDevice ) {
|
|
||||||
fhem "attr $name rg_wakeupDevice $wakeuptimerName";
|
|
||||||
}
|
|
||||||
elsif ( $wakeupDevice !~ /(.*,?)($wakeuptimerName)(.*,?)/ )
|
|
||||||
{
|
|
||||||
fhem "attr $name rg_wakeupDevice "
|
|
||||||
. $wakeupDevice
|
|
||||||
. ",$wakeuptimerName";
|
|
||||||
}
|
|
||||||
|
|
||||||
# trigger first update
|
|
||||||
RESIDENTStk_findDummySlaves($name);
|
|
||||||
fhem "sleep 1;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.";
|
|
||||||
}
|
|
||||||
elsif ( lc( $a[2] ) eq "locationmap" ) {
|
|
||||||
my $locationmapName = $name . "_map";
|
|
||||||
|
|
||||||
if ( IsDevice($locationmapName) ) {
|
|
||||||
return
|
|
||||||
"Device $locationmapName existing already, delete it first to have it re-created.";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
my $sortby = AttrVal( $name, "sortby", -1 );
|
|
||||||
$sortby++;
|
|
||||||
|
|
||||||
# create new weblink device
|
|
||||||
fhem "define $locationmapName weblink htmlCode {
|
|
||||||
'<ul style=\"width: 400px;; overflow: hidden;; height: 300px;;\">
|
|
||||||
<iframe name=\"$locationmapName\" src=\"https://www.google.com/maps/embed/v1/place?key=AIzaSyB66DvcpbXJ5eWgIkzxpUN2s_9l3_6fegM&q='
|
|
||||||
.ReadingsVal('$name','locationLat','')
|
|
||||||
.','
|
|
||||||
.ReadingsVal('$name','locationLong','')
|
|
||||||
.'&zoom=13\" width=\"480\" height=\"480\" frameborder=\"0\" style=\"border:0;; margin-top: -165px;; margin-left: -135px;;\">
|
|
||||||
</iframe>
|
|
||||||
</ul>'
|
|
||||||
}";
|
|
||||||
fhem "attr $locationmapName alias Current Location";
|
|
||||||
fhem
|
|
||||||
"attr $locationmapName comment Auto-created by GUEST module";
|
|
||||||
fhem "attr $locationmapName group " . $attr{$name}{group}
|
|
||||||
if ( defined( $attr{$name}{group} ) );
|
|
||||||
fhem "attr $locationmapName room " . $attr{$name}{room}
|
|
||||||
if ( defined( $attr{$name}{room} ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Weblink device $locationmapName was created.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# return usage hint
|
|
||||||
else {
|
|
||||||
return $usage;
|
|
||||||
}
|
|
||||||
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
|
|
||||||
# module Fn ####################################################################
|
|
||||||
sub GUEST_SetLocation($$$;$$$$$$) {
|
|
||||||
my ( $name, $location, $trigger, $id, $time, $lat, $long, $address,
|
|
||||||
$device ) = @_;
|
|
||||||
my $hash = $defs{$name};
|
|
||||||
my $state = ReadingsVal( $name, "state", "initialized" );
|
|
||||||
my $presence = ReadingsVal( $name, "presence", "present" );
|
|
||||||
my $currLocation = ReadingsVal( $name, "location", "-" );
|
|
||||||
my $currWayhome = ReadingsVal( $name, "wayhome", "0" );
|
|
||||||
my $currLat = ReadingsVal( $name, "locationLat", "-" );
|
|
||||||
my $currLong = ReadingsVal( $name, "locationLong", "-" );
|
|
||||||
my $currAddr = ReadingsVal( $name, "locationAddr", "" );
|
|
||||||
$id = "-" if ( !$id || $id eq "" );
|
|
||||||
$lat = "-" if ( !$lat || $lat eq "" );
|
|
||||||
$long = "-" if ( !$long || $long eq "" );
|
|
||||||
$address = "" if ( !$address );
|
|
||||||
$time = "" if ( !$time );
|
|
||||||
$device = "" if ( !$device );
|
|
||||||
|
|
||||||
Log3 $name, 5,
|
|
||||||
"GUEST $name: received location information: id=$id name=$location trig=$trigger date=$time lat=$lat long=$long address:$address device=$device";
|
|
||||||
|
|
||||||
my $searchstring;
|
|
||||||
|
|
||||||
readingsBeginUpdate($hash);
|
|
||||||
|
|
||||||
# read attributes
|
|
||||||
my @location_home =
|
|
||||||
split( ' ', AttrVal( $name, "rg_locationHome", "home" ) );
|
|
||||||
my @location_underway =
|
|
||||||
split( ' ', AttrVal( $name, "rg_locationUnderway", "underway" ) );
|
|
||||||
my @location_wayhome =
|
|
||||||
split( ' ', AttrVal( $name, "rg_locationWayhome", "wayhome" ) );
|
|
||||||
|
|
||||||
$searchstring = quotemeta($location);
|
|
||||||
|
|
||||||
# update locationPresence
|
|
||||||
readingsBulkUpdate( $hash, "locationPresence", "present" )
|
|
||||||
if ( $trigger == 1 );
|
|
||||||
readingsBulkUpdate( $hash, "locationPresence", "absent" )
|
|
||||||
if ( $trigger == 0 );
|
|
||||||
|
|
||||||
# check for implicit state change
|
|
||||||
#
|
|
||||||
my $stateChange = 0;
|
|
||||||
|
|
||||||
# home
|
|
||||||
if ( $location eq "home" || grep( m/^$searchstring$/, @location_home ) ) {
|
|
||||||
Log3 $name, 5, "GUEST $name: received signal from home location";
|
|
||||||
|
|
||||||
# home
|
|
||||||
if ( $state ne "home"
|
|
||||||
&& $state ne "gotosleep"
|
|
||||||
&& $state ne "asleep"
|
|
||||||
&& $state ne "awoken"
|
|
||||||
&& $trigger eq "1" )
|
|
||||||
{
|
|
||||||
$stateChange = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
# absent
|
|
||||||
elsif ($state ne "gone"
|
|
||||||
&& $state ne "none"
|
|
||||||
&& $state ne "absent"
|
|
||||||
&& $trigger eq "0" )
|
|
||||||
{
|
|
||||||
$stateChange = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# underway
|
|
||||||
elsif ($location eq "underway"
|
|
||||||
|| $location eq "wayhome"
|
|
||||||
|| grep( m/^$searchstring$/, @location_underway )
|
|
||||||
|| grep( m/^$searchstring$/, @location_wayhome ) )
|
|
||||||
{
|
|
||||||
Log3 $name, 5, "GUEST $name: received signal from underway location";
|
|
||||||
|
|
||||||
# absent
|
|
||||||
$stateChange = 2
|
|
||||||
if ( $state ne "gone"
|
|
||||||
&& $state ne "none"
|
|
||||||
&& $state ne "absent" );
|
|
||||||
}
|
|
||||||
|
|
||||||
# wayhome
|
|
||||||
if (
|
|
||||||
$location eq "wayhome"
|
|
||||||
|| ( grep( m/^$searchstring$/, @location_wayhome )
|
|
||||||
&& $trigger eq "0" )
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Log3 $name, 5, "GUEST $name: wayhome signal received";
|
|
||||||
|
|
||||||
# wayhome=true
|
|
||||||
if (
|
|
||||||
(
|
|
||||||
( $location eq "wayhome" && $trigger eq "1" )
|
|
||||||
|| ( $location ne "wayhome" && $trigger eq "0" )
|
|
||||||
)
|
|
||||||
&& ReadingsVal( $name, "wayhome", "0" ) ne "1"
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Log3 $name, 3, "GUEST $name: on way back home from $location";
|
|
||||||
readingsBulkUpdate( $hash, "wayhome", "1" );
|
|
||||||
}
|
|
||||||
|
|
||||||
# wayhome=false
|
|
||||||
elsif ($location eq "wayhome"
|
|
||||||
&& $trigger eq "0"
|
|
||||||
&& ReadingsVal( $name, "wayhome", "0" ) ne "0" )
|
|
||||||
{
|
|
||||||
Log3 $name, 3,
|
|
||||||
"GUEST $name: seems not to be on way back home anymore";
|
|
||||||
readingsBulkUpdate( $hash, "wayhome", "0" );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# activate wayhome tracing when reaching another location while wayhome=1
|
|
||||||
elsif ( $stateChange == 0 && $trigger == 1 && $currWayhome == 1 ) {
|
|
||||||
Log3 $name, 3,
|
|
||||||
"GUEST $name: seems to stay at $location before coming home";
|
|
||||||
readingsBulkUpdate( $hash, "wayhome", "2" );
|
|
||||||
}
|
|
||||||
|
|
||||||
# revert wayhome during active wayhome tracing
|
|
||||||
elsif ( $stateChange == 0 && $trigger == 0 && $currWayhome == 2 ) {
|
|
||||||
Log3 $name, 3, "GUEST $name: finally on way back home from $location";
|
|
||||||
readingsBulkUpdate( $hash, "wayhome", "1" );
|
|
||||||
}
|
|
||||||
|
|
||||||
my $currLongDiff = 0;
|
|
||||||
my $currLatDiff = 0;
|
|
||||||
$currLongDiff =
|
|
||||||
maxNum( ReadingsVal( $name, "lastLocationLong", 0 ), $currLong ) -
|
|
||||||
minNum( ReadingsVal( $name, "lastLocationLong", 0 ), $currLong )
|
|
||||||
if ( $currLong ne "-" );
|
|
||||||
$currLatDiff =
|
|
||||||
maxNum( ReadingsVal( $name, "lastLocationLat", 0 ), $currLat ) -
|
|
||||||
minNum( ReadingsVal( $name, "lastLocationLat", 0 ), $currLat )
|
|
||||||
if ( $currLat ne "-" );
|
|
||||||
|
|
||||||
if (
|
|
||||||
$trigger == 1
|
|
||||||
&& ( $stateChange > 0
|
|
||||||
|| ReadingsVal( $name, "lastLocation", "-" ) ne $currLocation
|
|
||||||
|| $currLongDiff > 0.00002
|
|
||||||
|| $currLatDiff > 0.00002 )
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Log3 $name, 5, "GUEST $name: archiving last known location";
|
|
||||||
readingsBulkUpdate( $hash, "lastLocationLat", $currLat );
|
|
||||||
readingsBulkUpdate( $hash, "lastLocationLong", $currLong );
|
|
||||||
readingsBulkUpdate( $hash, "lastLocationAddr", $currAddr )
|
|
||||||
if ( $currAddr ne "" );
|
|
||||||
readingsBulkUpdate( $hash, "lastLocation", $currLocation );
|
|
||||||
}
|
|
||||||
|
|
||||||
readingsBulkUpdate( $hash, "locationLat", $lat );
|
|
||||||
readingsBulkUpdate( $hash, "locationLong", $long );
|
|
||||||
|
|
||||||
if ( $address ne "" ) {
|
|
||||||
readingsBulkUpdate( $hash, "locationAddr", $address );
|
|
||||||
}
|
|
||||||
elsif ( $currAddr ne "" ) {
|
|
||||||
readingsBulkUpdate( $hash, "locationAddr", "-" );
|
|
||||||
}
|
|
||||||
|
|
||||||
readingsBulkUpdate( $hash, "location", $location );
|
|
||||||
|
|
||||||
readingsEndUpdate( $hash, 1 );
|
|
||||||
|
|
||||||
# trigger state change
|
|
||||||
if ( $stateChange > 0 ) {
|
|
||||||
Log3 $name, 4,
|
|
||||||
"GUEST $name: implicit state change caused by location " . $location;
|
|
||||||
|
|
||||||
GUEST_Set( $hash, $name, "silentSet", "state", "home" )
|
|
||||||
if $stateChange == 1;
|
|
||||||
GUEST_Set( $hash, $name, "silentSet", "state", "absent" )
|
|
||||||
if $stateChange == 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
@ -7,883 +7,26 @@ use Data::Dumper;
|
|||||||
use Time::Local;
|
use Time::Local;
|
||||||
|
|
||||||
require RESIDENTStk;
|
require RESIDENTStk;
|
||||||
|
our (@RESIDENTStk_attr);
|
||||||
|
|
||||||
# initialize ##################################################################
|
# initialize ##################################################################
|
||||||
sub ROOMMATE_Initialize($) {
|
sub ROOMMATE_Initialize($) {
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
|
|
||||||
$hash->{DefFn} = "ROOMMATE_Define";
|
$hash->{DefFn} = "RESIDENTStk_Define";
|
||||||
$hash->{UndefFn} = "ROOMMATE_Undefine";
|
$hash->{UndefFn} = "RESIDENTStk_Undefine";
|
||||||
$hash->{SetFn} = "ROOMMATE_Set";
|
$hash->{SetFn} = "RESIDENTStk_Set";
|
||||||
$hash->{AttrFn} = "RESIDENTStk_RG_Attr";
|
$hash->{AttrFn} = "RESIDENTStk_Attr";
|
||||||
$hash->{NotifyFn} = "RESIDENTStk_RG_Notify";
|
$hash->{NotifyFn} = "RESIDENTStk_Notify";
|
||||||
|
|
||||||
$hash->{AttrList} =
|
$hash->{AttrList} =
|
||||||
"disable:1,0 disabledForIntervals do_not_notify:1,0 rr_locationHome rr_locationWayhome rr_locationUnderway rr_autoGoneAfter:0,12,16,24,26,28,30,36,48,60 rr_showAllStates:0,1 rr_realname:group,alias rr_states:multiple-strict,home,gotosleep,asleep,awoken,absent,gone rr_locations rr_moods rr_moodDefault rr_moodSleepy rr_passPresenceTo rr_noDuration:0,1 rr_wakeupDevice rr_geofenceUUIDs rr_presenceDevices rr_lang:EN,DE "
|
"disable:1,0 disabledForIntervals do_not_notify:1,0 "
|
||||||
|
. "rr_states:multiple-strict,home,gotosleep,asleep,awoken,absent,gone "
|
||||||
. $readingFnAttributes;
|
. $readingFnAttributes;
|
||||||
}
|
|
||||||
|
|
||||||
# regular Fn ##################################################################
|
foreach (@RESIDENTStk_attr) {
|
||||||
sub ROOMMATE_Define($$) {
|
$hash->{AttrList} .= " rr_" . $_;
|
||||||
my ( $hash, $def ) = @_;
|
|
||||||
my @a = split( "[ \t][ \t]*", $def );
|
|
||||||
my $name = $hash->{NAME};
|
|
||||||
my $name_attr;
|
|
||||||
|
|
||||||
Log3 $name, 5, "ROOMMATE $name: called function ROOMMATE_Define()";
|
|
||||||
|
|
||||||
if ( int(@a) < 2 ) {
|
|
||||||
my $msg =
|
|
||||||
"Wrong syntax: define <name> ROOMMATE [RESIDENTS-DEVICE-NAMES]";
|
|
||||||
Log3 $name, 4, $msg;
|
|
||||||
return $msg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$hash->{NOTIFYDEV} = "";
|
|
||||||
|
|
||||||
$hash->{RESIDENTGROUPS} = defined( $a[2] ) ? $a[2] : "";
|
|
||||||
if ( defined( $hash->{RESIDENTGROUPS} ) ) {
|
|
||||||
foreach ( split( /,/, $hash->{RESIDENTGROUPS} ) ) {
|
|
||||||
RESIDENTStk_findResidentSlaves( $defs{$_} )
|
|
||||||
if ( IsDevice( $_, "RESIDENTS" ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# set reverse pointer
|
|
||||||
$modules{ROOMMATE}{defptr}{$name} = \$hash;
|
|
||||||
|
|
||||||
readingsBeginUpdate($hash);
|
|
||||||
|
|
||||||
# set default settings on first define
|
|
||||||
if ( $init_done && !defined( $hash->{OLDDEF} ) ) {
|
|
||||||
ROOMMATE_Attr( "init", $name, "rr_lang" );
|
|
||||||
|
|
||||||
my $groupname = $name;
|
|
||||||
$groupname =~ s/^rr_//;
|
|
||||||
$attr{$name}{group} = $groupname;
|
|
||||||
|
|
||||||
$attr{$name}{alias} = "Status";
|
|
||||||
$attr{$name}{icon} = "people_sensor";
|
|
||||||
$attr{$name}{rr_realname} = "group";
|
|
||||||
$attr{$name}{sortby} = "1";
|
|
||||||
$attr{$name}{webCmd} = "state";
|
|
||||||
|
|
||||||
my $firstRgr = $hash->{RESIDENTGROUPS};
|
|
||||||
$firstRgr =~ s/,[\s\S]*$//gmi;
|
|
||||||
$attr{$name}{room} = $attr{$firstRgr}{room}
|
|
||||||
if ( exists( $attr{$firstRgr}{room} ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
# trigger for modified objects
|
|
||||||
readingsBulkUpdateIfChanged( $hash, "state",
|
|
||||||
ReadingsVal( $name, "state", "" ) );
|
|
||||||
|
|
||||||
readingsEndUpdate( $hash, 1 );
|
|
||||||
|
|
||||||
# run timers
|
|
||||||
InternalTimer(
|
|
||||||
gettimeofday() + 15,
|
|
||||||
"RESIDENTStk_RG_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, 4,
|
|
||||||
"RESIDENTStk $name: concurrent AttrFn already defined for dummy module ("
|
|
||||||
. $modules{dummy}{AttrFn}
|
|
||||||
. "). Some attribute based functions like auto-creations will not be available.";
|
|
||||||
}
|
|
||||||
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub ROOMMATE_Undefine($$) {
|
|
||||||
my ( $hash, $name ) = @_;
|
|
||||||
|
|
||||||
RESIDENTStk_RG_StopInternalTimers($hash);
|
|
||||||
|
|
||||||
if ( defined( $hash->{RESIDENTGROUPS} ) ) {
|
|
||||||
my $old = $hash->{RESIDENTGROUPS};
|
|
||||||
delete $hash->{RESIDENTGROUPS};
|
|
||||||
foreach ( split( /,/, $old ) ) {
|
|
||||||
RESIDENTStk_findResidentSlaves( $defs{$_} )
|
|
||||||
if ( IsDevice( $_, "RESIDENTS" ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# release reverse pointer
|
|
||||||
delete $modules{ROOMMATE}{defptr}{$name};
|
|
||||||
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub ROOMMATE_Set($@);
|
|
||||||
|
|
||||||
sub ROOMMATE_Set($@) {
|
|
||||||
my ( $hash, @a ) = @_;
|
|
||||||
my $name = $hash->{NAME};
|
|
||||||
my $state = ReadingsVal( $name, "state", "initialized" );
|
|
||||||
my $presence = ReadingsVal( $name, "presence", "undefined" );
|
|
||||||
my $mood = ReadingsVal( $name, "mood", "-" );
|
|
||||||
my $location = ReadingsVal( $name, "location", "undefined" );
|
|
||||||
my $silent = 0;
|
|
||||||
|
|
||||||
return if ( IsDisabled($name) );
|
|
||||||
|
|
||||||
Log3 $name, 5, "ROOMMATE $name: called function ROOMMATE_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 "create" );
|
|
||||||
}
|
|
||||||
|
|
||||||
# states
|
|
||||||
my $states = (
|
|
||||||
defined( $attr{$name}{rr_states} ) ? $attr{$name}{rr_states}
|
|
||||||
: (
|
|
||||||
defined( $attr{$name}{rr_showAllStates} )
|
|
||||||
&& $attr{$name}{rr_showAllStates} == 1
|
|
||||||
? "home,gotosleep,asleep,awoken,absent,gone"
|
|
||||||
: "home,gotosleep,absent,gone"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
$states = $state . "," . $states if ( $states !~ /$state/ );
|
|
||||||
$states =~ s/ /,/g;
|
|
||||||
|
|
||||||
# moods
|
|
||||||
my $moods = (
|
|
||||||
defined( $attr{$name}{rr_moods} )
|
|
||||||
? $attr{$name}{rr_moods} . ",toggle"
|
|
||||||
: "calm,relaxed,happy,excited,lonely,sad,bored,stressed,uncomfortable,sleepy,angry,toggle"
|
|
||||||
);
|
|
||||||
$moods = $mood . "," . $moods if ( $moods !~ /$mood/ );
|
|
||||||
$moods =~ s/ /,/g;
|
|
||||||
|
|
||||||
# locations
|
|
||||||
my $locations = (
|
|
||||||
defined( $attr{$name}{rr_locations} )
|
|
||||||
? $attr{$name}{rr_locations}
|
|
||||||
: ""
|
|
||||||
);
|
|
||||||
if ( $locations !~ /$location/
|
|
||||||
&& $locations ne "" )
|
|
||||||
{
|
|
||||||
$locations = ":" . $location . "," . $locations;
|
|
||||||
}
|
|
||||||
elsif ( $locations ne "" ) {
|
|
||||||
$locations = ":" . $locations;
|
|
||||||
}
|
|
||||||
$locations =~ s/ /,/g;
|
|
||||||
|
|
||||||
my $usage = "Unknown argument " . $a[1] . ", choose one of state:$states";
|
|
||||||
$usage .= " mood:$moods";
|
|
||||||
$usage .= " location$locations";
|
|
||||||
if ($adminMode) {
|
|
||||||
$usage .= " create:wakeuptimer";
|
|
||||||
$usage .= ",locationMap"
|
|
||||||
if ( ReadingsVal( $name, "locationLat", "-" ) ne "-"
|
|
||||||
&& ReadingsVal( $name, "locationLong", "-" ) ne "-" );
|
|
||||||
}
|
|
||||||
|
|
||||||
# silentSet
|
|
||||||
if ( $a[1] eq "silentSet" ) {
|
|
||||||
$silent = 1;
|
|
||||||
my $first = shift @a;
|
|
||||||
$a[0] = $first;
|
|
||||||
}
|
|
||||||
|
|
||||||
# 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;
|
|
||||||
|
|
||||||
# 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, "ROOMMATE set $name " . $newstate if ( !$silent );
|
|
||||||
|
|
||||||
# if state changed
|
|
||||||
if ( $state ne $newstate ) {
|
|
||||||
readingsBeginUpdate($hash);
|
|
||||||
|
|
||||||
readingsBulkUpdate( $hash, "lastState", $state );
|
|
||||||
readingsBulkUpdate( $hash, "state", $newstate );
|
|
||||||
|
|
||||||
my $datetime = TimeNow();
|
|
||||||
|
|
||||||
# reset mood
|
|
||||||
my $mood_default =
|
|
||||||
( defined( $attr{$name}{"rr_moodDefault"} ) )
|
|
||||||
? $attr{$name}{"rr_moodDefault"}
|
|
||||||
: "calm";
|
|
||||||
my $mood_sleepy =
|
|
||||||
( defined( $attr{$name}{"rr_moodSleepy"} ) )
|
|
||||||
? $attr{$name}{"rr_moodSleepy"}
|
|
||||||
: "sleepy";
|
|
||||||
|
|
||||||
if (
|
|
||||||
$mood ne "-"
|
|
||||||
&& ( $newstate eq "gone"
|
|
||||||
|| $newstate eq "none"
|
|
||||||
|| $newstate eq "absent"
|
|
||||||
|| $newstate eq "asleep" )
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Log3 $name, 4,
|
|
||||||
"ROOMMATE $name: implicit mood change caused by state "
|
|
||||||
. $newstate;
|
|
||||||
ROOMMATE_Set( $hash, $name, "silentSet", "mood", "-" );
|
|
||||||
}
|
|
||||||
|
|
||||||
elsif ( $mood ne $mood_sleepy
|
|
||||||
&& ( $newstate eq "gotosleep" || $newstate eq "awoken" ) )
|
|
||||||
{
|
|
||||||
Log3 $name, 4,
|
|
||||||
"ROOMMATE $name: implicit mood change caused by state "
|
|
||||||
. $newstate;
|
|
||||||
ROOMMATE_Set( $hash, $name, "silentSet", "mood", $mood_sleepy );
|
|
||||||
}
|
|
||||||
|
|
||||||
elsif ( ( $mood eq "-" || $mood eq $mood_sleepy )
|
|
||||||
&& $newstate eq "home" )
|
|
||||||
{
|
|
||||||
Log3 $name, 4,
|
|
||||||
"ROOMMATE $name: implicit mood change caused by state "
|
|
||||||
. $newstate;
|
|
||||||
ROOMMATE_Set( $hash, $name, "silentSet", "mood",
|
|
||||||
$mood_default );
|
|
||||||
}
|
|
||||||
|
|
||||||
# if state 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"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
# calculate presence state
|
|
||||||
my $newpresence =
|
|
||||||
( $newstate ne "none"
|
|
||||||
&& $newstate ne "gone"
|
|
||||||
&& $newstate ne "absent" )
|
|
||||||
? "present"
|
|
||||||
: "absent";
|
|
||||||
|
|
||||||
# stop any running wakeup-timers in case state changed
|
|
||||||
my $wakeupState = ReadingsVal( $name, "wakeup", 0 );
|
|
||||||
if ( $wakeupState > 0 ) {
|
|
||||||
my $wakeupDeviceList = AttrVal( $name, "rr_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" ) {
|
|
||||||
Log3 $name, 4,
|
|
||||||
"ROOMMATE $name: ending wakeup-timer $wakeupDevice";
|
|
||||||
fhem "set $wakeupDevice:FILTER=running!=0 end";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Log3 $name, 4,
|
|
||||||
"ROOMMATE $name: stopping wakeup-timer $wakeupDevice";
|
|
||||||
fhem "set $wakeupDevice:FILTER=running!=0 stop";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# if presence changed
|
|
||||||
if ( $newpresence ne $presence ) {
|
|
||||||
readingsBulkUpdate( $hash, "presence", $newpresence );
|
|
||||||
|
|
||||||
# update location
|
|
||||||
my @location_home =
|
|
||||||
split( ' ', AttrVal( $name, "rr_locationHome", "home" ) );
|
|
||||||
my @location_underway =
|
|
||||||
split( ' ',
|
|
||||||
AttrVal( $name, "rr_locationUnderway", "underway" ) );
|
|
||||||
my @location_wayhome =
|
|
||||||
split( ' ',
|
|
||||||
AttrVal( $name, "rr_locationWayhome", "wayhome" ) );
|
|
||||||
my $searchstring = quotemeta($location);
|
|
||||||
|
|
||||||
if ( !$silent && $newpresence eq "present" ) {
|
|
||||||
if ( !grep( m/^$searchstring$/, @location_home )
|
|
||||||
&& $location ne $location_home[0] )
|
|
||||||
{
|
|
||||||
Log3 $name, 4,
|
|
||||||
"ROOMMATE $name: implicit location change caused by state "
|
|
||||||
. $newstate;
|
|
||||||
ROOMMATE_Set( $hash, $name, "silentSet", "location",
|
|
||||||
$location_home[0] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if ( !$silent
|
|
||||||
&& !grep( m/^$searchstring$/, @location_underway )
|
|
||||||
&& $location ne $location_underway[0] )
|
|
||||||
{
|
|
||||||
Log3 $name, 4,
|
|
||||||
"ROOMMATE $name: implicit location change caused by state "
|
|
||||||
. $newstate;
|
|
||||||
ROOMMATE_Set( $hash, $name, "silentSet", "location",
|
|
||||||
$location_underway[0] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# reset wayhome
|
|
||||||
readingsBulkUpdateIfChanged( $hash, "wayhome", "0" );
|
|
||||||
|
|
||||||
# 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"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# adjust linked objects
|
|
||||||
if ( defined( $attr{$name}{"rr_passPresenceTo"} )
|
|
||||||
&& $attr{$name}{"rr_passPresenceTo"} ne "" )
|
|
||||||
{
|
|
||||||
my @linkedObjects =
|
|
||||||
split( ' ', $attr{$name}{"rr_passPresenceTo"} );
|
|
||||||
|
|
||||||
foreach my $object (@linkedObjects) {
|
|
||||||
if ( IsDevice( $object, "ROOMMATE|GUEST" )
|
|
||||||
&& $defs{$object} ne $name
|
|
||||||
&& ReadingsVal( $object, "state", "" ) ne "gone"
|
|
||||||
&& ReadingsVal( $object, "state", "" ) ne "none" )
|
|
||||||
{
|
|
||||||
fhem("set $object $newstate");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# calculate duration timers
|
|
||||||
RESIDENTStk_RG_DurationTimer( $hash, $silent );
|
|
||||||
|
|
||||||
readingsEndUpdate( $hash, 1 );
|
|
||||||
|
|
||||||
# enable or disable AutoGone timer
|
|
||||||
if ( $newstate eq "absent" ) {
|
|
||||||
RESIDENTStk_RG_AutoGone($hash);
|
|
||||||
}
|
|
||||||
elsif ( $state eq "absent" ) {
|
|
||||||
delete $hash->{AUTOGONE} if ( $hash->{AUTOGONE} );
|
|
||||||
RESIDENTStk_RemoveInternalTimer( "AutoGone", $hash );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# mood
|
|
||||||
elsif ( $a[1] eq "mood" ) {
|
|
||||||
if ( defined( $a[2] ) && $a[2] ne "" ) {
|
|
||||||
Log3 $name, 2, "ROOMMATE set $name mood " . $a[2] if ( !$silent );
|
|
||||||
readingsBeginUpdate($hash) if ( !$silent );
|
|
||||||
|
|
||||||
if ( $a[2] eq "toggle"
|
|
||||||
&& ReadingsVal( $name, "lastMood", "" ) ne "" )
|
|
||||||
{
|
|
||||||
readingsBulkUpdate( $hash, "mood",
|
|
||||||
ReadingsVal( $name, "lastMood", "" ) );
|
|
||||||
readingsBulkUpdate( $hash, "lastMood", $mood );
|
|
||||||
}
|
|
||||||
elsif ( $mood ne $a[2] ) {
|
|
||||||
readingsBulkUpdate( $hash, "lastMood", $mood )
|
|
||||||
if ( $mood ne "-" );
|
|
||||||
readingsBulkUpdate( $hash, "mood", $a[2] );
|
|
||||||
}
|
|
||||||
|
|
||||||
readingsEndUpdate( $hash, 1 ) if ( !$silent );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return "Invalid 2nd argument, choose one of mood toggle";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# location
|
|
||||||
elsif ( $a[1] eq "location" ) {
|
|
||||||
if ( defined( $a[2] ) && $a[2] ne "" ) {
|
|
||||||
Log3 $name, 2, "ROOMMATE set $name location " . $a[2]
|
|
||||||
if ( !$silent );
|
|
||||||
|
|
||||||
if ( $location ne $a[2] ) {
|
|
||||||
my $searchstring;
|
|
||||||
|
|
||||||
readingsBeginUpdate($hash) if ( !$silent );
|
|
||||||
|
|
||||||
# read attributes
|
|
||||||
my @location_home =
|
|
||||||
split( ' ', AttrVal( $name, "rr_locationHome", "home" ) );
|
|
||||||
my @location_underway =
|
|
||||||
split( ' ',
|
|
||||||
AttrVal( $name, "rr_locationUnderway", "underway" ) );
|
|
||||||
my @location_wayhome =
|
|
||||||
split( ' ',
|
|
||||||
AttrVal( $name, "rr_locationWayhome", "wayhome" ) );
|
|
||||||
|
|
||||||
$searchstring = quotemeta($location);
|
|
||||||
readingsBulkUpdate( $hash, "lastLocation", $location )
|
|
||||||
if ( $location ne "wayhome"
|
|
||||||
&& !grep( m/^$searchstring$/, @location_underway ) );
|
|
||||||
readingsBulkUpdate( $hash, "location", $a[2] )
|
|
||||||
if ( $a[2] ne "wayhome" );
|
|
||||||
|
|
||||||
# wayhome detection
|
|
||||||
$searchstring = quotemeta($location);
|
|
||||||
if (
|
|
||||||
(
|
|
||||||
$a[2] eq "wayhome"
|
|
||||||
|| grep( m/^$searchstring$/, @location_wayhome )
|
|
||||||
)
|
|
||||||
&& ( $presence eq "absent" )
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Log3 $name, 3,
|
|
||||||
"ROOMMATE $name: on way back home from $location";
|
|
||||||
readingsBulkUpdateIfChanged( $hash, "wayhome", "1" );
|
|
||||||
}
|
|
||||||
|
|
||||||
readingsEndUpdate( $hash, 1 ) if ( !$silent );
|
|
||||||
|
|
||||||
# auto-updates
|
|
||||||
$searchstring = quotemeta( $a[2] );
|
|
||||||
if (
|
|
||||||
(
|
|
||||||
$a[2] eq "home"
|
|
||||||
|| grep( m/^$searchstring$/, @location_home )
|
|
||||||
)
|
|
||||||
&& $state ne "home"
|
|
||||||
&& $state ne "gotosleep"
|
|
||||||
&& $state ne "asleep"
|
|
||||||
&& $state ne "awoken"
|
|
||||||
&& $state ne "initialized"
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Log3 $name, 4,
|
|
||||||
"ROOMMATE $name: implicit state change caused by location "
|
|
||||||
. $a[2];
|
|
||||||
ROOMMATE_Set( $hash, $name, "silentSet", "state", "home" );
|
|
||||||
}
|
|
||||||
elsif (
|
|
||||||
(
|
|
||||||
$a[2] eq "underway"
|
|
||||||
|| grep( m/^$searchstring$/, @location_underway )
|
|
||||||
)
|
|
||||||
&& $state ne "gone"
|
|
||||||
&& $state ne "none"
|
|
||||||
&& $state ne "absent"
|
|
||||||
&& $state ne "initialized"
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Log3 $name, 4,
|
|
||||||
"ROOMMATE $name: implicit state change caused by location "
|
|
||||||
. $a[2];
|
|
||||||
ROOMMATE_Set( $hash, $name, "silentSet", "state",
|
|
||||||
"absent" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return "Invalid 2nd argument, choose one of location ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# create
|
|
||||||
elsif ( lc( $a[1] ) eq "create" ) {
|
|
||||||
if ( !defined( $a[2] ) || $a[2] !~ /^(wakeuptimer|locationMap)$/i ) {
|
|
||||||
return
|
|
||||||
"Invalid 2nd argument, choose one of wakeuptimer locationMap ";
|
|
||||||
}
|
|
||||||
elsif ( lc( $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 ROOMMATE 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 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 wakeupResetdays:multiple-strict,0,1,2,3,4,5,6 wakeupDays:multiple-strict,0,1,2,3,4,5,6 wakeupHolidays:,andHoliday,orHoliday,andNoHoliday,orNoHoliday wakeupEnforced:0,1,2,3";
|
|
||||||
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, "rr_wakeupDevice", 0 );
|
|
||||||
if ( !$wakeupDevice ) {
|
|
||||||
fhem "attr $name rr_wakeupDevice $wakeuptimerName";
|
|
||||||
}
|
|
||||||
elsif ( $wakeupDevice !~ /(.*,?)($wakeuptimerName)(.*,?)/ )
|
|
||||||
{
|
|
||||||
fhem "attr $name rr_wakeupDevice "
|
|
||||||
. $wakeupDevice
|
|
||||||
. ",$wakeuptimerName";
|
|
||||||
}
|
|
||||||
|
|
||||||
# trigger first update
|
|
||||||
RESIDENTStk_findDummySlaves($name);
|
|
||||||
fhem "sleep 1;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.";
|
|
||||||
}
|
|
||||||
elsif ( lc( $a[2] ) eq "locationmap" ) {
|
|
||||||
my $locationmapName = $name . "_map";
|
|
||||||
|
|
||||||
if ( IsDevice($locationmapName) ) {
|
|
||||||
return
|
|
||||||
"Device $locationmapName existing already, delete it first to have it re-created.";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
my $sortby = AttrVal( $name, "sortby", -1 );
|
|
||||||
$sortby++;
|
|
||||||
|
|
||||||
# create new weblink device
|
|
||||||
fhem "define $locationmapName weblink htmlCode {
|
|
||||||
'<ul style=\"width: 400px;; overflow: hidden;; height: 300px;;\">
|
|
||||||
<iframe name=\"$locationmapName\" src=\"https://www.google.com/maps/embed/v1/place?key=AIzaSyB66DvcpbXJ5eWgIkzxpUN2s_9l3_6fegM&q='
|
|
||||||
.ReadingsVal('$name','locationLat','')
|
|
||||||
.','
|
|
||||||
.ReadingsVal('$name','locationLong','')
|
|
||||||
.'&zoom=13\" width=\"480\" height=\"480\" frameborder=\"0\" style=\"border:0;; margin-top: -165px;; margin-left: -135px;;\">
|
|
||||||
</iframe>
|
|
||||||
</ul>'
|
|
||||||
}";
|
|
||||||
fhem "attr $locationmapName alias Current Location";
|
|
||||||
fhem
|
|
||||||
"attr $locationmapName comment Auto-created by ROOMMATE module";
|
|
||||||
fhem "attr $locationmapName group " . $attr{$name}{group}
|
|
||||||
if ( defined( $attr{$name}{group} ) );
|
|
||||||
fhem "attr $locationmapName room " . $attr{$name}{room}
|
|
||||||
if ( defined( $attr{$name}{room} ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Weblink device $locationmapName was created.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# return usage hint
|
|
||||||
else {
|
|
||||||
return $usage;
|
|
||||||
}
|
|
||||||
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
|
|
||||||
# module Fn ####################################################################
|
|
||||||
sub ROOMMATE_SetLocation($$$;$$$$$$) {
|
|
||||||
my ( $name, $location, $trigger, $id, $time, $lat, $long, $address,
|
|
||||||
$device ) = @_;
|
|
||||||
my $hash = $defs{$name};
|
|
||||||
my $state = ReadingsVal( $name, "state", "initialized" );
|
|
||||||
my $presence = ReadingsVal( $name, "presence", "present" );
|
|
||||||
my $currLocation = ReadingsVal( $name, "location", "-" );
|
|
||||||
my $currWayhome = ReadingsVal( $name, "wayhome", "0" );
|
|
||||||
my $currLat = ReadingsVal( $name, "locationLat", "-" );
|
|
||||||
my $currLong = ReadingsVal( $name, "locationLong", "-" );
|
|
||||||
my $currAddr = ReadingsVal( $name, "locationAddr", "" );
|
|
||||||
$id = "-" if ( !$id || $id eq "" );
|
|
||||||
$lat = "-" if ( !$lat || $lat eq "" );
|
|
||||||
$long = "-" if ( !$long || $long eq "" );
|
|
||||||
$address = "" if ( !$address );
|
|
||||||
$time = "" if ( !$time );
|
|
||||||
$device = "" if ( !$device );
|
|
||||||
|
|
||||||
Log3 $name, 5,
|
|
||||||
"ROOMMATE $name: received location information: id=$id name=$location trig=$trigger date=$time lat=$lat long=$long address:$address device=$device";
|
|
||||||
|
|
||||||
my $searchstring;
|
|
||||||
|
|
||||||
readingsBeginUpdate($hash);
|
|
||||||
|
|
||||||
# read attributes
|
|
||||||
my @location_home =
|
|
||||||
split( ' ', AttrVal( $name, "rr_locationHome", "home" ) );
|
|
||||||
my @location_underway =
|
|
||||||
split( ' ', AttrVal( $name, "rr_locationUnderway", "underway" ) );
|
|
||||||
my @location_wayhome =
|
|
||||||
split( ' ', AttrVal( $name, "rr_locationWayhome", "wayhome" ) );
|
|
||||||
|
|
||||||
$searchstring = quotemeta($location);
|
|
||||||
|
|
||||||
# update locationPresence
|
|
||||||
readingsBulkUpdate( $hash, "locationPresence", "present" )
|
|
||||||
if ( $trigger == 1 );
|
|
||||||
readingsBulkUpdate( $hash, "locationPresence", "absent" )
|
|
||||||
if ( $trigger == 0 );
|
|
||||||
|
|
||||||
# check for implicit state change
|
|
||||||
#
|
|
||||||
my $stateChange = 0;
|
|
||||||
|
|
||||||
# home
|
|
||||||
if ( $location eq "home" || grep( m/^$searchstring$/, @location_home ) ) {
|
|
||||||
Log3 $name, 5, "ROOMMATE $name: received signal from home location";
|
|
||||||
|
|
||||||
# home
|
|
||||||
if ( $state ne "home"
|
|
||||||
&& $state ne "gotosleep"
|
|
||||||
&& $state ne "asleep"
|
|
||||||
&& $state ne "awoken"
|
|
||||||
&& $trigger eq "1" )
|
|
||||||
{
|
|
||||||
$stateChange = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
# absent
|
|
||||||
elsif ($state ne "gone"
|
|
||||||
&& $state ne "none"
|
|
||||||
&& $state ne "absent"
|
|
||||||
&& $trigger eq "0" )
|
|
||||||
{
|
|
||||||
$stateChange = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# underway
|
|
||||||
elsif ($location eq "underway"
|
|
||||||
|| $location eq "wayhome"
|
|
||||||
|| grep( m/^$searchstring$/, @location_underway )
|
|
||||||
|| grep( m/^$searchstring$/, @location_wayhome ) )
|
|
||||||
{
|
|
||||||
Log3 $name, 5, "ROOMMATE $name: received signal from underway location";
|
|
||||||
|
|
||||||
# absent
|
|
||||||
$stateChange = 2
|
|
||||||
if ( $state ne "gone"
|
|
||||||
&& $state ne "none"
|
|
||||||
&& $state ne "absent" );
|
|
||||||
}
|
|
||||||
|
|
||||||
# wayhome
|
|
||||||
if (
|
|
||||||
$location eq "wayhome"
|
|
||||||
|| ( grep( m/^$searchstring$/, @location_wayhome )
|
|
||||||
&& $trigger eq "0" )
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Log3 $name, 5, "ROOMMATE $name: wayhome signal received";
|
|
||||||
|
|
||||||
# wayhome=true
|
|
||||||
if (
|
|
||||||
(
|
|
||||||
( $location eq "wayhome" && $trigger eq "1" )
|
|
||||||
|| ( $location ne "wayhome" && $trigger eq "0" )
|
|
||||||
)
|
|
||||||
&& ReadingsVal( $name, "wayhome", "0" ) ne "1"
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Log3 $name, 3, "ROOMMATE $name: on way back home from $location";
|
|
||||||
readingsBulkUpdate( $hash, "wayhome", "1" );
|
|
||||||
}
|
|
||||||
|
|
||||||
# wayhome=false
|
|
||||||
elsif ($location eq "wayhome"
|
|
||||||
&& $trigger eq "0"
|
|
||||||
&& ReadingsVal( $name, "wayhome", "0" ) ne "0" )
|
|
||||||
{
|
|
||||||
Log3 $name, 3,
|
|
||||||
"ROOMMATE $name: seems not to be on way back home anymore";
|
|
||||||
readingsBulkUpdate( $hash, "wayhome", "0" );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# activate wayhome tracing when reaching another location while wayhome=1
|
|
||||||
elsif ( $stateChange == 0 && $trigger == 1 && $currWayhome == 1 ) {
|
|
||||||
Log3 $name, 3,
|
|
||||||
"ROOMMATE $name: seems to stay at $location before coming home";
|
|
||||||
readingsBulkUpdate( $hash, "wayhome", "2" );
|
|
||||||
}
|
|
||||||
|
|
||||||
# revert wayhome during active wayhome tracing
|
|
||||||
elsif ( $stateChange == 0 && $trigger == 0 && $currWayhome == 2 ) {
|
|
||||||
Log3 $name, 3,
|
|
||||||
"ROOMMATE $name: finally on way back home from $location";
|
|
||||||
readingsBulkUpdate( $hash, "wayhome", "1" );
|
|
||||||
}
|
|
||||||
|
|
||||||
my $currLongDiff = 0;
|
|
||||||
my $currLatDiff = 0;
|
|
||||||
$currLongDiff =
|
|
||||||
maxNum( ReadingsVal( $name, "lastLocationLong", 0 ), $currLong ) -
|
|
||||||
minNum( ReadingsVal( $name, "lastLocationLong", 0 ), $currLong )
|
|
||||||
if ( $currLong ne "-" );
|
|
||||||
$currLatDiff =
|
|
||||||
maxNum( ReadingsVal( $name, "lastLocationLat", 0 ), $currLat ) -
|
|
||||||
minNum( ReadingsVal( $name, "lastLocationLat", 0 ), $currLat )
|
|
||||||
if ( $currLat ne "-" );
|
|
||||||
|
|
||||||
if (
|
|
||||||
$trigger == 1
|
|
||||||
&& ( $stateChange > 0
|
|
||||||
|| ReadingsVal( $name, "lastLocation", "-" ) ne $currLocation
|
|
||||||
|| $currLongDiff > 0.00002
|
|
||||||
|| $currLatDiff > 0.00002 )
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Log3 $name, 5, "ROOMMATE $name: archiving last known location";
|
|
||||||
readingsBulkUpdate( $hash, "lastLocationLat", $currLat );
|
|
||||||
readingsBulkUpdate( $hash, "lastLocationLong", $currLong );
|
|
||||||
readingsBulkUpdate( $hash, "lastLocationAddr", $currAddr )
|
|
||||||
if ( $currAddr ne "" );
|
|
||||||
readingsBulkUpdate( $hash, "lastLocation", $currLocation );
|
|
||||||
}
|
|
||||||
|
|
||||||
readingsBulkUpdate( $hash, "locationLat", $lat );
|
|
||||||
readingsBulkUpdate( $hash, "locationLong", $long );
|
|
||||||
|
|
||||||
if ( $address ne "" ) {
|
|
||||||
readingsBulkUpdate( $hash, "locationAddr", $address );
|
|
||||||
}
|
|
||||||
elsif ( $currAddr ne "" ) {
|
|
||||||
readingsBulkUpdate( $hash, "locationAddr", "-" );
|
|
||||||
}
|
|
||||||
|
|
||||||
readingsBulkUpdate( $hash, "location", $location );
|
|
||||||
|
|
||||||
readingsEndUpdate( $hash, 1 );
|
|
||||||
|
|
||||||
# trigger state change
|
|
||||||
if ( $stateChange > 0 ) {
|
|
||||||
Log3 $name, 4,
|
|
||||||
"ROOMMATE $name: implicit state change caused by location "
|
|
||||||
. $location;
|
|
||||||
|
|
||||||
ROOMMATE_Set( $hash, $name, "silentSet", "state", "home" )
|
|
||||||
if $stateChange == 1;
|
|
||||||
ROOMMATE_Set( $hash, $name, "silentSet", "state", "absent" )
|
|
||||||
if $stateChange == 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
@ -1,82 +1,25 @@
|
|||||||
|
###############################################################################
|
||||||
# $Id$
|
# $Id$
|
||||||
##############################################################################
|
|
||||||
#
|
|
||||||
# 98_GEOFANCY.pm
|
|
||||||
# An FHEM Perl module to receive geofencing webhooks from geofancy.com.
|
|
||||||
#
|
|
||||||
# Copyright by Julian Pawlowski
|
|
||||||
# e-mail: julian.pawlowski at gmail.com
|
|
||||||
#
|
|
||||||
# Based on HTTPSRV from Dr. Boris Neubert
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
##############################################################################
|
|
||||||
|
|
||||||
package main;
|
package main;
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use vars qw(%data);
|
|
||||||
use HttpUtils;
|
|
||||||
use Time::Local;
|
|
||||||
use Data::Dumper;
|
use Data::Dumper;
|
||||||
|
use Time::Local;
|
||||||
|
|
||||||
sub GEOFANCY_Set($@);
|
use HttpUtils;
|
||||||
sub GEOFANCY_Define($$);
|
|
||||||
sub GEOFANCY_Undefine($$);
|
|
||||||
|
|
||||||
#########################
|
# initialize ##################################################################
|
||||||
sub GEOFANCY_addExtension($$$) {
|
|
||||||
my ( $name, $func, $link ) = @_;
|
|
||||||
|
|
||||||
my $url = "/$link";
|
|
||||||
Log3 $name, 2, "Registering GEOFANCY $name for URL $url...";
|
|
||||||
$data{FWEXT}{$url}{deviceName} = $name;
|
|
||||||
$data{FWEXT}{$url}{FUNC} = $func;
|
|
||||||
$data{FWEXT}{$url}{LINK} = $link;
|
|
||||||
}
|
|
||||||
|
|
||||||
#########################
|
|
||||||
sub GEOFANCY_removeExtension($) {
|
|
||||||
my ($link) = @_;
|
|
||||||
|
|
||||||
my $url = "/$link";
|
|
||||||
my $name = $data{FWEXT}{$url}{deviceName};
|
|
||||||
Log3 $name, 2, "Unregistering GEOFANCY $name for URL $url...";
|
|
||||||
delete $data{FWEXT}{$url};
|
|
||||||
}
|
|
||||||
|
|
||||||
###################################
|
|
||||||
sub GEOFANCY_Initialize($) {
|
sub GEOFANCY_Initialize($) {
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
|
|
||||||
Log3 $hash, 5, "GEOFANCY_Initialize: Entering";
|
|
||||||
|
|
||||||
$hash->{SetFn} = "GEOFANCY_Set";
|
|
||||||
$hash->{DefFn} = "GEOFANCY_Define";
|
$hash->{DefFn} = "GEOFANCY_Define";
|
||||||
$hash->{UndefFn} = "GEOFANCY_Undefine";
|
$hash->{UndefFn} = "GEOFANCY_Undefine";
|
||||||
|
$hash->{SetFn} = "GEOFANCY_Set";
|
||||||
$hash->{AttrList} = "devAlias disable:0,1 " . $readingFnAttributes;
|
$hash->{AttrList} = "devAlias disable:0,1 " . $readingFnAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
###################################
|
# regular Fn ##################################################################
|
||||||
sub GEOFANCY_Define($$) {
|
sub GEOFANCY_Define($$) {
|
||||||
|
|
||||||
my ( $hash, $def ) = @_;
|
my ( $hash, $def ) = @_;
|
||||||
|
|
||||||
my @a = split( "[ \t]+", $def, 5 );
|
my @a = split( "[ \t]+", $def, 5 );
|
||||||
|
|
||||||
return "Usage: define <name> GEOFANCY <infix>"
|
return "Usage: define <name> GEOFANCY <infix>"
|
||||||
@ -94,17 +37,12 @@ sub GEOFANCY_Define($$) {
|
|||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
###################################
|
|
||||||
sub GEOFANCY_Undefine($$) {
|
sub GEOFANCY_Undefine($$) {
|
||||||
|
|
||||||
my ( $hash, $name ) = @_;
|
my ( $hash, $name ) = @_;
|
||||||
|
|
||||||
GEOFANCY_removeExtension( $hash->{fhem}{infix} );
|
GEOFANCY_removeExtension( $hash->{fhem}{infix} );
|
||||||
|
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
###################################
|
|
||||||
sub GEOFANCY_Set($@) {
|
sub GEOFANCY_Set($@) {
|
||||||
my ( $hash, @a ) = @_;
|
my ( $hash, @a ) = @_;
|
||||||
my $name = $hash->{NAME};
|
my $name = $hash->{NAME};
|
||||||
@ -145,13 +83,7 @@ sub GEOFANCY_Set($@) {
|
|||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
############################################################################################################
|
# module Fn ####################################################################
|
||||||
#
|
|
||||||
# Begin of helper functions
|
|
||||||
#
|
|
||||||
############################################################################################################
|
|
||||||
|
|
||||||
###################################
|
|
||||||
sub GEOFANCY_CGI() {
|
sub GEOFANCY_CGI() {
|
||||||
|
|
||||||
# Locative.app (https://itunes.apple.com/us/app/locative/id725198453?mt=8)
|
# Locative.app (https://itunes.apple.com/us/app/locative/id725198453?mt=8)
|
||||||
@ -606,15 +538,12 @@ m/(19|20)\d\d-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([0-1][0-9]|2[0-3]):([0-5
|
|||||||
|| lc($entry) eq "test" );
|
|| lc($entry) eq "test" );
|
||||||
$locName = $id if ( $locName eq "" );
|
$locName = $id if ( $locName eq "" );
|
||||||
|
|
||||||
ROOMMATE_SetLocation(
|
RESIDENTStk_SetLocation(
|
||||||
$deviceAlias, $locName, $trigger, $id, $time,
|
$deviceAlias, $locName, $trigger, $id, $time,
|
||||||
$lat, $long, $address, $device
|
$lat, $long, $address, $device
|
||||||
) if ( $defs{$deviceAlias}{TYPE} eq "ROOMMATE" );
|
)
|
||||||
|
if ( $defs{$deviceAlias}{TYPE} eq "ROOMMATE"
|
||||||
GUEST_SetLocation(
|
|| $defs{$deviceAlias}{TYPE} eq "GUEST" );
|
||||||
$deviceAlias, $locName, $trigger, $id, $time,
|
|
||||||
$lat, $long, $address, $device
|
|
||||||
) if ( $defs{$deviceAlias}{TYPE} eq "GUEST" );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$msg = lc($entry) . " OK";
|
$msg = lc($entry) . " OK";
|
||||||
@ -624,6 +553,25 @@ m/(19|20)\d\d-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([0-1][0-9]|2[0-3]):([0-5
|
|||||||
return ( "text/plain; charset=utf-8", $msg );
|
return ( "text/plain; charset=utf-8", $msg );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub GEOFANCY_addExtension($$$) {
|
||||||
|
my ( $name, $func, $link ) = @_;
|
||||||
|
|
||||||
|
my $url = "/$link";
|
||||||
|
Log3 $name, 2, "Registering GEOFANCY $name for URL $url...";
|
||||||
|
$data{FWEXT}{$url}{deviceName} = $name;
|
||||||
|
$data{FWEXT}{$url}{FUNC} = $func;
|
||||||
|
$data{FWEXT}{$url}{LINK} = $link;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub GEOFANCY_removeExtension($) {
|
||||||
|
my ($link) = @_;
|
||||||
|
|
||||||
|
my $url = "/$link";
|
||||||
|
my $name = $data{FWEXT}{$url}{deviceName};
|
||||||
|
Log3 $name, 2, "Unregistering GEOFANCY $name for URL $url...";
|
||||||
|
delete $data{FWEXT}{$url};
|
||||||
|
}
|
||||||
|
|
||||||
sub GEOFANCY_ISO8601UTCtoLocal ($) {
|
sub GEOFANCY_ISO8601UTCtoLocal ($) {
|
||||||
my ($datetime) = @_;
|
my ($datetime) = @_;
|
||||||
$datetime =~ s/T/ /g if ( defined( $datetime && $datetime ne "" ) );
|
$datetime =~ s/T/ /g if ( defined( $datetime && $datetime ne "" ) );
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user