diff --git a/README.md b/README.md index 506441d..8b09924 100644 --- a/README.md +++ b/README.md @@ -27,11 +27,16 @@ This project uses Python 3 and `uv` for dependency management. To set up the env Please refer to the [arcade-button-wiring.md](arcade-button-wiring.md) file for detailed instructions on how to connect the arcade button and its LED to the Raspberry Pi's GPIO pins. ## Usage -1. Make sure your audio file (`Laid Back - Sunshine Reggae.mp3` or any other MP3 you prefer) is in the project directory. +1. Make sure your audio file (`Laid Back - Sunshine Reggae.mp3` or any other MP3 you prefer) is in the project directory, or set a custom path. 2. Run the alarm clock script manually: ```bash + # Use the default music file or the MUSIC_FILE environment variable python3 wecker.py + + # Or pass a custom music file directly + python3 wecker.py --music-file /path/to/your/alarm.mp3 ``` + The music file is resolved in this order: `--music-file` argument, `MUSIC_FILE` environment variable, default `Laid Back - Sunshine Reggae.mp3`. ## Automating and Managing Alarms (GraphQL API) diff --git a/tests/test_wecker.py b/tests/test_wecker.py index 1c80612..e6588b1 100644 --- a/tests/test_wecker.py +++ b/tests/test_wecker.py @@ -124,3 +124,15 @@ def test_blinking_updates_time_correctly(mock_time, mock_gpio, mock_pygame): assert clock.state == wecker.STATE_WAIT_FOR_INPUT assert clock.last_interaction_time == 200.0 + + +def test_setup_uses_env_music_file(mock_gpio, mock_pygame, monkeypatch): + monkeypatch.setenv("MUSIC_FILE", "/custom/track.mp3") + wecker.setup() + mock_pygame.mixer.music.load.assert_called_with("/custom/track.mp3") + + +def test_setup_music_file_argument_overrides_env(mock_gpio, mock_pygame, monkeypatch): + monkeypatch.setenv("MUSIC_FILE", "/env/track.mp3") + wecker.setup(music_file="/arg/track.mp3") + mock_pygame.mixer.music.load.assert_called_with("/arg/track.mp3") diff --git a/wecker.py b/wecker.py index 6534c94..cb5ddeb 100644 --- a/wecker.py +++ b/wecker.py @@ -63,8 +63,14 @@ BUTTON_PIN = 17 LED_PIN = 27 -def setup(): +DEFAULT_MUSIC_FILE = "Laid Back - Sunshine Reggae.mp3" + + +def setup(music_file: str | None = None): """Initialize GPIO and audio hardware. Call once before running.""" + if music_file is None: + music_file = os.environ.get("MUSIC_FILE", DEFAULT_MUSIC_FILE) + GPIO.setmode(GPIO.BCM) GPIO.setup(LED_PIN, GPIO.OUT) GPIO.setup(BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) @@ -74,7 +80,7 @@ def setup(): pygame.mixer.init() try: - pygame.mixer.music.load("Laid Back - Sunshine Reggae.mp3") + pygame.mixer.music.load(music_file) pygame.mixer.music.set_volume(1.0) except Exception as e: logging.error(f"Error loading audio file: {e}") @@ -200,8 +206,8 @@ class AlarmClock: return True # Keep running -def run_alarm(test_mode=False): - setup() +def run_alarm(test_mode=False, music_file: str | None = None): + setup(music_file=music_file) logging.info("Alarm clock started. Music is playing in an endless loop.") if "pygame" in globals() and hasattr(pygame, "mixer") and pygame.mixer.get_init(): pygame.mixer.music.play(-1) @@ -231,9 +237,19 @@ def run_alarm(test_mode=False): if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description="Raspberry Pi alarm clock.") + parser.add_argument( + "--music-file", + default=os.environ.get("MUSIC_FILE", DEFAULT_MUSIC_FILE), + help="Path to the MP3 file to play (default: $MUSIC_FILE or default track).", + ) + args = parser.parse_args() + ensure_single_instance() try: - run_alarm() + run_alarm(music_file=args.music_file) finally: pygame.mixer.quit() GPIO.cleanup()