├── .gitignore ├── hack.py ├── inquiry-with-rssi.py ├── proximity_dagar.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # I symlinked the libpebble library for convenience, but it doesn't need to be in git 2 | pebble 3 | 4 | -------------------------------------------------------------------------------- /hack.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import select 6 | import subprocess 7 | from subprocess import CalledProcessError 8 | from pebble import pebble 9 | from evdev import UInput, ecodes as e 10 | from time import sleep 11 | import dbus 12 | from dbus.mainloop.glib import DBusGMainLoop 13 | 14 | media_manager = None 15 | peb = None 16 | 17 | pebbles = ['00:17:e9:4a:64:91'] 18 | 19 | def media_endpoint(endpoint, resp): 20 | global media_manager 21 | 22 | if media_manager == None: 23 | setup_media_connection() 24 | 25 | if resp == 'PLAYPAUSE': 26 | playpause() 27 | elif resp == 'NEXT': 28 | nextsong() 29 | elif resp == 'PREVIOUS': 30 | previoussong() 31 | else: 32 | print "unknown message: " + resp 33 | 34 | sleep(1) # Sleep for a bit, so media player has a chance to update before we query metadata again 35 | 36 | metadata = media_manager.Get('org.mpris.MediaPlayer2.Player', 'Metadata') 37 | 38 | title = "" 39 | if metadata.has_key('xesam:title'): 40 | title = str(metadata['xesam:title']) 41 | 42 | album = "" 43 | if metadata.has_key('xesam:album'): 44 | album = str(metadata['xesam:album']) 45 | 46 | artist = "" 47 | if metadata.has_key('xesam:artist'): 48 | artist = str(metadata['xesam:artist'][0]) # Artist is returned as an array 49 | 50 | peb.set_nowplaying_metadata(title, album, artist) 51 | 52 | def setup_media_connection(): 53 | global media_manager 54 | 55 | dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) 56 | 57 | # os.popen("nuvolaplayer") # Start music player 58 | 59 | bus = dbus.SessionBus() 60 | proxy = bus.get_object('org.mpris.MediaPlayer2.nuvolaplayer', '/org/mpris/MediaPlayer2') 61 | media_manager = dbus.Interface(proxy, 'org.freedesktop.DBus.Properties') 62 | 63 | def playpause(): os.system("xdotool key XF86AudioPlay") 64 | 65 | def nextsong(): os.system("xdotool key XF86AudioNext") 66 | 67 | def previoussong(): os.system("xdotool key XF86AudioPrev") 68 | 69 | def setup_pebble(addr): 70 | global peb 71 | 72 | peb = pebble.Pebble(id = addr) 73 | peb.set_print_pbl_logs(True) # Necessary to avoid a crash 74 | peb.connect_via_lightblue() 75 | peb.register_endpoint("MUSIC_CONTROL", media_endpoint) 76 | print "Connected to Pebble!" 77 | 78 | def main(): 79 | global peb, pebbles 80 | 81 | device_found = False; 82 | device = '' 83 | 84 | with open(os.devnull, "w") as fnull: 85 | while not device_found: 86 | for device in pebbles: 87 | try: 88 | subprocess.check_call(["sudo","/usr/bin/l2ping", "-c1", device], stdout=fnull) 89 | device_found = True 90 | print "Found Pebble " + device 91 | break; 92 | except CalledProcessError, e: 93 | sleep(1) 94 | 95 | setup_pebble(device) 96 | 97 | results = [] 98 | 99 | print "Enter 'q' to exit: " 100 | try: 101 | last_avg = -100 102 | avg = 0 103 | while peb._alive: 104 | # This will keep looping until the user presses enter 105 | # Ugly but it gets the job done 106 | if sys.stdin in select.select([sys.stdin], [], [], 0)[0]: 107 | line = raw_input() 108 | if line == 'q': 109 | peb.disconnect() 110 | break 111 | sleep(.25) 112 | try: 113 | rssi = subprocess.check_output(["hcitool", "rssi", "00:17:e9:4a:64:91"]) 114 | results.append(int(rssi[19:])) 115 | # print rssi[19:], 116 | except Exception, e: 117 | print e 118 | 119 | if len(results) > 10: 120 | results = results[-10:] 121 | 122 | if len(results) == 0: 123 | continue; 124 | elif len(results) < 10 : 125 | avg = sum(results) / float(len(results)) 126 | else: 127 | avg = sum(results[5:10]) / float(len(results[5:10])) 128 | 129 | if avg != last_avg: 130 | print "Distance: %.1f" % avg 131 | last_avg = avg 132 | 133 | if avg > -2: 134 | os.system("pkill i3lock") 135 | else: 136 | os.system("xautolock -locknow") 137 | 138 | peb.disconnect() 139 | except: 140 | peb.disconnect() 141 | 142 | if __name__ == '__main__': 143 | sys.exit(main()) 144 | 145 | -------------------------------------------------------------------------------- /inquiry-with-rssi.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python2.7 2 | # performs a simple device inquiry, followed by a remote name request of each 3 | # discovered device 4 | 5 | import os 6 | import sys 7 | import struct 8 | import bluetooth 9 | import bluetooth._bluetooth as bluez 10 | from time import sleep 11 | 12 | results = [] 13 | 14 | def printpacket(pkt): 15 | for c in pkt: 16 | sys.stdout.write("%02x " % struct.unpack("B",c)[0]) 17 | print 18 | 19 | 20 | def read_inquiry_mode(sock): 21 | """returns the current mode, or -1 on failure""" 22 | # save current filter 23 | old_filter = sock.getsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, 14) 24 | 25 | # Setup socket filter to receive only events related to the 26 | # read_inquiry_mode command 27 | flt = bluez.hci_filter_new() 28 | opcode = bluez.cmd_opcode_pack(bluez.OGF_HOST_CTL, 29 | bluez.OCF_READ_INQUIRY_MODE) 30 | bluez.hci_filter_set_ptype(flt, bluez.HCI_EVENT_PKT) 31 | bluez.hci_filter_set_event(flt, bluez.EVT_CMD_COMPLETE); 32 | bluez.hci_filter_set_opcode(flt, opcode) 33 | sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, flt ) 34 | 35 | # first read the current inquiry mode. 36 | bluez.hci_send_cmd(sock, bluez.OGF_HOST_CTL, 37 | bluez.OCF_READ_INQUIRY_MODE ) 38 | 39 | pkt = sock.recv(255) 40 | 41 | status,mode = struct.unpack("xxxxxxBB", pkt) 42 | if status != 0: mode = -1 43 | 44 | # restore old filter 45 | sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, old_filter ) 46 | return mode 47 | 48 | def write_inquiry_mode(sock, mode): 49 | """returns 0 on success, -1 on failure""" 50 | # save current filter 51 | old_filter = sock.getsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, 14) 52 | 53 | # Setup socket filter to receive only events related to the 54 | # write_inquiry_mode command 55 | flt = bluez.hci_filter_new() 56 | opcode = bluez.cmd_opcode_pack(bluez.OGF_HOST_CTL, 57 | bluez.OCF_WRITE_INQUIRY_MODE) 58 | bluez.hci_filter_set_ptype(flt, bluez.HCI_EVENT_PKT) 59 | bluez.hci_filter_set_event(flt, bluez.EVT_CMD_COMPLETE); 60 | bluez.hci_filter_set_opcode(flt, opcode) 61 | sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, flt ) 62 | 63 | # send the command! 64 | bluez.hci_send_cmd(sock, bluez.OGF_HOST_CTL, 65 | bluez.OCF_WRITE_INQUIRY_MODE, struct.pack("B", mode) ) 66 | 67 | pkt = sock.recv(255) 68 | 69 | status = struct.unpack("xxxxxxB", pkt)[0] 70 | 71 | # restore old filter 72 | sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, old_filter ) 73 | if status != 0: return -1 74 | return 0 75 | 76 | def device_inquiry_with_with_rssi(sock, wanted_addr): 77 | global results 78 | 79 | # save current filter 80 | old_filter = sock.getsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, 14) 81 | 82 | # perform a device inquiry on bluetooth device #0 83 | # The inquiry should last 8 * 1.28 = 10.24 seconds 84 | # before the inquiry is performed, bluez should flush its cache of 85 | # previously discovered devices 86 | flt = bluez.hci_filter_new() 87 | bluez.hci_filter_all_events(flt) 88 | bluez.hci_filter_set_ptype(flt, bluez.HCI_EVENT_PKT) 89 | sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, flt ) 90 | 91 | duration = 4 92 | max_responses = 255 93 | # 0x9e8b33 is the reserved code for general inquiry of Bluetooth devices 94 | cmd_pkt = struct.pack("BBBBB", 0x33, 0x8b, 0x9e, duration, max_responses) 95 | bluez.hci_send_cmd(sock, bluez.OGF_LINK_CTL, bluez.OCF_INQUIRY, cmd_pkt) 96 | 97 | done = False 98 | while not done: 99 | pkt = sock.recv(255) 100 | ptype, event, plen = struct.unpack("BBB", pkt[:3]) 101 | if event == bluez.EVT_INQUIRY_RESULT_WITH_RSSI: 102 | pkt = pkt[3:] 103 | nrsp = struct.unpack("B", pkt[0])[0] 104 | for i in range(nrsp): 105 | addr = bluez.ba2str( pkt[1+6*i:1+6*i+6] ) 106 | rssi = struct.unpack("b", pkt[1+13*nrsp+i])[0] 107 | 108 | if addr == wanted_addr: 109 | results.append( rssi ) 110 | # print "*** ", 111 | # print "[%s] RSSI: [%d]" % (addr, rssi) 112 | elif event == bluez.EVT_INQUIRY_COMPLETE: 113 | done = True 114 | elif event == bluez.EVT_CMD_STATUS: 115 | status, ncmd, opcode = struct.unpack("BBH", pkt[3:7]) 116 | if status != 0: 117 | print "uh oh..." 118 | done = True 119 | else: 120 | # print "unrecognized packet type 0x%02x" % ptype 121 | continue 122 | 123 | 124 | # restore old filter 125 | sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, old_filter ) 126 | 127 | if len(results) > 10: 128 | results = results[-10:] 129 | return results 130 | 131 | #dev_id = bluez.hci_get_route("C4:85:08:0F:E0:05") 132 | 133 | try: 134 | sock = bluez.hci_open_dev() 135 | except: 136 | print "error accessing bluetooth device..." 137 | sys.exit(1) 138 | 139 | try: 140 | mode = read_inquiry_mode(sock) 141 | except Exception, e: 142 | print "error reading inquiry mode. " 143 | print "Are you sure this a bluetooth 1.2 device?" 144 | print e 145 | sys.exit(1) 146 | print "current inquiry mode is %d" % mode 147 | 148 | if mode != 1: 149 | print "writing inquiry mode..." 150 | try: 151 | result = write_inquiry_mode(sock, 1) 152 | except Exception, e: 153 | print "error writing inquiry mode. Are you sure you're root?" 154 | print e 155 | sys.exit(1) 156 | if result != 0: 157 | print "error while setting inquiry mode" 158 | print "result: %d" % result 159 | 160 | 161 | 162 | while True: 163 | # Clear all filters 164 | cmd_pkt = struct.pack("B", 0x00) 165 | bluez.hci_send_cmd(sock, bluez.OGF_HOST_CTL, bluez.OCF_SET_EVENT_FLT, cmd_pkt) 166 | 167 | # cmd_pkt = struct.pack("BBBBBBBB", 0x02, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff) 168 | # bluez.hci_send_cmd(sock, bluez.OGF_HOST_CTL, bluez.OCF_SET_EVENT_FLT, cmd_pkt) 169 | 170 | # Set class-of-device filter 171 | cmd_pkt = struct.pack("BBBBBBBB", 0x01, 0x01, 0xf0, 0x07, 0x04, 0x00, 0x0f, 0x00) 172 | bluez.hci_send_cmd(sock, bluez.OGF_HOST_CTL, bluez.OCF_SET_EVENT_FLT, cmd_pkt) 173 | 174 | # print "inquiring" 175 | device_inquiry_with_with_rssi(sock, "00:17:E9:4A:64:91") 176 | sleep(3) 177 | # print results 178 | 179 | avg = -100 180 | if len(results) == 0: 181 | continue; 182 | elif len(results) < 10 : 183 | avg = sum(results) / float(len(results)) 184 | else: 185 | avg = sum(results[5:10]) / float(len(results[5:10])) 186 | print "avg = " + str(avg) 187 | if avg > -50: 188 | os.system("pkill i3lock") 189 | else: 190 | os.system("xautolock -locknow") 191 | -------------------------------------------------------------------------------- /proximity_dagar.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # Daniel Agar 3 | # 2011-11-13 4 | 5 | # TODO: add keyboard ctrl c handling 6 | # TODO: add command line arguments 7 | # TODO: add error handling and standard loggin 8 | 9 | import fcntl 10 | import struct 11 | import array 12 | import bluetooth 13 | import bluetooth._bluetooth as bt 14 | 15 | import time 16 | import os 17 | import datetime 18 | 19 | def bluetooth_rssi(addr): 20 | # Open hci socket 21 | hci_sock = bt.hci_open_dev() 22 | hci_fd = hci_sock.fileno() 23 | 24 | # Connect to device (to whatever you like) 25 | bt_sock = bluetooth.BluetoothSocket(bluetooth.L2CAP) 26 | bt_sock.settimeout(10) 27 | result = bt_sock.connect_ex((addr, 1)) # PSM 1 - Service Discovery 28 | 29 | try: 30 | # Get ConnInfo 31 | reqstr = struct.pack("6sB17s", bt.str2ba(addr), bt.ACL_LINK, "\0" * 17) 32 | request = array.array("c", reqstr ) 33 | handle = fcntl.ioctl(hci_fd, bt.HCIGETCONNINFO, request, 1) 34 | handle = struct.unpack("8xH14x", request.tostring())[0] 35 | 36 | # Get RSSI 37 | cmd_pkt=struct.pack('H', handle) 38 | rssi = bt.hci_send_req(hci_sock, bt.OGF_STATUS_PARAM, 39 | bt.OCF_READ_RSSI, bt.EVT_CMD_COMPLETE, 4, cmd_pkt) 40 | rssi = struct.unpack('b', rssi[3])[0] 41 | 42 | # Close sockets 43 | bt_sock.close() 44 | hci_sock.close() 45 | 46 | print rssi 47 | 48 | return rssi 49 | 50 | 51 | except: 52 | return None 53 | 54 | 55 | 56 | far = True 57 | far_count = 0 58 | 59 | # assume phone is initially far away 60 | rssi = -255 61 | rssi_prev1 = -255 62 | rssi_prev2 = -255 63 | 64 | near_cmd = 'br -n 1' 65 | far_cmd = 'br -f 1' 66 | 67 | dagar_addr = '00:17:E9:4A:64:91' 68 | #emily_addr = '43:29:B1:55:00:00' 69 | 70 | debug = 1 71 | 72 | while True: 73 | # get rssi reading for address 74 | rssi = bluetooth_rssi(dagar_addr) 75 | 76 | if debug: 77 | print datetime.datetime.now(), rssi, rssi_prev1, rssi_prev2, far, far_count 78 | 79 | 80 | if rssi == rssi_prev1 == rssi_prev2 == None: 81 | print datetime.datetime.now(), "can't detect address" 82 | time.sleep(3) 83 | 84 | elif rssi == rssi_prev1 == rssi_prev2 == 0: 85 | # change state if nearby 86 | if far: 87 | far = False 88 | far_count = 0 89 | os.system(near_cmd) 90 | print datetime.datetime.now(), "changing to near" 91 | 92 | time.sleep(30) 93 | 94 | elif rssi < -2 and rssi_prev1 < -2 and rssi_prev2 < -2: 95 | # if were near and single has been consisitenly low 96 | 97 | # need 10 in a row to set to far 98 | far_count += 1 99 | if not far and far_count > 10: 100 | # switch state to far 101 | far = True 102 | far_count = 0 103 | os.system(far_cmd) 104 | print datetime.datetime.now(), "changing to far" 105 | time.sleep(5) 106 | 107 | else: 108 | far_count = 0 109 | 110 | 111 | rssi_prev1 = rssi 112 | rssi_prev2 = rssi_prev1 113 | 114 | 115 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | twisted==12.0.0 2 | autobahn==0.5.14 3 | websocket-client==0.12.0 4 | sh==1.08 5 | pyserial>=2.6 6 | pypng==0.0.16 7 | # Only if using bluetooth connection to Pebble: 8 | git+http://github.com/pebble/lightblue-0.4.git#egg=lightblue-0.4 9 | pybluez 10 | 11 | --------------------------------------------------------------------------------