From 4faf119a2b6f06ec10ceae5ed7763aa0ae5ae822 Mon Sep 17 00:00:00 2001 From: Erwin Boskma Date: Thu, 25 Nov 2021 23:40:07 +0100 Subject: [PATCH] Optionally read token from file --- Cargo.lock | 2 +- Cargo.toml | 2 +- flake.lock | 12 ++-- src/main.rs | 162 ++++++++++++++++++++++++++++++++++------------------ 4 files changed, 113 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 16a323d..09d1f74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -222,7 +222,7 @@ dependencies = [ [[package]] name = "ha-now-playing" -version = "0.1.0" +version = "0.2.0" dependencies = [ "clap", "reqwest", diff --git a/Cargo.toml b/Cargo.toml index 891b110..79ef749 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ha-now-playing" description = "Retrieves the state of a media_player entity from Home Assistant" -version = "0.1.0" +version = "0.2.0" authors = ["Erwin Boskma "] edition = "2018" diff --git a/flake.lock b/flake.lock index 3df7290..45b3642 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ ] }, "locked": { - "lastModified": 1636968630, - "narHash": "sha256-/C8OVV8mo9M9MgQprHo4A5bM9waWMsw6PI4hb4DnGaA=", + "lastModified": 1637576998, + "narHash": "sha256-bGQ66hh4Dl78T9bd1pqdp6fprHMCkrkeKqED6sDUYqo=", "owner": "nmattia", "repo": "naersk", - "rev": "55c4c0288ea3f115b5dd73af88052c4712994746", + "rev": "b043f2447a4a761529254f4983cacd94b034a122", "type": "github" }, "original": { @@ -23,11 +23,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1637186689, - "narHash": "sha256-NU7BhgnwA/3ibmCeSzFK6xGi+Bari9mPfn+4cBmyEjw=", + "lastModified": 1637593665, + "narHash": "sha256-R7jKS7A+0tZS8qD5pBr1UFcMiTdsw5bfoxgXbYsoWhM=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "7fad01d9d5a3f82081c00fb57918d64145dc904c", + "rev": "98747f27ecfee70c8c97b195cbb94df80a074dda", "type": "github" }, "original": { diff --git a/src/main.rs b/src/main.rs index 2f83fe5..fbe64cf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +use std::{io::Read, path::PathBuf}; + use clap::Parser; use serde_json::json; @@ -15,7 +17,11 @@ struct Opts { /// API token #[clap(short, long)] - token: String, + token: Option, + + /// File with the API token + #[clap(long)] + token_file: Option, /// Use HTTP instead of HTTPS #[clap(short, long)] @@ -38,66 +44,108 @@ enum Command { fn main() -> Result<(), Box> { let opts = Opts::parse(); - if let Some(cmd) = opts.cmd { - call_service(cmd, &opts)?; - } else { - let url = format!( - "{}://{}/api/states/{}", - if opts.insecure { "http" } else { "https" }, - opts.host, - opts.entity - ); + let Opts { + host, + entity, + insecure, + cmd, + token, + token_file, + } = opts; - let client = reqwest::blocking::Client::new(); - let response = client - .get(url) - .bearer_auth(opts.token) - .header("Accept", "application/json") - .header("Content-Type", "application/json") - .send()? - .json::()?; - - if response["state"] == "playing" { - let attributes = &response["attributes"]; - let maybe_channel = attributes["media_channel"].as_str(); - let now_playing = if let Some(channel) = maybe_channel { - format!( - "[{}] {} - {}", - channel, - attributes["media_artist"] - .as_str() - .map_or("No artist", |artist| { artist }), - attributes["media_title"] - .as_str() - .map_or("No title", |title| { title }), - ) - } else { - format!( - "{} - {}", - attributes["media_artist"] - .as_str() - .map_or("No artist", |artist| artist), - attributes["media_title"] - .as_str() - .map_or("No title", |title| title), - ) - }; - - println!("{}", now_playing); + let token = if let Some(token) = token { + Some(token) + } else if let Some(token_file) = token_file { + if let Ok(mut file) = std::fs::File::open(token_file) { + let mut buf = String::new(); + file.read_to_string(&mut buf).ok(); + Some(buf) } else { - println!( - "Sonos {}", - response["state"] - .as_str() - .map_or("state unknown", |state| state) - ); + None + } + } else { + eprintln!("No API token given. Use either --token or --token-file"); + None + }; + + if let Some(token) = token { + if let Some(cmd) = cmd { + call_service(cmd, host, entity, insecure, token)?; + } else { + get_now_playing(host, entity, insecure, token)?; } } // println!("{:#?}", response); Ok(()) } +fn get_now_playing( + host: String, + entity: String, + insecure: bool, + token: String, +) -> Result<(), Box> { + let url = format!( + "{}://{}/api/states/{}", + if insecure { "http" } else { "https" }, + host, + entity + ); -fn call_service(command: Command, opts: &Opts) -> Result<(), Box> { + let client = reqwest::blocking::Client::new(); + let response = client + .get(url) + .bearer_auth(token) + .header("Accept", "application/json") + .header("Content-Type", "application/json") + .send()? + .json::()?; + + if response["state"] == "playing" { + let attributes = &response["attributes"]; + let maybe_channel = attributes["media_channel"].as_str(); + let now_playing = if let Some(channel) = maybe_channel { + format!( + "[{}] {} - {}", + channel, + attributes["media_artist"] + .as_str() + .map_or("No artist", |artist| { artist }), + attributes["media_title"] + .as_str() + .map_or("No title", |title| { title }), + ) + } else { + format!( + "{} - {}", + attributes["media_artist"] + .as_str() + .map_or("No artist", |artist| artist), + attributes["media_title"] + .as_str() + .map_or("No title", |title| title), + ) + }; + + println!("{}", now_playing); + } else { + println!( + "Sonos {}", + response["state"] + .as_str() + .map_or("state unknown", |state| state) + ); + } + + Ok(()) +} + +fn call_service( + command: Command, + host: String, + entity: String, + insecure: bool, + token: String, +) -> Result<(), Box> { let cmd = match command { Command::PlayPause => "media_play_pause", Command::VolumeUp => "volume_up", @@ -106,18 +154,18 @@ fn call_service(command: Command, opts: &Opts) -> Result<(), Box