vendored teslatte + some refactoring

This commit is contained in:
Alex Janka 2023-12-27 12:20:45 +11:00
parent b4a03f5e54
commit f2e0bb6dc5
8 changed files with 136 additions and 23 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
/target /target
/test-config

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "vendored/teslatte"]
path = vendored/teslatte
url = https://github.com/gak/teslatte

55
Cargo.lock generated
View file

@ -571,7 +571,7 @@ dependencies = [
"futures-util", "futures-util",
"http", "http",
"hyper", "hyper",
"rustls", "rustls 0.21.10",
"tokio", "tokio",
"tokio-rustls", "tokio-rustls",
] ]
@ -1083,7 +1083,7 @@ dependencies = [
"once_cell", "once_cell",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"rustls", "rustls 0.21.10",
"rustls-pemfile", "rustls-pemfile",
"serde", "serde",
"serde_json", "serde_json",
@ -1163,10 +1163,24 @@ checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
dependencies = [ dependencies = [
"log", "log",
"ring", "ring",
"rustls-webpki", "rustls-webpki 0.101.7",
"sct", "sct",
] ]
[[package]]
name = "rustls"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe6b63262c9fcac8659abfaa96cac103d28166d3ff3eaf8f412e19f3ae9e5a48"
dependencies = [
"log",
"ring",
"rustls-pki-types",
"rustls-webpki 0.102.0",
"subtle",
"zeroize",
]
[[package]] [[package]]
name = "rustls-pemfile" name = "rustls-pemfile"
version = "1.0.4" version = "1.0.4"
@ -1176,6 +1190,12 @@ dependencies = [
"base64", "base64",
] ]
[[package]]
name = "rustls-pki-types"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e9d979b3ce68192e42760c7810125eb6cf2ea10efae545a156063e61f314e2a"
[[package]] [[package]]
name = "rustls-webpki" name = "rustls-webpki"
version = "0.101.7" version = "0.101.7"
@ -1186,6 +1206,17 @@ dependencies = [
"untrusted", "untrusted",
] ]
[[package]]
name = "rustls-webpki"
version = "0.102.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de2635c8bc2b88d367767c5de8ea1d8db9af3f6219eba28442242d9ab81d1b89"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.14" version = "1.0.14"
@ -1389,6 +1420,12 @@ dependencies = [
"syn 2.0.43", "syn 2.0.43",
] ]
[[package]]
name = "subtle"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]] [[package]]
name = "supports-color" name = "supports-color"
version = "2.1.0" version = "2.1.0"
@ -1498,8 +1535,6 @@ dependencies = [
[[package]] [[package]]
name = "teslatte" name = "teslatte"
version = "0.1.11" version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caf300415380acad835db51dc04d5f331dfab81aa24e425d7da39af5d17b0830"
dependencies = [ dependencies = [
"chrono", "chrono",
"clap", "clap",
@ -1509,7 +1544,7 @@ dependencies = [
"pkce", "pkce",
"rand", "rand",
"reqwest", "reqwest",
"rustls", "rustls 0.22.1",
"serde", "serde",
"serde_json", "serde_json",
"strum", "strum",
@ -1652,7 +1687,7 @@ version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
dependencies = [ dependencies = [
"rustls", "rustls 0.21.10",
"tokio", "tokio",
] ]
@ -2100,3 +2135,9 @@ name = "yansi"
version = "0.5.1" version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
[[package]]
name = "zeroize"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"

View file

@ -28,5 +28,5 @@ clap = { version = "4.0", features = ["derive"] }
ron = "0.8" ron = "0.8"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.35.1", features = ["full"] } tokio = { version = "1.35.1", features = ["full"] }
teslatte = "0.1.11" teslatte = { path = "vendored/teslatte" }
thiserror = "1.0" thiserror = "1.0"

3
src/control.rs Normal file
View file

@ -0,0 +1,3 @@
use teslatte::OwnerApi;
pub async fn control_loop(_api: OwnerApi) {}

View file

@ -1,18 +1,40 @@
use thiserror::Error; use thiserror::Error;
#[derive(Error, Debug)]
pub enum SaveError {
#[error("stdio error")]
StdIo(#[from] std::io::Error),
#[error("ron")]
RonSpanned(#[from] ron::Error),
}
impl SaveError {
pub fn error_string(&self) -> String {
match self {
SaveError::StdIo(e) => format!("Error reading access token from disk: {e:?}"),
SaveError::RonSpanned(e) => format!("Error deserialising access token: {e:?}"),
}
}
}
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum AuthLoadError { pub enum AuthLoadError {
#[error("stdio error")] #[error("stdio error")]
StdIo(#[from] std::io::Error), StdIo(#[from] std::io::Error),
#[error("ron - spanned error")] #[error("ron - spanned error")]
RonSpanned(#[from] ron::error::SpannedError), RonSpanned(#[from] ron::error::SpannedError),
#[error("teslatte error")]
Teslatte(#[from] teslatte::error::TeslatteError),
#[error("save error")]
Save(#[from] SaveError),
} }
impl AuthLoadError { impl AuthLoadError {
pub fn error_string(&self) -> String { pub fn error_string(&self) -> String {
match self { match self {
AuthLoadError::Teslatte(e) => format!("Error refreshing access token: {e:?}"),
AuthLoadError::StdIo(e) => format!("Error reading access token from disk: {e:?}"), AuthLoadError::StdIo(e) => format!("Error reading access token from disk: {e:?}"),
AuthLoadError::RonSpanned(e) => format!("Error deserialising access token: {e:?}"), AuthLoadError::RonSpanned(e) => format!("Error deserialising access token: {e:?}"),
AuthLoadError::Save(e) => e.error_string(),
} }
} }
} }
@ -21,18 +43,15 @@ impl AuthLoadError {
pub enum LoginError { pub enum LoginError {
#[error("teslatte error")] #[error("teslatte error")]
Teslatte(#[from] teslatte::error::TeslatteError), Teslatte(#[from] teslatte::error::TeslatteError),
#[error("ron error")] #[error("save error")]
Ron(#[from] ron::Error), Save(#[from] SaveError),
#[error("stdio error")]
StdIo(#[from] std::io::Error),
} }
impl LoginError { impl LoginError {
pub fn error_string(&self) -> String { pub fn error_string(&self) -> String {
match self { match self {
LoginError::Teslatte(e) => format!("Authentication flow error: {e:?}"), LoginError::Teslatte(e) => format!("Authentication flow error: {e:?}"),
LoginError::Ron(e) => format!("Error serialising access token: {e:?}"), LoginError::Save(e) => e.error_string(),
LoginError::StdIo(e) => format!("Error saving access token to disk: {e:?}"),
} }
} }
} }

View file

@ -1,10 +1,15 @@
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use serde::{Deserialize, Serialize};
use std::{io::BufRead, path::PathBuf}; use std::{io::BufRead, path::PathBuf};
use teslatte::{auth::AccessToken, OwnerApi}; use teslatte::{
auth::{AccessToken, RefreshToken},
OwnerApi, VehicleApi,
};
use crate::{config::Config, errors::*}; use crate::{config::Config, errors::*};
mod config; mod config;
mod control;
mod errors; mod errors;
#[derive(Parser, Debug, Clone)] #[derive(Parser, Debug, Clone)]
@ -19,7 +24,10 @@ struct Args {
#[derive(Subcommand, Debug, Clone)] #[derive(Subcommand, Debug, Clone)]
enum Commands { enum Commands {
/// Run charge controller server /// Run charge controller server
Watch, Watch {
#[clap(long)]
flash: bool,
},
/// Authenticate with Tesla login /// Authenticate with Tesla login
Auth, Auth,
/// Print the default config file /// Print the default config file
@ -43,7 +51,7 @@ fn press_y_to_continue() -> bool {
async fn main() { async fn main() {
let args = Args::parse(); let args = Args::parse();
let auth_path = args.config_dir.join("auth"); let auth_path = args.config_dir.join("auth");
let config_path = args.config_dir.join("config"); // let config_path = args.config_dir.join("config");
match args.command { match args.command {
Commands::GenerateConfig => { Commands::GenerateConfig => {
@ -63,24 +71,61 @@ async fn main() {
println!("{}", e.error_string()); println!("{}", e.error_string());
} }
} }
Commands::Watch => match get_auth(auth_path) { Commands::Watch { flash } => match get_auth(auth_path).await {
Ok(api) => { Ok(api) => {
println!("got products: {:#?}", api.products().await) let products = api.products().await;
println!("got products: {:#?}", products);
if flash {
if let Ok(res) = products {
if let Some(teslatte::products::Product::Vehicle(vehicle)) = res.first() {
let _ = api.honk_horn(&vehicle.id).await;
match api.flash_lights(&vehicle.id).await {
Ok(_r) => println!("flashed"),
Err(e) => println!("error: {e:#?}"),
}
}
}
}
control::control_loop(api).await;
} }
Err(e) => println!("{}", e.error_string()), Err(e) => println!("{}", e.error_string()),
}, },
} }
} }
fn get_auth(auth_path: PathBuf) -> Result<OwnerApi, AuthLoadError> { async fn get_auth(auth_path: PathBuf) -> Result<OwnerApi, AuthLoadError> {
let key: AccessToken = ron::from_str(&std::fs::read_to_string(auth_path)?)?; let key: AuthInfo = ron::from_str(&std::fs::read_to_string(&auth_path)?)?;
Ok(OwnerApi::new(key, None)) let mut api = OwnerApi::new(key.access_token, key.refresh_token);
api.refresh().await?;
println!("Refreshed auth key");
save_key(auth_path, &api)?;
Ok(api)
}
#[derive(Serialize, Deserialize)]
struct AuthInfo {
access_token: AccessToken,
refresh_token: Option<RefreshToken>,
} }
async fn log_in(auth_path: PathBuf) -> Result<(), LoginError> { async fn log_in(auth_path: PathBuf) -> Result<(), LoginError> {
let v = OwnerApi::from_interactive_url().await?; let v = OwnerApi::from_interactive_url().await?;
std::fs::write(auth_path, ron::ser::to_string(&v.access_token)?)?; let products = v.products().await;
println!("got products: {:#?}", products);
save_key(auth_path, &v)?;
Ok(())
}
fn save_key(auth_path: PathBuf, api: &OwnerApi) -> Result<(), SaveError> {
std::fs::write(
auth_path,
ron::ser::to_string(&AuthInfo {
access_token: api.access_token.clone(),
refresh_token: api.refresh_token.clone(),
})?,
)?;
println!("Auth successfully saved"); println!("Auth successfully saved");
Ok(()) Ok(())
} }

1
vendored/teslatte Submodule

@ -0,0 +1 @@
Subproject commit 491d9a58a8183b91314990274da805c156a6f48c