bumble 0.0.218__py3-none-any.whl → 0.0.220__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bumble/_version.py +2 -2
- bumble/a2dp.py +57 -18
- bumble/apps/auracast.py +7 -13
- bumble/audio/io.py +3 -3
- bumble/avctp.py +8 -12
- bumble/avdtp.py +584 -533
- bumble/avrcp.py +20 -20
- bumble/controller.py +993 -507
- bumble/device.py +107 -183
- bumble/drivers/rtk.py +3 -1
- bumble/hci.py +38 -0
- bumble/hid.py +1 -1
- bumble/link.py +68 -165
- bumble/lmp.py +324 -0
- bumble/rfcomm.py +7 -3
- bumble/snoop.py +10 -4
- bumble/transport/common.py +6 -3
- bumble/transport/ws_client.py +2 -2
- bumble/transport/ws_server.py +16 -8
- bumble/utils.py +1 -5
- {bumble-0.0.218.dist-info → bumble-0.0.220.dist-info}/METADATA +3 -3
- {bumble-0.0.218.dist-info → bumble-0.0.220.dist-info}/RECORD +26 -25
- {bumble-0.0.218.dist-info → bumble-0.0.220.dist-info}/WHEEL +0 -0
- {bumble-0.0.218.dist-info → bumble-0.0.220.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.218.dist-info → bumble-0.0.220.dist-info}/licenses/LICENSE +0 -0
- {bumble-0.0.218.dist-info → bumble-0.0.220.dist-info}/top_level.txt +0 -0
bumble/device.py
CHANGED
|
@@ -907,7 +907,7 @@ class PeriodicAdvertisingSync(utils.EventEmitter):
|
|
|
907
907
|
hci.HCI_LE_Periodic_Advertising_Create_Sync_Command.Options.DUPLICATE_FILTERING_INITIALLY_ENABLED
|
|
908
908
|
)
|
|
909
909
|
|
|
910
|
-
|
|
910
|
+
await self.device.send_command(
|
|
911
911
|
hci.HCI_LE_Periodic_Advertising_Create_Sync_Command(
|
|
912
912
|
options=options,
|
|
913
913
|
advertising_sid=self.sid,
|
|
@@ -916,10 +916,9 @@ class PeriodicAdvertisingSync(utils.EventEmitter):
|
|
|
916
916
|
skip=self.skip,
|
|
917
917
|
sync_timeout=int(self.sync_timeout * 100),
|
|
918
918
|
sync_cte_type=0,
|
|
919
|
-
)
|
|
919
|
+
),
|
|
920
|
+
check_result=True,
|
|
920
921
|
)
|
|
921
|
-
if response.status != hci.HCI_Command_Status_Event.PENDING:
|
|
922
|
-
raise hci.HCI_StatusError(response)
|
|
923
922
|
|
|
924
923
|
self.state = self.State.PENDING
|
|
925
924
|
|
|
@@ -1915,16 +1914,13 @@ class Connection(utils.CompositeEventEmitter):
|
|
|
1915
1914
|
"""Idles the current task waiting for a disconnect or timeout"""
|
|
1916
1915
|
|
|
1917
1916
|
abort = asyncio.get_running_loop().create_future()
|
|
1918
|
-
|
|
1919
|
-
|
|
1917
|
+
with closing(utils.EventWatcher()) as watcher:
|
|
1918
|
+
watcher.on(self, self.EVENT_DISCONNECTION, abort.set_result)
|
|
1919
|
+
watcher.on(self, self.EVENT_DISCONNECTION_FAILURE, abort.set_exception)
|
|
1920
1920
|
|
|
1921
|
-
try:
|
|
1922
1921
|
await asyncio.wait_for(
|
|
1923
1922
|
utils.cancel_on_event(self.device, Device.EVENT_FLUSH, abort), timeout
|
|
1924
1923
|
)
|
|
1925
|
-
finally:
|
|
1926
|
-
self.remove_listener(self.EVENT_DISCONNECTION, abort.set_result)
|
|
1927
|
-
self.remove_listener(self.EVENT_DISCONNECTION_FAILURE, abort.set_exception)
|
|
1928
1924
|
|
|
1929
1925
|
async def set_data_length(self, tx_octets: int, tx_time: int) -> None:
|
|
1930
1926
|
return await self.device.set_data_length(self, tx_octets, tx_time)
|
|
@@ -2374,11 +2370,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
2374
2370
|
hci.Address.ANY: []
|
|
2375
2371
|
} # Futures, by BD address OR [Futures] for hci.Address.ANY
|
|
2376
2372
|
|
|
2377
|
-
|
|
2378
|
-
if sys.version_info >= (3, 10):
|
|
2379
|
-
self._cis_lock = asyncio.Lock()
|
|
2380
|
-
else:
|
|
2381
|
-
self._cis_lock = AsyncExitStack()
|
|
2373
|
+
self._cis_lock = asyncio.Lock()
|
|
2382
2374
|
|
|
2383
2375
|
# Own address type cache
|
|
2384
2376
|
self.connect_own_address_type = None
|
|
@@ -2887,7 +2879,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
2887
2879
|
self.address_resolver = smp.AddressResolver(resolving_keys)
|
|
2888
2880
|
|
|
2889
2881
|
if self.address_resolution_offload or self.address_generation_offload:
|
|
2890
|
-
await self.send_command(
|
|
2882
|
+
await self.send_command(
|
|
2883
|
+
hci.HCI_LE_Clear_Resolving_List_Command(), check_result=True
|
|
2884
|
+
)
|
|
2891
2885
|
|
|
2892
2886
|
# Add an empty entry for non-directed address generation.
|
|
2893
2887
|
await self.send_command(
|
|
@@ -2896,7 +2890,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
2896
2890
|
peer_identity_address=hci.Address.ANY,
|
|
2897
2891
|
peer_irk=bytes(16),
|
|
2898
2892
|
local_irk=self.irk,
|
|
2899
|
-
)
|
|
2893
|
+
),
|
|
2894
|
+
check_result=True,
|
|
2900
2895
|
)
|
|
2901
2896
|
|
|
2902
2897
|
for irk, address in resolving_keys:
|
|
@@ -2906,7 +2901,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
2906
2901
|
peer_identity_address=address,
|
|
2907
2902
|
peer_irk=irk,
|
|
2908
2903
|
local_irk=self.irk,
|
|
2909
|
-
)
|
|
2904
|
+
),
|
|
2905
|
+
check_result=True,
|
|
2910
2906
|
)
|
|
2911
2907
|
|
|
2912
2908
|
def supports_le_features(self, feature: hci.LeFeatureMask) -> bool:
|
|
@@ -3501,16 +3497,15 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3501
3497
|
check_result=True,
|
|
3502
3498
|
)
|
|
3503
3499
|
|
|
3504
|
-
|
|
3500
|
+
self.discovering = False
|
|
3501
|
+
await self.send_command(
|
|
3505
3502
|
hci.HCI_Inquiry_Command(
|
|
3506
3503
|
lap=hci.HCI_GENERAL_INQUIRY_LAP,
|
|
3507
3504
|
inquiry_length=DEVICE_DEFAULT_INQUIRY_LENGTH,
|
|
3508
3505
|
num_responses=0, # Unlimited number of responses.
|
|
3509
|
-
)
|
|
3506
|
+
),
|
|
3507
|
+
check_result=True,
|
|
3510
3508
|
)
|
|
3511
|
-
if response.status != hci.HCI_Command_Status_Event.PENDING:
|
|
3512
|
-
self.discovering = False
|
|
3513
|
-
raise hci.HCI_StatusError(response)
|
|
3514
3509
|
|
|
3515
3510
|
self.auto_restart_inquiry = auto_restart
|
|
3516
3511
|
self.discovering = True
|
|
@@ -3546,7 +3541,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3546
3541
|
scan_enable = 0x00
|
|
3547
3542
|
|
|
3548
3543
|
return await self.send_command(
|
|
3549
|
-
hci.HCI_Write_Scan_Enable_Command(scan_enable=scan_enable)
|
|
3544
|
+
hci.HCI_Write_Scan_Enable_Command(scan_enable=scan_enable),
|
|
3545
|
+
check_result=True,
|
|
3550
3546
|
)
|
|
3551
3547
|
|
|
3552
3548
|
async def set_discoverable(self, discoverable: bool = True) -> None:
|
|
@@ -3775,7 +3771,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3775
3771
|
for phy in phys
|
|
3776
3772
|
]
|
|
3777
3773
|
|
|
3778
|
-
|
|
3774
|
+
await self.send_command(
|
|
3779
3775
|
hci.HCI_LE_Extended_Create_Connection_Command(
|
|
3780
3776
|
initiator_filter_policy=0,
|
|
3781
3777
|
own_address_type=own_address_type,
|
|
@@ -3796,14 +3792,15 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3796
3792
|
supervision_timeouts=supervision_timeouts,
|
|
3797
3793
|
min_ce_lengths=min_ce_lengths,
|
|
3798
3794
|
max_ce_lengths=max_ce_lengths,
|
|
3799
|
-
)
|
|
3795
|
+
),
|
|
3796
|
+
check_result=True,
|
|
3800
3797
|
)
|
|
3801
3798
|
else:
|
|
3802
3799
|
if hci.HCI_LE_1M_PHY not in connection_parameters_preferences:
|
|
3803
3800
|
raise InvalidArgumentError('1M PHY preferences required')
|
|
3804
3801
|
|
|
3805
3802
|
prefs = connection_parameters_preferences[hci.HCI_LE_1M_PHY]
|
|
3806
|
-
|
|
3803
|
+
await self.send_command(
|
|
3807
3804
|
hci.HCI_LE_Create_Connection_Command(
|
|
3808
3805
|
le_scan_interval=int(
|
|
3809
3806
|
DEVICE_DEFAULT_CONNECT_SCAN_INTERVAL / 0.625
|
|
@@ -3825,7 +3822,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3825
3822
|
supervision_timeout=int(prefs.supervision_timeout / 10),
|
|
3826
3823
|
min_ce_length=int(prefs.min_ce_length / 0.625),
|
|
3827
3824
|
max_ce_length=int(prefs.max_ce_length / 0.625),
|
|
3828
|
-
)
|
|
3825
|
+
),
|
|
3826
|
+
check_result=True,
|
|
3829
3827
|
)
|
|
3830
3828
|
else:
|
|
3831
3829
|
# Save pending connection
|
|
@@ -3842,7 +3840,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3842
3840
|
)
|
|
3843
3841
|
|
|
3844
3842
|
# TODO: allow passing other settings
|
|
3845
|
-
|
|
3843
|
+
await self.send_command(
|
|
3846
3844
|
hci.HCI_Create_Connection_Command(
|
|
3847
3845
|
bd_addr=peer_address,
|
|
3848
3846
|
packet_type=0xCC18, # FIXME: change
|
|
@@ -3850,12 +3848,10 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3850
3848
|
clock_offset=0x0000,
|
|
3851
3849
|
allow_role_switch=0x01,
|
|
3852
3850
|
reserved=0,
|
|
3853
|
-
)
|
|
3851
|
+
),
|
|
3852
|
+
check_result=True,
|
|
3854
3853
|
)
|
|
3855
3854
|
|
|
3856
|
-
if result.status != hci.HCI_Command_Status_Event.PENDING:
|
|
3857
|
-
raise hci.HCI_StatusError(result)
|
|
3858
|
-
|
|
3859
3855
|
# Wait for the connection process to complete
|
|
3860
3856
|
if transport == PhysicalTransport.LE:
|
|
3861
3857
|
self.le_connecting = True
|
|
@@ -4007,7 +4003,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4007
4003
|
await self.send_command(
|
|
4008
4004
|
hci.HCI_Accept_Connection_Request_Command(
|
|
4009
4005
|
bd_addr=peer_address, role=role
|
|
4010
|
-
)
|
|
4006
|
+
),
|
|
4007
|
+
check_result=True,
|
|
4011
4008
|
)
|
|
4012
4009
|
|
|
4013
4010
|
# Wait for connection complete
|
|
@@ -4077,19 +4074,17 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4077
4074
|
connection.EVENT_DISCONNECTION_FAILURE, pending_disconnection.set_exception
|
|
4078
4075
|
)
|
|
4079
4076
|
|
|
4080
|
-
# Request a disconnection
|
|
4081
|
-
result = await self.send_command(
|
|
4082
|
-
hci.HCI_Disconnect_Command(
|
|
4083
|
-
connection_handle=connection.handle, reason=reason
|
|
4084
|
-
)
|
|
4085
|
-
)
|
|
4086
|
-
|
|
4087
4077
|
try:
|
|
4088
|
-
if result.status != hci.HCI_Command_Status_Event.PENDING:
|
|
4089
|
-
raise hci.HCI_StatusError(result)
|
|
4090
|
-
|
|
4091
4078
|
# Wait for the disconnection process to complete
|
|
4092
4079
|
self.disconnecting = True
|
|
4080
|
+
|
|
4081
|
+
# Request a disconnection
|
|
4082
|
+
await self.send_command(
|
|
4083
|
+
hci.HCI_Disconnect_Command(
|
|
4084
|
+
connection_handle=connection.handle, reason=reason
|
|
4085
|
+
),
|
|
4086
|
+
check_result=True,
|
|
4087
|
+
)
|
|
4093
4088
|
return await utils.cancel_on_event(
|
|
4094
4089
|
self, Device.EVENT_FLUSH, pending_disconnection
|
|
4095
4090
|
)
|
|
@@ -4175,7 +4170,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4175
4170
|
|
|
4176
4171
|
return
|
|
4177
4172
|
|
|
4178
|
-
|
|
4173
|
+
await self.send_command(
|
|
4179
4174
|
hci.HCI_LE_Connection_Update_Command(
|
|
4180
4175
|
connection_handle=connection.handle,
|
|
4181
4176
|
connection_interval_min=connection_interval_min,
|
|
@@ -4184,10 +4179,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4184
4179
|
supervision_timeout=supervision_timeout,
|
|
4185
4180
|
min_ce_length=min_ce_length,
|
|
4186
4181
|
max_ce_length=max_ce_length,
|
|
4187
|
-
)
|
|
4182
|
+
),
|
|
4183
|
+
check_result=True,
|
|
4188
4184
|
)
|
|
4189
|
-
if result.status != hci.HCI_Command_Status_Event.PENDING:
|
|
4190
|
-
raise hci.HCI_StatusError(result)
|
|
4191
4185
|
|
|
4192
4186
|
async def get_connection_rssi(self, connection):
|
|
4193
4187
|
result = await self.send_command(
|
|
@@ -4222,23 +4216,17 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4222
4216
|
(1 if rx_phys is None else 0) << 1
|
|
4223
4217
|
)
|
|
4224
4218
|
|
|
4225
|
-
|
|
4219
|
+
await self.send_command(
|
|
4226
4220
|
hci.HCI_LE_Set_PHY_Command(
|
|
4227
4221
|
connection_handle=connection.handle,
|
|
4228
4222
|
all_phys=all_phys_bits,
|
|
4229
4223
|
tx_phys=hci.phy_list_to_bits(tx_phys),
|
|
4230
4224
|
rx_phys=hci.phy_list_to_bits(rx_phys),
|
|
4231
4225
|
phy_options=phy_options,
|
|
4232
|
-
)
|
|
4226
|
+
),
|
|
4227
|
+
check_result=True,
|
|
4233
4228
|
)
|
|
4234
4229
|
|
|
4235
|
-
if result.status != hci.HCI_COMMAND_STATUS_PENDING:
|
|
4236
|
-
logger.warning(
|
|
4237
|
-
'HCI_LE_Set_PHY_Command failed: '
|
|
4238
|
-
f'{hci.HCI_Constant.error_name(result.status)}'
|
|
4239
|
-
)
|
|
4240
|
-
raise hci.HCI_StatusError(result)
|
|
4241
|
-
|
|
4242
4230
|
async def set_default_phy(
|
|
4243
4231
|
self,
|
|
4244
4232
|
tx_phys: Optional[Iterable[hci.Phy]] = None,
|
|
@@ -4455,43 +4443,26 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4455
4443
|
async def authenticate(self, connection: Connection) -> None:
|
|
4456
4444
|
# Set up event handlers
|
|
4457
4445
|
pending_authentication = asyncio.get_running_loop().create_future()
|
|
4446
|
+
with closing(utils.EventWatcher()) as watcher:
|
|
4458
4447
|
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
def on_authentication_failure(error_code):
|
|
4463
|
-
pending_authentication.set_exception(hci.HCI_Error(error_code))
|
|
4448
|
+
@watcher.on(connection, connection.EVENT_CONNECTION_AUTHENTICATION)
|
|
4449
|
+
def on_authentication() -> None:
|
|
4450
|
+
pending_authentication.set_result(None)
|
|
4464
4451
|
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
on_authentication_failure,
|
|
4469
|
-
)
|
|
4452
|
+
@watcher.on(connection, connection.EVENT_CONNECTION_AUTHENTICATION_FAILURE)
|
|
4453
|
+
def on_authentication_failure(error_code: int) -> None:
|
|
4454
|
+
pending_authentication.set_exception(hci.HCI_Error(error_code))
|
|
4470
4455
|
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
result = await self.send_command(
|
|
4456
|
+
# Request the authentication
|
|
4457
|
+
await self.send_command(
|
|
4474
4458
|
hci.HCI_Authentication_Requested_Command(
|
|
4475
4459
|
connection_handle=connection.handle
|
|
4476
|
-
)
|
|
4460
|
+
),
|
|
4461
|
+
check_result=True,
|
|
4477
4462
|
)
|
|
4478
|
-
if result.status != hci.HCI_COMMAND_STATUS_PENDING:
|
|
4479
|
-
logger.warning(
|
|
4480
|
-
'HCI_Authentication_Requested_Command failed: '
|
|
4481
|
-
f'{hci.HCI_Constant.error_name(result.status)}'
|
|
4482
|
-
)
|
|
4483
|
-
raise hci.HCI_StatusError(result)
|
|
4484
4463
|
|
|
4485
4464
|
# Wait for the authentication to complete
|
|
4486
4465
|
await connection.cancel_on_disconnection(pending_authentication)
|
|
4487
|
-
finally:
|
|
4488
|
-
connection.remove_listener(
|
|
4489
|
-
connection.EVENT_CONNECTION_AUTHENTICATION, on_authentication
|
|
4490
|
-
)
|
|
4491
|
-
connection.remove_listener(
|
|
4492
|
-
connection.EVENT_CONNECTION_AUTHENTICATION_FAILURE,
|
|
4493
|
-
on_authentication_failure,
|
|
4494
|
-
)
|
|
4495
4466
|
|
|
4496
4467
|
async def encrypt(self, connection: Connection, enable: bool = True):
|
|
4497
4468
|
if not enable and connection.transport == PhysicalTransport.LE:
|
|
@@ -4500,21 +4471,17 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4500
4471
|
# Set up event handlers
|
|
4501
4472
|
pending_encryption = asyncio.get_running_loop().create_future()
|
|
4502
4473
|
|
|
4503
|
-
|
|
4504
|
-
|
|
4474
|
+
# Request the encryption
|
|
4475
|
+
with closing(utils.EventWatcher()) as watcher:
|
|
4505
4476
|
|
|
4506
|
-
|
|
4507
|
-
|
|
4477
|
+
@watcher.on(connection, connection.EVENT_CONNECTION_ENCRYPTION_CHANGE)
|
|
4478
|
+
def _() -> None:
|
|
4479
|
+
pending_encryption.set_result(None)
|
|
4508
4480
|
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
connection.on(
|
|
4513
|
-
connection.EVENT_CONNECTION_ENCRYPTION_FAILURE, on_encryption_failure
|
|
4514
|
-
)
|
|
4481
|
+
@watcher.on(connection, connection.EVENT_CONNECTION_ENCRYPTION_FAILURE)
|
|
4482
|
+
def _(error_code: int):
|
|
4483
|
+
pending_encryption.set_exception(hci.HCI_Error(error_code))
|
|
4515
4484
|
|
|
4516
|
-
# Request the encryption
|
|
4517
|
-
try:
|
|
4518
4485
|
if connection.transport == PhysicalTransport.LE:
|
|
4519
4486
|
# Look for a key in the key store
|
|
4520
4487
|
if self.keystore is None:
|
|
@@ -4539,45 +4506,26 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4539
4506
|
if connection.role != hci.Role.CENTRAL:
|
|
4540
4507
|
raise InvalidStateError('only centrals can start encryption')
|
|
4541
4508
|
|
|
4542
|
-
|
|
4509
|
+
await self.send_command(
|
|
4543
4510
|
hci.HCI_LE_Enable_Encryption_Command(
|
|
4544
4511
|
connection_handle=connection.handle,
|
|
4545
4512
|
random_number=rand,
|
|
4546
4513
|
encrypted_diversifier=ediv,
|
|
4547
4514
|
long_term_key=ltk,
|
|
4548
|
-
)
|
|
4515
|
+
),
|
|
4516
|
+
check_result=True,
|
|
4549
4517
|
)
|
|
4550
|
-
|
|
4551
|
-
if result.status != hci.HCI_COMMAND_STATUS_PENDING:
|
|
4552
|
-
logger.warning(
|
|
4553
|
-
'HCI_LE_Enable_Encryption_Command failed: '
|
|
4554
|
-
f'{hci.HCI_Constant.error_name(result.status)}'
|
|
4555
|
-
)
|
|
4556
|
-
raise hci.HCI_StatusError(result)
|
|
4557
4518
|
else:
|
|
4558
|
-
|
|
4519
|
+
await self.send_command(
|
|
4559
4520
|
hci.HCI_Set_Connection_Encryption_Command(
|
|
4560
4521
|
connection_handle=connection.handle,
|
|
4561
4522
|
encryption_enable=0x01 if enable else 0x00,
|
|
4562
|
-
)
|
|
4523
|
+
),
|
|
4524
|
+
check_result=True,
|
|
4563
4525
|
)
|
|
4564
4526
|
|
|
4565
|
-
if result.status != hci.HCI_COMMAND_STATUS_PENDING:
|
|
4566
|
-
logger.warning(
|
|
4567
|
-
'HCI_Set_Connection_Encryption_Command failed: '
|
|
4568
|
-
f'{hci.HCI_Constant.error_name(result.status)}'
|
|
4569
|
-
)
|
|
4570
|
-
raise hci.HCI_StatusError(result)
|
|
4571
|
-
|
|
4572
4527
|
# Wait for the result
|
|
4573
4528
|
await connection.cancel_on_disconnection(pending_encryption)
|
|
4574
|
-
finally:
|
|
4575
|
-
connection.remove_listener(
|
|
4576
|
-
connection.EVENT_CONNECTION_ENCRYPTION_CHANGE, on_encryption_change
|
|
4577
|
-
)
|
|
4578
|
-
connection.remove_listener(
|
|
4579
|
-
connection.EVENT_CONNECTION_ENCRYPTION_FAILURE, on_encryption_failure
|
|
4580
|
-
)
|
|
4581
4529
|
|
|
4582
4530
|
async def update_keys(self, address: str, keys: PairingKeys) -> None:
|
|
4583
4531
|
if self.keystore is None:
|
|
@@ -4595,80 +4543,55 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4595
4543
|
async def switch_role(self, connection: Connection, role: hci.Role):
|
|
4596
4544
|
pending_role_change = asyncio.get_running_loop().create_future()
|
|
4597
4545
|
|
|
4598
|
-
|
|
4599
|
-
pending_role_change.set_result(new_role)
|
|
4546
|
+
with closing(utils.EventWatcher()) as watcher:
|
|
4600
4547
|
|
|
4601
|
-
|
|
4602
|
-
|
|
4548
|
+
@watcher.on(connection, connection.EVENT_ROLE_CHANGE)
|
|
4549
|
+
def _(new_role: hci.Role):
|
|
4550
|
+
pending_role_change.set_result(new_role)
|
|
4603
4551
|
|
|
4604
|
-
|
|
4605
|
-
|
|
4552
|
+
@watcher.on(connection, connection.EVENT_ROLE_CHANGE_FAILURE)
|
|
4553
|
+
def _(error_code: int):
|
|
4554
|
+
pending_role_change.set_exception(hci.HCI_Error(error_code))
|
|
4606
4555
|
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4556
|
+
await self.send_command(
|
|
4557
|
+
hci.HCI_Switch_Role_Command(bd_addr=connection.peer_address, role=role),
|
|
4558
|
+
check_result=True,
|
|
4610
4559
|
)
|
|
4611
|
-
if result.status != hci.HCI_COMMAND_STATUS_PENDING:
|
|
4612
|
-
logger.warning(
|
|
4613
|
-
'HCI_Switch_Role_Command failed: '
|
|
4614
|
-
f'{hci.HCI_Constant.error_name(result.status)}'
|
|
4615
|
-
)
|
|
4616
|
-
raise hci.HCI_StatusError(result)
|
|
4617
4560
|
await connection.cancel_on_disconnection(pending_role_change)
|
|
4618
|
-
finally:
|
|
4619
|
-
connection.remove_listener(connection.EVENT_ROLE_CHANGE, on_role_change)
|
|
4620
|
-
connection.remove_listener(
|
|
4621
|
-
connection.EVENT_ROLE_CHANGE_FAILURE, on_role_change_failure
|
|
4622
|
-
)
|
|
4623
4561
|
|
|
4624
4562
|
# [Classic only]
|
|
4625
4563
|
async def request_remote_name(self, remote: Union[hci.Address, Connection]) -> str:
|
|
4626
4564
|
# Set up event handlers
|
|
4627
|
-
pending_name = asyncio.get_running_loop().create_future()
|
|
4565
|
+
pending_name: asyncio.Future[str] = asyncio.get_running_loop().create_future()
|
|
4628
4566
|
|
|
4629
4567
|
peer_address = (
|
|
4630
4568
|
remote if isinstance(remote, hci.Address) else remote.peer_address
|
|
4631
4569
|
)
|
|
4632
4570
|
|
|
4633
|
-
|
|
4634
|
-
self.EVENT_REMOTE_NAME,
|
|
4635
|
-
lambda address, remote_name: (
|
|
4636
|
-
pending_name.set_result(remote_name)
|
|
4637
|
-
if address == peer_address
|
|
4638
|
-
else None
|
|
4639
|
-
),
|
|
4640
|
-
)
|
|
4641
|
-
failure_handler = self.on(
|
|
4642
|
-
self.EVENT_REMOTE_NAME_FAILURE,
|
|
4643
|
-
lambda address, error_code: (
|
|
4644
|
-
pending_name.set_exception(hci.HCI_Error(error_code))
|
|
4645
|
-
if address == peer_address
|
|
4646
|
-
else None
|
|
4647
|
-
),
|
|
4648
|
-
)
|
|
4571
|
+
with closing(utils.EventWatcher()) as watcher:
|
|
4649
4572
|
|
|
4650
|
-
|
|
4651
|
-
|
|
4573
|
+
@watcher.on(self, self.EVENT_REMOTE_NAME)
|
|
4574
|
+
def _(address: hci.Address, remote_name: str) -> None:
|
|
4575
|
+
if address == peer_address:
|
|
4576
|
+
pending_name.set_result(remote_name)
|
|
4577
|
+
|
|
4578
|
+
@watcher.on(self, self.EVENT_REMOTE_NAME_FAILURE)
|
|
4579
|
+
def _(address: hci.Address, error_code: int) -> None:
|
|
4580
|
+
if address == peer_address:
|
|
4581
|
+
pending_name.set_exception(hci.HCI_Error(error_code))
|
|
4582
|
+
|
|
4583
|
+
await self.send_command(
|
|
4652
4584
|
hci.HCI_Remote_Name_Request_Command(
|
|
4653
4585
|
bd_addr=peer_address,
|
|
4654
4586
|
page_scan_repetition_mode=hci.HCI_Remote_Name_Request_Command.R2,
|
|
4655
4587
|
reserved=0,
|
|
4656
4588
|
clock_offset=0, # TODO investigate non-0 values
|
|
4657
|
-
)
|
|
4589
|
+
),
|
|
4590
|
+
check_result=True,
|
|
4658
4591
|
)
|
|
4659
4592
|
|
|
4660
|
-
if result.status != hci.HCI_COMMAND_STATUS_PENDING:
|
|
4661
|
-
logger.warning(
|
|
4662
|
-
'HCI_Remote_Name_Request_Command failed: '
|
|
4663
|
-
f'{hci.HCI_Constant.error_name(result.status)}'
|
|
4664
|
-
)
|
|
4665
|
-
raise hci.HCI_StatusError(result)
|
|
4666
|
-
|
|
4667
4593
|
# Wait for the result
|
|
4668
4594
|
return await utils.cancel_on_event(self, Device.EVENT_FLUSH, pending_name)
|
|
4669
|
-
finally:
|
|
4670
|
-
self.remove_listener(self.EVENT_REMOTE_NAME, handler)
|
|
4671
|
-
self.remove_listener(self.EVENT_REMOTE_NAME_FAILURE, failure_handler)
|
|
4672
4595
|
|
|
4673
4596
|
# [LE only]
|
|
4674
4597
|
@utils.experimental('Only for testing.')
|
|
@@ -4684,8 +4607,6 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4684
4607
|
Returns:
|
|
4685
4608
|
List of created CIS handles corresponding to the same order of [cid_id].
|
|
4686
4609
|
"""
|
|
4687
|
-
num_cis = len(parameters.cis_parameters)
|
|
4688
|
-
|
|
4689
4610
|
response = await self.send_command(
|
|
4690
4611
|
hci.HCI_LE_Set_CIG_Parameters_Command(
|
|
4691
4612
|
cig_id=parameters.cig_id,
|
|
@@ -5753,9 +5674,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5753
5674
|
|
|
5754
5675
|
@host_event_handler
|
|
5755
5676
|
@with_connection_from_handle
|
|
5756
|
-
def on_connection_authentication_failure(
|
|
5757
|
-
self, connection: Connection, error: core.ConnectionError
|
|
5758
|
-
):
|
|
5677
|
+
def on_connection_authentication_failure(self, connection: Connection, error: int):
|
|
5759
5678
|
logger.debug(
|
|
5760
5679
|
f'*** Connection Authentication Failure: [0x{connection.handle:04X}] '
|
|
5761
5680
|
f'{connection.peer_address} as {connection.role_name}, error={error}'
|
|
@@ -5810,11 +5729,15 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5810
5729
|
# [Classic only]
|
|
5811
5730
|
@host_event_handler
|
|
5812
5731
|
@with_connection_from_address
|
|
5813
|
-
def on_authentication_user_confirmation_request(
|
|
5732
|
+
def on_authentication_user_confirmation_request(
|
|
5733
|
+
self, connection: Connection, code: int
|
|
5734
|
+
) -> None:
|
|
5814
5735
|
# Ask what the pairing config should be for this connection
|
|
5815
5736
|
pairing_config = self.pairing_config_factory(connection)
|
|
5816
5737
|
io_capability = pairing_config.delegate.classic_io_capability
|
|
5817
5738
|
peer_io_capability = connection.pairing_peer_io_capability
|
|
5739
|
+
if peer_io_capability is None:
|
|
5740
|
+
raise core.InvalidStateError("Unknown pairing_peer_io_capability")
|
|
5818
5741
|
|
|
5819
5742
|
async def confirm() -> bool:
|
|
5820
5743
|
# Ask the user to confirm the pairing, without display
|
|
@@ -5941,15 +5864,16 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5941
5864
|
# Respond
|
|
5942
5865
|
if io_capability == hci.IoCapability.KEYBOARD_ONLY:
|
|
5943
5866
|
# Ask the user to enter a string
|
|
5944
|
-
async def get_pin_code():
|
|
5945
|
-
|
|
5867
|
+
async def get_pin_code() -> None:
|
|
5868
|
+
pin_code_str = await connection.cancel_on_disconnection(
|
|
5946
5869
|
pairing_config.delegate.get_string(16)
|
|
5947
5870
|
)
|
|
5948
5871
|
|
|
5949
|
-
if
|
|
5950
|
-
pin_code = bytes(
|
|
5872
|
+
if pin_code_str is not None:
|
|
5873
|
+
pin_code = bytes(pin_code_str, encoding='utf-8')
|
|
5951
5874
|
pin_code_len = len(pin_code)
|
|
5952
|
-
|
|
5875
|
+
if not 1 <= pin_code_len <= 16:
|
|
5876
|
+
raise core.InvalidArgumentError("pin_code should be 1-16 bytes")
|
|
5953
5877
|
await self.host.send_command(
|
|
5954
5878
|
hci.HCI_PIN_Code_Request_Reply_Command(
|
|
5955
5879
|
bd_addr=connection.peer_address,
|
bumble/drivers/rtk.py
CHANGED
|
@@ -115,12 +115,14 @@ RTK_USB_PRODUCTS = {
|
|
|
115
115
|
# Realtek 8761BUV
|
|
116
116
|
(0x0B05, 0x190E),
|
|
117
117
|
(0x0BDA, 0x8771),
|
|
118
|
+
(0x0BDA, 0x877B),
|
|
119
|
+
(0x0BDA, 0xA728),
|
|
120
|
+
(0x0BDA, 0xA729),
|
|
118
121
|
(0x2230, 0x0016),
|
|
119
122
|
(0x2357, 0x0604),
|
|
120
123
|
(0x2550, 0x8761),
|
|
121
124
|
(0x2B89, 0x8761),
|
|
122
125
|
(0x7392, 0xC611),
|
|
123
|
-
(0x0BDA, 0x877B),
|
|
124
126
|
# Realtek 8821AE
|
|
125
127
|
(0x0B05, 0x17DC),
|
|
126
128
|
(0x13D3, 0x3414),
|
bumble/hci.py
CHANGED
|
@@ -3441,6 +3441,17 @@ class HCI_Write_Synchronous_Flow_Control_Enable_Command(HCI_Command):
|
|
|
3441
3441
|
synchronous_flow_control_enable: int = field(metadata=metadata(1))
|
|
3442
3442
|
|
|
3443
3443
|
|
|
3444
|
+
# -----------------------------------------------------------------------------
|
|
3445
|
+
@HCI_Command.command
|
|
3446
|
+
@dataclasses.dataclass
|
|
3447
|
+
class HCI_Set_Controller_To_Host_Flow_Control_Command(HCI_Command):
|
|
3448
|
+
'''
|
|
3449
|
+
See Bluetooth spec @ 7.3.38 Set Controller To Host Flow Control command
|
|
3450
|
+
'''
|
|
3451
|
+
|
|
3452
|
+
flow_control_enable: int = field(metadata=metadata(1))
|
|
3453
|
+
|
|
3454
|
+
|
|
3444
3455
|
# -----------------------------------------------------------------------------
|
|
3445
3456
|
@HCI_Command.command
|
|
3446
3457
|
@dataclasses.dataclass
|
|
@@ -4338,6 +4349,15 @@ class HCI_LE_Write_Suggested_Default_Data_Length_Command(HCI_Command):
|
|
|
4338
4349
|
suggested_max_tx_time: int = field(metadata=metadata(2))
|
|
4339
4350
|
|
|
4340
4351
|
|
|
4352
|
+
# -----------------------------------------------------------------------------
|
|
4353
|
+
@HCI_Command.command
|
|
4354
|
+
@dataclasses.dataclass
|
|
4355
|
+
class HCI_LE_Read_Local_P_256_Public_Key_Command(HCI_Command):
|
|
4356
|
+
'''
|
|
4357
|
+
See Bluetooth spec @ 7.8.36 LE LE Read Local P-256 Public Key command
|
|
4358
|
+
'''
|
|
4359
|
+
|
|
4360
|
+
|
|
4341
4361
|
# -----------------------------------------------------------------------------
|
|
4342
4362
|
@HCI_Command.command
|
|
4343
4363
|
@dataclasses.dataclass
|
|
@@ -4365,6 +4385,15 @@ class HCI_LE_Clear_Resolving_List_Command(HCI_Command):
|
|
|
4365
4385
|
'''
|
|
4366
4386
|
|
|
4367
4387
|
|
|
4388
|
+
# -----------------------------------------------------------------------------
|
|
4389
|
+
@HCI_Command.command
|
|
4390
|
+
@dataclasses.dataclass
|
|
4391
|
+
class HCI_LE_Read_Resolving_List_Size_Command(HCI_Command):
|
|
4392
|
+
'''
|
|
4393
|
+
See Bluetooth spec @ 7.8.41 LE Read Resolving List Size command
|
|
4394
|
+
'''
|
|
4395
|
+
|
|
4396
|
+
|
|
4368
4397
|
# -----------------------------------------------------------------------------
|
|
4369
4398
|
@HCI_Command.command
|
|
4370
4399
|
@dataclasses.dataclass
|
|
@@ -5028,6 +5057,15 @@ class HCI_LE_Periodic_Advertising_Terminate_Sync_Command(HCI_Command):
|
|
|
5028
5057
|
sync_handle: int = field(metadata=metadata(2))
|
|
5029
5058
|
|
|
5030
5059
|
|
|
5060
|
+
# -----------------------------------------------------------------------------
|
|
5061
|
+
@HCI_Command.command
|
|
5062
|
+
@dataclasses.dataclass
|
|
5063
|
+
class HCI_LE_Read_Transmit_Power_Command(HCI_Command):
|
|
5064
|
+
'''
|
|
5065
|
+
See Bluetooth spec @ 7.8.74 LE Read Transmit Power command
|
|
5066
|
+
'''
|
|
5067
|
+
|
|
5068
|
+
|
|
5031
5069
|
# -----------------------------------------------------------------------------
|
|
5032
5070
|
@HCI_Command.command
|
|
5033
5071
|
@dataclasses.dataclass
|
bumble/hid.py
CHANGED
|
@@ -246,7 +246,7 @@ class HID(ABC, utils.EventEmitter):
|
|
|
246
246
|
# Create a new L2CAP connection - interrupt channel
|
|
247
247
|
try:
|
|
248
248
|
channel = await self.connection.create_l2cap_channel(
|
|
249
|
-
l2cap.ClassicChannelSpec(
|
|
249
|
+
l2cap.ClassicChannelSpec(HID_INTERRUPT_PSM)
|
|
250
250
|
)
|
|
251
251
|
channel.sink = self.on_intr_pdu
|
|
252
252
|
self.l2cap_intr_channel = channel
|