Introduce a styles/ plugin package: - styles/base.py: AlarmStyle ABC (injected set_led, update()->bool contract) - styles/blink.py: BlinkStyle, the existing count-the-blinks puzzle moved out of wecker.py's AlarmClock - styles/simple.py: SimpleStyle, press-once-to-stop (the new default style) - styles/__init__.py: STYLES registry + get_style() validator + LEGACY_STYLE wecker.py now resolves the style via the registry and owns only the shared music/button/cleanup loop; the --style CLI arg defaults to blink so existing cron entries keep their behaviour. Unknown styles raise before hardware init.
30 lines
938 B
Python
30 lines
938 B
Python
from abc import ABC, abstractmethod
|
|
from typing import Callable
|
|
|
|
|
|
class AlarmStyle(ABC):
|
|
"""A pluggable alarm behaviour.
|
|
|
|
A style decides *when* the alarm stops and what the LED does while it
|
|
rings. It never touches hardware directly: ``set_led`` is injected by the
|
|
runner, so styles stay import-safe in the API process (no GPIO/pygame).
|
|
|
|
Lifecycle: construct once with ``set_led``, then call ``update`` every
|
|
tick. Return ``False`` from ``update`` to stop the alarm.
|
|
"""
|
|
|
|
def __init__(self, set_led: Callable[[bool], None]):
|
|
self.set_led = set_led
|
|
|
|
@abstractmethod
|
|
def update(self, now: float, is_pressed: bool) -> bool:
|
|
"""Advance the style by one tick.
|
|
|
|
Args:
|
|
now: current time (``time.time()`` from the runner).
|
|
is_pressed: whether the button is currently held down.
|
|
|
|
Returns:
|
|
True to keep ringing, False to stop.
|
|
"""
|