bumble 0.0.210__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.
Files changed (44) hide show
  1. bumble/_version.py +2 -2
  2. bumble/apps/bench.py +8 -4
  3. bumble/apps/console.py +2 -2
  4. bumble/apps/pair.py +185 -32
  5. bumble/att.py +13 -12
  6. bumble/avctp.py +2 -2
  7. bumble/avdtp.py +122 -68
  8. bumble/avrcp.py +11 -5
  9. bumble/core.py +13 -7
  10. bumble/{crypto.py → crypto/__init__.py} +11 -95
  11. bumble/crypto/builtin.py +652 -0
  12. bumble/crypto/cryptography.py +84 -0
  13. bumble/device.py +365 -185
  14. bumble/drivers/intel.py +3 -0
  15. bumble/gatt.py +3 -5
  16. bumble/gatt_client.py +5 -3
  17. bumble/gatt_server.py +8 -6
  18. bumble/hci.py +81 -4
  19. bumble/hfp.py +44 -20
  20. bumble/hid.py +24 -12
  21. bumble/host.py +24 -0
  22. bumble/keys.py +64 -48
  23. bumble/l2cap.py +19 -9
  24. bumble/pandora/host.py +11 -11
  25. bumble/pandora/l2cap.py +2 -2
  26. bumble/pandora/security.py +72 -56
  27. bumble/profiles/aics.py +3 -5
  28. bumble/profiles/ancs.py +3 -1
  29. bumble/profiles/ascs.py +11 -5
  30. bumble/profiles/asha.py +11 -6
  31. bumble/profiles/csip.py +1 -3
  32. bumble/profiles/gatt_service.py +1 -3
  33. bumble/profiles/hap.py +16 -33
  34. bumble/profiles/mcp.py +12 -9
  35. bumble/profiles/vcs.py +5 -5
  36. bumble/profiles/vocs.py +6 -9
  37. bumble/rfcomm.py +17 -8
  38. bumble/smp.py +14 -8
  39. {bumble-0.0.210.dist-info → bumble-0.0.212.dist-info}/METADATA +4 -4
  40. {bumble-0.0.210.dist-info → bumble-0.0.212.dist-info}/RECORD +44 -42
  41. {bumble-0.0.210.dist-info → bumble-0.0.212.dist-info}/WHEEL +1 -1
  42. {bumble-0.0.210.dist-info → bumble-0.0.212.dist-info}/entry_points.txt +0 -0
  43. {bumble-0.0.210.dist-info → bumble-0.0.212.dist-info}/licenses/LICENSE +0 -0
  44. {bumble-0.0.210.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: int
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: int = DEVICE_DEFAULT_ADVERTISING_INTERVAL
533
- primary_advertising_interval_max: int = DEVICE_DEFAULT_ADVERTISING_INTERVAL
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: int = DEVICE_DEFAULT_ADVERTISING_INTERVAL
558
- periodic_advertising_interval_max: int = DEVICE_DEFAULT_ADVERTISING_INTERVAL
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=advertising_parameters.periodic_advertising_interval_min,
683
- periodic_advertising_interval_max=advertising_parameters.periodic_advertising_interval_max,
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('start')
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('stop')
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('start_periodic')
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('stop_periodic')
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('termination', status)
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: int
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('state_change')
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('establishment')
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('cancellation')
968
+ self.emit(self.EVENT_CANCELLATION)
952
969
  return
953
970
 
954
971
  self.state = self.State.ERROR
955
- self.emit('error')
972
+ self.emit(self.EVENT_ERROR)
956
973
 
957
974
  def on_loss(self):
958
975
  self.state = self.State.LOST
959
- self.emit('loss')
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
- 'periodic_advertisement',
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
- 'biginfo_advertisement',
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: int = 0
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: int = 0
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: int
1203
+ subevent_interval: float # milliseconds.
1187
1204
  event_interval: int
1188
1205
  procedure_interval: int
1189
1206
  procedure_count: int
1190
- max_procedure_len: int
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
- # Create a GATT client for the connection
1216
- self.gatt_client = gatt_client.Client(connection)
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('connection_att_mtu_update')
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 = None # Per-connection 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('disconnection', abort.set_result)
1744
- self.on('disconnection_failure', abort.set_exception)
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, 'flush', abort), timeout
1813
+ utils.cancel_on_event(self.device, Device.EVENT_FLUSH, abort), timeout
1749
1814
  )
1750
1815
  finally:
1751
- self.remove_listener('disconnection', abort.set_result)
1752
- self.remove_listener('disconnection_failure', abort.set_exception)
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: int = DEVICE_DEFAULT_ADVERTISING_INTERVAL
1852
- advertising_interval_max: int = DEVICE_DEFAULT_ADVERTISING_INTERVAL
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[int] = None,
2752
- advertising_interval_max: Optional[int] = None,
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('advertisement', advertisement)
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('periodic_advertising_sync_transfer', pa_sync, connection)
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
- 'inquiry_result',
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('connection', on_connection)
3512
- self.on('connection_failure', on_connection_failure)
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(self, 'flush', pending_connection)
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, 'flush', pending_connection
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('connection', on_connection)
3689
- self.remove_listener('connection_failure', on_connection_failure)
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(self, 'flush', pending_request_fut)
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('connection', on_connection)
3783
- self.on('connection_failure', on_connection_failure)
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(self, 'flush', pending_connection)
3901
+ return await utils.cancel_on_event(
3902
+ self, Device.EVENT_FLUSH, pending_connection
3903
+ )
3803
3904
 
3804
3905
  finally:
