Skip to main content

How to write a simple AwesomeWM widget

Sometimes it's useful to be able to cook up a simple widget for you desktop environment. With AwesomeWM this is easy to do if you can handle some lua. Let's see how to write a simple one that shows us the internal temperature of the computer.

A simple widget

The base

The first step is to create a module to host the base widget. By default AwesomeWM configuration is stored on ~/.config/awesome/, so we'll create a directory there.

mkdir -p ~/.config/awesome/my-widgets

And inside there create a file, say ~/.config/awesome/my-widgets/temperature.lua. Inside that module we can add some commands to create and configure the widget, and export it.

The widget will be composed of two elements, an internal one which contains the text temp_text, and another that provides a background for the first, temp_widget. The one which provides the background is contains the first one, so is the one that we'll export.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-------------------------------------------------
-- Temperature widget
-------------------------------------------------

local wibox = require("wibox")  -- Provides the widgets
local watch = require("awful.widget.watch")

-- Create the text widget
local temp_text = wibox.widget{
    font = "Play 9",
    widget = wibox.widget.textbox,
}

-- Create the background widget
local temp_widget = wibox.widget.background()
temp_widget:set_widget(temp_text) -- Put the text inside of it

-- Set the colors and some text
temp_widget:set_bg("#008800")  -- Green background
temp_widget:set_fg("#ffffff")  -- White text
temp_text:set_text(" This is a simple widget ")

-- Export the widget
return temp_widget

Now let's show this widget in our desktop. Check that you have an ~/.config/awesome/rc.lua file. If you don't you can copy there the one in /etc/xdg/awesome/rc.lua.

Ok, so in the rc.lua file let's import the widget. Since the route to the module from the rc.lua is my-widgets/temperature.lua, we can import it with

local temperature_widget = require("my-widgets.temperature")

So add that line on the rc.lua after the other require()'s to import it. We'll also have to place the widget, we could for example add it just left of the systray. To do that locate with wibox.widget.systray(), and place the temperature_widget before, like this:

...
        temperature_widget,
        wibox.widget.systray(),
...

After this we can reload AwesomeWM with ModKey + Ctrl + R and see the result.

Our initial result

Make it move

Ok, so we already have a working widget, now we have to make it display something interesting. The computer temperature is something useful, so we'll go with that, but in the end is up to what you can come up with.

So, back on the temperature.lua file we'll import the watch module, which will allow us to monitor the output of a command and update the widget according to it:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
...

-- Let's say we did everything, just not returning temp_widget yet

local watch = require("awful.widget.watch")

watch("acpi -t", 10, function(widget, stdout, stderr, exitreason, exitcode)
    -- Do something, for example
    temp_text:set_text(stdout)
  end,
  temp_widget
)

return temp_widget

The code above will update the widget with the raw output of the command, which will be run every 10 seconds.

Raw output on widget

Now, let's extract the temperature from the command and set the background depending on it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
...
function(_, stdout, stderr, exitreason, exitcode)
  local temp = nil

  -- This loop matches the groups number(s).number(s)
  -- each pair is converted to a number and saved on `temp`
  -- (Only the last group is kept)
  for str in string.gmatch(stdout, "([0-9]+.[0-9]+)") do
    temp = tonumber(str)
  end

  -- Set that as text (not just the raw command)
  temp_widget_text:set_text(" " .. temp .. "ºC ")

  -- Set colors depending on the temperature
  if (temp < 70) then
    temp_widget:set_bg("#008800")  -- Green
    temp_widget:set_fg("#ffffff")
  elseif (temp < 80) then
    temp_widget:set_bg("#AB7300")  -- Orange
    temp_widget:set_fg("#ffffff")
    was_down = true
  else
    temp_widget:set_bg("#880000")  -- Red
    temp_widget:set_fg("#ffffff")
    was_down = true
  end
end,
...

And now we have our widget: Raw output: greenRaw output: orange

This is the code of the widget all together:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
-------------------------------------------------
-- Temperature widget
-------------------------------------------------

local wibox = require("wibox")  -- Provides the widgets
local watch = require("awful.widget.watch")  -- For periodic command execution

-- Create the text widget
local temp_text = wibox.widget{
    font = "Play 9",
    widget = wibox.widget.textbox,
}

-- Create the background widget
local temp_widget = wibox.widget.background()
temp_widget:set_widget(temp_text)

-- Set the base colors (will be immediately replaced)
temp_widget:set_bg("#008800")  -- Green background
temp_widget:set_fg("#ffffff")  -- White text

watch(
  "acpi -t", 10,
  function(_, stdout, stderr, exitreason, exitcode)
    local temp = nil

    -- This loop matches the groups number(s).number(s)
    -- each pair is converted to a number and saved on `temp`
    -- (Only the last group is kept)
    for str in string.gmatch(stdout, "([0-9]+.[0-9]+)") do
      temp = tonumber(str)
    end

    -- Set that as text (not just the raw command)
    temp_text:set_text(" " .. temp .. "ºC ")

    -- Set colors depending on the temperature
    if (temp < 70) then
      temp_widget:set_bg("#008800")
      temp_widget:set_fg("#ffffff")
    elseif (temp < 80) then
      temp_widget:set_bg("#AB7300")
      temp_widget:set_fg("#ffffff")
      was_down = true
    else
      temp_widget:set_bg("#880000")
      temp_widget:set_fg("#ffffff")
      was_down = true
    end
  end,
  temp_widget
)

temp_text:set_text(" ??? ")

-- Export the widget
return temp_widget

References: