Example #1
0
 def _func_wrapper(*args, **kwargs):
     try:
         return func(*args, **kwargs)
     except BGAPIError as exception:
         raise BluetoothBackendException() from exception
     except NotConnectedError as exception:
         raise BluetoothBackendException() from exception
Example #2
0
    def write_handle(self, handle, value):
        """Read from a BLE address.

        @param: mac - MAC address in format XX:XX:XX:XX:XX:XX
        @param: handle - BLE characteristics handle in format 0xXX
        @param: value - value to write to the given handle
        @param: timeout - timeout in seconds
        """

        if not self.is_connected():
            raise BluetoothBackendException('Not connected to any device.')

        attempt = 0
        delay = 10
        _LOGGER.debug("Enter write_ble (%s)", current_thread())

        while attempt <= self.retries:
            cmd = "gatttool --device={} --char-write-req -a {} -n {} --adapter={}".format(
                self._mac, self.byte_to_handle(handle),
                self.bytes_to_string(value), self.adapter)
            _LOGGER.debug("Running gatttool with a timeout of %d: %s",
                          self.timeout, cmd)

            with Popen(cmd,
                       shell=True,
                       stdout=PIPE,
                       stderr=PIPE,
                       preexec_fn=os.setsid) as process:
                try:
                    result = process.communicate(timeout=self.timeout)[0]
                    _LOGGER.debug("Finished gatttool")
                except TimeoutExpired:
                    # send signal to the process group
                    os.killpg(process.pid, signal.SIGINT)
                    result = process.communicate()[0]
                    _LOGGER.debug("Killed hanging gatttool")

            result = result.decode("utf-8").strip(' \n\t')
            if "Write Request failed" in result:
                raise BluetoothBackendException(
                    'Error writing handls to sensor: {}'.format(result))
            _LOGGER.debug("Got %s from gatttool", result)
            # Parse the output
            if "successfully" in result:
                _LOGGER.debug("Exit write_ble with result (%s)",
                              current_thread())
                return True

            attempt += 1
            _LOGGER.debug("Waiting for %s seconds before retrying", delay)
            if attempt < self.retries:
                time.sleep(delay)
                delay *= 2

        raise BluetoothBackendException("Exit write_ble, no data ({})".format(
            current_thread()))
Example #3
0
    def read_handle(self, handle):
        """Read from a BLE address.

        @param: mac - MAC address in format XX:XX:XX:XX:XX:XX
        @param: handle - BLE characteristics handle in format 0xXX
        @param: timeout - timeout in seconds
        """

        if not self.is_connected():
            raise BluetoothBackendException('Not connected to any device.')

        attempt = 0
        delay = 10
        _LOGGER.debug("Enter read_ble (%s)", current_thread())

        while attempt <= self.retries:
            cmd = "gatttool --device={} --char-read -a {} --adapter={}".format(
                self._mac, self.byte_to_handle(handle), self.adapter)
            _LOGGER.debug("Running gatttool with a timeout of %d: %s",
                          self.timeout, cmd)
            with Popen(cmd,
                       shell=True,
                       stdout=PIPE,
                       stderr=PIPE,
                       preexec_fn=os.setsid) as process:
                try:
                    result = process.communicate(timeout=self.timeout)[0]
                    _LOGGER.debug("Finished gatttool")
                except TimeoutExpired:
                    # send signal to the process group
                    os.killpg(process.pid, signal.SIGINT)
                    result = process.communicate()[0]
                    _LOGGER.debug("Killed hanging gatttool")

            result = result.decode("utf-8").strip(' \n\t')
            _LOGGER.debug("Got \"%s\" from gatttool", result)
            # Parse the output
            if "read failed" in result:
                raise BluetoothBackendException(
                    "Read error from gatttool: {}".format(result))

            res = re.search("( [0-9a-fA-F][0-9a-fA-F])+", result)
            if res:
                _LOGGER.debug("Exit read_ble with result (%s)",
                              current_thread())
                return bytes([int(x, 16) for x in res.group(0).split()])

            attempt += 1
            _LOGGER.debug("Waiting for %s seconds before retrying", delay)
            if attempt < self.retries:
                time.sleep(delay)
                delay *= 2

        raise BluetoothBackendException("Exit read_ble, no data ({})".format(
            current_thread()))
