feat: add pluggable alarm styles and migrate wecker.py
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.
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
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.
|
||||
"""
|
||||
Reference in New Issue
Block a user