--- title: "Waybar customisation" slug: /waybar-customisation/ date: 2024-03-03 tags: ["log", "productivity", "python", "linux"] --- On my local machine I am currently replacing my desktop environment (Gnome) with a tile-based window manager ([Hyprland](https://github.com/hyprwm/Hyprland)). I'm trying to strip out the bloat and have a lightweight and highly customised Arch workhorse. A key component is a useful status bar that displays metrics and provides a means of quickly executing common tasks. Previously, when I was using X, I opted for Polybar but now that I am on Wayland I'm using [waybar](https://github.com/Alexays/Waybar). So far, I've added two custom modules: one for displaying code metrics and another for managing my time-tracking. ![](./img/waybar-full.png)
Full status bar. Click to enlarge.
## Code metrics module I use WakaTime to collate metrics on my coding activity (time coding, programming languages, projects etc.) I already have a dashboard on this site that displays some of this data but I thought it would be useful to see my stats from the status bar as I am working. Below is an image of the module. The permanent display outputs the coding duration for the current day. When you hover you see a summary of the main languages used and the main projects I've been working on. ![](./img/wakatime-waybar.jpg) It's a straightforward Python script that queries the WakaTime API and parses the data and forwards it to the module: ```py #! /usr/local/bin/python3 import requests import os import json import textwrap WAKATIME_API_KEY = os.getenv("WAKATIME_API_KEY") WAKATIME_ENDPOINT = "https://wakatime.com/api/v1/users/current/status_bar/today" def get_data(url): response = requests.get(url) if response.status_code == 200: return response.json() else: raise Exception( f"Failed to fetch data from API. Status code: {response.status_code}" ) def generate_tooltip(time, languages, projects): return textwrap.dedent( f"""\ Time coding: {time} Languages: {languages} Projects: {projects}""" ) def format_metric(metrics): return ", ".join( [f'{metric["name"]} ({metric["percent"]}%)' for metric in metrics[:3]] ) def main(): output = {} try: data = get_data(WAKATIME_ENDPOINT + "?api_key=" + WAKATIME_API_KEY) digital_time = data["data"]["grand_total"]["digital"] human_time = data["data"]["grand_total"]["text"] langs = data["data"]["languages"] projects = data["data"]["projects"] tooltip = generate_tooltip( human_time, format_metric(langs), format_metric(projects) ) output["text"] = digital_time output["tooltip"] = tooltip except Exception as e: output["text"] = "Error" print(json.dumps(output)) if __name__ == "__main__": main() ``` Here's the module declaration in the Waybar config: ``` "custom/wakatime": { "exec": "source $HOME/dotfiles/.env && python3 $HOME/.config/waybar/resources/custom_modules/wakatime_waybar.py", "format": "󰅱 {}", "return-type": "json", "interval": 600 }, ``` ## Time-tracking module I've recently started using [time warrior](https://timewarrior.net/) to track my extra-curricular coding and study. This is a command-line time-tracker so it integrates really well with Waybar. The module highlights green when there is an active timer. This helps to remind me to stop the timer! When you click the current timer stops and when you right-click it resumes. This saves me going into the terminal when I stopping and starting an ongoing piece of work. ![](./img/timer-module.png)
Time warrior module in its active state.
Again I've used Python for the scripting: ```py #! /usr/local/bin/python3 import subprocess import json def invoke_shell(proc): try: result = subprocess.run( proc, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, ) return result.stdout.strip() except subprocess.CalledProcessError as e: return e.stderr.strip() def timer_active() -> bool: status = invoke_shell("timew get dom.active") if status == "1": return True else: return False def generate_tooltip(): tooltip = invoke_shell("timew summary :week") return tooltip def main(): output = {} try: if timer_active(): output["text"] = "󱫐 Timer" output["class"] = "active" else: output["text"] = "󱫦 Timer" output["class"] = "inactive" except Exception as e: output["text"] = "Error" print(json.dumps(output)) if __name__ == "__main__": main() ``` And here is the module declaration in the Waybar config: ``` "custom/timewarrior": { "exec": "python3 $HOME/.config/waybar/resources/custom_modules/time_warrior_waybar.py", "format": "{}", "on-click": "timew stop && notify-send 'Time Warrior' 'Timer stopped'", "on-click-right": "timew continue && notify-send 'Time Warrior' 'Timer resumed'", "return-type": "json", "interval": 5 }, ```