mirror of
https://github.com/google/bumble.git
synced 2026-06-10 09:02:27 +00:00
+40
-23
@@ -50,12 +50,7 @@ from bumble.hci import (
|
||||
HCI_Error,
|
||||
HCI_Packet,
|
||||
)
|
||||
from bumble.gatt import (
|
||||
GATT_GENERIC_ACCESS_SERVICE,
|
||||
GATT_CHARACTERISTIC_ATTRIBUTE_TYPE,
|
||||
GATT_DEVICE_NAME_CHARACTERISTIC,
|
||||
GATT_APPEARANCE_CHARACTERISTIC,
|
||||
)
|
||||
from bumble import gatt
|
||||
|
||||
from .test_utils import TwoDevices, async_barrier
|
||||
|
||||
@@ -592,32 +587,54 @@ async def test_power_on_default_static_address_should_not_be_any():
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
def test_gatt_services_with_gas():
|
||||
def test_gatt_services_with_gas_and_gatt():
|
||||
device = Device(host=Host(None, None))
|
||||
|
||||
# there should be one service and two chars, therefore 5 attributes
|
||||
assert len(device.gatt_server.attributes) == 5
|
||||
assert device.gatt_server.attributes[0].uuid == GATT_GENERIC_ACCESS_SERVICE
|
||||
assert device.gatt_server.attributes[1].type == GATT_CHARACTERISTIC_ATTRIBUTE_TYPE
|
||||
assert device.gatt_server.attributes[2].uuid == GATT_DEVICE_NAME_CHARACTERISTIC
|
||||
assert device.gatt_server.attributes[3].type == GATT_CHARACTERISTIC_ATTRIBUTE_TYPE
|
||||
assert device.gatt_server.attributes[4].uuid == GATT_APPEARANCE_CHARACTERISTIC
|
||||
# there should be 2 service, 5 chars, and 1 descriptors, therefore 13 attributes
|
||||
assert len(device.gatt_server.attributes) == 13
|
||||
assert device.gatt_server.attributes[0].uuid == gatt.GATT_GENERIC_ACCESS_SERVICE
|
||||
assert (
|
||||
device.gatt_server.attributes[1].type == gatt.GATT_CHARACTERISTIC_ATTRIBUTE_TYPE
|
||||
)
|
||||
assert device.gatt_server.attributes[2].uuid == gatt.GATT_DEVICE_NAME_CHARACTERISTIC
|
||||
assert (
|
||||
device.gatt_server.attributes[3].type == gatt.GATT_CHARACTERISTIC_ATTRIBUTE_TYPE
|
||||
)
|
||||
assert device.gatt_server.attributes[4].uuid == gatt.GATT_APPEARANCE_CHARACTERISTIC
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
def test_gatt_services_without_gas():
|
||||
device = Device(host=Host(None, None), generic_access_service=False)
|
||||
|
||||
# there should be no services
|
||||
assert len(device.gatt_server.attributes) == 0
|
||||
assert device.gatt_server.attributes[5].uuid == gatt.GATT_GENERIC_ATTRIBUTE_SERVICE
|
||||
assert (
|
||||
device.gatt_server.attributes[6].type == gatt.GATT_CHARACTERISTIC_ATTRIBUTE_TYPE
|
||||
)
|
||||
assert (
|
||||
device.gatt_server.attributes[7].uuid
|
||||
== gatt.GATT_SERVICE_CHANGED_CHARACTERISTIC
|
||||
)
|
||||
assert (
|
||||
device.gatt_server.attributes[8].type
|
||||
== gatt.GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR
|
||||
)
|
||||
assert (
|
||||
device.gatt_server.attributes[9].type == gatt.GATT_CHARACTERISTIC_ATTRIBUTE_TYPE
|
||||
)
|
||||
assert (
|
||||
device.gatt_server.attributes[10].uuid
|
||||
== gatt.GATT_CLIENT_SUPPORTED_FEATURES_CHARACTERISTIC
|
||||
)
|
||||
assert (
|
||||
device.gatt_server.attributes[11].type
|
||||
== gatt.GATT_CHARACTERISTIC_ATTRIBUTE_TYPE
|
||||
)
|
||||
assert (
|
||||
device.gatt_server.attributes[12].uuid == gatt.GATT_DATABASE_HASH_CHARACTERISTIC
|
||||
)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
async def run_test_device():
|
||||
await test_device_connect_parallel()
|
||||
await test_flush()
|
||||
await test_gatt_services_with_gas()
|
||||
await test_gatt_services_without_gas()
|
||||
await test_gatt_services_with_gas_and_gatt()
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
# Copyright 2021-2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Imports
|
||||
# -----------------------------------------------------------------------------
|
||||
from __future__ import annotations
|
||||
|
||||
from . import test_utils
|
||||
|
||||
from bumble import device
|
||||
from bumble import gatt
|
||||
from bumble.profiles import gatt_service
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
async def test_database_hash():
|
||||
devices = await test_utils.TwoDevices.create_with_connection()
|
||||
devices[0].gatt_server.services.clear()
|
||||
devices[0].gatt_server.attributes.clear()
|
||||
devices[0].gatt_server.attributes_by_handle.clear()
|
||||
devices[0].add_service(
|
||||
gatt.Service(
|
||||
gatt.GATT_GENERIC_ACCESS_SERVICE,
|
||||
characteristics=[
|
||||
gatt.Characteristic(
|
||||
gatt.GATT_DEVICE_NAME_CHARACTERISTIC,
|
||||
(
|
||||
gatt.Characteristic.Properties.READ
|
||||
| gatt.Characteristic.Properties.WRITE
|
||||
),
|
||||
gatt.Characteristic.Permissions.READ_REQUIRES_AUTHENTICATION,
|
||||
),
|
||||
gatt.Characteristic(
|
||||
gatt.GATT_APPEARANCE_CHARACTERISTIC,
|
||||
gatt.Characteristic.Properties.READ,
|
||||
gatt.Characteristic.Permissions.READ_REQUIRES_AUTHENTICATION,
|
||||
),
|
||||
],
|
||||
)
|
||||
)
|
||||
devices[0].add_service(
|
||||
gatt_service.GenericAttributeProfileService(
|
||||
server_supported_features=None,
|
||||
database_hash_enabled=True,
|
||||
service_change_enabled=True,
|
||||
)
|
||||
)
|
||||
devices[0].gatt_server.add_attribute(
|
||||
gatt.Service(gatt.GATT_GLUCOSE_SERVICE, characteristics=[])
|
||||
)
|
||||
# There is a special attribute order in the spec, so we need to add attribute one by
|
||||
# one here.
|
||||
battery_service = gatt.Service(
|
||||
gatt.GATT_BATTERY_SERVICE,
|
||||
characteristics=[
|
||||
gatt.Characteristic(
|
||||
gatt.GATT_BATTERY_LEVEL_CHARACTERISTIC,
|
||||
properties=gatt.Characteristic.Properties.READ,
|
||||
permissions=gatt.Characteristic.Permissions.READ_REQUIRES_AUTHENTICATION,
|
||||
)
|
||||
],
|
||||
primary=False,
|
||||
)
|
||||
battery_service.handle = 0x0014
|
||||
battery_service.end_group_handle = 0x0016
|
||||
devices[0].gatt_server.add_attribute(
|
||||
gatt.IncludedServiceDeclaration(battery_service)
|
||||
)
|
||||
c = gatt.Characteristic(
|
||||
'2A18',
|
||||
properties=(
|
||||
gatt.Characteristic.Properties.READ
|
||||
| gatt.Characteristic.Properties.INDICATE
|
||||
| gatt.Characteristic.Properties.EXTENDED_PROPERTIES
|
||||
),
|
||||
permissions=gatt.Characteristic.Permissions.READ_REQUIRES_AUTHENTICATION,
|
||||
)
|
||||
devices[0].gatt_server.add_attribute(
|
||||
gatt.CharacteristicDeclaration(c, devices[0].gatt_server.next_handle() + 1)
|
||||
)
|
||||
devices[0].gatt_server.add_attribute(c)
|
||||
devices[0].gatt_server.add_attribute(
|
||||
gatt.Descriptor(
|
||||
gatt.GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR,
|
||||
gatt.Descriptor.Permissions.READ_REQUIRES_AUTHENTICATION,
|
||||
b'\x02\x00',
|
||||
),
|
||||
)
|
||||
devices[0].gatt_server.add_attribute(
|
||||
gatt.Descriptor(
|
||||
gatt.GATT_CHARACTERISTIC_EXTENDED_PROPERTIES_DESCRIPTOR,
|
||||
gatt.Descriptor.Permissions.READ_REQUIRES_AUTHENTICATION,
|
||||
b'\x00\x00',
|
||||
),
|
||||
)
|
||||
devices[0].add_service(battery_service)
|
||||
|
||||
peer = device.Peer(devices.connections[1])
|
||||
client = await peer.discover_service_and_create_proxy(
|
||||
gatt_service.GenericAttributeProfileServiceProxy
|
||||
)
|
||||
assert client.database_hash_characteristic
|
||||
assert await client.database_hash_characteristic.read_value() == bytes.fromhex(
|
||||
'F1CA2D48ECF58BAC8A8830BBB9FBA990'
|
||||
)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
async def test_service_changed():
|
||||
devices = await test_utils.TwoDevices.create_with_connection()
|
||||
assert (service := devices[0].gatt_service)
|
||||
|
||||
peer = device.Peer(devices.connections[1])
|
||||
assert (
|
||||
client := await peer.discover_service_and_create_proxy(
|
||||
gatt_service.GenericAttributeProfileServiceProxy
|
||||
)
|
||||
)
|
||||
assert client.service_changed_characteristic
|
||||
indications = []
|
||||
await client.service_changed_characteristic.subscribe(
|
||||
indications.append, prefer_notify=False
|
||||
)
|
||||
await devices[0].indicate_subscribers(
|
||||
service.service_changed_characteristic, b'1234'
|
||||
)
|
||||
await test_utils.async_barrier()
|
||||
assert indications[0] == b'1234'
|
||||
+17
-8
@@ -957,11 +957,12 @@ async def test_discover_all():
|
||||
peer = Peer(connection)
|
||||
|
||||
await peer.discover_all()
|
||||
assert len(peer.gatt_client.services) == 3
|
||||
# service 1800 gets added automatically
|
||||
assert len(peer.gatt_client.services) == 4
|
||||
# service 1800 and 1801 get added automatically
|
||||
assert peer.gatt_client.services[0].uuid == UUID('1800')
|
||||
assert peer.gatt_client.services[1].uuid == service1.uuid
|
||||
assert peer.gatt_client.services[2].uuid == service2.uuid
|
||||
assert peer.gatt_client.services[1].uuid == UUID('1801')
|
||||
assert peer.gatt_client.services[2].uuid == service1.uuid
|
||||
assert peer.gatt_client.services[3].uuid == service2.uuid
|
||||
s = peer.get_services_by_uuid(service1.uuid)
|
||||
assert len(s) == 1
|
||||
assert len(s[0].characteristics) == 2
|
||||
@@ -1084,10 +1085,18 @@ CharacteristicDeclaration(handle=0x0002, value_handle=0x0003, uuid=UUID-16:2A00
|
||||
Characteristic(handle=0x0003, end=0x0003, uuid=UUID-16:2A00 (Device Name), READ)
|
||||
CharacteristicDeclaration(handle=0x0004, value_handle=0x0005, uuid=UUID-16:2A01 (Appearance), READ)
|
||||
Characteristic(handle=0x0005, end=0x0005, uuid=UUID-16:2A01 (Appearance), READ)
|
||||
Service(handle=0x0006, end=0x0009, uuid=3A657F47-D34F-46B3-B1EC-698E29B6B829)
|
||||
CharacteristicDeclaration(handle=0x0007, value_handle=0x0008, uuid=FDB159DB-036C-49E3-B3DB-6325AC750806, READ|WRITE|NOTIFY)
|
||||
Characteristic(handle=0x0008, end=0x0009, uuid=FDB159DB-036C-49E3-B3DB-6325AC750806, READ|WRITE|NOTIFY)
|
||||
Descriptor(handle=0x0009, type=UUID-16:2902 (Client Characteristic Configuration), value=0000)"""
|
||||
Service(handle=0x0006, end=0x000D, uuid=UUID-16:1801 (Generic Attribute))
|
||||
CharacteristicDeclaration(handle=0x0007, value_handle=0x0008, uuid=UUID-16:2A05 (Service Changed), INDICATE)
|
||||
Characteristic(handle=0x0008, end=0x0009, uuid=UUID-16:2A05 (Service Changed), INDICATE)
|
||||
Descriptor(handle=0x0009, type=UUID-16:2902 (Client Characteristic Configuration), value=0000)
|
||||
CharacteristicDeclaration(handle=0x000A, value_handle=0x000B, uuid=UUID-16:2B29 (Client Supported Features), READ|WRITE)
|
||||
Characteristic(handle=0x000B, end=0x000B, uuid=UUID-16:2B29 (Client Supported Features), READ|WRITE)
|
||||
CharacteristicDeclaration(handle=0x000C, value_handle=0x000D, uuid=UUID-16:2B2A (Database Hash), READ)
|
||||
Characteristic(handle=0x000D, end=0x000D, uuid=UUID-16:2B2A (Database Hash), READ)
|
||||
Service(handle=0x000E, end=0x0011, uuid=3A657F47-D34F-46B3-B1EC-698E29B6B829)
|
||||
CharacteristicDeclaration(handle=0x000F, value_handle=0x0010, uuid=FDB159DB-036C-49E3-B3DB-6325AC750806, READ|WRITE|NOTIFY)
|
||||
Characteristic(handle=0x0010, end=0x0011, uuid=FDB159DB-036C-49E3-B3DB-6325AC750806, READ|WRITE|NOTIFY)
|
||||
Descriptor(handle=0x0011, type=UUID-16:2902 (Client Characteristic Configuration), value=0000)"""
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user