More detailed Lua example
From Conky
The following examples were written to demonstrate some of what can be done with Lua and Conky. These examples display some widgets and show what can be done by combining Conky's built in variables with Lua's programming flexibility. These examples require Conky 1.7.2 or newer.
[edit] Dim your Conky, Luke
This example does the following:
- Colours the values of Conky's top based on a threshhold we define in the Lua script. As CPU usage increases for a process, it changes the colour to red, making for an interesting highlighting effect. The same occurs with top memory usage.
- The conky_datey() Lua function returns a colour based on the time of day. In this case, it prints the time (i.e., 20:40) and colours it based on the sunrise and sunset times. During the day, it colours the time with a nice sky blue, and at night it colours it with a darker colour. You have to determine the latitude/longitude for your location and pass it to the function, or else the sunset/rise times will be wrong. In my example, the lat/long values are for Calgary, AB.
In your conkyrc, add the following:
lua_load <path to script below>
TEXT
${font DejaVu Sans:size=40}${alignr}${lua_parse datey 51.045 -114.057222 1.5}${time %k:%M}${color}
${font}Name PID CPU% MEM%
${lua_parse top_cpu_colour 1}
${lua_parse top_cpu_colour 2}
${lua_parse top_cpu_colour 3}
${lua_parse top_cpu_colour 4}
${color}Mem usage
${lua_parse top_mem_colour 1}
${lua_parse top_mem_colour 2}
Save the following Lua script somewhere to be loaded from the above conkyrc:
--
-- Conky Lua scripting example
--
-- Copyright (c) 2009 Brenden Matthews, all rights reserved.
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
function components_to_colour(r, g, b)
-- Take the RGB components r, g, b, and return an RGB integer
return ((math.floor(r + 0.5) * 0x10000) + (math.floor(g + 0.5) * 0x100) + math.floor(b + 0.5)) % 0xffffff -- no bit shifting operator in Lua afaik
end
function colour_to_components(colour)
-- Take the RGB components r, g, b, and return an RGB integer
return (colour / 0x10000) % 0x100, (colour / 0x100) % 0x100, colour % 0x100
end
function conky_top_colour(value, default_colour, lower_thresh, upper_thresh)
--[[
This function returns a colour based on a threshold, by adding more of
the red component and reducing the other components. ``value'' is the
value we're checking the thresholds against, ``default_colour'' is the
original colour (before adjusting), and the ``lower_thresh'' and
``upper_thresh'' parameters are the low and high values for which we
start applying redness.
]]
local r, g, b = colour_to_components(default_colour)
local colour = 0
if value ~= nil and (value - lower_thresh) > 0 then
if value > upper_thresh then value = upper_thresh end
local perc = (value - lower_thresh) / (upper_thresh - lower_thresh)
if perc > 1 then perc = 1 end
-- add some redness, depending on where ``value'' lies within the
-- threshhold range
r = r + perc * (0xff - r)
b = b - perc * b
g = g - perc * g
end
colour = components_to_colour(r, g, b)
return string.format("${color #%06x}", colour)
end
-- parses the output from top and calls the colour function
function conky_top_cpu_colour(arg)
-- input is the top var number we want to use
local str = conky_parse(string.format(' ${top name %i} ${top pid %i} ${top cpu %i} ${top mem %i}', tonumber(arg), tonumber(arg), tonumber(arg), tonumber(arg)))
local cpu = tonumber(string.match(str, '(%d+%.%d+)'))
-- tweak the last 3 parameters to your liking
-- my machine has 4 CPUs, so an upper thresh of 25% is appropriate
return conky_top_colour(cpu, 0xd3d3d3, 15, 25) .. str
end
function conky_top_mem_colour(arg)
-- input is the top var number we want to use
local str = conky_parse(string.format(' ${top_mem name %i} ${top_mem pid %i} ${top_mem cpu %i} ${top_mem mem %i}', tonumber(arg), tonumber(arg), tonumber(arg), tonumber(arg)))
local mem = tonumber(string.match(str, '%d+%.%d+%s+(%d+%.%d+)'))
-- tweak the last 3 parameters to your liking
-- my machine has ~8GiB of ram, so an upper thresh of 15% seemed appropriate
return conky_top_colour(mem, 0xd3d3d3, 5, 15) .. str
end
function colour_transition(start, stop, position)
--[[
Transition from one colour to another based on the value of
``position'', which should be a number between 0 and 1.
]]
local rs, gs, bs = colour_to_components(start) -- start components
local re, ge, be = colour_to_components(stop) -- end components
local function tr(s, e, p)
return e + (e - s) * p
end
local rr, gr, br = tr(rs, re, position), tr(gs, ge, position), tr(bs, be, position) -- result components
return components_to_colour(rr, gr, br)
end
function get_timezone_offset()
-- returns the number of seconds of timezone offset
local tz = tonumber(os.date('%z'))
local tzh = math.floor(tz / 100 + 0.5)
local tzm = math.abs(tz) % 100 / 60.
if tzh < 0 then tzm = -tzm end
return (tzh + tzm) * 3600
end
function julian_to_unix(J)
-- converts a julian date into unit time
return (J - 2440588) * 86400
end
function get_julian_now()
-- returns the current time in julian date format
local now = os.time()
return now / 86400. + 2440588
end
function calculate_sunrise_sunset(latitude, longitude)
--[[
This function returns the unix timestamps in the local time for sunrise and
sunset times, according to ``latitude'' and ``longitude''. For the
latitude, north is positive and south is negative. For the longitude, west
is negative, and east is positive. You can usually determine the lat/long
for your location from Wikipedia or using some mapping tool.
In my case (Calgary, AB) the lat/long are 51.045 and -114.057222
Reference: http://en.wikipedia.org/wiki/Sunrise_equation
]]
-- Negate longitude, west is positive and east is negative
longitude = -longitude
-- Calculate current Julian Cycle
local n = math.floor(get_julian_now() - 2451545 - 0.0009 - longitude / 360 + 0.5)
-- Approximate Solar Noon
local Js = 2451545 + 0.0009 + longitude / 360 + n
-- Solar Mean Anomaly
local M = (357.5291 + 0.98560028 * (Js - 2451545)) % 360
-- Equation of Center
local C = (1.9148 * math.deg(math.sin(math.rad(M)))) + (0.0200 * math.deg(math.sin(math.rad(2 * M)))) + (0.0003 * math.deg(math.sin(math.rad(3 * M))))
-- Ecliptic Longitude
local lam = (M + 102.9372 + C + 180) % 360
-- Solar Transit
local Jt = Js + (0.0053 * math.deg(math.sin(math.rad(M)))) - (0.0069 * math.deg(math.sin(math.rad(2 * lam))))
-- Declination of the Sun
local delta = math.deg(math.asin(math.sin(math.rad(lam)) * math.sin(math.rad(23.45))))
-- Hour Angle
local w = math.deg(math.acos((math.sin(math.rad(-0.83)) - math.sin(math.rad(delta)) * math.sin(math.rad(latitude))) / (math.cos(math.rad(latitude)) * math.cos(math.rad(delta)))))
local J_set = 2451545 + 0.0009 + ((w + longitude)/360 + n + (0.0053 * math.deg(math.sin(math.rad(M)))) - (0.0069 * math.deg(math.sin(math.rad(2 * lam)))))
local J_rise = Jt - (J_set - Jt)
local rising_t, setting_t = julian_to_unix(J_rise), julian_to_unix(J_set)
-- apply timezone offset
local tz_offset = get_timezone_offset()
rising_t = rising_t + tz_offset
setting_t = setting_t + tz_offset
return rising_t, setting_t
end
local last_sunrise_set_check = 0
local sunrise, sunset = 0
function conky_datey(latitude, longitude, change)
--[[
Returns a colour at or between day_sky and night_sky (see below) depending on the
time of day. You must provide the ``latitude'' and ``longitude''
parameters for your location (see the comments for
calculate_sunrise_sunset() above for more info). The ``change'' parameter
is the number of hours we want to start and have a transition, so a value
of 1 will mean the transition starts 30 minutes before, and ends 30 minutes
after.
]]
local function to_hours(t)
return tonumber(os.date('%k', t)) + (tonumber(os.date('%M', t)) / 60) + (tonumber(os.date('%S', t)) / 3600)
end
if last_sunrise_set_check < os.time() - 86400 then
sunrise, sunset = calculate_sunrise_sunset(tonumber(latitude), tonumber(longitude))
-- convert unix times into hours
sunrise, sunset = to_hours(sunrise), to_hours(sunset)
end
local day_sky = 0x6698FF -- colour to use during daytime
local night_sky = 0x342D7E -- colour to use during nighttime
local hour = to_hours(os.time())
if hour > sunrise + change / 2 and hour < sunset - change / 2 then
-- midday
sky = day_sky
elseif hour > sunset + change / 2 or hour < sunrise - change / 2 then
-- midnight
sky = night_sky
elseif hour > sunset - change / 2 then
-- sunset time
sky = colour_transition(day_sky, night_sky, (hour - sunset - change / 2) / change)
elseif hour < sunrise + change / 2 then
-- sunrise time
sky = colour_transition(night_sky, day_sky, (hour - sunrise - change / 2) / change)
end
return string.format('${color #%6x}', sky)
end
[edit] Keep track of your marriage date
Another simple example of using lua, this time to make a calendar. This can be the base for further experimenting (changing fonts/colors for the current day and/or week-end, starting the week on a different day, display previous and/or next month, highlighting holidays/birthdays, adding reminders, etc.).
-- Conky Lua scripting example
--
-- Copyright (c) 2009 Cesare Tirabassi, all rights reserved.
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
-- Keep track of time to avoid redoing all the computations every cycle
c_timer = 0
-- Print a calendar
function conky_cal()
if c_timer == 0 then
-- Some useful arrays
day_per_month = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
Month = { "January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December" }
-- Retrieve current date
dtable = os.date("*t")
year = dtable.year
month = dtable.month
day = dtable.day
wday = dtable.wday
-- Adjust number of days for February if it is a leap year
if month == 2 then
if (year % 4 == 0) and (year % 100 ~= 0) or (year % 400 == 0) then
day_per_month[2] = day_per_month[2]+1
end
end
-- Compute what day it was the first of the month (0=Monday)
first_day = wday - 2 - (day-1) % 7
if first_day < 0 then first_day = first_day + 7 end
-- Format and print header
header = Month[month] .. " " .. year
result = string.rep(" ", (20-string.len(header))/2) .. header ..
"\nMo Tu We Th Fr Sa Su\n" .. string.rep(" ", first_day)
-- Print all days in right order (week starts on Monday)
count=first_day
for i=1,day_per_month[month],1 do
if i<10 then
result = result .. " "
end
if i == day then
result = result .. "${color FF0000}"
end
result = result .. i
if i == day then
result = result .. "$color"
end
count=count+1
if count==7 then
result = result .. "\n"
count = 0
else
result = result .. " "
end
end
end
-- Update timer, reset it after an hour
c_timer = c_timer + conky_info.update_interval
if c_timer >= 3600 then c_timer = 0 end
-- And finally, return the result
return result
end
[edit] I've known what it is to be hungry, but I always went right to a restaurant
Yet another simple example of using lua, this time to do page grabbing. It requires lua curl bindings (in Debian/Ubuntu liblua5.1-curl0) but can be easily modified to use any other library (ie. lua-socket) or simply ${curl}. The grabbed page is kept in memory, an alternative (better for large pages) would be to save it on disk. The code can easily be modified to run every so many updates instead of just once; Since the grabbed page is about a quote of the day once a day is pretty adequate for this example.
-- Conky Lua scripting example
--
-- Copyright (c) 2009 Cesare Tirabassi, all rights reserved.
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
require 'curl'
local init = true
local qotd = ""
function conky_qotd()
local buffer = {}
if (init) then
--Do the page grabbing during the first iteration only
init=false
--Fetch page
c = curl.easy_init()
c:setopt(curl.OPT_URL,'http://www.quotationspage.com/qotd.html')
c:setopt(curl.OPT_WRITEFUNCTION,
function (s, len) buffer[#buffer+1] = s return len end)
c:perform()
--Iterate over all quotes and return the first limited to 80 chars
for quote in
string.gmatch(table.concat(buffer),'href="/quote/%d+%.html">([^<]+)<')
do
if (string.len(quote)<=80) then qotd=quote break end
end
end
return qotd
end

