Mercurial > code > home > repos > homeauto
annotate service/bluetooth/bluetoothService.py @ 432:f134b64a0ab7
py3, rfid-console rename
Ignore-this: 1b28d912e8847685a87c0c9ccb703608
author | drewp@bigasterisk.com |
---|---|
date | Sun, 07 Apr 2019 03:58:51 -0700 |
parents | 3a429f6cc9dc |
children |
rev | line source |
---|---|
2 | 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 | |
10
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
18 import logging, time, datetime, restkit, jsonlib, sys, socket |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
19 import cyclone.web, pystache |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
20 from dateutil.tz import tzutc, tzlocal |
4
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
21 from bluetooth import discover_devices, lookup_name |
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
22 from twisted.internet import reactor, task |
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
23 from twisted.internet.threads import deferToThread |
2 | 24 from rdflib.Graph import Graph |
4
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
25 from rdflib import Literal, Namespace, RDFS, URIRef |
2 | 26 from pymongo import Connection |
27 from dateutil import tz | |
28 | |
29 sys.path.append("/my/proj/homeauto/lib") | |
30 from cycloneerr import PrettyErrorHandler | |
31 from logsetup import log | |
32 | |
10
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
33 mongo = Connection('bang', 27017, tz_aware=True)['visitor']['visitor'] |
2 | 34 |
35 ROOM = Namespace("http://projects.bigasterisk.com/room/") | |
36 | |
10
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
37 # the mongodb serves as a much bigger cache, but I am expecting that |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
38 # 1) i won't fill memory with too many names; 2) this process will see |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
39 # each new device before it leaves, so I'll have the leaving name in |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
40 # my cache |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
41 nameCache = {} # addr : name |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
42 |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
43 def lookupPastName(addr): |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
44 row = mongo.find_one({"address" : addr, |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
45 'name' : {'$exists' : True}}, |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
46 sort=[("created",-1)]) |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
47 if row is None: |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
48 return None |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
49 return row['name'] |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
50 |
4
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
51 def getNearbyDevices(): |
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
52 addrs = discover_devices() |
2 | 53 |
10
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
54 for a in addrs: |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
55 if a not in nameCache: |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
56 n = lookup_name(a) or lookupPastName(a) |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
57 if n is not None: |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
58 nameCache[a] = n |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
59 |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
60 log.debug("discover found %r", addrs) |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
61 return addrs |
2 | 62 |
63 hub = restkit.Resource( | |
64 # PSHB not working yet; "http://bang:9030/" | |
65 "http://slash:9049/" | |
66 ) | |
67 | |
68 def mongoInsert(msg): | |
69 try: | |
70 js = jsonlib.dumps(msg) | |
71 except UnicodeDecodeError: | |
72 pass | |
73 else: | |
16 | 74 if (msg.get('name', '') and |
75 msg['name'] not in ['THINKPAD_T43'] and | |
76 msg['action'] == 'arrive'): | |
2 | 77 hub.post("visitorNet", payload=js) # sans datetime |
78 msg['created'] = datetime.datetime.now(tz.gettz('UTC')) | |
79 mongo.insert(msg, safe=True) | |
80 | |
4
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
81 def deviceUri(addr): |
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
82 return URIRef("http://bigasterisk.com/bluetooth/%s" % addr) |
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
83 |
2 | 84 class Poller(object): |
85 def __init__(self): | |
4
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
86 self.lastAddrs = set() # addresses |
2 | 87 self.currentGraph = Graph() |
88 self.lastPollTime = 0 | |
89 | |
90 def poll(self): | |
91 log.debug("get devices") | |
4
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
92 devs = deferToThread(getNearbyDevices) |
2 | 93 |
94 devs.addCallback(self.compare) | |
95 devs.addErrback(log.error) | |
96 return devs | |
97 | |
10
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
98 def compare(self, addrs): |
2 | 99 self.lastPollTime = time.time() |
100 | |
4
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
101 newGraph = Graph() |
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
102 addrs = set(addrs) |
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
103 for addr in addrs.difference(self.lastAddrs): |
10
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
104 self.recordAction('arrive', addr) |
4
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
105 for addr in self.lastAddrs.difference(addrs): |
10
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
106 self.recordAction('leave', addr) |
4
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
107 for addr in addrs: |
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
108 uri = deviceUri(addr) |
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
109 newGraph.add((ROOM['bluetooth'], ROOM['senses'], uri)) |
10
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
110 if addr in nameCache: |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
111 newGraph.add((uri, RDFS.label, Literal(nameCache[addr]))) |
4
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
112 self.lastAddrs = addrs |
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
113 self.currentGraph = newGraph |
2 | 114 |
10
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
115 def recordAction(self, action, addr): |
4
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
116 doc = {"sensor" : "bluetooth", |
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
117 "address" : addr, |
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
118 "action" : action} |
10
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
119 if addr in nameCache: |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
120 doc["name"] = nameCache[addr] |
4
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
121 log.info("action: %s", doc) |
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
122 mongoInsert(doc) |
2 | 123 |
124 class Index(PrettyErrorHandler, cyclone.web.RequestHandler): | |
125 def get(self): | |
126 age = time.time() - self.settings.poller.lastPollTime | |
4
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
127 if age > self.settings.config['period'] + 30: |
2 | 128 raise ValueError("poll data is stale. age=%s" % age) |
10
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
129 |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
130 self.set_header("Content-Type", "application/xhtml+xml") |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
131 self.write(pystache.render( |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
132 open("index.xhtml").read(), |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
133 dict(host=socket.gethostname(), |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
134 ))) |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
135 |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
136 class Recent(PrettyErrorHandler, cyclone.web.RequestHandler): |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
137 def get(self): |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
138 name = {} # addr : name |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
139 events = [] |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
140 hours = float(self.get_argument("hours", default="3")) |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
141 t1 = datetime.datetime.now(tzutc()) - datetime.timedelta(seconds=60*60*hours) |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
142 for row in mongo.find({"sensor":"bluetooth", |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
143 "created":{"$gt":t1}}, sort=[("created", 1)]): |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
144 if 'name' in row: |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
145 name[row['address']] = row['name'] |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
146 row['t'] = int(row['created'].astimezone(tzlocal()).strftime("%s")) |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
147 del row['created'] |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
148 del row['_id'] |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
149 events.append(row) |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
150 |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
151 for r in events: |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
152 r['name'] = name.get(r['address'], r['address']) |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
153 self.set_header("Content-Type", "application/json") |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
154 self.write(jsonlib.dumps({"events" : events})) |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
155 |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
156 |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
157 class Static(PrettyErrorHandler, cyclone.web.RequestHandler): |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
158 def get(self, fn): |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
159 self.write(open(fn).read()) |
2 | 160 |
161 if __name__ == '__main__': | |
4
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
162 config = { |
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
163 "period" : 60, |
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
164 } |
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
165 log.setLevel(logging.INFO) |
2 | 166 poller = Poller() |
167 reactor.listenTCP(9077, cyclone.web.Application([ | |
168 (r'/', Index), | |
10
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
169 (r'/recent', Recent), |
f299b71f88f7
prettier viewer of recent BT activity. don't announce BT with no names. don't announce leaving BT
drewp@bigasterisk.com
parents:
4
diff
changeset
|
170 (r'/(underscore-min.js|pretty.js)', Static), |
4
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
171 # graph, json, table, ... |
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
172 ], poller=poller, config=config)) |
be855a111619
move a bunch of services into this tree, give them all web status pages
drewp@bigasterisk.com
parents:
2
diff
changeset
|
173 task.LoopingCall(poller.poll).start(config['period']) |
2 | 174 reactor.run() |