bumble 0.0.178__py3-none-any.whl → 0.0.180__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/hci.py CHANGED
@@ -21,7 +21,7 @@ import enum
21
21
  import functools
22
22
  import logging
23
23
  import struct
24
- from typing import Any, Dict, Callable, Optional, Type, Union
24
+ from typing import Any, Dict, Callable, Optional, Type, Union, List
25
25
 
26
26
  from .colors import color
27
27
  from .core import (
@@ -149,6 +149,7 @@ HCI_COMMAND_PACKET = 0x01
149
149
  HCI_ACL_DATA_PACKET = 0x02
150
150
  HCI_SYNCHRONOUS_DATA_PACKET = 0x03
151
151
  HCI_EVENT_PACKET = 0x04
152
+ HCI_ISO_DATA_PACKET = 0x05
152
153
 
153
154
  # HCI Event Codes
154
155
  HCI_INQUIRY_COMPLETE_EVENT = 0x01
@@ -3828,8 +3829,10 @@ class HCI_LE_Set_Advertising_Set_Random_Address_Command(HCI_Command):
3828
3829
  'advertising_event_properties',
3829
3830
  {
3830
3831
  'size': 2,
3831
- 'mapper': lambda x: HCI_LE_Set_Extended_Advertising_Parameters_Command.advertising_properties_string(
3832
- x
3832
+ 'mapper': lambda x: str(
3833
+ HCI_LE_Set_Extended_Advertising_Parameters_Command.AdvertisingProperties(
3834
+ x
3835
+ )
3833
3836
  ),
3834
3837
  },
3835
3838
  ),
@@ -3839,8 +3842,8 @@ class HCI_LE_Set_Advertising_Set_Random_Address_Command(HCI_Command):
3839
3842
  'primary_advertising_channel_map',
3840
3843
  {
3841
3844
  'size': 1,
3842
- 'mapper': lambda x: HCI_LE_Set_Extended_Advertising_Parameters_Command.channel_map_string(
3843
- x
3845
+ 'mapper': lambda x: str(
3846
+ HCI_LE_Set_Extended_Advertising_Parameters_Command.ChannelMap(x)
3844
3847
  ),
3845
3848
  },
3846
3849
  ),
@@ -3862,38 +3865,33 @@ class HCI_LE_Set_Extended_Advertising_Parameters_Command(HCI_Command):
3862
3865
  See Bluetooth spec @ 7.8.53 LE Set Extended Advertising Parameters Command
3863
3866
  '''
3864
3867
 
3865
- CONNECTABLE_ADVERTISING = 0
3866
- SCANNABLE_ADVERTISING = 1
3867
- DIRECTED_ADVERTISING = 2
3868
- HIGH_DUTY_CYCLE_DIRECTED_CONNECTABLE_ADVERTISING = 3
3869
- USE_LEGACY_ADVERTISING_PDUS = 4
3870
- ANONYMOUS_ADVERTISING = 5
3871
- INCLUDE_TX_POWER = 6
3872
-
3873
- ADVERTISING_PROPERTIES_NAMES = (
3874
- 'CONNECTABLE_ADVERTISING',
3875
- 'SCANNABLE_ADVERTISING',
3876
- 'DIRECTED_ADVERTISING',
3877
- 'HIGH_DUTY_CYCLE_DIRECTED_CONNECTABLE_ADVERTISING',
3878
- 'USE_LEGACY_ADVERTISING_PDUS',
3879
- 'ANONYMOUS_ADVERTISING',
3880
- 'INCLUDE_TX_POWER',
3881
- )
3882
-
3883
- CHANNEL_37 = 0
3884
- CHANNEL_38 = 1
3885
- CHANNEL_39 = 2
3868
+ class AdvertisingProperties(enum.IntFlag):
3869
+ CONNECTABLE_ADVERTISING = 1 << 0
3870
+ SCANNABLE_ADVERTISING = 1 << 1
3871
+ DIRECTED_ADVERTISING = 1 << 2
3872
+ HIGH_DUTY_CYCLE_DIRECTED_CONNECTABLE_ADVERTISING = 1 << 3
3873
+ USE_LEGACY_ADVERTISING_PDUS = 1 << 4
3874
+ ANONYMOUS_ADVERTISING = 1 << 5
3875
+ INCLUDE_TX_POWER = 1 << 6
3876
+
3877
+ def __str__(self) -> str:
3878
+ return '|'.join(
3879
+ flag.name
3880
+ for flag in HCI_LE_Set_Extended_Advertising_Parameters_Command.AdvertisingProperties
3881
+ if self.value & flag.value and flag.name is not None
3882
+ )
3886
3883
 
3887
- CHANNEL_NAMES = ('37', '38', '39')
3884
+ class ChannelMap(enum.IntFlag):
3885
+ CHANNEL_37 = 1 << 0
3886
+ CHANNEL_38 = 1 << 1
3887
+ CHANNEL_39 = 1 << 2
3888
3888
 
3889
- @classmethod
3890
- def advertising_properties_string(cls, properties):
3891
- # pylint: disable=line-too-long
3892
- return f'[{",".join(bit_flags_to_strings(properties, cls.ADVERTISING_PROPERTIES_NAMES))}]'
3893
-
3894
- @classmethod
3895
- def channel_map_string(cls, channel_map):
3896
- return f'[{",".join(bit_flags_to_strings(channel_map, cls.CHANNEL_NAMES))}]'
3889
+ def __str__(self) -> str:
3890
+ return '|'.join(
3891
+ flag.name
3892
+ for flag in HCI_LE_Set_Extended_Advertising_Parameters_Command.ChannelMap
3893
+ if self.value & flag.value and flag.name is not None
3894
+ )
3897
3895
 
3898
3896
 
3899
3897
  # -----------------------------------------------------------------------------
@@ -3905,9 +3903,9 @@ class HCI_LE_Set_Extended_Advertising_Parameters_Command(HCI_Command):
3905
3903
  'operation',
3906
3904
  {
3907
3905
  'size': 1,
3908
- 'mapper': lambda x: HCI_LE_Set_Extended_Advertising_Data_Command.operation_name(
3906
+ 'mapper': lambda x: HCI_LE_Set_Extended_Advertising_Data_Command.Operation(
3909
3907
  x
3910
- ),
3908
+ ).name,
3911
3909
  },
3912
3910
  ),
3913
3911
  ('fragment_preference', 1),
@@ -3925,23 +3923,12 @@ class HCI_LE_Set_Extended_Advertising_Data_Command(HCI_Command):
3925
3923
  See Bluetooth spec @ 7.8.54 LE Set Extended Advertising Data Command
3926
3924
  '''
3927
3925
 
3928
- INTERMEDIATE_FRAGMENT = 0x00
3929
- FIRST_FRAGMENT = 0x01
3930
- LAST_FRAGMENT = 0x02
3931
- COMPLETE_DATA = 0x03
3932
- UNCHANGED_DATA = 0x04
3933
-
3934
- OPERATION_NAMES = {
3935
- INTERMEDIATE_FRAGMENT: 'INTERMEDIATE_FRAGMENT',
3936
- FIRST_FRAGMENT: 'FIRST_FRAGMENT',
3937
- LAST_FRAGMENT: 'LAST_FRAGMENT',
3938
- COMPLETE_DATA: 'COMPLETE_DATA',
3939
- UNCHANGED_DATA: 'UNCHANGED_DATA',
3940
- }
3941
-
3942
- @classmethod
3943
- def operation_name(cls, operation):
3944
- return name_or_number(cls.OPERATION_NAMES, operation)
3926
+ class Operation(enum.IntEnum):
3927
+ INTERMEDIATE_FRAGMENT = 0x00
3928
+ FIRST_FRAGMENT = 0x01
3929
+ LAST_FRAGMENT = 0x02
3930
+ COMPLETE_DATA = 0x03
3931
+ UNCHANGED_DATA = 0x04
3945
3932
 
3946
3933
 
3947
3934
  # -----------------------------------------------------------------------------
@@ -3953,9 +3940,9 @@ class HCI_LE_Set_Extended_Advertising_Data_Command(HCI_Command):
3953
3940
  'operation',
3954
3941
  {
3955
3942
  'size': 1,
3956
- 'mapper': lambda x: HCI_LE_Set_Extended_Advertising_Data_Command.operation_name(
3943
+ 'mapper': lambda x: HCI_LE_Set_Extended_Advertising_Data_Command.Operation(
3957
3944
  x
3958
- ),
3945
+ ).name,
3959
3946
  },
3960
3947
  ),
3961
3948
  ('fragment_preference', 1),
@@ -3973,22 +3960,6 @@ class HCI_LE_Set_Extended_Scan_Response_Data_Command(HCI_Command):
3973
3960
  See Bluetooth spec @ 7.8.55 LE Set Extended Scan Response Data Command
3974
3961
  '''
3975
3962
 
3976
- INTERMEDIATE_FRAGMENT = 0x00
3977
- FIRST_FRAGMENT = 0x01
3978
- LAST_FRAGMENT = 0x02
3979
- COMPLETE_DATA = 0x03
3980
-
3981
- OPERATION_NAMES = {
3982
- INTERMEDIATE_FRAGMENT: 'INTERMEDIATE_FRAGMENT',
3983
- FIRST_FRAGMENT: 'FIRST_FRAGMENT',
3984
- LAST_FRAGMENT: 'LAST_FRAGMENT',
3985
- COMPLETE_DATA: 'COMPLETE_DATA',
3986
- }
3987
-
3988
- @classmethod
3989
- def operation_name(cls, operation):
3990
- return name_or_number(cls.OPERATION_NAMES, operation)
3991
-
3992
3963
 
3993
3964
  # -----------------------------------------------------------------------------
3994
3965
  @HCI_Command.command(
@@ -4386,6 +4357,158 @@ class HCI_LE_Set_Host_Feature_Command(HCI_Command):
4386
4357
  '''
