This commit is contained in:
Jörg Thalheim 2014-01-01 13:35:13 +01:00
commit 080e28504a
15 changed files with 193 additions and 87 deletions

21
CHANGES
View File

@ -2,6 +2,27 @@
Full changelog is available online:
http://git.sysphere.org/vicious/log/?showmsg=1
---------------------------------------------------
6e16a41 bat: fix broken battery remaining time (was always N/A)
bfbc1bd volume: get the normalized volume like alsamixer and DE's indicator
3ef0f11 Next release, tag 2.1.2
946271c bat: expose information on battery wear and tear
7626989 README: write a list of major contributors to the project
c51e13c init: stop allocating reg table functions with properties
52cbb64 Revert "init: emit timeout instead of forced update"
7961ca1 weather: add support for dew point
dc556e5 bat: Use a real minus sign for the charging status
f36997b README: explain wifi abbreviations linp and sign
71ea0f3 README: enable caching in vicious.widgets.cpu example
211d450 init: share timers when possible
75cd103 Revert "mpd: workaround command termination bug in mpd server v0.18"
a6a73f4 mpd: workaround command termination bug in mpd server v0.18
c795642 README: update awesome usage examples for v3.5
563cb6f pkg: revert to pacman as default Arch method, checkupdates optional
01b2302 pkg: use checkupdates on Arch Linux.
2641bf8 contrib: add Open Weather and ATi graphics widget types
0fd4fc5 mboxc: Fixed typo in setmetatable after ported to lua 5.2
052d19e README: explain vicious is a generic WM widget library
bb891d6 Next release, tag 2.1.1
fac688e wifi: add support for /usr/bin binary path
f7fdd90 README: usage examples are for awesome version 3.4
d63343e contrib: add buildbot monitoring widget

40
README
View File

@ -159,11 +159,12 @@ vicious.widgets.uptime
for 5 minutes and 6th for 15 minutes
vicious.widgets.bat
- provides state, charge, and remaining time for a requested battery
- provides state, charge, remaining time and wear for a requested
battery
- takes battery ID as an argument, i.e. "BAT0"
- returns 1st value as state of requested battery, 2nd as charge
level in percent and 3rd as remaining (charging or discharging)
time
level in percent, 3rd as remaining (charging or discharging)
time and 4th as the wear level in percent
vicious.widgets.mem
- provides RAM and Swap usage statistics
@ -218,13 +219,15 @@ vicious.widgets.wifi
- provides wireless information for a requested interface
- takes the network interface as an argument, i.e. "wlan0"
- returns a table with string keys: {ssid}, {mode}, {chan}, {rate},
{link}, {linp} and {sign}
{link}, {linp} (link quality in percent) and {sign} (signal level)
vicious.widgets.mbox
- provides the subject of last e-mail in a mbox file
- takes the full path to the mbox as an argument, or a table with
1st field as path, 2nd as maximum lenght and 3rd (optional) as
widget name - if 3rd field is present scrolling will be used
1st field as path, 2nd as maximum length and 3rd (optional) as
widget name - if 3rd field is present scrolling will be used (note: the
path will be escaped so special variables like ~ will not work, use
os.getenv("HOME").."mail" instead to access environment variables)
- returns 1st value as the subject of the last e-mail
vicious.widgets.mboxc
@ -243,11 +246,14 @@ vicious.widgets.mdir
vicious.widgets.gmail
- provides count of new and subject of last e-mail on Gmail
- takes an (optional) argument, if it's a number subject will be
truncated, if a table, with 1st field as maximum lenght and 2nd
truncated, if a table, with 1st field as maximum length and 2nd
the widget name (i.e. "gmailwidget"), scrolling will be used
- keeps login information in the ~/.netrc file, example:
machine mail.google.com login user password pass
- returns a table with string keys: {count} and {subject}
- to be able to use this widget, make sure in your Gmail account
you disabled "two step verification" (https://support.google.com/accounts/answer/1064203)
and then "allowed access to your account for less secure apps" (https://www.google.com/settings/security/lesssecureapps)
vicious.widgets.org
- provides agenda statistics for Emacs org-mode
@ -282,7 +288,8 @@ vicious.widgets.weather
- provides weather information for a requested station
- takes the ICAO station code as an argument, i.e. "LDRI"
- returns a table with string keys: {city}, {wind}, {windmph},
{windkmh}, {sky}, {weather}, {tempf}, {tempc}, {humid}, {press}
{windkmh}, {sky}, {weather}, {tempf}, {tempc}, {humid}, {dewf},
{dewc}, {press}
vicious.widgets.date
- provides access to os.date, with optional time formatting provided
@ -308,10 +315,10 @@ holds widget types that were obsoleted or rewritten. Contrib widgets
will not be imported by init unless you explicitly enable it, or load
them in your rc.lua.
Richard Kolkovich, a FreeBSD user, published his vicious-fbsd
branch. If you are also a BSD user you can find his work here:
Rudi Siegel, a FreeBSD user, published his FreeBSD branch. If you are
also a BSD user you can find his work here:
- http://git.sigil.org/vicious-fbsd/
- https://bitbucket.org/mutluyum/vicious_bsd/
Some users would like to avoid writing new modules. For them Vicious
kept the old Wicked functionality, possibility to register their own
@ -445,10 +452,12 @@ CPU usage widget
cpuwidget:set_background_color("#494B4F")
cpuwidget:set_color({ type = "linear", from = { 0, 0 }, to = { 50, 0 },
stops = { { 0, "#FF5656" }, { 0.5, "#88A175" }, { 1, "#AECF96" }})
vicious.cache(vicious.widgets.cpu)
vicious.register(cpuwidget, vicious.widgets.cpu, "$1", 3)
- updated every 3 seconds, feeds the graph with total usage
percentage of all CPUs/cores
percentage of all CPUs/cores and enable caching of this widget
type
Format functions
@ -565,9 +574,7 @@ Wicked written by:
Vicious written by:
- Adrian C. (anrxc) <anrxc sysphere.org>
Vicious contributors:
- Michael Unterkalmsteiner <miciu gmx.de>
- Martin Striz <striz raynet.cz>
Vicious major contributors:
- Benedikt Sauer <filmor gmail.com>
- Greg D. <jabbas jabbas.pl>
- Henning Glawe <glaweh debian.org>
@ -576,3 +583,6 @@ Vicious contributors:
- Hagen Schink <troja84 googlemail.com>
- Jörg Thalheim <jthalheim gmail.com>
- Arvydas Sidorenko <asido4 gmail.com>
- Dodo The Last <dodo.the.last gmail.com>
- ...
- Consult git log for a complete list of contributors

View File

@ -76,6 +76,11 @@ vicious.contrib.openweather
- returns a table with string keys: {city}, {wind deg}, {wind aim},
{wind kmh}, {wind mps}, {sky}, {weather}, {temp c}, {humid}, {press}
vicious.contrib.nvsmi
- provides (very basic) information about Nvidia GPU status from SMI
- takes optional card ID as an argument, i.e. "1", or defaults to ID 0
- returns 1st value as temperature of requested graphics device
vicious.contrib.ossvol
-

42
contrib/nvsmi.lua Normal file
View File

@ -0,0 +1,42 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2014, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local string = { match = string.match }
-- }}}
-- nvsmi: provides GPU information from nvidia SMI
-- vicious.contrib.nvsmi
local nvsmi = {}
-- {{{ GPU Information widget type
local function worker(format, warg)
-- Fallback to querying first device
if not warg then warg = "0" end
-- Get data from smi
-- * Todo: support more; MEMORY,UTILIZATION,ECC,POWER,CLOCK,COMPUTE,PIDS,PERFORMANCE
local f = io.popen("nvidia-smi -q -d TEMPERATURE -i " .. warg)
local smi = f:read("*all")
f:close()
-- Not installed
if smi == nil then return {0} end
-- Get temperature information
local _thermal = string.match(smi, "Gpu[%s]+:[%s]([%d]+)[%s]C")
-- Handle devices without data
if _thermal == nil then return {0} end
return {tonumber(_thermal)}
end
-- }}}
return setmetatable(nvsmi, { __call = function(_, ...) return worker(...) end })

View File

@ -18,7 +18,8 @@ local string = {
gmatch = string.gmatch
}
local math = {
floor = math.floor
floor = math.floor,
ceil = math.ceil
}
-- }}}
@ -29,15 +30,19 @@ local pulse = {}
-- {{{ Helper function
local function pacmd(args)
local f = io.popen("pacmd "..args)
local line = f:read("*all")
f:close()
return line
local f = io.popen("pacmd "..args)
if f == nil then
return nil
else
local line = f:read("*all")
f:close()
return line
end
end
local function escape(text)
local special_chars = { ["."] = "%.", ["-"] = "%-" }
return text:gsub("[%.%-]", special_chars)
local special_chars = { ["."] = "%.", ["-"] = "%-" }
return text:gsub("[%.%-]", special_chars)
end
local cached_sinks = {}
@ -47,10 +52,11 @@ local function get_sink_name(sink)
local key = sink or 1
-- Cache requests
if not cached_sinks[key] then
local line = pacmd("list-sinks")
for s in string.gmatch(line, "name: <(.-)>") do
table.insert(cached_sinks, s)
end
local line = pacmd("list-sinks")
if line == nil then return nil end
for s in string.gmatch(line, "name: <(.-)>") do
table.insert(cached_sinks, s)
end
end
return cached_sinks[key]
@ -66,10 +72,11 @@ local function worker(format, sink)
-- Get sink data
local data = pacmd("dump")
if sink == nil then return {0, "unknown"} end
-- If mute return 0 (not "Mute") so we don't break progressbars
if string.find(data,"set%-sink%-mute "..escape(sink).." yes") then
return {0, "off"}
return {0, "off"}
end
local vol = tonumber(string.match(data, "set%-sink%-volume "..escape(sink).." (0x[%x]+)"))
@ -93,6 +100,8 @@ function pulse.add(percent, sink)
if vol > 0x10000 then vol = 0x10000 end
if vol < 0 then vol = 0 end
vol = math.ceil(vol)
local cmd = string.format("pacmd set-sink-volume %s 0x%x >/dev/null", sink, vol)
return os.execute(cmd)
end

View File

@ -96,6 +96,15 @@ function helpers.escape(text)
end
-- }}}
-- {{{ Escape a string for save usage on the command line
function helpers.shellquote(s)
if s == nil then return "" end
-- use single quotes, and put single quotes into double quotes
-- the string $'b is then quoted as '$'"'"'b'"'"'
return "'" .. s:gsub("'", "'\"'\"'") .. "'"
end
-- }}}
-- {{{ Capitalize a string
function helpers.capitalize(text)
return text and text:gsub("([%w])([%w]*)", function(c, s)

View File

@ -10,7 +10,7 @@
local type = type
local pairs = pairs
local tonumber = tonumber
local capi = { timer = timer }
local timer = (type(timer) == 'table' and timer or require("gears.timer"))
local os = { time = os.time }
local table = {
insert = table.insert,
@ -116,7 +116,7 @@ local function regregister(reg)
-- Start the timer
if reg.timer > 0 then
local tm = timers[reg.timer] and timers[reg.timer].timer
tm = tm or capi.timer({ timeout = reg.timer })
tm = tm or timer({ timeout = reg.timer })
if tm.connect_signal then
tm:connect_signal("timeout", reg.update)
else
@ -131,7 +131,7 @@ local function regregister(reg)
tm:start()
end
-- Initial update
tm:emit_signal("timeout")
reg.update()
end
reg.running = true
end
@ -151,12 +151,11 @@ function vicious.register(widget, wtype, format, timer, warg)
timer = timer,
warg = warg,
widget = widget,
-- Update function
update = function ()
update(widget, reg)
end,
}
-- Set functions
reg.update = function ()
update(widget, reg)
end
-- Default to 2s timer
if reg.timer == nil then

View File

@ -1,6 +1,7 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2013, NormalRa <normalrawr@gmail.com>
---------------------------------------------------
-- {{{ Grab environment
@ -15,7 +16,7 @@ local math = {
-- }}}
-- Bat: provides state, charge, and remaining time for a requested battery
-- Bat: provides state, charge, remaining time, and wear for a requested battery
-- vicious.widgets.bat
local bat = {}
@ -30,12 +31,12 @@ local function worker(format, warg)
["Unknown\n"] = "",
["Charged\n"] = "",
["Charging\n"] = "+",
["Discharging\n"] = "-"
["Discharging\n"] = ""
}
-- Check if the battery is present
if battery.present ~= "1\n" then
return {battery_state["Unknown\n"], 0, "N/A"}
return {battery_state["Unknown\n"], 0, "N/A", 0}
end
@ -45,14 +46,17 @@ local function worker(format, warg)
-- Get capacity information
if battery.charge_now then
remaining, capacity = battery.charge_now, battery.charge_full
capacity_design = battery.charge_full_design or capacity
elseif battery.energy_now then
remaining, capacity = battery.energy_now, battery.energy_full
capacity_design = battery.energy_full_design or capacity
else
return {battery_state["Unknown\n"], 0, "N/A"}
return {battery_state["Unknown\n"], 0, "N/A", 0}
end
-- Calculate percentage (but work around broken BAT/ACPI implementations)
-- Calculate capacity and wear percentage (but work around broken BAT/ACPI implementations)
local percent = math.min(math.floor(remaining / capacity * 100), 100)
local wear = math.floor(100 - capacity / capacity_design * 100)
-- Get charge information
@ -61,7 +65,7 @@ local function worker(format, warg)
elseif battery.power_now then
rate = tonumber(battery.power_now)
else
return {state, percent, "N/A"}
return {state, percent, "N/A", wear}
end
-- Calculate remaining (charging or discharging) time
@ -70,10 +74,10 @@ local function worker(format, warg)
if rate ~= nil and rate ~= 0 then
if state == "+" then
timeleft = (tonumber(capacity) - tonumber(remaining)) / tonumber(rate)
elseif state == "-" then
elseif state == "" then
timeleft = tonumber(remaining) / tonumber(rate)
else
return {state, percent, time}
return {state, percent, time, wear}
end
-- Calculate time
@ -83,7 +87,7 @@ local function worker(format, warg)
time = string.format("%02d:%02d", hoursleft, minutesleft)
end
return {state, percent, time}
return {state, percent, time, wear}
end
-- }}}

View File

@ -27,12 +27,12 @@ local function worker(format, warg)
if warg then warg = "" else warg = "-l" end
local fs_info = {} -- Get data from df
local f = io.popen("LC_ALL=C df -kP " .. warg)
local f = io.popen("LC_ALL=C df -kP " .. helpers.shellquote(warg))
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]+)")
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)

