bumble 0.0.207__py3-none-any.whl → 0.0.209__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bumble/_version.py +9 -4
- bumble/apps/auracast.py +29 -35
- bumble/apps/bench.py +13 -10
- bumble/apps/console.py +19 -12
- bumble/apps/gg_bridge.py +1 -1
- bumble/att.py +61 -39
- bumble/controller.py +7 -8
- bumble/core.py +306 -159
- bumble/device.py +127 -82
- bumble/gatt.py +25 -228
- bumble/gatt_adapters.py +374 -0
- bumble/gatt_client.py +38 -31
- bumble/gatt_server.py +5 -5
- bumble/hci.py +76 -71
- bumble/host.py +19 -8
- bumble/l2cap.py +2 -2
- bumble/link.py +2 -2
- bumble/pairing.py +5 -5
- bumble/pandora/host.py +19 -23
- bumble/pandora/security.py +2 -3
- bumble/pandora/utils.py +2 -2
- bumble/profiles/aics.py +33 -23
- bumble/profiles/ancs.py +514 -0
- bumble/profiles/ascs.py +2 -1
- bumble/profiles/asha.py +11 -9
- bumble/profiles/bass.py +8 -5
- bumble/profiles/battery_service.py +13 -3
- bumble/profiles/device_information_service.py +16 -14
- bumble/profiles/gap.py +12 -8
- bumble/profiles/gatt_service.py +1 -0
- bumble/profiles/gmap.py +16 -11
- bumble/profiles/hap.py +8 -6
- bumble/profiles/heart_rate_service.py +20 -4
- bumble/profiles/mcp.py +11 -9
- bumble/profiles/pacs.py +37 -24
- bumble/profiles/tmap.py +6 -4
- bumble/profiles/vcs.py +6 -5
- bumble/profiles/vocs.py +49 -41
- bumble/smp.py +3 -3
- bumble/transport/usb.py +1 -3
- bumble/utils.py +10 -0
- {bumble-0.0.207.dist-info → bumble-0.0.209.dist-info}/METADATA +3 -3
- {bumble-0.0.207.dist-info → bumble-0.0.209.dist-info}/RECORD +47 -45
- {bumble-0.0.207.dist-info → bumble-0.0.209.dist-info}/WHEEL +1 -1
- {bumble-0.0.207.dist-info → bumble-0.0.209.dist-info}/LICENSE +0 -0
- {bumble-0.0.207.dist-info → bumble-0.0.209.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.207.dist-info → bumble-0.0.209.dist-info}/top_level.txt +0 -0
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
import struct
|
|
20
20
|
from typing import Optional, Tuple
|
|
21
21
|
|
|
22
|
-
from bumble.gatt_client import ServiceProxy, ProfileServiceProxy, CharacteristicProxy
|
|
23
22
|
from bumble.gatt import (
|
|
24
23
|
GATT_DEVICE_INFORMATION_SERVICE,
|
|
25
24
|
GATT_FIRMWARE_REVISION_STRING_CHARACTERISTIC,
|
|
@@ -32,9 +31,12 @@ from bumble.gatt import (
|
|
|
32
31
|
GATT_REGULATORY_CERTIFICATION_DATA_LIST_CHARACTERISTIC,
|
|
33
32
|
TemplateService,
|
|
34
33
|
Characteristic,
|
|
35
|
-
DelegatedCharacteristicAdapter,
|
|
36
|
-
UTF8CharacteristicAdapter,
|
|
37
34
|
)
|
|
35
|
+
from bumble.gatt_adapters import (
|
|
36
|
+
DelegatedCharacteristicProxyAdapter,
|
|
37
|
+
UTF8CharacteristicProxyAdapter,
|
|
38
|
+
)
|
|
39
|
+
from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy, ServiceProxy
|
|
38
40
|
|
|
39
41
|
|
|
40
42
|
# -----------------------------------------------------------------------------
|
|
@@ -62,7 +64,7 @@ class DeviceInformationService(TemplateService):
|
|
|
62
64
|
ieee_regulatory_certification_data_list: Optional[bytes] = None,
|
|
63
65
|
# TODO: pnp_id
|
|
64
66
|
):
|
|
65
|
-
characteristics = [
|
|
67
|
+
characteristics: list[Characteristic[bytes]] = [
|
|
66
68
|
Characteristic(
|
|
67
69
|
uuid,
|
|
68
70
|
Characteristic.Properties.READ,
|
|
@@ -107,14 +109,14 @@ class DeviceInformationService(TemplateService):
|
|
|
107
109
|
class DeviceInformationServiceProxy(ProfileServiceProxy):
|
|
108
110
|
SERVICE_CLASS = DeviceInformationService
|
|
109
111
|
|
|
110
|
-
manufacturer_name: Optional[
|
|
111
|
-
model_number: Optional[
|
|
112
|
-
serial_number: Optional[
|
|
113
|
-
hardware_revision: Optional[
|
|
114
|
-
firmware_revision: Optional[
|
|
115
|
-
software_revision: Optional[
|
|
116
|
-
system_id: Optional[
|
|
117
|
-
ieee_regulatory_certification_data_list: Optional[CharacteristicProxy]
|
|
112
|
+
manufacturer_name: Optional[CharacteristicProxy[str]]
|
|
113
|
+
model_number: Optional[CharacteristicProxy[str]]
|
|
114
|
+
serial_number: Optional[CharacteristicProxy[str]]
|
|
115
|
+
hardware_revision: Optional[CharacteristicProxy[str]]
|
|
116
|
+
firmware_revision: Optional[CharacteristicProxy[str]]
|
|
117
|
+
software_revision: Optional[CharacteristicProxy[str]]
|
|
118
|
+
system_id: Optional[CharacteristicProxy[tuple[int, int]]]
|
|
119
|
+
ieee_regulatory_certification_data_list: Optional[CharacteristicProxy[bytes]]
|
|
118
120
|
|
|
119
121
|
def __init__(self, service_proxy: ServiceProxy):
|
|
120
122
|
self.service_proxy = service_proxy
|
|
@@ -128,7 +130,7 @@ class DeviceInformationServiceProxy(ProfileServiceProxy):
|
|
|
128
130
|
('software_revision', GATT_SOFTWARE_REVISION_STRING_CHARACTERISTIC),
|
|
129
131
|
):
|
|
130
132
|
if characteristics := service_proxy.get_characteristics_by_uuid(uuid):
|
|
131
|
-
characteristic =
|
|
133
|
+
characteristic = UTF8CharacteristicProxyAdapter(characteristics[0])
|
|
132
134
|
else:
|
|
133
135
|
characteristic = None
|
|
134
136
|
self.__setattr__(field, characteristic)
|
|
@@ -136,7 +138,7 @@ class DeviceInformationServiceProxy(ProfileServiceProxy):
|
|
|
136
138
|
if characteristics := service_proxy.get_characteristics_by_uuid(
|
|
137
139
|
GATT_SYSTEM_ID_CHARACTERISTIC
|
|
138
140
|
):
|
|
139
|
-
self.system_id =
|
|
141
|
+
self.system_id = DelegatedCharacteristicProxyAdapter(
|
|
140
142
|
characteristics[0],
|
|
141
143
|
encode=lambda v: DeviceInformationService.pack_system_id(*v),
|
|
142
144
|
decode=DeviceInformationService.unpack_system_id,
|
bumble/profiles/gap.py
CHANGED
|
@@ -25,14 +25,15 @@ from bumble.core import Appearance
|
|
|
25
25
|
from bumble.gatt import (
|
|
26
26
|
TemplateService,
|
|
27
27
|
Characteristic,
|
|
28
|
-
CharacteristicAdapter,
|
|
29
|
-
DelegatedCharacteristicAdapter,
|
|
30
|
-
UTF8CharacteristicAdapter,
|
|
31
28
|
GATT_GENERIC_ACCESS_SERVICE,
|
|
32
29
|
GATT_DEVICE_NAME_CHARACTERISTIC,
|
|
33
30
|
GATT_APPEARANCE_CHARACTERISTIC,
|
|
34
31
|
)
|
|
35
|
-
from bumble.
|
|
32
|
+
from bumble.gatt_adapters import (
|
|
33
|
+
DelegatedCharacteristicProxyAdapter,
|
|
34
|
+
UTF8CharacteristicProxyAdapter,
|
|
35
|
+
)
|
|
36
|
+
from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy, ServiceProxy
|
|
36
37
|
|
|
37
38
|
# -----------------------------------------------------------------------------
|
|
38
39
|
# Logging
|
|
@@ -49,6 +50,9 @@ logger = logging.getLogger(__name__)
|
|
|
49
50
|
class GenericAccessService(TemplateService):
|
|
50
51
|
UUID = GATT_GENERIC_ACCESS_SERVICE
|
|
51
52
|
|
|
53
|
+
device_name_characteristic: Characteristic[bytes]
|
|
54
|
+
appearance_characteristic: Characteristic[bytes]
|
|
55
|
+
|
|
52
56
|
def __init__(
|
|
53
57
|
self, device_name: str, appearance: Union[Appearance, Tuple[int, int], int] = 0
|
|
54
58
|
):
|
|
@@ -84,8 +88,8 @@ class GenericAccessService(TemplateService):
|
|
|
84
88
|
class GenericAccessServiceProxy(ProfileServiceProxy):
|
|
85
89
|
SERVICE_CLASS = GenericAccessService
|
|
86
90
|
|
|
87
|
-
device_name: Optional[
|
|
88
|
-
appearance: Optional[
|
|
91
|
+
device_name: Optional[CharacteristicProxy[str]]
|
|
92
|
+
appearance: Optional[CharacteristicProxy[Appearance]]
|
|
89
93
|
|
|
90
94
|
def __init__(self, service_proxy: ServiceProxy):
|
|
91
95
|
self.service_proxy = service_proxy
|
|
@@ -93,14 +97,14 @@ class GenericAccessServiceProxy(ProfileServiceProxy):
|
|
|
93
97
|
if characteristics := service_proxy.get_characteristics_by_uuid(
|
|
94
98
|
GATT_DEVICE_NAME_CHARACTERISTIC
|
|
95
99
|
):
|
|
96
|
-
self.device_name =
|
|
100
|
+
self.device_name = UTF8CharacteristicProxyAdapter(characteristics[0])
|
|
97
101
|
else:
|
|
98
102
|
self.device_name = None
|
|
99
103
|
|
|
100
104
|
if characteristics := service_proxy.get_characteristics_by_uuid(
|
|
101
105
|
GATT_APPEARANCE_CHARACTERISTIC
|
|
102
106
|
):
|
|
103
|
-
self.appearance =
|
|
107
|
+
self.appearance = DelegatedCharacteristicProxyAdapter(
|
|
104
108
|
characteristics[0],
|
|
105
109
|
decode=lambda value: Appearance.from_int(
|
|
106
110
|
struct.unpack_from('<H', value, 0)[0],
|
bumble/profiles/gatt_service.py
CHANGED
|
@@ -110,6 +110,7 @@ class GenericAttributeProfileService(gatt.TemplateService):
|
|
|
110
110
|
gatt.GATT_CHARACTERISTIC_ATTRIBUTE_TYPE,
|
|
111
111
|
gatt.GATT_CHARACTERISTIC_EXTENDED_PROPERTIES_DESCRIPTOR,
|
|
112
112
|
):
|
|
113
|
+
assert isinstance(attribute.value, bytes)
|
|
113
114
|
return (
|
|
114
115
|
struct.pack("<H", attribute.handle)
|
|
115
116
|
+ attribute.type.to_bytes()
|
bumble/profiles/gmap.py
CHANGED
|
@@ -22,7 +22,6 @@ from typing import Optional
|
|
|
22
22
|
|
|
23
23
|
from bumble.gatt import (
|
|
24
24
|
TemplateService,
|
|
25
|
-
DelegatedCharacteristicAdapter,
|
|
26
25
|
Characteristic,
|
|
27
26
|
GATT_GAMING_AUDIO_SERVICE,
|
|
28
27
|
GATT_GMAP_ROLE_CHARACTERISTIC,
|
|
@@ -31,7 +30,8 @@ from bumble.gatt import (
|
|
|
31
30
|
GATT_BGS_FEATURES_CHARACTERISTIC,
|
|
32
31
|
GATT_BGR_FEATURES_CHARACTERISTIC,
|
|
33
32
|
)
|
|
34
|
-
from bumble.
|
|
33
|
+
from bumble.gatt_adapters import DelegatedCharacteristicProxyAdapter
|
|
34
|
+
from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy, ServiceProxy
|
|
35
35
|
from enum import IntFlag
|
|
36
36
|
|
|
37
37
|
|
|
@@ -150,10 +150,15 @@ class GamingAudioService(TemplateService):
|
|
|
150
150
|
class GamingAudioServiceProxy(ProfileServiceProxy):
|
|
151
151
|
SERVICE_CLASS = GamingAudioService
|
|
152
152
|
|
|
153
|
+
ugg_features: Optional[CharacteristicProxy[UggFeatures]] = None
|
|
154
|
+
ugt_features: Optional[CharacteristicProxy[UgtFeatures]] = None
|
|
155
|
+
bgs_features: Optional[CharacteristicProxy[BgsFeatures]] = None
|
|
156
|
+
bgr_features: Optional[CharacteristicProxy[BgrFeatures]] = None
|
|
157
|
+
|
|
153
158
|
def __init__(self, service_proxy: ServiceProxy) -> None:
|
|
154
159
|
self.service_proxy = service_proxy
|
|
155
160
|
|
|
156
|
-
self.gmap_role =
|
|
161
|
+
self.gmap_role = DelegatedCharacteristicProxyAdapter(
|
|
157
162
|
service_proxy.get_required_characteristic_by_uuid(
|
|
158
163
|
GATT_GMAP_ROLE_CHARACTERISTIC
|
|
159
164
|
),
|
|
@@ -163,31 +168,31 @@ class GamingAudioServiceProxy(ProfileServiceProxy):
|
|
|
163
168
|
if characteristics := service_proxy.get_characteristics_by_uuid(
|
|
164
169
|
GATT_UGG_FEATURES_CHARACTERISTIC
|
|
165
170
|
):
|
|
166
|
-
self.ugg_features =
|
|
167
|
-
|
|
171
|
+
self.ugg_features = DelegatedCharacteristicProxyAdapter(
|
|
172
|
+
characteristics[0],
|
|
168
173
|
decode=lambda value: UggFeatures(value[0]),
|
|
169
174
|
)
|
|
170
175
|
|
|
171
176
|
if characteristics := service_proxy.get_characteristics_by_uuid(
|
|
172
177
|
GATT_UGT_FEATURES_CHARACTERISTIC
|
|
173
178
|
):
|
|
174
|
-
self.ugt_features =
|
|
175
|
-
|
|
179
|
+
self.ugt_features = DelegatedCharacteristicProxyAdapter(
|
|
180
|
+
characteristics[0],
|
|
176
181
|
decode=lambda value: UgtFeatures(value[0]),
|
|
177
182
|
)
|
|
178
183
|
|
|
179
184
|
if characteristics := service_proxy.get_characteristics_by_uuid(
|
|
180
185
|
GATT_BGS_FEATURES_CHARACTERISTIC
|
|
181
186
|
):
|
|
182
|
-
self.bgs_features =
|
|
183
|
-
|
|
187
|
+
self.bgs_features = DelegatedCharacteristicProxyAdapter(
|
|
188
|
+
characteristics[0],
|
|
184
189
|
decode=lambda value: BgsFeatures(value[0]),
|
|
185
190
|
)
|
|
186
191
|
|
|
187
192
|
if characteristics := service_proxy.get_characteristics_by_uuid(
|
|
188
193
|
GATT_BGR_FEATURES_CHARACTERISTIC
|
|
189
194
|
):
|
|
190
|
-
self.bgr_features =
|
|
191
|
-
|
|
195
|
+
self.bgr_features = DelegatedCharacteristicProxyAdapter(
|
|
196
|
+
characteristics[0],
|
|
192
197
|
decode=lambda value: BgrFeatures(value[0]),
|
|
193
198
|
)
|
bumble/profiles/hap.py
CHANGED
|
@@ -18,14 +18,15 @@
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
import asyncio
|
|
20
20
|
import functools
|
|
21
|
-
from
|
|
21
|
+
from dataclasses import dataclass, field
|
|
22
|
+
import logging
|
|
23
|
+
from typing import Any, Dict, List, Optional, Set, Union
|
|
24
|
+
|
|
25
|
+
from bumble import att, gatt, gatt_adapters, gatt_client
|
|
22
26
|
from bumble.core import InvalidArgumentError, InvalidStateError
|
|
23
27
|
from bumble.device import Device, Connection
|
|
24
28
|
from bumble.utils import AsyncRunner, OpenIntEnum
|
|
25
29
|
from bumble.hci import Address
|
|
26
|
-
from dataclasses import dataclass, field
|
|
27
|
-
import logging
|
|
28
|
-
from typing import Any, Dict, List, Optional, Set, Union
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
# -----------------------------------------------------------------------------
|
|
@@ -631,11 +632,12 @@ class HearingAccessServiceProxy(gatt_client.ProfileServiceProxy):
|
|
|
631
632
|
|
|
632
633
|
hearing_aid_preset_control_point: gatt_client.CharacteristicProxy
|
|
633
634
|
preset_control_point_indications: asyncio.Queue
|
|
635
|
+
active_preset_index_notification: asyncio.Queue
|
|
634
636
|
|
|
635
637
|
def __init__(self, service_proxy: gatt_client.ServiceProxy) -> None:
|
|
636
638
|
self.service_proxy = service_proxy
|
|
637
639
|
|
|
638
|
-
self.server_features =
|
|
640
|
+
self.server_features = gatt_adapters.PackedCharacteristicProxyAdapter(
|
|
639
641
|
service_proxy.get_characteristics_by_uuid(
|
|
640
642
|
gatt.GATT_HEARING_AID_FEATURES_CHARACTERISTIC
|
|
641
643
|
)[0],
|
|
@@ -648,7 +650,7 @@ class HearingAccessServiceProxy(gatt_client.ProfileServiceProxy):
|
|
|
648
650
|
)[0]
|
|
649
651
|
)
|
|
650
652
|
|
|
651
|
-
self.active_preset_index =
|
|
653
|
+
self.active_preset_index = gatt_adapters.PackedCharacteristicProxyAdapter(
|
|
652
654
|
service_proxy.get_characteristics_by_uuid(
|
|
653
655
|
gatt.GATT_ACTIVE_PRESET_INDEX_CHARACTERISTIC
|
|
654
656
|
)[0],
|
|
@@ -16,13 +16,14 @@
|
|
|
16
16
|
# -----------------------------------------------------------------------------
|
|
17
17
|
# Imports
|
|
18
18
|
# -----------------------------------------------------------------------------
|
|
19
|
+
from __future__ import annotations
|
|
19
20
|
from enum import IntEnum
|
|
20
21
|
import struct
|
|
22
|
+
from typing import Optional
|
|
21
23
|
|
|
22
24
|
from bumble import core
|
|
23
|
-
from
|
|
24
|
-
from
|
|
25
|
-
from ..gatt import (
|
|
25
|
+
from bumble.att import ATT_Error
|
|
26
|
+
from bumble.gatt import (
|
|
26
27
|
GATT_HEART_RATE_SERVICE,
|
|
27
28
|
GATT_HEART_RATE_MEASUREMENT_CHARACTERISTIC,
|
|
28
29
|
GATT_BODY_SENSOR_LOCATION_CHARACTERISTIC,
|
|
@@ -30,10 +31,13 @@ from ..gatt import (
|
|
|
30
31
|
TemplateService,
|
|
31
32
|
Characteristic,
|
|
32
33
|
CharacteristicValue,
|
|
33
|
-
|
|
34
|
+
)
|
|
35
|
+
from bumble.gatt_adapters import (
|
|
34
36
|
DelegatedCharacteristicAdapter,
|
|
35
37
|
PackedCharacteristicAdapter,
|
|
38
|
+
SerializableCharacteristicAdapter,
|
|
36
39
|
)
|
|
40
|
+
from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy
|
|
37
41
|
|
|
38
42
|
|
|
39
43
|
# -----------------------------------------------------------------------------
|
|
@@ -43,6 +47,10 @@ class HeartRateService(TemplateService):
|
|
|
43
47
|
CONTROL_POINT_NOT_SUPPORTED = 0x80
|
|
44
48
|
RESET_ENERGY_EXPENDED = 0x01
|
|
45
49
|
|
|
50
|
+
heart_rate_measurement_characteristic: Characteristic[HeartRateMeasurement]
|
|
51
|
+
body_sensor_location_characteristic: Characteristic[BodySensorLocation]
|
|
52
|
+
heart_rate_control_point_characteristic: Characteristic[int]
|
|
53
|
+
|
|
46
54
|
class BodySensorLocation(IntEnum):
|
|
47
55
|
OTHER = 0
|
|
48
56
|
CHEST = 1
|
|
@@ -198,6 +206,14 @@ class HeartRateService(TemplateService):
|
|
|
198
206
|
class HeartRateServiceProxy(ProfileServiceProxy):
|
|
199
207
|
SERVICE_CLASS = HeartRateService
|
|
200
208
|
|
|
209
|
+
heart_rate_measurement: Optional[
|
|
210
|
+
CharacteristicProxy[HeartRateService.HeartRateMeasurement]
|
|
211
|
+
]
|
|
212
|
+
body_sensor_location: Optional[
|
|
213
|
+
CharacteristicProxy[HeartRateService.BodySensorLocation]
|
|
214
|
+
]
|
|
215
|
+
heart_rate_control_point: Optional[CharacteristicProxy[int]]
|
|
216
|
+
|
|
201
217
|
def __init__(self, service_proxy):
|
|
202
218
|
self.service_proxy = service_proxy
|
|
203
219
|
|
bumble/profiles/mcp.py
CHANGED
|
@@ -208,7 +208,7 @@ class MediaControlService(gatt.TemplateService):
|
|
|
208
208
|
properties=gatt.Characteristic.Properties.READ
|
|
209
209
|
| gatt.Characteristic.Properties.NOTIFY,
|
|
210
210
|
permissions=gatt.Characteristic.Permissions.READ_REQUIRES_ENCRYPTION,
|
|
211
|
-
value=media_player_name or 'Bumble Player',
|
|
211
|
+
value=(media_player_name or 'Bumble Player').encode(),
|
|
212
212
|
)
|
|
213
213
|
self.track_changed_characteristic = gatt.Characteristic(
|
|
214
214
|
uuid=gatt.GATT_TRACK_CHANGED_CHARACTERISTIC,
|
|
@@ -247,14 +247,16 @@ class MediaControlService(gatt.TemplateService):
|
|
|
247
247
|
permissions=gatt.Characteristic.Permissions.READ_REQUIRES_ENCRYPTION,
|
|
248
248
|
value=b'',
|
|
249
249
|
)
|
|
250
|
-
self.media_control_point_characteristic
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
250
|
+
self.media_control_point_characteristic: gatt.Characteristic[bytes] = (
|
|
251
|
+
gatt.Characteristic(
|
|
252
|
+
uuid=gatt.GATT_MEDIA_CONTROL_POINT_CHARACTERISTIC,
|
|
253
|
+
properties=gatt.Characteristic.Properties.WRITE
|
|
254
|
+
| gatt.Characteristic.Properties.WRITE_WITHOUT_RESPONSE
|
|
255
|
+
| gatt.Characteristic.Properties.NOTIFY,
|
|
256
|
+
permissions=gatt.Characteristic.Permissions.READ_REQUIRES_ENCRYPTION
|
|
257
|
+
| gatt.Characteristic.Permissions.WRITE_REQUIRES_ENCRYPTION,
|
|
258
|
+
value=gatt.CharacteristicValue(write=self.on_media_control_point),
|
|
259
|
+
)
|
|
258
260
|
)
|
|
259
261
|
self.media_control_point_opcodes_supported_characteristic = gatt.Characteristic(
|
|
260
262
|
uuid=gatt.GATT_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED_CHARACTERISTIC,
|
bumble/profiles/pacs.py
CHANGED
|
@@ -25,6 +25,7 @@ from typing import Optional, Sequence, Union
|
|
|
25
25
|
from bumble.profiles.bap import AudioLocation, CodecSpecificCapabilities, ContextType
|
|
26
26
|
from bumble.profiles import le_audio
|
|
27
27
|
from bumble import gatt
|
|
28
|
+
from bumble import gatt_adapters
|
|
28
29
|
from bumble import gatt_client
|
|
29
30
|
from bumble import hci
|
|
30
31
|
|
|
@@ -185,34 +186,42 @@ class PublishedAudioCapabilitiesService(gatt.TemplateService):
|
|
|
185
186
|
class PublishedAudioCapabilitiesServiceProxy(gatt_client.ProfileServiceProxy):
|
|
186
187
|
SERVICE_CLASS = PublishedAudioCapabilitiesService
|
|
187
188
|
|
|
188
|
-
sink_pac: Optional[
|
|
189
|
-
sink_audio_locations: Optional[
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
189
|
+
sink_pac: Optional[gatt_client.CharacteristicProxy[list[PacRecord]]] = None
|
|
190
|
+
sink_audio_locations: Optional[gatt_client.CharacteristicProxy[AudioLocation]] = (
|
|
191
|
+
None
|
|
192
|
+
)
|
|
193
|
+
source_pac: Optional[gatt_client.CharacteristicProxy[list[PacRecord]]] = None
|
|
194
|
+
source_audio_locations: Optional[gatt_client.CharacteristicProxy[AudioLocation]] = (
|
|
195
|
+
None
|
|
196
|
+
)
|
|
197
|
+
available_audio_contexts: gatt_client.CharacteristicProxy[tuple[ContextType, ...]]
|
|
198
|
+
supported_audio_contexts: gatt_client.CharacteristicProxy[tuple[ContextType, ...]]
|
|
194
199
|
|
|
195
200
|
def __init__(self, service_proxy: gatt_client.ServiceProxy):
|
|
196
201
|
self.service_proxy = service_proxy
|
|
197
202
|
|
|
198
|
-
self.available_audio_contexts =
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
+
self.available_audio_contexts = (
|
|
204
|
+
gatt_adapters.DelegatedCharacteristicProxyAdapter(
|
|
205
|
+
service_proxy.get_required_characteristic_by_uuid(
|
|
206
|
+
gatt.GATT_AVAILABLE_AUDIO_CONTEXTS_CHARACTERISTIC
|
|
207
|
+
),
|
|
208
|
+
decode=lambda x: tuple(map(ContextType, struct.unpack('<HH', x))),
|
|
209
|
+
)
|
|
203
210
|
)
|
|
204
211
|
|
|
205
|
-
self.supported_audio_contexts =
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
212
|
+
self.supported_audio_contexts = (
|
|
213
|
+
gatt_adapters.DelegatedCharacteristicProxyAdapter(
|
|
214
|
+
service_proxy.get_required_characteristic_by_uuid(
|
|
215
|
+
gatt.GATT_SUPPORTED_AUDIO_CONTEXTS_CHARACTERISTIC
|
|
216
|
+
),
|
|
217
|
+
decode=lambda x: tuple(map(ContextType, struct.unpack('<HH', x))),
|
|
218
|
+
)
|
|
210
219
|
)
|
|
211
220
|
|
|
212
221
|
if characteristics := service_proxy.get_characteristics_by_uuid(
|
|
213
222
|
gatt.GATT_SINK_PAC_CHARACTERISTIC
|
|
214
223
|
):
|
|
215
|
-
self.sink_pac =
|
|
224
|
+
self.sink_pac = gatt_adapters.DelegatedCharacteristicProxyAdapter(
|
|
216
225
|
characteristics[0],
|
|
217
226
|
decode=PacRecord.list_from_bytes,
|
|
218
227
|
)
|
|
@@ -220,7 +229,7 @@ class PublishedAudioCapabilitiesServiceProxy(gatt_client.ProfileServiceProxy):
|
|
|
220
229
|
if characteristics := service_proxy.get_characteristics_by_uuid(
|
|
221
230
|
gatt.GATT_SOURCE_PAC_CHARACTERISTIC
|
|
222
231
|
):
|
|
223
|
-
self.source_pac =
|
|
232
|
+
self.source_pac = gatt_adapters.DelegatedCharacteristicProxyAdapter(
|
|
224
233
|
characteristics[0],
|
|
225
234
|
decode=PacRecord.list_from_bytes,
|
|
226
235
|
)
|
|
@@ -228,15 +237,19 @@ class PublishedAudioCapabilitiesServiceProxy(gatt_client.ProfileServiceProxy):
|
|
|
228
237
|
if characteristics := service_proxy.get_characteristics_by_uuid(
|
|
229
238
|
gatt.GATT_SINK_AUDIO_LOCATION_CHARACTERISTIC
|
|
230
239
|
):
|
|
231
|
-
self.sink_audio_locations =
|
|
232
|
-
|
|
233
|
-
|
|
240
|
+
self.sink_audio_locations = (
|
|
241
|
+
gatt_adapters.DelegatedCharacteristicProxyAdapter(
|
|
242
|
+
characteristics[0],
|
|
243
|
+
decode=lambda x: AudioLocation(struct.unpack('<I', x)[0]),
|
|
244
|
+
)
|
|
234
245
|
)
|
|
235
246
|
|
|
236
247
|
if characteristics := service_proxy.get_characteristics_by_uuid(
|
|
237
248
|
gatt.GATT_SOURCE_AUDIO_LOCATION_CHARACTERISTIC
|
|
238
249
|
):
|
|
239
|
-
self.source_audio_locations =
|
|
240
|
-
|
|
241
|
-
|
|
250
|
+
self.source_audio_locations = (
|
|
251
|
+
gatt_adapters.DelegatedCharacteristicProxyAdapter(
|
|
252
|
+
characteristics[0],
|
|
253
|
+
decode=lambda x: AudioLocation(struct.unpack('<I', x)[0]),
|
|
254
|
+
)
|
|
242
255
|
)
|
bumble/profiles/tmap.py
CHANGED
|
@@ -24,11 +24,11 @@ import struct
|
|
|
24
24
|
from bumble.gatt import (
|
|
25
25
|
TemplateService,
|
|
26
26
|
Characteristic,
|
|
27
|
-
DelegatedCharacteristicAdapter,
|
|
28
27
|
GATT_TELEPHONY_AND_MEDIA_AUDIO_SERVICE,
|
|
29
28
|
GATT_TMAP_ROLE_CHARACTERISTIC,
|
|
30
29
|
)
|
|
31
|
-
from bumble.
|
|
30
|
+
from bumble.gatt_adapters import DelegatedCharacteristicProxyAdapter
|
|
31
|
+
from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy, ServiceProxy
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
# -----------------------------------------------------------------------------
|
|
@@ -53,6 +53,8 @@ class Role(enum.IntFlag):
|
|
|
53
53
|
class TelephonyAndMediaAudioService(TemplateService):
|
|
54
54
|
UUID = GATT_TELEPHONY_AND_MEDIA_AUDIO_SERVICE
|
|
55
55
|
|
|
56
|
+
role_characteristic: Characteristic[bytes]
|
|
57
|
+
|
|
56
58
|
def __init__(self, role: Role):
|
|
57
59
|
self.role_characteristic = Characteristic(
|
|
58
60
|
GATT_TMAP_ROLE_CHARACTERISTIC,
|
|
@@ -68,12 +70,12 @@ class TelephonyAndMediaAudioService(TemplateService):
|
|
|
68
70
|
class TelephonyAndMediaAudioServiceProxy(ProfileServiceProxy):
|
|
69
71
|
SERVICE_CLASS = TelephonyAndMediaAudioService
|
|
70
72
|
|
|
71
|
-
role:
|
|
73
|
+
role: CharacteristicProxy[Role]
|
|
72
74
|
|
|
73
75
|
def __init__(self, service_proxy: ServiceProxy):
|
|
74
76
|
self.service_proxy = service_proxy
|
|
75
77
|
|
|
76
|
-
self.role =
|
|
78
|
+
self.role = DelegatedCharacteristicProxyAdapter(
|
|
77
79
|
service_proxy.get_required_characteristic_by_uuid(
|
|
78
80
|
GATT_TMAP_ROLE_CHARACTERISTIC
|
|
79
81
|
),
|
bumble/profiles/vcs.py
CHANGED
|
@@ -25,6 +25,7 @@ from typing import Optional, Sequence
|
|
|
25
25
|
from bumble import att
|
|
26
26
|
from bumble import device
|
|
27
27
|
from bumble import gatt
|
|
28
|
+
from bumble import gatt_adapters
|
|
28
29
|
from bumble import gatt_client
|
|
29
30
|
|
|
30
31
|
|
|
@@ -209,14 +210,14 @@ class VolumeControlService(gatt.TemplateService):
|
|
|
209
210
|
class VolumeControlServiceProxy(gatt_client.ProfileServiceProxy):
|
|
210
211
|
SERVICE_CLASS = VolumeControlService
|
|
211
212
|
|
|
212
|
-
volume_control_point: gatt_client.CharacteristicProxy
|
|
213
|
-
volume_state:
|
|
214
|
-
volume_flags:
|
|
213
|
+
volume_control_point: gatt_client.CharacteristicProxy[bytes]
|
|
214
|
+
volume_state: gatt_client.CharacteristicProxy[VolumeState]
|
|
215
|
+
volume_flags: gatt_client.CharacteristicProxy[VolumeFlags]
|
|
215
216
|
|
|
216
217
|
def __init__(self, service_proxy: gatt_client.ServiceProxy) -> None:
|
|
217
218
|
self.service_proxy = service_proxy
|
|
218
219
|
|
|
219
|
-
self.volume_state =
|
|
220
|
+
self.volume_state = gatt_adapters.SerializableCharacteristicProxyAdapter(
|
|
220
221
|
service_proxy.get_required_characteristic_by_uuid(
|
|
221
222
|
gatt.GATT_VOLUME_STATE_CHARACTERISTIC
|
|
222
223
|
),
|
|
@@ -227,7 +228,7 @@ class VolumeControlServiceProxy(gatt_client.ProfileServiceProxy):
|
|
|
227
228
|
gatt.GATT_VOLUME_CONTROL_POINT_CHARACTERISTIC
|
|
228
229
|
)
|
|
229
230
|
|
|
230
|
-
self.volume_flags =
|
|
231
|
+
self.volume_flags = gatt_adapters.DelegatedCharacteristicProxyAdapter(
|
|
231
232
|
service_proxy.get_required_characteristic_by_uuid(
|
|
232
233
|
gatt.GATT_VOLUME_FLAGS_CHARACTERISTIC
|
|
233
234
|
),
|