Compare commits
10 commits
ef26ce4697
...
cf30a7d1bc
Author | SHA1 | Date | |
---|---|---|---|
Alex Janka | cf30a7d1bc | ||
Alex Janka | a350280662 | ||
Alex Janka | db77d944cb | ||
Alex Janka | cd1ba77cf4 | ||
Alex Janka | 636c5fc482 | ||
Alex Janka | 4888ce9d39 | ||
Alex Janka | b2e7fd7004 | ||
Alex Janka | 7fb468b072 | ||
Alex Janka | ca778af269 | ||
Alex Janka | 2735f59b3f |
65
src/auth.rs
65
src/auth.rs
|
@ -1,5 +1,5 @@
|
||||||
use crate::error::TeslatteError::{CouldNotFindCallbackCode, CouldNotFindState};
|
use crate::error::TeslatteError::{CouldNotFindCallbackCode, CouldNotFindState};
|
||||||
use crate::{OwnerApi, TeslatteError};
|
use crate::{FleetApi, OwnerApi, TeslatteError};
|
||||||
use derive_more::{Display, FromStr};
|
use derive_more::{Display, FromStr};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
|
@ -10,6 +10,7 @@ use url::Url;
|
||||||
|
|
||||||
const AUTHORIZE_URL: &str = "https://auth.tesla.com/oauth2/v3/authorize";
|
const AUTHORIZE_URL: &str = "https://auth.tesla.com/oauth2/v3/authorize";
|
||||||
const TOKEN_URL: &str = "https://auth.tesla.com/oauth2/v3/token";
|
const TOKEN_URL: &str = "https://auth.tesla.com/oauth2/v3/token";
|
||||||
|
const CLIENT_ID: &str = "48ad82d96e76-4cf0-a301-08794a139ad9";
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, FromStr, Display)]
|
#[derive(Debug, Clone, Serialize, Deserialize, FromStr, Display)]
|
||||||
pub struct AccessToken(pub String);
|
pub struct AccessToken(pub String);
|
||||||
|
@ -193,6 +194,68 @@ page, where the URL will start with https://auth.tesla.com/void/callback?code=..
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FleetApi {
|
||||||
|
/// Refresh the internally stored access token using the known refresh token.
|
||||||
|
pub async fn refresh(&mut self) -> Result<(), TeslatteError> {
|
||||||
|
match &self.refresh_token {
|
||||||
|
None => Err(TeslatteError::NoRefreshToken),
|
||||||
|
Some(refresh_token) => {
|
||||||
|
let response = Self::refresh_token(refresh_token).await?;
|
||||||
|
self.access_token = response.access_token;
|
||||||
|
self.refresh_token = Some(response.refresh_token);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn refresh_token(
|
||||||
|
refresh_token: &RefreshToken,
|
||||||
|
) -> Result<RefreshTokenResponse, TeslatteError> {
|
||||||
|
let url = "https://auth.tesla.com/oauth2/v3/token";
|
||||||
|
let payload = RefreshTokenRequest {
|
||||||
|
grant_type: "refresh_token".into(),
|
||||||
|
client_id: CLIENT_ID.into(),
|
||||||
|
refresh_token: refresh_token.0.clone(),
|
||||||
|
scope: "openid email offline_access".into(),
|
||||||
|
};
|
||||||
|
Self::auth_post(url, &payload).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn auth_post<'a, S, D>(url: &str, payload: &S) -> Result<D, TeslatteError>
|
||||||
|
where
|
||||||
|
S: Serialize,
|
||||||
|
D: DeserializeOwned,
|
||||||
|
{
|
||||||
|
let response = Client::new()
|
||||||
|
.post(url)
|
||||||
|
.header("Accept", "application/json")
|
||||||
|
.json(payload)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(|source| TeslatteError::FetchError {
|
||||||
|
source,
|
||||||
|
request: url.to_string(),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let body = response
|
||||||
|
.text()
|
||||||
|
.await
|
||||||
|
.map_err(|source| TeslatteError::FetchError {
|
||||||
|
source,
|
||||||
|
request: url.to_string(),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let json =
|
||||||
|
serde_json::from_str::<D>(&body).map_err(|source| TeslatteError::DecodeJsonError {
|
||||||
|
source,
|
||||||
|
body: body.to_string(),
|
||||||
|
request: url.to_string(),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(json)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
struct RefreshTokenRequest {
|
struct RefreshTokenRequest {
|
||||||
grant_type: String,
|
grant_type: String,
|
||||||
|
|
192
src/lib.rs
192
src/lib.rs
|
@ -25,7 +25,11 @@ pub mod vehicles;
|
||||||
#[cfg(feature = "cli")]
|
#[cfg(feature = "cli")]
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
|
|
||||||
const API_URL: &str = "https://owner-api.teslamotors.com/api/1";
|
const API_URL: &str = if cfg!(debug_assertions) {
|
||||||
|
"http://cnut.internal.alexjanka.com:4444/api/1"
|
||||||
|
} else {
|
||||||
|
"https://localhost:4443/api/1"
|
||||||
|
};
|
||||||
|
|
||||||
pub trait VehicleApi {
|
pub trait VehicleApi {
|
||||||
async fn vehicles(&self) -> Result<Vec<Vehicle>, TeslatteError>;
|
async fn vehicles(&self) -> Result<Vec<Vehicle>, TeslatteError>;
|
||||||
|
@ -98,6 +102,61 @@ pub trait VehicleApi {
|
||||||
) -> Result<PostResponse, TeslatteError>;
|
) -> Result<PostResponse, TeslatteError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait FleetVehicleApi {
|
||||||
|
async fn vehicles(&self) -> Result<Vec<Vehicle>, TeslatteError>;
|
||||||
|
async fn vehicle_data(
|
||||||
|
&self,
|
||||||
|
get_vehicle_data: &GetVehicleData,
|
||||||
|
) -> Result<VehicleData, TeslatteError>;
|
||||||
|
async fn wake_up(&self, vin: &str) -> Result<PostResponse, TeslatteError>;
|
||||||
|
|
||||||
|
// Alerts
|
||||||
|
async fn honk_horn(&self, vin: &str) -> Result<PostResponse, TeslatteError>;
|
||||||
|
async fn flash_lights(&self, vin: &str) -> Result<PostResponse, TeslatteError>;
|
||||||
|
|
||||||
|
// Charging
|
||||||
|
async fn charge_port_door_open(&self, vin: &str) -> Result<PostResponse, TeslatteError>;
|
||||||
|
async fn charge_port_door_close(&self, vin: &str) -> Result<PostResponse, TeslatteError>;
|
||||||
|
async fn set_charge_limit(
|
||||||
|
&self,
|
||||||
|
vin: &str,
|
||||||
|
data: &SetChargeLimit,
|
||||||
|
) -> Result<PostResponse, TeslatteError>;
|
||||||
|
async fn set_charging_amps(
|
||||||
|
&self,
|
||||||
|
vin: &str,
|
||||||
|
data: &SetChargingAmps,
|
||||||
|
) -> Result<PostResponse, TeslatteError>;
|
||||||
|
async fn charge_standard(&self, vin: &str) -> Result<PostResponse, TeslatteError>;
|
||||||
|
async fn charge_max_range(&self, vin: &str) -> Result<PostResponse, TeslatteError>;
|
||||||
|
async fn charge_start(&self, vin: &str) -> Result<PostResponse, TeslatteError>;
|
||||||
|
async fn charge_stop(&self, vin: &str) -> Result<PostResponse, TeslatteError>;
|
||||||
|
async fn set_scheduled_charging(
|
||||||
|
&self,
|
||||||
|
vin: &str,
|
||||||
|
data: &SetScheduledCharging,
|
||||||
|
) -> Result<PostResponse, TeslatteError>;
|
||||||
|
async fn set_scheduled_departure(
|
||||||
|
&self,
|
||||||
|
vin: &str,
|
||||||
|
data: &SetScheduledDeparture,
|
||||||
|
) -> Result<PostResponse, TeslatteError>;
|
||||||
|
|
||||||
|
// HVAC
|
||||||
|
async fn auto_conditioning_start(&self, vin: &str) -> Result<PostResponse, TeslatteError>;
|
||||||
|
async fn auto_conditioning_stop(&self, vin: &str) -> Result<PostResponse, TeslatteError>;
|
||||||
|
async fn set_temps(
|
||||||
|
&self,
|
||||||
|
vin: &str,
|
||||||
|
data: &SetTemperatures,
|
||||||
|
) -> Result<PostResponse, TeslatteError>;
|
||||||
|
|
||||||
|
// Doors
|
||||||
|
async fn door_unlock(&self, vin: &str) -> Result<PostResponse, TeslatteError>;
|
||||||
|
async fn door_lock(&self, vin: &str) -> Result<PostResponse, TeslatteError>;
|
||||||
|
async fn remote_start_drive(&self, vin: &str) -> Result<PostResponse, TeslatteError>;
|
||||||
|
}
|
||||||
|
|
||||||
trait EnergySitesApi {}
|
trait EnergySitesApi {}
|
||||||
|
|
||||||
trait ApiValues {
|
trait ApiValues {
|
||||||
|
@ -107,7 +166,7 @@ trait ApiValues {
|
||||||
/// Vehicle ID used by the owner-api endpoint.
|
/// Vehicle ID used by the owner-api endpoint.
|
||||||
///
|
///
|
||||||
/// This data comes from [`OwnerApi::vehicles()`] `id` field.
|
/// This data comes from [`OwnerApi::vehicles()`] `id` field.
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Display, FromStr, From, Deref)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Display, FromStr, From, Deref)]
|
||||||
pub struct VehicleId(u64);
|
pub struct VehicleId(u64);
|
||||||
|
|
||||||
impl VehicleId {
|
impl VehicleId {
|
||||||
|
@ -272,6 +331,135 @@ impl OwnerApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct FleetApi {
|
||||||
|
pub access_token: AccessToken,
|
||||||
|
pub refresh_token: Option<RefreshToken>,
|
||||||
|
pub print_responses: PrintResponses,
|
||||||
|
client: Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FleetApi {
|
||||||
|
pub fn new(access_token: AccessToken, refresh_token: Option<RefreshToken>) -> Self {
|
||||||
|
Self {
|
||||||
|
access_token,
|
||||||
|
refresh_token,
|
||||||
|
print_responses: PrintResponses::No,
|
||||||
|
client: Client::builder()
|
||||||
|
.timeout(std::time::Duration::from_secs(10))
|
||||||
|
.add_root_certificate(
|
||||||
|
reqwest::Certificate::from_pem(include_bytes!("./selfsigned.pem")).unwrap(),
|
||||||
|
)
|
||||||
|
.danger_accept_invalid_certs(cfg!(debug_assertions))
|
||||||
|
.build()
|
||||||
|
.unwrap(), // TODO: unwrap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get<D>(&self, url: &str) -> Result<D, TeslatteError>
|
||||||
|
where
|
||||||
|
D: for<'de> Deserialize<'de> + Debug,
|
||||||
|
{
|
||||||
|
self.request(&RequestData::Get { url }).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn post<S>(&self, url: &str, body: S) -> Result<PostResponse, TeslatteError>
|
||||||
|
where
|
||||||
|
S: Serialize + Debug,
|
||||||
|
{
|
||||||
|
let payload =
|
||||||
|
&serde_json::to_string(&body).expect("Should not fail creating the request struct.");
|
||||||
|
let request_data = RequestData::Post { url, payload };
|
||||||
|
let data = self.request::<PostResponse>(&request_data).await?;
|
||||||
|
|
||||||
|
if !data.result {
|
||||||
|
return Err(TeslatteError::ServerError {
|
||||||
|
request: format!("{request_data}"),
|
||||||
|
description: None,
|
||||||
|
msg: data.reason,
|
||||||
|
body: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn request<T>(&self, request_data: &RequestData<'_>) -> Result<T, TeslatteError>
|
||||||
|
where
|
||||||
|
T: for<'de> Deserialize<'de> + Debug,
|
||||||
|
{
|
||||||
|
debug!("{request_data}");
|
||||||
|
|
||||||
|
let request_builder = match request_data {
|
||||||
|
RequestData::Get { url } => self.client.get(*url),
|
||||||
|
RequestData::Post { url, payload } => self
|
||||||
|
.client
|
||||||
|
.post(*url)
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.body(payload.to_string()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let response_body = request_builder
|
||||||
|
.header("Accept", "application/json")
|
||||||
|
.header(
|
||||||
|
"Authorization",
|
||||||
|
format!("Bearer {}", self.access_token.0.trim()),
|
||||||
|
)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(|source| TeslatteError::FetchError {
|
||||||
|
source,
|
||||||
|
request: format!("{request_data}"),
|
||||||
|
})?
|
||||||
|
.text()
|
||||||
|
.await
|
||||||
|
.map_err(|source| TeslatteError::FetchError {
|
||||||
|
source,
|
||||||
|
request: format!("{request_data}"),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
debug!("Response: {response_body}");
|
||||||
|
|
||||||
|
Self::parse_json(request_data, response_body, self.print_responses)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_json<T>(
|
||||||
|
request_data: &RequestData,
|
||||||
|
response_body: String,
|
||||||
|
print_response: PrintResponses,
|
||||||
|
) -> Result<T, TeslatteError>
|
||||||
|
where
|
||||||
|
T: for<'de> Deserialize<'de> + Debug,
|
||||||
|
{
|
||||||
|
match print_response {
|
||||||
|
PrintResponses::No => {}
|
||||||
|
PrintResponses::Plain => {
|
||||||
|
println!("{}", response_body);
|
||||||
|
}
|
||||||
|
PrintResponses::Pretty => {
|
||||||
|
print_json_str(&response_body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let response: Response<T> = serde_json::from_str::<ResponseDeserializer<T>>(&response_body)
|
||||||
|
.map_err(|source| TeslatteError::DecodeJsonError {
|
||||||
|
source,
|
||||||
|
request: format!("{request_data}"),
|
||||||
|
body: response_body.to_string(),
|
||||||
|
})?
|
||||||
|
.into();
|
||||||
|
|
||||||
|
match response {
|
||||||
|
Response::Response(data) => Ok(data),
|
||||||
|
Response::Error(e) => Err(TeslatteError::ServerError {
|
||||||
|
request: format!("{request_data}"),
|
||||||
|
msg: e.error,
|
||||||
|
description: e.error_description,
|
||||||
|
body: Some(response_body.to_owned()),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
struct ResponseDeserializer<T> {
|
struct ResponseDeserializer<T> {
|
||||||
error: Option<ResponseError>,
|
error: Option<ResponseError>,
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::energy_sites::WallConnector;
|
||||||
use crate::error::TeslatteError;
|
use crate::error::TeslatteError;
|
||||||
use crate::powerwall::PowerwallId;
|
use crate::powerwall::PowerwallId;
|
||||||
use crate::vehicles::VehicleData;
|
use crate::vehicles::VehicleData;
|
||||||
use crate::{pub_get, OwnerApi};
|
use crate::{pub_get, FleetApi, OwnerApi};
|
||||||
use derive_more::Display;
|
use derive_more::Display;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
@ -12,6 +12,11 @@ impl OwnerApi {
|
||||||
pub_get!(products, Vec<Product>, "/products");
|
pub_get!(products, Vec<Product>, "/products");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
impl FleetApi {
|
||||||
|
pub_get!(products, Vec<Product>, "/products");
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Display)]
|
#[derive(Debug, Clone, Deserialize, Display)]
|
||||||
pub struct EnergySiteId(pub u64);
|
pub struct EnergySiteId(pub u64);
|
||||||
|
|
||||||
|
|
14
src/selfsigned.pem
Normal file
14
src/selfsigned.pem
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICJjCCAYigAwIBAgIUT9V89Ca28OFWbQM4tx7rBOaqw4UwCgYIKoZIzj0EAwIw
|
||||||
|
FDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIzMTIyNzA3MDcxNVoXDTMzMTIyNDA3
|
||||||
|
MDcxNVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIGbMBAGByqGSM49AgEGBSuBBAAj
|
||||||
|
A4GGAAQAQ0BavfhlRoxnE4CmRx22LKafnx6Oqs0fO/vSmPqgqWzMmMUCsak9bNoJ
|
||||||
|
fNSFS2beUY8+myR2kpAPadp5AQAz2wsBCHEriqlp88sQKJWGaAzzmuZP0UmxaIJK
|
||||||
|
Ftcv0RLyuWST5NN61xp0yzrbF9tSjXq34qrIPcxU7t2t3IylP3rApYujdTBzMB0G
|
||||||
|
A1UdDgQWBBThM5D2TLjTuZ6we4CgyRl+iWScezAfBgNVHSMEGDAWgBThM5D2TLjT
|
||||||
|
uZ6we4CgyRl+iWScezAPBgNVHRMBAf8EBTADAQH/MBMGA1UdJQQMMAoGCCsGAQUF
|
||||||
|
BwMBMAsGA1UdDwQEAwICjDAKBggqhkjOPQQDAgOBiwAwgYcCQUx062mqiK+K8AFf
|
||||||
|
/TkjzxXYacUbvy0+ubIpytOMOT36noMkShe8m0Y/1Y3l2HGlSvWeTQzkCF0fIu/d
|
||||||
|
NdBiRFkwAkIBtqfzcXGHklZgKNg9iKfUhoX93mDUFv/b1Z3AGHYHnVT3kJvOy2zO
|
||||||
|
uQLK0NgYXCVMADGzjWuY14XYvTyTBGxfw2w=
|
||||||
|
-----END CERTIFICATE-----
|
109
src/vehicles.rs
109
src/vehicles.rs
|
@ -4,10 +4,10 @@ use derive_more::{Deref, DerefMut, From};
|
||||||
// Sometimes the API will return a null for a field where I've put in a non Option type, which
|
// Sometimes the API will return a null for a field where I've put in a non Option type, which
|
||||||
// will cause the deserializer to fail. Please log an issue to fix these if you come across it.
|
// will cause the deserializer to fail. Please log an issue to fix these if you come across it.
|
||||||
use crate::{
|
use crate::{
|
||||||
get, get_args, post_arg, post_arg_empty, ApiValues, Empty, ExternalVehicleId, OwnerApi,
|
get, get_args, post_arg, post_arg_empty, ApiValues, Empty, ExternalVehicleId, FleetApi,
|
||||||
VehicleApi, VehicleId,
|
FleetVehicleApi, OwnerApi, VehicleApi, VehicleId,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
use strum::{Display, EnumString};
|
use strum::{Display, EnumString};
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
|
@ -43,6 +43,39 @@ impl VehicleApi for OwnerApi {
|
||||||
post_arg_empty!(remote_start_drive, "/vehicles/{}/command/remote_start_drive", VehicleId);
|
post_arg_empty!(remote_start_drive, "/vehicles/{}/command/remote_start_drive", VehicleId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
impl FleetVehicleApi for FleetApi {
|
||||||
|
get!(vehicles, Vec<Vehicle>, "/vehicles");
|
||||||
|
get_args!(vehicle_data, VehicleData, "/vehicles/{}/vehicle_data", GetVehicleData);
|
||||||
|
post_arg_empty!(wake_up, "/vehicles/{}/command/wake_up", str);
|
||||||
|
|
||||||
|
// Alerts
|
||||||
|
post_arg_empty!(honk_horn, "/vehicles/{}/command/honk_horn", str);
|
||||||
|
post_arg_empty!(flash_lights, "/vehicles/{}/command/flash_lights", str);
|
||||||
|
|
||||||
|
// Charging
|
||||||
|
post_arg_empty!(charge_port_door_open, "/vehicles/{}/command/charge_port_door_open", str);
|
||||||
|
post_arg_empty!(charge_port_door_close, "/vehicles/{}/command/charge_port_door_close", str);
|
||||||
|
post_arg!(set_charge_limit, SetChargeLimit, "/vehicles/{}/command/set_charge_limit", str);
|
||||||
|
post_arg!(set_charging_amps, SetChargingAmps, "/vehicles/{}/command/set_charging_amps", str);
|
||||||
|
post_arg_empty!(charge_standard, "/vehicles/{}/command/charge_standard", str);
|
||||||
|
post_arg_empty!(charge_max_range, "/vehicles/{}/command/charge_max_range", str);
|
||||||
|
post_arg_empty!(charge_start, "/vehicles/{}/command/charge_start", str);
|
||||||
|
post_arg_empty!(charge_stop, "/vehicles/{}/command/charge_stop", str);
|
||||||
|
post_arg!(set_scheduled_charging, SetScheduledCharging, "/vehicles/{}/command/set_scheduled_charging", str);
|
||||||
|
post_arg!(set_scheduled_departure, SetScheduledDeparture, "/vehicles/{}/command/set_scheduled_departure", str);
|
||||||
|
|
||||||
|
// HVAC
|
||||||
|
post_arg_empty!(auto_conditioning_start, "/vehicles/{}/command/auto_conditioning_start", str);
|
||||||
|
post_arg_empty!(auto_conditioning_stop, "/vehicles/{}/command/auto_conditioning_stop", str);
|
||||||
|
post_arg!(set_temps, SetTemperatures, "/vehicles/{}/command/set_temps", str);
|
||||||
|
|
||||||
|
// Doors
|
||||||
|
post_arg_empty!(door_unlock, "/vehicles/{}/command/door_unlock", str);
|
||||||
|
post_arg_empty!(door_lock, "/vehicles/{}/command/door_lock", str);
|
||||||
|
post_arg_empty!(remote_start_drive, "/vehicles/{}/command/remote_start_drive", str);
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Display, EnumString)]
|
#[derive(Debug, Clone, Display, EnumString)]
|
||||||
#[strum(serialize_all = "snake_case")]
|
#[strum(serialize_all = "snake_case")]
|
||||||
pub enum Endpoint {
|
pub enum Endpoint {
|
||||||
|
@ -185,7 +218,7 @@ pub struct ChargeState {
|
||||||
pub charger_pilot_current: Option<i64>,
|
pub charger_pilot_current: Option<i64>,
|
||||||
pub charger_power: Option<i64>,
|
pub charger_power: Option<i64>,
|
||||||
pub charger_voltage: Option<i64>,
|
pub charger_voltage: Option<i64>,
|
||||||
pub charging_state: String,
|
pub charging_state: ChargingState,
|
||||||
pub conn_charge_cable: String,
|
pub conn_charge_cable: String,
|
||||||
pub est_battery_range: f64,
|
pub est_battery_range: f64,
|
||||||
pub fast_charger_brand: String,
|
pub fast_charger_brand: String,
|
||||||
|
@ -208,8 +241,8 @@ pub struct ChargeState {
|
||||||
pub scheduled_charging_start_time: Option<i64>,
|
pub scheduled_charging_start_time: Option<i64>,
|
||||||
pub scheduled_charging_start_time_app: Option<i64>,
|
pub scheduled_charging_start_time_app: Option<i64>,
|
||||||
pub scheduled_charging_start_time_minutes: Option<i64>,
|
pub scheduled_charging_start_time_minutes: Option<i64>,
|
||||||
pub scheduled_departure_time: i64,
|
pub scheduled_departure_time: Option<i64>,
|
||||||
pub scheduled_departure_time_minutes: i64,
|
pub scheduled_departure_time_minutes: Option<i64>,
|
||||||
pub supercharger_session_trip_planner: bool,
|
pub supercharger_session_trip_planner: bool,
|
||||||
pub time_to_full_charge: f64,
|
pub time_to_full_charge: f64,
|
||||||
pub timestamp: u64,
|
pub timestamp: u64,
|
||||||
|
@ -218,6 +251,16 @@ pub struct ChargeState {
|
||||||
pub user_charge_enable_request: Option<bool>,
|
pub user_charge_enable_request: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Display, Deserialize, Serialize, PartialEq)]
|
||||||
|
pub enum ChargingState {
|
||||||
|
Charging,
|
||||||
|
Stopped,
|
||||||
|
Disconnected,
|
||||||
|
Complete,
|
||||||
|
#[serde(other)]
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct ClimateState {
|
pub struct ClimateState {
|
||||||
pub allow_cabin_overheat_protection: bool,
|
pub allow_cabin_overheat_protection: bool,
|
||||||
|
@ -225,13 +268,13 @@ pub struct ClimateState {
|
||||||
pub auto_seat_climate_right: Option<bool>,
|
pub auto_seat_climate_right: Option<bool>,
|
||||||
pub battery_heater: bool,
|
pub battery_heater: bool,
|
||||||
pub battery_heater_no_power: Option<bool>,
|
pub battery_heater_no_power: Option<bool>,
|
||||||
pub cabin_overheat_protection: String,
|
pub cabin_overheat_protection: CabinOverheatProtection,
|
||||||
pub cabin_overheat_protection_actively_cooling: Option<bool>,
|
pub cabin_overheat_protection_actively_cooling: Option<bool>,
|
||||||
pub climate_keeper_mode: String,
|
pub climate_keeper_mode: String,
|
||||||
pub defrost_mode: i64,
|
pub defrost_mode: i64,
|
||||||
pub driver_temp_setting: f64,
|
pub driver_temp_setting: f64,
|
||||||
pub fan_status: i64,
|
pub fan_status: i64,
|
||||||
pub hvac_auto_request: String,
|
pub hvac_auto_request: HvacAutoRequest,
|
||||||
pub inside_temp: Option<f64>,
|
pub inside_temp: Option<f64>,
|
||||||
pub is_auto_conditioning_on: Option<bool>,
|
pub is_auto_conditioning_on: Option<bool>,
|
||||||
pub is_climate_on: bool,
|
pub is_climate_on: bool,
|
||||||
|
@ -261,6 +304,23 @@ pub struct ClimateState {
|
||||||
pub steering_wheel_heat_level: Option<i64>,
|
pub steering_wheel_heat_level: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Display, Deserialize, Serialize, PartialEq)]
|
||||||
|
pub enum CabinOverheatProtection {
|
||||||
|
Off,
|
||||||
|
On,
|
||||||
|
FanOnly,
|
||||||
|
#[serde(other)]
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Display, Deserialize, Serialize, PartialEq)]
|
||||||
|
pub enum HvacAutoRequest {
|
||||||
|
Override,
|
||||||
|
On,
|
||||||
|
#[serde(other)]
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct DriveState {
|
pub struct DriveState {
|
||||||
/// From https://developer.tesla.com/docs/fleet-api#vehicle_data
|
/// From https://developer.tesla.com/docs/fleet-api#vehicle_data
|
||||||
|
@ -276,14 +336,42 @@ pub struct DriveState {
|
||||||
pub native_type: Option<String>,
|
pub native_type: Option<String>,
|
||||||
|
|
||||||
pub power: i64,
|
pub power: i64,
|
||||||
pub shift_state: Option<String>,
|
#[serde(deserialize_with = "map_null_to_default")]
|
||||||
|
pub shift_state: ShiftState,
|
||||||
/// gak: I've assumed this to be String.
|
/// gak: I've assumed this to be String.
|
||||||
pub speed: Option<String>,
|
pub speed: Option<i64>,
|
||||||
pub timestamp: i64,
|
pub timestamp: i64,
|
||||||
|
|
||||||
pub active_route_traffic_minutes_delay: Option<f64>,
|
pub active_route_traffic_minutes_delay: Option<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Display, Deserialize, Serialize, PartialEq)]
|
||||||
|
pub enum ShiftState {
|
||||||
|
#[serde(alias = "D")]
|
||||||
|
Drive,
|
||||||
|
#[serde(alias = "N")]
|
||||||
|
Neutral,
|
||||||
|
#[serde(alias = "R")]
|
||||||
|
Reverse,
|
||||||
|
#[serde(other)]
|
||||||
|
#[serde(alias = "P")]
|
||||||
|
Park,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ShiftState {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Park
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map_null_to_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
|
||||||
|
where
|
||||||
|
T: Default + Deserialize<'de>,
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
Ok(Option::deserialize(deserializer)?.unwrap_or_default())
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct GuiSettings {
|
pub struct GuiSettings {
|
||||||
pub gui_24_hour_time: bool,
|
pub gui_24_hour_time: bool,
|
||||||
|
@ -458,6 +546,7 @@ pub struct GranularAccess {
|
||||||
pub hide_private: bool,
|
pub hide_private: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Vehicles(Vec<Vehicle>);
|
pub struct Vehicles(Vec<Vehicle>);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue