From 989c8317323ed28d58d7cd90ff4e737fa80020d8 Mon Sep 17 00:00:00 2001 From: Aaro Varis Date: Thu, 5 Feb 2026 09:29:29 +0200 Subject: [PATCH] Add audio playback functionality and enhance alarm action reset logic --- init.py | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/init.py b/init.py index 25eeed1..95c17ac 100644 --- a/init.py +++ b/init.py @@ -5,6 +5,7 @@ from datetime import date, datetime, timedelta from zoneinfo import ZoneInfo from typing import Callable import requests +import subprocess from grove.gpio import GPIO from grove.display.jhd1802 import JHD1802 @@ -49,6 +50,19 @@ def isSunUp() -> bool: now = datetime.now(city.tzinfo) return s["sunrise"] <= now <= s["sunset"] +def playAudio(filePath: str) -> None: + """ + Play an audio file through the Raspberry Pi's standard aux output. + + Args: + filePath: Path to the audio file (.wav, .mp3, .ogg, etc.) + """ + if filePath.endswith(".wav"): + subprocess.Popen(["aplay", filePath], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + else: + # Use mpg123 for mp3, or ffplay/mpv as fallback for other formats + subprocess.Popen(["mpg123", "-q", filePath], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + class WakeUpAction: offsetSeconds: int # how many seconds before alarm time to trigger action: Callable[[], None] @@ -74,7 +88,8 @@ class AlarmClock: longPressThreshold: float = 1.0 # 1 second for long press buttonPressStartTime: float = 0.0 configMode: str = "normal" # modes: normal, set_hour, set_minute - alarmTime: datetime = datetime.now(ZoneInfo("Europe/Helsinki")).replace(hour=9, minute=13, second=0, microsecond=0) + alarmTime: datetime = datetime.now(ZoneInfo("Europe/Helsinki")).replace(hour=9, minute=32, second=0, microsecond=0) + actionsResetForToday: bool = False def __init__(self, pins: dict = {}): self.ledGpio = GPIO(pins.get("led", 5), GPIO.OUT) self.buttonGpio = GPIO(pins.get("button", 6), GPIO.IN) @@ -88,12 +103,17 @@ class AlarmClock: self.wakeupActions.append(WakeUpAction(offsetSeconds, action)) def resetWakeUpActions(self): - """Reset all wakeup actions so they can trigger again tomorrow""" + """Reset all wakeup actions so they can trigger again""" for action in self.wakeupActions: action.triggered = False + self.actionsResetForToday = True + print("Wakeup actions reset") def checkWakeUpActions(self): """Check and trigger any wakeup actions that are due""" + if not self.wakeupActions: + return + now = datetime.now(ZoneInfo(city.timezone)) today_alarm = now.replace( hour=self.alarmTime.hour, @@ -102,6 +122,20 @@ class AlarmClock: microsecond=0 ) + # Find the earliest action (largest offset) + max_offset = max(action.offsetSeconds for action in self.wakeupActions) + first_trigger_time = today_alarm - timedelta(seconds=max_offset) + reset_time = first_trigger_time - timedelta(seconds=10) + alarm_window_end = today_alarm + timedelta(minutes=1) + + # Reset actions 10 seconds before the first one should trigger + if now >= reset_time and now < first_trigger_time and not self.actionsResetForToday: + self.resetWakeUpActions() + + # After alarm window ends, allow reset to happen again tomorrow + if now >= alarm_window_end: + self.actionsResetForToday = False + for wakeupAction in self.wakeupActions: if wakeupAction.triggered: continue @@ -226,6 +260,7 @@ alarm_clock.addWakeUpAction(120, lambda: print("2 minutes to alarm!")) # 2 minu alarm_clock.addWakeUpAction(90, lambda: setShellyPlugState(shelly_deviceId, True)) # activate plug 90s before alarm alarm_clock.addWakeUpAction(30, lambda: print("30 seconds to alarm!")) # 30 seconds before alarm alarm_clock.addWakeUpAction(0, lambda: print("Alarm triggered!")) # At alarm time +alarm_clock.addWakeUpAction(0, lambda: playAudio("./boxing_bell_multiple.wav")) # Play sound at alarm time alarm_clock.start()