diff service/bluetooth/bluetoothService.py @ 815:7b0d282b292d

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
author drewp <drewp@bigasterisk.com>
date Tue, 16 Aug 2011 00:23:02 -0700
parents bebb8f7c5a3e
children 3a429f6cc9dc
line wrap: on
line diff
--- 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'])