Initial commit

This commit is contained in:
Erwin Boskma 2021-07-31 17:16:00 +02:00
commit 40925f1f31
Signed by: erwin
GPG key ID: 5D2F7887C661DEBD
5 changed files with 1292 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

1131
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

18
Cargo.toml Normal file
View file

@ -0,0 +1,18 @@
[package]
name = "ha-now-playing"
description = "Retrieves the state of a media_player entity from Home Assistant"
version = "0.1.0"
authors = ["Erwin Boskma <erwin@datarift.nl>"]
edition = "2018"
[dependencies]
clap = "3.0.0-beta.2"
reqwest = { version = "0.11.4", features = ["blocking", "json"] }
serde = { version = "1.0.126", features = ["derive"] }
serde_json = "1.0.64"
[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"

12
dprint.json Normal file
View file

@ -0,0 +1,12 @@
{
"$schema": "https://dprint.dev/schemas/v0.json",
"incremental": true,
"markdown": {
},
"includes": ["**/*.{md,rs}"],
"excludes": [],
"plugins": [
"https://plugins.dprint.dev/markdown-0.9.2.wasm",
"https://plugins.dprint.dev/rustfmt-0.4.0.exe-plugin@c6bb223ef6e5e87580177f6461a0ab0554ac9ea6b54f78ea7ae8bf63b14f5bc2"
]
}

130
src/main.rs Normal file
View file

@ -0,0 +1,130 @@
use clap::{AppSettings, Clap};
use serde_json::json;
/// Bert
#[derive(Clap, Debug)]
#[clap(version, author, about)]
#[clap(setting = AppSettings::ColoredHelp)]
struct Opts {
/// Home Assistant host
#[clap(short, long)]
host: String,
/// Media player entity ID
#[clap(short, long)]
entity: String,
/// API token
#[clap(short, long)]
token: String,
/// Use HTTP instead of HTTPS
#[clap(short, long)]
insecure: bool,
#[clap(subcommand)]
cmd: Option<Command>,
}
#[derive(Clap, Debug, Clone, Copy)]
enum Command {
/// Toggle playback
PlayPause,
/// Raise volume
VolumeUp,
/// Lower volume
VolumeDown,
}
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 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);
} else {
println!(
"Sonos {}",
response["state"]
.as_str()
.map_or("state unknown", |state| state)
);
}
}
// println!("{:#?}", response);
Ok(())
}
fn call_service(command: Command, opts: &Opts) -> Result<(), Box<dyn std::error::Error>> {
let cmd = match command {
Command::PlayPause => "media_play_pause",
Command::VolumeUp => "volume_up",
Command::VolumeDown => "volume_down",
};
let url = format!(
"{}://{}/api/services/media_player/{}",
if opts.insecure { "http" } else { "https" },
opts.host,
cmd
);
let body = json!({"entity_id": opts.entity.clone()}).to_string();
let client = reqwest::blocking::Client::new();
let response = client
.post(url)
.body(body)
.bearer_auth(opts.token.clone())
.header("Accept", "application/json")
.header("Content-Type", "application/json")
.send()?
.json::<serde_json::Value>()?;
println!("{:#?}", response);
Ok(())
}