pub struct DeviceManager { /* private fields */ }Expand description
Manager for multiple Aranet devices.
Implementations§
Source§impl DeviceManager
impl DeviceManager
Sourcepub fn with_event_capacity(capacity: usize) -> Self
pub fn with_event_capacity(capacity: usize) -> Self
Create a manager with custom event capacity.
Sourcepub fn with_config(config: ManagerConfig) -> Self
pub fn with_config(config: ManagerConfig) -> Self
Create a manager with full configuration.
Sourcepub fn events(&self) -> &EventDispatcher
pub fn events(&self) -> &EventDispatcher
Get the event dispatcher for subscribing to events.
Sourcepub fn config(&self) -> &ManagerConfig
pub fn config(&self) -> &ManagerConfig
Get the manager configuration.
Sourcepub async fn scan(&self) -> Result<Vec<DiscoveredDevice>>
pub async fn scan(&self) -> Result<Vec<DiscoveredDevice>>
Scan for available devices.
Sourcepub async fn scan_with_options(
&self,
options: ScanOptions,
) -> Result<Vec<DiscoveredDevice>>
pub async fn scan_with_options( &self, options: ScanOptions, ) -> Result<Vec<DiscoveredDevice>>
Scan with custom options.
Sourcepub async fn add_device(&self, identifier: &str) -> Result<()>
pub async fn add_device(&self, identifier: &str) -> Result<()>
Add a device to the manager by identifier.
Sourcepub async fn add_device_with_options(
&self,
identifier: &str,
reconnect_options: ReconnectOptions,
) -> Result<()>
pub async fn add_device_with_options( &self, identifier: &str, reconnect_options: ReconnectOptions, ) -> Result<()>
Add a device with custom reconnect options.
Sourcepub async fn connect(&self, identifier: &str) -> Result<()>
pub async fn connect(&self, identifier: &str) -> Result<()>
Connect to a device.
This method performs an atomic connect-or-skip operation:
- If the device doesn’t exist, it’s added and connected
- If the device exists but is not connected, it’s connected
- If the device is already connected, this is a no-op
§Connection Limits
If max_concurrent_connections is set in the config and would be exceeded,
this method returns an error. Use connected_count() to check the current
number of connections before calling this method.
The lock is held during the device entry update to prevent race conditions, but released during the actual BLE connection to avoid blocking other operations.
Sourcepub async fn disconnect(&self, identifier: &str) -> Result<()>
pub async fn disconnect(&self, identifier: &str) -> Result<()>
Disconnect from a device.
Sourcepub async fn remove_device(&self, identifier: &str) -> Result<()>
pub async fn remove_device(&self, identifier: &str) -> Result<()>
Remove a device from the manager.
Sourcepub async fn device_ids(&self) -> Vec<String>
pub async fn device_ids(&self) -> Vec<String>
Get a list of all managed device IDs.
Sourcepub async fn device_count(&self) -> usize
pub async fn device_count(&self) -> usize
Get the number of managed devices.
Sourcepub async fn connected_count(&self) -> usize
pub async fn connected_count(&self) -> usize
Get the number of connected devices (fast, doesn’t query BLE).
This returns the number of devices that have an active device handle,
without querying the BLE stack. Use connected_count_verified for
an accurate count that queries each device.
Sourcepub async fn can_connect(&self) -> bool
pub async fn can_connect(&self) -> bool
Check if a new connection can be made without exceeding the limit.
Returns true if another connection can be made, false if at limit.
Always returns true if max_concurrent_connections is 0 (unlimited).
Sourcepub async fn connection_status(&self) -> (usize, usize)
pub async fn connection_status(&self) -> (usize, usize)
Get the connection limit status.
Returns (current_connections, max_connections). If max is 0, there is no limit.
Sourcepub async fn available_connections(&self) -> Option<usize>
pub async fn available_connections(&self) -> Option<usize>
Get the number of available connection slots.
Returns None if there is no connection limit (unlimited).
Sourcepub async fn connected_count_verified(&self) -> usize
pub async fn connected_count_verified(&self) -> usize
Get the number of connected devices (verified via BLE).
This method queries each device to verify its connection status. The lock is released before making BLE calls to avoid contention.
Sourcepub async fn read_current(&self, identifier: &str) -> Result<CurrentReading>
pub async fn read_current(&self, identifier: &str) -> Result<CurrentReading>
Read current values from a specific device.
Sourcepub async fn read_all(&self) -> HashMap<String, Result<CurrentReading>>
pub async fn read_all(&self) -> HashMap<String, Result<CurrentReading>>
Read current values from all connected devices (in parallel).
This method releases the lock before performing async BLE operations, allowing other tasks to add/remove devices while reads are in progress. All reads are performed in parallel for maximum performance.
Sourcepub async fn connect_all(&self) -> HashMap<String, Result<()>>
pub async fn connect_all(&self) -> HashMap<String, Result<()>>
Connect to all known devices (in parallel).
Returns a map of device IDs to connection results.
Sourcepub async fn disconnect_all(&self) -> HashMap<String, Result<()>>
pub async fn disconnect_all(&self) -> HashMap<String, Result<()>>
Disconnect from all devices (in parallel).
Returns a map of device IDs to disconnection results.
Sourcepub fn try_is_connected(&self, identifier: &str) -> Option<bool>
pub fn try_is_connected(&self, identifier: &str) -> Option<bool>
Check if a specific device is connected (fast, doesn’t query BLE).
This method attempts to check if a device has an active connection handle
without blocking. Returns None if the lock couldn’t be acquired immediately,
or Some(bool) indicating whether the device has a connection handle.
Note: This only checks if we have a device handle, not whether the actual
BLE connection is still alive. Use is_connected for
a verified check.
Sourcepub async fn is_connected(&self, identifier: &str) -> bool
pub async fn is_connected(&self, identifier: &str) -> bool
Check if a specific device is connected (verified via BLE).
The lock is released before making the BLE call.
Sourcepub async fn get_device_info(&self, identifier: &str) -> Option<DeviceInfo>
pub async fn get_device_info(&self, identifier: &str) -> Option<DeviceInfo>
Get device info for a specific device.
Sourcepub async fn get_last_reading(&self, identifier: &str) -> Option<CurrentReading>
pub async fn get_last_reading(&self, identifier: &str) -> Option<CurrentReading>
Get the last cached reading for a device.
Sourcepub fn start_health_monitor(
self: &Arc<Self>,
cancel_token: CancellationToken,
) -> JoinHandle<()>
pub fn start_health_monitor( self: &Arc<Self>, cancel_token: CancellationToken, ) -> JoinHandle<()>
Start a background health check task that monitors connection status.
This spawns a task that periodically checks device connections and attempts to reconnect devices that have auto_reconnect enabled.
The task will run until the provided cancellation token is cancelled.
§Adaptive Intervals
If use_adaptive_interval is enabled in the config, the health check
interval will automatically adjust based on connection stability:
- When connections are stable, checks become less frequent (up to
max_health_check_interval) - When connections are unstable, checks become more frequent (down to
min_health_check_interval)
§Connection Validation
If use_connection_validation is enabled, health checks will perform
an actual BLE read (device.validate_connection()) to catch “zombie connections”
where the BLE stack thinks it’s connected but the device is out of range.
§Example
use tokio_util::sync::CancellationToken;
let manager = Arc::new(DeviceManager::new());
let cancel = CancellationToken::new();
let handle = manager.start_health_monitor(cancel.clone());
// Later, to stop the health monitor:
cancel.cancel();
handle.await.unwrap();Sourcepub async fn add_device_with_priority(
&self,
identifier: &str,
priority: DevicePriority,
) -> Result<()>
pub async fn add_device_with_priority( &self, identifier: &str, priority: DevicePriority, ) -> Result<()>
Add a device with priority.
Sourcepub async fn lowest_priority_connected(&self) -> Option<String>
pub async fn lowest_priority_connected(&self) -> Option<String>
Get the lowest priority connected device that could be disconnected.
Returns None if no devices can be disconnected (all are Critical priority or not connected).
Sourcepub async fn evict_lowest_priority(&self) -> Result<bool>
pub async fn evict_lowest_priority(&self) -> Result<bool>
Disconnect the lowest priority device to make room for a new connection.
Returns Ok(true) if a device was disconnected, Ok(false) if no eligible device found.
Sourcepub fn start_hybrid_monitor(
self: &Arc<Self>,
cancel_token: CancellationToken,
passive_options: Option<PassiveMonitorOptions>,
) -> JoinHandle<()>
pub fn start_hybrid_monitor( self: &Arc<Self>, cancel_token: CancellationToken, passive_options: Option<PassiveMonitorOptions>, ) -> JoinHandle<()>
Start hybrid monitoring using both passive (advertisement) and active connections.
This is the most efficient way to monitor multiple devices:
- Passive monitoring: Uses BLE advertisements to receive real-time readings without maintaining connections. Lower power consumption, unlimited devices.
- Active connections: Only established when needed (history download, settings changes).
§Requirements
Smart Home integration must be enabled on each device for passive monitoring.
§Example
use tokio_util::sync::CancellationToken;
let manager = Arc::new(DeviceManager::new());
let cancel = CancellationToken::new();
let handle = manager.start_hybrid_monitor(cancel.clone(), None);
// Receive readings via manager events
let mut rx = manager.events().subscribe();
while let Ok(event) = rx.recv().await {
if let DeviceEvent::Reading { device, reading } = event {
println!("{}: CO2 = {} ppm", device.id, reading.co2);
}
}Sourcepub async fn read_hybrid(
&self,
identifier: &str,
max_passive_age: Option<Duration>,
) -> Result<CurrentReading>
pub async fn read_hybrid( &self, identifier: &str, max_passive_age: Option<Duration>, ) -> Result<CurrentReading>
Get a reading using hybrid approach: try passive first, fall back to active.
This method checks if a recent passive reading is available. If not, it establishes an active connection to read the value.
§Arguments
identifier- Device identifiermax_passive_age- Maximum age of passive reading to accept (default: 60s)
Sourcepub async fn supports_passive_monitoring(&self, identifier: &str) -> bool
pub async fn supports_passive_monitoring(&self, identifier: &str) -> bool
Check if a device supports passive monitoring (Smart Home enabled).
This performs a quick scan to check if the device is broadcasting advertisement data with sensor readings.