CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-bleak

Cross-platform Bluetooth Low Energy GATT client library for asynchronous BLE communication

Overall
score

97%

Overview
Eval results
Files

data-structures.mddocs/

Data Structures and Models

Bleak provides comprehensive data structures representing BLE devices, advertisement data, and the complete GATT hierarchy including services, characteristics, and descriptors. These models provide a consistent interface across all supported platforms.

Capabilities

BLE Device Representation

Core representation of discovered BLE devices with address, name, and platform-specific details.

class BLEDevice:
    """A simple wrapper class representing a BLE server detected during scanning."""
    
    __slots__ = ("address", "name", "details")
    
    def __init__(self, address: str, name: Optional[str], details: Any, **kwargs: Any):
        """
        Initialize BLE device representation.
        
        Args:
            address: Bluetooth address of the device on this machine (UUID on macOS)
            name: Operating system name of the device (not necessarily the local name
                  from advertising data), suitable for display to the user
            details: OS native details required for connecting to the device
            **kwargs: Deprecated additional arguments (no effect)
        """
    
    address: str  # The Bluetooth address of the device on this machine (UUID on macOS)
    name: Optional[str]  # The operating system name of the device, suitable for display
    details: Any  # The OS native details required for connecting to the device
    
    def __str__(self) -> str:
        """String representation showing address and name."""
    
    def __repr__(self) -> str:
        """Detailed string representation for debugging."""

Advertisement Data Container

Structured container for BLE advertisement data received during device discovery.

class AdvertisementData(NamedTuple):
    """Container for BLE advertisement data from device discovery."""
    
    local_name: Optional[str]
    """Device local name or None if not included in advertising data."""
    
    manufacturer_data: dict[int, bytes]
    """
    Dictionary of manufacturer data from advertising.
    Keys are Bluetooth SIG Company Identifiers, values are data bytes.
    """
    
    service_data: dict[str, bytes]
    """
    Dictionary of service data from advertising.
    Keys are service UUIDs, values are associated data bytes.
    """
    
    service_uuids: list[str]
    """List of service UUIDs advertised by the device."""
    
    tx_power: Optional[int]
    """TX Power Level in dBm from advertising data, or None if not present."""
    
    rssi: int
    """Radio Receive Signal Strength Indicator (RSSI) in dBm."""
    
    platform_data: tuple[Any, ...]
    """
    Tuple of platform-specific data.
    This is not a stable API and may change between releases.
    """
    
    def __repr__(self) -> str:
        """Detailed string representation showing all available data."""

GATT Service Collection

Container managing the complete GATT service hierarchy for a connected device.

class BleakGATTServiceCollection:
    """Simple data container for storing the peripheral's service complement."""
    
    def __init__(self) -> None:
        """Initialize empty service collection."""
    
    def __getitem__(
        self, item: Union[str, int, UUID]
    ) -> Optional[Union[BleakGATTService, BleakGATTCharacteristic, BleakGATTDescriptor]]:
        """Get service, characteristic, or descriptor by UUID or handle."""
    
    def __iter__(self) -> Iterator[BleakGATTService]:
        """Returns an iterator over all BleakGATTService objects."""
    
    @property
    def services(self) -> dict[int, BleakGATTService]:
        """Returns dictionary of handles mapping to BleakGATTService."""
    
    @property
    def characteristics(self) -> dict[int, BleakGATTCharacteristic]:
        """Returns dictionary of handles mapping to BleakGATTCharacteristic."""
    
    @property
    def descriptors(self) -> dict[int, BleakGATTDescriptor]:
        """Returns a dictionary of integer handles mapping to BleakGATTDescriptor."""
    
    def get_service(
        self, specifier: Union[int, str, UUID]
    ) -> Optional[BleakGATTService]:
        """
        Get service by handle (int) or UUID (str or uuid.UUID).
        
        Args:
            specifier: Service handle (int) or UUID (str/UUID)
            
        Returns:
            BleakGATTService if found, None otherwise
            
        Raises:
            BleakError: If multiple services have the same UUID
        """
    
    def get_characteristic(
        self, specifier: Union[int, str, UUID]
    ) -> Optional[BleakGATTCharacteristic]:
        """
        Get characteristic by handle (int) or UUID (str or uuid.UUID).
        
        Args:
            specifier: Characteristic handle (int) or UUID (str/UUID)
            
        Returns:
            BleakGATTCharacteristic if found, None otherwise
            
        Raises:
            BleakError: If multiple characteristics have the same UUID
        """
    
    def get_descriptor(self, handle: int) -> Optional[BleakGATTDescriptor]:
        """
        Get descriptor by handle.
        
        Args:
            handle: Descriptor handle
            
        Returns:
            BleakGATTDescriptor if found, None otherwise
        """

GATT Service Representation

Representation of a GATT service containing characteristics and metadata.

class BleakGATTService:
    """Representation of a GATT service."""
    
    def __init__(self, obj: Any, handle: int, uuid: str) -> None:
        """
        Initialize GATT service.
        
        Args:
            obj: Platform-specific service object
            handle: Service handle
            uuid: Service UUID
        """
    
    def __str__(self) -> str:
        """String representation showing UUID, handle, and description."""
    
    @property
    def handle(self) -> int:
        """Handle of this service."""
    
    @property
    def uuid(self) -> str:
        """UUID of this service."""
    
    @property
    def description(self) -> str:
        """Human-readable description of this service."""
    
    @property
    def characteristics(self) -> list[BleakGATTCharacteristic]:
        """List of characteristics belonging to this service."""
    
    def get_characteristic(
        self, uuid: Union[str, UUID]
    ) -> Union[BleakGATTCharacteristic, None]:
        """
        Get characteristic by UUID.
        
        Args:
            uuid: Characteristic UUID to match
            
        Returns:
            First matching BleakGATTCharacteristic or None
        """

GATT Characteristic Representation

Representation of a GATT characteristic with properties, descriptors, and metadata.

class BleakGATTCharacteristic:
    """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,
    ):
        """
        Initialize GATT characteristic.
        
        Args:
            obj: Platform-specific characteristic object
            handle: Characteristic handle
            uuid: Characteristic UUID
            properties: List of characteristic properties
            max_write_without_response_size: Function returning max write size
            service: Parent service
        """
    
    def __str__(self) -> str:
        """String representation showing UUID, handle, and description."""
    
    @property
    def service_uuid(self) -> str:
        """UUID of the service containing this characteristic."""
    
    @property
    def service_handle(self) -> int:
        """Handle of the service containing this characteristic."""
    
    @property
    def handle(self) -> int:
        """Handle of this characteristic."""
    
    @property
    def uuid(self) -> str:
        """UUID of this characteristic."""
    
    @property
    def description(self) -> str:
        """Human-readable description of this characteristic."""
    
    @property
    def properties(self) -> list[CharacteristicPropertyName]:
        """List of properties supported by this characteristic."""
    
    @property
    def max_write_without_response_size(self) -> int:
        """
        Maximum size for write-without-response operations.
        
        Note: May return default value (20) initially and update later.
        BlueZ versions < 5.62 always return 20.
        """
    
    @property
    def descriptors(self) -> list[BleakGATTDescriptor]:
        """List of descriptors belonging to this characteristic."""
    
    def get_descriptor(
        self, specifier: Union[int, str, UUID]
    ) -> Union[BleakGATTDescriptor, None]:
        """
        Get descriptor by handle or UUID.
        
        Args:
            specifier: Descriptor handle (int) or UUID (str/UUID)
            
        Returns:
            BleakGATTDescriptor if found, None otherwise
        """

GATT Descriptor Representation

Representation of a GATT descriptor with metadata and parent characteristic reference.

class BleakGATTDescriptor:
    """Representation of a GATT descriptor."""
    
    def __init__(
        self, obj: Any, handle: int, uuid: str, characteristic: BleakGATTCharacteristic
    ):
        """
        Initialize GATT descriptor.
        
        Args:
            obj: Platform-specific descriptor object
            handle: Descriptor handle
            uuid: Descriptor UUID
            characteristic: Parent characteristic
        """
    
    def __str__(self) -> str:
        """String representation showing UUID, handle, and description."""
    
    @property
    def characteristic_uuid(self) -> str:
        """UUID of the characteristic this descriptor belongs to."""
    
    @property
    def characteristic_handle(self) -> int:
        """Handle of the characteristic this descriptor belongs to."""
    
    @property
    def uuid(self) -> str:
        """UUID of this descriptor."""
    
    @property
    def handle(self) -> int:
        """Handle of this descriptor."""
    
    @property
    def description(self) -> str:
        """Human-readable description of this descriptor."""

Characteristic Properties and Flags

Enumeration and utilities for GATT characteristic properties.

class GattCharacteristicsFlags(enum.Enum):
    """Enumeration of GATT characteristic property flags."""
    
    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

def gatt_char_props_to_strs(
    props: int,
) -> frozenset[CharacteristicPropertyName]:
    """
    Convert characteristic properties bitmask to set of property names.
    
    Args:
        props: Properties bitmask
        
    Returns:
        Frozenset of property name strings
    """

Usage Examples

Working with Discovered Devices

import asyncio
from bleak import BleakScanner

async def analyze_discovered_devices():
    # Discover devices with advertisement data
    discovered = await BleakScanner.discover(timeout=10.0, return_adv=True)
    
    for address, (device, adv_data) in discovered.items():
        print(f"Device: {device}")
        print(f"  Address: {device.address}")
        print(f"  Name: {device.name}")
        
        print(f"  Advertisement Data:")
        print(f"    Local Name: {adv_data.local_name}")
        print(f"    RSSI: {adv_data.rssi} dBm")
        print(f"    TX Power: {adv_data.tx_power}")
        print(f"    Service UUIDs: {adv_data.service_uuids}")
        
        # Process manufacturer data
        for company_id, data in adv_data.manufacturer_data.items():
            print(f"    Manufacturer {company_id:04x}: {data.hex()}")
        
        # Process service data
        for service_uuid, data in adv_data.service_data.items():
            print(f"    Service {service_uuid}: {data.hex()}")

asyncio.run(analyze_discovered_devices())

Exploring GATT Hierarchy

import asyncio
from bleak import BleakClient

async def explore_gatt_structure():
    address = "00:11:22:33:44:55"  # Replace with actual device address
    
    async with BleakClient(address) as client:
        services = client.services
        
        print(f"Device has {len(services.services)} services")
        print(f"Total characteristics: {len(services.characteristics)}")
        print(f"Total descriptors: {len(services.descriptors)}")
        
        # Iterate through service collection
        for service in services:
            print(f"\nService: {service}")
            print(f"  Handle: {service.handle}")
            print(f"  UUID: {service.uuid}")
            print(f"  Description: {service.description}")
            
            for char in service.characteristics:
                print(f"  Characteristic: {char}")
                print(f"    Handle: {char.handle}")
                print(f"    UUID: {char.uuid}")
                print(f"    Properties: {char.properties}")
                print(f"    Max write size: {char.max_write_without_response_size}")
                
                for desc in char.descriptors:
                    print(f"    Descriptor: {desc}")
                    print(f"      Handle: {desc.handle}")
                    print(f"      UUID: {desc.uuid}")

asyncio.run(explore_gatt_structure())

Finding Specific Services and Characteristics

import asyncio
from bleak import BleakClient

async def find_specific_elements():
    address = "00:11:22:33:44:55"  # Replace with actual device address
    
    async with BleakClient(address) as client:
        services = client.services
        
        # Find Generic Access service
        gap_service = services.get_service("00001800-0000-1000-8000-00805f9b34fb")
        if gap_service:
            print(f"Found GAP service: {gap_service.description}")
            
            # Find device name characteristic within the service
            device_name_char = gap_service.get_characteristic(
                "00002a00-0000-1000-8000-00805f9b34fb"
            )
            if device_name_char:
                print(f"Found device name characteristic: {device_name_char.description}")
        
        # Find characteristic by UUID across all services
        battery_char = services.get_characteristic("00002a19-0000-1000-8000-00805f9b34fb")
        if battery_char:
            print(f"Found battery level characteristic: {battery_char.description}")
            print(f"  Service UUID: {battery_char.service_uuid}")
            print(f"  Properties: {battery_char.properties}")
        
        # Find descriptor by handle
        desc = services.get_descriptor(42)  # Replace with actual handle
        if desc:
            print(f"Found descriptor: {desc.description}")
            print(f"  Characteristic UUID: {desc.characteristic_uuid}")

asyncio.run(find_specific_elements())

Working with Characteristic Properties

from bleak.assigned_numbers import gatt_char_props_to_strs

def analyze_characteristic_properties():
    # Example properties bitmask
    props_mask = 0x12  # read (0x02) + notify (0x10)
    
    # Convert to property names
    properties = gatt_char_props_to_strs(props_mask)
    print(f"Properties: {properties}")
    
    # Check for specific properties
    if "read" in properties:
        print("Characteristic supports reading")
    
    if "notify" in properties:
        print("Characteristic supports notifications")
    
    if "write" in properties:
        print("Characteristic supports write-with-response")
    
    if "write-without-response" in properties:
        print("Characteristic supports write-without-response")

analyze_characteristic_properties()

Types

# Characteristic property names
CharacteristicPropertyName = Literal[
    "broadcast",
    "read", 
    "write-without-response",
    "write",
    "notify",
    "indicate",
    "authenticated-signed-writes",
    "extended-properties",
    "reliable-write",
    "writable-auxiliaries",
    "encrypt-read",
    "encrypt-write",
    "encrypt-authenticated-read",
    "encrypt-authenticated-write",
    "authorize",
]

# Advertisement data type enumeration
class AdvertisementDataType(IntEnum):
    FLAGS = 0x01
    INCOMPLETE_LIST_SERVICE_UUID16 = 0x02
    COMPLETE_LIST_SERVICE_UUID16 = 0x03
    INCOMPLETE_LIST_SERVICE_UUID32 = 0x04
    COMPLETE_LIST_SERVICE_UUID32 = 0x05
    INCOMPLETE_LIST_SERVICE_UUID128 = 0x06
    COMPLETE_LIST_SERVICE_UUID128 = 0x07
    SHORTENED_LOCAL_NAME = 0x08
    COMPLETE_LOCAL_NAME = 0x09
    TX_POWER_LEVEL = 0x0A
    CLASS_OF_DEVICE = 0x0D
    SERVICE_DATA_UUID16 = 0x16
    SERVICE_DATA_UUID32 = 0x20
    SERVICE_DATA_UUID128 = 0x21
    MANUFACTURER_SPECIFIC_DATA = 0xFF

# Property mapping for internal use
CHARACTERISTIC_PROPERTIES: dict[int, CharacteristicPropertyName] = {
    0x1: "broadcast",
    0x2: "read",
    0x4: "write-without-response",
    0x8: "write",
    0x10: "notify",
    0x20: "indicate",
    0x40: "authenticated-signed-writes",
    0x80: "extended-properties",
    0x100: "reliable-write",
    0x200: "writable-auxiliaries",
}

Install with Tessl CLI

npx tessl i tessl/pypi-bleak

docs

data-structures.md

device-discovery.md

exception-handling.md

gatt-client.md

index.md

uuid-utilities.md

tile.json