Dateien nach "app/animations" hochladen
This commit is contained in:
1 parent
8f8d206166
commit
1c5b8c5685
5 files changed
+256
No files matched your search
@@ -0,0 +1,76 @@
|
||||
"""Base class for all animations."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import abc
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from shelly import ShellyRGBW2
|
||||
|
||||
log = logging.getLogger("animation")
|
||||
|
||||
|
||||
class Animation(abc.ABC):
|
||||
"""Subclass this and implement `tick()` to create a new animation.
|
||||
|
||||
`tick()` is called roughly every `interval` seconds while the animation
|
||||
is active. Keep it fast and non-blocking.
|
||||
"""
|
||||
|
||||
name: str = "base"
|
||||
interval: float = 0.12 # seconds between ticks
|
||||
|
||||
def __init__(self, shelly: "ShellyRGBW2"):
|
||||
self.shelly = shelly
|
||||
self._stop = threading.Event()
|
||||
self._thread: threading.Thread | None = None
|
||||
|
||||
def start(self) -> None:
|
||||
if self._thread and self._thread.is_alive():
|
||||
return
|
||||
self._stop.clear()
|
||||
self.on_start()
|
||||
self._thread = threading.Thread(
|
||||
target=self._run, name=f"anim-{self.name}", daemon=True
|
||||
)
|
||||
self._thread.start()
|
||||
log.info("Animation '%s' started", self.name)
|
||||
|
||||
def stop(self) -> None:
|
||||
self._stop.set()
|
||||
if self._thread:
|
||||
self._thread.join(timeout=2.0)
|
||||
self._thread = None
|
||||
self.on_stop()
|
||||
log.info("Animation '%s' stopped", self.name)
|
||||
|
||||
@property
|
||||
def running(self) -> bool:
|
||||
return self._thread is not None and self._thread.is_alive()
|
||||
|
||||
def _run(self) -> None:
|
||||
while not self._stop.is_set():
|
||||
try:
|
||||
self.tick()
|
||||
except Exception:
|
||||
log.exception("Error in animation '%s'", self.name)
|
||||
self._stop.wait(self.interval)
|
||||
|
||||
# ---- hooks for subclasses ----
|
||||
|
||||
def on_start(self) -> None:
|
||||
"""Called once when the animation starts (before first tick)."""
|
||||
self.shelly.turn_on()
|
||||
|
||||
def on_stop(self) -> None:
|
||||
"""Called once when the animation stops."""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def tick(self) -> None:
|
||||
"""Called every `interval` seconds. Update the light here."""
|
||||
...
|
||||
Reference in new issue
Block a user