Skip to content

Instantly share code, notes, and snippets.

@ttscoff
Last active June 25, 2021 17:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ttscoff/434fb51fb1098db966d8bf5720cbcfd2 to your computer and use it in GitHub Desktop.
Save ttscoff/434fb51fb1098db966d8bf5720cbcfd2 to your computer and use it in GitHub Desktop.
Tools for BetterTouchTool script widgets (touch bar and menu bar)
#!/usr/bin/env ruby
# frozen_string_literal: true
# Memory and CPU stats formatted to use in a BetterTouchTool touch bar script widget
# Also works in menu bar (Other Triggers), but be sure to select "Use mono space font"
require 'optparse'
# Settings
settings = {
chart_width: 8, # character width of chart, > 2
# Colors for level indicator, can be hex or rgb(a)
# max is cutoff percentage for indicator level
colors: [
{
max: 60, color: 'rgba(162, 191, 138, 1.00)'
},
{
max: 75, color: 'rgba(236, 204, 135, 1.00)'
},
{
max: 90, color: 'rgba(210, 135, 109, 1.00)'
},
{
max: 1000, color: 'rgba(197, 85, 98, 1.00)'
}
]
}
# defaults
options = {
percent: false,
mem_free: false,
averages: [1, 5, 15],
color_indicator: 1,
top: false,
background: false,
raw: false,
truncate: 0,
spacer: nil,
title: ''
}
parser = OptionParser.new do |opts|
opts.banner = "Usage: #{File.basename(__FILE__)} [options] [subcommand]"
opts.separator 'Subcommands:'
opts.separator ' cpu (default)'
opts.separator ' memory'
opts.separator ' ip [lan|wan (default)]'
opts.separator ' network [interface|location (default)]'
opts.separator ' doing'
opts.separator ''
opts.separator 'Options:'
opts.on('-a', '--averages AVERAGES',
'Comma separated list of CPU averages to display (1, 5, 15), default all') do |c|
options[:averages] = c.split(/,/).map(&:to_i)
end
opts.on('-c', '--color_from AVERAGE', 'Which CPU average to get level indicator color from (1, 5, 15)') do |c|
options[:color_indicator] = c.to_i
end
opts.on('-f', '--free', 'Display free memory instead of used memory') do
options[:mem_free] = true
end
opts.on('-i', '--indicator', 'Include background color indicating severity') do
options[:background] = true
end
opts.on('-p', '--percent', 'Display percentages instead of bar graph') do
options[:percent] = true
end
opts.on('-t', '--title TITLE', 'Include title in output, [TITLE][CHART/%]') do |c|
options[:title] = c
end
opts.on('--truncate LENGTH', 'Truncate output') do |c|
options[:truncate] = c.to_i
end
opts.on('--top', 'If CPU is maxed, include top process in output') do
options[:top] = true
end
opts.on('--no-btt', 'Output raw text without BetterTouchTool formatting') do
options[:raw] = true
end
opts.on('--empty CHARACTER', 'Include character when output is empty to prevent widget from collapsing') do |c|
options[:spacer] = c
end
opts.on('-h', '--help', 'Display this screen') do
puts opts
exit
end
end
parser.parse!
def rgba_to_btt(color)
elements = color.match(/rgba?\((?<r>\d+), *(?<g>\d+), *(?<b>\d+)(?:, *(?<a>[\d.]+))?\)/)
alpha = elements['a'] ? 255 * elements['a'].to_f : 255
%(#{elements['r']},#{elements['g']},#{elements['b']},#{alpha.to_i})
end
def hex_to_btt(color)
rgb = color.match(/^#?(..)(..)(..)$/).captures.map(&:hex)
"#{rgb.join(',')},255"
end
def color_to_btt(color)
color = color.strip.gsub(/ +/, '').downcase
case color
when /^rgba?\(\d+,\d+,\d+(,(0?\.\d+|1(\.0+)?))?\)$/
rgba_to_btt(color)
when /^#?[a-f0-9]{6}$/
hex_to_btt(color)
else
'25,25,25,255'
end
end
case ARGV[0]
when /^net/
options[:background] = false
chart = case ARGV[1]
when /^i/ # Interface
`osascript -e 'tell app "System Events" to tell current location of network preferences to get name of first service whose active is true'`.strip
else # Location
`osascript -e 'tell app "System Events" to get name of current location of network preferences'`.strip
end
when /^ip/
options[:background] = false
chart = case ARGV[1]
when /^lan/ # LAN IP
`ifconfig | grep "inet " | awk '{ print $2 }' | tail -n 1`.strip
else # WAN IP
`curl -SsL icanhazip.com`.strip
end
when /^doing/
## Add in .doingrc (`doing config`)
# views:
# btt:
# section: Currently
# count: 1
# order: desc
# template: "%title"
# tags_bool: NONE
# tags: done
options[:background] = false
chart = `/usr/local/bin/doing view btt`.strip
when /^m/
mem_free = `memory_pressure | tail -n 1 | awk '{ print $NF; }' | tr -d '%'`.to_i
mem_used = 100 - mem_free
memory = options[:mem_free] ? mem_free : mem_used
if options[:percent]
chart = "#{memory}%"
else
unit = (settings[:chart_width].to_f / 100)
chart_arr = Array.new(settings[:chart_width], '░')
chart_arr.fill('█', 0, (unit * memory).to_i)
chart = chart_arr.join('')
end
color = ''
settings[:colors].each do |c|
if mem_used <= c[:max]
color = color_to_btt(c[:color])
break
end
end
else
cores = `sysctl -n hw.ncpu`.to_i
all_loads = `sysctl -n vm.loadavg`.split(/ /)[1..3]
loads = []
loads.push(all_loads[0]) if options[:averages].include?(1)
loads.push(all_loads[1]) if options[:averages].include?(5)
loads.push(all_loads[2]) if options[:averages].include?(15)
indicator = case options[:color_indicator]
when 5
all_loads[1]
when 15
all_loads[2]
else
all_loads[0]
end
curr_load = ((indicator.to_f / cores) * 100).to_i
curr_load = 100 if curr_load > 100
if options[:percent]
chart = loads.map { |ld| "#{((ld.to_f / cores) * 100).to_i}%" }.join('|')
else
unit = (settings[:chart_width].to_f / 100)
chart_arr = Array.new(settings[:chart_width], '░')
fills = %w[ ].reverse.slice(0, loads.length).reverse
loads.reverse.each_with_index do |ld, idx|
this_load = ((ld.to_f / cores) * 100)
this_load = 100 if this_load > 100
chart_arr.fill(fills[idx], 0, (unit * this_load).to_i)
end
chart = chart_arr.join('')
end
color = ''
if options[:background]
settings[:colors].each do |c|
if curr_load <= c[:max]
color = color_to_btt(c[:color])
break
end
end
end
if options[:top] && curr_load >= 100
top_process = `ps -arcwwwxo "command"|iconv -c -f utf-8 -t ascii|head -n 2|tail -n 1`.strip
chart += " (#{top_process})"
end
end
chart = chart[0..options[:truncate]] if options[:truncate].positive? && chart.length > options[:truncate]
chart = options[:spacer] if chart.empty? && options[:spacer]
chart = "#{options[:title]}#{chart}"
if options[:raw]
print chart
else
out = %({\"text\": \"#{chart}\")
out += options[:background] ? %(,\"background_color\":\"#{color}\"}) : '}'
print out
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment