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

uuid-utilities.mddocs/

UUID Utilities and Constants

Bleak provides comprehensive utilities for UUID normalization, conversion, and lookup of Bluetooth assigned numbers. These utilities handle the conversion between different UUID formats and provide human-readable descriptions for standard Bluetooth services and characteristics.

Capabilities

UUID Normalization Functions

Convert UUIDs between different formats and normalize them to Bleak's standard 128-bit lowercase format.

def normalize_uuid_str(uuid: str) -> str:
    """
    Normalize a UUID string to Bleak's standard format.
    
    Converts UUID to lowercase and expands 16-bit and 32-bit UUIDs to 128-bit format.
    
    Args:
        uuid: UUID string in 16-bit, 32-bit, or 128-bit format
        
    Returns:
        128-bit UUID string in lowercase format
        
    Examples:
        normalize_uuid_str("1234") -> "00001234-0000-1000-8000-00805f9b34fb"
        normalize_uuid_str("12345678") -> "12345678-0000-1000-8000-00805f9b34fb"
        normalize_uuid_str("12345678-0000-1234-1234-1234567890ABC") -> 
            "12345678-0000-1234-1234-1234567890abc"
    
    Note: Function normalizes according to Bluetooth Core Specification Version 5.4 | Vol 3, Part B - Section 2.5.1
    """

def normalize_uuid_16(uuid: int) -> str:
    """
    Normalizes a 16-bit integer UUID to Bleak's standard format.
    
    Args:
        uuid: 16-bit integer UUID
        
    Returns:
        128-bit UUID string with format "0000xxxx-0000-1000-8000-00805f9b34fb"
        
    Example:
        normalize_uuid_16(0x1234) -> "00001234-0000-1000-8000-00805f9b34fb"
    """

def normalize_uuid_32(uuid: int) -> str:
    """
    Normalizes a 32-bit integer UUID to Bleak's standard format.
    
    Args:
        uuid: 32-bit integer UUID
        
    Returns:
        128-bit UUID string with format "xxxxxxxx-0000-1000-8000-00805f9b34fb"
        
    Example:
        normalize_uuid_32(0x12345678) -> "12345678-0000-1000-8000-00805f9b34fb"
    """

UUID Description Lookup

Convert UUIDs to human-readable descriptions using Bluetooth assigned numbers.

def uuidstr_to_str(uuid_: str) -> str:
    """
    Convert UUID string to human-readable description.
    
    Looks up the UUID in the assigned numbers database and returns
    a descriptive name if found.
    
    Args:
        uuid_: UUID string to look up
        
    Returns:
        Human-readable description or "Unknown" if not found
        
    Examples:
        uuidstr_to_str("0000180f-0000-1000-8000-00805f9b34fb") -> "Battery Service"
        uuidstr_to_str("00002a19-0000-1000-8000-00805f9b34fb") -> "Battery Level"
    """

def register_uuids(uuids_to_descriptions: dict[str, str]) -> None:
    """
    Register custom UUID to description mappings.
    
    Adds or modifies the mapping of 128-bit UUIDs to descriptions
    for application-specific or vendor-specific UUIDs.
    
    Args:
        uuids_to_descriptions: Dictionary mapping UUID strings to descriptions
        
    Example:
        register_uuids({
            "12345678-1234-5678-9012-123456789abc": "My Custom Service",
            "87654321-4321-8765-2109-cba987654321": "My Custom Characteristic"
        })
    """

UUID Assigned Numbers Constants

Comprehensive dictionaries containing Bluetooth SIG assigned numbers for services, characteristics, and descriptors.

# 16-bit UUID assignments
uuid16_dict: dict[int, str] = {
    0x1800: "Generic Access Profile",
    0x1801: "Generic Attribute Profile", 
    0x1802: "Immediate Alert",
    0x180A: "Device Information",
    0x180F: "Battery Service",
    0x1812: "Human Interface Device",
    # ... (complete mapping with hundreds of entries)
}

# 128-bit UUID assignments (vendor-specific and custom services)
uuid128_dict: dict[str, str] = {
    "6e400001-b5a3-f393-e0a9-e50e24dcca9e": "Nordic UART Service",
    "6e400002-b5a3-f393-e0a9-e50e24dcca9e": "Nordic UART RX",
    "6e400003-b5a3-f393-e0a9-e50e24dcca9e": "Nordic UART TX",
    # ... (complete mapping with custom and vendor UUIDs)
}

Advertisement Data Type Constants

Enumeration of Generic Access Profile advertisement data types.

class AdvertisementDataType(IntEnum):
    """Generic Access Profile advertisement data types."""
    
    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

Characteristic Properties

Constants and utilities for GATT characteristic properties.

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",
]

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",
}

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

Usage Examples

UUID Normalization and Conversion

from bleak.uuids import normalize_uuid_str, normalize_uuid_16, normalize_uuid_32

def demonstrate_uuid_normalization():
    # Normalize different UUID formats
    uuid_16bit = normalize_uuid_str("180F")  # Battery Service
    print(f"16-bit UUID: {uuid_16bit}")
    # Output: 0000180f-0000-1000-8000-00805f9b34fb
    
    uuid_32bit = normalize_uuid_str("12345678")
    print(f"32-bit UUID: {uuid_32bit}")
    # Output: 12345678-0000-1000-8000-00805f9b34fb
    
    uuid_128bit = normalize_uuid_str("12345678-1234-5678-9012-123456789ABC")
    print(f"128-bit UUID: {uuid_128bit}")
    # Output: 12345678-1234-5678-9012-123456789abc
    
    # Normalize from integers
    from_int_16 = normalize_uuid_16(0x180F)
    print(f"From 16-bit int: {from_int_16}")
    
    from_int_32 = normalize_uuid_32(0x12345678)  
    print(f"From 32-bit int: {from_int_32}")

demonstrate_uuid_normalization()

UUID Description Lookup

from bleak.uuids import uuidstr_to_str

def demonstrate_uuid_lookup():
    # Look up standard Bluetooth services
    gap_service = uuidstr_to_str("00001800-0000-1000-8000-00805f9b34fb")
    print(f"GAP Service: {gap_service}")
    # Output: Generic Access Profile
    
    battery_service = uuidstr_to_str("0000180f-0000-1000-8000-00805f9b34fb") 
    print(f"Battery Service: {battery_service}")
    # Output: Battery Service
    
    # Look up standard characteristics
    device_name = uuidstr_to_str("00002a00-0000-1000-8000-00805f9b34fb")
    print(f"Device Name: {device_name}")
    # Output: Device Name
    
    battery_level = uuidstr_to_str("00002a19-0000-1000-8000-00805f9b34fb")
    print(f"Battery Level: {battery_level}")
    # Output: Battery Level
    
    # Look up vendor-specific UUID
    nordic_uart = uuidstr_to_str("6e400001-b5a3-f393-e0a9-e50e24dcca9e")
    print(f"Nordic UART: {nordic_uart}")
    # Output: Nordic UART Service
    
    # Unknown UUID
    unknown = uuidstr_to_str("12345678-1234-5678-9012-123456789abc")
    print(f"Unknown UUID: {unknown}")
    # Output: Unknown

demonstrate_uuid_lookup()

Registering Custom UUIDs

from bleak.uuids import register_uuids, uuidstr_to_str

def demonstrate_custom_uuids():
    # Register custom UUID mappings
    custom_uuids = {
        "12345678-1234-5678-9012-123456789abc": "My IoT Device Service",
        "87654321-4321-8765-2109-cba987654321": "Temperature Sensor Characteristic", 
        "11111111-2222-3333-4444-555555555555": "Custom Control Point"
    }
    
    register_uuids(custom_uuids)
    
    # Now lookup returns custom descriptions
    service_desc = uuidstr_to_str("12345678-1234-5678-9012-123456789abc")
    print(f"Custom Service: {service_desc}")
    # Output: My IoT Device Service
    
    char_desc = uuidstr_to_str("87654321-4321-8765-2109-cba987654321")
    print(f"Custom Characteristic: {char_desc}")
    # Output: Temperature Sensor Characteristic

demonstrate_custom_uuids()

Working with Characteristic Properties

from bleak.assigned_numbers import gatt_char_props_to_strs, CHARACTERISTIC_PROPERTIES

def demonstrate_characteristic_properties():
    # Example properties bitmask (read + write + notify)
    props_mask = 0x1A  # 0x02 (read) + 0x08 (write) + 0x10 (notify)
    
    # Convert to property names
    properties = gatt_char_props_to_strs(props_mask)
    print(f"Properties: {properties}")
    # Output: frozenset({'read', 'write', 'notify'})
    
    # Check individual properties
    if "read" in properties:
        print("Characteristic supports reading")
        
    if "write" in properties:
        print("Characteristic supports write-with-response")
        
    if "notify" in properties:
        print("Characteristic supports notifications")
        
    if "write-without-response" not in properties:
        print("Characteristic does not support write-without-response")
    
    # Show all available property mappings
    print("\nAll property mappings:")
    for bit, name in CHARACTERISTIC_PROPERTIES.items():
        print(f"  0x{bit:02X}: {name}")

demonstrate_characteristic_properties()

Practical UUID Usage in BLE Operations

import asyncio
from bleak import BleakClient
from bleak.uuids import normalize_uuid_str, uuidstr_to_str

async def uuid_practical_usage():
    address = "00:11:22:33:44:55"  # Replace with actual device address
    
    async with BleakClient(address) as client:
        print(f"Connected to {client.name}")
        
        # Use UUID utilities with service discovery
        for service in client.services:
            service_desc = uuidstr_to_str(service.uuid)
            print(f"\nService: {service.uuid}")
            print(f"  Description: {service_desc}")
            
            for char in service.characteristics:
                char_desc = uuidstr_to_str(char.uuid)
                print(f"  Characteristic: {char.uuid}")
                print(f"    Description: {char_desc}")
                print(f"    Properties: {char.properties}")
                
                # Demonstrate reading standard characteristics
                if char.uuid == normalize_uuid_str("2A00"):  # Device Name
                    try:
                        data = await client.read_gatt_char(char.uuid)
                        name = data.decode('utf-8')
                        print(f"    Device Name: {name}")
                    except Exception as e:
                        print(f"    Could not read device name: {e}")
                        
                elif char.uuid == normalize_uuid_str("2A19"):  # Battery Level
                    try:
                        data = await client.read_gatt_char(char.uuid)
                        level = int.from_bytes(data, byteorder='little')
                        print(f"    Battery Level: {level}%")
                    except Exception as e:
                        print(f"    Could not read battery level: {e}")

# asyncio.run(uuid_practical_usage())  # Uncomment with real device

Advertisement Data Type Usage

from bleak.assigned_numbers import AdvertisementDataType

def process_advertisement_data(adv_data_raw):
    """Example of processing raw advertisement data using constants."""
    
    # Parse advertisement data (simplified example)
    pos = 0
    while pos < len(adv_data_raw):
        length = adv_data_raw[pos]
        if length == 0:
            break
            
        ad_type = adv_data_raw[pos + 1]
        data = adv_data_raw[pos + 2:pos + 1 + length]
        
        # Use constants for readable code
        if ad_type == AdvertisementDataType.COMPLETE_LOCAL_NAME:
            device_name = data.decode('utf-8')
            print(f"Device Name: {device_name}")
            
        elif ad_type == AdvertisementDataType.COMPLETE_LIST_SERVICE_UUID16:
            # Parse 16-bit service UUIDs
            for i in range(0, len(data), 2):
                uuid_16 = int.from_bytes(data[i:i+2], byteorder='little')
                print(f"Service UUID: {uuid_16:04X}")
                
        elif ad_type == AdvertisementDataType.TX_POWER_LEVEL:
            tx_power = int.from_bytes(data, byteorder='little', signed=True)
            print(f"TX Power: {tx_power} dBm")
            
        elif ad_type == AdvertisementDataType.MANUFACTURER_SPECIFIC_DATA:
            company_id = int.from_bytes(data[:2], byteorder='little')
            mfg_data = data[2:]
            print(f"Manufacturer {company_id:04X}: {mfg_data.hex()}")
        
        pos += 1 + length

# Example usage with mock data
mock_adv_data = bytes([
    0x05, 0x09, 0x54, 0x65, 0x73, 0x74,  # Complete local name: "Test"
    0x03, 0x03, 0x0F, 0x18,              # Complete 16-bit service UUID: 0x180F
    0x02, 0x0A, 0x00,                    # TX Power: 0 dBm
])

process_advertisement_data(mock_adv_data)

Types

# UUID dictionaries
uuid16_dict: dict[int, str]
uuid128_dict: dict[str, str] 

# Characteristic property type
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"
]

# Property mapping
CHARACTERISTIC_PROPERTIES: dict[int, CharacteristicPropertyName]

# Advertisement data types
class AdvertisementDataType(IntEnum): ...

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