question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Can't shut down notification properly.

See original GitHub issue
  • bleak version: 0.6.4
  • Python version: 3.7.3
  • Operating System: RaspbianOS (Linux)
  • BlueZ version (bluetoothctl -v) in case of Linux: 5.50

Description

I’m trying to write a bleak script that will do the following:

  1. Connect to a BLE heart rate monitor, and turn on notification, and then stay alive until the program is terminated.
  2. Attempt to reconnect if the connection is lost continuously until the program is terminated.
  3. Terminate on shutdown signal from Ctrl+C

What I Did

I can connect to the device without issue and turn on notification, but I’m getting stuck at the shutdown on Ctrl+C and the reconnection. In both cases, the program seems to hang when attempting to shutdown notification.

HEART_RATE_CHARACTERISTIC_UUID = (
    "00002a37-0000-1000-8000-00805f9b34fb" # UUID of heart rate characteristic
)


class DisconnectionException(Exception):
    """Raised when the device has disconnected."""


class ShutdownException(Exception):
    """Raised when the program should shutdown."""


def ask_exit():
    for task in asyncio.Task.all_tasks():
        task.cancel()

async def stay_connected(device_address: str, loop: asyncio.AbstractEventLoop):
    while True:
        print("Starting Loop")
        try:
            print("Connecting to device.")
            await connect_and_record(device_address=device_address, loop=loop)
        except DisconnectionException as e:
            print(e)
        except ShutdownException as e:
            break
        except Exception as e:
            print(e)
            pass
        print("End of Loop Iteration")

    loop.stop()


async def connect_and_record(device_address: str, loop: asyncio.AbstractEventLoop):
    async with BleakClient(device_address, loop=loop) as client:
        def disconnect_callback(client, future):
            raise DisconnectionException("Client with address {} got disconnected!".format(client.address))

        client.set_disconnected_callback(disconnect_callback)

        print("Connected: {0}".format(await client.is_connected()))
        print("Starting notification.")
        await client.start_notify(HEART_RATE_CHARACTERISTIC_UUID, heart_rate_data_handler)
        while True:
            try:
                await asyncio.sleep(1)
            except asyncio.CancelledError:
                print("Shutdown Request Received")
                break

        print("Shutting down notification.")
        await client.stop_notify(HEART_RATE_CHARACTERISTIC_UUID)
        print("Done shutting down notification.")
        print("Shutting Down.")
        raise ShutdownException


async def run(loop: asyncio.AbstractEventLoop):

    print("Attempting to connect to device and start recording.")
    await stay_connected(device_address=client_device_config.heart_rate_sensor_address, loop=loop)
    loop.stop()


def main():

    loop = asyncio.get_event_loop()

    for sig in (signal.SIGINT, signal.SIGTERM):
        loop.add_signal_handler(sig, ask_exit)

    asyncio.ensure_future(run(loop))
    loop.run_forever()

    # I had to manually remove the handlers to
    # avoid an exception on BaseEventLoop.__del__
    for sig in (signal.SIGINT, signal.SIGTERM):
        loop.remove_signal_handler(sig)

    loop.close()
    print("Done.")


if __name__ == '__main__':
    main()

I suspect this isn’t as much an issue as operator error, but any help would be appreciated. I think this would be a really useful example to add to the examples directory, as many use cases would involve turning on notification and leaving it running and needing reconnects.

Thanks!

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:7 (2 by maintainers)

github_iconTop GitHub Comments

4reactions
GilShoshan94commented, Aug 19, 2020

@kswann-imb Hi, I took a look at your code, I am relatively new to asyncio code so I might be wrong.

  • I don’t think it is a good idea to call loop.stop() from inside the same loop.
  • You don’t need to call loop.run_forever() since your code include a while loop that does that for you already. So I would call loop.run_run_until_complete(run()) instead.
  • While you do use a context manager for your BleakClient, nothing guarantee you that await client.stop_notify(...) will be call in case of exception, and since you do call task.cancel() I don’t know exactly how this is handled but I guess it make you skip this line (why start a new co-routine if a cancel was requested ?) and get back in stay_connected and from there you quit without stopping the notification.
  • So I think it is more safe here to use global flag to indicate when to exit.

I rewrote your code but did not try it since I don’t have your device. Try it. I hope it will help you.

import asyncio
import signal

from bleak import BleakClient

HEART_RATE_CHARACTERISTIC_UUID = "00002a37-0000-1000-8000-00805f9b34fb"  # UUID of heart rate characteristic
ADDRESS = "YOUR BLE ADDRESS"
exit_flag = False


def heart_rate_data_handler(sender, data):
    print(str(data))  # Do stuff


class DisconnectionException(Exception):
    """Raised when the device has disconnected."""


class ShutdownException(Exception):
    """Raised when the program should shutdown."""


def ask_exit():
    global exit_flag
    print("Shutdown Request Received")
    exit_flag = True


async def stay_connected(device_address: str, timeout: float = 4.0):
    global exit_flag
    exit_flag = False
    print("Starting Loop")
    client = BleakClient(address=device_address, timeout=timeout)
    try:
        print("Connecting to device.")
        await client.connect()
        await notify_and_record(client)
    except DisconnectionException as e:
        print(e)
    except ShutdownException as e:
        print(e)
    except Exception as e:
        print(e)
        pass

    print("Shutting down notification.")
    await client.stop_notify(HEART_RATE_CHARACTERISTIC_UUID)
    print("Done shutting down notification.")

    print("Disconnecting to device.")
    await client.disconnect()
    print("End of Loop Iteration")


async def notify_and_record(client):
    global exit_flag

    def disconnect_callback(client, future):
        raise DisconnectionException("Client with address {} got disconnected!".format(client.address))

    client.set_disconnected_callback(disconnect_callback)

    print("Connected: {0}".format(await client.is_connected()))
    print("Starting notification.")
    await client.start_notify(HEART_RATE_CHARACTERISTIC_UUID, heart_rate_data_handler)
    while not exit_flag:
        await asyncio.sleep(1)

    print("Shutting Down.")
    raise ShutdownException


async def run():

    print("Attempting to connect to device and start recording.")
    await stay_connected(device_address=ADDRESS)


def main():

    loop = asyncio.get_event_loop()

    for sig in (signal.SIGINT, signal.SIGTERM):
        loop.add_signal_handler(sig, ask_exit)

    loop.run_until_complete(run())

    # I had to manually remove the handlers to
    # avoid an exception on BaseEventLoop.__del__
    for sig in (signal.SIGINT, signal.SIGTERM):
        loop.remove_signal_handler(sig)

    loop.stop()
    loop.close()
    print("Done.")


if __name__ == "__main__":
    main()
1reaction
kswann-imbcommented, Sep 26, 2020

Thanks @hbldh. Overall though great package. I searched for a long time before finding something that worked well for BLE.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Mac not shut down properly notification e…
Has anyone experienced this? What did you do to make the error notification go away?
Read more >
How to Fix Can't Pull Down Notification Bar! [Drop Down Menu]
Notification panel or pull down menu is not coming down or going black on your Androids Phone or there are some options missing...
Read more >
While shutting down the computer notification pop-ups saying
I never have to force it closed because it will close itself in a few seconds and Shut Down will complete. Generally the...
Read more >
How to turn off notifications in Android 11 and older
1. Swipe down from the top to expand the Notification Shade. 2. Long-press on the notification. If you’re trying to silence Gmail, for...
Read more >
How to Turn Off Notifications in Windows 10
Is it the notification sound that's primarily annoying to you? Thanks to a late-2019 update from Microsoft, you can turn off all notification ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found