Turn Do Not Disturb on or off in an Applescript app (macOS Catalina)

Solution 1:

The example AppleScript code, shown below, was tested under macOS Catalina with Language & Region settings in System Preferences set to English (US) — Primary and worked for me without issue1 to turn Do Not Disturb either on or off.

  • 1 Assumes necessary and appropriate setting in System Preferences > Security & Privacy > Privacy have been set/addressed as needed.

The setDoNoDisturbTo() handler takes one parameter, "On" or "Off".

Example AppleScript code:

my setDoNoDisturbTo("Off")


on setDoNoDisturbTo(OnOff)
    set checkDNDstatusCMD to ¬
        {"defaults -currentHost read", space, ¬
            "~/Library/Preferences/ByHost/", ¬
            "com.apple.notificationcenterui", ¬
            space, "doNotDisturb"} as string
    set statusOfDND to ¬
        (do shell script checkDNDstatusCMD) ¬
            as number as boolean
    if statusOfDND is false and OnOff is "On" then
        set OnOff to true
    else if statusOfDND is true and OnOff is "Off" then
        set OnOff to false
    else
        return
    end if
    set changeDNDstatusCMD to ¬
        {"defaults -currentHost write", space, ¬
            "~/Library/Preferences/ByHost/", ¬
            "com.apple.notificationcenterui", ¬
            space, "doNotDisturb -boolean", space, OnOff, ¬
            space, "&& killall NotificationCenter"} as string
    do shell script changeDNDstatusCMD
end setDoNoDisturbTo

Note: The example AppleScript code is just that and sans any included error handling does not contain any additional error handling as may be appropriate. The onus is upon the user to add any error handling as may be appropriate, needed or wanted. Have a look at the try statement and error statement in the AppleScript Language Guide. See also, Working with Errors. Additionally, the use of the delay command may be necessary between events where appropriate, e.g. delay 0.5, with the value of the delay set appropriately.

Solution 2:

This is not as elegant as I was hoping for but it seems to work.

After playing with the Record feature for a while, I seem to be able to turn DND on when it's off:

-- Click the “Notification Center” menu bar item.
delay 1.109957
set timeoutSeconds to 0.0
set uiScript to "click menu bar item \"Notification Center\" of menu bar 1 of application process \"SystemUIServer\""
my doWithTimeout(uiScript, timeoutSeconds)

-- Click the “<fill in title>” button.
delay 2.096055
set timeoutSeconds to 0.0
set uiScript to "click UI Element 3 of group \"Do Not Disturb\" of scroll area 1 of window \"Notification Center\" of application process \"Notification Center\""
my doWithTimeout(uiScript, timeoutSeconds)

on doWithTimeout(uiScript, timeoutSeconds)
    set endDate to (current date) + timeoutSeconds
    repeat
        try
            run script "tell application \"System Events\"
" & uiScript & "
end tell"
            exit repeat
        on error errorMessage
            if ((current date) > endDate) then
                error "Can not " & uiScript
            end if
        end try
    end repeat
end doWithTimeout

and vice versa:

-- Click the “Notification Center” menu bar item.
delay 2.508349
set timeoutSeconds to 2.0
set uiScript to "click menu bar item \"Notification Center, Do Not Disturb enabled\" of menu bar 1 of application process \"SystemUIServer\""
my doWithTimeout(uiScript, timeoutSeconds)

-- Click the “<fill in title>” button.
delay 1.176098
set timeoutSeconds to 2.0
set uiScript to "click UI Element 3 of group \"Do Not Disturb\" of scroll area 1 of window \"Notification Center\" of application process \"Notification Center\""
my doWithTimeout(uiScript, timeoutSeconds)

on doWithTimeout(uiScript, timeoutSeconds)
    set endDate to (current date) + timeoutSeconds
    repeat
        try
            run script "tell application \"System Events\"
" & uiScript & "
end tell"
            exit repeat
        on error errorMessage
            if ((current date) > endDate) then
                error "Can not " & uiScript
            end if
        end try
    end repeat
end doWithTimeout

It would be better if there were a solution that checked its state or explicitly specified the desired new state instead of just toggling. There is a small difference between the "on" and "off" scripts so it's not exactly toggling but it works by clicking the button.