3805
- self.remove_listener('connection', on_connection)
3806
- self.remove_listener('connection_failure', on_connection_failure)
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('disconnection', pending_disconnection.set_result)
3861
- connection.on('disconnection_failure', pending_disconnection.set_exception)
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(self, 'flush', pending_disconnection)
3979
+ return await utils.cancel_on_event(
3980
+ self, Device.EVENT_FLUSH, pending_disconnection
3981
+ )
3877
3982
  finally:
3878
3983
  connection.remove_listener(
3879
- 'disconnection', pending_disconnection.set_result
3984
+ connection.EVENT_DISCONNECTION, pending_disconnection.set_result
3880
3985
  )
3881
3986
  connection.remove_listener(
3882
- 'disconnection_failure', pending_disconnection.set_exception
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,
@@ -3953,6 +4080,9 @@ class Device(utils.CompositeEventEmitter):
3953
4080
  return result.return_parameters.rssi
3954
4081
 
3955
4082
  async def get_connection_phy(self, connection: Connection) -> ConnectionPHY:
4083
+ if not self.host.supports_command(hci.HCI_LE_READ_PHY_COMMAND):
4084
+ return ConnectionPHY(hci.Phy.LE_1M, hci.Phy.LE_1M)
4085
+
3956
4086
  result = await self.send_command(
3957
4087
  hci.HCI_LE_Read_PHY_Command(connection_handle=connection.handle),
3958
4088
  check_result=True,
@@ -4072,7 +4202,7 @@ class Device(utils.CompositeEventEmitter):
4072
4202
  else:
4073
4203
  return None
4074
4204
 
4075
- return await utils.cancel_on_event(self, 'flush', peer_address)
4205
+ return await utils.cancel_on_event(self, Device.EVENT_FLUSH, peer_address)
4076
4206
  finally:
4077
4207
  if listener is not None:
4078
4208
  self.remove_listener(event_name, listener)
@@ -4122,7 +4252,7 @@ class Device(utils.CompositeEventEmitter):
4122
4252
  if not self.scanning:
4123
4253
  await self.start_scanning(filter_duplicates=True)
4124
4254
 
4125
- return await utils.cancel_on_event(self, 'flush', peer_address)
4255
+ return await utils.cancel_on_event(self, Device.EVENT_FLUSH, peer_address)
4126
4256
  finally:
4127
4257
  if listener is not None:
4128
4258
  self.remove_listener(event_name, listener)
@@ -4198,7 +4328,7 @@ class Device(utils.CompositeEventEmitter):
4198
4328
  return keys.link_key.value
4199
4329
 
4200
4330
  # [Classic only]
4201
- async def authenticate(self, connection):
4331
+ async def authenticate(self, connection: Connection) -> None:
4202
4332
  # Set up event handlers
4203
4333
  pending_authentication = asyncio.get_running_loop().create_future()
4204
4334
 
@@ -4208,8 +4338,11 @@ class Device(utils.CompositeEventEmitter):
4208
4338
  def on_authentication_failure(error_code):
4209
4339
  pending_authentication.set_exception(hci.HCI_Error(error_code))
4210
4340
 
4211
- connection.on('connection_authentication', on_authentication)
4212
- connection.on('connection_authentication_failure', on_authentication_failure)
4341
+ connection.on(connection.EVENT_CONNECTION_AUTHENTICATION, on_authentication)
4342
+ connection.on(
4343
+ connection.EVENT_CONNECTION_AUTHENTICATION_FAILURE,
4344
+ on_authentication_failure,
4345
+ )
4213
4346
 
4214
4347
  # Request the authentication
4215
4348
  try:
@@ -4227,12 +4360,15 @@ class Device(utils.CompositeEventEmitter):
4227
4360
 
4228
4361
  # Wait for the authentication to complete
4229
4362
  await utils.cancel_on_event(
4230
- connection, 'disconnection', pending_authentication
4363
+ connection, Connection.EVENT_DISCONNECTION, pending_authentication
4231
4364
  )
4232
4365
  finally:
4233
- connection.remove_listener('connection_authentication', on_authentication)
4234
4366
  connection.remove_listener(
4235
- 'connection_authentication_failure', on_authentication_failure
4367
+ connection.EVENT_CONNECTION_AUTHENTICATION, on_authentication
4368
+ )
4369
+ connection.remove_listener(
4370
+ connection.EVENT_CONNECTION_AUTHENTICATION_FAILURE,
4371
+ on_authentication_failure,
4236
4372
  )
4237
4373
 
4238
4374
  async def encrypt(self, connection, enable=True):
@@ -4248,8 +4384,12 @@ class Device(utils.CompositeEventEmitter):
4248
4384
  def on_encryption_failure(error_code):
4249
4385
  pending_encryption.set_exception(hci.HCI_Error(error_code))
4250
4386
 
4251
- connection.on('connection_encryption_change', on_encryption_change)
4252
- connection.on('connection_encryption_failure', on_encryption_failure)
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
+ )
4253
4393
 
4254
4394
  # Request the encryption
4255
4395
  try:
@@ -4308,13 +4448,15 @@ class Device(utils.CompositeEventEmitter):
4308
4448
  raise hci.HCI_StatusError(result)
4309
4449
 
4310
4450
  # Wait for the result
4311
- await utils.cancel_on_event(connection, 'disconnection', pending_encryption)
4451
+ await utils.cancel_on_event(
4452
+ connection, Connection.EVENT_DISCONNECTION, pending_encryption
4453
+ )
4312
4454
  finally:
4313
4455
  connection.remove_listener(
4314
- 'connection_encryption_change', on_encryption_change
4456
+ connection.EVENT_CONNECTION_ENCRYPTION_CHANGE, on_encryption_change
4315
4457
  )
4316
4458
  connection.remove_listener(
4317
- 'connection_encryption_failure', on_encryption_failure
4459
+ connection.EVENT_CONNECTION_ENCRYPTION_FAILURE, on_encryption_failure
4318
4460
  )
4319
4461
 
4320
4462
  async def update_keys(self, address: str, keys: PairingKeys) -> None:
@@ -4327,7 +4469,7 @@ class Device(utils.CompositeEventEmitter):
4327
4469
  except Exception as error:
4328
4470
  logger.warning(f'!!! error while storing keys: {error}')
4329
4471
  else:
4330
- self.emit('key_store_update')
4472
+ self.emit(self.EVENT_KEY_STORE_UPDATE)
4331
4473
 
4332
4474
  # [Classic only]
4333
4475
  async def switch_role(self, connection: Connection, role: hci.Role):
@@ -4339,8 +4481,8 @@ class Device(utils.CompositeEventEmitter):
4339
4481
  def on_role_change_failure(error_code):
4340
4482
  pending_role_change.set_exception(hci.HCI_Error(error_code))
4341
4483
 
4342
- connection.on('role_change', on_role_change)
4343
- connection.on('role_change_failure', on_role_change_failure)
4484
+ connection.on(connection.EVENT_ROLE_CHANGE, on_role_change)
4485
+ connection.on(connection.EVENT_ROLE_CHANGE_FAILURE, on_role_change_failure)
4344
4486
 
4345
4487
  try:
4346
4488
  result = await self.send_command(
@@ -4353,11 +4495,13 @@ class Device(utils.CompositeEventEmitter):
4353
4495
  )
4354
4496
  raise hci.HCI_StatusError(result)
4355
4497
  await utils.cancel_on_event(
4356
- connection, 'disconnection', pending_role_change
4498
+ connection, Connection.EVENT_DISCONNECTION, pending_role_change
4357
4499
  )
4358
4500
  finally:
4359
- connection.remove_listener('role_change', on_role_change)
4360
- connection.remove_listener('role_change_failure', on_role_change_failure)
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
+ )
4361
4505
 
