bumble 0.0.214__py3-none-any.whl → 0.0.215__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 +280 -278
- 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.215.dist-info}/METADATA +2 -1
- bumble-0.0.215.dist-info/RECORD +183 -0
- bumble-0.0.214.dist-info/RECORD +0 -182
- {bumble-0.0.214.dist-info → bumble-0.0.215.dist-info}/WHEEL +0 -0
- {bumble-0.0.214.dist-info → bumble-0.0.215.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.214.dist-info → bumble-0.0.215.dist-info}/licenses/LICENSE +0 -0
- {bumble-0.0.214.dist-info → bumble-0.0.215.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,9 +2169,7 @@ 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(self, address, *args, **kwargs):
|
|
2193
|
-
if connection := self.pending_connections.get(address, False):
|
|
2194
|
-
return function(self, connection, *args, **kwargs)
|
|
2172
|
+
def wrapper(self, address: hci.Address, *args, **kwargs):
|
|
2195
2173
|
for connection in self.connections.values():
|
|
2196
2174
|
if connection.peer_address == address:
|
|
2197
2175
|
return function(self, connection, *args, **kwargs)
|
|
@@ -2205,8 +2183,6 @@ def with_connection_from_address(function):
|
|
|
2205
2183
|
def try_with_connection_from_address(function):
|
|
2206
2184
|
@functools.wraps(function)
|
|
2207
2185
|
def wrapper(self, address, *args, **kwargs):
|
|
2208
|
-
if connection := self.pending_connections.get(address, False):
|
|
2209
|
-
return function(self, connection, address, *args, **kwargs)
|
|
2210
2186
|
for connection in self.connections.values():
|
|
2211
2187
|
if connection.peer_address == address:
|
|
2212
2188
|
return function(self, connection, address, *args, **kwargs)
|
|
@@ -2258,7 +2234,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
2258
2234
|
scan_response_data: bytes
|
|
2259
2235
|
cs_capabilities: ChannelSoundingCapabilities | None = None
|
|
2260
2236
|
connections: dict[int, Connection]
|
|
2261
|
-
|
|
2237
|
+
connection_roles: dict[hci.Address, hci.Role]
|
|
2262
2238
|
classic_pending_accepts: dict[
|
|
2263
2239
|
hci.Address,
|
|
2264
2240
|
list[asyncio.Future[Union[Connection, tuple[hci.Address, int, int]]]],
|
|
@@ -2380,7 +2356,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
2380
2356
|
self.le_connecting = False
|
|
2381
2357
|
self.disconnecting = False
|
|
2382
2358
|
self.connections = {} # Connections, by connection handle
|
|
2383
|
-
self.
|
|
2359
|
+
self.connection_roles = (
|
|
2360
|
+
{}
|
|
2361
|
+
) # Local connection roles, by BD address (BR/EDR only)
|
|
2384
2362
|
self.sco_links = {} # ScoLinks, by connection handle (BR/EDR only)
|
|
2385
2363
|
self.cis_links = {} # CisLinks, by connection handle (LE only)
|
|
2386
2364
|
self._pending_cis = {} # (CIS_ID, CIG_ID), by CIS_handle
|
|
@@ -2608,36 +2586,6 @@ class Device(utils.CompositeEventEmitter):
|
|
|
2608
2586
|
None,
|
|
2609
2587
|
)
|
|
2610
2588
|
|
|
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
2589
|
@overload
|
|
2642
2590
|
async def create_l2cap_channel(
|
|
2643
2591
|
self,
|
|
@@ -2705,7 +2653,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
2705
2653
|
def send_l2cap_pdu(self, connection_handle: int, cid: int, pdu: bytes) -> None:
|
|
2706
2654
|
self.host.send_l2cap_pdu(connection_handle, cid, pdu)
|
|
2707
2655
|
|
|
2708
|
-
async def send_command(self, command, check_result=False):
|
|
2656
|
+
async def send_command(self, command: hci.HCI_Command, check_result: bool = False):
|
|
2709
2657
|
try:
|
|
2710
2658
|
return await asyncio.wait_for(
|
|
2711
2659
|
self.host.send_command(command, check_result), self.command_timeout
|
|
@@ -2962,13 +2910,13 @@ class Device(utils.CompositeEventEmitter):
|
|
|
2962
2910
|
def supports_le_features(self, feature: hci.LeFeatureMask) -> bool:
|
|
2963
2911
|
return self.host.supports_le_features(feature)
|
|
2964
2912
|
|
|
2965
|
-
def supports_le_phy(self, phy:
|
|
2966
|
-
if phy == hci.
|
|
2913
|
+
def supports_le_phy(self, phy: hci.Phy) -> bool:
|
|
2914
|
+
if phy == hci.Phy.LE_1M:
|
|
2967
2915
|
return True
|
|
2968
2916
|
|
|
2969
|
-
feature_map: dict[
|
|
2970
|
-
hci.
|
|
2971
|
-
hci.
|
|
2917
|
+
feature_map: dict[hci.Phy, hci.LeFeatureMask] = {
|
|
2918
|
+
hci.Phy.LE_2M: hci.LeFeatureMask.LE_2M_PHY,
|
|
2919
|
+
hci.Phy.LE_CODED: hci.LeFeatureMask.LE_CODED_PHY,
|
|
2972
2920
|
}
|
|
2973
2921
|
if phy not in feature_map:
|
|
2974
2922
|
raise InvalidArgumentError('invalid PHY')
|
|
@@ -3245,8 +3193,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3245
3193
|
else 0
|
|
3246
3194
|
)
|
|
3247
3195
|
await advertising_set.start(duration=duration)
|
|
3248
|
-
except Exception
|
|
3249
|
-
logger.exception(
|
|
3196
|
+
except Exception:
|
|
3197
|
+
logger.exception('failed to start advertising set')
|
|
3250
3198
|
await advertising_set.remove()
|
|
3251
3199
|
raise
|
|
3252
3200
|
|
|
@@ -3572,7 +3520,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3572
3520
|
self.discovering = False
|
|
3573
3521
|
|
|
3574
3522
|
@host_event_handler
|
|
3575
|
-
def on_inquiry_result(
|
|
3523
|
+
def on_inquiry_result(
|
|
3524
|
+
self, address: hci.Address, class_of_device: int, data: bytes, rssi: int
|
|
3525
|
+
):
|
|
3576
3526
|
self.emit(
|
|
3577
3527
|
self.EVENT_INQUIRY_RESULT,
|
|
3578
3528
|
address,
|
|
@@ -3581,7 +3531,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3581
3531
|
rssi,
|
|
3582
3532
|
)
|
|
3583
3533
|
|
|
3584
|
-
async def set_scan_enable(
|
|
3534
|
+
async def set_scan_enable(
|
|
3535
|
+
self, inquiry_scan_enabled: bool, page_scan_enabled: bool
|
|
3536
|
+
):
|
|
3585
3537
|
if inquiry_scan_enabled and page_scan_enabled:
|
|
3586
3538
|
scan_enable = 0x03
|
|
3587
3539
|
elif page_scan_enabled:
|
|
@@ -3601,14 +3553,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3601
3553
|
# Synthesize an inquiry response if none is set already
|
|
3602
3554
|
if self.inquiry_response is None:
|
|
3603
3555
|
self.inquiry_response = bytes(
|
|
3604
|
-
AdvertisingData(
|
|
3605
|
-
[
|
|
3606
|
-
(
|
|
3607
|
-
AdvertisingData.COMPLETE_LOCAL_NAME,
|
|
3608
|
-
bytes(self.name, 'utf-8'),
|
|
3609
|
-
)
|
|
3610
|
-
]
|
|
3611
|
-
)
|
|
3556
|
+
AdvertisingData([data_types.CompleteLocalName(self.name)])
|
|
3612
3557
|
)
|
|
3613
3558
|
|
|
3614
3559
|
# Update the controller
|
|
@@ -3714,6 +3659,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3714
3659
|
# If the address is not parsable, assume it is a name instead
|
|
3715
3660
|
always_resolve = False
|
|
3716
3661
|
logger.debug('looking for peer by name')
|
|
3662
|
+
assert isinstance(peer_address, str)
|
|
3717
3663
|
peer_address = await self.find_peer_by_name(
|
|
3718
3664
|
peer_address, transport
|
|
3719
3665
|
) # TODO: timeout
|
|
@@ -3741,7 +3687,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3741
3687
|
):
|
|
3742
3688
|
pending_connection.set_result(connection)
|
|
3743
3689
|
|
|
3744
|
-
def on_connection_failure(error):
|
|
3690
|
+
def on_connection_failure(error: core.ConnectionError):
|
|
3745
3691
|
if transport == PhysicalTransport.LE or (
|
|
3746
3692
|
# match BR/EDR connection failure event against peer address
|
|
3747
3693
|
error.transport == transport
|
|
@@ -3881,9 +3827,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3881
3827
|
)
|
|
3882
3828
|
else:
|
|
3883
3829
|
# Save pending connection
|
|
3884
|
-
self.
|
|
3885
|
-
self, peer_address, hci.Role.CENTRAL
|
|
3886
|
-
)
|
|
3830
|
+
self.connection_roles[peer_address] = hci.Role.CENTRAL
|
|
3887
3831
|
|
|
3888
3832
|
# TODO: allow passing other settings
|
|
3889
3833
|
result = await self.send_command(
|
|
@@ -3936,7 +3880,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3936
3880
|
self.le_connecting = False
|
|
3937
3881
|
self.connect_own_address_type = None
|
|
3938
3882
|
else:
|
|
3939
|
-
self.
|
|
3883
|
+
self.connection_roles.pop(peer_address, None)
|
|
3940
3884
|
|
|
3941
3885
|
async def accept(
|
|
3942
3886
|
self,
|
|
@@ -3961,6 +3905,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3961
3905
|
except InvalidArgumentError:
|
|
3962
3906
|
# If the address is not parsable, assume it is a name instead
|
|
3963
3907
|
logger.debug('looking for peer by name')
|
|
3908
|
+
assert isinstance(peer_address, str)
|
|
3964
3909
|
peer_address = await self.find_peer_by_name(
|
|
3965
3910
|
peer_address, PhysicalTransport.BR_EDR
|
|
3966
3911
|
) # TODO: timeout
|
|
@@ -4019,7 +3964,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4019
3964
|
):
|
|
4020
3965
|
pending_connection.set_result(connection)
|
|
4021
3966
|
|
|
4022
|
-
def on_connection_failure(error):
|
|
3967
|
+
def on_connection_failure(error: core.ConnectionError):
|
|
4023
3968
|
if (
|
|
4024
3969
|
error.transport == PhysicalTransport.BR_EDR
|
|
4025
3970
|
and error.peer_address == peer_address
|
|
@@ -4029,13 +3974,11 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4029
3974
|
self.on(self.EVENT_CONNECTION, on_connection)
|
|
4030
3975
|
self.on(self.EVENT_CONNECTION_FAILURE, on_connection_failure)
|
|
4031
3976
|
|
|
4032
|
-
# Save
|
|
3977
|
+
# Save Peripheral hci.role.
|
|
4033
3978
|
# Even if we requested a role switch in the hci.HCI_Accept_Connection_Request
|
|
4034
3979
|
# command, this connection is still considered Peripheral until an eventual
|
|
4035
3980
|
# role change event.
|
|
4036
|
-
self.
|
|
4037
|
-
self, peer_address, hci.Role.PERIPHERAL
|
|
4038
|
-
)
|
|
3981
|
+
self.connection_roles[peer_address] = hci.Role.PERIPHERAL
|
|
4039
3982
|
|
|
4040
3983
|
try:
|
|
4041
3984
|
# Accept connection request
|
|
@@ -4053,10 +3996,10 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4053
3996
|
finally:
|
|
4054
3997
|
self.remove_listener(self.EVENT_CONNECTION, on_connection)
|
|
4055
3998
|
self.remove_listener(self.EVENT_CONNECTION_FAILURE, on_connection_failure)
|
|
4056
|
-
self.
|
|
3999
|
+
self.connection_roles.pop(peer_address, None)
|
|
4057
4000
|
|
|
4058
4001
|
@asynccontextmanager
|
|
4059
|
-
async def connect_as_gatt(self, peer_address):
|
|
4002
|
+
async def connect_as_gatt(self, peer_address: Union[hci.Address, str]):
|
|
4060
4003
|
async with AsyncExitStack() as stack:
|
|
4061
4004
|
connection = await stack.enter_async_context(
|
|
4062
4005
|
await self.connect(peer_address)
|
|
@@ -4092,6 +4035,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4092
4035
|
except InvalidArgumentError:
|
|
4093
4036
|
# If the address is not parsable, assume it is a name instead
|
|
4094
4037
|
logger.debug('looking for peer by name')
|
|
4038
|
+
assert isinstance(peer_address, str)
|
|
4095
4039
|
peer_address = await self.find_peer_by_name(
|
|
4096
4040
|
peer_address, PhysicalTransport.BR_EDR
|
|
4097
4041
|
) # TODO: timeout
|
|
@@ -4137,7 +4081,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4137
4081
|
)
|
|
4138
4082
|
self.disconnecting = False
|
|
4139
4083
|
|
|
4140
|
-
async def set_data_length(
|
|
4084
|
+
async def set_data_length(
|
|
4085
|
+
self, connection: Connection, tx_octets: int, tx_time: int
|
|
4086
|
+
) -> None:
|
|
4141
4087
|
if tx_octets < 0x001B or tx_octets > 0x00FB:
|
|
4142
4088
|
raise InvalidArgumentError('tx_octets must be between 0x001B and 0x00FB')
|
|
4143
4089
|
|
|
@@ -4240,7 +4186,11 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4240
4186
|
)
|
|
4241
4187
|
|
|
4242
4188
|
async def set_connection_phy(
|
|
4243
|
-
self,
|
|
4189
|
+
self,
|
|
4190
|
+
connection: Connection,
|
|
4191
|
+
tx_phys: Optional[Iterable[hci.Phy]] = None,
|
|
4192
|
+
rx_phys: Optional[Iterable[hci.Phy]] = None,
|
|
4193
|
+
phy_options: int = 0,
|
|
4244
4194
|
):
|
|
4245
4195
|
if not self.host.supports_command(hci.HCI_LE_SET_PHY_COMMAND):
|
|
4246
4196
|
logger.warning('ignoring request, command not supported')
|
|
@@ -4256,7 +4206,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4256
4206
|
all_phys=all_phys_bits,
|
|
4257
4207
|
tx_phys=hci.phy_list_to_bits(tx_phys),
|
|
4258
4208
|
rx_phys=hci.phy_list_to_bits(rx_phys),
|
|
4259
|
-
phy_options=
|
|
4209
|
+
phy_options=phy_options,
|
|
4260
4210
|
)
|
|
4261
4211
|
)
|
|
4262
4212
|
|
|
@@ -4267,7 +4217,11 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4267
4217
|
)
|
|
4268
4218
|
raise hci.HCI_StatusError(result)
|
|
4269
4219
|
|
|
4270
|
-
async def set_default_phy(
|
|
4220
|
+
async def set_default_phy(
|
|
4221
|
+
self,
|
|
4222
|
+
tx_phys: Optional[Iterable[hci.Phy]] = None,
|
|
4223
|
+
rx_phys: Optional[Iterable[hci.Phy]] = None,
|
|
4224
|
+
):
|
|
4271
4225
|
all_phys_bits = (1 if tx_phys is None else 0) | (
|
|
4272
4226
|
(1 if rx_phys is None else 0) << 1
|
|
4273
4227
|
)
|
|
@@ -4305,7 +4259,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4305
4259
|
check_result=True,
|
|
4306
4260
|
)
|
|
4307
4261
|
|
|
4308
|
-
async def find_peer_by_name(self, name, transport=PhysicalTransport.LE):
|
|
4262
|
+
async def find_peer_by_name(self, name: str, transport=PhysicalTransport.LE):
|
|
4309
4263
|
"""
|
|
4310
4264
|
Scan for a peer with a given name and return its address.
|
|
4311
4265
|
"""
|
|
@@ -4320,7 +4274,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4320
4274
|
if local_name == name:
|
|
4321
4275
|
peer_address.set_result(address)
|
|
4322
4276
|
|
|
4323
|
-
listener = None
|
|
4277
|
+
listener: Optional[Callable[..., None]] = None
|
|
4324
4278
|
was_scanning = self.scanning
|
|
4325
4279
|
was_discovering = self.discovering
|
|
4326
4280
|
try:
|
|
@@ -4426,10 +4380,10 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4426
4380
|
def smp_session_proxy(self, session_proxy: type[smp.Session]) -> None:
|
|
4427
4381
|
self.smp_manager.session_proxy = session_proxy
|
|
4428
4382
|
|
|
4429
|
-
async def pair(self, connection):
|
|
4383
|
+
async def pair(self, connection: Connection):
|
|
4430
4384
|
return await self.smp_manager.pair(connection)
|
|
4431
4385
|
|
|
4432
|
-
def request_pairing(self, connection):
|
|
4386
|
+
def request_pairing(self, connection: Connection):
|
|
4433
4387
|
return self.smp_manager.request_pairing(connection)
|
|
4434
4388
|
|
|
4435
4389
|
async def get_long_term_key(
|
|
@@ -4517,7 +4471,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4517
4471
|
on_authentication_failure,
|
|
4518
4472
|
)
|
|
4519
4473
|
|
|
4520
|
-
async def encrypt(self, connection, enable=True):
|
|
4474
|
+
async def encrypt(self, connection: Connection, enable: bool = True):
|
|
4521
4475
|
if not enable and connection.transport == PhysicalTransport.LE:
|
|
4522
4476
|
raise InvalidArgumentError('`enable` parameter is classic only.')
|
|
4523
4477
|
|
|
@@ -4527,7 +4481,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4527
4481
|
def on_encryption_change():
|
|
4528
4482
|
pending_encryption.set_result(None)
|
|
4529
4483
|
|
|
4530
|
-
def on_encryption_failure(error_code):
|
|
4484
|
+
def on_encryption_failure(error_code: int):
|
|
4531
4485
|
pending_encryption.set_exception(hci.HCI_Error(error_code))
|
|
4532
4486
|
|
|
4533
4487
|
connection.on(
|
|
@@ -4610,8 +4564,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4610
4564
|
try:
|
|
4611
4565
|
await self.keystore.update(address, keys)
|
|
4612
4566
|
await self.refresh_resolving_list()
|
|
4613
|
-
except Exception
|
|
4614
|
-
logger.
|
|
4567
|
+
except Exception:
|
|
4568
|
+
logger.exception('!!! error while storing keys')
|
|
4615
4569
|
else:
|
|
4616
4570
|
self.emit(self.EVENT_KEY_STORE_UPDATE)
|
|
4617
4571
|
|
|
@@ -4619,10 +4573,10 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4619
4573
|
async def switch_role(self, connection: Connection, role: hci.Role):
|
|
4620
4574
|
pending_role_change = asyncio.get_running_loop().create_future()
|
|
4621
4575
|
|
|
4622
|
-
def on_role_change(new_role):
|
|
4576
|
+
def on_role_change(new_role: hci.Role):
|
|
4623
4577
|
pending_role_change.set_result(new_role)
|
|
4624
4578
|
|
|
4625
|
-
def on_role_change_failure(error_code):
|
|
4579
|
+
def on_role_change_failure(error_code: int):
|
|
4626
4580
|
pending_role_change.set_exception(hci.HCI_Error(error_code))
|
|
4627
4581
|
|
|
4628
4582
|
connection.on(connection.EVENT_ROLE_CHANGE, on_role_change)
|
|
@@ -5212,10 +5166,10 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5212
5166
|
):
|
|
5213
5167
|
connection.emit(connection.EVENT_LINK_KEY)
|
|
5214
5168
|
|
|
5215
|
-
def add_service(self, service):
|
|
5169
|
+
def add_service(self, service: gatt.Service):
|
|
5216
5170
|
self.gatt_server.add_service(service)
|
|
5217
5171
|
|
|
5218
|
-
def add_services(self, services):
|
|
5172
|
+
def add_services(self, services: Iterable[gatt.Service]):
|
|
5219
5173
|
self.gatt_server.add_services(services)
|
|
5220
5174
|
|
|
5221
5175
|
def add_default_services(
|
|
@@ -5311,10 +5265,10 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5311
5265
|
@host_event_handler
|
|
5312
5266
|
def on_advertising_set_termination(
|
|
5313
5267
|
self,
|
|
5314
|
-
status,
|
|
5315
|
-
advertising_handle,
|
|
5316
|
-
connection_handle,
|
|
5317
|
-
number_of_completed_extended_advertising_events,
|
|
5268
|
+
status: int,
|
|
5269
|
+
advertising_handle: int,
|
|
5270
|
+
connection_handle: int,
|
|
5271
|
+
number_of_completed_extended_advertising_events: int,
|
|
5318
5272
|
):
|
|
5319
5273
|
# Legacy advertising set is also one of extended advertising sets.
|
|
5320
5274
|
if not (
|
|
@@ -5482,15 +5436,49 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5482
5436
|
self.emit(self.EVENT_CONNECTION, connection)
|
|
5483
5437
|
|
|
5484
5438
|
@host_event_handler
|
|
5485
|
-
def
|
|
5439
|
+
def on_classic_connection(
|
|
5440
|
+
self,
|
|
5441
|
+
connection_handle: int,
|
|
5442
|
+
peer_address: hci.Address,
|
|
5443
|
+
) -> None:
|
|
5444
|
+
connection_role = self.connection_roles.pop(peer_address, hci.Role.PERIPHERAL)
|
|
5445
|
+
|
|
5446
|
+
logger.debug(
|
|
5447
|
+
f'*** Connection: [0x{connection_handle:04X}] '
|
|
5448
|
+
f'{peer_address} {hci.HCI_Constant.role_name(connection_role)}'
|
|
5449
|
+
)
|
|
5450
|
+
if connection_handle in self.connections:
|
|
5451
|
+
logger.warning(
|
|
5452
|
+
'new connection reuses the same handle as a previous connection'
|
|
5453
|
+
)
|
|
5454
|
+
|
|
5455
|
+
# Create a new connection
|
|
5456
|
+
connection = Connection(
|
|
5457
|
+
device=self,
|
|
5458
|
+
handle=connection_handle,
|
|
5459
|
+
transport=PhysicalTransport.BR_EDR,
|
|
5460
|
+
self_address=self.public_address,
|
|
5461
|
+
self_resolvable_address=None,
|
|
5462
|
+
peer_address=peer_address,
|
|
5463
|
+
peer_resolvable_address=None,
|
|
5464
|
+
role=connection_role,
|
|
5465
|
+
parameters=Connection.Parameters(0.0, 0, 0.0),
|
|
5466
|
+
)
|
|
5467
|
+
self.connections[connection_handle] = connection
|
|
5468
|
+
|
|
5469
|
+
self.emit(self.EVENT_CONNECTION, connection)
|
|
5470
|
+
|
|
5471
|
+
@host_event_handler
|
|
5472
|
+
def on_le_connection(
|
|
5486
5473
|
self,
|
|
5487
5474
|
connection_handle: int,
|
|
5488
|
-
transport: core.PhysicalTransport,
|
|
5489
5475
|
peer_address: hci.Address,
|
|
5490
5476
|
self_resolvable_address: Optional[hci.Address],
|
|
5491
5477
|
peer_resolvable_address: Optional[hci.Address],
|
|
5492
5478
|
role: hci.Role,
|
|
5493
|
-
|
|
5479
|
+
connection_interval: int,
|
|
5480
|
+
peripheral_latency: int,
|
|
5481
|
+
supervision_timeout: int,
|
|
5494
5482
|
) -> None:
|
|
5495
5483
|
# Convert all-zeros addresses into None.
|
|
5496
5484
|
if self_resolvable_address == hci.Address.ANY_RANDOM:
|
|
@@ -5510,19 +5498,6 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5510
5498
|
'new connection reuses the same handle as a previous connection'
|
|
5511
5499
|
)
|
|
5512
5500
|
|
|
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
5501
|
if peer_resolvable_address is None:
|
|
5527
5502
|
# Resolve the peer address if we can
|
|
5528
5503
|
if self.address_resolver:
|
|
@@ -5572,16 +5547,16 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5572
5547
|
connection = Connection(
|
|
5573
5548
|
self,
|
|
5574
5549
|
connection_handle,
|
|
5575
|
-
|
|
5550
|
+
PhysicalTransport.LE,
|
|
5576
5551
|
self_address,
|
|
5577
5552
|
self_resolvable_address,
|
|
5578
5553
|
peer_address,
|
|
5579
5554
|
peer_resolvable_address,
|
|
5580
5555
|
role,
|
|
5581
5556
|
Connection.Parameters(
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5557
|
+
connection_interval * 1.25,
|
|
5558
|
+
peripheral_latency,
|
|
5559
|
+
supervision_timeout * 10.0,
|
|
5585
5560
|
),
|
|
5586
5561
|
)
|
|
5587
5562
|
self.connections[connection_handle] = connection
|
|
@@ -5613,7 +5588,12 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5613
5588
|
)
|
|
5614
5589
|
|
|
5615
5590
|
@host_event_handler
|
|
5616
|
-
def on_connection_failure(
|
|
5591
|
+
def on_connection_failure(
|
|
5592
|
+
self,
|
|
5593
|
+
transport: hci.PhysicalTransport,
|
|
5594
|
+
peer_address: hci.Address,
|
|
5595
|
+
error_code: int,
|
|
5596
|
+
):
|
|
5617
5597
|
logger.debug(
|
|
5618
5598
|
f'*** Connection failed: {hci.HCI_Constant.error_name(error_code)}'
|
|
5619
5599
|
)
|
|
@@ -5667,9 +5647,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5667
5647
|
# device configuration is set to accept any incoming connection
|
|
5668
5648
|
elif self.classic_accept_any:
|
|
5669
5649
|
# Save pending connection
|
|
5670
|
-
self.
|
|
5671
|
-
self, bd_addr, hci.Role.PERIPHERAL
|
|
5672
|
-
)
|
|
5650
|
+
self.connection_roles[bd_addr] = hci.Role.PERIPHERAL
|
|
5673
5651
|
|
|
5674
5652
|
self.host.send_command_sync(
|
|
5675
5653
|
hci.HCI_Accept_Connection_Request_Command(
|
|
@@ -5732,7 +5710,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5732
5710
|
|
|
5733
5711
|
@host_event_handler
|
|
5734
5712
|
@with_connection_from_handle
|
|
5735
|
-
def on_connection_authentication(self, connection):
|
|
5713
|
+
def on_connection_authentication(self, connection: Connection):
|
|
5736
5714
|
logger.debug(
|
|
5737
5715
|
f'*** Connection Authentication: [0x{connection.handle:04X}] '
|
|
5738
5716
|
f'{connection.peer_address} as {connection.role_name}'
|
|
@@ -5742,7 +5720,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5742
5720
|
|
|
5743
5721
|
@host_event_handler
|
|
5744
5722
|
@with_connection_from_handle
|
|
5745
|
-
def on_connection_authentication_failure(
|
|
5723
|
+
def on_connection_authentication_failure(
|
|
5724
|
+
self, connection: Connection, error: core.ConnectionError
|
|
5725
|
+
):
|
|
5746
5726
|
logger.debug(
|
|
5747
5727
|
f'*** Connection Authentication Failure: [0x{connection.handle:04X}] '
|
|
5748
5728
|
f'{connection.peer_address} as {connection.role_name}, error={error}'
|
|
@@ -5784,10 +5764,13 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5784
5764
|
@host_event_handler
|
|
5785
5765
|
@with_connection_from_address
|
|
5786
5766
|
def on_authentication_io_capability_response(
|
|
5787
|
-
self,
|
|
5767
|
+
self,
|
|
5768
|
+
connection: Connection,
|
|
5769
|
+
io_capability: int,
|
|
5770
|
+
authentication_requirements: int,
|
|
5788
5771
|
):
|
|
5789
|
-
connection.
|
|
5790
|
-
connection.
|
|
5772
|
+
connection.pairing_peer_io_capability = io_capability
|
|
5773
|
+
connection.pairing_peer_authentication_requirements = (
|
|
5791
5774
|
authentication_requirements
|
|
5792
5775
|
)
|
|
5793
5776
|
|
|
@@ -5798,7 +5781,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5798
5781
|
# Ask what the pairing config should be for this connection
|
|
5799
5782
|
pairing_config = self.pairing_config_factory(connection)
|
|
5800
5783
|
io_capability = pairing_config.delegate.classic_io_capability
|
|
5801
|
-
peer_io_capability = connection.
|
|
5784
|
+
peer_io_capability = connection.pairing_peer_io_capability
|
|
5802
5785
|
|
|
5803
5786
|
async def confirm() -> bool:
|
|
5804
5787
|
# Ask the user to confirm the pairing, without display
|
|
@@ -5859,8 +5842,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5859
5842
|
)
|
|
5860
5843
|
)
|
|
5861
5844
|
return
|
|
5862
|
-
except Exception
|
|
5863
|
-
logger.
|
|
5845
|
+
except Exception:
|
|
5846
|
+
logger.exception('exception while confirming')
|
|
5864
5847
|
|
|
5865
5848
|
await self.host.send_command(
|
|
5866
5849
|
hci.HCI_User_Confirmation_Request_Negative_Reply_Command(
|
|
@@ -5873,7 +5856,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5873
5856
|
# [Classic only]
|
|
5874
5857
|
@host_event_handler
|
|
5875
5858
|
@with_connection_from_address
|
|
5876
|
-
def on_authentication_user_passkey_request(self, connection) -> None:
|
|
5859
|
+
def on_authentication_user_passkey_request(self, connection: Connection) -> None:
|
|
5877
5860
|
# Ask what the pairing config should be for this connection
|
|
5878
5861
|
pairing_config = self.pairing_config_factory(connection)
|
|
5879
5862
|
|
|
@@ -5889,8 +5872,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5889
5872
|
)
|
|
5890
5873
|
)
|
|
5891
5874
|
return
|
|
5892
|
-
except Exception
|
|
5893
|
-
logger.
|
|
5875
|
+
except Exception:
|
|
5876
|
+
logger.exception('exception while asking for pass-key')
|
|
5894
5877
|
|
|
5895
5878
|
await self.host.send_command(
|
|
5896
5879
|
hci.HCI_User_Passkey_Request_Negative_Reply_Command(
|
|
@@ -5916,7 +5899,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5916
5899
|
# [Classic only]
|
|
5917
5900
|
@host_event_handler
|
|
5918
5901
|
@with_connection_from_address
|
|
5919
|
-
def on_pin_code_request(self, connection):
|
|
5902
|
+
def on_pin_code_request(self, connection: Connection):
|
|
5920
5903
|
# Classic legacy pairing
|
|
5921
5904
|
# Ask what the pairing config should be for this connection
|
|
5922
5905
|
pairing_config = self.pairing_config_factory(connection)
|
|
@@ -5960,7 +5943,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5960
5943
|
# [Classic only]
|
|
5961
5944
|
@host_event_handler
|
|
5962
5945
|
@with_connection_from_address
|
|
5963
|
-
def on_authentication_user_passkey_notification(
|
|
5946
|
+
def on_authentication_user_passkey_notification(
|
|
5947
|
+
self, connection: Connection, passkey: int
|
|
5948
|
+
):
|
|
5964
5949
|
# Ask what the pairing config should be for this connection
|
|
5965
5950
|
pairing_config = self.pairing_config_factory(connection)
|
|
5966
5951
|
|
|
@@ -5972,14 +5957,15 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5972
5957
|
# [Classic only]
|
|
5973
5958
|
@host_event_handler
|
|
5974
5959
|
@try_with_connection_from_address
|
|
5975
|
-
def on_remote_name(
|
|
5960
|
+
def on_remote_name(
|
|
5961
|
+
self, connection: Connection, address: hci.Address, remote_name: bytes
|
|
5962
|
+
):
|
|
5976
5963
|
# Try to decode the name
|
|
5977
5964
|
try:
|
|
5978
|
-
remote_name = remote_name.decode('utf-8')
|
|
5979
5965
|
if connection:
|
|
5980
|
-
connection.peer_name = remote_name
|
|
5966
|
+
connection.peer_name = remote_name.decode('utf-8')
|
|
5981
5967
|
connection.emit(connection.EVENT_REMOTE_NAME)
|
|
5982
|
-
self.emit(self.EVENT_REMOTE_NAME, address, remote_name)
|
|
5968
|
+
self.emit(self.EVENT_REMOTE_NAME, address, remote_name.decode('utf-8'))
|
|
5983
5969
|
except UnicodeDecodeError as error:
|
|
5984
5970
|
logger.warning('peer name is not valid UTF-8')
|
|
5985
5971
|
if connection:
|
|
@@ -5990,7 +5976,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5990
5976
|
# [Classic only]
|
|
5991
5977
|
@host_event_handler
|
|
5992
5978
|
@try_with_connection_from_address
|
|
5993
|
-
def on_remote_name_failure(
|
|
5979
|
+
def on_remote_name_failure(
|
|
5980
|
+
self, connection: Connection, address: hci.Address, error: int
|
|
5981
|
+
):
|
|
5994
5982
|
if connection:
|
|
5995
5983
|
connection.emit(connection.EVENT_REMOTE_NAME_FAILURE, error)
|
|
5996
5984
|
self.emit(self.EVENT_REMOTE_NAME_FAILURE, address, error)
|
|
@@ -6191,7 +6179,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6191
6179
|
|
|
6192
6180
|
@host_event_handler
|
|
6193
6181
|
@with_connection_from_handle
|
|
6194
|
-
def on_connection_encryption_key_refresh(self, connection):
|
|
6182
|
+
def on_connection_encryption_key_refresh(self, connection: Connection):
|
|
6195
6183
|
logger.debug(
|
|
6196
6184
|
f'*** Connection Key Refresh: [0x{connection.handle:04X}] '
|
|
6197
6185
|
f'{connection.peer_address} as {connection.role_name}'
|
|
@@ -6201,27 +6189,27 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6201
6189
|
@host_event_handler
|
|
6202
6190
|
@with_connection_from_handle
|
|
6203
6191
|
def on_connection_parameters_update(
|
|
6204
|
-
self,
|
|
6192
|
+
self,
|
|
6193
|
+
connection: Connection,
|
|
6194
|
+
connection_interval: int,
|
|
6195
|
+
peripheral_latency: int,
|
|
6196
|
+
supervision_timeout: int,
|
|
6205
6197
|
):
|
|
6206
6198
|
logger.debug(
|
|
6207
6199
|
f'*** Connection Parameters Update: [0x{connection.handle:04X}] '
|
|
6208
6200
|
f'{connection.peer_address} as {connection.role_name}, '
|
|
6209
|
-
f'{connection_parameters}'
|
|
6210
6201
|
)
|
|
6211
|
-
if
|
|
6212
|
-
connection.parameters.connection_interval
|
|
6213
|
-
!= connection_parameters.connection_interval * 1.25
|
|
6214
|
-
):
|
|
6202
|
+
if connection.parameters.connection_interval != connection_interval * 1.25:
|
|
6215
6203
|
connection.parameters = Connection.Parameters(
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6204
|
+
connection_interval * 1.25,
|
|
6205
|
+
peripheral_latency,
|
|
6206
|
+
supervision_timeout * 10.0,
|
|
6219
6207
|
)
|
|
6220
6208
|
else:
|
|
6221
6209
|
connection.parameters = Connection.Parameters(
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
|
|
6210
|
+
connection_interval * 1.25,
|
|
6211
|
+
peripheral_latency,
|
|
6212
|
+
supervision_timeout * 10.0,
|
|
6225
6213
|
connection.parameters.subrate_factor,
|
|
6226
6214
|
connection.parameters.continuation_number,
|
|
6227
6215
|
)
|
|
@@ -6229,7 +6217,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6229
6217
|
|
|
6230
6218
|
@host_event_handler
|
|
6231
6219
|
@with_connection_from_handle
|
|
6232
|
-
def on_connection_parameters_update_failure(
|
|
6220
|
+
def on_connection_parameters_update_failure(
|
|
6221
|
+
self, connection: Connection, error: int
|
|
6222
|
+
):
|
|
6233
6223
|
logger.debug(
|
|
6234
6224
|
f'*** Connection Parameters Update Failed: [0x{connection.handle:04X}] '
|
|
6235
6225
|
f'{connection.peer_address} as {connection.role_name}, '
|
|
@@ -6239,7 +6229,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6239
6229
|
|
|
6240
6230
|
@host_event_handler
|
|
6241
6231
|
@with_connection_from_handle
|
|
6242
|
-
def on_connection_phy_update(self, connection, phy):
|
|
6232
|
+
def on_connection_phy_update(self, connection: Connection, phy: core.ConnectionPHY):
|
|
6243
6233
|
logger.debug(
|
|
6244
6234
|
f'*** Connection PHY Update: [0x{connection.handle:04X}] '
|
|
6245
6235
|
f'{connection.peer_address} as {connection.role_name}, '
|
|
@@ -6249,7 +6239,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6249
6239
|
|
|
6250
6240
|
@host_event_handler
|
|
6251
6241
|
@with_connection_from_handle
|
|
6252
|
-
def on_connection_phy_update_failure(self, connection, error):
|
|
6242
|
+
def on_connection_phy_update_failure(self, connection: Connection, error: int):
|
|
6253
6243
|
logger.debug(
|
|
6254
6244
|
f'*** Connection PHY Update Failed: [0x{connection.handle:04X}] '
|
|
6255
6245
|
f'{connection.peer_address} as {connection.role_name}, '
|
|
@@ -6278,7 +6268,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6278
6268
|
|
|
6279
6269
|
@host_event_handler
|
|
6280
6270
|
@with_connection_from_handle
|
|
6281
|
-
def on_connection_att_mtu_update(self, connection, att_mtu):
|
|
6271
|
+
def on_connection_att_mtu_update(self, connection: Connection, att_mtu: int):
|
|
6282
6272
|
logger.debug(
|
|
6283
6273
|
f'*** Connection ATT MTU Update: [0x{connection.handle:04X}] '
|
|
6284
6274
|
f'{connection.peer_address} as {connection.role_name}, '
|
|
@@ -6290,7 +6280,12 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6290
6280
|
@host_event_handler
|
|
6291
6281
|
@with_connection_from_handle
|
|
6292
6282
|
def on_connection_data_length_change(
|
|
6293
|
-
self,
|
|
6283
|
+
self,
|
|
6284
|
+
connection: Connection,
|
|
6285
|
+
max_tx_octets: int,
|
|
6286
|
+
max_tx_time: int,
|
|
6287
|
+
max_rx_octets: int,
|
|
6288
|
+
max_rx_time: int,
|
|
6294
6289
|
):
|
|
6295
6290
|
logger.debug(
|
|
6296
6291
|
f'*** Connection Data Length Change: [0x{connection.handle:04X}] '
|
|
@@ -6414,15 +6409,22 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6414
6409
|
|
|
6415
6410
|
# [Classic only]
|
|
6416
6411
|
@host_event_handler
|
|
6417
|
-
@
|
|
6418
|
-
def on_role_change(
|
|
6419
|
-
connection.
|
|
6420
|
-
|
|
6412
|
+
@try_with_connection_from_address
|
|
6413
|
+
def on_role_change(
|
|
6414
|
+
self, connection: Connection, peer_address: hci.Address, new_role: hci.Role
|
|
6415
|
+
):
|
|
6416
|
+
if connection:
|
|
6417
|
+
connection.role = new_role
|
|
6418
|
+
connection.emit(connection.EVENT_ROLE_CHANGE, new_role)
|
|
6419
|
+
else:
|
|
6420
|
+
self.connection_roles[peer_address] = new_role
|
|
6421
6421
|
|
|
6422
6422
|
# [Classic only]
|
|
6423
6423
|
@host_event_handler
|
|
6424
6424
|
@try_with_connection_from_address
|
|
6425
|
-
def on_role_change_failure(
|
|
6425
|
+
def on_role_change_failure(
|
|
6426
|
+
self, connection: Connection, address: hci.Address, error: int
|
|
6427
|
+
):
|
|
6426
6428
|
if connection:
|
|
6427
6429
|
connection.emit(connection.EVENT_ROLE_CHANGE_FAILURE, error)
|
|
6428
6430
|
self.emit(self.EVENT_ROLE_CHANGE_FAILURE, address, error)
|
|
@@ -6436,7 +6438,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6436
6438
|
# [Classic only]
|
|
6437
6439
|
@host_event_handler
|
|
6438
6440
|
@with_connection_from_address
|
|
6439
|
-
def on_classic_pairing_failure(self, connection: Connection, status) -> None:
|
|
6441
|
+
def on_classic_pairing_failure(self, connection: Connection, status: int) -> None:
|
|
6440
6442
|
connection.emit(connection.EVENT_CLASSIC_PAIRING_FAILURE, status)
|
|
6441
6443
|
|
|
6442
6444
|
def on_pairing_start(self, connection: Connection) -> None:
|
|
@@ -6460,7 +6462,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6460
6462
|
connection.emit(connection.EVENT_PAIRING_FAILURE, reason)
|
|
6461
6463
|
|
|
6462
6464
|
@with_connection_from_handle
|
|
6463
|
-
def on_gatt_pdu(self, connection, pdu):
|
|
6465
|
+
def on_gatt_pdu(self, connection: Connection, pdu: bytes):
|
|
6464
6466
|
# Parse the L2CAP payload into an ATT PDU object
|
|
6465
6467
|
att_pdu = ATT_PDU.from_bytes(pdu)
|
|
6466
6468
|
|
|
@@ -6482,7 +6484,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6482
6484
|
connection.gatt_server.on_gatt_pdu(connection, att_pdu)
|
|
6483
6485
|
|
|
6484
6486
|
@with_connection_from_handle
|
|
6485
|
-
def on_smp_pdu(self, connection, pdu):
|
|
6487
|
+
def on_smp_pdu(self, connection: Connection, pdu: bytes):
|
|
6486
6488
|
self.smp_manager.on_smp_pdu(connection, pdu)
|
|
6487
6489
|
|
|
6488
6490
|
@host_event_handler
|