fix after rebase merge

This commit is contained in:
Gilles Boccon-Gibod
2024-08-09 18:12:55 -07:00
parent f66633459e
commit a699520188
9 changed files with 39 additions and 32 deletions

View File

@@ -495,6 +495,10 @@ async def run_assist(
print(color('Scanning for any broadcast', 'cyan')) print(color('Scanning for any broadcast', 'cyan'))
broadcast = await find_broadcast_by_name(device, broadcast_name) broadcast = await find_broadcast_by_name(device, broadcast_name)
if broadcast.broadcast_audio_announcement is None:
print(color('No broadcast audio announcement found', 'red'))
return
if ( if (
broadcast.basic_audio_announcement is None broadcast.basic_audio_announcement is None
or not broadcast.basic_audio_announcement.subgroups or not broadcast.basic_audio_announcement.subgroups

View File

@@ -484,7 +484,7 @@ class Speaker:
) )
) + bytes(bap.UnicastServerAdvertisingData()) ) + bytes(bap.UnicastServerAdvertisingData())
def on_pdu(pdu: HCI_IsoDataPacket, ase: bap.AseStateMachine): def on_pdu(pdu: HCI_IsoDataPacket, ase: ascs.AseStateMachine):
codec_config = ase.codec_specific_configuration codec_config = ase.codec_specific_configuration
assert isinstance(codec_config, bap.CodecSpecificConfiguration) assert isinstance(codec_config, bap.CodecSpecificConfiguration)
pcm = decode( pcm = decode(
@@ -494,7 +494,7 @@ class Speaker:
) )
self.device.abort_on('disconnection', self.ui_server.send_audio(pcm)) self.device.abort_on('disconnection', self.ui_server.send_audio(pcm))
def on_ase_state_change(ase: bap.AseStateMachine) -> None: def on_ase_state_change(ase: ascs.AseStateMachine) -> None:
if ase.state == ascs.AseStateMachine.State.STREAMING: if ase.state == ascs.AseStateMachine.State.STREAMING:
codec_config = ase.codec_specific_configuration codec_config = ase.codec_specific_configuration
assert isinstance(codec_config, bap.CodecSpecificConfiguration) assert isinstance(codec_config, bap.CodecSpecificConfiguration)

View File

@@ -988,7 +988,8 @@ class PeriodicAdvertisingSync(EventEmitter):
self.device.periodic_advertising_syncs.remove(self) self.device.periodic_advertising_syncs.remove(self)
async def transfer(self, connection: Connection, service_data: int = 0) -> None: async def transfer(self, connection: Connection, service_data: int = 0) -> None:
await connection.transfer_periodic_sync(self.sync_handle, service_data) if self.sync_handle is not None:
await connection.transfer_periodic_sync(self.sync_handle, service_data)
def on_establishment( def on_establishment(
self, self,
@@ -3606,7 +3607,8 @@ class Device(CompositeEventEmitter):
connection_handle=connection.handle, connection_handle=connection.handle,
service_data=service_data, service_data=service_data,
sync_handle=sync_handle, sync_handle=sync_handle,
), check_result=True ),
check_result=True,
) )
async def find_peer_by_name(self, name, transport=BT_LE_TRANSPORT): async def find_peer_by_name(self, name, transport=BT_LE_TRANSPORT):
@@ -3837,6 +3839,7 @@ class Device(CompositeEventEmitter):
if self.keystore is None: if self.keystore is None:
raise InvalidOperationError('no key store') raise InvalidOperationError('no key store')
logger.debug(f'Looking up key for {connection.peer_address}')
keys = await self.keystore.get(str(connection.peer_address)) keys = await self.keystore.get(str(connection.peer_address))
if keys is None: if keys is None:
raise InvalidOperationError('keys not found in key store') raise InvalidOperationError('keys not found in key store')
@@ -4315,6 +4318,12 @@ class Device(CompositeEventEmitter):
role: int, role: int,
connection_parameters: ConnectionParameters, connection_parameters: ConnectionParameters,
) -> None: ) -> None:
# Convert all-zeros addresses into None.
if self_resolvable_address == Address.ANY_RANDOM:
self_resolvable_address = None
if peer_resolvable_address == Address.ANY_RANDOM:
peer_resolvable_address = None
logger.debug( logger.debug(
f'*** Connection: [0x{connection_handle:04X}] ' f'*** Connection: [0x{connection_handle:04X}] '
f'{peer_address} {"" if role is None else HCI_Constant.role_name(role)}' f'{peer_address} {"" if role is None else HCI_Constant.role_name(role)}'
@@ -4374,12 +4383,6 @@ class Device(CompositeEventEmitter):
else self.random_address else self.random_address
) )
# Convert all-zeros addresses into None.
if self_resolvable_address == Address.ANY_RANDOM:
self_resolvable_address = None
if peer_resolvable_address == Address.ANY_RANDOM:
peer_resolvable_address = None
# Create a connection. # Create a connection.
connection = Connection( connection = Connection(
self, self,

View File

@@ -24,6 +24,7 @@ from typing import Any, Dict, List, Optional, Sequence, Tuple, Type, Union
from bumble import colors from bumble import colors
from bumble.profiles.bap import CodecSpecificConfiguration from bumble.profiles.bap import CodecSpecificConfiguration
from bumble.profiles import le_audio
from bumble import device from bumble import device
from bumble import gatt from bumble import gatt
from bumble import gatt_client from bumble import gatt_client
@@ -299,8 +300,7 @@ class AseStateMachine(gatt.Characteristic):
presentation_delay = 0 presentation_delay = 0
# Additional parameters in ENABLING, STREAMING, DISABLING State # Additional parameters in ENABLING, STREAMING, DISABLING State
# TODO: Parse this metadata = le_audio.Metadata()
metadata = b''
def __init__( def __init__(
self, self,
@@ -447,7 +447,7 @@ class AseStateMachine(gatt.Characteristic):
AseReasonCode.NONE, AseReasonCode.NONE,
) )
self.metadata = metadata self.metadata = le_audio.Metadata.from_bytes(metadata)
self.state = self.State.ENABLING self.state = self.State.ENABLING
return (AseResponseCode.SUCCESS, AseReasonCode.NONE) return (AseResponseCode.SUCCESS, AseReasonCode.NONE)
@@ -499,7 +499,7 @@ class AseStateMachine(gatt.Characteristic):
AseResponseCode.INVALID_ASE_STATE_MACHINE_TRANSITION, AseResponseCode.INVALID_ASE_STATE_MACHINE_TRANSITION,
AseReasonCode.NONE, AseReasonCode.NONE,
) )
self.metadata = metadata self.metadata = le_audio.Metadata.from_bytes(metadata)
return (AseResponseCode.SUCCESS, AseReasonCode.NONE) return (AseResponseCode.SUCCESS, AseReasonCode.NONE)
def on_release(self) -> Tuple[AseResponseCode, AseReasonCode]: def on_release(self) -> Tuple[AseResponseCode, AseReasonCode]:
@@ -576,8 +576,9 @@ class AseStateMachine(gatt.Characteristic):
self.State.STREAMING, self.State.STREAMING,
self.State.DISABLING, self.State.DISABLING,
): ):
metadata_bytes = bytes(self.metadata)
additional_parameters = ( additional_parameters = (
bytes([self.cig_id, self.cis_id, len(self.metadata)]) + self.metadata bytes([self.cig_id, self.cis_id, len(metadata_bytes)]) + metadata_bytes
) )
else: else:
additional_parameters = b'' additional_parameters = b''

View File

@@ -23,6 +23,7 @@ import struct
from typing import Optional, Sequence, Union from typing import Optional, Sequence, Union
from bumble.profiles.bap import AudioLocation, CodecSpecificCapabilities, ContextType from bumble.profiles.bap import AudioLocation, CodecSpecificCapabilities, ContextType
from bumble.profiles import le_audio
from bumble import gatt from bumble import gatt
from bumble import gatt_client from bumble import gatt_client
from bumble import hci from bumble import hci
@@ -37,10 +38,11 @@ logger = logging.getLogger(__name__)
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
@dataclasses.dataclass @dataclasses.dataclass
class PacRecord: class PacRecord:
'''Published Audio Capabilities Service, Table 3.2/3.4.'''
coding_format: hci.CodingFormat coding_format: hci.CodingFormat
codec_specific_capabilities: Union[CodecSpecificCapabilities, bytes] codec_specific_capabilities: Union[CodecSpecificCapabilities, bytes]
# TODO: Parse Metadata metadata: le_audio.Metadata = dataclasses.field(default_factory=le_audio.Metadata)
metadata: bytes = b''
@classmethod @classmethod
def from_bytes(cls, data: bytes) -> PacRecord: def from_bytes(cls, data: bytes) -> PacRecord:
@@ -53,7 +55,8 @@ class PacRecord:
] ]
offset += codec_specific_capabilities_size offset += codec_specific_capabilities_size
metadata_size = data[offset] metadata_size = data[offset]
metadata = data[offset : offset + metadata_size] offset += 1
metadata = le_audio.Metadata.from_bytes(data[offset : offset + metadata_size])
codec_specific_capabilities: Union[CodecSpecificCapabilities, bytes] codec_specific_capabilities: Union[CodecSpecificCapabilities, bytes]
if coding_format.codec_id == hci.CodecID.VENDOR_SPECIFIC: if coding_format.codec_id == hci.CodecID.VENDOR_SPECIFIC:
@@ -71,12 +74,13 @@ class PacRecord:
def __bytes__(self) -> bytes: def __bytes__(self) -> bytes:
capabilities_bytes = bytes(self.codec_specific_capabilities) capabilities_bytes = bytes(self.codec_specific_capabilities)
metadata_bytes = bytes(self.metadata)
return ( return (
bytes(self.coding_format) bytes(self.coding_format)
+ bytes([len(capabilities_bytes)]) + bytes([len(capabilities_bytes)])
+ capabilities_bytes + capabilities_bytes
+ bytes([len(self.metadata)]) + bytes([len(metadata_bytes)])
+ self.metadata + metadata_bytes
) )

