use esphome_client::{
EspHomeClient,
types::{
EspHomeMessage, ListEntitiesRequest, ListEntitiesSwitchResponse, SwitchCommandRequest,
},
};
use std::collections::HashMap;
use std::io::Write;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let args: Vec<String> = std::env::args().collect();
if args.len() < 2 {
eprintln!("Usage: {} <host:port> [api_key]", args[0]);
eprintln!("Example: {} 192.168.1.100:6053", args[0]);
std::process::exit(1);
}
let address = &args[1];
let api_key = args.get(2);
println!("Connecting to ESPHome device at {}", address);
let mut builder = EspHomeClient::builder().address(address);
if let Some(key) = api_key {
builder = builder.key(key);
}
let mut client = builder.connect().await?;
println!("Connected successfully!");
let mut switches: HashMap<u32, ListEntitiesSwitchResponse> = HashMap::new();
println!("\nDiscovering switches...");
client.try_write(ListEntitiesRequest {}).await?;
let mut discovery_complete = false;
while !discovery_complete {
let response = client.try_read().await?;
match response {
EspHomeMessage::ListEntitiesSwitchResponse(switch) => {
println!("[{}] {} - {}", switch.key, switch.name, switch.object_id);
switches.insert(switch.key, switch);
}
EspHomeMessage::ListEntitiesDoneResponse(_) => {
discovery_complete = true;
}
_ => {}
}
}
if switches.is_empty() {
println!("\nNo switches found on this device.");
return Ok(());
}
println!("\n=== Switch Control ===");
loop {
println!("\nAvailable switches:");
for (key, switch) in &switches {
println!(" {} - {}", key, switch.name);
}
println!("\nEnter command (format: <key> <on|off>) or 'quit' to exit:");
print!("> ");
std::io::stdout().flush()?;
let mut input = String::new();
std::io::stdin().read_line(&mut input)?;
let input = input.trim();
if input == "quit" {
break;
}
let parts: Vec<&str> = input.split_whitespace().collect();
if parts.len() != 2 {
println!("Invalid format. Use: <key> <on|off>");
continue;
}
let key: u32 = match parts[0].parse() {
Ok(k) => k,
Err(_) => {
println!("Invalid key. Must be a number.");
continue;
}
};
let state = match parts[1].to_lowercase().as_str() {
"on" | "true" | "1" => true,
"off" | "false" | "0" => false,
_ => {
println!("Invalid state. Use 'on' or 'off'.");
continue;
}
};
if !switches.contains_key(&key) {
println!("Switch with key {} not found.", key);
continue;
}
#[allow(unused_variables)]
let switch = &switches[&key];
client
.try_write(SwitchCommandRequest {
key,
state,
#[cfg(not(any(feature = "api-1-10", feature = "api-1-9", feature = "api-1-8")))]
device_id: switch.device_id,
})
.await?;
println!(
"Sent command to turn {} switch '{}'",
if state { "ON" } else { "OFF" },
switches[&key].name
);
}
client.close().await?;
println!("\nConnection closed.");
Ok(())
}