diff --git a/wecker.py b/wecker.py index 5acdedc..a6b0439 100644 --- a/wecker.py +++ b/wecker.py @@ -4,6 +4,7 @@ import logging import pygame import random import sys +import os # Configure logging logging.basicConfig( @@ -12,6 +13,49 @@ logging.basicConfig( handlers=[logging.FileHandler("wecker.log"), logging.StreamHandler()], ) +PID_FILE = "wecker.pid" + + +def ensure_single_instance(): + """Ensures that only one instance of the script is running.""" + if os.path.exists(PID_FILE): + try: + with open(PID_FILE, 'r') as f: + old_pid = int(f.read().strip()) + + # Check if the process is still running + os.kill(old_pid, 0) + # If os.kill succeeds, the process is running. + logging.error(f"Another instance of wecker (PID {old_pid}) is already running. Exiting.") + sys.exit(1) + except (ValueError, ProcessLookupError): + # ValueError: PID file is not an integer + # ProcessLookupError: PID doesn't exist + pass + except PermissionError: + # Process exists but we don't have permission to signal it. + logging.error(f"Another instance of wecker (PID {old_pid}) is already running. Exiting.") + sys.exit(1) + except OSError: + # Other OS errors, might mean process is dead + pass + + with open(PID_FILE, 'w') as f: + f.write(str(os.getpid())) + + +def remove_pid_file(): + """Removes the PID file.""" + if os.path.exists(PID_FILE): + try: + with open(PID_FILE, 'r') as f: + current_pid = int(f.read().strip()) + if current_pid == os.getpid(): + os.remove(PID_FILE) + except (ValueError, OSError): + pass + + # GPIO Setup BUTTON_PIN = 17 LED_PIN = 27 @@ -181,9 +225,11 @@ def run_alarm(test_mode=False): if __name__ == "__main__": + ensure_single_instance() try: run_alarm() finally: pygame.mixer.quit() GPIO.cleanup() + remove_pid_file() logging.info("Program finished.")