annotate service/bluetooth/bluetoothService.py @ 2:3a119010b960

moved from proj/room Ignore-this: bb65f6f4b41c2687c00eef9cdb8ff730
author drewp@bigasterisk.com
date Sun, 07 Aug 2011 20:15:10 -0700
parents
children be855a111619
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
2
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
1 #!/usr/bin/python
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
2
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
3 """
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
4 watch for bluetooth devices
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
5
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
6 this discoverer finds me if my treo has its screen on only, so I
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
7 have to wake up my own treo for a few seconds.
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
8
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
9 I can use 'hcitool cc <addr> && hcitool rssi <addr>' to wake it up and
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
10 get its signal strength, but that pattern crashes my treo easily. I
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
11 still don't have an access that wakes up the treo and then doesn't
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
12 crash it. Maybe I could pretend to be a headset or something.
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
13
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
14 depends on ubuntu package: python-bluez
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
15
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
16 """
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
17 from __future__ import absolute_import
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
18 import logging, time, datetime, restkit, jsonlib, cyclone.web, sys
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
19 from bluetooth import DeviceDiscoverer
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
20 from twisted.internet import reactor, defer, task
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
21 from rdflib.Graph import Graph
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
22 from rdflib import Literal, Variable, Namespace
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
23 from pymongo import Connection
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
24 from dateutil import tz
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
25
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
26 sys.path.append("/my/proj/homeauto/lib")
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
27 from cycloneerr import PrettyErrorHandler
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
28 from logsetup import log
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
29
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
30 mongo = Connection('bang', 27017)['visitor']['visitor']
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
31
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
32 ROOM = Namespace("http://projects.bigasterisk.com/room/")
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
33
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
34 class Disco(DeviceDiscoverer):
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
35 # it might be cool if this somehow returned
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
36 # _bt.EVT_INQUIRY_RESULT_WITH_RSSI: results. see
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
37 # /usr/share/pycentral/python-bluez/site-packages/bluetooth.py
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
38 def device_discovered(self, address, device_class, name):
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
39 log.debug("seeing: %s - %s (class 0x%X)" % (address, name, device_class))
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
40 self.nearby.append((address, name))
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
41
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
42 def inquiry_complete(self):
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
43 pass
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
44
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
45 def process_inquiry(self):
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
46 # more async version of the normal method
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
47 """
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
48 Starts calling process_event, returning a deferred that fires
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
49 when we're done.
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
50 """
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
51 self.done_inquiry = defer.Deferred()
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
52
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
53 if self.is_inquiring or len(self.names_to_find) > 0:
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
54 self.keep_processing()
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
55 else:
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
56 self.done_inquiry.callback(None)
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
57
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
58 return self.done_inquiry
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
59
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
60 def keep_processing(self):
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
61 # this one still blocks "a little bit"
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
62 if self.is_inquiring or len(self.names_to_find) > 0:
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
63 reactor.callLater(0, self.keep_processing)
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
64 log.debug("process_event()")
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
65 self.process_event() # <-- blocks here
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
66 else:
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
67 self.done_inquiry.callback(None)
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
68
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
69 def nearbyDevices(self):
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
70 """deferred to list of (addr,name) pairs"""
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
71 self.nearby = []
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
72 self.find_devices()
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
73 d = self.process_inquiry()
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
74 d.addCallback(lambda result: self.nearby)
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
75 return d
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
76
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
77 def devicesFromAddress(address):
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
78 for row in graph.query(
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
79 "SELECT ?dev { ?dev rm:bluetoothAddress ?addr }",
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
80 initNs=dict(rm=ROOM),
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
81 initBindings={Variable("?addr") : Literal(address)}):
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
82 (dev,) = row
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
83 yield dev
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
84
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
85 graph = Graph()
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
86 graph.parse("phones.n3", format="n3")
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
87
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
88 d = Disco()
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
89 hub = restkit.Resource(
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
90 # PSHB not working yet; "http://bang:9030/"
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
91 "http://slash:9049/"
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
92 )
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
93
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
94 def mongoInsert(msg):
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
95 try:
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
96 js = jsonlib.dumps(msg)
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
97 except UnicodeDecodeError:
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
98 pass
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
99 else:
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
100 if msg['name'] != 'THINKPAD_T43':
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
101 hub.post("visitorNet", payload=js) # sans datetime
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
102 msg['created'] = datetime.datetime.now(tz.gettz('UTC'))
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
103 mongo.insert(msg, safe=True)
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
104
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
105 class Poller(object):
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
106 def __init__(self):
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
107 self.lastDevs = set() # addresses
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
108 self.lastNameForAddress = {}
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
109 self.currentGraph = Graph()
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
110 self.lastPollTime = 0
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
111
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
112 def poll(self):
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
113 log.debug("get devices")
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
114 devs = d.nearbyDevices()
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
115
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
116 devs.addCallback(self.compare)
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
117 devs.addErrback(log.error)
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
118 return devs
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
119
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
120 def compare(self, newDevs):
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
121 self.lastPollTime = time.time()
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
122 log.debug("got: %r", newDevs)
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
123 lostDevs = self.lastDevs.copy()
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
124 prevDevs = self.lastDevs.copy()
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
125 self.lastDevs.clear()
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
126 stmts = []
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
127
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
128 for address, name in newDevs:
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
129 stmts.append((ROOM['bluetooth'],
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
130 ROOM['senses'],
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
131 Literal(str(address))))
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
132 if address not in prevDevs:
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
133 matches = 0
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
134 for dev in devicesFromAddress(address):
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
135 log.info("found %s" % dev)
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
136 matches += 1
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
137 if not matches:
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
138 log.info("no matches for %s (%s)" % (name, address))
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
139
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
140 print "%s %s %s" % (time.time(), name, address)
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
141
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
142 self.lastNameForAddress[address] = name
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
143 print 'mongoInsert', ({"sensor" : "bluetooth",
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
144 "address" : address,
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
145 "name" : name,
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
146 "action" : "arrive"})
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
147
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
148 lostDevs.discard(address)
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
149 self.lastDevs.add(address)
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
150
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
151 for address in lostDevs:
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
152 print 'mongoInsert', ({"sensor" : "bluetooth",
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
153 "address" : address,
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
154 "name" : self.lastNameForAddress[address],
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
155 "action" : "leave"})
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
156
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
157 for dev in devicesFromAddress(address):
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
158 log.info("lost %s" % dev)
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
159
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
160 class Index(PrettyErrorHandler, cyclone.web.RequestHandler):
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
161 def get(self):
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
162 age = time.time() - self.settings.poller.lastPollTime
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
163 if age > 60 + 30:
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
164 raise ValueError("poll data is stale. age=%s" % age)
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
165
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
166 self.write("bluetooth watcher. ")
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
167
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
168 if __name__ == '__main__':
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
169 log.setLevel(logging.DEBUG)
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
170 poller = Poller()
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
171 reactor.listenTCP(9077, cyclone.web.Application([
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
172 (r'/', Index),
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
173 ], poller=poller))
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
174 task.LoopingCall(poller.poll).start(1)
3a119010b960 moved from proj/room
drewp@bigasterisk.com
parents:
diff changeset
175 reactor.run()