bumble 0.0.179__py3-none-any.whl → 0.0.181__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/bench.py +110 -81
- bumble/apps/ble_rpa_tool.py +63 -0
- bumble/apps/console.py +4 -4
- bumble/apps/controller_info.py +34 -2
- bumble/apps/pair.py +6 -8
- bumble/att.py +53 -11
- bumble/controller.py +28 -1
- bumble/crypto.py +10 -0
- bumble/device.py +630 -134
- bumble/drivers/__init__.py +27 -31
- bumble/drivers/common.py +45 -0
- bumble/drivers/rtk.py +11 -4
- bumble/gatt.py +183 -58
- bumble/gatt_client.py +56 -20
- bumble/gatt_server.py +30 -22
- bumble/hci.py +227 -92
- bumble/helpers.py +81 -42
- bumble/hfp.py +37 -27
- bumble/hid.py +282 -61
- bumble/host.py +158 -93
- bumble/l2cap.py +11 -3
- bumble/profiles/asha_service.py +2 -2
- bumble/profiles/bap.py +1247 -0
- bumble/profiles/cap.py +52 -0
- bumble/profiles/csip.py +205 -0
- bumble/rfcomm.py +24 -17
- bumble/smp.py +1 -1
- bumble/transport/__init__.py +49 -21
- bumble/transport/android_emulator.py +1 -1
- bumble/transport/common.py +3 -2
- bumble/transport/hci_socket.py +1 -4
- bumble/transport/usb.py +1 -1
- bumble/utils.py +3 -6
- {bumble-0.0.179.dist-info → bumble-0.0.181.dist-info}/METADATA +1 -1
- {bumble-0.0.179.dist-info → bumble-0.0.181.dist-info}/RECORD +40 -35
- {bumble-0.0.179.dist-info → bumble-0.0.181.dist-info}/entry_points.txt +1 -0
- {bumble-0.0.179.dist-info → bumble-0.0.181.dist-info}/LICENSE +0 -0
- {bumble-0.0.179.dist-info → bumble-0.0.181.dist-info}/WHEEL +0 -0
- {bumble-0.0.179.dist-info → bumble-0.0.181.dist-info}/top_level.txt +0 -0
bumble/drivers/__init__.py
CHANGED
|
@@ -19,12 +19,17 @@ like loading firmware after a cold start.
|
|
|
19
19
|
# -----------------------------------------------------------------------------
|
|
20
20
|
# Imports
|
|
21
21
|
# -----------------------------------------------------------------------------
|
|
22
|
-
import
|
|
22
|
+
from __future__ import annotations
|
|
23
23
|
import logging
|
|
24
24
|
import pathlib
|
|
25
25
|
import platform
|
|
26
|
+
from typing import Dict, Iterable, Optional, Type, TYPE_CHECKING
|
|
27
|
+
|
|
26
28
|
from . import rtk
|
|
29
|
+
from .common import Driver
|
|
27
30
|
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from bumble.host import Host
|
|
28
33
|
|
|
29
34
|
# -----------------------------------------------------------------------------
|
|
30
35
|
# Logging
|
|
@@ -32,40 +37,31 @@ from . import rtk
|
|
|
32
37
|
logger = logging.getLogger(__name__)
|
|
33
38
|
|
|
34
39
|
|
|
35
|
-
# -----------------------------------------------------------------------------
|
|
36
|
-
# Classes
|
|
37
|
-
# -----------------------------------------------------------------------------
|
|
38
|
-
class Driver(abc.ABC):
|
|
39
|
-
"""Base class for drivers."""
|
|
40
|
-
|
|
41
|
-
@staticmethod
|
|
42
|
-
async def for_host(_host):
|
|
43
|
-
"""Return a driver instance for a host.
|
|
44
|
-
|
|
45
|
-
Args:
|
|
46
|
-
host: Host object for which a driver should be created.
|
|
47
|
-
|
|
48
|
-
Returns:
|
|
49
|
-
A Driver instance if a driver should be instantiated for this host, or
|
|
50
|
-
None if no driver instance of this class is needed.
|
|
51
|
-
"""
|
|
52
|
-
return None
|
|
53
|
-
|
|
54
|
-
@abc.abstractmethod
|
|
55
|
-
async def init_controller(self):
|
|
56
|
-
"""Initialize the controller."""
|
|
57
|
-
|
|
58
|
-
|
|
59
40
|
# -----------------------------------------------------------------------------
|
|
60
41
|
# Functions
|
|
61
42
|
# -----------------------------------------------------------------------------
|
|
62
|
-
async def get_driver_for_host(host):
|
|
63
|
-
"""Probe
|
|
64
|
-
|
|
43
|
+
async def get_driver_for_host(host: Host) -> Optional[Driver]:
|
|
44
|
+
"""Probe diver classes until one returns a valid instance for a host, or none is
|
|
45
|
+
found.
|
|
46
|
+
If a "driver" HCI metadata entry is present, only that driver class will be probed.
|
|
65
47
|
"""
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
48
|
+
driver_classes: Dict[str, Type[Driver]] = {"rtk": rtk.Driver}
|
|
49
|
+
probe_list: Iterable[str]
|
|
50
|
+
if driver_name := host.hci_metadata.get("driver"):
|
|
51
|
+
# Only probe a single driver
|
|
52
|
+
probe_list = [driver_name]
|
|
53
|
+
else:
|
|
54
|
+
# Probe all drivers
|
|
55
|
+
probe_list = driver_classes.keys()
|
|
56
|
+
|
|
57
|
+
for driver_name in probe_list:
|
|
58
|
+
if driver_class := driver_classes.get(driver_name):
|
|
59
|
+
logger.debug(f"Probing driver class: {driver_name}")
|
|
60
|
+
if driver := await driver_class.for_host(host):
|
|
61
|
+
logger.debug(f"Instantiated {driver_name} driver")
|
|
62
|
+
return driver
|
|
63
|
+
else:
|
|
64
|
+
logger.debug(f"Skipping unknown driver class: {driver_name}")
|
|
69
65
|
|
|
70
66
|
return None
|
|
71
67
|
|
bumble/drivers/common.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Copyright 2021-2023 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
"""
|
|
15
|
+
Common types for drivers.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
# -----------------------------------------------------------------------------
|
|
19
|
+
# Imports
|
|
20
|
+
# -----------------------------------------------------------------------------
|
|
21
|
+
import abc
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# -----------------------------------------------------------------------------
|
|
25
|
+
# Classes
|
|
26
|
+
# -----------------------------------------------------------------------------
|
|
27
|
+
class Driver(abc.ABC):
|
|
28
|
+
"""Base class for drivers."""
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
async def for_host(_host):
|
|
32
|
+
"""Return a driver instance for a host.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
host: Host object for which a driver should be created.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
A Driver instance if a driver should be instantiated for this host, or
|
|
39
|
+
None if no driver instance of this class is needed.
|
|
40
|
+
"""
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
@abc.abstractmethod
|
|
44
|
+
async def init_controller(self):
|
|
45
|
+
"""Initialize the controller."""
|
bumble/drivers/rtk.py
CHANGED
|
@@ -41,7 +41,7 @@ from bumble.hci import (
|
|
|
41
41
|
HCI_Reset_Command,
|
|
42
42
|
HCI_Read_Local_Version_Information_Command,
|
|
43
43
|
)
|
|
44
|
-
|
|
44
|
+
from bumble.drivers import common
|
|
45
45
|
|
|
46
46
|
# -----------------------------------------------------------------------------
|
|
47
47
|
# Logging
|
|
@@ -285,7 +285,7 @@ class Firmware:
|
|
|
285
285
|
)
|
|
286
286
|
|
|
287
287
|
|
|
288
|
-
class Driver:
|
|
288
|
+
class Driver(common.Driver):
|
|
289
289
|
@dataclass
|
|
290
290
|
class DriverInfo:
|
|
291
291
|
rom: int
|
|
@@ -470,8 +470,12 @@ class Driver:
|
|
|
470
470
|
logger.debug("USB metadata not found")
|
|
471
471
|
return False
|
|
472
472
|
|
|
473
|
-
|
|
474
|
-
|
|
473
|
+
if host.hci_metadata.get('driver') == 'rtk':
|
|
474
|
+
# Forced driver
|
|
475
|
+
return True
|
|
476
|
+
|
|
477
|
+
vendor_id = host.hci_metadata.get("vendor_id")
|
|
478
|
+
product_id = host.hci_metadata.get("product_id")
|
|
475
479
|
if vendor_id is None or product_id is None:
|
|
476
480
|
logger.debug("USB metadata not sufficient")
|
|
477
481
|
return False
|
|
@@ -486,6 +490,9 @@ class Driver:
|
|
|
486
490
|
|
|
487
491
|
@classmethod
|
|
488
492
|
async def driver_info_for_host(cls, host):
|
|
493
|
+
await host.send_command(HCI_Reset_Command(), check_result=True)
|
|
494
|
+
host.ready = True # Needed to let the host know the controller is ready.
|
|
495
|
+
|
|
489
496
|
response = await host.send_command(
|
|
490
497
|
HCI_Read_Local_Version_Information_Command(), check_result=True
|
|
491
498
|
)
|
bumble/gatt.py
CHANGED
|
@@ -23,16 +23,28 @@
|
|
|
23
23
|
# Imports
|
|
24
24
|
# -----------------------------------------------------------------------------
|
|
25
25
|
from __future__ import annotations
|
|
26
|
-
import asyncio
|
|
27
26
|
import enum
|
|
28
27
|
import functools
|
|
29
28
|
import logging
|
|
30
29
|
import struct
|
|
31
|
-
from typing import
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
from typing import (
|
|
31
|
+
Callable,
|
|
32
|
+
Dict,
|
|
33
|
+
Iterable,
|
|
34
|
+
List,
|
|
35
|
+
Optional,
|
|
36
|
+
Sequence,
|
|
37
|
+
Union,
|
|
38
|
+
TYPE_CHECKING,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
from bumble.colors import color
|
|
42
|
+
from bumble.core import UUID
|
|
43
|
+
from bumble.att import Attribute, AttributeValue
|
|
44
|
+
|
|
45
|
+
if TYPE_CHECKING:
|
|
46
|
+
from bumble.gatt_client import AttributeProxy
|
|
47
|
+
from bumble.device import Connection
|
|
36
48
|
|
|
37
49
|
|
|
38
50
|
# -----------------------------------------------------------------------------
|
|
@@ -93,20 +105,35 @@ GATT_RECONNECTION_CONFIGURATION_SERVICE = UUID.from_16_bits(0x1829, 'Reconne
|
|
|
93
105
|
GATT_INSULIN_DELIVERY_SERVICE = UUID.from_16_bits(0x183A, 'Insulin Delivery')
|
|
94
106
|
GATT_BINARY_SENSOR_SERVICE = UUID.from_16_bits(0x183B, 'Binary Sensor')
|
|
95
107
|
GATT_EMERGENCY_CONFIGURATION_SERVICE = UUID.from_16_bits(0x183C, 'Emergency Configuration')
|
|
108
|
+
GATT_AUTHORIZATION_CONTROL_SERVICE = UUID.from_16_bits(0x183D, 'Authorization Control')
|
|
96
109
|
GATT_PHYSICAL_ACTIVITY_MONITOR_SERVICE = UUID.from_16_bits(0x183E, 'Physical Activity Monitor')
|
|
110
|
+
GATT_ELAPSED_TIME_SERVICE = UUID.from_16_bits(0x183F, 'Elapsed Time')
|
|
111
|
+
GATT_GENERIC_HEALTH_SENSOR_SERVICE = UUID.from_16_bits(0x1840, 'Generic Health Sensor')
|
|
97
112
|
GATT_AUDIO_INPUT_CONTROL_SERVICE = UUID.from_16_bits(0x1843, 'Audio Input Control')
|
|
98
113
|
GATT_VOLUME_CONTROL_SERVICE = UUID.from_16_bits(0x1844, 'Volume Control')
|
|
99
114
|
GATT_VOLUME_OFFSET_CONTROL_SERVICE = UUID.from_16_bits(0x1845, 'Volume Offset Control')
|
|
100
|
-
GATT_COORDINATED_SET_IDENTIFICATION_SERVICE = UUID.from_16_bits(0x1846, 'Coordinated Set Identification
|
|
115
|
+
GATT_COORDINATED_SET_IDENTIFICATION_SERVICE = UUID.from_16_bits(0x1846, 'Coordinated Set Identification')
|
|
101
116
|
GATT_DEVICE_TIME_SERVICE = UUID.from_16_bits(0x1847, 'Device Time')
|
|
102
|
-
GATT_MEDIA_CONTROL_SERVICE = UUID.from_16_bits(0x1848, 'Media Control
|
|
103
|
-
GATT_GENERIC_MEDIA_CONTROL_SERVICE = UUID.from_16_bits(0x1849, 'Generic Media Control
|
|
117
|
+
GATT_MEDIA_CONTROL_SERVICE = UUID.from_16_bits(0x1848, 'Media Control')
|
|
118
|
+
GATT_GENERIC_MEDIA_CONTROL_SERVICE = UUID.from_16_bits(0x1849, 'Generic Media Control')
|
|
104
119
|
GATT_CONSTANT_TONE_EXTENSION_SERVICE = UUID.from_16_bits(0x184A, 'Constant Tone Extension')
|
|
105
|
-
GATT_TELEPHONE_BEARER_SERVICE = UUID.from_16_bits(0x184B, 'Telephone Bearer
|
|
106
|
-
GATT_GENERIC_TELEPHONE_BEARER_SERVICE = UUID.from_16_bits(0x184C, 'Generic Telephone Bearer
|
|
120
|
+
GATT_TELEPHONE_BEARER_SERVICE = UUID.from_16_bits(0x184B, 'Telephone Bearer')
|
|
121
|
+
GATT_GENERIC_TELEPHONE_BEARER_SERVICE = UUID.from_16_bits(0x184C, 'Generic Telephone Bearer')
|
|
107
122
|
GATT_MICROPHONE_CONTROL_SERVICE = UUID.from_16_bits(0x184D, 'Microphone Control')
|
|
108
|
-
|
|
109
|
-
|
|
123
|
+
GATT_AUDIO_STREAM_CONTROL_SERVICE = UUID.from_16_bits(0x184E, 'Audio Stream Control')
|
|
124
|
+
GATT_BROADCAST_AUDIO_SCAN_SERVICE = UUID.from_16_bits(0x184F, 'Broadcast Audio Scan')
|
|
125
|
+
GATT_PUBLISHED_AUDIO_CAPABILITIES_SERVICE = UUID.from_16_bits(0x1850, 'Published Audio Capabilities')
|
|
126
|
+
GATT_BASIC_AUDIO_ANNOUNCEMENT_SERVICE = UUID.from_16_bits(0x1851, 'Basic Audio Announcement')
|
|
127
|
+
GATT_BROADCAST_AUDIO_ANNOUNCEMENT_SERVICE = UUID.from_16_bits(0x1852, 'Broadcast Audio Announcement')
|
|
128
|
+
GATT_COMMON_AUDIO_SERVICE = UUID.from_16_bits(0x1853, 'Common Audio')
|
|
129
|
+
GATT_HEARING_ACCESS_SERVICE = UUID.from_16_bits(0x1854, 'Hearing Access')
|
|
130
|
+
GATT_TELEPHONY_AND_MEDIA_AUDIO_SERVICE = UUID.from_16_bits(0x1855, 'Telephony and Media Audio')
|
|
131
|
+
GATT_PUBLIC_BROADCAST_ANNOUNCEMENT_SERVICE = UUID.from_16_bits(0x1856, 'Public Broadcast Announcement')
|
|
132
|
+
GATT_ELECTRONIC_SHELF_LABEL_SERVICE = UUID.from_16_bits(0X1857, 'Electronic Shelf Label')
|
|
133
|
+
GATT_GAMING_AUDIO_SERVICE = UUID.from_16_bits(0x1858, 'Gaming Audio')
|
|
134
|
+
GATT_MESH_PROXY_SOLICITATION_SERVICE = UUID.from_16_bits(0x1859, 'Mesh Audio Solicitation')
|
|
135
|
+
|
|
136
|
+
# Attribute Types
|
|
110
137
|
GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE = UUID.from_16_bits(0x2800, 'Primary Service')
|
|
111
138
|
GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE = UUID.from_16_bits(0x2801, 'Secondary Service')
|
|
112
139
|
GATT_INCLUDE_ATTRIBUTE_TYPE = UUID.from_16_bits(0x2802, 'Include')
|
|
@@ -129,6 +156,8 @@ GATT_ENVIRONMENTAL_SENSING_MEASUREMENT_DESCRIPTOR = UUID.from_16_bits(0x290C,
|
|
|
129
156
|
GATT_ENVIRONMENTAL_SENSING_TRIGGER_DESCRIPTOR = UUID.from_16_bits(0x290D, 'Environmental Sensing Trigger Setting')
|
|
130
157
|
GATT_TIME_TRIGGER_DESCRIPTOR = UUID.from_16_bits(0x290E, 'Time Trigger Setting')
|
|
131
158
|
GATT_COMPLETE_BR_EDR_TRANSPORT_BLOCK_DATA_DESCRIPTOR = UUID.from_16_bits(0x290F, 'Complete BR-EDR Transport Block Data')
|
|
159
|
+
GATT_OBSERVATION_SCHEDULE_DESCRIPTOR = UUID.from_16_bits(0x290F, 'Observation Schedule')
|
|
160
|
+
GATT_VALID_RANGE_AND_ACCURACY_DESCRIPTOR = UUID.from_16_bits(0x290F, 'Valid Range And Accuracy')
|
|
132
161
|
|
|
133
162
|
# Device Information Service
|
|
134
163
|
GATT_SYSTEM_ID_CHARACTERISTIC = UUID.from_16_bits(0x2A23, 'System ID')
|
|
@@ -156,6 +185,96 @@ GATT_HEART_RATE_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2A39, 'Heart
|
|
|
156
185
|
# Battery Service
|
|
157
186
|
GATT_BATTERY_LEVEL_CHARACTERISTIC = UUID.from_16_bits(0x2A19, 'Battery Level')
|
|
158
187
|
|
|
188
|
+
# Telephony And Media Audio Service (TMAS)
|
|
189
|
+
GATT_TMAP_ROLE_CHARACTERISTIC = UUID.from_16_bits(0x2B51, 'TMAP Role')
|
|
190
|
+
|
|
191
|
+
# Audio Input Control Service (AICS)
|
|
192
|
+
GATT_AUDIO_INPUT_STATE_CHARACTERISTIC = UUID.from_16_bits(0x2B77, 'Audio Input State')
|
|
193
|
+
GATT_GAIN_SETTINGS_ATTRIBUTE_CHARACTERISTIC = UUID.from_16_bits(0x2B78, 'Gain Settings Attribute')
|
|
194
|
+
GATT_AUDIO_INPUT_TYPE_CHARACTERISTIC = UUID.from_16_bits(0x2B79, 'Audio Input Type')
|
|
195
|
+
GATT_AUDIO_INPUT_STATUS_CHARACTERISTIC = UUID.from_16_bits(0x2B7A, 'Audio Input Status')
|
|
196
|
+
GATT_AUDIO_INPUT_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2B7B, 'Audio Input Control Point')
|
|
197
|
+
GATT_AUDIO_INPUT_DESCRIPTION_CHARACTERISTIC = UUID.from_16_bits(0x2B7C, 'Audio Input Description')
|
|
198
|
+
|
|
199
|
+
# Volume Control Service (VCS)
|
|
200
|
+
GATT_VOLUME_STATE_CHARACTERISTIC = UUID.from_16_bits(0x2B7D, 'Volume State')
|
|
201
|
+
GATT_VOLUME_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2B7E, 'Volume Control Point')
|
|
202
|
+
GATT_VOLUME_FLAGS_CHARACTERISTIC = UUID.from_16_bits(0x2B7F, 'Volume Flags')
|
|
203
|
+
|
|
204
|
+
# Volume Offset Control Service (VOCS)
|
|
205
|
+
GATT_VOLUME_OFFSET_STATE_CHARACTERISTIC = UUID.from_16_bits(0x2B80, 'Volume Offset State')
|
|
206
|
+
GATT_AUDIO_LOCATION_CHARACTERISTIC = UUID.from_16_bits(0x2B81, 'Audio Location')
|
|
207
|
+
GATT_VOLUME_OFFSET_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2B82, 'Volume Offset Control Point')
|
|
208
|
+
GATT_AUDIO_OUTPUT_DESCRIPTION_CHARACTERISTIC = UUID.from_16_bits(0x2B83, 'Audio Output Description')
|
|
209
|
+
|
|
210
|
+
# Coordinated Set Identification Service (CSIS)
|
|
211
|
+
GATT_SET_IDENTITY_RESOLVING_KEY_CHARACTERISTIC = UUID.from_16_bits(0x2B84, 'Set Identity Resolving Key')
|
|
212
|
+
GATT_COORDINATED_SET_SIZE_CHARACTERISTIC = UUID.from_16_bits(0x2B85, 'Coordinated Set Size')
|
|
213
|
+
GATT_SET_MEMBER_LOCK_CHARACTERISTIC = UUID.from_16_bits(0x2B86, 'Set Member Lock')
|
|
214
|
+
GATT_SET_MEMBER_RANK_CHARACTERISTIC = UUID.from_16_bits(0x2B87, 'Set Member Rank')
|
|
215
|
+
|
|
216
|
+
# Media Control Service (MCS)
|
|
217
|
+
GATT_MEDIA_PLAYER_NAME_CHARACTERISTIC = UUID.from_16_bits(0x2B93, 'Media Player Name')
|
|
218
|
+
GATT_MEDIA_PLAYER_ICON_OBJECT_ID_CHARACTERISTIC = UUID.from_16_bits(0x2B94, 'Media Player Icon Object ID')
|
|
219
|
+
GATT_MEDIA_PLAYER_ICON_URL_CHARACTERISTIC = UUID.from_16_bits(0x2B95, 'Media Player Icon URL')
|
|
220
|
+
GATT_TRACK_CHANGED_CHARACTERISTIC = UUID.from_16_bits(0x2B96, 'Track Changed')
|
|
221
|
+
GATT_TRACK_TITLE_CHARACTERISTIC = UUID.from_16_bits(0x2B97, 'Track Title')
|
|
222
|
+
GATT_TRACK_DURATION_CHARACTERISTIC = UUID.from_16_bits(0x2B98, 'Track Duration')
|
|
223
|
+
GATT_TRACK_POSITION_CHARACTERISTIC = UUID.from_16_bits(0x2B99, 'Track Position')
|
|
224
|
+
GATT_PLAYBACK_SPEED_CHARACTERISTIC = UUID.from_16_bits(0x2B9A, 'Playback Speed')
|
|
225
|
+
GATT_SEEKING_SPEED_CHARACTERISTIC = UUID.from_16_bits(0x2B9B, 'Seeking Speed')
|
|
226
|
+
GATT_CURRENT_TRACK_SEGMENTS_OBJECT_ID_CHARACTERISTIC = UUID.from_16_bits(0x2B9C, 'Current Track Segments Object ID')
|
|
227
|
+
GATT_CURRENT_TRACK_OBJECT_ID_CHARACTERISTIC = UUID.from_16_bits(0x2B9D, 'Current Track Object ID')
|
|
228
|
+
GATT_NEXT_TRACK_OBJECT_ID_CHARACTERISTIC = UUID.from_16_bits(0x2B9E, 'Next Track Object ID')
|
|
229
|
+
GATT_PARENT_GROUP_OBJECT_ID_CHARACTERISTIC = UUID.from_16_bits(0x2B9F, 'Parent Group Object ID')
|
|
230
|
+
GATT_CURRENT_GROUP_OBJECT_ID_CHARACTERISTIC = UUID.from_16_bits(0x2BA0, 'Current Group Object ID')
|
|
231
|
+
GATT_PLAYING_ORDER_CHARACTERISTIC = UUID.from_16_bits(0x2BA1, 'Playing Order')
|
|
232
|
+
GATT_PLAYING_ORDERS_SUPPORTED_CHARACTERISTIC = UUID.from_16_bits(0x2BA2, 'Playing Orders Supported')
|
|
233
|
+
GATT_MEDIA_STATE_CHARACTERISTIC = UUID.from_16_bits(0x2BA3, 'Media State')
|
|
234
|
+
GATT_MEDIA_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2BA4, 'Media Control Point')
|
|
235
|
+
GATT_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED_CHARACTERISTIC = UUID.from_16_bits(0x2BA5, 'Media Control Point Opcodes Supported')
|
|
236
|
+
GATT_SEARCH_RESULTS_OBJECT_ID_CHARACTERISTIC = UUID.from_16_bits(0x2BA6, 'Search Results Object ID')
|
|
237
|
+
GATT_SEARCH_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2BA7, 'Search Control Point')
|
|
238
|
+
GATT_CONTENT_CONTROL_ID_CHARACTERISTIC = UUID.from_16_bits(0x2BBA, 'Content Control Id')
|
|
239
|
+
|
|
240
|
+
# Telephone Bearer Service (TBS)
|
|
241
|
+
GATT_BEARER_PROVIDER_NAME_CHARACTERISTIC = UUID.from_16_bits(0x2BB4, 'Bearer Provider Name')
|
|
242
|
+
GATT_BEARER_UCI_CHARACTERISTIC = UUID.from_16_bits(0x2BB5, 'Bearer UCI')
|
|
243
|
+
GATT_BEARER_TECHNOLOGY_CHARACTERISTIC = UUID.from_16_bits(0x2BB6, 'Bearer Technology')
|
|
244
|
+
GATT_BEARER_URI_SCHEMES_SUPPORTED_LIST_CHARACTERISTIC = UUID.from_16_bits(0x2BB7, 'Bearer URI Schemes Supported List')
|
|
245
|
+
GATT_BEARER_SIGNAL_STRENGTH_CHARACTERISTIC = UUID.from_16_bits(0x2BB8, 'Bearer Signal Strength')
|
|
246
|
+
GATT_BEARER_SIGNAL_STRENGTH_REPORTING_INTERVAL_CHARACTERISTIC = UUID.from_16_bits(0x2BB9, 'Bearer Signal Strength Reporting Interval')
|
|
247
|
+
GATT_BEARER_LIST_CURRENT_CALLS_CHARACTERISTIC = UUID.from_16_bits(0x2BBA, 'Bearer List Current Calls')
|
|
248
|
+
GATT_CONTENT_CONTROL_ID_CHARACTERISTIC = UUID.from_16_bits(0x2BBB, 'Content Control ID')
|
|
249
|
+
GATT_STATUS_FLAGS_CHARACTERISTIC = UUID.from_16_bits(0x2BBC, 'Status Flags')
|
|
250
|
+
GATT_INCOMING_CALL_TARGET_BEARER_URI_CHARACTERISTIC = UUID.from_16_bits(0x2BBD, 'Incoming Call Target Bearer URI')
|
|
251
|
+
GATT_CALL_STATE_CHARACTERISTIC = UUID.from_16_bits(0x2BBE, 'Call State')
|
|
252
|
+
GATT_CALL_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2BBF, 'Call Control Point')
|
|
253
|
+
GATT_CALL_CONTROL_POINT_OPTIONAL_OPCODES_CHARACTERISTIC = UUID.from_16_bits(0x2BC0, 'Call Control Point Optional Opcodes')
|
|
254
|
+
GATT_TERMINATION_REASON_CHARACTERISTIC = UUID.from_16_bits(0x2BC1, 'Termination Reason')
|
|
255
|
+
GATT_INCOMING_CALL_CHARACTERISTIC = UUID.from_16_bits(0x2BC2, 'Incoming Call')
|
|
256
|
+
GATT_CALL_FRIENDLY_NAME_CHARACTERISTIC = UUID.from_16_bits(0x2BC3, 'Call Friendly Name')
|
|
257
|
+
|
|
258
|
+
# Microphone Control Service (MICS)
|
|
259
|
+
GATT_MUTE_CHARACTERISTIC = UUID.from_16_bits(0x2BC3, 'Mute')
|
|
260
|
+
|
|
261
|
+
# Audio Stream Control Service (ASCS)
|
|
262
|
+
GATT_SINK_ASE_CHARACTERISTIC = UUID.from_16_bits(0x2BC4, 'Sink ASE')
|
|
263
|
+
GATT_SOURCE_ASE_CHARACTERISTIC = UUID.from_16_bits(0x2BC5, 'Source ASE')
|
|
264
|
+
GATT_ASE_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2BC6, 'ASE Control Point')
|
|
265
|
+
|
|
266
|
+
# Broadcast Audio Scan Service (BASS)
|
|
267
|
+
GATT_BROADCAST_AUDIO_SCAN_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2BC7, 'Broadcast Audio Scan Control Point')
|
|
268
|
+
GATT_BROADCAST_RECEIVE_STATE_CHARACTERISTIC = UUID.from_16_bits(0x2BC8, 'Broadcast Receive State')
|
|
269
|
+
|
|
270
|
+
# Published Audio Capabilities Service (PACS)
|
|
271
|
+
GATT_SINK_PAC_CHARACTERISTIC = UUID.from_16_bits(0x2BC9, 'Sink PAC')
|
|
272
|
+
GATT_SINK_AUDIO_LOCATION_CHARACTERISTIC = UUID.from_16_bits(0x2BCA, 'Sink Audio Location')
|
|
273
|
+
GATT_SOURCE_PAC_CHARACTERISTIC = UUID.from_16_bits(0x2BCB, 'Source PAC')
|
|
274
|
+
GATT_SOURCE_AUDIO_LOCATION_CHARACTERISTIC = UUID.from_16_bits(0x2BCC, 'Source Audio Location')
|
|
275
|
+
GATT_AVAILABLE_AUDIO_CONTEXTS_CHARACTERISTIC = UUID.from_16_bits(0x2BCD, 'Available Audio Contexts')
|
|
276
|
+
GATT_SUPPORTED_AUDIO_CONTEXTS_CHARACTERISTIC = UUID.from_16_bits(0x2BCE, 'Supported Audio Contexts')
|
|
277
|
+
|
|
159
278
|
# ASHA Service
|
|
160
279
|
GATT_ASHA_SERVICE = UUID.from_16_bits(0xFDF0, 'Audio Streaming for Hearing Aid')
|
|
161
280
|
GATT_ASHA_READ_ONLY_PROPERTIES_CHARACTERISTIC = UUID('6333651e-c481-4a3e-9169-7c902aad37bb', 'ReadOnlyProperties')
|
|
@@ -177,6 +296,9 @@ GATT_BOOT_KEYBOARD_INPUT_REPORT_CHARACTERISTIC = UUID.from_16_bi
|
|
|
177
296
|
GATT_CURRENT_TIME_CHARACTERISTIC = UUID.from_16_bits(0x2A2B, 'Current Time')
|
|
178
297
|
GATT_BOOT_KEYBOARD_OUTPUT_REPORT_CHARACTERISTIC = UUID.from_16_bits(0x2A32, 'Boot Keyboard Output Report')
|
|
179
298
|
GATT_CENTRAL_ADDRESS_RESOLUTION__CHARACTERISTIC = UUID.from_16_bits(0x2AA6, 'Central Address Resolution')
|
|
299
|
+
GATT_CLIENT_SUPPORTED_FEATURES_CHARACTERISTIC = UUID.from_16_bits(0x2B29, 'Client Supported Features')
|
|
300
|
+
GATT_DATABASE_HASH_CHARACTERISTIC = UUID.from_16_bits(0x2B2A, 'Database Hash')
|
|
301
|
+
GATT_SERVER_SUPPORTED_FEATURES_CHARACTERISTIC = UUID.from_16_bits(0x2B3A, 'Server Supported Features')
|
|
180
302
|
|
|
181
303
|
# fmt: on
|
|
182
304
|
# pylint: enable=line-too-long
|
|
@@ -258,9 +380,12 @@ class TemplateService(Service):
|
|
|
258
380
|
UUID: UUID
|
|
259
381
|
|
|
260
382
|
def __init__(
|
|
261
|
-
self,
|
|
383
|
+
self,
|
|
384
|
+
characteristics: List[Characteristic],
|
|
385
|
+
primary: bool = True,
|
|
386
|
+
included_services: List[Service] = [],
|
|
262
387
|
) -> None:
|
|
263
|
-
super().__init__(self.UUID, characteristics, primary)
|
|
388
|
+
super().__init__(self.UUID, characteristics, primary, included_services)
|
|
264
389
|
|
|
265
390
|
|
|
266
391
|
# -----------------------------------------------------------------------------
|
|
@@ -409,56 +534,43 @@ class CharacteristicDeclaration(Attribute):
|
|
|
409
534
|
|
|
410
535
|
|
|
411
536
|
# -----------------------------------------------------------------------------
|
|
412
|
-
class CharacteristicValue:
|
|
413
|
-
|
|
414
|
-
Characteristic value where reading and/or writing is delegated to functions
|
|
415
|
-
passed as arguments to the constructor.
|
|
416
|
-
'''
|
|
417
|
-
|
|
418
|
-
def __init__(self, read=None, write=None):
|
|
419
|
-
self._read = read
|
|
420
|
-
self._write = write
|
|
421
|
-
|
|
422
|
-
def read(self, connection):
|
|
423
|
-
return self._read(connection) if self._read else b''
|
|
424
|
-
|
|
425
|
-
def write(self, connection, value):
|
|
426
|
-
if self._write:
|
|
427
|
-
self._write(connection, value)
|
|
537
|
+
class CharacteristicValue(AttributeValue):
|
|
538
|
+
"""Same as AttributeValue, for backward compatibility"""
|
|
428
539
|
|
|
429
540
|
|
|
430
541
|
# -----------------------------------------------------------------------------
|
|
431
542
|
class CharacteristicAdapter:
|
|
432
543
|
'''
|
|
433
|
-
An adapter that can adapt
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
`
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
544
|
+
An adapter that can adapt Characteristic and AttributeProxy objects
|
|
545
|
+
by wrapping their `read_value()` and `write_value()` methods with ones that
|
|
546
|
+
return/accept encoded/decoded values.
|
|
547
|
+
|
|
548
|
+
For proxies (i.e used by a GATT client), the adaptation is one where the return
|
|
549
|
+
value of `read_value()` is decoded and the value passed to `write_value()` is
|
|
550
|
+
encoded. The `subscribe()` method, is wrapped with one where the values are decoded
|
|
551
|
+
before being passed to the subscriber.
|
|
552
|
+
|
|
553
|
+
For local values (i.e hosted by a GATT server) the adaptation is one where the
|
|
554
|
+
return value of `read_value()` is encoded and the value passed to `write_value()`
|
|
555
|
+
is decoded.
|
|
443
556
|
'''
|
|
444
557
|
|
|
445
|
-
|
|
558
|
+
read_value: Callable
|
|
559
|
+
write_value: Callable
|
|
560
|
+
|
|
561
|
+
def __init__(self, characteristic: Union[Characteristic, AttributeProxy]):
|
|
446
562
|
self.wrapped_characteristic = characteristic
|
|
447
|
-
self.subscribers
|
|
563
|
+
self.subscribers: Dict[
|
|
564
|
+
Callable, Callable
|
|
565
|
+
] = {} # Map from subscriber to proxy subscriber
|
|
448
566
|
|
|
449
|
-
if
|
|
450
|
-
characteristic.read_value
|
|
451
|
-
) and asyncio.iscoroutinefunction(characteristic.write_value):
|
|
452
|
-
self.read_value = self.read_decoded_value
|
|
453
|
-
self.write_value = self.write_decoded_value
|
|
454
|
-
else:
|
|
567
|
+
if isinstance(characteristic, Characteristic):
|
|
455
568
|
self.read_value = self.read_encoded_value
|
|
456
569
|
self.write_value = self.write_encoded_value
|
|
457
|
-
|
|
458
|
-
|
|
570
|
+
else:
|
|
571
|
+
self.read_value = self.read_decoded_value
|
|
572
|
+
self.write_value = self.write_decoded_value
|
|
459
573
|
self.subscribe = self.wrapped_subscribe
|
|
460
|
-
|
|
461
|
-
if hasattr(self.wrapped_characteristic, 'unsubscribe'):
|
|
462
574
|
self.unsubscribe = self.wrapped_unsubscribe
|
|
463
575
|
|
|
464
576
|
def __getattr__(self, name):
|
|
@@ -477,11 +589,13 @@ class CharacteristicAdapter:
|
|
|
477
589
|
else:
|
|
478
590
|
setattr(self.wrapped_characteristic, name, value)
|
|
479
591
|
|
|
480
|
-
def read_encoded_value(self, connection):
|
|
481
|
-
return self.encode_value(
|
|
592
|
+
async def read_encoded_value(self, connection):
|
|
593
|
+
return self.encode_value(
|
|
594
|
+
await self.wrapped_characteristic.read_value(connection)
|
|
595
|
+
)
|
|
482
596
|
|
|
483
|
-
def write_encoded_value(self, connection, value):
|
|
484
|
-
return self.wrapped_characteristic.write_value(
|
|
597
|
+
async def write_encoded_value(self, connection, value):
|
|
598
|
+
return await self.wrapped_characteristic.write_value(
|
|
485
599
|
connection, self.decode_value(value)
|
|
486
600
|
)
|
|
487
601
|
|
|
@@ -616,13 +730,24 @@ class Descriptor(Attribute):
|
|
|
616
730
|
'''
|
|
617
731
|
|
|
618
732
|
def __str__(self) -> str:
|
|
733
|
+
if isinstance(self.value, bytes):
|
|
734
|
+
value_str = self.value.hex()
|
|
735
|
+
elif isinstance(self.value, CharacteristicValue):
|
|
736
|
+
value = self.value.read(None)
|
|
737
|
+
if isinstance(value, bytes):
|
|
738
|
+
value_str = value.hex()
|
|
739
|
+
else:
|
|
740
|
+
value_str = '<async>'
|
|
741
|
+
else:
|
|
742
|
+
value_str = '<...>'
|
|
619
743
|
return (
|
|
620
744
|
f'Descriptor(handle=0x{self.handle:04X}, '
|
|
621
745
|
f'type={self.type}, '
|
|
622
|
-
f'value={
|
|
746
|
+
f'value={value_str})'
|
|
623
747
|
)
|
|
624
748
|
|
|
625
749
|
|
|
750
|
+
# -----------------------------------------------------------------------------
|
|
626
751
|
class ClientCharacteristicConfigurationBits(enum.IntFlag):
|
|
627
752
|
'''
|
|
628
753
|
See Vol 3, Part G - 3.3.3.3 - Table 3.11 Client Characteristic Configuration bit
|