View File

@@ -35,15 +35,13 @@ from bumble.hci import (
CodingFormat, CodingFormat,
OwnAddressType, OwnAddressType,
) )
from bumble.profiles.ascs import AudioStreamControlService
from bumble.profiles.bap import ( from bumble.profiles.bap import (
CodecSpecificCapabilities, CodecSpecificCapabilities,
ContextType, ContextType,
AudioLocation, AudioLocation,
SupportedSamplingFrequency, SupportedSamplingFrequency,
SupportedFrameDuration, SupportedFrameDuration,
PacRecord,
PublishedAudioCapabilitiesService,
AudioStreamControlService,
UnicastServerAdvertisingData, UnicastServerAdvertisingData,
) )
from bumble.profiles.mcp import ( from bumble.profiles.mcp import (
@@ -52,7 +50,7 @@ from bumble.profiles.mcp import (
MediaState, MediaState,
MediaControlPointOpcode, MediaControlPointOpcode,
) )
from bumble.profiles.pacs import PacRecord, PublishedAudioCapabilitiesService
from bumble.transport import open_transport_or_link from bumble.transport import open_transport_or_link
from typing import Optional from typing import Optional

View File

@@ -34,8 +34,8 @@ from bumble.hci import (
CodingFormat, CodingFormat,
HCI_IsoDataPacket, HCI_IsoDataPacket,
) )
from bumble.profiles.ascs import AseStateMachine, AudioStreamControlService
from bumble.profiles.bap import ( from bumble.profiles.bap import (
AseStateMachine,
UnicastServerAdvertisingData, UnicastServerAdvertisingData,
CodecSpecificConfiguration, CodecSpecificConfiguration,
CodecSpecificCapabilities, CodecSpecificCapabilities,
@@ -43,13 +43,10 @@ from bumble.profiles.bap import (
AudioLocation, AudioLocation,
SupportedSamplingFrequency, SupportedSamplingFrequency,
SupportedFrameDuration, SupportedFrameDuration,
PacRecord,
PublishedAudioCapabilitiesService,
AudioStreamControlService,
) )
from bumble.profiles.cap import CommonAudioServiceService from bumble.profiles.cap import CommonAudioServiceService
from bumble.profiles.csip import CoordinatedSetIdentificationService, SirkType from bumble.profiles.csip import CoordinatedSetIdentificationService, SirkType
from bumble.profiles.pacs import PacRecord, PublishedAudioCapabilitiesService
from bumble.transport import open_transport_or_link from bumble.transport import open_transport_or_link

View File

@@ -30,6 +30,7 @@ from bumble.hci import (
CodingFormat, CodingFormat,
OwnAddressType, OwnAddressType,
) )
from bumble.profiles.ascs import AudioStreamControlService
from bumble.profiles.bap import ( from bumble.profiles.bap import (
UnicastServerAdvertisingData, UnicastServerAdvertisingData,
CodecSpecificCapabilities, CodecSpecificCapabilities,
@@ -37,10 +38,8 @@ from bumble.profiles.bap import (
AudioLocation, AudioLocation,
SupportedSamplingFrequency, SupportedSamplingFrequency,
SupportedFrameDuration, SupportedFrameDuration,
PacRecord,
PublishedAudioCapabilitiesService,
AudioStreamControlService,
) )
from bumble.profiles.pacs import PacRecord, PublishedAudioCapabilitiesService
from bumble.profiles.cap import CommonAudioServiceService from bumble.profiles.cap import CommonAudioServiceService
from bumble.profiles.csip import CoordinatedSetIdentificationService, SirkType from bumble.profiles.csip import CoordinatedSetIdentificationService, SirkType
from bumble.profiles.vcp import VolumeControlService from bumble.profiles.vcp import VolumeControlService

View File

@@ -99,6 +99,7 @@ def test_operations() -> None:
def basic_broadcast_receive_state_check(brs: bass.BroadcastReceiveState) -> None: def basic_broadcast_receive_state_check(brs: bass.BroadcastReceiveState) -> None:
serialized = bytes(brs) serialized = bytes(brs)
parsed = bass.BroadcastReceiveState.from_bytes(serialized) parsed = bass.BroadcastReceiveState.from_bytes(serialized)
assert parsed is not None
assert bytes(parsed) == serialized assert bytes(parsed) == serialized