bumble 0.0.220__py3-none-any.whl → 0.0.221__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 (102) hide show
  1. bumble/_version.py +2 -2
  2. bumble/a2dp.py +5 -5
  3. bumble/apps/auracast.py +746 -473
  4. bumble/apps/bench.py +4 -5
  5. bumble/apps/console.py +5 -10
  6. bumble/apps/controller_info.py +12 -7
  7. bumble/apps/controller_loopback.py +1 -2
  8. bumble/apps/device_info.py +2 -3
  9. bumble/apps/gatt_dump.py +0 -1
  10. bumble/apps/lea_unicast/app.py +1 -1
  11. bumble/apps/pair.py +49 -46
  12. bumble/apps/pandora_server.py +2 -2
  13. bumble/apps/player/player.py +10 -12
  14. bumble/apps/rfcomm_bridge.py +10 -11
  15. bumble/apps/scan.py +1 -3
  16. bumble/apps/speaker/speaker.py +3 -4
  17. bumble/at.py +4 -5
  18. bumble/att.py +91 -25
  19. bumble/audio/io.py +5 -3
  20. bumble/avc.py +1 -2
  21. bumble/avctp.py +2 -3
  22. bumble/avdtp.py +53 -57
  23. bumble/avrcp.py +25 -27
  24. bumble/codecs.py +15 -15
  25. bumble/colors.py +7 -8
  26. bumble/controller.py +663 -391
  27. bumble/core.py +41 -49
  28. bumble/crypto/__init__.py +2 -1
  29. bumble/crypto/builtin.py +2 -8
  30. bumble/data_types.py +2 -1
  31. bumble/decoder.py +2 -3
  32. bumble/device.py +171 -142
  33. bumble/drivers/__init__.py +3 -2
  34. bumble/drivers/intel.py +6 -8
  35. bumble/drivers/rtk.py +1 -1
  36. bumble/gatt.py +9 -9
  37. bumble/gatt_adapters.py +6 -6
  38. bumble/gatt_client.py +110 -60
  39. bumble/gatt_server.py +209 -139
  40. bumble/hci.py +87 -74
  41. bumble/helpers.py +5 -5
  42. bumble/hfp.py +27 -26
  43. bumble/hid.py +9 -9
  44. bumble/host.py +44 -50
  45. bumble/keys.py +17 -17
  46. bumble/l2cap.py +1015 -218
  47. bumble/link.py +26 -159
  48. bumble/ll.py +200 -0
  49. bumble/pairing.py +14 -15
  50. bumble/pandora/__init__.py +2 -2
  51. bumble/pandora/device.py +6 -4
  52. bumble/pandora/host.py +19 -10
  53. bumble/pandora/l2cap.py +8 -9
  54. bumble/pandora/security.py +18 -16
  55. bumble/pandora/utils.py +4 -4
  56. bumble/profiles/aics.py +6 -8
  57. bumble/profiles/ams.py +3 -5
  58. bumble/profiles/ancs.py +11 -11
  59. bumble/profiles/ascs.py +5 -5
  60. bumble/profiles/asha.py +10 -9
  61. bumble/profiles/bass.py +9 -3
  62. bumble/profiles/battery_service.py +1 -2
  63. bumble/profiles/csip.py +9 -10
  64. bumble/profiles/device_information_service.py +16 -17
  65. bumble/profiles/gap.py +3 -4
  66. bumble/profiles/gatt_service.py +0 -1
  67. bumble/profiles/gmap.py +12 -13
  68. bumble/profiles/hap.py +3 -3
  69. bumble/profiles/heart_rate_service.py +7 -8
  70. bumble/profiles/le_audio.py +1 -1
  71. bumble/profiles/mcp.py +28 -28
  72. bumble/profiles/pacs.py +13 -17
  73. bumble/profiles/pbp.py +16 -0
  74. bumble/profiles/vcs.py +2 -2
  75. bumble/profiles/vocs.py +6 -9
  76. bumble/rfcomm.py +19 -18
  77. bumble/sdp.py +12 -11
  78. bumble/smp.py +20 -30
  79. bumble/snoop.py +2 -1
  80. bumble/tools/generate_company_id_list.py +1 -1
  81. bumble/tools/intel_util.py +2 -2
  82. bumble/tools/rtk_fw_download.py +1 -1
  83. bumble/tools/rtk_util.py +1 -1
  84. bumble/transport/__init__.py +1 -2
  85. bumble/transport/android_emulator.py +2 -3
  86. bumble/transport/android_netsim.py +49 -40
  87. bumble/transport/common.py +9 -9
  88. bumble/transport/file.py +1 -2
  89. bumble/transport/hci_socket.py +2 -3
  90. bumble/transport/pty.py +3 -5
  91. bumble/transport/pyusb.py +8 -5
  92. bumble/transport/serial.py +1 -2
  93. bumble/transport/vhci.py +1 -2
  94. bumble/transport/ws_server.py +2 -3
  95. bumble/utils.py +22 -9
  96. bumble/vendor/android/hci.py +4 -2
  97. {bumble-0.0.220.dist-info → bumble-0.0.221.dist-info}/METADATA +3 -2
  98. {bumble-0.0.220.dist-info → bumble-0.0.221.dist-info}/RECORD +102 -101
  99. {bumble-0.0.220.dist-info → bumble-0.0.221.dist-info}/WHEEL +0 -0
  100. {bumble-0.0.220.dist-info → bumble-0.0.221.dist-info}/entry_points.txt +0 -0
  101. {bumble-0.0.220.dist-info → bumble-0.0.221.dist-info}/licenses/LICENSE +0 -0
  102. {bumble-0.0.220.dist-info → bumble-0.0.221.dist-info}/top_level.txt +0 -0
bumble/avdtp.py CHANGED
@@ -22,16 +22,13 @@ import enum
22
22
  import logging
23
23
  import time
24
24
  import warnings
25
- from collections.abc import AsyncGenerator, Awaitable, Iterable
25
+ from collections.abc import AsyncGenerator, Awaitable, Callable, Iterable
26
26
  from dataclasses import dataclass, field
27
27
  from typing import (
28
28
  Any,
29
- Callable,
30
29
  ClassVar,
31
- Optional,
32
30
  SupportsBytes,
33
31
  TypeVar,
34
- Union,
35
32
  cast,
36
33
  )
37
34
 
@@ -184,7 +181,7 @@ class State(utils.OpenIntEnum):
184
181
  # -----------------------------------------------------------------------------
185
182
  async def find_avdtp_service_with_sdp_client(
186
183
  sdp_client: sdp.Client,
187
- ) -> Optional[tuple[int, int]]:
184
+ ) -> tuple[int, int] | None:
188
185
  '''
189
186
  Find an AVDTP service, using a connected SDP client, and return its version,
190
187
  or None if none is found
@@ -214,7 +211,7 @@ async def find_avdtp_service_with_sdp_client(
214
211
  # -----------------------------------------------------------------------------
215
212
  async def find_avdtp_service_with_connection(
216
213
  connection: device.Connection,
217
- ) -> Optional[tuple[int, int]]:
214
+ ) -> tuple[int, int] | None:
218
215
  '''
219
216
  Find an AVDTP service, for a connection, and return its version,
220
217
  or None if none is found
@@ -239,7 +236,7 @@ class RealtimeClock:
239
236
 
240
237
  # -----------------------------------------------------------------------------
241
238
  class MediaPacketPump:
242
- pump_task: Optional[asyncio.Task]
239
+ pump_task: asyncio.Task | None
243
240
 
