| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- # -*- coding: utf-8 -*-
- """
- Satellites-View: Liste und Verwaltung registrierter Satellites.
- Zeigt alle Satellites mit Verbindungsstatus. Buttons fuer
- Registration, Loeschen und Trennen. Bei Selektion wird
- die Satellite-Detailansicht geoeffnet.
- """
- from __future__ import annotations
- from textual.app import ComposeResult
- from textual.containers import Vertical, Horizontal
- from textual.widgets import Static, Button, ListView, ListItem, Label
- from trixy_core.tui.views.base import MainView
- class _SatelliteListItem(ListItem):
- """Einzelner Eintrag in der Satellite-Liste."""
- def __init__(self, sat_data: dict, **kwargs) -> None:
- super().__init__(**kwargs)
- self.sat_data = sat_data
- def compose(self) -> ComposeResult:
- alias = self.sat_data.get("alias", "Unbekannt")
- room = self.sat_data.get("room_id", "")
- connected = self.sat_data.get("connected", False)
- mac = self.sat_data.get("mac_address", "")
- iface_type = self.sat_data.get("interface_type", "")
- if connected:
- status = "[green]Verbunden[/green]"
- else:
- status = "[red]Getrennt[/red]"
- # Interface-Badge
- iface_badge = ""
- if connected and iface_type:
- iface_map = {
- "wifi": "[cyan]WLAN[/cyan]",
- "ethernet": "[green]LAN[/green]",
- "other": "[dim]?[/dim]",
- }
- iface_badge = f" {iface_map.get(iface_type, '')}"
- yield Label(
- f" {status}{iface_badge} [bold]{alias}[/bold] ({room}) [{mac}]"
- )
- class SatellitesView(MainView):
- """
- F3: Satellites-Ansicht (nur bei Server-Verbindung).
- Zeigt alle registrierten Satellites mit Verbindungsstatus.
- Bietet Aktionen fuer Registration, Loeschen und Trennen.
- """
- TITLE = "Satellites"
- VIEW_ID = "satellites"
- def __init__(self, **kwargs) -> None:
- super().__init__(**kwargs)
- self._satellites: list[dict] = []
- self._selected_index: int = -1
- self._registration_mode: bool = False
- self._refresh_counter: int = 0
- def compose(self) -> ComposeResult:
- # Status-Leiste
- yield Static("", id="sat-header")
- # Satellite-Liste
- yield ListView(id="sat-list")
- # Aktions-Buttons
- with Horizontal(id="sat-actions"):
- yield Button("Registrierung", id="sat-btn-register", variant="primary")
- yield Button("Trennen", id="sat-btn-disconnect", variant="warning")
- yield Button("Loeschen", id="sat-btn-delete", variant="error")
- # Status
- yield Static("", id="sat-status")
- async def on_activate(self) -> None:
- """Laedt die Satellite-Liste."""
- await self._load_satellites()
- async def refresh_data(self) -> None:
- """Periodisches Update (alle 10 Sekunden via 2s-Interval)."""
- self._refresh_counter += 1
- # Nur alle 5 Aufrufe (= 10 Sekunden bei 2s Interval)
- if self._refresh_counter % 5 == 0:
- await self._load_satellites()
- async def _load_satellites(self) -> None:
- """Laedt die Satellite-Liste vom Server."""
- if not self.connection:
- return
- try:
- response = await self.connection.request_satellites()
- except Exception:
- self._set_status("Fehler beim Laden der Satellites", error=True)
- return
- if response is None:
- return
- self._satellites = response.satellites
- self._registration_mode = response.registration_mode
- self._update_header(response)
- await self._update_list()
- def _update_header(self, response) -> None:
- """Aktualisiert die Kopfzeile."""
- header = self.query_one("#sat-header", Static)
- total = len(response.satellites)
- connected = sum(1 for s in response.satellites if s.get("connected", False))
- reg_status = ""
- if response.registration_mode:
- if response.permanent_mode:
- reg_status = " [yellow]Auto-Registrierung aktiv[/yellow]"
- else:
- reg_status = " [yellow]Registrierung aktiv[/yellow]"
- header.update(
- f"[bold]Satellites:[/bold] {connected}/{total} verbunden{reg_status}"
- )
- # Register-Button-Text anpassen
- try:
- btn = self.query_one("#sat-btn-register", Button)
- if self._registration_mode:
- btn.label = "Registrierung aus"
- btn.variant = "warning"
- else:
- btn.label = "Registrierung"
- btn.variant = "primary"
- except Exception:
- pass
- async def _update_list(self) -> None:
- """Aktualisiert die Satellite-Liste."""
- list_view = self.query_one("#sat-list", ListView)
- await list_view.clear()
- # Verbundene zuerst, dann getrennte
- sorted_sats = sorted(
- self._satellites,
- key=lambda s: (not s.get("connected", False), s.get("alias", "")),
- )
- for sat_data in sorted_sats:
- item = _SatelliteListItem(sat_data)
- await list_view.append(item)
- def _get_selected_satellite(self) -> dict | None:
- """Gibt den aktuell selektierten Satellite zurueck."""
- try:
- list_view = self.query_one("#sat-list", ListView)
- if list_view.highlighted_child is not None:
- item = list_view.highlighted_child
- if isinstance(item, _SatelliteListItem):
- return item.sat_data
- except Exception:
- pass
- return None
- async def on_list_view_selected(self, event: ListView.Selected) -> None:
- """Oeffnet die Detail-Ansicht fuer den selektierten Satellite."""
- if isinstance(event.item, _SatelliteListItem):
- sat_data = event.item.sat_data
- # SubMainView oeffnen via App
- try:
- app = self.app
- if hasattr(app, "open_satellite_detail"):
- await app.open_satellite_detail(sat_data)
- except Exception:
- pass
- async def on_button_pressed(self, event: Button.Pressed) -> None:
- """Verarbeitet Button-Klicks."""
- btn_id = event.button.id
- if not self.connection:
- return
- if btn_id == "sat-btn-register":
- await self._toggle_registration()
- elif btn_id == "sat-btn-disconnect":
- await self._disconnect_satellite()
- elif btn_id == "sat-btn-delete":
- await self._delete_satellite()
- async def _toggle_registration(self) -> None:
- """Schaltet den Registrierungsmodus um."""
- if self._registration_mode:
- action = "disable_registration"
- else:
- action = "enable_registration"
- result = await self.connection.satellite_action(action=action)
- if result and result.success:
- self._set_status(result.message)
- await self._load_satellites()
- else:
- error = result.error if result else "Unbekannter Fehler"
- self._set_status(f"Fehler: {error}", error=True)
- async def _disconnect_satellite(self) -> None:
- """Trennt den selektierten Satellite."""
- sat = self._get_selected_satellite()
- if not sat:
- self._set_status("Kein Satellite ausgewaehlt", error=True)
- return
- if not sat.get("connected", False):
- self._set_status("Satellite ist nicht verbunden", error=True)
- return
- result = await self.connection.satellite_action(
- action="disconnect",
- satellite_id=sat.get("satellite_id", ""),
- )
- if result and result.success:
- self._set_status(result.message)
- await self._load_satellites()
- else:
- error = result.error if result else "Unbekannter Fehler"
- self._set_status(f"Fehler: {error}", error=True)
- async def _delete_satellite(self) -> None:
- """Loescht die Registrierung des selektierten Satellites."""
- sat = self._get_selected_satellite()
- if not sat:
- self._set_status("Kein Satellite ausgewaehlt", error=True)
- return
- result = await self.connection.satellite_action(
- action="delete",
- satellite_id=sat.get("satellite_id", ""),
- )
- if result and result.success:
- self._set_status(result.message)
- await self._load_satellites()
- else:
- error = result.error if result else "Unbekannter Fehler"
- self._set_status(f"Fehler: {error}", error=True)
- def _set_status(self, text: str, error: bool = False) -> None:
- """Setzt die Statuszeile."""
- status = self.query_one("#sat-status", Static)
- if error:
- status.update(f"[red]{text}[/red]")
- else:
- status.update(f"[green]{text}[/green]")
|