Fix time deserialization for iso8601 dates with missing timezone flag

This commit is contained in:
Maxime Augier 2024-08-02 08:35:11 +02:00
parent 1fd48a9e30
commit b72e0d956e

View File

@ -1,7 +1,7 @@
use std::{io, time::{Duration, Instant}}; use std::{io, time::{Duration, Instant}};
use chrono::{DateTime, NaiveDateTime, Utc}; use chrono::NaiveDateTime;
use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize};
use thiserror::Error; use thiserror::Error;
use tracing::{debug, info, instrument}; use tracing::{debug, info, instrument};
@ -15,8 +15,13 @@ pub struct Context {
const API_BASE: &'static str = "https://api.easee.com/api/"; const API_BASE: &'static str = "https://api.easee.com/api/";
const REFRESH_TOKEN_DELAY: Duration = Duration::from_secs(600); const REFRESH_TOKEN_DELAY: Duration = Duration::from_secs(600);
fn parse_iso8601(s: &str) -> Result<NaiveDateTime, ApiError> {
Ok(NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S%.f")?)
fn parse_iso8601<'d, D: Deserializer<'d>>(de: D) -> Result<NaiveDateTime, D::Error> {
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)] #[derive(Deserialize, Debug)]
@ -26,8 +31,11 @@ pub struct Charger {
pub name: String, pub name: String,
pub product_code: u32, pub product_code: u32,
pub color: Option<i32>, pub color: Option<i32>,
pub created_on: DateTime<Utc>, #[serde(deserialize_with="parse_iso8601")]
pub updated_on: DateTime<Utc>, pub created_on: NaiveDateTime,
#[serde(deserialize_with="parse_iso8601")]
pub updated_on: NaiveDateTime,
pub level_of_access: u32, pub level_of_access: u32,
} }
@ -42,40 +50,42 @@ pub struct ChargerState {
pub energy_per_hour: f64, pub energy_per_hour: f64,
#[serde(rename="wiFiRSSI")] #[serde(rename="wiFiRSSI")]
pub wifi_rssi: u32, pub wifi_rssi: Option<i32>,
#[serde(rename="cellRSSI")] #[serde(rename="cellRSSI")]
pub cell_rssi: u32, pub cell_rssi: Option<i32>,
#[serde(rename="localRSSI")] #[serde(rename="localRSSI")]
pub local_rssi: u32, pub local_rssi: Option<i32>,
pub output_phase: u32, pub output_phase: u32,
pub dynamic_circuit_current_p1: u32, pub dynamic_circuit_current_p1: u32,
pub dynamic_circuit_current_p2: u32, pub dynamic_circuit_current_p2: u32,
pub dynamic_circuit_current_p3: u32, pub dynamic_circuit_current_p3: u32,
pub latest_pulse: DateTime<Utc>,
//#[serde(deserialize_with="parse_iso8601")]
//pub latest_pulse: NaiveDateTime,
pub charger_firmware: u32, pub charger_firmware: u32,
pub voltage: f64, pub voltage: f64,
#[serde(rename="chargerRAT")] #[serde(rename="chargerRAT")]
pub charger_rat: u32, pub charger_rat: u32,
pub lock_cable_permanently: bool, pub lock_cable_permanently: bool,
pub in_current_t2: f64, pub in_current_t2: Option<f64>,
pub in_current_t3: f64, pub in_current_t3: Option<f64>,
pub in_current_t4: f64, pub in_current_t4: Option<f64>,
pub in_current_t5: f64, pub in_current_t5: Option<f64>,
pub output_current: f64, pub output_current: f64,
pub is_online: bool, pub is_online: bool,
pub in_voltage_t1_t2: f64, pub in_voltage_t1_t2: Option<f64>,
pub in_voltage_t1_t3: f64, pub in_voltage_t1_t3: Option<f64>,
pub in_voltage_t1_t4: f64, pub in_voltage_t1_t4: Option<f64>,
pub in_voltage_t1_t5: f64, pub in_voltage_t1_t5: Option<f64>,
pub in_voltage_t2_t3: f64, pub in_voltage_t2_t3: Option<f64>,
pub in_voltage_t2_t4: f64, pub in_voltage_t2_t4: Option<f64>,
pub in_voltage_t2_t5: f64, pub in_voltage_t2_t5: Option<f64>,
pub in_voltage_t3_t4: f64, pub in_voltage_t3_t4: Option<f64>,
pub in_voltage_t3_t5: f64, pub in_voltage_t3_t5: Option<f64>,
pub in_voltage_t4_t5: f64, pub in_voltage_t4_t5: Option<f64>,
pub led_mode: u32, pub led_mode: u32,
pub cable_rating: f64, pub cable_rating: f64,
pub dynamic_charger_current: f64, pub dynamic_charger_current: f64,
@ -87,18 +97,18 @@ pub struct ChargerState {
pub circuit_total_phase_conductor_current_l3: f64, pub circuit_total_phase_conductor_current_l3: f64,
pub reason_for_no_current: u32, pub reason_for_no_current: u32,
#[serde(rename="WifiAPEnabled")] #[serde(rename="wiFiAPEnabled")]
pub wifi_ap_enabled: bool, pub wifi_ap_enabled: bool,
pub lifetime_energy: f64, pub lifetime_energy: f64,
pub offline_max_circuit_current_p1: u32, pub offline_max_circuit_current_p1: u32,
pub offline_max_circuit_current_p2: u32, pub offline_max_circuit_current_p2: u32,
pub offline_max_circuit_current_p3: u32, pub offline_max_circuit_current_p3: u32,
pub error_code: u32, pub error_code: u32,
pub fault_error_code: u32, pub fatal_error_code: u32,
pub eq_available_current_p1: f64, pub eq_available_current_p1: Option<f64>,
pub eq_available_current_p2: f64, pub eq_available_current_p2: Option<f64>,
pub eq_available_current_p3: f64, pub eq_available_current_p3: Option<f64>,
pub derated_current: f64, pub derated_current: Option<f64>,
pub derating_active: bool, pub derating_active: bool,
pub connected_to_cloud: bool, pub connected_to_cloud: bool,
@ -142,6 +152,9 @@ pub enum ApiError {
#[error("unexpected data: {1} when processing {0}")] #[error("unexpected data: {1} when processing {0}")]
UnexpectedData(serde_json::Value, serde_json::Error), UnexpectedData(serde_json::Value, serde_json::Error),
#[error("could not deserialize time string")]
DeserializeFail,
#[error("format error: {0}")] #[error("format error: {0}")]
FormatError(#[from] chrono::ParseError) FormatError(#[from] chrono::ParseError)
} }