forked from auracaster/bumble_mirror
Merge pull request #650 from google/gbg/gatt-adapter-typing
new GATT adapter classes with proper typing support
This commit is contained in:
@@ -24,16 +24,13 @@ import struct
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
from bumble import gatt
|
||||
from bumble.device import Connection
|
||||
from bumble.att import ATT_Error
|
||||
from bumble.gatt import (
|
||||
Attribute,
|
||||
Characteristic,
|
||||
SerializableCharacteristicAdapter,
|
||||
PackedCharacteristicAdapter,
|
||||
TemplateService,
|
||||
CharacteristicValue,
|
||||
UTF8CharacteristicAdapter,
|
||||
GATT_AUDIO_INPUT_CONTROL_SERVICE,
|
||||
GATT_AUDIO_INPUT_STATE_CHARACTERISTIC,
|
||||
GATT_GAIN_SETTINGS_ATTRIBUTE_CHARACTERISTIC,
|
||||
@@ -42,6 +39,14 @@ from bumble.gatt import (
|
||||
GATT_AUDIO_INPUT_CONTROL_POINT_CHARACTERISTIC,
|
||||
GATT_AUDIO_INPUT_DESCRIPTION_CHARACTERISTIC,
|
||||
)
|
||||
from bumble.gatt_adapters import (
|
||||
CharacteristicProxy,
|
||||
PackedCharacteristicProxyAdapter,
|
||||
SerializableCharacteristicAdapter,
|
||||
SerializableCharacteristicProxyAdapter,
|
||||
UTF8CharacteristicAdapter,
|
||||
UTF8CharacteristicProxyAdapter,
|
||||
)
|
||||
from bumble.gatt_client import ProfileServiceProxy, ServiceProxy
|
||||
from bumble.utils import OpenIntEnum
|
||||
|
||||
@@ -124,7 +129,7 @@ class AudioInputState:
|
||||
mute: Mute = Mute.NOT_MUTED
|
||||
gain_mode: GainMode = GainMode.MANUAL
|
||||
change_counter: int = 0
|
||||
attribute_value: Optional[CharacteristicValue] = None
|
||||
attribute: Optional[Attribute] = None
|
||||
|
||||
def __bytes__(self) -> bytes:
|
||||
return bytes(
|
||||
@@ -151,10 +156,8 @@ class AudioInputState:
|
||||
self.change_counter = (self.change_counter + 1) % (CHANGE_COUNTER_MAX_VALUE + 1)
|
||||
|
||||
async def notify_subscribers_via_connection(self, connection: Connection) -> None:
|
||||
assert self.attribute_value is not None
|
||||
await connection.device.notify_subscribers(
|
||||
attribute=self.attribute_value, value=bytes(self)
|
||||
)
|
||||
assert self.attribute is not None
|
||||
await connection.device.notify_subscribers(attribute=self.attribute)
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -315,24 +318,28 @@ class AudioInputDescription:
|
||||
'''
|
||||
|
||||
audio_input_description: str = "Bluetooth"
|
||||
attribute_value: Optional[CharacteristicValue] = None
|
||||
attribute: Optional[Attribute] = None
|
||||
|
||||
def on_read(self, _connection: Optional[Connection]) -> str:
|
||||
return self.audio_input_description
|
||||
|
||||
async def on_write(self, connection: Optional[Connection], value: str) -> None:
|
||||
assert connection
|
||||
assert self.attribute_value
|
||||
assert self.attribute
|
||||
|
||||
self.audio_input_description = value
|
||||
await connection.device.notify_subscribers(
|
||||
attribute=self.attribute_value, value=value
|
||||
)
|
||||
await connection.device.notify_subscribers(attribute=self.attribute)
|
||||
|
||||
|
||||
class AICSService(TemplateService):
|
||||
UUID = GATT_AUDIO_INPUT_CONTROL_SERVICE
|
||||
|
||||
audio_input_state_characteristic: Characteristic[AudioInputState]
|
||||
audio_input_type_characteristic: Characteristic[bytes]
|
||||
audio_input_status_characteristic: Characteristic[bytes]
|
||||
audio_input_control_point_characteristic: Characteristic[bytes]
|
||||
gain_settings_properties_characteristic: Characteristic[GainSettingsProperties]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
audio_input_state: Optional[AudioInputState] = None,
|
||||
@@ -374,9 +381,7 @@ class AICSService(TemplateService):
|
||||
),
|
||||
AudioInputState,
|
||||
)
|
||||
self.audio_input_state.attribute_value = (
|
||||
self.audio_input_state_characteristic.value
|
||||
)
|
||||
self.audio_input_state.attribute = self.audio_input_state_characteristic
|
||||
|
||||
self.gain_settings_properties_characteristic = (
|
||||
SerializableCharacteristicAdapter(
|
||||
@@ -425,8 +430,8 @@ class AICSService(TemplateService):
|
||||
),
|
||||
)
|
||||
)
|
||||
self.audio_input_description.attribute_value = (
|
||||
self.audio_input_control_point_characteristic.value
|
||||
self.audio_input_description.attribute = (
|
||||
self.audio_input_control_point_characteristic
|
||||
)
|
||||
|
||||
super().__init__(
|
||||
@@ -448,24 +453,29 @@ class AICSService(TemplateService):
|
||||
class AICSServiceProxy(ProfileServiceProxy):
|
||||
SERVICE_CLASS = AICSService
|
||||
|
||||
audio_input_state: CharacteristicProxy[AudioInputState]
|
||||
gain_settings_properties: CharacteristicProxy[GainSettingsProperties]
|
||||
audio_input_status: CharacteristicProxy[int]
|
||||
audio_input_control_point: CharacteristicProxy[bytes]
|
||||
|
||||
def __init__(self, service_proxy: ServiceProxy) -> None:
|
||||
self.service_proxy = service_proxy
|
||||
|
||||
self.audio_input_state = SerializableCharacteristicAdapter(
|
||||
self.audio_input_state = SerializableCharacteristicProxyAdapter(
|
||||
service_proxy.get_required_characteristic_by_uuid(
|
||||
GATT_AUDIO_INPUT_STATE_CHARACTERISTIC
|
||||
),
|
||||
AudioInputState,
|
||||
)
|
||||
|
||||
self.gain_settings_properties = SerializableCharacteristicAdapter(
|
||||
self.gain_settings_properties = SerializableCharacteristicProxyAdapter(
|
||||
service_proxy.get_required_characteristic_by_uuid(
|
||||
GATT_GAIN_SETTINGS_ATTRIBUTE_CHARACTERISTIC
|
||||
),
|
||||
GainSettingsProperties,
|
||||
)
|
||||
|
||||
self.audio_input_status = PackedCharacteristicAdapter(
|
||||
self.audio_input_status = PackedCharacteristicProxyAdapter(
|
||||
service_proxy.get_required_characteristic_by_uuid(
|
||||
GATT_AUDIO_INPUT_STATUS_CHARACTERISTIC
|
||||
),
|
||||
@@ -478,7 +488,7 @@ class AICSServiceProxy(ProfileServiceProxy):
|
||||
)
|
||||
)
|
||||
|
||||
self.audio_input_description = UTF8CharacteristicAdapter(
|
||||
self.audio_input_description = UTF8CharacteristicProxyAdapter(
|
||||
service_proxy.get_required_characteristic_by_uuid(
|
||||
GATT_AUDIO_INPUT_DESCRIPTION_CHARACTERISTIC
|
||||
)
|
||||
|
||||
@@ -134,12 +134,14 @@ class AshaService(gatt.TemplateService):
|
||||
),
|
||||
)
|
||||
|
||||
self.audio_control_point_characteristic = gatt.Characteristic(
|
||||
gatt.GATT_ASHA_AUDIO_CONTROL_POINT_CHARACTERISTIC,
|
||||
gatt.Characteristic.Properties.WRITE
|
||||
| gatt.Characteristic.Properties.WRITE_WITHOUT_RESPONSE,
|
||||
gatt.Characteristic.WRITEABLE,
|
||||
gatt.CharacteristicValue(write=self._on_audio_control_point_write),
|
||||
self.audio_control_point_characteristic: gatt.Characteristic[bytes] = (
|
||||
gatt.Characteristic(
|
||||
gatt.GATT_ASHA_AUDIO_CONTROL_POINT_CHARACTERISTIC,
|
||||
gatt.Characteristic.Properties.WRITE
|
||||
| gatt.Characteristic.Properties.WRITE_WITHOUT_RESPONSE,
|
||||
gatt.Characteristic.WRITEABLE,
|
||||
gatt.CharacteristicValue(write=self._on_audio_control_point_write),
|
||||
)
|
||||
)
|
||||
self.audio_status_characteristic = gatt.Characteristic(
|
||||
gatt.GATT_ASHA_AUDIO_STATUS_CHARACTERISTIC,
|
||||
@@ -147,7 +149,7 @@ class AshaService(gatt.TemplateService):
|
||||
gatt.Characteristic.READABLE,
|
||||
bytes([AudioStatus.OK]),
|
||||
)
|
||||
self.volume_characteristic = gatt.Characteristic(
|
||||
self.volume_characteristic: gatt.Characteristic[bytes] = gatt.Characteristic(
|
||||
gatt.GATT_ASHA_VOLUME_CHARACTERISTIC,
|
||||
gatt.Characteristic.Properties.WRITE_WITHOUT_RESPONSE,
|
||||
gatt.Characteristic.WRITEABLE,
|
||||
@@ -166,13 +168,13 @@ class AshaService(gatt.TemplateService):
|
||||
struct.pack('<H', self.psm),
|
||||
)
|
||||
|
||||
characteristics = [
|
||||
characteristics = (
|
||||
self.read_only_properties_characteristic,
|
||||
self.audio_control_point_characteristic,
|
||||
self.audio_status_characteristic,
|
||||
self.volume_characteristic,
|
||||
self.le_psm_out_characteristic,
|
||||
]
|
||||
)
|
||||
|
||||
super().__init__(characteristics)
|
||||
|
||||
|
||||
@@ -20,11 +20,12 @@ from __future__ import annotations
|
||||
import dataclasses
|
||||
import logging
|
||||
import struct
|
||||
from typing import ClassVar, List, Optional, Sequence
|
||||
from typing import ClassVar, Optional, Sequence
|
||||
|
||||
from bumble import core
|
||||
from bumble import device
|
||||
from bumble import gatt
|
||||
from bumble import gatt_adapters
|
||||
from bumble import gatt_client
|
||||
from bumble import hci
|
||||
from bumble import utils
|
||||
@@ -52,7 +53,7 @@ def encode_subgroups(subgroups: Sequence[SubgroupInfo]) -> bytes:
|
||||
)
|
||||
|
||||
|
||||
def decode_subgroups(data: bytes) -> List[SubgroupInfo]:
|
||||
def decode_subgroups(data: bytes) -> list[SubgroupInfo]:
|
||||
num_subgroups = data[0]
|
||||
offset = 1
|
||||
subgroups = []
|
||||
@@ -273,7 +274,7 @@ class BroadcastReceiveState:
|
||||
pa_sync_state: PeriodicAdvertisingSyncState
|
||||
big_encryption: BigEncryption
|
||||
bad_code: bytes
|
||||
subgroups: List[SubgroupInfo]
|
||||
subgroups: list[SubgroupInfo]
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls, data: bytes) -> BroadcastReceiveState:
|
||||
@@ -354,7 +355,9 @@ class BroadcastAudioScanServiceProxy(gatt_client.ProfileServiceProxy):
|
||||
SERVICE_CLASS = BroadcastAudioScanService
|
||||
|
||||
broadcast_audio_scan_control_point: gatt_client.CharacteristicProxy
|
||||
broadcast_receive_states: List[gatt.DelegatedCharacteristicAdapter]
|
||||
broadcast_receive_states: list[
|
||||
gatt_client.CharacteristicProxy[Optional[BroadcastReceiveState]]
|
||||
]
|
||||
|
||||
def __init__(self, service_proxy: gatt_client.ServiceProxy):
|
||||
self.service_proxy = service_proxy
|
||||
@@ -366,7 +369,7 @@ class BroadcastAudioScanServiceProxy(gatt_client.ProfileServiceProxy):
|
||||
)
|
||||
|
||||
self.broadcast_receive_states = [
|
||||
gatt.DelegatedCharacteristicAdapter(
|
||||
gatt_adapters.DelegatedCharacteristicProxyAdapter(
|
||||
characteristic,
|
||||
decode=lambda x: BroadcastReceiveState.from_bytes(x) if x else None,
|
||||
)
|
||||
|
||||
@@ -16,14 +16,20 @@
|
||||
# -----------------------------------------------------------------------------
|
||||
# Imports
|
||||
# -----------------------------------------------------------------------------
|
||||
from ..gatt_client import ProfileServiceProxy
|
||||
from ..gatt import (
|
||||
from typing import Optional
|
||||
|
||||
from bumble.gatt_client import ProfileServiceProxy
|
||||
from bumble.gatt import (
|
||||
GATT_BATTERY_SERVICE,
|
||||
GATT_BATTERY_LEVEL_CHARACTERISTIC,
|
||||
TemplateService,
|
||||
Characteristic,
|
||||
CharacteristicValue,
|
||||
)
|
||||
from bumble.gatt_client import CharacteristicProxy
|
||||
from bumble.gatt_adapters import (
|
||||
PackedCharacteristicAdapter,
|
||||
PackedCharacteristicProxyAdapter,
|
||||
)
|
||||
|
||||
|
||||
@@ -32,6 +38,8 @@ class BatteryService(TemplateService):
|
||||
UUID = GATT_BATTERY_SERVICE
|
||||
BATTERY_LEVEL_FORMAT = 'B'
|
||||
|
||||
battery_level_characteristic: Characteristic[int]
|
||||
|
||||
def __init__(self, read_battery_level):
|
||||
self.battery_level_characteristic = PackedCharacteristicAdapter(
|
||||
Characteristic(
|
||||
@@ -49,13 +57,15 @@ class BatteryService(TemplateService):
|
||||
class BatteryServiceProxy(ProfileServiceProxy):
|
||||
SERVICE_CLASS = BatteryService
|
||||
|
||||
battery_level: Optional[CharacteristicProxy[int]]
|
||||
|
||||
def __init__(self, service_proxy):
|
||||
self.service_proxy = service_proxy
|
||||
|
||||
if characteristics := service_proxy.get_characteristics_by_uuid(
|
||||
GATT_BATTERY_LEVEL_CHARACTERISTIC
|
||||
):
|
||||
self.battery_level = PackedCharacteristicAdapter(
|
||||
self.battery_level = PackedCharacteristicProxyAdapter(
|
||||
characteristics[0], pack_format=BatteryService.BATTERY_LEVEL_FORMAT
|
||||
)
|
||||
else:
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
import struct
|
||||
from typing import Optional, Tuple
|
||||
|
||||
from bumble.gatt_client import ServiceProxy, ProfileServiceProxy, CharacteristicProxy
|
||||
from bumble.gatt import (
|
||||
GATT_DEVICE_INFORMATION_SERVICE,
|
||||
GATT_FIRMWARE_REVISION_STRING_CHARACTERISTIC,
|
||||
@@ -32,9 +31,12 @@ from bumble.gatt import (
|
||||
GATT_REGULATORY_CERTIFICATION_DATA_LIST_CHARACTERISTIC,
|
||||
TemplateService,
|
||||
Characteristic,
|
||||
DelegatedCharacteristicAdapter,
|
||||
UTF8CharacteristicAdapter,
|
||||
)
|
||||
from bumble.gatt_adapters import (
|
||||
DelegatedCharacteristicProxyAdapter,
|
||||
UTF8CharacteristicProxyAdapter,
|
||||
)
|
||||
from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy, ServiceProxy
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -62,7 +64,7 @@ class DeviceInformationService(TemplateService):
|
||||
ieee_regulatory_certification_data_list: Optional[bytes] = None,
|
||||
# TODO: pnp_id
|
||||
):
|
||||
characteristics = [
|
||||
characteristics: list[Characteristic[bytes]] = [
|
||||
Characteristic(
|
||||
uuid,
|
||||
Characteristic.Properties.READ,
|
||||
@@ -107,14 +109,14 @@ class DeviceInformationService(TemplateService):
|
||||
class DeviceInformationServiceProxy(ProfileServiceProxy):
|
||||
SERVICE_CLASS = DeviceInformationService
|
||||
|
||||
manufacturer_name: Optional[UTF8CharacteristicAdapter]
|
||||
model_number: Optional[UTF8CharacteristicAdapter]
|
||||
serial_number: Optional[UTF8CharacteristicAdapter]
|
||||
hardware_revision: Optional[UTF8CharacteristicAdapter]
|
||||
firmware_revision: Optional[UTF8CharacteristicAdapter]
|
||||
software_revision: Optional[UTF8CharacteristicAdapter]
|
||||
system_id: Optional[DelegatedCharacteristicAdapter]
|
||||
ieee_regulatory_certification_data_list: Optional[CharacteristicProxy]
|
||||
manufacturer_name: Optional[CharacteristicProxy[str]]
|
||||
model_number: Optional[CharacteristicProxy[str]]
|
||||
serial_number: Optional[CharacteristicProxy[str]]
|
||||
hardware_revision: Optional[CharacteristicProxy[str]]
|
||||
firmware_revision: Optional[CharacteristicProxy[str]]
|
||||
software_revision: Optional[CharacteristicProxy[str]]
|
||||
system_id: Optional[CharacteristicProxy[tuple[int, int]]]
|
||||
ieee_regulatory_certification_data_list: Optional[CharacteristicProxy[bytes]]
|
||||
|
||||
def __init__(self, service_proxy: ServiceProxy):
|
||||
self.service_proxy = service_proxy
|
||||
@@ -128,7 +130,7 @@ class DeviceInformationServiceProxy(ProfileServiceProxy):
|
||||
('software_revision', GATT_SOFTWARE_REVISION_STRING_CHARACTERISTIC),
|
||||
):
|
||||
if characteristics := service_proxy.get_characteristics_by_uuid(uuid):
|
||||
characteristic = UTF8CharacteristicAdapter(characteristics[0])
|
||||
characteristic = UTF8CharacteristicProxyAdapter(characteristics[0])
|
||||
else:
|
||||
characteristic = None
|
||||
self.__setattr__(field, characteristic)
|
||||
@@ -136,7 +138,7 @@ class DeviceInformationServiceProxy(ProfileServiceProxy):
|
||||
if characteristics := service_proxy.get_characteristics_by_uuid(
|
||||
GATT_SYSTEM_ID_CHARACTERISTIC
|
||||
):
|
||||
self.system_id = DelegatedCharacteristicAdapter(
|
||||
self.system_id = DelegatedCharacteristicProxyAdapter(
|
||||
characteristics[0],
|
||||
encode=lambda v: DeviceInformationService.pack_system_id(*v),
|
||||
decode=DeviceInformationService.unpack_system_id,
|
||||
|
||||
@@ -25,14 +25,15 @@ from bumble.core import Appearance
|
||||
from bumble.gatt import (
|
||||
TemplateService,
|
||||
Characteristic,
|
||||
CharacteristicAdapter,
|
||||
DelegatedCharacteristicAdapter,
|
||||
UTF8CharacteristicAdapter,
|
||||
GATT_GENERIC_ACCESS_SERVICE,
|
||||
GATT_DEVICE_NAME_CHARACTERISTIC,
|
||||
GATT_APPEARANCE_CHARACTERISTIC,
|
||||
)
|
||||
from bumble.gatt_client import ProfileServiceProxy, ServiceProxy
|
||||
from bumble.gatt_adapters import (
|
||||
DelegatedCharacteristicProxyAdapter,
|
||||
UTF8CharacteristicProxyAdapter,
|
||||
)
|
||||
from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy, ServiceProxy
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Logging
|
||||
@@ -49,6 +50,9 @@ logger = logging.getLogger(__name__)
|
||||
class GenericAccessService(TemplateService):
|
||||
UUID = GATT_GENERIC_ACCESS_SERVICE
|
||||
|
||||
device_name_characteristic: Characteristic[bytes]
|
||||
appearance_characteristic: Characteristic[bytes]
|
||||
|
||||
def __init__(
|
||||
self, device_name: str, appearance: Union[Appearance, Tuple[int, int], int] = 0
|
||||
):
|
||||
@@ -84,8 +88,8 @@ class GenericAccessService(TemplateService):
|
||||
class GenericAccessServiceProxy(ProfileServiceProxy):
|
||||
SERVICE_CLASS = GenericAccessService
|
||||
|
||||
device_name: Optional[CharacteristicAdapter]
|
||||
appearance: Optional[DelegatedCharacteristicAdapter]
|
||||
device_name: Optional[CharacteristicProxy[str]]
|
||||
appearance: Optional[CharacteristicProxy[Appearance]]
|
||||
|
||||
def __init__(self, service_proxy: ServiceProxy):
|
||||
self.service_proxy = service_proxy
|
||||
@@ -93,14 +97,14 @@ class GenericAccessServiceProxy(ProfileServiceProxy):
|
||||
if characteristics := service_proxy.get_characteristics_by_uuid(
|
||||
GATT_DEVICE_NAME_CHARACTERISTIC
|
||||
):
|
||||
self.device_name = UTF8CharacteristicAdapter(characteristics[0])
|
||||
self.device_name = UTF8CharacteristicProxyAdapter(characteristics[0])
|
||||
else:
|
||||
self.device_name = None
|
||||
|
||||
if characteristics := service_proxy.get_characteristics_by_uuid(
|
||||
GATT_APPEARANCE_CHARACTERISTIC
|
||||
):
|
||||
self.appearance = DelegatedCharacteristicAdapter(
|
||||
self.appearance = DelegatedCharacteristicProxyAdapter(
|
||||
characteristics[0],
|
||||
decode=lambda value: Appearance.from_int(
|
||||
struct.unpack_from('<H', value, 0)[0],
|
||||
|
||||
@@ -110,6 +110,7 @@ class GenericAttributeProfileService(gatt.TemplateService):
|
||||
gatt.GATT_CHARACTERISTIC_ATTRIBUTE_TYPE,
|
||||
gatt.GATT_CHARACTERISTIC_EXTENDED_PROPERTIES_DESCRIPTOR,
|
||||
):
|
||||
assert isinstance(attribute.value, bytes)
|
||||
return (
|
||||
struct.pack("<H", attribute.handle)
|
||||
+ attribute.type.to_bytes()
|
||||
|
||||
@@ -22,7 +22,6 @@ from typing import Optional
|
||||
|
||||
from bumble.gatt import (
|
||||
TemplateService,
|
||||
DelegatedCharacteristicAdapter,
|
||||
Characteristic,
|
||||
GATT_GAMING_AUDIO_SERVICE,
|
||||
GATT_GMAP_ROLE_CHARACTERISTIC,
|
||||
@@ -31,7 +30,8 @@ from bumble.gatt import (
|
||||
GATT_BGS_FEATURES_CHARACTERISTIC,
|
||||
GATT_BGR_FEATURES_CHARACTERISTIC,
|
||||
)
|
||||
from bumble.gatt_client import ProfileServiceProxy, ServiceProxy
|
||||
from bumble.gatt_adapters import DelegatedCharacteristicProxyAdapter
|
||||
from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy, ServiceProxy
|
||||
from enum import IntFlag
|
||||
|
||||
|
||||
@@ -150,10 +150,15 @@ class GamingAudioService(TemplateService):
|
||||
class GamingAudioServiceProxy(ProfileServiceProxy):
|
||||
SERVICE_CLASS = GamingAudioService
|
||||
|
||||
ugg_features: Optional[CharacteristicProxy[UggFeatures]] = None
|
||||
ugt_features: Optional[CharacteristicProxy[UgtFeatures]] = None
|
||||
bgs_features: Optional[CharacteristicProxy[BgsFeatures]] = None
|
||||
bgr_features: Optional[CharacteristicProxy[BgrFeatures]] = None
|
||||
|
||||
def __init__(self, service_proxy: ServiceProxy) -> None:
|
||||
self.service_proxy = service_proxy
|
||||
|
||||
self.gmap_role = DelegatedCharacteristicAdapter(
|
||||
self.gmap_role = DelegatedCharacteristicProxyAdapter(
|
||||
service_proxy.get_required_characteristic_by_uuid(
|
||||
GATT_GMAP_ROLE_CHARACTERISTIC
|
||||
),
|
||||
@@ -163,31 +168,31 @@ class GamingAudioServiceProxy(ProfileServiceProxy):
|
||||
if characteristics := service_proxy.get_characteristics_by_uuid(
|
||||
GATT_UGG_FEATURES_CHARACTERISTIC
|
||||
):
|
||||
self.ugg_features = DelegatedCharacteristicAdapter(
|
||||
characteristic=characteristics[0],
|
||||
self.ugg_features = DelegatedCharacteristicProxyAdapter(
|
||||
characteristics[0],
|
||||
decode=lambda value: UggFeatures(value[0]),
|
||||
)
|
||||
|
||||
if characteristics := service_proxy.get_characteristics_by_uuid(
|
||||
GATT_UGT_FEATURES_CHARACTERISTIC
|
||||
):
|
||||
self.ugt_features = DelegatedCharacteristicAdapter(
|
||||
characteristic=characteristics[0],
|
||||
self.ugt_features = DelegatedCharacteristicProxyAdapter(
|
||||
characteristics[0],
|
||||
decode=lambda value: UgtFeatures(value[0]),
|
||||
)
|
||||
|
||||
if characteristics := service_proxy.get_characteristics_by_uuid(
|
||||
GATT_BGS_FEATURES_CHARACTERISTIC
|
||||
):
|
||||
self.bgs_features = DelegatedCharacteristicAdapter(
|
||||
characteristic=characteristics[0],
|
||||
self.bgs_features = DelegatedCharacteristicProxyAdapter(
|
||||
characteristics[0],
|
||||
decode=lambda value: BgsFeatures(value[0]),
|
||||
)
|
||||
|
||||
if characteristics := service_proxy.get_characteristics_by_uuid(
|
||||
GATT_BGR_FEATURES_CHARACTERISTIC
|
||||
):
|
||||
self.bgr_features = DelegatedCharacteristicAdapter(
|
||||
characteristic=characteristics[0],
|
||||
self.bgr_features = DelegatedCharacteristicProxyAdapter(
|
||||
characteristics[0],
|
||||
decode=lambda value: BgrFeatures(value[0]),
|
||||
)
|
||||
|
||||
@@ -18,14 +18,15 @@
|
||||
from __future__ import annotations
|
||||
import asyncio
|
||||
import functools
|
||||
from bumble import att, gatt, gatt_client
|
||||
from dataclasses import dataclass, field
|
||||
import logging
|
||||
from typing import Any, Dict, List, Optional, Set, Union
|
||||
|
||||
from bumble import att, gatt, gatt_adapters, gatt_client
|
||||
from bumble.core import InvalidArgumentError, InvalidStateError
|
||||
from bumble.device import Device, Connection
|
||||
from bumble.utils import AsyncRunner, OpenIntEnum
|
||||
from bumble.hci import Address
|
||||
from dataclasses import dataclass, field
|
||||
import logging
|
||||
from typing import Any, Dict, List, Optional, Set, Union
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -631,11 +632,12 @@ class HearingAccessServiceProxy(gatt_client.ProfileServiceProxy):
|
||||
|
||||
hearing_aid_preset_control_point: gatt_client.CharacteristicProxy
|
||||
preset_control_point_indications: asyncio.Queue
|
||||
active_preset_index_notification: asyncio.Queue
|
||||
|
||||
def __init__(self, service_proxy: gatt_client.ServiceProxy) -> None:
|
||||
self.service_proxy = service_proxy
|
||||
|
||||
self.server_features = gatt.PackedCharacteristicAdapter(
|
||||
self.server_features = gatt_adapters.PackedCharacteristicProxyAdapter(
|
||||
service_proxy.get_characteristics_by_uuid(
|
||||
gatt.GATT_HEARING_AID_FEATURES_CHARACTERISTIC
|
||||
)[0],
|
||||
@@ -648,7 +650,7 @@ class HearingAccessServiceProxy(gatt_client.ProfileServiceProxy):
|
||||
)[0]
|
||||
)
|
||||
|
||||
self.active_preset_index = gatt.PackedCharacteristicAdapter(
|
||||
self.active_preset_index = gatt_adapters.PackedCharacteristicProxyAdapter(
|
||||
service_proxy.get_characteristics_by_uuid(
|
||||
gatt.GATT_ACTIVE_PRESET_INDEX_CHARACTERISTIC
|
||||
)[0],
|
||||
|
||||
@@ -16,13 +16,14 @@
|
||||
# -----------------------------------------------------------------------------
|
||||
# Imports
|
||||
# -----------------------------------------------------------------------------
|
||||
from __future__ import annotations
|
||||
from enum import IntEnum
|
||||
import struct
|
||||
from typing import Optional
|
||||
|
||||
from bumble import core
|
||||
from ..gatt_client import ProfileServiceProxy
|
||||
from ..att import ATT_Error
|
||||
from ..gatt import (
|
||||
from bumble.att import ATT_Error
|
||||
from bumble.gatt import (
|
||||
GATT_HEART_RATE_SERVICE,
|
||||
GATT_HEART_RATE_MEASUREMENT_CHARACTERISTIC,
|
||||
GATT_BODY_SENSOR_LOCATION_CHARACTERISTIC,
|
||||
@@ -30,10 +31,13 @@ from ..gatt import (
|
||||
TemplateService,
|
||||
Characteristic,
|
||||
CharacteristicValue,
|
||||
SerializableCharacteristicAdapter,
|
||||
)
|
||||
from bumble.gatt_adapters import (
|
||||
DelegatedCharacteristicAdapter,
|
||||
PackedCharacteristicAdapter,
|
||||
SerializableCharacteristicAdapter,
|
||||
)
|
||||
from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -43,6 +47,10 @@ class HeartRateService(TemplateService):
|
||||
CONTROL_POINT_NOT_SUPPORTED = 0x80
|
||||
RESET_ENERGY_EXPENDED = 0x01
|
||||
|
||||
heart_rate_measurement_characteristic: Characteristic[HeartRateMeasurement]
|
||||
body_sensor_location_characteristic: Characteristic[BodySensorLocation]
|
||||
heart_rate_control_point_characteristic: Characteristic[int]
|
||||
|
||||
class BodySensorLocation(IntEnum):
|
||||
OTHER = 0
|
||||
CHEST = 1
|
||||
@@ -198,6 +206,14 @@ class HeartRateService(TemplateService):
|
||||
class HeartRateServiceProxy(ProfileServiceProxy):
|
||||
SERVICE_CLASS = HeartRateService
|
||||
|
||||
heart_rate_measurement: Optional[
|
||||
CharacteristicProxy[HeartRateService.HeartRateMeasurement]
|
||||
]
|
||||
body_sensor_location: Optional[
|
||||
CharacteristicProxy[HeartRateService.BodySensorLocation]
|
||||
]
|
||||
heart_rate_control_point: Optional[CharacteristicProxy[int]]
|
||||
|
||||
def __init__(self, service_proxy):
|
||||
self.service_proxy = service_proxy
|
||||
|
||||
|
||||
@@ -208,7 +208,7 @@ class MediaControlService(gatt.TemplateService):
|
||||
properties=gatt.Characteristic.Properties.READ
|
||||
| gatt.Characteristic.Properties.NOTIFY,
|
||||
permissions=gatt.Characteristic.Permissions.READ_REQUIRES_ENCRYPTION,
|
||||
value=media_player_name or 'Bumble Player',
|
||||
value=(media_player_name or 'Bumble Player').encode(),
|
||||
)
|
||||
self.track_changed_characteristic = gatt.Characteristic(
|
||||
uuid=gatt.GATT_TRACK_CHANGED_CHARACTERISTIC,
|
||||
@@ -247,14 +247,16 @@ class MediaControlService(gatt.TemplateService):
|
||||
permissions=gatt.Characteristic.Permissions.READ_REQUIRES_ENCRYPTION,
|
||||
value=b'',
|
||||
)
|
||||
self.media_control_point_characteristic = gatt.Characteristic(
|
||||
uuid=gatt.GATT_MEDIA_CONTROL_POINT_CHARACTERISTIC,
|
||||
properties=gatt.Characteristic.Properties.WRITE
|
||||
| gatt.Characteristic.Properties.WRITE_WITHOUT_RESPONSE
|
||||
| gatt.Characteristic.Properties.NOTIFY,
|
||||
permissions=gatt.Characteristic.Permissions.READ_REQUIRES_ENCRYPTION
|
||||
| gatt.Characteristic.Permissions.WRITE_REQUIRES_ENCRYPTION,
|
||||
value=gatt.CharacteristicValue(write=self.on_media_control_point),
|
||||
self.media_control_point_characteristic: gatt.Characteristic[bytes] = (
|
||||
gatt.Characteristic(
|
||||
uuid=gatt.GATT_MEDIA_CONTROL_POINT_CHARACTERISTIC,
|
||||
properties=gatt.Characteristic.Properties.WRITE
|
||||
| gatt.Characteristic.Properties.WRITE_WITHOUT_RESPONSE
|
||||
| gatt.Characteristic.Properties.NOTIFY,
|
||||
permissions=gatt.Characteristic.Permissions.READ_REQUIRES_ENCRYPTION
|
||||
| gatt.Characteristic.Permissions.WRITE_REQUIRES_ENCRYPTION,
|
||||
value=gatt.CharacteristicValue(write=self.on_media_control_point),
|
||||
)
|
||||
)
|
||||
self.media_control_point_opcodes_supported_characteristic = gatt.Characteristic(
|
||||
uuid=gatt.GATT_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED_CHARACTERISTIC,
|
||||
|
||||
@@ -25,6 +25,7 @@ from typing import Optional, Sequence, Union
|
||||
from bumble.profiles.bap import AudioLocation, CodecSpecificCapabilities, ContextType
|
||||
from bumble.profiles import le_audio
|
||||
from bumble import gatt
|
||||
from bumble import gatt_adapters
|
||||
from bumble import gatt_client
|
||||
from bumble import hci
|
||||
|
||||
@@ -185,34 +186,42 @@ class PublishedAudioCapabilitiesService(gatt.TemplateService):
|
||||
class PublishedAudioCapabilitiesServiceProxy(gatt_client.ProfileServiceProxy):
|
||||
SERVICE_CLASS = PublishedAudioCapabilitiesService
|
||||
|
||||
sink_pac: Optional[gatt.DelegatedCharacteristicAdapter] = None
|
||||
sink_audio_locations: Optional[gatt.DelegatedCharacteristicAdapter] = None
|
||||
source_pac: Optional[gatt.DelegatedCharacteristicAdapter] = None
|
||||
source_audio_locations: Optional[gatt.DelegatedCharacteristicAdapter] = None
|
||||
available_audio_contexts: gatt.DelegatedCharacteristicAdapter
|
||||
supported_audio_contexts: gatt.DelegatedCharacteristicAdapter
|
||||
sink_pac: Optional[gatt_client.CharacteristicProxy[list[PacRecord]]] = None
|
||||
sink_audio_locations: Optional[gatt_client.CharacteristicProxy[AudioLocation]] = (
|
||||
None
|
||||
)
|
||||
source_pac: Optional[gatt_client.CharacteristicProxy[list[PacRecord]]] = None
|
||||
source_audio_locations: Optional[gatt_client.CharacteristicProxy[AudioLocation]] = (
|
||||
None
|
||||
)
|
||||
available_audio_contexts: gatt_client.CharacteristicProxy[tuple[ContextType, ...]]
|
||||
supported_audio_contexts: gatt_client.CharacteristicProxy[tuple[ContextType, ...]]
|
||||
|
||||
def __init__(self, service_proxy: gatt_client.ServiceProxy):
|
||||
self.service_proxy = service_proxy
|
||||
|
||||
self.available_audio_contexts = gatt.DelegatedCharacteristicAdapter(
|
||||
service_proxy.get_required_characteristic_by_uuid(
|
||||
gatt.GATT_AVAILABLE_AUDIO_CONTEXTS_CHARACTERISTIC
|
||||
),
|
||||
decode=lambda x: tuple(map(ContextType, struct.unpack('<HH', x))),
|
||||
self.available_audio_contexts = (
|
||||
gatt_adapters.DelegatedCharacteristicProxyAdapter(
|
||||
service_proxy.get_required_characteristic_by_uuid(
|
||||
gatt.GATT_AVAILABLE_AUDIO_CONTEXTS_CHARACTERISTIC
|
||||
),
|
||||
decode=lambda x: tuple(map(ContextType, struct.unpack('<HH', x))),
|
||||
)
|
||||
)
|
||||
|
||||
self.supported_audio_contexts = gatt.DelegatedCharacteristicAdapter(
|
||||
service_proxy.get_required_characteristic_by_uuid(
|
||||
gatt.GATT_SUPPORTED_AUDIO_CONTEXTS_CHARACTERISTIC
|
||||
),
|
||||
decode=lambda x: tuple(map(ContextType, struct.unpack('<HH', x))),
|
||||
self.supported_audio_contexts = (
|
||||
gatt_adapters.DelegatedCharacteristicProxyAdapter(
|
||||
service_proxy.get_required_characteristic_by_uuid(
|
||||
gatt.GATT_SUPPORTED_AUDIO_CONTEXTS_CHARACTERISTIC
|
||||
),
|
||||
decode=lambda x: tuple(map(ContextType, struct.unpack('<HH', x))),
|
||||
)
|
||||
)
|
||||
|
||||
if characteristics := service_proxy.get_characteristics_by_uuid(
|
||||
gatt.GATT_SINK_PAC_CHARACTERISTIC
|
||||
):
|
||||
self.sink_pac = gatt.DelegatedCharacteristicAdapter(
|
||||
self.sink_pac = gatt_adapters.DelegatedCharacteristicProxyAdapter(
|
||||
characteristics[0],
|
||||
decode=PacRecord.list_from_bytes,
|
||||
)
|
||||
@@ -220,7 +229,7 @@ class PublishedAudioCapabilitiesServiceProxy(gatt_client.ProfileServiceProxy):
|
||||
if characteristics := service_proxy.get_characteristics_by_uuid(
|
||||
gatt.GATT_SOURCE_PAC_CHARACTERISTIC
|
||||
):
|
||||
self.source_pac = gatt.DelegatedCharacteristicAdapter(
|
||||
self.source_pac = gatt_adapters.DelegatedCharacteristicProxyAdapter(
|
||||
characteristics[0],
|
||||
decode=PacRecord.list_from_bytes,
|
||||
)
|
||||
@@ -228,15 +237,19 @@ class PublishedAudioCapabilitiesServiceProxy(gatt_client.ProfileServiceProxy):
|
||||
if characteristics := service_proxy.get_characteristics_by_uuid(
|
||||
gatt.GATT_SINK_AUDIO_LOCATION_CHARACTERISTIC
|
||||
):
|
||||
self.sink_audio_locations = gatt.DelegatedCharacteristicAdapter(
|
||||
characteristics[0],
|
||||
decode=lambda x: AudioLocation(struct.unpack('<I', x)[0]),
|
||||
self.sink_audio_locations = (
|
||||
gatt_adapters.DelegatedCharacteristicProxyAdapter(
|
||||
characteristics[0],
|
||||
decode=lambda x: AudioLocation(struct.unpack('<I', x)[0]),
|
||||
)
|
||||
)
|
||||
|
||||
if characteristics := service_proxy.get_characteristics_by_uuid(
|
||||
gatt.GATT_SOURCE_AUDIO_LOCATION_CHARACTERISTIC
|
||||
):
|
||||
self.source_audio_locations = gatt.DelegatedCharacteristicAdapter(
|
||||
characteristics[0],
|
||||
decode=lambda x: AudioLocation(struct.unpack('<I', x)[0]),
|
||||
self.source_audio_locations = (
|
||||
gatt_adapters.DelegatedCharacteristicProxyAdapter(
|
||||
characteristics[0],
|
||||
decode=lambda x: AudioLocation(struct.unpack('<I', x)[0]),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -24,11 +24,11 @@ import struct
|
||||
from bumble.gatt import (
|
||||
TemplateService,
|
||||
Characteristic,
|
||||
DelegatedCharacteristicAdapter,
|
||||
GATT_TELEPHONY_AND_MEDIA_AUDIO_SERVICE,
|
||||
GATT_TMAP_ROLE_CHARACTERISTIC,
|
||||
)
|
||||
from bumble.gatt_client import ProfileServiceProxy, ServiceProxy
|
||||
from bumble.gatt_adapters import DelegatedCharacteristicProxyAdapter
|
||||
from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy, ServiceProxy
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -53,6 +53,8 @@ class Role(enum.IntFlag):
|
||||
class TelephonyAndMediaAudioService(TemplateService):
|
||||
UUID = GATT_TELEPHONY_AND_MEDIA_AUDIO_SERVICE
|
||||
|
||||
role_characteristic: Characteristic[bytes]
|
||||
|
||||
def __init__(self, role: Role):
|
||||
self.role_characteristic = Characteristic(
|
||||
GATT_TMAP_ROLE_CHARACTERISTIC,
|
||||
@@ -68,12 +70,12 @@ class TelephonyAndMediaAudioService(TemplateService):
|
||||
class TelephonyAndMediaAudioServiceProxy(ProfileServiceProxy):
|
||||
SERVICE_CLASS = TelephonyAndMediaAudioService
|
||||
|
||||
role: DelegatedCharacteristicAdapter
|
||||
role: CharacteristicProxy[Role]
|
||||
|
||||
def __init__(self, service_proxy: ServiceProxy):
|
||||
self.service_proxy = service_proxy
|
||||
|
||||
self.role = DelegatedCharacteristicAdapter(
|
||||
self.role = DelegatedCharacteristicProxyAdapter(
|
||||
service_proxy.get_required_characteristic_by_uuid(
|
||||
GATT_TMAP_ROLE_CHARACTERISTIC
|
||||
),
|
||||
|
||||
@@ -25,6 +25,7 @@ from typing import Optional, Sequence
|
||||
from bumble import att
|
||||
from bumble import device
|
||||
from bumble import gatt
|
||||
from bumble import gatt_adapters
|
||||
from bumble import gatt_client
|
||||
|
||||
|
||||
@@ -209,14 +210,14 @@ class VolumeControlService(gatt.TemplateService):
|
||||
class VolumeControlServiceProxy(gatt_client.ProfileServiceProxy):
|
||||
SERVICE_CLASS = VolumeControlService
|
||||
|
||||
volume_control_point: gatt_client.CharacteristicProxy
|
||||
volume_state: gatt.SerializableCharacteristicAdapter
|
||||
volume_flags: gatt.DelegatedCharacteristicAdapter
|
||||
volume_control_point: gatt_client.CharacteristicProxy[bytes]
|
||||
volume_state: gatt_client.CharacteristicProxy[VolumeState]
|
||||
volume_flags: gatt_client.CharacteristicProxy[VolumeFlags]
|
||||
|
||||
def __init__(self, service_proxy: gatt_client.ServiceProxy) -> None:
|
||||
self.service_proxy = service_proxy
|
||||
|
||||
self.volume_state = gatt.SerializableCharacteristicAdapter(
|
||||
self.volume_state = gatt_adapters.SerializableCharacteristicProxyAdapter(
|
||||
service_proxy.get_required_characteristic_by_uuid(
|
||||
gatt.GATT_VOLUME_STATE_CHARACTERISTIC
|
||||
),
|
||||
@@ -227,7 +228,7 @@ class VolumeControlServiceProxy(gatt_client.ProfileServiceProxy):
|
||||
gatt.GATT_VOLUME_CONTROL_POINT_CHARACTERISTIC
|
||||
)
|
||||
|
||||
self.volume_flags = gatt.DelegatedCharacteristicAdapter(
|
||||
self.volume_flags = gatt_adapters.DelegatedCharacteristicProxyAdapter(
|
||||
service_proxy.get_required_characteristic_by_uuid(
|
||||
gatt.GATT_VOLUME_FLAGS_CHARACTERISTIC
|
||||
),
|
||||
|
||||
@@ -24,17 +24,19 @@ from bumble.device import Connection
|
||||
from bumble.att import ATT_Error
|
||||
from bumble.gatt import (
|
||||
Characteristic,
|
||||
DelegatedCharacteristicAdapter,
|
||||
TemplateService,
|
||||
CharacteristicValue,
|
||||
SerializableCharacteristicAdapter,
|
||||
UTF8CharacteristicAdapter,
|
||||
GATT_VOLUME_OFFSET_CONTROL_SERVICE,
|
||||
GATT_VOLUME_OFFSET_STATE_CHARACTERISTIC,
|
||||
GATT_AUDIO_LOCATION_CHARACTERISTIC,
|
||||
GATT_VOLUME_OFFSET_CONTROL_POINT_CHARACTERISTIC,
|
||||
GATT_AUDIO_OUTPUT_DESCRIPTION_CHARACTERISTIC,
|
||||
)
|
||||
from bumble.gatt_adapters import (
|
||||
DelegatedCharacteristicProxyAdapter,
|
||||
SerializableCharacteristicProxyAdapter,
|
||||
UTF8CharacteristicProxyAdapter,
|
||||
)
|
||||
from bumble.gatt_client import ProfileServiceProxy, ServiceProxy
|
||||
from bumble.utils import OpenIntEnum
|
||||
from bumble.profiles.bap import AudioLocation
|
||||
@@ -67,7 +69,7 @@ class ErrorCode(OpenIntEnum):
|
||||
class VolumeOffsetState:
|
||||
volume_offset: int = 0
|
||||
change_counter: int = 0
|
||||
attribute_value: Optional[CharacteristicValue] = None
|
||||
attribute: Optional[Characteristic] = None
|
||||
|
||||
def __bytes__(self) -> bytes:
|
||||
return struct.pack('<hB', self.volume_offset, self.change_counter)
|
||||
@@ -81,8 +83,8 @@ class VolumeOffsetState:
|
||||
self.change_counter = (self.change_counter + 1) % (CHANGE_COUNTER_MAX_VALUE + 1)
|
||||
|
||||
async def notify_subscribers_via_connection(self, connection: Connection) -> None:
|
||||
assert self.attribute_value is not None
|
||||
await connection.device.notify_subscribers(attribute=self.attribute_value)
|
||||
assert self.attribute is not None
|
||||
await connection.device.notify_subscribers(attribute=self.attribute)
|
||||
|
||||
def on_read(self, _connection: Optional[Connection]) -> bytes:
|
||||
return bytes(self)
|
||||
@@ -91,7 +93,7 @@ class VolumeOffsetState:
|
||||
@dataclass
|
||||
class VocsAudioLocation:
|
||||
audio_location: AudioLocation = AudioLocation.NOT_ALLOWED
|
||||
attribute_value: Optional[CharacteristicValue] = None
|
||||
attribute: Optional[Characteristic] = None
|
||||
|
||||
def __bytes__(self) -> bytes:
|
||||
return struct.pack('<I', self.audio_location)
|
||||
@@ -106,10 +108,10 @@ class VocsAudioLocation:
|
||||
|
||||
async def on_write(self, connection: Optional[Connection], value: bytes) -> None:
|
||||
assert connection
|
||||
assert self.attribute_value
|
||||
assert self.attribute
|
||||
|
||||
self.audio_location = AudioLocation(int.from_bytes(value, 'little'))
|
||||
await connection.device.notify_subscribers(attribute=self.attribute_value)
|
||||
await connection.device.notify_subscribers(attribute=self.attribute)
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -148,7 +150,7 @@ class VolumeOffsetControlPoint:
|
||||
@dataclass
|
||||
class AudioOutputDescription:
|
||||
audio_output_description: str = ''
|
||||
attribute_value: Optional[CharacteristicValue] = None
|
||||
attribute: Optional[Characteristic] = None
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls, data: bytes):
|
||||
@@ -162,10 +164,10 @@ class AudioOutputDescription:
|
||||
|
||||
async def on_write(self, connection: Optional[Connection], value: bytes) -> None:
|
||||
assert connection
|
||||
assert self.attribute_value
|
||||
assert self.attribute
|
||||
|
||||
self.audio_output_description = value.decode('utf-8')
|
||||
await connection.device.notify_subscribers(attribute=self.attribute_value)
|
||||
await connection.device.notify_subscribers(attribute=self.attribute)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -197,7 +199,7 @@ class VolumeOffsetControlService(TemplateService):
|
||||
VolumeOffsetControlPoint(self.volume_offset_state)
|
||||
)
|
||||
|
||||
self.volume_offset_state_characteristic = Characteristic(
|
||||
self.volume_offset_state_characteristic: Characteristic[bytes] = Characteristic(
|
||||
uuid=GATT_VOLUME_OFFSET_STATE_CHARACTERISTIC,
|
||||
properties=(
|
||||
Characteristic.Properties.READ | Characteristic.Properties.NOTIFY
|
||||
@@ -206,7 +208,7 @@ class VolumeOffsetControlService(TemplateService):
|
||||
value=CharacteristicValue(read=self.volume_offset_state.on_read),
|
||||
)
|
||||
|
||||
self.audio_location_characteristic = Characteristic(
|
||||
self.audio_location_characteristic: Characteristic[bytes] = Characteristic(
|
||||
uuid=GATT_AUDIO_LOCATION_CHARACTERISTIC,
|
||||
properties=(
|
||||
Characteristic.Properties.READ
|
||||
@@ -222,33 +224,39 @@ class VolumeOffsetControlService(TemplateService):
|
||||
write=self.audio_location.on_write,
|
||||
),
|
||||
)
|
||||
self.audio_location.attribute_value = self.audio_location_characteristic.value
|
||||
self.audio_location.attribute = self.audio_location_characteristic
|
||||
|
||||
self.volume_offset_control_point_characteristic = Characteristic(
|
||||
uuid=GATT_VOLUME_OFFSET_CONTROL_POINT_CHARACTERISTIC,
|
||||
properties=Characteristic.Properties.WRITE,
|
||||
permissions=Characteristic.Permissions.WRITE_REQUIRES_ENCRYPTION,
|
||||
value=CharacteristicValue(write=self.volume_offset_control_point.on_write),
|
||||
self.volume_offset_control_point_characteristic: Characteristic[bytes] = (
|
||||
Characteristic(
|
||||
uuid=GATT_VOLUME_OFFSET_CONTROL_POINT_CHARACTERISTIC,
|
||||
properties=Characteristic.Properties.WRITE,
|
||||
permissions=Characteristic.Permissions.WRITE_REQUIRES_ENCRYPTION,
|
||||
value=CharacteristicValue(
|
||||
write=self.volume_offset_control_point.on_write
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
self.audio_output_description_characteristic = Characteristic(
|
||||
uuid=GATT_AUDIO_OUTPUT_DESCRIPTION_CHARACTERISTIC,
|
||||
properties=(
|
||||
Characteristic.Properties.READ
|
||||
| Characteristic.Properties.NOTIFY
|
||||
| Characteristic.Properties.WRITE_WITHOUT_RESPONSE
|
||||
),
|
||||
permissions=(
|
||||
Characteristic.Permissions.READ_REQUIRES_ENCRYPTION
|
||||
| Characteristic.Permissions.WRITE_REQUIRES_ENCRYPTION
|
||||
),
|
||||
value=CharacteristicValue(
|
||||
read=self.audio_output_description.on_read,
|
||||
write=self.audio_output_description.on_write,
|
||||
),
|
||||
self.audio_output_description_characteristic: Characteristic[bytes] = (
|
||||
Characteristic(
|
||||
uuid=GATT_AUDIO_OUTPUT_DESCRIPTION_CHARACTERISTIC,
|
||||
properties=(
|
||||
Characteristic.Properties.READ
|
||||
| Characteristic.Properties.NOTIFY
|
||||
| Characteristic.Properties.WRITE_WITHOUT_RESPONSE
|
||||
),
|
||||
permissions=(
|
||||
Characteristic.Permissions.READ_REQUIRES_ENCRYPTION
|
||||
| Characteristic.Permissions.WRITE_REQUIRES_ENCRYPTION
|
||||
),
|
||||
value=CharacteristicValue(
|
||||
read=self.audio_output_description.on_read,
|
||||
write=self.audio_output_description.on_write,
|
||||
),
|
||||
)
|
||||
)
|
||||
self.audio_output_description.attribute_value = (
|
||||
self.audio_output_description_characteristic.value
|
||||
self.audio_output_description.attribute = (
|
||||
self.audio_output_description_characteristic
|
||||
)
|
||||
|
||||
super().__init__(
|
||||
@@ -271,14 +279,14 @@ class VolumeOffsetControlServiceProxy(ProfileServiceProxy):
|
||||
def __init__(self, service_proxy: ServiceProxy) -> None:
|
||||
self.service_proxy = service_proxy
|
||||
|
||||
self.volume_offset_state = SerializableCharacteristicAdapter(
|
||||
self.volume_offset_state = SerializableCharacteristicProxyAdapter(
|
||||
service_proxy.get_required_characteristic_by_uuid(
|
||||
GATT_VOLUME_OFFSET_STATE_CHARACTERISTIC
|
||||
),
|
||||
VolumeOffsetState,
|
||||
)
|
||||
|
||||
self.audio_location = DelegatedCharacteristicAdapter(
|
||||
self.audio_location = DelegatedCharacteristicProxyAdapter(
|
||||
service_proxy.get_required_characteristic_by_uuid(
|
||||
GATT_AUDIO_LOCATION_CHARACTERISTIC
|
||||
),
|
||||
@@ -292,7 +300,7 @@ class VolumeOffsetControlServiceProxy(ProfileServiceProxy):
|
||||
)
|
||||
)
|
||||
|
||||
self.audio_output_description = UTF8CharacteristicAdapter(
|
||||
self.audio_output_description = UTF8CharacteristicProxyAdapter(
|
||||
service_proxy.get_required_characteristic_by_uuid(
|
||||
GATT_AUDIO_OUTPUT_DESCRIPTION_CHARACTERISTIC
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user