mirror of
https://github.com/google/bumble.git
synced 2026-04-18 00:45:32 +00:00
format with Black
This commit is contained in:
115
bumble/gatt.py
115
bumble/gatt.py
@@ -42,6 +42,8 @@ logger = logging.getLogger(__name__)
|
||||
# -----------------------------------------------------------------------------
|
||||
# Constants
|
||||
# -----------------------------------------------------------------------------
|
||||
# fmt: off
|
||||
|
||||
GATT_REQUEST_TIMEOUT = 30 # seconds
|
||||
|
||||
GATT_MAX_ATTRIBUTE_VALUE_SIZE = 512
|
||||
@@ -174,11 +176,14 @@ GATT_CURRENT_TIME_CHARACTERISTIC = UUID.from_16_bi
|
||||
GATT_BOOT_KEYBOARD_OUTPUT_REPORT_CHARACTERISTIC = UUID.from_16_bits(0x2A32, 'Boot Keyboard Output Report')
|
||||
GATT_CENTRAL_ADDRESS_RESOLUTION__CHARACTERISTIC = UUID.from_16_bits(0x2AA6, 'Central Address Resolution')
|
||||
|
||||
# fmt: on
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Utils
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def show_services(services):
|
||||
for service in services:
|
||||
print(color(str(service), 'cyan'))
|
||||
@@ -202,14 +207,16 @@ class Service(Attribute):
|
||||
uuid = UUID(uuid)
|
||||
|
||||
super().__init__(
|
||||
GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE if primary else GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE,
|
||||
GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE
|
||||
if primary
|
||||
else GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE,
|
||||
Attribute.READABLE,
|
||||
uuid.to_pdu_bytes()
|
||||
uuid.to_pdu_bytes(),
|
||||
)
|
||||
self.uuid = uuid
|
||||
self.uuid = uuid
|
||||
self.included_services = []
|
||||
self.characteristics = characteristics[:]
|
||||
self.primary = primary
|
||||
self.characteristics = characteristics[:]
|
||||
self.primary = primary
|
||||
|
||||
def get_advertising_data(self):
|
||||
"""
|
||||
@@ -229,6 +236,7 @@ class TemplateService(Service):
|
||||
Convenience abstract class that can be used by profile-specific subclasses that want
|
||||
to expose their UUID as a class property
|
||||
'''
|
||||
|
||||
UUID = None
|
||||
|
||||
def __init__(self, characteristics, primary=True):
|
||||
@@ -242,24 +250,24 @@ class Characteristic(Attribute):
|
||||
'''
|
||||
|
||||
# Property flags
|
||||
BROADCAST = 0x01
|
||||
READ = 0x02
|
||||
WRITE_WITHOUT_RESPONSE = 0x04
|
||||
WRITE = 0x08
|
||||
NOTIFY = 0x10
|
||||
INDICATE = 0X20
|
||||
AUTHENTICATED_SIGNED_WRITES = 0X40
|
||||
EXTENDED_PROPERTIES = 0X80
|
||||
BROADCAST = 0x01
|
||||
READ = 0x02
|
||||
WRITE_WITHOUT_RESPONSE = 0x04
|
||||
WRITE = 0x08
|
||||
NOTIFY = 0x10
|
||||
INDICATE = 0x20
|
||||
AUTHENTICATED_SIGNED_WRITES = 0x40
|
||||
EXTENDED_PROPERTIES = 0x80
|
||||
|
||||
PROPERTY_NAMES = {
|
||||
BROADCAST: 'BROADCAST',
|
||||
READ: 'READ',
|
||||
WRITE_WITHOUT_RESPONSE: 'WRITE_WITHOUT_RESPONSE',
|
||||
WRITE: 'WRITE',
|
||||
NOTIFY: 'NOTIFY',
|
||||
INDICATE: 'INDICATE',
|
||||
BROADCAST: 'BROADCAST',
|
||||
READ: 'READ',
|
||||
WRITE_WITHOUT_RESPONSE: 'WRITE_WITHOUT_RESPONSE',
|
||||
WRITE: 'WRITE',
|
||||
NOTIFY: 'NOTIFY',
|
||||
INDICATE: 'INDICATE',
|
||||
AUTHENTICATED_SIGNED_WRITES: 'AUTHENTICATED_SIGNED_WRITES',
|
||||
EXTENDED_PROPERTIES: 'EXTENDED_PROPERTIES'
|
||||
EXTENDED_PROPERTIES: 'EXTENDED_PROPERTIES',
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
@@ -268,10 +276,13 @@ class Characteristic(Attribute):
|
||||
|
||||
@staticmethod
|
||||
def properties_as_string(properties):
|
||||
return ','.join([
|
||||
Characteristic.property_name(p) for p in Characteristic.PROPERTY_NAMES.keys()
|
||||
if properties & p
|
||||
])
|
||||
return ','.join(
|
||||
[
|
||||
Characteristic.property_name(p)
|
||||
for p in Characteristic.PROPERTY_NAMES.keys()
|
||||
if properties & p
|
||||
]
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def string_to_properties(properties_str: str):
|
||||
@@ -281,9 +292,16 @@ class Characteristic(Attribute):
|
||||
0,
|
||||
)
|
||||
|
||||
def __init__(self, uuid, properties, permissions, value = b'', descriptors: list[Descriptor] = []):
|
||||
def __init__(
|
||||
self,
|
||||
uuid,
|
||||
properties,
|
||||
permissions,
|
||||
value=b'',
|
||||
descriptors: list[Descriptor] = [],
|
||||
):
|
||||
super().__init__(uuid, permissions, value)
|
||||
self.uuid = self.type
|
||||
self.uuid = self.type
|
||||
if type(properties) is str:
|
||||
self.properties = Characteristic.string_to_properties(properties)
|
||||
else:
|
||||
@@ -304,25 +322,29 @@ 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)
|
||||
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.value_handle = value_handle
|
||||
self.characteristic = characteristic
|
||||
|
||||
def __str__(self):
|
||||
return f'CharacteristicDeclaration(handle=0x{self.handle:04X}, value_handle=0x{self.value_handle:04X}, uuid={self.characteristic.uuid}, properties={Characteristic.properties_as_string(self.characteristic.properties)})'
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
class CharacteristicValue:
|
||||
'''
|
||||
Characteristic value where reading and/or writing is delegated to functions
|
||||
passed as arguments to the constructor.
|
||||
'''
|
||||
|
||||
def __init__(self, read=None, write=None):
|
||||
self._read = read
|
||||
self._write = write
|
||||
@@ -349,18 +371,18 @@ class CharacteristicAdapter:
|
||||
If the characteristic has a `subscribe` method, it is wrapped with one where
|
||||
the values are decoded before being passed to the subscriber.
|
||||
'''
|
||||
|
||||
def __init__(self, characteristic):
|
||||
self.wrapped_characteristic = characteristic
|
||||
self.subscribers = {} # Map from subscriber to proxy subscriber
|
||||
|
||||
if (
|
||||
asyncio.iscoroutinefunction(characteristic.read_value) and
|
||||
asyncio.iscoroutinefunction(characteristic.write_value)
|
||||
):
|
||||
self.read_value = self.read_decoded_value
|
||||
if asyncio.iscoroutinefunction(
|
||||
characteristic.read_value
|
||||
) and asyncio.iscoroutinefunction(characteristic.write_value):
|
||||
self.read_value = self.read_decoded_value
|
||||
self.write_value = self.write_decoded_value
|
||||
else:
|
||||
self.read_value = self.read_encoded_value
|
||||
self.read_value = self.read_encoded_value
|
||||
self.write_value = self.write_encoded_value
|
||||
|
||||
if hasattr(self.wrapped_characteristic, 'subscribe'):
|
||||
@@ -379,7 +401,7 @@ class CharacteristicAdapter:
|
||||
'read_value',
|
||||
'write_value',
|
||||
'subscribe',
|
||||
'unsubscribe'
|
||||
'unsubscribe',
|
||||
}:
|
||||
super().__setattr__(name, value)
|
||||
else:
|
||||
@@ -389,15 +411,16 @@ class CharacteristicAdapter:
|
||||
return self.encode_value(self.wrapped_characteristic.read_value(connection))
|
||||
|
||||
def write_encoded_value(self, connection, value):
|
||||
return self.wrapped_characteristic.write_value(connection, self.decode_value(value))
|
||||
return self.wrapped_characteristic.write_value(
|
||||
connection, self.decode_value(value)
|
||||
)
|
||||
|
||||
async def read_decoded_value(self):
|
||||
return self.decode_value(await self.wrapped_characteristic.read_value())
|
||||
|
||||
async def write_decoded_value(self, value, with_response=False):
|
||||
return await self.wrapped_characteristic.write_value(
|
||||
self.encode_value(value),
|
||||
with_response
|
||||
self.encode_value(value), with_response
|
||||
)
|
||||
|
||||
def encode_value(self, value):
|
||||
@@ -417,6 +440,7 @@ class CharacteristicAdapter:
|
||||
|
||||
def on_change(value):
|
||||
original_subscriber(self.decode_value(value))
|
||||
|
||||
self.subscribers[subscriber] = on_change
|
||||
subscriber = on_change
|
||||
|
||||
@@ -438,6 +462,7 @@ class DelegatedCharacteristicAdapter(CharacteristicAdapter):
|
||||
'''
|
||||
Adapter that converts bytes values using an encode and a decode function.
|
||||
'''
|
||||
|
||||
def __init__(self, characteristic, encode=None, decode=None):
|
||||
super().__init__(characteristic)
|
||||
self.encode = encode
|
||||
@@ -460,6 +485,7 @@ class PackedCharacteristicAdapter(CharacteristicAdapter):
|
||||
they return/accept a tuple with the same number of elements as is required for
|
||||
the format.
|
||||
'''
|
||||
|
||||
def __init__(self, characteristic, format):
|
||||
super().__init__(characteristic)
|
||||
self.struct = struct.Struct(format)
|
||||
@@ -487,6 +513,7 @@ class MappedCharacteristicAdapter(PackedCharacteristicAdapter):
|
||||
is packed/unpacked according to format, with the arguments extracted from the dictionary
|
||||
by key, in the same order as they occur in the `keys` parameter.
|
||||
'''
|
||||
|
||||
def __init__(self, characteristic, format, keys):
|
||||
super().__init__(characteristic, format)
|
||||
self.keys = keys
|
||||
@@ -503,6 +530,7 @@ class UTF8CharacteristicAdapter(CharacteristicAdapter):
|
||||
'''
|
||||
Adapter that converts strings to/from bytes using UTF-8 encoding
|
||||
'''
|
||||
|
||||
def encode_value(self, value):
|
||||
return value.encode('utf-8')
|
||||
|
||||
@@ -516,7 +544,7 @@ class Descriptor(Attribute):
|
||||
See Vol 3, Part G - 3.3.3 Characteristic Descriptor Declarations
|
||||
'''
|
||||
|
||||
def __init__(self, descriptor_type, permissions, value = b''):
|
||||
def __init__(self, descriptor_type, permissions, value=b''):
|
||||
super().__init__(descriptor_type, permissions, value)
|
||||
|
||||
def __str__(self):
|
||||
@@ -527,6 +555,7 @@ class ClientCharacteristicConfigurationBits(enum.IntFlag):
|
||||
'''
|
||||
See Vol 3, Part G - 3.3.3.3 - Table 3.11 Client Characteristic Configuration bit field definition
|
||||
'''
|
||||
|
||||
DEFAULT = 0x0000
|
||||
NOTIFICATION = 0x0001
|
||||
INDICATION = 0x0002
|
||||
|
||||
Reference in New Issue
Block a user