Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Lego2012/8442cbb783ca98b5ab6b379bdde400ba to your computer and use it in GitHub Desktop.
Save Lego2012/8442cbb783ca98b5ab6b379bdde400ba to your computer and use it in GitHub Desktop.
Toggle Do Not Disturb Ventura AppleScript
#!/usr/bin/env osascript -l JavaScript
/**
* -----------------------------------------------------------------------------
* Toggle Do Not Disturb Mode from Control Center
* -----------------------------------------------------------------------------
*
* Created on May 18, 2023 by Stephan Casas
*
* Options:
* - TARGET_DND_MODE
* The name of the Do Not Disturb mode to toggle.
*
*
* Notes:
* This script was tested on macOS 13 Ventura and may break with future OS
* updates.
*/
const TARGET_DND_MODE = 'Do Not Disturb';
const $attr = Ref();
const $windows = Ref();
const $children = Ref();
function run(_) {
// Get the current Control Center PID.
const pid = $.NSRunningApplication.runningApplicationsWithBundleIdentifier(
'com.apple.controlcenter',
).firstObject.processIdentifier;
// Get the Control Center application.
const app = $.AXUIElementCreateApplication(pid);
// Get the Control Center menubar extra children.
$.AXUIElementCopyAttributeValue(app, 'AXChildren', $children);
$.AXUIElementCopyAttributeValue($children[0].js[0], 'AXChildren', $children);
// Locate the Control Center menubar extra (also has Clock, Users, etc.).
const ccExtra = $children[0].js.find((child) => {
$.AXUIElementCopyAttributeValue(child, 'AXIdentifier', $attr);
return $attr[0].js == 'com.apple.menuextra.controlcenter';
});
// Open Control Center window and await draw.
$.AXUIElementPerformAction(ccExtra, 'AXPress');
if (
!(() => {
const timeout = new Date().getTime() + 2000;
while (true) {
$.AXUIElementCopyAttributeValue(app, 'AXWindows', $windows);
if (
typeof $windows[0] == 'function' &&
($windows[0].js.length ?? 0) > 0
) {
return true;
}
if (new Date().getTime() > timeout) {
return false;
}
delay(0.1);
}
})()
) {
return;
}
// Get Control Center window children.
$.AXUIElementCopyAttributeValue($windows[0].js[0], 'AXChildren', $children);
// Locate the Control Center modules group.
const modulesGroup = $children[0].js.find((child) => {
$.AXUIElementCopyAttributeValue(child, 'AXRole', $attr);
return $attr[0].js == 'AXGroup';
});
// Get the individual modules within the modules group.
$.AXUIElementCopyAttributeValue(modulesGroup, 'AXChildren', $children);
// Locate the focus modes module.
const focusModes = $children[0].js.find((child) => {
$.AXUIElementCopyAttributeValue(child, 'AXIdentifier', $attr);
return $attr[0].js == 'controlcenter-focus-modes';
});
// Activate the Do Not Disturb module and await draw.
$.AXUIElementPerformAction(
focusModes,
// Wtf is this action name, Apple??
'Name:show details\nTarget:0x0\nSelector:(null)',
);
if (
!(() => {
const timeout = new Date().getTime() + 2000;
while (true) {
$.AXUIElementCopyAttributeValue(modulesGroup, 'AXChildren', $children);
if (
typeof $children[0] == 'function' &&
($children[0].js.length ?? 0) > 0
) {
return true;
}
if (new Date().getTime() > timeout) {
return false;
}
delay(0.1);
}
})()
) {
return;
}
// Get the scroll area containing the DND options.
const dndOptions = $children[0].js.find((child) => {
$.AXUIElementCopyAttributeValue(child, 'AXRole', $attr);
return $attr[0].js == 'AXScrollArea';
});
// Get all DND options.
$.AXUIElementCopyAttributeValue(dndOptions, 'AXChildren', $children);
// Locate the toggle element for the target DND mode.
const toggle = $children[0].js
.filter((child) => {
$.AXUIElementCopyAttributeValue(child, 'AXRole', $attr);
return $attr[0].js == `AXCheckBox`;
})
.find((child) => {
$.AXUIElementCopyAttributeValue(child, 'AXAttributedDescription', $attr);
return `${$attr[0].string.js}`.toLowerCase() == TARGET_DND_MODE.toLowerCase();
});
if (!toggle) {
console.log('Error: Could not get toggle for target Do Not Disturb mode.');
return 1;
}
// Press the toggle for the target DND mode.
$.AXUIElementPerformAction(toggle, 'AXPress');
// Send ⎋ to dismiss Control Center.
$.CGEventPost($.kCGHIDEventTap, $.CGEventCreateKeyboardEvent(null, 53, true));
return 0;
}
// prettier-ignore
(() => {
ObjC.import('Cocoa'); // yes, it's necessary -- stop telling me it isn't
ObjC.bindFunction('AXUIElementPerformAction', ['int', ['id', 'id']]);
ObjC.bindFunction('AXUIElementCreateApplication', ['id', ['unsigned int']]);
ObjC.bindFunction('AXUIElementCopyAttributeValue',['int', ['id', 'id', 'id*']]);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment