diff --git a/PROJECT_MAP.md b/PROJECT_MAP.md index ccdc188..85f10d9 100644 --- a/PROJECT_MAP.md +++ b/PROJECT_MAP.md @@ -221,8 +221,7 @@ tvctl │ ├── stop │ └── refresh ├── remote -│ ├── key -│ └── sequence [key...] +│ └── key [key...] ├── state ├── dev │ ├── install diff --git a/README.md b/README.md index f37fc87..7734b57 100644 --- a/README.md +++ b/README.md @@ -171,8 +171,7 @@ tvctl app refresh Refresh app cache from TV Send input to the TV. ``` -tvctl remote key Send a single keypress -tvctl remote sequence [key...] Send a sequence of keypresses +tvctl remote key [key...] Send one or more keypresses ``` **Available keys:** diff --git a/src/cli/mod.rs b/src/cli/mod.rs index a03d7ec..cc52f41 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -176,14 +176,9 @@ pub enum AppCommand { pub enum RemoteCommand { /// Send a single normalized key. Key { - /// Key name such as `home`, `down`, or `literal:abc`. - key: String, - }, - /// Send multiple normalized keys in order. - Sequence { - /// Key names such as `home down select`. + /// One or more key names such as `home`, `down`, or `literal:abc`. keys: Vec, - /// Delay between keys in milliseconds. + /// Delay between keys in milliseconds when sending more than one key. #[arg(long, default_value_t = DEFAULT_REMOTE_SEQUENCE_DELAY_MS)] delay_ms: u64, }, @@ -442,40 +437,41 @@ async fn handle_app_command(cli: &Cli, command: AppCommand) -> Result<(), CliErr async fn handle_remote_command(cli: &Cli, command: RemoteCommand) -> Result<(), CliError> { match command { - RemoteCommand::Key { key } => { - let response = send_request( - load_socket_path().await?, - &DaemonRequest::SendKey { - device: cli.device.clone(), - key: parse_tv_key(&key)?, - }, - ) - .await?; - let result: ActionResult = parse_response_data(response)?; - render(cli, &result, || result.detail.clone()) - } - RemoteCommand::Sequence { keys, delay_ms } => { + RemoteCommand::Key { keys, delay_ms } => { if keys.is_empty() { return Err(CliError::new( - "At least one key is required for `tvctl remote sequence`.", - "Pass one or more keys such as `home down select`.", + "At least one key is required for `tvctl remote key`.", + "Pass one or more keys such as `home` or `home down select`.", )); } - let parsed = keys - .iter() - .map(|key| parse_tv_key(key)) - .collect::, _>>()?; - let response = send_request( - load_socket_path().await?, - &DaemonRequest::SendSequence { - device: cli.device.clone(), - keys: parsed, - delay_ms, - }, - ) - .await?; - let result: ActionResult = parse_response_data(response)?; - render(cli, &result, || result.detail.clone()) + if keys.len() == 1 { + let response = send_request( + load_socket_path().await?, + &DaemonRequest::SendKey { + device: cli.device.clone(), + key: parse_tv_key(&keys[0])?, + }, + ) + .await?; + let result: ActionResult = parse_response_data(response)?; + render(cli, &result, || result.detail.clone()) + } else { + let parsed = keys + .iter() + .map(|key| parse_tv_key(key)) + .collect::, _>>()?; + let response = send_request( + load_socket_path().await?, + &DaemonRequest::SendSequence { + device: cli.device.clone(), + keys: parsed, + delay_ms, + }, + ) + .await?; + let result: ActionResult = parse_response_data(response)?; + render(cli, &result, || result.detail.clone()) + } } } } @@ -674,9 +670,15 @@ async fn daemon_stop(cli: &Cli) -> Result<(), CliError> { async fn daemon_status(cli: &Cli) -> Result<(), CliError> { if let Some(status) = daemon_status_payload().await { return render(cli, &status, || { + let http = if status.http_enabled { + format!("{}:{}", status.http_host, status.http_port) + } else { + "disabled".to_string() + }; + let default_device = status.default_device.as_deref().unwrap_or("none"); format!( - "tvctld is running on {} with {} known device(s).", - status.socket, status.device_count + "tvctld is running.\nPID: {}\nSocket: {}\nHTTP: {}\nKnown Devices: {}\nDefault Device: {}", + status.pid, status.socket, http, status.device_count, default_device ) }); } diff --git a/src/daemon/ipc.rs b/src/daemon/ipc.rs index 704d119..6f78810 100644 --- a/src/daemon/ipc.rs +++ b/src/daemon/ipc.rs @@ -169,8 +169,15 @@ pub struct DaemonStatus { pub socket: String, /// Whether the HTTP API is enabled. pub http_enabled: bool, + /// The HTTP bind host. + pub http_host: String, + /// The HTTP bind port. + pub http_port: u16, /// The number of known devices. pub device_count: usize, + /// The current default device, if configured. + #[serde(skip_serializing_if = "Option::is_none")] + pub default_device: Option, } /// Discovery results returned by the daemon. diff --git a/src/daemon/mod.rs b/src/daemon/mod.rs index e61a0aa..de1f4c7 100644 --- a/src/daemon/mod.rs +++ b/src/daemon/mod.rs @@ -218,7 +218,13 @@ async fn handle_request( pid: std::process::id(), socket: guard.paths.socket_file.display().to_string(), http_enabled: guard.config.daemon.http_enabled, + http_host: guard.config.daemon.http_host.clone(), + http_port: guard.config.daemon.http_port, device_count: guard.registry.devices.len(), + default_device: guard + .registry + .default_device() + .map(|device| device.name.clone()), }), false, )