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

Feat: CentralState #366

Merged
merged 5 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions examples/event_driven_discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
// connect to the adapter
let central = get_central(&manager).await;

let central_state = central.adapter_state().await.unwrap();
println!("CentralState: {:?}", central_state);

// Each adapter has an event stream, we fetch via events(),
// simplifying the type, this will return what is essentially a
// Future<Result<Stream<Item=CentralEvent>>>.
Expand All @@ -45,6 +48,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
.unwrap_or_default();
println!("DeviceDiscovered: {:?} {}", id, name);
}
CentralEvent::StateUpdate(state) => {
println!("AdapterStatusUpdate {:?}", state);
}
CentralEvent::DeviceConnected(id) => {
println!("DeviceConnected: {:?}", id);
}
Expand Down
17 changes: 17 additions & 0 deletions src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,19 @@ pub trait Peripheral: Send + Sync + Clone + Debug {
async fn read_descriptor(&self, descriptor: &Descriptor) -> Result<Vec<u8>>;
}

#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_cr")
)]
/// The state of the Central
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum CentralState {
Unknown = 0,
PoweredOn = 1,
PoweredOff = 2,
}

#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
Expand All @@ -321,6 +334,7 @@ pub enum CentralEvent {
id: PeripheralId,
services: Vec<Uuid>,
},
StateUpdate(CentralState),
}

/// Central is the "client" of BLE. It's able to scan for and establish connections to peripherals.
Expand Down Expand Up @@ -360,6 +374,9 @@ pub trait Central: Send + Sync + Clone {
/// The details of this are platform-specific andyou should not attempt to parse it, but it may
/// be useful for debug logs.
async fn adapter_info(&self) -> Result<String>;

/// Get information about the Bluetooth adapter state.
async fn adapter_state(&self) -> Result<CentralState>;
}

/// The Manager is the entry point to the library, providing access to all the Bluetooth adapters on
Expand Down
31 changes: 28 additions & 3 deletions src/bluez/adapter.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use super::peripheral::{Peripheral, PeripheralId};
use crate::api::{Central, CentralEvent, ScanFilter};
use crate::api::{Central, CentralEvent, CentralState, ScanFilter};
use crate::{Error, Result};
use async_trait::async_trait;
use bluez_async::{
AdapterId, BluetoothError, BluetoothEvent, BluetoothSession, DeviceEvent, DiscoveryFilter,
Transport,
AdapterEvent, AdapterId, BluetoothError, BluetoothEvent, BluetoothSession, DeviceEvent,
DiscoveryFilter, Transport,
};
use futures::stream::{self, Stream, StreamExt};
use std::pin::Pin;
Expand All @@ -22,6 +22,13 @@ impl Adapter {
}
}

fn get_central_state(powered: bool) -> CentralState {
match powered {
true => CentralState::PoweredOn,
false => CentralState::PoweredOff,
}
}

#[async_trait]
impl Central for Adapter {
type Peripheral = Peripheral;
Expand Down Expand Up @@ -105,6 +112,14 @@ impl Central for Adapter {
let adapter_info = self.session.get_adapter_info(&self.adapter).await?;
Ok(format!("{} ({})", adapter_info.id, adapter_info.modalias))
}

async fn adapter_state(&self) -> Result<CentralState> {
let mut powered = false;
if let Ok(info) = self.session.get_adapter_info(&self.adapter).await {
powered = info.powered;
}
Ok(get_central_state(powered))
}
}

impl From<BluetoothError> for Error {
Expand Down Expand Up @@ -162,6 +177,16 @@ async fn central_event(
}
_ => None,
},
BluetoothEvent::Adapter {
id,
event: adapter_event,
} if id == adapter_id => match adapter_event {
AdapterEvent::Powered { powered } => {
let state = get_central_state(powered);
Some(CentralEvent::StateUpdate(state))
}
_ => None,
},
_ => None,
}
}
41 changes: 37 additions & 4 deletions src/corebluetooth/adapter.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
use super::internal::{run_corebluetooth_thread, CoreBluetoothEvent, CoreBluetoothMessage};
use super::internal::{
run_corebluetooth_thread, CoreBluetoothEvent, CoreBluetoothMessage, CoreBluetoothReply,
CoreBluetoothReplyFuture,
};
use super::peripheral::{Peripheral, PeripheralId};
use crate::api::{Central, CentralEvent, ScanFilter};
use crate::api::{Central, CentralEvent, CentralState, ScanFilter};
use crate::common::adapter_manager::AdapterManager;
use crate::{Error, Result};
use async_trait::async_trait;
use futures::channel::mpsc::{self, Sender};
use futures::sink::SinkExt;
use futures::stream::{Stream, StreamExt};
use log::*;
use objc2_core_bluetooth::CBManagerState;
use std::pin::Pin;
use std::sync::Arc;
use tokio::task;
Expand All @@ -19,6 +23,14 @@ pub struct Adapter {
sender: Sender<CoreBluetoothMessage>,
}

