Beispiel #1
0
    async def stop_notify(
        self, char_specifier: Union[BleakGATTCharacteristic, int, str,
                                    uuid.UUID]
    ) -> None:
        """Deactivate notification/indication on a specified characteristic.

        Args:
            char_specifier (BleakGATTCharacteristic, int, str or UUID): The characteristic to deactivate
                notification/indication on, specified by either integer handle, UUID or
                directly by the BleakGATTCharacteristic object representing it.

        """
        if not isinstance(char_specifier, BleakGATTCharacteristic):
            characteristic = self.services.get_characteristic(char_specifier)
        else:
            characteristic = char_specifier
        if not characteristic:
            raise BleakError(
                "Characteristic {} not found!".format(char_specifier))

        status = await characteristic.obj.write_client_characteristic_configuration_descriptor_async(
            GattClientCharacteristicConfigurationDescriptorValue.NONE)

        if status != GattCommunicationStatus.SUCCESS:
            raise BleakError("Could not stop notify on {0}: {1}".format(
                characteristic.uuid, _communication_statues.get(status, "")))
        else:
            event_handler_token = self._notification_callbacks.pop(
                characteristic.handle)
            characteristic.obj.remove_value_changed(event_handler_token)
Beispiel #2
0
    async def stop_notify(
        self, char_specifier: Union[BleakGATTCharacteristic, int, str,
                                    uuid.UUID]
    ) -> None:
        """Deactivate notification/indication on a specified characteristic.

        Args:
            char_specifier (BleakGATTCharacteristic, int, str or UUID): The characteristic to deactivate
                notification/indication on, specified by either integer handle, UUID or
                directly by the BleakGATTCharacteristic object representing it.


        """
        manager = self._central_manager_delegate

        if not isinstance(char_specifier, BleakGATTCharacteristic):
            characteristic = self.services.get_characteristic(char_specifier)
        else:
            characteristic = char_specifier
        if not characteristic:
            raise BleakError(
                "Characteristic {} not found!".format(char_specifier))

        success = await manager.connected_peripheral_delegate.stopNotify_(
            characteristic.obj)
        if not success:
            raise BleakError("Could not stop notify on {0}: {1}".format(
                characteristic.uuid, success))
Beispiel #3
0
    async def read_gatt_descriptor(self, handle: int, **kwargs) -> bytearray:
        """Perform read operation on the specified GATT descriptor.

        Args:
            handle (int): The handle of the descriptor to read from.

        Returns:
            (bytearray) The read data.

        """
        if not self.is_connected:
            raise BleakError("Not connected")

        descriptor = self.services.get_descriptor(handle)
        if not descriptor:
            raise BleakError(
                "Descriptor with handle {0} was not found!".format(handle))

        reply = await self._bus.call(
            Message(
                destination=defs.BLUEZ_SERVICE,
                path=descriptor.path,
                interface=defs.GATT_DESCRIPTOR_INTERFACE,
                member="ReadValue",
                signature="a{sv}",
                body=[{}],
            ))
        assert_reply(reply)
        value = bytearray(reply.body[0])

        logger.debug("Read Descriptor {0} | {1}: {2}".format(
            handle, descriptor.path, value))
        return value
Beispiel #4
0
    async def write_gatt_descriptor(self, handle: int, data: bytearray) -> None:
        """Perform a write operation on the specified GATT descriptor.

        Args:
            handle (int): The handle of the descriptor to read from.
            data (bytes or bytearray): The data to send.

        """
        descriptor = self.services.get_descriptor(handle)
        if not descriptor:
            raise BleakError("Descriptor {0} was not found!".format(handle))

        writer = DataWriter()
        writer.WriteBytes(Array[Byte](data))
        write_result = await wrap_IAsyncOperation(
            IAsyncOperation[GattWriteResult](
                descriptor.obj.WriteValueAsync(writer.DetachBuffer())
            ),
            return_type=GattWriteResult,
            loop=self.loop,
        )
        if write_result.Status == GattCommunicationStatus.Success:
            logger.debug("Write Descriptor {0} : {1}".format(handle, data))
        else:
            raise BleakError(
                "Could not write value {0} to descriptor {1}: {2}".format(
                    data, descriptor.uuid, write_result.Status
                )
            )
Beispiel #5
0
    async def write_gatt_char(self,
                              _uuid: str,
                              data: bytearray,
                              response: bool = False) -> None:
        """Perform a write operation of the specified GATT characteristic.

        Args:
            _uuid (str or UUID): The uuid of the characteristics to write to.
            data (bytes or bytearray): The data to send.
            response (bool): If write-with-response operation should be done. Defaults to `False`.

        """
        characteristic = self.services.get_characteristic(str(_uuid))
        if not characteristic:
            raise BleakError("Characteristic {0} was not found!".format(_uuid))

        writer = DataWriter()
        writer.WriteBytes(Array[Byte](data))
        response = (GattWriteOption.WriteWithResponse
                    if response else GattWriteOption.WriteWithoutResponse)
        write_result = await wrap_IAsyncOperation(
            IAsyncOperation[GattWriteResult](
                characteristic.obj.WriteValueWithResultAsync(
                    writer.DetachBuffer(), response)),
            return_type=GattWriteResult,
            loop=self.loop,
        )
        if write_result.Status == GattCommunicationStatus.Success:
            logger.debug("Write Characteristic {0} : {1}".format(_uuid, data))
        else:
            raise BleakError(
                "Could not write value {0} to characteristic {1}: {2}".format(
                    data, characteristic.uuid, write_result.Status))
Beispiel #6
0
    async def write_gatt_descriptor(
            self, handle: int, data: Union[bytes, bytearray,
                                           memoryview]) -> None:
        """Perform a write operation on the specified GATT descriptor.

        Args:
            handle (int): The handle of the descriptor to read from.
            data (bytes or bytearray): The data to send.

        """
        if not self.is_connected:
            raise BleakError("Not connected")

        descriptor = self.services.get_descriptor(handle)
        if not descriptor:
            raise BleakError(
                "Descriptor with handle {0} was not found!".format(handle))

        reply = await self._bus.call(
            Message(
                destination=defs.BLUEZ_SERVICE,
                path=descriptor.path,
                interface=defs.GATT_DESCRIPTOR_INTERFACE,
                member="WriteValue",
                signature="aya{sv}",
                body=[bytes(data), {
                    "type": Variant("s", "command")
                }],
            ))
        assert_reply(reply)

        logger.debug("Write Descriptor {0} | {1}: {2}".format(
            handle, descriptor.path, data))
Beispiel #7
0
    async def write_gatt_char(
        self,
        _uuid: str,
        data: bytearray,
        response: bool = False
    ) -> Any:
        """Perform a write operation of the specified characteristic.

        Args:
            _uuid (str or UUID): The uuid of the characteristics to start notification on.
            data (bytes or bytearray): The data to send.
            response (bool): If write response is desired.

        """
        characteristic = self.characteristics.get(str(_uuid))
        if not characteristic:
            raise BleakError("Characteristic {0} was not found!".format(_uuid))

        write_results = await wrap_Task(
            self._bridge.WriteCharacteristicValueAsync(
                characteristic, data, response
            ),
            loop=self.loop,
        )
        if write_results == GattCommunicationStatus.Success:
            logger.debug("Write Characteristic {0} : {1}".format(_uuid, data))
        else:
            raise BleakError(
                "Could not write value {0} to characteristic {1}: {2}",
                data,
                characteristic.Uuid.ToString(),
                write_results,
            )
Beispiel #8
0
    async def start_notify(self, _uuid: Union[str, uuid.UUID],
                           callback: Callable[[str, Any],
                                              Any], **kwargs) -> None:
        """Activate notifications/indications on a characteristic.

        Callbacks must accept two inputs. The first will be a uuid string
        object and the second will be a bytearray.

        .. code-block:: python

            def callback(sender, data):
                print(f"{sender}: {data}")
            client.start_notify(char_uuid, callback)

        Args:
            _uuid (str or UUID): The uuid of the characteristics to start notification/indication on.
            callback (function): The function to be called on notification.

        """
        characteristic = self.services.get_characteristic(str(_uuid))
        if not characteristic:
            raise BleakError("Characteristic {0} was not found!".format(_uuid))

        if self._notification_callbacks.get(str(_uuid)):
            await self.stop_notify(_uuid)

        status = await self._start_notify(characteristic.obj, callback)

        if status != GattCommunicationStatus.Success:
            raise BleakError("Could not start notify on {0}: {1}".format(
                characteristic.uuid, status))
Beispiel #9
0
    async def write_gatt_descriptor(self, handle: int, data: bytearray) -> None:
        """Perform a write operation on the specified GATT descriptor.

        Args:
            handle (int): The handle of the descriptor to read from.
            data (bytes or bytearray): The data to send.

        """
        manager = self._device_info.manager().delegate()

        descriptor = self.services.get_descriptor(handle)
        if not descriptor:
            raise BleakError("Descriptor {} was not found!".format(handle))

        value = NSData.alloc().initWithBytes_length_(data, len(data))
        success = await manager.connected_peripheral_delegate.writeDescriptor_value_(
            descriptor.obj, value
        )
        if success:
            logger.debug("Write Descriptor {0} : {1}".format(handle, data))
        else:
            raise BleakError(
                "Could not write value {0} to descriptor {1}: {2}".format(
                    data, descriptor.uuid, success
                )
            )
Beispiel #10
0
    async def read_gatt_char(self, _uuid: str) -> bytearray:
        """Perform read operation on the specified characteristic.

        Args:
            _uuid (str or UUID): The uuid of the characteristics to start notification on.

        Returns:
            (bytearray) The read data.

        """
        characteristic = self.characteristics.get(str(_uuid))
        if not characteristic:
            raise BleakError("Characteristic {0} was not found!".format(_uuid))

        read_results = await wrap_Task(
            self._bridge.ReadCharacteristicValueAsync(characteristic), loop=self.loop
        )
        status, value = read_results.Item1, bytearray(read_results.Item2)
        if status == GattCommunicationStatus.Success:
            logger.debug("Read Characteristic {0} : {1}".format(_uuid, value))
        else:
            raise BleakError(
                "Could not read characteristic value for {0}: {1}",
                characteristic.Uuid.ToString(),
                status,
            )

        return value
Beispiel #11
0
    async def start_notify(self, _uuid: str, callback: Callable[[str, Any],
                                                                Any],
                           **kwargs) -> None:
        """Activate notifications/indications on a characteristic.

        Callbacks must accept two inputs. The first will be a uuid string
        object and the second will be a bytearray.

        .. code-block:: python

            def callback(sender, data):
                print(f"{sender}: {data}")
            client.start_notify(char_uuid, callback)

        Args:
            _uuid (str or UUID): The uuid of the characteristics to start notification/indication on.
            callback (function): The function to be called on notification.

        """
        _uuid = await self.get_appropriate_uuid(_uuid)
        characteristic = self.services.get_characteristic(_uuid)
        if not characteristic:
            raise BleakError("Characteristic {} not found!".format(_uuid))

        success = await cbapp.central_manager_delegate.connected_peripheral_delegate.startNotify_cb_(
            characteristic.obj, callback)
        if not success:
            raise BleakError("Could not start notify on {0}: {1}".format(
                characteristic.uuid, success))
Beispiel #12
0
 def _result_state_unthreadsafe(self, failure_str, source, data):
     logger.debug(
         f"Java state transfer {source} error={failure_str} data={data}")
     self.states[source] = (failure_str, *data)
     future = self.futures.get(source, None)
     if future is not None and not future.done():
         if failure_str is None:
             future.set_result(data)
         else:
             future.set_exception(BleakError(source, failure_str, *data))
     else:
         if failure_str is not None:
             # an error happened with nothing waiting for it
             exception = BleakError(source, failure_str, *data)
             namedfutures = [
                 namedfuture for namedfuture in self.futures.items()
                 if not namedfuture[1].done()
             ]
             if len(namedfutures):
                 # send it on existing requests
                 for name, future in namedfutures:
                     warnings.warn(
                         f"Redirecting error without home to {name}")
                     future.set_exception(exception)
             else:
                 # send it on the event thread
                 raise exception
Beispiel #13
0
def _ensure_success(result: Any, attr: Optional[str], fail_msg: str) -> Any:
    """
    Ensures that *status* is ``GattCommunicationStatus.SUCCESS``, otherwise
    raises ``BleakError``.

    Args:
        result: The result returned by a WinRT API method.
        attr: The name of the attribute containing the result.
        fail_msg: A message to include in the exception.
    """
    status = result.status if hasattr(result, "status") else result

    if status == GattCommunicationStatus.SUCCESS:
        return None if attr is None else getattr(result, attr)

    if status == GattCommunicationStatus.PROTOCOL_ERROR:
        err = PROTOCOL_ERROR_CODES.get(result.protocol_error, "Unknown")
        raise BleakError(
            f"{fail_msg}: Protocol Error 0x{result.protocol_error:02X}: {err}")

    if status == GattCommunicationStatus.ACCESS_DENIED:
        raise BleakError(f"{fail_msg}: Access Denied")

    if status == GattCommunicationStatus.UNREACHABLE:
        raise BleakError(f"{fail_msg}: Unreachable")

    raise BleakError(
        f"{fail_msg}: Unexpected status code 0x{result.status:02X}")
Beispiel #14
0
def get_device_object_path(hci_device, address):
    """Get object path for a Bluetooth device.

    Service         org.bluez
    Interface       org.bluez.Device1
    Object path     [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX

    Args:
        hci_device (str): Which bluetooth adapter to connect with.
        address (str): The Bluetooth address of the bluetooth device.

    Returns:
        String representation of device object path on format
        `/org/bluez/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX`.

    """
    if not validate_mac_address(address):
        raise BleakError("{0} is not a valid Bluetooth address.".format(address))

    if not validate_hci_device(hci_device):
        raise BleakError("{0} is not a valid HCI device.".format(hci_device))

    # TODO: Join using urljoin? Or pathlib?
    return "/org/bluez/{0}/dev_{1}".format(
        hci_device, "_".join(address.split(":")).upper()
    )
Beispiel #15
0
    async def write_gatt_char(self,
                              _uuid: str,
                              data: bytearray,
                              response: bool = False) -> None:
        """Perform a write operation of the specified GATT characteristic.

        Args:
            _uuid (str or UUID): The uuid of the characteristics to write to.
            data (bytes or bytearray): The data to send.
            response (bool): If write-with-response operation should be done. Defaults to `False`.

        """
        _uuid = await self.get_appropriate_uuid(_uuid)
        characteristic = self.services.get_characteristic(_uuid)
        if not characteristic:
            raise BleakError("Characteristic {} was not found!".format(_uuid))

        value = NSData.alloc().initWithBytes_length_(data, len(data))
        success = await cbapp.central_manager_delegate.connected_peripheral_delegate.writeCharacteristic_value_(
            characteristic.obj, value)
        if success:
            logger.debug("Write Characteristic {0} : {1}".format(_uuid, data))
        else:
            raise BleakError(
                "Could not write value {0} to characteristic {1}: {2}".format(
                    data, characteristic.uuid, success))
Beispiel #16
0
    async def stop_notify(
        self,
        char_specifier: Union[BleakGATTCharacteristicP4Android, int, str,
                              uuid.UUID],
    ) -> None:
        """Deactivate notification/indication on a specified characteristic.

        Args:
            char_specifier (BleakGATTCharacteristicP4Android, int, str or UUID): The characteristic to deactivate
                notification/indication on, specified by either integer handle, UUID or
                directly by the BleakGATTCharacteristicP4Android object representing it.

        """
        if not isinstance(char_specifier, BleakGATTCharacteristicP4Android):
            characteristic = self.services.get_characteristic(char_specifier)
        else:
            characteristic = char_specifier
        if not characteristic:
            raise BleakError(f"Characteristic {char_specifier} not found!")

        await self.write_gatt_descriptor(
            characteristic.notification_descriptor,
            defs.BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE,
        )

        if not self.__gatt.setCharacteristicNotification(
                characteristic.obj, False):
            raise BleakError(
                f"Failed to disable notification for characteristic {characteristic.uuid}"
            )
        del self._subscriptions[characteristic.handle]
Beispiel #17
0
    async def stop_notify(
        self,
        char_specifier: Union[BleakGATTCharacteristicBlueZDBus, int, str,
                              UUID],
    ) -> None:
        """Deactivate notification/indication on a specified characteristic.

        Args:
            char_specifier (BleakGATTCharacteristicBlueZDBus, int, str or UUID): The characteristic to deactivate
                notification/indication on, specified by either integer handle, UUID or
                directly by the BleakGATTCharacteristicBlueZDBus object representing it.

        """
        if not self.is_connected:
            raise BleakError("Not connected")

        if not isinstance(char_specifier, BleakGATTCharacteristicBlueZDBus):
            characteristic = self.services.get_characteristic(char_specifier)
        else:
            characteristic = char_specifier
        if not characteristic:
            raise BleakError(
                "Characteristic {} not found!".format(char_specifier))

        reply = await self._bus.call(
            Message(
                destination=defs.BLUEZ_SERVICE,
                path=characteristic.path,
                interface=defs.GATT_CHARACTERISTIC_INTERFACE,
                member="StopNotify",
            ))
        assert_reply(reply)

        self._notification_callbacks.pop(characteristic.path, None)
Beispiel #18
0
    async def stop_notify(self, _uuid: Union[str, uuid.UUID]) -> None:
        """Deactivate notification/indication on a specified characteristic.

        Args:
            _uuid: The characteristic to stop notifying/indicating on.

        """
        characteristic = self.services.get_characteristic(str(_uuid))
        if not characteristic:
            raise BleakError("Characteristic {0} was not found!".format(_uuid))

        status = await wrap_IAsyncOperation(
            IAsyncOperation[GattCommunicationStatus](
                characteristic.obj.
                WriteClientCharacteristicConfigurationDescriptorAsync(
                    getattr(
                        GattClientCharacteristicConfigurationDescriptorValue,
                        "None"))),
            return_type=GattCommunicationStatus,
            loop=self.loop,
        )

        if status != GattCommunicationStatus.Success:
            raise BleakError("Could not stop notify on {0}: {1}".format(
                characteristic.uuid, status))
        else:
            callback = self._callbacks.pop(characteristic.uuid)
            self._bridge.RemoveValueChangedCallback(characteristic.obj,
                                                    callback)
Beispiel #19
0
    async def read_gatt_char(
        self,
        char_specifier: Union[BleakGATTCharacteristic, int, str, uuid.UUID],
        **kwargs,
    ) -> bytearray:
        """Perform read operation on the specified GATT characteristic.

        Args:
            char_specifier (BleakGATTCharacteristic, int, str or UUID): The characteristic to read from,
                specified by either integer handle, UUID or directly by the
                BleakGATTCharacteristic object representing it.

        Keyword Args:
            use_cached (bool): ``False`` forces Windows to read the value from the
                device again and not use its own cached value. Defaults to ``False``.

        Returns:
            (bytearray) The read data.

        """

        use_cached = kwargs.get("use_cached", False)

        if not isinstance(char_specifier, BleakGATTCharacteristic):
            characteristic = self.services.get_characteristic(char_specifier)
        else:
            characteristic = char_specifier
        if not characteristic:
            raise BleakError(
                "Characteristic {0} was not found!".format(char_specifier))

        read_result = await wrap_IAsyncOperation(
            IAsyncOperation[GattReadResult](characteristic.obj.ReadValueAsync(
                BluetoothCacheMode.
                Cached if use_cached else BluetoothCacheMode.Uncached)),
            return_type=GattReadResult,
        )
        if read_result.Status == GattCommunicationStatus.Success:
            with BleakDataReader(read_result.Value) as reader:
                value = bytearray(reader.read())
            logger.debug("Read Characteristic {0} : {1}".format(
                characteristic.uuid, value))
        else:
            if read_result.Status == GattCommunicationStatus.ProtocolError:
                raise BleakDotNetTaskError(
                    "Could not get GATT characteristics for {0}: {1} (Error: 0x{2:02X}: {3})"
                    .format(
                        characteristic.uuid,
                        _communication_statues.get(read_result.Status, ""),
                        read_result.ProtocolError,
                        CONTROLLER_ERROR_CODES.get(read_result.ProtocolError,
                                                   "Unknown"),
                    ))
            else:
                raise BleakError(
                    "Could not read characteristic value for {0}: {1}".format(
                        characteristic.uuid,
                        _communication_statues.get(read_result.Status, ""),
                    ))
        return value
