| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493 |
- # -*- coding: utf-8 -*-
- """
- Capability-Registry für Satellites.
- Verwaltet die Fähigkeiten von Satellites und ermöglicht
- das Filtern nach bestimmten Capabilities.
- """
- from __future__ import annotations
- import logging
- from dataclasses import dataclass, field
- from datetime import datetime
- from enum import Enum, auto
- from typing import Any, Callable
- class CapabilityState(Enum):
- """Zustand einer Capability."""
- UNKNOWN = auto() # Noch nicht geprüft
- AVAILABLE = auto() # Verfügbar
- UNAVAILABLE = auto() # Nicht verfügbar
- DEGRADED = auto() # Eingeschränkt verfügbar
- CHECKING = auto() # Wird gerade geprüft
- @dataclass
- class CapabilityInfo:
- """Informationen über eine Capability."""
- id: str # z.B. "audio.input"
- name: str # z.B. "Audio-Eingang"
- description: str = ""
- category: str = "" # z.B. "audio", "display", "sensor"
- version: str = "" # Capability-Version
- required_features: list[str] = field(default_factory=list)
- metadata: dict[str, Any] = field(default_factory=dict)
- @dataclass
- class Capability:
- """
- Eine konkrete Capability eines Satellites.
- Enthält Informationen über Verfügbarkeit und
- spezifische Konfiguration.
- """
- info: CapabilityInfo
- state: CapabilityState = CapabilityState.UNKNOWN
- satellite_id: str = ""
- last_checked: datetime | None = None
- error_message: str = ""
- parameters: dict[str, Any] = field(default_factory=dict)
- @property
- def id(self) -> str:
- """Capability-ID."""
- return self.info.id
- @property
- def is_available(self) -> bool:
- """Prüft ob Capability verfügbar ist."""
- return self.state == CapabilityState.AVAILABLE
- @property
- def is_degraded(self) -> bool:
- """Prüft ob Capability eingeschränkt ist."""
- return self.state == CapabilityState.DEGRADED
- def update_state(
- self,
- state: CapabilityState,
- error: str = "",
- ) -> None:
- """Aktualisiert den Zustand."""
- self.state = state
- self.error_message = error
- self.last_checked = datetime.now()
- def to_dict(self) -> dict[str, Any]:
- """Konvertiert zu Dictionary."""
- return {
- "id": self.info.id,
- "name": self.info.name,
- "category": self.info.category,
- "state": self.state.name,
- "satellite_id": self.satellite_id,
- "last_checked": (
- self.last_checked.isoformat()
- if self.last_checked else None
- ),
- "error": self.error_message,
- "parameters": self.parameters,
- }
- class CapabilityRegistry:
- """
- Registry für Satellite-Capabilities.
- Verwaltet welche Capabilities auf welchen Satellites
- verfügbar sind und ermöglicht Suche nach Fähigkeiten.
- Beispiel:
- registry = CapabilityRegistry()
- # Capability registrieren
- registry.register(
- satellite_id="sat-001",
- capability=Capability(
- info=CapabilityInfo("audio.input", "Mikrofon"),
- state=CapabilityState.AVAILABLE,
- )
- )
- # Satellites mit Capability finden
- satellites = registry.find_satellites_with("audio.input")
- """
- def __init__(self, logger: logging.Logger | None = None) -> None:
- """
- Initialisiert die Registry.
- Args:
- logger: Logger-Instanz
- """
- self.logger = logger or logging.getLogger(__name__)
- # satellite_id -> {capability_id -> Capability}
- self._capabilities: dict[str, dict[str, Capability]] = {}
- # capability_id -> CapabilityInfo (Definitionen)
- self._definitions: dict[str, CapabilityInfo] = {}
- # Callbacks
- self._on_change: list[Callable[[str, Capability], None]] = []
- @property
- def satellite_count(self) -> int:
- """Anzahl registrierter Satellites."""
- return len(self._capabilities)
- @property
- def capability_count(self) -> int:
- """Anzahl definierter Capability-Typen."""
- return len(self._definitions)
- def define_capability(self, info: CapabilityInfo) -> None:
- """
- Definiert einen Capability-Typ.
- Args:
- info: Capability-Definition
- """
- self._definitions[info.id] = info
- self.logger.debug(f"Capability definiert: {info.id}")
- def get_definition(self, capability_id: str) -> CapabilityInfo | None:
- """Holt Capability-Definition."""
- return self._definitions.get(capability_id)
- def list_definitions(self) -> list[CapabilityInfo]:
- """Listet alle Capability-Definitionen."""
- return list(self._definitions.values())
- def register(
- self,
- satellite_id: str,
- capability: Capability,
- ) -> None:
- """
- Registriert eine Capability für einen Satellite.
- Args:
- satellite_id: Satellite-ID
- capability: Capability-Instanz
- """
- if satellite_id not in self._capabilities:
- self._capabilities[satellite_id] = {}
- capability.satellite_id = satellite_id
- self._capabilities[satellite_id][capability.id] = capability
- self.logger.debug(
- f"Capability registriert: {capability.id} für {satellite_id}"
- )
- # Callbacks
- for callback in self._on_change:
- try:
- callback(satellite_id, capability)
- except Exception as e:
- self.logger.error(f"Callback-Fehler: {e}")
- def unregister(
- self,
- satellite_id: str,
- capability_id: str | None = None,
- ) -> bool:
- """
- Entfernt Capability(s) für einen Satellite.
- Args:
- satellite_id: Satellite-ID
- capability_id: Optional: Spezifische Capability
- Returns:
- True wenn entfernt
- """
- if satellite_id not in self._capabilities:
- return False
- if capability_id:
- # Spezifische Capability entfernen
- if capability_id in self._capabilities[satellite_id]:
- del self._capabilities[satellite_id][capability_id]
- return True
- return False
- else:
- # Alle Capabilities des Satellites entfernen
- del self._capabilities[satellite_id]
- return True
- def update_state(
- self,
- satellite_id: str,
- capability_id: str,
- state: CapabilityState,
- error: str = "",
- ) -> bool:
- """
- Aktualisiert den Zustand einer Capability.
- Args:
- satellite_id: Satellite-ID
- capability_id: Capability-ID
- state: Neuer Zustand
- error: Optionale Fehlermeldung
- Returns:
- True wenn aktualisiert
- """
- cap = self.get(satellite_id, capability_id)
- if not cap:
- return False
- cap.update_state(state, error)
- # Callbacks
- for callback in self._on_change:
- try:
- callback(satellite_id, cap)
- except Exception as e:
- self.logger.error(f"Callback-Fehler: {e}")
- return True
- def get(
- self,
- satellite_id: str,
- capability_id: str,
- ) -> Capability | None:
- """
- Holt spezifische Capability.
- Args:
- satellite_id: Satellite-ID
- capability_id: Capability-ID
- Returns:
- Capability oder None
- """
- sat_caps = self._capabilities.get(satellite_id, {})
- return sat_caps.get(capability_id)
- def get_all(self, satellite_id: str) -> list[Capability]:
- """
- Holt alle Capabilities eines Satellites.
- Args:
- satellite_id: Satellite-ID
- Returns:
- Liste der Capabilities
- """
- return list(self._capabilities.get(satellite_id, {}).values())
- def get_available(self, satellite_id: str) -> list[Capability]:
- """Holt alle verfügbaren Capabilities eines Satellites."""
- return [
- cap for cap in self.get_all(satellite_id)
- if cap.is_available
- ]
- def has_capability(
- self,
- satellite_id: str,
- capability_id: str,
- require_available: bool = True,
- ) -> bool:
- """
- Prüft ob Satellite eine Capability hat.
- Args:
- satellite_id: Satellite-ID
- capability_id: Capability-ID
- require_available: Muss verfügbar sein
- Returns:
- True wenn vorhanden (und verfügbar)
- """
- cap = self.get(satellite_id, capability_id)
- if not cap:
- return False
- if require_available:
- return cap.is_available
- return True
- def has_all_capabilities(
- self,
- satellite_id: str,
- capability_ids: list[str],
- require_available: bool = True,
- ) -> bool:
- """Prüft ob Satellite alle angegebenen Capabilities hat."""
- return all(
- self.has_capability(satellite_id, cid, require_available)
- for cid in capability_ids
- )
- def has_any_capability(
- self,
- satellite_id: str,
- capability_ids: list[str],
- require_available: bool = True,
- ) -> bool:
- """Prüft ob Satellite mindestens eine der Capabilities hat."""
- return any(
- self.has_capability(satellite_id, cid, require_available)
- for cid in capability_ids
- )
- def find_satellites_with(
- self,
- capability_id: str,
- require_available: bool = True,
- ) -> list[str]:
- """
- Findet Satellites mit einer bestimmten Capability.
- Args:
- capability_id: Gesuchte Capability
- require_available: Muss verfügbar sein
- Returns:
- Liste von Satellite-IDs
- """
- result = []
- for sat_id in self._capabilities:
- if self.has_capability(sat_id, capability_id, require_available):
- result.append(sat_id)
- return result
- def find_satellites_with_all(
- self,
- capability_ids: list[str],
- require_available: bool = True,
- ) -> list[str]:
- """Findet Satellites mit allen angegebenen Capabilities."""
- result = []
- for sat_id in self._capabilities:
- if self.has_all_capabilities(sat_id, capability_ids, require_available):
- result.append(sat_id)
- return result
- def find_satellites_with_any(
- self,
- capability_ids: list[str],
- require_available: bool = True,
- ) -> list[str]:
- """Findet Satellites mit mindestens einer der Capabilities."""
- result = []
- for sat_id in self._capabilities:
- if self.has_any_capability(sat_id, capability_ids, require_available):
- result.append(sat_id)
- return result
- def get_by_category(
- self,
- satellite_id: str,
- category: str,
- ) -> list[Capability]:
- """Holt Capabilities einer Kategorie."""
- return [
- cap for cap in self.get_all(satellite_id)
- if cap.info.category == category
- ]
- def on_change(
- self,
- callback: Callable[[str, Capability], None],
- ) -> None:
- """Registriert Callback für Änderungen."""
- self._on_change.append(callback)
- def get_summary(self) -> dict[str, Any]:
- """Liefert Zusammenfassung der Registry."""
- total_caps = sum(
- len(caps) for caps in self._capabilities.values()
- )
- available_count = sum(
- sum(1 for c in caps.values() if c.is_available)
- for caps in self._capabilities.values()
- )
- return {
- "satellite_count": self.satellite_count,
- "definition_count": self.capability_count,
- "total_capabilities": total_caps,
- "available_capabilities": available_count,
- "satellites": {
- sat_id: {
- "capabilities": len(caps),
- "available": sum(1 for c in caps.values() if c.is_available),
- }
- for sat_id, caps in self._capabilities.items()
- },
- }
- def __contains__(self, satellite_id: str) -> bool:
- return satellite_id in self._capabilities
- def __len__(self) -> int:
- return self.satellite_count
- # Standard-Capability-Definitionen
- STANDARD_CAPABILITIES = [
- CapabilityInfo(
- id="audio.input",
- name="Audio-Eingang",
- description="Mikrofon für Sprachaufnahme",
- category="audio",
- ),
- CapabilityInfo(
- id="audio.output",
- name="Audio-Ausgang",
- description="Lautsprecher für Sprachausgabe",
- category="audio",
- ),
- CapabilityInfo(
- id="audio.music",
- name="Musik-Wiedergabe",
- description="Hochwertige Musikwiedergabe",
- category="audio",
- ),
- CapabilityInfo(
- id="display.screen",
- name="Bildschirm",
- description="Visuelle Anzeige",
- category="display",
- ),
- CapabilityInfo(
- id="display.led",
- name="LED-Anzeige",
- description="LED-Statusanzeige",
- category="display",
- ),
- CapabilityInfo(
- id="sensor.motion",
- name="Bewegungssensor",
- description="Erkennt Bewegungen",
- category="sensor",
- ),
- CapabilityInfo(
- id="sensor.temperature",
- name="Temperatursensor",
- description="Misst Raumtemperatur",
- category="sensor",
- ),
- CapabilityInfo(
- id="network.wifi",
- name="WLAN",
- description="WiFi-Konnektivität",
- category="network",
- ),
- CapabilityInfo(
- id="network.bluetooth",
- name="Bluetooth",
- description="Bluetooth-Konnektivität",
- category="network",
- ),
- ]
|