kevincar / bless

Cross-platform Bluetooth Low Energy Server Python Library
MIT License
78 stars 26 forks source link

Need Notify Example and documentation #102

Open tljstewart opened 1 year ago

tljstewart commented 1 year ago

Great library, was pointed here by @dlech on bleak repo looking for a BLE server. Successfully added read and write callback, but notify callback appears to be missing in the api, is it possible to notify once the characteristic.value is updated?

tljstewart commented 1 year ago

I think I found it, you need to call server.update_value(service_uuid, char_uuid) to notify, I would of suspected a callback similar to read_request_func , write_request_func... I'd recommend an api such as on_read, on_write, on_notify .

Currently, it seems a bit strange to set the characteristic.value then call server.update_value(service_uuid, char_uuid) to notify.

Perhaps when characteristic.value is set it should call server.update_value if notify property is set and has subscribers? Just some thoughts, as I use the library

If there is a readthedocs or a readme, I'd love to read over it. Thanks

Great library, and thank you for making it MIT licensed.

JHorwitz1011 commented 1 year ago

I second the need for some documentation and a notify example! Hesitant to use this without notify being easily usable

MarkusPiotrowski commented 1 year ago

This works for me, modified from the gattserver.py example. It's not very elegant, but may serve as an example. The asyncio usage in the examples is a little bit outdated, but I didn't change it here:

"""
Example for a BLE 4.0 Notify Server using a GATT dictionary of services and
characteristics
"""

import logging
import asyncio

from typing import Any, Dict

from bless import (  # type: ignore
        BlessServer,
        BlessGATTCharacteristic,
        GATTCharacteristicProperties,
        GATTAttributePermissions
)

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(name=__name__)

def write_request(
        characteristic: BlessGATTCharacteristic,
        value: Any,
        **kwargs
        ):
    characteristic.value = value
    logger.debug(f"Char value set to {characteristic.value}")
    # if characteristic.value == b'\x0f':
        # logger.debug("Nice")
        # trigger.set()

def notify(server, value):
    server.get_characteristic(
       YOUR_NOTIFY_UUID,
    ).value = value
    server.update_value(
        YOUR_SERVICE_UUID",
        YOUR_NOTIFY_UUID"
    )

async def run(loop):
    # Instantiate the server
    gatt: Dict = {
        YOUR_SERVICE_UUID: {
            YOUR_NOTIFY_UUID: {
                "Properties": GATTCharacteristicProperties.notify,
                "Permissions": GATTAttributePermissions.readable,
                "Value": None
            },
            "YOUR_WRITE_UUID: {  # This is not required, I need it for my case
                "Properties": (GATTCharacteristicProperties.write |
                               GATTCharacteristicProperties.write_without_response),
                "Permissions": GATTAttributePermissions.writeable,
                "Value": None
            },
        }
    }

    server_name = "MY_NOTIFY_SERVER"

    server = BlessServer(name=server_name, loop=loop, name_overwrite=True)
    server.write_request_func = write_request  # not required if we don't have a write characteristic

    await server.add_gatt(gatt)
    await server.start()

    logger.debug("Waiting for someone to subscribe")
    while not await server.is_connected():  # The name of this method is slightly misleading
        await asyncio.sleep(0.1)
    logger.debug("Someone has subscribed.")
    notify(server, b'Welcome') 

    # This is the actual notifying loop:
    while True:
        a = input("Value: ")
        if a == "quit":
            break
        else:
           notify(server, a.encode())
        await asyncio.sleep(0.5)

    logger.debug("Stopping")
    await server.stop()

loop = asyncio.get_event_loop()
loop.run_until_complete(run(loop))