aranet_core/lib.rs
1//! Core BLE library for Aranet environmental sensors.
2//!
3//! This crate provides low-level Bluetooth Low Energy (BLE) communication
4//! with Aranet sensors including the Aranet4, Aranet2, AranetRn+ (Radon), and
5//! Aranet Radiation devices.
6//!
7//! # Features
8//!
9//! - **Device discovery**: Scan for nearby Aranet devices via BLE
10//! - **Current readings**: CO₂, temperature, pressure, humidity, radon, radiation
11//! - **Historical data**: Download measurement history with timestamps
12//! - **Device settings**: Read/write measurement interval, Bluetooth range
13//! - **Auto-reconnection**: Configurable backoff and retry logic
14//! - **Real-time streaming**: Subscribe to sensor value changes
15//! - **Multi-device support**: Manage multiple sensors simultaneously
16//!
17//! # Supported Devices
18//!
19//! | Device | Sensors |
20//! |--------|---------|
21//! | Aranet4 | CO₂, Temperature, Pressure, Humidity |
22//! | Aranet2 | Temperature, Humidity |
23//! | AranetRn+ | Radon (Bq/m³), Temperature, Pressure, Humidity |
24//! | Aranet Radiation | Dose Rate (µSv/h), Total Dose (mSv) |
25//!
26//! # Platform Differences
27//!
28//! Device identification varies by platform due to differences in BLE implementations:
29//!
30//! - **macOS**: Devices are identified by a UUID assigned by CoreBluetooth. This UUID
31//! is stable for a given device on a given Mac, but differs between Macs. The UUID
32//! is not the same as the device's MAC address.
33//!
34//! - **Linux/Windows**: Devices are identified by their Bluetooth MAC address
35//! (e.g., `AA:BB:CC:DD:EE:FF`). This is consistent across machines.
36//!
37//! When storing device identifiers for reconnection, be aware that:
38//! - On macOS, the UUID may change if Bluetooth is reset or the device is unpaired
39//! - Cross-platform applications should store both the device name and identifier
40//! - The [`Device::address()`] method returns the appropriate identifier for the platform
41//!
42//! # Quick Start
43//!
44//! ```no_run
45//! use aranet_core::{Device, scan};
46//!
47//! #[tokio::main]
48//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
49//! // Scan for devices
50//! let devices = scan::scan_for_devices().await?;
51//! println!("Found {} devices", devices.len());
52//!
53//! // Connect to a device
54//! let device = Device::connect("Aranet4 12345").await?;
55//!
56//! // Read current values
57//! let reading = device.read_current().await?;
58//! println!("CO2: {} ppm", reading.co2);
59//!
60//! // Read device info
61//! let info = device.read_device_info().await?;
62//! println!("Serial: {}", info.serial);
63//!
64//! Ok(())
65//! }
66//! ```
67
68pub mod advertisement;
69pub mod commands;
70pub mod device;
71pub mod diagnostics;
72pub mod error;
73pub mod events;
74pub mod guard;
75pub mod history;
76pub mod manager;
77pub mod messages;
78pub mod metrics;
79pub mod mock;
80pub mod passive;
81pub mod platform;
82pub mod readings;
83pub mod reconnect;
84pub mod retry;
85pub mod scan;
86pub mod settings;
87pub mod streaming;
88pub mod thresholds;
89pub mod traits;
90pub mod util;
91pub mod validation;
92
93#[cfg(feature = "service-client")]
94pub mod service_client;
95
96// Re-export types and uuid modules from aranet-types for backwards compatibility
97pub use aranet_types::types;
98pub use aranet_types::uuid;
99
100// Core exports
101pub use device::{ConnectionConfig, Device, SignalQuality};
102pub use error::{ConnectionFailureReason, DeviceNotFoundReason, Error, Result};
103pub use history::{
104 HistoryCheckpoint, HistoryInfo, HistoryOptions, HistoryParam, PartialHistoryData,
105};
106pub use readings::ExtendedReading;
107pub use scan::{
108 DiscoveredDevice, FindProgress, ProgressCallback, ScanOptions, find_device_with_progress,
109 scan_with_retry,
110};
111pub use settings::{BluetoothRange, CalibrationData, DeviceSettings, MeasurementInterval};
112pub use traits::AranetDevice;
113
114/// Type alias for a shared device reference.
115///
116/// This is the recommended way to share a `Device` across multiple tasks.
117/// Since `Device` intentionally does not implement `Clone` (to prevent
118/// connection ownership ambiguity), wrapping it in `Arc` is the standard
119/// pattern for concurrent access.
120///
121/// # Choosing the Right Device Type
122///
123/// This crate provides several device types for different use cases:
124///
125/// | Type | Use Case | Auto-Reconnect | Thread-Safe |
126/// |------|----------|----------------|-------------|
127/// | [`Device`] | Single command, short-lived | No | Yes (via Arc) |
128/// | [`ReconnectingDevice`] | Long-running apps | Yes | Yes |
129/// | [`SharedDevice`] | Sharing Device across tasks | No | Yes |
130/// | [`DeviceManager`] | Managing multiple devices | Yes | Yes |
131///
132/// ## Decision Guide
133///
134/// ### Use [`Device`] when:
135/// - Running a single command (read, history download)
136/// - Connection lifetime is short and well-defined
137/// - You'll handle reconnection yourself
138///
139/// ```no_run
140/// # async fn example() -> aranet_core::Result<()> {
141/// use aranet_core::Device;
142/// let device = Device::connect("Aranet4 12345").await?;
143/// let reading = device.read_current().await?;
144/// device.disconnect().await?;
145/// # Ok(())
146/// # }
147/// ```
148///
149/// ### Use [`ReconnectingDevice`] when:
150/// - Building a long-running application (daemon, service)
151/// - You want automatic reconnection on connection loss
152/// - Continuous monitoring over extended periods
153///
154/// ```no_run
155/// # async fn example() -> aranet_core::Result<()> {
156/// use aranet_core::{AranetDevice, ReconnectingDevice, ReconnectOptions};
157/// let options = ReconnectOptions::default();
158/// let device = ReconnectingDevice::connect("Aranet4 12345", options).await?;
159/// // Will auto-reconnect on connection loss
160/// let reading = device.read_current().await?;
161/// # Ok(())
162/// # }
163/// ```
164///
165/// ### Use [`SharedDevice`] when:
166/// - Sharing a single [`Device`] across multiple async tasks
167/// - You need concurrent reads but want one connection
168///
169/// ```no_run
170/// # async fn example() -> aranet_core::Result<()> {
171/// use aranet_core::{Device, SharedDevice};
172/// use std::sync::Arc;
173///
174/// let device = Device::connect("Aranet4 12345").await?;
175/// let shared: SharedDevice = Arc::new(device);
176///
177/// let shared_clone = Arc::clone(&shared);
178/// tokio::spawn(async move {
179/// let reading = shared_clone.read_current().await;
180/// });
181/// # Ok(())
182/// # }
183/// ```
184///
185/// ### Use [`DeviceManager`] when:
186/// - Managing multiple devices simultaneously
187/// - Need centralized connection/disconnection handling
188/// - Building a multi-device monitoring application
189///
190/// ```no_run
191/// # async fn example() -> aranet_core::Result<()> {
192/// use aranet_core::DeviceManager;
193/// let manager = DeviceManager::new();
194/// manager.add_device("AA:BB:CC:DD:EE:FF").await?;
195/// manager.add_device("11:22:33:44:55:66").await?;
196/// // Manager handles connections for all devices
197/// # Ok(())
198/// # }
199/// ```
200pub type SharedDevice = std::sync::Arc<Device>;
201
202// New module exports
203pub use advertisement::{AdvertisementData, parse_advertisement, parse_advertisement_with_name};
204pub use commands::{
205 HISTORY_V1_REQUEST, HISTORY_V2_REQUEST, SET_BLUETOOTH_RANGE, SET_INTERVAL, SET_SMART_HOME,
206};
207pub use diagnostics::{
208 AdapterInfo, AdapterState, BluetoothDiagnostics, ConnectionStats, DiagnosticsCollector,
209 ErrorCategory, OperationStats, RecordedError, global_diagnostics,
210};
211pub use events::{DeviceEvent, EventReceiver, EventSender};
212pub use guard::{DeviceGuard, SharedDeviceGuard};
213pub use manager::{AdaptiveInterval, DeviceManager, DevicePriority, ManagedDevice, ManagerConfig};
214pub use messages::{CachedDevice, Command, SensorEvent};
215pub use metrics::{ConnectionMetrics, OperationMetrics};
216pub use mock::{MockDevice, MockDeviceBuilder};
217pub use passive::{PassiveMonitor, PassiveMonitorOptions, PassiveReading};
218pub use platform::{
219 AliasStore, DeviceAlias, Platform, PlatformConfig, current_platform, platform_config,
220};
221pub use reconnect::{ReconnectOptions, ReconnectingDevice};
222pub use retry::{RetryConfig, with_retry};
223pub use streaming::{ReadingStream, StreamOptions, StreamOptionsBuilder};
224pub use thresholds::{Co2Level, ThresholdConfig, Thresholds};
225pub use util::{create_identifier, format_peripheral_id};
226pub use validation::{ReadingValidator, ValidationResult, ValidationWarning};
227
228// Re-export from aranet-types
229pub use aranet_types::uuid as uuids;
230pub use aranet_types::{CurrentReading, DeviceInfo, DeviceType, HistoryRecord, Status};