bumble 0.0.208__py3-none-any.whl → 0.0.210__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 +7 -7
- bumble/apps/auracast.py +37 -29
- bumble/apps/bench.py +9 -7
- bumble/apps/console.py +1 -1
- bumble/apps/lea_unicast/app.py +6 -2
- bumble/apps/pair.py +4 -3
- bumble/apps/player/player.py +3 -3
- bumble/apps/rfcomm_bridge.py +1 -1
- bumble/apps/speaker/speaker.py +4 -2
- bumble/att.py +12 -5
- bumble/avc.py +5 -5
- bumble/avdtp.py +9 -10
- bumble/avrcp.py +18 -19
- bumble/bridge.py +2 -2
- bumble/controller.py +13 -15
- bumble/core.py +61 -60
- bumble/device.py +193 -162
- bumble/drivers/__init__.py +2 -2
- bumble/gap.py +1 -1
- bumble/gatt.py +16 -0
- bumble/gatt_adapters.py +3 -3
- bumble/gatt_client.py +27 -21
- bumble/gatt_server.py +9 -10
- bumble/hci.py +109 -90
- bumble/hfp.py +3 -3
- bumble/hid.py +4 -3
- bumble/host.py +30 -19
- bumble/keys.py +3 -3
- bumble/l2cap.py +21 -19
- bumble/link.py +5 -6
- bumble/pairing.py +3 -3
- bumble/pandora/__init__.py +5 -5
- bumble/pandora/host.py +30 -23
- bumble/pandora/l2cap.py +2 -2
- bumble/pandora/security.py +17 -19
- bumble/pandora/utils.py +2 -2
- bumble/profiles/aics.py +6 -6
- bumble/profiles/ancs.py +513 -0
- bumble/profiles/ascs.py +17 -10
- bumble/profiles/asha.py +5 -5
- bumble/profiles/bass.py +1 -1
- bumble/profiles/csip.py +10 -10
- bumble/profiles/gatt_service.py +12 -12
- bumble/profiles/hap.py +16 -16
- bumble/profiles/mcp.py +26 -24
- bumble/profiles/pacs.py +6 -6
- bumble/profiles/pbp.py +1 -1
- bumble/profiles/vcs.py +6 -4
- bumble/profiles/vocs.py +3 -3
- bumble/rfcomm.py +8 -8
- bumble/sdp.py +1 -1
- bumble/smp.py +39 -33
- bumble/transport/__init__.py +24 -19
- bumble/transport/android_emulator.py +8 -4
- bumble/transport/android_netsim.py +8 -5
- bumble/transport/common.py +5 -1
- bumble/transport/file.py +1 -1
- bumble/transport/hci_socket.py +1 -1
- bumble/transport/pty.py +1 -1
- bumble/transport/pyusb.py +3 -3
- bumble/transport/serial.py +1 -1
- bumble/transport/tcp_client.py +1 -1
- bumble/transport/tcp_server.py +1 -1
- bumble/transport/udp.py +1 -1
- bumble/transport/unix.py +1 -1
- bumble/transport/usb.py +1 -3
- bumble/transport/vhci.py +2 -2
- bumble/transport/ws_client.py +6 -1
- bumble/transport/ws_server.py +1 -1
- bumble/utils.py +89 -76
- {bumble-0.0.208.dist-info → bumble-0.0.210.dist-info}/METADATA +3 -2
- {bumble-0.0.208.dist-info → bumble-0.0.210.dist-info}/RECORD +77 -76
- {bumble-0.0.208.dist-info → bumble-0.0.210.dist-info}/WHEEL +1 -1
- {bumble-0.0.208.dist-info → bumble-0.0.210.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.208.dist-info → bumble-0.0.210.dist-info/licenses}/LICENSE +0 -0
- {bumble-0.0.208.dist-info → bumble-0.0.210.dist-info}/top_level.txt +0 -0
bumble/device.py
CHANGED
|
@@ -49,18 +49,14 @@ from typing import (
|
|
|
49
49
|
)
|
|
50
50
|
from typing_extensions import Self
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
from .
|
|
55
|
-
from .
|
|
56
|
-
from .
|
|
57
|
-
from .
|
|
58
|
-
from .
|
|
59
|
-
|
|
60
|
-
BT_BR_EDR_TRANSPORT,
|
|
61
|
-
BT_CENTRAL_ROLE,
|
|
62
|
-
BT_LE_TRANSPORT,
|
|
63
|
-
BT_PERIPHERAL_ROLE,
|
|
52
|
+
|
|
53
|
+
from bumble.colors import color
|
|
54
|
+
from bumble.att import ATT_CID, ATT_DEFAULT_MTU, ATT_PDU
|
|
55
|
+
from bumble.gatt import Attribute, Characteristic, Descriptor, Service
|
|
56
|
+
from bumble.host import DataPacketQueue, Host
|
|
57
|
+
from bumble.profiles.gap import GenericAccessService
|
|
58
|
+
from bumble.core import (
|
|
59
|
+
PhysicalTransport,
|
|
64
60
|
AdvertisingData,
|
|
65
61
|
BaseBumbleError,
|
|
66
62
|
ConnectionParameterUpdateError,
|
|
@@ -74,16 +70,8 @@ from .core import (
|
|
|
74
70
|
OutOfResourcesError,
|
|
75
71
|
UnreachableError,
|
|
76
72
|
)
|
|
77
|
-
from
|
|
78
|
-
|
|
79
|
-
CompositeEventEmitter,
|
|
80
|
-
EventWatcher,
|
|
81
|
-
setup_event_forwarding,
|
|
82
|
-
composite_listener,
|
|
83
|
-
deprecated,
|
|
84
|
-
experimental,
|
|
85
|
-
)
|
|
86
|
-
from .keys import (
|
|
73
|
+
from bumble import utils
|
|
74
|
+
from bumble.keys import (
|
|
87
75
|
KeyStore,
|
|
88
76
|
PairingKeys,
|
|
89
77
|
)
|
|
@@ -98,7 +86,7 @@ from bumble import core
|
|
|
98
86
|
from bumble.profiles import gatt_service
|
|
99
87
|
|
|
100
88
|
if TYPE_CHECKING:
|
|
101
|
-
from .transport.common import TransportSource, TransportSink
|
|
89
|
+
from bumble.transport.common import TransportSource, TransportSink
|
|
102
90
|
|
|
103
91
|
|
|
104
92
|
# -----------------------------------------------------------------------------
|
|
@@ -579,7 +567,7 @@ class PeriodicAdvertisingParameters:
|
|
|
579
567
|
|
|
580
568
|
# -----------------------------------------------------------------------------
|
|
581
569
|
@dataclass
|
|
582
|
-
class AdvertisingSet(EventEmitter):
|
|
570
|
+
class AdvertisingSet(utils.EventEmitter):
|
|
583
571
|
device: Device
|
|
584
572
|
advertising_handle: int
|
|
585
573
|
auto_restart: bool
|
|
@@ -796,13 +784,24 @@ class AdvertisingSet(EventEmitter):
|
|
|
796
784
|
)
|
|
797
785
|
del self.device.extended_advertising_sets[self.advertising_handle]
|
|
798
786
|
|
|
787
|
+
async def transfer_periodic_info(
|
|
788
|
+
self, connection: Connection, service_data: int = 0
|
|
789
|
+
) -> None:
|
|
790
|
+
if not self.periodic_enabled:
|
|
791
|
+
raise core.InvalidStateError(
|
|
792
|
+
f"Periodic Advertising is not enabled on Advertising Set 0x{self.advertising_handle:02X}"
|
|
793
|
+
)
|
|
794
|
+
await connection.transfer_periodic_set_info(
|
|
795
|
+
self.advertising_handle, service_data
|
|
796
|
+
)
|
|
797
|
+
|
|
799
798
|
def on_termination(self, status: int) -> None:
|
|
800
799
|
self.enabled = False
|
|
801
800
|
self.emit('termination', status)
|
|
802
801
|
|
|
803
802
|
|
|
804
803
|
# -----------------------------------------------------------------------------
|
|
805
|
-
class PeriodicAdvertisingSync(EventEmitter):
|
|
804
|
+
class PeriodicAdvertisingSync(utils.EventEmitter):
|
|
806
805
|
class State(Enum):
|
|
807
806
|
INIT = 0
|
|
808
807
|
PENDING = 1
|
|
@@ -931,7 +930,7 @@ class PeriodicAdvertisingSync(EventEmitter):
|
|
|
931
930
|
"received established event for cancelled sync, will terminate"
|
|
932
931
|
)
|
|
933
932
|
self.state = self.State.ESTABLISHED
|
|
934
|
-
AsyncRunner.spawn(self.terminate())
|
|
933
|
+
utils.AsyncRunner.spawn(self.terminate())
|
|
935
934
|
return
|
|
936
935
|
|
|
937
936
|
if status == hci.HCI_SUCCESS:
|
|
@@ -1017,7 +1016,7 @@ class BigParameters:
|
|
|
1017
1016
|
|
|
1018
1017
|
# -----------------------------------------------------------------------------
|
|
1019
1018
|
@dataclass
|
|
1020
|
-
class Big(EventEmitter):
|
|
1019
|
+
class Big(utils.EventEmitter):
|
|
1021
1020
|
class State(IntEnum):
|
|
1022
1021
|
PENDING = 0
|
|
1023
1022
|
ACTIVE = 1
|
|
@@ -1057,7 +1056,7 @@ class Big(EventEmitter):
|
|
|
1057
1056
|
logger.error('BIG %d is not active.', self.big_handle)
|
|
1058
1057
|
return
|
|
1059
1058
|
|
|
1060
|
-
with closing(EventWatcher()) as watcher:
|
|
1059
|
+
with closing(utils.EventWatcher()) as watcher:
|
|
1061
1060
|
terminated = asyncio.Event()
|
|
1062
1061
|
watcher.once(self, Big.Event.TERMINATION, lambda _: terminated.set())
|
|
1063
1062
|
await self.device.send_command(
|
|
@@ -1080,7 +1079,7 @@ class BigSyncParameters:
|
|
|
1080
1079
|
|
|
1081
1080
|
# -----------------------------------------------------------------------------
|
|
1082
1081
|
@dataclass
|
|
1083
|
-
class BigSync(EventEmitter):
|
|
1082
|
+
class BigSync(utils.EventEmitter):
|
|
1084
1083
|
class State(IntEnum):
|
|
1085
1084
|
PENDING = 0
|
|
1086
1085
|
ACTIVE = 1
|
|
@@ -1115,7 +1114,7 @@ class BigSync(EventEmitter):
|
|
|
1115
1114
|
logger.error('BIG Sync %d is not active.', self.big_handle)
|
|
1116
1115
|
return
|
|
1117
1116
|
|
|
1118
|
-
with closing(EventWatcher()) as watcher:
|
|
1117
|
+
with closing(utils.EventWatcher()) as watcher:
|
|
1119
1118
|
terminated = asyncio.Event()
|
|
1120
1119
|
watcher.once(self, BigSync.Event.TERMINATION, lambda _: terminated.set())
|
|
1121
1120
|
await self.device.send_command(
|
|
@@ -1245,7 +1244,7 @@ class Peer:
|
|
|
1245
1244
|
self,
|
|
1246
1245
|
uuids: Iterable[Union[core.UUID, str]] = (),
|
|
1247
1246
|
service: Optional[gatt_client.ServiceProxy] = None,
|
|
1248
|
-
) -> list[gatt_client.CharacteristicProxy]:
|
|
1247
|
+
) -> list[gatt_client.CharacteristicProxy[bytes]]:
|
|
1249
1248
|
return await self.gatt_client.discover_characteristics(
|
|
1250
1249
|
uuids=uuids, service=service
|
|
1251
1250
|
)
|
|
@@ -1260,7 +1259,7 @@ class Peer:
|
|
|
1260
1259
|
characteristic, start_handle, end_handle
|
|
1261
1260
|
)
|
|
1262
1261
|
|
|
1263
|
-
async def discover_attributes(self) -> list[gatt_client.AttributeProxy]:
|
|
1262
|
+
async def discover_attributes(self) -> list[gatt_client.AttributeProxy[bytes]]:
|
|
1264
1263
|
return await self.gatt_client.discover_attributes()
|
|
1265
1264
|
|
|
1266
1265
|
async def discover_all(self):
|
|
@@ -1314,7 +1313,7 @@ class Peer:
|
|
|
1314
1313
|
self,
|
|
1315
1314
|
uuid: core.UUID,
|
|
1316
1315
|
service: Optional[Union[gatt_client.ServiceProxy, core.UUID]] = None,
|
|
1317
|
-
) -> list[gatt_client.CharacteristicProxy]:
|
|
1316
|
+
) -> list[gatt_client.CharacteristicProxy[bytes]]:
|
|
1318
1317
|
if isinstance(service, core.UUID):
|
|
1319
1318
|
return list(
|
|
1320
1319
|
itertools.chain(
|
|
@@ -1384,7 +1383,7 @@ ConnectionParametersPreferences.default = ConnectionParametersPreferences()
|
|
|
1384
1383
|
|
|
1385
1384
|
# -----------------------------------------------------------------------------
|
|
1386
1385
|
@dataclass
|
|
1387
|
-
class ScoLink(CompositeEventEmitter):
|
|
1386
|
+
class ScoLink(utils.CompositeEventEmitter):
|
|
1388
1387
|
device: Device
|
|
1389
1388
|
acl_connection: Connection
|
|
1390
1389
|
handle: int
|
|
@@ -1475,7 +1474,7 @@ class _IsoLink:
|
|
|
1475
1474
|
|
|
1476
1475
|
# -----------------------------------------------------------------------------
|
|
1477
1476
|
@dataclass
|
|
1478
|
-
class CisLink(
|
|
1477
|
+
class CisLink(utils.EventEmitter, _IsoLink):
|
|
1479
1478
|
class State(IntEnum):
|
|
1480
1479
|
PENDING = 0
|
|
1481
1480
|
ESTABLISHED = 1
|
|
@@ -1552,16 +1551,16 @@ class IsoPacketStream:
|
|
|
1552
1551
|
|
|
1553
1552
|
|
|
1554
1553
|
# -----------------------------------------------------------------------------
|
|
1555
|
-
class Connection(CompositeEventEmitter):
|
|
1554
|
+
class Connection(utils.CompositeEventEmitter):
|
|
1556
1555
|
device: Device
|
|
1557
1556
|
handle: int
|
|
1558
|
-
transport:
|
|
1557
|
+
transport: core.PhysicalTransport
|
|
1559
1558
|
self_address: hci.Address
|
|
1560
1559
|
self_resolvable_address: Optional[hci.Address]
|
|
1561
1560
|
peer_address: hci.Address
|
|
1562
1561
|
peer_resolvable_address: Optional[hci.Address]
|
|
1563
1562
|
peer_le_features: Optional[hci.LeFeatureMask]
|
|
1564
|
-
role:
|
|
1563
|
+
role: hci.Role
|
|
1565
1564
|
encryption: int
|
|
1566
1565
|
authenticated: bool
|
|
1567
1566
|
sc: bool
|
|
@@ -1572,7 +1571,7 @@ class Connection(CompositeEventEmitter):
|
|
|
1572
1571
|
cs_configs: dict[int, ChannelSoundingConfig] # Config ID to Configuration
|
|
1573
1572
|
cs_procedures: dict[int, ChannelSoundingProcedure] # Config ID to Procedures
|
|
1574
1573
|
|
|
1575
|
-
@composite_listener
|
|
1574
|
+
@utils.composite_listener
|
|
1576
1575
|
class Listener:
|
|
1577
1576
|
def on_disconnection(self, reason):
|
|
1578
1577
|
pass
|
|
@@ -1651,7 +1650,7 @@ class Connection(CompositeEventEmitter):
|
|
|
1651
1650
|
return cls(
|
|
1652
1651
|
device,
|
|
1653
1652
|
None,
|
|
1654
|
-
|
|
1653
|
+
PhysicalTransport.BR_EDR,
|
|
1655
1654
|
device.public_address,
|
|
1656
1655
|
None,
|
|
1657
1656
|
peer_address,
|
|
@@ -1666,7 +1665,7 @@ class Connection(CompositeEventEmitter):
|
|
|
1666
1665
|
Finish an incomplete connection upon completion.
|
|
1667
1666
|
"""
|
|
1668
1667
|
assert self.handle is None
|
|
1669
|
-
assert self.transport ==
|
|
1668
|
+
assert self.transport == PhysicalTransport.BR_EDR
|
|
1670
1669
|
self.handle = handle
|
|
1671
1670
|
self.parameters = parameters
|
|
1672
1671
|
|
|
@@ -1674,9 +1673,9 @@ class Connection(CompositeEventEmitter):
|
|
|
1674
1673
|
def role_name(self):
|
|
1675
1674
|
if self.role is None:
|
|
1676
1675
|
return 'NOT-SET'
|
|
1677
|
-
if self.role ==
|
|
1676
|
+
if self.role == hci.Role.CENTRAL:
|
|
1678
1677
|
return 'CENTRAL'
|
|
1679
|
-
if self.role ==
|
|
1678
|
+
if self.role == hci.Role.PERIPHERAL:
|
|
1680
1679
|
return 'PERIPHERAL'
|
|
1681
1680
|
return f'UNKNOWN[{self.role}]'
|
|
1682
1681
|
|
|
@@ -1691,7 +1690,7 @@ class Connection(CompositeEventEmitter):
|
|
|
1691
1690
|
def send_l2cap_pdu(self, cid: int, pdu: bytes) -> None:
|
|
1692
1691
|
self.device.send_l2cap_pdu(self.handle, cid, pdu)
|
|
1693
1692
|
|
|
1694
|
-
@deprecated("Please use create_l2cap_channel()")
|
|
1693
|
+
@utils.deprecated("Please use create_l2cap_channel()")
|
|
1695
1694
|
async def open_l2cap_channel(
|
|
1696
1695
|
self,
|
|
1697
1696
|
psm,
|
|
@@ -1734,7 +1733,7 @@ class Connection(CompositeEventEmitter):
|
|
|
1734
1733
|
async def encrypt(self, enable: bool = True) -> None:
|
|
1735
1734
|
return await self.device.encrypt(self, enable)
|
|
1736
1735
|
|
|
1737
|
-
async def switch_role(self, role:
|
|
1736
|
+
async def switch_role(self, role: hci.Role) -> None:
|
|
1738
1737
|
return await self.device.switch_role(self, role)
|
|
1739
1738
|
|
|
1740
1739
|
async def sustain(self, timeout: Optional[float] = None) -> None:
|
|
@@ -1745,7 +1744,9 @@ class Connection(CompositeEventEmitter):
|
|
|
1745
1744
|
self.on('disconnection_failure', abort.set_exception)
|
|
1746
1745
|
|
|
1747
1746
|
try:
|
|
1748
|
-
await asyncio.wait_for(
|
|
1747
|
+
await asyncio.wait_for(
|
|
1748
|
+
utils.cancel_on_event(self.device, 'flush', abort), timeout
|
|
1749
|
+
)
|
|
1749
1750
|
finally:
|
|
1750
1751
|
self.remove_listener('disconnection', abort.set_result)
|
|
1751
1752
|
self.remove_listener('disconnection_failure', abort.set_exception)
|
|
@@ -1784,6 +1785,13 @@ class Connection(CompositeEventEmitter):
|
|
|
1784
1785
|
) -> None:
|
|
1785
1786
|
await self.device.transfer_periodic_sync(self, sync_handle, service_data)
|
|
1786
1787
|
|
|
1788
|
+
async def transfer_periodic_set_info(
|
|
1789
|
+
self, advertising_handle: int, service_data: int = 0
|
|
1790
|
+
) -> None:
|
|
1791
|
+
await self.device.transfer_periodic_set_info(
|
|
1792
|
+
self, advertising_handle, service_data
|
|
1793
|
+
)
|
|
1794
|
+
|
|
1787
1795
|
# [Classic only]
|
|
1788
1796
|
async def request_remote_name(self):
|
|
1789
1797
|
return await self.device.request_remote_name(self)
|
|
@@ -1814,7 +1822,7 @@ class Connection(CompositeEventEmitter):
|
|
|
1814
1822
|
raise
|
|
1815
1823
|
|
|
1816
1824
|
def __str__(self):
|
|
1817
|
-
if self.transport ==
|
|
1825
|
+
if self.transport == PhysicalTransport.LE:
|
|
1818
1826
|
return (
|
|
1819
1827
|
f'Connection(transport=LE, handle=0x{self.handle:04X}, '
|
|
1820
1828
|
f'role={self.role_name}, '
|
|
@@ -2022,7 +2030,7 @@ device_host_event_handlers: list[str] = []
|
|
|
2022
2030
|
|
|
2023
2031
|
|
|
2024
2032
|
# -----------------------------------------------------------------------------
|
|
2025
|
-
class Device(CompositeEventEmitter):
|
|
2033
|
+
class Device(utils.CompositeEventEmitter):
|
|
2026
2034
|
# Incomplete list of fields.
|
|
2027
2035
|
random_address: hci.Address # Random private address that may change periodically
|
|
2028
2036
|
public_address: (
|
|
@@ -2054,7 +2062,7 @@ class Device(CompositeEventEmitter):
|
|
|
2054
2062
|
_pending_cis: Dict[int, tuple[int, int]]
|
|
2055
2063
|
gatt_service: gatt_service.GenericAttributeProfileService | None = None
|
|
2056
2064
|
|
|
2057
|
-
@composite_listener
|
|
2065
|
+
@utils.composite_listener
|
|
2058
2066
|
class Listener:
|
|
2059
2067
|
def on_advertisement(self, advertisement):
|
|
2060
2068
|
pass
|
|
@@ -2275,7 +2283,9 @@ class Device(CompositeEventEmitter):
|
|
|
2275
2283
|
self.l2cap_channel_manager.register_fixed_channel(ATT_CID, self.on_gatt_pdu)
|
|
2276
2284
|
|
|
2277
2285
|
# Forward some events
|
|
2278
|
-
setup_event_forwarding(
|
|
2286
|
+
utils.setup_event_forwarding(
|
|
2287
|
+
self.gatt_server, self, 'characteristic_subscription'
|
|
2288
|
+
)
|
|
2279
2289
|
|
|
2280
2290
|
# Set the initial host
|
|
2281
2291
|
if host:
|
|
@@ -2364,11 +2374,11 @@ class Device(CompositeEventEmitter):
|
|
|
2364
2374
|
None,
|
|
2365
2375
|
)
|
|
2366
2376
|
|
|
2367
|
-
@deprecated("Please use create_l2cap_server()")
|
|
2377
|
+
@utils.deprecated("Please use create_l2cap_server()")
|
|
2368
2378
|
def register_l2cap_server(self, psm, server) -> int:
|
|
2369
2379
|
return self.l2cap_channel_manager.register_server(psm, server)
|
|
2370
2380
|
|
|
2371
|
-
@deprecated("Please use create_l2cap_server()")
|
|
2381
|
+
@utils.deprecated("Please use create_l2cap_server()")
|
|
2372
2382
|
def register_l2cap_channel_server(
|
|
2373
2383
|
self,
|
|
2374
2384
|
psm,
|
|
@@ -2381,7 +2391,7 @@ class Device(CompositeEventEmitter):
|
|
|
2381
2391
|
psm, server, max_credits, mtu, mps
|
|
2382
2392
|
)
|
|
2383
2393
|
|
|
2384
|
-
@deprecated("Please use create_l2cap_channel()")
|
|
2394
|
+
@utils.deprecated("Please use create_l2cap_channel()")
|
|
2385
2395
|
async def open_l2cap_channel(
|
|
2386
2396
|
self,
|
|
2387
2397
|
connection,
|
|
@@ -2713,7 +2723,7 @@ class Device(CompositeEventEmitter):
|
|
|
2713
2723
|
if phy == hci.HCI_LE_1M_PHY:
|
|
2714
2724
|
return True
|
|
2715
2725
|
|
|
2716
|
-
feature_map = {
|
|
2726
|
+
feature_map: dict[int, hci.LeFeatureMask] = {
|
|
2717
2727
|
hci.HCI_LE_2M_PHY: hci.LeFeatureMask.LE_2M_PHY,
|
|
2718
2728
|
hci.HCI_LE_CODED_PHY: hci.LeFeatureMask.LE_CODED_PHY,
|
|
2719
2729
|
}
|
|
@@ -2734,7 +2744,7 @@ class Device(CompositeEventEmitter):
|
|
|
2734
2744
|
self,
|
|
2735
2745
|
advertising_type: AdvertisingType = AdvertisingType.UNDIRECTED_CONNECTABLE_SCANNABLE,
|
|
2736
2746
|
target: Optional[hci.Address] = None,
|
|
2737
|
-
own_address_type:
|
|
2747
|
+
own_address_type: hci.OwnAddressType = hci.OwnAddressType.RANDOM,
|
|
2738
2748
|
auto_restart: bool = False,
|
|
2739
2749
|
advertising_data: Optional[bytes] = None,
|
|
2740
2750
|
scan_response_data: Optional[bytes] = None,
|
|
@@ -3015,7 +3025,7 @@ class Device(CompositeEventEmitter):
|
|
|
3015
3025
|
active: bool = True,
|
|
3016
3026
|
scan_interval: float = DEVICE_DEFAULT_SCAN_INTERVAL, # Scan interval in ms
|
|
3017
3027
|
scan_window: float = DEVICE_DEFAULT_SCAN_WINDOW, # Scan window in ms
|
|
3018
|
-
own_address_type:
|
|
3028
|
+
own_address_type: hci.OwnAddressType = hci.OwnAddressType.RANDOM,
|
|
3019
3029
|
filter_duplicates: bool = False,
|
|
3020
3030
|
scanning_phys: Sequence[int] = (hci.HCI_LE_1M_PHY, hci.HCI_LE_CODED_PHY),
|
|
3021
3031
|
) -> None:
|
|
@@ -3091,7 +3101,7 @@ class Device(CompositeEventEmitter):
|
|
|
3091
3101
|
# pylint: disable=line-too-long
|
|
3092
3102
|
hci.HCI_LE_Set_Scan_Parameters_Command(
|
|
3093
3103
|
le_scan_type=scan_type,
|
|
3094
|
-
le_scan_interval=int(
|
|
3104
|
+
le_scan_interval=int(scan_interval / 0.625),
|
|
3095
3105
|
le_scan_window=int(scan_window / 0.625),
|
|
3096
3106
|
own_address_type=own_address_type,
|
|
3097
3107
|
scanning_filter_policy=hci.HCI_LE_Set_Scan_Parameters_Command.BASIC_UNFILTERED_POLICY,
|
|
@@ -3222,7 +3232,7 @@ class Device(CompositeEventEmitter):
|
|
|
3222
3232
|
advertiser_clock_accuracy,
|
|
3223
3233
|
)
|
|
3224
3234
|
|
|
3225
|
-
AsyncRunner.spawn(self._update_periodic_advertising_syncs())
|
|
3235
|
+
utils.AsyncRunner.spawn(self._update_periodic_advertising_syncs())
|
|
3226
3236
|
|
|
3227
3237
|
return
|
|
3228
3238
|
|
|
@@ -3381,11 +3391,11 @@ class Device(CompositeEventEmitter):
|
|
|
3381
3391
|
async def connect(
|
|
3382
3392
|
self,
|
|
3383
3393
|
peer_address: Union[hci.Address, str],
|
|
3384
|
-
transport:
|
|
3394
|
+
transport: core.PhysicalTransport = PhysicalTransport.LE,
|
|
3385
3395
|
connection_parameters_preferences: Optional[
|
|
3386
|
-
|
|
3396
|
+
dict[hci.Phy, ConnectionParametersPreferences]
|
|
3387
3397
|
] = None,
|
|
3388
|
-
own_address_type:
|
|
3398
|
+
own_address_type: hci.OwnAddressType = hci.OwnAddressType.RANDOM,
|
|
3389
3399
|
timeout: Optional[float] = DEVICE_DEFAULT_CONNECT_TIMEOUT,
|
|
3390
3400
|
always_resolve: bool = False,
|
|
3391
3401
|
) -> Connection:
|
|
@@ -3431,22 +3441,23 @@ class Device(CompositeEventEmitter):
|
|
|
3431
3441
|
'''
|
|
3432
3442
|
|
|
3433
3443
|
# Check parameters
|
|
3434
|
-
if transport not in (
|
|
3444
|
+
if transport not in (PhysicalTransport.LE, PhysicalTransport.BR_EDR):
|
|
3435
3445
|
raise InvalidArgumentError('invalid transport')
|
|
3446
|
+
transport = core.PhysicalTransport(transport)
|
|
3436
3447
|
|
|
3437
3448
|
# Adjust the transport automatically if we need to
|
|
3438
|
-
if transport ==
|
|
3439
|
-
transport =
|
|
3440
|
-
elif transport ==
|
|
3441
|
-
transport =
|
|
3449
|
+
if transport == PhysicalTransport.LE and not self.le_enabled:
|
|
3450
|
+
transport = PhysicalTransport.BR_EDR
|
|
3451
|
+
elif transport == PhysicalTransport.BR_EDR and not self.classic_enabled:
|
|
3452
|
+
transport = PhysicalTransport.LE
|
|
3442
3453
|
|
|
3443
3454
|
# Check that there isn't already a pending connection
|
|
3444
|
-
if transport ==
|
|
3455
|
+
if transport == PhysicalTransport.LE and self.is_le_connecting:
|
|
3445
3456
|
raise InvalidStateError('connection already pending')
|
|
3446
3457
|
|
|
3447
3458
|
if isinstance(peer_address, str):
|
|
3448
3459
|
try:
|
|
3449
|
-
if transport ==
|
|
3460
|
+
if transport == PhysicalTransport.LE and peer_address.endswith('@'):
|
|
3450
3461
|
peer_address = hci.Address.from_string_for_transport(
|
|
3451
3462
|
peer_address[:-1], transport
|
|
3452
3463
|
)
|
|
@@ -3466,21 +3477,21 @@ class Device(CompositeEventEmitter):
|
|
|
3466
3477
|
else:
|
|
3467
3478
|
# All BR/EDR addresses should be public addresses
|
|
3468
3479
|
if (
|
|
3469
|
-
transport ==
|
|
3480
|
+
transport == PhysicalTransport.BR_EDR
|
|
3470
3481
|
and peer_address.address_type != hci.Address.PUBLIC_DEVICE_ADDRESS
|
|
3471
3482
|
):
|
|
3472
3483
|
raise InvalidArgumentError('BR/EDR addresses must be PUBLIC')
|
|
3473
3484
|
|
|
3474
3485
|
assert isinstance(peer_address, hci.Address)
|
|
3475
3486
|
|
|
3476
|
-
if transport ==
|
|
3487
|
+
if transport == PhysicalTransport.LE and always_resolve:
|
|
3477
3488
|
logger.debug('resolving address')
|
|
3478
3489
|
peer_address = await self.find_peer_by_identity_address(
|
|
3479
3490
|
peer_address
|
|
3480
3491
|
) # TODO: timeout
|
|
3481
3492
|
|
|
3482
3493
|
def on_connection(connection):
|
|
3483
|
-
if transport ==
|
|
3494
|
+
if transport == PhysicalTransport.LE or (
|
|
3484
3495
|
# match BR/EDR connection event against peer address
|
|
3485
3496
|
connection.transport == transport
|
|
3486
3497
|
and connection.peer_address == peer_address
|
|
@@ -3488,7 +3499,7 @@ class Device(CompositeEventEmitter):
|
|
|
3488
3499
|
pending_connection.set_result(connection)
|
|
3489
3500
|
|
|
3490
3501
|
def on_connection_failure(error):
|
|
3491
|
-
if transport ==
|
|
3502
|
+
if transport == PhysicalTransport.LE or (
|
|
3492
3503
|
# match BR/EDR connection failure event against peer address
|
|
3493
3504
|
error.transport == transport
|
|
3494
3505
|
and error.peer_address == peer_address
|
|
@@ -3502,7 +3513,7 @@ class Device(CompositeEventEmitter):
|
|
|
3502
3513
|
|
|
3503
3514
|
try:
|
|
3504
3515
|
# Tell the controller to connect
|
|
3505
|
-
if transport ==
|
|
3516
|
+
if transport == PhysicalTransport.LE:
|
|
3506
3517
|
if connection_parameters_preferences is None:
|
|
3507
3518
|
if connection_parameters_preferences is None:
|
|
3508
3519
|
connection_parameters_preferences = {
|
|
@@ -3628,7 +3639,7 @@ class Device(CompositeEventEmitter):
|
|
|
3628
3639
|
else:
|
|
3629
3640
|
# Save pending connection
|
|
3630
3641
|
self.pending_connections[peer_address] = Connection.incomplete(
|
|
3631
|
-
self, peer_address,
|
|
3642
|
+
self, peer_address, hci.Role.CENTRAL
|
|
3632
3643
|
)
|
|
3633
3644
|
|
|
3634
3645
|
# TODO: allow passing other settings
|
|
@@ -3647,18 +3658,18 @@ class Device(CompositeEventEmitter):
|
|
|
3647
3658
|
raise hci.HCI_StatusError(result)
|
|
3648
3659
|
|
|
3649
3660
|
# Wait for the connection process to complete
|
|
3650
|
-
if transport ==
|
|
3661
|
+
if transport == PhysicalTransport.LE:
|
|
3651
3662
|
self.le_connecting = True
|
|
3652
3663
|
|
|
3653
3664
|
if timeout is None:
|
|
3654
|
-
return await
|
|
3665
|
+
return await utils.cancel_on_event(self, 'flush', pending_connection)
|
|
3655
3666
|
|
|
3656
3667
|
try:
|
|
3657
3668
|
return await asyncio.wait_for(
|
|
3658
3669
|
asyncio.shield(pending_connection), timeout
|
|
3659
3670
|
)
|
|
3660
3671
|
except asyncio.TimeoutError:
|
|
3661
|
-
if transport ==
|
|
3672
|
+
if transport == PhysicalTransport.LE:
|
|
3662
3673
|
await self.send_command(
|
|
3663
3674
|
hci.HCI_LE_Create_Connection_Cancel_Command()
|
|
3664
3675
|
)
|
|
@@ -3668,13 +3679,15 @@ class Device(CompositeEventEmitter):
|
|
|
3668
3679
|
)
|
|
3669
3680
|
|
|
3670
3681
|
try:
|
|
3671
|
-
return await
|
|
3682
|
+
return await utils.cancel_on_event(
|
|
3683
|
+
self, 'flush', pending_connection
|
|
3684
|
+
)
|
|
3672
3685
|
except core.ConnectionError as error:
|
|
3673
3686
|
raise core.TimeoutError() from error
|
|
3674
3687
|
finally:
|
|
3675
3688
|
self.remove_listener('connection', on_connection)
|
|
3676
3689
|
self.remove_listener('connection_failure', on_connection_failure)
|
|
3677
|
-
if transport ==
|
|
3690
|
+
if transport == PhysicalTransport.LE:
|
|
3678
3691
|
self.le_connecting = False
|
|
3679
3692
|
self.connect_own_address_type = None
|
|
3680
3693
|
else:
|
|
@@ -3683,7 +3696,7 @@ class Device(CompositeEventEmitter):
|
|
|
3683
3696
|
async def accept(
|
|
3684
3697
|
self,
|
|
3685
3698
|
peer_address: Union[hci.Address, str] = hci.Address.ANY,
|
|
3686
|
-
role:
|
|
3699
|
+
role: hci.Role = hci.Role.PERIPHERAL,
|
|
3687
3700
|
timeout: Optional[float] = DEVICE_DEFAULT_CONNECT_TIMEOUT,
|
|
3688
3701
|
) -> Connection:
|
|
3689
3702
|
'''
|
|
@@ -3704,7 +3717,7 @@ class Device(CompositeEventEmitter):
|
|
|
3704
3717
|
# If the address is not parsable, assume it is a name instead
|
|
3705
3718
|
logger.debug('looking for peer by name')
|
|
3706
3719
|
peer_address = await self.find_peer_by_name(
|
|
3707
|
-
peer_address,
|
|
3720
|
+
peer_address, PhysicalTransport.BR_EDR
|
|
3708
3721
|
) # TODO: timeout
|
|
3709
3722
|
|
|
3710
3723
|
assert isinstance(peer_address, hci.Address)
|
|
@@ -3724,7 +3737,7 @@ class Device(CompositeEventEmitter):
|
|
|
3724
3737
|
|
|
3725
3738
|
try:
|
|
3726
3739
|
# Wait for a request or a completed connection
|
|
3727
|
-
pending_request =
|
|
3740
|
+
pending_request = utils.cancel_on_event(self, 'flush', pending_request_fut)
|
|
3728
3741
|
result = await (
|
|
3729
3742
|
asyncio.wait_for(pending_request, timeout)
|
|
3730
3743
|
if timeout
|
|
@@ -3754,14 +3767,14 @@ class Device(CompositeEventEmitter):
|
|
|
3754
3767
|
|
|
3755
3768
|
def on_connection(connection):
|
|
3756
3769
|
if (
|
|
3757
|
-
connection.transport ==
|
|
3770
|
+
connection.transport == PhysicalTransport.BR_EDR
|
|
3758
3771
|
and connection.peer_address == peer_address
|
|
3759
3772
|
):
|
|
3760
3773
|
pending_connection.set_result(connection)
|
|
3761
3774
|
|
|
3762
3775
|
def on_connection_failure(error):
|
|
3763
3776
|
if (
|
|
3764
|
-
error.transport ==
|
|
3777
|
+
error.transport == PhysicalTransport.BR_EDR
|
|
3765
3778
|
and error.peer_address == peer_address
|
|
3766
3779
|
):
|
|
3767
3780
|
pending_connection.set_exception(error)
|
|
@@ -3769,12 +3782,12 @@ class Device(CompositeEventEmitter):
|
|
|
3769
3782
|
self.on('connection', on_connection)
|
|
3770
3783
|
self.on('connection_failure', on_connection_failure)
|
|
3771
3784
|
|
|
3772
|
-
# Save pending connection, with the Peripheral role.
|
|
3785
|
+
# Save pending connection, with the Peripheral hci.role.
|
|
3773
3786
|
# Even if we requested a role switch in the hci.HCI_Accept_Connection_Request
|
|
3774
3787
|
# command, this connection is still considered Peripheral until an eventual
|
|
3775
3788
|
# role change event.
|
|
3776
3789
|
self.pending_connections[peer_address] = Connection.incomplete(
|
|
3777
|
-
self, peer_address,
|
|
3790
|
+
self, peer_address, hci.Role.PERIPHERAL
|
|
3778
3791
|
)
|
|
3779
3792
|
|
|
3780
3793
|
try:
|
|
@@ -3786,7 +3799,7 @@ class Device(CompositeEventEmitter):
|
|
|
3786
3799
|
)
|
|
3787
3800
|
|
|
3788
3801
|
# Wait for connection complete
|
|
3789
|
-
return await
|
|
3802
|
+
return await utils.cancel_on_event(self, 'flush', pending_connection)
|
|
3790
3803
|
|
|
3791
3804
|
finally:
|
|
3792
3805
|
self.remove_listener('connection', on_connection)
|
|
@@ -3831,7 +3844,7 @@ class Device(CompositeEventEmitter):
|
|
|
3831
3844
|
# If the address is not parsable, assume it is a name instead
|
|
3832
3845
|
logger.debug('looking for peer by name')
|
|
3833
3846
|
peer_address = await self.find_peer_by_name(
|
|
3834
|
-
peer_address,
|
|
3847
|
+
peer_address, PhysicalTransport.BR_EDR
|
|
3835
3848
|
) # TODO: timeout
|
|
3836
3849
|
|
|
3837
3850
|
await self.send_command(
|
|
@@ -3860,7 +3873,7 @@ class Device(CompositeEventEmitter):
|
|
|
3860
3873
|
|
|
3861
3874
|
# Wait for the disconnection process to complete
|
|
3862
3875
|
self.disconnecting = True
|
|
3863
|
-
return await
|
|
3876
|
+
return await utils.cancel_on_event(self, 'flush', pending_disconnection)
|
|
3864
3877
|
finally:
|
|
3865
3878
|
connection.remove_listener(
|
|
3866
3879
|
'disconnection', pending_disconnection.set_result
|
|
@@ -3903,7 +3916,7 @@ class Device(CompositeEventEmitter):
|
|
|
3903
3916
|
'''
|
|
3904
3917
|
|
|
3905
3918
|
if use_l2cap:
|
|
3906
|
-
if connection.role !=
|
|
3919
|
+
if connection.role != hci.Role.PERIPHERAL:
|
|
3907
3920
|
raise InvalidStateError(
|
|
3908
3921
|
'only peripheral can update connection parameters with l2cap'
|
|
3909
3922
|
)
|
|
@@ -4002,7 +4015,19 @@ class Device(CompositeEventEmitter):
|
|
|
4002
4015
|
check_result=True,
|
|
4003
4016
|
)
|
|
4004
4017
|
|
|
4005
|
-
async def
|
|
4018
|
+
async def transfer_periodic_set_info(
|
|
4019
|
+
self, connection: Connection, advertising_handle: int, service_data: int = 0
|
|
4020
|
+
) -> None:
|
|
4021
|
+
return await self.send_command(
|
|
4022
|
+
hci.HCI_LE_Periodic_Advertising_Set_Info_Transfer_Command(
|
|
4023
|
+
connection_handle=connection.handle,
|
|
4024
|
+
service_data=service_data,
|
|
4025
|
+
advertising_handle=advertising_handle,
|
|
4026
|
+
),
|
|
4027
|
+
check_result=True,
|
|
4028
|
+
)
|
|
4029
|
+
|
|
4030
|
+
async def find_peer_by_name(self, name, transport=PhysicalTransport.LE):
|
|
4006
4031
|
"""
|
|
4007
4032
|
Scan for a peer with a given name and return its address.
|
|
4008
4033
|
"""
|
|
@@ -4021,7 +4046,7 @@ class Device(CompositeEventEmitter):
|
|
|
4021
4046
|
was_scanning = self.scanning
|
|
4022
4047
|
was_discovering = self.discovering
|
|
4023
4048
|
try:
|
|
4024
|
-
if transport ==
|
|
4049
|
+
if transport == PhysicalTransport.LE:
|
|
4025
4050
|
event_name = 'advertisement'
|
|
4026
4051
|
listener = self.on(
|
|
4027
4052
|
event_name,
|
|
@@ -4033,7 +4058,7 @@ class Device(CompositeEventEmitter):
|
|
|
4033
4058
|
if not self.scanning:
|
|
4034
4059
|
await self.start_scanning(filter_duplicates=True)
|
|
4035
4060
|
|
|
4036
|
-
elif transport ==
|
|
4061
|
+
elif transport == PhysicalTransport.BR_EDR:
|
|
4037
4062
|
event_name = 'inquiry_result'
|
|
4038
4063
|
listener = self.on(
|
|
4039
4064
|
event_name,
|
|
@@ -4047,14 +4072,14 @@ class Device(CompositeEventEmitter):
|
|
|
4047
4072
|
else:
|
|
4048
4073
|
return None
|
|
4049
4074
|
|
|
4050
|
-
return await
|
|
4075
|
+
return await utils.cancel_on_event(self, 'flush', peer_address)
|
|
4051
4076
|
finally:
|
|
4052
4077
|
if listener is not None:
|
|
4053
4078
|
self.remove_listener(event_name, listener)
|
|
4054
4079
|
|
|
4055
|
-
if transport ==
|
|
4080
|
+
if transport == PhysicalTransport.LE and not was_scanning:
|
|
4056
4081
|
await self.stop_scanning()
|
|
4057
|
-
elif transport ==
|
|
4082
|
+
elif transport == PhysicalTransport.BR_EDR and not was_discovering:
|
|
4058
4083
|
await self.stop_discovery()
|
|
4059
4084
|
|
|
4060
4085
|
async def find_peer_by_identity_address(
|
|
@@ -4097,7 +4122,7 @@ class Device(CompositeEventEmitter):
|
|
|
4097
4122
|
if not self.scanning:
|
|
4098
4123
|
await self.start_scanning(filter_duplicates=True)
|
|
4099
4124
|
|
|
4100
|
-
return await
|
|
4125
|
+
return await utils.cancel_on_event(self, 'flush', peer_address)
|
|
4101
4126
|
finally:
|
|
4102
4127
|
if listener is not None:
|
|
4103
4128
|
self.remove_listener(event_name, listener)
|
|
@@ -4148,10 +4173,10 @@ class Device(CompositeEventEmitter):
|
|
|
4148
4173
|
if keys.ltk:
|
|
4149
4174
|
return keys.ltk.value
|
|
4150
4175
|
|
|
4151
|
-
if connection.role ==
|
|
4176
|
+
if connection.role == hci.Role.CENTRAL and keys.ltk_central:
|
|
4152
4177
|
return keys.ltk_central.value
|
|
4153
4178
|
|
|
4154
|
-
if connection.role ==
|
|
4179
|
+
if connection.role == hci.Role.PERIPHERAL and keys.ltk_peripheral:
|
|
4155
4180
|
return keys.ltk_peripheral.value
|
|
4156
4181
|
return None
|
|
4157
4182
|
|
|
@@ -4201,7 +4226,9 @@ class Device(CompositeEventEmitter):
|
|
|
4201
4226
|
raise hci.HCI_StatusError(result)
|
|
4202
4227
|
|
|
4203
4228
|
# Wait for the authentication to complete
|
|
4204
|
-
await
|
|
4229
|
+
await utils.cancel_on_event(
|
|
4230
|
+
connection, 'disconnection', pending_authentication
|
|
4231
|
+
)
|
|
4205
4232
|
finally:
|
|
4206
4233
|
connection.remove_listener('connection_authentication', on_authentication)
|
|
4207
4234
|
connection.remove_listener(
|
|
@@ -4209,7 +4236,7 @@ class Device(CompositeEventEmitter):
|
|
|
4209
4236
|
)
|
|
4210
4237
|
|
|
4211
4238
|
async def encrypt(self, connection, enable=True):
|
|
4212
|
-
if not enable and connection.transport ==
|
|
4239
|
+
if not enable and connection.transport == PhysicalTransport.LE:
|
|
4213
4240
|
raise InvalidArgumentError('`enable` parameter is classic only.')
|
|
4214
4241
|
|
|
4215
4242
|
# Set up event handlers
|
|
@@ -4226,7 +4253,7 @@ class Device(CompositeEventEmitter):
|
|
|
4226
4253
|
|
|
4227
4254
|
# Request the encryption
|
|
4228
4255
|
try:
|
|
4229
|
-
if connection.transport ==
|
|
4256
|
+
if connection.transport == PhysicalTransport.LE:
|
|
4230
4257
|
# Look for a key in the key store
|
|
4231
4258
|
if self.keystore is None:
|
|
4232
4259
|
raise InvalidOperationError('no key store')
|
|
@@ -4247,7 +4274,7 @@ class Device(CompositeEventEmitter):
|
|
|
4247
4274
|
else:
|
|
4248
4275
|
raise InvalidOperationError('no LTK found for peer')
|
|
4249
4276
|
|
|
4250
|
-
if connection.role != hci.
|
|
4277
|
+
if connection.role != hci.Role.CENTRAL:
|
|
4251
4278
|
raise InvalidStateError('only centrals can start encryption')
|
|
4252
4279
|
|
|
4253
4280
|
result = await self.send_command(
|
|
@@ -4281,7 +4308,7 @@ class Device(CompositeEventEmitter):
|
|
|
4281
4308
|
raise hci.HCI_StatusError(result)
|
|
4282
4309
|
|
|
4283
4310
|
# Wait for the result
|
|
4284
|
-
await
|
|
4311
|
+
await utils.cancel_on_event(connection, 'disconnection', pending_encryption)
|
|
4285
4312
|
finally:
|
|
4286
4313
|
connection.remove_listener(
|
|
4287
4314
|
'connection_encryption_change', on_encryption_change
|
|
@@ -4303,7 +4330,7 @@ class Device(CompositeEventEmitter):
|
|
|
4303
4330
|
self.emit('key_store_update')
|
|
4304
4331
|
|
|
4305
4332
|
# [Classic only]
|
|
4306
|
-
async def switch_role(self, connection: Connection, role:
|
|
4333
|
+
async def switch_role(self, connection: Connection, role: hci.Role):
|
|
4307
4334
|
pending_role_change = asyncio.get_running_loop().create_future()
|
|
4308
4335
|
|
|
4309
4336
|
def on_role_change(new_role):
|
|
@@ -4325,7 +4352,9 @@ class Device(CompositeEventEmitter):
|
|
|
4325
4352
|
f'{hci.HCI_Constant.error_name(result.status)}'
|
|
4326
4353
|
)
|
|
4327
4354
|
raise hci.HCI_StatusError(result)
|
|
4328
|
-
await
|
|
4355
|
+
await utils.cancel_on_event(
|
|
4356
|
+
connection, 'disconnection', pending_role_change
|
|
4357
|
+
)
|
|
4329
4358
|
finally:
|
|
4330
4359
|
connection.remove_listener('role_change', on_role_change)
|
|
4331
4360
|
connection.remove_listener('role_change_failure', on_role_change_failure)
|
|
@@ -4374,13 +4403,13 @@ class Device(CompositeEventEmitter):
|
|
|
4374
4403
|
raise hci.HCI_StatusError(result)
|
|
4375
4404
|
|
|
4376
4405
|
# Wait for the result
|
|
4377
|
-
return await
|
|
4406
|
+
return await utils.cancel_on_event(self, 'flush', pending_name)
|
|
4378
4407
|
finally:
|
|
4379
4408
|
self.remove_listener('remote_name', handler)
|
|
4380
4409
|
self.remove_listener('remote_name_failure', failure_handler)
|
|
4381
4410
|
|
|
4382
4411
|
# [LE only]
|
|
4383
|
-
@experimental('Only for testing.')
|
|
4412
|
+
@utils.experimental('Only for testing.')
|
|
4384
4413
|
async def setup_cig(
|
|
4385
4414
|
self,
|
|
4386
4415
|
cig_id: int,
|
|
@@ -4438,7 +4467,7 @@ class Device(CompositeEventEmitter):
|
|
|
4438
4467
|
return cis_handles
|
|
4439
4468
|
|
|
4440
4469
|
# [LE only]
|
|
4441
|
-
@experimental('Only for testing.')
|
|
4470
|
+
@utils.experimental('Only for testing.')
|
|
4442
4471
|
async def create_cis(
|
|
4443
4472
|
self, cis_acl_pairs: Sequence[tuple[int, int]]
|
|
4444
4473
|
) -> list[CisLink]:
|
|
@@ -4454,7 +4483,7 @@ class Device(CompositeEventEmitter):
|
|
|
4454
4483
|
cig_id=cig_id,
|
|
4455
4484
|
)
|
|
4456
4485
|
|
|
4457
|
-
with closing(EventWatcher()) as watcher:
|
|
4486
|
+
with closing(utils.EventWatcher()) as watcher:
|
|
4458
4487
|
pending_cis_establishments = {
|
|
4459
4488
|
cis_handle: asyncio.get_running_loop().create_future()
|
|
4460
4489
|
for cis_handle, _ in cis_acl_pairs
|
|
@@ -4481,7 +4510,7 @@ class Device(CompositeEventEmitter):
|
|
|
4481
4510
|
return await asyncio.gather(*pending_cis_establishments.values())
|
|
4482
4511
|
|
|
4483
4512
|
# [LE only]
|
|
4484
|
-
@experimental('Only for testing.')
|
|
4513
|
+
@utils.experimental('Only for testing.')
|
|
4485
4514
|
async def accept_cis_request(self, handle: int) -> CisLink:
|
|
4486
4515
|
"""[LE Only] Accepts an incoming CIS request.
|
|
4487
4516
|
|
|
@@ -4503,7 +4532,7 @@ class Device(CompositeEventEmitter):
|
|
|
4503
4532
|
if cis_link.state == CisLink.State.ESTABLISHED:
|
|
4504
4533
|
return cis_link
|
|
4505
4534
|
|
|
4506
|
-
with closing(EventWatcher()) as watcher:
|
|
4535
|
+
with closing(utils.EventWatcher()) as watcher:
|
|
4507
4536
|
pending_establishment = asyncio.get_running_loop().create_future()
|
|
4508
4537
|
|
|
4509
4538
|
def on_establishment() -> None:
|
|
@@ -4527,7 +4556,7 @@ class Device(CompositeEventEmitter):
|
|
|
4527
4556
|
raise UnreachableError()
|
|
4528
4557
|
|
|
4529
4558
|
# [LE only]
|
|
4530
|
-
@experimental('Only for testing.')
|
|
4559
|
+
@utils.experimental('Only for testing.')
|
|
4531
4560
|
async def reject_cis_request(
|
|
4532
4561
|
self,
|
|
4533
4562
|
handle: int,
|
|
@@ -4541,14 +4570,14 @@ class Device(CompositeEventEmitter):
|
|
|
4541
4570
|
)
|
|
4542
4571
|
|
|
4543
4572
|
# [LE only]
|
|
4544
|
-
@experimental('Only for testing.')
|
|
4573
|
+
@utils.experimental('Only for testing.')
|
|
4545
4574
|
async def create_big(
|
|
4546
4575
|
self, advertising_set: AdvertisingSet, parameters: BigParameters
|
|
4547
4576
|
) -> Big:
|
|
4548
4577
|
if (big_handle := self.next_big_handle()) is None:
|
|
4549
4578
|
raise core.OutOfResourcesError("All valid BIG handles already in use")
|
|
4550
4579
|
|
|
4551
|
-
with closing(EventWatcher()) as watcher:
|
|
4580
|
+
with closing(utils.EventWatcher()) as watcher:
|
|
4552
4581
|
big = Big(
|
|
4553
4582
|
big_handle=big_handle,
|
|
4554
4583
|
parameters=parameters,
|
|
@@ -4591,7 +4620,7 @@ class Device(CompositeEventEmitter):
|
|
|
4591
4620
|
return big
|
|
4592
4621
|
|
|
4593
4622
|
# [LE only]
|
|
4594
|
-
@experimental('Only for testing.')
|
|
4623
|
+
@utils.experimental('Only for testing.')
|
|
4595
4624
|
async def create_big_sync(
|
|
4596
4625
|
self, pa_sync: PeriodicAdvertisingSync, parameters: BigSyncParameters
|
|
4597
4626
|
) -> BigSync:
|
|
@@ -4601,7 +4630,7 @@ class Device(CompositeEventEmitter):
|
|
|
4601
4630
|
if (pa_sync_handle := pa_sync.sync_handle) is None:
|
|
4602
4631
|
raise core.InvalidStateError("PA Sync is not established")
|
|
4603
4632
|
|
|
4604
|
-
with closing(EventWatcher()) as watcher:
|
|
4633
|
+
with closing(utils.EventWatcher()) as watcher:
|
|
4605
4634
|
big_sync = BigSync(
|
|
4606
4635
|
big_handle=big_handle,
|
|
4607
4636
|
parameters=parameters,
|
|
@@ -4649,7 +4678,7 @@ class Device(CompositeEventEmitter):
|
|
|
4649
4678
|
Returns:
|
|
4650
4679
|
LE features supported by the remote device.
|
|
4651
4680
|
"""
|
|
4652
|
-
with closing(EventWatcher()) as watcher:
|
|
4681
|
+
with closing(utils.EventWatcher()) as watcher:
|
|
4653
4682
|
read_feature_future: asyncio.Future[hci.LeFeatureMask] = (
|
|
4654
4683
|
asyncio.get_running_loop().create_future()
|
|
4655
4684
|
)
|
|
@@ -4672,7 +4701,7 @@ class Device(CompositeEventEmitter):
|
|
|
4672
4701
|
)
|
|
4673
4702
|
return await read_feature_future
|
|
4674
4703
|
|
|
4675
|
-
@experimental('Only for testing.')
|
|
4704
|
+
@utils.experimental('Only for testing.')
|
|
4676
4705
|
async def get_remote_cs_capabilities(
|
|
4677
4706
|
self, connection: Connection
|
|
4678
4707
|
) -> ChannelSoundingCapabilities:
|
|
@@ -4680,7 +4709,7 @@ class Device(CompositeEventEmitter):
|
|
|
4680
4709
|
asyncio.get_running_loop().create_future()
|
|
4681
4710
|
)
|
|
4682
4711
|
|
|
4683
|
-
with closing(EventWatcher()) as watcher:
|
|
4712
|
+
with closing(utils.EventWatcher()) as watcher:
|
|
4684
4713
|
watcher.once(
|
|
4685
4714
|
connection, 'channel_sounding_capabilities', complete_future.set_result
|
|
4686
4715
|
)
|
|
@@ -4697,7 +4726,7 @@ class Device(CompositeEventEmitter):
|
|
|
4697
4726
|
)
|
|
4698
4727
|
return await complete_future
|
|
4699
4728
|
|
|
4700
|
-
@experimental('Only for testing.')
|
|
4729
|
+
@utils.experimental('Only for testing.')
|
|
4701
4730
|
async def set_default_cs_settings(
|
|
4702
4731
|
self,
|
|
4703
4732
|
connection: Connection,
|
|
@@ -4717,7 +4746,7 @@ class Device(CompositeEventEmitter):
|
|
|
4717
4746
|
check_result=True,
|
|
4718
4747
|
)
|
|
4719
4748
|
|
|
4720
|
-
@experimental('Only for testing.')
|
|
4749
|
+
@utils.experimental('Only for testing.')
|
|
4721
4750
|
async def create_cs_config(
|
|
4722
4751
|
self,
|
|
4723
4752
|
connection: Connection,
|
|
@@ -4754,7 +4783,7 @@ class Device(CompositeEventEmitter):
|
|
|
4754
4783
|
if config_id is None:
|
|
4755
4784
|
raise OutOfResourcesError("No available config ID on this connection!")
|
|
4756
4785
|
|
|
4757
|
-
with closing(EventWatcher()) as watcher:
|
|
4786
|
+
with closing(utils.EventWatcher()) as watcher:
|
|
4758
4787
|
watcher.once(
|
|
4759
4788
|
connection, 'channel_sounding_config', complete_future.set_result
|
|
4760
4789
|
)
|
|
@@ -4788,12 +4817,12 @@ class Device(CompositeEventEmitter):
|
|
|
4788
4817
|
)
|
|
4789
4818
|
return await complete_future
|
|
4790
4819
|
|
|
4791
|
-
@experimental('Only for testing.')
|
|
4820
|
+
@utils.experimental('Only for testing.')
|
|
4792
4821
|
async def enable_cs_security(self, connection: Connection) -> None:
|
|
4793
4822
|
complete_future: asyncio.Future[None] = (
|
|
4794
4823
|
asyncio.get_running_loop().create_future()
|
|
4795
4824
|
)
|
|
4796
|
-
with closing(EventWatcher()) as watcher:
|
|
4825
|
+
with closing(utils.EventWatcher()) as watcher:
|
|
4797
4826
|
|
|
4798
4827
|
def on_event(event: hci.HCI_LE_CS_Security_Enable_Complete_Event) -> None:
|
|
4799
4828
|
if event.connection_handle != connection.handle:
|
|
@@ -4812,7 +4841,7 @@ class Device(CompositeEventEmitter):
|
|
|
4812
4841
|
)
|
|
4813
4842
|
return await complete_future
|
|
4814
4843
|
|
|
4815
|
-
@experimental('Only for testing.')
|
|
4844
|
+
@utils.experimental('Only for testing.')
|
|
4816
4845
|
async def set_cs_procedure_parameters(
|
|
4817
4846
|
self,
|
|
4818
4847
|
connection: Connection,
|
|
@@ -4850,7 +4879,7 @@ class Device(CompositeEventEmitter):
|
|
|
4850
4879
|
check_result=True,
|
|
4851
4880
|
)
|
|
4852
4881
|
|
|
4853
|
-
@experimental('Only for testing.')
|
|
4882
|
+
@utils.experimental('Only for testing.')
|
|
4854
4883
|
async def enable_cs_procedure(
|
|
4855
4884
|
self,
|
|
4856
4885
|
connection: Connection,
|
|
@@ -4860,7 +4889,7 @@ class Device(CompositeEventEmitter):
|
|
|
4860
4889
|
complete_future: asyncio.Future[ChannelSoundingProcedure] = (
|
|
4861
4890
|
asyncio.get_running_loop().create_future()
|
|
4862
4891
|
)
|
|
4863
|
-
with closing(EventWatcher()) as watcher:
|
|
4892
|
+
with closing(utils.EventWatcher()) as watcher:
|
|
4864
4893
|
watcher.once(
|
|
4865
4894
|
connection, 'channel_sounding_procedure', complete_future.set_result
|
|
4866
4895
|
)
|
|
@@ -4900,10 +4929,12 @@ class Device(CompositeEventEmitter):
|
|
|
4900
4929
|
value=link_key, authenticated=authenticated
|
|
4901
4930
|
)
|
|
4902
4931
|
|
|
4903
|
-
|
|
4932
|
+
utils.cancel_on_event(
|
|
4933
|
+
self, 'flush', self.update_keys(str(bd_addr), pairing_keys)
|
|
4934
|
+
)
|
|
4904
4935
|
|
|
4905
4936
|
if connection := self.find_connection_by_bd_addr(
|
|
4906
|
-
bd_addr, transport=
|
|
4937
|
+
bd_addr, transport=PhysicalTransport.BR_EDR
|
|
4907
4938
|
):
|
|
4908
4939
|
connection.link_key_type = key_type
|
|
4909
4940
|
|
|
@@ -5169,7 +5200,7 @@ class Device(CompositeEventEmitter):
|
|
|
5169
5200
|
if advertising_set.auto_restart:
|
|
5170
5201
|
connection.once(
|
|
5171
5202
|
'disconnection',
|
|
5172
|
-
lambda _:
|
|
5203
|
+
lambda _: utils.cancel_on_event(self, 'flush', advertising_set.start()),
|
|
5173
5204
|
)
|
|
5174
5205
|
|
|
5175
5206
|
self.emit('connection', connection)
|
|
@@ -5178,11 +5209,11 @@ class Device(CompositeEventEmitter):
|
|
|
5178
5209
|
def on_connection(
|
|
5179
5210
|
self,
|
|
5180
5211
|
connection_handle: int,
|
|
5181
|
-
transport:
|
|
5212
|
+
transport: core.PhysicalTransport,
|
|
5182
5213
|
peer_address: hci.Address,
|
|
5183
5214
|
self_resolvable_address: Optional[hci.Address],
|
|
5184
5215
|
peer_resolvable_address: Optional[hci.Address],
|
|
5185
|
-
role:
|
|
5216
|
+
role: hci.Role,
|
|
5186
5217
|
connection_parameters: ConnectionParameters,
|
|
5187
5218
|
) -> None:
|
|
5188
5219
|
# Convert all-zeros addresses into None.
|
|
@@ -5203,7 +5234,7 @@ class Device(CompositeEventEmitter):
|
|
|
5203
5234
|
'new connection reuses the same handle as a previous connection'
|
|
5204
5235
|
)
|
|
5205
5236
|
|
|
5206
|
-
if transport ==
|
|
5237
|
+
if transport == PhysicalTransport.BR_EDR:
|
|
5207
5238
|
# Create a new connection
|
|
5208
5239
|
connection = self.pending_connections.pop(peer_address)
|
|
5209
5240
|
connection.complete(connection_handle, connection_parameters)
|
|
@@ -5225,8 +5256,8 @@ class Device(CompositeEventEmitter):
|
|
|
5225
5256
|
peer_address = resolved_address
|
|
5226
5257
|
|
|
5227
5258
|
self_address = None
|
|
5228
|
-
own_address_type: Optional[
|
|
5229
|
-
if role == hci.
|
|
5259
|
+
own_address_type: Optional[hci.OwnAddressType] = None
|
|
5260
|
+
if role == hci.Role.CENTRAL:
|
|
5230
5261
|
own_address_type = self.connect_own_address_type
|
|
5231
5262
|
assert own_address_type is not None
|
|
5232
5263
|
else:
|
|
@@ -5273,22 +5304,22 @@ class Device(CompositeEventEmitter):
|
|
|
5273
5304
|
)
|
|
5274
5305
|
self.connections[connection_handle] = connection
|
|
5275
5306
|
|
|
5276
|
-
if role == hci.
|
|
5307
|
+
if role == hci.Role.PERIPHERAL and self.legacy_advertiser:
|
|
5277
5308
|
if self.legacy_advertiser.auto_restart:
|
|
5278
5309
|
advertiser = self.legacy_advertiser
|
|
5279
5310
|
connection.once(
|
|
5280
5311
|
'disconnection',
|
|
5281
|
-
lambda _:
|
|
5312
|
+
lambda _: utils.cancel_on_event(self, 'flush', advertiser.start()),
|
|
5282
5313
|
)
|
|
5283
5314
|
else:
|
|
5284
5315
|
self.legacy_advertiser = None
|
|
5285
5316
|
|
|
5286
|
-
if role == hci.
|
|
5317
|
+
if role == hci.Role.CENTRAL or not self.supports_le_extended_advertising:
|
|
5287
5318
|
# We can emit now, we have all the info we need
|
|
5288
5319
|
self.emit('connection', connection)
|
|
5289
5320
|
return
|
|
5290
5321
|
|
|
5291
|
-
if role == hci.
|
|
5322
|
+
if role == hci.Role.PERIPHERAL and self.supports_le_extended_advertising:
|
|
5292
5323
|
if advertising_set := self.connecting_extended_advertising_sets.pop(
|
|
5293
5324
|
connection_handle, None
|
|
5294
5325
|
):
|
|
@@ -5305,7 +5336,7 @@ class Device(CompositeEventEmitter):
|
|
|
5305
5336
|
|
|
5306
5337
|
# For directed advertising, this means a timeout
|
|
5307
5338
|
if (
|
|
5308
|
-
transport ==
|
|
5339
|
+
transport == PhysicalTransport.LE
|
|
5309
5340
|
and self.legacy_advertiser
|
|
5310
5341
|
and self.legacy_advertiser.advertising_type.is_directed
|
|
5311
5342
|
):
|
|
@@ -5332,7 +5363,7 @@ class Device(CompositeEventEmitter):
|
|
|
5332
5363
|
hci.HCI_Connection_Complete_Event.ESCO_LINK_TYPE,
|
|
5333
5364
|
):
|
|
5334
5365
|
if connection := self.find_connection_by_bd_addr(
|
|
5335
|
-
bd_addr, transport=
|
|
5366
|
+
bd_addr, transport=PhysicalTransport.BR_EDR
|
|
5336
5367
|
):
|
|
5337
5368
|
self.emit('sco_request', connection, link_type)
|
|
5338
5369
|
else:
|
|
@@ -5353,7 +5384,7 @@ class Device(CompositeEventEmitter):
|
|
|
5353
5384
|
elif self.classic_accept_any:
|
|
5354
5385
|
# Save pending connection
|
|
5355
5386
|
self.pending_connections[bd_addr] = Connection.incomplete(
|
|
5356
|
-
self, bd_addr,
|
|
5387
|
+
self, bd_addr, hci.Role.PERIPHERAL
|
|
5357
5388
|
)
|
|
5358
5389
|
|
|
5359
5390
|
self.host.send_command_sync(
|
|
@@ -5405,7 +5436,7 @@ class Device(CompositeEventEmitter):
|
|
|
5405
5436
|
connection.emit('disconnection_failure', error)
|
|
5406
5437
|
|
|
5407
5438
|
@host_event_handler
|
|
5408
|
-
@AsyncRunner.run_in_task()
|
|
5439
|
+
@utils.AsyncRunner.run_in_task()
|
|
5409
5440
|
async def on_inquiry_complete(self):
|
|
5410
5441
|
if self.auto_restart_inquiry:
|
|
5411
5442
|
# Inquire again
|
|
@@ -5537,7 +5568,7 @@ class Device(CompositeEventEmitter):
|
|
|
5537
5568
|
|
|
5538
5569
|
async def reply() -> None:
|
|
5539
5570
|
try:
|
|
5540
|
-
if await
|
|
5571
|
+
if await utils.cancel_on_event(connection, 'disconnection', method()):
|
|
5541
5572
|
await self.host.send_command(
|
|
5542
5573
|
hci.HCI_User_Confirmation_Request_Reply_Command(
|
|
5543
5574
|
bd_addr=connection.peer_address
|
|
@@ -5553,7 +5584,7 @@ class Device(CompositeEventEmitter):
|
|
|
5553
5584
|
)
|
|
5554
5585
|
)
|
|
5555
5586
|
|
|
5556
|
-
AsyncRunner.spawn(reply())
|
|
5587
|
+
utils.AsyncRunner.spawn(reply())
|
|
5557
5588
|
|
|
5558
5589
|
# [Classic only]
|
|
5559
5590
|
@host_event_handler
|
|
@@ -5564,8 +5595,8 @@ class Device(CompositeEventEmitter):
|
|
|
5564
5595
|
|
|
5565
5596
|
async def reply() -> None:
|
|
5566
5597
|
try:
|
|
5567
|
-
number = await
|
|
5568
|
-
'disconnection', pairing_config.delegate.get_number()
|
|
5598
|
+
number = await utils.cancel_on_event(
|
|
5599
|
+
connection, 'disconnection', pairing_config.delegate.get_number()
|
|
5569
5600
|
)
|
|
5570
5601
|
if number is not None:
|
|
5571
5602
|
await self.host.send_command(
|
|
@@ -5583,7 +5614,7 @@ class Device(CompositeEventEmitter):
|
|
|
5583
5614
|
)
|
|
5584
5615
|
)
|
|
5585
5616
|
|
|
5586
|
-
AsyncRunner.spawn(reply())
|
|
5617
|
+
utils.AsyncRunner.spawn(reply())
|
|
5587
5618
|
|
|
5588
5619
|
# [Classic only]
|
|
5589
5620
|
@host_event_handler
|
|
@@ -5598,8 +5629,8 @@ class Device(CompositeEventEmitter):
|
|
|
5598
5629
|
if io_capability == hci.HCI_KEYBOARD_ONLY_IO_CAPABILITY:
|
|
5599
5630
|
# Ask the user to enter a string
|
|
5600
5631
|
async def get_pin_code():
|
|
5601
|
-
pin_code = await
|
|
5602
|
-
'disconnection', pairing_config.delegate.get_string(16)
|
|
5632
|
+
pin_code = await utils.cancel_on_event(
|
|
5633
|
+
connection, 'disconnection', pairing_config.delegate.get_string(16)
|
|
5603
5634
|
)
|
|
5604
5635
|
|
|
5605
5636
|
if pin_code is not None:
|
|
@@ -5637,8 +5668,8 @@ class Device(CompositeEventEmitter):
|
|
|
5637
5668
|
pairing_config = self.pairing_config_factory(connection)
|
|
5638
5669
|
|
|
5639
5670
|
# Show the passkey to the user
|
|
5640
|
-
|
|
5641
|
-
'disconnection', pairing_config.delegate.display_number(passkey)
|
|
5671
|
+
utils.cancel_on_event(
|
|
5672
|
+
connection, 'disconnection', pairing_config.delegate.display_number(passkey)
|
|
5642
5673
|
)
|
|
5643
5674
|
|
|
5644
5675
|
# [Classic only]
|
|
@@ -5670,7 +5701,7 @@ class Device(CompositeEventEmitter):
|
|
|
5670
5701
|
# [Classic only]
|
|
5671
5702
|
@host_event_handler
|
|
5672
5703
|
@with_connection_from_address
|
|
5673
|
-
@experimental('Only for testing.')
|
|
5704
|
+
@utils.experimental('Only for testing.')
|
|
5674
5705
|
def on_sco_connection(
|
|
5675
5706
|
self, acl_connection: Connection, sco_handle: int, link_type: int
|
|
5676
5707
|
) -> None:
|
|
@@ -5690,7 +5721,7 @@ class Device(CompositeEventEmitter):
|
|
|
5690
5721
|
# [Classic only]
|
|
5691
5722
|
@host_event_handler
|
|
5692
5723
|
@with_connection_from_address
|
|
5693
|
-
@experimental('Only for testing.')
|
|
5724
|
+
@utils.experimental('Only for testing.')
|
|
5694
5725
|
def on_sco_connection_failure(
|
|
5695
5726
|
self, acl_connection: Connection, status: int
|
|
5696
5727
|
) -> None:
|
|
@@ -5699,7 +5730,7 @@ class Device(CompositeEventEmitter):
|
|
|
5699
5730
|
|
|
5700
5731
|
# [Classic only]
|
|
5701
5732
|
@host_event_handler
|
|
5702
|
-
@experimental('Only for testing')
|
|
5733
|
+
@utils.experimental('Only for testing')
|
|
5703
5734
|
def on_sco_packet(
|
|
5704
5735
|
self, sco_handle: int, packet: hci.HCI_SynchronousDataPacket
|
|
5705
5736
|
) -> None:
|
|
@@ -5709,7 +5740,7 @@ class Device(CompositeEventEmitter):
|
|
|
5709
5740
|
# [LE only]
|
|
5710
5741
|
@host_event_handler
|
|
5711
5742
|
@with_connection_from_handle
|
|
5712
|
-
@experimental('Only for testing')
|
|
5743
|
+
@utils.experimental('Only for testing')
|
|
5713
5744
|
def on_cis_request(
|
|
5714
5745
|
self,
|
|
5715
5746
|
acl_connection: Connection,
|
|
@@ -5736,7 +5767,7 @@ class Device(CompositeEventEmitter):
|
|
|
5736
5767
|
|
|
5737
5768
|
# [LE only]
|
|
5738
5769
|
@host_event_handler
|
|
5739
|
-
@experimental('Only for testing')
|
|
5770
|
+
@utils.experimental('Only for testing')
|
|
5740
5771
|
def on_cis_establishment(self, cis_handle: int) -> None:
|
|
5741
5772
|
cis_link = self.cis_links[cis_handle]
|
|
5742
5773
|
cis_link.state = CisLink.State.ESTABLISHED
|
|
@@ -5756,7 +5787,7 @@ class Device(CompositeEventEmitter):
|
|
|
5756
5787
|
|
|
5757
5788
|
# [LE only]
|
|
5758
5789
|
@host_event_handler
|
|
5759
|
-
@experimental('Only for testing')
|
|
5790
|
+
@utils.experimental('Only for testing')
|
|
5760
5791
|
def on_cis_establishment_failure(self, cis_handle: int, status: int) -> None:
|
|
5761
5792
|
logger.debug(f'*** CIS Establishment Failure: cis=[0x{cis_handle:04X}] ***')
|
|
5762
5793
|
if cis_link := self.cis_links.pop(cis_handle):
|
|
@@ -5765,7 +5796,7 @@ class Device(CompositeEventEmitter):
|
|
|
5765
5796
|
|
|
5766
5797
|
# [LE only]
|
|
5767
5798
|
@host_event_handler
|
|
5768
|
-
@experimental('Only for testing')
|
|
5799
|
+
@utils.experimental('Only for testing')
|
|
5769
5800
|
def on_iso_packet(self, handle: int, packet: hci.HCI_IsoDataPacket) -> None:
|
|
5770
5801
|
if (cis_link := self.cis_links.get(handle)) and cis_link.sink:
|
|
5771
5802
|
cis_link.sink(packet)
|
|
@@ -5783,14 +5814,14 @@ class Device(CompositeEventEmitter):
|
|
|
5783
5814
|
connection.encryption = encryption
|
|
5784
5815
|
if (
|
|
5785
5816
|
not connection.authenticated
|
|
5786
|
-
and connection.transport ==
|
|
5817
|
+
and connection.transport == PhysicalTransport.BR_EDR
|
|
5787
5818
|
and encryption == hci.HCI_Encryption_Change_Event.AES_CCM
|
|
5788
5819
|
):
|
|
5789
5820
|
connection.authenticated = True
|
|
5790
5821
|
connection.sc = True
|
|
5791
5822
|
if (
|
|
5792
5823
|
not connection.authenticated
|
|
5793
|
-
and connection.transport ==
|
|
5824
|
+
and connection.transport == PhysicalTransport.LE
|
|
5794
5825
|
and encryption == hci.HCI_Encryption_Change_Event.E0_OR_AES_CCM
|
|
5795
5826
|
):
|
|
5796
5827
|
connection.authenticated = True
|