Please bear with us as we work to restore functionality to dotfiles.org.
Ruby system monitor for use with dzen2
#!/usr/bin/env ruby
# System monitor that dumps stats to standard out.
# This script is meant to be used with dzen and other programs that read from
# standard input and display meters, etc.
# Copyright (C) 2008 by J. Bromley
# $Id: sysmon.rb,v 0d77dcc54282 2008/07/19 05:23:26 jbromley $
require 'logger'
LOGGER = Logger.new('/tmp/sysmon.log')
LOGGER.info "INIT"
LOGGER.level = Logger::DEBUG
# configuration ========================================================
config = {}
config[:interval] = 1
config[:cpu_usage] = { }
config[:cpu_usage][:interval] = 1
config[:cpu_frequency] = { }
config[:cpu_frequency][:interval] = 1
config[:cpu_temperature] = { }
config[:cpu_temperature][:interval] = 6
config[:battery] = { }
config[:battery][:interval] = 6
config[:battery][:low_action] =
'zenity --warning --text "The battery is getting low."'
config[:battery][:critical_action] =
'zenity --error --text "The battery is critically low."'
config[:tpvol] = { }
# config[:tpvol][:interval] = 1
config[:xkb] = { }
# config[:xkb][:interval] = 1
config[:clock] = { }
config[:clock][:interval] = 30
config[:clock][:format] = "%a %D %I:%M %P"
# helper functions -----------------------------------------------------
def image(file)
"^fg(Orange)^i(/home/jay/images/xbm8x8/#{file})^fg()"
end
def image_nocolor(file)
"^i(/home/jay/images/xbm8x8/#{file})"
end
# base monitor class ---------------------------------------------------
class Monitor
def initialize(config)
@interval = config[:interval] || 1
@output = ''
@last_update = Time.at(0)
end
def update()
now = Time.new
if now - @last_update >= @interval
@output = update_stats()
@last_update = now
end
return @output
end
end
# cpu information ------------------------------------------------------
class CpuUsage < Monitor
def initialize(config)
super(config)
@cpu_keys = [:user, :system, :nice, :idle]
@prev_stats = Hash.new { |h, k| h[k] = 0.0 }
@cpu_keys = [:user, :system, :nice, :idle].each { |k| @prev_stats[k] }
@output = "#{image('cpu.xbm')} --%"
end
def update_stats()
load = '--'
begin
cpu_data = IO.readlines('/proc/stat').grep(/^cpu\s+/).first.split
stats = @cpu_keys.zip(cpu_data[1..4]).inject({}) do |h, v|
h[v.first] = v.last.to_i
h
end
dtotal = @cpu_keys.inject(0) do |accum, key|
accum += (stats[key] - @prev_stats[key])
accum
end
if dtotal > 0.0
load = "%02d" % (100 - ((stats[:idle] - @prev_stats[:idle]) * 100 / dtotal))
end
@prev_stats.merge!(stats)
return "#{image('cpu.xbm')} #{load}%"
rescue StandardError
LOGGER.error "#{$!.class}: #{$!.message}"
end
end
end
class CpuFrequency < Monitor
def initialize(config)
super(config)
end
def update_stats()
freq = '---MHz'
begin
freq = IO.readlines('/proc/cpuinfo').grep(/cpu MHz/).first.split[-1].sub(/\..*$/,'')
rescue StandardError
LOGGER.error "#{$!.class}: #{$!.message}"
end
return "#{freq}MHz"
end
end
class CpuTemperature < Monitor
def initialize(config)
super(config)
@temp_file = config[:temperature_file] || '/proc/acpi/thermal_zone/THM0/temperature'
end
def update_stats()
temperature = '--C'
begin
temp_data = IO.read(@temp_file).split
temperature = "#{temp_data[1]}#{temp_data[2]}"
rescue StandardError
LOGGER.error "#{$!.class}: #{$!.message}"
end
return temperature
end
end
# battery_info ---------------------------------------------------------
class BatteryMonitor < Monitor
def initialize(config)
super(config)
@state_file = config[:state_file] || '/proc/acpi/battery/BAT0/state'
@info_file = config[:info_file] || '/proc/acpi/battery/BAT0/info'
@level_low = config[:low] || 5
@low_action = config[:low_action] ||
'echo "Low battery" | xmessage -center -buttons quit:0 -default quit -file -'
@level_critical = config[:critical] || 2
@critical_action = config[:critical_action] ||
'echo "Critical battery" | xmessage -center -buttons quit:0 -default quit -file -'
@warned_low = false
@warned_critical = false
@output = '?---%[-:--]?'
end
def update_stats()
state = IO.readlines(@state_file)
info = IO.readlines(@info_file)
present = info[0].gsub(/.*:\s*/,'').chomp
output = "#{image('bat_empty_01.xbm')} N/A"
if present == "yes"
remaining_cap = state[4].gsub(/.*:\s*/,'').chomp.chomp("mWh").to_f
full_cap = info[2].gsub(/.*:\s*/,'').chomp.chomp("mWh").to_f
batt_percent = ((remaining_cap / full_cap) * 100).to_i
batt_percent = 100 if batt_percent > 100
charging_state = state[2].gsub(/.*:\s*/,'').chomp
# Take action in case battery is low/critical
if charging_state == "discharging" && batt_percent <= @level_critical
unless @warned_critical
LOGGER.info "Warning about critical battery."
system("ssid #{@critical_action} &")
@warned_critical = true
end
elsif charging_state == "discharging" && batt_percent <= @level_low
unless @warned_low
LOGGER.info "Warning about low battery."
system("ssid #{@low_action} &")
@warned_low = true
end
else
@warned_low = false
@warned_critical = false
end
# Calculate remaining time for discharge/charge.
time_remaining = '-:--'
present_rate = state[3].gsub(/.*:\s*/,'').chomp.chomp("mW").to_f
if charging_state == 'discharging'
time_left = remaining_cap / present_rate if present_rate > 0
elsif charging_state == 'charging'
time_left = (full_cap - remaining_cap) / present_rate if present_rate > 0
end
if time_left
hours = time_left.to_i
minutes = (time_left % 1 * 60).to_i
time_remaining = "%d:%02d" % [hours, minutes]
end
# Set charging state character
charging_state = '=' if charging_state == "charged" ||
(charging_state == "discharging" && batt_percent >= 97)
charging_state = '+' if charging_state == "charging"
charging_state = '-' if charging_state == "discharging"
# Check for the AC adapter state
ac_state = IO.read('/proc/acpi/ac_adapter/AC/state')[/on-line/]
if ac_state == "on-line"
ac_state = image_nocolor('ac_01.xbm')
else
ac_state = '-'
end
# Select a battery icon based on battery level
case
when batt_percent <= @level_critical
icon = image('bat_empty_01.xbm')
when batt_percent <= @level_low
icon = image('bat_low_01.xbm')
else
icon = image('bat_full_01.xbm')
end
output= "#{icon} #{charging_state}#{batt_percent}%[#{time_remaining}]#{ac_state}"
end
return output
end
end
# tp_volume ----------------------------------------------------------
class ThinkPadVolume < Monitor
def initialize(config)
super(config)
@mixers = config[:mixer] || 'Master'
@mixers = [@mixers] if !(Array === @mixers)
@output = '--%'
end
def update_stats()
if @mixers.empty?
return "vol: off"
end
status = ''
@mixers.reverse.each do |mixer| # show status of first mixer in list
status = IO.read('/proc/acpi/ibm/volume')
end
volume_abs = status[/^level:\s*(\d+)/, 1].to_i
if status[/on/]
volume = 'muted'
icon = image('spkr_02.xbm')
else
volume = "#{volume_abs * 100 / 14}%"
icon = image('spkr_01.xbm')
end
return "#{icon} #{volume}".strip
end
end
# xkeyboard ------------------------------------------------------------
require 'ruby-wmii/xkb'
class XkbMonitor < Monitor
def initialize(config)
super(config)
begin
@xkb = Xkb::XKeyboard.new
rescue StandardError
LOGGER.error "#{$!.class}: #{$!.message}"
end
@output = "#{image('info_02.xbm')} --"
end
def update()
layout = @xkb.current_group_symbol
return "#{image('info_02.xbm')} #{layout}"
end
end
# clock ----------------------------------------------------------------
class DateTimeMonitor < Monitor
def initialize(config)
super(config)
@time_format = config[:format]
end
def update()
icon = image('clock.xbm')
datetime = Time.new.strftime(@time_format)
"#{icon} #{datetime}"
end
end
# Main loop ============================================================
interval = config[:interval] || 1
monitors = []
monitors.push(CpuUsage.new(config[:cpu_usage]))
monitors.push(CpuFrequency.new(config[:cpu_frequency]))
monitors.push(CpuTemperature.new(config[:cpu_temperature]))
monitors.push(BatteryMonitor.new(config[:battery]))
monitors.push(ThinkPadVolume.new(config[:tpvol]))
monitors.push(XkbMonitor.new(config[:xkb]))
monitors.push(DateTimeMonitor.new(config[:clock]))
loop do
output = monitors.inject('') do |out, m|
out += m.update + " "
end
output += image('arch_10x10.xbm')
begin
puts output
STDOUT.flush
rescue StandardError
LOGGER.error "#{$!.class}: #{$!.message}"
exit!
end
sleep interval
end