use std::net::Ipv4Addr;
use clap::{ArgEnum, Parser, Subcommand};
use rpassword::read_password_from_tty;
use rbroadlink::{network::WirelessConnection, Device};
#[derive(Parser, Debug)]
#[clap(about, version, author)]
struct Args {
#[clap(subcommand)]
command: Commands,
}
#[derive(Subcommand, Debug)]
enum Commands {
Blast {
#[clap(long, short)]
local_ip: Option<Ipv4Addr>,
device_ip: Ipv4Addr,
code: String,
},
Connect {
#[clap(long, short)]
prompt: bool,
#[clap(arg_enum)]
security_mode: WirelessConnectionArg,
ssid: String,
password: Option<String>,
},
Learn {
#[clap(long, short)]
local_ip: Option<Ipv4Addr>,
device_ip: Ipv4Addr,
#[clap(arg_enum)]
code_type: LearnCodeType,
},
List {
#[clap(long, short)]
local_ip: Option<Ipv4Addr>,
},
Info {
#[clap(long, short)]
local_ip: Option<Ipv4Addr>,
device_ip: Ipv4Addr,
},
}
#[derive(ArgEnum, Clone, Debug)]
enum LearnCodeType {
IR,
RF,
}
#[derive(ArgEnum, Clone, Debug)]
enum WirelessConnectionArg {
None,
WEP,
WPA1,
WPA2,
WPA,
}
fn main() -> Result<(), String> {
let args = Args::parse();
return match args.command {
Commands::Blast {
local_ip,
device_ip,
code,
} => blast(local_ip, device_ip, code),
Commands::Connect {
security_mode,
ssid,
password,
prompt,
} => connect(security_mode, ssid, password, prompt),
Commands::Learn {
local_ip,
device_ip,
code_type,
} => learn(local_ip, device_ip, code_type),
Commands::List { local_ip } => list(local_ip),
Commands::Info {
local_ip,
device_ip,
} => info(local_ip, device_ip),
};
}
fn blast(local_ip: Option<Ipv4Addr>, device_ip: Ipv4Addr, code: String) -> Result<(), String> {
let device = Device::from_ip(device_ip, local_ip).expect("Could not connect to device!");
let hex_code = hex::decode(code).expect("Invalid code!");
let remote = match device {
Device::Remote { remote } => remote,
_ => return Err("Device specified is not a remote!".into()),
};
println!("Blasting IR/RF code: {:02X?}", hex_code);
return remote.send_code(&hex_code);
}
fn connect(
sec_mode: WirelessConnectionArg,
ssid: String,
password: Option<String>,
prompt: bool,
) -> Result<(), String> {
let password_prompt = Some("Wireless Password (will not show): ");
let unwrapped_pass = match sec_mode {
WirelessConnectionArg::None => "".into(),
_ => {
if prompt {
read_password_from_tty(password_prompt).expect("Could not read password!")
} else {
password.expect("This mode requires a password!")
}
}
};
let connection = match sec_mode {
WirelessConnectionArg::None => WirelessConnection::None(&ssid),
WirelessConnectionArg::WEP => WirelessConnection::WEP(&ssid, &unwrapped_pass),
WirelessConnectionArg::WPA1 => WirelessConnection::WPA1(&ssid, &unwrapped_pass),
WirelessConnectionArg::WPA2 => WirelessConnection::WPA2(&ssid, &unwrapped_pass),
WirelessConnectionArg::WPA => WirelessConnection::WPA(&ssid, &unwrapped_pass),
};
Device::connect_to_network(&connection).expect("Could not connect device to network!");
println!(
"Sending connection message with the following information: {:?}",
connection
);
return Ok(());
}
fn learn(
local_ip: Option<Ipv4Addr>,
device_ip: Ipv4Addr,
code_type: LearnCodeType,
) -> Result<(), String> {
println!("Attempting to learn a code of type {:?}...", code_type);
let device = Device::from_ip(device_ip, local_ip).expect("Could not connect to device!");
let remote = match device {
Device::Remote { remote } => remote,
_ => return Err("Device specified is not a remote!".into()),
};
let code = match code_type {
LearnCodeType::IR => remote.learn_ir(),
LearnCodeType::RF => remote.learn_rf(),
}
.expect("Could not learn code from device!");
let hex_string = hex::encode(&code);
println!("Got code => {}", hex_string);
return Ok(());
}
fn list(local_ip: Option<Ipv4Addr>) -> Result<(), String> {
println!("Searching for devices...");
let devs = Device::list(local_ip).expect("Could not list devices!");
if devs.len() == 0 {
println!("No devices found.")
} else {
println!("Devices:");
for dev in devs {
println!(" {}", dev);
}
}
return Ok(());
}
fn info(local_ip: Option<Ipv4Addr>, device_ip: Ipv4Addr) -> Result<(), String> {
println!("Getting information for device at {}", device_ip);
let device = Device::from_ip(device_ip, local_ip).expect("Could not connect to device!");
println!(" {}", device);
return Ok(());
}