use anyhow::{Context, Result, bail}; use lesavka_common::lesavka::{ CapturePowerCommand, Empty, SetCapturePowerRequest, relay_client::RelayClient, }; use tonic::{Request, transport::Channel}; #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum CommandKind { Status, Auto, On, Off, } impl CommandKind { fn parse(value: &str) -> Option { match value { "status" | "get" => Some(Self::Status), "auto" => Some(Self::Auto), "on" | "force-on" => Some(Self::On), "off" | "force-off" => Some(Self::Off), _ => None, } } } struct Config { server: String, command: CommandKind, } fn usage() -> &'static str { "Usage: lesavka-relayctl [--server http://HOST:50051] " } fn parse_args() -> Result { let mut args = std::env::args().skip(1); let mut server = "http://127.0.0.1:50051".to_string(); let mut command = None; while let Some(arg) = args.next() { match arg.as_str() { "--server" => { server = args .next() .context("missing value after --server")? .trim() .to_string(); } "--help" | "-h" => { println!("{}", usage()); std::process::exit(0); } _ if command.is_none() => { command = CommandKind::parse(arg.as_str()); if command.is_none() { bail!("unknown command `{arg}`\n{}", usage()); } } _ => bail!("unexpected argument `{arg}`\n{}", usage()), } } Ok(Config { server, command: command.unwrap_or(CommandKind::Status), }) } async fn connect(server_addr: &str) -> Result> { let channel = Channel::from_shared(server_addr.to_string()) .context("invalid relay server address")? .tcp_nodelay(true) .connect() .await .with_context(|| format!("connecting to relay at {server_addr}"))?; Ok(RelayClient::new(channel)) } fn print_state(state: lesavka_common::lesavka::CapturePowerState) { println!("available={}", state.available); println!("enabled={}", state.enabled); println!("mode={}", state.mode); println!("detected_devices={}", state.detected_devices); println!("active_leases={}", state.active_leases); println!("unit={}", state.unit); println!("detail={}", state.detail); } #[tokio::main(flavor = "current_thread")] async fn main() -> Result<()> { let config = parse_args()?; let mut client = connect(config.server.as_str()).await?; let reply = match config.command { CommandKind::Status => client .get_capture_power(Request::new(Empty {})) .await .context("querying capture power state")? .into_inner(), CommandKind::Auto => client .set_capture_power(Request::new(SetCapturePowerRequest { enabled: false, command: CapturePowerCommand::Auto as i32, })) .await .context("setting capture power to auto")? .into_inner(), CommandKind::On => client .set_capture_power(Request::new(SetCapturePowerRequest { enabled: true, command: CapturePowerCommand::ForceOn as i32, })) .await .context("forcing capture power on")? .into_inner(), CommandKind::Off => client .set_capture_power(Request::new(SetCapturePowerRequest { enabled: false, command: CapturePowerCommand::ForceOff as i32, })) .await .context("forcing capture power off")? .into_inner(), }; print_state(reply); Ok(()) }