Files
PlantCtrl/rust/src/webserver/get_json.rs
2025-10-04 01:24:00 +02:00

186 lines
5.6 KiB
Rust

use core::str::FromStr;
use crate::fat_error::{FatError, FatResult};
use crate::hal::{esp_time, PLANT_COUNT};
use crate::log::LogMessage;
use crate::plant_state::{MoistureSensorState, PlantState};
use crate::tank::determine_tank_state;
use crate::{get_version, BOARD_ACCESS};
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use chrono_tz::Tz;
use edge_http::io::server::Connection;
use embedded_io_async::{Read, Write};
use log::info;
use serde::Serialize;
#[derive(Serialize, Debug)]
struct LoadData<'a> {
rtc: &'a str,
native: &'a str,
}
#[derive(Serialize, Debug)]
struct Moistures {
moisture_a: Vec<String>,
moisture_b: Vec<String>,
}
#[derive(Serialize, Debug)]
struct SolarState {
mppt_voltage: f32,
mppt_current: f32,
is_day: bool,
}
pub(crate) async fn get_live_moisture<T, const N: usize>(
_request: &mut Connection<'_, T, N>,
) -> FatResult<Option<String>>
where
T: Read + Write,
{
let mut board = BOARD_ACCESS.get().await.lock().await;
let mut plant_state = Vec::new();
for i in 0..PLANT_COUNT {
plant_state.push(PlantState::read_hardware_state(i, &mut board).await);
}
let a = Vec::from_iter(plant_state.iter().map(|s| match &s.sensor_a {
MoistureSensorState::Disabled => "disabled".to_string(),
MoistureSensorState::MoistureValue {
raw_hz,
moisture_percent,
} => {
format!("{moisture_percent:.2}% {raw_hz}hz",)
}
MoistureSensorState::SensorError(err) => format!("{err:?}"),
}));
let b = Vec::from_iter(plant_state.iter().map(|s| match &s.sensor_b {
MoistureSensorState::Disabled => "disabled".to_string(),
MoistureSensorState::MoistureValue {
raw_hz,
moisture_percent,
} => {
format!("{moisture_percent:.2}% {raw_hz}hz",)
}
MoistureSensorState::SensorError(err) => format!("{err:?}"),
}));
let data = Moistures {
moisture_a: a,
moisture_b: b,
};
let json = serde_json::to_string(&data)?;
Ok(Some(json))
}
pub(crate) async fn tank_info<T, const N: usize>(
_request: &mut Connection<'_, T, N>,
) -> Result<Option<String>, FatError>
where
T: Read + Write,
{
let mut board = BOARD_ACCESS.get().await.lock().await;
let tank_state = determine_tank_state(&mut board).await;
//should be multisampled
let sensor = board.board_hal.get_tank_sensor()?;
let water_temp: FatResult<f32> = sensor.water_temperature_c().await;
Ok(Some(serde_json::to_string(&tank_state.as_mqtt_info(
&board.board_hal.get_config().tank,
&water_temp,
))?))
}
pub(crate) async fn get_timezones() -> FatResult<Option<String>> {
// Get all timezones compiled into the binary from chrono-tz
let timezones: Vec<&'static str> = chrono_tz::TZ_VARIANTS.iter().map(|tz| tz.name()).collect();
let json = serde_json::to_string(&timezones)?;
Ok(Some(json))
}
pub(crate) async fn get_solar_state<T, const N: usize>(
_request: &mut Connection<'_, T, N>,
) -> FatResult<Option<String>> {
let mut board = BOARD_ACCESS.get().await.lock().await;
let state = SolarState {
mppt_voltage: board.board_hal.get_mptt_voltage().await?.as_millivolts() as f32,
mppt_current: board.board_hal.get_mptt_current().await?.as_milliamperes() as f32,
is_day: board.board_hal.is_day(),
};
Ok(Some(serde_json::to_string(&state)?))
}
pub(crate) async fn get_version_web<T, const N: usize>(
_request: &mut Connection<'_, T, N>,
) -> FatResult<Option<String>> {
let mut board = BOARD_ACCESS.get().await.lock().await;
Ok(Some(serde_json::to_string(&get_version(&mut board).await)?))
}
pub(crate) async fn get_config<T, const N: usize>(
_request: &mut Connection<'_, T, N>,
) -> FatResult<Option<String>> {
let mut board = BOARD_ACCESS.get().await.lock().await;
let json = serde_json::to_string(&board.board_hal.get_config())?;
Ok(Some(json))
}
pub(crate) async fn get_battery_state<T, const N: usize>(
_request: &mut Connection<'_, T, N>,
) -> FatResult<Option<String>> {
let mut board = BOARD_ACCESS.get().await.lock().await;
let battery_state = board
.board_hal
.get_battery_monitor()
.get_battery_state()
.await?;
Ok(Some(serde_json::to_string(&battery_state)?))
}
pub(crate) async fn get_time<T, const N: usize>(
_request: &mut Connection<'_, T, N>,
) -> FatResult<Option<String>> {
let mut board = BOARD_ACCESS.get().await.lock().await;
let conf = board.board_hal.get_config();
let tz:Tz = match conf.timezone.as_ref(){
None => {
Tz::UTC
}
Some(tz_string) => {
match Tz::from_str(tz_string) {
Ok(tz) => {
tz
}
Err(err) => {
info!("failed parsing timezone {}", err);
Tz::UTC
}
}
}
};
let native = esp_time().await.with_timezone(&tz).to_rfc3339();
let rtc = match board.board_hal.get_rtc_module().get_rtc_time().await {
Ok(time) => time.with_timezone(&tz).to_rfc3339(),
Err(err) => {
format!("Error getting time: {}", err)
}
};
let data = LoadData {
rtc: rtc.as_str(),
native: native.as_str(),
};
let json = serde_json::to_string(&data)?;
Ok(Some(json))
}
pub(crate) async fn get_log_localization_config<T, const N: usize>(
_request: &mut Connection<'_, T, N>,
) -> FatResult<Option<String>> {
Ok(Some(serde_json::to_string(
&LogMessage::to_log_localisation_config(),
)?))
}