bumble 0.0.204__py3-none-any.whl → 0.0.207__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 +2 -2
- bumble/apps/auracast.py +626 -87
- bumble/apps/bench.py +225 -147
- bumble/apps/controller_info.py +23 -7
- bumble/apps/device_info.py +50 -4
- bumble/apps/lea_unicast/app.py +61 -201
- bumble/audio/__init__.py +17 -0
- bumble/audio/io.py +553 -0
- bumble/controller.py +24 -9
- bumble/core.py +4 -1
- bumble/device.py +993 -48
- bumble/gatt.py +35 -6
- bumble/gatt_client.py +14 -2
- bumble/hci.py +812 -14
- bumble/host.py +359 -63
- bumble/l2cap.py +3 -16
- bumble/profiles/aics.py +19 -38
- bumble/profiles/ascs.py +6 -18
- bumble/profiles/asha.py +5 -5
- bumble/profiles/bass.py +10 -19
- bumble/profiles/gatt_service.py +166 -0
- bumble/profiles/gmap.py +193 -0
- bumble/profiles/le_audio.py +87 -4
- bumble/profiles/pacs.py +48 -16
- bumble/profiles/tmap.py +3 -9
- bumble/profiles/{vcp.py → vcs.py} +33 -28
- bumble/profiles/vocs.py +54 -85
- bumble/sdp.py +223 -93
- bumble/smp.py +1 -1
- bumble/utils.py +2 -2
- bumble/vendor/android/hci.py +1 -1
- {bumble-0.0.204.dist-info → bumble-0.0.207.dist-info}/METADATA +12 -10
- {bumble-0.0.204.dist-info → bumble-0.0.207.dist-info}/RECORD +37 -34
- {bumble-0.0.204.dist-info → bumble-0.0.207.dist-info}/WHEEL +1 -1
- {bumble-0.0.204.dist-info → bumble-0.0.207.dist-info}/entry_points.txt +1 -0
- bumble/apps/lea_unicast/liblc3.wasm +0 -0
- {bumble-0.0.204.dist-info → bumble-0.0.207.dist-info}/LICENSE +0 -0
- {bumble-0.0.204.dist-info → bumble-0.0.207.dist-info}/top_level.txt +0 -0
bumble/profiles/pacs.py
CHANGED
|
@@ -72,6 +72,19 @@ class PacRecord:
|
|
|
72
72
|
metadata=metadata,
|
|
73
73
|
)
|
|
74
74
|
|
|
75
|
+
@classmethod
|
|
76
|
+
def list_from_bytes(cls, data: bytes) -> list[PacRecord]:
|
|
77
|
+
"""Parse a serialized list of records preceded by a one byte list length."""
|
|
78
|
+
record_count = data[0]
|
|
79
|
+
records = []
|
|
80
|
+
offset = 1
|
|
81
|
+
for _ in range(record_count):
|
|
82
|
+
record = PacRecord.from_bytes(data[offset:])
|
|
83
|
+
offset += len(bytes(record))
|
|
84
|
+
records.append(record)
|
|
85
|
+
|
|
86
|
+
return records
|
|
87
|
+
|
|
75
88
|
def __bytes__(self) -> bytes:
|
|
76
89
|
capabilities_bytes = bytes(self.codec_specific_capabilities)
|
|
77
90
|
metadata_bytes = bytes(self.metadata)
|
|
@@ -172,39 +185,58 @@ class PublishedAudioCapabilitiesService(gatt.TemplateService):
|
|
|
172
185
|
class PublishedAudioCapabilitiesServiceProxy(gatt_client.ProfileServiceProxy):
|
|
173
186
|
SERVICE_CLASS = PublishedAudioCapabilitiesService
|
|
174
187
|
|
|
175
|
-
sink_pac: Optional[
|
|
176
|
-
sink_audio_locations: Optional[
|
|
177
|
-
source_pac: Optional[
|
|
178
|
-
source_audio_locations: Optional[
|
|
179
|
-
available_audio_contexts:
|
|
180
|
-
supported_audio_contexts:
|
|
188
|
+
sink_pac: Optional[gatt.DelegatedCharacteristicAdapter] = None
|
|
189
|
+
sink_audio_locations: Optional[gatt.DelegatedCharacteristicAdapter] = None
|
|
190
|
+
source_pac: Optional[gatt.DelegatedCharacteristicAdapter] = None
|
|
191
|
+
source_audio_locations: Optional[gatt.DelegatedCharacteristicAdapter] = None
|
|
192
|
+
available_audio_contexts: gatt.DelegatedCharacteristicAdapter
|
|
193
|
+
supported_audio_contexts: gatt.DelegatedCharacteristicAdapter
|
|
181
194
|
|
|
182
195
|
def __init__(self, service_proxy: gatt_client.ServiceProxy):
|
|
183
196
|
self.service_proxy = service_proxy
|
|
184
197
|
|
|
185
|
-
self.available_audio_contexts =
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
)
|
|
198
|
+
self.available_audio_contexts = gatt.DelegatedCharacteristicAdapter(
|
|
199
|
+
service_proxy.get_required_characteristic_by_uuid(
|
|
200
|
+
gatt.GATT_AVAILABLE_AUDIO_CONTEXTS_CHARACTERISTIC
|
|
201
|
+
),
|
|
202
|
+
decode=lambda x: tuple(map(ContextType, struct.unpack('<HH', x))),
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
self.supported_audio_contexts = gatt.DelegatedCharacteristicAdapter(
|
|
206
|
+
service_proxy.get_required_characteristic_by_uuid(
|
|
207
|
+
gatt.GATT_SUPPORTED_AUDIO_CONTEXTS_CHARACTERISTIC
|
|
208
|
+
),
|
|
209
|
+
decode=lambda x: tuple(map(ContextType, struct.unpack('<HH', x))),
|
|
210
|
+
)
|
|
191
211
|
|
|
192
212
|
if characteristics := service_proxy.get_characteristics_by_uuid(
|
|
193
213
|
gatt.GATT_SINK_PAC_CHARACTERISTIC
|
|
194
214
|
):
|
|
195
|
-
self.sink_pac =
|
|
215
|
+
self.sink_pac = gatt.DelegatedCharacteristicAdapter(
|
|
216
|
+
characteristics[0],
|
|
217
|
+
decode=PacRecord.list_from_bytes,
|
|
218
|
+
)
|
|
196
219
|
|
|
197
220
|
if characteristics := service_proxy.get_characteristics_by_uuid(
|
|
198
221
|
gatt.GATT_SOURCE_PAC_CHARACTERISTIC
|
|
199
222
|
):
|
|
200
|
-
self.source_pac =
|
|
223
|
+
self.source_pac = gatt.DelegatedCharacteristicAdapter(
|
|
224
|
+
characteristics[0],
|
|
225
|
+
decode=PacRecord.list_from_bytes,
|
|
226
|
+
)
|
|
201
227
|
|
|
202
228
|
if characteristics := service_proxy.get_characteristics_by_uuid(
|
|
203
229
|
gatt.GATT_SINK_AUDIO_LOCATION_CHARACTERISTIC
|
|
204
230
|
):
|
|
205
|
-
self.sink_audio_locations =
|
|
231
|
+
self.sink_audio_locations = gatt.DelegatedCharacteristicAdapter(
|
|
232
|
+
characteristics[0],
|
|
233
|
+
decode=lambda x: AudioLocation(struct.unpack('<I', x)[0]),
|
|
234
|
+
)
|
|
206
235
|
|
|
207
236
|
if characteristics := service_proxy.get_characteristics_by_uuid(
|
|
208
237
|
gatt.GATT_SOURCE_AUDIO_LOCATION_CHARACTERISTIC
|
|
209
238
|
):
|
|
210
|
-
self.source_audio_locations =
|
|
239
|
+
self.source_audio_locations = gatt.DelegatedCharacteristicAdapter(
|
|
240
|
+
characteristics[0],
|
|
241
|
+
decode=lambda x: AudioLocation(struct.unpack('<I', x)[0]),
|
|
242
|
+
)
|
bumble/profiles/tmap.py
CHANGED
|
@@ -25,7 +25,6 @@ from bumble.gatt import (
|
|
|
25
25
|
TemplateService,
|
|
26
26
|
Characteristic,
|
|
27
27
|
DelegatedCharacteristicAdapter,
|
|
28
|
-
InvalidServiceError,
|
|
29
28
|
GATT_TELEPHONY_AND_MEDIA_AUDIO_SERVICE,
|
|
30
29
|
GATT_TMAP_ROLE_CHARACTERISTIC,
|
|
31
30
|
)
|
|
@@ -74,15 +73,10 @@ class TelephonyAndMediaAudioServiceProxy(ProfileServiceProxy):
|
|
|
74
73
|
def __init__(self, service_proxy: ServiceProxy):
|
|
75
74
|
self.service_proxy = service_proxy
|
|
76
75
|
|
|
77
|
-
if not (
|
|
78
|
-
characteristics := service_proxy.get_characteristics_by_uuid(
|
|
79
|
-
GATT_TMAP_ROLE_CHARACTERISTIC
|
|
80
|
-
)
|
|
81
|
-
):
|
|
82
|
-
raise InvalidServiceError('TMAP Role characteristic not found')
|
|
83
|
-
|
|
84
76
|
self.role = DelegatedCharacteristicAdapter(
|
|
85
|
-
|
|
77
|
+
service_proxy.get_required_characteristic_by_uuid(
|
|
78
|
+
GATT_TMAP_ROLE_CHARACTERISTIC
|
|
79
|
+
),
|
|
86
80
|
decode=lambda value: Role(
|
|
87
81
|
struct.unpack_from('<H', value, 0)[0],
|
|
88
82
|
),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2021-
|
|
1
|
+
# Copyright 2021-2025 Google LLC
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -17,14 +17,16 @@
|
|
|
17
17
|
# Imports
|
|
18
18
|
# -----------------------------------------------------------------------------
|
|
19
19
|
from __future__ import annotations
|
|
20
|
+
import dataclasses
|
|
20
21
|
import enum
|
|
21
22
|
|
|
23
|
+
from typing import Optional, Sequence
|
|
24
|
+
|
|
22
25
|
from bumble import att
|
|
23
26
|
from bumble import device
|
|
24
27
|
from bumble import gatt
|
|
25
28
|
from bumble import gatt_client
|
|
26
29
|
|
|
27
|
-
from typing import Optional, Sequence
|
|
28
30
|
|
|
29
31
|
# -----------------------------------------------------------------------------
|
|
30
32
|
# Constants
|
|
@@ -67,6 +69,20 @@ class VolumeControlPointOpcode(enum.IntEnum):
|
|
|
67
69
|
MUTE = 0x06
|
|
68
70
|
|
|
69
71
|
|
|
72
|
+
@dataclasses.dataclass
|
|
73
|
+
class VolumeState:
|
|
74
|
+
volume_setting: int
|
|
75
|
+
mute: int
|
|
76
|
+
change_counter: int
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def from_bytes(cls, data: bytes) -> VolumeState:
|
|
80
|
+
return cls(data[0], data[1], data[2])
|
|
81
|
+
|
|
82
|
+
def __bytes__(self) -> bytes:
|
|
83
|
+
return bytes([self.volume_setting, self.mute, self.change_counter])
|
|
84
|
+
|
|
85
|
+
|
|
70
86
|
# -----------------------------------------------------------------------------
|
|
71
87
|
# Server
|
|
72
88
|
# -----------------------------------------------------------------------------
|
|
@@ -126,16 +142,8 @@ class VolumeControlService(gatt.TemplateService):
|
|
|
126
142
|
included_services=list(included_services),
|
|
127
143
|
)
|
|
128
144
|
|
|
129
|
-
@property
|
|
130
|
-
def volume_state_bytes(self) -> bytes:
|
|
131
|
-
return bytes([self.volume_setting, self.muted, self.change_counter])
|
|
132
|
-
|
|
133
|
-
@volume_state_bytes.setter
|
|
134
|
-
def volume_state_bytes(self, new_value: bytes) -> None:
|
|
135
|
-
self.volume_setting, self.muted, self.change_counter = new_value
|
|
136
|
-
|
|
137
145
|
def _on_read_volume_state(self, _connection: Optional[device.Connection]) -> bytes:
|
|
138
|
-
return self.
|
|
146
|
+
return bytes(VolumeState(self.volume_setting, self.muted, self.change_counter))
|
|
139
147
|
|
|
140
148
|
def _on_write_volume_control_point(
|
|
141
149
|
self, connection: Optional[device.Connection], value: bytes
|
|
@@ -153,14 +161,9 @@ class VolumeControlService(gatt.TemplateService):
|
|
|
153
161
|
self.change_counter = (self.change_counter + 1) % 256
|
|
154
162
|
connection.abort_on(
|
|
155
163
|
'disconnection',
|
|
156
|
-
connection.device.notify_subscribers(
|
|
157
|
-
attribute=self.volume_state,
|
|
158
|
-
value=self.volume_state_bytes,
|
|
159
|
-
),
|
|
160
|
-
)
|
|
161
|
-
self.emit(
|
|
162
|
-
'volume_state', self.volume_setting, self.muted, self.change_counter
|
|
164
|
+
connection.device.notify_subscribers(attribute=self.volume_state),
|
|
163
165
|
)
|
|
166
|
+
self.emit('volume_state_change')
|
|
164
167
|
|
|
165
168
|
def _on_relative_volume_down(self) -> bool:
|
|
166
169
|
old_volume = self.volume_setting
|
|
@@ -207,24 +210,26 @@ class VolumeControlServiceProxy(gatt_client.ProfileServiceProxy):
|
|
|
207
210
|
SERVICE_CLASS = VolumeControlService
|
|
208
211
|
|
|
209
212
|
volume_control_point: gatt_client.CharacteristicProxy
|
|
213
|
+
volume_state: gatt.SerializableCharacteristicAdapter
|
|
214
|
+
volume_flags: gatt.DelegatedCharacteristicAdapter
|
|
210
215
|
|
|
211
216
|
def __init__(self, service_proxy: gatt_client.ServiceProxy) -> None:
|
|
212
217
|
self.service_proxy = service_proxy
|
|
213
218
|
|
|
214
|
-
self.volume_state = gatt.
|
|
215
|
-
service_proxy.
|
|
219
|
+
self.volume_state = gatt.SerializableCharacteristicAdapter(
|
|
220
|
+
service_proxy.get_required_characteristic_by_uuid(
|
|
216
221
|
gatt.GATT_VOLUME_STATE_CHARACTERISTIC
|
|
217
|
-
)
|
|
218
|
-
|
|
222
|
+
),
|
|
223
|
+
VolumeState,
|
|
219
224
|
)
|
|
220
225
|
|
|
221
|
-
self.volume_control_point = service_proxy.
|
|
226
|
+
self.volume_control_point = service_proxy.get_required_characteristic_by_uuid(
|
|
222
227
|
gatt.GATT_VOLUME_CONTROL_POINT_CHARACTERISTIC
|
|
223
|
-
)
|
|
228
|
+
)
|
|
224
229
|
|
|
225
|
-
self.volume_flags = gatt.
|
|
226
|
-
service_proxy.
|
|
230
|
+
self.volume_flags = gatt.DelegatedCharacteristicAdapter(
|
|
231
|
+
service_proxy.get_required_characteristic_by_uuid(
|
|
227
232
|
gatt.GATT_VOLUME_FLAGS_CHARACTERISTIC
|
|
228
|
-
)
|
|
229
|
-
|
|
233
|
+
),
|
|
234
|
+
decode=lambda data: VolumeFlags(data[0]),
|
|
230
235
|
)
|
bumble/profiles/vocs.py
CHANGED
|
@@ -27,8 +27,8 @@ from bumble.gatt import (
|
|
|
27
27
|
DelegatedCharacteristicAdapter,
|
|
28
28
|
TemplateService,
|
|
29
29
|
CharacteristicValue,
|
|
30
|
+
SerializableCharacteristicAdapter,
|
|
30
31
|
UTF8CharacteristicAdapter,
|
|
31
|
-
InvalidServiceError,
|
|
32
32
|
GATT_VOLUME_OFFSET_CONTROL_SERVICE,
|
|
33
33
|
GATT_VOLUME_OFFSET_STATE_CHARACTERISTIC,
|
|
34
34
|
GATT_AUDIO_LOCATION_CHARACTERISTIC,
|
|
@@ -82,9 +82,7 @@ class VolumeOffsetState:
|
|
|
82
82
|
|
|
83
83
|
async def notify_subscribers_via_connection(self, connection: Connection) -> None:
|
|
84
84
|
assert self.attribute_value is not None
|
|
85
|
-
await connection.device.notify_subscribers(
|
|
86
|
-
attribute=self.attribute_value, value=bytes(self)
|
|
87
|
-
)
|
|
85
|
+
await connection.device.notify_subscribers(attribute=self.attribute_value)
|
|
88
86
|
|
|
89
87
|
def on_read(self, _connection: Optional[Connection]) -> bytes:
|
|
90
88
|
return bytes(self)
|
|
@@ -111,9 +109,7 @@ class VocsAudioLocation:
|
|
|
111
109
|
assert self.attribute_value
|
|
112
110
|
|
|
113
111
|
self.audio_location = AudioLocation(int.from_bytes(value, 'little'))
|
|
114
|
-
await connection.device.notify_subscribers(
|
|
115
|
-
attribute=self.attribute_value, value=value
|
|
116
|
-
)
|
|
112
|
+
await connection.device.notify_subscribers(attribute=self.attribute_value)
|
|
117
113
|
|
|
118
114
|
|
|
119
115
|
@dataclass
|
|
@@ -169,9 +165,7 @@ class AudioOutputDescription:
|
|
|
169
165
|
assert self.attribute_value
|
|
170
166
|
|
|
171
167
|
self.audio_output_description = value.decode('utf-8')
|
|
172
|
-
await connection.device.notify_subscribers(
|
|
173
|
-
attribute=self.attribute_value, value=value
|
|
174
|
-
)
|
|
168
|
+
await connection.device.notify_subscribers(attribute=self.attribute_value)
|
|
175
169
|
|
|
176
170
|
|
|
177
171
|
# -----------------------------------------------------------------------------
|
|
@@ -203,37 +197,30 @@ class VolumeOffsetControlService(TemplateService):
|
|
|
203
197
|
VolumeOffsetControlPoint(self.volume_offset_state)
|
|
204
198
|
)
|
|
205
199
|
|
|
206
|
-
self.volume_offset_state_characteristic =
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
Characteristic.Properties.READ | Characteristic.Properties.NOTIFY
|
|
211
|
-
),
|
|
212
|
-
permissions=Characteristic.Permissions.READ_REQUIRES_ENCRYPTION,
|
|
213
|
-
value=CharacteristicValue(read=self.volume_offset_state.on_read),
|
|
200
|
+
self.volume_offset_state_characteristic = Characteristic(
|
|
201
|
+
uuid=GATT_VOLUME_OFFSET_STATE_CHARACTERISTIC,
|
|
202
|
+
properties=(
|
|
203
|
+
Characteristic.Properties.READ | Characteristic.Properties.NOTIFY
|
|
214
204
|
),
|
|
215
|
-
|
|
205
|
+
permissions=Characteristic.Permissions.READ_REQUIRES_ENCRYPTION,
|
|
206
|
+
value=CharacteristicValue(read=self.volume_offset_state.on_read),
|
|
216
207
|
)
|
|
217
208
|
|
|
218
|
-
self.audio_location_characteristic =
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
write=self.audio_location.on_write,
|
|
233
|
-
),
|
|
209
|
+
self.audio_location_characteristic = Characteristic(
|
|
210
|
+
uuid=GATT_AUDIO_LOCATION_CHARACTERISTIC,
|
|
211
|
+
properties=(
|
|
212
|
+
Characteristic.Properties.READ
|
|
213
|
+
| Characteristic.Properties.NOTIFY
|
|
214
|
+
| Characteristic.Properties.WRITE_WITHOUT_RESPONSE
|
|
215
|
+
),
|
|
216
|
+
permissions=(
|
|
217
|
+
Characteristic.Permissions.READ_REQUIRES_ENCRYPTION
|
|
218
|
+
| Characteristic.Permissions.WRITE_REQUIRES_ENCRYPTION
|
|
219
|
+
),
|
|
220
|
+
value=CharacteristicValue(
|
|
221
|
+
read=self.audio_location.on_read,
|
|
222
|
+
write=self.audio_location.on_write,
|
|
234
223
|
),
|
|
235
|
-
encode=lambda value: bytes(value),
|
|
236
|
-
decode=VocsAudioLocation.from_bytes,
|
|
237
224
|
)
|
|
238
225
|
self.audio_location.attribute_value = self.audio_location_characteristic.value
|
|
239
226
|
|
|
@@ -244,25 +231,22 @@ class VolumeOffsetControlService(TemplateService):
|
|
|
244
231
|
value=CharacteristicValue(write=self.volume_offset_control_point.on_write),
|
|
245
232
|
)
|
|
246
233
|
|
|
247
|
-
self.audio_output_description_characteristic =
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
),
|
|
263
|
-
)
|
|
234
|
+
self.audio_output_description_characteristic = Characteristic(
|
|
235
|
+
uuid=GATT_AUDIO_OUTPUT_DESCRIPTION_CHARACTERISTIC,
|
|
236
|
+
properties=(
|
|
237
|
+
Characteristic.Properties.READ
|
|
238
|
+
| Characteristic.Properties.NOTIFY
|
|
239
|
+
| Characteristic.Properties.WRITE_WITHOUT_RESPONSE
|
|
240
|
+
),
|
|
241
|
+
permissions=(
|
|
242
|
+
Characteristic.Permissions.READ_REQUIRES_ENCRYPTION
|
|
243
|
+
| Characteristic.Permissions.WRITE_REQUIRES_ENCRYPTION
|
|
244
|
+
),
|
|
245
|
+
value=CharacteristicValue(
|
|
246
|
+
read=self.audio_output_description.on_read,
|
|
247
|
+
write=self.audio_output_description.on_write,
|
|
248
|
+
),
|
|
264
249
|
)
|
|
265
|
-
|
|
266
250
|
self.audio_output_description.attribute_value = (
|
|
267
251
|
self.audio_output_description_characteristic.value
|
|
268
252
|
)
|
|
@@ -287,44 +271,29 @@ class VolumeOffsetControlServiceProxy(ProfileServiceProxy):
|
|
|
287
271
|
def __init__(self, service_proxy: ServiceProxy) -> None:
|
|
288
272
|
self.service_proxy = service_proxy
|
|
289
273
|
|
|
290
|
-
|
|
291
|
-
|
|
274
|
+
self.volume_offset_state = SerializableCharacteristicAdapter(
|
|
275
|
+
service_proxy.get_required_characteristic_by_uuid(
|
|
292
276
|
GATT_VOLUME_OFFSET_STATE_CHARACTERISTIC
|
|
293
|
-
)
|
|
294
|
-
|
|
295
|
-
raise InvalidServiceError("Volume Offset State characteristic not found")
|
|
296
|
-
self.volume_offset_state = DelegatedCharacteristicAdapter(
|
|
297
|
-
characteristics[0], decode=VolumeOffsetState.from_bytes
|
|
277
|
+
),
|
|
278
|
+
VolumeOffsetState,
|
|
298
279
|
)
|
|
299
280
|
|
|
300
|
-
if not (
|
|
301
|
-
characteristics := service_proxy.get_characteristics_by_uuid(
|
|
302
|
-
GATT_AUDIO_LOCATION_CHARACTERISTIC
|
|
303
|
-
)
|
|
304
|
-
):
|
|
305
|
-
raise InvalidServiceError("Audio Location characteristic not found")
|
|
306
281
|
self.audio_location = DelegatedCharacteristicAdapter(
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
282
|
+
service_proxy.get_required_characteristic_by_uuid(
|
|
283
|
+
GATT_AUDIO_LOCATION_CHARACTERISTIC
|
|
284
|
+
),
|
|
285
|
+
encode=lambda value: bytes([int(value)]),
|
|
286
|
+
decode=lambda data: AudioLocation(data[0]),
|
|
310
287
|
)
|
|
311
288
|
|
|
312
|
-
|
|
313
|
-
|
|
289
|
+
self.volume_offset_control_point = (
|
|
290
|
+
service_proxy.get_required_characteristic_by_uuid(
|
|
314
291
|
GATT_VOLUME_OFFSET_CONTROL_POINT_CHARACTERISTIC
|
|
315
292
|
)
|
|
316
|
-
)
|
|
317
|
-
raise InvalidServiceError(
|
|
318
|
-
"Volume Offset Control Point characteristic not found"
|
|
319
|
-
)
|
|
320
|
-
self.volume_offset_control_point = characteristics[0]
|
|
293
|
+
)
|
|
321
294
|
|
|
322
|
-
|
|
323
|
-
|
|
295
|
+
self.audio_output_description = UTF8CharacteristicAdapter(
|
|
296
|
+
service_proxy.get_required_characteristic_by_uuid(
|
|
324
297
|
GATT_AUDIO_OUTPUT_DESCRIPTION_CHARACTERISTIC
|
|
325
298
|
)
|
|
326
|
-
)
|
|
327
|
-
raise InvalidServiceError(
|
|
328
|
-
"Audio Output Description characteristic not found"
|
|
329
|
-
)
|
|
330
|
-
self.audio_output_description = UTF8CharacteristicAdapter(characteristics[0])
|
|
299
|
+
)
|