Basic WiFi Scanner

This example demonstrates building a simple but complete WiFi network scanner using nmrs.

Features

  • Lists all available WiFi networks
  • Shows signal strength with visual indicators
  • Displays security types
  • Filters by signal strength
  • Auto-refreshes every few seconds

Complete Code

use nmrs::{NetworkManager, models::Network};
use std::time::Duration;
use tokio::time::sleep;

#[tokio::main]
async fn main() -> nmrs::Result<()> {
    // Initialize NetworkManager
    let nm = NetworkManager::new().await?;
    
    println!("WiFi Network Scanner");
    println!("===================\n");
    
    // Scan loop
    loop {
        // Clear screen (Unix/Linux)
        print!("\x1B[2J\x1B[1;1H");
        
        // Get networks
        let mut networks = nm.list_networks().await?;
        
        // Sort by signal strength (strongest first)
        networks.sort_by(|a, b| {
            b.strength.unwrap_or(0).cmp(&a.strength.unwrap_or(0))
        });
        
        // Display header
        println!("WiFi Network Scanner - {} networks found\n", networks.len());
        println!("{:<30} {:>10} {:>8} {:<20}", 
                 "SSID", "Signal", "Band", "Security");
        println!("{}", "-".repeat(70));
        
        // Display each network
        for net in networks {
            print_network(&net);
        }
        
        println!("\n{}", "-".repeat(70));
        println!("Press Ctrl+C to exit");
        
        // Wait before next scan
        sleep(Duration::from_secs(5)).await;
    }
}

fn print_network(net: &Network) {
    let signal = net.strength.unwrap_or(0);
    let signal_bar = signal_strength_bar(signal);
    
    let band = match net.frequency {
        Some(freq) if freq > 5000 => "5GHz",
        Some(_) => "2.4GHz",
        None => "Unknown",
    };
    
    let security = match &net.security {
        nmrs::WifiSecurity::Open => "Open",
        nmrs::WifiSecurity::WpaPsk { .. } => "WPA-PSK",
        nmrs::WifiSecurity::WpaEap { .. } => "WPA-EAP",
    };
    
    println!("{:<30} {:>3}% {} {:>8} {:<20}",
             truncate_ssid(&net.ssid, 30),
             signal,
             signal_bar,
             band,
             security
    );
}

fn signal_strength_bar(strength: u8) -> String {
    let bars = match strength {
        80..=100 => "▂▄▆█",
        60..=79  => "▂▄▆▁",
        40..=59  => "▂▄▁▁",
        20..=39  => "▂▁▁▁",
        _        => "▁▁▁▁",
    };
    
    let color = match strength {
        70..=100 => "\x1b[32m", // Green
        40..=69  => "\x1b[33m", // Yellow
        _        => "\x1b[31m", // Red
    };
    
    format!("{}{}\x1b[0m", color, bars)
}

fn truncate_ssid(ssid: &str, max_len: usize) -> String {
    if ssid.len() <= max_len {
        ssid.to_string()
    } else {
        format!("{}...", &ssid[..max_len - 3])
    }
}

Running the Example

Add to your Cargo.toml:

[dependencies]
nmrs = "2.0.0"
tokio = { version = "1", features = ["full"] }

Run with:

cargo run

Sample Output

WiFi Network Scanner - 8 networks found

SSID                           Signal     Band Security            
----------------------------------------------------------------------
MyHomeNetwork                   92% ▂▄▆█  5GHz WPA-PSK            
CoffeeShop_Guest                78% ▂▄▆▁  2.4GHz Open              
Neighbor-5G                     65% ▂▄▆▁  5GHz WPA-PSK            
Corporate_WiFi                  58% ▂▄▁▁  5GHz WPA-EAP            
Guest_Network                   45% ▂▄▁▁  2.4GHz Open              
FarAwayNetwork                  22% ▂▁▁▁  2.4GHz WPA-PSK            

----------------------------------------------------------------------
Press Ctrl+C to exit

Enhancements

Filter by Signal Strength

#![allow(unused)]
fn main() {
// Only show networks with signal > 30%
let networks: Vec<_> = networks
    .into_iter()
    .filter(|n| n.strength.unwrap_or(0) > 30)
    .collect();
}

Group by Frequency Band

#![allow(unused)]
fn main() {
let mut networks_2_4ghz = Vec::new();
let mut networks_5ghz = Vec::new();

for net in networks {
    match net.frequency {
        Some(freq) if freq > 5000 => networks_5ghz.push(net),
        Some(_) => networks_2_4ghz.push(net),
        None => {}
    }
}

println!("\n5GHz Networks:");
for net in networks_5ghz {
    print_network(&net);
}

println!("\n2.4GHz Networks:");
for net in networks_2_4ghz {
    print_network(&net);
}
}

Add Connection Capability

#![allow(unused)]
fn main() {
use std::io::{self, Write};

// After displaying networks
print!("\nEnter number to connect (or 0 to skip): ");
io::stdout().flush()?;

let mut input = String::new();
io::stdin().read_line(&mut input)?;

if let Ok(choice) = input.trim().parse::<usize>() {
    if choice > 0 && choice <= networks.len() {
        let selected = &networks[choice - 1];
        
        // Get password if needed
        match &selected.security {
            nmrs::WifiSecurity::Open => {
                nm.connect(&selected.ssid, nmrs::WifiSecurity::Open).await?;
                println!("Connected to {}", selected.ssid);
            }
            _ => {
                print!("Enter password: ");
                io::stdout().flush()?;
                let mut password = String::new();
                io::stdin().read_line(&mut password)?;
                
                nm.connect(&selected.ssid, nmrs::WifiSecurity::WpaPsk {
                    psk: password.trim().to_string()
                }).await?;
                println!("Connected to {}", selected.ssid);
            }
        }
    }
}
}

Export to JSON

#![allow(unused)]
fn main() {
use serde::{Serialize, Deserialize};

#[derive(Serialize)]
struct NetworkExport {
    ssid: String,
    signal: u8,
    frequency: Option<u32>,
    security: String,
}

// Convert networks to exportable format
let exports: Vec<NetworkExport> = networks.iter().map(|n| {
    NetworkExport {
        ssid: n.ssid.clone(),
        signal: n.strength.unwrap_or(0),
        frequency: n.frequency,
        security: format!("{:?}", n.security),
    }
}).collect();

// Write to file
let json = serde_json::to_string_pretty(&exports)?;
std::fs::write("networks.json", json)?;
println!("Exported to networks.json");
}

See Also