This commit is contained in:
Derek Taylor
2025-12-03 11:18:49 -06:00
parent 1764608c79
commit 777960ac7b
758 changed files with 6 additions and 37421 deletions

View File

@@ -1,54 +0,0 @@
--[[
Licensed under GNU General Public License v2
* (c) 2013, Luke Bonham
* (c) 2010, Adrian C. <anrxc@sysphere.org>
--]]
local helpers = require("lain.helpers")
local shell = require("awful.util").shell
local wibox = require("wibox")
local string = { match = string.match,
format = string.format }
-- ALSA volume
-- lain.widget.alsa
local function factory(args)
local alsa = { widget = wibox.widget.textbox() }
local args = args or {}
local timeout = args.timeout or 5
local settings = args.settings or function() end
alsa.cmd = args.cmd or "amixer"
alsa.channel = args.channel or "Master"
alsa.togglechannel = args.togglechannel
local format_cmd = string.format("%s get %s", alsa.cmd, alsa.channel)
if alsa.togglechannel then
format_cmd = { shell, "-c", string.format("%s get %s; %s get %s",
alsa.cmd, alsa.channel, alsa.cmd, alsa.togglechannel) }
end
alsa.last = {}
function alsa.update()
helpers.async(format_cmd, function(mixer)
local l,s = string.match(mixer, "([%d]+)%%.*%[([%l]*)")
if alsa.last.level ~= l or alsa.last.status ~= s then
volume_now = { level = l, status = s }
widget = alsa.widget
settings()
alsa.last = volume_now
end
end)
end
helpers.newtimer(string.format("alsa-%s-%s", alsa.cmd, alsa.channel), timeout, alsa.update)
return alsa
end
return factory

View File

@@ -1,138 +0,0 @@
--[[
Licensed under GNU General Public License v2
* (c) 2013, Luke Bonham
* (c) 2013, Rman
--]]
local helpers = require("lain.helpers")
local awful = require("awful")
local naughty = require("naughty")
local wibox = require("wibox")
local math = { modf = math.modf }
local string = { format = string.format,
match = string.match,
rep = string.rep }
local type, tonumber = type, tonumber
-- ALSA volume bar
-- lain.widget.alsabar
local function factory(args)
local alsabar = {
colors = {
background = "#000000",
mute = "#EB8F8F",
unmute = "#A4CE8A"
},
_current_level = 0,
_playback = "off"
}
local args = args or {}
local timeout = args.timeout or 5
local settings = args.settings or function() end
local width = args.width or 63
local height = args.height or 1
local ticks = args.ticks or false
local ticks_size = args.ticks_size or 7
alsabar.cmd = args.cmd or "amixer"
alsabar.channel = args.channel or "Master"
alsabar.togglechannel = args.togglechannel
alsabar.colors = args.colors or alsabar.colors
alsabar.followtag = args.followtag or false
alsabar.notification_preset = args.notification_preset
if not alsabar.notification_preset then
alsabar.notification_preset = {}
alsabar.notification_preset.font = "Monospace 10"
end
local format_cmd = string.format("%s get %s", alsabar.cmd, alsabar.channel)
if alsabar.togglechannel then
format_cmd = { awful.util.shell, "-c", string.format("%s get %s; %s get %s",
alsabar.cmd, alsabar.channel, alsabar.cmd, alsabar.togglechannel) }
end
alsabar.bar = wibox.widget {
forced_height = height,
forced_width = width,
color = alsabar.colors.unmute,
background_color = alsabar.colors.background,
margins = 1,
paddings = 1,
ticks = ticks,
ticks_size = ticks_size,
widget = wibox.widget.progressbar
}
alsabar.tooltip = awful.tooltip({ objects = { alsabar.bar } })
function alsabar.update(callback)
helpers.async(format_cmd, function(mixer)
local vol, playback = string.match(mixer, "([%d]+)%%.*%[([%l]*)")
if not vol or not playback then return end
if vol ~= alsabar._current_level or playback ~= alsabar._playback then
alsabar._current_level = tonumber(vol)
alsabar.bar:set_value(alsabar._current_level / 100)
if alsabar._current_level == 0 or playback == "off" then
alsabar._playback = playback
alsabar.tooltip:set_text("[Muted]")
alsabar.bar.color = alsabar.colors.mute
else
alsabar._playback = "on"
alsabar.tooltip:set_text(string.format("%s: %s", alsabar.channel, vol))
alsabar.bar.color = alsabar.colors.unmute
end
volume_now = {
level = alsabar._current_level,
status = alsabar._playback
}
settings()
if type(callback) == "function" then callback() end
end
end)
end
function alsabar.notify()
alsabar.update(function()
local preset = alsabar.notification_preset
preset.title = string.format("%s - %s%%", alsabar.channel, alsabar._current_level)
if alsabar._playback == "off" then
preset.title = preset.title .. " Muted"
end
int = math.modf((alsabar._current_level / 100) * awful.screen.focused().mywibox.height)
preset.text = string.format("[%s%s]", string.rep("|", int),
string.rep(" ", awful.screen.focused().mywibox.height - int))
if alsabar.followtag then preset.screen = awful.screen.focused() end
if not alsabar.notification then
alsabar.notification = naughty.notify {
preset = preset,
destroy = function() alsabar.notification = nil end
}
else
naughty.replace_text(alsabar.notification, preset.title, preset.text)
end
end)
end
helpers.newtimer(string.format("alsabar-%s-%s", alsabar.cmd, alsabar.channel), timeout, alsabar.update)
return alsabar
end
return factory

View File