fn get_central_state(state: CBManagerState) -> CentralState {
match state {
CBManagerState::PoweredOn => CentralState::PoweredOn,
CBManagerState::PoweredOff => CentralState::PoweredOff,
_ => CentralState::Unknown,
}
}

impl Adapter {
pub(crate) async fn new() -> Result<Self> {
let (sender, mut receiver) = mpsc::channel(256);
Expand All @@ -29,7 +41,7 @@ impl Adapter {
debug!("Waiting on adapter connect");
if !matches!(
receiver.next().await,
Some(CoreBluetoothEvent::AdapterConnected)
Some(CoreBluetoothEvent::DidUpdateState { state: _ })
) {
return Err(Error::Other(
"Adapter failed to connect.".to_string().into(),
Expand Down Expand Up @@ -67,7 +79,10 @@ impl Adapter {
CoreBluetoothEvent::DeviceDisconnected { uuid } => {
manager_clone.emit(CentralEvent::DeviceDisconnected(uuid.into()));
}
_ => {}
CoreBluetoothEvent::DidUpdateState { state } => {
let central_state = get_central_state(state);
manager_clone.emit(CentralEvent::StateUpdate(central_state));
}
}
}
});
Expand Down Expand Up @@ -121,4 +136,22 @@ impl Central for Adapter {
// TODO: Get information about the adapter.
Ok("CoreBluetooth".to_string())
}

async fn adapter_state(&self) -> Result<CentralState> {
let fut = CoreBluetoothReplyFuture::default();
self.sender
.to_owned()
.send(CoreBluetoothMessage::GetAdapterState {
future: fut.get_state_clone(),
})
.await?;

match fut.await {
CoreBluetoothReply::AdapterState(state) => {
let central_state = get_central_state(state);
return Ok(central_state.clone());
}
_ => panic!("Shouldn't get anything but a AdapterState!"),
}
}
}
18 changes: 12 additions & 6 deletions src/corebluetooth/central_delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ use objc2::{declare_class, msg_send_id, mutability, rc::Retained, ClassType, Dec
use objc2_core_bluetooth::{
CBAdvertisementDataLocalNameKey, CBAdvertisementDataManufacturerDataKey,
CBAdvertisementDataServiceDataKey, CBAdvertisementDataServiceUUIDsKey, CBCentralManager,
CBCentralManagerDelegate, CBCharacteristic, CBDescriptor, CBPeripheral, CBPeripheralDelegate,
CBService, CBUUID,
CBCentralManagerDelegate, CBCharacteristic, CBDescriptor, CBManagerState, CBPeripheral,
CBPeripheralDelegate, CBService, CBUUID,
};
use objc2_foundation::{
NSArray, NSData, NSDictionary, NSError, NSNumber, NSObject, NSObjectProtocol, NSString,
Expand All @@ -41,7 +41,9 @@ use std::{
use uuid::Uuid;

pub enum CentralDelegateEvent {
DidUpdateState,
DidUpdateState {
state: CBManagerState,
},
DiscoveredPeripheral {
cbperipheral: Retained<CBPeripheral>,
local_name: Option<String>,
Expand Down Expand Up @@ -128,7 +130,10 @@ pub enum CentralDelegateEvent {
impl Debug for CentralDelegateEvent {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
CentralDelegateEvent::DidUpdateState => f.debug_tuple("DidUpdateState").finish(),
CentralDelegateEvent::DidUpdateState { state } => f
.debug_struct("CentralDelegateEvent")
.field("state", state)
.finish(),
CentralDelegateEvent::DiscoveredPeripheral {
cbperipheral,
local_name,
Expand Down Expand Up @@ -308,9 +313,10 @@ declare_class!(

unsafe impl CBCentralManagerDelegate for CentralDelegate {
#[method(centralManagerDidUpdateState:)]
fn delegate_centralmanagerdidupdatestate(&self, _central: &CBCentralManager) {
fn delegate_centralmanagerdidupdatestate(&self, central: &CBCentralManager) {
trace!("delegate_centralmanagerdidupdatestate");
self.send_event(CentralDelegateEvent::DidUpdateState);
let state = unsafe { central.state() };
self.send_event(CentralDelegateEvent::DidUpdateState { state });
}

// #[method(centralManager:willRestoreState:)]
Expand Down
27 changes: 20 additions & 7 deletions src/corebluetooth/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use objc2::{rc::Retained, runtime::AnyObject};
use objc2_core_bluetooth::{
CBCentralManager, CBCentralManagerScanOptionAllowDuplicatesKey, CBCharacteristic,
CBCharacteristicProperties, CBCharacteristicWriteType, CBDescriptor, CBManager,
CBManagerAuthorization, CBPeripheral, CBPeripheralState, CBService, CBUUID,
CBManagerAuthorization, CBManagerState, CBPeripheral, CBPeripheralState, CBService, CBUUID,
};
use objc2_foundation::{NSArray, NSData, NSMutableDictionary, NSNumber};
use std::{
Expand Down Expand Up @@ -146,6 +146,7 @@ impl CharacteristicInternal {

#[derive(Clone, Debug)]
pub enum CoreBluetoothReply {
AdapterState(CBManagerState),
ReadResult(Vec<u8>),
Connected(BTreeSet<Service>),
State(CBPeripheralState),
Expand Down Expand Up @@ -360,6 +361,9 @@ impl Debug for CoreBluetoothInternal {

#[derive(Debug)]
pub enum CoreBluetoothMessage {
GetAdapterState {
future: CoreBluetoothReplyStateShared,
},
StartScanning {
filter: ScanFilter,
},
Expand Down Expand Up @@ -421,7 +425,9 @@ pub enum CoreBluetoothMessage {

#[derive(Debug)]
pub enum CoreBluetoothEvent {
AdapterConnected,
DidUpdateState {
state: CBManagerState,
},
DeviceDiscovered {
uuid: Uuid,
name: Option<String>,
Expand Down Expand Up @@ -1050,14 +1056,11 @@ impl CoreBluetoothInternal {
select! {
delegate_msg = self.delegate_receiver.select_next_some() => {
match delegate_msg {
// TODO DidUpdateState does not imply that the adapter is
// on, just that it updated state.
//
// TODO We should probably also register some sort of
// "ready" variable in our adapter that will cause scans/etc
// to fail if this hasn't updated.
CentralDelegateEvent::DidUpdateState => {
self.dispatch_event(CoreBluetoothEvent::AdapterConnected).await
CentralDelegateEvent::DidUpdateState{state} => {
self.dispatch_event(CoreBluetoothEvent::DidUpdateState{state}).await
}
CentralDelegateEvent::DiscoveredPeripheral{cbperipheral, local_name} => {
self.on_discovered_peripheral(cbperipheral, local_name).await
Expand Down Expand Up @@ -1128,6 +1131,9 @@ impl CoreBluetoothInternal {
adapter_msg = self.message_receiver.select_next_some() => {
trace!("Adapter message!");
match adapter_msg {
CoreBluetoothMessage::GetAdapterState { future } => {
self.get_adapter_state(future);
},
CoreBluetoothMessage::StartScanning{filter} => self.start_discovery(filter),
CoreBluetoothMessage::StopScanning => self.stop_discovery(),
CoreBluetoothMessage::ConnectDevice{peripheral_uuid, future} => {
Expand Down Expand Up @@ -1171,6 +1177,13 @@ impl CoreBluetoothInternal {
}
}

fn get_adapter_state(&mut self, fut: CoreBluetoothReplyStateShared) {
let state = unsafe { self.manager.state() };
fut.lock()
.unwrap()
.set_reply(CoreBluetoothReply::AdapterState(state))
}

fn start_discovery(&mut self, filter: ScanFilter) {
trace!("BluetoothAdapter::start_discovery");
let service_uuids = scan_filter_to_service_uuids(filter);
Expand Down
6 changes: 5 additions & 1 deletion src/droidplug/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use super::{
peripheral::{Peripheral, PeripheralId},
};
use crate::{
api::{BDAddr, Central, CentralEvent, PeripheralProperties, ScanFilter},
api::{BDAddr, Central, CentralEvent, CentralState, PeripheralProperties, ScanFilter},
common::adapter_manager::AdapterManager,
Error, Result,
};
Expand Down Expand Up @@ -167,6 +167,10 @@ impl Central for Adapter {
async fn add_peripheral(&self, address: &PeripheralId) -> Result<Peripheral> {
self.add(address.0)
}

async fn adapter_state(&self) -> Result<CentralState> {
Ok(CentralState::Unknown)
}
}

pub(crate) fn adapter_report_scan_result_internal(
Expand Down
Loading
Loading