mirror of
https://github.com/google/bumble.git
synced 2026-07-19 15:00:48 +00:00
Make all event emitters async
* Also remove AbortableEventEmitter
This commit is contained in:
+3
-5
@@ -18,7 +18,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import asyncio.subprocess
|
|
||||||
import collections
|
import collections
|
||||||
import contextlib
|
import contextlib
|
||||||
import dataclasses
|
import dataclasses
|
||||||
@@ -36,7 +35,6 @@ from typing import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
import click
|
import click
|
||||||
import pyee
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import lc3 # type: ignore # pylint: disable=E0401
|
import lc3 # type: ignore # pylint: disable=E0401
|
||||||
@@ -121,9 +119,9 @@ def broadcast_code_bytes(broadcast_code: str) -> bytes:
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Scan For Broadcasts
|
# Scan For Broadcasts
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class BroadcastScanner(pyee.EventEmitter):
|
class BroadcastScanner(bumble.utils.EventEmitter):
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class Broadcast(pyee.EventEmitter):
|
class Broadcast(bumble.utils.EventEmitter):
|
||||||
name: str | None
|
name: str | None
|
||||||
sync: bumble.device.PeriodicAdvertisingSync
|
sync: bumble.device.PeriodicAdvertisingSync
|
||||||
broadcast_id: int
|
broadcast_id: int
|
||||||
@@ -376,7 +374,7 @@ class BroadcastScanner(pyee.EventEmitter):
|
|||||||
self.emit('broadcast_loss', broadcast)
|
self.emit('broadcast_loss', broadcast)
|
||||||
|
|
||||||
|
|
||||||
class PrintingBroadcastScanner(pyee.EventEmitter):
|
class PrintingBroadcastScanner(bumble.utils.EventEmitter):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, device: bumble.device.Device, filter_duplicates: bool, sync_timeout: float
|
self, device: bumble.device.Device, filter_duplicates: bool, sync_timeout: float
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import click
|
|||||||
import aiohttp.web
|
import aiohttp.web
|
||||||
|
|
||||||
import bumble
|
import bumble
|
||||||
|
from bumble import utils
|
||||||
from bumble.core import AdvertisingData
|
from bumble.core import AdvertisingData
|
||||||
from bumble.colors import color
|
from bumble.colors import color
|
||||||
from bumble.device import Device, DeviceConfiguration, AdvertisingParameters, CisLink
|
from bumble.device import Device, DeviceConfiguration, AdvertisingParameters, CisLink
|
||||||
@@ -359,7 +360,9 @@ class Speaker:
|
|||||||
pcm = decoder.decode(
|
pcm = decoder.decode(
|
||||||
pdu.iso_sdu_fragment, bit_depth=DEFAULT_PCM_BYTES_PER_SAMPLE * 8
|
pdu.iso_sdu_fragment, bit_depth=DEFAULT_PCM_BYTES_PER_SAMPLE * 8
|
||||||
)
|
)
|
||||||
self.device.abort_on('disconnection', self.ui_server.send_audio(pcm))
|
utils.cancel_on_event(
|
||||||
|
self.device, 'disconnection', self.ui_server.send_audio(pcm)
|
||||||
|
)
|
||||||
|
|
||||||
def on_ase_state_change(ase: ascs.AseStateMachine) -> None:
|
def on_ase_state_change(ase: ascs.AseStateMachine) -> None:
|
||||||
codec_config = ase.codec_specific_configuration
|
codec_config = ase.codec_specific_configuration
|
||||||
@@ -373,7 +376,8 @@ class Speaker:
|
|||||||
or codec_config.codec_frames_per_sdu is None
|
or codec_config.codec_frames_per_sdu is None
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
ase.cis_link.abort_on(
|
utils.cancel_on_event(
|
||||||
|
ase.cis_link,
|
||||||
'disconnection',
|
'disconnection',
|
||||||
lc3_source_task(
|
lc3_source_task(
|
||||||
filename=self.lc3_input_file_path,
|
filename=self.lc3_input_file_path,
|
||||||
|
|||||||
+2
-3
@@ -41,7 +41,6 @@ from typing import (
|
|||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
)
|
)
|
||||||
|
|
||||||
from pyee import EventEmitter
|
|
||||||
|
|
||||||
from bumble import utils
|
from bumble import utils
|
||||||
from bumble.core import UUID, name_or_number, InvalidOperationError, ProtocolError
|
from bumble.core import UUID, name_or_number, InvalidOperationError, ProtocolError
|
||||||
@@ -798,7 +797,7 @@ class AttributeValue(Generic[_T]):
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class Attribute(EventEmitter, Generic[_T]):
|
class Attribute(utils.EventEmitter, Generic[_T]):
|
||||||
class Permissions(enum.IntFlag):
|
class Permissions(enum.IntFlag):
|
||||||
READABLE = 0x01
|
READABLE = 0x01
|
||||||
WRITEABLE = 0x02
|
WRITEABLE = 0x02
|
||||||
@@ -845,7 +844,7 @@ class Attribute(EventEmitter, Generic[_T]):
|
|||||||
permissions: Union[str, Attribute.Permissions],
|
permissions: Union[str, Attribute.Permissions],
|
||||||
value: Union[AttributeValue[_T], _T, None] = None,
|
value: Union[AttributeValue[_T], _T, None] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
EventEmitter.__init__(self)
|
utils.EventEmitter.__init__(self)
|
||||||
self.handle = 0
|
self.handle = 0
|
||||||
self.end_group_handle = 0
|
self.end_group_handle = 0
|
||||||
if isinstance(permissions, str):
|
if isinstance(permissions, str):
|
||||||
|
|||||||
+5
-5
@@ -21,7 +21,7 @@ import struct
|
|||||||
from typing import Dict, Type, Union, Tuple
|
from typing import Dict, Type, Union, Tuple
|
||||||
|
|
||||||
from bumble import core
|
from bumble import core
|
||||||
from bumble.utils import OpenIntEnum
|
from bumble import utils
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@@ -43,7 +43,7 @@ class Frame:
|
|||||||
EXTENDED = 0x1E
|
EXTENDED = 0x1E
|
||||||
UNIT = 0x1F
|
UNIT = 0x1F
|
||||||
|
|
||||||
class OperationCode(OpenIntEnum):
|
class OperationCode(utils.OpenIntEnum):
|
||||||
# 0x00 - 0x0F: Unit and subunit commands
|
# 0x00 - 0x0F: Unit and subunit commands
|
||||||
VENDOR_DEPENDENT = 0x00
|
VENDOR_DEPENDENT = 0x00
|
||||||
RESERVE = 0x01
|
RESERVE = 0x01
|
||||||
@@ -204,7 +204,7 @@ class Frame:
|
|||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class CommandFrame(Frame):
|
class CommandFrame(Frame):
|
||||||
class CommandType(OpenIntEnum):
|
class CommandType(utils.OpenIntEnum):
|
||||||
# AV/C Digital Interface Command Set General Specification Version 4.1
|
# AV/C Digital Interface Command Set General Specification Version 4.1
|
||||||
# Table 7.1
|
# Table 7.1
|
||||||
CONTROL = 0x00
|
CONTROL = 0x00
|
||||||
@@ -240,7 +240,7 @@ class CommandFrame(Frame):
|
|||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class ResponseFrame(Frame):
|
class ResponseFrame(Frame):
|
||||||
class ResponseCode(OpenIntEnum):
|
class ResponseCode(utils.OpenIntEnum):
|
||||||
# AV/C Digital Interface Command Set General Specification Version 4.1
|
# AV/C Digital Interface Command Set General Specification Version 4.1
|
||||||
# Table 7.2
|
# Table 7.2
|
||||||
NOT_IMPLEMENTED = 0x08
|
NOT_IMPLEMENTED = 0x08
|
||||||
@@ -368,7 +368,7 @@ class PassThroughFrame:
|
|||||||
PRESSED = 0
|
PRESSED = 0
|
||||||
RELEASED = 1
|
RELEASED = 1
|
||||||
|
|
||||||
class OperationId(OpenIntEnum):
|
class OperationId(utils.OpenIntEnum):
|
||||||
SELECT = 0x00
|
SELECT = 0x00
|
||||||
UP = 0x01
|
UP = 0x01
|
||||||
DOWN = 0x01
|
DOWN = 0x01
|
||||||
|
|||||||
+5
-6
@@ -37,7 +37,6 @@ from typing import (
|
|||||||
cast,
|
cast,
|
||||||
)
|
)
|
||||||
|
|
||||||
from pyee import EventEmitter
|
|
||||||
|
|
||||||
from bumble.core import (
|
from bumble.core import (
|
||||||
BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE,
|
BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE,
|
||||||
@@ -57,7 +56,7 @@ from bumble.a2dp import (
|
|||||||
VendorSpecificMediaCodecInformation,
|
VendorSpecificMediaCodecInformation,
|
||||||
)
|
)
|
||||||
from bumble.rtp import MediaPacket
|
from bumble.rtp import MediaPacket
|
||||||
from bumble import sdp, device, l2cap
|
from bumble import sdp, device, l2cap, utils
|
||||||
from bumble.colors import color
|
from bumble.colors import color
|
||||||
|
|
||||||
|
|
||||||
@@ -1194,7 +1193,7 @@ class DelayReport_Reject(Simple_Reject):
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class Protocol(EventEmitter):
|
class Protocol(utils.EventEmitter):
|
||||||
local_endpoints: List[LocalStreamEndPoint]
|
local_endpoints: List[LocalStreamEndPoint]
|
||||||
remote_endpoints: Dict[int, DiscoveredStreamEndPoint]
|
remote_endpoints: Dict[int, DiscoveredStreamEndPoint]
|
||||||
streams: Dict[int, Stream]
|
streams: Dict[int, Stream]
|
||||||
@@ -1680,7 +1679,7 @@ class Protocol(EventEmitter):
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class Listener(EventEmitter):
|
class Listener(utils.EventEmitter):
|
||||||
servers: Dict[int, Protocol]
|
servers: Dict[int, Protocol]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -2063,7 +2062,7 @@ class DiscoveredStreamEndPoint(StreamEndPoint, StreamEndPointProxy):
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class LocalStreamEndPoint(StreamEndPoint, EventEmitter):
|
class LocalStreamEndPoint(StreamEndPoint, utils.EventEmitter):
|
||||||
stream: Optional[Stream]
|
stream: Optional[Stream]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -2076,7 +2075,7 @@ class LocalStreamEndPoint(StreamEndPoint, EventEmitter):
|
|||||||
configuration: Optional[Iterable[ServiceCapabilities]] = None,
|
configuration: Optional[Iterable[ServiceCapabilities]] = None,
|
||||||
):
|
):
|
||||||
StreamEndPoint.__init__(self, seid, media_type, tsep, 0, capabilities)
|
StreamEndPoint.__init__(self, seid, media_type, tsep, 0, capabilities)
|
||||||
EventEmitter.__init__(self)
|
utils.EventEmitter.__init__(self)
|
||||||
self.protocol = protocol
|
self.protocol = protocol
|
||||||
self.configuration = configuration if configuration is not None else []
|
self.configuration = configuration if configuration is not None else []
|
||||||
self.stream = None
|
self.stream = None
|
||||||
|
|||||||
+18
-19
@@ -38,7 +38,6 @@ from typing import (
|
|||||||
Union,
|
Union,
|
||||||
)
|
)
|
||||||
|
|
||||||
import pyee
|
|
||||||
|
|
||||||
from bumble.colors import color
|
from bumble.colors import color
|
||||||
from bumble.device import Device, Connection
|
from bumble.device import Device, Connection
|
||||||
@@ -53,7 +52,7 @@ from bumble.sdp import (
|
|||||||
DataElement,
|
DataElement,
|
||||||
ServiceAttribute,
|
ServiceAttribute,
|
||||||
)
|
)
|
||||||
from bumble.utils import AsyncRunner, OpenIntEnum
|
from bumble import utils
|
||||||
from bumble.core import (
|
from bumble.core import (
|
||||||
InvalidArgumentError,
|
InvalidArgumentError,
|
||||||
ProtocolError,
|
ProtocolError,
|
||||||
@@ -307,7 +306,7 @@ class Command:
|
|||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class GetCapabilitiesCommand(Command):
|
class GetCapabilitiesCommand(Command):
|
||||||
class CapabilityId(OpenIntEnum):
|
class CapabilityId(utils.OpenIntEnum):
|
||||||
COMPANY_ID = 0x02
|
COMPANY_ID = 0x02
|
||||||
EVENTS_SUPPORTED = 0x03
|
EVENTS_SUPPORTED = 0x03
|
||||||
|
|
||||||
@@ -637,7 +636,7 @@ class RegisterNotificationResponse(Response):
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class EventId(OpenIntEnum):
|
class EventId(utils.OpenIntEnum):
|
||||||
PLAYBACK_STATUS_CHANGED = 0x01
|
PLAYBACK_STATUS_CHANGED = 0x01
|
||||||
TRACK_CHANGED = 0x02
|
TRACK_CHANGED = 0x02
|
||||||
TRACK_REACHED_END = 0x03
|
TRACK_REACHED_END = 0x03
|
||||||
@@ -657,12 +656,12 @@ class EventId(OpenIntEnum):
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class CharacterSetId(OpenIntEnum):
|
class CharacterSetId(utils.OpenIntEnum):
|
||||||
UTF_8 = 0x06
|
UTF_8 = 0x06
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class MediaAttributeId(OpenIntEnum):
|
class MediaAttributeId(utils.OpenIntEnum):
|
||||||
TITLE = 0x01
|
TITLE = 0x01
|
||||||
ARTIST_NAME = 0x02
|
ARTIST_NAME = 0x02
|
||||||
ALBUM_NAME = 0x03
|
ALBUM_NAME = 0x03
|
||||||
@@ -682,7 +681,7 @@ class MediaAttribute:
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class PlayStatus(OpenIntEnum):
|
class PlayStatus(utils.OpenIntEnum):
|
||||||
STOPPED = 0x00
|
STOPPED = 0x00
|
||||||
PLAYING = 0x01
|
PLAYING = 0x01
|
||||||
PAUSED = 0x02
|
PAUSED = 0x02
|
||||||
@@ -701,33 +700,33 @@ class SongAndPlayStatus:
|
|||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class ApplicationSetting:
|
class ApplicationSetting:
|
||||||
class AttributeId(OpenIntEnum):
|
class AttributeId(utils.OpenIntEnum):
|
||||||
EQUALIZER_ON_OFF = 0x01
|
EQUALIZER_ON_OFF = 0x01
|
||||||
REPEAT_MODE = 0x02
|
REPEAT_MODE = 0x02
|
||||||
SHUFFLE_ON_OFF = 0x03
|
SHUFFLE_ON_OFF = 0x03
|
||||||
SCAN_ON_OFF = 0x04
|
SCAN_ON_OFF = 0x04
|
||||||
|
|
||||||
class EqualizerOnOffStatus(OpenIntEnum):
|
class EqualizerOnOffStatus(utils.OpenIntEnum):
|
||||||
OFF = 0x01
|
OFF = 0x01
|
||||||
ON = 0x02
|
ON = 0x02
|
||||||
|
|
||||||
class RepeatModeStatus(OpenIntEnum):
|
class RepeatModeStatus(utils.OpenIntEnum):
|
||||||
OFF = 0x01
|
OFF = 0x01
|
||||||
SINGLE_TRACK_REPEAT = 0x02
|
SINGLE_TRACK_REPEAT = 0x02
|
||||||
ALL_TRACK_REPEAT = 0x03
|
ALL_TRACK_REPEAT = 0x03
|
||||||
GROUP_REPEAT = 0x04
|
GROUP_REPEAT = 0x04
|
||||||
|
|
||||||
class ShuffleOnOffStatus(OpenIntEnum):
|
class ShuffleOnOffStatus(utils.OpenIntEnum):
|
||||||
OFF = 0x01
|
OFF = 0x01
|
||||||
ALL_TRACKS_SHUFFLE = 0x02
|
ALL_TRACKS_SHUFFLE = 0x02
|
||||||
GROUP_SHUFFLE = 0x03
|
GROUP_SHUFFLE = 0x03
|
||||||
|
|
||||||
class ScanOnOffStatus(OpenIntEnum):
|
class ScanOnOffStatus(utils.OpenIntEnum):
|
||||||
OFF = 0x01
|
OFF = 0x01
|
||||||
ALL_TRACKS_SCAN = 0x02
|
ALL_TRACKS_SCAN = 0x02
|
||||||
GROUP_SCAN = 0x03
|
GROUP_SCAN = 0x03
|
||||||
|
|
||||||
class GenericValue(OpenIntEnum):
|
class GenericValue(utils.OpenIntEnum):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -816,7 +815,7 @@ class PlayerApplicationSettingChangedEvent(Event):
|
|||||||
@dataclass
|
@dataclass
|
||||||
class Setting:
|
class Setting:
|
||||||
attribute_id: ApplicationSetting.AttributeId
|
attribute_id: ApplicationSetting.AttributeId
|
||||||
value_id: OpenIntEnum
|
value_id: utils.OpenIntEnum
|
||||||
|
|
||||||
player_application_settings: List[Setting]
|
player_application_settings: List[Setting]
|
||||||
|
|
||||||
@@ -824,7 +823,7 @@ class PlayerApplicationSettingChangedEvent(Event):
|
|||||||
def from_bytes(cls, pdu: bytes) -> PlayerApplicationSettingChangedEvent:
|
def from_bytes(cls, pdu: bytes) -> PlayerApplicationSettingChangedEvent:
|
||||||
def setting(attribute_id_int: int, value_id_int: int):
|
def setting(attribute_id_int: int, value_id_int: int):
|
||||||
attribute_id = ApplicationSetting.AttributeId(attribute_id_int)
|
attribute_id = ApplicationSetting.AttributeId(attribute_id_int)
|
||||||
value_id: OpenIntEnum
|
value_id: utils.OpenIntEnum
|
||||||
if attribute_id == ApplicationSetting.AttributeId.EQUALIZER_ON_OFF:
|
if attribute_id == ApplicationSetting.AttributeId.EQUALIZER_ON_OFF:
|
||||||
value_id = ApplicationSetting.EqualizerOnOffStatus(value_id_int)
|
value_id = ApplicationSetting.EqualizerOnOffStatus(value_id_int)
|
||||||
elif attribute_id == ApplicationSetting.AttributeId.REPEAT_MODE:
|
elif attribute_id == ApplicationSetting.AttributeId.REPEAT_MODE:
|
||||||
@@ -994,7 +993,7 @@ class Delegate:
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class Protocol(pyee.EventEmitter):
|
class Protocol(utils.EventEmitter):
|
||||||
"""AVRCP Controller and Target protocol."""
|
"""AVRCP Controller and Target protocol."""
|
||||||
|
|
||||||
class PacketType(enum.IntEnum):
|
class PacketType(enum.IntEnum):
|
||||||
@@ -1003,7 +1002,7 @@ class Protocol(pyee.EventEmitter):
|
|||||||
CONTINUE = 0b10
|
CONTINUE = 0b10
|
||||||
END = 0b11
|
END = 0b11
|
||||||
|
|
||||||
class PduId(OpenIntEnum):
|
class PduId(utils.OpenIntEnum):
|
||||||
GET_CAPABILITIES = 0x10
|
GET_CAPABILITIES = 0x10
|
||||||
LIST_PLAYER_APPLICATION_SETTING_ATTRIBUTES = 0x11
|
LIST_PLAYER_APPLICATION_SETTING_ATTRIBUTES = 0x11
|
||||||
LIST_PLAYER_APPLICATION_SETTING_VALUES = 0x12
|
LIST_PLAYER_APPLICATION_SETTING_VALUES = 0x12
|
||||||
@@ -1024,7 +1023,7 @@ class Protocol(pyee.EventEmitter):
|
|||||||
GET_FOLDER_ITEMS = 0x71
|
GET_FOLDER_ITEMS = 0x71
|
||||||
GET_TOTAL_NUMBER_OF_ITEMS = 0x75
|
GET_TOTAL_NUMBER_OF_ITEMS = 0x75
|
||||||
|
|
||||||
class StatusCode(OpenIntEnum):
|
class StatusCode(utils.OpenIntEnum):
|
||||||
INVALID_COMMAND = 0x00
|
INVALID_COMMAND = 0x00
|
||||||
INVALID_PARAMETER = 0x01
|
INVALID_PARAMETER = 0x01
|
||||||
PARAMETER_CONTENT_ERROR = 0x02
|
PARAMETER_CONTENT_ERROR = 0x02
|
||||||
@@ -1466,7 +1465,7 @@ class Protocol(pyee.EventEmitter):
|
|||||||
if self.avctp_protocol is not None:
|
if self.avctp_protocol is not None:
|
||||||
# TODO: find a better strategy instead of just closing
|
# TODO: find a better strategy instead of just closing
|
||||||
logger.warning("AVCTP protocol already active, closing connection")
|
logger.warning("AVCTP protocol already active, closing connection")
|
||||||
AsyncRunner.spawn(l2cap_channel.disconnect())
|
utils.AsyncRunner.spawn(l2cap_channel.disconnect())
|
||||||
return
|
return
|
||||||
|
|
||||||
self.avctp_protocol = avctp.Protocol(l2cap_channel)
|
self.avctp_protocol = avctp.Protocol(l2cap_channel)
|
||||||
|
|||||||
+56
-56
@@ -23,7 +23,7 @@ from typing import cast, overload, Literal, Union, Optional
|
|||||||
from typing_extensions import Self
|
from typing_extensions import Self
|
||||||
|
|
||||||
from bumble.company_ids import COMPANY_IDENTIFIERS
|
from bumble.company_ids import COMPANY_IDENTIFIERS
|
||||||
from bumble.utils import OpenIntEnum
|
from bumble import utils
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@@ -730,7 +730,7 @@ class DeviceClass:
|
|||||||
# Appearance
|
# Appearance
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class Appearance:
|
class Appearance:
|
||||||
class Category(OpenIntEnum):
|
class Category(utils.OpenIntEnum):
|
||||||
UNKNOWN = 0x0000
|
UNKNOWN = 0x0000
|
||||||
PHONE = 0x0001
|
PHONE = 0x0001
|
||||||
COMPUTER = 0x0002
|
COMPUTER = 0x0002
|
||||||
@@ -784,13 +784,13 @@ class Appearance:
|
|||||||
SPIROMETER = 0x0037
|
SPIROMETER = 0x0037
|
||||||
OUTDOOR_SPORTS_ACTIVITY = 0x0051
|
OUTDOOR_SPORTS_ACTIVITY = 0x0051
|
||||||
|
|
||||||
class UnknownSubcategory(OpenIntEnum):
|
class UnknownSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_UNKNOWN = 0x00
|
GENERIC_UNKNOWN = 0x00
|
||||||
|
|
||||||
class PhoneSubcategory(OpenIntEnum):
|
class PhoneSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_PHONE = 0x00
|
GENERIC_PHONE = 0x00
|
||||||
|
|
||||||
class ComputerSubcategory(OpenIntEnum):
|
class ComputerSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_COMPUTER = 0x00
|
GENERIC_COMPUTER = 0x00
|
||||||
DESKTOP_WORKSTATION = 0x01
|
DESKTOP_WORKSTATION = 0x01
|
||||||
SERVER_CLASS_COMPUTER = 0x02
|
SERVER_CLASS_COMPUTER = 0x02
|
||||||
@@ -808,49 +808,49 @@ class Appearance:
|
|||||||
MINI_PC = 0x0E
|
MINI_PC = 0x0E
|
||||||
STICK_PC = 0x0F
|
STICK_PC = 0x0F
|
||||||
|
|
||||||
class WatchSubcategory(OpenIntEnum):
|
class WatchSubcategory(utils.OpenIntEnum):
|
||||||
GENENERIC_WATCH = 0x00
|
GENENERIC_WATCH = 0x00
|
||||||
SPORTS_WATCH = 0x01
|
SPORTS_WATCH = 0x01
|
||||||
SMARTWATCH = 0x02
|
SMARTWATCH = 0x02
|
||||||
|
|
||||||
class ClockSubcategory(OpenIntEnum):
|
class ClockSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_CLOCK = 0x00
|
GENERIC_CLOCK = 0x00
|
||||||
|
|
||||||
class DisplaySubcategory(OpenIntEnum):
|
class DisplaySubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_DISPLAY = 0x00
|
GENERIC_DISPLAY = 0x00
|
||||||
|
|
||||||
class RemoteControlSubcategory(OpenIntEnum):
|
class RemoteControlSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_REMOTE_CONTROL = 0x00
|
GENERIC_REMOTE_CONTROL = 0x00
|
||||||
|
|
||||||
class EyeglassesSubcategory(OpenIntEnum):
|
class EyeglassesSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_EYEGLASSES = 0x00
|
GENERIC_EYEGLASSES = 0x00
|
||||||
|
|
||||||
class TagSubcategory(OpenIntEnum):
|
class TagSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_TAG = 0x00
|
GENERIC_TAG = 0x00
|
||||||
|
|
||||||
class KeyringSubcategory(OpenIntEnum):
|
class KeyringSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_KEYRING = 0x00
|
GENERIC_KEYRING = 0x00
|
||||||
|
|
||||||
class MediaPlayerSubcategory(OpenIntEnum):
|
class MediaPlayerSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_MEDIA_PLAYER = 0x00
|
GENERIC_MEDIA_PLAYER = 0x00
|
||||||
|
|
||||||
class BarcodeScannerSubcategory(OpenIntEnum):
|
class BarcodeScannerSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_BARCODE_SCANNER = 0x00
|
GENERIC_BARCODE_SCANNER = 0x00
|
||||||
|
|
||||||
class ThermometerSubcategory(OpenIntEnum):
|
class ThermometerSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_THERMOMETER = 0x00
|
GENERIC_THERMOMETER = 0x00
|
||||||
EAR_THERMOMETER = 0x01
|
EAR_THERMOMETER = 0x01
|
||||||
|
|
||||||
class HeartRateSensorSubcategory(OpenIntEnum):
|
class HeartRateSensorSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_HEART_RATE_SENSOR = 0x00
|
GENERIC_HEART_RATE_SENSOR = 0x00
|
||||||
HEART_RATE_BELT = 0x01
|
HEART_RATE_BELT = 0x01
|
||||||
|
|
||||||
class BloodPressureSubcategory(OpenIntEnum):
|
class BloodPressureSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_BLOOD_PRESSURE = 0x00
|
GENERIC_BLOOD_PRESSURE = 0x00
|
||||||
ARM_BLOOD_PRESSURE = 0x01
|
ARM_BLOOD_PRESSURE = 0x01
|
||||||
WRIST_BLOOD_PRESSURE = 0x02
|
WRIST_BLOOD_PRESSURE = 0x02
|
||||||
|
|
||||||
class HumanInterfaceDeviceSubcategory(OpenIntEnum):
|
class HumanInterfaceDeviceSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_HUMAN_INTERFACE_DEVICE = 0x00
|
GENERIC_HUMAN_INTERFACE_DEVICE = 0x00
|
||||||
KEYBOARD = 0x01
|
KEYBOARD = 0x01
|
||||||
MOUSE = 0x02
|
MOUSE = 0x02
|
||||||
@@ -863,16 +863,16 @@ class Appearance:
|
|||||||
TOUCHPAD = 0x09
|
TOUCHPAD = 0x09
|
||||||
PRESENTATION_REMOTE = 0x0A
|
PRESENTATION_REMOTE = 0x0A
|
||||||
|
|
||||||
class GlucoseMeterSubcategory(OpenIntEnum):
|
class GlucoseMeterSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_GLUCOSE_METER = 0x00
|
GENERIC_GLUCOSE_METER = 0x00
|
||||||
|
|
||||||
class RunningWalkingSensorSubcategory(OpenIntEnum):
|
class RunningWalkingSensorSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_RUNNING_WALKING_SENSOR = 0x00
|
GENERIC_RUNNING_WALKING_SENSOR = 0x00
|
||||||
IN_SHOE_RUNNING_WALKING_SENSOR = 0x01
|
IN_SHOE_RUNNING_WALKING_SENSOR = 0x01
|
||||||
ON_SHOW_RUNNING_WALKING_SENSOR = 0x02
|
ON_SHOW_RUNNING_WALKING_SENSOR = 0x02
|
||||||
ON_HIP_RUNNING_WALKING_SENSOR = 0x03
|
ON_HIP_RUNNING_WALKING_SENSOR = 0x03
|
||||||
|
|
||||||
class CyclingSubcategory(OpenIntEnum):
|
class CyclingSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_CYCLING = 0x00
|
GENERIC_CYCLING = 0x00
|
||||||
CYCLING_COMPUTER = 0x01
|
CYCLING_COMPUTER = 0x01
|
||||||
SPEED_SENSOR = 0x02
|
SPEED_SENSOR = 0x02
|
||||||
@@ -880,7 +880,7 @@ class Appearance:
|
|||||||
POWER_SENSOR = 0x04
|
POWER_SENSOR = 0x04
|
||||||
SPEED_AND_CADENCE_SENSOR = 0x05
|
SPEED_AND_CADENCE_SENSOR = 0x05
|
||||||
|
|
||||||
class ControlDeviceSubcategory(OpenIntEnum):
|
class ControlDeviceSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_CONTROL_DEVICE = 0x00
|
GENERIC_CONTROL_DEVICE = 0x00
|
||||||
SWITCH = 0x01
|
SWITCH = 0x01
|
||||||
MULTI_SWITCH = 0x02
|
MULTI_SWITCH = 0x02
|
||||||
@@ -895,13 +895,13 @@ class Appearance:
|
|||||||
ENERGY_HARVESTING_SWITCH = 0x0B
|
ENERGY_HARVESTING_SWITCH = 0x0B
|
||||||
PUSH_BUTTON = 0x0C
|
PUSH_BUTTON = 0x0C
|
||||||
|
|
||||||
class NetworkDeviceSubcategory(OpenIntEnum):
|
class NetworkDeviceSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_NETWORK_DEVICE = 0x00
|
GENERIC_NETWORK_DEVICE = 0x00
|
||||||
ACCESS_POINT = 0x01
|
ACCESS_POINT = 0x01
|
||||||
MESH_DEVICE = 0x02
|
MESH_DEVICE = 0x02
|
||||||
MESH_NETWORK_PROXY = 0x03
|
MESH_NETWORK_PROXY = 0x03
|
||||||
|
|
||||||
class SensorSubcategory(OpenIntEnum):
|
class SensorSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_SENSOR = 0x00
|
GENERIC_SENSOR = 0x00
|
||||||
MOTION_SENSOR = 0x01
|
MOTION_SENSOR = 0x01
|
||||||
AIR_QUALITY_SENSOR = 0x02
|
AIR_QUALITY_SENSOR = 0x02
|
||||||
@@ -929,7 +929,7 @@ class Appearance:
|
|||||||
FLAME_DETECTOR = 0x18
|
FLAME_DETECTOR = 0x18
|
||||||
VEHICLE_TIRE_PRESSURE_SENSOR = 0x19
|
VEHICLE_TIRE_PRESSURE_SENSOR = 0x19
|
||||||
|
|
||||||
class LightFixturesSubcategory(OpenIntEnum):
|
class LightFixturesSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_LIGHT_FIXTURES = 0x00
|
GENERIC_LIGHT_FIXTURES = 0x00
|
||||||
WALL_LIGHT = 0x01
|
WALL_LIGHT = 0x01
|
||||||
CEILING_LIGHT = 0x02
|
CEILING_LIGHT = 0x02
|
||||||
@@ -957,7 +957,7 @@ class Appearance:
|
|||||||
LOW_BAY_LIGHT = 0x18
|
LOW_BAY_LIGHT = 0x18
|
||||||
HIGH_BAY_LIGHT = 0x19
|
HIGH_BAY_LIGHT = 0x19
|
||||||
|
|
||||||
class FanSubcategory(OpenIntEnum):
|
class FanSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_FAN = 0x00
|
GENERIC_FAN = 0x00
|
||||||
CEILING_FAN = 0x01
|
CEILING_FAN = 0x01
|
||||||
AXIAL_FAN = 0x02
|
AXIAL_FAN = 0x02
|
||||||
@@ -966,7 +966,7 @@ class Appearance:
|
|||||||
DESK_FAN = 0x05
|
DESK_FAN = 0x05
|
||||||
WALL_FAN = 0x06
|
WALL_FAN = 0x06
|
||||||
|
|
||||||
class HvacSubcategory(OpenIntEnum):
|
class HvacSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_HVAC = 0x00
|
GENERIC_HVAC = 0x00
|
||||||
THERMOSTAT = 0x01
|
THERMOSTAT = 0x01
|
||||||
HUMIDIFIER = 0x02
|
HUMIDIFIER = 0x02
|
||||||
@@ -980,13 +980,13 @@ class Appearance:
|
|||||||
FAN_HEATER = 0x0A
|
FAN_HEATER = 0x0A
|
||||||
AIR_CURTAIN = 0x0B
|
AIR_CURTAIN = 0x0B
|
||||||
|
|
||||||
class AirConditioningSubcategory(OpenIntEnum):
|
class AirConditioningSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_AIR_CONDITIONING = 0x00
|
GENERIC_AIR_CONDITIONING = 0x00
|
||||||
|
|
||||||
class HumidifierSubcategory(OpenIntEnum):
|
class HumidifierSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_HUMIDIFIER = 0x00
|
GENERIC_HUMIDIFIER = 0x00
|
||||||
|
|
||||||
class HeatingSubcategory(OpenIntEnum):
|
class HeatingSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_HEATING = 0x00
|
GENERIC_HEATING = 0x00
|
||||||
RADIATOR = 0x01
|
RADIATOR = 0x01
|
||||||
BOILER = 0x02
|
BOILER = 0x02
|
||||||
@@ -996,7 +996,7 @@ class Appearance:
|
|||||||
FAN_HEATER = 0x06
|
FAN_HEATER = 0x06
|
||||||
AIR_CURTAIN = 0x07
|
AIR_CURTAIN = 0x07
|
||||||
|
|
||||||
class AccessControlSubcategory(OpenIntEnum):
|
class AccessControlSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_ACCESS_CONTROL = 0x00
|
GENERIC_ACCESS_CONTROL = 0x00
|
||||||
ACCESS_DOOR = 0x01
|
ACCESS_DOOR = 0x01
|
||||||
GARAGE_DOOR = 0x02
|
GARAGE_DOOR = 0x02
|
||||||
@@ -1008,7 +1008,7 @@ class Appearance:
|
|||||||
DOOR_LOCK = 0x08
|
DOOR_LOCK = 0x08
|
||||||
LOCKER = 0x09
|
LOCKER = 0x09
|
||||||
|
|
||||||
class MotorizedDeviceSubcategory(OpenIntEnum):
|
class MotorizedDeviceSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_MOTORIZED_DEVICE = 0x00
|
GENERIC_MOTORIZED_DEVICE = 0x00
|
||||||
MOTORIZED_GATE = 0x01
|
MOTORIZED_GATE = 0x01
|
||||||
AWNING = 0x02
|
AWNING = 0x02
|
||||||
@@ -1016,7 +1016,7 @@ class Appearance:
|
|||||||
CURTAINS = 0x04
|
CURTAINS = 0x04
|
||||||
SCREEN = 0x05
|
SCREEN = 0x05
|
||||||
|
|
||||||
class PowerDeviceSubcategory(OpenIntEnum):
|
class PowerDeviceSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_POWER_DEVICE = 0x00
|
GENERIC_POWER_DEVICE = 0x00
|
||||||
POWER_OUTLET = 0x01
|
POWER_OUTLET = 0x01
|
||||||
POWER_STRIP = 0x02
|
POWER_STRIP = 0x02
|
||||||
@@ -1028,7 +1028,7 @@ class Appearance:
|
|||||||
CHARGE_CASE = 0x08
|
CHARGE_CASE = 0x08
|
||||||
POWER_BANK = 0x09
|
POWER_BANK = 0x09
|
||||||
|
|
||||||
class LightSourceSubcategory(OpenIntEnum):
|
class LightSourceSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_LIGHT_SOURCE = 0x00
|
GENERIC_LIGHT_SOURCE = 0x00
|
||||||
INCANDESCENT_LIGHT_BULB = 0x01
|
INCANDESCENT_LIGHT_BULB = 0x01
|
||||||
LED_LAMP = 0x02
|
LED_LAMP = 0x02
|
||||||
@@ -1039,7 +1039,7 @@ class Appearance:
|
|||||||
LOW_VOLTAGE_HALOGEN = 0x07
|
LOW_VOLTAGE_HALOGEN = 0x07
|
||||||
ORGANIC_LIGHT_EMITTING_DIODE = 0x08
|
ORGANIC_LIGHT_EMITTING_DIODE = 0x08
|
||||||
|
|
||||||
class WindowCoveringSubcategory(OpenIntEnum):
|
class WindowCoveringSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_WINDOW_COVERING = 0x00
|
GENERIC_WINDOW_COVERING = 0x00
|
||||||
WINDOW_SHADES = 0x01
|
WINDOW_SHADES = 0x01
|
||||||
WINDOW_BLINDS = 0x02
|
WINDOW_BLINDS = 0x02
|
||||||
@@ -1048,7 +1048,7 @@ class Appearance:
|
|||||||
EXTERIOR_SHUTTER = 0x05
|
EXTERIOR_SHUTTER = 0x05
|
||||||
EXTERIOR_SCREEN = 0x06
|
EXTERIOR_SCREEN = 0x06
|
||||||
|
|
||||||
class AudioSinkSubcategory(OpenIntEnum):
|
class AudioSinkSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_AUDIO_SINK = 0x00
|
GENERIC_AUDIO_SINK = 0x00
|
||||||
STANDALONE_SPEAKER = 0x01
|
STANDALONE_SPEAKER = 0x01
|
||||||
SOUNDBAR = 0x02
|
SOUNDBAR = 0x02
|
||||||
@@ -1056,7 +1056,7 @@ class Appearance:
|
|||||||
STANDMOUNTED_SPEAKER = 0x04
|
STANDMOUNTED_SPEAKER = 0x04
|
||||||
SPEAKERPHONE = 0x05
|
SPEAKERPHONE = 0x05
|
||||||
|
|
||||||
class AudioSourceSubcategory(OpenIntEnum):
|
class AudioSourceSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_AUDIO_SOURCE = 0x00
|
GENERIC_AUDIO_SOURCE = 0x00
|
||||||
MICROPHONE = 0x01
|
MICROPHONE = 0x01
|
||||||
ALARM = 0x02
|
ALARM = 0x02
|
||||||
@@ -1068,7 +1068,7 @@ class Appearance:
|
|||||||
BROADCASTING_ROOM = 0x08
|
BROADCASTING_ROOM = 0x08
|
||||||
AUDITORIUM = 0x09
|
AUDITORIUM = 0x09
|
||||||
|
|
||||||
class MotorizedVehicleSubcategory(OpenIntEnum):
|
class MotorizedVehicleSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_MOTORIZED_VEHICLE = 0x00
|
GENERIC_MOTORIZED_VEHICLE = 0x00
|
||||||
CAR = 0x01
|
CAR = 0x01
|
||||||
LARGE_GOODS_VEHICLE = 0x02
|
LARGE_GOODS_VEHICLE = 0x02
|
||||||
@@ -1086,7 +1086,7 @@ class Appearance:
|
|||||||
CAMPER_CARAVAN = 0x0E
|
CAMPER_CARAVAN = 0x0E
|
||||||
RECREATIONAL_VEHICLE_MOTOR_HOME = 0x0F
|
RECREATIONAL_VEHICLE_MOTOR_HOME = 0x0F
|
||||||
|
|
||||||
class DomesticApplianceSubcategory(OpenIntEnum):
|
class DomesticApplianceSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_DOMESTIC_APPLIANCE = 0x00
|
GENERIC_DOMESTIC_APPLIANCE = 0x00
|
||||||
REFRIGERATOR = 0x01
|
REFRIGERATOR = 0x01
|
||||||
FREEZER = 0x02
|
FREEZER = 0x02
|
||||||
@@ -1104,21 +1104,21 @@ class Appearance:
|
|||||||
RICE_COOKER = 0x0E
|
RICE_COOKER = 0x0E
|
||||||
CLOTHES_STEAMER = 0x0F
|
CLOTHES_STEAMER = 0x0F
|
||||||
|
|
||||||
class WearableAudioDeviceSubcategory(OpenIntEnum):
|
class WearableAudioDeviceSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_WEARABLE_AUDIO_DEVICE = 0x00
|
GENERIC_WEARABLE_AUDIO_DEVICE = 0x00
|
||||||
EARBUD = 0x01
|
EARBUD = 0x01
|
||||||
HEADSET = 0x02
|
HEADSET = 0x02
|
||||||
HEADPHONES = 0x03
|
HEADPHONES = 0x03
|
||||||
NECK_BAND = 0x04
|
NECK_BAND = 0x04
|
||||||
|
|
||||||
class AircraftSubcategory(OpenIntEnum):
|
class AircraftSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_AIRCRAFT = 0x00
|
GENERIC_AIRCRAFT = 0x00
|
||||||
LIGHT_AIRCRAFT = 0x01
|
LIGHT_AIRCRAFT = 0x01
|
||||||
MICROLIGHT = 0x02
|
MICROLIGHT = 0x02
|
||||||
PARAGLIDER = 0x03
|
PARAGLIDER = 0x03
|
||||||
LARGE_PASSENGER_AIRCRAFT = 0x04
|
LARGE_PASSENGER_AIRCRAFT = 0x04
|
||||||
|
|
||||||
class AvEquipmentSubcategory(OpenIntEnum):
|
class AvEquipmentSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_AV_EQUIPMENT = 0x00
|
GENERIC_AV_EQUIPMENT = 0x00
|
||||||
AMPLIFIER = 0x01
|
AMPLIFIER = 0x01
|
||||||
RECEIVER = 0x02
|
RECEIVER = 0x02
|
||||||
@@ -1131,65 +1131,65 @@ class Appearance:
|
|||||||
OPTICAL_DISC_PLAYER = 0x09
|
OPTICAL_DISC_PLAYER = 0x09
|
||||||
SET_TOP_BOX = 0x0A
|
SET_TOP_BOX = 0x0A
|
||||||
|
|
||||||
class DisplayEquipmentSubcategory(OpenIntEnum):
|
class DisplayEquipmentSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_DISPLAY_EQUIPMENT = 0x00
|
GENERIC_DISPLAY_EQUIPMENT = 0x00
|
||||||
TELEVISION = 0x01
|
TELEVISION = 0x01
|
||||||
MONITOR = 0x02
|
MONITOR = 0x02
|
||||||
PROJECTOR = 0x03
|
PROJECTOR = 0x03
|
||||||
|
|
||||||
class HearingAidSubcategory(OpenIntEnum):
|
class HearingAidSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_HEARING_AID = 0x00
|
GENERIC_HEARING_AID = 0x00
|
||||||
IN_EAR_HEARING_AID = 0x01
|
IN_EAR_HEARING_AID = 0x01
|
||||||
BEHIND_EAR_HEARING_AID = 0x02
|
BEHIND_EAR_HEARING_AID = 0x02
|
||||||
COCHLEAR_IMPLANT = 0x03
|
COCHLEAR_IMPLANT = 0x03
|
||||||
|
|
||||||
class GamingSubcategory(OpenIntEnum):
|
class GamingSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_GAMING = 0x00
|
GENERIC_GAMING = 0x00
|
||||||
HOME_VIDEO_GAME_CONSOLE = 0x01
|
HOME_VIDEO_GAME_CONSOLE = 0x01
|
||||||
PORTABLE_HANDHELD_CONSOLE = 0x02
|
PORTABLE_HANDHELD_CONSOLE = 0x02
|
||||||
|
|
||||||
class SignageSubcategory(OpenIntEnum):
|
class SignageSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_SIGNAGE = 0x00
|
GENERIC_SIGNAGE = 0x00
|
||||||
DIGITAL_SIGNAGE = 0x01
|
DIGITAL_SIGNAGE = 0x01
|
||||||
ELECTRONIC_LABEL = 0x02
|
ELECTRONIC_LABEL = 0x02
|
||||||
|
|
||||||
class PulseOximeterSubcategory(OpenIntEnum):
|
class PulseOximeterSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_PULSE_OXIMETER = 0x00
|
GENERIC_PULSE_OXIMETER = 0x00
|
||||||
FINGERTIP_PULSE_OXIMETER = 0x01
|
FINGERTIP_PULSE_OXIMETER = 0x01
|
||||||
WRIST_WORN_PULSE_OXIMETER = 0x02
|
WRIST_WORN_PULSE_OXIMETER = 0x02
|
||||||
|
|
||||||
class WeightScaleSubcategory(OpenIntEnum):
|
class WeightScaleSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_WEIGHT_SCALE = 0x00
|
GENERIC_WEIGHT_SCALE = 0x00
|
||||||
|
|
||||||
class PersonalMobilityDeviceSubcategory(OpenIntEnum):
|
class PersonalMobilityDeviceSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_PERSONAL_MOBILITY_DEVICE = 0x00
|
GENERIC_PERSONAL_MOBILITY_DEVICE = 0x00
|
||||||
POWERED_WHEELCHAIR = 0x01
|
POWERED_WHEELCHAIR = 0x01
|
||||||
MOBILITY_SCOOTER = 0x02
|
MOBILITY_SCOOTER = 0x02
|
||||||
|
|
||||||
class ContinuousGlucoseMonitorSubcategory(OpenIntEnum):
|
class ContinuousGlucoseMonitorSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_CONTINUOUS_GLUCOSE_MONITOR = 0x00
|
GENERIC_CONTINUOUS_GLUCOSE_MONITOR = 0x00
|
||||||
|
|
||||||
class InsulinPumpSubcategory(OpenIntEnum):
|
class InsulinPumpSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_INSULIN_PUMP = 0x00
|
GENERIC_INSULIN_PUMP = 0x00
|
||||||
INSULIN_PUMP_DURABLE_PUMP = 0x01
|
INSULIN_PUMP_DURABLE_PUMP = 0x01
|
||||||
INSULIN_PUMP_PATCH_PUMP = 0x02
|
INSULIN_PUMP_PATCH_PUMP = 0x02
|
||||||
INSULIN_PEN = 0x03
|
INSULIN_PEN = 0x03
|
||||||
|
|
||||||
class MedicationDeliverySubcategory(OpenIntEnum):
|
class MedicationDeliverySubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_MEDICATION_DELIVERY = 0x00
|
GENERIC_MEDICATION_DELIVERY = 0x00
|
||||||
|
|
||||||
class SpirometerSubcategory(OpenIntEnum):
|
class SpirometerSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_SPIROMETER = 0x00
|
GENERIC_SPIROMETER = 0x00
|
||||||
HANDHELD_SPIROMETER = 0x01
|
HANDHELD_SPIROMETER = 0x01
|
||||||
|
|
||||||
class OutdoorSportsActivitySubcategory(OpenIntEnum):
|
class OutdoorSportsActivitySubcategory(utils.OpenIntEnum):
|
||||||
GENERIC_OUTDOOR_SPORTS_ACTIVITY = 0x00
|
GENERIC_OUTDOOR_SPORTS_ACTIVITY = 0x00
|
||||||
LOCATION_DISPLAY = 0x01
|
LOCATION_DISPLAY = 0x01
|
||||||
LOCATION_AND_NAVIGATION_DISPLAY = 0x02
|
LOCATION_AND_NAVIGATION_DISPLAY = 0x02
|
||||||
LOCATION_POD = 0x03
|
LOCATION_POD = 0x03
|
||||||
LOCATION_AND_NAVIGATION_POD = 0x04
|
LOCATION_AND_NAVIGATION_POD = 0x04
|
||||||
|
|
||||||
class _OpenSubcategory(OpenIntEnum):
|
class _OpenSubcategory(utils.OpenIntEnum):
|
||||||
GENERIC = 0x00
|
GENERIC = 0x00
|
||||||
|
|
||||||
SUBCATEGORY_CLASSES = {
|
SUBCATEGORY_CLASSES = {
|
||||||
@@ -1296,7 +1296,7 @@ class AdvertisingData:
|
|||||||
# fmt: off
|
# fmt: off
|
||||||
# pylint: disable=line-too-long
|
# pylint: disable=line-too-long
|
||||||
|
|
||||||
class Type(OpenIntEnum):
|
class Type(utils.OpenIntEnum):
|
||||||
FLAGS = 0x01
|
FLAGS = 0x01
|
||||||
INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS = 0x02
|
INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS = 0x02
|
||||||
COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS = 0x03
|
COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS = 0x03
|
||||||
|
|||||||
+85
-82
@@ -49,7 +49,6 @@ from typing import (
|
|||||||
)
|
)
|
||||||
from typing_extensions import Self
|
from typing_extensions import Self
|
||||||
|
|
||||||
from pyee import EventEmitter
|
|
||||||
|
|
||||||
from bumble.colors import color
|
from bumble.colors import color
|
||||||
from bumble.att import ATT_CID, ATT_DEFAULT_MTU, ATT_PDU
|
from bumble.att import ATT_CID, ATT_DEFAULT_MTU, ATT_PDU
|
||||||
@@ -71,15 +70,7 @@ from bumble.core import (
|
|||||||
OutOfResourcesError,
|
OutOfResourcesError,
|
||||||
UnreachableError,
|
UnreachableError,
|
||||||
)
|
)
|
||||||
from bumble.utils import (
|
from bumble import utils
|
||||||
AsyncRunner,
|
|
||||||
CompositeEventEmitter,
|
|
||||||
EventWatcher,
|
|
||||||
setup_event_forwarding,
|
|
||||||
composite_listener,
|
|
||||||
deprecated,
|
|
||||||
experimental,
|
|
||||||
)
|
|
||||||
from bumble.keys import (
|
from bumble.keys import (
|
||||||
KeyStore,
|
KeyStore,
|
||||||
PairingKeys,
|
PairingKeys,
|
||||||
@@ -576,7 +567,7 @@ class PeriodicAdvertisingParameters:
|
|||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@dataclass
|
@dataclass
|
||||||
class AdvertisingSet(EventEmitter):
|
class AdvertisingSet(utils.EventEmitter):
|
||||||
device: Device
|
device: Device
|
||||||
advertising_handle: int
|
advertising_handle: int
|
||||||
auto_restart: bool
|
auto_restart: bool
|
||||||
@@ -810,7 +801,7 @@ class AdvertisingSet(EventEmitter):
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class PeriodicAdvertisingSync(EventEmitter):
|
class PeriodicAdvertisingSync(utils.EventEmitter):
|
||||||
class State(Enum):
|
class State(Enum):
|
||||||
INIT = 0
|
INIT = 0
|
||||||
PENDING = 1
|
PENDING = 1
|
||||||
@@ -939,7 +930,7 @@ class PeriodicAdvertisingSync(EventEmitter):
|
|||||||
"received established event for cancelled sync, will terminate"
|
"received established event for cancelled sync, will terminate"
|
||||||
)
|
)
|
||||||
self.state = self.State.ESTABLISHED
|
self.state = self.State.ESTABLISHED
|
||||||
AsyncRunner.spawn(self.terminate())
|
utils.AsyncRunner.spawn(self.terminate())
|
||||||
return
|
return
|
||||||
|
|
||||||
if status == hci.HCI_SUCCESS:
|
if status == hci.HCI_SUCCESS:
|
||||||
@@ -1025,7 +1016,7 @@ class BigParameters:
|
|||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@dataclass
|
@dataclass
|
||||||
class Big(EventEmitter):
|
class Big(utils.EventEmitter):
|
||||||
class State(IntEnum):
|
class State(IntEnum):
|
||||||
PENDING = 0
|
PENDING = 0
|
||||||
ACTIVE = 1
|
ACTIVE = 1
|
||||||
@@ -1065,7 +1056,7 @@ class Big(EventEmitter):
|
|||||||
logger.error('BIG %d is not active.', self.big_handle)
|
logger.error('BIG %d is not active.', self.big_handle)
|
||||||
return
|
return
|
||||||
|
|
||||||
with closing(EventWatcher()) as watcher:
|
with closing(utils.EventWatcher()) as watcher:
|
||||||
terminated = asyncio.Event()
|
terminated = asyncio.Event()
|
||||||
watcher.once(self, Big.Event.TERMINATION, lambda _: terminated.set())
|
watcher.once(self, Big.Event.TERMINATION, lambda _: terminated.set())
|
||||||
await self.device.send_command(
|
await self.device.send_command(
|
||||||
@@ -1088,7 +1079,7 @@ class BigSyncParameters:
|
|||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@dataclass
|
@dataclass
|
||||||
class BigSync(EventEmitter):
|
class BigSync(utils.EventEmitter):
|
||||||
class State(IntEnum):
|
class State(IntEnum):
|
||||||
PENDING = 0
|
PENDING = 0
|
||||||
ACTIVE = 1
|
ACTIVE = 1
|
||||||
@@ -1123,7 +1114,7 @@ class BigSync(EventEmitter):
|
|||||||
logger.error('BIG Sync %d is not active.', self.big_handle)
|
logger.error('BIG Sync %d is not active.', self.big_handle)
|
||||||
return
|
return
|
||||||
|
|
||||||
with closing(EventWatcher()) as watcher:
|
with closing(utils.EventWatcher()) as watcher:
|
||||||
terminated = asyncio.Event()
|
terminated = asyncio.Event()
|
||||||
watcher.once(self, BigSync.Event.TERMINATION, lambda _: terminated.set())
|
watcher.once(self, BigSync.Event.TERMINATION, lambda _: terminated.set())
|
||||||
await self.device.send_command(
|
await self.device.send_command(
|
||||||
@@ -1392,7 +1383,7 @@ ConnectionParametersPreferences.default = ConnectionParametersPreferences()
|
|||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@dataclass
|
@dataclass
|
||||||
class ScoLink(CompositeEventEmitter):
|
class ScoLink(utils.CompositeEventEmitter):
|
||||||
device: Device
|
device: Device
|
||||||
acl_connection: Connection
|
acl_connection: Connection
|
||||||
handle: int
|
handle: int
|
||||||
@@ -1483,7 +1474,7 @@ class _IsoLink:
|
|||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@dataclass
|
@dataclass
|
||||||
class CisLink(CompositeEventEmitter, _IsoLink):
|
class CisLink(utils.EventEmitter, _IsoLink):
|
||||||
class State(IntEnum):
|
class State(IntEnum):
|
||||||
PENDING = 0
|
PENDING = 0
|
||||||
ESTABLISHED = 1
|
ESTABLISHED = 1
|
||||||
@@ -1560,7 +1551,7 @@ class IsoPacketStream:
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class Connection(CompositeEventEmitter):
|
class Connection(utils.CompositeEventEmitter):
|
||||||
device: Device
|
device: Device
|
||||||
handle: int
|
handle: int
|
||||||
transport: core.PhysicalTransport
|
transport: core.PhysicalTransport
|
||||||
@@ -1580,7 +1571,7 @@ class Connection(CompositeEventEmitter):
|
|||||||
cs_configs: dict[int, ChannelSoundingConfig] # Config ID to Configuration
|
cs_configs: dict[int, ChannelSoundingConfig] # Config ID to Configuration
|
||||||
cs_procedures: dict[int, ChannelSoundingProcedure] # Config ID to Procedures
|
cs_procedures: dict[int, ChannelSoundingProcedure] # Config ID to Procedures
|
||||||
|
|
||||||
@composite_listener
|
@utils.composite_listener
|
||||||
class Listener:
|
class Listener:
|
||||||
def on_disconnection(self, reason):
|
def on_disconnection(self, reason):
|
||||||
pass
|
pass
|
||||||
@@ -1699,7 +1690,7 @@ class Connection(CompositeEventEmitter):
|
|||||||
def send_l2cap_pdu(self, cid: int, pdu: bytes) -> None:
|
def send_l2cap_pdu(self, cid: int, pdu: bytes) -> None:
|
||||||
self.device.send_l2cap_pdu(self.handle, cid, pdu)
|
self.device.send_l2cap_pdu(self.handle, cid, pdu)
|
||||||
|
|
||||||
@deprecated("Please use create_l2cap_channel()")
|
@utils.deprecated("Please use create_l2cap_channel()")
|
||||||
async def open_l2cap_channel(
|
async def open_l2cap_channel(
|
||||||
self,
|
self,
|
||||||
psm,
|
psm,
|
||||||
@@ -1753,7 +1744,9 @@ class Connection(CompositeEventEmitter):
|
|||||||
self.on('disconnection_failure', abort.set_exception)
|
self.on('disconnection_failure', abort.set_exception)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await asyncio.wait_for(self.device.abort_on('flush', abort), timeout)
|
await asyncio.wait_for(
|
||||||
|
utils.cancel_on_event(self.device, 'flush', abort), timeout
|
||||||
|
)
|
||||||
finally:
|
finally:
|
||||||
self.remove_listener('disconnection', abort.set_result)
|
self.remove_listener('disconnection', abort.set_result)
|
||||||
self.remove_listener('disconnection_failure', abort.set_exception)
|
self.remove_listener('disconnection_failure', abort.set_exception)
|
||||||
@@ -2037,7 +2030,7 @@ device_host_event_handlers: list[str] = []
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class Device(CompositeEventEmitter):
|
class Device(utils.CompositeEventEmitter):
|
||||||
# Incomplete list of fields.
|
# Incomplete list of fields.
|
||||||
random_address: hci.Address # Random private address that may change periodically
|
random_address: hci.Address # Random private address that may change periodically
|
||||||
public_address: (
|
public_address: (
|
||||||
@@ -2069,7 +2062,7 @@ class Device(CompositeEventEmitter):
|
|||||||
_pending_cis: Dict[int, tuple[int, int]]
|
_pending_cis: Dict[int, tuple[int, int]]
|
||||||
gatt_service: gatt_service.GenericAttributeProfileService | None = None
|
gatt_service: gatt_service.GenericAttributeProfileService | None = None
|
||||||
|
|
||||||
@composite_listener
|
@utils.composite_listener
|
||||||
class Listener:
|
class Listener:
|
||||||
def on_advertisement(self, advertisement):
|
def on_advertisement(self, advertisement):
|
||||||
pass
|
pass
|
||||||
@@ -2290,7 +2283,9 @@ class Device(CompositeEventEmitter):
|
|||||||
self.l2cap_channel_manager.register_fixed_channel(ATT_CID, self.on_gatt_pdu)
|
self.l2cap_channel_manager.register_fixed_channel(ATT_CID, self.on_gatt_pdu)
|
||||||
|
|
||||||
# Forward some events
|
# Forward some events
|
||||||
setup_event_forwarding(self.gatt_server, self, 'characteristic_subscription')
|
utils.setup_event_forwarding(
|
||||||
|
self.gatt_server, self, 'characteristic_subscription'
|
||||||
|
)
|
||||||
|
|
||||||
# Set the initial host
|
# Set the initial host
|
||||||
if host:
|
if host:
|
||||||
@@ -2379,11 +2374,11 @@ class Device(CompositeEventEmitter):
|
|||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
|
||||||
@deprecated("Please use create_l2cap_server()")
|
@utils.deprecated("Please use create_l2cap_server()")
|
||||||
def register_l2cap_server(self, psm, server) -> int:
|
def register_l2cap_server(self, psm, server) -> int:
|
||||||
return self.l2cap_channel_manager.register_server(psm, server)
|
return self.l2cap_channel_manager.register_server(psm, server)
|
||||||
|
|
||||||
@deprecated("Please use create_l2cap_server()")
|
@utils.deprecated("Please use create_l2cap_server()")
|
||||||
def register_l2cap_channel_server(
|
def register_l2cap_channel_server(
|
||||||
self,
|
self,
|
||||||
psm,
|
psm,
|
||||||
@@ -2396,7 +2391,7 @@ class Device(CompositeEventEmitter):
|
|||||||
psm, server, max_credits, mtu, mps
|
psm, server, max_credits, mtu, mps
|
||||||
)
|
)
|
||||||
|
|
||||||
@deprecated("Please use create_l2cap_channel()")
|
@utils.deprecated("Please use create_l2cap_channel()")
|
||||||
async def open_l2cap_channel(
|
async def open_l2cap_channel(
|
||||||
self,
|
self,
|
||||||
connection,
|
connection,
|
||||||
@@ -3237,7 +3232,7 @@ class Device(CompositeEventEmitter):
|
|||||||
advertiser_clock_accuracy,
|
advertiser_clock_accuracy,
|
||||||
)
|
)
|
||||||
|
|
||||||
AsyncRunner.spawn(self._update_periodic_advertising_syncs())
|
utils.AsyncRunner.spawn(self._update_periodic_advertising_syncs())
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -3667,7 +3662,7 @@ class Device(CompositeEventEmitter):
|
|||||||
self.le_connecting = True
|
self.le_connecting = True
|
||||||
|
|
||||||
if timeout is None:
|
if timeout is None:
|
||||||
return await self.abort_on('flush', pending_connection)
|
return await utils.cancel_on_event(self, 'flush', pending_connection)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return await asyncio.wait_for(
|
return await asyncio.wait_for(
|
||||||
@@ -3684,7 +3679,9 @@ class Device(CompositeEventEmitter):
|
|||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return await self.abort_on('flush', pending_connection)
|
return await utils.cancel_on_event(
|
||||||
|
self, 'flush', pending_connection
|
||||||
|
)
|
||||||
except core.ConnectionError as error:
|
except core.ConnectionError as error:
|
||||||
raise core.TimeoutError() from error
|
raise core.TimeoutError() from error
|
||||||
finally:
|
finally:
|
||||||
@@ -3740,7 +3737,7 @@ class Device(CompositeEventEmitter):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# Wait for a request or a completed connection
|
# Wait for a request or a completed connection
|
||||||
pending_request = self.abort_on('flush', pending_request_fut)
|
pending_request = utils.cancel_on_event(self, 'flush', pending_request_fut)
|
||||||
result = await (
|
result = await (
|
||||||
asyncio.wait_for(pending_request, timeout)
|
asyncio.wait_for(pending_request, timeout)
|
||||||
if timeout
|
if timeout
|
||||||
@@ -3802,7 +3799,7 @@ class Device(CompositeEventEmitter):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Wait for connection complete
|
# Wait for connection complete
|
||||||
return await self.abort_on('flush', pending_connection)
|
return await utils.cancel_on_event(self, 'flush', pending_connection)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
self.remove_listener('connection', on_connection)
|
self.remove_listener('connection', on_connection)
|
||||||
@@ -3876,7 +3873,7 @@ class Device(CompositeEventEmitter):
|
|||||||
|
|
||||||
# Wait for the disconnection process to complete
|
# Wait for the disconnection process to complete
|
||||||
self.disconnecting = True
|
self.disconnecting = True
|
||||||
return await self.abort_on('flush', pending_disconnection)
|
return await utils.cancel_on_event(self, 'flush', pending_disconnection)
|
||||||
finally:
|
finally:
|
||||||
connection.remove_listener(
|
connection.remove_listener(
|
||||||
'disconnection', pending_disconnection.set_result
|
'disconnection', pending_disconnection.set_result
|
||||||
@@ -4075,7 +4072,7 @@ class Device(CompositeEventEmitter):
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return await self.abort_on('flush', peer_address)
|
return await utils.cancel_on_event(self, 'flush', peer_address)
|
||||||
finally:
|
finally:
|
||||||
if listener is not None:
|
if listener is not None:
|
||||||
self.remove_listener(event_name, listener)
|
self.remove_listener(event_name, listener)
|
||||||
@@ -4125,7 +4122,7 @@ class Device(CompositeEventEmitter):
|
|||||||
if not self.scanning:
|
if not self.scanning:
|
||||||
await self.start_scanning(filter_duplicates=True)
|
await self.start_scanning(filter_duplicates=True)
|
||||||
|
|
||||||
return await self.abort_on('flush', peer_address)
|
return await utils.cancel_on_event(self, 'flush', peer_address)
|
||||||
finally:
|
finally:
|
||||||
if listener is not None:
|
if listener is not None:
|
||||||
self.remove_listener(event_name, listener)
|
self.remove_listener(event_name, listener)
|
||||||
@@ -4229,7 +4226,9 @@ class Device(CompositeEventEmitter):
|
|||||||
raise hci.HCI_StatusError(result)
|
raise hci.HCI_StatusError(result)
|
||||||
|
|
||||||
# Wait for the authentication to complete
|
# Wait for the authentication to complete
|
||||||
await connection.abort_on('disconnection', pending_authentication)
|
await utils.cancel_on_event(
|
||||||
|
connection, 'disconnection', pending_authentication
|
||||||
|
)
|
||||||
finally:
|
finally:
|
||||||
connection.remove_listener('connection_authentication', on_authentication)
|
connection.remove_listener('connection_authentication', on_authentication)
|
||||||
connection.remove_listener(
|
connection.remove_listener(
|
||||||
@@ -4309,7 +4308,7 @@ class Device(CompositeEventEmitter):
|
|||||||
raise hci.HCI_StatusError(result)
|
raise hci.HCI_StatusError(result)
|
||||||
|
|
||||||
# Wait for the result
|
# Wait for the result
|
||||||
await connection.abort_on('disconnection', pending_encryption)
|
await utils.cancel_on_event(connection, 'disconnection', pending_encryption)
|
||||||
finally:
|
finally:
|
||||||
connection.remove_listener(
|
connection.remove_listener(
|
||||||
'connection_encryption_change', on_encryption_change
|
'connection_encryption_change', on_encryption_change
|
||||||
@@ -4353,7 +4352,9 @@ class Device(CompositeEventEmitter):
|
|||||||
f'{hci.HCI_Constant.error_name(result.status)}'
|
f'{hci.HCI_Constant.error_name(result.status)}'
|
||||||
)
|
)
|
||||||
raise hci.HCI_StatusError(result)
|
raise hci.HCI_StatusError(result)
|
||||||
await connection.abort_on('disconnection', pending_role_change)
|
await utils.cancel_on_event(
|
||||||
|
connection, 'disconnection', pending_role_change
|
||||||
|
)
|
||||||
finally:
|
finally:
|
||||||
connection.remove_listener('role_change', on_role_change)
|
connection.remove_listener('role_change', on_role_change)
|
||||||
connection.remove_listener('role_change_failure', on_role_change_failure)
|
connection.remove_listener('role_change_failure', on_role_change_failure)
|
||||||
@@ -4402,13 +4403,13 @@ class Device(CompositeEventEmitter):
|
|||||||
raise hci.HCI_StatusError(result)
|
raise hci.HCI_StatusError(result)
|
||||||
|
|
||||||
# Wait for the result
|
# Wait for the result
|
||||||
return await self.abort_on('flush', pending_name)
|
return await utils.cancel_on_event(self, 'flush', pending_name)
|
||||||
finally:
|
finally:
|
||||||
self.remove_listener('remote_name', handler)
|
self.remove_listener('remote_name', handler)
|
||||||
self.remove_listener('remote_name_failure', failure_handler)
|
self.remove_listener('remote_name_failure', failure_handler)
|
||||||
|
|
||||||
# [LE only]
|
# [LE only]
|
||||||
@experimental('Only for testing.')
|
@utils.experimental('Only for testing.')
|
||||||
async def setup_cig(
|
async def setup_cig(
|
||||||
self,
|
self,
|
||||||
cig_id: int,
|
cig_id: int,
|
||||||
@@ -4466,7 +4467,7 @@ class Device(CompositeEventEmitter):
|
|||||||
return cis_handles
|
return cis_handles
|
||||||
|
|
||||||
# [LE only]
|
# [LE only]
|
||||||
@experimental('Only for testing.')
|
@utils.experimental('Only for testing.')
|
||||||
async def create_cis(
|
async def create_cis(
|
||||||
self, cis_acl_pairs: Sequence[tuple[int, int]]
|
self, cis_acl_pairs: Sequence[tuple[int, int]]
|
||||||
) -> list[CisLink]:
|
) -> list[CisLink]:
|
||||||
@@ -4482,7 +4483,7 @@ class Device(CompositeEventEmitter):
|
|||||||
cig_id=cig_id,
|
cig_id=cig_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
with closing(EventWatcher()) as watcher:
|
with closing(utils.EventWatcher()) as watcher:
|
||||||
pending_cis_establishments = {
|
pending_cis_establishments = {
|
||||||
cis_handle: asyncio.get_running_loop().create_future()
|
cis_handle: asyncio.get_running_loop().create_future()
|
||||||
for cis_handle, _ in cis_acl_pairs
|
for cis_handle, _ in cis_acl_pairs
|
||||||
@@ -4509,7 +4510,7 @@ class Device(CompositeEventEmitter):
|
|||||||
return await asyncio.gather(*pending_cis_establishments.values())
|
return await asyncio.gather(*pending_cis_establishments.values())
|
||||||
|
|
||||||
# [LE only]
|
# [LE only]
|
||||||
@experimental('Only for testing.')
|
@utils.experimental('Only for testing.')
|
||||||
async def accept_cis_request(self, handle: int) -> CisLink:
|
async def accept_cis_request(self, handle: int) -> CisLink:
|
||||||
"""[LE Only] Accepts an incoming CIS request.
|
"""[LE Only] Accepts an incoming CIS request.
|
||||||
|
|
||||||
@@ -4531,7 +4532,7 @@ class Device(CompositeEventEmitter):
|
|||||||
if cis_link.state == CisLink.State.ESTABLISHED:
|
if cis_link.state == CisLink.State.ESTABLISHED:
|
||||||
return cis_link
|
return cis_link
|
||||||
|
|
||||||
with closing(EventWatcher()) as watcher:
|
with closing(utils.EventWatcher()) as watcher:
|
||||||
pending_establishment = asyncio.get_running_loop().create_future()
|
pending_establishment = asyncio.get_running_loop().create_future()
|
||||||
|
|
||||||
def on_establishment() -> None:
|
def on_establishment() -> None:
|
||||||
@@ -4555,7 +4556,7 @@ class Device(CompositeEventEmitter):
|
|||||||
raise UnreachableError()
|
raise UnreachableError()
|
||||||
|
|
||||||
# [LE only]
|
# [LE only]
|
||||||
@experimental('Only for testing.')
|
@utils.experimental('Only for testing.')
|
||||||
async def reject_cis_request(
|
async def reject_cis_request(
|
||||||
self,
|
self,
|
||||||
handle: int,
|
handle: int,
|
||||||
@@ -4569,14 +4570,14 @@ class Device(CompositeEventEmitter):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# [LE only]
|
# [LE only]
|
||||||
@experimental('Only for testing.')
|
@utils.experimental('Only for testing.')
|
||||||
async def create_big(
|
async def create_big(
|
||||||
self, advertising_set: AdvertisingSet, parameters: BigParameters
|
self, advertising_set: AdvertisingSet, parameters: BigParameters
|
||||||
) -> Big:
|
) -> Big:
|
||||||
if (big_handle := self.next_big_handle()) is None:
|
if (big_handle := self.next_big_handle()) is None:
|
||||||
raise core.OutOfResourcesError("All valid BIG handles already in use")
|
raise core.OutOfResourcesError("All valid BIG handles already in use")
|
||||||
|
|
||||||
with closing(EventWatcher()) as watcher:
|
with closing(utils.EventWatcher()) as watcher:
|
||||||
big = Big(
|
big = Big(
|
||||||
big_handle=big_handle,
|
big_handle=big_handle,
|
||||||
parameters=parameters,
|
parameters=parameters,
|
||||||
@@ -4619,7 +4620,7 @@ class Device(CompositeEventEmitter):
|
|||||||
return big
|
return big
|
||||||
|
|
||||||
# [LE only]
|
# [LE only]
|
||||||
@experimental('Only for testing.')
|
@utils.experimental('Only for testing.')
|
||||||
async def create_big_sync(
|
async def create_big_sync(
|
||||||
self, pa_sync: PeriodicAdvertisingSync, parameters: BigSyncParameters
|
self, pa_sync: PeriodicAdvertisingSync, parameters: BigSyncParameters
|
||||||
) -> BigSync:
|
) -> BigSync:
|
||||||
@@ -4629,7 +4630,7 @@ class Device(CompositeEventEmitter):
|
|||||||
if (pa_sync_handle := pa_sync.sync_handle) is None:
|
if (pa_sync_handle := pa_sync.sync_handle) is None:
|
||||||
raise core.InvalidStateError("PA Sync is not established")
|
raise core.InvalidStateError("PA Sync is not established")
|
||||||
|
|
||||||
with closing(EventWatcher()) as watcher:
|
with closing(utils.EventWatcher()) as watcher:
|
||||||
big_sync = BigSync(
|
big_sync = BigSync(
|
||||||
big_handle=big_handle,
|
big_handle=big_handle,
|
||||||
parameters=parameters,
|
parameters=parameters,
|
||||||
@@ -4677,7 +4678,7 @@ class Device(CompositeEventEmitter):
|
|||||||
Returns:
|
Returns:
|
||||||
LE features supported by the remote device.
|
LE features supported by the remote device.
|
||||||
"""
|
"""
|
||||||
with closing(EventWatcher()) as watcher:
|
with closing(utils.EventWatcher()) as watcher:
|
||||||
read_feature_future: asyncio.Future[hci.LeFeatureMask] = (
|
read_feature_future: asyncio.Future[hci.LeFeatureMask] = (
|
||||||
asyncio.get_running_loop().create_future()
|
asyncio.get_running_loop().create_future()
|
||||||
)
|
)
|
||||||
@@ -4700,7 +4701,7 @@ class Device(CompositeEventEmitter):
|
|||||||
)
|
)
|
||||||
return await read_feature_future
|
return await read_feature_future
|
||||||
|
|
||||||
@experimental('Only for testing.')
|
@utils.experimental('Only for testing.')
|
||||||
async def get_remote_cs_capabilities(
|
async def get_remote_cs_capabilities(
|
||||||
self, connection: Connection
|
self, connection: Connection
|
||||||
) -> ChannelSoundingCapabilities:
|
) -> ChannelSoundingCapabilities:
|
||||||
@@ -4708,7 +4709,7 @@ class Device(CompositeEventEmitter):
|
|||||||
asyncio.get_running_loop().create_future()
|
asyncio.get_running_loop().create_future()
|
||||||
)
|
)
|
||||||
|
|
||||||
with closing(EventWatcher()) as watcher:
|
with closing(utils.EventWatcher()) as watcher:
|
||||||
watcher.once(
|
watcher.once(
|
||||||
connection, 'channel_sounding_capabilities', complete_future.set_result
|
connection, 'channel_sounding_capabilities', complete_future.set_result
|
||||||
)
|
)
|
||||||
@@ -4725,7 +4726,7 @@ class Device(CompositeEventEmitter):
|
|||||||
)
|
)
|
||||||
return await complete_future
|
return await complete_future
|
||||||
|
|
||||||
@experimental('Only for testing.')
|
@utils.experimental('Only for testing.')
|
||||||
async def set_default_cs_settings(
|
async def set_default_cs_settings(
|
||||||
self,
|
self,
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
@@ -4745,7 +4746,7 @@ class Device(CompositeEventEmitter):
|
|||||||
check_result=True,
|
check_result=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@experimental('Only for testing.')
|
@utils.experimental('Only for testing.')
|
||||||
async def create_cs_config(
|
async def create_cs_config(
|
||||||
self,
|
self,
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
@@ -4782,7 +4783,7 @@ class Device(CompositeEventEmitter):
|
|||||||
if config_id is None:
|
if config_id is None:
|
||||||
raise OutOfResourcesError("No available config ID on this connection!")
|
raise OutOfResourcesError("No available config ID on this connection!")
|
||||||
|
|
||||||
with closing(EventWatcher()) as watcher:
|
with closing(utils.EventWatcher()) as watcher:
|
||||||
watcher.once(
|
watcher.once(
|
||||||
connection, 'channel_sounding_config', complete_future.set_result
|
connection, 'channel_sounding_config', complete_future.set_result
|
||||||
)
|
)
|
||||||
@@ -4816,12 +4817,12 @@ class Device(CompositeEventEmitter):
|
|||||||
)
|
)
|
||||||
return await complete_future
|
return await complete_future
|
||||||
|
|
||||||
@experimental('Only for testing.')
|
@utils.experimental('Only for testing.')
|
||||||
async def enable_cs_security(self, connection: Connection) -> None:
|
async def enable_cs_security(self, connection: Connection) -> None:
|
||||||
complete_future: asyncio.Future[None] = (
|
complete_future: asyncio.Future[None] = (
|
||||||
asyncio.get_running_loop().create_future()
|
asyncio.get_running_loop().create_future()
|
||||||
)
|
)
|
||||||
with closing(EventWatcher()) as watcher:
|
with closing(utils.EventWatcher()) as watcher:
|
||||||
|
|
||||||
def on_event(event: hci.HCI_LE_CS_Security_Enable_Complete_Event) -> None:
|
def on_event(event: hci.HCI_LE_CS_Security_Enable_Complete_Event) -> None:
|
||||||
if event.connection_handle != connection.handle:
|
if event.connection_handle != connection.handle:
|
||||||
@@ -4840,7 +4841,7 @@ class Device(CompositeEventEmitter):
|
|||||||
)
|
)
|
||||||
return await complete_future
|
return await complete_future
|
||||||
|
|
||||||
@experimental('Only for testing.')
|
@utils.experimental('Only for testing.')
|
||||||
async def set_cs_procedure_parameters(
|
async def set_cs_procedure_parameters(
|
||||||
self,
|
self,
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
@@ -4878,7 +4879,7 @@ class Device(CompositeEventEmitter):
|
|||||||
check_result=True,
|
check_result=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@experimental('Only for testing.')
|
@utils.experimental('Only for testing.')
|
||||||
async def enable_cs_procedure(
|
async def enable_cs_procedure(
|
||||||
self,
|
self,
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
@@ -4888,7 +4889,7 @@ class Device(CompositeEventEmitter):
|
|||||||
complete_future: asyncio.Future[ChannelSoundingProcedure] = (
|
complete_future: asyncio.Future[ChannelSoundingProcedure] = (
|
||||||
asyncio.get_running_loop().create_future()
|
asyncio.get_running_loop().create_future()
|
||||||
)
|
)
|
||||||
with closing(EventWatcher()) as watcher:
|
with closing(utils.EventWatcher()) as watcher:
|
||||||
watcher.once(
|
watcher.once(
|
||||||
connection, 'channel_sounding_procedure', complete_future.set_result
|
connection, 'channel_sounding_procedure', complete_future.set_result
|
||||||
)
|
)
|
||||||
@@ -4928,7 +4929,9 @@ class Device(CompositeEventEmitter):
|
|||||||
value=link_key, authenticated=authenticated
|
value=link_key, authenticated=authenticated
|
||||||
)
|
)
|
||||||
|
|
||||||
self.abort_on('flush', self.update_keys(str(bd_addr), pairing_keys))
|
utils.cancel_on_event(
|
||||||
|
self, 'flush', self.update_keys(str(bd_addr), pairing_keys)
|
||||||
|
)
|
||||||
|
|
||||||
if connection := self.find_connection_by_bd_addr(
|
if connection := self.find_connection_by_bd_addr(
|
||||||
bd_addr, transport=PhysicalTransport.BR_EDR
|
bd_addr, transport=PhysicalTransport.BR_EDR
|
||||||
@@ -5197,7 +5200,7 @@ class Device(CompositeEventEmitter):
|
|||||||
if advertising_set.auto_restart:
|
if advertising_set.auto_restart:
|
||||||
connection.once(
|
connection.once(
|
||||||
'disconnection',
|
'disconnection',
|
||||||
lambda _: self.abort_on('flush', advertising_set.start()),
|
lambda _: utils.cancel_on_event(self, 'flush', advertising_set.start()),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.emit('connection', connection)
|
self.emit('connection', connection)
|
||||||
@@ -5306,7 +5309,7 @@ class Device(CompositeEventEmitter):
|
|||||||
advertiser = self.legacy_advertiser
|
advertiser = self.legacy_advertiser
|
||||||
connection.once(
|
connection.once(
|
||||||
'disconnection',
|
'disconnection',
|
||||||
lambda _: self.abort_on('flush', advertiser.start()),
|
lambda _: utils.cancel_on_event(self, 'flush', advertiser.start()),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.legacy_advertiser = None
|
self.legacy_advertiser = None
|
||||||
@@ -5433,7 +5436,7 @@ class Device(CompositeEventEmitter):
|
|||||||
connection.emit('disconnection_failure', error)
|
connection.emit('disconnection_failure', error)
|
||||||
|
|
||||||
@host_event_handler
|
@host_event_handler
|
||||||
@AsyncRunner.run_in_task()
|
@utils.AsyncRunner.run_in_task()
|
||||||
async def on_inquiry_complete(self):
|
async def on_inquiry_complete(self):
|
||||||
if self.auto_restart_inquiry:
|
if self.auto_restart_inquiry:
|
||||||
# Inquire again
|
# Inquire again
|
||||||
@@ -5565,7 +5568,7 @@ class Device(CompositeEventEmitter):
|
|||||||
|
|
||||||
async def reply() -> None:
|
async def reply() -> None:
|
||||||
try:
|
try:
|
||||||
if await connection.abort_on('disconnection', method()):
|
if await utils.cancel_on_event(connection, 'disconnection', method()):
|
||||||
await self.host.send_command(
|
await self.host.send_command(
|
||||||
hci.HCI_User_Confirmation_Request_Reply_Command(
|
hci.HCI_User_Confirmation_Request_Reply_Command(
|
||||||
bd_addr=connection.peer_address
|
bd_addr=connection.peer_address
|
||||||
@@ -5581,7 +5584,7 @@ class Device(CompositeEventEmitter):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
AsyncRunner.spawn(reply())
|
utils.AsyncRunner.spawn(reply())
|
||||||
|
|
||||||
# [Classic only]
|
# [Classic only]
|
||||||
@host_event_handler
|
@host_event_handler
|
||||||
@@ -5592,8 +5595,8 @@ class Device(CompositeEventEmitter):
|
|||||||
|
|
||||||
async def reply() -> None:
|
async def reply() -> None:
|
||||||
try:
|
try:
|
||||||
number = await connection.abort_on(
|
number = await utils.cancel_on_event(
|
||||||
'disconnection', pairing_config.delegate.get_number()
|
connection, 'disconnection', pairing_config.delegate.get_number()
|
||||||
)
|
)
|
||||||
if number is not None:
|
if number is not None:
|
||||||
await self.host.send_command(
|
await self.host.send_command(
|
||||||
@@ -5611,7 +5614,7 @@ class Device(CompositeEventEmitter):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
AsyncRunner.spawn(reply())
|
utils.AsyncRunner.spawn(reply())
|
||||||
|
|
||||||
# [Classic only]
|
# [Classic only]
|
||||||
@host_event_handler
|
@host_event_handler
|
||||||
@@ -5626,8 +5629,8 @@ class Device(CompositeEventEmitter):
|
|||||||
if io_capability == hci.HCI_KEYBOARD_ONLY_IO_CAPABILITY:
|
if io_capability == hci.HCI_KEYBOARD_ONLY_IO_CAPABILITY:
|
||||||
# Ask the user to enter a string
|
# Ask the user to enter a string
|
||||||
async def get_pin_code():
|
async def get_pin_code():
|
||||||
pin_code = await connection.abort_on(
|
pin_code = await utils.cancel_on_event(
|
||||||
'disconnection', pairing_config.delegate.get_string(16)
|
connection, 'disconnection', pairing_config.delegate.get_string(16)
|
||||||
)
|
)
|
||||||
|
|
||||||
if pin_code is not None:
|
if pin_code is not None:
|
||||||
@@ -5665,8 +5668,8 @@ class Device(CompositeEventEmitter):
|
|||||||
pairing_config = self.pairing_config_factory(connection)
|
pairing_config = self.pairing_config_factory(connection)
|
||||||
|
|
||||||
# Show the passkey to the user
|
# Show the passkey to the user
|
||||||
connection.abort_on(
|
utils.cancel_on_event(
|
||||||
'disconnection', pairing_config.delegate.display_number(passkey)
|
connection, 'disconnection', pairing_config.delegate.display_number(passkey)
|
||||||
)
|
)
|
||||||
|
|
||||||
# [Classic only]
|
# [Classic only]
|
||||||
@@ -5698,7 +5701,7 @@ class Device(CompositeEventEmitter):
|
|||||||
# [Classic only]
|
# [Classic only]
|
||||||
@host_event_handler
|
@host_event_handler
|
||||||
@with_connection_from_address
|
@with_connection_from_address
|
||||||
@experimental('Only for testing.')
|
@utils.experimental('Only for testing.')
|
||||||
def on_sco_connection(
|
def on_sco_connection(
|
||||||
self, acl_connection: Connection, sco_handle: int, link_type: int
|
self, acl_connection: Connection, sco_handle: int, link_type: int
|
||||||
) -> None:
|
) -> None:
|
||||||
@@ -5718,7 +5721,7 @@ class Device(CompositeEventEmitter):
|
|||||||
# [Classic only]
|
# [Classic only]
|
||||||
@host_event_handler
|
@host_event_handler
|
||||||
@with_connection_from_address
|
@with_connection_from_address
|
||||||
@experimental('Only for testing.')
|
@utils.experimental('Only for testing.')
|
||||||
def on_sco_connection_failure(
|
def on_sco_connection_failure(
|
||||||
self, acl_connection: Connection, status: int
|
self, acl_connection: Connection, status: int
|
||||||
) -> None:
|
) -> None:
|
||||||
@@ -5727,7 +5730,7 @@ class Device(CompositeEventEmitter):
|
|||||||
|
|
||||||
# [Classic only]
|
# [Classic only]
|
||||||
@host_event_handler
|
@host_event_handler
|
||||||
@experimental('Only for testing')
|
@utils.experimental('Only for testing')
|
||||||
def on_sco_packet(
|
def on_sco_packet(
|
||||||
self, sco_handle: int, packet: hci.HCI_SynchronousDataPacket
|
self, sco_handle: int, packet: hci.HCI_SynchronousDataPacket
|
||||||
) -> None:
|
) -> None:
|
||||||
@@ -5737,7 +5740,7 @@ class Device(CompositeEventEmitter):
|
|||||||
# [LE only]
|
# [LE only]
|
||||||
@host_event_handler
|
@host_event_handler
|
||||||
@with_connection_from_handle
|
@with_connection_from_handle
|
||||||
@experimental('Only for testing')
|
@utils.experimental('Only for testing')
|
||||||
def on_cis_request(
|
def on_cis_request(
|
||||||
self,
|
self,
|
||||||
acl_connection: Connection,
|
acl_connection: Connection,
|
||||||
@@ -5764,7 +5767,7 @@ class Device(CompositeEventEmitter):
|
|||||||
|
|
||||||
# [LE only]
|
# [LE only]
|
||||||
@host_event_handler
|
@host_event_handler
|
||||||
@experimental('Only for testing')
|
@utils.experimental('Only for testing')
|
||||||
def on_cis_establishment(self, cis_handle: int) -> None:
|
def on_cis_establishment(self, cis_handle: int) -> None:
|
||||||
cis_link = self.cis_links[cis_handle]
|
cis_link = self.cis_links[cis_handle]
|
||||||
cis_link.state = CisLink.State.ESTABLISHED
|
cis_link.state = CisLink.State.ESTABLISHED
|
||||||
@@ -5784,7 +5787,7 @@ class Device(CompositeEventEmitter):
|
|||||||
|
|
||||||
# [LE only]
|
# [LE only]
|
||||||
@host_event_handler
|
@host_event_handler
|
||||||
@experimental('Only for testing')
|
@utils.experimental('Only for testing')
|
||||||
def on_cis_establishment_failure(self, cis_handle: int, status: int) -> None:
|
def on_cis_establishment_failure(self, cis_handle: int, status: int) -> None:
|
||||||
logger.debug(f'*** CIS Establishment Failure: cis=[0x{cis_handle:04X}] ***')
|
logger.debug(f'*** CIS Establishment Failure: cis=[0x{cis_handle:04X}] ***')
|
||||||
if cis_link := self.cis_links.pop(cis_handle):
|
if cis_link := self.cis_links.pop(cis_handle):
|
||||||
@@ -5793,7 +5796,7 @@ class Device(CompositeEventEmitter):
|
|||||||
|
|
||||||
# [LE only]
|
# [LE only]
|
||||||
@host_event_handler
|
@host_event_handler
|
||||||
@experimental('Only for testing')
|
@utils.experimental('Only for testing')
|
||||||
def on_iso_packet(self, handle: int, packet: hci.HCI_IsoDataPacket) -> None:
|
def on_iso_packet(self, handle: int, packet: hci.HCI_IsoDataPacket) -> None:
|
||||||
if (cis_link := self.cis_links.get(handle)) and cis_link.sink:
|
if (cis_link := self.cis_links.get(handle)) and cis_link.sink:
|
||||||
cis_link.sink(packet)
|
cis_link.sink(packet)
|
||||||
|
|||||||
@@ -35,15 +35,15 @@ from typing import (
|
|||||||
from bumble.core import InvalidOperationError
|
from bumble.core import InvalidOperationError
|
||||||
from bumble.gatt import Characteristic
|
from bumble.gatt import Characteristic
|
||||||
from bumble.gatt_client import CharacteristicProxy
|
from bumble.gatt_client import CharacteristicProxy
|
||||||
from bumble.utils import ByteSerializable, IntConvertible
|
from bumble import utils
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Typing
|
# Typing
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
_T = TypeVar('_T')
|
_T = TypeVar('_T')
|
||||||
_T2 = TypeVar('_T2', bound=ByteSerializable)
|
_T2 = TypeVar('_T2', bound=utils.ByteSerializable)
|
||||||
_T3 = TypeVar('_T3', bound=IntConvertible)
|
_T3 = TypeVar('_T3', bound=utils.IntConvertible)
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ from typing import (
|
|||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
)
|
)
|
||||||
|
|
||||||
from pyee import EventEmitter
|
|
||||||
|
|
||||||
from bumble.colors import color
|
from bumble.colors import color
|
||||||
from bumble.hci import HCI_Constant
|
from bumble.hci import HCI_Constant
|
||||||
@@ -69,6 +68,7 @@ from bumble.att import (
|
|||||||
ATT_Write_Request,
|
ATT_Write_Request,
|
||||||
ATT_Error,
|
ATT_Error,
|
||||||
)
|
)
|
||||||
|
from bumble import utils
|
||||||
from bumble import core
|
from bumble import core
|
||||||
from bumble.core import UUID, InvalidStateError
|
from bumble.core import UUID, InvalidStateError
|
||||||
from bumble.gatt import (
|
from bumble.gatt import (
|
||||||
@@ -117,11 +117,11 @@ def show_services(services: Iterable[ServiceProxy]) -> None:
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Proxies
|
# Proxies
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class AttributeProxy(EventEmitter, Generic[_T]):
|
class AttributeProxy(utils.EventEmitter, Generic[_T]):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, client: Client, handle: int, end_group_handle: int, attribute_type: UUID
|
self, client: Client, handle: int, end_group_handle: int, attribute_type: UUID
|
||||||
) -> None:
|
) -> None:
|
||||||
EventEmitter.__init__(self)
|
utils.EventEmitter.__init__(self)
|
||||||
self.client = client
|
self.client = client
|
||||||
self.handle = handle
|
self.handle = handle
|
||||||
self.end_group_handle = end_group_handle
|
self.end_group_handle = end_group_handle
|
||||||
|
|||||||
+9
-10
@@ -38,7 +38,6 @@ from typing import (
|
|||||||
Type,
|
Type,
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
)
|
)
|
||||||
from pyee import EventEmitter
|
|
||||||
|
|
||||||
from bumble.colors import color
|
from bumble.colors import color
|
||||||
from bumble.core import UUID
|
from bumble.core import UUID
|
||||||
@@ -83,7 +82,7 @@ from bumble.gatt import (
|
|||||||
Descriptor,
|
Descriptor,
|
||||||
Service,
|
Service,
|
||||||
)
|
)
|
||||||
from bumble.utils import AsyncRunner
|
from bumble import utils
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from bumble.device import Device, Connection
|
from bumble.device import Device, Connection
|
||||||
@@ -103,7 +102,7 @@ GATT_SERVER_DEFAULT_MAX_MTU = 517
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# GATT Server
|
# GATT Server
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class Server(EventEmitter):
|
class Server(utils.EventEmitter):
|
||||||
attributes: List[Attribute]
|
attributes: List[Attribute]
|
||||||
services: List[Service]
|
services: List[Service]
|
||||||
attributes_by_handle: Dict[int, Attribute]
|
attributes_by_handle: Dict[int, Attribute]
|
||||||
@@ -662,7 +661,7 @@ class Server(EventEmitter):
|
|||||||
|
|
||||||
self.send_response(connection, response)
|
self.send_response(connection, response)
|
||||||
|
|
||||||
@AsyncRunner.run_in_task()
|
@utils.AsyncRunner.run_in_task()
|
||||||
async def on_att_find_by_type_value_request(self, connection, request):
|
async def on_att_find_by_type_value_request(self, connection, request):
|
||||||
'''
|
'''
|
||||||
See Bluetooth spec Vol 3, Part F - 3.4.3.3 Find By Type Value Request
|
See Bluetooth spec Vol 3, Part F - 3.4.3.3 Find By Type Value Request
|
||||||
@@ -715,7 +714,7 @@ class Server(EventEmitter):
|
|||||||
|
|
||||||
self.send_response(connection, response)
|
self.send_response(connection, response)
|
||||||
|
|
||||||
@AsyncRunner.run_in_task()
|
@utils.AsyncRunner.run_in_task()
|
||||||
async def on_att_read_by_type_request(self, connection, request):
|
async def on_att_read_by_type_request(self, connection, request):
|
||||||
'''
|
'''
|
||||||
See Bluetooth spec Vol 3, Part F - 3.4.4.1 Read By Type Request
|
See Bluetooth spec Vol 3, Part F - 3.4.4.1 Read By Type Request
|
||||||
@@ -781,7 +780,7 @@ class Server(EventEmitter):
|
|||||||
|
|
||||||
self.send_response(connection, response)
|
self.send_response(connection, response)
|
||||||
|
|
||||||
@AsyncRunner.run_in_task()
|
@utils.AsyncRunner.run_in_task()
|
||||||
async def on_att_read_request(self, connection, request):
|
async def on_att_read_request(self, connection, request):
|
||||||
'''
|
'''
|
||||||
See Bluetooth spec Vol 3, Part F - 3.4.4.3 Read Request
|
See Bluetooth spec Vol 3, Part F - 3.4.4.3 Read Request
|
||||||
@@ -807,7 +806,7 @@ class Server(EventEmitter):
|
|||||||
)
|
)
|
||||||
self.send_response(connection, response)
|
self.send_response(connection, response)
|
||||||
|
|
||||||
@AsyncRunner.run_in_task()
|
@utils.AsyncRunner.run_in_task()
|
||||||
async def on_att_read_blob_request(self, connection, request):
|
async def on_att_read_blob_request(self, connection, request):
|
||||||
'''
|
'''
|
||||||
See Bluetooth spec Vol 3, Part F - 3.4.4.5 Read Blob Request
|
See Bluetooth spec Vol 3, Part F - 3.4.4.5 Read Blob Request
|
||||||
@@ -852,7 +851,7 @@ class Server(EventEmitter):
|
|||||||
)
|
)
|
||||||
self.send_response(connection, response)
|
self.send_response(connection, response)
|
||||||
|
|
||||||
@AsyncRunner.run_in_task()
|
@utils.AsyncRunner.run_in_task()
|
||||||
async def on_att_read_by_group_type_request(self, connection, request):
|
async def on_att_read_by_group_type_request(self, connection, request):
|
||||||
'''
|
'''
|
||||||
See Bluetooth spec Vol 3, Part F - 3.4.4.9 Read by Group Type Request
|
See Bluetooth spec Vol 3, Part F - 3.4.4.9 Read by Group Type Request
|
||||||
@@ -920,7 +919,7 @@ class Server(EventEmitter):
|
|||||||
|
|
||||||
self.send_response(connection, response)
|
self.send_response(connection, response)
|
||||||
|
|
||||||
@AsyncRunner.run_in_task()
|
@utils.AsyncRunner.run_in_task()
|
||||||
async def on_att_write_request(self, connection, request):
|
async def on_att_write_request(self, connection, request):
|
||||||
'''
|
'''
|
||||||
See Bluetooth spec Vol 3, Part F - 3.4.5.1 Write Request
|
See Bluetooth spec Vol 3, Part F - 3.4.5.1 Write Request
|
||||||
@@ -967,7 +966,7 @@ class Server(EventEmitter):
|
|||||||
response = ATT_Write_Response()
|
response = ATT_Write_Response()
|
||||||
self.send_response(connection, response)
|
self.send_response(connection, response)
|
||||||
|
|
||||||
@AsyncRunner.run_in_task()
|
@utils.AsyncRunner.run_in_task()
|
||||||
async def on_att_write_command(self, connection, request):
|
async def on_att_write_command(self, connection, request):
|
||||||
'''
|
'''
|
||||||
See Bluetooth spec Vol 3, Part F - 3.4.5.3 Write Command
|
See Bluetooth spec Vol 3, Part F - 3.4.5.3 Write Command
|
||||||
|
|||||||
+18
-18
@@ -40,7 +40,7 @@ from bumble.core import (
|
|||||||
name_or_number,
|
name_or_number,
|
||||||
padded_bytes,
|
padded_bytes,
|
||||||
)
|
)
|
||||||
from bumble.utils import OpenIntEnum
|
from bumble import utils
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@@ -739,7 +739,7 @@ class PhyBit(enum.IntFlag):
|
|||||||
LE_CODED = 1 << HCI_LE_CODED_PHY_BIT
|
LE_CODED = 1 << HCI_LE_CODED_PHY_BIT
|
||||||
|
|
||||||
|
|
||||||
class CsRole(OpenIntEnum):
|
class CsRole(utils.OpenIntEnum):
|
||||||
INITIATOR = 0x00
|
INITIATOR = 0x00
|
||||||
REFLECTOR = 0x01
|
REFLECTOR = 0x01
|
||||||
|
|
||||||
@@ -749,7 +749,7 @@ class CsRoleMask(enum.IntFlag):
|
|||||||
REFLECTOR = 0x02
|
REFLECTOR = 0x02
|
||||||
|
|
||||||
|
|
||||||
class CsSyncPhy(OpenIntEnum):
|
class CsSyncPhy(utils.OpenIntEnum):
|
||||||
LE_1M = 1
|
LE_1M = 1
|
||||||
LE_2M = 2
|
LE_2M = 2
|
||||||
LE_2M_2BT = 3
|
LE_2M_2BT = 3
|
||||||
@@ -760,7 +760,7 @@ class CsSyncPhySupported(enum.IntFlag):
|
|||||||
LE_2M_2BT = 0x02
|
LE_2M_2BT = 0x02
|
||||||
|
|
||||||
|
|
||||||
class RttType(OpenIntEnum):
|
class RttType(utils.OpenIntEnum):
|
||||||
AA_ONLY = 0x00
|
AA_ONLY = 0x00
|
||||||
SOUNDING_SEQUENCE_32_BIT = 0x01
|
SOUNDING_SEQUENCE_32_BIT = 0x01
|
||||||
SOUNDING_SEQUENCE_96_BIT = 0x02
|
SOUNDING_SEQUENCE_96_BIT = 0x02
|
||||||
@@ -770,7 +770,7 @@ class RttType(OpenIntEnum):
|
|||||||
RANDOM_SEQUENCE_128_BIT = 0x06
|
RANDOM_SEQUENCE_128_BIT = 0x06
|
||||||
|
|
||||||
|
|
||||||
class CsSnr(OpenIntEnum):
|
class CsSnr(utils.OpenIntEnum):
|
||||||
SNR_18_DB = 0x00
|
SNR_18_DB = 0x00
|
||||||
SNR_21_DB = 0x01
|
SNR_21_DB = 0x01
|
||||||
SNR_24_DB = 0x02
|
SNR_24_DB = 0x02
|
||||||
@@ -779,20 +779,20 @@ class CsSnr(OpenIntEnum):
|
|||||||
NOT_APPLIED = 0xFF
|
NOT_APPLIED = 0xFF
|
||||||
|
|
||||||
|
|
||||||
class CsDoneStatus(OpenIntEnum):
|
class CsDoneStatus(utils.OpenIntEnum):
|
||||||
ALL_RESULTS_COMPLETED = 0x00
|
ALL_RESULTS_COMPLETED = 0x00
|
||||||
PARTIAL = 0x01
|
PARTIAL = 0x01
|
||||||
ABORTED = 0x0F
|
ABORTED = 0x0F
|
||||||
|
|
||||||
|
|
||||||
class CsProcedureAbortReason(OpenIntEnum):
|
class CsProcedureAbortReason(utils.OpenIntEnum):
|
||||||
NO_ABORT = 0x00
|
NO_ABORT = 0x00
|
||||||
LOCAL_HOST_OR_REMOTE_REQUEST = 0x01
|
LOCAL_HOST_OR_REMOTE_REQUEST = 0x01
|
||||||
CHANNEL_MAP_UPDATE_INSTANT_PASSED = 0x02
|
CHANNEL_MAP_UPDATE_INSTANT_PASSED = 0x02
|
||||||
UNSPECIFIED = 0x0F
|
UNSPECIFIED = 0x0F
|
||||||
|
|
||||||
|
|
||||||
class CsSubeventAbortReason(OpenIntEnum):
|
class CsSubeventAbortReason(utils.OpenIntEnum):
|
||||||
NO_ABORT = 0x00
|
NO_ABORT = 0x00
|
||||||
LOCAL_HOST_OR_REMOTE_REQUEST = 0x01
|
LOCAL_HOST_OR_REMOTE_REQUEST = 0x01
|
||||||
NO_CS_SYNC_RECEIVED = 0x02
|
NO_CS_SYNC_RECEIVED = 0x02
|
||||||
@@ -890,7 +890,7 @@ HCI_LINK_TYPE_NAMES = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Address types
|
# Address types
|
||||||
class AddressType(OpenIntEnum):
|
class AddressType(utils.OpenIntEnum):
|
||||||
PUBLIC_DEVICE = 0x00
|
PUBLIC_DEVICE = 0x00
|
||||||
RANDOM_DEVICE = 0x01
|
RANDOM_DEVICE = 0x01
|
||||||
PUBLIC_IDENTITY = 0x02
|
PUBLIC_IDENTITY = 0x02
|
||||||
@@ -1239,7 +1239,7 @@ HCI_SUPPORTED_COMMANDS_MASKS = {
|
|||||||
|
|
||||||
# LE Supported Features
|
# LE Supported Features
|
||||||
# See Bluetooth spec @ Vol 6, Part B, 4.6 FEATURE SUPPORT
|
# See Bluetooth spec @ Vol 6, Part B, 4.6 FEATURE SUPPORT
|
||||||
class LeFeature(OpenIntEnum):
|
class LeFeature(utils.OpenIntEnum):
|
||||||
LE_ENCRYPTION = 0
|
LE_ENCRYPTION = 0
|
||||||
CONNECTION_PARAMETERS_REQUEST_PROCEDURE = 1
|
CONNECTION_PARAMETERS_REQUEST_PROCEDURE = 1
|
||||||
EXTENDED_REJECT_INDICATION = 2
|
EXTENDED_REJECT_INDICATION = 2
|
||||||
@@ -1537,7 +1537,7 @@ RTT_TYPE_SPEC = {'size': 1, 'mapper': lambda x: RttType(x).name}
|
|||||||
CS_SNR_SPEC = {'size': 1, 'mapper': lambda x: CsSnr(x).name}
|
CS_SNR_SPEC = {'size': 1, 'mapper': lambda x: CsSnr(x).name}
|
||||||
|
|
||||||
|
|
||||||
class CodecID(OpenIntEnum):
|
class CodecID(utils.OpenIntEnum):
|
||||||
# fmt: off
|
# fmt: off
|
||||||
U_LOG = 0x00
|
U_LOG = 0x00
|
||||||
A_LOG = 0x01
|
A_LOG = 0x01
|
||||||
@@ -5370,11 +5370,11 @@ class HCI_LE_CS_Create_Config_Command(HCI_Command):
|
|||||||
See Bluetooth spec @ 7.8.137 LE CS Create Config command
|
See Bluetooth spec @ 7.8.137 LE CS Create Config command
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class ChannelSelectionType(OpenIntEnum):
|
class ChannelSelectionType(utils.OpenIntEnum):
|
||||||
ALGO_3B = 0
|
ALGO_3B = 0
|
||||||
ALGO_3C = 1
|
ALGO_3C = 1
|
||||||
|
|
||||||
class Ch3cShape(OpenIntEnum):
|
class Ch3cShape(utils.OpenIntEnum):
|
||||||
HAT = 0x00
|
HAT = 0x00
|
||||||
X = 0x01
|
X = 0x01
|
||||||
|
|
||||||
@@ -6203,13 +6203,13 @@ class HCI_LE_Periodic_Advertising_Report_Event(HCI_LE_Meta_Event):
|
|||||||
TX_POWER_INFORMATION_NOT_AVAILABLE = 0x7F
|
TX_POWER_INFORMATION_NOT_AVAILABLE = 0x7F
|
||||||
RSSI_NOT_AVAILABLE = 0x7F
|
RSSI_NOT_AVAILABLE = 0x7F
|
||||||
|
|
||||||
class CteType(OpenIntEnum):
|
class CteType(utils.OpenIntEnum):
|
||||||
AOA_CONSTANT_TONE_EXTENSION = 0x00
|
AOA_CONSTANT_TONE_EXTENSION = 0x00
|
||||||
AOD_CONSTANT_TONE_EXTENSION_1US = 0x01
|
AOD_CONSTANT_TONE_EXTENSION_1US = 0x01
|
||||||
AOD_CONSTANT_TONE_EXTENSION_2US = 0x02
|
AOD_CONSTANT_TONE_EXTENSION_2US = 0x02
|
||||||
NO_CONSTANT_TONE_EXTENSION = 0xFF
|
NO_CONSTANT_TONE_EXTENSION = 0xFF
|
||||||
|
|
||||||
class DataStatus(OpenIntEnum):
|
class DataStatus(utils.OpenIntEnum):
|
||||||
DATA_COMPLETE = 0x00
|
DATA_COMPLETE = 0x00
|
||||||
DATA_INCOMPLETE_MORE_TO_COME = 0x01
|
DATA_INCOMPLETE_MORE_TO_COME = 0x01
|
||||||
DATA_INCOMPLETE_TRUNCATED_NO_MORE_TO_COME = 0x02
|
DATA_INCOMPLETE_TRUNCATED_NO_MORE_TO_COME = 0x02
|
||||||
@@ -6590,7 +6590,7 @@ class HCI_LE_CS_Config_Complete_Event(HCI_LE_Meta_Event):
|
|||||||
See Bluetooth spec @ 7.7.65.42 LE CS Config Complete event
|
See Bluetooth spec @ 7.7.65.42 LE CS Config Complete event
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class Action(OpenIntEnum):
|
class Action(utils.OpenIntEnum):
|
||||||
REMOVED = 0
|
REMOVED = 0
|
||||||
CREATED = 1
|
CREATED = 1
|
||||||
|
|
||||||
@@ -6642,7 +6642,7 @@ class HCI_LE_CS_Procedure_Enable_Complete_Event(HCI_LE_Meta_Event):
|
|||||||
See Bluetooth spec @ 7.7.65.43 LE CS Procedure Enable Complete event
|
See Bluetooth spec @ 7.7.65.43 LE CS Procedure Enable Complete event
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class State(OpenIntEnum):
|
class State(utils.OpenIntEnum):
|
||||||
DISABLED = 0
|
DISABLED = 0
|
||||||
ENABLED = 1
|
ENABLED = 1
|
||||||
|
|
||||||
@@ -6984,7 +6984,7 @@ class HCI_QOS_Setup_Complete_Event(HCI_Event):
|
|||||||
See Bluetooth spec @ 7.7.13 QoS Setup Complete Event
|
See Bluetooth spec @ 7.7.13 QoS Setup Complete Event
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class ServiceType(OpenIntEnum):
|
class ServiceType(utils.OpenIntEnum):
|
||||||
NO_TRAFFIC_AVAILABLE = 0x00
|
NO_TRAFFIC_AVAILABLE = 0x00
|
||||||
BEST_EFFORT_AVAILABLE = 0x01
|
BEST_EFFORT_AVAILABLE = 0x01
|
||||||
GUARANTEED_AVAILABLE = 0x02
|
GUARANTEED_AVAILABLE = 0x02
|
||||||
|
|||||||
+3
-3
@@ -24,7 +24,6 @@ import asyncio
|
|||||||
import dataclasses
|
import dataclasses
|
||||||
import enum
|
import enum
|
||||||
import traceback
|
import traceback
|
||||||
import pyee
|
|
||||||
import re
|
import re
|
||||||
from typing import (
|
from typing import (
|
||||||
Dict,
|
Dict,
|
||||||
@@ -45,6 +44,7 @@ from bumble import at
|
|||||||
from bumble import device
|
from bumble import device
|
||||||
from bumble import rfcomm
|
from bumble import rfcomm
|
||||||
from bumble import sdp
|
from bumble import sdp
|
||||||
|
from bumble import utils
|
||||||
from bumble.colors import color
|
from bumble.colors import color
|
||||||
from bumble.core import (
|
from bumble.core import (
|
||||||
ProtocolError,
|
ProtocolError,
|
||||||
@@ -690,7 +690,7 @@ class HfIndicatorState:
|
|||||||
current_status: int = 0
|
current_status: int = 0
|
||||||
|
|
||||||
|
|
||||||
class HfProtocol(pyee.EventEmitter):
|
class HfProtocol(utils.EventEmitter):
|
||||||
"""
|
"""
|
||||||
Implementation for the Hands-Free side of the Hands-Free profile.
|
Implementation for the Hands-Free side of the Hands-Free profile.
|
||||||
|
|
||||||
@@ -1146,7 +1146,7 @@ class HfProtocol(pyee.EventEmitter):
|
|||||||
logger.error(traceback.format_exc())
|
logger.error(traceback.format_exc())
|
||||||
|
|
||||||
|
|
||||||
class AgProtocol(pyee.EventEmitter):
|
class AgProtocol(utils.EventEmitter):
|
||||||
"""
|
"""
|
||||||
Implementation for the Audio-Gateway side of the Hands-Free profile.
|
Implementation for the Audio-Gateway side of the Hands-Free profile.
|
||||||
|
|
||||||
|
|||||||
+4
-3
@@ -22,11 +22,12 @@ import enum
|
|||||||
import struct
|
import struct
|
||||||
|
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from pyee import EventEmitter
|
|
||||||
from typing import Optional, Callable
|
from typing import Optional, Callable
|
||||||
from typing_extensions import override
|
from typing_extensions import override
|
||||||
|
|
||||||
from bumble import l2cap, device
|
from bumble import l2cap
|
||||||
|
from bumble import device
|
||||||
|
from bumble import utils
|
||||||
from bumble.core import InvalidStateError, ProtocolError
|
from bumble.core import InvalidStateError, ProtocolError
|
||||||
from bumble.hci import Address
|
from bumble.hci import Address
|
||||||
|
|
||||||
@@ -195,7 +196,7 @@ class SendHandshakeMessage(Message):
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class HID(ABC, EventEmitter):
|
class HID(ABC, utils.EventEmitter):
|
||||||
l2cap_ctrl_channel: Optional[l2cap.ClassicChannel] = None
|
l2cap_ctrl_channel: Optional[l2cap.ClassicChannel] = None
|
||||||
l2cap_intr_channel: Optional[l2cap.ClassicChannel] = None
|
l2cap_intr_channel: Optional[l2cap.ClassicChannel] = None
|
||||||
connection: Optional[device.Connection] = None
|
connection: Optional[device.Connection] = None
|
||||||
|
|||||||
+7
-6
@@ -34,7 +34,6 @@ from typing import (
|
|||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
)
|
)
|
||||||
|
|
||||||
import pyee
|
|
||||||
|
|
||||||
from bumble.colors import color
|
from bumble.colors import color
|
||||||
from bumble.l2cap import L2CAP_PDU
|
from bumble.l2cap import L2CAP_PDU
|
||||||
@@ -47,7 +46,7 @@ from bumble.core import (
|
|||||||
ConnectionPHY,
|
ConnectionPHY,
|
||||||
ConnectionParameters,
|
ConnectionParameters,
|
||||||
)
|
)
|
||||||
from bumble.utils import AbortableEventEmitter
|
from bumble import utils
|
||||||
from bumble.transport.common import TransportLostError
|
from bumble.transport.common import TransportLostError
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -61,7 +60,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class DataPacketQueue(pyee.EventEmitter):
|
class DataPacketQueue(utils.EventEmitter):
|
||||||
"""
|
"""
|
||||||
Flow-control queue for host->controller data packets (ACL, ISO).
|
Flow-control queue for host->controller data packets (ACL, ISO).
|
||||||
|
|
||||||
@@ -234,7 +233,7 @@ class IsoLink:
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class Host(AbortableEventEmitter):
|
class Host(utils.EventEmitter):
|
||||||
connections: Dict[int, Connection]
|
connections: Dict[int, Connection]
|
||||||
cis_links: Dict[int, IsoLink]
|
cis_links: Dict[int, IsoLink]
|
||||||
bis_links: Dict[int, IsoLink]
|
bis_links: Dict[int, IsoLink]
|
||||||
@@ -1289,7 +1288,8 @@ class Host(AbortableEventEmitter):
|
|||||||
logger.debug('no long term key provider')
|
logger.debug('no long term key provider')
|
||||||
long_term_key = None
|
long_term_key = None
|
||||||
else:
|
else:
|
||||||
long_term_key = await self.abort_on(
|
long_term_key = await utils.cancel_on_event(
|
||||||
|
self,
|
||||||
'flush',
|
'flush',
|
||||||
# pylint: disable-next=not-callable
|
# pylint: disable-next=not-callable
|
||||||
self.long_term_key_provider(
|
self.long_term_key_provider(
|
||||||
@@ -1447,7 +1447,8 @@ class Host(AbortableEventEmitter):
|
|||||||
logger.debug('no link key provider')
|
logger.debug('no link key provider')
|
||||||
link_key = None
|
link_key = None
|
||||||
else:
|
else:
|
||||||
link_key = await self.abort_on(
|
link_key = await utils.cancel_on_event(
|
||||||
|
self,
|
||||||
'flush',
|
'flush',
|
||||||
# pylint: disable-next=not-callable
|
# pylint: disable-next=not-callable
|
||||||
self.link_key_provider(event.bd_addr),
|
self.link_key_provider(event.bd_addr),
|
||||||
|
|||||||
+13
-14
@@ -23,7 +23,6 @@ import logging
|
|||||||
import struct
|
import struct
|
||||||
|
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from pyee import EventEmitter
|
|
||||||
from typing import (
|
from typing import (
|
||||||
Dict,
|
Dict,
|
||||||
Type,
|
Type,
|
||||||
@@ -39,7 +38,7 @@ from typing import (
|
|||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
)
|
)
|
||||||
|
|
||||||
from bumble.utils import deprecated
|
from bumble import utils
|
||||||
from bumble.colors import color
|
from bumble.colors import color
|
||||||
from bumble.core import (
|
from bumble.core import (
|
||||||
InvalidStateError,
|
InvalidStateError,
|
||||||
@@ -720,7 +719,7 @@ class L2CAP_LE_Flow_Control_Credit(L2CAP_Control_Frame):
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class ClassicChannel(EventEmitter):
|
class ClassicChannel(utils.EventEmitter):
|
||||||
class State(enum.IntEnum):
|
class State(enum.IntEnum):
|
||||||
# States
|
# States
|
||||||
CLOSED = 0x00
|
CLOSED = 0x00
|
||||||
@@ -821,8 +820,8 @@ class ClassicChannel(EventEmitter):
|
|||||||
|
|
||||||
# Wait for the connection to succeed or fail
|
# Wait for the connection to succeed or fail
|
||||||
try:
|
try:
|
||||||
return await self.connection.abort_on(
|
return await utils.cancel_on_event(
|
||||||
'disconnection', self.connection_result
|
self.connection, 'disconnection', self.connection_result
|
||||||
)
|
)
|
||||||
finally:
|
finally:
|
||||||
self.connection_result = None
|
self.connection_result = None
|
||||||
@@ -1026,7 +1025,7 @@ class ClassicChannel(EventEmitter):
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class LeCreditBasedChannel(EventEmitter):
|
class LeCreditBasedChannel(utils.EventEmitter):
|
||||||
"""
|
"""
|
||||||
LE Credit-based Connection Oriented Channel
|
LE Credit-based Connection Oriented Channel
|
||||||
"""
|
"""
|
||||||
@@ -1381,7 +1380,7 @@ class LeCreditBasedChannel(EventEmitter):
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class ClassicChannelServer(EventEmitter):
|
class ClassicChannelServer(utils.EventEmitter):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
manager: ChannelManager,
|
manager: ChannelManager,
|
||||||
@@ -1406,7 +1405,7 @@ class ClassicChannelServer(EventEmitter):
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class LeCreditBasedChannelServer(EventEmitter):
|
class LeCreditBasedChannelServer(utils.EventEmitter):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
manager: ChannelManager,
|
manager: ChannelManager,
|
||||||
@@ -1536,7 +1535,7 @@ class ChannelManager:
|
|||||||
if cid in self.fixed_channels:
|
if cid in self.fixed_channels:
|
||||||
del self.fixed_channels[cid]
|
del self.fixed_channels[cid]
|
||||||
|
|
||||||
@deprecated("Please use create_classic_server")
|
@utils.deprecated("Please use create_classic_server")
|
||||||
def register_server(
|
def register_server(
|
||||||
self,
|
self,
|
||||||
psm: int,
|
psm: int,
|
||||||
@@ -1582,7 +1581,7 @@ class ChannelManager:
|
|||||||
|
|
||||||
return self.servers[spec.psm]
|
return self.servers[spec.psm]
|
||||||
|
|
||||||
@deprecated("Please use create_le_credit_based_server()")
|
@utils.deprecated("Please use create_le_credit_based_server()")
|
||||||
def register_le_coc_server(
|
def register_le_coc_server(
|
||||||
self,
|
self,
|
||||||
psm: int,
|
psm: int,
|
||||||
@@ -2126,7 +2125,7 @@ class ChannelManager:
|
|||||||
if channel.source_cid in connection_channels:
|
if channel.source_cid in connection_channels:
|
||||||
del connection_channels[channel.source_cid]
|
del connection_channels[channel.source_cid]
|
||||||
|
|
||||||
@deprecated("Please use create_le_credit_based_channel()")
|
@utils.deprecated("Please use create_le_credit_based_channel()")
|
||||||
async def open_le_coc(
|
async def open_le_coc(
|
||||||
self, connection: Connection, psm: int, max_credits: int, mtu: int, mps: int
|
self, connection: Connection, psm: int, max_credits: int, mtu: int, mps: int
|
||||||
) -> LeCreditBasedChannel:
|
) -> LeCreditBasedChannel:
|
||||||
@@ -2183,7 +2182,7 @@ class ChannelManager:
|
|||||||
|
|
||||||
return channel
|
return channel
|
||||||
|
|
||||||
@deprecated("Please use create_classic_channel()")
|
@utils.deprecated("Please use create_classic_channel()")
|
||||||
async def connect(self, connection: Connection, psm: int) -> ClassicChannel:
|
async def connect(self, connection: Connection, psm: int) -> ClassicChannel:
|
||||||
return await self.create_classic_channel(
|
return await self.create_classic_channel(
|
||||||
connection=connection, spec=ClassicChannelSpec(psm=psm)
|
connection=connection, spec=ClassicChannelSpec(psm=psm)
|
||||||
@@ -2233,12 +2232,12 @@ class ChannelManager:
|
|||||||
|
|
||||||
|
|
||||||
class Channel(ClassicChannel):
|
class Channel(ClassicChannel):
|
||||||
@deprecated("Please use ClassicChannel")
|
@utils.deprecated("Please use ClassicChannel")
|
||||||
def __init__(self, *args, **kwargs) -> None:
|
def __init__(self, *args, **kwargs) -> None:
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class LeConnectionOrientedChannel(LeCreditBasedChannel):
|
class LeConnectionOrientedChannel(LeCreditBasedChannel):
|
||||||
@deprecated("Please use LeCreditBasedChannel")
|
@utils.deprecated("Please use LeCreditBasedChannel")
|
||||||
def __init__(self, *args, **kwargs) -> None:
|
def __init__(self, *args, **kwargs) -> None:
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|||||||
+10
-3
@@ -20,6 +20,7 @@ import grpc.aio
|
|||||||
import logging
|
import logging
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
|
import bumble.utils
|
||||||
from bumble.pandora import utils
|
from bumble.pandora import utils
|
||||||
from bumble.pandora.config import Config
|
from bumble.pandora.config import Config
|
||||||
from bumble.core import (
|
from bumble.core import (
|
||||||
@@ -534,7 +535,9 @@ class HostService(HostServicer):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
self.log.debug('Stop advertising')
|
self.log.debug('Stop advertising')
|
||||||
await self.device.abort_on('flush', self.device.stop_advertising())
|
await bumble.utils.cancel_on_event(
|
||||||
|
self.device, 'flush', self.device.stop_advertising()
|
||||||
|
)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -602,7 +605,9 @@ class HostService(HostServicer):
|
|||||||
self.device.remove_listener('advertisement', handler) # type: ignore
|
self.device.remove_listener('advertisement', handler) # type: ignore
|
||||||
try:
|
try:
|
||||||
self.log.debug('Stop scanning')
|
self.log.debug('Stop scanning')
|
||||||
await self.device.abort_on('flush', self.device.stop_scanning())
|
await bumble.utils.cancel_on_event(
|
||||||
|
self.device, 'flush', self.device.stop_scanning()
|
||||||
|
)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -642,7 +647,9 @@ class HostService(HostServicer):
|
|||||||
self.device.remove_listener('inquiry_result', result_handler) # type: ignore
|
self.device.remove_listener('inquiry_result', result_handler) # type: ignore
|
||||||
try:
|
try:
|
||||||
self.log.debug('Stop inquiry')
|
self.log.debug('Stop inquiry')
|
||||||
await self.device.abort_on('flush', self.device.stop_discovery())
|
await bumble.utils.cancel_on_event(
|
||||||
|
self.device, 'flush', self.device.stop_discovery()
|
||||||
|
)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ from bumble.core import (
|
|||||||
PhysicalTransport,
|
PhysicalTransport,
|
||||||
ProtocolError,
|
ProtocolError,
|
||||||
)
|
)
|
||||||
|
import bumble.utils
|
||||||
from bumble.device import Connection as BumbleConnection, Device
|
from bumble.device import Connection as BumbleConnection, Device
|
||||||
from bumble.hci import HCI_Error, Role
|
from bumble.hci import HCI_Error, Role
|
||||||
from bumble.utils import EventWatcher
|
|
||||||
from bumble.pairing import PairingConfig, PairingDelegate as BasePairingDelegate
|
from bumble.pairing import PairingConfig, PairingDelegate as BasePairingDelegate
|
||||||
from google.protobuf import any_pb2 # pytype: disable=pyi-error
|
from google.protobuf import any_pb2 # pytype: disable=pyi-error
|
||||||
from google.protobuf import empty_pb2 # pytype: disable=pyi-error
|
from google.protobuf import empty_pb2 # pytype: disable=pyi-error
|
||||||
@@ -300,7 +300,7 @@ class SecurityService(SecurityServicer):
|
|||||||
|
|
||||||
security_result = asyncio.get_running_loop().create_future()
|
security_result = asyncio.get_running_loop().create_future()
|
||||||
|
|
||||||
with contextlib.closing(EventWatcher()) as watcher:
|
with contextlib.closing(bumble.utils.EventWatcher()) as watcher:
|
||||||
|
|
||||||
@watcher.on(connection, 'pairing')
|
@watcher.on(connection, 'pairing')
|
||||||
def on_pairing(*_: Any) -> None:
|
def on_pairing(*_: Any) -> None:
|
||||||
@@ -449,7 +449,7 @@ class SecurityService(SecurityServicer):
|
|||||||
'security_request': pair,
|
'security_request': pair,
|
||||||
}
|
}
|
||||||
|
|
||||||
with contextlib.closing(EventWatcher()) as watcher:
|
with contextlib.closing(bumble.utils.EventWatcher()) as watcher:
|
||||||
# register event handlers
|
# register event handlers
|
||||||
for event, listener in listeners.items():
|
for event, listener in listeners.items():
|
||||||
watcher.on(connection, event, listener)
|
watcher.on(connection, event, listener)
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ from bumble.gatt_adapters import (
|
|||||||
UTF8CharacteristicProxyAdapter,
|
UTF8CharacteristicProxyAdapter,
|
||||||
)
|
)
|
||||||
from bumble.gatt_client import ProfileServiceProxy, ServiceProxy
|
from bumble.gatt_client import ProfileServiceProxy, ServiceProxy
|
||||||
from bumble.utils import OpenIntEnum
|
from bumble import utils
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Logging
|
# Logging
|
||||||
@@ -64,7 +64,7 @@ GAIN_SETTINGS_MIN_VALUE = 0
|
|||||||
GAIN_SETTINGS_MAX_VALUE = 255
|
GAIN_SETTINGS_MAX_VALUE = 255
|
||||||
|
|
||||||
|
|
||||||
class ErrorCode(OpenIntEnum):
|
class ErrorCode(utils.OpenIntEnum):
|
||||||
'''
|
'''
|
||||||
Cf. 1.6 Application error codes
|
Cf. 1.6 Application error codes
|
||||||
'''
|
'''
|
||||||
@@ -76,7 +76,7 @@ class ErrorCode(OpenIntEnum):
|
|||||||
GAIN_MODE_CHANGE_NOT_ALLOWED = 0x84
|
GAIN_MODE_CHANGE_NOT_ALLOWED = 0x84
|
||||||
|
|
||||||
|
|
||||||
class Mute(OpenIntEnum):
|
class Mute(utils.OpenIntEnum):
|
||||||
'''
|
'''
|
||||||
Cf. 2.2.1.2 Mute Field
|
Cf. 2.2.1.2 Mute Field
|
||||||
'''
|
'''
|
||||||
@@ -86,7 +86,7 @@ class Mute(OpenIntEnum):
|
|||||||
DISABLED = 0x02
|
DISABLED = 0x02
|
||||||
|
|
||||||
|
|
||||||
class GainMode(OpenIntEnum):
|
class GainMode(utils.OpenIntEnum):
|
||||||
'''
|
'''
|
||||||
Cf. 2.2.1.3 Gain Mode
|
Cf. 2.2.1.3 Gain Mode
|
||||||
'''
|
'''
|
||||||
@@ -97,7 +97,7 @@ class GainMode(OpenIntEnum):
|
|||||||
AUTOMATIC = 0x03
|
AUTOMATIC = 0x03
|
||||||
|
|
||||||
|
|
||||||
class AudioInputStatus(OpenIntEnum):
|
class AudioInputStatus(utils.OpenIntEnum):
|
||||||
'''
|
'''
|
||||||
Cf. 3.4 Audio Input Status
|
Cf. 3.4 Audio Input Status
|
||||||
'''
|
'''
|
||||||
@@ -106,7 +106,7 @@ class AudioInputStatus(OpenIntEnum):
|
|||||||
ACTIVE = 0x01
|
ACTIVE = 0x01
|
||||||
|
|
||||||
|
|
||||||
class AudioInputControlPointOpCode(OpenIntEnum):
|
class AudioInputControlPointOpCode(utils.OpenIntEnum):
|
||||||
'''
|
'''
|
||||||
Cf. 3.5.1 Audio Input Control Point procedure requirements
|
Cf. 3.5.1 Audio Input Control Point procedure requirements
|
||||||
'''
|
'''
|
||||||
|
|||||||
+9
-10
@@ -28,7 +28,6 @@ import logging
|
|||||||
import struct
|
import struct
|
||||||
from typing import Optional, Sequence, Union
|
from typing import Optional, Sequence, Union
|
||||||
|
|
||||||
from pyee import EventEmitter
|
|
||||||
|
|
||||||
from bumble.att import ATT_Error
|
from bumble.att import ATT_Error
|
||||||
from bumble.device import Peer
|
from bumble.device import Peer
|
||||||
@@ -42,7 +41,7 @@ from bumble.gatt import (
|
|||||||
)
|
)
|
||||||
from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy, ServiceProxy
|
from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy, ServiceProxy
|
||||||
from bumble.gatt_adapters import SerializableCharacteristicProxyAdapter
|
from bumble.gatt_adapters import SerializableCharacteristicProxyAdapter
|
||||||
from bumble.utils import OpenIntEnum
|
from bumble import utils
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@@ -60,16 +59,16 @@ logger = logging.getLogger(__name__)
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Protocol
|
# Protocol
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class ActionId(OpenIntEnum):
|
class ActionId(utils.OpenIntEnum):
|
||||||
POSITIVE = 0
|
POSITIVE = 0
|
||||||
NEGATIVE = 1
|
NEGATIVE = 1
|
||||||
|
|
||||||
|
|
||||||
class AppAttributeId(OpenIntEnum):
|
class AppAttributeId(utils.OpenIntEnum):
|
||||||
DISPLAY_NAME = 0
|
DISPLAY_NAME = 0
|
||||||
|
|
||||||
|
|
||||||
class CategoryId(OpenIntEnum):
|
class CategoryId(utils.OpenIntEnum):
|
||||||
OTHER = 0
|
OTHER = 0
|
||||||
INCOMING_CALL = 1
|
INCOMING_CALL = 1
|
||||||
MISSED_CALL = 2
|
MISSED_CALL = 2
|
||||||
@@ -84,13 +83,13 @@ class CategoryId(OpenIntEnum):
|
|||||||
ENTERTAINMENT = 11
|
ENTERTAINMENT = 11
|
||||||
|
|
||||||
|
|
||||||
class CommandId(OpenIntEnum):
|
class CommandId(utils.OpenIntEnum):
|
||||||
GET_NOTIFICATION_ATTRIBUTES = 0
|
GET_NOTIFICATION_ATTRIBUTES = 0
|
||||||
GET_APP_ATTRIBUTES = 1
|
GET_APP_ATTRIBUTES = 1
|
||||||
PERFORM_NOTIFICATION_ACTION = 2
|
PERFORM_NOTIFICATION_ACTION = 2
|
||||||
|
|
||||||
|
|
||||||
class EventId(OpenIntEnum):
|
class EventId(utils.OpenIntEnum):
|
||||||
NOTIFICATION_ADDED = 0
|
NOTIFICATION_ADDED = 0
|
||||||
NOTIFICATION_MODIFIED = 1
|
NOTIFICATION_MODIFIED = 1
|
||||||
NOTIFICATION_REMOVED = 2
|
NOTIFICATION_REMOVED = 2
|
||||||
@@ -104,7 +103,7 @@ class EventFlags(enum.IntFlag):
|
|||||||
NEGATIVE_ACTION = 1 << 4
|
NEGATIVE_ACTION = 1 << 4
|
||||||
|
|
||||||
|
|
||||||
class NotificationAttributeId(OpenIntEnum):
|
class NotificationAttributeId(utils.OpenIntEnum):
|
||||||
APP_IDENTIFIER = 0
|
APP_IDENTIFIER = 0
|
||||||
TITLE = 1
|
TITLE = 1
|
||||||
SUBTITLE = 2
|
SUBTITLE = 2
|
||||||
@@ -156,7 +155,7 @@ class Notification:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ErrorCode(OpenIntEnum):
|
class ErrorCode(utils.OpenIntEnum):
|
||||||
UNKNOWN_COMMAND = 0xA0
|
UNKNOWN_COMMAND = 0xA0
|
||||||
INVALID_COMMAND = 0xA1
|
INVALID_COMMAND = 0xA1
|
||||||
INVALID_PARAMETER = 0xA2
|
INVALID_PARAMETER = 0xA2
|
||||||
@@ -243,7 +242,7 @@ class AncsProxy(ProfileServiceProxy):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class AncsClient(EventEmitter):
|
class AncsClient(utils.EventEmitter):
|
||||||
_expected_response_command_id: Optional[CommandId]
|
_expected_response_command_id: Optional[CommandId]
|
||||||
_expected_response_notification_uid: Optional[int]
|
_expected_response_notification_uid: Optional[int]
|
||||||
_expected_response_app_identifier: Optional[str]
|
_expected_response_app_identifier: Optional[str]
|
||||||
|
|||||||
+13
-6
@@ -23,6 +23,7 @@ import logging
|
|||||||
import struct
|
import struct
|
||||||
from typing import Any, Dict, List, Optional, Sequence, Tuple, Type, Union
|
from typing import Any, Dict, List, Optional, Sequence, Tuple, Type, Union
|
||||||
|
|
||||||
|
from bumble import utils
|
||||||
from bumble import colors
|
from bumble import colors
|
||||||
from bumble.profiles.bap import CodecSpecificConfiguration
|
from bumble.profiles.bap import CodecSpecificConfiguration
|
||||||
from bumble.profiles import le_audio
|
from bumble.profiles import le_audio
|
||||||
@@ -343,8 +344,10 @@ class AseStateMachine(gatt.Characteristic):
|
|||||||
and cis_id == self.cis_id
|
and cis_id == self.cis_id
|
||||||
and self.state == self.State.ENABLING
|
and self.state == self.State.ENABLING
|
||||||
):
|
):
|
||||||
acl_connection.abort_on(
|
utils.cancel_on_event(
|
||||||
'flush', self.service.device.accept_cis_request(cis_handle)
|
acl_connection,
|
||||||
|
'flush',
|
||||||
|
self.service.device.accept_cis_request(cis_handle),
|
||||||
)
|
)
|
||||||
|
|
||||||
def on_cis_establishment(self, cis_link: device.CisLink) -> None:
|
def on_cis_establishment(self, cis_link: device.CisLink) -> None:
|
||||||
@@ -361,7 +364,9 @@ class AseStateMachine(gatt.Characteristic):
|
|||||||
self.state = self.State.STREAMING
|
self.state = self.State.STREAMING
|
||||||
await self.service.device.notify_subscribers(self, self.value)
|
await self.service.device.notify_subscribers(self, self.value)
|
||||||
|
|
||||||
cis_link.acl_connection.abort_on('flush', post_cis_established())
|
utils.cancel_on_event(
|
||||||
|
cis_link.acl_connection, 'flush', post_cis_established()
|
||||||
|
)
|
||||||
self.cis_link = cis_link
|
self.cis_link = cis_link
|
||||||
|
|
||||||
def on_cis_disconnection(self, _reason) -> None:
|
def on_cis_disconnection(self, _reason) -> None:
|
||||||
@@ -509,7 +514,7 @@ class AseStateMachine(gatt.Characteristic):
|
|||||||
self.state = self.State.IDLE
|
self.state = self.State.IDLE
|
||||||
await self.service.device.notify_subscribers(self, self.value)
|
await self.service.device.notify_subscribers(self, self.value)
|
||||||
|
|
||||||
self.service.device.abort_on('flush', remove_cis_async())
|
utils.cancel_on_event(self.service.device, 'flush', remove_cis_async())
|
||||||
return (AseResponseCode.SUCCESS, AseReasonCode.NONE)
|
return (AseResponseCode.SUCCESS, AseReasonCode.NONE)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -691,7 +696,8 @@ class AudioStreamControlService(gatt.TemplateService):
|
|||||||
control_point_notification = bytes(
|
control_point_notification = bytes(
|
||||||
[operation.op_code, len(responses)]
|
[operation.op_code, len(responses)]
|
||||||
) + b''.join(map(bytes, responses))
|
) + b''.join(map(bytes, responses))
|
||||||
self.device.abort_on(
|
utils.cancel_on_event(
|
||||||
|
self.device,
|
||||||
'flush',
|
'flush',
|
||||||
self.device.notify_subscribers(
|
self.device.notify_subscribers(
|
||||||
self.ase_control_point, control_point_notification
|
self.ase_control_point, control_point_notification
|
||||||
@@ -700,7 +706,8 @@ class AudioStreamControlService(gatt.TemplateService):
|
|||||||
|
|
||||||
for ase_id, *_ in responses:
|
for ase_id, *_ in responses:
|
||||||
if ase := self.ase_state_machines.get(ase_id):
|
if ase := self.ase_state_machines.get(ase_id):
|
||||||
self.device.abort_on(
|
utils.cancel_on_event(
|
||||||
|
self.device,
|
||||||
'flush',
|
'flush',
|
||||||
self.device.notify_subscribers(ase, ase.value),
|
self.device.notify_subscribers(ase, ase.value),
|
||||||
)
|
)
|
||||||
|
|||||||
+13
-13
@@ -25,14 +25,14 @@ from typing import Any, Dict, List, Optional, Set, Union
|
|||||||
from bumble import att, gatt, gatt_adapters, gatt_client
|
from bumble import att, gatt, gatt_adapters, gatt_client
|
||||||
from bumble.core import InvalidArgumentError, InvalidStateError
|
from bumble.core import InvalidArgumentError, InvalidStateError
|
||||||
from bumble.device import Device, Connection
|
from bumble.device import Device, Connection
|
||||||
from bumble.utils import AsyncRunner, OpenIntEnum
|
from bumble import utils
|
||||||
from bumble.hci import Address
|
from bumble.hci import Address
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Constants
|
# Constants
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class ErrorCode(OpenIntEnum):
|
class ErrorCode(utils.OpenIntEnum):
|
||||||
'''See Hearing Access Service 2.4. Attribute Profile error codes.'''
|
'''See Hearing Access Service 2.4. Attribute Profile error codes.'''
|
||||||
|
|
||||||
INVALID_OPCODE = 0x80
|
INVALID_OPCODE = 0x80
|
||||||
@@ -42,7 +42,7 @@ class ErrorCode(OpenIntEnum):
|
|||||||
INVALID_PARAMETERS_LENGTH = 0x84
|
INVALID_PARAMETERS_LENGTH = 0x84
|
||||||
|
|
||||||
|
|
||||||
class HearingAidType(OpenIntEnum):
|
class HearingAidType(utils.OpenIntEnum):
|
||||||
'''See Hearing Access Service 3.1. Hearing Aid Features.'''
|
'''See Hearing Access Service 3.1. Hearing Aid Features.'''
|
||||||
|
|
||||||
BINAURAL_HEARING_AID = 0b00
|
BINAURAL_HEARING_AID = 0b00
|
||||||
@@ -50,35 +50,35 @@ class HearingAidType(OpenIntEnum):
|
|||||||
BANDED_HEARING_AID = 0b10
|
BANDED_HEARING_AID = 0b10
|
||||||
|
|
||||||
|
|
||||||
class PresetSynchronizationSupport(OpenIntEnum):
|
class PresetSynchronizationSupport(utils.OpenIntEnum):
|
||||||
'''See Hearing Access Service 3.1. Hearing Aid Features.'''
|
'''See Hearing Access Service 3.1. Hearing Aid Features.'''
|
||||||
|
|
||||||
PRESET_SYNCHRONIZATION_IS_NOT_SUPPORTED = 0b0
|
PRESET_SYNCHRONIZATION_IS_NOT_SUPPORTED = 0b0
|
||||||
PRESET_SYNCHRONIZATION_IS_SUPPORTED = 0b1
|
PRESET_SYNCHRONIZATION_IS_SUPPORTED = 0b1
|
||||||
|
|
||||||
|
|
||||||
class IndependentPresets(OpenIntEnum):
|
class IndependentPresets(utils.OpenIntEnum):
|
||||||
'''See Hearing Access Service 3.1. Hearing Aid Features.'''
|
'''See Hearing Access Service 3.1. Hearing Aid Features.'''
|
||||||
|
|
||||||
IDENTICAL_PRESET_RECORD = 0b0
|
IDENTICAL_PRESET_RECORD = 0b0
|
||||||
DIFFERENT_PRESET_RECORD = 0b1
|
DIFFERENT_PRESET_RECORD = 0b1
|
||||||
|
|
||||||
|
|
||||||
class DynamicPresets(OpenIntEnum):
|
class DynamicPresets(utils.OpenIntEnum):
|
||||||
'''See Hearing Access Service 3.1. Hearing Aid Features.'''
|
'''See Hearing Access Service 3.1. Hearing Aid Features.'''
|
||||||
|
|
||||||
PRESET_RECORDS_DOES_NOT_CHANGE = 0b0
|
PRESET_RECORDS_DOES_NOT_CHANGE = 0b0
|
||||||
PRESET_RECORDS_MAY_CHANGE = 0b1
|
PRESET_RECORDS_MAY_CHANGE = 0b1
|
||||||
|
|
||||||
|
|
||||||
class WritablePresetsSupport(OpenIntEnum):
|
class WritablePresetsSupport(utils.OpenIntEnum):
|
||||||
'''See Hearing Access Service 3.1. Hearing Aid Features.'''
|
'''See Hearing Access Service 3.1. Hearing Aid Features.'''
|
||||||
|
|
||||||
WRITABLE_PRESET_RECORDS_NOT_SUPPORTED = 0b0
|
WRITABLE_PRESET_RECORDS_NOT_SUPPORTED = 0b0
|
||||||
WRITABLE_PRESET_RECORDS_SUPPORTED = 0b1
|
WRITABLE_PRESET_RECORDS_SUPPORTED = 0b1
|
||||||
|
|
||||||
|
|
||||||
class HearingAidPresetControlPointOpcode(OpenIntEnum):
|
class HearingAidPresetControlPointOpcode(utils.OpenIntEnum):
|
||||||
'''See Hearing Access Service 3.3.1 Hearing Aid Preset Control Point operation requirements.'''
|
'''See Hearing Access Service 3.3.1 Hearing Aid Preset Control Point operation requirements.'''
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
@@ -130,7 +130,7 @@ def HearingAidFeatures_from_bytes(data: int) -> HearingAidFeatures:
|
|||||||
class PresetChangedOperation:
|
class PresetChangedOperation:
|
||||||
'''See Hearing Access Service 3.2.2.2. Preset Changed operation.'''
|
'''See Hearing Access Service 3.2.2.2. Preset Changed operation.'''
|
||||||
|
|
||||||
class ChangeId(OpenIntEnum):
|
class ChangeId(utils.OpenIntEnum):
|
||||||
# fmt: off
|
# fmt: off
|
||||||
GENERIC_UPDATE = 0x00
|
GENERIC_UPDATE = 0x00
|
||||||
PRESET_RECORD_DELETED = 0x01
|
PRESET_RECORD_DELETED = 0x01
|
||||||
@@ -190,11 +190,11 @@ class PresetRecord:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Property:
|
class Property:
|
||||||
class Writable(OpenIntEnum):
|
class Writable(utils.OpenIntEnum):
|
||||||
CANNOT_BE_WRITTEN = 0b0
|
CANNOT_BE_WRITTEN = 0b0
|
||||||
CAN_BE_WRITTEN = 0b1
|
CAN_BE_WRITTEN = 0b1
|
||||||
|
|
||||||
class IsAvailable(OpenIntEnum):
|
class IsAvailable(utils.OpenIntEnum):
|
||||||
IS_UNAVAILABLE = 0b0
|
IS_UNAVAILABLE = 0b0
|
||||||
IS_AVAILABLE = 0b1
|
IS_AVAILABLE = 0b1
|
||||||
|
|
||||||
@@ -333,7 +333,7 @@ class HearingAccessService(gatt.TemplateService):
|
|||||||
# Update the active preset index if needed
|
# Update the active preset index if needed
|
||||||
await self.notify_active_preset_for_connection(connection)
|
await self.notify_active_preset_for_connection(connection)
|
||||||
|
|
||||||
connection.abort_on('disconnection', on_connection_async())
|
utils.cancel_on_event(connection, 'disconnection', on_connection_async())
|
||||||
|
|
||||||
def _on_read_active_preset_index(
|
def _on_read_active_preset_index(
|
||||||
self, __connection__: Optional[Connection]
|
self, __connection__: Optional[Connection]
|
||||||
@@ -382,7 +382,7 @@ class HearingAccessService(gatt.TemplateService):
|
|||||||
if len(presets) == 0:
|
if len(presets) == 0:
|
||||||
raise att.ATT_Error(att.ErrorCode.OUT_OF_RANGE)
|
raise att.ATT_Error(att.ErrorCode.OUT_OF_RANGE)
|
||||||
|
|
||||||
AsyncRunner.spawn(self._read_preset_response(connection, presets))
|
utils.AsyncRunner.spawn(self._read_preset_response(connection, presets))
|
||||||
|
|
||||||
async def _read_preset_response(
|
async def _read_preset_response(
|
||||||
self, connection: Connection, presets: List[PresetRecord]
|
self, connection: Connection, presets: List[PresetRecord]
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import enum
|
|||||||
from typing import Optional, Sequence
|
from typing import Optional, Sequence
|
||||||
|
|
||||||
from bumble import att
|
from bumble import att
|
||||||
|
from bumble import utils
|
||||||
from bumble import device
|
from bumble import device
|
||||||
from bumble import gatt
|
from bumble import gatt
|
||||||
from bumble import gatt_adapters
|
from bumble import gatt_adapters
|
||||||
@@ -160,7 +161,8 @@ class VolumeControlService(gatt.TemplateService):
|
|||||||
handler = getattr(self, '_on_' + opcode.name.lower())
|
handler = getattr(self, '_on_' + opcode.name.lower())
|
||||||
if handler(*value[2:]):
|
if handler(*value[2:]):
|
||||||
self.change_counter = (self.change_counter + 1) % 256
|
self.change_counter = (self.change_counter + 1) % 256
|
||||||
connection.abort_on(
|
utils.cancel_on_event(
|
||||||
|
connection,
|
||||||
'disconnection',
|
'disconnection',
|
||||||
connection.device.notify_subscribers(attribute=self.volume_state),
|
connection.device.notify_subscribers(attribute=self.volume_state),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ from bumble.gatt_adapters import (
|
|||||||
UTF8CharacteristicProxyAdapter,
|
UTF8CharacteristicProxyAdapter,
|
||||||
)
|
)
|
||||||
from bumble.gatt_client import ProfileServiceProxy, ServiceProxy
|
from bumble.gatt_client import ProfileServiceProxy, ServiceProxy
|
||||||
from bumble.utils import OpenIntEnum
|
from bumble import utils
|
||||||
from bumble.profiles.bap import AudioLocation
|
from bumble.profiles.bap import AudioLocation
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@@ -50,11 +50,11 @@ MAX_VOLUME_OFFSET = 255
|
|||||||
CHANGE_COUNTER_MAX_VALUE = 0xFF
|
CHANGE_COUNTER_MAX_VALUE = 0xFF
|
||||||
|
|
||||||
|
|
||||||
class SetVolumeOffsetOpCode(OpenIntEnum):
|
class SetVolumeOffsetOpCode(utils.OpenIntEnum):
|
||||||
SET_VOLUME_OFFSET = 0x01
|
SET_VOLUME_OFFSET = 0x01
|
||||||
|
|
||||||
|
|
||||||
class ErrorCode(OpenIntEnum):
|
class ErrorCode(utils.OpenIntEnum):
|
||||||
"""
|
"""
|
||||||
See Volume Offset Control Service 1.6. Application error codes.
|
See Volume Offset Control Service 1.6. Application error codes.
|
||||||
"""
|
"""
|
||||||
|
|||||||
+4
-4
@@ -25,11 +25,11 @@ import enum
|
|||||||
from typing import Callable, Dict, List, Optional, Tuple, Union, TYPE_CHECKING
|
from typing import Callable, Dict, List, Optional, Tuple, Union, TYPE_CHECKING
|
||||||
from typing_extensions import Self
|
from typing_extensions import Self
|
||||||
|
|
||||||
from pyee import EventEmitter
|
|
||||||
|
|
||||||
from bumble import core
|
from bumble import core
|
||||||
from bumble import l2cap
|
from bumble import l2cap
|
||||||
from bumble import sdp
|
from bumble import sdp
|
||||||
|
from bumble import utils
|
||||||
from bumble.colors import color
|
from bumble.colors import color
|
||||||
from bumble.core import (
|
from bumble.core import (
|
||||||
UUID,
|
UUID,
|
||||||
@@ -441,7 +441,7 @@ class RFCOMM_MCC_MSC:
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class DLC(EventEmitter):
|
class DLC(utils.EventEmitter):
|
||||||
class State(enum.IntEnum):
|
class State(enum.IntEnum):
|
||||||
INIT = 0x00
|
INIT = 0x00
|
||||||
CONNECTING = 0x01
|
CONNECTING = 0x01
|
||||||
@@ -749,7 +749,7 @@ class DLC(EventEmitter):
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class Multiplexer(EventEmitter):
|
class Multiplexer(utils.EventEmitter):
|
||||||
class Role(enum.IntEnum):
|
class Role(enum.IntEnum):
|
||||||
INITIATOR = 0x00
|
INITIATOR = 0x00
|
||||||
RESPONDER = 0x01
|
RESPONDER = 0x01
|
||||||
@@ -1075,7 +1075,7 @@ class Client:
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class Server(EventEmitter):
|
class Server(utils.EventEmitter):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, device: Device, l2cap_mtu: int = RFCOMM_DEFAULT_L2CAP_MTU
|
self, device: Device, l2cap_mtu: int = RFCOMM_DEFAULT_L2CAP_MTU
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|||||||
+21
-16
@@ -41,7 +41,6 @@ from typing import (
|
|||||||
cast,
|
cast,
|
||||||
)
|
)
|
||||||
|
|
||||||
from pyee import EventEmitter
|
|
||||||
|
|
||||||
from bumble.colors import color
|
from bumble.colors import color
|
||||||
from bumble.hci import (
|
from bumble.hci import (
|
||||||
@@ -60,6 +59,7 @@ from bumble.core import (
|
|||||||
)
|
)
|
||||||
from bumble.keys import PairingKeys
|
from bumble.keys import PairingKeys
|
||||||
from bumble import crypto
|
from bumble import crypto
|
||||||
|
from bumble import utils
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from bumble.device import Connection, Device
|
from bumble.device import Connection, Device
|
||||||
@@ -899,7 +899,7 @@ class Session:
|
|||||||
|
|
||||||
self.send_pairing_failed(SMP_CONFIRM_VALUE_FAILED_ERROR)
|
self.send_pairing_failed(SMP_CONFIRM_VALUE_FAILED_ERROR)
|
||||||
|
|
||||||
self.connection.abort_on('disconnection', prompt())
|
utils.cancel_on_event(self.connection, 'disconnection', prompt())
|
||||||
|
|
||||||
def prompt_user_for_numeric_comparison(
|
def prompt_user_for_numeric_comparison(
|
||||||
self, code: int, next_steps: Callable[[], None]
|
self, code: int, next_steps: Callable[[], None]
|
||||||
@@ -918,7 +918,7 @@ class Session:
|
|||||||
|
|
||||||
self.send_pairing_failed(SMP_CONFIRM_VALUE_FAILED_ERROR)
|
self.send_pairing_failed(SMP_CONFIRM_VALUE_FAILED_ERROR)
|
||||||
|
|
||||||
self.connection.abort_on('disconnection', prompt())
|
utils.cancel_on_event(self.connection, 'disconnection', prompt())
|
||||||
|
|
||||||
def prompt_user_for_number(self, next_steps: Callable[[int], None]) -> None:
|
def prompt_user_for_number(self, next_steps: Callable[[int], None]) -> None:
|
||||||
async def prompt() -> None:
|
async def prompt() -> None:
|
||||||
@@ -935,7 +935,7 @@ class Session:
|
|||||||
logger.warning(f'exception while prompting: {error}')
|
logger.warning(f'exception while prompting: {error}')
|
||||||
self.send_pairing_failed(SMP_PASSKEY_ENTRY_FAILED_ERROR)
|
self.send_pairing_failed(SMP_PASSKEY_ENTRY_FAILED_ERROR)
|
||||||
|
|
||||||
self.connection.abort_on('disconnection', prompt())
|
utils.cancel_on_event(self.connection, 'disconnection', prompt())
|
||||||
|
|
||||||
def display_passkey(self) -> None:
|
def display_passkey(self) -> None:
|
||||||
# Generate random Passkey/PIN code
|
# Generate random Passkey/PIN code
|
||||||
@@ -950,7 +950,8 @@ class Session:
|
|||||||
logger.debug(f'TK from passkey = {self.tk.hex()}')
|
logger.debug(f'TK from passkey = {self.tk.hex()}')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.connection.abort_on(
|
utils.cancel_on_event(
|
||||||
|
self.connection,
|
||||||
'disconnection',
|
'disconnection',
|
||||||
self.pairing_config.delegate.display_number(self.passkey, digits=6),
|
self.pairing_config.delegate.display_number(self.passkey, digits=6),
|
||||||
)
|
)
|
||||||
@@ -1049,7 +1050,7 @@ class Session:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Perform the next steps asynchronously in case we need to wait for input
|
# Perform the next steps asynchronously in case we need to wait for input
|
||||||
self.connection.abort_on('disconnection', next_steps())
|
utils.cancel_on_event(self.connection, 'disconnection', next_steps())
|
||||||
else:
|
else:
|
||||||
confirm_value = crypto.c1(
|
confirm_value = crypto.c1(
|
||||||
self.tk,
|
self.tk,
|
||||||
@@ -1172,8 +1173,8 @@ class Session:
|
|||||||
self.connection.transport == PhysicalTransport.BR_EDR
|
self.connection.transport == PhysicalTransport.BR_EDR
|
||||||
and self.initiator_key_distribution & SMP_ENC_KEY_DISTRIBUTION_FLAG
|
and self.initiator_key_distribution & SMP_ENC_KEY_DISTRIBUTION_FLAG
|
||||||
):
|
):
|
||||||
self.ctkd_task = self.connection.abort_on(
|
self.ctkd_task = utils.cancel_on_event(
|
||||||
'disconnection', self.get_link_key_and_derive_ltk()
|
self.connection, 'disconnection', self.get_link_key_and_derive_ltk()
|
||||||
)
|
)
|
||||||
elif not self.sc:
|
elif not self.sc:
|
||||||
# Distribute the LTK, EDIV and RAND
|
# Distribute the LTK, EDIV and RAND
|
||||||
@@ -1211,8 +1212,8 @@ class Session:
|
|||||||
self.connection.transport == PhysicalTransport.BR_EDR
|
self.connection.transport == PhysicalTransport.BR_EDR
|
||||||
and self.responder_key_distribution & SMP_ENC_KEY_DISTRIBUTION_FLAG
|
and self.responder_key_distribution & SMP_ENC_KEY_DISTRIBUTION_FLAG
|
||||||
):
|
):
|
||||||
self.ctkd_task = self.connection.abort_on(
|
self.ctkd_task = utils.cancel_on_event(
|
||||||
'disconnection', self.get_link_key_and_derive_ltk()
|
self.connection, 'disconnection', self.get_link_key_and_derive_ltk()
|
||||||
)
|
)
|
||||||
# Distribute the LTK, EDIV and RAND
|
# Distribute the LTK, EDIV and RAND
|
||||||
elif not self.sc:
|
elif not self.sc:
|
||||||
@@ -1304,7 +1305,9 @@ class Session:
|
|||||||
|
|
||||||
# Wait for the pairing process to finish
|
# Wait for the pairing process to finish
|
||||||
assert self.pairing_result
|
assert self.pairing_result
|
||||||
await self.connection.abort_on('disconnection', self.pairing_result)
|
await utils.cancel_on_event(
|
||||||
|
self.connection, 'disconnection', self.pairing_result
|
||||||
|
)
|
||||||
|
|
||||||
def on_disconnection(self, _: int) -> None:
|
def on_disconnection(self, _: int) -> None:
|
||||||
self.connection.remove_listener('disconnection', self.on_disconnection)
|
self.connection.remove_listener('disconnection', self.on_disconnection)
|
||||||
@@ -1322,7 +1325,7 @@ class Session:
|
|||||||
if self.is_initiator:
|
if self.is_initiator:
|
||||||
self.distribute_keys()
|
self.distribute_keys()
|
||||||
|
|
||||||
self.connection.abort_on('disconnection', self.on_pairing())
|
utils.cancel_on_event(self.connection, 'disconnection', self.on_pairing())
|
||||||
|
|
||||||
def on_connection_encryption_change(self) -> None:
|
def on_connection_encryption_change(self) -> None:
|
||||||
if self.connection.is_encrypted and not self.completed:
|
if self.connection.is_encrypted and not self.completed:
|
||||||
@@ -1431,8 +1434,10 @@ class Session:
|
|||||||
def on_smp_pairing_request_command(
|
def on_smp_pairing_request_command(
|
||||||
self, command: SMP_Pairing_Request_Command
|
self, command: SMP_Pairing_Request_Command
|
||||||
) -> None:
|
) -> None:
|
||||||
self.connection.abort_on(
|
utils.cancel_on_event(
|
||||||
'disconnection', self.on_smp_pairing_request_command_async(command)
|
self.connection,
|
||||||
|
'disconnection',
|
||||||
|
self.on_smp_pairing_request_command_async(command),
|
||||||
)
|
)
|
||||||
|
|
||||||
async def on_smp_pairing_request_command_async(
|
async def on_smp_pairing_request_command_async(
|
||||||
@@ -1877,7 +1882,7 @@ class Session:
|
|||||||
self.wait_before_continuing = None
|
self.wait_before_continuing = None
|
||||||
self.send_pairing_dhkey_check_command()
|
self.send_pairing_dhkey_check_command()
|
||||||
|
|
||||||
self.connection.abort_on('disconnection', next_steps())
|
utils.cancel_on_event(self.connection, 'disconnection', next_steps())
|
||||||
else:
|
else:
|
||||||
self.send_pairing_dhkey_check_command()
|
self.send_pairing_dhkey_check_command()
|
||||||
else:
|
else:
|
||||||
@@ -1921,7 +1926,7 @@ class Session:
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class Manager(EventEmitter):
|
class Manager(utils.EventEmitter):
|
||||||
'''
|
'''
|
||||||
Implements the Initiator and Responder roles of the Security Manager Protocol
|
Implements the Initiator and Responder roles of the Security Manager Protocol
|
||||||
'''
|
'''
|
||||||
|
|||||||
+91
-78
@@ -38,7 +38,8 @@ from typing import (
|
|||||||
)
|
)
|
||||||
from typing_extensions import Self
|
from typing_extensions import Self
|
||||||
|
|
||||||
from pyee import EventEmitter
|
import pyee
|
||||||
|
import pyee.asyncio
|
||||||
|
|
||||||
from bumble.colors import color
|
from bumble.colors import color
|
||||||
|
|
||||||
@@ -56,6 +57,48 @@ def setup_event_forwarding(emitter, forwarder, event_name):
|
|||||||
emitter.on(event_name, emit)
|
emitter.on(event_name, emit)
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
def wrap_async(function):
|
||||||
|
"""
|
||||||
|
Wraps the provided function in an async function.
|
||||||
|
"""
|
||||||
|
return functools.partial(async_call, function)
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
def deprecated(msg: str):
|
||||||
|
"""
|
||||||
|
Throw deprecation warning before execution.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def wrapper(function):
|
||||||
|
@functools.wraps(function)
|
||||||
|
def inner(*args, **kwargs):
|
||||||
|
warnings.warn(msg, DeprecationWarning, stacklevel=2)
|
||||||
|
return function(*args, **kwargs)
|
||||||
|
|
||||||
|
return inner
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
def experimental(msg: str):
|
||||||
|
"""
|
||||||
|
Throws a future warning before execution.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def wrapper(function):
|
||||||
|
@functools.wraps(function)
|
||||||
|
def inner(*args, **kwargs):
|
||||||
|
warnings.warn(msg, FutureWarning, stacklevel=2)
|
||||||
|
return function(*args, **kwargs)
|
||||||
|
|
||||||
|
return inner
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
def composite_listener(cls):
|
def composite_listener(cls):
|
||||||
"""
|
"""
|
||||||
@@ -113,21 +156,23 @@ class EventWatcher:
|
|||||||
```
|
```
|
||||||
'''
|
'''
|
||||||
|
|
||||||
handlers: List[Tuple[EventEmitter, str, Callable[..., Any]]]
|
handlers: List[Tuple[pyee.EventEmitter, str, Callable[..., Any]]]
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.handlers = []
|
self.handlers = []
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def on(
|
def on(
|
||||||
self, emitter: EventEmitter, event: str
|
self, emitter: pyee.EventEmitter, event: str
|
||||||
) -> Callable[[_Handler], _Handler]: ...
|
) -> Callable[[_Handler], _Handler]: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def on(self, emitter: EventEmitter, event: str, handler: _Handler) -> _Handler: ...
|
def on(
|
||||||
|
self, emitter: pyee.EventEmitter, event: str, handler: _Handler
|
||||||
|
) -> _Handler: ...
|
||||||
|
|
||||||
def on(
|
def on(
|
||||||
self, emitter: EventEmitter, event: str, handler: Optional[_Handler] = None
|
self, emitter: pyee.EventEmitter, event: str, handler: Optional[_Handler] = None
|
||||||
) -> Union[_Handler, Callable[[_Handler], _Handler]]:
|
) -> Union[_Handler, Callable[[_Handler], _Handler]]:
|
||||||
'''Watch an event until the context is closed.
|
'''Watch an event until the context is closed.
|
||||||
|
|
||||||
@@ -147,16 +192,16 @@ class EventWatcher:
|
|||||||
|
|
||||||
@overload
|
@overload
|
||||||
def once(
|
def once(
|
||||||
self, emitter: EventEmitter, event: str
|
self, emitter: pyee.EventEmitter, event: str
|
||||||
) -> Callable[[_Handler], _Handler]: ...
|
) -> Callable[[_Handler], _Handler]: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def once(
|
def once(
|
||||||
self, emitter: EventEmitter, event: str, handler: _Handler
|
self, emitter: pyee.EventEmitter, event: str, handler: _Handler
|
||||||
) -> _Handler: ...
|
) -> _Handler: ...
|
||||||
|
|
||||||
def once(
|
def once(
|
||||||
self, emitter: EventEmitter, event: str, handler: Optional[_Handler] = None
|
self, emitter: pyee.EventEmitter, event: str, handler: Optional[_Handler] = None
|
||||||
) -> Union[_Handler, Callable[[_Handler], _Handler]]:
|
) -> Union[_Handler, Callable[[_Handler], _Handler]]:
|
||||||
'''Watch an event for once.
|
'''Watch an event for once.
|
||||||
|
|
||||||
@@ -184,38 +229,48 @@ class EventWatcher:
|
|||||||
_T = TypeVar('_T')
|
_T = TypeVar('_T')
|
||||||
|
|
||||||
|
|
||||||
class AbortableEventEmitter(EventEmitter):
|
def cancel_on_event(
|
||||||
def abort_on(self, event: str, awaitable: Awaitable[_T]) -> Awaitable[_T]:
|
emitter: pyee.EventEmitter, event: str, awaitable: Awaitable[_T]
|
||||||
"""
|
) -> Awaitable[_T]:
|
||||||
Set a coroutine or future to abort when an event occur.
|
"""Set a coroutine or future to cancel when an event occur."""
|
||||||
"""
|
future = asyncio.ensure_future(awaitable)
|
||||||
future = asyncio.ensure_future(awaitable)
|
if future.done():
|
||||||
if future.done():
|
|
||||||
return future
|
|
||||||
|
|
||||||
def on_event(*_):
|
|
||||||
if future.done():
|
|
||||||
return
|
|
||||||
msg = f'abort: {event} event occurred.'
|
|
||||||
if isinstance(future, asyncio.Task):
|
|
||||||
# python < 3.9 does not support passing a message on `Task.cancel`
|
|
||||||
if sys.version_info < (3, 9, 0):
|
|
||||||
future.cancel()
|
|
||||||
else:
|
|
||||||
future.cancel(msg)
|
|
||||||
else:
|
|
||||||
future.set_exception(asyncio.CancelledError(msg))
|
|
||||||
|
|
||||||
def on_done(_):
|
|
||||||
self.remove_listener(event, on_event)
|
|
||||||
|
|
||||||
self.on(event, on_event)
|
|
||||||
future.add_done_callback(on_done)
|
|
||||||
return future
|
return future
|
||||||
|
|
||||||
|
def on_event(*args, **kwargs) -> None:
|
||||||
|
del args, kwargs
|
||||||
|
if future.done():
|
||||||
|
return
|
||||||
|
msg = f'abort: {event} event occurred.'
|
||||||
|
if isinstance(future, asyncio.Task):
|
||||||
|
# python < 3.9 does not support passing a message on `Task.cancel`
|
||||||
|
if sys.version_info < (3, 9, 0):
|
||||||
|
future.cancel()
|
||||||
|
else:
|
||||||
|
future.cancel(msg)
|
||||||
|
else:
|
||||||
|
future.set_exception(asyncio.CancelledError(msg))
|
||||||
|
|
||||||
|
def on_done(_):
|
||||||
|
emitter.remove_listener(event, on_event)
|
||||||
|
|
||||||
|
emitter.on(event, on_event)
|
||||||
|
future.add_done_callback(on_done)
|
||||||
|
return future
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class CompositeEventEmitter(AbortableEventEmitter):
|
class EventEmitter(pyee.asyncio.AsyncIOEventEmitter):
|
||||||
|
"""A Base EventEmitter for Bumble."""
|
||||||
|
|
||||||
|
@deprecated("Use `cancel_on_event` instead.")
|
||||||
|
def abort_on(self, event: str, awaitable: Awaitable[_T]) -> Awaitable[_T]:
|
||||||
|
"""Set a coroutine or future to abort when an event occur."""
|
||||||
|
return cancel_on_event(self, event, awaitable)
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
class CompositeEventEmitter(EventEmitter):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._listener = None
|
self._listener = None
|
||||||
@@ -430,48 +485,6 @@ async def async_call(function, *args, **kwargs):
|
|||||||
return function(*args, **kwargs)
|
return function(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
def wrap_async(function):
|
|
||||||
"""
|
|
||||||
Wraps the provided function in an async function.
|
|
||||||
"""
|
|
||||||
return functools.partial(async_call, function)
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
def deprecated(msg: str):
|
|
||||||
"""
|
|
||||||
Throw deprecation warning before execution.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def wrapper(function):
|
|
||||||
@functools.wraps(function)
|
|
||||||
def inner(*args, **kwargs):
|
|
||||||
warnings.warn(msg, DeprecationWarning, stacklevel=2)
|
|
||||||
return function(*args, **kwargs)
|
|
||||||
|
|
||||||
return inner
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
def experimental(msg: str):
|
|
||||||
"""
|
|
||||||
Throws a future warning before execution.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def wrapper(function):
|
|
||||||
@functools.wraps(function)
|
|
||||||
def inner(*args, **kwargs):
|
|
||||||
warnings.warn(msg, FutureWarning, stacklevel=2)
|
|
||||||
return function(*args, **kwargs)
|
|
||||||
|
|
||||||
return inner
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class OpenIntEnum(enum.IntEnum):
|
class OpenIntEnum(enum.IntEnum):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -19,12 +19,8 @@ import asyncio
|
|||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
from bumble.device import (
|
from bumble import utils
|
||||||
Device,
|
from bumble.device import Device, Connection
|
||||||
Connection,
|
|
||||||
AdvertisingParameters,
|
|
||||||
AdvertisingEventProperties,
|
|
||||||
)
|
|
||||||
from bumble.hci import (
|
from bumble.hci import (
|
||||||
OwnAddressType,
|
OwnAddressType,
|
||||||
)
|
)
|
||||||
@@ -79,7 +75,9 @@ async def main() -> None:
|
|||||||
def on_cis_request(
|
def on_cis_request(
|
||||||
connection: Connection, cis_handle: int, _cig_id: int, _cis_id: int
|
connection: Connection, cis_handle: int, _cig_id: int, _cis_id: int
|
||||||
):
|
):
|
||||||
connection.abort_on('disconnection', devices[0].accept_cis_request(cis_handle))
|
utils.cancel_on_event(
|
||||||
|
connection, 'disconnection', devices[0].accept_cis_request(cis_handle)
|
||||||
|
)
|
||||||
|
|
||||||
devices[0].on('cis_request', on_cis_request)
|
devices[0].on('cis_request', on_cis_request)
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import websockets
|
|||||||
import functools
|
import functools
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from bumble import utils
|
||||||
from bumble import rfcomm
|
from bumble import rfcomm
|
||||||
from bumble import hci
|
from bumble import hci
|
||||||
from bumble.device import Device, Connection
|
from bumble.device import Device, Connection
|
||||||
@@ -60,7 +61,8 @@ def on_dlc(dlc: rfcomm.DLC, configuration: hfp.HfConfiguration):
|
|||||||
else:
|
else:
|
||||||
raise RuntimeError("unknown active codec")
|
raise RuntimeError("unknown active codec")
|
||||||
|
|
||||||
connection.abort_on(
|
utils.cancel_on_event(
|
||||||
|
connection,
|
||||||
'disconnection',
|
'disconnection',
|
||||||
connection.device.send_command(
|
connection.device.send_command(
|
||||||
hci.HCI_Enhanced_Accept_Synchronous_Connection_Request_Command(
|
hci.HCI_Enhanced_Accept_Synchronous_Connection_Request_Command(
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import os
|
|||||||
import websockets
|
import websockets
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from bumble import utils
|
||||||
from bumble.core import AdvertisingData
|
from bumble.core import AdvertisingData
|
||||||
from bumble.device import (
|
from bumble.device import (
|
||||||
Device,
|
Device,
|
||||||
@@ -169,7 +170,7 @@ async def main() -> None:
|
|||||||
mcp.on('track_position', on_track_position)
|
mcp.on('track_position', on_track_position)
|
||||||
await mcp.subscribe_characteristics()
|
await mcp.subscribe_characteristics()
|
||||||
|
|
||||||
connection.abort_on('disconnection', on_connection_async())
|
utils.cancel_on_event(connection, 'disconnection', on_connection_async())
|
||||||
|
|
||||||
device.on('connection', on_connection)
|
device.on('connection', on_connection)
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ from bumble.hci import (
|
|||||||
HCI_Error,
|
HCI_Error,
|
||||||
HCI_Packet,
|
HCI_Packet,
|
||||||
)
|
)
|
||||||
|
from bumble import utils
|
||||||
from bumble import gatt
|
from bumble import gatt
|
||||||
|
|
||||||
from .test_utils import TwoDevices, async_barrier
|
from .test_utils import TwoDevices, async_barrier
|
||||||
@@ -251,7 +252,7 @@ async def test_device_connect_parallel():
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_flush():
|
async def test_flush():
|
||||||
d0 = Device(host=Host(None, None))
|
d0 = Device(host=Host(None, None))
|
||||||
task = d0.abort_on('flush', asyncio.sleep(10000))
|
task = utils.cancel_on_event(d0, 'flush', asyncio.sleep(10000))
|
||||||
await d0.host.flush()
|
await d0.host.flush()
|
||||||
try:
|
try:
|
||||||
await task
|
await task
|
||||||
@@ -482,8 +483,8 @@ async def test_cis():
|
|||||||
_cig_id: int,
|
_cig_id: int,
|
||||||
_cis_id: int,
|
_cis_id: int,
|
||||||
):
|
):
|
||||||
acl_connection.abort_on(
|
utils.cancel_on_event(
|
||||||
'disconnection', devices[1].accept_cis_request(cis_handle)
|
acl_connection, 'disconnection', devices[1].accept_cis_request(cis_handle)
|
||||||
)
|
)
|
||||||
peripheral_cis_futures[cis_handle] = asyncio.get_running_loop().create_future()
|
peripheral_cis_futures[cis_handle] = asyncio.get_running_loop().create_future()
|
||||||
|
|
||||||
|
|||||||
@@ -15,14 +15,14 @@
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Imports
|
# Imports
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
import pyee
|
|
||||||
|
|
||||||
|
from bumble import utils
|
||||||
from bumble.device import Device
|
from bumble.device import Device
|
||||||
from bumble.hci import HCI_Reset_Command
|
from bumble.hci import HCI_Reset_Command
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class Scanner(pyee.EventEmitter):
|
class Scanner(utils.EventEmitter):
|
||||||
"""
|
"""
|
||||||
Scanner web app
|
Scanner web app
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user