bumble 0.0.194__py3-none-any.whl → 0.0.198__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 (54) hide show
  1. bumble/_version.py +2 -2
  2. bumble/apps/auracast.py +692 -0
  3. bumble/apps/bench.py +77 -23
  4. bumble/apps/console.py +5 -20
  5. bumble/apps/controller_info.py +3 -3
  6. bumble/apps/device_info.py +230 -0
  7. bumble/apps/gatt_dump.py +4 -0
  8. bumble/apps/lea_unicast/app.py +16 -17
  9. bumble/at.py +12 -6
  10. bumble/avc.py +8 -5
  11. bumble/avctp.py +3 -2
  12. bumble/avdtp.py +5 -1
  13. bumble/avrcp.py +2 -1
  14. bumble/codecs.py +17 -13
  15. bumble/colors.py +6 -2
  16. bumble/core.py +726 -122
  17. bumble/device.py +817 -117
  18. bumble/drivers/rtk.py +13 -8
  19. bumble/gatt.py +6 -1
  20. bumble/gatt_client.py +10 -4
  21. bumble/hci.py +283 -20
  22. bumble/hid.py +24 -28
  23. bumble/host.py +29 -0
  24. bumble/l2cap.py +24 -17
  25. bumble/link.py +8 -3
  26. bumble/pandora/host.py +3 -2
  27. bumble/profiles/ascs.py +739 -0
  28. bumble/profiles/bap.py +85 -862
  29. bumble/profiles/bass.py +440 -0
  30. bumble/profiles/csip.py +4 -4
  31. bumble/profiles/gap.py +110 -0
  32. bumble/profiles/heart_rate_service.py +4 -3
  33. bumble/profiles/le_audio.py +83 -0
  34. bumble/profiles/mcp.py +448 -0
  35. bumble/profiles/pacs.py +210 -0
  36. bumble/profiles/pbp.py +46 -0
  37. bumble/profiles/tmap.py +89 -0
  38. bumble/rfcomm.py +14 -3
  39. bumble/sdp.py +13 -11
  40. bumble/smp.py +20 -8
  41. bumble/snoop.py +5 -4
  42. bumble/transport/__init__.py +8 -2
  43. bumble/transport/android_emulator.py +9 -3
  44. bumble/transport/android_netsim.py +9 -7
  45. bumble/transport/common.py +46 -18
  46. bumble/transport/pyusb.py +2 -2
  47. bumble/transport/unix.py +56 -0
  48. bumble/transport/usb.py +57 -46
  49. {bumble-0.0.194.dist-info → bumble-0.0.198.dist-info}/METADATA +41 -41
  50. {bumble-0.0.194.dist-info → bumble-0.0.198.dist-info}/RECORD +54 -43
  51. {bumble-0.0.194.dist-info → bumble-0.0.198.dist-info}/WHEEL +1 -1
  52. {bumble-0.0.194.dist-info → bumble-0.0.198.dist-info}/LICENSE +0 -0
  53. {bumble-0.0.194.dist-info → bumble-0.0.198.dist-info}/entry_points.txt +0 -0
  54. {bumble-0.0.194.dist-info → bumble-0.0.198.dist-info}/top_level.txt +0 -0
bumble/drivers/rtk.py CHANGED
@@ -33,6 +33,7 @@ from typing import Tuple
33
33
  import weakref
34
34
 
35
35
 
