159 lines
5.0 KiB
Python
159 lines
5.0 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Created on 2019-03-19 by hbldh <henrik.blidh@nedomkull.com>
|
|
"""
|
|
Interface class for the Bleak representation of a GATT Characteristic
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import enum
|
|
from collections.abc import Callable
|
|
from typing import TYPE_CHECKING, Any, Union
|
|
from uuid import UUID
|
|
|
|
from bleak.assigned_numbers import CharacteristicPropertyName
|
|
from bleak.backends.descriptor import BleakGATTDescriptor
|
|
from bleak.uuids import normalize_uuid_str, uuidstr_to_str
|
|
|
|
# to prevent circular import
|
|
if TYPE_CHECKING:
|
|
from bleak.backends.service import BleakGATTService
|
|
|
|
|
|
class GattCharacteristicsFlags(enum.Enum):
|
|
broadcast = 0x0001
|
|
read = 0x0002
|
|
write_without_response = 0x0004
|
|
write = 0x0008
|
|
notify = 0x0010
|
|
indicate = 0x0020
|
|
authenticated_signed_writes = 0x0040
|
|
extended_properties = 0x0080
|
|
reliable_write = 0x0100
|
|
writable_auxiliaries = 0x0200
|
|
|
|
|
|
class BleakGATTCharacteristic:
|
|
"""The Bleak representation of a GATT Characteristic"""
|
|
|
|
def __init__(
|
|
self,
|
|
obj: Any,
|
|
handle: int,
|
|
uuid: str,
|
|
properties: list[CharacteristicPropertyName],
|
|
max_write_without_response_size: Callable[[], int],
|
|
service: BleakGATTService,
|
|
):
|
|
"""
|
|
Args:
|
|
obj:
|
|
A platform-specific object for this characteristic.
|
|
max_write_without_response_size:
|
|
The maximum size in bytes that can be written to the
|
|
characteristic in a single write without response command.
|
|
service:
|
|
The service this characteristic belongs to.
|
|
"""
|
|
self.obj = obj
|
|
self._handle = handle
|
|
self._uuid = uuid
|
|
self._properties = properties
|
|
self._max_write_without_response_size = max_write_without_response_size
|
|
self._service = service
|
|
self._descriptors: dict[int, BleakGATTDescriptor] = {}
|
|
|
|
def __str__(self):
|
|
return f"{self.uuid} (Handle: {self.handle}): {self.description}"
|
|
|
|
@property
|
|
def service_uuid(self) -> str:
|
|
"""The UUID of the Service containing this characteristic"""
|
|
return self._service.uuid
|
|
|
|
@property
|
|
def service_handle(self) -> int:
|
|
"""The integer handle of the Service containing this characteristic"""
|
|
return self._service.handle
|
|
|
|
@property
|
|
def handle(self) -> int:
|
|
"""The handle for this characteristic"""
|
|
return self._handle
|
|
|
|
@property
|
|
def uuid(self) -> str:
|
|
"""The UUID for this characteristic"""
|
|
return self._uuid
|
|
|
|
@property
|
|
def description(self) -> str:
|
|
"""Description for this characteristic"""
|
|
return uuidstr_to_str(self.uuid)
|
|
|
|
@property
|
|
def properties(self) -> list[CharacteristicPropertyName]:
|
|
"""Properties of this characteristic"""
|
|
return self._properties
|
|
|
|
@property
|
|
def max_write_without_response_size(self) -> int:
|
|
"""
|
|
Gets the maximum size in bytes that can be used for the *data* argument
|
|
of :meth:`BleakClient.write_gatt_char()` when ``response=False``.
|
|
|
|
In rare cases, a device may take a long time to update this value, so
|
|
reading this property may return the default value of ``20`` and reading
|
|
it again after a some time may return the expected higher value.
|
|
|
|
If you *really* need to wait for a higher value, you can do something
|
|
like this:
|
|
|
|
.. code-block:: python
|
|
|
|
async with asyncio.timeout(10):
|
|
while char.max_write_without_response_size == 20:
|
|
await asyncio.sleep(0.5)
|
|
|
|
.. warning:: Linux quirk: For BlueZ versions < 5.62, this property
|
|
will always return ``20``.
|
|
|
|
.. versionadded:: 0.16
|
|
"""
|
|
|
|
# for backwards compatibility
|
|
if isinstance(self._max_write_without_response_size, int):
|
|
return self._max_write_without_response_size
|
|
|
|
return self._max_write_without_response_size()
|
|
|
|
@property
|
|
def descriptors(self) -> list[BleakGATTDescriptor]:
|
|
"""List of descriptors for this service"""
|
|
return list(self._descriptors.values())
|
|
|
|
def get_descriptor(
|
|
self, specifier: Union[int, str, UUID]
|
|
) -> Union[BleakGATTDescriptor, None]:
|
|
"""Get a descriptor by handle (int) or UUID (str or uuid.UUID)"""
|
|
if isinstance(specifier, int):
|
|
return self._descriptors.get(specifier)
|
|
|
|
uuid = normalize_uuid_str(str(specifier))
|
|
for descriptor in self._descriptors.values():
|
|
if descriptor.uuid == uuid:
|
|
return descriptor
|
|
|
|
return None
|
|
|
|
def add_descriptor(self, descriptor: BleakGATTDescriptor) -> None:
|
|
"""Add a :py:class:`~BleakGATTDescriptor` to the characteristic.
|
|
|
|
Should not be used by end user, but rather by `bleak` itself.
|
|
"""
|
|
if descriptor.handle in self._descriptors:
|
|
raise ValueError(
|
|
f"Descriptor with handle {descriptor.handle} already exists"
|
|
)
|
|
|
|
self._descriptors[descriptor.handle] = descriptor
|