2024-01-08 12:00:09 +11:00
|
|
|
use chrono::DateTime;
|
2024-01-07 10:08:16 +11:00
|
|
|
use serde::{Deserialize, Serialize};
|
2024-01-18 16:53:13 +11:00
|
|
|
use teslatte::vehicles::{CabinOverheatProtection, ChargingState, HvacAutoRequest, ShiftState};
|
2024-01-07 10:08:16 +11:00
|
|
|
|
2024-01-14 09:40:34 +11:00
|
|
|
use crate::{config::access_config, errors::TeslaStateParseError};
|
2024-01-12 08:47:21 +11:00
|
|
|
|
2024-01-14 09:40:34 +11:00
|
|
|
#[derive(Default, Clone, Copy, Serialize, Deserialize, Debug)]
|
2024-01-08 12:00:09 +11:00
|
|
|
pub struct CarState {
|
|
|
|
pub charge_state: Option<ChargeState>,
|
|
|
|
pub location_data: Option<LocationData>,
|
2024-01-09 11:11:16 +11:00
|
|
|
pub climate_state: Option<ClimateState>,
|
2024-01-21 10:06:14 +11:00
|
|
|
pub vehicle_state: Option<VehicleState>,
|
2024-01-09 11:11:16 +11:00
|
|
|
}
|
|
|
|
|
2024-01-16 12:28:15 +11:00
|
|
|
impl CarState {
|
|
|
|
pub fn is_charging_at_home(&self) -> bool {
|
|
|
|
self.charge_state
|
|
|
|
.is_some_and(|v| v.charging_state == ChargingState::Charging)
|
|
|
|
&& self.location_data.is_some_and(|v| v.home)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-21 10:06:14 +11:00
|
|
|
#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
|
|
|
|
pub struct VehicleState {
|
|
|
|
pub sentry_mode: Option<bool>,
|
|
|
|
pub sentry_mode_available: Option<bool>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<teslatte::vehicles::VehicleState> for VehicleState {
|
|
|
|
fn from(value: teslatte::vehicles::VehicleState) -> Self {
|
|
|
|
Self {
|
|
|
|
sentry_mode: value.sentry_mode,
|
|
|
|
sentry_mode_available: value.sentry_mode_available,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-09 11:11:16 +11:00
|
|
|
#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
|
|
|
|
pub struct ClimateState {
|
|
|
|
pub inside_temp: f64,
|
2024-01-09 12:06:18 +11:00
|
|
|
pub outside_temp: f64,
|
|
|
|
pub battery_heater: bool,
|
2024-01-15 09:42:58 +11:00
|
|
|
pub climate_on: bool,
|
|
|
|
pub preconditioning: bool,
|
|
|
|
pub remote_heater_control_enabled: bool,
|
2024-01-18 16:32:24 +11:00
|
|
|
pub is_auto_conditioning_on: bool,
|
|
|
|
pub driver_temp_setting: f64,
|
|
|
|
pub passenger_temp_setting: f64,
|
2024-01-18 16:53:13 +11:00
|
|
|
pub cabin_overheat_protection: CabinOverheatProtection,
|
|
|
|
pub hvac_auto_request: HvacAutoRequest,
|
2024-01-09 11:11:16 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
impl TryFrom<teslatte::vehicles::ClimateState> for ClimateState {
|
2024-01-12 08:47:21 +11:00
|
|
|
type Error = TeslaStateParseError;
|
2024-01-09 11:11:16 +11:00
|
|
|
|
|
|
|
fn try_from(value: teslatte::vehicles::ClimateState) -> Result<Self, Self::Error> {
|
|
|
|
Ok(Self {
|
2024-01-12 08:47:21 +11:00
|
|
|
inside_temp: value.inside_temp.ok_or(TeslaStateParseError::NoValue)?,
|
|
|
|
outside_temp: value.outside_temp.ok_or(TeslaStateParseError::NoValue)?,
|
2024-01-09 12:06:18 +11:00
|
|
|
battery_heater: value.battery_heater,
|
2024-01-15 09:42:58 +11:00
|
|
|
climate_on: value.is_climate_on,
|
|
|
|
preconditioning: value.is_preconditioning,
|
|
|
|
remote_heater_control_enabled: value.remote_heater_control_enabled,
|
2024-01-18 16:32:24 +11:00
|
|
|
is_auto_conditioning_on: value.is_auto_conditioning_on.unwrap_or(false),
|
|
|
|
driver_temp_setting: value.driver_temp_setting,
|
|
|
|
passenger_temp_setting: value.passenger_temp_setting,
|
2024-01-18 16:53:13 +11:00
|
|
|
cabin_overheat_protection: value.cabin_overheat_protection,
|
|
|
|
hvac_auto_request: value.hvac_auto_request,
|
2024-01-09 11:11:16 +11:00
|
|
|
})
|
|
|
|
}
|
2024-01-08 12:00:09 +11:00
|
|
|
}
|
|
|
|
|
2024-01-07 10:08:16 +11:00
|
|
|
#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
|
|
|
|
pub struct ChargeState {
|
|
|
|
pub battery_level: i64,
|
|
|
|
pub battery_range: f64,
|
|
|
|
pub charge_amps: i64,
|
2024-01-07 10:33:32 +11:00
|
|
|
pub charge_rate: f64,
|
2024-01-07 10:08:16 +11:00
|
|
|
pub charge_current_request: i64,
|
|
|
|
pub charge_current_request_max: i64,
|
2024-01-15 15:47:13 +11:00
|
|
|
pub charger_connected: bool,
|
2024-01-07 10:08:16 +11:00
|
|
|
pub charge_enable_request: bool,
|
2024-01-16 11:00:11 +11:00
|
|
|
pub charging_state: ChargingState,
|
2024-01-07 10:33:32 +11:00
|
|
|
pub charge_limit_soc: i64,
|
2024-01-23 15:31:27 +11:00
|
|
|
pub charger_actual_current: Option<i64>,
|
|
|
|
pub charger_phases: Option<i64>,
|
|
|
|
pub charger_pilot_current: Option<i64>,
|
|
|
|
pub charger_power: Option<i64>,
|
|
|
|
pub charger_voltage: Option<i64>,
|
2024-01-07 10:08:16 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<teslatte::vehicles::ChargeState> 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,
|
2024-01-07 10:33:32 +11:00
|
|
|
charge_rate: value.charge_rate,
|
2024-01-07 10:08:16 +11:00
|
|
|
charge_current_request: value.charge_current_request,
|
|
|
|
charge_current_request_max: value.charge_current_request_max,
|
2024-01-16 11:00:11 +11:00
|
|
|
charger_connected: value.conn_charge_cable != "<invalid>",
|
2024-01-07 10:08:16 +11:00
|
|
|
charge_enable_request: value.charge_enable_request,
|
2024-01-16 11:00:11 +11:00
|
|
|
charging_state: value.charging_state,
|
2024-01-07 10:33:32 +11:00
|
|
|
charge_limit_soc: value.charge_limit_soc,
|
2024-01-23 15:31:27 +11:00
|
|
|
charger_actual_current: value.charger_actual_current,
|
|
|
|
charger_phases: value.charger_phases,
|
|
|
|
charger_pilot_current: value.charger_pilot_current,
|
|
|
|
charger_power: value.charger_power,
|
|
|
|
charger_voltage: value.charger_voltage,
|
2024-01-07 10:08:16 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-01-08 12:00:09 +11:00
|
|
|
|
2024-01-18 10:01:35 +11:00
|
|
|
pub struct FromDriveState(pub LocationData, pub DriveState);
|
|
|
|
|
2024-01-08 12:00:09 +11:00
|
|
|
#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
|
|
|
|
pub struct LocationData {
|
|
|
|
pub gps_as_of: DateTime<chrono::Utc>,
|
2024-01-14 09:40:34 +11:00
|
|
|
pub home: bool,
|
2024-01-18 10:01:35 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
|
|
|
|
pub struct DriveState {
|
2024-01-18 08:51:44 +11:00
|
|
|
pub power: i64,
|
2024-01-18 10:01:35 +11:00
|
|
|
pub speed: i64,
|
|
|
|
pub shift_state: ShiftState,
|
2024-01-08 12:00:09 +11:00
|
|
|
}
|
|
|
|
|
2024-01-18 10:01:35 +11:00
|
|
|
impl TryFrom<teslatte::vehicles::DriveState> for FromDriveState {
|
2024-01-12 08:47:21 +11:00
|
|
|
type Error = TeslaStateParseError;
|
2024-01-08 12:00:09 +11:00
|
|
|
|
|
|
|
fn try_from(value: teslatte::vehicles::DriveState) -> Result<Self, Self::Error> {
|
2024-01-12 08:47:21 +11:00
|
|
|
let gps_as_of = chrono::DateTime::from_timestamp(
|
|
|
|
value.gps_as_of.ok_or(TeslaStateParseError::NoValue)?,
|
|
|
|
0,
|
|
|
|
)
|
|
|
|
.ok_or(TeslaStateParseError::InvalidTimestamp)?;
|
2024-01-08 12:00:09 +11:00
|
|
|
let coords = Coords {
|
2024-01-12 08:47:21 +11:00
|
|
|
latitude: value.latitude.ok_or(TeslaStateParseError::NoValue)?,
|
|
|
|
longitude: value.longitude.ok_or(TeslaStateParseError::NoValue)?,
|
2024-01-08 12:00:09 +11:00
|
|
|
};
|
2024-01-14 09:40:34 +11:00
|
|
|
let home = coords.overlaps(&access_config().coords);
|
2024-01-18 08:51:44 +11:00
|
|
|
let power = value.power;
|
2024-01-18 10:01:35 +11:00
|
|
|
let speed = value.speed.unwrap_or(0);
|
|
|
|
let shift_state = value.shift_state;
|
|
|
|
Ok(Self(
|
|
|
|
LocationData { gps_as_of, home },
|
|
|
|
DriveState {
|
|
|
|
power,
|
|
|
|
speed,
|
|
|
|
shift_state,
|
|
|
|
},
|
|
|
|
))
|
2024-01-08 12:00:09 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-23 12:14:04 +11:00
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
|
2024-01-08 12:00:09 +11:00
|
|
|
pub struct Coords {
|
|
|
|
pub latitude: f64,
|
|
|
|
pub longitude: f64,
|
|
|
|
}
|
|
|
|
|
2024-01-22 10:38:34 +11:00
|
|
|
// ~111 metres
|
2024-01-08 12:00:09 +11:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|