Skip to content

Commit 1ade565

Browse files
committed
Add D-Bus notification helper class for desktop notifications
1 parent 1d78ff6 commit 1ade565

File tree

1 file changed

+103
-0
lines changed

1 file changed

+103
-0
lines changed

not1mm/testing/notification.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
"""
2+
Simple D-Bus desktop notification helper.
3+
4+
Uses the org.freedesktop.Notifications D-Bus API via python-dbus.
5+
"""
6+
7+
from typing import Dict, Iterable, Optional
8+
9+
try:
10+
import dbus
11+
except Exception as _err: # pragma: no cover - runtime import check
12+
dbus = None
13+
14+
15+
class DbusNotification:
16+
"""Send desktop notifications over the session D-Bus.
17+
18+
Example:
19+
notifier = DbusNotification("MyApp")
20+
nid = notifier.notify("Hello", "This is a body", timeout=3000)
21+
22+
Methods:
23+
notify(summary, body, icon, timeout, actions, hints, replaces_id) -> int
24+
close(nid)
25+
"""
26+
27+
def __init__(self, app_name: str = "Not1MM") -> None:
28+
if dbus is None:
29+
raise RuntimeError(
30+
"dbus-python is required (install system package or 'pip install dbus-python')."
31+
)
32+
self.app_name = app_name
33+
self.bus = dbus.SessionBus()
34+
obj = self.bus.get_object(
35+
"org.freedesktop.Notifications", "/org/freedesktop/Notifications"
36+
)
37+
self.iface = dbus.Interface(obj, "org.freedesktop.Notifications")
38+
39+
def notify(
40+
self,
41+
summary: str,
42+
body: str = "",
43+
icon: str = "",
44+
timeout: int = 5000,
45+
actions: Optional[Iterable[str]] = None,
46+
hints: Optional[Dict[str, object]] = None,
47+
replaces_id: int = 0,
48+
) -> int:
49+
"""Send a notification.
50+
51+
Args:
52+
summary: short summary/title
53+
body: longer body text
54+
icon: icon name or path (string)
55+
timeout: milliseconds before the notification expires (int)
56+
actions: list of action identifiers and labels (flat list of strings)
57+
hints: dict of hint keys to values (e.g., {'urgency': dbus.Byte(1)})
58+
replaces_id: notification id to replace
59+
60+
Returns:
61+
notification id (int)
62+
"""
63+
actions = actions or []
64+
hints = hints or {}
65+
66+
actions_arr = dbus.Array(actions, signature="s")
67+
hints_dict = dbus.Dictionary(hints, signature="sv")
68+
69+
nid = self.iface.Notify(
70+
self.app_name,
71+
int(replaces_id),
72+
icon,
73+
summary,
74+
body,
75+
actions_arr,
76+
hints_dict,
77+
int(timeout),
78+
)
79+
try:
80+
return int(nid)
81+
except Exception:
82+
return nid
83+
84+
def close(self, nid: int) -> None:
85+
"""Close a previously shown notification by id."""
86+
try:
87+
self.iface.CloseNotification(int(nid))
88+
except Exception:
89+
# ignore failures to close (e.g., invalid id)
90+
pass
91+
92+
93+
if __name__ == "__main__":
94+
# Simple demo usage
95+
try:
96+
n = DbusNotification("Not1MM")
97+
nid = n.notify("Test notification", "This was sent over D-Bus", timeout=5000)
98+
print("Notification sent, id:", nid)
99+
except RuntimeError as e:
100+
print("Runtime error:", e)
101+
print(
102+
"On many systems you must install the system package 'python3-dbus' or similar."
103+
)

0 commit comments

Comments
 (0)