Skip to content

Instantly share code, notes, and snippets.

@drewkerr
Last active October 10, 2025 07:11
Show Gist options
  • Save drewkerr/0f2b61ce34e2b9e3ce0ec6a92ab05c18 to your computer and use it in GitHub Desktop.
Save drewkerr/0f2b61ce34e2b9e3ce0ec6a92ab05c18 to your computer and use it in GitHub Desktop.
Read the current Focus mode on macOS Monterey (12.0+) using JavaScript for Automation (JXA)
const app = Application.currentApplication()
app.includeStandardAdditions = true
function getJSON(path) {
const fullPath = path.replace(/^~/, app.pathTo('home folder'))
const contents = app.read(fullPath)
return JSON.parse(contents)
}
function run() {
let focus = "No focus" // default
const assert = getJSON("~/Library/DoNotDisturb/DB/Assertions.json").data[0].storeAssertionRecords
const config = getJSON("~/Library/DoNotDisturb/DB/ModeConfigurations.json").data[0].modeConfigurations
if (assert) { // focus set manually
const modeid = assert[0].assertionDetails.assertionDetailsModeIdentifier
focus = config[modeid].mode.name
} else { // focus set by trigger
const date = new Date
const now = date.getHours() * 60 + date.getMinutes()
for (const modeid in config) {
const triggers = config[modeid].triggers.triggers[0]
if (triggers && triggers.enabledSetting == 2) {
const start = triggers.timePeriodStartTimeHour * 60 + triggers.timePeriodStartTimeMinute
const end = triggers.timePeriodEndTimeHour * 60 + triggers.timePeriodEndTimeMinute
if (start < end) {
if (now >= start && now < end) {
focus = config[modeid].mode.name
}
} else if (start > end) { // includes midnight
if (now >= start || now < end) {
focus = config[modeid].mode.name
}
}
}
}
}
return focus
}
@devnoname120
Copy link

devnoname120 commented Jul 5, 2023

I use the following Alfred Workflow in order to enable/disable DND: https://github.com/vitorgalvao/calm-notifications-workflow/tree/main/Workflow

It uses its own Shortcut under the hood that needs to be installed first.

I post it here as an example of a working implementation that people can look into.

@roman-ld
Copy link

Python if you want in that context.

#!/usr/bin/env python3

import json
import os
import datetime

ASSERT_PATH = os.path.expanduser("~/Library/DoNotDisturb/DB/Assertions.json")
MODECONFIG_PATH = os.path.expanduser("~/Library/DoNotDisturb/DB/ModeConfigurations.json")
def get_focus():
    focus = "No focus" #default
    assertJ = json.load(open(ASSERT_PATH))['data'][0]['storeAssertionRecords']
    configJ = json.load(open(MODECONFIG_PATH))['data'][0]['modeConfigurations']
    if assertJ:
        modeid = assertJ[0]['assertionDetails']['assertionDetailsModeIdentifier']
        focus = configJ[modeid]['mode']['name']
    else:
        date = datetime.datetime.today()
        now = date.hour * 60 + date.minute

        for modeid in configJ:
            triggers = configJ[modeid]['triggers']['triggers'][0]
            if triggers and triggers['enabledSetting'] == 2:
                start = triggers['timePeriodStartTimeHour'] * 60 + triggers['timePeriodStartTimeMinute']
                end = triggers['timePeriodEndTimeHour'] * 60 + triggers['timePeriodEndTimeMinute']
                if start < end:
                    if now >= start and now < end:
                        focus = configJ[modeid]['mode']['name']
                elif start > end: # includes midnight
                    if now >= start or now < end:
                        focus = configJ[modeid]['mode']['name']
    return focus

if '__main__' == __name__:
    print(get_focus())

@tooh
Copy link

tooh commented Sep 17, 2025

@roman-ld Had this script running on Sequioa. Upgraded last weekend to Tahoe. Script is now broken, info is not anymore in Assertions. Would appreciate a fix.

@roman-ld
Copy link

roman-ld commented Sep 17, 2025

@tooh welcome to macos updates!
Still working for me after upgrading to Tahoe.

I think I've found the problem, have you agreed to the "Xcode and Apple SDKs license" after upgrading?

I looked at what my python3 I was using (which python3).
Found I'm on homebrew for python3 (meaning I didn't have to agree for the python to work).
Used find to find other python3 on my path (find "${(s/:/)PATH}" -name python3).

Aside: Apple's zsh now has several entries that are not readable / search able from a non-admin account (don't worry about these if you run into this as a non-admin user).

find: /usr/sbin/authserver: Permission denied
find: /var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin: No such file or directory
find: /var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin: No such file or directory
find: /var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin: No such file or directory
find: /Library/Apple/usr/bin: No such file or directory

When I switched the #! to #!/usr/bin/python3 (macos stock python3) I got:

You have not agreed to the Xcode and Apple SDKs license.
...

Bottom of that says:

Agreeing to the Xcode and Apple SDKs license requires admin privileges, please accept the Xcode license as the root user (e.g. 'sudo xcodebuild -license').

After agreeing (running the -license, typing accept) the script worked with macos stock python3

I hope this helps.

@luckman212
Copy link

luckman212 commented Sep 24, 2025

I just needed something dead-simple: focus mode on or off. None of the above were working without errors for me on Tahoe, so I adapted the Python example from @roman-ld above (thanks) and stripped out some parts that I don't need.

I saved this as /usr/local/bin/dnd and chmod +x'd it. “Works for me” but YMMV.

With this you can do things like if dnd -q ; then ... ; fi in shell scripts.

#!/usr/bin/env python3

"""
Check DND status - tested only on macOS 26.0 Tahoe
exits with 0 if a focus mode is enabled, 1 otherwise
prints mode unless -q/--quiet is passed as arg

based on https://gist.github.com/drewkerr/0f2b61ce34e2b9e3ce0ec6a92ab05c18

"""

import json
import os
import sys

ASSERTIONS_PATH = os.path.expanduser("~/Library/DoNotDisturb/DB/Assertions.json")
CONFIG_PATH = os.path.expanduser("~/Library/DoNotDisturb/DB/ModeConfigurations.json")
QUIET = False

def msg(m: str, q: bool) -> None:
	if not q:
		print(m)

def get_focus(q: bool) -> int:
	try:
		ASSERTION_RECS = json.load(open(ASSERTIONS_PATH))['data'][0]['storeAssertionRecords']
		FOCUS_CONFIGS = json.load(open(CONFIG_PATH))['data'][0]['modeConfigurations']
		MODE = ASSERTION_RECS[0]['assertionDetails']['assertionDetailsModeIdentifier']
		msg(FOCUS_CONFIGS[MODE]['mode']['name'], q)
		return 0
	except:
		msg("No focus", q)
		return 1

if '__main__' == __name__:
	if len(sys.argv) > 1:
		if sys.argv[1] in ['-q', '--quiet']:
			QUIET = True
	sys.exit(get_focus(QUIET))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment