mod adapter_manager;
pub use adapter_manager::AdapterManager;
use crate::{
api::UUID::{B128, B16},
Result,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::sync::mpsc::Receiver;
use std::{
collections::BTreeSet,
convert::TryFrom,
fmt::{self, Debug, Display, Formatter},
str::FromStr,
};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum AddressType {
Random,
Public,
}
impl Default for AddressType {
fn default() -> Self {
AddressType::Public
}
}
impl AddressType {
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,
}
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Hash, Eq, PartialEq, Default)]
#[repr(C)]
pub struct BDAddr {
pub address: [u8; 6usize],
}
impl Display for BDAddr {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let a = self.address;
write!(
f,
"{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
a[5], a[4], a[3], a[2], a[1], a[0]
)
}
}
impl Debug for BDAddr {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
(self as &dyn Display).fmt(f)
}
}
type ParseBDAddrResult<T> = std::result::Result<T, ParseBDAddrError>;
#[derive(Debug, Fail, Clone, PartialEq)]
pub enum ParseBDAddrError {
#[fail(display = "Bluetooth address has to be 6 bytes long")]
IncorrectByteCount,
#[fail(display = "Malformed integer in Bluetooth address")]
InvalidInt,
}
impl FromStr for BDAddr {
type Err = ParseBDAddrError;
fn from_str(s: &str) -> ParseBDAddrResult<Self> {
let bytes = s
.split(':')
.map(|part: &str| {
u8::from_str_radix(part, 16).map_err(|_| ParseBDAddrError::InvalidInt)
})
.collect::<ParseBDAddrResult<Vec<u8>>>()?;
if let Ok(mut address) = <[u8; 6]>::try_from(bytes.as_slice()) {
address.reverse();
Ok(BDAddr { address })
} else {
Err(ParseBDAddrError::IncorrectByteCount)
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ValueNotification {
pub uuid: UUID,
pub handle: Option<u16>,
pub value: Vec<u8>,
}
pub type Callback<T> = Box<dyn Fn(Result<T>) + Send>;
pub type CommandCallback = Callback<()>;
pub type RequestCallback = Callback<Vec<u8>>;
pub type NotificationHandler = Box<dyn FnMut(ValueNotification) + Send>;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash)]
pub enum UUID {
B16(u16),
B128([u8; 16]),
}
impl UUID {
pub fn size(&self) -> usize {
match *self {
B16(_) => 2,
B128(_) => 16,
}
}
}
impl Display for UUID {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match *self {
B16(u) => write!(f, "{:02X}:{:02X}", u >> 8, u & 0xFF),
B128(a) => {
for i in (1..a.len()).rev() {
write!(f, "{:02X}:", a[i])?;
}
write!(f, "{:02X}", a[0])
}
}
}
}
impl Debug for UUID {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
(self as &dyn Display).fmt(f)
}
}
type ParseUUIDResult<T> = std::result::Result<T, ParseUUIDError>;
#[derive(Debug, Fail, Clone, PartialEq)]
pub enum ParseUUIDError {
#[fail(display = "UUID has to be either 2 or 16 bytes long")]
IncorrectByteCount,
#[fail(display = "Malformed integer in UUID")]
InvalidInt,
}
impl FromStr for UUID {
type Err = ParseUUIDError;
fn from_str(s: &str) -> ParseUUIDResult<Self> {
let bytes = s
.split(':')
.map(|part: &str| u8::from_str_radix(part, 16).map_err(|_| ParseUUIDError::InvalidInt))
.collect::<ParseUUIDResult<Vec<u8>>>()?;
if let Ok(bytes) = <[u8; 2]>::try_from(bytes.as_slice()) {
Ok(UUID::B16(u16::from_be_bytes(bytes)))
} else if let Ok(mut bytes) = <[u8; 16]>::try_from(bytes.as_slice()) {
bytes.reverse();
Ok(UUID::B128(bytes))
} else {
Err(ParseUUIDError::IncorrectByteCount)
}
}
}
bitflags! {
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;
}
}
impl CharPropFlags {
pub fn new() -> Self {
Self { bits: 0 }
}
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)]
pub struct Characteristic {
pub start_handle: u16,
pub end_handle: u16,
pub value_handle: u16,
pub uuid: UUID,
pub properties: CharPropFlags,
}
impl Display for Characteristic {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(
f,
"uuid: {:?}, char properties: {:?}",
self.properties, self.uuid
)
}
}
#[derive(Debug, Default, Clone)]
pub struct PeripheralProperties {
pub address: BDAddr,
pub address_type: AddressType,
pub local_name: Option<String>,
pub tx_power_level: Option<i8>,
pub manufacturer_data: Option<Vec<u8>>,
pub discovery_count: u32,
pub has_scan_response: bool,
}
pub trait Peripheral: Send + Sync + Clone + Debug {
fn address(&self) -> BDAddr;
fn properties(&self) -> PeripheralProperties;
fn characteristics(&self) -> BTreeSet<Characteristic>;
fn is_connected(&self) -> bool;
fn connect(&self) -> Result<()>;
fn disconnect(&self) -> Result<()>;
fn discover_characteristics(&self) -> Result<Vec<Characteristic>>;
fn discover_characteristics_in_range(
&self,
start: u16,
end: u16,
) -> Result<Vec<Characteristic>>;
fn command_async(
&self,
characteristic: &Characteristic,
data: &[u8],
handler: Option<CommandCallback>,
);
fn command(&self, characteristic: &Characteristic, data: &[u8]) -> Result<()>;
fn request_async(
&self,
characteristic: &Characteristic,
data: &[u8],
handler: Option<RequestCallback>,
);
fn request(&self, characteristic: &Characteristic, data: &[u8]) -> Result<Vec<u8>>;
fn read_async(&self, characteristic: &Characteristic, handler: Option<RequestCallback>);
fn read(&self, characteristic: &Characteristic) -> Result<Vec<u8>>;
fn read_by_type_async(
&self,
characteristic: &Characteristic,
uuid: UUID,
handler: Option<RequestCallback>,
);
fn read_by_type(&self, characteristic: &Characteristic, uuid: UUID) -> Result<Vec<u8>>;
fn subscribe(&self, characteristic: &Characteristic) -> Result<()>;
fn unsubscribe(&self, characteristic: &Characteristic) -> Result<()>;
fn on_notification(&self, handler: NotificationHandler);
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone)]
pub enum CentralEvent {
DeviceDiscovered(BDAddr),
DeviceLost(BDAddr),
DeviceUpdated(BDAddr),
DeviceConnected(BDAddr),
DeviceDisconnected(BDAddr),
}
pub trait Central<P: Peripheral>: Send + Sync + Clone {
fn event_receiver(&self) -> Option<Receiver<CentralEvent>>;
fn start_scan(&self) -> Result<()>;
fn active(&self, enabled: bool);
fn filter_duplicates(&self, enabled: bool);
fn stop_scan(&self) -> Result<()>;
fn peripherals(&self) -> Vec<P>;
fn peripheral(&self, address: BDAddr) -> Option<P>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_uuid() {
let values = vec![
("2A:00", Ok(UUID::B16(0x2A00))),
(
"00:00:15:32:12:12:EF:DE:15:23:78:5F:EA:BC:D1:23",
Ok(UUID::B128([
0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x32,
0x15, 0x00, 0x00,
])),
),
("2A:00:00", Err(ParseUUIDError::IncorrectByteCount)),
("2A:100", Err(ParseUUIDError::InvalidInt)),
("ZZ:00", Err(ParseUUIDError::InvalidInt)),
];
for (input, expected) in values {
let result: ParseUUIDResult<UUID> = input.parse();
assert_eq!(result, expected);
if let Ok(uuid) = result {
assert_eq!(input, uuid.to_string());
}
}
}
#[test]
fn parse_addr() {
let values = vec![
(
"2A:00:AA:BB:CC:DD",
Ok(BDAddr {
address: [0xDD, 0xCC, 0xBB, 0xAA, 0x00, 0x2A],
}),
),
("2A:00:00", Err(ParseBDAddrError::IncorrectByteCount)),
("2A:00:AA:BB:CC:ZZ", Err(ParseBDAddrError::InvalidInt)),
];
for (input, expected) in values {
let result: ParseBDAddrResult<BDAddr> = input.parse();
assert_eq!(result, expected);
if let Ok(uuid) = result {
assert_eq!(input, uuid.to_string());
}
}
}
}