Closed GilShoshan94 closed 4 years ago
Never mind, just saw to service_explorer.py
example....
I mist it.
I close this.
I appreciate the effort. If you find a better way to textually represent a BleakGATTServiceCollection
, either make a PR adding a better method to the __str__
method or as a separate "verbose" method!
Hi @hbldh ,
I ended up building from your example service_explorer.py
.
I just used f-string format the get a more consistent output.
Here is my script, look at the _get_infos(client: BleakClient)
. It could be use in the BleakGATTServiceCollection
object in the __str__
method.
If you find it good I can make the PR (I admit, I never did a PR in an open source project and don't know what it is custom to do).
def print_all_services_info(mac_addr: str, timeout: float = 2.0):
"""
Explores all the services, charateristics and descriptors of the GATT server and prints a report.
Parameters
----------
mac_addr : str
MAC Address of the BLE device.
timeout : float, optional
Timeout for required ``discover`` call, by default 2.0.
"""
async def _get_infos(client: BleakClient) -> str:
"""
Extracts the all the infos for a report.
"""
serv_collection = await client.get_services()
sp = " " # space indent
separator = f"{'_'*200}\n" # separator between services
res = separator
# `for serv in serv_collection:` is equivalent to `for serv in serv_collection.services.values():`
for serv in serv_collection:
res += f"[Service] {serv.uuid} : {serv.description}\n"
for char in serv.characteristics:
if "read" in char.properties:
try:
value = bytes(await client.read_gatt_char(char.handle))
except Exception as e:
value = str(str(e).encode())
else:
value = str(b"")
prop = f"({','.join(char.properties)})"
res += (
f"{sp}[Characteristic] {char.uuid} : (Handle: {char.handle:<3}) "
f"{prop:<42} | Name: {char.description:<40} | Value: {value}\n"
)
for desc in char.descriptors:
value = bytes(await client.read_gatt_descriptor(desc.handle))
res += (
f"{sp*2}[Descriptor] {desc.uuid} : (Handle: {desc.handle:<3}) "
f"{'':<42} | Name: {desc.description:<40} | Value: {value}\n"
)
res += separator
return res
async def print_infos(mac_addr: str, timeout: float):
async with BleakClient(mac_addr, timeout=timeout) as client:
res = await _get_infos(client)
print(res)
loop = asyncio.get_event_loop()
loop.run_until_complete(print_infos(mac_addr, timeout))
And here is the result when I tried it on my headphone:
________________________________________________________________________________________________________________________________________________________________________________________________________
[Service] 0000febe-0000-1000-8000-00805f9b34fb : Bose Corporation
[Characteristic] 9ec813b4-256b-4090-93a8-a4f0e9107733 : (Handle: 2 ) (read,notify) | Name: | Value: b'\x00\x00\x00\x00\x00\x00'
[Descriptor] 00002902-0000-1000-8000-00805f9b34fb : (Handle: 4 ) | Name: Client Characteristic Configuration | Value: b'\x00\x00'
[Characteristic] d417c028-9818-4354-99d1-2ac09d074591 : (Handle: 5 ) (read,write-without-response,write,notify) | Name: | Value: b''
[Descriptor] 00002902-0000-1000-8000-00805f9b34fb : (Handle: 7 ) | Name: Client Characteristic Configuration | Value: b'\x00\x00'
[Characteristic] c65b8f2f-aee2-4c89-b758-bc4892d6f2d8 : (Handle: 8 ) (read,write-without-response,write,notify) | Name: | Value: b'Could not get GATT characteristics for c65b8f2f-aee2-4c89-b758-bc4892d6f2d8: ProtocolError (Error: 0x05)'
[Descriptor] 00002902-0000-1000-8000-00805f9b34fb : (Handle: 10 ) | Name: Client Characteristic Configuration | Value: b'\x00\x00'
[Characteristic] 234bfbd5-e3b3-4536-a3fe-723620d4b78d : (Handle: 11 ) (write) | Name: | Value: b''
________________________________________________________________________________________________________________________________________________________________________________________________________
[Service] 00001801-0000-1000-8000-00805f9b34fb : Generic Attribute Profile
[Characteristic] 00002a05-0000-1000-8000-00805f9b34fb : (Handle: 14 ) (indicate) | Name: | Value: b''
[Descriptor] 00002902-0000-1000-8000-00805f9b34fb : (Handle: 16 ) | Name: Client Characteristic Configuration | Value: b'\x02\x00'
________________________________________________________________________________________________________________________________________________________________________________________________________
[Service] 00001800-0000-1000-8000-00805f9b34fb : Generic Access Profile
[Characteristic] 00002a00-0000-1000-8000-00805f9b34fb : (Handle: 18 ) (read) | Name: | Value: b'LE-GS Bose QuietComfort 35'
[Characteristic] 00002a01-0000-1000-8000-00805f9b34fb : (Handle: 20 ) (read) | Name: | Value: b'\x00\x00'
________________________________________________________________________________________________________________________________________________________________________________________________________
[Service] 0000180a-0000-1000-8000-00805f9b34fb : Device Information
[Characteristic] 00002a29-0000-1000-8000-00805f9b34fb : (Handle: 23 ) (read) | Name: | Value: b'Bose Corporation'
[Characteristic] 00002a24-0000-1000-8000-00805f9b34fb : (Handle: 25 ) (read) | Name: | Value: b'759944-0010'
[Characteristic] 00002a25-0000-1000-8000-00805f9b34fb : (Handle: 27 ) (read) | Name: | Value: b'072546Z62660675AE'
[Characteristic] 00002a27-0000-1000-8000-00805f9b34fb : (Handle: 29 ) (read) | Name: | Value: b'1.0.0'
[Characteristic] 00002a26-0000-1000-8000-00805f9b34fb : (Handle: 31 ) (read) | Name: | Value: b'3.0.3'
[Characteristic] 00002a28-0000-1000-8000-00805f9b34fb : (Handle: 33 ) (read) | Name: | Value: b'3.0.3'
[Characteristic] 00002a23-0000-1000-8000-00805f9b34fb : (Handle: 35 ) (read) | Name: | Value: b'\x00\x00\x00\x00\x00\x1f\xdf\x08'
[Characteristic] 00002a50-0000-1000-8000-00805f9b34fb : (Handle: 37 ) (read) | Name: | Value: b'\x01\x9e\x00\x0c@\x03\x03'
________________________________________________________________________________________________________________________________________________________________________________________________________
I still want to support Python 3.5 so I cannot use f-strings I am afraid. Apart from that, a __str__
method cannot use await
keywords, so this would in that case be a formatter/data-fetcher method and might by added to e.g. bleak/utils.py
or a new bleak/formatting.py
file.
@hbldh I forgot the f-string was introduced in 3.6. sorry about that... I tried it here with .format() . I am very new to async (I started learning it because your library use it).
We can still do a version without await
for the __str__
, we simply don't request the value (But we lose it...).
Like that (in bleak\backends\service.py
in the class BleakGATTService
):
def __str__(self) -> str:
"""
Extracts the all the infos for a report.
"""
sp = " " # space indent
separator = "{0}\n".format('_'*200) # separator between services
result = separator
for serv in self.services.values():
result += "[Service] {0} : {1}\n".format(serv.uuid, serv.description)
for char in serv.characteristics:
prop = "({0})".format(','.join(char.properties))
result += (
"{0}[Characteristic] {1} : (Handle: {2:<3}) ".format(sp, char.uuid, char.handle)
"{0:<42} | Name: {1:<40}\n".format(prop, char.description)
)
for desc in char.descriptors:
result += (
"{0}[Descriptor] {1} : (Handle: {2:<3}) ".format(sp*2, desc.uuid, desc.handle)
"{0:<42} | Name: {1:<40}\n".format('', desc.description)
)
result += separator
return result
And the result should look like that:
...
________________________________________________________________________________________________________________________________________________________________________________________________________
[Service] 00001801-0000-1000-8000-00805f9b34fb : Generic Attribute Profile
[Characteristic] 00002a05-0000-1000-8000-00805f9b34fb : (Handle: 14 ) (indicate) | Name:
[Descriptor] 00002902-0000-1000-8000-00805f9b34fb : (Handle: 16 ) | Name: Client Characteristic Configuration
________________________________________________________________________________________________________________________________________________________________________________________________________
[Service] 00001800-0000-1000-8000-00805f9b34fb : Generic Access Profile
[Characteristic] 00002a00-0000-1000-8000-00805f9b34fb : (Handle: 18 ) (read) | Name:
[Characteristic] 00002a01-0000-1000-8000-00805f9b34fb : (Handle: 20 ) (read) | Name:
________________________________________________________________________________________________________________________________________________________________________________________________________
...
I tried to convert the f-string to str.format() I did not test this code.
bluetoothctl -v
) in case of Linux: --Description
@hbldh and the other contributors. Hi, Thank you so much for this amazing library !!! Very impressive that you made a platform agnostic GATT client.
I am still messing around and was trying the examples. I found the 'get_services.py' not useful enough as it just prints:
I had to explore this object in debug mode as well as to dig a bit in the library to understand what this object has to offer. I wrote a script that explores each services, characteristics and descriptors and extracts the available information. I think it can be a good example for newcomers and wanted to contribute back.
Here's my script:
And when I tried it on my BLE headset (I had to higher the timeout to 5.0) this is the result:
Which I think is more useful. This example also shows some of the properties available.
Again thank you so much for this library. I am continuing to explore it, if it is welcomed, I can share more useful example as I am writing some utilities for myself. Let me know.