Beispiel #20
0
    async def write_gatt_char(
        self,
        char_specifier: Union[BleakGATTCharacteristicP4Android, int, str,
                              uuid.UUID],
        data: bytearray,
        response: bool = False,
    ) -> None:
        """Perform a write operation on the specified GATT characteristic.

        Args:
            char_specifier (BleakGATTCharacteristicP4Android, int, str or UUID): The characteristic to write
                to, specified by either integer handle, UUID or directly by the
                BleakGATTCharacteristicP4Android object representing it.
            data (bytes or bytearray): The data to send.
            response (bool): If write-with-response operation should be done. Defaults to `False`.

        """
        if not isinstance(char_specifier, BleakGATTCharacteristicP4Android):
            characteristic = self.services.get_characteristic(char_specifier)
        else:
            characteristic = char_specifier

        if not characteristic:
            raise BleakError(f"Characteristic {char_specifier} was not found!")

        if ("write" not in characteristic.properties
                and "write-without-response" not in characteristic.properties):
            raise BleakError(
                f"Characteristic {str(characteristic.uuid)} does not support write operations!"
            )
        if not response and "write-without-response" not in characteristic.properties:
            response = True
            # Force response here, since the device only supports that.
        if (response and "write" not in characteristic.properties
                and "write-without-response" in characteristic.properties):
            response = False
            logger.warning(
                "Characteristic %s does not support Write with response. Trying without..."
                % str(characteristic.uuid))

        if response:
            characteristic.obj.setWriteType(
                defs.BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT)
        else:
            characteristic.obj.setWriteType(
                defs.BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE)

        characteristic.obj.setValue(data)

        await self.__callbacks.perform_and_wait(
            dispatchApi=self.__gatt.writeCharacteristic,
            dispatchParams=(characteristic.obj, ),
            resultApi=("onCharacteristicWrite", characteristic.handle),
        )

        logger.debug(
            f"Write Characteristic {characteristic.uuid} | {characteristic.handle}: {data}"
        )
Beispiel #21
0
    async def write_gatt_char(
        self,
        char_specifier: Union[BleakGATTCharacteristic, int, str, uuid.UUID],
        data: bytearray,
        response: bool = False,
    ) -> None:
        """Perform a write operation of the specified GATT characteristic.

        Args:
            char_specifier (BleakGATTCharacteristic, int, str or UUID): The characteristic to write
                to, specified by either integer handle, UUID or directly by the
                BleakGATTCharacteristic object representing it.
            data (bytes or bytearray): The data to send.
            response (bool): If write-with-response operation should be done. Defaults to `False`.

        """
        if not isinstance(char_specifier, BleakGATTCharacteristic):
            characteristic = self.services.get_characteristic(char_specifier)
        else:
            characteristic = char_specifier
        if not characteristic:
            raise BleakError(
                "Characteristic {} was not found!".format(char_specifier))

        with BleakDataWriter(data) as writer:
            response = (GattWriteOption.WriteWithResponse
                        if response else GattWriteOption.WriteWithoutResponse)
            write_result = await wrap_IAsyncOperation(
                IAsyncOperation[GattWriteResult](
                    characteristic.obj.WriteValueWithResultAsync(
                        writer.detach_buffer(), response)),
                return_type=GattWriteResult,
            )

        if write_result.Status == GattCommunicationStatus.Success:
            logger.debug("Write Characteristic {0} : {1}".format(
                characteristic.uuid, data))
        else:
            if write_result.Status == GattCommunicationStatus.ProtocolError:
                raise BleakError(
                    "Could not write value {0} to characteristic {1}: {2} (Error: 0x{3:02X}: {4})"
                    .format(
                        data,
                        characteristic.uuid,
                        _communication_statues.get(write_result.Status, ""),
                        write_result.ProtocolError,
                        CONTROLLER_ERROR_CODES.get(write_result.ProtocolError,
                                                   "Unknown"),
                    ))
            else:
                raise BleakError(
                    "Could not write value {0} to characteristic {1}: {2}".
                    format(
                        data,
                        characteristic.uuid,
                        _communication_statues.get(write_result.Status, ""),
                    ))
Beispiel #22
0
    async def connect(self) -> bool:
        """Connect to the specified GATT server.

        Returns:
            Boolean representing connection status.

        """

        # A Discover must have been run before connecting to any devices. Do a quick one here
        # to ensure that it has been done.
        await discover(timeout=0.1, loop=self.loop)

        # Create system bus
        self._bus = await txdbus_connect(
            reactor, busAddress="system").asFuture(self.loop)
        # TODO: Handle path errors from txdbus/dbus
        self._device_path = get_device_object_path(self.device, self.address)

        def _services_resolved_callback(message):
            iface, changed, invalidated = message.body
            is_resolved = defs.DEVICE_INTERFACE and changed.get(
                "ServicesResolved", False)
            if iface == is_resolved:
                logger.info("Services resolved.")
                self.services_resolved = True

        rule_id = await signals.listen_properties_changed(
            self._bus, self.loop, _services_resolved_callback)

        logger.debug("Connecting to BLE device @ {0} with {1}".format(
            self.address, self.device))
        try:
            await self._bus.callRemote(
                self._device_path,
                "Connect",
                interface="org.bluez.Device1",
                destination="org.bluez",
            ).asFuture(self.loop)
        except RemoteError as e:
            raise BleakError(str(e))

        if await self.is_connected():
            logger.debug("Connection successful.")
        else:
            raise BleakError("Connection to {0} was not successful!".format(
                self.address))

        # Get all services. This means making the actual connection.
        await self.get_services()
        properties = await self._get_device_properties()
        if not properties.get("Connected"):
            raise BleakError("Connection failed!")

        await self._bus.delMatch(rule_id).asFuture(self.loop)
        self._rules["PropChanged"] = await signals.listen_properties_changed(
            self._bus, self.loop, self._properties_changed_callback)
        return True
Beispiel #23
0
    async def pair(self, protection_level: int = None, **kwargs) -> bool:
        """Attempts to pair with the device.

        Keyword Args:
            protection_level:
                    Windows.Devices.Enumeration.DevicePairingProtectionLevel
                        1: None - Pair the device using no levels of protection.
                        2: Encryption - Pair the device using encryption.
                        3: EncryptionAndAuthentication - Pair the device using
                           encryption and authentication. (This will not work in Bleak...)

        Returns:
            Boolean regarding success of pairing.

        """

        if (self._requester.device_information.pairing.can_pair
                and not self._requester.device_information.pairing.is_paired):

            # Currently only supporting Just Works solutions...
            ceremony = DevicePairingKinds.CONFIRM_ONLY
            custom_pairing = self._requester.device_information.pairing.custom

            def handler(sender, args):
                args.accept()

            pairing_requested_token = custom_pairing.add_pairing_requested(
                handler)
            try:
                if protection_level:
                    pairing_result = await custom_pairing.pair_async(
                        ceremony, protection_level)
                else:
                    pairing_result = await custom_pairing.pair_async(ceremony)

            except Exception as e:
                raise BleakError("Failure trying to pair with device!") from e
            finally:
                custom_pairing.remove_pairing_requested(
                    pairing_requested_token)

            if pairing_result.status not in (
                    DevicePairingResultStatus.PAIRED,
                    DevicePairingResultStatus.ALREADY_PAIRED,
            ):
                raise BleakError("Could not pair with device: {0}: {1}".format(
                    pairing_result.status,
                    _pairing_statuses.get(pairing_result.status),
                ))
            else:
                logger.info(
                    "Paired to device with protection level {0}.".format(
                        pairing_result.protection_level_used))
                return True
        else:
            return self._requester.device_information.pairing.is_paired
Beispiel #24
0
    async def read_gatt_char(self,
                             char_specifier: Union[BleakGATTCharacteristic,
                                                   int, str, uuid.UUID],
                             **kwargs) -> bytearray:
        """Perform read operation on the specified GATT characteristic.

        Args:
            char_specifier (BleakGATTCharacteristic, int, str or UUID): The characteristic to read from,
                specified by either integer handle, UUID or directly by the
                BleakGATTCharacteristic object representing it.

        Keyword Args:
            use_cached (bool): ``False`` forces Windows to read the value from the
                device again and not use its own cached value. Defaults to ``False``.

        Returns:
            (bytearray) The read data.

        """
        use_cached = kwargs.get("use_cached", False)

        if not isinstance(char_specifier, BleakGATTCharacteristic):
            characteristic = self.services.get_characteristic(char_specifier)
        else:
            characteristic = char_specifier
        if not characteristic:
            raise BleakError(
                "Characteristic {0} was not found!".format(char_specifier))

        read_result = await characteristic.obj.read_value_async(
            BluetoothCacheMode.CACHED if use_cached else BluetoothCacheMode.
            UNCACHED)

        if read_result.status == GattCommunicationStatus.SUCCESS:
            value = bytearray(
                CryptographicBuffer.copy_to_byte_array(read_result.value))
            logger.debug("Read Characteristic {0} : {1}".format(
                characteristic.uuid, value))
        else:
            if read_result.status == GattCommunicationStatus.PROTOCOL_ERROR:
                raise BleakDotNetTaskError(
                    "Could not get GATT characteristics for {0}: {1} (Error: 0x{2:02X}: {3})"
                    .format(
                        characteristic.uuid,
                        _communication_statues.get(read_result.status, ""),
                        read_result.protocol_error,
                        CONTROLLER_ERROR_CODES.get(read_result.protocol_error,
                                                   "Unknown"),
                    ))
            else:
                raise BleakError(
                    "Could not read characteristic value for {0}: {1}".format(
                        characteristic.uuid,
                        _communication_statues.get(read_result.status, ""),
                    ))
        return value
