/ src / hardware / automation.rs
automation.rs
  1  use anyhow::Result;
  2  use log::info;
  3  
  4  use crate::hardware::{DeviceType, MultiRelayController};
  5  
  6  /// Automation rules for controlling multiple devices based on sensor data
  7  pub struct AutomationEngine {
  8      temp_high_threshold_c: f32,
  9      temp_low_threshold_c: f32,
 10      humidity_high_threshold: f32,
 11      gas_threshold_kohm: f32,
 12  }
 13  
 14  impl AutomationEngine {
 15      pub fn new(temp_high: f32, temp_low: f32, humidity_high: f32, gas_threshold: f32) -> Self {
 16          Self {
 17              temp_high_threshold_c: temp_high,
 18              temp_low_threshold_c: temp_low,
 19              humidity_high_threshold: humidity_high,
 20              gas_threshold_kohm: gas_threshold,
 21          }
 22      }
 23  
 24      /// Update thresholds (useful when settings change)
 25      pub fn update_thresholds(
 26          &mut self,
 27          temp_high: f32,
 28          temp_low: f32,
 29          humidity_high: f32,
 30          gas_threshold: f32,
 31      ) {
 32          self.temp_high_threshold_c = temp_high;
 33          self.temp_low_threshold_c = temp_low;
 34          self.humidity_high_threshold = humidity_high;
 35          self.gas_threshold_kohm = gas_threshold;
 36      }
 37  
 38      /// Evaluate sensor data and control devices
 39      /// Returns true if any device state changed
 40      pub fn evaluate(
 41          &self,
 42          temp_c: f32,
 43          humidity_percent: f32,
 44          gas_resistance_kohm: f32,
 45          relay_controller: &MultiRelayController,
 46      ) -> Result<bool> {
 47          let mut state_changed = false;
 48  
 49          // Exhaust fan: Turn on when temperature OR humidity is high
 50          let should_activate_exhaust =
 51              temp_c > self.temp_high_threshold_c || humidity_percent > self.humidity_high_threshold;
 52  
 53          if let Some(exhaust_fan) = relay_controller.get_channel(DeviceType::ExhaustFan) {
 54              let was_on = exhaust_fan.is_on();
 55              if should_activate_exhaust && !was_on {
 56                  relay_controller.turn_on(DeviceType::ExhaustFan)?;
 57                  info!(
 58                      "Automation: Exhaust fan ON (temp={:.1}°C > {:.1}°C or humidity={:.1}% > {:.1}%)",
 59                      temp_c, self.temp_high_threshold_c, humidity_percent, self.humidity_high_threshold
 60                  );
 61                  state_changed = true;
 62              } else if !should_activate_exhaust && was_on {
 63                  relay_controller.turn_off(DeviceType::ExhaustFan)?;
 64                  info!("Automation: Exhaust fan OFF (temp and humidity normal)");
 65                  state_changed = true;
 66              }
 67          }
 68  
 69          // Circulation fan: Turn on when exhaust fan is on OR when air quality is poor
 70          let should_activate_circulation =
 71              should_activate_exhaust || gas_resistance_kohm < self.gas_threshold_kohm;
 72  
 73          if let Some(circulation_fan) = relay_controller.get_channel(DeviceType::CirculationFan) {
 74              let was_on = circulation_fan.is_on();
 75              if should_activate_circulation && !was_on {
 76                  relay_controller.turn_on(DeviceType::CirculationFan)?;
 77                  info!("Automation: Circulation fan ON (improving air circulation)");
 78                  state_changed = true;
 79              } else if !should_activate_circulation && was_on {
 80                  relay_controller.turn_off(DeviceType::CirculationFan)?;
 81                  info!("Automation: Circulation fan OFF");
 82                  state_changed = true;
 83              }
 84          }
 85  
 86          // Heater: Turn on when temperature is low (mutually exclusive with exhaust fan logic)
 87          let should_activate_heater = temp_c < self.temp_low_threshold_c;
 88  
 89          if let Some(heater) = relay_controller.get_channel(DeviceType::Heater) {
 90              let was_on = heater.is_on();
 91              if should_activate_heater && !was_on {
 92                  relay_controller.turn_on(DeviceType::Heater)?;
 93                  info!(
 94                      "Automation: Heater ON (temp={:.1}°C < {:.1}°C)",
 95                      temp_c, self.temp_low_threshold_c
 96                  );
 97                  state_changed = true;
 98              } else if !should_activate_heater && was_on {
 99                  relay_controller.turn_off(DeviceType::Heater)?;
100                  info!("Automation: Heater OFF (temp normal)");
101                  state_changed = true;
102              }
103          }
104  
105          // Air purifier: Turn on when gas resistance is low (poor air quality)
106          let should_activate_purifier = gas_resistance_kohm < self.gas_threshold_kohm;
107  
108          if let Some(purifier) = relay_controller.get_channel(DeviceType::AirPurifier) {
109              let was_on = purifier.is_on();
110              if should_activate_purifier && !was_on {
111                  relay_controller.turn_on(DeviceType::AirPurifier)?;
112                  info!(
113                      "Automation: Air purifier ON (gas={:.1}kΩ < {:.1}kΩ)",
114                      gas_resistance_kohm, self.gas_threshold_kohm
115                  );
116                  state_changed = true;
117              } else if !should_activate_purifier && was_on {
118                  relay_controller.turn_off(DeviceType::AirPurifier)?;
119                  info!("Automation: Air purifier OFF (air quality normal)");
120                  state_changed = true;
121              }
122          }
123  
124          Ok(state_changed)
125      }
126  }