bumble 0.0.214__py3-none-any.whl → 0.0.216__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 +16 -3
- bumble/a2dp.py +15 -16
- bumble/apps/auracast.py +13 -38
- bumble/apps/bench.py +9 -10
- bumble/apps/ble_rpa_tool.py +1 -0
- bumble/apps/console.py +22 -25
- bumble/apps/controller_info.py +19 -19
- bumble/apps/controller_loopback.py +2 -2
- bumble/apps/controllers.py +1 -1
- bumble/apps/device_info.py +3 -3
- bumble/apps/gatt_dump.py +1 -1
- bumble/apps/gg_bridge.py +5 -6
- bumble/apps/hci_bridge.py +3 -3
- bumble/apps/l2cap_bridge.py +3 -3
- bumble/apps/lea_unicast/app.py +15 -25
- bumble/apps/pair.py +30 -43
- bumble/apps/pandora_server.py +5 -4
- bumble/apps/player/player.py +19 -22
- bumble/apps/rfcomm_bridge.py +3 -8
- bumble/apps/scan.py +16 -6
- bumble/apps/show.py +3 -4
- bumble/apps/speaker/speaker.py +22 -22
- bumble/apps/unbond.py +2 -1
- bumble/apps/usb_probe.py +1 -2
- bumble/att.py +241 -246
- bumble/audio/io.py +5 -9
- bumble/avc.py +2 -2
- bumble/avctp.py +6 -7
- bumble/avdtp.py +19 -22
- bumble/avrcp.py +1096 -588
- bumble/codecs.py +2 -0
- bumble/controller.py +52 -13
- bumble/core.py +567 -248
- bumble/crypto/__init__.py +2 -2
- bumble/crypto/builtin.py +1 -1
- bumble/crypto/cryptography.py +2 -4
- bumble/data_types.py +1025 -0
- bumble/device.py +318 -279
- bumble/drivers/__init__.py +3 -2
- bumble/drivers/intel.py +3 -4
- bumble/drivers/rtk.py +26 -9
- bumble/gap.py +4 -4
- bumble/gatt.py +3 -2
- bumble/gatt_adapters.py +3 -11
- bumble/gatt_client.py +69 -81
- bumble/gatt_server.py +124 -124
- bumble/hci.py +67 -18
- bumble/helpers.py +19 -26
- bumble/hfp.py +10 -21
- bumble/hid.py +22 -16
- bumble/host.py +181 -103
- bumble/keys.py +5 -3
- bumble/l2cap.py +121 -74
- bumble/link.py +8 -9
- bumble/pairing.py +7 -6
- bumble/pandora/__init__.py +8 -7
- bumble/pandora/config.py +3 -1
- bumble/pandora/device.py +3 -2
- bumble/pandora/host.py +38 -36
- bumble/pandora/l2cap.py +22 -21
- bumble/pandora/security.py +15 -15
- bumble/pandora/utils.py +5 -3
- bumble/profiles/aics.py +11 -11
- bumble/profiles/ams.py +7 -8
- bumble/profiles/ancs.py +6 -7
- bumble/profiles/ascs.py +4 -9
- bumble/profiles/asha.py +8 -12
- bumble/profiles/bap.py +11 -23
- bumble/profiles/bass.py +2 -7
- bumble/profiles/battery_service.py +3 -4
- bumble/profiles/cap.py +1 -2
- bumble/profiles/csip.py +2 -6
- bumble/profiles/device_information_service.py +2 -2
- bumble/profiles/gap.py +4 -4
- bumble/profiles/gatt_service.py +1 -4
- bumble/profiles/gmap.py +5 -5
- bumble/profiles/hap.py +62 -59
- bumble/profiles/heart_rate_service.py +5 -4
- bumble/profiles/le_audio.py +3 -1
- bumble/profiles/mcp.py +3 -7
- bumble/profiles/pacs.py +3 -6
- bumble/profiles/pbp.py +2 -0
- bumble/profiles/tmap.py +2 -3
- bumble/profiles/vcs.py +2 -8
- bumble/profiles/vocs.py +8 -8
- bumble/rfcomm.py +11 -14
- bumble/rtp.py +1 -0
- bumble/sdp.py +10 -8
- bumble/smp.py +142 -153
- bumble/snoop.py +5 -5
- bumble/tools/generate_company_id_list.py +1 -0
- bumble/tools/intel_fw_download.py +3 -3
- bumble/tools/intel_util.py +4 -4
- bumble/tools/rtk_fw_download.py +6 -3
- bumble/tools/rtk_util.py +24 -7
- bumble/transport/__init__.py +19 -15
- bumble/transport/android_emulator.py +8 -13
- bumble/transport/android_netsim.py +19 -18
- bumble/transport/common.py +12 -15
- bumble/transport/file.py +1 -1
- bumble/transport/hci_socket.py +4 -6
- bumble/transport/pty.py +5 -6
- bumble/transport/pyusb.py +7 -10
- bumble/transport/serial.py +2 -1
- bumble/transport/tcp_client.py +2 -2
- bumble/transport/tcp_server.py +11 -14
- bumble/transport/udp.py +3 -3
- bumble/transport/unix.py +67 -1
- bumble/transport/usb.py +6 -6
- bumble/transport/vhci.py +0 -1
- bumble/transport/ws_client.py +2 -1
- bumble/transport/ws_server.py +3 -2
- bumble/utils.py +20 -5
- bumble/vendor/android/hci.py +1 -2
- bumble/vendor/zephyr/hci.py +0 -1
- {bumble-0.0.214.dist-info → bumble-0.0.216.dist-info}/METADATA +2 -1
- bumble-0.0.216.dist-info/RECORD +183 -0
- bumble-0.0.214.dist-info/RECORD +0 -182
- {bumble-0.0.214.dist-info → bumble-0.0.216.dist-info}/WHEEL +0 -0
- {bumble-0.0.214.dist-info → bumble-0.0.216.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.214.dist-info → bumble-0.0.216.dist-info}/licenses/LICENSE +0 -0
- {bumble-0.0.214.dist-info → bumble-0.0.216.dist-info}/top_level.txt +0 -0
bumble/device.py
CHANGED
|
@@ -16,24 +16,22 @@
|
|
|
16
16
|
# Imports
|
|
17
17
|
# -----------------------------------------------------------------------------
|
|
18
18
|
from __future__ import annotations
|
|
19
|
+
|
|
19
20
|
import asyncio
|
|
20
21
|
import collections
|
|
21
|
-
from collections.abc import Iterable, Sequence
|
|
22
|
-
from contextlib import (
|
|
23
|
-
asynccontextmanager,
|
|
24
|
-
AsyncExitStack,
|
|
25
|
-
closing,
|
|
26
|
-
)
|
|
27
22
|
import copy
|
|
28
|
-
from dataclasses import dataclass, field
|
|
29
|
-
from enum import Enum, IntEnum
|
|
30
23
|
import functools
|
|
31
24
|
import itertools
|
|
32
25
|
import json
|
|
33
26
|
import logging
|
|
34
27
|
import secrets
|
|
35
28
|
import sys
|
|
29
|
+
from collections.abc import Iterable, Sequence
|
|
30
|
+
from contextlib import AsyncExitStack, asynccontextmanager, closing
|
|
31
|
+
from dataclasses import dataclass, field
|
|
32
|
+
from enum import Enum, IntEnum
|
|
36
33
|
from typing import (
|
|
34
|
+
TYPE_CHECKING,
|
|
37
35
|
Any,
|
|
38
36
|
Awaitable,
|
|
39
37
|
Callable,
|
|
@@ -43,47 +41,47 @@ from typing import (
|
|
|
43
41
|
Union,
|
|
44
42
|
cast,
|
|
45
43
|
overload,
|
|
46
|
-
TYPE_CHECKING,
|
|
47
44
|
)
|
|
48
|
-
from typing_extensions import Self
|
|
49
45
|
|
|
46
|
+
from typing_extensions import Self
|
|
50
47
|
|
|
51
|
-
from bumble
|
|
48
|
+
from bumble import (
|
|
49
|
+
core,
|
|
50
|
+
data_types,
|
|
51
|
+
gatt,
|
|
52
|
+
gatt_client,
|
|
53
|
+
gatt_server,
|
|
54
|
+
hci,
|
|
55
|
+
l2cap,
|
|
56
|
+
pairing,
|
|
57
|
+
sdp,
|
|
58
|
+
smp,
|
|
59
|
+
utils,
|
|
60
|
+
)
|
|
52
61
|
from bumble.att import ATT_CID, ATT_DEFAULT_MTU, ATT_PDU
|
|
53
|
-
from bumble.
|
|
54
|
-
from bumble.host import DataPacketQueue, Host
|
|
55
|
-
from bumble.profiles.gap import GenericAccessService
|
|
62
|
+
from bumble.colors import color
|
|
56
63
|
from bumble.core import (
|
|
57
|
-
PhysicalTransport,
|
|
58
64
|
AdvertisingData,
|
|
59
65
|
BaseBumbleError,
|
|
60
|
-
ConnectionParameterUpdateError,
|
|
61
66
|
CommandTimeoutError,
|
|
67
|
+
ConnectionParameterUpdateError,
|
|
62
68
|
ConnectionPHY,
|
|
63
69
|
InvalidArgumentError,
|
|
64
70
|
InvalidOperationError,
|
|
65
71
|
InvalidStateError,
|
|
66
72
|
NotSupportedError,
|
|
67
73
|
OutOfResourcesError,
|
|
74
|
+
PhysicalTransport,
|
|
68
75
|
UnreachableError,
|
|
69
76
|
)
|
|
70
|
-
from bumble import
|
|
71
|
-
from bumble.
|
|
72
|
-
|
|
73
|
-
PairingKeys,
|
|
74
|
-
)
|
|
75
|
-
from bumble import hci
|
|
76
|
-
from bumble import pairing
|
|
77
|
-
from bumble import gatt_client
|
|
78
|
-
from bumble import gatt_server
|
|
79
|
-
from bumble import smp
|
|
80
|
-
from bumble import sdp
|
|
81
|
-
from bumble import l2cap
|
|
82
|
-
from bumble import core
|
|
77
|
+
from bumble.gatt import Attribute, Characteristic, Descriptor, Service
|
|
78
|
+
from bumble.host import DataPacketQueue, Host
|
|
79
|
+
from bumble.keys import KeyStore, PairingKeys
|
|
83
80
|
from bumble.profiles import gatt_service
|
|
81
|
+
from bumble.profiles.gap import GenericAccessService
|
|
84
82
|
|
|
85
83
|
if TYPE_CHECKING:
|
|
86
|
-
from bumble.transport.common import
|
|
84
|
+
from bumble.transport.common import TransportSink, TransportSource
|
|
87
85
|
|
|
88
86
|
_T = TypeVar('_T')
|
|
89
87
|
|
|
@@ -267,7 +265,7 @@ class ExtendedAdvertisement(Advertisement):
|
|
|
267
265
|
|
|
268
266
|
# -----------------------------------------------------------------------------
|
|
269
267
|
class AdvertisementDataAccumulator:
|
|
270
|
-
def __init__(self, passive=False):
|
|
268
|
+
def __init__(self, passive: bool = False):
|
|
271
269
|
self.passive = passive
|
|
272
270
|
self.last_advertisement = None
|
|
273
271
|
self.last_data = b''
|
|
@@ -1166,14 +1164,11 @@ class BigSync(utils.EventEmitter):
|
|
|
1166
1164
|
logger.error('BIG Sync %d is not active.', self.big_handle)
|
|
1167
1165
|
return
|
|
1168
1166
|
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
check_result=True,
|
|
1175
|
-
)
|
|
1176
|
-
await terminated.wait()
|
|
1167
|
+
await self.device.send_command(
|
|
1168
|
+
hci.HCI_LE_BIG_Terminate_Sync_Command(big_handle=self.big_handle),
|
|
1169
|
+
check_result=True,
|
|
1170
|
+
)
|
|
1171
|
+
self.state = BigSync.State.TERMINATED
|
|
1177
1172
|
|
|
1178
1173
|
|
|
1179
1174
|
# -----------------------------------------------------------------------------
|
|
@@ -1249,7 +1244,7 @@ class LePhyOptions:
|
|
|
1249
1244
|
PREFER_S_2_CODED_PHY = 1
|
|
1250
1245
|
PREFER_S_8_CODED_PHY = 2
|
|
1251
1246
|
|
|
1252
|
-
def __init__(self, coded_phy_preference=0):
|
|
1247
|
+
def __init__(self, coded_phy_preference: int = 0):
|
|
1253
1248
|
self.coded_phy_preference = coded_phy_preference
|
|
1254
1249
|
|
|
1255
1250
|
def __int__(self):
|
|
@@ -1458,6 +1453,8 @@ class _IsoLink:
|
|
|
1458
1453
|
handle: int
|
|
1459
1454
|
device: Device
|
|
1460
1455
|
sink: Callable[[hci.HCI_IsoDataPacket], Any] | None = None
|
|
1456
|
+
data_paths: set[_IsoLink.Direction]
|
|
1457
|
+
_data_path_lock: asyncio.Lock
|
|
1461
1458
|
|
|
1462
1459
|
class Direction(IntEnum):
|
|
1463
1460
|
HOST_TO_CONTROLLER = (
|
|
@@ -1467,6 +1464,10 @@ class _IsoLink:
|
|
|
1467
1464
|
hci.HCI_LE_Setup_ISO_Data_Path_Command.Direction.CONTROLLER_TO_HOST
|
|
1468
1465
|
)
|
|
1469
1466
|
|
|
1467
|
+
def __init__(self) -> None:
|
|
1468
|
+
self._data_path_lock = asyncio.Lock()
|
|
1469
|
+
self.data_paths = set()
|
|
1470
|
+
|
|
1470
1471
|
async def setup_data_path(
|
|
1471
1472
|
self,
|
|
1472
1473
|
direction: _IsoLink.Direction,
|
|
@@ -1487,37 +1488,45 @@ class _IsoLink:
|
|
|
1487
1488
|
Raises:
|
|
1488
1489
|
HCI_Error: When command complete status is not HCI_SUCCESS.
|
|
1489
1490
|
"""
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1491
|
+
async with self._data_path_lock:
|
|
1492
|
+
if direction in self.data_paths:
|
|
1493
|
+
return
|
|
1494
|
+
await self.device.send_command(
|
|
1495
|
+
hci.HCI_LE_Setup_ISO_Data_Path_Command(
|
|
1496
|
+
connection_handle=self.handle,
|
|
1497
|
+
data_path_direction=direction,
|
|
1498
|
+
data_path_id=data_path_id,
|
|
1499
|
+
codec_id=codec_id or hci.CodingFormat(hci.CodecID.TRANSPARENT),
|
|
1500
|
+
controller_delay=controller_delay,
|
|
1501
|
+
codec_configuration=codec_configuration,
|
|
1502
|
+
),
|
|
1503
|
+
check_result=True,
|
|
1504
|
+
)
|
|
1505
|
+
self.data_paths.add(direction)
|
|
1501
1506
|
|
|
1502
|
-
async def remove_data_path(self, directions: Iterable[_IsoLink.Direction]) ->
|
|
1507
|
+
async def remove_data_path(self, directions: Iterable[_IsoLink.Direction]) -> None:
|
|
1503
1508
|
"""Remove a data path with controller on given direction.
|
|
1504
1509
|
|
|
1505
1510
|
Args:
|
|
1506
1511
|
direction: Direction of data path.
|
|
1507
1512
|
|
|
1508
|
-
|
|
1509
|
-
|
|
1513
|
+
Raises:
|
|
1514
|
+
HCI_Error: When command complete status is not HCI_SUCCESS.
|
|
1510
1515
|
"""
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
+
async with self._data_path_lock:
|
|
1517
|
+
directions_to_remove = set(directions).intersection(self.data_paths)
|
|
1518
|
+
if not directions_to_remove:
|
|
1519
|
+
return
|
|
1520
|
+
await self.device.send_command(
|
|
1521
|
+
hci.HCI_LE_Remove_ISO_Data_Path_Command(
|
|
1522
|
+
connection_handle=self.handle,
|
|
1523
|
+
data_path_direction=sum(
|
|
1524
|
+
1 << direction for direction in directions_to_remove
|
|
1525
|
+
),
|
|
1516
1526
|
),
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
return response.return_parameters.status
|
|
1527
|
+
check_result=True,
|
|
1528
|
+
)
|
|
1529
|
+
self.data_paths.difference_update(directions_to_remove)
|
|
1521
1530
|
|
|
1522
1531
|
def write(self, sdu: bytes) -> None:
|
|
1523
1532
|
"""Write an ISO SDU."""
|
|
@@ -1627,7 +1636,8 @@ class CisLink(utils.EventEmitter, _IsoLink):
|
|
|
1627
1636
|
EVENT_ESTABLISHMENT_FAILURE: ClassVar[str] = "establishment_failure"
|
|
1628
1637
|
|
|
1629
1638
|
def __post_init__(self) -> None:
|
|
1630
|
-
|
|
1639
|
+
utils.EventEmitter.__init__(self)
|
|
1640
|
+
_IsoLink.__init__(self)
|
|
1631
1641
|
|
|
1632
1642
|
async def disconnect(
|
|
1633
1643
|
self, reason: int = hci.HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR
|
|
@@ -1643,6 +1653,7 @@ class BisLink(_IsoLink):
|
|
|
1643
1653
|
sink: Callable[[hci.HCI_IsoDataPacket], Any] | None = None
|
|
1644
1654
|
|
|
1645
1655
|
def __post_init__(self) -> None:
|
|
1656
|
+
super().__init__()
|
|
1646
1657
|
self.device = self.big.device
|
|
1647
1658
|
|
|
1648
1659
|
|
|
@@ -1697,6 +1708,7 @@ class Connection(utils.CompositeEventEmitter):
|
|
|
1697
1708
|
self_address: hci.Address
|
|
1698
1709
|
self_resolvable_address: Optional[hci.Address]
|
|
1699
1710
|
peer_address: hci.Address
|
|
1711
|
+
peer_name: Optional[str]
|
|
1700
1712
|
peer_resolvable_address: Optional[hci.Address]
|
|
1701
1713
|
peer_le_features: Optional[hci.LeFeatureMask]
|
|
1702
1714
|
role: hci.Role
|
|
@@ -1786,15 +1798,22 @@ class Connection(utils.CompositeEventEmitter):
|
|
|
1786
1798
|
|
|
1787
1799
|
@dataclass
|
|
1788
1800
|
class Parameters:
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1801
|
+
"""
|
|
1802
|
+
LE connection parameters.
|
|
1803
|
+
|
|
1804
|
+
Attributes:
|
|
1805
|
+
connection_interval: Connection interval, in milliseconds.
|
|
1806
|
+
peripheral_latency: Peripheral latency, in number of intervals.
|
|
1807
|
+
supervision_timeout: Supervision timeout, in milliseconds.
|
|
1808
|
+
subrate_factor: See Bluetooth spec Vol 6, Part B - 4.5.1 Connection events
|
|
1809
|
+
continuation_number: See Bluetooth spec Vol 6, Part B - 4.5.1 Connection events
|
|
1810
|
+
"""
|
|
1811
|
+
|
|
1812
|
+
connection_interval: float
|
|
1813
|
+
peripheral_latency: int
|
|
1814
|
+
supervision_timeout: float
|
|
1815
|
+
subrate_factor: int = 1
|
|
1816
|
+
continuation_number: int = 0
|
|
1798
1817
|
|
|
1799
1818
|
def __init__(
|
|
1800
1819
|
self,
|
|
@@ -1835,36 +1854,6 @@ class Connection(utils.CompositeEventEmitter):
|
|
|
1835
1854
|
self.cs_configs = {}
|
|
1836
1855
|
self.cs_procedures = {}
|
|
1837
1856
|
|
|
1838
|
-
# [Classic only]
|
|
1839
|
-
@classmethod
|
|
1840
|
-
def incomplete(cls, device, peer_address, role):
|
|
1841
|
-
"""
|
|
1842
|
-
Instantiate an incomplete connection (ie. one waiting for a HCI Connection
|
|
1843
|
-
Complete event).
|
|
1844
|
-
Once received it shall be completed using the `.complete` method.
|
|
1845
|
-
"""
|
|
1846
|
-
return cls(
|
|
1847
|
-
device,
|
|
1848
|
-
None,
|
|
1849
|
-
PhysicalTransport.BR_EDR,
|
|
1850
|
-
device.public_address,
|
|
1851
|
-
None,
|
|
1852
|
-
peer_address,
|
|
1853
|
-
None,
|
|
1854
|
-
role,
|
|
1855
|
-
None,
|
|
1856
|
-
)
|
|
1857
|
-
|
|
1858
|
-
# [Classic only]
|
|
1859
|
-
def complete(self, handle, parameters):
|
|
1860
|
-
"""
|
|
1861
|
-
Finish an incomplete connection upon completion.
|
|
1862
|
-
"""
|
|
1863
|
-
assert self.handle is None
|
|
1864
|
-
assert self.transport == PhysicalTransport.BR_EDR
|
|
1865
|
-
self.handle = handle
|
|
1866
|
-
self.parameters = parameters
|
|
1867
|
-
|
|
1868
1857
|
@property
|
|
1869
1858
|
def role_name(self):
|
|
1870
1859
|
if self.role is None:
|
|
@@ -1876,7 +1865,7 @@ class Connection(utils.CompositeEventEmitter):
|
|
|
1876
1865
|
return f'UNKNOWN[{self.role}]'
|
|
1877
1866
|
|
|
1878
1867
|
@property
|
|
1879
|
-
def is_encrypted(self):
|
|
1868
|
+
def is_encrypted(self) -> bool:
|
|
1880
1869
|
return self.encryption != 0
|
|
1881
1870
|
|
|
1882
1871
|
@property
|
|
@@ -1886,16 +1875,6 @@ class Connection(utils.CompositeEventEmitter):
|
|
|
1886
1875
|
def send_l2cap_pdu(self, cid: int, pdu: bytes) -> None:
|
|
1887
1876
|
self.device.send_l2cap_pdu(self.handle, cid, pdu)
|
|
1888
1877
|
|
|
1889
|
-
@utils.deprecated("Please use create_l2cap_channel()")
|
|
1890
|
-
async def open_l2cap_channel(
|
|
1891
|
-
self,
|
|
1892
|
-
psm,
|
|
1893
|
-
max_credits=DEVICE_DEFAULT_L2CAP_COC_MAX_CREDITS,
|
|
1894
|
-
mtu=DEVICE_DEFAULT_L2CAP_COC_MTU,
|
|
1895
|
-
mps=DEVICE_DEFAULT_L2CAP_COC_MPS,
|
|
1896
|
-
):
|
|
1897
|
-
return await self.device.open_l2cap_channel(self, psm, max_credits, mtu, mps)
|
|
1898
|
-
|
|
1899
1878
|
@overload
|
|
1900
1879
|
async def create_l2cap_channel(
|
|
1901
1880
|
self, spec: l2cap.ClassicChannelSpec
|
|
@@ -1947,7 +1926,7 @@ class Connection(utils.CompositeEventEmitter):
|
|
|
1947
1926
|
self.remove_listener(self.EVENT_DISCONNECTION, abort.set_result)
|
|
1948
1927
|
self.remove_listener(self.EVENT_DISCONNECTION_FAILURE, abort.set_exception)
|
|
1949
1928
|
|
|
1950
|
-
async def set_data_length(self, tx_octets, tx_time) -> None:
|
|
1929
|
+
async def set_data_length(self, tx_octets: int, tx_time: int) -> None:
|
|
1951
1930
|
return await self.device.set_data_length(self, tx_octets, tx_time)
|
|
1952
1931
|
|
|
1953
1932
|
async def update_parameters(
|
|
@@ -1977,7 +1956,12 @@ class Connection(utils.CompositeEventEmitter):
|
|
|
1977
1956
|
use_l2cap=use_l2cap,
|
|
1978
1957
|
)
|
|
1979
1958
|
|
|
1980
|
-
async def set_phy(
|
|
1959
|
+
async def set_phy(
|
|
1960
|
+
self,
|
|
1961
|
+
tx_phys: Optional[Iterable[hci.Phy]] = None,
|
|
1962
|
+
rx_phys: Optional[Iterable[hci.Phy]] = None,
|
|
1963
|
+
phy_options: int = 0,
|
|
1964
|
+
):
|
|
1981
1965
|
return await self.device.set_connection_phy(self, tx_phys, rx_phys, phy_options)
|
|
1982
1966
|
|
|
1983
1967
|
async def get_phy(self) -> ConnectionPHY:
|
|
@@ -2076,9 +2060,7 @@ class DeviceConfiguration:
|
|
|
2076
2060
|
connectable: bool = True
|
|
2077
2061
|
discoverable: bool = True
|
|
2078
2062
|
advertising_data: bytes = bytes(
|
|
2079
|
-
AdvertisingData(
|
|
2080
|
-
[(AdvertisingData.COMPLETE_LOCAL_NAME, bytes(DEVICE_DEFAULT_NAME, 'utf-8'))]
|
|
2081
|
-
)
|
|
2063
|
+
AdvertisingData([data_types.CompleteLocalName(DEVICE_DEFAULT_NAME)])
|
|
2082
2064
|
)
|
|
2083
2065
|
irk: bytes = bytes(16) # This really must be changed for any level of security
|
|
2084
2066
|
keystore: Optional[str] = None
|
|
@@ -2122,9 +2104,7 @@ class DeviceConfiguration:
|
|
|
2122
2104
|
self.advertising_data = bytes.fromhex(advertising_data)
|
|
2123
2105
|
elif name is not None:
|
|
2124
2106
|
self.advertising_data = bytes(
|
|
2125
|
-
AdvertisingData(
|
|
2126
|
-
[(AdvertisingData.COMPLETE_LOCAL_NAME, bytes(self.name, 'utf-8'))]
|
|
2127
|
-
)
|
|
2107
|
+
AdvertisingData([data_types.CompleteLocalName(self.name)])
|
|
2128
2108
|
)
|
|
2129
2109
|
|
|
2130
2110
|
# Load scan response data
|
|
@@ -2176,7 +2156,7 @@ class DeviceConfiguration:
|
|
|
2176
2156
|
# Decorator that converts the first argument from a connection handle to a connection
|
|
2177
2157
|
def with_connection_from_handle(function):
|
|
2178
2158
|
@functools.wraps(function)
|
|
2179
|
-
def wrapper(self, connection_handle, *args, **kwargs):
|
|
2159
|
+
def wrapper(self, connection_handle: int, *args, **kwargs):
|
|
2180
2160
|
if (connection := self.lookup_connection(connection_handle)) is None:
|
|
2181
2161
|
raise ObjectLookupError(
|
|
2182
2162
|
f'no connection for handle: 0x{connection_handle:04x}'
|
|
@@ -2189,12 +2169,12 @@ def with_connection_from_handle(function):
|
|
|
2189
2169
|
# Decorator that converts the first argument from a bluetooth address to a connection
|
|
2190
2170
|
def with_connection_from_address(function):
|
|
2191
2171
|
@functools.wraps(function)
|
|
2192
|
-
def wrapper(
|
|
2193
|
-
if connection :=
|
|
2194
|
-
return function(
|
|
2195
|
-
for connection in
|
|
2172
|
+
def wrapper(device: Device, address: hci.Address, *args, **kwargs):
|
|
2173
|
+
if connection := device.pending_connections.get(address):
|
|
2174
|
+
return function(device, connection, address, *args, **kwargs)
|
|
2175
|
+
for connection in device.connections.values():
|
|
2196
2176
|
if connection.peer_address == address:
|
|
2197
|
-
return function(
|
|
2177
|
+
return function(device, connection, *args, **kwargs)
|
|
2198
2178
|
raise ObjectLookupError('no connection for address')
|
|
2199
2179
|
|
|
2200
2180
|
return wrapper
|
|
@@ -2204,13 +2184,13 @@ def with_connection_from_address(function):
|
|
|
2204
2184
|
# connection
|
|
2205
2185
|
def try_with_connection_from_address(function):
|
|
2206
2186
|
@functools.wraps(function)
|
|
2207
|
-
def wrapper(
|
|
2208
|
-
if connection :=
|
|
2209
|
-
return function(
|
|
2210
|
-
for connection in
|
|
2187
|
+
def wrapper(device: Device, address: hci.Address, *args, **kwargs):
|
|
2188
|
+
if connection := device.pending_connections.get(address):
|
|
2189
|
+
return function(device, connection, address, *args, **kwargs)
|
|
2190
|
+
for connection in device.connections.values():
|
|
2211
2191
|
if connection.peer_address == address:
|
|
2212
|
-
return function(
|
|
2213
|
-
return function(
|
|
2192
|
+
return function(device, connection, address, *args, **kwargs)
|
|
2193
|
+
return function(device, None, address, *args, **kwargs)
|
|
2214
2194
|
|
|
2215
2195
|
return wrapper
|
|
2216
2196
|
|
|
@@ -2380,7 +2360,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
2380
2360
|
self.le_connecting = False
|
|
2381
2361
|
self.disconnecting = False
|
|
2382
2362
|
self.connections = {} # Connections, by connection handle
|
|
2383
|
-
self.pending_connections =
|
|
2363
|
+
self.pending_connections = (
|
|
2364
|
+
{}
|
|
2365
|
+
) # Pending connections, by BD address (BR/EDR only)
|
|
2384
2366
|
self.sco_links = {} # ScoLinks, by connection handle (BR/EDR only)
|
|
2385
2367
|
self.cis_links = {} # CisLinks, by connection handle (LE only)
|
|
2386
2368
|
self._pending_cis = {} # (CIS_ID, CIG_ID), by CIS_handle
|
|
@@ -2608,36 +2590,6 @@ class Device(utils.CompositeEventEmitter):
|
|
|
2608
2590
|
None,
|
|
2609
2591
|
)
|
|
2610
2592
|
|
|
2611
|
-
@utils.deprecated("Please use create_l2cap_server()")
|
|
2612
|
-
def register_l2cap_server(self, psm, server) -> int:
|
|
2613
|
-
return self.l2cap_channel_manager.register_server(psm, server)
|
|
2614
|
-
|
|
2615
|
-
@utils.deprecated("Please use create_l2cap_server()")
|
|
2616
|
-
def register_l2cap_channel_server(
|
|
2617
|
-
self,
|
|
2618
|
-
psm,
|
|
2619
|
-
server,
|
|
2620
|
-
max_credits=DEVICE_DEFAULT_L2CAP_COC_MAX_CREDITS,
|
|
2621
|
-
mtu=DEVICE_DEFAULT_L2CAP_COC_MTU,
|
|
2622
|
-
mps=DEVICE_DEFAULT_L2CAP_COC_MPS,
|
|
2623
|
-
):
|
|
2624
|
-
return self.l2cap_channel_manager.register_le_coc_server(
|
|
2625
|
-
psm, server, max_credits, mtu, mps
|
|
2626
|
-
)
|
|
2627
|
-
|
|
2628
|
-
@utils.deprecated("Please use create_l2cap_channel()")
|
|
2629
|
-
async def open_l2cap_channel(
|
|
2630
|
-
self,
|
|
2631
|
-
connection,
|
|
2632
|
-
psm,
|
|
2633
|
-
max_credits=DEVICE_DEFAULT_L2CAP_COC_MAX_CREDITS,
|
|
2634
|
-
mtu=DEVICE_DEFAULT_L2CAP_COC_MTU,
|
|
2635
|
-
mps=DEVICE_DEFAULT_L2CAP_COC_MPS,
|
|
2636
|
-
):
|
|
2637
|
-
return await self.l2cap_channel_manager.open_le_coc(
|
|
2638
|
-
connection, psm, max_credits, mtu, mps
|
|
2639
|
-
)
|
|
2640
|
-
|
|
2641
2593
|
@overload
|
|
2642
2594
|
async def create_l2cap_channel(
|
|
2643
2595
|
self,
|
|
@@ -2705,7 +2657,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
2705
2657
|
def send_l2cap_pdu(self, connection_handle: int, cid: int, pdu: bytes) -> None:
|
|
2706
2658
|
self.host.send_l2cap_pdu(connection_handle, cid, pdu)
|
|
2707
2659
|
|
|
2708
|
-
async def send_command(self, command, check_result=False):
|
|
2660
|
+
async def send_command(self, command: hci.HCI_Command, check_result: bool = False):
|
|
2709
2661
|
try:
|
|
2710
2662
|
return await asyncio.wait_for(
|
|
2711
2663
|
self.host.send_command(command, check_result), self.command_timeout
|
|
@@ -2962,13 +2914,13 @@ class Device(utils.CompositeEventEmitter):
|
|
|
2962
2914
|
def supports_le_features(self, feature: hci.LeFeatureMask) -> bool:
|
|
2963
2915
|
return self.host.supports_le_features(feature)
|
|
2964
2916
|
|
|
2965
|
-
def supports_le_phy(self, phy:
|
|
2966
|
-
if phy == hci.
|
|
2917
|
+
def supports_le_phy(self, phy: hci.Phy) -> bool:
|
|
2918
|
+
if phy == hci.Phy.LE_1M:
|
|
2967
2919
|
return True
|
|
2968
2920
|
|
|
2969
|
-
feature_map: dict[
|
|
2970
|
-
hci.
|
|
2971
|
-
hci.
|
|
2921
|
+
feature_map: dict[hci.Phy, hci.LeFeatureMask] = {
|
|
2922
|
+
hci.Phy.LE_2M: hci.LeFeatureMask.LE_2M_PHY,
|
|
2923
|
+
hci.Phy.LE_CODED: hci.LeFeatureMask.LE_CODED_PHY,
|
|
2972
2924
|
}
|
|
2973
2925
|
if phy not in feature_map:
|
|
2974
2926
|
raise InvalidArgumentError('invalid PHY')
|
|
@@ -3245,8 +3197,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3245
3197
|
else 0
|
|
3246
3198
|
)
|
|
3247
3199
|
await advertising_set.start(duration=duration)
|
|
3248
|
-
except Exception
|
|
3249
|
-
logger.exception(
|
|
3200
|
+
except Exception:
|
|
3201
|
+
logger.exception('failed to start advertising set')
|
|
3250
3202
|
await advertising_set.remove()
|
|
3251
3203
|
raise
|
|
3252
3204
|
|
|
@@ -3572,7 +3524,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3572
3524
|
self.discovering = False
|
|
3573
3525
|
|
|
3574
3526
|
@host_event_handler
|
|
3575
|
-
def on_inquiry_result(
|
|
3527
|
+
def on_inquiry_result(
|
|
3528
|
+
self, address: hci.Address, class_of_device: int, data: bytes, rssi: int
|
|
3529
|
+
):
|
|
3576
3530
|
self.emit(
|
|
3577
3531
|
self.EVENT_INQUIRY_RESULT,
|
|
3578
3532
|
address,
|
|
@@ -3581,7 +3535,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3581
3535
|
rssi,
|
|
3582
3536
|
)
|
|
3583
3537
|
|
|
3584
|
-
async def set_scan_enable(
|
|
3538
|
+
async def set_scan_enable(
|
|
3539
|
+
self, inquiry_scan_enabled: bool, page_scan_enabled: bool
|
|
3540
|
+
):
|
|
3585
3541
|
if inquiry_scan_enabled and page_scan_enabled:
|
|
3586
3542
|
scan_enable = 0x03
|
|
3587
3543
|
elif page_scan_enabled:
|
|
@@ -3601,14 +3557,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3601
3557
|
# Synthesize an inquiry response if none is set already
|
|
3602
3558
|
if self.inquiry_response is None:
|
|
3603
3559
|
self.inquiry_response = bytes(
|
|
3604
|
-
AdvertisingData(
|
|
3605
|
-
[
|
|
3606
|
-
(
|
|
3607
|
-
AdvertisingData.COMPLETE_LOCAL_NAME,
|
|
3608
|
-
bytes(self.name, 'utf-8'),
|
|
3609
|
-
)
|
|
3610
|
-
]
|
|
3611
|
-
)
|
|
3560
|
+
AdvertisingData([data_types.CompleteLocalName(self.name)])
|
|
3612
3561
|
)
|
|
3613
3562
|
|
|
3614
3563
|
# Update the controller
|
|
@@ -3714,6 +3663,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3714
3663
|
# If the address is not parsable, assume it is a name instead
|
|
3715
3664
|
always_resolve = False
|
|
3716
3665
|
logger.debug('looking for peer by name')
|
|
3666
|
+
assert isinstance(peer_address, str)
|
|
3717
3667
|
peer_address = await self.find_peer_by_name(
|
|
3718
3668
|
peer_address, transport
|
|
3719
3669
|
) # TODO: timeout
|
|
@@ -3741,7 +3691,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3741
3691
|
):
|
|
3742
3692
|
pending_connection.set_result(connection)
|
|
3743
3693
|
|
|
3744
|
-
def on_connection_failure(error):
|
|
3694
|
+
def on_connection_failure(error: core.ConnectionError):
|
|
3745
3695
|
if transport == PhysicalTransport.LE or (
|
|
3746
3696
|
# match BR/EDR connection failure event against peer address
|
|
3747
3697
|
error.transport == transport
|
|
@@ -3881,8 +3831,16 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3881
3831
|
)
|
|
3882
3832
|
else:
|
|
3883
3833
|
# Save pending connection
|
|
3884
|
-
self.pending_connections[peer_address] = Connection
|
|
3885
|
-
self,
|
|
3834
|
+
self.pending_connections[peer_address] = Connection(
|
|
3835
|
+
device=self,
|
|
3836
|
+
handle=0,
|
|
3837
|
+
transport=core.PhysicalTransport.BR_EDR,
|
|
3838
|
+
self_address=self.public_address,
|
|
3839
|
+
self_resolvable_address=None,
|
|
3840
|
+
peer_address=peer_address,
|
|
3841
|
+
peer_resolvable_address=None,
|
|
3842
|
+
role=hci.Role.CENTRAL,
|
|
3843
|
+
parameters=Connection.Parameters(0, 0, 0),
|
|
3886
3844
|
)
|
|
3887
3845
|
|
|
3888
3846
|
# TODO: allow passing other settings
|
|
@@ -3961,6 +3919,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3961
3919
|
except InvalidArgumentError:
|
|
3962
3920
|
# If the address is not parsable, assume it is a name instead
|
|
3963
3921
|
logger.debug('looking for peer by name')
|
|
3922
|
+
assert isinstance(peer_address, str)
|
|
3964
3923
|
peer_address = await self.find_peer_by_name(
|
|
3965
3924
|
peer_address, PhysicalTransport.BR_EDR
|
|
3966
3925
|
) # TODO: timeout
|
|
@@ -4019,7 +3978,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4019
3978
|
):
|
|
4020
3979
|
pending_connection.set_result(connection)
|
|
4021
3980
|
|
|
4022
|
-
def on_connection_failure(error):
|
|
3981
|
+
def on_connection_failure(error: core.ConnectionError):
|
|
4023
3982
|
if (
|
|
4024
3983
|
error.transport == PhysicalTransport.BR_EDR
|
|
4025
3984
|
and error.peer_address == peer_address
|
|
@@ -4029,12 +3988,20 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4029
3988
|
self.on(self.EVENT_CONNECTION, on_connection)
|
|
4030
3989
|
self.on(self.EVENT_CONNECTION_FAILURE, on_connection_failure)
|
|
4031
3990
|
|
|
4032
|
-
# Save
|
|
3991
|
+
# Save Peripheral hci.role.
|
|
4033
3992
|
# Even if we requested a role switch in the hci.HCI_Accept_Connection_Request
|
|
4034
3993
|
# command, this connection is still considered Peripheral until an eventual
|
|
4035
3994
|
# role change event.
|
|
4036
|
-
self.pending_connections[peer_address] = Connection
|
|
4037
|
-
self,
|
|
3995
|
+
self.pending_connections[peer_address] = Connection(
|
|
3996
|
+
device=self,
|
|
3997
|
+
handle=0,
|
|
3998
|
+
transport=core.PhysicalTransport.BR_EDR,
|
|
3999
|
+
self_address=self.public_address,
|
|
4000
|
+
self_resolvable_address=None,
|
|
4001
|
+
peer_address=peer_address,
|
|
4002
|
+
peer_resolvable_address=None,
|
|
4003
|
+
role=hci.Role.PERIPHERAL,
|
|
4004
|
+
parameters=Connection.Parameters(0, 0, 0),
|
|
4038
4005
|
)
|
|
4039
4006
|
|
|
4040
4007
|
try:
|
|
@@ -4056,7 +4023,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4056
4023
|
self.pending_connections.pop(peer_address, None)
|
|
4057
4024
|
|
|
4058
4025
|
@asynccontextmanager
|
|
4059
|
-
async def connect_as_gatt(self, peer_address):
|
|
4026
|
+
async def connect_as_gatt(self, peer_address: Union[hci.Address, str]):
|
|
4060
4027
|
async with AsyncExitStack() as stack:
|
|
4061
4028
|
connection = await stack.enter_async_context(
|
|
4062
4029
|
await self.connect(peer_address)
|
|
@@ -4092,6 +4059,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4092
4059
|
except InvalidArgumentError:
|
|
4093
4060
|
# If the address is not parsable, assume it is a name instead
|
|
4094
4061
|
logger.debug('looking for peer by name')
|
|
4062
|
+
assert isinstance(peer_address, str)
|
|
4095
4063
|
peer_address = await self.find_peer_by_name(
|
|
4096
4064
|
peer_address, PhysicalTransport.BR_EDR
|
|
4097
4065
|
) # TODO: timeout
|
|
@@ -4137,7 +4105,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4137
4105
|
)
|
|
4138
4106
|
self.disconnecting = False
|
|
4139
4107
|
|
|
4140
|
-
async def set_data_length(
|
|
4108
|
+
async def set_data_length(
|
|
4109
|
+
self, connection: Connection, tx_octets: int, tx_time: int
|
|
4110
|
+
) -> None:
|
|
4141
4111
|
if tx_octets < 0x001B or tx_octets > 0x00FB:
|
|
4142
4112
|
raise InvalidArgumentError('tx_octets must be between 0x001B and 0x00FB')
|
|
4143
4113
|
|
|
@@ -4240,7 +4210,11 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4240
4210
|
)
|
|
4241
4211
|
|
|
4242
4212
|
async def set_connection_phy(
|
|
4243
|
-
self,
|
|
4213
|
+
self,
|
|
4214
|
+
connection: Connection,
|
|
4215
|
+
tx_phys: Optional[Iterable[hci.Phy]] = None,
|
|
4216
|
+
rx_phys: Optional[Iterable[hci.Phy]] = None,
|
|
4217
|
+
phy_options: int = 0,
|
|
4244
4218
|
):
|
|
4245
4219
|
if not self.host.supports_command(hci.HCI_LE_SET_PHY_COMMAND):
|
|
4246
4220
|
logger.warning('ignoring request, command not supported')
|
|
@@ -4256,7 +4230,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4256
4230
|
all_phys=all_phys_bits,
|
|
4257
4231
|
tx_phys=hci.phy_list_to_bits(tx_phys),
|
|
4258
4232
|
rx_phys=hci.phy_list_to_bits(rx_phys),
|
|
4259
|
-
phy_options=
|
|
4233
|
+
phy_options=phy_options,
|
|
4260
4234
|
)
|
|
4261
4235
|
)
|
|
4262
4236
|
|
|
@@ -4267,7 +4241,11 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4267
4241
|
)
|
|
4268
4242
|
raise hci.HCI_StatusError(result)
|
|
4269
4243
|
|
|
4270
|
-
async def set_default_phy(
|
|
4244
|
+
async def set_default_phy(
|
|
4245
|
+
self,
|
|
4246
|
+
tx_phys: Optional[Iterable[hci.Phy]] = None,
|
|
4247
|
+
rx_phys: Optional[Iterable[hci.Phy]] = None,
|
|
4248
|
+
):
|
|
4271
4249
|
all_phys_bits = (1 if tx_phys is None else 0) | (
|
|
4272
4250
|
(1 if rx_phys is None else 0) << 1
|
|
4273
4251
|
)
|
|
@@ -4305,7 +4283,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4305
4283
|
check_result=True,
|
|
4306
4284
|
)
|
|
4307
4285
|
|
|
4308
|
-
async def find_peer_by_name(self, name, transport=PhysicalTransport.LE):
|
|
4286
|
+
async def find_peer_by_name(self, name: str, transport=PhysicalTransport.LE):
|
|
4309
4287
|
"""
|
|
4310
4288
|
Scan for a peer with a given name and return its address.
|
|
4311
4289
|
"""
|
|
@@ -4320,7 +4298,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4320
4298
|
if local_name == name:
|
|
4321
4299
|
peer_address.set_result(address)
|
|
4322
4300
|
|
|
4323
|
-
listener = None
|
|
4301
|
+
listener: Optional[Callable[..., None]] = None
|
|
4324
4302
|
was_scanning = self.scanning
|
|
4325
4303
|
was_discovering = self.discovering
|
|
4326
4304
|
try:
|
|
@@ -4426,10 +4404,10 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4426
4404
|
def smp_session_proxy(self, session_proxy: type[smp.Session]) -> None:
|
|
4427
4405
|
self.smp_manager.session_proxy = session_proxy
|
|
4428
4406
|
|
|
4429
|
-
async def pair(self, connection):
|
|
4407
|
+
async def pair(self, connection: Connection):
|
|
4430
4408
|
return await self.smp_manager.pair(connection)
|
|
4431
4409
|
|
|
4432
|
-
def request_pairing(self, connection):
|
|
4410
|
+
def request_pairing(self, connection: Connection):
|
|
4433
4411
|
return self.smp_manager.request_pairing(connection)
|
|
4434
4412
|
|
|
4435
4413
|
async def get_long_term_key(
|
|
@@ -4517,7 +4495,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4517
4495
|
on_authentication_failure,
|
|
4518
4496
|
)
|
|
4519
4497
|
|
|
4520
|
-
async def encrypt(self, connection, enable=True):
|
|
4498
|
+
async def encrypt(self, connection: Connection, enable: bool = True):
|
|
4521
4499
|
if not enable and connection.transport == PhysicalTransport.LE:
|
|
4522
4500
|
raise InvalidArgumentError('`enable` parameter is classic only.')
|
|
4523
4501
|
|
|
@@ -4527,7 +4505,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4527
4505
|
def on_encryption_change():
|
|
4528
4506
|
pending_encryption.set_result(None)
|
|
4529
4507
|
|
|
4530
|
-
def on_encryption_failure(error_code):
|
|
4508
|
+
def on_encryption_failure(error_code: int):
|
|
4531
4509
|
pending_encryption.set_exception(hci.HCI_Error(error_code))
|
|
4532
4510
|
|
|
4533
4511
|
connection.on(
|
|
@@ -4610,8 +4588,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4610
4588
|
try:
|
|
4611
4589
|
await self.keystore.update(address, keys)
|
|
4612
4590
|
await self.refresh_resolving_list()
|
|
4613
|
-
except Exception
|
|
4614
|
-
logger.
|
|
4591
|
+
except Exception:
|
|
4592
|
+
logger.exception('!!! error while storing keys')
|
|
4615
4593
|
else:
|
|
4616
4594
|
self.emit(self.EVENT_KEY_STORE_UPDATE)
|
|
4617
4595
|
|
|
@@ -4619,10 +4597,10 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4619
4597
|
async def switch_role(self, connection: Connection, role: hci.Role):
|
|
4620
4598
|
pending_role_change = asyncio.get_running_loop().create_future()
|
|
4621
4599
|
|
|
4622
|
-
def on_role_change(new_role):
|
|
4600
|
+
def on_role_change(new_role: hci.Role):
|
|
4623
4601
|
pending_role_change.set_result(new_role)
|
|
4624
4602
|
|
|
4625
|
-
def on_role_change_failure(error_code):
|
|
4603
|
+
def on_role_change_failure(error_code: int):
|
|
4626
4604
|
pending_role_change.set_exception(hci.HCI_Error(error_code))
|
|
4627
4605
|
|
|
4628
4606
|
connection.on(connection.EVENT_ROLE_CHANGE, on_role_change)
|
|
@@ -5212,10 +5190,10 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5212
5190
|
):
|
|
5213
5191
|
connection.emit(connection.EVENT_LINK_KEY)
|
|
5214
5192
|
|
|
5215
|
-
def add_service(self, service):
|
|
5193
|
+
def add_service(self, service: gatt.Service):
|
|
5216
5194
|
self.gatt_server.add_service(service)
|
|
5217
5195
|
|
|
5218
|
-
def add_services(self, services):
|
|
5196
|
+
def add_services(self, services: Iterable[gatt.Service]):
|
|
5219
5197
|
self.gatt_server.add_services(services)
|
|
5220
5198
|
|
|
5221
5199
|
def add_default_services(
|
|
@@ -5311,10 +5289,10 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5311
5289
|
@host_event_handler
|
|
5312
5290
|
def on_advertising_set_termination(
|
|
5313
5291
|
self,
|
|
5314
|
-
status,
|
|
5315
|
-
advertising_handle,
|
|
5316
|
-
connection_handle,
|
|
5317
|
-
number_of_completed_extended_advertising_events,
|
|
5292
|
+
status: int,
|
|
5293
|
+
advertising_handle: int,
|
|
5294
|
+
connection_handle: int,
|
|
5295
|
+
number_of_completed_extended_advertising_events: int,
|
|
5318
5296
|
):
|
|
5319
5297
|
# Legacy advertising set is also one of extended advertising sets.
|
|
5320
5298
|
if not (
|
|
@@ -5482,15 +5460,47 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5482
5460
|
self.emit(self.EVENT_CONNECTION, connection)
|
|
5483
5461
|
|
|
5484
5462
|
@host_event_handler
|
|
5485
|
-
def
|
|
5463
|
+
def on_classic_connection(
|
|
5464
|
+
self,
|
|
5465
|
+
connection_handle: int,
|
|
5466
|
+
peer_address: hci.Address,
|
|
5467
|
+
) -> None:
|
|
5468
|
+
if connection := self.pending_connections.pop(peer_address, None):
|
|
5469
|
+
connection.handle = connection_handle
|
|
5470
|
+
else:
|
|
5471
|
+
# Create a new connection
|
|
5472
|
+
connection = Connection(
|
|
5473
|
+
device=self,
|
|
5474
|
+
handle=connection_handle,
|
|
5475
|
+
transport=PhysicalTransport.BR_EDR,
|
|
5476
|
+
self_address=self.public_address,
|
|
5477
|
+
self_resolvable_address=None,
|
|
5478
|
+
peer_address=peer_address,
|
|
5479
|
+
peer_resolvable_address=None,
|
|
5480
|
+
role=hci.Role.PERIPHERAL,
|
|
5481
|
+
parameters=Connection.Parameters(0.0, 0, 0.0),
|
|
5482
|
+
)
|
|
5483
|
+
|
|
5484
|
+
logger.debug('*** %s', connection)
|
|
5485
|
+
if connection_handle in self.connections:
|
|
5486
|
+
logger.warning(
|
|
5487
|
+
'new connection reuses the same handle as a previous connection'
|
|
5488
|
+
)
|
|
5489
|
+
self.connections[connection_handle] = connection
|
|
5490
|
+
|
|
5491
|
+
self.emit(self.EVENT_CONNECTION, connection)
|
|
5492
|
+
|
|
5493
|
+
@host_event_handler
|
|
5494
|
+
def on_le_connection(
|
|
5486
5495
|
self,
|
|
5487
5496
|
connection_handle: int,
|
|
5488
|
-
transport: core.PhysicalTransport,
|
|
5489
5497
|
peer_address: hci.Address,
|
|
5490
5498
|
self_resolvable_address: Optional[hci.Address],
|
|
5491
5499
|
peer_resolvable_address: Optional[hci.Address],
|
|
5492
5500
|
role: hci.Role,
|
|
5493
|
-
|
|
5501
|
+
connection_interval: int,
|
|
5502
|
+
peripheral_latency: int,
|
|
5503
|
+
supervision_timeout: int,
|
|
5494
5504
|
) -> None:
|
|
5495
5505
|
# Convert all-zeros addresses into None.
|
|
5496
5506
|
if self_resolvable_address == hci.Address.ANY_RANDOM:
|
|
@@ -5510,19 +5520,6 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5510
5520
|
'new connection reuses the same handle as a previous connection'
|
|
5511
5521
|
)
|
|
5512
5522
|
|
|
5513
|
-
if transport == PhysicalTransport.BR_EDR:
|
|
5514
|
-
# Create a new connection
|
|
5515
|
-
connection = self.pending_connections.pop(peer_address)
|
|
5516
|
-
connection.complete(connection_handle, connection_parameters)
|
|
5517
|
-
self.connections[connection_handle] = connection
|
|
5518
|
-
|
|
5519
|
-
# Emit an event to notify listeners of the new connection
|
|
5520
|
-
self.emit(self.EVENT_CONNECTION, connection)
|
|
5521
|
-
|
|
5522
|
-
return
|
|
5523
|
-
|
|
5524
|
-
assert connection_parameters is not None
|
|
5525
|
-
|
|
5526
5523
|
if peer_resolvable_address is None:
|
|
5527
5524
|
# Resolve the peer address if we can
|
|
5528
5525
|
if self.address_resolver:
|
|
@@ -5572,16 +5569,16 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5572
5569
|
connection = Connection(
|
|
5573
5570
|
self,
|
|
5574
5571
|
connection_handle,
|
|
5575
|
-
|
|
5572
|
+
PhysicalTransport.LE,
|
|
5576
5573
|
self_address,
|
|
5577
5574
|
self_resolvable_address,
|
|
5578
5575
|
peer_address,
|
|
5579
5576
|
peer_resolvable_address,
|
|
5580
5577
|
role,
|
|
5581
5578
|
Connection.Parameters(
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5579
|
+
connection_interval * 1.25,
|
|
5580
|
+
peripheral_latency,
|
|
5581
|
+
supervision_timeout * 10.0,
|
|
5585
5582
|
),
|
|
5586
5583
|
)
|
|
5587
5584
|
self.connections[connection_handle] = connection
|
|
@@ -5613,7 +5610,12 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5613
5610
|
)
|
|
5614
5611
|
|
|
5615
5612
|
@host_event_handler
|
|
5616
|
-
def on_connection_failure(
|
|
5613
|
+
def on_connection_failure(
|
|
5614
|
+
self,
|
|
5615
|
+
transport: hci.PhysicalTransport,
|
|
5616
|
+
peer_address: hci.Address,
|
|
5617
|
+
error_code: int,
|
|
5618
|
+
):
|
|
5617
5619
|
logger.debug(
|
|
5618
5620
|
f'*** Connection failed: {hci.HCI_Constant.error_name(error_code)}'
|
|
5619
5621
|
)
|
|
@@ -5638,7 +5640,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5638
5640
|
|
|
5639
5641
|
# FIXME: Explore a delegate-model for BR/EDR wait connection #56.
|
|
5640
5642
|
@host_event_handler
|
|
5641
|
-
def on_connection_request(
|
|
5643
|
+
def on_connection_request(
|
|
5644
|
+
self, bd_addr: hci.Address, class_of_device: int, link_type: int
|
|
5645
|
+
):
|
|
5642
5646
|
logger.debug(f'*** Connection request: {bd_addr}')
|
|
5643
5647
|
|
|
5644
5648
|
# Handle SCO request.
|
|
@@ -5667,8 +5671,16 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5667
5671
|
# device configuration is set to accept any incoming connection
|
|
5668
5672
|
elif self.classic_accept_any:
|
|
5669
5673
|
# Save pending connection
|
|
5670
|
-
self.pending_connections[bd_addr] = Connection
|
|
5671
|
-
self,
|
|
5674
|
+
self.pending_connections[bd_addr] = Connection(
|
|
5675
|
+
device=self,
|
|
5676
|
+
handle=0,
|
|
5677
|
+
transport=core.PhysicalTransport.BR_EDR,
|
|
5678
|
+
self_address=self.public_address,
|
|
5679
|
+
self_resolvable_address=None,
|
|
5680
|
+
peer_address=bd_addr,
|
|
5681
|
+
peer_resolvable_address=None,
|
|
5682
|
+
role=hci.Role.PERIPHERAL,
|
|
5683
|
+
parameters=Connection.Parameters(0, 0, 0),
|
|
5672
5684
|
)
|
|
5673
5685
|
|
|
5674
5686
|
self.host.send_command_sync(
|
|
@@ -5732,7 +5744,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5732
5744
|
|
|
5733
5745
|
@host_event_handler
|
|
5734
5746
|
@with_connection_from_handle
|
|
5735
|
-
def on_connection_authentication(self, connection):
|
|
5747
|
+
def on_connection_authentication(self, connection: Connection):
|
|
5736
5748
|
logger.debug(
|
|
5737
5749
|
f'*** Connection Authentication: [0x{connection.handle:04X}] '
|
|
5738
5750
|
f'{connection.peer_address} as {connection.role_name}'
|
|
@@ -5742,7 +5754,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5742
5754
|
|
|
5743
5755
|
@host_event_handler
|
|
5744
5756
|
@with_connection_from_handle
|
|
5745
|
-
def on_connection_authentication_failure(
|
|
5757
|
+
def on_connection_authentication_failure(
|
|
5758
|
+
self, connection: Connection, error: core.ConnectionError
|
|
5759
|
+
):
|
|
5746
5760
|
logger.debug(
|
|
5747
5761
|
f'*** Connection Authentication Failure: [0x{connection.handle:04X}] '
|
|
5748
5762
|
f'{connection.peer_address} as {connection.role_name}, error={error}'
|
|
@@ -5784,10 +5798,13 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5784
5798
|
@host_event_handler
|
|
5785
5799
|
@with_connection_from_address
|
|
5786
5800
|
def on_authentication_io_capability_response(
|
|
5787
|
-
self,
|
|
5801
|
+
self,
|
|
5802
|
+
connection: Connection,
|
|
5803
|
+
io_capability: int,
|
|
5804
|
+
authentication_requirements: int,
|
|
5788
5805
|
):
|
|
5789
|
-
connection.
|
|
5790
|
-
connection.
|
|
5806
|
+
connection.pairing_peer_io_capability = io_capability
|
|
5807
|
+
connection.pairing_peer_authentication_requirements = (
|
|
5791
5808
|
authentication_requirements
|
|
5792
5809
|
)
|
|
5793
5810
|
|
|
@@ -5798,7 +5815,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5798
5815
|
# Ask what the pairing config should be for this connection
|
|
5799
5816
|
pairing_config = self.pairing_config_factory(connection)
|
|
5800
5817
|
io_capability = pairing_config.delegate.classic_io_capability
|
|
5801
|
-
peer_io_capability = connection.
|
|
5818
|
+
peer_io_capability = connection.pairing_peer_io_capability
|
|
5802
5819
|
|
|
5803
5820
|
async def confirm() -> bool:
|
|
5804
5821
|
# Ask the user to confirm the pairing, without display
|
|
@@ -5859,8 +5876,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5859
5876
|
)
|
|
5860
5877
|
)
|
|
5861
5878
|
return
|
|
5862
|
-
except Exception
|
|
5863
|
-
logger.
|
|
5879
|
+
except Exception:
|
|
5880
|
+
logger.exception('exception while confirming')
|
|
5864
5881
|
|
|
5865
5882
|
await self.host.send_command(
|
|
5866
5883
|
hci.HCI_User_Confirmation_Request_Negative_Reply_Command(
|
|
@@ -5873,7 +5890,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5873
5890
|
# [Classic only]
|
|
5874
5891
|
@host_event_handler
|
|
5875
5892
|
@with_connection_from_address
|
|
5876
|
-
def on_authentication_user_passkey_request(self, connection) -> None:
|
|
5893
|
+
def on_authentication_user_passkey_request(self, connection: Connection) -> None:
|
|
5877
5894
|
# Ask what the pairing config should be for this connection
|
|
5878
5895
|
pairing_config = self.pairing_config_factory(connection)
|
|
5879
5896
|
|
|
@@ -5889,8 +5906,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5889
5906
|
)
|
|
5890
5907
|
)
|
|
5891
5908
|
return
|
|
5892
|
-
except Exception
|
|
5893
|
-
logger.
|
|
5909
|
+
except Exception:
|
|
5910
|
+
logger.exception('exception while asking for pass-key')
|
|
5894
5911
|
|
|
5895
5912
|
await self.host.send_command(
|
|
5896
5913
|
hci.HCI_User_Passkey_Request_Negative_Reply_Command(
|
|
@@ -5916,7 +5933,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5916
5933
|
# [Classic only]
|
|
5917
5934
|
@host_event_handler
|
|
5918
5935
|
@with_connection_from_address
|
|
5919
|
-
def on_pin_code_request(self, connection):
|
|
5936
|
+
def on_pin_code_request(self, connection: Connection):
|
|
5920
5937
|
# Classic legacy pairing
|
|
5921
5938
|
# Ask what the pairing config should be for this connection
|
|
5922
5939
|
pairing_config = self.pairing_config_factory(connection)
|
|
@@ -5960,7 +5977,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5960
5977
|
# [Classic only]
|
|
5961
5978
|
@host_event_handler
|
|
5962
5979
|
@with_connection_from_address
|
|
5963
|
-
def on_authentication_user_passkey_notification(
|
|
5980
|
+
def on_authentication_user_passkey_notification(
|
|
5981
|
+
self, connection: Connection, passkey: int
|
|
5982
|
+
):
|
|
5964
5983
|
# Ask what the pairing config should be for this connection
|
|
5965
5984
|
pairing_config = self.pairing_config_factory(connection)
|
|
5966
5985
|
|
|
@@ -5972,14 +5991,15 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5972
5991
|
# [Classic only]
|
|
5973
5992
|
@host_event_handler
|
|
5974
5993
|
@try_with_connection_from_address
|
|
5975
|
-
def on_remote_name(
|
|
5994
|
+
def on_remote_name(
|
|
5995
|
+
self, connection: Optional[Connection], address: hci.Address, remote_name: bytes
|
|
5996
|
+
):
|
|
5976
5997
|
# Try to decode the name
|
|
5977
5998
|
try:
|
|
5978
|
-
remote_name = remote_name.decode('utf-8')
|
|
5979
5999
|
if connection:
|
|
5980
|
-
connection.peer_name = remote_name
|
|
6000
|
+
connection.peer_name = remote_name.decode('utf-8')
|
|
5981
6001
|
connection.emit(connection.EVENT_REMOTE_NAME)
|
|
5982
|
-
self.emit(self.EVENT_REMOTE_NAME, address, remote_name)
|
|
6002
|
+
self.emit(self.EVENT_REMOTE_NAME, address, remote_name.decode('utf-8'))
|
|
5983
6003
|
except UnicodeDecodeError as error:
|
|
5984
6004
|
logger.warning('peer name is not valid UTF-8')
|
|
5985
6005
|
if connection:
|
|
@@ -5990,7 +6010,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5990
6010
|
# [Classic only]
|
|
5991
6011
|
@host_event_handler
|
|
5992
6012
|
@try_with_connection_from_address
|
|
5993
|
-
def on_remote_name_failure(
|
|
6013
|
+
def on_remote_name_failure(
|
|
6014
|
+
self, connection: Optional[Connection], address: hci.Address, error: int
|
|
6015
|
+
):
|
|
5994
6016
|
if connection:
|
|
5995
6017
|
connection.emit(connection.EVENT_REMOTE_NAME_FAILURE, error)
|
|
5996
6018
|
self.emit(self.EVENT_REMOTE_NAME_FAILURE, address, error)
|
|
@@ -6191,7 +6213,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6191
6213
|
|
|
6192
6214
|
@host_event_handler
|
|
6193
6215
|
@with_connection_from_handle
|
|
6194
|
-
def on_connection_encryption_key_refresh(self, connection):
|
|
6216
|
+
def on_connection_encryption_key_refresh(self, connection: Connection):
|
|
6195
6217
|
logger.debug(
|
|
6196
6218
|
f'*** Connection Key Refresh: [0x{connection.handle:04X}] '
|
|
6197
6219
|
f'{connection.peer_address} as {connection.role_name}'
|
|
@@ -6201,27 +6223,27 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6201
6223
|
@host_event_handler
|
|
6202
6224
|
@with_connection_from_handle
|
|
6203
6225
|
def on_connection_parameters_update(
|
|
6204
|
-
self,
|
|
6226
|
+
self,
|
|
6227
|
+
connection: Connection,
|
|
6228
|
+
connection_interval: int,
|
|
6229
|
+
peripheral_latency: int,
|
|
6230
|
+
supervision_timeout: int,
|
|
6205
6231
|
):
|
|
6206
6232
|
logger.debug(
|
|
6207
6233
|
f'*** Connection Parameters Update: [0x{connection.handle:04X}] '
|
|
6208
6234
|
f'{connection.peer_address} as {connection.role_name}, '
|
|
6209
|
-
f'{connection_parameters}'
|
|
6210
6235
|
)
|
|
6211
|
-
if
|
|
6212
|
-
connection.parameters.connection_interval
|
|
6213
|
-
!= connection_parameters.connection_interval * 1.25
|
|
6214
|
-
):
|
|
6236
|
+
if connection.parameters.connection_interval != connection_interval * 1.25:
|
|
6215
6237
|
connection.parameters = Connection.Parameters(
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6238
|
+
connection_interval * 1.25,
|
|
6239
|
+
peripheral_latency,
|
|
6240
|
+
supervision_timeout * 10.0,
|
|
6219
6241
|
)
|
|
6220
6242
|
else:
|
|
6221
6243
|
connection.parameters = Connection.Parameters(
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
|
|
6244
|
+
connection_interval * 1.25,
|
|
6245
|
+
peripheral_latency,
|
|
6246
|
+
supervision_timeout * 10.0,
|
|
6225
6247
|
connection.parameters.subrate_factor,
|
|
6226
6248
|
connection.parameters.continuation_number,
|
|
6227
6249
|
)
|
|
@@ -6229,7 +6251,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6229
6251
|
|
|
6230
6252
|
@host_event_handler
|
|
6231
6253
|
@with_connection_from_handle
|
|
6232
|
-
def on_connection_parameters_update_failure(
|
|
6254
|
+
def on_connection_parameters_update_failure(
|
|
6255
|
+
self, connection: Connection, error: int
|
|
6256
|
+
):
|
|
6233
6257
|
logger.debug(
|
|
6234
6258
|
f'*** Connection Parameters Update Failed: [0x{connection.handle:04X}] '
|
|
6235
6259
|
f'{connection.peer_address} as {connection.role_name}, '
|
|
@@ -6239,7 +6263,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6239
6263
|
|
|
6240
6264
|
@host_event_handler
|
|
6241
6265
|
@with_connection_from_handle
|
|
6242
|
-
def on_connection_phy_update(self, connection, phy):
|
|
6266
|
+
def on_connection_phy_update(self, connection: Connection, phy: core.ConnectionPHY):
|
|
6243
6267
|
logger.debug(
|
|
6244
6268
|
f'*** Connection PHY Update: [0x{connection.handle:04X}] '
|
|
6245
6269
|
f'{connection.peer_address} as {connection.role_name}, '
|
|
@@ -6249,7 +6273,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6249
6273
|
|
|
6250
6274
|
@host_event_handler
|
|
6251
6275
|
@with_connection_from_handle
|
|
6252
|
-
def on_connection_phy_update_failure(self, connection, error):
|
|
6276
|
+
def on_connection_phy_update_failure(self, connection: Connection, error: int):
|
|
6253
6277
|
logger.debug(
|
|
6254
6278
|
f'*** Connection PHY Update Failed: [0x{connection.handle:04X}] '
|
|
6255
6279
|
f'{connection.peer_address} as {connection.role_name}, '
|
|
@@ -6278,7 +6302,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6278
6302
|
|
|
6279
6303
|
@host_event_handler
|
|
6280
6304
|
@with_connection_from_handle
|
|
6281
|
-
def on_connection_att_mtu_update(self, connection, att_mtu):
|
|
6305
|
+
def on_connection_att_mtu_update(self, connection: Connection, att_mtu: int):
|
|
6282
6306
|
logger.debug(
|
|
6283
6307
|
f'*** Connection ATT MTU Update: [0x{connection.handle:04X}] '
|
|
6284
6308
|
f'{connection.peer_address} as {connection.role_name}, '
|
|
@@ -6290,7 +6314,12 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6290
6314
|
@host_event_handler
|
|
6291
6315
|
@with_connection_from_handle
|
|
6292
6316
|
def on_connection_data_length_change(
|
|
6293
|
-
self,
|
|
6317
|
+
self,
|
|
6318
|
+
connection: Connection,
|
|
6319
|
+
max_tx_octets: int,
|
|
6320
|
+
max_tx_time: int,
|
|
6321
|
+
max_rx_octets: int,
|
|
6322
|
+
max_rx_time: int,
|
|
6294
6323
|
):
|
|
6295
6324
|
logger.debug(
|
|
6296
6325
|
f'*** Connection Data Length Change: [0x{connection.handle:04X}] '
|
|
@@ -6414,15 +6443,25 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6414
6443
|
|
|
6415
6444
|
# [Classic only]
|
|
6416
6445
|
@host_event_handler
|
|
6417
|
-
@
|
|
6418
|
-
def on_role_change(
|
|
6419
|
-
|
|
6420
|
-
connection
|
|
6446
|
+
@try_with_connection_from_address
|
|
6447
|
+
def on_role_change(
|
|
6448
|
+
self,
|
|
6449
|
+
connection: Optional[Connection],
|
|
6450
|
+
peer_address: hci.Address,
|
|
6451
|
+
new_role: hci.Role,
|
|
6452
|
+
):
|
|
6453
|
+
if connection:
|
|
6454
|
+
connection.role = new_role
|
|
6455
|
+
connection.emit(connection.EVENT_ROLE_CHANGE, new_role)
|
|
6456
|
+
else:
|
|
6457
|
+
logger.warning("Role change to unknown connection %s", peer_address)
|
|
6421
6458
|
|
|
6422
6459
|
# [Classic only]
|
|
6423
6460
|
@host_event_handler
|
|
6424
6461
|
@try_with_connection_from_address
|
|
6425
|
-
def on_role_change_failure(
|
|
6462
|
+
def on_role_change_failure(
|
|
6463
|
+
self, connection: Optional[Connection], address: hci.Address, error: int
|
|
6464
|
+
):
|
|
6426
6465
|
if connection:
|
|
6427
6466
|
connection.emit(connection.EVENT_ROLE_CHANGE_FAILURE, error)
|
|
6428
6467
|
self.emit(self.EVENT_ROLE_CHANGE_FAILURE, address, error)
|
|
@@ -6436,7 +6475,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6436
6475
|
# [Classic only]
|
|
6437
6476
|
@host_event_handler
|
|
6438
6477
|
@with_connection_from_address
|
|
6439
|
-
def on_classic_pairing_failure(self, connection: Connection, status) -> None:
|
|
6478
|
+
def on_classic_pairing_failure(self, connection: Connection, status: int) -> None:
|
|
6440
6479
|
connection.emit(connection.EVENT_CLASSIC_PAIRING_FAILURE, status)
|
|
6441
6480
|
|
|
6442
6481
|
def on_pairing_start(self, connection: Connection) -> None:
|
|
@@ -6460,7 +6499,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6460
6499
|
connection.emit(connection.EVENT_PAIRING_FAILURE, reason)
|
|
6461
6500
|
|
|
6462
6501
|
@with_connection_from_handle
|
|
6463
|
-
def on_gatt_pdu(self, connection, pdu):
|
|
6502
|
+
def on_gatt_pdu(self, connection: Connection, pdu: bytes):
|
|
6464
6503
|
# Parse the L2CAP payload into an ATT PDU object
|
|
6465
6504
|
att_pdu = ATT_PDU.from_bytes(pdu)
|
|
6466
6505
|
|
|
@@ -6482,7 +6521,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6482
6521
|
connection.gatt_server.on_gatt_pdu(connection, att_pdu)
|
|
6483
6522
|
|
|
6484
6523
|
@with_connection_from_handle
|
|
6485
|
-
def on_smp_pdu(self, connection, pdu):
|
|
6524
|
+
def on_smp_pdu(self, connection: Connection, pdu: bytes):
|
|
6486
6525
|
self.smp_manager.on_smp_pdu(connection, pdu)
|
|
6487
6526
|
|
|
6488
6527
|
@host_event_handler
|