1use crate::error::{ApiError, Result};
2use crate::lights::Light;
3use reqwest::blocking::get;
4use std::collections::HashMap;
5
6#[derive(Deserialize, Serialize, Debug)]
7pub struct BridgeDevice {
9 #[serde(rename = "UDN")]
10 pub udn: String,
11 #[serde(rename = "deviceType")]
12 pub device_type: String,
13 pub manufacturer: String,
14 #[serde(rename = "modelName")]
15 pub model_name: String,
16 #[serde(rename = "modelDescription")]
17 pub model_description: String,
18 #[serde(rename = "serialNumber")]
19 pub serial_number: String,
20 #[serde(rename = "friendlyName")]
21 pub friendly_name: String,
22}
23
24#[derive(Deserialize, Serialize, Debug)]
30pub struct Bridge {
31 #[serde(rename = "URLBase")]
32 pub url_base: String,
34 pub device: BridgeDevice,
36}
37
38#[derive(Deserialize, Debug)]
39pub enum ApiResponseSection {
43 #[serde(rename = "error")]
44 Err(ApiError),
45 #[serde(rename = "success")]
46 Success(HashMap<String, serde_json::Value>),
47}
48
49impl Bridge {
50 pub fn from_description_url(url: String) -> Result<Bridge> {
52 let response = get(&url)?.text()?;
53 let bridge: Bridge = serde_xml_rs::from_str(&response)?;
54 Ok(bridge)
55 }
56
57 pub fn user_readable_identifier(&self) -> &str {
59 &self.device.friendly_name
60 }
61
62 pub fn register_user(&self) -> Result<String> {
68 let client = reqwest::blocking::Client::new();
69 let mut url = self.url_base.clone();
70 url.push_str("api");
71 let mut params = HashMap::new();
72 params.insert("devicetype", "Hust Hue API client");
73 let response = client.post(&url).json(¶ms).send()?;
74 let response: Vec<ApiResponseSection> = serde_json::from_reader(response)?;
75 let mut errors = vec![];
77 let mut success = None;
78 for section in response {
79 match section {
80 ApiResponseSection::Err(e) => errors.push(e),
81 ApiResponseSection::Success(hashmap) => success = Some(hashmap),
82 }
83 }
84 if let Some(hashmap) = success {
85 if let Some(username) = hashmap.get("username") {
86 return Ok(username.to_string());
87 }
88 }
89 Err(errors)?
90 }
91
92 fn light_change_result(&self, response: Vec<ApiResponseSection>) -> Result<()> {
97 let mut errors = vec![];
98 let success = response
99 .into_iter()
100 .any(|section| match section {
102 ApiResponseSection::Success(_) => true,
103 ApiResponseSection::Err(e) => {
104 errors.push(e);
105 false
106 }
107 });
108 if success {
109 return Ok(())
110 }
111 Err(errors)?
112 }
113
114 pub fn modify_light<T: serde::ser::Serialize>(&self, user: &str, light: &str, key: &str, value: T) -> Result<()> {
123 let client = reqwest::blocking::Client::new();
124 let url = format!("{}api/{}/lights/{}/state", self.url_base, user, light);
125 let mut params = HashMap::new();
126 params.insert(key, value);
127 let response = client
128 .put(&url)
129 .json(¶ms)
130 .send()?;
131 let response: Vec<ApiResponseSection> = serde_json::from_reader(response)?;
132 self.light_change_result(response)
133 }
134
135 pub fn get_all_lights(&self, user: &str) -> Result<HashMap<String, Light>> {
140 let url = format!("{}api/{}/lights", self.url_base, user);
141 let response = get(&url)?;
142 Ok(serde_json::from_reader(response)?)
143 }
144
145 pub fn switch_light(&self, user: &str, light: &str, on: bool) -> Result<()> {
154 self.modify_light(user, light, "on", on)
155 }
156}