aranet_core/messages.rs
1//! Message types for UI/worker communication.
2//!
3//! This module defines the command and event enums used for bidirectional
4//! communication between UI threads and background BLE workers. These types
5//! are shared between TUI and GUI applications.
6//!
7//! # Architecture
8//!
9//! ```text
10//! +------------------+ Command +------------------+
11//! | UI Thread | --------------> | SensorWorker |
12//! | (egui/ratatui) | | (tokio runtime) |
13//! | | <-------------- | |
14//! +------------------+ SensorEvent +------------------+
15//! ```
16//!
17//! - [`Command`]: Messages sent from the UI thread to the background worker
18//! - [`SensorEvent`]: Events sent from the worker back to the UI thread
19
20use std::time::Duration;
21
22use crate::DiscoveredDevice;
23use crate::settings::DeviceSettings;
24use aranet_types::{CurrentReading, DeviceType, HistoryRecord};
25
26/// Commands sent from the UI thread to the background worker.
27///
28/// These commands represent user-initiated actions that require
29/// Bluetooth operations or other background processing.
30#[derive(Debug, Clone)]
31pub enum Command {
32 /// Load cached devices and readings from the store on startup.
33 LoadCachedData,
34
35 /// Scan for nearby Aranet devices.
36 Scan {
37 /// How long to scan for devices.
38 duration: Duration,
39 },
40
41 /// Connect to a specific device.
42 Connect {
43 /// The device identifier to connect to.
44 device_id: String,
45 },
46
47 /// Disconnect from a specific device.
48 Disconnect {
49 /// The device identifier to disconnect from.
50 device_id: String,
51 },
52
53 /// Refresh the current reading for a single device.
54 RefreshReading {
55 /// The device identifier to refresh.
56 device_id: String,
57 },
58
59 /// Refresh readings for all connected devices.
60 RefreshAll,
61
62 /// Sync history from device (download from BLE and save to store).
63 SyncHistory {
64 /// The device identifier to sync history for.
65 device_id: String,
66 },
67
68 /// Set the measurement interval for a device.
69 SetInterval {
70 /// The device identifier.
71 device_id: String,
72 /// The new interval in seconds.
73 interval_secs: u16,
74 },
75
76 /// Set the Bluetooth range for a device.
77 SetBluetoothRange {
78 /// The device identifier.
79 device_id: String,
80 /// Whether to use extended range (true) or standard (false).
81 extended: bool,
82 },
83
84 /// Set Smart Home integration mode for a device.
85 SetSmartHome {
86 /// The device identifier.
87 device_id: String,
88 /// Whether to enable Smart Home mode.
89 enabled: bool,
90 },
91
92 /// Shut down the worker thread.
93 Shutdown,
94}
95
96/// Cached device data loaded from the store.
97#[derive(Debug, Clone)]
98pub struct CachedDevice {
99 /// Device identifier.
100 pub id: String,
101 /// Device name.
102 pub name: Option<String>,
103 /// Device type.
104 pub device_type: Option<DeviceType>,
105 /// Latest reading, if available.
106 pub reading: Option<CurrentReading>,
107 /// When history was last synced.
108 pub last_sync: Option<time::OffsetDateTime>,
109}
110
111/// Events sent from the background worker to the UI thread.
112///
113/// These events represent the results of background operations
114/// and are used to update the UI state.
115#[derive(Debug, Clone)]
116pub enum SensorEvent {
117 /// Cached data loaded from the store on startup.
118 CachedDataLoaded {
119 /// Cached devices with their latest readings.
120 devices: Vec<CachedDevice>,
121 },
122
123 /// A device scan has started.
124 ScanStarted,
125
126 /// A device scan has completed successfully.
127 ScanComplete {
128 /// The list of discovered devices.
129 devices: Vec<DiscoveredDevice>,
130 },
131
132 /// A device scan failed.
133 ScanError {
134 /// Description of the error.
135 error: String,
136 },
137
138 /// Attempting to connect to a device.
139 DeviceConnecting {
140 /// The device identifier.
141 device_id: String,
142 },
143
144 /// Successfully connected to a device.
145 DeviceConnected {
146 /// The device identifier.
147 device_id: String,
148 /// The device name, if available.
149 name: Option<String>,
150 /// The device type, if detected.
151 device_type: Option<DeviceType>,
152 /// RSSI signal strength in dBm.
153 rssi: Option<i16>,
154 },
155
156 /// Disconnected from a device.
157 DeviceDisconnected {
158 /// The device identifier.
159 device_id: String,
160 },
161
162 /// Failed to connect to a device.
163 ConnectionError {
164 /// The device identifier.
165 device_id: String,
166 /// Description of the error.
167 error: String,
168 },
169
170 /// Received an updated reading from a device.
171 ReadingUpdated {
172 /// The device identifier.
173 device_id: String,
174 /// The current sensor reading.
175 reading: CurrentReading,
176 },
177
178 /// Failed to read from a device.
179 ReadingError {
180 /// The device identifier.
181 device_id: String,
182 /// Description of the error.
183 error: String,
184 },
185
186 /// Historical data loaded for a device.
187 HistoryLoaded {
188 /// The device identifier.
189 device_id: String,
190 /// The historical records.
191 records: Vec<HistoryRecord>,
192 },
193
194 /// History sync started for a device.
195 HistorySyncStarted {
196 /// The device identifier.
197 device_id: String,
198 },
199
200 /// History sync completed for a device.
201 HistorySynced {
202 /// The device identifier.
203 device_id: String,
204 /// Number of records synced.
205 count: usize,
206 },
207
208 /// History sync failed for a device.
209 HistorySyncError {
210 /// The device identifier.
211 device_id: String,
212 /// Description of the error.
213 error: String,
214 },
215
216 /// Measurement interval changed for a device.
217 IntervalChanged {
218 /// The device identifier.
219 device_id: String,
220 /// The new interval in seconds.
221 interval_secs: u16,
222 },
223
224 /// Failed to set measurement interval.
225 IntervalError {
226 /// The device identifier.
227 device_id: String,
228 /// Description of the error.
229 error: String,
230 },
231
232 /// Device settings loaded from the device.
233 SettingsLoaded {
234 /// The device identifier.
235 device_id: String,
236 /// The device settings.
237 settings: DeviceSettings,
238 },
239
240 /// Bluetooth range changed for a device.
241 BluetoothRangeChanged {
242 /// The device identifier.
243 device_id: String,
244 /// Whether extended range is now enabled.
245 extended: bool,
246 },
247
248 /// Failed to set Bluetooth range.
249 BluetoothRangeError {
250 /// The device identifier.
251 device_id: String,
252 /// Description of the error.
253 error: String,
254 },
255
256 /// Smart Home setting changed for a device.
257 SmartHomeChanged {
258 /// The device identifier.
259 device_id: String,
260 /// Whether Smart Home mode is now enabled.
261 enabled: bool,
262 },
263
264 /// Failed to set Smart Home mode.
265 SmartHomeError {
266 /// The device identifier.
267 device_id: String,
268 /// Description of the error.
269 error: String,
270 },
271}
272
273#[cfg(test)]
274mod tests {
275 use super::*;
276
277 #[test]
278 fn test_command_debug() {
279 let cmd = Command::Scan {
280 duration: Duration::from_secs(5),
281 };
282 let debug = format!("{:?}", cmd);
283 assert!(debug.contains("Scan"));
284 assert!(debug.contains("5"));
285 }
286
287 #[test]
288 fn test_command_clone() {
289 let cmd = Command::Connect {
290 device_id: "test-device".to_string(),
291 };
292 let cloned = cmd.clone();
293 match cloned {
294 Command::Connect { device_id } => assert_eq!(device_id, "test-device"),
295 _ => panic!("Expected Connect variant"),
296 }
297 }
298
299 #[test]
300 fn test_sensor_event_debug() {
301 let event = SensorEvent::ScanStarted;
302 let debug = format!("{:?}", event);
303 assert!(debug.contains("ScanStarted"));
304 }
305
306 #[test]
307 fn test_cached_device_default_values() {
308 let device = CachedDevice {
309 id: "test".to_string(),
310 name: None,
311 device_type: None,
312 reading: None,
313 last_sync: None,
314 };
315 assert_eq!(device.id, "test");
316 assert!(device.name.is_none());
317 }
318}