This repository has been archived by the owner on Nov 28, 2021. It is now read-only.
forked from atlefren/pytilt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
blescan.py
121 lines (93 loc) · 3.48 KB
/
blescan.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# based on https://github.com/switchdoclabs/iBeacon-Scanner-
import os
import sys
import struct
import bluetooth._bluetooth as bluez
LE_META_EVENT = 0x3e
LE_PUBLIC_ADDRESS = 0x00
LE_RANDOM_ADDRESS = 0x01
LE_SET_SCAN_PARAMETERS_CP_SIZE = 7
OGF_LE_CTL = 0x08
OCF_LE_SET_SCAN_PARAMETERS = 0x000B
OCF_LE_SET_SCAN_ENABLE = 0x000C
OCF_LE_CREATE_CONN = 0x000D
LE_ROLE_MASTER = 0x00
LE_ROLE_SLAVE = 0x01
# these are actually subevents of LE_META_EVENT
EVT_LE_CONN_COMPLETE = 0x01
EVT_LE_ADVERTISING_REPORT = 0x02
EVT_LE_CONN_UPDATE_COMPLETE = 0x03
EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE = 0x04
# Advertisment event types
ADV_IND = 0x00
ADV_DIRECT_IND = 0x01
ADV_SCAN_IND = 0x02
ADV_NONCONN_IND = 0x03
ADV_SCAN_RSP = 0x04
def returnnumberpacket(pkt):
integer = 0
multiple = 256
for c in pkt:
integer += struct.unpack('B', bytes([c]))[0] * multiple
multiple = 1
return integer
def returnstringpacket(pkt):
string = ''
for c in pkt:
string += '%02x' % struct.unpack('B', bytes([c]))[0]
return string
def printpacket(pkt):
for c in pkt:
sys.stdout.write('%02x ' % struct.unpack('B', bytes([c]))[0])
def get_packed_bdaddr(bdaddr_string):
packable_addr = []
addr = bdaddr_string.split(':')
addr.reverse()
for b in addr:
packable_addr.append(int(b, 16))
return struct.pack('<BBBBBB', *packable_addr)
def packed_bdaddr_to_string(bdaddr_packed):
return ':'.join('%02x' % i for i in struct.unpack("<BBBBBB", bdaddr_packed[::-1]))
def hci_enable_le_scan(sock):
hci_toggle_le_scan(sock, 0x01)
def hci_disable_le_scan(sock):
hci_toggle_le_scan(sock, 0x00)
def hci_toggle_le_scan(sock, enable):
cmd_pkt = struct.pack("<BB", enable, 0x00)
bluez.hci_send_cmd(sock, OGF_LE_CTL, OCF_LE_SET_SCAN_ENABLE, cmd_pkt)
def hci_le_set_scan_parameters(sock):
old_filter = sock.getsockopt(bluez.SOL_HCI, bluez.HCI_FILTER, 14)
SCAN_RANDOM = 0x01
OWN_TYPE = SCAN_RANDOM
SCAN_TYPE = 0x01
def parse_events(sock, loop_count=100):
old_filter = sock.getsockopt(bluez.SOL_HCI, bluez.HCI_FILTER, 14)
# perform a device inquiry on bluetooth device #0
# The inquiry should last 8 * 1.28 = 10.24 seconds
# before the inquiry is performed, bluez should flush its cache of
# previously discovered devices
flt = bluez.hci_filter_new()
bluez.hci_filter_all_events(flt)
bluez.hci_filter_set_ptype(flt, bluez.HCI_EVENT_PKT)
sock.setsockopt(bluez.SOL_HCI, bluez.HCI_FILTER, flt)
beacons = []
for i in range(0, loop_count):
pkt = sock.recv(255)
ptype, event, plen = struct.unpack('BBB', pkt[:3])
if event == LE_META_EVENT:
subevent, = struct.unpack('B', pkt[3:4])
pkt = pkt[4:]
if subevent == EVT_LE_CONN_COMPLETE:
le_handle_connection_complete(pkt)
elif subevent == EVT_LE_ADVERTISING_REPORT:
num_reports = struct.unpack('B', pkt[0:1])[0]
report_pkt_offset = 0
for i in range(0, num_reports):
beacons.append({
'uuid': returnstringpacket(pkt[report_pkt_offset - 22: report_pkt_offset - 6]),
'minor': returnnumberpacket(pkt[report_pkt_offset - 4: report_pkt_offset - 2]),
'major': returnnumberpacket(pkt[report_pkt_offset - 6: report_pkt_offset - 4])
})
done = True
sock.setsockopt(bluez.SOL_HCI, bluez.HCI_FILTER, old_filter)
return beacons