mirror of
https://github.com/google/bumble.git
synced 2026-05-10 04:18:03 +00:00
Merge pull request #80 from AlanRosenthal/alan/gatt_server_getter
Added class CharacteristicDeclaration, gatt_server getters
This commit is contained in:
@@ -283,6 +283,23 @@ class Characteristic(Attribute):
|
||||
return f'Characteristic(handle=0x{self.handle:04X}, end=0x{self.end_group_handle:04X}, uuid={self.uuid}, properties={Characteristic.properties_as_string(self.properties)})'
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
class CharacteristicDeclaration(Attribute):
|
||||
'''
|
||||
See Vol 3, Part G - 3.3.1 CHARACTERISTIC DECLARATION
|
||||
'''
|
||||
def __init__(self, characteristic, value_handle):
|
||||
declaration_bytes = struct.pack(
|
||||
'<BH',
|
||||
characteristic.properties,
|
||||
value_handle
|
||||
) + characteristic.uuid.to_pdu_bytes()
|
||||
super().__init__(GATT_CHARACTERISTIC_ATTRIBUTE_TYPE, Attribute.READABLE, declaration_bytes)
|
||||
self.characteristic = characteristic
|
||||
|
||||
def __str__(self):
|
||||
return f'CharacteristicDeclaration(handle=0x{self.handle:04X}, value_handle=0x{self.value_handle:04X}, uuid={self.uuid}, properties={Characteristic.properties_as_string(self.properties)})'
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
class CharacteristicValue:
|
||||
'''
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
import asyncio
|
||||
import logging
|
||||
from collections import defaultdict
|
||||
from typing import Tuple, Optional
|
||||
from pyee import EventEmitter
|
||||
from colors import color
|
||||
|
||||
@@ -82,6 +83,63 @@ class Server(EventEmitter):
|
||||
return attribute
|
||||
return None
|
||||
|
||||
def get_service_attribute(self, service_uuid: UUID) -> Optional[Service]:
|
||||
return next(
|
||||
(
|
||||
attribute
|
||||
for attribute in self.attributes
|
||||
if attribute.type == GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE
|
||||
and attribute.uuid == service_uuid
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
def get_characteristic_attributes(
|
||||
self, service_uuid: UUID, characteristic_uuid: UUID
|
||||
) -> Optional[Tuple[CharacteristicDeclaration, Characteristic]]:
|
||||
service_handle = self.get_service_attribute(service_uuid)
|
||||
if not service_handle:
|
||||
return None
|
||||
|
||||
return next(
|
||||
(
|
||||
(attribute, self.get_attribute(attribute.characteristic.handle))
|
||||
for attribute in map(
|
||||
self.get_attribute,
|
||||
range(service_handle.handle, service_handle.end_group_handle + 1),
|
||||
)
|
||||
if attribute.type == GATT_CHARACTERISTIC_ATTRIBUTE_TYPE
|
||||
and attribute.characteristic.uuid == characteristic_uuid
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
def get_descriptor_attribute(
|
||||
self, service_uuid: UUID, characteristic_uuid: UUID, descriptor_uuid: UUID
|
||||
) -> Optional[Descriptor]:
|
||||
characteristics = self.get_characteristic_attributes(
|
||||
service_uuid, characteristic_uuid
|
||||
)
|
||||
if not characteristics:
|
||||
return None
|
||||
|
||||
(_, characteristic_value) = characteristics
|
||||
|
||||
return next(
|
||||
(
|
||||
attribute
|
||||
for attribute in map(
|
||||
self.get_attribute,
|
||||
range(
|
||||
characteristic_value.handle + 1,
|
||||
characteristic_value.end_group_handle + 1,
|
||||
),
|
||||
)
|
||||
if attribute.type == descriptor_uuid
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
def add_attribute(self, attribute):
|
||||
# Assign a handle to this attribute
|
||||
attribute.handle = self.next_handle()
|
||||
@@ -98,16 +156,9 @@ class Server(EventEmitter):
|
||||
|
||||
# Add all characteristics
|
||||
for characteristic in service.characteristics:
|
||||
# Add a Characteristic Declaration (Vol 3, Part G - 3.3.1 Characteristic Declaration)
|
||||
declaration_bytes = struct.pack(
|
||||
'<BH',
|
||||
characteristic.properties,
|
||||
self.next_handle() + 1, # The value will be the next attribute after this declaration
|
||||
) + characteristic.uuid.to_pdu_bytes()
|
||||
characteristic_declaration = Attribute(
|
||||
GATT_CHARACTERISTIC_ATTRIBUTE_TYPE,
|
||||
Attribute.READABLE,
|
||||
declaration_bytes
|
||||
# Add a Characteristic Declaration
|
||||
characteristic_declaration = CharacteristicDeclaration(
|
||||
characteristic, self.next_handle() + 1
|
||||
)
|
||||
self.add_attribute(characteristic_declaration)
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ from bumble.device import Device, Peer
|
||||
from bumble.host import Host
|
||||
from bumble.gatt import (
|
||||
GATT_BATTERY_LEVEL_CHARACTERISTIC,
|
||||
GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR,
|
||||
CharacteristicAdapter,
|
||||
DelegatedCharacteristicAdapter,
|
||||
PackedCharacteristicAdapter,
|
||||
@@ -226,6 +227,37 @@ async def test_characteristic_encoding():
|
||||
assert last_change is None
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@pytest.mark.asyncio
|
||||
async def test_attribute_getters():
|
||||
[client, server] = LinkedDevices().devices[:2]
|
||||
|
||||
characteristic_uuid = UUID('FDB159DB-036C-49E3-B3DB-6325AC750806')
|
||||
characteristic = Characteristic(
|
||||
characteristic_uuid,
|
||||
Characteristic.READ | Characteristic.WRITE | Characteristic.NOTIFY,
|
||||
Characteristic.READABLE | Characteristic.WRITEABLE,
|
||||
bytes([123])
|
||||
)
|
||||
|
||||
service_uuid = UUID('3A657F47-D34F-46B3-B1EC-698E29B6B829')
|
||||
service = Service(service_uuid, [characteristic])
|
||||
server.add_service(service)
|
||||
|
||||
service_attr = server.gatt_server.get_service_attribute(service_uuid)
|
||||
assert service_attr
|
||||
|
||||
(char_decl_attr, char_value_attr) = server.gatt_server.get_characteristic_attributes(service_uuid, characteristic_uuid)
|
||||
assert char_decl_attr and char_value_attr
|
||||
|
||||
desc_attr = server.gatt_server.get_descriptor_attribute(service_uuid, characteristic_uuid, GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR)
|
||||
assert desc_attr
|
||||
|
||||
# assert all handles are in expected order
|
||||
assert service_attr.handle < char_decl_attr.handle < char_value_attr.handle < desc_attr.handle == service_attr.end_group_handle
|
||||
# assert characteristic declarations attribute is followed by characteristic value attribute
|
||||
assert char_decl_attr.handle + 1 == char_value_attr.handle
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
def test_CharacteristicAdapter():
|
||||
# Check that the CharacteristicAdapter base class is transparent
|
||||
|
||||
Reference in New Issue
Block a user