regulator state set button
All checks were successful
Build .deb on release / Build-Deb (push) Successful in 1m53s
All checks were successful
Build .deb on release / Build-Deb (push) Successful in 1m53s
This commit is contained in:
parent
1cde09ff58
commit
ccd46b657c
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -2538,7 +2538,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tesla-charge-controller"
|
name = "tesla-charge-controller"
|
||||||
version = "1.1.4"
|
version = "1.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap 4.4.11",
|
"clap 4.4.11",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "tesla-charge-controller"
|
name = "tesla-charge-controller"
|
||||||
version = "1.1.4"
|
version = "1.2.0"
|
||||||
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"
|
||||||
|
|
|
@ -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 {
|
impl Default for RegulatorState {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Absorption
|
Self::Absorption
|
||||||
|
@ -95,6 +106,7 @@ pub enum PliRequest {
|
||||||
ReadRam(u8),
|
ReadRam(u8),
|
||||||
ReadEeprom(u8),
|
ReadEeprom(u8),
|
||||||
SyncTime,
|
SyncTime,
|
||||||
|
SetRegulatorState(RegulatorState),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pli {
|
impl Pli {
|
||||||
|
@ -182,6 +194,11 @@ impl Pli {
|
||||||
"Set time: {now} corresponds to {timestamp} + minutes {min} + seconds {sec}"
|
"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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,8 @@ pub enum ServerError {
|
||||||
#[error("no data")]
|
#[error("no data")]
|
||||||
// 503
|
// 503
|
||||||
NoData,
|
NoData,
|
||||||
|
#[error("invalid parameters")]
|
||||||
|
InvalidParameters,
|
||||||
#[error("prometheus")]
|
#[error("prometheus")]
|
||||||
Prometheus(#[from] prometheus::Error),
|
Prometheus(#[from] prometheus::Error),
|
||||||
}
|
}
|
||||||
|
@ -49,6 +51,7 @@ impl<'a> Responder<'a, 'a> for ServerError {
|
||||||
Err(match self {
|
Err(match self {
|
||||||
ServerError::Lock => rocket::http::Status::InternalServerError,
|
ServerError::Lock => rocket::http::Status::InternalServerError,
|
||||||
ServerError::NoData => rocket::http::Status::ServiceUnavailable,
|
ServerError::NoData => rocket::http::Status::ServiceUnavailable,
|
||||||
|
Self::InvalidParameters => rocket::http::Status::BadRequest,
|
||||||
ServerError::Prometheus(_) => rocket::http::Status::InternalServerError,
|
ServerError::Prometheus(_) => rocket::http::Status::InternalServerError,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ use std::sync::{Arc, RwLock};
|
||||||
use rocket::{
|
use rocket::{
|
||||||
fairing::{Fairing, Info, Kind},
|
fairing::{Fairing, Info, Kind},
|
||||||
http::Header,
|
http::Header,
|
||||||
|
request::FromParam,
|
||||||
serde::json::Json,
|
serde::json::Json,
|
||||||
Request, Response, State,
|
Request, Response, State,
|
||||||
};
|
};
|
||||||
|
@ -11,9 +12,9 @@ use tokio::sync::mpsc::UnboundedSender;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api_interface::InterfaceRequest,
|
api_interface::InterfaceRequest,
|
||||||
charge_controllers::pl::{PlState, PliRequest},
|
charge_controllers::pl::{PlState, PliRequest, RegulatorState},
|
||||||
config::{access_config, write_to_config},
|
config::{access_config, write_to_config},
|
||||||
errors::ServerError,
|
errors::{PrintErrors, ServerError},
|
||||||
tesla_charge_rate::{TcrcRequest, TcrcState},
|
tesla_charge_rate::{TcrcRequest, TcrcState},
|
||||||
types::CarState,
|
types::CarState,
|
||||||
};
|
};
|
||||||
|
@ -75,7 +76,8 @@ fn rocket(state: ServerState) -> rocket::Rocket<rocket::Build> {
|
||||||
metrics,
|
metrics,
|
||||||
sync_time,
|
sync_time,
|
||||||
read_ram,
|
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()?))))
|
.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;
|
pub struct Cors;
|
||||||
|
|
||||||
#[rocket::async_trait]
|
#[rocket::async_trait]
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
<p>
|
<p>
|
||||||
<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="/regulator">Regulator control→</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>
|
<a class="outlink" href="/shutoff">Shutoff voltage control→</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
33
webapp/regulator/index.html
Normal file
33
webapp/regulator/index.html
Normal 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>
|
|
@ -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) {
|
function register(func) {
|
||||||
return setInterval(func, 5000);
|
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() {
|
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");
|
||||||
|
@ -285,6 +315,18 @@ function refresh_shutoff() {
|
||||||
.then((json) => update_shutoff(json));
|
.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) {
|
function update_shutoff(data) {
|
||||||
var voltage_set_button = document.getElementById("set-shutoff-voltage");
|
var voltage_set_button = document.getElementById("set-shutoff-voltage");
|
||||||
var voltage_number_input = document.getElementById("shutoff-voltage");
|
var voltage_number_input = document.getElementById("shutoff-voltage");
|
||||||
|
@ -298,6 +340,14 @@ function update_shutoff(data) {
|
||||||
time_number_input.value = data.time;
|
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() {
|
function refresh_buttons() {
|
||||||
fetch(api_url + "/control-state")
|
fetch(api_url + "/control-state")
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
|
|
Loading…
Reference in a new issue