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.
- bumble/_version.py +2 -2
- bumble/apps/bench.py +8 -4
- bumble/apps/console.py +2 -2
- bumble/apps/pair.py +185 -32
- bumble/att.py +13 -12
- bumble/avctp.py +2 -2
- bumble/avdtp.py +122 -68
- bumble/avrcp.py +11 -5
- bumble/core.py +13 -7
- bumble/{crypto.py → crypto/__init__.py} +11 -95
- bumble/crypto/builtin.py +652 -0
- bumble/crypto/cryptography.py +84 -0
- bumble/device.py +365 -185
- bumble/drivers/intel.py +3 -0
- bumble/gatt.py +3 -5
- bumble/gatt_client.py +5 -3
- bumble/gatt_server.py +8 -6
- bumble/hci.py +81 -4
- bumble/hfp.py +44 -20
- bumble/hid.py +24 -12
- bumble/host.py +24 -0
- bumble/keys.py +64 -48
- bumble/l2cap.py +19 -9
- bumble/pandora/host.py +11 -11
- bumble/pandora/l2cap.py +2 -2
- bumble/pandora/security.py +72 -56
- bumble/profiles/aics.py +3 -5
- bumble/profiles/ancs.py +3 -1
- bumble/profiles/ascs.py +11 -5
- bumble/profiles/asha.py +11 -6
- bumble/profiles/csip.py +1 -3
- bumble/profiles/gatt_service.py +1 -3
- bumble/profiles/hap.py +16 -33
- bumble/profiles/mcp.py +12 -9
- bumble/profiles/vcs.py +5 -5
- bumble/profiles/vocs.py +6 -9
- bumble/rfcomm.py +17 -8
- bumble/smp.py +14 -8
- {bumble-0.0.210.dist-info → bumble-0.0.212.dist-info}/METADATA +4 -4
- {bumble-0.0.210.dist-info → bumble-0.0.212.dist-info}/RECORD +44 -42
- {bumble-0.0.210.dist-info → bumble-0.0.212.dist-info}/WHEEL +1 -1
- {bumble-0.0.210.dist-info → bumble-0.0.212.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.210.dist-info → bumble-0.0.212.dist-info}/licenses/LICENSE +0 -0
- {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,
|
|
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(
|
|
1243
|
-
l2cap_channel.on(
|
|
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(
|
|
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(
|
|
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,
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
1623
|
-
|
|
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
|
-
|
|
1641
|
-
|
|
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(
|
|
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.
|
|
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(
|
|
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(
|
|
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(
|
|
1742
|
-
channel.on(
|
|
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
|
|
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(
|
|
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(
|
|
1942
|
-
channel.on(
|
|
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
|
-
|
|
2124
|
+
async def start(self) -> None:
|
|
2125
|
+
"""[Source Only] Handles when receiving start command."""
|
|
2085
2126
|
|
|
2086
|
-
async def stop(self):
|
|
2087
|
-
|
|
2127
|
+
async def stop(self) -> None:
|
|
2128
|
+
"""[Source Only] Handles when receiving stop command."""
|
|
2088
2129
|
|
|
2089
|
-
async def close(self):
|
|
2090
|
-
|
|
2130
|
+
async def close(self) -> None:
|
|
2131
|
+
"""[Source Only] Handles when receiving close command."""
|
|
2091
2132
|
|
|
2092
|
-
def on_reconfigure_command(self, command):
|
|
2093
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
2125
|
-
self.emit(
|
|
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
|
|
2128
|
-
self.emit(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
1463
|
+
l2cap_channel.on(
|
|
1464
|
+
l2cap_channel.EVENT_OPEN, lambda: self._on_avctp_channel_open(l2cap_channel)
|
|
1465
|
+
)
|
|
1460
1466
|
|
|
1461
|
-
self.emit(
|
|
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(
|
|
1482
|
+
l2cap_channel.on(l2cap_channel.EVENT_CLOSE, self._on_avctp_channel_close)
|
|
1477
1483
|
|
|
1478
|
-
self.emit(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
1411
|
-
LE_GENERAL_DISCOVERABLE_MODE_FLAG =
|
|
1412
|
-
BR_EDR_NOT_SUPPORTED_FLAG =
|
|
1413
|
-
BR_EDR_CONTROLLER_FLAG =
|
|
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
|
|