From b72e0d956ea7815fdca494c96cd378863667d09c Mon Sep 17 00:00:00 2001 From: Maxime Augier Date: Fri, 2 Aug 2024 08:35:11 +0200 Subject: [PATCH] Fix time deserialization for iso8601 dates with missing timezone flag --- src/api.rs | 73 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/src/api.rs b/src/api.rs index d181643..2349eb3 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,7 +1,7 @@ use std::{io, time::{Duration, Instant}}; -use chrono::{DateTime, NaiveDateTime, Utc}; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use chrono::NaiveDateTime; +use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize}; use thiserror::Error; use tracing::{debug, info, instrument}; @@ -15,8 +15,13 @@ pub struct Context { const API_BASE: &'static str = "https://api.easee.com/api/"; const REFRESH_TOKEN_DELAY: Duration = Duration::from_secs(600); -fn parse_iso8601(s: &str) -> Result { - Ok(NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S%.f")?) + + +fn parse_iso8601<'d, D: Deserializer<'d>>(de: D) -> Result { + use serde::de::Error; + let s = <&str as Deserialize>::deserialize(de)?; + Ok(NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S%.f") + .map_err(D::Error::custom)?) } #[derive(Deserialize, Debug)] @@ -26,8 +31,11 @@ pub struct Charger { pub name: String, pub product_code: u32, pub color: Option, - pub created_on: DateTime, - pub updated_on: DateTime, + #[serde(deserialize_with="parse_iso8601")] + pub created_on: NaiveDateTime, + + #[serde(deserialize_with="parse_iso8601")] + pub updated_on: NaiveDateTime, pub level_of_access: u32, } @@ -42,40 +50,42 @@ pub struct ChargerState { pub energy_per_hour: f64, #[serde(rename="wiFiRSSI")] - pub wifi_rssi: u32, + pub wifi_rssi: Option, #[serde(rename="cellRSSI")] - pub cell_rssi: u32, + pub cell_rssi: Option, #[serde(rename="localRSSI")] - pub local_rssi: u32, + pub local_rssi: Option, pub output_phase: u32, pub dynamic_circuit_current_p1: u32, pub dynamic_circuit_current_p2: u32, pub dynamic_circuit_current_p3: u32, - pub latest_pulse: DateTime, + + //#[serde(deserialize_with="parse_iso8601")] + //pub latest_pulse: NaiveDateTime, pub charger_firmware: u32, pub voltage: f64, #[serde(rename="chargerRAT")] pub charger_rat: u32, pub lock_cable_permanently: bool, - pub in_current_t2: f64, - pub in_current_t3: f64, - pub in_current_t4: f64, - pub in_current_t5: f64, + pub in_current_t2: Option, + pub in_current_t3: Option, + pub in_current_t4: Option, + pub in_current_t5: Option, pub output_current: f64, pub is_online: bool, - pub in_voltage_t1_t2: f64, - pub in_voltage_t1_t3: f64, - pub in_voltage_t1_t4: f64, - pub in_voltage_t1_t5: f64, - pub in_voltage_t2_t3: f64, - pub in_voltage_t2_t4: f64, - pub in_voltage_t2_t5: f64, - pub in_voltage_t3_t4: f64, - pub in_voltage_t3_t5: f64, - pub in_voltage_t4_t5: f64, + pub in_voltage_t1_t2: Option, + pub in_voltage_t1_t3: Option, + pub in_voltage_t1_t4: Option, + pub in_voltage_t1_t5: Option, + pub in_voltage_t2_t3: Option, + pub in_voltage_t2_t4: Option, + pub in_voltage_t2_t5: Option, + pub in_voltage_t3_t4: Option, + pub in_voltage_t3_t5: Option, + pub in_voltage_t4_t5: Option, pub led_mode: u32, pub cable_rating: f64, pub dynamic_charger_current: f64, @@ -87,18 +97,18 @@ pub struct ChargerState { pub circuit_total_phase_conductor_current_l3: f64, pub reason_for_no_current: u32, - #[serde(rename="WifiAPEnabled")] + #[serde(rename="wiFiAPEnabled")] pub wifi_ap_enabled: bool, pub lifetime_energy: f64, pub offline_max_circuit_current_p1: u32, pub offline_max_circuit_current_p2: u32, pub offline_max_circuit_current_p3: u32, pub error_code: u32, - pub fault_error_code: u32, - pub eq_available_current_p1: f64, - pub eq_available_current_p2: f64, - pub eq_available_current_p3: f64, - pub derated_current: f64, + pub fatal_error_code: u32, + pub eq_available_current_p1: Option, + pub eq_available_current_p2: Option, + pub eq_available_current_p3: Option, + pub derated_current: Option, pub derating_active: bool, pub connected_to_cloud: bool, @@ -142,6 +152,9 @@ pub enum ApiError { #[error("unexpected data: {1} when processing {0}")] UnexpectedData(serde_json::Value, serde_json::Error), + #[error("could not deserialize time string")] + DeserializeFail, + #[error("format error: {0}")] FormatError(#[from] chrono::ParseError) }