Mercurial > code > home > repos > homeauto
diff service/tomatoWifi/tomatoWifi.py @ 867:d9bc9a82dcca
redo wifi scraper to work with zyxel router report page too. add last connected time (from mongo) to web table
Ignore-this: 18bade72e14d40532bd019791d03fa7d
darcs-hash:20130407000819-312f9-6b262edf71421b17947050e80560005d7bffa80b
author | drewp <drewp@bigasterisk.com> |
---|---|
date | Sat, 06 Apr 2013 17:08:19 -0700 |
parents | 1168b0cd90af |
children | bca6d6c63bdc |
line wrap: on
line diff
--- a/service/tomatoWifi/tomatoWifi.py Sun Feb 10 13:41:35 2013 -0800 +++ b/service/tomatoWifi/tomatoWifi.py Sat Apr 06 17:08:19 2013 -0700 @@ -12,18 +12,19 @@ """ from __future__ import division import sys, cyclone.web, json, traceback, time, pystache, datetime, logging +import web.utils from cyclone.httpclient import fetch sys.path.append("/home/drewp/projects/photo/lib/python2.7/site-packages") from dateutil import tz from twisted.internet import reactor, task -from twisted.internet.defer import inlineCallbacks, returnValue - +from twisted.internet.defer import inlineCallbacks from pymongo import Connection, DESCENDING from rdflib import Namespace, Literal, URIRef sys.path.append("/my/site/magma") from stategraph import StateGraph from wifi import Wifi +from dhcpparse import addDhcpData sys.path.append("/my/proj/homeauto/lib") from cycloneerr import PrettyErrorHandler @@ -53,21 +54,44 @@ class Table(PrettyErrorHandler, cyclone.web.RequestHandler): def get(self): - def rowDict(addr): - addr['cls'] = "signal" if addr.get('signal') else "nosignal" - if 'lease' in addr: - addr['lease'] = addr['lease'].replace("0 days, ", "") - return addr + def rowDict(row): + row['cls'] = "signal" if row.get('connected') else "nosignal" + if 'name' not in row: + row['name'] = row['clientHostname'] + if 'signal' not in row: + row['signal'] = 'yes' if row['connected'] else 'no' + + try: + conn = self.whenConnected(row['mac']) + row['connectedAgo'] = web.utils.datestr( + conn.astimezone(tz.tzutc()).replace(tzinfo=None)) + except ValueError: + pass + + return row self.set_header("Content-Type", "application/xhtml+xml") self.write(pystache.render( open("table.mustache").read(), dict( rows=sorted(map(rowDict, self.settings.poller.lastAddrs), - key=lambda a: (a.get('router'), - a.get('name'), - a.get('mac')))))) + key=lambda a: (not a.get('connected'), + a.get('name')))))) + def whenConnected(self, macThatIsNowConnected): + lastArrive = None + for ev in self.settings.mongo.find({'address': macThatIsNowConnected}, + sort=[('created', -1)], + max_scan=100000): + if ev['action'] == 'arrive': + lastArrive = ev + if ev['action'] == 'leave': + break + if lastArrive is None: + raise ValueError("no past arrivals") + + return lastArrive['created'] + class Json(PrettyErrorHandler, cyclone.web.RequestHandler): def get(self): @@ -91,36 +115,22 @@ raise ValueError("poll data is stale. age=%s" % age) for dev in self.settings.poller.lastAddrs: - if not dev.get('signal'): + if not dev.get('connected'): continue uri = URIRef("http://bigasterisk.com/wifiDevice/%s" % dev['mac']) g.add((uri, ROOM['macAddress'], Literal(dev['mac']))) + g.add((uri, ROOM['connected'], aps)) - if 'rawName' in dev: - g.add((uri, ROOM['wifiNetworkName'], Literal(dev['rawName']))) - g.add((uri, ROOM['deviceName'], Literal(dev['name']))) - g.add((uri, ROOM['signalStrength'], Literal(dev['signal']))) + if 'clientHostname' in dev: + g.add((uri, ROOM['wifiNetworkName'], Literal(dev['clientHostname']))) + if 'name' in dev: + g.add((uri, ROOM['deviceName'], Literal(dev['name']))) + if 'signal' in dev: + g.add((uri, ROOM['signalStrength'], Literal(dev['signal']))) self.set_header('Content-type', 'application/x-trig') self.write(g.asTrig()) -class Application(cyclone.web.Application): - def __init__(self, wifi, poller): - handlers = [ - (r"/", Index), - (r'/json', Json), - (r'/graph', GraphHandler), - (r'/table', Table), - #(r'/activity', Activity), - ] - settings = { - 'wifi' : wifi, - 'poller' : poller, - 'mongo' : Connection('bang', 27017, - tz_aware=True)['house']['sensor'] - } - cyclone.web.Application.__init__(self, handlers, **settings) - class Poller(object): def __init__(self, wifi, mongo): self.wifi = wifi @@ -137,8 +147,9 @@ def poll(self): try: newAddrs = yield self.wifi.getPresentMacAddrs() - - newWithSignal = [a for a in newAddrs if a.get('signal')] + addDhcpData(newAddrs) + + newWithSignal = [a for a in newAddrs if a.get('connected')] actions = self.computeActions(newWithSignal) for action in actions: @@ -161,33 +172,27 @@ log.error("poll error: %s\n%s", e, traceback.format_exc()) def computeActions(self, newWithSignal): - def removeVolatile(a): - ret = dict((k,v) for k,v in a.items() if k in ['name', 'mac']) - ret['signal'] = bool(a.get('signal')) - return ret - - def find(a, others): - a = removeVolatile(a) - return any(a == removeVolatile(o) for o in others) - actions = [] def makeAction(addr, act): - return dict(sensor="wifi", + d = dict(sensor="wifi", address=addr.get('mac'), name=addr.get('name'), - networkName=addr.get('rawName'), + networkName=addr.get('clientHostname'), action=act) + if act == 'arrive' and 'ip' in addr: + # this won't cover the possible case that you get on + # wifi but don't have an ip yet. We'll record an + # action with no ip and then never record your ip. + d['ip'] = addr['ip'] + return d for addr in newWithSignal: - if not find(addr, self.lastWithSignal): - # the point of all the removeVolatile stuff is so - # I have the complete addr object here, although - # it is currently mostly thrown out by makeAction + if addr['mac'] not in [r['mac'] for r in self.lastWithSignal]: actions.append(makeAction(addr, 'arrive')) for addr in self.lastWithSignal: - if not find(addr, newWithSignal): + if addr['mac'] not in [r['mac'] for r in newWithSignal]: actions.append(makeAction(addr, 'leave')) return actions @@ -227,13 +232,24 @@ from twisted.python import log as twlog #log.startLogging(sys.stdout) #log.setLevel(10) - log.setLevel(logging.DEBUG) + #log.setLevel(logging.DEBUG) - mongo = Connection('bang', 27017)['visitor']['visitor'] + mongo = Connection('bang', 27017, tz_aware=True)['visitor']['visitor'] wifi = Wifi() poller = Poller(wifi, mongo) task.LoopingCall(poller.poll).start(1/config['pollFrequency']) - reactor.listenTCP(config['servePort'], Application(wifi, poller)) + reactor.listenTCP(config['servePort'], + cyclone.web.Application( + [ + (r"/", Index), + (r'/json', Json), + (r'/graph', GraphHandler), + (r'/table', Table), + #(r'/activity', Activity), + ], + wifi=wifi, + poller=poller, + mongo=mongo)) reactor.run()