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 }