Add config

This commit is contained in:
Daan Boerlage 2022-04-09 22:51:17 +02:00
parent a48b5c3f9c
commit 01a9671f2f
Signed by: daan
GPG key ID: FCE070E1E4956606
10 changed files with 193 additions and 20 deletions

View file

@ -3,11 +3,12 @@ use serde::{Deserialize, Serialize};
use crate::api::models::lights::{Light, On};
use crate::api::models::Response;
use crate::config::Config;
use crate::{api, ToggleState};
pub async fn all() -> Result<Vec<Light>> {
let request_url = format!("https://{}/clip/v2/resource/light", api::HUB_IP);
let client = api::get_client()?;
pub async fn all(config: Config) -> Result<Vec<Light>> {
let request_url = format!("https://{}/clip/v2/resource/light", config.hostname);
let client = api::get_client(&config)?;
match client.get(request_url).send().await {
Ok(response) => Ok(response.json::<Response<Light>>().await?.data),
@ -29,16 +30,16 @@ pub struct LightStatePut {
on: On,
}
pub async fn toggle(id: String, state: ToggleState) -> Result<()> {
let request_url = format!("https://{}/clip/v2/resource/light/{}", api::HUB_IP, id);
let client = api::get_client()?;
pub async fn toggle(config: Config, id: String, state: ToggleState) -> Result<()> {
let request_url = format!("https://{}/clip/v2/resource/light/{}", config.hostname, id);
let client = api::get_client(&config)?;
let target_state: LightStatePut = LightStatePut {
on: On {
on: match state {
ToggleState::On => true,
ToggleState::Off => false,
ToggleState::Swap => match all().await?.into_iter().find(|x| x.id == id) {
ToggleState::Swap => match all(config).await?.into_iter().find(|x| x.id == id) {
Some(light) => !light.on.on,
None => true,
},

View file

@ -1,23 +1,22 @@
pub mod lights;
pub mod models;
use crate::config::Config;
use eyre::Result;
use reqwest::{header, Client};
const HUB_IP: &'static str = "10.60.0.163";
const HUE_TOKEN: &'static str = "sNLQz7sOVfji2VA42jeLwhx2rnwBCkYu-OVc2ddw";
pub fn get_client() -> Result<Client> {
pub fn get_client(config: &Config) -> Result<Client> {
let mut headers = header::HeaderMap::new();
headers.insert(
"hue-application-key",
header::HeaderValue::from_static(HUE_TOKEN),
header::HeaderValue::from_str(config.token.as_str())?,
);
let client = reqwest::Client::builder()
.use_native_tls()
.danger_accept_invalid_certs(true)
.default_headers(headers)
.timeout(Duration::from_secs(3))
.build()?;
Ok(client)

View file

@ -1,12 +1,14 @@
use eyre::Result;
use crate::api;
use crate::config::Config;
pub async fn exec() -> Result<()> {
let lights = api::lights::all().await?;
pub async fn exec(config: Config) -> Result<()> {
let lights = api::lights::all(config).await?;
for light in lights {
println!("{} - {}", light.id, light.metadata.name)
let icon = if light.on.on { "💡" } else { "🔌" };
println!("{} - {} - {}", icon, light.id, light.metadata.name)
}
Ok(())

View file

@ -1,16 +1,17 @@
use eyre::Result;
use std::str::FromStr;
use crate::config::Config;
use crate::{api, ToggleState};
pub async fn exec(id: &String, state: &Option<String>) -> Result<()> {
pub async fn exec(config: Config, id: &String, state: &Option<String>) -> Result<()> {
let target_state = if let Some(target) = &state {
ToggleState::from_str(target.as_str()).unwrap()
} else {
ToggleState::Swap
};
api::lights::toggle(id.clone(), target_state).await?;
api::lights::toggle(config, id.clone(), target_state).await?;
Ok(())
}

33
src/config/config_file.rs Normal file
View file

@ -0,0 +1,33 @@
use eyre::Result;
use serde::Deserialize;
use std::fs;
use std::path::Path;
#[derive(Deserialize)]
pub struct ConfigFile {
pub token: Option<String>,
pub host: Option<Host>,
}
#[derive(Deserialize)]
pub struct Host {
pub hostname: String,
}
pub async fn read() -> Result<Option<ConfigFile>> {
let home_dir = match dirs::home_dir() {
Some(h) => h,
None => return Ok(None),
};
let config_file_path = home_dir.join(Path::new(".hue-control.toml"));
let content = match fs::read_to_string(config_file_path) {
Ok(c) => c,
Err(_) => return Ok(None),
};
let config: ConfigFile = toml::from_str(&content)?;
Ok(Some(config))
}

View file

@ -0,0 +1 @@

59
src/config/mod.rs Normal file
View file

@ -0,0 +1,59 @@
mod config_file;
mod environment;
use eyre::{eyre, Result};
#[derive(Debug)]
pub struct Config {
pub hostname: String,
pub token: String,
}
pub async fn resolve() -> Result<Config> {
let mut config = Config {
hostname: "".to_string(),
token: "".to_string(),
};
let config_file = config_file::read().await?;
if let Some(c) = config_file {
if let Some(token) = c.token {
config.token = token;
}
if let Some(host) = c.host {
config.hostname = host.hostname
}
}
if let Ok(token) = std::env::var("HUE_TOKEN") {
config.token = token;
}
if let Ok(host) = std::env::var("HUE_HOSTNAME") {
config.hostname = host;
}
println!("{:?}", config);
if let Err(e) = validate_config(&config) {
return Err(e);
}
Ok(config)
}
fn validate_config(config: &Config) -> Result<()> {
if config.token.is_empty() {
return Err(eyre!("Token config variable is empty"));
}
if config.token.len() != 40 || !config.token.is_ascii() {
return Err(eyre!("Token format is invalid"));
}
if config.hostname.is_empty() {
return Err(eyre!("hostname is empty"));
}
Ok(())
}

View file

@ -1,6 +1,7 @@
mod api;
mod args;
mod commands;
mod config;
use crate::args::ToggleState;
use clap::Parser;
@ -9,13 +10,14 @@ use eyre::Result;
#[tokio::main]
async fn main() -> Result<()> {
let args = args::Args::parse();
let config = config::resolve().await?;
match &args.command {
args::Commands::Toggle { id, state } => {
commands::toggle::exec(id, state).await?;
commands::toggle::exec(config, id, state).await?;
}
args::Commands::List => {
commands::list::exec().await?;
commands::list::exec(config).await?;
}
args::Commands::Inspect => {
panic!("Not implemented");