Joe Gilmore

6 min read

Make Your Mac Speak the Time Only While You’re Working

A simple little macOS automation that announces the time every hour while Work Focus is active, follows your Mac volume, and never shouts at you louder than 40%.

Make Your Mac Speak the Time Only While You’re Working

Make Your Mac Speak the Time Only While You're Working

I really like the built-in macOS feature that announces the time every hour.

It sounds ridiculously simple, but on busy days I find it surprisingly useful. When I'm buried in coding, emails, or some other task, hearing:

"The time is 10 A M."

gives me a tiny reality check.

Wait... I've been doing this for an hour already?

The problem with Apple's normal Announce the time setting is that it's basically either on or off. I don't particularly want my Mac announcing the time while I'm watching something, relaxing in the evening, or generally not working.

What I really wanted was:

Only announce the time while my Mac's Work Focus is active.

And while we're at it, I wanted one extra safety feature.

If I've accidentally left my Mac volume at 100%, I absolutely do not want a robotic voice unexpectedly yelling "THE TIME IS NINE A M" across the room. 😅

So this little setup:

  • Uses macOS Work Focus as the master on/off switch
  • Announces the time once every hour
  • Deliberately skips 6:00 AM
  • Automatically stops when Work Focus turns off
  • Works if I manually enable Work Focus outside my normal schedule
  • Follows the Mac's current volume
  • Caps the announcement itself at 40% maximum volume
  • Doesn't alter the Mac's actual system volume
  • Requires no paid software or additional apps

Once installed, there is nothing to remember. It just quietly does its thing.

Before We Start: Give Terminal Full Disk Access

There’s one slightly hidden macOS permission we need first.

Our script checks whether Work Focus is currently active by reading macOS’s Focus database. By default, Terminal is not allowed to read that file, so without this permission you’ll get an error similar to:

The file “Assertions.json” couldn’t be opened because you don’t have permission to view it.

To fix that:

  1. Open System Settings
  2. Go to Privacy & Security
  3. Select Full Disk Access
  4. Enable Terminal

If Terminal isn’t already listed, click the + button and add:

/Applications/Utilities/Terminal.app

Then quit Terminal completely and reopen it so the new permission takes effect.

Full Disk Access sounds rather dramatic, but in this setup we only use Terminal to read macOS's Focus status database. The script does not modify that database.

You can now continue with the Work Focus detection test below.

Step 1: Set Up Your Work Focus

First go to:

System Settings → Focus → Work

Configure your Work Focus however you normally use it.

For example, mine is scheduled for:

6:00 AM → 3:00 PM

The important thing is that our script doesn't contain that schedule.

Instead, the script asks macOS:

"Is Work Focus active right now?"

That means I can change my Work Focus schedule later without ever touching the script.

Even better, if I manually disable Work Focus at lunchtime, the announcements stop too.

Step 2: Check That We Can Detect Work Focus

Turn Work Focus ON.

Then open Terminal and run:

/usr/bin/plutil -extract data.0.storeAssertionRecords json -o - \ "$HOME/Library/DoNotDisturb/DB/Assertions.json"

Somewhere in the resulting data you should see:

com.apple.focus.work

That's the identifier we'll use to determine whether Work Focus is currently active.

Apple doesn't currently provide a lovely little Terminal command such as focus status, so we're reading the macOS Focus assertion database instead. This is therefore the one part of this setup that could potentially need adjusting in a future macOS release.

Step 3: Create the Time Announcer Script

First create somewhere to keep personal scripts:

mkdir -p "$HOME/Scripts"

Now create our script:

nano "$HOME/Scripts/work-time-announcer.sh"

Paste the following:

#!/bin/zsh # ============================================================ # Work Focus Time Announcer # # Announces the time while macOS Work Focus is active. # # - Skips 06:00 # - Respects mute # - Follows current Mac volume up to 40% # - Temporarily lowers louder volumes to 40% # - Restores original volume immediately afterwards # ============================================================ FOCUS_DB="$HOME/Library/DoNotDisturb/DB/Assertions.json" WORK_FOCUS_ID="com.apple.focus.work" MAX_ANNOUNCEMENT_VOLUME=40 # ------------------------------------------------------------ # Skip 06:00 # ------------------------------------------------------------ CURRENT_HOUR=$((10#$(date +%H))) if (( CURRENT_HOUR == 6 )); then exit 0 fi # ------------------------------------------------------------ # Check that the Focus database exists # ------------------------------------------------------------ if [[ ! -f "$FOCUS_DB" ]]; then exit 0 fi # ------------------------------------------------------------ # Read currently active Focus modes # ------------------------------------------------------------ ACTIVE_FOCUS=$( /usr/bin/plutil \ -extract data.0.storeAssertionRecords \ json \ -o - \ "$FOCUS_DB" 2>/dev/null ) # ------------------------------------------------------------ # Exit unless Work Focus is active # ------------------------------------------------------------ if ! echo "$ACTIVE_FOCUS" | /usr/bin/grep -q "$WORK_FOCUS_ID"; then exit 0 fi # ------------------------------------------------------------ # Respect system mute # ------------------------------------------------------------ IS_MUTED=$( /usr/bin/osascript \ -e 'output muted of (get volume settings)' ) if [[ "$IS_MUTED" == "true" ]]; then exit 0 fi # ------------------------------------------------------------ # Get current Mac output volume # ------------------------------------------------------------ ORIGINAL_VOLUME=$( /usr/bin/osascript \ -e 'output volume of (get volume settings)' ) # ------------------------------------------------------------ # Work out announcement volume # # Below 40% -> leave it alone # Above 40% -> temporarily use 40% # ------------------------------------------------------------ if (( ORIGINAL_VOLUME > MAX_ANNOUNCEMENT_VOLUME )); then ANNOUNCEMENT_VOLUME=$MAX_ANNOUNCEMENT_VOLUME else ANNOUNCEMENT_VOLUME=$ORIGINAL_VOLUME fi # ------------------------------------------------------------ # Function to restore the original volume # # trap means we also restore it if the script gets interrupted. # ------------------------------------------------------------ restore_volume() { /usr/bin/osascript \ -e "set volume output volume $ORIGINAL_VOLUME" \ >/dev/null 2>&1 } trap restore_volume EXIT INT TERM # ------------------------------------------------------------ # Temporarily set safe announcement volume # ------------------------------------------------------------ /usr/bin/osascript \ -e "set volume output volume $ANNOUNCEMENT_VOLUME" \ >/dev/null # ------------------------------------------------------------ # Create friendly spoken time # ------------------------------------------------------------ SPOKEN_TIME=$(date "+%-I %p") # ------------------------------------------------------------ # Speak! # ------------------------------------------------------------ /usr/bin/say "The time is $SPOKEN_TIME" # ------------------------------------------------------------ # Restore original Mac volume # ------------------------------------------------------------ restore_volume trap - EXIT INT TERM exit 0

Save it using:

Control + O

Press Enter to confirm the filename.

Then:

Control + X

Now make the script executable:

chmod +x "$HOME/Scripts/work-time-announcer.sh"

Step 4: Test It

Make sure Work Focus is ON.

Then run:

"$HOME/Scripts/work-time-announcer.sh"

Your Mac should announce the current time.

For example:

"The time is 9 A M."

Turn Work Focus off and run the exact same command again.

This time:

Nothing should happen.

Perfect.

The Clever Little Volume Safety Net

One thing I particularly wanted was for the announcement to respect my existing Mac volume without ever becoming ridiculously loud.

The script therefore behaves like this:

Mac VolumeAnnouncement Volume
10%10%
20%20%
30%30%
40%40%
50%40%
75%40%
100%40%

So if my Mac is quiet, the announcement stays quiet.

But if I've accidentally left the Mac at full blast, the speaking clock is still restricted to 40%.

Importantly, the script doesn't turn the Mac's volume down.

It only sets the volume of that individual spoken announcement.

Afterwards my normal system volume remains exactly where it was.

If the Mac is muted, the script doesn't speak at all.

Step 5: Make It Completely Automatic

Running the script manually isn't much use.

We want macOS to automatically check it at the beginning of every hour.

For this we'll use launchd, Apple's built-in system for running scheduled processes.

Create a LaunchAgent:

nano "$HOME/Library/LaunchAgents/com.joegilmore.worktime.plist"