Example #4
0
    def parameter_value(self, parameter, read_cached=True):
        """Return a value of one of the monitored paramaters.

        This method will try to retrieve the data from cache and only
        request it by bluetooth if no cached value is stored or the cache is
        expired.
        This behaviour can be overwritten by the "read_cached" parameter.
        """
        # Special handling for battery attribute
        if parameter == MI_BATTERY:
            return self.battery_level()

        # Use the lock to make sure the cache isn't updated multiple times
        with self.lock:
            if (read_cached is False) or \
                    (self._last_read is None) or \
                    (datetime.now() - self._cache_timeout > self._last_read):
                self.fill_cache()
            else:
                _LOGGER.debug("Using cache (%s < %s)",
                              datetime.now() - self._last_read,
                              self._cache_timeout)

        if self.cache_available() and (len(self._cache) == 16):
            return self._parse_data()[parameter]
        else:
            raise BluetoothBackendException(
                "Could not read data from Mi Flora sensor %s" % self._mac)
Example #5
0
 def check_backend(self):
     try:
         call('gatttool', stdout=PIPE, stderr=PIPE)
         return True
     except OSError as e:
         msg = 'gatttool not found: {}'.format(str(e))
         _LOGGER.error(msg)
         raise BluetoothBackendException(msg)
Example #6
0
    def write_handle(self, handle, value):
        """Write a handle from the device.

        You must be connected to do this.
        """
        if self._peripheral is None:
            raise BluetoothBackendException('not connected to backend')
        return self._peripheral.writeCharacteristic(handle, value, True)
Example #7
0
    def read_handle(self, handle):
        """Read a handle from the device.

        You must be connected to do this.
        """
        if self._peripheral is None:
            raise BluetoothBackendException('not connected to backend')
        return self._peripheral.readCharacteristic(handle)
Example #8
0
    def name(self):
        """Return the name of the sensor."""
        with self._bt_interface.connect(self._mac) as connection:
            name = connection.read_handle(_HANDLE_READ_NAME)  # pylint: disable=no-member

        if not name:
            raise BluetoothBackendException(
                "Could not read data from Mi Flora sensor %s" % self._mac)
        return ''.join(chr(n) for n in name)
Example #9
0
 def connect(self, mac):
     """Connect to a device."""
     from bluepy.btle import Peripheral
     match_result = re.search(r'hci([\d]+)', self.adapter)
     if match_result is None:
         raise BluetoothBackendException(
             'Invalid pattern "{}" for BLuetooth adpater. '
             'Expetected something like "hci0".'.format(self.adapter))
     iface = int(match_result.group(1))
     self._peripheral = Peripheral(mac, iface=iface)
Example #10
0
 def _func_wrapper(*args, **kwargs):
     error_count = 0
     last_error = None
     while error_count < RETRY_LIMIT:
         try:
             return func(*args, **kwargs)
         except BTLEException as exception:
             error_count += 1
             last_error = exception
             time.sleep(RETRY_DELAY)
             _LOGGER.debug('Call to %s failed, try %d of %d', func,
                           error_count, RETRY_LIMIT)
     raise BluetoothBackendException() from last_error
Example #11
0
 def _func_wrapper(*args, **kwargs):
     try:
         return func(*args, **kwargs)
     except IOError as exception:
         raise BluetoothBackendException() from exception
Example #12
0
 def check_backend(self):
     try:
         import bluepy.btle  # noqa: F401
     except ImportError:
         raise BluetoothBackendException('bluepy not found')
Example #13
0
 def write_handle(self, handle, value):
     if self._peripheral is None:
         raise BluetoothBackendException('not connected to backend')
     return self._peripheral.writeCharacteristic(handle, value, True)
Example #14
0
 def read_handle(self, handle):
     if self._peripheral is None:
         raise BluetoothBackendException('not connected to backend')
     return self._peripheral.readCharacteristic(handle)
Example #15
0
 def connect(self, mac):
     """Raise exception when connecting."""
     raise BluetoothBackendException('always raising exceptions')
Example #16
0
 def check_backend(self):
     """Check if the backend is available."""
     try:
         import bluepy.btle  # noqa: F401
     except ImportError:
         raise BluetoothBackendException('bluepy not found')
Example #17
0
 def write_handle(self, handle, value):
     """Write a handle to the device."""
     if not self.is_connected():
         raise BluetoothBackendException('Not connected to device!')
     self._device.char_write_handle(handle, value, True)
     return True
Example #18
0
 def read_handle(self, handle):
     """Read a handle from the device."""
     if not self.is_connected():
         raise BluetoothBackendException('Not connected to device!')
     return self._device.char_read_handle(handle)
Example #19
0
 def read_write(self, _, __):  # pylint: disable=no-self-use
     """Writing always fails."""
     raise BluetoothBackendException('always raising')
Example #20
0
 def read_handle(self, _):
     """Reading always fails."""
     raise BluetoothBackendException('always raising')