use clap::{Parser, Subcommand}; use serde::{Deserialize, Serialize}; use std::{io::BufRead, path::PathBuf}; use teslatte::{ auth::{AccessToken, RefreshToken}, vehicles::{ActuateTrunk, SetChargingAmps}, FleetApi, FleetVehicleApi, }; use tokio::try_join; use crate::{config::Config, errors::*}; mod config; mod errors; #[derive(Parser, Debug, Clone)] #[clap(author, version, about, long_about = None)] struct Args { #[command(subcommand)] command: Commands, #[clap(long, default_value = "/etc/tesla-charge-controller")] config_dir: PathBuf, } #[derive(Subcommand, Debug, Clone)] enum Commands { /// Run charge controller server Watch { #[clap(long)] flash: bool, }, /// Run charge controller server SetChargeRate { #[clap(long)] amps: usize, }, /// Print the default config file GenerateConfig, /// Start charging StartCharge, /// Stop charging StopCharge, /// Toggle boot ToggleBoot, /// Open charger door ChargerOpen, /// Close charger door ChargerClose, } #[allow(unused)] fn press_y_to_continue() -> bool { println!("Continue? [y/N]"); let mut line = String::new(); let stdin = std::io::stdin(); stdin.lock().read_line(&mut line).unwrap(); if line.to_uppercase() == "Y\n" { true } else { println!("Exiting now!"); false } } fn loop_prompt() -> bool { println!("Again? [Y/n]"); let mut line = String::new(); let stdin = std::io::stdin(); stdin.lock().read_line(&mut line).unwrap(); if line.to_uppercase() == "N\n" { println!("Exiting now!"); false } else { true } } #[tokio::main] async fn main() { let args = Args::parse(); let auth_path = args.config_dir.join("auth"); // let config_path = args.config_dir.join("config"); match get_auth(auth_path).await { Ok(api) => { let products = api.products().await; if let Ok(res) = products { if let Some(teslatte::products::Product::Vehicle(vehicle)) = res.first() { api.wake_up(&vehicle.vin).await.unwrap(); match match args.command { Commands::GenerateConfig => { println!( "{}", ron::ser::to_string_pretty(&Config::default(), Default::default()) .unwrap() ); Ok(MaybePostResponse::None) } Commands::Watch { flash } => { if flash { println!("{vehicle:#?}"); match try_join!( api.honk_horn(&vehicle.vin), api.flash_lights(&vehicle.vin) ) { Ok(_r) => println!("flashed"), Err(e) => println!("error: {e:#?}"), } while loop_prompt() { match try_join!( api.honk_horn(&vehicle.vin), api.flash_lights(&vehicle.vin) ) { Ok(_r) => println!("honked"), Err(e) => println!("error: {e:#?}"), } } } Ok(MaybePostResponse::None) } Commands::SetChargeRate { amps } => api .set_charging_amps( &vehicle.vin, &SetChargingAmps { charging_amps: amps as i64, }, ) .await .map(|v| v.into()), Commands::StartCharge => { api.charge_start(&vehicle.vin).await.map(|v| v.into()) } Commands::StopCharge => { api.charge_stop(&vehicle.vin).await.map(|v| v.into()) } Commands::ToggleBoot => api .actuate_trunk( &vehicle.vin, &ActuateTrunk { which_trunk: teslatte::vehicles::Trunk::rear, }, ) .await .map(|v| v.into()), Commands::ChargerOpen => api .charge_port_door_open(&vehicle.vin) .await .map(|v| v.into()), Commands::ChargerClose => api .charge_port_door_close(&vehicle.vin) .await .map(|v| v.into()), } { Ok(MaybePostResponse::PostResponse(response)) => { println!("post response: {response:#?}") } Ok(_) => {} Err(error) => println!("teslatte error: {error:#?}"), } } } } Err(e) => println!("{}", e.error_string()), } } #[derive(Debug)] pub enum MaybePostResponse { None, PostResponse(teslatte::PostResponse), } impl From for MaybePostResponse { fn from(value: teslatte::PostResponse) -> Self { MaybePostResponse::PostResponse(value) } } async fn get_auth(auth_path: PathBuf) -> Result { let key: AuthInfo = ron::from_str(&std::fs::read_to_string(&auth_path)?)?; let mut api = FleetApi::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, } fn save_key(auth_path: PathBuf, api: &FleetApi) -> 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"); Ok(()) }