use clap::{Parser, Subcommand}; use serde::{Deserialize, Serialize}; use std::{io::BufRead, path::PathBuf}; use teslatte::{ auth::{AccessToken, RefreshToken}, 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, }, /// Print the default config file GenerateConfig, } #[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 args.command { Commands::GenerateConfig => { println!( "{}", ron::ser::to_string_pretty(&Config::default(), Default::default()).unwrap() ); } Commands::Watch { flash } => match get_auth(auth_path).await { Ok(api) => { 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() { 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:#?}"), } } } } } } Err(e) => println!("{}", e.error_string()), }, } } 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(()) }