Last active
May 7, 2018 07:24
-
-
Save mattmattmatt/21f28d998f10f2135cda83a8feac0813 to your computer and use it in GitHub Desktop.
Hammerspoon automation configuration
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
hs.alert.show('Hammerspoon config loaded') | |
hotkeyF3 = hs.hotkey.modal.new('', 'f3') | |
hotkeyF4 = hs.hotkey.modal.new('', 'f4') | |
hs.window.animationDuration = 0.1 | |
cachedNumScreens = #hs.screen.allScreens() | |
hs.dockicon.hide() | |
kodihost = 'http://192.168.1.140' | |
------------------------------- | |
-- Auto-reload functionality -- | |
------------------------------- | |
function reloadConfig(files) | |
doReload = false | |
for _,file in pairs(files) do | |
if file:sub(-4) == '.lua' then | |
doReload = true | |
end | |
end | |
if doReload then | |
hs.reload() | |
end | |
end | |
hs.pathwatcher.new(os.getenv('HOME') .. '/.hammerspoon/', reloadConfig):start() | |
------------------- | |
-- Menu bar icon -- | |
------------------- | |
function setMenuIcon(state, title) | |
if state then | |
menuIcon = hs.menubar.new() | |
menuIcon:setTitle(title) | |
menuIcon:setTooltip('Hammerspoon Hotkey Mode Active') | |
else | |
if menuIcon then | |
menuIcon:delete() | |
end | |
end | |
end | |
setMenuIcon(false) | |
------------------------------- | |
-- Window size constants -- | |
------------------------------- | |
local size1 = hs.geometry(0.0, 0.0, 0.5, 0.8) | |
local size2 = hs.geometry(0.5, 0.0, 0.5, 0.8) | |
local size3 = hs.geometry(0.0, 0.0, 0.5, 0.7) | |
local size4 = hs.geometry(0.5, 0.0, 0.5, 0.7) | |
local size5 = hs.geometry(0.0, 0.0, 0.6, 1.0) | |
local size6 = hs.geometry(0.4, 0.0, 0.6, 1.0) | |
local size7 = hs.geometry(0.0, 0.1, 0.5, 0.7) | |
local size8 = hs.geometry(0.5, 0.1, 0.5, 0.7) | |
local size9 = hs.geometry(0.45, 0.1, 0.55, 0.7) | |
local sizeCenter = hs.geometry(0.15, 0.15, 0.7, 0.7) | |
local function sizeHotkeyUnit(unit, k) | |
local win = hs.window.focusedWindow() | |
win:moveToUnit(unit) | |
if k then | |
k:exit() | |
end | |
end | |
------------------------- | |
-- Normal key bindings -- | |
------------------------- | |
hs.hotkey.bind({'shift', 'ctrl'}, 'f12', function() hs.caffeinate.systemSleep() end) | |
hs.hotkey.bind({'shift', 'ctrl'}, 'left', function() sizeHotkeyUnit(hs.geometry(0, 0, 0.5, 1)) end) | |
hs.hotkey.bind({'shift', 'ctrl'}, 'right', function() sizeHotkeyUnit(hs.geometry(0.5, 0, 0.5, 1)) end) | |
hs.hotkey.bind({'shift', 'ctrl'}, 'up', function() sizeHotkeyUnit(hs.geometry(0, 0, 1, 1)) end) | |
hs.hotkey.bind({'shift', 'ctrl'}, 'down', function() sizeHotkeyUnit(sizeCenter) end) | |
hs.hotkey.bind({'alt', 'ctrl'}, 'left', function() | |
hs.alert.show('Left', 0.5) | |
local win = hs.window.focusedWindow() | |
win:moveOneScreenWest(false, true, 0) | |
end) | |
hs.hotkey.bind({'alt', 'ctrl'}, 'right', function() | |
hs.alert.show('Right', 0.5) | |
local win = hs.window.focusedWindow() | |
win:moveOneScreenEast(false, true, 0) | |
end) | |
for key, name in pairs({ | |
f1 = 'Finder', | |
f2 = 'Google Chrome', | |
f3 = 'Atom', | |
f4 = 'Terminal', | |
f5 = 'Slack', | |
f6 = 'Spotify', | |
f7 = 'System Preferences', | |
}) do | |
hs.hotkey.bind({'cmd'}, key, function() | |
local app = hs.application.get(name) | |
if not app then | |
hs.alert.closeAll() | |
hs.alert.show(name, 0.5) | |
hs.application.launchOrFocus(name) | |
elseif not app:isFrontmost() then | |
hs.alert.closeAll() | |
hs.alert.show(name, 0.5) | |
app:activate() | |
end | |
end) | |
end | |
--[[ | |
hs.hotkey.bind({'alt', 'ctrl'}, 'up', function() | |
hs.alert.show('Up', 0.5) | |
local win = hs.window.focusedWindow() | |
win:moveOneScreenNorth(false, true, 0) | |
end) | |
hs.hotkey.bind({'alt', 'ctrl'}, 'down', function() | |
hs.alert.show('Down', 0.5) | |
local win = hs.window.focusedWindow() | |
win:moveOneScreenSouth(false, true, 0) | |
end) | |
--]] | |
------------------ | |
-- Kodi Hotkeys -- | |
------------------ | |
function sendKodiCommand(name, method, params) | |
hs.alert.show('Kodi ' .. name, 0.6) | |
local data = [[{ | |
"jsonrpc": "2.0", | |
"method": "]] .. method .. [[", | |
"params": ]] .. params .. [[, | |
"id": 1 | |
}]] | |
local headers = { | |
["Content-Type"] = 'application/json' | |
} | |
print(hs.http.post(kodihost .. '/jsonrpc', data, headers)) | |
end | |
function unbindKodiKeys() | |
hs.hotkey.deleteAll({'ctrl'}, 'f8'); | |
hs.hotkey.deleteAll({'ctrl'}, 'f7'); | |
hs.hotkey.deleteAll({'ctrl'}, 'f9'); | |
hs.hotkey.deleteAll({'ctrl'}, 'f10'); | |
hs.hotkey.deleteAll({'ctrl'}, 'f11'); | |
hs.hotkey.deleteAll({'ctrl'}, 'f12'); | |
hs.hotkey.deleteAll({'ctrl'}, 'left'); | |
hs.hotkey.deleteAll({'ctrl'}, 'right'); | |
hs.hotkey.deleteAll({'ctrl'}, 'up'); | |
hs.hotkey.deleteAll({'ctrl'}, 'down'); | |
hs.hotkey.deleteAll({'ctrl'}, 'return'); | |
hs.hotkey.deleteAll({'ctrl'}, 'delete'); | |
hs.hotkey.deleteAll({'ctrl'}, 'c'); | |
end | |
function bindKodiKeys() | |
hs.hotkey.bind({'ctrl'}, 'f8', function() sendKodiCommand('Play/Pause', 'Player.PlayPause', '{"playerid": 0}') end) | |
hs.hotkey.bind({'ctrl'}, 'f7', function() sendKodiCommand('Previous', 'Player.GoTo', '{"playerid": 0, "to": "previous"}') end) | |
hs.hotkey.bind({'ctrl'}, 'f9', function() sendKodiCommand('Next', 'Player.GoTo', '{"playerid": 0, "to": "next"}') end) | |
hs.hotkey.bind({'ctrl'}, 'f10', function() sendKodiCommand('Mute/Unmute', 'Input.ExecuteAction', '{"action": "mute"}') end) | |
hs.hotkey.bind({'ctrl'}, 'f11', function() sendKodiCommand('Volume Down', 'Input.ExecuteAction', '{"action": "volumedown"}') end, nil, function() sendKodiCommand('Volume Down', 'Input.ExecuteAction', '{"action": "volumedown"}') end) | |
hs.hotkey.bind({'ctrl'}, 'f12', function() sendKodiCommand('Volume Up', 'Input.ExecuteAction', '{"action": "volumeup"}') end, nil, function() sendKodiCommand('Volume Up', 'Input.ExecuteAction', '{"action": "volumeup"}') end) | |
hs.hotkey.bind({'ctrl'}, 'left', function() sendKodiCommand('Left', 'Input.ExecuteAction', '{"action": "left"}') end) | |
hs.hotkey.bind({'ctrl'}, 'right', function() sendKodiCommand('Right', 'Input.ExecuteAction', '{"action": "right"}') end) | |
hs.hotkey.bind({'ctrl'}, 'up', function() sendKodiCommand('Up', 'Input.ExecuteAction', '{"action": "up"}') end) | |
hs.hotkey.bind({'ctrl'}, 'down', function() sendKodiCommand('Down', 'Input.ExecuteAction', '{"action": "down"}') end) | |
hs.hotkey.bind({'ctrl'}, 'return', function() sendKodiCommand('Select', 'Input.ExecuteAction', '{"action": "select"}') end) | |
hs.hotkey.bind({'ctrl'}, 'delete', function() sendKodiCommand('Back', 'Input.ExecuteAction', '{"action": "back"}') end) | |
hs.hotkey.bind({'ctrl'}, '+', function() sendKodiCommand('Context', 'Input.ExecuteAction', '{"action": "contextmenu"}') end) | |
hs.hotkey.bind({'ctrl'}, '´', function() hs.urlevent.openURL(kodihost .. '/#music') end) | |
end | |
-- Only bind Kodi keys after Hammerspoon loads if we are at home | |
if hs.wifi.currentNetwork('en0') == 'Do a flip!' then | |
bindKodiKeys() | |
end | |
-- Bind and unbind Kodi keys depending on wifi network | |
hs.wifi.watcher.new(function(watcher, message, interface) | |
print('Connected to wifi:') | |
print(hs.wifi.currentNetwork(interface)) | |
if hs.wifi.currentNetwork(interface) == 'Do a flip!' then | |
bindKodiKeys() | |
else | |
unbindKodiKeys() | |
end | |
end):start() | |
------------------------------ | |
-- F4 Modal Hotkey for Kodi -- | |
------------------------------ | |
function hotkeyF4:entered() | |
setMenuIcon(true, 'Kodi') | |
hs.alert.show('Kodi Active', 1) | |
end | |
function hotkeyF4:exited() | |
setMenuIcon(false) | |
hs.alert.closeAll() | |
end | |
hotkeyF4:bind('', 'escape', function() hotkeyF4:exit() end) | |
hotkeyF4:bind('', 'f3', function() hotkeyF4:exit() hotkeyF3:enter() end) | |
hotkeyF4:bind('', 'f4', function() hotkeyF4:exit() end) | |
hotkeyF4:bind('', 'f8', function() sendKodiCommand('Play/Pause', 'Player.PlayPause', '{"playerid": 0}') end) | |
hotkeyF4:bind('', 'f7', function() sendKodiCommand('Previous', 'Player.GoTo', '{"playerid": 0, "to": "previous"}') end) | |
hotkeyF4:bind('', 'f9', function() sendKodiCommand('Next', 'Player.GoTo', '{"playerid": 0, "to": "next"}') end) | |
hotkeyF4:bind('', 'f10', function() sendKodiCommand('Mute/Unmute', 'Input.ExecuteAction', '{"action": "mute"}') end) | |
hotkeyF4:bind('', 'f11', function() sendKodiCommand('Volume Down', 'Input.ExecuteAction', '{"action": "volumedown"}') end, nil, function() sendKodiCommand('Volume Down', 'Input.ExecuteAction', '{"action": "volumedown"}') end) | |
hotkeyF4:bind('', 'f12', function() sendKodiCommand('Volume Up', 'Input.ExecuteAction', '{"action": "volumeup"}') end, nil, function() sendKodiCommand('Volume Up', 'Input.ExecuteAction', '{"action": "volumeup"}') end) | |
hotkeyF4:bind('', 'left', function() sendKodiCommand('Left', 'Input.ExecuteAction', '{"action": "left"}') end) | |
hotkeyF4:bind('', 'right', function() sendKodiCommand('Right', 'Input.ExecuteAction', '{"action": "right"}') end) | |
hotkeyF4:bind('', 'up', function() sendKodiCommand('Up', 'Input.ExecuteAction', '{"action": "up"}') end) | |
hotkeyF4:bind('', 'down', function() sendKodiCommand('Down', 'Input.ExecuteAction', '{"action": "down"}') end) | |
hotkeyF4:bind('', 'return', function() sendKodiCommand('Select', 'Input.ExecuteAction', '{"action": "select"}') end) | |
hotkeyF4:bind('', 'delete', function() sendKodiCommand('Back', 'Input.ExecuteAction', '{"action": "back"}') end) | |
hotkeyF4:bind('', '+', function() sendKodiCommand('Back', 'Input.ExecuteAction', '{"action": "contextmenu"}') end) | |
hotkeyF4:bind('', 'c', function() sendKodiCommand('Back', 'Input.ExecuteAction', '{"action": "contextmenu"}') end) | |
hotkeyF4:bind('', '^', function() hs.alert.show('Toggling TV', 0.6) hs.http.get('http://pi.my.home:1880/bridge?topic=events/tv&value=2') end) | |
--------------------- | |
-- F3 Modal Hotkey -- | |
--------------------- | |
function hotkeyF3:entered() | |
setMenuIcon(true, 'F3') | |
hs.alert.show('F3 Active', 1) | |
end | |
function hotkeyF3:exited() | |
setMenuIcon(false) | |
hs.alert.closeAll() | |
end | |
hotkeyF3:bind('', 'escape', function() hotkeyF3:exit() end) | |
hotkeyF3:bind('', 'f3', function() hotkeyF3:exit() end) | |
hotkeyF3:bind('', 'f4', function() hotkeyF3:exit() hotkeyF4:enter() end) | |
hotkeyF3:bind('', 'pad1', function() sizeHotkeyUnit(size1, hotkeyF3) end) | |
hotkeyF3:bind('', 'pad2', function() sizeHotkeyUnit(size2, hotkeyF3) end) | |
hotkeyF3:bind('', 'pad3', function() sizeHotkeyUnit(size3, hotkeyF3) end) | |
hotkeyF3:bind('', 'pad4', function() sizeHotkeyUnit(size4, hotkeyF3) end) | |
hotkeyF3:bind('', 'pad5', function() sizeHotkeyUnit(size5, hotkeyF3) end) | |
hotkeyF3:bind('', 'pad6', function() sizeHotkeyUnit(size6, hotkeyF3) end) | |
hotkeyF3:bind('', 'pad7', function() sizeHotkeyUnit(size7, hotkeyF3) end) | |
hotkeyF3:bind('', 'pad8', function() sizeHotkeyUnit(size8, hotkeyF3) end) | |
hotkeyF3:bind('', 'pad9', function() sizeHotkeyUnit(size9, hotkeyF3) end) | |
hotkeyF3:bind('', '1', function() sizeHotkeyUnit(size1, hotkeyF3) end) | |
hotkeyF3:bind('', '2', function() sizeHotkeyUnit(size2, hotkeyF3) end) | |
hotkeyF3:bind('', '3', function() sizeHotkeyUnit(size3, hotkeyF3) end) | |
hotkeyF3:bind('', '4', function() sizeHotkeyUnit(size4, hotkeyF3) end) | |
hotkeyF3:bind('', '5', function() sizeHotkeyUnit(size5, hotkeyF3) end) | |
hotkeyF3:bind('', '6', function() sizeHotkeyUnit(size6, hotkeyF3) end) | |
hotkeyF3:bind('', '7', function() sizeHotkeyUnit(size7, hotkeyF3) end) | |
hotkeyF3:bind('', '8', function() sizeHotkeyUnit(size8, hotkeyF3) end) | |
hotkeyF3:bind('', '9', function() sizeHotkeyUnit(size9, hotkeyF3) end) | |
----------------------------------- | |
-- Display Layout Configurations -- | |
----------------------------------- | |
local laptopScreen = 'Color LCD' | |
local externalScreen = 'Thunderbolt Display' | |
local doubleLayout = { | |
{'Atom', nil, externalScreen, size5, nil, nil}, | |
{'Brackets', nil, externalScreen, sizeCenter, nil, nil}, | |
{nil, 'Inbox – [email protected]', externalScreen, size8, nil, nil}, | |
{nil, 'Issuu.com.*Calendar.*', externalScreen, size4, nil, nil}, | |
{'Slack', nil, externalScreen, size3, nil, nil}, | |
{'Spotify', nil, externalScreen, size9, nil, nil}, | |
{'Terminal', nil, laptopScreen, hs.layout.maximized, nil, nil}, | |
{'Google Chrome', nil, externalScreen, size1, nil, nil}, | |
{nil, '.*Developer.*Tools.*', externalScreen, hs.layout.right50, nil, nil}, | |
} | |
local laptopLayout = { | |
{nil, 'Issuu.com.*Calendar.*', laptopScreen, hs.layout.left50, nil, nil}, | |
{'Atom', nil, laptopScreen, hs.layout.left75, nil, nil}, | |
{'Google Chrome', nil, laptopScreen, hs.layout.maximized, nil, nil}, | |
{nil, '.*Developer.*Tools.*', laptopScreen, hs.layout.right70, nil, nil}, | |
{'Terminal', nil, laptopScreen, hs.layout.right50, nil, nil}, | |
{'Brackets', nil, externalScreen, sizeCenter, nil, nil}, | |
{'Slack', nil, externalScreen, sizeCenter, nil, nil}, | |
} | |
local function windowTitleComparator(windowTitle, searchPattern) | |
-- print(string.match(windowTitle, searchPattern) ~= nil, windowTitle, searchPattern) | |
return string.match(windowTitle, searchPattern) ~= nil | |
end | |
function onScreenChanged(forceRelayout) | |
hs.alert.show('Screen change detected') | |
-- avoid relayout if the number of screens didn't change (e.g. wakeup from sleep) | |
print(cachedNumScreens .. ' - ' .. #hs.screen.allScreens()) | |
if true or cachedNumScreens ~= #hs.screen.allScreens() or forceRelayout then | |
cachedNumScreens = #hs.screen.allScreens() | |
if #hs.screen.allScreens() == 2 then | |
hs.layout.apply(doubleLayout, windowTitleComparator) | |
elseif #hs.screen.allScreens() == 1 then | |
hs.layout.apply(laptopLayout, windowTitleComparator) | |
end | |
end | |
end | |
-- local screenWatcher = hs.screen.watcher.new(onScreenChanged) | |
-- screenWatcher:start() | |
hs.hotkey.bind({'ctrl'}, 'f3', function() onScreenChanged(true) end) | |
hs.hotkey.bind({'cmd'}, 'f9', function() | |
print('\n\n-- Applications --') | |
hs.fnutils.each(hs.application.runningApplications(), function(app) print(app:title()) end) | |
print('\n\n-- Window Titles --') | |
hs.fnutils.each(hs.window.visibleWindows(), function(app) print(app:title()) end) | |
end) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment