bumble 0.0.211__py3-none-any.whl → 0.0.212__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bumble/_version.py +2 -2
- bumble/apps/bench.py +4 -2
- bumble/apps/console.py +2 -2
- bumble/apps/pair.py +185 -32
- bumble/att.py +13 -12
- bumble/avctp.py +2 -2
- bumble/avdtp.py +122 -68
- bumble/avrcp.py +11 -5
- bumble/core.py +13 -7
- bumble/{crypto.py → crypto/__init__.py} +11 -95
- bumble/crypto/builtin.py +652 -0
- bumble/crypto/cryptography.py +84 -0
- bumble/device.py +362 -185
- bumble/drivers/intel.py +3 -0
- bumble/gatt.py +3 -5
- bumble/gatt_client.py +5 -3
- bumble/gatt_server.py +8 -6
- bumble/hci.py +67 -2
- bumble/hfp.py +44 -20
- bumble/hid.py +24 -12
- bumble/host.py +24 -0
- bumble/keys.py +64 -48
- bumble/l2cap.py +19 -9
- bumble/pandora/host.py +11 -11
- bumble/pandora/l2cap.py +2 -2
- bumble/pandora/security.py +72 -56
- bumble/profiles/aics.py +3 -5
- bumble/profiles/ancs.py +3 -1
- bumble/profiles/ascs.py +11 -5
- bumble/profiles/asha.py +11 -6
- bumble/profiles/csip.py +1 -3
- bumble/profiles/gatt_service.py +1 -3
- bumble/profiles/hap.py +16 -33
- bumble/profiles/mcp.py +12 -9
- bumble/profiles/vcs.py +5 -5
- bumble/profiles/vocs.py +6 -9
- bumble/rfcomm.py +17 -8
- bumble/smp.py +14 -8
- {bumble-0.0.211.dist-info → bumble-0.0.212.dist-info}/METADATA +4 -4
- {bumble-0.0.211.dist-info → bumble-0.0.212.dist-info}/RECORD +44 -42
- {bumble-0.0.211.dist-info → bumble-0.0.212.dist-info}/WHEEL +1 -1
- {bumble-0.0.211.dist-info → bumble-0.0.212.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.211.dist-info → bumble-0.0.212.dist-info}/licenses/LICENSE +0 -0
- {bumble-0.0.211.dist-info → bumble-0.0.212.dist-info}/top_level.txt +0 -0
bumble/device.py
CHANGED
|
@@ -61,7 +61,6 @@ from bumble.core import (
|
|
|
61
61
|
BaseBumbleError,
|
|
62
62
|
ConnectionParameterUpdateError,
|
|
63
63
|
CommandTimeoutError,
|
|
64
|
-
ConnectionParameters,
|
|
65
64
|
ConnectionPHY,
|
|
66
65
|
InvalidArgumentError,
|
|
67
66
|
InvalidOperationError,
|
|
@@ -484,7 +483,7 @@ class BIGInfoAdvertisement:
|
|
|
484
483
|
sid: int
|
|
485
484
|
num_bis: int
|
|
486
485
|
nse: int
|
|
487
|
-
iso_interval:
|
|
486
|
+
iso_interval: float
|
|
488
487
|
bn: int
|
|
489
488
|
pto: int
|
|
490
489
|
irc: int
|
|
@@ -502,7 +501,7 @@ class BIGInfoAdvertisement:
|
|
|
502
501
|
sid,
|
|
503
502
|
report.num_bis,
|
|
504
503
|
report.nse,
|
|
505
|
-
report.iso_interval,
|
|
504
|
+
report.iso_interval * 1.25,
|
|
506
505
|
report.bn,
|
|
507
506
|
report.pto,
|
|
508
507
|
report.irc,
|
|
@@ -529,8 +528,8 @@ class AdvertisingParameters:
|
|
|
529
528
|
advertising_event_properties: AdvertisingEventProperties = field(
|
|
530
529
|
default_factory=AdvertisingEventProperties
|
|
531
530
|
)
|
|
532
|
-
primary_advertising_interval_min:
|
|
533
|
-
primary_advertising_interval_max:
|
|
531
|
+
primary_advertising_interval_min: float = DEVICE_DEFAULT_ADVERTISING_INTERVAL
|
|
532
|
+
primary_advertising_interval_max: float = DEVICE_DEFAULT_ADVERTISING_INTERVAL
|
|
534
533
|
primary_advertising_channel_map: (
|
|
535
534
|
hci.HCI_LE_Set_Extended_Advertising_Parameters_Command.ChannelMap
|
|
536
535
|
) = (
|
|
@@ -554,8 +553,8 @@ class AdvertisingParameters:
|
|
|
554
553
|
# -----------------------------------------------------------------------------
|
|
555
554
|
@dataclass
|
|
556
555
|
class PeriodicAdvertisingParameters:
|
|
557
|
-
periodic_advertising_interval_min:
|
|
558
|
-
periodic_advertising_interval_max:
|
|
556
|
+
periodic_advertising_interval_min: float = DEVICE_DEFAULT_ADVERTISING_INTERVAL
|
|
557
|
+
periodic_advertising_interval_max: float = DEVICE_DEFAULT_ADVERTISING_INTERVAL
|
|
559
558
|
periodic_advertising_properties: (
|
|
560
559
|
hci.HCI_LE_Set_Periodic_Advertising_Parameters_Command.Properties
|
|
561
560
|
) = field(
|
|
@@ -581,6 +580,12 @@ class AdvertisingSet(utils.EventEmitter):
|
|
|
581
580
|
enabled: bool = False
|
|
582
581
|
periodic_enabled: bool = False
|
|
583
582
|
|
|
583
|
+
EVENT_START = "start"
|
|
584
|
+
EVENT_STOP = "stop"
|
|
585
|
+
EVENT_START_PERIODIC = "start_periodic"
|
|
586
|
+
EVENT_STOP_PERIODIC = "stop_periodic"
|
|
587
|
+
EVENT_TERMINATION = "termination"
|
|
588
|
+
|
|
584
589
|
def __post_init__(self) -> None:
|
|
585
590
|
super().__init__()
|
|
586
591
|
|
|
@@ -679,8 +684,12 @@ class AdvertisingSet(utils.EventEmitter):
|
|
|
679
684
|
await self.device.send_command(
|
|
680
685
|
hci.HCI_LE_Set_Periodic_Advertising_Parameters_Command(
|
|
681
686
|
advertising_handle=self.advertising_handle,
|
|
682
|
-
periodic_advertising_interval_min=
|
|
683
|
-
|
|
687
|
+
periodic_advertising_interval_min=int(
|
|
688
|
+
advertising_parameters.periodic_advertising_interval_min / 1.25
|
|
689
|
+
),
|
|
690
|
+
periodic_advertising_interval_max=int(
|
|
691
|
+
advertising_parameters.periodic_advertising_interval_max / 1.25
|
|
692
|
+
),
|
|
684
693
|
periodic_advertising_properties=advertising_parameters.periodic_advertising_properties,
|
|
685
694
|
),
|
|
686
695
|
check_result=True,
|
|
@@ -731,7 +740,7 @@ class AdvertisingSet(utils.EventEmitter):
|
|
|
731
740
|
)
|
|
732
741
|
self.enabled = True
|
|
733
742
|
|
|
734
|
-
self.emit(
|
|
743
|
+
self.emit(self.EVENT_START)
|
|
735
744
|
|
|
736
745
|
async def stop(self) -> None:
|
|
737
746
|
await self.device.send_command(
|
|
@@ -745,7 +754,7 @@ class AdvertisingSet(utils.EventEmitter):
|
|
|
745
754
|
)
|
|
746
755
|
self.enabled = False
|
|
747
756
|
|
|
748
|
-
self.emit(
|
|
757
|
+
self.emit(self.EVENT_STOP)
|
|
749
758
|
|
|
750
759
|
async def start_periodic(self, include_adi: bool = False) -> None:
|
|
751
760
|
if self.periodic_enabled:
|
|
@@ -759,7 +768,7 @@ class AdvertisingSet(utils.EventEmitter):
|
|
|
759
768
|
)
|
|
760
769
|
self.periodic_enabled = True
|
|
761
770
|
|
|
762
|
-
self.emit(
|
|
771
|
+
self.emit(self.EVENT_START_PERIODIC)
|
|
763
772
|
|
|
764
773
|
async def stop_periodic(self) -> None:
|
|
765
774
|
if not self.periodic_enabled:
|
|
@@ -773,7 +782,7 @@ class AdvertisingSet(utils.EventEmitter):
|
|
|
773
782
|
)
|
|
774
783
|
self.periodic_enabled = False
|
|
775
784
|
|
|
776
|
-
self.emit(
|
|
785
|
+
self.emit(self.EVENT_STOP_PERIODIC)
|
|
777
786
|
|
|
778
787
|
async def remove(self) -> None:
|
|
779
788
|
await self.device.send_command(
|
|
@@ -797,7 +806,7 @@ class AdvertisingSet(utils.EventEmitter):
|
|
|
797
806
|
|
|
798
807
|
def on_termination(self, status: int) -> None:
|
|
799
808
|
self.enabled = False
|
|
800
|
-
self.emit(
|
|
809
|
+
self.emit(self.EVENT_TERMINATION, status)
|
|
801
810
|
|
|
802
811
|
|
|
803
812
|
# -----------------------------------------------------------------------------
|
|
@@ -820,9 +829,17 @@ class PeriodicAdvertisingSync(utils.EventEmitter):
|
|
|
820
829
|
filter_duplicates: bool
|
|
821
830
|
status: int
|
|
822
831
|
advertiser_phy: int
|
|
823
|
-
periodic_advertising_interval:
|
|
832
|
+
periodic_advertising_interval: float # Advertising interval, in milliseconds
|
|
824
833
|
advertiser_clock_accuracy: int
|
|
825
834
|
|
|
835
|
+
EVENT_STATE_CHANGE = "state_change"
|
|
836
|
+
EVENT_ESTABLISHMENT = "establishment"
|
|
837
|
+
EVENT_CANCELLATION = "cancellation"
|
|
838
|
+
EVENT_ERROR = "error"
|
|
839
|
+
EVENT_LOSS = "loss"
|
|
840
|
+
EVENT_PERIODIC_ADVERTISEMENT = "periodic_advertisement"
|
|
841
|
+
EVENT_BIGINFO_ADVERTISEMENT = "biginfo_advertisement"
|
|
842
|
+
|
|
826
843
|
def __init__(
|
|
827
844
|
self,
|
|
828
845
|
device: Device,
|
|
@@ -855,7 +872,7 @@ class PeriodicAdvertisingSync(utils.EventEmitter):
|
|
|
855
872
|
def state(self, state: State) -> None:
|
|
856
873
|
logger.debug(f'{self} -> {state.name}')
|
|
857
874
|
self._state = state
|
|
858
|
-
self.emit(
|
|
875
|
+
self.emit(self.EVENT_STATE_CHANGE)
|
|
859
876
|
|
|
860
877
|
async def establish(self) -> None:
|
|
861
878
|
if self.state != self.State.INIT:
|
|
@@ -936,10 +953,10 @@ class PeriodicAdvertisingSync(utils.EventEmitter):
|
|
|
936
953
|
if status == hci.HCI_SUCCESS:
|
|
937
954
|
self.sync_handle = sync_handle
|
|
938
955
|
self.advertiser_phy = advertiser_phy
|
|
939
|
-
self.periodic_advertising_interval = periodic_advertising_interval
|
|
956
|
+
self.periodic_advertising_interval = periodic_advertising_interval * 1.25
|
|
940
957
|
self.advertiser_clock_accuracy = advertiser_clock_accuracy
|
|
941
958
|
self.state = self.State.ESTABLISHED
|
|
942
|
-
self.emit(
|
|
959
|
+
self.emit(self.EVENT_ESTABLISHMENT)
|
|
943
960
|
return
|
|
944
961
|
|
|
945
962
|
# We don't need to keep a reference anymore
|
|
@@ -948,15 +965,15 @@ class PeriodicAdvertisingSync(utils.EventEmitter):
|
|
|
948
965
|
|
|
949
966
|
if status == hci.HCI_OPERATION_CANCELLED_BY_HOST_ERROR:
|
|
950
967
|
self.state = self.State.CANCELLED
|
|
951
|
-
self.emit(
|
|
968
|
+
self.emit(self.EVENT_CANCELLATION)
|
|
952
969
|
return
|
|
953
970
|
|
|
954
971
|
self.state = self.State.ERROR
|
|
955
|
-
self.emit(
|
|
972
|
+
self.emit(self.EVENT_ERROR)
|
|
956
973
|
|
|
957
974
|
def on_loss(self):
|
|
958
975
|
self.state = self.State.LOST
|
|
959
|
-
self.emit(
|
|
976
|
+
self.emit(self.EVENT_LOSS)
|
|
960
977
|
|
|
961
978
|
def on_periodic_advertising_report(self, report) -> None:
|
|
962
979
|
self.data_accumulator += report.data
|
|
@@ -967,7 +984,7 @@ class PeriodicAdvertisingSync(utils.EventEmitter):
|
|
|
967
984
|
return
|
|
968
985
|
|
|
969
986
|
self.emit(
|
|
970
|
-
|
|
987
|
+
self.EVENT_PERIODIC_ADVERTISEMENT,
|
|
971
988
|
PeriodicAdvertisement(
|
|
972
989
|
self.advertiser_address,
|
|
973
990
|
self.sid,
|
|
@@ -984,7 +1001,7 @@ class PeriodicAdvertisingSync(utils.EventEmitter):
|
|
|
984
1001
|
|
|
985
1002
|
def on_biginfo_advertising_report(self, report) -> None:
|
|
986
1003
|
self.emit(
|
|
987
|
-
|
|
1004
|
+
self.EVENT_BIGINFO_ADVERTISEMENT,
|
|
988
1005
|
BIGInfoAdvertisement.from_report(self.advertiser_address, self.sid, report),
|
|
989
1006
|
)
|
|
990
1007
|
|
|
@@ -1041,7 +1058,7 @@ class Big(utils.EventEmitter):
|
|
|
1041
1058
|
pto: int = 0
|
|
1042
1059
|
irc: int = 0
|
|
1043
1060
|
max_pdu: int = 0
|
|
1044
|
-
iso_interval:
|
|
1061
|
+
iso_interval: float = 0.0
|
|
1045
1062
|
bis_links: Sequence[BisLink] = ()
|
|
1046
1063
|
|
|
1047
1064
|
def __post_init__(self) -> None:
|
|
@@ -1102,7 +1119,7 @@ class BigSync(utils.EventEmitter):
|
|
|
1102
1119
|
pto: int = 0
|
|
1103
1120
|
irc: int = 0
|
|
1104
1121
|
max_pdu: int = 0
|
|
1105
|
-
iso_interval:
|
|
1122
|
+
iso_interval: float = 0.0
|
|
1106
1123
|
bis_links: Sequence[BisLink] = ()
|
|
1107
1124
|
|
|
1108
1125
|
def __post_init__(self) -> None:
|
|
@@ -1183,11 +1200,11 @@ class ChannelSoundingProcedure:
|
|
|
1183
1200
|
selected_tx_power: int
|
|
1184
1201
|
subevent_len: int
|
|
1185
1202
|
subevents_per_event: int
|
|
1186
|
-
subevent_interval:
|
|
1203
|
+
subevent_interval: float # milliseconds.
|
|
1187
1204
|
event_interval: int
|
|
1188
1205
|
procedure_interval: int
|
|
1189
1206
|
procedure_count: int
|
|
1190
|
-
max_procedure_len:
|
|
1207
|
+
max_procedure_len: float # milliseconds.
|
|
1191
1208
|
|
|
1192
1209
|
|
|
1193
1210
|
# -----------------------------------------------------------------------------
|
|
@@ -1212,9 +1229,8 @@ class Peer:
|
|
|
1212
1229
|
def __init__(self, connection: Connection) -> None:
|
|
1213
1230
|
self.connection = connection
|
|
1214
1231
|
|
|
1215
|
-
#
|
|
1216
|
-
self.gatt_client = gatt_client
|
|
1217
|
-
connection.gatt_client = self.gatt_client
|
|
1232
|
+
# Shortcut to the connection's GATT client
|
|
1233
|
+
self.gatt_client = connection.gatt_client
|
|
1218
1234
|
|
|
1219
1235
|
@property
|
|
1220
1236
|
def services(self) -> list[gatt_client.ServiceProxy]:
|
|
@@ -1222,7 +1238,7 @@ class Peer:
|
|
|
1222
1238
|
|
|
1223
1239
|
async def request_mtu(self, mtu: int) -> int:
|
|
1224
1240
|
mtu = await self.gatt_client.request_mtu(mtu)
|
|
1225
|
-
self.connection.emit(
|
|
1241
|
+
self.connection.emit(self.connection.EVENT_CONNECTION_ATT_MTU_UPDATE)
|
|
1226
1242
|
return mtu
|
|
1227
1243
|
|
|
1228
1244
|
async def discover_service(
|
|
@@ -1390,6 +1406,9 @@ class ScoLink(utils.CompositeEventEmitter):
|
|
|
1390
1406
|
link_type: int
|
|
1391
1407
|
sink: Optional[Callable[[hci.HCI_SynchronousDataPacket], Any]] = None
|
|
1392
1408
|
|
|
1409
|
+
EVENT_DISCONNECTION: ClassVar[str] = "disconnection"
|
|
1410
|
+
EVENT_DISCONNECTION_FAILURE: ClassVar[str] = "disconnection_failure"
|
|
1411
|
+
|
|
1393
1412
|
def __post_init__(self) -> None:
|
|
1394
1413
|
super().__init__()
|
|
1395
1414
|
|
|
@@ -1487,6 +1506,11 @@ class CisLink(utils.EventEmitter, _IsoLink):
|
|
|
1487
1506
|
state: State = State.PENDING
|
|
1488
1507
|
sink: Callable[[hci.HCI_IsoDataPacket], Any] | None = None
|
|
1489
1508
|
|
|
1509
|
+
EVENT_DISCONNECTION: ClassVar[str] = "disconnection"
|
|
1510
|
+
EVENT_DISCONNECTION_FAILURE: ClassVar[str] = "disconnection_failure"
|
|
1511
|
+
EVENT_ESTABLISHMENT: ClassVar[str] = "establishment"
|
|
1512
|
+
EVENT_ESTABLISHMENT_FAILURE: ClassVar[str] = "establishment_failure"
|
|
1513
|
+
|
|
1490
1514
|
def __post_init__(self) -> None:
|
|
1491
1515
|
super().__init__()
|
|
1492
1516
|
|
|
@@ -1562,15 +1586,50 @@ class Connection(utils.CompositeEventEmitter):
|
|
|
1562
1586
|
peer_le_features: Optional[hci.LeFeatureMask]
|
|
1563
1587
|
role: hci.Role
|
|
1564
1588
|
encryption: int
|
|
1589
|
+
encryption_key_size: int
|
|
1565
1590
|
authenticated: bool
|
|
1566
1591
|
sc: bool
|
|
1567
|
-
link_key_type: int
|
|
1568
1592
|
gatt_client: gatt_client.Client
|
|
1569
1593
|
pairing_peer_io_capability: Optional[int]
|
|
1570
1594
|
pairing_peer_authentication_requirements: Optional[int]
|
|
1571
1595
|
cs_configs: dict[int, ChannelSoundingConfig] # Config ID to Configuration
|
|
1572
1596
|
cs_procedures: dict[int, ChannelSoundingProcedure] # Config ID to Procedures
|
|
1573
1597
|
|
|
1598
|
+
EVENT_CONNECTION_ATT_MTU_UPDATE = "connection_att_mtu_update"
|
|
1599
|
+
EVENT_DISCONNECTION = "disconnection"
|
|
1600
|
+
EVENT_DISCONNECTION_FAILURE = "disconnection_failure"
|
|
1601
|
+
EVENT_CONNECTION_AUTHENTICATION = "connection_authentication"
|
|
1602
|
+
EVENT_CONNECTION_AUTHENTICATION_FAILURE = "connection_authentication_failure"
|
|
1603
|
+
EVENT_REMOTE_NAME = "remote_name"
|
|
1604
|
+
EVENT_REMOTE_NAME_FAILURE = "remote_name_failure"
|
|
1605
|
+
EVENT_CONNECTION_ENCRYPTION_CHANGE = "connection_encryption_change"
|
|
1606
|
+
EVENT_CONNECTION_ENCRYPTION_FAILURE = "connection_encryption_failure"
|
|
1607
|
+
EVENT_CONNECTION_ENCRYPTION_KEY_REFRESH = "connection_encryption_key_refresh"
|
|
1608
|
+
EVENT_CONNECTION_PARAMETERS_UPDATE = "connection_parameters_update"
|
|
1609
|
+
EVENT_CONNECTION_PARAMETERS_UPDATE_FAILURE = "connection_parameters_update_failure"
|
|
1610
|
+
EVENT_CONNECTION_PHY_UPDATE = "connection_phy_update"
|
|
1611
|
+
EVENT_CONNECTION_PHY_UPDATE_FAILURE = "connection_phy_update_failure"
|
|
1612
|
+
EVENT_CONNECTION_ATT_MTU_UPDATE = "connection_att_mtu_update"
|
|
1613
|
+
EVENT_CONNECTION_DATA_LENGTH_CHANGE = "connection_data_length_change"
|
|
1614
|
+
EVENT_CHANNEL_SOUNDING_CAPABILITIES_FAILURE = (
|
|
1615
|
+
"channel_sounding_capabilities_failure"
|
|
1616
|
+
)
|
|
1617
|
+
EVENT_CHANNEL_SOUNDING_CAPABILITIES = "channel_sounding_capabilities"
|
|
1618
|
+
EVENT_CHANNEL_SOUNDING_CONFIG_FAILURE = "channel_sounding_config_failure"
|
|
1619
|
+
EVENT_CHANNEL_SOUNDING_CONFIG = "channel_sounding_config"
|
|
1620
|
+
EVENT_CHANNEL_SOUNDING_CONFIG_REMOVED = "channel_sounding_config_removed"
|
|
1621
|
+
EVENT_CHANNEL_SOUNDING_PROCEDURE_FAILURE = "channel_sounding_procedure_failure"
|
|
1622
|
+
EVENT_CHANNEL_SOUNDING_PROCEDURE = "channel_sounding_procedure"
|
|
1623
|
+
EVENT_ROLE_CHANGE = "role_change"
|
|
1624
|
+
EVENT_ROLE_CHANGE_FAILURE = "role_change_failure"
|
|
1625
|
+
EVENT_CLASSIC_PAIRING = "classic_pairing"
|
|
1626
|
+
EVENT_CLASSIC_PAIRING_FAILURE = "classic_pairing_failure"
|
|
1627
|
+
EVENT_PAIRING_START = "pairing_start"
|
|
1628
|
+
EVENT_PAIRING = "pairing"
|
|
1629
|
+
EVENT_PAIRING_FAILURE = "pairing_failure"
|
|
1630
|
+
EVENT_SECURITY_REQUEST = "security_request"
|
|
1631
|
+
EVENT_LINK_KEY = "link_key"
|
|
1632
|
+
|
|
1574
1633
|
@utils.composite_listener
|
|
1575
1634
|
class Listener:
|
|
1576
1635
|
def on_disconnection(self, reason):
|
|
@@ -1600,17 +1659,23 @@ class Connection(utils.CompositeEventEmitter):
|
|
|
1600
1659
|
def on_connection_encryption_key_refresh(self):
|
|
1601
1660
|
pass
|
|
1602
1661
|
|
|
1662
|
+
@dataclass
|
|
1663
|
+
class Parameters:
|
|
1664
|
+
connection_interval: float # Connection interval, in milliseconds. [LE only]
|
|
1665
|
+
peripheral_latency: int # Peripheral latency, in number of intervals. [LE only]
|
|
1666
|
+
supervision_timeout: float # Supervision timeout, in milliseconds.
|
|
1667
|
+
|
|
1603
1668
|
def __init__(
|
|
1604
1669
|
self,
|
|
1605
|
-
device,
|
|
1606
|
-
handle,
|
|
1607
|
-
transport,
|
|
1608
|
-
self_address,
|
|
1609
|
-
self_resolvable_address,
|
|
1610
|
-
peer_address,
|
|
1611
|
-
peer_resolvable_address,
|
|
1612
|
-
role,
|
|
1613
|
-
parameters,
|
|
1670
|
+
device: Device,
|
|
1671
|
+
handle: int,
|
|
1672
|
+
transport: core.PhysicalTransport,
|
|
1673
|
+
self_address: hci.Address,
|
|
1674
|
+
self_resolvable_address: Optional[hci.Address],
|
|
1675
|
+
peer_address: hci.Address,
|
|
1676
|
+
peer_resolvable_address: Optional[hci.Address],
|
|
1677
|
+
role: hci.Role,
|
|
1678
|
+
parameters: Parameters,
|
|
1614
1679
|
):
|
|
1615
1680
|
super().__init__()
|
|
1616
1681
|
self.device = device
|
|
@@ -1624,12 +1689,12 @@ class Connection(utils.CompositeEventEmitter):
|
|
|
1624
1689
|
self.role = role
|
|
1625
1690
|
self.parameters = parameters
|
|
1626
1691
|
self.encryption = 0
|
|
1692
|
+
self.encryption_key_size = 0
|
|
1627
1693
|
self.authenticated = False
|
|
1628
1694
|
self.sc = False
|
|
1629
|
-
self.link_key_type = None
|
|
1630
1695
|
self.att_mtu = ATT_DEFAULT_MTU
|
|
1631
1696
|
self.data_length = DEVICE_DEFAULT_DATA_LENGTH
|
|
1632
|
-
self.gatt_client =
|
|
1697
|
+
self.gatt_client = gatt_client.Client(self) # Per-connection client
|
|
1633
1698
|
self.gatt_server = (
|
|
1634
1699
|
device.gatt_server
|
|
1635
1700
|
) # By default, use the device's shared server
|
|
@@ -1740,28 +1805,38 @@ class Connection(utils.CompositeEventEmitter):
|
|
|
1740
1805
|
"""Idles the current task waiting for a disconnect or timeout"""
|
|
1741
1806
|
|
|
1742
1807
|
abort = asyncio.get_running_loop().create_future()
|
|
1743
|
-
self.on(
|
|
1744
|
-
self.on(
|
|
1808
|
+
self.on(self.EVENT_DISCONNECTION, abort.set_result)
|
|
1809
|
+
self.on(self.EVENT_DISCONNECTION_FAILURE, abort.set_exception)
|
|
1745
1810
|
|
|
1746
1811
|
try:
|
|
1747
1812
|
await asyncio.wait_for(
|
|
1748
|
-
utils.cancel_on_event(self.device,
|
|
1813
|
+
utils.cancel_on_event(self.device, Device.EVENT_FLUSH, abort), timeout
|
|
1749
1814
|
)
|
|
1750
1815
|
finally:
|
|
1751
|
-
self.remove_listener(
|
|
1752
|
-
self.remove_listener(
|
|
1816
|
+
self.remove_listener(self.EVENT_DISCONNECTION, abort.set_result)
|
|
1817
|
+
self.remove_listener(self.EVENT_DISCONNECTION_FAILURE, abort.set_exception)
|
|
1753
1818
|
|
|
1754
1819
|
async def set_data_length(self, tx_octets, tx_time) -> None:
|
|
1755
1820
|
return await self.device.set_data_length(self, tx_octets, tx_time)
|
|
1756
1821
|
|
|
1757
1822
|
async def update_parameters(
|
|
1758
1823
|
self,
|
|
1759
|
-
connection_interval_min,
|
|
1760
|
-
connection_interval_max,
|
|
1761
|
-
max_latency,
|
|
1762
|
-
supervision_timeout,
|
|
1824
|
+
connection_interval_min: float,
|
|
1825
|
+
connection_interval_max: float,
|
|
1826
|
+
max_latency: int,
|
|
1827
|
+
supervision_timeout: float,
|
|
1763
1828
|
use_l2cap=False,
|
|
1764
|
-
):
|
|
1829
|
+
) -> None:
|
|
1830
|
+
"""
|
|
1831
|
+
Request an update of the connection parameters.
|
|
1832
|
+
|
|
1833
|
+
Args:
|
|
1834
|
+
connection_interval_min: Minimum interval, in milliseconds.
|
|
1835
|
+
connection_interval_max: Maximum interval, in milliseconds.
|
|
1836
|
+
max_latency: Latency, in number of intervals.
|
|
1837
|
+
supervision_timeout: Timeout, in milliseconds.
|
|
1838
|
+
use_l2cap: Request the update via L2CAP.
|
|
1839
|
+
"""
|
|
1765
1840
|
return await self.device.update_connection_parameters(
|
|
1766
1841
|
self,
|
|
1767
1842
|
connection_interval_min,
|
|
@@ -1848,8 +1923,8 @@ class DeviceConfiguration:
|
|
|
1848
1923
|
address: hci.Address = hci.Address(DEVICE_DEFAULT_ADDRESS)
|
|
1849
1924
|
class_of_device: int = DEVICE_DEFAULT_CLASS_OF_DEVICE
|
|
1850
1925
|
scan_response_data: bytes = DEVICE_DEFAULT_SCAN_RESPONSE_DATA
|
|
1851
|
-
advertising_interval_min:
|
|
1852
|
-
advertising_interval_max:
|
|
1926
|
+
advertising_interval_min: float = DEVICE_DEFAULT_ADVERTISING_INTERVAL
|
|
1927
|
+
advertising_interval_max: float = DEVICE_DEFAULT_ADVERTISING_INTERVAL
|
|
1853
1928
|
le_enabled: bool = True
|
|
1854
1929
|
le_simultaneous_enabled: bool = False
|
|
1855
1930
|
le_privacy_enabled: bool = False
|
|
@@ -2062,6 +2137,26 @@ class Device(utils.CompositeEventEmitter):
|
|
|
2062
2137
|
_pending_cis: Dict[int, tuple[int, int]]
|
|
2063
2138
|
gatt_service: gatt_service.GenericAttributeProfileService | None = None
|
|
2064
2139
|
|
|
2140
|
+
EVENT_ADVERTISEMENT = "advertisement"
|
|
2141
|
+
EVENT_PERIODIC_ADVERTISING_SYNC_TRANSFER = "periodic_advertising_sync_transfer"
|
|
2142
|
+
EVENT_KEY_STORE_UPDATE = "key_store_update"
|
|
2143
|
+
EVENT_FLUSH = "flush"
|
|
2144
|
+
EVENT_CONNECTION = "connection"
|
|
2145
|
+
EVENT_CONNECTION_FAILURE = "connection_failure"
|
|
2146
|
+
EVENT_SCO_REQUEST = "sco_request"
|
|
2147
|
+
EVENT_INQUIRY_COMPLETE = "inquiry_complete"
|
|
2148
|
+
EVENT_REMOTE_NAME = "remote_name"
|
|
2149
|
+
EVENT_REMOTE_NAME_FAILURE = "remote_name_failure"
|
|
2150
|
+
EVENT_SCO_CONNECTION = "sco_connection"
|
|
2151
|
+
EVENT_SCO_CONNECTION_FAILURE = "sco_connection_failure"
|
|
2152
|
+
EVENT_CIS_REQUEST = "cis_request"
|
|
2153
|
+
EVENT_CIS_ESTABLISHMENT = "cis_establishment"
|
|
2154
|
+
EVENT_CIS_ESTABLISHMENT_FAILURE = "cis_establishment_failure"
|
|
2155
|
+
EVENT_ROLE_CHANGE_FAILURE = "role_change_failure"
|
|
2156
|
+
EVENT_INQUIRY_RESULT = "inquiry_result"
|
|
2157
|
+
EVENT_REMOTE_NAME = "remote_name"
|
|
2158
|
+
EVENT_REMOTE_NAME_FAILURE = "remote_name_failure"
|
|
2159
|
+
|
|
2065
2160
|
@utils.composite_listener
|
|
2066
2161
|
class Listener:
|
|
2067
2162
|
def on_advertisement(self, advertisement):
|
|
@@ -2748,8 +2843,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
2748
2843
|
auto_restart: bool = False,
|
|
2749
2844
|
advertising_data: Optional[bytes] = None,
|
|
2750
2845
|
scan_response_data: Optional[bytes] = None,
|
|
2751
|
-
advertising_interval_min: Optional[
|
|
2752
|
-
advertising_interval_max: Optional[
|
|
2846
|
+
advertising_interval_min: Optional[float] = None,
|
|
2847
|
+
advertising_interval_max: Optional[float] = None,
|
|
2753
2848
|
) -> None:
|
|
2754
2849
|
"""Start legacy advertising.
|
|
2755
2850
|
|
|
@@ -3149,7 +3244,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3149
3244
|
accumulator = AdvertisementDataAccumulator(passive=self.scanning_is_passive)
|
|
3150
3245
|
self.advertisement_accumulators[report.address] = accumulator
|
|
3151
3246
|
if advertisement := accumulator.update(report):
|
|
3152
|
-
self.emit(
|
|
3247
|
+
self.emit(self.EVENT_ADVERTISEMENT, advertisement)
|
|
3153
3248
|
|
|
3154
3249
|
async def create_periodic_advertising_sync(
|
|
3155
3250
|
self,
|
|
@@ -3273,7 +3368,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3273
3368
|
periodic_advertising_interval=periodic_advertising_interval,
|
|
3274
3369
|
advertiser_clock_accuracy=advertiser_clock_accuracy,
|
|
3275
3370
|
)
|
|
3276
|
-
self.emit(
|
|
3371
|
+
self.emit(self.EVENT_PERIODIC_ADVERTISING_SYNC_TRANSFER, pa_sync, connection)
|
|
3277
3372
|
|
|
3278
3373
|
@host_event_handler
|
|
3279
3374
|
@with_periodic_advertising_sync_from_handle
|
|
@@ -3331,7 +3426,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3331
3426
|
@host_event_handler
|
|
3332
3427
|
def on_inquiry_result(self, address, class_of_device, data, rssi):
|
|
3333
3428
|
self.emit(
|
|
3334
|
-
|
|
3429
|
+
self.EVENT_INQUIRY_RESULT,
|
|
3335
3430
|
address,
|
|
3336
3431
|
class_of_device,
|
|
3337
3432
|
AdvertisingData.from_bytes(data),
|
|
@@ -3508,8 +3603,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3508
3603
|
|
|
3509
3604
|
# Create a future so that we can wait for the connection's result
|
|
3510
3605
|
pending_connection = asyncio.get_running_loop().create_future()
|
|
3511
|
-
self.on(
|
|
3512
|
-
self.on(
|
|
3606
|
+
self.on(self.EVENT_CONNECTION, on_connection)
|
|
3607
|
+
self.on(self.EVENT_CONNECTION_FAILURE, on_connection_failure)
|
|
3513
3608
|
|
|
3514
3609
|
try:
|
|
3515
3610
|
# Tell the controller to connect
|
|
@@ -3662,7 +3757,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3662
3757
|
self.le_connecting = True
|
|
3663
3758
|
|
|
3664
3759
|
if timeout is None:
|
|
3665
|
-
return await utils.cancel_on_event(
|
|
3760
|
+
return await utils.cancel_on_event(
|
|
3761
|
+
self, Device.EVENT_FLUSH, pending_connection
|
|
3762
|
+
)
|
|
3666
3763
|
|
|
3667
3764
|
try:
|
|
3668
3765
|
return await asyncio.wait_for(
|
|
@@ -3680,13 +3777,13 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3680
3777
|
|
|
3681
3778
|
try:
|
|
3682
3779
|
return await utils.cancel_on_event(
|
|
3683
|
-
self,
|
|
3780
|
+
self, Device.EVENT_FLUSH, pending_connection
|
|
3684
3781
|
)
|
|
3685
3782
|
except core.ConnectionError as error:
|
|
3686
3783
|
raise core.TimeoutError() from error
|
|
3687
3784
|
finally:
|
|
3688
|
-
self.remove_listener(
|
|
3689
|
-
self.remove_listener(
|
|
3785
|
+
self.remove_listener(self.EVENT_CONNECTION, on_connection)
|
|
3786
|
+
self.remove_listener(self.EVENT_CONNECTION_FAILURE, on_connection_failure)
|
|
3690
3787
|
if transport == PhysicalTransport.LE:
|
|
3691
3788
|
self.le_connecting = False
|
|
3692
3789
|
self.connect_own_address_type = None
|
|
@@ -3737,7 +3834,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3737
3834
|
|
|
3738
3835
|
try:
|
|
3739
3836
|
# Wait for a request or a completed connection
|
|
3740
|
-
pending_request = utils.cancel_on_event(
|
|
3837
|
+
pending_request = utils.cancel_on_event(
|
|
3838
|
+
self, Device.EVENT_FLUSH, pending_request_fut
|
|
3839
|
+
)
|
|
3741
3840
|
result = await (
|
|
3742
3841
|
asyncio.wait_for(pending_request, timeout)
|
|
3743
3842
|
if timeout
|
|
@@ -3779,8 +3878,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3779
3878
|
):
|
|
3780
3879
|
pending_connection.set_exception(error)
|
|
3781
3880
|
|
|
3782
|
-
self.on(
|
|
3783
|
-
self.on(
|
|
3881
|
+
self.on(self.EVENT_CONNECTION, on_connection)
|
|
3882
|
+
self.on(self.EVENT_CONNECTION_FAILURE, on_connection_failure)
|
|
3784
3883
|
|
|
3785
3884
|
# Save pending connection, with the Peripheral hci.role.
|
|
3786
3885
|
# Even if we requested a role switch in the hci.HCI_Accept_Connection_Request
|
|
@@ -3799,11 +3898,13 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3799
3898
|
)
|
|
3800
3899
|
|
|
3801
3900
|
# Wait for connection complete
|
|
3802
|
-
return await utils.cancel_on_event(
|
|
3901
|
+
return await utils.cancel_on_event(
|
|
3902
|
+
self, Device.EVENT_FLUSH, pending_connection
|
|
3903
|
+
)
|
|
3803
3904
|
|
|
3804
3905
|
finally:
|
|
3805
|
-
self.remove_listener(
|
|
3806
|
-
self.remove_listener(
|
|
3906
|
+
self.remove_listener(self.EVENT_CONNECTION, on_connection)
|
|
3907
|
+
self.remove_listener(self.EVENT_CONNECTION_FAILURE, on_connection_failure)
|
|
3807
3908
|
self.pending_connections.pop(peer_address, None)
|
|
3808
3909
|
|
|
3809
3910
|
@asynccontextmanager
|
|
@@ -3857,8 +3958,10 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3857
3958
|
) -> None:
|
|
3858
3959
|
# Create a future so that we can wait for the disconnection's result
|
|
3859
3960
|
pending_disconnection = asyncio.get_running_loop().create_future()
|
|
3860
|
-
connection.on(
|
|
3861
|
-
connection.on(
|
|
3961
|
+
connection.on(connection.EVENT_DISCONNECTION, pending_disconnection.set_result)
|
|
3962
|
+
connection.on(
|
|
3963
|
+
connection.EVENT_DISCONNECTION_FAILURE, pending_disconnection.set_exception
|
|
3964
|
+
)
|
|
3862
3965
|
|
|
3863
3966
|
# Request a disconnection
|
|
3864
3967
|
result = await self.send_command(
|
|
@@ -3873,13 +3976,16 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3873
3976
|
|
|
3874
3977
|
# Wait for the disconnection process to complete
|
|
3875
3978
|
self.disconnecting = True
|
|
3876
|
-
return await utils.cancel_on_event(
|
|
3979
|
+
return await utils.cancel_on_event(
|
|
3980
|
+
self, Device.EVENT_FLUSH, pending_disconnection
|
|
3981
|
+
)
|
|
3877
3982
|
finally:
|
|
3878
3983
|
connection.remove_listener(
|
|
3879
|
-
|
|
3984
|
+
connection.EVENT_DISCONNECTION, pending_disconnection.set_result
|
|
3880
3985
|
)
|
|
3881
3986
|
connection.remove_listener(
|
|
3882
|
-
|
|
3987
|
+
connection.EVENT_DISCONNECTION_FAILURE,
|
|
3988
|
+
pending_disconnection.set_exception,
|
|
3883
3989
|
)
|
|
3884
3990
|
self.disconnecting = False
|
|
3885
3991
|
|
|
@@ -3901,20 +4007,39 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3901
4007
|
|
|
3902
4008
|
async def update_connection_parameters(
|
|
3903
4009
|
self,
|
|
3904
|
-
connection,
|
|
3905
|
-
connection_interval_min,
|
|
3906
|
-
connection_interval_max,
|
|
3907
|
-
max_latency,
|
|
3908
|
-
supervision_timeout,
|
|
3909
|
-
min_ce_length=0,
|
|
3910
|
-
max_ce_length=0,
|
|
3911
|
-
use_l2cap=False,
|
|
4010
|
+
connection: Connection,
|
|
4011
|
+
connection_interval_min: float,
|
|
4012
|
+
connection_interval_max: float,
|
|
4013
|
+
max_latency: int,
|
|
4014
|
+
supervision_timeout: float,
|
|
4015
|
+
min_ce_length: float = 0.0,
|
|
4016
|
+
max_ce_length: float = 0.0,
|
|
4017
|
+
use_l2cap: bool = False,
|
|
3912
4018
|
) -> None:
|
|
3913
4019
|
'''
|
|
4020
|
+
Request an update of the connection parameters.
|
|
4021
|
+
|
|
4022
|
+
Args:
|
|
4023
|
+
connection: The connection to update
|
|
4024
|
+
connection_interval_min: Minimum interval, in milliseconds.
|
|
4025
|
+
connection_interval_max: Maximum interval, in milliseconds.
|
|
4026
|
+
max_latency: Latency, in number of intervals.
|
|
4027
|
+
supervision_timeout: Timeout, in milliseconds.
|
|
4028
|
+
min_ce_length: Minimum connection event length, in milliseconds.
|
|
4029
|
+
max_ce_length: Maximum connection event length, in milliseconds.
|
|
4030
|
+
use_l2cap: Request the update via L2CAP.
|
|
4031
|
+
|
|
3914
4032
|
NOTE: the name of the parameters may look odd, but it just follows the names
|
|
3915
4033
|
used in the Bluetooth spec.
|
|
3916
4034
|
'''
|
|
3917
4035
|
|
|
4036
|
+
# Convert the input parameters
|
|
4037
|
+
connection_interval_min = int(connection_interval_min / 1.25)
|
|
4038
|
+
connection_interval_max = int(connection_interval_max / 1.25)
|
|
4039
|
+
supervision_timeout = int(supervision_timeout / 10)
|
|
4040
|
+
min_ce_length = int(min_ce_length / 0.625)
|
|
4041
|
+
max_ce_length = int(max_ce_length / 0.625)
|
|
4042
|
+
|
|
3918
4043
|
if use_l2cap:
|
|
3919
4044
|
if connection.role != hci.Role.PERIPHERAL:
|
|
3920
4045
|
raise InvalidStateError(
|
|
@@ -3932,6 +4057,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3932
4057
|
if l2cap_result != l2cap.L2CAP_CONNECTION_PARAMETERS_ACCEPTED_RESULT:
|
|
3933
4058
|
raise ConnectionParameterUpdateError(l2cap_result)
|
|
3934
4059
|
|
|
4060
|
+
return
|
|
4061
|
+
|
|
3935
4062
|
result = await self.send_command(
|
|
3936
4063
|
hci.HCI_LE_Connection_Update_Command(
|
|
3937
4064
|
connection_handle=connection.handle,
|
|
@@ -4075,7 +4202,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4075
4202
|
else:
|
|
4076
4203
|
return None
|
|
4077
4204
|
|
|
4078
|
-
return await utils.cancel_on_event(self,
|
|
4205
|
+
return await utils.cancel_on_event(self, Device.EVENT_FLUSH, peer_address)
|
|
4079
4206
|
finally:
|
|
4080
4207
|
if listener is not None:
|
|
4081
4208
|
self.remove_listener(event_name, listener)
|
|
@@ -4125,7 +4252,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4125
4252
|
if not self.scanning:
|
|
4126
4253
|
await self.start_scanning(filter_duplicates=True)
|
|
4127
4254
|
|
|
4128
|
-
return await utils.cancel_on_event(self,
|
|
4255
|
+
return await utils.cancel_on_event(self, Device.EVENT_FLUSH, peer_address)
|
|
4129
4256
|
finally:
|
|
4130
4257
|
if listener is not None:
|
|
4131
4258
|
self.remove_listener(event_name, listener)
|
|
@@ -4201,7 +4328,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4201
4328
|
return keys.link_key.value
|
|
4202
4329
|
|
|
4203
4330
|
# [Classic only]
|
|
4204
|
-
async def authenticate(self, connection):
|
|
4331
|
+
async def authenticate(self, connection: Connection) -> None:
|
|
4205
4332
|
# Set up event handlers
|
|
4206
4333
|
pending_authentication = asyncio.get_running_loop().create_future()
|
|
4207
4334
|
|
|
@@ -4211,8 +4338,11 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4211
4338
|
def on_authentication_failure(error_code):
|
|
4212
4339
|
pending_authentication.set_exception(hci.HCI_Error(error_code))
|
|
4213
4340
|
|
|
4214
|
-
connection.on(
|
|
4215
|
-
connection.on(
|
|
4341
|
+
connection.on(connection.EVENT_CONNECTION_AUTHENTICATION, on_authentication)
|
|
4342
|
+
connection.on(
|
|
4343
|
+
connection.EVENT_CONNECTION_AUTHENTICATION_FAILURE,
|
|
4344
|
+
on_authentication_failure,
|
|
4345
|
+
)
|
|
4216
4346
|
|
|
4217
4347
|
# Request the authentication
|
|
4218
4348
|
try:
|
|
@@ -4230,12 +4360,15 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4230
4360
|
|
|
4231
4361
|
# Wait for the authentication to complete
|
|
4232
4362
|
await utils.cancel_on_event(
|
|
4233
|
-
connection,
|
|
4363
|
+
connection, Connection.EVENT_DISCONNECTION, pending_authentication
|
|
4234
4364
|
)
|
|
4235
4365
|
finally:
|
|
4236
|
-
connection.remove_listener('connection_authentication', on_authentication)
|
|
4237
4366
|
connection.remove_listener(
|
|
4238
|
-
|
|
4367
|
+
connection.EVENT_CONNECTION_AUTHENTICATION, on_authentication
|
|
4368
|
+
)
|
|
4369
|
+
connection.remove_listener(
|
|
4370
|
+
connection.EVENT_CONNECTION_AUTHENTICATION_FAILURE,
|
|
4371
|
+
on_authentication_failure,
|
|
4239
4372
|
)
|
|
4240
4373
|
|
|
4241
4374
|
async def encrypt(self, connection, enable=True):
|
|
@@ -4251,8 +4384,12 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4251
4384
|
def on_encryption_failure(error_code):
|
|
4252
4385
|
pending_encryption.set_exception(hci.HCI_Error(error_code))
|
|
4253
4386
|
|
|
4254
|
-
connection.on(
|
|
4255
|
-
|
|
4387
|
+
connection.on(
|
|
4388
|
+
connection.EVENT_CONNECTION_ENCRYPTION_CHANGE, on_encryption_change
|
|
4389
|
+
)
|
|
4390
|
+
connection.on(
|
|
4391
|
+
connection.EVENT_CONNECTION_ENCRYPTION_FAILURE, on_encryption_failure
|
|
4392
|
+
)
|
|
4256
4393
|
|
|
4257
4394
|
# Request the encryption
|
|
4258
4395
|
try:
|
|
@@ -4311,13 +4448,15 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4311
4448
|
raise hci.HCI_StatusError(result)
|
|
4312
4449
|
|
|
4313
4450
|
# Wait for the result
|
|
4314
|
-
await utils.cancel_on_event(
|
|
4451
|
+
await utils.cancel_on_event(
|
|
4452
|
+
connection, Connection.EVENT_DISCONNECTION, pending_encryption
|
|
4453
|
+
)
|
|
4315
4454
|
finally:
|
|
4316
4455
|
connection.remove_listener(
|
|
4317
|
-
|
|
4456
|
+
connection.EVENT_CONNECTION_ENCRYPTION_CHANGE, on_encryption_change
|
|
4318
4457
|
)
|
|
4319
4458
|
connection.remove_listener(
|
|
4320
|
-
|
|
4459
|
+
connection.EVENT_CONNECTION_ENCRYPTION_FAILURE, on_encryption_failure
|
|
4321
4460
|
)
|
|
4322
4461
|
|
|
4323
4462
|
async def update_keys(self, address: str, keys: PairingKeys) -> None:
|
|
@@ -4330,7 +4469,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4330
4469
|
except Exception as error:
|
|
4331
4470
|
logger.warning(f'!!! error while storing keys: {error}')
|
|
4332
4471
|
else:
|
|
4333
|
-
self.emit(
|
|
4472
|
+
self.emit(self.EVENT_KEY_STORE_UPDATE)
|
|
4334
4473
|
|
|
4335
4474
|
# [Classic only]
|
|
4336
4475
|
async def switch_role(self, connection: Connection, role: hci.Role):
|
|
@@ -4342,8 +4481,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4342
4481
|
def on_role_change_failure(error_code):
|
|
4343
4482
|
pending_role_change.set_exception(hci.HCI_Error(error_code))
|
|
4344
4483
|
|
|
4345
|
-
connection.on(
|
|
4346
|
-
connection.on(
|
|
4484
|
+
connection.on(connection.EVENT_ROLE_CHANGE, on_role_change)
|
|
4485
|
+
connection.on(connection.EVENT_ROLE_CHANGE_FAILURE, on_role_change_failure)
|
|
4347
4486
|
|
|
4348
4487
|
try:
|
|
4349
4488
|
result = await self.send_command(
|
|
@@ -4356,11 +4495,13 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4356
4495
|
)
|
|
4357
4496
|
raise hci.HCI_StatusError(result)
|
|
4358
4497
|
await utils.cancel_on_event(
|
|
4359
|
-
connection,
|
|
4498
|
+
connection, Connection.EVENT_DISCONNECTION, pending_role_change
|
|
4360
4499
|
)
|
|
4361
4500
|
finally:
|
|
4362
|
-
connection.remove_listener(
|
|
4363
|
-
connection.remove_listener(
|
|
4501
|
+
connection.remove_listener(connection.EVENT_ROLE_CHANGE, on_role_change)
|
|
4502
|
+
connection.remove_listener(
|
|
4503
|
+
connection.EVENT_ROLE_CHANGE_FAILURE, on_role_change_failure
|
|
4504
|
+
)
|
|
4364
4505
|
|
|
4365
4506
|
# [Classic only]
|
|
4366
4507
|
async def request_remote_name(self, remote: Union[hci.Address, Connection]) -> str:
|
|
@@ -4372,7 +4513,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4372
4513
|
)
|
|
4373
4514
|
|
|
4374
4515
|
handler = self.on(
|
|
4375
|
-
|
|
4516
|
+
self.EVENT_REMOTE_NAME,
|
|
4376
4517
|
lambda address, remote_name: (
|
|
4377
4518
|
pending_name.set_result(remote_name)
|
|
4378
4519
|
if address == peer_address
|
|
@@ -4380,7 +4521,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4380
4521
|
),
|
|
4381
4522
|
)
|
|
4382
4523
|
failure_handler = self.on(
|
|
4383
|
-
|
|
4524
|
+
self.EVENT_REMOTE_NAME_FAILURE,
|
|
4384
4525
|
lambda address, error_code: (
|
|
4385
4526
|
pending_name.set_exception(hci.HCI_Error(error_code))
|
|
4386
4527
|
if address == peer_address
|
|
@@ -4406,10 +4547,10 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4406
4547
|
raise hci.HCI_StatusError(result)
|
|
4407
4548
|
|
|
4408
4549
|
# Wait for the result
|
|
4409
|
-
return await utils.cancel_on_event(self,
|
|
4550
|
+
return await utils.cancel_on_event(self, Device.EVENT_FLUSH, pending_name)
|
|
4410
4551
|
finally:
|
|
4411
|
-
self.remove_listener(
|
|
4412
|
-
self.remove_listener(
|
|
4552
|
+
self.remove_listener(self.EVENT_REMOTE_NAME, handler)
|
|
4553
|
+
self.remove_listener(self.EVENT_REMOTE_NAME_FAILURE, failure_handler)
|
|
4413
4554
|
|
|
4414
4555
|
# [LE only]
|
|
4415
4556
|
@utils.experimental('Only for testing.')
|
|
@@ -4500,8 +4641,10 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4500
4641
|
if pending_future := pending_cis_establishments.get(cis_handle):
|
|
4501
4642
|
pending_future.set_exception(hci.HCI_Error(status))
|
|
4502
4643
|
|
|
4503
|
-
watcher.on(self,
|
|
4504
|
-
watcher.on(
|
|
4644
|
+
watcher.on(self, self.EVENT_CIS_ESTABLISHMENT, on_cis_establishment)
|
|
4645
|
+
watcher.on(
|
|
4646
|
+
self, self.EVENT_CIS_ESTABLISHMENT_FAILURE, on_cis_establishment_failure
|
|
4647
|
+
)
|
|
4505
4648
|
await self.send_command(
|
|
4506
4649
|
hci.HCI_LE_Create_CIS_Command(
|
|
4507
4650
|
cis_connection_handle=[p[0] for p in cis_acl_pairs],
|
|
@@ -4544,8 +4687,12 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4544
4687
|
def on_establishment_failure(status: int) -> None:
|
|
4545
4688
|
pending_establishment.set_exception(hci.HCI_Error(status))
|
|
4546
4689
|
|
|
4547
|
-
watcher.on(cis_link,
|
|
4548
|
-
watcher.on(
|
|
4690
|
+
watcher.on(cis_link, cis_link.EVENT_ESTABLISHMENT, on_establishment)
|
|
4691
|
+
watcher.on(
|
|
4692
|
+
cis_link,
|
|
4693
|
+
cis_link.EVENT_ESTABLISHMENT_FAILURE,
|
|
4694
|
+
on_establishment_failure,
|
|
4695
|
+
)
|
|
4549
4696
|
|
|
4550
4697
|
await self.send_command(
|
|
4551
4698
|
hci.HCI_LE_Accept_CIS_Request_Command(connection_handle=handle),
|
|
@@ -4913,33 +5060,33 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4913
5060
|
|
|
4914
5061
|
@host_event_handler
|
|
4915
5062
|
def on_flush(self):
|
|
4916
|
-
self.emit(
|
|
5063
|
+
self.emit(self.EVENT_FLUSH)
|
|
4917
5064
|
for _, connection in self.connections.items():
|
|
4918
|
-
connection.emit(
|
|
5065
|
+
connection.emit(connection.EVENT_DISCONNECTION, 0)
|
|
4919
5066
|
self.connections = {}
|
|
4920
5067
|
|
|
4921
5068
|
# [Classic only]
|
|
4922
5069
|
@host_event_handler
|
|
4923
|
-
def on_link_key(self, bd_addr, link_key, key_type):
|
|
5070
|
+
def on_link_key(self, bd_addr: hci.Address, link_key: bytes, key_type: int) -> None:
|
|
4924
5071
|
# Store the keys in the key store
|
|
4925
5072
|
if self.keystore:
|
|
4926
5073
|
authenticated = key_type in (
|
|
4927
5074
|
hci.HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_192_TYPE,
|
|
4928
5075
|
hci.HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_256_TYPE,
|
|
4929
5076
|
)
|
|
4930
|
-
pairing_keys = PairingKeys(
|
|
4931
|
-
|
|
4932
|
-
|
|
5077
|
+
pairing_keys = PairingKeys(
|
|
5078
|
+
link_key=PairingKeys.Key(value=link_key, authenticated=authenticated),
|
|
5079
|
+
link_key_type=key_type,
|
|
4933
5080
|
)
|
|
4934
5081
|
|
|
4935
5082
|
utils.cancel_on_event(
|
|
4936
|
-
self,
|
|
5083
|
+
self, Device.EVENT_FLUSH, self.update_keys(str(bd_addr), pairing_keys)
|
|
4937
5084
|
)
|
|
4938
5085
|
|
|
4939
5086
|
if connection := self.find_connection_by_bd_addr(
|
|
4940
5087
|
bd_addr, transport=PhysicalTransport.BR_EDR
|
|
4941
5088
|
):
|
|
4942
|
-
connection.
|
|
5089
|
+
connection.emit(connection.EVENT_LINK_KEY)
|
|
4943
5090
|
|
|
4944
5091
|
def add_service(self, service):
|
|
4945
5092
|
self.gatt_server.add_service(service)
|
|
@@ -5111,7 +5258,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5111
5258
|
big.pto = pto
|
|
5112
5259
|
big.irc = irc
|
|
5113
5260
|
big.max_pdu = max_pdu
|
|
5114
|
-
big.iso_interval = iso_interval
|
|
5261
|
+
big.iso_interval = iso_interval * 1.25
|
|
5115
5262
|
big.state = Big.State.ACTIVE
|
|
5116
5263
|
|
|
5117
5264
|
for bis_link in big.bis_links:
|
|
@@ -5160,7 +5307,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5160
5307
|
big_sync.pto = pto
|
|
5161
5308
|
big_sync.irc = irc
|
|
5162
5309
|
big_sync.max_pdu = max_pdu
|
|
5163
|
-
big_sync.iso_interval = iso_interval
|
|
5310
|
+
big_sync.iso_interval = iso_interval * 1.25
|
|
5164
5311
|
big_sync.bis_links = [
|
|
5165
5312
|
BisLink(handle=handle, big=big_sync) for handle in bis_handles
|
|
5166
5313
|
]
|
|
@@ -5202,11 +5349,13 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5202
5349
|
# Setup auto-restart of the advertising set if needed.
|
|
5203
5350
|
if advertising_set.auto_restart:
|
|
5204
5351
|
connection.once(
|
|
5205
|
-
|
|
5206
|
-
lambda _: utils.cancel_on_event(
|
|
5352
|
+
Connection.EVENT_DISCONNECTION,
|
|
5353
|
+
lambda _: utils.cancel_on_event(
|
|
5354
|
+
self, Device.EVENT_FLUSH, advertising_set.start()
|
|
5355
|
+
),
|
|
5207
5356
|
)
|
|
5208
5357
|
|
|
5209
|
-
self.emit(
|
|
5358
|
+
self.emit(self.EVENT_CONNECTION, connection)
|
|
5210
5359
|
|
|
5211
5360
|
@host_event_handler
|
|
5212
5361
|
def on_connection(
|
|
@@ -5217,7 +5366,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5217
5366
|
self_resolvable_address: Optional[hci.Address],
|
|
5218
5367
|
peer_resolvable_address: Optional[hci.Address],
|
|
5219
5368
|
role: hci.Role,
|
|
5220
|
-
connection_parameters: ConnectionParameters,
|
|
5369
|
+
connection_parameters: Optional[core.ConnectionParameters],
|
|
5221
5370
|
) -> None:
|
|
5222
5371
|
# Convert all-zeros addresses into None.
|
|
5223
5372
|
if self_resolvable_address == hci.Address.ANY_RANDOM:
|
|
@@ -5244,10 +5393,12 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5244
5393
|
self.connections[connection_handle] = connection
|
|
5245
5394
|
|
|
5246
5395
|
# Emit an event to notify listeners of the new connection
|
|
5247
|
-
self.emit(
|
|
5396
|
+
self.emit(self.EVENT_CONNECTION, connection)
|
|
5248
5397
|
|
|
5249
5398
|
return
|
|
5250
5399
|
|
|
5400
|
+
assert connection_parameters is not None
|
|
5401
|
+
|
|
5251
5402
|
if peer_resolvable_address is None:
|
|
5252
5403
|
# Resolve the peer address if we can
|
|
5253
5404
|
if self.address_resolver:
|
|
@@ -5303,7 +5454,11 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5303
5454
|
peer_address,
|
|
5304
5455
|
peer_resolvable_address,
|
|
5305
5456
|
role,
|
|
5306
|
-
|
|
5457
|
+
Connection.Parameters(
|
|
5458
|
+
connection_parameters.connection_interval * 1.25,
|
|
5459
|
+
connection_parameters.peripheral_latency,
|
|
5460
|
+
connection_parameters.supervision_timeout * 10.0,
|
|
5461
|
+
),
|
|
5307
5462
|
)
|
|
5308
5463
|
self.connections[connection_handle] = connection
|
|
5309
5464
|
|
|
@@ -5311,15 +5466,17 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5311
5466
|
if self.legacy_advertiser.auto_restart:
|
|
5312
5467
|
advertiser = self.legacy_advertiser
|
|
5313
5468
|
connection.once(
|
|
5314
|
-
|
|
5315
|
-
lambda _: utils.cancel_on_event(
|
|
5469
|
+
Connection.EVENT_DISCONNECTION,
|
|
5470
|
+
lambda _: utils.cancel_on_event(
|
|
5471
|
+
self, Device.EVENT_FLUSH, advertiser.start()
|
|
5472
|
+
),
|
|
5316
5473
|
)
|
|
5317
5474
|
else:
|
|
5318
5475
|
self.legacy_advertiser = None
|
|
5319
5476
|
|
|
5320
5477
|
if role == hci.Role.CENTRAL or not self.supports_le_extended_advertising:
|
|
5321
5478
|
# We can emit now, we have all the info we need
|
|
5322
|
-
self.emit(
|
|
5479
|
+
self.emit(self.EVENT_CONNECTION, connection)
|
|
5323
5480
|
return
|
|
5324
5481
|
|
|
5325
5482
|
if role == hci.Role.PERIPHERAL and self.supports_le_extended_advertising:
|
|
@@ -5353,7 +5510,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5353
5510
|
'hci',
|
|
5354
5511
|
hci.HCI_Constant.error_name(error_code),
|
|
5355
5512
|
)
|
|
5356
|
-
self.emit(
|
|
5513
|
+
self.emit(self.EVENT_CONNECTION_FAILURE, error)
|
|
5357
5514
|
|
|
5358
5515
|
# FIXME: Explore a delegate-model for BR/EDR wait connection #56.
|
|
5359
5516
|
@host_event_handler
|
|
@@ -5368,7 +5525,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5368
5525
|
if connection := self.find_connection_by_bd_addr(
|
|
5369
5526
|
bd_addr, transport=PhysicalTransport.BR_EDR
|
|
5370
5527
|
):
|
|
5371
|
-
self.emit(
|
|
5528
|
+
self.emit(self.EVENT_SCO_REQUEST, connection, link_type)
|
|
5372
5529
|
else:
|
|
5373
5530
|
logger.error(f'SCO request from a non-connected device {bd_addr}')
|
|
5374
5531
|
return
|
|
@@ -5412,14 +5569,14 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5412
5569
|
f'*** Disconnection: [0x{connection.handle:04X}] '
|
|
5413
5570
|
f'{connection.peer_address} as {connection.role_name}, reason={reason}'
|
|
5414
5571
|
)
|
|
5415
|
-
connection.emit(
|
|
5572
|
+
connection.emit(connection.EVENT_DISCONNECTION, reason)
|
|
5416
5573
|
|
|
5417
5574
|
# Cleanup subsystems that maintain per-connection state
|
|
5418
5575
|
self.gatt_server.on_disconnection(connection)
|
|
5419
5576
|
elif sco_link := self.sco_links.pop(connection_handle, None):
|
|
5420
|
-
sco_link.emit(
|
|
5577
|
+
sco_link.emit(sco_link.EVENT_DISCONNECTION, reason)
|
|
5421
5578
|
elif cis_link := self.cis_links.pop(connection_handle, None):
|
|
5422
|
-
cis_link.emit(
|
|
5579
|
+
cis_link.emit(cis_link.EVENT_DISCONNECTION, reason)
|
|
5423
5580
|
else:
|
|
5424
5581
|
logger.error(
|
|
5425
5582
|
f'*** Unknown disconnection handle=0x{connection_handle}, reason={reason} ***'
|
|
@@ -5427,7 +5584,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5427
5584
|
|
|
5428
5585
|
@host_event_handler
|
|
5429
5586
|
@with_connection_from_handle
|
|
5430
|
-
def on_disconnection_failure(self, connection, error_code):
|
|
5587
|
+
def on_disconnection_failure(self, connection: Connection, error_code: int):
|
|
5431
5588
|
logger.debug(f'*** Disconnection failed: {error_code}')
|
|
5432
5589
|
error = core.ConnectionError(
|
|
5433
5590
|
error_code,
|
|
@@ -5436,7 +5593,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5436
5593
|
'hci',
|
|
5437
5594
|
hci.HCI_Constant.error_name(error_code),
|
|
5438
5595
|
)
|
|
5439
|
-
connection.emit(
|
|
5596
|
+
connection.emit(connection.EVENT_DISCONNECTION_FAILURE, error)
|
|
5440
5597
|
|
|
5441
5598
|
@host_event_handler
|
|
5442
5599
|
@utils.AsyncRunner.run_in_task()
|
|
@@ -5447,7 +5604,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5447
5604
|
else:
|
|
5448
5605
|
self.auto_restart_inquiry = True
|
|
5449
5606
|
self.discovering = False
|
|
5450
|
-
self.emit(
|
|
5607
|
+
self.emit(self.EVENT_INQUIRY_COMPLETE)
|
|
5451
5608
|
|
|
5452
5609
|
@host_event_handler
|
|
5453
5610
|
@with_connection_from_handle
|
|
@@ -5457,7 +5614,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5457
5614
|
f'{connection.peer_address} as {connection.role_name}'
|
|
5458
5615
|
)
|
|
5459
5616
|
connection.authenticated = True
|
|
5460
|
-
connection.emit(
|
|
5617
|
+
connection.emit(connection.EVENT_CONNECTION_AUTHENTICATION)
|
|
5461
5618
|
|
|
5462
5619
|
@host_event_handler
|
|
5463
5620
|
@with_connection_from_handle
|
|
@@ -5466,7 +5623,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5466
5623
|
f'*** Connection Authentication Failure: [0x{connection.handle:04X}] '
|
|
5467
5624
|
f'{connection.peer_address} as {connection.role_name}, error={error}'
|
|
5468
5625
|
)
|
|
5469
|
-
connection.emit(
|
|
5626
|
+
connection.emit(connection.EVENT_CONNECTION_AUTHENTICATION_FAILURE, error)
|
|
5470
5627
|
|
|
5471
5628
|
# [Classic only]
|
|
5472
5629
|
@host_event_handler
|
|
@@ -5571,7 +5728,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5571
5728
|
|
|
5572
5729
|
async def reply() -> None:
|
|
5573
5730
|
try:
|
|
5574
|
-
if await utils.cancel_on_event(
|
|
5731
|
+
if await utils.cancel_on_event(
|
|
5732
|
+
connection, Connection.EVENT_DISCONNECTION, method()
|
|
5733
|
+
):
|
|
5575
5734
|
await self.host.send_command(
|
|
5576
5735
|
hci.HCI_User_Confirmation_Request_Reply_Command(
|
|
5577
5736
|
bd_addr=connection.peer_address
|
|
@@ -5599,7 +5758,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5599
5758
|
async def reply() -> None:
|
|
5600
5759
|
try:
|
|
5601
5760
|
number = await utils.cancel_on_event(
|
|
5602
|
-
connection,
|
|
5761
|
+
connection,
|
|
5762
|
+
Connection.EVENT_DISCONNECTION,
|
|
5763
|
+
pairing_config.delegate.get_number(),
|
|
5603
5764
|
)
|
|
5604
5765
|
if number is not None:
|
|
5605
5766
|
await self.host.send_command(
|
|
@@ -5633,7 +5794,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5633
5794
|
# Ask the user to enter a string
|
|
5634
5795
|
async def get_pin_code():
|
|
5635
5796
|
pin_code = await utils.cancel_on_event(
|
|
5636
|
-
connection,
|
|
5797
|
+
connection,
|
|
5798
|
+
Connection.EVENT_DISCONNECTION,
|
|
5799
|
+
pairing_config.delegate.get_string(16),
|
|
5637
5800
|
)
|
|
5638
5801
|
|
|
5639
5802
|
if pin_code is not None:
|
|
@@ -5672,7 +5835,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5672
5835
|
|
|
5673
5836
|
# Show the passkey to the user
|
|
5674
5837
|
utils.cancel_on_event(
|
|
5675
|
-
connection,
|
|
5838
|
+
connection,
|
|
5839
|
+
Connection.EVENT_DISCONNECTION,
|
|
5840
|
+
pairing_config.delegate.display_number(passkey, digits=6),
|
|
5676
5841
|
)
|
|
5677
5842
|
|
|
5678
5843
|
# [Classic only]
|
|
@@ -5684,22 +5849,22 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5684
5849
|
remote_name = remote_name.decode('utf-8')
|
|
5685
5850
|
if connection:
|
|
5686
5851
|
connection.peer_name = remote_name
|
|
5687
|
-
connection.emit(
|
|
5688
|
-
self.emit(
|
|
5852
|
+
connection.emit(connection.EVENT_REMOTE_NAME)
|
|
5853
|
+
self.emit(self.EVENT_REMOTE_NAME, address, remote_name)
|
|
5689
5854
|
except UnicodeDecodeError as error:
|
|
5690
5855
|
logger.warning('peer name is not valid UTF-8')
|
|
5691
5856
|
if connection:
|
|
5692
|
-
connection.emit(
|
|
5857
|
+
connection.emit(connection.EVENT_REMOTE_NAME_FAILURE, error)
|
|
5693
5858
|
else:
|
|
5694
|
-
self.emit(
|
|
5859
|
+
self.emit(self.EVENT_REMOTE_NAME_FAILURE, address, error)
|
|
5695
5860
|
|
|
5696
5861
|
# [Classic only]
|
|
5697
5862
|
@host_event_handler
|
|
5698
5863
|
@try_with_connection_from_address
|
|
5699
5864
|
def on_remote_name_failure(self, connection: Connection, address, error):
|
|
5700
5865
|
if connection:
|
|
5701
|
-
connection.emit(
|
|
5702
|
-
self.emit(
|
|
5866
|
+
connection.emit(connection.EVENT_REMOTE_NAME_FAILURE, error)
|
|
5867
|
+
self.emit(self.EVENT_REMOTE_NAME_FAILURE, address, error)
|
|
5703
5868
|
|
|
5704
5869
|
# [Classic only]
|
|
5705
5870
|
@host_event_handler
|
|
@@ -5719,7 +5884,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5719
5884
|
handle=sco_handle,
|
|
5720
5885
|
link_type=link_type,
|
|
5721
5886
|
)
|
|
5722
|
-
self.emit(
|
|
5887
|
+
self.emit(self.EVENT_SCO_CONNECTION, sco_link)
|
|
5723
5888
|
|
|
5724
5889
|
# [Classic only]
|
|
5725
5890
|
@host_event_handler
|
|
@@ -5729,7 +5894,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5729
5894
|
self, acl_connection: Connection, status: int
|
|
5730
5895
|
) -> None:
|
|
5731
5896
|
logger.debug(f'*** SCO connection failure: {acl_connection.peer_address}***')
|
|
5732
|
-
self.emit(
|
|
5897
|
+
self.emit(self.EVENT_SCO_CONNECTION_FAILURE)
|
|
5733
5898
|
|
|
5734
5899
|
# [Classic only]
|
|
5735
5900
|
@host_event_handler
|
|
@@ -5766,7 +5931,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5766
5931
|
cig_id=cig_id,
|
|
5767
5932
|
cis_id=cis_id,
|
|
5768
5933
|
)
|
|
5769
|
-
self.emit(
|
|
5934
|
+
self.emit(self.EVENT_CIS_REQUEST, acl_connection, cis_handle, cig_id, cis_id)
|
|
5770
5935
|
|
|
5771
5936
|
# [LE only]
|
|
5772
5937
|
@host_event_handler
|
|
@@ -5785,8 +5950,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5785
5950
|
f'cis_id=[0x{cis_link.cis_id:02X}] ***'
|
|
5786
5951
|
)
|
|
5787
5952
|
|
|
5788
|
-
cis_link.emit(
|
|
5789
|
-
self.emit(
|
|
5953
|
+
cis_link.emit(cis_link.EVENT_ESTABLISHMENT)
|
|
5954
|
+
self.emit(self.EVENT_CIS_ESTABLISHMENT, cis_link)
|
|
5790
5955
|
|
|
5791
5956
|
# [LE only]
|
|
5792
5957
|
@host_event_handler
|
|
@@ -5794,8 +5959,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5794
5959
|
def on_cis_establishment_failure(self, cis_handle: int, status: int) -> None:
|
|
5795
5960
|
logger.debug(f'*** CIS Establishment Failure: cis=[0x{cis_handle:04X}] ***')
|
|
5796
5961
|
if cis_link := self.cis_links.pop(cis_handle):
|
|
5797
|
-
cis_link.emit(
|
|
5798
|
-
self.emit(
|
|
5962
|
+
cis_link.emit(cis_link.EVENT_ESTABLISHMENT_FAILURE, status)
|
|
5963
|
+
self.emit(self.EVENT_CIS_ESTABLISHMENT_FAILURE, cis_handle, status)
|
|
5799
5964
|
|
|
5800
5965
|
# [LE only]
|
|
5801
5966
|
@host_event_handler
|
|
@@ -5808,13 +5973,17 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5808
5973
|
|
|
5809
5974
|
@host_event_handler
|
|
5810
5975
|
@with_connection_from_handle
|
|
5811
|
-
def on_connection_encryption_change(
|
|
5976
|
+
def on_connection_encryption_change(
|
|
5977
|
+
self, connection, encryption, encryption_key_size
|
|
5978
|
+
):
|
|
5812
5979
|
logger.debug(
|
|
5813
5980
|
f'*** Connection Encryption Change: [0x{connection.handle:04X}] '
|
|
5814
5981
|
f'{connection.peer_address} as {connection.role_name}, '
|
|
5815
|
-
f'encryption={encryption}'
|
|
5982
|
+
f'encryption={encryption}, '
|
|
5983
|
+
f'key_size={encryption_key_size}'
|
|
5816
5984
|
)
|
|
5817
5985
|
connection.encryption = encryption
|
|
5986
|
+
connection.encryption_key_size = encryption_key_size
|
|
5818
5987
|
if (
|
|
5819
5988
|
not connection.authenticated
|
|
5820
5989
|
and connection.transport == PhysicalTransport.BR_EDR
|
|
@@ -5829,7 +5998,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5829
5998
|
):
|
|
5830
5999
|
connection.authenticated = True
|
|
5831
6000
|
connection.sc = True
|
|
5832
|
-
connection.emit(
|
|
6001
|
+
connection.emit(connection.EVENT_CONNECTION_ENCRYPTION_CHANGE)
|
|
5833
6002
|
|
|
5834
6003
|
@host_event_handler
|
|
5835
6004
|
@with_connection_from_handle
|
|
@@ -5839,7 +6008,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5839
6008
|
f'{connection.peer_address} as {connection.role_name}, '
|
|
5840
6009
|
f'error={error}'
|
|
5841
6010
|
)
|
|
5842
|
-
connection.emit(
|
|
6011
|
+
connection.emit(connection.EVENT_CONNECTION_ENCRYPTION_FAILURE, error)
|
|
5843
6012
|
|
|
5844
6013
|
@host_event_handler
|
|
5845
6014
|
@with_connection_from_handle
|
|
@@ -5848,7 +6017,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5848
6017
|
f'*** Connection Key Refresh: [0x{connection.handle:04X}] '
|
|
5849
6018
|
f'{connection.peer_address} as {connection.role_name}'
|
|
5850
6019
|
)
|
|
5851
|
-
connection.emit(
|
|
6020
|
+
connection.emit(connection.EVENT_CONNECTION_ENCRYPTION_KEY_REFRESH)
|
|
5852
6021
|
|
|
5853
6022
|
@host_event_handler
|
|
5854
6023
|
@with_connection_from_handle
|
|
@@ -5859,7 +6028,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5859
6028
|
f'{connection_parameters}'
|
|
5860
6029
|
)
|
|
5861
6030
|
connection.parameters = connection_parameters
|
|
5862
|
-
connection.emit(
|
|
6031
|
+
connection.emit(connection.EVENT_CONNECTION_PARAMETERS_UPDATE)
|
|
5863
6032
|
|
|
5864
6033
|
@host_event_handler
|
|
5865
6034
|
@with_connection_from_handle
|
|
@@ -5869,7 +6038,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5869
6038
|
f'{connection.peer_address} as {connection.role_name}, '
|
|
5870
6039
|
f'error={error}'
|
|
5871
6040
|
)
|
|
5872
|
-
connection.emit(
|
|
6041
|
+
connection.emit(connection.EVENT_CONNECTION_PARAMETERS_UPDATE_FAILURE, error)
|
|
5873
6042
|
|
|
5874
6043
|
@host_event_handler
|
|
5875
6044
|
@with_connection_from_handle
|
|
@@ -5879,7 +6048,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5879
6048
|
f'{connection.peer_address} as {connection.role_name}, '
|
|
5880
6049
|
f'{phy}'
|
|
5881
6050
|
)
|
|
5882
|
-
connection.emit(
|
|
6051
|
+
connection.emit(connection.EVENT_CONNECTION_PHY_UPDATE, phy)
|
|
5883
6052
|
|
|
5884
6053
|
@host_event_handler
|
|
5885
6054
|
@with_connection_from_handle
|
|
@@ -5889,7 +6058,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5889
6058
|
f'{connection.peer_address} as {connection.role_name}, '
|
|
5890
6059
|
f'error={error}'
|
|
5891
6060
|
)
|
|
5892
|
-
connection.emit(
|
|
6061
|
+
connection.emit(connection.EVENT_CONNECTION_PHY_UPDATE_FAILURE, error)
|
|
5893
6062
|
|
|
5894
6063
|
@host_event_handler
|
|
5895
6064
|
@with_connection_from_handle
|
|
@@ -5900,7 +6069,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5900
6069
|
f'{att_mtu}'
|
|
5901
6070
|
)
|
|
5902
6071
|
connection.att_mtu = att_mtu
|
|
5903
|
-
connection.emit(
|
|
6072
|
+
connection.emit(connection.EVENT_CONNECTION_ATT_MTU_UPDATE)
|
|
5904
6073
|
|
|
5905
6074
|
@host_event_handler
|
|
5906
6075
|
@with_connection_from_handle
|
|
@@ -5917,7 +6086,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5917
6086
|
max_rx_octets,
|
|
5918
6087
|
max_rx_time,
|
|
5919
6088
|
)
|
|
5920
|
-
connection.emit(
|
|
6089
|
+
connection.emit(connection.EVENT_CONNECTION_DATA_LENGTH_CHANGE)
|
|
5921
6090
|
|
|
5922
6091
|
@host_event_handler
|
|
5923
6092
|
def on_cs_remote_supported_capabilities(
|
|
@@ -5927,7 +6096,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5927
6096
|
return
|
|
5928
6097
|
|
|
5929
6098
|
if event.status != hci.HCI_SUCCESS:
|
|
5930
|
-
connection.emit(
|
|
6099
|
+
connection.emit(
|
|
6100
|
+
connection.EVENT_CHANNEL_SOUNDING_CAPABILITIES_FAILURE, event.status
|
|
6101
|
+
)
|
|
5931
6102
|
return
|
|
5932
6103
|
|
|
5933
6104
|
capabilities = ChannelSoundingCapabilities(
|
|
@@ -5952,7 +6123,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5952
6123
|
t_sw_time_supported=event.t_sw_time_supported,
|
|
5953
6124
|
tx_snr_capability=event.tx_snr_capability,
|
|
5954
6125
|
)
|
|
5955
|
-
connection.emit(
|
|
6126
|
+
connection.emit(connection.EVENT_CHANNEL_SOUNDING_CAPABILITIES, capabilities)
|
|
5956
6127
|
|
|
5957
6128
|
@host_event_handler
|
|
5958
6129
|
def on_cs_config(self, event: hci.HCI_LE_CS_Config_Complete_Event):
|
|
@@ -5960,7 +6131,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5960
6131
|
return
|
|
5961
6132
|
|
|
5962
6133
|
if event.status != hci.HCI_SUCCESS:
|
|
5963
|
-
connection.emit(
|
|
6134
|
+
connection.emit(
|
|
6135
|
+
connection.EVENT_CHANNEL_SOUNDING_CONFIG_FAILURE, event.status
|
|
6136
|
+
)
|
|
5964
6137
|
return
|
|
5965
6138
|
if event.action == hci.HCI_LE_CS_Config_Complete_Event.Action.CREATED:
|
|
5966
6139
|
config = ChannelSoundingConfig(
|
|
@@ -5986,11 +6159,13 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5986
6159
|
t_pm_time=event.t_pm_time,
|
|
5987
6160
|
)
|
|
5988
6161
|
connection.cs_configs[event.config_id] = config
|
|
5989
|
-
connection.emit(
|
|
6162
|
+
connection.emit(connection.EVENT_CHANNEL_SOUNDING_CONFIG, config)
|
|
5990
6163
|
elif event.action == hci.HCI_LE_CS_Config_Complete_Event.Action.REMOVED:
|
|
5991
6164
|
try:
|
|
5992
6165
|
config = connection.cs_configs.pop(event.config_id)
|
|
5993
|
-
connection.emit(
|
|
6166
|
+
connection.emit(
|
|
6167
|
+
connection.EVENT_CHANNEL_SOUNDING_CONFIG_REMOVED, config.config_id
|
|
6168
|
+
)
|
|
5994
6169
|
except KeyError:
|
|
5995
6170
|
logger.error('Removing unknown config %d', event.config_id)
|
|
5996
6171
|
|
|
@@ -6000,7 +6175,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6000
6175
|
return
|
|
6001
6176
|
|
|
6002
6177
|
if event.status != hci.HCI_SUCCESS:
|
|
6003
|
-
connection.emit(
|
|
6178
|
+
connection.emit(
|
|
6179
|
+
connection.EVENT_CHANNEL_SOUNDING_PROCEDURE_FAILURE, event.status
|
|
6180
|
+
)
|
|
6004
6181
|
return
|
|
6005
6182
|
|
|
6006
6183
|
procedure = ChannelSoundingProcedure(
|
|
@@ -6017,37 +6194,37 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6017
6194
|
max_procedure_len=event.max_procedure_len,
|
|
6018
6195
|
)
|
|
6019
6196
|
connection.cs_procedures[procedure.config_id] = procedure
|
|
6020
|
-
connection.emit(
|
|
6197
|
+
connection.emit(connection.EVENT_CHANNEL_SOUNDING_PROCEDURE, procedure)
|
|
6021
6198
|
|
|
6022
6199
|
# [Classic only]
|
|
6023
6200
|
@host_event_handler
|
|
6024
6201
|
@with_connection_from_address
|
|
6025
6202
|
def on_role_change(self, connection, new_role):
|
|
6026
6203
|
connection.role = new_role
|
|
6027
|
-
connection.emit(
|
|
6204
|
+
connection.emit(connection.EVENT_ROLE_CHANGE, new_role)
|
|
6028
6205
|
|
|
6029
6206
|
# [Classic only]
|
|
6030
6207
|
@host_event_handler
|
|
6031
6208
|
@try_with_connection_from_address
|
|
6032
6209
|
def on_role_change_failure(self, connection, address, error):
|
|
6033
6210
|
if connection:
|
|
6034
|
-
connection.emit(
|
|
6035
|
-
self.emit(
|
|
6211
|
+
connection.emit(connection.EVENT_ROLE_CHANGE_FAILURE, error)
|
|
6212
|
+
self.emit(self.EVENT_ROLE_CHANGE_FAILURE, address, error)
|
|
6036
6213
|
|
|
6037
6214
|
# [Classic only]
|
|
6038
6215
|
@host_event_handler
|
|
6039
6216
|
@with_connection_from_address
|
|
6040
6217
|
def on_classic_pairing(self, connection: Connection) -> None:
|
|
6041
|
-
connection.emit(
|
|
6218
|
+
connection.emit(connection.EVENT_CLASSIC_PAIRING)
|
|
6042
6219
|
|
|
6043
6220
|
# [Classic only]
|
|
6044
6221
|
@host_event_handler
|
|
6045
6222
|
@with_connection_from_address
|
|
6046
6223
|
def on_classic_pairing_failure(self, connection: Connection, status) -> None:
|
|
6047
|
-
connection.emit(
|
|
6224
|
+
connection.emit(connection.EVENT_CLASSIC_PAIRING_FAILURE, status)
|
|
6048
6225
|
|
|
6049
6226
|
def on_pairing_start(self, connection: Connection) -> None:
|
|
6050
|
-
connection.emit(
|
|
6227
|
+
connection.emit(connection.EVENT_PAIRING_START)
|
|
6051
6228
|
|
|
6052
6229
|
def on_pairing(
|
|
6053
6230
|
self,
|
|
@@ -6061,10 +6238,10 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6061
6238
|
connection.peer_address = identity_address
|
|
6062
6239
|
connection.sc = sc
|
|
6063
6240
|
connection.authenticated = True
|
|
6064
|
-
connection.emit(
|
|
6241
|
+
connection.emit(connection.EVENT_PAIRING, keys)
|
|
6065
6242
|
|
|
6066
6243
|
def on_pairing_failure(self, connection: Connection, reason: int) -> None:
|
|
6067
|
-
connection.emit(
|
|
6244
|
+
connection.emit(connection.EVENT_PAIRING_FAILURE, reason)
|
|
6068
6245
|
|
|
6069
6246
|
@with_connection_from_handle
|
|
6070
6247
|
def on_gatt_pdu(self, connection, pdu):
|