refactor: PlantInfo structure (consistent layout)

- fix: use tagged enum serialization for MoistureSensorError and PumpError
- fix: flatten PlantInfo sensors to SensorTelemetry with top-level moisture_pct
This commit is contained in:
2026-05-10 14:04:55 +02:00
parent df3159aa16
commit 6bf7a04024

View File

@@ -1,5 +1,6 @@
use crate::hal::Moistures;
use crate::{config::PlantConfig, hal::HAL, in_time_range};
use alloc::string::String;
use chrono::{DateTime, TimeDelta, Utc};
use chrono_tz::Tz;
use serde::{Deserialize, Serialize};
@@ -7,12 +8,14 @@ use serde::{Deserialize, Serialize};
const MOIST_SENSOR_MAX_FREQUENCY: f32 = 70000.; // 70kHz
const MOIST_SENSOR_MIN_FREQUENCY: f32 = 400.; // this is really, really dry, think like cactus levels
#[derive(Debug, PartialEq, Serialize)]
#[derive(Debug, PartialEq, Clone, Serialize)]
#[serde(tag = "kind")]
pub enum MoistureSensorError {
MissingMessage,
NotExpectedMessage { hz: f32 },
ShortCircuit { hz: f32, max: f32 },
OpenLoop { hz: f32, min: f32 },
BoardError { message: String },
}
#[derive(Debug, PartialEq, Serialize)]
@@ -46,6 +49,14 @@ impl MoistureSensorState {
impl MoistureSensorState {}
#[derive(Debug, PartialEq, Serialize)]
pub struct SensorTelemetry {
pub moisture_pct: Option<f32>,
pub raw_hz: Option<f32>,
pub error: Option<MoistureSensorError>,
}
#[derive(Debug, PartialEq, Serialize)]
#[serde(tag = "kind")]
pub enum PumpError {
PumpNotWorking {
failed_attempts: usize,
@@ -215,7 +226,7 @@ impl PlantState {
pub fn plant_moisture(
&self,
) -> (
Option<u8>,
Option<f32>,
(Option<&MoistureSensorError>, Option<&MoistureSensorError>),
) {
match (
@@ -223,13 +234,13 @@ impl PlantState {
self.sensor_b.moisture_percent(),
) {
(Some(moisture_a), Some(moisture_b)) => {
(Some(((moisture_a + moisture_b) / 2.) as u8), (None, None))
(Some((moisture_a + moisture_b) / 2.), (None, None))
}
(Some(moisture_percent), _) => {
(Some(moisture_percent as u8), (None, self.sensor_b.is_err()))
(Some(moisture_percent), (None, self.sensor_b.is_err()))
}
(_, Some(moisture_percent)) => {
(Some(moisture_percent as u8), (self.sensor_a.is_err(), None))
(Some(moisture_percent), (self.sensor_a.is_err(), None))
}
_ => (None, (self.sensor_a.is_err(), self.sensor_b.is_err())),
}
@@ -247,7 +258,7 @@ impl PlantState {
if let Some(moisture_percent) = moisture_percent {
if self.pump_in_timeout(plant_conf, current_time) {
false
} else if moisture_percent < plant_conf.target_moisture {
} else if moisture_percent < plant_conf.target_moisture.into() {
in_time_range(
current_time,
plant_conf.pump_hour_start,
@@ -273,19 +284,21 @@ impl PlantState {
&self,
plant_conf: &PlantConfig,
current_time: &DateTime<Tz>,
) -> PlantInfo<'_> {
) -> PlantInfo {
let (moisture_pct, _) = self.plant_moisture();
PlantInfo {
sensor_a: &self.sensor_a,
sensor_b: &self.sensor_b,
moisture_pct,
sensor_a: Self::sensor_to_telemetry(&self.sensor_a),
sensor_b: Self::sensor_to_telemetry(&self.sensor_b),
mode: plant_conf.mode,
do_water: self.needs_to_be_watered(plant_conf, current_time),
dry: if let Some(moisture_percent) = self.plant_moisture().0 {
moisture_percent < plant_conf.target_moisture
dry: if let Some(moisture_percent) = moisture_pct {
moisture_percent < plant_conf.target_moisture.into()
} else {
false
},
cooldown: self.pump_in_timeout(plant_conf, current_time),
out_of_work_hour: in_time_range(
out_of_work_hour: !in_time_range(
current_time,
plant_conf.pump_hour_start,
plant_conf.pump_hour_end,
@@ -315,15 +328,42 @@ impl PlantState {
last_fertilizer_time: self.last_fertilizer_time,
}
}
fn sensor_to_telemetry(sensor: &MoistureSensorState) -> SensorTelemetry {
match sensor {
MoistureSensorState::NoMessage => {
SensorTelemetry {
moisture_pct: None,
raw_hz: None,
error: None
}
}
MoistureSensorState::MoistureValue {
hz,
moisture_percent,
} => SensorTelemetry {
moisture_pct: Some(*moisture_percent),
raw_hz: Some(*hz),
error: None,
},
MoistureSensorState::SensorError(err) => SensorTelemetry {
moisture_pct: None,
raw_hz: None,
error: Some(err.clone()),
},
}
}
}
#[derive(Debug, PartialEq, Serialize)]
/// State of a single plant to be tracked
pub struct PlantInfo<'a> {
pub struct PlantInfo {
/// combined plant moisture from available sensors
moisture_pct: Option<f32>,
/// state of humidity sensor on bank a
sensor_a: &'a MoistureSensorState,
sensor_a: SensorTelemetry,
/// state of humidity sensor on bank b
sensor_b: &'a MoistureSensorState,
sensor_b: SensorTelemetry,
/// configured plant watering mode
mode: PlantWateringMode,
/// the plant needs to be watered