Hammerspoon – macOS automation with Lua
hammerspoon.org
hammerspoon.org
* As a replacement for the classic caffeine app. [1]
* To provide shortcuts for window managements. [2]
* To shutdown bluetooth on sleep. Otherwise, devices I have setup to pair with both my mac and phone will tend to pair with the mac, even though it's asleep. [3]
1: https://gist.github.com/heptal/50998f66de5aba955c00
2: https://github.com/miromannino/miro-windows-manager
3: https://gist.github.com/ysimonson/fea48ee8a68ed2cbac12473e87...
I'm already using hammerspoon for making scrolling work with my trackball mouse and it works great.
On top of that, Hammerspoon is very actively developed and the maintainers are super knowledgeable and responsive.
I've shared a couple scripts on here before, like Anycomplete (Google autocomplete anywhere): https://news.ycombinator.com/item?id=13065670
For the casual Mac user (e. g. product manager or the guy from sales) the visual Sikuli or UI.Vision RPA tools are more suitable for creating automation scripts.
Why would I prefer this over the built in option?
For example, to obtain the current date and one day from now, in Objective-C you might write:
NSDate *now = [NSDate date];
NSDate *tmr = [date dateByAddingTimeInterval: 86400];
The equivalent in JavaScript is simply var now = $.NSDate.date;
var tmr = now.dateByAddingTimeInterval(86400);Yeah, I can’t do this. It’s non-obvious to me how it translates until I actually stumble upon the correct thing.
It certainly reads nicer to non-programmers, but I feel like writing it requires just as much learning as any more "traditional" scripting language, so I'm not really sure what the benefit is.
I really like Applescript. I agree that it's harder to write, and especially harder to write well, because functional Applescript won't necessarily come out as english sentences, if you aren't careful to construct it the right way.
But the enhanced readability makes it worthwhile. I love that I can look at my own Applescript code and just read what it's doing in english.
I actually wish I could do more with Applescript than just Mac scripting. I've been thinking for a few years now that I ought to play around with Hypercard, but PowerPC emulators are a pain to get running. There's a modern clone called SuperCard, but it costs $200...
P.S. The "open dictionary" option built into the Script Editor is helpful for finding the right "incantation".
Read-only, write-never?
;)
Back when appscript was still a thing I hacked together a quick-n-dirty AppleScript-command-to-Python/Ruby/ObjC translator app which appscript users absolutely adored, plus it greatly reduced the number of “How do I…” support questions because, more often than not, users already had a working example in AppleScript which they could just run through the translator to get the answer for themselves.
Needless to say, Sal Soghoian completely ignored my expert recommendation for this killer feature which could be implemented in a day, choosing instead to dump JXA and its few hapless users down the same black hole of nothingness as all his other product failures. PEBKAC. Not Invented Here. So it goes.
--
[edited to add]
Just d/led the JavaScriptOSA zip from the appscript project page on SourceForge, and the bundled JABDemo.app still works. It’s not codesigned so you’ll have to right-click the app to Open it, overriding the security warnings, but it still works even on 10.15 (caveat any bugs in the underlying JavaScript Apple event bridge, of which there were a few, plus the endless string of permission requests whenever you try to control another app). Example:
Original AppleScript commands:
tell app "textedit" to get name of document 1
tell app "finder" to count every folder of home whose name begins with "D"
Generated JavaScript equivalents: app("TextEdit").documents.at(1).name.get()
app("Finder").home.folders.where(its.name.beginsWith("D")).count({each: k.item})
I mean, I wrote the thing and even to me years later it’s still like freaking voodoo! :)I'm already using MenuHammer[1] (menu system inspired by Spacemacs) and Spectacle.
But plan for a next year or two is to build custom ergo keyboard (r/ErgoMechKeyboards has a bunch of interesting stuff - I'm aiming for Corne or Iris) and then to customize keyboard layout + Hammerspoon to reduce mouse usage as much as possible. With various VIM plugins available for editors and Firefox (Tridactyl) I'm quite optimistic.
Exactly. It's been years since first released and nothing is really out there to tell you how to use it. I wish they would finish it up as it is much better that AppleScript.
Worst part is: it should never have come to this.
(For those that don’t know, I am the world expert at building production-ready Apple event bridges, with half-a-dozen under my belt. Outside of myself, Dave Winer’s Userland, and the original AppleScript creators, no-one else on the planet has ever managed this.)
Immediately after JXA was surprised-announced at WWDC14, I dropped everything else, reached out to Sal Soghoian (Mac Automation PM), and crash-coursed six weeks of testing and critiquing JXA, up to and including writing a nearly-complete “JavaScriptOSA” reference implementation for the JXA devs to learn from, or just outright steal.
Even rushed and unfinished, JavaScriptOSA showed what JXA should’ve been. And they had double the developers, all the AppleScript source and internal Apple documentation, and a year. You can d/l it here:
https://sourceforge.net/projects/appscript/files/JavaScriptO...
While the Xcode project is far too old to build now and some of that code is a lashup of which I’m not proud, the prebuilt component may still work in Script Editor (though I’ve not tested it in years), as should the standalone “demo” app I bundled with it.
Bear in mind, my JavaScriptOSA implementation[1] was based on my previous appscript work [2], which had alrady proven itself a genuine AppleScript replacement years earlier. Apple even considered including Python and Ruby appscript in Mac OS X Leopard, so it’s not like my code wasn’t already known about, read, or used by folks within Apple. But the Mac Automation team still thought they knew better than a production-proven solution that had at its height a couple thousand extremely happy users, whereas their own previous attempt had only proven an embarrassing failure.
I didn’t get so much as a thanks-but-no-thanks; 200+ work hours straight down the crapper. And JXA shipped half-baked and broken, and immediately abandoned to sink without trace. I scrapped my plan to write the book on Automating your Mac with JavaScript, because you can’t write really good books about really lame broken technology, at least not without losing your mind. (I should know: I lead-authored one of the last AppleScript books.) Apple’s last, best chance to turn Mac Automation around, pissed up the wall and forgotten just as fast.
Honestly, Apple’s real mistake was not firing that preening incompetent, Sal Soghoian, years sooner, while there was still something worth saving.
..
Okay, so with hindsight, JXA was never likely to succeed, even if they had done its implementation and marketing right. As an OSA language component, it lives in its own little closed world, completely separate to Node.js and its vast npm ecosystem. Even in 2014 Node was already killing it in the JS-on-server-and-desktop wars, but instead of “Embrace and Extend” JXA chose “Not Invented Here” instead.
Worse, JXA is built on OSA which is a ComponentManager technology; a hideously ancient, inherently insecure, and long-deprecated in-process plugin architecture. Thus the JXA implementation was already obsolete even before it shipped. A straight port of node and npm CLI tools onto JavaScriptCore with a V8 API compatibility layer for C-based extensions would have been the right choice. But the Mac Automation team didn’t see, because like their technologies they exist in a closed little world completely isolated from everything and everyone else.
One More Thing: Around the same time as Sal was getting his ass ejected from Apple, I wrote one more Apple event bridge, nodeautomation, just to prove my point that creating a competent AppleScript alternative for a massively popular, developer-friendly language is both quickly and easily achievable, so Sal’s team had absolutely no excuse for fucking it up twice.[3] Although it looks like recent macOS/Node updates have broken the build (again), so you’d need to run it on an older platform if you wanted to play with it.
I can still vouch for Python3-appscript, as that’s what I use myself, but I no longer provide support for any of them because as much as I love Apple event automation and the incredible solutions it makes easy to build, even I accept it has been utterly buggered to death now, and the only question remaining is how much longer till Apple finally put it out our misery for good.
..
That said, if any Hammerspoon fans wish to cross my palm with silver, I’ve previously offered to do a Lua-Apple event bridge for it, and would be happy to do so even now. (Misery loves company.:)
--
[1] No relation to Late Night Software’s original JavaScriptOSA, which like JXA was also riddled with flaws and never went anywhere.
[2] http://appscript.sourceforge.net/
[3] Or thrice, if you count the time after the JXA fiasco that I offered to give Sal my SwiftAutomation bridge [4] outright, just as Swift was starting its meteoric rise, and he threw it back in my face. I’ve dealt with some stupid PMs over the years, but Jebus that one was dumber’n all those other stumps put together. What a waste.
(Though when end-user automation does work it is epic AF.)
Unfortunately, programmers look at AppleScript syntax, which is superficially OO-like, and assume it is OOP. Which causes huge confusion and frustration when it behaves in completely non-OO ways.
The confusion is confounded by AppleScript’s fondness for “magical” behaviors—including overloading native language operators and automatically dispatching remote calls (aka “implicit gets”) without obvious rhyme or reason—plus enough syntactic sugar to rot anyone’s brain to mush.
Ironically, non-programmers don’t struggle nearly so much because they are operating on simple ignorance rather than outright misunderstanding, so they accept what it’s doing on face value, rather than trying to map everything they see onto completely the wrong mental model and getting utterly honked that prediction and observation refuse to match up.
In fact there are hard rules behind all of AS’s behavior, but they are utterly opaque and never adequately explained anywhere, so are largely indistinguishable from “What the F* is Going On?”. Hell, I rewrote most of the early chapters for the last edition of Apress’s AppleScript book, and even I bottled it as “too hard to explain to anyone else”.
..
Things might’ve turned out differently had AppleScript’s original designers not quit in disgust at Apple [mis]management shortly after AppleScript 1.1 went out, but when they walked out much of the expert insight and knowledge went with them, not to mention the ability to fix it once users’ feedback was pointing all its flaws out. But “coulda, woulda, shoulda” is the Mac Automation story throughout (my own failed contributions included).
Dr William Cook’s paper on the early history of AppleScript and its design and ambitions is well worth a read:
It’s only a pity it wasn’t published a decade earlier, back when classic Mac OS developers were attempting to build Apple event bridges for languages like Perl, Python, and JavaScript. All of those popular programmer-friendly languages had their own native Apple event bridges from very early on. And all of those bridges sucked.
Not because they’re technically hard to write, but because no-one ever explained how.
And since (with the exception of Perl and Frontier) all those third-party devs were coming at the problem from a strongly OOP background, they all promptyl fell into the ORM trap: trying to make Apple events behave like the host language’s native OO, instead of vice-versa. ’Cos if you think creating an Object-Relational Mapper over half-a-dozen similiar SQL-speaking RDBMSes results in a crippled dumbed-down API that fights you more than it helps, imagine trying to create one that works fully and correctly over hundreds of wildly differing targets with no common dialect at all.
(Perl’s Mac::Glue did avoid the ORM trap, but—being Perl—ran into impedance mismatches of its own. Only Frontier got it more or less right… right in time for Dave Winer to throw his toys out the pram and sulk off.)
Thus AppleScript never won because it was good. It won because all of its alternatives were even worse.
Even the first Python appscript, which I wrote way back in 2003, was a stinker; although it did get better eventually (after I threw it out and redesigned and rewrote it from scratch). Bloodly lovely bit of kit in the end, never equalled before or since—and I say that as its most rabidly demanding and intolerant user.:) Still kicking myself for blowing the one chance to get it bundled in Mac OS X itself; Apple Automation would be in a very different place today if it had.
Automator/AppleScript are sufficient when you only need their standard hook points―services and such.
Btw, JXA is a big disappointment: it maps Objective-C/AppleScript's calls and especially structures very awkwardly instead of providing more JS-native experience. And afaik its official documentation is lacking.
I had macros for mining up, down, left, right, and in all diagonals (eventually expanded to also placing dirt below me so I don't fall into a chasm and die), and eventually integrated mining with placing rails for underground transport!
My reasoning was that I enjoyed the action parts but not the walking parts, so I made a train tunnel, but that was even more boring than walking, so I automated it. Eventually I was having more fun automating the game than playing it.
Oh, side note, getting infinite money and life in CheatEngine (also has Mac version now) taught me an important life lesson. Using my cheat-superpowers I defeated Wall of Flesh (who had been obstructing my progress for weeks), and suddenly felt a deep sense of dread. I hadn't really achieved anything, I had cheated! Suddenly I realized I had the exact same attitude towards the rest of my life, always trying to find an easy way out, a way to win without playing the game, and that this was a source of great dissatisfaction.
Additionally, I use it to bind caps lock to escape, unless it’s pressed with another key, in which case it’s curl. So it’s great to not need Karabiner anymore.
In linux and windows you can just use the superkey+1, 2, etc. I'm surprised macos doesn't have the equivalent to this built in.
One funny thing while searching for information was that almost every Hammerspoon discussion had the mandatory "why not AppleScript?!" comment.
Rhetorical, shurely!?
https://news.ycombinator.com/item?id=12097303
(Understand it's not a dupe; sharing for info purposes).
I wrote a simple window manager that just lets me pick a set of windows across applications and bring them all to the front and cycle through them.
https://github.com/macrael/panes
It probably could be faster but it's so nice to be able to keep my terminal and my editor frontmost while I'm running tests with `entr`.
[1] https://github.com/agzam/spacehammer [2] https://gitlab.com/technomancy/fennel-mode
A Spacehammer REPL would be huge for prototyping/developing new features in Fennel. My github is mitchellw. Would love to help out :)
fennel.eval([=[ (code) ]=])
and then sending to the hs cli.I haven't done much yet; this is the first time I've tried to write a real Emacs mode. It would be much nicer to have a real Fennel REPL integrated in Hammerspoon, but the above would be enough for things like eval-last-sexp and org-babel-eval.
[1] Sadly, the hs cli does not handle multi-line strings.
Once I get a few quirks ironed out, I'll submit a PR to spacemacs to include it in the dev branch
Very very rarely do I have to manually move windows around -- I find that almost all of my needs are covered with a set of specific window positions and sizes.
I also have a window selector that displays all windows and their names and allows me to use substrings to filter the list. But I find I never use it.
I used to use Hammerspoon to implement a "vi navigation" mode, toggled by tapping the Cmd key. In vi navigation mode, hjkl move the cursor, x deletes a character, and so on. But I discovered that Karabiner Elements can do something similar, and now I'm using that instead.
I used to use Hammerspoon for a "vi
For me it is indispensable as a hotkey launcher, keyboard, window mover/resizer, clipboard history manager, stay-awake, GIPHY API lookup, pomodoro timer, and a snippet manager.
My most used combo is ctrl (remapped capslock) - ' as a launcher, then C for Chrome, J for IntelliJ, V for MacVim, ... keeps me off cmd-tab which keeps me off seeing notification number in mail and slack.
Hammerspoon is my #1 tool for modding OSX to make it more usable.
- Tossing windows around?
- Controlling music?
- Jumping between apps?
- Adding more convenient shortcuts for your favorite apps?
- Editing any text anywhere with your favorite editor?
Why not modality then?
Not something most people are switching all the time, but if you're writing CSS for a
prefers-color-scheme: dark
media query, then being able to pop back and forth on demand is handy.I actually had Hammerspoon installed for multiple years on my home computer and the only thing I ever wrote with it was a hotkey to reload my Hammerspoon config.
Lots of useful examples https://www.hammerspoon.org/go/#table-of-contents
sudo killall -STOP -c usbd
Let's see if it works - Automator didn't work too well cmd + shift - 9 : /usr/bin/osascript -e 'do shell script "killall coreaudiod 2>&1" with administrator privileges'
Here's the relevant line from my skhd configuration. This does pop a password prompt, but it works. I imagine you could adapt this to fit Hammerspoon.However it works a bit slowly sometimes, but dunno if that's a problem with Hammerspoon or with my machine.
Keyboard Maestro on the other side is commercial software with a nice UI editor were you can compose automation scripts by simply dragging and dropping actions. Personally I use Keyboard Maestro and find it absolutely worth its money. Even without any programming experience you can create powerfull scripts within minutes. I use it as clipboard manager, snippet manager, text expansion and much more.