View File

@ -10,7 +10,6 @@ local io = { popen = io.popen }
local setmetatable = setmetatable
local helpers = require("vicious.helpers")
local string = {
find = string.find,
match = string.match
}
-- }}}
@ -23,18 +22,9 @@ local 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"
--},
inbox = "https://mail.google.com/mail/feed/atom",
unread = "https://mail.google.com/mail/feed/atom/unread",
--labelname = "https://mail.google.com/mail/feed/atom/labelname",
}
-- Default is just Inbox
@ -49,31 +39,35 @@ local mail = {
-- {{{ Gmail widget type
local function worker(format, warg)
-- Get info from the Gmail atom feed
local f = io.popen("curl --connect-timeout 1 -m 3 -fsn " .. feed[1])
local f = io.popen("curl --connect-timeout 1 -m 3 -fsn " .. feed)
-- 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}"]
local xml = f:read(2000)
-- 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
if xml ~= nil then
return mail
end
mail["{count}"] = -- Count comes before messages and matches at least 0
tonumber(string.match(xml, "<fullcount>([%d]+)</fullcount>")) or mail["{count}"]
-- Find subject tag
local title = string.match(xml, "<entry>.-<title>(.-)</title>")
if title ~= nil 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)
end
f:close()
return mail

View File

@ -8,6 +8,7 @@ local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local string = { gmatch = string.gmatch }
local helpers = require("vicious.helpers")
-- }}}
@ -22,7 +23,8 @@ local function worker(format, warg)
if warg == nil then warg = 7634 end
local hdd_temp = {} -- Get info from the hddtemp daemon
local f = io.popen("echo | curl --connect-timeout 1 -fsm 3 telnet://127.0.0.1:"..warg)
local quoted = helpers.shellquote(warg)
local f = io.popen("echo | curl --connect-timeout 1 -fsm 3 telnet://127.0.0.1:"..quoted)
for line in f:lines() do
for d, t in string.gmatch(line, "|([%/%a%d]+)|.-|([%d]+)|[CF]+|") do

