Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

On MacOS notifications stream freezes forever when computer locked #387

Open
dmtrKovalenko opened this issue May 23, 2024 · 0 comments
Open
Labels
bug Something isn't working corebluetooth (macos/ios) Issues related to the MacOS/iOS CoreBluetooth impl

Comments

@dmtrKovalenko
Copy link

With the basic provided example for notification stream

use btleplug::api::{Central, CharPropFlags, Manager as _, Peripheral, ScanFilter};
use btleplug::platform::Manager;
use futures::stream::StreamExt;
use std::error::Error;
use std::time::Duration;
use tokio::time;
use uuid::Uuid;

/// Only devices whose name contains this string will be tried.
const PERIPHERAL_NAME_MATCH_FILTER: &str = "CO2CICKA Sensor";
/// UUID of the characteristic for which we should subscribe to notifications.
const NOTIFY_CHARACTERISTIC_UUID: Uuid = Uuid::from_u128(0x0000FFE1_0000_1000_8000_00805F9B34FB);

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    pretty_env_logger::init();

    let manager = Manager::new().await?;
    let adapter_list = manager.adapters().await?;
    if adapter_list.is_empty() {
        eprintln!("No Bluetooth adapters found");
    }

    for adapter in adapter_list.iter() {
        println!("Starting scan...");
        adapter
            .start_scan(ScanFilter::default())
            .await
            .expect("Can't scan BLE adapter for connected devices...");
        time::sleep(Duration::from_secs(2)).await;
        let peripherals = adapter.peripherals().await?;

        if peripherals.is_empty() {
            eprintln!("->>> BLE peripheral devices were not found, sorry. Exiting...");
        } else {
            // All peripheral devices in range.
            for peripheral in peripherals.iter() {
                let properties = peripheral.properties().await?;
                let is_connected = peripheral.is_connected().await?;
                let local_name = properties
                    .unwrap()
                    .local_name
                    .unwrap_or(String::from("(peripheral name unknown)"));
                println!(
                    "Peripheral {:?} is connected: {:?}",
                    &local_name, is_connected
                );
                // Check if it's the peripheral we want.
                if local_name.contains(PERIPHERAL_NAME_MATCH_FILTER) {
                    println!("Found matching peripheral {:?}...", &local_name);
                    if !is_connected {
                        // Connect if we aren't already connected.
                        if let Err(err) = peripheral.connect().await {
                            eprintln!("Error connecting to peripheral, skipping: {}", err);
                            continue;
                        }
                    }
                    let is_connected = peripheral.is_connected().await?;
                    println!(
                        "Now connected ({:?}) to peripheral {:?}.",
                        is_connected, &local_name
                    );
                    if is_connected {
                        println!("Discover peripheral {:?} services...", local_name);
                        peripheral.discover_services().await?;
                        for characteristic in peripheral.characteristics() {
                            println!("Checking characteristic {:?}", characteristic);
                            // Subscribe to notifications from the characteristic with the selected
                            // UUID.
                            if characteristic.uuid == NOTIFY_CHARACTERISTIC_UUID
                                && characteristic.properties.contains(CharPropFlags::NOTIFY)
                            {
                                println!("Subscribing to characteristic {:?}", characteristic.uuid);
                                peripheral.subscribe(&characteristic).await?;
                                // Print the first 4 notifications received.
                                let mut notification_stream = peripheral.notifications().await?;

                                // Process while the BLE connection is not broken or stopped.
                                while let Some(data) = notification_stream.next().await {
                                    println!(
                                        "Received data from {:?} [{:?}]: {:?}",
                                        local_name, data.uuid, data.value
                                    );
                                }
                            }
                        }
                        println!("Disconnecting from peripheral {:?}...", local_name);
                        peripheral.disconnect().await?;
                    }
                } else {
                    println!("Skipping unknown peripheral {:?}", peripheral);
                }
            }
        }
    }
    Ok(())
}

If I remove the .take(4) everything works fine and stream lasts forever unless the computer is locked. Then the connection is dying on the blte plug side (but not at the peripheral side) and peripheral is not scannable anymore until the whole program is killed.

I tried to add timeouts like

        while let Some(data) = timeout(TIMEOUT, notification_stream.next()).await? {
            tracing::debug!("Received data from sensor {data:?}");
            match TData::from_bytes(data.value) {
                Ok(data) => fun(data),
                Err(e) => tracing::error!("Error decodring data from sensor {}", e),
            }

            let is_connected = timeout(TIMEOUT, self.peripheral.is_connected())
                .await
                .map_err(|_| "Connection lost")??;

            if !is_connected {
                return Err("BLE connection was lost".into());
            }
        }

but it doesn't help

@dmtrKovalenko dmtrKovalenko added the bug Something isn't working label May 23, 2024
@dmtrKovalenko dmtrKovalenko changed the title On MacOS notifications stream is freezes forever when computer locked On MacOS notifications stream freezes forever when computer locked May 23, 2024
@qwandor qwandor added the corebluetooth (macos/ios) Issues related to the MacOS/iOS CoreBluetooth impl label May 23, 2024
dmtrKovalenko added a commit to dmtrKovalenko/btleplug that referenced this issue May 24, 2024
closes deviceplug#387

In Core Bluetooth when the device is not applicable for background
bluetooth manager will create an event for state change. And then change
the manager state to power off. Currently, it is not tracked at all which
leads to the forever stuck unresolved issues while the connection to
peripheral is still held.

An additional problem I faced that there is no way to manually kill the
event loop of the corebluetooth from outside so the
`CoreBluetoothInternal::drop` is never called because it is always
living in the stalled thread.

In this change, I added an API to access the manager state and exited
the event loop when if the manager turned off.
dmtrKovalenko added a commit to dmtrKovalenko/btleplug that referenced this issue May 24, 2024
closes deviceplug#387

In Core Bluetooth when the device is not applicable for background
bluetooth manager will create an event for state change. And then change
the manager state to power off. Currently, it is not tracked at all which
leads to the forever stuck unresolved issues while the connection to
peripheral is still held.

An additional problem I faced that there is no way to manually kill the
event loop of the corebluetooth from outside so the
`CoreBluetoothInternal::drop` is never called because it is always
living in the stalled thread.

In this change, I added an API to access the manager state and exited
the event loop when if the manager turned off.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working corebluetooth (macos/ios) Issues related to the MacOS/iOS CoreBluetooth impl
Projects
None yet
Development

No branches or pull requests

2 participants