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