Paste:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.joegilmore.worktime</string> <key>ProgramArguments</key> <array> <string>/bin/zsh</string> <string>/Users/YOUR_USERNAME/Scripts/work-time-announcer.sh</string> </array> <key>StartCalendarInterval</key> <dict> <key>Minute</key> <integer>0</integer> </dict> <key>StandardOutPath</key> <string>/tmp/work-time-announcer.log</string> <key>StandardErrorPath</key> <string>/tmp/work-time-announcer-error.log</string> </dict> </plist>

Before saving it, we need to replace:

YOUR_USERNAME

with our actual macOS username.

To find it, open another Terminal window and run:

whoami

If it returns:

joe

then this:

/Users/YOUR_USERNAME/Scripts/work-time-announcer.sh

becomes:

/Users/joe/Scripts/work-time-announcer.sh

Save the file with:

Control + O → Enter → Control + X

Step 6: Activate the LaunchAgent

Finally run:

launchctl bootstrap \ "gui/$(id -u)" \ "$HOME/Library/LaunchAgents/com.joegilmore.worktime.plist"

That's it.

There is now no application to open and no Terminal window that needs to stay running.

macOS will wake our tiny script at the beginning of every hour.

The script checks:

  1. Is it 6:00 AM? If yes, stay quiet.
  2. Is Work Focus active? If not, exit.
  3. Is the Mac muted? If yes, exit.
  4. What's the current system volume?
  5. Cap the speech volume at 40%.
  6. Announce the time.
  7. Exit again.

The whole thing only exists for a moment each hour.

What My Day Now Looks Like

My Work Focus switches on at:

6:00 AM

Nothing is announced because I've deliberately excluded that hour.

Then:

7:00 AM

"The time is 7 A M."

8:00 AM

"The time is 8 A M."

9:00 AM

"The time is 9 A M."

And so on.

My Work Focus eventually switches off and the announcements simply stop.

There's no separate timetable to maintain.

If I later change my Work Focus schedule from:

6:00 AM → 3:00 PM

to:

8:00 AM → 1:00 PM

I don't need to modify a single line of code.

The announcements automatically follow it.

Manually Turning Work Focus On and Off Works Too

This is probably my favourite part.

Suppose I finish early and turn Work Focus off at 11:30 AM.

At midday:

🔇 Silence.

Or perhaps I decide to do a couple of hours of coding in the evening and manually enable Work Focus again.

The next hourly check sees that Work Focus is active and the speaking clock comes back to life.

So Work Focus effectively becomes the switch for my entire working environment.

Changing the Maximum Volume

I've chosen 40% as my maximum announcement volume.

That's controlled by this line:

MAX_SPEECH_VOLUME="0.40"

For a quieter maximum of 25%:

MAX_SPEECH_VOLUME="0.25"

For 30%:

MAX_SPEECH_VOLUME="0.30"

Or 50%:

MAX_SPEECH_VOLUME="0.50"

Personally, 40% feels like a nice ceiling.

Loud enough to notice without having macOS suddenly barking the time at me because I forgot I'd previously turned the speakers up. 😄

Changing Which Hour Gets Skipped

I've deliberately excluded 6:00 AM because that's when my Work Focus begins.

This section controls it:

if (( CURRENT_HOUR == 6 )); then exit 0 fi

If I ever wanted to include 6:00 AM again, I could simply remove that section.

Or change 6 to another hour.

Disabling the Whole Thing

If I ever want to remove the automatic scheduler, I can run:

launchctl bootout \ "gui/$(id -u)" \ "$HOME/Library/LaunchAgents/com.joegilmore.worktime.plist"

The script itself can remain safely in:

~/Scripts/work-time-announcer.sh

and be re-enabled later.

Why I Like This Setup

It's an incredibly small automation, but it's exactly the sort of thing I love adding to my Mac.

There's no subscription.

No menu-bar application.

No permanent process sitting around doing unnecessary work.

No additional timetable to maintain.

macOS already knows when I'm working because Work Focus knows when I'm working.

So we're simply borrowing that information and making the computer a tiny bit more useful.

And every hour, somewhere amongst the code editors, browser tabs and whatever rabbit hole I've disappeared into, my Mac politely interrupts:

"The time is 11 A M."

Sometimes that's all the reminder I need to realise I've spent quite enough time solving the wrong problem. 🙂