How I automated my macbook to help me get ready for work.

Featured on Hashnode

I recently started playing around with applescript. Applescript is macos's native scripting language enabling you to control things from opening applications to changing songs that play on Spotify. There are a lot of cool things you can programatically control on your mac and applescript opens the doors to a lot of cool automation.

As a productivity enthusiast I wanted to see if I can launch applications I use for software development work using applescript. Here is a gist of the script.

Screen Shot 2022-09-14 at 8.55.27 AM.png

What the script does

  1. Turn on focus mode to work disabling most notifications except for emails, slack and other productivity based applications.
  2. Lauch applications I typically use for work and or personal projects - VS Code, Calendar, Zoom, Slack, Spotify, Google Chrome, etc...

So while the script would save me from individually opening applications, I would need to manually trigger the script. Manual actions kill the point of an "automation" and I would want this to run every morning at 9am. How can I do this?

Using launchd

launchd is an "open-source service management framework for starting, stopping and managing daemons, applications, processes, and scripts. Written and designed by Dave Zarzycki at Apple, it was introduced with Mac OS X Tiger and is licensed under the Apache License."

Apple has their own solution when it comes to scripting and automation. While running a cron job may also be a solution, you can also create a LaunchAgent or LaunchDaemon to run a script for you.

Differences

  • LaunchAgent - Needs a user logged into the computer to run.
  • LaunchDaemon - Does not rely on a logged in user.

So how to get your LaunchAgent or LaunchDaemon setup -

Screen Shot 2022-09-14 at 7.42.23 AM.png

Go open your ~/Library/LaunchAgents or ~/Library/LaunchDaemons files. There you will see a bunch of files with the filetype of .plist which are formatted xml files. Create a new .plist file. You can refer back to the launchd documentation for the specific formatting of the document. For our use case we only need a couple things

LaunchAgent requirements

  1. Path of program / script we need to trigger
  2. A schedule of when that occurs
  3. A label of the launchAgent itself to have it identified
<?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>Label</key>
    <string>com.morningautomation.plist</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/bin/osascript</string>
        <string>/Library/Scripts/Personal/setup-morning.applescript</string>
    </array>
    <key>StartCalendarInterval</key>
    <array>
        <dict>
            <key>Weekday</key>
            <integer>1</integer>
            <key>Hour</key>
            <integer>9</integer>
            <key>Minute</key>
            <integer>0</integer>
        </dict>
        <dict>
            <key>Weekday</key>
            <integer>2</integer>
            <key>Hour</key>
            <integer>9</integer>
            <key>Minute</key>
            <integer>0</integer>
        </dict>
        <dict>
            <key>Weekday</key>
            <integer>3</integer>
            <key>Hour</key>
            <integer>9</integer>
            <key>Minute</key>
            <integer>0</integer>
        </dict>
        <dict>
            <key>Weekday</key>
            <integer>4</integer>
            <key>Hour</key>
            <integer>9</integer>
            <key>Minute</key>
            <integer>0</integer>
        </dict>
        <dict>
            <key>Weekday</key>
            <integer>5</integer>
            <key>Hour</key>
            <integer>9</integer>
            <key>Minute</key>
            <integer>0</integer>
        </dict>
    </array>
  </dict>
</plist>

So label is going to be needed when we load the .plist file onto our launchctl function that we will run in a bit.

<key>ProgramArguments</key> is a pointer to where our script is. I saved my script /Library/Scripts/Personal/... you can save yours where you want just reference it here. Also note that I have included <string>/usr/bin/osascript</string> in addition. This is required if you are running a .applescript or .scpt file type.

There is no one-liner when it comes to scheduling the trigger for a specific date and time so I had to manually add each date and time for the script trigger. Under a <key>StartCalendarInterval</key> we define an array of <dict> that has keys of the weekday, hour and minute and the specific integers referring to that. Again, everything is referenced in the launchd docs linked earlier.

Screen Shot 2022-09-14 at 7.51.40 AM.png

Now all we need to do is load it up. Go to your terminal

host:~ user$ launchctl load ~/Library/LaunchAgents/com.morningautomation.plist

Run to check if loaded successfully

host:~ user$ launchctl list | grep <name you identified on label key>

Alternatives

I honestly had a lot of trouble loading from the terminal, especially when debugging or ensuring it even runs. But you can use a launch control gui that you would have to download. There is a free trial and you don't need to pay for it. I used it to easily load and debug some of the scripts (quickly change the times to get it to trigger the applescript immediately instead of waiting).

But if you do pay for it (I have not but it seems appealing) you can do the whole setup without having to write your own .plist file which may be good if you don't know how to code or want a faster solution. Also there is a better debuging experience using this program.

It looks like this -

Screen Shot 2022-09-14 at 7.58.54 AM.png

How can I make this better ?

  1. Use applescript to open zoom links that are linked on calendar and open them whenever the calendar event starts.
  2. Setup my own morning briefing such as telling me some relevant news and weather information.
  3. Open up VS Code to specific programs and run the development commands via terminal such as building or watching in dev mode. This would get my whole development mode started for me.