Category Archives: Hammerspoon
how to add a window switcher
command tab Application switcher, you might enjoy adding a window switcher to your list of keyboard hotkeys. The window switcher allows you to jump between different windows, both those of other apps and the same app with a hotkey like option tab, which sits nicely next to command tab in my muscle memory!
This is particularly useful if you have a couple of windows open in several applications, and it is much faster and neater than first using command tab and then command backtick to cycle through an app’s windows. Another advantage here is that the window switcher will include full screen and non-full screen windows in multiple spaces, which command backtick typically does not handle well, something I find particularly frustrating when using Xcode.
Adding a window switcher is easy and doesn’t require any hacking. It does require Hammerspoon, however. But if you’re a regular reader of this blog, you’ll already have installed Hammerspoon after reading my earlier posts on it here and here and here. π
With Hammerspoon up and running, adding the window switcher is just a case of cutting and pasting some code into your config file, saving it then reloading. You can use the default code in hs.window.switcher docs or use mine below. The default code is a bit ugly for my liking. Instead, I use the code below, which sets up the switcher’s ui as seen in the screenshots here with option tab and option-shift tab for shortcuts, but you can modify the appearance to suit your taste. As ever, the Hammerspoon docs are wonderfully clear and easy to follow (take a lesson, Apple!).

-- set up your windowfilter
switcher = hs.window.switcher.new() -- default windowfilter: only visible windows, all Spaces
switcher.ui.highlightColor = {0.4,0.4,0.5,0.8}
switcher.ui.thumbnailSize = 112
switcher.ui.selectedThumbnailSize = 284
switcher.ui.backgroundColor = {0.3, 0.3, 0.3, 0.5}
switcher.ui.fontName = 'System'
switcher.ui.textSize = 14
switcher.ui.showSelectedTitle = false
-- bind to hotkeys; WARNING: at least one modifier key is required!
hs.hotkey.bind("alt","tab",function()switcher:next()end)
hs.hotkey.bind("alt-shift","tab",function()switcher:previous()end)
--EOF
Adjust values such as shortcut bindings as you please, and that’s it. For two minutes work, you just added a very useful window switcher to macOS!
Enjoy! π
how to display wifi name (ssid) in menu bar

If youβre one for visiting coffee shops and other places with public networks, you might have an interest in todayβs Hammerspoon tip.
This little code added to your config file will display the name (SSID) of the currently connected Wifi network in your menu bar. Whenever the Wifi network changes, the name will automatically update so you can always see at a glance that youβre connected to the Wifi network that you think you are.
Playing around with that unusually long number (between 2147483644 and β¦647) will move the SSID name along the icon bar. Set it to the default β1000β if you want macOS to decide where it should be.
wifiWatcher = nilfunction ssidChanged()local wifiName = hs.wifi.currentNetwork()if wifiName thenwifiMenu:setTitle(wifiName)elsewifiMenu:setTitle("Wifi OFF")endendwifiMenu = hs.menubar.newWithPriority(2147483645)ssidChanged()wifiWatcher = hs.wifi.watcher.new(ssidChanged):start()

Enjoy! π
Further Reading
More fun with Hammerspoon
Get automated with Hammerspoon
more fun with Hammerspoon

For anyone using Hammerspoon and folder βwatchersβ to alert them to changes to a given folder, Iβve updated the watcher function I wrote about here
The problem with the simple alert I demonstrated last time is that it only hangs around for a second or two (much less than a Folder Action alert, which takes a couple of minutes to time out). In this updated function, it now also writes a list of the file changes to a (by default) file on the Desktop. The write is an append: if the file doesnβt exist it will create it before writing; if it does exist, it will append the latest changes and date to the file. This way, even if you miss the alert, youβll always have a record of what files have been added, deleted or modified in your watched folder.
In this example, the folder being watched is ~/Library/LaunchAgents since we want to be aware of any adware, malware or other unsavoury processes being surreptitiously added by, for example, apps we download from the internet. Although there are, of course, many legitimate reasons for apps placing items in here, this folder is prime real estate for attackers as it is one of the locations that can launch processes at log in time without the userβs interaction (or knowledge).
Hereβs the code, also available from my pastebin here. A code walkthrough follows.
function writeStringToFileWithMode(aString, aPath, aMode)local this_filethis_file = io.open(aPath, aMode)this_file:write(aString)this_file:close()endfunction myFolderWatch(files)local str = "Launch Agents folder was modified on " .. os.date() .. " :\n\t"local this_path = os.getenv("HOME") .. "/Desktop/LaunchFolderModified.txt"local ignore = "DS_Store"local count = 0for _,file in pairs (files) docount = count + 1i = string.find(file, ignore)if not i thenstr = str .. file .. "\n\t"elseif count == 1 thenstr = "\n"endendendstr = str .. "\n"writeStringToFileWithMode(str, this_path, "a")if string.len(str) > 2 thenhs.alert.show("Launch Agents folder was modified.")endendlocal aWatcher = hs.pathwatcher.new(os.getenv("HOME") .. "/Library/LaunchAgents/", myFolderWatch):start()
Code walkthrough
The first function, βwriteStringToFileWithModeβ is just a convenience function. Hopefully the clue is in the name. The βaModeβ parameter is either βwβ for write(over) or βaβ for write(append).
The myFolderWatch function starts off by declaring some local variables.
βstrβ includes the initial line that we want to write to our output file and interpolates the time of the change by calling os.date().
βthis_pathβ defines the path that we want to write our list of file names too.
The β .. β in both these assignments is a string concatenator (i.e., like β&β in AppleScript or βstringByAppendingStringβ in Obj-C).
βignoreβ is a string that will help us to exclude .DS_Store files from triggering the alert or appearing in the list of file changes.
The βcountβ variable is an internal var we need in order to eliminate .DS_Store changes when itβs the only change. Lua doesnβt have an easy way to count entries in tables, so we bascially iterate a variable each time through the loop to achieve the same effect.
After that, we have the βforβ loop. For loops in Lua are weird (at least for me), as youβll see that they have this structure for a,b in pair (aPair). I wonβt go into why, other than to say its a result of Luaβs table data structure. The '_' here is just a dummy variable for the first parameter. The βfilesβ in parentheses are the list of file names (not file specifiers, you AppleScripters!) that were added, deleted, or modified in the watched folder.
The loop begins by incrementing the count, then checks for the ignore file (.DS_Store). If the ignore file is not found, we then append the filename to the str variable.
If it is found, we check the count. If the count is β1β (i.e., the only change was .DS_Store) we discard the entire str var and replace it with new line character. If the count is more than 1 we donβt do anything to βstrβ. We just ignore adding anything to it all.
At the end of the for loop we add another new line to the string just so our outputted text file looks nice and neat.
Then we call the write function mentioned above, passing βaβ (append) for the mode.
Finally, we fire the UI alert on the condition that the string has got more than 2 characters in it (if it didnβt it was just the β\n\nβ string from ignoring the DS.Store file).
After the function definition, the aWatcher variable sets up the watcher on the path we want to observe and tells the watcher to start monitoring. It tells the watcher to call our myFolderWatch function when anything happens.
Deploying the code
After editing the config file, remember thereβs two steps: i. save the file and ii. choose βReload Configβ from the Hammerspoon menu.
Further Reading:
More details are available about Hammerspoon from the official site here.
My beginners guide with the original simple watcher function is here.