36
+ from bumble import core
36
37
  from bumble.hci import (
37
38
  hci_vendor_command_op_code,
38
39
  STATUS_SPEC,
@@ -49,6 +50,10 @@ from bumble.drivers import common
49
50
  logger = logging.getLogger(__name__)
50
51
 
51
52
 
53
+ class RtkFirmwareError(core.BaseBumbleError):
54
+ """Error raised when RTK firmware initialization fails."""
55
+
56
+
52
57
  # -----------------------------------------------------------------------------
53
58
  # Constants
54
59
  # -----------------------------------------------------------------------------
@@ -208,15 +213,15 @@ class Firmware:
208
213
  extension_sig = bytes([0x51, 0x04, 0xFD, 0x77])
209
214
 
210
215
  if not firmware.startswith(RTK_EPATCH_SIGNATURE):
211
- raise ValueError("Firmware does not start with epatch signature")
216
+ raise RtkFirmwareError("Firmware does not start with epatch signature")
212
217
 
213
218
  if not firmware.endswith(extension_sig):
214
- raise ValueError("Firmware does not end with extension sig")
219
+ raise RtkFirmwareError("Firmware does not end with extension sig")
215
220
 
216
221
  # The firmware should start with a 14 byte header.
217
222
  epatch_header_size = 14
218
223
  if len(firmware) < epatch_header_size:
219
- raise ValueError("Firmware too short")
224
+ raise RtkFirmwareError("Firmware too short")
220
225
 
221
226
  # Look for the "project ID", starting from the end.
222
227
  offset = len(firmware) - len(extension_sig)
@@ -230,7 +235,7 @@ class Firmware:
230
235
  break
231
236
 
232
237
  if length == 0:
233
- raise ValueError("Invalid 0-length instruction")
238
+ raise RtkFirmwareError("Invalid 0-length instruction")
234
239
 
235
240
  if opcode == 0 and length == 1:
236
241
  project_id = firmware[offset - 1]
@@ -239,7 +244,7 @@ class Firmware:
239
244
  offset -= length
240
245
 
241
246
  if project_id < 0:
242
- raise ValueError("Project ID not found")
247
+ raise RtkFirmwareError("Project ID not found")
243
248
 
244
249
  self.project_id = project_id
245
250
 
@@ -252,7 +257,7 @@ class Firmware:
252
257
  # <PatchLength_1><PatchLength_2>...<PatchLength_N> (16 bits each)
253
258
  # <PatchOffset_1><PatchOffset_2>...<PatchOffset_N> (32 bits each)
254
259
  if epatch_header_size + 8 * num_patches > len(firmware):
255
- raise ValueError("Firmware too short")
260
+ raise RtkFirmwareError("Firmware too short")
256
261
  chip_id_table_offset = epatch_header_size
257
262
  patch_length_table_offset = chip_id_table_offset + 2 * num_patches
258
263
  patch_offset_table_offset = chip_id_table_offset + 4 * num_patches
@@ -266,7 +271,7 @@ class Firmware:
266
271
  "<I", firmware, patch_offset_table_offset + 4 * patch_index
267
272
  )
268
273
  if patch_offset + patch_length > len(firmware):
269
- raise ValueError("Firmware too short")
274
+ raise RtkFirmwareError("Firmware too short")
270
275
 
271
276
  # Get the SVN version for the patch
272
277
  (svn_version,) = struct.unpack_from(
@@ -645,7 +650,7 @@ class Driver(common.Driver):
645
650
  ):
646
651
  return await self.download_for_rtl8723b()
647
652
 
648
- raise ValueError("ROM not supported")
653
+ raise RtkFirmwareError("ROM not supported")
649
654
 
650
655
  async def init_controller(self):
651
656
  await self.download_firmware()
bumble/gatt.py CHANGED
@@ -39,7 +39,7 @@ from typing import (
39
39
  )
40
40
 
41
41
  from bumble.colors import color
42
- from bumble.core import UUID
42
+ from bumble.core import BaseBumbleError, UUID
43
43
  from bumble.att import Attribute, AttributeValue
44
44
 
45
45
  if TYPE_CHECKING:
@@ -320,6 +320,11 @@ def show_services(services: Iterable[Service]) -> None:
320
320
  print(color(' ' + str(descriptor), 'green'))
321
321
 
322
322
 
323
+ # -----------------------------------------------------------------------------
324
+ class InvalidServiceError(BaseBumbleError):
325
+ """The service is not compliant with the spec/profile"""
326
+
327
+
323
328
  # -----------------------------------------------------------------------------
324
329
  class Service(Attribute):
325
330
  '''
bumble/gatt_client.py CHANGED
@@ -253,7 +253,7 @@ class ProfileServiceProxy:
253
253
  SERVICE_CLASS: Type[TemplateService]
254
254
 
255
255
  @classmethod
256
- def from_client(cls, client: Client) -> ProfileServiceProxy:
256
+ def from_client(cls, client: Client) -> Optional[ProfileServiceProxy]:
257
257
  return ServiceProxy.from_client(cls, client, cls.SERVICE_CLASS.UUID)
258
258
 
259
259
 
@@ -283,6 +283,8 @@ class Client:
283
283
  self.services = []
284
284
  self.cached_values = {}
285
285
 
286
+ connection.on('disconnection', self.on_disconnection)
287
+
286
288
  def send_gatt_pdu(self, pdu: bytes) -> None:
287
289
  self.connection.send_l2cap_pdu(ATT_CID, pdu)
288
290
 
@@ -331,9 +333,9 @@ class Client:
331
333
  async def request_mtu(self, mtu: int) -> int:
332
334
  # Check the range
333
335
  if mtu < ATT_DEFAULT_MTU:
334
- raise ValueError(f'MTU must be >= {ATT_DEFAULT_MTU}')
336
+ raise core.InvalidArgumentError(f'MTU must be >= {ATT_DEFAULT_MTU}')
335
337
  if mtu > 0xFFFF:
336
- raise ValueError('MTU must be <= 0xFFFF')
338
+ raise core.InvalidArgumentError('MTU must be <= 0xFFFF')
337
339
 
338
340
  # We can only send one request per connection
339
341
  if self.mtu_exchange_done:
@@ -405,7 +407,7 @@ class Client:
405
407
  if not already_known:
406
408
  self.services.append(service)
407
409
 
408
- async def discover_services(self, uuids: Iterable[UUID] = []) -> List[ServiceProxy]:
410
+ async def discover_services(self, uuids: Iterable[UUID] = ()) -> List[ServiceProxy]:
409
411
  '''
410
412
  See Vol 3, Part G - 4.4.1 Discover All Primary Services
