2023-09-19 08:52:12 +00:00
|
|
|
use std::{
|
2023-10-16 11:37:31 +00:00
|
|
|
fs,
|
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-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-09-19 08:52:12 +00:00
|
|
|
mod tasmota;
|
2023-10-16 11:46:25 +00:00
|
|
|
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-09-19 08:52:12 +00:00
|
|
|
use tasmota::Tasmota;
|
2023-10-16 11:55:19 +00:00
|
|
|
use tibber::TimeResolution::Daily;
|
2023-10-16 11:46:25 +00:00
|
|
|
use tibber_handler::TibberHandler;
|
2023-09-29 16:05:49 +00:00
|
|
|
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-09-21 05:30:39 +00:00
|
|
|
fn read_power_usage(
|
|
|
|
tasmota_plugs: Vec<Tasmota>,
|
|
|
|
db: Arc<Mutex<DataBase>>,
|
|
|
|
) -> impl Future<Output = Result<()>> {
|
|
|
|
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<Tasmota>,
|
|
|
|
db: Arc<Mutex<DataBase>>,
|
2023-10-05 08:08:57 +00:00
|
|
|
dishwasher: Vec<Arc<MideaDishwasher>>,
|
2023-09-21 05:30:39 +00:00
|
|
|
) -> Result<()> {
|
2023-09-29 16:05:49 +00:00
|
|
|
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-29 16:05:49 +00:00
|
|
|
|
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-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)
|
2023-09-29 16:05:49 +00:00
|
|
|
.service(plug_data_range)
|
2023-09-21 05:30:39 +00:00
|
|
|
})
|
2023-09-29 16:05:49 +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
|
|
|
|
2023-10-16 11:46:25 +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-09-21 08:08:23 +00:00
|
|
|
try_join!(
|
2023-09-21 05:30:39 +00:00
|
|
|
read_power_usage(tasmota_plugs.clone(), shared_db.clone()),
|
2023-10-05 08:08:57 +00:00
|
|
|
run_web_server(devices, tasmota_plugs, shared_db, dishwasher)
|
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
|
|
|
}
|