@@ -1,180 +0,0 @@
--[[
Licensed under GNU General Public License v2
* (c) 2013, Luke Bonham
* (c) 2010-2012, Peter Hofmann
--]]
local first_line = require("lain.helpers").first_line
local newtimer = require("lain.helpers").newtimer
local naughty = require("naughty")
local wibox = require("wibox")
local math = { abs = math.abs,
floor = math.floor,
log10 = math.log10,
min = math.min }
local string = { format = string.format }
local ipairs = ipairs
local tonumber = tonumber
-- Battery infos
-- lain.widget.bat
local function factory(args)
local bat = { widget = wibox.widget.textbox() }
local args = args or {}
local timeout = args.timeout or 30
local batteries = args.batteries or (args.battery and {args.battery}) or {"BAT1"}
local ac = args.ac or "AC0"
local notify = args.notify or "on"
local n_perc = args.n_perc or { 5, 15 }
local settings = args.settings or function() end
bat_notification_critical_preset = {
title = "Battery exhausted",
text = "Shutdown imminent",
timeout = 15,
fg = "#000000",
bg = "#FFFFFF"
}
bat_notification_low_preset = {
title = "Battery low",
text = "Plug the cable!",
timeout = 15,
fg = "#202020",
bg = "#CDCDCD"
}
bat_now = {
status = "N/A",
ac_status = "N/A",
perc = "N/A",
time = "N/A",
watt = "N/A"
}
bat_now.n_status = {}
bat_now.n_perc = {}
for i = 1, #batteries do
bat_now.n_status[i] = "N/A"
bat_now.n_perc[i] = 0
end
function bat.update()
local sum_rate_current = 0
local sum_rate_voltage = 0
local sum_rate_power = 0
local sum_rate_energy = 0
local sum_energy_now = 0
local sum_energy_full = 0
local pspath = "/sys/class/power_supply/"
for i, battery in ipairs(batteries) do
local bstr = pspath .. battery
local present = first_line(bstr .. "/present")
if tonumber(present) == 1 then
-- current_now(I)[uA], voltage_now(U)[uV], power_now(P)[uW]
local rate_current = tonumber(first_line(bstr .. "/current_now"))
local rate_voltage = tonumber(first_line(bstr .. "/voltage_now"))
local rate_power = tonumber(first_line(bstr .. "/power_now"))
-- energy_now(P)[uWh], charge_now(I)[uAh]
local energy_now = tonumber(first_line(bstr .. "/energy_now") or
first_line(bstr .. "/charge_now"))
-- energy_full(P)[uWh], charge_full(I)[uAh]
local energy_full = tonumber(first_line(bstr .. "/energy_full") or
first_line(bstr .. "/charge_full"))
local energy_percentage = tonumber(first_line(bstr .. "/capacity")) or
math.floor((energy_now / energy_full) * 100)
bat_now.n_status[i] = first_line(bstr .. "/status") or "N/A"
bat_now.n_perc[i] = energy_percentage or bat_now.n_perc[i]
sum_rate_current = sum_rate_current + (rate_current or 0)
sum_rate_voltage = sum_rate_voltage + (rate_voltage or 0)
sum_rate_power = sum_rate_power + (rate_power or 0)
sum_rate_energy = sum_rate_energy + (rate_power or (((rate_voltage or 0) * (rate_current or 0)) / 1e6))
sum_energy_now = sum_energy_now + (energy_now or 0)
sum_energy_full = sum_energy_full + (energy_full or 0)
end
end
-- When one of the battery is charging, others' status are either
-- "Full", "Unknown" or "Charging". When the laptop is not plugged in,
-- one or more of the batteries may be full, but only one battery
-- discharging suffices to set global status to "Discharging".
bat_now.status = bat_now.n_status[1]
for _,status in ipairs(bat_now.n_status) do
if status == "Discharging" or status == "Charging" then
bat_now.status = status
end
end
bat_now.ac_status = tonumber(first_line(string.format("%s%s/online", pspath, ac))) or "N/A"
if bat_now.status ~= "N/A" then
if bat_now.status ~= "Full" and sum_rate_power == 0 and bat_now.ac_status == 1 then
bat_now.perc = math.floor(math.min(100, (sum_energy_now / sum_energy_full) * 100))
bat_now.time = "00:00"
bat_now.watt = 0
-- update {perc,time,watt} iff battery not full and rate > 0
elseif bat_now.status ~= "Full" then
local rate_time = 0
-- Calculate time and watt if rates are greater then 0
if (sum_rate_power > 0 or sum_rate_current > 0) then
local div = (sum_rate_power > 0 and sum_rate_power) or sum_rate_current
if bat_now.status == "Charging" then
rate_time = (sum_energy_full - sum_energy_now) / div
else -- Discharging
rate_time = sum_energy_now / div
end
if 0 < rate_time and rate_time < 0.01 then -- check for magnitude discrepancies (#199)
rate_time_magnitude = math.abs(math.floor(math.log10(rate_time)))
rate_time = rate_time * 10^(rate_time_magnitude - 2)
end
end
local hours = math.floor(rate_time)
local minutes = math.floor((rate_time - hours) * 60)
bat_now.perc = math.floor(math.min(100, (sum_energy_now / sum_energy_full) * 100))
bat_now.time = string.format("%02d:%02d", hours, minutes)
bat_now.watt = tonumber(string.format("%.2f", sum_rate_energy / 1e6))
elseif bat_now.status == "Full" then
bat_now.perc = 100
bat_now.time = "00:00"
bat_now.watt = 0
end
end
widget = bat.widget
settings()
-- notifications for critical and low levels
if notify == "on" and bat_now.status == "Discharging" then
if tonumber(bat_now.perc) <= n_perc[1] then
bat.id = naughty.notify({
preset = bat_notification_critical_preset,
replaces_id = bat.id
}).id
elseif tonumber(bat_now.perc) <= n_perc[2] then
bat.id = naughty.notify({
preset = bat_notification_low_preset,
replaces_id = bat.id
}).id
end
end
end
newtimer("batteries", timeout, bat.update)
return bat
end
return factory

View File

@@ -1,127 +0,0 @@
--[[
Licensed under GNU General Public License v2
* (c) 2013, Luke Bonham
--]]
local helpers = require("lain.helpers")
local markup = require("lain.util.markup")
local awful = require("awful")
local naughty = require("naughty")
local mouse = mouse
local os = { date = os.date }
local string = { format = string.format,
gsub = string.gsub }
local ipairs = ipairs
local tonumber = tonumber
local setmetatable = setmetatable
-- Calendar notification
-- lain.widget.calendar
local calendar = { offset = 0 }
function calendar.hide()
if not calendar.notification then return end
naughty.destroy(calendar.notification)
calendar.notification = nil
end
function calendar.show(t_out, inc_offset, scr)
local f, offs = nil, inc_offset or 0
calendar.notification_preset.screen = scr or (calendar.followtag and awful.screen.focused()) or 1
calendar.offset = calendar.offset + offs
local current_month = (offs == 0 or calendar.offset == 0)
if current_month then -- today highlighted
calendar.offset = 0
calendar.icon = calendar.icons:len() > 0 and string.format("%s%s.png", calendar.icons, tonumber(os.date("%d")))
f = calendar.cal
else -- no current month showing, no day to highlight
local month = tonumber(os.date("%m"))
local year = tonumber(os.date("%Y"))
month = month + calendar.offset
while month > 12 do
month = month - 12
year = year + 1
end
while month < 1 do
month = month + 12
year = year - 1
end
calendar.icon = nil
f = string.format("%s %s %s", calendar.cal, month, year)
end
helpers.async(f, function(ws)
local fg, bg = calendar.notification_preset.fg, calendar.notification_preset.bg
calendar.notification_preset.text = ws:gsub("%c%[%d+[m]?%s?%d+%c%[%d+[m]?",
markup.bold(markup.color(bg, fg, os.date("%e")))):gsub("\n*$", "")
local widget_focused = true
if t_out == 0 and mouse.current_widgets then
widget_focused = false
for i, widget in ipairs(calendar.attach_to) do
for _,v in ipairs(mouse.current_widgets) do
if widget == v then
widget_focused = true
break
end
end
end
end
if widget_focused then
calendar.hide()
calendar.notification = naughty.notify({
preset = calendar.notification_preset,
icon = calendar.icon,
timeout = t_out or calendar.notification_preset.timeout or 5
})
end
end)
end
function calendar.hover_on() calendar.show(0) end
function calendar.hover_off() calendar.hide() end
function calendar.prev() calendar.show(0, -1) end
function calendar.next() calendar.show(0, 1) end
function calendar.attach(widget)
widget:connect_signal("mouse::enter", calendar.hover_on)
widget:connect_signal("mouse::leave", calendar.hover_off)
widget:buttons(awful.util.table.join(
awful.button({}, 1, calendar.prev),
awful.button({}, 3, calendar.next),
awful.button({}, 2, calendar.hover_on),
awful.button({}, 4, calendar.prev),
awful.button({}, 5, calendar.next)))
end
local function factory(args)
local args = args or {}
calendar.cal = args.cal or "/usr/bin/cal"
calendar.attach_to = args.attach_to or {}
calendar.followtag = args.followtag or false
calendar.icons = args.icons or helpers.icons_dir .. "cal/white/"
calendar.notification_preset = args.notification_preset
if not calendar.notification_preset then
calendar.notification_preset = {
font = "Monospace 10",
fg = "#FFFFFF",
bg = "#000000"
}
end
for i, widget in ipairs(calendar.attach_to) do calendar.attach(widget) end
end
return setmetatable(calendar, { __call = function(_, ...) return factory(...) end })

View File

@@ -1,18 +0,0 @@
--[[
Lain
Layouts, widgets and utilities for Awesome WM
Users contributed widgets section
Licensed under GNU General Public License v2
* (c) 2013, Luke Bonham
--]]
local wrequire = require("lain.helpers").wrequire
local setmetatable = setmetatable
local widget = { _NAME = "lain.widget.contrib" }
return setmetatable(widget, { __index = wrequire })

View File

@@ -1,77 +0,0 @@
--[[
Licensed under GNU General Public License v2
* (c) 2015, Dario Gjorgjevski
--]]
local helpers = require("lain.helpers")
local awful = require("awful")
local wibox = require("wibox")
local string = { format = string.format,
match = string.match }
local execute = os.execute
local setmetatable = setmetatable
-- Keyboard layout switcher
-- lain.widget.contrib.kblayout
local function factory(args)
local kbdlayout = { widget = wibox.widget.textbox() }
local args = args or {}
local layouts = args.layouts or {}
local settings = args.settings or function () end
local add_us_secondary = true
local timeout = args.timeout or 5
local idx = 1
if args.add_us_secondary == false then add_us_secondary = false end
local function kbd_run_settings(layout, variant)
kbdlayout_now = {
layout = string.match(layout, "[^,]+"), -- Make sure to match the primary layout only.
variant = variant
}
widget = kbdlayout.widget
settings()
end
function kbdlayout.update()
helpers.async("setxkbmap -query", function(status)
kbd_run_settings(string.match(status, "layout:%s*([^\n]*)"),
string.match(status, "variant:%s*([^\n]*)"))
end)
end
function kbdlayout.set(i)
if #layouts == 0 then return end
idx = ((i - 1) % #layouts) + 1 -- Make sure to wrap around as needed.
local to_execute = "setxkbmap " .. layouts[idx].layout
if add_us_secondary and not string.match(layouts[idx].layout, ",?us,?") then
to_execute = to_execute .. ",us"
end
if layouts[idx].variant then
to_execute = to_execute .. " " .. layouts[idx].variant
end
if execute(to_execute) then
kbd_run_settings(layouts[idx].layout, layouts[idx].variant)
end
end
function kbdlayout.next() kbdlayout.set(idx + 1) end
function kbdlayout.prev() kbdlayout.set(idx - 1) end
-- Mouse bindings
kbdlayout.widget:buttons(awful.util.table.join(
awful.button({ }, 1, function () kbdlayout.next() end),
awful.button({ }, 3, function () kbdlayout.prev() end)))
helpers.newtimer("kbdlayout", timeout, kbdlayout.update)
return kbdlayout
end
return factory

View File

@@ -1,97 +0,0 @@
--[[
Licensed under GNU General Public License v2
* (c) 2014, anticlockwise <http://github.com/anticlockwise>
--]]
local helpers = require("lain.helpers")
local shell = require("awful.util").shell
local focused = require("awful.screen").focused
local escape_f = require("awful.util").escape
local naughty = require("naughty")
local wibox = require("wibox")
local os = { getenv = os.getenv }
local string = { format = string.format,
gmatch = string.gmatch }
-- MOC audio player
-- lain.widget.contrib.moc
local function factory(args)
local moc = { widget = wibox.widget.textbox() }
local args = args or {}
local timeout = args.timeout or 2
local music_dir = args.music_dir or os.getenv("HOME") .. "/Music"
local cover_pattern = args.cover_pattern or "*\\.(jpg|jpeg|png|gif)$"
local cover_size = args.cover_size or 100
local default_art = args.default_art or ""
local followtag = args.followtag or false
local settings = args.settings or function() end
moc_notification_preset = { title = "Now playing", timeout = 6 }
helpers.set_map("current moc track", nil)
function moc.update()
helpers.async("mocp -i", function(f)
moc_now = {
state = "N/A",
file = "N/A",
artist = "N/A",
title = "N/A",
album = "N/A",
elapsed = "N/A",
total = "N/A"
}
for line in string.gmatch(f, "[^\n]+") do
for k, v in string.gmatch(line, "([%w]+):[%s](.*)$") do
if k == "State" then moc_now.state = v
elseif k == "File" then moc_now.file = v
elseif k == "Artist" then moc_now.artist = escape_f(v)
elseif k == "SongTitle" then moc_now.title = escape_f(v)
elseif k == "Album" then moc_now.album = escape_f(v)
elseif k == "CurrentTime" then moc_now.elapsed = escape_f(v)
elseif k == "TotalTime" then moc_now.total = escape_f(v)
end
end
end
moc_notification_preset.text = string.format("%s (%s) - %s\n%s", moc_now.artist,
moc_now.album, moc_now.total, moc_now.title)
widget = moc.widget
settings()
if moc_now.state == "PLAY" then
if moc_now.title ~= helpers.get_map("current moc track") then
helpers.set_map("current moc track", moc_now.title)
if followtag then moc_notification_preset.screen = focused() end
local common = {
preset = moc_notification_preset,
icon = default_art,
icon_size = cover_size,
replaces_id = moc.id,
}
local path = string.format("%s/%s", music_dir, string.match(moc_now.file, ".*/"))
local cover = string.format("find '%s' -maxdepth 1 -type f | egrep -i -m1 '%s'", path, cover_pattern)
helpers.async({ shell, "-c", cover }, function(current_icon)
common.icon = current_icon:gsub("\n", "")
moc.id = naughty.notify(common).id
end)
end
elseif moc_now.state ~= "PAUSE" then
helpers.set_map("current moc track", nil)
end
end)
end
moc.timer = helpers.newtimer("moc", timeout, moc.update, true, true)
return moc
end
return factory

View File

@@ -1,53 +0,0 @@
--[[
Licensed under GNU General Public License v2
* (c) 2014, blueluke <http://github.com/blueluke>
--]]
local async = require("lain.helpers").async
local awful = require("awful")
local execute = os.execute
local type = type
-- Redshift
-- lain.widget.contrib.redshift
local redshift = { active = false, pid = nil }
function redshift:start()
execute("pkill redshift")
awful.spawn.with_shell("redshift -x") -- clear adjustments
redshift.pid = awful.spawn.with_shell("redshift")
redshift.active = true
if type(redshift.update_fun) == "function" then
redshift.update_fun(redshift.active)
end
end
function redshift:toggle()
async({ awful.util.shell, "-c", string.format("ps -p %d -o pid=", redshift.pid) }, function(f)
if f and #f > 0 then -- redshift is running
-- Sending -USR1 toggles redshift (See project website)
execute("pkill -USR1 redshift")
redshift.active = not redshift.active
else -- not started or killed, (re)start it
redshift:start()
end
redshift.update_fun(redshift.active)
end)
end
-- Attach to a widget
-- Provides a button which toggles redshift on/off on click
-- @param widget: Widget to attach to.
-- @param fun: Function to be run each time redshift is toggled (optional).
-- Use it to update widget text or icons on status change.
function redshift:attach(widget, fun)
redshift.update_fun = fun or function() end
if not redshift.pid then redshift:start() end
if widget then
widget:buttons(awful.util.table.join(awful.button({}, 1, function () redshift:toggle() end)))
end
end
return redshift

View File

@@ -1,97 +0,0 @@
--[[
Licensed under GNU General Public License v2
* (c) 2013, Jan Xie
--]]
local helpers = require("lain.helpers")
local markup = require("lain.util").markup
local awful = require("awful")
local naughty = require("naughty")
local mouse = mouse
local string = { format = string.format, gsub = string.gsub }
-- Taskwarrior notification
-- lain.widget.contrib.task
local task = {}
function task.hide()
if not task.notification then return end
naughty.destroy(task.notification)
task.notification = nil
end
function task.show(scr)
if task.followtag then
task.notification_preset.screen = awful.screen.focused()
elseif scr then
task.notification_preset.screen = scr
end
helpers.async({ awful.util.shell, "-c", task.show_cmd }, function(f)
local widget_focused = true
if mouse.current_widgets then
widget_focused = false
for _,v in ipairs(mouse.current_widgets) do
if task.widget == v then
widget_focused = true
break
end
end
end
if widget_focused then
task.hide()
task.notification = naughty.notify({
preset = task.notification_preset,
title = "task next",
text = markup.font(task.notification_preset.font,
awful.util.escape(f:gsub("\n*$", "")))
})
end
end)
end
function task.prompt()
awful.prompt.run {
prompt = task.prompt_text,
textbox = awful.screen.focused().mypromptbox.widget,
exe_callback = function(t)
helpers.async(t, function(f)
naughty.notify {
preset = task.notification_preset,
title = t,
text = markup.font(task.notification_preset.font,
awful.util.escape(f:gsub("\n*$", "")))
}
end)
end,
history_path = awful.util.getdir("cache") .. "/history_task"
}
end
function task.attach(widget, args)
local args = args or {}
task.show_cmd = args.show_cmd or "task next"
task.prompt_text = args.prompt_text or "Enter task command: "
task.followtag = args.followtag or false
task.notification_preset = args.notification_preset
task.widget = widget
if not task.notification_preset then
task.notification_preset = {
font = "Monospace 10",
icon = helpers.icons_dir .. "/taskwarrior.png"
}
end
if widget then
widget:connect_signal("mouse::enter", function () task.show() end)
widget:connect_signal("mouse::leave", function () task.hide() end)
end
end
return task

View File

@@ -1,161 +0,0 @@
--[[
tpbat.lua
Battery status widget for ThinkPad laptops that use SMAPI
lain.widget.contrib.tpbat
More on tp_smapi: http://www.thinkwiki.org/wiki/Tp_smapi
Licensed under GNU General Public License v2
* (c) 2013, Conor Heine
* (c) 2013, Luke Bonham
* (c) 2010-2012, Peter Hofmann
--]]
local debug = { getinfo = debug.getinfo }
local newtimer = require("lain.helpers").newtimer
local first_line = require("lain.helpers").first_line
local naughty = require("naughty")
local wibox = require("wibox")
local string = { format = string.format }
local math = { floor = math.floor }
local tostring = tostring
local setmetatable = setmetatable
package.path = debug.getinfo(1,"S").source:match[[^@?(.*[\/])[^\/]-$]] .. "?.lua;" .. package.path
local smapi = require("smapi")
-- ThinkPad SMAPI-enabled battery info widget
-- lain.widget.contrib.tpbat
local tpbat = {}
function tpbat.hide()
if not tpbat.notification then return end
naughty.destroy(tpbat.notification)
tpbat.notification = nil
end
function tpbat.show(t_out)
tpbat.hide()
local bat = tpbat.bat
if bat == nil or not bat:installed() then return end
local t_out = t_out or 0
local mfgr = bat:get('manufacturer') or "no_mfgr"
local model = bat:get('model') or "no_model"
local chem = bat:get('chemistry') or "no_chem"
local status = bat:get('state') or "nil"
local time = bat:remaining_time()
local msg = "\t"
if status ~= "idle" and status ~= "nil" then
if time == "N/A" then
msg = "...Calculating time remaining..."
else
msg = time .. (status == "charging" and " until charged" or " remaining")
end
else
msg = "On AC Power"
end
local str = string.format("%s : %s %s (%s)\n", bat.name, mfgr, model, chem)
.. string.format("\n%s \t\t\t %s", status:upper(), msg)
tpbat.notification = naughty.notify({
text = str,
timeout = t_out,
screen = client.focus and client.focus.screen or 1
})
end
function tpbat.register(args)
local args = args or {}
local timeout = args.timeout or 30
local battery = args.battery or "BAT0"
local settings = args.settings or function() end
tpbat.bat = smapi:battery(battery) -- Create a new battery
local bat = tpbat.bat
tpbat.widget = wibox.widget.textbox()
bat_notification_low_preset = {
title = "Battery low",
text = "Plug the cable!",
timeout = 15,
fg = "#202020",
bg = "#CDCDCD"
}
bat_notification_critical_preset = {
title = "Battery exhausted",
text = "Shutdown imminent",
timeout = 15,
fg = "#000000",
bg = "#FFFFFF"
}
if bat:get('state') == nil
then
local n = naughty.notify({
preset = bat_notification_low_preset,
title = "SMAPI Battery Warning: Unable to read battery state!",
text = "This widget is intended for ThinkPads. Is tp_smapi installed? Check your configs & paths.",
screen = client.focus and client.focus.screen or 1
})
end
function tpbat.update()
bat_now = {
status = "Not present",
perc = "N/A",
time = "N/A",
watt = "N/A"
}
if bat:installed()
then
bat_now.status = bat:status() or "N/A"
bat_now.perc = bat:percent()
bat_now.time = bat:remaining_time()
-- bat_now.watt = string.format("%.2fW", (VOLTS * AMPS) / 1e12)
-- notifications for low and critical states (when discharging)
if bat_now.status == "discharging"
then
if bat_now.perc <= 5
then
tpbat.id = naughty.notify({
preset = bat_notification_critical_preset,
replaces_id = tpbat.id,
screen = client.focus and client.focus.screen or 1
}).id
elseif bat_now.perc <= 15
then
tpbat.id = naughty.notify({
preset = bat_notification_low_preset,
replaces_id = tpbat.id,
screen = client.focus and client.focus.screen or 1
}).id
end
end
bat_now.perc = tostring(bat_now.perc)
end
widget = tpbat.widget
settings()
end
newtimer("tpbat-" .. bat.name, timeout, tpbat.update)
widget:connect_signal('mouse::enter', function () tpbat.show() end)
widget:connect_signal('mouse::leave', function () tpbat.hide() end)
return tpbat
end
return setmetatable(tpbat, { __call = function(_, ...) return tpbat.register(...) end })

View File

@@ -1,98 +0,0 @@
--[[
smapi.lua
Interface with thinkpad battery information
Licensed under GNU General Public License v2
* (c) 2013, Conor Heine
--]]
local first_line = require("lain.helpers").first_line
local string = { format = string.format }
local tonumber = tonumber
local setmetatable = setmetatable
local smapi = {}
local apipath = "/sys/devices/platform/smapi"
-- Most are readable values, but some can be written to (not implemented, yet?)
local readable = {
barcoding = true,
charging_max_current = true,
charging_max_voltage = true,
chemistry = true,
current_avg = true,
current_now = true,
cycle_count = true,
design_capacity = true,
design_voltage = true,
dump = true,
first_use_date = true,
force_discharge = false,
group0_voltage = true,
group1_voltage = true,
group2_voltage = true,
group3_voltage = true,
inhibit_charge_minutes = false,
installed = true,
last_full_capacity = true,
manufacture_date = true,
manufacturer = true,
model = true,
power_avg = true,
power_now = true,
remaining_capacity = true,
remaining_charging_time = true,
remaining_percent = true,
remaining_percent_error = true,
remaining_running_time = true,
remaining_running_time_now = true,
serial = true,
start_charge_thresh = false,
state = true,
stop_charge_thresh = false,
temperature = true,
voltage = true
}
function smapi:battery(name)
local bat = {}
bat.name = name
bat.path = apipath .. "/" .. name
function bat:get(item)
return self.path ~= nil and readable[item] and first_line(self.path .. "/" .. item) or nil
end
function bat:installed()
return self:get("installed") == "1"
end
function bat:status()
return self:get('state')
end
-- Remaining time can either be time until battery dies or time until charging completes
function bat:remaining_time()
local time_val = bat_now.status == 'discharging' and 'remaining_running_time' or 'remaining_charging_time'
local mins_left = self:get(time_val)
if not mins_left:find("^%d+") then return "N/A" end
local hrs = math.floor(mins_left / 60)
local min = mins_left % 60
return string.format("%02d:%02d", hrs, min)
end
function bat:percent()
return tonumber(self:get("remaining_percent"))
end
return setmetatable(bat, {__metatable = false, __newindex = false})
end
return smapi

View File

@@ -1,78 +0,0 @@
--[[
Licensed under GNU General Public License v2
* (c) 2013, Luke Bonham
* (c) 2010-2012, Peter Hofmann
--]]
local helpers = require("lain.helpers")
local wibox = require("wibox")
local math = { ceil = math.ceil }
local string = { format = string.format,
gmatch = string.gmatch }
local tostring = tostring
-- CPU usage
-- lain.widget.cpu
local function factory(args)
local cpu = { core = {}, widget = wibox.widget.textbox() }
local args = args or {}
local timeout = args.timeout or 2
local settings = args.settings or function() end
function cpu.update()
-- Read the amount of time the CPUs have spent performing
-- different kinds of work. Read the first line of /proc/stat
-- which is the sum of all CPUs.
local times = helpers.lines_match("cpu","/proc/stat")
for index,time in pairs(times) do
local coreid = index - 1
local core = cpu.core[coreid] or
{ last_active = 0 , last_total = 0, usage = 0 }
local at = 1
local idle = 0
local total = 0
for field in string.gmatch(time, "[%s]+([^%s]+)") do
-- 4 = idle, 5 = ioWait. Essentially, the CPUs have done
-- nothing during these times.
if at == 4 or at == 5 then
idle = idle + field
end
total = total + field
at = at + 1
end
local active = total - idle
if core.last_active ~= active or core.last_total ~= total then
-- Read current data and calculate relative values.
local dactive = active - core.last_active
local dtotal = total - core.last_total
local usage = math.ceil((dactive / dtotal) * 100)
core.last_active = active
core.last_total = total
core.usage = usage
-- Save current data for the next run.
cpu.core[coreid] = core
end
end
cpu_now = cpu.core
cpu_now.usage = cpu_now[0].usage
widget = cpu.widget
settings()
end
helpers.newtimer("cpu", timeout, cpu.update)
return cpu
end
return factory

View File

@@ -1,123 +0,0 @@
--[[
Licensed under GNU General Public License v2
* (c) 2013, Luke Bonham
--]]
local helpers = require("lain.helpers")
local shell = require("awful.util").shell
local focused = require("awful.screen").focused
local wibox = require("wibox")
local naughty = require("naughty")
local string = string
local tonumber = tonumber
-- File system disk space usage
-- lain.widget.fs
local function factory(args)
local fs = { unit = { ["mb"] = 1024, ["gb"] = 1024^2 }, widget = wibox.widget.textbox() }
function fs.hide()
if not fs.notification then return end
naughty.destroy(fs.notification)
fs.notification = nil
end
function fs.show(seconds, scr)
fs.update()
fs.hide()
if fs.followtag then
fs.notification_preset.screen = focused()
else
fs.notification_preset.screen = scr or 1
end
fs.notification = naughty.notify({
preset = fs.notification_preset,
timeout = seconds or 5
})
end
local args = args or {}
local timeout = args.timeout or 600
local partition = args.partition or "/"
local showpopup = args.showpopup or "on"
local notify = args.notify or "on"
local settings = args.settings or function() end
fs.options = args.options
fs.followtag = args.followtag or false
fs.notification_preset = args.notification_preset
if not fs.notification_preset then
fs.notification_preset = {
font = "Monospace 10",
fg = "#FFFFFF",
bg = "#000000"
}
end
helpers.set_map(partition, false)
function fs.update()
fs_info, fs_now = {}, {}
helpers.async({ shell, "-c", "/usr/bin/env LC_ALL=C df -k --output=target,size,used,avail,pcent" }, function(f)
for line in string.gmatch(f, "\n[^\n]+") do
local m,s,u,a,p = string.match(line, "(/.-%s).-(%d+).-(%d+).-(%d+).-([%d]+)%%")
m = m:gsub(" ", "") -- clean target from any whitespace
fs_info[m .. " size_mb"] = string.format("%.1f", tonumber(s) / fs.unit["mb"])
fs_info[m .. " size_gb"] = string.format("%.1f", tonumber(s) / fs.unit["gb"])
fs_info[m .. " used_mb"] = string.format("%.1f", tonumber(u) / fs.unit["mb"])
fs_info[m .. " used_gb"] = string.format("%.1f", tonumber(u) / fs.unit["gb"])
fs_info[m .. " used_p"] = p
fs_info[m .. " avail_mb"] = string.format("%.1f", tonumber(a) / fs.unit["mb"])
fs_info[m .. " avail_gb"] = string.format("%.1f", tonumber(a) / fs.unit["gb"])
fs_info[m .. " avail_p"] = string.format("%d", 100 - tonumber(p))
end
fs_now.size_mb = fs_info[partition .. " size_mb"] or "N/A"
fs_now.size_gb = fs_info[partition .. " size_gb"] or "N/A"
fs_now.used = fs_info[partition .. " used_p"] or "N/A"
fs_now.used_mb = fs_info[partition .. " used_mb"] or "N/A"
fs_now.used_gb = fs_info[partition .. " used_gb"] or "N/A"
fs_now.available = fs_info[partition .. " avail_p"] or "N/A"
fs_now.available_mb = fs_info[partition .. " avail_mb"] or "N/A"
fs_now.available_gb = fs_info[partition .. " avail_gb"] or "N/A"
notification_preset = fs.notification_preset
widget = fs.widget
settings()
if notify == "on" and tonumber(fs_now.used) and tonumber(fs_now.used) >= 99 and not helpers.get_map(partition) then
naughty.notify({
preset = naughty.config.presets.critical,
title = "Warning",
text = partition .. " is full",
})
helpers.set_map(partition, true)
else
helpers.set_map(partition, false)
end
end)
local notifycmd = (fs.options and string.format("dfs %s", fs.options)) or "dfs"
helpers.async(helpers.scripts_dir .. notifycmd, function(ws)
fs.notification_preset.text = ws:gsub("\n*$", "")
end)
end
if showpopup == "on" then
fs.widget:connect_signal('mouse::enter', function () fs.show(0) end)
fs.widget:connect_signal('mouse::leave', function () fs.hide() end)
end
helpers.newtimer(partition, timeout, fs.update)
return fs
end
return factory

View File

@@ -1,84 +0,0 @@
--[[
Licensed under GNU General Public License v2
* (c) 2013, Luke Bonham
--]]
local helpers = require("lain.helpers")
local naughty = require("naughty")
local wibox = require("wibox")
local string = { format = string.format,
gsub = string.gsub }
local type = type
local tonumber = tonumber
-- Mail IMAP check
-- lain.widget.imap
local function factory(args)
local imap = { widget = wibox.widget.textbox() }
local args = args or {}
local server = args.server
local mail = args.mail
local password = args.password
local port = args.port or 993
local timeout = args.timeout or 60
local is_plain = args.is_plain or false
local followtag = args.followtag or false
local notify = args.notify or "on"
local settings = args.settings or function() end
local head_command = "curl --connect-timeout 3 -fsm 3"
local request = "-X 'SEARCH (UNSEEN)'"
if not server or not mail or not password then return end
helpers.set_map(mail, 0)
if not is_plain then
if type(password) == "string" or type(password) == "table" then
helpers.async(password, function(f) password = f:gsub("\n", "") end)
elseif type(password) == "function" then
local p = password()
end
end
function update()
mail_notification_preset = {
icon = helpers.icons_dir .. "mail.png",
position = "top_left"
}
if followtag then
mail_notification_preset.screen = awful.screen.focused()
end
curl = string.format("%s --url imaps://%s:%s/INBOX -u %s:%q %s -k",
head_command, server, port, mail, password, request)
helpers.async(curl, function(f)
_, mailcount = string.gsub(f, "%d+", "")
widget = imap.widget
settings()
if notify == "on" and mailcount >= 1 and mailcount > helpers.get_map(mail) then
if mailcount == 1 then
nt = mail .. " has one new message"
else
nt = mail .. " has <b>" .. mailcount .. "</b> new messages"
end
naughty.notify { preset = mail_notification_preset, text = nt }
end
helpers.set_map(mail, mailcount)
end)
end
imap.timer = helpers.newtimer(mail, timeout, update, true, true)
return imap
end
return factory

View File

@@ -1,19 +0,0 @@
--[[
Lain
Layouts, widgets and utilities for Awesome WM
Widgets section
Licensed under GNU General Public License v2
* (c) 2013, Luke Bonham
* (c) 2010-2012, Peter Hofmann
--]]
local wrequire = require("lain.helpers").wrequire
local setmetatable = setmetatable
local widget = { _NAME = "lain.widget" }
return setmetatable(widget, { __index = wrequire })

View File

@@ -1,50 +0,0 @@
--[[
Licensed under GNU General Public License v2
* (c) 2013, Luke Bonham
* (c) 2010-2012, Peter Hofmann
--]]
local helpers = require("lain.helpers")
local wibox = require("wibox")
local gmatch, lines, floor = string.gmatch, io.lines, math.floor
-- Memory usage (ignoring caches)
-- lain.widget.mem
local function factory(args)
local mem = { widget = wibox.widget.textbox() }
local args = args or {}
local timeout = args.timeout or 2
local settings = args.settings or function() end
function mem.update()
mem_now = {}
for line in lines("/proc/meminfo") do
for k, v in gmatch(line, "([%a]+):[%s]+([%d]+).+") do
if k == "MemTotal" then mem_now.total = floor(v / 1024 + 0.5)
elseif k == "MemFree" then mem_now.free = floor(v / 1024 + 0.5)
elseif k == "Buffers" then mem_now.buf = floor(v / 1024 + 0.5)
elseif k == "Cached" then mem_now.cache = floor(v / 1024 + 0.5)
elseif k == "SwapTotal" then mem_now.swap = floor(v / 1024 + 0.5)
elseif k == "SwapFree" then mem_now.swapf = floor(v / 1024 + 0.5)
elseif k == "SReclaimable" then mem_now.srec = floor(v / 1024 + 0.5)
end
end
end
mem_now.used = mem_now.total - mem_now.free - mem_now.buf - mem_now.cache - mem_now.srec
mem_now.swapused = mem_now.swap - mem_now.swapf
mem_now.perc = math.floor(mem_now.used / mem_now.total * 100)
widget = mem.widget
settings()
end
helpers.newtimer("mem", timeout, mem.update)
return mem
end
return factory

View File

@@ -1,134 +0,0 @@
--[[
Licensed under GNU General Public License v2
* (c) 2013, Luke Bonham
* (c) 2010, Adrian C. <anrxc@sysphere.org>
--]]
local helpers = require("lain.helpers")
local shell = require("awful.util").shell
local escape_f = require("awful.util").escape
local focused = require("awful.screen").focused
local naughty = require("naughty")
local wibox = require("wibox")
local os = { getenv = os.getenv }
local string = { format = string.format,
gmatch = string.gmatch,
match = string.match }
-- MPD infos
-- lain.widget.mpd
local function factory(args)
local mpd = { widget = wibox.widget.textbox() }
local args = args or {}
local timeout = args.timeout or 2
local password = (args.password and #args.password > 0 and string.format("password %s\\n", args.password)) or ""
local host = args.host or os.getenv("MPD_HOST") or "127.0.0.1"
local port = args.port or os.getenv("MPD_PORT") or "6600"
local music_dir = args.music_dir or os.getenv("HOME") .. "/Music"
local cover_pattern = args.cover_pattern or "*\\.(jpg|jpeg|png|gif)$"
local cover_size = args.cover_size or 100
local default_art = args.default_art
local notify = args.notify or "on"
local followtag = args.followtag or false
local settings = args.settings or function() end
local mpdh = string.format("telnet://%s:%s", host, port)
local echo = string.format("printf \"%sstatus\\ncurrentsong\\nclose\\n\"", password)
local cmd = string.format("%s | curl --connect-timeout 1 -fsm 3 %s", echo, mpdh)
mpd_notification_preset = { title = "Now playing", timeout = 6 }
helpers.set_map("current mpd track", nil)
function mpd.update()
helpers.async({ shell, "-c", cmd }, function(f)
mpd_now = {
random_mode = false,
single_mode = false,
repeat_mode = false,
consume_mode = false,
pls_pos = "N/A",
pls_len = "N/A",
state = "N/A",
file = "N/A",
name = "N/A",
artist = "N/A",
title = "N/A",
album = "N/A",
genre = "N/A",
track = "N/A",
date = "N/A",
time = "N/A",
elapsed = "N/A"
}
for line in string.gmatch(f, "[^\n]+") do
for k, v in string.gmatch(line, "([%w]+):[%s](.*)$") do
if k == "state" then mpd_now.state = v
elseif k == "file" then mpd_now.file = v
elseif k == "Name" then mpd_now.name = escape_f(v)
elseif k == "Artist" then mpd_now.artist = escape_f(v)
elseif k == "Title" then mpd_now.title = escape_f(v)
elseif k == "Album" then mpd_now.album = escape_f(v)
elseif k == "Genre" then mpd_now.genre = escape_f(v)
elseif k == "Track" then mpd_now.track = escape_f(v)
elseif k == "Date" then mpd_now.date = escape_f(v)
elseif k == "Time" then mpd_now.time = v
elseif k == "elapsed" then mpd_now.elapsed = string.match(v, "%d+")
elseif k == "song" then mpd_now.pls_pos = v
elseif k == "playlistlength" then mpd_now.pls_len = v
elseif k == "repeat" then mpd_now.repeat_mode = v ~= "0"
elseif k == "single" then mpd_now.single_mode = v ~= "0"
elseif k == "random" then mpd_now.random_mode = v ~= "0"
elseif k == "consume" then mpd_now.consume_mode = v ~= "0"
end
end
end
mpd_notification_preset.text = string.format("%s (%s) - %s\n%s", mpd_now.artist,
mpd_now.album, mpd_now.date, mpd_now.title)
widget = mpd.widget
settings()
if mpd_now.state == "play" then
if notify == "on" and mpd_now.title ~= helpers.get_map("current mpd track") then
helpers.set_map("current mpd track", mpd_now.title)
if followtag then mpd_notification_preset.screen = focused() end
local common = {
preset = mpd_notification_preset,
icon = default_art,
icon_size = cover_size,
replaces_id = mpd.id
}
if not string.match(mpd_now.file, "http.*://") then -- local file instead of http stream
local path = string.format("%s/%s", music_dir, string.match(mpd_now.file, ".*/"))
local cover = string.format("find '%s' -maxdepth 1 -type f | egrep -i -m1 '%s'",
path:gsub("'", "'\\''"), cover_pattern)
helpers.async({ shell, "-c", cover }, function(current_icon)
common.icon = current_icon:gsub("\n", "")
if #common.icon == 0 then common.icon = nil end
mpd.id = naughty.notify(common).id
end)
else
mpd.id = naughty.notify(common).id
end
end
elseif mpd_now.state ~= "pause" then
helpers.set_map("current mpd track", nil)
end
end)
end
mpd.timer = helpers.newtimer("mpd", timeout, mpd.update, true, true)
return mpd
end
return factory

View File

@@ -1,103 +0,0 @@
--[[
Licensed under GNU General Public License v2
* (c) 2013, Luke Bonham
* (c) 2010-2012, Peter Hofmann
--]]
local helpers = require("lain.helpers")
local naughty = require("naughty")
local wibox = require("wibox")
local string = { format = string.format, match = string.match }
-- Network infos
-- lain.widget.net
local function factory(args)
local net = { widget = wibox.widget.textbox(), devices = {} }
local args = args or {}
local timeout = args.timeout or 2
local units = args.units or 1024 -- KB
local notify = args.notify or "on"
local screen = args.screen or 1
local settings = args.settings or function() end
-- Compatibility with old API where iface was a string corresponding to 1 interface
net.iface = (args.iface and (type(args.iface) == "string" and {args.iface}) or
(type(args.iface) == "table" and args.iface)) or {}
function net.get_device()
helpers.async(string.format("ip link show", device_cmd), function(ws)
ws = ws:match("(%w+): <BROADCAST,MULTICAST,.-UP,LOWER_UP>")
net.iface = ws and { ws } or {}
end)
end
if #net.iface == 0 then net.get_device() end
function net.update()
-- These are the totals over all specified interfaces
net_now = {
devices = {},
-- Bytes since last iteration
sent = 0,
received = 0
}
for i, dev in ipairs(net.iface) do
local dev_now = {}
local dev_before = net.devices[dev] or { last_t = 0, last_r = 0 }
local now_t = tonumber(helpers.first_line(string.format("/sys/class/net/%s/statistics/tx_bytes", dev)) or 0)
local now_r = tonumber(helpers.first_line(string.format("/sys/class/net/%s/statistics/rx_bytes", dev)) or 0)
dev_now.carrier = helpers.first_line(string.format("/sys/class/net/%s/carrier", dev)) or "0"
dev_now.state = helpers.first_line(string.format("/sys/class/net/%s/operstate", dev)) or "down"
dev_now.sent = (now_t - dev_before.last_t) / timeout / units
dev_now.received = (now_r - dev_before.last_r) / timeout / units
net_now.sent = net_now.sent + dev_now.sent
net_now.received = net_now.received + dev_now.received
dev_now.sent = string.format("%.1f", dev_now.sent)
dev_now.received = string.format("%.1f", dev_now.received)
dev_now.last_t = now_t
dev_now.last_r = now_r
net.devices[dev] = dev_now
-- Notify only once when connection is loss
if string.match(dev_now.carrier, "0") and notify == "on" and helpers.get_map(dev) then
naughty.notify {
title = dev,
text = "No carrier",
icon = helpers.icons_dir .. "no_net.png",
screen = screen
}
helpers.set_map(dev, false)
elseif string.match(dev_now.carrier, "1") then
helpers.set_map(dev, true)
end
net_now.carrier = dev_now.carrier
net_now.state = dev_now.state
net_now.devices[dev] = dev_now
-- new_now.sent and net_now.received will be
-- the totals across all specified devices
end
net_now.sent = string.format("%.1f", net_now.sent)
net_now.received = string.format("%.1f", net_now.received)
widget = net.widget
settings()
end
helpers.newtimer("network", timeout, net.update)
return net
end
return factory

View File

@@ -1,59 +0,0 @@
--[[
Licensed under GNU General Public License v2
* (c) 2016, Luke Bonham
--]]
local helpers = require("lain.helpers")
local shell = require("awful.util").shell
local wibox = require("wibox")
local string = { gmatch = string.gmatch,
match = string.match,
format = string.format }
local type = type
-- PulseAudio volume
-- lain.widget.pulse
local function factory(args)
local pulse = { widget = wibox.widget.textbox(), device = "N/A" }
local args = args or {}
local timeout = args.timeout or 5
local settings = args.settings or function() end
pulse.devicetype = args.devicetype or "sink"
pulse.cmd = args.cmd or "pacmd list-" .. pulse.devicetype .. "s | sed -n -e '/*/,$!d' -e '/index/p' -e '/base volume/d' -e '/volume:/p' -e '/muted:/p' -e '/device\\.string/p'"
function pulse.update()
helpers.async({ shell, "-c", type(pulse.cmd) == "string" and pulse.cmd or pulse.cmd() },
function(s)
volume_now = {
index = string.match(s, "index: (%S+)") or "N/A",
device = string.match(s, "device.string = \"(%S+)\"") or "N/A",
muted = string.match(s, "muted: (%S+)") or "N/A"
}
pulse.device = volume_now.index
local ch = 1
volume_now.channel = {}
for v in string.gmatch(s, ":.-(%d+)%%") do
volume_now.channel[ch] = v
ch = ch + 1
end
volume_now.left = volume_now.channel[1] or "N/A"
volume_now.right = volume_now.channel[2] or "N/A"
widget = pulse.widget
settings()
end)
end
helpers.newtimer("pulse", timeout, pulse.update)
return pulse
end
return factory

View File

@@ -1,148 +0,0 @@
--[[
Licensed under GNU General Public License v2
* (c) 2013, Luke Bonham
* (c) 2013, Rman
--]]
local helpers = require("lain.helpers")
local awful = require("awful")
local naughty = require("naughty")
local wibox = require("wibox")
local math = { modf = math.modf }
local string = { format = string.format,
match = string.match,
gmatch = string.gmatch,
rep = string.rep }
local type, tonumber = type, tonumber
-- PulseAudio volume bar
-- lain.widget.pulsebar
local function factory(args)
local pulsebar = {
colors = {
background = "#000000",
mute = "#EB8F8F",
unmute = "#A4CE8A"
},
_current_level = 0,
_mute = "no",
device = "N/A"
}
local args = args or {}
local timeout = args.timeout or 5
local settings = args.settings or function() end
local width = args.width or 63
local height = args.heigth or 1
local ticks = args.ticks or false
local ticks_size = args.ticks_size or 7
pulsebar.colors = args.colors or pulsebar.colors
pulsebar.followtag = args.followtag or false
pulsebar.notification_preset = args.notification_preset
pulsebar.devicetype = args.devicetype or "sink"
pulsebar.cmd = args.cmd or "pacmd list-" .. pulsebar.devicetype .. "s | sed -n -e '/*/,$!d' -e '/index/p' -e '/base volume/d' -e '/volume:/p' -e '/muted:/p' -e '/device\\.string/p'"
if not pulsebar.notification_preset then
pulsebar.notification_preset = {
font = "Monospace 10"
}
end
pulsebar.bar = wibox.widget {
forced_height = height,
forced_width = width,
color = pulsebar.colors.unmute,
background_color = pulsebar.colors.background,
margins = 1,
paddings = 1,
ticks = ticks,
ticks_size = ticks_size,
widget = wibox.widget.progressbar,
}
pulsebar.tooltip = awful.tooltip({ objects = { pulsebar.bar } })
function pulsebar.update(callback)
helpers.async({ awful.util.shell, "-c", type(pulsebar.cmd) == "string" and pulsebar.cmd or pulsebar.cmd() },
function(s)
volume_now = {
index = string.match(s, "index: (%S+)") or "N/A",
device = string.match(s, "device.string = \"(%S+)\"") or "N/A",
muted = string.match(s, "muted: (%S+)") or "N/A"
}
pulsebar.device = volume_now.index
local ch = 1
volume_now.channel = {}
for v in string.gmatch(s, ":.-(%d+)%%") do
volume_now.channel[ch] = v
ch = ch + 1
end
volume_now.left = volume_now.channel[1] or "N/A"
volume_now.right = volume_now.channel[2] or "N/A"
local volu = volume_now.left
local mute = volume_now.muted
if volu:match("N/A") or mute:match("N/A") then return end
if volu ~= pulsebar._current_level or mute ~= pulsebar._mute then
pulsebar._current_level = tonumber(volu)
pulsebar.bar:set_value(pulsebar._current_level / 100)
if pulsebar._current_level == 0 or mute == "yes" then
pulsebar._mute = mute
pulsebar.tooltip:set_text ("[muted]")
pulsebar.bar.color = pulsebar.colors.mute
else
pulsebar._mute = "no"
pulsebar.tooltip:set_text(string.format("%s %s: %s", pulsebar.devicetype, pulsebar.device, volu))
pulsebar.bar.color = pulsebar.colors.unmute
end
settings()
if type(callback) == "function" then callback() end
end
end)
end
function pulsebar.notify()
pulsebar.update(function()
local preset = pulsebar.notification_preset
preset.title = string.format("%s %s - %s%%", pulsebar.devicetype, pulsebar.device, pulsebar._current_level)
if pulsebar._mute == "yes" then
preset.title = preset.title .. " muted"
end
int = math.modf((pulsebar._current_level / 100) * awful.screen.focused().mywibox.height)
preset.text = string.format("[%s%s]", string.rep("|", int),
string.rep(" ", awful.screen.focused().mywibox.height - int))
if pulsebar.followtag then preset.screen = awful.screen.focused() end
if not pulsebar.notification then
pulsebar.notification = naughty.notify {
preset = preset,
destroy = function() pulsebar.notification = nil end
}
else
naughty.replace_text(pulsebar.notification, preset.title, preset.text)
end
end)
end
helpers.newtimer(string.format("pulsebar-%s", pulsebar.sink), timeout, pulsebar.update)
return pulsebar
end
return factory

View File

@@ -1,38 +0,0 @@
--[[
Licensed under GNU General Public License v2
* (c) 2013, Luke Bonham
* (c) 2010-2012, Peter Hofmann
--]]
local helpers = require("lain.helpers")
local wibox = require("wibox")
local open, match = io.open, string.match
-- System load
-- lain.widget.sysload
local function factory(args)
local sysload = { widget = wibox.widget.textbox() }
local args = args or {}
local timeout = args.timeout or 2
local settings = args.settings or function() end
function sysload.update()
local f = open("/proc/loadavg")
local ret = f:read("*all")
f:close()
load_1, load_5, load_15 = match(ret, "([^%s]+) ([^%s]+) ([^%s]+)")
widget = sysload.widget
settings()
end
helpers.newtimer("sysload", timeout, sysload.update)
return sysload
end
return factory

View File

@@ -1,41 +0,0 @@
--[[
Licensed under GNU General Public License v2
* (c) 2013, Luke Bonham
--]]
local helpers = require("lain.helpers")
local wibox = require("wibox")
local open = io.open
local tonumber = tonumber
-- coretemp
-- lain.widget.temp
local function factory(args)
local temp = { widget = wibox.widget.textbox() }
local args = args or {}
local timeout = args.timeout or 2
local tempfile = args.tempfile or "/sys/class/thermal/thermal_zone0/temp"
local settings = args.settings or function() end
function temp.update()
local f = open(tempfile)
if f then
coretemp_now = tonumber(f:read("*all")) / 1000
f:close()
else
coretemp_now = "N/A"
end
widget = temp.widget
settings()
end
helpers.newtimer("coretemp", timeout, temp.update)
return temp
end
return factory

View File

@@ -1,163 +0,0 @@
--[[
Licensed under GNU General Public License v2
* (c) 2015, Luke Bonham
--]]
local helpers = require("lain.helpers")
local json = require("lain.util").dkjson
local focused = require("awful.screen").focused
local naughty = require("naughty")
local wibox = require("wibox")
local math, os, string, tonumber = math, os, string, tonumber
-- OpenWeatherMap
-- current weather and X-days forecast
-- lain.widget.weather
local function factory(args)
local weather = { widget = wibox.widget.textbox() }
local args = args or {}
local APPID = args.APPID or "3e321f9414eaedbfab34983bda77a66e" -- lain default
local timeout = args.timeout or 60 * 15 -- 15 min
local timeout_forecast = args.timeout or 60 * 60 * 24 -- 24 hrs
local current_call = args.current_call or "curl -s 'http://api.openweathermap.org/data/2.5/weather?id=%s&units=%s&lang=%s&APPID=%s'"
local forecast_call = args.forecast_call or "curl -s 'http://api.openweathermap.org/data/2.5/forecast/daily?id=%s&units=%s&lang=%s&cnt=%s&APPID=%s'"
local city_id = args.city_id or 0 -- placeholder
-- local units = args.units or "metric"
local units = args.units or "imperial"
local lang = args.lang or "en"
local cnt = args.cnt or 5
local date_cmd = args.date_cmd or "date -u -d @%d +'%%a %%d'"
local icons_path = args.icons_path or helpers.icons_dir .. "openweathermap/"
local notification_preset = args.notification_preset or {}
local notification_text_fun = args.notification_text_fun or
function (wn)
local day = os.date("%a %d", wn["dt"])
local tmin = math.floor(wn["temp"]["min"])
local tmax = math.floor(wn["temp"]["max"])
local desc = wn["weather"][1]["description"]
return string.format("<b>%s</b>: %s, %d - %d ", day, desc, tmin, tmax)
end
local weather_na_markup = args.weather_na_markup or " N/A "
local followtag = args.followtag or false
local showpopup = args.showpopup or "on"
local settings = args.settings or function() end
weather.widget:set_markup(weather_na_markup)
weather.icon_path = icons_path .. "na.png"
weather.icon = wibox.widget.imagebox(weather.icon_path)
function weather.show(t_out)
weather.hide()
if followtag then
notification_preset.screen = focused()
end
if not weather.notification_text then
weather.update()
weather.forecast_update()
end
weather.notification = naughty.notify({
text = weather.notification_text,
icon = weather.icon_path,
timeout = t_out,
preset = notification_preset
})
end
function weather.hide()
if weather.notification then
naughty.destroy(weather.notification)
weather.notification = nil
end
end
function weather.attach(obj)
obj:connect_signal("mouse::enter", function()
weather.show(0)
end)
obj:connect_signal("mouse::leave", function()
weather.hide()
end)
end
function weather.forecast_update()
local cmd = string.format(forecast_call, city_id, units, lang, cnt, APPID)
helpers.async(cmd, function(f)
local pos, err
weather_now, pos, err = json.decode(f, 1, nil)
if not err and type(weather_now) == "table" and tonumber(weather_now["cod"]) == 200 then
weather.notification_text = ""
for i = 1, weather_now["cnt"] do
weather.notification_text = weather.notification_text ..
notification_text_fun(weather_now["list"][i])
if i < weather_now["cnt"] then
weather.notification_text = weather.notification_text .. "\n"
end
end
end
end)
end
function weather.update()
local cmd = string.format(current_call, city_id, units, lang, APPID)
helpers.async(cmd, function(f)
local pos, err, icon
weather_now, pos, err = json.decode(f, 1, nil)
if not err and type(weather_now) == "table" and tonumber(weather_now["cod"]) == 200 then
local sunrise = tonumber(weather_now["sys"]["sunrise"])
local sunset = tonumber(weather_now["sys"]["sunset"])
local icon = weather_now["weather"][1]["icon"]
local loc_now = os.time()
local loc_m = os.time { year = os.date("%Y"), month = os.date("%m"), day = os.date("%d"), hour = 0 }
local loc_t = os.difftime(loc_now, loc_m)
local loc_d = os.date("*t", loc_now)
local utc_d = os.date("!*t", loc_now)
local utc_now = os.time(utc_d)
local offdt = (loc_d.isdst and 1 or 0) * 3600 + 100 * (loc_d.min - utc_d.min) / 60
local offset = os.difftime(loc_now, utc_now) + offdt
local offday = (offset < 0 and -86400) or 86400
if math.abs(loc_now - utc_now - offdt + loc_t) >= 86400 then
utc_now = utc_now + offday
end
if offday * (loc_now - utc_now - offdt) > 0 then
sunrise = sunrise + offday
sunset = sunset + offday
end
if sunrise <= loc_now and loc_now <= sunset then
icon = string.gsub(icon, "n", "d")
else
icon = string.gsub(icon, "d", "n")
end
weather.icon_path = icons_path .. icon .. ".png"
widget = weather.widget
settings()
else
weather.icon_path = icons_path .. "na.png"
weather.widget:set_markup(weather_na_markup)
end
weather.icon:set_image(weather.icon_path)
end)
end
if showpopup == "on" then weather.attach(weather.widget) end
weather.timer = helpers.newtimer("weather-" .. city_id, timeout, weather.update, false, true)
weather.timer_forecast = helpers.newtimer("weather_forecast-" .. city_id, timeout, weather.forecast_update, false, true)
return weather
end
return factory