#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, format, string::String, string::ToString, vec::Vec};
use async_trait::async_trait;
use crate::config::{BleConfig, BlePhy, DiscoveryConfig};
use crate::error::Result;
use crate::transport::BleConnection;
use crate::NodeId;
#[cfg(all(feature = "linux", target_os = "linux"))]
pub mod linux;
#[cfg(feature = "android")]
pub mod android;
#[cfg(any(feature = "macos", feature = "ios"))]
pub mod apple;
#[cfg(feature = "windows")]
pub mod windows;
#[cfg(feature = "embedded")]
pub mod embedded;
#[cfg(feature = "esp32")]
pub mod esp32;
#[cfg(feature = "std")]
pub mod mock;
#[derive(Debug, Clone)]
pub struct DiscoveredDevice {
pub address: String,
pub name: Option<String>,
pub rssi: i8,
pub is_peat_node: bool,
pub node_id: Option<NodeId>,
pub adv_data: Vec<u8>,
}
pub type DiscoveryCallback = std::sync::Arc<dyn Fn(DiscoveredDevice) + Send + Sync>;
pub type ConnectionCallback = std::sync::Arc<dyn Fn(NodeId, ConnectionEvent) + Send + Sync>;
#[derive(Debug, Clone)]
pub enum ConnectionEvent {
Connected {
mtu: u16,
phy: BlePhy,
},
Disconnected {
reason: DisconnectReason,
},
ServicesDiscovered {
has_peat_service: bool,
},
DataReceived {
data: Vec<u8>,
},
MtuChanged {
mtu: u16,
},
PhyChanged {
phy: BlePhy,
},
RssiUpdated {
rssi: i8,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DisconnectReason {
LocalRequest,
RemoteRequest,
Timeout,
LinkLoss,
ConnectionFailed,
Unknown,
}
#[async_trait]
pub trait BleAdapter: Send + Sync {
async fn init(&mut self, config: &BleConfig) -> Result<()>;
async fn start(&self) -> Result<()>;
async fn stop(&self) -> Result<()>;
fn is_powered(&self) -> bool;
fn address(&self) -> Option<String>;
async fn start_scan(&self, config: &DiscoveryConfig) -> Result<()>;
async fn stop_scan(&self) -> Result<()>;
async fn start_advertising(&self, config: &DiscoveryConfig) -> Result<()>;
async fn stop_advertising(&self) -> Result<()>;
fn set_discovery_callback(&mut self, callback: Option<DiscoveryCallback>);
async fn connect(&self, peer_id: &NodeId) -> Result<Box<dyn BleConnection>>;
async fn disconnect(&self, peer_id: &NodeId) -> Result<()>;
fn get_connection(&self, peer_id: &NodeId) -> Option<Box<dyn BleConnection>>;
fn peer_count(&self) -> usize;
fn connected_peers(&self) -> Vec<NodeId>;
fn set_connection_callback(&mut self, callback: Option<ConnectionCallback>);
async fn register_gatt_service(&self) -> Result<()>;
async fn unregister_gatt_service(&self) -> Result<()>;
async fn write_to_peer(
&self,
peer_id: &NodeId,
char_uuid: uuid::Uuid,
data: &[u8],
) -> Result<()> {
let _ = (peer_id, char_uuid, data);
Err(crate::error::BleError::NotSupported(
"write_to_peer not implemented for this adapter".into(),
))
}
fn supports_coded_phy(&self) -> bool;
fn supports_extended_advertising(&self) -> bool;
fn max_mtu(&self) -> u16;
fn max_connections(&self) -> u8;
}
#[derive(Debug, Default)]
pub struct StubAdapter {
powered: bool,
}
#[async_trait]
impl BleAdapter for StubAdapter {
async fn init(&mut self, _config: &BleConfig) -> Result<()> {
self.powered = true;
Ok(())
}
async fn start(&self) -> Result<()> {
Ok(())
}
async fn stop(&self) -> Result<()> {
Ok(())
}
fn is_powered(&self) -> bool {
self.powered
}
fn address(&self) -> Option<String> {
Some("00:00:00:00:00:00".to_string())
}
async fn start_scan(&self, _config: &DiscoveryConfig) -> Result<()> {
Ok(())
}
async fn stop_scan(&self) -> Result<()> {
Ok(())
}
async fn start_advertising(&self, _config: &DiscoveryConfig) -> Result<()> {
Ok(())
}
async fn stop_advertising(&self) -> Result<()> {
Ok(())
}
fn set_discovery_callback(&mut self, _callback: Option<DiscoveryCallback>) {}
async fn connect(&self, peer_id: &NodeId) -> Result<Box<dyn BleConnection>> {
Err(crate::error::BleError::NotSupported(format!(
"Stub adapter cannot connect to {}",
peer_id
)))
}
async fn disconnect(&self, _peer_id: &NodeId) -> Result<()> {
Ok(())
}
fn get_connection(&self, _peer_id: &NodeId) -> Option<Box<dyn BleConnection>> {
None
}
fn peer_count(&self) -> usize {
0
}
fn connected_peers(&self) -> Vec<NodeId> {
Vec::new()
}
fn set_connection_callback(&mut self, _callback: Option<ConnectionCallback>) {}
async fn register_gatt_service(&self) -> Result<()> {
Ok(())
}
async fn unregister_gatt_service(&self) -> Result<()> {
Ok(())
}
fn supports_coded_phy(&self) -> bool {
false
}
fn supports_extended_advertising(&self) -> bool {
false
}
fn max_mtu(&self) -> u16 {
23
}
fn max_connections(&self) -> u8 {
0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_stub_adapter() {
let mut adapter = StubAdapter::default();
assert!(!adapter.is_powered());
adapter.init(&BleConfig::default()).await.unwrap();
assert!(adapter.is_powered());
assert_eq!(adapter.peer_count(), 0);
assert!(!adapter.supports_coded_phy());
}
}