Honestly, Hammerspoon is an awesome tool to automate your macOS computer. It can react to system events, keyboard shortcuts and much more. Just have a look at its API and the Getting Started Guide. It is made for people that are not afraid to use an editor and code in the sweet programming language Lua. If you can code, it is more then sufficient to take a look at Learn Lua in Y Minutes to get a good grasp of Lua.

I could successfully replace 5 tools (CaffeineMoomKeyboard Maestro and Browserism) with a single script, that works much better and is way slimmer then these tools.

Caffeine

Caffeine has been a nice tool for disabling the sleep mode of my computer during a presentation or meeting. Sadly, it isn’t maintained anymore. A valid alternative is Amphetamine, but it has way to much options for me. I just need on and off. This can be accomplished with just 15 lines of Lua code in Hammerspoon. The code is taken straight from the Getting Started Guide. I just added the images from this reddit post.

--
-- Caffeine Replacement - Keep display awake when caffeine is active.
--

local caffeine = hs.menubar.new()

function setCaffeineDisplay(state)
    if state then
        caffeine:setIcon(os.getenv("HOME") .. "/.hammerspoon/caffeine/active.png")
    else
        caffeine:setIcon(os.getenv("HOME") .. "/.hammerspoon/caffeine/inactive.png")
    end
end

function caffeineClicked()
    setCaffeineDisplay(hs.caffeinate.toggle("displayIdle"))
end

if caffeine then
    caffeine:setClickCallback(caffeineClicked)
    setCaffeineDisplay(hs.caffeinate.get("displayIdle"))
end

Here you can download the images I use.

Moom & Keyboard Maestro

Well, macOS provides this fullscreen split view mode, but from my point of view it is useless crap. As much as I love split view on my iPad, I hate the implementation in macOS.

I have been working with some handy shortcuts for years now.

  • CTRL + ALT + Left — Move the current window to the left edge of the screen and resize it to half of the current screen size
  • CTRL + ALT + Right — Just the same in the other direction.
  • CTRL + ALT + Up — Resize the current window to “normal” fullscreen.
  • CTRL + ALT + Down — Resize the current window to 2/3 of the current screen width and the full screen height. Afterwards center it on the screen.

So far I have been using Moom or Keyboard Maestro for this setup. Moom is nice, but I don’t like the UI and it has some issues when moving and resizing windows at the same time. Keyboard Maestro has the worst user interface I know. I never got accustomed to it. Plain code works much better for me. So, I was pretty happy to find some good advice and examples in the Getting Started Guide again.

--
-- Window Movement
--
-- CTRL + ALT + Left - Move current window to the left half of the screen.
-- CTRL + ALT + Right - Move current window to the right half of the screen.
-- CTRL + ALT + Up - Go "fullscreen".
-- CTRL + ALT + Down - Center window, covering 2/3 of screen size.
--

function move_window(direction)
    return function()
        local win      = hs.window.focusedWindow()
        local app      = win:application()
        local app_name = app:name()
        local f        = win:frame()
        local screen   = win:screen()
        local max      = screen:frame()

        if direction == "left" then
            if app_name == "Tweetbot" then
                f.x = max.x
            else
                f.x = max.x
                f.w = max.w / 2
            end
        elseif direction == "right" then
            if app_name == "Tweetbot" then
                f.x = max.x + (max.w - f.w)
            else
                f.x = max.x + (max.w / 2)
                f.w = max.w / 2
            end
        elseif direction == "up" then
            f.x = max.x
            f.w = max.w
        elseif direction == "down" then
            f.x = max.x + (max.w / 6)
            f.w = max.w * 2 / 3
        else
            hs.alert.show("move_window(): Freaky parameter received " .. direction)
        end

        f.y = max.y
        f.h = max.h
        win:setFrame(f, 0)
    end
end

local hyper = {"ctrl", "alt"}
hs.hotkey.bind(hyper, "Left", move_window("left"))
hs.hotkey.bind(hyper, "Right", move_window("right"))
hs.hotkey.bind(hyper, "Up", move_window("up"))
hs.hotkey.bind(hyper, "Down", move_window("down"))

Browserism

Browserism is a nice tool to switch the default browser on macOS. But obviously I want to replace it with Hammerspoon, too.

This task was a bit trickier then the others. I wasted a lot of time to find out, how to switch the default browser using AppleScript. Apparently, this is not possible. You have to create an executable with Objective C or Swift to do it. But Hammerspoon can be set as the default browser, too. After I found out about that, the solution was pretty simple and with the hs.settings module you can even store the default browser setting between restarts.

--
-- Browser Menu
--

-- Step 1: Take care, that Hammerspoon is the default browser
if hs.urlevent.getDefaultHandler("http") ~= "org.hammerspoon.hammerspoon" then
    hs.urlevent.setDefaultHandler("http")
end

-- Step 2: Setup the browser menu
local active_browser     = hs.settings.get("active_browser") or "com.apple.safari"
local browser_menu       = hs.menubar.new()
local available_browsers = {
    ["com.apple.safari"] = {
        name = "Safari",
        icon = os.getenv("HOME") .. "/.hammerspoon/browsermenu/safari.png"
    },
    ["org.mozilla.firefox"] = {
        name = "Firefox",
        icon = os.getenv("HOME") .. "/.hammerspoon/browsermenu/firefox.png"
    },
    ["com.google.chrome"] = {
        name = "Google Chrome",
        icon = os.getenv("HOME") .. "/.hammerspoon/browsermenu/chrome.png"
    },
}

function init_browser_menu()
    local menu_items = {}

    for browser_id, browser_data in pairs(available_browsers) do
        local image = hs.image.imageFromPath(browser_data["icon"]):setSize({w=16, h=16})

        if browser_id == active_browser then
            browser_menu:setIcon(image)
        end

        table.insert(menu_items, {
            title   = browser_data["name"],
            image   = image,
            checked = browser_id == active_browser,
            fn      = function()
                active_browser = browser_id
                hs.settings.set("active_browser", browser_id)
                init_browser_menu()
            end
        })
    end

    browser_menu:setMenu(menu_items)
end

init_browser_menu()

-- Step 3: Register a handler for opening URLs
hs.urlevent.httpCallback = function(scheme, host, params, fullURL)
    hs.urlevent.openURLWithBundle(fullURL, active_browser)
end

Here you can download the icons I use. They were created using Font Awesome.

bildschirmfoto-2016-11-29-um-17-00-22

What’s next?

I think I will replace some more tools with a short snippet of Hammerspoon configuration.

  • Viscosity is simply the best OpenVPN client for macOS. Forget about Tunnelblick. But I think, I can replace it, too. I would prefer to edit my OpenVPN configs with an editor.
  • Hammerspoon can react to Wifi events. I would love to automatically connect to my private OpenVPN Server as soon as I connect to an untrusted Wifi network.

Complete Configuration

Here you can see my complete configuration file including automatic reloading if the file changes.


Image credit: Matthew Hurst. Shared under the Creative Commons Attribution-ShareAlike 2.0 Generic license.

About the Author Oliver Andrich

Developer – Web Aficionado – Part-Time Blogger

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s