webui shutoff voltage + better start/stop requests
All checks were successful
Build .deb on release / Build-Deb (push) Successful in 1m53s

This commit is contained in:
Alex Janka 2024-01-26 09:04:06 +11:00
parent 4ebd386c05
commit 7f4a880720
7 changed files with 129 additions and 16 deletions

2
Cargo.lock generated
View file

@ -2633,7 +2633,7 @@ dependencies = [
[[package]] [[package]]
name = "tesla-charge-controller" name = "tesla-charge-controller"
version = "1.0.29" version = "1.0.30"
dependencies = [ dependencies = [
"chrono", "chrono",
"clap 4.4.11", "clap 4.4.11",

View file

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

View file

@ -62,6 +62,9 @@ fn rocket(state: ServerState) -> rocket::Rocket<rocket::Build> {
flash, flash,
disable_control, disable_control,
enable_control, enable_control,
set_shutoff,
set_shutoff_time,
shutoff_status,
set_max, set_max,
set_min, set_min,
set_proportional_gain, set_proportional_gain,
@ -125,27 +128,50 @@ async fn flash(state: &State<ServerState>, remote_addr: std::net::IpAddr) {
#[post("/disable-control")] #[post("/disable-control")]
async fn disable_control(state: &State<ServerState>, remote_addr: std::net::IpAddr) { async fn disable_control(state: &State<ServerState>, remote_addr: std::net::IpAddr) {
log::warn!("disabling control: {remote_addr:?}"); log::warn!("disabling control: {remote_addr:?}");
match state match state.tcrc_state.write() {
.tcrc_requests Ok(mut handle) => handle.control_enable = false,
.send(TcrcRequest::DisableAutomaticControl) Err(e) => log::error!("Error disabling control: {e:?}"),
{
Ok(_) => {}
Err(e) => log::error!("Error sending stop control request: {e:?}"),
} }
} }
#[post("/enable-control")] #[post("/enable-control")]
async fn enable_control(state: &State<ServerState>, remote_addr: std::net::IpAddr) { async fn enable_control(state: &State<ServerState>, remote_addr: std::net::IpAddr) {
log::warn!("enabling control: {remote_addr:?}"); log::warn!("enabling control: {remote_addr:?}");
match state match state.tcrc_state.write() {
.tcrc_requests Ok(mut handle) => handle.control_enable = true,
.send(TcrcRequest::EnableAutomaticControl) Err(e) => log::error!("Error enabling control: {e:?}"),
{
Ok(_) => {}
Err(e) => log::error!("Error sending stop control request: {e:?}"),
} }
} }
#[post("/shutoff/voltage/<voltage>")]
async fn set_shutoff(voltage: f64, remote_addr: std::net::IpAddr) {
log::warn!("setting shutoff voltage: {remote_addr:?}");
let voltage = voltage.clamp(40., 60.);
write_to_config().shutoff_voltage = voltage;
}
#[post("/shutoff/time/<time>")]
async fn set_shutoff_time(time: u64, remote_addr: std::net::IpAddr) {
log::warn!("setting shutoff time: {remote_addr:?}");
let time = time.clamp(5, 120);
write_to_config().shutoff_voltage_time_seconds = time;
}
#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
struct ShutoffStatus {
voltage: f64,
time: u64,
}
#[get("/shutoff/status")]
async fn shutoff_status() -> Json<ShutoffStatus> {
let config = access_config();
Json(ShutoffStatus {
voltage: config.shutoff_voltage,
time: config.shutoff_voltage_time_seconds,
})
}
#[post("/set-max/<limit>")] #[post("/set-max/<limit>")]
async fn set_max(limit: i64, remote_addr: std::net::IpAddr) { async fn set_max(limit: i64, remote_addr: std::net::IpAddr) {
log::warn!("setting max: {remote_addr:?}"); log::warn!("setting max: {remote_addr:?}");

View file

@ -20,6 +20,7 @@ pub struct TeslaChargeRateController {
control_enable_gauge: Gauge, control_enable_gauge: Gauge,
} }
#[allow(dead_code)]
pub enum TcrcRequest { pub enum TcrcRequest {
DisableAutomaticControl, DisableAutomaticControl,
EnableAutomaticControl, EnableAutomaticControl,

View file

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

View file

@ -40,6 +40,15 @@ function init_pid() {
}); });
} }
function init_shutoff() {
refresh_shutoff();
document.addEventListener("visibilitychange", () => {
if (!document.hidden) {
refresh_shutoff();
}
});
}
function init_info() { function init_info() {
refresh_interval = register(refresh_info); refresh_interval = register(refresh_info);
@ -138,6 +147,34 @@ function set_derivative() {
} }
} }
function set_shutoff_voltage() {
var set_button = document.getElementById("set-shutoff-voltage");
var number_input = document.getElementById("shutoff-voltage");
if (!isNaN(number_input.value)) {
set_button.disabled = true;
number_input.disabled = true;
fetch(api_url + "/shutoff/voltage/" + number_input.value, { method: "POST" })
.then(async (response) => {
let delayres = await delay(100);
refresh_shutoff();
});
}
}
function set_shutoff_time() {
var set_button = document.getElementById("set-shutoff-time");
var number_input = document.getElementById("shutoff-time");
if (!isNaN(number_input.value)) {
set_button.disabled = true;
number_input.disabled = true;
fetch(api_url + "/shutoff/time/" + number_input.value, { method: "POST" })
.then(async (response) => {
let delayres = await delay(100);
refresh_shutoff();
});
}
}
function set_load_divisor() { function set_load_divisor() {
var set_button = document.getElementById("set-load-divisor"); var set_button = document.getElementById("set-load-divisor");
var number_input = document.getElementById("load-divisor"); var number_input = document.getElementById("load-divisor");
@ -159,7 +196,7 @@ function disable_automatic_control() {
fetch(api_url + "/disable-control", { method: "POST" }) fetch(api_url + "/disable-control", { method: "POST" })
.then(async (response) => { .then(async (response) => {
let delayres = await delay(1000); let delayres = await delay(100);
refresh_buttons(); refresh_buttons();
}); });
} }
@ -171,7 +208,7 @@ function enable_automatic_control() {
document.body.classList.add("loading"); document.body.classList.add("loading");
fetch(api_url + "/enable-control", { method: "POST" }) fetch(api_url + "/enable-control", { method: "POST" })
.then(async (response) => { .then(async (response) => {
let delayres = await delay(1000); let delayres = await delay(100);
refresh_buttons(); refresh_buttons();
}); });
} }
@ -242,6 +279,25 @@ function update_gains(data) {
load_divisor_input.value = data.load_divisor; load_divisor_input.value = data.load_divisor;
} }
function refresh_shutoff() {
fetch(api_url + "/shutoff/status")
.then((response) => response.json())
.then((json) => update_shutoff(json));
}
function update_shutoff(data) {
var voltage_set_button = document.getElementById("set-shutoff-voltage");
var voltage_number_input = document.getElementById("shutoff-voltage");
voltage_set_button.disabled = false;
voltage_number_input.disabled = false;
voltage_number_input.value = data.voltage;
var time_set_button = document.getElementById("set-shutoff-time");
var time_number_input = document.getElementById("shutoff-time");
time_set_button.disabled = false;
time_number_input.disabled = false;
time_number_input.value = data.time;
}
function refresh_buttons() { function refresh_buttons() {
fetch(api_url + "/control-state") fetch(api_url + "/control-state")
.then((response) => response.json()) .then((response) => response.json())

29
webapp/shutoff/index.html Normal file
View file

@ -0,0 +1,29 @@
<!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_shutoff()">
<div class="container">
<p id="pid-control">
<h3>Shutoff voltage:</h3>
<input type="number" id="shutoff-voltage" step="1" min="40" max="60" autocomplete="off" />
<button id="set-shutoff-voltage" onclick="set_shutoff_voltage()">Set shutoff voltage</button>
<br><br>
<input type="number" id="shutoff-time" step="1" autocomplete="off" />
<button id="set-shutoff-time" onclick="set_shutoff_time()">Set shutoff time</button>
</p>
<p>
<a class="outlink" href="/">←Home</a>
</p>
</div>
</body>
</html>