waybar
This commit is contained in:
commit
98cf630184
404
.config/waybar/config.jsonc
Normal file
404
.config/waybar/config.jsonc
Normal file
@ -0,0 +1,404 @@
|
|||||||
|
{
|
||||||
|
"layer": "top",
|
||||||
|
"position": "top",
|
||||||
|
"margin-top": 1,
|
||||||
|
"margin-left": 1,
|
||||||
|
"margin-right": 1,
|
||||||
|
"margin-bottom": 1,
|
||||||
|
"height": 54,
|
||||||
|
"spacing": 0,
|
||||||
|
"modules-left": [
|
||||||
|
"custom/launcher",
|
||||||
|
"hyprland/workspaces",
|
||||||
|
"cpu",
|
||||||
|
// "temperature",
|
||||||
|
"custom/temps",
|
||||||
|
"power-profiles-daemon",
|
||||||
|
"memory",
|
||||||
|
// "custom/updates",
|
||||||
|
"custom/fans",
|
||||||
|
"custom/weather"
|
||||||
|
// "custom/mpris-buttons"
|
||||||
|
],
|
||||||
|
"modules-center": [
|
||||||
|
"custom/calendar_clock"
|
||||||
|
],
|
||||||
|
"modules-right": [
|
||||||
|
//"mpd",
|
||||||
|
//"custom/rate",
|
||||||
|
// "image#test",
|
||||||
|
// "custom/dmp_nowplaying",
|
||||||
|
"custom/eww_nowplaying",
|
||||||
|
"custom/lyrics",
|
||||||
|
"mpris",
|
||||||
|
"custom/roon",
|
||||||
|
"tray",
|
||||||
|
"custom/wf-recorder",
|
||||||
|
//"image#gpu",
|
||||||
|
"bluetooth",
|
||||||
|
"network",
|
||||||
|
"wireplumber",
|
||||||
|
"custom/pwrate",
|
||||||
|
"battery",
|
||||||
|
"custom/powermenu"
|
||||||
|
//"hyprland/language"
|
||||||
|
//"pulseaudio/slider",
|
||||||
|
],
|
||||||
|
"custom/roon": {
|
||||||
|
"exec": "cat /tmp/waybar_roon_info.json",
|
||||||
|
"format": "{text}",
|
||||||
|
"tooltip": true,
|
||||||
|
"return-type": "json",
|
||||||
|
"signal": 3
|
||||||
|
},
|
||||||
|
"custom/temps": {
|
||||||
|
"exec": "~/.config/waybar/scripts/temps.sh",
|
||||||
|
"interval": 10,
|
||||||
|
"return-type": "json",
|
||||||
|
"tooltip": true,
|
||||||
|
"on-click": "~/.config/waybar/scripts/temps.sh click"
|
||||||
|
},
|
||||||
|
|
||||||
|
"custom/dmp_nowplaying": {
|
||||||
|
"exec": "~/.config/waybar/scripts/dmp_nowplaying.sh",
|
||||||
|
"return-type": "json",
|
||||||
|
"interval": 5,
|
||||||
|
"tooltip": true
|
||||||
|
},
|
||||||
|
"custom/eww_nowplaying": {
|
||||||
|
"exec": "echo '🎶 '",
|
||||||
|
"interval": 3600, // No es dinámico, solo muestra el ícono
|
||||||
|
"tooltip": false,
|
||||||
|
"on-click": "~/.config/eww/scripts/toggle_nowplaying.sh"
|
||||||
|
},
|
||||||
|
|
||||||
|
"custom/fans": {
|
||||||
|
"exec": "~/.config/waybar/scripts/fan_speed.sh",
|
||||||
|
"format": "{text}",
|
||||||
|
"return-type": "json",
|
||||||
|
"on-click": "~/.config/waybar/scripts/fan_speed.sh next",
|
||||||
|
"on-click-right": "~/.config/waybar/scripts/toggle_fans.sh",
|
||||||
|
"interval": 5,
|
||||||
|
"tooltip": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"custom/updates": {
|
||||||
|
"format": "{text}",
|
||||||
|
"exec": "/bin/bash /home/teraflops/.config/waybar/scripts/updates.sh",
|
||||||
|
"interval": 300,
|
||||||
|
"return-type": "json",
|
||||||
|
"tooltip": true,
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
"hyprland/language": {
|
||||||
|
"format-es": "es",
|
||||||
|
"format-en": "us",
|
||||||
|
"keyboard-name": "asus-keyboard",
|
||||||
|
"keyboard-name": "melgeek-mojo68-c1-keyboard",
|
||||||
|
"keyboard-name": "mwstudio-mmkzoo65-keyboard",
|
||||||
|
"on-click": "hyprctl switchxkblayout mwstudio-mmkzoo65-keyboard next"
|
||||||
|
},
|
||||||
|
"custom/mpris-buttons": {
|
||||||
|
"exec": "~/.config/waybar/scripts/mpris_buttons.sh",
|
||||||
|
// "interval": 1,
|
||||||
|
"return-type": "json",
|
||||||
|
"on-click": "playerctl play-pause",
|
||||||
|
"on-click-right": "playerctl next",
|
||||||
|
"on-click-middle": "playerctl previous",
|
||||||
|
"signal": 24
|
||||||
|
},
|
||||||
|
|
||||||
|
"pulseaudio/slider": {
|
||||||
|
"min": 0,
|
||||||
|
"max": 100,
|
||||||
|
"orientation": "vertical"
|
||||||
|
},
|
||||||
|
"custom/network_speed": {
|
||||||
|
"exec": "/home/teraflops/.local/bin/network_speed_client.py",
|
||||||
|
"interval": 1,
|
||||||
|
"return-type": "json",
|
||||||
|
"format": "{}",
|
||||||
|
"tooltip": true,
|
||||||
|
},
|
||||||
|
"image#gpu": {
|
||||||
|
"exec": "/tmp/gpu.sh",
|
||||||
|
"size": 18,
|
||||||
|
// "interval": 1,
|
||||||
|
// "signal": 3,
|
||||||
|
"tooltip": true,
|
||||||
|
"on-click": "python /home/teraflops/.local/bin/comandos.py",
|
||||||
|
},
|
||||||
|
"custom/lyrics": {
|
||||||
|
"format": "{text}",
|
||||||
|
"exec": "/home/teraflops/.config/waybar/scripts/lyrics_module.sh",
|
||||||
|
"return-type": "json",
|
||||||
|
"escape": true,
|
||||||
|
"interval": 10,
|
||||||
|
"on-empty": "hide",
|
||||||
|
//"on-click": "termite -t playlist -e ncmpcpp",
|
||||||
|
"on-click": "/home/teraflops/.config/waybar/scripts/show_cover.py",
|
||||||
|
//"on-scroll-down": "/home/teraflops/.config/waybar/scripts/lyrics_scroll.sh",
|
||||||
|
"signal": 9
|
||||||
|
},
|
||||||
|
"mpris": {
|
||||||
|
"format": "{title}",
|
||||||
|
"format-paused": "⏸ {title}",
|
||||||
|
"tooltip-format": "{title}\n{artist} ",
|
||||||
|
//"title-len": 20,
|
||||||
|
"artist-len": 0,
|
||||||
|
"album-len": 0,
|
||||||
|
"dynamic-len": 20,
|
||||||
|
"ellipsis": "…",
|
||||||
|
"expand": false,
|
||||||
|
"max-length": 60,
|
||||||
|
// "min-length": 25,
|
||||||
|
"tooltip": true,
|
||||||
|
//"ignored-players": ["firefox"],
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
"hyprland/workspaces": {
|
||||||
|
"on-click": "activate",
|
||||||
|
"format": "{icon}",
|
||||||
|
"format-icons": {
|
||||||
|
"default": "",
|
||||||
|
"1": "",
|
||||||
|
"2": "",
|
||||||
|
"3": "",
|
||||||
|
"4": "",
|
||||||
|
"active": "",
|
||||||
|
"urgent": "",
|
||||||
|
},
|
||||||
|
"persistent-workspaces": {
|
||||||
|
"*": 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"custom/inetspeedup": {
|
||||||
|
"format": "{}",
|
||||||
|
"exec": "/home/teraflops/.config/waybar/scripts/inetup.py",
|
||||||
|
"on-click": "/home/teraflops/.config/waybar/scripts/inetup.py toggle",
|
||||||
|
"interval": 10,
|
||||||
|
"return-type": "json",
|
||||||
|
"signal": 20,
|
||||||
|
},
|
||||||
|
"custom/inetspeeddown": {
|
||||||
|
"format": "{}",
|
||||||
|
"exec": "/home/teraflops/.config/waybar/scripts/inetdown.py",
|
||||||
|
"on-click": "/home/teraflops/.config/waybar/scripts/inetdown.py toggle",
|
||||||
|
"interval": 10,
|
||||||
|
"return-type": "json",
|
||||||
|
"signal": 21,
|
||||||
|
},
|
||||||
|
"custom/borg": {
|
||||||
|
//pkill -RTMIN+12 waybar
|
||||||
|
"format": "BACKUP RUNNING",
|
||||||
|
"exec": "echo '{\"class\": \"running\"}'",
|
||||||
|
"exec-if": "pgrep -x pika-backup",
|
||||||
|
"return-type": "json",
|
||||||
|
"signal": 12,
|
||||||
|
},
|
||||||
|
"custom/wf-recorder": {
|
||||||
|
//pkill -RTMIN+11 waybar
|
||||||
|
"format": "• ",
|
||||||
|
"exec": "echo '{\"class\": \"recording\"}'",
|
||||||
|
"exec-if": "pgrep -x wf-recorder",
|
||||||
|
"return-type": "json",
|
||||||
|
"signal": 11,
|
||||||
|
},
|
||||||
|
"custom/tailscale": {
|
||||||
|
"format": "VPN",
|
||||||
|
"exec": "echo '{\"class\": \"vpn\"}'",
|
||||||
|
"exec-if": "pgrep -x tailscaled",
|
||||||
|
"return-type": "json",
|
||||||
|
"signal": 22,
|
||||||
|
},
|
||||||
|
"custom/pwrate": {
|
||||||
|
"exec": "/home/teraflops/.local/bin/pwrate3.sh",
|
||||||
|
"return-type": "json",
|
||||||
|
"format": "{}",
|
||||||
|
"signal": 23
|
||||||
|
},
|
||||||
|
"custom/rate": {
|
||||||
|
//pkill -RTMIN+15 waybar
|
||||||
|
//"exec": "/home/teraflops/.local/bin/show_rate.sh",
|
||||||
|
"exec": "python /home/teraflops/.local/bin/now_playing_tooltip.py",
|
||||||
|
//"exec": "echo '{\"text\":\"🎵\", \"tooltip\":\"Testing Tooltip\"}'", // Simple test module
|
||||||
|
"tooltip": true,
|
||||||
|
"return-type": "json",
|
||||||
|
"escape": false,
|
||||||
|
"on-click": "python /home/teraflops/.local/bin/now_playing_tooltip.py toggle",
|
||||||
|
"on-click-right": "pkill -Af now_playing_tooltip.py",
|
||||||
|
"signal": 15,
|
||||||
|
"class": "rate-module",
|
||||||
|
},
|
||||||
|
"backlight": {
|
||||||
|
"device": "amdgpu_bl1",
|
||||||
|
"format": "{icon}",
|
||||||
|
"format-icons": [
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"custom/weather": {
|
||||||
|
"exec": "python ~/.config/waybar/scripts/weather.py",
|
||||||
|
"restart-interval": 300,
|
||||||
|
"return-type": "json",
|
||||||
|
// "on-click": "xdg-open https://weather.com/en-IN/weather/today/l/$(location_id)"
|
||||||
|
// "format-alt": "{alt}",
|
||||||
|
},
|
||||||
|
"bluetooth": {
|
||||||
|
"format": "",
|
||||||
|
"format-connected": "",
|
||||||
|
"format-connected-battery": " ",
|
||||||
|
"tooltip-format": "{controller_alias}\t{controller_address}\n\n{num_connections} connected",
|
||||||
|
"tooltip-format-connected": "{controller_alias}\t{controller_address}\n\n{num_connections} connected\n\n{device_enumerate}",
|
||||||
|
"tooltip-format-enumerate-connected": "{device_alias}\t{device_address}",
|
||||||
|
"tooltip-format-enumerate-connected-battery": "{device_alias}\t{device_address}\t{device_battery_percentage}%",
|
||||||
|
"on-click": "/home/teraflops/.config/waybar/scripts/bt_menu.py",
|
||||||
|
},
|
||||||
|
"hyprland/window": {
|
||||||
|
"max-length": 60,
|
||||||
|
"format": "<span font='11' rise='-4444'>{}</span>",
|
||||||
|
"separate-outputs": true,
|
||||||
|
},
|
||||||
|
"temperature": {
|
||||||
|
"hwmon-path": [
|
||||||
|
"/sys/class/thermal/thermal_zone0/temp"
|
||||||
|
],
|
||||||
|
"critical-threshold": 80,
|
||||||
|
"format-critical": "{temperatureC}°C",
|
||||||
|
"format": "{temperatureC}°C",
|
||||||
|
},
|
||||||
|
"memory": {
|
||||||
|
"interval": 3,
|
||||||
|
"format": " {}% ",
|
||||||
|
"max-length": 10,
|
||||||
|
},
|
||||||
|
"cpu": {
|
||||||
|
"interval": 1,
|
||||||
|
"format": "{icon} {usage:>2}%",
|
||||||
|
"format-icons": [
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" "
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"power-profiles-daemon": {
|
||||||
|
"format": "{icon}",
|
||||||
|
"tooltip-format": "Power profile: {profile}\nDriver: {driver}",
|
||||||
|
"tooltip": true,
|
||||||
|
"format-icons": {
|
||||||
|
"default": "BAL",
|
||||||
|
"performance": "PERF",
|
||||||
|
"balanced": "BAL",
|
||||||
|
"power-saver": "PSAV",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"custom/powermenu": {
|
||||||
|
"format": "⏻ ",
|
||||||
|
"tooltip": false,
|
||||||
|
"on-click": "/home/teraflops/.config/waybar/scripts/wlogout.sh",
|
||||||
|
},
|
||||||
|
"tray": {
|
||||||
|
"icon-size": 18,
|
||||||
|
"spacing": 10,
|
||||||
|
},
|
||||||
|
"custom/calendar_clock": {
|
||||||
|
"exec": "python3 /home/teraflops/.local/bin/list_events_v5_json.py",
|
||||||
|
"interval": 120,
|
||||||
|
"tooltip": true,
|
||||||
|
"return-type": "json",
|
||||||
|
},
|
||||||
|
"network": {
|
||||||
|
"format-wifi": "{icon}",
|
||||||
|
"format-disconnected": " ",
|
||||||
|
"format-icons": [
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" "
|
||||||
|
],
|
||||||
|
"tooltip-format-wifi": "{essid}",
|
||||||
|
"tooltip-format-disconnected": "Disconnected",
|
||||||
|
"nospacing": 1,
|
||||||
|
"on-click": "/home/teraflops/.config/waybar/scripts/wifi_menu.py",
|
||||||
|
},
|
||||||
|
//"battery": {
|
||||||
|
// "bat": "BAT1", // Usa el identificador correcto de tu batería
|
||||||
|
//"interval": 60,
|
||||||
|
//"format": "{icon}",
|
||||||
|
//"tooltip": false,
|
||||||
|
//"format-icons": ["", "", "", "", ""],
|
||||||
|
//"menu": "on-click",
|
||||||
|
//"menu-file": "/home/teraflops/.config/waybar/menu/battery_menu.xml",
|
||||||
|
//"menu-actions": {
|
||||||
|
// "view_details": "notify send 1",
|
||||||
|
//"check_health": "notify send 2",
|
||||||
|
//"activate_saving_mode": "notify send 3",
|
||||||
|
//},
|
||||||
|
"battery": {
|
||||||
|
"interval": 60,
|
||||||
|
"states": {
|
||||||
|
"warning": 30,
|
||||||
|
"critical": 15,
|
||||||
|
},
|
||||||
|
"format": "{icon}",
|
||||||
|
"format-charging": " ",
|
||||||
|
"format-plugged": "{icon}",
|
||||||
|
"format-icons": [
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" "
|
||||||
|
],
|
||||||
|
"max-length": 18,
|
||||||
|
"menu": "on-click",
|
||||||
|
"menu-file": "/home/teraflops/.config/waybar/menu/battery_menu.xml",
|
||||||
|
"menu-actions": {
|
||||||
|
"view_details": "/home/teraflops/.config/waybar/scripts/view_battery_details_yad.sh",
|
||||||
|
"check_health": "/home/teraflops/.config/waybar/scripts/check_battery_health.sh",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"wireplumber": {
|
||||||
|
"format": "{icon}",
|
||||||
|
"nospacing": 1,
|
||||||
|
"tooltip-format": "Volume : {volume}%",
|
||||||
|
"format-muted": " ",
|
||||||
|
"format-icons": {
|
||||||
|
"headphone": " ",
|
||||||
|
"default": [
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" "
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"on-click": "/home/teraflops/.config/waybar/scripts/volume_menu.py",
|
||||||
|
"on-click-right": "pavucontrol",
|
||||||
|
"on-update": "/usr/bin/pkill -RTMIN+23 waybar",
|
||||||
|
// "on-update": "/home/teraflops/.local/bin/show_rate.sh",
|
||||||
|
"scroll-step": 3,
|
||||||
|
},
|
||||||
|
"custom/launcher": {
|
||||||
|
"format": " ",
|
||||||
|
"tooltip": false,
|
||||||
|
"on-click": "/home/teraflops/.config/rofi/launchers/misc/launcher.sh &",
|
||||||
|
},
|
||||||
|
}
|
386
.config/waybar/config.jsonc_bicolor
Normal file
386
.config/waybar/config.jsonc_bicolor
Normal file
@ -0,0 +1,386 @@
|
|||||||
|
{
|
||||||
|
"layer": "top",
|
||||||
|
"position": "top",
|
||||||
|
"margin-top": 1,
|
||||||
|
"margin-left": 1,
|
||||||
|
"margin-right": 1,
|
||||||
|
"margin-bottom": 1,
|
||||||
|
"height": 54,
|
||||||
|
"spacing": 0,
|
||||||
|
"modules-left": [
|
||||||
|
"custom/launcher",
|
||||||
|
"hyprland/workspaces",
|
||||||
|
"cpu",
|
||||||
|
"temperature",
|
||||||
|
"power-profiles-daemon",
|
||||||
|
"memory",
|
||||||
|
"custom/fans",
|
||||||
|
"custom/weather"
|
||||||
|
// "custom/updates"
|
||||||
|
// "custom/mpris-buttons"
|
||||||
|
],
|
||||||
|
"modules-center": [
|
||||||
|
"custom/calendar_clock"
|
||||||
|
],
|
||||||
|
"modules-right": [
|
||||||
|
//"mpd",
|
||||||
|
//"custom/rate",
|
||||||
|
// "image#test",
|
||||||
|
"custom/lyrics",
|
||||||
|
"mpris",
|
||||||
|
"custom/roon",
|
||||||
|
"tray",
|
||||||
|
"custom/wf-recorder",
|
||||||
|
//"image#gpu",
|
||||||
|
"bluetooth",
|
||||||
|
"network",
|
||||||
|
"wireplumber",
|
||||||
|
"custom/pwrate",
|
||||||
|
"battery",
|
||||||
|
"custom/powermenu"
|
||||||
|
//"hyprland/language"
|
||||||
|
//"pulseaudio/slider",
|
||||||
|
],
|
||||||
|
"custom/roon": {
|
||||||
|
"exec": "cat /tmp/waybar_roon_info.json",
|
||||||
|
"format": "{text}",
|
||||||
|
"tooltip": true,
|
||||||
|
"return-type": "json",
|
||||||
|
"signal": 3
|
||||||
|
},
|
||||||
|
"custom/fans": {
|
||||||
|
"exec": "~/.config/waybar/scripts/fan_speed.sh",
|
||||||
|
"format": "{text}",
|
||||||
|
"markup": "pango",
|
||||||
|
"return-type": "json",
|
||||||
|
"on-click": "~/.config/waybar/scripts/fan_speed.sh next",
|
||||||
|
"on-click-right": "~/.config/waybar/scripts/toggle_fans.sh",
|
||||||
|
"interval": 5,
|
||||||
|
"tooltip": true
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
"custom/updates": {
|
||||||
|
"format": "📦 {}",
|
||||||
|
"exec": "~/.config/waybar/scripts/updates.sh",
|
||||||
|
"interval": 18,
|
||||||
|
"return-type": "json",
|
||||||
|
"tooltip": true,
|
||||||
|
"tooltip-format": "{full_text}"
|
||||||
|
},
|
||||||
|
|
||||||
|
"hyprland/language": {
|
||||||
|
"format-es": "es",
|
||||||
|
"format-en": "us",
|
||||||
|
"keyboard-name": "asus-keyboard",
|
||||||
|
"keyboard-name": "melgeek-mojo68-c1-keyboard",
|
||||||
|
"keyboard-name": "mwstudio-mmkzoo65-keyboard",
|
||||||
|
"on-click": "hyprctl switchxkblayout mwstudio-mmkzoo65-keyboard next"
|
||||||
|
},
|
||||||
|
"custom/mpris-buttons": {
|
||||||
|
"exec": "~/.config/waybar/scripts/mpris_buttons.sh",
|
||||||
|
// "interval": 1,
|
||||||
|
"return-type": "json",
|
||||||
|
"on-click": "playerctl play-pause",
|
||||||
|
"on-click-right": "playerctl next",
|
||||||
|
"on-click-middle": "playerctl previous",
|
||||||
|
"signal": 24
|
||||||
|
},
|
||||||
|
|
||||||
|
"pulseaudio/slider": {
|
||||||
|
"min": 0,
|
||||||
|
"max": 100,
|
||||||
|
"orientation": "vertical"
|
||||||
|
},
|
||||||
|
"custom/network_speed": {
|
||||||
|
"exec": "/home/teraflops/.local/bin/network_speed_client.py",
|
||||||
|
"interval": 1,
|
||||||
|
"return-type": "json",
|
||||||
|
"format": "{}",
|
||||||
|
"tooltip": true,
|
||||||
|
},
|
||||||
|
"image#gpu": {
|
||||||
|
"exec": "/tmp/gpu.sh",
|
||||||
|
"size": 18,
|
||||||
|
// "interval": 1,
|
||||||
|
// "signal": 3,
|
||||||
|
"tooltip": true,
|
||||||
|
"on-click": "python /home/teraflops/.local/bin/comandos.py",
|
||||||
|
},
|
||||||
|
"custom/lyrics": {
|
||||||
|
"format": "{text}",
|
||||||
|
"exec": "/home/teraflops/.config/waybar/scripts/lyrics_module.sh",
|
||||||
|
"return-type": "json",
|
||||||
|
"escape": true,
|
||||||
|
"interval": 10,
|
||||||
|
"on-empty": "hide",
|
||||||
|
//"on-click": "termite -t playlist -e ncmpcpp",
|
||||||
|
"on-click": "/home/teraflops/.config/waybar/scripts/show_cover.py",
|
||||||
|
//"on-scroll-down": "/home/teraflops/.config/waybar/scripts/lyrics_scroll.sh",
|
||||||
|
"signal": 9
|
||||||
|
},
|
||||||
|
"mpris": {
|
||||||
|
"format": "{title}",
|
||||||
|
"format-paused": "⏸ {title}",
|
||||||
|
"tooltip-format": "{title}\n{artist} ",
|
||||||
|
//"title-len": 20,
|
||||||
|
"artist-len": 0,
|
||||||
|
"album-len": 0,
|
||||||
|
"dynamic-len": 20,
|
||||||
|
"ellipsis": "…",
|
||||||
|
"expand": false,
|
||||||
|
"max-length": 60,
|
||||||
|
// "min-length": 25,
|
||||||
|
"tooltip": true,
|
||||||
|
//"ignored-players": ["firefox"],
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
"hyprland/workspaces": {
|
||||||
|
"on-click": "activate",
|
||||||
|
"format": "{icon}",
|
||||||
|
"format-icons": {
|
||||||
|
"default": "",
|
||||||
|
"1": "",
|
||||||
|
"2": "",
|
||||||
|
"3": "",
|
||||||
|
"4": "",
|
||||||
|
"active": "",
|
||||||
|
"urgent": "",
|
||||||
|
},
|
||||||
|
"persistent-workspaces": {
|
||||||
|
"*": 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"custom/inetspeedup": {
|
||||||
|
"format": "{}",
|
||||||
|
"exec": "/home/teraflops/.config/waybar/scripts/inetup.py",
|
||||||
|
"on-click": "/home/teraflops/.config/waybar/scripts/inetup.py toggle",
|
||||||
|
"interval": 10,
|
||||||
|
"return-type": "json",
|
||||||
|
"signal": 20,
|
||||||
|
},
|
||||||
|
"custom/inetspeeddown": {
|
||||||
|
"format": "{}",
|
||||||
|
"exec": "/home/teraflops/.config/waybar/scripts/inetdown.py",
|
||||||
|
"on-click": "/home/teraflops/.config/waybar/scripts/inetdown.py toggle",
|
||||||
|
"interval": 10,
|
||||||
|
"return-type": "json",
|
||||||
|
"signal": 21,
|
||||||
|
},
|
||||||
|
"custom/borg": {
|
||||||
|
//pkill -RTMIN+12 waybar
|
||||||
|
"format": "BACKUP RUNNING",
|
||||||
|
"exec": "echo '{\"class\": \"running\"}'",
|
||||||
|
"exec-if": "pgrep -x pika-backup",
|
||||||
|
"return-type": "json",
|
||||||
|
"signal": 12,
|
||||||
|
},
|
||||||
|
"custom/wf-recorder": {
|
||||||
|
//pkill -RTMIN+11 waybar
|
||||||
|
"format": "• ",
|
||||||
|
"exec": "echo '{\"class\": \"recording\"}'",
|
||||||
|
"exec-if": "pgrep -x wf-recorder",
|
||||||
|
"return-type": "json",
|
||||||
|
"signal": 11,
|
||||||
|
},
|
||||||
|
"custom/tailscale": {
|
||||||
|
"format": "VPN",
|
||||||
|
"exec": "echo '{\"class\": \"vpn\"}'",
|
||||||
|
"exec-if": "pgrep -x tailscaled",
|
||||||
|
"return-type": "json",
|
||||||
|
"signal": 22,
|
||||||
|
},
|
||||||
|
"custom/pwrate": {
|
||||||
|
"exec": "/home/teraflops/.local/bin/pwrate3.sh",
|
||||||
|
"return-type": "json",
|
||||||
|
"format": "{}",
|
||||||
|
"signal": 23
|
||||||
|
},
|
||||||
|
"custom/rate": {
|
||||||
|
//pkill -RTMIN+15 waybar
|
||||||
|
//"exec": "/home/teraflops/.local/bin/show_rate.sh",
|
||||||
|
"exec": "python /home/teraflops/.local/bin/now_playing_tooltip.py",
|
||||||
|
//"exec": "echo '{\"text\":\"🎵\", \"tooltip\":\"Testing Tooltip\"}'", // Simple test module
|
||||||
|
"tooltip": true,
|
||||||
|
"return-type": "json",
|
||||||
|
"escape": false,
|
||||||
|
"on-click": "python /home/teraflops/.local/bin/now_playing_tooltip.py toggle",
|
||||||
|
"on-click-right": "pkill -Af now_playing_tooltip.py",
|
||||||
|
"signal": 15,
|
||||||
|
"class": "rate-module",
|
||||||
|
},
|
||||||
|
"backlight": {
|
||||||
|
"device": "amdgpu_bl1",
|
||||||
|
"format": "{icon}",
|
||||||
|
"format-icons": [
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"custom/weather": {
|
||||||
|
"exec": "python ~/.config/waybar/scripts/weather.py",
|
||||||
|
"restart-interval": 300,
|
||||||
|
"return-type": "json",
|
||||||
|
// "on-click": "xdg-open https://weather.com/en-IN/weather/today/l/$(location_id)"
|
||||||
|
// "format-alt": "{alt}",
|
||||||
|
},
|
||||||
|
"bluetooth": {
|
||||||
|
"format": "",
|
||||||
|
"format-connected": "",
|
||||||
|
"format-connected-battery": " ",
|
||||||
|
"tooltip-format": "{controller_alias}\t{controller_address}\n\n{num_connections} connected",
|
||||||
|
"tooltip-format-connected": "{controller_alias}\t{controller_address}\n\n{num_connections} connected\n\n{device_enumerate}",
|
||||||
|
"tooltip-format-enumerate-connected": "{device_alias}\t{device_address}",
|
||||||
|
"tooltip-format-enumerate-connected-battery": "{device_alias}\t{device_address}\t{device_battery_percentage}%",
|
||||||
|
"on-click": "/home/teraflops/.config/waybar/scripts/bt_menu.py",
|
||||||
|
},
|
||||||
|
"hyprland/window": {
|
||||||
|
"max-length": 60,
|
||||||
|
"format": "<span font='11' rise='-4444'>{}</span>",
|
||||||
|
"separate-outputs": true,
|
||||||
|
},
|
||||||
|
"temperature": {
|
||||||
|
"hwmon-path": [
|
||||||
|
"/sys/class/thermal/thermal_zone0/temp"
|
||||||
|
],
|
||||||
|
"critical-threshold": 80,
|
||||||
|
"format": "<span foreground='#fa9f8e'>cpu:</span> <span foreground='#81a1c1'>{temperatureC}°C</span>",
|
||||||
|
"format-critical": "<span foreground='#fa9f8e'>cpu:</span> <span foreground='#81a1c1'>{temperatureC}°C</span>"
|
||||||
|
},
|
||||||
|
"memory": {
|
||||||
|
"interval": 3,
|
||||||
|
"format": "<span foreground='#fa9f8e'> </span> <span foreground='#81a1c1'>{}%</span>",
|
||||||
|
"markup": "pango",
|
||||||
|
"max-length": 10
|
||||||
|
},
|
||||||
|
|
||||||
|
"cpu": {
|
||||||
|
"interval": 1,
|
||||||
|
"format": "<span foreground='#fa9f8e'>cpu:</span> <span foreground='#81a1c1'>{usage}%</span>",
|
||||||
|
"format-icons": [
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" "
|
||||||
|
],
|
||||||
|
"markup": "pango"
|
||||||
|
},
|
||||||
|
|
||||||
|
"power-profiles-daemon": {
|
||||||
|
"format": "{icon}",
|
||||||
|
"tooltip-format": "Power profile: {profile}\nDriver: {driver}",
|
||||||
|
"tooltip": true,
|
||||||
|
"format-icons": {
|
||||||
|
"default": "BAL",
|
||||||
|
"performance": "PERF",
|
||||||
|
"balanced": "BAL",
|
||||||
|
"power-saver": "PSAV",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"custom/powermenu": {
|
||||||
|
"format": "⏻ ",
|
||||||
|
"tooltip": false,
|
||||||
|
"on-click": "/home/teraflops/.config/waybar/scripts/wlogout.sh",
|
||||||
|
},
|
||||||
|
"tray": {
|
||||||
|
"icon-size": 18,
|
||||||
|
"spacing": 10,
|
||||||
|
},
|
||||||
|
"custom/calendar_clock": {
|
||||||
|
"exec": "python3 /home/teraflops/.local/bin/list_events_v5_json.py",
|
||||||
|
"interval": 120,
|
||||||
|
"tooltip": true,
|
||||||
|
"return-type": "json",
|
||||||
|
},
|
||||||
|
"network": {
|
||||||
|
"format-wifi": "{icon}",
|
||||||
|
"format-disconnected": " ",
|
||||||
|
"format-icons": [
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" "
|
||||||
|
],
|
||||||
|
"tooltip-format-wifi": "{essid}",
|
||||||
|
"tooltip-format-disconnected": "Disconnected",
|
||||||
|
"nospacing": 1,
|
||||||
|
"on-click": "/home/teraflops/.config/waybar/scripts/wifi_menu.py",
|
||||||
|
},
|
||||||
|
//"battery": {
|
||||||
|
// "bat": "BAT1", // Usa el identificador correcto de tu batería
|
||||||
|
//"interval": 60,
|
||||||
|
//"format": "{icon}",
|
||||||
|
//"tooltip": false,
|
||||||
|
//"format-icons": ["", "", "", "", ""],
|
||||||
|
//"menu": "on-click",
|
||||||
|
//"menu-file": "/home/teraflops/.config/waybar/menu/battery_menu.xml",
|
||||||
|
//"menu-actions": {
|
||||||
|
// "view_details": "notify send 1",
|
||||||
|
//"check_health": "notify send 2",
|
||||||
|
//"activate_saving_mode": "notify send 3",
|
||||||
|
//},
|
||||||
|
"battery": {
|
||||||
|
"interval": 60,
|
||||||
|
"states": {
|
||||||
|
"warning": 30,
|
||||||
|
"critical": 15,
|
||||||
|
},
|
||||||
|
"format": "{icon}",
|
||||||
|
"format-charging": " ",
|
||||||
|
"format-plugged": "{icon}",
|
||||||
|
"format-icons": [
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" "
|
||||||
|
],
|
||||||
|
"max-length": 18,
|
||||||
|
"menu": "on-click",
|
||||||
|
"menu-file": "/home/teraflops/.config/waybar/menu/battery_menu.xml",
|
||||||
|
"menu-actions": {
|
||||||
|
"view_details": "/home/teraflops/.config/waybar/scripts/view_battery_details_yad.sh",
|
||||||
|
"check_health": "/home/teraflops/.config/waybar/scripts/check_battery_health_yad.sh",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"wireplumber": {
|
||||||
|
"format": "{icon}",
|
||||||
|
"nospacing": 1,
|
||||||
|
"tooltip-format": "Volume : {volume}%",
|
||||||
|
"format-muted": " ",
|
||||||
|
"format-icons": {
|
||||||
|
"headphone": " ",
|
||||||
|
"default": [
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" "
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"on-click": "/home/teraflops/.config/waybar/scripts/volume_menu.py",
|
||||||
|
"on-click-right": "pavucontrol",
|
||||||
|
"on-update": "/usr/bin/pkill -RTMIN+23 waybar && /usr/bin/pkill -RTMIN+24 waybar ",
|
||||||
|
// "on-update": "/home/teraflops/.local/bin/show_rate.sh",
|
||||||
|
"scroll-step": 3,
|
||||||
|
},
|
||||||
|
"custom/launcher": {
|
||||||
|
"format": " ",
|
||||||
|
"tooltip": false,
|
||||||
|
"on-click": "/home/teraflops/.config/rofi/launchers/misc/launcher.sh &",
|
||||||
|
},
|
||||||
|
}
|
16
.config/waybar/menu/battery_menu.xml
Normal file
16
.config/waybar/menu/battery_menu.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<interface>
|
||||||
|
<object class="GtkMenu" id="menu">
|
||||||
|
<child>
|
||||||
|
<object class="GtkMenuItem" id="view_details">
|
||||||
|
<property name="label">Ver Detalles de la Batería</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkMenuItem" id="check_health">
|
||||||
|
<property name="label">Verificar Salud de la Batería</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</interface>
|
||||||
|
|
52
.config/waybar/menu/unique.xml
Normal file
52
.config/waybar/menu/unique.xml
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<interface>
|
||||||
|
<object class="GtkMenu" id="menu">
|
||||||
|
|
||||||
|
<!-- Sección para Batería -->
|
||||||
|
<child>
|
||||||
|
<object class="GtkMenuItem" id="battery_view_details">
|
||||||
|
<property name="label">Ver Detalles de la Batería</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkMenuItem" id="battery_check_health">
|
||||||
|
<property name="label">Verificar Salud de la Batería</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkMenuItem" id="battery_activate_saving_mode">
|
||||||
|
<property name="label">Activar Modo de Ahorro de Batería</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkSeparatorMenuItem" id="battery_delimiter"/>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<!-- Sección para Wireplumber -->
|
||||||
|
<child>
|
||||||
|
<object class="GtkMenuItem" id="wireplumber_mute_unmute">
|
||||||
|
<property name="label">Mute/Unmute</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkMenuItem" id="wireplumber_create_sink">
|
||||||
|
<property name="label">Crear Sink</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkMenuItem" id="wireplumber_set_default_sink">
|
||||||
|
<property name="label">Establecer Sink Predeterminado</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkMenuItem" id="wireplumber_open_pavucontrol">
|
||||||
|
<property name="label">Abrir Pavucontrol</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkSeparatorMenuItem" id="wireplumber_delimiter"/>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
</object>
|
||||||
|
</interface>
|
||||||
|
|
26
.config/waybar/menu/wireplumber_menu.xml
Normal file
26
.config/waybar/menu/wireplumber_menu.xml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<interface>
|
||||||
|
<object class="GtkMenu" id="menu">
|
||||||
|
<child>
|
||||||
|
<object class="GtkMenuItem" id="mute/unmute">
|
||||||
|
<property name="label">Ver Detalles de la Batería</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkMenuItem" id="create sink">
|
||||||
|
<property name="label">Verificar Salud de la Batería</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkMenuItem" id="set default sink">
|
||||||
|
<property name="label">Crear Sink</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkMenuItem" id="open pavucontrol">
|
||||||
|
<property name="label">Abrir Pavucontrol</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</interface>
|
||||||
|
|
Binary file not shown.
Binary file not shown.
23
.config/waybar/scripts/__pycache__/tiling.d.er
Normal file
23
.config/waybar/scripts/__pycache__/tiling.d.er
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
##[pylyzer] failed /home/teraflops/.config/waybar/scripts/tiling.py 1731075378 1588
|
||||||
|
.___v_desugar_1 = pyimport "<failure>"
|
||||||
|
.<failure> = pyimport "<failure>"
|
||||||
|
.Gtk: Never
|
||||||
|
.subprocess = pyimport "<failure>"
|
||||||
|
|
||||||
|
|
||||||
|
.gi = pyimport "__init__"
|
||||||
|
.__init__ = pyimport "__init__"
|
||||||
|
|
||||||
|
|
||||||
|
.layouts: Dict!({{"vertical"}: {"hyprctl dispatch layout vert"}, {"horizontal"}: {"hyprctl dispatch layout horiz"}, {"floating"}: {"hyprctl dispatch layout float"}, {"tabbed"}: {"hyprctl dispatch layout tabbed"}})
|
||||||
|
.LayoutSelector: ClassType
|
||||||
|
.LayoutSelector <: Never
|
||||||
|
.LayoutSelector.__call__: () -> tiling.LayoutSelector
|
||||||
|
.LayoutSelector.create_button: (self: tiling.LayoutSelector, label: Obj, image_path: Obj, command: Obj, grid: Obj, row: Obj, col: Obj) -> Never
|
||||||
|
.LayoutSelector.on_button_clicked: (self: tiling.LayoutSelector, widget: Obj, command: Obj) -> Never
|
||||||
|
|
||||||
|
tiling = pyimport "tiling"
|
||||||
|
.win: .LayoutSelector
|
||||||
|
|
||||||
|
|
||||||
|
|
5
.config/waybar/scripts/activate_saving_mode.sh
Executable file
5
.config/waybar/scripts/activate_saving_mode.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Reducir el brillo al 50% (asegúrate de tener instalada xbacklight)
|
||||||
|
xbacklight -set 50
|
||||||
|
zenity --info --text="Modo de Ahorro Activado:\nBrillo reducido al 50%." --title="Modo de Ahorro Activado"
|
||||||
|
|
37
.config/waybar/scripts/apply_grid_layout.sh
Executable file
37
.config/waybar/scripts/apply_grid_layout.sh
Executable file
@ -0,0 +1,37 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Obtiene las ventanas abiertas en el espacio de trabajo actual
|
||||||
|
windows=$(hyprctl clients -j | jq -r '.[] | .address')
|
||||||
|
|
||||||
|
# Cuenta las ventanas abiertas
|
||||||
|
window_count=$(echo "$windows" | wc -l)
|
||||||
|
|
||||||
|
# Define las posiciones y tamaños de cada ventana según la cuadrícula
|
||||||
|
case $window_count in
|
||||||
|
1)
|
||||||
|
hyprctl dispatch movewindow 50% 50% resizewindowpixel 100% 100% ;;
|
||||||
|
2)
|
||||||
|
hyprctl dispatch focuswindow $(echo "$windows" | sed -n 1p)
|
||||||
|
hyprctl dispatch movewindow 0 0 resizewindowpixel 50% 100%
|
||||||
|
hyprctl dispatch focuswindow $(echo "$windows" | sed -n 2p)
|
||||||
|
hyprctl dispatch movewindow 50% 0 resizewindowpixel 50% 100% ;;
|
||||||
|
3)
|
||||||
|
# Configura tres ventanas, dos arriba y una en la mitad inferior
|
||||||
|
hyprctl dispatch focuswindow $(echo "$windows" | sed -n 1p)
|
||||||
|
hyprctl dispatch movewindow 0 0 resizewindowpixel 50% 50%
|
||||||
|
hyprctl dispatch focuswindow $(echo "$windows" | sed -n 2p)
|
||||||
|
hyprctl dispatch movewindow 50% 0 resizewindowpixel 50% 50%
|
||||||
|
hyprctl dispatch focuswindow $(echo "$windows" | sed -n 3p)
|
||||||
|
hyprctl dispatch movewindow 0 50% resizewindowpixel 100% 50% ;;
|
||||||
|
4)
|
||||||
|
# Configura cuatro ventanas en una cuadrícula de 2x2
|
||||||
|
hyprctl dispatch focuswindow $(echo "$windows" | sed -n 1p)
|
||||||
|
hyprctl dispatch movewindow 0 0 resizewindowpixel 50% 50%
|
||||||
|
hyprctl dispatch focuswindow $(echo "$windows" | sed -n 2p)
|
||||||
|
hyprctl dispatch movewindow 50% 0 resizewindowpixel 50% 50%
|
||||||
|
hyprctl dispatch focuswindow $(echo "$windows" | sed -n 3p)
|
||||||
|
hyprctl dispatch movewindow 0 50% resizewindowpixel 50% 50%
|
||||||
|
hyprctl dispatch focuswindow $(echo "$windows" | sed -n 4p)
|
||||||
|
hyprctl dispatch movewindow 50% 50% resizewindowpixel 50% 50% ;;
|
||||||
|
esac
|
||||||
|
|
76
.config/waybar/scripts/battery_menu.py
Executable file
76
.config/waybar/scripts/battery_menu.py
Executable file
@ -0,0 +1,76 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
|
def view_details():
|
||||||
|
try:
|
||||||
|
details = subprocess.check_output(
|
||||||
|
["upower", "-i", "/org/freedesktop/UPower/devices/battery_BAT1"],
|
||||||
|
text=True
|
||||||
|
)
|
||||||
|
# Extraer información relevante si es necesario
|
||||||
|
subprocess.run(["notify-send", "Detalles de la Batería", details])
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
subprocess.run(
|
||||||
|
["notify-send", "Error", "No se pudieron obtener los detalles de la batería."])
|
||||||
|
|
||||||
|
|
||||||
|
def check_health():
|
||||||
|
try:
|
||||||
|
health = subprocess.check_output(
|
||||||
|
["upower", "-i", "/org/freedesktop/UPower/devices/battery_BAT1"],
|
||||||
|
text=True
|
||||||
|
)
|
||||||
|
# Extraer información específica sobre la salud
|
||||||
|
energy_full = ""
|
||||||
|
for line in health.splitlines():
|
||||||
|
if "energy-full:" in line:
|
||||||
|
energy_full = line.split(":", 1)[1].strip()
|
||||||
|
break
|
||||||
|
if energy_full:
|
||||||
|
subprocess.run(["notify-send", "Salud de la Batería",
|
||||||
|
f"La capacidad máxima actual es {energy_full}."])
|
||||||
|
else:
|
||||||
|
subprocess.run(["notify-send", "Salud de la Batería",
|
||||||
|
"No se pudo determinar la capacidad máxima."])
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
subprocess.run(
|
||||||
|
["notify-send", "Error", "No se pudo verificar la salud de la batería."])
|
||||||
|
|
||||||
|
|
||||||
|
def save_mode():
|
||||||
|
try:
|
||||||
|
# Reducir el brillo al 50% (asegúrate de tener xbacklight instalado)
|
||||||
|
subprocess.run(["xbacklight", "-set", "50"])
|
||||||
|
subprocess.run(
|
||||||
|
["notify-send", "Modo de Ahorro Activado", "Brillo reducido al 50%."])
|
||||||
|
except Exception as e:
|
||||||
|
subprocess.run(
|
||||||
|
["notify-send", "Error", "No se pudo activar el modo de ahorro."])
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
subprocess.run(
|
||||||
|
["notify-send", "Error", "No se proporcionó ninguna acción."])
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
action = sys.argv[1]
|
||||||
|
|
||||||
|
if action == "view_details":
|
||||||
|
view_details()
|
||||||
|
elif action == "check_health":
|
||||||
|
check_health()
|
||||||
|
elif action == "save_mode":
|
||||||
|
save_mode()
|
||||||
|
else:
|
||||||
|
subprocess.run(
|
||||||
|
["notify-send", "Error", f"Acción desconocida: {action}"])
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
304
.config/waybar/scripts/bt_menu.py
Executable file
304
.config/waybar/scripts/bt_menu.py
Executable file
@ -0,0 +1,304 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from gi.repository import Gtk, GLib, Gio
|
||||||
|
import threading
|
||||||
|
from pydbus import SystemBus
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
|
class BluetoothMenu(Gtk.Application):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(application_id="org.example.BluetoothMenu")
|
||||||
|
self.bus = SystemBus()
|
||||||
|
self.adapter = self.bus.get("org.bluez", "/org/bluez/hci0")
|
||||||
|
self.object_manager = self.bus.get("org.bluez", "/")
|
||||||
|
self.window = None
|
||||||
|
self.device_window = None
|
||||||
|
self.scanning = False
|
||||||
|
self.discovered_devices = {}
|
||||||
|
|
||||||
|
def do_activate(self):
|
||||||
|
if not self.window:
|
||||||
|
self.window = Gtk.ApplicationWindow(application=self)
|
||||||
|
self.window.set_default_size(200, 150)
|
||||||
|
self.window.set_title("Bluetooth Menu")
|
||||||
|
self.vbox = Gtk.Box(
|
||||||
|
orientation=Gtk.Orientation.VERTICAL, spacing=6)
|
||||||
|
|
||||||
|
if hasattr(self.window, "set_child"):
|
||||||
|
self.window.set_child(self.vbox)
|
||||||
|
else:
|
||||||
|
self.window.add(self.vbox)
|
||||||
|
|
||||||
|
self.build_menu()
|
||||||
|
self.update_bluetooth_buttons()
|
||||||
|
|
||||||
|
self.window.present()
|
||||||
|
|
||||||
|
def build_menu(self):
|
||||||
|
self.item_scan = Gtk.Button(label='Escanear Dispositivos')
|
||||||
|
self.item_scan.connect('clicked', self.scan_and_show_devices)
|
||||||
|
self.add_to_box(self.vbox, self.item_scan)
|
||||||
|
|
||||||
|
self.item_on = Gtk.Button(label='Encender Bluetooth')
|
||||||
|
self.item_on.connect('clicked', self.turn_on_bluetooth)
|
||||||
|
self.add_to_box(self.vbox, self.item_on)
|
||||||
|
|
||||||
|
self.item_off = Gtk.Button(label='Apagar Bluetooth')
|
||||||
|
self.item_off.connect('clicked', self.turn_off_bluetooth)
|
||||||
|
self.add_to_box(self.vbox, self.item_off)
|
||||||
|
|
||||||
|
self.item_send_file = Gtk.Button(label='Enviar Archivo')
|
||||||
|
self.item_send_file.connect('clicked', self.send_file_dialog)
|
||||||
|
self.add_to_box(self.vbox, self.item_send_file)
|
||||||
|
|
||||||
|
item_quit = Gtk.Button(label='Cerrar')
|
||||||
|
item_quit.connect('clicked', self.exit_app)
|
||||||
|
self.add_to_box(self.vbox, item_quit)
|
||||||
|
|
||||||
|
def add_to_box(self, box, widget):
|
||||||
|
if hasattr(box, "append"):
|
||||||
|
box.append(widget)
|
||||||
|
else:
|
||||||
|
box.pack_start(widget, True, True, 0)
|
||||||
|
|
||||||
|
def send_file_dialog(self, _):
|
||||||
|
dialog = Gtk.FileChooserDialog(
|
||||||
|
title="Selecciona un archivo para enviar",
|
||||||
|
transient_for=self.window,
|
||||||
|
action=Gtk.FileChooserAction.OPEN,
|
||||||
|
modal=True
|
||||||
|
)
|
||||||
|
dialog.add_buttons(
|
||||||
|
"Cancelar", Gtk.ResponseType.CANCEL,
|
||||||
|
"Abrir", Gtk.ResponseType.OK
|
||||||
|
)
|
||||||
|
|
||||||
|
dialog.connect("response", self.on_file_chosen)
|
||||||
|
dialog.show()
|
||||||
|
|
||||||
|
def on_file_chosen(self, dialog, response):
|
||||||
|
if response == Gtk.ResponseType.OK:
|
||||||
|
file = dialog.get_file() # Obtenemos el archivo directamente
|
||||||
|
if file:
|
||||||
|
file_path = file.get_path() # Obtenemos la ruta del archivo
|
||||||
|
|
||||||
|
# Oculta la ventana principal antes de abrir la ventana de selección de dispositivos
|
||||||
|
self.window.hide()
|
||||||
|
|
||||||
|
# Llama a la función para seleccionar el dispositivo y enviar el archivo
|
||||||
|
self.select_device_for_file_transfer(file_path)
|
||||||
|
|
||||||
|
dialog.destroy()
|
||||||
|
|
||||||
|
def select_device_for_file_transfer(self, file_path):
|
||||||
|
if self.device_window:
|
||||||
|
self.device_window.destroy()
|
||||||
|
|
||||||
|
self.device_window = Gtk.Window(title="Seleccionar dispositivo")
|
||||||
|
self.device_window.set_default_size(300, 400)
|
||||||
|
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
|
||||||
|
self.device_window.set_child(vbox)
|
||||||
|
|
||||||
|
devices = self.get_discovered_devices()
|
||||||
|
for name, address in devices:
|
||||||
|
button = Gtk.Button(label=f"{name} ({address})")
|
||||||
|
button.connect("clicked", self.send_file_to_device,
|
||||||
|
file_path, address)
|
||||||
|
vbox.append(button)
|
||||||
|
|
||||||
|
# Añade un botón "Cerrar" al final de la ventana de selección de dispositivos
|
||||||
|
close_button = Gtk.Button(label="Cerrar")
|
||||||
|
close_button.connect("clicked", lambda _: self.device_window.destroy())
|
||||||
|
vbox.append(close_button)
|
||||||
|
|
||||||
|
# Muestra la ventana de selección de dispositivos
|
||||||
|
self.device_window.show()
|
||||||
|
|
||||||
|
# Restaura la ventana principal al cerrar la ventana de selección de dispositivos
|
||||||
|
self.device_window.connect("destroy", lambda w: self.window.show())
|
||||||
|
|
||||||
|
def send_file_to_device(self, button, file_path, address):
|
||||||
|
try:
|
||||||
|
# Ejecuta bluetooth-sendto con la dirección del dispositivo y el archivo
|
||||||
|
result = subprocess.run(
|
||||||
|
['bluetooth-sendto', '--device=' + address, file_path],
|
||||||
|
capture_output=True,
|
||||||
|
text=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Verifica si se produjo un error en la ejecución
|
||||||
|
if result.returncode == 0:
|
||||||
|
self.show_notification(f"Archivo enviado a {address}")
|
||||||
|
else:
|
||||||
|
self.show_notification(
|
||||||
|
f"Error al enviar archivo: {result.stderr}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.show_notification(f"Error al ejecutar bluetooth-sendto: {e}")
|
||||||
|
|
||||||
|
def update_bluetooth_buttons(self):
|
||||||
|
powered = self.adapter.Powered
|
||||||
|
self.item_on.set_sensitive(not powered)
|
||||||
|
self.item_off.set_sensitive(powered)
|
||||||
|
|
||||||
|
def scan_and_show_devices(self, _):
|
||||||
|
try:
|
||||||
|
# Inicia el escaneo solo si no está en proceso
|
||||||
|
if not self.scanning:
|
||||||
|
self.adapter.StartDiscovery()
|
||||||
|
self.scanning = True
|
||||||
|
self.show_notification(
|
||||||
|
"Escaneo de dispositivos Bluetooth iniciado")
|
||||||
|
|
||||||
|
# Muestra la ventana de dispositivos en tiempo real
|
||||||
|
self.show_device_list_window()
|
||||||
|
except Exception as e:
|
||||||
|
self.show_notification(f"Error iniciando el escaneo: {e}")
|
||||||
|
|
||||||
|
def show_device_list_window(self):
|
||||||
|
if self.device_window:
|
||||||
|
self.device_window.destroy()
|
||||||
|
|
||||||
|
self.device_window = Gtk.Window(title="Dispositivos Disponibles")
|
||||||
|
self.device_window.set_default_size(400, 500)
|
||||||
|
self.device_window.set_resizable(True)
|
||||||
|
self.device_window.set_transient_for(self.window)
|
||||||
|
|
||||||
|
scrolled_window = Gtk.ScrolledWindow()
|
||||||
|
scrolled_window.set_policy(
|
||||||
|
Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||||
|
scrolled_window.set_min_content_height(400)
|
||||||
|
|
||||||
|
self.device_list_vbox = Gtk.Box(
|
||||||
|
orientation=Gtk.Orientation.VERTICAL, spacing=10)
|
||||||
|
if hasattr(scrolled_window, "set_child"):
|
||||||
|
scrolled_window.set_child(self.device_list_vbox)
|
||||||
|
else:
|
||||||
|
scrolled_window.add(self.device_list_vbox)
|
||||||
|
|
||||||
|
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
|
||||||
|
self.add_to_box(vbox, scrolled_window)
|
||||||
|
|
||||||
|
refresh_button = Gtk.Button(label="Refrescar")
|
||||||
|
refresh_button.connect("clicked", self.refresh_device_list)
|
||||||
|
self.add_to_box(vbox, refresh_button)
|
||||||
|
|
||||||
|
close_button = Gtk.Button(label="Cerrar")
|
||||||
|
close_button.connect("clicked", self.stop_scan_and_close)
|
||||||
|
self.add_to_box(vbox, close_button)
|
||||||
|
|
||||||
|
if hasattr(self.device_window, "set_child"):
|
||||||
|
self.device_window.set_child(vbox)
|
||||||
|
else:
|
||||||
|
self.device_window.add(vbox)
|
||||||
|
|
||||||
|
self.device_window.present()
|
||||||
|
|
||||||
|
# Asegura que el temporizador se ejecute para actualizar la lista en tiempo real
|
||||||
|
if not hasattr(self, 'update_devices_timer'):
|
||||||
|
self.update_devices_timer = GLib.timeout_add_seconds(
|
||||||
|
2, self.update_device_list)
|
||||||
|
|
||||||
|
def get_discovered_devices(self):
|
||||||
|
devices = []
|
||||||
|
managed_objects = self.object_manager.GetManagedObjects()
|
||||||
|
for path, interfaces in managed_objects.items():
|
||||||
|
if "org.bluez.Device1" in interfaces:
|
||||||
|
device_properties = interfaces["org.bluez.Device1"]
|
||||||
|
if device_properties.get("Connected") or device_properties.get("Paired"):
|
||||||
|
# Omitir dispositivos emparejados o ya conectados
|
||||||
|
continue
|
||||||
|
name = device_properties.get("Name", "Desconocido")
|
||||||
|
address = device_properties.get("Address")
|
||||||
|
devices.append((name, address))
|
||||||
|
return devices
|
||||||
|
|
||||||
|
def refresh_device_list(self, _):
|
||||||
|
self.discovered_devices.clear()
|
||||||
|
if self.device_list_vbox and self.device_window:
|
||||||
|
self.clear_box_children(self.device_list_vbox)
|
||||||
|
self.show_notification("Lista de dispositivos actualizada")
|
||||||
|
|
||||||
|
def clear_box_children(self, box):
|
||||||
|
if hasattr(box, 'get_children'):
|
||||||
|
for child in box.get_children():
|
||||||
|
box.remove(child)
|
||||||
|
else:
|
||||||
|
child = box.get_first_child()
|
||||||
|
while child:
|
||||||
|
next_child = child.get_next_sibling()
|
||||||
|
box.remove(child)
|
||||||
|
child = next_child
|
||||||
|
|
||||||
|
def update_device_list(self):
|
||||||
|
devices = self.get_discovered_devices()
|
||||||
|
for name, address in devices:
|
||||||
|
if address not in self.discovered_devices:
|
||||||
|
self.discovered_devices[address] = name
|
||||||
|
if self.device_window and self.device_list_vbox:
|
||||||
|
button = Gtk.Button(label=f"{name} ({address})")
|
||||||
|
button.connect("clicked", self.pair_trust_connect, address)
|
||||||
|
self.add_to_box(self.device_list_vbox, button)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def stop_scan_and_close(self, _):
|
||||||
|
if self.device_window:
|
||||||
|
self.device_window.destroy()
|
||||||
|
self.device_window = None
|
||||||
|
self.discovered_devices.clear()
|
||||||
|
|
||||||
|
def get_discovered_devices(self):
|
||||||
|
devices = []
|
||||||
|
managed_objects = self.object_manager.GetManagedObjects()
|
||||||
|
for path, interfaces in managed_objects.items():
|
||||||
|
if "org.bluez.Device1" in interfaces:
|
||||||
|
device_properties = interfaces["org.bluez.Device1"]
|
||||||
|
name = device_properties.get("Name", "Desconocido")
|
||||||
|
address = device_properties.get("Address")
|
||||||
|
devices.append((name, address))
|
||||||
|
return devices
|
||||||
|
|
||||||
|
def pair_trust_connect(self, button, address):
|
||||||
|
threading.Thread(target=self.pair_trust_connect_thread,
|
||||||
|
args=(address,)).start()
|
||||||
|
|
||||||
|
def pair_trust_connect_thread(self, address):
|
||||||
|
device_path = f"/org/bluez/hci0/dev_{address.replace(':', '_')}"
|
||||||
|
device = self.bus.get("org.bluez", device_path)
|
||||||
|
try:
|
||||||
|
device.Pair()
|
||||||
|
device.Trusted = True
|
||||||
|
device.Connect()
|
||||||
|
self.show_notification(f"Conectado a {address}")
|
||||||
|
except Exception as e:
|
||||||
|
self.show_notification(f"Error al conectar con {address}: {e}")
|
||||||
|
|
||||||
|
def turn_on_bluetooth(self, _):
|
||||||
|
self.adapter.Powered = True
|
||||||
|
self.show_notification("Bluetooth Encendido")
|
||||||
|
self.update_bluetooth_buttons()
|
||||||
|
|
||||||
|
def turn_off_bluetooth(self, _):
|
||||||
|
self.adapter.Powered = False
|
||||||
|
self.show_notification("Bluetooth Apagado")
|
||||||
|
self.update_bluetooth_buttons()
|
||||||
|
|
||||||
|
def exit_app(self, _):
|
||||||
|
if self.scanning:
|
||||||
|
self.adapter.StopDiscovery()
|
||||||
|
self.scanning = False
|
||||||
|
if hasattr(self, 'update_devices_timer'):
|
||||||
|
GLib.source_remove(self.update_devices_timer)
|
||||||
|
self.quit()
|
||||||
|
|
||||||
|
def show_notification(self, message):
|
||||||
|
subprocess.run(['notify-send', message])
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
app = BluetoothMenu()
|
||||||
|
app.run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
4
.config/waybar/scripts/check_battery_health.sh
Executable file
4
.config/waybar/scripts/check_battery_health.sh
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
HEALTH=$(upower -i /org/freedesktop/UPower/devices/battery_BAT1 | grep "energy-full")
|
||||||
|
zenity --info --text="Salud de la Batería:\nLa capacidad máxima actual es $HEALTH." --title="Salud de la Batería"
|
||||||
|
|
9
.config/waybar/scripts/config.json
Normal file
9
.config/waybar/scripts/config.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"roonstate": {
|
||||||
|
"tokens": {
|
||||||
|
"4000ff7f-2284-4810-9e5d-a0a7b1f7bc1a": "a979118e-66c9-48a0-8eae-c6899d5dca81",
|
||||||
|
"0754ce41-74ac-44bb-a9d5-e47d8f377f46": "77528fe8-8f9e-422b-84e7-794bfe5d8739"
|
||||||
|
},
|
||||||
|
"paired_core_id": "0754ce41-74ac-44bb-a9d5-e47d8f377f46"
|
||||||
|
}
|
||||||
|
}
|
51
.config/waybar/scripts/discovery.py
Normal file
51
.config/waybar/scripts/discovery.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
from roonapi import RoonApi
|
||||||
|
import time
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
appinfo = {
|
||||||
|
"extension_id": "python_roon_waybar",
|
||||||
|
"display_name": "Waybar Roon Extension",
|
||||||
|
"display_version": "1.0.0",
|
||||||
|
"publisher": "gregd",
|
||||||
|
"email": "mygreat@emailaddress.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Lee el core_id y el token desde los archivos
|
||||||
|
try:
|
||||||
|
core_id = open("/home/teraflops/apikeys/roon_core_id").read().strip()
|
||||||
|
token = open("/home/teraflops/apikeys/roon_core_token").read().strip()
|
||||||
|
except FileNotFoundError:
|
||||||
|
print("Error: core_id o token no encontrados. Debes autorizar usando discovery.py primero.")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
# Conéctate a Roon usando el core_id y el token guardados
|
||||||
|
roonapi = RoonApi(appinfo, token, "192.168.1.37", 9330, True)
|
||||||
|
|
||||||
|
|
||||||
|
def my_state_callback(event, changed_ids):
|
||||||
|
"""Llamado cuando algo cambia en Roon."""
|
||||||
|
for zone_id in changed_ids:
|
||||||
|
zone = roonapi.zones[zone_id]
|
||||||
|
if zone['state'] == 'playing':
|
||||||
|
track_name = zone['now_playing']['three_line']['line1']
|
||||||
|
artist_name = zone['now_playing']['three_line']['line2']
|
||||||
|
print(f"Canción en reproducción: {track_name} - {artist_name}")
|
||||||
|
|
||||||
|
# Actualiza Waybar usando un archivo de texto o variable
|
||||||
|
with open("/tmp/waybar_roon_track", "w") as f:
|
||||||
|
f.write(f"{track_name} - {artist_name}")
|
||||||
|
|
||||||
|
# Envía notificación de la canción
|
||||||
|
subprocess.run([
|
||||||
|
"notify-send", "Roon Now Playing",
|
||||||
|
f"{track_name} - {artist_name}"
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
# Registra el callback para recibir actualizaciones
|
||||||
|
roonapi.register_state_callback(my_state_callback)
|
||||||
|
|
||||||
|
# Mantén el script corriendo para escuchar cambios
|
||||||
|
while True:
|
||||||
|
time.sleep(10)
|
||||||
|
|
35
.config/waybar/scripts/dmp_nowplaying.sh
Executable file
35
.config/waybar/scripts/dmp_nowplaying.sh
Executable file
@ -0,0 +1,35 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
IP="192.168.1.140"
|
||||||
|
ENDPOINT="http://$IP:9529/ZidooMusicControl/v2/getState"
|
||||||
|
|
||||||
|
data=$(curl -s --max-time 2 "$ENDPOINT")
|
||||||
|
|
||||||
|
if [[ -n "$data" ]]; then
|
||||||
|
artist=$(echo "$data" | jq -r '.playingMusic.artist')
|
||||||
|
title=$(echo "$data" | jq -r '.playingMusic.title')
|
||||||
|
status=$(echo "$data" | jq -r '.everSoloPlayInfo.playStatus')
|
||||||
|
|
||||||
|
# Detalles extra para el tooltip
|
||||||
|
bitrate=$(echo "$data" | jq -r '.playingMusic.bitrate')
|
||||||
|
samplerate=$(echo "$data" | jq -r '.playingMusic.sampleRate')
|
||||||
|
quality=$(echo "$data" | jq -r '.playingMusic.audioQuality')
|
||||||
|
|
||||||
|
# Escapar caracteres especiales
|
||||||
|
artist="${artist//&/&}"
|
||||||
|
title="${title//&/&}"
|
||||||
|
bitrate="${bitrate//&/&}"
|
||||||
|
samplerate="${samplerate//&/&}"
|
||||||
|
quality="${quality//&/&}"
|
||||||
|
|
||||||
|
tooltip="<b>${artist} - ${title}</b>\n${quality} | ${samplerate} | ${bitrate}"
|
||||||
|
|
||||||
|
if [[ "$status" == "1" && "$artist" != "null" && "$title" != "null" ]]; then
|
||||||
|
echo "{\"text\": \"🎵 $artist - $title\", \"tooltip\": \"$tooltip\"}"
|
||||||
|
else
|
||||||
|
echo "{\"text\": \"⏸️ Pausado o sin música\", \"tooltip\": \"No se está reproduciendo música.\"}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "{\"text\": \"⚠️ DMP-A6 desconectado\", \"tooltip\": \"No se pudo conectar al Eversolo.\"}"
|
||||||
|
fi
|
||||||
|
|
1
.config/waybar/scripts/down.txt
Normal file
1
.config/waybar/scripts/down.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
⬇ 548.60 Mbps
|
51
.config/waybar/scripts/fan_speed.sh
Executable file
51
.config/waybar/scripts/fan_speed.sh
Executable file
@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
STATE_FILE="/tmp/fancycle_state"
|
||||||
|
HWMON_PATH="/sys/class/hwmon/hwmon3" # Ajusta según tu caso
|
||||||
|
|
||||||
|
# Si no existe el archivo de estado, inícialo en 1 (fan1)
|
||||||
|
if [[ ! -f "$STATE_FILE" ]]; then
|
||||||
|
echo 1 > "$STATE_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Si el script es invocado con el argumento "next", incrementa el estado
|
||||||
|
if [[ "$1" == "next" ]]; then
|
||||||
|
current_state=$(cat "$STATE_FILE")
|
||||||
|
# Suponiendo que tienes 3 ventiladores y quieres ciclar 1->2->3->1->...
|
||||||
|
next_state=$(( (current_state % 3) + 1 ))
|
||||||
|
echo "$next_state" > "$STATE_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Lee el estado actual
|
||||||
|
state=$(cat "$STATE_FILE")
|
||||||
|
|
||||||
|
# Lee la info real de tus ventiladores
|
||||||
|
# Lee la info real de tus ventiladores
|
||||||
|
fan1_label=$(cat "$HWMON_PATH/fan1_label" 2>/dev/null | sed 's/_fan//I' | tr '[:lower:]' '[:upper:]')
|
||||||
|
fan1_speed=$(cat "$HWMON_PATH/fan1_input" 2>/dev/null)
|
||||||
|
|
||||||
|
fan2_label=$(cat "$HWMON_PATH/fan2_label" 2>/dev/null | sed 's/_fan//I' | tr '[:lower:]' '[:upper:]')
|
||||||
|
fan2_speed=$(cat "$HWMON_PATH/fan2_input" 2>/dev/null)
|
||||||
|
|
||||||
|
fan3_label=$(cat "$HWMON_PATH/fan3_label" 2>/dev/null | sed 's/_fan//I' | tr '[:lower:]' '[:upper:]')
|
||||||
|
fan3_speed=$(cat "$HWMON_PATH/fan3_input" 2>/dev/null)
|
||||||
|
|
||||||
|
|
||||||
|
# Prepara el tooltip con TODOS los ventiladores
|
||||||
|
tooltip="${fan1_label}: ${fan1_speed} RPM
|
||||||
|
${fan2_label}: ${fan2_speed} RPM
|
||||||
|
${fan3_label}: ${fan3_speed} RPM"
|
||||||
|
|
||||||
|
# Según el "estado", decide cuál ventilador se muestra en "text"
|
||||||
|
case "$state" in
|
||||||
|
1) text="${fan1_label}: ${fan1_speed} RPM" ;;
|
||||||
|
2) text="${fan2_label}: ${fan2_speed} RPM" ;;
|
||||||
|
3) text="${fan3_label}: ${fan3_speed} RPM" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Usa jq para generar JSON
|
||||||
|
jq -c -n \
|
||||||
|
--arg text "$text" \
|
||||||
|
--arg tooltip "$tooltip" \
|
||||||
|
'{ "text": $text, "tooltip": $tooltip }'
|
||||||
|
|
49
.config/waybar/scripts/fan_speed.sh.v1
Executable file
49
.config/waybar/scripts/fan_speed.sh.v1
Executable file
@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
STATE_FILE="/tmp/fancycle_state"
|
||||||
|
HWMON_PATH="/sys/class/hwmon/hwmon3" # Ajusta según tu caso
|
||||||
|
|
||||||
|
# Si no existe el archivo de estado, inícialo en 1 (fan1)
|
||||||
|
if [[ ! -f "$STATE_FILE" ]]; then
|
||||||
|
echo 1 > "$STATE_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Si el script es invocado con el argumento "next", incrementa el estado
|
||||||
|
if [[ "$1" == "next" ]]; then
|
||||||
|
current_state=$(cat "$STATE_FILE")
|
||||||
|
# Suponiendo que tienes 3 ventiladores y quieres ciclar 1->2->3->1->...
|
||||||
|
next_state=$(( (current_state % 3) + 1 ))
|
||||||
|
echo "$next_state" > "$STATE_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Lee el estado actual
|
||||||
|
state=$(cat "$STATE_FILE")
|
||||||
|
|
||||||
|
# Lee la info real de tus ventiladores
|
||||||
|
fan1_label=$(cat "$HWMON_PATH/fan1_label" 2>/dev/null)
|
||||||
|
fan1_speed=$(cat "$HWMON_PATH/fan1_input" 2>/dev/null)
|
||||||
|
|
||||||
|
fan2_label=$(cat "$HWMON_PATH/fan2_label" 2>/dev/null)
|
||||||
|
fan2_speed=$(cat "$HWMON_PATH/fan2_input" 2>/dev/null)
|
||||||
|
|
||||||
|
fan3_label=$(cat "$HWMON_PATH/fan3_label" 2>/dev/null)
|
||||||
|
fan3_speed=$(cat "$HWMON_PATH/fan3_input" 2>/dev/null)
|
||||||
|
|
||||||
|
# Prepara el tooltip con TODOS los ventiladores
|
||||||
|
tooltip="${fan1_label}: ${fan1_speed} RPM
|
||||||
|
${fan2_label}: ${fan2_speed} RPM
|
||||||
|
${fan3_label}: ${fan3_speed} RPM"
|
||||||
|
|
||||||
|
# Según el "estado", decide cuál ventilador se muestra en "text"
|
||||||
|
case "$state" in
|
||||||
|
1) text="${fan1_label}: ${fan1_speed} RPM" ;;
|
||||||
|
2) text="${fan2_label}: ${fan2_speed} RPM" ;;
|
||||||
|
3) text="${fan3_label}: ${fan3_speed} RPM" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Usa jq para generar JSON
|
||||||
|
jq -c -n \
|
||||||
|
--arg text "$text" \
|
||||||
|
--arg tooltip "$tooltip" \
|
||||||
|
'{ "text": $text, "tooltip": $tooltip }'
|
||||||
|
|
51
.config/waybar/scripts/fan_speed.sh_bicolor
Executable file
51
.config/waybar/scripts/fan_speed.sh_bicolor
Executable file
@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
STATE_FILE="/tmp/fancycle_state"
|
||||||
|
HWMON_PATH="/sys/class/hwmon/hwmon3" # Ajusta según tu caso
|
||||||
|
|
||||||
|
# Si no existe el archivo de estado, inícialo en 1 (fan1)
|
||||||
|
if [[ ! -f "$STATE_FILE" ]]; then
|
||||||
|
echo 1 > "$STATE_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Si el script es invocado con el argumento "next", incrementa el estado
|
||||||
|
if [[ "$1" == "next" ]]; then
|
||||||
|
current_state=$(cat "$STATE_FILE")
|
||||||
|
next_state=$(( (current_state % 3) + 1 ))
|
||||||
|
echo "$next_state" > "$STATE_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
state=$(cat "$STATE_FILE")
|
||||||
|
|
||||||
|
# Lee info de los ventiladores
|
||||||
|
fan1_label=$(cat "$HWMON_PATH/fan1_label" 2>/dev/null | sed 's/_fan//I')
|
||||||
|
fan1_speed=$(cat "$HWMON_PATH/fan1_input" 2>/dev/null)
|
||||||
|
|
||||||
|
fan2_label=$(cat "$HWMON_PATH/fan2_label" 2>/dev/null | sed 's/_fan//I')
|
||||||
|
fan2_speed=$(cat "$HWMON_PATH/fan2_input" 2>/dev/null)
|
||||||
|
|
||||||
|
fan3_label=$(cat "$HWMON_PATH/fan3_label" 2>/dev/null | sed 's/_fan//I')
|
||||||
|
fan3_speed=$(cat "$HWMON_PATH/fan3_input" 2>/dev/null)
|
||||||
|
|
||||||
|
tooltip="${fan1_label}: ${fan1_speed} RPM
|
||||||
|
${fan2_label}: ${fan2_speed} RPM
|
||||||
|
${fan3_label}: ${fan3_speed} RPM"
|
||||||
|
|
||||||
|
# Determinar texto con color según estado
|
||||||
|
case "$state" in
|
||||||
|
1)
|
||||||
|
text="<span foreground='#fa9f8e'>${fan1_label}:</span> <span foreground='#81a1c1'>${fan1_speed} RPM</span>"
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
text="<span foreground='#fa9f8e'>${fan2_label}:</span> <span foreground='#81a1c1'>${fan2_speed} RPM</span>"
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
text="<span foreground='#fa9f8e'>${fan3_label}:</span> <span foreground='#81a1c1'>${fan3_speed} RPM</span>"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
jq -c -n \
|
||||||
|
--arg text "$text" \
|
||||||
|
--arg tooltip "$tooltip" \
|
||||||
|
'{ "text": $text, "tooltip": $tooltip }'
|
||||||
|
|
49
.config/waybar/scripts/get_current_track.py
Executable file
49
.config/waybar/scripts/get_current_track.py
Executable file
@ -0,0 +1,49 @@
|
|||||||
|
import time
|
||||||
|
from roonapi import RoonApi
|
||||||
|
|
||||||
|
appinfo = {
|
||||||
|
"extension_id": "python_roon_waybar",
|
||||||
|
"display_name": "Waybar Roon Extension",
|
||||||
|
"display_version": "1.0.0",
|
||||||
|
"publisher": "gregd",
|
||||||
|
"email": "mygreat@emailaddress.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
core_id = open("/home/teraflops/apikeys/roon_core_id").read()
|
||||||
|
token = open("/home/teraflops/apikeys/roon_core_token").read()
|
||||||
|
|
||||||
|
roonapi = RoonApi(appinfo, token, "192.168.1.37", "9330", True)
|
||||||
|
|
||||||
|
|
||||||
|
def send_album_cover_notification(zone):
|
||||||
|
"""Envía una notificación con la carátula del álbum."""
|
||||||
|
album_cover_url = zone['now_playing']['image_key']
|
||||||
|
response = requests.get(
|
||||||
|
f"http://{roonapi.host}:{roonapi.port}/image/{album_cover_url}")
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
cover_path = "/tmp/roon_album_cover.jpg"
|
||||||
|
with open(cover_path, "wb") as f:
|
||||||
|
f.write(response.content)
|
||||||
|
|
||||||
|
# Envía la notificación usando notify-send
|
||||||
|
subprocess.run([
|
||||||
|
"notify-send", "-i", cover_path, "Now Playing",
|
||||||
|
f"{zone['now_playing']['three_line']['line1']
|
||||||
|
} - {zone['now_playing']['three_line']['line2']}"
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_track():
|
||||||
|
"""Obtiene la canción actual que está sonando en Roon."""
|
||||||
|
zones = roonapi.zones
|
||||||
|
for zone in zones.values():
|
||||||
|
if zone['state'] == 'playing':
|
||||||
|
track_name = zone['now_playing']['three_line']['line1']
|
||||||
|
artist_name = zone['now_playing']['three_line']['line2']
|
||||||
|
return f"{track_name} - {artist_name}"
|
||||||
|
return "No track playing"
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print(get_current_track())
|
70
.config/waybar/scripts/get_lyrics.py
Executable file
70
.config/waybar/scripts/get_lyrics.py
Executable file
@ -0,0 +1,70 @@
|
|||||||
|
import sys
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
with open(os.path.expanduser("~/apikeys/musixmatch"), "r") as file:
|
||||||
|
musixmatch_token = file.read().strip()
|
||||||
|
|
||||||
|
def get_lyrics(artist, song_title):
|
||||||
|
search_url = "https://api.musixmatch.com/ws/1.1/track.search"
|
||||||
|
params = {
|
||||||
|
"q_track_artist": f"{artist} {song_title}",
|
||||||
|
"apikey": musixmatch_token,
|
||||||
|
"s_track_rating": "desc",
|
||||||
|
"f_has_lyrics": "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.get(search_url, params=params)
|
||||||
|
if response.status_code != 200:
|
||||||
|
return f"Error al buscar la canción: {response.status_code}"
|
||||||
|
|
||||||
|
data = response.json()
|
||||||
|
tracks = data.get("message", {}).get("body", {}).get("track_list", [])
|
||||||
|
if not tracks:
|
||||||
|
return "No results found for this track."
|
||||||
|
|
||||||
|
track_id = None
|
||||||
|
for t in tracks:
|
||||||
|
t_title = t["track"]["track_name"].lower()
|
||||||
|
t_artist = t["track"]["artist_name"].lower()
|
||||||
|
if song_title.lower() in t_title and artist.lower() in t_artist:
|
||||||
|
track_id = t["track"]["track_id"]
|
||||||
|
print("\n Filtered search result:")
|
||||||
|
print("Track found:", t["track"]["track_name"])
|
||||||
|
print("Artist foung:", t["track"]["artist_name"])
|
||||||
|
print("Track ID:", track_id)
|
||||||
|
print("---")
|
||||||
|
break
|
||||||
|
|
||||||
|
if not track_id:
|
||||||
|
track_id = tracks[0]["track"]["track_id"]
|
||||||
|
print("\n generic search result:")
|
||||||
|
print("Title found:", tracks[0]["track"]["track_name"])
|
||||||
|
print("Artist found:", tracks[0]["track"]["artist_name"])
|
||||||
|
print("Track ID:", track_id)
|
||||||
|
print("---")
|
||||||
|
|
||||||
|
lyrics_url = "https://api.musixmatch.com/ws/1.1/track.lyrics.get"
|
||||||
|
lyrics_params = {
|
||||||
|
"track_id": track_id,
|
||||||
|
"apikey": musixmatch_token
|
||||||
|
}
|
||||||
|
|
||||||
|
lyrics_response = requests.get(lyrics_url, params=lyrics_params)
|
||||||
|
if lyrics_response.status_code != 200:
|
||||||
|
return f"Error obtaining lyrics: {lyrics_response.status_code}"
|
||||||
|
|
||||||
|
lyrics_data = lyrics_response.json()
|
||||||
|
lyrics = lyrics_data.get("message", {}).get("body", {}).get("lyrics", {}).get("lyrics_body", "")
|
||||||
|
return lyrics or "no lyrics available."
|
||||||
|
|
||||||
|
if len(sys.argv) != 3:
|
||||||
|
print("Usage: python get_lyrics.py <artist> <song>")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
artist = sys.argv[1]
|
||||||
|
song_title = sys.argv[2]
|
||||||
|
lyrics = get_lyrics(artist, song_title)
|
||||||
|
print(lyrics)
|
||||||
|
|
28
.config/waybar/scripts/get_lyrics.sh
Executable file
28
.config/waybar/scripts/get_lyrics.sh
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Rutas y archivos temporales
|
||||||
|
CURRENT_TRACK_FILE="/tmp/current_track.txt"
|
||||||
|
LYRICS_FILE="/tmp/lyrics.txt"
|
||||||
|
LOG_FILE="/tmp/lyrics_error.log"
|
||||||
|
|
||||||
|
# Obtener la información actual del track usando `playerctl`
|
||||||
|
TRACK_INFO=$(playerctl metadata --format "{{artist}} - {{title}}" 2>>"$LOG_FILE")
|
||||||
|
|
||||||
|
# Validar si hay información del track
|
||||||
|
if [ -z "$TRACK_INFO" ]; then
|
||||||
|
echo "No track info available" > "$LYRICS_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extraer artista y título
|
||||||
|
ARTIST=$(echo "$TRACK_INFO" | cut -d '-' -f 1 | xargs)
|
||||||
|
TITLE=$(echo "$TRACK_INFO" | cut -d '-' -f 2 | xargs)
|
||||||
|
|
||||||
|
# Usar Python para obtener las letras
|
||||||
|
python3 ~/.config/waybar/scripts/get_lyrics.py "$ARTIST" "$TITLE" > "$LYRICS_FILE"
|
||||||
|
|
||||||
|
# Verificar si el archivo de letras está vacío
|
||||||
|
if [ ! -s "$LYRICS_FILE" ]; then
|
||||||
|
echo "Letras no encontradas para $ARTIST - $TITLE" > "$LYRICS_FILE"
|
||||||
|
fi
|
||||||
|
|
4
.config/waybar/scripts/get_power_profile.sh
Executable file
4
.config/waybar/scripts/get_power_profile.sh
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
profile = $(asusctl profile -p |awk '{print $4}')
|
||||||
|
|
2
.config/waybar/scripts/gpu.sh
Executable file
2
.config/waybar/scripts/gpu.sh
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
echo "/home/teraflops/Icons/nvidia.png"
|
64
.config/waybar/scripts/inetdown.py
Executable file
64
.config/waybar/scripts/inetdown.py
Executable file
@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def read_value_from_file(file_path):
|
||||||
|
try:
|
||||||
|
with open(file_path, 'r') as file:
|
||||||
|
value = file.read().strip()
|
||||||
|
return value
|
||||||
|
except Exception as e:
|
||||||
|
return f"Error: {e}"
|
||||||
|
|
||||||
|
def read_click_state(state_file):
|
||||||
|
try:
|
||||||
|
if os.path.exists(state_file):
|
||||||
|
with open(state_file, 'r') as file:
|
||||||
|
state = file.read().strip()
|
||||||
|
return state == "clicked"
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def toggle_click_state(state_file):
|
||||||
|
try:
|
||||||
|
if read_click_state(state_file):
|
||||||
|
with open(state_file, 'w') as file:
|
||||||
|
file.write("unclicked")
|
||||||
|
else:
|
||||||
|
with open(state_file, 'w') as file:
|
||||||
|
file.write("clicked")
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def generate_waybar_json(value, clicked):
|
||||||
|
if clicked:
|
||||||
|
data = {
|
||||||
|
"text": value,
|
||||||
|
#"tooltip": f"Download Speed: {value}",
|
||||||
|
#"class": "custom"
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
data = {
|
||||||
|
"text": "DL",
|
||||||
|
"tooltip": f"Dpwnload Speed: {value}",
|
||||||
|
"class": "custom"
|
||||||
|
}
|
||||||
|
return json.dumps(data)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
file_path = "/home/teraflops/.config/waybar/scripts/down.txt"
|
||||||
|
state_file = "/tmp/waybar_tooltip_state"
|
||||||
|
value = read_value_from_file(file_path)
|
||||||
|
|
||||||
|
# Check for the toggle argument
|
||||||
|
if len(sys.argv) > 1 and sys.argv[1] == "toggle":
|
||||||
|
toggle_click_state(state_file)
|
||||||
|
|
||||||
|
clicked = read_click_state(state_file)
|
||||||
|
waybar_json = generate_waybar_json(value, clicked)
|
||||||
|
print(waybar_json)
|
||||||
|
|
64
.config/waybar/scripts/inetup.py
Executable file
64
.config/waybar/scripts/inetup.py
Executable file
@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def read_value_from_file(file_path):
|
||||||
|
try:
|
||||||
|
with open(file_path, 'r') as file:
|
||||||
|
value = file.read().strip()
|
||||||
|
return value
|
||||||
|
except Exception as e:
|
||||||
|
return f"Error: {e}"
|
||||||
|
|
||||||
|
def read_click_state(state_file):
|
||||||
|
try:
|
||||||
|
if os.path.exists(state_file):
|
||||||
|
with open(state_file, 'r') as file:
|
||||||
|
state = file.read().strip()
|
||||||
|
return state == "clicked"
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def toggle_click_state(state_file):
|
||||||
|
try:
|
||||||
|
if read_click_state(state_file):
|
||||||
|
with open(state_file, 'w') as file:
|
||||||
|
file.write("unclicked")
|
||||||
|
else:
|
||||||
|
with open(state_file, 'w') as file:
|
||||||
|
file.write("clicked")
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def generate_waybar_json(value, clicked):
|
||||||
|
if clicked:
|
||||||
|
data = {
|
||||||
|
"text": value,
|
||||||
|
#"tooltip": f"Upload Speed: {value}",
|
||||||
|
#"class": "custom"
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
data = {
|
||||||
|
"text": "UP",
|
||||||
|
"tooltip": f"Upload Speed: {value}",
|
||||||
|
"class": "custom"
|
||||||
|
}
|
||||||
|
return json.dumps(data)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
file_path = "/home/teraflops/.config/waybar/scripts/up.txt"
|
||||||
|
state_file = "/tmp/waybar_tooltip_state"
|
||||||
|
value = read_value_from_file(file_path)
|
||||||
|
|
||||||
|
# Check for the toggle argument
|
||||||
|
if len(sys.argv) > 1 and sys.argv[1] == "toggle":
|
||||||
|
toggle_click_state(state_file)
|
||||||
|
|
||||||
|
clicked = read_click_state(state_file)
|
||||||
|
waybar_json = generate_waybar_json(value, clicked)
|
||||||
|
print(waybar_json)
|
||||||
|
|
154
.config/waybar/scripts/lastfm_cover_downloader.py
Executable file
154
.config/waybar/scripts/lastfm_cover_downloader.py
Executable file
@ -0,0 +1,154 @@
|
|||||||
|
# lastfm_cover_downloader.py
|
||||||
|
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
import urllib.parse
|
||||||
|
from pathlib import Path
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Configuración básica de logging
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||||
|
)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Obtener la ruta de la clave de API de Last.fm desde una variable de entorno
|
||||||
|
API_KEY_FILE = os.environ.get(
|
||||||
|
'LASTFM_API_KEY_PATH', os.path.expandvars('$HOME/apikeys/lastfm'))
|
||||||
|
|
||||||
|
|
||||||
|
def get_lastfm_api_key():
|
||||||
|
"""Leer la clave de API de Last.fm desde un archivo."""
|
||||||
|
try:
|
||||||
|
with open(API_KEY_FILE, 'r') as file:
|
||||||
|
api_key = file.readline().strip()
|
||||||
|
logger.info(f"Successfully read Last.fm API key from {
|
||||||
|
API_KEY_FILE}")
|
||||||
|
return api_key
|
||||||
|
except FileNotFoundError:
|
||||||
|
logger.error(f"API key file '{API_KEY_FILE}' not found.")
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error reading API key: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
# Obtener la clave de API de Last.fm
|
||||||
|
LASTFM_API_KEY = get_lastfm_api_key()
|
||||||
|
if not LASTFM_API_KEY:
|
||||||
|
raise RuntimeError("Last.fm API key could not be loaded.")
|
||||||
|
|
||||||
|
|
||||||
|
def get_largest_cover_image(data):
|
||||||
|
"""Seleccionar la imagen de portada más grande disponible en la respuesta de Last.fm."""
|
||||||
|
if 'album' in data and 'image' in data['album']:
|
||||||
|
# Definir el orden de prioridad de tamaños
|
||||||
|
size_priority = ["mega", "extralarge", "large", "medium", "small"]
|
||||||
|
for size in size_priority:
|
||||||
|
for img in data['album']['image']:
|
||||||
|
if img["size"] == size and img["#text"]:
|
||||||
|
logger.info(f"Using {size} cover: {img['#text']}")
|
||||||
|
return img["#text"]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def download_image(cover_url, output_path):
|
||||||
|
"""Descargar la imagen de portada desde una URL y guardarla en la ruta especificada."""
|
||||||
|
try:
|
||||||
|
logger.info(f"Downloading image from {cover_url} to {output_path}")
|
||||||
|
response = requests.get(cover_url, stream=True)
|
||||||
|
if response.status_code == 200:
|
||||||
|
with open(output_path, 'wb') as f:
|
||||||
|
for chunk in response.iter_content(1024):
|
||||||
|
f.write(chunk)
|
||||||
|
logger.info(f"Image downloaded to {output_path}")
|
||||||
|
return output_path
|
||||||
|
else:
|
||||||
|
logger.error(f"Failed to download image. HTTP Status: {
|
||||||
|
response.status_code}")
|
||||||
|
raise Exception(f"HTTP Status {response.status_code}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error downloading image: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def download_cover_from_lastfm(artist, album, album_dir):
|
||||||
|
"""Descargar la portada del álbum desde Last.fm y guardarla en el directorio del álbum."""
|
||||||
|
try:
|
||||||
|
logger.info(f"Fetching cover for '{album}' by {
|
||||||
|
artist} from Last.fm...")
|
||||||
|
# URL de la API de Last.fm
|
||||||
|
url = f"http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key={LASTFM_API_KEY}&artist={
|
||||||
|
urllib.parse.quote(artist)}&album={urllib.parse.quote(album)}&format=json"
|
||||||
|
response = requests.get(url)
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
cover_url = get_largest_cover_image(data)
|
||||||
|
if cover_url:
|
||||||
|
# Asegurar que el directorio existe
|
||||||
|
album_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
local_cover_path = album_dir / "cover.jpg" # Guardar como 'cover.jpg'
|
||||||
|
download_image(cover_url, local_cover_path)
|
||||||
|
logger.info(f"Downloaded cover to {local_cover_path}")
|
||||||
|
return local_cover_path
|
||||||
|
logger.warning(f"No cover found for '{album}' by {artist} on Last.fm.")
|
||||||
|
raise Exception(f"No cover found for '{album}' by {artist}.")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error fetching cover from Last.fm: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def cover_exists(album_dir):
|
||||||
|
"""Verificar si 'cover.jpg' o 'Cover.jpg' existe en el directorio del álbum."""
|
||||||
|
cover_path_lower = album_dir / "cover.jpg"
|
||||||
|
cover_path_upper = album_dir / "Cover.jpg"
|
||||||
|
exists = cover_path_lower.exists() or cover_path_upper.exists()
|
||||||
|
logger.info(f"Checking cover existence in '{album_dir}': {
|
||||||
|
'Found' if exists else 'Not found'}")
|
||||||
|
return exists
|
||||||
|
|
||||||
|
|
||||||
|
def get_local_album_dir(xesam_url, music_base_path=os.environ.get('MUSIC_LIBRARY_PATH', os.path.expandvars('$HOME/media/all'))):
|
||||||
|
"""Extraer el directorio del álbum desde la ruta `xesam:url`."""
|
||||||
|
if not xesam_url:
|
||||||
|
logger.warning(
|
||||||
|
"xesam:url is empty or missing. Skipping cover detection.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
logger.info(f"Raw xesam:url value: {xesam_url}")
|
||||||
|
|
||||||
|
# Convertir la URL xesam en una ruta de archivo local
|
||||||
|
local_path = urllib.parse.unquote(xesam_url).replace("file://", "")
|
||||||
|
logger.info(f"Converted local path: {local_path}")
|
||||||
|
|
||||||
|
# Eliminar '/trackXXXX' si es una ruta de archivo CUE
|
||||||
|
if "/track" in local_path and ".cue" in local_path:
|
||||||
|
local_path = local_path.split("/track")[0]
|
||||||
|
logger.info(f"Stripped CUE path: {local_path}")
|
||||||
|
|
||||||
|
# Si la ruta no es absoluta, agregar el directorio base de música
|
||||||
|
track_path = Path(local_path)
|
||||||
|
if not track_path.is_absolute():
|
||||||
|
track_path = Path(music_base_path) / track_path
|
||||||
|
logger.info(f"Updated to absolute path: {track_path}")
|
||||||
|
|
||||||
|
# Verificar si la ruta resultante existe
|
||||||
|
if not track_path.exists():
|
||||||
|
logger.warning(f"The path '{track_path}' does not exist.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Manejar archivos CUE: Subir al directorio del álbum real
|
||||||
|
if track_path.suffix == ".cue":
|
||||||
|
return track_path.parent
|
||||||
|
|
||||||
|
# Manejo estándar de rutas
|
||||||
|
if track_path.is_file():
|
||||||
|
return track_path.parent
|
||||||
|
else:
|
||||||
|
logger.warning(f"Path is not a valid file: {track_path}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error extracting local album directory: {e}")
|
||||||
|
return None
|
||||||
|
|
32
.config/waybar/scripts/live.py
Executable file
32
.config/waybar/scripts/live.py
Executable file
@ -0,0 +1,32 @@
|
|||||||
|
import asyncio
|
||||||
|
from msgraph.core import GraphClient
|
||||||
|
from azure.identity import DeviceCodeCredential
|
||||||
|
|
||||||
|
# Reemplaza con tu CLIENT_ID
|
||||||
|
credential = DeviceCodeCredential(
|
||||||
|
client_id="b8d7f3bf-e9a7-4352-b379-833e99a79733"
|
||||||
|
)
|
||||||
|
scopes = ['Mail.Read']
|
||||||
|
graph_client = GraphClient(credential=credential, scopes=scopes)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_mail():
|
||||||
|
response = await graph_client.get(
|
||||||
|
'/me/messages',
|
||||||
|
params={
|
||||||
|
'$select': 'sender,subject',
|
||||||
|
'$filter': 'isRead eq false'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if response.status_code == 200:
|
||||||
|
mail = response.json()
|
||||||
|
for message in mail.get('value', []):
|
||||||
|
sender = message['sender']['emailAddress']['address']
|
||||||
|
subject = message['subject']
|
||||||
|
print(f"De: {sender}, Asunto: {subject}")
|
||||||
|
else:
|
||||||
|
print(f"Error: {response.status_code}")
|
||||||
|
print(response.json())
|
||||||
|
|
||||||
|
asyncio.run(get_mail())
|
||||||
|
|
95
.config/waybar/scripts/lyrics_module.sh
Executable file
95
.config/waybar/scripts/lyrics_module.sh
Executable file
@ -0,0 +1,95 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#set -x # Activa el modo de depuración
|
||||||
|
|
||||||
|
# Paths y archivos temporales
|
||||||
|
CURRENT_TRACK_FILE="/tmp/current_track.txt"
|
||||||
|
LYRICS_FILE="/tmp/lyrics.txt"
|
||||||
|
LOG_FILE="/tmp/lyrics_error.log"
|
||||||
|
STATE_FILE="/tmp/lyrics_state.txt"
|
||||||
|
|
||||||
|
# Función para limpiar el título de la canción
|
||||||
|
clean_title() {
|
||||||
|
local title="$1"
|
||||||
|
# Eliminar prefijos como "B3 ", "A1 ", etc.
|
||||||
|
title=$(echo "$title" | sed -E 's/^[A-Za-z0-9]{1,3}[[:space:]]+//')
|
||||||
|
# Eliminar caracteres especiales como "$", "#", etc.
|
||||||
|
title=$(echo "$title" | tr -d '$#@!%^&*()[]{}<>?/\\|`~')
|
||||||
|
# Eliminar espacios adicionales al inicio y al final
|
||||||
|
title=$(echo "$title" | sed 's/^ *//;s/ *$//')
|
||||||
|
echo "$title"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Obtener información de la pista actual
|
||||||
|
TRACK_INFO=$(playerctl metadata --format "{{artist}} - {{title}}" 2>>"$LOG_FILE")
|
||||||
|
STATUS=$(playerctl status 2>>"$LOG_FILE")
|
||||||
|
echo "STATUS: '$STATUS'" >> "$LOG_FILE"
|
||||||
|
|
||||||
|
# Si no hay información de pista o el estado no es 'Playing', salimos
|
||||||
|
if [ -z "$TRACK_INFO" ] || [ "$STATUS" != "Playing" ]; then
|
||||||
|
jq -c -n '{"text":"","tooltip":""}'
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "TRACK_INFO: '$TRACK_INFO'" >> "$LOG_FILE"
|
||||||
|
|
||||||
|
if [ -z "$TRACK_INFO" ]; then
|
||||||
|
echo "No track info available" > "$LYRICS_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extraer artista y título
|
||||||
|
ARTIST=$(echo "$TRACK_INFO" | cut -d '-' -f 1 | sed 's/^ *//;s/ *$//')
|
||||||
|
TITLE=$(echo "$TRACK_INFO" | cut -d '-' -f 2- | sed 's/^ *//;s/ *$//')
|
||||||
|
|
||||||
|
echo "ARTIST: '$ARTIST'" >> "$LOG_FILE"
|
||||||
|
echo "TITLE Original: '$TITLE'" >> "$LOG_FILE"
|
||||||
|
|
||||||
|
# Limpiar el título de la canción
|
||||||
|
CLEAN_TITLE=$(clean_title "$TITLE")
|
||||||
|
echo "TITLE Limpio: '$CLEAN_TITLE'" >> "$LOG_FILE"
|
||||||
|
|
||||||
|
# Verificar si la pista ha cambiado
|
||||||
|
if [ "$TRACK_INFO" != "$(cat "$CURRENT_TRACK_FILE" 2>/dev/null)" ]; then
|
||||||
|
echo "$TRACK_INFO" > "$CURRENT_TRACK_FILE"
|
||||||
|
|
||||||
|
# Obtener letras usando el script Python
|
||||||
|
lyrics=$(python3 ~/.config/waybar/scripts/get_lyrics.py "$ARTIST" "$CLEAN_TITLE" 2>>"$LOG_FILE")
|
||||||
|
|
||||||
|
echo "lyrics: '$lyrics'" >> "$LOG_FILE"
|
||||||
|
|
||||||
|
if [[ "$lyrics" == "Letras no encontradas." ]]; then
|
||||||
|
lyrics="Letras no encontradas para $ARTIST - $CLEAN_TITLE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$lyrics" > "$LYRICS_FILE"
|
||||||
|
else
|
||||||
|
lyrics=$(cat "$LYRICS_FILE")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Leer el estado
|
||||||
|
if [ -f "$STATE_FILE" ]; then
|
||||||
|
STATE=$(cat "$STATE_FILE")
|
||||||
|
else
|
||||||
|
STATE="full"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ajustar las letras según el estado
|
||||||
|
if [ "$STATE" = "second_half" ]; then
|
||||||
|
total_lines=$(echo "$lyrics" | wc -l)
|
||||||
|
half_line=$(( (total_lines + 1) / 2 ))
|
||||||
|
lyrics=$(echo "$lyrics" | tail -n +$half_line)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verificar si el player activo es Firefox
|
||||||
|
PLAYER_NAME=$(playerctl -a metadata --format '{{playerName}}' 2>>"$LOG_FILE" | head -n 1)
|
||||||
|
echo "PLAYER_NAME: '$PLAYER_NAME'" >> "$LOG_FILE"
|
||||||
|
|
||||||
|
if [[ "$PLAYER_NAME" == "firefox" ]]; then
|
||||||
|
jq -c -n '{"text":"","tooltip":""}'
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generar JSON para Waybar
|
||||||
|
jq -c -n --arg text "🎵" --arg tooltip "$lyrics" '{"text":$text,"tooltip":$tooltip}'
|
||||||
|
|
18
.config/waybar/scripts/lyrics_scroll.sh
Executable file
18
.config/waybar/scripts/lyrics_scroll.sh
Executable file
@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
STATE_FILE="/tmp/lyrics_state.txt"
|
||||||
|
|
||||||
|
if [ ! -f "$STATE_FILE" ]; then
|
||||||
|
echo "second_half" > "$STATE_FILE"
|
||||||
|
else
|
||||||
|
STATE=$(cat "$STATE_FILE")
|
||||||
|
if [ "$STATE" = "second_half" ]; then
|
||||||
|
echo "full" > "$STATE_FILE"
|
||||||
|
else
|
||||||
|
echo "second_half" > "$STATE_FILE"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Send signal to Waybar to update the module immediately
|
||||||
|
pkill -SIGRTMIN+9 waybar
|
||||||
|
|
22
.config/waybar/scripts/mpd_playlist.sh
Executable file
22
.config/waybar/scripts/mpd_playlist.sh
Executable file
@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Obtener las primeras 5 canciones de la lista de reproducción actual de MPD
|
||||||
|
playlist=$(mpc playlist | head -n 5)
|
||||||
|
|
||||||
|
# Verificar si la lista de reproducción no está vacía
|
||||||
|
if [[ -z "$playlist" ]]; then
|
||||||
|
playlist="No hay canciones en la lista de reproducción."
|
||||||
|
else
|
||||||
|
total=$(mpc playlist | wc -l)
|
||||||
|
if [[ "$total" -gt 5 ]]; then
|
||||||
|
playlist="$playlist\n...y más"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Reemplazar saltos de línea por '\n' para que el tooltip los entienda
|
||||||
|
playlist=$(echo "$playlist" | sed ':a;N;$!ba;s/\n/\\n/g')
|
||||||
|
|
||||||
|
# Generar JSON compacto y seguro
|
||||||
|
jq -c -n --arg text "🎶" --arg tooltip "$playlist" '{"text":$text,"tooltip":$tooltip}'
|
||||||
|
|
||||||
|
|
24
.config/waybar/scripts/mpris_buttons.sh
Executable file
24
.config/waybar/scripts/mpris_buttons.sh
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Comprobar si hay algún reproductor activo
|
||||||
|
status=$(playerctl status 2>/dev/null)
|
||||||
|
if [[ $? -ne 0 ]]; then
|
||||||
|
echo "" # No mostrar nada si no hay reproductor
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Obtener metadatos
|
||||||
|
artist=$(playerctl metadata artist 2>/dev/null)
|
||||||
|
title=$(playerctl metadata title 2>/dev/null)
|
||||||
|
playback_status=$(playerctl status)
|
||||||
|
|
||||||
|
# Iconos según el estado
|
||||||
|
if [[ "$playback_status" == "Playing" ]]; then
|
||||||
|
play_icon=" "
|
||||||
|
else
|
||||||
|
play_icon=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Botones
|
||||||
|
echo "{\"text\": \" $play_icon \", \"tooltip\": \"$artist - $title\"}"
|
||||||
|
|
119
.config/waybar/scripts/roon_debug.js
Normal file
119
.config/waybar/scripts/roon_debug.js
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
const { RoonExtension } = require('roon-kit');
|
||||||
|
const fs = require('fs');
|
||||||
|
const { exec } = require('child_process');
|
||||||
|
|
||||||
|
const lyricsScriptPath = "/home/teraflops/.config/waybar/scripts/get_lyrics.py";
|
||||||
|
|
||||||
|
function log(msg) {
|
||||||
|
const timestamp = new Date().toISOString();
|
||||||
|
const fullMsg = `[${timestamp}] ${msg}`;
|
||||||
|
console.log(fullMsg);
|
||||||
|
fs.appendFileSync('/tmp/roon_debug.log', fullMsg + '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
const extension = new RoonExtension({
|
||||||
|
description: {
|
||||||
|
extension_id: 'roon-kit-now-playing',
|
||||||
|
display_name: "Roon Kit Now Playing",
|
||||||
|
display_version: "0.2.0",
|
||||||
|
publisher: 'roon-kit',
|
||||||
|
email: 'stevenic@microsoft.com',
|
||||||
|
website: 'https://github.com/Stevenic/roon-kit'
|
||||||
|
},
|
||||||
|
RoonApiBrowse: 'not_required',
|
||||||
|
RoonApiImage: 'required',
|
||||||
|
RoonApiTransport: 'required',
|
||||||
|
subscribe_outputs: false,
|
||||||
|
subscribe_zones: true,
|
||||||
|
log_level: 'none'
|
||||||
|
});
|
||||||
|
|
||||||
|
function cleanTitle(title) {
|
||||||
|
return title.replace(/^[A-Za-z0-9]{1,3}\s+/, '').replace(/[$#@!%^&*(){}\[\]<>?/\\|`~]/g, '').trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLyrics(artist, title, callback) {
|
||||||
|
const cmd = `python3 ${lyricsScriptPath} "${artist}" "${title}"`;
|
||||||
|
log(`🎤 Ejecutando: ${cmd}`);
|
||||||
|
exec(cmd, (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
log(`❌ Error en getLyrics: ${stderr}`);
|
||||||
|
callback("Letras no disponibles.");
|
||||||
|
} else {
|
||||||
|
const lyrics = stdout.trim();
|
||||||
|
log(`✅ Letras obtenidas (${lyrics.length} chars)`);
|
||||||
|
callback(lyrics || "Letras no encontradas.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
extension.on("subscribe_zones", async (core, response, body) => {
|
||||||
|
log("🔄 Evento subscribe_zones recibido");
|
||||||
|
log(JSON.stringify(body, null, 2));
|
||||||
|
|
||||||
|
const changedZones = body.zones_changed ?? [];
|
||||||
|
const addedZones = body.zones_added ?? [];
|
||||||
|
const removedZones = body.zones_removed ?? [];
|
||||||
|
|
||||||
|
if (removedZones.length > 0) {
|
||||||
|
log("🧹 Zona eliminada, limpiando /tmp/waybar_roon_info.json");
|
||||||
|
fs.writeFileSync('/tmp/waybar_roon_info.json', JSON.stringify({ text: '', tooltip: '' }));
|
||||||
|
exec("/usr/bin/pkill -RTMIN+3 waybar");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const zone of [...addedZones, ...changedZones]) {
|
||||||
|
log(`🎧 Zona: ${zone.display_name}, estado: ${zone.state}`);
|
||||||
|
if (zone.state === 'playing') {
|
||||||
|
const track = cleanTitle(zone.now_playing?.one_line?.line1 || '');
|
||||||
|
const artist = zone.now_playing?.one_line?.line2 || '';
|
||||||
|
const album = zone.now_playing?.three_line?.line3 || '';
|
||||||
|
|
||||||
|
log(`🎵 Reproduciendo: ${track} - ${artist} (${album})`);
|
||||||
|
|
||||||
|
getLyrics(artist, track, async (lyrics) => {
|
||||||
|
const displayText = `${track} - ${artist}`;
|
||||||
|
const tooltip = `🎵 ${track}\n👤 ${artist}\n💿 ${album}\n\n${lyrics}`;
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
text: displayText,
|
||||||
|
tooltip: tooltip
|
||||||
|
};
|
||||||
|
|
||||||
|
log(`✍️ Escribiendo JSON para Waybar: ${JSON.stringify(data)}`);
|
||||||
|
fs.writeFileSync('/tmp/waybar_roon_info.json', JSON.stringify(data));
|
||||||
|
exec("/usr/bin/pkill -RTMIN+3 waybar");
|
||||||
|
|
||||||
|
const image_key = zone.now_playing?.image_key;
|
||||||
|
if (image_key) {
|
||||||
|
try {
|
||||||
|
const imageData = await core.services.RoonApiImage.get_image(image_key, { width: 300, height: 300 });
|
||||||
|
const coverPath = '/tmp/roon_album_cover.jpg';
|
||||||
|
fs.writeFileSync(coverPath, imageData.image);
|
||||||
|
exec(`notify-send -i ${coverPath} "Now Playing" "${displayText}"`);
|
||||||
|
log("✅ Notificación enviada con carátula");
|
||||||
|
} catch (error) {
|
||||||
|
log(`⚠️ Error obteniendo imagen: ${error}`);
|
||||||
|
exec(`notify-send "Now Playing" "${displayText}"`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
exec(`notify-send "Now Playing" "${displayText}"`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
extension.start_discovery();
|
||||||
|
extension.set_status('Esperando conexión al Core de Roon...');
|
||||||
|
log("🔍 Buscando Core...");
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const core = await extension.get_core();
|
||||||
|
if (core) {
|
||||||
|
log("✅ Core emparejado correctamente");
|
||||||
|
extension.set_status('Emparejado con el Core de Roon');
|
||||||
|
} else {
|
||||||
|
log("❌ No se emparejó con ningún Core");
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
12
.config/waybar/scripts/roon_track.sh
Executable file
12
.config/waybar/scripts/roon_track.sh
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Script para Waybar que muestra la canción actual de Roon
|
||||||
|
|
||||||
|
# Variables de configuración
|
||||||
|
PYTHON_SCRIPT="/home/teraflops/.config/waybar/scripts/get_current_track.py"
|
||||||
|
|
||||||
|
# Ejecuta el script de Python y captura la salida
|
||||||
|
TRACK_INFO=$(python3 "$PYTHON_SCRIPT")
|
||||||
|
|
||||||
|
# Imprime el nombre de la canción y el artista
|
||||||
|
echo "$TRACK_INFO"
|
||||||
|
|
41
.config/waybar/scripts/roon_track_writer.js
Normal file
41
.config/waybar/scripts/roon_track_writer.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
const { RoonExtension } = require('roon-kit');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const outputTrackFile = "/tmp/roon_track.txt";
|
||||||
|
|
||||||
|
const extension = new RoonExtension({
|
||||||
|
description: {
|
||||||
|
extension_id: 'roon-track-writer',
|
||||||
|
display_name: "Roon Track Writer",
|
||||||
|
display_version: "0.1.0",
|
||||||
|
publisher: 'teraflops',
|
||||||
|
email: 'teraflops@example.com'
|
||||||
|
},
|
||||||
|
RoonApiTransport: 'required',
|
||||||
|
subscribe_zones: true
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
extension.on("subscribe_zones", async (core, response, body) => {
|
||||||
|
const changedZones = body.zones_changed ?? [];
|
||||||
|
const addedZones = body.zones_added ?? [];
|
||||||
|
|
||||||
|
for (const zone of [...changedZones, ...addedZones]) {
|
||||||
|
if (zone.state === 'playing') {
|
||||||
|
const artist = zone.now_playing?.one_line?.line2 || '';
|
||||||
|
const track = zone.now_playing?.one_line?.line1 || '';
|
||||||
|
const line = `${artist} - ${track}`;
|
||||||
|
fs.writeFileSync(outputTrackFile, line);
|
||||||
|
console.log(`🎵 Track escrito: ${line}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
extension.start_discovery();
|
||||||
|
extension.set_status("Esperando conexión con Roon...");
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const core = await extension.get_core();
|
||||||
|
extension.set_status("Emparejado con el Core de Roon");
|
||||||
|
})();
|
||||||
|
|
194
.config/waybar/scripts/show_cover.py
Executable file
194
.config/waybar/scripts/show_cover.py
Executable file
@ -0,0 +1,194 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import threading
|
||||||
|
from pathlib import Path
|
||||||
|
import asyncio
|
||||||
|
from lastfm_cover_downloader import download_cover_from_lastfm, cover_exists, get_local_album_dir
|
||||||
|
from dbus_next.aio import MessageBus
|
||||||
|
from dbus_next import Variant
|
||||||
|
from gi.repository import Gtk, GdkPixbuf, GLib
|
||||||
|
import os
|
||||||
|
import gi
|
||||||
|
gi.require_version("Gtk", "4.0")
|
||||||
|
|
||||||
|
os.environ["GDK_BACKEND"] = "x11" # O "wayland" si usas Wayland
|
||||||
|
|
||||||
|
|
||||||
|
class CoverPanel:
|
||||||
|
def __init__(self, music_base_path):
|
||||||
|
self.music_base_path = Path(music_base_path)
|
||||||
|
self.window = Gtk.Window(title="Album Cover")
|
||||||
|
self.window.set_default_size(300, 300)
|
||||||
|
self.window.set_resizable(False)
|
||||||
|
self.window.set_decorated(False)
|
||||||
|
self.window.set_opacity(0.8)
|
||||||
|
|
||||||
|
# Crear el widget Gtk.Picture para mostrar y escalar la imagen
|
||||||
|
self.picture = Gtk.Picture()
|
||||||
|
self.window.set_child(self.picture)
|
||||||
|
|
||||||
|
self.window.present()
|
||||||
|
print("CoverPanel window initialized successfully.")
|
||||||
|
|
||||||
|
def update_cover(self, cover_path):
|
||||||
|
print(f"Attempting to update cover with path: {cover_path}")
|
||||||
|
if cover_path and cover_path.exists():
|
||||||
|
print(f"Updating cover with path: {cover_path.resolve()}")
|
||||||
|
try:
|
||||||
|
# Configurar Gtk.Picture para mostrar la imagen y escalarla automáticamente
|
||||||
|
pixbuf = GdkPixbuf.Pixbuf.new_from_file(str(cover_path))
|
||||||
|
GLib.idle_add(self.picture.set_paintable,
|
||||||
|
Gtk.Picture.new_for_pixbuf(pixbuf).get_paintable())
|
||||||
|
print(f"Cover panel updated with: {cover_path.resolve()}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error loading image: {e}")
|
||||||
|
else:
|
||||||
|
print(f"Cover path does not exist or is invalid: {cover_path}")
|
||||||
|
|
||||||
|
def load_initial_cover(self):
|
||||||
|
"""Busca y carga la última carátula disponible en la biblioteca."""
|
||||||
|
cover_path = self.find_latest_cover()
|
||||||
|
if cover_path:
|
||||||
|
self.update_cover(cover_path)
|
||||||
|
|
||||||
|
def find_latest_cover(self):
|
||||||
|
"""Busca la carátula más reciente en la biblioteca de música."""
|
||||||
|
for album_dir in sorted(self.music_base_path.glob("*/"), reverse=True):
|
||||||
|
if cover_exists(album_dir):
|
||||||
|
return album_dir / "cover.jpg"
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class MprisCoverHandlerWithGTK:
|
||||||
|
def __init__(self, music_base_path):
|
||||||
|
print("Inicializando MprisCoverHandlerWithGTK.")
|
||||||
|
self.music_base_path = music_base_path
|
||||||
|
self.panel = CoverPanel(music_base_path)
|
||||||
|
self.bus = None
|
||||||
|
|
||||||
|
async def load_current_song_cover(self):
|
||||||
|
"""Intenta cargar la carátula de la canción actual usando MPRIS."""
|
||||||
|
try:
|
||||||
|
self.bus = await MessageBus().connect()
|
||||||
|
introspectable = await self.bus.introspect("org.mpris.MediaPlayer2.mpd", "/org/mpris/MediaPlayer2")
|
||||||
|
obj = self.bus.get_proxy_object(
|
||||||
|
"org.mpris.MediaPlayer2.mpd", "/org/mpris/MediaPlayer2", introspectable)
|
||||||
|
properties = obj.get_interface("org.freedesktop.DBus.Properties")
|
||||||
|
metadata_variant = await properties.call_get("org.mpris.MediaPlayer2.Player", "Metadata")
|
||||||
|
metadata = metadata_variant.value # Obtener el valor real del Variant
|
||||||
|
|
||||||
|
track_title = metadata.get('xesam:title', Variant('s', '')).value
|
||||||
|
artist = metadata.get('xesam:artist', Variant('as', [])).value
|
||||||
|
album = metadata.get('xesam:album', Variant('s', '')).value
|
||||||
|
xesam_url = metadata.get('xesam:url', Variant('s', '')).value
|
||||||
|
|
||||||
|
print(f"Metadata recibida: Track Title: {track_title}, Artist: {
|
||||||
|
artist}, Album: {album}, URL: {xesam_url}")
|
||||||
|
|
||||||
|
if not isinstance(artist, list):
|
||||||
|
artist = [str(artist)]
|
||||||
|
artist = [str(a) for a in artist]
|
||||||
|
|
||||||
|
if artist and album != 'Unknown' and xesam_url:
|
||||||
|
album_dir = get_local_album_dir(
|
||||||
|
xesam_url, self.music_base_path)
|
||||||
|
print(f"Determined album directory: {album_dir}")
|
||||||
|
|
||||||
|
if album_dir and cover_exists(album_dir):
|
||||||
|
cover_path = album_dir / "cover.jpg"
|
||||||
|
self.panel.update_cover(cover_path)
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error loading current song cover: {e}")
|
||||||
|
|
||||||
|
# Si no se encuentra la carátula de la canción actual, cargar la última carátula encontrada
|
||||||
|
print("No current song cover found. Loading last known cover.")
|
||||||
|
self.panel.load_initial_cover()
|
||||||
|
|
||||||
|
async def on_properties_changed(self, interface, changed, invalidated):
|
||||||
|
print("on_properties_changed: Detectando cambios de propiedades MPRIS.")
|
||||||
|
if 'Metadata' in changed:
|
||||||
|
metadata = changed['Metadata'].value
|
||||||
|
|
||||||
|
# Extraer los valores reales de cada Variant
|
||||||
|
track_title = metadata.get('xesam:title', Variant('s', '')).value
|
||||||
|
artist = metadata.get('xesam:artist', Variant('as', [])).value
|
||||||
|
album = metadata.get('xesam:album', Variant('s', '')).value
|
||||||
|
xesam_url = metadata.get('xesam:url', Variant('s', '')).value
|
||||||
|
|
||||||
|
print(f"Metadata recibida: Track Title: {track_title}, Artist: {
|
||||||
|
artist}, Album: {album}, URL: {xesam_url}")
|
||||||
|
|
||||||
|
if not isinstance(artist, list):
|
||||||
|
artist = [str(artist)]
|
||||||
|
artist = [str(a) for a in artist]
|
||||||
|
|
||||||
|
if not artist or album == 'Unknown' or not xesam_url:
|
||||||
|
print(f"Skipping track '{
|
||||||
|
track_title}' due to missing metadata.")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"Now playing: {track_title} by {
|
||||||
|
', '.join(artist)} from the album '{album}'")
|
||||||
|
album_dir = get_local_album_dir(xesam_url, self.music_base_path)
|
||||||
|
print(f"Determined album directory: {album_dir}")
|
||||||
|
|
||||||
|
if not album_dir:
|
||||||
|
print(f"Could not determine album directory for '{
|
||||||
|
track_title}'. xesam:url: {xesam_url}")
|
||||||
|
return
|
||||||
|
|
||||||
|
if cover_exists(album_dir):
|
||||||
|
print(f"Cover already exists in '{
|
||||||
|
album_dir}', loading from local.")
|
||||||
|
cover_path = album_dir / "cover.jpg"
|
||||||
|
self.panel.update_cover(cover_path)
|
||||||
|
else:
|
||||||
|
print(f"No cover found in '{
|
||||||
|
album_dir}', attempting to download.")
|
||||||
|
try:
|
||||||
|
cover_path = download_cover_from_lastfm(
|
||||||
|
artist[0], album, album_dir)
|
||||||
|
if cover_path:
|
||||||
|
self.panel.update_cover(cover_path)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error downloading cover: {e}")
|
||||||
|
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
os.system("/usr/bin/pkill -RTMIN+23 waybar")
|
||||||
|
|
||||||
|
async def main(self):
|
||||||
|
print("Starting D-Bus listener with GTK panel...")
|
||||||
|
# Cargar la carátula de la canción actual al inicio
|
||||||
|
await self.load_current_song_cover()
|
||||||
|
|
||||||
|
introspectable = await self.bus.introspect("org.mpris.MediaPlayer2.mpd", "/org/mpris/MediaPlayer2")
|
||||||
|
obj = self.bus.get_proxy_object(
|
||||||
|
"org.mpris.MediaPlayer2.mpd", "/org/mpris/MediaPlayer2", introspectable)
|
||||||
|
properties = obj.get_interface("org.freedesktop.DBus.Properties")
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
os.system("/usr/bin/pkill -RTMIN+23 waybar")
|
||||||
|
|
||||||
|
properties.on_properties_changed(self.on_properties_changed)
|
||||||
|
print("D-Bus listener with GTK panel started successfully.")
|
||||||
|
await asyncio.Future()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("Iniciando script show_cover.py")
|
||||||
|
MUSIC_BASE_PATH = os.environ.get(
|
||||||
|
'MUSIC_LIBRARY_PATH', os.path.expandvars('$HOME/media/all'))
|
||||||
|
print(f"MUSIC_BASE_PATH: {MUSIC_BASE_PATH}")
|
||||||
|
|
||||||
|
fetcher = MprisCoverHandlerWithGTK(MUSIC_BASE_PATH)
|
||||||
|
|
||||||
|
# Ejecutar el ciclo de asyncio en un hilo separado
|
||||||
|
def run_asyncio():
|
||||||
|
asyncio.run(fetcher.main())
|
||||||
|
|
||||||
|
asyncio_thread = threading.Thread(target=run_asyncio)
|
||||||
|
asyncio_thread.start()
|
||||||
|
|
||||||
|
# Ejecutar el ciclo de GTK en el hilo principal
|
||||||
|
loop = GLib.MainLoop()
|
||||||
|
loop.run()
|
76
.config/waybar/scripts/temps.sh
Executable file
76
.config/waybar/scripts/temps.sh
Executable file
@ -0,0 +1,76 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
STATE_FILE="/tmp/waybar_temp_mode"
|
||||||
|
MODES=("cpu" "igpu" "dgpu")
|
||||||
|
|
||||||
|
# Detecta si la dGPU está realmente activa
|
||||||
|
dgpu_is_active() {
|
||||||
|
runtime_status=$(cat /sys/bus/pci/devices/0000:01:00.0/power/runtime_status 2>/dev/null)
|
||||||
|
[[ "$runtime_status" == "active" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
get_dgpu_temp() {
|
||||||
|
if dgpu_is_active; then
|
||||||
|
nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader,nounits 2>/dev/null || echo "N/A"
|
||||||
|
else
|
||||||
|
echo "N/A"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
get_cpu_temp() {
|
||||||
|
sensors | awk '/^Tctl:/ {print $2}' | sed 's/+//;s/°C//'
|
||||||
|
}
|
||||||
|
|
||||||
|
get_igpu_temp() {
|
||||||
|
sensors | awk '/amdgpu/ {found=1} found && /edge:/ {print $2; exit}' | sed 's/+//;s/°C//'
|
||||||
|
}
|
||||||
|
|
||||||
|
get_nvme_temp() {
|
||||||
|
sensors | awk '/nvme/ {found=1} found && /Composite:/ {print $2; exit}' | sed 's/+//;s/°C//'
|
||||||
|
}
|
||||||
|
|
||||||
|
get_board_temp() {
|
||||||
|
sensors | awk '/temp1:/ && /°C/ {print $2; exit}' | sed 's/+//;s/°C//'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Leer estado actual
|
||||||
|
current_mode="cpu"
|
||||||
|
[[ -f "$STATE_FILE" ]] && current_mode=$(<"$STATE_FILE")
|
||||||
|
|
||||||
|
# Cambiar modo si se hizo clic
|
||||||
|
if [[ "$1" == "click" ]]; then
|
||||||
|
for i in "${!MODES[@]}"; do
|
||||||
|
if [[ "${MODES[$i]}" == "$current_mode" ]]; then
|
||||||
|
next_index=$(( (i + 1) % ${#MODES[@]} ))
|
||||||
|
echo "${MODES[$next_index]}" > "$STATE_FILE"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Obtener temperaturas
|
||||||
|
cpu=$(get_cpu_temp)
|
||||||
|
igpu=$(get_igpu_temp)
|
||||||
|
dgpu=$(get_dgpu_temp)
|
||||||
|
nvme=$(get_nvme_temp)
|
||||||
|
board=$(get_board_temp)
|
||||||
|
|
||||||
|
cpu=${cpu:-N/A}
|
||||||
|
igpu=${igpu:-N/A}
|
||||||
|
dgpu=${dgpu:-N/A}
|
||||||
|
nvme=${nvme:-N/A}
|
||||||
|
board=${board:-N/A}
|
||||||
|
|
||||||
|
# Definir ícono y valor visible
|
||||||
|
case "$current_mode" in
|
||||||
|
cpu) icon="CPU"; temp="$cpu" ;;
|
||||||
|
igpu) icon="iGPU"; temp="$igpu" ;;
|
||||||
|
dgpu) icon="dGPU"; temp="$dgpu" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Tooltip con \n y escapado correcto
|
||||||
|
tooltip=$(printf "CPU: %s°C\niGPU: %s°C\ndGPU: %s°C\nNVMe: %s°C\nBoard: %s°C" "$cpu" "$igpu" "$dgpu" "$nvme" "$board")
|
||||||
|
tooltip_escaped=$(echo "$tooltip" | jq -Rs .)
|
||||||
|
|
||||||
|
# Salida final
|
||||||
|
echo "{\"text\": \"$icon: ${temp}°C\", \"tooltip\": ${tooltip_escaped}}"
|
95
.config/waybar/scripts/temps.sh_solodedicada
Executable file
95
.config/waybar/scripts/temps.sh_solodedicada
Executable file
@ -0,0 +1,95 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
STATE_FILE="/tmp/waybar_temp_mode"
|
||||||
|
MODES=("cpu" "igpu" "dgpu")
|
||||||
|
|
||||||
|
# Detecta si la dGPU está realmente activa
|
||||||
|
dgpu_is_active() {
|
||||||
|
runtime_status=$(cat /sys/bus/pci/devices/0000:01:00.0/power/runtime_status 2>/dev/null)
|
||||||
|
[[ "$runtime_status" == "active" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
get_dgpu_temp() {
|
||||||
|
gpu_mode_file="/tmp/gpu_mode"
|
||||||
|
timestamp_file="/tmp/gpu_mode_timestamp"
|
||||||
|
|
||||||
|
echo_na() {
|
||||||
|
echo "N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
[[ ! -f "$gpu_mode_file" || ! -f "$timestamp_file" ]] && echo_na && return
|
||||||
|
|
||||||
|
mode=$(< "$gpu_mode_file")
|
||||||
|
last_change=$(< "$timestamp_file")
|
||||||
|
now=$(date +%s)
|
||||||
|
diff=$((now - last_change))
|
||||||
|
|
||||||
|
# Solo permitir nvidia-smi si:
|
||||||
|
# 1. El modo es nvidia
|
||||||
|
# 2. Han pasado más de 5s
|
||||||
|
# 3. La dGPU está realmente activa
|
||||||
|
if [[ "$mode" == "nvidia" && "$diff" -gt 5 && $(dgpu_is_active) ]]; then
|
||||||
|
nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader,nounits 2>/dev/null
|
||||||
|
else
|
||||||
|
echo_na
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
get_cpu_temp() {
|
||||||
|
sensors | awk '/^Tctl:/ {print $2}' | sed 's/+//;s/°C//'
|
||||||
|
}
|
||||||
|
|
||||||
|
get_igpu_temp() {
|
||||||
|
sensors | awk '/amdgpu/ {found=1} found && /edge:/ {print $2; exit}' | sed 's/+//;s/°C//'
|
||||||
|
}
|
||||||
|
|
||||||
|
get_nvme_temp() {
|
||||||
|
sensors | awk '/nvme/ {found=1} found && /Composite:/ {print $2; exit}' | sed 's/+//;s/°C//'
|
||||||
|
}
|
||||||
|
|
||||||
|
get_board_temp() {
|
||||||
|
sensors | awk '/temp1:/ && /°C/ {print $2; exit}' | sed 's/+//;s/°C//'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Leer estado actual
|
||||||
|
current_mode="cpu"
|
||||||
|
[[ -f "$STATE_FILE" ]] && current_mode=$(<"$STATE_FILE")
|
||||||
|
|
||||||
|
# Cambiar modo si se hizo clic
|
||||||
|
if [[ "$1" == "click" ]]; then
|
||||||
|
for i in "${!MODES[@]}"; do
|
||||||
|
if [[ "${MODES[$i]}" == "$current_mode" ]]; then
|
||||||
|
next_index=$(( (i + 1) % ${#MODES[@]} ))
|
||||||
|
echo "${MODES[$next_index]}" > "$STATE_FILE"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Obtener temperaturas
|
||||||
|
cpu=$(get_cpu_temp)
|
||||||
|
igpu=$(get_igpu_temp)
|
||||||
|
dgpu=$(get_dgpu_temp)
|
||||||
|
nvme=$(get_nvme_temp)
|
||||||
|
board=$(get_board_temp)
|
||||||
|
|
||||||
|
cpu=${cpu:-N/A}
|
||||||
|
igpu=${igpu:-N/A}
|
||||||
|
dgpu=${dgpu:-N/A}
|
||||||
|
nvme=${nvme:-N/A}
|
||||||
|
board=${board:-N/A}
|
||||||
|
|
||||||
|
# Definir ícono y valor visible
|
||||||
|
case "$current_mode" in
|
||||||
|
cpu) icon="CPU"; temp="$cpu" ;;
|
||||||
|
igpu) icon="iGPU"; temp="$igpu" ;;
|
||||||
|
dgpu) icon="dGPU"; temp="$dgpu" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Tooltip con \n y escapado correcto
|
||||||
|
tooltip=$(printf "CPU: %s°C\niGPU: %s°C\ndGPU: %s°C\nNVMe: %s°C\nBoard: %s°C" "$cpu" "$igpu" "$dgpu" "$nvme" "$board")
|
||||||
|
tooltip_escaped=$(echo "$tooltip" | jq -Rs .)
|
||||||
|
|
||||||
|
# Salida final
|
||||||
|
echo "{\"text\": \"$icon: ${temp}°C\", \"tooltip\": ${tooltip_escaped}}"
|
||||||
|
|
1
.config/waybar/scripts/tiling.py
Normal file
1
.config/waybar/scripts/tiling.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
22
.config/waybar/scripts/toggle_dmp_widget.sh
Executable file
22
.config/waybar/scripts/toggle_dmp_widget.sh
Executable file
@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
PIDFILE="/tmp/dmp_widget.pid"
|
||||||
|
|
||||||
|
if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then
|
||||||
|
kill "$(cat "$PIDFILE")"
|
||||||
|
rm -f "$PIDFILE"
|
||||||
|
else
|
||||||
|
yad --title="DMP-A6 Info" \
|
||||||
|
--window-icon=audio-x-generic \
|
||||||
|
--on-top --skip-taskbar \
|
||||||
|
--undecorated \
|
||||||
|
--geometry=500x300+40+40 \
|
||||||
|
--no-buttons \
|
||||||
|
--image="/tmp/dmp_cover.jpg" \
|
||||||
|
--text="$(~/.config/eww/scripts/get_dmp_state.sh | jq -r '.title + "\n" + .artist + "\n" + .info')" \
|
||||||
|
--text-align=center \
|
||||||
|
--fontname="JetBrains Mono 11" \
|
||||||
|
--borders=12 &
|
||||||
|
echo $! > "$PIDFILE"
|
||||||
|
fi
|
||||||
|
|
15
.config/waybar/scripts/toggle_fans.sh
Executable file
15
.config/waybar/scripts/toggle_fans.sh
Executable file
@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Comprobamos el estado actual leyendo la línea de CPU en "-g"
|
||||||
|
status=$(asusctl fan-curve -g 2>/dev/null | grep "^CPU: enabled:")
|
||||||
|
|
||||||
|
# Si se encuentra "true" en esa línea, significa que las fan-curves están activas
|
||||||
|
# (asumimos que CPU/GPU/MID van sincronizados para el perfil Balanced).
|
||||||
|
if echo "$status" | grep -q "true"; then
|
||||||
|
echo "Fan-curve actualmente habilitado -> Lo deshabilitamos"
|
||||||
|
asusctl fan-curve -m Balanced -e false
|
||||||
|
else
|
||||||
|
echo "Fan-curve actualmente deshabilitado -> Lo habilitamos"
|
||||||
|
asusctl fan-curve -m Balanced -e true
|
||||||
|
fi
|
||||||
|
|
1
.config/waybar/scripts/up.txt
Normal file
1
.config/waybar/scripts/up.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
⬆ 525.94 Mbps
|
70
.config/waybar/scripts/updates.sh
Executable file
70
.config/waybar/scripts/updates.sh
Executable file
@ -0,0 +1,70 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
SYNC_INTERVAL=1800
|
||||||
|
SYNC_TIMESTAMP="/tmp/last_checkupdates_sync"
|
||||||
|
NO_ZERO_OUTPUT=false
|
||||||
|
|
||||||
|
now=$(date +%s)
|
||||||
|
last=$(cat "$SYNC_TIMESTAMP" 2>/dev/null || echo 0)
|
||||||
|
if (( now - last >= SYNC_INTERVAL )); then
|
||||||
|
rm -rf "/tmp/checkup-db-$UID"
|
||||||
|
checkupdates > /dev/null 2>&1
|
||||||
|
echo "$now" > "$SYNC_TIMESTAMP"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Mapping: REPOS destino de cada paquete
|
||||||
|
declare -A update_repo_map
|
||||||
|
mapping=$(sudo pacman -Syuw --print-format '%r %n' --noconfirm | grep -v '^::' | grep -v '^descargando')
|
||||||
|
while read -r repo pkg; do
|
||||||
|
[[ -z "$repo" || -z "$pkg" ]] && continue
|
||||||
|
update_repo_map["$pkg"]="$repo"
|
||||||
|
done <<< "$mapping"
|
||||||
|
|
||||||
|
updates=$(checkupdates --nosync 2>/dev/null)
|
||||||
|
aur_updates=$(paru -Qua 2>/dev/null)
|
||||||
|
|
||||||
|
total_count=0
|
||||||
|
tooltip_lines=()
|
||||||
|
|
||||||
|
# OJO: este while debe ser así, ¡NUNCA uses tuberías aquí!
|
||||||
|
IFS=$'\n'
|
||||||
|
for line in $updates; do
|
||||||
|
[[ -z "$line" ]] && continue
|
||||||
|
pkg=$(awk '{print $1}' <<< "$line")
|
||||||
|
old=$(awk '{print $2}' <<< "$line")
|
||||||
|
new=$(awk '{print $4}' <<< "$line")
|
||||||
|
repo="${update_repo_map[$pkg]}"
|
||||||
|
[[ -z "$repo" ]] && repo="???"
|
||||||
|
formatted=$(printf "%-15s %-30s %s → %s" "$repo" "$pkg" "$old" "$new")
|
||||||
|
tooltip_lines+=("$formatted")
|
||||||
|
((total_count++))
|
||||||
|
done
|
||||||
|
|
||||||
|
# AUR
|
||||||
|
for line in $aur_updates; do
|
||||||
|
[[ -z "$line" ]] && continue
|
||||||
|
pkg=$(awk '{print $1}' <<< "$line")
|
||||||
|
old=$(awk '{print $2}' <<< "$line")
|
||||||
|
new=$(awk '{print $4}' <<< "$line")
|
||||||
|
formatted=$(printf "%-15s %-30s %s → %s" "aur" "$pkg" "$old" "$new")
|
||||||
|
tooltip_lines+=("$formatted")
|
||||||
|
((total_count++))
|
||||||
|
done
|
||||||
|
|
||||||
|
aur_rebuild=$(cat /tmp/aur_rebuild_count 2>/dev/null || echo 0)
|
||||||
|
if [[ "$aur_rebuild" -gt 0 ]]; then
|
||||||
|
tooltip_lines+=("")
|
||||||
|
tooltip_lines+=("️ $aur_rebuild paquetes AUR necesitan recompilación")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$total_count" -eq 0 && "$aur_rebuild" -eq 0 ]]; then
|
||||||
|
if [[ "$NO_ZERO_OUTPUT" == true ]]; then
|
||||||
|
echo '{"text": "", "tooltip": "Todo actualizado", "class": "updated", "alt": "updated"}'
|
||||||
|
else
|
||||||
|
echo '{"text": "0", "tooltip": "Todo actualizado", "class": "updated", "alt": "updated"}'
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
tooltip=$(printf "%s\n" "${tooltip_lines[@]}" | jq -R -s '.')
|
||||||
|
echo "{\"text\": \"$total_count\", \"tooltip\": $tooltip, \"class\": \"has-updates\", \"alt\": \"has-updates\"}"
|
||||||
|
fi
|
||||||
|
|
5
.config/waybar/scripts/view_battery_details.sh
Executable file
5
.config/waybar/scripts/view_battery_details.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
DETAILS=$(upower -i /org/freedesktop/UPower/devices/battery_BAT1 | grep -E "state|percentage|energy|energy-full|energy-rate|voltage")
|
||||||
|
echo "$DETAILS" | zenity --text-info --title="Detalles de la Batería" --width=400 --height=300
|
||||||
|
|
||||||
|
|
4
.config/waybar/scripts/view_battery_details_yad.sh
Executable file
4
.config/waybar/scripts/view_battery_details_yad.sh
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
DETAILS=$(upower -i /org/freedesktop/UPower/devices/battery_BAT1 | grep -E "state|percentage|energy|energy-full|energy-rate|voltage")
|
||||||
|
echo "$DETAILS" | yad --text-info --title="Detalles de la Batería" --width=400 --height=300
|
||||||
|
|
63
.config/waybar/scripts/volume_menu.py
Executable file
63
.config/waybar/scripts/volume_menu.py
Executable file
@ -0,0 +1,63 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from gi.repository import AppIndicator3
|
||||||
|
from gi.repository import Gtk, GLib, GObject
|
||||||
|
import gi
|
||||||
|
import subprocess
|
||||||
|
import psutil
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
gi.require_version("Gtk", "3.0")
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeSlider(Gtk.Window):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(title="Control de Volumen")
|
||||||
|
self.set_default_size(50, 300) # Tamaño de la ventana ajustado
|
||||||
|
|
||||||
|
# Crear el control deslizante vertical
|
||||||
|
self.slider = Gtk.Scale(orientation=Gtk.Orientation.VERTICAL)
|
||||||
|
self.slider.set_range(0, 100) # Rango de 0 a 100 para el volumen
|
||||||
|
self.slider.set_value(self.get_current_volume())
|
||||||
|
# Invertir para que vaya de abajo hacia arriba
|
||||||
|
self.slider.set_inverted(True)
|
||||||
|
self.slider.connect("value-changed", self.on_volume_changed)
|
||||||
|
|
||||||
|
# Ajustar el ancho del control deslizante
|
||||||
|
self.slider.set_size_request(30, -1) # Establece el ancho a 30 píxeles
|
||||||
|
|
||||||
|
# Añadir el control deslizante a la ventana
|
||||||
|
self.add(self.slider)
|
||||||
|
|
||||||
|
def get_current_volume(self):
|
||||||
|
"""Obtiene el volumen actual usando pamixer"""
|
||||||
|
result = subprocess.run(
|
||||||
|
["pamixer", "--get-volume"], capture_output=True, text=True)
|
||||||
|
# Default al 50%
|
||||||
|
return int(result.stdout.strip()) if result.returncode == 0 else 50
|
||||||
|
|
||||||
|
def on_volume_changed(self, slider):
|
||||||
|
"""Ajusta el volumen según la posición del control deslizante"""
|
||||||
|
volume = int(slider.get_value())
|
||||||
|
subprocess.run(["pamixer", "--set-volume", str(volume)])
|
||||||
|
|
||||||
|
|
||||||
|
def is_another_instance_running():
|
||||||
|
"""Comprueba si hay otra instancia de este script en ejecución"""
|
||||||
|
current_pid = os.getpid()
|
||||||
|
for process in psutil.process_iter(attrs=['pid', 'cmdline']):
|
||||||
|
if process.info['pid'] != current_pid and 'volume_slider.py' in process.info['cmdline']:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if is_another_instance_running():
|
||||||
|
print("Ya hay una instancia ejecutándose. Finalizando esta.")
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
# Ejecutar la aplicación
|
||||||
|
win = VolumeSlider()
|
||||||
|
win.connect("destroy", Gtk.main_quit)
|
||||||
|
win.show_all()
|
||||||
|
Gtk.main()
|
229
.config/waybar/scripts/weather.py
Executable file
229
.config/waybar/scripts/weather.py
Executable file
@ -0,0 +1,229 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import gi
|
||||||
|
import requests
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
gi.require_version('Geoclue', '2.0')
|
||||||
|
from gi.repository import Geoclue
|
||||||
|
|
||||||
|
# ─────────── ICONOS DEL CLIMA ─────────── #
|
||||||
|
weather_icons = {
|
||||||
|
"01d": "", "01n": "", "02d": "", "02n": "",
|
||||||
|
"03d": "", "03n": "", "04d": "", "04n": "",
|
||||||
|
"09d": "", "09n": "", "10d": "", "10n": "",
|
||||||
|
"11d": "", "11n": "", "13d": "", "13n": "",
|
||||||
|
"50d": "", "50n": "", "default": ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─────────── API KEYS ─────────── #
|
||||||
|
try:
|
||||||
|
with open('/home/teraflops/apikeys/openweathermaps', 'r') as f:
|
||||||
|
api_key = f.read().strip()
|
||||||
|
with open('/home/teraflops/apikeys/google_geoip', 'r') as f:
|
||||||
|
google_key = f.read().strip()
|
||||||
|
except Exception:
|
||||||
|
print(json.dumps({"text": "Error al leer las API keys"}))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# ─────────── OBTENER UBICACIÓN ─────────── #
|
||||||
|
def get_location_from_geoclue():
|
||||||
|
try:
|
||||||
|
clue = Geoclue.Simple.new_sync("weather-script", Geoclue.AccuracyLevel.EXACT, None)
|
||||||
|
location = clue.get_location()
|
||||||
|
lat = location.get_property('latitude')
|
||||||
|
lon = location.get_property('longitude')
|
||||||
|
desc = location.get_property('description')
|
||||||
|
acc = location.get_property('accuracy')
|
||||||
|
print(f"🛰️ GeoClue: {lat}, {lon} — Precisión: {acc:.1f} m — Fuente: {desc}")
|
||||||
|
return lat, lon, desc
|
||||||
|
except Exception as e:
|
||||||
|
print("GeoClue (GI): error al obtener ubicación:", e)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_location_from_google():
|
||||||
|
try:
|
||||||
|
url = f"https://www.googleapis.com/geolocation/v1/geolocate?key={google_key}"
|
||||||
|
response = requests.post(url, json={})
|
||||||
|
response.raise_for_status()
|
||||||
|
data = response.json()
|
||||||
|
lat = data['location']['lat']
|
||||||
|
lon = data['location']['lng']
|
||||||
|
accuracy = data.get('accuracy', 0)
|
||||||
|
print(f"🛰️ GoogleGeo: {lat}, {lon} — Precisión: {accuracy:.0f} m")
|
||||||
|
return lat, lon, f"GoogleGeo ({accuracy:.0f} m)"
|
||||||
|
except Exception as e:
|
||||||
|
print("Google Geolocation API: fallo:", e)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def reverse_geocode(lat, lon):
|
||||||
|
try:
|
||||||
|
url = "https://nominatim.openstreetmap.org/reverse"
|
||||||
|
params = {
|
||||||
|
"lat": lat,
|
||||||
|
"lon": lon,
|
||||||
|
"format": "json",
|
||||||
|
"zoom": 10,
|
||||||
|
"addressdetails": 1,
|
||||||
|
}
|
||||||
|
headers = {"User-Agent": "weather-waybar-script"}
|
||||||
|
response = requests.get(url, params=params, headers=headers)
|
||||||
|
response.raise_for_status()
|
||||||
|
data = response.json()
|
||||||
|
address = data.get("address", {})
|
||||||
|
ciudad = address.get("city") or address.get("town") or address.get("village") or "Desconocido"
|
||||||
|
pais = address.get("country", "Desconocido")
|
||||||
|
return ciudad, pais
|
||||||
|
except Exception as e:
|
||||||
|
print("Reverse geocode falló:", e)
|
||||||
|
return "Desconocido", "Desconocido"
|
||||||
|
|
||||||
|
# ─────────── ALERTA LLUVIA SOLO EN TOOLTIP ─────────── #
|
||||||
|
def check_rain_alert(forecast_data, threshold=60, max_horas=6):
|
||||||
|
ahora = datetime.utcnow()
|
||||||
|
mensaje_alerta = ""
|
||||||
|
|
||||||
|
for block in forecast_data['list']:
|
||||||
|
bloque_hora = datetime.strptime(block['dt_txt'], "%Y-%m-%d %H:%M:%S")
|
||||||
|
if bloque_hora <= ahora:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if (bloque_hora - ahora).total_seconds() > max_horas * 3600:
|
||||||
|
continue
|
||||||
|
|
||||||
|
pop = block.get('pop', 0) * 100
|
||||||
|
if pop >= threshold:
|
||||||
|
hora_local = bloque_hora.replace(tzinfo=timezone.utc).astimezone().strftime("%H:%M")
|
||||||
|
mensaje_alerta = f"⚠️ Posible lluvia a las {hora_local} ({int(pop)}%)"
|
||||||
|
break
|
||||||
|
|
||||||
|
return mensaje_alerta
|
||||||
|
|
||||||
|
# ─────────── PRONÓSTICO EXTENDIDO ─────────── #
|
||||||
|
def get_daily_forecast(forecast_data):
|
||||||
|
from collections import defaultdict
|
||||||
|
dias = defaultdict(list)
|
||||||
|
for entry in forecast_data['list']:
|
||||||
|
fecha = entry['dt_txt'].split(' ')[0]
|
||||||
|
dias[fecha].append(entry)
|
||||||
|
|
||||||
|
resumen = []
|
||||||
|
for i, (dia, bloques) in enumerate(dias.items()):
|
||||||
|
if i >= 3: break
|
||||||
|
temps = [b['main']['temp'] for b in bloques]
|
||||||
|
pops = [b.get('pop', 0) * 100 for b in bloques]
|
||||||
|
estado = bloques[0]['weather'][0]['description'].capitalize()
|
||||||
|
resumen.append(f"📅 {dia}: {estado}, {round(min(temps))}–{round(max(temps))}°C, ☔ {round(max(pops))}%")
|
||||||
|
return "\n".join(resumen)
|
||||||
|
|
||||||
|
# ─────────── UBICACIÓN ─────────── #
|
||||||
|
usar_ubicacion_manual = False # Cambia a True si quieres forzar ubicación
|
||||||
|
|
||||||
|
if usar_ubicacion_manual:
|
||||||
|
latitud, longitud = 40.4168, -3.7038 # Ejemplo: Madrid
|
||||||
|
region = "Manual"
|
||||||
|
print(f"🧪 Usando ubicación manual: {latitud}, {longitud}")
|
||||||
|
else:
|
||||||
|
loc = get_location_from_geoclue() or get_location_from_google()
|
||||||
|
if not loc:
|
||||||
|
print(json.dumps({"text": "Error al obtener la ubicación"}))
|
||||||
|
sys.exit(1)
|
||||||
|
latitud, longitud, region = loc
|
||||||
|
|
||||||
|
ciudad, pais = reverse_geocode(latitud, longitud)
|
||||||
|
|
||||||
|
# ─────────── API WEATHER ─────────── #
|
||||||
|
params = {
|
||||||
|
"lat": latitud,
|
||||||
|
"lon": longitud,
|
||||||
|
"appid": api_key,
|
||||||
|
"units": "metric",
|
||||||
|
"lang": "es",
|
||||||
|
}
|
||||||
|
|
||||||
|
weather_url = "https://api.openweathermap.org/data/2.5/weather"
|
||||||
|
response = requests.get(weather_url, params=params)
|
||||||
|
if response.status_code != 200:
|
||||||
|
print(json.dumps({"text": "Error al obtener datos del clima"}))
|
||||||
|
sys.exit(1)
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
# ─────────── DATOS CLIMA ─────────── #
|
||||||
|
temp = f"{round(data['main']['temp'])}°C"
|
||||||
|
status = data['weather'][0]['description'].capitalize()
|
||||||
|
status_code = data['weather'][0]['icon']
|
||||||
|
icon = weather_icons.get(status_code, weather_icons["default"])
|
||||||
|
temp_feel = f"{round(data['main']['feels_like'])}°C"
|
||||||
|
temp_feel_text = f"Sensación de {temp_feel}"
|
||||||
|
temp_min = f"{round(data['main']['temp_min'])}°C"
|
||||||
|
temp_max = f"{round(data['main']['temp_max'])}°C"
|
||||||
|
temp_min_max = f" {temp_min}\t\t {temp_max}"
|
||||||
|
wind_speed = f"{round(data['wind']['speed'] * 3.6)} km/h"
|
||||||
|
wind_text = f" {wind_speed}"
|
||||||
|
humidity = f"{data['main']['humidity']}%"
|
||||||
|
humidity_text = f" {humidity}"
|
||||||
|
visibility = data.get('visibility', 0)
|
||||||
|
visibility_km = f"{visibility / 1000} km"
|
||||||
|
visibility_text = f" {visibility_km}"
|
||||||
|
|
||||||
|
# ─────────── CALIDAD DEL AIRE ─────────── #
|
||||||
|
aqi_url = "https://api.openweathermap.org/data/2.5/air_pollution"
|
||||||
|
aqi_response = requests.get(aqi_url, params=params)
|
||||||
|
if aqi_response.status_code == 200:
|
||||||
|
aqi = aqi_response.json()['list'][0]['main']['aqi']
|
||||||
|
aqi_levels = {1: "Bueno", 2: "Moderado", 3: "Perjudicial", 4: "Malo", 5: "Muy malo"}
|
||||||
|
air_quality_index = aqi_levels.get(aqi, "N/A")
|
||||||
|
else:
|
||||||
|
air_quality_index = "N/A"
|
||||||
|
|
||||||
|
# ─────────── PRONÓSTICO ─────────── #
|
||||||
|
forecast_url = "https://api.openweathermap.org/data/2.5/forecast"
|
||||||
|
forecast_response = requests.get(forecast_url, params=params)
|
||||||
|
if forecast_response.status_code == 200:
|
||||||
|
forecast_data = forecast_response.json()
|
||||||
|
precipitation = forecast_data['list'][0].get('pop', 0) * 100
|
||||||
|
prediction = f"\n\n {precipitation:.0f}% probabilidad de lluvia"
|
||||||
|
alerta_lluvia = check_rain_alert(forecast_data)
|
||||||
|
pronostico_dias = get_daily_forecast(forecast_data)
|
||||||
|
else:
|
||||||
|
prediction = ""
|
||||||
|
alerta_lluvia = ""
|
||||||
|
pronostico_dias = ""
|
||||||
|
|
||||||
|
# ─────────── TOOLTIP ─────────── #
|
||||||
|
time_of_day = "Día" if status_code.endswith('d') else "Noche"
|
||||||
|
ubicacion = f"{ciudad}, {region}, {pais} ({latitud:.2f}, {longitud:.2f})"
|
||||||
|
|
||||||
|
tooltip_text = ""
|
||||||
|
if alerta_lluvia:
|
||||||
|
tooltip_text += f"{alerta_lluvia}\n\n"
|
||||||
|
|
||||||
|
tooltip_text += str.format(
|
||||||
|
"{}\n{}\n{}\n{}\n\n{}\n{}\n{}{}\n\n<small>{}</small>",
|
||||||
|
f'<span size="xx-large">{temp}</span>',
|
||||||
|
f"<big>{icon}</big>",
|
||||||
|
f"<big>{status} ({time_of_day})</big>",
|
||||||
|
f"<small>{temp_feel_text}</small>",
|
||||||
|
f"<big>{temp_min_max}</big>",
|
||||||
|
f"{wind_text}\t{humidity_text}",
|
||||||
|
f"{visibility_text}\tICA {air_quality_index}",
|
||||||
|
f"<i>{prediction}</i>",
|
||||||
|
f"Ubicación: {ubicacion}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if pronostico_dias:
|
||||||
|
tooltip_text += f"\n\n<b>Próximos días:</b>\n{pronostico_dias}"
|
||||||
|
|
||||||
|
# ─────────── OUTPUT PARA WAYBAR ─────────── #
|
||||||
|
out_data = {
|
||||||
|
"text": f"{icon} {temp}",
|
||||||
|
"alt": f"{status} ({time_of_day})",
|
||||||
|
"tooltip": tooltip_text,
|
||||||
|
"class": status_code,
|
||||||
|
}
|
||||||
|
|
||||||
|
print(json.dumps(out_data))
|
||||||
|
|
440
.config/waybar/scripts/wifi_menu.py
Executable file
440
.config/waybar/scripts/wifi_menu.py
Executable file
@ -0,0 +1,440 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import dbus
|
||||||
|
import dbus.mainloop.glib
|
||||||
|
from gi.repository import AppIndicator3
|
||||||
|
from gi.repository import Gtk, GLib, GObject
|
||||||
|
import gi
|
||||||
|
import subprocess
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
# Especificar las versiones antes de importar
|
||||||
|
gi.require_version('Gtk', '3.0')
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkMenu(Gtk.Window):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(title="Menú de Red") # Título estático
|
||||||
|
self.set_default_size(300, 200)
|
||||||
|
self.set_border_width(10)
|
||||||
|
|
||||||
|
# Crear una caja vertical para organizar los widgets
|
||||||
|
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
|
||||||
|
self.add(vbox)
|
||||||
|
|
||||||
|
# Etiqueta para mostrar el estado de la red
|
||||||
|
self.status_label = Gtk.Label(label="Estado de la red: Desconocido")
|
||||||
|
vbox.pack_start(self.status_label, False, False, 0)
|
||||||
|
|
||||||
|
# Construir el menú
|
||||||
|
self.build_menu(vbox)
|
||||||
|
|
||||||
|
# Inicializar D-Bus
|
||||||
|
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
|
||||||
|
self.bus = dbus.SystemBus()
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.network_manager = self.bus.get_object(
|
||||||
|
'org.freedesktop.NetworkManager', '/org/freedesktop/NetworkManager')
|
||||||
|
self.network_manager_props = dbus.Interface(
|
||||||
|
self.network_manager, 'org.freedesktop.DBus.Properties')
|
||||||
|
except dbus.exceptions.DBusException as e:
|
||||||
|
print(
|
||||||
|
f"Error al conectar con NetworkManager a través de D-Bus: {e}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Suscribirse a las señales de cambio de propiedades
|
||||||
|
self.bus.add_signal_receiver(
|
||||||
|
self.on_properties_changed,
|
||||||
|
dbus_interface='org.freedesktop.DBus.Properties',
|
||||||
|
signal_name='PropertiesChanged',
|
||||||
|
arg0='org.freedesktop.NetworkManager',
|
||||||
|
path='/org/freedesktop/NetworkManager',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Obtener estado inicial
|
||||||
|
self.update_status_initial()
|
||||||
|
|
||||||
|
def build_menu(self, vbox):
|
||||||
|
# Botón Conectar WiFi
|
||||||
|
self.item_connect = Gtk.Button(label='Conectar WiFi')
|
||||||
|
self.item_connect.connect('clicked', self.connect_wifi)
|
||||||
|
vbox.pack_start(self.item_connect, True, True, 0)
|
||||||
|
|
||||||
|
# Botón Desconectar WiFi
|
||||||
|
self.item_disconnect = Gtk.Button(label='Desconectar WiFi')
|
||||||
|
self.item_disconnect.connect('clicked', self.disconnect_wifi)
|
||||||
|
vbox.pack_start(self.item_disconnect, True, True, 0)
|
||||||
|
|
||||||
|
# Botón Ver Redes Disponibles
|
||||||
|
self.item_view_networks = Gtk.Button(label='Ver Redes Disponibles')
|
||||||
|
self.item_view_networks.connect('clicked', self.show_network_list)
|
||||||
|
vbox.pack_start(self.item_view_networks, True, True, 0)
|
||||||
|
|
||||||
|
# Botón Olvidar Red
|
||||||
|
self.item_forget_network = Gtk.Button(label='Olvidar Red')
|
||||||
|
self.item_forget_network.connect('clicked', self.forget_network)
|
||||||
|
vbox.pack_start(self.item_forget_network, True, True, 0)
|
||||||
|
|
||||||
|
# Botón Crear AP
|
||||||
|
self.item_create_ap = Gtk.Button(label='Crear AP')
|
||||||
|
self.item_create_ap.connect('clicked', self.create_ap)
|
||||||
|
vbox.pack_start(self.item_create_ap, True, True, 0)
|
||||||
|
|
||||||
|
# Separador
|
||||||
|
separator = Gtk.Separator()
|
||||||
|
vbox.pack_start(separator, False, False, 10)
|
||||||
|
|
||||||
|
# Botón Salir
|
||||||
|
close_button = Gtk.Button(label='Cerrar')
|
||||||
|
close_button.connect('clicked', self.quit)
|
||||||
|
vbox.pack_start(close_button, False, False, 0)
|
||||||
|
|
||||||
|
def create_ap(self, _):
|
||||||
|
"""Ejecuta el comando para abrir wihotspot-gui."""
|
||||||
|
try:
|
||||||
|
subprocess.run(['/usr/bin/wihotspot-gui'], check=True)
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
self.show_notification("Error al iniciar wihotspot-gui")
|
||||||
|
|
||||||
|
def update_status_initial(self):
|
||||||
|
"""Inicializa el estado de la conexión WiFi al abrir la aplicación."""
|
||||||
|
try:
|
||||||
|
wifi_state = self.get_wifi_state()
|
||||||
|
ssid = self.get_connected_ssid()
|
||||||
|
self.set_status(wifi_state, ssid)
|
||||||
|
except dbus.exceptions.DBusException as e:
|
||||||
|
print(f"Error al obtener el estado inicial: {e}", file=sys.stderr)
|
||||||
|
|
||||||
|
def forget_network(self, _):
|
||||||
|
"""Función para olvidar una red guardada"""
|
||||||
|
# Obtener la lista de redes WiFi guardadas de manera confiable
|
||||||
|
saved_networks = subprocess.getoutput(
|
||||||
|
"nmcli -t -f NAME,TYPE connection show | grep ':802-11-wireless$' | cut -d: -f1").splitlines()
|
||||||
|
|
||||||
|
if not saved_networks:
|
||||||
|
self.show_notification("No hay redes guardadas para olvidar.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Crear una ventana para seleccionar la red a olvidar
|
||||||
|
window = Gtk.Window(title="Olvidar Red Guardada")
|
||||||
|
window.set_default_size(300, 200)
|
||||||
|
window.set_border_width(10)
|
||||||
|
|
||||||
|
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
|
||||||
|
|
||||||
|
# Añadir botones para cada red guardada
|
||||||
|
for network in saved_networks:
|
||||||
|
button = Gtk.Button(label=network)
|
||||||
|
button.connect("clicked", self.delete_network_profile, network)
|
||||||
|
vbox.pack_start(button, True, True, 0)
|
||||||
|
|
||||||
|
# Botón para cerrar la ventana
|
||||||
|
close_button = Gtk.Button(label="Cerrar")
|
||||||
|
close_button.connect("clicked", lambda x: window.destroy())
|
||||||
|
vbox.pack_start(close_button, False, False, 0)
|
||||||
|
|
||||||
|
window.add(vbox)
|
||||||
|
window.show_all()
|
||||||
|
|
||||||
|
def delete_network_profile(self, button, network):
|
||||||
|
"""Elimina el perfil de conexión guardado para la red especificada"""
|
||||||
|
try:
|
||||||
|
subprocess.run(
|
||||||
|
['nmcli', 'connection', 'delete', network], check=True)
|
||||||
|
self.show_notification(f"Red '{network}' olvidada exitosamente.")
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
self.show_notification(f"Error al olvidar la red '{network}'.")
|
||||||
|
|
||||||
|
def connect_wifi(self, _):
|
||||||
|
"""Función llamada al hacer clic en el botón 'Conectar WiFi'.
|
||||||
|
Muestra la lista de redes y permite al usuario seleccionar una."""
|
||||||
|
self.show_network_list(None)
|
||||||
|
|
||||||
|
def get_available_networks_list(self):
|
||||||
|
"""Obtiene la lista de redes WiFi junto con la intensidad de la señal y la banda (2.4 GHz o 5 GHz), sin duplicados y sin entradas vacías"""
|
||||||
|
try:
|
||||||
|
# Obtener SSID, señal y frecuencia
|
||||||
|
output = subprocess.getoutput(
|
||||||
|
"nmcli -t -f SSID,SIGNAL,FREQ dev wifi list")
|
||||||
|
networks = {}
|
||||||
|
for line in output.splitlines():
|
||||||
|
if line:
|
||||||
|
# Separar el SSID, señal y frecuencia en tres variables
|
||||||
|
parts = line.split(":")
|
||||||
|
|
||||||
|
# Validación para evitar entradas vacías en SSID
|
||||||
|
ssid = parts[0].strip()
|
||||||
|
if not ssid:
|
||||||
|
continue # Saltamos esta entrada si el SSID está vacío
|
||||||
|
|
||||||
|
signal_strength = int(parts[1].strip()) if len(
|
||||||
|
parts) > 1 and parts[1].isdigit() else 0
|
||||||
|
frequency = int(parts[2].strip().split()[0]) if len(
|
||||||
|
parts) > 2 and parts[2].strip().split()[0].isdigit() else 0
|
||||||
|
|
||||||
|
# Determinar la banda en función de la frecuencia
|
||||||
|
band = "5 GHz" if frequency >= 5000 else "2.4 GHz"
|
||||||
|
|
||||||
|
# Evitar duplicados: guardar solo la señal más alta para cada SSID
|
||||||
|
if ssid in networks:
|
||||||
|
if signal_strength > networks[ssid][0]:
|
||||||
|
networks[ssid] = (signal_strength, band)
|
||||||
|
else:
|
||||||
|
networks[ssid] = (signal_strength, band)
|
||||||
|
|
||||||
|
# Convertir el diccionario a una lista de tuplas (SSID, señal, banda)
|
||||||
|
return [(ssid, data[0], data[1]) for ssid, data in networks.items()]
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error al obtener la lista de redes disponibles: {
|
||||||
|
e}", file=sys.stderr)
|
||||||
|
return []
|
||||||
|
|
||||||
|
def show_network_list(self, _):
|
||||||
|
# Llamar a la función para obtener la lista de redes con señal y banda
|
||||||
|
networks = self.get_available_networks_list()
|
||||||
|
|
||||||
|
if not networks:
|
||||||
|
self.show_notification("No se encontraron redes disponibles.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Crear una ventana de lista de redes
|
||||||
|
window = Gtk.Window(title="Redes Disponibles")
|
||||||
|
window.set_default_size(300, 200)
|
||||||
|
window.set_border_width(10)
|
||||||
|
|
||||||
|
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
|
||||||
|
|
||||||
|
# Añadir botones para cada red disponible con la intensidad de señal y la banda
|
||||||
|
for network, signal_strength, band in networks:
|
||||||
|
# Mostramos el nombre de la red junto con su intensidad de señal y la banda (2.4 GHz o 5 GHz)
|
||||||
|
button_label = f"{network} ({signal_strength}%, {band})"
|
||||||
|
button = Gtk.Button(label=button_label)
|
||||||
|
button.connect(
|
||||||
|
"clicked", self.prompt_password_and_connect, network)
|
||||||
|
vbox.pack_start(button, True, True, 0)
|
||||||
|
|
||||||
|
# Botón para cerrar la ventana
|
||||||
|
close_button = Gtk.Button(label="Cerrar")
|
||||||
|
close_button.connect("clicked", lambda x: window.destroy())
|
||||||
|
vbox.pack_start(close_button, False, False, 0)
|
||||||
|
|
||||||
|
window.add(vbox)
|
||||||
|
window.show_all()
|
||||||
|
|
||||||
|
def prompt_password_and_connect(self, button, network):
|
||||||
|
# Verificar si el perfil de la conexión ya existe
|
||||||
|
existing_profile = subprocess.getoutput(
|
||||||
|
f"nmcli -t -f NAME connection show | grep '^{network}$'")
|
||||||
|
|
||||||
|
if existing_profile:
|
||||||
|
# Si el perfil ya existe, intentar conectarse sin solicitar la contraseña
|
||||||
|
self.show_notification(f"Conectando a {network}...")
|
||||||
|
# None para indicar que no se requiere contraseña
|
||||||
|
self.connect_to_network_with_nmcli(network, None)
|
||||||
|
else:
|
||||||
|
# Si no existe el perfil, solicitar la contraseña
|
||||||
|
dialog = Gtk.Dialog(
|
||||||
|
title=f"Conectar a {network}",
|
||||||
|
transient_for=self,
|
||||||
|
flags=0
|
||||||
|
)
|
||||||
|
dialog.add_buttons(
|
||||||
|
Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
|
||||||
|
Gtk.STOCK_OK, Gtk.ResponseType.OK
|
||||||
|
)
|
||||||
|
|
||||||
|
# Crear un campo de entrada de texto para la contraseña
|
||||||
|
box = dialog.get_content_area()
|
||||||
|
label = Gtk.Label(label="Introduce la contraseña:")
|
||||||
|
box.add(label)
|
||||||
|
|
||||||
|
password_entry = Gtk.Entry()
|
||||||
|
# Ocultar la contraseña al escribir
|
||||||
|
password_entry.set_visibility(False)
|
||||||
|
box.add(password_entry)
|
||||||
|
dialog.show_all()
|
||||||
|
|
||||||
|
# Ejecutar el diálogo y obtener la respuesta del usuario
|
||||||
|
response = dialog.run()
|
||||||
|
if response == Gtk.ResponseType.OK:
|
||||||
|
password = password_entry.get_text()
|
||||||
|
dialog.destroy()
|
||||||
|
# Intentar conectar a la red con nmcli y configuración de seguridad
|
||||||
|
self.connect_to_network_with_nmcli(network, password)
|
||||||
|
else:
|
||||||
|
dialog.destroy()
|
||||||
|
print("Conexión cancelada por el usuario.")
|
||||||
|
|
||||||
|
def connect_to_network_with_nmcli(self, ssid, password):
|
||||||
|
# Detectar la interfaz de red WiFi principal
|
||||||
|
try:
|
||||||
|
interface = subprocess.getoutput(
|
||||||
|
"nmcli device status | grep wifi | grep -v 'p2p' | awk '{print $1}'").strip()
|
||||||
|
if not interface:
|
||||||
|
self.show_notification(
|
||||||
|
"No se encontró una interfaz WiFi válida.")
|
||||||
|
return
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
self.show_notification("Error al obtener la interfaz de red.")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Verificar si ya existe un perfil de conexión con el SSID
|
||||||
|
existing_profile = subprocess.getoutput(
|
||||||
|
f"nmcli -t -f NAME connection show | grep '^{ssid}$'")
|
||||||
|
|
||||||
|
if existing_profile:
|
||||||
|
# Si el perfil existe y se proporciona una contraseña, actualizarlo
|
||||||
|
if password:
|
||||||
|
subprocess.run([
|
||||||
|
'nmcli', 'connection', 'modify', ssid,
|
||||||
|
'wifi-sec.key-mgmt', 'wpa-psk', 'wifi-sec.psk', password,
|
||||||
|
'connection.interface-name', interface
|
||||||
|
], check=True)
|
||||||
|
self.show_notification(f"Perfil actualizado: {ssid}")
|
||||||
|
else:
|
||||||
|
# Si el perfil no existe, crearlo
|
||||||
|
subprocess.run([
|
||||||
|
'nmcli', 'connection', 'add', 'type', 'wifi',
|
||||||
|
'ifname', interface, 'con-name', ssid, 'ssid', ssid,
|
||||||
|
'wifi-sec.key-mgmt', 'wpa-psk', 'wifi-sec.psk', password
|
||||||
|
], check=True)
|
||||||
|
self.show_notification(f"Perfil creado: {ssid}")
|
||||||
|
|
||||||
|
# Intentar conectar usando el perfil existente o actualizado
|
||||||
|
subprocess.run(['nmcli', 'connection', 'up', ssid], check=True)
|
||||||
|
self.show_notification(f"Conectado a {ssid}")
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
self.show_notification(f"Error al intentar conectar a {ssid}")
|
||||||
|
|
||||||
|
def disconnect_wifi(self, _):
|
||||||
|
"""Función para desconectar la red WiFi actual."""
|
||||||
|
try:
|
||||||
|
# Intentar desconectar la red activa
|
||||||
|
subprocess.run(['nmcli', 'networking', 'off'], check=True)
|
||||||
|
# Reactivar después de desconectar
|
||||||
|
subprocess.run(['nmcli', 'networking', 'on'], check=True)
|
||||||
|
self.show_notification("WiFi Desconectado.")
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
self.show_notification("Error al desconectar WiFi.")
|
||||||
|
|
||||||
|
def quit(self, _):
|
||||||
|
Gtk.main_quit()
|
||||||
|
|
||||||
|
def get_available_networks_list(self):
|
||||||
|
"""Obtiene la lista de redes WiFi junto con la intensidad de la señal y la banda (2.4 GHz o 5 GHz), sin duplicados y sin entradas vacías"""
|
||||||
|
try:
|
||||||
|
# Obtener SSID, señal y frecuencia
|
||||||
|
output = subprocess.getoutput(
|
||||||
|
"nmcli -t -f SSID,SIGNAL,FREQ dev wifi list")
|
||||||
|
networks = {}
|
||||||
|
for line in output.splitlines():
|
||||||
|
if line:
|
||||||
|
# Separar el SSID, señal y frecuencia en tres variables
|
||||||
|
parts = line.split(":")
|
||||||
|
|
||||||
|
# Validación para evitar entradas vacías en SSID
|
||||||
|
ssid = parts[0].strip()
|
||||||
|
if not ssid:
|
||||||
|
continue # Saltamos esta entrada si el SSID está vacío
|
||||||
|
|
||||||
|
signal_strength = int(parts[1].strip()) if len(
|
||||||
|
parts) > 1 and parts[1].isdigit() else 0
|
||||||
|
frequency = int(parts[2].strip().split()[0]) if len(
|
||||||
|
parts) > 2 and parts[2].strip().split()[0].isdigit() else 0
|
||||||
|
|
||||||
|
# Determinar la banda en función de la frecuencia
|
||||||
|
band = "5 GHz" if frequency >= 5000 else "2.4 GHz"
|
||||||
|
|
||||||
|
# Evitar duplicados: guardar solo la señal más alta para cada SSID
|
||||||
|
if ssid in networks:
|
||||||
|
if signal_strength > networks[ssid][0]:
|
||||||
|
networks[ssid] = (signal_strength, band)
|
||||||
|
else:
|
||||||
|
networks[ssid] = (signal_strength, band)
|
||||||
|
|
||||||
|
# Convertir el diccionario a una lista de tuplas (SSID, señal, banda)
|
||||||
|
return [(ssid, data[0], data[1]) for ssid, data in networks.items()]
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error al obtener la lista de redes disponibles: {
|
||||||
|
e}", file=sys.stderr)
|
||||||
|
return []
|
||||||
|
|
||||||
|
def show_notification(self, message):
|
||||||
|
"""Muestra una notificación en el sistema"""
|
||||||
|
subprocess.run(['notify-send', message])
|
||||||
|
|
||||||
|
def on_properties_changed(self, interface, changed_properties, invalidated_properties):
|
||||||
|
if 'State' in changed_properties:
|
||||||
|
wifi_state = self.get_wifi_state()
|
||||||
|
ssid = self.get_connected_ssid()
|
||||||
|
self.set_status(wifi_state, ssid)
|
||||||
|
|
||||||
|
def set_status(self, wifi_state, ssid):
|
||||||
|
if wifi_state == 'disabled':
|
||||||
|
status_text = "WiFi Apagado"
|
||||||
|
# Deshabilitar el botón de Desconectar WiFi y habilitar Conectar WiFi
|
||||||
|
self.item_disconnect.set_sensitive(False)
|
||||||
|
self.item_connect.set_sensitive(True)
|
||||||
|
elif wifi_state == 'enabled' and not ssid:
|
||||||
|
status_text = "WiFi Encendido (No Conectado)"
|
||||||
|
# Habilitar ambos botones
|
||||||
|
self.item_disconnect.set_sensitive(True)
|
||||||
|
self.item_connect.set_sensitive(True)
|
||||||
|
elif wifi_state == 'enabled' and ssid:
|
||||||
|
status_text = f"Conectado a: {ssid}"
|
||||||
|
# Deshabilitar el botón de Conectar WiFi y habilitar Desconectar WiFi
|
||||||
|
self.item_connect.set_sensitive(False)
|
||||||
|
self.item_disconnect.set_sensitive(True)
|
||||||
|
else:
|
||||||
|
status_text = "WiFi Estado Desconocido"
|
||||||
|
# Habilitar ambos botones por defecto
|
||||||
|
self.item_connect.set_sensitive(True)
|
||||||
|
self.item_disconnect.set_sensitive(True)
|
||||||
|
|
||||||
|
print(f"Actualizando estado: '{status_text}'")
|
||||||
|
self.status_label.set_text(f"Estado de la red: {status_text}")
|
||||||
|
|
||||||
|
def get_wifi_state(self):
|
||||||
|
try:
|
||||||
|
output = subprocess.getoutput("nmcli radio wifi")
|
||||||
|
print(f"Estado WiFi: {output}")
|
||||||
|
return output.lower()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error al obtener el estado de WiFi: {e}", file=sys.stderr)
|
||||||
|
return "unknown"
|
||||||
|
|
||||||
|
def get_connected_ssid(self):
|
||||||
|
try:
|
||||||
|
# Usar iwgetid para obtener el SSID conectado
|
||||||
|
ssid = subprocess.getoutput("iwgetid -r").strip()
|
||||||
|
if ssid:
|
||||||
|
print(f"SSID conectado obtenido con iwgetid: {ssid}")
|
||||||
|
return ssid
|
||||||
|
else:
|
||||||
|
# Fallback a nmcli si iwgetid no retorna SSID
|
||||||
|
output = subprocess.getoutput(
|
||||||
|
"nmcli -t -f ACTIVE,SSID dev wifi | grep '^yes' | cut -d':' -f2").strip()
|
||||||
|
if output:
|
||||||
|
print(f"SSID conectado obtenido con nmcli: {output}")
|
||||||
|
return output
|
||||||
|
else:
|
||||||
|
print(f"SSID conectado obtenido: None")
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error al obtener SSID conectado: {e}", file=sys.stderr)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Para evitar múltiples instancias, usar una lock con D-Bus o similar
|
||||||
|
# Aquí, simplemente se muestra la ventana
|
||||||
|
window = NetworkMenu()
|
||||||
|
window.connect("destroy", Gtk.main_quit)
|
||||||
|
window.show_all()
|
||||||
|
Gtk.main()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
51
.config/waybar/scripts/wlogout.sh
Executable file
51
.config/waybar/scripts/wlogout.sh
Executable file
@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# wlogout (Power, Screen Lock, Suspend, etc)
|
||||||
|
|
||||||
|
# Set variables for parameters
|
||||||
|
A_2160=680
|
||||||
|
B_2160=750
|
||||||
|
A_1440=500
|
||||||
|
B_1440=550
|
||||||
|
A_1080=300
|
||||||
|
B_1080=380
|
||||||
|
A_720=50
|
||||||
|
B_720=50
|
||||||
|
|
||||||
|
# Check if wlogout is already running
|
||||||
|
if pgrep -x "wlogout" > /dev/null; then
|
||||||
|
pkill -x "wlogout"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Detect monitor resolution and scaling factor
|
||||||
|
resolution=$(hyprctl -j monitors | jq -r '.[] | select(.focused==true) | .height / .scale' | awk -F'.' '{print $1}')
|
||||||
|
hypr_scale=$(hyprctl -j monitors | jq -r '.[] | select(.focused==true) | .scale')
|
||||||
|
|
||||||
|
echo "Detected Resolution: $resolution"
|
||||||
|
|
||||||
|
# Prints the screen and applies blur. Very slow, looking to make faster
|
||||||
|
#grim - | magick - -blur 0x8 /tmp/shot_blurred.png
|
||||||
|
#grim - | convert - -scale 2.5% -resize 4000% /tmp/shot_blurred.png
|
||||||
|
|
||||||
|
# Set parameters based on screen resolution and scaling factor
|
||||||
|
if ((resolution == 1353)); then
|
||||||
|
wlogout --protocol layer-shell -b 6 -T 600 -B 560 &
|
||||||
|
echo "Setting parameters for custom 1353p resolution"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if ((resolution >= 2160)); then
|
||||||
|
wlogout --protocol layer-shell -b 3 -T $(awk "BEGIN {printf \"%.0f\", $A_2160 * 2160 * $hypr_scale / $resolution}") -B $(awk "BEGIN {printf \"%.0f\", $B_2160 * 2160 * $hypr_scale / $resolution}") &
|
||||||
|
echo "Setting parameters for resolution >= 2160p"
|
||||||
|
elif ((resolution >= 1440)); then
|
||||||
|
wlogout --protocol layer-shell -b 6 -T $(awk "BEGIN {printf \"%.0f\", $A_1440 * 1440 * $hypr_scale / $resolution}") -B $(awk "BEGIN {printf \"%.0f\", $B_1440 * 1440 * $hypr_scale / $resolution}") &
|
||||||
|
echo "Setting parameters for resolution >= 1440p"
|
||||||
|
elif ((resolution >= 1080)); then
|
||||||
|
wlogout --protocol layer-shell -b 6 -T $(awk "BEGIN {printf \"%.0f\", $A_1080 * 1080 * $hypr_scale / $resolution}") -B $(awk "BEGIN {printf \"%.0f\", $B_1080 * 1080 * $hypr_scale / $resolution}") &
|
||||||
|
echo "Setting parameters for resolution >= 1080p"
|
||||||
|
elif ((resolution > 720)); then
|
||||||
|
wlogout --protocol layer-shell -b 3 -T $(awk "BEGIN {printf \"%.0f\", $A_720 * 720 * $hypr_scale / $resolution}") -B $(awk "BEGIN {printf \"%.0f\", $B_720 * 720 * $hypr_scale / $resolution}") &
|
||||||
|
echo "Setting parameters for resolution >= 720p"
|
||||||
|
else
|
||||||
|
wlogout &
|
||||||
|
echo "Setting default parameters for resolution <= 720p"
|
||||||
|
fi
|
572
.config/waybar/style.css
Normal file
572
.config/waybar/style.css
Normal file
@ -0,0 +1,572 @@
|
|||||||
|
* {
|
||||||
|
font-family: "Ubuntu Nerd Font", "Font Awesome 6 Pro";
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
window#waybar {
|
||||||
|
background-color: #242933;
|
||||||
|
color: #ffffff;
|
||||||
|
border-radius: 8px;
|
||||||
|
border-width: 3px;
|
||||||
|
border-color: #3b4252;
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
window#waybar.hidden {
|
||||||
|
opacity: 0.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#window {
|
||||||
|
color: #64727d;
|
||||||
|
margin-left: 50px;
|
||||||
|
margin-right: 50px;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#workspaces,
|
||||||
|
#custom-calendar_clock,
|
||||||
|
#custom-weather,
|
||||||
|
#cpu,
|
||||||
|
#memory,
|
||||||
|
#custom-updtes,
|
||||||
|
#custom-rate,
|
||||||
|
#custom-inetspeedup,
|
||||||
|
#custom-inetspeeddown,
|
||||||
|
#tray,
|
||||||
|
#custom-separator,
|
||||||
|
#mode,
|
||||||
|
#custom-lock,
|
||||||
|
#workspaces,
|
||||||
|
#idle_inhibitor,
|
||||||
|
#power-profiles-daemon,
|
||||||
|
#custom-fans,
|
||||||
|
#custom-powermenu,
|
||||||
|
#custom-launcher,
|
||||||
|
#custom-vpn,
|
||||||
|
#custom-borg,
|
||||||
|
#temperature,
|
||||||
|
#custom-temps,
|
||||||
|
#cpu {
|
||||||
|
color: #fa9f8e;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mpris {
|
||||||
|
color: #fa9f8e;
|
||||||
|
}
|
||||||
|
#custom-roon {
|
||||||
|
color: #fa9f8e;
|
||||||
|
}
|
||||||
|
#custom-dmp_nowplaying {
|
||||||
|
color: #fa9f8e;
|
||||||
|
}
|
||||||
|
#custom-lyrics {
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
#image {
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#temperature {
|
||||||
|
color: #fa9f8e;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
#custom-temps {
|
||||||
|
color: #fa9f8e;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-tailscale {
|
||||||
|
color: #81a1c1;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
#cutom-borg {
|
||||||
|
color: #fa9f8e;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
#power-profiles-daemon {
|
||||||
|
color: #81a1c1;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
#custom-fans {
|
||||||
|
color: #fa9f8e;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
#memory {
|
||||||
|
font-size: 15px;
|
||||||
|
color: #81a1c1;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
#custom-updates {
|
||||||
|
font-size: 15px;
|
||||||
|
color: #81a1c1;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-inetspeeddown {
|
||||||
|
font-size: 15px;
|
||||||
|
color: #81a1c1;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
#custom-inetspeedup {
|
||||||
|
font-size: 15px;
|
||||||
|
color: #81a1c1;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes button_activate {
|
||||||
|
from {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#workspaces button {
|
||||||
|
border: none;
|
||||||
|
color: #d4d2a9;
|
||||||
|
padding: 3px;
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#workspaces button.active {
|
||||||
|
color: #81a1c1;
|
||||||
|
background: radial-gradient(
|
||||||
|
circle,
|
||||||
|
rgba(20, 48, 187, 0.699) 5%,
|
||||||
|
rgba(36, 39, 54, 0.5) 18%,
|
||||||
|
rgba(132, 129, 156, 0) 23%,
|
||||||
|
rgba(70, 76, 98, 0.201) 24%,
|
||||||
|
rgba(132, 129, 156, 0) 30%
|
||||||
|
);
|
||||||
|
animation: button_activate 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#workspaces button.urgent {
|
||||||
|
color: #fa9f8e;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#workspaces button.persistent {
|
||||||
|
color: #85909e;
|
||||||
|
}
|
||||||
|
|
||||||
|
#workspaces button:hover {
|
||||||
|
border: none;
|
||||||
|
background: radial-gradient(
|
||||||
|
circle,
|
||||||
|
rgba(25, 79, 255, 0.1) 20%,
|
||||||
|
rgba(132, 129, 156, 0) 30%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#workspaces button.active:hover,
|
||||||
|
#workspaces button.urgent:hover {
|
||||||
|
background: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-launcher {
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
|
||||||
|
padding-right: 5px;
|
||||||
|
padding-left: 0px;
|
||||||
|
|
||||||
|
font-size: 22px;
|
||||||
|
|
||||||
|
color: #7a95c9;
|
||||||
|
|
||||||
|
margin-top: 1px;
|
||||||
|
margin-bottom: 1px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#workspaces,
|
||||||
|
#custom-calendar_clock,
|
||||||
|
#custom-weather,
|
||||||
|
#custom-network_speed,
|
||||||
|
#cpu,
|
||||||
|
#custom-song_name,
|
||||||
|
#custom-vpn,
|
||||||
|
#custom-borg,
|
||||||
|
#temperature,
|
||||||
|
#custom-temps,
|
||||||
|
#custom-fans,
|
||||||
|
#power-profiles-daemon,
|
||||||
|
#memory,
|
||||||
|
#custom-updates,
|
||||||
|
#custom-tailscale,
|
||||||
|
#custom-rate,
|
||||||
|
#custom-inetspeedup,
|
||||||
|
#custom-inetspeeddown,
|
||||||
|
#pulseaudio,
|
||||||
|
#custom-separator,
|
||||||
|
#tray #network {
|
||||||
|
background-color: #3b4252;
|
||||||
|
padding: 0em 2em;
|
||||||
|
|
||||||
|
font-size: 20px;
|
||||||
|
|
||||||
|
padding-left: 7.5px;
|
||||||
|
padding-right: 7.5px;
|
||||||
|
|
||||||
|
padding-top: 2px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
|
||||||
|
font-size: 19px;
|
||||||
|
}
|
||||||
|
#custom-separator {
|
||||||
|
color: #1b5e20;
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
#workspaces {
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
#custom-pwrate {
|
||||||
|
color: #81a1c1;
|
||||||
|
padding-left: 10px;
|
||||||
|
margin-right: 15px;
|
||||||
|
font-size: 13px;
|
||||||
|
border-radius: 0px 8px 8px 0px;
|
||||||
|
margin-left: -15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wireplumber {
|
||||||
|
color: #81a1c1;
|
||||||
|
padding-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
font-size: 23px;
|
||||||
|
border-radius: 0px 8px 8px 0px;
|
||||||
|
margin-left: -15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wireplumber.muted {
|
||||||
|
color: #fb958b;
|
||||||
|
padding-left: 10px;
|
||||||
|
font-size: 23px;
|
||||||
|
border-radius: 0px 8px 8px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#backlight {
|
||||||
|
color: #81a1c1;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
font-size: 24px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
#custom-wf-recorder {
|
||||||
|
color: #ff0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#network {
|
||||||
|
padding-left: 0.2em;
|
||||||
|
color: #5e81ac;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
padding-left: 12px;
|
||||||
|
padding-right: 12px;
|
||||||
|
font-size: 19px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#network.disconnected {
|
||||||
|
color: #fb958b;
|
||||||
|
}
|
||||||
|
|
||||||
|
#battery {
|
||||||
|
color: #81a1c1;
|
||||||
|
font-size: 19px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#battery.critical,
|
||||||
|
#battery.warning,
|
||||||
|
#battery.full,
|
||||||
|
#battery.plugged {
|
||||||
|
color: #81a1c1;
|
||||||
|
font-size: 19px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#battery.charging {
|
||||||
|
font-size: 19px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#battery.full,
|
||||||
|
#battery.plugged {
|
||||||
|
font-size: 19px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes blink {
|
||||||
|
to {
|
||||||
|
background-color: rgba(30, 34, 42, 0.5);
|
||||||
|
color: #abb2bf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#battery.warning {
|
||||||
|
color: #ecd3a0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#battery.critical:not(.charging) {
|
||||||
|
color: #fb958b;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-lock {
|
||||||
|
color: #ecd3a0;
|
||||||
|
padding: 0 15px 0 15px;
|
||||||
|
margin-left: 7px;
|
||||||
|
margin-top: 7px;
|
||||||
|
margin-bottom: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-calendar_clock {
|
||||||
|
color: #8a909e;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bluetooth {
|
||||||
|
color: #fb958b;
|
||||||
|
padding-left: 10px;
|
||||||
|
margin-right: -5px;
|
||||||
|
font-size: 18px;
|
||||||
|
border-radius: 0px 8px 8px 0px;
|
||||||
|
margin-left: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bluetooth.on {
|
||||||
|
color: #fb958b;
|
||||||
|
padding-left: 10px;
|
||||||
|
margin-right: -5px;
|
||||||
|
font-size: 18px;
|
||||||
|
border-radius: 0px 8px 8px 0px;
|
||||||
|
margin-left: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bluetooth.connected {
|
||||||
|
color: #81a1c1;
|
||||||
|
padding-left: 10px;
|
||||||
|
margin-right: -5px;
|
||||||
|
font-size: 18px;
|
||||||
|
border-radius: 0px 8px 8px 0px;
|
||||||
|
margin-left: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bluetooth.off {
|
||||||
|
color: #8a909e;
|
||||||
|
padding-left: 10px;
|
||||||
|
margin-right: -5px;
|
||||||
|
font-size: 18px;
|
||||||
|
border-radius: 0px 8px 8px 0px;
|
||||||
|
margin-left: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-powermenu {
|
||||||
|
color: #e78284;
|
||||||
|
margin-right: 12px;
|
||||||
|
margin-left: 12px;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0 6px 0 6.8px;
|
||||||
|
margin-top: 7px;
|
||||||
|
margin-bottom: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
tooltip {
|
||||||
|
font-family: Ubuntu Nerd Font;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 15px;
|
||||||
|
background-color: #242933;
|
||||||
|
}
|
||||||
|
|
||||||
|
tooltip label {
|
||||||
|
font-family: Ubuntu Nerd Font;
|
||||||
|
padding: 5px;
|
||||||
|
background-color: #242933;
|
||||||
|
}
|
||||||
|
|
||||||
|
#now_playing > tooltip {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
label:focus {
|
||||||
|
background-color: #242933;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tray {
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-size: 30px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tray > .passive {
|
||||||
|
-gtk-icon-effect: dim;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tray > .needs-attention {
|
||||||
|
-gtk-icon-effect: highlight;
|
||||||
|
background-color: #eb4d4b;
|
||||||
|
}
|
||||||
|
|
||||||
|
#idle_inhibitor {
|
||||||
|
background-color: #242933;
|
||||||
|
}
|
||||||
|
|
||||||
|
#idle_inhibitor.activated {
|
||||||
|
background-color: #ecf0f1;
|
||||||
|
color: #2d3436;
|
||||||
|
}
|
||||||
|
|
||||||
|
#memory,
|
||||||
|
#custom-updates,
|
||||||
|
#custom-rate,
|
||||||
|
#custom-tailscale,
|
||||||
|
#custom-inetspeedup,
|
||||||
|
#custom-inetspeeddown,
|
||||||
|
#cpu {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mpris {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
#custom-roon {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-dmp_nowplaying {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#power-profiles-daemon {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
#custom-fans {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
#temperature {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
#custom-temps {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-vpn {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
#custom-borg {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
#custom-song_name,
|
||||||
|
#custom-spotify,
|
||||||
|
#custom-next,
|
||||||
|
#custom-pre {
|
||||||
|
background-color: #3b4252;
|
||||||
|
font-size: 20px;
|
||||||
|
padding-right: 6.5px;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-weather.severe {
|
||||||
|
color: #8a909e;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-weather.sunnyDay {
|
||||||
|
color: #8a909e;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-weather.clearNight {
|
||||||
|
color: #8a909e;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-weather.cloudyFoggyDay,
|
||||||
|
#custom-weather.cloudyFoggyNight {
|
||||||
|
color: #8a909e;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-weather.rainyDay,
|
||||||
|
#custom-weather.rainyNight {
|
||||||
|
color: #8a909e;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-weather.showyIcyDay,
|
||||||
|
#custom-weather.snowyIcyNight {
|
||||||
|
color: #8a909e;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-weather {
|
||||||
|
color: #8a909e;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
#custom-network_speed {
|
||||||
|
color: #8a909e;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
#waybar.info {
|
||||||
|
background-color: rgba(10, 10, 10, 0.0);
|
||||||
|
font-family: JetBrainsMonoNL Nerd Font Mono;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#submap {
|
||||||
|
background-color: rgba(10, 10, 10, 0.8);
|
||||||
|
color: rgba(152, 239, 106, 1);
|
||||||
|
}
|
549
.config/waybar/style.css_bicolor
Normal file
549
.config/waybar/style.css_bicolor
Normal file
@ -0,0 +1,549 @@
|
|||||||
|
* {
|
||||||
|
font-family: "Ubuntu Nerd Font", "Font Awesome 6 Pro";
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
window#waybar {
|
||||||
|
background-color: #242933;
|
||||||
|
color: #ffffff;
|
||||||
|
border-radius: 8px;
|
||||||
|
border-width: 3px;
|
||||||
|
border-color: #3b4252;
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
window#waybar.hidden {
|
||||||
|
opacity: 0.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#window {
|
||||||
|
color: #64727d;
|
||||||
|
margin-left: 50px;
|
||||||
|
margin-right: 50px;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#workspaces,
|
||||||
|
#custom-calendar_clock,
|
||||||
|
#custom-weather,
|
||||||
|
#cpu,
|
||||||
|
#memory,
|
||||||
|
#custom-rate,
|
||||||
|
#custom-inetspeedup,
|
||||||
|
#custom-inetspeeddown,
|
||||||
|
#tray,
|
||||||
|
#custom-separator,
|
||||||
|
#mode,
|
||||||
|
#custom-lock,
|
||||||
|
#workspaces,
|
||||||
|
#idle_inhibitor,
|
||||||
|
#power-profiles-daemon,
|
||||||
|
#custom-fans,
|
||||||
|
#custom-powermenu,
|
||||||
|
#custom-launcher,
|
||||||
|
#custom-vpn,
|
||||||
|
#custom-borg,
|
||||||
|
|
||||||
|
|
||||||
|
#cpu {
|
||||||
|
color: #fa9f8e;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
#mpris {
|
||||||
|
color: #fa9f8e;
|
||||||
|
}
|
||||||
|
#custom-roon {
|
||||||
|
color: #fa9f8e;
|
||||||
|
}
|
||||||
|
#custom-lyrics {
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
#image {
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#temperature {
|
||||||
|
color: #fa9f8e;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
#custom-tailscale {
|
||||||
|
color: #81a1c1;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
#cutom-borg {
|
||||||
|
color: #fa9f8e;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
#power-profiles-daemon {
|
||||||
|
color: #fa9f8e;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
#custom-fans {
|
||||||
|
color: #fa9f8e;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
#memory {
|
||||||
|
font-size: 15px;
|
||||||
|
color: #81a1c1;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
#custom-inetspeeddown {
|
||||||
|
font-size: 15px;
|
||||||
|
color: #81a1c1;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
#custom-inetspeedup {
|
||||||
|
font-size: 15px;
|
||||||
|
color: #81a1c1;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes button_activate {
|
||||||
|
from {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#workspaces button {
|
||||||
|
border: none;
|
||||||
|
color: #d4d2a9;
|
||||||
|
padding: 3px;
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#workspaces button.active {
|
||||||
|
color: #81a1c1;
|
||||||
|
background: radial-gradient(
|
||||||
|
circle,
|
||||||
|
rgba(20, 48, 187, 0.699) 5%,
|
||||||
|
rgba(36, 39, 54, 0.5) 18%,
|
||||||
|
rgba(132, 129, 156, 0) 23%,
|
||||||
|
rgba(70, 76, 98, 0.201) 24%,
|
||||||
|
rgba(132, 129, 156, 0) 30%
|
||||||
|
);
|
||||||
|
animation: button_activate 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#workspaces button.urgent {
|
||||||
|
color: #fa9f8e;
|
||||||
|
background: radial-gradient(
|
||||||
|
circle,
|
||||||
|
rgba(25, 79, 255, 0.3) 20%,
|
||||||
|
rgba(132, 129, 156, 0) 30%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#workspaces button.persistent {
|
||||||
|
color: #85909e;
|
||||||
|
}
|
||||||
|
|
||||||
|
#workspaces button:hover {
|
||||||
|
border: none;
|
||||||
|
background: radial-gradient(
|
||||||
|
circle,
|
||||||
|
rgba(25, 79, 255, 0.1) 20%,
|
||||||
|
rgba(132, 129, 156, 0) 30%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#workspaces button.active:hover,
|
||||||
|
#workspaces button.urgent:hover {
|
||||||
|
background: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-launcher {
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
|
||||||
|
padding-right: 5px;
|
||||||
|
padding-left: 0px;
|
||||||
|
|
||||||
|
font-size: 22px;
|
||||||
|
|
||||||
|
color: #7a95c9;
|
||||||
|
|
||||||
|
margin-top: 1px;
|
||||||
|
margin-bottom: 1px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#workspaces,
|
||||||
|
#custom-calendar_clock,
|
||||||
|
#custom-weather,
|
||||||
|
#custom-network_speed,
|
||||||
|
#cpu,
|
||||||
|
#custom-song_name,
|
||||||
|
#custom-vpn,
|
||||||
|
#custom-borg,
|
||||||
|
#temperature,
|
||||||
|
#custom-fans,
|
||||||
|
#power-profiles-daemon,
|
||||||
|
#memory,
|
||||||
|
#custom-tailscale,
|
||||||
|
#custom-rate,
|
||||||
|
#custom-inetspeedup,
|
||||||
|
#custom-inetspeeddown,
|
||||||
|
#pulseaudio,
|
||||||
|
#custom-separator,
|
||||||
|
#tray #network {
|
||||||
|
background-color: #3b4252;
|
||||||
|
padding: 0em 2em;
|
||||||
|
|
||||||
|
font-size: 20px;
|
||||||
|
|
||||||
|
padding-left: 7.5px;
|
||||||
|
padding-right: 7.5px;
|
||||||
|
|
||||||
|
padding-top: 2px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
|
||||||
|
font-size: 19px;
|
||||||
|
}
|
||||||
|
#custom-separator {
|
||||||
|
color: #1b5e20;
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
#workspaces {
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
#custom-pwrate {
|
||||||
|
color: #81a1c1;
|
||||||
|
padding-left: 10px;
|
||||||
|
margin-right: 15px;
|
||||||
|
font-size: 13px;
|
||||||
|
border-radius: 0px 8px 8px 0px;
|
||||||
|
margin-left: -15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wireplumber {
|
||||||
|
color: #81a1c1;
|
||||||
|
padding-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
font-size: 23px;
|
||||||
|
border-radius: 0px 8px 8px 0px;
|
||||||
|
margin-left: -15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wireplumber.muted {
|
||||||
|
color: #fb958b;
|
||||||
|
padding-left: 10px;
|
||||||
|
font-size: 23px;
|
||||||
|
border-radius: 0px 8px 8px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#backlight {
|
||||||
|
color: #81a1c1;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
font-size: 24px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
#custom-wf-recorder {
|
||||||
|
color: #ff0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#network {
|
||||||
|
padding-left: 0.2em;
|
||||||
|
color: #5e81ac;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
padding-left: 12px;
|
||||||
|
padding-right: 12px;
|
||||||
|
font-size: 19px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#network.disconnected {
|
||||||
|
color: #fb958b;
|
||||||
|
}
|
||||||
|
|
||||||
|
#battery {
|
||||||
|
color: #81a1c1;
|
||||||
|
font-size: 19px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#battery.critical,
|
||||||
|
#battery.warning,
|
||||||
|
#battery.full,
|
||||||
|
#battery.plugged {
|
||||||
|
color: #81a1c1;
|
||||||
|
font-size: 19px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#battery.charging {
|
||||||
|
font-size: 19px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#battery.full,
|
||||||
|
#battery.plugged {
|
||||||
|
font-size: 19px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes blink {
|
||||||
|
to {
|
||||||
|
background-color: rgba(30, 34, 42, 0.5);
|
||||||
|
color: #abb2bf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#battery.warning {
|
||||||
|
color: #ecd3a0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#battery.critical:not(.charging) {
|
||||||
|
color: #fb958b;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-lock {
|
||||||
|
color: #ecd3a0;
|
||||||
|
padding: 0 15px 0 15px;
|
||||||
|
margin-left: 7px;
|
||||||
|
margin-top: 7px;
|
||||||
|
margin-bottom: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-calendar_clock {
|
||||||
|
color: #8a909e;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bluetooth {
|
||||||
|
color: #fb958b;
|
||||||
|
padding-left: 10px;
|
||||||
|
margin-right: -5px;
|
||||||
|
font-size: 18px;
|
||||||
|
border-radius: 0px 8px 8px 0px;
|
||||||
|
margin-left: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bluetooth.on {
|
||||||
|
color: #fb958b;
|
||||||
|
padding-left: 10px;
|
||||||
|
margin-right: -5px;
|
||||||
|
font-size: 18px;
|
||||||
|
border-radius: 0px 8px 8px 0px;
|
||||||
|
margin-left: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bluetooth.connected {
|
||||||
|
color: #81a1c1;
|
||||||
|
padding-left: 10px;
|
||||||
|
margin-right: -5px;
|
||||||
|
font-size: 18px;
|
||||||
|
border-radius: 0px 8px 8px 0px;
|
||||||
|
margin-left: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bluetooth.off {
|
||||||
|
color: #8a909e;
|
||||||
|
padding-left: 10px;
|
||||||
|
margin-right: -5px;
|
||||||
|
font-size: 18px;
|
||||||
|
border-radius: 0px 8px 8px 0px;
|
||||||
|
margin-left: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-powermenu {
|
||||||
|
color: #e78284;
|
||||||
|
margin-right: 12px;
|
||||||
|
margin-left: 12px;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0 6px 0 6.8px;
|
||||||
|
margin-top: 7px;
|
||||||
|
margin-bottom: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
tooltip {
|
||||||
|
font-family: Ubuntu Nerd Font;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 15px;
|
||||||
|
background-color: #242933;
|
||||||
|
}
|
||||||
|
|
||||||
|
tooltip label {
|
||||||
|
font-family: Ubuntu Nerd Font;
|
||||||
|
padding: 5px;
|
||||||
|
background-color: #242933;
|
||||||
|
}
|
||||||
|
|
||||||
|
#now_playing > tooltip {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
label:focus {
|
||||||
|
background-color: #242933;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tray {
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-size: 30px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tray > .passive {
|
||||||
|
-gtk-icon-effect: dim;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tray > .needs-attention {
|
||||||
|
-gtk-icon-effect: highlight;
|
||||||
|
background-color: #eb4d4b;
|
||||||
|
}
|
||||||
|
|
||||||
|
#idle_inhibitor {
|
||||||
|
background-color: #242933;
|
||||||
|
}
|
||||||
|
|
||||||
|
#idle_inhibitor.activated {
|
||||||
|
background-color: #ecf0f1;
|
||||||
|
color: #2d3436;
|
||||||
|
}
|
||||||
|
|
||||||
|
#memory,
|
||||||
|
#custom-rate,
|
||||||
|
#custom-tailscale,
|
||||||
|
#custom-inetspeedup,
|
||||||
|
#custom-inetspeeddown,
|
||||||
|
#cpu {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mpris {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
#custom-roon {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#power-profiles-daemon {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
#custom-fans {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
#temperature {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
#custom-vpn {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
#custom-borg {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
#custom-song_name,
|
||||||
|
#custom-spotify,
|
||||||
|
#custom-next,
|
||||||
|
#custom-pre {
|
||||||
|
background-color: #3b4252;
|
||||||
|
font-size: 20px;
|
||||||
|
padding-right: 6.5px;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-weather.severe {
|
||||||
|
color: #8a909e;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-weather.sunnyDay {
|
||||||
|
color: #8a909e;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-weather.clearNight {
|
||||||
|
color: #8a909e;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-weather.cloudyFoggyDay,
|
||||||
|
#custom-weather.cloudyFoggyNight {
|
||||||
|
color: #8a909e;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-weather.rainyDay,
|
||||||
|
#custom-weather.rainyNight {
|
||||||
|
color: #8a909e;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-weather.showyIcyDay,
|
||||||
|
#custom-weather.snowyIcyNight {
|
||||||
|
color: #8a909e;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-weather {
|
||||||
|
color: #8a909e;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
#custom-network_speed {
|
||||||
|
color: #8a909e;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
#waybar.info {
|
||||||
|
background-color: rgba(10, 10, 10, 0.0);
|
||||||
|
font-family: JetBrainsMonoNL Nerd Font Mono;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#submap {
|
||||||
|
background-color: rgba(10, 10, 10, 0.8);
|
||||||
|
color: rgba(152, 239, 106, 1);
|
||||||
|
}
|
210
.config/waybar/sway_config
Normal file
210
.config/waybar/sway_config
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
{
|
||||||
|
"layer": "top",
|
||||||
|
"position": "top",
|
||||||
|
"margin-top": 10,
|
||||||
|
"margin-left": 10,
|
||||||
|
"margin-right": 10,
|
||||||
|
"height": 54,
|
||||||
|
"spacing": 0,
|
||||||
|
"modules-left": ["custom/launcher","sway/workspaces","cpu","temperature","power-profiles-daemon","memory","custom/vpn"],
|
||||||
|
"modules-center": ["sway/window"],
|
||||||
|
"modules-right": ["tray","custom/wf-recorder","bluetooth","network", "wireplumber","custom/weather","clock","battery","custom/powermenu"],
|
||||||
|
|
||||||
|
// "sway/workspaces": {
|
||||||
|
// "on-click": "activate",
|
||||||
|
// "format": "{icon}",
|
||||||
|
// "format-icons": {
|
||||||
|
// "default": "",
|
||||||
|
// "1": "",
|
||||||
|
// "2": "",
|
||||||
|
// "3": "",
|
||||||
|
// "4": "",
|
||||||
|
// "active": "",
|
||||||
|
// "urgent": "",
|
||||||
|
// },
|
||||||
|
// "persistent-workspaces": {
|
||||||
|
// "*": 4,
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
|
||||||
|
"sway/workspaces": {
|
||||||
|
"disable-scroll": true,
|
||||||
|
"all-outputs": true,
|
||||||
|
"format": "{icon}",
|
||||||
|
"format-icons": {
|
||||||
|
"1": "",
|
||||||
|
"2": "",
|
||||||
|
"3": "",
|
||||||
|
"4": "",
|
||||||
|
"urgent": "",
|
||||||
|
"focused": "",
|
||||||
|
"default": ""
|
||||||
|
},
|
||||||
|
"persistent-workspaces": {
|
||||||
|
"1": [],
|
||||||
|
"2": [],
|
||||||
|
"3": [],
|
||||||
|
"4": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"custom/vpn": {
|
||||||
|
//pkill -RTMIN+10 waybar
|
||||||
|
"format": "VPN ",
|
||||||
|
"exec": "echo '{\"class\": \"connected\"}'",
|
||||||
|
"exec-if": "test -d /proc/sys/net/ipv4/conf/wg0",
|
||||||
|
"return-type": "json",
|
||||||
|
"signal": 10,
|
||||||
|
},
|
||||||
|
|
||||||
|
"custom/wf-recorder": {
|
||||||
|
//pkill -RTMIN+11 waybar
|
||||||
|
"format": "• ",
|
||||||
|
"exec": "echo '{\"class\": \"recording\"}'",
|
||||||
|
"exec-if": "pgrep -x wf-recorder >/dev/null",
|
||||||
|
"return-type": "json",
|
||||||
|
"signal": 11,
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
"backlight": {
|
||||||
|
"device": "amdgpu_bl1",
|
||||||
|
"format": "{icon}",
|
||||||
|
"format-icons": [" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "]
|
||||||
|
},
|
||||||
|
|
||||||
|
"custom/weather": {
|
||||||
|
"exec": "python ~/.config/waybar/scripts/weather.py",
|
||||||
|
"restart-interval": 300,
|
||||||
|
"return-type": "json",
|
||||||
|
// "on-click": "xdg-open https://weather.com/en-IN/weather/today/l/$(location_id)"
|
||||||
|
// "format-alt": "{alt}",
|
||||||
|
},
|
||||||
|
|
||||||
|
"bluetooth": {
|
||||||
|
"format": "",
|
||||||
|
"format-connected": "",
|
||||||
|
"format-connected-battery": "",
|
||||||
|
"tooltip-format": "{controller_alias}\t{controller_address}\n\n{num_connections} connected",
|
||||||
|
"tooltip-format-connected": "{controller_alias}\t{controller_address}\n\n{num_connections} connected\n\n{device_enumerate}",
|
||||||
|
"tooltip-format-enumerate-connected": "{device_alias}\t{device_address}",
|
||||||
|
"tooltip-format-enumerate-connected-battery": "{device_alias}\t{device_address}\t{device_battery_percentage}%",
|
||||||
|
"on-click": "/home/teraflops/.local/bin/bt.sh",
|
||||||
|
"on-click-right" :"bluetoothctl connect 88:C9:E8:EF:69:1E",
|
||||||
|
},
|
||||||
|
|
||||||
|
"hyprland/window": {
|
||||||
|
"max-length": 80,
|
||||||
|
"format": "<span font='14' rise='-4444'>{}</span>",
|
||||||
|
"separate-outputs": true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"temperature": {
|
||||||
|
"hwmon-path": ["/sys/class/thermal/thermal_zone0/temp"],
|
||||||
|
"critical-threshold": 80,
|
||||||
|
"format-critical": "{temperatureC}°C",
|
||||||
|
"format": "{temperatureC}°C"
|
||||||
|
},
|
||||||
|
|
||||||
|
"memory": {
|
||||||
|
"interval": 3,
|
||||||
|
"format": " {}% ",
|
||||||
|
"max-length": 10
|
||||||
|
},
|
||||||
|
|
||||||
|
"cpu": {
|
||||||
|
"interval": 1,
|
||||||
|
"format": " {}% ",
|
||||||
|
"max-length": 10
|
||||||
|
},
|
||||||
|
|
||||||
|
"power-profiles-daemon": {
|
||||||
|
"format": "{icon}",
|
||||||
|
"tooltip-format": "Power profile: {profile}\nDriver: {driver}",
|
||||||
|
"tooltip": true,
|
||||||
|
"format-icons": {
|
||||||
|
"default": "BAL",
|
||||||
|
"performance": "PERF",
|
||||||
|
"balanced": "BAL",
|
||||||
|
"power-saver": "PSAV"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"custom/powermenu": {
|
||||||
|
"format": "⏻ ",
|
||||||
|
"tooltip": false,
|
||||||
|
"on-click": "/home/teraflops/.config/waybar/scripts/wlogout.sh"
|
||||||
|
},
|
||||||
|
|
||||||
|
"tray": {
|
||||||
|
"spacing": 10
|
||||||
|
},
|
||||||
|
|
||||||
|
"clock": {
|
||||||
|
"tooltip-format": "<tt><small>{calendar}</small></tt>",
|
||||||
|
"format-alt": " {:%a, %d %b %Y} ",
|
||||||
|
"format": " {:%H:%M} ",
|
||||||
|
"calendar": {
|
||||||
|
"weeks-pos" : "none",
|
||||||
|
"on-scroll" : 1,
|
||||||
|
"format": {
|
||||||
|
"months": "<span color='#8fbcbb'><b>{}</b></span>",
|
||||||
|
"days": "<span color='#8a909e'>{}</span>",
|
||||||
|
"weeks": "<span color='#99ffdd'><b>W{}</b></span>",
|
||||||
|
"weekdays": "<span color='#81a1c1'><b>{}</b></span>",
|
||||||
|
"today": "<span color='#88c0d0'><b>{}</b></span>"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"actions": {
|
||||||
|
"on-click-right": "none",
|
||||||
|
"on-click-forward": "tz_up",
|
||||||
|
"on-click-backward": "tz_down",
|
||||||
|
"on-scroll-up": "shift_up",
|
||||||
|
"on-scroll-down": "shift_down"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"network": {
|
||||||
|
"format-wifi": "{icon}",
|
||||||
|
"format-disconnected": " ",
|
||||||
|
"format-icons": [" "," "," "," "," "],
|
||||||
|
"tooltip-format-wifi": "{essid}",
|
||||||
|
"tooltip-format-disconnected": "Disconnected",
|
||||||
|
"nospacing": 1,
|
||||||
|
},
|
||||||
|
|
||||||
|
"battery": {
|
||||||
|
"interval": 60,
|
||||||
|
"states": {
|
||||||
|
"warning": 30,
|
||||||
|
"critical": 15
|
||||||
|
},
|
||||||
|
"format": "{icon}",
|
||||||
|
"format-charging": " ",
|
||||||
|
"format-plugged": "{icon}",
|
||||||
|
"format-icons": [" ", " ", " ", " ", " "],
|
||||||
|
"max-length": 25,
|
||||||
|
"on-click": "gnome-terminal -- battop",
|
||||||
|
},
|
||||||
|
|
||||||
|
"wireplumber": {
|
||||||
|
"format": "{icon}",
|
||||||
|
"nospacing": 1,
|
||||||
|
"tooltip-format": "Volume : {volume}%",
|
||||||
|
"format-muted": " ",
|
||||||
|
"format-icons": {
|
||||||
|
"headphone": " ",
|
||||||
|
"default": [" ", " ", " "]
|
||||||
|
},
|
||||||
|
"on-click": "pamixer -t",
|
||||||
|
"on-click-right": "pavucontrol",
|
||||||
|
"scroll-step": 5,
|
||||||
|
},
|
||||||
|
|
||||||
|
"custom/launcher": {
|
||||||
|
"format": " ",
|
||||||
|
"tooltip": false,
|
||||||
|
"on-click": "/home/teraflops/.config/rofi/launchers/misc/launcher.sh &"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user