4387
4358
 
4388
4359
 
4360
+ # -----------------------------------------------------------------------------
4361
+ @HCI_Command.command(
4362
+ fields=[
4363
+ ('cig_id', 1),
4364
+ ('sdu_interval_c_to_p', 3),
4365
+ ('sdu_interval_p_to_c', 3),
4366
+ ('worst_case_sca', 1),
4367
+ ('packing', 1),
4368
+ ('framing', 1),
4369
+ ('max_transport_latency_c_to_p', 2),
4370
+ ('max_transport_latency_p_to_c', 2),
4371
+ [
4372
+ ('cis_id', 1),
4373
+ ('max_sdu_c_to_p', 2),
4374
+ ('max_sdu_p_to_c', 2),
4375
+ ('phy_c_to_p', 1),
4376
+ ('phy_p_to_c', 1),
4377
+ ('rtn_c_to_p', 1),
4378
+ ('rtn_p_to_c', 1),
4379
+ ],
4380
+ ],
4381
+ return_parameters_fields=[
4382
+ ('status', STATUS_SPEC),
4383
+ ('cig_id', 1),
4384
+ [('connection_handle', 2)],
4385
+ ],
4386
+ )
4387
+ class HCI_LE_Set_CIG_Parameters_Command(HCI_Command):
4388
+ '''
4389
+ See Bluetooth spec @ 7.8.97 LE Set CIG Parameters Command
4390
+ '''
4391
+
4392
+ cig_id: int
4393
+ sdu_interval_c_to_p: int
4394
+ sdu_interval_p_to_c: int
4395
+ worst_case_sca: int
4396
+ packing: int
4397
+ framing: int
4398
+ max_transport_latency_c_to_p: int
4399
+ max_transport_latency_p_to_c: int
4400
+ cis_id: List[int]
4401
+ max_sdu_c_to_p: List[int]
4402
+ max_sdu_p_to_c: List[int]
4403
+ phy_c_to_p: List[int]
4404
+ phy_p_to_c: List[int]
4405
+ rtn_c_to_p: List[int]
4406
+ rtn_p_to_c: List[int]
4407
+
4408
+
4409
+ # -----------------------------------------------------------------------------
4410
+ @HCI_Command.command(
4411
+ fields=[
4412
+ [
4413
+ ('cis_connection_handle', 2),
4414
+ ('acl_connection_handle', 2),
4415
+ ],
4416
+ ],
4417
+ )
4418
+ class HCI_LE_Create_CIS_Command(HCI_Command):
4419
+ '''
4420
+ See Bluetooth spec @ 7.8.99 LE Create CIS command
4421
+ '''
4422
+
4423
+ cis_connection_handle: List[int]
4424
+ acl_connection_handle: List[int]
4425
+
4426
+
4427
+ # -----------------------------------------------------------------------------
4428
+ @HCI_Command.command(
4429
+ fields=[('cig_id', 1)],
4430
+ return_parameters_fields=[('status', STATUS_SPEC), ('cig_id', 1)],
4431
+ )
4432
+ class HCI_LE_Remove_CIG_Command(HCI_Command):
4433
+ '''
4434
+ See Bluetooth spec @ 7.8.100 LE Remove CIG command
4435
+ '''
4436
+
4437
+ cig_id: int
4438
+
4439
+
4440
+ # -----------------------------------------------------------------------------
4441
+ @HCI_Command.command(
4442
+ fields=[('connection_handle', 2)],
4443
+ )
4444
+ class HCI_LE_Accept_CIS_Request_Command(HCI_Command):
4445
+ '''
4446
+ See Bluetooth spec @ 7.8.101 LE Accept CIS Request command
4447
+ '''
4448
+
4449
+ connection_handle: int
4450
+
4451
+
4452
+ # -----------------------------------------------------------------------------
4453
+ @HCI_Command.command(
4454
+ fields=[('connection_handle', 2)],
4455
+ )
4456
+ class HCI_LE_Reject_CIS_Request_Command(HCI_Command):
4457
+ '''
4458
+ See Bluetooth spec @ 7.8.102 LE Reject CIS Request command
4459
+ '''
4460
+
4461
+ connection_handle: int
4462
+
4463
+
4464
+ # -----------------------------------------------------------------------------
4465
+ @HCI_Command.command(
4466
+ fields=[
4467
+ ('connection_handle', 2),
4468
+ ('data_path_direction', 1),
4469
+ ('data_path_id', 1),
4470
+ ('codec_id', 5),
4471
+ ('controller_delay', 3),
4472
+ ('codec_configuration', '*'),
4473
+ ],
4474
+ return_parameters_fields=[
4475
+ ('status', STATUS_SPEC),
4476
+ ('connection_handle', 2),
4477
+ ],
4478
+ )
4479
+ class HCI_LE_Setup_ISO_Data_Path_Command(HCI_Command):
4480
+ '''
4481
+ See Bluetooth spec @ 7.8.109 LE Setup ISO Data Path command
4482
+ '''
4483
+
4484
+ connection_handle: int
4485
+ data_path_direction: int
4486
+ data_path_id: int
4487
+ codec_id: int
4488
+ controller_delay: int
4489
+ codec_configuration: int
4490
+
4491
+
4492
+ # -----------------------------------------------------------------------------
4493
+ @HCI_Command.command(
4494
+ fields=[
4495
+ ('connection_handle', 2),
4496
+ ('data_path_direction', 1),
4497
+ ],
4498
+ return_parameters_fields=[
4499
+ ('status', STATUS_SPEC),
4500
+ ('connection_handle', 2),
4501
+ ],
4502
+ )
4503
+ class HCI_LE_Remove_ISO_Data_Path_Command(HCI_Command):
4504
+ '''
4505
+ See Bluetooth spec @ 7.8.110 LE Remove ISO Data Path command
4506
+ '''
4507
+
4508
+ connection_handle: int
4509
+ data_path_direction: int
4510
+
4511
+
4389
4512
  # -----------------------------------------------------------------------------
