# -*- coding: utf-8 -*- """ Statische LED-Animationen (einmalig, CONTINUOUS = False). Enthaelt einfache Animationen wie Solid, Blink-Varianten, Flash und sequentielles Fuellen. """ from __future__ import annotations from collections.abc import Iterator from trixy_core.led.animations import IAnimation, RGB from trixy_core.led.layouts import LEDLayout # Schwarz / aus OFF: RGB = (0, 0, 0) class SolidAnimation(IAnimation): """Alle LEDs auf eine Farbe setzen (1 Frame). Bei Jewel-Layouts wird die Center-LED optional mit einer separaten Farbe (center_color) angesteuert. """ NAME = "solid" CONTINUOUS = False def frames( self, led_count: int, color: RGB, speed: int, layout: LEDLayout, **opts: object, ) -> Iterator[list[RGB]]: center_color: RGB | None = opts.get("center_color") # type: ignore[assignment] frame = [color] * led_count # Jewel: Center-LED separat einfaerben if layout.center_index is not None and center_color is not None: frame[layout.center_index] = center_color yield frame class SingleBlinkAnimation(IAnimation): """Einmaliges Blinken: an → aus.""" NAME = "single-blink" CONTINUOUS = False def frames( self, led_count: int, color: RGB, speed: int, layout: LEDLayout, **opts: object, ) -> Iterator[list[RGB]]: yield from _blink_frames(led_count, color, layout, 1, **opts) class DoubleBlinkAnimation(IAnimation): """Zweimaliges Blinken: an/aus/an/aus.""" NAME = "double-blink" CONTINUOUS = False def frames( self, led_count: int, color: RGB, speed: int, layout: LEDLayout, **opts: object, ) -> Iterator[list[RGB]]: yield from _blink_frames(led_count, color, layout, 2, **opts) class TripleBlinkAnimation(IAnimation): """Dreimaliges Blinken: an/aus/an/aus/an/aus.""" NAME = "triple-blink" CONTINUOUS = False def frames( self, led_count: int, color: RGB, speed: int, layout: LEDLayout, **opts: object, ) -> Iterator[list[RGB]]: yield from _blink_frames(led_count, color, layout, 3, **opts) class FlashAnimation(IAnimation): """Kurzer heller Blitz (50 ms an, dann aus). Aehnlich wie Blink, aber nur ein einzelner schneller Frame. Die Engine verwendet speed=50 fuer den an-Frame. """ NAME = "flash" CONTINUOUS = False def frames( self, led_count: int, color: RGB, speed: int, layout: LEDLayout, **opts: object, ) -> Iterator[list[RGB]]: # Heller Frame — volle Helligkeit on_frame = [color] * led_count off_frame = [OFF] * led_count yield on_frame yield off_frame class FillSequentialAnimation(IAnimation): """LEDs schalten nacheinander ein (Ring-Reihenfolge). Bei Jewel: Ring-LEDs zuerst, Center-LED zuletzt. Jeder Frame fuegt eine weitere LED hinzu. """ NAME = "fill-sequential" CONTINUOUS = False def frames( self, led_count: int, color: RGB, speed: int, layout: LEDLayout, **opts: object, ) -> Iterator[list[RGB]]: center_color: RGB | None = opts.get("center_color") # type: ignore[assignment] # Reihenfolge: Ring-LEDs zuerst, dann Center (falls vorhanden) fill_order: list[int] = list(layout.ring_indices) if layout.center_index is not None: fill_order.append(layout.center_index) frame = [OFF] * led_count for idx in fill_order: # Center-LED bekommt eigene Farbe wenn angegeben if idx == layout.center_index and center_color is not None: frame[idx] = center_color else: frame[idx] = color # Kopie yielden, damit spaetere Aenderungen den Frame nicht # nachtraeglich veraendern yield list(frame) # ── Hilfsfunktionen ────────────────────────────────────────────────────── def _blink_frames( led_count: int, color: RGB, layout: LEDLayout, count: int, **opts: object, ) -> Iterator[list[RGB]]: """Erzeugt Blink-Frames (an/aus) fuer die angegebene Anzahl. Args: led_count: Gesamtanzahl LEDs. color: Primaerfarbe. layout: LED-Layout. count: Anzahl Blink-Zyklen. **opts: Zusaetzliche Optionen (center_color). Yields: Abwechselnd an- und aus-Frames. """ center_color: RGB | None = opts.get("center_color") # type: ignore[assignment] on_frame = [color] * led_count if layout.center_index is not None and center_color is not None: on_frame[layout.center_index] = center_color off_frame = [OFF] * led_count for _ in range(count): yield list(on_frame) yield list(off_frame) def get_animations() -> list[type[IAnimation]]: """Gibt alle statischen Animationsklassen zurueck.""" return [ SolidAnimation, SingleBlinkAnimation, DoubleBlinkAnimation, TripleBlinkAnimation, FlashAnimation, FillSequentialAnimation, ]