186 lines
5.6 KiB
Rust
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(),
|
|
)?))
|
|
}
|