244
241
  def __init__(
245
242
  self, packets: AsyncGenerator, clock: RealtimeClock = RealtimeClock()
@@ -296,7 +293,7 @@ class MediaPacketPump:
296
293
 
297
294
  # -----------------------------------------------------------------------------
298
295
  class MessageAssembler:
299
- message: Optional[bytes]
296
+ message: bytes | None
300
297
  signal_identifier: SignalIdentifier
301
298
 
302
299
  def __init__(self, callback: Callable[[int, Message], Any]) -> None:
@@ -470,15 +467,15 @@ class MediaCodecCapabilities(ServiceCapabilities):
470
467
 
471
468
  media_type: MediaType
472
469
  media_codec_type: a2dp.CodecType
473
- media_codec_information: Union[bytes, SupportsBytes]
470
+ media_codec_information: bytes | SupportsBytes
474
471
 
475
472
  # Override init to allow passing service_capabilities_bytes.
476
473
  def __init__(
477
474
  self,
478
475
  media_type: MediaType,
479
476
  media_codec_type: a2dp.CodecType,
480
- media_codec_information: Union[bytes, SupportsBytes],
481
- service_capabilities_bytes: Optional[bytes] = None,
477
+ media_codec_information: bytes | SupportsBytes,
478
+ service_capabilities_bytes: bytes | None = None,
482
479
  ) -> None:
483
480
  self.media_type = media_type
484
481
  self.media_codec_type = media_codec_type
@@ -553,7 +550,7 @@ class Message:
553
550
 
554
551
  message_type: MessageType
555
552
  signal_identifier: SignalIdentifier
556
- _payload: Optional[bytes] = None
553
+ _payload: bytes | None = None
557
554
  fields: ClassVar[hci.Fields] = ()
558
555
 
559
556
  @property
@@ -607,7 +604,7 @@ class Message:
607
604
  instance.signal_identifier = signal_identifier
608
605
  return instance
609
606
 
610
- def to_string(self, details: Union[str, Iterable[str]]) -> str:
607
+ def to_string(self, details: str | Iterable[str]) -> str:
611
608
  base = color(
612
609
  f'{self.signal_identifier.name}_{self.message_type.name}',
613
610
  'yellow',
@@ -1266,9 +1263,9 @@ class Protocol(utils.EventEmitter):
1266
1263
  local_endpoints: list[LocalStreamEndPoint]
1267
1264
  remote_endpoints: dict[int, DiscoveredStreamEndPoint]
1268
1265
  streams: dict[int, Stream]
1269
- transaction_results: list[Optional[asyncio.Future[Message]]]
1266
+ transaction_results: list[asyncio.Future[Message] | None]
1270
1267
  channel_connector: Callable[[], Awaitable[l2cap.ClassicChannel]]
1271
- channel_acceptor: Optional[Stream]
1268
+ channel_acceptor: Stream | None
1272
1269
 
1273
1270
  EVENT_OPEN = "open"
1274
1271
  EVENT_CLOSE = "close"
@@ -1311,7 +1308,7 @@ class Protocol(utils.EventEmitter):
1311
1308
  l2cap_channel.on(l2cap_channel.EVENT_OPEN, self.on_l2cap_channel_open)
1312
1309
  l2cap_channel.on(l2cap_channel.EVENT_CLOSE, self.on_l2cap_channel_close)
1313
1310
 
1314
- def get_local_endpoint_by_seid(self, seid: int) -> Optional[LocalStreamEndPoint]:
1311
+ def get_local_endpoint_by_seid(self, seid: int) -> LocalStreamEndPoint | None:
1315
1312
  if 0 < seid <= len(self.local_endpoints):
1316
1313
  return self.local_endpoints[seid - 1]
1317
1314
 
@@ -1385,7 +1382,7 @@ class Protocol(utils.EventEmitter):
1385
1382
 
1386
1383
  def find_remote_sink_by_codec(
1387
1384
  self, media_type: int, codec_type: int, vendor_id: int = 0, codec_id: int = 0
1388
- ) -> Optional[DiscoveredStreamEndPoint]:
1385
+ ) -> DiscoveredStreamEndPoint | None:
1389
1386
  for endpoint in self.remote_endpoints.values():
1390
1387
  if (
1391
1388
  not endpoint.in_use
@@ -1569,10 +1566,9 @@ class Protocol(utils.EventEmitter):
1569
1566
 
1570
1567
  assert False # Should never reach this
1571
1568
 
1572
- async def get_capabilities(self, seid: int) -> Union[
1573
- Get_Capabilities_Response,
1574
- Get_All_Capabilities_Response,
1575
- ]:
1569
+ async def get_capabilities(
1570
+ self, seid: int
1571
+ ) -> Get_Capabilities_Response | Get_All_Capabilities_Response:
1576
1572
  if self.version > (1, 2):
1577
1573
  return await self.send_command(Get_All_Capabilities_Command(seid))
1578
1574
 
@@ -1604,7 +1600,7 @@ class Protocol(utils.EventEmitter):
1604
1600
  async def abort(self, seid: int) -> Abort_Response:
1605
1601
  return await self.send_command(Abort_Command(seid))
1606
1602
 
1607
- def on_discover_command(self, command: Discover_Command) -> Optional[Message]:
1603
+ def on_discover_command(self, command: Discover_Command) -> Message | None:
1608
1604
  endpoint_infos = [
1609
1605
  EndPointInfo(endpoint.seid, 0, endpoint.media_type, endpoint.tsep)
1610
1606
  for endpoint in self.local_endpoints
@@ -1613,7 +1609,7 @@ class Protocol(utils.EventEmitter):
1613
1609
 
1614
1610
  def on_get_capabilities_command(
1615
1611
  self, command: Get_Capabilities_Command
1616
- ) -> Optional[Message]:
1612
+ ) -> Message | None:
1617
1613
  endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
1618
1614
  if endpoint is None:
1619
1615
  return Get_Capabilities_Reject(AVDTP_BAD_ACP_SEID_ERROR)
@@ -1622,7 +1618,7 @@ class Protocol(utils.EventEmitter):
1622
1618
 
1623
1619
  def on_get_all_capabilities_command(
1624
1620
  self, command: Get_All_Capabilities_Command
1625
- ) -> Optional[Message]:
1621
+ ) -> Message | None:
1626
1622
  endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
1627
1623
  if endpoint is None:
1628
1624
  return Get_All_Capabilities_Reject(AVDTP_BAD_ACP_SEID_ERROR)
@@ -1631,7 +1627,7 @@ class Protocol(utils.EventEmitter):
1631
1627
 
1632
1628
  def on_set_configuration_command(
1633
1629
  self, command: Set_Configuration_Command
1634
- ) -> Optional[Message]:
1630
+ ) -> Message | None:
1635
1631
  endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
1636
1632
  if endpoint is None:
1637
1633
  return Set_Configuration_Reject(error_code=AVDTP_BAD_ACP_SEID_ERROR)
@@ -1649,7 +1645,7 @@ class Protocol(utils.EventEmitter):
1649
1645
 
1650
1646
  def on_get_configuration_command(
1651
1647
  self, command: Get_Configuration_Command
1652
- ) -> Optional[Message]:
1648
+ ) -> Message | None:
1653
1649
  endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
1654
1650
  if endpoint is None:
1655
1651
  return Get_Configuration_Reject(AVDTP_BAD_ACP_SEID_ERROR)
@@ -1658,7 +1654,7 @@ class Protocol(utils.EventEmitter):
1658
1654
 
1659
1655
  return endpoint.stream.on_get_configuration_command()
1660
1656
 
1661
- def on_reconfigure_command(self, command: Reconfigure_Command) -> Optional[Message]:
1657
+ def on_reconfigure_command(self, command: Reconfigure_Command) -> Message | None:
1662
1658
  endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
1663
1659
  if endpoint is None:
1664
1660
  return Reconfigure_Reject(error_code=AVDTP_BAD_ACP_SEID_ERROR)
@@ -1668,7 +1664,7 @@ class Protocol(utils.EventEmitter):
1668
1664
  result = endpoint.stream.on_reconfigure_command(command.capabilities)
1669
1665
  return result or Reconfigure_Response()
1670
1666
 
1671
- def on_open_command(self, command: Open_Command) -> Optional[Message]:
1667
+ def on_open_command(self, command: Open_Command) -> Message | None:
1672
1668
  endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
1673
1669
  if endpoint is None:
1674
1670
  return Open_Reject(AVDTP_BAD_ACP_SEID_ERROR)
@@ -1678,7 +1674,7 @@ class Protocol(utils.EventEmitter):
1678
1674
  result = endpoint.stream.on_open_command()
1679
1675
  return result or Open_Response()
1680
1676
 
1681
- def on_start_command(self, command: Start_Command) -> Optional[Message]:
1677
+ def on_start_command(self, command: Start_Command) -> Message | None:
1682
1678
  for seid in command.acp_seids:
1683
1679
  endpoint = self.get_local_endpoint_by_seid(seid)
1684
1680
  if endpoint is None:
@@ -1697,7 +1693,7 @@ class Protocol(utils.EventEmitter):
1697
1693
 
1698
1694
  return Start_Response()
1699
1695
 
1700
- def on_suspend_command(self, command: Suspend_Command) -> Optional[Message]:
1696
+ def on_suspend_command(self, command: Suspend_Command) -> Message | None:
1701
1697
  for seid in command.acp_seids:
1702
1698
  endpoint = self.get_local_endpoint_by_seid(seid)
1703
1699
  if endpoint is None:
@@ -1716,7 +1712,7 @@ class Protocol(utils.EventEmitter):
1716
1712
 
1717
1713
  return Suspend_Response()
1718
1714
 
1719
- def on_close_command(self, command: Close_Command) -> Optional[Message]:
1715
+ def on_close_command(self, command: Close_Command) -> Message | None:
1720
1716
  endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
1721
1717
  if endpoint is None:
1722
1718
  return Close_Reject(AVDTP_BAD_ACP_SEID_ERROR)
@@ -1726,7 +1722,7 @@ class Protocol(utils.EventEmitter):
1726
1722
  result = endpoint.stream.on_close_command()
1727
1723
  return result or Close_Response()
1728
1724
 
1729
- def on_abort_command(self, command: Abort_Command) -> Optional[Message]:
1725
+ def on_abort_command(self, command: Abort_Command) -> Message | None:
1730
1726
  endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
1731
1727
  if endpoint is None or endpoint.stream is None:
1732
1728
  return Abort_Response()
@@ -1736,7 +1732,7 @@ class Protocol(utils.EventEmitter):
1736
1732
 
1737
1733
  def on_security_control_command(
1738
1734
  self, command: Security_Control_Command
1739
- ) -> Optional[Message]:
1735
+ ) -> Message | None:
1740
1736
  endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
1741
1737
  if endpoint is None:
1742
1738
  return Security_Control_Reject(AVDTP_BAD_ACP_SEID_ERROR)
@@ -1744,7 +1740,7 @@ class Protocol(utils.EventEmitter):
1744
1740
  result = endpoint.on_security_control_command(command.data)
1745
1741
  return result or Security_Control_Response()
1746
1742
 
1747
- def on_delayreport_command(self, command: DelayReport_Command) -> Optional[Message]:
1743
+ def on_delayreport_command(self, command: DelayReport_Command) -> Message | None:
1748
1744
  endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
1749
1745
  if endpoint is None:
1750
1746
  return DelayReport_Reject(AVDTP_BAD_ACP_SEID_ERROR)
@@ -1825,7 +1821,7 @@ class Stream:
1825
1821
  Pair of a local and a remote stream endpoint that can stream from one to the other
1826
1822
  '''
1827
1823
 
1828
- rtp_channel: Optional[l2cap.ClassicChannel]
1824
+ rtp_channel: l2cap.ClassicChannel | None
1829
1825
 
1830
1826
  def change_state(self, state: State) -> None:
1831
1827
  logger.debug(f'{self} state change -> {color(state.name, "cyan")}')
@@ -1914,7 +1910,7 @@ class Stream:
1914
1910
 
1915
1911
  def on_set_configuration_command(
1916
1912
  self, configuration: Iterable[ServiceCapabilities]
1917
- ) -> Optional[Message]:
1913
+ ) -> Message | None:
1918
1914
  if self.state != State.IDLE:
1919
1915
  return Set_Configuration_Reject(error_code=AVDTP_BAD_STATE_ERROR)
1920
1916
 
@@ -1925,7 +1921,7 @@ class Stream:
1925
1921
  self.change_state(State.CONFIGURED)
1926
1922
  return None
1927
1923
 
1928
- def on_get_configuration_command(self) -> Optional[Message]:
1924
+ def on_get_configuration_command(self) -> Message | None:
1929
1925
  if self.state not in (
1930
1926
  State.CONFIGURED,
1931
1927
  State.OPEN,
@@ -1937,7 +1933,7 @@ class Stream:
1937
1933
 
1938
1934
  def on_reconfigure_command(
1939
1935
  self, configuration: Iterable[ServiceCapabilities]
1940
- ) -> Optional[Message]:
1936
+ ) -> Message | None:
1941
1937
  if self.state != State.OPEN:
1942
1938
  return Reconfigure_Reject(error_code=AVDTP_BAD_STATE_ERROR)
1943
1939
 
@@ -1947,7 +1943,7 @@ class Stream:
1947
1943
 
1948
1944
  return None
1949
1945
 
1950
- def on_open_command(self) -> Optional[Message]:
1946
+ def on_open_command(self) -> Message | None:
1951
1947
  if self.state != State.CONFIGURED:
1952
1948
  return Open_Reject(AVDTP_BAD_STATE_ERROR)
1953
1949
 
@@ -1961,7 +1957,7 @@ class Stream:
1961
1957
  self.change_state(State.OPEN)
1962
1958
  return None
1963
1959
 
1964
- def on_start_command(self) -> Optional[Message]:
1960
+ def on_start_command(self) -> Message | None:
1965
1961
  if self.state != State.OPEN:
1966
1962
  return Open_Reject(AVDTP_BAD_STATE_ERROR)
1967
1963
 
@@ -1977,7 +1973,7 @@ class Stream:
1977
1973
  self.change_state(State.STREAMING)
1978
1974
  return None
1979
1975
 
1980
- def on_suspend_command(self) -> Optional[Message]:
1976
+ def on_suspend_command(self) -> Message | None:
1981
1977
  if self.state != State.STREAMING:
1982
1978
  return Open_Reject(AVDTP_BAD_STATE_ERROR)
1983
1979
 
@@ -1988,7 +1984,7 @@ class Stream:
1988
1984
  self.change_state(State.OPEN)
1989
1985
  return None
1990
1986
 
1991
- def on_close_command(self) -> Optional[Message]:
1987
+ def on_close_command(self) -> Message | None:
1992
1988
  if self.state not in (State.OPEN, State.STREAMING):
1993
1989
  return Open_Reject(AVDTP_BAD_STATE_ERROR)
1994
1990
 
@@ -2007,7 +2003,7 @@ class Stream:
2007
2003
 
2008
2004
  return None
2009
2005
 
2010
- def on_abort_command(self) -> Optional[Message]:
2006
+ def on_abort_command(self) -> Message | None:
2011
2007
  if self.rtp_channel is None:
2012
2008
  # No need to wait
2013
2009
  self.change_state(State.IDLE)
@@ -2120,7 +2116,7 @@ class DiscoveredStreamEndPoint(StreamEndPoint, StreamEndPointProxy):
2120
2116
 
2121
2117
  # -----------------------------------------------------------------------------
2122
2118
  class LocalStreamEndPoint(StreamEndPoint, utils.EventEmitter):
2123
- stream: Optional[Stream]
2119
+ stream: Stream | None
2124
2120
 
2125
2121
  EVENT_CONFIGURATION = "configuration"
2126
2122
  EVENT_OPEN = "open"
@@ -2142,7 +2138,7 @@ class LocalStreamEndPoint(StreamEndPoint, utils.EventEmitter):
2142
2138
  media_type: MediaType,
2143
2139
  tsep: StreamEndPointType,
2144
2140
  capabilities: Iterable[ServiceCapabilities],
2145
- configuration: Optional[Iterable[ServiceCapabilities]] = None,
2141
+ configuration: Iterable[ServiceCapabilities] | None = None,
2146
2142
  ):
2147
2143
  StreamEndPoint.__init__(self, seid, media_type, tsep, 0, capabilities)
2148
2144
  utils.EventEmitter.__init__(self)
@@ -2161,13 +2157,13 @@ class LocalStreamEndPoint(StreamEndPoint, utils.EventEmitter):
2161
2157
 
2162
2158
  def on_reconfigure_command(
2163
2159
  self, command: Iterable[ServiceCapabilities]
2164
- ) -> Optional[Message]:
2160
+ ) -> Message | None:
2165
2161
  del command # unused.
2166
2162
  return None
2167
2163
 
2168
2164
  def on_set_configuration_command(
2169
2165
  self, configuration: Iterable[ServiceCapabilities]
2170
- ) -> Optional[Message]:
2166
+ ) -> Message | None:
2171
2167
  logger.debug(
2172
2168
  '<<< received configuration: '
2173
2169
  f'{",".join([str(capability) for capability in configuration])}'
@@ -2176,34 +2172,34 @@ class LocalStreamEndPoint(StreamEndPoint, utils.EventEmitter):
2176
2172
  self.emit(self.EVENT_CONFIGURATION)
2177
2173
  return None
2178
2174
 
2179
- def on_get_configuration_command(self) -> Optional[Message]:
2175
+ def on_get_configuration_command(self) -> Message | None:
2180
2176
  return Get_Configuration_Response(self.configuration)
2181
2177
 
2182
- def on_open_command(self) -> Optional[Message]:
2178
+ def on_open_command(self) -> Message | None:
2183
2179
  self.emit(self.EVENT_OPEN)
2184
2180
  return None
2185
2181
 
2186
- def on_start_command(self) -> Optional[Message]:
2182
+ def on_start_command(self) -> Message | None:
2187
2183
  self.emit(self.EVENT_START)
2188
2184
  return None
2189
2185
 
2190
- def on_suspend_command(self) -> Optional[Message]:
2186
+ def on_suspend_command(self) -> Message | None:
2191
2187
  self.emit(self.EVENT_SUSPEND)
2192
2188
  return None
2193
2189
 
2194
- def on_close_command(self) -> Optional[Message]:
2190
+ def on_close_command(self) -> Message | None:
2195
2191
  self.emit(self.EVENT_CLOSE)
2196
2192
  return None
2197
2193
 
2198
- def on_abort_command(self) -> Optional[Message]:
2194
+ def on_abort_command(self) -> Message | None:
2199
2195
  self.emit(self.EVENT_ABORT)
2200
2196
  return None
2201
2197
 
2202
- def on_delayreport_command(self, delay: int) -> Optional[Message]:
2198
+ def on_delayreport_command(self, delay: int) -> Message | None:
2203
2199
  self.emit(self.EVENT_DELAY_REPORT, delay)
2204
2200
  return None
2205
2201
 
2206
- def on_security_control_command(self, data: bytes) -> Optional[Message]:
2202
+ def on_security_control_command(self, data: bytes) -> Message | None:
2207
2203
  self.emit(self.EVENT_SECURITY_CONTROL, data)
2208
2204
  return None
2209
2205
 
@@ -2255,12 +2251,12 @@ class LocalSource(LocalStreamEndPoint):
2255
2251
  self.emit(self.EVENT_STOP)
2256
2252
 
2257
2253
  @override
2258
- def on_start_command(self) -> Optional[Message]:
2254
+ def on_start_command(self) -> Message | None:
2259
2255
  asyncio.create_task(self.start())
2260
2256
  return None
2261
2257
 
2262
2258
  @override
2263
- def on_suspend_command(self) -> Optional[Message]:
2259
+ def on_suspend_command(self) -> Message | None:
2264
2260
  asyncio.create_task(self.stop())
2265
2261
  return None
2266
2262
 
bumble/avrcp.py CHANGED
@@ -24,7 +24,7 @@ import logging
24
24
  import struct
25
25
  from collections.abc import AsyncIterator, Awaitable, Callable, Iterable, Sequence
26
26
  from dataclasses import dataclass, field
27
- from typing import ClassVar, Optional, SupportsBytes, TypeVar, Union
27
+ from typing import ClassVar, SupportsBytes, TypeVar
28
28
 
29
29
  from bumble import avc, avctp, core, hci, l2cap, utils
30
30
  from bumble.colors import color
@@ -196,7 +196,7 @@ def make_controller_service_sdp_records(
196
196
  service_record_handle: int,
197
197
  avctp_version: tuple[int, int] = (1, 4),
198
198
  avrcp_version: tuple[int, int] = (1, 6),
199
- supported_features: Union[int, ControllerFeatures] = 1,
199
+ supported_features: int | ControllerFeatures = 1,
200
200
  ) -> list[ServiceAttribute]:
201
201
  avctp_version_int = avctp_version[0] << 8 | avctp_version[1]
202
202
  avrcp_version_int = avrcp_version[0] << 8 | avrcp_version[1]
@@ -288,7 +288,7 @@ def make_target_service_sdp_records(
288
288
  service_record_handle: int,
289
289
  avctp_version: tuple[int, int] = (1, 4),
290
290
  avrcp_version: tuple[int, int] = (1, 6),
291
- supported_features: Union[int, TargetFeatures] = 0x23,
291
+ supported_features: int | TargetFeatures = 0x23,
292
292
  ) -> list[ServiceAttribute]:
293
293
  # TODO: support a way to compute the supported features from a feature list
294
294
  avctp_version_int = avctp_version[0] << 8 | avctp_version[1]
@@ -478,7 +478,7 @@ class BrowseableItem:
478
478
  MEDIA_ELEMENT = 0x03
479
479
 
480
480
  item_type: ClassVar[Type]
481
- _payload: Optional[bytes] = None
481
+ _payload: bytes | None = None
482
482
 
483
483
  subclasses: ClassVar[dict[Type, type[BrowseableItem]]] = {}
484
484
  fields: ClassVar[hci.Fields] = ()
@@ -672,7 +672,7 @@ class PduAssembler:
672
672
  6.3.1 AVRCP specific AV//C commands
673
673
  """
674
674
 
675
- pdu_id: Optional[PduId]
675
+ pdu_id: PduId | None
676
676
  payload: bytes
677
677
 
678
678
  def __init__(self, callback: Callable[[PduId, bytes], None]) -> None:
@@ -725,7 +725,7 @@ class PduAssembler:
725
725
  # -----------------------------------------------------------------------------
726
726
  class Command:
727
727
  pdu_id: ClassVar[PduId]
728
- _payload: Optional[bytes] = None
728
+ _payload: bytes | None = None
729
729
 
730
730
  _Command = TypeVar('_Command', bound='Command')
731
731
  subclasses: ClassVar[dict[int, type[Command]]] = {}
@@ -1017,7 +1017,7 @@ class AddToNowPlayingCommand(Command):
1017
1017
  # -----------------------------------------------------------------------------
1018
1018
  class Response:
1019
1019
  pdu_id: PduId
1020
- _payload: Optional[bytes] = None
1020
+ _payload: bytes | None = None
1021
1021
 
1022
1022
  fields: ClassVar[hci.Fields] = ()
1023
1023
  subclasses: ClassVar[dict[PduId, type[Response]]] = {}
@@ -1079,7 +1079,7 @@ class NotImplementedResponse(Response):
1079
1079
  class GetCapabilitiesResponse(Response):
1080
1080
  pdu_id = PduId.GET_CAPABILITIES
1081
1081
  capability_id: GetCapabilitiesCommand.CapabilityId
1082
- capabilities: Sequence[Union[SupportsBytes, bytes]]
1082
+ capabilities: Sequence[SupportsBytes | bytes]
1083
1083
 
1084
1084
  @classmethod
1085
1085
  def from_parameters(cls, parameters: bytes) -> Response:
@@ -1092,7 +1092,7 @@ class GetCapabilitiesResponse(Response):
1092
1092
  capability_id = GetCapabilitiesCommand.CapabilityId(parameters[0])
1093
1093
  capability_count = parameters[1]
1094
1094
 
1095
- capabilities: list[Union[SupportsBytes, bytes]]
1095
+ capabilities: list[SupportsBytes | bytes]
1096
1096
  if capability_id == GetCapabilitiesCommand.CapabilityId.EVENTS_SUPPORTED:
1097
1097
  capabilities = [EventId(parameters[2 + x]) for x in range(capability_count)]
1098
1098
  else:
@@ -1363,7 +1363,7 @@ class AddToNowPlayingResponse(Response):
1363
1363
  # -----------------------------------------------------------------------------
1364
1364
  class Event:
1365
1365
  event_id: EventId
1366
- _pdu: Optional[bytes] = None
1366
+ _pdu: bytes | None = None
1367
1367
 
1368
1368
  _Event = TypeVar('_Event', bound='Event')
1369
1369
  subclasses: ClassVar[dict[int, type[Event]]] = {}
@@ -1436,13 +1436,13 @@ class PlayerApplicationSettingChangedEvent(Event):
1436
1436
  attribute_id: ApplicationSetting.AttributeId = field(
1437
1437
  metadata=ApplicationSetting.AttributeId.type_metadata(1)
1438
1438
  )
1439
- value_id: Union[
1440
- ApplicationSetting.EqualizerOnOffStatus,
1441
- ApplicationSetting.RepeatModeStatus,
1442
- ApplicationSetting.ShuffleOnOffStatus,
1443
- ApplicationSetting.ScanOnOffStatus,
1444
- ApplicationSetting.GenericValue,
1445
- ] = field(metadata=hci.metadata(1))
1439
+ value_id: (
1440
+ ApplicationSetting.EqualizerOnOffStatus
1441
+ | ApplicationSetting.RepeatModeStatus
1442
+ | ApplicationSetting.ShuffleOnOffStatus
1443
+ | ApplicationSetting.ScanOnOffStatus
1444
+ | ApplicationSetting.GenericValue
1445
+ ) = field(metadata=hci.metadata(1))
1446
1446
 
1447
1447
  def __post_init__(self) -> None:
1448
1448
  super().__post_init__()
@@ -1628,17 +1628,17 @@ class Protocol(utils.EventEmitter):
1628
1628
  delegate: Delegate
1629
1629
  send_transaction_label: int
1630
1630
  command_pdu_assembler: PduAssembler
1631
- receive_command_state: Optional[ReceiveCommandState]
1631
+ receive_command_state: ReceiveCommandState | None
1632
1632
  response_pdu_assembler: PduAssembler
1633
- receive_response_state: Optional[ReceiveResponseState]
1634
- avctp_protocol: Optional[avctp.Protocol]
1633
+ receive_response_state: ReceiveResponseState | None
1634
+ avctp_protocol: avctp.Protocol | None
1635
1635
  free_commands: asyncio.Queue
1636
1636
  pending_commands: dict[int, PendingCommand] # Pending commands, by label
1637
1637
  notification_listeners: dict[EventId, NotificationListener]
1638
1638
 
1639
1639
  @staticmethod
1640
1640
  def _check_vendor_dependent_frame(
1641
- frame: Union[avc.VendorDependentCommandFrame, avc.VendorDependentResponseFrame],
1641
+ frame: avc.VendorDependentCommandFrame | avc.VendorDependentResponseFrame,
1642
1642
  ) -> bool:
1643
1643
  if frame.company_id != AVRCP_BLUETOOTH_SIG_COMPANY_ID:
1644
1644
  logger.debug("unsupported company id, ignoring")
@@ -1650,7 +1650,7 @@ class Protocol(utils.EventEmitter):
1650
1650
 
1651
1651
  return True
1652
1652
 
1653
- def __init__(self, delegate: Optional[Delegate] = None) -> None:
1653
+ def __init__(self, delegate: Delegate | None = None) -> None:
1654
1654
  super().__init__()
1655
1655
  self.delegate = delegate if delegate else Delegate()
1656
1656
  self.command_pdu_assembler = PduAssembler(self._on_command_pdu)
@@ -2011,7 +2011,7 @@ class Protocol(utils.EventEmitter):
2011
2011
  f"{command} is not a valid AV/C Command Frame"
2012
2012
  )
2013
2013
  logger.debug(
2014
- f"<<< AVCTP Command, transaction_label={transaction_label}: " f"{command}"
2014
+ f"<<< AVCTP Command, transaction_label={transaction_label}: {command}"
2015
2015
  )
2016
2016
 
2017
2017
  # Only addressing the unit, or the PANEL subunit with subunit ID 0 is supported
@@ -2067,9 +2067,7 @@ class Protocol(utils.EventEmitter):
2067
2067
  # TODO handle other types
2068
2068
  self.send_not_implemented_response(transaction_label, command)
2069
2069
 
2070
- def _on_avctp_response(
2071
- self, transaction_label: int, payload: Optional[bytes]
2072
- ) -> None:
2070
+ def _on_avctp_response(self, transaction_label: int, payload: bytes | None) -> None:
2073
2071
  response = avc.ResponseFrame.from_bytes(payload) if payload else None
2074
2072
  if not isinstance(response, avc.ResponseFrame):
2075
2073
  raise core.InvalidPacketError(
@@ -2176,7 +2174,7 @@ class Protocol(utils.EventEmitter):
2176
2174
  # NOTE: with a small number of supported responses, a manual switch like this
2177
2175
  # is Ok, but if/when more responses are supported, a lookup mechanism would be
2178
2176
  # more appropriate.
2179
- response: Optional[Response] = None
2177
+ response: Response | None = None
2180
2178
  if response_code == avc.ResponseFrame.ResponseCode.REJECTED:
2181
2179
  response = RejectedResponse(pdu_id=pdu_id, status_code=StatusCode(pdu[0]))
2182
2180
  elif response_code == avc.ResponseFrame.ResponseCode.NOT_IMPLEMENTED:
bumble/codecs.py CHANGED
@@ -163,23 +163,23 @@ class AacAudioRtpPacket:
163
163
  cls, reader: BitReader, channel_configuration: int, audio_object_type: int
164
164
  ) -> Self:
165
165
  # GASpecificConfig - ISO/EIC 14496-3 Table 4.1
166
- frame_length_flag = reader.read(1)
166
+ reader.read(1) # frame_length_flag
167
167
  depends_on_core_coder = reader.read(1)
168
168
  if depends_on_core_coder:
169
- core_coder_delay = reader.read(14)
169
+ reader.read(14) # core_coder_delay
170
170
  extension_flag = reader.read(1)
171
171
  if not channel_configuration:
172
172
  raise core.InvalidPacketError('program_config_element not supported')
173
173
  if audio_object_type in (6, 20):
174
- layer_nr = reader.read(3)
174
+ reader.read(3) # layer_nr
175
175
  if extension_flag:
176
176
  if audio_object_type == 22:
177
- num_of_sub_frame = reader.read(5)
178
- layer_length = reader.read(11)
177
+ reader.read(5) # num_of_sub_frame
178
+ reader.read(11) # layer_length
179
179
  if audio_object_type in (17, 19, 20, 23):
180
- aac_section_data_resilience_flags = reader.read(1)
181
- aac_scale_factor_data_resilience_flags = reader.read(1)
182
- aac_spectral_data_resilience_flags = reader.read(1)
180
+ reader.read(1) # aac_section_data_resilience_flags
181
+ reader.read(1) # aac_scale_factor_data_resilience_flags
182
+ reader.read(1) # aac_spectral_data_resilience_flags
183
183
  extension_flag_3 = reader.read(1)
184
184
  if extension_flag_3 == 1:
185
185
  raise core.InvalidPacketError('extensionFlag3 == 1 not supported')
@@ -364,10 +364,10 @@ class AacAudioRtpPacket:
364
364
  if audio_mux_version_a != 0:
365
365
  raise core.InvalidPacketError('audioMuxVersionA != 0 not supported')
366
366
  if audio_mux_version == 1:
367
- tara_buffer_fullness = AacAudioRtpPacket.read_latm_value(reader)
368
- stream_cnt = 0
369
- all_streams_same_time_framing = reader.read(1)
370
- num_sub_frames = reader.read(6)
367
+ AacAudioRtpPacket.read_latm_value(reader) # tara_buffer_fullness
368
+ # stream_cnt = 0
369
+ reader.read(1) # all_streams_same_time_framing
370
+ reader.read(6) # num_sub_frames
371
371
  num_program = reader.read(4)
372
372
  if num_program != 0:
373
373
  raise core.InvalidPacketError('num_program != 0 not supported')
@@ -391,9 +391,9 @@ class AacAudioRtpPacket:
391
391
  reader.skip(asc_len)
392
392
  frame_length_type = reader.read(3)
393
393
  if frame_length_type == 0:
394
- latm_buffer_fullness = reader.read(8)
394
+ reader.read(8) # latm_buffer_fullness
395
395
  elif frame_length_type == 1:
396
- frame_length = reader.read(9)
396
+ reader.read(9) # frame_length
397
397
  else:
398
398
  raise core.InvalidPacketError(
399
399
  f'frame_length_type {frame_length_type} not supported'
@@ -413,7 +413,7 @@ class AacAudioRtpPacket:
413
413
  break
414
414
  crc_check_present = reader.read(1)
415
415
  if crc_check_present:
416
- crc_checksum = reader.read(8)
416
+ reader.read(8) # crc_checksum
417
417
 
418
418
  return cls(other_data_present, other_data_len_bits, audio_specific_config)
419
419