bumble 0.0.201__py3-none-any.whl → 0.0.203__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
bumble/device.py CHANGED
@@ -51,129 +51,9 @@ from typing_extensions import Self
51
51
 
52
52
  from pyee import EventEmitter
53
53
 
54
- from bumble import hci
55
54
  from .colors import color
56
55
  from .att import ATT_CID, ATT_DEFAULT_MTU, ATT_PDU
57
56
  from .gatt import Characteristic, Descriptor, Service
58
- from .hci import (
59
- HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_192_TYPE,
60
- HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_256_TYPE,
61
- HCI_CENTRAL_ROLE,
62
- HCI_PERIPHERAL_ROLE,
63
- HCI_COMMAND_STATUS_PENDING,
64
- HCI_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES_ERROR,
65
- HCI_DISPLAY_YES_NO_IO_CAPABILITY,
66
- HCI_DISPLAY_ONLY_IO_CAPABILITY,
67
- HCI_EXTENDED_INQUIRY_MODE,
68
- HCI_GENERAL_INQUIRY_LAP,
69
- HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR,
70
- HCI_KEYBOARD_ONLY_IO_CAPABILITY,
71
- HCI_LE_1M_PHY,
72
- HCI_LE_1M_PHY_BIT,
73
- HCI_LE_2M_PHY,
74
- HCI_LE_CODED_PHY,
75
- HCI_LE_CODED_PHY_BIT,
76
- HCI_LE_EXTENDED_CREATE_CONNECTION_COMMAND,
77
- HCI_LE_RAND_COMMAND,
78
- HCI_LE_READ_PHY_COMMAND,
79
- HCI_LE_SET_PHY_COMMAND,
80
- HCI_MITM_NOT_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS,
81
- HCI_MITM_NOT_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS,
82
- HCI_MITM_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS,
83
- HCI_MITM_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS,
84
- HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY,
85
- HCI_OPERATION_CANCELLED_BY_HOST_ERROR,
86
- HCI_R2_PAGE_SCAN_REPETITION_MODE,
87
- HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR,
88
- HCI_SUCCESS,
89
- HCI_WRITE_LE_HOST_SUPPORT_COMMAND,
90
- HCI_Accept_Connection_Request_Command,
91
- HCI_Authentication_Requested_Command,
92
- HCI_Command_Status_Event,
93
- HCI_Constant,
94
- HCI_Create_Connection_Cancel_Command,
95
- HCI_Create_Connection_Command,
96
- HCI_Connection_Complete_Event,
97
- HCI_Disconnect_Command,
98
- HCI_Encryption_Change_Event,
99
- HCI_Error,
100
- HCI_IO_Capability_Request_Reply_Command,
101
- HCI_Inquiry_Cancel_Command,
102
- HCI_Inquiry_Command,
103
- HCI_IsoDataPacket,
104
- HCI_LE_Accept_CIS_Request_Command,
105
- HCI_LE_Add_Device_To_Resolving_List_Command,
106
- HCI_LE_Advertising_Report_Event,
107
- HCI_LE_BIGInfo_Advertising_Report_Event,
108
- HCI_LE_Clear_Resolving_List_Command,
109
- HCI_LE_Connection_Update_Command,
110
- HCI_LE_Create_Connection_Cancel_Command,
111
- HCI_LE_Create_Connection_Command,
112
- HCI_LE_Create_CIS_Command,
113
- HCI_LE_Periodic_Advertising_Create_Sync_Command,
114
- HCI_LE_Periodic_Advertising_Create_Sync_Cancel_Command,
115
- HCI_LE_Periodic_Advertising_Report_Event,
116
- HCI_LE_Periodic_Advertising_Sync_Transfer_Command,
117
- HCI_LE_Periodic_Advertising_Terminate_Sync_Command,
118
- HCI_LE_Enable_Encryption_Command,
119
- HCI_LE_Extended_Advertising_Report_Event,
120
- HCI_LE_Extended_Create_Connection_Command,
121
- HCI_LE_Rand_Command,
122
- HCI_LE_Read_PHY_Command,
123
- HCI_LE_Read_Remote_Features_Command,
124
- HCI_LE_Reject_CIS_Request_Command,
125
- HCI_LE_Remove_Advertising_Set_Command,
126
- HCI_LE_Set_Address_Resolution_Enable_Command,
127
- HCI_LE_Set_Advertising_Data_Command,
128
- HCI_LE_Set_Advertising_Enable_Command,
129
- HCI_LE_Set_Advertising_Parameters_Command,
130
- HCI_LE_Set_Advertising_Set_Random_Address_Command,
131
- HCI_LE_Set_CIG_Parameters_Command,
132
- HCI_LE_Set_Data_Length_Command,
133
- HCI_LE_Set_Default_PHY_Command,
134
- HCI_LE_Set_Extended_Scan_Enable_Command,
135
- HCI_LE_Set_Extended_Scan_Parameters_Command,
136
- HCI_LE_Set_Extended_Scan_Response_Data_Command,
137
- HCI_LE_Set_Extended_Advertising_Data_Command,
138
- HCI_LE_Set_Extended_Advertising_Enable_Command,
139
- HCI_LE_Set_Extended_Advertising_Parameters_Command,
140
- HCI_LE_Set_Host_Feature_Command,
141
- HCI_LE_Set_Periodic_Advertising_Enable_Command,
142
- HCI_LE_Set_PHY_Command,
143
- HCI_LE_Set_Random_Address_Command,
144
- HCI_LE_Set_Scan_Enable_Command,
145
- HCI_LE_Set_Scan_Parameters_Command,
146
- HCI_LE_Set_Scan_Response_Data_Command,
147
- HCI_PIN_Code_Request_Reply_Command,
148
- HCI_PIN_Code_Request_Negative_Reply_Command,
149
- HCI_Read_BD_ADDR_Command,
150
- HCI_Read_RSSI_Command,
151
- HCI_Reject_Connection_Request_Command,
152
- HCI_Remote_Name_Request_Command,
153
- HCI_Switch_Role_Command,
154
- HCI_Set_Connection_Encryption_Command,
155
- HCI_StatusError,
156
- HCI_SynchronousDataPacket,
157
- HCI_User_Confirmation_Request_Negative_Reply_Command,
158
- HCI_User_Confirmation_Request_Reply_Command,
159
- HCI_User_Passkey_Request_Negative_Reply_Command,
160
- HCI_User_Passkey_Request_Reply_Command,
161
- HCI_Write_Class_Of_Device_Command,
162
- HCI_Write_Extended_Inquiry_Response_Command,
163
- HCI_Write_Inquiry_Mode_Command,
164
- HCI_Write_LE_Host_Support_Command,
165
- HCI_Write_Local_Name_Command,
166
- HCI_Write_Scan_Enable_Command,
167
- HCI_Write_Secure_Connections_Host_Support_Command,
168
- HCI_Write_Simple_Pairing_Mode_Command,
169
- Address,
170
- OwnAddressType,
171
- LeFeature,
172
- LeFeatureMask,
173
- LmpFeatureMask,
174
- Phy,
175
- phy_list_to_bits,
176
- )
177
57
  from .host import Host
178
58
  from .profiles.gap import GenericAccessService
179
59
  from .core import (
@@ -207,6 +87,7 @@ from .keys import (
207
87
  KeyStore,
208
88
  PairingKeys,
209
89
  )
90
+ from bumble import hci
210
91
  from bumble import pairing
211
92
  from bumble import gatt_client
212
93
  from bumble import gatt_server
@@ -262,7 +143,7 @@ DEVICE_DEFAULT_L2CAP_COC_MTU = l2cap.L2CAP_LE_CREDIT_BASED_CONN
262
143
  DEVICE_DEFAULT_L2CAP_COC_MPS = l2cap.L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MPS
263
144
  DEVICE_DEFAULT_L2CAP_COC_MAX_CREDITS = l2cap.L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_INITIAL_CREDITS
264
145
  DEVICE_DEFAULT_ADVERTISING_TX_POWER = (
265
- HCI_LE_Set_Extended_Advertising_Parameters_Command.TX_POWER_NO_PREFERENCE
146
+ hci.HCI_LE_Set_Extended_Advertising_Parameters_Command.TX_POWER_NO_PREFERENCE
266
147
  )
267
148
  DEVICE_DEFAULT_PERIODIC_ADVERTISING_SYNC_SKIP = 0
268
149
  DEVICE_DEFAULT_PERIODIC_ADVERTISING_SYNC_TIMEOUT = 5.0
@@ -286,8 +167,8 @@ class ObjectLookupError(BaseBumbleError):
286
167
  @dataclass
287
168
  class Advertisement:
288
169
  # Attributes
289
- address: Address
290
- rssi: int = HCI_LE_Extended_Advertising_Report_Event.RSSI_NOT_AVAILABLE
170
+ address: hci.Address
171
+ rssi: int = hci.HCI_LE_Extended_Advertising_Report_Event.RSSI_NOT_AVAILABLE
291
172
  is_legacy: bool = False
292
173
  is_anonymous: bool = False
293
174
  is_connectable: bool = False
@@ -299,17 +180,17 @@ class Advertisement:
299
180
  primary_phy: int = 0
300
181
  secondary_phy: int = 0
301
182
  tx_power: int = (
302
- HCI_LE_Extended_Advertising_Report_Event.TX_POWER_INFORMATION_NOT_AVAILABLE
183
+ hci.HCI_LE_Extended_Advertising_Report_Event.TX_POWER_INFORMATION_NOT_AVAILABLE
303
184
  )
304
185
  sid: int = 0
305
186
  data_bytes: bytes = b''
306
187
 
307
188
  # Constants
308
189
  TX_POWER_NOT_AVAILABLE: ClassVar[int] = (
309
- HCI_LE_Extended_Advertising_Report_Event.TX_POWER_INFORMATION_NOT_AVAILABLE
190
+ hci.HCI_LE_Extended_Advertising_Report_Event.TX_POWER_INFORMATION_NOT_AVAILABLE
310
191
  )
311
192
  RSSI_NOT_AVAILABLE: ClassVar[int] = (
312
- HCI_LE_Extended_Advertising_Report_Event.RSSI_NOT_AVAILABLE
193
+ hci.HCI_LE_Extended_Advertising_Report_Event.RSSI_NOT_AVAILABLE
313
194
  )
314
195
 
315
196
  def __post_init__(self) -> None:
@@ -317,10 +198,10 @@ class Advertisement:
317
198
 
318
199
  @classmethod
319
200
  def from_advertising_report(cls, report) -> Optional[Advertisement]:
320
- if isinstance(report, HCI_LE_Advertising_Report_Event.Report):
201
+ if isinstance(report, hci.HCI_LE_Advertising_Report_Event.Report):
321
202
  return LegacyAdvertisement.from_advertising_report(report)
322
203
 
323
- if isinstance(report, HCI_LE_Extended_Advertising_Report_Event.Report):
204
+ if isinstance(report, hci.HCI_LE_Extended_Advertising_Report_Event.Report):
324
205
  return ExtendedAdvertisement.from_advertising_report(report)
325
206
 
326
207
  return None
@@ -336,18 +217,18 @@ class LegacyAdvertisement(Advertisement):
336
217
  is_legacy=True,
337
218
  is_connectable=report.event_type
338
219
  in (
339
- HCI_LE_Advertising_Report_Event.ADV_IND,
340
- HCI_LE_Advertising_Report_Event.ADV_DIRECT_IND,
220
+ hci.HCI_LE_Advertising_Report_Event.ADV_IND,
221
+ hci.HCI_LE_Advertising_Report_Event.ADV_DIRECT_IND,
341
222
  ),
342
223
  is_directed=report.event_type
343
- == HCI_LE_Advertising_Report_Event.ADV_DIRECT_IND,
224
+ == hci.HCI_LE_Advertising_Report_Event.ADV_DIRECT_IND,
344
225
  is_scannable=report.event_type
345
226
  in (
346
- HCI_LE_Advertising_Report_Event.ADV_IND,
347
- HCI_LE_Advertising_Report_Event.ADV_SCAN_IND,
227
+ hci.HCI_LE_Advertising_Report_Event.ADV_IND,
228
+ hci.HCI_LE_Advertising_Report_Event.ADV_SCAN_IND,
348
229
  ),
349
230
  is_scan_response=report.event_type
350
- == HCI_LE_Advertising_Report_Event.SCAN_RSP,
231
+ == hci.HCI_LE_Advertising_Report_Event.SCAN_RSP,
351
232
  data_bytes=report.data,
352
233
  )
353
234
 
