use crate::lightstructs::*;
use dotenv;
use reqwest::Client;
use serde_json::value::Value;
use std::collections::BTreeMap;
use std::env;
use std::error::Error;
use std::fmt;
use std::fs::File;
use std::io::prelude::*;
use std::thread::sleep;
use std::time::Duration;
type Lights = BTreeMap<u8, Light>;
#[derive(Debug)]
pub struct Bridge {
ip: String,
key: String,
client: Client,
base_url: String,
pub light_ids: Vec<u8>,
pub n_lights: u8,
pub lights: Option<Lights>,
}
impl Bridge {
fn discover(filename: &str) -> Result<(String, String), Box<dyn Error>> {
dotenv::from_filename(filename)?;
let ip = env::var("HUE_IP")?;
let key = env::var("HUE_KEY")?;
Ok((ip, key))
}
pub fn register(configpath: &str) -> Result<(String, String), Box<dyn Error>> {
let _client = Client::new();
let mut ip = String::new();
let mut name = String::new();
println!("NOTE! Registration will create the `~/.huemanity` containing IP and KEY info");
println!("Enter the IP of your HUE bridge:");
std::io::stdin().read_line(&mut ip)?;
ip = ip.trim().to_string();
println!("Enter the desired app name:");
std::io::stdin().read_line(&mut name)?;
name = name.trim().to_string();
let body = serde_json::json!({ "devicetype": format!("{}", name) });
let ping_it = || -> Value {
_client
.post(&format!("http://{}/api", ip))
.json(&body)
.send()
.unwrap()
.json()
.unwrap()
};
let mut response = ping_it();
loop {
if response[0]["error"]["type"] == 101 {
println!("Please press the hub button!");
sleep(Duration::from_secs(5));
response = ping_it();
} else {
break;
}
}
let key = &response[0]["success"]["username"];
let mut file = File::create(configpath)?;
file.write_all(format!("HUE_IP=\"{}\"\nHUE_KEY={}", ip, key).as_ref())?;
println!(".env File successfully saved!");
Ok((ip, key.to_string().replace("\"", "")))
}
pub fn link() -> Self {
let mut filename = dirs::home_dir().unwrap();
filename.push(".huemanity");
let path = filename.to_str().unwrap();
let client = Client::new();
let (ip, key) = match Self::discover(path) {
Ok(tupl) => tupl,
_ => {
println!("Unable to find required `HUE_KEY` and `HUE_IP` in environment!");
let result = match Self::register(path) {
Ok(tupl) => {
println!("Registration successful");
tupl
}
Err(e) => panic!("Could not register due to: {}", e),
};
result
}
};
let base_url = format!("http://{}/api/{}/", ip, key);
let mut bridge = Bridge {
ip,
key,
client,
base_url,
light_ids: Vec::new(),
n_lights: 0,
lights: None,
};
match bridge.collect_lights() {
Ok(_) => println!("Collected lights sucessfully!"),
Err(e) => println!("Could not collect lights: {}", e),
}
println!("Connected to:\n{}", bridge);
println!("Found {} lights", bridge.n_lights);
bridge
}
fn send(
&self,
endpoint: &str,
req_type: RequestType,
params: Option<&SendableState>,
) -> Result<reqwest::Response, Box<dyn std::error::Error>> {
let target = format!("{}{}", self.base_url, endpoint);
let response = match req_type {
RequestType::Post => self.client.post(&target).json(¶ms).send()?,
RequestType::Get => self.client.get(&target).send()?,
RequestType::Put => self.client.put(&target).json(¶ms).send()?,
};
Ok(response)
}
pub fn state(
&self,
light: u8,
state: &SendableState,
) -> Result<(), Box<dyn std::error::Error>> {
self.send(
&format!("lights/{}/state", light),
RequestType::Put,
Some(state),
)?;
Ok(())
}
pub fn state_all(&self, state: &SendableState) -> Result<(), Box<dyn std::error::Error>> {
for light in self.light_ids.iter() {
self.state(*light, state)?;
}
Ok(())
}
pub fn collect_lights(&mut self) -> Result<(), Box<dyn std::error::Error>> {
let lights: Lights = self.send("lights", RequestType::Get, None)?.json()?;
self.light_ids = lights.keys().cloned().map(|integer| integer).collect();
self.lights = Some(lights);
self.n_lights = self.light_ids.len() as u8;
Ok(())
}
pub fn light_info(&self) {
println!("Lights available on your bridge:");
let lights = self.lights.as_ref().unwrap();
for (id, light) in lights.iter() {
println!("{}:\n{:?}", id, light);
}
}
}
impl fmt::Display for Bridge {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "bridge: {}\nlights: {:?}", self.ip, self.light_ids)
}
}