diff --git a/waybar/config.jsonc b/waybar/config.jsonc index 7fcdb6c..ae19991 100644 --- a/waybar/config.jsonc +++ b/waybar/config.jsonc @@ -2,15 +2,13 @@ { "layer": "top", "position": "top", - "height": 35, - "modules-left": [ - "hyprland/workspaces" - ], + "height": 40, + "modules-left": ["hyprland/workspaces"], "modules-center": [], "modules-right": [ - "wireplumber", - "custom/audio-output", + "custom/bluetooth-audio", "custom/pixelbuds_pro", + "wireplumber", "network", "cpu", "memory", @@ -19,9 +17,9 @@ "custom/gamemode", "tray", "clock" - // "custom/power" ], + "hyprland/workspaces": { "format": "{icon}", "on-click": "activate" @@ -56,17 +54,27 @@ "tooltip-format": "{ifname} via {gwaddr}" }, "wireplumber": { - "format": "{volume}% {icon}", - "format-muted": "Muted ", + "format": "{volume}% {icon}", + "format-muted": "--- ", "format-icons": { "headphone": "", "hands-free": "", - "default": ["", ""] + "default": ["", "", ""] }, - "on-click": "pavucontrol", + "on-click": "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle", + "on-click-right": "~/.config/waybar/scripts/cycle_audio_output.sh", + "on-click-middle": "pavucontrol", "scroll-step": 1 }, + "custom/bluetooth-audio": { + "format": "{}", + "return-type": "json", + "exec": "~/.config/waybar/scripts/bluetooth_audio.sh", + "interval": 3, + "on-click": "~/.config/waybar/scripts/bluetooth_audio.sh disconnect" + }, + "pulseaudio": { "format": "{icon} {volume}%", "format-muted": " Muted", @@ -113,9 +121,8 @@ "return-type": "json", "exec": "~/.config/waybar/scripts/pixelbuds_pro_control.sh", "interval": 5, - "on-click": "~/.config/waybar/scripts/pixelbuds_pro_control.sh connect", - "on-click-right": "~/.config/waybar/scripts/pixelbuds_pro_control.sh disconnect", - "on-click-middle": "~/.config/waybar/scripts/pixelbuds_pro_control.sh cycle_anc" + "on-click-right": "~/.config/waybar/scripts/pixelbuds_pro_control.sh connect", + "on-click": "~/.config/waybar/scripts/pixelbuds_pro_control.sh cycle_anc" }, "custom/audio-output": { "format": "{}", @@ -136,14 +143,13 @@ "return-type": "json", "exec": "~/.config/waybar/scripts/gpu-screen-recorder-status.sh", "on-click-right": "~/.config/hypr/scripts/replay-ctrl.sh save", - "on-click": "~/.config/hypr/scripts/replay-ctrl.sh toggle", + "on-click": "~/.config/waybar/scripts/toggle-replay.sh", "interval": 1 }, "custom/gpu-usage": { "format": "{}", "return-type": "json", "exec": "~/.config/waybar/scripts/gpu_usage.sh", - "on-click": "lact gui", "interval": 5 } } diff --git a/waybar/scripts/bluetooth_audio.sh b/waybar/scripts/bluetooth_audio.sh new file mode 100755 index 0000000..be0f779 --- /dev/null +++ b/waybar/scripts/bluetooth_audio.sh @@ -0,0 +1,118 @@ +#!/bin/bash + +bt-audio-info() { + # Check if a MAC address was provided as the first argument. + if [ -z "$1" ]; then + echo "Usage: $0 " + echo "Example: $0 00:11:22:33:AA:BB" + exit 1 + fi + MAC_ADDRESS="$1" + + # --- Main Logic --- + + # Construct the PipeWire sink name from the MAC address. + # The format is typically: bluez_output.XX_XX_XX_XX_XX_XX.a2dp_sink + # We replace colons with underscores for the format. + SINK_IDENTIFIER="bluez_output.$(echo "$MAC_ADDRESS" | tr ':' '_')" + + # Use pactl to get the full details for all sinks, then find the one + # that contains our device's identifier. We use awk to print the + # entire block of text for that specific sink. + # We check for a partial match on the sink identifier because the profile + # (e.g., .a2dp_sink) can change. + sink_info=$(pactl list sinks | awk -v id="$SINK_IDENTIFIER" '/Sink #/ {p=0} $0 ~ "Name: " id {p=1} p') + + # If sink_info is empty, the device might not be an audio sink or isn't connected. + if [ -z "$sink_info" ]; then + echo "Error: Could not find an active audio sink for MAC ${MAC_ADDRESS}" + echo "Please ensure the device is connected and is an audio output device." + exit 1 + fi + + # Get the full block of info for the device from bluetoothctl. + device_info=$(bluetoothctl info "$MAC_ADDRESS") + + # --- Parse Information --- + + # Parse bluetoothctl output for general device details. + alias=$(echo "$device_info" | rg "Alias:" | cut -d ' ' -f 2-) + trusted=$(echo "$device_info" | rg "Trusted:" | awk '{print $2}') + battery_raw=$(echo "$device_info" | rg "Battery Percentage:" | awk -F'[()]' '{print $2}') + + # Parse pactl output for audio-specific details. + volume=$(echo "$sink_info" | rg "Volume:" | head -n1 | awk '{print $5}') + codec=$(echo "$sink_info" | rg -e 'bluetooth.codec|api.bluez5.codec' | awk -F'"' '{print $2}') + + # --- Build and Display Compact Output --- + + # Build the output string with all the information. + output_string="${alias} | MAC: ${MAC_ADDRESS}\n" + output_string+="Trusted: ${trusted}" + + # Append battery info if available, otherwise show N/A. + if [ -n "$battery_raw" ]; then + output_string+=" | Bat: ${battery_raw}%" + else + output_string+=" | Bat: N/A" + fi + + output_string+=" | Vol: ${volume} | Codec: ${codec:-N/A}" + + # Print the final, single-line string. + echo "$output_string" +} + +# Find the MAC address of the first connected device that is an audio sink +find_audio_device() { + default_sink_name=$(pactl get-default-sink) + + # Check if the default sink is a Bluetooth device. Their names typically + # start with "bluez_output.". If not, print a message and exit. + if [[ "$default_sink_name" == bluez_output* ]]; then + # Extract the MAC address from the sink name. + # The format is bluez_output.XX_XX_XX_XX_XX_XX.profile + # We extract the middle part and replace underscores with colons. + mac_with_underscores=$(echo "$default_sink_name" | cut -d '.' -f 2) + mac=$(echo "$mac_with_underscores" | tr '_' ':') + echo "$mac" + return + fi + + # else look for the first bluetooth device that provides a sink + bluetoothctl devices Connected | rg '^Device ' | awk '{print $2}' | while read -r mac; + do + # Check if the device provides the "Audio Sink" service + if bluetoothctl info "$mac" | rg -q "0000110b-0000-1000-8000-00805f9b34fb"; then + echo "$mac" + # If you only want the first audio device found, you can 'break' or 'exit' here. + break + fi + done +} + +# If the script is called with "disconnect" +if [ "$1" == "disconnect" ]; then + device_mac=$(find_audio_device) + if [ -n "$device_mac" ]; then + bluetoothctl disconnect "$device_mac" + fi + echo "{}" + exit 0 +fi + +# Main logic to display the device name +device_mac=$(find_audio_device) + +if [ -n "$device_mac" ]; then + # Get the device alias (name) + device_name=$(bluetoothctl info "$device_mac" | rg "Alias:" | cut -d ' ' -f 2-) + # Output in Waybar's JSON format + tooltip=$(bt-audio-info $device_mac) + echo "{\"text\": \"$device_name 󰂰\", \"tooltip\": \"$tooltip\"}" +else + # Output empty string when no device is connected + echo "{}" +fi + + diff --git a/waybar/scripts/pixelbuds-anc-ctrl.sh b/waybar/scripts/pixelbuds-anc-ctrl.sh new file mode 100755 index 0000000..ab981b7 --- /dev/null +++ b/waybar/scripts/pixelbuds-anc-ctrl.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +# --- CONFIGURATION --- +# Your Pixel Buds Pro's MAC Address +MAC_ADDRESS="B4:23:A2:09:D3:53" +# --- END CONFIGURATION --- + +# First, check if the device is connected using bluetoothctl. +if bluetoothctl info "$MAC_ADDRESS" | grep -q "Connected: yes"; then + # --- DEVICE IS CONNECTED --- + + # This function gets the current ANC status and formats it for Waybar. + get_status() { + # Get the mode from pbpctrl. Redirect errors to null in case of a temporary glitch. + current_mode=$(pbpctrl get anc 2>/dev/null) + + # Fallback: If pbpctrl fails or returns 'unknown', hide the module. + # This handles cases where buds are connected but not fully ready. + if [[ $? -ne 0 || "$current_mode" == "unknown (0)"* || -z "$current_mode" ]]; then + echo "{}" + exit 0 + fi + + # Set icon and tooltip based on the current mode. + case "$current_mode" in + "active") + icon="ANC: Active" + class="anc-active" + ;; + "aware") + icon="ANC: Aware" + class="anc-aware" + ;; + "off") + icon="ANC: Off" + class="anc-off" + ;; + *) + # Failsafe to hide the module if the mode is unrecognized. + echo "{}" + exit 0 + ;; + esac + echo "{\"text\":\"$icon\", \"class\":\"$class\"}" + } + + # Handle click actions passed from Waybar. + case "$1" in + # Right-click: Cycle between active and aware modes. + cycle) + current_mode=$(pbpctrl get anc 2>/dev/null) + if [[ "$current_mode" == "active" ]]; then + pbpctrl set anc aware + else + pbpctrl set anc active + fi + sleep 0.1 # Brief pause for the state to update. + ;; + # Left-click: Turn ANC off. + off) + pbpctrl set anc off + sleep 0.1 # Brief pause for the state to update. + ;; + esac + + # Always display the current status after any action or on a scheduled interval. + get_status + +else + # --- DEVICE IS NOT CONNECTED --- + # Output an empty JSON object to completely hide the Waybar module. + echo "{}" +fi diff --git a/waybar/scripts/pixelbuds.sh b/waybar/scripts/pixelbuds.sh new file mode 100755 index 0000000..e30f826 --- /dev/null +++ b/waybar/scripts/pixelbuds.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +# --- CONFIGURATION --- +# Your Pixel Buds Pro's MAC Address +MAC_ADDRESS="B4:23:A2:09:D3:53" +# --- END CONFIGURATION --- + +# Check if the device is connected using bluetoothctl. +if bluetoothctl info "$MAC_ADDRESS" | grep -q "Connected: yes"; then + + # If connected, get battery info from pbpctrl. + if battery_output=$(pbpctrl show battery); then + # Use awk to grab the third field, which is the percentage or "unknown". + left_bud=$(echo "$battery_output" | grep "left bud:" | awk '{print $3}') + right_bud=$(echo "$battery_output" | grep "right bud:" | awk '{print $3}') + + # If both buds are unknown (e.g., case is closed and buds are out of range), + # or if the command output is empty, hide the module. + if ([[ "$left_bud" == "unknown" ]] && [[ "$right_bud" == "unknown" ]]) || \ + [[ -z "$left_bud" && -z "$right_bud" ]]; then + echo "{}" + exit 0 + fi + + # Prepare the display string for the left bud. + if [[ "$left_bud" == "unknown" ]]; then + left_display="L: ---" + else + left_display="L: $left_bud" + fi + + # Prepare the display string for the right bud. + if [[ "$right_bud" == "unknown" ]]; then + right_display="R: ---" + else + right_display="R: $right_bud" + fi + + # Format the final output for Waybar as JSON. + printf '{"text": "%s | %s", "tooltip": "Pixel Buds Pro 2", "class": "connected"}\n' "$left_display" "$right_display" + + else + # pbpctrl failed to run, so hide the module. + echo "{}" + fi +else + # Not connected, output an empty JSON object to hide the module. + echo "{}" +fi diff --git a/waybar/scripts/pixelbuds_pro_control.sh b/waybar/scripts/pixelbuds_pro_control.sh index 0525128..0abfe3b 100755 --- a/waybar/scripts/pixelbuds_pro_control.sh +++ b/waybar/scripts/pixelbuds_pro_control.sh @@ -5,106 +5,115 @@ MAC_ADDRESS="B4:23:A2:09:D3:53" # --- END CONFIGURATION --- -# First, check if the device is connected using bluetoothctl. -if bluetoothctl info "$MAC_ADDRESS" | grep -q "Connected: yes"; then - # --- DEVICE IS CONNECTED --- - - # This function gets the current status and formats it for Waybar. - get_status() { - # Get all info from pbpctrl in one go. Redirect errors to null. - battery_output=$(pbpctrl show battery 2>/dev/null) - - # Fallback: If pbpctrl fails or returns 'unknown', hide the module. - if [[ $? -ne 0 || -z "$battery_output" ]]; then - not_connected - fi - - # --- PARSE BATTERY INFO --- - left_bud=$(echo "$battery_output" | grep "left bud:" | awk '{print $3}') - right_bud=$(echo "$battery_output" | grep "right bud:" | awk '{print $3}') - - if ([[ "$left_bud" == "unknown" ]] && [[ "$right_bud" == "unknown" ]]) || \ - [[ -z "$left_bud" && -z "$right_bud" ]]; then - echo "{}" - exit 0 - fi - - if [[ "$left_bud" == "unknown" ]]; then - left_display="L: ---" - else - left_display="L: $left_bud" - fi - - if [[ "$right_bud" == "unknown" ]]; then - right_display="R: ---" - else - right_display="R: $right_bud" - fi - - # --- PARSE ANC INFO --- - current_mode=$(pbpctrl get anc 2>/dev/null) - - case "$current_mode" in - "active") - anc_icon="ANC" - class="anc-active" - ;; - "aware") - anc_icon="Aware" - class="anc-aware" - ;; - "off") - anc_icon="Off" - class="anc-off" - ;; - *) - anc_icon="?" - class="anc-unknown" - ;; - esac - - # --- FORMAT OUTPUT --- - printf '{"text": "%s | %s | %s", "tooltip": "Pixel Buds Pro 2", "class": "%s"}\n' \ - "$left_display" "$right_display" "$anc_icon" "$class" - } - - # Handle click actions passed from Waybar. - case "$1" in - cycle_anc) - - current_mode=$(pbpctrl get anc 2>/dev/null) - next_mode="" - case "$current_mode" in - active) - next_mode="aware" - ;; - aware) - next_mode="off" - ;; - off) - next_mode="active" - ;; - esac - - pbpctrl set anc "$next_mode" - sleep 0.1 - ;; - connect) - bluetoothctl connect "$MAC_ADDRESS" - ;; - disconnect) - bluetoothctl disconnect "$MAC_ADDRESS" - ;; - esac - - get_status - -else - # --- DEVICE IS NOT CONNECTED --- - not_connected -fi - not_connected() { - printf '{"text": "L: --- | R: --- | Off", "tooltip": "Pixel Buds Pro 2", "class": "disconnected"}\n' + printf "{\"text\": \"\", \"tooltip\": \"Pixel Buds Pro 2 not connected\", \"class\": \"disconnected\"}\n" exit 0 } + +# This function gets the current status and formats it for Waybar. +get_status() { + # Get all info from pbpctrl in one go. Redirect errors to null. + battery_output=$(pbpctrl show battery 2>/dev/null) + + # Fallback: If pbpctrl fails or returns 'unknown', hide the module. + if [[ $? -ne 0 || -z "$battery_output" ]]; then + not_connected + fi + + # --- PARSE BATTERY INFO --- + left_bud=$(echo "$battery_output" | grep "left bud:" | awk '{print $3}') + right_bud=$(echo "$battery_output" | grep "right bud:" | awk '{print $3}') + + if ([[ "$left_bud" == "unknown" ]] && [[ "$right_bud" == "unknown" ]]) || \ + [[ -z "$left_bud" && -z "$right_bud" ]]; then + echo "{}" + exit 0 + fi + + if [[ "$left_bud" == "unknown" ]]; then + left_display="L: ---" + else + left_display="L: $left_bud" + fi + + if [[ "$right_bud" == "unknown" ]]; then + right_display="R: ---" + else + right_display="R: $right_bud" + fi + + # --- PARSE ANC INFO --- + current_mode=$(pbpctrl get anc 2>/dev/null) + + case "$current_mode" in + "active") + anc_icon="ANC" + class="anc-active" + ;; + "aware") + anc_icon="Aware" + class="anc-aware" + ;; + "off") + anc_icon="Off" + class="anc-off" + ;; + *) + anc_icon="?" + class="anc-unknown" + ;; + esac + + # --- FORMAT OUTPUT --- + printf '{"text": "%s | %s | %s", "tooltip": "Pixel Buds Pro 2", "class": "%s"}\n' \ + "$left_display" "$right_display" "$anc_icon" "$class" +} + +status() { + # First, check if the device is connected using bluetoothctl. + if bluetoothctl info "$MAC_ADDRESS" | grep -q "Connected: yes"; then + # --- DEVICE IS CONNECTED --- + get_status + else + # --- DEVICE IS NOT CONNECTED --- + not_connected + fi +} + +# Handle click actions passed from Waybar. +case "$1" in + cycle_anc) + current_mode=$(pbpctrl get anc 2>/dev/null) + next_mode="active" + case "$current_mode" in + active) + next_mode="aware" + ;; + aware) + next_mode="off" + ;; + off) + next_mode="active" + ;; + esac + pbpctrl set anc "$next_mode" + sleep 0.1 + ;; + connect) + if bluetoothctl info "$MAC_ADDRESS" | grep -q "Connected: yes"; then + notify-send "Pixel Buds Pro 2 are already connected" + else + bluetoothctl connect "$MAC_ADDRESS" & disown + fi + ;; + disconnect) + if bluetoothctl info "$MAC_ADDRESS" | grep -q "Connected: yes"; then + bluetoothctl disconnect "$MAC_ADDRESS" & disown + else + notify-send "Pixel Buds Pro 2 aren't connected" + fi + ;; + *) + status + ;; +esac diff --git a/waybar/scripts/power-profile.sh b/waybar/scripts/power-profile.sh deleted file mode 100755 index 7fda66b..0000000 --- a/waybar/scripts/power-profile.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash - -# Script to manage and display system76-power profiles for Waybar - -# Define the available power profiles -PROFILES=("Performance" "Balanced" "Battery") - -# Get the current power profile -CURRENT_PROFILE=$(system76-power profile | awk '/Power Profile/ {print $3}') - -# Function to switch to the next profile -switch_next_profile() { - # Find the index of the current profile - for i in "${!PROFILES[@]}"; do - if [[ "${PROFILES[$i]}" == "$CURRENT_PROFILE" ]]; then - current_index=$i - break - fi - done - - # Calculate the index of the next profile - next_index=$(((current_index + 1) % ${#PROFILES[@]})) - NEXT_PROFILE=${PROFILES[$next_index]} - - # Switch to the next profile - system76-power profile "$NEXT_PROFILE" -} - -# If the script is called with "next", switch the profile -if [[ "$1" == "next" ]]; then - switch_next_profile - # After switching, get the new current profile - CURRENT_PROFILE=$(system76-power profile | awk '/Power Profile/ {print $3}') -fi - -# Set an icon based on the current profile -case $CURRENT_PROFILE in - "Performance") - ICON="🚀" - ;; - "Balanced") - ICON="⚖️" - ;; - "Battery") - ICON="🔋" - ;; - *) - ICON="❓" - ;; -esac - -# Output in JSON format for Waybar -printf '{"text": "%s", "tooltip": "Power Profile: %s", "class": "%s"}\n' "$ICON" "$CURRENT_PROFILE" "$(echo $CURRENT_PROFILE | tr '[:upper:]' '[:lower:]')" - diff --git a/waybar/scripts/tlp-profile.sh b/waybar/scripts/tlp-profile.sh index c3ac7f6..e6a7239 100755 --- a/waybar/scripts/tlp-profile.sh +++ b/waybar/scripts/tlp-profile.sh @@ -51,4 +51,4 @@ else # Fallback for "fully-charged", "pending-charge", etc. fi # Output JSON for Waybar -printf '{"text": "%s %s%%", "tooltip": "%s", "class": "%s"}\n' "$icon" "$percentage" "$tooltip" "$class" +printf '{"text": "%s%% %s", "tooltip": "%s", "class": "%s"}\n' "$percentage" "$icon" "$tooltip" "$class" diff --git a/waybar/style.css b/waybar/style.css index 153936c..a563482 100644 --- a/waybar/style.css +++ b/waybar/style.css @@ -10,7 +10,6 @@ } window#waybar { - /* background-color: alpha(@crust, 0.9); */ background-color: alpha(@base, 0.4); padding: 10px; transition-property: background-color; @@ -41,7 +40,7 @@ window#waybar.hidden { #workspaces button.active { color: @text; - border-bottom: 3px solid @green; + border-bottom: 3px solid @teal; } #workspaces button:hover { @@ -68,6 +67,7 @@ window#waybar.hidden { #clock, #tray, #custom-pixelbuds_pro, +#custom-bluetooth-audio, #custom-audio-output, #custom-gpu-screen-recorder { border-radius: 4px; @@ -77,20 +77,35 @@ window#waybar.hidden { color: @text; } +#wireplumber.muted, #custom-pixelbuds_pro { + background-color: @base; + color: @subtext1; + border-bottom: 3px solid @subtext1; +} + +#custom-pixelbuds_pro.disconnected { + padding-right: 16px; + padding-left: 12px; +} + #custom-pixelbuds_pro.anc-active { - color: @teal; + color: @sapphire; + border-bottom: 3px solid @sapphire; +} + +#custom-bluetooth-audio { + color: @sapphire; + border-bottom: 3px solid @sapphire; } #custom-pixelbuds_pro.anc-aware { - color: @yellow; + color: @sapphire; + border-bottom: 3px solid @sapphire; } #custom-pixelbuds_pro.anc-off { color: @text; -} - -#custom-pixelbuds_pro.disconnected { - color: @subtext1; + border-bottom: 3px solid @text; } #custom-gpu-screen-recorder, @@ -114,18 +129,22 @@ window#waybar.hidden { #custom-tlp.charging { color: @teal; + border-bottom: 3px solid @teal; } #custom-tlp.bat { color: @sapphire; + border-bottom: 3px solid @sapphire; } #custom-tlp.warning { color: @yellow; + border-bottom: 3px solid @yellow; } #custom-tlp.critical { color: @red; + border-bottom: 3px solid @red; animation-name: blink; animation-duration: 0.8s; animation-timing-function: linear; @@ -136,6 +155,7 @@ window#waybar.hidden { @keyframes blink { to { color: @yellow; + border-bottom: 3px solid @yellow; opacity: 0.6; } } @@ -155,9 +175,9 @@ window#waybar.hidden { border-bottom: 3px solid @teal; } -#memory, #cpu { - color: @teal; - border-bottom: 3px solid @teal; +#memory, #cpu, #network { + color: @mauve; + border-bottom: 3px solid @mauve; } @@ -166,23 +186,20 @@ window#waybar.hidden { } #wireplumber { - color: @mauve; padding-right: 15px; } -#custom-audio-output { - color: @mauve; +#custom-audio-output, #wireplumber { + color: @teal; + border-bottom: 3px solid @teal; } #network { - color: @mauve; padding-right: 15px; - border-bottom: 3px solid @mauve; } #clock { - font-family: JetBrainsMono Nerd Font; - color: @green; + color: @teal; } tooltip { @@ -195,23 +212,3 @@ tooltip label { padding: 5px; background-color: @base; } - -#battery.charging { - color: @green; - border-bottom: 3px solid @green; -} - -#battery.critical { - color: @red; - border-bottom: 3px solid @red; -} - -#battery.warning { - color: @yellow; - border-bottom: 3px solid @yellow; -} - -#battery.bat { - color: @text; - border-bottom: 3px solid @text; -} \ No newline at end of file