4362
4506
  # [Classic only]
4363
4507
  async def request_remote_name(self, remote: Union[hci.Address, Connection]) -> str:
@@ -4369,7 +4513,7 @@ class Device(utils.CompositeEventEmitter):
4369
4513
  )
4370
4514
 
4371
4515
  handler = self.on(
4372
- 'remote_name',
4516
+ self.EVENT_REMOTE_NAME,
4373
4517
  lambda address, remote_name: (
4374
4518
  pending_name.set_result(remote_name)
4375
4519
  if address == peer_address
@@ -4377,7 +4521,7 @@ class Device(utils.CompositeEventEmitter):
4377
4521
  ),
4378
4522
  )
4379
4523
  failure_handler = self.on(
4380
- 'remote_name_failure',
4524
+ self.EVENT_REMOTE_NAME_FAILURE,
4381
4525
  lambda address, error_code: (
4382
4526
  pending_name.set_exception(hci.HCI_Error(error_code))
4383
4527
  if address == peer_address
@@ -4403,10 +4547,10 @@ class Device(utils.CompositeEventEmitter):
4403
4547
  raise hci.HCI_StatusError(result)
4404
4548
 
4405
4549
  # Wait for the result
4406
- return await utils.cancel_on_event(self, 'flush', pending_name)
4550
+ return await utils.cancel_on_event(self, Device.EVENT_FLUSH, pending_name)
4407
4551
  finally:
4408
- self.remove_listener('remote_name', handler)
4409
- self.remove_listener('remote_name_failure', failure_handler)
4552
+ self.remove_listener(self.EVENT_REMOTE_NAME, handler)
4553
+ self.remove_listener(self.EVENT_REMOTE_NAME_FAILURE, failure_handler)
4410
4554
 
4411
4555
  # [LE only]
4412
4556
  @utils.experimental('Only for testing.')
@@ -4497,8 +4641,10 @@ class Device(utils.CompositeEventEmitter):
4497
4641
  if pending_future := pending_cis_establishments.get(cis_handle):
4498
4642
  pending_future.set_exception(hci.HCI_Error(status))
4499
4643
 
4500
- watcher.on(self, 'cis_establishment', on_cis_establishment)
4501
- watcher.on(self, 'cis_establishment_failure', on_cis_establishment_failure)
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
+ )
4502
4648
  await self.send_command(
4503
4649
  hci.HCI_LE_Create_CIS_Command(
4504
4650
  cis_connection_handle=[p[0] for p in cis_acl_pairs],
@@ -4541,8 +4687,12 @@ class Device(utils.CompositeEventEmitter):
4541
4687
  def on_establishment_failure(status: int) -> None:
4542
4688
  pending_establishment.set_exception(hci.HCI_Error(status))
4543
4689
 
4544
- watcher.on(cis_link, 'establishment', on_establishment)
4545
- watcher.on(cis_link, 'establishment_failure', on_establishment_failure)
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
+ )
4546
4696
 
4547
4697
  await self.send_command(
4548
4698
  hci.HCI_LE_Accept_CIS_Request_Command(connection_handle=handle),
@@ -4910,33 +5060,33 @@ class Device(utils.CompositeEventEmitter):
4910
5060
 
4911
5061
  @host_event_handler
4912
5062
  def on_flush(self):
4913
- self.emit('flush')
5063
+ self.emit(self.EVENT_FLUSH)
4914
5064
  for _, connection in self.connections.items():
4915
- connection.emit('disconnection', 0)
5065
+ connection.emit(connection.EVENT_DISCONNECTION, 0)
4916
5066
  self.connections = {}
4917
5067
 
4918
5068
  # [Classic only]
4919
5069
  @host_event_handler
4920
- 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:
4921
5071
  # Store the keys in the key store
4922
5072
  if self.keystore:
4923
5073
  authenticated = key_type in (
4924
5074
  hci.HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_192_TYPE,
4925
5075
  hci.HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_256_TYPE,
4926
5076
  )
4927
- pairing_keys = PairingKeys()
4928
- pairing_keys.link_key = PairingKeys.Key(
4929
- value=link_key, authenticated=authenticated
5077
+ pairing_keys = PairingKeys(
5078
+ link_key=PairingKeys.Key(value=link_key, authenticated=authenticated),
5079
+ link_key_type=key_type,
4930
5080
  )
4931
5081
 
4932
5082
  utils.cancel_on_event(
4933
- self, 'flush', self.update_keys(str(bd_addr), pairing_keys)
5083
+ self, Device.EVENT_FLUSH, self.update_keys(str(bd_addr), pairing_keys)
4934
5084
  )
4935
5085
 
4936
5086
  if connection := self.find_connection_by_bd_addr(
4937
5087
  bd_addr, transport=PhysicalTransport.BR_EDR
4938
5088
  ):
4939
- connection.link_key_type = key_type
5089
+ connection.emit(connection.EVENT_LINK_KEY)
4940
5090
 
4941
5091
  def add_service(self, service):
4942
5092
  self.gatt_server.add_service(service)
@@ -5108,7 +5258,7 @@ class Device(utils.CompositeEventEmitter):
5108
5258
  big.pto = pto
5109
5259
  big.irc = irc
5110
5260
  big.max_pdu = max_pdu
5111
- big.iso_interval = iso_interval
5261
+ big.iso_interval = iso_interval * 1.25
5112
5262
  big.state = Big.State.ACTIVE
5113
5263
 
5114
5264
  for bis_link in big.bis_links:
@@ -5157,7 +5307,7 @@ class Device(utils.CompositeEventEmitter):
5157
5307
  big_sync.pto = pto
5158
5308
  big_sync.irc = irc
5159
5309
  big_sync.max_pdu = max_pdu
5160
- big_sync.iso_interval = iso_interval
5310
+ big_sync.iso_interval = iso_interval * 1.25
5161
5311
  big_sync.bis_links = [
5162
5312
  BisLink(handle=handle, big=big_sync) for handle in bis_handles
5163
5313
  ]
@@ -5199,11 +5349,13 @@ class Device(utils.CompositeEventEmitter):
5199
5349
  # Setup auto-restart of the advertising set if needed.
5200
5350
  if advertising_set.auto_restart:
5201
5351
  connection.once(
5202
- 'disconnection',
5203
- lambda _: utils.cancel_on_event(self, 'flush', advertising_set.start()),
5352
+ Connection.EVENT_DISCONNECTION,
5353
+ lambda _: utils.cancel_on_event(
5354
+ self, Device.EVENT_FLUSH, advertising_set.start()
5355
+ ),
5204
5356
  )
5205
5357
 
5206
- self.emit('connection', connection)
5358
+ self.emit(self.EVENT_CONNECTION, connection)
5207
5359
 
5208
5360
  @host_event_handler
5209
5361
  def on_connection(
@@ -5214,7 +5366,7 @@ class Device(utils.CompositeEventEmitter):
5214
5366
  self_resolvable_address: Optional[hci.Address],
5215
5367
  peer_resolvable_address: Optional[hci.Address],
5216
5368
  role: hci.Role,
5217
- connection_parameters: ConnectionParameters,
5369
+ connection_parameters: Optional[core.ConnectionParameters],
5218
5370
  ) -> None:
5219
5371
  # Convert all-zeros addresses into None.
5220
5372
  if self_resolvable_address == hci.Address.ANY_RANDOM:
@@ -5241,10 +5393,12 @@ class Device(utils.CompositeEventEmitter):
5241
5393
  self.connections[connection_handle] = connection
5242
5394
 
5243
5395
  # Emit an event to notify listeners of the new connection
5244
- self.emit('connection', connection)
5396
+ self.emit(self.EVENT_CONNECTION, connection)
5245
5397
 
5246
5398
  return
5247
5399
 
5400
+ assert connection_parameters is not None
5401
+
5248
5402
  if peer_resolvable_address is None:
5249
5403
  # Resolve the peer address if we can
5250
5404
  if self.address_resolver:
@@ -5300,7 +5454,11 @@ class Device(utils.CompositeEventEmitter):
5300
5454
  peer_address,
5301
5455
  peer_resolvable_address,
5302
5456
  role,
5303
- connection_parameters,
5457
+ Connection.Parameters(
5458
+ connection_parameters.connection_interval * 1.25,
5459
+ connection_parameters.peripheral_latency,
5460
+ connection_parameters.supervision_timeout * 10.0,
5461
+ ),
5304
5462
  )
5305
5463
  self.connections[connection_handle] = connection
5306
5464
 
@@ -5308,15 +5466,17 @@ class Device(utils.CompositeEventEmitter):
5308
5466
  if self.legacy_advertiser.auto_restart:
5309
5467
  advertiser = self.legacy_advertiser
5310
5468
  connection.once(
5311
- 'disconnection',
5312
- lambda _: utils.cancel_on_event(self, 'flush', advertiser.start()),
5469
+ Connection.EVENT_DISCONNECTION,
5470
+ lambda _: utils.cancel_on_event(
5471
+ self, Device.EVENT_FLUSH, advertiser.start()
5472
+ ),
5313
5473
  )
5314
5474
  else:
5315
5475
  self.legacy_advertiser = None
5316
5476
 
5317
5477
  if role == hci.Role.CENTRAL or not self.supports_le_extended_advertising:
5318
5478
  # We can emit now, we have all the info we need
5319
- self.emit('connection', connection)
5479
+ self.emit(self.EVENT_CONNECTION, connection)
5320
5480
  return
5321
5481
 
5322
5482
  if role == hci.Role.PERIPHERAL and self.supports_le_extended_advertising:
@@ -5350,7 +5510,7 @@ class Device(utils.CompositeEventEmitter):
5350
5510
  'hci',
5351
5511
  hci.HCI_Constant.error_name(error_code),
5352
5512
  )
5353
- self.emit('connection_failure', error)
5513
+ self.emit(self.EVENT_CONNECTION_FAILURE, error)
5354
5514
 
5355
5515
  # FIXME: Explore a delegate-model for BR/EDR wait connection #56.
5356
5516
  @host_event_handler
@@ -5365,7 +5525,7 @@ class Device(utils.CompositeEventEmitter):
5365
5525
  if connection := self.find_connection_by_bd_addr(
5366
5526
  bd_addr, transport=PhysicalTransport.BR_EDR
5367
5527
  ):
5368
- self.emit('sco_request', connection, link_type)
5528
+ self.emit(self.EVENT_SCO_REQUEST, connection, link_type)
5369
5529
  else:
5370
5530
  logger.error(f'SCO request from a non-connected device {bd_addr}')
5371
5531
  return
@@ -5409,14 +5569,14 @@ class Device(utils.CompositeEventEmitter):
5409
5569
  f'*** Disconnection: [0x{connection.handle:04X}] '
5410
5570
  f'{connection.peer_address} as {connection.role_name}, reason={reason}'
5411
5571
  )
5412
- connection.emit('disconnection', reason)
5572
+ connection.emit(connection.EVENT_DISCONNECTION, reason)
5413
5573
 
5414
5574
  # Cleanup subsystems that maintain per-connection state
5415
5575
  self.gatt_server.on_disconnection(connection)
5416
5576
  elif sco_link := self.sco_links.pop(connection_handle, None):
5417
- sco_link.emit('disconnection', reason)
5577
+ sco_link.emit(sco_link.EVENT_DISCONNECTION, reason)
5418
5578
  elif cis_link := self.cis_links.pop(connection_handle, None):
5419
- cis_link.emit('disconnection', reason)
5579
+ cis_link.emit(cis_link.EVENT_DISCONNECTION, reason)
5420
5580
  else:
5421
5581
  logger.error(
5422
5582
  f'*** Unknown disconnection handle=0x{connection_handle}, reason={reason} ***'
@@ -5424,7 +5584,7 @@ class Device(utils.CompositeEventEmitter):
5424
5584
 
5425
5585
  @host_event_handler
5426
5586
  @with_connection_from_handle
5427
- def on_disconnection_failure(self, connection, error_code):
5587
+ def on_disconnection_failure(self, connection: Connection, error_code: int):
5428
5588
  logger.debug(f'*** Disconnection failed: {error_code}')
5429
5589
  error = core.ConnectionError(
5430
5590
  error_code,
@@ -5433,7 +5593,7 @@ class Device(utils.CompositeEventEmitter):
5433
5593
  'hci',
5434
5594
  hci.HCI_Constant.error_name(error_code),
5435
5595
  )
5436
- connection.emit('disconnection_failure', error)
5596
+ connection.emit(connection.EVENT_DISCONNECTION_FAILURE, error)
5437
5597
 
5438
5598
  @host_event_handler
5439
5599
  @utils.AsyncRunner.run_in_task()
@@ -5444,7 +5604,7 @@ class Device(utils.CompositeEventEmitter):
5444
5604
  else:
5445
5605
  self.auto_restart_inquiry = True
5446
5606
  self.discovering = False
5447
- self.emit('inquiry_complete')
5607
+ self.emit(self.EVENT_INQUIRY_COMPLETE)
5448
5608
 
5449
5609
  @host_event_handler
5450
5610
  @with_connection_from_handle
@@ -5454,7 +5614,7 @@ class Device(utils.CompositeEventEmitter):
5454
5614
  f'{connection.peer_address} as {connection.role_name}'
5455
5615
  )
5456
5616
  connection.authenticated = True
5457
- connection.emit('connection_authentication')
5617
+ connection.emit(connection.EVENT_CONNECTION_AUTHENTICATION)
5458
5618
 
5459
5619
  @host_event_handler
5460
5620
  @with_connection_from_handle
@@ -5463,7 +5623,7 @@ class Device(utils.CompositeEventEmitter):
5463
5623
  f'*** Connection Authentication Failure: [0x{connection.handle:04X}] '
5464
5624
  f'{connection.peer_address} as {connection.role_name}, error={error}'
5465
5625
  )
