def __str__(self):
        """Get a string representing the last sample.

        Return:
            str: A string representing the last sample.
        """
        with lock(self):
            sample = self._last_sample

        if sample is None:
            return self._name + ': Unknown'
        if not sample._data:
            return self._name + ': Unknown'

        result = ''
        if len(sample._data) >= 2:
            result = '%s(%d): Activity is \"%s\", Time is \"%s\"' \
                % (self._name,
                   sample._timestamp,
                   str(self.get_activity(sample)),
                   str(self.get_time(sample))
                   )
        if len(sample._data) == 3:
            result += ', Algorithm is \"%d\"' \
                % (self.get_algorithm(sample))
        return result
Ejemplo n.º 2
0
    def _build_debug_console(self, debug_service):
        """Build a debug console used to read/write debug messages from/to the
        Bluetooth device.

        Args:
            debug_service (Service): The BLE service. Refer to
            `Service <https://ianharvey.github.io/bluepy-doc/service.html>`_
            for more information.

        Returns:
            :class:`blue_st_sdk.debug_console.DebugConsole`: A debug console
            used to read/write debug messages from/to the Bluetooth device.
            None if the device doesn't export the needed characteristics.
        """
        try:
            stdinout = None
            stderr = None
            with lock(self):
                characteristics = debug_service.getCharacteristics()
            for characteristic in characteristics:
                if str(characteristic.uuid) == \
                    str(Debug.DEBUG_STDINOUT_BLUESTSDK_SERVICE_UUID):
                    stdinout = characteristic
                elif str(characteristic.uuid) == \
                    str(Debug.DEBUG_STDERR_BLUESTSDK_SERVICE_UUID):
                    stderr = characteristic
                if stdinout and stderr:
                    return DebugConsole(self, stdinout, stderr)
            return None
        except BTLEException as e:
            self._unexpected_disconnect()
Ejemplo n.º 3
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()
Ejemplo n.º 4
0
    def characteristic_has_other_notifying_features(self, characteristic,
                                                    feature):
        """Check whether a characteristic has other enabled features beyond the
        given one.

        Args:
            characteristic (Characteristic): The BLE characteristic to check.
            Refer to
            `Characteristic <https://ianharvey.github.io/bluepy-doc/characteristic.html>`_
            for more information.
            feature (:class:`blue_st_sdk.feature.Feature`): The given feature.

        Returns:
            True if the characteristic has other enabled features beyond the
            given one, False otherwise.
        """
        with lock(self):
            features = self._get_corresponding_features(
                characteristic.getHandle())
        for feature_entry in features:
            if feature_entry == feature:
                pass
            elif feature_entry.is_notifying():
                return True
        return False
Ejemplo n.º 5
0
    def _build_features_known_uuid(self, characteristic, feature_classes):
        """Build the given features of a BLE characteristic.

        After building the features, add them to the dictionary of the features
        to be updated.

        Args:
            characteristic (Characteristic): The BLE characteristic. Refer to
            `Characteristic <https://ianharvey.github.io/bluepy-doc/characteristic.html>`_
            for more information.
            feature_classes (list): The list of feature-classes to instantiate.
        """
        # Build the features.
        features = []
        for feature_class in feature_classes:
            feature = self._build_feature_from_class(feature_class)
            if feature is not None:
                feature.set_enable(True)
                features.append(feature)
                self._available_features.append(feature)

        # If the features are valid, add an entry for the corresponding
        # characteristic.
        try:
            if features:
                with lock(self):
                    self._update_char_handle_to_features_dict[
                        characteristic.getHandle()] = features
        except BTLEException as e:
            self._unexpected_disconnect()
Ejemplo n.º 6
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)
Ejemplo n.º 7
0
 def _unexpected_disconnect(self):
     """Handle an unexpected disconnection."""
     try:
         # Disconnecting.
         self._update_node_status(NodeStatus.UNREACHABLE)
         with lock(self):
             super(Node, self).disconnect()
         self._update_node_status(NodeStatus.IDLE, True)
     except BTLEException as e:
         pass