4390
4513
  # HCI Events
4391
4514
  # -----------------------------------------------------------------------------
@@ -5005,6 +5128,48 @@ class HCI_LE_Channel_Selection_Algorithm_Event(HCI_LE_Meta_Event):
5005
5128
  '''
5006
5129
 
5007
5130
 
5131
+ # -----------------------------------------------------------------------------
5132
+ @HCI_LE_Meta_Event.event(
5133
+ [
5134
+ ('status', STATUS_SPEC),
5135
+ ('connection_handle', 2),
5136
+ ('cig_sync_delay', 3),
5137
+ ('cis_sync_delay', 3),
5138
+ ('transport_latency_c_to_p', 3),
5139
+ ('transport_latency_p_to_c', 3),
5140
+ ('phy_c_to_p', 1),
5141
+ ('phy_p_to_c', 1),
5142
+ ('nse', 1),
5143
+ ('bn_c_to_p', 1),
5144
+ ('bn_p_to_c', 1),
5145
+ ('ft_c_to_p', 1),
5146
+ ('ft_p_to_c', 1),
5147
+ ('max_pdu_c_to_p', 2),
5148
+ ('max_pdu_p_to_c', 2),
5149
+ ('iso_interval', 2),
5150
+ ]
5151
+ )
5152
+ class HCI_LE_CIS_Established_Event(HCI_LE_Meta_Event):
5153
+ '''
5154
+ See Bluetooth spec @ 7.7.65.25 LE CIS Established Event
5155
+ '''
5156
+
5157
+
5158
+ # -----------------------------------------------------------------------------
5159
+ @HCI_LE_Meta_Event.event(
5160
+ [
5161
+ ('acl_connection_handle', 2),
5162
+ ('cis_connection_handle', 2),
5163
+ ('cig_id', 1),
5164
+ ('cis_id', 1),
5165
+ ]
5166
+ )
5167
+ class HCI_LE_CIS_Request_Event(HCI_LE_Meta_Event):
5168
+ '''
5169
+ See Bluetooth spec @ 7.7.65.26 LE CIS Request Event
5170
+ '''
5171
+
5172
+
5008
5173
  # -----------------------------------------------------------------------------
5009
5174
  @HCI_Event.event([('status', STATUS_SPEC)])
5010
5175
  class HCI_Inquiry_Complete_Event(HCI_Event):
@@ -5131,6 +5296,10 @@ class HCI_Disconnection_Complete_Event(HCI_Event):
5131
5296
  See Bluetooth spec @ 7.7.5 Disconnection Complete Event
5132
5297
  '''
