Mercurial > code > home > repos > homeauto
diff service/bluetooth/bluetoothService.py @ 4:be855a111619
move a bunch of services into this tree, give them all web status pages
Ignore-this: a11e90f9d2cd9470565c743f54943c4b
author | drewp@bigasterisk.com |
---|---|
date | Mon, 08 Aug 2011 00:31:31 -0700 |
parents | 3a119010b960 |
children | f299b71f88f7 |
line wrap: on
line diff
--- a/service/bluetooth/bluetoothService.py Sun Aug 07 21:50:21 2011 -0700 +++ b/service/bluetooth/bluetoothService.py Mon Aug 08 00:31:31 2011 -0700 @@ -16,10 +16,11 @@ """ from __future__ import absolute_import import logging, time, datetime, restkit, jsonlib, cyclone.web, sys -from bluetooth import DeviceDiscoverer -from twisted.internet import reactor, defer, task +from bluetooth import discover_devices, lookup_name +from twisted.internet import reactor, task +from twisted.internet.threads import deferToThread from rdflib.Graph import Graph -from rdflib import Literal, Variable, Namespace +from rdflib import Literal, Namespace, RDFS, URIRef from pymongo import Connection from dateutil import tz @@ -31,61 +32,15 @@ ROOM = Namespace("http://projects.bigasterisk.com/room/") -class Disco(DeviceDiscoverer): - # it might be cool if this somehow returned - # _bt.EVT_INQUIRY_RESULT_WITH_RSSI: results. see - # /usr/share/pycentral/python-bluez/site-packages/bluetooth.py - def device_discovered(self, address, device_class, name): - log.debug("seeing: %s - %s (class 0x%X)" % (address, name, device_class)) - self.nearby.append((address, name)) - - def inquiry_complete(self): - pass - - def process_inquiry(self): - # more async version of the normal method - """ - Starts calling process_event, returning a deferred that fires - when we're done. - """ - self.done_inquiry = defer.Deferred() - - if self.is_inquiring or len(self.names_to_find) > 0: - self.keep_processing() - else: - self.done_inquiry.callback(None) - - return self.done_inquiry +def getNearbyDevices(): + addrs = discover_devices() - def keep_processing(self): - # this one still blocks "a little bit" - if self.is_inquiring or len(self.names_to_find) > 0: - reactor.callLater(0, self.keep_processing) - log.debug("process_event()") - self.process_event() # <-- blocks here - else: - self.done_inquiry.callback(None) + # 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 - def nearbyDevices(self): - """deferred to list of (addr,name) pairs""" - self.nearby = [] - self.find_devices() - d = self.process_inquiry() - d.addCallback(lambda result: self.nearby) - return d - -def devicesFromAddress(address): - for row in graph.query( - "SELECT ?dev { ?dev rm:bluetoothAddress ?addr }", - initNs=dict(rm=ROOM), - initBindings={Variable("?addr") : Literal(address)}): - (dev,) = row - yield dev - -graph = Graph() -graph.parse("phones.n3", format="n3") - -d = Disco() hub = restkit.Resource( # PSHB not working yet; "http://bang:9030/" "http://slash:9049/" @@ -102,74 +57,66 @@ msg['created'] = datetime.datetime.now(tz.gettz('UTC')) mongo.insert(msg, safe=True) +def deviceUri(addr): + return URIRef("http://bigasterisk.com/bluetooth/%s" % addr) + class Poller(object): def __init__(self): - self.lastDevs = set() # addresses - self.lastNameForAddress = {} + self.lastAddrs = set() # addresses self.currentGraph = Graph() self.lastPollTime = 0 def poll(self): log.debug("get devices") - devs = d.nearbyDevices() + devs = deferToThread(getNearbyDevices) devs.addCallback(self.compare) devs.addErrback(log.error) return devs - def compare(self, newDevs): + def compare(self, (addrs, names)): self.lastPollTime = time.time() - log.debug("got: %r", newDevs) - lostDevs = self.lastDevs.copy() - prevDevs = self.lastDevs.copy() - self.lastDevs.clear() - stmts = [] - - for address, name in newDevs: - stmts.append((ROOM['bluetooth'], - ROOM['senses'], - Literal(str(address)))) - if address not in prevDevs: - matches = 0 - for dev in devicesFromAddress(address): - log.info("found %s" % dev) - matches += 1 - if not matches: - log.info("no matches for %s (%s)" % (name, address)) - print "%s %s %s" % (time.time(), name, address) - - self.lastNameForAddress[address] = name - print 'mongoInsert', ({"sensor" : "bluetooth", - "address" : address, - "name" : name, - "action" : "arrive"}) + newGraph = Graph() + addrs = set(addrs) + for addr in addrs.difference(self.lastAddrs): + self.recordAction('arrive', addr, names) + for addr in self.lastAddrs.difference(addrs): + self.recordAction('leave', addr, names) + 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]))) + self.lastAddrs = addrs + self.currentGraph = newGraph - lostDevs.discard(address) - self.lastDevs.add(address) - - for address in lostDevs: - print 'mongoInsert', ({"sensor" : "bluetooth", - "address" : address, - "name" : self.lastNameForAddress[address], - "action" : "leave"}) - - for dev in devicesFromAddress(address): - log.info("lost %s" % dev) + def recordAction(self, action, addr, names): + doc = {"sensor" : "bluetooth", + "address" : addr, + "action" : action} + if addr in names: + doc["name"] = names[addr] + log.info("action: %s", doc) + mongoInsert(doc) class Index(PrettyErrorHandler, cyclone.web.RequestHandler): def get(self): age = time.time() - self.settings.poller.lastPollTime - if age > 60 + 30: + if age > self.settings.config['period'] + 30: raise ValueError("poll data is stale. age=%s" % age) self.write("bluetooth watcher. ") if __name__ == '__main__': - log.setLevel(logging.DEBUG) + config = { + "period" : 60, + } + log.setLevel(logging.INFO) poller = Poller() reactor.listenTCP(9077, cyclone.web.Application([ (r'/', Index), - ], poller=poller)) - task.LoopingCall(poller.poll).start(1) + # graph, json, table, ... + ], poller=poller, config=config)) + task.LoopingCall(poller.poll).start(config['period']) reactor.run()