Initial commit of Hammerspoon config

This commit is contained in:
Franco Pellicciotti
2026-05-14 18:59:23 -04:00
commit 8a9f5c37ff
683 changed files with 180195 additions and 0 deletions
+163
View File
@@ -0,0 +1,163 @@
-- ~/.hammerspoon/NetworkMenu.lua
NetworkMenu = {}
NetworkMenu.menuBarItem = hs.menubar.new()
-- Global state management
automationEnabled = (automationEnabled == nil) and true or automationEnabled
local nasOnline = false
local lastInternalIP = "Offline"
local lastExternalIP = "Offline"
local vpnActive = false
-- 1. ASYNC STATUS UPDATE (Prevents hangs and crashes)
function updateStatus()
if not config then
print("NetworkMenu Error: Config not found")
return
end
-- A. Async NAS Ping
hs.task.new("/sbin/ping", function(code)
nasOnline = (code == 0)
end, {"-c", "1", "-t", "1", config.nasIP}):start()
-- B. Internal VPN IP (Fast local command)
local internalCmd = "ifconfig | grep -A 1 'utun' | grep 'inet ' | awk '{print $2}' | head -n 1"
local internalIP = hs.execute(internalCmd):gsub("%s+", "")
lastInternalIP = (internalIP == "" and "Offline" or internalIP)
vpnActive = (internalIP ~= "")
-- C. Async External IP
hs.task.new("/usr/bin/curl", function(exitCode, stdOut)
if exitCode == 0 and stdOut then
lastExternalIP = stdOut:gsub("%s+", "")
else
lastExternalIP = "Offline"
end
end, {"-s", "-m", "2", "icanhazip.com"}):start()
-- D. Update Menu Icon
local currentSSID = hs.wifi.currentNetwork()
if NetworkMenu.menuBarItem then
local automationIcon = automationEnabled and "" or "🔘"
local icon = (currentSSID == config.homeSSID) and "🏠" or "🌐"
if vpnActive then icon = icon .. "🛡️" end
if nasOnline then icon = icon .. "🟢" end
NetworkMenu.menuBarItem:setTitle(automationIcon .. icon)
end
end
-- 2. Aggressive Backgrounding for OpenVPN
function NetworkMenu.deepHideOpenVPN()
local count = 0
local hideTimer
hideTimer = hs.timer.doEvery(0.1, function()
local app = hs.application.get("OpenVPN Connect")
if app then
app:hide()
hs.applescript.applescript('tell application "System Events" to set visible of process "OpenVPN Connect" to false')
end
count = count + 1
if count > 30 then hideTimer:stop() end
end)
end
-- 3. Check Share Status (Local Filesystem Check)
local function getShareStatus()
local statusLines = {}
if not config or not config.shares then return statusLines end
for _, share in ipairs(config.shares) do
local path = "/Volumes/" .. share
local isMounted = hs.fs.attributes(path) ~= nil
local icon = isMounted and "🟢" or "⚪️"
table.insert(statusLines, { title = " " .. icon .. " " .. share, disabled = true })
end
return statusLines
end
-- 4. THE MENU UI
function NetworkMenu.buildMenu()
local currentSSID = hs.wifi.currentNetwork() or "Disconnected"
local isHome = config and (currentSSID == config.homeSSID)
local menu = {
{ title = (automationEnabled and "🟢" or "🔴") .. " Automation: " .. (automationEnabled and "Enabled" or "Disabled"),
fn = function()
automationEnabled = not automationEnabled
updateStatus()
hs.alert.show("Network Automation: " .. (automationEnabled and "ON" or "OFF"))
end
},
{ title = "-" },
{ title = "📍 Profile: " .. (isHome and "Home" or "Remote"), disabled = true },
{ title = " " .. (isHome and "🏠" or "🌐") .. " Location: " .. currentSSID, disabled = true },
{ title = "-" },
{ title = (vpnActive and "🔒" or "🔓") .. " VPN: " .. (vpnActive and "Connected" or "Disconnected"), disabled = true },
{ title = " 🆔 Int IP: " .. lastInternalIP, disabled = true },
{ title = " 🌍 Ext IP: " .. lastExternalIP, disabled = true },
{ title = (nasOnline and "" or "") .. " NAS Status: " .. (nasOnline and "Online" or "Offline"), disabled = true },
{ title = " 📂 Mounted Shares:", disabled = true },
}
local shares = getShareStatus()
for _, s in ipairs(shares) do table.insert(menu, s) end
table.insert(menu, { title = "-" })
table.insert(menu, { title = "⚡️ VPN Controls:", disabled = true })
table.insert(menu, { title = " 🔒 Connect VPN", fn = function()
if config then
hs.execute(string.format('"%s" --connect-shortcut=%s', config.openvpnPath, config.vpnProfileID))
NetworkMenu.deepHideOpenVPN()
hs.timer.doAfter(6, updateStatus)
end
end })
table.insert(menu, { title = " 🔓 Disconnect & Quit VPN", fn = function()
if config then
hs.execute(string.format('"%s" --disconnect-shortcut', config.openvpnPath))
hs.timer.doAfter(1, function()
local app = hs.application.get("OpenVPN Connect")
if app then app:kill() end
updateStatus()
end)
end
end })
table.insert(menu, { title = "-" })
table.insert(menu, { title = "📂 NAS Actions:", disabled = true })
table.insert(menu, { title = " ⬆️ Mount All Shares", fn = function()
if config then
for _, share in ipairs(config.shares) do
hs.applescript.applescript(string.format('mount volume "smb://%s/%s"', config.nasIP, share))
end
end
end })
table.insert(menu, { title = " ⬇️ Unmount All Shares", fn = function()
if config then
for _, share in ipairs(config.shares) do
hs.execute(string.format("diskutil unmount /Volumes/%s", share))
end
end
end })
table.insert(menu, { title = "-" })
table.insert(menu, { title = "🔄 Refresh Status", fn = updateStatus })
return menu
end
-- 5. INITIALIZATION
if NetworkMenu.menuBarItem then
NetworkMenu.menuBarItem:setMenu(NetworkMenu.buildMenu)
-- Small delay before first status update to ensure config is ready
hs.timer.doAfter(1, updateStatus)
end
-- Watchers
NetworkMenu.watcher = hs.wifi.watcher.new(updateStatus):start()
NetworkMenu.timer = hs.timer.doEvery(30, updateStatus)
print("NetworkMenu Module Loaded Successfully")
return NetworkMenu