bumble 0.0.220__py3-none-any.whl → 0.0.222__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/a2dp.py +5 -5
- bumble/apps/auracast.py +746 -473
- bumble/apps/bench.py +4 -5
- bumble/apps/console.py +5 -10
- bumble/apps/controller_info.py +12 -7
- bumble/apps/controller_loopback.py +1 -2
- bumble/apps/device_info.py +2 -3
- bumble/apps/gatt_dump.py +0 -1
- bumble/apps/lea_unicast/app.py +1 -1
- bumble/apps/pair.py +49 -46
- bumble/apps/pandora_server.py +2 -2
- bumble/apps/player/player.py +10 -12
- bumble/apps/rfcomm_bridge.py +10 -11
- bumble/apps/scan.py +1 -3
- bumble/apps/speaker/speaker.py +3 -4
- bumble/at.py +4 -5
- bumble/att.py +91 -25
- bumble/audio/io.py +5 -3
- bumble/avc.py +1 -2
- bumble/avctp.py +2 -3
- bumble/avdtp.py +53 -57
- bumble/avrcp.py +25 -27
- bumble/codecs.py +15 -15
- bumble/colors.py +7 -8
- bumble/controller.py +663 -391
- bumble/core.py +41 -49
- bumble/crypto/__init__.py +2 -1
- bumble/crypto/builtin.py +2 -8
- bumble/data_types.py +2 -1
- bumble/decoder.py +2 -3
- bumble/device.py +171 -142
- bumble/drivers/__init__.py +3 -2
- bumble/drivers/intel.py +6 -8
- bumble/drivers/rtk.py +1 -1
- bumble/gatt.py +9 -9
- bumble/gatt_adapters.py +6 -6
- bumble/gatt_client.py +110 -60
- bumble/gatt_server.py +209 -139
- bumble/hci.py +87 -74
- bumble/helpers.py +5 -5
- bumble/hfp.py +27 -26
- bumble/hid.py +9 -9
- bumble/host.py +44 -50
- bumble/keys.py +17 -17
- bumble/l2cap.py +1070 -218
- bumble/link.py +26 -159
- bumble/ll.py +200 -0
- bumble/pairing.py +14 -15
- bumble/pandora/__init__.py +2 -2
- bumble/pandora/device.py +6 -4
- bumble/pandora/host.py +19 -10
- bumble/pandora/l2cap.py +8 -9
- bumble/pandora/security.py +18 -16
- bumble/pandora/utils.py +4 -4
- bumble/profiles/aics.py +6 -8
- bumble/profiles/ams.py +3 -5
- bumble/profiles/ancs.py +11 -11
- bumble/profiles/ascs.py +5 -5
- bumble/profiles/asha.py +10 -9
- bumble/profiles/bass.py +9 -3
- bumble/profiles/battery_service.py +1 -2
- bumble/profiles/csip.py +9 -10
- bumble/profiles/device_information_service.py +16 -17
- bumble/profiles/gap.py +3 -4
- bumble/profiles/gatt_service.py +0 -1
- bumble/profiles/gmap.py +12 -13
- bumble/profiles/hap.py +3 -3
- bumble/profiles/heart_rate_service.py +7 -8
- bumble/profiles/le_audio.py +1 -1
- bumble/profiles/mcp.py +28 -28
- bumble/profiles/pacs.py +13 -17
- bumble/profiles/pbp.py +16 -0
- bumble/profiles/vcs.py +2 -2
- bumble/profiles/vocs.py +6 -9
- bumble/rfcomm.py +19 -18
- bumble/sdp.py +12 -11
- bumble/smp.py +20 -30
- bumble/snoop.py +2 -1
- bumble/tools/generate_company_id_list.py +1 -1
- bumble/tools/intel_util.py +2 -2
- bumble/tools/rtk_fw_download.py +1 -1
- bumble/tools/rtk_util.py +1 -1
- bumble/transport/__init__.py +1 -2
- bumble/transport/android_emulator.py +2 -3
- bumble/transport/android_netsim.py +49 -40
- bumble/transport/common.py +9 -9
- bumble/transport/file.py +1 -2
- bumble/transport/hci_socket.py +2 -3
- bumble/transport/pty.py +3 -5
- bumble/transport/pyusb.py +8 -5
- bumble/transport/serial.py +1 -2
- bumble/transport/vhci.py +1 -2
- bumble/transport/ws_server.py +2 -3
- bumble/utils.py +22 -9
- bumble/vendor/android/hci.py +4 -2
- {bumble-0.0.220.dist-info → bumble-0.0.222.dist-info}/METADATA +3 -2
- {bumble-0.0.220.dist-info → bumble-0.0.222.dist-info}/RECORD +102 -101
- {bumble-0.0.220.dist-info → bumble-0.0.222.dist-info}/WHEEL +0 -0
- {bumble-0.0.220.dist-info → bumble-0.0.222.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.220.dist-info → bumble-0.0.222.dist-info}/licenses/LICENSE +0 -0
- {bumble-0.0.220.dist-info → bumble-0.0.222.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
|
-
) ->
|
|
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
|
-
) ->
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
481
|
-
service_capabilities_bytes:
|
|
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:
|
|
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:
|
|
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[
|
|
1266
|
+
transaction_results: list[asyncio.Future[Message] | None]
|
|
1270
1267
|
channel_connector: Callable[[], Awaitable[l2cap.ClassicChannel]]
|
|
1271
|
-
channel_acceptor:
|
|
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) ->
|
|
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
|
-
) ->
|
|
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(
|
|
1573
|
-
|
|
1574
|
-
|
|
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) ->
|
|
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
|
-
) ->
|
|
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
|
-
) ->
|
|
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
|
-
) ->
|
|
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
|
-
) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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
|
-
) ->
|
|
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) ->
|
|
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:
|
|
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
|
-
) ->
|
|
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) ->
|
|
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
|
-
) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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:
|
|
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:
|
|
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
|
-
) ->
|
|
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
|
-
) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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,
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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[
|
|
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[
|
|
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:
|
|
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:
|
|
1440
|
-
ApplicationSetting.EqualizerOnOffStatus
|
|
1441
|
-
ApplicationSetting.RepeatModeStatus
|
|
1442
|
-
ApplicationSetting.ShuffleOnOffStatus
|
|
1443
|
-
ApplicationSetting.ScanOnOffStatus
|
|
1444
|
-
ApplicationSetting.GenericValue
|
|
1445
|
-
|
|
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:
|
|
1631
|
+
receive_command_state: ReceiveCommandState | None
|
|
1632
1632
|
response_pdu_assembler: PduAssembler
|
|
1633
|
-
receive_response_state:
|
|
1634
|
-
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:
|
|
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:
|
|
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}:
|
|
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:
|
|
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
|
-
|
|
166
|
+
reader.read(1) # frame_length_flag
|
|
167
167
|
depends_on_core_coder = reader.read(1)
|
|
168
168
|
if depends_on_core_coder:
|
|
169
|
-
|
|
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
|
-
|
|
174
|
+
reader.read(3) # layer_nr
|
|
175
175
|
if extension_flag:
|
|
176
176
|
if audio_object_type == 22:
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
-
|
|
368
|
-
stream_cnt = 0
|
|
369
|
-
|
|
370
|
-
|
|
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
|
-
|
|
394
|
+
reader.read(8) # latm_buffer_fullness
|
|
395
395
|
elif frame_length_type == 1:
|
|
396
|
-
|
|
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
|
-
|
|
416
|
+
reader.read(8) # crc_checksum
|
|
417
417
|
|
|
418
418
|
return cls(other_data_present, other_data_len_bits, audio_specific_config)
|
|
419
419
|
|