Optionally read token from file

This commit is contained in:
Erwin Boskma 2021-11-25 23:40:07 +01:00
parent dd2894089a
commit 4faf119a2b
Signed by: erwin
GPG key ID: 270B20D17394F7E5
4 changed files with 113 additions and 65 deletions

2
Cargo.lock generated
View file

@ -222,7 +222,7 @@ dependencies = [
[[package]] [[package]]
name = "ha-now-playing" name = "ha-now-playing"
version = "0.1.0" version = "0.2.0"
dependencies = [ dependencies = [
"clap", "clap",
"reqwest", "reqwest",

View file

@ -1,7 +1,7 @@
[package] [package]
name = "ha-now-playing" name = "ha-now-playing"
description = "Retrieves the state of a media_player entity from Home Assistant" description = "Retrieves the state of a media_player entity from Home Assistant"
version = "0.1.0" version = "0.2.0"
authors = ["Erwin Boskma <erwin@datarift.nl>"] authors = ["Erwin Boskma <erwin@datarift.nl>"]
edition = "2018" edition = "2018"

View file

@ -7,11 +7,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1636968630, "lastModified": 1637576998,
"narHash": "sha256-/C8OVV8mo9M9MgQprHo4A5bM9waWMsw6PI4hb4DnGaA=", "narHash": "sha256-bGQ66hh4Dl78T9bd1pqdp6fprHMCkrkeKqED6sDUYqo=",
"owner": "nmattia", "owner": "nmattia",
"repo": "naersk", "repo": "naersk",
"rev": "55c4c0288ea3f115b5dd73af88052c4712994746", "rev": "b043f2447a4a761529254f4983cacd94b034a122",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -23,11 +23,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1637186689, "lastModified": 1637593665,
"narHash": "sha256-NU7BhgnwA/3ibmCeSzFK6xGi+Bari9mPfn+4cBmyEjw=", "narHash": "sha256-R7jKS7A+0tZS8qD5pBr1UFcMiTdsw5bfoxgXbYsoWhM=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "7fad01d9d5a3f82081c00fb57918d64145dc904c", "rev": "98747f27ecfee70c8c97b195cbb94df80a074dda",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -1,3 +1,5 @@
use std::{io::Read, path::PathBuf};
use clap::Parser; use clap::Parser;
use serde_json::json; use serde_json::json;
@ -15,7 +17,11 @@ struct Opts {
/// API token /// API token
#[clap(short, long)] #[clap(short, long)]
token: String, token: Option<String>,
/// File with the API token
#[clap(long)]
token_file: Option<PathBuf>,
/// Use HTTP instead of HTTPS /// Use HTTP instead of HTTPS
#[clap(short, long)] #[clap(short, long)]
@ -38,66 +44,108 @@ enum Command {
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let opts = Opts::parse(); let opts = Opts::parse();
if let Some(cmd) = opts.cmd { let Opts {
call_service(cmd, &opts)?; host,
} else { entity,
let url = format!( insecure,
"{}://{}/api/states/{}", cmd,
if opts.insecure { "http" } else { "https" }, token,
opts.host, token_file,
opts.entity } = opts;
);
let client = reqwest::blocking::Client::new(); let token = if let Some(token) = token {
let response = client Some(token)
.get(url) } else if let Some(token_file) = token_file {
.bearer_auth(opts.token) if let Ok(mut file) = std::fs::File::open(token_file) {
.header("Accept", "application/json") let mut buf = String::new();
.header("Content-Type", "application/json") file.read_to_string(&mut buf).ok();
.send()? Some(buf)
.json::<serde_json::Value>()?;
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 { } else {
println!( None
"Sonos {}", }
response["state"] } else {
.as_str() eprintln!("No API token given. Use either --token or --token-file");
.map_or("state unknown", |state| state) 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); // println!("{:#?}", response);
Ok(()) Ok(())
} }
fn get_now_playing(
host: String,
entity: String,
insecure: bool,
token: String,
) -> Result<(), Box<dyn std::error::Error>> {
let url = format!(
"{}://{}/api/states/{}",
if insecure { "http" } else { "https" },
host,
entity
);
fn call_service(command: Command, opts: &Opts) -> Result<(), Box<dyn std::error::Error>> { 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::<serde_json::Value>()?;
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<dyn std::error::Error>> {
let cmd = match command { let cmd = match command {
Command::PlayPause => "media_play_pause", Command::PlayPause => "media_play_pause",
Command::VolumeUp => "volume_up", Command::VolumeUp => "volume_up",
@ -106,18 +154,18 @@ fn call_service(command: Command, opts: &Opts) -> Result<(), Box<dyn std::error:
let url = format!( let url = format!(
"{}://{}/api/services/media_player/{}", "{}://{}/api/services/media_player/{}",
if opts.insecure { "http" } else { "https" }, if insecure { "http" } else { "https" },
opts.host, host,
cmd cmd
); );
let body = json!({"entity_id": opts.entity.clone()}).to_string(); let body = json!({ "entity_id": entity }).to_string();
let client = reqwest::blocking::Client::new(); let client = reqwest::blocking::Client::new();
let response = client let response = client
.post(url) .post(url)
.body(body) .body(body)
.bearer_auth(opts.token.clone()) .bearer_auth(token)
.header("Accept", "application/json") .header("Accept", "application/json")
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.send()? .send()?