Ejemplo n.º 8
0
 def add_logger(self, logger):
     """Add a logger.
     
     Args:
         logger (:class:`blue_st_sdk.feature.FeatureLogger`): Logger to
         be added.
     """
     if logger is not None:
         with lock(self):
             if not logger in self._loggers:
                 self._loggers.append(logger)
Ejemplo n.º 9
0
    def _set_listener(self, listener):
        """Set the listener to the debug console.

        Args:
            listener (:class:`blue_st_sdk.firmware_upgrade.debug_console.DebugConsoleListener`):
            Listener to the debug console.
        """
        with lock(self):
            self._debug_console.remove_listener(self._debug_console_listener)
            self._debug_console.add_listener(listener)
            self._debug_console_listener = listener
Ejemplo n.º 10
0
 def remove_logger(self, logger):
     """Remove a logger.
     
     Args:
         logger (:class:`blue_st_sdk.feature.FeatureLogger`): Logger to
         be removed.
     """
     if logger is not None:
         with lock(self):
             if logger in self._loggers:
                 self._loggers.remove(logger)
Ejemplo n.º 11
0
 def remove_listener(self, listener):
     """Remove a listener.
     
     Args:
         listener (:class:`blue_st_sdk.manager.ManagerListener`): Listener to
         be removed.
     """
     if listener is not None:
         with lock(self):
             if listener in self._listeners:
                 self._listeners.remove(listener)
Ejemplo n.º 12
0
 def add_listener(self, listener):
     """Add a listener.
     
     Args:
         listener (:class:`blue_st_sdk.manager.ManagerListener`): Listener to
         be added.
     """
     if listener is not None:
         with lock(self):
             if not listener in self._listeners:
                 self._listeners.append(listener)
Ejemplo n.º 13
0
    def remove_listener(self, listener):
        """Remove a listener.

        Args:
            listener (:class:`blue_st_sdk.firmware_upgrade.utils.firmware_upgrade.FirmwareUpgradeListener`):
            Listener to be removed.
        """
        if listener is not None:
            with lock(self):
                if listener in self._listeners:
                    self._listeners.remove(listener)
Ejemplo n.º 14
0
 def add_listener(self, listener):
     """Add a listener.
     
     Args:
         listener (:class:`blue_st_sdk.firmware_upgrade.utils.firmware_upgrade.FirmwareUpgradeListener`):
         Listener to be added.
     """
     if listener is not None:
         with lock(self):
             if not listener in self._listeners:
                 self._listeners.append(listener)
Ejemplo n.º 15
0
 def stop(self):
     """Stop the thread."""
     self._stop_called.set()
     while not (self._process_done.isSet() or self._exc):
         pass
     try:
         self._exc = None
         with lock(self):
             self._scanner.stop()
     except BTLEException as e:
         # Save details of the exception raised but don't re-raise, just
         # complete the function.
         import sys
         self._exc = sys.exc_info()
Ejemplo n.º 16
0
    def add_listener(self, listener):
        """Adding a listener.

        Args:
            listener (:class:`blue_st_sdk.debug.DebugListener`): Listener to
            be added.
        """
        if listener is not None:
            with lock(self):
                if not listener in self._listeners:
                    self._listeners.append(listener)
                if self._listeners:
                    self._node.set_notification_status(
                        self._stdinout_characteristic, True)
                    self._node.set_notification_status(
                        self._stderr_characteristic, True)
Ejemplo n.º 17
0
    def remove_listener(self, listener):
        """Remove a listener.

        Args:
            listener (:class:`blue_st_sdk.debug.DebugListener`): Listener to
            be removed.
        """
        if listener is not None:
            with lock(self):
                if listener in self._listeners:
                    self._listeners.remove(listener)
                if not self._listeners:
                    self._node.set_notification_status(
                        self._stdinout_characteristic, False)
                    self._node.set_notification_status(
                        self._stderr_characteristic, False)
Ejemplo n.º 18
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()
Ejemplo n.º 19
0
    def characteristic_can_be_notified(self, characteristic):
        """Check if a characteristics can be notified.

        Args:
            characteristic (Characteristic): The BLE characteristic to check.
            Refer to
            `Characteristic <https://ianharvey.github.io/bluepy-doc/characteristic.html>`_
            for more information.

        Returns:
            bool: True if the characteristic can be notified, False otherwise.
        """
        try:
            if characteristic is not None:
                with lock(self):
                    return "NOTIFY" in characteristic.propertiesToString()
            return False
        except BTLEException as e:
            self._unexpected_disconnect()
