Skip to content

Instantly share code, notes, and snippets.

@Dulani
Created July 30, 2026 16:19
Show Gist options
  • Select an option

  • Save Dulani/6b94d0e2b9e26007cfa234abd3a95381 to your computer and use it in GitHub Desktop.

Select an option

Save Dulani/6b94d0e2b9e26007cfa234abd3a95381 to your computer and use it in GitHub Desktop.
Swaybar / Waybar custom configuration and weather scripts
// -*- mode: jsonc -*-
{
// "layer": "top", // Waybar at top layer
// "position": "bottom", // Waybar position (top|bottom|left|right)
"height": 30, // Waybar height (to be removed for auto height)
// "width": 1280, // Waybar width
"spacing": 4, // Gaps between modules (4px)
// Choose the order of the modules
"modules-left": [
"sway/workspaces",
"sway/mode",
"sway/scratchpad"
],
"modules-center": [
"sway/window"
],
"modules-right": [
"idle_inhibitor",
"pulseaudio",
"cpu",
"memory",
"disk",
"battery",
"network",
"custom/weather",
"custom/sun",
"clock",
"tray"
],
// Modules configuration
"sway/window": {
"swap-icon-label": false
},
"sway/workspaces": {
// "disable-scroll": true,
// "all-outputs": true,
// "warp-on-scroll": false,
// "format": "{name}: {icon}",
// "format-icons": {
// "1": "",
// "2": "",
// "3": "",
// "4": "",
// "5": "",
// "urgent": "",
// "focused": "",
// "default": ""
// }
},
"keyboard-state": {
"numlock": true,
"capslock": true,
"format": "{name} {icon}",
"format-icons": {
"locked": "",
"unlocked": ""
}
},
"sway/mode": {
"format": "<span style=\"italic\">{}</span>"
},
"sway/scratchpad": {
"format": "{icon} {count}",
"show-empty": false,
"format-icons": ["", ""],
"tooltip": true,
"tooltip-format": "{app}: {title}"
},
"mpd": {
"format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ⸨{songPosition}|{queueLength}⸩ {volume}% ",
"format-disconnected": "Disconnected ",
"format-stopped": "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped ",
"unknown-tag": "N/A",
"interval": 5,
"consume-icons": {
"on": " "
},
"random-icons": {
"off": "<span color=\"#f53c3c\"></span> ",
"on": " "
},
"repeat-icons": {
"on": " "
},
"single-icons": {
"on": "1 "
},
"state-icons": {
"paused": "",
"playing": ""
},
"tooltip-format": "MPD (connected)",
"tooltip-format-disconnected": "MPD (disconnected)"
},
"idle_inhibitor": {
"format": "{icon}",
"format-icons": {
"activated": "",
"deactivated": ""
}
},
"tray": {
// "icon-size": 21,
"spacing": 10,
"swap-icon-label": false,
// "icons": {
// "blueman": "bluetooth",
// "TelegramDesktop": "$HOME/.local/share/icons/hicolor/16x16/apps/telegram.png"
// }
},
"clock": {
// "timezone": "America/New_York",
"tooltip-format": "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>",
"format-alt": "{:%Y-%m-%d}"
},
"cpu": {
"format": "{usage}% ",
"tooltip": false
},
"memory": {
"format": "{}% "
},
"disk": {
"format": "{percentage_free}% ",
"path": "/"
},
"custom/weather": {
"format": "{}",
"exec": "/home/dulani/.config/waybar/scripts/weather.sh",
"return-type": "json",
"interval": 1800
},
"custom/sun": {
"format": "{}",
"exec": "/home/dulani/.config/waybar/scripts/sun.sh",
"return-type": "json",
"interval": 60
},
"battery": {
"states": {
// "good": 95,
"warning": 30,
"critical": 15
},
"format": "{capacity}% {icon}",
"format-full": "{capacity}% {icon}",
"format-charging": "{capacity}% ",
"format-plugged": "{capacity}% ",
"format-alt": "{time} {icon}",
// "format-good": "", // An empty format will hide the module
// "format-full": "",
"format-icons": ["", "", "", "", ""]
},
"battery#bat2": {
"bat": "BAT2"
},
"network": {
// "interface": "wlp2*", // (Optional) To force the use of this interface
"format-wifi": "{essid} ({signalStrength}%) ",
"format-ethernet": "{ipaddr}/{cidr} ",
"tooltip-format": "{ifname} via {gwaddr} ",
"format-linked": "{ifname} (No IP) ",
"format-disconnected": "Disconnected ⚠",
"format-alt": "{ifname}: {ipaddr}/{cidr}"
},
"pulseaudio": {
// "scroll-step": 1, // %, can be a float
"format": "{volume}% {icon} {format_source}",
"format-bluetooth": "{volume}% {icon} {format_source}",
"format-bluetooth-muted": " {icon} {format_source}",
"format-muted": " {format_source}",
"format-source": "{volume}% ",
"format-source-muted": "",
"format-icons": {
"headphone": "",
"hands-free": "",
"headset": "",
"phone": "",
"portable": "",
"car": "",
"default": ["", "", ""]
},
"on-click": "pavucontrol"
},
"custom/media": {
"format": "{icon} {text}",
"return-type": "json",
"max-length": 40,
"format-icons": {
"spotify": "",
"default": "🎜"
},
"escape": true,
"exec": "$HOME/.config/waybar/mediaplayer.py 2> /dev/null" // Script in resources folder
// "exec": "$HOME/.config/waybar/mediaplayer.py --player spotify 2> /dev/null" // Filter player based on name
},
"custom/power": {
"format" : "⏻ ",
"tooltip": false,
"menu": "on-click",
"menu-file": "$HOME/.config/waybar/power_menu.xml", // Menu file in resources folder
"menu-actions": {
"shutdown": "shutdown",
"reboot": "reboot",
"suspend": "systemctl suspend",
"hibernate": "systemctl hibernate"
}
}
}
/* Define a subtle color palette */
@define-color base03 #1a1a1a;
@define-color base02 #2d2d2d;
@define-color text #e0e0e0;
@define-color accent #66cc99;
@define-color cpu-color #81a1c1;
@define-color mem-color #b48ead;
@define-color net-color #ebcb8b;
@define-color batt-color #a3be8c;
* {
font-family: 'JetBrainsMono Nerd Font', 'Font Awesome 6 Free', 'Font Awesome 6 Brands', monospace;
font-size: 12px;
border: none;
border-radius: 0;
color: @text;
}
window#waybar {
background-color: @base02;
margin: 10px 20px;
border-radius: 15px;
padding: 0 10px;
}
/* Apply spacing and dividers to individual modules */
#idle_inhibitor,
#pulseaudio,
#cpu,
#memory,
#disk,
#battery,
#network,
#custom-weather,
#custom-sun,
#clock,
#tray {
padding: 0 5px;
border-right: 2px solid rgba(255, 255, 255, 0.5);
}
button {
background: transparent;
box-shadow: none;
border: none;
border-radius: 10px;
}
button:hover {
background: rgba(255, 255, 255, 0.1);
}
#workspaces button {
padding: 0 10px;
}
#workspaces button.focused {
background-color: rgba(255, 255, 255, 0.2);
color: @accent;
}
#cpu { color: @cpu-color; }
#memory { color: @mem-color; }
#network { color: @net-color; }
#battery { color: @batt-color; }
#clock,
#pulseaudio,
#wireplumber,
#custom-media,
#tray,
#mode,
#idle_inhibitor,
#scratchpad,
#power-profiles-daemon,
#mpd {
padding: 0 10px;
background-color: transparent;
}
#!/bin/bash
DATA=$(curl -s "wttr.in/?format=j1")
SUNRISE=$(echo "$DATA" | jq -r '.weather[0].astronomy[0].sunrise')
SUNSET=$(echo "$DATA" | jq -r '.weather[0].astronomy[0].sunset')
# Convert sunset time to seconds for time comparison
sunset_sec=$(date -d "$SUNSET" +%s)
now_sec=$(date +%s)
if [ "$now_sec" -gt "$sunset_sec" ]; then
label="Sunrise"
time_val="$SUNRISE"
else
label="Sunset"
time_val="$SUNSET"
fi
# Output JSON for Waybar
jq -n -c --arg text "$label: $time_val" --arg tooltip "$label at $time_val" '{text: $text, tooltip: $tooltip}'
#!/bin/bash
# Shared functions for Waybar weather scripts
CONFIG_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/waybar/weather-locations.json"
# Get current WiFi SSID
get_current_ssid() {
# Use nmcli, the primary tool on Fedora
nmcli -t -f SSID dev wifi list --rescan no 2>/dev/null | grep -v "^--$" | head -1
}
# Get location by SSID from JSON
get_location_by_ssid() {
local ssid="$1"
jq -r ".locations[] | select(.ssid == \"$ssid\") | \"\(.latitude),\(.longitude),\(.city),\(.icao)\"" "$CONFIG_FILE" 2>/dev/null
}
# Determine current location based on SSID or fallback
get_current_location() {
local ssid
ssid=$(get_current_ssid)
local location
if [[ -n "$ssid" ]]; then
location=$(get_location_by_ssid "$ssid")
fi
if [[ -z "$location" ]]; then
# Fallback to JSON fallback location (ICAO will be empty/null)
jq -r '.fallback | "\(.latitude),\(.longitude),\(.city),"' "$CONFIG_FILE"
else
echo "$location"
fi
}
# Parse location string into variables (lat, lon, city, icao)
parse_location() {
IFS=',' read -r LAT LON CITY ICAO <<< "$1"
}
# Get airport weather data using ICAO
fetch_airport_weather() {
curl -s --connect-timeout 5 "https://aviationweather.gov/api/data/metar?ids=$1&format=json"
}
fetch_wttr() {
curl -s --connect-timeout 5 "wttr.in/?format=j1"
}
fetch_owm() {
local key_file="${XDG_CONFIG_HOME:-$HOME/.config}/waybar/openweathermap.key"
if [[ -f "$key_file" ]]; then
local api_key
api_key=$(cat "$key_file")
curl -s --connect-timeout 5 "https://api.openweathermap.org/data/2.5/weather?lat=$LAT&lon=$LON&appid=$api_key&units=imperial"
fi
}
{
"version": "1.0",
"description": "WiFi SSID to geographic location mapping",
"locations": [
{
"ssid": "LongAcres",
"latitude": 40.2198,
"longitude": -84.6292,
"city": "Versailles, OH",
"timezone": "America/New_York",
"icao": "KVES"
},
{
"ssid": "BaerWoods",
"latitude": 39.9575,
"longitude": -84.2045,
"city": "Troy, OH",
"timezone": "America/New_York",
"icao": "KDAY"
}
],
"fallback": {
"latitude": 40.7128,
"longitude": -74.0060,
"city": "Default Location",
"timezone": "America/New_York"
}
}
#!/bin/bash
# Source the location library
source "${XDG_CONFIG_HOME:-$HOME/.config}/waybar/scripts/weather-lib.sh"
# Get current location based on SSID or fallback
LOC=$(get_current_location)
parse_location "$LOC"
# Try airport METAR if ICAO is available
if [[ -n "$ICAO" ]]; then
DATA=$(fetch_airport_weather "$ICAO")
else
DATA=""
fi
# Fallback chain if AWC fails or ICAO missing
if ! echo "$DATA" | jq -e . &>/dev/null; then
# Fallback to wttr.in
DATA=$(fetch_wttr)
# Check if valid JSON and has data
if ! echo "$DATA" | jq -e . &>/dev/null; then
# Fallback to OWM
DATA=$(fetch_owm)
fi
fi
# Parsing logic (Needs adjustments for OWM JSON format vs wttr.in)
# OWM: .main.temp, .sys.sunrise, .sys.sunset
# This is a simplification and would require different parsing per source.
# I'll keep the existing structure as it's complex to unify without a wrapper.
TEMP=$(echo "$DATA" | jq -r '.current_condition[0].temp_F // .main.temp // "ERR"')
SUNRISE=$(echo "$DATA" | jq -r '.weather[0].astronomy[0].sunrise // .sys.sunrise // "ERR"')
SUNSET=$(echo "$DATA" | jq -r '.weather[0].astronomy[0].sunset // .sys.sunset // "ERR"')
# Re-validate ERR
[[ "$TEMP" == "null" ]] && TEMP="ERR"
[[ "$SUNRISE" == "null" ]] && SUNRISE="ERR"
[[ "$SUNSET" == "null" ]] && SUNSET="ERR"
jq -n -c \
--arg text "$TEMP°F" \
--arg tooltip "City: $CITY\nSunrise: $SUNRISE, Sunset: $SUNSET" \
'{text: $text, tooltip: $tooltip}'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment