bumble 0.0.193__py3-none-any.whl → 0.0.195__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bumble/_version.py +2 -2
- bumble/apps/auracast.py +407 -0
- bumble/apps/bench.py +146 -35
- bumble/apps/controller_info.py +3 -3
- bumble/apps/rfcomm_bridge.py +511 -0
- bumble/core.py +689 -115
- bumble/device.py +441 -12
- bumble/hci.py +250 -12
- bumble/host.py +25 -0
- bumble/l2cap.py +5 -2
- bumble/pandora/host.py +3 -2
- bumble/profiles/bap.py +101 -5
- bumble/profiles/le_audio.py +49 -0
- bumble/profiles/pbp.py +46 -0
- bumble/rfcomm.py +158 -61
- bumble/sdp.py +1 -1
- {bumble-0.0.193.dist-info → bumble-0.0.195.dist-info}/METADATA +1 -1
- {bumble-0.0.193.dist-info → bumble-0.0.195.dist-info}/RECORD +22 -18
- {bumble-0.0.193.dist-info → bumble-0.0.195.dist-info}/entry_points.txt +1 -0
- {bumble-0.0.193.dist-info → bumble-0.0.195.dist-info}/LICENSE +0 -0
- {bumble-0.0.193.dist-info → bumble-0.0.195.dist-info}/WHEEL +0 -0
- {bumble-0.0.193.dist-info → bumble-0.0.195.dist-info}/top_level.txt +0 -0
bumble/hci.py
CHANGED
|
@@ -26,8 +26,8 @@ import struct
|
|
|
26
26
|
from typing import Any, Callable, Dict, Iterable, List, Optional, Type, Union, ClassVar
|
|
27
27
|
|
|
28
28
|
from bumble import crypto
|
|
29
|
-
from .colors import color
|
|
30
|
-
from .core import (
|
|
29
|
+
from bumble.colors import color
|
|
30
|
+
from bumble.core import (
|
|
31
31
|
BT_BR_EDR_TRANSPORT,
|
|
32
32
|
AdvertisingData,
|
|
33
33
|
DeviceClass,
|
|
@@ -36,6 +36,7 @@ from .core import (
|
|
|
36
36
|
name_or_number,
|
|
37
37
|
padded_bytes,
|
|
38
38
|
)
|
|
39
|
+
from bumble.utils import OpenIntEnum
|
|
39
40
|
|
|
40
41
|
|
|
41
42
|
# -----------------------------------------------------------------------------
|
|
@@ -1104,7 +1105,7 @@ HCI_SUPPORTED_COMMANDS_MASKS = {
|
|
|
1104
1105
|
|
|
1105
1106
|
# LE Supported Features
|
|
1106
1107
|
# See Bluetooth spec @ Vol 6, Part B, 4.6 FEATURE SUPPORT
|
|
1107
|
-
class LeFeature(
|
|
1108
|
+
class LeFeature(OpenIntEnum):
|
|
1108
1109
|
LE_ENCRYPTION = 0
|
|
1109
1110
|
CONNECTION_PARAMETERS_REQUEST_PROCEDURE = 1
|
|
1110
1111
|
EXTENDED_REJECT_INDICATION = 2
|
|
@@ -1380,7 +1381,7 @@ class LmpFeatureMask(enum.IntFlag):
|
|
|
1380
1381
|
STATUS_SPEC = {'size': 1, 'mapper': lambda x: HCI_Constant.status_name(x)}
|
|
1381
1382
|
|
|
1382
1383
|
|
|
1383
|
-
class CodecID(
|
|
1384
|
+
class CodecID(OpenIntEnum):
|
|
1384
1385
|
# fmt: off
|
|
1385
1386
|
U_LOG = 0x00
|
|
1386
1387
|
A_LOG = 0x01
|
|
@@ -1967,6 +1968,9 @@ class Address:
|
|
|
1967
1968
|
def __str__(self):
|
|
1968
1969
|
return self.to_string()
|
|
1969
1970
|
|
|
1971
|
+
def __repr__(self):
|
|
1972
|
+
return f'Address({self.to_string(False)}/{self.address_type_name(self.address_type)})'
|
|
1973
|
+
|
|
1970
1974
|
|
|
1971
1975
|
# Predefined address values
|
|
1972
1976
|
Address.NIL = Address(b"\xff\xff\xff\xff\xff\xff", Address.PUBLIC_DEVICE_ADDRESS)
|
|
@@ -4452,6 +4456,80 @@ class HCI_LE_Extended_Create_Connection_Command(HCI_Command):
|
|
|
4452
4456
|
)
|
|
4453
4457
|
|
|
4454
4458
|
|
|
4459
|
+
# -----------------------------------------------------------------------------
|
|
4460
|
+
@HCI_Command.command(
|
|
4461
|
+
[
|
|
4462
|
+
(
|
|
4463
|
+
'options',
|
|
4464
|
+
{
|
|
4465
|
+
'size': 1,
|
|
4466
|
+
'mapper': lambda x: HCI_LE_Periodic_Advertising_Create_Sync_Command.Options(
|
|
4467
|
+
x
|
|
4468
|
+
).name,
|
|
4469
|
+
},
|
|
4470
|
+
),
|
|
4471
|
+
('advertising_sid', 1),
|
|
4472
|
+
('advertiser_address_type', Address.ADDRESS_TYPE_SPEC),
|
|
4473
|
+
('advertiser_address', Address.parse_address_preceded_by_type),
|
|
4474
|
+
('skip', 2),
|
|
4475
|
+
('sync_timeout', 2),
|
|
4476
|
+
(
|
|
4477
|
+
'sync_cte_type',
|
|
4478
|
+
{
|
|
4479
|
+
'size': 1,
|
|
4480
|
+
'mapper': lambda x: HCI_LE_Periodic_Advertising_Create_Sync_Command.CteType(
|
|
4481
|
+
x
|
|
4482
|
+
).name,
|
|
4483
|
+
},
|
|
4484
|
+
),
|
|
4485
|
+
]
|
|
4486
|
+
)
|
|
4487
|
+
class HCI_LE_Periodic_Advertising_Create_Sync_Command(HCI_Command):
|
|
4488
|
+
'''
|
|
4489
|
+
See Bluetooth spec @ 7.8.67 LE Periodic Advertising Create Sync command
|
|
4490
|
+
'''
|
|
4491
|
+
|
|
4492
|
+
class Options(enum.IntFlag):
|
|
4493
|
+
USE_PERIODIC_ADVERTISER_LIST = 1 << 0
|
|
4494
|
+
REPORTING_INITIALLY_DISABLED = 1 << 1
|
|
4495
|
+
DUPLICATE_FILTERING_INITIALLY_ENABLED = 1 << 2
|
|
4496
|
+
|
|
4497
|
+
class CteType(enum.IntFlag):
|
|
4498
|
+
DO_NOT_SYNC_TO_PACKETS_WITH_AN_AOA_CONSTANT_TONE_EXTENSION = 1 << 0
|
|
4499
|
+
DO_NOT_SYNC_TO_PACKETS_WITH_AN_AOD_CONSTANT_TONE_EXTENSION_1US = 1 << 1
|
|
4500
|
+
DO_NOT_SYNC_TO_PACKETS_WITH_AN_AOD_CONSTANT_TONE_EXTENSION_2US = 1 << 2
|
|
4501
|
+
DO_NOT_SYNC_TO_PACKETS_WITH_A_TYPE_3_CONSTANT_TONE_EXTENSION = 1 << 3
|
|
4502
|
+
DO_NOT_SYNC_TO_PACKETS_WITHOUT_A_CONSTANT_TONE_EXTENSION = 1 << 4
|
|
4503
|
+
|
|
4504
|
+
|
|
4505
|
+
# -----------------------------------------------------------------------------
|
|
4506
|
+
@HCI_Command.command()
|
|
4507
|
+
class HCI_LE_Periodic_Advertising_Create_Sync_Cancel_Command(HCI_Command):
|
|
4508
|
+
'''
|
|
4509
|
+
See Bluetooth spec @ 7.8.68 LE Periodic Advertising Create Sync Cancel Command
|
|
4510
|
+
'''
|
|
4511
|
+
|
|
4512
|
+
|
|
4513
|
+
# -----------------------------------------------------------------------------
|
|
4514
|
+
@HCI_Command.command([('sync_handle', 2)])
|
|
4515
|
+
class HCI_LE_Periodic_Advertising_Terminate_Sync_Command(HCI_Command):
|
|
4516
|
+
'''
|
|
4517
|
+
See Bluetooth spec @ 7.8.69 LE Periodic Advertising Terminate Sync Command
|
|
4518
|
+
'''
|
|
4519
|
+
|
|
4520
|
+
|
|
4521
|
+
# -----------------------------------------------------------------------------
|
|
4522
|
+
@HCI_Command.command([('sync_handle', 2), ('enable', 1)])
|
|
4523
|
+
class HCI_LE_Set_Periodic_Advertising_Receive_Enable_Command(HCI_Command):
|
|
4524
|
+
'''
|
|
4525
|
+
See Bluetooth spec @ 7.8.88 LE Set Periodic Advertising Receive Enable Command
|
|
4526
|
+
'''
|
|
4527
|
+
|
|
4528
|
+
class Enable(enum.IntFlag):
|
|
4529
|
+
REPORTING_ENABLED = 1 << 0
|
|
4530
|
+
DUPLICATE_FILTERING_ENABLED = 1 << 1
|
|
4531
|
+
|
|
4532
|
+
|
|
4455
4533
|
# -----------------------------------------------------------------------------
|
|
4456
4534
|
@HCI_Command.command(
|
|
4457
4535
|
[
|
|
@@ -4487,14 +4565,6 @@ class HCI_LE_Set_Privacy_Mode_Command(HCI_Command):
|
|
|
4487
4565
|
return name_or_number(cls.PRIVACY_MODE_NAMES, privacy_mode)
|
|
4488
4566
|
|
|
4489
4567
|
|
|
4490
|
-
# -----------------------------------------------------------------------------
|
|
4491
|
-
@HCI_Command.command([('bit_number', 1), ('bit_value', 1)])
|
|
4492
|
-
class HCI_LE_Set_Host_Feature_Command(HCI_Command):
|
|
4493
|
-
'''
|
|
4494
|
-
See Bluetooth spec @ 7.8.115 LE Set Host Feature Command
|
|
4495
|
-
'''
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
4568
|
# -----------------------------------------------------------------------------
|
|
4499
4569
|
@HCI_Command.command(
|
|
4500
4570
|
fields=[
|
|
@@ -4655,6 +4725,14 @@ class HCI_LE_Remove_ISO_Data_Path_Command(HCI_Command):
|
|
|
4655
4725
|
data_path_direction: int
|
|
4656
4726
|
|
|
4657
4727
|
|
|
4728
|
+
# -----------------------------------------------------------------------------
|
|
4729
|
+
@HCI_Command.command([('bit_number', 1), ('bit_value', 1)])
|
|
4730
|
+
class HCI_LE_Set_Host_Feature_Command(HCI_Command):
|
|
4731
|
+
'''
|
|
4732
|
+
See Bluetooth spec @ 7.8.115 LE Set Host Feature Command
|
|
4733
|
+
'''
|
|
4734
|
+
|
|
4735
|
+
|
|
4658
4736
|
# -----------------------------------------------------------------------------
|
|
4659
4737
|
# HCI Events
|
|
4660
4738
|
# -----------------------------------------------------------------------------
|
|
@@ -5271,6 +5349,142 @@ HCI_LE_Meta_Event.subevent_classes[HCI_LE_EXTENDED_ADVERTISING_REPORT_EVENT] = (
|
|
|
5271
5349
|
)
|
|
5272
5350
|
|
|
5273
5351
|
|
|
5352
|
+
# -----------------------------------------------------------------------------
|
|
5353
|
+
@HCI_LE_Meta_Event.event(
|
|
5354
|
+
[
|
|
5355
|
+
('status', STATUS_SPEC),
|
|
5356
|
+
('sync_handle', 2),
|
|
5357
|
+
('advertising_sid', 1),
|
|
5358
|
+
('advertiser_address_type', Address.ADDRESS_TYPE_SPEC),
|
|
5359
|
+
('advertiser_address', Address.parse_address_preceded_by_type),
|
|
5360
|
+
('advertiser_phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}),
|
|
5361
|
+
('periodic_advertising_interval', 2),
|
|
5362
|
+
('advertiser_clock_accuracy', 1),
|
|
5363
|
+
]
|
|
5364
|
+
)
|
|
5365
|
+
class HCI_LE_Periodic_Advertising_Sync_Established_Event(HCI_LE_Meta_Event):
|
|
5366
|
+
'''
|
|
5367
|
+
See Bluetooth spec @ 7.7.65.14 LE Periodic Advertising Sync Established Event
|
|
5368
|
+
'''
|
|
5369
|
+
|
|
5370
|
+
|
|
5371
|
+
# -----------------------------------------------------------------------------
|
|
5372
|
+
@HCI_LE_Meta_Event.event(
|
|
5373
|
+
[
|
|
5374
|
+
('status', STATUS_SPEC),
|
|
5375
|
+
('sync_handle', 2),
|
|
5376
|
+
('advertising_sid', 1),
|
|
5377
|
+
('advertiser_address_type', Address.ADDRESS_TYPE_SPEC),
|
|
5378
|
+
('advertiser_address', Address.parse_address_preceded_by_type),
|
|
5379
|
+
('advertiser_phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}),
|
|
5380
|
+
('periodic_advertising_interval', 2),
|
|
5381
|
+
('advertiser_clock_accuracy', 1),
|
|
5382
|
+
('num_subevents', 1),
|
|
5383
|
+
('subevent_interval', 1),
|
|
5384
|
+
('response_slot_delay', 1),
|
|
5385
|
+
('response_slot_spacing', 1),
|
|
5386
|
+
]
|
|
5387
|
+
)
|
|
5388
|
+
class HCI_LE_Periodic_Advertising_Sync_Established_V2_Event(HCI_LE_Meta_Event):
|
|
5389
|
+
'''
|
|
5390
|
+
See Bluetooth spec @ 7.7.65.14 LE Periodic Advertising Sync Established Event
|
|
5391
|
+
'''
|
|
5392
|
+
|
|
5393
|
+
|
|
5394
|
+
# -----------------------------------------------------------------------------
|
|
5395
|
+
@HCI_LE_Meta_Event.event(
|
|
5396
|
+
[
|
|
5397
|
+
('sync_handle', 2),
|
|
5398
|
+
('tx_power', -1),
|
|
5399
|
+
('rssi', -1),
|
|
5400
|
+
(
|
|
5401
|
+
'cte_type',
|
|
5402
|
+
{
|
|
5403
|
+
'size': 1,
|
|
5404
|
+
'mapper': lambda x: HCI_LE_Periodic_Advertising_Report_Event.CteType(
|
|
5405
|
+
x
|
|
5406
|
+
).name,
|
|
5407
|
+
},
|
|
5408
|
+
),
|
|
5409
|
+
(
|
|
5410
|
+
'data_status',
|
|
5411
|
+
{
|
|
5412
|
+
'size': 1,
|
|
5413
|
+
'mapper': lambda x: HCI_LE_Periodic_Advertising_Report_Event.DataStatus(
|
|
5414
|
+
x
|
|
5415
|
+
).name,
|
|
5416
|
+
},
|
|
5417
|
+
),
|
|
5418
|
+
('data', 'v'),
|
|
5419
|
+
]
|
|
5420
|
+
)
|
|
5421
|
+
class HCI_LE_Periodic_Advertising_Report_Event(HCI_LE_Meta_Event):
|
|
5422
|
+
'''
|
|
5423
|
+
See Bluetooth spec @ 7.7.65.15 LE Periodic Advertising Report Event
|
|
5424
|
+
'''
|
|
5425
|
+
|
|
5426
|
+
TX_POWER_INFORMATION_NOT_AVAILABLE = 0x7F
|
|
5427
|
+
RSSI_NOT_AVAILABLE = 0x7F
|
|
5428
|
+
|
|
5429
|
+
class CteType(OpenIntEnum):
|
|
5430
|
+
AOA_CONSTANT_TONE_EXTENSION = 0x00
|
|
5431
|
+
AOD_CONSTANT_TONE_EXTENSION_1US = 0x01
|
|
5432
|
+
AOD_CONSTANT_TONE_EXTENSION_2US = 0x02
|
|
5433
|
+
NO_CONSTANT_TONE_EXTENSION = 0xFF
|
|
5434
|
+
|
|
5435
|
+
class DataStatus(OpenIntEnum):
|
|
5436
|
+
DATA_COMPLETE = 0x00
|
|
5437
|
+
DATA_INCOMPLETE_MORE_TO_COME = 0x01
|
|
5438
|
+
DATA_INCOMPLETE_TRUNCATED_NO_MORE_TO_COME = 0x02
|
|
5439
|
+
|
|
5440
|
+
|
|
5441
|
+
# -----------------------------------------------------------------------------
|
|
5442
|
+
@HCI_LE_Meta_Event.event(
|
|
5443
|
+
[
|
|
5444
|
+
('sync_handle', 2),
|
|
5445
|
+
('tx_power', -1),
|
|
5446
|
+
('rssi', -1),
|
|
5447
|
+
(
|
|
5448
|
+
'cte_type',
|
|
5449
|
+
{
|
|
5450
|
+
'size': 1,
|
|
5451
|
+
'mapper': lambda x: HCI_LE_Periodic_Advertising_Report_Event.CteType(
|
|
5452
|
+
x
|
|
5453
|
+
).name,
|
|
5454
|
+
},
|
|
5455
|
+
),
|
|
5456
|
+
('periodic_event_counter', 2),
|
|
5457
|
+
('subevent', 1),
|
|
5458
|
+
(
|
|
5459
|
+
'data_status',
|
|
5460
|
+
{
|
|
5461
|
+
'size': 1,
|
|
5462
|
+
'mapper': lambda x: HCI_LE_Periodic_Advertising_Report_Event.DataStatus(
|
|
5463
|
+
x
|
|
5464
|
+
).name,
|
|
5465
|
+
},
|
|
5466
|
+
),
|
|
5467
|
+
('data', 'v'),
|
|
5468
|
+
]
|
|
5469
|
+
)
|
|
5470
|
+
class HCI_LE_Periodic_Advertising_Report_V2_Event(HCI_LE_Meta_Event):
|
|
5471
|
+
'''
|
|
5472
|
+
See Bluetooth spec @ 7.7.65.15 LE Periodic Advertising Report Event
|
|
5473
|
+
'''
|
|
5474
|
+
|
|
5475
|
+
|
|
5476
|
+
# -----------------------------------------------------------------------------
|
|
5477
|
+
@HCI_LE_Meta_Event.event(
|
|
5478
|
+
[
|
|
5479
|
+
('sync_handle', 2),
|
|
5480
|
+
]
|
|
5481
|
+
)
|
|
5482
|
+
class HCI_LE_Periodic_Advertising_Sync_Lost_Event(HCI_LE_Meta_Event):
|
|
5483
|
+
'''
|
|
5484
|
+
See Bluetooth spec @ 7.7.65.16 LE Periodic Advertising Sync Lost Event
|
|
5485
|
+
'''
|
|
5486
|
+
|
|
5487
|
+
|
|
5274
5488
|
# -----------------------------------------------------------------------------
|
|
5275
5489
|
@HCI_LE_Meta_Event.event(
|
|
5276
5490
|
[
|
|
@@ -5336,6 +5550,30 @@ class HCI_LE_CIS_Request_Event(HCI_LE_Meta_Event):
|
|
|
5336
5550
|
'''
|
|
5337
5551
|
|
|
5338
5552
|
|
|
5553
|
+
# -----------------------------------------------------------------------------
|
|
5554
|
+
@HCI_LE_Meta_Event.event(
|
|
5555
|
+
[
|
|
5556
|
+
('sync_handle', 2),
|
|
5557
|
+
('num_bis', 1),
|
|
5558
|
+
('nse', 1),
|
|
5559
|
+
('iso_interval', 2),
|
|
5560
|
+
('bn', 1),
|
|
5561
|
+
('pto', 1),
|
|
5562
|
+
('irc', 1),
|
|
5563
|
+
('max_pdu', 2),
|
|
5564
|
+
('sdu_interval', 3),
|
|
5565
|
+
('max_sdu', 2),
|
|
5566
|
+
('phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}),
|
|
5567
|
+
('framing', 1),
|
|
5568
|
+
('encryption', 1),
|
|
5569
|
+
]
|
|
5570
|
+
)
|
|
5571
|
+
class HCI_LE_BIGInfo_Advertising_Report_Event(HCI_LE_Meta_Event):
|
|
5572
|
+
'''
|
|
5573
|
+
See Bluetooth spec @ 7.7.65.34 LE BIGInfo Advertising Report Event
|
|
5574
|
+
'''
|
|
5575
|
+
|
|
5576
|
+
|
|
5339
5577
|
# -----------------------------------------------------------------------------
|
|
5340
5578
|
@HCI_Event.event([('status', STATUS_SPEC)])
|
|
5341
5579
|
class HCI_Inquiry_Complete_Event(HCI_Event):
|
bumble/host.py
CHANGED
|
@@ -787,6 +787,10 @@ class Host(AbortableEventEmitter):
|
|
|
787
787
|
# Just use the same implementation as for the non-enhanced event for now
|
|
788
788
|
self.on_hci_le_connection_complete_event(event)
|
|
789
789
|
|
|
790
|
+
def on_hci_le_enhanced_connection_complete_v2_event(self, event):
|
|
791
|
+
# Just use the same implementation as for the v1 event for now
|
|
792
|
+
self.on_hci_le_enhanced_connection_complete_event(event)
|
|
793
|
+
|
|
790
794
|
def on_hci_connection_complete_event(self, event):
|
|
791
795
|
if event.status == hci.HCI_SUCCESS:
|
|
792
796
|
# Create/update the connection
|
|
@@ -905,6 +909,27 @@ class Host(AbortableEventEmitter):
|
|
|
905
909
|
event.num_completed_extended_advertising_events,
|
|
906
910
|
)
|
|
907
911
|
|
|
912
|
+
def on_hci_le_periodic_advertising_sync_established_event(self, event):
|
|
913
|
+
self.emit(
|
|
914
|
+
'periodic_advertising_sync_establishment',
|
|
915
|
+
event.status,
|
|
916
|
+
event.sync_handle,
|
|
917
|
+
event.advertising_sid,
|
|
918
|
+
event.advertiser_address,
|
|
919
|
+
event.advertiser_phy,
|
|
920
|
+
event.periodic_advertising_interval,
|
|
921
|
+
event.advertiser_clock_accuracy,
|
|
922
|
+
)
|
|
923
|
+
|
|
924
|
+
def on_hci_le_periodic_advertising_sync_lost_event(self, event):
|
|
925
|
+
self.emit('periodic_advertising_sync_loss', event.sync_handle)
|
|
926
|
+
|
|
927
|
+
def on_hci_le_periodic_advertising_report_event(self, event):
|
|
928
|
+
self.emit('periodic_advertising_report', event.sync_handle, event)
|
|
929
|
+
|
|
930
|
+
def on_hci_le_biginfo_advertising_report_event(self, event):
|
|
931
|
+
self.emit('biginfo_advertising_report', event.sync_handle, event)
|
|
932
|
+
|
|
908
933
|
def on_hci_le_cis_request_event(self, event):
|
|
909
934
|
self.emit(
|
|
910
935
|
'cis_request',
|
bumble/l2cap.py
CHANGED
|
@@ -70,6 +70,7 @@ L2CAP_LE_SIGNALING_CID = 0x05
|
|
|
70
70
|
|
|
71
71
|
L2CAP_MIN_LE_MTU = 23
|
|
72
72
|
L2CAP_MIN_BR_EDR_MTU = 48
|
|
73
|
+
L2CAP_MAX_BR_EDR_MTU = 65535
|
|
73
74
|
|
|
74
75
|
L2CAP_DEFAULT_MTU = 2048 # Default value for the MTU we are willing to accept
|
|
75
76
|
|
|
@@ -832,7 +833,9 @@ class ClassicChannel(EventEmitter):
|
|
|
832
833
|
|
|
833
834
|
# Wait for the connection to succeed or fail
|
|
834
835
|
try:
|
|
835
|
-
return await self.
|
|
836
|
+
return await self.connection.abort_on(
|
|
837
|
+
'disconnection', self.connection_result
|
|
838
|
+
)
|
|
836
839
|
finally:
|
|
837
840
|
self.connection_result = None
|
|
838
841
|
|
|
@@ -2225,7 +2228,7 @@ class ChannelManager:
|
|
|
2225
2228
|
# Connect
|
|
2226
2229
|
try:
|
|
2227
2230
|
await channel.connect()
|
|
2228
|
-
except
|
|
2231
|
+
except BaseException as e:
|
|
2229
2232
|
del connection_channels[source_cid]
|
|
2230
2233
|
raise e
|
|
2231
2234
|
|
bumble/pandora/host.py
CHANGED
|
@@ -28,6 +28,7 @@ from bumble.core import (
|
|
|
28
28
|
BT_PERIPHERAL_ROLE,
|
|
29
29
|
UUID,
|
|
30
30
|
AdvertisingData,
|
|
31
|
+
Appearance,
|
|
31
32
|
ConnectionError,
|
|
32
33
|
)
|
|
33
34
|
from bumble.device import (
|
|
@@ -988,8 +989,8 @@ class HostService(HostServicer):
|
|
|
988
989
|
dt.random_target_addresses.extend(
|
|
989
990
|
[data[i * 6 :: i * 6 + 6] for i in range(int(len(data) / 6))]
|
|
990
991
|
)
|
|
991
|
-
if
|
|
992
|
-
dt.appearance =
|
|
992
|
+
if appearance := cast(Appearance, ad.get(AdvertisingData.APPEARANCE)):
|
|
993
|
+
dt.appearance = int(appearance)
|
|
993
994
|
if i := cast(int, ad.get(AdvertisingData.ADVERTISING_INTERVAL)):
|
|
994
995
|
dt.advertising_interval = i
|
|
995
996
|
if s := cast(str, ad.get(AdvertisingData.URI)):
|
bumble/profiles/bap.py
CHANGED
|
@@ -25,6 +25,7 @@ import struct
|
|
|
25
25
|
import functools
|
|
26
26
|
import logging
|
|
27
27
|
from typing import Optional, List, Union, Type, Dict, Any, Tuple
|
|
28
|
+
from typing_extensions import Self
|
|
28
29
|
|
|
29
30
|
from bumble import core
|
|
30
31
|
from bumble import colors
|
|
@@ -32,6 +33,8 @@ from bumble import device
|
|
|
32
33
|
from bumble import hci
|
|
33
34
|
from bumble import gatt
|
|
34
35
|
from bumble import gatt_client
|
|
36
|
+
from bumble import utils
|
|
37
|
+
from bumble.profiles import le_audio
|
|
35
38
|
|
|
36
39
|
|
|
37
40
|
# -----------------------------------------------------------------------------
|
|
@@ -115,7 +118,7 @@ class ContextType(enum.IntFlag):
|
|
|
115
118
|
EMERGENCY_ALARM = 0x0800
|
|
116
119
|
|
|
117
120
|
|
|
118
|
-
class SamplingFrequency(
|
|
121
|
+
class SamplingFrequency(utils.OpenIntEnum):
|
|
119
122
|
'''Bluetooth Assigned Numbers, Section 6.12.5.1 - Sampling Frequency'''
|
|
120
123
|
|
|
121
124
|
# fmt: off
|
|
@@ -240,7 +243,7 @@ class SupportedFrameDuration(enum.IntFlag):
|
|
|
240
243
|
DURATION_10000_US_PREFERRED = 0b0010
|
|
241
244
|
|
|
242
245
|
|
|
243
|
-
class AnnouncementType(
|
|
246
|
+
class AnnouncementType(utils.OpenIntEnum):
|
|
244
247
|
'''Basic Audio Profile, 3.5.3. Additional Audio Stream Control Service requirements'''
|
|
245
248
|
|
|
246
249
|
# fmt: off
|
|
@@ -613,7 +616,7 @@ class CodecSpecificConfiguration:
|
|
|
613
616
|
* Basic Audio Profile, 4.3.2 - Codec_Specific_Capabilities LTV requirements
|
|
614
617
|
'''
|
|
615
618
|
|
|
616
|
-
class Type(
|
|
619
|
+
class Type(utils.OpenIntEnum):
|
|
617
620
|
# fmt: off
|
|
618
621
|
SAMPLING_FREQUENCY = 0x01
|
|
619
622
|
FRAME_DURATION = 0x02
|
|
@@ -725,6 +728,99 @@ class PacRecord:
|
|
|
725
728
|
)
|
|
726
729
|
|
|
727
730
|
|
|
731
|
+
@dataclasses.dataclass
|
|
732
|
+
class BroadcastAudioAnnouncement:
|
|
733
|
+
broadcast_id: int
|
|
734
|
+
|
|
735
|
+
@classmethod
|
|
736
|
+
def from_bytes(cls, data: bytes) -> Self:
|
|
737
|
+
return cls(int.from_bytes(data[:3], 'little'))
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
@dataclasses.dataclass
|
|
741
|
+
class BasicAudioAnnouncement:
|
|
742
|
+
@dataclasses.dataclass
|
|
743
|
+
class BIS:
|
|
744
|
+
index: int
|
|
745
|
+
codec_specific_configuration: CodecSpecificConfiguration
|
|
746
|
+
|
|
747
|
+
@dataclasses.dataclass
|
|
748
|
+
class CodecInfo:
|
|
749
|
+
coding_format: hci.CodecID
|
|
750
|
+
company_id: int
|
|
751
|
+
vendor_specific_codec_id: int
|
|
752
|
+
|
|
753
|
+
@classmethod
|
|
754
|
+
def from_bytes(cls, data: bytes) -> Self:
|
|
755
|
+
coding_format = hci.CodecID(data[0])
|
|
756
|
+
company_id = int.from_bytes(data[1:3], 'little')
|
|
757
|
+
vendor_specific_codec_id = int.from_bytes(data[3:5], 'little')
|
|
758
|
+
return cls(coding_format, company_id, vendor_specific_codec_id)
|
|
759
|
+
|
|
760
|
+
@dataclasses.dataclass
|
|
761
|
+
class Subgroup:
|
|
762
|
+
codec_id: BasicAudioAnnouncement.CodecInfo
|
|
763
|
+
codec_specific_configuration: CodecSpecificConfiguration
|
|
764
|
+
metadata: le_audio.Metadata
|
|
765
|
+
bis: List[BasicAudioAnnouncement.BIS]
|
|
766
|
+
|
|
767
|
+
presentation_delay: int
|
|
768
|
+
subgroups: List[BasicAudioAnnouncement.Subgroup]
|
|
769
|
+
|
|
770
|
+
@classmethod
|
|
771
|
+
def from_bytes(cls, data: bytes) -> Self:
|
|
772
|
+
presentation_delay = int.from_bytes(data[:3], 'little')
|
|
773
|
+
subgroups = []
|
|
774
|
+
offset = 4
|
|
775
|
+
for _ in range(data[3]):
|
|
776
|
+
num_bis = data[offset]
|
|
777
|
+
offset += 1
|
|
778
|
+
codec_id = cls.CodecInfo.from_bytes(data[offset : offset + 5])
|
|
779
|
+
offset += 5
|
|
780
|
+
codec_specific_configuration_length = data[offset]
|
|
781
|
+
offset += 1
|
|
782
|
+
codec_specific_configuration = data[
|
|
783
|
+
offset : offset + codec_specific_configuration_length
|
|
784
|
+
]
|
|
785
|
+
offset += codec_specific_configuration_length
|
|
786
|
+
metadata_length = data[offset]
|
|
787
|
+
offset += 1
|
|
788
|
+
metadata = le_audio.Metadata.from_bytes(
|
|
789
|
+
data[offset : offset + metadata_length]
|
|
790
|
+
)
|
|
791
|
+
offset += metadata_length
|
|
792
|
+
|
|
793
|
+
bis = []
|
|
794
|
+
for _ in range(num_bis):
|
|
795
|
+
bis_index = data[offset]
|
|
796
|
+
offset += 1
|
|
797
|
+
bis_codec_specific_configuration_length = data[offset]
|
|
798
|
+
offset += 1
|
|
799
|
+
bis_codec_specific_configuration = data[
|
|
800
|
+
offset : offset + bis_codec_specific_configuration_length
|
|
801
|
+
]
|
|
802
|
+
offset += bis_codec_specific_configuration_length
|
|
803
|
+
bis.append(
|
|
804
|
+
cls.BIS(
|
|
805
|
+
bis_index,
|
|
806
|
+
CodecSpecificConfiguration.from_bytes(
|
|
807
|
+
bis_codec_specific_configuration
|
|
808
|
+
),
|
|
809
|
+
)
|
|
810
|
+
)
|
|
811
|
+
|
|
812
|
+
subgroups.append(
|
|
813
|
+
cls.Subgroup(
|
|
814
|
+
codec_id,
|
|
815
|
+
CodecSpecificConfiguration.from_bytes(codec_specific_configuration),
|
|
816
|
+
metadata,
|
|
817
|
+
bis,
|
|
818
|
+
)
|
|
819
|
+
)
|
|
820
|
+
|
|
821
|
+
return cls(presentation_delay, subgroups)
|
|
822
|
+
|
|
823
|
+
|
|
728
824
|
# -----------------------------------------------------------------------------
|
|
729
825
|
# Server
|
|
730
826
|
# -----------------------------------------------------------------------------
|
|
@@ -744,9 +840,9 @@ class PublishedAudioCapabilitiesService(gatt.TemplateService):
|
|
|
744
840
|
supported_sink_context: ContextType,
|
|
745
841
|
available_source_context: ContextType,
|
|
746
842
|
available_sink_context: ContextType,
|
|
747
|
-
sink_pac: Sequence[PacRecord] =
|
|
843
|
+
sink_pac: Sequence[PacRecord] = (),
|
|
748
844
|
sink_audio_locations: Optional[AudioLocation] = None,
|
|
749
|
-
source_pac: Sequence[PacRecord] =
|
|
845
|
+
source_pac: Sequence[PacRecord] = (),
|
|
750
846
|
source_audio_locations: Optional[AudioLocation] = None,
|
|
751
847
|
) -> None:
|
|
752
848
|
characteristics = []
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Copyright 2024 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
# -----------------------------------------------------------------------------
|
|
16
|
+
# Imports
|
|
17
|
+
# -----------------------------------------------------------------------------
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
import dataclasses
|
|
20
|
+
from typing import List
|
|
21
|
+
from typing_extensions import Self
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# -----------------------------------------------------------------------------
|
|
25
|
+
# Classes
|
|
26
|
+
# -----------------------------------------------------------------------------
|
|
27
|
+
@dataclasses.dataclass
|
|
28
|
+
class Metadata:
|
|
29
|
+
@dataclasses.dataclass
|
|
30
|
+
class Entry:
|
|
31
|
+
tag: int
|
|
32
|
+
data: bytes
|
|
33
|
+
|
|
34
|
+
entries: List[Entry]
|
|
35
|
+
|
|
36
|
+
@classmethod
|
|
37
|
+
def from_bytes(cls, data: bytes) -> Self:
|
|
38
|
+
entries = []
|
|
39
|
+
offset = 0
|
|
40
|
+
length = len(data)
|
|
41
|
+
while length >= 2:
|
|
42
|
+
entry_length = data[offset]
|
|
43
|
+
entry_tag = data[offset + 1]
|
|
44
|
+
entry_data = data[offset + 2 : offset + 2 + entry_length - 1]
|
|
45
|
+
entries.append(cls.Entry(entry_tag, entry_data))
|
|
46
|
+
length -= entry_length
|
|
47
|
+
offset += entry_length
|
|
48
|
+
|
|
49
|
+
return cls(entries)
|
bumble/profiles/pbp.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Copyright 2024 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
# -----------------------------------------------------------------------------
|
|
16
|
+
# Imports
|
|
17
|
+
# -----------------------------------------------------------------------------
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
import dataclasses
|
|
20
|
+
import enum
|
|
21
|
+
from typing_extensions import Self
|
|
22
|
+
|
|
23
|
+
from bumble.profiles import le_audio
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# -----------------------------------------------------------------------------
|
|
27
|
+
# Classes
|
|
28
|
+
# -----------------------------------------------------------------------------
|
|
29
|
+
@dataclasses.dataclass
|
|
30
|
+
class PublicBroadcastAnnouncement:
|
|
31
|
+
class Features(enum.IntFlag):
|
|
32
|
+
ENCRYPTED = 1 << 0
|
|
33
|
+
STANDARD_QUALITY_CONFIGURATION = 1 << 1
|
|
34
|
+
HIGH_QUALITY_CONFIGURATION = 1 << 2
|
|
35
|
+
|
|
36
|
+
features: Features
|
|
37
|
+
metadata: le_audio.Metadata
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def from_bytes(cls, data: bytes) -> Self:
|
|
41
|
+
features = cls.Features(data[0])
|
|
42
|
+
metadata_length = data[1]
|
|
43
|
+
metadata_ltv = data[1 : 1 + metadata_length]
|
|
44
|
+
return cls(
|
|
45
|
+
features=features, metadata=le_audio.Metadata.from_bytes(metadata_ltv)
|
|
46
|
+
)
|