bumble 0.0.210__py3-none-any.whl → 0.0.212__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 (44) hide show
  1. bumble/_version.py +2 -2
  2. bumble/apps/bench.py +8 -4
  3. bumble/apps/console.py +2 -2
  4. bumble/apps/pair.py +185 -32
  5. bumble/att.py +13 -12
  6. bumble/avctp.py +2 -2
  7. bumble/avdtp.py +122 -68
  8. bumble/avrcp.py +11 -5
  9. bumble/core.py +13 -7
  10. bumble/{crypto.py → crypto/__init__.py} +11 -95
  11. bumble/crypto/builtin.py +652 -0
  12. bumble/crypto/cryptography.py +84 -0
  13. bumble/device.py +365 -185
  14. bumble/drivers/intel.py +3 -0
  15. bumble/gatt.py +3 -5
  16. bumble/gatt_client.py +5 -3
  17. bumble/gatt_server.py +8 -6
  18. bumble/hci.py +81 -4
  19. bumble/hfp.py +44 -20
  20. bumble/hid.py +24 -12
  21. bumble/host.py +24 -0
  22. bumble/keys.py +64 -48
  23. bumble/l2cap.py +19 -9
  24. bumble/pandora/host.py +11 -11
  25. bumble/pandora/l2cap.py +2 -2
  26. bumble/pandora/security.py +72 -56
  27. bumble/profiles/aics.py +3 -5
  28. bumble/profiles/ancs.py +3 -1
  29. bumble/profiles/ascs.py +11 -5
  30. bumble/profiles/asha.py +11 -6
  31. bumble/profiles/csip.py +1 -3
  32. bumble/profiles/gatt_service.py +1 -3
  33. bumble/profiles/hap.py +16 -33
  34. bumble/profiles/mcp.py +12 -9
  35. bumble/profiles/vcs.py +5 -5
  36. bumble/profiles/vocs.py +6 -9
  37. bumble/rfcomm.py +17 -8
  38. bumble/smp.py +14 -8
  39. {bumble-0.0.210.dist-info → bumble-0.0.212.dist-info}/METADATA +4 -4
  40. {bumble-0.0.210.dist-info → bumble-0.0.212.dist-info}/RECORD +44 -42
  41. {bumble-0.0.210.dist-info → bumble-0.0.212.dist-info}/WHEEL +1 -1
  42. {bumble-0.0.210.dist-info → bumble-0.0.212.dist-info}/entry_points.txt +0 -0
  43. {bumble-0.0.210.dist-info → bumble-0.0.212.dist-info}/licenses/LICENSE +0 -0
  44. {bumble-0.0.210.dist-info → bumble-0.0.212.dist-info}/top_level.txt +0 -0
bumble/avdtp.py CHANGED
@@ -896,7 +896,7 @@ class Set_Configuration_Reject(Message):
896
896
  self.service_category = self.payload[0]
897
897
  self.error_code = self.payload[1]
898
898
 
899
- def __init__(self, service_category, error_code):
899
+ def __init__(self, error_code: int, service_category: int = 0) -> None:
900
900
  super().__init__(payload=bytes([service_category, error_code]))
901
901
  self.service_category = service_category
902
902
  self.error_code = error_code
@@ -1132,6 +1132,14 @@ class Security_Control_Command(Message):
1132
1132
  See Bluetooth AVDTP spec - 8.17.1 Security Control Command
1133
1133
  '''
1134
1134
 
1135
+ def init_from_payload(self):
1136
+ # pylint: disable=attribute-defined-outside-init
1137
+ self.acp_seid = self.payload[0] >> 2
1138
+ self.data = self.payload[1:]
1139
+
1140
+ def __str__(self) -> str:
1141
+ return self.to_string([f'ACP_SEID: {self.acp_seid}', f'data: {self.data}'])
1142
+
1135
1143
 
1136
1144
  # -----------------------------------------------------------------------------
1137
1145
  @Message.subclass
@@ -1200,6 +1208,9 @@ class Protocol(utils.EventEmitter):
1200
1208
  transaction_results: List[Optional[asyncio.Future[Message]]]
1201
1209
  channel_connector: Callable[[], Awaitable[l2cap.ClassicChannel]]
1202
1210
 
1211
+ EVENT_OPEN = "open"
1212
+ EVENT_CLOSE = "close"
1213
+
1203
1214
  class PacketType(enum.IntEnum):
1204
1215
  SINGLE_PACKET = 0
1205
1216
  START_PACKET = 1
@@ -1239,8 +1250,8 @@ class Protocol(utils.EventEmitter):
1239
1250
 
1240
1251
  # Register to receive PDUs from the channel
1241
1252
  l2cap_channel.sink = self.on_pdu
1242
- l2cap_channel.on('open', self.on_l2cap_channel_open)
1243
- l2cap_channel.on('close', self.on_l2cap_channel_close)
1253
+ l2cap_channel.on(l2cap_channel.EVENT_OPEN, self.on_l2cap_channel_open)
1254
+ l2cap_channel.on(l2cap_channel.EVENT_CLOSE, self.on_l2cap_channel_close)
1244
1255
 
1245
1256
  def get_local_endpoint_by_seid(self, seid: int) -> Optional[LocalStreamEndPoint]:
1246
1257
  if 0 < seid <= len(self.local_endpoints):
@@ -1410,20 +1421,20 @@ class Protocol(utils.EventEmitter):
1410
1421
  self.transaction_results[transaction_label] = None
1411
1422
  self.transaction_semaphore.release()
1412
1423
 
1413
- def on_l2cap_connection(self, channel):
1424
+ def on_l2cap_connection(self, channel: l2cap.ClassicChannel) -> None:
1414
1425
  # Forward the channel to the endpoint that's expecting it
1415
1426
  if self.channel_acceptor is None:
1416
1427
  logger.warning(color('!!! l2cap connection with no acceptor', 'red'))
1417
1428
  return
1418
1429
  self.channel_acceptor.on_l2cap_connection(channel)
1419
1430
 
1420
- def on_l2cap_channel_open(self):
1431
+ def on_l2cap_channel_open(self) -> None:
1421
1432
  logger.debug(color('<<< L2CAP channel open', 'magenta'))
1422
- self.emit('open')
1433
+ self.emit(self.EVENT_OPEN)
1423
1434
 
1424
- def on_l2cap_channel_close(self):
1435
+ def on_l2cap_channel_close(self) -> None:
1425
1436
  logger.debug(color('<<< L2CAP channel close', 'magenta'))
1426
- self.emit('close')
1437
+ self.emit(self.EVENT_CLOSE)
1427
1438
 
1428
1439
  def send_message(self, transaction_label: int, message: Message) -> None:
1429
1440
  logger.debug(
@@ -1541,28 +1552,34 @@ class Protocol(utils.EventEmitter):
1541
1552
  async def abort(self, seid: int) -> Abort_Response:
1542
1553
  return await self.send_command(Abort_Command(seid))
1543
1554
 
1544
- def on_discover_command(self, _command):
1555
+ def on_discover_command(self, command: Discover_Command) -> Optional[Message]:
1545
1556
  endpoint_infos = [
1546
1557
  EndPointInfo(endpoint.seid, 0, endpoint.media_type, endpoint.tsep)
1547
1558
  for endpoint in self.local_endpoints
1548
1559
  ]
1549
1560
  return Discover_Response(endpoint_infos)
1550
1561
 
1551
- def on_get_capabilities_command(self, command):
1562
+ def on_get_capabilities_command(
1563
+ self, command: Get_Capabilities_Command
1564
+ ) -> Optional[Message]:
1552
1565
  endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
1553
1566
  if endpoint is None:
1554
1567
  return Get_Capabilities_Reject(AVDTP_BAD_ACP_SEID_ERROR)
1555
1568
 
1556
1569
  return Get_Capabilities_Response(endpoint.capabilities)
1557
1570
 
1558
- def on_get_all_capabilities_command(self, command):
1571
+ def on_get_all_capabilities_command(
1572
+ self, command: Get_All_Capabilities_Command
1573
+ ) -> Optional[Message]:
1559
1574
  endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
1560
1575
  if endpoint is None:
1561
1576
  return Get_All_Capabilities_Reject(AVDTP_BAD_ACP_SEID_ERROR)
1562
1577
 
1563
1578
  return Get_All_Capabilities_Response(endpoint.capabilities)
1564
1579
 
1565
- def on_set_configuration_command(self, command):
1580
+ def on_set_configuration_command(
1581
+ self, command: Set_Configuration_Command
1582
+ ) -> Optional[Message]:
1566
1583
  endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
1567
1584
  if endpoint is None:
1568
1585
  return Set_Configuration_Reject(AVDTP_BAD_ACP_SEID_ERROR)
@@ -1578,7 +1595,9 @@ class Protocol(utils.EventEmitter):
1578
1595
  result = stream.on_set_configuration_command(command.capabilities)
1579
1596
  return result or Set_Configuration_Response()
1580
1597
 
1581
- def on_get_configuration_command(self, command):
1598
+ def on_get_configuration_command(
1599
+ self, command: Get_Configuration_Command
1600
+ ) -> Optional[Message]:
1582
1601
  endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
1583
1602
  if endpoint is None:
1584
1603
  return Get_Configuration_Reject(AVDTP_BAD_ACP_SEID_ERROR)
@@ -1587,7 +1606,7 @@ class Protocol(utils.EventEmitter):
1587
1606
 
1588
1607
  return endpoint.stream.on_get_configuration_command()
1589
1608
 
1590
- def on_reconfigure_command(self, command):
1609
+ def on_reconfigure_command(self, command: Reconfigure_Command) -> Optional[Message]:
1591
1610
  endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
1592
1611
  if endpoint is None:
1593
1612
  return Reconfigure_Reject(0, AVDTP_BAD_ACP_SEID_ERROR)
@@ -1597,7 +1616,7 @@ class Protocol(utils.EventEmitter):
1597
1616
  result = endpoint.stream.on_reconfigure_command(command.capabilities)
1598
1617
  return result or Reconfigure_Response()
1599
1618
 
1600
- def on_open_command(self, command):
1619
+ def on_open_command(self, command: Open_Command) -> Optional[Message]:
1601
1620
  endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
1602
1621
  if endpoint is None:
1603
1622
  return Open_Reject(AVDTP_BAD_ACP_SEID_ERROR)
@@ -1607,25 +1626,26 @@ class Protocol(utils.EventEmitter):
1607
1626
  result = endpoint.stream.on_open_command()
1608
1627
  return result or Open_Response()
1609
1628
 
1610
- def on_start_command(self, command):
1629
+ def on_start_command(self, command: Start_Command) -> Optional[Message]:
1611
1630
  for seid in command.acp_seids:
1612
1631
  endpoint = self.get_local_endpoint_by_seid(seid)
1613
1632
  if endpoint is None:
1614
1633
  return Start_Reject(seid, AVDTP_BAD_ACP_SEID_ERROR)
1615
1634
  if endpoint.stream is None:
1616
- return Start_Reject(AVDTP_BAD_STATE_ERROR)
1635
+ return Start_Reject(seid, AVDTP_BAD_STATE_ERROR)
1617
1636
 
1618
1637
  # Start all streams
1619
1638
  # TODO: deal with partial failures
1620
1639
  for seid in command.acp_seids:
1621
1640
  endpoint = self.get_local_endpoint_by_seid(seid)
1622
- result = endpoint.stream.on_start_command()
1623
- if result is not None:
1641
+ if not endpoint or not endpoint.stream:
1642
+ raise InvalidStateError("Should already be checked!")
1643
+ if (result := endpoint.stream.on_start_command()) is not None:
1624
1644
  return result
1625
1645
 
1626
1646
  return Start_Response()
1627
1647
 
1628
- def on_suspend_command(self, command):
1648
+ def on_suspend_command(self, command: Suspend_Command) -> Optional[Message]:
1629
1649
  for seid in command.acp_seids:
1630
1650
  endpoint = self.get_local_endpoint_by_seid(seid)
1631
1651
  if endpoint is None:
@@ -1637,13 +1657,14 @@ class Protocol(utils.EventEmitter):
1637
1657
  # TODO: deal with partial failures
1638
1658
  for seid in command.acp_seids:
1639
1659
  endpoint = self.get_local_endpoint_by_seid(seid)
1640
- result = endpoint.stream.on_suspend_command()
1641
- if result is not None:
1660
+ if not endpoint or not endpoint.stream:
1661
+ raise InvalidStateError("Should already be checked!")
1662
+ if (result := endpoint.stream.on_suspend_command()) is not None:
1642
1663
  return result
1643
1664
 
1644
1665
  return Suspend_Response()
1645
1666
 
1646
- def on_close_command(self, command):
1667
+ def on_close_command(self, command: Close_Command) -> Optional[Message]:
1647
1668
  endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
1648
1669
  if endpoint is None:
1649
1670
  return Close_Reject(AVDTP_BAD_ACP_SEID_ERROR)
@@ -1653,7 +1674,7 @@ class Protocol(utils.EventEmitter):
1653
1674
  result = endpoint.stream.on_close_command()
1654
1675
  return result or Close_Response()
1655
1676
 
1656
- def on_abort_command(self, command):
1677
+ def on_abort_command(self, command: Abort_Command) -> Optional[Message]:
1657
1678
  endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
1658
1679
  if endpoint is None or endpoint.stream is None:
1659
1680
  return Abort_Response()
@@ -1661,15 +1682,17 @@ class Protocol(utils.EventEmitter):
1661
1682
  endpoint.stream.on_abort_command()
1662
1683
  return Abort_Response()
1663
1684
 
1664
- def on_security_control_command(self, command):
1685
+ def on_security_control_command(
1686
+ self, command: Security_Control_Command
1687
+ ) -> Optional[Message]:
1665
1688
  endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
1666
1689
  if endpoint is None:
1667
1690
  return Security_Control_Reject(AVDTP_BAD_ACP_SEID_ERROR)
1668
1691
 
1669
- result = endpoint.on_security_control_command(command.payload)
1692
+ result = endpoint.on_security_control_command(command.data)
1670
1693
  return result or Security_Control_Response()
1671
1694
 
1672
- def on_delayreport_command(self, command):
1695
+ def on_delayreport_command(self, command: DelayReport_Command) -> Optional[Message]:
1673
1696
  endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
1674
1697
  if endpoint is None:
1675
1698
  return DelayReport_Reject(AVDTP_BAD_ACP_SEID_ERROR)
@@ -1682,6 +1705,8 @@ class Protocol(utils.EventEmitter):
1682
1705
  class Listener(utils.EventEmitter):
1683
1706
  servers: Dict[int, Protocol]
1684
1707
 
1708
+ EVENT_CONNECTION = "connection"
1709
+
1685
1710
  @staticmethod
1686
1711
  def create_registrar(device: device.Device):
1687
1712
  warnings.warn("Please use Listener.for_device()", DeprecationWarning)
@@ -1716,7 +1741,7 @@ class Listener(utils.EventEmitter):
1716
1741
  l2cap_server = device.create_l2cap_server(
1717
1742
  spec=l2cap.ClassicChannelSpec(psm=AVDTP_PSM)
1718
1743
  )
1719
- l2cap_server.on('connection', listener.on_l2cap_connection)
1744
+ l2cap_server.on(l2cap_server.EVENT_CONNECTION, listener.on_l2cap_connection)
1720
1745
  return listener
1721
1746
 
1722
1747
  def on_l2cap_connection(self, channel: l2cap.ClassicChannel) -> None:
@@ -1732,14 +1757,14 @@ class Listener(utils.EventEmitter):
1732
1757
  logger.debug('setting up new Protocol for the connection')
1733
1758
  server = Protocol(channel, self.version)
1734
1759
  self.set_server(channel.connection, server)
1735
- self.emit('connection', server)
1760
+ self.emit(self.EVENT_CONNECTION, server)
1736
1761
 
1737
1762
  def on_channel_close():
1738
1763
  logger.debug('removing Protocol for the connection')
1739
1764
  self.remove_server(channel.connection)
1740
1765
 
1741
- channel.on('open', on_channel_open)
1742
- channel.on('close', on_channel_close)
1766
+ channel.on(channel.EVENT_OPEN, on_channel_open)
1767
+ channel.on(channel.EVENT_CLOSE, on_channel_close)
1743
1768
 
1744
1769
 
1745
1770
  # -----------------------------------------------------------------------------
@@ -1788,6 +1813,7 @@ class Stream:
1788
1813
  )
1789
1814
 
1790
1815
  async def start(self) -> None:
1816
+ """[Source] Start streaming."""
1791
1817
  # Auto-open if needed
1792
1818
  if self.state == AVDTP_CONFIGURED_STATE:
1793
1819
  await self.open()
@@ -1804,6 +1830,7 @@ class Stream:
1804
1830
  self.change_state(AVDTP_STREAMING_STATE)
1805
1831
 
1806
1832
  async def stop(self) -> None:
1833
+ """[Source] Stop streaming and transit to OPEN state."""
1807
1834
  if self.state != AVDTP_STREAMING_STATE:
1808
1835
  raise InvalidStateError('current state is not STREAMING')
1809
1836
 
@@ -1816,6 +1843,7 @@ class Stream:
1816
1843
  self.change_state(AVDTP_OPEN_STATE)
1817
1844
 
1818
1845
  async def close(self) -> None:
1846
+ """[Source] Close channel and transit to IDLE state."""
1819
1847
  if self.state not in (AVDTP_OPEN_STATE, AVDTP_STREAMING_STATE):
1820
1848
  raise InvalidStateError('current state is not OPEN or STREAMING')
1821
1849
 
@@ -1847,7 +1875,7 @@ class Stream:
1847
1875
  self.change_state(AVDTP_CONFIGURED_STATE)
1848
1876
  return None
1849
1877
 
1850
- def on_get_configuration_command(self, configuration):
1878
+ def on_get_configuration_command(self):
1851
1879
  if self.state not in (
1852
1880
  AVDTP_CONFIGURED_STATE,
1853
1881
  AVDTP_OPEN_STATE,
@@ -1855,7 +1883,7 @@ class Stream:
1855
1883
  ):
1856
1884
  return Get_Configuration_Reject(AVDTP_BAD_STATE_ERROR)
1857
1885
 
1858
- return self.local_endpoint.on_get_configuration_command(configuration)
1886
+ return self.local_endpoint.on_get_configuration_command()
1859
1887
 
1860
1888
  def on_reconfigure_command(self, configuration):
1861
1889
  if self.state != AVDTP_OPEN_STATE:
@@ -1935,20 +1963,20 @@ class Stream:
1935
1963
  # Wait for the RTP channel to be closed
1936
1964
  self.change_state(AVDTP_ABORTING_STATE)
1937
1965
 
1938
- def on_l2cap_connection(self, channel):
1966
+ def on_l2cap_connection(self, channel: l2cap.ClassicChannel) -> None:
1939
1967
  logger.debug(color('<<< stream channel connected', 'magenta'))
1940
1968
  self.rtp_channel = channel
1941
- channel.on('open', self.on_l2cap_channel_open)
1942
- channel.on('close', self.on_l2cap_channel_close)
1969
+ channel.on(channel.EVENT_OPEN, self.on_l2cap_channel_open)
1970
+ channel.on(channel.EVENT_CLOSE, self.on_l2cap_channel_close)
1943
1971
 
1944
1972
  # We don't need more channels
1945
1973
  self.protocol.channel_acceptor = None
1946
1974
 
1947
- def on_l2cap_channel_open(self):
1975
+ def on_l2cap_channel_open(self) -> None:
1948
1976
  logger.debug(color('<<< stream channel open', 'magenta'))
1949
1977
  self.local_endpoint.on_rtp_channel_open()
1950
1978
 
1951
- def on_l2cap_channel_close(self):
1979
+ def on_l2cap_channel_close(self) -> None:
1952
1980
  logger.debug(color('<<< stream channel closed', 'magenta'))
1953
1981
  self.local_endpoint.on_rtp_channel_close()
1954
1982
  self.local_endpoint.in_use = 0
@@ -2065,6 +2093,19 @@ class DiscoveredStreamEndPoint(StreamEndPoint, StreamEndPointProxy):
2065
2093
  class LocalStreamEndPoint(StreamEndPoint, utils.EventEmitter):
2066
2094
  stream: Optional[Stream]
2067
2095
 
2096
+ EVENT_CONFIGURATION = "configuration"
2097
+ EVENT_OPEN = "open"
2098
+ EVENT_START = "start"
2099
+ EVENT_STOP = "stop"
2100
+ EVENT_RTP_PACKET = "rtp_packet"
2101
+ EVENT_SUSPEND = "suspend"
2102
+ EVENT_CLOSE = "close"
2103
+ EVENT_ABORT = "abort"
2104
+ EVENT_DELAY_REPORT = "delay_report"
2105
+ EVENT_SECURITY_CONTROL = "security_control"
2106
+ EVENT_RTP_CHANNEL_OPEN = "rtp_channel_open"
2107
+ EVENT_RTP_CHANNEL_CLOSE = "rtp_channel_close"
2108
+
2068
2109
  def __init__(
2069
2110
  self,
2070
2111
  protocol: Protocol,
@@ -2080,52 +2121,65 @@ class LocalStreamEndPoint(StreamEndPoint, utils.EventEmitter):
2080
2121
  self.configuration = configuration if configuration is not None else []
2081
2122
  self.stream = None
2082
2123
 
2083
- async def start(self):
2084
- pass
2124
+ async def start(self) -> None:
2125
+ """[Source Only] Handles when receiving start command."""
2085
2126
 
2086
- async def stop(self):
2087
- pass
2127
+ async def stop(self) -> None:
2128
+ """[Source Only] Handles when receiving stop command."""
2088
2129
 
2089
- async def close(self):
2090
- pass
2130
+ async def close(self) -> None:
2131
+ """[Source Only] Handles when receiving close command."""
2091
2132
 
2092
- def on_reconfigure_command(self, command):
2093
- pass
2133
+ def on_reconfigure_command(self, command) -> Optional[Message]:
2134
+ return None
2094
2135
 
2095
- def on_set_configuration_command(self, configuration):
2136
+ def on_set_configuration_command(self, configuration) -> Optional[Message]:
2096
2137
  logger.debug(
2097
2138
  '<<< received configuration: '
2098
2139
  f'{",".join([str(capability) for capability in configuration])}'
2099
2140
  )
2100
2141
  self.configuration = configuration
2101
- self.emit('configuration')
2142
+ self.emit(self.EVENT_CONFIGURATION)
2143
+ return None
2102
2144
 
2103
- def on_get_configuration_command(self):
2145
+ def on_get_configuration_command(self) -> Optional[Message]:
2104
2146
  return Get_Configuration_Response(self.configuration)
2105
2147
 
2106
- def on_open_command(self):
2107
- self.emit('open')
2148
+ def on_open_command(self) -> Optional[Message]:
2149
+ self.emit(self.EVENT_OPEN)
2150
+ return None
2108
2151
 
2109
- def on_start_command(self):
2110
- self.emit('start')
2152
+ def on_start_command(self) -> Optional[Message]:
2153
+ self.emit(self.EVENT_START)
2154
+ return None
2111
2155
 
2112
- def on_suspend_command(self):
2113
- self.emit('suspend')
2156
+ def on_suspend_command(self) -> Optional[Message]:
2157
+ self.emit(self.EVENT_SUSPEND)
2158
+ return None
2114
2159
 
2115
- def on_close_command(self):
2116
- self.emit('close')
2160
+ def on_close_command(self) -> Optional[Message]:
2161
+ self.emit(self.EVENT_CLOSE)
2162
+ return None
2117
2163
 
2118
- def on_abort_command(self):
2119
- self.emit('abort')
2164
+ def on_abort_command(self) -> Optional[Message]:
2165
+ self.emit(self.EVENT_ABORT)
2166
+ return None
2120
2167
 
2121
- def on_delayreport_command(self, delay: int):
2122
- self.emit('delay_report', delay)
2168
+ def on_delayreport_command(self, delay: int) -> Optional[Message]:
2169
+ self.emit(self.EVENT_DELAY_REPORT, delay)
2170
+ return None
2123
2171
 
2124
- def on_rtp_channel_open(self):
2125
- self.emit('rtp_channel_open')
2172
+ def on_security_control_command(self, data: bytes) -> Optional[Message]:
2173
+ self.emit(self.EVENT_SECURITY_CONTROL, data)
2174
+ return None
2126
2175
 
2127
- def on_rtp_channel_close(self):
2128
- self.emit('rtp_channel_close')
2176
+ def on_rtp_channel_open(self) -> None:
2177
+ self.emit(self.EVENT_RTP_CHANNEL_OPEN)
2178
+ return None
2179
+
2180
+ def on_rtp_channel_close(self) -> None:
2181
+ self.emit(self.EVENT_RTP_CHANNEL_CLOSE)
2182
+ return None
2129
2183
 
2130
2184
 
2131
2185
  # -----------------------------------------------------------------------------
@@ -2156,13 +2210,13 @@ class LocalSource(LocalStreamEndPoint):
2156
2210
  if self.packet_pump and self.stream and self.stream.rtp_channel:
2157
2211
  return await self.packet_pump.start(self.stream.rtp_channel)
2158
2212
 
2159
- self.emit('start')
2213
+ self.emit(self.EVENT_START)
2160
2214
 
2161
2215
  async def stop(self) -> None:
2162
2216
  if self.packet_pump:
2163
2217
  return await self.packet_pump.stop()
2164
2218
 
2165
- self.emit('stop')
2219
+ self.emit(self.EVENT_STOP)
2166
2220
 
2167
2221
  def on_start_command(self):
2168
2222
  asyncio.create_task(self.start())
@@ -2203,4 +2257,4 @@ class LocalSink(LocalStreamEndPoint):
2203
2257
  f'{color("<<< RTP Packet:", "green")} '
2204
2258
  f'{rtp_packet} {rtp_packet.payload[:16].hex()}'
2205
2259
  )
2206
- self.emit('rtp_packet', rtp_packet)
2260
+ self.emit(self.EVENT_RTP_PACKET, rtp_packet)
bumble/avrcp.py CHANGED
@@ -996,6 +996,10 @@ class Delegate:
996
996
  class Protocol(utils.EventEmitter):
997
997
  """AVRCP Controller and Target protocol."""
998
998
 
999
+ EVENT_CONNECTION = "connection"
1000
+ EVENT_START = "start"
1001
+ EVENT_STOP = "stop"
1002
+
999
1003
  class PacketType(enum.IntEnum):
1000
1004
  SINGLE = 0b00
1001
1005
  START = 0b01
@@ -1456,9 +1460,11 @@ class Protocol(utils.EventEmitter):
1456
1460
 
1457
1461
  def _on_avctp_connection(self, l2cap_channel: l2cap.ClassicChannel) -> None:
1458
1462
  logger.debug("AVCTP connection established")
1459
- l2cap_channel.on("open", lambda: self._on_avctp_channel_open(l2cap_channel))
1463
+ l2cap_channel.on(
1464
+ l2cap_channel.EVENT_OPEN, lambda: self._on_avctp_channel_open(l2cap_channel)
1465
+ )
1460
1466
 
1461
- self.emit("connection")
1467
+ self.emit(self.EVENT_CONNECTION)
1462
1468
 
1463
1469
  def _on_avctp_channel_open(self, l2cap_channel: l2cap.ClassicChannel) -> None:
1464
1470
  logger.debug("AVCTP channel open")
@@ -1473,15 +1479,15 @@ class Protocol(utils.EventEmitter):
1473
1479
  self.avctp_protocol.register_response_handler(
1474
1480
  AVRCP_PID, self._on_avctp_response
1475
1481
  )
1476
- l2cap_channel.on("close", self._on_avctp_channel_close)
1482
+ l2cap_channel.on(l2cap_channel.EVENT_CLOSE, self._on_avctp_channel_close)
1477
1483
 
1478
- self.emit("start")
1484
+ self.emit(self.EVENT_START)
1479
1485
 
1480
1486
  def _on_avctp_channel_close(self) -> None:
1481
1487
  logger.debug("AVCTP channel closed")
1482
1488
  self.avctp_protocol = None
1483
1489
 
1484
- self.emit("stop")
1490
+ self.emit(self.EVENT_STOP)
1485
1491
 
1486
1492
  def _on_avctp_command(
1487
1493
  self, transaction_label: int, command: avc.CommandFrame
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