二改模版笔记
自动重新加载HS
lua
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
myWatcher = hs.pathwatcher.new(os.getenv("HOME") .. "/.hammerspoon/", reloadConfig):start()
hs.alert.show("Config loaded")
调节音量
lua
function changeVolume(diff)
return function()
local current = hs.audiodevice.defaultOutputDevice():volume()
local new = math.min(100, math.max(0, math.floor(current + diff)))
if new > 0 then
hs.audiodevice.defaultOutputDevice():setMuted(false)
end
hs.alert.closeAll(0.0)
hs.alert.show("Volume " .. new .. "%", {}, 0.5)
hs.audiodevice.defaultOutputDevice():setVolume(new)
end
end
hs.hotkey.bind({"shift","ctrl",'command'}, 'Down', changeVolume(-3))
hs.hotkey.bind({"shift","ctrl",'command'}, 'Up', changeVolume(3))
显示七日天气
lua
local urlApi = 'http://v1.yiketianqi.com/free/api'
local menubar = hs.menubar.new()
local menuData = {}
local weaEmoji = {
lei = '🌩️',
qing = '☀️',
shachen = '😷',
wu = '🌫',
xue = '❄️',
yu = '🌧',
yujiaxue = '🌨',
yun = '☁️',
zhenyu = '🌧',
yin = '⛅️',
default = ''
}
function updateMenubar()
menubar:setTooltip("Weather Info")
menubar:setMenu(menuData)
end
function getWeather()
hs.http.doAsyncRequest(urlApi, "GET", nil,nil, function(code, body, htable)
if code ~= 200 then
print('get weather error:'..code)
return
end
rawjson = hs.json.decode(body)
city = rawjson.city
menuData = {}
for k, v in pairs(rawjson.data) do
if k == 1 then
menubar:setTitle(weaEmoji[v.wea_img])
if v.win_speed == "<3级" then
titlestr = string.format("%s %s %s 🌡️%s-%s°C", city,weaEmoji[v.wea_img], v.wea, v.tem_night, v.tem_day)
else
titlestr = string.format("%s %s 🌡️%s-%s°C 💨%s %s %s", city,weaEmoji[v.wea_img],v.tem_night, v.tem_day, v.win_speed, v.win, v.wea)
end
item = { title = titlestr }
table.insert(menuData, item)
table.insert(menuData, {title = '-'})
else
if v.win_speed == "<3级" then
titlestr = string.format("%s %s 🌡️%s-%s°C %s", v.date, weaEmoji[v.wea_img], v.tem_night, v.tem_day, v.wea)
else
titlestr = string.format("%s %s 🌡️%s-%s°C 💨%s %s %s", v.date, weaEmoji[v.wea_img],v.tem_night, v.tem_day, v.win_speed, v.win, v.wea)
end
item = { title = titlestr }
table.insert(menuData, item)
end
end
updateMenubar()
end)
end
menubar:setTitle('👀')
getWeather()
updateMenubar()
hs.timer.doEvery(3000, getWeather)
剪贴板
其中设置这个为true
的话,可以直接插入
local pasteOnSelect = false -- Auto-type on click
lua
--[[
From https://github.com/victorso/.hammerspoon/blob/master/tools/clipboard.lua
Modified by Diego Zamboni
]]--
-- Feel free to change those settings
local frequency = 0.8 -- Speed in seconds to check for clipboard changes. If you check too frequently, you will loose performance, if you check sparsely you will loose copies
local hist_size = 100 -- How many items to keep on history
local label_length = 70 -- How wide (in characters) the dropdown menu should be. Copies larger than this will have their label truncated and end with "..." (unicode for elipsis ...)
local honor_clearcontent = false --asmagill request. If any application clears the pasteboard, we also remove it from the history https://groups.google.com/d/msg/hammerspoon/skEeypZHOmM/Tg8QnEj_N68J
local pasteOnSelect = false -- Auto-type on click
-- Don't change anything bellow this line
local jumpcut = hs.menubar.new()
jumpcut:setTooltip("Clipboard history")
local pasteboard = require("hs.pasteboard") -- http://www.hammerspoon.org/docs/hs.pasteboard.html
local settings = require("hs.settings") -- http://www.hammerspoon.org/docs/hs.settings.html
local last_change = pasteboard.changeCount() -- displays how many times the pasteboard owner has changed // Indicates a new copy has been made
--Array to store the clipboard history
local clipboard_history = settings.get("so.victor.hs.jumpcut") or {} --If no history is saved on the system, create an empty history
function subStringUTF8(str, startIndex, endIndex)
if startIndex < 0 then
startIndex = subStringGetTotalIndex(str) + startIndex + 1
end
if endIndex ~= nil and endIndex < 0 then
endIndex = subStringGetTotalIndex(str) + endIndex + 1
end
if endIndex == nil then
return string.sub(str, subStringGetTrueIndex(str, startIndex))
else
return string.sub(str, subStringGetTrueIndex(str, startIndex), subStringGetTrueIndex(str, endIndex + 1) - 1)
end
end
--返回当前截取字符串正确下标
function subStringGetTrueIndex(str, index)
local curIndex = 0
local i = 1
local lastCount = 1
repeat
lastCount = subStringGetByteCount(str, i)
i = i + lastCount
curIndex = curIndex + 1
until(curIndex >= index)
return i - lastCount
end
--返回当前字符实际占用的字符数
function subStringGetByteCount(str, index)
local curByte = string.byte(str, index)
local byteCount = 1
if curByte == nil then
byteCount = 0
elseif curByte > 0 and curByte <= 127 then
byteCount = 1
elseif curByte>=192 and curByte<=223 then
byteCount = 2
elseif curByte>=224 and curByte<=239 then
byteCount = 3
elseif curByte>=240 and curByte<=247 then
byteCount = 4
end
return byteCount
end
-- Append a history counter to the menu
function setTitle()
if (#clipboard_history == 0) then
jumpcut:setTitle("✂") -- Unicode magic
else
jumpcut:setTitle("✂") -- Unicode magic
-- jumpcut:setTitle("✂ ("..#clipboard_history..")") -- updates the menu counter
end
end
function putOnPaste(string,key)
if (pasteOnSelect) then
hs.eventtap.keyStrokes(string)
pasteboard.setContents(string)
last_change = pasteboard.changeCount()
else
if (key.alt == true) then -- If the option/alt key is active when clicking on the menu, perform a "direct paste", without changing the clipboard
hs.eventtap.keyStrokes(string) -- Defeating paste blocking http://www.hammerspoon.org/go/#pasteblock
else
pasteboard.setContents(string)
last_change = pasteboard.changeCount() -- Updates last_change to prevent item duplication when putting on paste
end
end
end
-- Clears the clipboard and history
function clearAll()
pasteboard.clearContents()
clipboard_history = {}
settings.set("so.victor.hs.jumpcut",clipboard_history)
now = pasteboard.changeCount()
setTitle()
end
-- Clears the last added to the history
function clearLastItem()
table.remove(clipboard_history,#clipboard_history)
settings.set("so.victor.hs.jumpcut",clipboard_history)
now = pasteboard.changeCount()
setTitle()
end
function pasteboardToClipboard(item)
-- Loop to enforce limit on qty of elements in history. Removes the oldest items
while (#clipboard_history >= hist_size) do
table.remove(clipboard_history,1)
end
table.insert(clipboard_history, item)
settings.set("so.victor.hs.jumpcut",clipboard_history) -- updates the saved history
setTitle() -- updates the menu counter
end
-- Dynamic menu by cmsj https://github.com/Hammerspoon/hammerspoon/issues/61#issuecomment-64826257
populateMenu = function(key)
setTitle() -- Update the counter every time the menu is refreshed
menuData = {}
if (#clipboard_history == 0) then
table.insert(menuData, {title="None", disabled = true}) -- If the history is empty, display "None"
else
for k,v in pairs(clipboard_history) do
if (string.len(v) > label_length) then
table.insert(menuData,1, {title=subStringUTF8(v,0,label_length).."...", fn = function() putOnPaste(v,key) end }) -- Truncate long strings
else
table.insert(menuData,1, {title=v, fn = function() putOnPaste(v,key) end })
end -- end if else
end-- end for
end-- end if else
-- footer
table.insert(menuData, {title="-"})
table.insert(menuData, {title="Clear All", fn = function() clearAll() end })
if (key.alt == true or pasteOnSelect) then
table.insert(menuData, {title="Direct Paste Mode ✍", disabled=true})
end
return menuData
end
-- If the pasteboard owner has changed, we add the current item to our history and update the counter.
function storeCopy()
now = pasteboard.changeCount()
if (now > last_change) then
current_clipboard = pasteboard.getContents()
-- asmagill requested this feature. It prevents the history from keeping items removed by password managers
if (current_clipboard == nil and honor_clearcontent) then
clearLastItem()
else
pasteboardToClipboard(current_clipboard)
end
last_change = now
end
end
--Checks for changes on the pasteboard. Is it possible to replace with eventtap?
timer = hs.timer.new(frequency, storeCopy)
timer:start()
setTitle() --Avoid wrong title if the user already has something on his saved history
jumpcut:setMenu(populateMenu)
hs.hotkey.bind({"cmd", "shift"}, "v", function() jumpcut:popupMenu(hs.mouse.getAbsolutePosition()) end)
系统信息显示
lua
--- 显示系统信息
--- 可显示CPU\内存\硬盘\网络等实时信息
--- Created by sugood(https://github.com/sugood).
--- DateTime: 2022/01/14 22:00
---
local menubaritem = hs.menubar.new()
local menuData = {}
-- ipv4Interface ipv6 Interface
local interface = hs.network.primaryInterfaces()
-- 该对象用于存储全局变量,避免每次获取速度都创建新的局部变量
local obj = {}
function init()
if interface then
local interface_detail = hs.network.interfaceDetails(interface)
if interface_detail.IPv4 then
local ipv4 = interface_detail.IPv4.Addresses[1]
table.insert(menuData, {
title = "IPv4:" .. ipv4,
tooltip = "Copy Ipv4 to clipboard",
fn = function()
hs.pasteboard.setContents(ipv4)
end
})
end
local mac = hs.execute('ifconfig ' .. interface .. ' | grep ether | awk \'{print $2}\'')
table.insert(menuData, {
title = 'MAC:' .. mac,
tooltip = 'Copy MAC to clipboard',
fn = function()
hs.pasteboard.setContents(mac)
end
})
obj.last_down = hs.execute('netstat -ibn | grep -e ' .. interface .. ' -m 1 | awk \'{print $7}\'')
obj.last_up = hs.execute('netstat -ibn | grep -e ' .. interface .. ' -m 1 | awk \'{print $10}\'')
else
obj.last_down = 0
obj.last_down = 0
end
local date=os.date("%Y-%m-%d %a");
table.insert(menuData, {
title = 'Date: '..date,
tooltip = 'Copy Now DateTime',
fn = function()
hs.pasteboard.setContents(os.date("%Y-%m-%d %H:%M:%S"))
end
})
table.insert(menuData, {
title = '打开:监 视 器 (⇧⌃A)',
tooltip = 'Show Activity Monitor',
fn = function()
bindActivityMonitorKey()
end
})
table.insert(menuData, {
title = '打开:磁盘工具 (⇧⌃D)',
tooltip = 'Show Disk Utility',
fn = function()
bindDiskKey()
end
})
table.insert(menuData, {
title = '打开:系统日历 (⇧⌃C)',
tooltip = 'Show calendar',
fn = function()
bindCalendarKey()
end
})
menubaritem:setMenu(menuData)
end
function scan()
if interface then
obj.current_down = hs.execute('netstat -ibn | grep -e ' .. interface .. ' -m 1 | awk \'{print $7}\'')
obj.current_up = hs.execute('netstat -ibn | grep -e ' .. interface .. ' -m 1 | awk \'{print $10}\'')
else
obj.current_down = 0
obj.current_up = 0
end
obj.cpu_used = getCpu()
obj.disk_used = getRootVolumes()
obj.mem_used = getVmStats()
obj.down_bytes = obj.current_down - obj.last_down
obj.up_bytes = obj.current_up - obj.last_up
obj.down_speed = format_speed(obj.down_bytes)
obj.up_speed = format_speed(obj.up_bytes)
obj.display_text = hs.styledtext.new('▲ ' .. obj.up_speed .. '\n'..'▼ ' .. obj.down_speed , {font={size=9}, color={hex='#FFFFFF'}, paragraphStyle={alignment="left", maximumLineHeight=18}})
obj.display_disk_text = hs.styledtext.new(obj.disk_used ..'\n'.. 'SSD ' , {font={size=9}, color={hex='#FFFFFF'}, paragraphStyle={alignment="left", maximumLineHeight=18}})
obj.display_mem_text = hs.styledtext.new(obj.mem_used ..'\n'.. 'MEM ' , {font={size=9}, color={hex='#FFFFFF'}, paragraphStyle={alignment="left", maximumLineHeight=18}})
obj.display_cpu_text = hs.styledtext.new(obj.cpu_used ..'\n'.. 'CPU ' , {font={size=9}, color={hex='#FFFFFF'}, paragraphStyle={alignment="left", maximumLineHeight=18}})
obj.last_down = obj.current_down
obj.last_up = obj.current_up
local canvas = hs.canvas.new{x = 0, y = 0, h = 24, w = 30+30+30+60}
-- canvas[1] = {type = 'text', text = obj.display_text}
canvas:appendElements({
type = "text",
text = obj.display_cpu_text,
-- withShadow = true,
trackMouseEnterExit = true,
},{
type = "text",
text = obj.display_disk_text,
-- withShadow = true,
trackMouseEnterExit = true,
frame = { x = 30, y = "0", h = "1", w = "1", }
},{
type = "text",
text = obj.display_mem_text,
-- withShadow = true,
trackMouseEnterExit = true,
frame = { x = 60, y = "0", h = "1", w = "1", }
},{
type = "text",
text = obj.display_text,
-- withShadow = true,
trackMouseEnterExit = true,
frame = { x = 90, y = "0", h = "1", w = "1", }
})
menubaritem:setIcon(canvas:imageFromCanvas())
canvas:delete()
canvas = nil
end
function format_speed(bytes)
-- 单位 Byte/s
if bytes < 1024 then
return string.format('%6.0f', bytes) .. ' B/s'
else
-- 单位 KB/s
if bytes < 1048576 then
-- 因为是每两秒刷新一次,所以要除以 (1024 * 2)
return string.format('%6.1f', bytes / 2048) .. ' KB/s'
-- 单位 MB/s
else
-- 除以 (1024 * 1024 * 2)
return string.format('%6.1f', bytes / 2097152) .. ' MB/s'
end
end
end
function getCpu()
local data = hs.host.cpuUsage()
local cpu = (data["overall"]["active"])
return formatPercent(cpu)
end
function getVmStats()
local vmStats = hs.host.vmStat()
-- --1024^2
-- local megDiv = 1048576
-- local megMulti = vmStats.pageSize / megDiv
-- local totalMegs = vmStats.memSize / megDiv --总内存
-- local megsCached = vmStats.fileBackedPages * megMulti --缓存内存
-- local freeMegs = vmStats.pagesFree * megMulti --空闲内存
-- --第一种方法使用 APP内存+联动内存+被压缩内存 = 已使用内存
-- --local megsUsed = vmStats.pagesWiredDown * megMulti -- 联动内存
-- --megsUsed = megsUsed + vmStats.pagesUsedByVMCompressor * megMulti -- 被压缩内存
-- --megsUsed = megsUsed + (vmStats.pagesActive +vmStats.pagesSpeculative)* megMulti -- APP内存
-- --第二种方法使用 总内存-缓存内存-空闲内存 = 已使用内存
-- local megsUsed = totalMegs - megsCached - freeMegs
--第三种方法,由于部分设备pageSize获取不正确,所以只能通过已使用页数+缓存页数+空闲页数计算总页数
local megsUsed = vmStats.pagesWiredDown -- 联动内存
megsUsed = megsUsed + vmStats.pagesUsedByVMCompressor -- 被压缩内存
megsUsed = megsUsed + vmStats.pagesActive +vmStats.pagesSpeculative -- APP内存
local megsCached = vmStats.fileBackedPages --缓存内存
local freeMegs = vmStats.pagesFree --空闲内存
local totalMegs = megsUsed + megsCached + freeMegs
local usedMem = megsUsed/totalMegs * 100
return formatPercent(usedMem)
end
function getRootVolumes()
local vols = hs.fs.volume.allVolumes()
for key, vol in pairs(vols) do
local size = vol.NSURLVolumeTotalCapacityKey
local free = vol.NSURLVolumeAvailableCapacityKey
local usedSSD = (1-free/size) * 100
if ( string.find(vol.NSURLVolumeNameKey,'Macintosh') ~= nil) then
return formatPercent(usedSSD)
end
end
return ' 0%'
end
function formatPercent(percent)
if ( percent <= 0 ) then
return " 1%"
elseif ( percent < 10 ) then
return " " .. string.format("%.f", percent) .. "%"
elseif (percent > 99 )then
return "100%"
else
return string.format("%.f", percent) .. "%"
end
end
local setSysInfo= function()
-- if config ~=nil and config[1].showSysInfo ~= 'on' then
if 1 then
if(menuBarItem ~= nil and menuBarItem:isInMenuBar() == false) then
return
end
if (menuBarItem == nil) then
print("设置状态栏:系统信息")
menuBarItem= hs.menubar.new()
elseif (menuBarItem:isInMenuBar() == false) then
menuBarItem:delete()
menuBarItem= hs.menubar.new()
end
init()
scan()
if obj.timer then
obj.timer:stop()
obj.timer = nil
end
-- 三秒刷新一次
obj.timer = hs.timer.doEvery(3, scan):start()
end
end
function initData()
setSysInfo()
--监听系统信息开关的状态,判断是否要重置
hs.timer.doEvery(1, setSysInfo)
end
-- 初始化
initData()