PySide6/PJSUA2-basiertes Desktop-Softphone mit: - SIP-Telefonie (Anrufe, DTMF, Halten, Transfer, Konferenz) - CardDAV-Kontaktsync mit Write-Back (Multi-Account) - Kontaktverwaltung mit Suche und Bearbeiten-Dialog - Anrufliste mit Namensauflösung - Favoriten-Panel (manuell + häufig angerufen) - BLF-Panel (SIP Presence Monitoring) - Responsives Layout (kompakt/erweitert) - Click-to-Call (tel:/sip: URI via D-Bus) - KDE-Integration (Tray, Benachrichtigungen) - PipeWire/PulseAudio Audio-Routing - AppImage Build-Support (PyInstaller + appimagetool) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
110 lines
3.8 KiB
Python
110 lines
3.8 KiB
Python
"""Anruf-Benachrichtigung - KDE Plasma Notification mit Annehmen/Ablehnen."""
|
|
|
|
try:
|
|
import dbus
|
|
DBUS_VERFUEGBAR = True
|
|
except ImportError:
|
|
DBUS_VERFUEGBAR = False
|
|
|
|
|
|
class AnrufBenachrichtigung:
|
|
"""KDE-Benachrichtigung mit Action-Buttons über D-Bus.
|
|
|
|
Nutzt org.freedesktop.Notifications für native KDE Plasma
|
|
Benachrichtigungen mit Annehmen/Ablehnen-Buttons.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self._notification_id = 0
|
|
self._callback_annehmen = None
|
|
self._callback_ablehnen = None
|
|
self._bus = None
|
|
self._interface = None
|
|
|
|
if not DBUS_VERFUEGBAR:
|
|
return
|
|
|
|
try:
|
|
self._bus = dbus.SessionBus()
|
|
proxy = self._bus.get_object(
|
|
"org.freedesktop.Notifications",
|
|
"/org/freedesktop/Notifications",
|
|
)
|
|
self._interface = dbus.Interface(
|
|
proxy, "org.freedesktop.Notifications"
|
|
)
|
|
|
|
# Signal-Handler für Button-Klicks
|
|
self._bus.add_signal_receiver(
|
|
self._on_action,
|
|
signal_name="ActionInvoked",
|
|
dbus_interface="org.freedesktop.Notifications",
|
|
)
|
|
# Signal wenn Notification geschlossen wird
|
|
self._bus.add_signal_receiver(
|
|
self._on_closed,
|
|
signal_name="NotificationClosed",
|
|
dbus_interface="org.freedesktop.Notifications",
|
|
)
|
|
except dbus.DBusException:
|
|
self._interface = None
|
|
|
|
def anruf_anzeigen(self, anrufer, callback_annehmen=None, callback_ablehnen=None):
|
|
"""Benachrichtigung für eingehenden Anruf anzeigen.
|
|
|
|
Args:
|
|
anrufer: Anrufer-Nummer oder Name
|
|
callback_annehmen: Funktion bei Klick auf "Annehmen"
|
|
callback_ablehnen: Funktion bei Klick auf "Ablehnen"
|
|
"""
|
|
if not self._interface:
|
|
return
|
|
|
|
self._callback_annehmen = callback_annehmen
|
|
self._callback_ablehnen = callback_ablehnen
|
|
|
|
try:
|
|
self._notification_id = self._interface.Notify(
|
|
"SIP Softphone", # App-Name
|
|
0, # Replaces-ID (0 = neue Notification)
|
|
"call-start", # Icon (Freedesktop-Icon-Name)
|
|
f"Eingehender Anruf", # Titel
|
|
f"Anruf von {anrufer}", # Text
|
|
["annehmen", "Annehmen", "ablehnen", "Ablehnen"], # Actions
|
|
{
|
|
"urgency": dbus.Byte(2), # Critical - bleibt stehen
|
|
"category": "im.received",
|
|
"desktop-entry": "sipwebapp",
|
|
},
|
|
0, # Timeout: 0 = kein Auto-Close (Critical)
|
|
)
|
|
except dbus.DBusException:
|
|
pass
|
|
|
|
def schliessen(self):
|
|
"""Benachrichtigung schließen (z.B. wenn Anruf endet)."""
|
|
if self._interface and self._notification_id:
|
|
try:
|
|
self._interface.CloseNotification(self._notification_id)
|
|
except dbus.DBusException:
|
|
pass
|
|
self._notification_id = 0
|
|
self._callback_annehmen = None
|
|
self._callback_ablehnen = None
|
|
|
|
def _on_action(self, notif_id, action_id):
|
|
"""D-Bus Signal: Button wurde geklickt."""
|
|
if notif_id != self._notification_id:
|
|
return
|
|
|
|
if action_id == "annehmen" and self._callback_annehmen:
|
|
self._callback_annehmen()
|
|
elif action_id == "ablehnen" and self._callback_ablehnen:
|
|
self._callback_ablehnen()
|
|
|
|
self._notification_id = 0
|
|
|
|
def _on_closed(self, notif_id, reason):
|
|
"""D-Bus Signal: Notification wurde geschlossen."""
|
|
if notif_id == self._notification_id:
|
|
self._notification_id = 0
|