Do-not-disturb on every device¶
One do-not-disturb for all your devices: turned on or off on any of them, it holds on all, each silencing its own system as far as the system lets an application do it; one list of the people who may reach you then, kept in the sharing itself; and the phone kept in step in the background. Code: crates/sioul-core/src/everywhere.rs (the rules), crates/sioul-app/src/everywhere.rs (applying and saying it), crates/sioul-app/src/steps.rs and android/package/src/com/aurelienpierre/sioul/StepService.java (the phone in the background); the systems' own do-not-disturb is the layer of pauses.md (crates/sioul-app/src/dnd.rs).
The owner's words: "a global, cross-device do-not-disturb mode that communicates through the shared vaults between all instances of the app, and allows for example to turn the phone's DnD on or off from the desktop when in a task, with unified contacts management for the people allowed to go through the DnD mode across devices […] handled through phone calls, SMS and email."
Why it holds¶
Do-not-disturb holds while one of its reasons does. Each reason is read from the one place that already says it, and that place is shared as it is: every device reads the same files and comes to the same answer, and nothing is said twice.
| Reason | Read from (part of the sharing) | Setting ([dnd]) |
Ends |
|---|---|---|---|
| the pause | quiet.toml, paused_since/paused_ended (time) |
pauses, on |
when you come back |
| Free time | quiet.toml, free_since/free_ended (time); never with the pause |
pauses, on |
when you come back, at the night's start |
| sleep | Health's night and naps (health), on each device's clock | sleep, off |
at waking |
| focus | time/running.toml (time): a session counting, not paused |
focus, off |
30 minutes past the time chosen, three hours without one |
| the switch | state/do-not-disturb.toml (settings) |
button, on: the switch shows |
when you turn it off, or at the end you chose |
- The latest change wins, the switch against the reasons too: the switch pressed off after a reason began holds that reason off until it ends; a reason that begins after the press holds again (the next focus session, the next night). A press "on" never holds a reason off. Pressed off during a pause, the switch lifts only the system's do-not-disturb: the pause still holds Sioul's own notifications.
- A focus session forgotten never keeps you unreachable for long: it holds do-not-disturb until 30 minutes past the time chosen, three hours for a session without one (
FOCUS_GRACE,FOCUS_OPEN). - Said first: the pause, Free time, sleep, focus, the switch. The end said is that of every reason that holds, when each has one; a focus session's limit is never said (it is no promise).
-
Its modes: the pause and Free time keep their own (pauses.md); sleep, focus and the switch share a third, "Do not disturb (Sioul)" (
dnd::Which::Global). Each lets through what the matrix of what reaches you lets through then (attention.md, §3.3:Attention::silenceat the pause's column, Free time's with its "Nothing at all", and for the third the strictest of Sleep's and Do not disturb's that hold), as far as each system can say it. On a phone (android.md):Through the mode When Calls from your contacts the phone screens calls, and the matrix lets someone known or Always through call then (the screening declined the others before the phone rang) Calls from anyone the phone screens calls, and the matrix lets strangers or hidden numbers call then Calls from starred contacts the phone does not screen calls, and the matrix lets anyone call then Messages from starred contacts the matrix lets anyone write then (never more: Android rings before Sioul's listener can hold, and the mode is what keeps a held message silent) A second call within 15 minutes whenever calls get through Priority conversations (Android 11 and later) the Always through row of Messages lets them come then: the conversations set to Always through, marked Priority in Android Alarms always Dose reminders, an event's alarms the matrix lets them come then, each in a channel of its own that passes ("Doses during a pause", "An event's alarms during a pause") New mail from someone Always through told then, in its own channel that passes ("New mail from people always let through") With the usual matrix every mode lets through Always through (their calls as the screening lets them ring; the starred's messages; priority conversations), a second call, alarms, doses and an event's alarms (in sleep and Free time, when its event falls then); Free time lets your safe people call too, but with "Nothing at all". Each mode's policy is worked out again at each apply (the minute's, a press, an exchange), and the mode brought up to date when it changed.
The switch, shared¶
state/do-not-disturb.toml, in the settings part: the one part every device shares, where the triggers' own settings travel ([dnd] in config.toml). One table per device, written by that device alone and shared whole (whole = ["device.*"]): two devices never fight over one entry in the sharing's later-wins, and the merge is Sioul's own, in code.
[device."<the device's id in the sharing>"]
kind = "phone" # or "computer"
name = "" # a computer's host name
pressed = 1759760000000 # its last press of the switch (milliseconds, a hybrid clock)
on = true # what that press asked
until = 1759765400000 # the end chosen with it; 0: until turned off
seen = 1759754000000 # the latest press of another device known here then
why = "manual" # what holds here now, as this device reads the reasons
silenced = true # its system's do-not-disturb is on for it
line = "…" # its system's answer, in its words
at = 1759760000000 # when this table was written
via = "system" # its last press came from its own do-not-disturb (absent: the switch)
turned_off = true # while it holds: its own do-not-disturb turned off unheard, left off
system_on = true # while it is off: its system still silenced (`line` says why)
closed = true # Sioul quit on this computer: it follows nothing until it starts again
-
Sioul closed on a computer: as Sioul quits (its window closed, the tray's Quit, SIGTERM, SIGINT, SIGHUP), the computer writes its table as it then holds (
Device::closing:closed,silencedonly for what outlives Sioul, GNOME's switch or a Mac's Focus, Plasma's inhibitions ending with it), sent by the last exchange; the other devices say "On laptop: Sioul is closed." (Follows::Closed). A crash writes nothing: a computer said silenced but not heard from for half an hour (COMPUTER_QUIET; a running computer writes its news at least every fifteen minutes) is "waiting for its news". A phone's table holds while Sioul is closed: its modes stay. -
Latest wins, whatever the clocks: a press is stamped after every press already known on that device (a hybrid clock: the later of now and the latest stamp seen, plus one), so a press made after seeing another comes after it even when the clocks disagree.
- A margin for clocks that disagree: two presses on two devices that did not know of each other (
seenbefore the other's stamp), less than a minute apart (MARGIN_MS): "on" wins, the quieter choice; one more press, now seeing the other, settles it. Further apart, the later stamp wins. - The end passes on each device by its own clock: no record needed.
- Never written over a file that does not read (half written by hand, damaged): the press fails and says why; the other devices' tables are never written from here.
Applying it¶
everywhere::apply() is the one caller of the do-not-disturb layer: for each of Sioul's three modes, on when its reason holds, off otherwise; the layer does nothing when nothing changed. Called at the window's start and each minute (the pauses' tick), at once on a press, and after an exchange that wrote the switch, quiet.toml, running.toml or the settings. Then this device's table says what holds here and whether its system is silenced, the phone's card is told (homecard::dnd_seen), and on a phone an alarm comes back at the next end (below).
- On Android, only in Sioul's own process, where the layer's modes are kept (
PauseMode's memory): the background service, in a process of its own, asks it throughDndReceiver; so does the alarm of the next end (the switch's end, waking, the next night when sleep counts, Free time's end, a focus session's limit), set exact through Doze after each apply, Sioul closed or not (steps::next). - Where it holds, in the status line beside the sounds (
qml/DndApplet.qml): "Do not disturb, on every device, until 15:00." when every other device heard from in the last seven days (LIVE_DAYS) holds it with its system silenced, and this one too; "here; your other devices' news is on its way" while every device not silenced yet has simply not been heard from since it began (a device counts only by a table it wrote after do-not-disturb began, a minute's margin for clocks that disagree: its line says "waiting for its news", never what held there before; the news comes at the sharing's pace, about a minute with a Nextcloud server); "here only" when there is no other device, or none can follow; "not on every device" in between; "on your other devices; this one keeps only Sioul's own notifications back" when this device's system cannot be silenced (Windows, a desktop without a way, a phone without the access). The menu (a right click, a long press) lists each device in its own words: "On your phone: silenced.", "On laptop: This computer's notifications cannot be silenced by Sioul: Windows…", "On another device: waiting for its news.", "On laptop: an older Sioul, which cannot follow until it is updated." No colour but the accent while it is on. - The switch's menu: for 30 minutes, an hour, two hours; until what now is for ends (the end of working hours, say); until you turn it off; off, on every device; its settings.
Both ways¶
The owner's words (7 October 2026): "Yes, do not disturb is a property of an individual, not of a device, so it should be synced across all devices." Each device's own do-not-disturb and the switch mirror each other. Sources: AOSP 12, 15 and 16 (NotificationManagerService, ZenModeHelper, ZenModeConfig, NotificationManager's documentation), plasma-workspace 6.7.5 (libnotificationmanager, the notifications applet's Globals.qml), GNOME Shell (js/ui/status/doNotDisturb.js) and gsettings-desktop-schemas.
- Heard as it happens, whatever caused it (
everywhere::heard_from_system,sioul_core::everywhere::heard): each system's do-not-disturb as one fact, on or off (Android's interruption filter not ALL, Plasma'sInhibited, GNOME'sshow-bannersfalse). A change that disagrees with what holds on that device, once Sioul's own change settled (three seconds) and the state read again, is a press of the switch there (via = "system"): the person's tile or applet, Bedtime, a schedule, a driving mode, another app, Plasma's full screen or mirrored screens. One that agrees is Sioul's own change coming back: nothing. One found later, not heard as it happened (Sioul closed on a computer, a phone still starting), is only said on that device ("turned off here", "… stays on"), never pressed: its time is unknown, and another device may have pressed since. The repeats of one change (a status and a filter, two of Plasma's signals) press once (HEARING).- Android:
DndReceiver, declared in the manifest and not exported, hearsACTION_INTERRUPTION_FILTER_CHANGED, which Android sends every app with Do Not Disturb access, running or not (AOSPNotificationManagerService.sendRegisteredOnlyBroadcast: "explicitly send the broadcast to all DND packages, even if they aren't currently running"), andACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGEDfor Sioul's own modes (ENABLED, DISABLED, REMOVED; ACTIVATED and DEACTIVATED from Android 15, Sioul targeting 36). Both are protected broadcasts.DndHeard.decidedecides in Java: the filter as a change from the state last seen (seen_on; at a restart or an update, the state found then is noted, never pressed, so that the next change counts from it: forgetting it instead, as 0.0.3 did, made the first change after each update or restart a "first sight", never heard; noted too at the first apply when none was ever seen), against Sioul's record (sioul_on, which Rust writes at each apply;on.<kind>); a status only against the mode as Android has it now, so that a late word of Sioul's own update is no press. Before each change of its own,PauseModenotes when it settles (changing_until); a change heard before then is looked at again after it (three looks at most). The library is loaded only for a press (nativeHeard). The notification listener, while bound (whenever Sioul has notification access), hears every change too (AppNotes.onInterruptionFilterChanged, which Android calls on a bound listener at each change of the filter) and forwards it toDndReceiverin Sioul's own process (DndReceiver.HEARD): one change, two words, decided one at a time, the second finding nothing new. Each decision is logged (adb logcat -s sioul: "Do not disturb: the filter heard: on."), nothing of the person's in it. - Plasma:
sioul_sync::dnd::watch_inhibitedfollowsPropertiesChangedofInhibitedfrom the server's owner; a change that held a second is read again (Properties.Get) and told once Sioul's own change settled (quiet, set before eachInhibit,UnInhibitorUntildeleted). A new server (plasmashell started again) is read without telling. - GNOME: dconf's
Notifyofshow-banners(watch_dconf_told), read again withgsettingsa second later, once Sioul's own write settled. - Mac and Windows: not heard. macOS gives an ordinary application no change of the Focus (macOS 26's Shortcuts automations, "When Do Not Disturb is turned off", could call a command of Sioul's: not built); Windows has no documented do-not-disturb for applications (
FocusSessionManagersays only the Clock app's focus sessions).
- Android:
- Off reaches the system (
apply,dnd::system_off): when do-not-disturb goes from on to off on a device (Kept::on_here, kept across restarts), Sioul's modes leave as before, then the system's own do-not-disturb is turned off, once, where Sioul can:- Android 12 to 14:
setInterruptionFilter(INTERRUPTION_FILTER_ALL), with Sioul's Do Not Disturb access. Android also snoozes every other mode on at that moment (Bedtime, a schedule, a driving mode) until it ends by itself. - Android 15 and later: an application that targets 15 or later may end only a mode of its own (
MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES). The switch says "This phone's own do-not-disturb stays on: turn it off in its quick settings.", with Android's do-not-disturb settings (open("modes")). - Plasma: Plasma's own,
[DoNotDisturb] Untilinplasmanotifyrc, deleted withkwriteconfig6 --file plasmanotifyrc --group DoNotDisturb --key Until --delete --notify '', KDE's own tool;--notifymakes Plasma's settings reload the group at once (KConfigWatcher). Another application's inhibition, a full-screen window or mirrored screens are not Sioul's to end: "Plasma keeps this computer's notifications back for another application or a full-screen window; it ends with them." - GNOME:
show-bannersback to true, with the person's yes ([pause] gnome); without it, "GNOME's Do Not Disturb stays on: turn it off in the top bar." - While Sioul's is off, what the system still holds is said (
dnd::still_on; this device's table:system_on,line). Nothing is ever locked: no immutable setting, never a second "off" for one change.
- Android 12 to 14:
- No mode over the system's own cause (
sioul_core::everywhere::held_by_system,Ask::stack): when the only reason for the switch's mode on a device is a press that device heard from its own system, Sioul adds no mode there: Android shows an application only the effective filter, so a mode of Sioul's would hide the end of a schedule. The device counts as silenced ("This phone is silenced by its own do-not-disturb…"); on Android, Sioul's channels that pass are still used (held.<kind>). The pause, Free time, focus and sleep enter Sioul's modes as before; when they leave, a cause that ended meanwhile shows, and becomes the "off" it is. - No loops: Sioul's own changes agree with what holds, and the settle covers the moment they take. A system "on" again within the settle after Sioul turned it off is on before and on after: neither a press nor a second "off".
- Said: while it holds, "Do not disturb on your other devices; you turned it off here." (
Here::turned_off), with Silence this device again (everywhere::again: a switch "on" heard from the system becomes the switch's own, then each mode leaves and enters afresh; on Android 16 itsSOURCE_USER_ACTIONends the person's override); while off, the "… stays on" line with what can be opened, and the last "off" heard from a system in the switch's tip ("Turned off on your phone at 14:02, outside Sioul.",last_off, twelve hours). - The phone's tile (
DndTile): "Do not disturb (Sioul)" among the quick settings, Sioul's switch pressed without opening Sioul (DndReceiver.TOGGLE,everywhere::toggle_here), lit as Rust last said (sioul_on). - Sent at once: a press made outside the window goes with the window's exchange; on a phone without the window, the background service steps at once (
StepService.HEARD, the reason "heard": no sync app asked first), or, when it does not run, Sioul's own process exchanges itself (steps::send_now).
Always through¶
config/dnd-people.toml, in the senders part, beside your lists: the people who must always reach you, each with a name, numbers and addresses, kept in the sharing itself, since the address books of two devices may not be in step. One entry per person ([[person]], keyed by its id): two devices adding two people keep both; one changed on two devices keeps the later.
- Edited in Settings ▸ What reaches you ▸ Exceptions (
qml/DndSetup.qml, its part "list"), and one person at a time from their sheet (PersonSheet.qml): Add your safe senders (each address on the Safe list, with its card's name and numbers when a card has it; each card and each number on it; everyone whose card is in a category on it; patterns such as@example.orgortel:+3319900*name nobody, said and left out), Add a contact… (from your address books), Add someone by hand, Change, Take off the list. Someone added again with one of their addresses, numbers or their card is merged into the one already there. A person without a number, or without an address, is said so under their name. - What it means is the Always through row of each channel in the matrix (attention.md), the same at every time and under do-not-disturb: as usual their calls ring and their messages come at once at any time, sleep and pauses included; their mail comes at once, told at critical urgency on a computer, but is shown without a notification while you sleep or pause. A cell may say "as their list" (=) instead. The blocked never, even on the list: blocking someone takes them off it, and putting someone blocked on it takes them off the blocked list (
porch::set_standing,porch::unblock_person). - Under do-not-disturb, everyone else is held as the Do not disturb column says: their mail shown, not told; their calls to voicemail; their messages held. Sioul's own come as their rows say: doses, codes, reminders and Health's notices; sites, other apps' automatons and the pause to move wait. Nothing is hidden from the Porch itself: do-not-disturb is about being interrupted.
- On a computer, no calls nor SMS: the list's mail comes at critical urgency, which GNOME's Do Not Disturb and Plasma's show. Plasma hides even critical ones when
[Notifications] CriticalInDndMode=false(the owner's setting), unless Sioul may "Show in do not disturb mode" ([Applications][com.aurelienpierre.Sioul] ShowPopupsInDndMode): What reaches you ▸ Do not disturb reads both, says that the list's mail cannot show then, and offers Plasma's notification settings at Sioul's page. Sioul never writes Plasma's configuration. - On a phone, calls and messages: Android has no list of its own (research: emergency-pause.md 7.1): a mode lets through calls and messages from nobody, starred contacts, contacts or anyone, a second call from the same number within 15 minutes, and the conversations marked Priority (Android 11 and later). Sioul's modes let through, as far as Android can say it, what the matrix lets through ("Its modes", above): while the phone screens calls, the Always through people's calls ring through the mode when they are among the phone's contacts (the screening declined the others before the phone rang), else when they are starred; their messages when they are starred; their conversations once marked Priority in Android, which What reaches you ▸ Exceptions opens beside a conversation set to Always through (android.md). Sioul never writes a star nor a contact (the coordinator's decision of 6 October 2026, pending the owner's: stars are your favourites everywhere, may travel with your contacts' sync, and a crash or an uninstall could leave Sioul's behind). It reads (READ_CONTACTS, asked in Settings ▸ This phone with its reason) who on the list is starred on this phone, number by number (
ContactsContract.PhoneLookup, an interface unchanged since Android 2, the same on 12 to 16), and says "Two people on your list are not starred on this phone", with each one's Open their contact (the Contacts app, where the star is) or, when no contact has their number, Add to contacts (the Contacts app's own form, filled; you save it): someone in no contact of the phone rings through no mode. Repeat callers stay let through: emergency services may call back from a number you do not know.
The phone in the background¶
"Sioul keeps your devices in step": a foreground service in a process of its own, with one quiet notification, follows your other devices while Sioul is closed: do-not-disturb within a few minutes, mail at its times. See android.md: how it wakes through Doze, what it costs, what "working" means for it (it never says the phone is in use: its exports only refresh when it last shared). A phone's sync app late or stuck does not hold the switch back where the sharing folder lives on a Nextcloud one of your accounts reaches: Sioul reads the others' files there too (database.md); with no sync app at all, Sioul keeps the folder in step itself (database.md).
Older Sioul¶
- A Sioul that does not know
state/do-not-disturb.tomlnorconfig/dnd-people.tomlkeeps their records waiting for a version that knows them (the sharing'sknown_part): it writes neither, takes nothing of them out, and shares the rest. It ignores[dnd]inconfig.tomland keeps it as it is. - It writes no table of its own, nor an entry in the devices' registry: the status line says it runs an older Sioul, which cannot follow until it is updated, and never waits for it.
- Its pauses still silence it, as the pauses always did on each device.
Tested¶
cargo test --release -p sioul-core everywhere: the latest press wins whatever device made it; a press after seeing another comes after it whatever the clocks; two presses that did not see each other within a minute turn it on, further apart the later; the end by each device's clock; each trigger a setting, the pause first, Free time never with it; an "off" pressed after a reason began holds it off until it ends; a forgotten focus session lets you be reached again; mail and Sioul's own notifications gated by the switch and focus only; where it holds, in words, English and French; who counts as another device; only this device's table written and a broken file left alone; the list admits its people by address and number, merges, and travels a person at a time, never read empty; the Safe list brings its people with their cards; the settings' defaults; every word in both languages.cargo test --release -p sioul-sync do_not_disturb: two devices sharing one folder, each device's table travelling whole and never written by the other, the list a person at a time (two added apart both kept, one taken off gone everywhere), and a Sioul that knows neither file writing neither and taking nothing out.dbus-run-session -- cargo test --release -p sioul-app everywhere, then… steps: what each mode asks; who on the list is not starred, in words; the background service's rhythm; mail at its rhythm, never asleep nor paused, nor without accounts or the setting; nothing reaches Java on a computer; no permission to write contacts in the manifest, and no write inDndContacts.java. The do-not-disturb layer's own (… dnd::) cover the third mode beside the pauses, and the line a phone's mode says, read back from Android: today's two sentences, the matrix's parts (calls from contacts, messages from the starred, priority conversations), as Android changed them, Android 10's missing conversations, an event's alarms through or silenced.- The Java of the modes, compiled against
android-36and checked on a JVM (android/jvm-checks/ModeCheck.java): the policy made of Rust's ask (the matrix's words, nested or beside the older ones, or the olderpeoplealone), which modes let a channel that passes be used, an event's own alarm told apart from Sioul's reminder, a conversation's key as Rust's. - The record's fields a later version adds are kept and written back as they were (
settings_have_their_defaults_and_old_files_read): this device never sends another version's table or person as changed. - Both ways:
cargo test --release -p sioul-core everywhere(a change of the system presses only when it disagrees; a press heard from the system says so and holds like any; the switch held by the system alone adds no mode; a nightly schedule turns it on, then off, on every device; a press made off line meets the latest; a device turned off by hand says so, in both languages);dbus-run-session -- cargo test --release -p sioul-sync dnd(the session'sInhibitedfollowed against a stand-in server: a change that holds is told, a flicker is not, nothing before Sioul's own change settled, on again within the settle is nothing, a new server is read without telling; dconf's changes of a key told);dbus-run-session -- cargo test --release -p sioul-app dnd(held by Plasma's or GNOME's own, its end unheard said; an inhibition ended from Plasma's applet said and never asked again; Sioul's off turning the system's own off where it can and saying the rest; GNOME's switch turned off while Sioul was closed; KConfig's dates;kwriteconfig6in a folder of the test's own, telling nobody). The Java on a JVM (android/jvm-checks/HeardCheck.java:DndHeard.decide,PauseMode.kindOf,passesNowwith a held mode): the tile's off and on, a schedule's end, Sioul's own changes agreeing, the settle, on again within it, a first sight, the first change after an update or a restart heard both ways (the state found then noted,DndHeard.noted), Android 15's statuses against the mode as it is now.
Not built¶
- Sioul starring the list's people itself (WRITE_CONTACTS, with your yes, its own stars only, taken back when someone leaves the list): held until the owner decides; it would build on the guided version.
- A device's words in the others' language: each device writes its system's answer in its own language.
- Measured on a phone: estimated so far (android.md).
- A command for macOS's Shortcuts automations (macOS 26: "When Do Not Disturb is turned off" running
sioul dnd …): possible, not built; nothing else lets Sioul hear a Mac's Focus. - Both ways tried on Plasma itself: built and checked on a computer (Rust's tests, the Java on a JVM:
android/jvm-checks/), not yet on Plasma itself. Tried on a phone, below.
Tried on a phone¶
8 October 2026, a phone on Android 12 (/e/OS), Sioul with Do Not Disturb access and notification access; the phone's own do-not-disturb turned on, then off, with adb shell cmd notification set_dnd on and off, and put back as it was; the times from adb logcat -s sioul and the notification read every quarter of a second (dumpsys notification):
- 0.0.3: the first change after the update was not heard ("first sight", above); the next ones were, within a second (Android 12 sends
ACTION_INTERRUPTION_FILTER_CHANGEDto the manifest receiver of a package with Do Not Disturb access). The background service's notification kept "Every call rings until 09:32." at 09:39: its words came only with a step, and no step runs while the window is on the screen. - With the fix, Sioul's window closed (just updated): the first change after the update heard at once ("the filter heard: on", the switch pressed and sent); the listener's word of it found nothing new; off heard at once, Sioul's mode left. The notification said each change within a tenth of a second (76 milliseconds after "on" was heard: "Do not disturb here; your other devices' news is on its way."; 45 after "off": "Do not disturb is off."), after a first try showed "off" only 3.4 seconds late: its words waited in Android's queue of background broadcasts behind
DndReceiver's own, held open while Sioul's change settled; they go on the foreground queue now, told once this device's table is written. - Sioul's off turns the phone's off: the phone's own on (heard, Sioul's switch on), then Sioul's switch pressed off in its status line: the phone's own off 45 milliseconds later (Android 12,
setInterruptionFilter(ALL)), the notification 90 milliseconds after. - Not checked: a computer following the phone's press (the press was sent: "sharing: 1 sent"); Android 15 and later, where Sioul says the phone's own stays on.