#![feature(never_type)] #[macro_use] extern crate rocket; use api_interface::TeslaInterface; use clap::{Parser, Subcommand}; use std::path::PathBuf; use crate::config::Config; mod api_interface; mod config; mod errors; mod server; mod types; #[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, /// Print the default config file GenerateConfig, } #[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 => match TeslaInterface::load(auth_path).await { Ok(mut interface) => { let config: Config = ron::from_str(&std::fs::read_to_string(&config_path).unwrap()).unwrap(); let (api_requests, receiver) = async_channel::unbounded(); let server_handle = server::launch_server(server::ServerState { config, state: interface.state.clone(), api_requests, }); tokio::task::spawn(async move { let mut interval = tokio::time::interval(std::time::Duration::from_secs(120)); loop { tokio::select! { _ = interval.tick() => interface.refresh().await, message = receiver.recv() => match message { Ok(message) => interface.process_request(message).await, Err(e) => eprintln!("Error on receive channel: {e:#?}") } } } }); server_handle.await; } Err(e) => println!("{}", e.error_string()), }, } }