feat: Autostart + Minimiert starten Funktionalität

- Autostart: Desktop-Entry wird in ~/.config/autostart/ erstellt
- Minimiert starten: App startet direkt im System-Tray
- Einstellungen lesen Config direkt, kein CLI-Flag nötig
- README.md aktualisiert

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Eduard Wisch 2026-02-25 11:28:55 +01:00
parent 71e5b92b50
commit b0fd6889a3
4 changed files with 98 additions and 1 deletions

View file

@ -16,6 +16,8 @@ Basiert auf **PJSUA2** (SIP-Stack) und **PySide6** (Qt-GUI). Verbindet sich übe
- **Responsives Layout**: Kompakt (nur Wählfeld) oder Breit (Wählfeld + Tabs)
- **Click-to-Call**: `tel:` und `sip:` URI-Handler via D-Bus Single-Instance
- **KDE-Integration**: System-Tray, D-Bus-Benachrichtigungen mit Annehmen/Ablehnen
- **Autostart**: Automatisch beim Systemstart starten (XDG-konform)
- **Minimiert starten**: Optional direkt in den System-Tray starten
- **PipeWire-Audio**: Natives PipeWire/PulseAudio-Routing, separates Klingelton-Gerät
- **AppImage**: Komplett unabhängiges Paket (kein Python/Qt auf dem Zielsystem nötig)
@ -207,6 +209,14 @@ Gespeichert unter `~/.config/sipwebapp/config.json`:
- CardDAV-Accounts (Multi-Account)
- BLF-Extensions
- Favoriten (manuell gepinnt)
- Allgemein: Autostart, Minimiert starten, Tray-Verhalten
### Autostart
Wenn in den Einstellungen aktiviert, wird ein Desktop-Entry erstellt:
`~/.config/autostart/sipwebapp.desktop`
Die Option "Minimiert starten" lässt die App direkt im System-Tray starten, ohne das Hauptfenster anzuzeigen.
## AppImage bauen

24
main.py
View file

@ -58,6 +58,17 @@ def _uri_aus_args():
return ""
def _minimiert_starten(config=None):
"""Prüft ob minimiert gestartet werden soll (Config oder Kommandozeile)."""
# Kommandozeile hat Vorrang (für manuelles Testen)
if "--minimiert" in sys.argv or "-m" in sys.argv:
return True
# Sonst aus Config lesen
if config:
return config.allgemein.get("minimiert_starten", False)
return False
class _DBusService(dbus.service.Object):
"""D-Bus Service für Single-Instance und URI-Weiterleitung."""
@ -111,6 +122,11 @@ def main():
)
from ui.hauptfenster import HauptFenster
from utils.config_manager import ConfigManager
# Config laden für minimiert_starten Einstellung
config = ConfigManager()
fenster = HauptFenster()
# D-Bus Service registrieren (für Click-to-Call von Browser)
@ -123,7 +139,13 @@ def main():
except dbus.DBusException:
pass
fenster.show()
# Minimiert starten? (aus Config oder --minimiert Flag)
minimiert = _minimiert_starten(config)
if minimiert:
# Direkt in Tray starten, Fenster nicht anzeigen
fenster.hide()
else:
fenster.show()
fenster.starten()
# Wenn mit URI gestartet, Anruf auslösen

View file

@ -178,6 +178,10 @@ class EinstellungenDialog(QDialog):
"Beim Systemstart automatisch starten")
form.addRow(self.autostart_check)
self.minimiert_starten_check = QCheckBox(
"Minimiert starten (direkt in System-Tray)")
form.addRow(self.minimiert_starten_check)
# Klingelton-Datei
klingelton_layout = QHBoxLayout()
self.klingelton_eingabe = QLineEdit()
@ -289,6 +293,8 @@ class EinstellungenDialog(QDialog):
allg = self._config.allgemein
self.tray_check.setChecked(allg.get("minimieren_in_tray", True))
self.autostart_check.setChecked(allg.get("autostart", False))
self.minimiert_starten_check.setChecked(
allg.get("minimiert_starten", False))
self.klingelton_eingabe.setText(allg.get("klingelton", ""))
# CardDAV-Accounts laden
@ -462,6 +468,8 @@ class EinstellungenDialog(QDialog):
self.tray_check.isChecked())
self._config.set("allgemein", "autostart",
self.autostart_check.isChecked())
self._config.set("allgemein", "minimiert_starten",
self.minimiert_starten_check.isChecked())
self._config.set("allgemein", "klingelton",
self.klingelton_eingabe.text())
@ -478,5 +486,7 @@ class EinstellungenDialog(QDialog):
self._config.set("blf", "extensions", extensions)
self._config.speichern()
# Autostart Desktop-Entry erstellen/entfernen
self._config.autostart_aktualisieren()
self.einstellungen_geaendert.emit()
self.accept()

View file

@ -2,6 +2,8 @@
import json
import os
import shutil
import sys
from pathlib import Path
@ -10,6 +12,13 @@ KONFIG_VERZEICHNIS = Path(
os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config")
) / "sipwebapp"
# Autostart-Verzeichnis (XDG-Standard)
AUTOSTART_VERZEICHNIS = Path(
os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config")
) / "autostart"
AUTOSTART_DATEI = AUTOSTART_VERZEICHNIS / "sipwebapp.desktop"
KONFIG_DATEI = KONFIG_VERZEICHNIS / "config.json"
ANRUFLISTE_DATEI = KONFIG_VERZEICHNIS / "anrufliste.json"
KONTAKTE_DATEI = KONFIG_VERZEICHNIS / "kontakte.json"
@ -33,6 +42,7 @@ STANDARD_KONFIG = {
"allgemein": {
"minimieren_in_tray": True,
"autostart": False,
"minimiert_starten": False, # Beim Autostart minimiert in Tray starten
"klingelton": "", # Pfad zu WAV-Datei
},
"carddav": {
@ -202,3 +212,48 @@ class ConfigManager:
KONFIG_VERZEICHNIS.mkdir(parents=True, exist_ok=True)
with open(KONTAKTE_DATEI, "w", encoding="utf-8") as f:
json.dump(kontakte, f, indent=2, ensure_ascii=False)
# --- Autostart ---
def autostart_aktivieren(self):
"""Desktop-Entry für Autostart erstellen."""
AUTOSTART_VERZEICHNIS.mkdir(parents=True, exist_ok=True)
# Executable ermitteln
if getattr(sys, 'frozen', False):
# PyInstaller/AppImage: sys.executable ist das Binary
exec_path = sys.executable
else:
# Entwicklung: Python + main.py
main_py = Path(__file__).parent.parent / "main.py"
exec_path = f"{sys.executable} {main_py}"
# Kein --minimiert Flag nötig - App liest selbst aus Config
desktop_entry = f"""[Desktop Entry]
Type=Application
Version=1.0
Name=SIP Softphone
Comment=SIP-Softphone für FreePBX/Asterisk
GenericName=Internet-Telefonie
Exec={exec_path}
Icon=sipwebapp
Categories=Network;Telephony;
StartupNotify=false
Terminal=false
X-GNOME-Autostart-enabled=true
"""
with open(AUTOSTART_DATEI, "w", encoding="utf-8") as f:
f.write(desktop_entry)
def autostart_deaktivieren(self):
"""Desktop-Entry für Autostart entfernen."""
if AUTOSTART_DATEI.exists():
AUTOSTART_DATEI.unlink()
def autostart_aktualisieren(self):
"""Autostart-Eintrag basierend auf Konfiguration erstellen oder entfernen."""
if self.allgemein.get("autostart", False):
self.autostart_aktivieren()
else:
self.autostart_deaktivieren()