Ruff: Add and fix UP rules

This commit is contained in:
Josh Wu
2026-01-01 03:07:06 +08:00
parent 8e28f4e159
commit 3f643de4c1
102 changed files with 922 additions and 999 deletions

View File

@@ -22,9 +22,9 @@ import enum
import logging
import struct
from collections.abc import AsyncGenerator, Awaitable, Callable
from typing import Union
from typing import ClassVar
from typing_extensions import ClassVar, Self
from typing_extensions import Self
from bumble import utils
from bumble.codecs import AacAudioRtpPacket
@@ -266,7 +266,7 @@ class MediaCodecInformation:
@classmethod
def create(
cls, media_codec_type: int, data: bytes
) -> Union[MediaCodecInformation, bytes]:
) -> MediaCodecInformation | bytes:
if media_codec_type == CodecType.SBC:
return SbcMediaCodecInformation.from_bytes(data)
elif media_codec_type == CodecType.MPEG_2_4_AAC:
@@ -686,7 +686,7 @@ class SbcPacketSource:
# Prepare for next packets
sequence_number += 1
sequence_number &= 0xFFFF
sample_count += sum((frame.sample_count for frame in frames))
sample_count += sum(frame.sample_count for frame in frames)
frames = [frame]
frames_size = len(frame.payload)
else:

View File

@@ -12,7 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Union
from bumble import core
@@ -63,18 +62,18 @@ def tokenize_parameters(buffer: bytes) -> list[bytes]:
return [bytes(token) for token in tokens if len(token) > 0]
def parse_parameters(buffer: bytes) -> list[Union[bytes, list]]:
def parse_parameters(buffer: bytes) -> list[bytes | list]:
"""Parse the parameters using the comma and parenthesis separators.
Raises AtParsingError in case of invalid input string."""
tokens = tokenize_parameters(buffer)
accumulator: list[list] = [[]]
current: Union[bytes, list] = bytes()
current: bytes | list = b''
for token in tokens:
if token == b',':
accumulator[-1].append(current)
current = bytes()
current = b''
elif token == b'(':
accumulator.append([])
elif token == b')':

View File

@@ -29,15 +29,12 @@ import enum
import functools
import inspect
import struct
from collections.abc import Awaitable, Callable
from typing import (
TYPE_CHECKING,
Awaitable,
Callable,
ClassVar,
Generic,
Optional,
TypeVar,
Union,
)
from bumble import hci, utils
@@ -220,7 +217,7 @@ class ATT_PDU:
fields: ClassVar[hci.Fields] = ()
op_code: int = dataclasses.field(init=False)
name: str = dataclasses.field(init=False)
_payload: Optional[bytes] = dataclasses.field(default=None, init=False)
_payload: bytes | None = dataclasses.field(default=None, init=False)
@classmethod
def from_bytes(cls, pdu: bytes) -> ATT_PDU:
@@ -760,26 +757,24 @@ class AttributeValue(Generic[_T]):
def __init__(
self,
read: Union[
Callable[[Connection], _T],
Callable[[Connection], Awaitable[_T]],
None,
] = None,
write: Union[
Callable[[Connection, _T], None],
Callable[[Connection, _T], Awaitable[None]],
None,
] = None,
read: (
Callable[[Connection], _T] | Callable[[Connection], Awaitable[_T]] | None
) = None,
write: (
Callable[[Connection, _T], None]
| Callable[[Connection, _T], Awaitable[None]]
| None
) = None,
):
self._read = read
self._write = write
def read(self, connection: Connection) -> Union[_T, Awaitable[_T]]:
def read(self, connection: Connection) -> _T | Awaitable[_T]:
if self._read is None:
raise InvalidOperationError('AttributeValue has no read function')
return self._read(connection)
def write(self, connection: Connection, value: _T) -> Union[Awaitable[None], None]:
def write(self, connection: Connection, value: _T) -> Awaitable[None] | None:
if self._write is None:
raise InvalidOperationError('AttributeValue has no write function')
return self._write(connection, value)
@@ -828,13 +823,13 @@ class Attribute(utils.EventEmitter, Generic[_T]):
EVENT_READ = "read"
EVENT_WRITE = "write"
value: Union[AttributeValue[_T], _T, None]
value: AttributeValue[_T] | _T | None
def __init__(
self,
attribute_type: Union[str, bytes, UUID],
permissions: Union[str, Attribute.Permissions],
value: Union[AttributeValue[_T], _T, None] = None,
attribute_type: str | bytes | UUID,
permissions: str | Attribute.Permissions,
value: AttributeValue[_T] | _T | None = None,
) -> None:
utils.EventEmitter.__init__(self)
self.handle = 0
@@ -883,7 +878,7 @@ class Attribute(utils.EventEmitter, Generic[_T]):
error_code=ATT_INSUFFICIENT_AUTHORIZATION_ERROR, att_handle=self.handle
)
value: Union[_T, None]
value: _T | None
if isinstance(self.value, AttributeValue):
try:
read_value = self.value.read(connection)

View File

@@ -26,7 +26,8 @@ import logging
import pathlib
import sys
import wave
from typing import TYPE_CHECKING, AsyncGenerator, BinaryIO
from collections.abc import AsyncGenerator
from typing import TYPE_CHECKING, BinaryIO
from bumble.colors import color

View File

