use chrono::DateTime; use serde::{Deserialize, Serialize}; use teslatte::vehicles::ChargingState; use crate::{config::access_config, errors::TeslaStateParseError}; #[derive(Default, Clone, Copy, Serialize, Deserialize, Debug)] pub struct CarState { pub charge_state: Option, pub location_data: Option, pub climate_state: Option, } #[derive(Clone, Copy, Serialize, Deserialize, Debug)] pub struct ClimateState { pub inside_temp: f64, pub outside_temp: f64, pub battery_heater: bool, pub climate_on: bool, pub preconditioning: bool, pub remote_heater_control_enabled: bool, } impl TryFrom for ClimateState { type Error = TeslaStateParseError; fn try_from(value: teslatte::vehicles::ClimateState) -> Result { Ok(Self { inside_temp: value.inside_temp.ok_or(TeslaStateParseError::NoValue)?, outside_temp: value.outside_temp.ok_or(TeslaStateParseError::NoValue)?, battery_heater: value.battery_heater, climate_on: value.is_climate_on, preconditioning: value.is_preconditioning, remote_heater_control_enabled: value.remote_heater_control_enabled, }) } } #[derive(Clone, Copy, Serialize, Deserialize, Debug)] pub struct ChargeState { pub battery_level: i64, pub battery_range: f64, pub charge_amps: i64, pub charge_rate: f64, pub charge_current_request: i64, pub charge_current_request_max: i64, pub charger_connected: bool, pub charge_enable_request: bool, pub charging_state: ChargingState, pub charge_limit_soc: i64, } impl ChargeState { #[allow(unused)] pub fn range_km(&self) -> f64 { self.battery_range * 1.60934 } } impl From for ChargeState { fn from(value: teslatte::vehicles::ChargeState) -> Self { ChargeState { battery_level: value.battery_level, battery_range: value.battery_range, charge_amps: value.charge_amps, charge_rate: value.charge_rate, charge_current_request: value.charge_current_request, charge_current_request_max: value.charge_current_request_max, charger_connected: value.conn_charge_cable != "", charge_enable_request: value.charge_enable_request, charging_state: value.charging_state, charge_limit_soc: value.charge_limit_soc, } } } #[derive(Clone, Copy, Serialize, Deserialize, Debug)] pub struct LocationData { pub gps_as_of: DateTime, pub home: bool, } impl TryFrom for LocationData { type Error = TeslaStateParseError; fn try_from(value: teslatte::vehicles::DriveState) -> Result { let gps_as_of = chrono::DateTime::from_timestamp( value.gps_as_of.ok_or(TeslaStateParseError::NoValue)?, 0, ) .ok_or(TeslaStateParseError::InvalidTimestamp)?; let coords = Coords { latitude: value.latitude.ok_or(TeslaStateParseError::NoValue)?, longitude: value.longitude.ok_or(TeslaStateParseError::NoValue)?, }; let home = coords.overlaps(&access_config().coords); Ok(Self { gps_as_of, home }) } } #[derive(Serialize, Deserialize, Debug, Clone, Copy)] pub struct Coords { pub latitude: f64, pub longitude: f64, } const COORD_PRECISION: f64 = 0.001; impl Coords { pub fn overlaps(&self, other: &Coords) -> bool { (self.latitude - other.latitude).abs() < COORD_PRECISION && (self.longitude - other.longitude).abs() < COORD_PRECISION } }