Esempio n. 1
0
    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
Esempio n. 2
0
    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
Esempio n. 3
0
    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 []
Esempio n. 4
0
    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 []
Esempio n. 5
0
    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 []