Example #1
0
    def _open_serial_port(self):
        """
        Open a connection to the named serial port, or auto-detect the first
        port matching the BLED device. This will wait until data can actually be
        read from the connection, so it will not return until the device is
        fully booted.

        Raises a NotConnectedError if the device cannot connect after 10
        attempts, with a short pause in between each attempt.
        """
        for attempt in range(MAX_RECONNECTION_ATTEMPTS):
            try:
                serial_port = self._serial_port or self._detect_device_port()
                self._ser = None

                log.debug("Attempting to connect to serial port after "
                          "restarting device")
                self._ser = serial.Serial(serial_port,
                                          baudrate=115200,
                                          timeout=0.25)
                # Wait until we can actually read from the device
                self._ser.read()
                break
            except (BGAPIError, serial.serialutil.SerialException,
                    serial_exception):
                if self._ser:
                    self._ser.close()
                elif attempt == 0:
                    raise NotConnectedError(
                        "No BGAPI compatible device detected")
                self._ser = None
                time.sleep(0.25)
        else:
            raise NotConnectedError("Unable to reconnect with USB "
                                    "device after rebooting")
Example #2
0
    def _open_serial_port(self,
                          max_connection_attempts=MAX_CONNECTION_ATTEMPTS):
        """
        Open a connection to the named serial port, or auto-detect the first
        port matching the BLED device. This will wait until data can actually be
        read from the connection, so it will not return until the device is
        fully booted.

        max_connection_attempts -- Max number of times to retry
            detecting and connecting to a device.

        Raises a NotConnectedError if the device cannot connect after 10
        attempts, with a short pause in between each attempt.
        """
        for attempt in range(max_connection_attempts):
            log.debug("Opening connection to serial port (attempt %d)",
                      attempt + 1)
            try:
                serial_port = self._serial_port or self._detect_device_port()
                self._ser = None
                for lattempt in range(MAX_CONNECTION_ATTEMPTS):
                    try:
                        self._ser = serial.Serial(serial_port,
                                                  baudrate=115200,
                                                  timeout=0.25)
                        break
                    except (BGAPIError, serial.serialutil.SerialException,
                            serial_exception):
                        time.sleep(0.25)
                try:
                    # Wait until we can actually read from the device
                    self._ser.read()
                    break
                except (BGAPIError, serial.serialutil.SerialException,
                        serial_exception):
                    if self._ser:
                        self._ser.close()
                    elif attempt == 0:
                        raise NotConnectedError(
                            "No BGAPI compatible device detected")
                        self._ser = None
                        time.sleep(0.5)
            except (BGAPIError, serial.serialutil.SerialException,
                    serial_exception):
                log.debug("Failed to open serial port", exc_info=True)
                if self._ser:
                    self._ser.close()
                elif attempt == 0:
                    raise NotConnectedError(
                        "No BGAPI compatible device detected")
                self._ser = None
                time.sleep(0.25)
        else:
            raise NotConnectedError("Unable to reconnect with USB "
                                    "device after rebooting")
Example #3
0
    def connect(
            self,
            address,
            timeout=5,
            addr_type=constants.ble_address_type['gap_address_type_public'],
            interval_min=60,
            interval_max=76,
            supervision_timeout=100,
            latency=0):
        """
        Connnect directly to a device given the ble address then discovers and
        stores the characteristic and characteristic descriptor handles.

        Requires that the adapter is not connected to a device already.

        address -- a bytearray containing the device mac address.
        timeout -- number of seconds to wait before returning if not connected.
        addr_type -- one of the ble_address_type constants.

        Raises BGAPIError or NotConnectedError on failure.
        """

        address_bytes = bytearray(unhexlify(address.replace(":", "")))
        for device in self._connections.values():
            if device._address == bgapi_address_to_hex(address_bytes):
                return device

        log.info("Connecting to device at address %s (timeout %ds)", address,
                 timeout)
        self.set_bondable(False)
        self.send_command(
            CommandBuilder.gap_connect_direct(address_bytes, addr_type,
                                              interval_min, interval_max,
                                              supervision_timeout, latency))

        self.expect(ResponsePacketType.gap_connect_direct)
        try:
            _, packet = self.expect(EventPacketType.connection_status,
                                    timeout=timeout)
            # TODO what do we do if the status isn't 'connected'? Retry? Raise
            # an exception? Should also check the address matches the expected
            # TODO i'm finding that when reconnecting to the same MAC, we geta
            # conneciotn status of "disconnected" but that is picked up here as
            # "connected", then we don't get anything else.
            if self._connection_status_flag(
                    packet['flags'],
                    constants.connection_status_flag['connected']):
                device = BGAPIBLEDevice(
                    bgapi_address_to_hex(packet['address']),
                    packet['connection_handle'], self)
                if self._connection_status_flag(
                        packet['flags'],
                        constants.connection_status_flag['encrypted']):
                    device.encrypted = True
                self._connections[packet['connection_handle']] = device
                log.info("Connected to %s", address)
                return device
        except ExpectedResponseTimeout:
            raise NotConnectedError()
 def connect(self, address, retry=False):
     con = None
     try:
         if self.adapter is None:
             self.new_connection()
         if address not in self.connection_cache:
             con = self.adapter.connect(address)
             self.connection_cache[address] = con
         else:
             con = self.connection_cache[address]
             self.connection_cache[address] = con
     except NotConnectedError, ExpectedResponseTimeout:
         self.disconnect(address)
         self.new_connection()
         if not retry:
             return self.connect(address, True)
         raise NotConnectedError()
Example #5
0
    def discover_characteristics(self, timeout=15):
        self._characteristics = {}
        self._receiver.register_callback(
            "discover",
            self._save_charecteristic_callback,
        )
        self.sendline('characteristics')
        max_time = time.time() + timeout
        while not self._characteristics and time.time() < max_time:
            time.sleep(1.5)
        if not self._characteristics:
            raise NotConnectedError("Characteristic discovery failed")
        # Sleep one extra second in case we caught characteristic
        # in the middle
        time.sleep(1)

        return self._characteristics
Example #6
0
    def connect(self, address, timeout=DEFAULT_CONNECT_TIMEOUT_S,
                address_type='public'):
        log.info('Connecting with timeout=%s', timeout)
        self._con.sendline('sec-level low')
        self._address = address
        try:
            with self._connection_lock:
                cmd = 'connect %s %s' % (self._address, address_type)
                self._con.sendline(cmd)
                self._con.expect(b'Connection successful.*\[LE\]>', timeout)
        except pexpect.TIMEOUT:
            message = ("Timed out connecting to %s after %s seconds."
                       % (self._address, timeout))
            log.error(message)
            raise NotConnectedError(message)

        self._connected_device = GATTToolBLEDevice(address, self)
        return self._connected_device
Example #7
0
    def connect(self, address, timeout=DEFAULT_CONNECT_TIMEOUT_S,
                address_type=BLEAddressType.public):
        log.info('Connecting to %s with timeout=%s', address, timeout)
        self.sendline('sec-level low')
        self._address = address

        try:
            cmd = 'connect {0} {1}'.format(self._address, address_type.name)
            with self._receiver.event("connect", timeout):
                self.sendline(cmd)
        except NotificationTimeout:
            message = "Timed out connecting to {0} after {1} seconds.".format(
                self._address, timeout
            )
            log.error(message)
            raise NotConnectedError(message)

        self._connected_device = GATTToolBLEDevice(address, self)
        return self._connected_device
Example #8
0
    def _send(self, values):
        """
        Send a command to the unit.
        """

        if self._device is None:
            raise NotConnectedError()

        try:
            checksum = 0
            for v in values:
                checksum += v

            payload = tuple(values) + ((checksum >> 8) & 0xff, checksum & 0xff)

            self._device.char_write_handle(_HANDLE, payload)

        except NotConnectedError:
            self._device = None
            raise
Example #9
0
    def connect(self,
                address,
                timeout=5,
                address_type=BLEAddressType.public,
                interval_min=60,
                interval_max=76,
                supervision_timeout=100,
                latency=0):
        """
        Connnect directly to a device given the ble address then discovers and
        stores the characteristic and characteristic descriptor handles.

        Requires that the adapter is not connected to a device already.

        address -- a bytearray containing the device mac address.
        timeout -- number of seconds to wait before returning if not connected.
        address_type -- one of BLEAddressType's values, either public or random.

        Raises BGAPIError or NotConnectedError on failure.
        """

        address_bytes = bytearray(unhexlify(address.replace(":", "")))
        for device in self._connections.values():
            if device._address == bgapi_address_to_hex(address_bytes):
                return device

        log.info("Connecting to device at address %s (timeout %ds)", address,
                 timeout)
        self.set_bondable(False)

        if address_type == BLEAddressType.public:
            addr_type = constants.ble_address_type['gap_address_type_public']
        else:
            addr_type = constants.ble_address_type['gap_address_type_random']

        self.send_command(
            CommandBuilder.gap_connect_direct(address_bytes, addr_type,
                                              interval_min, interval_max,
                                              supervision_timeout, latency))

        try:
            self.expect(ResponsePacketType.gap_connect_direct)
            _, packet = self.expect(EventPacketType.connection_status,
                                    timeout=timeout)
            # TODO what do we do if the status isn't 'connected'? Retry?
            # Raise an exception? Should also check the address matches the
            # expected TODO i'm finding that when reconnecting to the same
            # MAC, we geta conneciotn status of "disconnected" but that is
            # picked up here as "connected", then we don't get anything
            # else.
            if self._connection_status_flag(
                    packet['flags'],
                    constants.connection_status_flag['connected']):
                device = BGAPIBLEDevice(
                    bgapi_address_to_hex(packet['address']),
                    packet['connection_handle'], self)
                if self._connection_status_flag(
                        packet['flags'],
                        constants.connection_status_flag['encrypted']):
                    device.encrypted = True
                self._connections[packet['connection_handle']] = device
                log.info("Connected to %s", address)
                return device
        except ExpectedResponseTimeout:
            # If the connection doesn't occur because the device isn't there
            # then you should manually stop the command.
            #
            # If we never get the connection status it is likely that it
            # didn't occur because the device isn't there. If that is true
            # then we have to manually stop the command.
            self._end_procedure()
            exc = NotConnectedError()
            exc.__cause__ = None
            raise exc
Example #10
0
 def send_command(self, *args, **kwargs):
     with self._lock:
         if self._ser is None:
             log.warn("Unexpectedly not connected to USB device")
             raise NotConnectedError()
         return self._lib.send_command(self._ser, *args, **kwargs)
Example #11
0
 def wrapper(self, connected_device, *args, **kwargs):
     if connected_device != self._connected_device:
         raise NotConnectedError()
     return func(self, *args, **kwargs)
Example #12
0
 def wrapper(self, *args, **kwargs):
     if not self._connected:
         raise NotConnectedError()
     return func(self, *args, **kwargs)
Example #13
0
    def start(self, reset=True, tries=5):
        """
        Connect to the USB adapter, reset it's state and start a backgroud
        receiver thread.
        """
        if self._running and self._running.is_set():
            self.stop()

        # Fail immediately if no device is attached, don't retry waiting for one
        # to be plugged in.
        self._open_serial_port(max_connection_attempts=1)

        if reset:
            log.debug(
                "Resetting and reconnecting to device for a clean environment")
            # Blow everything away and start anew.
            # Only way to be sure is to burn it down and start again.
            # (Aka reset remote state machine)
            # Note: Could make this a conditional based on parameter if this
            # happens to be too slow on some systems.

            # The zero param just means we want to do a normal restart instead of
            # starting a firmware update restart.
            self.send_command(CommandBuilder.system_reset(0))
            self._ser.flush()
            self._ser.close()

            # Re-open the port. On Windows, it has been observed that the
            # port is no immediately available - so retry for up to 2 seconds.
            start = time.clock()
            retry_t = 0.2
            while True:
                try:
                    self._open_serial_port()
                except:
                    if time.clock() - start > 2:
                        raise
                    else:
                        log.debug('Port not ready, retry in %.2f seconds...' %
                                  retry_t)
                        time.sleep(retry_t)
                else:
                    break

        if tries is None or not tries:
            # Try at least once to open the port
            tries = 1
        # Sometimes when opening the port without a reset, it'll fail to respond
        # So let's try to repeat the initialization process a few times
        while tries:
            tries -= 1
            try:
                self._initialize_device(reset)
                return
            except ExpectedResponseTimeout:
                if tries:
                    log.info("BLED unresponsive, re-opening")
                    self.stop()
                    self._open_serial_port(max_connection_attempts=1)
                    continue
        # If we got here, we failed to open the port
        raise NotConnectedError()
Example #14
0
    def connect(self, address, timeout=5,
                address_type=BLEAddressType.public,
                interval_min=60, interval_max=76, supervision_timeout=100,
                latency=0):
        """
        Connnect directly to a device given the ble address then discovers and
        stores the characteristic and characteristic descriptor handles.

        Requires that the adapter is not connected to a device already.

        address -- a bytearray containing the device mac address.
        timeout -- number of seconds to wait before returning if not connected.
        address_type -- one of BLEAddressType's values, either public or random.

        Raises BGAPIError or NotConnectedError on failure.
        """

        address_bytes = bytearray(unhexlify(address.replace(":", "")))
        for device in self._connections.values():
            if device._address == bgapi_address_to_hex(address_bytes):
                return device

        log.info("Connecting to device at address %s (timeout %ds)",
                 address, timeout)
        self.set_bondable(False)

        if address_type == BLEAddressType.public:
            addr_type = constants.ble_address_type['gap_address_type_public']
        else:
            addr_type = constants.ble_address_type['gap_address_type_random']

        self.send_command(
            CommandBuilder.gap_connect_direct(
                address_bytes, addr_type, interval_min, interval_max,
                supervision_timeout, latency))

        try:
            self.expect(ResponsePacketType.gap_connect_direct)
            _, packet = self.expect(EventPacketType.connection_status,
                                    timeout=timeout)
            # TODO what do we do if the status isn't 'connected'? Retry?
            # Raise an exception? Should also check the address matches the
            # expected TODO i'm finding that when reconnecting to the same
            # MAC, we geta conneciotn status of "disconnected" but that is
            # picked up here as "connected", then we don't get anything
            # else.
            if self._connection_status_flag(
                    packet['flags'],
                    constants.connection_status_flag['connected']):
                device = BGAPIBLEDevice(
                    bgapi_address_to_hex(packet['address']),
                    packet['connection_handle'],
                    self)
                if self._connection_status_flag(
                        packet['flags'],
                        constants.connection_status_flag['encrypted']):
                    device.encrypted = True
                self._connections[packet['connection_handle']] = device
                log.info("Connected to %s", address)
                return device
        except ExpectedResponseTimeout:
            # If the connection doesn't occur because the device isn't there
            # then you should manually stop the command.
            #
            # If we never get the connection status it is likely that it
            # didn't occur because the device isn't there. If that is true
            # then we have to manually stop the command.
            self._end_procedure()
            exc = NotConnectedError()
            exc.__cause__ = None
            raise exc