hive_btle/platform/
mod.rs

1//! Platform abstraction layer for BLE
2//!
3//! This module defines the traits that platform-specific implementations
4//! must implement to provide BLE functionality.
5//!
6//! ## Supported Platforms
7//!
8//! - **Linux**: BlueZ via D-Bus (`bluer` crate)
9//! - **Android**: JNI to Android Bluetooth APIs
10//! - **macOS/iOS**: CoreBluetooth
11//! - **Windows**: WinRT Bluetooth APIs
12//! - **Embedded**: ESP-IDF NimBLE
13//!
14//! ## Architecture
15//!
16//! Each platform provides an implementation of `BleAdapter` that handles:
17//! - Adapter initialization and power management
18//! - Discovery (scanning and advertising)
19//! - GATT server and client operations
20//! - Connection management
21
22#[cfg(not(feature = "std"))]
23use alloc::{boxed::Box, format, string::String, string::ToString, vec::Vec};
24
25use async_trait::async_trait;
26
27use crate::config::{BleConfig, BlePhy, DiscoveryConfig};
28use crate::error::Result;
29use crate::transport::BleConnection;
30use crate::NodeId;
31
32// Platform-specific modules (conditionally compiled)
33#[cfg(all(feature = "linux", target_os = "linux"))]
34pub mod linux;
35
36#[cfg(feature = "android")]
37pub mod android;
38
39#[cfg(any(feature = "macos", feature = "ios"))]
40pub mod apple;
41
42#[cfg(feature = "windows")]
43pub mod windows;
44
45#[cfg(feature = "embedded")]
46pub mod embedded;
47
48#[cfg(feature = "esp32")]
49pub mod esp32;
50
51// Mock adapter for testing (always available in std builds)
52#[cfg(feature = "std")]
53pub mod mock;
54
55/// Discovered BLE device
56#[derive(Debug, Clone)]
57pub struct DiscoveredDevice {
58    /// Device address (MAC or platform-specific)
59    pub address: String,
60    /// Device name (if available)
61    pub name: Option<String>,
62    /// RSSI in dBm
63    pub rssi: i8,
64    /// Is this a HIVE node?
65    pub is_hive_node: bool,
66    /// Parsed HIVE node ID (if HIVE node)
67    pub node_id: Option<NodeId>,
68    /// Raw advertising data
69    pub adv_data: Vec<u8>,
70}
71
72/// Callback for discovered devices
73pub type DiscoveryCallback = std::sync::Arc<dyn Fn(DiscoveredDevice) + Send + Sync>;
74
75/// Callback for connection events
76pub type ConnectionCallback = std::sync::Arc<dyn Fn(NodeId, ConnectionEvent) + Send + Sync>;
77
78/// Connection event types
79#[derive(Debug, Clone)]
80pub enum ConnectionEvent {
81    /// Connection established
82    Connected {
83        /// Negotiated MTU
84        mtu: u16,
85        /// Connection PHY
86        phy: BlePhy,
87    },
88    /// Connection lost
89    Disconnected {
90        /// Reason for disconnection
91        reason: DisconnectReason,
92    },
93    /// GATT services discovered
94    ServicesDiscovered {
95        /// Whether the HIVE service was found
96        has_hive_service: bool,
97    },
98    /// Data received from peer (characteristic read or notification)
99    DataReceived {
100        /// The received data
101        data: Vec<u8>,
102    },
103    /// MTU changed
104    MtuChanged {
105        /// New MTU value
106        mtu: u16,
107    },
108    /// PHY changed
109    PhyChanged {
110        /// New PHY
111        phy: BlePhy,
112    },
113    /// RSSI updated
114    RssiUpdated {
115        /// New RSSI value in dBm
116        rssi: i8,
117    },
118}
119
120/// Reason for disconnection
121#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub enum DisconnectReason {
123    /// Disconnected by local request
124    LocalRequest,
125    /// Disconnected by remote device
126    RemoteRequest,
127    /// Connection timeout
128    Timeout,
129    /// Link loss (device out of range)
130    LinkLoss,
131    /// Connection failed
132    ConnectionFailed,
133    /// Unknown reason
134    Unknown,
135}
136
137/// Platform-specific BLE adapter
138///
139/// This is the main abstraction trait that each platform must implement.
140/// It provides all BLE functionality needed by the transport layer.
141#[async_trait]
142pub trait BleAdapter: Send + Sync {
143    /// Initialize the adapter with the given configuration
144    async fn init(&mut self, config: &BleConfig) -> Result<()>;
145
146    /// Start the adapter (begin advertising and/or scanning)
147    async fn start(&self) -> Result<()>;
148
149    /// Stop the adapter
150    async fn stop(&self) -> Result<()>;
151
152    /// Check if the adapter is powered on
153    fn is_powered(&self) -> bool;
154
155    /// Get the adapter's Bluetooth address
156    fn address(&self) -> Option<String>;
157
158    // === Discovery ===
159
160    /// Start scanning for devices
161    async fn start_scan(&self, config: &DiscoveryConfig) -> Result<()>;
162
163    /// Stop scanning
164    async fn stop_scan(&self) -> Result<()>;
165
166    /// Start advertising
167    async fn start_advertising(&self, config: &DiscoveryConfig) -> Result<()>;
168
169    /// Stop advertising
170    async fn stop_advertising(&self) -> Result<()>;
171
172    /// Set callback for discovered devices
173    fn set_discovery_callback(&mut self, callback: Option<DiscoveryCallback>);
174
175    // === Connections ===
176
177    /// Connect to a peer by node ID
178    async fn connect(&self, peer_id: &NodeId) -> Result<Box<dyn BleConnection>>;
179
180    /// Disconnect from a peer
181    async fn disconnect(&self, peer_id: &NodeId) -> Result<()>;
182
183    /// Get an existing connection
184    fn get_connection(&self, peer_id: &NodeId) -> Option<Box<dyn BleConnection>>;
185
186    /// Get the number of connected peers
187    fn peer_count(&self) -> usize;
188
189    /// Get list of connected peer IDs
190    fn connected_peers(&self) -> Vec<NodeId>;
191
192    /// Set callback for connection events
193    fn set_connection_callback(&mut self, callback: Option<ConnectionCallback>);
194
195    // === GATT ===
196
197    /// Register the HIVE GATT service
198    async fn register_gatt_service(&self) -> Result<()>;
199
200    /// Unregister the HIVE GATT service
201    async fn unregister_gatt_service(&self) -> Result<()>;
202
203    // === Capabilities ===
204
205    /// Check if Coded PHY is supported
206    fn supports_coded_phy(&self) -> bool;
207
208    /// Check if extended advertising is supported
209    fn supports_extended_advertising(&self) -> bool;
210
211    /// Get maximum supported MTU
212    fn max_mtu(&self) -> u16;
213
214    /// Get maximum number of connections
215    fn max_connections(&self) -> u8;
216}
217
218/// Stub adapter for testing and platforms without BLE
219#[derive(Debug, Default)]
220pub struct StubAdapter {
221    powered: bool,
222}
223
224#[async_trait]
225impl BleAdapter for StubAdapter {
226    async fn init(&mut self, _config: &BleConfig) -> Result<()> {
227        self.powered = true;
228        Ok(())
229    }
230
231    async fn start(&self) -> Result<()> {
232        Ok(())
233    }
234
235    async fn stop(&self) -> Result<()> {
236        Ok(())
237    }
238
239    fn is_powered(&self) -> bool {
240        self.powered
241    }
242
243    fn address(&self) -> Option<String> {
244        Some("00:00:00:00:00:00".to_string())
245    }
246
247    async fn start_scan(&self, _config: &DiscoveryConfig) -> Result<()> {
248        Ok(())
249    }
250
251    async fn stop_scan(&self) -> Result<()> {
252        Ok(())
253    }
254
255    async fn start_advertising(&self, _config: &DiscoveryConfig) -> Result<()> {
256        Ok(())
257    }
258
259    async fn stop_advertising(&self) -> Result<()> {
260        Ok(())
261    }
262
263    fn set_discovery_callback(&mut self, _callback: Option<DiscoveryCallback>) {}
264
265    async fn connect(&self, peer_id: &NodeId) -> Result<Box<dyn BleConnection>> {
266        Err(crate::error::BleError::NotSupported(format!(
267            "Stub adapter cannot connect to {}",
268            peer_id
269        )))
270    }
271
272    async fn disconnect(&self, _peer_id: &NodeId) -> Result<()> {
273        Ok(())
274    }
275
276    fn get_connection(&self, _peer_id: &NodeId) -> Option<Box<dyn BleConnection>> {
277        None
278    }
279
280    fn peer_count(&self) -> usize {
281        0
282    }
283
284    fn connected_peers(&self) -> Vec<NodeId> {
285        Vec::new()
286    }
287
288    fn set_connection_callback(&mut self, _callback: Option<ConnectionCallback>) {}
289
290    async fn register_gatt_service(&self) -> Result<()> {
291        Ok(())
292    }
293
294    async fn unregister_gatt_service(&self) -> Result<()> {
295        Ok(())
296    }
297
298    fn supports_coded_phy(&self) -> bool {
299        false
300    }
301
302    fn supports_extended_advertising(&self) -> bool {
303        false
304    }
305
306    fn max_mtu(&self) -> u16 {
307        23
308    }
309
310    fn max_connections(&self) -> u8 {
311        0
312    }
313}
314
315#[cfg(test)]
316mod tests {
317    use super::*;
318
319    #[tokio::test]
320    async fn test_stub_adapter() {
321        let mut adapter = StubAdapter::default();
322        assert!(!adapter.is_powered());
323
324        adapter.init(&BleConfig::default()).await.unwrap();
325        assert!(adapter.is_powered());
326        assert_eq!(adapter.peer_count(), 0);
327        assert!(!adapter.supports_coded_phy());
328    }
329}