use std::{ net::IpAddr, sync::{Arc, Mutex}, time::Duration, }; use actix_web::web::Data; use anyhow::{bail, Result}; use dns_lookup::{lookup_addr, lookup_host}; use reqwest::Client; use crate::{ db::DataBase, since_epoch, task_scheduler::{Scheduler, Task}, }; pub struct Thermostat { device: String, } impl Thermostat { pub fn new(device: impl ToString) -> Self { Self { device: device.to_string(), } } pub async fn set_temperature(&self, temperature: f32) -> Result<()> { let ips = lookup_host(&self.device)?; if ips.is_empty() { bail!("could not resolve device name {}", self.device); } let resp = Client::new() .post(format!("http://{}/ext_t?temp={}", ips[0], temperature)) .send() .await?; if !resp.status().is_success() { bail!("response error"); } Ok(()) } } #[derive(Debug)] pub enum ThermometerChange { Temperature(f32), Humidity(f32), } pub struct Thermometer; impl Thermometer { pub fn push_change( change: ThermometerChange, ip: IpAddr, db: Data>>, scheduler: Data, ) -> Result<()> { let db_lock = db.lock().unwrap(); let device_id = lookup_addr(&ip)?.trim_end_matches(".fritz.box").to_string(); if db_lock.device_exists(&device_id)? { match change { ThermometerChange::Temperature(temp) => { db_lock.write(&device_id, since_epoch()?, "temperature", temp)?; for action_set in db_lock.action_sets(&device_id)? { if let Some(push_device) = action_set.push_device() { if action_set.parameter("temperature") && push_device == device_id { if let Some(receive_device) = action_set.receive_device() { scheduler.add_task(Task::one_shot( Duration::from_secs(0), Box::pin(async move { let _ = Thermostat::new(receive_device) .set_temperature(temp); }), )); } } } } } ThermometerChange::Humidity(humid) => { db_lock.write(&device_id, since_epoch()?, "humidity", humid)?; } } } Ok(()) } }