Skip to main content

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}