2022-08-22 21:05:35 +02:00
|
|
|
#![doc = include_str!("../README.md")]
|
2022-08-22 21:23:32 +02:00
|
|
|
#![warn(
|
|
|
|
clippy::all,
|
|
|
|
missing_debug_implementations,
|
|
|
|
rust_2018_idioms,
|
|
|
|
rustdoc::broken_intra_doc_links
|
|
|
|
)]
|
|
|
|
#![deny(missing_docs)]
|
2022-08-22 21:05:35 +02:00
|
|
|
|
2020-10-09 17:07:18 +02:00
|
|
|
use std::sync::Mutex;
|
2021-06-13 20:51:18 +02:00
|
|
|
|
2022-02-11 14:17:27 +01:00
|
|
|
use once_cell::sync::Lazy;
|
2022-08-22 21:22:06 +02:00
|
|
|
use rocket::fairing::AdHoc;
|
2021-06-13 20:51:18 +02:00
|
|
|
use rocket::serde::json::Json;
|
|
|
|
use rocket::{get, routes};
|
|
|
|
use serde::{Deserialize, Serialize};
|
2021-08-27 20:43:34 +02:00
|
|
|
|
|
|
|
use self::update::update_loop;
|
|
|
|
|
|
|
|
mod update;
|
2020-10-09 19:47:43 +02:00
|
|
|
|
2021-08-27 21:11:45 +02:00
|
|
|
/// The base URL of My Autarco site.
|
|
|
|
const BASE_URL: &str = "https://my.autarco.com";
|
|
|
|
|
|
|
|
/// The interval between data polls.
|
2020-10-09 19:47:43 +02:00
|
|
|
///
|
2021-08-16 20:15:43 +02:00
|
|
|
/// This depends on with which interval Autaurco processes new information from the invertor.
|
2020-10-09 17:07:18 +02:00
|
|
|
const POLL_INTERVAL: u64 = 300;
|
2020-10-09 13:30:37 +02:00
|
|
|
|
2023-01-03 17:08:25 +01:00
|
|
|
/// The extra configuration necessary to access the My Autarco site.
|
2021-08-16 20:15:43 +02:00
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
struct Config {
|
|
|
|
/// The username of the account to login with
|
|
|
|
username: String,
|
|
|
|
/// The password of the account to login with
|
|
|
|
password: String,
|
|
|
|
/// The Autarco site ID to track
|
|
|
|
site_id: String,
|
2020-10-09 16:50:11 +02:00
|
|
|
}
|
2020-10-09 16:31:21 +02:00
|
|
|
|
2022-02-11 14:17:27 +01:00
|
|
|
/// The global, concurrently accessible current status.
|
|
|
|
static STATUS: Lazy<Mutex<Option<Status>>> = Lazy::new(|| Mutex::new(None));
|
|
|
|
|
2021-08-16 20:15:43 +02:00
|
|
|
/// The current photovoltaic invertor status.
|
|
|
|
#[derive(Clone, Copy, Debug, Serialize)]
|
|
|
|
struct Status {
|
|
|
|
/// Current power production (W)
|
|
|
|
current_w: u32,
|
|
|
|
/// Total energy produced since installation (kWh)
|
|
|
|
total_kwh: u32,
|
|
|
|
/// Timestamp of last update
|
|
|
|
last_updated: u64,
|
|
|
|
}
|
2020-10-09 19:47:43 +02:00
|
|
|
|
2021-08-27 21:11:45 +02:00
|
|
|
/// Returns the current (last known) status.
|
2020-10-09 17:30:41 +02:00
|
|
|
#[get("/", format = "application/json")]
|
2020-10-09 19:47:43 +02:00
|
|
|
async fn status() -> Option<Json<Status>> {
|
2020-10-09 23:06:56 +02:00
|
|
|
let status_guard = STATUS.lock().expect("Status mutex was poisoined");
|
2021-08-27 21:11:45 +02:00
|
|
|
status_guard.map(Json)
|
2020-10-09 17:30:41 +02:00
|
|
|
}
|
|
|
|
|
2023-01-03 17:08:25 +01:00
|
|
|
/// Creates a Rocket and attaches the config parsing and update loop as fairings.
|
2022-08-22 21:22:06 +02:00
|
|
|
#[rocket::launch]
|
|
|
|
fn rocket() -> _ {
|
|
|
|
rocket::build()
|
|
|
|
.mount("/", routes![status])
|
2023-01-03 17:08:25 +01:00
|
|
|
.attach(AdHoc::config::<Config>())
|
|
|
|
.attach(AdHoc::on_liftoff("Updater", |rocket| {
|
2022-08-22 21:22:06 +02:00
|
|
|
Box::pin(async move {
|
|
|
|
// We don't care about the join handle nor error results?
|
2023-01-03 17:08:25 +01:00
|
|
|
let config = rocket.figment().extract().expect("Invalid configuration");
|
|
|
|
let _ = rocket::tokio::spawn(update_loop(config));
|
2022-08-22 21:22:06 +02:00
|
|
|
})
|
|
|
|
}))
|
2020-10-09 23:06:56 +02:00
|
|
|
}
|