Mercurial > code > home > repos > homeauto
changeset 1290:4fa09ec19c86
rm old bluetooth tracking service. 'beacon' is the new one.
Ignore-this: d4c47f2b8c0b44766725bc2365e207f0
darcs-hash:5744332ed7cfcd62adb2d4d6bb78bb255bf6f0e3
author | drewp <drewp@bigasterisk.com> |
---|---|
date | Sun, 21 Apr 2019 01:24:50 -0700 |
parents | add5df9cd476 |
children | c1b0813372cc |
files | service/bluetooth/bluetoothService.py service/bluetooth/index.xhtml service/bluetooth/phones.n3 service/bluetooth/pretty.js |
diffstat | 4 files changed, 0 insertions(+), 380 deletions(-) [+] |
line wrap: on
line diff
--- a/service/bluetooth/bluetoothService.py Sun Apr 21 01:24:19 2019 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,174 +0,0 @@ -#!/usr/bin/python - -""" -watch for bluetooth devices - -this discoverer finds me if my treo has its screen on only, so I -have to wake up my own treo for a few seconds. - -I can use 'hcitool cc <addr> && hcitool rssi <addr>' to wake it up and -get its signal strength, but that pattern crashes my treo easily. I -still don't have an access that wakes up the treo and then doesn't -crash it. Maybe I could pretend to be a headset or something. - -depends on ubuntu package: python-bluez - -""" -from __future__ import absolute_import -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 -from rdflib.Graph import Graph -from rdflib import Literal, Namespace, RDFS, URIRef -from pymongo import Connection -from dateutil import tz - -sys.path.append("/my/proj/homeauto/lib") -from cycloneerr import PrettyErrorHandler -from logsetup import log - -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() - - 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/" - "http://slash:9049/" - ) - -def mongoInsert(msg): - try: - js = jsonlib.dumps(msg) - except UnicodeDecodeError: - pass - else: - if (msg.get('name', '') and - msg['name'] not in ['THINKPAD_T43'] and - msg['action'] == 'arrive'): - hub.post("visitorNet", payload=js) # sans datetime - 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.lastAddrs = set() # addresses - self.currentGraph = Graph() - self.lastPollTime = 0 - - def poll(self): - log.debug("get devices") - devs = deferToThread(getNearbyDevices) - - devs.addCallback(self.compare) - devs.addErrback(log.error) - return devs - - def compare(self, addrs): - self.lastPollTime = time.time() - - newGraph = Graph() - addrs = set(addrs) - for addr in addrs.difference(self.lastAddrs): - self.recordAction('arrive', addr) - for addr in self.lastAddrs.difference(addrs): - self.recordAction('leave', addr) - for addr in addrs: - uri = deviceUri(addr) - newGraph.add((ROOM['bluetooth'], ROOM['senses'], uri)) - if addr in nameCache: - newGraph.add((uri, RDFS.label, Literal(nameCache[addr]))) - self.lastAddrs = addrs - self.currentGraph = newGraph - - def recordAction(self, action, addr): - doc = {"sensor" : "bluetooth", - "address" : addr, - "action" : action} - if addr in nameCache: - doc["name"] = nameCache[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 > self.settings.config['period'] + 30: - raise ValueError("poll data is stale. age=%s" % age) - - 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 = { - "period" : 60, - } - log.setLevel(logging.INFO) - 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']) - reactor.run()
--- a/service/bluetooth/index.xhtml Sun Apr 21 01:24:19 2019 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,157 +0,0 @@ -<?xml version="1.0" encoding="iso-8859-1"?> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" -"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml"> - <head> - <title></title> - <style type="text/css" media="all"> - /* <![CDATA[ */ - body { - font-family: sans-serif; - font-size: 12px; - } - .stripChart { - background: none repeat scroll 0 0 #EAEAEA; - border-bottom: 1px solid #b7b7b7; - display: inline-block; - height: 22px; - margin: 0; - padding: 0; - white-space: nowrap; - } - .chartArea { - width: 300px; - height: 20px; - border: 1px solid gray; - display: inline-block; - position: relative; - background: white; - } - .name { - display: inline-block; - width: 16em; - } - - .chartArea > span { - position: absolute; - height: 20px; - background: #d3e1ed; - background: -moz-linear-gradient(left, #d3e1ed 0%, #86aecc 100%); - background: -webkit-gradient(linear, left top, right top, color-stop(0%,#d3e1ed), color-stop(100%,#86aecc)); - background: -webkit-linear-gradient(left, #d3e1ed 0%,#86aecc 100%); - background: -o-linear-gradient(left, #d3e1ed 0%,#86aecc 100%); - background: linear-gradient(left, #d3e1ed 0%,#86aecc 100%); - } - - .stripChart > .timeLeft, .stripChart > .timeRight { - font-size: 10px; - vertical-align: super; - padding: 0 4px; - } - - /* ]]> */ - </style> - - </head> - <body> - <h1>bluetooth watcher on {{host}}</h1> - - <p>Recent activity</p> - - <div id="activity"/> - <script type="text/javascript" src="//bigasterisk.com/lib/jquery-2.0.3.min.js"/> - <script type="text/javascript" src="//bigasterisk.com/lib/underscore-1.5.2.min.js"/> - <script type="text/javascript" src="pretty.js"/> - - <script type="text/javascript"> - // <![CDATA[ - - function StripChart(parent) { - var strips = [[null, null]]; // [[starttime, endtime], ...] - this.addEvent = function (ev) { - // must pass events in order - if (ev.action == "arrive") { - if (_.isNull(_.last(strips)[1])) { - if (_.isNull(_.last(strips)[0])) { - _.last(strips)[0] = ev.t; - } else { - // two arrives in a row - } - } else { - strips.push([ev.t, null]); - } - } - if (ev.action == "leave") { - if (_.isNull(_.last(strips)[1])) { - _.last(strips)[1] = ev.t; - } else { - // two leaves in a row - } - } - }; - function stripIsComplete(se) { - return !_.isNull(se[0]) && !_.isNull(se[1]); - } - function displayTime(secs) { - var iso = new Date(secs*1000).toJSON().replace(/\.\d+/,""); - return prettyDate(iso); - } - this.draw = function () { - var now = .001*new Date(); - if (_.isNull(_.last(strips)[1])) { - _.last(strips)[1] = now; - } - var complete = _.select(strips, stripIsComplete); - if (_.isEmpty(complete)) { - return; - } - var t1 = _.first(complete)[0], t2 = now; - function xForTime(t) { - return 300 * (t - t1) / (t2 - t1) - } - parent.append($("<span>").addClass("timeLeft").text(displayTime(t1))); - var out = $("<span>").addClass("chartArea") - parent.append(out); - var lastX = 0; - $.each(complete, function (i, se) { - var x1 = xForTime(se[0]), x2 = xForTime(se[1]); - if (x1 < lastX) { - // culls ok, but may leave gaps. I'd rather - // something that joins the slivers intead of - // skipping them - return; - } - var w = Math.max(2, x2 - x1) - out.append($("<span>").css({left: x1, width: w})); - lastX = x1 + w; - }); - parent.append($("<span>").addClass("timeRight").text("now")); - }; - } - - $(function() { - var addressRow = {}; // address : row - var chart = {} // address : StripChart - - $.getJSON("recent", {hours:24*7}, function (data) { - - $.each(data.events, function (i, ev) { - if (!addressRow[ev.address]) { - var row = $("<li>").append($("<span>").addClass("name").text(ev.name)).append($("<span>").addClass("stripChart")); - $("#activity").append(row); - addressRow[ev.address] = row; - chart[ev.address] = new StripChart(row.find(".stripChart")); - } - chart[ev.address].addEvent(ev); - }); - $.each(chart, function (k, c) { - c.draw(); - }); - }); - }); - // ]]> -</script> - - - </body> -</html>
--- a/service/bluetooth/phones.n3 Sun Apr 21 01:24:19 2019 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -@prefix : <http://projects.bigasterisk.com/room/>. - -<http://bigasterisk.com/foaf.rdf#drewp> :carries :drewTreo . - -:drewTreo :bluetoothAddress "00:07:E0:5C:8A:DD" . - -:johnTreo :bluetoothAddress "00:07:E0:1E:F5:5C" . - -:kelsiTreo :bluetoothAddress "00:07:E0:BE:AD:B2" . - -# 00:1D:FE:37:89:D0 drew's pre - -#1199324300.71 2008-01-02 17:38:20,705 INFO io.bluetooth:105: no matches for Jawbone (00:17:84:11:E5:D1)
--- a/service/bluetooth/pretty.js Sun Apr 21 01:24:19 2019 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -/* - * JavaScript Pretty Date - * Copyright (c) 2008 John Resig (jquery.com) - * Licensed under the MIT license. - */ - -// Takes an ISO time and returns a string representing how -// long ago the date represents. -function prettyDate(time){ - var date = new Date((time || "").replace(/-/g,"/").replace(/[TZ]/g," ")), - diff = (((new Date()).getTime() - date.getTime()) / 1000), - day_diff = Math.floor(diff / 86400); - - if ( isNaN(day_diff) || day_diff < 0 || day_diff >= 31 ) - return; - - return day_diff == 0 && ( - diff < 60 && "just now" || - diff < 120 && "1 minute ago" || - diff < 3600 && Math.floor( diff / 60 ) + " minutes ago" || - diff < 7200 && "1 hour ago" || - diff < 86400 && Math.floor( diff / 3600 ) + " hours ago") || - day_diff == 1 && "Yesterday" || - day_diff < 7 && day_diff + " days ago" || - day_diff < 31 && Math.ceil( day_diff / 7 ) + " weeks ago"; -} - -// If jQuery is included in the page, adds a jQuery plugin to handle it as well -if ( typeof jQuery != "undefined" ) - jQuery.fn.prettyDate = function(){ - return this.each(function(){ - var date = prettyDate(this.title); - if ( date ) - jQuery(this).text( date ); - }); - };