finish refactor of plant state logic
This commit is contained in:
parent
e941a4973d
commit
519c8d2c52
@ -4,7 +4,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use chrono::{DateTime, Datelike, TimeDelta, Timelike, Utc};
|
use chrono::{DateTime, Datelike, Timelike};
|
||||||
use chrono_tz::{Europe::Berlin, Tz};
|
use chrono_tz::{Europe::Berlin, Tz};
|
||||||
|
|
||||||
use esp_idf_hal::delay::Delay;
|
use esp_idf_hal::delay::Delay;
|
||||||
@ -29,7 +29,7 @@ mod plant_state;
|
|||||||
mod tank;
|
mod tank;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
use plant_state::{PlantInfo, PlantState};
|
use plant_state::PlantState;
|
||||||
use tank::*;
|
use tank::*;
|
||||||
|
|
||||||
const TIME_ZONE: Tz = Berlin;
|
const TIME_ZONE: Tz = Berlin;
|
||||||
@ -406,8 +406,21 @@ fn safe_main() -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut plantstate: [PlantState; PLANT_COUNT] =
|
let plantstate: [PlantState; PLANT_COUNT] =
|
||||||
core::array::from_fn(|i| PlantState::read_hardware_state(i, &mut board, &config.plants[i]));
|
core::array::from_fn(|i| PlantState::read_hardware_state(i, &mut board, &config.plants[i]));
|
||||||
|
for (plant_id, (plant_state, plant_conf)) in plantstate.iter().zip(&config.plants).enumerate() {
|
||||||
|
match serde_json::to_string(&plant_state.to_mqtt_info(plant_conf, &timezone_time)) {
|
||||||
|
Ok(state) => {
|
||||||
|
let plant_topic = format!("/plant{}", plant_id + 1);
|
||||||
|
let _ = board.mqtt_publish(&config, &plant_topic, state.as_bytes());
|
||||||
|
//reduce speed as else messages will be dropped
|
||||||
|
Delay::new_default().delay_ms(200);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
println!("Error publishing plant state {}", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let pump_required = plantstate
|
let pump_required = plantstate
|
||||||
.iter()
|
.iter()
|
||||||
@ -447,17 +460,16 @@ fn safe_main() -> anyhow::Result<()> {
|
|||||||
//state.active = true;
|
//state.active = true;
|
||||||
if !dry_run {
|
if !dry_run {
|
||||||
board.pump(plant_id, true)?;
|
board.pump(plant_id, true)?;
|
||||||
Delay::new_default().delay_ms(1000*plant_config.pump_time_s as u32);
|
Delay::new_default().delay_ms(1000 * plant_config.pump_time_s as u32);
|
||||||
board.pump(plant_id, false)?;
|
board.pump(plant_id, false)?;
|
||||||
}
|
}
|
||||||
} else if !state.pump_in_timeout(&plant_config, &timezone_time){
|
} else if !state.pump_in_timeout(&plant_config, &timezone_time) {
|
||||||
// plant does not need to be watered and is not in timeout
|
// plant does not need to be watered and is not in timeout
|
||||||
// -> reset consecutive pump count
|
// -> reset consecutive pump count
|
||||||
board.store_consecutive_pump_count(plant_id, 0);
|
board.store_consecutive_pump_count(plant_id, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//update_plant_state(&mut plantstate, &mut board, &config);
|
|
||||||
|
|
||||||
let is_day = board.is_day();
|
let is_day = board.is_day();
|
||||||
let state_of_charge = board.state_charge_percent().unwrap_or(0);
|
let state_of_charge = board.state_charge_percent().unwrap_or(0);
|
||||||
@ -622,40 +634,6 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn time_to_string_utc(value_option: Option<DateTime<Utc>>) -> String {
|
|
||||||
let converted = value_option.and_then(|utc| Some(utc.with_timezone(&TIME_ZONE)));
|
|
||||||
return time_to_string(converted);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn time_to_string(value_option: Option<DateTime<Tz>>) -> String {
|
|
||||||
match value_option {
|
|
||||||
Some(value) => {
|
|
||||||
let europe_time = value.with_timezone(&TIME_ZONE);
|
|
||||||
if europe_time.year() > 2023 {
|
|
||||||
return europe_time.to_rfc3339();
|
|
||||||
} else {
|
|
||||||
//initial value of 0 in rtc memory
|
|
||||||
return "N/A".to_owned();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => return "N/A".to_owned(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sensor_to_string(value: &Option<u8>, error: &Option<SensorError>, enabled: bool) -> String {
|
|
||||||
if enabled {
|
|
||||||
match error {
|
|
||||||
Some(error) => return format!("{:?}", error),
|
|
||||||
None => match value {
|
|
||||||
Some(v) => return v.to_string(),
|
|
||||||
None => return "Error".to_owned(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return "disabled".to_owned();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_string<T: Display>(value: Result<T>) -> String {
|
fn to_string<T: Display>(value: Result<T>) -> String {
|
||||||
return match value {
|
return match value {
|
||||||
Ok(v) => v.to_string(),
|
Ok(v) => v.to_string(),
|
||||||
@ -665,7 +643,7 @@ fn to_string<T: Display>(value: Result<T>) -> String {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn in_time_range(cur: &DateTime<Tz>, start: u8, end: u8) -> bool {
|
pub fn in_time_range(cur: &DateTime<Tz>, start: u8, end: u8) -> bool {
|
||||||
let curhour = cur.hour() as u8;
|
let curhour = cur.hour() as u8;
|
||||||
//eg 10-14
|
//eg 10-14
|
||||||
if start < end {
|
if start < end {
|
||||||
|
@ -668,7 +668,7 @@ impl PlantCtrlBoard<'_> {
|
|||||||
);
|
);
|
||||||
results[repeat] = hz;
|
results[repeat] = hz;
|
||||||
}
|
}
|
||||||
results.sort_by(|a,b| a.partial_cmp(b).unwrap()); // floats don't seem to implement total_ord
|
results.sort_by(|a, b| a.partial_cmp(b).unwrap()); // floats don't seem to implement total_ord
|
||||||
|
|
||||||
let mid = results.len() / 2;
|
let mid = results.len() / 2;
|
||||||
|
|
||||||
|
@ -1,38 +1,40 @@
|
|||||||
use chrono::{DateTime, TimeDelta, Utc};
|
use chrono::{DateTime, TimeDelta, Utc};
|
||||||
use chrono_tz::Tz;
|
use chrono_tz::Tz;
|
||||||
use measurements::humidity;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{self, PlantConfig},
|
config::{self, PlantConfig},
|
||||||
plant_hal::{self, PLANT_COUNT},
|
in_time_range, plant_hal,
|
||||||
};
|
};
|
||||||
|
|
||||||
const MOIST_SENSOR_MAX_FREQUENCY: f32 = 5500.; // 60kHz (500Hz margin)
|
const MOIST_SENSOR_MAX_FREQUENCY: f32 = 5500.; // 60kHz (500Hz margin)
|
||||||
const MOIST_SENSOR_MIN_FREQUENCY: f32 = 150.; // this is really really dry, think like cactus levels
|
const MOIST_SENSOR_MIN_FREQUENCY: f32 = 150.; // this is really really dry, think like cactus levels
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize)]
|
#[derive(Debug, PartialEq, Serialize)]
|
||||||
pub enum HumiditySensorError {
|
pub enum MoistureSensorError {
|
||||||
ShortCircuit { hz: f32, max: f32 },
|
ShortCircuit { hz: f32, max: f32 },
|
||||||
OpenLoop { hz: f32, min: f32 },
|
OpenLoop { hz: f32, min: f32 },
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize)]
|
|
||||||
pub enum HumiditySensorState {
|
|
||||||
Disabled,
|
|
||||||
HumidityValue { raw_hz: f32, moisture_percent: f32 },
|
|
||||||
SensorError(HumiditySensorError),
|
|
||||||
BoardError(String),
|
BoardError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HumiditySensorState {
|
#[derive(Debug, PartialEq, Serialize)]
|
||||||
pub fn is_err(&self) -> bool {
|
pub enum MoistureSensorState {
|
||||||
matches!(self, Self::SensorError(_)) || matches!(self, Self::BoardError(_))
|
Disabled,
|
||||||
|
MoistureValue { raw_hz: f32, moisture_percent: f32 },
|
||||||
|
SensorError(MoistureSensorError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MoistureSensorState {
|
||||||
|
pub fn is_err(&self) -> Option<&MoistureSensorError> {
|
||||||
|
match self {
|
||||||
|
MoistureSensorState::SensorError(moisture_sensor_error) => Some(moisture_sensor_error),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn moisture_percent(&self) -> Option<f32> {
|
pub fn moisture_percent(&self) -> Option<f32> {
|
||||||
if let HumiditySensorState::HumidityValue {
|
if let MoistureSensorState::MoistureValue {
|
||||||
raw_hz,
|
raw_hz: _,
|
||||||
moisture_percent,
|
moisture_percent,
|
||||||
} = self
|
} = self
|
||||||
{
|
{
|
||||||
@ -43,7 +45,7 @@ impl HumiditySensorState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HumiditySensorState {}
|
impl MoistureSensorState {}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize)]
|
#[derive(Debug, PartialEq, Serialize)]
|
||||||
pub enum PumpError {
|
pub enum PumpError {
|
||||||
@ -59,30 +61,41 @@ pub struct PumpState {
|
|||||||
previous_pump: Option<DateTime<Utc>>,
|
previous_pump: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
impl PumpState {
|
||||||
|
fn is_err(&self, plant_config: &PlantConfig) -> Option<PumpError> {
|
||||||
|
if self.consecutive_pump_count > plant_config.max_consecutive_pump_count as u32 {
|
||||||
|
Some(PumpError::PumpNotWorking {
|
||||||
|
failed_attempts: self.consecutive_pump_count as usize,
|
||||||
|
max_allowed_failures: plant_config.max_consecutive_pump_count as usize,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum PlantWateringMode {
|
pub enum PlantWateringMode {
|
||||||
OFF,
|
OFF,
|
||||||
TargetMoisture,
|
TargetMoisture,
|
||||||
TimerOnly,
|
TimerOnly,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum PlantError {}
|
|
||||||
|
|
||||||
pub struct PlantState {
|
pub struct PlantState {
|
||||||
pub sensor_a: HumiditySensorState,
|
pub sensor_a: MoistureSensorState,
|
||||||
pub sensor_b: HumiditySensorState,
|
pub sensor_b: MoistureSensorState,
|
||||||
pub pump: PumpState,
|
pub pump: PumpState,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_range_moisture(s: f32) -> Result<f32, HumiditySensorError> {
|
fn map_range_moisture(s: f32) -> Result<f32, MoistureSensorError> {
|
||||||
if s < MOIST_SENSOR_MIN_FREQUENCY {
|
if s < MOIST_SENSOR_MIN_FREQUENCY {
|
||||||
return Err(HumiditySensorError::OpenLoop {
|
return Err(MoistureSensorError::OpenLoop {
|
||||||
hz: s,
|
hz: s,
|
||||||
min: MOIST_SENSOR_MIN_FREQUENCY,
|
min: MOIST_SENSOR_MIN_FREQUENCY,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if s > MOIST_SENSOR_MAX_FREQUENCY {
|
if s > MOIST_SENSOR_MAX_FREQUENCY {
|
||||||
return Err(HumiditySensorError::ShortCircuit {
|
return Err(MoistureSensorError::ShortCircuit {
|
||||||
hz: s,
|
hz: s,
|
||||||
max: MOIST_SENSOR_MAX_FREQUENCY,
|
max: MOIST_SENSOR_MAX_FREQUENCY,
|
||||||
});
|
});
|
||||||
@ -102,30 +115,34 @@ impl PlantState {
|
|||||||
let sensor_a = if config.sensor_a {
|
let sensor_a = if config.sensor_a {
|
||||||
match board.measure_moisture_hz(plant_id, plant_hal::Sensor::A) {
|
match board.measure_moisture_hz(plant_id, plant_hal::Sensor::A) {
|
||||||
Ok(raw) => match map_range_moisture(raw) {
|
Ok(raw) => match map_range_moisture(raw) {
|
||||||
Ok(moisture_percent) => HumiditySensorState::HumidityValue {
|
Ok(moisture_percent) => MoistureSensorState::MoistureValue {
|
||||||
raw_hz: raw,
|
raw_hz: raw,
|
||||||
moisture_percent,
|
moisture_percent,
|
||||||
},
|
},
|
||||||
Err(err) => HumiditySensorState::SensorError(err),
|
Err(err) => MoistureSensorState::SensorError(err),
|
||||||
},
|
},
|
||||||
Err(err) => HumiditySensorState::BoardError(err.to_string()),
|
Err(err) => MoistureSensorState::SensorError(MoistureSensorError::BoardError(
|
||||||
|
err.to_string(),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
HumiditySensorState::Disabled
|
MoistureSensorState::Disabled
|
||||||
};
|
};
|
||||||
let sensor_b = if config.sensor_b {
|
let sensor_b = if config.sensor_b {
|
||||||
match board.measure_moisture_hz(plant_id, plant_hal::Sensor::B) {
|
match board.measure_moisture_hz(plant_id, plant_hal::Sensor::B) {
|
||||||
Ok(raw) => match map_range_moisture(raw) {
|
Ok(raw) => match map_range_moisture(raw) {
|
||||||
Ok(moisture_percent) => HumiditySensorState::HumidityValue {
|
Ok(moisture_percent) => MoistureSensorState::MoistureValue {
|
||||||
raw_hz: raw,
|
raw_hz: raw,
|
||||||
moisture_percent,
|
moisture_percent,
|
||||||
},
|
},
|
||||||
Err(err) => HumiditySensorState::SensorError(err),
|
Err(err) => MoistureSensorState::SensorError(err),
|
||||||
},
|
},
|
||||||
Err(err) => HumiditySensorState::BoardError(err.to_string()),
|
Err(err) => MoistureSensorState::SensorError(MoistureSensorError::BoardError(
|
||||||
|
err.to_string(),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
HumiditySensorState::Disabled
|
MoistureSensorState::Disabled
|
||||||
};
|
};
|
||||||
let previous_pump = board.last_pump_time(plant_id);
|
let previous_pump = board.last_pump_time(plant_id);
|
||||||
let consecutive_pump_count = board.consecutive_pump_count(plant_id);
|
let consecutive_pump_count = board.consecutive_pump_count(plant_id);
|
||||||
@ -144,6 +161,9 @@ impl PlantState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn pump_in_timeout(&self, plant_conf: &PlantConfig, current_time: &DateTime<Tz>) -> bool {
|
pub fn pump_in_timeout(&self, plant_conf: &PlantConfig, current_time: &DateTime<Tz>) -> bool {
|
||||||
|
if matches!(plant_conf.mode, PlantWateringMode::OFF) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
self.pump.previous_pump.is_some_and(|last_pump| {
|
self.pump.previous_pump.is_some_and(|last_pump| {
|
||||||
last_pump
|
last_pump
|
||||||
.checked_add_signed(TimeDelta::minutes(plant_conf.pump_cooldown_min.into()))
|
.checked_add_signed(TimeDelta::minutes(plant_conf.pump_cooldown_min.into()))
|
||||||
@ -154,7 +174,26 @@ impl PlantState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_err(&self) -> bool {
|
pub fn is_err(&self) -> bool {
|
||||||
self.sensor_a.is_err() || self.sensor_b.is_err()
|
self.sensor_a.is_err().is_some() || self.sensor_b.is_err().is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn plant_moisture(
|
||||||
|
&self,
|
||||||
|
) -> (
|
||||||
|
Option<f32>,
|
||||||
|
(Option<&MoistureSensorError>, Option<&MoistureSensorError>),
|
||||||
|
) {
|
||||||
|
match (
|
||||||
|
self.sensor_a.moisture_percent(),
|
||||||
|
self.sensor_b.moisture_percent(),
|
||||||
|
) {
|
||||||
|
(Some(moisture_a), Some(moisture_b)) => {
|
||||||
|
(Some((moisture_a + moisture_b) / 2.), (None, None))
|
||||||
|
}
|
||||||
|
(Some(moisture_percent), _) => (Some(moisture_percent), (None, self.sensor_b.is_err())),
|
||||||
|
(_, Some(moisture_percent)) => (Some(moisture_percent), (self.sensor_a.is_err(), None)),
|
||||||
|
_ => (None, (self.sensor_a.is_err(), self.sensor_b.is_err())),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn needs_to_be_watered(
|
pub fn needs_to_be_watered(
|
||||||
@ -165,26 +204,20 @@ impl PlantState {
|
|||||||
match plant_conf.mode {
|
match plant_conf.mode {
|
||||||
PlantWateringMode::OFF => false,
|
PlantWateringMode::OFF => false,
|
||||||
PlantWateringMode::TargetMoisture => {
|
PlantWateringMode::TargetMoisture => {
|
||||||
let moisture_percent = match (
|
let (moisture_percent, _) = self.plant_moisture();
|
||||||
self.sensor_a.moisture_percent(),
|
if let Some(moisture_percent) = moisture_percent {
|
||||||
self.sensor_b.moisture_percent(),
|
if self.pump_in_timeout(plant_conf, current_time) {
|
||||||
) {
|
|
||||||
(Some(moisture_a), Some(moisture_b)) => (moisture_a + moisture_b) / 2.,
|
|
||||||
(Some(moisture_percent), _) => moisture_percent,
|
|
||||||
(_, Some(moisture_percent)) => moisture_percent,
|
|
||||||
_ => {
|
|
||||||
// Case for both sensors hitting an error do not water plant in this case
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if self.pump_in_timeout(plant_conf, current_time) {
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
if moisture_percent < plant_conf.target_moisture {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
false
|
||||||
|
} else {
|
||||||
|
if moisture_percent < plant_conf.target_moisture {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// in case no moisture can be determined do not water plant
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PlantWateringMode::TimerOnly => {
|
PlantWateringMode::TimerOnly => {
|
||||||
@ -196,61 +229,53 @@ impl PlantState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
//fn update_plant_state(
|
pub fn to_mqtt_info(&self, plant_conf: &PlantConfig, current_time: &DateTime<Tz>) -> PlantInfo {
|
||||||
// plantstate: &mut [PlantInfo; PLANT_COUNT],
|
PlantInfo {
|
||||||
// board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
|
sensor_a: &self.sensor_a,
|
||||||
// config: &PlantControllerConfig,
|
sensor_b: &self.sensor_b,
|
||||||
//) {
|
mode: plant_conf.mode,
|
||||||
// for plant in 0..PLANT_COUNT {
|
do_water: self.needs_to_be_watered(plant_conf, current_time),
|
||||||
// let state = &plantstate[plant];
|
dry: if let Some(moisture_percent) = self.plant_moisture().0 {
|
||||||
// let plant_config = &config.plants[plant];
|
moisture_percent < plant_conf.target_moisture
|
||||||
//
|
} else {
|
||||||
// let mode = format!("{:?}", plant_config.mode);
|
false
|
||||||
//
|
},
|
||||||
// let plant_dto = PlantStateMQTT {
|
cooldown: self.pump_in_timeout(plant_conf, current_time),
|
||||||
// a: &sensor_to_string(
|
out_of_work_hour: in_time_range(
|
||||||
// &state.a,
|
current_time,
|
||||||
// &state.sensor_error_a,
|
plant_conf.pump_hour_start,
|
||||||
// plant_config.mode != PlantWateringMode::OFF,
|
plant_conf.pump_hour_end,
|
||||||
// ),
|
),
|
||||||
// a_raw: &state.a_raw.unwrap_or(0).to_string(),
|
consecutive_pump_count: self.pump.consecutive_pump_count,
|
||||||
// b: &sensor_to_string(&state.b, &state.sensor_error_b, plant_config.sensor_b),
|
pump_error: self.pump.is_err(plant_conf),
|
||||||
// b_raw: &state.b_raw.unwrap_or(0).to_string(),
|
last_pump: self
|
||||||
// active: state.active,
|
.pump
|
||||||
// mode: &mode,
|
.previous_pump
|
||||||
// last_pump: &time_to_string_utc(board.last_pump_time(plant)),
|
.map(|t| t.with_timezone(¤t_time.timezone())),
|
||||||
// next_pump: &time_to_string(state.next_pump),
|
next_pump: if matches!(
|
||||||
// consecutive_pump_count: state.consecutive_pump_count,
|
plant_conf.mode,
|
||||||
// cooldown: state.cooldown,
|
PlantWateringMode::TimerOnly | PlantWateringMode::TargetMoisture
|
||||||
// dry: state.dry,
|
) {
|
||||||
// not_effective: state.not_effective,
|
self.pump.previous_pump.and_then(|last_pump| {
|
||||||
// out_of_work_hour: state.out_of_work_hour,
|
last_pump
|
||||||
// pump_error: state.pump_error,
|
.checked_add_signed(TimeDelta::minutes(plant_conf.pump_cooldown_min.into()))
|
||||||
// };
|
.map(|t| t.with_timezone(¤t_time.timezone()))
|
||||||
//
|
})
|
||||||
// match serde_json::to_string(&plant_dto) {
|
} else {
|
||||||
// Ok(state) => {
|
None
|
||||||
// let plant_topic = format!("/plant{}", plant + 1);
|
},
|
||||||
// let _ = board.mqtt_publish(&config, &plant_topic, state.as_bytes());
|
}
|
||||||
// //reduce speed as else messages will be dropped
|
}
|
||||||
// Delay::new_default().delay_ms(200);
|
}
|
||||||
// }
|
|
||||||
// Err(err) => {
|
|
||||||
// println!("Error publishing lightstate {}", err);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize)]
|
#[derive(Debug, PartialEq, Serialize)]
|
||||||
/// State of a single plant to be tracked
|
/// State of a single plant to be tracked
|
||||||
pub struct PlantInfo {
|
pub struct PlantInfo<'a> {
|
||||||
/// state of humidity sensor on bank a
|
/// state of humidity sensor on bank a
|
||||||
sensor_a: HumiditySensorState,
|
sensor_a: &'a MoistureSensorState,
|
||||||
/// state of humidity sensor on bank b
|
/// state of humidity sensor on bank b
|
||||||
sensor_b: HumiditySensorState,
|
sensor_b: &'a MoistureSensorState,
|
||||||
/// configured plant watering mode
|
/// configured plant watering mode
|
||||||
mode: PlantWateringMode,
|
mode: PlantWateringMode,
|
||||||
/// plant needs to be watered
|
/// plant needs to be watered
|
||||||
@ -259,13 +284,9 @@ pub struct PlantInfo {
|
|||||||
dry: bool,
|
dry: bool,
|
||||||
/// plant irrigation cooldown is active
|
/// plant irrigation cooldown is active
|
||||||
cooldown: bool,
|
cooldown: bool,
|
||||||
/// we want to irrigate but tank is empty
|
|
||||||
no_water: bool,
|
|
||||||
/// plant should not be watered at this time of day TODO: does this really belong here? Isn't this a global setting?
|
/// plant should not be watered at this time of day TODO: does this really belong here? Isn't this a global setting?
|
||||||
out_of_work_hour: bool,
|
out_of_work_hour: bool,
|
||||||
/// is pump currently running
|
/// how often has the pump been watered without reaching target moisture
|
||||||
active: bool,
|
|
||||||
/// how often has the logic determined that plant should have been irrigated but wasn't
|
|
||||||
consecutive_pump_count: u32,
|
consecutive_pump_count: u32,
|
||||||
pump_error: Option<PumpError>,
|
pump_error: Option<PumpError>,
|
||||||
/// last time when pump was active
|
/// last time when pump was active
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
pub trait LimitPrecision {
|
pub trait LimitPrecision {
|
||||||
fn to_precision(self, presision: i32) -> Self;
|
fn to_precision(self, presision: i32) -> Self;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user