trixy_plugin.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. # -*- coding: utf-8 -*-
  2. """
  3. Basisklasse für Trixy-Plugins.
  4. Plugins erweitern diese Klasse und implementieren ihre Funktionalität.
  5. """
  6. import json
  7. from abc import ABC, abstractmethod
  8. from pathlib import Path
  9. from typing import Any, TYPE_CHECKING
  10. from trixy_core.utils.debug import pdebug, perror, pinfo
  11. if TYPE_CHECKING:
  12. from trixy_core.application import IApplication
  13. class TrixyPlugin(ABC):
  14. """
  15. Abstrakte Basisklasse für alle Trixy-Plugins.
  16. Ein Plugin muss in einem eigenen Verzeichnis unter ./plugins/ liegen
  17. und folgende Struktur haben:
  18. - main.py: Plugin-Klasse die TrixyPlugin erweitert
  19. - config.json: Plugin-Konfiguration
  20. Attribute:
  21. NAME: Eindeutiger Plugin-Name
  22. VERSION: Plugin-Version
  23. DESCRIPTION: Kurze Beschreibung
  24. AUTHOR: Autor des Plugins
  25. """
  26. NAME: str = ""
  27. VERSION: str = "1.0.0"
  28. DESCRIPTION: str = ""
  29. AUTHOR: str = ""
  30. def __init__(
  31. self,
  32. application: "IApplication",
  33. plugin_path: Path,
  34. config: dict[str, Any] | None = None
  35. ) -> None:
  36. """
  37. Initialisiert das Plugin.
  38. Args:
  39. application: Referenz zur Hauptanwendung
  40. plugin_path: Pfad zum Plugin-Verzeichnis
  41. config: Optionale Konfiguration
  42. """
  43. self._application = application
  44. self._plugin_path = plugin_path
  45. self._config: dict[str, Any] = config or {}
  46. self._enabled = True
  47. @property
  48. def application(self) -> "IApplication":
  49. """Gibt die Anwendungsreferenz zurück."""
  50. return self._application
  51. @property
  52. def plugin_path(self) -> Path:
  53. """Gibt den Plugin-Pfad zurück."""
  54. return self._plugin_path
  55. @property
  56. def config(self) -> dict[str, Any]:
  57. """Gibt die Plugin-Konfiguration zurück."""
  58. return self._config
  59. @config.setter
  60. def config(self, value: dict[str, Any]) -> None:
  61. """Setzt die Plugin-Konfiguration."""
  62. self._config = value
  63. @property
  64. def enabled(self) -> bool:
  65. """Ist das Plugin aktiviert?"""
  66. return self._enabled
  67. @enabled.setter
  68. def enabled(self, value: bool) -> None:
  69. """Aktiviert oder deaktiviert das Plugin."""
  70. self._enabled = value
  71. @property
  72. def name(self) -> str:
  73. """Gibt den Plugin-Namen zurück."""
  74. return self.NAME or self.__class__.__name__
  75. def is_enabled(self) -> bool:
  76. """Prüft, ob das Plugin aktiviert ist."""
  77. return self._enabled
  78. def get_config_value(self, key: str, default: Any = None) -> Any:
  79. """
  80. Gibt einen Konfigurationswert zurück.
  81. Args:
  82. key: Schlüssel (unterstützt Punkt-Notation: "section.key")
  83. default: Standardwert
  84. Returns:
  85. Konfigurationswert oder default
  86. """
  87. keys = key.split(".")
  88. value = self._config
  89. for k in keys:
  90. if isinstance(value, dict) and k in value:
  91. value = value[k]
  92. else:
  93. return default
  94. return value
  95. def set_config_value(self, key: str, value: Any) -> None:
  96. """
  97. Setzt einen Konfigurationswert.
  98. Args:
  99. key: Schlüssel (unterstützt Punkt-Notation)
  100. value: Zu setzender Wert
  101. """
  102. keys = key.split(".")
  103. config = self._config
  104. for k in keys[:-1]:
  105. if k not in config:
  106. config[k] = {}
  107. config = config[k]
  108. config[keys[-1]] = value
  109. def reload_config(self) -> bool:
  110. """
  111. Lädt die Konfiguration neu aus der Datei.
  112. Returns:
  113. True bei Erfolg
  114. """
  115. config_path = self._plugin_path / "config.json"
  116. if not config_path.exists():
  117. pdebug(f"Keine config.json für Plugin: {self.name}")
  118. return False
  119. try:
  120. with open(config_path, "r", encoding="utf-8") as f:
  121. self._config = json.load(f)
  122. pinfo(f"Plugin-Konfiguration neu geladen: {self.name}")
  123. return True
  124. except Exception as e:
  125. perror(f"Fehler beim Laden der Plugin-Konfiguration: {e}")
  126. return False
  127. def save_config(self) -> bool:
  128. """
  129. Speichert die Konfiguration in die Datei.
  130. Returns:
  131. True bei Erfolg
  132. """
  133. config_path = self._plugin_path / "config.json"
  134. try:
  135. with open(config_path, "w", encoding="utf-8") as f:
  136. json.dump(self._config, f, indent=2, ensure_ascii=False)
  137. pinfo(f"Plugin-Konfiguration gespeichert: {self.name}")
  138. return True
  139. except Exception as e:
  140. perror(f"Fehler beim Speichern der Plugin-Konfiguration: {e}")
  141. return False
  142. @abstractmethod
  143. async def on_load(self) -> None:
  144. """
  145. Wird aufgerufen, wenn das Plugin geladen wird.
  146. Hier sollten Event-Handler registriert und
  147. Initialisierungen durchgeführt werden.
  148. """
  149. pass
  150. @abstractmethod
  151. async def on_unload(self) -> None:
  152. """
  153. Wird aufgerufen, wenn das Plugin entladen wird.
  154. Hier sollten Ressourcen freigegeben werden.
  155. """
  156. pass
  157. async def on_enable(self) -> None:
  158. """Wird aufgerufen, wenn das Plugin aktiviert wird."""
  159. pass
  160. async def on_disable(self) -> None:
  161. """Wird aufgerufen, wenn das Plugin deaktiviert wird."""
  162. pass
  163. async def on_config_change(self, old_config: dict[str, Any]) -> None:
  164. """
  165. Wird aufgerufen, wenn sich die Konfiguration ändert.
  166. Args:
  167. old_config: Vorherige Konfiguration
  168. """
  169. pass
  170. def get_config_tool_tabs(self) -> list[dict[str, Any]]:
  171. """
  172. Gibt zusaetzliche Tabs fuer das Config-Tool zurueck.
  173. Plugins koennen diese Methode ueberschreiben, um benutzerdefinierte
  174. Tabs in der Plugin-Detailansicht des Config-Tools anzubieten.
  175. Returns:
  176. Liste von Tab-Definitionen, z.B.:
  177. [{"title": "Test", "description": "TTS-Test Seite"}]
  178. """
  179. return []
  180. def __repr__(self) -> str:
  181. return f"<Plugin {self.name} v{self.VERSION} enabled={self._enabled}>"