More detailed Lua example

From Conky

Jump to: navigation, search

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
Personal tools