5466
- connection.emit('connection_authentication_failure', error)
5626
+ connection.emit(connection.EVENT_CONNECTION_AUTHENTICATION_FAILURE, error)
5467
5627
 
5468
5628
  # [Classic only]
5469
5629
  @host_event_handler
@@ -5568,7 +5728,9 @@ class Device(utils.CompositeEventEmitter):
5568
5728
 
5569
5729
  async def reply() -> None:
5570
5730
  try:
5571
- if await utils.cancel_on_event(connection, 'disconnection', method()):
5731
+ if await utils.cancel_on_event(
5732
+ connection, Connection.EVENT_DISCONNECTION, method()
5733
+ ):
5572
5734
  await self.host.send_command(
5573
5735
  hci.HCI_User_Confirmation_Request_Reply_Command(
5574
5736
  bd_addr=connection.peer_address
@@ -5596,7 +5758,9 @@ class Device(utils.CompositeEventEmitter):
5596
5758
  async def reply() -> None:
5597
5759
  try:
5598
5760
  number = await utils.cancel_on_event(
5599
- connection, 'disconnection', pairing_config.delegate.get_number()
5761
+ connection,
5762
+ Connection.EVENT_DISCONNECTION,
5763
+ pairing_config.delegate.get_number(),
5600
5764
  )
5601
5765
  if number is not None:
5602
5766
  await self.host.send_command(
@@ -5630,7 +5794,9 @@ class Device(utils.CompositeEventEmitter):
5630
5794
  # Ask the user to enter a string
5631
5795
  async def get_pin_code():
5632
5796
  pin_code = await utils.cancel_on_event(
5633
- connection, 'disconnection', pairing_config.delegate.get_string(16)
5797
+ connection,
5798
+ Connection.EVENT_DISCONNECTION,
5799
+ pairing_config.delegate.get_string(16),
5634
5800
  )
5635
5801
 
5636
5802
  if pin_code is not None:
@@ -5669,7 +5835,9 @@ class Device(utils.CompositeEventEmitter):
5669
5835
 
5670
5836
  # Show the passkey to the user
5671
5837
  utils.cancel_on_event(
5672
- connection, 'disconnection', pairing_config.delegate.display_number(passkey)
5838
+ connection,
5839
+ Connection.EVENT_DISCONNECTION,
5840
+ pairing_config.delegate.display_number(passkey, digits=6),
5673
5841
  )
5674
5842
 
5675
5843
  # [Classic only]
@@ -5681,22 +5849,22 @@ class Device(utils.CompositeEventEmitter):
5681
5849
  remote_name = remote_name.decode('utf-8')
5682
5850
  if connection:
5683
5851
  connection.peer_name = remote_name
5684
- connection.emit('remote_name')
5685
- self.emit('remote_name', address, remote_name)
5852
+ connection.emit(connection.EVENT_REMOTE_NAME)
5853
+ self.emit(self.EVENT_REMOTE_NAME, address, remote_name)
5686
5854
  except UnicodeDecodeError as error:
5687
5855
  logger.warning('peer name is not valid UTF-8')
5688
5856
  if connection:
5689
- connection.emit('remote_name_failure', error)
5857
+ connection.emit(connection.EVENT_REMOTE_NAME_FAILURE, error)
5690
5858
  else:
5691
- self.emit('remote_name_failure', address, error)
5859
+ self.emit(self.EVENT_REMOTE_NAME_FAILURE, address, error)
5692
5860
 
5693
5861
  # [Classic only]
5694
5862
  @host_event_handler
5695
5863
  @try_with_connection_from_address
5696
5864
  def on_remote_name_failure(self, connection: Connection, address, error):
5697
5865
  if connection:
5698
- connection.emit('remote_name_failure', error)
5699
- self.emit('remote_name_failure', address, error)
5866
+ connection.emit(connection.EVENT_REMOTE_NAME_FAILURE, error)
5867
+ self.emit(self.EVENT_REMOTE_NAME_FAILURE, address, error)
5700
5868
 
5701
5869
  # [Classic only]
5702
5870
  @host_event_handler
@@ -5716,7 +5884,7 @@ class Device(utils.CompositeEventEmitter):
5716
5884
  handle=sco_handle,
5717
5885
  link_type=link_type,
5718
5886
  )
5719
- self.emit('sco_connection', sco_link)
5887
+ self.emit(self.EVENT_SCO_CONNECTION, sco_link)
5720
5888
 
5721
5889
  # [Classic only]
5722
5890
  @host_event_handler
@@ -5726,7 +5894,7 @@ class Device(utils.CompositeEventEmitter):
5726
5894
  self, acl_connection: Connection, status: int
5727
5895
  ) -> None:
5728
5896
  logger.debug(f'*** SCO connection failure: {acl_connection.peer_address}***')
5729
- self.emit('sco_connection_failure')
5897
+ self.emit(self.EVENT_SCO_CONNECTION_FAILURE)
5730
5898
 
5731
5899
  # [Classic only]
5732
5900
  @host_event_handler
@@ -5763,7 +5931,7 @@ class Device(utils.CompositeEventEmitter):
5763
5931
  cig_id=cig_id,
5764
5932
  cis_id=cis_id,
5765
5933
  )
5766
- self.emit('cis_request', acl_connection, cis_handle, cig_id, cis_id)
5934
+ self.emit(self.EVENT_CIS_REQUEST, acl_connection, cis_handle, cig_id, cis_id)
5767
5935
 
5768
5936
  # [LE only]
5769
5937
  @host_event_handler
@@ -5782,8 +5950,8 @@ class Device(utils.CompositeEventEmitter):
5782
5950
  f'cis_id=[0x{cis_link.cis_id:02X}] ***'
5783
5951
  )
5784
5952
 
5785
- cis_link.emit('establishment')
5786
- self.emit('cis_establishment', cis_link)
5953
+ cis_link.emit(cis_link.EVENT_ESTABLISHMENT)
5954
+ self.emit(self.EVENT_CIS_ESTABLISHMENT, cis_link)
5787
5955
 
5788
5956
  # [LE only]
5789
5957
  @host_event_handler
@@ -5791,8 +5959,8 @@ class Device(utils.CompositeEventEmitter):
5791
5959
  def on_cis_establishment_failure(self, cis_handle: int, status: int) -> None:
5792
5960
  logger.debug(f'*** CIS Establishment Failure: cis=[0x{cis_handle:04X}] ***')
5793
5961
  if cis_link := self.cis_links.pop(cis_handle):
