About
Contact Notification Sounds
Plays a unique sound per person, group or app on your notifications.
Configure a list of contacts β each mapped to a sound file (or a folder of sounds) β from the panel. When any notification arrives, the plugin checks its app name, title and body for your contacts' names (case-insensitive by default) and plays the assigned sound for the first contact matched.
Plugin
| Field | Value |
|---|---|
| ID | nilsonlinux/contact-sounds |
| Entries | Service: monitor; Panel: Panel; Shortcut: sounds; Bar widget: contact-sounds-widget |
Features
- Unique notification sounds per contact β you always know who messaged you before looking.
- Contacts, groups and apps β each entry can be typed as Contato, Grupo or App. Contact/group entries match the notification title and body; app entries match the notification app name only, so an app entry can't be triggered by a mention of its name in some other app's text.
- Photo / group photo / app icon in the list β every row in the panel starts with the entry's avatar: an optional photo/icon you give it, or the app's own icon for app entries (glyph placeholder otherwise).
- Folders of sounds β instead of a single
.ogg/.mp3, an entry can point to a folder; the panel lists that folder's sounds in a menu-list. Pick one sound for the entry, or leave Random (any sound) so any sound from the folder plays. - Panel of entries β open it from the control-center tile or the bar bell
icon: play each sound, add an entry (type + name + sound file or folder
- optional photo), edit, or delete (with an inline confirmation).
- Drag-and-drop reordering β grab any row's drag handle and drop it onto the thin strips between rows to change the list order; the order persists exactly like the entries themselves.
- Stop button for long sounds β when you test a sound longer than ~5s, the panel also shows a stop button that immediately cuts the audio (it destroys the active PipeWire stream; see How it works).
- Overlapping playback β rapid hits for the same contact are never swallowed: each play re-uses one of three ring-slot buffers so a fresh copy always starts from the beginning, even if the previous one is still playing.
- App filter β optionally restrict matching to specific notification apps (e.g. Telegram, Discord) so browser spam never matches.
- OGG/MP3/WAV β any local
.ogg,.mp3or.wavfile with an absolute path, or a folder containing them. - Hide the bar icon β toggle
show_widgetoff to remove the bell from the bar.
How it works
Entries are edited only from the panel, and the contact list is owned by the plugin itself β it does not live in Noctalia's app settings.
- The authoritative list is kept in the plugin's persistent data file
~/.local/state/noctalia/plugins/data/nilsonlinux/contact-sounds/state.json(vianoctalia.pluginDataDir(), which survives plugin updates and Noctalia restarts). Every add/edit/delete/reorder in the panel is sent as a command to themonitorservice (noctalia.state"contacts.cmd"), which applies it, writesstate.jsonimmediately and republishes β no config reload, so the panel never closes while you edit. - On startup, the service loads the list back from
state.jsonand republishes it ascontacts.data. The panel reads the current value withnoctalia.state.get("contacts.data")on load and watches it for changes, so a freshly opened panel always shows the real saved list β not whatever the app config still has. service.luaurunsscripts/monitor.sh, adbus-monitorpipeline that watches theorg.freedesktop.Notificationssession interface and forwards everyNotify()call asapp \x1e title \x1e body.- The service matches contact names in that text and plays the sound through
Noctalia's audio system (
noctalia.sound); plugin sounds are not silenced by Do Not Disturb. A folder entry resolves to its picked sound, or a random member, per hit. Sounds are loaded one per tick to respect the runtime's pending-load cap, and a rapid second hit reloads a ring slot so it is never suppressed while a previous copy is still playing. - Stop: there is no runtime "stop" API for playback, so the stop button
runs
scripts/stop_sounds.py, which finds the activenoctalia-soundPipeWire nodes inpw-dumpand destroys them withpw-cli. Afterwards the service invalidates the loaded buffers, so the next play always mints a fresh one.scripts/sound_durations.pyprobes how long each sound is (viaffprobe,mutagenor a.wavheader) so the stop button only appears for sounds longer than ~5s. - Exclusive sound: the service keeps
~/.config/noctalia/contact-sounds.tomlin sync with[notification.filter.contact-sounds-*]rules that setplay_sound = false(scripts/sync_filter.sh), so Noctalia's system notification sound is suppressed exactly when the plugin will play a contact's audio. The rules only cover contact sounds that loaded, are disabled while case-sensitive matching is on (system filters are always case-insensitive), and become one rule per allowed app whenonly_appsis set. The file is removed when the plugin is disabled or uninstalled, so the system sound can never be left muted. Notifications that match nothing keep their normal system sound. panel.luaurenders the list and all actions.widget.luaudraws the bar bell (hidden whenshow_widgetis off) andshortcut.luauadds the control-center tile; both open the panel.
Open the panel directly with:
noctalia msg panel-toggle nilsonlinux/contact-sounds:Panel
Requirements
dbus-monitor,gawkandpython3(all usually present on Arch/Debian) β declared as plugin dependencies.- The stop button additionally needs
pw-dumpandpw-cli(PipeWire). - Sound-duration probing optionally uses
ffprobe(ffmpeg) orpython3-mutagen; without them the stop button is simply not shown for sounds it can't measure.
Usage
Entries are managed only from the panel β the contact settings are hidden from Settings β Plugins so the list can't be edited outside the UI.
- Click the bar bell (or the control-center tile) to open the panel.
- Click + to add an entry:
- Type β
Contact,GrouporApp. - Name β type it exactly as it appears in a notification. The field starts empty for every new entry.
- Sound β a
.ogg/.mp3/.wavfile, or a sounds folder. When the form opens it suggests folders you have used before; the pencil/βlist button switches between picking an existing folder and typing a path. - Photo / icon path β optional avatar for the row.
- Type β
- Save β the entry is written to the plugin's
state.jsoninstantly and appears in the list. It survives plugin reloads, plugin updates and restarting Noctalia. - Play (
βΆ) β hear the entry's sound; Edit (β) and Delete (π, confirm withβ/βin the row) manage entries, and Drag any row's handle to reorder the list. - For folder entries, a menu-list shows the folder's sounds β pick one, or Random (any sound) so any file from the folder plays per notification.
- For sounds longer than ~5s a stop (
β) button also appears in the row, cutting off the playback immediately.
Example of what the list holds underneath (edited via the panel only):
"Maria Souza" = "/home/you/Documents/Sounds/maria.ogg"
"JoΓ£o Carlos" = "/home/you/Documents/Sounds/joao.ogg"
"FamΓlia" = "/home/you/Documents/Sounds/familia" # folder of sounds
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
contacts |
string_map |
{} |
"Name" = "/path/to/sound.{ogg,mp3,wav}" or a folder of sounds. (hidden β edited from the panel) |
contact_kinds |
string_map |
{} |
"Name" = "contact" | "group" | "app" (default contact). (hidden β edited from the panel) |
contact_images |
string_map |
{} |
"Name" = "/path/to/photo-or-icon.png" shown at the start of the row. (hidden β edited from the panel) |
folder_sounds |
string_map |
{} |
"Name" = "sound.ogg" picked inside a folder entry; empty = random. (hidden β edited from the panel) |
sound_folders |
string_map |
{} |
Registry of known sound-folder paths offered when adding an entry. (hidden β edited from the panel) |
contact_order |
string_list |
[] |
Contact names in display order; set by drag-and-drop in the panel. (hidden β edited from the panel) |
show_widget |
bool |
true |
Show the bell icon in the bar. |
match_case_sensitive |
bool |
false |
Match names with exact capitalization. (advanced) |
only_apps |
string_list |
[] |
App-name allow-list; empty = all apps. (advanced) |
glyph (widget) |
glyph |
bell-ringing |
Icon shown in the bar for the bell widget. |
Notes
Notification text and panel labels come from translations/<locale>.json
(English and pt-BR included).
The contact list is stored in the plugin's persistent data file
(noctalia.pluginDataDir()/state.json) β it is never kept in the app-managed
settings.toml, so panel-created entries survive config reloads and plugin
updates untouched.
~/.config/noctalia/contact-sounds.toml is managed by the plugin
(scripts/sync_filter.sh) and should not be edited by hand. Matching is always
case-insensitive for the exclusive-sound suppression; with only_apps set, a
contact must appear in the notification summary or body (a name that only shows
up in the app name is covered only when no app allow-list is configured).
Sound files are played at the Noctalia audio volume.
License
MIT
Versions
| Version | Plugin API | Updated |
|---|---|---|
| v1.5.2 latest | 20 | Sep 29, 2026 |
Older versions stay installable on a Noctalia release whose plugin API is below the latest version's.