View File

@ -7,6 +7,7 @@
-- {{{ Grab environment
local io = { popen = io.popen }
local setmetatable = setmetatable
local helpers = require("vicious.helpers")
-- }}}
@ -23,13 +24,14 @@ local function worker(format, warg)
local count = { new = 0, cur = 0 }
for i=1, #warg do
quoted_path = helpers.shellquote(warg[i])
-- Recursively find new messages
local f = io.popen("find '"..warg[i].."' -type f -wholename '*/new/*'")
local f = io.popen("find "..quoted_path.." -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]*$'")
local f = io.popen("find "..quoted_path.." -type f -regex '.*/cur/.*2,[^S]*$'")
for line in f:lines() do count.cur = count.cur + 1 end
f:close()
end

View File

@ -8,6 +8,7 @@ local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local string = { match = string.match }
local helpers = require("vicious.helpers")
-- }}}
@ -26,7 +27,7 @@ local function worker(format, warg)
}
-- Get mixer control contents
local f = io.popen("amixer get " .. warg)
local f = io.popen("amixer -M get " .. helpers.shellquote(warg))
local mixer = f:read("*all")
f:close()

View File

@ -28,6 +28,8 @@ local _weather = {
["{weather}"] = "N/A",
["{tempf}"] = "N/A",
["{tempc}"] = "N/A",
["{dewf}"] = "N/A",
["{dewc}"] = "N/A",
["{humid}"] = "N/A",
["{press}"] = "N/A"
}
@ -38,8 +40,8 @@ local function worker(format, warg)
-- 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..warg..".TXT")
local url = "http://weather.noaa.gov/pub/data/observations/metar/decoded/"..warg
local f = io.popen("curl --connect-timeout 1 -fsm 3 "..helpers.shellquote(url)..".TXT")
local ws = f:read("*all")
f:close()
@ -58,6 +60,8 @@ local function worker(format, warg)
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["{dewf}"] = -- Dew Point in fahrenheit
string.match(ws, "Dew[%s]Point:[%s]([%-]?[%d%.]+).*[%c]") or _weather["{dewf}"]
_weather["{humid}"] = -- Relative humidity in percent
string.match(ws, "Relative[%s]Humidity:[%s]([%d]+)%%") or _weather["{humid}"]
_weather["{press}"] = -- Pressure in hPa
@ -71,6 +75,10 @@ local function worker(format, warg)
if _weather["{tempf}"] ~= "N/A" then
_weather["{tempf}"] = tonumber(_weather["{tempf}"])
_weather["{tempc}"] = math.ceil((_weather["{tempf}"] - 32) * 5/9)
end -- Dew Point in °C if °F was available
if _weather["{dewf}"] ~= "N/A" then
_weather["{dewf}"] = tonumber(_weather["{dewf}"])
_weather["{dewc}"] = math.ceil((_weather["{dewf}"] - 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}"])

View File

@ -58,7 +58,7 @@ local function worker(format, warg)
end
-- Get data from iwconfig where available
local f = io.popen(iwconfig .." ".. warg .. " 2>&1")
local f = io.popen(iwconfig .." ".. helpers.shellquote(warg) .. " 2>&1")
local iw = f:read("*all")
f:close()