rpi_host/types.rs
1//! Types for WiFi management.
2//!
3//! This module contains all the data types used throughout the library:
4//!
5//! - [`WifiMode`]: Current operating mode (Client, Hotspot, Disconnected)
6//! - [`NetworkInfo`]: Information about discovered WiFi networks
7//! - [`HotspotConfig`]: Configuration builder for creating hotspots
8//! - [`HotspotBand`]: WiFi frequency band selection
9//! - [`SecurityType`]: Network security classification
10//! - [`ConnectionStatus`]: Detailed connection status
11//! - [`ConnectivityResult`]: Internet connectivity check results
12
13/// The current operating mode of the WiFi interface.
14///
15/// # Example
16///
17/// ```no_run
18/// use rpi_host::{WifiManager, WifiMode};
19///
20/// let wifi = WifiManager::new()?;
21/// match wifi.get_mode()? {
22/// WifiMode::Client => println!("Connected as client"),
23/// WifiMode::Hotspot => println!("Running as hotspot"),
24/// WifiMode::Disconnected => println!("Not connected"),
25/// }
26/// # Ok::<(), rpi_host::WifiError>(())
27/// ```
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum WifiMode {
30 /// WiFi is disabled or not connected to any network.
31 Disconnected,
32 /// Connected to a WiFi network as a client.
33 Client,
34 /// Operating as a WiFi hotspot (access point).
35 Hotspot,
36}
37
38impl std::fmt::Display for WifiMode {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 match self {
41 WifiMode::Disconnected => write!(f, "Disconnected"),
42 WifiMode::Client => write!(f, "Client"),
43 WifiMode::Hotspot => write!(f, "Hotspot"),
44 }
45 }
46}
47
48/// Security type of a WiFi network.
49///
50/// This enum represents the various WiFi security protocols that may be
51/// encountered when scanning networks.
52///
53/// # Example
54///
55/// ```
56/// use rpi_host::SecurityType;
57///
58/// let security = SecurityType::from("WPA2");
59/// assert!(matches!(security, SecurityType::Wpa2Psk));
60/// ```
61#[derive(Debug, Clone, PartialEq, Eq)]
62pub enum SecurityType {
63 /// Open network with no security. Anyone can connect.
64 Open,
65 /// WEP encryption. Legacy and insecure - avoid if possible.
66 Wep,
67 /// WPA Personal (PSK). Older but still common.
68 WpaPsk,
69 /// WPA2 Personal. Currently the most common security type.
70 Wpa2Psk,
71 /// WPA3 Personal. Newest and most secure.
72 Wpa3Psk,
73 /// WPA/WPA2/WPA3 Enterprise (802.1X). Requires username/password or certificate.
74 Enterprise,
75 /// Unknown or unrecognized security type.
76 Unknown(String),
77}
78
79impl std::fmt::Display for SecurityType {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 match self {
82 SecurityType::Open => write!(f, "Open"),
83 SecurityType::Wep => write!(f, "WEP"),
84 SecurityType::WpaPsk => write!(f, "WPA-PSK"),
85 SecurityType::Wpa2Psk => write!(f, "WPA2-PSK"),
86 SecurityType::Wpa3Psk => write!(f, "WPA3-PSK"),
87 SecurityType::Enterprise => write!(f, "Enterprise"),
88 SecurityType::Unknown(s) => write!(f, "Unknown({})", s),
89 }
90 }
91}
92
93impl From<&str> for SecurityType {
94 fn from(s: &str) -> Self {
95 let lower = s.to_lowercase();
96 if lower.is_empty() || lower == "--" {
97 SecurityType::Open
98 } else if lower.contains("wpa3") {
99 SecurityType::Wpa3Psk
100 } else if lower.contains("wpa2") {
101 SecurityType::Wpa2Psk
102 } else if lower.contains("wpa") {
103 SecurityType::WpaPsk
104 } else if lower.contains("wep") {
105 SecurityType::Wep
106 } else if lower.contains("802.1x") || lower.contains("enterprise") {
107 SecurityType::Enterprise
108 } else {
109 SecurityType::Unknown(s.to_string())
110 }
111 }
112}
113
114/// Information about a discovered WiFi network.
115///
116/// Returned by [`WifiManager::scan()`][crate::WifiManager::scan] when scanning
117/// for available networks.
118///
119/// # Example
120///
121/// ```no_run
122/// use rpi_host::WifiManager;
123///
124/// let wifi = WifiManager::new()?;
125/// for network in wifi.scan()? {
126/// println!(
127/// "{}: {}% signal, {} security{}",
128/// network.ssid,
129/// network.signal_strength,
130/// network.security,
131/// if network.is_connected { " (connected)" } else { "" }
132/// );
133/// }
134/// # Ok::<(), rpi_host::WifiError>(())
135/// ```
136#[derive(Debug, Clone)]
137pub struct NetworkInfo {
138 /// The SSID (network name).
139 pub ssid: String,
140
141 /// Signal strength as a percentage (0-100).
142 ///
143 /// Higher values indicate a stronger signal.
144 pub signal_strength: u8,
145
146 /// Security type of the network.
147 pub security: SecurityType,
148
149 /// BSSID (MAC address of the access point).
150 ///
151 /// Useful for distinguishing between access points with the same SSID.
152 pub bssid: Option<String>,
153
154 /// Frequency in MHz (e.g., 2412 for channel 1, 5180 for channel 36).
155 pub frequency: Option<u32>,
156
157 /// Whether the device is currently connected to this network.
158 pub is_connected: bool,
159}
160
161impl NetworkInfo {
162 /// Create a new NetworkInfo with required fields.
163 ///
164 /// # Arguments
165 ///
166 /// * `ssid` - The network name
167 /// * `signal_strength` - Signal strength (0-100)
168 /// * `security` - Security type of the network
169 pub fn new(ssid: impl Into<String>, signal_strength: u8, security: SecurityType) -> Self {
170 Self {
171 ssid: ssid.into(),
172 signal_strength,
173 security,
174 bssid: None,
175 frequency: None,
176 is_connected: false,
177 }
178 }
179
180 /// Set the BSSID (builder pattern).
181 pub fn with_bssid(mut self, bssid: impl Into<String>) -> Self {
182 self.bssid = Some(bssid.into());
183 self
184 }
185
186 /// Set the frequency (builder pattern).
187 pub fn with_frequency(mut self, frequency: u32) -> Self {
188 self.frequency = Some(frequency);
189 self
190 }
191
192 /// Set the connected status (builder pattern).
193 pub fn with_connected(mut self, connected: bool) -> Self {
194 self.is_connected = connected;
195 self
196 }
197}
198
199/// Configuration for creating a WiFi hotspot.
200///
201/// Use the builder pattern to configure hotspot settings before passing
202/// to [`WifiManager::start_hotspot_with_config()`][crate::WifiManager::start_hotspot_with_config].
203///
204/// # Example
205///
206/// ```no_run
207/// use rpi_host::{WifiManager, HotspotConfig, HotspotBand};
208///
209/// let wifi = WifiManager::new()?;
210///
211/// // Basic configuration
212/// let config = HotspotConfig::new("MyHotspot")
213/// .with_password("password123");
214///
215/// // Advanced configuration
216/// let config = HotspotConfig::new("My5GHotspot")
217/// .with_password("securepass")
218/// .with_band(HotspotBand::A)
219/// .with_channel(36);
220///
221/// wifi.start_hotspot_with_config(config)?;
222/// # Ok::<(), rpi_host::WifiError>(())
223/// ```
224#[derive(Debug, Clone)]
225pub struct HotspotConfig {
226 /// The SSID (name) for the hotspot.
227 pub ssid: String,
228
229 /// Optional password for the hotspot.
230 ///
231 /// If `None`, creates an open network. If `Some`, must be at least 8 characters.
232 pub password: Option<String>,
233
234 /// The wireless interface to use.
235 ///
236 /// Defaults to "wlan0".
237 pub interface: String,
238
239 /// WiFi frequency band to use.
240 ///
241 /// Defaults to 2.4GHz ([`HotspotBand::Bg`]) for maximum compatibility.
242 pub band: HotspotBand,
243
244 /// WiFi channel to use.
245 ///
246 /// Set to 0 for automatic channel selection.
247 pub channel: u8,
248}
249
250impl HotspotConfig {
251 /// Create a new hotspot configuration with the given SSID.
252 ///
253 /// Uses default values:
254 /// - No password (open network)
255 /// - Interface: wlan0
256 /// - Band: 2.4GHz
257 /// - Channel: auto
258 pub fn new(ssid: impl Into<String>) -> Self {
259 Self {
260 ssid: ssid.into(),
261 password: None,
262 interface: "wlan0".to_string(),
263 band: HotspotBand::Bg,
264 channel: 0,
265 }
266 }
267
268 /// Set the hotspot password (must be at least 8 characters).
269 pub fn with_password(mut self, password: impl Into<String>) -> Self {
270 self.password = Some(password.into());
271 self
272 }
273
274 /// Set the wireless interface to use.
275 pub fn with_interface(mut self, interface: impl Into<String>) -> Self {
276 self.interface = interface.into();
277 self
278 }
279
280 /// Set the frequency band.
281 ///
282 /// Use [`HotspotBand::Bg`] (2.4GHz) for maximum device compatibility,
283 /// or [`HotspotBand::A`] (5GHz) for faster speeds with compatible devices.
284 pub fn with_band(mut self, band: HotspotBand) -> Self {
285 self.band = band;
286 self
287 }
288
289 /// Set the WiFi channel.
290 ///
291 /// For 2.4GHz, valid channels are typically 1-11 (varies by region).
292 /// For 5GHz, common channels include 36, 40, 44, 48, 149, 153, 157, 161.
293 /// Set to 0 for automatic selection.
294 pub fn with_channel(mut self, channel: u8) -> Self {
295 self.channel = channel;
296 self
297 }
298}
299
300/// WiFi frequency band for hotspot mode.
301///
302/// # Comparison
303///
304/// | Band | Frequency | Range | Speed | Compatibility |
305/// |------|-----------|-------|-------|---------------|
306/// | Bg | 2.4 GHz | Good | Lower | Excellent |
307/// | A | 5 GHz | Lower | Higher| Good |
308#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
309pub enum HotspotBand {
310 /// 2.4 GHz band (802.11b/g/n).
311 ///
312 /// Better range and wall penetration. Compatible with all WiFi devices.
313 /// May experience more interference from other devices.
314 #[default]
315 Bg,
316
317 /// 5 GHz band (802.11a/n/ac).
318 ///
319 /// Faster speeds and less interference, but shorter range.
320 /// Not all devices support 5GHz.
321 A,
322}
323
324impl std::fmt::Display for HotspotBand {
325 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
326 match self {
327 HotspotBand::Bg => write!(f, "bg"),
328 HotspotBand::A => write!(f, "a"),
329 }
330 }
331}
332
333/// Detailed status of the current WiFi connection.
334///
335/// Returned by [`WifiManager::status()`][crate::WifiManager::status].
336///
337/// # Example
338///
339/// ```no_run
340/// use rpi_host::WifiManager;
341///
342/// let wifi = WifiManager::new()?;
343/// let status = wifi.status()?;
344///
345/// println!("Interface: {}", status.interface);
346/// println!("Mode: {}", status.mode);
347/// println!("Connected to: {:?}", status.connection_name);
348/// println!("IP: {:?}", status.ip_address);
349/// println!("Internet: {}", status.has_internet);
350/// # Ok::<(), rpi_host::WifiError>(())
351/// ```
352#[derive(Debug, Clone)]
353pub struct ConnectionStatus {
354 /// Current operating mode.
355 pub mode: WifiMode,
356
357 /// Name of the active connection.
358 ///
359 /// For client mode, this is the SSID. For hotspot mode, this is the hotspot name.
360 /// `None` if disconnected.
361 pub connection_name: Option<String>,
362
363 /// IP address assigned to the interface.
364 ///
365 /// `None` if not connected or no IP assigned yet.
366 pub ip_address: Option<String>,
367
368 /// Whether internet is reachable.
369 ///
370 /// Only meaningful in client mode. Always `false` in hotspot mode.
371 pub has_internet: bool,
372
373 /// The wireless interface name (e.g., "wlan0").
374 pub interface: String,
375}
376
377impl ConnectionStatus {
378 /// Create a disconnected status for the given interface.
379 pub fn disconnected(interface: impl Into<String>) -> Self {
380 Self {
381 mode: WifiMode::Disconnected,
382 connection_name: None,
383 ip_address: None,
384 has_internet: false,
385 interface: interface.into(),
386 }
387 }
388}
389
390/// Result of an internet connectivity check.
391///
392/// Returned by [`WifiManager::check_connectivity()`][crate::WifiManager::check_connectivity].
393///
394/// # Example
395///
396/// ```no_run
397/// use rpi_host::WifiManager;
398///
399/// let wifi = WifiManager::new()?;
400/// let result = wifi.check_connectivity()?;
401///
402/// if result.is_connected {
403/// println!("Internet OK! Latency: {:?}ms to {}", result.latency_ms, result.target);
404/// } else {
405/// println!("No internet connection");
406/// }
407/// # Ok::<(), rpi_host::WifiError>(())
408/// ```
409#[derive(Debug, Clone)]
410pub struct ConnectivityResult {
411 /// Whether the connectivity check succeeded.
412 pub is_connected: bool,
413
414 /// Round-trip latency in milliseconds.
415 ///
416 /// `Some(ms)` if connected, `None` if not.
417 pub latency_ms: Option<u64>,
418
419 /// The target that was checked (IP address or hostname).
420 pub target: String,
421}