layouts.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. # -*- coding: utf-8 -*-
  2. """
  3. LED Layout-Definitionen.
  4. Definiert verschiedene physische LED-Anordnungen (Single, Jewel, Ring)
  5. und stellt eine automatische Layout-Erkennung bereit.
  6. """
  7. from __future__ import annotations
  8. from dataclasses import dataclass, field
  9. from trixy_core.utils.debug import pdebug, pwarn
  10. @dataclass
  11. class LEDLayout:
  12. """Layout-Definition fuer eine LED-Anordnung.
  13. Beschreibt die physische Anordnung der LEDs, insbesondere
  14. welche LEDs zum Ring gehoeren und ob eine Center-LED existiert.
  15. Attributes:
  16. name: Bezeichnung des Layouts (z.B. "single", "jewel_7", "ring_12").
  17. led_count: Gesamtanzahl der LEDs.
  18. center_index: Index der Center-LED (None wenn keine vorhanden).
  19. ring_indices: Indizes der Ring-LEDs in Reihenfolge.
  20. """
  21. name: str
  22. led_count: int
  23. center_index: int | None = None
  24. ring_indices: list[int] = field(default_factory=list)
  25. @property
  26. def has_center(self) -> bool:
  27. """Prueft ob das Layout eine Center-LED hat."""
  28. return self.center_index is not None
  29. @property
  30. def ring_count(self) -> int:
  31. """Anzahl der Ring-LEDs."""
  32. return len(self.ring_indices)
  33. # ── Vordefinierte Layouts ──────────────────────────────────────────────
  34. LAYOUT_SINGLE = LEDLayout(
  35. name="single",
  36. led_count=1,
  37. center_index=None,
  38. ring_indices=[0],
  39. )
  40. LAYOUT_JEWEL_7 = LEDLayout(
  41. name="jewel_7",
  42. led_count=7,
  43. center_index=0,
  44. ring_indices=[1, 2, 3, 4, 5, 6],
  45. )
  46. LAYOUT_RING_12 = LEDLayout(
  47. name="ring_12",
  48. led_count=12,
  49. center_index=None,
  50. ring_indices=list(range(12)),
  51. )
  52. LAYOUT_RING_16 = LEDLayout(
  53. name="ring_16",
  54. led_count=16,
  55. center_index=None,
  56. ring_indices=list(range(16)),
  57. )
  58. LAYOUT_RING_24 = LEDLayout(
  59. name="ring_24",
  60. led_count=24,
  61. center_index=None,
  62. ring_indices=list(range(24)),
  63. )
  64. # Lookup-Tabelle: Name → Layout
  65. LAYOUTS: dict[str, LEDLayout] = {
  66. "single": LAYOUT_SINGLE,
  67. "jewel_7": LAYOUT_JEWEL_7,
  68. "ring_12": LAYOUT_RING_12,
  69. "ring_16": LAYOUT_RING_16,
  70. "ring_24": LAYOUT_RING_24,
  71. }
  72. # Auto-Detect: LED-Anzahl → Layout-Name (fuer gaengige Konfigurationen)
  73. _COUNT_TO_LAYOUT: dict[int, str] = {
  74. 1: "single",
  75. 7: "jewel_7",
  76. 12: "ring_12",
  77. 16: "ring_16",
  78. 24: "ring_24",
  79. }
  80. def get_layout(type_name: str, led_count: int) -> LEDLayout:
  81. """Gibt passendes Layout zurueck oder erkennt es automatisch.
  82. Sucht zuerst nach dem angegebenen Layout-Namen. Falls kein Name
  83. angegeben oder nicht gefunden, wird anhand der LED-Anzahl ein
  84. passendes Layout gewaehlt. Als Fallback wird ein generisches
  85. Ring-Layout erzeugt.
  86. Args:
  87. type_name: Gewuenschter Layout-Name (z.B. "jewel_7").
  88. Leerer String fuer Auto-Erkennung.
  89. led_count: Anzahl der LEDs.
  90. Returns:
  91. Passendes LEDLayout fuer die Konfiguration.
  92. """
  93. # Expliziter Layout-Name angegeben
  94. if type_name and type_name in LAYOUTS:
  95. layout = LAYOUTS[type_name]
  96. if layout.led_count != led_count:
  97. pwarn(
  98. f"LED-Layout '{type_name}' erwartet {layout.led_count} LEDs, "
  99. f"aber {led_count} konfiguriert — verwende Layout trotzdem"
  100. )
  101. pdebug(f"LED-Layout '{type_name}' geladen ({layout.led_count} LEDs)")
  102. return layout
  103. # Warnung wenn unbekannter Name angegeben
  104. if type_name:
  105. pwarn(
  106. f"Unbekanntes LED-Layout '{type_name}' — versuche Auto-Erkennung"
  107. )
  108. # Auto-Erkennung anhand LED-Anzahl
  109. if led_count in _COUNT_TO_LAYOUT:
  110. auto_name = _COUNT_TO_LAYOUT[led_count]
  111. layout = LAYOUTS[auto_name]
  112. pdebug(
  113. f"LED-Layout automatisch erkannt: '{auto_name}' ({led_count} LEDs)"
  114. )
  115. return layout
  116. # Fallback: Generisches Ring-Layout
  117. pdebug(
  118. f"Kein vordefiniertes Layout fuer {led_count} LEDs — "
  119. f"erzeuge generisches Ring-Layout"
  120. )
  121. return LEDLayout(
  122. name=f"ring_{led_count}",
  123. led_count=led_count,
  124. center_index=None,
  125. ring_indices=list(range(led_count)),
  126. )