HomeServer/src/main.rs

162 lines
4.1 KiB
Rust
Raw Normal View History

2023-09-19 08:52:12 +00:00
use std::{
2023-10-16 11:37:31 +00:00
fs,
2023-10-23 08:03:16 +00:00
pin::pin,
2023-09-19 08:52:12 +00:00
sync::{Arc, Mutex},
thread,
time::{Duration, SystemTime, UNIX_EPOCH},
};
2023-10-05 09:20:36 +00:00
use crate::{db::DataBase, midea_helper::MideaDiscovery, web_server::plug_data_range};
2023-09-19 08:52:12 +00:00
2023-10-23 08:03:16 +00:00
mod action;
2023-09-20 10:19:41 +00:00
mod data;
2023-09-19 08:52:12 +00:00
mod db;
mod devices;
2023-10-05 08:08:57 +00:00
mod midea_helper;
2023-10-23 08:03:16 +00:00
mod task_scheduler;
2023-09-19 08:52:12 +00:00
mod tasmota;
2023-10-19 14:26:09 +00:00
mod temperature;
mod tibber_handler;
2023-09-21 05:30:39 +00:00
mod web_server;
2023-09-19 08:52:12 +00:00
2023-10-09 18:12:34 +00:00
use actix_cors::Cors;
2023-09-21 08:08:23 +00:00
use actix_web::{web::Data, App, HttpServer};
2023-09-19 08:52:12 +00:00
use anyhow::Result;
use devices::Devices;
2023-09-21 05:30:39 +00:00
use futures::{future::try_join_all, try_join, Future};
2023-10-05 08:08:57 +00:00
use midea_helper::MideaDishwasher;
2023-10-23 08:03:16 +00:00
use task_scheduler::{Scheduler, Task};
2023-09-19 08:52:12 +00:00
use tasmota::Tasmota;
use tibber::TimeResolution::Daily;
use tibber_handler::TibberHandler;
use web_server::*;
2023-09-19 08:52:12 +00:00
fn since_epoch() -> Result<u64> {
Ok(SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs())
}
2023-10-23 08:03:16 +00:00
fn handle_error(
f: impl Future<Output = Result<()>> + Send + 'static,
) -> impl Future<Output = ()> + Unpin + Send + 'static {
Box::pin(async move {
if let Err(err) = f.await {
println!("{err}:?");
}
})
}
fn setup_tasmota_tasks(
scheduler: &Scheduler,
2023-09-21 05:30:39 +00:00
tasmota_plugs: Vec<Tasmota>,
db: Arc<Mutex<DataBase>>,
2023-10-23 08:03:16 +00:00
) {
for plug in tasmota_plugs.into_iter() {
let db_clone = db.clone();
let fut = async move {
if let Ok(usage) = plug.read_power_usage().await {
db_clone
.lock()
.unwrap()
.write(plug.name(), since_epoch()?, "watts", usage)?;
}
Ok(())
};
scheduler.add_task(Task::looping(Duration::from_secs(3), handle_error(fut)));
2023-09-21 05:30:39 +00:00
}
}
async fn run_web_server(
devices: Devices,
plugs: Vec<Tasmota>,
db: Arc<Mutex<DataBase>>,
2023-10-05 08:08:57 +00:00
dishwasher: Vec<Arc<MideaDishwasher>>,
2023-10-23 08:03:16 +00:00
scheduler: Scheduler,
2023-09-21 05:30:39 +00:00
) -> Result<()> {
const IP: &str = "0.0.0.0";
const PORT: u16 = 8062;
2023-10-05 09:20:36 +00:00
println!("Starting server on http://{IP}:{PORT}");
2023-09-21 05:30:39 +00:00
HttpServer::new(move || {
2023-10-09 18:12:34 +00:00
let cors = Cors::default()
.allow_any_origin()
.allow_any_method()
.allow_any_header();
2023-09-21 05:30:39 +00:00
App::new()
2023-10-09 18:12:34 +00:00
.wrap(cors)
2023-09-21 05:30:39 +00:00
.app_data(Data::new(devices.clone()))
.app_data(Data::new(db.clone()))
.app_data(Data::new(plugs.clone()))
2023-10-05 08:08:57 +00:00
.app_data(Data::new(dishwasher.clone()))
2023-10-23 08:03:16 +00:00
.app_data(Data::new(scheduler.clone()))
2023-09-21 05:30:39 +00:00
.service(device_query)
.service(plug_state)
.service(change_plug_state)
2023-09-21 08:46:23 +00:00
.service(change_device_name)
2023-09-21 20:20:42 +00:00
.service(plug_data)
.service(plug_data_range)
2023-10-19 14:26:09 +00:00
.service(push_temperature)
.service(push_humidity)
2023-09-21 05:30:39 +00:00
})
.bind((IP, PORT))
2023-09-21 05:30:39 +00:00
.map_err(|err| anyhow::Error::msg(format!("failed binding to address: {err:#?}")))?
.run()
.await?;
Ok(())
}
2023-09-19 08:52:12 +00:00
#[tokio::main]
async fn main() -> Result<()> {
let db_future = DataBase::new("home_server.db");
let devices_future = Devices::read("devices.conf");
2023-10-16 11:37:31 +00:00
let tibber_future = TibberHandler::new(fs::read_to_string("tibber_token.txt")?);
2023-09-19 08:52:12 +00:00
let (db, devices, tibber, midea) = try_join!(
db_future,
devices_future,
tibber_future,
MideaDiscovery::discover()
)?;
2023-10-16 11:37:31 +00:00
let prices_today = tibber.prices_today().await?;
let prices_tomorrow = tibber.prices_tomorrow().await?;
let consumption = tibber.consumption(Daily, 1).await?;
2023-09-19 08:52:12 +00:00
2023-09-21 08:08:23 +00:00
db.register_devices(&devices)?;
2023-09-19 08:52:12 +00:00
let shared_db = Arc::new(Mutex::new(db));
2023-09-21 05:30:39 +00:00
2023-09-19 08:52:12 +00:00
let tasmota_plugs: Vec<Tasmota> = devices
.plugs
.iter()
2023-09-22 05:43:56 +00:00
.map(|(plug, _)| Tasmota::new(plug))
2023-09-19 08:52:12 +00:00
.collect();
2023-10-05 09:20:36 +00:00
let dishwasher = MideaDishwasher::create(midea, shared_db.clone())
2023-10-05 08:08:57 +00:00
.await?
.into_iter()
.map(|d| Arc::new(d))
.collect();
2023-09-19 08:52:12 +00:00
2023-10-23 08:03:16 +00:00
let scheduler = Scheduler::default();
setup_tasmota_tasks(&scheduler, tasmota_plugs.clone(), shared_db.clone());
let scheduler_clone = scheduler.clone();
2023-09-21 08:08:23 +00:00
try_join!(
2023-10-23 08:03:16 +00:00
scheduler.run(),
run_web_server(
devices,
tasmota_plugs,
shared_db,
dishwasher,
scheduler_clone
)
2023-09-21 05:30:39 +00:00
)?;
2023-09-19 08:52:12 +00:00
2023-09-21 05:30:39 +00:00
Ok(())
2023-09-19 08:52:12 +00:00
}