Optionally read token from file
This commit is contained in:
parent
dd2894089a
commit
4faf119a2b
4 changed files with 113 additions and 65 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -222,7 +222,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ha-now-playing"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"reqwest",
|
||||
|
|
|
@ -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 <erwin@datarift.nl>"]
|
||||
edition = "2018"
|
||||
|
||||
|
|
12
flake.lock
12
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": {
|
||||
|
|
162
src/main.rs
162
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<String>,
|
||||
|
||||
/// File with the API token
|
||||
#[clap(long)]
|
||||
token_file: Option<PathBuf>,
|
||||
|
||||
/// Use HTTP instead of HTTPS
|
||||
#[clap(short, long)]
|
||||
|
@ -38,66 +44,108 @@ enum Command {
|
|||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
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::<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);
|
||||
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<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 {
|
||||
Command::PlayPause => "media_play_pause",
|
||||
Command::VolumeUp => "volume_up",
|
||||
|
@ -106,18 +154,18 @@ fn call_service(command: Command, opts: &Opts) -> Result<(), Box<dyn std::error:
|
|||
|
||||
let url = format!(
|
||||
"{}://{}/api/services/media_player/{}",
|
||||
if opts.insecure { "http" } else { "https" },
|
||||
opts.host,
|
||||
if insecure { "http" } else { "https" },
|
||||
host,
|
||||
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 response = client
|
||||
.post(url)
|
||||
.body(body)
|
||||
.bearer_auth(opts.token.clone())
|
||||
.bearer_auth(token)
|
||||
.header("Accept", "application/json")
|
||||
.header("Content-Type", "application/json")
|
||||
.send()?
|
||||
|
|
Loading…
Reference in a new issue