-
-
Save drewkerr/0f2b61ce34e2b9e3ce0ec6a92ab05c18 to your computer and use it in GitHub Desktop.
| 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 | |
| } |
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())
@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.
@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.
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))Reading the JSON files in the user library throws permissions problems for me when attempting to run the script outside of a terminal (e.g. on a schedule via Lingon.app). That goes for the both the JavaScript for Automation as well as the Python variants that were posted. Any suggestions on how to overcome this other than running as root?
With @roman-ld's Python script, I also ran into errors thrown because of keys that were expected by the script but missing in the JSON files on my system. I'm barely familiar with python but was able to come up with workarounds in case it's useful for someone:
The storeAssertionRecords key in the line assertJ = json.load(open(ASSERT_PATH))['data'][0]['storeAssertionRecords'] was missing, so I replaced that line with:
assertJ = json.load(open(ASSERT_PATH))['data'][0].get('storeAssertionRecords')and the index 0 in the line triggers = configJ[modeid]['triggers']['triggers'][0] was missing, so I replaced that line with these:
triggers_arr = configJ[modeid]['triggers']['triggers']
triggers = triggers_arr[0] if len(triggers_arr) > 0 else NoneEDIT: The solution to the permissions problem mentioned above was to drag the script onto MacOS's Settings > Privacy & Security > Full Disk Access pane. (Manually adding with the + button sometimes works too depending on the script extension). At least on my system, the script will not show up in the GUI of the preferences pane (thanks, Apple), but will be registered as having full disk access. Good luck removing its permission if you ever want to, without being able to select it in the GUI, however. I had to use Tinkertool System to remove full disk access perms for everything.
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
Shortcutunder the hood that needs to be installed first.I post it here as an example of a working implementation that people can look into.