411
413
  '''
@@ -1072,6 +1074,10 @@ class Client:
1072
1074
  )
1073
1075
  )
1074
1076
 
1077
+ def on_disconnection(self, _) -> None:
1078
+ if self.pending_response and not self.pending_response.done():
1079
+ self.pending_response.cancel()
1080
+
1075
1081
  def on_gatt_pdu(self, att_pdu: ATT_PDU) -> None:
1076
1082
  logger.debug(
1077
1083
  f'GATT Response to client: [0x{self.connection.handle:04X}] {att_pdu}'
bumble/hci.py CHANGED
@@ -26,16 +26,19 @@ import struct
26
26
  from typing import Any, Callable, Dict, Iterable, List, Optional, Type, Union, ClassVar
27
27
 
28
28
  from bumble import crypto
29
- from .colors import color
30
- from .core import (
29
+ from bumble.colors import color
30
+ from bumble.core import (
31
31
  BT_BR_EDR_TRANSPORT,
32
32
  AdvertisingData,
33
33
  DeviceClass,
34
+ InvalidArgumentError,
35
+ InvalidPacketError,
34
36
  ProtocolError,
35
37
  bit_flags_to_strings,
36
38
  name_or_number,
37
39
  padded_bytes,
38
40
  )
41
+ from bumble.utils import OpenIntEnum
39
42
 
40
43
 
41
44
  # -----------------------------------------------------------------------------
@@ -91,14 +94,14 @@ def map_class_of_device(class_of_device):
91
94
  )
92
95
 
93
96
 
94
- def phy_list_to_bits(phys):
97
+ def phy_list_to_bits(phys: Optional[Iterable[int]]) -> int:
95
98
  if phys is None:
96
99
  return 0
97
100
 
98
101
  phy_bits = 0
99
102
  for phy in phys:
100
103
  if phy not in HCI_LE_PHY_TYPE_TO_BIT:
101
- raise ValueError('invalid PHY')
104
+ raise InvalidArgumentError('invalid PHY')
102
105
  phy_bits |= 1 << HCI_LE_PHY_TYPE_TO_BIT[phy]
103
106
  return phy_bits
104
107
 
@@ -1104,7 +1107,7 @@ HCI_SUPPORTED_COMMANDS_MASKS = {
1104
1107
 
1105
1108
  # LE Supported Features
1106
1109
  # See Bluetooth spec @ Vol 6, Part B, 4.6 FEATURE SUPPORT
1107
- class LeFeature(enum.IntEnum):
1110
+ class LeFeature(OpenIntEnum):
1108
1111
  LE_ENCRYPTION = 0
1109
1112
  CONNECTION_PARAMETERS_REQUEST_PROCEDURE = 1
1110
1113
  EXTENDED_REJECT_INDICATION = 2
@@ -1380,7 +1383,7 @@ class LmpFeatureMask(enum.IntFlag):
1380
1383
  STATUS_SPEC = {'size': 1, 'mapper': lambda x: HCI_Constant.status_name(x)}
1381
1384
 
1382
1385
 
1383
- class CodecID(enum.IntEnum):
1386
+ class CodecID(OpenIntEnum):
1384
1387
  # fmt: off
1385
1388
  U_LOG = 0x00
1386
1389
  A_LOG = 0x01
@@ -1552,7 +1555,7 @@ class HCI_Object:
1552
1555
  new_offset, field_value = field_type(data, offset)
1553
1556
  return (field_value, new_offset - offset)
1554
1557
 
1555
- raise ValueError(f'unknown field type {field_type}')
1558
+ raise InvalidArgumentError(f'unknown field type {field_type}')
1556
1559
 
1557
1560
  @staticmethod
1558
1561
  def dict_from_bytes(data, offset, fields):
@@ -1621,7 +1624,7 @@ class HCI_Object:
1621
1624
  if 0 <= field_value <= 255:
1622
1625
  field_bytes = bytes([field_value])
1623
1626
  else:
1624
- raise ValueError('value too large for *-typed field')
1627
+ raise InvalidArgumentError('value too large for *-typed field')
1625
1628
  else:
1626
1629
  field_bytes = bytes(field_value)
1627
1630
  elif field_type == 'v':
@@ -1640,7 +1643,9 @@ class HCI_Object:
1640
1643
  elif len(field_bytes) > field_type:
1641
1644
  field_bytes = field_bytes[:field_type]
1642
1645
  else:
1643
- raise ValueError(f"don't know how to serialize type {type(field_value)}")
1646
+ raise InvalidArgumentError(
1647
+ f"don't know how to serialize type {type(field_value)}"
1648
+ )
1644
1649
 
1645
1650
  return field_bytes
1646
1651
 
@@ -1834,6 +1839,12 @@ class Address:
1834
1839
  data, offset, Address.PUBLIC_DEVICE_ADDRESS
1835
1840
  )
1836
1841
 
1842
+ @staticmethod
1843
+ def parse_random_address(data, offset):
1844
+ return Address.parse_address_with_type(
1845
+ data, offset, Address.RANDOM_DEVICE_ADDRESS
1846
+ )
1847
+
1837
1848
  @staticmethod
1838
1849
  def parse_address_with_type(data, offset, address_type):
1839
1850
  return offset + 6, Address(data[offset : offset + 6], address_type)
@@ -1904,7 +1915,7 @@ class Address:
1904
1915
  self.address_bytes = bytes(reversed(bytes.fromhex(address)))
1905
1916
 
1906
1917
  if len(self.address_bytes) != 6:
1907
- raise ValueError('invalid address length')
1918
+ raise InvalidArgumentError('invalid address length')
1908
1919
 
1909
1920
  self.address_type = address_type
1910
1921
 
@@ -1960,13 +1971,17 @@ class Address:
1960
1971
 
1961
1972
  def __eq__(self, other):
1962
1973
  return (
1963
- self.address_bytes == other.address_bytes
1974
+ isinstance(other, Address)
1975
+ and self.address_bytes == other.address_bytes
1964
1976
  and self.is_public == other.is_public
1965
1977
  )
1966
1978
 
1967
1979
  def __str__(self):
1968
1980
  return self.to_string()
1969
1981
 
1982
+ def __repr__(self):
1983
+ return f'Address({self.to_string(False)}/{self.address_type_name(self.address_type)})'
1984
+
1970
1985
 
1971
1986
  # Predefined address values
1972
1987
  Address.NIL = Address(b"\xff\xff\xff\xff\xff\xff", Address.PUBLIC_DEVICE_ADDRESS)
@@ -2104,7 +2119,7 @@ class HCI_Command(HCI_Packet):
2104
2119
  op_code, length = struct.unpack_from('<HB', packet, 1)
2105
2120
  parameters = packet[4:]
2106
2121
  if len(parameters) != length:
2107
- raise ValueError('invalid packet length')
2122
+ raise InvalidPacketError('invalid packet length')
2108
2123
 
2109
2124
  # Look for a registered class
2110
2125
  cls = HCI_Command.command_classes.get(op_code)
@@ -4452,6 +4467,68 @@ class HCI_LE_Extended_Create_Connection_Command(HCI_Command):
4452
4467
  )
4453
4468
 
4454
4469
 
4470
+ # -----------------------------------------------------------------------------
4471
+ @HCI_Command.command(
4472
+ [
4473
+ (
4474
+ 'options',
4475
+ {
4476
+ 'size': 1,
4477
+ 'mapper': lambda x: HCI_LE_Periodic_Advertising_Create_Sync_Command.Options(
4478
+ x
4479
+ ).name,
4480
+ },
4481
+ ),
4482
+ ('advertising_sid', 1),
4483
+ ('advertiser_address_type', Address.ADDRESS_TYPE_SPEC),
4484
+ ('advertiser_address', Address.parse_address_preceded_by_type),
4485
+ ('skip', 2),
4486
+ ('sync_timeout', 2),
4487
+ (
4488
+ 'sync_cte_type',
4489
+ {
4490
+ 'size': 1,
4491
+ 'mapper': lambda x: HCI_LE_Periodic_Advertising_Create_Sync_Command.CteType(
4492
+ x
4493
+ ).name,
4494
+ },
4495
+ ),
4496
+ ]
4497
+ )
4498
+ class HCI_LE_Periodic_Advertising_Create_Sync_Command(HCI_Command):
4499
+ '''
4500
+ See Bluetooth spec @ 7.8.67 LE Periodic Advertising Create Sync command
4501
+ '''
4502
+
4503
+ class Options(enum.IntFlag):
4504
+ USE_PERIODIC_ADVERTISER_LIST = 1 << 0
4505
+ REPORTING_INITIALLY_DISABLED = 1 << 1
4506
+ DUPLICATE_FILTERING_INITIALLY_ENABLED = 1 << 2
4507
+
4508
+ class CteType(enum.IntFlag):
4509
+ DO_NOT_SYNC_TO_PACKETS_WITH_AN_AOA_CONSTANT_TONE_EXTENSION = 1 << 0
4510
+ DO_NOT_SYNC_TO_PACKETS_WITH_AN_AOD_CONSTANT_TONE_EXTENSION_1US = 1 << 1
4511
+ DO_NOT_SYNC_TO_PACKETS_WITH_AN_AOD_CONSTANT_TONE_EXTENSION_2US = 1 << 2
4512
+ DO_NOT_SYNC_TO_PACKETS_WITH_A_TYPE_3_CONSTANT_TONE_EXTENSION = 1 << 3
4513
+ DO_NOT_SYNC_TO_PACKETS_WITHOUT_A_CONSTANT_TONE_EXTENSION = 1 << 4
4514
+
4515
+
4516
+ # -----------------------------------------------------------------------------
4517
+ @HCI_Command.command()
4518
+ class HCI_LE_Periodic_Advertising_Create_Sync_Cancel_Command(HCI_Command):
4519
+ '''
4520
+ See Bluetooth spec @ 7.8.68 LE Periodic Advertising Create Sync Cancel Command
4521
+ '''
4522
+
4523
+
4524
+ # -----------------------------------------------------------------------------
4525
+ @HCI_Command.command([('sync_handle', 2)])
4526
+ class HCI_LE_Periodic_Advertising_Terminate_Sync_Command(HCI_Command):
4527
+ '''
4528
+ See Bluetooth spec @ 7.8.69 LE Periodic Advertising Terminate Sync Command
4529
+ '''
4530
+
4531
+
4455
4532
  # -----------------------------------------------------------------------------
4456
4533
  @HCI_Command.command(
4457
4534
  [
@@ -4488,10 +4565,28 @@ class HCI_LE_Set_Privacy_Mode_Command(HCI_Command):
4488
4565
 
4489
4566
 
4490
4567
  # -----------------------------------------------------------------------------
4491
- @HCI_Command.command([('bit_number', 1), ('bit_value', 1)])
4492
- class HCI_LE_Set_Host_Feature_Command(HCI_Command):
4568
+ @HCI_Command.command([('sync_handle', 2), ('enable', 1)])
4569
+ class HCI_LE_Set_Periodic_Advertising_Receive_Enable_Command(HCI_Command):
4493
4570
  '''
