regulator state set button
All checks were successful
Build .deb on release / Build-Deb (push) Successful in 1m53s

This commit is contained in:
Alex Janka 2024-02-13 09:32:35 +11:00
parent 1cde09ff58
commit ccd46b657c
8 changed files with 142 additions and 5 deletions

2
Cargo.lock generated
View file

@ -2538,7 +2538,7 @@ dependencies = [
[[package]]
name = "tesla-charge-controller"
version = "1.1.4"
version = "1.2.0"
dependencies = [
"chrono",
"clap 4.4.11",

View file

@ -1,6 +1,6 @@
[package]
name = "tesla-charge-controller"
version = "1.1.4"
version = "1.2.0"
edition = "2021"
license = "MITNFA"
description = "Controls Tesla charge rate based on solar charge data"

View file

@ -50,6 +50,17 @@ impl From<u8> for RegulatorState {
}
}
impl From<RegulatorState> for u8 {
fn from(value: RegulatorState) -> Self {
match value {
RegulatorState::Boost => 0b00,
RegulatorState::Equalise => 0b01,
RegulatorState::Absorption => 0b10,
RegulatorState::Float => 0b11,
}
}
}
impl Default for RegulatorState {
fn default() -> Self {
Self::Absorption
@ -95,6 +106,7 @@ pub enum PliRequest {
ReadRam(u8),
ReadEeprom(u8),
SyncTime,
SetRegulatorState(RegulatorState),
}
impl Pli {
@ -182,6 +194,11 @@ impl Pli {
"Set time: {now} corresponds to {timestamp} + minutes {min} + seconds {sec}"
);
}
PliRequest::SetRegulatorState(state) => {
log::warn!("Setting regulator state to {state:?}");
self.write_ram(PlRamAddress::Rstate, state.into())
.some_or_print_with("setting regulator state");
}
}
}

View file

@ -34,6 +34,8 @@ pub enum ServerError {
#[error("no data")]
// 503
NoData,
#[error("invalid parameters")]
InvalidParameters,
#[error("prometheus")]
Prometheus(#[from] prometheus::Error),
}
@ -49,6 +51,7 @@ impl<'a> Responder<'a, 'a> for ServerError {
Err(match self {
ServerError::Lock => rocket::http::Status::InternalServerError,
ServerError::NoData => rocket::http::Status::ServiceUnavailable,
Self::InvalidParameters => rocket::http::Status::BadRequest,
ServerError::Prometheus(_) => rocket::http::Status::InternalServerError,
})
}

View file

@ -3,6 +3,7 @@ use std::sync::{Arc, RwLock};
use rocket::{
fairing::{Fairing, Info, Kind},
http::Header,
request::FromParam,
serde::json::Json,
Request, Response, State,
};
@ -11,9 +12,9 @@ use tokio::sync::mpsc::UnboundedSender;
use crate::{
api_interface::InterfaceRequest,
charge_controllers::pl::{PlState, PliRequest},
charge_controllers::pl::{PlState, PliRequest, RegulatorState},
config::{access_config, write_to_config},
errors::ServerError,
errors::{PrintErrors, ServerError},
tesla_charge_rate::{TcrcRequest, TcrcState},
types::CarState,
};
@ -75,7 +76,8 @@ fn rocket(state: ServerState) -> rocket::Rocket<rocket::Build> {
metrics,
sync_time,
read_ram,
read_eeprom
read_eeprom,
set_regulator_state
],
)
}
@ -250,6 +252,37 @@ async fn regulator_state(state: &State<ServerState>) -> Result<Json<PlState>, Se
.and_then(|v| Ok(Json(*(v.read()?))))
}
#[post("/set-regulator-state/<regulator_state>")]
async fn set_regulator_state(
state: &State<ServerState>,
regulator_state: Option<RegulatorState>,
) -> Result<(), ServerError> {
if let Some(regulator_state) = regulator_state {
log::info!("Requesting regulator state set to {regulator_state:?}");
state
.pli_requests
.send(PliRequest::SetRegulatorState(regulator_state))
.some_or_print_with("requesting new pl regulator state");
Ok(())
} else {
Err(ServerError::InvalidParameters)
}
}
impl FromParam<'_> for RegulatorState {
type Error = ();
fn from_param(param: &'_ str) -> Result<Self, Self::Error> {
match param.to_lowercase().as_str() {
"boost" => Ok(Self::Boost),
"equalise" => Ok(Self::Equalise),
"absorption" => Ok(Self::Absorption),
"float" => Ok(Self::Float),
_ => Err(()),
}
}
}
pub struct Cors;
#[rocket::async_trait]

View file

@ -33,6 +33,7 @@
<p>
<a class="outlink" href="/grafana">Grafana→</a>
<a class="outlink" href="/info">Car state info→</a>
<a class="outlink" href="/regulator">Regulator control→</a>
<a class="outlink" href="/pid">PID control variables→</a>
<a class="outlink" href="/shutoff">Shutoff voltage control→</a>
</p>

View file

@ -0,0 +1,33 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Tesla Charge Control</title>
<link id="favicon" rel="icon"
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>">
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
</head>
<body onload="init_regulator()">
<div class="container">
<p id="pid-control">
<h3>Regulator state:</h3>
<div id="current-state"></div>
<br>
<select name="regstate" id="regstate">
<option value="boost">Boost</option>
<option value="equalise">Equalise</option>
<option value="absorption">Absorption</option>
<option value="float">Float</option>
</select>
<button id="set-regulator-state" onclick="set_regulator_state()">Set regulator state</button>
</p>
<p>
<a class="outlink" href="/">←Home</a>
</p>
</div>
</body>
</html>

View file

@ -63,6 +63,21 @@ function init_info() {
});
}
function init_regulator() {
refresh_interval = register(refresh_regulator_state);
refresh_regulator_state();
document.addEventListener("visibilitychange", () => {
if (document.hidden) {
clearInterval(refresh_interval);
} else {
refresh_regulator_state();
refresh_interval = register(refresh_regulator_state);
}
});
}
function register(func) {
return setInterval(func, 5000);
}
@ -175,6 +190,21 @@ function set_shutoff_time() {
}
}
function set_regulator_state() {
var set_button = document.getElementById("set-regulator-state");
var state_input = document.getElementById("regstate");
set_button.disabled = true;
state_input.disabled = true;
fetch(api_url + "/set-regulator-state/" + state_input.value, { method: "POST" })
.then(async (response) => {
let delayres = await delay(300);
refresh_regulator_state();
});
}
function set_load_divisor() {
var set_button = document.getElementById("set-load-divisor");
var number_input = document.getElementById("load-divisor");
@ -285,6 +315,18 @@ function refresh_shutoff() {
.then((json) => update_shutoff(json));
}
function refresh_regulator_state() {
var set_button = document.getElementById("set-regulator-state");
var state_input = document.getElementById("regstate");
set_button.disabled = false;
state_input.disabled = false;
fetch(api_url + "/regulator-state")
.then((response) => { response.json() })
.then((json) => update_regulator_state(json));
}
function update_shutoff(data) {
var voltage_set_button = document.getElementById("set-shutoff-voltage");
var voltage_number_input = document.getElementById("shutoff-voltage");
@ -298,6 +340,14 @@ function update_shutoff(data) {
time_number_input.value = data.time;
}
function update_regulator_state(state) {
console.log(state);
if (state == null) { var cur = "Unknown" } else { var cur = state.regulator_state }
var current_state = document.getElementById("current-state");
current_state.textContent = "Current state: " + cur;
}
function refresh_buttons() {
fetch(api_url + "/control-state")
.then((response) => response.json())