kevincar / bless

Cross-platform Bluetooth Low Energy Server Python Library
MIT License
113 stars 30 forks source link

bless does not advertise more then one service #142

Open ch3p4ll3 opened 2 weeks ago

ch3p4ll3 commented 2 weeks ago

The problem I made a script that need to advertise more than one service. I tried accessing it's content's by using the android app "nRF Connect" and it shows me only the first service I configured

Reproduction For the creation of the services I used a script very similar to this one:

from bless import BlessServer, GATTCharacteristicProperties, GATTAttributePermissions, BlessGATTService, BlessGATTCharacteristic

from .base_service import BaseService

SERVICE_UUID = "bfc2bc5c-39b5-41fc-be0d-e577272563e6"
CHARACTERISTIC_UUID = "698eb415-817e-41c2-a20b-6b4764d79276"

class VersionService(BaseService):
    def __init__(self, server: BlessServer):
        super().__init__(server)
        self.service: BlessGATTService = None

    async def setup(self):
        self.logger.debug(f"Setting up {self.__class__.__name__}")
        await self.server.add_new_service(SERVICE_UUID)

        char_flags = (
            GATTCharacteristicProperties.read
            | GATTCharacteristicProperties.indicate
        )
        permissions = GATTAttributePermissions.readable

        # Add the characteristic to the server
        await self.server.add_new_characteristic(
            SERVICE_UUID,
            CHARACTERISTIC_UUID,
            char_flags,
            self.__get_version(),
            permissions
        )

        self.service = self.server.get_service(SERVICE_UUID)

async def main():
    server = BlessServer(name="My-Server")

    services = [
        VersionService(),
        ServiceX(),
        ServiceY()
    ]

    for i in services:
        i.setup()

    await server.start()

All the additional services are identical to the example class I put above, if I print on screen server.services all the services seem to be present

Expected behavior That all services are displayed in the app

Screenshots Services from server.services: image

services from nRF Connect image

Desktop (please complete the following information):

kratz00 commented 2 days ago

It is really hard to give advice, if it is not easily to reproduce a problem, do you mind sharing your actual code?

  1. the constructor calls of the services are missing server
  2. in the loop i.setup() is not awaited
  3. main* is never called
  4. missing import - from .base_service import BaseService
  5. missing code for ServiceX and ServiceY
ch3p4ll3 commented 2 days ago

Sorry, I wrote the example a little quickly. Anyway I tested also with the gattserver example by adding a service and the result is always the same: only the first service is advertised

"""
Example for a BLE 4.0 Server using a GATT dictionary of services and
characteristics
"""
import sys
import logging
import asyncio
import threading

from typing import Any, Dict, Union

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

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

trigger: Union[asyncio.Event, threading.Event]
if sys.platform in ["darwin", "win32"]:
    trigger = threading.Event()
else:
    trigger = asyncio.Event()

def read_request(characteristic: BlessGATTCharacteristic, **kwargs) -> bytearray:
    logger.debug(f"Reading {characteristic.value}")
    return characteristic.value

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()

async def run(loop):
    trigger.clear()

    # Instantiate the server
    gatt: Dict = {
        "A07498CA-AD5B-474E-940D-16F1FBE7E8CD": {
            "51FF12BB-3ED8-46E5-B4F9-D64E2FEC021B": {
                "Properties": (
                    GATTCharacteristicProperties.read
                    | GATTCharacteristicProperties.write
                    | GATTCharacteristicProperties.indicate
                ),
                "Permissions": (
                    GATTAttributePermissions.readable
                    | GATTAttributePermissions.writeable
                ),
                "Value": None,
            }
        },
        "5c339364-c7be-4f23-b666-a8ff73a6a86a": {
            "bfc0c92f-317d-4ba9-976b-cc11ce77b4ca": {
                "Properties": GATTCharacteristicProperties.read,
                "Permissions": GATTAttributePermissions.readable,
                "Value": bytearray(b"\x69"),
            }
        },

        "A07498CA-AD5B-474E-940D-16F1FBE7E8CC": {
            "51FF12BB-3ED8-46E5-B4F9-D64E2FEC021C": {
                "Properties": (
                    GATTCharacteristicProperties.read
                    | GATTCharacteristicProperties.write
                    | GATTCharacteristicProperties.indicate
                ),
                "Permissions": (
                    GATTAttributePermissions.readable
                    | GATTAttributePermissions.writeable
                ),
                "Value": None,
            }
        },
        "5c339364-c7be-4f23-b666-a8ff73a6a86d": {
            "bfc0c92f-317d-4ba9-976b-cc11ce77b4cd": {
                "Properties": GATTCharacteristicProperties.read,
                "Permissions": GATTAttributePermissions.readable,
                "Value": bytearray(b"\x69"),
            }
        }
    }
    my_service_name = "Test Service"
    server = BlessServer(name=my_service_name, loop=loop)
    server.read_request_func = read_request
    server.write_request_func = write_request

    await server.add_gatt(gatt)
    await server.start()
    logger.debug(server.get_characteristic("51FF12BB-3ED8-46E5-B4F9-D64E2FEC021B"))
    logger.debug("Advertising")
    logger.info(
        "Write '0xF' to the advertised characteristic: "
        + "51FF12BB-3ED8-46E5-B4F9-D64E2FEC021B"
    )
    if trigger.__module__ == "threading":
        trigger.wait()
    else:
        await trigger.wait()
    await asyncio.sleep(2)
    logger.debug("Updating")
    server.get_characteristic("51FF12BB-3ED8-46E5-B4F9-D64E2FEC021B").value = bytearray(
        b"i"
    )
    server.update_value(
        "A07498CA-AD5B-474E-940D-16F1FBE7E8CD", "51FF12BB-3ED8-46E5-B4F9-D64E2FEC021B"
    )
    await asyncio.sleep(5)
    await server.stop()

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