# -*- coding: utf-8 -*- """ Loudest-Strategie für Arbitration. Wählt den Satellite mit dem höchsten Audio-Level. Dies ist die Standard-Strategie und entspricht dem Prinzip "wer am lautesten gehört wird, ist am nächsten". """ from typing import Any from trixy_core.arbitration.candidate import ArbitrationCandidate from trixy_core.arbitration.strategies.base import ArbitrationStrategy class LoudestStrategy(ArbitrationStrategy): """ Strategie: Höchstes Audio-Level gewinnt. Diese Strategie wählt den Satellite aus, bei dem das Wakeword am lautesten erkannt wurde. Dies ist typischerweise der Satellite, der dem Sprecher am nächsten ist. Beispiel: ```python strategy = LoudestStrategy() candidates = [ ArbitrationCandidate(satellite_id="kitchen", audio_level=0.7), ArbitrationCandidate(satellite_id="living_room", audio_level=0.85), ] winner = strategy.select(candidates) # winner.satellite_id == "living_room" ``` """ def __init__(self, min_audio_level: float = 0.1) -> None: """ Initialisiert die Strategie. Args: min_audio_level: Minimales Audio-Level für Gültigkeit """ self.min_audio_level = min_audio_level @property def name(self) -> str: return "loudest" @property def description(self) -> str: return "Wählt den Satellite mit dem höchsten Audio-Level" def calculate_score( self, candidate: ArbitrationCandidate, context: dict[str, Any] | None = None, ) -> float: """ Score basiert nur auf Audio-Level. Args: candidate: Der Kandidat context: Nicht verwendet Returns: Audio-Level als Score (0.0-1.0) """ candidate.score_details = { "audio_level": candidate.audio_level, } return candidate.audio_level def select( self, candidates: list[ArbitrationCandidate], context: dict[str, Any] | None = None, ) -> ArbitrationCandidate | None: """ Wählt Kandidaten mit höchstem Audio-Level. Args: candidates: Liste der Kandidaten context: Nicht verwendet Returns: Kandidat mit höchstem audio_level oder None """ valid = self.filter_valid(candidates) if not valid: return None # Scores berechnen for candidate in valid: candidate.score = self.calculate_score(candidate, context) # Höchsten Score auswählen winner = max(valid, key=lambda c: c.score) return winner def validate_candidate(self, candidate: ArbitrationCandidate) -> bool: """Prüft ob Audio-Level über Minimum liegt.""" base_valid = super().validate_candidate(candidate) return base_valid and candidate.audio_level >= self.min_audio_level