Ejemplo n.º 20
0
    def set_notification_status(self, characteristic, status):
        """Ask the node to set the notification status of the given
        characteristic.

        Args:
            characteristic (Characteristic): The BLE characteristic to check.
            Refer to
            `Characteristic <https://ianharvey.github.io/bluepy-doc/characteristic.html>`_
            for more information.
            status (bool): True if the notifications have to be turned on, False
            otherwise.
        """
        try:
            with lock(self):
                self.writeCharacteristic(
                    characteristic.getHandle() + 1, self._NOTIFICATION_ON
                    if status else self._NOTIFICATION_OFF, True)
        except BTLEException as e:
            self._unexpected_disconnect()
Ejemplo n.º 21
0
    def __init__(self, show_warnings=False, *args, **kwargs):
        """Constructor.

        Args:
            show_warnings (bool, optional): If True shows warnings, if any, when
            discovering devices not respecting the BlueSTSDK's advertising
            data format, nothing otherwise.
        """
        try:
            super(_StoppableScanner, self).__init__(*args, **kwargs)
            self._stop_called = threading.Event()
            self._process_done = threading.Event()
            with lock(self):
                self._scanner = Scanner().withDelegate(_ScannerDelegate(show_warnings))
        except BTLEException as e:
            # Save details of the exception raised but don't re-raise, just
            # complete the function.
            import sys
            self._exc = sys.exc_info()
Ejemplo n.º 22
0
    def update(self, timestamp, data, offset, notify_update=False):
        """Update feature's internal data through an atomic operation, and
        notify the registered listeners about the update, if needed.

        This method has to be called by a node whenever it receives new data
        from the feature, not by the application.

        When overriding this method, please remember to update the timestamp and
        the last-updated value, and to acquire the write-lock.

        Args:
            timestamp (int): Package's timestamp.
            data (list): Feature's data.
            offset (int): Offset position to start reading data.
            notify_update (bool, optional): If True all the registered listeners
            are notified about the new data.

        Returns:
            int: The number of bytes read.

        Raises:
            :exc:`blue_st_sdk.utils.blue_st_exceptions.BlueSTInvalidDataException`
            if the data array has not enough data to read.
        """
        # Update the feature's internal data
        sample = None
        with lock(self):
            try:
                extracted_data = self.extract_data(timestamp, data, offset)
            except BlueSTInvalidDataException as e:
                raise e
            sample = self._last_sample = extracted_data.get_sample()
            read_bytes = extracted_data.get_read_bytes()
            self._last_update = datetime.now()
        if notify_update:
            # Notify all the registered listeners about the new data.
            self._notify_update(sample)

        # Log the new data through all the registered loggers.
        self._log_update(data[offset:offset + read_bytes], sample)

        return read_bytes
Ejemplo n.º 23
0
    def __str__(self):
        """Get a string representing the last sample.

        Return:
            str: A string representing the last sample.
        """
        with lock(self):
            sample = self._last_sample

        if sample is None:
            return self._name + ': Unknown'
        if not sample._data:
            return self._name + ': Unknown'

        if len(sample._data) == 1:
            result = '%s(%d): %s %s' \
                % (self._name,
                   sample._timestamp,
                   str(sample._data[0]),
                   self._description[0]._unit)
            return result

        # Check on timestamp (ADPCM Audio and ADPCM Sync samples don't have
        # the timestamp field in order to save bandwidth.)
        if sample._timestamp is not None:
            result = '%s(%d): ( ' % (self._name, sample._timestamp)

            i = 0
            while i < len(sample._data):
                result += '%s: %s %s%s' \
                    % (self._description[i]._name,
                       str(sample._data[i]),
                       self._description[i]._unit,
                       '    ' if i < len(sample._data) - 1 else ' )')
                i += 1
        else:
            # Only for Audio Features.
            result = str(self._name) + " - "
            for i in range(0, len(sample._data) - 1):
                result += str(sample._data[i]) + ", "
            result += str(sample._data[len(sample._data) - 1])
        return result
