Browse Source

add watering history display

main
Nareshkumar Rao 7 months ago
parent
commit
5b4ccdbca4
  1. 9
      html/growpi/package-lock.json
  2. 1
      html/growpi/package.json
  3. 29
      html/growpi/src/App.vue
  4. 2
      src/history.rs
  5. 25
      src/server.rs

9
html/growpi/package-lock.json

@ -8,6 +8,7 @@
"name": "growpi", "name": "growpi",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"date-format": "^4.0.14",
"primevue": "^3.52.0", "primevue": "^3.52.0",
"vue": "^3.4.21" "vue": "^3.4.21"
}, },
@ -722,6 +723,14 @@
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
}, },
"node_modules/date-format": {
"version": "4.0.14",
"resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz",
"integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==",
"engines": {
"node": ">=4.0"
}
},
"node_modules/entities": { "node_modules/entities": {
"version": "4.5.0", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",

1
html/growpi/package.json

@ -9,6 +9,7 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"date-format": "^4.0.14",
"primevue": "^3.52.0", "primevue": "^3.52.0",
"vue": "^3.4.21" "vue": "^3.4.21"
}, },

29
html/growpi/src/App.vue

@ -3,6 +3,7 @@ import Button from 'primevue/button';
import Image from 'primevue/image'; import Image from 'primevue/image';
import InputNumber from 'primevue/inputnumber'; import InputNumber from 'primevue/inputnumber';
import ToggleButton from 'primevue/togglebutton'; import ToggleButton from 'primevue/togglebutton';
import format from 'date-format'
export default { export default {
data() { data() {
return { return {
@ -13,7 +14,8 @@ export default {
water_primed: false, water_primed: false,
fan_state: false, fan_state: false,
pumpAmount: 150, pumpAmount: 150,
image_src: "/image"
image_src: "/image",
watering_history: [{ time: 1714976340, amount: 150, moisture_before_watering: 0.7490353 }]
}; };
}, },
components: { ToggleButton, Button, InputNumber, Image }, components: { ToggleButton, Button, InputNumber, Image },
@ -25,6 +27,9 @@ export default {
this.pump_state = to_bool(info.pump_state); this.pump_state = to_bool(info.pump_state);
this.temperature = info.temperature; this.temperature = info.temperature;
this.soil_moisture = info.soil_moisture; this.soil_moisture = info.soil_moisture;
let response = await fetch("/api/watering_history/10");
this.watering_history = await response.json();
}, },
async updateImage() { async updateImage() {
this.image_src = "/image#" + new Date().getTime(); this.image_src = "/image#" + new Date().getTime();
@ -41,6 +46,9 @@ export default {
async refreshImage() { async refreshImage() {
await fetch("/api/refresh_image"); await fetch("/api/refresh_image");
this.updateImage(); this.updateImage();
},
formatDate(timestamp) {
return format.asString('dd-MM-yyyy at hh:mm', new Date(timestamp * 1000));
} }
}, },
created() { created() {
@ -125,6 +133,25 @@ function to_bool(state) {
<Button @click="refreshImage();" label="Refresh Image" /> <Button @click="refreshImage();" label="Refresh Image" />
</td> </td>
</tr> </tr>
<tr>
<td colspan="2">
<b>Watering History</b>
<br /><br />
<table class="main-table">
<tr v-for="record in watering_history">
<td>
{{ formatDate(record.time) }}
</td>
<td>
{{ record.amount }} grams
</td>
<td>
{{ Math.round(record.moisture_before_watering * 100 * 100) / 100 }}% moisture
</td>
</tr>
</table>
</td>
</tr>
</table> </table>
</template> </template>
<style> <style>

2
src/history.rs

@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
use crate::error::GenericResult; use crate::error::GenericResult;
#[derive(Serialize, Deserialize)]
#[derive(Clone, Serialize, Deserialize)]
pub struct WateringRecord { pub struct WateringRecord {
pub time: i64, pub time: i64,
pub amount: u64, pub amount: u64,

25
src/server.rs

@ -37,6 +37,10 @@ fn setup_router(program_state: ProgramStateShared) -> Router {
.route("/api/switch/:device/:state", get(switch_handler)) .route("/api/switch/:device/:state", get(switch_handler))
.route("/api/pump/:quantity", get(pump_handler)) .route("/api/pump/:quantity", get(pump_handler))
.route("/api/refresh_image", get(image_refresh_handler)) .route("/api/refresh_image", get(image_refresh_handler))
.route(
"/api/watering_history/:entries",
get(watering_history_handler),
)
.route("/image", get(image_handler)) .route("/image", get(image_handler))
.route("/*path", get(site_handler)) .route("/*path", get(site_handler))
.route("/", get(root_handler)) .route("/", get(root_handler))
@ -156,3 +160,24 @@ async fn image_refresh_handler(State(program_state): State<ProgramStateShared>)
let _ = control::imaging::save_latest_image(program_state).await; let _ = control::imaging::save_latest_image(program_state).await;
StatusCode::OK.into_response() StatusCode::OK.into_response()
} }
async fn watering_history_handler(
Path(entries): Path<usize>,
State(program_state): State<ProgramStateShared>,
) -> Response {
let records = lock_state(&program_state).map(|state| {
state
.history
.watering_records
.iter()
.rev()
.take(entries)
.cloned()
.collect::<Vec<_>>()
});
let records = records.map(Json);
match records {
Ok(records) => records.into_response(),
Err(_) => StatusCode::SERVICE_UNAVAILABLE.into_response(),
}
}

Loading…
Cancel
Save