@@ -361,14 +242,14 @@ class ExtendedAdvertisement(Advertisement):
361
242
  return cls(
362
243
  address = report.address,
363
244
  rssi = report.rssi,
364
- is_legacy = report.event_type & (1 << HCI_LE_Extended_Advertising_Report_Event.LEGACY_ADVERTISING_PDU_USED) != 0,
365
- is_anonymous = report.address.address_type == HCI_LE_Extended_Advertising_Report_Event.ANONYMOUS_ADDRESS_TYPE,
366
- is_connectable = report.event_type & (1 << HCI_LE_Extended_Advertising_Report_Event.CONNECTABLE_ADVERTISING) != 0,
367
- is_directed = report.event_type & (1 << HCI_LE_Extended_Advertising_Report_Event.DIRECTED_ADVERTISING) != 0,
368
- is_scannable = report.event_type & (1 << HCI_LE_Extended_Advertising_Report_Event.SCANNABLE_ADVERTISING) != 0,
369
- is_scan_response = report.event_type & (1 << HCI_LE_Extended_Advertising_Report_Event.SCAN_RESPONSE) != 0,
370
- is_complete = (report.event_type >> 5 & 3) == HCI_LE_Extended_Advertising_Report_Event.DATA_COMPLETE,
371
- is_truncated = (report.event_type >> 5 & 3) == HCI_LE_Extended_Advertising_Report_Event.DATA_INCOMPLETE_TRUNCATED_NO_MORE_TO_COME,
245
+ is_legacy = report.event_type & (1 << hci.HCI_LE_Extended_Advertising_Report_Event.LEGACY_ADVERTISING_PDU_USED) != 0,
246
+ is_anonymous = report.address.address_type == hci.HCI_LE_Extended_Advertising_Report_Event.ANONYMOUS_ADDRESS_TYPE,
247
+ is_connectable = report.event_type & (1 << hci.HCI_LE_Extended_Advertising_Report_Event.CONNECTABLE_ADVERTISING) != 0,
248
+ is_directed = report.event_type & (1 << hci.HCI_LE_Extended_Advertising_Report_Event.DIRECTED_ADVERTISING) != 0,
249
+ is_scannable = report.event_type & (1 << hci.HCI_LE_Extended_Advertising_Report_Event.SCANNABLE_ADVERTISING) != 0,
250
+ is_scan_response = report.event_type & (1 << hci.HCI_LE_Extended_Advertising_Report_Event.SCAN_RESPONSE) != 0,
251
+ is_complete = (report.event_type >> 5 & 3) == hci.HCI_LE_Extended_Advertising_Report_Event.DATA_COMPLETE,
252
+ is_truncated = (report.event_type >> 5 & 3) == hci.HCI_LE_Extended_Advertising_Report_Event.DATA_INCOMPLETE_TRUNCATED_NO_MORE_TO_COME,
372
253
  primary_phy = report.primary_phy,
373
254
  secondary_phy = report.secondary_phy,
374
255
  tx_power = report.tx_power,
@@ -473,15 +354,15 @@ class AdvertisingType(IntEnum):
473
354
  class LegacyAdvertiser:
474
355
  device: Device
475
356
  advertising_type: AdvertisingType
476
- own_address_type: OwnAddressType
477
- peer_address: Address
357
+ own_address_type: hci.OwnAddressType
358
+ peer_address: hci.Address
478
359
  auto_restart: bool
479
360
 
480
361
  async def start(self) -> None:
481
362
  # Set/update the advertising data if the advertising type allows it
482
363
  if self.advertising_type.has_data:
483
364
  await self.device.send_command(
484
- HCI_LE_Set_Advertising_Data_Command(
365
+ hci.HCI_LE_Set_Advertising_Data_Command(
485
366
  advertising_data=self.device.advertising_data
486
367
  ),
487
368
  check_result=True,
@@ -490,7 +371,7 @@ class LegacyAdvertiser:
490
371
  # Set/update the scan response data if the advertising is scannable
491
372
  if self.advertising_type.is_scannable:
492
373
  await self.device.send_command(
493
- HCI_LE_Set_Scan_Response_Data_Command(
374
+ hci.HCI_LE_Set_Scan_Response_Data_Command(
494
375
  scan_response_data=self.device.scan_response_data
495
376
  ),
496
377
  check_result=True,
@@ -498,7 +379,7 @@ class LegacyAdvertiser:
498
379
 
499
380
  # Set the advertising parameters
500
381
  await self.device.send_command(
501
- HCI_LE_Set_Advertising_Parameters_Command(
382
+ hci.HCI_LE_Set_Advertising_Parameters_Command(
502
383
  advertising_interval_min=self.device.advertising_interval_min,
503
384
  advertising_interval_max=self.device.advertising_interval_max,
504
385
  advertising_type=int(self.advertising_type),
@@ -513,14 +394,14 @@ class LegacyAdvertiser:
513
394
 
514
395
  # Enable advertising
515
396
  await self.device.send_command(
516
- HCI_LE_Set_Advertising_Enable_Command(advertising_enable=1),
397
+ hci.HCI_LE_Set_Advertising_Enable_Command(advertising_enable=1),
517
398
  check_result=True,
518
399
  )
519
400
 
520
401
  async def stop(self) -> None:
521
402
  # Disable advertising
522
403
  await self.device.send_command(
523
- HCI_LE_Set_Advertising_Enable_Command(advertising_enable=0),
404
+ hci.HCI_LE_Set_Advertising_Enable_Command(advertising_enable=0),
524
405
  check_result=True,
525
406
  )
526
407
 
@@ -537,8 +418,8 @@ class AdvertisingEventProperties:
537
418
  include_tx_power: bool = False
538
419
 
539
420
  def __int__(self) -> int:
540
- properties = (
541
- HCI_LE_Set_Extended_Advertising_Parameters_Command.AdvertisingProperties(0)
421
+ properties = hci.HCI_LE_Set_Extended_Advertising_Parameters_Command.AdvertisingProperties(
422
+ 0
542
423
  )
543
424
  if self.is_connectable:
544
425
  properties |= properties.CONNECTABLE_ADVERTISING
@@ -576,21 +457,21 @@ class AdvertisingEventProperties:
576
457
  # -----------------------------------------------------------------------------
577
458
  @dataclass
578
459
  class PeriodicAdvertisement:
579
- address: Address
460
+ address: hci.Address
580
461
  sid: int
581
462
  tx_power: int = (
582
- HCI_LE_Periodic_Advertising_Report_Event.TX_POWER_INFORMATION_NOT_AVAILABLE
463
+ hci.HCI_LE_Periodic_Advertising_Report_Event.TX_POWER_INFORMATION_NOT_AVAILABLE
583
464
  )
584
- rssi: int = HCI_LE_Periodic_Advertising_Report_Event.RSSI_NOT_AVAILABLE
465
+ rssi: int = hci.HCI_LE_Periodic_Advertising_Report_Event.RSSI_NOT_AVAILABLE
585
466
  is_truncated: bool = False
586
467
  data_bytes: bytes = b''
587
468
 
588
469
  # Constants
589
470
  TX_POWER_NOT_AVAILABLE: ClassVar[int] = (
590
- HCI_LE_Periodic_Advertising_Report_Event.TX_POWER_INFORMATION_NOT_AVAILABLE
471
+ hci.HCI_LE_Periodic_Advertising_Report_Event.TX_POWER_INFORMATION_NOT_AVAILABLE
591
472
  )
592
473
  RSSI_NOT_AVAILABLE: ClassVar[int] = (
593
- HCI_LE_Periodic_Advertising_Report_Event.RSSI_NOT_AVAILABLE
474
+ hci.HCI_LE_Periodic_Advertising_Report_Event.RSSI_NOT_AVAILABLE
594
475
  )
595
476
 
596
477
  def __post_init__(self) -> None:
@@ -602,7 +483,7 @@ class PeriodicAdvertisement:
602
483
  # -----------------------------------------------------------------------------
603
484
  @dataclass
604
485
  class BIGInfoAdvertisement:
605
- address: Address
486
+ address: hci.Address
606
487
  sid: int
607
488
  num_bis: int
608
489
  nse: int
@@ -613,12 +494,12 @@ class BIGInfoAdvertisement:
613
494
  max_pdu: int
614
495
  sdu_interval: int
615
496
  max_sdu: int
616
- phy: Phy
497
+ phy: hci.Phy
617
498
  framed: bool
618
499
  encrypted: bool
619
500
 
620
501
  @classmethod
621
- def from_report(cls, address: Address, sid: int, report) -> Self:
502
+ def from_report(cls, address: hci.Address, sid: int, report) -> Self:
622
503
  return cls(
623
504
  address,
624
505
  sid,
@@ -631,7 +512,7 @@ class BIGInfoAdvertisement:
631
512
  report.max_pdu,
632
513
  report.sdu_interval,
633
514
  report.max_sdu,
634
- Phy(report.phy),
515
+ hci.Phy(report.phy),
635
516
  report.framing != 0,
636
517
  report.encryption != 0,
637
518
  )
@@ -639,7 +520,9 @@ class BIGInfoAdvertisement:
639
520
 
640
521
  # -----------------------------------------------------------------------------
641
522
  # TODO: replace with typing.TypeAlias when the code base is all Python >= 3.10
642
- AdvertisingChannelMap = HCI_LE_Set_Extended_Advertising_Parameters_Command.ChannelMap
523
+ AdvertisingChannelMap = (
524
+ hci.HCI_LE_Set_Extended_Advertising_Parameters_Command.ChannelMap
525
+ )
643
526
 
644
527
 
645
528
  # -----------------------------------------------------------------------------
@@ -652,19 +535,19 @@ class AdvertisingParameters:
652
535
  primary_advertising_interval_min: int = DEVICE_DEFAULT_ADVERTISING_INTERVAL
653
536
  primary_advertising_interval_max: int = DEVICE_DEFAULT_ADVERTISING_INTERVAL
654
537
  primary_advertising_channel_map: (
655
- HCI_LE_Set_Extended_Advertising_Parameters_Command.ChannelMap
538
+ hci.HCI_LE_Set_Extended_Advertising_Parameters_Command.ChannelMap
656
539
  ) = (
657
540
  AdvertisingChannelMap.CHANNEL_37
658
541
  | AdvertisingChannelMap.CHANNEL_38
659
542
  | AdvertisingChannelMap.CHANNEL_39
660
543
  )
661
- own_address_type: OwnAddressType = OwnAddressType.RANDOM
662
- peer_address: Address = Address.ANY
544
+ own_address_type: hci.OwnAddressType = hci.OwnAddressType.RANDOM
545
+ peer_address: hci.Address = hci.Address.ANY
663
546
  advertising_filter_policy: int = 0
664
547
  advertising_tx_power: int = DEVICE_DEFAULT_ADVERTISING_TX_POWER
665
- primary_advertising_phy: Phy = Phy.LE_1M
548
+ primary_advertising_phy: hci.Phy = hci.Phy.LE_1M
666
549
  secondary_advertising_max_skip: int = 0
667
- secondary_advertising_phy: Phy = Phy.LE_1M
550
+ secondary_advertising_phy: hci.Phy = hci.Phy.LE_1M
668
551
  advertising_sid: int = 0
669
552
  enable_scan_request_notifications: bool = False
670
553
  primary_advertising_phy_options: int = 0
@@ -674,8 +557,15 @@ class AdvertisingParameters:
674
557
  # -----------------------------------------------------------------------------
675
558
  @dataclass
676
559
  class PeriodicAdvertisingParameters:
677
- # TODO implement this class
678
- pass
560
+ periodic_advertising_interval_min: int = DEVICE_DEFAULT_ADVERTISING_INTERVAL
561
+ periodic_advertising_interval_max: int = DEVICE_DEFAULT_ADVERTISING_INTERVAL
562
+ periodic_advertising_properties: (
563
+ hci.HCI_LE_Set_Periodic_Advertising_Parameters_Command.Properties
564
+ ) = field(
565
+ default_factory=lambda: hci.HCI_LE_Set_Periodic_Advertising_Parameters_Command.Properties(
566
+ 0
567
+ )
568
+ )
679
569
 
680
570
 
681
571
  # -----------------------------------------------------------------------------
@@ -684,7 +574,7 @@ class AdvertisingSet(EventEmitter):
684
574
  device: Device
685
575
  advertising_handle: int
686
576
  auto_restart: bool
687
- random_address: Optional[Address]
577
+ random_address: Optional[hci.Address]
688
578
  advertising_parameters: AdvertisingParameters
689
579
  advertising_data: bytes
690
580
  scan_response_data: bytes
@@ -692,6 +582,7 @@ class AdvertisingSet(EventEmitter):
692
582
  periodic_advertising_data: bytes
693
583
  selected_tx_power: int = 0
694
584
  enabled: bool = False
585
+ periodic_enabled: bool = False
695
586
 
696
587
  def __post_init__(self) -> None:
697
588
  super().__init__()
@@ -711,7 +602,7 @@ class AdvertisingSet(EventEmitter):
711
602
  )
712
603
 
713
604
  response = await self.device.send_command(
714
- HCI_LE_Set_Extended_Advertising_Parameters_Command(
605
+ hci.HCI_LE_Set_Extended_Advertising_Parameters_Command(
715
606
  advertising_handle=self.advertising_handle,
716
607
  advertising_event_properties=int(
717
608
  advertising_parameters.advertising_event_properties
@@ -720,7 +611,7 @@ class AdvertisingSet(EventEmitter):
720
611
  int(advertising_parameters.primary_advertising_interval_min / 0.625)
721
612
  ),
722
613
  primary_advertising_interval_max=(
723
- int(advertising_parameters.primary_advertising_interval_min / 0.625)
614
+ int(advertising_parameters.primary_advertising_interval_max / 0.625)
724
615
  ),
725
616
  primary_advertising_channel_map=int(
726
617
  advertising_parameters.primary_advertising_channel_map
@@ -752,10 +643,10 @@ class AdvertisingSet(EventEmitter):
752
643
  async def set_advertising_data(self, advertising_data: bytes) -> None:
753
644
  # pylint: disable=line-too-long
754
645
  await self.device.send_command(
755
- HCI_LE_Set_Extended_Advertising_Data_Command(
646
+ hci.HCI_LE_Set_Extended_Advertising_Data_Command(
756
647
  advertising_handle=self.advertising_handle,
757
- operation=HCI_LE_Set_Extended_Advertising_Data_Command.Operation.COMPLETE_DATA,
758
- fragment_preference=HCI_LE_Set_Extended_Advertising_Parameters_Command.SHOULD_NOT_FRAGMENT,
648
+ operation=hci.HCI_LE_Set_Extended_Advertising_Data_Command.Operation.COMPLETE_DATA,
649
+ fragment_preference=hci.HCI_LE_Set_Extended_Advertising_Parameters_Command.SHOULD_NOT_FRAGMENT,
759
650
  advertising_data=advertising_data,
760
651
  ),
761
652
  check_result=True,
@@ -775,10 +666,10 @@ class AdvertisingSet(EventEmitter):
775
666
  return
776
667
 
777
668
  await self.device.send_command(
778
- HCI_LE_Set_Extended_Scan_Response_Data_Command(
669
+ hci.HCI_LE_Set_Extended_Scan_Response_Data_Command(
779
670
  advertising_handle=self.advertising_handle,
780
- operation=HCI_LE_Set_Extended_Advertising_Data_Command.Operation.COMPLETE_DATA,
781
- fragment_preference=HCI_LE_Set_Extended_Advertising_Parameters_Command.SHOULD_NOT_FRAGMENT,
671
+ operation=hci.HCI_LE_Set_Extended_Advertising_Data_Command.Operation.COMPLETE_DATA,
672
+ fragment_preference=hci.HCI_LE_Set_Extended_Advertising_Parameters_Command.SHOULD_NOT_FRAGMENT,
782
673
  scan_response_data=scan_response_data,
783
674
  ),
784
675
  check_result=True,
@@ -788,16 +679,31 @@ class AdvertisingSet(EventEmitter):
788
679
  async def set_periodic_advertising_parameters(
789
680
  self, advertising_parameters: PeriodicAdvertisingParameters
790
681
  ) -> None:
791
- # TODO: send command
682
+ await self.device.send_command(
683
+ hci.HCI_LE_Set_Periodic_Advertising_Parameters_Command(
684
+ advertising_handle=self.advertising_handle,
685
+ periodic_advertising_interval_min=advertising_parameters.periodic_advertising_interval_min,
686
+ periodic_advertising_interval_max=advertising_parameters.periodic_advertising_interval_max,
687
+ periodic_advertising_properties=advertising_parameters.periodic_advertising_properties,
688
+ ),
689
+ check_result=True,
690
+ )
792
691
  self.periodic_advertising_parameters = advertising_parameters
793
692
 
794
693
  async def set_periodic_advertising_data(self, advertising_data: bytes) -> None:
795
- # TODO: send command
694
+ await self.device.send_command(
695
+ hci.HCI_LE_Set_Periodic_Advertising_Data_Command(
696
+ advertising_handle=self.advertising_handle,
697
+ operation=hci.HCI_LE_Set_Extended_Advertising_Data_Command.Operation.COMPLETE_DATA,
698
+ advertising_data=advertising_data,
699
+ ),
700
+ check_result=True,
701
+ )
796
702
  self.periodic_advertising_data = advertising_data
797
703
 
798
- async def set_random_address(self, random_address: Address) -> None:
704
+ async def set_random_address(self, random_address: hci.Address) -> None:
799
705
  await self.device.send_command(
800
- HCI_LE_Set_Advertising_Set_Random_Address_Command(
706
+ hci.HCI_LE_Set_Advertising_Set_Random_Address_Command(
801
707
  advertising_handle=self.advertising_handle,
802
708
  random_address=(random_address or self.device.random_address),
803
709
  ),
@@ -818,7 +724,7 @@ class AdvertisingSet(EventEmitter):
818
724
  (the default) for an unlimited number of advertisements.
819
725
  """
820
726
  await self.device.send_command(
821
- HCI_LE_Set_Extended_Advertising_Enable_Command(
727
+ hci.HCI_LE_Set_Extended_Advertising_Enable_Command(
822
728
  enable=1,
823
729
  advertising_handles=[self.advertising_handle],
824
730
  durations=[round(duration * 100)],
@@ -830,20 +736,9 @@ class AdvertisingSet(EventEmitter):
830
736
 
831
737
  self.emit('start')
832
738
 
833
- async def start_periodic(self, include_adi: bool = False) -> None:
834
- await self.device.send_command(
835
- HCI_LE_Set_Periodic_Advertising_Enable_Command(
836
- enable=1 | (2 if include_adi else 0),
837
- advertising_handles=self.advertising_handle,
838
- ),
839
- check_result=True,
840
- )
841
-
842
- self.emit('start_periodic')
843
-
844
739
  async def stop(self) -> None:
845
740
  await self.device.send_command(
846
- HCI_LE_Set_Extended_Advertising_Enable_Command(
741
+ hci.HCI_LE_Set_Extended_Advertising_Enable_Command(
847
742
  enable=0,
848
743
  advertising_handles=[self.advertising_handle],
849
744
  durations=[0],
@@ -855,20 +750,37 @@ class AdvertisingSet(EventEmitter):
855
750
 
856
751
  self.emit('stop')
857
752
 
753
+ async def start_periodic(self, include_adi: bool = False) -> None:
754
+ if self.periodic_enabled:
755
+ return
756
+ await self.device.send_command(
757
+ hci.HCI_LE_Set_Periodic_Advertising_Enable_Command(
758
+ enable=1 | (2 if include_adi else 0),
759
+ advertising_handle=self.advertising_handle,
760
+ ),
761
+ check_result=True,
762
+ )
763
+ self.periodic_enabled = True
764
+
765
+ self.emit('start_periodic')
766
+
858
767
  async def stop_periodic(self) -> None:
768
+ if not self.periodic_enabled:
769
+ return
859
770
  await self.device.send_command(
860
- HCI_LE_Set_Periodic_Advertising_Enable_Command(
771
+ hci.HCI_LE_Set_Periodic_Advertising_Enable_Command(
861
772
  enable=0,
862
- advertising_handles=self.advertising_handle,
773
+ advertising_handle=self.advertising_handle,
863
774
  ),
864
775
  check_result=True,
865
776
  )
777
+ self.periodic_enabled = False
866
778
 
867
779
  self.emit('stop_periodic')
868
780
 
869
781
  async def remove(self) -> None:
870
782
  await self.device.send_command(
871
- HCI_LE_Remove_Advertising_Set_Command(
783
+ hci.HCI_LE_Remove_Advertising_Set_Command(
872
784
  advertising_handle=self.advertising_handle
873
785
  ),
874
786
  check_result=True,
@@ -893,7 +805,7 @@ class PeriodicAdvertisingSync(EventEmitter):
893
805
 
894
806
  _state: State
895
807
  sync_handle: Optional[int]
896
- advertiser_address: Address
808
+ advertiser_address: hci.Address
897
809
  sid: int
898
810
  skip: int
899
811
  sync_timeout: float # Sync timeout, in seconds
@@ -906,7 +818,7 @@ class PeriodicAdvertisingSync(EventEmitter):
906
818
  def __init__(
907
819
  self,
908
820
  device: Device,
909
- advertiser_address: Address,
821
+ advertiser_address: hci.Address,
910
822
  sid: int,
911
823
  skip: int,
912
824
  sync_timeout: float,
@@ -921,7 +833,7 @@ class PeriodicAdvertisingSync(EventEmitter):
921
833
  self.skip = skip
922
834
  self.sync_timeout = sync_timeout
923
835
  self.filter_duplicates = filter_duplicates
924
- self.status = HCI_SUCCESS
836
+ self.status = hci.HCI_SUCCESS
925
837
  self.advertiser_phy = 0
926
838
  self.periodic_advertising_interval = 0
927
839
  self.advertiser_clock_accuracy = 0
@@ -941,14 +853,14 @@ class PeriodicAdvertisingSync(EventEmitter):
941
853
  if self.state != self.State.INIT:
942
854
  raise InvalidStateError('sync not in init state')
943
855
 
944
- options = HCI_LE_Periodic_Advertising_Create_Sync_Command.Options(0)
856
+ options = hci.HCI_LE_Periodic_Advertising_Create_Sync_Command.Options(0)
945
857
  if self.filter_duplicates:
946
858
  options |= (
947
- HCI_LE_Periodic_Advertising_Create_Sync_Command.Options.DUPLICATE_FILTERING_INITIALLY_ENABLED
859
+ hci.HCI_LE_Periodic_Advertising_Create_Sync_Command.Options.DUPLICATE_FILTERING_INITIALLY_ENABLED
948
860
  )
949
861
 
950
862
  response = await self.device.send_command(
951
- HCI_LE_Periodic_Advertising_Create_Sync_Command(
863
+ hci.HCI_LE_Periodic_Advertising_Create_Sync_Command(
952
864
  options=options,
953
865
  advertising_sid=self.sid,
954
866
  advertiser_address_type=self.advertiser_address.address_type,
@@ -958,8 +870,8 @@ class PeriodicAdvertisingSync(EventEmitter):
958
870
  sync_cte_type=0,
959
871
  )
960
872
  )
961
- if response.status != HCI_Command_Status_Event.PENDING:
962
- raise HCI_StatusError(response)
873
+ if response.status != hci.HCI_Command_Status_Event.PENDING:
874
+ raise hci.HCI_StatusError(response)
963
875
 
964
876
  self.state = self.State.PENDING
965
877
 
@@ -970,9 +882,9 @@ class PeriodicAdvertisingSync(EventEmitter):
970
882
  if self.state == self.State.PENDING:
971
883
  self.state = self.State.CANCELLED
972
884
  response = await self.device.send_command(
973
- HCI_LE_Periodic_Advertising_Create_Sync_Cancel_Command(),
885
+ hci.HCI_LE_Periodic_Advertising_Create_Sync_Cancel_Command(),
974
886
  )
975
- if response.return_parameters == HCI_SUCCESS:
887
+ if response.return_parameters == hci.HCI_SUCCESS:
976
888
  if self in self.device.periodic_advertising_syncs:
977
889
  self.device.periodic_advertising_syncs.remove(self)
978
890
  return
@@ -981,7 +893,7 @@ class PeriodicAdvertisingSync(EventEmitter):
981
893
  self.state = self.State.TERMINATED
982
894
  if self.sync_handle is not None:
983
895
  await self.device.send_command(
984
- HCI_LE_Periodic_Advertising_Terminate_Sync_Command(
896
+ hci.HCI_LE_Periodic_Advertising_Terminate_Sync_Command(
985
897
  sync_handle=self.sync_handle
986
898
  )
987
899
  )
@@ -1013,7 +925,7 @@ class PeriodicAdvertisingSync(EventEmitter):
1013
925
  AsyncRunner.spawn(self.terminate())
1014
926
  return
1015
927
 
1016
- if status == HCI_SUCCESS:
928
+ if status == hci.HCI_SUCCESS:
1017
929
  self.sync_handle = sync_handle
1018
930
  self.advertiser_phy = advertiser_phy
1019
931
  self.periodic_advertising_interval = periodic_advertising_interval
@@ -1026,7 +938,7 @@ class PeriodicAdvertisingSync(EventEmitter):
1026
938
  if self in self.device.periodic_advertising_syncs:
1027
939
  self.device.periodic_advertising_syncs.remove(self)
1028
940
 
1029
- if status == HCI_OPERATION_CANCELLED_BY_HOST_ERROR:
941
+ if status == hci.HCI_OPERATION_CANCELLED_BY_HOST_ERROR:
1030
942
  self.state = self.State.CANCELLED
1031
943
  self.emit('cancellation')
1032
944
  return
@@ -1042,7 +954,7 @@ class PeriodicAdvertisingSync(EventEmitter):
1042
954
  self.data_accumulator += report.data
1043
955
  if (
1044
956
  report.data_status
1045
- == HCI_LE_Periodic_Advertising_Report_Event.DataStatus.DATA_INCOMPLETE_MORE_TO_COME
957
+ == hci.HCI_LE_Periodic_Advertising_Report_Event.DataStatus.DATA_INCOMPLETE_MORE_TO_COME
1046
958
  ):
1047
959
  return
1048
960
 
@@ -1055,7 +967,7 @@ class PeriodicAdvertisingSync(EventEmitter):
1055
967
  report.rssi,
1056
968
  is_truncated=(
1057
969
  report.data_status
1058
- == HCI_LE_Periodic_Advertising_Report_Event.DataStatus.DATA_INCOMPLETE_TRUNCATED_NO_MORE_TO_COME
970
+ == hci.HCI_LE_Periodic_Advertising_Report_Event.DataStatus.DATA_INCOMPLETE_TRUNCATED_NO_MORE_TO_COME
1059
971
  ),
1060
972
  data_bytes=self.data_accumulator,
1061
973
  ),
@@ -1278,13 +1190,13 @@ class ScoLink(CompositeEventEmitter):
1278
1190
  acl_connection: Connection
1279
1191
  handle: int
1280
1192
  link_type: int
1281
- sink: Optional[Callable[[HCI_SynchronousDataPacket], Any]] = None
1193
+ sink: Optional[Callable[[hci.HCI_SynchronousDataPacket], Any]] = None
1282
1194
 
1283
1195
  def __post_init__(self) -> None:
1284
1196
  super().__init__()
1285
1197
 
1286
1198
  async def disconnect(
1287
- self, reason: int = HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR
1199
+ self, reason: int = hci.HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR
1288
1200
  ) -> None:
1289
1201
  await self.device.disconnect(self, reason)
1290
1202
 
@@ -1302,13 +1214,13 @@ class CisLink(CompositeEventEmitter):
1302
1214
  cis_id: int # CIS ID assigned by Central device
1303
1215
  cig_id: int # CIG ID assigned by Central device
1304
1216
  state: State = State.PENDING
1305
- sink: Optional[Callable[[HCI_IsoDataPacket], Any]] = None
1217
+ sink: Optional[Callable[[hci.HCI_IsoDataPacket], Any]] = None
1306
1218
 
1307
1219
  def __post_init__(self) -> None:
1308
1220
  super().__init__()
1309
1221
 
1310
1222
  async def disconnect(
1311
- self, reason: int = HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR
1223
+ self, reason: int = hci.HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR
1312
1224
  ) -> None:
1313
1225
  await self.device.disconnect(self, reason)
1314
1226
 
@@ -1318,11 +1230,11 @@ class Connection(CompositeEventEmitter):
1318
1230
  device: Device
1319
1231
  handle: int
1320
1232
  transport: int
1321
- self_address: Address
1322
- self_resolvable_address: Optional[Address]
1323
- peer_address: Address
1324
- peer_resolvable_address: Optional[Address]
1325
- peer_le_features: Optional[LeFeatureMask]
1233
+ self_address: hci.Address
1234
+ self_resolvable_address: Optional[hci.Address]
1235
+ peer_address: hci.Address
1236
+ peer_resolvable_address: Optional[hci.Address]
1237
+ peer_le_features: Optional[hci.LeFeatureMask]
1326
1238
  role: int
1327
1239
  encryption: int
1328
1240
  authenticated: bool
@@ -1478,7 +1390,7 @@ class Connection(CompositeEventEmitter):
1478
1390
  return await self.device.create_l2cap_channel(connection=self, spec=spec)
1479
1391
 
1480
1392
  async def disconnect(
1481
- self, reason: int = HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR
1393
+ self, reason: int = hci.HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR
1482
1394
  ) -> None:
1483
1395
  await self.device.disconnect(self, reason)
1484
1396
 
@@ -1549,7 +1461,7 @@ class Connection(CompositeEventEmitter):
1549
1461
  async def request_remote_name(self):
1550
1462
  return await self.device.request_remote_name(self)
1551
1463
 
1552
- async def get_remote_le_features(self) -> LeFeatureMask:
1464
+ async def get_remote_le_features(self) -> hci.LeFeatureMask:
1553
1465
  """[LE Only] Reads remote LE supported features.
1554
1466
 
1555
1467
  Returns:
@@ -1565,9 +1477,9 @@ class Connection(CompositeEventEmitter):
1565
1477
  if exc_type is None:
1566
1478
  try:
1567
1479
  await self.disconnect()
1568
- except HCI_StatusError as error:
1480
+ except hci.HCI_StatusError as error:
1569
1481
  # Invalid parameter means the connection is no longer valid
1570
- if error.error_code != HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR:
1482
+ if error.error_code != hci.HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR:
1571
1483
  raise
1572
1484
 
1573
1485
  def __str__(self):
@@ -1594,7 +1506,7 @@ class Connection(CompositeEventEmitter):
1594
1506
  class DeviceConfiguration:
1595
1507
  # Setup defaults
1596
1508
  name: str = DEVICE_DEFAULT_NAME
1597
- address: Address = Address(DEVICE_DEFAULT_ADDRESS)
1509
+ address: hci.Address = hci.Address(DEVICE_DEFAULT_ADDRESS)
1598
1510
  class_of_device: int = DEVICE_DEFAULT_CLASS_OF_DEVICE
1599
1511
  scan_response_data: bytes = DEVICE_DEFAULT_SCAN_RESPONSE_DATA
1600
1512
  advertising_interval_min: int = DEVICE_DEFAULT_ADVERTISING_INTERVAL
@@ -1632,12 +1544,12 @@ class DeviceConfiguration:
1632
1544
 
1633
1545
  # Load simple properties
1634
1546
  if address := config.pop('address', None):
1635
- self.address = Address(address)
1547
+ self.address = hci.Address(address)
1636
1548
 
1637
1549
  # Load or synthesize an IRK
1638
1550
  if irk := config.pop('irk', None):
1639
1551
  self.irk = bytes.fromhex(irk)
1640
- elif self.address != Address(DEVICE_DEFAULT_ADDRESS):
1552
+ elif self.address != hci.Address(DEVICE_DEFAULT_ADDRESS):
1641
1553
  # Construct an IRK from the address bytes
1642
1554
  # NOTE: this is not secure, but will always give the same IRK for the same
1643
1555
  # address
@@ -1660,6 +1572,10 @@ class DeviceConfiguration:
1660
1572
  )
1661
1573
  )
1662
1574
 
1575
+ # Load scan response data
1576
+ if scan_response_data := config.pop('scan_response_data', None):
1577
+ self.scan_response_data = bytes.fromhex(scan_response_data)
1578
+
1663
1579
  # Load advertising interval (for backward compatibility)
1664
1580
  if advertising_interval := config.pop('advertising_interval', None):
1665
1581
  self.advertising_interval_min = advertising_interval
@@ -1774,9 +1690,11 @@ device_host_event_handlers: List[str] = []
1774
1690
  # -----------------------------------------------------------------------------
1775
1691
  class Device(CompositeEventEmitter):
1776
1692
  # Incomplete list of fields.
1777
- random_address: Address # Random private address that may change periodically
1778
- public_address: Address # Public address that is globally unique (from controller)
1779
- static_address: Address # Random static address that does not change once set
1693
+ random_address: hci.Address # Random private address that may change periodically
1694
+ public_address: (
1695
+ hci.Address
1696
+ ) # Public address that is globally unique (from controller)
1697
+ static_address: hci.Address # Random static address that does not change once set
1780
1698
  classic_enabled: bool
1781
1699
  name: str
1782
1700
  class_of_device: int
@@ -1784,11 +1702,12 @@ class Device(CompositeEventEmitter):
1784
1702
  advertising_data: bytes
1785
1703
  scan_response_data: bytes
1786
1704
  connections: Dict[int, Connection]
1787
- pending_connections: Dict[Address, Connection]
1705
+ pending_connections: Dict[hci.Address, Connection]
1788
1706
  classic_pending_accepts: Dict[
1789
- Address, List[asyncio.Future[Union[Connection, Tuple[Address, int, int]]]]
1707
+ hci.Address,
1708
+ List[asyncio.Future[Union[Connection, Tuple[hci.Address, int, int]]]],
1790
1709
  ]
1791
- advertisement_accumulators: Dict[Address, AdvertisementDataAccumulator]
1710
+ advertisement_accumulators: Dict[hci.Address, AdvertisementDataAccumulator]
1792
1711
  periodic_advertising_syncs: List[PeriodicAdvertisingSync]
1793
1712
  config: DeviceConfiguration
1794
1713
  legacy_advertiser: Optional[LegacyAdvertiser]
@@ -1822,7 +1741,7 @@ class Device(CompositeEventEmitter):
1822
1741
  def with_hci(
1823
1742
  cls,
1824
1743
  name: str,
1825
- address: Address,
1744
+ address: hci.Address,
1826
1745
  hci_source: TransportSource,
1827
1746
  hci_sink: TransportSink,
1828
1747
  ) -> Device:
@@ -1858,7 +1777,7 @@ class Device(CompositeEventEmitter):
1858
1777
  def __init__(
1859
1778
  self,
1860
1779
  name: Optional[str] = None,
1861
- address: Optional[Address] = None,
1780
+ address: Optional[hci.Address] = None,
1862
1781
  config: Optional[DeviceConfiguration] = None,
1863
1782
  host: Optional[Host] = None,
1864
1783
  generic_access_service: bool = True,
@@ -1890,8 +1809,8 @@ class Device(CompositeEventEmitter):
1890
1809
  self.inquiry_response = None
1891
1810
  self.address_resolver = None
1892
1811
  self.classic_pending_accepts = {
1893
- Address.ANY: []
1894
- } # Futures, by BD address OR [Futures] for Address.ANY
1812
+ hci.Address.ANY: []
1813
+ } # Futures, by BD address OR [Futures] for hci.Address.ANY
1895
1814
 
1896
1815
  # In Python <= 3.9 + Rust Runtime, asyncio.Lock cannot be properly initiated.
1897
1816
  if sys.version_info >= (3, 10):
@@ -1907,7 +1826,7 @@ class Device(CompositeEventEmitter):
1907
1826
  self.config = config
1908
1827
 
1909
1828
  self.name = config.name
1910
- self.public_address = Address.ANY
1829
+ self.public_address = hci.Address.ANY
1911
1830
  self.random_address = config.address
1912
1831
  self.static_address = config.address
1913
1832
  self.class_of_device = config.class_of_device
@@ -1982,7 +1901,7 @@ class Device(CompositeEventEmitter):
1982
1901
  # If an address is passed, override the address from the config
1983
1902
  if address:
1984
1903
  if isinstance(address, str):
1985
- address = Address(address)
1904
+ address = hci.Address(address)
1986
1905
  self.random_address = address
1987
1906
  self.static_address = address
1988
1907
 
@@ -2062,12 +1981,12 @@ class Device(CompositeEventEmitter):
2062
1981
 
2063
1982
  def find_connection_by_bd_addr(
2064
1983
  self,
2065
- bd_addr: Address,
1984
+ bd_addr: hci.Address,
2066
1985
  transport: Optional[int] = None,
2067
1986
  check_address_type: bool = False,
2068
1987
  ) -> Optional[Connection]:
2069
1988
  for connection in self.connections.values():
2070
- if connection.peer_address.to_bytes() == bd_addr.to_bytes():
1989
+ if bytes(connection.peer_address) == bytes(bd_addr):
2071
1990
  if (
2072
1991
  check_address_type
2073
1992
  and connection.peer_address.address_type != bd_addr.address_type
@@ -2201,8 +2120,8 @@ class Device(CompositeEventEmitter):
2201
2120
  await self.host.reset()
2202
2121
 
2203
2122
  # Try to get the public address from the controller
2204
- response = await self.send_command(HCI_Read_BD_ADDR_Command())
2205
- if response.return_parameters.status == HCI_SUCCESS:
2123
+ response = await self.send_command(hci.HCI_Read_BD_ADDR_Command())
2124
+ if response.return_parameters.status == hci.HCI_SUCCESS:
2206
2125
  logger.debug(
2207
2126
  color(f'BD_ADDR: {response.return_parameters.bd_addr}', 'yellow')
2208
2127
  )
@@ -2219,9 +2138,9 @@ class Device(CompositeEventEmitter):
2219
2138
  smp.SMP_BR_CID, self.on_smp_pdu
2220
2139
  )
2221
2140
 
2222
- if self.host.supports_command(HCI_WRITE_LE_HOST_SUPPORT_COMMAND):
2141
+ if self.host.supports_command(hci.HCI_WRITE_LE_HOST_SUPPORT_COMMAND):
2223
2142
  await self.send_command(
2224
- HCI_Write_LE_Host_Support_Command(
2143
+ hci.HCI_Write_LE_Host_Support_Command(
2225
2144
  le_supported_host=int(self.le_enabled),
2226
2145
  simultaneous_le_host=int(self.le_simultaneous_enabled),
2227
2146
  ),
@@ -2230,12 +2149,12 @@ class Device(CompositeEventEmitter):
2230
2149
 
2231
2150
  if self.le_enabled:
2232
2151
  # Generate a random address if not set.
2233
- if self.static_address == Address.ANY_RANDOM:
2234
- self.static_address = Address.generate_static_address()
2152
+ if self.static_address == hci.Address.ANY_RANDOM:
2153
+ self.static_address = hci.Address.generate_static_address()
2235
2154
 
2236
2155
  # If LE Privacy is enabled, generate an RPA
2237
2156
  if self.le_privacy_enabled:
2238
- self.random_address = Address.generate_private_address(self.irk)
2157
+ self.random_address = hci.Address.generate_private_address(self.irk)
2239
2158
  logger.info(f'Initial RPA: {self.random_address}')
2240
2159
  if self.le_rpa_timeout > 0:
2241
2160
  # Start a task to periodically generate a new RPA
@@ -2245,15 +2164,15 @@ class Device(CompositeEventEmitter):
2245
2164
  else:
2246
2165
  self.random_address = self.static_address
2247
2166
 
2248
- if self.random_address != Address.ANY_RANDOM:
2167
+ if self.random_address != hci.Address.ANY_RANDOM:
2249
2168
  logger.debug(
2250
2169
  color(
2251
- f'LE Random Address: {self.random_address}',
2170
+ f'LE Random hci.Address: {self.random_address}',
2252
2171
  'yellow',
2253
2172
  )
2254
2173
  )
2255
2174
  await self.send_command(
2256
- HCI_LE_Set_Random_Address_Command(
2175
+ hci.HCI_LE_Set_Random_Address_Command(
2257
2176
  random_address=self.random_address
2258
2177
  ),
2259
2178
  check_result=True,
@@ -2266,7 +2185,7 @@ class Device(CompositeEventEmitter):
2266
2185
  # Enable address resolution
2267
2186
  if self.address_resolution_offload:
2268
2187
  await self.send_command(
2269
- HCI_LE_Set_Address_Resolution_Enable_Command(
2188
+ hci.HCI_LE_Set_Address_Resolution_Enable_Command(
2270
2189
  address_resolution_enable=1
2271
2190
  ),
2272
2191
  check_result=True,
@@ -2274,8 +2193,8 @@ class Device(CompositeEventEmitter):
2274
2193
 
2275
2194
  if self.cis_enabled:
2276
2195
  await self.send_command(
2277
- HCI_LE_Set_Host_Feature_Command(
2278
- bit_number=LeFeature.CONNECTED_ISOCHRONOUS_STREAM,
2196
+ hci.HCI_LE_Set_Host_Feature_Command(
2197
+ bit_number=hci.LeFeature.CONNECTED_ISOCHRONOUS_STREAM,
2279
2198
  bit_value=1,
2280
2199
  ),
2281
2200
  check_result=True,
@@ -2283,18 +2202,20 @@ class Device(CompositeEventEmitter):
2283
2202
 
2284
2203
  if self.classic_enabled:
2285
2204
  await self.send_command(
2286
- HCI_Write_Local_Name_Command(local_name=self.name.encode('utf8'))
2205
+ hci.HCI_Write_Local_Name_Command(local_name=self.name.encode('utf8'))
2287
2206
  )
2288
2207
  await self.send_command(
2289
- HCI_Write_Class_Of_Device_Command(class_of_device=self.class_of_device)
2208
+ hci.HCI_Write_Class_Of_Device_Command(
2209
+ class_of_device=self.class_of_device
2210
+ )
2290
2211
  )
2291
2212
  await self.send_command(
2292
- HCI_Write_Simple_Pairing_Mode_Command(
2213
+ hci.HCI_Write_Simple_Pairing_Mode_Command(
2293
2214
  simple_pairing_mode=int(self.classic_ssp_enabled)
2294
2215
  )
2295
2216
  )
2296
2217
  await self.send_command(
2297
- HCI_Write_Secure_Connections_Host_Support_Command(
2218
+ hci.HCI_Write_Secure_Connections_Host_Support_Command(
2298
2219
  secure_connections_host_support=int(self.classic_sc_enabled)
2299
2220
  )
2300
2221
  )
@@ -2302,14 +2223,16 @@ class Device(CompositeEventEmitter):
2302
2223
  await self.set_discoverable(self.discoverable)
2303
2224
 
2304
2225
  if self.classic_interlaced_scan_enabled:
2305
- if self.host.supports_lmp_features(LmpFeatureMask.INTERLACED_PAGE_SCAN):
2226
+ if self.host.supports_lmp_features(
2227
+ hci.LmpFeatureMask.INTERLACED_PAGE_SCAN
2228
+ ):
2306
2229
  await self.send_command(
2307
2230
  hci.HCI_Write_Page_Scan_Type_Command(page_scan_type=1),
2308
2231
  check_result=True,
2309
2232
  )
2310
2233
 
2311
2234
  if self.host.supports_lmp_features(
2312
- LmpFeatureMask.INTERLACED_INQUIRY_SCAN
2235
+ hci.LmpFeatureMask.INTERLACED_INQUIRY_SCAN
2313
2236
  ):
2314
2237
  await self.send_command(
2315
2238
  hci.HCI_Write_Inquiry_Scan_Type_Command(scan_type=1),
@@ -2344,11 +2267,11 @@ class Device(CompositeEventEmitter):
2344
2267
  logger.debug('skipping RPA update')
2345
2268
  return False
2346
2269
 
2347
- random_address = Address.generate_private_address(self.irk)
2270
+ random_address = hci.Address.generate_private_address(self.irk)
2348
2271
  response = await self.send_command(
2349
- HCI_LE_Set_Random_Address_Command(random_address=self.random_address)
2272
+ hci.HCI_LE_Set_Random_Address_Command(random_address=self.random_address)
2350
2273
  )
2351
- if response.return_parameters == HCI_SUCCESS:
2274
+ if response.return_parameters == hci.HCI_SUCCESS:
2352
2275
  logger.info(f'new RPA: {random_address}')
2353
2276
  self.random_address = random_address
2354
2277
  return True
@@ -2371,13 +2294,13 @@ class Device(CompositeEventEmitter):
2371
2294
  self.address_resolver = smp.AddressResolver(resolving_keys)
2372
2295
 
2373
2296
  if self.address_resolution_offload or self.address_generation_offload:
2374
- await self.send_command(HCI_LE_Clear_Resolving_List_Command())
2297
+ await self.send_command(hci.HCI_LE_Clear_Resolving_List_Command())
2375
2298
 
2376
2299
  # Add an empty entry for non-directed address generation.
2377
2300
  await self.send_command(
2378
- HCI_LE_Add_Device_To_Resolving_List_Command(
2379
- peer_identity_address_type=Address.ANY.address_type,
2380
- peer_identity_address=Address.ANY,
2301
+ hci.HCI_LE_Add_Device_To_Resolving_List_Command(
2302
+ peer_identity_address_type=hci.Address.ANY.address_type,
2303
+ peer_identity_address=hci.Address.ANY,
2381
2304
  peer_irk=bytes(16),
2382
2305
  local_irk=self.irk,
2383
2306
  )
@@ -2385,7 +2308,7 @@ class Device(CompositeEventEmitter):
2385
2308
 
2386
2309
  for irk, address in resolving_keys:
2387
2310
  await self.send_command(
2388
- HCI_LE_Add_Device_To_Resolving_List_Command(
2311
+ hci.HCI_LE_Add_Device_To_Resolving_List_Command(
2389
2312
  peer_identity_address_type=address.address_type,
2390
2313
  peer_identity_address=address,
2391
2314
  peer_irk=irk,
@@ -2393,16 +2316,16 @@ class Device(CompositeEventEmitter):
2393
2316
  )
2394
2317
  )
2395
2318
 
2396
- def supports_le_features(self, feature: LeFeatureMask) -> bool:
2319
+ def supports_le_features(self, feature: hci.LeFeatureMask) -> bool:
2397
2320
  return self.host.supports_le_features(feature)
2398
2321
 
2399
2322
  def supports_le_phy(self, phy: int) -> bool:
2400
- if phy == HCI_LE_1M_PHY:
2323
+ if phy == hci.HCI_LE_1M_PHY:
2401
2324
  return True
2402
2325
 
2403
2326
  feature_map = {
2404
- HCI_LE_2M_PHY: LeFeatureMask.LE_2M_PHY,
2405
- HCI_LE_CODED_PHY: LeFeatureMask.LE_CODED_PHY,
2327
+ hci.HCI_LE_2M_PHY: hci.LeFeatureMask.LE_2M_PHY,
2328
+ hci.HCI_LE_CODED_PHY: hci.LeFeatureMask.LE_CODED_PHY,
2406
2329
  }
2407
2330
  if phy not in feature_map:
2408
2331
  raise InvalidArgumentError('invalid PHY')
@@ -2411,17 +2334,17 @@ class Device(CompositeEventEmitter):
2411
2334
 
2412
2335
  @property
2413
2336
  def supports_le_extended_advertising(self):
2414
- return self.supports_le_features(LeFeatureMask.LE_EXTENDED_ADVERTISING)
2337
+ return self.supports_le_features(hci.LeFeatureMask.LE_EXTENDED_ADVERTISING)
2415
2338
 
2416
2339
  @property
2417
2340
  def supports_le_periodic_advertising(self):
2418
- return self.supports_le_features(LeFeatureMask.LE_PERIODIC_ADVERTISING)
2341
+ return self.supports_le_features(hci.LeFeatureMask.LE_PERIODIC_ADVERTISING)
2419
2342
 
2420
2343
  async def start_advertising(
2421
2344
  self,
2422
2345
  advertising_type: AdvertisingType = AdvertisingType.UNDIRECTED_CONNECTABLE_SCANNABLE,
2423
- target: Optional[Address] = None,
2424
- own_address_type: int = OwnAddressType.RANDOM,
2346
+ target: Optional[hci.Address] = None,
2347
+ own_address_type: int = hci.OwnAddressType.RANDOM,
2425
2348
  auto_restart: bool = False,
2426
2349
  advertising_data: Optional[bytes] = None,
2427
2350
  scan_response_data: Optional[bytes] = None,
@@ -2472,7 +2395,7 @@ class Device(CompositeEventEmitter):
2472
2395
  raise InvalidArgumentError('directed advertising requires a target')
2473
2396
  peer_address = target
2474
2397
  else:
2475
- peer_address = Address.ANY
2398
+ peer_address = hci.Address.ANY
2476
2399
 
2477
2400
  # If we're already advertising, stop now because we'll be re-creating
2478
2401
  # a new advertiser or advertising set.
@@ -2494,7 +2417,7 @@ class Device(CompositeEventEmitter):
2494
2417
  ),
2495
2418
  primary_advertising_interval_min=self.advertising_interval_min,
2496
2419
  primary_advertising_interval_max=self.advertising_interval_max,
2497
- own_address_type=OwnAddressType(own_address_type),
2420
+ own_address_type=hci.OwnAddressType(own_address_type),
2498
2421
  peer_address=peer_address,
2499
2422
  ),
2500
2423
  advertising_data=(
@@ -2509,7 +2432,7 @@ class Device(CompositeEventEmitter):
2509
2432
  self.legacy_advertiser = LegacyAdvertiser(
2510
2433
  device=self,
2511
2434
  advertising_type=advertising_type,
2512
- own_address_type=OwnAddressType(own_address_type),
2435
+ own_address_type=hci.OwnAddressType(own_address_type),
2513
2436
  peer_address=peer_address,
2514
2437
  auto_restart=auto_restart,
2515
2438
  )
@@ -2531,7 +2454,7 @@ class Device(CompositeEventEmitter):
2531
2454
  async def create_advertising_set(
2532
2455
  self,
2533
2456
  advertising_parameters: Optional[AdvertisingParameters] = None,
2534
- random_address: Optional[Address] = None,
2457
+ random_address: Optional[hci.Address] = None,
2535
2458
  advertising_data: bytes = b'',
2536
2459
  scan_response_data: bytes = b'',
2537
2460
  periodic_advertising_parameters: Optional[PeriodicAdvertisingParameters] = None,
@@ -2571,14 +2494,27 @@ class Device(CompositeEventEmitter):
2571
2494
  if advertising_parameters is None:
2572
2495
  advertising_parameters = AdvertisingParameters()
2573
2496
 
2497
+ if periodic_advertising_data and periodic_advertising_parameters is None:
2498
+ periodic_advertising_parameters = PeriodicAdvertisingParameters()
2499
+
2574
2500
  if (
2575
2501
  not advertising_parameters.advertising_event_properties.is_legacy
2576
2502
  and advertising_data
2577
2503
  and scan_response_data
2578
2504
  ):
2579
2505
  raise InvalidArgumentError(
2580
- "Extended advertisements can't have both data and scan \
2581
- response data"
2506
+ "Extended advertisements can't have both data and scan response data"
2507
+ )
2508
+
2509
+ if periodic_advertising_parameters and (
2510
+ advertising_parameters.advertising_event_properties.is_connectable
2511
+ or advertising_parameters.advertising_event_properties.is_scannable
2512
+ or advertising_parameters.advertising_event_properties.is_anonymous
2513
+ or advertising_parameters.advertising_event_properties.is_legacy
2514
+ ):
2515
+ raise InvalidArgumentError(
2516
+ "Periodic advertising set cannot be connectable, scannable, anonymous,"
2517
+ "or legacy"
2582
2518
  )
2583
2519
 
2584
2520
  # Allocate a new handle
@@ -2600,7 +2536,7 @@ class Device(CompositeEventEmitter):
2600
2536
  # provided.
2601
2537
  if (
2602
2538
  advertising_parameters.own_address_type
2603
- in (OwnAddressType.RANDOM, OwnAddressType.RESOLVABLE_OR_RANDOM)
2539
+ in (hci.OwnAddressType.RANDOM, hci.OwnAddressType.RESOLVABLE_OR_RANDOM)
2604
2540
  and random_address is None
2605
2541
  ):
2606
2542
  random_address = self.random_address
@@ -2633,18 +2569,20 @@ class Device(CompositeEventEmitter):
2633
2569
  await advertising_set.set_scan_response_data(scan_response_data)
2634
2570
 
2635
2571
  if periodic_advertising_parameters:
2636
- # TODO: call LE Set Periodic Advertising Parameters command
2637
- raise NotImplementedError('periodic advertising not yet supported')
2572
+ await advertising_set.set_periodic_advertising_parameters(
2573
+ periodic_advertising_parameters
2574
+ )
2638
2575
 
2639
2576
  if periodic_advertising_data:
2640
- # TODO: call LE Set Periodic Advertising Data command
2641
- raise NotImplementedError('periodic advertising not yet supported')
2577
+ await advertising_set.set_periodic_advertising_data(
2578
+ periodic_advertising_data
2579
+ )
2642
2580
 
2643
- except HCI_Error as error:
2581
+ except hci.HCI_Error as error:
2644
2582
  # Remove the advertising set so that it doesn't stay dangling in the
2645
2583
  # controller.
2646
2584
  await self.send_command(
2647
- HCI_LE_Remove_Advertising_Set_Command(
2585
+ hci.HCI_LE_Remove_Advertising_Set_Command(
2648
2586
  advertising_handle=advertising_handle
2649
2587
  ),
2650
2588
  check_result=False,
@@ -2687,9 +2625,9 @@ class Device(CompositeEventEmitter):
2687
2625
  active: bool = True,
2688
2626
  scan_interval: int = DEVICE_DEFAULT_SCAN_INTERVAL, # Scan interval in ms
2689
2627
  scan_window: int = DEVICE_DEFAULT_SCAN_WINDOW, # Scan window in ms
2690
- own_address_type: int = OwnAddressType.RANDOM,
2628
+ own_address_type: int = hci.OwnAddressType.RANDOM,
2691
2629
  filter_duplicates: bool = False,
2692
- scanning_phys: List[int] = [HCI_LE_1M_PHY, HCI_LE_CODED_PHY],
2630
+ scanning_phys: List[int] = [hci.HCI_LE_1M_PHY, hci.HCI_LE_CODED_PHY],
2693
2631
  ) -> None:
2694
2632
  # Check that the arguments are legal
2695
2633
  if scan_interval < scan_window:
@@ -2709,29 +2647,29 @@ class Device(CompositeEventEmitter):
2709
2647
  if not legacy and self.supports_le_extended_advertising:
2710
2648
  # Set the scanning parameters
2711
2649
  scan_type = (
2712
- HCI_LE_Set_Extended_Scan_Parameters_Command.ACTIVE_SCANNING
2650
+ hci.HCI_LE_Set_Extended_Scan_Parameters_Command.ACTIVE_SCANNING
2713
2651
  if active
2714
- else HCI_LE_Set_Extended_Scan_Parameters_Command.PASSIVE_SCANNING
2652
+ else hci.HCI_LE_Set_Extended_Scan_Parameters_Command.PASSIVE_SCANNING
2715
2653
  )
2716
2654
  scanning_filter_policy = (
2717
- HCI_LE_Set_Extended_Scan_Parameters_Command.BASIC_UNFILTERED_POLICY
2655
+ hci.HCI_LE_Set_Extended_Scan_Parameters_Command.BASIC_UNFILTERED_POLICY
2718
2656
  ) # TODO: support other types
2719
2657
 
2720
2658
  scanning_phy_count = 0
2721
2659
  scanning_phys_bits = 0
2722
- if HCI_LE_1M_PHY in scanning_phys:
2723
- scanning_phys_bits |= 1 << HCI_LE_1M_PHY_BIT
2660
+ if hci.HCI_LE_1M_PHY in scanning_phys:
2661
+ scanning_phys_bits |= 1 << hci.HCI_LE_1M_PHY_BIT
2724
2662
  scanning_phy_count += 1
2725
- if HCI_LE_CODED_PHY in scanning_phys:
2726
- if self.supports_le_features(LeFeatureMask.LE_CODED_PHY):
2727
- scanning_phys_bits |= 1 << HCI_LE_CODED_PHY_BIT
2663
+ if hci.HCI_LE_CODED_PHY in scanning_phys:
2664
+ if self.supports_le_features(hci.LeFeatureMask.LE_CODED_PHY):
2665
+ scanning_phys_bits |= 1 << hci.HCI_LE_CODED_PHY_BIT
2728
2666
  scanning_phy_count += 1
2729
2667
 
2730
2668
  if scanning_phy_count == 0:
2731
2669
  raise InvalidArgumentError('at least one scanning PHY must be enabled')
2732
2670
 
2733
2671
  await self.send_command(
2734
- HCI_LE_Set_Extended_Scan_Parameters_Command(
2672
+ hci.HCI_LE_Set_Extended_Scan_Parameters_Command(
2735
2673
  own_address_type=own_address_type,
2736
2674
  scanning_filter_policy=scanning_filter_policy,
2737
2675
  scanning_phys=scanning_phys_bits,
@@ -2744,7 +2682,7 @@ class Device(CompositeEventEmitter):
2744
2682
 
2745
2683
  # Enable scanning
2746
2684
  await self.send_command(
2747
- HCI_LE_Set_Extended_Scan_Enable_Command(
2685
+ hci.HCI_LE_Set_Extended_Scan_Enable_Command(
2748
2686
  enable=1,
2749
2687
  filter_duplicates=1 if filter_duplicates else 0,
2750
2688
  duration=0, # TODO allow other values
@@ -2755,25 +2693,25 @@ class Device(CompositeEventEmitter):
2755
2693
  else:
2756
2694
  # Set the scanning parameters
2757
2695
  scan_type = (
2758
- HCI_LE_Set_Scan_Parameters_Command.ACTIVE_SCANNING
2696
+ hci.HCI_LE_Set_Scan_Parameters_Command.ACTIVE_SCANNING
2759
2697
  if active
2760
- else HCI_LE_Set_Scan_Parameters_Command.PASSIVE_SCANNING
2698
+ else hci.HCI_LE_Set_Scan_Parameters_Command.PASSIVE_SCANNING
2761
2699
  )
2762
2700
  await self.send_command(
2763
2701
  # pylint: disable=line-too-long
2764
- HCI_LE_Set_Scan_Parameters_Command(
2702
+ hci.HCI_LE_Set_Scan_Parameters_Command(
2765
2703
  le_scan_type=scan_type,
2766
2704
  le_scan_interval=int(scan_window / 0.625),
2767
2705
  le_scan_window=int(scan_window / 0.625),
2768
2706
  own_address_type=own_address_type,
2769
- scanning_filter_policy=HCI_LE_Set_Scan_Parameters_Command.BASIC_UNFILTERED_POLICY,
2707
+ scanning_filter_policy=hci.HCI_LE_Set_Scan_Parameters_Command.BASIC_UNFILTERED_POLICY,
2770
2708
  ),
2771
2709
  check_result=True,
2772
2710
  )
2773
2711
 
2774
2712
  # Enable scanning
2775
2713
  await self.send_command(
2776
- HCI_LE_Set_Scan_Enable_Command(
2714
+ hci.HCI_LE_Set_Scan_Enable_Command(
2777
2715
  le_scan_enable=1, filter_duplicates=1 if filter_duplicates else 0
2778
2716
  ),
2779
2717
  check_result=True,
@@ -2786,14 +2724,16 @@ class Device(CompositeEventEmitter):
2786
2724
  # Disable scanning
2787
2725
  if not legacy and self.supports_le_extended_advertising:
2788
2726
  await self.send_command(
2789
- HCI_LE_Set_Extended_Scan_Enable_Command(
2727
+ hci.HCI_LE_Set_Extended_Scan_Enable_Command(
2790
2728
  enable=0, filter_duplicates=0, duration=0, period=0
2791
2729
  ),
2792
2730
  check_result=True,
2793
2731
  )
2794
2732
  else:
2795
2733
  await self.send_command(
2796
- HCI_LE_Set_Scan_Enable_Command(le_scan_enable=0, filter_duplicates=0),
2734
+ hci.HCI_LE_Set_Scan_Enable_Command(
2735
+ le_scan_enable=0, filter_duplicates=0
2736
+ ),
2797
2737
  check_result=True,
2798
2738
  )
2799
2739
 
@@ -2813,7 +2753,7 @@ class Device(CompositeEventEmitter):
2813
2753
 
2814
2754
  async def create_periodic_advertising_sync(
2815
2755
  self,
2816
- advertiser_address: Address,
2756
+ advertiser_address: hci.Address,
2817
2757
  sid: int,
2818
2758
  skip: int = DEVICE_DEFAULT_PERIODIC_ADVERTISING_SYNC_SKIP,
2819
2759
  sync_timeout: float = DEVICE_DEFAULT_PERIODIC_ADVERTISING_SYNC_TIMEOUT,
@@ -2874,7 +2814,7 @@ class Device(CompositeEventEmitter):
2874
2814
  status: int,
2875
2815
  sync_handle: int,
2876
2816
  advertising_sid: int,
2877
- advertiser_address: Address,
2817
+ advertiser_address: hci.Address,
2878
2818
  advertiser_phy: int,
2879
2819
  periodic_advertising_interval: int,
2880
2820
  advertiser_clock_accuracy: int,
@@ -2912,7 +2852,7 @@ class Device(CompositeEventEmitter):
2912
2852
  def on_periodic_advertising_report(
2913
2853
  self,
2914
2854
  periodic_advertising_sync: PeriodicAdvertisingSync,
2915
- report: HCI_LE_Periodic_Advertising_Report_Event,
2855
+ report: hci.HCI_LE_Periodic_Advertising_Report_Event,
2916
2856
  ):
2917
2857
  periodic_advertising_sync.on_periodic_advertising_report(report)
2918
2858
 
@@ -2921,33 +2861,35 @@ class Device(CompositeEventEmitter):
2921
2861
  def on_biginfo_advertising_report(
2922
2862
  self,
2923
2863
  periodic_advertising_sync: PeriodicAdvertisingSync,
2924
- report: HCI_LE_BIGInfo_Advertising_Report_Event,
2864
+ report: hci.HCI_LE_BIGInfo_Advertising_Report_Event,
2925
2865
  ):
2926
2866
  periodic_advertising_sync.on_biginfo_advertising_report(report)
2927
2867
 
2928
2868
  async def start_discovery(self, auto_restart: bool = True) -> None:
2929
2869
  await self.send_command(
2930
- HCI_Write_Inquiry_Mode_Command(inquiry_mode=HCI_EXTENDED_INQUIRY_MODE),
2870
+ hci.HCI_Write_Inquiry_Mode_Command(
2871
+ inquiry_mode=hci.HCI_EXTENDED_INQUIRY_MODE
2872
+ ),
2931
2873
  check_result=True,
2932
2874
  )
2933
2875
 
2934
2876
  response = await self.send_command(
2935
- HCI_Inquiry_Command(
2936
- lap=HCI_GENERAL_INQUIRY_LAP,
2877
+ hci.HCI_Inquiry_Command(
2878
+ lap=hci.HCI_GENERAL_INQUIRY_LAP,
2937
2879
  inquiry_length=DEVICE_DEFAULT_INQUIRY_LENGTH,
2938
2880
  num_responses=0, # Unlimited number of responses.
2939
2881
  )
2940
2882
  )
2941
- if response.status != HCI_Command_Status_Event.PENDING:
2883
+ if response.status != hci.HCI_Command_Status_Event.PENDING:
2942
2884
  self.discovering = False
2943
- raise HCI_StatusError(response)
2885
+ raise hci.HCI_StatusError(response)
2944
2886
 
2945
2887
  self.auto_restart_inquiry = auto_restart
2946
2888
  self.discovering = True
2947
2889
 
2948
2890
  async def stop_discovery(self) -> None:
2949
2891
  if self.discovering:
2950
- await self.send_command(HCI_Inquiry_Cancel_Command(), check_result=True)
2892
+ await self.send_command(hci.HCI_Inquiry_Cancel_Command(), check_result=True)
2951
2893
  self.auto_restart_inquiry = True
2952
2894
  self.discovering = False
2953
2895
 
@@ -2972,7 +2914,7 @@ class Device(CompositeEventEmitter):
2972
2914
  scan_enable = 0x00
2973
2915
 
2974
2916
  return await self.send_command(
2975
- HCI_Write_Scan_Enable_Command(scan_enable=scan_enable)
2917
+ hci.HCI_Write_Scan_Enable_Command(scan_enable=scan_enable)
2976
2918
  )
2977
2919
 
2978
2920
  async def set_discoverable(self, discoverable: bool = True) -> None:
@@ -2993,7 +2935,7 @@ class Device(CompositeEventEmitter):
2993
2935
 
2994
2936
  # Update the controller
2995
2937
  await self.send_command(
2996
- HCI_Write_Extended_Inquiry_Response_Command(
2938
+ hci.HCI_Write_Extended_Inquiry_Response_Command(
2997
2939
  fec_required=0, extended_inquiry_response=self.inquiry_response
2998
2940
  ),
2999
2941
  check_result=True,
@@ -3013,12 +2955,12 @@ class Device(CompositeEventEmitter):
3013
2955
 
3014
2956
  async def connect(
3015
2957
  self,
3016
- peer_address: Union[Address, str],
2958
+ peer_address: Union[hci.Address, str],
3017
2959
  transport: int = BT_LE_TRANSPORT,
3018
2960
  connection_parameters_preferences: Optional[
3019
2961
  Dict[int, ConnectionParametersPreferences]
3020
2962
  ] = None,
3021
- own_address_type: int = OwnAddressType.RANDOM,
2963
+ own_address_type: int = hci.OwnAddressType.RANDOM,
3022
2964
  timeout: Optional[float] = DEVICE_DEFAULT_CONNECT_TIMEOUT,
3023
2965
  always_resolve: bool = False,
3024
2966
  ) -> Connection:
@@ -3030,7 +2972,7 @@ class Device(CompositeEventEmitter):
3030
2972
 
3031
2973
  Args:
3032
2974
  peer_address:
3033
- Address or name of the device to connect to.
2975
+ hci.Address or name of the device to connect to.
3034
2976
  If a string is passed:
3035
2977
  If the string is an address followed by a `@` suffix, the `always_resolve`
3036
2978
  argument is implicitly set to True, so the connection is made to the
@@ -3050,8 +2992,8 @@ class Device(CompositeEventEmitter):
3050
2992
 
3051
2993
  own_address_type:
3052
2994
  (BLE only, ignored for BR/EDR)
3053
- OwnAddressType.RANDOM to use this device's random address, or
3054
- OwnAddressType.PUBLIC to use this device's public address.
2995
+ hci.OwnAddressType.RANDOM to use this device's random address, or
2996
+ hci.OwnAddressType.PUBLIC to use this device's public address.
3055
2997
 
3056
2998
  timeout:
3057
2999
  Maximum time to wait for a connection to be established, in seconds.
@@ -3080,13 +3022,13 @@ class Device(CompositeEventEmitter):
3080
3022
  if isinstance(peer_address, str):
3081
3023
  try:
3082
3024
  if transport == BT_LE_TRANSPORT and peer_address.endswith('@'):
3083
- peer_address = Address.from_string_for_transport(
3025
+ peer_address = hci.Address.from_string_for_transport(
3084
3026
  peer_address[:-1], transport
3085
3027
  )
3086
3028
  always_resolve = True
3087
3029
  logger.debug('forcing address resolution')
3088
3030
  else:
3089
- peer_address = Address.from_string_for_transport(
3031
+ peer_address = hci.Address.from_string_for_transport(
3090
3032
  peer_address, transport
3091
3033
  )
3092
3034
  except (InvalidArgumentError, ValueError):
@@ -3100,11 +3042,11 @@ class Device(CompositeEventEmitter):
3100
3042
  # All BR/EDR addresses should be public addresses
3101
3043
  if (
3102
3044
  transport == BT_BR_EDR_TRANSPORT
3103
- and peer_address.address_type != Address.PUBLIC_DEVICE_ADDRESS
3045
+ and peer_address.address_type != hci.Address.PUBLIC_DEVICE_ADDRESS
3104
3046
  ):
3105
3047
  raise InvalidArgumentError('BR/EDR addresses must be PUBLIC')
3106
3048
 
3107
- assert isinstance(peer_address, Address)
3049
+ assert isinstance(peer_address, hci.Address)
3108
3050
 
3109
3051
  if transport == BT_LE_TRANSPORT and always_resolve:
3110
3052
  logger.debug('resolving address')
@@ -3139,13 +3081,13 @@ class Device(CompositeEventEmitter):
3139
3081
  if connection_parameters_preferences is None:
3140
3082
  if connection_parameters_preferences is None:
3141
3083
  connection_parameters_preferences = {
3142
- HCI_LE_1M_PHY: ConnectionParametersPreferences.default
3084
+ hci.HCI_LE_1M_PHY: ConnectionParametersPreferences.default
3143
3085
  }
3144
3086
 
3145
3087
  self.connect_own_address_type = own_address_type
3146
3088
 
3147
3089
  if self.host.supports_command(
3148
- HCI_LE_EXTENDED_CREATE_CONNECTION_COMMAND
3090
+ hci.HCI_LE_EXTENDED_CREATE_CONNECTION_COMMAND
3149
3091
  ):
3150
3092
  # Only keep supported PHYs
3151
3093
  phys = sorted(
@@ -3162,7 +3104,7 @@ class Device(CompositeEventEmitter):
3162
3104
  raise InvalidArgumentError('at least one supported PHY needed')
3163
3105
 
3164
3106
  phy_count = len(phys)
3165
- initiating_phys = phy_list_to_bits(phys)
3107
+ initiating_phys = hci.phy_list_to_bits(phys)
3166
3108
 
3167
3109
  connection_interval_mins = [
3168
3110
  int(
@@ -3207,7 +3149,7 @@ class Device(CompositeEventEmitter):
3207
3149
  ]
3208
3150
 
3209
3151
  result = await self.send_command(
3210
- HCI_LE_Extended_Create_Connection_Command(
3152
+ hci.HCI_LE_Extended_Create_Connection_Command(
3211
3153
  initiator_filter_policy=0,
3212
3154
  own_address_type=own_address_type,
3213
3155
  peer_address_type=peer_address.address_type,
@@ -3230,12 +3172,12 @@ class Device(CompositeEventEmitter):
3230
3172
  )
3231
3173
  )
3232
3174
  else:
3233
- if HCI_LE_1M_PHY not in connection_parameters_preferences:
3175
+ if hci.HCI_LE_1M_PHY not in connection_parameters_preferences:
3234
3176
  raise InvalidArgumentError('1M PHY preferences required')
3235
3177
 
3236
- prefs = connection_parameters_preferences[HCI_LE_1M_PHY]
3178
+ prefs = connection_parameters_preferences[hci.HCI_LE_1M_PHY]
3237
3179
  result = await self.send_command(
3238
- HCI_LE_Create_Connection_Command(
3180
+ hci.HCI_LE_Create_Connection_Command(
3239
3181
  le_scan_interval=int(
3240
3182
  DEVICE_DEFAULT_CONNECT_SCAN_INTERVAL / 0.625
3241
3183
  ),
@@ -3266,18 +3208,18 @@ class Device(CompositeEventEmitter):
3266
3208
 
3267
3209
  # TODO: allow passing other settings
3268
3210
  result = await self.send_command(
3269
- HCI_Create_Connection_Command(
3211
+ hci.HCI_Create_Connection_Command(
3270
3212
  bd_addr=peer_address,
3271
3213
  packet_type=0xCC18, # FIXME: change
3272
- page_scan_repetition_mode=HCI_R2_PAGE_SCAN_REPETITION_MODE,
3214
+ page_scan_repetition_mode=hci.HCI_R2_PAGE_SCAN_REPETITION_MODE,
3273
3215
  clock_offset=0x0000,
3274
3216
  allow_role_switch=0x01,
3275
3217
  reserved=0,
3276
3218
  )
3277
3219
  )
3278
3220
 
3279
- if result.status != HCI_Command_Status_Event.PENDING:
3280
- raise HCI_StatusError(result)
3221
+ if result.status != hci.HCI_Command_Status_Event.PENDING:
3222
+ raise hci.HCI_StatusError(result)
3281
3223
 
3282
3224
  # Wait for the connection process to complete
3283
3225
  if transport == BT_LE_TRANSPORT:
@@ -3292,10 +3234,12 @@ class Device(CompositeEventEmitter):
3292
3234
  )
3293
3235
  except asyncio.TimeoutError:
3294
3236
  if transport == BT_LE_TRANSPORT:
3295
- await self.send_command(HCI_LE_Create_Connection_Cancel_Command())
3237
+ await self.send_command(
3238
+ hci.HCI_LE_Create_Connection_Cancel_Command()
3239
+ )
3296
3240
  else:
3297
3241
  await self.send_command(
3298
- HCI_Create_Connection_Cancel_Command(bd_addr=peer_address)
3242
+ hci.HCI_Create_Connection_Cancel_Command(bd_addr=peer_address)
3299
3243
  )
3300
3244
 
3301
3245
  try:
@@ -3313,7 +3257,7 @@ class Device(CompositeEventEmitter):
3313
3257
 
3314
3258
  async def accept(
3315
3259
  self,
3316
- peer_address: Union[Address, str] = Address.ANY,
3260
+ peer_address: Union[hci.Address, str] = hci.Address.ANY,
3317
3261
  role: int = BT_PERIPHERAL_ROLE,
3318
3262
  timeout: Optional[float] = DEVICE_DEFAULT_CONNECT_TIMEOUT,
3319
3263
  ) -> Connection:
@@ -3330,7 +3274,7 @@ class Device(CompositeEventEmitter):
3330
3274
 
3331
3275
  if isinstance(peer_address, str):
3332
3276
  try:
3333
- peer_address = Address(peer_address)
3277
+ peer_address = hci.Address(peer_address)
3334
3278
  except InvalidArgumentError:
3335
3279
  # If the address is not parsable, assume it is a name instead
3336
3280
  logger.debug('looking for peer by name')
@@ -3338,16 +3282,16 @@ class Device(CompositeEventEmitter):
3338
3282
  peer_address, BT_BR_EDR_TRANSPORT
3339
3283
  ) # TODO: timeout
3340
3284
 
3341
- assert isinstance(peer_address, Address)
3285
+ assert isinstance(peer_address, hci.Address)
3342
3286
 
3343
- if peer_address == Address.NIL:
3287
+ if peer_address == hci.Address.NIL:
3344
3288
  raise InvalidArgumentError('accept on nil address')
3345
3289
 
3346
3290
  # Create a future so that we can wait for the request
3347
3291
  pending_request_fut = asyncio.get_running_loop().create_future()
3348
3292
 
3349
- if peer_address == Address.ANY:
3350
- self.classic_pending_accepts[Address.ANY].append(pending_request_fut)
3293
+ if peer_address == hci.Address.ANY:
3294
+ self.classic_pending_accepts[hci.Address.ANY].append(pending_request_fut)
3351
3295
  elif peer_address in self.classic_pending_accepts:
3352
3296
  raise InvalidStateError('accept connection already pending')
3353
3297
  else:
@@ -3363,8 +3307,10 @@ class Device(CompositeEventEmitter):
3363
3307
  )
3364
3308
  except Exception:
3365
3309
  # Remove future from device context
3366
- if peer_address == Address.ANY:
3367
- self.classic_pending_accepts[Address.ANY].remove(pending_request_fut)
3310
+ if peer_address == hci.Address.ANY:
3311
+ self.classic_pending_accepts[hci.Address.ANY].remove(
3312
+ pending_request_fut
3313
+ )
3368
3314
  else:
3369
3315
  self.classic_pending_accepts.pop(peer_address)
3370
3316
  raise
@@ -3376,7 +3322,7 @@ class Device(CompositeEventEmitter):
3376
3322
 
3377
3323
  # Otherwise, result came from `on_connection_request`
3378
3324
  peer_address, _class_of_device, _link_type = result
3379
- assert isinstance(peer_address, Address)
3325
+ assert isinstance(peer_address, hci.Address)
3380
3326
 
3381
3327
  # Create a future so that we can wait for the connection's result
3382
3328
  pending_connection = asyncio.get_running_loop().create_future()
@@ -3399,7 +3345,7 @@ class Device(CompositeEventEmitter):
3399
3345
  self.on('connection_failure', on_connection_failure)
3400
3346
 
3401
3347
  # Save pending connection, with the Peripheral role.
3402
- # Even if we requested a role switch in the HCI_Accept_Connection_Request
3348
+ # Even if we requested a role switch in the hci.HCI_Accept_Connection_Request
3403
3349
  # command, this connection is still considered Peripheral until an eventual
3404
3350
  # role change event.
3405
3351
  self.pending_connections[peer_address] = Connection.incomplete(
@@ -3409,7 +3355,9 @@ class Device(CompositeEventEmitter):
3409
3355
  try:
3410
3356
  # Accept connection request
3411
3357
  await self.send_command(
3412
- HCI_Accept_Connection_Request_Command(bd_addr=peer_address, role=role)
3358
+ hci.HCI_Accept_Connection_Request_Command(
3359
+ bd_addr=peer_address, role=role
3360
+ )
3413
3361
  )
3414
3362
 
3415
3363
  # Wait for connection complete
@@ -3444,7 +3392,7 @@ class Device(CompositeEventEmitter):
3444
3392
  if not self.is_le_connecting:
3445
3393
  return
3446
3394
  await self.send_command(
3447
- HCI_LE_Create_Connection_Cancel_Command(), check_result=True
3395
+ hci.HCI_LE_Create_Connection_Cancel_Command(), check_result=True
3448
3396
  )
3449
3397
 
3450
3398
  # BR/EDR: try to cancel to ongoing connection
@@ -3453,7 +3401,7 @@ class Device(CompositeEventEmitter):
3453
3401
  else:
3454
3402
  if isinstance(peer_address, str):
3455
3403
  try:
3456
- peer_address = Address(peer_address)
3404
+ peer_address = hci.Address(peer_address)
3457
3405
  except InvalidArgumentError:
3458
3406
  # If the address is not parsable, assume it is a name instead
3459
3407
  logger.debug('looking for peer by name')
@@ -3462,7 +3410,7 @@ class Device(CompositeEventEmitter):
3462
3410
  ) # TODO: timeout
3463
3411
 
3464
3412
  await self.send_command(
3465
- HCI_Create_Connection_Cancel_Command(bd_addr=peer_address),
3413
+ hci.HCI_Create_Connection_Cancel_Command(bd_addr=peer_address),
3466
3414
  check_result=True,
3467
3415
  )
3468
3416
 
@@ -3476,12 +3424,14 @@ class Device(CompositeEventEmitter):
3476
3424
 
3477
3425
  # Request a disconnection
3478
3426
  result = await self.send_command(
3479
- HCI_Disconnect_Command(connection_handle=connection.handle, reason=reason)
3427
+ hci.HCI_Disconnect_Command(
3428
+ connection_handle=connection.handle, reason=reason
3429
+ )
3480
3430
  )
3481
3431
 
3482
3432
  try:
3483
- if result.status != HCI_Command_Status_Event.PENDING:
3484
- raise HCI_StatusError(result)
3433
+ if result.status != hci.HCI_Command_Status_Event.PENDING:
3434
+ raise hci.HCI_StatusError(result)
3485
3435
 
3486
3436
  # Wait for the disconnection process to complete
3487
3437
  self.disconnecting = True
@@ -3503,7 +3453,7 @@ class Device(CompositeEventEmitter):
3503
3453
  raise InvalidArgumentError('tx_time must be between 0x0148 and 0x4290')
3504
3454
 
3505
3455
  return await self.send_command(
3506
- HCI_LE_Set_Data_Length_Command(
3456
+ hci.HCI_LE_Set_Data_Length_Command(
3507
3457
  connection_handle=connection.handle,
3508
3458
  tx_octets=tx_octets,
3509
3459
  tx_time=tx_time,
@@ -3545,7 +3495,7 @@ class Device(CompositeEventEmitter):
3545
3495
  raise ConnectionParameterUpdateError(l2cap_result)
3546
3496
 
3547
3497
  result = await self.send_command(
3548
- HCI_LE_Connection_Update_Command(
3498
+ hci.HCI_LE_Connection_Update_Command(
3549
3499
  connection_handle=connection.handle,
3550
3500
  connection_interval_min=connection_interval_min,
3551
3501
  connection_interval_max=connection_interval_max,
@@ -3555,18 +3505,18 @@ class Device(CompositeEventEmitter):
3555
3505
  max_ce_length=max_ce_length,
3556
3506
  )
3557
3507
  )
3558
- if result.status != HCI_Command_Status_Event.PENDING:
3559
- raise HCI_StatusError(result)
3508
+ if result.status != hci.HCI_Command_Status_Event.PENDING:
3509
+ raise hci.HCI_StatusError(result)
3560
3510
 
3561
3511
  async def get_connection_rssi(self, connection):
3562
3512
  result = await self.send_command(
3563
- HCI_Read_RSSI_Command(handle=connection.handle), check_result=True
3513
+ hci.HCI_Read_RSSI_Command(handle=connection.handle), check_result=True
3564
3514
  )
3565
3515
  return result.return_parameters.rssi
3566
3516
 
3567
3517
  async def get_connection_phy(self, connection):
3568
3518
  result = await self.send_command(
3569
- HCI_LE_Read_PHY_Command(connection_handle=connection.handle),
3519
+ hci.HCI_LE_Read_PHY_Command(connection_handle=connection.handle),
3570
3520
  check_result=True,
3571
3521
  )
3572
3522
  return (result.return_parameters.tx_phy, result.return_parameters.rx_phy)
@@ -3574,7 +3524,7 @@ class Device(CompositeEventEmitter):
3574
3524
  async def set_connection_phy(
3575
3525
  self, connection, tx_phys=None, rx_phys=None, phy_options=None
3576
3526
  ):
3577
- if not self.host.supports_command(HCI_LE_SET_PHY_COMMAND):
3527
+ if not self.host.supports_command(hci.HCI_LE_SET_PHY_COMMAND):
3578
3528
  logger.warning('ignoring request, command not supported')
3579
3529
  return
3580
3530
 
@@ -3583,21 +3533,21 @@ class Device(CompositeEventEmitter):
3583
3533
  )
3584
3534
 
3585
3535
  result = await self.send_command(
3586
- HCI_LE_Set_PHY_Command(
3536
+ hci.HCI_LE_Set_PHY_Command(
3587
3537
  connection_handle=connection.handle,
3588
3538
  all_phys=all_phys_bits,
3589
- tx_phys=phy_list_to_bits(tx_phys),
3590
- rx_phys=phy_list_to_bits(rx_phys),
3539
+ tx_phys=hci.phy_list_to_bits(tx_phys),
3540
+ rx_phys=hci.phy_list_to_bits(rx_phys),
3591
3541
  phy_options=0 if phy_options is None else int(phy_options),
3592
3542
  )
3593
3543
  )
3594
3544
 
3595
- if result.status != HCI_COMMAND_STATUS_PENDING:
3545
+ if result.status != hci.HCI_COMMAND_STATUS_PENDING:
3596
3546
  logger.warning(
3597
3547
  'HCI_LE_Set_PHY_Command failed: '
3598
- f'{HCI_Constant.error_name(result.status)}'
3548
+ f'{hci.HCI_Constant.error_name(result.status)}'
3599
3549
  )
3600
- raise HCI_StatusError(result)
3550
+ raise hci.HCI_StatusError(result)
3601
3551
 
3602
3552
  async def set_default_phy(self, tx_phys=None, rx_phys=None):
3603
3553
  all_phys_bits = (1 if tx_phys is None else 0) | (
@@ -3605,10 +3555,10 @@ class Device(CompositeEventEmitter):
3605
3555
  )
3606
3556
 
3607
3557
  return await self.send_command(
3608
- HCI_LE_Set_Default_PHY_Command(
3558
+ hci.HCI_LE_Set_Default_PHY_Command(
3609
3559
  all_phys=all_phys_bits,
3610
- tx_phys=phy_list_to_bits(tx_phys),
3611
- rx_phys=phy_list_to_bits(rx_phys),
3560
+ tx_phys=hci.phy_list_to_bits(tx_phys),
3561
+ rx_phys=hci.phy_list_to_bits(rx_phys),
3612
3562
  ),
3613
3563
  check_result=True,
3614
3564
  )
@@ -3617,7 +3567,7 @@ class Device(CompositeEventEmitter):
3617
3567
  self, connection: Connection, sync_handle: int, service_data: int = 0
3618
3568
  ) -> None:
3619
3569
  return await self.send_command(
3620
- HCI_LE_Periodic_Advertising_Sync_Transfer_Command(
3570
+ hci.HCI_LE_Periodic_Advertising_Sync_Transfer_Command(
3621
3571
  connection_handle=connection.handle,
3622
3572
  service_data=service_data,
3623
3573
  sync_handle=sync_handle,
@@ -3681,7 +3631,9 @@ class Device(CompositeEventEmitter):
3681
3631
  elif transport == BT_BR_EDR_TRANSPORT and not was_discovering:
3682
3632
  await self.stop_discovery()
3683
3633
 
3684
- async def find_peer_by_identity_address(self, identity_address: Address) -> Address:
3634
+ async def find_peer_by_identity_address(
3635
+ self, identity_address: hci.Address
3636
+ ) -> hci.Address:
3685
3637
  """
3686
3638
  Scan for a peer with a resolvable address that can be resolved to a given
3687
3639
  identity address.
@@ -3777,7 +3729,7 @@ class Device(CompositeEventEmitter):
3777
3729
  return keys.ltk_peripheral.value
3778
3730
  return None
3779
3731
 
3780
- async def get_link_key(self, address: Address) -> Optional[bytes]:
3732
+ async def get_link_key(self, address: hci.Address) -> Optional[bytes]:
3781
3733
  if self.keystore is None:
3782
3734
  return None
3783
3735
 
@@ -3803,7 +3755,7 @@ class Device(CompositeEventEmitter):
3803
3755
  pending_authentication.set_result(None)
3804
3756
 
3805
3757
  def on_authentication_failure(error_code):
3806
- pending_authentication.set_exception(HCI_Error(error_code))
3758
+ pending_authentication.set_exception(hci.HCI_Error(error_code))
3807
3759
 
3808
3760
  connection.on('connection_authentication', on_authentication)
3809
3761
  connection.on('connection_authentication_failure', on_authentication_failure)
@@ -3811,16 +3763,16 @@ class Device(CompositeEventEmitter):
3811
3763
  # Request the authentication
3812
3764
  try:
3813
3765
  result = await self.send_command(
3814
- HCI_Authentication_Requested_Command(
3766
+ hci.HCI_Authentication_Requested_Command(
3815
3767
  connection_handle=connection.handle
3816
3768
  )
3817
3769
  )
3818
- if result.status != HCI_COMMAND_STATUS_PENDING:
3770
+ if result.status != hci.HCI_COMMAND_STATUS_PENDING:
3819
3771
  logger.warning(
3820
3772
  'HCI_Authentication_Requested_Command failed: '
3821
- f'{HCI_Constant.error_name(result.status)}'
3773
+ f'{hci.HCI_Constant.error_name(result.status)}'
3822
3774
  )
3823
- raise HCI_StatusError(result)
3775
+ raise hci.HCI_StatusError(result)
3824
3776
 
3825
3777
  # Wait for the authentication to complete
3826
3778
  await connection.abort_on('disconnection', pending_authentication)
@@ -3841,7 +3793,7 @@ class Device(CompositeEventEmitter):
3841
3793
  pending_encryption.set_result(None)
3842
3794
 
3843
3795
  def on_encryption_failure(error_code):
3844
- pending_encryption.set_exception(HCI_Error(error_code))
3796
+ pending_encryption.set_exception(hci.HCI_Error(error_code))
3845
3797
 
3846
3798
  connection.on('connection_encryption_change', on_encryption_change)
3847
3799
  connection.on('connection_encryption_failure', on_encryption_failure)
@@ -3869,11 +3821,11 @@ class Device(CompositeEventEmitter):
3869
3821
  else:
3870
3822
  raise InvalidOperationError('no LTK found for peer')
3871
3823
 
3872
- if connection.role != HCI_CENTRAL_ROLE:
3824
+ if connection.role != hci.HCI_CENTRAL_ROLE:
3873
3825
  raise InvalidStateError('only centrals can start encryption')
3874
3826
 
3875
3827
  result = await self.send_command(
3876
- HCI_LE_Enable_Encryption_Command(
3828
+ hci.HCI_LE_Enable_Encryption_Command(
3877
3829
  connection_handle=connection.handle,
3878
3830
  random_number=rand,
3879
3831
  encrypted_diversifier=ediv,
@@ -3881,26 +3833,26 @@ class Device(CompositeEventEmitter):
3881
3833
  )
3882
3834
  )
3883
3835
 
3884
- if result.status != HCI_COMMAND_STATUS_PENDING:
3836
+ if result.status != hci.HCI_COMMAND_STATUS_PENDING:
3885
3837
  logger.warning(
3886
3838
  'HCI_LE_Enable_Encryption_Command failed: '
3887
- f'{HCI_Constant.error_name(result.status)}'
3839
+ f'{hci.HCI_Constant.error_name(result.status)}'
3888
3840
  )
3889
- raise HCI_StatusError(result)
3841
+ raise hci.HCI_StatusError(result)
3890
3842
  else:
3891
3843
  result = await self.send_command(
3892
- HCI_Set_Connection_Encryption_Command(
3844
+ hci.HCI_Set_Connection_Encryption_Command(
3893
3845
  connection_handle=connection.handle,
3894
3846
  encryption_enable=0x01 if enable else 0x00,
3895
3847
  )
3896
3848
  )
3897
3849
 
3898
- if result.status != HCI_COMMAND_STATUS_PENDING:
3850
+ if result.status != hci.HCI_COMMAND_STATUS_PENDING:
3899
3851
  logger.warning(
3900
3852
  'HCI_Set_Connection_Encryption_Command failed: '
3901
- f'{HCI_Constant.error_name(result.status)}'
3853
+ f'{hci.HCI_Constant.error_name(result.status)}'
3902
3854
  )
3903
- raise HCI_StatusError(result)
3855
+ raise hci.HCI_StatusError(result)
3904
3856
 
3905
3857
  # Wait for the result
3906
3858
  await connection.abort_on('disconnection', pending_encryption)
@@ -3932,32 +3884,34 @@ class Device(CompositeEventEmitter):
3932
3884
  pending_role_change.set_result(new_role)
3933
3885
 
3934
3886
  def on_role_change_failure(error_code):
3935
- pending_role_change.set_exception(HCI_Error(error_code))
3887
+ pending_role_change.set_exception(hci.HCI_Error(error_code))
3936
3888
 
3937
3889
  connection.on('role_change', on_role_change)
3938
3890
  connection.on('role_change_failure', on_role_change_failure)
3939
3891
 
3940
3892
  try:
3941
3893
  result = await self.send_command(
3942
- HCI_Switch_Role_Command(bd_addr=connection.peer_address, role=role)
3894
+ hci.HCI_Switch_Role_Command(bd_addr=connection.peer_address, role=role)
3943
3895
  )
3944
- if result.status != HCI_COMMAND_STATUS_PENDING:
3896
+ if result.status != hci.HCI_COMMAND_STATUS_PENDING:
3945
3897
  logger.warning(
3946
3898
  'HCI_Switch_Role_Command failed: '
3947
- f'{HCI_Constant.error_name(result.status)}'
3899
+ f'{hci.HCI_Constant.error_name(result.status)}'
3948
3900
  )
3949
- raise HCI_StatusError(result)
3901
+ raise hci.HCI_StatusError(result)
3950
3902
  await connection.abort_on('disconnection', pending_role_change)
3951
3903
  finally:
3952
3904
  connection.remove_listener('role_change', on_role_change)
3953
3905
  connection.remove_listener('role_change_failure', on_role_change_failure)
3954
3906
 
3955
3907
  # [Classic only]
3956
- async def request_remote_name(self, remote: Union[Address, Connection]) -> str:
3908
+ async def request_remote_name(self, remote: Union[hci.Address, Connection]) -> str:
3957
3909
  # Set up event handlers
3958
3910
  pending_name = asyncio.get_running_loop().create_future()
3959
3911
 
3960
- peer_address = remote if isinstance(remote, Address) else remote.peer_address
3912
+ peer_address = (
3913
+ remote if isinstance(remote, hci.Address) else remote.peer_address
3914
+ )
3961
3915
 
3962
3916
  handler = self.on(
3963
3917
  'remote_name',
@@ -3970,7 +3924,7 @@ class Device(CompositeEventEmitter):
3970
3924
  failure_handler = self.on(
3971
3925
  'remote_name_failure',
3972
3926
  lambda address, error_code: (
3973
- pending_name.set_exception(HCI_Error(error_code))
3927
+ pending_name.set_exception(hci.HCI_Error(error_code))
3974
3928
  if address == peer_address
3975
3929
  else None
3976
3930
  ),
@@ -3978,20 +3932,20 @@ class Device(CompositeEventEmitter):
3978
3932
 
3979
3933
  try:
3980
3934
  result = await self.send_command(
3981
- HCI_Remote_Name_Request_Command(
3935
+ hci.HCI_Remote_Name_Request_Command(
3982
3936
  bd_addr=peer_address,
3983
- page_scan_repetition_mode=HCI_Remote_Name_Request_Command.R2,
3937
+ page_scan_repetition_mode=hci.HCI_Remote_Name_Request_Command.R2,
3984
3938
  reserved=0,
3985
3939
  clock_offset=0, # TODO investigate non-0 values
3986
3940
  )
3987
3941
  )
3988
3942
 
3989
- if result.status != HCI_COMMAND_STATUS_PENDING:
3943
+ if result.status != hci.HCI_COMMAND_STATUS_PENDING:
3990
3944
  logger.warning(
3991
3945
  'HCI_Remote_Name_Request_Command failed: '
3992
- f'{HCI_Constant.error_name(result.status)}'
3946
+ f'{hci.HCI_Constant.error_name(result.status)}'
3993
3947
  )
3994
- raise HCI_StatusError(result)
3948
+ raise hci.HCI_StatusError(result)
3995
3949
 
3996
3950
  # Wait for the result
3997
3951
  return await self.abort_on('flush', pending_name)
@@ -4011,7 +3965,7 @@ class Device(CompositeEventEmitter):
4011
3965
  retransmission_number: int,
4012
3966
  max_transport_latency: Tuple[int, int],
4013
3967
  ) -> List[int]:
4014
- """Sends HCI_LE_Set_CIG_Parameters_Command.
3968
+ """Sends hci.HCI_LE_Set_CIG_Parameters_Command.
4015
3969
 
4016
3970
  Args:
4017
3971
  cig_id: CIG_ID.
@@ -4029,7 +3983,7 @@ class Device(CompositeEventEmitter):
4029
3983
  num_cis = len(cis_id)
4030
3984
 
4031
3985
  response = await self.send_command(
4032
- HCI_LE_Set_CIG_Parameters_Command(
3986
+ hci.HCI_LE_Set_CIG_Parameters_Command(
4033
3987
  cig_id=cig_id,
4034
3988
  sdu_interval_c_to_p=sdu_interval[0],
4035
3989
  sdu_interval_p_to_c=sdu_interval[1],
@@ -4041,8 +3995,8 @@ class Device(CompositeEventEmitter):
4041
3995
  cis_id=cis_id,
4042
3996
  max_sdu_c_to_p=[max_sdu[0]] * num_cis,
4043
3997
  max_sdu_p_to_c=[max_sdu[1]] * num_cis,
4044
- phy_c_to_p=[HCI_LE_2M_PHY] * num_cis,
4045
- phy_p_to_c=[HCI_LE_2M_PHY] * num_cis,
3998
+ phy_c_to_p=[hci.HCI_LE_2M_PHY] * num_cis,
3999
+ phy_p_to_c=[hci.HCI_LE_2M_PHY] * num_cis,
4046
4000
  rtn_c_to_p=[retransmission_number] * num_cis,
4047
4001
  rtn_p_to_c=[retransmission_number] * num_cis,
4048
4002
  ),
@@ -4084,12 +4038,12 @@ class Device(CompositeEventEmitter):
4084
4038
 
4085
4039
  def on_cis_establishment_failure(cis_handle: int, status: int) -> None:
4086
4040
  if pending_future := pending_cis_establishments.get(cis_handle):
4087
- pending_future.set_exception(HCI_Error(status))
4041
+ pending_future.set_exception(hci.HCI_Error(status))
4088
4042
 
4089
4043
  watcher.on(self, 'cis_establishment', on_cis_establishment)
4090
4044
  watcher.on(self, 'cis_establishment_failure', on_cis_establishment_failure)
4091
4045
  await self.send_command(
4092
- HCI_LE_Create_CIS_Command(
4046
+ hci.HCI_LE_Create_CIS_Command(
4093
4047
  cis_connection_handle=[p[0] for p in cis_acl_pairs],
4094
4048
  acl_connection_handle=[p[1] for p in cis_acl_pairs],
4095
4049
  ),
@@ -4128,13 +4082,13 @@ class Device(CompositeEventEmitter):
4128
4082
  pending_establishment.set_result(None)
4129
4083
 
4130
4084
  def on_establishment_failure(status: int) -> None:
4131
- pending_establishment.set_exception(HCI_Error(status))
4085
+ pending_establishment.set_exception(hci.HCI_Error(status))
4132
4086
 
4133
4087
  watcher.on(cis_link, 'establishment', on_establishment)
4134
4088
  watcher.on(cis_link, 'establishment_failure', on_establishment_failure)
4135
4089
 
4136
4090
  await self.send_command(
4137
- HCI_LE_Accept_CIS_Request_Command(connection_handle=handle),
4091
+ hci.HCI_LE_Accept_CIS_Request_Command(connection_handle=handle),
4138
4092
  check_result=True,
4139
4093
  )
4140
4094
 
@@ -4149,14 +4103,16 @@ class Device(CompositeEventEmitter):
4149
4103
  async def reject_cis_request(
4150
4104
  self,
4151
4105
  handle: int,
4152
- reason: int = HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR,
4106
+ reason: int = hci.HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR,
4153
4107
  ) -> None:
4154
4108
  await self.send_command(
4155
- HCI_LE_Reject_CIS_Request_Command(connection_handle=handle, reason=reason),
4109
+ hci.HCI_LE_Reject_CIS_Request_Command(
4110
+ connection_handle=handle, reason=reason
4111
+ ),
4156
4112
  check_result=True,
4157
4113
  )
4158
4114
 
4159
- async def get_remote_le_features(self, connection: Connection) -> LeFeatureMask:
4115
+ async def get_remote_le_features(self, connection: Connection) -> hci.LeFeatureMask:
4160
4116
  """[LE Only] Reads remote LE supported features.
4161
4117
 
4162
4118
  Args:
@@ -4166,22 +4122,22 @@ class Device(CompositeEventEmitter):
4166
4122
  LE features supported by the remote device.
4167
4123
  """
4168
4124
  with closing(EventWatcher()) as watcher:
4169
- read_feature_future: asyncio.Future[LeFeatureMask] = (
4125
+ read_feature_future: asyncio.Future[hci.LeFeatureMask] = (
4170
4126
  asyncio.get_running_loop().create_future()
4171
4127
  )
4172
4128
 
4173
4129
  def on_le_remote_features(handle: int, features: int):
4174
4130
  if handle == connection.handle:
4175
- read_feature_future.set_result(LeFeatureMask(features))
4131
+ read_feature_future.set_result(hci.LeFeatureMask(features))
4176
4132
 
4177
4133
  def on_failure(handle: int, status: int):
4178
4134
  if handle == connection.handle:
4179
- read_feature_future.set_exception(HCI_Error(status))
4135
+ read_feature_future.set_exception(hci.HCI_Error(status))
4180
4136
 
4181
4137
  watcher.on(self.host, 'le_remote_features', on_le_remote_features)
4182
4138
  watcher.on(self.host, 'le_remote_features_failure', on_failure)
4183
4139
  await self.send_command(
4184
- HCI_LE_Read_Remote_Features_Command(
4140
+ hci.HCI_LE_Read_Remote_Features_Command(
4185
4141
  connection_handle=connection.handle
4186
4142
  ),
4187
4143
  check_result=True,
@@ -4201,8 +4157,8 @@ class Device(CompositeEventEmitter):
4201
4157
  # Store the keys in the key store
4202
4158
  if self.keystore:
4203
4159
  authenticated = key_type in (
4204
- HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_192_TYPE,
4205
- HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_256_TYPE,
4160
+ hci.HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_192_TYPE,
4161
+ hci.HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_256_TYPE,
4206
4162
  )
4207
4163
  pairing_keys = PairingKeys()
4208
4164
  pairing_keys.link_key = PairingKeys.Key(
@@ -4256,7 +4212,7 @@ class Device(CompositeEventEmitter):
4256
4212
 
4257
4213
  advertising_set.on_termination(status)
4258
4214
 
4259
- if status != HCI_SUCCESS:
4215
+ if status != hci.HCI_SUCCESS:
4260
4216
  logger.debug(
4261
4217
  f'advertising set {advertising_handle} '
4262
4218
  f'terminated with status {status}'
@@ -4285,13 +4241,13 @@ class Device(CompositeEventEmitter):
4285
4241
  advertising_set.random_address
4286
4242
  if advertising_set.random_address is not None
4287
4243
  and advertising_set.advertising_parameters.own_address_type
4288
- in (OwnAddressType.RANDOM, OwnAddressType.RESOLVABLE_OR_RANDOM)
4244
+ in (hci.OwnAddressType.RANDOM, hci.OwnAddressType.RESOLVABLE_OR_RANDOM)
4289
4245
  else self.public_address
4290
4246
  )
4291
4247
 
4292
4248
  if advertising_set.advertising_parameters.own_address_type in (
4293
- OwnAddressType.RANDOM,
4294
- OwnAddressType.PUBLIC,
4249
+ hci.OwnAddressType.RANDOM,
4250
+ hci.OwnAddressType.PUBLIC,
4295
4251
  ):
4296
4252
  connection.self_resolvable_address = None
4297
4253
 
@@ -4307,11 +4263,11 @@ class Device(CompositeEventEmitter):
4307
4263
  def _emit_le_connection(self, connection: Connection) -> None:
4308
4264
  # If supported, read which PHY we're connected with before
4309
4265
  # notifying listeners of the new connection.
4310
- if self.host.supports_command(HCI_LE_READ_PHY_COMMAND):
4266
+ if self.host.supports_command(hci.HCI_LE_READ_PHY_COMMAND):
4311
4267
 
4312
4268
  async def read_phy():
4313
4269
  result = await self.send_command(
4314
- HCI_LE_Read_PHY_Command(connection_handle=connection.handle),
4270
+ hci.HCI_LE_Read_PHY_Command(connection_handle=connection.handle),
4315
4271
  check_result=True,
4316
4272
  )
4317
4273
  connection.phy = ConnectionPHY(
@@ -4332,24 +4288,24 @@ class Device(CompositeEventEmitter):
4332
4288
  self,
4333
4289
  connection_handle: int,
4334
4290
  transport: int,
4335
- peer_address: Address,
4336
- self_resolvable_address: Optional[Address],
4337
- peer_resolvable_address: Optional[Address],
4291
+ peer_address: hci.Address,
4292
+ self_resolvable_address: Optional[hci.Address],
4293
+ peer_resolvable_address: Optional[hci.Address],
4338
4294
  role: int,
4339
4295
  connection_parameters: ConnectionParameters,
4340
4296
  ) -> None:
4341
4297
  # Convert all-zeros addresses into None.
4342
- if self_resolvable_address == Address.ANY_RANDOM:
4298
+ if self_resolvable_address == hci.Address.ANY_RANDOM:
4343
4299
  self_resolvable_address = None
4344
4300
  if (
4345
- peer_resolvable_address == Address.ANY_RANDOM
4301
+ peer_resolvable_address == hci.Address.ANY_RANDOM
4346
4302
  or not peer_address.is_resolved
4347
4303
  ):
4348
4304
  peer_resolvable_address = None
4349
4305
 
4350
4306
  logger.debug(
4351
4307
  f'*** Connection: [0x{connection_handle:04X}] '
4352
- f'{peer_address} {"" if role is None else HCI_Constant.role_name(role)}'
4308
+ f'{peer_address} {"" if role is None else hci.HCI_Constant.role_name(role)}'
4353
4309
  )
4354
4310
  if connection_handle in self.connections:
4355
4311
  logger.warning(
@@ -4373,20 +4329,20 @@ class Device(CompositeEventEmitter):
4373
4329
  if peer_address.is_resolvable:
4374
4330
  resolved_address = self.address_resolver.resolve(peer_address)
4375
4331
  if resolved_address is not None:
4376
- logger.debug(f'*** Address resolved as {resolved_address}')
4332
+ logger.debug(f'*** hci.Address resolved as {resolved_address}')
4377
4333
  peer_resolvable_address = peer_address
4378
4334
  peer_address = resolved_address
4379
4335
 
4380
4336
  self_address = None
4381
4337
  own_address_type: Optional[int] = None
4382
- if role == HCI_CENTRAL_ROLE:
4338
+ if role == hci.HCI_CENTRAL_ROLE:
4383
4339
  own_address_type = self.connect_own_address_type
4384
4340
  assert own_address_type is not None
4385
4341
  else:
4386
4342
  if self.supports_le_extended_advertising:
4387
4343
  # We'll know the address when the advertising set terminates,
4388
4344
  # Use a temporary placeholder value for self_address.
4389
- self_address = Address.ANY_RANDOM
4345
+ self_address = hci.Address.ANY_RANDOM
4390
4346
  else:
4391
4347
  # We were connected via a legacy advertisement.
4392
4348
  if self.legacy_advertiser:
@@ -4401,15 +4357,15 @@ class Device(CompositeEventEmitter):
4401
4357
  self.public_address
4402
4358
  if own_address_type
4403
4359
  in (
4404
- OwnAddressType.PUBLIC,
4405
- OwnAddressType.RESOLVABLE_OR_PUBLIC,
4360
+ hci.OwnAddressType.PUBLIC,
4361
+ hci.OwnAddressType.RESOLVABLE_OR_PUBLIC,
4406
4362
  )
4407
4363
  else self.random_address
4408
4364
  )
4409
4365
 
4410
4366
  # Some controllers may return local resolvable address even not using address
4411
4367
  # generation offloading. Ignore the value to prevent SMP failure.
4412
- if own_address_type in (OwnAddressType.RANDOM, OwnAddressType.PUBLIC):
4368
+ if own_address_type in (hci.OwnAddressType.RANDOM, hci.OwnAddressType.PUBLIC):
4413
4369
  self_resolvable_address = None
4414
4370
 
4415
4371
  # Create a connection.
@@ -4423,11 +4379,11 @@ class Device(CompositeEventEmitter):
4423
4379
  peer_resolvable_address,
4424
4380
  role,
4425
4381
  connection_parameters,
4426
- ConnectionPHY(HCI_LE_1M_PHY, HCI_LE_1M_PHY),
4382
+ ConnectionPHY(hci.HCI_LE_1M_PHY, hci.HCI_LE_1M_PHY),
4427
4383
  )
4428
4384
  self.connections[connection_handle] = connection
4429
4385
 
4430
- if role == HCI_PERIPHERAL_ROLE and self.legacy_advertiser:
4386
+ if role == hci.HCI_PERIPHERAL_ROLE and self.legacy_advertiser:
4431
4387
  if self.legacy_advertiser.auto_restart:
4432
4388
  advertiser = self.legacy_advertiser
4433
4389
  connection.once(
@@ -4437,12 +4393,12 @@ class Device(CompositeEventEmitter):
4437
4393
  else:
4438
4394
  self.legacy_advertiser = None
4439
4395
 
4440
- if role == HCI_CENTRAL_ROLE or not self.supports_le_extended_advertising:
4396
+ if role == hci.HCI_CENTRAL_ROLE or not self.supports_le_extended_advertising:
4441
4397
  # We can emit now, we have all the info we need
4442
4398
  self._emit_le_connection(connection)
4443
4399
  return
4444
4400
 
4445
- if role == HCI_PERIPHERAL_ROLE and self.supports_le_extended_advertising:
4401
+ if role == hci.HCI_PERIPHERAL_ROLE and self.supports_le_extended_advertising:
4446
4402
  if advertising_set := self.connecting_extended_advertising_sets.pop(
4447
4403
  connection_handle, None
4448
4404
  ):
@@ -4453,7 +4409,9 @@ class Device(CompositeEventEmitter):
4453
4409
 
4454
4410
  @host_event_handler
4455
4411
  def on_connection_failure(self, transport, peer_address, error_code):
4456
- logger.debug(f'*** Connection failed: {HCI_Constant.error_name(error_code)}')
4412
+ logger.debug(
4413
+ f'*** Connection failed: {hci.HCI_Constant.error_name(error_code)}'
4414
+ )
4457
4415
 
4458
4416
  # For directed advertising, this means a timeout
4459
4417
  if (
@@ -4469,7 +4427,7 @@ class Device(CompositeEventEmitter):
4469
4427
  transport,
4470
4428
  peer_address,
4471
4429
  'hci',
4472
- HCI_Constant.error_name(error_code),
4430
+ hci.HCI_Constant.error_name(error_code),
4473
4431
  )
4474
4432
  self.emit('connection_failure', error)
4475
4433
 
@@ -4480,8 +4438,8 @@ class Device(CompositeEventEmitter):
4480
4438
 
4481
4439
  # Handle SCO request.
4482
4440
  if link_type in (
4483
- HCI_Connection_Complete_Event.SCO_LINK_TYPE,
4484
- HCI_Connection_Complete_Event.ESCO_LINK_TYPE,
4441
+ hci.HCI_Connection_Complete_Event.SCO_LINK_TYPE,
4442
+ hci.HCI_Connection_Complete_Event.ESCO_LINK_TYPE,
4485
4443
  ):
4486
4444
  if connection := self.find_connection_by_bd_addr(
4487
4445
  bd_addr, transport=BT_BR_EDR_TRANSPORT
@@ -4497,8 +4455,8 @@ class Device(CompositeEventEmitter):
4497
4455
  future.set_result((bd_addr, class_of_device, link_type))
4498
4456
 
4499
4457
  # match first pending future for ANY address
4500
- elif len(self.classic_pending_accepts[Address.ANY]) > 0:
4501
- future = self.classic_pending_accepts[Address.ANY].pop(0)
4458
+ elif len(self.classic_pending_accepts[hci.Address.ANY]) > 0:
4459
+ future = self.classic_pending_accepts[hci.Address.ANY].pop(0)
4502
4460
  future.set_result((bd_addr, class_of_device, link_type))
4503
4461
 
4504
4462
  # device configuration is set to accept any incoming connection
@@ -4509,7 +4467,7 @@ class Device(CompositeEventEmitter):
4509
4467
  )
4510
4468
 
4511
4469
  self.host.send_command_sync(
4512
- HCI_Accept_Connection_Request_Command(
4470
+ hci.HCI_Accept_Connection_Request_Command(
4513
4471
  bd_addr=bd_addr, role=0x01 # Remain the peripheral
4514
4472
  )
4515
4473
  )
@@ -4517,9 +4475,9 @@ class Device(CompositeEventEmitter):
4517
4475
  # reject incoming connection
4518
4476
  else:
4519
4477
  self.host.send_command_sync(
4520
- HCI_Reject_Connection_Request_Command(
4478
+ hci.HCI_Reject_Connection_Request_Command(
4521
4479
  bd_addr=bd_addr,
4522
- reason=HCI_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES_ERROR,
4480
+ reason=hci.HCI_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES_ERROR,
4523
4481
  )
4524
4482
  )
4525
4483
 
@@ -4552,7 +4510,7 @@ class Device(CompositeEventEmitter):
4552
4510
  connection.transport,
4553
4511
  connection.peer_address,
4554
4512
  'hci',
4555
- HCI_Constant.error_name(error_code),
4513
+ hci.HCI_Constant.error_name(error_code),
4556
4514
  )
4557
4515
  connection.emit('disconnection_failure', error)
4558
4516
 
@@ -4597,19 +4555,19 @@ class Device(CompositeEventEmitter):
4597
4555
  authentication_requirements = (
4598
4556
  # No Bonding
4599
4557
  (
4600
- HCI_MITM_NOT_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS,
4601
- HCI_MITM_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS,
4558
+ hci.HCI_MITM_NOT_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS,
4559
+ hci.HCI_MITM_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS,
4602
4560
  ),
4603
4561
  # General Bonding
4604
4562
  (
4605
- HCI_MITM_NOT_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS,
4606
- HCI_MITM_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS,
4563
+ hci.HCI_MITM_NOT_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS,
4564
+ hci.HCI_MITM_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS,
4607
4565
  ),
4608
4566
  )[1 if pairing_config.bonding else 0][1 if pairing_config.mitm else 0]
4609
4567
 
4610
4568
  # Respond
4611
4569
  self.host.send_command_sync(
4612
- HCI_IO_Capability_Request_Reply_Command(
4570
+ hci.HCI_IO_Capability_Request_Reply_Command(
4613
4571
  bd_addr=connection.peer_address,
4614
4572
  io_capability=pairing_config.delegate.classic_io_capability,
4615
4573
  oob_data_present=0x00, # Not present
@@ -4659,29 +4617,29 @@ class Device(CompositeEventEmitter):
4659
4617
 
4660
4618
  # See Bluetooth spec @ Vol 3, Part C 5.2.2.6
4661
4619
  methods = {
4662
- HCI_DISPLAY_ONLY_IO_CAPABILITY: {
4663
- HCI_DISPLAY_ONLY_IO_CAPABILITY: display_auto_confirm,
4664
- HCI_DISPLAY_YES_NO_IO_CAPABILITY: display_confirm,
4665
- HCI_KEYBOARD_ONLY_IO_CAPABILITY: na,
4666
- HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: auto_confirm,
4620
+ hci.HCI_DISPLAY_ONLY_IO_CAPABILITY: {
4621
+ hci.HCI_DISPLAY_ONLY_IO_CAPABILITY: display_auto_confirm,
4622
+ hci.HCI_DISPLAY_YES_NO_IO_CAPABILITY: display_confirm,
4623
+ hci.HCI_KEYBOARD_ONLY_IO_CAPABILITY: na,
4624
+ hci.HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: auto_confirm,
4667
4625
  },
4668
- HCI_DISPLAY_YES_NO_IO_CAPABILITY: {
4669
- HCI_DISPLAY_ONLY_IO_CAPABILITY: display_auto_confirm,
4670
- HCI_DISPLAY_YES_NO_IO_CAPABILITY: display_confirm,
4671
- HCI_KEYBOARD_ONLY_IO_CAPABILITY: na,
4672
- HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: auto_confirm,
4626
+ hci.HCI_DISPLAY_YES_NO_IO_CAPABILITY: {
4627
+ hci.HCI_DISPLAY_ONLY_IO_CAPABILITY: display_auto_confirm,
4628
+ hci.HCI_DISPLAY_YES_NO_IO_CAPABILITY: display_confirm,
4629
+ hci.HCI_KEYBOARD_ONLY_IO_CAPABILITY: na,
4630
+ hci.HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: auto_confirm,
4673
4631
  },
4674
- HCI_KEYBOARD_ONLY_IO_CAPABILITY: {
4675
- HCI_DISPLAY_ONLY_IO_CAPABILITY: na,
4676
- HCI_DISPLAY_YES_NO_IO_CAPABILITY: na,
4677
- HCI_KEYBOARD_ONLY_IO_CAPABILITY: na,
4678
- HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: auto_confirm,
4632
+ hci.HCI_KEYBOARD_ONLY_IO_CAPABILITY: {
4633
+ hci.HCI_DISPLAY_ONLY_IO_CAPABILITY: na,
4634
+ hci.HCI_DISPLAY_YES_NO_IO_CAPABILITY: na,
4635
+ hci.HCI_KEYBOARD_ONLY_IO_CAPABILITY: na,
4636
+ hci.HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: auto_confirm,
4679
4637
  },
4680
- HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: {
4681
- HCI_DISPLAY_ONLY_IO_CAPABILITY: confirm,
4682
- HCI_DISPLAY_YES_NO_IO_CAPABILITY: confirm,
4683
- HCI_KEYBOARD_ONLY_IO_CAPABILITY: auto_confirm,
4684
- HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: auto_confirm,
4638
+ hci.HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: {
4639
+ hci.HCI_DISPLAY_ONLY_IO_CAPABILITY: confirm,
4640
+ hci.HCI_DISPLAY_YES_NO_IO_CAPABILITY: confirm,
4641
+ hci.HCI_KEYBOARD_ONLY_IO_CAPABILITY: auto_confirm,
4642
+ hci.HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: auto_confirm,
4685
4643
  },
4686
4644
  }
4687
4645
 
@@ -4691,7 +4649,7 @@ class Device(CompositeEventEmitter):
4691
4649
  try:
4692
4650
  if await connection.abort_on('disconnection', method()):
4693
4651
  await self.host.send_command(
4694
- HCI_User_Confirmation_Request_Reply_Command(
4652
+ hci.HCI_User_Confirmation_Request_Reply_Command(
4695
4653
  bd_addr=connection.peer_address
4696
4654
  )
4697
4655
  )
@@ -4700,7 +4658,7 @@ class Device(CompositeEventEmitter):
4700
4658
  logger.warning(f'exception while confirming: {error}')
4701
4659
 
4702
4660
  await self.host.send_command(
4703
- HCI_User_Confirmation_Request_Negative_Reply_Command(
4661
+ hci.HCI_User_Confirmation_Request_Negative_Reply_Command(
4704
4662
  bd_addr=connection.peer_address
4705
4663
  )
4706
4664
  )
@@ -4721,7 +4679,7 @@ class Device(CompositeEventEmitter):
4721
4679
  )
4722
4680
  if number is not None:
4723
4681
  await self.host.send_command(
4724
- HCI_User_Passkey_Request_Reply_Command(
4682
+ hci.HCI_User_Passkey_Request_Reply_Command(
4725
4683
  bd_addr=connection.peer_address, numeric_value=number
4726
4684
  )
4727
4685
  )
@@ -4730,7 +4688,7 @@ class Device(CompositeEventEmitter):
4730
4688
  logger.warning(f'exception while asking for pass-key: {error}')
4731
4689
 
4732
4690
  await self.host.send_command(
4733
- HCI_User_Passkey_Request_Negative_Reply_Command(
4691
+ hci.HCI_User_Passkey_Request_Negative_Reply_Command(
4734
4692
  bd_addr=connection.peer_address
4735
4693
  )
4736
4694
  )
@@ -4747,7 +4705,7 @@ class Device(CompositeEventEmitter):
4747
4705
  io_capability = pairing_config.delegate.classic_io_capability
4748
4706
 
4749
4707
  # Respond
4750
- if io_capability == HCI_KEYBOARD_ONLY_IO_CAPABILITY:
4708
+ if io_capability == hci.HCI_KEYBOARD_ONLY_IO_CAPABILITY:
4751
4709
  # Ask the user to enter a string
4752
4710
  async def get_pin_code():
4753
4711
  pin_code = await connection.abort_on(
@@ -4759,7 +4717,7 @@ class Device(CompositeEventEmitter):
4759
4717
  pin_code_len = len(pin_code)
4760
4718
  assert 0 < pin_code_len <= 16, "pin_code should be 1-16 bytes"
4761
4719
  await self.host.send_command(
4762
- HCI_PIN_Code_Request_Reply_Command(
4720
+ hci.HCI_PIN_Code_Request_Reply_Command(
4763
4721
  bd_addr=connection.peer_address,
4764
4722
  pin_code_length=pin_code_len,
4765
4723
  pin_code=pin_code,
@@ -4768,7 +4726,7 @@ class Device(CompositeEventEmitter):
4768
4726
  else:
4769
4727
  logger.debug("delegate.get_string() returned None")
4770
4728
  await self.host.send_command(
4771
- HCI_PIN_Code_Request_Negative_Reply_Command(
4729
+ hci.HCI_PIN_Code_Request_Negative_Reply_Command(
4772
4730
  bd_addr=connection.peer_address
4773
4731
  )
4774
4732
  )
@@ -4776,7 +4734,7 @@ class Device(CompositeEventEmitter):
4776
4734
  asyncio.create_task(get_pin_code())
4777
4735
  else:
4778
4736
  self.host.send_command_sync(
4779
- HCI_PIN_Code_Request_Negative_Reply_Command(
4737
+ hci.HCI_PIN_Code_Request_Negative_Reply_Command(
4780
4738
  bd_addr=connection.peer_address
4781
4739
  )
4782
4740
  )
@@ -4852,7 +4810,9 @@ class Device(CompositeEventEmitter):
4852
4810
  # [Classic only]
4853
4811
  @host_event_handler
4854
4812
  @experimental('Only for testing')
4855
- def on_sco_packet(self, sco_handle: int, packet: HCI_SynchronousDataPacket) -> None:
4813
+ def on_sco_packet(
4814
+ self, sco_handle: int, packet: hci.HCI_SynchronousDataPacket
4815
+ ) -> None:
4856
4816
  if (sco_link := self.sco_links.get(sco_handle)) and sco_link.sink:
4857
4817
  sco_link.sink(packet)
4858
4818
 
@@ -4916,7 +4876,7 @@ class Device(CompositeEventEmitter):
4916
4876
  # [LE only]
4917
4877
  @host_event_handler
4918
4878
  @experimental('Only for testing')
4919
- def on_iso_packet(self, handle: int, packet: HCI_IsoDataPacket) -> None:
4879
+ def on_iso_packet(self, handle: int, packet: hci.HCI_IsoDataPacket) -> None:
4920
4880
  if (cis_link := self.cis_links.get(handle)) and cis_link.sink:
4921
4881
  cis_link.sink(packet)
4922
4882
 
@@ -4932,14 +4892,14 @@ class Device(CompositeEventEmitter):
4932
4892
  if (
4933
4893
  not connection.authenticated
4934
4894
  and connection.transport == BT_BR_EDR_TRANSPORT
4935
- and encryption == HCI_Encryption_Change_Event.AES_CCM
4895
+ and encryption == hci.HCI_Encryption_Change_Event.AES_CCM
4936
4896
  ):
4937
4897
  connection.authenticated = True
4938
4898
  connection.sc = True
4939
4899
  if (
4940
4900
  not connection.authenticated
4941
4901
  and connection.transport == BT_LE_TRANSPORT
4942
- and encryption == HCI_Encryption_Change_Event.E0_OR_AES_CCM
4902
+ and encryption == hci.HCI_Encryption_Change_Event.E0_OR_AES_CCM
4943
4903
  ):
4944
4904
  connection.authenticated = True
4945
4905
  connection.sc = True
@@ -5067,7 +5027,7 @@ class Device(CompositeEventEmitter):
5067
5027
  def on_pairing(
5068
5028
  self,
5069
5029
  connection: Connection,
5070
- identity_address: Optional[Address],
5030
+ identity_address: Optional[hci.Address],
5071
5031
  keys: PairingKeys,
5072
5032
  sc: bool,
5073
5033
  ) -> None: