0
# UUID Utilities and Constants
1
2
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.
3
4
## Capabilities
5
6
### UUID Normalization Functions
7
8
Convert UUIDs between different formats and normalize them to Bleak's standard 128-bit lowercase format.
9
10
```python { .api }
11
def normalize_uuid_str(uuid: str) -> str:
12
"""
13
Normalize a UUID string to Bleak's standard format.
14
15
Converts UUID to lowercase and expands 16-bit and 32-bit UUIDs to 128-bit format.
16
17
Args:
18
uuid: UUID string in 16-bit, 32-bit, or 128-bit format
19
20
Returns:
21
128-bit UUID string in lowercase format
22
23
Examples:
24
normalize_uuid_str("1234") -> "00001234-0000-1000-8000-00805f9b34fb"
25
normalize_uuid_str("12345678") -> "12345678-0000-1000-8000-00805f9b34fb"
26
normalize_uuid_str("12345678-0000-1234-1234-1234567890ABC") ->
27
"12345678-0000-1234-1234-1234567890abc"
28
29
Note: Function normalizes according to Bluetooth Core Specification Version 5.4 | Vol 3, Part B - Section 2.5.1
30
"""
31
32
def normalize_uuid_16(uuid: int) -> str:
33
"""
34
Normalizes a 16-bit integer UUID to Bleak's standard format.
35
36
Args:
37
uuid: 16-bit integer UUID
38
39
Returns:
40
128-bit UUID string with format "0000xxxx-0000-1000-8000-00805f9b34fb"
41
42
Example:
43
normalize_uuid_16(0x1234) -> "00001234-0000-1000-8000-00805f9b34fb"
44
"""
45
46
def normalize_uuid_32(uuid: int) -> str:
47
"""
48
Normalizes a 32-bit integer UUID to Bleak's standard format.
49
50
Args:
51
uuid: 32-bit integer UUID
52
53
Returns:
54
128-bit UUID string with format "xxxxxxxx-0000-1000-8000-00805f9b34fb"
55
56
Example:
57
normalize_uuid_32(0x12345678) -> "12345678-0000-1000-8000-00805f9b34fb"
58
"""
59
```
60
61
### UUID Description Lookup
62
63
Convert UUIDs to human-readable descriptions using Bluetooth assigned numbers.
64
65
```python { .api }
66
def uuidstr_to_str(uuid_: str) -> str:
67
"""
68
Convert UUID string to human-readable description.
69
70
Looks up the UUID in the assigned numbers database and returns
71
a descriptive name if found.
72
73
Args:
74
uuid_: UUID string to look up
75
76
Returns:
77
Human-readable description or "Unknown" if not found
78
79
Examples:
80
uuidstr_to_str("0000180f-0000-1000-8000-00805f9b34fb") -> "Battery Service"
81
uuidstr_to_str("00002a19-0000-1000-8000-00805f9b34fb") -> "Battery Level"
82
"""
83
84
def register_uuids(uuids_to_descriptions: dict[str, str]) -> None:
85
"""
86
Register custom UUID to description mappings.
87
88
Adds or modifies the mapping of 128-bit UUIDs to descriptions
89
for application-specific or vendor-specific UUIDs.
90
91
Args:
92
uuids_to_descriptions: Dictionary mapping UUID strings to descriptions
93
94
Example:
95
register_uuids({
96
"12345678-1234-5678-9012-123456789abc": "My Custom Service",
97
"87654321-4321-8765-2109-cba987654321": "My Custom Characteristic"
98
})
99
"""
100
```
101
102
### UUID Assigned Numbers Constants
103
104
Comprehensive dictionaries containing Bluetooth SIG assigned numbers for services, characteristics, and descriptors.
105
106
```python { .api }
107
# 16-bit UUID assignments
108
uuid16_dict: dict[int, str] = {
109
0x1800: "Generic Access Profile",
110
0x1801: "Generic Attribute Profile",
111
0x1802: "Immediate Alert",
112
0x180A: "Device Information",
113
0x180F: "Battery Service",
114
0x1812: "Human Interface Device",
115
# ... (complete mapping with hundreds of entries)
116
}
117
118
# 128-bit UUID assignments (vendor-specific and custom services)
119
uuid128_dict: dict[str, str] = {
120
"6e400001-b5a3-f393-e0a9-e50e24dcca9e": "Nordic UART Service",
121
"6e400002-b5a3-f393-e0a9-e50e24dcca9e": "Nordic UART RX",
122
"6e400003-b5a3-f393-e0a9-e50e24dcca9e": "Nordic UART TX",
123
# ... (complete mapping with custom and vendor UUIDs)
124
}
125
```
126
127
### Advertisement Data Type Constants
128
129
Enumeration of Generic Access Profile advertisement data types.
130
131
```python { .api }
132
class AdvertisementDataType(IntEnum):
133
"""Generic Access Profile advertisement data types."""
134
135
FLAGS = 0x01
136
INCOMPLETE_LIST_SERVICE_UUID16 = 0x02
137
COMPLETE_LIST_SERVICE_UUID16 = 0x03
138
INCOMPLETE_LIST_SERVICE_UUID32 = 0x04
139
COMPLETE_LIST_SERVICE_UUID32 = 0x05
140
INCOMPLETE_LIST_SERVICE_UUID128 = 0x06
141
COMPLETE_LIST_SERVICE_UUID128 = 0x07
142
SHORTENED_LOCAL_NAME = 0x08
143
COMPLETE_LOCAL_NAME = 0x09
144
TX_POWER_LEVEL = 0x0A
145
CLASS_OF_DEVICE = 0x0D
146
SERVICE_DATA_UUID16 = 0x16
147
SERVICE_DATA_UUID32 = 0x20
148
SERVICE_DATA_UUID128 = 0x21
149
MANUFACTURER_SPECIFIC_DATA = 0xFF
150
```
151
152
### Characteristic Properties
153
154
Constants and utilities for GATT characteristic properties.
155
156
```python { .api }
157
CharacteristicPropertyName = Literal[
158
"broadcast",
159
"read",
160
"write-without-response",
161
"write",
162
"notify",
163
"indicate",
164
"authenticated-signed-writes",
165
"extended-properties",
166
"reliable-write",
167
"writable-auxiliaries",
168
"encrypt-read",
169
"encrypt-write",
170
"encrypt-authenticated-read",
171
"encrypt-authenticated-write",
172
"authorize",
173
]
174
175
CHARACTERISTIC_PROPERTIES: dict[int, CharacteristicPropertyName] = {
176
0x1: "broadcast",
177
0x2: "read",
178
0x4: "write-without-response",
179
0x8: "write",
180
0x10: "notify",
181
0x20: "indicate",
182
0x40: "authenticated-signed-writes",
183
0x80: "extended-properties",
184
0x100: "reliable-write",
185
0x200: "writable-auxiliaries",
186
}
187
188
def gatt_char_props_to_strs(props: int) -> frozenset[CharacteristicPropertyName]:
189
"""
190
Convert GATT characteristic properties bitmask to set of property names.
191
192
Args:
193
props: Characteristic properties bitmask
194
195
Returns:
196
Frozenset of property name strings
197
"""
198
```
199
200
## Usage Examples
201
202
### UUID Normalization and Conversion
203
204
```python
205
from bleak.uuids import normalize_uuid_str, normalize_uuid_16, normalize_uuid_32
206
207
def demonstrate_uuid_normalization():
208
# Normalize different UUID formats
209
uuid_16bit = normalize_uuid_str("180F") # Battery Service
210
print(f"16-bit UUID: {uuid_16bit}")
211
# Output: 0000180f-0000-1000-8000-00805f9b34fb
212
213
uuid_32bit = normalize_uuid_str("12345678")
214
print(f"32-bit UUID: {uuid_32bit}")
215
# Output: 12345678-0000-1000-8000-00805f9b34fb
216
217
uuid_128bit = normalize_uuid_str("12345678-1234-5678-9012-123456789ABC")
218
print(f"128-bit UUID: {uuid_128bit}")
219
# Output: 12345678-1234-5678-9012-123456789abc
220
221
# Normalize from integers
222
from_int_16 = normalize_uuid_16(0x180F)
223
print(f"From 16-bit int: {from_int_16}")
224
225
from_int_32 = normalize_uuid_32(0x12345678)
226
print(f"From 32-bit int: {from_int_32}")
227
228
demonstrate_uuid_normalization()
229
```
230
231
### UUID Description Lookup
232
233
```python
234
from bleak.uuids import uuidstr_to_str
235
236
def demonstrate_uuid_lookup():
237
# Look up standard Bluetooth services
238
gap_service = uuidstr_to_str("00001800-0000-1000-8000-00805f9b34fb")
239
print(f"GAP Service: {gap_service}")
240
# Output: Generic Access Profile
241
242
battery_service = uuidstr_to_str("0000180f-0000-1000-8000-00805f9b34fb")
243
print(f"Battery Service: {battery_service}")
244
# Output: Battery Service
245
246
# Look up standard characteristics
247
device_name = uuidstr_to_str("00002a00-0000-1000-8000-00805f9b34fb")
248
print(f"Device Name: {device_name}")
249
# Output: Device Name
250
251
battery_level = uuidstr_to_str("00002a19-0000-1000-8000-00805f9b34fb")
252
print(f"Battery Level: {battery_level}")
253
# Output: Battery Level
254
255
# Look up vendor-specific UUID
256
nordic_uart = uuidstr_to_str("6e400001-b5a3-f393-e0a9-e50e24dcca9e")
257
print(f"Nordic UART: {nordic_uart}")
258
# Output: Nordic UART Service
259
260
# Unknown UUID
261
unknown = uuidstr_to_str("12345678-1234-5678-9012-123456789abc")
262
print(f"Unknown UUID: {unknown}")
263
# Output: Unknown
264
265
demonstrate_uuid_lookup()
266
```
267
268
### Registering Custom UUIDs
269
270
```python
271
from bleak.uuids import register_uuids, uuidstr_to_str
272
273
def demonstrate_custom_uuids():
274
# Register custom UUID mappings
275
custom_uuids = {
276
"12345678-1234-5678-9012-123456789abc": "My IoT Device Service",
277
"87654321-4321-8765-2109-cba987654321": "Temperature Sensor Characteristic",
278
"11111111-2222-3333-4444-555555555555": "Custom Control Point"
279
}
280
281
register_uuids(custom_uuids)
282
283
# Now lookup returns custom descriptions
284
service_desc = uuidstr_to_str("12345678-1234-5678-9012-123456789abc")
285
print(f"Custom Service: {service_desc}")
286
# Output: My IoT Device Service
287
288
char_desc = uuidstr_to_str("87654321-4321-8765-2109-cba987654321")
289
print(f"Custom Characteristic: {char_desc}")
290
# Output: Temperature Sensor Characteristic
291
292
demonstrate_custom_uuids()
293
```
294
295
### Working with Characteristic Properties
296
297
```python
298
from bleak.assigned_numbers import gatt_char_props_to_strs, CHARACTERISTIC_PROPERTIES
299
300
def demonstrate_characteristic_properties():
301
# Example properties bitmask (read + write + notify)
302
props_mask = 0x1A # 0x02 (read) + 0x08 (write) + 0x10 (notify)
303
304
# Convert to property names
305
properties = gatt_char_props_to_strs(props_mask)
306
print(f"Properties: {properties}")
307
# Output: frozenset({'read', 'write', 'notify'})
308
309
# Check individual properties
310
if "read" in properties:
311
print("Characteristic supports reading")
312
313
if "write" in properties:
314
print("Characteristic supports write-with-response")
315
316
if "notify" in properties:
317
print("Characteristic supports notifications")
318
319
if "write-without-response" not in properties:
320
print("Characteristic does not support write-without-response")
321
322
# Show all available property mappings
323
print("\nAll property mappings:")
324
for bit, name in CHARACTERISTIC_PROPERTIES.items():
325
print(f" 0x{bit:02X}: {name}")
326
327
demonstrate_characteristic_properties()
328
```
329
330
### Practical UUID Usage in BLE Operations
331
332
```python
333
import asyncio
334
from bleak import BleakClient
335
from bleak.uuids import normalize_uuid_str, uuidstr_to_str
336
337
async def uuid_practical_usage():
338
address = "00:11:22:33:44:55" # Replace with actual device address
339
340
async with BleakClient(address) as client:
341
print(f"Connected to {client.name}")
342
343
# Use UUID utilities with service discovery
344
for service in client.services:
345
service_desc = uuidstr_to_str(service.uuid)
346
print(f"\nService: {service.uuid}")
347
print(f" Description: {service_desc}")
348
349
for char in service.characteristics:
350
char_desc = uuidstr_to_str(char.uuid)
351
print(f" Characteristic: {char.uuid}")
352
print(f" Description: {char_desc}")
353
print(f" Properties: {char.properties}")
354
355
# Demonstrate reading standard characteristics
356
if char.uuid == normalize_uuid_str("2A00"): # Device Name
357
try:
358
data = await client.read_gatt_char(char.uuid)
359
name = data.decode('utf-8')
360
print(f" Device Name: {name}")
361
except Exception as e:
362
print(f" Could not read device name: {e}")
363
364
elif char.uuid == normalize_uuid_str("2A19"): # Battery Level
365
try:
366
data = await client.read_gatt_char(char.uuid)
367
level = int.from_bytes(data, byteorder='little')
368
print(f" Battery Level: {level}%")
369
except Exception as e:
370
print(f" Could not read battery level: {e}")
371
372
# asyncio.run(uuid_practical_usage()) # Uncomment with real device
373
```
374
375
### Advertisement Data Type Usage
376
377
```python
378
from bleak.assigned_numbers import AdvertisementDataType
379
380
def process_advertisement_data(adv_data_raw):
381
"""Example of processing raw advertisement data using constants."""
382
383
# Parse advertisement data (simplified example)
384
pos = 0
385
while pos < len(adv_data_raw):
386
length = adv_data_raw[pos]
387
if length == 0:
388
break
389
390
ad_type = adv_data_raw[pos + 1]
391
data = adv_data_raw[pos + 2:pos + 1 + length]
392
393
# Use constants for readable code
394
if ad_type == AdvertisementDataType.COMPLETE_LOCAL_NAME:
395
device_name = data.decode('utf-8')
396
print(f"Device Name: {device_name}")
397
398
elif ad_type == AdvertisementDataType.COMPLETE_LIST_SERVICE_UUID16:
399
# Parse 16-bit service UUIDs
400
for i in range(0, len(data), 2):
401
uuid_16 = int.from_bytes(data[i:i+2], byteorder='little')
402
print(f"Service UUID: {uuid_16:04X}")
403
404
elif ad_type == AdvertisementDataType.TX_POWER_LEVEL:
405
tx_power = int.from_bytes(data, byteorder='little', signed=True)
406
print(f"TX Power: {tx_power} dBm")
407
408
elif ad_type == AdvertisementDataType.MANUFACTURER_SPECIFIC_DATA:
409
company_id = int.from_bytes(data[:2], byteorder='little')
410
mfg_data = data[2:]
411
print(f"Manufacturer {company_id:04X}: {mfg_data.hex()}")
412
413
pos += 1 + length
414
415
# Example usage with mock data
416
mock_adv_data = bytes([
417
0x05, 0x09, 0x54, 0x65, 0x73, 0x74, # Complete local name: "Test"
418
0x03, 0x03, 0x0F, 0x18, # Complete 16-bit service UUID: 0x180F
419
0x02, 0x0A, 0x00, # TX Power: 0 dBm
420
])
421
422
process_advertisement_data(mock_adv_data)
423
```
424
425
## Types
426
427
```python { .api }
428
# UUID dictionaries
429
uuid16_dict: dict[int, str]
430
uuid128_dict: dict[str, str]
431
432
# Characteristic property type
433
CharacteristicPropertyName = Literal[
434
"broadcast", "read", "write-without-response", "write",
435
"notify", "indicate", "authenticated-signed-writes",
436
"extended-properties", "reliable-write", "writable-auxiliaries",
437
"encrypt-read", "encrypt-write", "encrypt-authenticated-read",
438
"encrypt-authenticated-write", "authorize"
439
]
440
441
# Property mapping
442
CHARACTERISTIC_PROPERTIES: dict[int, CharacteristicPropertyName]
443
444
# Advertisement data types
445
class AdvertisementDataType(IntEnum): ...
446
```