Created
January 6, 2018 17:02
-
-
Save JeremyOttley/f5539b1256a3063e26c33f429bf80e25 to your computer and use it in GitHub Desktop.
jruby status bar
This file contains 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
#!/usr/bin/env jruby | |
# coding: utf-8 | |
# $Id: statusbar.rb,v 1.18 2016/10/07 22:27:26 jashank Exp $ | |
require 'cgi' | |
require 'date' | |
require 'dbus' | |
require 'json' | |
require 'pg' | |
require 'pmap' | |
$colors = { | |
fg: "#ffffff", | |
bg: "#000000", | |
red: "#ff0000", | |
green: "#00ff00", | |
} | |
$sym = { | |
lock: "\u{f023}", | |
info_circle: "\u{f05a}", | |
unlock: "\u{f09c}", | |
rss: "\u{f09e}", | |
tachometer: "\u{f0e4}", | |
sitemap: "\u{f0e8}", | |
caret_square_o_down: "\u{f150}", | |
caret_square_o_up: "\u{f151}", | |
plug: "\u{f1e6}", | |
wifi: "\u{f1eb}", | |
battery_4: "\u{f240}", | |
battery_3: "\u{f241}", | |
battery_2: "\u{f242}", | |
battery_1: "\u{f243}", | |
battery_0: "\u{f244}", | |
pause_circle: "\u{f28b}", | |
bluetooth: "\u{f293}", | |
} | |
$sym.keys.each {|k| $sym[k] = '<span face="FontAwesome">' + $sym[k] + '</span>'} | |
@order = [ | |
:taskmgr, | |
:ifwatch_bond0, | |
:ifwatch_enp0s25, | |
:wlwatch_wlp3s0, | |
:ifwatch_bnep0, | |
:ifwatch_tun0, | |
:battery, | |
:thermal, | |
:cpufreq, | |
:loadavg, | |
:date | |
] | |
# full_text: The full_text will be displayed by i3bar on the status | |
# line. This is the only required key. | |
# | |
# short_text: Where appropriate, the short_text (string) entry should | |
# also be provided. | |
# | |
# color: To make the current state of the information easy to spot, | |
# colors can be used. Colors are specified in hex (like in HTML), | |
# starting with a leading hash sign. For example, #ff0000 means red. | |
# | |
# background: Overrides the background color for this block. | |
# | |
# border: Overrides the border color for this block. | |
# | |
# min_width: The minimum width (in pixels) of the block; the value can | |
# also be a string, and the width of the text given determines the | |
# minimum width of the block. | |
# | |
# align: Align to the center, right or left (default) of the block. | |
# | |
# name and instance: Every block should have a unique name (string) | |
# entry so that it can be easily identified in scripts which process | |
# the output. i3bar completely ignores the name and instance | |
# fields. Make sure to also specify an instance (string) entry where | |
# appropriate. For example, the user can have multiple disk space | |
# blocks for multiple mount points. | |
# | |
# urgent: A boolean: whether the current value is urgent. | |
# | |
# separator: A boolean which specifies whether a separator line should | |
# be drawn after this block. The default is true, meaning the | |
# separator line will be drawn. Note that if you disable the separator | |
# line, there will still be a gap after the block, unless you also use | |
# separator_block_width. | |
# | |
# separator_block_width: The amount of pixels to leave blank after the | |
# block. In the middle of this gap, a separator line will be drawn | |
# unless separator is disabled. Normally, you want to set this to an | |
# odd value (the default is 9 pixels), since the separator line is | |
# drawn in the middle. | |
# | |
# markup: A string that indicates how the text of the block should be | |
# parsed. Set to "pango" to use Pango markup. Set to "none" to not use | |
# any markup (default). | |
$blocks = { | |
date: { | |
interval: 1, | |
proc: lambda { | |
s = DateTime.now.strftime("%Y-%m-%d %H:%M:%S"); | |
{ | |
name: "date", | |
full_text: s, | |
short_text: s[-8..-1], | |
color: $colors[:fg], | |
markup: "pango", | |
} | |
} | |
}, | |
taskmgr: { | |
interval: 30, | |
proc: lambda { | |
res = $pg_tock.exec "select coalesce(current_task(), '[no current task]')" | |
{ | |
name: "taskmgr", | |
full_text: res.getvalue(0, 0), | |
color: $colors[:fg], | |
markup: "pango", | |
} | |
} | |
}, | |
ifwatch_bond0: { | |
interval: 5, | |
proc: lambda { | |
begin | |
ipl = `ip link show bond0 2>/dev/null` | |
status = !(ipl =~ /state UP/).nil? | |
ipa = "" | |
if status | |
ipa = `ip addr show bond0 2>/dev/null` | |
.split("\n") | |
.grep(/inet /) | |
.map {|i| i.strip } | |
if ipa.nil? or ipa.empty? | |
ipa = " (!v4)" | |
else | |
ipa = " " + ipa.first.split(' ')[1] | |
end | |
end | |
{ | |
name: "ifwatch_bond0", | |
full_text: $sym[:sitemap] + ipa, | |
color: $colors[status ? :green : :red], | |
markup: "pango", | |
} | |
rescue | |
{ | |
name: "ifwatch_bond0", | |
full_text: $sym[:sitemap], | |
color: $colors[:red], | |
markup: "pango", | |
} | |
end | |
} | |
}, | |
ifwatch_enp0s25: { | |
interval: 5, | |
proc: lambda { | |
begin | |
ipl = `ip link show enp0s25 2>/dev/null` | |
status = !(ipl =~ /state UP/).nil? | |
{ | |
name: "ifwatch_enp0s25", | |
full_text: $sym[status ? :caret_square_o_up : :caret_square_o_down], | |
color: $colors[status ? :green : :red], | |
markup: "pango", | |
} | |
rescue | |
{ | |
name: "ifwatch_enp0s25", | |
full_text: $sym[:caret_square_o_down], | |
color: $colors[:red], | |
markup: "pango", | |
} | |
end | |
} | |
}, | |
wlwatch_wlp3s0: { | |
interval: 5, | |
proc: lambda { | |
begin | |
iwi = `iwgetid -r wlp3s0`.chomp | |
status = (iwi != "") | |
iwi = CGI.escapeHTML iwi | |
{ | |
name: "wlwatch_wlp3s0", | |
full_text: $sym[:wifi] + (status ? " #{iwi}" : ""), | |
color: $colors[status ? :green : :red], | |
markup: "pango", | |
} | |
rescue | |
{ | |
name: "wlwatch_wlp3s0", | |
full_text: $sym[:wifi], | |
color: $colors[:red], | |
markup: "pango", | |
} | |
end | |
} | |
}, | |
ifwatch_bnep0: { | |
interval: 5, | |
proc: lambda { | |
begin | |
ipl = `ip link show bnep0 2>/dev/null` | |
status = !(ipl =~ /state UP/).nil? | |
{ | |
name: "ifwatch_bnep0", | |
full_text: $sym[:bluetooth], | |
color: $colors[status ? :green : :red], | |
markup: "pango", | |
} | |
rescue | |
{ | |
name: "ifwatch_bnep0", | |
full_text: $sym[:bluetooth], | |
color: $colors[:red], | |
markup: "pango", | |
} | |
end | |
} | |
}, | |
ifwatch_tun0: { | |
interval: 5, | |
proc: lambda { | |
begin | |
ipl = `ip link show tun0 2>/dev/null` | |
status = !(ipl =~ /,UP,/).nil? | |
{ | |
name: "ifwatch_tun0", | |
full_text: $sym[status ? :lock : :unlock], | |
color: $colors[status ? :green : :red], | |
markup: "pango", | |
} | |
rescue | |
{ | |
name: "ifwatch_tun0", | |
full_text: $sym[:unlock], | |
color: $colors[:red], | |
markup: "pango", | |
} | |
end | |
} | |
}, | |
ifwatch_tap0: { | |
interval: 5, | |
proc: lambda { | |
begin | |
ipl = `ip link show tap0 2>/dev/null` | |
status = !(ipl =~ /,UP,/).nil? | |
{ | |
name: "ifwatch_tap0", | |
full_text: $sym[status ? :lock : :unlock], | |
color: $colors[status ? :green : :red], | |
markup: "pango", | |
} | |
rescue | |
{ | |
name: "ifwatch_tap0", | |
full_text: $sym[:unlock], | |
color: $colors[:red], | |
markup: "pango", | |
} | |
end | |
} | |
}, | |
battery: { | |
interval: 30, | |
proc: lambda { | |
# https://upower.freedesktop.org/docs/Device.html#Device:State | |
acad = [1,4].include? $upower_ifa["State"] | |
pc = $upower_ifa["Energy"] / $upower_ifa["EnergyFull"] | |
etime = $upower_ifa["TimeToEmpty"] | |
ftime = $upower_ifa["TimeToFull"] | |
sym = | |
if acad then :plug | |
elsif 0.8 < pc and pc <= 1.0 then :battery_4 | |
elsif 0.6 < pc and pc <= 0.8 then :battery_3 | |
elsif 0.4 < pc and pc <= 0.6 then :battery_2 | |
elsif 0.2 < pc and pc <= 0.4 then :battery_1 | |
else :battery_0 end | |
col = | |
if acad then :fg | |
elsif sym == :battery_0 then :red | |
else :green end | |
timestr = | |
if acad | |
sprintf "%3d:%02d", (ftime/3660), (ftime%3600)/60 | |
else | |
sprintf "%3d:%02d", (etime/3600), (etime%3600)/60 | |
end | |
{ | |
name: "battery", | |
full_text: sprintf( | |
"%s %.01f%% (%s)", $sym[sym], pc*100.0, timestr.strip), | |
color: $colors[col], | |
markup: "pango", | |
} | |
} | |
}, | |
thermal: { | |
interval: 5, | |
proc: lambda { | |
therms = [0, 1].pmap {|i| | |
File.open ("/sys/devices/virtual/thermal/thermal_zone#{i}/temp") {|f| | |
f.to_a.first.chomp.to_f / 1000 } } | |
temp = therms.reduce(:+) / therms.length | |
fanstat = File.open ("/proc/acpi/ibm/fan") {|f| | |
f.to_a.map {|s| s.chomp } } | |
rpm = fanstat.grep(/^speed/).first.split("\t")[2].to_i | |
{ | |
name: "thermal", | |
full_text: sprintf("%.01f°C (%d R)", temp, rpm), | |
color: $colors[temp >= 60.0 ? :red : :green], | |
markup: "pango", | |
} | |
} | |
}, | |
cpufreq: { | |
interval: 2, | |
proc: lambda { | |
raw_freqs = (0..3).pmap {|i| | |
File.open ("/sys/devices/system/cpu/cpu#{i}/cpufreq/scaling_cur_freq") {|f| | |
f.to_a.first.chomp.to_i } } | |
raw_freq = raw_freqs.reduce(:+) / raw_freqs.length | |
freq_units = | |
if raw_freq >= 1e+6 then [ raw_freq.to_f / 1e+6, "G" ] | |
elsif raw_freq >= 1e+3 then [ raw_freq.to_f / 1e+3, "M" ] | |
else [ raw_freq, "k" ] end | |
freq, units = freq_units | |
str = (units != "G") ? | |
sprintf("#{$sym[:tachometer]} %d%s", freq, units) : | |
sprintf("#{$sym[:tachometer]} %.1f%s", freq, units) | |
{ | |
name: "cpufreq", | |
full_text: str, | |
min_width: "#{$sym[:tachometer]} 888M", | |
align: "center", | |
color: $colors[raw_freq >= 2.6e+6 ? :red : :fg], | |
markup: "pango", | |
} | |
} | |
}, | |
loadavg: { | |
interval: 5, | |
proc: lambda { | |
lavg = File.open ("/proc/loadavg") {|f| | |
f.to_a.first.chomp.split(' ')[0..2].map {|s| s.to_f} } | |
one, five, fifteen = lavg | |
{ | |
name: "loadavg", | |
full_text: sprintf("%.02f:%.02f:%.02f", one, five, fifteen), | |
color: $colors[one >= 4 ? :red : :green], | |
markup: "pango", | |
} | |
} | |
}, | |
} | |
$pg_tock = PG.connect dbname: "tock" | |
$dbus_sys = DBus.system_bus | |
$upower_svc = $dbus_sys["org.freedesktop.UPower"] | |
$upower_obj = $upower_svc.object "/org/freedesktop/UPower/devices/battery_BAT0" | |
$upower_obj.introspect | |
$upower_ifa = $upower_obj["org.freedesktop.UPower.Device"] | |
class Notifier < DBus::Object | |
@id = 0 | |
dbus_interface "org.freedesktop.Notifications" do | |
dbus_method :GetCapabilities do | |
["body"] | |
end | |
dbus_method :Notify, "in app_name:s, in replaces_id:u, in app_icon:s, in summary:s, in body:s, in actions:av, in hints:{sv}, in action_timeout:i" do |app_name, replaces_id, app_icon, summary, body, actions, hints, action_timeout| | |
$notify_q << { app_name: app_name, summary: summary, body: body.strip, id: @id } | |
@id += 1 | |
end | |
dbus_method :CloseNotification, "in id:u" do |id| | |
$stderr.puts "close #{id}" | |
end | |
dbus_method :GetServerInformation, "out name:s, out vendor:s, out version:s, out spec_version:s" do | |
["statusbar.rb", "Rulingia", "$Revision: 1.18 $", "1.2"] | |
end | |
end | |
end | |
$dbus_sess = DBus.session_bus | |
svc = nil | |
$notify_q = [] | |
begin | |
svc = $dbus_sess.request_service "org.freedesktop.Notifications" | |
svc.export Notifier.new "/org/freedesktop/Notifications" | |
Thread.new { | |
dloop = DBus::Main.new | |
dloop << $dbus_sess | |
dloop.run | |
} | |
rescue DBus::NameRequestError => e | |
svc = nil | |
end | |
i = 0 | |
puts "{\"version\":1}[" | |
loop do | |
if $notify_q.empty? | |
# Normal behaviour | |
puts ( | |
@order.pmap do |k| | |
$blocks[k][:val] = | |
$blocks[k][:proc].call if i % $blocks[k][:interval] == 0 | |
$blocks[k][:val] | |
end.to_json + "," | |
) | |
else | |
# Empty the notification queue. | |
until $notify_q.empty? | |
curr = $notify_q.shift | |
out = [ | |
{ | |
name: "notification", | |
full_text: ($sym[:info_circle] + " " + curr[:app_name] + ": " + curr[:summary] + | |
($notify_q.length > 5 ? " (#{$notify_q.length})" : "#{curr[:body]}")), | |
short_text: ($sym[:info_circle] + " " + curr[:app_name] + ": " + curr[:summary] + | |
($notify_q.length > 5 ? " (#{$notify_q.length})" : "")), | |
markup: "pango", | |
} | |
] | |
puts out.to_json + "," | |
interval = ($notify_q.length > 5) ? 1 : 3 | |
sleep interval | |
i += interval | |
end | |
end | |
sleep 1 | |
i += 1 | |
end | |
puts "]" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment