diff options
Diffstat (limited to 'widgets')
-rw-r--r-- | widgets/bat.lua | 79 | ||||
-rw-r--r-- | widgets/cpu.lua | 78 | ||||
-rw-r--r-- | widgets/cpufreq.lua | 54 | ||||
-rw-r--r-- | widgets/cpuinf.lua | 44 | ||||
-rw-r--r-- | widgets/date.lua | 23 | ||||
-rw-r--r-- | widgets/dio.lua | 71 | ||||
-rw-r--r-- | widgets/entropy.lua | 33 | ||||
-rw-r--r-- | widgets/fs.lua | 52 | ||||
-rw-r--r-- | widgets/gmail.lua | 83 | ||||
-rw-r--r-- | widgets/hddtemp.lua | 38 | ||||
-rw-r--r-- | widgets/init.lua | 36 | ||||
-rw-r--r-- | widgets/mbox.lua | 50 | ||||
-rw-r--r-- | widgets/mboxc.lua | 55 | ||||
-rw-r--r-- | widgets/mdir.lua | 38 | ||||
-rw-r--r-- | widgets/mem.lua | 49 | ||||
-rw-r--r-- | widgets/mpd.lua | 59 | ||||
-rw-r--r-- | widgets/net.lua | 75 | ||||
-rw-r--r-- | widgets/org.lua | 59 | ||||
-rw-r--r-- | widgets/os.lua | 58 | ||||
-rw-r--r-- | widgets/pkg.lua | 41 | ||||
-rw-r--r-- | widgets/thermal.lua | 42 | ||||
-rw-r--r-- | widgets/uptime.lua | 37 | ||||
-rw-r--r-- | widgets/volume.lua | 50 | ||||
-rw-r--r-- | widgets/weather.lua | 83 | ||||
-rw-r--r-- | widgets/wifi.lua | 72 |
25 files changed, 1359 insertions, 0 deletions
diff --git a/widgets/bat.lua b/widgets/bat.lua new file mode 100644 index 0000000..d6e3e6b --- /dev/null +++ b/widgets/bat.lua @@ -0,0 +1,79 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +--------------------------------------------------- + +-- {{{ Grab environment +local tonumber = tonumber +local setmetatable = setmetatable +local string = { format = string.format } +local helpers = require("vicious.helpers") +local math = { + min = math.min, + floor = math.floor +} +-- }}} + + +-- Batsys: provides state, charge, and remaining time for a requested battery +module("vicious.widgets.bat") + + +-- {{{ Battery widget type +local function worker(format, batid) + -- Apple PMU and ACPI/procfs battery widgets are in the [contrib] branch + local battery = helpers.pathtotable("/sys/class/power_supply/" .. batid) + local battery_state = { + ["Full\n"] = "↯", + ["Unknown\n"] = "⌁", + ["Charged\n"] = "↯", + ["Charging\n"] = "+", + ["Discharging\n"] = "-" + } + + -- Check if the battery is present + if not battery.present == "1\n" then + return {battery_state["Unknown\n"], 0, "N/A"} + end + + + -- Get state information + local state = battery_state[battery.status] or battery_state["Unknown\n"] + + -- Get capacity information + if battery.charge_now then + remaining, capacity = battery.charge_now, battery.charge_full + elseif battery.energy_now then + remaining, capacity = battery.energy_now, battery.energy_full + else + return {battery_state["Unknown\n"], 0, "N/A"} + end + + -- Calculate percentage (but work around broken BAT/ACPI implementations) + local percent = math.min(math.floor(remaining / capacity * 100), 100) + + + -- Get charge information + if battery.current_now then + rate = battery.current_now + else -- Todo: other rate sources, as with capacity? + return {state, percent, "N/A"} + end + + -- Calculate remaining (charging or discharging) time + if state == "+" then + timeleft = (tonumber(capacity) - tonumber(remaining)) / tonumber(rate) + elseif state == "-" then + timeleft = tonumber(remaining) / tonumber(rate) + else + return {state, percent, "N/A"} + end + local hoursleft = math.floor(timeleft) + local minutesleft = math.floor((timeleft - hoursleft) * 60 ) + local time = string.format("%02d:%02d", hoursleft, minutesleft) + + return {state, percent, time} +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/cpu.lua b/widgets/cpu.lua new file mode 100644 index 0000000..3dfc22f --- /dev/null +++ b/widgets/cpu.lua @@ -0,0 +1,78 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +-- * (c) 2009, Lucas de Vries <lucas@glacicle.com> +--------------------------------------------------- + +-- {{{ Grab environment +local ipairs = ipairs +local io = { lines = io.lines } +local setmetatable = setmetatable +local math = { floor = math.floor } +local table = { insert = table.insert } +local string = { + find = string.find, + gmatch = string.gmatch +} +-- }}} + + +-- Cpu: provides CPU usage for all available CPUs/cores +module("vicious.widgets.cpu") + + +-- Initialise function tables +local cpu_usage = {} +local cpu_total = {} +local cpu_active = {} + +-- {{{ CPU widget type +local function worker(format) + local cpu_lines = {} + + -- Get CPU stats + for line in io.lines("/proc/stat") do + if string.find(line, "^cpu") then + cpu_lines[#cpu_lines+1] = {} + + for i in string.gmatch(line, "[%s]+([%d]+)") do + table.insert(cpu_lines[#cpu_lines], i) + end + end + end + + -- Ensure tables are initialized correctly + while #cpu_total < #cpu_lines do + table.insert(cpu_total, 0) + table.insert(cpu_active, 0) + table.insert(cpu_usage, 0) + end + + local total_new = {} + local active_new = {} + local diff_total = {} + local diff_active = {} + + for i, v in ipairs(cpu_lines) do + -- Calculate totals + total_new[i] = 0 + for j = 1, #v do + total_new[i] = total_new[i] + v[j] + end + active_new[i] = v[1] + v[2] + v[3] + + -- Calculate percentage + diff_total[i] = total_new[i] - cpu_total[i] + diff_active[i] = active_new[i] - cpu_active[i] + cpu_usage[i] = math.floor(diff_active[i] / diff_total[i] * 100) + + -- Store totals + cpu_total[i] = total_new[i] + cpu_active[i] = active_new[i] + end + + return cpu_usage +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/cpufreq.lua b/widgets/cpufreq.lua new file mode 100644 index 0000000..7f60f5a --- /dev/null +++ b/widgets/cpufreq.lua @@ -0,0 +1,54 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +--------------------------------------------------- + +-- {{{ Grab environment +local tonumber = tonumber +local setmetatable = setmetatable +local string = { match = string.match } +local helpers = require("vicious.helpers") +-- }}} + + +-- Cpufreq: provides freq, voltage and governor info for a requested CPU +module("vicious.widgets.cpufreq") + + +-- {{{ CPU frequency widget type +local function worker(format, cpuid) + local cpufreq = helpers.pathtotable("/sys/devices/system/cpu/"..cpuid.."/cpufreq") + local governor_state = { + ["ondemand\n"] = "↯", + ["powersave\n"] = "⌁", + ["userspace\n"] = "¤", + ["performance\n"] = "⚡", + ["conservative\n"] = "↯" + } + -- Default voltage values + local voltage = { v = "N/A", mv = "N/A" } + + + -- Get the current frequency + local freq = tonumber(cpufreq.scaling_cur_freq) + -- Calculate MHz and GHz + local freqmhz = freq / 1000 + local freqghz = freqmhz / 1000 + + -- Get the current voltage + if cpufreq.scaling_voltages then + voltage.mv = tonumber(string.match(cpufreq.scaling_voltages, freq.."[%s]([%d]+)")) + -- Calculate voltage from mV + voltage.v = voltage.mv / 1000 + end + + -- Get the current governor + local governor = cpufreq.scaling_governor + -- Represent the governor as a symbol + governor = governor_state[governor] or governor + + return {freqmhz, freqghz, voltage.mv, voltage.v, governor} +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/cpuinf.lua b/widgets/cpuinf.lua new file mode 100644 index 0000000..8bee530 --- /dev/null +++ b/widgets/cpuinf.lua @@ -0,0 +1,44 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +--------------------------------------------------- + +-- {{{ Grab environment +local tonumber = tonumber +local io = { lines = io.lines } +local setmetatable = setmetatable +local string = { gmatch = string.gmatch } +-- }}} + + +-- Cpuinf: provides speed and cache information for all available CPUs/cores +module("vicious.widgets.cpuinf") + + +-- {{{ CPU Information widget type +local function worker(format) + local id = nil + local cpu_info = {} + + -- Get CPU info + for line in io.lines("/proc/cpuinfo") do + for k, v in string.gmatch(line, "([%a%s]+)[%s]+:[%s]([%d]+).-$") do + if k == "processor" then + id = v + elseif k == "cpu MHz\t" or k == "cpu MHz" then + local speed = tonumber(v) + cpu_info["{cpu"..id.." mhz}"] = speed + cpu_info["{cpu"..id.." ghz}"] = speed / 1000 + elseif k == "cache size" then + local cache = tonumber(v) + cpu_info["{cpu"..id.." kb}"] = cache + cpu_info["{cpu"..id.." mb}"] = cache / 1024 + end + end + end + + return cpu_info +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/date.lua b/widgets/date.lua new file mode 100644 index 0000000..68a803d --- /dev/null +++ b/widgets/date.lua @@ -0,0 +1,23 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +-- * (c) 2009, Lucas de Vries <lucas@glacicle.com> +--------------------------------------------------- + +-- {{{ Grab environment +local os = { date = os.date } +local setmetatable = setmetatable +-- }}} + + +-- Date: provides access to os.date with optional custom formatting +module("vicious.widgets.date") + + +-- {{{ Date widget type +local function worker(format) + return os.date(format or nil) +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/dio.lua b/widgets/dio.lua new file mode 100644 index 0000000..9731d57 --- /dev/null +++ b/widgets/dio.lua @@ -0,0 +1,71 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +--------------------------------------------------- + +-- {{{ Grab environment +local ipairs = ipairs +local setmetatable = setmetatable +local table = { insert = table.insert } +local string = { gmatch = string.gmatch } +local helpers = require("vicious.helpers") +-- }}} + + +-- Disk I/O: provides I/O statistics for requested storage devices +module("vicious.widgets.dio") + + +-- Initialise function tables +local disk_usage = {} +local disk_total = {} +-- Variable definitions +local unit = { ["s"] = 1, ["kb"] = 2, ["mb"] = 2048 } + +-- {{{ Disk I/O widget type +local function worker(format, disk) + local disk_lines = { [disk] = {} } + local disk_stats = helpers.pathtotable("/sys/block/" .. disk) + local disk_queue = helpers.pathtotable("/sys/block/" .. disk .. "/queue") + + if disk_stats.stat then + local match = string.gmatch(disk_stats.stat, "[%s]+([%d]+)") + for i = 1, 11 do -- Store disk stats + table.insert(disk_lines[disk], match()) + end + end + + -- Ensure tables are initialized correctly + local diff_total = { [disk] = {} } + if not disk_total[disk] then + disk_usage[disk] = {} + disk_total[disk] = {} + + while #disk_total[disk] < #disk_lines[disk] do + table.insert(disk_total[disk], 0) + end + end + + for i, v in ipairs(disk_lines[disk]) do + -- Diskstats are absolute, substract our last reading + diff_total[disk][i] = v - disk_total[disk][i] + + -- Store totals + disk_total[disk][i] = v + end + + -- Calculate and store I/O + helpers.uformat(disk_usage[disk], "read", diff_total[disk][3], unit) + helpers.uformat(disk_usage[disk], "write", diff_total[disk][7], unit) + helpers.uformat(disk_usage[disk], "total", diff_total[disk][7] + diff_total[disk][3], unit) + + -- Store I/O scheduler + if disk_queue.scheduler then + disk_usage[disk]["{sched}"] = string.gmatch(disk_queue.scheduler, "%[([%a]+)%]") + end + + return disk_usage[disk] +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/entropy.lua b/widgets/entropy.lua new file mode 100644 index 0000000..395d5d9 --- /dev/null +++ b/widgets/entropy.lua @@ -0,0 +1,33 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +--------------------------------------------------- + +-- {{{ Grab environment +local tonumber = tonumber +local setmetatable = setmetatable +local math = { ceil = math.ceil } +local helpers = require("vicious.helpers") +-- }}} + + +-- Entropy: provides available system entropy +module("vicious.widgets.entropy") + + +-- {{{ Entropy widget type +local function worker(format) + local random = helpers.pathtotable("/proc/sys/kernel/random") + + -- Linux 2.6 has a default entropy pool of 4096-bits + local poolsize = tonumber(random.poolsize) + -- Get available entropy + local ent = tonumber(random.entropy_avail) + -- Calculate percentage + local ent_percent = math.ceil(ent * 100 / poolsize) + + return {ent, ent_percent} +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/fs.lua b/widgets/fs.lua new file mode 100644 index 0000000..a85b903 --- /dev/null +++ b/widgets/fs.lua @@ -0,0 +1,52 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +-- * (c) 2009, Lucas de Vries <lucas@glacicle.com> +--------------------------------------------------- + +-- {{{ Grab environment +local tonumber = tonumber +local io = { popen = io.popen } +local setmetatable = setmetatable +local string = { match = string.match } +local helpers = require("vicious.helpers") +-- }}} + + +-- FS: provides file system disk space usage +module("vicious.widgets.fs") + + +-- Variable definitions +local unit = { ["mb"] = 1024, ["gb"] = 1024^2 } + +-- {{{ Filesystem widget type +local function worker(format, nfs) + -- Fallback to listing local filesystems + if nfs then nfs = "" else nfs = "-l" end + + -- Get (non-localized)data from df + local f = io.popen("LC_ALL=C df -kP " .. nfs) + local fs_info = {} + + for line in f:lines() do -- Match: (size) (used)(avail)(use%) (mount) + local s = string.match(line, "^.-[%s]([%d]+)") + local u,a,p = string.match(line, "([%d]+)[%D]+([%d]+)[%D]+([%d]+)%%") + local m = string.match(line, "%%[%s]([%p%w]+)") + + if u and m then -- Handle 1st line and broken regexp + helpers.uformat(fs_info, m .. " size", s, unit) + helpers.uformat(fs_info, m .. " used", u, unit) + helpers.uformat(fs_info, m .. " avail", a, unit) + + fs_info["{" .. m .. " used_p}"] = tonumber(p) + fs_info["{" .. m .. " avail_p}"] = 100 - tonumber(p) + end + end + f:close() + + return fs_info +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/gmail.lua b/widgets/gmail.lua new file mode 100644 index 0000000..b2f0d8c --- /dev/null +++ b/widgets/gmail.lua @@ -0,0 +1,83 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +--------------------------------------------------- + +-- {{{ Grab environment +local type = type +local tonumber = tonumber +local io = { popen = io.popen } +local setmetatable = setmetatable +local helpers = require("vicious.helpers") +local string = { + find = string.find, + match = string.match +} +-- }}} + + +-- Gmail: provides count of new and subject of last e-mail on Gmail +module("vicious.widgets.gmail") + + +-- {{{ Variable definitions +local rss = { + inbox = { + "https://mail.google.com/mail/feed/atom", + "Gmail %- Inbox" + }, + unread = { + "https://mail.google.com/mail/feed/atom/unread", + "Gmail %- Label" + }, + --labelname = { + -- "https://mail.google.com/mail/feed/atom/labelname", + -- "Gmail %- Label" + --}, +} + +-- Default is all unread +local feed = rss.unread +-- }}} + + +-- {{{ Gmail widget type +local function worker(format, warg) + local mail = { + ["{count}"] = 0, + ["{subject}"] = "N/A" + } + + -- Get info from the Gmail atom feed + local f = io.popen("curl --connect-timeout 1 -m 3 -fsn " .. feed[1]) + + -- Could be huge don't read it all at once, info we are after is at the top + for line in f:lines() do + mail["{count}"] = -- Count comes before messages and matches at least 0 + tonumber(string.match(line, "<fullcount>([%d]+)</fullcount>")) or mail["{count}"] + + -- Find subject tags + local title = string.match(line, "<title>(.*)</title>") + -- If the subject changed then break out of the loop + if title ~= nil and not string.find(title, feed[2]) then + -- Check if we should scroll, or maybe truncate + if warg then + if type(warg) == "table" then + title = helpers.scroll(title, warg[1], warg[2]) + else + title = helpers.truncate(title, warg) + end + end + + -- Spam sanitize the subject and store + mail["{subject}"] = helpers.escape(title) + break + end + end + f:close() + + return mail +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/hddtemp.lua b/widgets/hddtemp.lua new file mode 100644 index 0000000..6fcd05f --- /dev/null +++ b/widgets/hddtemp.lua @@ -0,0 +1,38 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +--------------------------------------------------- + +-- {{{ Grab environment +local tonumber = tonumber +local io = { popen = io.popen } +local setmetatable = setmetatable +local string = { gmatch = string.gmatch } +-- }}} + + +-- Hddtemp: provides hard drive temperatures using the hddtemp daemon +module("vicious.widgets.hddtemp") + + +-- {{{ HDD Temperature widget type +local function worker(format, port) + -- Fallback to default hddtemp port + if port == nil then port = 7634 end + + -- Get info from the hddtemp daemon + local f = io.popen("curl --connect-timeout 1 -fsm 3 telnet://127.0.0.1:"..port) + local hdd_temp = {} + + for line in f:lines() do + for d, t in string.gmatch(line, "|([%/%a%d]+)|.-|([%d]+)|[CF]+|") do + hdd_temp["{"..d.."}"] = tonumber(t) + end + end + f:close() + + return hdd_temp +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/init.lua b/widgets/init.lua new file mode 100644 index 0000000..5b8ae04 --- /dev/null +++ b/widgets/init.lua @@ -0,0 +1,36 @@ +--------------------------------------------------- +-- Vicious widgets for the awesome window manager +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +--------------------------------------------------- + +-- {{{ Configure widgets +require("vicious.widgets.cpu") +require("vicious.widgets.cpuinf") +require("vicious.widgets.cpufreq") +require("vicious.widgets.thermal") +require("vicious.widgets.uptime") +require("vicious.widgets.bat") +require("vicious.widgets.mem") +require("vicious.widgets.os") +require("vicious.widgets.fs") +require("vicious.widgets.dio") +require("vicious.widgets.hddtemp") +require("vicious.widgets.net") +require("vicious.widgets.wifi") +require("vicious.widgets.mbox") +require("vicious.widgets.mboxc") +require("vicious.widgets.mdir") +require("vicious.widgets.gmail") +require("vicious.widgets.entropy") +require("vicious.widgets.org") +require("vicious.widgets.pkg") +require("vicious.widgets.mpd") +require("vicious.widgets.volume") +require("vicious.widgets.weather") +require("vicious.widgets.date") +-- }}} + +-- Vicious: widgets for the awesome window manager +module("vicious.widgets") diff --git a/widgets/mbox.lua b/widgets/mbox.lua new file mode 100644 index 0000000..eca5403 --- /dev/null +++ b/widgets/mbox.lua @@ -0,0 +1,50 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +--------------------------------------------------- + +-- {{{ Grab environment +local type = type +local io = { open = io.open } +local setmetatable = setmetatable +local string = { gfind = string.gfind } +local helpers = require("vicious.helpers") +-- }}} + + +-- Mbox: provides the subject of last e-mail in a mbox file +module("vicious.widgets.mbox") + + +-- {{{ Mailbox widget type +local function worker(format, warg) + if type(warg) ~= "table" then mbox = warg end + -- mbox could be huge, get a 30kb chunk from EOF + -- * attachments could be much bigger than this + local f = io.open(mbox or warg[1]) + f:seek("end", -30720) + local txt = f:read("*all") + f:close() + + -- Default value + local subject = "N/A" + + -- Find all Subject lines + for i in string.gfind(txt, "Subject: ([^\n]*)") do + subject = i + end + + -- Check if we should scroll, or maybe truncate + if type(warg) == "table" then + if warg[3] ~= nil then + subject = helpers.scroll(subject, warg[2], warg[3]) + else + subject = helpers.truncate(subject, warg[2]) + end + end + + return {helpers.escape(subject)} +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/mboxc.lua b/widgets/mboxc.lua new file mode 100644 index 0000000..c5f694c --- /dev/null +++ b/widgets/mboxc.lua @@ -0,0 +1,55 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +--------------------------------------------------- + +-- {{{ Grab environment +local io = { open = io.open } +local setmetatable = setmetatable +local string = { find = string.find } +-- }}} + + +-- Mboxc: provides the count of total, old and new messages in mbox files +module("vicious.widgets.mboxc") + + +-- {{{ Mbox count widget type +local function worker(format, warg) + -- Initialise counters + local count = { old = 0, total = 0, new = 0 } + + -- Get data from mbox files + for i=1, #warg do + local f = io.open(warg[i]) + + while true do + -- Read the mbox line by line, if we are going to read + -- some *HUGE* folders then switch to reading chunks + local lines = f:read("*line") + if not lines then break end + + -- Find all messages + -- * http://www.jwz.org/doc/content-length.html + local _, from = string.find(lines, "^From[%s]") + if from ~= nil then count.total = count.total + 1 end + + -- Read messages have the Status header + local _, status = string.find(lines, "^Status:[%s]RO$") + if status ~= nil then count.old = count.old + 1 end + + -- Skip the folder internal data + local _, int = string.find(lines, "^Subject:[%s].*FOLDER[%s]INTERNAL[%s]DATA") + if int ~= nil then count.total = count.total - 1 end + end + f:close() + end + + -- Substract total from old to get the new count + count.new = count.total - count.old + + return {count.total, count.old, count.new} +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/mdir.lua b/widgets/mdir.lua new file mode 100644 index 0000000..858a9b1 --- /dev/null +++ b/widgets/mdir.lua @@ -0,0 +1,38 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +-- * (c) Maildir Biff Widget, Fredrik Ax +--------------------------------------------------- + +-- {{{ Grab environment +local io = { popen = io.popen } +local setmetatable = setmetatable +-- }}} + + +-- Mdir: provides the number of new and unread messages in Maildir structures/dirs +module("vicious.widgets.mdir") + + +-- {{{ Maildir widget type +local function worker(format, warg) + -- Initialise counters + local count = { new = 0, cur = 0 } + + for i=1, #warg do + -- Recursively find new messages + local f = io.popen("find "..warg[i].." -type f -wholename '*/new/*'") + for line in f:lines() do count.new = count.new + 1 end + f:close() + + -- Recursively find "old" messages lacking the Seen flag + local f = io.popen("find "..warg[i].." -type f -regex '.*/cur/.*2,[^S]*$'") + for line in f:lines() do count.cur = count.cur + 1 end + f:close() + end + + return {count.new, count.cur} +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/mem.lua b/widgets/mem.lua new file mode 100644 index 0000000..c2f5886 --- /dev/null +++ b/widgets/mem.lua @@ -0,0 +1,49 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +-- * (c) 2009, Lucas de Vries <lucas@glacicle.com> +--------------------------------------------------- + +-- {{{ Grab environment +local io = { lines = io.lines } +local setmetatable = setmetatable +local math = { floor = math.floor } +local string = { gmatch = string.gmatch } +-- }}} + + +-- Mem: provides RAM and Swap usage statistics +module("vicious.widgets.mem") + + +-- {{{ Memory widget type +local function worker(format) + local mem = { buf = {}, swp = {} } + + -- Get MEM info + for line in io.lines("/proc/meminfo") do + for k, v in string.gmatch(line, "([%a]+):[%s]+([%d]+).+") do + if k == "MemTotal" then mem.total = math.floor(v/1024) + elseif k == "MemFree" then mem.buf.f = math.floor(v/1024) + elseif k == "Buffers" then mem.buf.b = math.floor(v/1024) + elseif k == "Cached" then mem.buf.c = math.floor(v/1024) + elseif k == "SwapTotal" then mem.swp.t = math.floor(v/1024) + elseif k == "SwapFree" then mem.swp.f = math.floor(v/1024) + end + end + end + + -- Calculate memory percentage + mem.free = mem.buf.f + mem.buf.b + mem.buf.c + mem.inuse = mem.total - mem.free + mem.usep = math.floor(mem.inuse / mem.total * 100) + -- Calculate swap percentage + mem.swp.inuse = mem.swp.t - mem.swp.f + mem.swp.usep = math.floor(mem.swp.inuse / mem.swp.t * 100) + + return {mem.usep, mem.inuse, mem.total, mem.free, + mem.swp.usep, mem.swp.inuse, mem.swp.t, mem.swp.f} +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/mpd.lua b/widgets/mpd.lua new file mode 100644 index 0000000..009ae7d --- /dev/null +++ b/widgets/mpd.lua @@ -0,0 +1,59 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +--------------------------------------------------- + +-- {{{ Grab environment +local tonumber = tonumber +local io = { popen = io.popen } +local setmetatable = setmetatable +local string = { gmatch = string.gmatch } +local helpers = require("vicious.helpers") +-- }}} + + +-- Mpd: provides Music Player Daemon information +module("vicious.widgets.mpd") + + +-- {{{ MPD widget type +local function worker(format, warg) + local mpd_state = { + ["{volume}"] = 0, + ["{state}"] = "N/A", + ["{Artist}"] = "N/A", + ["{Title}"] = "N/A", + ["{Album}"] = "N/A", + ["{Genre}"] = "N/A" + } + + -- Fallback to MPD defaults + local pass = warg and warg[1] or "\"\"" + local host = warg and warg[2] or "127.0.0.1" + local port = warg and warg[3] or "6600" + + -- Construct MPD client options + local mpdh = "telnet://"..host..":"..port + local echo = "echo 'password "..pass.."\nstatus\ncurrentsong\nclose'" + + -- Get data from MPD server + local f = io.popen(echo.." | curl --connect-timeout 1 -fsm 3 "..mpdh) + + for line in f:lines() do + for k, v in string.gmatch(line, "([%w]+):[%s](.*)$") do + if k == "volume" then mpd_state["{"..k.."}"] = v and tonumber(v) + elseif k == "state" then mpd_state["{"..k.."}"] = helpers.capitalize(v) + elseif k == "Artist" then mpd_state["{"..k.."}"] = helpers.escape(v) + elseif k == "Title" then mpd_state["{"..k.."}"] = helpers.escape(v) + elseif k == "Album" then mpd_state["{"..k.."}"] = helpers.escape(v) + elseif k == "Genre" then mpd_state["{"..k.."}"] = helpers.escape(v) + end + end + end + f:close() + + return mpd_state +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/net.lua b/widgets/net.lua new file mode 100644 index 0000000..9fd221f --- /dev/null +++ b/widgets/net.lua @@ -0,0 +1,75 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +-- * (c) 2009, Lucas de Vries <lucas@glacicle.com> +--------------------------------------------------- + +-- {{{ Grab environment +local tonumber = tonumber +local os = { time = os.time } +local io = { lines = io.lines } +local setmetatable = setmetatable +local string = { match = string.match } +local helpers = require("vicious.helpers") +-- }}} + + +-- Net: provides usage statistics for all network interfaces +module("vicious.widgets.net") + + +-- Initialise function tables +local nets = {} +-- Variable definitions +local unit = { ["b"] = 1, ["kb"] = 1024, + ["mb"] = 1024^2, ["gb"] = 1024^3 +} + +-- {{{ Net widget type +local function worker(format) + local args = {} + + -- Get NET stats + for line in io.lines("/proc/net/dev") do + -- Match wmaster0 as well as rt0 (multiple leading spaces) + local name = string.match(line, "^[%s]?[%s]?[%s]?[%s]?([%w]+):") + if name ~= nil then + -- Received bytes, first value after the name + local recv = tonumber(string.match(line, ":[%s]*([%d]+)")) + -- Transmited bytes, 7 fields from end of the line + local send = tonumber(string.match(line, + "([%d]+)%s+%d+%s+%d+%s+%d+%s+%d+%s+%d+%s+%d+%s+%d$")) + + helpers.uformat(args, name .. " rx", recv, unit) + helpers.uformat(args, name .. " tx", send, unit) + + if nets[name] == nil then + -- Default values on the first run + nets[name] = {} + helpers.uformat(args, name .. " down", 0, unit) + helpers.uformat(args, name .. " up", 0, unit) + + nets[name].time = os.time() + else -- Net stats are absolute, substract our last reading + local interval = os.time() - nets[name].time > 0 and + os.time() - nets[name].time or 1 + nets[name].time = os.time() + + local down = (recv - nets[name][1]) / interval + local up = (send - nets[name][2]) / interval + + helpers.uformat(args, name .. " down", down, unit) + helpers.uformat(args, name .. " up", up, unit) + end + + -- Store totals + nets[name][1] = recv + nets[name][2] = send + end + end + + return args +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/org.lua b/widgets/org.lua new file mode 100644 index 0000000..8764e3a --- /dev/null +++ b/widgets/org.lua @@ -0,0 +1,59 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +-- * (c) org-awesome, Damien Leone +--------------------------------------------------- + +-- {{{ Grab environment +local io = { lines = io.lines } +local setmetatable = setmetatable +local string = { find = string.find } +local os = { + time = os.time, + date = os.date +} +-- }}} + + +-- Org: provides agenda statistics for Emacs org-mode +module("vicious.widgets.org") + + +-- {{{ OrgMode widget type +local function worker(format, warg) + -- Compute delays + local today = os.time{ year=os.date("%Y"), month=os.date("%m"), day=os.date("%d") } + local soon = today + 24 * 3600 * 3 -- 3 days ahead is close + local future = today + 24 * 3600 * 7 -- 7 days ahead is maximum + + -- Initialise counters + local count = { past = 0, today = 0, soon = 0, future = 0 } + + -- Get data from agenda files + for i=1, #warg do + for line in io.lines(warg[i]) do + local scheduled = string.find(line, "SCHEDULED:") + local closed = string.find(line, "CLOSED:") + local deadline = string.find(line, "DEADLINE:") + + if (scheduled and not closed) or (deadline and not closed) then + local b, e, y, m, d = string.find(line, "(%d%d%d%d)-(%d%d)-(%d%d)") + + if b then + local t = os.time{ year = y, month = m, day = d } + + if t < today then count.past = count.past + 1 + elseif t == today then count.today = count.today + 1 + elseif t <= soon then count.soon = count.soon + 1 + elseif t <= future then count.future = count.future + 1 + end + end + end + end + end + + return {count.past, count.today, count.soon, count.future} +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/os.lua b/widgets/os.lua new file mode 100644 index 0000000..8b1d11e --- /dev/null +++ b/widgets/os.lua @@ -0,0 +1,58 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +--------------------------------------------------- + +-- {{{ Grab environment +local pairs = pairs +local io = { popen = io.popen } +local os = { getenv = os.getenv } +local setmetatable = setmetatable +local helpers = require("vicious.helpers") +local string = { + gsub = string.gsub, + match = string.match +} +-- }}} + + +-- OS: provides operating system information +module("vicious.widgets.os") + + +-- {{{ Operating system widget type +local function worker(format) + local system = { + ["ostype"] = "N/A", + ["hostname"] = "N/A", + ["osrelease"] = "N/A", + ["username"] = "N/A" + } + + -- Linux manual page: uname(2) + local kernel = helpers.pathtotable("/proc/sys/kernel") + for k, v in pairs(system) do + if kernel[k] then + system[k] = string.gsub(kernel[k], "[%s]*$", "") + end + end + + -- BSD manual page: uname(1) + if system["ostype"] == "N/A" then + local f = io.popen("uname -snr") + local uname = f:read("*line") + f:close() + + system["ostype"], system["hostname"], system["osrelease"] = + string.match(uname, "([%w]+)[%s]([%w%p]+)[%s]([%w%p]+)") + end + + -- Get user from the environment + system["username"] = os.getenv("USER") + + return {system["ostype"], system["osrelease"], + system["username"], system["hostname"]} +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/pkg.lua b/widgets/pkg.lua new file mode 100644 index 0000000..fa2375b --- /dev/null +++ b/widgets/pkg.lua @@ -0,0 +1,41 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +--------------------------------------------------- + +-- {{{ Grab environment +local io = { popen = io.popen } +local math = { max = math.max } +local setmetatable = setmetatable +-- }}} + + +-- Pkg: provides number of pending updates on GNU/Linux +module("vicious.widgets.pkg") + + +-- {{{ Packages widget type +local function worker(format, dist) + -- Initialise counters + local updates = 0 + local manager = { + ["Arch"] = { cmd = "pacman -Qu" }, + ["Arch S"] = { cmd = "yes | pacman -Sup", sub = 2 }, + ["Debian"] = { cmd = "apt-show-versions -u -b" }, + ["Fedora"] = { cmd = "yum list updates", sub = 3 } + } + + -- Check if updates are available + local pkg = manager[dist] + local f = io.popen(pkg.cmd) + + for line in f:lines() do + updates = updates + 1 + end + f:close() + + return {pkg.sub and math.max(updates-pkg.sub, 0) or updates} +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/thermal.lua b/widgets/thermal.lua new file mode 100644 index 0000000..9768c57 --- /dev/null +++ b/widgets/thermal.lua @@ -0,0 +1,42 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +--------------------------------------------------- + +-- {{{ Grab environment +local type = type +local tonumber = tonumber +local setmetatable = setmetatable +local string = { match = string.match } +local helpers = require("vicious.helpers") +-- }}} + + +-- Thermal: provides temperature levels of ACPI and coretemp thermal zones +module("vicious.widgets.thermal") + + +-- {{{ Thermal widget type +local function worker(format, warg) + local zone = { -- Known temperature data sources + ["sys"] = {"/sys/class/thermal/", file = "temp", div = 1000}, + ["core"] = {"/sys/devices/platform/", file = "temp1_input",div = 1000}, + ["proc"] = {"/proc/acpi/thermal_zone/",file = "temperature"} + } -- Default to /sys/class/thermal + local warg = type(warg) == "table" and warg or {warg, "sys"} + local thermal = helpers.pathtotable(zone[warg[2]][1] .. warg[1]) + + -- Get temperature from thermal zone + if thermal[zone[warg[2]].file] then + if zone[warg[2]].div then + return {thermal[zone[warg[2]].file] / zone[warg[2]].div} + else -- /proc/acpi "temperature: N C" + return {tonumber(string.match(thermal[zone[warg[2]].file], "[%d]+"))} + end + end + + return {0} +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/uptime.lua b/widgets/uptime.lua new file mode 100644 index 0000000..ebc5af8 --- /dev/null +++ b/widgets/uptime.lua @@ -0,0 +1,37 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +-- * (c) 2009, Lucas de Vries <lucas@glacicle.com> +--------------------------------------------------- + +-- {{{ Grab environment +local setmetatable = setmetatable +local math = { floor = math.floor } +local string = { match = string.match } +local helpers = require("vicious.helpers") +-- }}} + + +-- Uptime: provides system uptime and load information +module("vicious.widgets.uptime") + + +-- {{{ Uptime widget type +local function worker(format) + local proc = helpers.pathtotable("/proc") + + -- Get system uptime + local up_t = math.floor(string.match(proc.uptime, "[%d]+")) + local up_d = math.floor(up_t / (3600 * 24)) + local up_h = math.floor((up_t % (3600 * 24)) / 3600) + local up_m = math.floor(((up_t % (3600 * 24)) % 3600) / 60) + + -- Get load averages + local l1, l5, l15 = -- Get load averages for past 1, 5 and 15 minutes + string.match(proc.loadavg, "([%d]*%.[%d]*)%s([%d]*%.[%d]*)%s([%d]*%.[%d]*)") + + return {up_d, up_h, up_m, l1, l5, l15} +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/volume.lua b/widgets/volume.lua new file mode 100644 index 0000000..42f72e4 --- /dev/null +++ b/widgets/volume.lua @@ -0,0 +1,50 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +--------------------------------------------------- + +-- {{{ Grab environment +local tonumber = tonumber +local io = { popen = io.popen } +local setmetatable = setmetatable +local string = { match = string.match } +-- }}} + + +-- Volume: provides volume levels and state of requested ALSA mixers +module("vicious.widgets.volume") + + +-- {{{ Volume widget type +local function worker(format, warg) + local mixer_state = { + ["on"] = "♫", -- "", + ["off"] = "♩" -- "M" + } + + -- Get mixer control contents + local f = io.popen("amixer get " .. warg) + local mixer = f:read("*all") + f:close() + + -- Capture mixer control state: [5%] ... ... [on] + local volu, mute = string.match(mixer, "([%d]+)%%.*%[([%l]*)") + -- Handle mixers without data + if volu == nil then + return {0, mixer_state["off"]} + end + + -- Handle mixers without mute + if mute == "" and volu == "0" + -- Handle mixers that are muted + or mute == "off" then + mute = mixer_state["off"] + else + mute = mixer_state["on"] + end + + return {tonumber(volu), mute} +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/weather.lua b/widgets/weather.lua new file mode 100644 index 0000000..effa138 --- /dev/null +++ b/widgets/weather.lua @@ -0,0 +1,83 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +--------------------------------------------------- + +-- {{{ Grab environment +local tonumber = tonumber +local io = { popen = io.popen } +local setmetatable = setmetatable +local math = { ceil = math.ceil } +local string = { match = string.match } +local helpers = require("vicious.helpers") +-- }}} + + +-- Weather: provides weather information for a requested station +module("vicious.widgets.weather") + + +-- {{{ Weather widget type +local function worker(format, station) + -- Default values + local weather = { + ["{city}"] = "N/A", + ["{wind}"] = "N/A", + ["{windmph}"] = "N/A", + ["{windkmh}"] = "N/A", + ["{sky}"] = "N/A", + ["{weather}"] = "N/A", + ["{tempf}"] = "N/A", + ["{tempc}"] = "N/A", + ["{humid}"] = "N/A", + ["{press}"] = "N/A" + } + + -- Get weather forceast by the station ICAO code, from: + -- * US National Oceanic and Atmospheric Administration + local noaa = "http://weather.noaa.gov/pub/data/observations/metar/decoded/" + local f = io.popen("curl --connect-timeout 1 -fsm 3 "..noaa..station..".TXT") + local ws = f:read("*all") + f:close() + + -- Check if there was a timeout or a problem with the station + if ws == nil then return weather end + + weather["{city}"] = -- City and/or area + string.match(ws, "^(.+)%,.*%([%u]+%)") or weather["{city}"] + weather["{wind}"] = -- Wind direction and degrees if available + string.match(ws, "Wind:[%s][%a]+[%s][%a]+[%s](.+)[%s]at.+$") or weather["{wind}"] + weather["{windmph}"] = -- Wind speed in MPH if available + string.match(ws, "Wind:[%s].+[%s]at[%s]([%d]+)[%s]MPH") or weather["{windmph}"] + weather["{sky}"] = -- Sky conditions if available + string.match(ws, "Sky[%s]conditions:[%s](.-)[%c]") or weather["{sky}"] + weather["{weather}"] = -- Weather conditions if available + string.match(ws, "Weather:[%s](.-)[%c]") or weather["{weather}"] + weather["{tempf}"] = -- Temperature in fahrenheit + string.match(ws, "Temperature:[%s]([%-]?[%d%.]+).*[%c]") or weather["{tempf}"] + weather["{humid}"] = -- Relative humidity in percent + string.match(ws, "Relative[%s]Humidity:[%s]([%d]+)%%") or weather["{humid}"] + weather["{press}"] = -- Pressure in hPa + string.match(ws, "Pressure[%s].+%((.+)[%s]hPa%)") or weather["{press}"] + + -- Wind speed in km/h if MPH was available + if weather["{windmph}"] ~= "N/A" then + weather["{windmph}"] = tonumber(weather["{windmph}"]) + weather["{windkmh}"] = math.ceil(weather["{windmph}"] * 1.6) + end -- Temperature in °C if °F was available + if weather["{tempf}"] ~= "N/A" then + weather["{tempf}"] = tonumber(weather["{tempf}"]) + weather["{tempc}"] = math.ceil((weather["{tempf}"] - 32) * 5/9) + end -- Capitalize some stats so they don't look so out of place + if weather["{sky}"] ~= "N/A" then + weather["{sky}"] = helpers.capitalize(weather["{sky}"]) + end + if weather["{weather}"] ~= "N/A" then + weather["{weather}"] = helpers.capitalize(weather["{weather}"]) + end + + return weather +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/wifi.lua b/widgets/wifi.lua new file mode 100644 index 0000000..65ffb79 --- /dev/null +++ b/widgets/wifi.lua @@ -0,0 +1,72 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. <anrxc@sysphere.org> +--------------------------------------------------- + +-- {{{ Grab environment +local tonumber = tonumber +local setmetatable = setmetatable +local io = { + open = io.open, + popen = io.popen +} +local string = { + find = string.find, + match = string.match +} +-- }}} + + +-- Wifi: provides wireless information for a requested interface +module("vicious.widgets.wifi") + + +-- {{{ Wireless widget type +local function worker(format, iface) + -- Default values + local winfo = { + ["{ssid}"] = "N/A", + ["{mode}"] = "N/A", + ["{chan}"] = "N/A", + ["{rate}"] = 0, + ["{link}"] = 0, + ["{sign}"] = 0 + } + + -- Get data from iwconfig where available + local iwconfig = "/sbin/iwconfig" + local f = io.open(iwconfig, "rb") + if not f then + iwconfig = "/usr/sbin/iwconfig" + else + f:close() + end + local f = io.popen(iwconfig .." ".. iface .. " 2>&1") + local iw = f:read("*all") + f:close() + + -- iwconfig wasn't found, isn't executable, or non-wireless interface + if iw == nil or string.find(iw, "No such device") then + return winfo + end + + -- Output differs from system to system, some stats can be + -- separated by =, and not all drivers report all stats + winfo["{ssid}"] = -- SSID can have almost anything in it + string.match(iw, 'ESSID[=:]"([%w%p]+[%s]*[%w%p]*]*)"') or winfo["{ssid}"] + winfo["{mode}"] = -- Modes are simple, but also match the "-" in Ad-Hoc + string.match(iw, "Mode[=:]([%w%-]*)") or winfo["{mode}"] + winfo["{chan}"] = -- Channels are plain digits + tonumber(string.match(iw, "Channel[=:]([%d]+)") or winfo["{chan}"]) + winfo["{rate}"] = -- Bitrate can start with a space, we don't want to display Mb/s + tonumber(string.match(iw, "Bit Rate[=:]([%s]?[%d%.]*)") or winfo["{rate}"]) + winfo["{link}"] = -- Link quality can contain a slash (32/70), match only the first number + tonumber(string.match(iw, "Link Quality[=:]([%d]+)") or winfo["{link}"]) + winfo["{sign}"] = -- Signal level can be a negative value, don't display decibel notation + tonumber(string.match(iw, "Signal level[=:]([%-]?[%d]+)") or winfo["{sign}"]) + + return winfo +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) |