tesla-charge-controller/src/tesla_charge_rate.rs

118 lines
4 KiB
Rust
Raw Normal View History

2024-01-16 11:00:11 +11:00
use std::sync::{Arc, RwLock};
use metrics::{describe_gauge, gauge, Gauge};
use serde::{Deserialize, Serialize};
use teslatte::vehicles::ChargingState;
use crate::{
api_interface::InterfaceRequest,
charge_controllers::pl::PlState,
config::access_config,
types::{CarState, ChargeState},
};
pub struct TeslaChargeRateController {
pub car_state: Arc<RwLock<CarState>>,
pub pl_state: Option<Arc<RwLock<PlState>>>,
pub tcrc_state: Arc<RwLock<TcrcState>>,
control_enable_gauge: Gauge,
}
pub enum TcrcRequest {
DisableAutomaticControl,
EnableAutomaticControl,
}
2024-01-16 11:10:20 +11:00
#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
2024-01-16 11:00:11 +11:00
pub struct TcrcState {
pub control_enable: bool,
}
2024-01-16 11:10:20 +11:00
impl Default for TcrcState {
fn default() -> Self {
Self {
control_enable: true,
}
}
}
2024-01-16 11:00:11 +11:00
impl TeslaChargeRateController {
pub fn new(car_state: Arc<RwLock<CarState>>, pl_state: Option<Arc<RwLock<PlState>>>) -> Self {
describe_gauge!("tcrc_control_enable", "Enable Tesla charge rate control");
Self {
car_state,
pl_state,
tcrc_state: Default::default(),
control_enable_gauge: gauge!("tcrc_control_enable"),
}
}
pub fn control_charge_rate(&mut self) -> Option<InterfaceRequest> {
if let Some(pl_state) = self.pl_state.as_ref().and_then(|v| v.read().ok()) {
if let Ok(car_state) = self.car_state.read() {
if let Some(charge_state) = car_state.charge_state {
// check if we're charging at home
if charge_state.charging_state == ChargingState::Charging
&& car_state.location_data.is_some_and(|v| v.home)
{
// automatic control or not, check if we're below shutoff voltage
if pl_state.battery_voltage < access_config().shutoff_voltage {
return Some(InterfaceRequest::StopCharge);
} else if self.tcrc_state.read().is_ok_and(|v| v.control_enable) {
return get_control(&pl_state, &charge_state);
}
} else if charge_state.charging_state == ChargingState::Disconnected {
// only disable automatic control until the next time we're unplugged
self.tcrc_state
.write()
.expect("failed to write to tcrc state")
.control_enable = true;
self.control_enable_gauge.set(1.);
}
}
}
}
None
}
pub fn process_request(&mut self, message: TcrcRequest) {
match message {
TcrcRequest::DisableAutomaticControl => {
self.tcrc_state
.write()
.expect("failed to write to tcrc state")
.control_enable = false;
self.control_enable_gauge.set(0.);
}
TcrcRequest::EnableAutomaticControl => {
self.tcrc_state
.write()
.expect("failed to write to tcrc state")
.control_enable = true;
self.control_enable_gauge.set(1.);
}
}
}
}
fn get_control(pl_state: &PlState, charge_state: &ChargeState) -> Option<InterfaceRequest> {
let config = access_config();
if pl_state.duty_cycle > config.duty_cycle_too_high {
let new_rate = charge_state.charge_amps - 1;
return if new_rate >= config.min_rate {
Some(InterfaceRequest::SetChargeRate(new_rate))
} else {
None
};
} else if pl_state.duty_cycle < config.duty_cycle_too_low {
let new_rate = charge_state.charge_amps + 1;
return if new_rate <= config.max_rate {
Some(InterfaceRequest::SetChargeRate(new_rate))
} else {
None
};
}
None
}