global config + single api endpoint for car state
This commit is contained in:
parent
78fdf7a08d
commit
b4e7140335
|
@ -1,7 +1,18 @@
|
|||
use std::{path::PathBuf, sync::OnceLock};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::types::Coords;
|
||||
|
||||
pub(super) static CONFIG_PATH: OnceLock<PathBuf> = OnceLock::new();
|
||||
static CONFIG: OnceLock<Config> = 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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<RwLock<CarState>>,
|
||||
pub pl_state: Option<Arc<RwLock<PlState>>>,
|
||||
pub api_requests: Sender<InterfaceRequest>,
|
||||
|
@ -51,15 +49,7 @@ fn rocket(state: ServerState) -> rocket::Rocket<rocket::Build> {
|
|||
.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<ServerState>) -> Result<Json<bool>, 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<ServerState>) -> Result<Json<ChargeState>, ServerError> {
|
||||
Ok(Json(
|
||||
state
|
||||
.car_state
|
||||
.read()?
|
||||
.charge_state
|
||||
.ok_or(ServerError::NoData)?,
|
||||
))
|
||||
}
|
||||
|
||||
#[get("/climate-state")]
|
||||
async fn climate_state(state: &State<ServerState>) -> Result<Json<ClimateState>, ServerError> {
|
||||
Ok(Json(
|
||||
state
|
||||
.car_state
|
||||
.read()?
|
||||
.climate_state
|
||||
.ok_or(ServerError::NoData)?,
|
||||
))
|
||||
#[get("/car-state")]
|
||||
async fn car_state(state: &State<ServerState>) -> Result<Json<CarState>, ServerError> {
|
||||
Ok(Json(*state.car_state.read()?))
|
||||
}
|
||||
|
||||
#[post("/flash")]
|
||||
|
|
|
@ -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<ChargeState>,
|
||||
pub location_data: Option<LocationData>,
|
||||
|
@ -66,8 +66,8 @@ impl From<teslatte::vehicles::ChargeState> for ChargeState {
|
|||
|
||||
#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
|
||||
pub struct LocationData {
|
||||
pub coords: Coords,
|
||||
pub gps_as_of: DateTime<chrono::Utc>,
|
||||
pub home: bool,
|
||||
}
|
||||
|
||||
impl TryFrom<teslatte::vehicles::DriveState> for LocationData {
|
||||
|
@ -83,7 +83,8 @@ impl TryFrom<teslatte::vehicles::DriveState> 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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,23 +35,23 @@
|
|||
let favicon = document.getElementById("favicon");
|
||||
|
||||
favicon.setAttribute("href", "data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>" + "⏳" + "</text></svg>");
|
||||
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,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>" + get_emoji(charge_state) + "</text></svg>");
|
||||
favicon.setAttribute("href", "data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>" + get_emoji(state.charge_state) + "</text></svg>");
|
||||
|
||||
|
||||
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) {
|
||||
|
|
Loading…
Reference in a new issue