Custom Conky & Spotify Widget Setup

A complete guide to our personalized desktop widgets on Kali Linux XFCE

Created: December 11, 2025

📋 Overview

This guide documents the setup of two custom desktop widgets:

System Monitor (Conky)

  • Host information & uptime
  • CPU usage & temperature
  • RAM usage with progress bar
  • NVIDIA GPU stats
  • Disk usage
  • Network status (WLAN & ETH)

Spotify Controller (GTK Widget)

  • Current track & artist display
  • Play/Pause status
  • Clickable Previous button
  • Clickable Play/Pause button
  • Clickable Next button
  • Auto-updates every second

⚙️ Prerequisites

Required packages for the widgets to function:

# Install required packages
sudo apt update
sudo apt install conky-all playerctl python3-gi python3-gi-cairo gir1.2-gtk-3.0

# Verify installations
which conky playerctl python3
Package Purpose
conky-all System monitor with all features enabled
playerctl Command-line media player controller (MPRIS)
python3-gi Python GTK bindings for the Spotify widget
lm-sensors Hardware monitoring (CPU temp)

🖥️ Conky System Monitor Widget

Widget Preview

HOST INFO
Hostname: kali
User: ringmast4r
Kernel: 6.17.10+kali-amd64
Arch: x86_64
Uptime: 3h 24m

SYSTEM
CPU: 23%               3.2GHz
██████░░░░░░░░░░░░░░░░░░░
CPU Temp: 72°C
RAM: 4.2GiB / 16GiB (26%)
██████░░░░░░░░░░░░░░░░░░░

GPU
NVIDIA GeForce GTX 1650
Temp: 54°C         Fan: 0%
GPU: 5%           Mem: 12%

DISK USAGE
Root: 45G / 234G (19%)

NETWORK
WLAN: 192.168.1.100
Down: 125KiB/s    Up: 23KiB/s

Configuration File

~/.config/conky/conky-unified.conf
conky.config = {
    alignment = 'top_right',
    gap_x = 20,
    gap_y = 50,
    minimum_width = 280,
    maximum_width = 280,
    background = true,
    double_buffer = true,
    own_window = true,
    own_window_type = 'desktop',
    own_window_argb_visual = true,
    own_window_argb_value = 180,
    own_window_hints = 'undecorated,below,sticky,skip_taskbar,skip_pager',
    font = 'DejaVu Sans Mono:size=10',
    use_xft = true,
    update_interval = 2.0,
    default_color = 'white',
    color1 = 'cyan',
    color2 = 'orange',
    color3 = 'lime',
}

conky.text = [[
${color1}HOST INFO ${hr 2}${color}
Hostname: ${nodename}
User: ${user_names}
Kernel: ${kernel}
Arch: ${machine}
Uptime: ${uptime_short}

${color2}SYSTEM ${hr 2}${color}
CPU: ${cpu}% ${alignr}${freq_g}GHz
${cpubar 6}
CPU Temp: ${hwmon coretemp temp 1}°C
RAM: $mem / $memmax ($memperc%)
${membar 6}

${color3}GPU ${hr 2}${color}
${execi 5 nvidia-smi --query-gpu=name --format=csv,noheader,nounits}
Temp: ${execi 5 nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader,nounits}°C ${alignr}Fan: ${execi 5 nvidia-smi --query-gpu=fan.speed --format=csv,noheader,nounits}%
GPU: ${execi 5 nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits}% ${alignr}Mem: ${execi 5 nvidia-smi --query-gpu=utilization.memory --format=csv,noheader,nounits}%
VRAM: ${execi 5 nvidia-smi --query-gpu=memory.used --format=csv,noheader,nounits}MiB / ${execi 5 nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits}MiB

${color1}DISK USAGE ${hr 2}${color}
Root: ${fs_used /} / ${fs_size /} (${fs_used_perc /}%)
${fs_bar 6 /}

${color2}NETWORK ${hr 2}${color}
WLAN: ${if_up wlan0}${addr wlan0}${else}Disconnected${endif}
Down: ${downspeed wlan0} ${alignr}Up: ${upspeed wlan0}
Total: ${totaldown wlan0} ${alignr}Total: ${totalup wlan0}
ETH: ${if_up eth0}${addr eth0}${else}Disconnected${endif}
Down: ${downspeed eth0} ${alignr}Up: ${upspeed eth0}
]]

Key Configuration Options Explained

Option Description
alignment Position on screen (top_right, bottom_left, etc.)
gap_x / gap_y Pixel offset from screen edge
own_window_argb_value Transparency (0=invisible, 255=opaque)
update_interval Refresh rate in seconds
${hwmon coretemp temp 1} CPU package temperature from lm-sensors
${execi N cmd} Execute command every N seconds
${if_up interface} Conditional display based on network interface status

Tip: Finding Your Temperature Sensor

Run sensors command to see available sensors. Look for coretemp-isa-0000 for Intel CPUs or k10temp for AMD CPUs.

🎵 Spotify Control Widget

Widget Preview

SPOTIFY
Bohemian Rhapsody
Queen
[Playing]

Why a Python GTK Widget?

Conky doesn't support mouse click events natively. To create a clickable Spotify controller, we built a custom GTK3 widget in Python that:

Widget Source Code

~/.config/conky/spotify-widget.py
#!/usr/bin/env python3
"""
Spotify Control Widget - A simple GTK widget for controlling Spotify
"""

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib, Gdk
import subprocess

class SpotifyWidget(Gtk.Window):
    def __init__(self):
        super().__init__(title="Spotify")

        # Window setup - borderless, sticky, bottom-right
        self.set_decorated(False)
        self.set_keep_below(True)
        self.set_skip_taskbar_hint(True)
        self.set_skip_pager_hint(True)
        self.stick()

        # Semi-transparent background
        screen = self.get_screen()
        visual = screen.get_rgba_visual()
        if visual:
            self.set_visual(visual)
        self.set_app_paintable(True)
        self.connect("draw", self.on_draw)

        # Size and position
        self.set_default_size(280, 130)
        self.set_resizable(False)

        # Position bottom-right with gap
        display = Gdk.Display.get_default()
        monitor = display.get_primary_monitor()
        geometry = monitor.get_geometry()
        x = geometry.width - 280 - 20
        y = geometry.height - 130 - 20
        self.move(x, y)

        # Main container
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
        vbox.set_margin_start(10)
        vbox.set_margin_end(10)
        vbox.set_margin_top(10)
        vbox.set_margin_bottom(10)
        self.add(vbox)

        # Header
        header = Gtk.Label()
        header.set_markup('<span color="#1DB954" font_weight="bold">SPOTIFY</span>')
        header.set_halign(Gtk.Align.START)
        vbox.pack_start(header, False, False, 0)

        # Separator
        sep = Gtk.Separator()
        vbox.pack_start(sep, False, False, 2)

        # Track info labels
        self.title_label = Gtk.Label(label="Not playing")
        self.title_label.set_halign(Gtk.Align.START)
        self.title_label.set_ellipsize(3)
        self.title_label.set_max_width_chars(35)
        vbox.pack_start(self.title_label, False, False, 0)

        self.artist_label = Gtk.Label()
        self.artist_label.set_markup('<span color="gray">-</span>')
        self.artist_label.set_halign(Gtk.Align.START)
        vbox.pack_start(self.artist_label, False, False, 0)

        self.status_label = Gtk.Label(label="[Stopped]")
        self.status_label.set_halign(Gtk.Align.CENTER)
        vbox.pack_start(self.status_label, False, False, 5)

        # Control buttons
        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
        hbox.set_halign(Gtk.Align.CENTER)
        vbox.pack_start(hbox, False, False, 0)

        for label, callback in [("⏮", self.on_prev),
                                 ("⏯", self.on_play),
                                 ("⏭", self.on_next)]:
            btn = Gtk.Button(label=label)
            btn.connect("clicked", callback)
            btn.set_size_request(60, 35)
            hbox.pack_start(btn, False, False, 0)

        # Update every second
        GLib.timeout_add_seconds(1, self.update_info)
        self.apply_css()

    def apply_css(self):
        css = b"""
        window { background-color: transparent; }
        button {
            background: rgba(50, 50, 50, 0.8);
            color: white;
            border: 1px solid #555;
            border-radius: 5px;
            font-size: 16px;
        }
        button:hover { background: rgba(29, 185, 84, 0.8); }
        label { color: white; }
        separator { background-color: #1DB954; min-height: 2px; }
        """
        style_provider = Gtk.CssProvider()
        style_provider.load_from_data(css)
        Gtk.StyleContext.add_provider_for_screen(
            Gdk.Screen.get_default(), style_provider,
            Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)

    def on_draw(self, widget, cr):
        cr.set_source_rgba(0.1, 0.1, 0.1, 0.85)
        cr.set_operator(1)
        cr.paint()
        cr.set_operator(2)
        return False

    def run_cmd(self, cmd):
        try:
            result = subprocess.run(cmd, shell=True,
                capture_output=True, text=True, timeout=2)
            return result.stdout.strip()
        except:
            return ""

    def update_info(self):
        if self.run_cmd("pgrep -x spotify"):
            title = self.run_cmd("playerctl -p spotify metadata --format '{{title}}'")
            artist = self.run_cmd("playerctl -p spotify metadata --format '{{artist}}'")
            status = self.run_cmd("playerctl -p spotify status")
            self.title_label.set_text(title[:35] if title else "Unknown")
            self.artist_label.set_markup(
                f'<span color="gray">{artist[:35] if artist else "Unknown"}</span>')
            self.status_label.set_text(f"[{status}]" if status else "[Unknown]")
        else:
            self.title_label.set_text("Spotify not running")
            self.artist_label.set_markup('<span color="gray">-</span>')
            self.status_label.set_text("[Stopped]")
        return True

    def on_prev(self, button):
        self.run_cmd("playerctl -p spotify previous")
        GLib.timeout_add(300, self.update_info)

    def on_play(self, button):
        self.run_cmd("playerctl -p spotify play-pause")
        GLib.timeout_add(300, self.update_info)

    def on_next(self, button):
        self.run_cmd("playerctl -p spotify next")
        GLib.timeout_add(300, self.update_info)

