Mercurial > code > home > repos > homeauto
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 |