bumble 0.0.198__py3-none-any.whl → 0.0.200__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/gatt_client.py CHANGED
@@ -68,7 +68,7 @@ from .att import (
68
68
  ATT_Error,
69
69
  )
70
70
  from . import core
71
- from .core import UUID, InvalidStateError, ProtocolError
71
+ from .core import UUID, InvalidStateError
72
72
  from .gatt import (
73
73
  GATT_CHARACTERISTIC_ATTRIBUTE_TYPE,
74
74
  GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR,
@@ -345,12 +345,7 @@ class Client:
345
345
  self.mtu_exchange_done = True
346
346
  response = await self.send_request(ATT_Exchange_MTU_Request(client_rx_mtu=mtu))
347
347
  if response.op_code == ATT_ERROR_RESPONSE:
348
- raise ProtocolError(
349
- response.error_code,
350
- 'att',
351
- ATT_PDU.error_name(response.error_code),
352
- response,
353
- )
348
+ raise ATT_Error(error_code=response.error_code, message=response)
354
349
 
355
350
  # Compute the final MTU
356
351
  self.connection.att_mtu = min(mtu, response.server_rx_mtu)
@@ -936,12 +931,7 @@ class Client:
936
931
  if response is None:
937
932
  raise TimeoutError('read timeout')
938
933
  if response.op_code == ATT_ERROR_RESPONSE:
939
- raise ProtocolError(
940
- response.error_code,
941
- 'att',
942
- ATT_PDU.error_name(response.error_code),
943
- response,
944
- )
934
+ raise ATT_Error(error_code=response.error_code, message=response)
945
935
 
946
936
  # If the value is the max size for the MTU, try to read more unless the caller
947
937
  # specifically asked not to do that
@@ -963,12 +953,7 @@ class Client:
963
953
  ATT_INVALID_OFFSET_ERROR,
964
954
  ):
965
955
  break
966
- raise ProtocolError(
967
- response.error_code,
968
- 'att',
969
- ATT_PDU.error_name(response.error_code),
970
- response,
971
- )
956
+ raise ATT_Error(error_code=response.error_code, message=response)
972
957
 
973
958
  part = response.part_attribute_value
974
959
  attribute_value += part
@@ -1061,12 +1046,7 @@ class Client:
1061
1046
  )
1062
1047
  )
1063
1048
  if response.op_code == ATT_ERROR_RESPONSE:
1064
- raise ProtocolError(
1065
- response.error_code,
1066
- 'att',
1067
- ATT_PDU.error_name(response.error_code),
1068
- response,
1069
- )
1049
+ raise ATT_Error(error_code=response.error_code, message=response)
1070
1050
  else:
1071
1051
  await self.send_command(
1072
1052
  ATT_Write_Command(
bumble/gatt_server.py CHANGED
@@ -915,7 +915,7 @@ class Server(EventEmitter):
915
915
  See Bluetooth spec Vol 3, Part F - 3.4.5.1 Write Request
916
916
  '''
917
917
 
918
- # Check that the attribute exists
918
+ # Check that the attribute exists
919
919
  attribute = self.get_attribute(request.attribute_handle)
920
920
  if attribute is None:
921
921
  self.send_response(
@@ -942,11 +942,19 @@ class Server(EventEmitter):
942
942
  )
943
943
  return
944
944
 
945
- # Accept the value
946
- await attribute.write_value(connection, request.attribute_value)
947
-
948
- # Done
949
- self.send_response(connection, ATT_Write_Response())
945
+ try:
946
+ # Accept the value
947
+ await attribute.write_value(connection, request.attribute_value)
948
+ except ATT_Error as error:
949
+ response = ATT_Error_Response(
950
+ request_opcode_in_error=request.op_code,
951
+ attribute_handle_in_error=request.attribute_handle,
952
+ error_code=error.error_code,
953
+ )
954
+ else:
955
+ # Done
956
+ response = ATT_Write_Response()
957
+ self.send_response(connection, response)
950
958
 
951
959
  @AsyncRunner.run_in_task()
952
960
  async def on_att_write_command(self, connection, request):
bumble/hci.py CHANGED
@@ -267,6 +267,19 @@ HCI_LE_PERIODIC_ADVERTISING_SYNC_TRANSFER_RECEIVED_V2_EVENT = 0X26
267
267
  HCI_LE_PERIODIC_ADVERTISING_SUBEVENT_DATA_REQUEST_EVENT = 0X27
268
268
  HCI_LE_PERIODIC_ADVERTISING_RESPONSE_REPORT_EVENT = 0X28
269
269
  HCI_LE_ENHANCED_CONNECTION_COMPLETE_V2_EVENT = 0X29
270
+ HCI_LE_READ_ALL_REMOTE_FEATURES_COMPLETE_EVENT = 0x2A
271
+ HCI_LE_CIS_ESTABLISHED_V2_EVENT = 0x2B
272
+ HCI_LE_CS_READ_REMOTE_SUPPORTED_CAPABILITIES_COMPLETE_EVENT = 0x2C
273
+ HCI_LE_CS_READ_REMOTE_FAE_TABLE_COMPLETE_EVENT = 0x2D
274
+ HCI_LE_CS_SECURITY_ENABLE_COMPLETE_EVENT = 0x2E
275
+ HCI_LE_CS_CONFIG_COMPLETE_EVENT = 0x2F
276
+ HCI_LE_CS_PROCEDURE_ENABLE_EVENT = 0x30
277
+ HCI_LE_CS_SUBEVENT_RESULT_EVENT = 0x31
278
+ HCI_LE_CS_SUBEVENT_RESULT_CONTINUE_EVENT = 0x32
279
+ HCI_LE_CS_TEST_END_COMPLETE_EVENT = 0x33
280
+ HCI_LE_MONITORED_ADVERTISERS_REPORT_EVENT = 0x34
281
+ HCI_LE_FRAME_SPACE_UPDATE_EVENT = 0x35
282
+
270
283
 
271
284
 
272
285
  # HCI Command
@@ -573,11 +586,36 @@ HCI_LE_SET_DATA_RELATED_ADDRESS_CHANGES_COMMAND = hci_c
573
586
  HCI_LE_SET_DEFAULT_SUBRATE_COMMAND = hci_command_op_code(0x08, 0x007D)
574
587
  HCI_LE_SUBRATE_REQUEST_COMMAND = hci_command_op_code(0x08, 0x007E)
575
588
  HCI_LE_SET_EXTENDED_ADVERTISING_PARAMETERS_V2_COMMAND = hci_command_op_code(0x08, 0x007F)
589
+ HCI_LE_SET_DECISION_DATA_COMMAND = hci_command_op_code(0x08, 0x0080)
590
+ HCI_LE_SET_DECISION_INSTRUCTIONS_COMMAND = hci_command_op_code(0x08, 0x0081)
576
591
  HCI_LE_SET_PERIODIC_ADVERTISING_SUBEVENT_DATA_COMMAND = hci_command_op_code(0x08, 0x0082)
577
592
  HCI_LE_SET_PERIODIC_ADVERTISING_RESPONSE_DATA_COMMAND = hci_command_op_code(0x08, 0x0083)
578
593
  HCI_LE_SET_PERIODIC_SYNC_SUBEVENT_COMMAND = hci_command_op_code(0x08, 0x0084)
579
594
  HCI_LE_EXTENDED_CREATE_CONNECTION_V2_COMMAND = hci_command_op_code(0x08, 0x0085)
580
595
  HCI_LE_SET_PERIODIC_ADVERTISING_PARAMETERS_V2_COMMAND = hci_command_op_code(0x08, 0x0086)
596
+ HCI_LE_READ_ALL_LOCAL_SUPPORTED_FEATURES_COMMAND = hci_command_op_code(0x08, 0x0087)
597
+ HCI_LE_READ_ALL_REMOTE_FEATURES_COMMAND = hci_command_op_code(0x08, 0x0088)
598
+ HCI_LE_CS_READ_LOCAL_SUPPORTED_CAPABILITIES_COMMAND = hci_command_op_code(0x08, 0x0089)
599
+ HCI_LE_CS_READ_REMOTE_SUPPORTED_CAPABILITIES_COMMAND = hci_command_op_code(0x08, 0x008A)
600
+ HCI_LE_CS_WRITE_CACHED_REMOTE_SUPPORTED_CAPABILITIES = hci_command_op_code(0x08, 0x008B)
601
+ HCI_LE_CS_SECURITY_ENABLE_COMMAND = hci_command_op_code(0x08, 0x008C)
602
+ HCI_LE_CS_SET_DEFAULT_SETTINGS_COMMAND = hci_command_op_code(0x08, 0x008D)
603
+ HCI_LE_CS_READ_REMOTE_FAE_TABLE_COMMAND = hci_command_op_code(0x08, 0x008E)
604
+ HCI_LE_CS_WRITE_CACHED_REMOTE_FAE_TABLE_COMMAND = hci_command_op_code(0x08, 0x008F)
605
+ HCI_LE_CS_CREATE_CONFIG_COMMAND = hci_command_op_code(0x08, 0x0090)
606
+ HCI_LE_CS_REMOVE_CONFIG_COMMAND = hci_command_op_code(0x08, 0x0091)
607
+ HCI_LE_CS_SET_CHANNEL_CLASSIFICATION_COMMAND = hci_command_op_code(0x08, 0x0092)
608
+ HCI_LE_CS_SET_PROCEDURE_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x0093)
609
+ HCI_LE_CS_PROCEDURE_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0094)
610
+ HCI_LE_CS_TEST_COMMAND = hci_command_op_code(0x08, 0x0095)
611
+ HCI_LE_CS_TEST_END_COMMAND = hci_command_op_code(0x08, 0x0096)
612
+ HCI_LE_SET_HOST_FEATURE_V2_COMMAND = hci_command_op_code(0x08, 0x0097)
613
+ HCI_LE_ADD_DEVICE_TO_MONITORED_ADVERTISERS_LIST_COMMAND = hci_command_op_code(0x08, 0x0098)
614
+ HCI_LE_REMOVE_DEVICE_FROM_MONITORED_ADVERTISERS_LIST_COMMAND = hci_command_op_code(0x08, 0x0099)
615
+ HCI_LE_CLEAR_MONITORED_ADVERTISERS_LIST_COMMAND = hci_command_op_code(0x08, 0x009A)
616
+ HCI_LE_READ_MONITORED_ADVERTISERS_LIST_SIZE_COMMAND = hci_command_op_code(0x08, 0x009B)
617
+ HCI_LE_ENABLE_MONITORING_ADVERTISERS_COMMAND = hci_command_op_code(0x08, 0x009C)
618
+ HCI_LE_FRAME_SPACE_UPDATE_COMMAND = hci_command_op_code(0x08, 0x009D)
581
619
 
582
620
 
583
621
  # HCI Error Codes
@@ -1150,8 +1188,16 @@ class LeFeature(OpenIntEnum):
1150
1188
  CHANNEL_CLASSIFICATION = 39
1151
1189
  ADVERTISING_CODING_SELECTION = 40
1152
1190
  ADVERTISING_CODING_SELECTION_HOST_SUPPORT = 41
1191
+ DECISION_BASED_ADVERTISING_FILTERING = 42
1153
1192
  PERIODIC_ADVERTISING_WITH_RESPONSES_ADVERTISER = 43
1154
1193
  PERIODIC_ADVERTISING_WITH_RESPONSES_SCANNER = 44
1194
+ UNSEGMENTED_FRAMED_MODE = 45
1195
+ CHANNEL_SOUNDING = 46
1196
+ CHANNEL_SOUNDING_HOST_SUPPORT = 47
1197
+ CHANNEL_SOUNDING_TONE_QUALITY_INDICATION = 48
1198
+ LL_EXTENDED_FEATURE_SET = 63
1199
+ MONITORING_ADVERTISERS = 64
1200
+ FRAME_SPACE_UPDATE = 65
1155
1201
 
1156
1202
  class LeFeatureMask(enum.IntFlag):
1157
1203
  LE_ENCRYPTION = 1 << LeFeature.LE_ENCRYPTION
@@ -1196,8 +1242,16 @@ class LeFeatureMask(enum.IntFlag):
1196
1242
  CHANNEL_CLASSIFICATION = 1 << LeFeature.CHANNEL_CLASSIFICATION
1197
1243
  ADVERTISING_CODING_SELECTION = 1 << LeFeature.ADVERTISING_CODING_SELECTION
1198
1244
  ADVERTISING_CODING_SELECTION_HOST_SUPPORT = 1 << LeFeature.ADVERTISING_CODING_SELECTION_HOST_SUPPORT
1245
+ DECISION_BASED_ADVERTISING_FILTERING = 1 << LeFeature.DECISION_BASED_ADVERTISING_FILTERING
1199
1246
  PERIODIC_ADVERTISING_WITH_RESPONSES_ADVERTISER = 1 << LeFeature.PERIODIC_ADVERTISING_WITH_RESPONSES_ADVERTISER
1200
1247
  PERIODIC_ADVERTISING_WITH_RESPONSES_SCANNER = 1 << LeFeature.PERIODIC_ADVERTISING_WITH_RESPONSES_SCANNER
1248
+ UNSEGMENTED_FRAMED_MODE = 1 << LeFeature.UNSEGMENTED_FRAMED_MODE
1249
+ CHANNEL_SOUNDING = 1 << LeFeature.CHANNEL_SOUNDING
1250
+ CHANNEL_SOUNDING_HOST_SUPPORT = 1 << LeFeature.CHANNEL_SOUNDING_HOST_SUPPORT
1251
+ CHANNEL_SOUNDING_TONE_QUALITY_INDICATION = 1 << LeFeature.CHANNEL_SOUNDING_TONE_QUALITY_INDICATION
1252
+ LL_EXTENDED_FEATURE_SET = 1 << LeFeature.LL_EXTENDED_FEATURE_SET
1253
+ MONITORING_ADVERTISERS = 1 << LeFeature.MONITORING_ADVERTISERS
1254
+ FRAME_SPACE_UPDATE = 1 << LeFeature.FRAME_SPACE_UPDATE
1201
1255
 
1202
1256
  class LmpFeature(enum.IntEnum):
1203
1257
  # Page 0 (Legacy LMP features)
@@ -1565,12 +1619,16 @@ class HCI_Object:
1565
1619
  # This is an array field, starting with a 1-byte item count.
1566
1620
  item_count = data[offset]
1567
1621
  offset += 1
1622
+ # Set fields first, because item_count might be 0.
1623
+ for sub_field_name, _ in field:
1624
+ result[sub_field_name] = []
1625
+
1568
1626
  for _ in range(item_count):
1569
1627
  for sub_field_name, sub_field_type in field:
1570
1628
  value, size = HCI_Object.parse_field(
1571
1629
  data, offset, sub_field_type
1572
1630
  )
1573
- result.setdefault(sub_field_name, []).append(value)
1631
+ result[sub_field_name].append(value)
1574
1632
  offset += size
1575
1633
  continue
1576
1634
 
@@ -2982,6 +3040,27 @@ class HCI_Write_Inquiry_Scan_Activity_Command(HCI_Command):
2982
3040
  '''
2983
3041
 
2984
3042
 
3043
+ # -----------------------------------------------------------------------------
3044
+ @HCI_Command.command(
3045
+ return_parameters_fields=[
3046
+ ('status', STATUS_SPEC),
3047
+ ('authentication_enable', 1),
3048
+ ]
3049
+ )
3050
+ class HCI_Read_Authentication_Enable_Command(HCI_Command):
3051
+ '''
3052
+ See Bluetooth spec @ 7.3.23 Read Authentication Enable Command
3053
+ '''
3054
+
3055
+
3056
+ # -----------------------------------------------------------------------------
3057
+ @HCI_Command.command([('authentication_enable', 1)])
3058
+ class HCI_Write_Authentication_Enable_Command(HCI_Command):
3059
+ '''
3060
+ See Bluetooth spec @ 7.3.24 Write Authentication Enable Command
3061
+ '''
3062
+
3063
+
2985
3064
  # -----------------------------------------------------------------------------
2986
3065
  @HCI_Command.command(
2987
3066
  return_parameters_fields=[
@@ -3022,7 +3101,12 @@ class HCI_Write_Voice_Setting_Command(HCI_Command):
3022
3101
 
3023
3102
 
3024
3103
  # -----------------------------------------------------------------------------
3025
- @HCI_Command.command()
3104
+ @HCI_Command.command(
3105
+ return_parameters_fields=[
3106
+ ('status', STATUS_SPEC),
3107
+ ('synchronous_flow_control_enable', 1),
3108
+ ]
3109
+ )
3026
3110
  class HCI_Read_Synchronous_Flow_Control_Enable_Command(HCI_Command):
3027
3111
  '''
3028
3112
  See Bluetooth spec @ 7.3.36 Read Synchronous Flow Control Enable Command
@@ -3191,7 +3275,13 @@ class HCI_Set_Event_Mask_Page_2_Command(HCI_Command):
3191
3275
 
3192
3276
 
3193
3277
  # -----------------------------------------------------------------------------
3194
- @HCI_Command.command()
3278
+ @HCI_Command.command(
3279
+ return_parameters_fields=[
3280
+ ('status', STATUS_SPEC),
3281
+ ('le_supported_host', 1),
3282
+ ('unused', 1),
3283
+ ]
3284
+ )
3195
3285
  class HCI_Read_LE_Host_Support_Command(HCI_Command):
3196
3286
  '''
3197
3287
  See Bluetooth spec @ 7.3.78 Read LE Host Support Command
@@ -3324,13 +3414,39 @@ class HCI_Read_BD_ADDR_Command(HCI_Command):
3324
3414
 
3325
3415
 
3326
3416
  # -----------------------------------------------------------------------------
3327
- @HCI_Command.command()
3417
+ @HCI_Command.command(
3418
+ return_parameters_fields=[
3419
+ ("status", STATUS_SPEC),
3420
+ [("standard_codec_ids", 1)],
3421
+ [("vendor_specific_codec_ids", 4)],
3422
+ ]
3423
+ )
3328
3424
  class HCI_Read_Local_Supported_Codecs_Command(HCI_Command):
3329
3425
  '''
3330
3426
  See Bluetooth spec @ 7.4.8 Read Local Supported Codecs Command
3331
3427
  '''
3332
3428
 
3333
3429
 
3430
+ # -----------------------------------------------------------------------------
3431
+ @HCI_Command.command(
3432
+ return_parameters_fields=[
3433
+ ("status", STATUS_SPEC),
3434
+ [("standard_codec_ids", 1), ("standard_codec_transports", 1)],
3435
+ [("vendor_specific_codec_ids", 4), ("vendor_specific_codec_transports", 1)],
3436
+ ]
3437
+ )
3438
+ class HCI_Read_Local_Supported_Codecs_V2_Command(HCI_Command):
3439
+ '''
3440
+ See Bluetooth spec @ 7.4.8 Read Local Supported Codecs Command
3441
+ '''
3442
+
3443
+ class Transport(enum.IntFlag):
3444
+ BR_EDR_ACL = 1 << 0
3445
+ BR_EDR_SCO = 1 << 1
3446
+ LE_CIS = 1 << 2
3447
+ LE_BIS = 1 << 3
3448
+
3449
+
3334
3450
  # -----------------------------------------------------------------------------
3335
3451
  @HCI_Command.command(
3336
3452
  fields=[('handle', 2)],
@@ -3488,7 +3604,12 @@ class HCI_LE_Set_Advertising_Parameters_Command(HCI_Command):
3488
3604
 
3489
3605
 
3490
3606
  # -----------------------------------------------------------------------------
3491
- @HCI_Command.command()
3607
+ @HCI_Command.command(
3608
+ return_parameters_fields=[
3609
+ ('status', STATUS_SPEC),
3610
+ ('tx_power_level', 1),
3611
+ ]
3612
+ )
3492
3613
  class HCI_LE_Read_Advertising_Physical_Channel_Tx_Power_Command(HCI_Command):
3493
3614
  '''
3494
3615
  See Bluetooth spec @ 7.8.6 LE Read Advertising Physical Channel Tx Power Command
@@ -3612,7 +3733,12 @@ class HCI_LE_Create_Connection_Cancel_Command(HCI_Command):
3612
3733
 
3613
3734
 
3614
3735
  # -----------------------------------------------------------------------------
3615
- @HCI_Command.command()
3736
+ @HCI_Command.command(
3737
+ return_parameters_fields=[
3738
+ ('status', STATUS_SPEC),
3739
+ ('filter_accept_list_size', 1),
3740
+ ]
3741
+ )
3616
3742
  class HCI_LE_Read_Filter_Accept_List_Size_Command(HCI_Command):
3617
3743
  '''
3618
3744
  See Bluetooth spec @ 7.8.14 LE Read Filter Accept List Size Command
@@ -3723,7 +3849,12 @@ class HCI_LE_Long_Term_Key_Request_Negative_Reply_Command(HCI_Command):
3723
3849
 
3724
3850
 
3725
3851
  # -----------------------------------------------------------------------------
3726
- @HCI_Command.command()
3852
+ @HCI_Command.command(
3853
+ return_parameters_fields=[
3854
+ ('status', STATUS_SPEC),
3855
+ ('le_states', 8),
3856
+ ]
3857
+ )
3727
3858
  class HCI_LE_Read_Supported_States_Command(HCI_Command):
3728
3859
  '''
3729
3860
  See Bluetooth spec @ 7.8.27 LE Read Supported States Command
@@ -4698,6 +4829,102 @@ class HCI_LE_Reject_CIS_Request_Command(HCI_Command):
4698
4829
  reason: int
4699
4830
 
4700
4831
 
4832
+ # -----------------------------------------------------------------------------
4833
+ @HCI_Command.command(
4834
+ fields=[
4835
+ ('big_handle', 1),
4836
+ ('advertising_handle', 1),
4837
+ ('num_bis', 1),
4838
+ ('sdu_interval', 3),
4839
+ ('max_sdu', 2),
4840
+ ('max_transport_latency', 2),
4841
+ ('rtn', 1),
4842
+ ('phy', 1),
4843
+ ('packing', 1),
4844
+ ('framing', 1),
4845
+ ('encryption', 1),
4846
+ ('broadcast_code', 16),
4847
+ ],
4848
+ )
4849
+ class HCI_LE_Create_BIG_Command(HCI_Command):
4850
+ '''
4851
+ See Bluetooth spec @ 7.8.103 LE Create BIG command
4852
+ '''
4853
+
4854
+ big_handle: int
4855
+ advertising_handle: int
4856
+ num_bis: int
4857
+ sdu_interval: int
4858
+ max_sdu: int
4859
+ max_transport_latency: int
4860
+ rtn: int
4861
+ phy: int
4862
+ packing: int
4863
+ framing: int
4864
+ encryption: int
4865
+ broadcast_code: int
4866
+
4867
+
4868
+ # -----------------------------------------------------------------------------
4869
+ @HCI_Command.command(
4870
+ fields=[
4871
+ ('big_handle', 1),
4872
+ ('reason', {'size': 1, 'mapper': HCI_Constant.error_name}),
4873
+ ],
4874
+ )
4875
+ class HCI_LE_Terminate_BIG_Command(HCI_Command):
4876
+ '''
4877
+ See Bluetooth spec @ 7.8.105 LE Terminate BIG command
4878
+ '''
4879
+
4880
+ big_handle: int
4881
+ reason: int
4882
+
4883
+
4884
+ # -----------------------------------------------------------------------------
4885
+ @HCI_Command.command(
4886
+ fields=[
4887
+ ('big_handle', 1),
4888
+ ('sync_handle', 2),
4889
+ ('encryption', 1),
4890
+ ('broadcast_code', 16),
4891
+ ('mse', 1),
4892
+ ('big_sync_timeout', 2),
4893
+ [('bis', 1)],
4894
+ ],
4895
+ )
4896
+ class HCI_LE_BIG_Create_Sync_Command(HCI_Command):
4897
+ '''
4898
+ See Bluetooth spec @ 7.8.106 LE BIG Create Sync command
4899
+ '''
4900
+
4901
+ big_handle: int
4902
+ sync_handle: int
4903
+ encryption: int
4904
+ broadcast_code: int
4905
+ mse: int
4906
+ big_sync_timeout: int
4907
+ bis: List[int]
4908
+
4909
+
4910
+ # -----------------------------------------------------------------------------
4911
+ @HCI_Command.command(
4912
+ fields=[
4913
+ ('big_handle', 1),
4914
+ ],
4915
+ return_parameters_fields=[
4916
+ ('status', STATUS_SPEC),
4917
+ ('big_handle', 2),
4918
+ ],
4919
+ )
4920
+ class HCI_LE_BIG_Terminate_Sync_Command(HCI_Command):
4921
+ '''
4922
+ See Bluetooth spec @ 7.8.107. LE BIG Terminate Sync command
4923
+ '''
4924
+
4925
+ big_handle: int
4926
+
4927
+
4701
4928
  # -----------------------------------------------------------------------------
4702
4929
  @HCI_Command.command(
4703
4930
  fields=[
@@ -5533,6 +5760,27 @@ class HCI_LE_Channel_Selection_Algorithm_Event(HCI_LE_Meta_Event):
5533
5760
  '''
5534
5761
 
5535
5762
 
5763
+ # -----------------------------------------------------------------------------
5764
+ @HCI_LE_Meta_Event.event(
5765
+ [
5766
+ ('status', STATUS_SPEC),
5767
+ ('connection_handle', 2),
5768
+ ('service_data', 2),
5769
+ ('sync_handle', 2),
5770
+ ('advertising_sid', 1),
5771
+ ('advertiser_address_type', Address.ADDRESS_TYPE_SPEC),
5772
+ ('advertiser_address', Address.parse_address_preceded_by_type),
5773
+ ('advertiser_phy', 1),
5774
+ ('periodic_advertising_interval', 2),
5775
+ ('advertiser_clock_accuracy', 1),
5776
+ ]
5777
+ )
5778
+ class HCI_LE_Periodic_Advertising_Sync_Transfer_Received_Event(HCI_LE_Meta_Event):
5779
+ '''
5780
+ See Bluetooth spec @ 7.7.65.24 LE Periodic Advertising Sync Transfer Received Event
5781
+ '''
5782
+
5783
+
5536
5784
  # -----------------------------------------------------------------------------
5537
5785
  @HCI_LE_Meta_Event.event(
5538
5786
  [
@@ -5817,6 +6065,32 @@ class HCI_Read_Remote_Version_Information_Complete_Event(HCI_Event):
5817
6065
  '''
5818
6066
 
5819
6067
 
6068
+ # -----------------------------------------------------------------------------
6069
+ @HCI_Event.event(
6070
+ [
6071
+ ('status', STATUS_SPEC),
6072
+ ('connection_handle', 2),
6073
+ ('unused', 1),
6074
+ (
6075
+ 'service_type',
6076
+ {
6077
+ 'size': 1,
6078
+ 'mapper': lambda x: HCI_QOS_Setup_Complete_Event.ServiceType(x).name,
6079
+ },
6080
+ ),
6081
+ ]
6082
+ )
6083
+ class HCI_QOS_Setup_Complete_Event(HCI_Event):
6084
+ '''
6085
+ See Bluetooth spec @ 7.7.13 QoS Setup Complete Event
6086
+ '''
6087
+
6088
+ class ServiceType(OpenIntEnum):
6089
+ NO_TRAFFIC_AVAILABLE = 0x00
6090
+ BEST_EFFORT_AVAILABLE = 0x01
6091
+ GUARANTEED_AVAILABLE = 0x02
6092
+
6093
+
5820
6094
  # -----------------------------------------------------------------------------
5821
6095
  @HCI_Event.event(
5822
6096
  [
@@ -6225,6 +6499,23 @@ class HCI_Synchronous_Connection_Changed_Event(HCI_Event):
6225
6499
  '''
6226
6500
 
6227
6501
 
6502
+ # -----------------------------------------------------------------------------
6503
+ @HCI_Event.event(
6504
+ [
6505
+ ('status', STATUS_SPEC),
6506
+ ('connection_handle', 2),
6507
+ ('max_tx_latency', 2),
6508
+ ('max_rx_latency', 2),
6509
+ ('min_remote_timeout', 2),
6510
+ ('min_local_timeout', 2),
6511
+ ]
6512
+ )
6513
+ class HCI_Sniff_Subrating_Event(HCI_Event):
6514
+ '''
6515
+ See Bluetooth spec @ 7.7.37 Sniff Subrating Event
6516
+ '''
6517
+
6518
+
6228
6519
  # -----------------------------------------------------------------------------
6229
6520
  @HCI_Event.event(
6230
6521
  [
bumble/hfp.py CHANGED
@@ -795,29 +795,32 @@ class HfProtocol(pyee.EventEmitter):
795
795
  # Append to the read buffer.
796
796
  self.read_buffer.extend(data)
797
797
 
798
- # Locate header and trailer.
799
- header = self.read_buffer.find(b'\r\n')
800
- trailer = self.read_buffer.find(b'\r\n', header + 2)
801
- if header == -1 or trailer == -1:
802
- return
803
-
804
- # Isolate the AT response code and parameters.
805
- raw_response = self.read_buffer[header + 2 : trailer]
806
- response = AtResponse.parse_from(raw_response)
807
- logger.debug(f"<<< {raw_response.decode()}")
808
-
809
- # Consume the response bytes.
810
- self.read_buffer = self.read_buffer[trailer + 2 :]
811
-
812
- # Forward the received code to the correct queue.
813
- if self.command_lock.locked() and (
814
- response.code in STATUS_CODES or response.code in RESPONSE_CODES
815
- ):
816
- self.response_queue.put_nowait(response)
817
- elif response.code in UNSOLICITED_CODES:
818
- self.unsolicited_queue.put_nowait(response)
819
- else:
820
- logger.warning(f"dropping unexpected response with code '{response.code}'")
798
+ while self.read_buffer:
799
+ # Locate header and trailer.
800
+ header = self.read_buffer.find(b'\r\n')
801
+ trailer = self.read_buffer.find(b'\r\n', header + 2)
802
+ if header == -1 or trailer == -1:
803
+ return
804
+
805
+ # Isolate the AT response code and parameters.
806
+ raw_response = self.read_buffer[header + 2 : trailer]
807
+ response = AtResponse.parse_from(raw_response)
808
+ logger.debug(f"<<< {raw_response.decode()}")
809
+
810
+ # Consume the response bytes.
811
+ self.read_buffer = self.read_buffer[trailer + 2 :]
812
+
813
+ # Forward the received code to the correct queue.
814
+ if self.command_lock.locked() and (
815
+ response.code in STATUS_CODES or response.code in RESPONSE_CODES
816
+ ):
817
+ self.response_queue.put_nowait(response)
818
+ elif response.code in UNSOLICITED_CODES:
819
+ self.unsolicited_queue.put_nowait(response)
820
+ else:
821
+ logger.warning(
822
+ f"dropping unexpected response with code '{response.code}'"
823
+ )
821
824
 
822
825
  async def execute_command(
823
826
  self,
@@ -1244,31 +1247,32 @@ class AgProtocol(pyee.EventEmitter):
1244
1247
  # Append to the read buffer.
1245
1248
  self.read_buffer.extend(data)
1246
1249
 
1247
- # Locate the trailer.
1248
- trailer = self.read_buffer.find(b'\r')
1249
- if trailer == -1:
1250
- return
1251
-
1252
- # Isolate the AT response code and parameters.
1253
- raw_command = self.read_buffer[:trailer]
1254
- command = AtCommand.parse_from(raw_command)
1255
- logger.debug(f"<<< {raw_command.decode()}")
1256
-
1257
- # Consume the response bytes.
1258
- self.read_buffer = self.read_buffer[trailer + 1 :]
1259
-
1260
- if command.sub_code == AtCommand.SubCode.TEST:
1261
- handler_name = f'_on_{command.code.lower()}_test'
1262
- elif command.sub_code == AtCommand.SubCode.READ:
1263
- handler_name = f'_on_{command.code.lower()}_read'
1264
- else:
1265
- handler_name = f'_on_{command.code.lower()}'
1266
-
1267
- if handler := getattr(self, handler_name, None):
1268
- handler(*command.parameters)
1269
- else:
1270
- logger.warning('Handler %s not found', handler_name)
1271
- self.send_response('ERROR')
1250
+ while self.read_buffer:
1251
+ # Locate the trailer.
1252
+ trailer = self.read_buffer.find(b'\r')
1253
+ if trailer == -1:
1254
+ return
1255
+
1256
+ # Isolate the AT response code and parameters.
1257
+ raw_command = self.read_buffer[:trailer]
1258
+ command = AtCommand.parse_from(raw_command)
1259
+ logger.debug(f"<<< {raw_command.decode()}")
1260
+
1261
+ # Consume the response bytes.
1262
+ self.read_buffer = self.read_buffer[trailer + 1 :]
1263
+
1264
+ if command.sub_code == AtCommand.SubCode.TEST:
1265
+ handler_name = f'_on_{command.code.lower()}_test'
1266
+ elif command.sub_code == AtCommand.SubCode.READ:
1267
+ handler_name = f'_on_{command.code.lower()}_read'
1268
+ else:
1269
+ handler_name = f'_on_{command.code.lower()}'
1270
+
1271
+ if handler := getattr(self, handler_name, None):
1272
+ handler(*command.parameters)
1273
+ else:
1274
+ logger.warning('Handler %s not found', handler_name)
1275
+ self.send_response('ERROR')
1272
1276
 
1273
1277
  def send_response(self, response: str) -> None:
1274
1278
  """Sends an AT response."""