if __name__ == "__main__":
    win = SpotifyWidget()
    win.connect("destroy", Gtk.main_quit)
    win.show_all()
    Gtk.main()

Note: Spotify Must Be Running

The widget requires Spotify to be open to display track info and respond to controls. When Spotify is closed, it will show "Spotify not running".

🚀 Autostart Configuration

To have both widgets start automatically when you log in:

Conky Autostart

~/.config/autostart/conky.desktop
[Desktop Entry]
Type=Application
Name=Conky
Comment=System Monitor Widget
Exec=conky -c /home/ringmast4r/.config/conky/conky-unified.conf
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
X-GNOME-Autostart-Delay=3

Spotify Widget Autostart

~/.config/autostart/spotify-widget.desktop
[Desktop Entry]
Type=Application
Name=Spotify Widget
Comment=Spotify Control Widget
Exec=python3 /home/ringmast4r/.config/conky/spotify-widget.py
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
X-GNOME-Autostart-Delay=5

Tip: Manual Launch Commands

You can also start the widgets manually:

# Start Conky
conky -c ~/.config/conky/conky-unified.conf &

# Start Spotify Widget
python3 ~/.config/conky/spotify-widget.py &

# Kill and restart Conky
killall conky && conky -c ~/.config/conky/conky-unified.conf &

🎨 Customization Tips

Changing Colors

In the Conky config, modify the color definitions:

color1 = 'cyan',      -- Section headers (HOST INFO, DISK)
color2 = 'orange',    -- Section headers (SYSTEM, NETWORK)
color3 = 'lime',      -- Section headers (GPU)

Changing Position

Position Alignment Value
Top Left'top_left'
Top Right'top_right'
Bottom Left'bottom_left'
Bottom Right'bottom_right'
Middle'middle_middle'

Adjusting Transparency

-- Conky: 0 (invisible) to 255 (opaque)
own_window_argb_value = 180,

-- Spotify Widget (in on_draw method):
cr.set_source_rgba(0.1, 0.1, 0.1, 0.85)  -- Last value is alpha (0-1)

Adding More System Info

Useful Conky variables to add:

-- Battery (for laptops)
Battery: ${battery_percent BAT0}% ${battery_bar 6 BAT0}

-- Load average
Load: ${loadavg 1} ${loadavg 2} ${loadavg 3}

-- Top processes
${top name 1} ${top cpu 1}%
${top name 2} ${top cpu 2}%

-- Swap usage
Swap: $swap / $swapmax
${swapbar 6}

🔧 Troubleshooting

Conky not showing?

# Check if running
pgrep -a conky

# Check for errors
conky -c ~/.config/conky/conky-unified.conf

# Common fix: compositor issues
# Try changing own_window_type to 'normal' or 'override'

CPU temperature not showing?

# Install and configure lm-sensors
sudo apt install lm-sensors
sudo sensors-detect
sensors  # View available sensors

# Find correct sensor name in output and update config

Spotify widget not controlling playback?

# Test playerctl directly
playerctl -p spotify status
playerctl -p spotify play-pause

# If "No players found", Spotify might need restart
# or playerctl service needs to detect it

GPU stats showing errors?

# Requires NVIDIA drivers
nvidia-smi

# For AMD GPUs, use radeontop or remove GPU section

📁 File Summary

File Purpose
~/.config/conky/conky-unified.conf Main Conky system monitor configuration
~/.config/conky/spotify-widget.py Python GTK Spotify control widget
~/.config/autostart/conky.desktop Conky autostart entry
~/.config/autostart/spotify-widget.desktop Spotify widget autostart entry
~/.local/bin/spotify-control CLI helper script for Spotify control