hive_btle/platform/
mod.rs1#[cfg(not(feature = "std"))]
38use alloc::{boxed::Box, format, string::String, string::ToString, vec::Vec};
39
40use async_trait::async_trait;
41
42use crate::config::{BleConfig, BlePhy, DiscoveryConfig};
43use crate::error::Result;
44use crate::transport::BleConnection;
45use crate::NodeId;
46
47#[cfg(all(feature = "linux", target_os = "linux"))]
49pub mod linux;
50
51#[cfg(feature = "android")]
52pub mod android;
53
54#[cfg(any(feature = "macos", feature = "ios"))]
55pub mod apple;
56
57#[cfg(feature = "windows")]
58pub mod windows;
59
60#[cfg(feature = "embedded")]
61pub mod embedded;
62
63#[cfg(feature = "esp32")]
64pub mod esp32;
65
66#[cfg(feature = "std")]
68pub mod mock;
69
70#[derive(Debug, Clone)]
72pub struct DiscoveredDevice {
73 pub address: String,
75 pub name: Option<String>,
77 pub rssi: i8,
79 pub is_hive_node: bool,
81 pub node_id: Option<NodeId>,
83 pub adv_data: Vec<u8>,
85}
86
87pub type DiscoveryCallback = std::sync::Arc<dyn Fn(DiscoveredDevice) + Send + Sync>;
89
90pub type ConnectionCallback = std::sync::Arc<dyn Fn(NodeId, ConnectionEvent) + Send + Sync>;
92
93#[derive(Debug, Clone)]
95pub enum ConnectionEvent {
96 Connected {
98 mtu: u16,
100 phy: BlePhy,
102 },
103 Disconnected {
105 reason: DisconnectReason,
107 },
108 ServicesDiscovered {
110 has_hive_service: bool,
112 },
113 DataReceived {
115 data: Vec<u8>,
117 },
118 MtuChanged {
120 mtu: u16,
122 },
123 PhyChanged {
125 phy: BlePhy,
127 },
128 RssiUpdated {
130 rssi: i8,
132 },
133}
134
135#[derive(Debug, Clone, Copy, PartialEq, Eq)]
137pub enum DisconnectReason {
138 LocalRequest,
140 RemoteRequest,
142 Timeout,
144 LinkLoss,
146 ConnectionFailed,
148 Unknown,
150}
151
152#[async_trait]
157pub trait BleAdapter: Send + Sync {
158 async fn init(&mut self, config: &BleConfig) -> Result<()>;
160
161 async fn start(&self) -> Result<()>;
163
164 async fn stop(&self) -> Result<()>;
166
167 fn is_powered(&self) -> bool;
169
170 fn address(&self) -> Option<String>;
172
173 async fn start_scan(&self, config: &DiscoveryConfig) -> Result<()>;
177
178 async fn stop_scan(&self) -> Result<()>;
180
181 async fn start_advertising(&self, config: &DiscoveryConfig) -> Result<()>;
183
184 async fn stop_advertising(&self) -> Result<()>;
186
187 fn set_discovery_callback(&mut self, callback: Option<DiscoveryCallback>);
189
190 async fn connect(&self, peer_id: &NodeId) -> Result<Box<dyn BleConnection>>;
194
195 async fn disconnect(&self, peer_id: &NodeId) -> Result<()>;
197
198 fn get_connection(&self, peer_id: &NodeId) -> Option<Box<dyn BleConnection>>;
200
201 fn peer_count(&self) -> usize;
203
204 fn connected_peers(&self) -> Vec<NodeId>;
206
207 fn set_connection_callback(&mut self, callback: Option<ConnectionCallback>);
209
210 async fn register_gatt_service(&self) -> Result<()>;
214
215 async fn unregister_gatt_service(&self) -> Result<()>;
217
218 async fn write_to_peer(
230 &self,
231 peer_id: &NodeId,
232 char_uuid: uuid::Uuid,
233 data: &[u8],
234 ) -> Result<()> {
235 let _ = (peer_id, char_uuid, data);
236 Err(crate::error::BleError::NotSupported(
237 "write_to_peer not implemented for this adapter".into(),
238 ))
239 }
240
241 fn supports_coded_phy(&self) -> bool;
245
246 fn supports_extended_advertising(&self) -> bool;
248
249 fn max_mtu(&self) -> u16;
251
252 fn max_connections(&self) -> u8;
254}
255
256#[derive(Debug, Default)]
258pub struct StubAdapter {
259 powered: bool,
260}
261
262#[async_trait]
263impl BleAdapter for StubAdapter {
264 async fn init(&mut self, _config: &BleConfig) -> Result<()> {
265 self.powered = true;
266 Ok(())
267 }
268
269 async fn start(&self) -> Result<()> {
270 Ok(())
271 }
272
273 async fn stop(&self) -> Result<()> {
274 Ok(())
275 }
276
277 fn is_powered(&self) -> bool {
278 self.powered
279 }
280
281 fn address(&self) -> Option<String> {
282 Some("00:00:00:00:00:00".to_string())
283 }
284
285 async fn start_scan(&self, _config: &DiscoveryConfig) -> Result<()> {
286 Ok(())
287 }
288
289 async fn stop_scan(&self) -> Result<()> {
290 Ok(())
291 }
292
293 async fn start_advertising(&self, _config: &DiscoveryConfig) -> Result<()> {
294 Ok(())
295 }
296
297 async fn stop_advertising(&self) -> Result<()> {
298 Ok(())
299 }
300
301 fn set_discovery_callback(&mut self, _callback: Option<DiscoveryCallback>) {}
302
303 async fn connect(&self, peer_id: &NodeId) -> Result<Box<dyn BleConnection>> {
304 Err(crate::error::BleError::NotSupported(format!(
305 "Stub adapter cannot connect to {}",
306 peer_id
307 )))
308 }
309
310 async fn disconnect(&self, _peer_id: &NodeId) -> Result<()> {
311 Ok(())
312 }
313
314 fn get_connection(&self, _peer_id: &NodeId) -> Option<Box<dyn BleConnection>> {
315 None
316 }
317
318 fn peer_count(&self) -> usize {
319 0
320 }
321
322 fn connected_peers(&self) -> Vec<NodeId> {
323 Vec::new()
324 }
325
326 fn set_connection_callback(&mut self, _callback: Option<ConnectionCallback>) {}
327
328 async fn register_gatt_service(&self) -> Result<()> {
329 Ok(())
330 }
331
332 async fn unregister_gatt_service(&self) -> Result<()> {
333 Ok(())
334 }
335
336 fn supports_coded_phy(&self) -> bool {
337 false
338 }
339
340 fn supports_extended_advertising(&self) -> bool {
341 false
342 }
343
344 fn max_mtu(&self) -> u16 {
345 23
346 }
347
348 fn max_connections(&self) -> u8 {
349 0
350 }
351}
352
353#[cfg(test)]
354mod tests {
355 use super::*;
356
357 #[tokio::test]
358 async fn test_stub_adapter() {
359 let mut adapter = StubAdapter::default();
360 assert!(!adapter.is_powered());
361
362 adapter.init(&BleConfig::default()).await.unwrap();
363 assert!(adapter.is_powered());
364 assert_eq!(adapter.peer_count(), 0);
365 assert!(!adapter.supports_coded_phy());
366 }
367}