Ich verwende zuhause eine zuverlässige Lösung für die Anwesenheitserkennung von Personen mittels ihrer Smartphones. Das Problem bei den Smartphones ist, dass man sie zwar pingen kann, aber sie nur antworten, wenn sie entweder nicht im Standby sind oder geladen werden. Wenn sie einfach nur so rumliegen, dann antworten sie nicht auf das Ping. Plugins wie homebridge-people versuchen das Problem zu reduzieren, aber zuverlässig ist es nicht.
Meine Lösung funktioniert so: Wenn ein SmartPhone zuhause ist, ist es im WLAN und bleibt auch im WLAN, selbst wenn es im Standby ist! Ich habe bei mir zuhause vier Access Points vom Typ Apple AirPort Extreme, die man per SNMP abfragen kann. So erhält man eine Liste aller aller MAC-Adressen aller Geräte, die im WLAN angemeldet sind. Ich verwende das Plugin homebridge-http-webhooks. Damit lassen sich beliebige Geräte anlegen, unter anderem "Occupy Sensor" für jede Person. Die config.json enthält bei mir den folgenden Eintrag:
{
"platform": "HttpWebHooks",
"webhook_port": "10000",
"cache_directory": "/var/homebridge/.node-persist/storage",
"sensors": [
{
"id": "xxx",
"name": "XXX (Mein Name)",
"type": "occupancy"
},
{
"id": "yyy",
"name": "(Name der Frau)",
"type": "occupancy"
},
{
"id": "zzz",
"name": "(Name der grossen Tochter)",
"type": "occupancy"
},
{
"id": "anyone",
"name": "Irgendjemand",
"type": "occupancy"
}
]
},
Alles anzeigen
Damit erscheinen insgesamt vier Occupancy Sensors, der letzte soll aktiv werden, wenn irgendjemand zuhause ist. Das Plugin kann nun per URL von außen über Port 10000 gesteuert werden. Die Logik sitzt bei mir im folgenden Perl-Skript:
#!/usr/bin/perl -w
use strict;
use Data::Dumper;
use Net::Ping;
use Net::SNMP;
use LWP::UserAgent;
my %state = ();
my $anyone = 'anyone';
my $baseurl = 'http://localhost:10000';
my @aps = ('axug','axeg','axog','axdg');
my $oid = '.1.3.6.1.4.1.63.501.3.2.2.1.1';
my @here;
my %persons = (
'xxx' => { 'mac' => '9c:f4:8e:xx:xx:xx' },
'yyy' => { 'mac' => '9c:fc:01:xx:xx:xx' },
'zzz' => { 'mac' => '60:a3:7d:xx:xx:xx' }
);
my $ping = Net::Ping->new();
for my $ap (@aps) {
if( $ping->ping($ap,2) ) {
#print "$ap is up\n";
my ($session, $error) = Net::SNMP->session(
'-hostname' => $ap,
'-community' => 'public',
'-translate' => [-octetstring => 0],
'-version' => 'snmpv2c'
);
my %table;
my $table = $session->get_table(
'-baseoid' => $oid
);
if (!defined $table) {
printf "ERROR: %s\n", $session->error();
$session->close();
next;
}
$session->close();
foreach my $mac (values %$table) {
$mac = lc $mac;
push @here,$mac;
#print $mac."\n";
}
} else {
#print "$ap is down\n";
}
}
my $ua = LWP::UserAgent->new();
my $anystate = 'false';
for my $person (keys %persons) {
my $mac = $persons{$person}->{'mac'};
my $here = 0;
if( grep( $_ eq $mac, @here ) ) {
$here = 1;
$anystate='true';
}
$persons{$person}->{'should'} = $here;
my $state = 'false';
$state = 'true' if $here;
my $change = 0;
{
my $url="$baseurl?accessoryId=$person";
my $req = HTTP::Request->new( GET => $url );
my $res = $ua->request( $req );
my (undef,$right) = split( /"state":/, $res->content() );
#print "$right\n";
unless( $right =~ /$state/ ) {
#print "Need to change $person\n";
$change = 1;
}
}
if( $change ) {
#print "Chaning $person to $state\n";
my $url="$baseurl?accessoryId=$person&state=$state";
my $req = HTTP::Request->new( GET => $url );
my $res = $ua->request( $req );
my $content = $res->content;
#print "content=$content\n";
}
}
my $url = "$baseurl?accessoryId=$anyone&state=$anystate";
my $req = HTTP::Request->new( GET => $url );
my $res = $ua->request( $req );
Alles anzeigen
Man kann im Kopf der Datei die folgenden Parameter anpassen:
- aps: Die Liste der Access Points, bei mir axug = Untergeschoss, axeg = Erdgeschoss, axog = Obergeschoss, axdg = Dachgeschoss
- persons: Die Liste der Personen (die keys müssen natürlich identisch sein mit den Einträgen in der config.json) und die MAC-Adresse ihrer iPhones.
Das Script ist angepasst für AirPort Extreme bis Generation 4 (die flachen), die neueren (hohen) haben wohl kein SNMP mehr, daher sind sie für mich uninteressant. Aber die Logik lässt sich leicht an andere Access Points anpassen, die ihre Zustände per SNMP rausrücken.
Dieses Skript muss noch in die Crontab eingetragen werden, damit es minütlich läuft:
Diese Anleitung dient als Anregung, wie eine zuverlässige Lösung funktioniert und eignet sich für Leute, die sich mit Unix, Perl und SNMP auskennen.