pub(crate) mod bdaddr;
pub mod bleuuid;
use crate::Result;
use async_trait::async_trait;
use bitflags::bitflags;
use futures::stream::Stream;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "serde")]
use serde_cr as serde;
use std::{
collections::{BTreeSet, HashMap},
fmt::{self, Debug, Display, Formatter},
pin::Pin,
time::Duration,
};
use uuid::Uuid;
pub use self::bdaddr::{BDAddr, ParseBDAddrError};
use crate::platform::PeripheralId;
pub const DEFAULT_MTU_SIZE: u16 = 23;
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_cr")
)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
pub enum AddressType {
Random,
#[default]
Public,
}
impl AddressType {
#[allow(clippy::should_implement_trait)]
pub fn from_str(v: &str) -> Option<AddressType> {
match v {
"public" => Some(AddressType::Public),
"random" => Some(AddressType::Random),
_ => None,
}
}
pub fn from_u8(v: u8) -> Option<AddressType> {
match v {
1 => Some(AddressType::Public),
2 => Some(AddressType::Random),
_ => None,
}
}
pub fn num(&self) -> u8 {
match *self {
AddressType::Public => 1,
AddressType::Random => 2,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ValueNotification {
pub uuid: Uuid,
pub service_uuid: Uuid,
pub value: Vec<u8>,
}
bitflags! {
#[derive(Default, Debug, PartialEq, Eq, Ord, PartialOrd, Clone, Copy)]
pub struct CharPropFlags: u8 {
const BROADCAST = 0x01;
const READ = 0x02;
const WRITE_WITHOUT_RESPONSE = 0x04;
const WRITE = 0x08;
const NOTIFY = 0x10;
const INDICATE = 0x20;
const AUTHENTICATED_SIGNED_WRITES = 0x40;
const EXTENDED_PROPERTIES = 0x80;
}
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)]
pub struct Service {
pub uuid: Uuid,
pub primary: bool,
pub characteristics: BTreeSet<Characteristic>,
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)]
pub struct Characteristic {
pub uuid: Uuid,
pub service_uuid: Uuid,
pub properties: CharPropFlags,
pub descriptors: BTreeSet<Descriptor>,
}
impl Display for Characteristic {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(
f,
"uuid: {:?}, char properties: {:?}",
self.uuid, self.properties
)
}
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)]
pub struct Descriptor {
pub uuid: Uuid,
pub service_uuid: Uuid,
pub characteristic_uuid: Uuid,
}
impl Display for Descriptor {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "uuid: {:?}", self.uuid)
}
}
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_cr")
)]
#[derive(Debug, Default, Clone)]
pub struct PeripheralProperties {
pub address: BDAddr,
pub address_type: Option<AddressType>,
pub local_name: Option<String>,
pub advertisement_name: Option<String>,
pub tx_power_level: Option<i16>,
pub rssi: Option<i16>,
pub manufacturer_data: HashMap<u16, Vec<u8>>,
pub service_data: HashMap<Uuid, Vec<u8>>,
pub services: Vec<Uuid>,
pub class: Option<u32>,
}
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_cr")
)]
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct ScanFilter {
pub services: Vec<Uuid>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ConnectionParameters {
pub interval_us: u32,
pub latency: u16,
pub supervision_timeout_us: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnectionParameterPreset {
Balanced,
ThroughputOptimized,
PowerOptimized,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum WriteType {
WithResponse,
WithoutResponse,
}
#[async_trait]
pub trait Peripheral: Send + Sync + Clone + Debug {
fn id(&self) -> PeripheralId;
fn address(&self) -> BDAddr;
fn mtu(&self) -> u16;
async fn properties(&self) -> Result<Option<PeripheralProperties>>;
fn services(&self) -> BTreeSet<Service>;
fn characteristics(&self) -> BTreeSet<Characteristic> {
self.services()
.iter()
.flat_map(|service| service.characteristics.clone().into_iter())
.collect()
}
async fn is_connected(&self) -> Result<bool>;
async fn connect(&self) -> Result<()>;
async fn connect_with_timeout(&self, timeout: Duration) -> Result<()> {
tokio::time::timeout(timeout, self.connect())
.await
.map_err(|_| crate::Error::TimedOut(timeout))?
}
async fn disconnect(&self) -> Result<()>;
async fn discover_services(&self) -> Result<()>;
async fn discover_services_with_timeout(&self, timeout: Duration) -> Result<()> {
tokio::time::timeout(timeout, self.discover_services())
.await
.map_err(|_| crate::Error::TimedOut(timeout))?
}
async fn write(
&self,
characteristic: &Characteristic,
data: &[u8],
write_type: WriteType,
) -> Result<()>;
async fn read(&self, characteristic: &Characteristic) -> Result<Vec<u8>>;
async fn subscribe(&self, characteristic: &Characteristic) -> Result<()>;
async fn unsubscribe(&self, characteristic: &Characteristic) -> Result<()>;
async fn notifications(&self) -> Result<Pin<Box<dyn Stream<Item = ValueNotification> + Send>>>;
async fn write_descriptor(&self, descriptor: &Descriptor, data: &[u8]) -> Result<()>;
async fn read_descriptor(&self, descriptor: &Descriptor) -> Result<Vec<u8>>;
async fn connection_parameters(&self) -> Result<Option<ConnectionParameters>> {
Err(crate::Error::NotSupported(
"connection_parameters".to_string(),
))
}
async fn request_connection_parameters(
&self,
_preset: ConnectionParameterPreset,
) -> Result<()> {
Err(crate::Error::NotSupported(
"request_connection_parameters".to_string(),
))
}
async fn read_rssi(&self) -> Result<i16> {
Err(crate::Error::NotSupported("read_rssi".to_string()))
}
}
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_cr")
)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum CentralState {
Unknown = 0,
PoweredOn = 1,
PoweredOff = 2,
}
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_cr")
)]
#[derive(Debug, Clone)]
pub enum CentralEvent {
DeviceDiscovered(PeripheralId),
DeviceUpdated(PeripheralId),
DeviceConnected(PeripheralId),
DeviceDisconnected(PeripheralId),
DeviceServicesModified(PeripheralId),
ManufacturerDataAdvertisement {
id: PeripheralId,
manufacturer_data: HashMap<u16, Vec<u8>>,
},
ServiceDataAdvertisement {
id: PeripheralId,
service_data: HashMap<Uuid, Vec<u8>>,
},
ServicesAdvertisement {
id: PeripheralId,
services: Vec<Uuid>,
},
RssiUpdate {
id: PeripheralId,
rssi: i16,
},
StateUpdate(CentralState),
}
#[async_trait]
pub trait Central: Send + Sync + Clone {
type Peripheral: Peripheral;
async fn events(&self) -> Result<Pin<Box<dyn Stream<Item = CentralEvent> + Send>>>;
async fn start_scan(&self, filter: ScanFilter) -> Result<()>;
async fn stop_scan(&self) -> Result<()>;
async fn peripherals(&self) -> Result<Vec<Self::Peripheral>>;
async fn peripheral(&self, id: &PeripheralId) -> Result<Self::Peripheral>;
async fn add_peripheral(&self, address: &PeripheralId) -> Result<Self::Peripheral>;
async fn clear_peripherals(&self) -> Result<()>;
async fn adapter_info(&self) -> Result<String>;
async fn adapter_state(&self) -> Result<CentralState>;
}
#[async_trait]
pub trait Manager {
type Adapter: Central;
async fn adapters(&self) -> Result<Vec<Self::Adapter>>;
}