Initial implementation of Sahsa Clock
Pygame framebuffer clock for Le Potato (ARM Debian) with aiohttp webhook server. Renders 12-hour clock directly to /dev/fb0 (no X11/Wayland). Supports full-screen message overlays pushed via a browser dashboard or Bearer-token API. Includes first-run setup wizard, session-based dashboard auth, bcrypt password storage, per-IP rate limiting, and systemd service unit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
44
state.py
Normal file
44
state.py
Normal file
@@ -0,0 +1,44 @@
|
||||
import threading
|
||||
import time
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
|
||||
@dataclass
|
||||
class MessageState:
|
||||
_text: str | None = field(default=None)
|
||||
_expires_at: float | None = field(default=None) # monotonic timestamp
|
||||
_lock: threading.Lock = field(default_factory=threading.Lock)
|
||||
|
||||
def set(self, text: str, duration: float | None) -> None:
|
||||
"""Set message. duration=None means persistent (no expiry)."""
|
||||
with self._lock:
|
||||
self._text = text
|
||||
self._expires_at = time.monotonic() + duration if duration else None
|
||||
|
||||
def clear(self) -> None:
|
||||
with self._lock:
|
||||
self._text = None
|
||||
self._expires_at = None
|
||||
|
||||
def get(self) -> tuple[str | None, float | None]:
|
||||
"""Return (text, remaining_seconds). Both None if no active message."""
|
||||
with self._lock:
|
||||
if self._text is None:
|
||||
return None, None
|
||||
if self._expires_at is not None and time.monotonic() >= self._expires_at:
|
||||
self._text = None
|
||||
self._expires_at = None
|
||||
return None, None
|
||||
remaining: float | None = None
|
||||
if self._expires_at is not None:
|
||||
remaining = max(0.0, self._expires_at - time.monotonic())
|
||||
return self._text, remaining
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
text, remaining = self.get()
|
||||
return {
|
||||
"active": text is not None,
|
||||
"text": text,
|
||||
"remaining_seconds": round(remaining, 1) if remaining is not None else None,
|
||||
"persistent": text is not None and remaining is None,
|
||||
}
|
||||
Reference in New Issue
Block a user