Display relay consumption history
This commit is contained in:
		@@ -115,6 +115,11 @@ impl DevicesList {
 | 
				
			|||||||
        self.0.clone().into_values().collect()
 | 
					        self.0.clone().into_values().collect()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Get a reference on the full list of devices
 | 
				
			||||||
 | 
					    pub fn full_list_ref(&self) -> Vec<&Device> {
 | 
				
			||||||
 | 
					        self.0.values().collect()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Get the information about a single device
 | 
					    /// Get the information about a single device
 | 
				
			||||||
    pub fn get_single(&self, id: &DeviceId) -> Option<Device> {
 | 
					    pub fn get_single(&self, id: &DeviceId) -> Option<Device> {
 | 
				
			||||||
        self.0.get(id).cloned()
 | 
					        self.0.get(id).cloned()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -53,7 +53,7 @@ impl EnergyActor {
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
        self.consumption_cache.add_value(latest_consumption);
 | 
					        self.consumption_cache.add_value(latest_consumption);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let devices_list = self.devices.full_list();
 | 
					        let devices_list = self.devices.full_list_ref();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut history =
 | 
					        let mut history =
 | 
				
			||||||
            ConsumptionHistoryFile::open(time_secs(), ConsumptionHistoryType::GridConsumption)?;
 | 
					            ConsumptionHistoryFile::open(time_secs(), ConsumptionHistoryType::GridConsumption)?;
 | 
				
			||||||
@@ -119,7 +119,21 @@ impl Handler<GetCurrConsumption> for EnergyActor {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Get current consumption
 | 
					/// Get relays consumption
 | 
				
			||||||
 | 
					#[derive(Message)]
 | 
				
			||||||
 | 
					#[rtype(result = "usize")]
 | 
				
			||||||
 | 
					pub struct RelaysConsumption;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Handler<RelaysConsumption> for EnergyActor {
 | 
				
			||||||
 | 
					    type Result = usize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn handle(&mut self, _msg: RelaysConsumption, _ctx: &mut Context<Self>) -> Self::Result {
 | 
				
			||||||
 | 
					        self.engine
 | 
				
			||||||
 | 
					            .sum_relays_consumption(&self.devices.full_list_ref())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Check if device exists
 | 
				
			||||||
#[derive(Message)]
 | 
					#[derive(Message)]
 | 
				
			||||||
#[rtype(result = "bool")]
 | 
					#[rtype(result = "bool")]
 | 
				
			||||||
pub struct CheckDeviceExists(pub DeviceId);
 | 
					pub struct CheckDeviceExists(pub DeviceId);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -55,7 +55,7 @@ pub struct EnergyEngine {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
impl DeviceRelay {
 | 
					impl DeviceRelay {
 | 
				
			||||||
    // Note : this function is not recursive
 | 
					    // Note : this function is not recursive
 | 
				
			||||||
    fn has_running_dependencies(&self, s: &RelaysState, devices: &[Device]) -> bool {
 | 
					    fn has_running_dependencies(&self, s: &RelaysState, devices: &[&Device]) -> bool {
 | 
				
			||||||
        for d in devices {
 | 
					        for d in devices {
 | 
				
			||||||
            for r in &d.relays {
 | 
					            for r in &d.relays {
 | 
				
			||||||
                if r.depends_on.contains(&self.id) && s.get(&r.id).unwrap().is_on() {
 | 
					                if r.depends_on.contains(&self.id) && s.get(&r.id).unwrap().is_on() {
 | 
				
			||||||
@@ -72,7 +72,7 @@ impl DeviceRelay {
 | 
				
			|||||||
        self.depends_on.iter().any(|id| s.get(id).unwrap().is_off())
 | 
					        self.depends_on.iter().any(|id| s.get(id).unwrap().is_off())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn is_having_conflict(&self, s: &RelaysState, devices: &[Device]) -> bool {
 | 
					    fn is_having_conflict(&self, s: &RelaysState, devices: &[&Device]) -> bool {
 | 
				
			||||||
        if self
 | 
					        if self
 | 
				
			||||||
            .conflicts_with
 | 
					            .conflicts_with
 | 
				
			||||||
            .iter()
 | 
					            .iter()
 | 
				
			||||||
@@ -94,7 +94,7 @@ impl DeviceRelay {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn sum_relays_consumption(state: &RelaysState, devices: &[Device]) -> usize {
 | 
					fn sum_relays_consumption(state: &RelaysState, devices: &[&Device]) -> usize {
 | 
				
			||||||
    let mut consumption = 0;
 | 
					    let mut consumption = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for d in devices {
 | 
					    for d in devices {
 | 
				
			||||||
@@ -119,11 +119,11 @@ impl EnergyEngine {
 | 
				
			|||||||
        self.relays_state.get_mut(&relay_id).unwrap()
 | 
					        self.relays_state.get_mut(&relay_id).unwrap()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn sum_relays_consumption(&self, devices: &[Device]) -> usize {
 | 
					    pub fn sum_relays_consumption(&self, devices: &[&Device]) -> usize {
 | 
				
			||||||
        sum_relays_consumption(&self.relays_state, devices)
 | 
					        sum_relays_consumption(&self.relays_state, devices)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn print_summary(&mut self, curr_consumption: EnergyConsumption, devices: &[Device]) {
 | 
					    fn print_summary(&mut self, curr_consumption: EnergyConsumption, devices: &[&Device]) {
 | 
				
			||||||
        log::info!("Current consumption: {curr_consumption}");
 | 
					        log::info!("Current consumption: {curr_consumption}");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut table = Table::new();
 | 
					        let mut table = Table::new();
 | 
				
			||||||
@@ -172,13 +172,13 @@ impl EnergyEngine {
 | 
				
			|||||||
    pub fn estimated_consumption_without_relays(
 | 
					    pub fn estimated_consumption_without_relays(
 | 
				
			||||||
        &self,
 | 
					        &self,
 | 
				
			||||||
        curr_consumption: EnergyConsumption,
 | 
					        curr_consumption: EnergyConsumption,
 | 
				
			||||||
        devices: &[Device],
 | 
					        devices: &[&Device],
 | 
				
			||||||
    ) -> EnergyConsumption {
 | 
					    ) -> EnergyConsumption {
 | 
				
			||||||
        curr_consumption - self.sum_relays_consumption(devices) as i32
 | 
					        curr_consumption - self.sum_relays_consumption(devices) as i32
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Refresh energy engine; this method shall never fail !
 | 
					    /// Refresh energy engine; this method shall never fail !
 | 
				
			||||||
    pub fn refresh(&mut self, curr_consumption: EnergyConsumption, devices: &[Device]) {
 | 
					    pub fn refresh(&mut self, curr_consumption: EnergyConsumption, devices: &[&Device]) {
 | 
				
			||||||
        let base_production = self.estimated_consumption_without_relays(curr_consumption, devices);
 | 
					        let base_production = self.estimated_consumption_without_relays(curr_consumption, devices);
 | 
				
			||||||
        log::info!("Estimated base production: {base_production}");
 | 
					        log::info!("Estimated base production: {base_production}");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -366,7 +366,7 @@ impl EnergyEngine {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Save relays state to disk
 | 
					    /// Save relays state to disk
 | 
				
			||||||
    pub fn persist_relays_state(&mut self, devices: &[Device]) -> anyhow::Result<()> {
 | 
					    pub fn persist_relays_state(&mut self, devices: &[&Device]) -> anyhow::Result<()> {
 | 
				
			||||||
        // Save all relays state
 | 
					        // Save all relays state
 | 
				
			||||||
        for d in devices {
 | 
					        for d in devices {
 | 
				
			||||||
            for r in &d.relays {
 | 
					            for r in &d.relays {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -139,6 +139,10 @@ pub async fn secure_server(energy_actor: EnergyActorAddr) -> anyhow::Result<()>
 | 
				
			|||||||
                "/web_api/energy/cached_consumption",
 | 
					                "/web_api/energy/cached_consumption",
 | 
				
			||||||
                web::get().to(energy_controller::cached_consumption),
 | 
					                web::get().to(energy_controller::cached_consumption),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					            .route(
 | 
				
			||||||
 | 
					                "/web_api/energy/relays_consumption",
 | 
				
			||||||
 | 
					                web::get().to(energy_controller::relays_consumption),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            .route(
 | 
					            .route(
 | 
				
			||||||
                "/web_api/energy/relays_consumption/history",
 | 
					                "/web_api/energy/relays_consumption/history",
 | 
				
			||||||
                web::get().to(energy_controller::relays_consumption_history),
 | 
					                web::get().to(energy_controller::relays_consumption_history),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
use crate::app_config::ConsumptionHistoryType;
 | 
					use crate::app_config::ConsumptionHistoryType;
 | 
				
			||||||
 | 
					use crate::energy::consumption::EnergyConsumption;
 | 
				
			||||||
use crate::energy::consumption_history_file::ConsumptionHistoryFile;
 | 
					use crate::energy::consumption_history_file::ConsumptionHistoryFile;
 | 
				
			||||||
use crate::energy::{consumption, energy_actor};
 | 
					use crate::energy::{consumption, energy_actor};
 | 
				
			||||||
use crate::server::custom_error::HttpResult;
 | 
					use crate::server::custom_error::HttpResult;
 | 
				
			||||||
@@ -36,6 +37,14 @@ pub async fn cached_consumption(energy_actor: WebEnergyActor) -> HttpResult {
 | 
				
			|||||||
    Ok(HttpResponse::Ok().json(Consumption { consumption }))
 | 
					    Ok(HttpResponse::Ok().json(Consumption { consumption }))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Get current relays consumption
 | 
				
			||||||
 | 
					pub async fn relays_consumption(energy_actor: WebEnergyActor) -> HttpResult {
 | 
				
			||||||
 | 
					    let consumption =
 | 
				
			||||||
 | 
					        energy_actor.send(energy_actor::RelaysConsumption).await? as EnergyConsumption;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(HttpResponse::Ok().json(Consumption { consumption }))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub async fn relays_consumption_history() -> HttpResult {
 | 
					pub async fn relays_consumption_history() -> HttpResult {
 | 
				
			||||||
    let history = ConsumptionHistoryFile::get_history(
 | 
					    let history = ConsumptionHistoryFile::get_history(
 | 
				
			||||||
        ConsumptionHistoryType::RelayConsumption,
 | 
					        ConsumptionHistoryType::RelayConsumption,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,6 +35,18 @@ export class EnergyApi {
 | 
				
			|||||||
    return data.data.consumption;
 | 
					    return data.data.consumption;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Get relays consumption
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  static async RelaysConsumption(): Promise<number> {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      await APIClient.exec({
 | 
				
			||||||
 | 
					        method: "GET",
 | 
				
			||||||
 | 
					        uri: "/energy/relays_consumption",
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    ).data.consumption;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Get relays consumption history
 | 
					   * Get relays consumption history
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -83,7 +83,7 @@ export class RelayApi {
 | 
				
			|||||||
    return (
 | 
					    return (
 | 
				
			||||||
      await APIClient.exec({
 | 
					      await APIClient.exec({
 | 
				
			||||||
        method: "GET",
 | 
					        method: "GET",
 | 
				
			||||||
        uri: `/relay/${relay.id}/state`,
 | 
					        uri: `/relay/${relay.id}/status`,
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
    ).data;
 | 
					    ).data;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ import { Typography } from "@mui/material";
 | 
				
			|||||||
import { CurrConsumptionWidget } from "./HomeRoute/CurrConsumptionWidget";
 | 
					import { CurrConsumptionWidget } from "./HomeRoute/CurrConsumptionWidget";
 | 
				
			||||||
import Grid from "@mui/material/Grid2";
 | 
					import Grid from "@mui/material/Grid2";
 | 
				
			||||||
import { CachedConsumptionWidget } from "./HomeRoute/CachedConsumptionWidget";
 | 
					import { CachedConsumptionWidget } from "./HomeRoute/CachedConsumptionWidget";
 | 
				
			||||||
 | 
					import { RelayConsumptionWidget } from "./HomeRoute/RelayConsumptionWidget";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function HomeRoute(): React.ReactElement {
 | 
					export function HomeRoute(): React.ReactElement {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
@@ -18,6 +19,9 @@ export function HomeRoute(): React.ReactElement {
 | 
				
			|||||||
        <Grid size={{ xs: 12, sm: 6, lg: 3 }}>
 | 
					        <Grid size={{ xs: 12, sm: 6, lg: 3 }}>
 | 
				
			||||||
          <CurrConsumptionWidget />
 | 
					          <CurrConsumptionWidget />
 | 
				
			||||||
        </Grid>
 | 
					        </Grid>
 | 
				
			||||||
 | 
					        <Grid size={{ xs: 12, sm: 6, lg: 3 }}>
 | 
				
			||||||
 | 
					          <RelayConsumptionWidget />
 | 
				
			||||||
 | 
					        </Grid>
 | 
				
			||||||
        <Grid size={{ xs: 12, sm: 6, lg: 3 }}>
 | 
					        <Grid size={{ xs: 12, sm: 6, lg: 3 }}>
 | 
				
			||||||
          <CachedConsumptionWidget />
 | 
					          <CachedConsumptionWidget />
 | 
				
			||||||
        </Grid>
 | 
					        </Grid>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					import React from "react";
 | 
				
			||||||
 | 
					import { EnergyApi } from "../../api/EnergyApi";
 | 
				
			||||||
 | 
					import { useSnackbar } from "../../hooks/context_providers/SnackbarProvider";
 | 
				
			||||||
 | 
					import StatCard from "../../widgets/StatCard";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function RelayConsumptionWidget(): React.ReactElement {
 | 
				
			||||||
 | 
					  const snackbar = useSnackbar();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [val, setVal] = React.useState<undefined | number>();
 | 
				
			||||||
 | 
					  const [history, setHistory] = React.useState<number[] | undefined>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const refresh = async () => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const s = await EnergyApi.RelaysConsumption();
 | 
				
			||||||
 | 
					      const history = await EnergyApi.RelaysConsumptionHistory();
 | 
				
			||||||
 | 
					      setVal(s);
 | 
				
			||||||
 | 
					      setHistory(history);
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      console.error(e);
 | 
				
			||||||
 | 
					      snackbar("Failed to refresh current relays consumption!");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  React.useEffect(() => {
 | 
				
			||||||
 | 
					    const i = setInterval(() => refresh(), 3000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return () => clearInterval(i);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <StatCard
 | 
				
			||||||
 | 
					      title="Relays consumption"
 | 
				
			||||||
 | 
					      data={history ?? []}
 | 
				
			||||||
 | 
					      interval="Last day"
 | 
				
			||||||
 | 
					      value={val?.toString() ?? "Loading"}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -61,7 +61,10 @@ export function TimeWidget(p: {
 | 
				
			|||||||
}): React.ReactElement {
 | 
					}): React.ReactElement {
 | 
				
			||||||
  if (!p.time) return <></>;
 | 
					  if (!p.time) return <></>;
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Tooltip title={formatDate(p.time)} arrow>
 | 
					    <Tooltip
 | 
				
			||||||
 | 
					      title={formatDate(p.diff ? new Date().getTime() / 1000 - p.time : p.time)}
 | 
				
			||||||
 | 
					      arrow
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
      <span>{p.diff ? timeDiff(0, p.time) : timeDiffFromNow(p.time)}</span>
 | 
					      <span>{p.diff ? timeDiff(0, p.time) : timeDiffFromNow(p.time)}</span>
 | 
				
			||||||
    </Tooltip>
 | 
					    </Tooltip>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user