diff --git a/examples/event_driven_discovery.rs b/examples/event_driven_discovery.rs index be177471..ef7ace92 100644 --- a/examples/event_driven_discovery.rs +++ b/examples/event_driven_discovery.rs @@ -40,37 +40,31 @@ async fn main() -> Result<(), Box> { // thread (not task, as this library does not yet use async channels). while let Some(event) = events.next().await { match event { - CentralEvent::DeviceDiscovered(bd_addr) => { - println!("DeviceDiscovered: {:?}", bd_addr); + CentralEvent::DeviceDiscovered(id) => { + println!("DeviceDiscovered: {:?}", id); } - CentralEvent::DeviceConnected(bd_addr) => { - println!("DeviceConnected: {:?}", bd_addr); + CentralEvent::DeviceConnected(id) => { + println!("DeviceConnected: {:?}", id); } - CentralEvent::DeviceDisconnected(bd_addr) => { - println!("DeviceDisconnected: {:?}", bd_addr); + CentralEvent::DeviceDisconnected(id) => { + println!("DeviceDisconnected: {:?}", id); } CentralEvent::ManufacturerDataAdvertisement { - address, + id, manufacturer_data, } => { println!( "ManufacturerDataAdvertisement: {:?}, {:?}", - address, manufacturer_data + id, manufacturer_data ); } - CentralEvent::ServiceDataAdvertisement { - address, - service_data, - } => { - println!( - "ServiceDataAdvertisement: {:?}, {:?}", - address, service_data - ); + CentralEvent::ServiceDataAdvertisement { id, service_data } => { + println!("ServiceDataAdvertisement: {:?}, {:?}", id, service_data); } - CentralEvent::ServicesAdvertisement { address, services } => { + CentralEvent::ServicesAdvertisement { id, services } => { let services: Vec = services.into_iter().map(|s| s.to_short_string()).collect(); - println!("ServicesAdvertisement: {:?}, {:?}", address, services); + println!("ServicesAdvertisement: {:?}, {:?}", id, services); } _ => {} } diff --git a/src/api/mod.rs b/src/api/mod.rs index a953105e..86b827b5 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -41,6 +41,8 @@ use uuid::Uuid; pub use self::bdaddr::{BDAddr, ParseBDAddrError}; +use crate::platform::PeripheralId; + #[cfg_attr( feature = "serde", derive(Serialize, Deserialize), @@ -179,6 +181,9 @@ pub enum WriteType { /// as well as functions for communication. #[async_trait] pub trait Peripheral: Send + Sync + Clone + Debug { + /// Returns the unique identifier of the peripheral. + fn id(&self) -> PeripheralId; + /// Returns the MAC address of the peripheral. fn address(&self) -> BDAddr; @@ -237,23 +242,23 @@ pub trait Peripheral: Send + Sync + Clone + Debug { )] #[derive(Debug, Clone)] pub enum CentralEvent { - DeviceDiscovered(BDAddr), - DeviceUpdated(BDAddr), - DeviceConnected(BDAddr), - DeviceDisconnected(BDAddr), + DeviceDiscovered(PeripheralId), + DeviceUpdated(PeripheralId), + DeviceConnected(PeripheralId), + DeviceDisconnected(PeripheralId), /// Emitted when a Manufacturer Data advertisement has been received from a device ManufacturerDataAdvertisement { - address: BDAddr, + id: PeripheralId, manufacturer_data: HashMap>, }, /// Emitted when a Service Data advertisement has been received from a device ServiceDataAdvertisement { - address: BDAddr, + id: PeripheralId, service_data: HashMap>, }, /// Emitted when the advertised services for a device has been updated ServicesAdvertisement { - address: BDAddr, + id: PeripheralId, services: Vec, }, } @@ -281,7 +286,7 @@ pub trait Central: Send + Sync + Clone { async fn peripherals(&self) -> Result>; /// Returns a particular [`Peripheral`] by its address if it has been discovered. - async fn peripheral(&self, address: BDAddr) -> Result; + async fn peripheral(&self, id: PeripheralId) -> Result; /// Add a [`Peripheral`] from a MAC address without a scan result. Not supported on all Bluetooth systems. async fn add_peripheral(&self, address: BDAddr) -> Result; diff --git a/src/bluez/adapter.rs b/src/bluez/adapter.rs index 7b05a501..47b7e7d3 100644 --- a/src/bluez/adapter.rs +++ b/src/bluez/adapter.rs @@ -1,4 +1,4 @@ -use super::peripheral::Peripheral; +use super::peripheral::{Peripheral, PeripheralId}; use crate::api::{BDAddr, Central, CentralEvent}; use crate::{Error, Result}; use async_trait::async_trait; @@ -37,7 +37,7 @@ impl Central for Adapter { let initial_events = stream::iter( devices .into_iter() - .map(|device| CentralEvent::DeviceDiscovered(BDAddr::from(&device.mac_address))), + .map(|device| CentralEvent::DeviceDiscovered((&device.mac_address).into())), ); let session = self.session.clone(); @@ -68,12 +68,12 @@ impl Central for Adapter { .collect()) } - async fn peripheral(&self, address: BDAddr) -> Result { + async fn peripheral(&self, id: PeripheralId) -> Result { let devices = self.session.get_devices().await?; devices .into_iter() .find_map(|device| { - if BDAddr::from(&device.mac_address) == address { + if PeripheralId::from(&device.mac_address) == id { Some(Peripheral::new(self.session.clone(), device)) } else { None @@ -130,7 +130,7 @@ async fn central_event(event: BluetoothEvent, session: BluetoothSession) -> Opti } => { let device = session.get_device_info(&id).await.ok()?; Some(CentralEvent::ManufacturerDataAdvertisement { - address: (&device.mac_address).into(), + id: (&device.mac_address).into(), manufacturer_data, }) } @@ -140,7 +140,7 @@ async fn central_event(event: BluetoothEvent, session: BluetoothSession) -> Opti } => { let device = session.get_device_info(&id).await.ok()?; Some(CentralEvent::ServiceDataAdvertisement { - address: (&device.mac_address).into(), + id: (&device.mac_address).into(), service_data, }) } @@ -150,7 +150,7 @@ async fn central_event(event: BluetoothEvent, session: BluetoothSession) -> Opti } => { let device = session.get_device_info(&id).await.ok()?; Some(CentralEvent::ServicesAdvertisement { - address: (&device.mac_address).into(), + id: (&device.mac_address).into(), services, }) } diff --git a/src/bluez/peripheral.rs b/src/bluez/peripheral.rs index 3cea0a4c..a7665a2b 100644 --- a/src/bluez/peripheral.rs +++ b/src/bluez/peripheral.rs @@ -15,6 +15,9 @@ use crate::api::{ }; use crate::{Error, Result}; +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct PeripheralId(BDAddr); + /// Implementation of [api::Peripheral](crate::api::Peripheral). #[derive(Clone, Debug)] pub struct Peripheral { @@ -58,6 +61,10 @@ impl Peripheral { #[async_trait] impl api::Peripheral for Peripheral { + fn id(&self) -> PeripheralId { + PeripheralId(self.address()) + } + fn address(&self) -> BDAddr { self.mac_address } @@ -192,6 +199,12 @@ impl From<&MacAddress> for BDAddr { } } +impl From<&MacAddress> for PeripheralId { + fn from(mac_address: &MacAddress) -> Self { + PeripheralId(BDAddr::from(mac_address)) + } +} + impl From for AddressType { fn from(address_type: bluez_async::AddressType) -> Self { match address_type { diff --git a/src/common/adapter_manager.rs b/src/common/adapter_manager.rs index b3a36209..67581817 100644 --- a/src/common/adapter_manager.rs +++ b/src/common/adapter_manager.rs @@ -11,7 +11,8 @@ // following copyright: // // Copyright (c) 2014 The Rust Project Developers -use crate::api::{BDAddr, CentralEvent, Peripheral}; +use crate::api::{CentralEvent, Peripheral}; +use crate::platform::PeripheralId; use dashmap::{mapref::one::RefMut, DashMap}; use futures::stream::{Stream, StreamExt}; use log::trace; @@ -30,7 +31,7 @@ where #[derive(Debug)] struct Shared { - peripherals: DashMap, + peripherals: DashMap, events_channel: broadcast::Sender, } @@ -52,8 +53,8 @@ where { pub fn emit(&self, event: CentralEvent) { match event { - CentralEvent::DeviceDisconnected(ref addr) => { - self.shared.peripherals.remove(addr); + CentralEvent::DeviceDisconnected(ref id) => { + self.shared.peripherals.remove(id); } _ => {} } @@ -74,13 +75,12 @@ where })) } - pub fn add_peripheral(&self, addr: BDAddr, peripheral: PeripheralType) { + pub fn add_peripheral(&self, peripheral: PeripheralType) { assert!( - !self.shared.peripherals.contains_key(&addr), + !self.shared.peripherals.contains_key(&peripheral.id()), "Adding a peripheral that's already in the map." ); - assert_eq!(peripheral.address(), addr, "Device has unexpected address."); // TODO remove addr argument - self.shared.peripherals.insert(addr, peripheral); + self.shared.peripherals.insert(peripheral.id(), peripheral); } pub fn peripherals(&self) -> Vec { @@ -91,14 +91,14 @@ where .collect() } - pub fn peripheral_mut(&self, address: BDAddr) -> Option> { - self.shared.peripherals.get_mut(&address) + pub fn peripheral_mut(&self, id: PeripheralId) -> Option> { + self.shared.peripherals.get_mut(&id) } - pub fn peripheral(&self, address: BDAddr) -> Option { + pub fn peripheral(&self, id: PeripheralId) -> Option { self.shared .peripherals - .get(&address) + .get(&id) .map(|val| val.value().clone()) } } diff --git a/src/corebluetooth/adapter.rs b/src/corebluetooth/adapter.rs index ae99cf83..eb4937c7 100644 --- a/src/corebluetooth/adapter.rs +++ b/src/corebluetooth/adapter.rs @@ -1,5 +1,5 @@ use super::internal::{run_corebluetooth_thread, CoreBluetoothEvent, CoreBluetoothMessage}; -use super::peripheral::Peripheral; +use super::peripheral::{Peripheral, PeripheralId}; use crate::api::{BDAddr, Central, CentralEvent}; use crate::common::adapter_manager::AdapterManager; use crate::{Error, Result}; @@ -19,11 +19,6 @@ pub struct Adapter { sender: Sender, } -pub(crate) fn uuid_to_bdaddr(uuid: &str) -> BDAddr { - let b: [u8; 6] = uuid.as_bytes()[0..6].try_into().unwrap(); - BDAddr::try_from(b).unwrap() -} - impl Adapter { pub(crate) async fn new() -> Result { let (sender, mut receiver) = mpsc::channel(256); @@ -53,31 +48,24 @@ impl Adapter { name, event_receiver, } => { - // TODO Gotta change uuid into a BDAddr for now. Expand - // library identifier type. :( - let id = uuid_to_bdaddr(&uuid.to_string()); - manager_clone.add_peripheral( - id, - Peripheral::new( - uuid, - name, - manager_clone.clone(), - event_receiver, - adapter_sender_clone.clone(), - ), - ); - manager_clone.emit(CentralEvent::DeviceDiscovered(id)); + manager_clone.add_peripheral(Peripheral::new( + uuid, + name, + manager_clone.clone(), + event_receiver, + adapter_sender_clone.clone(), + )); + manager_clone.emit(CentralEvent::DeviceDiscovered(uuid.into())); } CoreBluetoothEvent::DeviceUpdated { uuid, name } => { - let id = uuid_to_bdaddr(&uuid.to_string()); + let id = uuid.into(); if let Some(entry) = manager_clone.peripheral_mut(id) { entry.value().update_name(&name); - manager_clone.emit(CentralEvent::DeviceUpdated(id)); + manager_clone.emit(CentralEvent::DeviceUpdated(uuid.into())); } } CoreBluetoothEvent::DeviceDisconnected { uuid } => { - let id = uuid_to_bdaddr(&uuid.to_string()); - manager_clone.emit(CentralEvent::DeviceDisconnected(id)); + manager_clone.emit(CentralEvent::DeviceDisconnected(uuid.into())); } _ => {} } @@ -119,10 +107,8 @@ impl Central for Adapter { Ok(self.manager.peripherals()) } - async fn peripheral(&self, address: BDAddr) -> Result { - self.manager - .peripheral(address) - .ok_or(Error::DeviceNotFound) + async fn peripheral(&self, id: PeripheralId) -> Result { + self.manager.peripheral(id).ok_or(Error::DeviceNotFound) } async fn add_peripheral(&self, _address: BDAddr) -> Result { diff --git a/src/corebluetooth/peripheral.rs b/src/corebluetooth/peripheral.rs index e4bc25a0..c1f91069 100644 --- a/src/corebluetooth/peripheral.rs +++ b/src/corebluetooth/peripheral.rs @@ -6,7 +6,6 @@ // for full license information. use super::{ - adapter::uuid_to_bdaddr, framework::cb::CBPeripheralState, internal::{ CBPeripheralEvent, CoreBluetoothMessage, CoreBluetoothReply, CoreBluetoothReplyFuture, @@ -35,6 +34,9 @@ use tokio::sync::broadcast; use tokio::task; use uuid::Uuid; +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct PeripheralId(Uuid); + /// Implementation of [api::Peripheral](crate::api::Peripheral). #[derive(Clone)] pub struct Peripheral { @@ -65,9 +67,7 @@ impl Peripheral { // Since we're building the object, we have an active advertisement. // Build properties now. let properties = Mutex::from(PeripheralProperties { - // Rumble required ONLY a BDAddr, not something you can get from - // MacOS, so we make it up for now. This sucks. - address: uuid_to_bdaddr(&uuid.to_string()), + address: BDAddr::default(), address_type: None, local_name, tx_power_level: None, @@ -108,7 +108,7 @@ impl Peripheral { shared .manager .emit(CentralEvent::ManufacturerDataAdvertisement { - address: properties.address, + id: shared.uuid.into(), manufacturer_data: properties.manufacturer_data.clone(), }); } @@ -117,7 +117,7 @@ impl Peripheral { properties.service_data.extend(service_data.clone()); shared.manager.emit(CentralEvent::ServiceDataAdvertisement { - address: properties.address, + id: shared.uuid.into(), service_data, }); } @@ -126,7 +126,7 @@ impl Peripheral { properties.services = services.clone(); shared.manager.emit(CentralEvent::ServicesAdvertisement { - address: properties.address, + id: shared.uuid.into(), services, }); } @@ -169,10 +169,12 @@ impl Debug for Peripheral { #[async_trait] impl api::Peripheral for Peripheral { + fn id(&self) -> PeripheralId { + PeripheralId(self.shared.uuid) + } + fn address(&self) -> BDAddr { - // TODO: look at moving/copying address out of properties so we don't have to - // take a lock here! (the address for the peripheral won't ever change) - self.shared.properties.lock().unwrap().address + BDAddr::default() } async fn properties(&self) -> Result> { @@ -215,11 +217,9 @@ impl api::Peripheral for Peripheral { match fut.await { CoreBluetoothReply::Connected(chars) => { *(self.shared.characteristics.lock().unwrap()) = chars; - self.shared.manager.emit(CentralEvent::DeviceConnected( - // TODO: look at moving/copying address out of properties so we don't have to - // take a lock here! (the address for the peripheral won't ever change) - self.shared.properties.lock().unwrap().address, - )); + self.shared + .manager + .emit(CentralEvent::DeviceConnected(self.shared.uuid.into())); } _ => panic!("Shouldn't get anything but connected!"), } @@ -333,6 +333,12 @@ impl api::Peripheral for Peripheral { } } +impl From for PeripheralId { + fn from(uuid: Uuid) -> Self { + PeripheralId(uuid) + } +} + impl From for Error { fn from(_: SendError) -> Self { Error::Other("Channel closed".to_string().into()) diff --git a/src/platform.rs b/src/platform.rs index 60d402f9..cd743b16 100644 --- a/src/platform.rs +++ b/src/platform.rs @@ -2,11 +2,17 @@ //! traits. Refer for the `api` module for how to use them. #[cfg(target_os = "linux")] -pub use crate::bluez::{adapter::Adapter, manager::Manager, peripheral::Peripheral}; +pub use crate::bluez::{ + adapter::Adapter, manager::Manager, peripheral::Peripheral, peripheral::PeripheralId, +}; #[cfg(any(target_os = "macos", target_os = "ios"))] -pub use crate::corebluetooth::{adapter::Adapter, manager::Manager, peripheral::Peripheral}; +pub use crate::corebluetooth::{ + adapter::Adapter, manager::Manager, peripheral::Peripheral, peripheral::PeripheralId, +}; #[cfg(target_os = "windows")] -pub use crate::winrtble::{adapter::Adapter, manager::Manager, peripheral::Peripheral}; +pub use crate::winrtble::{ + adapter::Adapter, manager::Manager, peripheral::Peripheral, peripheral::PeripheralId, +}; use crate::api::{self, Central}; use static_assertions::assert_impl_all; @@ -16,3 +22,4 @@ use std::fmt::Debug; assert_impl_all!(Adapter: Central, Clone, Debug, Send, Sized, Sync); assert_impl_all!(Manager: api::Manager, Clone, Debug, Send, Sized, Sync); assert_impl_all!(Peripheral: api::Peripheral, Clone, Debug, Send, Sized, Sync); +assert_impl_all!(PeripheralId: Clone, Debug, Send, Sized, Sync, Eq, PartialEq); diff --git a/src/winrtble/adapter.rs b/src/winrtble/adapter.rs index c1fe87f1..15f5902e 100644 --- a/src/winrtble/adapter.rs +++ b/src/winrtble/adapter.rs @@ -11,7 +11,7 @@ // // Copyright (c) 2014 The Rust Project Developers -use super::{ble::watcher::BLEWatcher, peripheral::Peripheral}; +use super::{ble::watcher::BLEWatcher, peripheral::Peripheral, peripheral::PeripheralId}; use crate::{ api::{BDAddr, Central, CentralEvent}, common::adapter_manager::AdapterManager, @@ -67,7 +67,7 @@ impl Central for Adapter { } else { let peripheral = Peripheral::new(manager.clone(), address); peripheral.update_properties(args); - manager.add_peripheral(address, peripheral); + manager.add_peripheral(peripheral); manager.emit(CentralEvent::DeviceDiscovered(address)); } })) @@ -83,10 +83,8 @@ impl Central for Adapter { Ok(self.manager.peripherals()) } - async fn peripheral(&self, address: BDAddr) -> Result { - self.manager - .peripheral(address) - .ok_or(Error::DeviceNotFound) + async fn peripheral(&self, id: PeripheralId) -> Result { + self.manager.peripheral(id).ok_or(Error::DeviceNotFound) } async fn add_peripheral(&self, _address: BDAddr) -> Result { diff --git a/src/winrtble/peripheral.rs b/src/winrtble/peripheral.rs index 2590ae17..18756113 100644 --- a/src/winrtble/peripheral.rs +++ b/src/winrtble/peripheral.rs @@ -41,6 +41,9 @@ use uuid::Uuid; use bindings::Windows::Devices::Bluetooth::Advertisement::*; use bindings::Windows::Devices::Bluetooth::BluetoothAddressType; +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct PeripheralId(BDAddr); + /// Implementation of [api::Peripheral](crate::api::Peripheral). #[derive(Clone)] pub struct Peripheral { @@ -140,7 +143,7 @@ impl Peripheral { self.shared .adapter .emit(CentralEvent::ManufacturerDataAdvertisement { - address: self.shared.address, + id: self.shared.address.into(), manufacturer_data: manufacturer_data_guard.clone(), }); } @@ -196,7 +199,7 @@ impl Peripheral { self.shared .adapter .emit(CentralEvent::ServiceDataAdvertisement { - address: self.shared.address, + id: self.shared.address.into(), service_data: service_data_guard.clone(), }); } @@ -234,7 +237,7 @@ impl Peripheral { self.shared .adapter .emit(CentralEvent::ServicesAdvertisement { - address: self.shared.address, + id: self.shared.address.into(), services: services_guard.iter().map(|uuid| *uuid).collect(), }); } @@ -306,6 +309,10 @@ impl Debug for Peripheral { #[async_trait] impl ApiPeripheral for Peripheral { + fn id(&self) -> PeripheralId { + PeripheralId(self.shared.address) + } + /// Returns the address of the peripheral. fn address(&self) -> BDAddr { self.shared.address @@ -346,7 +353,7 @@ impl ApiPeripheral for Peripheral { .connected .store(is_connected, Ordering::Relaxed); if !is_connected { - adapter_clone.emit(CentralEvent::DeviceDisconnected(address)); + adapter_clone.emit(CentralEvent::DeviceDisconnected(address.into())); } }), ) @@ -357,7 +364,7 @@ impl ApiPeripheral for Peripheral { *d = Some(device); self.shared .adapter - .emit(CentralEvent::DeviceConnected(self.shared.address)); + .emit(CentralEvent::DeviceConnected(self.shared.address.into())); Ok(()) } @@ -367,7 +374,7 @@ impl ApiPeripheral for Peripheral { *device = None; self.shared .adapter - .emit(CentralEvent::DeviceDisconnected(self.shared.address)); + .emit(CentralEvent::DeviceDisconnected(self.shared.address.into())); Ok(()) } @@ -458,3 +465,9 @@ impl ApiPeripheral for Peripheral { Ok(notifications_stream_from_broadcast_receiver(receiver)) } } + +impl From for PeripheralId { + fn from(address: BDAddr) -> Self { + PeripheralId(address) + } +}