alarm.rs
1 /// Alarm system for critical failure detection 2 use anyhow::Result; 3 use esp_idf_hal::gpio::{AnyOutputPin, Output, PinDriver}; 4 use log::{error, info, warn}; 5 use std::sync::{Arc, Mutex}; 6 7 /// Alarm conditions that can trigger the external alarm 8 #[derive(Debug, Clone, Copy, PartialEq)] 9 pub enum AlarmCondition { 10 WifiDisconnectedTooLong, 11 MqttFailureTooLong, 12 SensorFailureTooLong, 13 AutomationFailure, 14 CriticalTemperatureLow, 15 CriticalTemperatureHigh, 16 } 17 18 impl AlarmCondition { 19 pub fn description(&self) -> &'static str { 20 match self { 21 AlarmCondition::WifiDisconnectedTooLong => "WiFi disconnected for too long", 22 AlarmCondition::MqttFailureTooLong => "MQTT publishing failed for too long", 23 AlarmCondition::SensorFailureTooLong => "Sensor reading failed for too long", 24 AlarmCondition::AutomationFailure => "Automation failed to activate device", 25 AlarmCondition::CriticalTemperatureLow => "Critical low temperature", 26 AlarmCondition::CriticalTemperatureHigh => "Critical high temperature", 27 } 28 } 29 } 30 31 /// External alarm controller 32 pub struct AlarmController { 33 relay: Arc<Mutex<PinDriver<'static, AnyOutputPin, Output>>>, 34 active: Arc<Mutex<bool>>, 35 active_conditions: Arc<Mutex<Vec<AlarmCondition>>>, 36 } 37 38 impl AlarmController { 39 /// Create new alarm controller 40 pub fn new(pin: AnyOutputPin, active_high: bool) -> Result<Self> { 41 let mut driver = PinDriver::output(pin)?; 42 43 // Initialize to OFF state 44 if active_high { 45 driver.set_low()?; 46 } else { 47 driver.set_high()?; 48 } 49 50 info!( 51 "Alarm controller initialized (active_high: {})", 52 active_high 53 ); 54 55 Ok(Self { 56 relay: Arc::new(Mutex::new(driver)), 57 active: Arc::new(Mutex::new(false)), 58 active_conditions: Arc::new(Mutex::new(Vec::new())), 59 }) 60 } 61 62 /// Activate alarm with a specific condition 63 pub fn activate(&self, condition: AlarmCondition) -> Result<()> { 64 let mut conditions = self.active_conditions.lock().unwrap(); 65 66 // Add condition if not already present 67 if !conditions.contains(&condition) { 68 conditions.push(condition); 69 error!("ALARM ACTIVATED: {}", condition.description()); 70 } 71 72 // Turn on alarm if not already active 73 let mut active = self.active.lock().unwrap(); 74 if !*active { 75 let mut relay = self.relay.lock().unwrap(); 76 relay.set_high()?; // Assuming active high for external relay 77 *active = true; 78 warn!("External alarm lamp turned ON"); 79 } 80 81 Ok(()) 82 } 83 84 /// Clear a specific alarm condition 85 pub fn clear_condition(&self, condition: AlarmCondition) -> Result<()> { 86 let mut conditions = self.active_conditions.lock().unwrap(); 87 88 if let Some(pos) = conditions.iter().position(|c| *c == condition) { 89 conditions.remove(pos); 90 info!("Alarm condition cleared: {}", condition.description()); 91 } 92 93 // Turn off alarm if no more active conditions 94 if conditions.is_empty() { 95 let mut active = self.active.lock().unwrap(); 96 if *active { 97 let mut relay = self.relay.lock().unwrap(); 98 relay.set_low()?; 99 *active = false; 100 info!("External alarm lamp turned OFF - all conditions cleared"); 101 } 102 } 103 104 Ok(()) 105 } 106 107 /// Clear all alarm conditions and turn off alarm 108 pub fn clear_all(&self) -> Result<()> { 109 let mut conditions = self.active_conditions.lock().unwrap(); 110 conditions.clear(); 111 112 let mut active = self.active.lock().unwrap(); 113 if *active { 114 let mut relay = self.relay.lock().unwrap(); 115 relay.set_low()?; 116 *active = false; 117 info!("External alarm lamp turned OFF - all conditions manually cleared"); 118 } 119 120 Ok(()) 121 } 122 123 /// Check if alarm is currently active 124 pub fn is_active(&self) -> bool { 125 *self.active.lock().unwrap() 126 } 127 128 /// Get list of active alarm conditions 129 pub fn get_active_conditions(&self) -> Vec<AlarmCondition> { 130 self.active_conditions.lock().unwrap().clone() 131 } 132 133 /// Test alarm (turn on briefly for testing) 134 pub fn test(&self) -> Result<()> { 135 info!("Testing alarm lamp..."); 136 let mut relay = self.relay.lock().unwrap(); 137 relay.set_high()?; 138 std::thread::sleep(std::time::Duration::from_millis(500)); 139 relay.set_low()?; 140 info!("Alarm test complete"); 141 Ok(()) 142 } 143 } 144 145 impl Drop for AlarmController { 146 fn drop(&mut self) { 147 // Ensure alarm is off when controller is dropped 148 if let Ok(mut relay) = self.relay.lock() { 149 let _ = relay.set_low(); 150 } 151 } 152 } 153 154 /// Alarm monitoring state 155 pub struct AlarmMonitor { 156 pub wifi_disconnected_since: Option<i64>, 157 pub mqtt_failed_since: Option<i64>, 158 pub sensor_failed_since: Option<i64>, 159 pub last_temp_check: i64, 160 } 161 162 impl AlarmMonitor { 163 pub fn new() -> Self { 164 Self { 165 wifi_disconnected_since: None, 166 mqtt_failed_since: None, 167 sensor_failed_since: None, 168 last_temp_check: 0, 169 } 170 } 171 }