📊 A fully-customizable Conky script entirely built in Lua https://samuel.forestier.app/blog/tutorials/a-laide-du-lua-mon-script-conky-revu-en-beaute
Go to file
Samuel FORESTIER df466c9142 Moves to new personal domain name 2023-11-03 22:00:12 +01:00
lua Improves "top" processes appearance by properly pad displayed values 2022-09-27 19:39:21 +02:00
.gitignore The work is huge, let's save this somewhere safe 2019-08-22 11:29:17 +02:00
.luacheckrc Ends configuration, new optimizations, improves code styles and design 2019-08-23 19:13:26 +02:00
LICENSE The work is huge, let's save this somewhere safe 2019-08-22 11:29:17 +02:00
README.md Moves to new personal domain name 2023-11-03 22:00:12 +01:00
config.json.dist Removes trailing mount points '/' 2022-09-27 19:22:42 +02:00
conky.lua Prevents project path from missing ending path separator 2021-12-15 10:05:47 +01:00
run_scs.sh Ships a handy Bash wrapper to run this project from _anywhere_ 2021-12-15 10:03:19 +01:00

README.md

SimpleConkyScript

A fully-customizable Conky script entirely built in Lua

Horloge's Desktop

Installation

# Debian (>= 9) example procedure
sudo apt install conky-all hddtemp lm-sensors lsb-release x11-utils luarocks
sudo luarocks install luaposix
sudo luarocks install lua-cjson
# Note for Debian >= 11 (Lua 5.3), you may wanna freeze version 2.1.0-1 (see <mpx/lua-cjson#56>)
sudo luarocks install lua-cjson 2.1.0-1

sudo sensors-detect --auto
sudo systemctl enable --now hddtemp.service

# Universal section
mkdir ~/.conky/
cd ~/.conky/
git clone https://git.forestier.app/HorlogeSkynet/SimpleConkyScript.git
cd SimpleConkyScript/
cp config.json.dist config.json

Usage

conky -c ~/.conky/SimpleConkyScript/conky.lua

Static Analysis

sudo luarocks install luacheck
luacheck .

Configuration Documentation

This project is fully-customizable from configuration. It means that you can safely pull latest changes without tweaking any Lua code ( 😎 ).
The main idea is to describe the content of "columns" displayed on your screen (please refer to the screen-shot above).
To achieve this, you can either use pre-defined types of entity (detailed below) or tweak global settings.

Comprehensive documentation of the `config.json` file

The configuration file must contain (at least) the settings (as long as all its children) and the content objects.

{
	"settings": {
		"conky": {
			// Specific settings related to Conky itself.
			// You can read further information about them here : <https://github.com/brndnmtthws/conky/wiki/Configurations#settings>.
			// Entries present by default are mostly mine, working against a Cinnamon setup.
		},
		// Below objects (as long as their associated entries) are all REQUIRED.
		"timers": {
			// Number of iterations to wait before drawing anything on the screen.
			// It allows Conky to initialize its internals and gather some data before rendering anything.
			// You MAY lower this value at your own risks.
			"startup_threshold": 3,

			// Number of iterations to wait before checking the user configuration (and reloading Conky if it changed).
			// Use `null` to disable this behavior (default).
			"config_autoreload": null,

			// Number of iterations to wait before gathering current screen resolutions (and reloading Conky if it changed).
			// This option is very useful if you are used to plug your laptop on a dock or play with multiple screens.
			"resolution_autodetect": null
		},
		"display": {
			// The width (in pixels) of the columns drawn on screen.
			// You MAY lower this value, but don't forget about very long content that could be displayed (i.e. IPv6 addresses).
			"columns_width": 460,

			// The number of pixels to automatically add between two entities.
			"entity_padding": 7,

			// A subcommand to be run in order to retrieve the current screen dimensions (output MUST be formatted as `${WIDTH}x${HEIGHT}`).
			"resolution_exec": "xdpyinfo | grep dimensions | tr -s ' ' | cut -d ' ' -f 3"
		},
		"font": {
			// The font size and family to be used when writing down text on the screen.
			// The font family SHOULD be a "Monospaced" one (see <https://en.wikipedia.org/wiki/Monospaced_font>).
			"size": 15,
			"family": "Consolas"
		},
		"colors": {
			// Below colors are defined as RGBA values.
			// Keys speak for themselves.
			"default": [1, 1, 1, 1],
			"clock_second_hand": [1, 0, 0, 1],
			"bar_background": [0.4, 0.4, 0.4, 1],
			"graph_in": [0, 0.5, 0.66, 1],
			"graph_out": [1, 0.5, 0, 1]
		},
		"temperature": {
			// The temperature unit that will be employed.
			// You can either specify `F` for Fahrenheit or `C` for Celsius (default).
			"unit": "C",

			// The degree symbol that should be set before the unit.
			// Please be careful, the one set by default required you to use a font family supporting Unicode.
			"degree": "°",

			// The string that will be added to any temperature exceeding the threshold set (see `drive_temperature` &  `sensor` entities below).
			"warning": "/!\\"
		}
	},
	"content": {
		// Entities are represented by a list of JSON objects.
		// This first list will describe entities set within the left column.
		"left": [
			{
				// An entity is an object containing at the very least a `type` key.
				// You will find below an exhaustive list of supported `type`.
				//
				// You MAY temporary "hide" any entity by setting the `disabled` key to `true`.
				// This is pretty handy to avoid cutting/pasting content in your JSON.
				//
				// One more important note about configuration values :
				// Only omitting a key or setting its value to `false` would make the option evaluate to `false` in Lua.
				"type": "...",
				"disabled": false
			},
			{
				// This entity will display a clock, as present on the screen-shot above.
				"type": "clock"
			},
			{
				// This entity will render the current date-time and output it as a 'text' object.
				// This is a regular 'text' object underly, so you MAY tweak the `align_center` setting (see `text` entity below).
				// You can check available `format` tokens here : <https://www.lua.org/pil/22.1.html>.
				"type": "date_time",
				"format": "%H:%M:%S, %A %d/%m/%Y",
				"align_center": true
			},
			{
				// This entity will display an horizontal line, allowing you to separate entities and thus creating "blocks".
				// `width` allows you to change the line horizontal dimension, proportionally to the `columns_width` value (fully-optional and defaults to 50%).
				"type": "separator",
				"width": 0.5
			},
			{
				// A typical entity that would evaluate `var` and `arg` through Conky and show the result as a 'text' object.
				// For example, the lines below would produce : "Hostname: ${nodename}".
				//
				// `arg` is fully-optional and MAY be omitted.
				//
				// `align_center` is fully-optional and defaults to `false`.
				"type": "text",
				"var": "nodename",
				"arg": "",
				"name": "Hostname",
				"align_center": false
			},
			{
				// A typical entity that would evaluate `var` and `arg` through Conky and show the result as a 'bar' object.
				// `arg` is fully-optional and MAY be omitted.
				//
				// `show_percent` allows you to add the numerical percentage value next to the `name` of your 'bar' object (disabled by default).
				//
				// You MAY use `bar` with any variable evaluating to a valid percentage.
				"type": "bar",
				"var": "battery_percent",
				"arg": "",
				"name": "Battery",
				"show_percent": false
			},
			{
				// A typical entity that would evaluate `var` and `arg` through Conky and show the result as a 'graph' object.
				// `arg` is fully-optional and MAY be omitted.
				//
				// This entity is really strong, you can actually provide _any_ data to it, as long as it is consistent across time.
				"type": "graph",
				"name": "CPU activity",
				"var": "cpu",
				"arg": "cpu0"
			},
			{
				// This is the most powerful entity currently available.
				// It allows you to execute (synchronously though) a subcommand and show the (STDOUT) result as a 'text' object.
				// For example, on Debian, the lines below MIGHT produce : "Updates: 42 available".
				//
				// `interval` represents the number of iterations to wait before executing the command again (the output is cached).
				// Please be careful, with `update_interval` Conky setting set to 2 seconds, such a command would be executed each 3600 * 2 seconds, or 2 hours.
				// You MAY set `interval` to `null` to completely disable value update (the first obtained output would be kept until reload).
				// Inversely, you MAY set `interval` to `0` (or even omit the entry) to update it during each iteration (use at your own risks).
				//
				// `pre_text` and `post_text` (fully-optional values) allow you to respectively set some texts before and after obtained output.
				// `align_center` acts as described above for `text` entities.
				//
				// When the command fails, `on_error` content will be displayed.
				"type": "command",
				"exec": "apt list --upgradable -qq 2> /dev/null | wc -l",
				"name": "Updates",
				"interval": 3600,
				"pre_text": "",
				"post_text": " available",
				"align_center": false,
				"on_error": "Not detected"
			},
			{
				// `brightness` is a very specific entity.
				// Through `cur_file` and `max_file`, it computes a brightness percentage ratio and produces a 'bar' object.
				// You SHOULD specify an `interval` to avoid opening and reading two files during each iteration.
				//
				// For this entity, `name` is fully-optional and defaults to "Brightness" when not specified.
				//
				// `show_percent` is also available and MAY be tweaked.
				//
				// Please be careful, distributed file paths MAY NOT work on your system.
				// You MAY run `ls -l /sys/class/backlight/` to check what is actually available on your system.
				"type": "brightness",
				"name": "Brightness",
				"cur_file": "/sys/class/backlight/intel_backlight/actual_brightness",
				"max_file": "/sys/class/backlight/intel_backlight/max_brightness",
				"interval": 10,
				"show_percent": false
			},
			{
				// This entity allows you to show a 'top'-like output for processes, based on their CPU usage.
				// `max_processes` MAY be set between `1` and `10` (defaults to `10`).
				"type": "top_processes",
				"max_processes": 5
			},
			{
				/* ... */
			}
		],
		// This second list will describe entities set within the middle column.
		"middle": [
			{
				/* ... */
			}
		],
		// This third list will describe entities set within the right column.
		"right": [
			{
				// This entity relies entirely on the LM-SENSORS program.
				// It underly uses a `sensors` call to retrieve temperature information from a chip.
				// The identifier of the `chip` to prompt MUST be specified and could be found by manually running `sensors -A`.
				// The final displayed value corresponds to the chip temperature input(s) average.
				//
				// `is_fan` is fully-optional and defaults to `false`.
				//
				// `interval` acts as described for `command` type entity (see above).
				//
				// `threshold` represents the maximum temperature value that could be reached before adding the symbol specified in `settings.temperature.warning` (see above).
				// You can either specify a Celsius or Fahrenheit value, as long as it is consistent to the unit specified in `settings.temperature.unit` (see above too).
				//
				// When no temperature could be retrieved or computed, `on_error` will be displayed.
				"type": "sensor",
				"name": "CPUs",
				"chip": "coretemp-isa-0000",
				"is_fan": false,
				"interval": 5,
				"threshold": 70,
				"on_error": "Not detected"
			},
			{
				// On the same idea, you may provide a fan speed control `chip` as long as `is_fan` is set to `true`.
				// The final displayed value corresponds to the chip fan speed input(s) average, in RPM (Revolutions per minute).
				//
				// `threshold` represents the maximum fan speed value that could be reached before adding the symbol specified in `settings.temperature.warning` (see above).
				//
				// When no fan speed could be retrieved or computed, `on_error` will be displayed.
				"type": "sensor",
				"name": "Dell SMM",
				"chip": "dell_smm-virtual-0",
				"is_fan": true,
				"interval": 5,
				"threshold": 5400,
				"on_error": "Not detected"
			},
			{
				// This entity works as the `sensor` above, but relies on the HDDTEMP program.
				// You'll have to specify the path to the targeted `drive`.
				//
				// `wake_up` allows you to force drive wake up (when needed) to measure its temperature (disabled by default).
				//
				// `interval`, `threshold` and `on_error` acts as described for the `sensor` type entity (see above).
				"type": "drive_temperature",
				"drive": "/dev/sda",
				"wake_up": false,
				"interval": 5,
				"threshold": 60,
				"on_error": "Not detected"
			},
			{
				// This entity is a handy wrapper to the 'bar' entity.
				// By specifying any partition `mount_point`, it will display a 'bar' representing its usage.
				// `show_percent` acts as described above for `bar` entities.
				"type": "partition_usage",
				"mount_point": "/home",
				"show_percent": false
			},
			{
				// This entity will display two graphs representing the I/O rate of the specified `drive`.
				// To set graphs colors, please refer to `settings.colors.graph_{in,out}`.
				"type": "drive_io",
				"drive": "/dev/sda"
			},
			{
				// This entity will display two graphs representing the I/O rate of the specified network `interface`.
				// As `drive_io` above, please refer to `settings.colors.graph_{in,out}` to set graphs colors.
				//
				// Please be careful, such an entity will be skipped when the interface is down (see `if_up_strictness` for further information).
				"type": "network_interface_io",
				"interface": "enp0s25"
			},
			{
				// This entity will display each IP addresses associated to the specified network `interface`.
				// `hide_v6` allows you to hide any IPv6 address (disabled by default).
				//
				// Please be careful, such an entity will be skipped when the interface is down (see `if_up_strictness` for further information).
				"type": "network_interface_addr",
				"interface": "enp0s25",
				"hide_v6": false
			},
			{
				// This entity is a disguised 'text' object.
				// It allows you to show Conky version, with `{pre,post}_text` (acting as with the `command` entity).
				// It could be very useful to give you credits on screen-shot for instance.
				//
				// `align_center` is available and acts as described above.
				"type": "version",
				"pre_text": "Horloge's script on ",
				"post_text": "",
				"align_center": true
			},
			{
				/* ... */
			}
		]
	}
}

Frequently asked questions

My Conky does not work, it [insert your very specific issue details here]. What should I do ?

Please, refer to the official Conky's FAQ first.
If you can't find anything helpful, please contact me with your configuration and Conky terminal output attached.

Your script is cool but I'd like to install it elsewhere. How could I achieve that ?

You'll have to tweak your conky execution with an additional environment variable :

CONKY_PATH="/another/script/location" conky -c "${CONKY_PATH}/conky.lua"

Rationale : There is no way to fetch the project location from the configuration file itself (conky.lua).
Fortunately for us, we may read Conky environment variables directly from Lua !

A simple Bash wrapper is also packaged :

bash run_scs.sh

How can I get the same background image ?

Sorry, it has been deleted upstream by its owner.
I'm not sure if I am allowed to redistribute copy of it now 😕

My device drive temperature is not displayed !

Yeah it happens sometimes when the hddtemp daemon is not properly configured.
Simply run the command below to enable the SUID bit, and leave other options with their default value :

sudo dpkg-reconfigure hddtemp

The console reads sh: 1: hddtemp: not found

Same as above.

How can I contribute to this project ?

At the moment, SimpleConkyScript is not open to public contributions.
However, you may send patches to samuel+dev@forestier.app, I'll personally take the time to review them.

What could I achieve from the configuration file ?

