diff --git a/src/main.rs b/src/main.rs index 1406ef2..416ff03 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,10 +21,12 @@ const POLL_INTERVAL: u64 = 300; /// The base URL of My Autarco site const BASE_URL: &'static str = "https://my.autarco.com"; +/// Returns the login URL for the My Autarco site. fn login_url() -> Result { Url::parse(&format!("{}/auth/login", BASE_URL)) } +/// Returns an API endpoint URL for the given site ID and endpoint of the My Autarco site. fn api_url(site_id: &str, endpoint: &str) -> Result { Url::parse(&format!( "{}/api/site/{}/kpis/{}", @@ -43,6 +45,14 @@ struct Config { site_id: String, } +/// Loads the configuration +/// +/// The configuration file `autarco.toml` should be located in the project path. +/// +/// # Errors +/// +/// Returns an error if the file could not be found, opened or read and if the contents are +/// not valid TOML or does not contain all the necessary keys (see [`Config`]). async fn load_config() -> Result { let config_file_name = Path::new(env!("CARGO_MANIFEST_DIR")).join("autarco.toml"); let mut file = File::open(config_file_name).await?; @@ -70,7 +80,7 @@ lazy_static! { static ref STATUS: Mutex> = Mutex::new(None); } -/// The energy data returnes by the energy API endpoint +/// The energy data returned by the energy API endpoint #[derive(Debug, Deserialize)] struct ApiEnergy { /// Total energy produced today (kWh) @@ -88,6 +98,10 @@ struct ApiPower { pv_now: u32, } +/// Performs a login on the My Autarco site +/// +/// It mainly stores the acquired cookie in the client's cookie jar. The login credentials come +/// from the loaded configuration (see [`Config`]). async fn login(config: &Config, client: &reqwest::Client) -> Result<()> { let params = [ ("username", &config.username), @@ -98,6 +112,10 @@ async fn login(config: &Config, client: &reqwest::Client) -> Result<()> { Ok(()) } +/// Retrieves a status update from the API of the My Autarco site. +/// +/// It needs the cookie from the login to be able to perform the action. It uses both the `energy` +/// and `power` endpoint to construct the [`Status`] struct. async fn update(config: &Config, client: &reqwest::Client, last_updated: u64) -> Result { // Retrieve the data from the API endpoints let api_energy_url = api_url(&config.site_id, "energy")?; @@ -117,6 +135,10 @@ async fn update(config: &Config, client: &reqwest::Client, last_updated: u64) -> }) } +/// Main update loop that logs in and periodically acquires updates from the API. +/// +/// It updates the mutex-guarded current update [`Status`] struct which can be retrieved via +/// Rocket. async fn update_loop() -> Result<()> { let config = load_config().await?; let client = reqwest::ClientBuilder::new().cookie_store(true).build()?; @@ -154,12 +176,14 @@ async fn update_loop() -> Result<()> { } } +/// Returns the current (last known) status #[get("/", format = "application/json")] async fn status() -> Option> { let status_guard = STATUS.lock().expect("Status mutex was poisoined"); status_guard.map(|status| Json(status)) } +/// Starts the main update loop and sets up and launches Rocket #[rocket::main] async fn main() -> Result<()> { color_eyre::install()?;