190 lines
5.4 KiB
Rust
190 lines
5.4 KiB
Rust
use reqwest::{Client, Error};
|
|
use serde::Deserialize;
|
|
use serde_json::json;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct HaClient {
|
|
base_url: String,
|
|
token: String,
|
|
client: Client,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct HaState {
|
|
pub entity_id: String,
|
|
pub attributes: serde_json::Value,
|
|
}
|
|
|
|
impl HaClient {
|
|
pub fn new(base_url: &str, token: &str) -> Self {
|
|
Self {
|
|
base_url: base_url.trim_end_matches('/').to_string(),
|
|
token: token.to_string(),
|
|
client: Client::new(),
|
|
}
|
|
}
|
|
|
|
pub async fn get_lights(&self) -> Result<Vec<HaState>, Error> {
|
|
let url = format!("{}/api/states", self.base_url);
|
|
let states: Vec<HaState> = self
|
|
.client
|
|
.get(&url)
|
|
.bearer_auth(&self.token)
|
|
.send()
|
|
.await?
|
|
.json()
|
|
.await?;
|
|
|
|
Ok(states
|
|
.into_iter()
|
|
.filter(|s| s.entity_id.starts_with("light."))
|
|
.collect())
|
|
}
|
|
|
|
pub async fn get_color_lights(&self) -> Result<Vec<HaState>, Error> {
|
|
let color_modes = ["hs", "xy", "rgb", "rgbw", "rgbww"];
|
|
let lights = self.get_lights().await?;
|
|
|
|
Ok(lights
|
|
.into_iter()
|
|
.filter(|l| {
|
|
l.attributes
|
|
.get("supported_color_modes")
|
|
.and_then(|m| m.as_array())
|
|
.map(|modes| {
|
|
modes
|
|
.iter()
|
|
.any(|m| m.as_str().map_or(false, |s| color_modes.contains(&s)))
|
|
})
|
|
.unwrap_or(false)
|
|
})
|
|
.collect())
|
|
}
|
|
|
|
pub async fn create_scene_snapshot(
|
|
&self,
|
|
scene_id: &str,
|
|
entities: &[String],
|
|
) -> Result<(), Error> {
|
|
self.client
|
|
.post(format!("{}/api/services/scene/create", self.base_url))
|
|
.bearer_auth(&self.token)
|
|
.json(&json!({
|
|
"scene_id": scene_id,
|
|
"snapshot_entities": entities,
|
|
}))
|
|
.send()
|
|
.await?
|
|
.error_for_status()?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn turn_on_scene(&self, scene_entity_id: &str) -> Result<(), Error> {
|
|
self.client
|
|
.post(format!("{}/api/services/scene/turn_on", self.base_url))
|
|
.bearer_auth(&self.token)
|
|
.json(&json!({ "entity_id": scene_entity_id }))
|
|
.send()
|
|
.await?
|
|
.error_for_status()?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn set_light_color(
|
|
&self,
|
|
entity_id: &str,
|
|
r: u8,
|
|
g: u8,
|
|
b: u8,
|
|
states: &[HaState],
|
|
) -> Result<(), Error> {
|
|
let url = format!("{}/api/services/light/turn_on", self.base_url);
|
|
|
|
let mut payload = json!({
|
|
"entity_id": entity_id,
|
|
});
|
|
|
|
if let Some(state) = states.iter().find(|s| s.entity_id == entity_id) {
|
|
if let Some(brightness) = state.attributes.get("brightness") {
|
|
payload["brightness"] = brightness.clone();
|
|
}
|
|
|
|
let color_mode = state
|
|
.attributes
|
|
.get("color_mode")
|
|
.and_then(|m| m.as_str())
|
|
.unwrap_or("");
|
|
|
|
match color_mode {
|
|
"rgbw" => {
|
|
let w = state
|
|
.attributes
|
|
.get("rgbw_color")
|
|
.and_then(|v| v.as_array())
|
|
.and_then(|a| a.get(3))
|
|
.cloned()
|
|
.unwrap_or(json!(0));
|
|
payload["rgbw_color"] = json!([r, g, b, w]);
|
|
}
|
|
"rgbww" => {
|
|
let cw = state
|
|
.attributes
|
|
.get("rgbww_color")
|
|
.and_then(|v| v.as_array())
|
|
.and_then(|a| a.get(3))
|
|
.cloned()
|
|
.unwrap_or(json!(0));
|
|
let ww = state
|
|
.attributes
|
|
.get("rgbww_color")
|
|
.and_then(|v| v.as_array())
|
|
.and_then(|a| a.get(4))
|
|
.cloned()
|
|
.unwrap_or(json!(0));
|
|
payload["rgbww_color"] = json!([r, g, b, cw, ww]);
|
|
}
|
|
_ => {
|
|
payload["rgb_color"] = json!([r, g, b]);
|
|
}
|
|
}
|
|
} else {
|
|
payload["rgb_color"] = json!([r, g, b]);
|
|
}
|
|
|
|
self.client
|
|
.post(&url)
|
|
.bearer_auth(&self.token)
|
|
.json(&payload)
|
|
.send()
|
|
.await?
|
|
.error_for_status()?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn set_lights_color_parallel(
|
|
&self,
|
|
entity_ids: &[String],
|
|
r: u8,
|
|
g: u8,
|
|
b: u8,
|
|
) -> Result<(), Error> {
|
|
let states = self.get_lights().await.unwrap_or_default();
|
|
|
|
let futures: Vec<_> = entity_ids
|
|
.iter()
|
|
.map(|entity_id| self.set_light_color(entity_id, r, g, b, &states))
|
|
.collect();
|
|
|
|
let results = futures::future::join_all(futures).await;
|
|
|
|
for result in results {
|
|
if let Err(e) = result {
|
|
eprintln!("Failed to update light: {}", e);
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|