diff --git a/src/services.rs b/src/services.rs index 3a24ec9..5369f37 100644 --- a/src/services.rs +++ b/src/services.rs @@ -44,8 +44,8 @@ pub(crate) trait Service { fn poll_interval(&self) -> u64; /// Perfoms a login on the cloud service (if necessary). - async fn login(&self) -> Result<(), reqwest::Error>; + async fn login(&mut self) -> Result<(), reqwest::Error>; /// Retrieves a status update using the API of the cloud service. - async fn update(&self, timestamp: u64) -> Result; + async fn update(&mut self, timestamp: u64) -> Result; } diff --git a/src/services/hoymiles.rs b/src/services/hoymiles.rs index fb30846..227f8f7 100644 --- a/src/services/hoymiles.rs +++ b/src/services/hoymiles.rs @@ -37,10 +37,12 @@ pub(crate) fn service(config: Config) -> Result { let client = ClientBuilder::new() .cookie_provider(Arc::clone(&cookie_jar)) .build()?; + let total_kwh = 0f32; let service = Service { client, config, cookie_jar, + total_kwh, }; Ok(service) @@ -55,6 +57,8 @@ pub(crate) struct Service { config: Config, /// The cookie jar used for API requests. cookie_jar: Arc, + /// The last known total produced energy value. + total_kwh: f32, } /// Returns the login URL for the Hoymiles site. @@ -254,7 +258,7 @@ impl super::Service for Service { /// It mainly stores the acquired cookies in the client's cookie jar and adds the token cookie /// provided by the logins response. The login credentials come from the loaded configuration /// (see [`Config`]). - async fn login(&self) -> Result<(), reqwest::Error> { + async fn login(&mut self) -> Result<(), reqwest::Error> { let base_url = Url::parse(BASE_URL).expect("valid base URL"); let login_url = login_url().expect("valid login URL"); let login_request = ApiLoginRequest::new(&self.config.username, &self.config.password); @@ -284,7 +288,7 @@ impl super::Service for Service { /// It needs the cookies from the login to be able to perform the action. /// It uses a endpoint to construct the [`Status`] struct, but it needs to summarize the today /// value with the total value because Hoymiles only includes it after the day has finished. - async fn update(&self, last_updated: u64) -> Result { + async fn update(&mut self, last_updated: u64) -> Result { let api_url = api_url().expect("valid API power URL"); let api_data_request = ApiDataRequest::new(self.config.sid); let api_response = self @@ -302,7 +306,16 @@ impl super::Service for Service { Err(err) => return Err(err), }; let current_w = api_data.real_power; - let total_kwh = (api_data.total_eq + api_data.today_eq) / 1000.0; + let mut total_kwh = (api_data.total_eq + api_data.today_eq) / 1000.0; + + // Sometimes it can be that `today_eq` is reset when the day switches but it has not been + // added to `total_eq` yet. The `total_eq` should always be non-decreasing, so return the + // last known value until this is corrected (this most suredly happens during the night). + if total_kwh <= self.total_kwh { + total_kwh = self.total_kwh + } else { + self.total_kwh = total_kwh; + } Ok(Status { current_w, diff --git a/src/services/my_autarco.rs b/src/services/my_autarco.rs index 16ab7bf..35c0a98 100644 --- a/src/services/my_autarco.rs +++ b/src/services/my_autarco.rs @@ -86,7 +86,7 @@ impl super::Service for Service { /// /// 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(&self) -> Result<(), reqwest::Error> { + async fn login(&mut self) -> Result<(), reqwest::Error> { let params = [ ("username", &self.config.username), ("password", &self.config.password), @@ -101,7 +101,7 @@ impl super::Service for Service { /// /// 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(&self, last_updated: u64) -> Result { + async fn update(&mut self, last_updated: u64) -> Result { // Retrieve the data from the API endpoints. let api_energy_url = api_url(&self.config.site_id, "energy").expect("valid API energy URL"); let api_response = self.client.get(api_energy_url).send().await?; diff --git a/src/update.rs b/src/update.rs index 7ecc354..795dc32 100644 --- a/src/update.rs +++ b/src/update.rs @@ -15,6 +15,8 @@ use crate::{ /// It updates the mutex-guarded current update [`Status`](crate::Status) struct which can be /// retrieved via Rocket. pub(super) async fn update_loop(service: Services) -> color_eyre::Result<()> { + let mut service = service; + // Log in on the cloud service. println!("⚡ Logging in..."); service.login().await?;