4494
- See Bluetooth spec @ 7.8.115 LE Set Host Feature Command
4571
+ See Bluetooth spec @ 7.8.88 LE Set Periodic Advertising Receive Enable Command
4572
+ '''
4573
+
4574
+ class Enable(enum.IntFlag):
4575
+ REPORTING_ENABLED = 1 << 0
4576
+ DUPLICATE_FILTERING_ENABLED = 1 << 1
4577
+
4578
+
4579
+ # -----------------------------------------------------------------------------
4580
+ @HCI_Command.command(
4581
+ fields=[('connection_handle', 2), ('service_data', 2), ('sync_handle', 2)],
4582
+ return_parameters_fields=[
4583
+ ('status', STATUS_SPEC),
4584
+ ('connection_handle', 2),
4585
+ ],
4586
+ )
4587
+ class HCI_LE_Periodic_Advertising_Sync_Transfer_Command(HCI_Command):
4588
+ '''
4589
+ See Bluetooth spec @ 7.8.89 LE Periodic Advertising Sync Transfer Command
4495
4590
  '''
4496
4591
 
4497
4592
 
@@ -4655,6 +4750,14 @@ class HCI_LE_Remove_ISO_Data_Path_Command(HCI_Command):
4655
4750
  data_path_direction: int
4656
4751
 
4657
4752
 
4753
+ # -----------------------------------------------------------------------------
4754
+ @HCI_Command.command([('bit_number', 1), ('bit_value', 1)])
4755
+ class HCI_LE_Set_Host_Feature_Command(HCI_Command):
4756
+ '''
4757
+ See Bluetooth spec @ 7.8.115 LE Set Host Feature Command
4758
+ '''
4759
+
4760
+
4658
4761
  # -----------------------------------------------------------------------------
4659
4762
  # HCI Events
4660
4763
  # -----------------------------------------------------------------------------
@@ -4729,7 +4832,7 @@ class HCI_Event(HCI_Packet):
4729
4832
  length = packet[2]
4730
4833
  parameters = packet[3:]
4731
4834
  if len(parameters) != length:
4732
- raise ValueError('invalid packet length')
4835
+ raise InvalidPacketError('invalid packet length')
4733
4836
 
4734
4837
  cls: Any
4735
4838
  if event_code == HCI_LE_META_EVENT:
@@ -5096,8 +5199,8 @@ class HCI_LE_Data_Length_Change_Event(HCI_LE_Meta_Event):
5096
5199
  ),
5097
5200
  ('peer_address_type', Address.ADDRESS_TYPE_SPEC),
5098
5201
  ('peer_address', Address.parse_address_preceded_by_type),
5099
- ('local_resolvable_private_address', Address.parse_address),
5100
- ('peer_resolvable_private_address', Address.parse_address),
5202
+ ('local_resolvable_private_address', Address.parse_random_address),
5203
+ ('peer_resolvable_private_address', Address.parse_random_address),
5101
5204
  ('connection_interval', 2),
5102
5205
  ('peripheral_latency', 2),
5103
5206
  ('supervision_timeout', 2),
@@ -5271,6 +5374,142 @@ HCI_LE_Meta_Event.subevent_classes[HCI_LE_EXTENDED_ADVERTISING_REPORT_EVENT] = (
5271
5374
  )
5272
5375
 
5273
5376
 
5377
+ # -----------------------------------------------------------------------------
5378
+ @HCI_LE_Meta_Event.event(
5379
+ [
5380
+ ('status', STATUS_SPEC),
5381
+ ('sync_handle', 2),
5382
+ ('advertising_sid', 1),
5383
+ ('advertiser_address_type', Address.ADDRESS_TYPE_SPEC),
5384
+ ('advertiser_address', Address.parse_address_preceded_by_type),
5385
+ ('advertiser_phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}),
5386
+ ('periodic_advertising_interval', 2),
5387
+ ('advertiser_clock_accuracy', 1),
5388
+ ]
5389
+ )
5390
+ class HCI_LE_Periodic_Advertising_Sync_Established_Event(HCI_LE_Meta_Event):
5391
+ '''
5392
+ See Bluetooth spec @ 7.7.65.14 LE Periodic Advertising Sync Established Event
5393
+ '''
5394
+
5395
+
5396
+ # -----------------------------------------------------------------------------
5397
+ @HCI_LE_Meta_Event.event(
5398
+ [
5399
+ ('status', STATUS_SPEC),
5400
+ ('sync_handle', 2),
5401
+ ('advertising_sid', 1),
5402
+ ('advertiser_address_type', Address.ADDRESS_TYPE_SPEC),
5403
+ ('advertiser_address', Address.parse_address_preceded_by_type),
5404
+ ('advertiser_phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}),
5405
+ ('periodic_advertising_interval', 2),
5406
+ ('advertiser_clock_accuracy', 1),
5407
+ ('num_subevents', 1),
5408
+ ('subevent_interval', 1),
5409
+ ('response_slot_delay', 1),
5410
+ ('response_slot_spacing', 1),
5411
+ ]
5412
+ )
5413
+ class HCI_LE_Periodic_Advertising_Sync_Established_V2_Event(HCI_LE_Meta_Event):
5414
+ '''
5415
+ See Bluetooth spec @ 7.7.65.14 LE Periodic Advertising Sync Established Event
5416
+ '''
5417
+
5418
+
5419
+ # -----------------------------------------------------------------------------
5420
+ @HCI_LE_Meta_Event.event(
5421
+ [
5422
+ ('sync_handle', 2),
5423
+ ('tx_power', -1),
5424
+ ('rssi', -1),
5425
+ (
5426
+ 'cte_type',
5427
+ {
5428
+ 'size': 1,
5429
+ 'mapper': lambda x: HCI_LE_Periodic_Advertising_Report_Event.CteType(
5430
+ x
5431
+ ).name,
5432
+ },
5433
+ ),
5434
+ (
5435
+ 'data_status',
5436
+ {
5437
+ 'size': 1,
5438
+ 'mapper': lambda x: HCI_LE_Periodic_Advertising_Report_Event.DataStatus(
5439
+ x
5440
+ ).name,
5441
+ },
5442
+ ),
5443
+ ('data', 'v'),
5444
+ ]
5445
+ )
5446
+ class HCI_LE_Periodic_Advertising_Report_Event(HCI_LE_Meta_Event):
5447
+ '''
5448
+ See Bluetooth spec @ 7.7.65.15 LE Periodic Advertising Report Event
5449
+ '''
5450
+
5451
+ TX_POWER_INFORMATION_NOT_AVAILABLE = 0x7F
5452
+ RSSI_NOT_AVAILABLE = 0x7F
5453
+
5454
+ class CteType(OpenIntEnum):
5455
+ AOA_CONSTANT_TONE_EXTENSION = 0x00
5456
+ AOD_CONSTANT_TONE_EXTENSION_1US = 0x01
5457
+ AOD_CONSTANT_TONE_EXTENSION_2US = 0x02
5458
+ NO_CONSTANT_TONE_EXTENSION = 0xFF
5459
+
5460
+ class DataStatus(OpenIntEnum):
5461
+ DATA_COMPLETE = 0x00
5462
+ DATA_INCOMPLETE_MORE_TO_COME = 0x01
5463
+ DATA_INCOMPLETE_TRUNCATED_NO_MORE_TO_COME = 0x02
5464
+
5465
+
5466
+ # -----------------------------------------------------------------------------
5467
+ @HCI_LE_Meta_Event.event(
5468
+ [
5469
+ ('sync_handle', 2),
5470
+ ('tx_power', -1),
5471
+ ('rssi', -1),
5472
+ (
5473
+ 'cte_type',
5474
+ {
5475
+ 'size': 1,
5476
+ 'mapper': lambda x: HCI_LE_Periodic_Advertising_Report_Event.CteType(
5477
+ x
5478
+ ).name,
5479
+ },
5480
+ ),
5481
+ ('periodic_event_counter', 2),
5482
+ ('subevent', 1),
5483
+ (
5484
+ 'data_status',
5485
+ {
5486
+ 'size': 1,
5487
+ 'mapper': lambda x: HCI_LE_Periodic_Advertising_Report_Event.DataStatus(
5488
+ x
5489
+ ).name,
5490
+ },
5491
+ ),
5492
+ ('data', 'v'),
5493
+ ]
5494
+ )
5495
+ class HCI_LE_Periodic_Advertising_Report_V2_Event(HCI_LE_Meta_Event):
5496
+ '''
5497
+ See Bluetooth spec @ 7.7.65.15 LE Periodic Advertising Report Event
5498
+ '''
5499
+
5500
+
5501
+ # -----------------------------------------------------------------------------
5502
+ @HCI_LE_Meta_Event.event(
5503
+ [
5504
+ ('sync_handle', 2),
5505
+ ]
5506
+ )
5507
+ class HCI_LE_Periodic_Advertising_Sync_Lost_Event(HCI_LE_Meta_Event):
5508
+ '''
5509
+ See Bluetooth spec @ 7.7.65.16 LE Periodic Advertising Sync Lost Event
5510
+ '''
5511
+
5512
+
5274
5513
  # -----------------------------------------------------------------------------
5275
5514
  @HCI_LE_Meta_Event.event(
5276
5515
  [
@@ -5336,6 +5575,30 @@ class HCI_LE_CIS_Request_Event(HCI_LE_Meta_Event):
5336
5575
  '''
