From 35020937a23d2f30d44c444978137204a17606e7 Mon Sep 17 00:00:00 2001 From: Nareshkumar Rao Date: Fri, 3 May 2024 23:56:29 +0200 Subject: [PATCH] wip --- Cargo.lock | 154 +++++++++++++++++++++++++++++++++ Cargo.toml | 2 + html/growpi/index.html | 2 +- html/growpi/public/favicon.ico | Bin 4286 -> 4286 bytes html/growpi/src/App.vue | 8 +- src/config.rs | 7 ++ src/control/soil.rs | 16 ++++ src/server.rs | 56 ++++++++++-- 8 files changed, 233 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9855b01..2dbc95e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -148,6 +148,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bumpalo" version = "3.16.0" @@ -207,6 +216,25 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "csv" version = "1.3.0" @@ -228,6 +256,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "embedded-hal" version = "0.2.7" @@ -341,6 +379,16 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "gimli" version = "0.28.1" @@ -356,8 +404,10 @@ dependencies = [ "chrono", "csv", "libc", + "mime_guess", "nb 1.1.0", "rppal", + "rust-embed", "rustyline", "serde", "tokio", @@ -551,6 +601,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "miniz_oxide" version = "0.7.2" @@ -712,6 +772,40 @@ dependencies = [ "void", ] +[[package]] +name = "rust-embed" +version = "8.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb78f46d0066053d16d4ca7b898e9343bc3530f71c61d5ad84cd404ada068745" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91ac2a3c6c0520a3fb3dd89321177c3c692937c4eb21893378219da10c44fc8" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f69089032567ffff4eada41c573fc43ff466c7db7c5688b2e7969584345581" +dependencies = [ + "sha2", + "walkdir", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -765,6 +859,15 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "serde" version = "1.0.198" @@ -827,6 +930,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "smallvec" version = "1.13.2" @@ -999,6 +1113,21 @@ dependencies = [ "once_cell", ] +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -1023,12 +1152,28 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1089,6 +1234,15 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "windows-core" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index cf425c9..4c64001 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,5 @@ tokio = { "version" = "1.37" } chrono = "0.4" tower-http = { "version" = "0.5", features = ["cors"] } csv = "1.3.0" +rust-embed = { "version" = "8.3.0", features = ["debug-embed"] } +mime_guess = "=2.0.4" \ No newline at end of file diff --git a/html/growpi/index.html b/html/growpi/index.html index 99f583a..86fe5f0 100644 --- a/html/growpi/index.html +++ b/html/growpi/index.html @@ -4,7 +4,7 @@ - Vite App + GrowPi
diff --git a/html/growpi/public/favicon.ico b/html/growpi/public/favicon.ico index df36fcfb72584e00488330b560ebcf34a41c64c2..f5f3a5cf074a80d5eccc6cd5973fbe04686c63e9 100644 GIT binary patch literal 4286 zcmchbT}V_x6vxMmvWQmHJ_t!G3ymTpqsY|qD_z(1A<{70wp40!*9hq$NOnaZDj_H$ z%is%Ciw&=qkoc`y!cbVP2_bRD-P+ zl(886q^2_Z&2GgrwwN)N4#C8BKxCilb-(@{sxr_fL6 zG&GNLf_;HRb=c-Z?a(ji5!t5mLe4gb2lNDGvdpJCME#T79n;CCT*$o~`XexgDqG7s zD_ixQuu*?x_HWdeO@(jslqNTN%c~l_hPGn^6`jWhjMq?JInuY|U7afnoHY`wx+CWs z^qtsd*y>!HIegy2x2ti$cmr`=$kPt(vw6!>wEAT8>J#C20x?5~=URdvKYTZbn{H-u z)2$8s$lY8{Hm#WPHsdh-PGVdk>)7JXjYIpFiQ_~2W$iEFR!`|9xS?_2d~N72iWBDp z^R)N$0h_nZa?__HM*_SdHhOB|Qs^JD=OU1}|c-B3C=KA6g-DCR@kaDuX`J z8WVNYT^Qt3U2F@~DMmk8PPkw7y%n_QnicX&RnsMFt-A~Lb9p(Q!bNpl_pbeEIyq~>`NNCMr=2bH+Duy3F^tTo!3AsE`8b-7*4yUmh7x%Wb! z74jDc*yJOAyXpLJ4#G()^3vj4J^I|p2x)u^k?$-->sKDC&l}0ax;H6)-6;3#I3G=k)ZaYLWsTW6hyDR3 CUPpic literal 4286 zcmds*O-Phc6o&64GDVCEQHxsW(p4>LW*W<827=Unuo8sGpRux(DN@jWP-e29Wl%wj zY84_aq9}^Am9-cWTD5GGEo#+5Fi2wX_P*bo+xO!)p*7B;iKlbFd(U~_d(U?#hLj56 zPhFkj-|A6~Qk#@g^#D^U0XT1cu=c-vu1+SElX9NR;kzAUV(q0|dl0|%h|dI$%VICy zJnu2^L*Te9JrJMGh%-P79CL0}dq92RGU6gI{v2~|)p}sG5x0U*z<8U;Ij*hB9z?ei z@g6Xq-pDoPl=MANPiR7%172VA%r)kevtV-_5H*QJKFmd;8yA$98zCxBZYXTNZ#QFk2(TX0;Y2dt&WitL#$96|gJY=3xX zpCoi|YNzgO3R`f@IiEeSmKrPSf#h#Qd<$%Ej^RIeeYfsxhPMOG`S`Pz8q``=511zm zAm)MX5AV^5xIWPyEu7u>qYs?pn$I4nL9J!=K=SGlKLXpE<5x+2cDTXq?brj?n6sp= zphe9;_JHf40^9~}9i08r{XM$7HB!`{Ys~TK0kx<}ZQng`UPvH*11|q7&l9?@FQz;8 zx!=3<4seY*%=OlbCbcae?5^V_}*K>Uo6ZWV8mTyE^B=DKy7-sdLYkR5Z?paTgK-zyIkKjIcpyO z{+uIt&YSa_$QnN_@t~L014dyK(fOOo+W*MIxbA6Ndgr=Y!f#Tokqv}n<7-9qfHkc3 z=>a|HWqcX8fzQCT=dqVbogRq!-S>H%yA{1w#2Pn;=e>JiEj7Hl;zdt-2f+j2%DeVD zsW0Ab)ZK@0cIW%W7z}H{&~yGhn~D;aiP4=;m-HCo`BEI+Kd6 z={Xwx{TKxD#iCLfl2vQGDitKtN>z|-AdCN|$jTFDg0m3O`WLD4_s#$S diff --git a/html/growpi/src/App.vue b/html/growpi/src/App.vue index b784a9e..8c16c4c 100644 --- a/html/growpi/src/App.vue +++ b/html/growpi/src/App.vue @@ -25,12 +25,12 @@ export default { this.soil_moisture = info.soil_moisture; }, async sendSwitch(name, state) { - await fetch("http://192.168.0.107:2205/switch/" + name + "/" + to_state(state)); + await fetch("/api/switch/" + name + "/" + to_state(state)); await this.updateInfo(); }, async pumpWater() { console.log("Pumping: " + this.pumpAmount); - await fetch("http://192.168.0.107:2205/pump/" + this.pumpAmount); + await fetch("/api/pump/" + this.pumpAmount); await this.updateInfo(); } }, @@ -41,11 +41,11 @@ export default { } async function switchDevice(state) { - await fetch("http://192.168.0.107:2205/info") + await fetch("/api/info") } async function receiveInfo() { - const response = await fetch("http://192.168.0.107:2205/info"); + const response = await fetch("/api/info"); const info = await response.json(); return info; } diff --git a/src/config.rs b/src/config.rs index e9fda03..137f6f6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -60,6 +60,11 @@ pub struct DataLoggingSettings { pub frequency_mins: u64, } +#[derive(Serialize, Deserialize, Clone)] +pub struct ServerSettings { + pub port: u16, +} + #[derive(Serialize, Deserialize)] pub struct Configuration { pub board_settings: BoardSettings, @@ -69,6 +74,7 @@ pub struct Configuration { pub water_pump_settings: WaterPumpSettings, pub controller_settings: ControllerSettings, pub data_logging_settings: DataLoggingSettings, + pub server_settings: ServerSettings, } impl Configuration { @@ -123,6 +129,7 @@ impl Default for Configuration { enabled: true, frequency_mins: 60, }, + server_settings: ServerSettings { port: 2205 }, } } } diff --git a/src/control/soil.rs b/src/control/soil.rs index 4682c3f..fb274e7 100644 --- a/src/control/soil.rs +++ b/src/control/soil.rs @@ -1,5 +1,7 @@ use std::time::Duration; +use chrono::{DateTime, Utc}; + use crate::{ actuators, error::GenericResult, @@ -26,6 +28,20 @@ fn soil_moisture_control(program_state: ProgramStateShared) -> GenericResult<()> let mut program_state = lock_state(&program_state)?; let config = &program_state.config.controller_settings; let watering_amount = config.watering_amount_grams; + let last_watering_time = program_state + .history + .watering_records + .iter() + .max_by_key(|x| x.time) + .and_then(|record| DateTime::from_timestamp(record.time, 0)); + if let Some(last_watering_time) = last_watering_time { + let hours_passed = (Utc::now() - last_watering_time).num_hours(); + if hours_passed as u64 <= config.watering_frequency_hours { + return Err("Watered too soon ago".into()); + } + } else { + return Err("Could not load last watering time".into()); + } actuators::pump_water( watering_amount.try_into().unwrap_or(100), &mut program_state, diff --git a/src/server.rs b/src/server.rs index 1620c43..c9ba691 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,7 +1,7 @@ use axum::{ extract::{Path, State}, - http::{Method, StatusCode}, - response::IntoResponse, + http::{header, HeaderValue, Method, StatusCode}, + response::{IntoResponse, Response}, routing::get, Json, Router, }; @@ -17,8 +17,13 @@ use crate::{ }; pub async fn run_server(program_state: ProgramStateShared) { - let app: Router = setup_router(program_state); - let listener = tokio::net::TcpListener::bind("0.0.0.0:2205").await.unwrap(); + let app: Router = setup_router(program_state.clone()); + let port = lock_state(&program_state) + .map(|state| state.config.server_settings.port) + .unwrap_or(2205); + let listener = tokio::net::TcpListener::bind(("0.0.0.0", port)) + .await + .unwrap(); axum::serve(listener, app).await.unwrap(); } @@ -28,9 +33,11 @@ fn setup_router(program_state: ProgramStateShared) -> Router { .allow_origin(Any); Router::new() - .route("/info", get(info_handler)) - .route("/switch/:device/:state", get(switch_handler)) - .route("/pump/:quantity", get(pump_handler)) + .route("/api/info", get(info_handler)) + .route("/api/switch/:device/:state", get(switch_handler)) + .route("/api/pump/:quantity", get(pump_handler)) + .route("/*path", get(site_handler)) + .route("/", get(root_handler)) .with_state(program_state) .layer(cors) } @@ -97,3 +104,38 @@ async fn info_handler( pump_state, })) } + +#[derive(rust_embed::RustEmbed)] +#[folder = "html/growpi/dist/"] +struct Asset; + +async fn site_handler(Path(path): Path) -> Response { + serve_site(Some(path)) +} +async fn root_handler() -> Response { + serve_site(None) +} + +fn serve_site(path: Option) -> Response { + let path = match path { + Some(path) => path, + None => "index.html".to_string(), + }; + let asset = Asset::get(&path).or_else(|| Asset::get("index.html")); + + let content = asset + .as_ref() + .and_then(|file| std::str::from_utf8(&file.data).ok()); + let content = content.unwrap_or("Sorry, couldn't load that asset :("); + + let mut response = content.to_string().into_response(); + + let mime_type = mime_guess::from_path(path).first_or_text_plain(); + let header_value = HeaderValue::from_str(mime_type.as_ref()); + if let Ok(header_value) = header_value { + response + .headers_mut() + .append(header::CONTENT_TYPE, header_value); + } + response +}