Nareshkumar Rao 7 months ago
parent
commit
35020937a2
  1. 154
      Cargo.lock
  2. 2
      Cargo.toml
  3. 2
      html/growpi/index.html
  4. BIN
      html/growpi/public/favicon.ico
  5. 8
      html/growpi/src/App.vue
  6. 7
      src/config.rs
  7. 16
      src/control/soil.rs
  8. 56
      src/server.rs

154
Cargo.lock

@ -148,6 +148,15 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" 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]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.16.0" version = "3.16.0"
@ -207,6 +216,25 @@ version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" 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]] [[package]]
name = "csv" name = "csv"
version = "1.3.0" version = "1.3.0"
@ -228,6 +256,16 @@ dependencies = [
"memchr", "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]] [[package]]
name = "embedded-hal" name = "embedded-hal"
version = "0.2.7" version = "0.2.7"
@ -341,6 +379,16 @@ dependencies = [
"pin-utils", "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]] [[package]]
name = "gimli" name = "gimli"
version = "0.28.1" version = "0.28.1"
@ -356,8 +404,10 @@ dependencies = [
"chrono", "chrono",
"csv", "csv",
"libc", "libc",
"mime_guess",
"nb 1.1.0", "nb 1.1.0",
"rppal", "rppal",
"rust-embed",
"rustyline", "rustyline",
"serde", "serde",
"tokio", "tokio",
@ -551,6 +601,16 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 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]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.7.2" version = "0.7.2"
@ -712,6 +772,40 @@ dependencies = [
"void", "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]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.23" version = "0.1.23"
@ -765,6 +859,15 @@ version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" 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]] [[package]]
name = "serde" name = "serde"
version = "1.0.198" version = "1.0.198"
@ -827,6 +930,17 @@ dependencies = [
"serde", "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]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.13.2" version = "1.13.2"
@ -999,6 +1113,21 @@ dependencies = [
"once_cell", "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]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.12" version = "1.0.12"
@ -1023,12 +1152,28 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]] [[package]]
name = "void" name = "void"
version = "1.0.2" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 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]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" 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" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" 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]] [[package]]
name = "windows-core" name = "windows-core"
version = "0.52.0" version = "0.52.0"

2
Cargo.toml

@ -16,3 +16,5 @@ tokio = { "version" = "1.37" }
chrono = "0.4" chrono = "0.4"
tower-http = { "version" = "0.5", features = ["cors"] } tower-http = { "version" = "0.5", features = ["cors"] }
csv = "1.3.0" csv = "1.3.0"
rust-embed = { "version" = "8.3.0", features = ["debug-embed"] }
mime_guess = "=2.0.4"

2
html/growpi/index.html

@ -4,7 +4,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<link rel="icon" href="/favicon.ico"> <link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
<title>GrowPi</title>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>

BIN
html/growpi/public/favicon.ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

8
html/growpi/src/App.vue

@ -25,12 +25,12 @@ export default {
this.soil_moisture = info.soil_moisture; this.soil_moisture = info.soil_moisture;
}, },
async sendSwitch(name, state) { 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(); await this.updateInfo();
}, },
async pumpWater() { async pumpWater() {
console.log("Pumping: " + this.pumpAmount); 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(); await this.updateInfo();
} }
}, },
@ -41,11 +41,11 @@ export default {
} }
async function switchDevice(state) { async function switchDevice(state) {
await fetch("http://192.168.0.107:2205/info")
await fetch("/api/info")
} }
async function receiveInfo() { 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(); const info = await response.json();
return info; return info;
} }

7
src/config.rs

@ -60,6 +60,11 @@ pub struct DataLoggingSettings {
pub frequency_mins: u64, pub frequency_mins: u64,
} }
#[derive(Serialize, Deserialize, Clone)]
pub struct ServerSettings {
pub port: u16,
}
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Configuration { pub struct Configuration {
pub board_settings: BoardSettings, pub board_settings: BoardSettings,
@ -69,6 +74,7 @@ pub struct Configuration {
pub water_pump_settings: WaterPumpSettings, pub water_pump_settings: WaterPumpSettings,
pub controller_settings: ControllerSettings, pub controller_settings: ControllerSettings,
pub data_logging_settings: DataLoggingSettings, pub data_logging_settings: DataLoggingSettings,
pub server_settings: ServerSettings,
} }
impl Configuration { impl Configuration {
@ -123,6 +129,7 @@ impl Default for Configuration {
enabled: true, enabled: true,
frequency_mins: 60, frequency_mins: 60,
}, },
server_settings: ServerSettings { port: 2205 },
} }
} }
} }

16
src/control/soil.rs

@ -1,5 +1,7 @@
use std::time::Duration; use std::time::Duration;
use chrono::{DateTime, Utc};
use crate::{ use crate::{
actuators, actuators,
error::GenericResult, error::GenericResult,
@ -26,6 +28,20 @@ fn soil_moisture_control(program_state: ProgramStateShared) -> GenericResult<()>
let mut program_state = lock_state(&program_state)?; let mut program_state = lock_state(&program_state)?;
let config = &program_state.config.controller_settings; let config = &program_state.config.controller_settings;
let watering_amount = config.watering_amount_grams; 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( actuators::pump_water(
watering_amount.try_into().unwrap_or(100), watering_amount.try_into().unwrap_or(100),
&mut program_state, &mut program_state,

56
src/server.rs

@ -1,7 +1,7 @@
use axum::{ use axum::{
extract::{Path, State}, extract::{Path, State},
http::{Method, StatusCode},
response::IntoResponse,
http::{header, HeaderValue, Method, StatusCode},
response::{IntoResponse, Response},
routing::get, routing::get,
Json, Router, Json, Router,
}; };
@ -17,8 +17,13 @@ use crate::{
}; };
pub async fn run_server(program_state: ProgramStateShared) { 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(); axum::serve(listener, app).await.unwrap();
} }
@ -28,9 +33,11 @@ fn setup_router(program_state: ProgramStateShared) -> Router {
.allow_origin(Any); .allow_origin(Any);
Router::new() 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) .with_state(program_state)
.layer(cors) .layer(cors)
} }
@ -97,3 +104,38 @@ async fn info_handler(
pump_state, pump_state,
})) }))
} }
#[derive(rust_embed::RustEmbed)]
#[folder = "html/growpi/dist/"]
struct Asset;
async fn site_handler(Path(path): Path<String>) -> Response {
serve_site(Some(path))
}
async fn root_handler() -> Response {
serve_site(None)
}
fn serve_site(path: Option<String>) -> 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
}

Loading…
Cancel
Save