bumble 0.0.211__py3-none-any.whl → 0.0.213__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 (95) hide show
  1. bumble/_version.py +2 -2
  2. bumble/a2dp.py +6 -0
  3. bumble/apps/README.md +0 -3
  4. bumble/apps/auracast.py +11 -9
  5. bumble/apps/bench.py +482 -31
  6. bumble/apps/console.py +5 -5
  7. bumble/apps/controller_info.py +47 -10
  8. bumble/apps/controller_loopback.py +7 -3
  9. bumble/apps/controllers.py +2 -2
  10. bumble/apps/device_info.py +2 -2
  11. bumble/apps/gatt_dump.py +2 -2
  12. bumble/apps/gg_bridge.py +2 -2
  13. bumble/apps/hci_bridge.py +2 -2
  14. bumble/apps/l2cap_bridge.py +2 -2
  15. bumble/apps/lea_unicast/app.py +6 -1
  16. bumble/apps/pair.py +204 -43
  17. bumble/apps/pandora_server.py +2 -2
  18. bumble/apps/rfcomm_bridge.py +1 -1
  19. bumble/apps/scan.py +2 -2
  20. bumble/apps/show.py +4 -2
  21. bumble/apps/speaker/speaker.html +1 -0
  22. bumble/apps/speaker/speaker.js +113 -62
  23. bumble/apps/speaker/speaker.py +126 -18
  24. bumble/at.py +4 -4
  25. bumble/att.py +15 -18
  26. bumble/avc.py +7 -7
  27. bumble/avctp.py +5 -5
  28. bumble/avdtp.py +138 -88
  29. bumble/avrcp.py +52 -58
  30. bumble/colors.py +2 -2
  31. bumble/controller.py +84 -23
  32. bumble/core.py +13 -7
  33. bumble/{crypto.py → crypto/__init__.py} +11 -95
  34. bumble/crypto/builtin.py +652 -0
  35. bumble/crypto/cryptography.py +84 -0
  36. bumble/device.py +688 -345
  37. bumble/drivers/__init__.py +2 -2
  38. bumble/drivers/common.py +0 -2
  39. bumble/drivers/intel.py +40 -40
  40. bumble/drivers/rtk.py +28 -35
  41. bumble/gatt.py +7 -9
  42. bumble/gatt_adapters.py +4 -5
  43. bumble/gatt_client.py +31 -34
  44. bumble/gatt_server.py +15 -17
  45. bumble/hci.py +2635 -2878
  46. bumble/helpers.py +4 -5
  47. bumble/hfp.py +76 -57
  48. bumble/hid.py +24 -12
  49. bumble/host.py +117 -34
  50. bumble/keys.py +68 -52
  51. bumble/l2cap.py +329 -403
  52. bumble/link.py +6 -270
  53. bumble/pairing.py +23 -20
  54. bumble/pandora/__init__.py +1 -1
  55. bumble/pandora/config.py +2 -2
  56. bumble/pandora/device.py +6 -6
  57. bumble/pandora/host.py +38 -39
  58. bumble/pandora/l2cap.py +4 -4
  59. bumble/pandora/security.py +73 -57
  60. bumble/pandora/utils.py +3 -3
  61. bumble/profiles/aics.py +3 -5
  62. bumble/profiles/ancs.py +3 -1
  63. bumble/profiles/ascs.py +143 -136
  64. bumble/profiles/asha.py +13 -8
  65. bumble/profiles/bap.py +3 -4
  66. bumble/profiles/csip.py +3 -5
  67. bumble/profiles/device_information_service.py +2 -2
  68. bumble/profiles/gap.py +2 -2
  69. bumble/profiles/gatt_service.py +1 -3
  70. bumble/profiles/hap.py +42 -58
  71. bumble/profiles/le_audio.py +4 -4
  72. bumble/profiles/mcp.py +16 -13
  73. bumble/profiles/vcs.py +8 -10
  74. bumble/profiles/vocs.py +6 -9
  75. bumble/rfcomm.py +27 -18
  76. bumble/rtp.py +1 -2
  77. bumble/sdp.py +2 -2
  78. bumble/smp.py +71 -69
  79. bumble/tools/rtk_util.py +2 -2
  80. bumble/transport/__init__.py +2 -16
  81. bumble/transport/android_netsim.py +5 -5
  82. bumble/transport/common.py +4 -4
  83. bumble/transport/pyusb.py +2 -2
  84. bumble/utils.py +2 -5
  85. bumble/vendor/android/hci.py +118 -200
  86. bumble/vendor/zephyr/hci.py +32 -27
  87. {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/METADATA +5 -5
  88. {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/RECORD +92 -93
  89. {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/WHEEL +1 -1
  90. {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/entry_points.txt +0 -1
  91. bumble/apps/link_relay/__init__.py +0 -0
  92. bumble/apps/link_relay/link_relay.py +0 -289
  93. bumble/apps/link_relay/logging.yml +0 -21
  94. {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/licenses/LICENSE +0 -0
  95. {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/top_level.txt +0 -0
bumble/avrcp.py CHANGED
@@ -26,14 +26,11 @@ from typing import (
26
26
  Awaitable,
27
27
  Callable,
28
28
  cast,
29
- Dict,
30
29
  Iterable,
31
30
  List,
32
31
  Optional,
33
32
  Sequence,
34
33
  SupportsBytes,
35
- Tuple,
36
- Type,
37
34
  TypeVar,
38
35
  Union,
39
36
  )
@@ -53,19 +50,10 @@ from bumble.sdp import (
53
50
  ServiceAttribute,
54
51
  )
55
52
  from bumble import utils
56
- from bumble.core import (
57
- InvalidArgumentError,
58
- ProtocolError,
59
- BT_L2CAP_PROTOCOL_ID,
60
- BT_AVCTP_PROTOCOL_ID,
61
- BT_AV_REMOTE_CONTROL_SERVICE,
62
- BT_AV_REMOTE_CONTROL_CONTROLLER_SERVICE,
63
- BT_AV_REMOTE_CONTROL_TARGET_SERVICE,
64
- )
53
+ from bumble import core
65
54
  from bumble import l2cap
66
55
  from bumble import avc
67
56
  from bumble import avctp
68
- from bumble import utils
69
57
 
70
58
 
71
59
  # -----------------------------------------------------------------------------
@@ -84,10 +72,10 @@ AVRCP_BLUETOOTH_SIG_COMPANY_ID = 0x001958
84
72
  # -----------------------------------------------------------------------------
85
73
  def make_controller_service_sdp_records(
86
74
  service_record_handle: int,
87
- avctp_version: Tuple[int, int] = (1, 4),
88
- avrcp_version: Tuple[int, int] = (1, 6),
75
+ avctp_version: tuple[int, int] = (1, 4),
76
+ avrcp_version: tuple[int, int] = (1, 6),
89
77
  supported_features: int = 1,
90
- ) -> List[ServiceAttribute]:
78
+ ) -> list[ServiceAttribute]:
91
79
  # TODO: support a way to compute the supported features from a feature list
92
80
  avctp_version_int = avctp_version[0] << 8 | avctp_version[1]
93
81
  avrcp_version_int = avrcp_version[0] << 8 | avrcp_version[1]
@@ -105,8 +93,8 @@ def make_controller_service_sdp_records(
105
93
  SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID,
106
94
  DataElement.sequence(
107
95
  [
108
- DataElement.uuid(BT_AV_REMOTE_CONTROL_SERVICE),
109
- DataElement.uuid(BT_AV_REMOTE_CONTROL_CONTROLLER_SERVICE),
96
+ DataElement.uuid(core.BT_AV_REMOTE_CONTROL_SERVICE),
97
+ DataElement.uuid(core.BT_AV_REMOTE_CONTROL_CONTROLLER_SERVICE),
110
98
  ]
111
99
  ),
112
100
  ),
@@ -116,13 +104,13 @@ def make_controller_service_sdp_records(
116
104
  [
117
105
  DataElement.sequence(
118
106
  [
119
- DataElement.uuid(BT_L2CAP_PROTOCOL_ID),
107
+ DataElement.uuid(core.BT_L2CAP_PROTOCOL_ID),
120
108
  DataElement.unsigned_integer_16(avctp.AVCTP_PSM),
121
109
  ]
122
110
  ),
123
111
  DataElement.sequence(
124
112
  [
125
- DataElement.uuid(BT_AVCTP_PROTOCOL_ID),
113
+ DataElement.uuid(core.BT_AVCTP_PROTOCOL_ID),
126
114
  DataElement.unsigned_integer_16(avctp_version_int),
127
115
  ]
128
116
  ),
@@ -135,7 +123,7 @@ def make_controller_service_sdp_records(
135
123
  [
136
124
  DataElement.sequence(
137
125
  [
138
- DataElement.uuid(BT_AV_REMOTE_CONTROL_SERVICE),
126
+ DataElement.uuid(core.BT_AV_REMOTE_CONTROL_SERVICE),
139
127
  DataElement.unsigned_integer_16(avrcp_version_int),
140
128
  ]
141
129
  ),
@@ -152,10 +140,10 @@ def make_controller_service_sdp_records(
152
140
  # -----------------------------------------------------------------------------
153
141
  def make_target_service_sdp_records(
154
142
  service_record_handle: int,
155
- avctp_version: Tuple[int, int] = (1, 4),
156
- avrcp_version: Tuple[int, int] = (1, 6),
143
+ avctp_version: tuple[int, int] = (1, 4),
144
+ avrcp_version: tuple[int, int] = (1, 6),
157
145
  supported_features: int = 0x23,
158
- ) -> List[ServiceAttribute]:
146
+ ) -> list[ServiceAttribute]:
159
147
  # TODO: support a way to compute the supported features from a feature list
160
148
  avctp_version_int = avctp_version[0] << 8 | avctp_version[1]
161
149
  avrcp_version_int = avrcp_version[0] << 8 | avrcp_version[1]
@@ -173,7 +161,7 @@ def make_target_service_sdp_records(
173
161
  SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID,
174
162
  DataElement.sequence(
175
163
  [
176
- DataElement.uuid(BT_AV_REMOTE_CONTROL_TARGET_SERVICE),
164
+ DataElement.uuid(core.BT_AV_REMOTE_CONTROL_TARGET_SERVICE),
177
165
  ]
178
166
  ),
179
167
  ),
@@ -183,13 +171,13 @@ def make_target_service_sdp_records(
183
171
  [
184
172
  DataElement.sequence(
185
173
  [
186
- DataElement.uuid(BT_L2CAP_PROTOCOL_ID),
174
+ DataElement.uuid(core.BT_L2CAP_PROTOCOL_ID),
187
175
  DataElement.unsigned_integer_16(avctp.AVCTP_PSM),
188
176
  ]
189
177
  ),
190
178
  DataElement.sequence(
191
179
  [
192
- DataElement.uuid(BT_AVCTP_PROTOCOL_ID),
180
+ DataElement.uuid(core.BT_AVCTP_PROTOCOL_ID),
193
181
  DataElement.unsigned_integer_16(avctp_version_int),
194
182
  ]
195
183
  ),
@@ -202,7 +190,7 @@ def make_target_service_sdp_records(
202
190
  [
203
191
  DataElement.sequence(
204
192
  [
205
- DataElement.uuid(BT_AV_REMOTE_CONTROL_SERVICE),
193
+ DataElement.uuid(core.BT_AV_REMOTE_CONTROL_SERVICE),
206
194
  DataElement.unsigned_integer_16(avrcp_version_int),
207
195
  ]
208
196
  ),
@@ -291,7 +279,7 @@ class Command:
291
279
  pdu_id: Protocol.PduId
292
280
  parameter: bytes
293
281
 
294
- def to_string(self, properties: Dict[str, str]) -> str:
282
+ def to_string(self, properties: dict[str, str]) -> str:
295
283
  properties_str = ",".join(
296
284
  [f"{name}={value}" for name, value in properties.items()]
297
285
  )
@@ -337,7 +325,7 @@ class GetPlayStatusCommand(Command):
337
325
  # -----------------------------------------------------------------------------
338
326
  class GetElementAttributesCommand(Command):
339
327
  identifier: int
340
- attribute_ids: List[MediaAttributeId]
328
+ attribute_ids: list[MediaAttributeId]
341
329
 
342
330
  @classmethod
343
331
  def from_bytes(cls, pdu: bytes) -> GetElementAttributesCommand:
@@ -409,7 +397,7 @@ class Response:
409
397
  pdu_id: Protocol.PduId
410
398
  parameter: bytes
411
399
 
412
- def to_string(self, properties: Dict[str, str]) -> str:
400
+ def to_string(self, properties: dict[str, str]) -> str:
413
401
  properties_str = ",".join(
414
402
  [f"{name}={value}" for name, value in properties.items()]
415
403
  )
@@ -454,7 +442,7 @@ class NotImplementedResponse(Response):
454
442
  # -----------------------------------------------------------------------------
455
443
  class GetCapabilitiesResponse(Response):
456
444
  capability_id: GetCapabilitiesCommand.CapabilityId
457
- capabilities: List[Union[SupportsBytes, bytes]]
445
+ capabilities: list[Union[SupportsBytes, bytes]]
458
446
 
459
447
  @classmethod
460
448
  def from_bytes(cls, pdu: bytes) -> GetCapabilitiesResponse:
@@ -467,7 +455,7 @@ class GetCapabilitiesResponse(Response):
467
455
  capability_id = GetCapabilitiesCommand.CapabilityId(pdu[0])
468
456
  capability_count = pdu[1]
469
457
 
470
- capabilities: List[Union[SupportsBytes, bytes]]
458
+ capabilities: list[Union[SupportsBytes, bytes]]
471
459
  if capability_id == GetCapabilitiesCommand.CapabilityId.EVENTS_SUPPORTED:
472
460
  capabilities = [EventId(pdu[2 + x]) for x in range(capability_count)]
473
461
  else:
@@ -540,13 +528,13 @@ class GetPlayStatusResponse(Response):
540
528
 
541
529
  # -----------------------------------------------------------------------------
542
530
  class GetElementAttributesResponse(Response):
543
- attributes: List[MediaAttribute]
531
+ attributes: list[MediaAttribute]
544
532
 
545
533
  @classmethod
546
534
  def from_bytes(cls, pdu: bytes) -> GetElementAttributesResponse:
547
535
  num_attributes = pdu[0]
548
536
  offset = 1
549
- attributes: List[MediaAttribute] = []
537
+ attributes: list[MediaAttribute] = []
550
538
  for _ in range(num_attributes):
551
539
  (
552
540
  attribute_id_int,
@@ -817,7 +805,7 @@ class PlayerApplicationSettingChangedEvent(Event):
817
805
  attribute_id: ApplicationSetting.AttributeId
818
806
  value_id: utils.OpenIntEnum
819
807
 
820
- player_application_settings: List[Setting]
808
+ player_application_settings: list[Setting]
821
809
 
822
810
  @classmethod
823
811
  def from_bytes(cls, pdu: bytes) -> PlayerApplicationSettingChangedEvent:
@@ -939,7 +927,7 @@ class VolumeChangedEvent(Event):
939
927
 
940
928
 
941
929
  # -----------------------------------------------------------------------------
942
- EVENT_SUBCLASSES: Dict[EventId, Type[Event]] = {
930
+ EVENT_SUBCLASSES: dict[EventId, type[Event]] = {
943
931
  EventId.PLAYBACK_STATUS_CHANGED: PlaybackStatusChangedEvent,
944
932
  EventId.PLAYBACK_POS_CHANGED: PlaybackPositionChangedEvent,
945
933
  EventId.TRACK_CHANGED: TrackChangedEvent,
@@ -967,14 +955,14 @@ class Delegate:
967
955
  def __init__(self, status_code: Protocol.StatusCode) -> None:
968
956
  self.status_code = status_code
969
957
 
970
- supported_events: List[EventId]
958
+ supported_events: list[EventId]
971
959
  volume: int
972
960
 
973
961
  def __init__(self, supported_events: Iterable[EventId] = ()) -> None:
974
962
  self.supported_events = list(supported_events)
975
963
  self.volume = 0
976
964
 
977
- async def get_supported_events(self) -> List[EventId]:
965
+ async def get_supported_events(self) -> list[EventId]:
978
966
  return self.supported_events
979
967
 
980
968
  async def set_absolute_volume(self, volume: int) -> None:
@@ -996,6 +984,10 @@ class Delegate:
996
984
  class Protocol(utils.EventEmitter):
997
985
  """AVRCP Controller and Target protocol."""
998
986
 
987
+ EVENT_CONNECTION = "connection"
988
+ EVENT_START = "start"
989
+ EVENT_STOP = "stop"
990
+
999
991
  class PacketType(enum.IntEnum):
1000
992
  SINGLE = 0b00
1001
993
  START = 0b01
@@ -1120,8 +1112,8 @@ class Protocol(utils.EventEmitter):
1120
1112
  receive_response_state: Optional[ReceiveResponseState]
1121
1113
  avctp_protocol: Optional[avctp.Protocol]
1122
1114
  free_commands: asyncio.Queue
1123
- pending_commands: Dict[int, PendingCommand] # Pending commands, by label
1124
- notification_listeners: Dict[EventId, NotificationListener]
1115
+ pending_commands: dict[int, PendingCommand] # Pending commands, by label
1116
+ notification_listeners: dict[EventId, NotificationListener]
1125
1117
 
1126
1118
  @staticmethod
1127
1119
  def _check_vendor_dependent_frame(
@@ -1186,7 +1178,7 @@ class Protocol(utils.EventEmitter):
1186
1178
 
1187
1179
  @staticmethod
1188
1180
  def _check_response(
1189
- response_context: ResponseContext, expected_type: Type[_R]
1181
+ response_context: ResponseContext, expected_type: type[_R]
1190
1182
  ) -> _R:
1191
1183
  if isinstance(response_context, Protocol.FinalResponse):
1192
1184
  if (
@@ -1207,7 +1199,7 @@ class Protocol(utils.EventEmitter):
1207
1199
  def _delegate_command(
1208
1200
  self, transaction_label: int, command: Command, method: Awaitable
1209
1201
  ) -> None:
1210
- async def call():
1202
+ async def call() -> None:
1211
1203
  try:
1212
1204
  await method
1213
1205
  except Delegate.Error as error:
@@ -1226,7 +1218,7 @@ class Protocol(utils.EventEmitter):
1226
1218
 
1227
1219
  utils.AsyncRunner.spawn(call())
1228
1220
 
1229
- async def get_supported_events(self) -> List[EventId]:
1221
+ async def get_supported_events(self) -> list[EventId]:
1230
1222
  """Get the list of events supported by the connected peer."""
1231
1223
  response_context = await self.send_avrcp_command(
1232
1224
  avc.CommandFrame.CommandType.STATUS,
@@ -1249,7 +1241,7 @@ class Protocol(utils.EventEmitter):
1249
1241
 
1250
1242
  async def get_element_attributes(
1251
1243
  self, element_identifier: int, attribute_ids: Sequence[MediaAttributeId]
1252
- ) -> List[MediaAttribute]:
1244
+ ) -> list[MediaAttribute]:
1253
1245
  """Get element attributes from the connected peer."""
1254
1246
  response_context = await self.send_avrcp_command(
1255
1247
  avc.CommandFrame.CommandType.STATUS,
@@ -1331,7 +1323,7 @@ class Protocol(utils.EventEmitter):
1331
1323
 
1332
1324
  async def monitor_player_application_settings(
1333
1325
  self,
1334
- ) -> AsyncIterator[List[PlayerApplicationSettingChangedEvent.Setting]]:
1326
+ ) -> AsyncIterator[list[PlayerApplicationSettingChangedEvent.Setting]]:
1335
1327
  """Monitor Player Application Setting changes from the connected peer."""
1336
1328
  async for event in self.monitor_events(
1337
1329
  EventId.PLAYER_APPLICATION_SETTING_CHANGED, 0
@@ -1411,7 +1403,7 @@ class Protocol(utils.EventEmitter):
1411
1403
  def notify_track_changed(self, identifier: bytes) -> None:
1412
1404
  """Notify the connected peer of a Track change."""
1413
1405
  if len(identifier) != 8:
1414
- raise InvalidArgumentError("identifier must be 8 bytes")
1406
+ raise core.InvalidArgumentError("identifier must be 8 bytes")
1415
1407
  self.notify_event(TrackChangedEvent(identifier))
1416
1408
 
1417
1409
  def notify_playback_position_changed(self, position: int) -> None:
@@ -1456,9 +1448,11 @@ class Protocol(utils.EventEmitter):
1456
1448
 
1457
1449
  def _on_avctp_connection(self, l2cap_channel: l2cap.ClassicChannel) -> None:
1458
1450
  logger.debug("AVCTP connection established")
1459
- l2cap_channel.on("open", lambda: self._on_avctp_channel_open(l2cap_channel))
1451
+ l2cap_channel.on(
1452
+ l2cap_channel.EVENT_OPEN, lambda: self._on_avctp_channel_open(l2cap_channel)
1453
+ )
1460
1454
 
1461
- self.emit("connection")
1455
+ self.emit(self.EVENT_CONNECTION)
1462
1456
 
1463
1457
  def _on_avctp_channel_open(self, l2cap_channel: l2cap.ClassicChannel) -> None:
1464
1458
  logger.debug("AVCTP channel open")
@@ -1473,15 +1467,15 @@ class Protocol(utils.EventEmitter):
1473
1467
  self.avctp_protocol.register_response_handler(
1474
1468
  AVRCP_PID, self._on_avctp_response
1475
1469
  )
1476
- l2cap_channel.on("close", self._on_avctp_channel_close)
1470
+ l2cap_channel.on(l2cap_channel.EVENT_CLOSE, self._on_avctp_channel_close)
1477
1471
 
1478
- self.emit("start")
1472
+ self.emit(self.EVENT_START)
1479
1473
 
1480
1474
  def _on_avctp_channel_close(self) -> None:
1481
1475
  logger.debug("AVCTP channel closed")
1482
1476
  self.avctp_protocol = None
1483
1477
 
1484
- self.emit("stop")
1478
+ self.emit(self.EVENT_STOP)
1485
1479
 
1486
1480
  def _on_avctp_command(
1487
1481
  self, transaction_label: int, command: avc.CommandFrame
@@ -1676,7 +1670,7 @@ class Protocol(utils.EventEmitter):
1676
1670
  else:
1677
1671
  logger.debug("unexpected PDU ID")
1678
1672
  pending_command.response.set_exception(
1679
- ProtocolError(
1673
+ core.ProtocolError(
1680
1674
  error_code=None,
1681
1675
  error_namespace="avrcp",
1682
1676
  details="unexpected PDU ID",
@@ -1685,7 +1679,7 @@ class Protocol(utils.EventEmitter):
1685
1679
  else:
1686
1680
  logger.debug("unexpected response code")
1687
1681
  pending_command.response.set_exception(
1688
- ProtocolError(
1682
+ core.ProtocolError(
1689
1683
  error_code=None,
1690
1684
  error_namespace="avrcp",
1691
1685
  details="unexpected response code",
@@ -1863,12 +1857,12 @@ class Protocol(utils.EventEmitter):
1863
1857
  ) -> None:
1864
1858
  logger.debug(f"<<< AVRCP command PDU: {command}")
1865
1859
 
1866
- async def get_supported_events():
1860
+ async def get_supported_events() -> None:
1867
1861
  if (
1868
1862
  command.capability_id
1869
1863
  != GetCapabilitiesCommand.CapabilityId.EVENTS_SUPPORTED
1870
1864
  ):
1871
- raise Protocol.InvalidParameterError
1865
+ raise core.InvalidArgumentError()
1872
1866
 
1873
1867
  supported_events = await self.delegate.get_supported_events()
1874
1868
  self.send_avrcp_response(
@@ -1884,7 +1878,7 @@ class Protocol(utils.EventEmitter):
1884
1878
  ) -> None:
1885
1879
  logger.debug(f"<<< AVRCP command PDU: {command}")
1886
1880
 
1887
- async def set_absolute_volume():
1881
+ async def set_absolute_volume() -> None:
1888
1882
  await self.delegate.set_absolute_volume(command.volume)
1889
1883
  effective_volume = await self.delegate.get_absolute_volume()
1890
1884
  self.send_avrcp_response(
@@ -1900,7 +1894,7 @@ class Protocol(utils.EventEmitter):
1900
1894
  ) -> None:
1901
1895
  logger.debug(f"<<< AVRCP command PDU: {command}")
1902
1896
 
1903
- async def register_notification():
1897
+ async def register_notification() -> None:
1904
1898
  # Check if the event is supported.
1905
1899
  supported_events = await self.delegate.get_supported_events()
1906
1900
  if command.event_id not in supported_events:
bumble/colors.py CHANGED
@@ -13,7 +13,7 @@
13
13
  # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
14
 
15
15
  from functools import partial
16
- from typing import List, Optional, Union
16
+ from typing import Optional, Union
17
17
 
18
18
 
19
19
  class ColorError(ValueError):
@@ -65,7 +65,7 @@ def color(
65
65
  bg: Optional[ColorSpec] = None,
66
66
  style: Optional[str] = None,
67
67
  ) -> str:
68
- codes: List[ColorSpec] = []
68
+ codes: list[ColorSpec] = []
69
69
 
70
70
  if fg:
71
71
  codes.append(_color_code(fg, 30))
bumble/controller.py CHANGED
@@ -27,7 +27,7 @@ from bumble.colors import color
27
27
  from bumble.core import (
28
28
  PhysicalTransport,
29
29
  )
30
-
30
+ from bumble import hci
31
31
  from bumble.hci import (
32
32
  HCI_ACL_DATA_PACKET,
33
33
  HCI_COMMAND_DISALLOWED_ERROR,
@@ -63,7 +63,7 @@ from bumble.hci import (
63
63
  HCI_Packet,
64
64
  HCI_Role_Change_Event,
65
65
  )
66
- from typing import Optional, Union, Dict, Any, TYPE_CHECKING
66
+ from typing import Optional, Union, Any, TYPE_CHECKING
67
67
 
68
68
  if TYPE_CHECKING:
69
69
  from bumble.link import LocalLink
@@ -108,7 +108,9 @@ class Connection:
108
108
  def on_hci_acl_data_packet(self, packet):
109
109
  self.assembler.feed_packet(packet)
110
110
  self.controller.send_hci_packet(
111
- HCI_Number_Of_Completed_Packets_Event([(self.handle, 1)])
111
+ HCI_Number_Of_Completed_Packets_Event(
112
+ connection_handles=[self.handle], num_completed_packets=[1]
113
+ )
112
114
  )
113
115
 
114
116
  def on_acl_pdu(self, data):
@@ -132,17 +134,17 @@ class Controller:
132
134
  self.hci_sink = None
133
135
  self.link = link
134
136
 
135
- self.central_connections: Dict[Address, Connection] = (
137
+ self.central_connections: dict[Address, Connection] = (
136
138
  {}
137
139
  ) # Connections where this controller is the central
138
- self.peripheral_connections: Dict[Address, Connection] = (
140
+ self.peripheral_connections: dict[Address, Connection] = (
139
141
  {}
140
142
  ) # Connections where this controller is the peripheral
141
- self.classic_connections: Dict[Address, Connection] = (
143
+ self.classic_connections: dict[Address, Connection] = (
142
144
  {}
143
145
  ) # Connections in BR/EDR
144
- self.central_cis_links: Dict[int, CisLink] = {} # CIS links by handle
145
- self.peripheral_cis_links: Dict[int, CisLink] = {} # CIS links by handle
146
+ self.central_cis_links: dict[int, CisLink] = {} # CIS links by handle
147
+ self.peripheral_cis_links: dict[int, CisLink] = {} # CIS links by handle
146
148
 
147
149
  self.hci_version = HCI_VERSION_BLUETOOTH_CORE_5_0
148
150
  self.hci_revision = 0
@@ -392,7 +394,7 @@ class Controller:
392
394
  peer_address=peer_address,
393
395
  link=self.link,
394
396
  transport=PhysicalTransport.LE,
395
- link_type=HCI_Connection_Complete_Event.ACL_LINK_TYPE,
397
+ link_type=HCI_Connection_Complete_Event.LinkType.ACL,
396
398
  )
397
399
  self.peripheral_connections[peer_address] = connection
398
400
  logger.debug(f'New PERIPHERAL connection handle: 0x{connection_handle:04X}')
@@ -452,7 +454,7 @@ class Controller:
452
454
  peer_address=peer_address,
453
455
  link=self.link,
454
456
  transport=PhysicalTransport.LE,
455
- link_type=HCI_Connection_Complete_Event.ACL_LINK_TYPE,
457
+ link_type=HCI_Connection_Complete_Event.LinkType.ACL,
456
458
  )
457
459
  self.central_connections[peer_address] = connection
458
460
  logger.debug(
@@ -542,15 +544,14 @@ class Controller:
542
544
  acl_packet = HCI_AclDataPacket(connection.handle, 2, 0, len(data), data)
543
545
  self.send_hci_packet(acl_packet)
544
546
 
545
- def on_link_advertising_data(self, sender_address, data):
547
+ def on_link_advertising_data(self, sender_address: Address, data: bytes):
546
548
  # Ignore if we're not scanning
547
549
  if self.le_scan_enable == 0:
548
550
  return
549
551
 
550
552
  # Send a scan report
551
553
  report = HCI_LE_Advertising_Report_Event.Report(
552
- HCI_LE_Advertising_Report_Event.Report.FIELDS,
553
- event_type=HCI_LE_Advertising_Report_Event.ADV_IND,
554
+ event_type=HCI_LE_Advertising_Report_Event.EventType.ADV_IND,
554
555
  address_type=sender_address.address_type,
555
556
  address=sender_address,
556
557
  data=data,
@@ -560,8 +561,7 @@ class Controller:
560
561
 
561
562
  # Simulate a scan response
562
563
  report = HCI_LE_Advertising_Report_Event.Report(
563
- HCI_LE_Advertising_Report_Event.Report.FIELDS,
564
- event_type=HCI_LE_Advertising_Report_Event.SCAN_RSP,
564
+ event_type=HCI_LE_Advertising_Report_Event.EventType.SCAN_RSP,
565
565
  address_type=sender_address.address_type,
566
566
  address=sender_address,
567
567
  data=data,
@@ -618,8 +618,8 @@ class Controller:
618
618
  cis_sync_delay=0,
619
619
  transport_latency_c_to_p=0,
620
620
  transport_latency_p_to_c=0,
621
- phy_c_to_p=0,
622
- phy_p_to_c=0,
621
+ phy_c_to_p=1,
622
+ phy_p_to_c=1,
623
623
  nse=0,
624
624
  bn_c_to_p=0,
625
625
  bn_p_to_c=0,
@@ -695,7 +695,7 @@ class Controller:
695
695
  peer_address=peer_address,
696
696
  link=self.link,
697
697
  transport=PhysicalTransport.BR_EDR,
698
- link_type=HCI_Connection_Complete_Event.ACL_LINK_TYPE,
698
+ link_type=HCI_Connection_Complete_Event.LinkType.ACL,
699
699
  )
700
700
  self.classic_connections[peer_address] = connection
701
701
  logger.debug(
@@ -709,7 +709,7 @@ class Controller:
709
709
  connection_handle=connection_handle,
710
710
  bd_addr=peer_address,
711
711
  encryption_enabled=False,
712
- link_type=HCI_Connection_Complete_Event.ACL_LINK_TYPE,
712
+ link_type=HCI_Connection_Complete_Event.LinkType.ACL,
713
713
  )
714
714
  )
715
715
  else:
@@ -720,7 +720,7 @@ class Controller:
720
720
  connection_handle=0,
721
721
  bd_addr=peer_address,
722
722
  encryption_enabled=False,
723
- link_type=HCI_Connection_Complete_Event.ACL_LINK_TYPE,
723
+ link_type=HCI_Connection_Complete_Event.LinkType.ACL,
724
724
  )
725
725
  )
726
726
 
@@ -945,7 +945,7 @@ class Controller:
945
945
  )
946
946
  )
947
947
  self.link.classic_sco_connect(
948
- self, connection.peer_address, HCI_Connection_Complete_Event.ESCO_LINK_TYPE
948
+ self, connection.peer_address, HCI_Connection_Complete_Event.LinkType.ESCO
949
949
  )
950
950
 
951
951
  def on_hci_enhanced_accept_synchronous_connection_request_command(self, command):
@@ -974,10 +974,71 @@ class Controller:
974
974
  )
975
975
  )
976
976
  self.link.classic_accept_sco_connection(
977
- self, connection.peer_address, HCI_Connection_Complete_Event.ESCO_LINK_TYPE
977
+ self, connection.peer_address, HCI_Connection_Complete_Event.LinkType.ESCO
978
+ )
979
+
980
+ def on_hci_sniff_mode_command(self, command: hci.HCI_Sniff_Mode_Command):
981
+ '''
982
+ See Bluetooth spec Vol 4, Part E - 7.2.2 Sniff Mode command
983
+ '''
984
+ if self.link is None:
985
+ self.send_hci_packet(
986
+ hci.HCI_Command_Status_Event(
987
+ status=hci.HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
988
+ num_hci_command_packets=1,
989
+ command_opcode=command.op_code,
990
+ )
991
+ )
992
+ return
993
+
994
+ self.send_hci_packet(
995
+ hci.HCI_Command_Status_Event(
996
+ status=HCI_SUCCESS,
997
+ num_hci_command_packets=1,
998
+ command_opcode=command.op_code,
999
+ )
1000
+ )
1001
+ self.send_hci_packet(
1002
+ hci.HCI_Mode_Change_Event(
1003
+ status=HCI_SUCCESS,
1004
+ connection_handle=command.connection_handle,
1005
+ current_mode=hci.HCI_Mode_Change_Event.Mode.SNIFF,
1006
+ interval=2,
1007
+ )
1008
+ )
1009
+
1010
+ def on_hci_exit_sniff_mode_command(self, command: hci.HCI_Exit_Sniff_Mode_Command):
1011
+ '''
1012
+ See Bluetooth spec Vol 4, Part E - 7.2.3 Exit Sniff Mode command
1013
+ '''
1014
+
1015
+ if self.link is None:
1016
+ self.send_hci_packet(
1017
+ hci.HCI_Command_Status_Event(
1018
+ status=hci.HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
1019
+ num_hci_command_packets=1,
1020
+ command_opcode=command.op_code,
1021
+ )
1022
+ )
1023
+ return
1024
+
1025
+ self.send_hci_packet(
1026
+ hci.HCI_Command_Status_Event(
1027
+ status=HCI_SUCCESS,
1028
+ num_hci_command_packets=1,
1029
+ command_opcode=command.op_code,
1030
+ )
1031
+ )
1032
+ self.send_hci_packet(
1033
+ hci.HCI_Mode_Change_Event(
1034
+ status=HCI_SUCCESS,
1035
+ connection_handle=command.connection_handle,
1036
+ current_mode=hci.HCI_Mode_Change_Event.Mode.ACTIVE,
1037
+ interval=2,
1038
+ )
978
1039
  )
979
1040
 
980
- def on_hci_switch_role_command(self, command):
1041
+ def on_hci_switch_role_command(self, command: hci.HCI_Switch_Role_Command):
981
1042
  '''
982
1043
  See Bluetooth spec Vol 4, Part E - 7.2.8 Switch Role command
983
1044
  '''
bumble/core.py CHANGED
@@ -809,7 +809,7 @@ class Appearance:
809
809
  STICK_PC = 0x0F
810
810
 
811
811
  class WatchSubcategory(utils.OpenIntEnum):
812
- GENENERIC_WATCH = 0x00
812
+ GENERIC_WATCH = 0x00
813
813
  SPORTS_WATCH = 0x01
814
814
  SMARTWATCH = 0x02
815
815
 
@@ -1127,7 +1127,7 @@ class Appearance:
1127
1127
  TURNTABLE = 0x05
1128
1128
  CD_PLAYER = 0x06
1129
1129
  DVD_PLAYER = 0x07
1130
- BLUERAY_PLAYER = 0x08
1130
+ BLURAY_PLAYER = 0x08
1131
1131
  OPTICAL_DISC_PLAYER = 0x09
1132
1132
  SET_TOP_BOX = 0x0A
1133
1133
 
@@ -1351,6 +1351,12 @@ class AdvertisingData:
1351
1351
  THREE_D_INFORMATION_DATA = 0x3D
1352
1352
  MANUFACTURER_SPECIFIC_DATA = 0xFF
1353
1353
 
1354
+ class Flags(enum.IntFlag):
1355
+ LE_LIMITED_DISCOVERABLE_MODE = 1 << 0
1356
+ LE_GENERAL_DISCOVERABLE_MODE = 1 << 1
1357
+ BR_EDR_NOT_SUPPORTED = 1 << 2
1358
+ SIMULTANEOUS_LE_BR_EDR_CAPABLE = 1 << 3
1359
+
1354
1360
  # For backward-compatibility
1355
1361
  FLAGS = Type.FLAGS
1356
1362
  INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS = Type.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS
@@ -1407,11 +1413,11 @@ class AdvertisingData:
1407
1413
  THREE_D_INFORMATION_DATA = Type.THREE_D_INFORMATION_DATA
1408
1414
  MANUFACTURER_SPECIFIC_DATA = Type.MANUFACTURER_SPECIFIC_DATA
1409
1415
 
1410
- LE_LIMITED_DISCOVERABLE_MODE_FLAG = 0x01
1411
- LE_GENERAL_DISCOVERABLE_MODE_FLAG = 0x02
1412
- BR_EDR_NOT_SUPPORTED_FLAG = 0x04
1413
- BR_EDR_CONTROLLER_FLAG = 0x08
1414
- BR_EDR_HOST_FLAG = 0x10
1416
+ LE_LIMITED_DISCOVERABLE_MODE_FLAG = Flags.LE_LIMITED_DISCOVERABLE_MODE
1417
+ LE_GENERAL_DISCOVERABLE_MODE_FLAG = Flags.LE_GENERAL_DISCOVERABLE_MODE
1418
+ BR_EDR_NOT_SUPPORTED_FLAG = Flags.BR_EDR_NOT_SUPPORTED
1419
+ BR_EDR_CONTROLLER_FLAG = Flags.SIMULTANEOUS_LE_BR_EDR_CAPABLE
1420
+ BR_EDR_HOST_FLAG = 0x10 # Deprecated
1415
1421
 
1416
1422
  ad_structures: list[tuple[int, bytes]]
1417
1423