Well, the only limit is your imagination. Some examples :

Wanna ping and probe a Minecraft server for the current number of players ?

Sure, first run :

pip3 install mcstatus

... and then add a new entry block containing :

{
	"type": "command",
	"exec": "python3 -c 'from mcstatus import MinecraftServer; server = MinecraftServer(\"YOUR.MINECRAFT.SERVER\"); status = server.status(); print(\"{}/{} ({} ms)\".format(status.players.online, status.players.max, server.ping()))'",
	"name": "Minecraft server",
	"interval": 300,
	"on_error": "Can't be reached"
}

Wanna ping and probe a Mumble server for current number of users and bandwidth ?

From: https://github.com/mumble-voip/mumble-scripts/blob/master/Non-RPC/mumble-ping.py

{
	"type": "command",
	"exec": "python3 -c 'from datetime import datetime; from struct import pack, unpack; from socket import AF_INET, SOCK_DGRAM, socket; s = socket(AF_INET, SOCK_DGRAM); s.sendto(pack(\">iQ\", 0, datetime.now().microsecond), (\"YOUR.MUMBLE.SERVER\", 64738)); r, _ = s.recvfrom(1024); r = unpack(\">bbbbQiii\", r); t = (datetime.now().microsecond - r[4]) / 1000.0; print(\"{}/{} ({} ms, {} Kbits/s)\".format(r[5], r[6], round(t + (1000 if t < 0 else 0), 2), r[7] / 1000))'",
	"name": "Mumble server",
	"interval": 300,
	"on_error": "Can't be reached"
}

Wanna display the number of unread RSS items from your News Nextcloud app ?

{
	"type": "command",
	"exec": "python3 -c 'NC_ADDR = \"https://YOUR.NEXTCLOUD.SERVER/\"; NC_CREDS = (\"USERNAME\", \"PASSWORD\"); from json import loads; from base64 import b64encode; from urllib.parse import urljoin; from urllib.request import Request, urlopen; print(len(loads(urlopen(Request(url=urljoin(NC_ADDR, \"apps/news/api/v1-2/items?getRead=false\"), headers={\"Authorization\": \"Basic {}\".format(b64encode(\"{}:{}\".format(*NC_CREDS).encode()).decode())})).read().decode())[\"items\"]))'",
	"name": "Unread RSS items",
	"interval": 300,
	"on_error": "Can't be reached"
},

Acknowledgments

  • To NAERNON for the code structure, design and ideas
  • To Nelis Oostens for his clock's code, and the Lua section of Conky's wiki