# HG changeset patch # User drewp # Date 1313479382 25200 # Node ID 7b0d282b292d4d567f949bfa59b77f608c48a18d # Parent 84f336f6900466b7e0dc1100529a03ddfc87ac8d prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT Ignore-this: ad7b99b1259dc1501179a70faca5454c darcs-hash:20110816072302-312f9-6af13fa96f1d70c2065bf5fec84da7574fee88a5.gz diff -r 84f336f69004 -r 7b0d282b292d service/bluetooth/bluetoothService.py --- a/service/bluetooth/bluetoothService.py Tue Aug 16 00:22:41 2011 -0700 +++ b/service/bluetooth/bluetoothService.py Tue Aug 16 00:23:02 2011 -0700 @@ -15,7 +15,9 @@ """ from __future__ import absolute_import -import logging, time, datetime, restkit, jsonlib, cyclone.web, sys +import logging, time, datetime, restkit, jsonlib, sys, socket +import cyclone.web, pystache +from dateutil.tz import tzutc, tzlocal from bluetooth import discover_devices, lookup_name from twisted.internet import reactor, task from twisted.internet.threads import deferToThread @@ -28,18 +30,35 @@ from cycloneerr import PrettyErrorHandler from logsetup import log -mongo = Connection('bang', 27017)['visitor']['visitor'] +mongo = Connection('bang', 27017, tz_aware=True)['visitor']['visitor'] ROOM = Namespace("http://projects.bigasterisk.com/room/") +# the mongodb serves as a much bigger cache, but I am expecting that +# 1) i won't fill memory with too many names; 2) this process will see +# each new device before it leaves, so I'll have the leaving name in +# my cache +nameCache = {} # addr : name + +def lookupPastName(addr): + row = mongo.find_one({"address" : addr, + 'name' : {'$exists' : True}}, + sort=[("created",-1)]) + if row is None: + return None + return row['name'] + def getNearbyDevices(): addrs = discover_devices() - # this can be done during discover_devices, but my plan was to - # cache it more in here - names = dict((a, lookup_name(a)) for a in addrs) - log.debug("discover found %r %r", addrs, names) - return addrs, names + for a in addrs: + if a not in nameCache: + n = lookup_name(a) or lookupPastName(a) + if n is not None: + nameCache[a] = n + + log.debug("discover found %r", addrs) + return addrs hub = restkit.Resource( # PSHB not working yet; "http://bang:9030/" @@ -52,7 +71,7 @@ except UnicodeDecodeError: pass else: - if msg['name'] != 'THINKPAD_T43': + if msg.get('name', '') and msg['name'] not in ['THINKPAD_T43']: hub.post("visitorNet", payload=js) # sans datetime msg['created'] = datetime.datetime.now(tz.gettz('UTC')) mongo.insert(msg, safe=True) @@ -74,29 +93,29 @@ devs.addErrback(log.error) return devs - def compare(self, (addrs, names)): + def compare(self, addrs): self.lastPollTime = time.time() newGraph = Graph() addrs = set(addrs) for addr in addrs.difference(self.lastAddrs): - self.recordAction('arrive', addr, names) + self.recordAction('arrive', addr) for addr in self.lastAddrs.difference(addrs): - self.recordAction('leave', addr, names) + self.recordAction('leave', addr) for addr in addrs: uri = deviceUri(addr) newGraph.add((ROOM['bluetooth'], ROOM['senses'], uri)) - if addr in names: - newGraph.add((uri, RDFS.label, Literal(names[addr]))) + if addr in nameCache: + newGraph.add((uri, RDFS.label, Literal(nameCache[addr]))) self.lastAddrs = addrs self.currentGraph = newGraph - def recordAction(self, action, addr, names): + def recordAction(self, action, addr): doc = {"sensor" : "bluetooth", "address" : addr, "action" : action} - if addr in names: - doc["name"] = names[addr] + if addr in nameCache: + doc["name"] = nameCache[addr] log.info("action: %s", doc) mongoInsert(doc) @@ -105,8 +124,37 @@ age = time.time() - self.settings.poller.lastPollTime if age > self.settings.config['period'] + 30: raise ValueError("poll data is stale. age=%s" % age) - - self.write("bluetooth watcher. ") + + self.set_header("Content-Type", "application/xhtml+xml") + self.write(pystache.render( + open("index.xhtml").read(), + dict(host=socket.gethostname(), + ))) + +class Recent(PrettyErrorHandler, cyclone.web.RequestHandler): + def get(self): + name = {} # addr : name + events = [] + hours = float(self.get_argument("hours", default="3")) + t1 = datetime.datetime.now(tzutc()) - datetime.timedelta(seconds=60*60*hours) + for row in mongo.find({"sensor":"bluetooth", + "created":{"$gt":t1}}, sort=[("created", 1)]): + if 'name' in row: + name[row['address']] = row['name'] + row['t'] = int(row['created'].astimezone(tzlocal()).strftime("%s")) + del row['created'] + del row['_id'] + events.append(row) + + for r in events: + r['name'] = name.get(r['address'], r['address']) + self.set_header("Content-Type", "application/json") + self.write(jsonlib.dumps({"events" : events})) + + +class Static(PrettyErrorHandler, cyclone.web.RequestHandler): + def get(self, fn): + self.write(open(fn).read()) if __name__ == '__main__': config = { @@ -116,6 +164,8 @@ poller = Poller() reactor.listenTCP(9077, cyclone.web.Application([ (r'/', Index), + (r'/recent', Recent), + (r'/(underscore-min.js|pretty.js)', Static), # graph, json, table, ... ], poller=poller, config=config)) task.LoopingCall(poller.poll).start(config['period']) diff -r 84f336f69004 -r 7b0d282b292d service/bluetooth/index.xhtml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/bluetooth/index.xhtml Tue Aug 16 00:23:02 2011 -0700 @@ -0,0 +1,157 @@ + + + + + + + + + +

bluetooth watcher on {{host}}

+ +

Recent activity

+ +
+ + + + + \ No newline at end of file