use std::{ fs, sync::{Arc, Mutex}, thread, time::{Duration, SystemTime, UNIX_EPOCH}, }; use crate::{db::DataBase, midea_helper::MideaDiscovery, web_server::plug_data_range}; mod data; mod db; mod devices; mod midea_helper; mod tasmota; mod tibber_handler; mod web_server; use actix_cors::Cors; use actix_web::{web::Data, App, HttpServer}; use anyhow::Result; use devices::Devices; use futures::{future::try_join_all, try_join, Future}; use midea_helper::MideaDishwasher; use tasmota::Tasmota; use tibber::TimeResolution::Daily; use tibber_handler::TibberHandler; use web_server::*; fn since_epoch() -> Result { Ok(SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs()) } fn read_power_usage( tasmota_plugs: Vec, db: Arc>, ) -> impl Future> { async move { loop { try_join_all(tasmota_plugs.iter().map(|plug| async { if let Ok(usage) = plug.read_power_usage().await { db.lock() .unwrap() .write(plug.name(), since_epoch()?, usage)?; } Ok::<(), anyhow::Error>(()) })) .await?; thread::sleep(Duration::from_secs(3)); } } } async fn run_web_server( devices: Devices, plugs: Vec, db: Arc>, dishwasher: Vec>, ) -> Result<()> { const IP: &str = "0.0.0.0"; const PORT: u16 = 8062; println!("Starting server on http://{IP}:{PORT}"); HttpServer::new(move || { let cors = Cors::default() .allow_any_origin() .allow_any_method() .allow_any_header(); App::new() .wrap(cors) .app_data(Data::new(devices.clone())) .app_data(Data::new(db.clone())) .app_data(Data::new(plugs.clone())) .app_data(Data::new(dishwasher.clone())) .service(device_query) .service(plug_state) .service(change_plug_state) .service(change_device_name) .service(plug_data) .service(plug_data_range) }) .bind((IP, PORT)) .map_err(|err| anyhow::Error::msg(format!("failed binding to address: {err:#?}")))? .run() .await?; Ok(()) } #[tokio::main] async fn main() -> Result<()> { let db_future = DataBase::new("home_server.db"); let devices_future = Devices::read("devices.conf"); let tibber_future = TibberHandler::new(fs::read_to_string("tibber_token.txt")?); let (db, devices, tibber, midea) = try_join!( db_future, devices_future, tibber_future, MideaDiscovery::discover() )?; let prices_today = tibber.prices_today().await?; let prices_tomorrow = tibber.prices_tomorrow().await?; let consumption = tibber.consumption(Daily, 1).await?; db.register_devices(&devices)?; let shared_db = Arc::new(Mutex::new(db)); let tasmota_plugs: Vec = devices .plugs .iter() .map(|(plug, _)| Tasmota::new(plug)) .collect(); let dishwasher = MideaDishwasher::create(midea, shared_db.clone()) .await? .into_iter() .map(|d| Arc::new(d)) .collect(); try_join!( read_power_usage(tasmota_plugs.clone(), shared_db.clone()), run_web_server(devices, tasmota_plugs, shared_db, dishwasher) )?; Ok(()) }