def __init__(self, hci_device='hci0', gatttool_logfile=None, cli_options=None, search_window_size=None, max_read=None): """ Initialize. hci_device -- the hci_device to use with GATTTool. gatttool_logfile -- an optional filename to store raw gatttool input and output. search_window_size -- integer (optional); size in bytes of the search window that is used by `pexpect.expect`. This value should not exceed max_read max_read -- integer; number of bytes to read into gatt buffer at a time. Defaults to ~2000 """ if is_windows(): raise BLEError("The GATTToolBackend requires BlueZ, " "which is not available in Windows") self._hci_device = hci_device self._cli_options = cli_options self._connected_device = None self._gatttool_logfile = gatttool_logfile self._receiver = None self._con = None # gatttool interactive session self._characteristics = {} self._running = threading.Event() self._address = None self._send_lock = threading.Lock() self._auto_reconnect = False self._reconnecting = False self._search_window_size = search_window_size self._scan = None self._max_read = max_read
def __init__(self, hci_device='hci0', gatttool_logfile=None, cli_options=None, search_window_size=200): """ Initialize. hci_device -- the hci_device to use with GATTTool. gatttool_logfile -- an optional filename to store raw gatttool input and output. search_window_size -- integer (optional); size in bytes of the search window that is used by `pexpect.expect` """ if is_windows(): raise BLEError("The GATTToolBackend requires BlueZ, " "which is not available in Windows") self._hci_device = hci_device self._cli_options = cli_options self._connected_device = None self._gatttool_logfile = gatttool_logfile self._receiver = None self._con = None # gatttool interactive session self._characteristics = {} self._running = threading.Event() self._address = None self._send_lock = threading.Lock() self._search_window_size = search_window_size
def scan(self, timeout=10, run_as_root=False): """ By default, scanning with gatttool requires root privileges. If you don't want to require root, you must add a few 'capabilities' to your system. If you have libcap installed, run this to enable normal users to perform LE scanning: setcap 'cap_net_raw,cap_net_admin+eip' `which hcitool` If you do use root, the hcitool subprocess becomes more difficult to terminate cleanly, and may leave your Bluetooth adapter in a bad state. """ cmd = 'hcitool lescan' if run_as_root: cmd = 'sudo %s' % cmd log.info("Starting BLE scan") scan = pexpect.spawn(cmd) # "lescan" doesn't exit, so we're forcing a timeout here: try: scan.expect('foooooo', timeout=timeout) except pexpect.EOF: message = "Unexpected error when scanning" if "No such device" in scan.before.decode('utf-8'): message = "No BLE adapter found" log.error(message) raise BLEError(message) except pexpect.TIMEOUT: devices = {} for line in scan.before.decode('utf-8').split('\r\n'): match = re.match( r'(([0-9A-Fa-f][0-9A-Fa-f]:?){6}) (\(?[\w]+\)?)', line) if match is not None: address = match.group(1) name = match.group(3) if name == "(unknown)": name = None if address in devices: if (devices[address]['name'] is None) and (name is not None): log.info("Discovered name of %s as %s", address, name) devices[address]['name'] = name else: log.info("Discovered %s (%s)", address, name) devices[address] = { 'address': address, 'name': name } log.info("Found %d BLE devices", len(devices)) return [device for device in devices.values()] return []
def scan(self, timeout=10, run_as_root=False): """ By default, scanning with gatttool requires root privileges. If you don't want to require root, you must add a few 'capabilities' to your system. If you have libcap installed, run this to enable normal users to perform LE scanning: setcap 'cap_net_raw,cap_net_admin+eip' `which hcitool` If you do use root, the hcitool subprocess becomes more difficult to terminate cleanly, and may leave your Bluetooth adapter in a bad state. """ cmd = 'hcitool -i %s lescan' % self._hci_device if run_as_root: cmd = 'sudo %s' % cmd log.info("Starting BLE scan") scan = pexpect.spawn(cmd) # "lescan" doesn't exit, so we're forcing a timeout here: try: scan.expect('foooooo', timeout=timeout) except pexpect.EOF: before_eof = scan.before.decode('utf-8') if "No such device" in before_eof: message = "No BLE adapter found" elif "Set scan parameters failed: Input/output error" in before_eof: message = ("BLE adapter requires reset after a scan as root" "- call adapter.reset()") else: message = "Unexpected error when scanning: %s" % before_eof log.error(message) raise BLEError(message) except pexpect.TIMEOUT: devices = {} for line in scan.before.decode('utf-8').split('\r\n'): if 'sudo' in line: raise BLEError("Enable passwordless sudo for 'hcitool' " "before scanning") match = re.match( r'(([0-9A-Fa-f][0-9A-Fa-f]:?){6}) (\(?.+\)?)', line) if match is not None: address = match.group(1) name = match.group(3) if name == "(unknown)": name = None if address in devices: if (devices[address]['name'] is None) and (name is not None): log.info("Discovered name of %s as %s", address, name) devices[address]['name'] = name else: log.info("Discovered %s (%s)", address, name) devices[address] = { 'address': address, 'name': name } log.info("Found %d BLE devices", len(devices)) return [device for device in devices.values()] finally: # Wait for lescan to exit cleanly, otherwise it leaves the BLE # adapter in a bad state and the device must be reset through BlueZ. # This will not work if run_as_root was used, since this process # itself doesn't have permission to terminate a process running as # root (hcitool itself). We recommend using the setcap tool to allow # scanning as a non-root user: # # $ sudo setcap 'cap_net_raw,cap_net_admin+eip' `which hcitool` try: scan.kill(signal.SIGINT) scan.wait() except OSError: log.error("Unable to gracefully stop the scan - " "BLE adapter may need to be reset.") return []
def scan(self, timeout=10, run_as_root=False): """ By default, scanning with gatttool requires root privileges. If you don't want to require root, you must add a few 'capabilities' to your system. If you have libcap installed, run this to enable normal users to perform LE scanning: setcap 'cap_net_raw,cap_net_admin+eip' `which hcitool` If you do use root, the hcitool subprocess becomes more difficult to terminate cleanly, and may leave your Bluetooth adapter in a bad state. """ cmd = 'hcitool -i %s lescan' % self._hci_device if run_as_root and which('sudo') != None: cmd = 'sudo %s' % cmd log.info("Starting BLE scan") # "lescan" doesn't exit, so we're forcing a timeout self._scan = scan = pexpect.spawn(cmd, timeout=timeout) try: scan.expect('foooooo') except pexpect.EOF: before_eof = scan.before.decode('utf-8', 'replace') if "No such device" in before_eof: message = "No BLE adapter found" elif "Set scan parameters failed: Input/output error" in before_eof: message = ("BLE adapter requires reset after a scan as root" "- call adapter.reset()") else: message = "Unexpected error when scanning: %s" % before_eof log.error(message) raise BLEError(message) except pexpect.TIMEOUT: devices = {} for line in scan.before.decode('utf-8', 'replace').split('\r\n'): if 'sudo' in line: raise BLEError("Enable passwordless sudo for 'hcitool' " "before scanning") match = re.match(r'(([0-9A-Fa-f][0-9A-Fa-f]:?){6}) (\(?.+\)?)', line) if match is not None: address = match.group(1) name = match.group(3) if name == "(unknown)": name = None if address in devices: if (devices[address]['name'] is None) and (name is not None): log.info("Discovered name of %s as %s", address, name) devices[address]['name'] = name else: log.info("Discovered %s (%s)", address, name) devices[address] = {'address': address, 'name': name} log.info("Found %d BLE devices", len(devices)) return [device for device in devices.values()] finally: self.kill() return []