Skip to content

Commit a939e0b

Browse files
committed
Initial commit. Port from sdbus central repository
0 parents  commit a939e0b

6 files changed

Lines changed: 584 additions & 0 deletions

File tree

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Freedesktop notifications binds for python-sdbus
2+
3+
Supports both asyncio (under `sdbus_async.notifications` module) and blocking (under `sdbus_block.notifications` module)
4+
5+
Implemented:
6+
7+
* NotificationsInterface - notifications interface to implement your own notifications daemon.
8+
* FreedesktopNotifications - notifications interface proxy connected to 'org.freedesktop.Notifications' and '/org/freedesktop/Notifications'
9+
10+
This is the sub-project of [python-sdbus](https://github.com/igo95862/python-sdbus).
11+
12+
See the [notifications interface documentation](https://python-sdbus.readthedocs.io/en/latest/proxies/notifications.html) and [python-sdbus Documentation](https://python-sdbus.readthedocs.io/en/latest/proxies/notifications.html).
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
# SPDX-License-Identifier: LGPL-2.1-or-later
2+
3+
# Copyright (C) 2020, 2021 igo95862
4+
5+
# This file is part of python-sdbus
6+
7+
# This library is free software; you can redistribute it and/or
8+
# modify it under the terms of the GNU Lesser General Public
9+
# License as published by the Free Software Foundation; either
10+
# version 2.1 of the License, or (at your option) any later version.
11+
12+
# This library is distributed in the hope that it will be useful,
13+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
# Lesser General Public License for more details.
16+
17+
# You should have received a copy of the GNU Lesser General Public
18+
# License along with this library; if not, write to the Free Software
19+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20+
from __future__ import annotations
21+
22+
from pathlib import Path
23+
from typing import Any, Dict, List, Optional, Tuple, Union
24+
25+
from sdbus import (DbusInterfaceCommonAsync, dbus_method_async,
26+
dbus_signal_async)
27+
from sdbus.sd_bus_internals import SdBus
28+
29+
30+
class NotificationsInterface(
31+
DbusInterfaceCommonAsync,
32+
interface_name='org.freedesktop.Notifications'):
33+
34+
@dbus_method_async('u')
35+
async def close_notification(self, notif_id: int) -> None:
36+
"""Close notification by id.
37+
38+
:param int notif_id: Notification id to close.
39+
"""
40+
raise NotImplementedError
41+
42+
@dbus_method_async()
43+
async def get_capabilities(self) -> List[str]:
44+
"""Returns notification daemon capabilities.
45+
46+
List of capabilities:
47+
48+
* "action-icons" - Supports using icons instead of text for \
49+
displaying actions.
50+
* "actions" - The server will provide the specified actions to the \
51+
user.
52+
* "body" - Supports body text.
53+
* "body-hyperlinks" - The server supports hyperlinks in \
54+
the notifications.
55+
* "body-images" - The server supports images in the notifications.
56+
* "body-markup" - Supports markup in the body text.
57+
* "icon-multi" - The server will render an animation of all \
58+
the frames in a given image array.
59+
* "icon-static" - Supports display of exactly 1 frame of any \
60+
given image array.
61+
* "persistence" - The server supports persistence of notifications.
62+
* "sound" - The server supports sounds on notifications.
63+
64+
:returns: List of capabilities
65+
:rtype: List[str]
66+
"""
67+
raise NotImplementedError
68+
69+
@dbus_method_async()
70+
async def get_server_information(self) -> Tuple[str, str, str, str]:
71+
"""Returns notification server information.
72+
73+
:returns: Tuple of server name, server vendor, version, notifications \
74+
specification version
75+
:rtype: Tuple[str, str, str, str]
76+
"""
77+
raise NotImplementedError
78+
79+
@dbus_method_async("susssasa{sv}i")
80+
async def notify(
81+
self,
82+
app_name: str = '',
83+
replaces_id: int = 0,
84+
app_icon: str = '',
85+
summary: str = '',
86+
body: str = '',
87+
actions: List[str] = [],
88+
hints: Dict[str, Tuple[str, Any]] = {},
89+
expire_timeout: int = -1, ) -> int:
90+
"""Create new notification.
91+
92+
Only ``summary`` argument is required.
93+
94+
:param str app_name: Application that sent notification. Optional.
95+
:param int replaces_id: Optional notification id to replace.
96+
:param str app_icon: Optional application icon name.
97+
:param str summary: Summary of notification.
98+
:param str body: Optional body of notification.
99+
:param List[str] actions: Optional list of actions presented to user. \
100+
List index becomes action id.
101+
:param Dict[str,Tuple[str,Any]] hints: Extra options such as sounds \
102+
that can be passed. See :py:meth:`create_hints`.
103+
:param int expire_timeout: Optional notification expiration timeout \
104+
in milliseconds. -1 means dependent on server setting, \
105+
0 is never expire.
106+
:returns: New notification id.
107+
:rtype: int
108+
"""
109+
110+
raise NotImplementedError
111+
112+
@dbus_signal_async()
113+
def action_invoked(self) -> Tuple[int, int]:
114+
"""Signal when user invokes one of the actions specified.
115+
116+
First element of tuple is notification id.
117+
118+
Second element is the index of the action invoked. \
119+
Matches the index of passed list of actions.
120+
"""
121+
raise NotImplementedError
122+
123+
@dbus_signal_async()
124+
def notification_closed(self) -> Tuple[int, int]:
125+
"""Signal when notification is closed.
126+
127+
First element of the tuple is notification id.
128+
129+
Second element is the reason which can be:
130+
131+
* 1 - notification expired
132+
* 2 - notification was dismissed by user
133+
* 3 - notification was closed by call to :py:meth:`close_notification`
134+
* 4 - undefined/reserved reasons.
135+
"""
136+
raise NotImplementedError
137+
138+
def create_hints(
139+
self,
140+
use_action_icons: Optional[bool] = None,
141+
category: Optional[str] = None,
142+
desktop_entry_name: Optional[str] = None,
143+
image_data_tuple: Optional[
144+
Tuple[int, int, int, bool, int, int, Union[bytes, bytearray]]
145+
] = None,
146+
image_path: Optional[Union[str, Path]] = None,
147+
is_resident: Optional[bool] = None,
148+
sound_file_path: Optional[Union[str, Path]] = None,
149+
sound_name: Optional[str] = None,
150+
suppress_sound: Optional[bool] = None,
151+
is_transient: Optional[bool] = None,
152+
xy_pos: Optional[Tuple[int, int]] = None,
153+
urgency: Optional[int] = None,
154+
) -> Dict[str, Tuple[str, Any]]:
155+
"""Create hints dictionary for :py:meth:`notify`.
156+
157+
All parameters are optional.
158+
159+
:param bool use_action_icons: When set, a server that has the \
160+
"action-icons" capability will attempt to interpret any action \
161+
identifier as a named icon.
162+
:param str category: The type of notification. (what types there are?)
163+
:param str desktop_entry_name: This specifies the name of the \
164+
desktop filename representing the calling program. \
165+
An example would be "rhythmbox" from "rhythmbox.desktop".
166+
:param Tuple[int,int,int,bool,int,int,Union[bytes,bytearray]] \
167+
image_data_tuple: This is a raw data image format which \
168+
describes the width, height, rowstride, has alpha, \
169+
bits per sample, channels and image data respectively.
170+
:param Union[str,Path] image_path: Path to notification image. \
171+
(alternative to desktop_entry_name)
172+
:param bool is_resident: When set the server will not automatically \
173+
remove the notification when an action has been invoked.
174+
:param Union[str,Path] sound_file_path: The path to a sound file \
175+
to play when the notification pops up.
176+
:param str sound_name: A themeable named sound to play. Similar to \
177+
icon-name, only for sounds. \
178+
An example would be "message-new-instant".
179+
:param bool suppress_sound: Causes the server to suppress playing \
180+
any sounds when this notification is displayed.
181+
:param bool is_transient: When set the server will treat \
182+
the notification as transient and by-pass the server's \
183+
persistence capability.
184+
:param Tuple[int,int] xy_pos: Specifies the X and Y location on the \
185+
screen that the notification should point to.
186+
:param int urgency: The urgency level. 0 low, 1 normal, 2 critical.
187+
"""
188+
189+
hints_dict: Dict[str, Tuple[str, Any]] = {}
190+
191+
# action-icons
192+
if use_action_icons is not None:
193+
hints_dict['action-icons'] = ('b', use_action_icons)
194+
195+
# category
196+
if category is not None:
197+
hints_dict['category'] = ('s', category)
198+
199+
# desktop-entry
200+
if desktop_entry_name is not None:
201+
hints_dict['desktop-entry'] = ('s', desktop_entry_name)
202+
203+
# image-data
204+
if image_data_tuple is not None:
205+
hints_dict['image-data'] = ('iiibiiay', image_data_tuple)
206+
207+
# image-path
208+
if image_path is not None:
209+
hints_dict['image-path'] = (
210+
's',
211+
image_path
212+
if isinstance(image_path, str)
213+
else str(image_path))
214+
215+
# resident
216+
if is_resident is not None:
217+
hints_dict['resident'] = ('b', is_resident)
218+
219+
# sound-file
220+
if sound_file_path is not None:
221+
hints_dict['sound-file'] = (
222+
's',
223+
sound_file_path
224+
if isinstance(sound_file_path, str)
225+
else str(sound_file_path))
226+
227+
# sound-name
228+
if sound_name is not None:
229+
hints_dict['sound-name'] = (
230+
's', sound_name
231+
)
232+
233+
# suppress-sound
234+
if suppress_sound is not None:
235+
hints_dict['suppress-sound'] = ('b', suppress_sound)
236+
237+
# is_transient
238+
if is_transient is not None:
239+
hints_dict['is_transient'] = ('b', is_transient)
240+
241+
# x
242+
# y
243+
if xy_pos is not None:
244+
hints_dict['x'] = ('i', xy_pos[0])
245+
hints_dict['y'] = ('i', xy_pos[1])
246+
247+
return hints_dict
248+
249+
250+
class FreedesktopNotifications(NotificationsInterface):
251+
def __init__(self, bus: Optional[SdBus] = None) -> None:
252+
"""Dbus interface object path and service name is
253+
predetermined.
254+
(at ``'org.freedesktop.Notifications'``,
255+
``'/org/freedesktop/Notifications'``)
256+
257+
:param SdBus bus:
258+
Optional dbus connection. \
259+
If not passed the default dbus will be used.
260+
"""
261+
super().__init__()
262+
self._connect(
263+
'org.freedesktop.Notifications',
264+
'/org/freedesktop/Notifications',
265+
bus,
266+
)

sdbus_async/notifications/py.typed

Whitespace-only changes.

0 commit comments

Comments
 (0)