@@ -19,7 +19,6 @@ from __future__ import annotations
import enum
import struct
from typing import Union
from bumble import core, utils
@@ -166,7 +165,7 @@ class Frame:
def to_bytes(
self,
ctype_or_response: Union[CommandFrame.CommandType, ResponseFrame.ResponseCode],
ctype_or_response: CommandFrame.CommandType | ResponseFrame.ResponseCode,
) -> bytes:
# TODO: support extended subunit types and ids.
return (

View File

@@ -21,7 +21,6 @@ import logging
import struct
from collections.abc import Callable
from enum import IntEnum
from typing import Optional
from bumble import core, l2cap
from bumble.colors import color
@@ -147,7 +146,7 @@ class MessageAssembler:
class Protocol:
CommandHandler = Callable[[int, bytes], None]
command_handlers: dict[int, CommandHandler] # Command handlers, by PID
ResponseHandler = Callable[[int, Optional[bytes]], None]
ResponseHandler = Callable[[int, bytes | None], None]
response_handlers: dict[int, ResponseHandler] # Response handlers, by PID
next_transaction_label: int
message_assembler: MessageAssembler

View File

@@ -22,16 +22,13 @@ import enum
import logging
import time
import warnings
from collections.abc import AsyncGenerator, Awaitable, Iterable
from collections.abc import AsyncGenerator, Awaitable, Callable, Iterable
from dataclasses import dataclass, field
from typing import (
Any,
Callable,
ClassVar,
Optional,
SupportsBytes,
TypeVar,
Union,
cast,
)
@@ -184,7 +181,7 @@ class State(utils.OpenIntEnum):
# -----------------------------------------------------------------------------
async def find_avdtp_service_with_sdp_client(
sdp_client: sdp.Client,
) -> Optional[tuple[int, int]]:
) -> tuple[int, int] | None:
'''
Find an AVDTP service, using a connected SDP client, and return its version,
or None if none is found
@@ -214,7 +211,7 @@ async def find_avdtp_service_with_sdp_client(
# -----------------------------------------------------------------------------
async def find_avdtp_service_with_connection(
connection: device.Connection,
) -> Optional[tuple[int, int]]:
) -> tuple[int, int] | None:
'''
Find an AVDTP service, for a connection, and return its version,
or None if none is found
@@ -239,7 +236,7 @@ class RealtimeClock:
# -----------------------------------------------------------------------------
class MediaPacketPump:
pump_task: Optional[asyncio.Task]
pump_task: asyncio.Task | None
def __init__(
self, packets: AsyncGenerator, clock: RealtimeClock = RealtimeClock()
@@ -296,7 +293,7 @@ class MediaPacketPump:
# -----------------------------------------------------------------------------
class MessageAssembler:
message: Optional[bytes]
message: bytes | None
signal_identifier: SignalIdentifier
def __init__(self, callback: Callable[[int, Message], Any]) -> None:
@@ -470,15 +467,15 @@ class MediaCodecCapabilities(ServiceCapabilities):
media_type: MediaType
media_codec_type: a2dp.CodecType
media_codec_information: Union[bytes, SupportsBytes]
media_codec_information: bytes | SupportsBytes
# Override init to allow passing service_capabilities_bytes.
def __init__(
self,
media_type: MediaType,
media_codec_type: a2dp.CodecType,
media_codec_information: Union[bytes, SupportsBytes],
service_capabilities_bytes: Optional[bytes] = None,
media_codec_information: bytes | SupportsBytes,
service_capabilities_bytes: bytes | None = None,
) -> None:
self.media_type = media_type
self.media_codec_type = media_codec_type
@@ -553,7 +550,7 @@ class Message:
message_type: MessageType
signal_identifier: SignalIdentifier
_payload: Optional[bytes] = None
_payload: bytes | None = None
fields: ClassVar[hci.Fields] = ()
@property
@@ -607,7 +604,7 @@ class Message:
instance.signal_identifier = signal_identifier
return instance
def to_string(self, details: Union[str, Iterable[str]]) -> str:
def to_string(self, details: str | Iterable[str]) -> str:
base = color(
f'{self.signal_identifier.name}_{self.message_type.name}',
'yellow',
@@ -1266,9 +1263,9 @@ class Protocol(utils.EventEmitter):
local_endpoints: list[LocalStreamEndPoint]
remote_endpoints: dict[int, DiscoveredStreamEndPoint]
streams: dict[int, Stream]
transaction_results: list[Optional[asyncio.Future[Message]]]
transaction_results: list[asyncio.Future[Message] | None]
channel_connector: Callable[[], Awaitable[l2cap.ClassicChannel]]
channel_acceptor: Optional[Stream]
channel_acceptor: Stream | None
EVENT_OPEN = "open"
EVENT_CLOSE = "close"
@@ -1311,7 +1308,7 @@ class Protocol(utils.EventEmitter):
l2cap_channel.on(l2cap_channel.EVENT_OPEN, self.on_l2cap_channel_open)
l2cap_channel.on(l2cap_channel.EVENT_CLOSE, self.on_l2cap_channel_close)
def get_local_endpoint_by_seid(self, seid: int) -> Optional[LocalStreamEndPoint]:
def get_local_endpoint_by_seid(self, seid: int) -> LocalStreamEndPoint | None:
if 0 < seid <= len(self.local_endpoints):
return self.local_endpoints[seid - 1]
@@ -1385,7 +1382,7 @@ class Protocol(utils.EventEmitter):
def find_remote_sink_by_codec(
self, media_type: int, codec_type: int, vendor_id: int = 0, codec_id: int = 0
) -> Optional[DiscoveredStreamEndPoint]:
) -> DiscoveredStreamEndPoint | None:
for endpoint in self.remote_endpoints.values():
if (
not endpoint.in_use
@@ -1569,10 +1566,9 @@ class Protocol(utils.EventEmitter):
assert False # Should never reach this
async def get_capabilities(self, seid: int) -> Union[
Get_Capabilities_Response,
Get_All_Capabilities_Response,
]:
async def get_capabilities(
self, seid: int
) -> Get_Capabilities_Response | Get_All_Capabilities_Response:
if self.version > (1, 2):
return await self.send_command(Get_All_Capabilities_Command(seid))
@@ -1604,7 +1600,7 @@ class Protocol(utils.EventEmitter):
async def abort(self, seid: int) -> Abort_Response:
return await self.send_command(Abort_Command(seid))
def on_discover_command(self, command: Discover_Command) -> Optional[Message]:
def on_discover_command(self, command: Discover_Command) -> Message | None:
endpoint_infos = [
EndPointInfo(endpoint.seid, 0, endpoint.media_type, endpoint.tsep)
for endpoint in self.local_endpoints
@@ -1613,7 +1609,7 @@ class Protocol(utils.EventEmitter):
def on_get_capabilities_command(
self, command: Get_Capabilities_Command
) -> Optional[Message]:
) -> Message | None:
endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
if endpoint is None:
return Get_Capabilities_Reject(AVDTP_BAD_ACP_SEID_ERROR)
@@ -1622,7 +1618,7 @@ class Protocol(utils.EventEmitter):
def on_get_all_capabilities_command(
self, command: Get_All_Capabilities_Command
) -> Optional[Message]:
) -> Message | None:
endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
if endpoint is None:
return Get_All_Capabilities_Reject(AVDTP_BAD_ACP_SEID_ERROR)
@@ -1631,7 +1627,7 @@ class Protocol(utils.EventEmitter):
def on_set_configuration_command(
self, command: Set_Configuration_Command
) -> Optional[Message]:
) -> Message | None:
endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
if endpoint is None:
return Set_Configuration_Reject(error_code=AVDTP_BAD_ACP_SEID_ERROR)
@@ -1649,7 +1645,7 @@ class Protocol(utils.EventEmitter):
def on_get_configuration_command(
self, command: Get_Configuration_Command
) -> Optional[Message]:
) -> Message | None:
endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
if endpoint is None:
return Get_Configuration_Reject(AVDTP_BAD_ACP_SEID_ERROR)
@@ -1658,7 +1654,7 @@ class Protocol(utils.EventEmitter):
return endpoint.stream.on_get_configuration_command()
def on_reconfigure_command(self, command: Reconfigure_Command) -> Optional[Message]:
def on_reconfigure_command(self, command: Reconfigure_Command) -> Message | None:
endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
if endpoint is None:
return Reconfigure_Reject(error_code=AVDTP_BAD_ACP_SEID_ERROR)
@@ -1668,7 +1664,7 @@ class Protocol(utils.EventEmitter):
result = endpoint.stream.on_reconfigure_command(command.capabilities)
return result or Reconfigure_Response()
def on_open_command(self, command: Open_Command) -> Optional[Message]:
def on_open_command(self, command: Open_Command) -> Message | None:
endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
if endpoint is None:
return Open_Reject(AVDTP_BAD_ACP_SEID_ERROR)
@@ -1678,7 +1674,7 @@ class Protocol(utils.EventEmitter):
result = endpoint.stream.on_open_command()
return result or Open_Response()
def on_start_command(self, command: Start_Command) -> Optional[Message]:
def on_start_command(self, command: Start_Command) -> Message | None:
for seid in command.acp_seids:
endpoint = self.get_local_endpoint_by_seid(seid)
if endpoint is None:
@@ -1697,7 +1693,7 @@ class Protocol(utils.EventEmitter):
return Start_Response()
def on_suspend_command(self, command: Suspend_Command) -> Optional[Message]:
def on_suspend_command(self, command: Suspend_Command) -> Message | None:
for seid in command.acp_seids:
endpoint = self.get_local_endpoint_by_seid(seid)
if endpoint is None:
@@ -1716,7 +1712,7 @@ class Protocol(utils.EventEmitter):
return Suspend_Response()
def on_close_command(self, command: Close_Command) -> Optional[Message]:
def on_close_command(self, command: Close_Command) -> Message | None:
endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
if endpoint is None:
return Close_Reject(AVDTP_BAD_ACP_SEID_ERROR)
@@ -1726,7 +1722,7 @@ class Protocol(utils.EventEmitter):
result = endpoint.stream.on_close_command()
return result or Close_Response()
def on_abort_command(self, command: Abort_Command) -> Optional[Message]:
def on_abort_command(self, command: Abort_Command) -> Message | None:
endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
if endpoint is None or endpoint.stream is None:
return Abort_Response()
@@ -1736,7 +1732,7 @@ class Protocol(utils.EventEmitter):
def on_security_control_command(
self, command: Security_Control_Command
) -> Optional[Message]:
) -> Message | None:
endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
if endpoint is None:
return Security_Control_Reject(AVDTP_BAD_ACP_SEID_ERROR)
@@ -1744,7 +1740,7 @@ class Protocol(utils.EventEmitter):
result = endpoint.on_security_control_command(command.data)
return result or Security_Control_Response()
def on_delayreport_command(self, command: DelayReport_Command) -> Optional[Message]:
def on_delayreport_command(self, command: DelayReport_Command) -> Message | None:
endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
if endpoint is None:
return DelayReport_Reject(AVDTP_BAD_ACP_SEID_ERROR)
@@ -1825,7 +1821,7 @@ class Stream:
Pair of a local and a remote stream endpoint that can stream from one to the other
'''
rtp_channel: Optional[l2cap.ClassicChannel]
rtp_channel: l2cap.ClassicChannel | None
def change_state(self, state: State) -> None:
logger.debug(f'{self} state change -> {color(state.name, "cyan")}')
@@ -1914,7 +1910,7 @@ class Stream:
def on_set_configuration_command(
self, configuration: Iterable[ServiceCapabilities]
) -> Optional[Message]:
) -> Message | None:
if self.state != State.IDLE:
return Set_Configuration_Reject(error_code=AVDTP_BAD_STATE_ERROR)
@@ -1925,7 +1921,7 @@ class Stream:
self.change_state(State.CONFIGURED)
return None
def on_get_configuration_command(self) -> Optional[Message]:
def on_get_configuration_command(self) -> Message | None:
if self.state not in (
State.CONFIGURED,
State.OPEN,
@@ -1937,7 +1933,7 @@ class Stream:
def on_reconfigure_command(
self, configuration: Iterable[ServiceCapabilities]
) -> Optional[Message]:
) -> Message | None:
if self.state != State.OPEN:
return Reconfigure_Reject(error_code=AVDTP_BAD_STATE_ERROR)
@@ -1947,7 +1943,7 @@ class Stream:
return None
def on_open_command(self) -> Optional[Message]:
def on_open_command(self) -> Message | None:
if self.state != State.CONFIGURED:
return Open_Reject(AVDTP_BAD_STATE_ERROR)
@@ -1961,7 +1957,7 @@ class Stream:
self.change_state(State.OPEN)
return None
def on_start_command(self) -> Optional[Message]:
def on_start_command(self) -> Message | None:
if self.state != State.OPEN:
return Open_Reject(AVDTP_BAD_STATE_ERROR)
@@ -1977,7 +1973,7 @@ class Stream:
self.change_state(State.STREAMING)
return None
def on_suspend_command(self) -> Optional[Message]:
def on_suspend_command(self) -> Message | None:
if self.state != State.STREAMING:
return Open_Reject(AVDTP_BAD_STATE_ERROR)
@@ -1988,7 +1984,7 @@ class Stream:
self.change_state(State.OPEN)
return None
def on_close_command(self) -> Optional[Message]:
def on_close_command(self) -> Message | None:
if self.state not in (State.OPEN, State.STREAMING):
return Open_Reject(AVDTP_BAD_STATE_ERROR)
@@ -2007,7 +2003,7 @@ class Stream:
return None
def on_abort_command(self) -> Optional[Message]:
def on_abort_command(self) -> Message | None:
if self.rtp_channel is None:
# No need to wait
self.change_state(State.IDLE)
@@ -2120,7 +2116,7 @@ class DiscoveredStreamEndPoint(StreamEndPoint, StreamEndPointProxy):
# -----------------------------------------------------------------------------
class LocalStreamEndPoint(StreamEndPoint, utils.EventEmitter):
stream: Optional[Stream]
stream: Stream | None
EVENT_CONFIGURATION = "configuration"
EVENT_OPEN = "open"
@@ -2142,7 +2138,7 @@ class LocalStreamEndPoint(StreamEndPoint, utils.EventEmitter):
media_type: MediaType,
tsep: StreamEndPointType,
capabilities: Iterable[ServiceCapabilities],
configuration: Optional[Iterable[ServiceCapabilities]] = None,
configuration: Iterable[ServiceCapabilities] | None = None,
):
StreamEndPoint.__init__(self, seid, media_type, tsep, 0, capabilities)
utils.EventEmitter.__init__(self)
@@ -2161,13 +2157,13 @@ class LocalStreamEndPoint(StreamEndPoint, utils.EventEmitter):
def on_reconfigure_command(
self, command: Iterable[ServiceCapabilities]
) -> Optional[Message]:
) -> Message | None:
del command # unused.
return None
def on_set_configuration_command(
self, configuration: Iterable[ServiceCapabilities]
) -> Optional[Message]:
) -> Message | None:
logger.debug(
'<<< received configuration: '
f'{",".join([str(capability) for capability in configuration])}'
@@ -2176,34 +2172,34 @@ class LocalStreamEndPoint(StreamEndPoint, utils.EventEmitter):
self.emit(self.EVENT_CONFIGURATION)
return None
def on_get_configuration_command(self) -> Optional[Message]:
def on_get_configuration_command(self) -> Message | None:
return Get_Configuration_Response(self.configuration)
def on_open_command(self) -> Optional[Message]:
def on_open_command(self) -> Message | None:
self.emit(self.EVENT_OPEN)
return None
def on_start_command(self) -> Optional[Message]:
def on_start_command(self) -> Message | None:
self.emit(self.EVENT_START)
return None
def on_suspend_command(self) -> Optional[Message]:
def on_suspend_command(self) -> Message | None:
self.emit(self.EVENT_SUSPEND)
return None
def on_close_command(self) -> Optional[Message]:
def on_close_command(self) -> Message | None:
self.emit(self.EVENT_CLOSE)
return None
def on_abort_command(self) -> Optional[Message]:
def on_abort_command(self) -> Message | None:
self.emit(self.EVENT_ABORT)
return None
def on_delayreport_command(self, delay: int) -> Optional[Message]:
def on_delayreport_command(self, delay: int) -> Message | None:
self.emit(self.EVENT_DELAY_REPORT, delay)
return None
def on_security_control_command(self, data: bytes) -> Optional[Message]:
def on_security_control_command(self, data: bytes) -> Message | None:
self.emit(self.EVENT_SECURITY_CONTROL, data)
return None
@@ -2255,12 +2251,12 @@ class LocalSource(LocalStreamEndPoint):
self.emit(self.EVENT_STOP)
@override
def on_start_command(self) -> Optional[Message]:
def on_start_command(self) -> Message | None:
asyncio.create_task(self.start())
return None
@override
def on_suspend_command(self) -> Optional[Message]:
def on_suspend_command(self) -> Message | None:
asyncio.create_task(self.stop())
return None

View File

@@ -24,7 +24,7 @@ import logging
import struct
from collections.abc import AsyncIterator, Awaitable, Callable, Iterable, Sequence
from dataclasses import dataclass, field
from typing import ClassVar, Optional, SupportsBytes, TypeVar, Union
from typing import ClassVar, SupportsBytes, TypeVar
from bumble import avc, avctp, core, hci, l2cap, utils
from bumble.colors import color
@@ -196,7 +196,7 @@ def make_controller_service_sdp_records(
service_record_handle: int,
avctp_version: tuple[int, int] = (1, 4),
avrcp_version: tuple[int, int] = (1, 6),
supported_features: Union[int, ControllerFeatures] = 1,
supported_features: int | ControllerFeatures = 1,
) -> list[ServiceAttribute]:
avctp_version_int = avctp_version[0] << 8 | avctp_version[1]
avrcp_version_int = avrcp_version[0] << 8 | avrcp_version[1]
@@ -288,7 +288,7 @@ def make_target_service_sdp_records(
service_record_handle: int,
avctp_version: tuple[int, int] = (1, 4),
avrcp_version: tuple[int, int] = (1, 6),
supported_features: Union[int, TargetFeatures] = 0x23,
supported_features: int | TargetFeatures = 0x23,
) -> list[ServiceAttribute]:
# TODO: support a way to compute the supported features from a feature list
avctp_version_int = avctp_version[0] << 8 | avctp_version[1]
@@ -478,7 +478,7 @@ class BrowseableItem:
MEDIA_ELEMENT = 0x03
item_type: ClassVar[Type]
_payload: Optional[bytes] = None
_payload: bytes | None = None
subclasses: ClassVar[dict[Type, type[BrowseableItem]]] = {}
fields: ClassVar[hci.Fields] = ()
@@ -672,7 +672,7 @@ class PduAssembler:
6.3.1 AVRCP specific AV//C commands
"""
pdu_id: Optional[PduId]
pdu_id: PduId | None
payload: bytes
def __init__(self, callback: Callable[[PduId, bytes], None]) -> None:
@@ -725,7 +725,7 @@ class PduAssembler:
# -----------------------------------------------------------------------------
class Command:
pdu_id: ClassVar[PduId]
_payload: Optional[bytes] = None
_payload: bytes | None = None
_Command = TypeVar('_Command', bound='Command')
subclasses: ClassVar[dict[int, type[Command]]] = {}
@@ -1017,7 +1017,7 @@ class AddToNowPlayingCommand(Command):
# -----------------------------------------------------------------------------
class Response:
pdu_id: PduId
_payload: Optional[bytes] = None
_payload: bytes | None = None
fields: ClassVar[hci.Fields] = ()
subclasses: ClassVar[dict[PduId, type[Response]]] = {}
@@ -1079,7 +1079,7 @@ class NotImplementedResponse(Response):
class GetCapabilitiesResponse(Response):
pdu_id = PduId.GET_CAPABILITIES
capability_id: GetCapabilitiesCommand.CapabilityId
capabilities: Sequence[Union[SupportsBytes, bytes]]
capabilities: Sequence[SupportsBytes | bytes]
@classmethod
def from_parameters(cls, parameters: bytes) -> Response:
@@ -1092,7 +1092,7 @@ class GetCapabilitiesResponse(Response):
capability_id = GetCapabilitiesCommand.CapabilityId(parameters[0])
capability_count = parameters[1]
capabilities: list[Union[SupportsBytes, bytes]]
capabilities: list[SupportsBytes | bytes]
if capability_id == GetCapabilitiesCommand.CapabilityId.EVENTS_SUPPORTED:
capabilities = [EventId(parameters[2 + x]) for x in range(capability_count)]
else:
@@ -1363,7 +1363,7 @@ class AddToNowPlayingResponse(Response):
# -----------------------------------------------------------------------------
class Event:
event_id: EventId
_pdu: Optional[bytes] = None
_pdu: bytes | None = None
_Event = TypeVar('_Event', bound='Event')
subclasses: ClassVar[dict[int, type[Event]]] = {}
@@ -1436,13 +1436,13 @@ class PlayerApplicationSettingChangedEvent(Event):
attribute_id: ApplicationSetting.AttributeId = field(
metadata=ApplicationSetting.AttributeId.type_metadata(1)
)
value_id: Union[
ApplicationSetting.EqualizerOnOffStatus,
ApplicationSetting.RepeatModeStatus,
ApplicationSetting.ShuffleOnOffStatus,
ApplicationSetting.ScanOnOffStatus,
ApplicationSetting.GenericValue,
] = field(metadata=hci.metadata(1))
value_id: (
ApplicationSetting.EqualizerOnOffStatus
| ApplicationSetting.RepeatModeStatus
| ApplicationSetting.ShuffleOnOffStatus
| ApplicationSetting.ScanOnOffStatus
| ApplicationSetting.GenericValue
) = field(metadata=hci.metadata(1))
def __post_init__(self) -> None:
super().__post_init__()
@@ -1628,17 +1628,17 @@ class Protocol(utils.EventEmitter):
delegate: Delegate
send_transaction_label: int
command_pdu_assembler: PduAssembler
receive_command_state: Optional[ReceiveCommandState]
receive_command_state: ReceiveCommandState | None
response_pdu_assembler: PduAssembler
receive_response_state: Optional[ReceiveResponseState]
avctp_protocol: Optional[avctp.Protocol]
receive_response_state: ReceiveResponseState | None
avctp_protocol: avctp.Protocol | None
free_commands: asyncio.Queue
pending_commands: dict[int, PendingCommand] # Pending commands, by label
notification_listeners: dict[EventId, NotificationListener]
@staticmethod
def _check_vendor_dependent_frame(
frame: Union[avc.VendorDependentCommandFrame, avc.VendorDependentResponseFrame],
frame: avc.VendorDependentCommandFrame | avc.VendorDependentResponseFrame,
) -> bool:
if frame.company_id != AVRCP_BLUETOOTH_SIG_COMPANY_ID:
logger.debug("unsupported company id, ignoring")
@@ -1650,7 +1650,7 @@ class Protocol(utils.EventEmitter):
return True
def __init__(self, delegate: Optional[Delegate] = None) -> None:
def __init__(self, delegate: Delegate | None = None) -> None:
super().__init__()
self.delegate = delegate if delegate else Delegate()
self.command_pdu_assembler = PduAssembler(self._on_command_pdu)
@@ -2067,9 +2067,7 @@ class Protocol(utils.EventEmitter):
# TODO handle other types
self.send_not_implemented_response(transaction_label, command)
def _on_avctp_response(
self, transaction_label: int, payload: Optional[bytes]
) -> None:
def _on_avctp_response(self, transaction_label: int, payload: bytes | None) -> None:
response = avc.ResponseFrame.from_bytes(payload) if payload else None
if not isinstance(response, avc.ResponseFrame):
raise core.InvalidPacketError(
@@ -2176,7 +2174,7 @@ class Protocol(utils.EventEmitter):
# NOTE: with a small number of supported responses, a manual switch like this
# is Ok, but if/when more responses are supported, a lookup mechanism would be
# more appropriate.
response: Optional[Response] = None
response: Response | None = None
if response_code == avc.ResponseFrame.ResponseCode.REJECTED:
response = RejectedResponse(pdu_id=pdu_id, status_code=StatusCode(pdu[0]))
elif response_code == avc.ResponseFrame.ResponseCode.NOT_IMPLEMENTED:

View File

@@ -13,7 +13,6 @@
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
from functools import partial
from typing import Optional, Union
class ColorError(ValueError):
@@ -38,7 +37,7 @@ STYLES = (
)
ColorSpec = Union[str, int]
ColorSpec = str | int
def _join(*values: ColorSpec) -> str:
@@ -56,14 +55,14 @@ def _color_code(spec: ColorSpec, base: int) -> str:
elif isinstance(spec, int) and 0 <= spec <= 255:
return _join(base + 8, 5, spec)
else:
raise ColorError('Invalid color spec "%s"' % spec)
raise ColorError(f'Invalid color spec "{spec}"')
def color(
s: str,
fg: Optional[ColorSpec] = None,
bg: Optional[ColorSpec] = None,
style: Optional[str] = None,
fg: ColorSpec | None = None,
bg: ColorSpec | None = None,
style: str | None = None,
) -> str:
codes: list[ColorSpec] = []
@@ -76,10 +75,10 @@ def color(
if style_part in STYLES:
codes.append(STYLES.index(style_part))
else:
raise ColorError('Invalid style "%s"' % style_part)
raise ColorError(f'Invalid style "{style_part}"')
if codes:
return '\x1b[{0}m{1}\x1b[0m'.format(_join(*codes), s)
return f'\x1b[{_join(*codes)}m{s}\x1b[0m'
else:
return s

View File

@@ -23,7 +23,7 @@ import itertools
import logging
import random
import struct
from typing import TYPE_CHECKING, Any, Optional, Union, cast
from typing import TYPE_CHECKING, Any, cast
from bumble import hci, link, ll, lmp
from bumble import link as bumble_link
@@ -52,7 +52,7 @@ class CisLink:
handle: int
cis_id: int
cig_id: int
acl_connection: Optional[Connection] = None
acl_connection: Connection | None = None
data_paths: set[int] = dataclasses.field(default_factory=set)
@@ -73,7 +73,7 @@ class LegacyAdvertiser:
scan_response_data: bytes = b''
enabled: bool = False
timer_handle: Optional[asyncio.Handle] = None
timer_handle: asyncio.Handle | None = None
@property
def address(self) -> hci.Address:
@@ -124,12 +124,12 @@ class LegacyAdvertiser:
class AdvertisingSet:
controller: Controller
handle: int
parameters: Optional[hci.HCI_LE_Set_Extended_Advertising_Parameters_Command] = None
parameters: hci.HCI_LE_Set_Extended_Advertising_Parameters_Command | None = None
data: bytearray = dataclasses.field(default_factory=bytearray)
scan_response_data: bytearray = dataclasses.field(default_factory=bytearray)
enabled: bool = False
timer_handle: Optional[asyncio.Handle] = None
random_address: Optional[hci.Address] = None
timer_handle: asyncio.Handle | None = None
random_address: hci.Address | None = None
@property
def address(self) -> hci.Address | None:
@@ -223,7 +223,7 @@ class Connection:
# -----------------------------------------------------------------------------
class Controller:
hci_sink: Optional[TransportSink] = None
hci_sink: TransportSink | None = None
le_connections: dict[hci.Address, Connection] # LE Connections
classic_connections: dict[hci.Address, Connection] # Connections in BR/EDR
@@ -292,8 +292,8 @@ class Controller:
sync_flow_control: bool = False
local_name: str = 'Bumble'
advertising_interval: int = 2000
advertising_data: Optional[bytes] = None
advertising_timer_handle: Optional[asyncio.Handle] = None
advertising_data: bytes | None = None
advertising_timer_handle: asyncio.Handle | None = None
classic_scan_enable: int = 0
classic_allow_role_switch: bool = True
pending_le_connection: (
@@ -308,9 +308,9 @@ class Controller:
self,
name: str,
host_source=None,
host_sink: Optional[TransportSink] = None,
link: Optional[link.LocalLink] = None,
public_address: Optional[Union[bytes, str, hci.Address]] = None,
host_sink: TransportSink | None = None,
link: link.LocalLink | None = None,
public_address: bytes | str | hci.Address | None = None,
) -> None:
self.name = name
self.link = link or bumble_link.LocalLink()
@@ -351,18 +351,18 @@ class Controller:
)
@property
def host(self) -> Optional[TransportSink]:
def host(self) -> TransportSink | None:
return self.hci_sink
@host.setter
def host(self, host: Optional[TransportSink]) -> None:
def host(self, host: TransportSink | None) -> None:
'''
Sets the host (sink) for this controller, and set this controller as the
controller (sink) for the host
'''
self.set_packet_sink(host)
def set_packet_sink(self, sink: Optional[TransportSink]) -> None:
def set_packet_sink(self, sink: TransportSink | None) -> None:
'''
Method from the Packet Source interface
'''
@@ -373,7 +373,7 @@ class Controller:
return self._public_address
@public_address.setter
def public_address(self, address: Union[hci.Address, str]) -> None:
def public_address(self, address: hci.Address | str) -> None:
if isinstance(address, str):
address = hci.Address(address)
self._public_address = address
@@ -383,7 +383,7 @@ class Controller:
return self._random_address
@random_address.setter
def random_address(self, address: Union[hci.Address, str]) -> None:
def random_address(self, address: hci.Address | str) -> None:
if isinstance(address, str):
address = hci.Address(address)
self._random_address = address
@@ -415,7 +415,7 @@ class Controller:
def on_hci_command_packet(self, command: hci.HCI_Command) -> None:
handler_name = f'on_{command.name.lower()}'
handler = getattr(self, handler_name, self.on_hci_command)
result: Optional[bytes] = handler(command)
result: bytes | None = handler(command)
if isinstance(result, bytes):
self.send_hci_packet(
hci.HCI_Command_Complete_Event(
@@ -472,7 +472,7 @@ class Controller:
if handle not in current_handles
)
def find_connection_by_handle(self, handle: int) -> Optional[Connection]:
def find_connection_by_handle(self, handle: int) -> Connection | None:
for connection in itertools.chain(
self.le_connections.values(),
self.classic_connections.values(),
@@ -481,13 +481,13 @@ class Controller:
return connection
return None
def find_classic_sco_link_by_handle(self, handle: int) -> Optional[ScoLink]:
def find_classic_sco_link_by_handle(self, handle: int) -> ScoLink | None:
for connection in self.sco_links.values():
if connection.handle == handle:
return connection
return None
def find_iso_link_by_handle(self, handle: int) -> Optional[CisLink]:
def find_iso_link_by_handle(self, handle: int) -> CisLink | None:
return self.central_cis_links.get(handle) or self.peripheral_cis_links.get(
handle
)
@@ -1130,13 +1130,13 @@ class Controller:
############################################################
# HCI handlers
############################################################
def on_hci_command(self, command: hci.HCI_Command) -> Optional[bytes]:
def on_hci_command(self, command: hci.HCI_Command) -> bytes | None:
logger.warning(color(f'--- Unsupported command {command}', 'red'))
return bytes([hci.HCI_UNKNOWN_HCI_COMMAND_ERROR])
def on_hci_create_connection_command(
self, command: hci.HCI_Create_Connection_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.1.5 Create Connection command
'''
@@ -1186,7 +1186,7 @@ class Controller:
def on_hci_disconnect_command(
self, command: hci.HCI_Disconnect_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.1.6 Disconnect Command
'''
@@ -1256,7 +1256,7 @@ class Controller:
def on_hci_accept_connection_request_command(
self, command: hci.HCI_Accept_Connection_Request_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.1.8 Accept Connection Request command
'''
@@ -1314,7 +1314,7 @@ class Controller:
def on_hci_remote_name_request_command(
self, command: hci.HCI_Remote_Name_Request_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.1.19 Remote Name Request command
'''
@@ -1332,7 +1332,7 @@ class Controller:
def on_hci_enhanced_setup_synchronous_connection_command(
self, command: hci.HCI_Enhanced_Setup_Synchronous_Connection_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.1.45 Enhanced Setup Synchronous Connection command
'''
@@ -1389,7 +1389,7 @@ class Controller:
def on_hci_enhanced_accept_synchronous_connection_request_command(
self, command: hci.HCI_Enhanced_Accept_Synchronous_Connection_Request_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.1.46 Enhanced Accept Synchronous Connection Request command
'''
@@ -1427,7 +1427,7 @@ class Controller:
def on_hci_sniff_mode_command(
self, command: hci.HCI_Sniff_Mode_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.2.2 Sniff Mode command
'''
@@ -1460,7 +1460,7 @@ class Controller:
def on_hci_exit_sniff_mode_command(
self, command: hci.HCI_Exit_Sniff_Mode_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.2.3 Exit Sniff Mode command
'''
@@ -1494,7 +1494,7 @@ class Controller:
def on_hci_switch_role_command(
self, command: hci.HCI_Switch_Role_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.2.8 Switch hci.Role command
'''
@@ -1551,7 +1551,7 @@ class Controller:
def on_hci_set_event_mask_command(
self, command: hci.HCI_Set_Event_Mask_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.3.1 Set Event Mask Command
'''
@@ -1560,7 +1560,7 @@ class Controller:
)
return bytes([hci.HCI_SUCCESS])
def on_hci_reset_command(self, _command: hci.HCI_Reset_Command) -> Optional[bytes]:
def on_hci_reset_command(self, _command: hci.HCI_Reset_Command) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.3.2 Reset Command
'''
@@ -1569,7 +1569,7 @@ class Controller:
def on_hci_write_local_name_command(
self, command: hci.HCI_Write_Local_Name_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.3.11 Write Local Name Command
'''
@@ -1586,7 +1586,7 @@ class Controller:
def on_hci_read_local_name_command(
self, _command: hci.HCI_Read_Local_Name_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.3.12 Read Local Name Command
'''
@@ -1598,7 +1598,7 @@ class Controller:
def on_hci_read_class_of_device_command(
self, _command: hci.HCI_Read_Class_Of_Device_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.3.25 Read Class of Device Command
'''
@@ -1606,7 +1606,7 @@ class Controller:
def on_hci_write_class_of_device_command(
self, _command: hci.HCI_Write_Class_Of_Device_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.3.26 Write Class of Device Command
'''
@@ -1614,7 +1614,7 @@ class Controller:
def on_hci_read_synchronous_flow_control_enable_command(
self, _command: hci.HCI_Read_Synchronous_Flow_Control_Enable_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.3.36 Read Synchronous Flow Control Enable
Command
@@ -1627,7 +1627,7 @@ class Controller:
def on_hci_write_synchronous_flow_control_enable_command(
self, command: hci.HCI_Write_Synchronous_Flow_Control_Enable_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.3.37 Write Synchronous Flow Control Enable
Command
@@ -1643,7 +1643,7 @@ class Controller:
def on_hci_set_controller_to_host_flow_control_command(
self, _command: hci.HCI_Set_Controller_To_Host_Flow_Control_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.3.38 Set Controller To Host Flow Control
Command
@@ -1654,7 +1654,7 @@ class Controller:
def on_hci_host_buffer_size_command(
self, _command: hci.HCI_Host_Buffer_Size_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.3.39 Host Buffer Size Command
'''
@@ -1664,7 +1664,7 @@ class Controller:
def on_hci_write_extended_inquiry_response_command(
self, _command: hci.HCI_Write_Extended_Inquiry_Response_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.3.56 Write Extended Inquiry Response
Command
@@ -1673,7 +1673,7 @@ class Controller:
def on_hci_write_simple_pairing_mode_command(
self, _command: hci.HCI_Write_Simple_Pairing_Mode_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.3.59 Write Simple Pairing Mode Command
'''
@@ -1681,7 +1681,7 @@ class Controller:
def on_hci_set_event_mask_page_2_command(
self, command: hci.HCI_Set_Event_Mask_Page_2_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.3.69 Set Event Mask Page 2 Command
'''
@@ -1692,7 +1692,7 @@ class Controller:
def on_hci_read_le_host_support_command(
self, _command: hci.HCI_Read_LE_Host_Support_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.3.78 Write LE Host Support Command
'''
@@ -1700,7 +1700,7 @@ class Controller:
def on_hci_write_le_host_support_command(
self, _command: hci.HCI_Write_LE_Host_Support_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.3.79 Write LE Host Support Command
'''
@@ -1709,7 +1709,7 @@ class Controller:
def on_hci_write_authenticated_payload_timeout_command(
self, command: hci.HCI_Write_Authenticated_Payload_Timeout_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.3.94 Write Authenticated Payload Timeout
Command
@@ -1719,7 +1719,7 @@ class Controller:
def on_hci_read_local_version_information_command(
self, _command: hci.HCI_Read_Local_Version_Information_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.4.1 Read Local Version Information Command
'''
@@ -1735,7 +1735,7 @@ class Controller:
def on_hci_read_local_supported_commands_command(
self, _command: hci.HCI_Read_Local_Supported_Commands_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.4.2 Read Local Supported Commands Command
'''
@@ -1743,7 +1743,7 @@ class Controller:
def on_hci_read_local_supported_features_command(
self, _command: hci.HCI_Read_Local_Supported_Features_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.4.3 Read Local Supported Features Command
'''
@@ -1751,7 +1751,7 @@ class Controller:
def on_hci_read_local_extended_features_command(
self, command: hci.HCI_Read_Local_Extended_Features_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.4.4 Read Local Extended Features Command
'''
@@ -1774,7 +1774,7 @@ class Controller:
def on_hci_read_buffer_size_command(
self, _command: hci.HCI_Read_Buffer_Size_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.4.5 Read Buffer Size Command
'''
@@ -1789,7 +1789,7 @@ class Controller:
def on_hci_read_bd_addr_command(
self, _command: hci.HCI_Read_BD_ADDR_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.4.6 Read BD_ADDR Command
'''
@@ -1802,7 +1802,7 @@ class Controller:
def on_hci_le_set_default_subrate_command(
self, command: hci.HCI_LE_Set_Default_Subrate_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 6, Part E - 7.8.123 LE Set Event Mask Command
'''
@@ -1818,7 +1818,7 @@ class Controller:
def on_hci_le_subrate_request_command(
self, command: hci.HCI_LE_Subrate_Request_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 6, Part E - 7.8.124 LE Subrate Request command
'''
@@ -1852,7 +1852,7 @@ class Controller:
def on_hci_le_set_event_mask_command(
self, command: hci.HCI_LE_Set_Event_Mask_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.1 LE Set Event Mask Command
'''
@@ -1863,7 +1863,7 @@ class Controller:
def on_hci_le_read_buffer_size_command(
self, _command: hci.HCI_LE_Read_Buffer_Size_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.2 LE Read Buffer Size Command
'''
@@ -1876,7 +1876,7 @@ class Controller:
def on_hci_le_read_buffer_size_v2_command(
self, _command: hci.HCI_LE_Read_Buffer_Size_V2_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.2 LE Read Buffer Size Command
'''
@@ -1891,7 +1891,7 @@ class Controller:
def on_hci_le_read_local_supported_features_command(
self, _command: hci.HCI_LE_Read_Local_Supported_Features_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.3 LE Read Local Supported Features
Command
@@ -1900,7 +1900,7 @@ class Controller:
def on_hci_le_set_random_address_command(
self, command: hci.HCI_LE_Set_Random_Address_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.4 LE Set Random hci.Address Command
'''
@@ -1909,7 +1909,7 @@ class Controller:
def on_hci_le_set_advertising_parameters_command(
self, command: hci.HCI_LE_Set_Advertising_Parameters_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.5 LE Set Advertising Parameters Command
'''
@@ -1933,7 +1933,7 @@ class Controller:
def on_hci_le_read_advertising_physical_channel_tx_power_command(
self, _command: hci.HCI_LE_Read_Advertising_Physical_Channel_Tx_Power_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.6 LE Read Advertising Physical Channel
Tx Power Command
@@ -1942,7 +1942,7 @@ class Controller:
def on_hci_le_set_advertising_data_command(
self, command: hci.HCI_LE_Set_Advertising_Data_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.7 LE Set Advertising Data Command
'''
@@ -1952,7 +1952,7 @@ class Controller:
def on_hci_le_set_scan_response_data_command(
self, command: hci.HCI_LE_Set_Scan_Response_Data_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.8 LE Set Scan Response Data Command
'''
@@ -1961,7 +1961,7 @@ class Controller:
def on_hci_le_set_advertising_enable_command(
self, command: hci.HCI_LE_Set_Advertising_Enable_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.9 LE Set Advertising Enable Command
'''
@@ -1974,7 +1974,7 @@ class Controller:
def on_hci_le_set_scan_parameters_command(
self, command: hci.HCI_LE_Set_Scan_Parameters_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.10 LE Set Scan Parameters Command
'''
@@ -1990,7 +1990,7 @@ class Controller:
def on_hci_le_set_scan_enable_command(
self, command: hci.HCI_LE_Set_Scan_Enable_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.11 LE Set Scan Enable Command
'''
@@ -2000,7 +2000,7 @@ class Controller:
def on_hci_le_create_connection_command(
self, command: hci.HCI_LE_Create_Connection_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.12 LE Create Connection Command
'''
@@ -2035,7 +2035,7 @@ class Controller:
def on_hci_le_create_connection_cancel_command(
self, _command: hci.HCI_LE_Create_Connection_Cancel_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.13 LE Create Connection Cancel Command
'''
@@ -2043,7 +2043,7 @@ class Controller:
def on_hci_le_extended_create_connection_command(
self, command: hci.HCI_LE_Extended_Create_Connection_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.66 LE Extended Create Connection Command
'''
@@ -2074,7 +2074,7 @@ class Controller:
def on_hci_le_read_filter_accept_list_size_command(
self, _command: hci.HCI_LE_Read_Filter_Accept_List_Size_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.14 LE Read Filter Accept List Size
Command
@@ -2083,7 +2083,7 @@ class Controller:
def on_hci_le_clear_filter_accept_list_command(
self, _command: hci.HCI_LE_Clear_Filter_Accept_List_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.15 LE Clear Filter Accept List Command
'''
@@ -2091,7 +2091,7 @@ class Controller:
def on_hci_le_add_device_to_filter_accept_list_command(
self, _command: hci.HCI_LE_Add_Device_To_Filter_Accept_List_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.16 LE Add Device To Filter Accept List
Command
@@ -2100,7 +2100,7 @@ class Controller:
def on_hci_le_remove_device_from_filter_accept_list_command(
self, _command: hci.HCI_LE_Remove_Device_From_Filter_Accept_List_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.17 LE Remove Device From Filter Accept
List Command
@@ -2109,7 +2109,7 @@ class Controller:
def on_hci_write_scan_enable_command(
self, command: hci.HCI_Write_Scan_Enable_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.3.18 Write Scan Enable Command
'''
@@ -2118,7 +2118,7 @@ class Controller:
def on_hci_le_read_remote_features_command(
self, command: hci.HCI_LE_Read_Remote_Features_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.21 LE Read Remote Features Command
'''
@@ -2154,9 +2154,7 @@ class Controller:
)
return None
def on_hci_le_rand_command(
self, _command: hci.HCI_LE_Rand_Command
) -> Optional[bytes]:
def on_hci_le_rand_command(self, _command: hci.HCI_LE_Rand_Command) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.23 LE Rand Command
'''
@@ -2164,7 +2162,7 @@ class Controller:
def on_hci_le_enable_encryption_command(
self, command: hci.HCI_LE_Enable_Encryption_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.24 LE Enable Encryption Command
'''
@@ -2204,7 +2202,7 @@ class Controller:
def on_hci_le_read_supported_states_command(
self, _command: hci.HCI_LE_Read_Supported_States_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.27 LE Read Supported States Command
'''
@@ -2212,7 +2210,7 @@ class Controller:
def on_hci_le_read_suggested_default_data_length_command(
self, _command: hci.HCI_LE_Read_Suggested_Default_Data_Length_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.34 LE Read Suggested Default Data Length
Command
@@ -2226,7 +2224,7 @@ class Controller:
def on_hci_le_write_suggested_default_data_length_command(
self, command: hci.HCI_LE_Write_Suggested_Default_Data_Length_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.35 LE Write Suggested Default Data Length
Command
@@ -2238,7 +2236,7 @@ class Controller:
def on_hci_le_read_local_p_256_public_key_command(
self, _command: hci.HCI_LE_Read_Local_P_256_Public_Key_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.36 LE Read P-256 Public Key Command
'''
@@ -2247,7 +2245,7 @@ class Controller:
def on_hci_le_add_device_to_resolving_list_command(
self, _command: hci.HCI_LE_Add_Device_To_Resolving_List_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.38 LE Add Device To Resolving List
Command
@@ -2256,7 +2254,7 @@ class Controller:
def on_hci_le_clear_resolving_list_command(
self, _command: hci.HCI_LE_Clear_Resolving_List_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.40 LE Clear Resolving List Command
'''
@@ -2264,7 +2262,7 @@ class Controller:
def on_hci_le_read_resolving_list_size_command(
self, _command: hci.HCI_LE_Read_Resolving_List_Size_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.41 LE Read Resolving List Size Command
'''
@@ -2272,7 +2270,7 @@ class Controller:
def on_hci_le_set_address_resolution_enable_command(
self, command: hci.HCI_LE_Set_Address_Resolution_Enable_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.44 LE Set hci.Address Resolution Enable
Command
@@ -2288,7 +2286,7 @@ class Controller:
def on_hci_le_set_resolvable_private_address_timeout_command(
self, command: hci.HCI_LE_Set_Resolvable_Private_Address_Timeout_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.45 LE Set Resolvable Private hci.Address
Timeout Command
@@ -2298,7 +2296,7 @@ class Controller:
def on_hci_le_read_maximum_data_length_command(
self, _command: hci.HCI_LE_Read_Maximum_Data_Length_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.46 LE Read Maximum Data Length Command
'''
@@ -2313,7 +2311,7 @@ class Controller:
def on_hci_le_read_phy_command(
self, command: hci.HCI_LE_Read_PHY_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.47 LE Read PHY Command
'''
@@ -2327,7 +2325,7 @@ class Controller:
def on_hci_le_set_default_phy_command(
self, command: hci.HCI_LE_Set_Default_PHY_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.48 LE Set Default PHY Command
'''
@@ -2338,7 +2336,7 @@ class Controller:
def on_hci_le_set_advertising_set_random_address_command(
self, command: hci.HCI_LE_Set_Advertising_Set_Random_Address_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.52 LE Set Advertising Set Random hci.Address
Command
@@ -2353,7 +2351,7 @@ class Controller:
def on_hci_le_set_extended_advertising_parameters_command(
self, command: hci.HCI_LE_Set_Extended_Advertising_Parameters_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.53 LE Set Extended Advertising Parameters
Command
@@ -2369,7 +2367,7 @@ class Controller:
def on_hci_le_set_extended_advertising_data_command(
self, command: hci.HCI_LE_Set_Extended_Advertising_Data_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.54 LE Set Extended Advertising Data
Command
@@ -2393,7 +2391,7 @@ class Controller:
def on_hci_le_set_extended_scan_response_data_command(
self, command: hci.HCI_LE_Set_Extended_Scan_Response_Data_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.55 LE Set Extended Scan Response Data
Command
@@ -2417,7 +2415,7 @@ class Controller:
def on_hci_le_set_extended_advertising_enable_command(
self, command: hci.HCI_LE_Set_Extended_Advertising_Enable_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.56 LE Set Extended Advertising Enable
Command
@@ -2438,7 +2436,7 @@ class Controller:
def on_hci_le_remove_advertising_set_command(
self, command: hci.HCI_LE_Remove_Advertising_Set_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.59 LE Remove Advertising Set Command
'''
@@ -2449,7 +2447,7 @@ class Controller:
def on_hci_le_clear_advertising_sets_command(
self, _command: hci.HCI_LE_Clear_Advertising_Sets_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.60 LE Clear Advertising Sets Command
'''
@@ -2460,7 +2458,7 @@ class Controller:
def on_hci_le_read_maximum_advertising_data_length_command(
self, _command: hci.HCI_LE_Read_Maximum_Advertising_Data_Length_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.57 LE Read Maximum Advertising Data
Length Command
@@ -2469,7 +2467,7 @@ class Controller:
def on_hci_le_read_number_of_supported_advertising_sets_command(
self, _command: hci.HCI_LE_Read_Number_Of_Supported_Advertising_Sets_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.58 LE Read Number of Supported
Advertising Set Command
@@ -2478,7 +2476,7 @@ class Controller:
def on_hci_le_set_periodic_advertising_parameters_command(
self, _command: hci.HCI_LE_Set_Periodic_Advertising_Parameters_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.61 LE Set Periodic Advertising Parameters
Command
@@ -2487,7 +2485,7 @@ class Controller:
def on_hci_le_set_periodic_advertising_data_command(
self, _command: hci.HCI_LE_Set_Periodic_Advertising_Data_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.62 LE Set Periodic Advertising Data
Command
@@ -2496,7 +2494,7 @@ class Controller:
def on_hci_le_set_periodic_advertising_enable_command(
self, _command: hci.HCI_LE_Set_Periodic_Advertising_Enable_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.63 LE Set Periodic Advertising Enable
Command
@@ -2505,7 +2503,7 @@ class Controller:
def on_hci_le_read_transmit_power_command(
self, _command: hci.HCI_LE_Read_Transmit_Power_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.74 LE Read Transmit Power Command
'''
@@ -2513,7 +2511,7 @@ class Controller:
def on_hci_le_set_cig_parameters_command(
self, command: hci.HCI_LE_Set_CIG_Parameters_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.97 LE Set CIG Parameter Command
'''
@@ -2539,7 +2537,7 @@ class Controller:
def on_hci_le_create_cis_command(
self, command: hci.HCI_LE_Create_CIS_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.99 LE Create CIS Command
'''
@@ -2574,7 +2572,7 @@ class Controller:
def on_hci_le_remove_cig_command(
self, command: hci.HCI_LE_Remove_CIG_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.100 LE Remove CIG Command
'''
@@ -2591,7 +2589,7 @@ class Controller:
def on_hci_le_accept_cis_request_command(
self, command: hci.HCI_LE_Accept_CIS_Request_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.101 LE Accept CIS Request Command
'''
@@ -2620,7 +2618,7 @@ class Controller:
def on_hci_le_setup_iso_data_path_command(
self, command: hci.HCI_LE_Setup_ISO_Data_Path_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.109 LE Setup ISO Data Path Command
'''
@@ -2641,7 +2639,7 @@ class Controller:
def on_hci_le_remove_iso_data_path_command(
self, command: hci.HCI_LE_Remove_ISO_Data_Path_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.110 LE Remove ISO Data Path Command
'''
@@ -2667,7 +2665,7 @@ class Controller:
def on_hci_le_set_host_feature_command(
self, _command: hci.HCI_LE_Set_Host_Feature_Command
) -> Optional[bytes]:
) -> bytes | None:
'''
See Bluetooth spec Vol 4, Part E - 7.8.115 LE Set Host Feature command
'''

View File

@@ -20,14 +20,11 @@ from __future__ import annotations
import dataclasses
import enum
import struct
from collections.abc import Iterable
from typing import (
Any,
ClassVar,
Iterable,
Literal,
Optional,
Type,
Union,
cast,
overload,
)
@@ -102,7 +99,7 @@ class BaseError(BaseBumbleError):
def __init__(
self,
error_code: Optional[int],
error_code: int | None,
error_namespace: str = '',
error_name: str = '',
details: str = '',
@@ -215,11 +212,9 @@ class UUID:
UUIDS: list[UUID] = [] # Registry of all instances created
uuid_bytes: bytes
name: Optional[str]
name: str | None
def __init__(
self, uuid_str_or_int: Union[str, int], name: Optional[str] = None
) -> None:
def __init__(self, uuid_str_or_int: str | int, name: str | None = None) -> None:
if isinstance(uuid_str_or_int, int):
self.uuid_bytes = struct.pack('<H', uuid_str_or_int)
else:
@@ -252,7 +247,7 @@ class UUID:
return self
@classmethod
def from_bytes(cls, uuid_bytes: bytes, name: Optional[str] = None) -> UUID:
def from_bytes(cls, uuid_bytes: bytes, name: str | None = None) -> UUID:
if len(uuid_bytes) in (2, 4, 16):
self = cls.__new__(cls)
self.uuid_bytes = uuid_bytes
@@ -263,11 +258,11 @@ class UUID:
raise InvalidArgumentError('only 2, 4 and 16 bytes are allowed')
@classmethod
def from_16_bits(cls, uuid_16: int, name: Optional[str] = None) -> UUID:
def from_16_bits(cls, uuid_16: int, name: str | None = None) -> UUID:
return cls.from_bytes(struct.pack('<H', uuid_16), name)
@classmethod
def from_32_bits(cls, uuid_32: int, name: Optional[str] = None) -> UUID:
def from_32_bits(cls, uuid_32: int, name: str | None = None) -> UUID:
return cls.from_bytes(struct.pack('<I', uuid_32), name)
@classmethod
@@ -733,7 +728,7 @@ class ClassOfDevice:
MajorDeviceClass.HEALTH: HEALTH_MINOR_DEVICE_CLASS_LABELS,
}
_MINOR_DEVICE_CLASSES: ClassVar[dict[MajorDeviceClass, Type]] = {
_MINOR_DEVICE_CLASSES: ClassVar[dict[MajorDeviceClass, type]] = {
MajorDeviceClass.COMPUTER: ComputerMinorDeviceClass,
MajorDeviceClass.PHONE: PhoneMinorDeviceClass,
MajorDeviceClass.LAN_NETWORK_ACCESS_POINT: LanNetworkMinorDeviceClass,
@@ -748,17 +743,17 @@ class ClassOfDevice:
major_service_classes: MajorServiceClasses
major_device_class: MajorDeviceClass
minor_device_class: Union[
ComputerMinorDeviceClass,
PhoneMinorDeviceClass,
LanNetworkMinorDeviceClass,
AudioVideoMinorDeviceClass,
PeripheralMinorDeviceClass,
WearableMinorDeviceClass,
ToyMinorDeviceClass,
HealthMinorDeviceClass,
int,
]
minor_device_class: (
ComputerMinorDeviceClass
| PhoneMinorDeviceClass
| LanNetworkMinorDeviceClass
| AudioVideoMinorDeviceClass
| PeripheralMinorDeviceClass
| WearableMinorDeviceClass
| ToyMinorDeviceClass
| HealthMinorDeviceClass
| int
)
@classmethod
def from_int(cls, class_of_device: int) -> Self:
@@ -1547,7 +1542,7 @@ class DataType:
return f"{self.__class__.__name__}({self.value_string()})"
@classmethod
def from_advertising_data(cls, advertising_data: AdvertisingData) -> Optional[Self]:
def from_advertising_data(cls, advertising_data: AdvertisingData) -> Self | None:
if (data := advertising_data.get(cls.ad_type, raw=True)) is None:
return None
@@ -1575,16 +1570,16 @@ class DataType:
# -----------------------------------------------------------------------------
# Advertising Data
# -----------------------------------------------------------------------------
AdvertisingDataObject = Union[
list[UUID],
tuple[UUID, bytes],
bytes,
str,
int,
tuple[int, int],
tuple[int, bytes],
Appearance,
]
AdvertisingDataObject = (
list[UUID]
| tuple[UUID, bytes]
| bytes
| str
| int
| tuple[int, int]
| tuple[int, bytes]
| Appearance
)
class AdvertisingData:
@@ -1721,7 +1716,7 @@ class AdvertisingData:
def __init__(
self,
ad_structures: Optional[Iterable[Union[tuple[int, bytes], DataType]]] = None,
ad_structures: Iterable[tuple[int, bytes] | DataType] | None = None,
) -> None:
if ad_structures is None:
ad_structures = []
@@ -2019,7 +2014,7 @@ class AdvertisingData:
AdvertisingData.Type.LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS,
],
raw: Literal[False] = False,
) -> Optional[list[UUID]]: ...
) -> list[UUID] | None: ...
@overload
def get(
@@ -2030,7 +2025,7 @@ class AdvertisingData:
AdvertisingData.Type.SERVICE_DATA_128_BIT_UUID,
],
raw: Literal[False] = False,
) -> Optional[tuple[UUID, bytes]]: ...
) -> tuple[UUID, bytes] | None: ...
@overload
def get(
@@ -2042,7 +2037,7 @@ class AdvertisingData:
AdvertisingData.Type.BROADCAST_NAME,
],
raw: Literal[False] = False,
) -> Optional[Optional[str]]: ...
) -> str | None: ...
@overload
def get(
@@ -2054,38 +2049,36 @@ class AdvertisingData:
AdvertisingData.Type.CLASS_OF_DEVICE,
],
raw: Literal[False] = False,
) -> Optional[int]: ...
) -> int | None: ...
@overload
def get(
self,
type_id: Literal[AdvertisingData.Type.PERIPHERAL_CONNECTION_INTERVAL_RANGE,],
raw: Literal[False] = False,
) -> Optional[tuple[int, int]]: ...
) -> tuple[int, int] | None: ...
@overload
def get(
self,
type_id: Literal[AdvertisingData.Type.MANUFACTURER_SPECIFIC_DATA,],
raw: Literal[False] = False,
) -> Optional[tuple[int, bytes]]: ...
) -> tuple[int, bytes] | None: ...
@overload
def get(
self,
type_id: Literal[AdvertisingData.Type.APPEARANCE,],
raw: Literal[False] = False,
) -> Optional[Appearance]: ...
) -> Appearance | None: ...
@overload
def get(self, type_id: int, raw: Literal[True]) -> Optional[bytes]: ...
def get(self, type_id: int, raw: Literal[True]) -> bytes | None: ...
@overload
def get(
self, type_id: int, raw: bool = False
) -> Optional[AdvertisingDataObject]: ...
def get(self, type_id: int, raw: bool = False) -> AdvertisingDataObject | None: ...
def get(self, type_id: int, raw: bool = False) -> Optional[AdvertisingDataObject]:
def get(self, type_id: int, raw: bool = False) -> AdvertisingDataObject | None:
'''
Get advertising data as a simple AdvertisingDataObject object.

View File

@@ -29,7 +29,6 @@ import dataclasses
import functools
import secrets
import struct
from typing import Optional
from bumble import core
@@ -309,7 +308,7 @@ class _CMAC:
self.digest_size = mac_len
self._key = key
self._block_size = bs = 16
self._mac_tag: Optional[bytes] = None
self._mac_tag: bytes | None = None
self._update_after_digest = update_after_digest
# Section 5.3 of NIST SP 800 38B and Appendix B
@@ -348,7 +347,7 @@ class _CMAC:
self._last_ct = zero_block
# Last block that was encrypted with AES
self._last_pt: Optional[bytes] = None
self._last_pt: bytes | None = None
# Counter for total message size
self._data_size = 0

View File

@@ -25,7 +25,8 @@ from __future__ import annotations
import dataclasses
import math
import struct
from typing import Any, ClassVar, Sequence
from collections.abc import Sequence
from typing import Any, ClassVar
from typing_extensions import Self

View File

@@ -12,7 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Union
# -----------------------------------------------------------------------------
# Constants
@@ -167,12 +166,12 @@ class G722Decoder:
# The initial value in BLOCK 3H
self._band[1].det = 8
def decode_frame(self, encoded_data: Union[bytes, bytearray]) -> bytearray:
def decode_frame(self, encoded_data: bytes | bytearray) -> bytearray:
result_array = bytearray(len(encoded_data) * 4)
self.g722_decode(result_array, encoded_data)
return result_array
def g722_decode(self, result_array, encoded_data: Union[bytes, bytearray]) -> int:
def g722_decode(self, result_array, encoded_data: bytes | bytearray) -> int:
"""Decode the data frame using g722 decoder."""
result_length = 0

View File

@@ -25,19 +25,15 @@ import itertools
import json
import logging
import secrets
from collections.abc import Iterable, Sequence
from collections.abc import Awaitable, Callable, Iterable, Sequence
from contextlib import AsyncExitStack, asynccontextmanager, closing
from dataclasses import dataclass, field
from enum import Enum, IntEnum
from typing import (
TYPE_CHECKING,
Any,
Awaitable,
Callable,
ClassVar,
Optional,
TypeVar,
Union,
cast,
overload,
)
@@ -188,7 +184,7 @@ class Advertisement:
self.data = AdvertisingData.from_bytes(self.data_bytes)
@classmethod
def from_advertising_report(cls, report) -> Optional[Advertisement]:
def from_advertising_report(cls, report) -> Advertisement | None:
if isinstance(report, hci.HCI_LE_Advertising_Report_Event.Report):
return LegacyAdvertisement.from_advertising_report(report)
@@ -604,11 +600,11 @@ class AdvertisingSet(utils.EventEmitter):
device: Device
advertising_handle: int
auto_restart: bool
random_address: Optional[hci.Address]
random_address: hci.Address | None
advertising_parameters: AdvertisingParameters
advertising_data: bytes
scan_response_data: bytes
periodic_advertising_parameters: Optional[PeriodicAdvertisingParameters]
periodic_advertising_parameters: PeriodicAdvertisingParameters | None
periodic_advertising_data: bytes
selected_tx_power: int = 0
enabled: bool = False
@@ -855,7 +851,7 @@ class PeriodicAdvertisingSync(utils.EventEmitter):
TERMINATED = 6
_state: State
sync_handle: Optional[int]
sync_handle: int | None
advertiser_address: hci.Address
sid: int
skip: int
@@ -1282,7 +1278,7 @@ class Peer:
return mtu
async def discover_service(
self, uuid: Union[core.UUID, str]
self, uuid: core.UUID | str
) -> list[gatt_client.ServiceProxy]:
return await self.gatt_client.discover_service(uuid)
@@ -1298,8 +1294,8 @@ class Peer:
async def discover_characteristics(
self,
uuids: Iterable[Union[core.UUID, str]] = (),
service: Optional[gatt_client.ServiceProxy] = None,
uuids: Iterable[core.UUID | str] = (),
service: gatt_client.ServiceProxy | None = None,
) -> list[gatt_client.CharacteristicProxy[bytes]]:
return await self.gatt_client.discover_characteristics(
uuids=uuids, service=service
@@ -1307,9 +1303,9 @@ class Peer:
async def discover_descriptors(
self,
characteristic: Optional[gatt_client.CharacteristicProxy] = None,
start_handle: Optional[int] = None,
end_handle: Optional[int] = None,
characteristic: gatt_client.CharacteristicProxy | None = None,
start_handle: int | None = None,
end_handle: int | None = None,
):
return await self.gatt_client.discover_descriptors(
characteristic, start_handle, end_handle
@@ -1330,7 +1326,7 @@ class Peer:
async def subscribe(
self,
characteristic: gatt_client.CharacteristicProxy,
subscriber: Optional[Callable[[bytes], Any]] = None,
subscriber: Callable[[bytes], Any] | None = None,
prefer_notify: bool = True,
) -> None:
return await self.gatt_client.subscribe(
@@ -1340,25 +1336,23 @@ class Peer:
async def unsubscribe(
self,
characteristic: gatt_client.CharacteristicProxy,
subscriber: Optional[Callable[[bytes], Any]] = None,
subscriber: Callable[[bytes], Any] | None = None,
) -> None:
return await self.gatt_client.unsubscribe(characteristic, subscriber)
async def read_value(
self, attribute: Union[int, gatt_client.AttributeProxy]
) -> bytes:
async def read_value(self, attribute: int | gatt_client.AttributeProxy) -> bytes:
return await self.gatt_client.read_value(attribute)
async def write_value(
self,
attribute: Union[int, gatt_client.AttributeProxy],
attribute: int | gatt_client.AttributeProxy,
value: bytes,
with_response: bool = False,
) -> None:
return await self.gatt_client.write_value(attribute, value, with_response)
async def read_characteristics_by_uuid(
self, uuid: core.UUID, service: Optional[gatt_client.ServiceProxy] = None
self, uuid: core.UUID, service: gatt_client.ServiceProxy | None = None
) -> list[bytes]:
return await self.gatt_client.read_characteristics_by_uuid(uuid, service)
@@ -1368,7 +1362,7 @@ class Peer:
def get_characteristics_by_uuid(
self,
uuid: core.UUID,
service: Optional[Union[gatt_client.ServiceProxy, core.UUID]] = None,
service: gatt_client.ServiceProxy | core.UUID | None = None,
) -> list[gatt_client.CharacteristicProxy[bytes]]:
if isinstance(service, core.UUID):
return list(
@@ -1384,7 +1378,7 @@ class Peer:
def create_service_proxy(
self, proxy_class: type[_PROXY_CLASS]
) -> Optional[_PROXY_CLASS]:
) -> _PROXY_CLASS | None:
if proxy := proxy_class.from_client(self.gatt_client):
return cast(_PROXY_CLASS, proxy)
@@ -1392,7 +1386,7 @@ class Peer:
async def discover_service_and_create_proxy(
self, proxy_class: type[_PROXY_CLASS]
) -> Optional[_PROXY_CLASS]:
) -> _PROXY_CLASS | None:
# Discover the first matching service and its characteristics
services = await self.discover_service(proxy_class.SERVICE_CLASS.UUID)
if services:
@@ -1401,7 +1395,7 @@ class Peer:
return self.create_service_proxy(proxy_class)
return None
async def sustain(self, timeout: Optional[float] = None) -> None:
async def sustain(self, timeout: float | None = None) -> None:
await self.connection.sustain(timeout)
# [Classic only]
@@ -1444,7 +1438,7 @@ class ScoLink(utils.CompositeEventEmitter):
acl_connection: Connection
handle: int
link_type: int
sink: Optional[Callable[[hci.HCI_SynchronousDataPacket], Any]] = None
sink: Callable[[hci.HCI_SynchronousDataPacket], Any] | None = None
EVENT_DISCONNECTION: ClassVar[str] = "disconnection"
EVENT_DISCONNECTION_FAILURE: ClassVar[str] = "disconnection_failure"
@@ -1627,8 +1621,8 @@ class CisLink(utils.EventEmitter, _IsoLink):
cis_sync_delay: int = 0 # CIS sync delay, in microseconds
transport_latency_c_to_p: int = 0 # C->P transport latency, in microseconds
transport_latency_p_to_c: int = 0 # P->C transport latency, in microseconds
phy_c_to_p: Optional[hci.Phy] = None
phy_p_to_c: Optional[hci.Phy] = None
phy_c_to_p: hci.Phy | None = None
phy_p_to_c: hci.Phy | None = None
nse: int = 0
bn_c_to_p: int = 0
bn_p_to_c: int = 0
@@ -1716,11 +1710,11 @@ class Connection(utils.CompositeEventEmitter):
handle: int
transport: core.PhysicalTransport
self_address: hci.Address
self_resolvable_address: Optional[hci.Address]
self_resolvable_address: hci.Address | None
peer_address: hci.Address
peer_name: Optional[str]
peer_resolvable_address: Optional[hci.Address]
peer_le_features: Optional[hci.LeFeatureMask]
peer_name: str | None
peer_resolvable_address: hci.Address | None
peer_le_features: hci.LeFeatureMask | None
role: hci.Role
parameters: Parameters
encryption: int
@@ -1728,8 +1722,8 @@ class Connection(utils.CompositeEventEmitter):
authenticated: bool
sc: bool
gatt_client: gatt_client.Client
pairing_peer_io_capability: Optional[int]
pairing_peer_authentication_requirements: Optional[int]
pairing_peer_io_capability: int | None
pairing_peer_authentication_requirements: int | None
cs_configs: dict[int, ChannelSoundingConfig] # Config ID to Configuration
cs_procedures: dict[int, ChannelSoundingProcedure] # Config ID to Procedures
classic_mode: int = hci.HCI_Mode_Change_Event.Mode.ACTIVE
@@ -1831,9 +1825,9 @@ class Connection(utils.CompositeEventEmitter):
handle: int,
transport: core.PhysicalTransport,
self_address: hci.Address,
self_resolvable_address: Optional[hci.Address],
self_resolvable_address: hci.Address | None,
peer_address: hci.Address,
peer_resolvable_address: Optional[hci.Address],
peer_resolvable_address: hci.Address | None,
role: hci.Role,
parameters: Parameters,
):
@@ -1896,8 +1890,8 @@ class Connection(utils.CompositeEventEmitter):
) -> l2cap.LeCreditBasedChannel: ...
async def create_l2cap_channel(
self, spec: Union[l2cap.ClassicChannelSpec, l2cap.LeCreditBasedChannelSpec]
) -> Union[l2cap.ClassicChannel, l2cap.LeCreditBasedChannel]:
self, spec: l2cap.ClassicChannelSpec | l2cap.LeCreditBasedChannelSpec
) -> l2cap.ClassicChannel | l2cap.LeCreditBasedChannel:
return await self.device.create_l2cap_channel(connection=self, spec=spec)
async def disconnect(
@@ -1921,7 +1915,7 @@ class Connection(utils.CompositeEventEmitter):
async def switch_role(self, role: hci.Role) -> None:
return await self.device.switch_role(self, role)
async def sustain(self, timeout: Optional[float] = None) -> None:
async def sustain(self, timeout: float | None = None) -> None:
"""Idles the current task waiting for a disconnect or timeout"""
abort = asyncio.get_running_loop().create_future()
@@ -1965,8 +1959,8 @@ class Connection(utils.CompositeEventEmitter):
async def set_phy(
self,
tx_phys: Optional[Iterable[hci.Phy]] = None,
rx_phys: Optional[Iterable[hci.Phy]] = None,
tx_phys: Iterable[hci.Phy] | None = None,
rx_phys: Iterable[hci.Phy] | None = None,
phy_options: int = 0,
):
return await self.device.set_connection_phy(self, tx_phys, rx_phys, phy_options)
@@ -2070,12 +2064,12 @@ class DeviceConfiguration:
AdvertisingData([data_types.CompleteLocalName(DEVICE_DEFAULT_NAME)])
)
irk: bytes = bytes(16) # This really must be changed for any level of security
keystore: Optional[str] = None
keystore: str | None = None
address_resolution_offload: bool = False
address_generation_offload: bool = False
cis_enabled: bool = False
channel_sounding_enabled: bool = False
identity_address_type: Optional[int] = None
identity_address_type: int | None = None
io_capability: int = pairing.PairingDelegate.IoCapability.NO_OUTPUT_NO_INPUT
gap_service_enabled: bool = True
gatt_service_enabled: bool = True
@@ -2143,7 +2137,7 @@ class DeviceConfiguration:
setattr(self, key, value)
def load_from_file(self, filename: str) -> None:
with open(filename, 'r', encoding='utf-8') as file:
with open(filename, encoding='utf-8') as file:
self.load_from_dict(json.load(file))
@classmethod
@@ -2254,12 +2248,12 @@ class Device(utils.CompositeEventEmitter):
pending_connections: dict[hci.Address, Connection]
classic_pending_accepts: dict[
hci.Address,
list[asyncio.Future[Union[Connection, tuple[hci.Address, int, int]]]],
list[asyncio.Future[Connection | tuple[hci.Address, int, int]]],
]
advertisement_accumulators: dict[hci.Address, AdvertisementDataAccumulator]
periodic_advertising_syncs: list[PeriodicAdvertisingSync]
config: DeviceConfiguration
legacy_advertiser: Optional[LegacyAdvertiser]
legacy_advertiser: LegacyAdvertiser | None
sco_links: dict[int, ScoLink]
cis_links: dict[int, CisLink]
bigs: dict[int, Big]
@@ -2347,10 +2341,10 @@ class Device(utils.CompositeEventEmitter):
def __init__(
self,
name: Optional[str] = None,
address: Optional[hci.Address] = None,
config: Optional[DeviceConfiguration] = None,
host: Optional[Host] = None,
name: str | None = None,
address: hci.Address | None = None,
config: DeviceConfiguration | None = None,
host: Host | None = None,
) -> None:
super().__init__()
@@ -2407,7 +2401,7 @@ class Device(utils.CompositeEventEmitter):
self.le_simultaneous_enabled = config.le_simultaneous_enabled
self.le_privacy_enabled = config.le_privacy_enabled
self.le_rpa_timeout = config.le_rpa_timeout
self.le_rpa_periodic_update_task: Optional[asyncio.Task] = None
self.le_rpa_periodic_update_task: asyncio.Task | None = None
self.le_subrate_enabled = config.le_subrate_enabled
self.classic_enabled = config.classic_enabled
self.cis_enabled = config.cis_enabled
@@ -2431,8 +2425,8 @@ class Device(utils.CompositeEventEmitter):
# can be initialized from a config object, and for backward compatibility for
# client code that may set those values directly before calling
# start_advertising().
self.legacy_advertising_set: Optional[AdvertisingSet] = None
self.legacy_advertiser: Optional[LegacyAdvertiser] = None
self.legacy_advertising_set: AdvertisingSet | None = None
self.legacy_advertiser: LegacyAdvertiser | None = None
self.advertising_data = config.advertising_data
self.scan_response_data = config.scan_response_data
self.advertising_interval_min = config.advertising_interval_min
@@ -2550,7 +2544,7 @@ class Device(utils.CompositeEventEmitter):
def sdp_service_records(self, service_records):
self.sdp_server.service_records = service_records
def lookup_connection(self, connection_handle: int) -> Optional[Connection]:
def lookup_connection(self, connection_handle: int) -> Connection | None:
if connection := self.connections.get(connection_handle):
return connection
@@ -2559,9 +2553,9 @@ class Device(utils.CompositeEventEmitter):
def find_connection_by_bd_addr(
self,
bd_addr: hci.Address,
transport: Optional[int] = None,
transport: int | None = None,
check_address_type: bool = False,
) -> Optional[Connection]:
) -> Connection | None:
for connection in self.connections.values():
if bytes(connection.peer_address) == bytes(bd_addr):
if (
@@ -2576,7 +2570,7 @@ class Device(utils.CompositeEventEmitter):
def lookup_periodic_advertising_sync(
self, sync_handle: int
) -> Optional[PeriodicAdvertisingSync]:
) -> PeriodicAdvertisingSync | None:
return next(
(
sync
@@ -2614,8 +2608,8 @@ class Device(utils.CompositeEventEmitter):
async def create_l2cap_channel(
self,
connection: Connection,
spec: Union[l2cap.ClassicChannelSpec, l2cap.LeCreditBasedChannelSpec],
) -> Union[l2cap.ClassicChannel, l2cap.LeCreditBasedChannel]:
spec: l2cap.ClassicChannelSpec | l2cap.LeCreditBasedChannelSpec,
) -> l2cap.ClassicChannel | l2cap.LeCreditBasedChannel:
if isinstance(spec, l2cap.ClassicChannelSpec):
return await self.l2cap_channel_manager.create_classic_channel(
connection=connection, spec=spec
@@ -2629,25 +2623,25 @@ class Device(utils.CompositeEventEmitter):
def create_l2cap_server(
self,
spec: l2cap.ClassicChannelSpec,
handler: Optional[Callable[[l2cap.ClassicChannel], Any]] = None,
handler: Callable[[l2cap.ClassicChannel], Any] | None = None,
) -> l2cap.ClassicChannelServer: ...
@overload
def create_l2cap_server(
self,
spec: l2cap.LeCreditBasedChannelSpec,
handler: Optional[Callable[[l2cap.LeCreditBasedChannel], Any]] = None,
handler: Callable[[l2cap.LeCreditBasedChannel], Any] | None = None,
) -> l2cap.LeCreditBasedChannelServer: ...
def create_l2cap_server(
self,
spec: Union[l2cap.ClassicChannelSpec, l2cap.LeCreditBasedChannelSpec],
handler: Union[
Callable[[l2cap.ClassicChannel], Any],
Callable[[l2cap.LeCreditBasedChannel], Any],
None,
] = None,
) -> Union[l2cap.ClassicChannelServer, l2cap.LeCreditBasedChannelServer]:
spec: l2cap.ClassicChannelSpec | l2cap.LeCreditBasedChannelSpec,
handler: (
Callable[[l2cap.ClassicChannel], Any]
| Callable[[l2cap.LeCreditBasedChannel], Any]
| None
) = None,
) -> l2cap.ClassicChannelServer | l2cap.LeCreditBasedChannelServer:
if isinstance(spec, l2cap.ClassicChannelSpec):
return self.l2cap_channel_manager.create_classic_server(
spec=spec,
@@ -2949,13 +2943,13 @@ class Device(utils.CompositeEventEmitter):
async def start_advertising(
self,
advertising_type: AdvertisingType = AdvertisingType.UNDIRECTED_CONNECTABLE_SCANNABLE,
target: Optional[hci.Address] = None,
target: hci.Address | None = None,
own_address_type: hci.OwnAddressType = hci.OwnAddressType.RANDOM,
auto_restart: bool = False,
advertising_data: Optional[bytes] = None,
scan_response_data: Optional[bytes] = None,
advertising_interval_min: Optional[float] = None,
advertising_interval_max: Optional[float] = None,
advertising_data: bytes | None = None,
scan_response_data: bytes | None = None,
advertising_interval_min: float | None = None,
advertising_interval_max: float | None = None,
) -> None:
"""Start legacy advertising.
@@ -3059,11 +3053,11 @@ class Device(utils.CompositeEventEmitter):
async def create_advertising_set(
self,
advertising_parameters: Optional[AdvertisingParameters] = None,
random_address: Optional[hci.Address] = None,
advertising_parameters: AdvertisingParameters | None = None,
random_address: hci.Address | None = None,
advertising_data: bytes = b'',
scan_response_data: bytes = b'',
periodic_advertising_parameters: Optional[PeriodicAdvertisingParameters] = None,
periodic_advertising_parameters: PeriodicAdvertisingParameters | None = None,
periodic_advertising_data: bytes = b'',
auto_start: bool = True,
auto_restart: bool = False,
@@ -3599,13 +3593,13 @@ class Device(utils.CompositeEventEmitter):
async def connect(
self,
peer_address: Union[hci.Address, str],
peer_address: hci.Address | str,
transport: core.PhysicalTransport = PhysicalTransport.LE,
connection_parameters_preferences: Optional[
dict[hci.Phy, ConnectionParametersPreferences]
] = None,
connection_parameters_preferences: (
dict[hci.Phy, ConnectionParametersPreferences] | None
) = None,
own_address_type: hci.OwnAddressType = hci.OwnAddressType.RANDOM,
timeout: Optional[float] = DEVICE_DEFAULT_CONNECT_TIMEOUT,
timeout: float | None = DEVICE_DEFAULT_CONNECT_TIMEOUT,
always_resolve: bool = False,
) -> Connection:
'''
@@ -3915,9 +3909,9 @@ class Device(utils.CompositeEventEmitter):
async def accept(
self,
peer_address: Union[hci.Address, str] = hci.Address.ANY,
peer_address: hci.Address | str = hci.Address.ANY,
role: hci.Role = hci.Role.PERIPHERAL,
timeout: Optional[float] = DEVICE_DEFAULT_CONNECT_TIMEOUT,
timeout: float | None = DEVICE_DEFAULT_CONNECT_TIMEOUT,
) -> Connection:
'''
Wait and accept any incoming connection or a connection from `peer_address` when
@@ -4041,7 +4035,7 @@ class Device(utils.CompositeEventEmitter):
self.pending_connections.pop(peer_address, None)
@asynccontextmanager
async def connect_as_gatt(self, peer_address: Union[hci.Address, str]):
async def connect_as_gatt(self, peer_address: hci.Address | str):
async with AsyncExitStack() as stack:
connection = await stack.enter_async_context(
await self.connect(peer_address)
@@ -4088,7 +4082,7 @@ class Device(utils.CompositeEventEmitter):
)
async def disconnect(
self, connection: Union[Connection, ScoLink, CisLink], reason: int
self, connection: Connection | ScoLink | CisLink, reason: int
) -> None:
# Create a future so that we can wait for the disconnection's result
pending_disconnection = asyncio.get_running_loop().create_future()
@@ -4227,8 +4221,8 @@ class Device(utils.CompositeEventEmitter):
async def set_connection_phy(
self,
connection: Connection,
tx_phys: Optional[Iterable[hci.Phy]] = None,
rx_phys: Optional[Iterable[hci.Phy]] = None,
tx_phys: Iterable[hci.Phy] | None = None,
rx_phys: Iterable[hci.Phy] | None = None,
phy_options: int = 0,
):
if not self.host.supports_command(hci.HCI_LE_SET_PHY_COMMAND):
@@ -4252,8 +4246,8 @@ class Device(utils.CompositeEventEmitter):
async def set_default_phy(
self,
tx_phys: Optional[Iterable[hci.Phy]] = None,
rx_phys: Optional[Iterable[hci.Phy]] = None,
tx_phys: Iterable[hci.Phy] | None = None,
rx_phys: Iterable[hci.Phy] | None = None,
):
all_phys_bits = (1 if tx_phys is None else 0) | (
(1 if rx_phys is None else 0) << 1
@@ -4307,7 +4301,7 @@ class Device(utils.CompositeEventEmitter):
if local_name == name:
peer_address.set_result(address)
listener: Optional[Callable[..., None]] = None
listener: Callable[..., None] | None = None
was_scanning = self.scanning
was_discovering = self.discovering
try:
@@ -4421,7 +4415,7 @@ class Device(utils.CompositeEventEmitter):
async def get_long_term_key(
self, connection_handle: int, rand: bytes, ediv: int
) -> Optional[bytes]:
) -> bytes | None:
if (connection := self.lookup_connection(connection_handle)) is None:
return None
@@ -4445,7 +4439,7 @@ class Device(utils.CompositeEventEmitter):
return keys.ltk_peripheral.value
return None
async def get_link_key(self, address: hci.Address) -> Optional[bytes]:
async def get_link_key(self, address: hci.Address) -> bytes | None:
if self.keystore is None:
return None
@@ -4583,7 +4577,7 @@ class Device(utils.CompositeEventEmitter):
await connection.cancel_on_disconnection(pending_role_change)
# [Classic only]
async def request_remote_name(self, remote: Union[hci.Address, Connection]) -> str:
async def request_remote_name(self, remote: hci.Address | Connection) -> str:
# Set up event handlers
pending_name: asyncio.Future[str] = asyncio.get_running_loop().create_future()
@@ -5153,7 +5147,7 @@ class Device(utils.CompositeEventEmitter):
self,
connection: Connection,
attribute: Attribute,
value: Optional[Any] = None,
value: Any | None = None,
force: bool = False,
) -> None:
"""
@@ -5172,7 +5166,7 @@ class Device(utils.CompositeEventEmitter):
await self.gatt_server.notify_subscriber(connection, attribute, value, force)
async def notify_subscribers(
self, attribute: Attribute, value: Optional[Any] = None, force: bool = False
self, attribute: Attribute, value: Any | None = None, force: bool = False
) -> None:
"""
Send a notification to all the subscribers of an attribute.
@@ -5192,7 +5186,7 @@ class Device(utils.CompositeEventEmitter):
self,
connection: Connection,
attribute: Attribute,
value: Optional[Any] = None,
value: Any | None = None,
force: bool = False,
):
"""
@@ -5213,7 +5207,7 @@ class Device(utils.CompositeEventEmitter):
await self.gatt_server.indicate_subscriber(connection, attribute, value, force)
async def indicate_subscribers(
self, attribute: Attribute, value: Optional[Any] = None, force: bool = False
self, attribute: Attribute, value: Any | None = None, force: bool = False
):
"""
Send an indication to all the subscribers of an attribute.
@@ -5437,8 +5431,8 @@ class Device(utils.CompositeEventEmitter):
self,
connection_handle: int,
peer_address: hci.Address,
self_resolvable_address: Optional[hci.Address],
peer_resolvable_address: Optional[hci.Address],
self_resolvable_address: hci.Address | None,
peer_resolvable_address: hci.Address | None,
role: hci.Role,
connection_interval: int,
peripheral_latency: int,
@@ -5473,7 +5467,7 @@ class Device(utils.CompositeEventEmitter):
peer_address = resolved_address
self_address = None
own_address_type: Optional[hci.OwnAddressType] = None
own_address_type: hci.OwnAddressType | None = None
if role == hci.Role.CENTRAL:
own_address_type = self.connect_own_address_type
assert own_address_type is not None
@@ -5938,7 +5932,7 @@ class Device(utils.CompositeEventEmitter):
@host_event_handler
@try_with_connection_from_address
def on_remote_name(
self, connection: Optional[Connection], address: hci.Address, remote_name: bytes
self, connection: Connection | None, address: hci.Address, remote_name: bytes
):
# Try to decode the name
try:
@@ -5957,7 +5951,7 @@ class Device(utils.CompositeEventEmitter):
@host_event_handler
@try_with_connection_from_address
def on_remote_name_failure(
self, connection: Optional[Connection], address: hci.Address, error: int
self, connection: Connection | None, address: hci.Address, error: int
):
if connection:
connection.emit(connection.EVENT_REMOTE_NAME_FAILURE, error)
@@ -6402,7 +6396,7 @@ class Device(utils.CompositeEventEmitter):
@host_event_handler
@try_with_connection_from_address
def on_role_change_failure(
self, connection: Optional[Connection], address: hci.Address, error: int
self, connection: Connection | None, address: hci.Address, error: int
):
if connection:
connection.emit(connection.EVENT_ROLE_CHANGE_FAILURE, error)
@@ -6426,7 +6420,7 @@ class Device(utils.CompositeEventEmitter):
def on_pairing(
self,
connection: Connection,
identity_address: Optional[hci.Address],
identity_address: hci.Address | None,
keys: PairingKeys,
sc: bool,
) -> None:

View File

@@ -24,7 +24,8 @@ from __future__ import annotations
import logging
import pathlib
import platform
from typing import TYPE_CHECKING, Iterable, Optional
from collections.abc import Iterable
from typing import TYPE_CHECKING
from bumble.drivers import intel, rtk
from bumble.drivers.common import Driver
@@ -41,7 +42,7 @@ logger = logging.getLogger(__name__)
# -----------------------------------------------------------------------------
# Functions
# -----------------------------------------------------------------------------
async def get_driver_for_host(host: Host) -> Optional[Driver]:
async def get_driver_for_host(host: Host) -> Driver | None:
"""Probe diver classes until one returns a valid instance for a host, or none is
found.
If a "driver" HCI metadata entry is present, only that driver class will be probed.

View File

@@ -29,7 +29,7 @@ import os
import pathlib
import platform
import struct
from typing import TYPE_CHECKING, Any, Optional
from typing import TYPE_CHECKING, Any
from bumble import core, hci, utils
from bumble.drivers import common
@@ -353,8 +353,8 @@ class Driver(common.Driver):
self.reset_complete = asyncio.Event()
# Parse configuration options from the driver name.
self.ddc_addon: Optional[bytes] = None
self.ddc_override: Optional[bytes] = None
self.ddc_addon: bytes | None = None
self.ddc_override: bytes | None = None
driver = host.hci_metadata.get("driver")
if driver is not None and driver.startswith("intel/"):
for key, value in [
@@ -602,7 +602,7 @@ class Driver(common.Driver):
await self.load_ddc_if_any(firmware_base_name)
async def load_ddc_if_any(self, firmware_base_name: Optional[str] = None) -> None:
async def load_ddc_if_any(self, firmware_base_name: str | None = None) -> None:
"""
Check for and load any Device Data Configuration (DDC) blobs.

View File

@@ -28,7 +28,8 @@ import enum
import functools
import logging
import struct
from typing import Iterable, Optional, Sequence, TypeVar, Union
from collections.abc import Iterable, Sequence
from typing import TypeVar
from bumble.att import Attribute, AttributeValue
from bumble.colors import color
@@ -355,7 +356,7 @@ class Service(Attribute):
def __init__(
self,
uuid: Union[str, UUID],
uuid: str | UUID,
characteristics: Iterable[Characteristic],
primary=True,
included_services: Iterable[Service] = (),
@@ -378,7 +379,7 @@ class Service(Attribute):
self.characteristics = list(characteristics)
self.primary = primary
def get_advertising_data(self) -> Optional[bytes]:
def get_advertising_data(self) -> bytes | None:
"""
Get Service specific advertising data
Defined by each Service, default value is empty
@@ -502,10 +503,10 @@ class Characteristic(Attribute[_T]):
def __init__(
self,
uuid: Union[str, bytes, UUID],
uuid: str | bytes | UUID,
properties: Characteristic.Properties,
permissions: Union[str, Attribute.Permissions],
value: Union[AttributeValue[_T], _T, None] = None,
permissions: str | Attribute.Permissions,
value: AttributeValue[_T] | _T | None = None,
descriptors: Sequence[Descriptor] = (),
):
super().__init__(uuid, permissions, value)

View File

@@ -22,7 +22,8 @@
from __future__ import annotations
import struct
from typing import Any, Callable, Generic, Iterable, Literal, Optional, TypeVar
from collections.abc import Callable, Iterable
from typing import Any, Generic, Literal, TypeVar
from bumble import utils
from bumble.core import InvalidOperationError
@@ -74,8 +75,8 @@ class DelegatedCharacteristicAdapter(CharacteristicAdapter[_T]):
def __init__(
self,
characteristic: Characteristic,
encode: Optional[Callable[[_T], bytes]] = None,
decode: Optional[Callable[[bytes], _T]] = None,
encode: Callable[[_T], bytes] | None = None,
decode: Callable[[bytes], _T] | None = None,
):
super().__init__(characteristic)
self.encode = encode
@@ -101,8 +102,8 @@ class DelegatedCharacteristicProxyAdapter(CharacteristicProxyAdapter[_T]):
def __init__(
self,
characteristic_proxy: CharacteristicProxy,
encode: Optional[Callable[[_T], bytes]] = None,
decode: Optional[Callable[[bytes], _T]] = None,
encode: Callable[[_T], bytes] | None = None,
decode: Callable[[bytes], _T] | None = None,
):
super().__init__(characteristic_proxy)
self.encode = encode

View File

@@ -28,16 +28,13 @@ from __future__ import annotations
import asyncio
import logging
import struct
from collections.abc import Callable, Iterable
from datetime import datetime
from typing import (
TYPE_CHECKING,
Any,
Callable,
Generic,
Iterable,
Optional,
TypeVar,
Union,
)
from bumble import att, core, utils
@@ -192,7 +189,7 @@ class CharacteristicProxy(AttributeProxy[_T]):
self.descriptors_discovered = False
self.subscribers = {} # Map from subscriber to proxy subscriber
def get_descriptor(self, descriptor_type: UUID) -> Optional[DescriptorProxy]:
def get_descriptor(self, descriptor_type: UUID) -> DescriptorProxy | None:
for descriptor in self.descriptors:
if descriptor.type == descriptor_type:
return descriptor
@@ -204,7 +201,7 @@ class CharacteristicProxy(AttributeProxy[_T]):
async def subscribe(
self,
subscriber: Optional[Callable[[_T], Any]] = None,
subscriber: Callable[[_T], Any] | None = None,
prefer_notify: bool = True,
) -> None:
if subscriber is not None:
@@ -253,7 +250,7 @@ class ProfileServiceProxy:
SERVICE_CLASS: type[TemplateService]
@classmethod
def from_client(cls, client: Client) -> Optional[ProfileServiceProxy]:
def from_client(cls, client: Client) -> ProfileServiceProxy | None:
return ServiceProxy.from_client(cls, client, cls.SERVICE_CLASS.UUID)
@@ -264,13 +261,11 @@ class Client:
services: list[ServiceProxy]
cached_values: dict[int, tuple[datetime, bytes]]
notification_subscribers: dict[
int, set[Union[CharacteristicProxy, Callable[[bytes], Any]]]
int, set[CharacteristicProxy | Callable[[bytes], Any]]
]
indication_subscribers: dict[
int, set[Union[CharacteristicProxy, Callable[[bytes], Any]]]
]
pending_response: Optional[asyncio.futures.Future[att.ATT_PDU]]
pending_request: Optional[att.ATT_PDU]
indication_subscribers: dict[int, set[CharacteristicProxy | Callable[[bytes], Any]]]
pending_response: asyncio.futures.Future[att.ATT_PDU] | None
pending_request: att.ATT_PDU | None
def __init__(self, connection: Connection) -> None:
self.connection = connection
@@ -360,7 +355,7 @@ class Client:
return [service for service in self.services if service.uuid == uuid]
def get_characteristics_by_uuid(
self, uuid: UUID, service: Optional[ServiceProxy] = None
self, uuid: UUID, service: ServiceProxy | None = None
) -> list[CharacteristicProxy[bytes]]:
services = [service] if service else self.services
return [
@@ -369,13 +364,14 @@ class Client:
if c.uuid == uuid
]
def get_attribute_grouping(self, attribute_handle: int) -> Optional[
Union[
ServiceProxy,
tuple[ServiceProxy, CharacteristicProxy],
tuple[ServiceProxy, CharacteristicProxy, DescriptorProxy],
]
]:
def get_attribute_grouping(
self, attribute_handle: int
) -> (
ServiceProxy
| tuple[ServiceProxy, CharacteristicProxy]
| tuple[ServiceProxy, CharacteristicProxy, DescriptorProxy]
| None
):
"""
Get the attribute(s) associated with an attribute handle
"""
@@ -478,7 +474,7 @@ class Client:
return services
async def discover_service(self, uuid: Union[str, UUID]) -> list[ServiceProxy]:
async def discover_service(self, uuid: str | UUID) -> list[ServiceProxy]:
'''
See Vol 3, Part G - 4.4.2 Discover Primary Service by Service UUID
'''
@@ -612,7 +608,7 @@ class Client:
return included_services
async def discover_characteristics(
self, uuids, service: Optional[ServiceProxy]
self, uuids, service: ServiceProxy | None
) -> list[CharacteristicProxy[bytes]]:
'''
See Vol 3, Part G - 4.6.1 Discover All Characteristics of a Service and 4.6.2
@@ -699,9 +695,9 @@ class Client:
async def discover_descriptors(
self,
characteristic: Optional[CharacteristicProxy] = None,
start_handle: Optional[int] = None,
end_handle: Optional[int] = None,
characteristic: CharacteristicProxy | None = None,
start_handle: int | None = None,
end_handle: int | None = None,
) -> list[DescriptorProxy]:
'''
See Vol 3, Part G - 4.7.1 Discover All Characteristic Descriptors
@@ -810,7 +806,7 @@ class Client:
async def subscribe(
self,
characteristic: CharacteristicProxy,
subscriber: Optional[Callable[[Any], Any]] = None,
subscriber: Callable[[Any], Any] | None = None,
prefer_notify: bool = True,
) -> None:
# If we haven't already discovered the descriptors for this characteristic,
@@ -860,7 +856,7 @@ class Client:
async def unsubscribe(
self,
characteristic: CharacteristicProxy,
subscriber: Optional[Callable[[Any], Any]] = None,
subscriber: Callable[[Any], Any] | None = None,
force: bool = False,
) -> None:
'''
@@ -925,7 +921,7 @@ class Client:
await self.write_value(cccd, b'\x00\x00', with_response=True)
async def read_value(
self, attribute: Union[int, AttributeProxy], no_long_read: bool = False
self, attribute: int | AttributeProxy, no_long_read: bool = False
) -> bytes:
'''
See Vol 3, Part G - 4.8.1 Read Characteristic Value
@@ -980,7 +976,7 @@ class Client:
return attribute_value
async def read_characteristics_by_uuid(
self, uuid: UUID, service: Optional[ServiceProxy]
self, uuid: UUID, service: ServiceProxy | None
) -> list[bytes]:
'''
See Vol 3, Part G - 4.8.2 Read Using Characteristic UUID
@@ -1038,7 +1034,7 @@ class Client:
async def write_value(
self,
attribute: Union[int, AttributeProxy],
attribute: int | AttributeProxy,
value: bytes,
with_response: bool = False,
) -> None:

View File

@@ -29,7 +29,8 @@ import asyncio
import logging
import struct
from collections import defaultdict
from typing import TYPE_CHECKING, Iterable, Optional, TypeVar
from collections.abc import Iterable
from typing import TYPE_CHECKING, TypeVar
from bumble import att, utils
from bumble.colors import color
@@ -73,7 +74,7 @@ class Server(utils.EventEmitter):
attributes_by_handle: dict[int, att.Attribute]
subscribers: dict[int, dict[int, bytes]]
indication_semaphores: defaultdict[int, asyncio.Semaphore]
pending_confirmations: defaultdict[int, Optional[asyncio.futures.Future]]
pending_confirmations: defaultdict[int, asyncio.futures.Future | None]
EVENT_CHARACTERISTIC_SUBSCRIPTION = "characteristic_subscription"
@@ -109,7 +110,7 @@ class Server(utils.EventEmitter):
and (data := attribute.get_advertising_data())
}
def get_attribute(self, handle: int) -> Optional[att.Attribute]:
def get_attribute(self, handle: int) -> att.Attribute | None:
attribute = self.attributes_by_handle.get(handle)
if attribute:
return attribute
@@ -126,7 +127,7 @@ class Server(utils.EventEmitter):
def get_attribute_group(
self, handle: int, group_type: type[AttributeGroupType]
) -> Optional[AttributeGroupType]:
) -> AttributeGroupType | None:
return next(
(
attribute
@@ -137,7 +138,7 @@ class Server(utils.EventEmitter):
None,
)
def get_service_attribute(self, service_uuid: UUID) -> Optional[Service]:
def get_service_attribute(self, service_uuid: UUID) -> Service | None:
return next(
(
attribute
@@ -151,7 +152,7 @@ class Server(utils.EventEmitter):
def get_characteristic_attributes(
self, service_uuid: UUID, characteristic_uuid: UUID
) -> Optional[tuple[CharacteristicDeclaration, Characteristic]]:
) -> tuple[CharacteristicDeclaration, Characteristic] | None:
service_handle = self.get_service_attribute(service_uuid)
if not service_handle:
return None
@@ -176,7 +177,7 @@ class Server(utils.EventEmitter):
def get_descriptor_attribute(
self, service_uuid: UUID, characteristic_uuid: UUID, descriptor_uuid: UUID
) -> Optional[Descriptor]:
) -> Descriptor | None:
characteristics = self.get_characteristic_attributes(
service_uuid, characteristic_uuid
)
@@ -334,7 +335,7 @@ class Server(utils.EventEmitter):
self,
connection: Connection,
attribute: att.Attribute,
value: Optional[bytes] = None,
value: bytes | None = None,
force: bool = False,
) -> None:
# Check if there's a subscriber
@@ -377,7 +378,7 @@ class Server(utils.EventEmitter):
self,
connection: Connection,
attribute: att.Attribute,
value: Optional[bytes] = None,
value: bytes | None = None,
force: bool = False,
) -> None:
# Check if there's a subscriber
@@ -437,7 +438,7 @@ class Server(utils.EventEmitter):
self,
indicate: bool,
attribute: att.Attribute,
value: Optional[bytes] = None,
value: bytes | None = None,
force: bool = False,
) -> None:
# Get all the connections for which there's at least one subscription
@@ -464,7 +465,7 @@ class Server(utils.EventEmitter):
async def notify_subscribers(
self,
attribute: att.Attribute,
value: Optional[bytes] = None,
value: bytes | None = None,
force: bool = False,
):
return await self._notify_or_indicate_subscribers(
@@ -474,7 +475,7 @@ class Server(utils.EventEmitter):
async def indicate_subscribers(
self,
attribute: att.Attribute,
value: Optional[bytes] = None,
value: bytes | None = None,
force: bool = False,
):
return await self._notify_or_indicate_subscribers(True, attribute, value, force)

View File

@@ -24,17 +24,13 @@ import functools
import logging
import secrets
import struct
from collections.abc import Sequence
from collections.abc import Callable, Iterable, Sequence
from dataclasses import field
from typing import (
Any,
Callable,
ClassVar,
Iterable,
Literal,
Optional,
TypeVar,
Union,
cast,
)
@@ -106,7 +102,7 @@ def map_class_of_device(class_of_device):
)
def phy_list_to_bits(phys: Optional[Iterable[Phy]]) -> int:
def phy_list_to_bits(phys: Iterable[Phy] | None) -> int:
if phys is None:
return 0
@@ -184,8 +180,8 @@ class SpecableFlag(enum.IntFlag):
# - "v" for variable length bytes with a leading length byte
# - an integer [1, 4] for 1-byte, 2-byte or 4-byte unsigned little-endian integers
# - an integer [-2, -1] for 1-byte, 2-byte signed little-endian integers
FieldSpec = Union[dict[str, Any], Callable[[bytes, int], tuple[int, Any]], str, int]
Fields = Sequence[Union[tuple[str, FieldSpec], 'Fields']]
FieldSpec = dict[str, Any] | Callable[[bytes, int], tuple[int, Any]] | str | int
Fields = Sequence['tuple[str, FieldSpec] | Fields']
@dataclasses.dataclass
@@ -2156,7 +2152,7 @@ class Address:
def __init__(
self,
address: Union[bytes, str],
address: bytes | str,
address_type: AddressType = RANDOM_DEVICE_ADDRESS,
) -> None:
'''
@@ -2421,9 +2417,9 @@ class HCI_Command(HCI_Packet):
def __init__(
self,
parameters: Optional[bytes] = None,
parameters: bytes | None = None,
*,
op_code: Optional[int] = None,
op_code: int | None = None,
**kwargs,
) -> None:
# op_code should be set in cls.
@@ -5714,7 +5710,7 @@ class HCI_Event(HCI_Packet):
hci_packet_type = HCI_EVENT_PACKET
event_names: dict[int, str] = {}
event_classes: dict[int, type[HCI_Event]] = {}
vendor_factories: list[Callable[[bytes], Optional[HCI_Event]]] = []
vendor_factories: list[Callable[[bytes], HCI_Event | None]] = []
event_code: int
fields: Fields = ()
_parameters: bytes = b''
@@ -5775,14 +5771,12 @@ class HCI_Event(HCI_Packet):
return event_class
@classmethod
def add_vendor_factory(
cls, factory: Callable[[bytes], Optional[HCI_Event]]
) -> None:
def add_vendor_factory(cls, factory: Callable[[bytes], HCI_Event | None]) -> None:
cls.vendor_factories.append(factory)
@classmethod
def remove_vendor_factory(
cls, factory: Callable[[bytes], Optional[HCI_Event]]
cls, factory: Callable[[bytes], HCI_Event | None]
) -> None:
if factory in cls.vendor_factories:
cls.vendor_factories.remove(factory)
@@ -5795,7 +5789,7 @@ class HCI_Event(HCI_Packet):
if len(parameters) != length:
raise InvalidPacketError('invalid packet length')
subclass: Optional[type[HCI_Event]]
subclass: type[HCI_Event] | None
if event_code == HCI_LE_META_EVENT:
# We do this dispatch here and not in the subclass in order to avoid call
# loops
@@ -5833,9 +5827,9 @@ class HCI_Event(HCI_Packet):
def __init__(
self,
parameters: Optional[bytes] = None,
parameters: bytes | None = None,
*,
event_code: Optional[int] = None,
event_code: int | None = None,
**kwargs,
):
if event_code is not None:
@@ -5944,9 +5938,7 @@ class HCI_Extended_Event(HCI_Event):
cls.subevent_names.update(cls.subevent_map(symbols))
@classmethod
def subclass_from_parameters(
cls, parameters: bytes
) -> Optional[HCI_Extended_Event]:
def subclass_from_parameters(cls, parameters: bytes) -> HCI_Extended_Event | None:
"""
Factory method that parses the subevent code, finds a registered subclass,
and creates an instance if found.
@@ -5966,9 +5958,9 @@ class HCI_Extended_Event(HCI_Event):
def __init__(
self,
parameters: Optional[bytes] = None,
parameters: bytes | None = None,
*,
subevent_code: Optional[int] = None,
subevent_code: int | None = None,
**kwargs,
) -> None:
if subevent_code is not None:
@@ -6964,7 +6956,7 @@ class HCI_Command_Complete_Event(HCI_Event):
command_opcode: int = field(
metadata=metadata({'size': 2, 'mapper': HCI_Command.command_name})
)
return_parameters: Union[bytes, HCI_Object, int] = field(metadata=metadata("*"))
return_parameters: bytes | HCI_Object | int = field(metadata=metadata("*"))
def map_return_parameters(self, return_parameters):
'''Map simple 'status' return parameters to their named constant form'''
@@ -7548,20 +7540,20 @@ class HCI_IsoDataPacket(HCI_Packet):
iso_sdu_fragment: bytes
pb_flag: int
ts_flag: int = 0
time_stamp: Optional[int] = None
packet_sequence_number: Optional[int] = None
iso_sdu_length: Optional[int] = None
packet_status_flag: Optional[int] = None
time_stamp: int | None = None
packet_sequence_number: int | None = None
iso_sdu_length: int | None = None
packet_status_flag: int | None = None
def __post_init__(self) -> None:
self.ts_flag = self.time_stamp is not None
@staticmethod
def from_bytes(packet: bytes) -> HCI_IsoDataPacket:
time_stamp: Optional[int] = None
packet_sequence_number: Optional[int] = None
iso_sdu_length: Optional[int] = None
packet_status_flag: Optional[int] = None
time_stamp: int | None = None
packet_sequence_number: int | None = None
iso_sdu_length: int | None = None
packet_status_flag: int | None = None
pos = 1
pdu_info, data_total_length = struct.unpack_from('<HH', packet, pos)
@@ -7644,7 +7636,7 @@ class HCI_IsoDataPacket(HCI_Packet):
# -----------------------------------------------------------------------------
class HCI_AclDataPacketAssembler:
current_data: Optional[bytes]
current_data: bytes | None
def __init__(self, callback: Callable[[bytes], Any]) -> None:
self.callback = callback

View File

@@ -20,7 +20,7 @@ from __future__ import annotations
import datetime
import logging
from collections.abc import Callable, MutableMapping
from typing import Any, Optional, cast
from typing import Any, cast
from bumble import avc, avctp, avdtp, avrcp, crypto, rfcomm, sdp
from bumble.att import ATT_CID, ATT_PDU
@@ -70,7 +70,7 @@ AVCTP_PID_NAMES = {avrcp.AVRCP_PID: 'AVRCP'}
class PacketTracer:
class AclStream:
psms: MutableMapping[int, int]
peer: Optional[PacketTracer.AclStream]
peer: PacketTracer.AclStream | None
avdtp_assemblers: MutableMapping[int, avdtp.MessageAssembler]
avctp_assemblers: MutableMapping[int, avctp.MessageAssembler]
@@ -201,7 +201,7 @@ class PacketTracer:
self.label = label
self.emit_message = emit_message
self.acl_streams = {} # ACL streams, by connection handle
self.packet_timestamp: Optional[datetime.datetime] = None
self.packet_timestamp: datetime.datetime | None = None
def start_acl_stream(self, connection_handle: int) -> PacketTracer.AclStream:
logger.info(
@@ -230,7 +230,7 @@ class PacketTracer:
self.peer.end_acl_stream(connection_handle)
def on_packet(
self, timestamp: Optional[datetime.datetime], packet: HCI_Packet
self, timestamp: datetime.datetime | None, packet: HCI_Packet
) -> None:
self.packet_timestamp = timestamp
self.emit(packet)
@@ -262,7 +262,7 @@ class PacketTracer:
self,
packet: HCI_Packet,
direction: int = 0,
timestamp: Optional[datetime.datetime] = None,
timestamp: datetime.datetime | None = None,
) -> None:
if direction == 0:
self.host_to_controller_analyzer.on_packet(timestamp, packet)

View File

@@ -25,7 +25,8 @@ import enum
import logging
import re
import traceback
from typing import TYPE_CHECKING, Any, ClassVar, Iterable, Optional, Union
from collections.abc import Iterable
from typing import TYPE_CHECKING, Any, ClassVar
from typing_extensions import Self
@@ -80,7 +81,7 @@ class HfpProtocol:
dlc.sink = self.feed
def feed(self, data: Union[bytes, str]) -> None:
def feed(self, data: bytes | str) -> None:
# Convert the data to a string if needed
if isinstance(data, bytes):
data = data.decode('utf-8')
@@ -324,8 +325,8 @@ class CallInfo:
status: CallInfoStatus
mode: CallInfoMode
multi_party: CallInfoMultiParty
number: Optional[str] = None
type: Optional[int] = None
number: str | None = None
type: int | None = None
@dataclasses.dataclass
@@ -353,10 +354,10 @@ class CallLineIdentification:
number: str
type: int
subaddr: Optional[str] = None
satype: Optional[int] = None
alpha: Optional[str] = None
cli_validity: Optional[int] = None
subaddr: str | None = None
satype: int | None = None
alpha: str | None = None
cli_validity: int | None = None
@classmethod
def parse_from(cls, parameters: list[bytes]) -> Self:
@@ -584,7 +585,7 @@ class AgIndicatorState:
indicator: AgIndicator
supported_values: set[int]
current_status: int
index: Optional[int] = None
index: int | None = None
enabled: bool = True
@property
@@ -728,7 +729,7 @@ class HfProtocol(utils.EventEmitter):
command_lock: asyncio.Lock
if TYPE_CHECKING:
response_queue: asyncio.Queue[AtResponse]
unsolicited_queue: asyncio.Queue[Optional[AtResponse]]
unsolicited_queue: asyncio.Queue[AtResponse | None]
else:
response_queue: asyncio.Queue
unsolicited_queue: asyncio.Queue
@@ -820,7 +821,7 @@ class HfProtocol(utils.EventEmitter):
cmd: str,
timeout: float = 1.0,
response_type: AtResponseType = AtResponseType.NONE,
) -> Union[None, AtResponse, list[AtResponse]]:
) -> None | AtResponse | list[AtResponse]:
"""
Sends an AT command and wait for the peer response.
Wait for the AT responses sent by the peer, to the status code.
@@ -1411,7 +1412,7 @@ class AgProtocol(utils.EventEmitter):
self.emit(self.EVENT_VOICE_RECOGNITION, VoiceRecognitionState(int(vrec)))
def _on_chld(self, operation_code: bytes) -> None:
call_index: Optional[int] = None
call_index: int | None = None
if len(operation_code) > 1:
call_index = int(operation_code[1:])
operation_code = operation_code[:1] + b'x'
@@ -1481,8 +1482,8 @@ class AgProtocol(utils.EventEmitter):
def _on_cmer(
self,
mode: bytes,
keypad: Optional[bytes] = None,
display: Optional[bytes] = None,
keypad: bytes | None = None,
display: bytes | None = None,
indicator: bytes = b'',
) -> None:
if (
@@ -1844,7 +1845,7 @@ def make_ag_sdp_records(
async def find_hf_sdp_record(
connection: device.Connection,
) -> Optional[tuple[int, ProfileVersion, HfSdpFeature]]:
) -> tuple[int, ProfileVersion, HfSdpFeature] | None:
"""Searches a Hands-Free SDP record from remote device.
Args:
@@ -1864,9 +1865,9 @@ async def find_hf_sdp_record(
],
)
for attribute_lists in search_result:
channel: Optional[int] = None
version: Optional[ProfileVersion] = None
features: Optional[HfSdpFeature] = None
channel: int | None = None
version: ProfileVersion | None = None
features: HfSdpFeature | None = None
for attribute in attribute_lists:
# The layout is [[L2CAP_PROTOCOL], [RFCOMM_PROTOCOL, RFCOMM_CHANNEL]].
if attribute.id == sdp.SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID:
@@ -1896,7 +1897,7 @@ async def find_hf_sdp_record(
async def find_ag_sdp_record(
connection: device.Connection,
) -> Optional[tuple[int, ProfileVersion, AgSdpFeature]]:
) -> tuple[int, ProfileVersion, AgSdpFeature] | None:
"""Searches an Audio-Gateway SDP record from remote device.
Args:
@@ -1915,9 +1916,9 @@ async def find_ag_sdp_record(
],
)
for attribute_lists in search_result:
channel: Optional[int] = None
version: Optional[ProfileVersion] = None
features: Optional[AgSdpFeature] = None
channel: int | None = None
version: ProfileVersion | None = None
features: AgSdpFeature | None = None
for attribute in attribute_lists:
# The layout is [[L2CAP_PROTOCOL], [RFCOMM_PROTOCOL, RFCOMM_CHANNEL]].
if attribute.id == sdp.SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID:

View File

@@ -21,8 +21,8 @@ import enum
import logging
import struct
from abc import ABC, abstractmethod
from collections.abc import Callable
from dataclasses import dataclass
from typing import Callable, Optional
from typing_extensions import override
@@ -195,9 +195,9 @@ class SendHandshakeMessage(Message):
# -----------------------------------------------------------------------------
class HID(ABC, utils.EventEmitter):
l2cap_ctrl_channel: Optional[l2cap.ClassicChannel] = None
l2cap_intr_channel: Optional[l2cap.ClassicChannel] = None
connection: Optional[device.Connection] = None
l2cap_ctrl_channel: l2cap.ClassicChannel | None = None
l2cap_intr_channel: l2cap.ClassicChannel | None = None
connection: device.Connection | None = None
EVENT_INTERRUPT_DATA = "interrupt_data"
EVENT_CONTROL_DATA = "control_data"
@@ -212,7 +212,7 @@ class HID(ABC, utils.EventEmitter):
def __init__(self, device: device.Device, role: Role) -> None:
super().__init__()
self.remote_device_bd_address: Optional[Address] = None
self.remote_device_bd_address: Address | None = None
self.device = device
self.role = role
@@ -353,10 +353,10 @@ class Device(HID):
data: bytes = b''
status: int = 0
get_report_cb: Optional[Callable[[int, int, int], GetSetStatus]] = None
set_report_cb: Optional[Callable[[int, int, int, bytes], GetSetStatus]] = None
get_protocol_cb: Optional[Callable[[], GetSetStatus]] = None
set_protocol_cb: Optional[Callable[[int], GetSetStatus]] = None
get_report_cb: Callable[[int, int, int], GetSetStatus] | None = None
set_report_cb: Callable[[int, int, int, bytes], GetSetStatus] | None = None
get_protocol_cb: Callable[[], GetSetStatus] | None = None
set_protocol_cb: Callable[[int], GetSetStatus] | None = None
def __init__(self, device: device.Device) -> None:
super().__init__(device, HID.Role.DEVICE)

View File

@@ -22,7 +22,8 @@ import collections
import dataclasses
import logging
import struct
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Optional, Union, cast
from collections.abc import Awaitable, Callable
from typing import TYPE_CHECKING, Any, cast
from bumble import drivers, hci, utils
from bumble.colors import color
@@ -198,7 +199,7 @@ class Connection:
self.peer_address = peer_address
self.assembler = hci.HCI_AclDataPacketAssembler(self.on_acl_pdu)
self.transport = transport
acl_packet_queue: Optional[DataPacketQueue] = (
acl_packet_queue: DataPacketQueue | None = (
host.le_acl_packet_queue
if transport == PhysicalTransport.LE
else host.acl_packet_queue
@@ -241,20 +242,18 @@ class Host(utils.EventEmitter):
bis_links: dict[int, IsoLink]
sco_links: dict[int, ScoLink]
bigs: dict[int, set[int]]
acl_packet_queue: Optional[DataPacketQueue] = None
le_acl_packet_queue: Optional[DataPacketQueue] = None
iso_packet_queue: Optional[DataPacketQueue] = None
hci_sink: Optional[TransportSink] = None
acl_packet_queue: DataPacketQueue | None = None
le_acl_packet_queue: DataPacketQueue | None = None
iso_packet_queue: DataPacketQueue | None = None
hci_sink: TransportSink | None = None
hci_metadata: dict[str, Any]
long_term_key_provider: Optional[
Callable[[int, bytes, int], Awaitable[Optional[bytes]]]
]
link_key_provider: Optional[Callable[[hci.Address], Awaitable[Optional[bytes]]]]
long_term_key_provider: Callable[[int, bytes, int], Awaitable[bytes | None]] | None
link_key_provider: Callable[[hci.Address], Awaitable[bytes | None]] | None
def __init__(
self,
controller_source: Optional[TransportSource] = None,
controller_sink: Optional[TransportSink] = None,
controller_source: TransportSource | None = None,
controller_sink: TransportSink | None = None,
) -> None:
super().__init__()
@@ -266,7 +265,7 @@ class Host(utils.EventEmitter):
self.sco_links = {} # SCO links, by connection handle
self.bigs = {} # BIG Handle to BIS Handles
self.pending_command = None
self.pending_response: Optional[asyncio.Future[Any]] = None
self.pending_response: asyncio.Future[Any] | None = None
self.number_of_supported_advertising_sets = 0
self.maximum_advertising_data_length = 31
self.local_version = None
@@ -279,7 +278,7 @@ class Host(utils.EventEmitter):
self.long_term_key_provider = None
self.link_key_provider = None
self.pairing_io_capability_provider = None # Classic only
self.snooper: Optional[Snooper] = None
self.snooper: Snooper | None = None
# Connect to the source and sink if specified
if controller_source:
@@ -290,9 +289,9 @@ class Host(utils.EventEmitter):
def find_connection_by_bd_addr(
self,
bd_addr: hci.Address,
transport: Optional[int] = None,
transport: int | None = None,
check_address_type: bool = False,
) -> Optional[Connection]:
) -> Connection | None:
for connection in self.connections.values():
if bytes(connection.peer_address) == bytes(bd_addr):
if (
@@ -632,7 +631,7 @@ class Host(utils.EventEmitter):
)
@property
def controller(self) -> Optional[TransportSink]:
def controller(self) -> TransportSink | None:
return self.hci_sink
@controller.setter
@@ -641,7 +640,7 @@ class Host(utils.EventEmitter):
if controller:
self.set_packet_source(controller)
def set_packet_sink(self, sink: Optional[TransportSink]) -> None:
def set_packet_sink(self, sink: TransportSink | None) -> None:
self.hci_sink = sink
def set_packet_source(self, source: TransportSource) -> None:
@@ -656,7 +655,7 @@ class Host(utils.EventEmitter):
self.hci_sink.on_packet(bytes(packet))
async def send_command(
self, command, check_result=False, response_timeout: Optional[int] = None
self, command, check_result=False, response_timeout: int | None = None
):
# Wait until we can send (only one pending command at a time)
async with self.command_semaphore:
@@ -899,7 +898,7 @@ class Host(utils.EventEmitter):
self.emit('l2cap_pdu', connection.handle, cid, pdu)
def on_command_processed(
self, event: Union[hci.HCI_Command_Complete_Event, hci.HCI_Command_Status_Event]
self, event: hci.HCI_Command_Complete_Event | hci.HCI_Command_Status_Event
):
if self.pending_response:
# Check that it is what we were expecting
@@ -962,11 +961,11 @@ class Host(utils.EventEmitter):
def on_hci_le_connection_complete_event(
self,
event: Union[
hci.HCI_LE_Connection_Complete_Event,
hci.HCI_LE_Enhanced_Connection_Complete_Event,
hci.HCI_LE_Enhanced_Connection_Complete_V2_Event,
],
event: (
hci.HCI_LE_Connection_Complete_Event
| hci.HCI_LE_Enhanced_Connection_Complete_Event
| hci.HCI_LE_Enhanced_Connection_Complete_V2_Event
),
):
# Check if this is a cancellation
if event.status == hci.HCI_SUCCESS:
@@ -1011,10 +1010,10 @@ class Host(utils.EventEmitter):
def on_hci_le_enhanced_connection_complete_event(
self,
event: Union[
hci.HCI_LE_Enhanced_Connection_Complete_Event,
hci.HCI_LE_Enhanced_Connection_Complete_V2_Event,
],
event: (
hci.HCI_LE_Enhanced_Connection_Complete_Event
| hci.HCI_LE_Enhanced_Connection_Complete_V2_Event
),
):
# Just use the same implementation as for the non-enhanced event for now
self.on_hci_le_connection_complete_event(event)

View File

@@ -27,7 +27,7 @@ import dataclasses
import json
import logging
import os
from typing import TYPE_CHECKING, Any, Optional
from typing import TYPE_CHECKING, Any
from typing_extensions import Self
@@ -51,8 +51,8 @@ class PairingKeys:
class Key:
value: bytes
authenticated: bool = False
ediv: Optional[int] = None
rand: Optional[bytes] = None
ediv: int | None = None
rand: bytes | None = None
@classmethod
def from_dict(cls, key_dict: dict[str, Any]) -> PairingKeys.Key:
@@ -74,17 +74,17 @@ class PairingKeys:
return key_dict
address_type: Optional[hci.AddressType] = None
ltk: Optional[Key] = None
ltk_central: Optional[Key] = None
ltk_peripheral: Optional[Key] = None
irk: Optional[Key] = None
csrk: Optional[Key] = None
link_key: Optional[Key] = None # Classic
link_key_type: Optional[int] = None # Classic
address_type: hci.AddressType | None = None
ltk: Key | None = None
ltk_central: Key | None = None
ltk_peripheral: Key | None = None
irk: Key | None = None
csrk: Key | None = None
link_key: Key | None = None # Classic
link_key_type: int | None = None # Classic
@classmethod
def key_from_dict(cls, keys_dict: dict[str, Any], key_name: str) -> Optional[Key]:
def key_from_dict(cls, keys_dict: dict[str, Any], key_name: str) -> Key | None:
key_dict = keys_dict.get(key_name)
if key_dict is None:
return None
@@ -156,7 +156,7 @@ class KeyStore:
async def update(self, name: str, keys: PairingKeys) -> None:
pass
async def get(self, _name: str) -> Optional[PairingKeys]:
async def get(self, _name: str) -> PairingKeys | None:
return None
async def get_all(self) -> list[tuple[str, PairingKeys]]:
@@ -274,7 +274,7 @@ class JsonKeyStore(KeyStore):
@classmethod
def from_device(
cls: type[Self], device: Device, filename: Optional[str] = None
cls: type[Self], device: Device, filename: str | None = None
) -> Self:
if not filename:
# Extract the filename from the config if there is one
@@ -297,7 +297,7 @@ class JsonKeyStore(KeyStore):
# Try to open the file, without failing. If the file does not exist, it
# will be created upon saving.
try:
with open(self.filename, 'r', encoding='utf-8') as json_file:
with open(self.filename, encoding='utf-8') as json_file:
db = json.load(json_file)
except FileNotFoundError:
db = {}
@@ -348,7 +348,7 @@ class JsonKeyStore(KeyStore):
key_map.clear()
await self.save(db)
async def get(self, name: str) -> Optional[PairingKeys]:
async def get(self, name: str) -> PairingKeys | None:
_, key_map = await self.load()
if name not in key_map:
return None
@@ -370,7 +370,7 @@ class MemoryKeyStore(KeyStore):
async def update(self, name: str, keys: PairingKeys) -> None:
self.all_keys[name] = keys
async def get(self, name: str) -> Optional[PairingKeys]:
async def get(self, name: str) -> PairingKeys | None:
return self.all_keys.get(name)
async def get_all(self) -> list[tuple[str, PairingKeys]]:

View File

@@ -24,7 +24,7 @@ import logging
import struct
from collections import deque
from collections.abc import Callable, Iterable, Sequence
from typing import TYPE_CHECKING, Any, ClassVar, Optional, SupportsBytes, TypeVar, Union
from typing import TYPE_CHECKING, Any, ClassVar, SupportsBytes, TypeVar
from typing_extensions import override
@@ -175,7 +175,7 @@ class ClassicChannelSpec:
fcs_enabled: Whether to enable FCS (Frame Check Sequence).
'''
psm: Optional[int] = None
psm: int | None = None
mtu: int = L2CAP_DEFAULT_MTU
mps: int = L2CAP_DEFAULT_MPS
tx_window_size: int = DEFAULT_TX_WINDOW_SIZE
@@ -188,7 +188,7 @@ class ClassicChannelSpec:
@dataclasses.dataclass
class LeCreditBasedChannelSpec:
psm: Optional[int] = None
psm: int | None = None
mtu: int = L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MTU
mps: int = L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MPS
max_credits: int = L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_INITIAL_CREDITS
@@ -372,7 +372,7 @@ class L2CAP_Control_Frame:
fields: ClassVar[hci.Fields] = ()
code: int = dataclasses.field(default=0, init=False)
name: str = dataclasses.field(default='', init=False)
_payload: Optional[bytes] = dataclasses.field(default=None, init=False)
_payload: bytes | None = dataclasses.field(default=None, init=False)
identifier: int
@@ -910,8 +910,8 @@ class EnhancedRetransmissionProcessor(Processor):
_num_receiver_ready_polls_sent: int = 0
_pending_pdus: list[_PendingPdu]
_monitor_handle: Optional[asyncio.TimerHandle] = None
_receiver_ready_poll_handle: Optional[asyncio.TimerHandle] = None
_monitor_handle: asyncio.TimerHandle | None = None
_receiver_ready_poll_handle: asyncio.TimerHandle | None = None
# Timeout, in seconds.
monitor_timeout: float
@@ -1109,10 +1109,10 @@ class ClassicChannel(utils.EventEmitter):
EVENT_OPEN = "open"
EVENT_CLOSE = "close"
connection_result: Optional[asyncio.Future[None]]
disconnection_result: Optional[asyncio.Future[None]]
response: Optional[asyncio.Future[bytes]]
sink: Optional[Callable[[bytes], Any]]
connection_result: asyncio.Future[None] | None
disconnection_result: asyncio.Future[None] | None
response: asyncio.Future[bytes] | None
sink: Callable[[bytes], Any] | None
state: State
connection: Connection
mtu: int
@@ -1159,7 +1159,7 @@ class ClassicChannel(utils.EventEmitter):
def write(self, sdu: bytes) -> None:
self.processor.send_sdu(sdu)
def send_pdu(self, pdu: Union[SupportsBytes, bytes]) -> None:
def send_pdu(self, pdu: SupportsBytes | bytes) -> None:
if self.state != self.State.OPEN:
raise InvalidStateError('channel not open')
self.manager.send_pdu(
@@ -1542,13 +1542,13 @@ class LeCreditBasedChannel(utils.EventEmitter):
CONNECTION_ERROR = 5
out_queue: deque[bytes]
connection_result: Optional[asyncio.Future[LeCreditBasedChannel]]
disconnection_result: Optional[asyncio.Future[None]]
in_sdu: Optional[bytes]
out_sdu: Optional[bytes]
connection_result: asyncio.Future[LeCreditBasedChannel] | None
disconnection_result: asyncio.Future[None] | None
in_sdu: bytes | None
out_sdu: bytes | None
state: State
connection: Connection
sink: Optional[Callable[[bytes], Any]]
sink: Callable[[bytes], Any] | None
EVENT_OPEN = "open"
EVENT_CLOSE = "close"
@@ -1608,7 +1608,7 @@ class LeCreditBasedChannel(utils.EventEmitter):
elif new_state == self.State.DISCONNECTED:
self.emit(self.EVENT_CLOSE)
def send_pdu(self, pdu: Union[SupportsBytes, bytes]) -> None:
def send_pdu(self, pdu: SupportsBytes | bytes) -> None:
self.manager.send_pdu(self.connection, self.destination_cid, pdu)
def send_control_frame(self, frame: L2CAP_Control_Frame) -> None:
@@ -1913,7 +1913,7 @@ class ClassicChannelServer(utils.EventEmitter):
self,
manager: ChannelManager,
psm: int,
handler: Optional[Callable[[ClassicChannel], Any]],
handler: Callable[[ClassicChannel], Any] | None,
spec: ClassicChannelSpec,
) -> None:
super().__init__()
@@ -1940,7 +1940,7 @@ class LeCreditBasedChannelServer(utils.EventEmitter):
self,
manager: ChannelManager,
psm: int,
handler: Optional[Callable[[LeCreditBasedChannel], Any]],
handler: Callable[[LeCreditBasedChannel], Any] | None,
max_credits: int,
mtu: int,
mps: int,
@@ -1966,12 +1966,12 @@ class LeCreditBasedChannelServer(utils.EventEmitter):
# -----------------------------------------------------------------------------
class ChannelManager:
identifiers: dict[int, int]
channels: dict[int, dict[int, Union[ClassicChannel, LeCreditBasedChannel]]]
channels: dict[int, dict[int, ClassicChannel | LeCreditBasedChannel]]
servers: dict[int, ClassicChannelServer]
le_coc_channels: dict[int, dict[int, LeCreditBasedChannel]]
le_coc_servers: dict[int, LeCreditBasedChannelServer]
le_coc_requests: dict[int, L2CAP_LE_Credit_Based_Connection_Request]
fixed_channels: dict[int, Optional[Callable[[int, bytes], Any]]]
fixed_channels: dict[int, Callable[[int, bytes], Any] | None]
pending_credit_based_connections: dict[
int,
dict[
@@ -1982,8 +1982,8 @@ class ChannelManager:
],
],
]
_host: Optional[Host]
connection_parameters_update_response: Optional[asyncio.Future[int]]
_host: Host | None
connection_parameters_update_response: asyncio.Future[int] | None
def __init__(
self,
@@ -2089,7 +2089,7 @@ class ChannelManager:
def create_classic_server(
self,
spec: ClassicChannelSpec,
handler: Optional[Callable[[ClassicChannel], Any]] = None,
handler: Callable[[ClassicChannel], Any] | None = None,
) -> ClassicChannelServer:
if not spec.psm:
# Find a free PSM
@@ -2125,7 +2125,7 @@ class ChannelManager:
def create_le_credit_based_server(
self,
spec: LeCreditBasedChannelSpec,
handler: Optional[Callable[[LeCreditBasedChannel], Any]] = None,
handler: Callable[[LeCreditBasedChannel], Any] | None = None,
) -> LeCreditBasedChannelServer:
if not spec.psm:
# Find a free PSM
@@ -2175,7 +2175,7 @@ class ChannelManager:
self,
connection: Connection,
cid: int,
pdu: Union[SupportsBytes, bytes],
pdu: SupportsBytes | bytes,
with_fcs: bool = False,
) -> None:
pdu_str = pdu.hex() if isinstance(pdu, bytes) else str(pdu)

View File

@@ -19,7 +19,7 @@ import asyncio
# Imports
# -----------------------------------------------------------------------------
import logging
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING
from bumble import core, hci, ll, lmp
@@ -67,7 +67,7 @@ class LocalLink:
def find_classic_controller(
self, address: hci.Address
) -> Optional[controller.Controller]:
) -> controller.Controller | None:
for controller in self.controllers:
if controller.public_address == address:
return controller

View File

@@ -20,7 +20,6 @@ from __future__ import annotations
import enum
import secrets
from dataclasses import dataclass
from typing import Optional
from bumble import hci
from bumble.core import AdvertisingData, LeRole
@@ -45,16 +44,16 @@ from bumble.smp import (
class OobData:
"""OOB data that can be sent from one device to another."""
address: Optional[hci.Address] = None
role: Optional[LeRole] = None
shared_data: Optional[OobSharedData] = None
legacy_context: Optional[OobLegacyContext] = None
address: hci.Address | None = None
role: LeRole | None = None
shared_data: OobSharedData | None = None
legacy_context: OobLegacyContext | None = None
@classmethod
def from_ad(cls, ad: AdvertisingData) -> OobData:
instance = cls()
shared_data_c: Optional[bytes] = None
shared_data_r: Optional[bytes] = None
shared_data_c: bytes | None = None
shared_data_r: bytes | None = None
for ad_type, ad_data in ad.ad_structures:
if ad_type == AdvertisingData.LE_BLUETOOTH_DEVICE_ADDRESS:
instance.address = hci.Address(ad_data)
@@ -181,14 +180,14 @@ class PairingDelegate:
"""Compare two numbers."""
return True
async def get_number(self) -> Optional[int]:
async def get_number(self) -> int | None:
"""
Return an optional number as an answer to a passkey request.
Returning `None` will result in a negative reply.
"""
return 0
async def get_string(self, max_length: int) -> Optional[str]:
async def get_string(self, max_length: int) -> str | None:
"""
Return a string whose utf-8 encoding is up to max_length bytes.
"""
@@ -239,18 +238,18 @@ class PairingConfig:
class OobConfig:
"""Config for OOB pairing."""
our_context: Optional[OobContext]
peer_data: Optional[OobSharedData]
legacy_context: Optional[OobLegacyContext]
our_context: OobContext | None
peer_data: OobSharedData | None
legacy_context: OobLegacyContext | None
def __init__(
self,
sc: bool = True,
mitm: bool = True,
bonding: bool = True,
delegate: Optional[PairingDelegate] = None,
identity_address_type: Optional[AddressType] = None,
oob: Optional[OobConfig] = None,
delegate: PairingDelegate | None = None,
identity_address_type: AddressType | None = None,
oob: OobConfig | None = None,
) -> None:
self.sc = sc
self.mitm = mitm

View File

@@ -19,7 +19,7 @@ This module implement the Pandora Bluetooth test APIs for the Bumble stack.
__version__ = "0.0.1"
from typing import Callable, Optional
from collections.abc import Callable
import grpc
import grpc.aio
@@ -58,7 +58,7 @@ def register_servicer_hook(
async def serve(
bumble: PandoraDevice,
config: Config = Config(),
grpc_server: Optional[grpc.aio.Server] = None,
grpc_server: grpc.aio.Server | None = None,
port: int = 0,
) -> None:
# initialize a gRPC server if not provided.

View File

@@ -16,7 +16,7 @@
from __future__ import annotations
from typing import Any, Optional
from typing import Any
from bumble import transport
from bumble.core import (
@@ -54,7 +54,7 @@ class PandoraDevice:
# HCI transport name & instance.
_hci_name: str
_hci: Optional[transport.Transport] # type: ignore[name-defined]
_hci: transport.Transport | None # type: ignore[name-defined]
def __init__(self, config: dict[str, Any]) -> None:
self.config = config
@@ -98,7 +98,7 @@ class PandoraDevice:
await self.close()
await self.open()
def info(self) -> Optional[dict[str, str]]:
def info(self) -> dict[str, str] | None:
return {
'public_bd_address': str(self.device.public_address),
'random_address': str(self.device.random_address),

View File

@@ -17,7 +17,8 @@ from __future__ import annotations
import asyncio
import logging
import struct
from typing import AsyncGenerator, Optional, cast
from collections.abc import AsyncGenerator
from typing import cast
import grpc
import grpc.aio
@@ -623,7 +624,7 @@ class HostService(HostServicer):
self.log.debug('Inquiry')
inquiry_queue: asyncio.Queue[
Optional[tuple[Address, int, AdvertisingData, int]]
tuple[Address, int, AdvertisingData, int] | None
] = asyncio.Queue()
complete_handler = self.device.on(
self.device.EVENT_INQUIRY_COMPLETE, lambda: inquiry_queue.put_nowait(None)

View File

@@ -18,8 +18,8 @@ import json
import logging
from asyncio import Future
from asyncio import Queue as AsyncQueue
from collections.abc import AsyncGenerator
from dataclasses import dataclass
from typing import AsyncGenerator, Optional, Union
import grpc
from google.protobuf import any_pb2, empty_pb2 # pytype: disable=pyi-error
@@ -56,7 +56,7 @@ from bumble.l2cap import (
from bumble.pandora import utils
from bumble.pandora.config import Config
L2capChannel = Union[ClassicChannel, LeCreditBasedChannel]
L2capChannel = ClassicChannel | LeCreditBasedChannel
@dataclass
@@ -107,10 +107,8 @@ class L2CAPService(L2CAPServicer):
oneof = request.WhichOneof('type')
self.log.debug(f'WaitConnection channel request type: {oneof}.')
channel_type = getattr(request, oneof)
spec: Optional[Union[ClassicChannelSpec, LeCreditBasedChannelSpec]] = None
l2cap_server: Optional[
Union[ClassicChannelServer, LeCreditBasedChannelServer]
] = None
spec: ClassicChannelSpec | LeCreditBasedChannelSpec | None = None
l2cap_server: ClassicChannelServer | LeCreditBasedChannelServer | None = None
if isinstance(channel_type, CreditBasedChannelRequest):
spec = LeCreditBasedChannelSpec(
psm=channel_type.spsm,
@@ -217,7 +215,7 @@ class L2CAPService(L2CAPServicer):
oneof = request.WhichOneof('type')
self.log.debug(f'Channel request type: {oneof}.')
channel_type = getattr(request, oneof)
spec: Optional[Union[ClassicChannelSpec, LeCreditBasedChannelSpec]] = None
spec: ClassicChannelSpec | LeCreditBasedChannelSpec | None = None
if isinstance(channel_type, CreditBasedChannelRequest):
spec = LeCreditBasedChannelSpec(
psm=channel_type.spsm,

View File

@@ -17,8 +17,8 @@ from __future__ import annotations
import asyncio
import contextlib
import logging
from collections.abc import Awaitable
from typing import Any, AsyncGenerator, AsyncIterator, Callable, Optional, Union
from collections.abc import AsyncGenerator, AsyncIterator, Awaitable, Callable
from typing import Any
import grpc
from google.protobuf import (
@@ -66,7 +66,7 @@ class PairingDelegate(BasePairingDelegate):
def __init__(
self,
connection: BumbleConnection,
service: "SecurityService",
service: SecurityService,
io_capability: BasePairingDelegate.IoCapability = BasePairingDelegate.NO_OUTPUT_NO_INPUT,
local_initiator_key_distribution: BasePairingDelegate.KeyDistribution = BasePairingDelegate.DEFAULT_KEY_DISTRIBUTION,
local_responder_key_distribution: BasePairingDelegate.KeyDistribution = BasePairingDelegate.DEFAULT_KEY_DISTRIBUTION,
@@ -132,7 +132,7 @@ class PairingDelegate(BasePairingDelegate):
assert answer.answer_variant() == 'confirm' and answer.confirm is not None
return answer.confirm
async def get_number(self) -> Optional[int]:
async def get_number(self) -> int | None:
self.log.debug(
f"Pairing event: `passkey_entry_request` (io_capability: {self.io_capability})"
)
@@ -149,7 +149,7 @@ class PairingDelegate(BasePairingDelegate):
assert answer.answer_variant() == 'passkey'
return answer.passkey
async def get_string(self, max_length: int) -> Optional[str]:
async def get_string(self, max_length: int) -> str | None:
self.log.debug(
f"Pairing event: `pin_code_request` (io_capability: {self.io_capability})"
)
@@ -197,8 +197,8 @@ class SecurityService(SecurityServicer):
self.log = utils.BumbleServerLoggerAdapter(
logging.getLogger(), {'service_name': 'Security', 'device': device}
)
self.event_queue: Optional[asyncio.Queue[PairingEvent]] = None
self.event_answer: Optional[AsyncIterator[PairingEventAnswer]] = None
self.event_queue: asyncio.Queue[PairingEvent] | None = None
self.event_answer: AsyncIterator[PairingEventAnswer] | None = None
self.device = device
self.config = config
@@ -233,7 +233,7 @@ class SecurityService(SecurityServicer):
if level == LEVEL2:
return connection.encryption != 0 and connection.authenticated
link_key_type: Optional[int] = None
link_key_type: int | None = None
if (keystore := connection.device.keystore) and (
keys := await keystore.get(str(connection.peer_address))
):
@@ -412,8 +412,8 @@ class SecurityService(SecurityServicer):
wait_for_security: asyncio.Future[str] = (
asyncio.get_running_loop().create_future()
)
authenticate_task: Optional[asyncio.Future[None]] = None
pair_task: Optional[asyncio.Future[None]] = None
authenticate_task: asyncio.Future[None] | None = None
pair_task: asyncio.Future[None] | None = None
async def authenticate() -> None:
if (encryption := connection.encryption) != 0:
@@ -459,7 +459,7 @@ class SecurityService(SecurityServicer):
if self.need_pairing(connection, level):
bumble.utils.AsyncRunner.spawn(connection.pair())
listeners: dict[str, Callable[..., Union[None, Awaitable[None]]]] = {
listeners: dict[str, Callable[..., None | Awaitable[None]]] = {
'disconnection': set_failure('connection_died'),
'pairing_failure': set_failure('pairing_failure'),
'connection_authentication_failure': set_failure('authentication_failure'),
@@ -502,7 +502,7 @@ class SecurityService(SecurityServicer):
return WaitSecurityResponse(**kwargs)
async def reached_security_level(
self, connection: BumbleConnection, level: Union[SecurityLevel, LESecurityLevel]
self, connection: BumbleConnection, level: SecurityLevel | LESecurityLevel
) -> bool:
self.log.debug(
str(

View File

@@ -18,7 +18,8 @@ import contextlib
import functools
import inspect
import logging
from typing import Any, Generator, MutableMapping, Optional
from collections.abc import Generator, MutableMapping
from typing import Any
import grpc
from google.protobuf.message import Message # pytype: disable=pyi-error
@@ -34,7 +35,7 @@ ADDRESS_TYPES: dict[str, AddressType] = {
}
def address_from_request(request: Message, field: Optional[str]) -> Address:
def address_from_request(request: Message, field: str | None) -> Address:
if field is None:
return Address.ANY
return Address(bytes(reversed(getattr(request, field))), ADDRESS_TYPES[field])
@@ -95,8 +96,7 @@ def rpc(func: Any) -> Any:
@functools.wraps(func)
def gen_wrapper(self: Any, request: Any, context: grpc.ServicerContext) -> Any:
with exception_to_rpc_error(context):
for v in func(self, request, context):
yield v
yield from func(self, request, context)
@functools.wraps(func)
def wrapper(self: Any, request: Any, context: grpc.ServicerContext) -> Any:

View File

@@ -22,7 +22,6 @@ from __future__ import annotations
import logging
import struct
from dataclasses import dataclass
from typing import Optional
from bumble import utils
from bumble.att import ATT_Error
@@ -129,7 +128,7 @@ class AudioInputState:
mute: Mute = Mute.NOT_MUTED
gain_mode: GainMode = GainMode.MANUAL
change_counter: int = 0
attribute: Optional[Attribute] = None
attribute: Attribute | None = None
def __bytes__(self) -> bytes:
return bytes(
@@ -316,7 +315,7 @@ class AudioInputDescription:
'''
audio_input_description: str = "Bluetooth"
attribute: Optional[Attribute] = None
attribute: Attribute | None = None
def on_read(self, _connection: Connection) -> str:
return self.audio_input_description
@@ -339,11 +338,11 @@ class AICSService(TemplateService):
def __init__(
self,
audio_input_state: Optional[AudioInputState] = None,
gain_settings_properties: Optional[GainSettingsProperties] = None,
audio_input_state: AudioInputState | None = None,
gain_settings_properties: GainSettingsProperties | None = None,
audio_input_type: str = "local",
audio_input_status: Optional[AudioInputStatus] = None,
audio_input_description: Optional[AudioInputDescription] = None,
audio_input_status: AudioInputStatus | None = None,
audio_input_description: AudioInputDescription | None = None,
):
self.audio_input_state = (
AudioInputState() if audio_input_state is None else audio_input_state

View File

@@ -25,7 +25,7 @@ import asyncio
import dataclasses
import enum
import logging
from typing import Iterable, Optional, Union
from collections.abc import Iterable
from bumble import utils
from bumble.device import Peer
@@ -230,7 +230,7 @@ class AmsClient(utils.EventEmitter):
self.supported_commands = set()
@classmethod
async def for_peer(cls, peer: Peer) -> Optional[AmsClient]:
async def for_peer(cls, peer: Peer) -> AmsClient | None:
ams_proxy = await peer.discover_service_and_create_proxy(AmsProxy)
if ams_proxy is None:
return None
@@ -263,9 +263,7 @@ class AmsClient(utils.EventEmitter):
async def observe(
self,
entity: EntityId,
attributes: Iterable[
Union[PlayerAttributeId, QueueAttributeId, TrackAttributeId]
],
attributes: Iterable[PlayerAttributeId | QueueAttributeId | TrackAttributeId],
) -> None:
await self._ams_proxy.entity_update.write_value(
bytes([entity] + list(attributes)), with_response=True

View File

@@ -27,7 +27,7 @@ import datetime
import enum
import logging
import struct
from typing import Optional, Sequence, Union
from collections.abc import Sequence
from bumble import utils
from bumble.att import ATT_Error
@@ -116,7 +116,7 @@ class NotificationAttributeId(utils.OpenIntEnum):
@dataclasses.dataclass
class NotificationAttribute:
attribute_id: NotificationAttributeId
value: Union[str, int, datetime.datetime]
value: str | int | datetime.datetime
@dataclasses.dataclass
@@ -242,10 +242,10 @@ class AncsProxy(ProfileServiceProxy):
class AncsClient(utils.EventEmitter):
_expected_response_command_id: Optional[CommandId]
_expected_response_notification_uid: Optional[int]
_expected_response_app_identifier: Optional[str]
_expected_app_identifier: Optional[str]
_expected_response_command_id: CommandId | None
_expected_response_notification_uid: int | None
_expected_response_app_identifier: str | None
_expected_app_identifier: str | None
_expected_response_tuples: int
_response_accumulator: bytes
@@ -255,12 +255,12 @@ class AncsClient(utils.EventEmitter):
super().__init__()
self._ancs_proxy = ancs_proxy
self._command_semaphore = asyncio.Semaphore()
self._response: Optional[asyncio.Future] = None
self._response: asyncio.Future | None = None
self._reset_response()
self._started = False
@classmethod
async def for_peer(cls, peer: Peer) -> Optional[AncsClient]:
async def for_peer(cls, peer: Peer) -> AncsClient | None:
ancs_proxy = await peer.discover_service_and_create_proxy(AncsProxy)
if ancs_proxy is None:
return None
@@ -316,7 +316,7 @@ class AncsClient(utils.EventEmitter):
# Not enough data yet.
return
attributes: list[Union[NotificationAttribute, AppAttribute]] = []
attributes: list[NotificationAttribute | AppAttribute] = []
if command_id == CommandId.GET_NOTIFICATION_ATTRIBUTES:
(notification_uid,) = struct.unpack_from(
@@ -342,7 +342,7 @@ class AncsClient(utils.EventEmitter):
str_value = attribute_data[3 : 3 + attribute_data_length].decode(
"utf-8"
)
value: Union[str, int, datetime.datetime]
value: str | int | datetime.datetime
if attribute_id == NotificationAttributeId.MESSAGE_SIZE:
value = int(str_value)
elif attribute_id == NotificationAttributeId.DATE:
@@ -415,7 +415,7 @@ class AncsClient(utils.EventEmitter):
self,
notification_uid: int,
attributes: Sequence[
Union[NotificationAttributeId, tuple[NotificationAttributeId, int]]
NotificationAttributeId | tuple[NotificationAttributeId, int]
],
) -> list[NotificationAttribute]:
if not self._started:

View File

@@ -24,7 +24,7 @@ import logging
import struct
from collections.abc import Sequence
from dataclasses import dataclass, field
from typing import Any, Optional, TypeVar, Union
from typing import Any, TypeVar
from bumble import colors, device, gatt, gatt_client, hci, utils
from bumble.profiles import le_audio
@@ -49,7 +49,7 @@ class ASE_Operation:
classes: dict[int, type[ASE_Operation]] = {}
op_code: Opcode
name: str
fields: Optional[Sequence[Any]] = None
fields: Sequence[Any] | None = None
ase_id: Sequence[int]
class Opcode(enum.IntEnum):
@@ -278,7 +278,7 @@ class AseStateMachine(gatt.Characteristic):
EVENT_STATE_CHANGE = "state_change"
cis_link: Optional[device.CisLink] = None
cis_link: device.CisLink | None = None
# Additional parameters in CODEC_CONFIGURED State
preferred_framing = 0 # Unframed PDU supported
@@ -290,7 +290,7 @@ class AseStateMachine(gatt.Characteristic):
preferred_presentation_delay_min = 0
preferred_presentation_delay_max = 0
codec_id = hci.CodingFormat(hci.CodecID.LC3)
codec_specific_configuration: Union[CodecSpecificConfiguration, bytes] = b''
codec_specific_configuration: CodecSpecificConfiguration | bytes = b''
# Additional parameters in QOS_CONFIGURED State
cig_id = 0
@@ -610,7 +610,7 @@ class AudioStreamControlService(gatt.TemplateService):
ase_state_machines: dict[int, AseStateMachine]
ase_control_point: gatt.Characteristic[bytes]
_active_client: Optional[device.Connection] = None
_active_client: device.Connection | None = None
def __init__(
self,

View File

@@ -19,7 +19,8 @@
import enum
import logging
import struct
from typing import Any, Callable, Optional, Union
from collections.abc import Callable
from typing import Any
from bumble import data_types, gatt, gatt_client, l2cap, utils
from bumble.core import AdvertisingData
@@ -90,20 +91,20 @@ class AshaService(gatt.TemplateService):
EVENT_DISCONNECTED = "disconnected"
EVENT_VOLUME_CHANGED = "volume_changed"
audio_sink: Optional[Callable[[bytes], Any]]
active_codec: Optional[Codec] = None
audio_type: Optional[AudioType] = None
volume: Optional[int] = None
other_state: Optional[int] = None
connection: Optional[Connection] = None
audio_sink: Callable[[bytes], Any] | None
active_codec: Codec | None = None
audio_type: AudioType | None = None
volume: int | None = None
other_state: int | None = None
connection: Connection | None = None
def __init__(
self,
capability: int,
hisyncid: Union[list[int], bytes],
hisyncid: list[int] | bytes,
device: Device,
psm: int = 0,
audio_sink: Optional[Callable[[bytes], Any]] = None,
audio_sink: Callable[[bytes], Any] | None = None,
feature_map: int = FeatureMap.LE_COC_AUDIO_OUTPUT_STREAMING_SUPPORTED,
protocol_version: int = 0x01,
render_delay_milliseconds: int = 0,

View File

@@ -21,7 +21,8 @@ from __future__ import annotations
import dataclasses
import logging
import struct
from typing import ClassVar, Optional, Sequence
from collections.abc import Sequence
from typing import ClassVar
from bumble import core, device, gatt, gatt_adapters, gatt_client, hci, utils
@@ -351,7 +352,7 @@ class BroadcastAudioScanServiceProxy(gatt_client.ProfileServiceProxy):
broadcast_audio_scan_control_point: gatt_client.CharacteristicProxy[bytes]
broadcast_receive_states: list[
gatt_client.CharacteristicProxy[Optional[BroadcastReceiveState]]
gatt_client.CharacteristicProxy[BroadcastReceiveState | None]
]
def __init__(self, service_proxy: gatt_client.ServiceProxy):

View File

@@ -16,7 +16,6 @@
# -----------------------------------------------------------------------------
# Imports
# -----------------------------------------------------------------------------
from typing import Optional
from bumble.gatt import (
GATT_BATTERY_LEVEL_CHARACTERISTIC,
@@ -56,7 +55,7 @@ class BatteryService(TemplateService):
class BatteryServiceProxy(ProfileServiceProxy):
SERVICE_CLASS = BatteryService
battery_level: Optional[CharacteristicProxy[int]]
battery_level: CharacteristicProxy[int] | None
def __init__(self, service_proxy):
self.service_proxy = service_proxy

View File

@@ -20,7 +20,6 @@ from __future__ import annotations
import enum
import struct
from typing import Optional
from bumble import core, crypto, device, gatt, gatt_client
@@ -96,17 +95,17 @@ class CoordinatedSetIdentificationService(gatt.TemplateService):
set_identity_resolving_key: bytes
set_identity_resolving_key_characteristic: gatt.Characteristic[bytes]
coordinated_set_size_characteristic: Optional[gatt.Characteristic[bytes]] = None
set_member_lock_characteristic: Optional[gatt.Characteristic[bytes]] = None
set_member_rank_characteristic: Optional[gatt.Characteristic[bytes]] = None
coordinated_set_size_characteristic: gatt.Characteristic[bytes] | None = None
set_member_lock_characteristic: gatt.Characteristic[bytes] | None = None
set_member_rank_characteristic: gatt.Characteristic[bytes] | None = None
def __init__(
self,
set_identity_resolving_key: bytes,
set_identity_resolving_key_type: SirkType,
coordinated_set_size: Optional[int] = None,
set_member_lock: Optional[MemberLock] = None,
set_member_rank: Optional[int] = None,
coordinated_set_size: int | None = None,
set_member_lock: MemberLock | None = None,
set_member_rank: int | None = None,
) -> None:
if len(set_identity_resolving_key) != SET_IDENTITY_RESOLVING_KEY_LENGTH:
raise core.InvalidArgumentError(
@@ -198,9 +197,9 @@ class CoordinatedSetIdentificationProxy(gatt_client.ProfileServiceProxy):
SERVICE_CLASS = CoordinatedSetIdentificationService
set_identity_resolving_key: gatt_client.CharacteristicProxy[bytes]
coordinated_set_size: Optional[gatt_client.CharacteristicProxy[bytes]] = None
set_member_lock: Optional[gatt_client.CharacteristicProxy[bytes]] = None
set_member_rank: Optional[gatt_client.CharacteristicProxy[bytes]] = None
coordinated_set_size: gatt_client.CharacteristicProxy[bytes] | None = None
set_member_lock: gatt_client.CharacteristicProxy[bytes] | None = None
set_member_rank: gatt_client.CharacteristicProxy[bytes] | None = None
def __init__(self, service_proxy: gatt_client.ServiceProxy) -> None:
self.service_proxy = service_proxy

View File

@@ -17,7 +17,6 @@
# Imports
# -----------------------------------------------------------------------------
import struct
from typing import Optional
from bumble.gatt import (
GATT_DEVICE_INFORMATION_SERVICE,
@@ -54,14 +53,14 @@ class DeviceInformationService(TemplateService):
def __init__(
self,
manufacturer_name: Optional[str] = None,
model_number: Optional[str] = None,
serial_number: Optional[str] = None,
hardware_revision: Optional[str] = None,
firmware_revision: Optional[str] = None,
software_revision: Optional[str] = None,
system_id: Optional[tuple[int, int]] = None, # (OUI, Manufacturer ID)
ieee_regulatory_certification_data_list: Optional[bytes] = None,
manufacturer_name: str | None = None,
model_number: str | None = None,
serial_number: str | None = None,
hardware_revision: str | None = None,
firmware_revision: str | None = None,
software_revision: str | None = None,
system_id: tuple[int, int] | None = None, # (OUI, Manufacturer ID)
ieee_regulatory_certification_data_list: bytes | None = None,
# TODO: pnp_id
):
characteristics: list[Characteristic[bytes]] = [
@@ -109,14 +108,14 @@ class DeviceInformationService(TemplateService):
class DeviceInformationServiceProxy(ProfileServiceProxy):
SERVICE_CLASS = DeviceInformationService
manufacturer_name: Optional[CharacteristicProxy[str]]
model_number: Optional[CharacteristicProxy[str]]
serial_number: Optional[CharacteristicProxy[str]]
hardware_revision: Optional[CharacteristicProxy[str]]
firmware_revision: Optional[CharacteristicProxy[str]]
software_revision: Optional[CharacteristicProxy[str]]
system_id: Optional[CharacteristicProxy[tuple[int, int]]]
ieee_regulatory_certification_data_list: Optional[CharacteristicProxy[bytes]]
manufacturer_name: CharacteristicProxy[str] | None
model_number: CharacteristicProxy[str] | None
serial_number: CharacteristicProxy[str] | None
hardware_revision: CharacteristicProxy[str] | None
firmware_revision: CharacteristicProxy[str] | None
software_revision: CharacteristicProxy[str] | None
system_id: CharacteristicProxy[tuple[int, int]] | None
ieee_regulatory_certification_data_list: CharacteristicProxy[bytes] | None
def __init__(self, service_proxy: ServiceProxy):
self.service_proxy = service_proxy

View File

@@ -19,7 +19,6 @@
# -----------------------------------------------------------------------------
import logging
import struct
from typing import Optional, Union
from bumble.core import Appearance
from bumble.gatt import (
@@ -54,7 +53,7 @@ class GenericAccessService(TemplateService):
appearance_characteristic: Characteristic[bytes]
def __init__(
self, device_name: str, appearance: Union[Appearance, tuple[int, int], int] = 0
self, device_name: str, appearance: Appearance | tuple[int, int] | int = 0
):
if isinstance(appearance, int):
appearance_int = appearance
@@ -88,8 +87,8 @@ class GenericAccessService(TemplateService):
class GenericAccessServiceProxy(ProfileServiceProxy):
SERVICE_CLASS = GenericAccessService
device_name: Optional[CharacteristicProxy[str]]
appearance: Optional[CharacteristicProxy[Appearance]]
device_name: CharacteristicProxy[str] | None
appearance: CharacteristicProxy[Appearance] | None
def __init__(self, service_proxy: ServiceProxy):
self.service_proxy = service_proxy

View File

@@ -19,7 +19,6 @@
# -----------------------------------------------------------------------------
import struct
from enum import IntFlag
from typing import Optional
from bumble.gatt import (
GATT_BGR_FEATURES_CHARACTERISTIC,
@@ -77,18 +76,18 @@ class GamingAudioService(TemplateService):
UUID = GATT_GAMING_AUDIO_SERVICE
gmap_role: Characteristic
ugg_features: Optional[Characteristic] = None
ugt_features: Optional[Characteristic] = None
bgs_features: Optional[Characteristic] = None
bgr_features: Optional[Characteristic] = None
ugg_features: Characteristic | None = None
ugt_features: Characteristic | None = None
bgs_features: Characteristic | None = None
bgr_features: Characteristic | None = None
def __init__(
self,
gmap_role: GmapRole,
ugg_features: Optional[UggFeatures] = None,
ugt_features: Optional[UgtFeatures] = None,
bgs_features: Optional[BgsFeatures] = None,
bgr_features: Optional[BgrFeatures] = None,
ugg_features: UggFeatures | None = None,
ugt_features: UgtFeatures | None = None,
bgs_features: BgsFeatures | None = None,
bgr_features: BgrFeatures | None = None,
) -> None:
characteristics = []
@@ -150,10 +149,10 @@ class GamingAudioService(TemplateService):
class GamingAudioServiceProxy(ProfileServiceProxy):
SERVICE_CLASS = GamingAudioService
ugg_features: Optional[CharacteristicProxy[UggFeatures]] = None
ugt_features: Optional[CharacteristicProxy[UgtFeatures]] = None
bgs_features: Optional[CharacteristicProxy[BgsFeatures]] = None
bgr_features: Optional[CharacteristicProxy[BgrFeatures]] = None
ugg_features: CharacteristicProxy[UggFeatures] | None = None
ugt_features: CharacteristicProxy[UgtFeatures] | None = None
bgs_features: CharacteristicProxy[BgsFeatures] | None = None
bgr_features: CharacteristicProxy[BgrFeatures] | None = None
def __init__(self, service_proxy: ServiceProxy) -> None:
self.service_proxy = service_proxy

View File

@@ -20,7 +20,7 @@ from __future__ import annotations
import asyncio
import logging
from dataclasses import dataclass, field
from typing import Any, Optional, Union
from typing import Any
from bumble import att, gatt, gatt_adapters, gatt_client, utils
from bumble.core import InvalidArgumentError, InvalidStateError
@@ -145,7 +145,7 @@ class PresetChangedOperation:
return bytes([self.prev_index]) + bytes(self.preset_record)
change_id: ChangeId
additional_parameters: Union[Generic, int]
additional_parameters: Generic | int
def to_bytes(self, is_last: bool) -> bytes:
if isinstance(self.additional_parameters, PresetChangedOperation.Generic):
@@ -235,7 +235,7 @@ class HearingAccessService(gatt.TemplateService):
preset_records: dict[int, PresetRecord] # key is the preset index
read_presets_request_in_progress: bool
other_server_in_binaural_set: Optional[HearingAccessService] = None
other_server_in_binaural_set: HearingAccessService | None = None
preset_changed_operations_history_per_device: dict[
Address, list[PresetChangedOperation]

View File

@@ -20,7 +20,6 @@ from __future__ import annotations
import struct
from enum import IntEnum
from typing import Optional
from bumble import core
from bumble.att import ATT_Error
@@ -207,13 +206,13 @@ class HeartRateService(TemplateService):
class HeartRateServiceProxy(ProfileServiceProxy):
SERVICE_CLASS = HeartRateService
heart_rate_measurement: Optional[
CharacteristicProxy[HeartRateService.HeartRateMeasurement]
]
body_sensor_location: Optional[
CharacteristicProxy[HeartRateService.BodySensorLocation]
]
heart_rate_control_point: Optional[CharacteristicProxy[int]]
heart_rate_measurement: (
CharacteristicProxy[HeartRateService.HeartRateMeasurement] | None
)
body_sensor_location: (
CharacteristicProxy[HeartRateService.BodySensorLocation] | None
)
heart_rate_control_point: CharacteristicProxy[int] | None
def __init__(self, service_proxy):
self.service_proxy = service_proxy

View File

@@ -22,7 +22,7 @@ import asyncio
import dataclasses
import enum
import struct
from typing import TYPE_CHECKING, ClassVar, Optional
from typing import TYPE_CHECKING, ClassVar
from typing_extensions import Self
@@ -196,7 +196,7 @@ class MediaControlService(gatt.TemplateService):
UUID = gatt.GATT_MEDIA_CONTROL_SERVICE
def __init__(self, media_player_name: Optional[str] = None) -> None:
def __init__(self, media_player_name: str | None = None) -> None:
self.track_position = 0
self.media_player_name_characteristic = gatt.Characteristic(
@@ -337,32 +337,32 @@ class MediaControlServiceProxy(
EVENT_TRACK_DURATION = "track_duration"
EVENT_TRACK_POSITION = "track_position"
media_player_name: Optional[gatt_client.CharacteristicProxy[bytes]] = None
media_player_icon_object_id: Optional[gatt_client.CharacteristicProxy[bytes]] = None
media_player_icon_url: Optional[gatt_client.CharacteristicProxy[bytes]] = None
track_changed: Optional[gatt_client.CharacteristicProxy[bytes]] = None
track_title: Optional[gatt_client.CharacteristicProxy[bytes]] = None
track_duration: Optional[gatt_client.CharacteristicProxy[bytes]] = None
track_position: Optional[gatt_client.CharacteristicProxy[bytes]] = None
playback_speed: Optional[gatt_client.CharacteristicProxy[bytes]] = None
seeking_speed: Optional[gatt_client.CharacteristicProxy[bytes]] = None
current_track_segments_object_id: Optional[
gatt_client.CharacteristicProxy[bytes]
] = None
current_track_object_id: Optional[gatt_client.CharacteristicProxy[bytes]] = None
next_track_object_id: Optional[gatt_client.CharacteristicProxy[bytes]] = None
parent_group_object_id: Optional[gatt_client.CharacteristicProxy[bytes]] = None
current_group_object_id: Optional[gatt_client.CharacteristicProxy[bytes]] = None
playing_order: Optional[gatt_client.CharacteristicProxy[bytes]] = None
playing_orders_supported: Optional[gatt_client.CharacteristicProxy[bytes]] = None
media_state: Optional[gatt_client.CharacteristicProxy[bytes]] = None
media_control_point: Optional[gatt_client.CharacteristicProxy[bytes]] = None
media_control_point_opcodes_supported: Optional[
gatt_client.CharacteristicProxy[bytes]
] = None
search_control_point: Optional[gatt_client.CharacteristicProxy[bytes]] = None
search_results_object_id: Optional[gatt_client.CharacteristicProxy[bytes]] = None
content_control_id: Optional[gatt_client.CharacteristicProxy[bytes]] = None
media_player_name: gatt_client.CharacteristicProxy[bytes] | None = None
media_player_icon_object_id: gatt_client.CharacteristicProxy[bytes] | None = None
media_player_icon_url: gatt_client.CharacteristicProxy[bytes] | None = None
track_changed: gatt_client.CharacteristicProxy[bytes] | None = None
track_title: gatt_client.CharacteristicProxy[bytes] | None = None
track_duration: gatt_client.CharacteristicProxy[bytes] | None = None
track_position: gatt_client.CharacteristicProxy[bytes] | None = None
playback_speed: gatt_client.CharacteristicProxy[bytes] | None = None
seeking_speed: gatt_client.CharacteristicProxy[bytes] | None = None
current_track_segments_object_id: gatt_client.CharacteristicProxy[bytes] | None = (
None
)
current_track_object_id: gatt_client.CharacteristicProxy[bytes] | None = None
next_track_object_id: gatt_client.CharacteristicProxy[bytes] | None = None
parent_group_object_id: gatt_client.CharacteristicProxy[bytes] | None = None
current_group_object_id: gatt_client.CharacteristicProxy[bytes] | None = None
playing_order: gatt_client.CharacteristicProxy[bytes] | None = None
playing_orders_supported: gatt_client.CharacteristicProxy[bytes] | None = None
media_state: gatt_client.CharacteristicProxy[bytes] | None = None
media_control_point: gatt_client.CharacteristicProxy[bytes] | None = None
media_control_point_opcodes_supported: (
gatt_client.CharacteristicProxy[bytes] | None
) = None
search_control_point: gatt_client.CharacteristicProxy[bytes] | None = None
search_results_object_id: gatt_client.CharacteristicProxy[bytes] | None = None
content_control_id: gatt_client.CharacteristicProxy[bytes] | None = None
if TYPE_CHECKING:
media_control_point_notifications: asyncio.Queue[bytes]

View File

@@ -21,7 +21,7 @@ from __future__ import annotations
import dataclasses
import logging
import struct
from typing import Optional, Sequence, Union
from collections.abc import Sequence
from bumble import gatt, gatt_adapters, gatt_client, hci
from bumble.profiles import le_audio
@@ -39,7 +39,7 @@ class PacRecord:
'''Published Audio Capabilities Service, Table 3.2/3.4.'''
coding_format: hci.CodingFormat
codec_specific_capabilities: Union[CodecSpecificCapabilities, bytes]
codec_specific_capabilities: CodecSpecificCapabilities | bytes
metadata: le_audio.Metadata = dataclasses.field(default_factory=le_audio.Metadata)
@classmethod
@@ -56,7 +56,7 @@ class PacRecord:
offset += 1
metadata = le_audio.Metadata.from_bytes(data[offset : offset + metadata_size])
codec_specific_capabilities: Union[CodecSpecificCapabilities, bytes]
codec_specific_capabilities: CodecSpecificCapabilities | bytes
if coding_format.codec_id == hci.CodecID.VENDOR_SPECIFIC:
codec_specific_capabilities = codec_specific_capabilities_bytes
else:
@@ -101,10 +101,10 @@ class PacRecord:
class PublishedAudioCapabilitiesService(gatt.TemplateService):
UUID = gatt.GATT_PUBLISHED_AUDIO_CAPABILITIES_SERVICE
sink_pac: Optional[gatt.Characteristic[bytes]]
sink_audio_locations: Optional[gatt.Characteristic[bytes]]
source_pac: Optional[gatt.Characteristic[bytes]]
source_audio_locations: Optional[gatt.Characteristic[bytes]]
sink_pac: gatt.Characteristic[bytes] | None
sink_audio_locations: gatt.Characteristic[bytes] | None
source_pac: gatt.Characteristic[bytes] | None
source_audio_locations: gatt.Characteristic[bytes] | None
available_audio_contexts: gatt.Characteristic[bytes]
supported_audio_contexts: gatt.Characteristic[bytes]
@@ -115,9 +115,9 @@ class PublishedAudioCapabilitiesService(gatt.TemplateService):
available_source_context: ContextType,
available_sink_context: ContextType,
sink_pac: Sequence[PacRecord] = (),
sink_audio_locations: Optional[AudioLocation] = None,
sink_audio_locations: AudioLocation | None = None,
source_pac: Sequence[PacRecord] = (),
source_audio_locations: Optional[AudioLocation] = None,
source_audio_locations: AudioLocation | None = None,
) -> None:
characteristics = []
@@ -183,14 +183,10 @@ class PublishedAudioCapabilitiesService(gatt.TemplateService):
class PublishedAudioCapabilitiesServiceProxy(gatt_client.ProfileServiceProxy):
SERVICE_CLASS = PublishedAudioCapabilitiesService
sink_pac: Optional[gatt_client.CharacteristicProxy[list[PacRecord]]] = None
sink_audio_locations: Optional[gatt_client.CharacteristicProxy[AudioLocation]] = (
None
)
source_pac: Optional[gatt_client.CharacteristicProxy[list[PacRecord]]] = None
source_audio_locations: Optional[gatt_client.CharacteristicProxy[AudioLocation]] = (
None
)
sink_pac: gatt_client.CharacteristicProxy[list[PacRecord]] | None = None
sink_audio_locations: gatt_client.CharacteristicProxy[AudioLocation] | None = None
source_pac: gatt_client.CharacteristicProxy[list[PacRecord]] | None = None
source_audio_locations: gatt_client.CharacteristicProxy[AudioLocation] | None = None
available_audio_contexts: gatt_client.CharacteristicProxy[tuple[ContextType, ...]]
supported_audio_contexts: gatt_client.CharacteristicProxy[tuple[ContextType, ...]]

View File

@@ -20,7 +20,7 @@ from __future__ import annotations
import dataclasses
import enum
from typing import Sequence
from collections.abc import Sequence
from bumble import att, device, gatt, gatt_adapters, gatt_client

View File

@@ -18,7 +18,6 @@
# -----------------------------------------------------------------------------
import struct
from dataclasses import dataclass
from typing import Optional
from bumble import utils
from bumble.att import ATT_Error
@@ -69,7 +68,7 @@ class ErrorCode(utils.OpenIntEnum):
class VolumeOffsetState:
volume_offset: int = 0
change_counter: int = 0
attribute: Optional[Characteristic] = None
attribute: Characteristic | None = None
def __bytes__(self) -> bytes:
return struct.pack('<hB', self.volume_offset, self.change_counter)
@@ -93,7 +92,7 @@ class VolumeOffsetState:
@dataclass
class VocsAudioLocation:
audio_location: AudioLocation = AudioLocation.NOT_ALLOWED
attribute: Optional[Characteristic] = None
attribute: Characteristic | None = None
def __bytes__(self) -> bytes:
return struct.pack('<I', self.audio_location)
@@ -147,7 +146,7 @@ class VolumeOffsetControlPoint:
@dataclass
class AudioOutputDescription:
audio_output_description: str = ''
attribute: Optional[Characteristic] = None
attribute: Characteristic | None = None
@classmethod
def from_bytes(cls, data: bytes):
@@ -172,9 +171,9 @@ class VolumeOffsetControlService(TemplateService):
def __init__(
self,
volume_offset_state: Optional[VolumeOffsetState] = None,
audio_location: Optional[VocsAudioLocation] = None,
audio_output_description: Optional[AudioOutputDescription] = None,
volume_offset_state: VolumeOffsetState | None = None,
audio_location: VocsAudioLocation | None = None,
audio_output_description: AudioOutputDescription | None = None,
) -> None:
self.volume_offset_state = (
VolumeOffsetState() if volume_offset_state is None else volume_offset_state

View File

@@ -22,7 +22,8 @@ import collections
import dataclasses
import enum
import logging
from typing import TYPE_CHECKING, Callable, Optional, Union
from collections.abc import Callable
from typing import TYPE_CHECKING
from typing_extensions import Self
@@ -119,7 +120,7 @@ RFCOMM_DYNAMIC_CHANNEL_NUMBER_END = 30
# -----------------------------------------------------------------------------
def make_service_sdp_records(
service_record_handle: int, channel: int, uuid: Optional[UUID] = None
service_record_handle: int, channel: int, uuid: UUID | None = None
) -> list[sdp.ServiceAttribute]:
"""
Create SDP records for an RFComm service given a channel number and an
@@ -186,7 +187,7 @@ async def find_rfcomm_channels(connection: Connection) -> dict[int, list[UUID]]:
)
for attribute_lists in search_result:
service_classes: list[UUID] = []
channel: Optional[int] = None
channel: int | None = None
for attribute in attribute_lists:
# The layout is [[L2CAP_PROTOCOL], [RFCOMM_PROTOCOL, RFCOMM_CHANNEL]].
if attribute.id == sdp.SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID:
@@ -207,7 +208,7 @@ async def find_rfcomm_channels(connection: Connection) -> dict[int, list[UUID]]:
# -----------------------------------------------------------------------------
async def find_rfcomm_channel_with_uuid(
connection: Connection, uuid: str | UUID
) -> Optional[int]:
) -> int | None:
"""Searches an RFCOMM channel associated with given UUID from service records.
Args:
@@ -473,15 +474,15 @@ class DLC(utils.EventEmitter):
self.state = DLC.State.INIT
self.role = multiplexer.role
self.c_r = 1 if self.role == Multiplexer.Role.INITIATOR else 0
self.connection_result: Optional[asyncio.Future] = None
self.disconnection_result: Optional[asyncio.Future] = None
self.connection_result: asyncio.Future | None = None
self.disconnection_result: asyncio.Future | None = None
self.drained = asyncio.Event()
self.drained.set()
# Queued packets when sink is not set.
self._enqueued_rx_packets: collections.deque[bytes] = collections.deque(
maxlen=DEFAULT_RX_QUEUE_SIZE
)
self._sink: Optional[Callable[[bytes], None]] = None
self._sink: Callable[[bytes], None] | None = None
# Compute the MTU
max_overhead = 4 + 1 # header with 2-byte length + fcs
@@ -490,11 +491,11 @@ class DLC(utils.EventEmitter):
)
@property
def sink(self) -> Optional[Callable[[bytes], None]]:
def sink(self) -> Callable[[bytes], None] | None:
return self._sink
@sink.setter
def sink(self, sink: Optional[Callable[[bytes], None]]) -> None:
def sink(self, sink: Callable[[bytes], None] | None) -> None:
self._sink = sink
# Dump queued packets to sink
if sink:
@@ -712,7 +713,7 @@ class DLC(utils.EventEmitter):
self.drained.set()
# Stream protocol
def write(self, data: Union[bytes, str]) -> None:
def write(self, data: bytes | str) -> None:
# We can only send bytes
if not isinstance(data, bytes):
if isinstance(data, str):
@@ -769,10 +770,10 @@ class Multiplexer(utils.EventEmitter):
EVENT_DLC = "dlc"
connection_result: Optional[asyncio.Future]
disconnection_result: Optional[asyncio.Future]
open_result: Optional[asyncio.Future]
acceptor: Optional[Callable[[int], Optional[tuple[int, int]]]]
connection_result: asyncio.Future | None
disconnection_result: asyncio.Future | None
open_result: asyncio.Future | None
acceptor: Callable[[int], tuple[int, int] | None] | None
dlcs: dict[int, DLC]
def __init__(self, l2cap_channel: l2cap.ClassicChannel, role: Role) -> None:
@@ -784,7 +785,7 @@ class Multiplexer(utils.EventEmitter):
self.connection_result = None
self.disconnection_result = None
self.open_result = None
self.open_pn: Optional[RFCOMM_MCC_PN] = None
self.open_pn: RFCOMM_MCC_PN | None = None
self.open_rx_max_credits = 0
self.acceptor = None
@@ -1031,8 +1032,8 @@ class Multiplexer(utils.EventEmitter):
# -----------------------------------------------------------------------------
class Client:
multiplexer: Optional[Multiplexer]
l2cap_channel: Optional[l2cap.ClassicChannel]
multiplexer: Multiplexer | None
l2cap_channel: l2cap.ClassicChannel | None
def __init__(
self, connection: Connection, l2cap_mtu: int = RFCOMM_DEFAULT_L2CAP_MTU
@@ -1145,7 +1146,7 @@ class Server(utils.EventEmitter):
# Notify
self.emit(self.EVENT_START, multiplexer)
def accept_dlc(self, channel_number: int) -> Optional[tuple[int, int]]:
def accept_dlc(self, channel_number: int) -> tuple[int, int] | None:
return self.dlc_configs.get(channel_number)
def on_dlc(self, dlc: DLC) -> None:

View File

@@ -20,7 +20,8 @@ from __future__ import annotations
import asyncio
import logging
import struct
from typing import TYPE_CHECKING, Iterable, NewType, Optional, Sequence, Union
from collections.abc import Iterable, Sequence
from typing import TYPE_CHECKING, NewType
from typing_extensions import Self
@@ -497,7 +498,7 @@ class ServiceAttribute:
@staticmethod
def find_attribute_in_list(
attribute_list: Iterable[ServiceAttribute], attribute_id: int
) -> Optional[DataElement]:
) -> DataElement | None:
return next(
(
attribute.value
@@ -778,11 +779,11 @@ class SDP_ServiceSearchAttributeResponse(SDP_PDU):
class Client:
def __init__(self, connection: Connection, mtu: int = 0) -> None:
self.connection = connection
self.channel: Optional[l2cap.ClassicChannel] = None
self.channel: l2cap.ClassicChannel | None = None
self.mtu = mtu
self.request_semaphore = asyncio.Semaphore(1)
self.pending_request: Optional[SDP_PDU] = None
self.pending_response: Optional[asyncio.futures.Future[SDP_PDU]] = None
self.pending_request: SDP_PDU | None = None
self.pending_response: asyncio.futures.Future[SDP_PDU] | None = None
self.next_transaction_id = 0
async def connect(self) -> None:
@@ -898,7 +899,7 @@ class Client:
async def search_attributes(
self,
uuids: Iterable[core.UUID],
attribute_ids: Iterable[Union[int, tuple[int, int]]],
attribute_ids: Iterable[int | tuple[int, int]],
) -> list[list[ServiceAttribute]]:
"""
Search for attributes by UUID and attribute IDs.
@@ -970,7 +971,7 @@ class Client:
async def get_attributes(
self,
service_record_handle: int,
attribute_ids: Iterable[Union[int, tuple[int, int]]],
attribute_ids: Iterable[int | tuple[int, int]],
) -> list[ServiceAttribute]:
"""
Get attributes for a service.
@@ -1042,10 +1043,10 @@ class Client:
# -----------------------------------------------------------------------------
class Server:
CONTINUATION_STATE = bytes([0x01, 0x00])
channel: Optional[l2cap.ClassicChannel]
channel: l2cap.ClassicChannel | None
Service = NewType('Service', list[ServiceAttribute])
service_records: dict[int, Service]
current_response: Union[None, bytes, tuple[int, list[int]]]
current_response: None | bytes | tuple[int, list[int]]
def __init__(self, device: Device) -> None:
self.device = device
@@ -1123,7 +1124,7 @@ class Server:
self,
continuation_state: bytes,
transaction_id: int,
) -> Optional[bool]:
) -> bool | None:
# Check if this is a valid continuation
if len(continuation_state) > 1:
if (

View File

@@ -27,8 +27,9 @@ from __future__ import annotations
import asyncio
import enum
import logging
from collections.abc import Awaitable, Callable
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Awaitable, Callable, ClassVar, Optional, TypeVar, cast
from typing import TYPE_CHECKING, ClassVar, TypeVar, cast
from bumble import crypto, utils
from bumble.colors import color
@@ -204,10 +205,10 @@ class SMP_Command:
fields: ClassVar[Fields]
code: int = field(default=0, init=False)
name: str = field(default='', init=False)
_payload: Optional[bytes] = field(default=None, init=False)
_payload: bytes | None = field(default=None, init=False)
@classmethod
def from_bytes(cls, pdu: bytes) -> "SMP_Command":
def from_bytes(cls, pdu: bytes) -> SMP_Command:
code = pdu[0]
subclass = SMP_Command.smp_classes.get(code)
@@ -545,7 +546,7 @@ class OobContext:
r: bytes
def __init__(
self, ecc_key: Optional[crypto.EccKey] = None, r: Optional[bytes] = None
self, ecc_key: crypto.EccKey | None = None, r: bytes | None = None
) -> None:
self.ecc_key = crypto.EccKey.generate() if ecc_key is None else ecc_key
self.r = crypto.r() if r is None else r
@@ -561,7 +562,7 @@ class OobLegacyContext:
tk: bytes
def __init__(self, tk: Optional[bytes] = None) -> None:
def __init__(self, tk: bytes | None = None) -> None:
self.tk = crypto.r() if tk is None else tk
@@ -668,31 +669,31 @@ class Session:
self.stk = None
self.ltk_ediv = 0
self.ltk_rand = bytes(8)
self.link_key: Optional[bytes] = None
self.link_key: bytes | None = None
self.maximum_encryption_key_size: int = 0
self.initiator_key_distribution: int = 0
self.responder_key_distribution: int = 0
self.peer_random_value: Optional[bytes] = None
self.peer_random_value: bytes | None = None
self.peer_public_key_x: bytes = bytes(32)
self.peer_public_key_y = bytes(32)
self.peer_ltk = None
self.peer_ediv = None
self.peer_rand: Optional[bytes] = None
self.peer_rand: bytes | None = None
self.peer_identity_resolving_key = None
self.peer_bd_addr: Optional[Address] = None
self.peer_bd_addr: Address | None = None
self.peer_signature_key = None
self.peer_expected_distributions: list[type[SMP_Command]] = []
self.dh_key = b''
self.confirm_value = None
self.passkey: Optional[int] = None
self.passkey: int | None = None
self.passkey_ready = asyncio.Event()
self.passkey_step = 0
self.passkey_display = False
self.pairing_method: PairingMethod = PairingMethod.JUST_WORKS
self.pairing_config = pairing_config
self.wait_before_continuing: Optional[asyncio.Future[None]] = None
self.wait_before_continuing: asyncio.Future[None] | None = None
self.completed = False
self.ctkd_task: Optional[Awaitable[None]] = None
self.ctkd_task: Awaitable[None] | None = None
# Decide if we're the initiator or the responder
self.is_initiator = is_initiator
@@ -711,7 +712,7 @@ class Session:
# Create a future that can be used to wait for the session to complete
if self.is_initiator:
self.pairing_result: Optional[asyncio.Future[None]] = (
self.pairing_result: asyncio.Future[None] | None = (
asyncio.get_running_loop().create_future()
)
else:
@@ -819,7 +820,7 @@ class Session:
def auth_req(self) -> int:
return smp_auth_req(self.bonding, self.mitm, self.sc, self.keypress, self.ct2)
def get_long_term_key(self, rand: bytes, ediv: int) -> Optional[bytes]:
def get_long_term_key(self, rand: bytes, ediv: int) -> bytes | None:
if not self.sc and not self.completed:
if rand == self.ltk_rand and ediv == self.ltk_ediv:
return self.stk
@@ -930,7 +931,7 @@ class Session:
self.pairing_config.delegate.display_number(self.passkey, digits=6)
)
def input_passkey(self, next_steps: Optional[Callable[[], None]] = None) -> None:
def input_passkey(self, next_steps: Callable[[], None] | None = None) -> None:
# Prompt the user for the passkey displayed on the peer
def after_input(passkey: int) -> None:
self.passkey = passkey
@@ -947,7 +948,7 @@ class Session:
self.prompt_user_for_number(after_input)
def display_or_input_passkey(
self, next_steps: Optional[Callable[[], None]] = None
self, next_steps: Callable[[], None] | None = None
) -> None:
if self.passkey_display:
@@ -1918,7 +1919,7 @@ class Manager(utils.EventEmitter):
sessions: dict[int, Session]
pairing_config_factory: Callable[[Connection], PairingConfig]
session_proxy: type[Session]
_ecc_key: Optional[crypto.EccKey]
_ecc_key: crypto.EccKey | None
def __init__(
self,
@@ -2011,7 +2012,7 @@ class Manager(utils.EventEmitter):
self.device.on_pairing_start(session.connection)
async def on_pairing(
self, session: Session, identity_address: Optional[Address], keys: PairingKeys
self, session: Session, identity_address: Address | None, keys: PairingKeys
) -> None:
# Store the keys in the key store
if self.device.keystore and identity_address is not None:
@@ -2030,7 +2031,7 @@ class Manager(utils.EventEmitter):
def get_long_term_key(
self, connection: Connection, rand: bytes, ediv: int
) -> Optional[bytes]:
) -> bytes | None:
if session := self.sessions.get(connection.handle):
return session.get_long_term_key(rand, ediv)

View File

@@ -16,13 +16,14 @@ import datetime
import logging
import os
import struct
from collections.abc import Generator
# -----------------------------------------------------------------------------
# Imports
# -----------------------------------------------------------------------------
from contextlib import contextmanager
from enum import IntEnum
from typing import BinaryIO, Generator
from typing import BinaryIO
from bumble import core
from bumble.hci import HCI_COMMAND_PACKET, HCI_EVENT_PACKET

View File

@@ -18,7 +18,6 @@
import logging
import os
import re
from typing import Optional
from bumble import utils
from bumble.snoop import create_snooper
@@ -111,7 +110,7 @@ async def open_transport(name: str) -> Transport:
# -----------------------------------------------------------------------------
async def _open_transport(scheme: str, spec: Optional[str]) -> Transport:
async def _open_transport(scheme: str, spec: str | None) -> Transport:
# pylint: disable=import-outside-toplevel
# pylint: disable=too-many-return-statements

View File

@@ -16,7 +16,6 @@
# Imports
# -----------------------------------------------------------------------------
import logging
from typing import Optional, Union
import grpc.aio
@@ -44,7 +43,7 @@ logger = logging.getLogger(__name__)
# -----------------------------------------------------------------------------
async def open_android_emulator_transport(spec: Optional[str]) -> Transport:
async def open_android_emulator_transport(spec: str | None) -> Transport:
'''
Open a transport connection to an Android emulator via its gRPC interface.
The parameter string has this syntax:
@@ -89,7 +88,7 @@ async def open_android_emulator_transport(spec: Optional[str]) -> Transport:
logger.debug('connecting to gRPC server at %s', server_address)
channel = grpc.aio.insecure_channel(server_address)
service: Union[EmulatedBluetoothServiceStub, VhciForwardingServiceStub]
service: EmulatedBluetoothServiceStub | VhciForwardingServiceStub
if mode == 'host':
# Connect as a host
service = EmulatedBluetoothServiceStub(channel)

View File

@@ -22,7 +22,6 @@ import os
import pathlib
import platform
import sys
from typing import Optional
import grpc.aio
@@ -66,7 +65,7 @@ DEFAULT_VARIANT = ''
# -----------------------------------------------------------------------------
def get_ini_dir() -> Optional[pathlib.Path]:
def get_ini_dir() -> pathlib.Path | None:
if sys.platform == 'darwin':
if tmpdir := os.getenv('TMPDIR', None):
return pathlib.Path(tmpdir)
@@ -100,7 +99,7 @@ def find_grpc_port(instance_number: int) -> int:
ini_file = ini_dir / ini_file_name(instance_number)
logger.debug(f'Looking for .ini file at {ini_file}')
if ini_file.is_file():
with open(ini_file, 'r') as ini_file_data:
with open(ini_file) as ini_file_data:
for line in ini_file_data.readlines():
if '=' in line:
key, value = line.split('=')
@@ -146,7 +145,7 @@ def publish_grpc_port(grpc_port: int, instance_number: int) -> bool:
# -----------------------------------------------------------------------------
async def open_android_netsim_controller_transport(
server_host: Optional[str], server_port: int, options: dict[str, str]
server_host: str | None, server_port: int, options: dict[str, str]
) -> Transport:
if server_host == '_' or not server_host:
server_host = 'localhost'
@@ -301,9 +300,9 @@ async def open_android_netsim_controller_transport(
# -----------------------------------------------------------------------------
async def open_android_netsim_host_transport_with_address(
server_host: Optional[str],
server_host: str | None,
server_port: int,
options: Optional[dict[str, str]] = None,
options: dict[str, str] | None = None,
):
if server_host == '_' or not server_host:
server_host = 'localhost'
@@ -328,7 +327,7 @@ async def open_android_netsim_host_transport_with_address(
# -----------------------------------------------------------------------------
async def open_android_netsim_host_transport_with_channel(
channel, options: Optional[dict[str, str]] = None
channel, options: dict[str, str] | None = None
):
# Wrapper for I/O operations
class HciDevice:
@@ -408,7 +407,7 @@ async def open_android_netsim_host_transport_with_channel(
# -----------------------------------------------------------------------------
async def open_android_netsim_transport(spec: Optional[str]) -> Transport:
async def open_android_netsim_transport(spec: str | None) -> Transport:
'''
Open a transport connection as a client or server, implementing Android's `netsim`
simulator protocol over gRPC.

View File

@@ -23,7 +23,7 @@ import io
import logging
import struct
from collections.abc import Awaitable, Callable
from typing import Any, ContextManager, Optional, Protocol
from typing import Any, Protocol
from bumble import core, hci
from bumble.colors import color
@@ -107,11 +107,11 @@ class PacketParser:
NEED_LENGTH = 1
NEED_BODY = 2
sink: Optional[TransportSink]
sink: TransportSink | None
extended_packet_info: dict[int, tuple[int, int, str]]
packet_info: Optional[tuple[int, int, str]] = None
packet_info: tuple[int, int, str] | None = None
def __init__(self, sink: Optional[TransportSink] = None) -> None:
def __init__(self, sink: TransportSink | None = None) -> None:
self.sink = sink
self.extended_packet_info = {}
self.reset()
@@ -176,7 +176,7 @@ class PacketReader:
self.source = source
self.at_end = False
def next_packet(self) -> Optional[bytes]:
def next_packet(self) -> bytes | None:
# Get the packet type
packet_type = self.source.read(1)
if len(packet_type) != 1:
@@ -253,7 +253,7 @@ class BaseSource:
"""
terminated: asyncio.Future[None]
sink: Optional[TransportSink]
sink: TransportSink | None
def __init__(self) -> None:
self.terminated = asyncio.get_running_loop().create_future()
@@ -357,7 +357,7 @@ class Transport:
# -----------------------------------------------------------------------------
class PumpedPacketSource(ParserSource):
pump_task: Optional[asyncio.Task[None]]
pump_task: asyncio.Task[None] | None
def __init__(self, receive) -> None:
super().__init__()
@@ -390,7 +390,7 @@ class PumpedPacketSource(ParserSource):
# -----------------------------------------------------------------------------
class PumpedPacketSink:
pump_task: Optional[asyncio.Task[None]]
pump_task: asyncio.Task[None] | None
def __init__(self, send: Callable[[bytes], Awaitable[Any]]):
self.send_function = send
@@ -443,7 +443,7 @@ class SnoopingTransport(Transport):
@staticmethod
def create_with(
transport: Transport, snooper: ContextManager[Snooper]
transport: Transport, snooper: contextlib.AbstractContextManager[Snooper]
) -> SnoopingTransport:
"""
Create an instance given a snooper that works as as context manager.

View File

@@ -16,7 +16,6 @@
# Imports
# -----------------------------------------------------------------------------
import asyncio
import io
import logging
from bumble.transport.common import StreamPacketSink, StreamPacketSource, Transport
@@ -36,7 +35,7 @@ async def open_file_transport(spec: str) -> Transport:
'''
# Open the file
file = io.open(spec, 'r+b', buffering=0)
file = open(spec, 'r+b', buffering=0)
# Setup reading
read_transport, packet_source = await asyncio.get_running_loop().connect_read_pipe(

View File

@@ -22,7 +22,6 @@ import logging
import os
import socket
import struct
from typing import Optional
from bumble.transport.common import ParserSource, Transport
@@ -33,7 +32,7 @@ logger = logging.getLogger(__name__)
# -----------------------------------------------------------------------------
async def open_hci_socket_transport(spec: Optional[str]) -> Transport:
async def open_hci_socket_transport(spec: str | None) -> Transport:
'''
Open an HCI Socket (only available on some platforms).
The parameter string is either empty (to use the first/default Bluetooth adapter)
@@ -87,7 +86,7 @@ async def open_hci_socket_transport(spec: Optional[str]) -> Transport:
)
!= 0
):
raise IOError(ctypes.get_errno(), os.strerror(ctypes.get_errno()))
raise OSError(ctypes.get_errno(), os.strerror(ctypes.get_errno()))
class HciSocketSource(ParserSource):
def __init__(self, hci_socket):

View File

@@ -17,12 +17,10 @@
# -----------------------------------------------------------------------------
import asyncio
import atexit
import io
import logging
import os
import pty
import tty
from typing import Optional
from bumble.transport.common import StreamPacketSink, StreamPacketSource, Transport
@@ -33,7 +31,7 @@ logger = logging.getLogger(__name__)
# -----------------------------------------------------------------------------
async def open_pty_transport(spec: Optional[str]) -> Transport:
async def open_pty_transport(spec: str | None) -> Transport:
'''
Open a PTY transport.
The parameter string may be empty, or a path name where a symbolic link
@@ -48,11 +46,11 @@ async def open_pty_transport(spec: Optional[str]) -> Transport:
tty.setraw(replica)
read_transport, packet_source = await asyncio.get_running_loop().connect_read_pipe(
StreamPacketSource, io.open(primary, 'rb', closefd=False)
StreamPacketSource, open(primary, 'rb', closefd=False)
)
write_transport, _ = await asyncio.get_running_loop().connect_write_pipe(
asyncio.BaseProtocol, io.open(primary, 'wb', closefd=False)
asyncio.BaseProtocol, open(primary, 'wb', closefd=False)
)
packet_sink = StreamPacketSink(write_transport)

View File

@@ -19,7 +19,6 @@ import asyncio
import logging
import threading
import time
from typing import Optional
import usb.core
import usb.util
@@ -389,7 +388,7 @@ def _set_port_status(device: UsbDevice, port: int, on: bool):
)
def _find_device_by_path(sys_path: str) -> Optional[UsbDevice]:
def _find_device_by_path(sys_path: str) -> UsbDevice | None:
"""Finds a USB device based on its system path."""
bus_num, *port_parts = sys_path.split('-')
ports = [int(port) for port in port_parts[0].split('.')]
@@ -402,7 +401,7 @@ def _find_device_by_path(sys_path: str) -> Optional[UsbDevice]:
return None
def _find_hub_by_device_path(sys_path: str) -> Optional[UsbDevice]:
def _find_hub_by_device_path(sys_path: str) -> UsbDevice | None:
"""Finds the USB hub associated with a specific device path."""
hub_sys_path = sys_path.rsplit('.', 1)[0]
hub_device = _find_device_by_path(hub_sys_path)

View File

@@ -17,7 +17,6 @@
# -----------------------------------------------------------------------------
import asyncio
import logging
from typing import Optional
import serial_asyncio
@@ -52,7 +51,7 @@ class SerialPacketSource(StreamPacketSource):
logger.debug('connection made')
self._ready.set()
def connection_lost(self, exc: Optional[Exception]) -> None:
def connection_lost(self, exc: Exception | None) -> None:
logger.debug('connection lost')
self.on_transport_lost()

View File

@@ -16,7 +16,6 @@
# Imports
# -----------------------------------------------------------------------------
import logging
from typing import Optional
from bumble.transport.common import Transport
from bumble.transport.file import open_file_transport
@@ -28,7 +27,7 @@ logger = logging.getLogger(__name__)
# -----------------------------------------------------------------------------
async def open_vhci_transport(spec: Optional[str]) -> Transport:
async def open_vhci_transport(spec: str | None) -> Transport:
'''
Open a VHCI transport (only available on some platforms).
The parameter string is either empty (to use the default VHCI device

View File

@@ -16,7 +16,6 @@
# Imports
# -----------------------------------------------------------------------------
import logging
from typing import Optional
import websockets.asyncio.server
@@ -43,8 +42,8 @@ async def open_ws_server_transport(spec: str) -> Transport:
class WsServerTransport(Transport):
sink: PumpedPacketSink
source: ParserSource
connection: Optional[websockets.asyncio.server.ServerConnection]
server: Optional[websockets.asyncio.server.Server]
connection: websockets.asyncio.server.ServerConnection | None
server: websockets.asyncio.server.Server | None
def __init__(self) -> None:
source = ParserSource()

View File

@@ -23,14 +23,11 @@ import enum
import functools
import logging
import warnings
from collections.abc import Awaitable, Callable
from typing import (
Any,
Awaitable,
Callable,
Optional,
Protocol,
TypeVar,
Union,
overload,
)
@@ -169,8 +166,8 @@ class EventWatcher:
) -> _Handler: ...
def on(
self, emitter: pyee.EventEmitter, event: str, handler: Optional[_Handler] = None
) -> Union[_Handler, Callable[[_Handler], _Handler]]:
self, emitter: pyee.EventEmitter, event: str, handler: _Handler | None = None
) -> _Handler | Callable[[_Handler], _Handler]:
'''Watch an event until the context is closed.
Args:
@@ -198,8 +195,8 @@ class EventWatcher:
) -> _Handler: ...
def once(
self, emitter: pyee.EventEmitter, event: str, handler: Optional[_Handler] = None
) -> Union[_Handler, Callable[[_Handler], _Handler]]:
self, emitter: pyee.EventEmitter, event: str, handler: _Handler | None = None
) -> _Handler | Callable[[_Handler], _Handler]:
'''Watch an event for once.
Args:

View File

@@ -18,7 +18,6 @@
import dataclasses
import struct
from dataclasses import field
from typing import Optional
from bumble import hci
@@ -209,7 +208,7 @@ class HCI_Android_Vendor_Event(hci.HCI_Extended_Event):
@classmethod
def subclass_from_parameters(
cls, parameters: bytes
) -> Optional[hci.HCI_Extended_Event]:
) -> hci.HCI_Extended_Event | None:
subevent_code = parameters[0]
if subevent_code == HCI_BLUETOOTH_QUALITY_REPORT_EVENT:
quality_report_id = parameters[1]