Anwesenheitserkennung mit IP Adresse

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
marioir
User
Beiträge: 4
Registriert: Freitag 4. September 2015, 11:12

Hallo ich bin neu hier im Forum.
Bastel hin und wieder an Python Scripten rum. Bisher bin ich auch ganz gut klar gekommen und lese mich auch in diversen Tutorials ein.

Mein Anliegen ist folgendermaßen, ich besitze ein LightManager Air von JBMedia für meine Lichtsteuerung, jetzt will ich bei Anwesenheit bestimmte Lichter Schalten, bzw. Lichter wieder ausschalten wenn ich die Wohnung verlasse. Natürlich soll die Abwesenheit nicht sofort wirken, sondern erst mit einer Verzögerung von ca. 10 Min. So kann man mal kurz das WLAN ausschalten oder auch mal kurz den Müll runterbringen.

Habe dazu ein kleines Programm geschrieben, mit dem Ich meine Anwesenheit auslese, Also ob Handy IP zu pingen ist oder nicht.
Soweit funktioniert es auch ganz gut, aber ab und zu kann er mein Handy nicht Pingen, und schaltet mein Staus auf Abwesend. Ein paar Sekunden später findet er das Handy wieder und setzt den Status auf Anwesend, was natürlich Nachts sehr ärgerlich ist, da im Flur mein Licht an geht. Warum wer das Handy nicht pingen kann ist mir bisher ein rätsel. Gelegentlich überspringt er auch die wartezeit und setzt den Status direkt auf Abwesend.

Hier mal mein Quellcode

Code: Alles auswählen

#!/usr/bin/env python

import os, time
import httplib
import urllib2

ip = "192.168.1.22"
logfile = "logfile.log"
warteZeit=10 #    sekunden
warteabwesend= 600 #Wartezeit abwesend

def log(msg):
  file = open(logfile,"a")
  file.write("%s: %s\n" % (time.strftime("%d.%m.%Y %H:%M:%S"), msg))
  file.close

def istAnwesend():
    response = os.system("ping -c 1 " + ip)
    return response == 0

def anwesend():
		httpServ = httplib.HTTPConnection("192.168.1.26", 80)
		httpServ.connect()
		httpServ.request('GET', "/control?key=80")
		log("Handy ist anewesend")

def abwesend():
		httpServ = httplib.HTTPConnection("192.168.1.26", 80)
		httpServ.connect()
		httpServ.request('GET', "/control?key=81")
		log("Handy ist abewesend")

def warteAufAnwesenheit():
    while not istAnwesend():
        time.sleep(warteZeit)
    anwesend()
    
def warteAufAbwesenheit():
    while istAnwesend():
        time.sleep(warteZeit)
    log("Warte 10 min ob handy wirklich abwesend ist")
    time.sleep(warteabwesend)
    while istAnwesend():
        time.sleep(warteZeit)
    abwesend()
    
if istAnwesend():
    anwesend()
    warteAufAbwesenheit()        

while True:
    warteAufAnwesenheit()        
    warteAufAbwesenheit()
Vielleicht findet ja einer von euch mein Fehler, oder es gibt noch ne bessere möglichkeit festzustellen ob mein Handy im Netzwerk ist oder nicht.

Lg Mario
Benutzeravatar
Sr4l
User
Beiträge: 1091
Registriert: Donnerstag 28. Dezember 2006, 20:02
Wohnort: Kassel
Kontaktdaten:

Lass deinen Code doch viel öfter laufen. Also jede Minute oder noch häufiger. Anschalten tust du sofort. Ausschalten tust du erst wenn das Handy X mal nicht erreichbar war. Wie oft das Handy in Folge nicht erreichbar war, musst du natürlich zählen und bei einem Erfolg wieder zurücksetzen.

PS: Setz deinen Code bitte in Code Tags, nicht in Quote. Der Button ist eins daneben. Dann ist der Code eingerückt und farbig.

PPS: Wenn du das gewünschte Verhalten hast, können wir über Verbesserungen und Style reden ;-)
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@marior: Deine Einrückung ist uneinheitlich (sollte immer 4 Leerzeichen pro Ebene sein) und Deine Namenschreibung richtet sich nicht nach PEP8. close soltest Du auch aufrufen oder besser gleich Dateien mit with öffnen.

ungetestet etwa so:

Code: Alles auswählen

import time
import datetime
import httplib
 
 
ANWESEND = 80
ABWESEND = 81

ip = "192.168.1.22"
logfile = "logfile.log"
sleep_time = 10 # in Sekunden
warteabwesend= 600 #Wartezeit abwesend in Sekunden
 
def log(msg):
    with open(logfile,"a") as log:
        file.write("{0:%d.%m.%Y %H:%M:%S}: {1}".format(datetime.datetime.now(), msg))
 
def ist_anwesend():
    response = os.system("ping -c 1 " + ip)
    return response == 0
 
def update_status(key):
    httpServ = httplib.HTTPConnection("192.168.1.26", 80)
    httpServ.connect()
    httpServ.request('GET', "/control?key=%d" % key)

def main():
    current_state = ANWESEND
    gone_time = 0
    while True:
        if ist_anwesend():
            gone_time = 0
             
        if current_state == ABWESEND and gone_time == 0:
            update_status(ANWESEND)
            current_state = ANWESEND
            log("Handy ist anwesend")
        elif current_state == ANWESEND and gone_time == warteabwesend:
            update_status(ABWESEND)
            current_state = ABWESEND
            log("Handy ist abwesend")

        gone_time += sleep_time
        time.sleep(sleep_time)

if __name__ == '__main__':
    main()
marioir
User
Beiträge: 4
Registriert: Freitag 4. September 2015, 11:12

Hey danke schon mal für eure schnelle Antwort.

Ja mit der Einrückung habe ich zurzeit noch so meine Probleme. Sag ja bin absoluter Anfänger.

@Sr4l: Habe dein Rat mal zu Herzen genommen und es ein wenig Umprogrammiert, habe es hier auf der Arbeit noch nicht testen können.

Code: Alles auswählen

#!/usr/bin/env python

import os, time
import httplib
import urllib2

ip = "192.168.1.22"
logfile = "logfile.log"
warteZeit=10 #    sekunden
warteabwesend=600 #Wartezeit abwesend
Zeit=60

def log(msg):
 	file = open(logfile,"a")
 	file.write("%s: %s\n" % (time.strftime("%d.%m.%Y %H:%M:%S"), msg))
 	file.close

def istAnwesend():
	response = os.system("ping -c 1 " + ip)
	return response == 0

def anwesend():
	httpServ = httplib.HTTPConnection("192.168.1.26", 80)
	httpServ.connect()
	httpServ.request('GET', "/control?key=80")
	log("Handy ist anewesend")

def abwesend():
	httpServ = httplib.HTTPConnection("192.168.1.26", 80)
	httpServ.connect()
	httpServ.request('GET', "/control?key=81")
	log("Handy ist abewesend")

def warteAufAnwesenheit():
	while not istAnwesend():
		time.sleep(warteZeit)
		anwesend()
    
def warteAufAbwesenheit():
	while istAnwesend():
		time.sleep(warteZeit)
		counter()

def counter():
	x = 0
	
	while x < 10:
		if not istAnwesend():
			x = x + 1
			time.sleep(Zeit)
		else:
			x = 0
	else:
		abwesend()
		x = 0
			
	    
if istAnwesend():
	anwesend()
	warteAufAbwesenheit()        

while True:
	warteAufAnwesenheit()        
	warteAufAbwesenheit()

@ Sirius3: Cool, das sieht schon um einiges Profesioeller aus, als ich es hinbekommen würde. Auch hier gilt, werde es am Wochenende mal testen und ein Feedback geben.
marioir
User
Beiträge: 4
Registriert: Freitag 4. September 2015, 11:12

Gude..

Jetzt habe ich bissl länger gebraucht um zu testen.
Wieder viel zu lange gearbeitet die Woche. Bin am Wochenende zum testen gekommen.

Das Script von Sirius3 hat noch nicht so Funktioniert, Er erkennt zwar das Handy im WLAN ist und schaltet dann auch auf anwesend nur erkennt er nicht wenn Handy nicht mehr im Netzwerk ist.
Muss mir das mal in ruhe angucken, vielleicht finde ich die Ursache ja noch.

Den Tipp mit der 10 fachen abfrage, die mir Sr4l empholen hat. Habe ich jetzt noch mal verbessert.
Es scheint auch soweit zu Funktionieren. Allerdings habe ich das Gefühl das dass Problem wohl an meinen Handy liegt.
Obwohl in den Einstellungen steht das WLAN auch in Standby aktiv sein soll, verliert er die Verbindung und lässt sich nicht mehr Pingen.
Aktiviere ich das Handy wieder schaltet alles wieder auf Anwesend.

Code: Alles auswählen

#!/usr/bin/env python

import os, time
import httplib
import urllib2

