feat: pass alarm style through crontab manager and GraphQL API
- crontab_manager.get_alarms now parses --style from the cron command (defaults to 'blink' for legacy entries without --style) - GraphQL Alarm type gains a 'style' field - setAlarm accepts a 'style' arg (default 'simple') and validates it against the styles registry; unknown styles raise a GraphQL error - _default_command embeds --style <name> into the cron command - startRinging accepts 'style' (default 'simple') and passes --style to the spawned wecker.py The crontab remains the single source of truth; the style simply rides in the cron command alongside --music-file.
This commit is contained in:
+15
-7
@@ -1,6 +1,7 @@
|
||||
import strawberry
|
||||
from typing import List, Optional
|
||||
import os
|
||||
import shlex
|
||||
import sys
|
||||
import time
|
||||
import subprocess
|
||||
@@ -8,6 +9,7 @@ import signal
|
||||
from pathlib import Path
|
||||
from api.crontab_manager import CrontabManager
|
||||
from common import PID_FILE
|
||||
from styles import get_style
|
||||
|
||||
|
||||
def get_manager():
|
||||
@@ -42,7 +44,7 @@ def _project_root() -> Path:
|
||||
return start.parent.parent
|
||||
|
||||
|
||||
def _default_command() -> str:
|
||||
def _default_command(style: str) -> str:
|
||||
"""Return the default shell command to run wecker.py from crontab."""
|
||||
project_root = _project_root()
|
||||
python_exec = sys.executable
|
||||
@@ -50,18 +52,18 @@ def _default_command() -> str:
|
||||
return (
|
||||
f"cd {project_root} && "
|
||||
f"XDG_RUNTIME_DIR={runtime_dir} SDL_AUDIODRIVER=pulse "
|
||||
f"{python_exec} wecker.py >> wecker.log 2>&1"
|
||||
f"{python_exec} wecker.py --style {shlex.quote(style)} >> wecker.log 2>&1"
|
||||
)
|
||||
|
||||
|
||||
def _start_wecker_process() -> subprocess.Popen:
|
||||
def _start_wecker_process(style: str) -> subprocess.Popen:
|
||||
"""Start wecker.py without invoking a shell."""
|
||||
project_root = _project_root()
|
||||
env = os.environ.copy()
|
||||
env.setdefault("SDL_AUDIODRIVER", "pulse")
|
||||
env.setdefault("XDG_RUNTIME_DIR", f"/run/user/{os.getuid()}")
|
||||
return subprocess.Popen(
|
||||
[sys.executable, str(project_root / "wecker.py")],
|
||||
[sys.executable, str(project_root / "wecker.py"), "--style", style],
|
||||
cwd=project_root,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
@@ -75,6 +77,7 @@ class Alarm:
|
||||
id: str
|
||||
cron_expression: str
|
||||
is_enabled: bool
|
||||
style: str
|
||||
|
||||
|
||||
def _alarm_from_dict(data: dict) -> Alarm:
|
||||
@@ -83,6 +86,7 @@ def _alarm_from_dict(data: dict) -> Alarm:
|
||||
id=data["id"],
|
||||
cron_expression=data["cron_expression"],
|
||||
is_enabled=data["is_enabled"],
|
||||
style=data["style"],
|
||||
)
|
||||
|
||||
|
||||
@@ -118,8 +122,10 @@ class Mutation:
|
||||
cron_expression: str,
|
||||
is_enabled: bool = True,
|
||||
id: Optional[str] = None,
|
||||
style: str = "simple",
|
||||
) -> Alarm:
|
||||
command = _default_command()
|
||||
get_style(style) # validate; raises ValueError on unknown style
|
||||
command = _default_command(style)
|
||||
|
||||
manager = get_manager()
|
||||
new_id = manager.set_alarm(
|
||||
@@ -132,6 +138,7 @@ class Mutation:
|
||||
id=new_id,
|
||||
cron_expression=cron_expression,
|
||||
is_enabled=is_enabled,
|
||||
style=style,
|
||||
)
|
||||
|
||||
@strawberry.field
|
||||
@@ -146,12 +153,13 @@ class Mutation:
|
||||
return False
|
||||
|
||||
@strawberry.field
|
||||
def start_ringing(self) -> bool:
|
||||
def start_ringing(self, style: str = "simple") -> bool:
|
||||
"""Start the wecker alarm if it is not already ringing.
|
||||
Returns True if started, False if already ringing."""
|
||||
if is_wecker_ringing():
|
||||
return False
|
||||
_start_wecker_process()
|
||||
get_style(style) # validate; raises ValueError on unknown style
|
||||
_start_wecker_process(style)
|
||||
return True
|
||||
|
||||
@strawberry.field
|
||||
|
||||
Reference in New Issue
Block a user