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]]
|
[[package]]
|
||||||
name = "ha-now-playing"
|
name = "ha-now-playing"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
|
|
@ -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"
|
||||||
|
|
||||||
|
|
12
flake.lock
12
flake.lock
|
@ -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": {
|
||||||
|
|
162
src/main.rs
162
src/main.rs
|
@ -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()?
|
||||||
|
|
Loading…
Reference in a new issue