sat_connection_view.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. # -*- coding: utf-8 -*-
  2. """
  3. Satellite SubView: Connection (F2).
  4. Zeigt Verbindungsinformationen pro Socket-Stream:
  5. Command, Audio Input, Audio Output, Music Output.
  6. """
  7. from __future__ import annotations
  8. from textual.app import ComposeResult
  9. from textual.containers import Vertical
  10. from textual.widgets import Static
  11. from trixy_core.tui.views.base import MainView
  12. from trixy_core.tui.widgets.status_table import StatusTable
  13. class SatConnectionView(MainView):
  14. """F2 im Satellite-Kontext: Verbindungsinformationen."""
  15. TITLE = "Verbindung"
  16. VIEW_ID = "sat_connection"
  17. def __init__(self, **kwargs) -> None:
  18. super().__init__(**kwargs)
  19. self._sat_data: dict = {}
  20. self._detail_data: object | None = None
  21. self._refresh_counter: int = 0
  22. def set_satellite(self, sat_data: dict) -> None:
  23. """Setzt die Satellite-Basisdaten."""
  24. self._sat_data = sat_data
  25. def compose(self) -> ComposeResult:
  26. with Vertical(classes="section-box"):
  27. yield Static("[bold]Command Socket[/bold]", classes="section-header")
  28. yield StatusTable(id="sat-conn-command")
  29. with Vertical(classes="section-box"):
  30. yield Static("[bold]Audio Input Stream[/bold]", classes="section-header")
  31. yield StatusTable(id="sat-conn-audio-in")
  32. with Vertical(classes="section-box"):
  33. yield Static("[bold]Audio Output Stream[/bold]", classes="section-header")
  34. yield StatusTable(id="sat-conn-audio-out")
  35. with Vertical(classes="section-box"):
  36. yield Static("[bold]Music Output Stream[/bold]", classes="section-header")
  37. yield StatusTable(id="sat-conn-music-out")
  38. async def on_activate(self) -> None:
  39. """Laedt und zeigt die Verbindungsinfos."""
  40. await self._load_detail()
  41. async def refresh_data(self) -> None:
  42. """Periodisches Update (alle 10 Sekunden)."""
  43. self._refresh_counter += 1
  44. if self._refresh_counter % 5 == 0:
  45. await self._load_detail()
  46. async def _load_detail(self) -> None:
  47. """Laedt detaillierte Satellite-Daten vom Server."""
  48. if not self.connection or not self._sat_data:
  49. return
  50. sat_id = self._sat_data.get("satellite_id", "")
  51. if not sat_id:
  52. return
  53. try:
  54. detail = await self.connection.request_satellite_detail(sat_id)
  55. except Exception:
  56. return
  57. if detail is None or detail.error:
  58. return
  59. self._detail_data = detail
  60. self._update_display()
  61. def _update_display(self) -> None:
  62. """Aktualisiert die Anzeige."""
  63. d = self._detail_data
  64. if d is None:
  65. return
  66. sockets = d.sockets if hasattr(d, "sockets") else {}
  67. if isinstance(sockets, dict):
  68. self._update_socket_table("#sat-conn-command", sockets.get("command", {}))
  69. self._update_socket_table("#sat-conn-audio-in", sockets.get("audio_in", {}))
  70. self._update_socket_table("#sat-conn-audio-out", sockets.get("audio_out", {}))
  71. self._update_socket_table("#sat-conn-music-out", sockets.get("music_out", {}))
  72. def _update_socket_table(self, table_id: str, sock_info: dict) -> None:
  73. """Aktualisiert eine einzelne Socket-Tabelle."""
  74. table = self.query_one(table_id, StatusTable)
  75. if not sock_info:
  76. table.update_data([("Status", "[dim]Keine Daten[/dim]")])
  77. return
  78. connected = sock_info.get("connected", False)
  79. if connected:
  80. status = "[green]Verbunden[/green]"
  81. else:
  82. status = "[red]Getrennt[/red]"
  83. rows = [("Status", status)]
  84. ip = sock_info.get("ip", "")
  85. port = sock_info.get("port", 0)
  86. if ip:
  87. rows.append(("Adresse", f"{ip}:{port}"))
  88. table.update_data(rows)