ip = "192.168.1.22"
logfile = "logfile.log"
warteZeit=10 # sekunden
Zeit=10 # Wartezeit abwesend

def log(msg):
 	file = open(logfile,"a")
 	file.write("%s: %s\n" % (time.strftime("%d.%m.%Y %H:%M:%S"), msg))
 	file.close

def istAnwesend():
	response = os.system("ping -c 1 " + ip)
	return response == 0

def anwesend():
	httpServ = httplib.HTTPConnection("192.168.1.26", 80)
	httpServ.connect()
	httpServ.request('GET', "/control?key=80")
	log("Handy ist anewesend")

def abwesend():
	httpServ = httplib.HTTPConnection("192.168.1.26", 80)
	httpServ.connect()
	httpServ.request('GET', "/control?key=81")
	log("Handy ist abewesend")

def warteAufAnwesenheit():
	while not istAnwesend():
		time.sleep(warteZeit)
	anwesend()
    
def warteAufAbwesenheit():
	while istAnwesend():
		time.sleep(warteZeit)
	counter()

def counter():
	x = 0
	
	while x < 10:
		if not istAnwesend():
			x = x + 1
			time.sleep(Zeit)
		else:
			x = 0
	else:
		abwesend()
		x = 0
		warteAufAnwesenheit()			
		
	    
if istAnwesend():
	anwesend()
	warteAufAbwesenheit()        

while True:
	warteAufAnwesenheit()        
	warteAufAbwesenheit()
BlackJack

@marioir: Am Style Guide for Python Code orientiert sich das immer noch nicht.

Die Protokolldatei wird auch immer noch nicht wieder geschlossen. Das ist bei Protokolldateien nicht ganz unwichtig wenn man die ”live” beobachten möchte, denn nur wenn man `flush()` auf einer Datei aufruft die man absichtlich offen lässt, oder eben wenn man die Datei schliesst, ist sichergestellt das auch zu dem Zeitpunkt eventuell intern gepufferte Daten geschrieben und damit ausserhalb des Programms in der Datei sichtbar sind. In Deinem Fall kann es sonst sogar passieren das die Schreibvorgänge nicht in der Reihenfolge in der sie getätigt wurden in der Datei auftauchen. Sirius3 hat die ``with``-Anweisung ja schon erwähnt und auch gezeigt wie man sie verwendet.

Den Namen `file` sollte man nicht neu binden, denn das ist bereits der Name des eingebauten Dateidatentyps. `logfile` ist ein unpassender Name für einen Datei*namen*.

Man sollte bei Namen auch unnötige Abkürzungen vermeiden. `message` ist wesentlich deutlicher als `msg`.

Python hat in der Standardbibliothek übrigens ein `logging`-Modul.

`urllib2` wird importiert, aber nirgends verwendet.

Anstatt `os.system()` sollte man das `subprocess`-Modul verwenden.

`anwesend` und `abwesend` sind keine guten Namen für Funktionen. Funktionen und Methoden werden üblicherweise nach Tätigkeiten benannt weil sie etwas tun und um sie besser von Dingen wie Zuständen oder passiven Werten unterscheiden zu können. Die beiden Funktionen sind auch fast identisch, da sollte man eine Funktion draus machen und diese entsprechend parametrisieren.

Die `counter()`-Funktion würde ich nicht aus der Wartefunktion herausziehen. Jedenfalls nicht unter *dem* Namen. Insbesondere weil die `counter()`-Funktion eigentlich die komplette Wartefunktion enthält, denn die zwei anderen Zeilen in Deiner Wartefunktion kann man da natürlich ausführen, die sind aber redundant wenn die `counter()`-Funktion richtig arbeitet.

Das (Zeit ab)Warten wird in der Funktion an der falschen Stelle gemacht beziehungsweise fehlt ein warten, denn es wird nur gewartet wenn das Mobiltelefon nicht anpingbar war. Im anderen Fall wird ohne Pause gepingt wie blöde. Was der Recher oder das Netz an der Stelle halt hergibt. Das ist Dir wahrscheinlich nur wegen der ersten beiden eigentlich überflüssigen Zeilen in der Wartefunktion nicht aufgefallen, durch die dieses Ping-Flooding erst einsetzt wenn das Telefon das erste mal nicht erreichbar war.

