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.
Files changed (44) hide show
  1. bumble/_version.py +2 -2
  2. bumble/apps/bench.py +4 -2
  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 +362 -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 +67 -2
  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.211.dist-info → bumble-0.0.212.dist-info}/METADATA +4 -4
  40. {bumble-0.0.211.dist-info → bumble-0.0.212.dist-info}/RECORD +44 -42
  41. {bumble-0.0.211.dist-info → bumble-0.0.212.dist-info}/WHEEL +1 -1
  42. {bumble-0.0.211.dist-info → bumble-0.0.212.dist-info}/entry_points.txt +0 -0
  43. {bumble-0.0.211.dist-info → bumble-0.0.212.dist-info}/licenses/LICENSE +0 -0
  44. {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: 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,
@@ -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, 'flush', peer_address)
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, 'flush', peer_address)
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('connection_authentication', on_authentication)
4215
- 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
+ )
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, 'disconnection', pending_authentication
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
- '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,
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('connection_encryption_change', on_encryption_change)
4255
- 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
+ )
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(connection, 'disconnection', pending_encryption)
4451
+ await utils.cancel_on_event(
4452
+ connection, Connection.EVENT_DISCONNECTION, pending_encryption
4453
+ )
4315
4454
  finally:
4316
4455
  connection.remove_listener(
4317
- 'connection_encryption_change', on_encryption_change
4456
+ connection.EVENT_CONNECTION_ENCRYPTION_CHANGE, on_encryption_change
4318
4457
  )
4319
4458
  connection.remove_listener(
4320
- 'connection_encryption_failure', on_encryption_failure
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('key_store_update')
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('role_change', on_role_change)
4346
- 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)
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, 'disconnection', pending_role_change
4498
+ connection, Connection.EVENT_DISCONNECTION, pending_role_change
4360
4499
  )
4361
4500
  finally:
4362
- connection.remove_listener('role_change', on_role_change)
4363
- 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
+ )
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
- 'remote_name',
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
- 'remote_name_failure',
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, 'flush', pending_name)
4550
+ return await utils.cancel_on_event(self, Device.EVENT_FLUSH, pending_name)
4410
4551
  finally:
4411
- self.remove_listener('remote_name', handler)
4412
- 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)
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, 'cis_establishment', on_cis_establishment)
4504
- 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
+ )
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, 'establishment', on_establishment)
4548
- 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
+ )
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('flush')
5063
+ self.emit(self.EVENT_FLUSH)
4917
5064
  for _, connection in self.connections.items():
4918
- connection.emit('disconnection', 0)
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
- pairing_keys.link_key = PairingKeys.Key(
4932
- 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,
4933
5080
  )
4934
5081
 
4935
5082
  utils.cancel_on_event(
4936
- self, 'flush', self.update_keys(str(bd_addr), pairing_keys)
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.link_key_type = key_type
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
- 'disconnection',
5206
- 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
+ ),
5207
5356
  )
5208
5357
 
5209
- self.emit('connection', connection)
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('connection', connection)
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
- 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
+ ),
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
- 'disconnection',
5315
- 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
+ ),
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('connection', connection)
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('connection_failure', error)
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('sco_request', connection, link_type)
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('disconnection', reason)
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('disconnection', reason)
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('disconnection', reason)
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('disconnection_failure', error)
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('inquiry_complete')
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('connection_authentication')
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('connection_authentication_failure', error)
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(connection, 'disconnection', method()):
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, 'disconnection', pairing_config.delegate.get_number()
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, 'disconnection', pairing_config.delegate.get_string(16)
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, 'disconnection', pairing_config.delegate.display_number(passkey)
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('remote_name')
5688
- self.emit('remote_name', address, remote_name)
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('remote_name_failure', error)
5857
+ connection.emit(connection.EVENT_REMOTE_NAME_FAILURE, error)
5693
5858
  else:
5694
- self.emit('remote_name_failure', address, error)
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('remote_name_failure', error)
5702
- 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)
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('sco_connection', sco_link)
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('sco_connection_failure')
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('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)
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('establishment')
5789
- self.emit('cis_establishment', cis_link)
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('establishment_failure', status)
5798
- 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)
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(self, connection, encryption):
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('connection_encryption_change')
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('connection_encryption_failure', error)
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('connection_encryption_key_refresh')
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('connection_parameters_update')
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('connection_parameters_update_failure', error)
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('connection_phy_update', phy)
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('connection_phy_update_failure', error)
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('connection_att_mtu_update')
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('connection_data_length_change')
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('channel_sounding_capabilities_failure', event.status)
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('channel_sounding_capabilities', capabilities)
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('channel_sounding_config_failure', event.status)
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('channel_sounding_config', config)
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('channel_sounding_config_removed', config.config_id)
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('channel_sounding_procedure_failure', event.status)
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('channel_sounding_procedure', procedure)
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('role_change', new_role)
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('role_change_failure', error)
6035
- 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)
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('classic_pairing')
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('classic_pairing_failure', status)
6224
+ connection.emit(connection.EVENT_CLASSIC_PAIRING_FAILURE, status)
6048
6225
 
6049
6226
  def on_pairing_start(self, connection: Connection) -> None:
6050
- connection.emit('pairing_start')
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('pairing', keys)
6241
+ connection.emit(connection.EVENT_PAIRING, keys)
6065
6242
 
6066
6243
  def on_pairing_failure(self, connection: Connection, reason: int) -> None:
6067
- connection.emit('pairing_failure', reason)
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):