diff --git a/alert_api/alarm_siren.py b/alert_api/alarm_siren.py index 5c5ee8f..5dd6dcd 100644 --- a/alert_api/alarm_siren.py +++ b/alert_api/alarm_siren.py @@ -152,6 +152,11 @@ class AlarmSiren: # Check for alarms to trigger now = datetime.now() for alarm_id, alarm_info in list(self.active_alarms.items()): + # Check if alarm is more than 1 hour late + if now > alarm_info['trigger_time'] + timedelta(hours=1): + logger.warning(f"Alarm {alarm_id} is over 1 hour late. Disabling.") + del self.active_alarms[alarm_id] + continue if now >= alarm_info['trigger_time']: # Trigger alarm if not already active if alarm_info['process'] is None: @@ -179,7 +184,14 @@ class AlarmSiren: logger.info(f"Removing non-repeating alarm {alarm_id}.") del self.active_alarms[alarm_id] - time.sleep(1) # Prevent tight loop + # Actively clean up zombie processes + for alarm_id, alarm_info in list(self.active_alarms.items()): + process = alarm_info.get('process') + if process and process.poll() is not None: + # Remove terminated processes + alarm_info['process'] = None + + time.sleep(0.5) # Prevent tight loop except Exception as e: logger.error(f"Error in playback worker: {e}") time.sleep(1) @@ -230,13 +242,19 @@ def dismiss_alarm(self, alarm_id: int): logger.warning(f"Cannot dismiss alarm {alarm_id} - not found in active alarms") return False - # Stop playback + # Stop playback and terminate any running process alarm_info = self.active_alarms.pop(alarm_id, None) - process = alarm_info.get('process') - if process: - process.terminate() - process.wait() + if alarm_info and alarm_info.get('process'): + try: + alarm_info['process'].terminate() + alarm_info['process'].wait(timeout=2) + except Exception as e: + logger.error(f"Error terminating alarm process: {e}") + # Force kill if terminate fails + try: + alarm_info['process'].kill() + except Exception: + pass logger.info(f"Dismissed alarm {alarm_id}.") return True - diff --git a/alert_api/main.py b/alert_api/main.py index 334def3..5c193f5 100755 --- a/alert_api/main.py +++ b/alert_api/main.py @@ -7,6 +7,7 @@ import threading import logging from http.server import HTTPServer from multiprocessing import Queue +import subprocess # Import our custom modules from alarm_api import AlertApi, run as run_api @@ -153,6 +154,12 @@ class AlarmSystemManager: if self.api_thread and self.api_thread.is_alive(): self.api_thread.join(timeout=2) + # Kill any remaining mpg123 processes + try: + subprocess.run(['pkill', 'mpg123'], check=False) + except Exception as e: + logger.error(f"Error killing mpg123 processes: {e}") + logger.info("Alarm System shutdown complete") def main(): diff --git a/alert_api/ncurses_ui_draw.py b/alert_api/ncurses_ui_draw.py index e8f6cbb..0e6b2e5 100644 --- a/alert_api/ncurses_ui_draw.py +++ b/alert_api/ncurses_ui_draw.py @@ -29,6 +29,7 @@ def _draw_error(stdscr, error_message): def _draw_active_alarms(stdscr, context): """Draw the active alarms screen""" + _init_colors() height, width = stdscr.getmaxyx() active_alarms = context.get('active_alarms', {})