In der `counter()`-Funktion macht der ``else``-Zweig keinen Sinn da der in jedem Fall nach der Schleife ausgeführt wird. Der Inhalt gehört also einfach ganz normal hinter die Schleife. Und das erneute setzen von `x` auf 0 ist sinnlos da die Schleife die `x` als Bedingung prüft an der Stelle nicht mehr durchlaufen wird. Ebenso ist der Aufruf der anderen Wartefunktion unsinnig, denn wenn man an der Stelle stattdessen direkt zum Aufrufer zurückkehrt wird die als nächstes von der ``while``-Schleife im Hauptprogramm ja sowieso aufgerufen. Du denkst an der Stelle viel zu kompliziert und weiter als das innherhalb dieser Funkion nötig ist.

Ebenso überflüssig ist im Hauptprogramm der erste ``if``-Test. Ist der nicht vorhanden wird `warteAufAnwesenheit()` als erstes aufgerufen, was genau den gleichen Effekt hat.

Das schalten des Lichts sollte nicht in den Wartefunktionen passieren, denn das erwartet man beim Namen der Funktion nicht das die ausser warten noch etwas anderes macht.

Ich komme dann ungefähr bei so etwas heraus (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python
from __future__ import absolute_import, division, print_function
import httplib
import subprocess
import time
from contextlib import closing

LOG_FILENAME = 'logfile.log'
MOBILE_DEVICE_IP = '192.168.1.22'
LIGHT_SERVER_IP = '192.168.1.26'
CHECK_INTERVAL = 10  # sekunden
AWAY_CHECK_INTERVAL = 10  # Wartezeit abwesend
AN, AUS = True, False


def log(message):
    with open(LOG_FILENAME, "a") as logfile:
        logfile.write(
            '{0}: {1}\n'.format(time.strftime('%d.%m.%Y %H:%M:%S'), message)
        )


def ist_anwesend():
    return subprocess.call(['ping', '-c', '1', MOBILE_DEVICE_IP]) == 0


def warte_auf_anwesenheit():
    while not ist_anwesend():
        time.sleep(CHECK_INTERVAL)


def warte_auf_abwesenheit():
    away_count = 0
    while away_count < 10:
        if not ist_anwesend():
            away_count += 1
            time.sleep(AWAY_CHECK_INTERVAL)
        else:
            away_count = 0
            time.sleep(CHECK_INTERVAL)


def schalte_licht(state):
    with closing(httplib.HTTPConnection(LIGHT_SERVER_IP, 80)) as connection:
        connection.connect()
        connection.request(
            'GET', '/control?key={0}'.format(80 if state else 81)
        )
        log('Licht {0}geschaltet'.format('ein' if state else 'aus'))


def main():
    while True:
        warte_auf_anwesenheit()
        schalte_licht(AN)
        warte_auf_abwesenheit()
        schalte_licht(AUS)


if __name__ == '__main__':
    main()
Ich persönlich würde die deuschen Bezeichner noch durch Englische ersetzen und das externe `requests`-Modul für die Anfragen an den Webserver verwenden.
marioir
User
Beiträge: 4
Registriert: Freitag 4. September 2015, 11:12

@BlackJack

Ein sehr übersichtliches und verständliches Programm.
Werde es natürlich wieder testen.

Was die Logdatei angeht, dachte ich, dass die Datei mit "file.close" geschlossen wird.
Wahrscheinlich habe ich das beim durchlesen falsch verstanden.

Wollte von Anfang an eigentlich keine eigene Counter Funktion definieren, aber für mich war es erstmal der einfachste weg. Jetzt wo ich mir das Programm so ansehe, ist dann doch einiges sehr verständlich und merke das ich an paar stellen einfach zu kompliziert gedacht habe.

"urlliib2" war noch aus einem älteren Versuch und habe es vergessen zu löschen.

Finde gut das ihr soviel Verständnis habt und ein Leien doch recht gut unter die Arme greift.

Melde mich dann nochmal wenn ich es getestet habe.
BlackJack

@marioir: Das Ergebnis des Ausdrucks ``file.close`` ist das Attribut `close` von dem Objekt das an den Namen `file` gebunden ist. Also die Methode. Damit die Datei geschlossen wird muss man die Methode dann auch *aufrufen*: ``file.close()``. Das passiert nicht automatisch weil man sonst keine Möglichkeit hätte nur die Methode als Ergebnis zu bekommen ohne sie aufzurufen. Methoden sind auch Werte die man beispielweise in Datenstrukturen stecken oder als Argumente übergeben kann.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Aus `ist_anwesend()` könnte man besser `ist_erreichbar()` machen. Dann hätte man nämlich eine schönere Unterscheidung zwischen der Nichterreichbarkeit aus technischer Sicht einerseits und der Abwesenheit sozusagen als Interpretation einer mehrfachen Nichterreichbarkeit andererseits.
Antworten