| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- # -*- 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,
- ]
|