5133
5298
 
5299
+ status: int
5300
+ connection_handle: int
5301
+ reason: int
5302
+
5134
5303
 
5135
5304
  # -----------------------------------------------------------------------------
5136
5305
  @HCI_Event.event([('status', STATUS_SPEC), ('connection_handle', 2)])
@@ -5815,18 +5984,17 @@ class HCI_SynchronousDataPacket(HCI_Packet):
5815
5984
  h, data_total_length = struct.unpack_from('<HB', packet, 1)
5816
5985
  connection_handle = h & 0xFFF
5817
5986
  packet_status = (h >> 12) & 0b11
5818
- rfu = (h >> 14) & 0b11
5819
5987
  data = packet[4:]
5820
5988
  if len(data) != data_total_length:
5821
5989
  raise ValueError(
5822
5990
  f'invalid packet length {len(data)} != {data_total_length}'
5823
5991
  )
5824
5992
  return HCI_SynchronousDataPacket(
5825
- connection_handle, packet_status, rfu, data_total_length, data
5993
+ connection_handle, packet_status, data_total_length, data
5826
5994
  )
5827
5995
 
5828
5996
  def to_bytes(self) -> bytes:
5829
- h = (self.packet_status << 12) | (self.rfu << 14) | self.connection_handle
5997
+ h = (self.packet_status << 12) | self.connection_handle
5830
5998
  return (
5831
5999
  struct.pack('<BHB', HCI_SYNCHRONOUS_DATA_PACKET, h, self.data_total_length)
5832
6000
  + self.data
@@ -5836,13 +6004,11 @@ class HCI_SynchronousDataPacket(HCI_Packet):
5836
6004
  self,
5837
6005
  connection_handle: int,
5838
6006
  packet_status: int,
5839
- rfu: int,
5840
6007
  data_total_length: int,
5841
6008
  data: bytes,
5842
6009
  ) -> None:
5843
6010
  self.connection_handle = connection_handle
5844
6011
  self.packet_status = packet_status
5845
- self.rfu = rfu
5846
6012
  self.data_total_length = data_total_length
5847
6013
  self.data = data
5848
6014
 
@@ -5853,12 +6019,119 @@ class HCI_SynchronousDataPacket(HCI_Packet):
5853
6019
  return (
5854
6020
  f'{color("SCO", "blue")}: '
5855
6021
  f'handle=0x{self.connection_handle:04x}, '
5856
- f'ps={self.packet_status}, rfu={self.rfu}, '
6022
+ f'ps={self.packet_status}, '
5857
6023
  f'data_total_length={self.data_total_length}, '
5858
6024
  f'data={self.data.hex()}'
5859
6025
  )
5860
6026
 
5861
6027
 
