vitorgalvao / alfred-workflows

Collection of Alfred workflows
BSD 3-Clause "New" or "Revised" License
2.44k stars 167 forks source link

method for checking DND status in Big Sur #122

Closed paulrudy closed 3 years ago

paulrudy commented 3 years ago

Edit: this is in reference to your Calm Notifications workflow

In case it's useful to you:

With help from someone on reddit, I was able to cobble together code that tests for Do Not Disturb status on Big Sur. The plist file ~/Library/Preferences/com.apple.ncprefs contains a plist item dnd_prefs. That item contains a base64-encoded binary plist (bplist). When that is decoded, if DND is enabled, it would look something like this:

<?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>dndDisplayLock</key>
    <false/>
    <key>dndDisplaySleep</key>
    <false/>
    <key>dndMirrored</key>
    <false/>
    <key>facetimeCanBreakDND</key>
    <false/>
    <key>repeatedFacetimeCallsBreaksDND</key>
    <false/>
    <key>userPref</key>
    <dict>
        <key>date</key>
        <date>2021-01-15T04:35:29Z</date>
        <key>enabled</key>
        <true/>
        <key>reason</key>
        <integer>1</integer>
    </dict>
</dict>
</plist>

The presence of the <key>userPref</key> and the <key>enabled</key> within the adjacent <dict></dict> indicate DND is enabled.

So the following code produces 1 if DND is enabled, and 0 if it is disabled:

#!/bin/zsh

dnd_enabled=$(plutil -extract dnd_prefs xml1 -o - ~/Library/Preferences/com.apple.ncprefs.plist | xpath -q -e 'string(//data)' | base64 -D | plutil -convert xml1 - -o - | xpath -q -e 'boolean(//key[text()="userPref"]/following-sibling::dict/key[text()="enabled"])')

echo $dnd_enabled 

Note: there seems to often be a lag time of a few seconds before the result reflects any change to DND.

Feel free to incorporate that in your workflow.

vitorgalvao commented 3 years ago

Thank you. I’ll look at the code and make improvements if I can. But that only checks the status. Do you have a solution to change it as well?

paulrudy commented 3 years ago

I haven't looked into it, but I imagine inserting the code (Edit: specifically, the <key>userPref</key> and its adjacent <dict></dict>, which are added when DND is enabled) that the script I shared detects might be enough. I might play around with it sometime in the future. Brett Terpstra seems to have reverse engineered activating and deactivating DND for his Bunch app. Maybe he would share his discoveries.

I noticed that the Do Not Disturb, Limited workflow uses a key combination (ctrl + option + shift + command + D) to turn DND on and off, and that works. But its time-limited function no longer works.

vitorgalvao commented 3 years ago

I haven't looked into it, but I imagine inserting the code (Edit: specifically, the <key>userPref</key> and its adjacent <dict></dict>, which are added when DND is enabled) that the script I shared detects might be enough.

Not a safe assumption on macOS, I’m afraid. There are plenty of cases (e.g. app preferences) where directly editing a plist isn’t enough to enact a change. Even with the old method we still needed to always kill the Notification Centre for the changes to be noticed.

paulrudy commented 3 years ago

I see, thanks. It looks like there might be a clue in the binary for Bunch.app, but I don't know how too read it.

vitorgalvao commented 3 years ago

https://twitter.com/vhgalvao/status/1350285067789152257 https://twitter.com/ttscoff/status/135055329675123917 https://twitter.com/ttscoff/status/1350553508819427329

paulrudy commented 3 years ago

Cool! I was messing around with plutil and the binary-encoded plist inside dnd_prefs, editing and re-encoding it (essentially a stepwise way of doing this that Brett linked to, and couldn't get it to work. The killall usernoted and killall ControlCenter make it work. Except that the menu bar icon doesn't update.

Decoding, editing, and re-encoding the embedded plist could lead to a workflow that is able to do additional things, like scheduled DND. Here's an example returned when DND is scheduled:

<?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>dndDisplayLock</key>
    <false/>
    <key>dndDisplaySleep</key>
    <false/>
    <key>dndMirrored</key>
    <false/>
    <key>facetimeCanBreakDND</key>
    <false/>
    <key>repeatedFacetimeCallsBreaksDND</key>
    <false/>
    <key>scheduledTime</key>
    <dict>
        <key>enabled</key>
        <true/>
        <key>end</key>
        <real>420</real>
        <key>start</key>
        <real>960</real>
    </dict>
    <key>userPref</key>
    <dict>
        <key>date</key>
        <date>2021-01-18T00:53:47Z</date>
        <key>enabled</key>
        <true/>
        <key>reason</key>
        <integer>1</integer>
    </dict>
</dict>
</plist>

But at my novice level, I'm not sure it's worth figuring it out when I can just move the mouse and click :)

vitorgalvao commented 3 years ago

I’ve developed a (so far it seems) robust solution for this. With that method—since it parses the XML—it would indeed be possible to schedule DnD, though at the moment I’m not interested in developing that feature.

It’s what the Workflow is using now, so closing as solved.

Thank you for the details you provided; they helped.