1

I'm trying to toggle the "Announce the time:" checkbox on the "Dock & Menu Bar" pane of System Preferences (in Big Sur).

I've gotten this far:

tell application "System Preferences"
    activate
    reveal anchor "Clock" of pane id "com.apple.preference.dock" -- pane "Dock & Manu Bar"
    -- nothing I've tried here works
end tell
tell application "System Events"
    -- nothing I've tried here works
end tell

That reveals the correct pane with this checkbox:

enter image description here

I've tried using global coordinates with click at {x,y}, but that's ignored or I get a timeout error. (I'm able to click the buttons on the main Sys Prefs screen, so I know I have the coordinates correct.) I've also tried clicking the element named Announce the time:, but that didn't work either.

How can I toggle the Announce the time: checkbox?

0
2

Using Accessibility Inspector, a part of Xcode, it shows that some of the time the Announce the time: checkbox reports "Parent does not report element as one of its children" and as such will cause AppleScript code to fail under certain conditions, and under other conditions not fail. In other words, writing my own code I can get it to check that checkbox sometimes and not other times.

In trying to ascertain a pattern to see how to workaround this situation I came to the conclusion that using fully visible UI Scripting was the most reliable way. It's also unfortunately an ugly way and requires letting the script finish without interruption once started, otherwise it will fail for sure. Such are the pitfalls of fully visible UI Scripting.

The example AppleScript code, shown below, was tested in Script Editor under macOS Big Sur with Language & Region settings in System Preferences set to English (US) — Primary and worked for me without issue1.

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

This script requires that the Use keyboard navigation to move focus between controls checkbox is checked on the System Preferences > Keyboard > Shortcuts tab, and as coded, the script checks its status and toggles the checkbox, as necessary, based on its current status.


Example AppleScript code:

--  # Check to see if System Preferences is 
--  # running and if yes, then close it.
--  # 
--  # This is done so the script will not fail 
--  # if it is running and a modal sheet is 
--  # showing, hence the use of 'killall' 
--  # as 'quit' fails when done so, if it is.
--  #
--  # This is also done to allow default behaviors
--  # to be predictable from a clean occurrence.

if running of application "System Preferences" then
    try
        tell application "System Preferences" to quit
    on error
        do shell script "killall 'System Preferences'"
    end try
    delay 0.1
end if

--  # Make sure System Preferences is not running before
--  # opening it again. Otherwise there can be an issue
--  # when trying to reopen it while it's actually closing.

repeat while running of application "System Preferences" is true
    delay 0.1
end repeat

--  # Get the fully qualified POSIX pathname of the target .plist file.

set thePropertyListFilePath to ¬
    the POSIX path of ¬
        (path to preferences from user domain as string) & ¬
    ".GlobalPreferences.plist"

--  # Get the value of AppleKeyboardUIMode to determine if the
--  # 'Use keyboard navigation to move focus between controls'
--  # checkbox is checked on the **System Preferences** >  
--  # **Keyboard** > **Shortcuts** tab.

tell application "System Events" to ¬
    tell the property list file thePropertyListFilePath to ¬
        set keyboardNavigation to the value of ¬
            the property list item "AppleKeyboardUIMode"

if keyboardNavigation = 0 then
    --  # Check the checkbox.
    my toggleKeyboardNavagition()
end if

--  # Open System Preferences to the Clock row of the Dock & Menu Bar pane.
--  # 
--  # This UI Script needs it to be visible, hence the activate command.

tell application "System Preferences"
    activate
    reveal anchor "Clock" of ¬
        pane id "com.apple.preference.dock"
end tell

delay 1

--  # Tab to the 'Announce the time:' checkbox and toggle it.

tell application "System Events"
    repeat 9 times
        key code 48 --  # tab key
        delay 0.2
    end repeat
    key code 49 --  # spacebar
    delay 0.1
end tell

if keyboardNavigation = 0 then
    --  # Uncheck the checkbox if it
    --  # was previously unchecked.
    my toggleKeyboardNavagition()
end if

delay 0.5

tell application "System Preferences" to quit


--  # Handler(s) #


--  # Toggles checkbox: 'Use keyboard navigation 
--  # to move focus between controls'

on toggleKeyboardNavagition()
    tell application "System Preferences"
        activate
        reveal anchor "shortcutsTab" of ¬
            pane id "com.apple.preference.keyboard"
    end tell
    tell application "System Events"
        tell front window of ¬
            application process "System Preferences"
            set i to 0
            repeat until (exists checkbox 1 of tab group 1)
                delay 0.1
                set i to i + 1
                if i ≥ 20 then return
            end repeat
            click checkbox 1 of tab group 1
        end tell
    end tell
end toggleKeyboardNavagition
  • Note that because of the comments and coding style the script is long. It ends with end toggleKeyboardNavagition, so make sure you highlight all of it when copying and pasting to Script Editor for testing.

Notes:

In the tell application "System Events" block in with there is the repeat 9 times loop that does the tabbing in order to toggle the Announce the time: checkbox, the value of the delay command may need to be adjusted.

If the normal state of the Use keyboard navigation to move focus between controls checkbox is unchecked, then do not run the script immediately back to back as it takes a second or two for the value of the property list item "AppleKeyboardUIMode" in the users global preferences file to update the change. I'm mentioning this mainly for when doing testing more so than when in normal production use, as it shouldn't be an issue then.


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.

2
  • 1
    This is the accepted answer. The script works perfectly, and I appreciate your attention to detail (e.g., making sure the initial conditions are correct and restoring the "Use keyboard navigation" checkbox). I'd rather use the defaults write command (because AS doesn't work if the ScreenSaver is on), but I'd need a way to force the OS to update its cache. Meanwhile, your AS is good. Thank you.
    – Jeff
    Apr 23 at 22:00
  • @Jeff RE: "(because AS doesn't work if the ScreenSaver is on)," -- You could try adding tell application "System Events" to stop screen savers as the first line of code in the script. May 5 at 12:49
1

Given the lack of script-friendliness of the current incarnation of the System Preferences app, perhaps a more fruitful approach would be to use this answer on Ask Different to change the setting directly.

So to turn on time announcements, you would use both of the following:

do shell script "defaults write com.apple.speech.synthesis.general.prefs TimeAnnouncementPrefs -dict-add TimeAnnouncementsEnabled -bool YES "

do shell script "open /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/SpeechSynthesis.framework/Versions/A/SpeechSynthesisServer.app"

And to turn off announcements, you would use just

do shell script "defaults write com.apple.speech.synthesis.general.prefs TimeAnnouncementPrefs -dict-add TimeAnnouncementsEnabled -bool NO "

3
  • Hmm. I’m running Big Sur and I can confirm that checking the checkbox in System Preferences DOES change that particular defaults setting. Curiouser and curiouser. I wonder if there’s something needed to get the system to re-read the defaults setting?
    – Daniel
    Apr 22 at 15:25
  • Aha! For whatever reason, System Preferences will read the value correctly if you use -bool YES as the value but not if you use 1 as the value to indicate true.
    – Daniel
    Apr 22 at 15:29
  • 1
    OP here. My original method was to use the defaults command. The defaults command updates the .plist file, but the system caches the settings, so the change does not take effect in the OS itself. The only way to force a cache update is to (1) actually click the checkbox (hence my desire to use System Events) or (2) reboot.
    – Jeff
    Apr 22 at 16:26
-2

Here's my version, sans the clean-up at the start, which I recommend that you include.

tell application "System Preferences"
    activate
    delay 1

    reveal anchor "Clock" of pane id "com.apple.preference.dock"
end tell

tell application "System Events" to tell process "System Preferences"
    repeat until window "Dock & Menu Bar" exists
        delay 0.5
    end repeat

    repeat until checkbox "Announce the time:" of window "Dock & Menu Bar" exists
        delay 0.5
    end repeat

    tell window "Dock & Menu Bar"
        set theCheckBox to checkbox "Announce the time:"
        set theState to value of theCheckBox
        if theState is 0 then click theCheckBox
    end tell
end tell

tell application "System Preferences" to quit

PS: For some reason, I encountered some failure where the check box could not be found. Upon inspection with UI Browser, I noticed that the checkbox was detached. I'm not sure how the issue went away and it's working for me now. Also you may want to limit the wait period instead of waiting indefinitely.

3
  • If you look at the linked image you'll see Accessibility Inspector, a part of Xcode, reports the error Parent does not report element as one of its children and as such this script fails and does not work properly or consistently and why the OP couldn't resolve the issue prior to posting the question. May 2 at 12:47
  • Yeah I saw it, and like I've mentioned, it is working for me right now. I posted my answer anyway in hope of helping others. May 2 at 23:36
  • While it may be working for you now, nonetheless, it will ultimately fail again and so on. Posting an answer with this type of know issue is not an answer! May 2 at 23:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .