Da ich in einer Wohnung mit vielen Heizkörpern günstige MAX! Heizkörperventile einsetze, bin ich auch wieder zum Nutzer der FHEM Homeautomatisierungssoftware geworden.
FHEM Anwesenheitsstatus |
Vieles, was ich sonst per BASH Script und Crontabs gelöst hatte, lässt sich so sauber per Webfrontend umsetzen. Es gibt für alle erdenklichen Anforderungen ein Modul - so z.B. auch für die Fritz!Box.
Allerdings scheint das Modul 72_FRITZBOX.pm noch nicht so richtig in der "neuen" SOAP Zeit angekommen zu sein. So wird TR-064 zwar für Nachrichten unterstützt, aber vieles scheint noch über die Fernsteuerung der Web-Oberfläche zu laufen.
Für die meisten Leute funktioniert das ja auch und bringt darüber hinaus eine Unmenge weiterer Funktionen mit sich.
Für mich disqualifizierte sich das Modul dadurch, dass ich das Polling-Interval auf minimal 60 Sekunden stellen konnte (und auch ständig Events generierte - jede Signalstärkenänderung eines Geräts = neuer Event). Also habe ich eine eigene kleine Anwesenheitserkennung per Perl geschrieben, die per HTTP Aufruf mit FHEM kommuniziert.
In FHEM habe ich zunaächst ein RESIDENTS "Gerät" angelegt und zwei ROOMMATEs.
define Familie RESIDENTS define Jennifer ROOMMATE Familie attr Jennifer rr_autoGoneAfter 1 define Hansi ROOMMATE Familie attr Hansi rr_autoGoneAfter 1
Das schöne an diesen Modulen sind verschiedene Eigenschaften und Aktionen, die sich ohne Notifys auf das Resident Gerät auswirken - z.B. die automatische vollständige Abwesenheit nach einer Stunde (in meiner Konfiguration). Man könnte auch auf die Roomate Geräte verzichten, aber dann hat man z.B. keine Auto-Abwesenheit.
Nun kann ich die Anwesenheit einfach per HTTP GET ändern:
curl -s http://localhost:8083/fhem?cmd=set%20Hansi%20home curl -s http://localhost:8083/fhem?cmd=set%20Hansi%20absent
Fehlt noch der Code, um den WLAN Router abzufragen. Auf einem OpenWRT Router könnte man z.B. folgenden Code einsetzen:
#!/bin/sh STATION="a1:b1:c1:d1:e1:f1|a2:b2:c2:d2:e2:f2|a3:b3:c3:d3:e3:f3|a4:b4:c4:d4:e4:f4" STATE="" COUNT=0 MAXCOUNT=3 while [ true ] ; do PRESENCE=`iw dev wlan1 station dump | grep -E -i "$STATION"` echo $PRESENCE if [ -n "$PRESENCE" ] ; then if [ -z "$STATE" ] ; then wget -q -O - http://fhemhost:8083/fhem?cmd=set%20Familie%20home > /dev/null 2>&1 STATE="here" fi COUNT=0 else if [ -n "$STATE" ] ; then if [ $COUNT -ge $MAXCOUNT ] ; then wget -q -O - http://fhemhost:8083/fhem?cmd=set%20Familie%20absent > /dev/null 2>&1 STATE="" else COUNT=$((COUNT+1)) fi fi fi sleep 10 done
Um eine Fritz!Box per TR064 abzufragen, benutze ich SOAP::Lite in Perl (siehe auch mein letzter Fritz!Box TR064 Beitrag).
Ursprünglich hatte ich versucht, tatsächlich nur die Assoziierten WLAN Geräte abzufragen, allerdings ist die Liste, die man mit GetTotalAssociations() erhält nicht immer vollständig.
Selbst das direkte Abfragen von GetGenericAssociatedDeviceInfo(NewAssociatedDeviceMACAddress) ergab bei offensichtlich verbundenen und aktiven Geräten einen NoSuchEntryInArray Fehler.
Daher gehe ich einen ähnlichen Weg wie in der Unbound Host Extrahierung und benutze die Host Informationen der Box. Das hat auch den Vorteil, aktuell (FRITZ!OS: 06.90) ohne Authentifizierung an der Fritz!Box auzukommen.
#!/usr/bin/perl -wT use strict; use SOAP::Lite; # +trace => 'debug', readable => 1; use LWP::Simple; # MACs in lower case!!! my %station = ( 'a1:b1:c1:d1:e1:f1' => 'Jennifer', 'a2:b2:c2:d2:e2:f2' => 'Hansi', ); # wait for interval before a device becomes absent my $maxinterval = 3; my %presence = ( 'Jennifer' => 0, 'Hansi' => 0, ); my $fritzboxIP = 'fritz.box'; my $fritzboxPort = '49000'; my $login = 'smarthome'; my $password = 'geheim'; my $sleep = 10; my $client = SOAP::Lite -> uri( 'urn:dslforum-org:service:Hosts:1' ) -> proxy( 'http://'.$fritzboxIP.':'.$fritzboxPort.'/upnp/control/hosts' ); while( 1 ) { eval { foreach my $mac (keys %station) { my $host = $client->GetSpecificHostEntry(SOAP::Data->type('string')->name('NewMACAddress')->value($mac))->valueof('//GetSpecificHostEntryResponse'); if( $host->{'NewActive'} == 1) { # print( $mac ."\n" ); if( ! $presence{$station{$mac}} ) { print( 'Anwesenheit erkannt fuer: '. $station{$mac} ."\n" ); get('http://localhost:8088/fhem?cmd=set%20'. $station{$mac} .'%20home'); } $presence{$station{$mac}} = $maxinterval; } } foreach my $p ( keys %presence ) { if( $presence{$p} ) { $presence{$p}--; if( ! $presence{$p} ) { print( 'Abwesenheit erkannt fuer: '. $p ."\n" ); get('http://localhost:8088/fhem?cmd=set%20'. $p .'%20absent'); } } } } or do { if( $@ ){ print( "Error: $@\n" ); } }; sleep( $sleep ); } # in case authentication is needed sub SOAP::Transport::HTTP::Client::get_basic_credentials { return $login => $password; }
Update (2017-11-07): Auf einer Fritz!Box mit FREETZ
Sollte die Fritz!Box unter Freetz laufen, kann das Script natürlich auch wieder direkt auf der Fritzbox den WLAN Status überwachen:#!/bin/sh NAME="Jennifer Hansi" STATION="A1:B1:C1:D1:E1:F1 A2:B2:C2:D2:E2:F2" SLEEP=10 MAXINTERVAL=3 ###################################################### IDX=0 for N in $NAME ; do IDX=$((IDX+1)) eval NAME_${IDX}=$N eval STATE_${IDX}=0 eval COUNT_${IDX}=0 done IDX=0 for S in $STATION ; do IDX=$((IDX+1)) eval STATION_${IDX}=$S done while [ true ] ; do C=0 FOUND=0 MAX=$(ctlmgr_ctl r wlan status/wlanlist/count) while [ $C -lt $MAX ] ; do D=$(ctlmgr_ctl r wlan status/wlanlist${C}/state) if [ $D -gt 0 ] ; then MAC=$(ctlmgr_ctl r wlan status/wlanlist${C}/mac) for I in $(seq 1 $IDX) ; do if [ "$MAC" = "$(eval echo \$STATION_${I})" ] ; then if [ $(eval echo \$STATE_${I}) -eq 0 ] ; then # echo "$(eval echo \$NAME_${I}) home" wget -q -O - "http://fhemhost:8083/fhem?cmd=set%20$(eval echo \$NAME_${I})%20home" > /dev/null eval STATE_${I}=1 fi eval COUNT_${I}=0 FOUND=$((FOUND+1)) if [ $FOUND -ge $IDX ] ; then C=$MAX fi fi done fi C=$((C+1)) done for I in $(seq 1 $IDX) ; do if [ $(eval echo \$STATE_${I}) -gt 0 ] ; then if [ $(eval echo \$COUNT_${I}) -gt $MAXINTERVAL ] ; then # echo "$(eval echo \$NAME_${I}) absent" wget -q -O - "http://fhemhost:8083/fhem?cmd=set%20$(eval echo \$NAME_${I})%20absent" > /dev/null eval STATE_${I}=0 else eval COUNT_${I}=$(($(eval echo \$COUNT_${I})+1)) fi fi done sleep $SLEEP done
Keine Kommentare:
Kommentar veröffentlichen