Files
dotfiles/scripts/wireguard-status.sh
Hermes 08f7a1a9ee feat: add WireGuard Waybar custom module
- Added scripts/wireguard-status.sh — JSON output with lock/unlock icons,
  human-readable TX/RX stats, tooltip with peer/endpoint/IP details
- Added custom/wireguard module to Waybar modules.json (10s interval, JSON
  return type, click left/right for up/down)
- Added custom/wireguard to modules-right bar config
- Added CSS for #custom-wireguard (connected/disconnected classes)
- Added openresolv install and Waybar refresh instructions to README.org
2026-05-20 14:33:28 -04:00

130 lines
4.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# wireguard-status.sh -- JSON status for Waybar
# Outputs: {"text":"...","class":"connected|disconnected","tooltip":"..."}
#
# Waybar config example (in ~/.config/waybar/config.jsonc):
# "custom/wireguard": {
# "exec": "$HOME/.config/waybar/scripts/wireguard-status.sh",
# "interval": 10,
# "return-type": "json"
# }
set -euo pipefail
# --- Config -------------------------------------------------------
ICON_LOCKED="\uf023" # nf-fa-lock (FontAwesome)
ICON_UNLOCKED="\uf09e" # nf-fa-unlock
# Override with environment variables if set:
ICON_LOCKED="${WG_ICON_LOCKED:-$ICON_LOCKED}"
ICON_UNLOCKED="${WG_ICON_UNLOCKED:-$ICON_UNLOCKED}"
# --- Helpers ------------------------------------------------------
die() { echo "$*" >&2; exit 1; }
# Find the wg binary — works on NixOS, standard Linux, etc.
find_wg() {
if command -v wg &>/dev/null; then
echo "wg"
elif [ -x /run/current-system/sw/bin/wg ]; then
echo "/run/current-system/sw/bin/wg"
elif [ -x /usr/bin/wg ]; then
echo "/usr/bin/wg"
else
return 1
fi
}
# Find the ip binary
find_ip() {
if command -v ip &>/dev/null; then
echo "ip"
elif [ -x /run/current-system/sw/bin/ip ]; then
echo "/run/current-system/sw/bin/ip"
elif [ -x /usr/sbin/ip ]; then
echo "/usr/sbin/ip"
elif [ -x /sbin/ip ]; then
echo "/sbin/ip"
else
return 1
fi
}
format_bytes() {
local bytes=$1
if [ "$bytes" -lt 1024 ]; then
echo "${bytes}B"
elif [ "$bytes" -lt $((1024 * 1024)) ]; then
echo "$(awk "BEGIN { printf \"%.1f KiB\", $bytes / 1024 }")"
elif [ "$bytes" -lt $((1024 * 1024 * 1024)) ]; then
echo "$(awk "BEGIN { printf \"%.1f MiB\", $bytes / (1024 * 1024) }")"
else
echo "$(awk "BEGIN { printf \"%.2f GiB\", $bytes / (1024 * 1024 * 1024) }")"
fi
}
# --- Main ---------------------------------------------------------
WG_BIN=$(find_wg 2>/dev/null) || {
# wg not installed — report as disconnected
printf '{"text":"%s","class":"disconnected","tooltip":"WireGuard not installed"}\n' "$ICON_UNLOCKED"
exit 0
}
IP_BIN=$(find_ip 2>/dev/null) || true
# Find all WireGuard interfaces
INTERFACES=$("$WG_BIN" show interfaces 2>/dev/null) || {
# No WireGuard interfaces exist (wg module not loaded?)
printf '{"text":"%s","class":"disconnected","tooltip":"No WireGuard interfaces"}\n' "$ICON_UNLOCKED"
exit 0
}
# Parse into array, handling possible single-line space-separated output
IFS=' ' read -ra ifaces <<< "$INTERFACES"
if [ ${#ifaces[@]} -eq 0 ]; then
printf '{"text":"%s","class":"disconnected","tooltip":"No WireGuard interfaces"}\n' "$ICON_UNLOCKED"
exit 0
fi
# Just use the first interface (most setups have one)
IFACE="${ifaces[0]}"
# Get transfer stats
TRANSFER=$("$WG_BIN" show "$IFACE" transfer 2>/dev/null) || {
printf '{"text":"%s","class":"disconnected","tooltip":"Cannot read stats for %s"}\n' "$ICON_UNLOCKED" "$IFACE"
exit 0
}
# Parse: "rx_bytes tx_bytes"
RX_BYTES=$(echo "$TRANSFER" | awk '{print $1}')
TX_BYTES=$(echo "$TRANSFER" | awk '{print $2}')
RX_HUMAN=$(format_bytes "${RX_BYTES:-0}")
TX_HUMAN=$(format_bytes "${TX_BYTES:-0}")
# Get endpoint info for tooltip
ENDPOINT=$("$WG_BIN" show "$IFACE" endpoints 2>/dev/null | head -1 | awk '{print $2}') || ENDPOINT=""
PEER=$("$WG_BIN" show "$IFACE" peers 2>/dev/null | head -1) || PEER=""
# Build status line
STATUS_TEXT="${ICON_LOCKED} \u2193${RX_HUMAN} \u2191${TX_HUMAN}"
# Build tooltip
TOOLTIP="${IFACE}"
if [ -n "$PEER" ]; then
TOOLTIP="${TOOLTIP}\nPeer: ${PEER}"
fi
if [ -n "$ENDPOINT" ]; then
TOOLTIP="${TOOLTIP}\nEndpoint: ${ENDPOINT}"
fi
# Add IP info if ip is available
if [ -n "$IP_BIN" ]; then
IFACE_IP=$("$IP_BIN" -4 addr show dev "$IFACE" 2>/dev/null | awk '/inet / {print $2}') || IFACE_IP=""
if [ -n "$IFACE_IP" ]; then
TOOLTIP="${TOOLTIP}\nIP: ${IFACE_IP}"
fi
fi
TOOLTIP="${TOOLTIP}\nRX: ${RX_HUMAN}\nTX: ${TX_HUMAN}"
printf '{"text":"%s","class":"connected","tooltip":"%s"}\n' "$STATUS_TEXT" "$TOOLTIP"