bumble 0.0.214__py3-none-any.whl → 0.0.216__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. bumble/_version.py +16 -3
  2. bumble/a2dp.py +15 -16
  3. bumble/apps/auracast.py +13 -38
  4. bumble/apps/bench.py +9 -10
  5. bumble/apps/ble_rpa_tool.py +1 -0
  6. bumble/apps/console.py +22 -25
  7. bumble/apps/controller_info.py +19 -19
  8. bumble/apps/controller_loopback.py +2 -2
  9. bumble/apps/controllers.py +1 -1
  10. bumble/apps/device_info.py +3 -3
  11. bumble/apps/gatt_dump.py +1 -1
  12. bumble/apps/gg_bridge.py +5 -6
  13. bumble/apps/hci_bridge.py +3 -3
  14. bumble/apps/l2cap_bridge.py +3 -3
  15. bumble/apps/lea_unicast/app.py +15 -25
  16. bumble/apps/pair.py +30 -43
  17. bumble/apps/pandora_server.py +5 -4
  18. bumble/apps/player/player.py +19 -22
  19. bumble/apps/rfcomm_bridge.py +3 -8
  20. bumble/apps/scan.py +16 -6
  21. bumble/apps/show.py +3 -4
  22. bumble/apps/speaker/speaker.py +22 -22
  23. bumble/apps/unbond.py +2 -1
  24. bumble/apps/usb_probe.py +1 -2
  25. bumble/att.py +241 -246
  26. bumble/audio/io.py +5 -9
  27. bumble/avc.py +2 -2
  28. bumble/avctp.py +6 -7
  29. bumble/avdtp.py +19 -22
  30. bumble/avrcp.py +1096 -588
  31. bumble/codecs.py +2 -0
  32. bumble/controller.py +52 -13
  33. bumble/core.py +567 -248
  34. bumble/crypto/__init__.py +2 -2
  35. bumble/crypto/builtin.py +1 -1
  36. bumble/crypto/cryptography.py +2 -4
  37. bumble/data_types.py +1025 -0
  38. bumble/device.py +318 -279
  39. bumble/drivers/__init__.py +3 -2
  40. bumble/drivers/intel.py +3 -4
  41. bumble/drivers/rtk.py +26 -9
  42. bumble/gap.py +4 -4
  43. bumble/gatt.py +3 -2
  44. bumble/gatt_adapters.py +3 -11
  45. bumble/gatt_client.py +69 -81
  46. bumble/gatt_server.py +124 -124
  47. bumble/hci.py +67 -18
  48. bumble/helpers.py +19 -26
  49. bumble/hfp.py +10 -21
  50. bumble/hid.py +22 -16
  51. bumble/host.py +181 -103
  52. bumble/keys.py +5 -3
  53. bumble/l2cap.py +121 -74
  54. bumble/link.py +8 -9
  55. bumble/pairing.py +7 -6
  56. bumble/pandora/__init__.py +8 -7
  57. bumble/pandora/config.py +3 -1
  58. bumble/pandora/device.py +3 -2
  59. bumble/pandora/host.py +38 -36
  60. bumble/pandora/l2cap.py +22 -21
  61. bumble/pandora/security.py +15 -15
  62. bumble/pandora/utils.py +5 -3
  63. bumble/profiles/aics.py +11 -11
  64. bumble/profiles/ams.py +7 -8
  65. bumble/profiles/ancs.py +6 -7
  66. bumble/profiles/ascs.py +4 -9
  67. bumble/profiles/asha.py +8 -12
  68. bumble/profiles/bap.py +11 -23
  69. bumble/profiles/bass.py +2 -7
  70. bumble/profiles/battery_service.py +3 -4
  71. bumble/profiles/cap.py +1 -2
  72. bumble/profiles/csip.py +2 -6
  73. bumble/profiles/device_information_service.py +2 -2
  74. bumble/profiles/gap.py +4 -4
  75. bumble/profiles/gatt_service.py +1 -4
  76. bumble/profiles/gmap.py +5 -5
  77. bumble/profiles/hap.py +62 -59
  78. bumble/profiles/heart_rate_service.py +5 -4
  79. bumble/profiles/le_audio.py +3 -1
  80. bumble/profiles/mcp.py +3 -7
  81. bumble/profiles/pacs.py +3 -6
  82. bumble/profiles/pbp.py +2 -0
  83. bumble/profiles/tmap.py +2 -3
  84. bumble/profiles/vcs.py +2 -8
  85. bumble/profiles/vocs.py +8 -8
  86. bumble/rfcomm.py +11 -14
  87. bumble/rtp.py +1 -0
  88. bumble/sdp.py +10 -8
  89. bumble/smp.py +142 -153
  90. bumble/snoop.py +5 -5
  91. bumble/tools/generate_company_id_list.py +1 -0
  92. bumble/tools/intel_fw_download.py +3 -3
  93. bumble/tools/intel_util.py +4 -4
  94. bumble/tools/rtk_fw_download.py +6 -3
  95. bumble/tools/rtk_util.py +24 -7
  96. bumble/transport/__init__.py +19 -15
  97. bumble/transport/android_emulator.py +8 -13
  98. bumble/transport/android_netsim.py +19 -18
  99. bumble/transport/common.py +12 -15
  100. bumble/transport/file.py +1 -1
  101. bumble/transport/hci_socket.py +4 -6
  102. bumble/transport/pty.py +5 -6
  103. bumble/transport/pyusb.py +7 -10
  104. bumble/transport/serial.py +2 -1
  105. bumble/transport/tcp_client.py +2 -2
  106. bumble/transport/tcp_server.py +11 -14
  107. bumble/transport/udp.py +3 -3
  108. bumble/transport/unix.py +67 -1
  109. bumble/transport/usb.py +6 -6
  110. bumble/transport/vhci.py +0 -1
  111. bumble/transport/ws_client.py +2 -1
  112. bumble/transport/ws_server.py +3 -2
  113. bumble/utils.py +20 -5
  114. bumble/vendor/android/hci.py +1 -2
  115. bumble/vendor/zephyr/hci.py +0 -1
  116. {bumble-0.0.214.dist-info → bumble-0.0.216.dist-info}/METADATA +2 -1
  117. bumble-0.0.216.dist-info/RECORD +183 -0
  118. bumble-0.0.214.dist-info/RECORD +0 -182
  119. {bumble-0.0.214.dist-info → bumble-0.0.216.dist-info}/WHEEL +0 -0
  120. {bumble-0.0.214.dist-info → bumble-0.0.216.dist-info}/entry_points.txt +0 -0
  121. {bumble-0.0.214.dist-info → bumble-0.0.216.dist-info}/licenses/LICENSE +0 -0
  122. {bumble-0.0.214.dist-info → bumble-0.0.216.dist-info}/top_level.txt +0 -0
bumble/data_types.py ADDED
@@ -0,0 +1,1025 @@
1
+ # Copyright 2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """
15
+ Classes representing "Data Types" defined in
16
+ "Supplement to the Bluetooth Core Specification", Part A and
17
+ "Assigned Numbers", 2.3 Common Data Types.
18
+ """
19
+
20
+ # -----------------------------------------------------------------------------
21
+ # Imports
22
+ # -----------------------------------------------------------------------------
23
+ from __future__ import annotations
24
+
25
+ import dataclasses
26
+ import math
27
+ import struct
28
+ from typing import Any, ClassVar, Sequence
29
+
30
+ from typing_extensions import Self
31
+
32
+ from bumble import company_ids, core, hci
33
+
34
+
35
+ # -----------------------------------------------------------------------------
36
+ class GenericAdvertisingData(core.DataType):
37
+ """Data Type for which there is no specific subclass"""
38
+
39
+ label = "Generic Advertising Data"
40
+ ad_data: bytes
41
+
42
+ def __init__(self, ad_data: bytes, ad_type: core.AdvertisingData.Type) -> None:
43
+ self.ad_data = ad_data
44
+ self.ad_type = ad_type
45
+
46
+ def value_string(self) -> str:
47
+ return f"type={self.ad_type.name}, data={self.ad_data.hex().upper()}"
48
+
49
+ @classmethod
50
+ def from_bytes(
51
+ cls,
52
+ ad_data: bytes,
53
+ ad_type: core.AdvertisingData.Type = core.AdvertisingData.Type(0),
54
+ ) -> GenericAdvertisingData:
55
+ return cls(ad_data, ad_type)
56
+
57
+ def __bytes__(self) -> bytes:
58
+ return self.ad_data
59
+
60
+ def __eq__(self, other: Any) -> bool:
61
+ return (
62
+ isinstance(other, GenericAdvertisingData)
63
+ and self.ad_type == other.ad_type
64
+ and self.ad_data == other.ad_data
65
+ )
66
+
67
+
68
+ @dataclasses.dataclass
69
+ class ListOfServiceUUIDs(core.DataType):
70
+ """Base class for complete or incomplete lists of UUIDs."""
71
+
72
+ _uuid_size: ClassVar[int] = 0
73
+ uuids: Sequence[core.UUID]
74
+
75
+ @classmethod
76
+ def from_bytes(cls, data: bytes) -> ListOfServiceUUIDs:
77
+ return cls(
78
+ [
79
+ core.UUID.from_bytes(data[x : x + cls._uuid_size])
80
+ for x in range(0, len(data), cls._uuid_size)
81
+ ]
82
+ )
83
+
84
+ def __post_init__(self) -> None:
85
+ for uuid in self.uuids:
86
+ if len(uuid.uuid_bytes) != self._uuid_size:
87
+ raise TypeError("incompatible UUID type")
88
+
89
+ def __bytes__(self) -> bytes:
90
+ return b"".join(bytes(uuid) for uuid in self.uuids)
91
+
92
+ def value_string(self) -> str:
93
+ return ", ".join(list(map(str, self.uuids)))
94
+
95
+
96
+ class IncompleteListOf16BitServiceUUIDs(ListOfServiceUUIDs):
97
+ """
98
+ See Supplement to the Bluetooth Core Specification, Part A
99
+ 1.1 SERVICE OR SERVICE CLASS UUID
100
+ """
101
+
102
+ _uuid_size = 2
103
+ label = "Incomplete List Of 16-bit Service or Service Class UUIDs"
104
+ ad_type = core.AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS
105
+
106
+
107
+ class CompleteListOf16BitServiceUUIDs(ListOfServiceUUIDs):
108
+ """
109
+ See Supplement to the Bluetooth Core Specification, Part A
110
+ 1.1 SERVICE OR SERVICE CLASS UUID
111
+ """
112
+
113
+ _uuid_size = 2
114
+ label = "Complete List Of 16-bit Service or Service Class UUIDs"
115
+ ad_type = core.AdvertisingData.COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS
116
+
117
+
118
+ class IncompleteListOf32BitServiceUUIDs(ListOfServiceUUIDs):
119
+ """
120
+ See Supplement to the Bluetooth Core Specification, Part A
121
+ 1.1 SERVICE OR SERVICE CLASS UUID
122
+ """
123
+
124
+ _uuid_size = 4
125
+ label = "Incomplete List Of 32-bit Service or Service Class UUIDs"
126
+ ad_type = core.AdvertisingData.INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS
127
+
128
+
129
+ class CompleteListOf32BitServiceUUIDs(ListOfServiceUUIDs):
130
+ """
131
+ See Supplement to the Bluetooth Core Specification, Part A
132
+ 1.1 SERVICE OR SERVICE CLASS UUID
133
+ """
134
+
135
+ _uuid_size = 4
136
+ label = "Complete List Of 32-bit Service or Service Class UUIDs"
137
+ ad_type = core.AdvertisingData.COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS
138
+
139
+
140
+ class IncompleteListOf128BitServiceUUIDs(ListOfServiceUUIDs):
141
+ """
142
+ See Supplement to the Bluetooth Core Specification, Part A
143
+ 1.1 SERVICE OR SERVICE CLASS UUID
144
+ """
145
+
146
+ _uuid_size = 16
147
+ label = "Incomplete List Of 128-bit Service or Service Class UUIDs"
148
+ ad_type = core.AdvertisingData.INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS
149
+
150
+
151
+ class CompleteListOf128BitServiceUUIDs(ListOfServiceUUIDs):
152
+ """
153
+ See Supplement to the Bluetooth Core Specification, Part A
154
+ 1.1 SERVICE OR SERVICE CLASS UUID
155
+ """
156
+
157
+ _uuid_size = 16
158
+ label = "Complete List Of 128-bit Service or Service Class UUIDs"
159
+ ad_type = core.AdvertisingData.COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS
160
+
161
+
162
+ class StringDataType(str, core.DataType):
163
+ @classmethod
164
+ def from_bytes(cls, data: bytes) -> Self:
165
+ return cls(data.decode("utf-8"))
166
+
167
+ def __bytes__(self) -> bytes:
168
+ return self.encode("utf-8")
169
+
170
+ def __str__(self) -> str:
171
+ return core.DataType.__str__(self)
172
+
173
+ def value_string(self) -> str:
174
+ return repr(self)
175
+
176
+
177
+ class CompleteLocalName(StringDataType):
178
+ """
179
+ See Supplement to the Bluetooth Core Specification, Part A
180
+ 1.2 LOCAL NAME
181
+ """
182
+
183
+ label = "Complete Local Name"
184
+ ad_type = core.AdvertisingData.COMPLETE_LOCAL_NAME
185
+
186
+
187
+ class ShortenedLocalName(StringDataType):
188
+ """
189
+ See Supplement to the Bluetooth Core Specification, Part A
190
+ 1.2 LOCAL NAME
191
+ """
192
+
193
+ label = "Shortened Local Name"
194
+ ad_type = core.AdvertisingData.SHORTENED_LOCAL_NAME
195
+
196
+
197
+ class Flags(int, core.DataType):
198
+ """
199
+ See Supplement to the Bluetooth Core Specification, Part A
200
+ 1.3 FLAGS
201
+ """
202
+
203
+ label = "Flags"
204
+ ad_type = core.AdvertisingData.FLAGS
205
+
206
+ def __init__(self, flags: core.AdvertisingData.Flags) -> None:
207
+ pass
208
+
209
+ @classmethod
210
+ def from_bytes(cls, data: bytes) -> Flags: # type: ignore[override]
211
+ return cls(core.AdvertisingData.Flags(int.from_bytes(data, byteorder="little")))
212
+
213
+ def __bytes__(self) -> bytes:
214
+ bytes_length = 1 if self == 0 else math.ceil(self.bit_length() / 8)
215
+ return self.to_bytes(length=bytes_length, byteorder="little")
216
+
217
+ def value_string(self) -> str:
218
+ return core.AdvertisingData.Flags(self).composite_name
219
+
220
+
221
+ @dataclasses.dataclass
222
+ class ManufacturerSpecificData(core.DataType):
223
+ """
224
+ See Supplement to the Bluetooth Core Specification, Part A
225
+ 1.4 MANUFACTURER SPECIFIC DATA
226
+ """
227
+
228
+ label = "Manufacturer Specific Data"
229
+ ad_type = core.AdvertisingData.Type.MANUFACTURER_SPECIFIC_DATA
230
+
231
+ company_identifier: int
232
+ data: bytes
233
+
234
+ @classmethod
235
+ def from_bytes(cls, data: bytes) -> ManufacturerSpecificData:
236
+ company_identifier = int.from_bytes(data[:2], "little")
237
+ return cls(company_identifier, data[2:])
238
+
239
+ def __bytes__(self) -> bytes:
240
+ return self.company_identifier.to_bytes(2, "little") + self.data
241
+
242
+ def value_string(self) -> str:
243
+ if company := company_ids.COMPANY_IDENTIFIERS.get(self.company_identifier):
244
+ company_str = repr(company)
245
+ else:
246
+ company_str = f'0x{self.company_identifier:04X}'
247
+ return f"company={company_str}, data={self.data.hex().upper()}"
248
+
249
+
250
+ class FixedSizeIntDataType(int, core.DataType):
251
+ _fixed_size: int = 0
252
+ _signed: bool = False
253
+
254
+ @classmethod
255
+ def from_bytes(cls, data: bytes) -> Self: # type: ignore[override]
256
+ if len(data) != cls._fixed_size:
257
+ raise ValueError(f"data must be {cls._fixed_size} byte")
258
+ return cls(int.from_bytes(data, byteorder="little", signed=cls._signed))
259
+
260
+ def __bytes__(self) -> bytes:
261
+ return self.to_bytes(
262
+ length=self._fixed_size, byteorder="little", signed=self._signed
263
+ )
264
+
265
+ def value_string(self) -> str:
266
+ return str(int(self))
267
+
268
+
269
+ class TxPowerLevel(FixedSizeIntDataType):
270
+ """
271
+ See Supplement to the Bluetooth Core Specification, Part A
272
+ 1.5 TX POWER LEVEL
273
+ """
274
+
275
+ _fixed_size = 1
276
+ _signed = True
277
+ label = "TX Power Level"
278
+ ad_type = core.AdvertisingData.Type.TX_POWER_LEVEL
279
+
280
+
281
+ class FixedSizeBytesDataType(bytes, core.DataType):
282
+ _fixed_size: int = 0
283
+
284
+ @classmethod
285
+ def from_bytes(cls, data: bytes) -> Self:
286
+ if len(data) != cls._fixed_size:
287
+ raise ValueError(f"data must be {cls._fixed_size} bytes")
288
+ return cls(data)
289
+
290
+ def value_string(self) -> str:
291
+ return self.hex().upper()
292
+
293
+ def __str__(self) -> str:
294
+ return core.DataType.__str__(self)
295
+
296
+ def __bytes__(self) -> bytes: # pylint: disable=E0308
297
+ # Python < 3.11 compatibility (before 3.11, the byte class does not have
298
+ # a __bytes__ method).
299
+ # Concatenate with an empty string to perform a direct conversion without
300
+ # calling bytes() explicity, which may cause an infinite recursion.
301
+ return b"" + self
302
+
303
+
304
+ class ClassOfDevice(core.ClassOfDevice, core.DataType):
305
+ """
306
+ See Supplement to the Bluetooth Core Specification, Part A
307
+ 1.6 SECURE SIMPLE PAIRING OUT OF BAND (OOB)
308
+ """
309
+
310
+ label = "Class of Device"
311
+ ad_type = core.AdvertisingData.Type.CLASS_OF_DEVICE
312
+
313
+ @classmethod
314
+ def from_bytes(cls, data: bytes) -> ClassOfDevice:
315
+ return cls.from_int(int.from_bytes(data, byteorder="little"))
316
+
317
+ def __bytes__(self) -> bytes:
318
+ return int(self).to_bytes(3, byteorder="little")
319
+
320
+ def __eq__(self, value: Any) -> bool:
321
+ return core.ClassOfDevice.__eq__(self, value)
322
+
323
+ def value_string(self) -> str:
324
+ return (
325
+ f"{self.major_service_classes_labels()},"
326
+ f"{self.major_device_class_label()}/"
327
+ f"{self.minor_device_class_label()}"
328
+ )
329
+
330
+ def __str__(self) -> str:
331
+ return core.DataType.__str__(self)
332
+
333
+
334
+ class SecureSimplePairingHashC192(FixedSizeBytesDataType):
335
+ """
336
+ See Supplement to the Bluetooth Core Specification, Part A
337
+ 1.6 SECURE SIMPLE PAIRING OUT OF BAND (OOB)
338
+ """
339
+
340
+ _fixed_size = 16
341
+ label = "Secure Simple Pairing Hash C-192"
342
+ ad_type = core.AdvertisingData.Type.SIMPLE_PAIRING_HASH_C_192
343
+
344
+
345
+ class SecureSimplePairingRandomizerR192(FixedSizeBytesDataType):
346
+ """
347
+ See Supplement to the Bluetooth Core Specification, Part A
348
+ 1.6 SECURE SIMPLE PAIRING OUT OF BAND (OOB)
349
+ """
350
+
351
+ _fixed_size = 16
352
+ label = "Secure Simple Pairing Randomizer R-192"
353
+ ad_type = core.AdvertisingData.Type.SIMPLE_PAIRING_RANDOMIZER_R_192
354
+
355
+
356
+ class SecureSimplePairingHashC256(FixedSizeBytesDataType):
357
+ """
358
+ See Supplement to the Bluetooth Core Specification, Part A
359
+ 1.6 SECURE SIMPLE PAIRING OUT OF BAND (OOB)
360
+ """
361
+
362
+ _fixed_size = 16
363
+ label = "Secure Simple Pairing Hash C-256"
364
+ ad_type = core.AdvertisingData.Type.SIMPLE_PAIRING_HASH_C_256
365
+
366
+
367
+ class SecureSimplePairingRandomizerR256(FixedSizeBytesDataType):
368
+ """
369
+ See Supplement to the Bluetooth Core Specification, Part A
370
+ 1.6 SECURE SIMPLE PAIRING OUT OF BAND (OOB)
371
+ """
372
+
373
+ _fixed_size = 16
374
+ label = "Secure Simple Pairing Randomizer R-256"
375
+ ad_type = core.AdvertisingData.Type.SIMPLE_PAIRING_RANDOMIZER_R_256
376
+
377
+
378
+ class LeSecureConnectionsConfirmationValue(FixedSizeBytesDataType):
379
+ """
380
+ See Supplement to the Bluetooth Core Specification, Part A
381
+ 1.6 SECURE SIMPLE PAIRING OUT OF BAND (OOB)
382
+ """
383
+
384
+ _fixed_size = 16
385
+ label = "LE Secure Connections Confirmation Value"
386
+ ad_type = core.AdvertisingData.Type.LE_SECURE_CONNECTIONS_CONFIRMATION_VALUE
387
+
388
+
389
+ class LeSecureConnectionsRandomValue(FixedSizeBytesDataType):
390
+ """
391
+ See Supplement to the Bluetooth Core Specification, Part A
392
+ 1.6 SECURE SIMPLE PAIRING OUT OF BAND (OOB)
393
+ """
394
+
395
+ _fixed_size = 16
396
+ label = "LE Secure Connections Random Value"
397
+ ad_type = core.AdvertisingData.Type.LE_SECURE_CONNECTIONS_RANDOM_VALUE
398
+
399
+
400
+ class SecurityManagerOutOfBandFlag(int, core.DataType):
401
+ """
402
+ See Supplement to the Bluetooth Core Specification, Part A
403
+ 1.7 SECURITY MANAGER OUT OF BAND (OOB)
404
+ """
405
+
406
+ label = "Security Manager Out of Band Flag"
407
+ ad_type = core.AdvertisingData.Type.SECURITY_MANAGER_OUT_OF_BAND_FLAGS
408
+
409
+ def __init__(self, flag: core.SecurityManagerOutOfBandFlag) -> None:
410
+ pass
411
+
412
+ @classmethod
413
+ # type: ignore[override]
414
+ def from_bytes(cls, data: bytes) -> SecurityManagerOutOfBandFlag:
415
+ if len(data) != 1:
416
+ raise ValueError("data must be 1 byte")
417
+ return SecurityManagerOutOfBandFlag(core.SecurityManagerOutOfBandFlag(data[0]))
418
+
419
+ def __bytes__(self) -> bytes:
420
+ return bytes([self])
421
+
422
+ def __str__(self) -> str:
423
+ return core.DataType.__str__(self)
424
+
425
+ def value_string(self) -> str:
426
+ return core.SecurityManagerOutOfBandFlag(self).composite_name
427
+
428
+
429
+ class SecurityManagerTKValue(FixedSizeBytesDataType):
430
+ """
431
+ See Supplement to the Bluetooth Core Specification, Part A
432
+ 1.8 SECURITY MANAGER TK VALUE
433
+ """
434
+
435
+ _fixed_size = 16
436
+ label = "Security Manager TK Value"
437
+ ad_type = core.AdvertisingData.Type.SECURITY_MANAGER_TK_VALUE
438
+
439
+
440
+ @dataclasses.dataclass
441
+ class PeripheralConnectionIntervalRange(core.DataType):
442
+ """
443
+ See Supplement to the Bluetooth Core Specification, Part A
444
+ 1.9 PERIPHERAL CONNECTION INTERVAL RANGE
445
+ """
446
+
447
+ label = "Peripheral Connection Interval Range"
448
+ ad_type = core.AdvertisingData.Type.PERIPHERAL_CONNECTION_INTERVAL_RANGE
449
+
450
+ connection_interval_min: int
451
+ connection_interval_max: int
452
+
453
+ @classmethod
454
+ def from_bytes(cls, data: bytes) -> PeripheralConnectionIntervalRange:
455
+ return cls(*struct.unpack("<HH", data))
456
+
457
+ def __bytes__(self) -> bytes:
458
+ return struct.pack(
459
+ "<HH", self.connection_interval_min, self.connection_interval_max
460
+ )
461
+
462
+ def value_string(self) -> str:
463
+ return (
464
+ f"connection_interval_min={self.connection_interval_min}, "
465
+ f"connection_interval_max={self.connection_interval_max}"
466
+ )
467
+
468
+
469
+ class ListOf16BitServiceSolicitationUUIDs(ListOfServiceUUIDs):
470
+ """
471
+ See Supplement to the Bluetooth Core Specification, Part A
472
+ 1.10 SERVICE SOLICITATION
473
+ """
474
+
475
+ _uuid_size = 2
476
+ label = "List of 16 bit Service Solicitation UUIDs"
477
+ ad_type = core.AdvertisingData.Type.LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS
478
+
479
+
480
+ class ListOf32BitServiceSolicitationUUIDs(ListOfServiceUUIDs):
481
+ """
482
+ See Supplement to the Bluetooth Core Specification, Part A
483
+ 1.10 SERVICE SOLICITATION
484
+ """
485
+
486
+ _uuid_size = 4
487
+ label = "List of 32 bit Service Solicitation UUIDs"
488
+ ad_type = core.AdvertisingData.Type.LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS
489
+
490
+
491
+ class ListOf128BitServiceSolicitationUUIDs(ListOfServiceUUIDs):
492
+ """
493
+ See Supplement to the Bluetooth Core Specification, Part A
494
+ 1.10 SERVICE SOLICITATION
495
+ """
496
+
497
+ _uuid_size = 16
498
+ label = "List of 128 bit Service Solicitation UUIDs"
499
+ ad_type = core.AdvertisingData.Type.LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS
500
+
501
+
502
+ @dataclasses.dataclass
503
+ class ServiceData(core.DataType):
504
+ """Base class for service data lists of UUIDs."""
505
+
506
+ _uuid_size: ClassVar[int] = 0
507
+
508
+ service_uuid: core.UUID
509
+ data: bytes
510
+
511
+ @classmethod
512
+ def from_bytes(cls, data: bytes) -> Self:
513
+ service_uuid = core.UUID.from_bytes(data[: cls._uuid_size])
514
+ return cls(service_uuid, data[cls._uuid_size :])
515
+
516
+ def __bytes__(self) -> bytes:
517
+ return self.service_uuid.to_bytes() + self.data
518
+
519
+ def value_string(self) -> str:
520
+ return f"service={self.service_uuid}, data={self.data.hex().upper()}"
521
+
522
+
523
+ class ServiceData16BitUUID(ServiceData):
524
+ """
525
+ See Supplement to the Bluetooth Core Specification, Part A
526
+ 1.11 SERVICE DATA
527
+ """
528
+
529
+ _uuid_size = 2
530
+ label = "Service Data - 16 bit UUID"
531
+ ad_type = core.AdvertisingData.Type.SERVICE_DATA_16_BIT_UUID
532
+
533
+
534
+ class ServiceData32BitUUID(ServiceData):
535
+ """
536
+ See Supplement to the Bluetooth Core Specification, Part A
537
+ 1.11 SERVICE DATA
538
+ """
539
+
540
+ _uuid_size = 4
541
+ label = "Service Data - 32 bit UUID"
542
+ ad_type = core.AdvertisingData.Type.SERVICE_DATA_32_BIT_UUID
543
+
544
+
545
+ class ServiceData128BitUUID(ServiceData):
546
+ """
547
+ See Supplement to the Bluetooth Core Specification, Part A
548
+ 1.11 SERVICE DATA
549
+ """
550
+
551
+ _uuid_size = 16
552
+ label = "Service Data - 128 bit UUID"
553
+ ad_type = core.AdvertisingData.Type.SERVICE_DATA_128_BIT_UUID
554
+
555
+
556
+ class Appearance(core.Appearance, core.DataType):
557
+ """
558
+ See Supplement to the Bluetooth Core Specification, Part A
559
+ 1.12 APPEARANCE
560
+ """
561
+
562
+ label = "Appearance"
563
+ ad_type = core.AdvertisingData.Type.APPEARANCE
564
+
565
+ @classmethod
566
+ def from_bytes(cls, data: bytes):
567
+ return cls.from_int(int.from_bytes(data, byteorder="little"))
568
+
569
+ def __bytes__(self) -> bytes:
570
+ return int(self).to_bytes(2, byteorder="little")
571
+
572
+ def __str__(self) -> str:
573
+ return core.DataType.__str__(self)
574
+
575
+ def value_string(self) -> str:
576
+ return core.Appearance.__str__(self)
577
+
578
+
579
+ class PublicTargetAddress(hci.Address, core.DataType):
580
+ """
581
+ See Supplement to the Bluetooth Core Specification, Part A
582
+ 1.13 PUBLIC TARGET ADDRESS
583
+ """
584
+
585
+ label = "Public Target Address"
586
+ ad_type = core.AdvertisingData.Type.PUBLIC_TARGET_ADDRESS
587
+
588
+ def __init__(self, address: hci.Address) -> None:
589
+ self.address_bytes = address.address_bytes
590
+ self.address_type = hci.Address.PUBLIC_DEVICE_ADDRESS
591
+
592
+ @classmethod
593
+ def from_bytes(cls, data: bytes) -> PublicTargetAddress:
594
+ return cls(hci.Address(data))
595
+
596
+ def __str__(self) -> str:
597
+ return core.DataType.__str__(self)
598
+
599
+ def to_string(self, use_label: bool = False) -> str:
600
+ return core.DataType.to_string(self, use_label)
601
+
602
+ def value_string(self) -> str:
603
+ return hci.Address.to_string(self, with_type_qualifier=False)
604
+
605
+
606
+ class RandomTargetAddress(hci.Address, core.DataType):
607
+ """
608
+ See Supplement to the Bluetooth Core Specification, Part A
609
+ 1.14 RANDOM TARGET ADDRESS
610
+ """
611
+
612
+ label = "Random Target Address"
613
+ ad_type = core.AdvertisingData.Type.RANDOM_TARGET_ADDRESS
614
+
615
+ def __init__(self, address: hci.Address) -> None:
616
+ self.address_bytes = address.address_bytes
617
+ self.address_type = hci.Address.RANDOM_DEVICE_ADDRESS
618
+
619
+ @classmethod
620
+ def from_bytes(cls, data: bytes) -> RandomTargetAddress:
621
+ return cls(hci.Address(data))
622
+
623
+ def __str__(self) -> str:
624
+ return core.DataType.__str__(self)
625
+
626
+ def to_string(self, use_label: bool = False) -> str:
627
+ return core.DataType.to_string(self, use_label)
628
+
629
+ def value_string(self) -> str:
630
+ return hci.Address.to_string(self, with_type_qualifier=False)
631
+
632
+
633
+ class AdvertisingInterval(FixedSizeIntDataType):
634
+ """
635
+ See Supplement to the Bluetooth Core Specification, Part A
636
+ 1.15 ADVERTISING INTERVAL
637
+ """
638
+
639
+ _fixed_size = 2
640
+ label = "Advertising Interval"
641
+ ad_type = core.AdvertisingData.Type.ADVERTISING_INTERVAL
642
+
643
+
644
+ class AdvertisingIntervalLong(int, core.DataType):
645
+ """
646
+ See Supplement to the Bluetooth Core Specification, Part A
647
+ 1.15 ADVERTISING INTERVAL
648
+ """
649
+
650
+ label = "Advertising Interval - long"
651
+ ad_type = core.AdvertisingData.Type.ADVERTISING_INTERVAL_LONG
652
+
653
+ @classmethod
654
+ def from_bytes(cls, data: bytes) -> Self: # type: ignore[override]
655
+ return cls(int.from_bytes(data, byteorder="little"))
656
+
657
+ def __bytes__(self) -> bytes:
658
+ return self.to_bytes(length=4 if self >= 0x1000000 else 3, byteorder="little")
659
+
660
+ def value_string(self) -> str:
661
+ return str(int(self))
662
+
663
+
664
+ class LeBluetoothDeviceAddress(hci.Address, core.DataType):
665
+ """
666
+ See Supplement to the Bluetooth Core Specification, Part A
667
+ 1.16 LE BLUETOOTH DEVICE ADDRESS
668
+ """
669
+
670
+ label = "LE Bluetooth Device Address"
671
+ ad_type = core.AdvertisingData.Type.LE_BLUETOOTH_DEVICE_ADDRESS
672
+
673
+ def __init__(self, address: hci.Address) -> None:
674
+ self.address_bytes = address.address_bytes
675
+ self.address_type = address.address_type
676
+
677
+ @classmethod
678
+ def from_bytes(cls, data: bytes) -> LeBluetoothDeviceAddress:
679
+ return cls(hci.Address(data[1:], hci.AddressType(data[0])))
680
+
681
+ def __bytes__(self) -> bytes:
682
+ return bytes([self.address_type]) + self.address_bytes
683
+
684
+ def __str__(self) -> str:
685
+ return core.DataType.__str__(self)
686
+
687
+ def to_string(self, use_label: bool = False) -> str:
688
+ return core.DataType.to_string(self, use_label)
689
+
690
+ def value_string(self) -> str:
691
+ return (
692
+ f"{hci.Address.to_string(self, with_type_qualifier=False)}"
693
+ f"/{'PUBLIC' if self.is_public else 'RANDOM'}"
694
+ )
695
+
696
+
697
+ class LeRole(int, core.DataType):
698
+ """
699
+ See Supplement to the Bluetooth Core Specification, Part A
700
+ 1.17 LE ROLE
701
+ """
702
+
703
+ label = "LE Role"
704
+ ad_type = core.AdvertisingData.Type.LE_ROLE
705
+
706
+ def __init__(self, role: core.LeRole) -> None:
707
+ pass
708
+
709
+ @classmethod
710
+ def from_bytes(cls, data: bytes) -> Self: # type: ignore[override]
711
+ return cls(core.LeRole(data[0]))
712
+
713
+ def __bytes__(self) -> bytes:
714
+ return bytes([self])
715
+
716
+ def value_string(self) -> str:
717
+ return core.LeRole(self).name
718
+
719
+
720
+ class Uri(str, core.DataType):
721
+ """
722
+ See Supplement to the Bluetooth Core Specification, Part A
723
+ 1.18 UNIFORM RESOURCE IDENTIFIER (URI)
724
+ """
725
+
726
+ label = "URI"
727
+ ad_type = core.AdvertisingData.Type.URI
728
+
729
+ @classmethod
730
+ def from_bytes(cls, data: bytes) -> Self:
731
+ return cls(data.decode("utf-8"))
732
+
733
+ def __bytes__(self):
734
+ return self.encode("utf-8")
735
+
736
+ def __str__(self) -> str:
737
+ return core.DataType.__str__(self)
738
+
739
+ def value_string(self) -> str:
740
+ return repr(self)
741
+
742
+
743
+ class LeSupportedFeatures(int, core.DataType):
744
+ """
745
+ See Supplement to the Bluetooth Core Specification, Part A
746
+ 1.19 LE SUPPORTED FEATURES
747
+ """
748
+
749
+ label = "LE Supported Features"
750
+ ad_type = core.AdvertisingData.Type.LE_SUPPORTED_FEATURES
751
+
752
+ @classmethod
753
+ def from_bytes(cls, data: bytes) -> LeSupportedFeatures: # type: ignore[override]
754
+ return cls(int.from_bytes(data, byteorder="little"))
755
+
756
+ def __bytes__(self) -> bytes:
757
+ bytes_length = 1 if self == 0 else math.ceil(self.bit_length() / 8)
758
+ return self.to_bytes(length=bytes_length, byteorder="little")
759
+
760
+ def value_string(self) -> str:
761
+ return hci.LeFeatureMask(self).composite_name
762
+
763
+
764
+ @dataclasses.dataclass
765
+ class ChannelMapUpdateIndication(core.DataType):
766
+ """
767
+ See Supplement to the Bluetooth Core Specification, Part A
768
+ 1.20 CHANNEL MAP UPDATE INDICATION
769
+ """
770
+
771
+ label = "Channel Map Update Indication"
772
+ ad_type = core.AdvertisingData.Type.CHANNEL_MAP_UPDATE_INDICATION
773
+
774
+ chm: int
775
+ instant: int
776
+
777
+ @classmethod
778
+ def from_bytes(cls, data: bytes) -> ChannelMapUpdateIndication:
779
+ return cls(
780
+ int.from_bytes(data[:5], byteorder="little"),
781
+ int.from_bytes(data[5:7], byteorder="little"),
782
+ )
783
+
784
+ def __bytes__(self) -> bytes:
785
+ return self.chm.to_bytes(5, byteorder="little") + self.instant.to_bytes(
786
+ 2, byteorder="little"
787
+ )
788
+
789
+ def value_string(self) -> str:
790
+ return f"chm={self.chm:010X}, instant={self.instant}"
791
+
792
+
793
+ class BigInfo(core.DataType):
794
+ """
795
+ See Supplement to the Bluetooth Core Specification, Part A
796
+ 1.21 BIGINFO
797
+ """
798
+
799
+ # TODO
800
+
801
+
802
+ class BroadcastCode(str, core.DataType):
803
+ """
804
+ See Supplement to the Bluetooth Core Specification, Part A
805
+ 1.22 BROADCAST_CODE
806
+ """
807
+
808
+ label = "Broadcast Code"
809
+ ad_type = core.AdvertisingData.Type.BROADCAST_CODE
810
+
811
+ def __init__(self, value: str) -> None:
812
+ encoded = value.encode("utf-8")
813
+ if len(encoded) > 16:
814
+ raise ValueError("broadcast code must be <= 16 bytes in utf-8 encoding")
815
+
816
+ @classmethod
817
+ def from_bytes(cls, data: bytes) -> Self:
818
+ return cls(data.strip(bytes([0])).decode("utf-8"))
819
+
820
+ def __bytes__(self) -> bytes:
821
+ return self.encode("utf-8")
822
+
823
+ def __str__(self) -> str:
824
+ return core.DataType.__str__(self)
825
+
826
+ def value_string(self) -> str:
827
+ return repr(self)
828
+
829
+
830
+ @dataclasses.dataclass
831
+ class EncryptedData(core.DataType):
832
+ """
833
+ See Supplement to the Bluetooth Core Specification, Part A
834
+ 1.23 ENCRYPTED DATA
835
+ """
836
+
837
+ label = "Encrypted Data"
838
+ ad_type = core.AdvertisingData.Type.ENCRYPTED_ADVERTISING_DATA
839
+
840
+ randomizer: int
841
+ payload: bytes
842
+ mic: bytes
843
+
844
+ @classmethod
845
+ def from_bytes(cls, data: bytes) -> EncryptedData:
846
+ randomizer = int.from_bytes(data[:5], byteorder="little")
847
+ payload = data[5 : len(data) - 4]
848
+ mic = data[-4:]
849
+ return cls(randomizer, payload, mic)
850
+
851
+ def __bytes__(self) -> bytes:
852
+ return self.randomizer.to_bytes(5, byteorder="little") + self.payload + self.mic
853
+
854
+ def value_string(self) -> str:
855
+ return (
856
+ f"randomizer=0x{self.randomizer:010X}, "
857
+ f"payload={self.payload.hex().upper()}, "
858
+ f"mic={self.mic.hex().upper()}"
859
+ )
860
+
861
+
862
+ @dataclasses.dataclass
863
+ class PeriodicAdvertisingResponseTimingInformation(core.DataType):
864
+ """
865
+ See Supplement to the Bluetooth Core Specification, Part A
866
+ 1.24 PERIODIC ADVERTISING RESPONSE TIMING INFORMATION
867
+ """
868
+
869
+ label = "Periodic Advertising Response Timing Information"
870
+ ad_type = core.AdvertisingData.Type.PERIODIC_ADVERTISING_RESPONSE_TIMING_INFORMATION
871
+
872
+ rspaa: int
873
+ num_subevents: int
874
+ subevent_interval: int
875
+ response_slot_delay: int
876
+ response_slot_spacing: int
877
+
878
+ @classmethod
879
+ def from_bytes(cls, data: bytes) -> PeriodicAdvertisingResponseTimingInformation:
880
+ return cls(
881
+ int.from_bytes(data[:4], byteorder="little"),
882
+ data[4],
883
+ data[5],
884
+ data[6],
885
+ data[7],
886
+ )
887
+
888
+ def __bytes__(self) -> bytes:
889
+ return self.rspaa.to_bytes(4, byteorder="little") + bytes(
890
+ [
891
+ self.num_subevents,
892
+ self.subevent_interval,
893
+ self.response_slot_delay,
894
+ self.response_slot_spacing,
895
+ ]
896
+ )
897
+
898
+ def value_string(self) -> str:
899
+ return (
900
+ f"rspaa=0x{self.rspaa:08X}, "
901
+ f"num_subevents={self.num_subevents}, "
902
+ f"subevent_interval={self.subevent_interval}, "
903
+ f"response_slot_delay={self.response_slot_delay}, "
904
+ f"response_slot_spacing={self.response_slot_spacing}"
905
+ )
906
+
907
+
908
+ class BroadcastName(StringDataType):
909
+ """
910
+ See Assigned Numbers, 6.12.6.13 Broadcast_Name
911
+ """
912
+
913
+ label = "Broadcast Name"
914
+ ad_type = core.AdvertisingData.Type.BROADCAST_NAME
915
+
916
+
917
+ class ResolvableSetIdentifier(FixedSizeBytesDataType):
918
+ """
919
+ See Coordinated Set Identification Service, 3.1 RSI AD Type
920
+ """
921
+
922
+ label = "Resolvable Set Identifier"
923
+ ad_type = core.AdvertisingData.Type.RESOLVABLE_SET_IDENTIFIER
924
+ _fixed_size = 6
925
+
926
+
927
+ # -----------------------------------------------------------------------------
928
+ _AD_TO_DATA_TYPE_CLASS_MAP: dict[core.AdvertisingData.Type, type[core.DataType]] = {
929
+ core.AdvertisingData.Type.FLAGS: Flags,
930
+ core.AdvertisingData.Type.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS: IncompleteListOf16BitServiceUUIDs,
931
+ core.AdvertisingData.Type.COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS: CompleteListOf16BitServiceUUIDs,
932
+ core.AdvertisingData.Type.INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS: IncompleteListOf32BitServiceUUIDs,
933
+ core.AdvertisingData.Type.COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS: CompleteListOf32BitServiceUUIDs,
934
+ core.AdvertisingData.Type.INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS: IncompleteListOf128BitServiceUUIDs,
935
+ core.AdvertisingData.Type.COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS: CompleteListOf128BitServiceUUIDs,
936
+ core.AdvertisingData.Type.SHORTENED_LOCAL_NAME: ShortenedLocalName,
937
+ core.AdvertisingData.Type.COMPLETE_LOCAL_NAME: CompleteLocalName,
938
+ core.AdvertisingData.Type.TX_POWER_LEVEL: TxPowerLevel,
939
+ core.AdvertisingData.Type.CLASS_OF_DEVICE: ClassOfDevice,
940
+ core.AdvertisingData.Type.SIMPLE_PAIRING_HASH_C_192: SecureSimplePairingHashC192,
941
+ core.AdvertisingData.Type.SIMPLE_PAIRING_RANDOMIZER_R_192: SecureSimplePairingRandomizerR192,
942
+ # core.AdvertisingData.Type.DEVICE_ID: TBD,
943
+ core.AdvertisingData.Type.SECURITY_MANAGER_TK_VALUE: SecurityManagerTKValue,
944
+ core.AdvertisingData.Type.SECURITY_MANAGER_OUT_OF_BAND_FLAGS: SecurityManagerOutOfBandFlag,
945
+ core.AdvertisingData.Type.PERIPHERAL_CONNECTION_INTERVAL_RANGE: PeripheralConnectionIntervalRange,
946
+ core.AdvertisingData.Type.LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS: ListOf16BitServiceSolicitationUUIDs,
947
+ core.AdvertisingData.Type.LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS: ListOf128BitServiceSolicitationUUIDs,
948
+ core.AdvertisingData.Type.SERVICE_DATA_16_BIT_UUID: ServiceData16BitUUID,
949
+ core.AdvertisingData.Type.PUBLIC_TARGET_ADDRESS: PublicTargetAddress,
950
+ core.AdvertisingData.Type.RANDOM_TARGET_ADDRESS: RandomTargetAddress,
951
+ core.AdvertisingData.Type.APPEARANCE: Appearance,
952
+ core.AdvertisingData.Type.ADVERTISING_INTERVAL: AdvertisingInterval,
953
+ core.AdvertisingData.Type.LE_BLUETOOTH_DEVICE_ADDRESS: LeBluetoothDeviceAddress,
954
+ core.AdvertisingData.Type.LE_ROLE: LeRole,
955
+ core.AdvertisingData.Type.SIMPLE_PAIRING_HASH_C_256: SecureSimplePairingHashC256,
956
+ core.AdvertisingData.Type.SIMPLE_PAIRING_RANDOMIZER_R_256: SecureSimplePairingRandomizerR256,
957
+ core.AdvertisingData.Type.LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS: ListOf32BitServiceSolicitationUUIDs,
958
+ core.AdvertisingData.Type.SERVICE_DATA_32_BIT_UUID: ServiceData32BitUUID,
959
+ core.AdvertisingData.Type.SERVICE_DATA_128_BIT_UUID: ServiceData128BitUUID,
960
+ core.AdvertisingData.Type.LE_SECURE_CONNECTIONS_CONFIRMATION_VALUE: LeSecureConnectionsConfirmationValue,
961
+ core.AdvertisingData.Type.LE_SECURE_CONNECTIONS_RANDOM_VALUE: LeSecureConnectionsRandomValue,
962
+ core.AdvertisingData.Type.URI: Uri,
963
+ # core.AdvertisingData.Type.INDOOR_POSITIONING: TBD,
964
+ # core.AdvertisingData.Type.TRANSPORT_DISCOVERY_DATA: TBD,
965
+ core.AdvertisingData.Type.LE_SUPPORTED_FEATURES: LeSupportedFeatures,
966
+ core.AdvertisingData.Type.CHANNEL_MAP_UPDATE_INDICATION: ChannelMapUpdateIndication,
967
+ # core.AdvertisingData.Type.PB_ADV: TBD,
968
+ # core.AdvertisingData.Type.MESH_MESSAGE: TBD,
969
+ # core.AdvertisingData.Type.MESH_BEACON: TBD,
970
+ # core.AdvertisingData.Type.BIGINFO: BigInfo,
971
+ core.AdvertisingData.Type.BROADCAST_CODE: BroadcastCode,
972
+ core.AdvertisingData.Type.RESOLVABLE_SET_IDENTIFIER: ResolvableSetIdentifier,
973
+ core.AdvertisingData.Type.ADVERTISING_INTERVAL_LONG: AdvertisingIntervalLong,
974
+ core.AdvertisingData.Type.BROADCAST_NAME: BroadcastName,
975
+ core.AdvertisingData.Type.ENCRYPTED_ADVERTISING_DATA: EncryptedData,
976
+ core.AdvertisingData.Type.PERIODIC_ADVERTISING_RESPONSE_TIMING_INFORMATION: PeriodicAdvertisingResponseTimingInformation,
977
+ # core.AdvertisingData.Type.ELECTRONIC_SHELF_LABEL: TBD,
978
+ # core.AdvertisingData.Type.THREE_D_INFORMATION_DATA: TBD,
979
+ core.AdvertisingData.Type.MANUFACTURER_SPECIFIC_DATA: ManufacturerSpecificData,
980
+ }
981
+
982
+
983
+ def data_type_from_advertising_data(
984
+ advertising_data_type: core.AdvertisingData.Type,
985
+ advertising_data: bytes,
986
+ ) -> core.DataType:
987
+ """
988
+ Creates a DataType object given a type ID and serialized data.
989
+
990
+ NOTE: in general, if you know the type ID, it is preferrable to simply call the
991
+ `from_bytes` factory class method of the associated DataType class directly.
992
+ For example, use BroadcastName.from_bytes(bn_data) rather than
993
+ data_type_from_advertising_data(AdvertisingData.Type.BROADCAST_NAME, bn_data)
994
+
995
+ Args:
996
+ advertising_data_type: type ID of the data.
997
+ advertising_data: serialized data.
998
+
999
+ Returns:
1000
+ a DataType subclass instance.
1001
+
1002
+ """
1003
+ if data_type_class := _AD_TO_DATA_TYPE_CLASS_MAP.get(advertising_data_type):
1004
+ return data_type_class.from_bytes(advertising_data)
1005
+
1006
+ return GenericAdvertisingData(advertising_data, advertising_data_type)
1007
+
1008
+
1009
+ def data_types_from_advertising_data(
1010
+ advertising_data: core.AdvertisingData,
1011
+ ) -> list[core.DataType]:
1012
+ """
1013
+ Create DataType objects representing all the advertising data structs contained
1014
+ in an AdvertisingData object.
1015
+
1016
+ Args:
1017
+ advertising_data: the AdvertisingData in which to look for the data type.
1018
+
1019
+ Returns:
1020
+ a list of DataType subclass instances.
1021
+ """
1022
+ return [
1023
+ data_type_from_advertising_data(ad_type, ad_data)
1024
+ for (ad_type, ad_data) in advertising_data.ad_structures
1025
+ ]