Beispiel #25
0
    async def read_gatt_char(self,
                             char_specifier: Union[BleakGATTCharacteristic,
                                                   int, str, uuid.UUID],
                             use_cached=False,
                             **kwargs) -> bytearray:
        """Perform read operation on the specified GATT characteristic.

        Args:
            char_specifier (BleakGATTCharacteristic, int, str or UUID): The characteristic to read from,
                specified by either integer handle, UUID or directly by the
                BleakGATTCharacteristic object representing it.
            use_cached (bool): `False` forces Windows to read the value from the
                device again and not use its own cached value. Defaults to `False`.

        Returns:
            (bytearray) The read data.

        """
        if not isinstance(char_specifier, BleakGATTCharacteristic):
            characteristic = self.services.get_characteristic(char_specifier)
        else:
            characteristic = char_specifier
        if not characteristic:
            raise BleakError(
                "Characteristic {0} was not found!".format(char_specifier))

        read_result = await wrap_IAsyncOperation(
            IAsyncOperation[GattReadResult](characteristic.obj.ReadValueAsync(
                BluetoothCacheMode.
                Cached if use_cached else BluetoothCacheMode.Uncached)),
            return_type=GattReadResult,
            loop=self.loop,
        )
        if read_result.Status == GattCommunicationStatus.Success:
            reader = DataReader.FromBuffer(IBuffer(read_result.Value))
            output = Array.CreateInstance(Byte, reader.UnconsumedBufferLength)
            reader.ReadBytes(output)
            value = bytearray(output)
            logger.debug("Read Characteristic {0} : {1}".format(
                characteristic.uuid, value))
        else:
            if read_result.Status == GattCommunicationStatus.ProtocolError:
                raise BleakDotNetTaskError(
                    "Could not get GATT characteristics for {0}: {1} (Error: 0x{2:02X})"
                    .format(
                        characteristic.uuid,
                        _communication_statues.get(read_result.Status, ""),
                        read_result.ProtocolError,
                    ))
            else:
                raise BleakError(
                    "Could not read characteristic value for {0}: {1}".format(
                        characteristic.uuid,
                        _communication_statues.get(read_result.Status, ""),
                    ))
        return value
Beispiel #26
0
    async def read_gatt_descriptor(
        self, handle: int, use_cached=False, **kwargs
    ) -> bytearray:
        """Perform read operation on the specified GATT descriptor.

        Args:
            handle (int): The handle of the descriptor to read from.
            use_cached (bool): `False` forces Windows to read the value from the
                device again and not use its own cached value. Defaults to `False`.

        Returns:
            (bytearray) The read data.

        """
        descriptor = self.services.get_descriptor(handle)
        if not descriptor:
            raise BleakError("Descriptor with handle {0} was not found!".format(handle))

        read_result = await wrap_IAsyncOperation(
            IAsyncOperation[GattReadResult](
                descriptor.obj.ReadValueAsync(
                    BluetoothCacheMode.Cached
                    if use_cached
                    else BluetoothCacheMode.Uncached
                )
            ),
            return_type=GattReadResult,
        )
        if read_result.Status == GattCommunicationStatus.Success:
            with BleakDataReader(read_result.Value) as reader:
                value = bytearray(reader.read())
            logger.debug("Read Descriptor {0} : {1}".format(handle, value))
        else:
            if read_result.Status == GattCommunicationStatus.ProtocolError:
                raise BleakDotNetTaskError(
                    "Could not get GATT characteristics for {0}: {1} (Error: 0x{2:02X}: {3})".format(
                        descriptor.uuid,
                        _communication_statues.get(read_result.Status, ""),
                        read_result.ProtocolError,
                        CONTROLLER_ERROR_CODES.get(
                            read_result.ProtocolError, "Unknown"
                        ),
                    )
                )
            else:
                raise BleakError(
                    "Could not read Descriptor value for {0}: {1}".format(
                        descriptor.uuid,
                        _communication_statues.get(read_result.Status, ""),
                    )
                )

        return value
Beispiel #27
0
    async def write_gatt_char(
        self,
        char_specifier: Union[BleakGATTCharacteristic, int, str, uuid.UUID],
        data: Union[bytes, bytearray, memoryview],
        response: bool = False,
    ) -> None:
        """Perform a write operation of the specified GATT characteristic.

        Args:
            char_specifier (BleakGATTCharacteristic, int, str or UUID): The characteristic to write
                to, specified by either integer handle, UUID or directly by the
                BleakGATTCharacteristic object representing it.
            data (bytes or bytearray): The data to send.
            response (bool): If write-with-response operation should be done. Defaults to `False`.

        """
        if not isinstance(char_specifier, BleakGATTCharacteristic):
            characteristic = self.services.get_characteristic(char_specifier)
        else:
            characteristic = char_specifier
        if not characteristic:
            raise BleakError(
                "Characteristic {} was not found!".format(char_specifier))

        response = (GattWriteOption.WRITE_WITH_RESPONSE
                    if response else GattWriteOption.WRITE_WITHOUT_RESPONSE)
        write_result = await characteristic.obj.write_value_with_result_async(
            CryptographicBuffer.create_from_byte_array(list(data)), response)

        if write_result.status == GattCommunicationStatus.SUCCESS:
            logger.debug("Write Characteristic {0} : {1}".format(
                characteristic.uuid, data))
        else:
            if write_result.status == GattCommunicationStatus.PROTOCOL_ERROR:
                raise BleakError(
                    "Could not write value {0} to characteristic {1}: {2} (Error: 0x{3:02X}: {4})"
                    .format(
                        data,
                        characteristic.uuid,
                        _communication_statues.get(write_result.status, ""),
                        write_result.protocol_error,
                        CONTROLLER_ERROR_CODES.get(write_result.protocol_error,
                                                   "Unknown"),
                    ))
            else:
                raise BleakError(
                    "Could not write value {0} to characteristic {1}: {2}".
                    format(
                        data,
                        characteristic.uuid,
                        _communication_statues.get(write_result.status, ""),
                    ))
Beispiel #28
0
    async def connect(self) -> bool:
        """Connect to the specified GATT server.

        Returns:
            Boolean representing connection status.

        """
        # Try to find the desired device.
        devices = await discover(2.0, loop=self.loop)
        sought_device = list(
            filter(lambda x: x.address.upper() == self.address.upper(), devices)
        )

        if len(sought_device):
            self._device_info = sought_device[0].details
        else:
            raise BleakError(
                "Device with address {0} was " "not found.".format(self.address)
            )

        logger.debug("Connecting to BLE device @ {0}".format(self.address))

        self._requester = await wrap_IAsyncOperation(
            IAsyncOperation[BluetoothLEDevice](
                BluetoothLEDevice.FromBluetoothAddressAsync(
                    UInt64(self._device_info.BluetoothAddress)
                )
            ),
            return_type=BluetoothLEDevice,
            loop=self.loop,
        )

        def _ConnectionStatusChanged_Handler(sender, args):
            logger.debug("_ConnectionStatusChanged_Handler: " + args.ToString())

        self._requester.ConnectionStatusChanged += _ConnectionStatusChanged_Handler

        # Obtain services, which also leads to connection being established.
        await self.get_services()
        await asyncio.sleep(0.2, loop=self.loop)
        connected = await self.is_connected()
        if connected:
            logger.debug("Connection successful.")
        else:
            raise BleakError(
                "Connection to {0} was not successful!".format(self.address)
            )

        return connected
Beispiel #29
0
    async def read_gatt_descriptor(self, handle: int, **kwargs) -> bytearray:
        """Perform read operation on the specified GATT descriptor.

        Args:
            handle (int): The handle of the descriptor to read from.

        Keyword Args:
            use_cached (bool): `False` forces Windows to read the value from the
                device again and not use its own cached value. Defaults to `False`.

        Returns:
            (bytearray) The read data.

        """
        use_cached = kwargs.get("use_cached", False)

        descriptor = self.services.get_descriptor(handle)
        if not descriptor:
            raise BleakError(
                "Descriptor with handle {0} was not found!".format(handle))

        read_result = await descriptor.obj.read_value_async(
            BluetoothCacheMode.CACHED if use_cached else BluetoothCacheMode.
            UNCACHED)

        if read_result.status == GattCommunicationStatus.SUCCESS:
            value = bytearray(
                CryptographicBuffer.copy_to_byte_array(read_result.value))
            logger.debug("Read Descriptor {0} : {1}".format(handle, value))
        else:
            if read_result.status == GattCommunicationStatus.PROTOCOL_ERROR:
                raise BleakDotNetTaskError(
                    "Could not get GATT characteristics for {0}: {1} (Error: 0x{2:02X}: {3})"
                    .format(
                        descriptor.uuid,
                        _communication_statues.get(read_result.status, ""),
                        read_result.protocol_error,
                        CONTROLLER_ERROR_CODES.get(read_result.protocol_error,
                                                   "Unknown"),
                    ))
            else:
                raise BleakError(
                    "Could not read Descriptor value for {0}: {1}".format(
                        descriptor.uuid,
                        _communication_statues.get(read_result.status, ""),
                    ))

        return value
Beispiel #30
0
    def did_update_value_for_characteristic(
        self,
        peripheral: CBPeripheral,
        characteristic: CBCharacteristic,
        value: NSData,
        error: Optional[NSError],
    ):
        c_handle = characteristic.handle()

        if error is None:
            notify_callback = self._characteristic_notify_callbacks.get(
                c_handle)
            if notify_callback:
                notify_callback(c_handle, bytearray(value))

        future = self._characteristic_read_futures.get(c_handle)
        if not future:
            return  # only expected on read
        if error is not None:
            exception = BleakError(
                f"Failed to read characteristic {c_handle}: {error}")
            future.set_exception(exception)
        else:
            logger.debug("Read characteristic value")
            future.set_result(None)