5794
- cis_link.emit('establishment_failure', status)
5795
- self.emit('cis_establishment_failure', cis_handle, status)
5962
+ cis_link.emit(cis_link.EVENT_ESTABLISHMENT_FAILURE, status)
5963
+ self.emit(self.EVENT_CIS_ESTABLISHMENT_FAILURE, cis_handle, status)
5796
5964
 
5797
5965
  # [LE only]
5798
5966
  @host_event_handler
@@ -5805,13 +5973,17 @@ class Device(utils.CompositeEventEmitter):
5805
5973
 
5806
5974
  @host_event_handler
5807
5975
  @with_connection_from_handle
5808
- def on_connection_encryption_change(self, connection, encryption):
5976
+ def on_connection_encryption_change(
5977
+ self, connection, encryption, encryption_key_size
5978
+ ):
5809
5979
  logger.debug(
5810
5980
  f'*** Connection Encryption Change: [0x{connection.handle:04X}] '
5811
5981
  f'{connection.peer_address} as {connection.role_name}, '
5812
- f'encryption={encryption}'
5982
+ f'encryption={encryption}, '
5983
+ f'key_size={encryption_key_size}'
5813
5984
  )
5814
5985
  connection.encryption = encryption
5986
+ connection.encryption_key_size = encryption_key_size
5815
5987
  if (
5816
5988
  not connection.authenticated
5817
5989
  and connection.transport == PhysicalTransport.BR_EDR
@@ -5826,7 +5998,7 @@ class Device(utils.CompositeEventEmitter):
5826
5998
  ):
5827
5999
  connection.authenticated = True
5828
6000
  connection.sc = True
5829
- connection.emit('connection_encryption_change')
6001
+ connection.emit(connection.EVENT_CONNECTION_ENCRYPTION_CHANGE)
5830
6002
 
5831
6003
  @host_event_handler
5832
6004
  @with_connection_from_handle
@@ -5836,7 +6008,7 @@ class Device(utils.CompositeEventEmitter):
5836
6008
  f'{connection.peer_address} as {connection.role_name}, '
5837
6009
  f'error={error}'
5838
6010
  )
5839
- connection.emit('connection_encryption_failure', error)
6011
+ connection.emit(connection.EVENT_CONNECTION_ENCRYPTION_FAILURE, error)
5840
6012
 
5841
6013
  @host_event_handler
5842
6014
  @with_connection_from_handle
@@ -5845,7 +6017,7 @@ class Device(utils.CompositeEventEmitter):
5845
6017
  f'*** Connection Key Refresh: [0x{connection.handle:04X}] '
5846
6018
  f'{connection.peer_address} as {connection.role_name}'
5847
6019
  )
5848
- connection.emit('connection_encryption_key_refresh')
6020
+ connection.emit(connection.EVENT_CONNECTION_ENCRYPTION_KEY_REFRESH)
5849
6021
 
5850
6022
  @host_event_handler
5851
6023
  @with_connection_from_handle
@@ -5856,7 +6028,7 @@ class Device(utils.CompositeEventEmitter):
5856
6028
  f'{connection_parameters}'
5857
6029
  )
5858
6030
  connection.parameters = connection_parameters
5859
- connection.emit('connection_parameters_update')
6031
+ connection.emit(connection.EVENT_CONNECTION_PARAMETERS_UPDATE)
5860
6032
 
5861
6033
  @host_event_handler
5862
6034
  @with_connection_from_handle
@@ -5866,7 +6038,7 @@ class Device(utils.CompositeEventEmitter):
5866
6038
  f'{connection.peer_address} as {connection.role_name}, '
5867
6039
  f'error={error}'
5868
6040
  )
5869
- connection.emit('connection_parameters_update_failure', error)
6041
+ connection.emit(connection.EVENT_CONNECTION_PARAMETERS_UPDATE_FAILURE, error)
5870
6042
 
5871
6043
  @host_event_handler
5872
6044
  @with_connection_from_handle
@@ -5876,7 +6048,7 @@ class Device(utils.CompositeEventEmitter):
5876
6048
  f'{connection.peer_address} as {connection.role_name}, '
5877
6049
  f'{phy}'
5878
6050
  )
5879
- connection.emit('connection_phy_update', phy)
6051
+ connection.emit(connection.EVENT_CONNECTION_PHY_UPDATE, phy)
5880
6052
 
5881
6053
  @host_event_handler
5882
6054
  @with_connection_from_handle
@@ -5886,7 +6058,7 @@ class Device(utils.CompositeEventEmitter):
5886
6058
  f'{connection.peer_address} as {connection.role_name}, '
5887
6059
  f'error={error}'
5888
6060
  )
5889
- connection.emit('connection_phy_update_failure', error)
6061
+ connection.emit(connection.EVENT_CONNECTION_PHY_UPDATE_FAILURE, error)
5890
6062
 
5891
6063
  @host_event_handler
5892
6064
  @with_connection_from_handle
@@ -5897,7 +6069,7 @@ class Device(utils.CompositeEventEmitter):
5897
6069
  f'{att_mtu}'
5898
6070
  )
5899
6071
  connection.att_mtu = att_mtu
5900
- connection.emit('connection_att_mtu_update')
6072
+ connection.emit(connection.EVENT_CONNECTION_ATT_MTU_UPDATE)
5901
6073
 
5902
6074
  @host_event_handler
5903
6075
  @with_connection_from_handle
@@ -5914,7 +6086,7 @@ class Device(utils.CompositeEventEmitter):
5914
6086
  max_rx_octets,
5915
6087
  max_rx_time,
5916
6088
  )
5917
- connection.emit('connection_data_length_change')
6089
+ connection.emit(connection.EVENT_CONNECTION_DATA_LENGTH_CHANGE)
5918
6090
 
5919
6091
  @host_event_handler
