comparison service/tomatoWifi/tomatoWifi.py @ 980:2f1cb8b5950a

rewrites for better graph export, removal of dhcp reader Ignore-this: ecc5280b15d66020412f82ad84862074 darcs-hash:20150504002120-312f9-85bcb342f5bdae78d7b6d9083929944d4a467001
author drewp <drewp@bigasterisk.com>
date Sun, 03 May 2015 17:21:20 -0700
parents 2489d111f4f1
children fc184ea1416d
comparison
equal deleted inserted replaced
979:76bb0bf74bd1 980:2f1cb8b5950a
23 from pymongo import Connection, DESCENDING 23 from pymongo import Connection, DESCENDING
24 from rdflib import Namespace, Literal, URIRef 24 from rdflib import Namespace, Literal, URIRef
25 sys.path.append("/my/site/magma") 25 sys.path.append("/my/site/magma")
26 from stategraph import StateGraph 26 from stategraph import StateGraph
27 from wifi import Wifi 27 from wifi import Wifi
28 from dhcpparse import addDhcpData
29 28
30 sys.path.append("/my/proj/homeauto/lib") 29 sys.path.append("/my/proj/homeauto/lib")
31 from cycloneerr import PrettyErrorHandler 30 from cycloneerr import PrettyErrorHandler
32 from logsetup import log 31 from logsetup import log
33 32
34 import rdflib
35 from rdflib import plugin
36 plugin.register(
37 "sparql", rdflib.query.Processor,
38 "rdfextras.sparql.processor", "Processor")
39 plugin.register(
40 "sparql", rdflib.query.Result,
41 "rdfextras.sparql.query", "SPARQLQueryResult")
42 33
43 DEV = Namespace("http://projects.bigasterisk.com/device/") 34 DEV = Namespace("http://projects.bigasterisk.com/device/")
44 ROOM = Namespace("http://projects.bigasterisk.com/room/") 35 ROOM = Namespace("http://projects.bigasterisk.com/room/")
45 reasoning = "http://bang:9071/" 36 reasoning = "http://bang:9071/"
46 37
48 def get(self): 39 def get(self):
49 40
50 age = time.time() - self.settings.poller.lastPollTime 41 age = time.time() - self.settings.poller.lastPollTime
51 if age > 10: 42 if age > 10:
52 raise ValueError("poll data is stale. age=%s" % age) 43 raise ValueError("poll data is stale. age=%s" % age)
53 44
54 self.write("this is wifiusage. needs index page that embeds the table") 45 self.set_header("Content-Type", "text/html")
55 46 self.write(open("index.html").read())
47
48 def whenConnected(mongo, macThatIsNowConnected):
49 lastArrive = None
50 for ev in mongo.find({'address': macThatIsNowConnected.upper()},
51 sort=[('created', -1)],
52 max_scan=100000):
53 if ev['action'] == 'arrive':
54 lastArrive = ev
55 if ev['action'] == 'leave':
56 break
57 if lastArrive is None:
58 raise ValueError("no past arrivals")
59
60 return lastArrive['created']
61
62 def connectedAgoString(conn):
63 return web.utils.datestr(
64 conn.astimezone(tz.tzutc()).replace(tzinfo=None))
65
56 class Table(PrettyErrorHandler, cyclone.web.RequestHandler): 66 class Table(PrettyErrorHandler, cyclone.web.RequestHandler):
57 def get(self): 67 def get(self):
58 def rowDict(row): 68 def rowDict(row):
59 row['cls'] = "signal" if row.get('connected') else "nosignal" 69 row['cls'] = "signal" if row.get('connected') else "nosignal"
60 if 'name' not in row: 70 if 'name' not in row:
61 row['name'] = row.get('clientHostname', '-') 71 row['name'] = row.get('clientHostname', '-')
62 if 'signal' not in row: 72 if 'signal' not in row:
63 row['signal'] = 'yes' if row['connected'] else 'no' 73 row['signal'] = 'yes' if row.get('connected') else 'no'
64 74
65 try: 75 try:
66 conn = self.whenConnected(row['mac']) 76 conn = whenConnected(self.settings.mongo, row.get('mac', '??'))
67 row['connectedAgo'] = web.utils.datestr( 77 row['connectedAgo'] = connectedAgoString(conn)
68 conn.astimezone(tz.tzutc()).replace(tzinfo=None))
69 except ValueError: 78 except ValueError:
70 pass 79 row['connectedAgo'] = 'yes' if row.get('connected') else ''
71 80 row['router'] = row.get('ssid', '')
72 return row 81 return row
73 82
74 self.set_header("Content-Type", "application/xhtml+xml") 83 self.set_header("Content-Type", "application/xhtml+xml")
75 self.write(pystache.render( 84 self.write(pystache.render(
76 open("table.mustache").read(), 85 open("table.mustache").read(),
77 dict( 86 dict(
78 rows=sorted(map(rowDict, self.settings.poller.lastAddrs), 87 rows=sorted(map(rowDict, self.settings.poller.lastAddrs),
79 key=lambda a: (not a.get('connected'), 88 key=lambda a: (not a.get('connected'),
80 a.get('name')))))) 89 a.get('name'))))))
81 90
82 def whenConnected(self, macThatIsNowConnected):
83 lastArrive = None
84 for ev in self.settings.mongo.find({'address': macThatIsNowConnected},
85 sort=[('created', -1)],
86 max_scan=100000):
87 if ev['action'] == 'arrive':
88 lastArrive = ev
89 if ev['action'] == 'leave':
90 break
91 if lastArrive is None:
92 raise ValueError("no past arrivals")
93
94 return lastArrive['created']
95
96 91
97 class Json(PrettyErrorHandler, cyclone.web.RequestHandler): 92 class Json(PrettyErrorHandler, cyclone.web.RequestHandler):
98 def get(self): 93 def get(self):
99 self.set_header("Content-Type", "application/json") 94 self.set_header("Content-Type", "application/json")
100 age = time.time() - self.settings.poller.lastPollTime 95 age = time.time() - self.settings.poller.lastPollTime
126 g.add((uri, ROOM['wifiNetworkName'], Literal(dev['clientHostname']))) 121 g.add((uri, ROOM['wifiNetworkName'], Literal(dev['clientHostname'])))
127 if 'name' in dev: 122 if 'name' in dev:
128 g.add((uri, ROOM['deviceName'], Literal(dev['name']))) 123 g.add((uri, ROOM['deviceName'], Literal(dev['name'])))
129 if 'signal' in dev: 124 if 'signal' in dev:
130 g.add((uri, ROOM['signalStrength'], Literal(dev['signal']))) 125 g.add((uri, ROOM['signalStrength'], Literal(dev['signal'])))
126 try:
127 conn = whenConnected(self.settings.mongo, dev['mac'])
128 except ValueError:
129 pass
130 else:
131 g.add((uri, ROOM['connectedAgo'],
132 Literal(connectedAgoString(conn))))
133 g.add((uri, ROOM['connected'], Literal(conn)))
131 134
132 self.set_header('Content-type', 'application/x-trig') 135 self.set_header('Content-type', 'application/x-trig')
133 self.write(g.asTrig()) 136 self.write(g.asTrig())
134 137
135 class Poller(object): 138 class Poller(object):
144 dt = time.time() - self.lastPollTime 147 dt = time.time() - self.lastPollTime
145 assert dt < 10, "last poll was %s sec ago" % dt 148 assert dt < 10, "last poll was %s sec ago" % dt
146 149
147 @inlineCallbacks 150 @inlineCallbacks
148 def poll(self): 151 def poll(self):
152
153 connectedField = 'connected'
154
155 # UVA mode:
156 addDhcpData = lambda *args: None
157
149 try: 158 try:
150 newAddrs = yield self.wifi.getPresentMacAddrs() 159 newAddrs = yield self.wifi.getPresentMacAddrs()
151 addDhcpData(newAddrs) 160 addDhcpData(newAddrs)
152 161
153 newWithSignal = [a for a in newAddrs if a.get('connected')] 162 newWithSignal = [a for a in newAddrs if a.get('connected')]
168 timeout=2, 177 timeout=2,
169 headers={'user-agent': ['tomatoWifi']}).addErrback(log.warn) 178 headers={'user-agent': ['tomatoWifi']}).addErrback(log.warn)
170 self.lastAddrs = newAddrs 179 self.lastAddrs = newAddrs
171 self.lastPollTime = time.time() 180 self.lastPollTime = time.time()
172 except Exception, e: 181 except Exception, e:
173 log.error("poll error: %s\n%s", e, traceback.format_exc()) 182 log.error("poll error: %r\n%s", e, traceback.format_exc())
174 183
175 def computeActions(self, newWithSignal): 184 def computeActions(self, newWithSignal):
176 actions = [] 185 actions = []
177 186
178 def makeAction(addr, act): 187 def makeAction(addr, act):
179 d = dict(sensor="wifi", 188 d = dict(sensor="wifi",
180 address=addr.get('mac'), 189 address=addr.get('mac').upper(), # mongo data is legacy uppercase
181 name=addr.get('name'), 190 name=addr.get('name'),
182 networkName=addr.get('clientHostname'), 191 networkName=addr.get('clientHostname'),
183 action=act) 192 action=act)
184 if act == 'arrive' and 'ip' in addr: 193 if act == 'arrive' and 'ip' in addr:
185 # this won't cover the possible case that you get on 194 # this won't cover the possible case that you get on
186 # wifi but don't have an ip yet. We'll record an 195 # wifi but don't have an ip yet. We'll record an
187 # action with no ip and then never record your ip. 196 # action with no ip and then never record your ip.
188 d['ip'] = addr['ip'] 197 d['ip'] = addr['ip']
229 config = { 238 config = {
230 'servePort' : 9070, 239 'servePort' : 9070,
231 'pollFrequency' : 1/5, 240 'pollFrequency' : 1/5,
232 } 241 }
233 from twisted.python import log as twlog 242 from twisted.python import log as twlog
234 #log.startLogging(sys.stdout) 243 #twlog.startLogging(sys.stdout)
235 #log.setLevel(10) 244 #log.setLevel(10)
236 #log.setLevel(logging.DEBUG) 245 #log.setLevel(logging.DEBUG)
237 246
238 mongo = Connection('bang', 27017, tz_aware=True)['visitor']['visitor'] 247 mongo = Connection('bang', 27017, tz_aware=True)['visitor']['visitor']
239 248