2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-03 10:46:53 +00:00

GEOFANCY,ROOMMATE,GUEST: mesh modules by introducing attribute r*_geofenceUUIDs to bring down notify-device clutter

git-svn-id: https://svn.fhem.de/fhem/trunk@10323 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
jpawlowski 2016-01-01 23:22:33 +00:00
parent 78280796cd
commit 69eee038aa
5 changed files with 383 additions and 286 deletions

View File

@ -1,5 +1,11 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it.
- feature: 20_ROOMMATE,
20_GUEST: new attribute r*_geofenceUUIDs for direct GEOFANCY
support as an alternative for notify/DOIF/watchdog
clutter
- change: 98_GEOFANCY: attribute devAlias is now mandatory to have all
readings created (improves security/stability)
- feature: configDB: rescue mode added, read forum #46538
- changed: 49_SSCam: documentation changed
HINT: use IP-Adresses instead of hostnames in SSCAM-Define!

View File

@ -584,7 +584,7 @@ sub RESIDENTS_Set($@) {
fhem
"attr $wakeuptimerName comment Auto-created by RESIDENTS module for use with RESIDENTS Toolkit";
fhem
"attr $wakeuptimerName devStateIcon OFF:general_aus\@red:reset running:general_an\@blue:stop .*:general_an\@green:nextRun%20OFF";
"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";

View File

@ -22,20 +22,6 @@
# You should have received a copy of the GNU General Public License
# along with fhem. If not, see <http://www.gnu.org/licenses/>.
#
#
# Version: 1.2.1
#
# Major Version History:
# - 1.2.0 - 2015-03-11
# -- add RESIDENTStoolkit support
#
# - 1.1.0 - 2014-04-07
# -- new readings in computer readable format (*_cr)
# -- format of readings durTimer readings changed from minutes to HH:MM:ss
#
# - 1.0.0 - 2014-02-08
# -- First release
#
##############################################################################
package main;
@ -62,7 +48,7 @@ sub GUEST_Initialize($) {
$hash->{NotifyFn} = "GUEST_Notify";
$hash->{UndefFn} = "GUEST_Undefine";
$hash->{AttrList} =
"rg_locationHome rg_locationWayhome rg_locationUnderway rg_autoGoneAfter: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_locationHome rg_locationWayhome rg_locationUnderway rg_autoGoneAfter: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 "
. $readingFnAttributes;
}
@ -132,6 +118,9 @@ sub GUEST_Define($$) {
$modified = 0;
}
# set reverse pointer
$modules{GUEST}{defptr}{$name} = \$hash;
readingsBeginUpdate($hash);
# set default settings on first define
@ -169,7 +158,9 @@ sub GUEST_Define($$) {
}
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.";
"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;
@ -198,6 +189,9 @@ sub GUEST_Undefine($$) {
}
}
# release reverse pointer
delete $modules{GUEST}{defptr}{$name};
return undef;
}
@ -682,91 +676,13 @@ sub GUEST_Set($@) {
# location
elsif ( $a[1] eq "location" ) {
if ( defined( $a[2] ) && $a[2] ne "" ) {
Log3 $name, 2, "GUEST set $name location " . $a[2] if ( !$silent );
shift @a;
shift @a;
my $location = join( " ", @a );
Log3 $name, 2, "ROOMMATE set $name location " . $location
if ( !$silent );
if ( $location ne $a[2] ) {
my $searchstring;
readingsBeginUpdate($hash) if ( !$silent );
# read attributes
my @location_home =
( defined( $attr{$name}{"rg_locationHome"} ) )
? split( ' ', $attr{$name}{"rg_locationHome"} )
: ("home");
my @location_underway =
( defined( $attr{$name}{"rg_locationUnderway"} ) )
? split( ' ', $attr{$name}{"rg_locationUnderway"} )
: ("underway");
my @location_wayhome =
( defined( $attr{$name}{"rg_locationWayhome"} ) )
? split( ' ', $attr{$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";
readingsBulkUpdate( $hash, "wayhome", "1" )
if ( !defined( $hash->{READINGS}{wayhome}{VAL} )
|| $hash->{READINGS}{wayhome}{VAL} ne "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" );
}
}
GUEST_SetLocation( $name, $location, 1, undef );
}
else {
return "Invalid 2nd argument, choose one of location ";
@ -795,7 +711,7 @@ sub GUEST_Set($@) {
fhem
"attr $wakeuptimerName comment Auto-created by GUEST module for use with RESIDENTS Toolkit";
fhem
"attr $wakeuptimerName devStateIcon OFF:general_aus\@red:reset running:general_an\@blue:stop .*:general_an\@green:nextRun%20OFF";
"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";
@ -1002,6 +918,85 @@ sub GUEST_DurationTimer($;$) {
return undef;
}
###################################
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 $lastLocation = ReadingsVal( $name, "location", undef );
$location = "underway" if ( $trigger eq "0" );
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, "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", $lastLocation )
if ( $lastLocation
&& $location ne "wayhome"
&& !grep( m/^$searchstring$/, @location_underway ) );
readingsBulkUpdate( $hash, "location", $location )
if ( $location ne "wayhome" );
# wayhome detection
$searchstring = quotemeta($location);
if (
(
$location eq "wayhome"
|| grep( m/^$searchstring$/, @location_wayhome )
)
&& ( $presence eq "absent" )
)
{
Log3 $name, 3, "GUEST $name: on way back home from $location";
readingsBulkUpdate( $hash, "wayhome", "1" )
if ( ReadingsVal( $name, "wayhome", "0" ) ne "1" );
}
readingsEndUpdate( $hash, 1 );
# auto-updates
$searchstring = quotemeta($location);
if ( ( $location eq "home" || grep( m/^$searchstring$/, @location_home ) )
&& $state ne "home"
&& $state ne "gotosleep"
&& $state ne "asleep"
&& $state ne "awoken" )
{
Log3 $name, 4,
"GUEST $name: implicit state change caused by location " . $location;
GUEST_Set( $hash, $name, "silentSet", "state", "home" );
}
elsif (
(
$location eq "underway"
|| grep( m/^$searchstring$/, @location_underway )
)
&& $state ne "gone"
&& $state ne "none"
&& $state ne "absent"
)
{
Log3 $name, 4,
"GUEST $name: implicit state change caused by location " . $location;
GUEST_Set( $hash, $name, "silentSet", "state", "absent" );
}
}
###################################
sub GUEST_StartInternalTimers($$) {
my ($hash) = @_;
@ -1166,6 +1161,9 @@ sub GUEST_StartInternalTimers($$) {
<li>
<b>rg_autoGoneAfter</b> - hours after which state should be auto-set to 'gone' when current state is 'absent'; defaults to 16 hours
</li>
<li>
<b>rg_geofenceUUIDs</b> - comma separated list of device UUIDs updating their location via <a href="#GEOFANCY">GEOFANCY</a>. Avoids necessity for additional notify/DOIF/watchdog devices and can make GEOFANCY attribute <i>devAlias</i> obsolete. (using more than one UUID/device might not be a good idea as location my leap)
</li>
<li>
<b>rg_locationHome</b> - locations matching these will be treated as being at home; first entry reflects default value to be used with state correlation; separate entries by space; defaults to 'home'
</li>
@ -1463,6 +1461,9 @@ sub GUEST_StartInternalTimers($$) {
<li>
<b>rg_autoGoneAfter</b> - Anzahl der Stunden, nach denen sich der Status automatisch auf 'gone' ändert, wenn der aktuellen Status 'absent' ist; Standard ist 36 Stunden
</li>
<li>
<b>rg_geofenceUUIDs</b> - Mit Komma getrennte Liste von Ger&auml;te UUIDs, die ihren Standort &uuml;ber <a href="#GEOFANCY">GEOFANCY</a> aktualisieren. Vermeidet zus&auml;tzliche notify/DOIF/watchdog Ger&auml;te und kann als Ersatz für das GEOFANCY attribute <i>devAlias</i> dienen. (hier ehr als eine UUID/Device zu hinterlegen ist eher keine gute Idee da die Lokation dann wom&ouml;glich anfängt zu springen)
</li>
<li>
<b>rg_locationHome</b> - hiermit übereinstimmende Lokationen werden als zu Hause gewertet; der erste Eintrag wird für das Zusammenspiel bei Statusänderungen benutzt; mehrere Einträge durch Leerzeichen trennen; Standard ist 'home'
</li>

View File

@ -22,20 +22,6 @@
# You should have received a copy of the GNU General Public License
# along with fhem. If not, see <http://www.gnu.org/licenses/>.
#
#
# Version: 1.2.1
#
# Major Version History:
# - 1.2.0 - 2015-03-11
# -- add RESIDENTStoolkit support
#
# - 1.1.0 - 2014-04-07
# -- new readings in computer readable format (*_cr)
# -- format of readings durTimer readings changed from minutes to HH:MM:ss
#
# - 1.0.0 - 2014-02-08
# -- First release
#
##############################################################################
package main;
@ -62,7 +48,7 @@ sub ROOMMATE_Initialize($) {
$hash->{NotifyFn} = "ROOMMATE_Notify";
$hash->{UndefFn} = "ROOMMATE_Undefine";
$hash->{AttrList} =
"rr_locationHome rr_locationWayhome rr_locationUnderway rr_autoGoneAfter: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_locationHome rr_locationWayhome rr_locationUnderway rr_autoGoneAfter: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 "
. $readingFnAttributes;
}
@ -133,6 +119,9 @@ sub ROOMMATE_Define($$) {
$modified = 0;
}
# set reverse pointer
$modules{ROOMMATE}{defptr}{$name} = \$hash;
readingsBeginUpdate($hash);
# set default settings on first define
@ -174,7 +163,9 @@ sub ROOMMATE_Define($$) {
}
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.";
"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;
@ -203,6 +194,9 @@ sub ROOMMATE_Undefine($$) {
}
}
# release reverse pointer
delete $modules{ROOMMATE}{defptr}{$name};
return undef;
}
@ -662,93 +656,13 @@ sub ROOMMATE_Set($@) {
# location
elsif ( $a[1] eq "location" ) {
if ( defined( $a[2] ) && $a[2] ne "" ) {
Log3 $name, 2, "ROOMMATE set $name location " . $a[2]
shift @a;
shift @a;
my $location = join( " ", @a );
Log3 $name, 2, "ROOMMATE set $name location " . $location
if ( !$silent );
if ( $location ne $a[2] ) {
my $searchstring;
readingsBeginUpdate($hash) if ( !$silent );
# read attributes
my @location_home =
( defined( $attr{$name}{"rr_locationHome"} ) )
? split( ' ', $attr{$name}{"rr_locationHome"} )
: ("home");
my @location_underway =
( defined( $attr{$name}{"rr_locationUnderway"} ) )
? split( ' ', $attr{$name}{"rr_locationUnderway"} )
: ("underway");
my @location_wayhome =
( defined( $attr{$name}{"rr_locationWayhome"} ) )
? split( ' ', $attr{$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";
readingsBulkUpdate( $hash, "wayhome", "1" )
if ( !defined( $hash->{READINGS}{wayhome}{VAL} )
|| $hash->{READINGS}{wayhome}{VAL} ne "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" );
}
}
ROOMMATE_SetLocation( $name, $location, 1, undef );
}
else {
return "Invalid 2nd argument, choose one of location ";
@ -777,7 +691,7 @@ sub ROOMMATE_Set($@) {
fhem
"attr $wakeuptimerName comment Auto-created by ROOMMATE module for use with RESIDENTS Toolkit";
fhem
"attr $wakeuptimerName devStateIcon OFF:general_aus\@red:reset running:general_an\@blue:stop .*:general_an\@green:nextRun%20OFF";
"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";
@ -977,6 +891,87 @@ sub ROOMMATE_DurationTimer($;$) {
return undef;
}
###################################
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 $lastLocation = ReadingsVal( $name, "location", undef );
$location = "underway" if ( $trigger eq "0" );
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);
readingsBulkUpdate( $hash, "lastLocation", $lastLocation )
if ( $lastLocation
&& $location ne "wayhome"
&& !grep( m/^$searchstring$/, @location_underway ) );
readingsBulkUpdate( $hash, "location", $location )
if ( $location ne "wayhome" );
# wayhome detection
$searchstring = quotemeta($location);
if (
(
$location eq "wayhome"
|| grep( m/^$searchstring$/, @location_wayhome )
)
&& ( $presence eq "absent" )
)
{
Log3 $name, 3, "ROOMMATE $name: on way back home from $location";
readingsBulkUpdate( $hash, "wayhome", "1" )
if ( ReadingsVal( $name, "wayhome", "0" ) ne "1" );
}
readingsEndUpdate( $hash, 1 );
# auto-updates
$searchstring = quotemeta($location);
if ( ( $location eq "home" || grep( m/^$searchstring$/, @location_home ) )
&& $state ne "home"
&& $state ne "gotosleep"
&& $state ne "asleep"
&& $state ne "awoken" )
{
Log3 $name, 4,
"ROOMMATE $name: implicit state change caused by location "
. $location;
ROOMMATE_Set( $hash, $name, "silentSet", "state", "home" );
}
elsif (
(
$location eq "underway"
|| grep( m/^$searchstring$/, @location_underway )
)
&& $state ne "gone"
&& $state ne "none"
&& $state ne "absent"
)
{
Log3 $name, 4,
"ROOMMATE $name: implicit state change caused by location "
. $location;
ROOMMATE_Set( $hash, $name, "silentSet", "state", "absent" );
}
}
###################################
sub ROOMMATE_StartInternalTimers($$) {
my ($hash) = @_;
@ -1147,6 +1142,9 @@ sub ROOMMATE_StartInternalTimers($$) {
<li>
<b>rr_autoGoneAfter</b> - hours after which state should be auto-set to 'gone' when current state is 'absent'; defaults to 36 hours
</li>
<li>
<b>rr_geofenceUUIDs</b> - comma separated list of device UUIDs updating their location via <a href="#GEOFANCY">GEOFANCY</a>. Avoids necessity for additional notify/DOIF/watchdog devices and can make GEOFANCY attribute <i>devAlias</i> obsolete. (using more than one UUID/device might not be a good idea as location my leap)
</li>
<li>
<b>rr_locationHome</b> - locations matching these will be treated as being at home; first entry reflects default value to be used with state correlation; separate entries by space; defaults to 'home'
</li>
@ -1444,6 +1442,9 @@ sub ROOMMATE_StartInternalTimers($$) {
<li>
<b>rr_autoGoneAfter</b> - Anzahl der Stunden, nach denen sich der Status automatisch auf 'gone' ändert, wenn der aktuellen Status 'absent' ist; Standard ist 36 Stunden
</li>
<li>
<b>rr_geofenceUUIDs</b> - Mit Komma getrennte Liste von Ger&auml;te UUIDs, die ihren Standort &uuml;ber <a href="#GEOFANCY">GEOFANCY</a> aktualisieren. Vermeidet zus&auml;tzliche notify/DOIF/watchdog Ger&auml;te und kann als Ersatz für das GEOFANCY attribute <i>devAlias</i> dienen. (hier ehr als eine UUID/Device zu hinterlegen ist eher keine gute Idee da die Lokation dann wom&ouml;glich anfängt zu springen)
</li>
<li>
<b>rr_locationHome</b> - hiermit übereinstimmende Lokationen werden als zu Hause gewertet; der erste Eintrag wird für das Zusammenspiel bei Statusänderungen benutzt; mehrere Einträge durch Leerzeichen trennen; Standard ist 'home'
</li>

View File

@ -24,16 +24,6 @@
# You should have received a copy of the GNU General Public License
# along with fhem. If not, see <http://www.gnu.org/licenses/>.
#
#
# Version: 1.1.2
#
# Major Version History:
# - 1.1.0 - 2014-02-06
# -- Support for both apps: Geofency and Locative
#
# - 1.0.0 - 2014-01-09
# -- First release
#
##############################################################################
package main;
@ -166,27 +156,28 @@ sub GEOFANCY_Set($@) {
###################################
sub GEOFANCY_CGI() {
# Locative
# Locative.app
# /$infix?device=UUIDdev&id=UUIDloc&latitude=xx.x&longitude=xx.x&trigger=(enter|exit)
#
# Geofency
# Geofency.app
# /$infix?id=UUIDloc&name=locName&entry=(1|0)&date=DATE&latitude=xx.x&longitude=xx.x&device=UUIDdev
my ($request) = @_;
my $hash;
my $name = "";
my $link = "";
my $URI = "";
my $device = "";
my $id = "";
my $lat = "";
my $long = "";
my $address = "-";
my $entry = "";
my $msg = "";
my $date = "";
my $time = "";
my $locName = "";
my $name = "";
my $link = "";
my $URI = "";
my $device = "";
my $deviceAlias = "-";
my $id = "";
my $lat = "";
my $long = "";
my $address = "-";
my $entry = "";
my $msg = "";
my $date = "";
my $time = "";
my $locName = "";
# data received
if ( $request =~ m,^(/[^/]+?)(?:\&|\?)(.*)?$, ) {
@ -331,8 +322,9 @@ 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
$date = $webArgs->{date};
$lat = $webArgs->{latitude};
$long = $webArgs->{longitude};
$address = $webArgs->{address} if (defined($webArgs->{address}));
$device = $webArgs->{device};
$address = $webArgs->{address}
if ( defined( $webArgs->{address} ) );
$device = $webArgs->{device};
}
else {
return "fatal error";
@ -341,7 +333,7 @@ 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
# no data received
else {
Log3 undef, 3, "GEOFANCY: No data received";
Log3 undef, 5, "GEOFANCY: No data received";
return ( "text/plain; charset=utf-8", "NOK No data received" );
}
@ -356,6 +348,66 @@ 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
$hash = $defs{$name};
# update ROOMMATE devices associated with this device UUID
my $matchingResident = 0;
if ( defined( $modules{ROOMMATE}{defptr} ) ) {
Log3 $name, 5, "GEOFANCY $name: found defptr for ROOMMATE\n"
. Dumper( $modules{ROOMMATE}{defptr} );
while ( my ( $key, $value ) = each %{ $modules{ROOMMATE}{defptr} } ) {
Log3 $name, 5, "GEOFANCY $name: Checking rr_geofenceUUIDs for $key";
my $geofenceUUIDs = AttrVal( $key, "rr_geofenceUUIDs", undef );
next if !$geofenceUUIDs;
Log3 $name, 5,
"GEOFANCY $name: ROOMMATE device $key has assigned UUIDs: $geofenceUUIDs";
my @UUIDs = split( ',', $geofenceUUIDs );
if (@UUIDs) {
foreach (@UUIDs) {
if ( $_ eq $device ) {
Log3 $name, 4,
"GEOFANCY $name: Found matching UUID at ROOMMATE device $key";
$deviceAlias = $key;
$matchingResident = 1;
last;
}
}
}
last if $matchingResident eq "1";
}
}
# update GUEST devices associated with this device UUID
if ( $matchingResident == 0 && defined( $modules{GUEST}{defptr} ) ) {
while ( my ( $key, $value ) = each %{ $modules{GUEST}{defptr} } ) {
my $geofenceUUIDs = AttrVal( $key, "rg_geofenceUUIDs", undef );
next if !$geofenceUUIDs;
Log3 $name, 5,
"GEOFANCY $name: GUEST device $key has assigned UUIDs: $geofenceUUIDs";
my @UUIDs = split( ',', $geofenceUUIDs );
if (@UUIDs) {
foreach (@UUIDs) {
if ( $_ eq $device ) {
Log3 $name, 4,
"GEOFANCY $name: Found matching UUID at GUEST device $key";
$deviceAlias = $key;
$matchingResident = 1;
last;
}
}
}
last if $matchingResident eq "1";
}
}
# Device alias handling
#
delete $hash->{helper}{device_aliases}
@ -377,11 +429,15 @@ 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
}
}
$device = $hash->{helper}{device_aliases}{$device}
if $hash->{helper}{device_aliases}{$device};
$deviceAlias = $hash->{helper}{device_aliases}{$device}
if ( $hash->{helper}{device_aliases}{$device} && $matchingResident == 0 );
Log3 $name, 4,
"GEOFANCY $name: id=$id name=$locName entry=$entry date=$date lat=$lat long=$long dev=$device";
"GEOFANCY $name: id=$id name=$locName trig=$entry date=$date lat=$lat long=$long address:$address dev=$device devAlias=$deviceAlias";
Log3 $name, 3,
"GEOFANCY $name: Unknown device UUID $device: Set attribute devAlias for $name or assign $device to any ROOMMATE or GUEST device using attribute r*_geofenceUUIDs"
if ( $deviceAlias eq "-" );
readingsBeginUpdate($hash);
@ -399,63 +455,91 @@ 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
# General readings
readingsBulkUpdate( $hash, "state",
"id:$id name:$locName trig:$entry date:$date lat:$lat long:$long address:$address dev:$device"
"id:$id trig:$entry date:$date lat:$lat long:$long dev:$device devAlias=$deviceAlias"
);
readingsBulkUpdate( $hash, "lastDeviceUUID", $device );
readingsBulkUpdate( $hash, "lastDevice", $deviceAlias );
$id = $locName if ( defined($locName) && $locName ne "" );
# update local device readings if
# - UUID was not assigned to any resident device
# - UUID has a defined devAlias
if ( $matchingResident == 0 && $deviceAlias ne "-" ) {
readingsBulkUpdate( $hash, "lastDevice", $device );
readingsBulkUpdate( $hash, "lastArr", $device . " " . $id )
if ( $entry eq "enter" || $entry eq "1" );
readingsBulkUpdate( $hash, "lastDep", $device . " " . $id )
if ( $entry eq "exit" || $entry eq "0" );
$id = $locName if ( defined($locName) && $locName ne "" );
if ( $entry eq "enter" || $entry eq "1" || $entry eq "test" ) {
Log3 $name, 4, "GEOFANCY $name: $device arrived at $id";
readingsBulkUpdate( $hash, $device, "arrived " . $id );
readingsBulkUpdate( $hash, "currLoc_" . $device, $id );
readingsBulkUpdate( $hash, "currLocLat_" . $device, $lat );
readingsBulkUpdate( $hash, "currLocLong_" . $device, $long );
readingsBulkUpdate( $hash, "currLocAddr_" . $device, $address );
readingsBulkUpdate( $hash, "currLocTime_" . $device, $time );
}
elsif ( $entry eq "exit" || $entry eq "0" ) {
my $currReading;
my $lastReading;
readingsBulkUpdate( $hash, "lastArr", $deviceAlias . " " . $id )
if ( $entry eq "enter" || $entry eq "1" );
readingsBulkUpdate( $hash, "lastDep", $deviceAlias . " " . $id )
if ( $entry eq "exit" || $entry eq "0" );
Log3 $name, 4, "GEOFANCY $name: $device left $id and is underway";
# backup last known location if not "underway"
$currReading = "currLoc_" . $device;
if ( defined( $hash->{READINGS}{$currReading}{VAL} )
&& $hash->{READINGS}{$currReading}{VAL} ne "underway" )
{
foreach ( 'Loc', 'LocLat', 'LocLong' ) {
$currReading = "curr" . $_ . "_" . $device;
$lastReading = "last" . $_ . "_" . $device;
readingsBulkUpdate( $hash, $lastReading,
$hash->{READINGS}{$currReading}{VAL} )
if ( defined( $hash->{READINGS}{$currReading}{VAL} ) );
}
$currReading = "currLocTime_" . $device;
readingsBulkUpdate(
$hash,
"lastLocArr_" . $device,
$hash->{READINGS}{$currReading}{VAL}
) if ( defined( $hash->{READINGS}{$currReading}{VAL} ) );
readingsBulkUpdate( $hash, "lastLocDep_" . $device, $time );
if ( $entry eq "enter" || $entry eq "1" || $entry eq "test" ) {
Log3 $name, 4, "GEOFANCY $name: $deviceAlias arrived at $id";
readingsBulkUpdate( $hash, $deviceAlias, "arrived " . $id );
readingsBulkUpdate( $hash, "currLoc_" . $deviceAlias, $id );
readingsBulkUpdate( $hash, "currLocLat_" . $deviceAlias, $lat );
readingsBulkUpdate( $hash, "currLocLong_" . $deviceAlias, $long );
readingsBulkUpdate( $hash, "currLocAddr_" . $deviceAlias,
$address );
readingsBulkUpdate( $hash, "currLocTime_" . $deviceAlias, $time );
}
elsif ( $entry eq "exit" || $entry eq "0" ) {
my $currReading;
my $lastReading;
readingsBulkUpdate( $hash, $device, "left " . $id );
readingsBulkUpdate( $hash, "currLoc_" . $device, "underway" );
readingsBulkUpdate( $hash, "currLocLat_" . $device, "-" );
readingsBulkUpdate( $hash, "currLocLong_" . $device, "-" );
readingsBulkUpdate( $hash, "currLocAddr_" . $device, "-" );
readingsBulkUpdate( $hash, "currLocTime_" . $device, $time );
Log3 $name, 4,
"GEOFANCY $name: $deviceAlias left $id and is in transit";
# backup last known location if not "underway"
$currReading = "currLoc_" . $deviceAlias;
if ( defined( $hash->{READINGS}{$currReading}{VAL} )
&& $hash->{READINGS}{$currReading}{VAL} ne "underway" )
{
foreach ( 'Loc', 'LocLat', 'LocLong', 'LocAddr' ) {
$currReading = "curr" . $_ . "_" . $deviceAlias;
$lastReading = "last" . $_ . "_" . $deviceAlias;
readingsBulkUpdate( $hash, $lastReading,
$hash->{READINGS}{$currReading}{VAL} )
if ( defined( $hash->{READINGS}{$currReading}{VAL} ) );
}
$currReading = "currLocTime_" . $deviceAlias;
readingsBulkUpdate(
$hash,
"lastLocArr_" . $deviceAlias,
$hash->{READINGS}{$currReading}{VAL}
) if ( defined( $hash->{READINGS}{$currReading}{VAL} ) );
readingsBulkUpdate( $hash, "lastLocDep_" . $deviceAlias,
$time );
}
readingsBulkUpdate( $hash, $deviceAlias, "left " . $id );
readingsBulkUpdate( $hash, "currLoc_" . $deviceAlias, "underway" );
readingsBulkUpdate( $hash, "currLocLat_" . $deviceAlias, "-" );
readingsBulkUpdate( $hash, "currLocLong_" . $deviceAlias, "-" );
readingsBulkUpdate( $hash, "currLocAddr_" . $deviceAlias, "-" );
readingsBulkUpdate( $hash, "currLocTime_" . $deviceAlias, $time );
}
}
readingsEndUpdate( $hash, 1 );
# trigger update of resident device readings
if ( $matchingResident == 1 ) {
my $trigger = 0;
$trigger = 1
if ( $entry eq "enter" || $entry eq "1" || $entry eq "test" );
$locName = $id if ( $locName eq "" );
ROOMMATE_SetLocation(
$deviceAlias, $locName, $trigger, $id, $time,
$lat, $long, $address, $device
) if ( $defs{$deviceAlias}{TYPE} eq "ROOMMATE" );
GUEST_SetLocation(
$deviceAlias, $locName, $trigger, $id, $time,
$lat, $long, $address, $device
) if ( $defs{$deviceAlias}{TYPE} eq "GUEST" );
}
$msg = "$entry OK";
$msg .= "\ndevice=$device id=$id lat=$lat long=$long trig=$entry"
if ( $entry eq "test" );
@ -534,17 +618,22 @@ sub GEOFANCY_ISO8601UTCtoLocal ($) {
<a name="GEOFANCYattr" id="GEOFANCYattr"></a> <b>Attributes</b><br>
<br>
<ul>
<li>devAlias: can be used to rename device names in the format DEVICEUUID:Aliasname. Separate using blank to rename multiple devices.
<li>devAlias: Mandatory attribute to assign device name alias to an UUID in the format DEVICEUUID:Aliasname (most readings will only be created if devAlias was defined).<br>
Separate using <i>blank</i> to rename multiple device UUIDs.<br>
<br>
Should you be using GEOFANCY together with <a href="#ROOMMATE">ROOMMATE</a> or <a href="#GUEST">GUEST</a> you might consider using attribute r*_geofenceUUIDs directly at those devices instead.
</li>
</ul><br>
<br>
<b>Usage information</b><br>
<b>Usage information / Hints on Security</b><br>
<br>
<div style="margin-left: 2em">
Likely your FHEM installation is not reachable directly from the internet (good idea!).<br>
It is recommended to have a reverse proxy like nginx or Apache in front of FHEM where you can make sure access is only possible to specific subdirectories like /fhem/geo.<br>
You might also want to think about protecting the access by using HTTP Basic Authentication and encryption via SSL.<br>
Also the definition of a dedicated FHEMWEB instance for that purpose might help to restrict FHEM's functionality (note that the 'hidden' attributes of FHEMWEB currently do NOT protect from just guessing/knowing the correct URL!)<br>
It is recommended to have a reverse proxy like <a href="http://loredo.me/post/116633549315/geeking-out-with-haproxy-on-pfsense-the-ultimate">HAproxy</a>, <a href="http://www.apsis.ch/pound/">Pound</a> or <a href="https://www.varnish-cache.org/">Varnish</a> in front of FHEM where you can make sure access is only possible to a specific URI like /fhem/geo. Apache or Nginx might do as well. However, in case you have Apache or Nginx running already you should still consider one of the named reverse proxies in front of it for fine-grain security configuration.<br>
<br>
You might also want to think about protecting the access by using HTTP Basic Authentication and encryption via TLS/SSL. Using TLS offloading in the reverse proxy software is highly recommended and software like HAproxy provides high control of data flow for TLS.<br>
<br>
Also the definition of a dedicated FHEMWEB instance for that purpose together with <a href="#alllowed">allowed</a> might help to restrict FHEM's functionality (e.g. set attributes allowedCommands and allowedDevices to ",". Note that attributes <i>hiddengroup</i> and <i>hiddenroom</i> of FHEMWEB do NOT protect from just guessing/knowing the correct URI but would help tremendously to prevent easy inspection of your FHEM setup.)<br>
<br>
To make that reverse proxy available from the internet, just forward the appropriate port via your internet router.<br>
<br>