Initial commit
This commit is contained in:
commit
40925f1f31
5 changed files with 1292 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
1131
Cargo.lock
generated
Normal file
1131
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
18
Cargo.toml
Normal file
18
Cargo.toml
Normal 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
12
dprint.json
Normal 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
130
src/main.rs
Normal 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(())
|
||||
}
|
Loading…
Reference in a new issue