Rolling My Own Mac Push Notifications

So I have a server that runs various jobs for me, automating some things that are time consuming or just checking in on things that I'm interested in and pinging me. Until yesterday, my notification of choice was email, born mostly out of simplicity.

Then I saw this on Twitter:

I hadn't really thought about getting any notifications such as job errors or status updates through my Mac's notification center, but suddenly it seemed much more appealing and much less intrusive than the email route. So off I went to figure it out.

Whelp, very early on I discovered that in order to use the Apple Push Notification service (APNs), you needed a Mac or iOS developer license, which will shorten your bank account $99/year. As I am neither of those things, I was determined to find a workaround using tools I already had.

My Parameters

  • I didn't want to run the jobs on my computer, I wanted to keep them on the remote server
  • I wanted to have real(ish) time notifications sent to my Mac from the server
  • I didn't want to spent more than an hour implementing this

Tools

terminal-notifier

Super easy to install:

[sudo] gem install terminal-notifier

Super easy to use:

terminal-notifier -title "Hey There" -message "Just checking in."

Sample Notification

Dropbox

I then set up a notifications folder in Dropbox. The goal was to add a shell script containing a command for terminal-notifier to the directory whenever we wanted to trigger a notification.

My jobs are written in PHP, so I chose the excellent Flysystem from The PHP League to hook into my Dropbox account.

Whipped up a quick class and I was on my way:

use DropboxClient;
use LeagueFlysystemFilesystem;
use LeagueFlysystemAdapterDropbox as Adapter;

class MacNotifier {

    protected $filesystem;

    public function __construct()
    {
        $client           = new Client('MY_ACCESS_TOKEN', 'Job');
        $this->filesystem = new Filesystem(new Adapter($client));
    }

    public function notify($title, $body, $url = null, $sender = null)
    {
        $filename     = 'notifications/' . microtime(TRUE) . '.sh';
        $notification = $this->getNotification($title, $body, $url, $sender);

        $this->filesystem->write($filename, $notification);
    }

    protected function getNotification($title, $body, $url, $sender)
    {
        $args = [
                '-title'   => $title,
                '-message' => $body,
                '-open'    => $url,
                '-sender'  => $sender,
            ];

        $args_str = [];

        foreach ($args as $key => $value) {
            if (!$value) continue;
            $args_str[] = "{$key} "{$value}"";
        }

        return 'terminal-notifier ' . implode(' ', $args_str);
    }
}

Hazel

Next I simply told Hazel to keep an eye on the notifications folder and execute anything with an .sh extension:

Hazel Settings

As a bonus, I had Hazel automatically clear anything that was older than today so that the directory didn't get too cluttered:

Hazel Clear Settings

In Action

Now, if I wanted to a job to notify me of something:

$notifier = new MacNotifier();
$notifier->notify('Oh, Hey Fella.', 'Just a friendly greeting');

Optionally, I could also pass in a url to jump to when clicking on the notification:

$notifier = new MacNotifier();
$notifier->notify('Oh, Hey Fella.', 'Just a friendly greeting', 'http://joe.codes');

I could also hijack the icon of an app on my computer (such as Automator) if I didn't want the standard Terminal icon for the notification:

$notifier = new MacNotifier();
$notifier->notify('Oh, Hey Fella.', 'Just a friendly greeting', 'http://joe.codes', 'com.apple.Automator');

Final Notification

Conclusion

All in all, it works beautifully. The only minor glitch is that I get a double notification, first the notification from Dropbox that the file has been added, then the actual notification I was looking for.

There you have it, a fairly simple hack for Mac desktop notifications without the APNs. Enjoy.

Title background image by Thomas Leuthard