-
-
Notifications
You must be signed in to change notification settings - Fork 725
Module: Custom
The custom
module displays either the output of a script or static text. To display static text, specify only the format
field.
Addressed by custom/<name>
option | typeof | default | description |
---|---|---|---|
exec |
string | ||
exec-if |
string | The path to a script, which determines if the script in exec should be executed.exec will be executed if the exit code of exec-if equals 0. |
|
exec-on-event |
bool | true |
If an event command is set (e.g. on-click or on-scroll-up ) then re-execute the script after executing the event command. |
return-type |
string | See return-type
|
|
interval |
integer | The interval (in seconds) in which the information gets polled. Use once if you want to execute the module only on startup. You can update it manually with a signal. If no interval is defined, it is assumed that the out script loops it self. |
|
restart-interval |
integer | The restart interval (in seconds). Can't be used with the interval option, so only with continuous scripts. Once the script exit, it'll be re-executed after the restart-interval. |
|
signal |
integer | The signal number used to update the module. The number is valid between 1 and N, where SIGRTMIN+N = SIGRTMAX . |
|
format |
string | {} |
The format, how information should be displayed. On {} data gets inserted. |
format-icons |
array/object/string | If the type is an array, then based on the set percentage, the corresponding icon gets selected (low to high). If the type is an object, then the icon will be selected according to alt string from the output.If the type is a string, it will be pasted as is. NOTE: Arrays can be nested into objects. Icons will be selected first according to alt then percentage. |
|
rotate |
integer | Positive value to rotate the text label. | |
max-length |
integer | The maximum length in character the module should display. | |
on-click |
string | Command to execute when clicked on the module. | |
on-click-middle |
string | Command to execute when you middle clicked on the module using mousewheel. | |
on-click-right |
string | Command to execute when you right clicked on the module. | |
on-scroll-up |
string | Command to execute when scrolling up on the module. | |
on-scroll-down |
string | Command to execute when scrolling down on the module. | |
smooth-scrolling-threshold |
double | Threshold to be used when scrolling. | |
tooltip |
bool | true |
Option to enable tooltip on hover. |
escape |
bool | false |
Option to enable escaping of script output |
- When
return-type
is set tojson
, Waybar expects theexec
-script to output its data in JSON format. This should look like this:{"text": "$text", "alt": "$alt", "tooltip": "$tooltip", "class": "$class", "percentage": $percentage }
. This means the output should also be on a single line. This can be achieved by piping the output of your script throughjq --unbuffered --compact-output
. Theclass
parameter also accepts an array of strings. - If nothing or an invalid option is specified Waybar expects i3blocks style output, where values are
newline
separated. This should look like this:$text\n$tooltip\n$class
class
is a CSS class, to apply different styles in style.css
string | replacement |
---|---|
{} |
Output of the script. |
{percentage} |
Percentage which can be set via a json return-type. |
{icon} |
An icon from 'format-icons' according to percentage. |
#custom-<name>
-
#custom-<name>.<class>
-
<class>
can be set by the script. For more information seereturn-type
-
~/.config/waybar/config
"custom/dunst": {
"exec": "~/.config/waybar/scripts/dunst.sh",
"on-click": "dunstctl set-paused toggle",
"restart-interval": 1,
}
~/.config/waybar/scripts/dunst.sh
#!/bin/bash
COUNT=$(dunstctl count waiting)
ENABLED=
DISABLED=
if [ $COUNT != 0 ]; then DISABLED=" $COUNT"; fi
if dunstctl is-paused | grep -q "false" ; then echo $ENABLED; else echo $DISABLED; fi
Or if you want a version that reacts to dbus events instead:
#!/usr/bin/env bash
set -euo pipefail
readonly ENABLED=' '
readonly DISABLED=' '
dbus-monitor path='/org/freedesktop/Notifications',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged' --profile |
while read -r _; do
PAUSED="$(dunstctl is-paused)"
if [ "$PAUSED" == 'false' ]; then
CLASS="enabled"
TEXT="$ENABLED"
else
CLASS="disabled"
TEXT="$DISABLED"
COUNT="$(dunstctl count waiting)"
if [ "$COUNT" != '0' ]; then
TEXT="$DISABLED ($COUNT)"
fi
fi
printf '{"text": "%s", "class": "%s"}\n' "$TEXT" "$CLASS"
done
Supports vlc, mpv, RhythmBox, web browsers, cmus, mpd, spotify and others.
"custom/media": {
"format": "{icon} {}",
"escape": true,
"return-type": "json",
"max-length": 40,
"on-click": "playerctl play-pause",
"on-click-right": "playerctl stop",
"smooth-scrolling-threshold": 10, // This value was tested using a trackpad, it should be lowered if using a mouse.
"on-scroll-up": "playerctl next",
"on-scroll-down": "playerctl previous",
"exec": "$HOME/.config/waybar/mediaplayer.py 2> /dev/null", // Script in resources/custom_modules folder
}
"custom/spotify": {
"format": "{icon} {}",
"escape": true,
"return-type": "json",
"max-length": 40,
"interval": 30, // Remove this if your script is endless and write in loop
"on-click": "playerctl -p spotify play-pause",
"on-click-right": "killall spotify",
"smooth-scrolling-threshold": 10, // This value was tested using a trackpad, it should be lowered if using a mouse.
"on-scroll-up" : "playerctl -p spotify next",
"on-scroll-down" : "playerctl -p spotify previous",
"exec": "$HOME/.config/waybar/mediaplayer.py 2> /dev/null", // Script in resources/custom_modules folder
"exec-if": "pgrep spotify"
}
"custom/mpd": {
"format": "♪ {}",
//"max-length": 15,
"interval": 10,
"exec": "mpc current",
"exec-if": "pgrep mpd",
"on-click": "mpc toggle",
"on-click-right": "sonata"
}
"custom/cmus": {
"format": "♪ {}",
//"max-length": 15,
"interval": 10,
"exec": "cmus-remote -C \"format_print '%a - %t'\"", // artist - title
"exec-if": "pgrep cmus",
"on-click": "cmus-remote -u", //toggle pause
"escape": true //handle markup entities
}
"custom/media": {
"format": "{icon}{}",
"return-type": "json",
"format-icons": {
"Playing": " ",
"Paused": " ",
},
"max-length":70,
"exec": "playerctl -a metadata --format '{\"text\": \"{{playerName}}: {{artist}} - {{markup_escape(title)}}\", \"tooltip\": \"{{playerName}} : {{markup_escape(title)}}\", \"alt\": \"{{status}}\", \"class\": \"{{status}}\"}' -F",
"on-click": "playerctl play-pause",
}
Uses Wireplumber
~/.config/waybar/config
"custom/pipewire": {
"tooltip": false,
"max-length": 6,
"exec": "$HOME/.config/waybar/scripts/pipewire.sh",
"on-click": "pavucontrol",
"on-click-right": "qpwgraph"
}
~/.config/waybar/scripts/pipewire.sh
#!/bin/bash
set -e
# https://blog.dhampir.no/content/sleeping-without-a-subprocess-in-bash-and-how-to-sleep-forever
snore() {
local IFS
[[ -n "${_snore_fd:-}" ]] || exec {_snore_fd}<> <(:)
read -r ${1:+-t "$1"} -u $_snore_fd || :
}
DELAY=0.2
while snore $DELAY; do
WP_OUTPUT=$(wpctl get-volume @DEFAULT_AUDIO_SINK@)
if [[ $WP_OUTPUT =~ ^Volume:[[:blank:]]([0-9]+)\.([0-9]{2})([[:blank:]].MUTED.)?$ ]]; then
if [[ -n ${BASH_REMATCH[3]} ]]; then
printf "MUTE\n"
else
VOLUME=$((10#${BASH_REMATCH[1]}${BASH_REMATCH[2]}))
ICON=(
""
""
""
)
if [[ $VOLUME -gt 50 ]]; then
printf "%s" "${ICON[0]} "
elif [[ $VOLUME -gt 25 ]]; then
printf "%s" "${ICON[1]} "
elif [[ $VOLUME -ge 0 ]]; then
printf "%s" "${ICON[2]} "
fi
printf "$VOLUME%%\n"
fi
fi
done
exit 0
"custom/pacman": {
"format": "{} ",
"interval": "once",
"exec": "pacman_packages",
"on-click": "update-system",
"signal": 8
}
//alternate
"custom/pacman": {
"format": "{} ",
"interval": 3600, // every hour
"exec": "checkupdates | wc -l", // # of updates
"exec-if": "exit 0", // always run; consider advanced run conditions
"on-click": "termite -e 'sudo pacman -Syu'; pkill -SIGRTMIN+8 waybar", // update system
"signal": 8
}
You can use the signal and update the number of available packages with pkill -RTMIN+8 waybar
.
"custom/deadbeef": {
"format": " {}",
"max-length": 50,
"interval": 10,
"exec": "deadbeef --nowplaying-tf '{\"text\": \"%title%\", \"tooltip\":\"%artist% - %title%\",\"class\":\"$if(%isplaying%,playing,not-playing)\"}'",
"return-type": "json",
"exec-if": "pgrep deadbeef",
"on-click": "deadbeef --toggle-pause"
}
(the indicator is quite silly and only checks whether a tunnel exists or not)
"custom/vpn": {
"format": "VPN ",
"exec": "echo '{\"class\": \"connected\"}'",
"exec-if": "test -d /proc/sys/net/ipv4/conf/tun0",
"return-type": "json",
"interval": 5
}
"custom/github": {
"format": "{} ",
"return-type": "json",
"interval": 60,
"exec": "$HOME/.config/waybar/github.sh",
"on-click": "xdg-open https://github.com/notifications"
}
- Make sure
jq
is installed. - Create
notifications.token
, a personal access token, withnotifications
in scope at https://github.com/settings/tokens. - Create
github.sh
with the contents below, replacingusername
with your own.
#!/bin/bash
token=`cat ${HOME}/.config/github/notifications.token`
count=`curl -u username:${token} https://api.github.com/notifications | jq '. | length'`
if [[ "$count" != "0" ]]; then
echo '{"text":'$count',"tooltip":"$tooltip","class":"$class"}'
fi
Replace Berlin+Germany
with your own city.
"custom/weather": {
"exec": "$XDG_CONFIG_HOME/waybar/get_weather.sh Berlin+Germany",
"return-type": "json",
"format": "{}",
"tooltip": true,
"interval": 3600
}
#!/usr/bin/env bash
# get_weather.sh
for i in {1..5}
do
text=$(curl -s "https://wttr.in/$1?format=1")
if [[ $? == 0 ]]
then
text=$(echo "$text" | sed -E "s/\s+/ /g")
tooltip=$(curl -s "https://wttr.in/$1?format=4")
if [[ $? == 0 ]]
then
tooltip=$(echo "$tooltip" | sed -E "s/\s+/ /g")
echo "{\"text\":\"$text\", \"tooltip\":\"$tooltip\"}"
exit
fi
fi
sleep 2
done
echo "{\"text\":\"error\", \"tooltip\":\"error\"}"
Requires jq
Get all the scratchpad nodes. Shows the count as module text and the window class/app_id, id, and name on hover, and doesn't display anything if there are no nodes in the scratchpad.
"custom/scratchpad-indicator": {
"interval": 3,
"return-type": "json",
"exec": "swaymsg -t get_tree | jq --unbuffered --compact-output '(recurse(.nodes[]) | select(.name == \"__i3_scratch\") | .focus) as $scratch_ids | [.. | (.nodes? + .floating_nodes?) // empty | .[] | select(.id |IN($scratch_ids[]))] as $scratch_nodes | if ($scratch_nodes|length) > 0 then { text: \"\\($scratch_nodes | length)\", tooltip: $scratch_nodes | map(\"\\(.app_id // .window_properties.class) (\\(.id)): \\(.name)\") | join(\"\\n\") } else empty end'",
"format": "{} 🗗",
"on-click": "exec swaymsg 'scratchpad show'",
"on-click-right": "exec swaymsg 'move scratchpad'"
}
A simpler version, that only shows the number of windows when there is at least one (hidden when there are 0). Shows no additional info on hover.
"custom/scratchpad_indicator": {
"interval": 3,
"exec": "swaymsg -t get_tree | jq 'recurse(.nodes[]) | first(select(.name==\"__i3_scratch\")) | .floating_nodes | length | select(. >= 1)'",
"format": "{} ",
"on-click": "swaymsg 'scratchpad show'",
"on-click-right": "swaymsg 'move scratchpad'"
}
"custom/output-scale": {
"format": "{icon} {}",
"return-type": "json",
"format-icons": { // These are FontAwesome 4 icons. Update them as needed.
"scale": " \uf0b2",
"noscale": "\uf066"
},
"exec-on-event": true,
"interval": "once",
"exec": "( swaymsg -r -t get_outputs | jq '.[0].scale' | xargs test 1 == ) && echo '{\"alt\": \"noscale\"}' || echo '{\"alt\":\"scale\"}'",
"exec-if": "sleep 0.1", // Give enough time for `sway output` command changes to propagate so we can read them in the next `exec`
"on-click": "( swaymsg -r -t get_outputs | jq '.[0].scale' | xargs test 1 = ) && swaymsg output DP-1 scale 1.4 || swaymsg output DP-1 scale 1"
}
- Change the desired scaling parameter in
on-click
configuration. - Update the correct output from
DP-1
to the one you have. - Change the index
[0]
inexec
andon-click
if you have more than one output, and need to adjust non-zero output.
"custom/pulseaudio-cycle": {
"return-type": "json",
"exec-on-event": true,
"interval": "5s",
"exec" "pactl --format=json list sinks | jq -cM --unbuffered \"map(select(.name == \\\"$(pactl get-default-sink)\\\"))[0].properties | [.\\\"media.name\\\",.\\\"alsa.name\\\",.\\\"node.nick\\\",.\\\"alsa.long_card_name\\\"] | map(select(length>0))[0] | {text:.}\"",
"exec-if": "sleep 0.1", // Give enough time for `pactl get-default-sink` to update
"on-click": "pactl --format=json list sinks short | jq -cM --unbuffered \"[.[].name] | .[((index(\\\"$(pactl get-default-sink)\\\")+1)%length)]\" | xargs pactl set-default-sink"
}
- Next calendar events (using khal)
- Chromecast currently playing (using catt)
- Weather with detailed forecast (using wttr.in)
- Another Weather module (using weather.com)
- External screen brightness (using ddcutil)
- Another external screen brightness (using ddcutil too)
- Screen brightness without external scripts (still ddcutil)
- Reminders (using remind)
- WireGuard (using NetworkManager/nmcli)
-
GPU usage monitor (using data from
/sys/class/hwmon/
) - UPower (Customizable energy/battery information using UPower dbus (battery, bluetooth, line-power etc))
- Screenrecorder (record your screen into a file)
- Sway WM adaptive sync toggle (using Sway IPC)
- Pacman updates
- Pacman and AUR updates
- Notifications for Arch Linux package updates
- RI - Rest your Eyes and Self
- Home
- Installation
- Configuration
- Styling
- Examples
- FAQ
- Modules:
- Backlight/Slider
- Backlight
- Battery
- Bluetooth
- CPU
- Cava
- Clock
- Custom
- DWL
- Disk
- Gamemode
- Group
- Hyprland
- Idle Inhibitor
- Image
- JACK
- Keyboard State
- Language
- Load
- MPD
- MPRIS
- Memory
- Network
- Niri
- Power Profiles Daemon
- Privacy
- PulseAudio/Slider
- PulseAudio
- River
- Sndio
- Sway
- Systemd failed units
- Taskbar
- Temperature
- Tray
- UPower
- User
- WirePlumber
- Workspaces
- Writing Modules