Beispiel #1
0
    def write_feature(self, feature, data):
        """Synchronous request to write a feature.

        Args:
            feature (:class:`blue_st_sdk.feature.Feature`): The feature to
            write.
            data (str): The data to be written.

        Raises:
            :exc:`blue_st_sdk.utils.blue_st_exceptions.BlueSTInvalidOperationException`
            is raised if the feature is not enabled or the operation
            required is not supported.
        """
        if not feature.is_enabled():
            raise BlueSTInvalidOperationException(' The "' +
                                                  feature.get_name() +
                                                  '" feature is not enabled.')

        characteristic = feature.get_characteristic()
        if not self.characteristic_can_be_written(characteristic):
            raise BlueSTInvalidOperationException(
                ' The "' + feature.get_name() + '" feature is not writeable.')

        try:
            with lock(self):
                char_handle = characteristic.getHandle()
                self.writeCharacteristic(char_handle, data, True)
        except BTLEException as e:
            self._unexpected_disconnect()
Beispiel #2
0
    def stop_discovery(self):
        """Stop a discovery process.

        To be preceeded by a call to :meth:`start_discovery()`.
        This method can be particularly useful when stopping a discovery process
        from an interactive GUI.

        Returns:
            bool: True if the discovery has been stopped, False if there are no
            running discovery processes.

        Raises:
            :exc:`blue_st_sdk.utils.blue_st_exceptions.BlueSTInvalidOperationException`
            is raised if this method is not run as root.
        """
        try:
            #print('stop_discovery()')
            if self.is_discovering():
                self._scanner_thread.stop()
                self._scanner_thread.join()
                self._notify_discovery_change(False)
                return True
            return False
        except BTLEException as e:
            msg = '\nBluetooth scanning requires root privilege, ' \
                  'so please run the script with \"sudo\".'
            raise BlueSTInvalidOperationException(msg)
Beispiel #3
0
    def discover(self,
                 timeout_s=SCANNING_TIME_DEFAULT_s,
                 asynchronous=False,
                 show_warnings=False):
        """Perform the discovery process.

        This method can be run in synchronous (blocking) or asynchronous
        (non-blocking) way. Default is synchronous.

        The discovery process will last *timeout_s* seconds if provided, a
        default timeout otherwise.

        Please note that when running a discovery process, the already connected
        devices get disconnected (limitation intrinsic to the bluepy library).

        Args:
            timeout_s (int, optional): Time in seconds to wait before stopping
            the discovery process.
            asynchronous (bool, optional): If True the method is run in
            asynchronous way, thus non-blocking the execution of the thread,
            the opposite otherwise.
            show_warnings (bool, optional): If True shows warnings, if any, when
            discovering devices not respecting the BlueSTSDK's advertising
            data format, nothing otherwise.

        Returns:
            bool: True if the synchronous discovery has finished or if the
            asynchronous discovery has started, False if a discovery is already
            running.

        Raises:
            :exc:`blue_st_sdk.utils.blue_st_exceptions.BlueSTInvalidOperationException`
            is raised if this method is not run as root.
        """
        try:
            if not asynchronous:
                # Synchronous version.
                if self.is_discovering():
                    return False
                self._discovered_nodes = []
                self._notify_discovery_change(True)
                with lock(self):
                    self._scanner = \
                        Scanner().withDelegate(_ScannerDelegate(show_warnings))
                    self._scanner.scan(timeout_s)
                self._notify_discovery_change(False)
                return True
            else:
                # Asynchronous version.
                if not self.start_discovery(show_warnings):
                    return False
                threading.Timer(timeout_s, self.stop_discovery).start()
                return True
        except BTLEException as e:
            msg = '\nBluetooth scanning requires root privileges, ' \
                  'so please run the application with \"sudo\".'
            raise BlueSTInvalidOperationException(msg)
Beispiel #4
0
    def read_feature(self, feature):
        """Synchronous request to read a feature.

        Args:
            feature (:class:`blue_st_sdk.feature.Feature`): The feature to read.

        Raises:
            :exc:`blue_st_sdk.utils.blue_st_exceptions.BlueSTInvalidOperationException`
            is raised if the feature is not enabled or the operation
            required is not supported.
            :exc:`blue_st_sdk.utils.blue_st_exceptions.BlueSTInvalidDataException`
            if the data array has not enough data to read.
        """
        if not feature.is_enabled():
            raise BlueSTInvalidOperationException(' The "' +
                                                  feature.get_name() +
                                                  '" feature is not enabled.')

        characteristic = feature.get_characteristic()
        if not self.characteristic_can_be_read(characteristic):
            raise BlueSTInvalidOperationException(' The "' +
                                                  feature.get_name() +
                                                  '" feature is not readable.')

        # Reading data.
        try:
            with lock(self):
                char_handle = characteristic.getHandle()
                data = self.readCharacteristic(char_handle)

            # Calling on-read callback.
            if self._debug_console and \
                Debug.is_debug_characteristic(str(characteristic.uuid)):
                # Calling on-read callback for a debug characteristic.
                self._debug_console.on_update_characteristic(
                    characteristic, data)
            else:
                # Calling on-read callback for the other characteristics.
                self._update_features(char_handle, data, False)
        except BlueSTInvalidDataException as e:
            raise e
        except BTLEException as e:
            self._unexpected_disconnect()
Beispiel #5
0
 def join(self):
     """Join the thread.
     
     Raises:
         :exc:`blue_st_sdk.utils.blue_st_exceptions.BlueSTInvalidOperationException`
         is raised if this method is not run as root.
     """
     super(_StoppableScanner, self).join()
     if self._exc:
         msg = '\nBluetooth scanning requires root privilege, ' \
               'so please run the script with \"sudo\".'
         raise BlueSTInvalidOperationException(msg)
Beispiel #6
0
    def reset_discovery(self):
        """Reset the discovery process.

        Stop the discovery process and remove all the already discovered nodes.
        Node already bounded with the device will be kept in the list.

        Raises:
            :exc:`blue_st_sdk.utils.blue_st_exceptions.BlueSTInvalidOperationException`
            is raised if this method is not run as root.
        """
        try:
            if self.is_discovering():
                self.stop_discovery()
            self.remove_nodes()
        except BTLEException as e:
            msg = '\nBluetooth scanning requires root privilege, ' \
                  'so please run the script with \"sudo\".'
            raise BlueSTInvalidOperationException(msg)
Beispiel #7
0
    def start_discovery(self, show_warnings=False):
        """Start the discovery process.

        This is an asynchronous (non-blocking) method.

        The discovery process will last indefinitely, until stopped by a call to
        :meth:`stop_discovery()`.
        This method can be particularly useful when starting a discovery process
        from an interactive GUI.

        Please note that when running a discovery process, the already connected
        devices get disconnected (limitation intrinsic to the bluepy library).

        Args:
            show_warnings (bool, optional): If True shows warnings, if any, when
            discovering devices not respecting the BlueSTSDK's advertising
            data format, nothing otherwise.

        Returns:
            bool: True if the discovery has started, False if a discovery is
            already running.

        Raises:
            :exc:`blue_st_sdk.utils.blue_st_exceptions.BlueSTInvalidOperationException`
            is raised if this method is not run as root.
        """
        try:
            #print('start_discovery()')
            if self.is_discovering():
                return False
            self._discovered_nodes = []
            self._notify_discovery_change(True)
            self._scanner_thread = _StoppableScanner(show_warnings)
            self._scanner_thread.start()
            return True
        except BTLEException as e:
            msg = '\nBluetooth scanning requires root privilege, ' \
                  'so please run the script with \"sudo\".'
            raise BlueSTInvalidOperationException(msg)
Beispiel #8
0
    def __init__(self, scan_entry):
        """Constructor.

        Args:
            scan_entry (ScanEntry): BLE device. It contains device information
                and advertising data. Refer to
                `ScanEntry <https://ianharvey.github.io/bluepy-doc/scanentry.html>`_
                for more information.

        Raises:
            :exc:`blue_st_sdk.utils.blue_st_exceptions.BlueSTInvalidAdvertisingDataException`
            is raised if the advertising data is not well formed.
            :exc:`blue_st_sdk.utils.blue_st_exceptions.BlueSTInvalidOperationException`
            is raised if the operation requested is not supported.
        """
        # Creating an un-connected "Peripheral" object.
        # It is needed to call the "connect()" method on this object (passing a
        # device address) before it will be usable.
        try:
            with lock(self):
                Peripheral.__init__(self)
        except BTLEException as e:
            raise BlueSTInvalidOperationException(
                'Bluetooth invalid operation.')

        self._friendly_name = None
        """Friendly name."""

        self._last_rssi_update = None
        """Last update to the Received Signal Strength Indication."""

        self._status = NodeStatus.INIT
        """Status."""

        self._thread_pool = ThreadPoolExecutor(Node._NUMBER_OF_THREADS)
        """Pool of thread used to notify the listeners."""

        self._listeners = []
        """List of listeners to the node changes.
        It is a thread safe list, so a listener can subscribe itself through a
        callback."""

        self._available_features = []
        """List of all the available features as claimed by the advertising data.
        (No duplicates.)"""

        self._mask_to_feature_dic = {}
        """Mask to feature dictionary: there is an entry for each one-bit-high
        32-bit mask."""
        """UUID to list of external features dictionary: there is an entry for
        each list of exported external features.
        Note: A UUID may export more than one feature.
        Note: BlueSTSDK_Android: mExternalCharFeatures."""
        self._external_uuid_to_features_dic = UUIDToFeatureMap()

        self._update_char_handle_to_features_dict = {}
        """Characteristic's handle to list of features dictionary: it tells
        which features to update when new data from a characteristic are
        received.
        Note: A UUID may export more than one feature.
        Note: The same feature may be added to different list of features in
              case more characteristics have the same corresponding bit set to
              high.
        Note: BlueSTSDK_Android: mCharFeatureMap."""

        self._char_handle_to_characteristic_dict = {}
        """Characteristic's handle to characteristic dictionary."""

        self._unwrap_timestamp = UnwrapTimestamp()
        """Unwrap timestamp reference."""

        #self._characteristic_write_queue = Queue()
        """Queue of write jobs."""

        # Debug console used to read/write debug messages from/to the Bluetooth
        # device. None if the device doesn't export the debug service.
        self._debug_console = None

        # Advertising data.
        try:
            self._device = scan_entry
            """BLE device.
            Python's "ScanEntry" object, equivalent to Android's "BluetoothDevice"
            object."""

            with lock(self):
                self._advertising_data = BlueSTAdvertisingDataParser.parse(
                    scan_entry.getScanData())
                """Advertising data."""

            self._rssi = scan_entry.rssi
            """Received Signal Strength Indication."""
        except BlueSTInvalidAdvertisingDataException as e:
            raise e
        except BTLEException as e:
            raise BlueSTInvalidOperationException(
                'Bluetooth invalid operation.')

        # Updating node.
        self._update_rssi(self._rssi)
        self._update_node_status(NodeStatus.IDLE)

        # Building available features.
        self._build_available_features()