6028
+ # -----------------------------------------------------------------------------
6029
+ class HCI_IsoDataPacket(HCI_Packet):
6030
+ '''
6031
+ See Bluetooth spec @ 5.4.5 HCI ISO Data Packets
6032
+ '''
6033
+
6034
+ hci_packet_type = HCI_ISO_DATA_PACKET
6035
+
6036
+ @staticmethod
6037
+ def from_bytes(packet: bytes) -> HCI_IsoDataPacket:
6038
+ time_stamp: Optional[int] = None
6039
+ packet_sequence_number: Optional[int] = None
6040
+ iso_sdu_length: Optional[int] = None
6041
+ packet_status_flag: Optional[int] = None
6042
+
6043
+ pos = 1
6044
+ pdu_info, data_total_length = struct.unpack_from('<HH', packet, pos)
6045
+ connection_handle = pdu_info & 0xFFF
6046
+ pb_flag = (pdu_info >> 12) & 0b11
6047
+ ts_flag = (pdu_info >> 14) & 0b01
6048
+ pos += 4
6049
+
6050
+ # pb_flag in (0b00, 0b10) but faster
6051
+ should_include_sdu_info = not (pb_flag & 0b01)
6052
+
6053
+ if ts_flag:
6054
+ if not should_include_sdu_info:
6055
+ logger.warn(f'Timestamp included when pb_flag={bin(pb_flag)}')
6056
+ time_stamp, _ = struct.unpack_from('<I', packet, pos)
6057
+ pos += 4
6058
+
6059
+ if should_include_sdu_info:
6060
+ packet_sequence_number, sdu_info = struct.unpack_from('<HH', packet, pos)
6061
+ iso_sdu_length = sdu_info & 0xFFF
6062
+ packet_status_flag = sdu_info >> 14
6063
+ pos += 4
6064
+
6065
+ iso_sdu_fragment = packet[pos:]
6066
+ return HCI_IsoDataPacket(
6067
+ connection_handle=connection_handle,
6068
+ pb_flag=pb_flag,
6069
+ ts_flag=ts_flag,
6070
+ data_total_length=data_total_length,
6071
+ time_stamp=time_stamp,
6072
+ packet_sequence_number=packet_sequence_number,
6073
+ iso_sdu_length=iso_sdu_length,
6074
+ packet_status_flag=packet_status_flag,
6075
+ iso_sdu_fragment=iso_sdu_fragment,
6076
+ )
6077
+
6078
+ def __init__(
6079
+ self,
6080
+ connection_handle: int,
6081
+ pb_flag: int,
6082
+ ts_flag: int,
6083
+ data_total_length: int,
6084
+ time_stamp: Optional[int],
6085
+ packet_sequence_number: Optional[int],
6086
+ iso_sdu_length: Optional[int],
6087
+ packet_status_flag: Optional[int],
6088
+ iso_sdu_fragment: bytes,
6089
+ ) -> None:
6090
+ self.connection_handle = connection_handle
6091
+ self.pb_flag = pb_flag
6092
+ self.ts_flag = ts_flag
6093
+ self.data_total_length = data_total_length
6094
+ self.time_stamp = time_stamp
6095
+ self.packet_sequence_number = packet_sequence_number
6096
+ self.iso_sdu_length = iso_sdu_length
6097
+ self.packet_status_flag = packet_status_flag
6098
+ self.iso_sdu_fragment = iso_sdu_fragment
6099
+
6100
+ def __bytes__(self) -> bytes:
6101
+ return self.to_bytes()
6102
+
6103
+ def to_bytes(self) -> bytes:
6104
+ fmt = '<BHH'
6105
+ args = [
6106
+ HCI_ISO_DATA_PACKET,
6107
+ self.ts_flag << 14 | self.pb_flag << 12 | self.connection_handle,
6108
+ self.data_total_length,
6109
+ ]
6110
+ if self.time_stamp is not None:
6111
+ fmt += 'I'
6112
+ args.append(self.time_stamp)
6113
+ if (
6114
+ self.packet_sequence_number is not None
6115
+ and self.iso_sdu_length is not None
6116
+ and self.packet_status_flag is not None
6117
+ ):
6118
+ fmt += 'HH'
6119
+ args += [
6120
+ self.packet_sequence_number,
6121
+ self.iso_sdu_length | self.packet_status_flag << 14,
6122
+ ]
6123
+ return struct.pack(fmt, args) + self.iso_sdu_fragment
6124
+
6125
+ def __str__(self) -> str:
6126
+ return (
6127
+ f'{color("ISO", "blue")}: '
6128
+ f'handle=0x{self.connection_handle:04x}, '
6129
+ f'ps={self.packet_status_flag}, '
6130
+ f'data_total_length={self.data_total_length}, '
6131
+ f'sdu={self.iso_sdu_fragment.hex()}'
6132
+ )
6133
+
6134
+
5862
6135
  # -----------------------------------------------------------------------------
5863
6136
  class HCI_AclDataPacketAssembler:
5864
6137
  current_data: Optional[bytes]