Ejemplo n.º 24
0
    def disconnect(self):
        """Close the connection to the node.

        Returns:
            bool: True if the disconnection to the node has been successful,
            False otherwise.
        """
        try:
            if not self.is_connected():
                return False

            # Disconnecting.
            self._update_node_status(NodeStatus.DISCONNECTING)
            with lock(self):
                super(Node, self).disconnect()
            self._update_node_status(NodeStatus.IDLE)

            return self._status == NodeStatus.IDLE
        except BTLEException as e:
            self._unexpected_disconnect()
Ejemplo n.º 25
0
    def run(self):
        """Run the thread."""
        self._stop_called.clear()
        self._process_done.clear()
        try:
            with lock(self):
                self._scanner.clear()
                self._exc = None
                self._scanner.start(passive=False)
                while True:
                    #print('.')
                    self._scanner.process(_ScannerDelegate._SCANNING_TIME_PROCESS_s)
                    if self._stop_called.isSet():
                        self._process_done.set()
                        break
 
        except BTLEException as e:
            # Save details of the exception raised but don't re-raise, just
            # complete the function.
            import sys
            self._exc = sys.exc_info()
Ejemplo n.º 26
0
    def __str__(self):
        """Get a string representing the last sample.

        Return:
            str: A string representing the last sample.
        """
        with lock(self):
            sample = self._last_sample

        if sample is None:
            return self._name + ': Unknown'
        if not sample._data:
            return self._name + ': Unknown'

        if len(sample._data) == 1:
            result = '%s(%d): Scene is \"%s\"' \
                % (self._name,
                   sample._timestamp,
                   str(self.get_scene(sample))
                   )
        return result
Ejemplo n.º 27
0
    def __str__(self):
        """Get a string representing the last sample.

        Return:
            str: A string representing the last sample.
        """
        with lock(self):
            sample = self._last_sample

        if sample is None:
            return self._name + ': Unknown'
        if not sample._data:
            return self._name + ': Unknown'

        if len(sample._data) == 1:
            switch = 'ON' if self.get_switch_status(sample) else 'OFF'
            result = '%s(%d): %s' \
                % (self._name,
                   sample._timestamp,
                   switch)
            return result
Ejemplo n.º 28
0
    def wait_for_notifications(self, timeout_s):
        """Block until a notification is received from the peripheral, or until
        the given timeout has elapsed.

        If a notification is received, the
        :meth:`blue_st_sdk.feature.FeatureListener.on_update` method of any
        added listener is called.

        Args:
            timeout_s (float): Time in seconds to wait before returning.

        Returns:
            bool: True if a notification is received before the timeout elapses,
            False otherwise.
        """
        try:
            if self.is_connected():
                with lock(self):
                    return self.waitForNotifications(timeout_s)
            return False
        except BTLEException as e:
            self._unexpected_disconnect()
Ejemplo n.º 29
0
    def _build_features(self, characteristic):
        """Build the exported features of a BLE characteristic.

        After building the features, add them to the dictionary of the features
        to be updated.

        Args:
            characteristic (Characteristic): The BLE characteristic. Refer to
            `Characteristic <https://ianharvey.github.io/bluepy-doc/characteristic.html>`_
            for more information.
        """
        try:
            # Extracting the feature mask from the characteristic's UUID.
            feature_mask = FeatureCharacteristic.extract_feature_mask(
                characteristic.uuid)
            # Looking for the exported features in reverse order to get them in
            # the correct order in case of characteristic that exports multiple
            # features.
            features = []
            mask = 1 << 31
            for i in range(0, 32):
                if (feature_mask & mask) != 0:
                    if mask in self._mask_to_feature_dic:
                        feature = self._mask_to_feature_dic[mask]
                        if feature is not None:
                            feature.set_enable(True)
                            features.append(feature)
                mask = mask >> 1

            # If the features are valid, add an entry for the corresponding
            # characteristic.
            if features:
                with lock(self):
                    self._update_char_handle_to_features_dict[
                        characteristic.getHandle()] = features
        except BTLEException as e:
            self._node._unexpected_disconnect()
Ejemplo n.º 30
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()