Can set forced state to relays

This commit is contained in:
2025-10-29 15:49:05 +01:00
parent 58eceeda2d
commit 3625188706
4 changed files with 136 additions and 14 deletions

View File

@@ -8,7 +8,7 @@ use crate::energy::consumption;
use crate::energy::consumption::EnergyConsumption;
use crate::energy::consumption_cache::ConsumptionCache;
use crate::energy::consumption_history_file::ConsumptionHistoryFile;
use crate::energy::engine::EnergyEngine;
use crate::energy::engine::{EnergyEngine, RelayForcedState};
use crate::utils::time_utils::time_secs;
use actix::prelude::*;
use openssl::x509::X509Req;
@@ -328,6 +328,19 @@ impl Handler<UpdateDeviceRelay> for EnergyActor {
}
}
#[derive(Message)]
#[rtype(result = "anyhow::Result<()>")]
pub struct SetRelayForcedState(pub DeviceRelayID, pub RelayForcedState);
impl Handler<SetRelayForcedState> for EnergyActor {
type Result = anyhow::Result<()>;
fn handle(&mut self, msg: SetRelayForcedState, _ctx: &mut Context<Self>) -> Self::Result {
self.engine.relay_state(msg.0).set_forced(msg.1);
Ok(())
}
}
/// Delete a device relay
#[derive(Message)]
#[rtype(result = "anyhow::Result<()>")]
@@ -408,6 +421,7 @@ pub struct ResRelayState {
pub id: DeviceRelayID,
pub on: bool,
pub r#for: usize,
pub forced_state: RelayForcedState,
}
/// Get the state of all relays
@@ -427,6 +441,7 @@ impl Handler<GetAllRelaysState> for EnergyActor {
id: d.id,
on: state.is_on(),
r#for: state.state_for(),
forced_state: state.actual_forced_state(),
})
}

View File

@@ -25,19 +25,56 @@ impl DeviceState {
}
}
#[derive(Default, Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(tag = "type")]
pub enum RelayForcedState {
#[default]
None,
Off {
until: u64,
},
On {
until: u64,
},
}
#[derive(Default, Clone)]
pub struct RelayState {
on: bool,
since: usize,
forced_state: RelayForcedState,
}
impl RelayState {
/// Get actual forced state (returns None if state is expired)
pub fn actual_forced_state(&self) -> RelayForcedState {
match self.forced_state {
RelayForcedState::Off { until } if until > time_secs() => {
RelayForcedState::Off { until }
}
RelayForcedState::On { until } if until > time_secs() => RelayForcedState::On { until },
_ => RelayForcedState::None,
}
}
pub fn is_on(&self) -> bool {
self.on
let forced_state = self.actual_forced_state();
(self.on || matches!(forced_state, RelayForcedState::On { .. }))
&& !matches!(forced_state, RelayForcedState::Off { .. })
}
fn is_off(&self) -> bool {
!self.on
!self.is_on()
}
/// Check if relay state is enforced
pub fn is_forced(&self) -> bool {
self.actual_forced_state() != RelayForcedState::None
}
pub fn set_forced(&mut self, s: RelayForcedState) {
self.since = time_secs() as usize;
self.forced_state = s;
}
pub fn state_for(&self) -> usize {
@@ -146,7 +183,11 @@ impl EnergyEngine {
r.name,
r.consumption,
format!("{} / {}", r.minimal_downtime, r.minimal_uptime),
status.is_on().to_string(),
status.is_on().to_string()
+ match status.is_forced() {
true => " (Forced)",
false => "",
},
status.since,
match dev_online {
true => "Online",
@@ -192,19 +233,28 @@ impl EnergyEngine {
let mut new_relays_state = self.relays_state.clone();
// Forcefully turn off relays that belongs to offline devices
// Forcefully turn off disabled relays
for d in devices {
if !self.device_state(&d.id).is_online() {
for r in &d.relays {
for r in &d.relays {
if !r.enabled || !d.enabled {
new_relays_state.get_mut(&r.id).unwrap().on = false;
}
}
}
// Forcefully turn off disabled relays
// Apply forced relays state
for d in devices {
for r in &d.relays {
if !r.enabled || !d.enabled {
if self.relay_state(r.id).is_forced() {
new_relays_state.get_mut(&r.id).unwrap().on = self.relay_state(r.id).is_on();
}
}
}
// Forcefully turn off relays that belongs to offline devices
for d in devices {
if !self.device_state(&d.id).is_online() {
for r in &d.relays {
new_relays_state.get_mut(&r.id).unwrap().on = false;
}
}
@@ -216,7 +266,9 @@ impl EnergyEngine {
for d in devices {
for r in &d.relays {
if new_relays_state.get(&r.id).unwrap().is_off() {
if new_relays_state.get(&r.id).unwrap().is_off()
|| new_relays_state.get(&r.id).unwrap().is_forced()
{
continue;
}
@@ -240,7 +292,7 @@ impl EnergyEngine {
for d in devices {
for r in &d.relays {
let state = new_relays_state.get(&r.id).unwrap();
if state.is_off() {
if state.is_off() || state.is_forced() {
continue;
}
@@ -271,7 +323,9 @@ impl EnergyEngine {
continue;
}
if new_relays_state.get(&r.id).unwrap().is_on() {
if new_relays_state.get(&r.id).unwrap().is_on()
|| new_relays_state.get(&r.id).unwrap().is_forced()
{
continue;
}
@@ -298,7 +352,7 @@ impl EnergyEngine {
}
}
// Order relays
// Order relays to select the ones with the most elevated priorities
let mut ordered_relays = devices
.iter()
.filter(|d| self.device_state(&d.id).is_online() && d.enabled)
@@ -308,10 +362,13 @@ impl EnergyEngine {
ordered_relays.sort_by_key(|r| r.priority);
ordered_relays.reverse();
// Select relays to start, starting with those with highest priorities
loop {
let mut changed = false;
for relay in &ordered_relays {
if new_relays_state.get(&relay.id).unwrap().is_on() {
if new_relays_state.get(&relay.id).unwrap().is_on()
|| new_relays_state.get(&relay.id).unwrap().is_forced()
{
continue;
}