5337
5576
 
5338
5577
 
5578
+ # -----------------------------------------------------------------------------
5579
+ @HCI_LE_Meta_Event.event(
5580
+ [
5581
+ ('sync_handle', 2),
5582
+ ('num_bis', 1),
5583
+ ('nse', 1),
5584
+ ('iso_interval', 2),
5585
+ ('bn', 1),
5586
+ ('pto', 1),
5587
+ ('irc', 1),
5588
+ ('max_pdu', 2),
5589
+ ('sdu_interval', 3),
5590
+ ('max_sdu', 2),
5591
+ ('phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}),
5592
+ ('framing', 1),
5593
+ ('encryption', 1),
5594
+ ]
5595
+ )
5596
+ class HCI_LE_BIGInfo_Advertising_Report_Event(HCI_LE_Meta_Event):
5597
+ '''
5598
+ See Bluetooth spec @ 7.7.65.34 LE BIGInfo Advertising Report Event
5599
+ '''
5600
+
5601
+
5339
5602
  # -----------------------------------------------------------------------------
5340
5603
  @HCI_Event.event([('status', STATUS_SPEC)])
5341
5604
  class HCI_Inquiry_Complete_Event(HCI_Event):
@@ -6104,7 +6367,7 @@ class HCI_AclDataPacket(HCI_Packet):
6104
6367
  bc_flag = (h >> 14) & 3
6105
6368
  data = packet[5:]
6106
6369
  if len(data) != data_total_length:
6107
- raise ValueError('invalid packet length')
6370
+ raise InvalidPacketError('invalid packet length')
6108
6371
  return HCI_AclDataPacket(
6109
6372
  connection_handle, pb_flag, bc_flag, data_total_length, data
6110
6373
  )
@@ -6152,7 +6415,7 @@ class HCI_SynchronousDataPacket(HCI_Packet):
6152
6415
  packet_status = (h >> 12) & 0b11
6153
6416
  data = packet[4:]
6154
6417
  if len(data) != data_total_length:
6155
- raise ValueError(
6418
+ raise InvalidPacketError(
6156
6419
  f'invalid packet length {len(data)} != {data_total_length}'
6157
6420
  )
6158
6421
  return HCI_SynchronousDataPacket(