This commit is contained in:
zack 2025-05-09 18:43:27 -04:00
parent dd60fce00f
commit fdd9dd20ae
No known key found for this signature in database
GPG key ID: EE8A2B709E2401D1
14 changed files with 544 additions and 204 deletions

View file

@ -48,55 +48,14 @@ in {
@define-color mantle ${colors.mantle.hex};
@define-color crust ${colors.crust.hex};
/* Mullvad specific styles */
#custom-mullvad.connected {
color: @green;
}
#custom-mullvad.disconnected {
color: @red;
}
${builtins.readFile ./mullvad-style.css}
${builtins.readFile ./style.css}
'';
settings = let
# Script to get Mullvad status for Waybar
mullvad-status = pkgs.writeShellScriptBin "mullvad-status-waybar" ''
#!${pkgs.runtimeShell}
set -euo pipefail
# Run mullvad status, capture output, handle potential errors
STATUS=$(mullvad status || echo "Disconnected")
if echo "$STATUS" | grep -q "Connected"; then
# Extract Relay using sed: find line starting with Relay:, remove prefix
SERVER=$(echo "$STATUS" | sed -n 's/^\s*Relay:\s*//p')
# Extract Location using sed: find line starting with Visible location:,
# remove prefix, keep text up to the first comma
LOCATION=$(echo "$STATUS" | sed -n 's/^\s*Visible location:\s*\([^,]*\).*/\1/p')
# Trim potential leading/trailing whitespace just in case sed leaves some
SERVER=$(echo "$SERVER" | sed 's/^[ \t]*//;s/[ \t]*$//')
LOCATION=$(echo "$LOCATION" | sed 's/^[ \t]*//;s/[ \t]*$//')
# Construct tooltip based on extracted info
if [ -n "$SERVER" ] && [ -n "$LOCATION" ]; then
TOOLTIP="Mullvad: Connected via $SERVER ($LOCATION)"
elif [ -n "$SERVER" ]; then
# Fallback if location parsing failed but server was found
TOOLTIP="Mullvad: Connected via $SERVER"
else
# Generic fallback if parsing failed
TOOLTIP="Mullvad: Connected"
fi
# Output JSON for Waybar
# Using nerd font icons: nf-fa-lock (connected), nf-fa-unlock (disconnected)
# Ensure your font supports these glyphs:  (U+F023),  (U+F09C)
echo '{"text": "", "tooltip": "'"$TOOLTIP"'", "class": "connected"}'
else
# Output disconnected status
echo '{"text": "", "tooltip": "Mullvad: Disconnected", "class": "disconnected"}'
fi
'';
# Import the Mullvad scripts
mullvad-status = import ./mullvad-status.nix {inherit pkgs;};
mullvad-server-list = import ./mullvad-server-list.nix {inherit pkgs;};
mullvad-menu = import ./mullvad-menu.nix {inherit pkgs;};
# Script to toggle Mullvad connection
mullvad-toggle = pkgs.writeShellScriptBin "mullvad-toggle" ''
@ -155,6 +114,7 @@ in {
interval = 1;
exec = "${mullvad-status}/bin/mullvad-status-waybar";
"on-click" = "${mullvad-toggle}/bin/mullvad-toggle";
"on-click-right" = "${mullvad-menu}/bin/mullvad-menu";
tooltip = true;
};
@ -217,7 +177,7 @@ in {
"custom/gpu-temp" = {
interval = 10;
exec = "nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader,nounits";
format = "{}°C ";
format = "{}°C ";
tooltip = false;
};
@ -238,8 +198,8 @@ in {
"network#speed" = {
interval = 1;
format = "{ifname}%%";
format-wifi = " {bandwidthDownBytes} {bandwidthUpBytes}";
format-ethernet = " {bandwidthDownBytes} {bandwidthUpBytes}";
format-wifi = " {bandwidthDownBytes} {bandwidthUpBytes}";
format-ethernet = " {bandwidthDownBytes} {bandwidthUpBytes}";
format-disconnected = "󰌙";
tooltip-format = "{ipaddr}";
format-linked = "󰈁 {ifname} (No IP)";
@ -277,7 +237,7 @@ in {
hwmon-path-abs = "/sys/devices/platform/asus-ec-sensors/hwmon/hwmon3";
input_filename = "temp2_input";
critical-threshold = 70;
format = "{temperatureC}°C ";
format = "{temperatureC}°C ";
format-critical = "󰸁 {temperatureC}°C";
};
@ -311,10 +271,10 @@ in {
pulseaudio = {
format = "{icon} {volume}%";
"format-muted" = "";
"format-muted" = "";
"format-icons" = {
headphone = "";
default = ["" ""];
headphone = "";
default = ["" ""];
};
"on-click" = "pavucontrol";
};

View file

@ -0,0 +1,7 @@
{pkgs}: let
script = ./mullvad-menu.sh;
in
pkgs.writeScriptBin "mullvad-menu" ''
#!${pkgs.runtimeShell}
exec ${pkgs.bash}/bin/bash ${script}
''

View file

@ -0,0 +1,126 @@
#!/usr/bin/env bash
set -euo pipefail
# Helper: pick a relay (country [city] [hostname]) via fuzzel + jq
pick_relay() {
local api="$API_RESPONSE"
local country_list country_sel country_code
local city_list city_sel city_code loc_key
local host_list host_sel
# Build "Country Name (cc)" array
mapfile -t country_list < <(
jq -r '
.locations
| to_entries[]
| "\(.value.country) (\(.key|split("-")[0]))"
' <<<"$api" | sort -u
)
country_sel=$(printf '%s\n' "${country_list[@]}" |
fuzzel --dmenu --prompt="Select country:")
[[ -z "$country_sel" ]] && return 1
country_code=$(grep -oP '(?<=\()[^)]+(?=\))' <<<"$country_sel")
# Build "City Name (ccc)" array for that country
mapfile -t city_list < <(
jq -r --arg cc "$country_code" '
.locations
| to_entries[]
| select(.key|startswith("\($cc)-"))
| "\(.value.city) (\(.key|split("-")[1]))"
' <<<"$api" | sort -u
)
if ((${#city_list[@]})); then
city_sel=$(printf '%s\n' "${city_list[@]}" |
fuzzel --dmenu --prompt="Select city in $country_sel:")
[[ -z "$city_sel" ]] && return 1
city_code=$(grep -oP '(?<=\()[^)]+(?=\))' <<<"$city_sel")
loc_key="$country_code-$city_code"
fi
# Optional hostname picker
mapfile -t host_list < <(
jq -r --arg loc "${loc_key:-}" '
( .openvpn.relays[]
, .wireguard.relays[]
, .bridge.relays[] )
| select(.location == $loc)
| .hostname
' <<<"$api" | sort -u
)
if ((${#host_list[@]})); then
host_sel=$(printf '%s\n' "${host_list[@]}" |
fuzzel --dmenu --prompt="Select hostname (optional):")
# if they pick a hostname, we switch to pure-hostname mode
[[ -n "$host_sel" ]] && {
RELAY_CMD_ARGS=("$host_sel")
return 0
}
fi
# Assemble country [city]
RELAY_CMD_ARGS=("$country_code")
[[ -n "${city_code-}" ]] && RELAY_CMD_ARGS+=("$city_code")
return 0
}
# Ensure mullvad CLI exists
if ! command -v mullvad >/dev/null 2>&1; then
echo "Mullvad CLI not found" | fuzzel --dmenu
exit 1
fi
# Fetch status and API once
STATUS_RAW=$(mullvad status 2>/dev/null || echo "Disconnected")
API_RESPONSE=$(curl -s "https://api.mullvad.net/app/v1/relays")
# Determine state and current relay (if any)
if [[ $STATUS_RAW == Connecting* ]]; then
STATE=Connecting
elif grep -q "^Connected" <<<"$STATUS_RAW"; then
STATE=Connected
else
STATE=Disconnected
fi
# Try to parse the current relay hostname for Connected/Connecting
if [[ $STATE != Disconnected ]]; then
CURRENT_RELAY=$(grep -E 'Relay:' <<<"$STATUS_RAW" |
sed -E 's/.*Relay:[[:space:]]*//')
fi
# Main menu
case $STATE in
Connected | Connecting)
# Offer Disconnect or Change Location
CHOICE=$(printf "Disconnect\nChange Location" |
fuzzel --dmenu --prompt="$STATE ${CURRENT_RELAY:-}")
case "$CHOICE" in
Disconnect)
mullvad disconnect
;;
"Change Location")
if pick_relay; then
mullvad relay set location "${RELAY_CMD_ARGS[@]}"
fi
;;
esac
;;
Disconnected)
# Offer Connect or Connect to Location
CHOICE=$(printf "Connect\nConnect to Location" |
fuzzel --dmenu --prompt="Disconnected")
case "$CHOICE" in
Connect)
mullvad connect
;;
"Connect to Location")
if pick_relay; then
mullvad relay set location "${RELAY_CMD_ARGS[@]}"
mullvad connect
fi
;;
esac
;;
esac

View file

@ -0,0 +1,57 @@
{pkgs}:
pkgs.writeShellScriptBin "mullvad-server-list" ''
#!${pkgs.runtimeShell}
set -euo pipefail
# Check if mullvad is installed
if ! command -v mullvad >/dev/null 2>&1; then
echo "Mullvad CLI not found" | fuzzel --dmenu
exit 1
fi
# Get the list of countries
COUNTRIES=$(mullvad relay list | grep -E "^[[:space:]]+[[:alpha:]]" | sed 's/^[[:space:]]*//g')
# If no argument is provided, show the list of countries
if [ $# -eq 0 ]; then
echo "$COUNTRIES" | sort | fuzzel --dmenu --prompt="Select country: "
exit 0
fi
COUNTRY="$1"
# If country is provided but no city, show cities for that country
if [ $# -eq 1 ]; then
CITIES=$(mullvad relay list | grep -A 100 "^[[:space:]]*$COUNTRY" | grep -E "^[[:space:]]{4}[[:alpha:]]" | sed 's/^[[:space:]]*//g' | head -n $(mullvad relay list | grep -A 100 "^[[:space:]]*$COUNTRY" | grep -E "^[[:space:]]{4}[[:alpha:]]" | wc -l))
if [ -z "$CITIES" ]; then
# If no cities found, show servers for this country
SERVERS=$(mullvad relay list | grep -A 100 "^[[:space:]]*$COUNTRY" | grep -E "^[[:space:]]{8}[a-z0-9]+" | sed 's/^[[:space:]]*//g' | cut -d' ' -f1-2)
echo "$SERVERS" | fuzzel --dmenu --prompt="Select server in $COUNTRY: "
else
echo "$CITIES" | fuzzel --dmenu --prompt="Select city in $COUNTRY: "
fi
exit 0
fi
# If both country and city are provided, show servers in that city
CITY="$2"
SERVERS=$(mullvad relay list | grep -A 100 "^[[:space:]]*$COUNTRY" | grep -A 100 "^[[:space:]]*$CITY" | grep -E "^[[:space:]]{8}[a-z0-9]+" | sed 's/^[[:space:]]*//g')
# Extract server information and load (where available)
SERVER_INFO=""
while read -r server; do
# Get server details
SERVER_NAME=$(echo "$server" | awk '{print $1}')
SERVER_TYPE=$(echo "$server" | awk '{print $2}')
# Get server load if available (using 'mullvad relay list --location all')
LOAD_INFO=$(mullvad relay list --location all | grep "$SERVER_NAME" | grep -o '[0-9]\+%' || echo "N/A")
# Add server with load info to the list
SERVER_INFO="${SERVER_INFO}${SERVER_NAME} (${SERVER_TYPE}) - Load: ${LOAD_INFO}"$'\n'
done <<< "$SERVERS"
# Display the server list with load information
echo "$SERVER_INFO" | grep -v "^$" | fuzzel --dmenu --prompt="Select server in $CITY: "
''

View file

@ -0,0 +1,4 @@
{pkgs}:
pkgs.writeShellScriptBin "mullvad-status-waybar" ''
exec ${pkgs.bash}/bin/bash ${./mullvad-status.sh} | jq -c
''

View file

@ -0,0 +1,35 @@
#!/usr/bin/env bash
set -euo pipefail
# Get status (fall back to “Disconnected” on error)
STATUS=$(mullvad status 2>/dev/null || echo "Disconnected")
if echo "$STATUS" | grep -q "^Connected"; then
# Extract relay hostname
SERVER=$(echo "$STATUS" |
sed -n 's/^[[:space:]]*Relay:[[:space:]]*//p' |
sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
# Grab the entire Visible location line (location + IPs)
FULL_LOC=$(echo "$STATUS" |
sed -n 's/^[[:space:]]*Visible location:[[:space:]]*//p')
# Split off the humanreadable location (before first dot)
LOCATION=${FULL_LOC%%.*}
# The part after the first “. ” is the IP info
IPS=${FULL_LOC#*. }
TOOLTIP="Connected via ${SERVER} (${IPS})"
# Emit JSON for Waybar
echo '{"text": "'"${LOCATION}"'"
, "tooltip": "'"${TOOLTIP}"'"
, "class": "connected"
}'
else
echo '{"text": "Disconnected"
, "tooltip": "Mullvad: Disconnected"
, "class": "disconnected"
}'
fi

View file

@ -0,0 +1,18 @@
/* Mullvad specific styles */
#custom-mullvad.connected {
color: @green;
border-bottom: 2px solid @green;
padding-bottom: 2px;
}
#custom-mullvad.disconnected {
color: @red;
border-bottom: 2px solid @red;
padding-bottom: 2px;
}
#custom-mullvad {
margin: 0 8px;
padding: 0 5px;
min-width: 100px;
}

View file

@ -1,5 +1,5 @@
* {
font-family: Iosevka Nerd Font;
font-family: Iosevka Nerd Font, monospace;
min-height: 14px;
font-size: 14px;
border: none;