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, moisture_b: Vec, } #[derive(Serialize, Debug)] struct SolarState { mppt_voltage: f32, mppt_current: f32, is_day: bool, } pub(crate) async fn get_live_moisture( _request: &mut Connection<'_, T, N>, ) -> FatResult> where T: Read + Write, { let mut board = BOARD_ACCESS.get().await.lock().await; let moistures = board.board_hal.measure_moisture_hz().await?; let mut plant_state = Vec::new(); for i in 0..PLANT_COUNT { plant_state.push(PlantState::read_hardware_state(moistures, 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( _request: &mut Connection<'_, T, N>, ) -> Result, 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 = 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> { // 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( _request: &mut Connection<'_, T, N>, ) -> FatResult> { 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( _request: &mut Connection<'_, T, N>, ) -> FatResult> { 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( _request: &mut Connection<'_, T, N>, ) -> FatResult> { 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( _request: &mut Connection<'_, T, N>, ) -> FatResult> { 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( _request: &mut Connection<'_, T, N>, ) -> FatResult> { 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( _request: &mut Connection<'_, T, N>, ) -> FatResult> { Ok(Some(serde_json::to_string( &LogMessage::to_log_localisation_config(), )?)) }