diff --git a/contrib/batacpi.lua b/contrib/batacpi.lua new file mode 100644 index 0000000..62156fd --- /dev/null +++ b/contrib/batacpi.lua @@ -0,0 +1,51 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. +--------------------------------------------------- + +-- {{{ Grab environment +local tonumber = tonumber +local io = { popen = io.popen } +local setmetatable = setmetatable +local table = { insert = table.insert } +local string = { match = string.match } +-- }}} + + +-- Batacpi: provides state, charge, and remaining time for all batteries using acpitool +module("vicious.contrib.batacpi") + + +-- {{{ Battery widget type +local function worker(format) + local battery_info = {} + local battery_state = { + ["full"] = "↯", + ["unknown"] = "⌁", + ["charged"] = "↯", + ["charging"] = "+", + ["discharging"] = "-" + } + + -- Get data from acpitool + local f = io.popen("acpitool -b") + + for line in f:lines() do + -- Check if the battery is present + if string.match(line, "^[%s]+Battery.*") then + -- Store state and charge information + table.insert(battery_info, (battery_state[string.match(line, "([%a]*),") or "unknown"])) + table.insert(battery_info, (tonumber(string.match(line, "([%d]?[%d]?[%d])%.")) or 0)) + -- Store remaining time information + table.insert(battery_info, (string.match(line, "%%,%s(.*)") or "N/A")) + else + return {battery_state["unknown"], 0, "N/A"} + end + end + f:close() + + return battery_info +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/batpmu.lua b/contrib/batpmu.lua new file mode 100644 index 0000000..e84295e --- /dev/null +++ b/contrib/batpmu.lua @@ -0,0 +1,78 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. +--------------------------------------------------- + +-- {{{ Grab environment +local tonumber = tonumber +local io = { open = io.open } +local setmetatable = setmetatable +local math = { + min = math.min, + floor = math.floor +} +local string = { + find = string.find, + match = string.match, + format = string.format +} +-- }}} + + +-- Batpmu: provides state, charge and remaining time for a requested battery using PMU +module("vicious.contrib.batpmu") + + +-- {{{ Battery widget type +local function worker(format, batid) + local battery_state = { + ["full"] = "↯", + ["unknown"] = "⌁", + ["00000013"] = "+", + ["00000011"] = "-" + } + + -- Get /proc/pmu/battery* state + local f = io.open("/proc/pmu/" .. batid) + -- Handler for incompetent users + if not f then return {battery_state["unknown"], 0, "N/A"} end + local statefile = f:read("*all") + f:close() + + -- Get /proc/pmu/info data + local f = io.open("/proc/pmu/info") + local infofile = f:read("*all") + f:close() + + -- Check if the battery is present + if infofile == nil or string.find(infofile, "Battery count[%s]+:[%s]0") then + return {battery_state["unknown"], 0, "N/A"} + end + + + -- Get capacity and charge information + local capacity = string.match(statefile, "max_charge[%s]+:[%s]([%d]+).*") + local remaining = string.match(statefile, "charge[%s]+:[%s]([%d]+).*") + + -- Calculate percentage + local percent = math.min(math.floor(remaining / capacity * 100), 100) + + + -- Get timer information + local timer = string.match(statefile, "time rem%.[%s]+:[%s]([%d]+).*") + if timer == "0" then return {battery_state["full"], percent, "N/A"} end + + -- Get state information + local state = string.match(statefile, "flags[%s]+:[%s]([%d]+).*") + local state = battery_state[state] or battery_state["unknown"] + + -- Calculate remaining (charging or discharging) time + local hoursleft = math.floor(tonumber(timer) / 3600) + local minutesleft = math.floor((tonumber(timer) / 60) % 60) + local time = string.format("%02d:%02d", hoursleft, minutesleft) + + return {state, percent, time} +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/batproc.lua b/contrib/batproc.lua new file mode 100644 index 0000000..dac4d44 --- /dev/null +++ b/contrib/batproc.lua @@ -0,0 +1,85 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. +--------------------------------------------------- + +-- {{{ Grab environment +local tonumber = tonumber +local io = { open = io.open } +local setmetatable = setmetatable +local math = { + min = math.min, + floor = math.floor +} +local string = { + find = string.find, + match = string.match, + format = string.format +} +-- }}} + + +-- Batproc: provides state, charge, and remaining time for a requested battery using procfs +module("vicious.contrib.batproc") + + +-- {{{ Battery widget type +local function worker(format, batid) + local battery_state = { + ["full"] = "↯", + ["unknown"] = "⌁", + ["charged"] = "↯", + ["charging"] = "+", + ["discharging"] = "-" + } + + -- Get /proc/acpi/battery info + local f = io.open("/proc/acpi/battery/"..batid.."/info") + -- Handler for incompetent users + if not f then return {battery_state["unknown"], 0, "N/A"} end + local infofile = f:read("*all") + f:close() + + -- Check if the battery is present + if infofile == nil or string.find(infofile, "present:[%s]+no") then + return {battery_state["unknown"], 0, "N/A"} + end + + -- Get capacity information + local capacity = string.match(infofile, "last full capacity:[%s]+([%d]+).*") + + + -- Get /proc/acpi/battery state + local f = io.open("/proc/acpi/battery/"..batid.."/state") + local statefile = f:read("*all") + f:close() + + -- Get state information + local state = string.match(statefile, "charging state:[%s]+([%a]+).*") + local state = battery_state[state] or battery_state["unknown"] + + -- Get charge information + local rate = string.match(statefile, "present rate:[%s]+([%d]+).*") + local remaining = string.match(statefile, "remaining capacity:[%s]+([%d]+).*") + + + -- Calculate percentage (but work around broken BAT/ACPI implementations) + local percent = math.min(math.floor(remaining / capacity * 100), 100) + + -- 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/contrib/init.lua b/contrib/init.lua new file mode 100644 index 0000000..8df46b5 --- /dev/null +++ b/contrib/init.lua @@ -0,0 +1,22 @@ +--------------------------------------------------- +-- Vicious widgets for the awesome window manager +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. +--------------------------------------------------- + +-- {{{ Configure widgets +require("vicious.contrib.batacpi") +require("vicious.contrib.batpmu") +require("vicious.contrib.batproc") +require("vicious.contrib.mpc") +require("vicious.contrib.netcfg") +require("vicious.contrib.net") +require("vicious.contrib.ossvol") +require("vicious.contrib.pulse") +require("vicious.contrib.rss") +require("vicious.contrib.sensors") +-- }}} + +-- Vicious: widgets for the awesome window manager +module("vicious.contrib") diff --git a/contrib/mpc.lua b/contrib/mpc.lua new file mode 100644 index 0000000..8f1f0a9 --- /dev/null +++ b/contrib/mpc.lua @@ -0,0 +1,47 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. +-- * (c) 2009, Lucas de Vries +--------------------------------------------------- + +-- {{{ Grab environment +local type = type +local io = { popen = io.popen } +local setmetatable = setmetatable +local string = { find = string.find } +local helpers = require("vicious.helpers") +-- }}} + + +-- Mpc: provides the currently playing song in MPD +module("vicious.contrib.mpc") + + +-- {{{ MPC widget type +local function worker(format, warg) + -- Get data from mpd + local f = io.popen("mpc") + local np = f:read("*line") + f:close() + + -- Not installed, + if np == nil or -- off or stoppped. + (string.find(np, "MPD_HOST") or string.find(np, "volume:")) + then + return {"Stopped"} + end + + -- Check if we should scroll, or maybe truncate + if warg then + if type(warg) == "table" then + np = helpers.scroll(np, warg[1], warg[2]) + else + np = helpers.truncate(np, warg) + end + end + + return {helpers.escape(np)} +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/net.lua b/contrib/net.lua new file mode 100644 index 0000000..8f18604 --- /dev/null +++ b/contrib/net.lua @@ -0,0 +1,138 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. +-- * (c) 2009, Henning Glawe +-- * (c) 2009, Lucas de Vries +--------------------------------------------------- + +-- {{{ Grab environment +local pairs = pairs +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.contrib.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, tignorelist) + local args = {} + local tignore = {} + local total_rx = 0 + local total_tx = 0 + local any_up = 0 + + if not tignorelist then + tignorelist = {"lo", "wmaster0"} + end + for k, i in pairs(tignorelist) do + tignore[i] = true + end + + -- 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$")) + + if not tignore[name] then + total_rx = total_rx + recv + total_tx = total_tx + send + end + + 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) + args["{"..name.." carrier}"] = 0 + + 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) + + -- Carrier detection + sysnet = helpers.pathtotable("/sys/class/net/" .. name) + + if sysnet.carrier then + ccarrier = tonumber(sysnet.carrier) + + args["{"..name.." carrier}"] = ccarrier + if ccarrier ~= 0 and not tignore[name] then + any_up = 1 + end + else + args["{"..name.." carrier}"] = 0 + end + end + + -- Store totals + nets[name][1] = recv + nets[name][2] = send + end + end + + helpers.uformat(args, "total rx", total_rx, unit) + helpers.uformat(args, "total tx", total_tx, unit) + + if nets["total"] == nil then + -- Default values on the first run + nets["total"] = {} + + helpers.uformat(args, "total down", 0, unit) + helpers.uformat(args, "total up", 0, unit) + args["{total carrier}"] = 0 + + nets["total"].time = os.time() + else -- Net stats are absolute, substract our last reading + local interval = os.time() - nets["total"].time > 0 and + os.time() - nets["total"].time or 1 + nets["total"].time = os.time() + + local down = (total_rx - nets["total"][1]) / interval + local up = (total_tx - nets["total"][2]) / interval + + helpers.uformat(args, "total down", down, unit) + helpers.uformat(args, "total up", up, unit) + args["{total carrier}"] = any_up + end + + -- Store totals + nets["total"][1] = total_rx + nets["total"][2] = total_tx + + return args +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/netcfg.lua b/contrib/netcfg.lua new file mode 100644 index 0000000..fc22e8d --- /dev/null +++ b/contrib/netcfg.lua @@ -0,0 +1,34 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Radu A. +--------------------------------------------------- + +-- {{{ Grab environment +local io = { popen = io.popen } +local setmetatable = setmetatable +local table = { insert = table.insert } +-- }}} + + +-- Netcfg: provides active netcfg network profiles +module("vicious.contrib.netcfg") + + +-- {{{ Netcfg widget type +local function worker(format) + -- Initialize counters + local profiles = {} + + local f = io.popen("ls -1 /var/run/network/profiles") + for line in f:lines() do + if line ~= nil then + table.insert(profiles, line) + end + end + f:close() + + return profiles +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/ossvol.lua b/contrib/ossvol.lua new file mode 100644 index 0000000..eb14cb8 --- /dev/null +++ b/contrib/ossvol.lua @@ -0,0 +1,53 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Adrian C. +--------------------------------------------------- + +-- {{{ Grab environment +local tonumber = tonumber +local io = { popen = io.popen } +local setmetatable = setmetatable +local string = { match = string.match } +-- }}} + + +-- Ossvol: provides volume levels of requested OSS mixers +module("vicious.contrib.ossvol") + + +-- {{{ Volume widget type +local function worker(format, warg) + if not warg then return end + + local mixer_state = { + ["on"] = "♫", -- "", + ["off"] = "♩" -- "M" + } + + -- Get mixer control contents + local f = io.popen("ossmix -c") + local mixer = f:read("*all") + f:close() + + -- Capture mixer control state + local volu = tonumber(string.match(mixer, warg .. "[%s]([%d%.]+)"))/0.25 + local mute = string.match(mixer, "vol%.mute[%s]([%a]+)") + -- Handle mixers without data + if volu == nil then + return {0, mixer_state["off"]} + end + + -- Handle mixers without mute + if mute == "OFF" and volu == "0" + -- Handle mixers that are muted + or mute == "ON" then + mute = mixer_state["off"] + else + mute = mixer_state["on"] + end + + return {volu, mute} +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/pulse.lua b/contrib/pulse.lua new file mode 100644 index 0000000..5986265 --- /dev/null +++ b/contrib/pulse.lua @@ -0,0 +1,105 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, MrMagne +--------------------------------------------------- +-- Usage example +-- +-- -- Register widget +-- vicious.register(vol, vicious.contrib.pulse, " $1%", 2, "alsa_output.pci-0000_00_1b.0.analog-stereo") +-- -- Register buttons +-- vol:buttons(awful.util.table.join( +-- awful.button({ }, 1, function () awful.util.spawn("pavucontrol") end), +-- awful.button({ }, 4, function () vicious.contrib.pulse.add(5,"alsa_output.pci-0000_00_1b.0.analog-stereo") end), +-- awful.button({ }, 5, function () vicious.contrib.pulse.add(-5,"alsa_output.pci-0000_00_1b.0.analog-stereo") end) +-- )) +--------------------------------------------------- + +-- {{{ Grab environment +local type = type +local tonumber = tonumber +local io = { popen = io.popen } +local setmetatable = setmetatable +local os = { execute = os.execute } +local table = { insert = table.insert } +local string = { + find = string.find, + match = string.match, + format = string.format, + gmatch = string.gmatch +} +-- }}} + + +-- Pulse: provides volume levels of requested pulseaudio sinks +module("vicious.contrib.pulse") + + +-- {{{ Helper function +local function get_sink_name(sink) + -- If no sink is specified take the first one + if sink == nil then + local f = io.popen("pacmd list-sinks | grep name:") + local line = f:read("*all") + f:close() + + sink = string.match(line, "<(.*)>") + -- If sink is an index, retrieve its name + elseif type(sink) == "number" then + local f = io.popen("pacmd list-sinks | grep name:") + local line = f:read("*all") + f:close() + + local sinks = {} + for s in string.gmatch(line, "<(.*)>") do + table.insert(sinks, s) + end + + sink = sinks[sink] + end + + return sink +end +-- }}} + +-- {{{ Pulseaudio widget type +local function worker(format, sink) + sink = get_sink_name(sink) + if sink == nil then return {0} end + + -- Get sink data + local f = io.popen("pacmd dump | grep '\\(set-sink-volume " .. sink.."\\)\\|\\(set-sink-mute "..sink.."\\)'") + local data = f:read("*all") + f:close() + + -- If mute return 0 (not "Mute") so we don't break progressbars + if string.match(data," (yes)\n$") then + return {0} + end + + local vol = tonumber(string.match(data, "(0x[%x]+)")) + if vol == nil then vol = 0 end + + return { vol/0x10000*100 } +end +-- }}} + +-- {{{ Volume control helper +function add(percent, sink) + sink = get_sink_name(sink) + if sink == nil then return end + + local f = io.popen("pacmd dump | grep 'set-sink-volume " .. sink.."'") + local data = f:read("*all") + f:close() + + local initial_vol = tonumber(string.match(data, "(0x[%x]+)")) + local vol = initial_vol + percent/100*0x10000 + if vol > 0x10000 then vol = 0x10000 end + if vol < 0 then vol = 0 end + + local cmd = "pacmd set-sink-volume "..sink..string.format(" 0x%x", vol).." >/dev/null" + os.execute(cmd) +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/rss.lua b/contrib/rss.lua new file mode 100644 index 0000000..bba1bf2 --- /dev/null +++ b/contrib/rss.lua @@ -0,0 +1,67 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2009, olcc +-- +-- This is now a standalone RSS reader for awesome: +-- * http://github.com/olcc/aware +--------------------------------------------------- + +-- {{{ Grab environment +local pairs = pairs +local io = { popen = io.popen } +local setmetatable = setmetatable +-- }}} + + +-- RSS: provides latest world news +module("vicious.contrib.rss") + + +-- {{{ RSS widget type +local function worker(format, input) + -- input: * feed - feed url + -- * object - entity to look for (typically: 'item') + -- * fields - fields to read (example: 'link', 'title', 'description') + -- output: * count - number of entities found + -- * one table for each field, containing wanted values + local feed = input.feed + local object = input.object + local fields = input.fields + + -- Initialise tables + local out = {} + + for _, v in pairs(fields) do + out[v] = {} + end + + -- Initialise variables + local ob = nil + local i,j,k = 1, 1, 0 + local curl = "curl -A 'Mozilla/4.0' -fsm 5 --connect-timeout 3 " + + -- Get the feed + local f = io.popen(curl .. '"' .. feed .. '"') + local feed = f:read("*all") + f:close() + + while true do + i, j, ob = feed.find(feed, "<" .. object .. ">(.-)", i) + if not ob then break end + + for _, v in pairs(fields) do + out[v][k] = ob:match("<" .. v .. ">(.*)") + end + + k = k+1 + i = j+1 + end + + -- Update the entity count + out.count = k + + return out +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/sensors.lua b/contrib/sensors.lua new file mode 100644 index 0000000..45c7d9a --- /dev/null +++ b/contrib/sensors.lua @@ -0,0 +1,68 @@ +--------------------------------------------------- +-- Licensed under the GNU General Public License v2 +-- * (c) 2010, Greg D. +--------------------------------------------------- + +-- {{{ Grab environment +local tonumber = tonumber +local io = { popen = io.popen } +local setmetatable = setmetatable +local table = { insert = table.insert } +local string = { + gsub = string.gsub, + match = string.match +} +-- }}} + + +-- Sensors: provides access to lm_sensors data +module("vicious.contrib.sensors") + + +-- {{{ Split helper function +local function datasplit(str) + -- Splitting strings into associative array + -- with some magic to get the values right. + str = string.gsub(str, "\n", ":") + + local tbl = {} + string.gsub(str, "([^:]*)", function (v) + if string.match(v, ".") then + table.insert(tbl, v) + end + end) + + local assoc = {} + for c = 1, #tbl, 2 do + local k = string.gsub(tbl[c], ".*_", "") + local v = tonumber(string.match(tbl[c+1], "[%d]+")) + assoc[k] = v + end + + return assoc +end +-- }}} + +-- {{{ Sensors widget type +local function worker(format, warg) + -- Get data from all sensors + local f = io.popen("LANG=C sensors -uA") + local lm_sensors = f:read("*all") + f:close() + + local sensor_data = string.gsub( + string.match(lm_sensors, warg..":\n(%s%s.-)\n[^ ]"), " ", "") + + -- One of: crit, max + local divisor = "crit" + local s_data = datasplit(sensor_data) + + if s_data[divisor] and s_data[divisor] > 0 then + s_data.percent = s_data.input / s_data[divisor] * 100 + end + + return {s_data.input, tonumber(s_data.percent)} +end +-- }}} + +setmetatable(_M, { __call = function(_, ...) return worker(...) end })