bumble 0.0.214__py3-none-any.whl → 0.0.215__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.
- bumble/_version.py +16 -3
- bumble/a2dp.py +15 -16
- bumble/apps/auracast.py +13 -38
- bumble/apps/bench.py +9 -10
- bumble/apps/ble_rpa_tool.py +1 -0
- bumble/apps/console.py +22 -25
- bumble/apps/controller_info.py +19 -19
- bumble/apps/controller_loopback.py +2 -2
- bumble/apps/controllers.py +1 -1
- bumble/apps/device_info.py +3 -3
- bumble/apps/gatt_dump.py +1 -1
- bumble/apps/gg_bridge.py +5 -6
- bumble/apps/hci_bridge.py +3 -3
- bumble/apps/l2cap_bridge.py +3 -3
- bumble/apps/lea_unicast/app.py +15 -25
- bumble/apps/pair.py +30 -43
- bumble/apps/pandora_server.py +5 -4
- bumble/apps/player/player.py +19 -22
- bumble/apps/rfcomm_bridge.py +3 -8
- bumble/apps/scan.py +16 -6
- bumble/apps/show.py +3 -4
- bumble/apps/speaker/speaker.py +22 -22
- bumble/apps/unbond.py +2 -1
- bumble/apps/usb_probe.py +1 -2
- bumble/att.py +241 -246
- bumble/audio/io.py +5 -9
- bumble/avc.py +2 -2
- bumble/avctp.py +6 -7
- bumble/avdtp.py +19 -22
- bumble/avrcp.py +1096 -588
- bumble/codecs.py +2 -0
- bumble/controller.py +52 -13
- bumble/core.py +567 -248
- bumble/crypto/__init__.py +2 -2
- bumble/crypto/builtin.py +1 -1
- bumble/crypto/cryptography.py +2 -4
- bumble/data_types.py +1025 -0
- bumble/device.py +280 -278
- bumble/drivers/__init__.py +3 -2
- bumble/drivers/intel.py +3 -4
- bumble/drivers/rtk.py +26 -9
- bumble/gap.py +4 -4
- bumble/gatt.py +3 -2
- bumble/gatt_adapters.py +3 -11
- bumble/gatt_client.py +69 -81
- bumble/gatt_server.py +124 -124
- bumble/hci.py +67 -18
- bumble/helpers.py +19 -26
- bumble/hfp.py +10 -21
- bumble/hid.py +22 -16
- bumble/host.py +181 -103
- bumble/keys.py +5 -3
- bumble/l2cap.py +121 -74
- bumble/link.py +8 -9
- bumble/pairing.py +7 -6
- bumble/pandora/__init__.py +8 -7
- bumble/pandora/config.py +3 -1
- bumble/pandora/device.py +3 -2
- bumble/pandora/host.py +38 -36
- bumble/pandora/l2cap.py +22 -21
- bumble/pandora/security.py +15 -15
- bumble/pandora/utils.py +5 -3
- bumble/profiles/aics.py +11 -11
- bumble/profiles/ams.py +7 -8
- bumble/profiles/ancs.py +6 -7
- bumble/profiles/ascs.py +4 -9
- bumble/profiles/asha.py +8 -12
- bumble/profiles/bap.py +11 -23
- bumble/profiles/bass.py +2 -7
- bumble/profiles/battery_service.py +3 -4
- bumble/profiles/cap.py +1 -2
- bumble/profiles/csip.py +2 -6
- bumble/profiles/device_information_service.py +2 -2
- bumble/profiles/gap.py +4 -4
- bumble/profiles/gatt_service.py +1 -4
- bumble/profiles/gmap.py +5 -5
- bumble/profiles/hap.py +62 -59
- bumble/profiles/heart_rate_service.py +5 -4
- bumble/profiles/le_audio.py +3 -1
- bumble/profiles/mcp.py +3 -7
- bumble/profiles/pacs.py +3 -6
- bumble/profiles/pbp.py +2 -0
- bumble/profiles/tmap.py +2 -3
- bumble/profiles/vcs.py +2 -8
- bumble/profiles/vocs.py +8 -8
- bumble/rfcomm.py +11 -14
- bumble/rtp.py +1 -0
- bumble/sdp.py +10 -8
- bumble/smp.py +142 -153
- bumble/snoop.py +5 -5
- bumble/tools/generate_company_id_list.py +1 -0
- bumble/tools/intel_fw_download.py +3 -3
- bumble/tools/intel_util.py +4 -4
- bumble/tools/rtk_fw_download.py +6 -3
- bumble/tools/rtk_util.py +24 -7
- bumble/transport/__init__.py +19 -15
- bumble/transport/android_emulator.py +8 -13
- bumble/transport/android_netsim.py +19 -18
- bumble/transport/common.py +12 -15
- bumble/transport/file.py +1 -1
- bumble/transport/hci_socket.py +4 -6
- bumble/transport/pty.py +5 -6
- bumble/transport/pyusb.py +7 -10
- bumble/transport/serial.py +2 -1
- bumble/transport/tcp_client.py +2 -2
- bumble/transport/tcp_server.py +11 -14
- bumble/transport/udp.py +3 -3
- bumble/transport/unix.py +67 -1
- bumble/transport/usb.py +6 -6
- bumble/transport/vhci.py +0 -1
- bumble/transport/ws_client.py +2 -1
- bumble/transport/ws_server.py +3 -2
- bumble/utils.py +20 -5
- bumble/vendor/android/hci.py +1 -2
- bumble/vendor/zephyr/hci.py +0 -1
- {bumble-0.0.214.dist-info → bumble-0.0.215.dist-info}/METADATA +2 -1
- bumble-0.0.215.dist-info/RECORD +183 -0
- bumble-0.0.214.dist-info/RECORD +0 -182
- {bumble-0.0.214.dist-info → bumble-0.0.215.dist-info}/WHEEL +0 -0
- {bumble-0.0.214.dist-info → bumble-0.0.215.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.214.dist-info → bumble-0.0.215.dist-info}/licenses/LICENSE +0 -0
- {bumble-0.0.214.dist-info → bumble-0.0.215.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
|
+
]
|