From b4e71403350ea27479ad992ec11a4d810bd310a0 Mon Sep 17 00:00:00 2001 From: Alex Janka Date: Sun, 14 Jan 2024 09:40:34 +1100 Subject: [PATCH] global config + single api endpoint for car state --- src/config.rs | 11 +++++++++++ src/main.rs | 9 ++++----- src/server/mod.rs | 39 ++++++--------------------------------- src/types.rs | 9 +++++---- webapp/index.html | 27 ++++++++++++--------------- 5 files changed, 38 insertions(+), 57 deletions(-) diff --git a/src/config.rs b/src/config.rs index 565d73b..f3e6231 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,7 +1,18 @@ +use std::{path::PathBuf, sync::OnceLock}; + use serde::{Deserialize, Serialize}; use crate::types::Coords; +pub(super) static CONFIG_PATH: OnceLock = OnceLock::new(); +static CONFIG: OnceLock = OnceLock::new(); + +pub fn access_config<'a>() -> &'a Config { + CONFIG.get_or_init(|| { + ron::from_str(&std::fs::read_to_string(CONFIG_PATH.get().unwrap()).unwrap()).unwrap() + }) +} + #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Config { pub tesla_watch_interval_seconds: u64, diff --git a/src/main.rs b/src/main.rs index 02c387f..fdeae21 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ extern crate rocket; use api_interface::TeslaInterface; use charge_controllers::pl::Pli; use clap::{Parser, Subcommand}; +use config::{access_config, CONFIG_PATH}; use errors::PrintErrors; use std::path::PathBuf; @@ -41,7 +42,8 @@ async fn main() { env_logger::init(); let auth_path = args.config_dir.join("auth"); - let config_path = args.config_dir.join("config"); + + let _ = CONFIG_PATH.set(args.config_dir.join("config")); let _recorder = metrics_prometheus::install(); @@ -54,9 +56,7 @@ async fn main() { } Commands::Watch => { if let Some(mut interface) = TeslaInterface::load(auth_path).await.some_or_print() { - let config: Config = - ron::from_str(&std::fs::read_to_string(&config_path).unwrap()).unwrap(); - + let config = access_config(); // build the channel that takes messages from the webserver thread to the api thread let (api_requests, api_receiver) = async_channel::unbounded(); // and to the pli thread @@ -94,7 +94,6 @@ async fn main() { }; let server_handle = server::launch_server(server::ServerState { - config: config.clone(), car_state: interface.state.clone(), pl_state, api_requests, diff --git a/src/server/mod.rs b/src/server/mod.rs index 916c2f4..fd455f0 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -11,9 +11,8 @@ use rocket::{ use crate::{ api_interface::InterfaceRequest, charge_controllers::pl::{PlState, PliRequest}, - config::Config, errors::ServerError, - types::{CarState, ChargeState, ClimateState}, + types::CarState, }; use self::static_handler::UiStatic; @@ -21,7 +20,6 @@ use self::static_handler::UiStatic; mod static_handler; pub struct ServerState { - pub config: Config, pub car_state: Arc>, pub pl_state: Option>>, pub api_requests: Sender, @@ -51,15 +49,7 @@ fn rocket(state: ServerState) -> rocket::Rocket { .mount("/", fileserver) .mount( "/", - routes![ - home, - charge_state, - flash, - climate_state, - metrics, - read_ram, - regulator_state - ], + routes![home, car_state, regulator_state, flash, metrics, read_ram,], ) } @@ -70,29 +60,12 @@ async fn home(state: &State) -> Result, ServerError> { .read()? .location_data .ok_or(ServerError::NoData)?; - Ok(Json(location_data.coords.overlaps(&state.config.coords))) + Ok(Json(location_data.home)) } -#[get("/charge-state")] -async fn charge_state(state: &State) -> Result, ServerError> { - Ok(Json( - state - .car_state - .read()? - .charge_state - .ok_or(ServerError::NoData)?, - )) -} - -#[get("/climate-state")] -async fn climate_state(state: &State) -> Result, ServerError> { - Ok(Json( - state - .car_state - .read()? - .climate_state - .ok_or(ServerError::NoData)?, - )) +#[get("/car-state")] +async fn car_state(state: &State) -> Result, ServerError> { + Ok(Json(*state.car_state.read()?)) } #[post("/flash")] diff --git a/src/types.rs b/src/types.rs index f8e1eb3..a83e7c2 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,9 +1,9 @@ use chrono::DateTime; use serde::{Deserialize, Serialize}; -use crate::errors::TeslaStateParseError; +use crate::{config::access_config, errors::TeslaStateParseError}; -#[derive(Default)] +#[derive(Default, Clone, Copy, Serialize, Deserialize, Debug)] pub struct CarState { pub charge_state: Option, pub location_data: Option, @@ -66,8 +66,8 @@ impl From for ChargeState { #[derive(Clone, Copy, Serialize, Deserialize, Debug)] pub struct LocationData { - pub coords: Coords, pub gps_as_of: DateTime, + pub home: bool, } impl TryFrom for LocationData { @@ -83,7 +83,8 @@ impl TryFrom for LocationData { latitude: value.latitude.ok_or(TeslaStateParseError::NoValue)?, longitude: value.longitude.ok_or(TeslaStateParseError::NoValue)?, }; - Ok(Self { coords, gps_as_of }) + let home = coords.overlaps(&access_config().coords); + Ok(Self { gps_as_of, home }) } } diff --git a/webapp/index.html b/webapp/index.html index ba9ddb2..e800e40 100644 --- a/webapp/index.html +++ b/webapp/index.html @@ -35,23 +35,23 @@ let favicon = document.getElementById("favicon"); favicon.setAttribute("href", "data:image/svg+xml," + "⏳" + ""); - fetch(api_url + "/charge-state") + fetch(api_url + "/car-state") .then((response) => response.json()) - .then((json) => update_charge_state(json)); + .then((json) => update_state(json)); + - fetch(api_url + "/home").then((response) => response.json()).then((response) => update_home(response)); } - function update_charge_state(charge_state) { + function update_state(state) { let favicon = document.getElementById("favicon"); - favicon.setAttribute("href", "data:image/svg+xml," + get_emoji(charge_state) + ""); + favicon.setAttribute("href", "data:image/svg+xml," + get_emoji(state.charge_state) + ""); var info_div = document.getElementById("info"); while (info_div.childElementCount > 0) { info_div.removeChild(info_div.firstChild) } - var arr = ["Battery " + charge_state.battery_level + "%", "Range: " + (charge_state.battery_range * 1.60934).toFixed(1) + "km", "Charging at " + charge_state.charge_rate.toFixed(1) + " amps", "Charging until battery at " + charge_state.charge_limit_soc + "%", + var arr = ["Battery " + state.charge_state.battery_level + "%", "Range: " + (state.charge_state.battery_range * 1.60934).toFixed(1) + "km", "Charging at " + state.charge_state.charge_rate.toFixed(1) + " amps", "Charging until battery at " + state.charge_state.charge_limit_soc + "%", ]; for (line in arr) { el = document.createElement('p'); @@ -64,22 +64,19 @@ el.appendChild(document.createTextNode("Full charge state:")); state_json = document.createElement('pre'); - state_json.appendChild(document.createTextNode(JSON.stringify(charge_state, null, '\t'))); + state_json.appendChild(document.createTextNode(JSON.stringify(state.charge_state, null, '\t'))); el.appendChild(state_json); info_div.appendChild(el); - } - function update_home(response) { - var info_div = document.getElementById("info"); - el = document.createElement('p'); - if (response) { - el.appendChild(document.createTextNode("At home")); + home = document.createElement('p'); + if (state.location_data.home) { + home.appendChild(document.createTextNode("At home")); } else { - el.appendChild(document.createTextNode("Not home")); + home.appendChild(document.createTextNode("Not home")); } - info_div.appendChild(el); + info_div.appendChild(home); } function get_emoji(charge_state) {