Description of what you are sharing
Problem
I want to be able to set my current Focus Mode dynamically based on user input. e.g.
shortcuts run "Set_Focus_Mode" <<< "Work"
shortcuts run "Set_Focus_Mode" <<< "Reduce Interruptions"
shortcuts run "Set_Focus_Mode" <<< "No Focus"
Or, via BTT:
Unfortunately, it's not possible to set the name of a Focus Mode dynamically from a variable (see screenshot below).
Impact
This requires manually building an Apple Shortcut with many deeply nested If
statements, one for each Focus Mode you have uniquely configured in System Settings β like so:
If {Shortcut Input} is {Name of Focus Mode N^1}
Turn {Name of the Focus Mode N^1} On until Turn Off
Otherwise
If {Shortcut Input} is {Name of a Focus Mode N^2}
Turn {Name of the Focus Mode N^2} On until Turn Off
Otherwise
If {Shortcut Input} is {Name of a Focus Mode N^3}
Turn {Name of the Focus Mode N^3} On until Turn Off
Otherwise
... repeat for each of your Focus Modes,
i.e. until N^a where a == number of
Focus Modes you've configured.
This is tedious to build. Furthermore, once built, it cannot simply be shared with others since everyone has their own unique set of Focus Modes.
Solution
Programmatically build the Set_Focus_Mode
Shortcut based on the your unique set of Focus Modes. The script will pull your unique set of Focus Modes from ~/Library/DoNotDisturb/DB/ModeConfigurations.json
, e.g.:
cat ~/Library/DoNotDisturb/DB/ModeConfigurations.json | \
jq '
.data[0].modeConfigurations
| to_entries
| map({
key: .value.mode.name,
value: .key
})
| from_entries
'
{
"Gaming": "com.apple.focus.gaming",
"Sleep": "com.apple.sleep.sleep-mode",
"Reading": "com.apple.focus.reading",
"Fitness": "com.apple.donotdisturb.mode.workout",
"Driving": "com.apple.donotdisturb.mode.driving",
"Do Not Disturb": "com.apple.donotdisturb.mode.default",
"Mindfulness": "com.apple.focus.mindfulness",
"Personal": "com.apple.focus.personal-time",
"Reduce Interruptions": "com.apple.focus.reduce-interruptions",
"Work": "com.apple.focus.work"
}
Here's a shell script that will automatically create the Shortcut, sign it, and open it in the Shortcuts app.
#!/usr/bin/env bash
set -euo pipefail
# βββ Logging Helpers ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
info() { printf '\033[1;32m[INFO]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[WARN]\033[0m %s\n' "$*"; }
error() { printf '\033[1;31m[ERROR]\033[0m %s\n' "$*"; }
debug() { printf '\033[1;34m[DEBUG]\033[0m %s\n' "$*"; }
# βββ 1. Paths & filenames βββββββββββββββββββββββββββββββββββββββββββββββββββββ
CONFIG="${HOME}/Library/DoNotDisturb/DB/ModeConfigurations.json"
JSON_OUT="focus_mode.json"
TMP="focus_mode.tmp.json"
SHORTCUT="focus_mode.shortcut"
SIGNED="focus_mode-signed.shortcut"
info "Using config: $CONFIG"
info "Will build JSON at: $JSON_OUT"
# βββ 2. Start a minimal JSON skeleton βββββββββββββββββββββββββββββββββββββββββ
info "Initializing JSON skeleton"
cat > "$JSON_OUT" <<'EOF'
{
"WFQuickActionSurfaces": [],
"WFWorkflowActions": [],
"WFWorkflowImportQuestions": [],
"WFWorkflowTypes": [],
"WFWorkflowHasShortcutInputVariables": true,
"WFWorkflowMinimumClientVersionString": "1113",
"WFWorkflowMinimumClientVersion": 1113,
"WFWorkflowClientVersion": "3514.0.4.200",
"WFWorkflowHasOutputFallback": true,
"WFWorkflowIcon": {
"WFWorkflowIconGlyphNumber": 59782,
"WFWorkflowIconStartColor": 4274264319
},
"WFWorkflowInputContentItemClasses": ["WFAppContentItem","WFStringContentItem"],
"WFWorkflowOutputContentItemClasses": ["WFStringContentItem"],
"WFWorkflowNoInputBehavior": {
"Name": "WFWorkflowNoInputBehaviorAskForInput",
"Parameters": { "ItemClass": "WFStringContentItem" }
}
}
EOF
# βββ 3. Read Focus Modes into bash arrays ββββββββββββββββββββββββββββββββββββββ
info "Reading focus modes from JSON"
declare -a NAMES IDS
while IFS=$'\t' read -r name id; do
NAMES+=( "$name" )
IDS+=( "$id" )
debug "Found mode: '$name' -> $id"
done < <(
jq -r '
.data[0].modeConfigurations
| to_entries[]
| "\(.value.mode.name)\t\(.key)"
' "$CONFIG"
)
info "Total modes: ${#NAMES[@]}"
# βββ 4. Generate all the UUIDs we need ββββββββββββββββββββββββββββββββββββββββ
info "Generating UUIDs"
trim_uuid=$(uuidgen | tr '[:lower:]' '[:upper:]')
debug "trim_uuid=$trim_uuid"
result_uuid=$(uuidgen | tr '[:lower:]' '[:upper:]')
debug "result_uuid=$result_uuid"
declare -a GIDS
for idx in "${!NAMES[@]}"; do
gid=$(uuidgen | tr '[:lower:]' '[:upper:]')
GIDS+=( "$gid" )
debug "group $idx: ${NAMES[idx]} -> gid=$gid"
done
# Find which index corresponds to the built-in βDo Not Disturbβ (default-off) mode
DEFAULT_IDX=0
for i in "${!IDS[@]}"; do
if [[ "${IDS[i]}" == "com.apple.donotdisturb.mode.default" ]]; then
DEFAULT_IDX=$i
debug "Defaultβoff mode at index $i (${NAMES[i]})"
fi
done
no_focus_gid=${GIDS[$DEFAULT_IDX]}
info "No-Focus group ID: $no_focus_gid"
# βββ 5. Append the βTrim Whitespaceβ action βββββββββββββββββββββββββββββββββ
info "Appending Trim Whitespace action"
jq --arg tu "$trim_uuid" '
.WFWorkflowActions += [
{
"WFWorkflowActionIdentifier":"is.workflow.actions.text.trimwhitespace",
"WFWorkflowActionParameters":{
"UUID":$tu,
"WFInput":{
"WFSerializationType":"WFTextTokenString",
"Value":{
"string":"\uFFFC",
"attachmentsByRange":{ "{0, 1}":{ "Type":"ExtensionInput" } }
}
}
}
}
]
' "$JSON_OUT" > "$TMP" && mv "$TMP" "$JSON_OUT"
# βββ 6. Append the βNo Focusβ branch ββββββββββββββββββββββββββββββββββββββββ
info "Appending No Focus branch"
jq --arg tu "$trim_uuid" --arg gid "$no_focus_gid" '
.WFWorkflowActions += [
{
"WFWorkflowActionIdentifier":"is.workflow.actions.conditional",
"WFWorkflowActionParameters":{
"GroupingIdentifier":$gid,
"WFCondition":4,
"WFConditionalActionString":"No Focus",
"WFControlFlowMode":0,
"WFInput":{
"Type":"Variable",
"Variable":{
"WFSerializationType":"WFTextTokenAttachment",
"Value":{
"Type":"ActionOutput",
"OutputName":"Updated Text",
"OutputUUID":$tu
}
}
}
}
},
{
"WFWorkflowActionIdentifier":"is.workflow.actions.dnd.set",
"WFWorkflowActionParameters":{}
},
{
"WFWorkflowActionIdentifier":"is.workflow.actions.conditional",
"WFWorkflowActionParameters":{
"GroupingIdentifier":$gid,
"WFControlFlowMode":1
}
}
]
' "$JSON_OUT" > "$TMP" && mv "$TMP" "$JSON_OUT"
# βββ 7. Dynamically append one IFβSETβEND block per mode ββββββββββββββββββββ
info "Appending per-mode branches"
for i in "${!NAMES[@]}"; do
name=${NAMES[i]}
id=${IDS[i]}
gid=${GIDS[i]}
info " β’ $name (id=$id, gid=$gid)"
jq \
--arg name "$name" \
--arg id "$id" \
--arg gid "$gid" \
--arg tu "$trim_uuid" \
'
.WFWorkflowActions += [
{
"WFWorkflowActionIdentifier":"is.workflow.actions.conditional",
"WFWorkflowActionParameters":{
"GroupingIdentifier":$gid,
"WFCondition":4,
"WFConditionalActionString":$name,
"WFControlFlowMode":0,
"WFInput":{
"Type":"Variable",
"Variable":{
"WFSerializationType":"WFTextTokenAttachment",
"Value":{
"Type":"ActionOutput",
"OutputName":"Updated Text",
"OutputUUID":$tu
}
}
}
}
},
{
"WFWorkflowActionIdentifier":"is.workflow.actions.dnd.set",
"WFWorkflowActionParameters":
( if $id == "com.apple.donotdisturb.mode.default"
then { "Enabled":1 }
else {
"Enabled":1,
"FocusModes":{
"Identifier":$id,
"DisplayString":$name
}
}
end
)
},
{
"WFWorkflowActionIdentifier":"is.workflow.actions.conditional",
"WFWorkflowActionParameters":{
"GroupingIdentifier":$gid,
"WFControlFlowMode":1
}
}
]
' "$JSON_OUT" > "$TMP" && mv "$TMP" "$JSON_OUT"
done
# βββ 8. Fallback alert βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
info "Appending fallback alert"
jq --arg tu "$trim_uuid" '
.WFWorkflowActions += [
{
"WFWorkflowActionIdentifier":"is.workflow.actions.alert",
"WFWorkflowActionParameters":{
"WFAlertActionTitle":"Do you want to continue?",
"WFAlertActionMessage":{
"WFSerializationType":"WFTextTokenString",
"Value":{
"string":"\uFFFC",
"attachmentsByRange":{ "{0, 1}":{
"Type":"ActionOutput",
"OutputName":"Updated Text",
"OutputUUID":$tu
}}
}
}
}
}
]
' "$JSON_OUT" > "$TMP" && mv "$TMP" "$JSON_OUT"
# βββ 9. Close all IF blocks (in reverse order), tagging default branch βββββββ
info "Closing all conditionals"
for (( idx=${#GIDS[@]}-1; idx>=0; idx-- )); do
gid=${GIDS[idx]}
debug "Closing group $gid"
jq \
--arg gid "$gid" \
--arg ru "$result_uuid" \
--arg dg "$no_focus_gid" \
'
.WFWorkflowActions += [
{
"WFWorkflowActionIdentifier":"is.workflow.actions.conditional",
"WFWorkflowActionParameters":
( if $gid == $dg
then { "GroupingIdentifier":$gid, "WFControlFlowMode":2, "UUID":$ru }
else { "GroupingIdentifier":$gid, "WFControlFlowMode":2 }
end
)
}
]
' "$JSON_OUT" > "$TMP" && mv "$TMP" "$JSON_OUT"
done
# βββ 10. Final output action ββββββββββββββββββββββββββββββββββββββββββββββββ
info "Appending final output action"
jq --arg ru "$result_uuid" '
.WFWorkflowActions += [
{
"WFWorkflowActionIdentifier":"is.workflow.actions.output",
"WFWorkflowActionParameters":{
"WFNoOutputSurfaceBehavior":"Respond",
"WFOutput":{
"WFSerializationType":"WFTextTokenString",
"Value":{
"string":"\uFFFC",
"attachmentsByRange":{ "{0, 1}":{
"Type":"ActionOutput",
"OutputName":"If Result",
"OutputUUID":$ru
}}
}
},
"WFResponse":{
"WFSerializationType":"WFTextTokenString",
"Value":{
"string":"\uFFFC",
"attachmentsByRange":{ "{0, 1}":{
"Type":"ActionOutput",
"OutputName":"If Result",
"OutputUUID":$ru
}}
}
}
}
}
]
' "$JSON_OUT" > "$TMP" && mv "$TMP" "$JSON_OUT"
# βββ 11. Convert JSON β binary .shortcut, then sign & open βββββββββββββββββ
info "Converting JSON β binary .shortcut"
if plutil -convert binary1 -o "$SHORTCUT" "$JSON_OUT"; then
info " β wrote $SHORTCUT"
else
error "plutil conversion failed"
exit 1
fi
info "Signing shortcut"
if shortcuts sign --mode anyone --input "$SHORTCUT" --output "$SIGNED"; then
info " β signed to $SIGNED"
else
warn "Signing failed; please ensure Shortcuts CLI is installed"
fi
info "Opening signed shortcut"
if open "$SIGNED"; then
info " β opened $SIGNED"
else
warn "Failed to open $SIGNED"
fi
info "π Done! Generated, signed, and opened: $SIGNED"
Setup Instructions
-
Save the shell script to a file, e.g.
focus_mode.sh
-
Make the shell script executable by running the following in your preferred terminal app:
chmod +x focus_mode.sh
Note: set full path of
focus_mode.sh
if necessary -
Run the shell script:
./focus_mode.sh
Here's the log output when I ran the script:
[INFO] Using config: ~/Library/DoNotDisturb/DB/ModeConfigurations.json [INFO] Will build JSON at: focus_mode.json [INFO] Initializing JSON skeleton [INFO] Reading focus modes from JSON [DEBUG] Found mode: 'Gaming' -> com.apple.focus.gaming [DEBUG] Found mode: 'Sleep' -> com.apple.sleep.sleep-mode [DEBUG] Found mode: 'Reading' -> com.apple.focus.reading [DEBUG] Found mode: 'Fitness' -> com.apple.donotdisturb.mode.workout [DEBUG] Found mode: 'Driving' -> com.apple.donotdisturb.mode.driving [DEBUG] Found mode: 'Do Not Disturb' -> com.apple.donotdisturb.mode.default [DEBUG] Found mode: 'Mindfulness' -> com.apple.focus.mindfulness [DEBUG] Found mode: 'Personal' -> com.apple.focus.personal-time [DEBUG] Found mode: 'Reduce Interruptions' -> com.apple.focus.reduce-interruptions [DEBUG] Found mode: 'Work' -> com.apple.focus.work [INFO] Total modes: 10 [INFO] Generating UUIDs [DEBUG] trim_uuid=46C3EA76-2668-4034-BC79-9FA45D7980DA [DEBUG] result_uuid=39163F06-556B-4676-8FBD-78AA37E1849D [DEBUG] group 0: Gaming -> gid=612344E1-16E4-4F0B-A10D-3A71948B92E1 [DEBUG] group 1: Sleep -> gid=A03833F9-1F60-405A-8F60-AEAA033BC4E9 [DEBUG] group 2: Reading -> gid=601CBDF1-B667-4960-9A9C-29AFD9B5A9D7 [DEBUG] group 3: Fitness -> gid=53C04063-ECBB-40F7-ABCE-AE436C1717DC [DEBUG] group 4: Driving -> gid=1D40BB7F-82B9-43E4-A428-7BBD3EFF2123 [DEBUG] group 5: Do Not Disturb -> gid=E8A0EE75-A270-4FC6-89DA-64BE67DE962D [DEBUG] group 6: Mindfulness -> gid=29001490-CA3B-4963-9ABB-363BAB04DC82 [DEBUG] group 7: Personal -> gid=FA959250-D897-4B22-9C70-BC5D429AC29D [DEBUG] group 8: Reduce Interruptions -> gid=B670DCF2-7321-4FB4-A0E6-8E51B0713061 [DEBUG] group 9: Work -> gid=C229B6CC-913C-4474-9CA5-04A3625CE6BC [DEBUG] Defaultβoff mode at index 5 (Do Not Disturb) [INFO] No-Focus group ID: E8A0EE75-A270-4FC6-89DA-64BE67DE962D [INFO] Appending Trim Whitespace action [INFO] Appending No Focus branch [INFO] Appending per-mode branches [INFO] β’ Gaming (id=com.apple.focus.gaming, gid=612344E1-16E4-4F0B-A10D-3A71948B92E1) [INFO] β’ Sleep (id=com.apple.sleep.sleep-mode, gid=A03833F9-1F60-405A-8F60-AEAA033BC4E9) [INFO] β’ Reading (id=com.apple.focus.reading, gid=601CBDF1-B667-4960-9A9C-29AFD9B5A9D7) [INFO] β’ Fitness (id=com.apple.donotdisturb.mode.workout, gid=53C04063-ECBB-40F7-ABCE-AE436C1717DC) [INFO] β’ Driving (id=com.apple.donotdisturb.mode.driving, gid=1D40BB7F-82B9-43E4-A428-7BBD3EFF2123) [INFO] β’ Do Not Disturb (id=com.apple.donotdisturb.mode.default, gid=E8A0EE75-A270-4FC6-89DA-64BE67DE962D) [INFO] β’ Mindfulness (id=com.apple.focus.mindfulness, gid=29001490-CA3B-4963-9ABB-363BAB04DC82) [INFO] β’ Personal (id=com.apple.focus.personal-time, gid=FA959250-D897-4B22-9C70-BC5D429AC29D) [INFO] β’ Reduce Interruptions (id=com.apple.focus.reduce-interruptions, gid=B670DCF2-7321-4FB4-A0E6-8E51B0713061) [INFO] β’ Work (id=com.apple.focus.work, gid=C229B6CC-913C-4474-9CA5-04A3625CE6BC) [INFO] Appending fallback alert [INFO] Closing all conditionals [DEBUG] Closing group C229B6CC-913C-4474-9CA5-04A3625CE6BC [DEBUG] Closing group B670DCF2-7321-4FB4-A0E6-8E51B0713061 [DEBUG] Closing group FA959250-D897-4B22-9C70-BC5D429AC29D [DEBUG] Closing group 29001490-CA3B-4963-9ABB-363BAB04DC82 [DEBUG] Closing group E8A0EE75-A270-4FC6-89DA-64BE67DE962D [DEBUG] Closing group 1D40BB7F-82B9-43E4-A428-7BBD3EFF2123 [DEBUG] Closing group 53C04063-ECBB-40F7-ABCE-AE436C1717DC [DEBUG] Closing group 601CBDF1-B667-4960-9A9C-29AFD9B5A9D7 [DEBUG] Closing group A03833F9-1F60-405A-8F60-AEAA033BC4E9 [DEBUG] Closing group 612344E1-16E4-4F0B-A10D-3A71948B92E1 [INFO] Appending final output action [INFO] Converting JSON β binary .shortcut [INFO] β wrote focus_mode.shortcut [INFO] Signing shortcut ERROR: Unrecognized attribute string flag '?' in attribute string "T@"NSString",?,R,C" for property debugDescription ERROR: Unrecognized attribute string flag '?' in attribute string "T@"NSString",?,R,C" for property debugDescription ERROR: Unrecognized attribute string flag '?' in attribute string "T@"NSString",?,R,C" for property debugDescription ERROR: Unrecognized attribute string flag '?' in attribute string "T@"NSString",?,R,C" for property debugDescription ERROR: Unrecognized attribute string flag '?' in attribute string "T@"NSString",?,R,C" for property debugDescription [INFO] β signed to focus_mode-signed.shortcut [INFO] Opening signed shortcut [INFO] β opened focus_mode-signed.shortcut [INFO] π Done! Generated, signed, and opened: focus_mode-signed.shortcut
-
Enjoy your personalized Shortcut to dynamically set your Focus Modes