Compare commits
132 Commits
50316a665f
...
1.0.3
| Author | SHA1 | Date | |
|---|---|---|---|
| 4d46f9c52c | |||
| c90a05fcfd | |||
| abdca20a66 | |||
| 88a24565b4 | |||
| 3625188706 | |||
| 58eceeda2d | |||
| c44c6c3bf5 | |||
| 4ef714fdbd | |||
| bab4525908 | |||
| 2ed4299032 | |||
| bcc92da065 | |||
| af83130b63 | |||
| 950b576373 | |||
| 2459b0cf99 | |||
| 814204b4ce | |||
| de8fcfbcfb | |||
| 49a7011d84 | |||
| 39aff8054a | |||
| 850f828dca | |||
| 59383debd2 | |||
| 58cf0a9614 | |||
| f30aaf71dd | |||
| 27ce9e9a96 | |||
| 903dd104f3 | |||
| 205981fbc8 | |||
| 623b0a4671 | |||
| fd25e71cf8 | |||
| 842b48e782 | |||
| e8374a8ef7 | |||
| 347c247285 | |||
| c59aeed4ab | |||
| b7f1beb1b7 | |||
| 4bd700c2db | |||
| 3f1c5e4ac0 | |||
| f77506dc46 | |||
| 5b7554b6bf | |||
| 8fdbcc4f3a | |||
| 9f4c2b0e35 | |||
| 0a7138a82b | |||
| 2ce09a94b1 | |||
| 355b2a71ce | |||
| 6b04bf4261 | |||
| 99c6963210 | |||
| 89cfd3ce21 | |||
| 0c40ff2750 | |||
| 75be1ed1d2 | |||
| 6a2baca3f2 | |||
| 4d0d20b424 | |||
| 05cf488be7 | |||
| b8ecc83668 | |||
| c1912717e4 | |||
| a8780e60d3 | |||
| 5005cf84f9 | |||
| 561992010c | |||
| 4725e67ee1 | |||
| fbb55628be | |||
| 79e49ed5a9 | |||
| 138b9d2dbe | |||
| 3413a1ee21 | |||
| 64055568e0 | |||
| af71b574cc | |||
| 6088237b79 | |||
| 05a033b51c | |||
| 3d3ccf5242 | |||
| 68521c238c | |||
| f03278f8c5 | |||
| a63d1f17de | |||
| bb6d8a8be1 | |||
| 693414b1eb | |||
| f74e86a8db | |||
| 53d4cb1de7 | |||
| 498ca3925a | |||
| 155806df78 | |||
| 79f3668021 | |||
| 660e6e8a5b | |||
| 0e3182434f | |||
| cf2f034e6c | |||
| ddd27519a9 | |||
| e8d2e8b318 | |||
| 0dfc25a918 | |||
| 3078b3c645 | |||
| 9d3bed68af | |||
| 9fe00f149c | |||
| 65164055d6 | |||
| 08680122f2 | |||
| 642366540e | |||
| 482da63a3b | |||
| cade9dc02b | |||
| 9b87025b27 | |||
| 46d3f3580c | |||
| bda9f6a9c9 | |||
| 41380a103f | |||
| 2573778b82 | |||
| 527126d926 | |||
| 9a1104c57b | |||
| bf3e703bb1 | |||
| d7607bc483 | |||
| 306be8272a | |||
| 742bb8e2ed | |||
| e10d7ef478 | |||
| 18ba6c60d6 | |||
| d70562f97a | |||
| b0d192a9aa | |||
| 99f6b17f4e | |||
| 3fff4d624e | |||
| e2f6212a75 | |||
| bff1c88da8 | |||
| 7933c2dae3 | |||
| 15b8a69700 | |||
| 157c77142a | |||
| 25f6e808ed | |||
| 6f298d3238 | |||
| b75e868cc3 | |||
| 6a2b5e320d | |||
| 52637fc401 | |||
| 8ea9da9443 | |||
| b1b4eaa341 | |||
| 94652a82fe | |||
| 832ab86536 | |||
| 32dbbf4678 | |||
| 1f48203564 | |||
| 0b6526f901 | |||
| 741db9e13b | |||
| 061ae8c208 | |||
| 4a68800907 | |||
| 6738d47507 | |||
| acbce81b46 | |||
| 1615531d67 | |||
| 5355b351d1 | |||
| ad10df3e7f | |||
| edf70cb8fc | |||
| 8af3018b34 |
37
.drone.yml
37
.drone.yml
@@ -46,6 +46,11 @@ steps:
|
||||
path: /usr/local/cargo/registry
|
||||
- name: web_app
|
||||
path: /tmp/web_build
|
||||
- name: releases
|
||||
path: /tmp/releases
|
||||
when:
|
||||
event:
|
||||
- tag
|
||||
depends_on:
|
||||
- backend_check
|
||||
- web_build
|
||||
@@ -54,13 +59,41 @@ steps:
|
||||
- mv /tmp/web_build/dist static
|
||||
- cargo build --release
|
||||
- ls -lah target/release/central_backend
|
||||
- mv target/release/central_backend /tmp/releases/central_backend
|
||||
|
||||
# Build ESP32 program
|
||||
- name: esp32_compile
|
||||
image: espressif/idf:v5.4.2
|
||||
image: espressif/idf:v5.5.1
|
||||
volumes:
|
||||
- name: releases
|
||||
path: /tmp/releases
|
||||
commands:
|
||||
- cd esp32_device
|
||||
- /opt/esp/entrypoint.sh idf.py build
|
||||
- ls -lah build/main.bin
|
||||
- cp build/main.bin /tmp/releases/wt32-eth01.bin
|
||||
|
||||
# Auto-release to Gitea
|
||||
- name: gitea_release
|
||||
image: plugins/gitea-release
|
||||
depends_on:
|
||||
- backend_compile
|
||||
- esp32_compile
|
||||
when:
|
||||
event:
|
||||
- tag
|
||||
volumes:
|
||||
- name: releases
|
||||
path: /tmp/releases
|
||||
environment:
|
||||
PLUGIN_API_KEY:
|
||||
from_secret: GITEA_API_KEY # needs permission write:repository
|
||||
settings:
|
||||
base_url: https://gitea.communiquons.org
|
||||
files:
|
||||
- /tmp/releases/central_backend
|
||||
- /tmp/releases/wt32-eth01.bin
|
||||
checksum: sha512
|
||||
|
||||
|
||||
volumes:
|
||||
@@ -68,3 +101,5 @@ volumes:
|
||||
temp: {}
|
||||
- name: web_app
|
||||
temp: {}
|
||||
- name: releases
|
||||
temp: {}
|
||||
|
||||
700
central_backend/Cargo.lock
generated
700
central_backend/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,46 +1,46 @@
|
||||
[package]
|
||||
name = "central_backend"
|
||||
version = "1.0.2"
|
||||
version = "1.0.3"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.27"
|
||||
log = "0.4.28"
|
||||
env_logger = "0.11.8"
|
||||
lazy_static = "1.5.0"
|
||||
dotenvy = "0.15.7"
|
||||
clap = { version = "4.5.45", features = ["derive", "env"] }
|
||||
anyhow = "1.0.99"
|
||||
thiserror = "2.0.12"
|
||||
openssl = { version = "0.10.73" }
|
||||
openssl-sys = "0.9.109"
|
||||
libc = "0.2.174"
|
||||
clap = { version = "4.5.50", features = ["derive", "env"] }
|
||||
anyhow = "1.0.100"
|
||||
thiserror = "2.0.17"
|
||||
openssl = { version = "0.10.74" }
|
||||
openssl-sys = "0.9.110"
|
||||
libc = "0.2.177"
|
||||
foreign-types-shared = "0.1.1"
|
||||
asn1 = "0.22.0"
|
||||
asn1 = "0.23.0"
|
||||
actix-web = { version = "4.11.0", features = ["openssl"] }
|
||||
futures = "0.3.31"
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
reqwest = { version = "0.12.22", features = ["json"] }
|
||||
serde_json = "1.0.142"
|
||||
rand = "0.9.2"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
reqwest = { version = "0.12.24", features = ["json"] }
|
||||
serde_json = "1.0.145"
|
||||
rand = "0.10.0-rc.0"
|
||||
actix = "0.13.5"
|
||||
actix-identity = "0.8.0"
|
||||
actix-session = { version = "0.10.1", features = ["cookie-session"] }
|
||||
actix-identity = "0.9.0"
|
||||
actix-session = { version = "0.11.0", features = ["cookie-session"] }
|
||||
actix-cors = "0.7.1"
|
||||
actix-multipart = { version = "0.7.2", features = ["derive"] }
|
||||
actix-remote-ip = "0.1.0"
|
||||
futures-util = "0.3.31"
|
||||
uuid = { version = "1.17.0", features = ["v4", "serde"] }
|
||||
semver = { version = "1.0.26", features = ["serde"] }
|
||||
uuid = { version = "1.18.1", features = ["v4", "serde"] }
|
||||
semver = { version = "1.0.27", features = ["serde"] }
|
||||
lazy-regex = "3.4.1"
|
||||
tokio = { version = "1.47.1", features = ["full"] }
|
||||
tokio = { version = "1.48.0", features = ["full"] }
|
||||
tokio_schedule = "0.3.2"
|
||||
mime_guess = "2.0.5"
|
||||
rust-embed = "8.7.2"
|
||||
jsonwebtoken = { version = "9.3.1", features = ["use_pem"] }
|
||||
rust-embed = "8.8.0"
|
||||
jsonwebtoken = { version = "10.1.0", features = ["use_pem", "rust_crypto"] }
|
||||
prettytable-rs = "0.10.0"
|
||||
chrono = "0.4.41"
|
||||
chrono = "0.4.42"
|
||||
serde_yml = "0.0.12"
|
||||
bincode = "2.0.1"
|
||||
fs4 = { version = "0.13.1", features = ["sync"] }
|
||||
zip = { version = "2.2.0", features = ["bzip2"] }
|
||||
zip = { version = "6.0.0", features = ["bzip2"] }
|
||||
walkdir = "2.5.0"
|
||||
|
||||
33
central_backend/engine_test/test_turn_forced_off.yaml
Normal file
33
central_backend/engine_test/test_turn_forced_off.yaml
Normal file
@@ -0,0 +1,33 @@
|
||||
devices:
|
||||
- id: dev1
|
||||
info:
|
||||
reference: A
|
||||
version: 0.0.1
|
||||
max_relays: 1
|
||||
time_create: 1
|
||||
time_update: 1
|
||||
name: Dev1
|
||||
description: Day1
|
||||
validated: true
|
||||
enabled: true
|
||||
relays:
|
||||
- id: dcb3fd91-bf9b-4de3-99e5-92c1c7dd72e9
|
||||
name: R1
|
||||
enabled: true
|
||||
priority: 1
|
||||
consumption: 100
|
||||
minimal_uptime: 10
|
||||
minimal_downtime: 10
|
||||
depends_on: []
|
||||
conflicts_with: []
|
||||
|
||||
on: false
|
||||
for: 5000
|
||||
forced_state:
|
||||
type: Off
|
||||
for_secs: 500
|
||||
should_be_on: false
|
||||
|
||||
online: true
|
||||
|
||||
curr_consumption: -10000
|
||||
49
central_backend/engine_test/test_turn_forced_on.yaml
Normal file
49
central_backend/engine_test/test_turn_forced_on.yaml
Normal file
@@ -0,0 +1,49 @@
|
||||
devices:
|
||||
- id: dev1
|
||||
info:
|
||||
reference: A
|
||||
version: 0.0.1
|
||||
max_relays: 1
|
||||
time_create: 1
|
||||
time_update: 1
|
||||
name: Dev1
|
||||
description: Day1
|
||||
validated: true
|
||||
enabled: true
|
||||
relays:
|
||||
- id: dcb3fd91-bf9b-4de3-99e5-92c1c7dd72e9
|
||||
name: R1
|
||||
enabled: true
|
||||
priority: 1
|
||||
consumption: 100
|
||||
minimal_uptime: 10
|
||||
minimal_downtime: 10
|
||||
depends_on: []
|
||||
conflicts_with: []
|
||||
|
||||
on: false
|
||||
for: 500
|
||||
forced_state:
|
||||
type: On
|
||||
for_secs: 500
|
||||
should_be_on: true
|
||||
|
||||
- id: dcb3fd91-bf9b-4de3-99e5-92c1c7dd72f0
|
||||
name: R2
|
||||
enabled: true
|
||||
priority: 1
|
||||
consumption: 100
|
||||
minimal_uptime: 10
|
||||
minimal_downtime: 10
|
||||
depends_on: [ ]
|
||||
conflicts_with: [ ]
|
||||
|
||||
on: false
|
||||
for: 500
|
||||
forced_state:
|
||||
type: None
|
||||
should_be_on: false
|
||||
|
||||
online: true
|
||||
|
||||
curr_consumption: 10000
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -25,19 +25,83 @@ impl DeviceState {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum SetRelayForcedStateReq {
|
||||
#[default]
|
||||
None,
|
||||
Off {
|
||||
for_secs: u64,
|
||||
},
|
||||
On {
|
||||
for_secs: u64,
|
||||
},
|
||||
}
|
||||
|
||||
impl SetRelayForcedStateReq {
|
||||
pub fn to_forced_state(&self) -> RelayForcedState {
|
||||
match &self {
|
||||
SetRelayForcedStateReq::None => RelayForcedState::None,
|
||||
SetRelayForcedStateReq::Off { for_secs } => RelayForcedState::Off {
|
||||
until: time_secs() + for_secs,
|
||||
},
|
||||
SetRelayForcedStateReq::On { for_secs } => RelayForcedState::On {
|
||||
until: time_secs() + for_secs,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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 +210,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 +260,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 +293,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 +319,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 +350,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 +379,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 +389,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;
|
||||
}
|
||||
|
||||
@@ -383,7 +467,7 @@ impl EnergyEngine {
|
||||
mod test {
|
||||
use crate::devices::device::{Device, DeviceId, DeviceRelayID};
|
||||
use crate::energy::consumption::EnergyConsumption;
|
||||
use crate::energy::engine::EnergyEngine;
|
||||
use crate::energy::engine::{EnergyEngine, SetRelayForcedStateReq};
|
||||
use crate::utils::time_utils::time_secs;
|
||||
use rust_embed::Embed;
|
||||
|
||||
@@ -392,6 +476,8 @@ mod test {
|
||||
id: DeviceRelayID,
|
||||
on: bool,
|
||||
r#for: usize,
|
||||
#[serde(default)]
|
||||
forced_state: SetRelayForcedStateReq,
|
||||
should_be_on: bool,
|
||||
}
|
||||
|
||||
@@ -439,6 +525,7 @@ mod test {
|
||||
let s = engine.relay_state(r.id);
|
||||
s.on = r.on;
|
||||
s.since = time_secs() as usize - r.r#for;
|
||||
s.forced_state = r.forced_state.to_forced_state()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::server::custom_error::HttpResult;
|
||||
use crate::server::devices_api::jwt_parser::JWTRequest;
|
||||
use actix_web::{HttpResponse, web};
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
#[derive(Debug, serde::Deserialize, Clone)]
|
||||
pub struct LogRequest {
|
||||
severity: LogSeverity,
|
||||
message: String,
|
||||
|
||||
@@ -30,7 +30,7 @@ pub struct JWTRequest {
|
||||
}
|
||||
|
||||
impl JWTRequest {
|
||||
pub async fn parse_jwt<E: DeserializeOwned>(
|
||||
pub async fn parse_jwt<E: DeserializeOwned + std::clone::Clone>(
|
||||
&self,
|
||||
actor: WebEnergyActor,
|
||||
) -> anyhow::Result<(Device, E)> {
|
||||
|
||||
@@ -130,7 +130,7 @@ pub async fn get_certificate(query: web::Query<ReqWithDevID>, actor: WebEnergyAc
|
||||
.body(cert))
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
struct Claims {
|
||||
info: DeviceInfo,
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::server::web_api::*;
|
||||
use crate::server::web_app_controller;
|
||||
use actix_cors::Cors;
|
||||
use actix_identity::IdentityMiddleware;
|
||||
use actix_identity::config::LogoutBehaviour;
|
||||
use actix_identity::config::LogoutBehavior;
|
||||
use actix_remote_ip::RemoteIPConfig;
|
||||
use actix_session::SessionMiddleware;
|
||||
use actix_session::storage::CookieSessionStore;
|
||||
@@ -84,7 +84,7 @@ pub async fn secure_server(energy_actor: EnergyActorAddr) -> anyhow::Result<()>
|
||||
.build();
|
||||
|
||||
let identity_middleware = IdentityMiddleware::builder()
|
||||
.logout_behaviour(LogoutBehaviour::PurgeSession)
|
||||
.logout_behavior(LogoutBehavior::PurgeSession)
|
||||
.visit_deadline(Some(Duration::from_secs(
|
||||
constants::MAX_INACTIVITY_DURATION,
|
||||
)))
|
||||
@@ -231,6 +231,10 @@ pub async fn secure_server(energy_actor: EnergyActorAddr) -> anyhow::Result<()>
|
||||
"/web_api/relay/{id}",
|
||||
web::put().to(relays_controller::update),
|
||||
)
|
||||
.route(
|
||||
"/web_api/relay/{id}/forced_state",
|
||||
web::put().to(relays_controller::set_forced_state),
|
||||
)
|
||||
.route(
|
||||
"/web_api/relay/{id}",
|
||||
web::delete().to(relays_controller::delete),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::devices::device::{DeviceId, DeviceRelay, DeviceRelayID};
|
||||
use crate::energy::energy_actor;
|
||||
use crate::energy::engine::SetRelayForcedStateReq;
|
||||
use crate::server::WebEnergyActor;
|
||||
use crate::server::custom_error::HttpResult;
|
||||
use actix_web::{HttpResponse, web};
|
||||
@@ -85,6 +86,29 @@ pub async fn update(
|
||||
Ok(HttpResponse::Accepted().finish())
|
||||
}
|
||||
|
||||
/// Set relay forced status
|
||||
pub async fn set_forced_state(
|
||||
actor: WebEnergyActor,
|
||||
req: web::Json<SetRelayForcedStateReq>,
|
||||
path: web::Path<RelayIDInPath>,
|
||||
) -> HttpResult {
|
||||
// Check if relay exists first
|
||||
let list = actor.send(energy_actor::GetAllRelaysState).await?;
|
||||
if !list.into_iter().any(|r| r.id == path.id) {
|
||||
return Ok(HttpResponse::NotFound().json("Relay not found!"));
|
||||
};
|
||||
|
||||
// Update relay forced state
|
||||
actor
|
||||
.send(energy_actor::SetRelayForcedState(
|
||||
path.id,
|
||||
req.to_forced_state(),
|
||||
))
|
||||
.await??;
|
||||
|
||||
Ok(HttpResponse::Accepted().finish())
|
||||
}
|
||||
|
||||
/// Delete an existing relay
|
||||
pub async fn delete(actor: WebEnergyActor, path: web::Path<RelayIDInPath>) -> HttpResult {
|
||||
actor
|
||||
|
||||
1687
central_frontend/package-lock.json
generated
1687
central_frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -12,34 +12,34 @@
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@fontsource/roboto": "^5.2.6",
|
||||
"@fontsource/roboto": "^5.2.8",
|
||||
"@mdi/js": "^7.4.47",
|
||||
"@mdi/react": "^1.6.1",
|
||||
"@mui/icons-material": "^7.0.2",
|
||||
"@mui/material": "^7.0.2",
|
||||
"@mui/x-charts": "^7.29.1",
|
||||
"@mui/x-date-pickers": "^7.29.4",
|
||||
"date-and-time": "^3.6.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"filesize": "^10.1.6",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-router-dom": "^7.7.1",
|
||||
"semver": "^7.7.2"
|
||||
"@mui/icons-material": "^7.3.4",
|
||||
"@mui/material": "^7.3.4",
|
||||
"@mui/x-charts": "^8.15.0",
|
||||
"@mui/x-date-pickers": "^8.15.0",
|
||||
"date-and-time": "^4.1.0",
|
||||
"dayjs": "^1.11.18",
|
||||
"filesize": "^11.0.13",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-router-dom": "^7.9.4",
|
||||
"semver": "^7.7.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.1.11",
|
||||
"@types/react-dom": "^19.1.7",
|
||||
"@types/semver": "^7.7.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.39.1",
|
||||
"@typescript-eslint/parser": "^8.37.0",
|
||||
"@vitejs/plugin-react": "^4.7.0",
|
||||
"eslint": "^9.33.0",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.20",
|
||||
"globals": "^16.3.0",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.24.1",
|
||||
"vite": "^6.3.5"
|
||||
"@types/react": "^19.2.2",
|
||||
"@types/react-dom": "^19.2.2",
|
||||
"@types/semver": "^7.7.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.46.2",
|
||||
"@typescript-eslint/parser": "^8.46.2",
|
||||
"@vitejs/plugin-react": "^5.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint-plugin-react-hooks": "^7.0.1",
|
||||
"eslint-plugin-react-refresh": "^0.4.24",
|
||||
"globals": "^16.4.0",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.46.2",
|
||||
"vite": "^7.1.12"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
import { APIClient } from "./ApiClient";
|
||||
import { Device, DeviceRelay } from "./DeviceApi";
|
||||
|
||||
export type RelayForcedState =
|
||||
| { type: "None" }
|
||||
| { type: "Off" | "On"; until: number };
|
||||
|
||||
export type SetRelayForcedState =
|
||||
| { type: "None" }
|
||||
| { type: "Off" | "On"; for_secs: number };
|
||||
|
||||
export interface RelayStatus {
|
||||
id: string;
|
||||
on: boolean;
|
||||
for: number;
|
||||
forced_state: RelayForcedState;
|
||||
}
|
||||
|
||||
export type RelaysStatus = Map<string, RelayStatus>;
|
||||
@@ -48,6 +57,20 @@ export class RelayApi {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set relay forced state
|
||||
*/
|
||||
static async SetForcedState(
|
||||
relay: DeviceRelay,
|
||||
forced: SetRelayForcedState
|
||||
): Promise<void> {
|
||||
await APIClient.exec({
|
||||
method: "PUT",
|
||||
uri: `/relay/${relay.id}/forced_state`,
|
||||
jsonData: forced,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a relay configuration
|
||||
*/
|
||||
|
||||
@@ -84,7 +84,7 @@ export function DeployOTAUpdateDialogProvider(p: {
|
||||
</DialogContentText>
|
||||
|
||||
<FormControl>
|
||||
<FormLabel>Gender</FormLabel>
|
||||
<FormLabel>Deployment target</FormLabel>
|
||||
<RadioGroup
|
||||
name="radio-buttons-group"
|
||||
value={allDevices}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
TextField,
|
||||
} from "@mui/material";
|
||||
import { DeviceRelay } from "../api/DeviceApi";
|
||||
import React from "react";
|
||||
|
||||
export function SelectForcedStateDurationDialog(p: {
|
||||
relay: DeviceRelay;
|
||||
forcedState: string;
|
||||
onCancel: () => void;
|
||||
onSubmit: (duration: number) => void;
|
||||
}): React.ReactElement {
|
||||
const [duration, setDuration] = React.useState(60);
|
||||
|
||||
return (
|
||||
<Dialog open onClose={p.onCancel}>
|
||||
<DialogTitle>Set forced relay state</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
Please specify the number of minutes the relay <i>{p.relay.name}</i>{" "}
|
||||
will remain in forced state <i>{p.forcedState}</i>:
|
||||
</DialogContentText>
|
||||
|
||||
<TextField
|
||||
label="Duration (min)"
|
||||
variant="standard"
|
||||
value={Math.floor(duration / 60)}
|
||||
onChange={(e) => {
|
||||
const val = Number.parseInt(e.target.value);
|
||||
setDuration((Number.isNaN(val) ? 1 : val) * 60);
|
||||
}}
|
||||
fullWidth
|
||||
style={{ marginTop: "5px" }}
|
||||
/>
|
||||
|
||||
<p>Equivalent in seconds: {duration} secs</p>
|
||||
<p>Equivalent in hours: {duration / 3600} hours</p>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={p.onCancel}>Cancel</Button>
|
||||
<Button onClick={() => p.onSubmit(duration)} autoFocus>
|
||||
Start timer
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -10,16 +10,16 @@ import {
|
||||
} from "@mui/material";
|
||||
import React from "react";
|
||||
import { Device, DeviceRelay } from "../../api/DeviceApi";
|
||||
import { RelayApi, RelayStatus } from "../../api/RelayApi";
|
||||
import { EditDeviceRelaysDialog } from "../../dialogs/EditDeviceRelaysDialog";
|
||||
import { DeviceRouteCard } from "./DeviceRouteCard";
|
||||
import { useAlert } from "../../hooks/context_providers/AlertDialogProvider";
|
||||
import { useConfirm } from "../../hooks/context_providers/ConfirmDialogProvider";
|
||||
import { useLoadingMessage } from "../../hooks/context_providers/LoadingMessageProvider";
|
||||
import { RelayApi, RelayStatus } from "../../api/RelayApi";
|
||||
import { useSnackbar } from "../../hooks/context_providers/SnackbarProvider";
|
||||
import { useAlert } from "../../hooks/context_providers/AlertDialogProvider";
|
||||
import { AsyncWidget } from "../../widgets/AsyncWidget";
|
||||
import { TimeWidget } from "../../widgets/TimeWidget";
|
||||
import { BoolText } from "../../widgets/BoolText";
|
||||
import { TimeWidget } from "../../widgets/TimeWidget";
|
||||
import { DeviceRouteCard } from "./DeviceRouteCard";
|
||||
|
||||
export function DeviceRelays(p: {
|
||||
device: Device;
|
||||
@@ -145,7 +145,8 @@ function RelayEntryStatus(
|
||||
errMsg="Failed to load relay status!"
|
||||
build={() => (
|
||||
<>
|
||||
<BoolText val={state!.on} positive="ON" negative="OFF" /> for{" "}
|
||||
<BoolText val={state!.on} positive="ON" negative="OFF" />{" "}
|
||||
{state?.forced_state.type !== "None" && <b>Forced</b>} for{" "}
|
||||
<TimeWidget diff time={state!.for} />
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -16,12 +16,13 @@ import React from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Device, DeviceApi, DeviceRelay, DeviceURL } from "../api/DeviceApi";
|
||||
import { RelayApi, RelaysStatus } from "../api/RelayApi";
|
||||
import { ServerApi } from "../api/ServerApi";
|
||||
import { AsyncWidget } from "../widgets/AsyncWidget";
|
||||
import { BoolText } from "../widgets/BoolText";
|
||||
import { CopyToClipboard } from "../widgets/CopyToClipboard";
|
||||
import { RelayForcedState } from "../widgets/RelayForcedState";
|
||||
import { SolarEnergyRouteContainer } from "../widgets/SolarEnergyRouteContainer";
|
||||
import { TimeWidget } from "../widgets/TimeWidget";
|
||||
import { CopyToClipboard } from "../widgets/CopyToClipboard";
|
||||
import { ServerApi } from "../api/ServerApi";
|
||||
|
||||
export function RelaysListRoute(p: {
|
||||
homeWidget?: boolean;
|
||||
@@ -104,6 +105,7 @@ function RelaysList(p: {
|
||||
<TableCell>Priority</TableCell>
|
||||
<TableCell>Consumption</TableCell>
|
||||
<TableCell>Status</TableCell>
|
||||
<TableCell>Forced state</TableCell>
|
||||
<TableCell></TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
@@ -129,6 +131,13 @@ function RelaysList(p: {
|
||||
/>{" "}
|
||||
for <TimeWidget diff time={p.status.get(row.id)!.for} />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<RelayForcedState
|
||||
relay={row}
|
||||
state={p.status.get(row.id)!}
|
||||
onUpdated={p.onReload}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Tooltip title="Copy legacy api status">
|
||||
<CopyToClipboard
|
||||
|
||||
79
central_frontend/src/widgets/RelayForcedState.tsx
Normal file
79
central_frontend/src/widgets/RelayForcedState.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import { MenuItem, Select, SelectChangeEvent } from "@mui/material";
|
||||
import { DeviceRelay } from "../api/DeviceApi";
|
||||
import { RelayApi, RelayStatus, SetRelayForcedState } from "../api/RelayApi";
|
||||
import { TimeWidget } from "./TimeWidget";
|
||||
import { useLoadingMessage } from "../hooks/context_providers/LoadingMessageProvider";
|
||||
import { useAlert } from "../hooks/context_providers/AlertDialogProvider";
|
||||
import { useSnackbar } from "../hooks/context_providers/SnackbarProvider";
|
||||
import React from "react";
|
||||
import { SelectForcedStateDurationDialog } from "../dialogs/SelectForcedStateDurationDialog";
|
||||
|
||||
export function RelayForcedState(p: {
|
||||
relay: DeviceRelay;
|
||||
state: RelayStatus;
|
||||
onUpdated: () => void;
|
||||
}): React.ReactElement {
|
||||
const loadingMessage = useLoadingMessage();
|
||||
const alert = useAlert();
|
||||
const snackbar = useSnackbar();
|
||||
|
||||
const [futureStateType, setFutureStateType] = React.useState<
|
||||
string | undefined
|
||||
>();
|
||||
|
||||
const handleChange = (event: SelectChangeEvent) => {
|
||||
if (event.target.value == "None") {
|
||||
submitChange({ type: "None" });
|
||||
} else {
|
||||
setFutureStateType(event.target.value);
|
||||
}
|
||||
};
|
||||
|
||||
const submitChange = async (state: SetRelayForcedState) => {
|
||||
try {
|
||||
loadingMessage.show("Setting forced state...");
|
||||
await RelayApi.SetForcedState(p.relay, state);
|
||||
p.onUpdated();
|
||||
snackbar("Forced state successfully updated!");
|
||||
} catch (e) {
|
||||
console.error(`Failed to set relay forced state! ${e}`);
|
||||
alert(`Failed to set loading state for relay! ${e}`);
|
||||
} finally {
|
||||
loadingMessage.hide();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Select
|
||||
value={p.state.forced_state.type}
|
||||
onChange={handleChange}
|
||||
size="small"
|
||||
variant="standard"
|
||||
>
|
||||
<MenuItem value={"None"}>None</MenuItem>
|
||||
<MenuItem value={"Off"}>Off</MenuItem>
|
||||
<MenuItem value={"On"}>On</MenuItem>
|
||||
</Select>
|
||||
{p.state.forced_state.type !== "None" && (
|
||||
<>
|
||||
<TimeWidget future time={p.state.forced_state.until} /> left
|
||||
</>
|
||||
)}
|
||||
|
||||
{futureStateType !== undefined && (
|
||||
<SelectForcedStateDurationDialog
|
||||
{...p}
|
||||
forcedState={futureStateType}
|
||||
onCancel={() => setFutureStateType(undefined)}
|
||||
onSubmit={(d) =>
|
||||
submitChange({
|
||||
type: futureStateType as any,
|
||||
for_secs: d,
|
||||
})
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -106,7 +106,7 @@ export default function StatCard({
|
||||
<Box sx={{ width: "100%", height: 100 }}>
|
||||
{data && interval && (
|
||||
<SparkLineChart
|
||||
colors={[chartColor]}
|
||||
color={chartColor}
|
||||
data={data}
|
||||
area
|
||||
showHighlight
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Tooltip } from "@mui/material";
|
||||
import date from "date-and-time";
|
||||
import { format } from "date-and-time";
|
||||
import { time } from "../utils/DateUtils";
|
||||
|
||||
export function formatDate(time: number): string {
|
||||
const t = new Date();
|
||||
t.setTime(1000 * time);
|
||||
return date.format(t, "DD/MM/YYYY HH:mm:ss");
|
||||
return format(t, "DD/MM/YYYY HH:mm:ss");
|
||||
}
|
||||
|
||||
export function timeDiff(a: number, b: number): string {
|
||||
@@ -51,13 +51,14 @@ export function timeDiff(a: number, b: number): string {
|
||||
return `${diffYears} years`;
|
||||
}
|
||||
|
||||
export function timeDiffFromNow(t: number): string {
|
||||
return timeDiff(t, time());
|
||||
export function timeDiffFromNow(t: number, future?: boolean): string {
|
||||
return future ? timeDiff(time(), t) : timeDiff(t, time());
|
||||
}
|
||||
|
||||
export function TimeWidget(p: {
|
||||
time?: number;
|
||||
diff?: boolean;
|
||||
future?: boolean;
|
||||
}): React.ReactElement {
|
||||
if (!p.time) return <></>;
|
||||
return (
|
||||
@@ -65,7 +66,9 @@ export function TimeWidget(p: {
|
||||
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, p.future)}
|
||||
</span>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
54
custom_consumption/Cargo.lock
generated
54
custom_consumption/Cargo.lock
generated
@@ -623,9 +623,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.45"
|
||||
version = "4.5.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318"
|
||||
checksum = "0c2cfd7bf8a6017ddaa4e32ffe7403d547790db06bd171c1c53926faab501623"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -633,9 +633,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.44"
|
||||
version = "4.5.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8"
|
||||
checksum = "0a4c05b9e80c5ccd3a7ef080ad7b6ba7d6fc00a985b8b157197075677c82c7a0"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -645,9 +645,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.45"
|
||||
version = "4.5.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6"
|
||||
checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
@@ -844,9 +844,9 @@ checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53"
|
||||
|
||||
[[package]]
|
||||
name = "ecolor"
|
||||
version = "0.32.1"
|
||||
version = "0.32.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6a7fc3172c2ef56966b2ce4f84177e159804c40b9a84de8861558ce4a59f422"
|
||||
checksum = "94bdf37f8d5bd9aa7f753573fdda9cf7343afa73dd28d7bfe9593bd9798fc07e"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"emath",
|
||||
@@ -854,9 +854,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "eframe"
|
||||
version = "0.32.1"
|
||||
version = "0.32.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34037a80dc03a4147e1684bff4e4fdab2b1408d715d7b78470cd3179258964b9"
|
||||
checksum = "14d1c15e7bd136b309bd3487e6ffe5f668b354cd9768636a836dd738ac90eb0b"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"bytemuck",
|
||||
@@ -890,9 +890,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "egui"
|
||||
version = "0.32.1"
|
||||
version = "0.32.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49e2be082f77715496b4a39fdc6f5dc7491fefe2833111781b8697ea6ee919a7"
|
||||
checksum = "5d5d0306cd61ca75e29682926d71f2390160247f135965242e904a636f51c0dc"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"ahash",
|
||||
@@ -908,9 +908,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "egui-wgpu"
|
||||
version = "0.32.1"
|
||||
version = "0.32.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64c7277a171ec1b711080ddb3b0bfa1b3aa9358834d5386d39e83fbc16d61212"
|
||||
checksum = "c12eca13293f8eba27a32aaaa1c765bfbf31acd43e8d30d5881dcbe5e99ca0c7"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"bytemuck",
|
||||
@@ -928,9 +928,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "egui-winit"
|
||||
version = "0.32.1"
|
||||
version = "0.32.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe6d8b0f8d6de4d43e794e343f03bacc3908aada931f0ed6fd7041871388a590"
|
||||
checksum = "f95d0a91f9cb0dc2e732d49c2d521ac8948e1f0b758f306fb7b14d6f5db3927f"
|
||||
dependencies = [
|
||||
"accesskit_winit",
|
||||
"ahash",
|
||||
@@ -948,9 +948,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "egui_glow"
|
||||
version = "0.32.1"
|
||||
version = "0.32.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ab645760288e42eab70283a5cccf44509a6f43b554351855d3c73594bfe3c23"
|
||||
checksum = "cc7037813341727937f9e22f78d912f3e29bc3c46e2f40a9e82bb51cbf5e4cfb"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"bytemuck",
|
||||
@@ -966,9 +966,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "emath"
|
||||
version = "0.32.1"
|
||||
version = "0.32.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "935df67dc48fdeef132f2f7ada156ddc79e021344dd42c17f066b956bb88dde3"
|
||||
checksum = "45fd7bc25f769a3c198fe1cf183124bf4de3bd62ef7b4f1eaf6b08711a3af8db"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
@@ -1025,9 +1025,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "epaint"
|
||||
version = "0.32.1"
|
||||
version = "0.32.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b66fc0a5a9d322917de9bd3ac7d426ca8aa3127fbf1e76fae5b6b25e051e06a3"
|
||||
checksum = "63adcea970b7a13094fe97a36ab9307c35a750f9e24bf00bb7ef3de573e0fddb"
|
||||
dependencies = [
|
||||
"ab_glyph",
|
||||
"ahash",
|
||||
@@ -1043,9 +1043,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "epaint_default_fonts"
|
||||
version = "0.32.1"
|
||||
version = "0.32.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f6cf8ce0fb817000aa24f5e630bda904a353536bd430b83ebc1dceee95b4a3a"
|
||||
checksum = "1537accc50c9cab5a272c39300bdd0dd5dca210f6e5e8d70be048df9596e7ca2"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
@@ -1714,7 +1714,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.48.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1770,9 +1770,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.27"
|
||||
version = "0.4.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
|
||||
|
||||
[[package]]
|
||||
name = "malloc_buf"
|
||||
|
||||
@@ -5,8 +5,8 @@ edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
env_logger = "0.11.8"
|
||||
log = "0.4.27"
|
||||
clap = { version = "4.5.45", features = ["derive", "env"] }
|
||||
egui = "0.32.1"
|
||||
eframe = "0.32.1"
|
||||
log = "0.4.28"
|
||||
clap = { version = "4.5.50", features = ["derive", "env"] }
|
||||
egui = "0.32.3"
|
||||
eframe = "0.32.3"
|
||||
lazy_static = "1.5.0"
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#define CONFIG_ETH_USE_ESP32_EMAC
|
||||
|
||||
#include "esp_eth.h"
|
||||
#include "esp_eth_mac.h"
|
||||
#include "esp_eth_com.h"
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.0.2
|
||||
1.0.3
|
||||
Reference in New Issue
Block a user