"""Plugin ABC für das Waveshare 7.3" Dashboard. Ein Plugin ist ein Python-Modul in /plugins/, das eine Klasse Widget exportiert. Die Klasse wird beim Start dynamisch geladen und in der Admin-UI zur Auswahl angeboten. Minimal-Beispiel siehe plugins/hello.py. """ from __future__ import annotations from abc import ABC, abstractmethod from typing import Any class Widget(ABC): # ---- Metadaten (Klassenattribute) ---- name: str = "" # Eindeutiger Identifier, lowercase, keine Leerzeichen label: str = "" # Anzeigename in der UI description: str = "" # Kurzbeschreibung in der UI category: str = "general" # "info" | "system" | "weather" | "smart-home" | ... # Optional: Schema der Config-Felder (für UI-Form-Generierung). # Liste von Dicts mit keys: key, label, type, default, secret, choices, help # type ∈ {"string", "int", "float", "bool", "secret", "select", "lat_lon"} config_schema: list[dict] = [] default_config: dict = {} def __init__(self, config: dict): # Merge defaults mit übergebener Config merged = dict(self.default_config) merged.update(config or {}) self.config = merged @abstractmethod def fetch(self) -> dict: """Daten holen. Sollte schnell sein — wird alle refresh_interval Sekunden aufgerufen, plus einmal vor jedem Render. Exceptions werden geloggt und führen zur Beibehaltung der letzten Daten.""" @abstractmethod def render(self, draw, fonts, x: int, y: int, w: int, h: int) -> None: """Zeichne in den gegebenen Slot (x,y,w,h) auf den draw-Context. Renderer nutzt RGB-Palette aus palette.py.""" # ---- Optionale Lifecycle-Hooks ---- def on_load(self) -> None: """Wird einmal beim Plugin-Load aufgerufen.""" def on_unload(self) -> None: """Wird beim Beenden aufgerufen.""" # ---- Helper für Plugins ---- def cfg(self, key: str, default: Any = None) -> Any: return self.config.get(key, default) def all_widget_classes() -> list[type[Widget]]: """Lade alle Plugin-Klassen aus dem plugins/-Ordner.""" import os, importlib, pkgutil plugins_pkg = os.path.join(os.path.dirname(__file__), "..", "plugins") plugins_pkg = os.path.abspath(plugins_pkg) classes: list[type[Widget]] = [] for _, modname, _ in pkgutil.iter_modules([plugins_pkg]): mod = importlib.import_module(f"plugins.{modname}") cls = getattr(mod, "Widget", None) if cls and isinstance(cls, type) and issubclass(cls, Widget) and cls is not Widget: classes.append(cls) # alphabetisch classes.sort(key=lambda c: c.label or c.name) return classes