5920
6092
  def on_cs_remote_supported_capabilities(
@@ -5924,7 +6096,9 @@ class Device(utils.CompositeEventEmitter):
5924
6096
  return
5925
6097
 
5926
6098
  if event.status != hci.HCI_SUCCESS:
5927
- connection.emit('channel_sounding_capabilities_failure', event.status)
6099
+ connection.emit(
6100
+ connection.EVENT_CHANNEL_SOUNDING_CAPABILITIES_FAILURE, event.status
6101
+ )
5928
6102
  return
5929
6103
 
5930
6104
  capabilities = ChannelSoundingCapabilities(
@@ -5949,7 +6123,7 @@ class Device(utils.CompositeEventEmitter):
5949
6123
  t_sw_time_supported=event.t_sw_time_supported,
5950
6124
  tx_snr_capability=event.tx_snr_capability,
5951
6125
  )
5952
- connection.emit('channel_sounding_capabilities', capabilities)
6126
+ connection.emit(connection.EVENT_CHANNEL_SOUNDING_CAPABILITIES, capabilities)
5953
6127
 
5954
6128
  @host_event_handler
5955
6129
  def on_cs_config(self, event: hci.HCI_LE_CS_Config_Complete_Event):
@@ -5957,7 +6131,9 @@ class Device(utils.CompositeEventEmitter):
5957
6131
  return
5958
6132
 
5959
6133
  if event.status != hci.HCI_SUCCESS:
5960
- connection.emit('channel_sounding_config_failure', event.status)
6134
+ connection.emit(
6135
+ connection.EVENT_CHANNEL_SOUNDING_CONFIG_FAILURE, event.status
6136
+ )
5961
6137
  return
5962
6138
  if event.action == hci.HCI_LE_CS_Config_Complete_Event.Action.CREATED:
5963
6139
  config = ChannelSoundingConfig(
@@ -5983,11 +6159,13 @@ class Device(utils.CompositeEventEmitter):
5983
6159
  t_pm_time=event.t_pm_time,
5984
6160
  )
5985
6161
  connection.cs_configs[event.config_id] = config
5986
- connection.emit('channel_sounding_config', config)
6162
+ connection.emit(connection.EVENT_CHANNEL_SOUNDING_CONFIG, config)
5987
6163
  elif event.action == hci.HCI_LE_CS_Config_Complete_Event.Action.REMOVED:
5988
6164
  try:
5989
6165
  config = connection.cs_configs.pop(event.config_id)
5990
- connection.emit('channel_sounding_config_removed', config.config_id)
6166
+ connection.emit(
6167
+ connection.EVENT_CHANNEL_SOUNDING_CONFIG_REMOVED, config.config_id
6168
+ )
5991
6169
  except KeyError:
5992
6170
  logger.error('Removing unknown config %d', event.config_id)
5993
6171
 
@@ -5997,7 +6175,9 @@ class Device(utils.CompositeEventEmitter):
5997
6175
  return
5998
6176
 
5999
6177
  if event.status != hci.HCI_SUCCESS:
6000
- connection.emit('channel_sounding_procedure_failure', event.status)
6178
+ connection.emit(
6179
+ connection.EVENT_CHANNEL_SOUNDING_PROCEDURE_FAILURE, event.status
6180
+ )
6001
6181
  return
6002
6182
 
6003
6183
  procedure = ChannelSoundingProcedure(
@@ -6014,37 +6194,37 @@ class Device(utils.CompositeEventEmitter):
6014
6194
  max_procedure_len=event.max_procedure_len,
6015
6195
  )
6016
6196
  connection.cs_procedures[procedure.config_id] = procedure
6017
- connection.emit('channel_sounding_procedure', procedure)
6197
+ connection.emit(connection.EVENT_CHANNEL_SOUNDING_PROCEDURE, procedure)
6018
6198
 
6019
6199
  # [Classic only]
6020
6200
  @host_event_handler
6021
6201
  @with_connection_from_address
6022
6202
  def on_role_change(self, connection, new_role):
6023
6203
  connection.role = new_role
6024
- connection.emit('role_change', new_role)
6204
+ connection.emit(connection.EVENT_ROLE_CHANGE, new_role)
6025
6205
 
6026
6206
  # [Classic only]
6027
6207
  @host_event_handler
6028
6208
  @try_with_connection_from_address
6029
6209
  def on_role_change_failure(self, connection, address, error):
6030
6210
  if connection:
6031
- connection.emit('role_change_failure', error)
6032
- self.emit('role_change_failure', address, error)
6211
+ connection.emit(connection.EVENT_ROLE_CHANGE_FAILURE, error)
6212
+ self.emit(self.EVENT_ROLE_CHANGE_FAILURE, address, error)
6033
6213
 
6034
6214
  # [Classic only]
6035
6215
  @host_event_handler
6036
6216
  @with_connection_from_address
6037
6217
  def on_classic_pairing(self, connection: Connection) -> None:
6038
- connection.emit('classic_pairing')
6218
+ connection.emit(connection.EVENT_CLASSIC_PAIRING)
6039
6219
 
6040
6220
  # [Classic only]
6041
6221
  @host_event_handler
6042
6222
  @with_connection_from_address
6043
6223
  def on_classic_pairing_failure(self, connection: Connection, status) -> None:
6044
- connection.emit('classic_pairing_failure', status)
6224
+ connection.emit(connection.EVENT_CLASSIC_PAIRING_FAILURE, status)
6045
6225
 
6046
6226
  def on_pairing_start(self, connection: Connection) -> None:
6047
- connection.emit('pairing_start')
6227
+ connection.emit(connection.EVENT_PAIRING_START)
6048
6228
 
6049
6229
  def on_pairing(
6050
6230
  self,
@@ -6058,10 +6238,10 @@ class Device(utils.CompositeEventEmitter):
6058
6238
  connection.peer_address = identity_address
6059
6239
  connection.sc = sc
6060
6240
  connection.authenticated = True
6061
- connection.emit('pairing', keys)
6241
+ connection.emit(connection.EVENT_PAIRING, keys)
6062
6242
 
6063
6243
  def on_pairing_failure(self, connection: Connection, reason: int) -> None:
6064
- connection.emit('pairing_failure', reason)
6244
+ connection.emit(connection.EVENT_PAIRING_FAILURE, reason)
6065
6245
 
6066
6246
  @with_connection_from_handle
6067
6247
  def on_gatt_pdu(self, connection, pdu):