aranet_core/
lib.rs

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