diff --git a/home/hyprland/waybar/common.nix b/home/hyprland/waybar/common.nix new file mode 100644 index 0000000..3606114 --- /dev/null +++ b/home/hyprland/waybar/common.nix @@ -0,0 +1,157 @@ +{ + widgets = { + "group/media" = { + orientation = "horizontal"; + + modules = [ + "mpris" + "custom/cava" + "wireplumber" + ]; + }; + + mpris = { + format = "{player_icon}"; + format-paused = "{status_icon}"; + max-length = 100; + player-icons = { + default = "||"; + mpv = "||"; + }; + status-icons = { + paused = "▶"; + }; + }; + + "custom/cava" = { + exec = "sh ~/.config/waybar/cava.sh"; + format = "{} ♪"; + }; + + wireplumber = { + format = "{volume}%"; + format-muted = ""; + max-volume = 110; + scroll-step = 0.2; + }; + + "group/hardware" = { + orientation = "horizontal"; + modules = [ + "cpu" + "network" + "memory" + "disk" + "temperature" + ]; + }; + + network = { + # Wifi + tooltip = true; + format-wifi = "{icon} "; + format-icons = [ + "󰤟" + "󰤢" + "󰤥" + ]; + rotate = 0; + + # Ethernet + format-ethernet = "ᯤ"; + tooltip-format = "Network: {essid}\nSignal strength: {signaldBm}dBm ({signalStrength}%)\nFrequency: {frequency}MHz\nInterface: {ifname}\nIP: {ipaddr}/{cidr}\nGateway: {gwaddr}\nNetmask: {netmask}\nCurrent󰈀 : {bandwidthTotalBits}\nUp 󰶣: {bandwidthUpBits}\nDown 󰶡: {bandwidthDownBits}"; + format-linked = "󰈀 {ifname} (No IP)"; + format-disconnected = " "; + tooltip-format-disconnected = "Disconnected"; + on-click = "/usr/local/bin/ags -t ControlPanel"; + interval = 2; + }; + + memory = { + interval = 1; + rotate = 270; + format = "{icon}"; + format-icons = [ + "󰝦" + "󰪞" + "󰪟" + "󰪠" + "󰪡" + "󰪢" + "󰪣" + "󰪤" + "󰪥" + ]; + max-length = 10; + }; + + cpu = { + interval = 1; + format = "{icon}"; + rotate = 270; + format-icons = [ + "󰝦" + "󰪞" + "󰪟" + "󰪠" + "󰪡" + "󰪢" + "󰪣" + "󰪤" + "󰪥" + ]; + }; + + temperature = { + format = "{temperatureC}°C "; + thermal-zone = 0; + hwmon-path = "/sys/class/hwmon/hwmon0/temp1_input"; + critical-threshold = 80; + }; + + disk = { + format = "{percentage_free}% "; + tooltip = true; + tooltip-format = "{free} / {total} ({percentage_free})"; + }; + + clock = { + format = "{:%a %b %d, %I:%M %p}"; + rotate = 0; + on-click = " "; + tooltip-format = "{calendar}"; + calendar = { + mode = "month"; + mode-mon-col = 3; + on-scroll = 1; + on-click-right = "mode"; + format = { + months = "{}"; + weekdays = "{}"; + today = "{}"; + }; + }; + actions = { + on-click-right = "mode"; + on-click-forward = "tz_up"; + on-click-backward = "tz_down"; + on-scroll-up = "shift_up"; + on-scroll-down = "shift_down"; + }; + }; + + "custom/nixicon" = { + format = ""; + on-click = "rofi -show drun -theme $HOME/.config/rofi/custom.rasi"; + tooltip = false; + }; + + "custom/weather" = { + format = "{}"; + exec = "curl -s 'wttr.in/52.281311,10.527029?format=2'"; + interval = 60; + tooltip = false; + }; + }; + +} diff --git a/home/hyprland/waybar/config.jsonc b/home/hyprland/waybar/config.jsonc deleted file mode 100644 index d7c5f45..0000000 --- a/home/hyprland/waybar/config.jsonc +++ /dev/null @@ -1,609 +0,0 @@ -// --// waybar config generated by wbarconfgen.sh //-- // - -[ -{ - "layer": "top", - "position": "top", - "mod": "dock", - //"width": 20, - "margin-top": 7, - "margin-bottom": 0, - "margin-left": 10, - "margin-right": 10, - "exclusive": true, - "passthrough": false, - "gtk-layer-shell": true, - "reload_style_on_change": true, - "output": "Virtual-1", - - "modules-left": [], - "modules-center": ["wlr/taskbar"], - "modules-right": ["cpu"], - - "wlr/taskbar": { - "all-outputs": true, - "format": "{icon}", - "icon-size": 20, - "active-first": true - } -}, - -{ -// sourced from header module // - - "layer": "top", - "position": "top", - "mod": "dock", - "margin-left": 10, - "margin-right": 10, - "margin-top": 7, - "margin-bottom": 0, - "exclusive": true, - "passthrough": false, - "gtk-layer-shell": true, - "reload_style_on_change": true, - "output": "DP-1", - -// positions generated based on config.ctl // - - "modules-left": ["custom/smallspacer","hyprland/workspaces","custom/spacer","mpris"], - "modules-center": ["custom/padd","custom/l_end","custom/r_end","hyprland/window","custom/padd"], - "modules-right": ["custom/padd","custom/l_end","group/expand","network","group/expand-3","group/expand-2","group/expand-4","memory","cpu","clock","custom/notification","custom/padd"], - - -// sourced from modules based on config.ctl // - -"custom/led": { - "format": "󰍿 ", - "format-alt": "󰍿 ", - "on-click": "~/mouse.sh", - "tooltip": false, -}, - -"upower": { - "icon-size": 20, - "format": "", - "format-alt": "{}[{time}]", - "tooltip": true, - "tooltip-spacing": 20, - "on-click-right": "pkill waybar & hyprctl dispatch exec waybar" -}, - -"upower#headset": { -"format": " {percentage}", -"native-path": "/org/freedesktop/UPower/devices/headset_dev_A6_98_9A_0D_D3_49", -"show-icon": false, -"tooltip": false, -}, - -"group/expand-4": { - "orientation": "horizontal", - "drawer": { - "transition-duration": 600, - "children-class": "not-power", - "transition-to-left": true, - "click-to-reveal": true - }, - "modules": ["upower","upower/headset"] - }, - -"custom/smallspacer":{ -"format": " ", -}, -"memory": { - "interval": 1, - "rotate": 270, - "format": "{icon}", - "format-icons": ["󰝦","󰪞","󰪟","󰪠","󰪡","󰪢","󰪣","󰪤","󰪥"], - "max-length": 10 -}, -"cpu": { - "interval": 1, - "format": "{icon}", - "rotate": 270, - "format-icons": ["󰝦","󰪞","󰪟","󰪠","󰪡","󰪢","󰪣","󰪤","󰪥"], -}, - - -"mpris": { - "format": "{player_icon} {dynamic}", - "format-paused": "{status_icon} {dynamic}", - "max-length": 100, - "player-icons": { - "default": "⏸", - "mpv": "🎵" - }, - "status-icons": { - "paused": "▶" - }, - // "ignored-players": ["firefox"] -}, -"tray": { - "icon-size": 16, - "rotate": 0, - "spacing": 3 - }, - - "group/expand": { - "orientation": "horizontal", - "drawer": { - "transition-duration": 600, - "children-class": "not-power", - "transition-to-left": true, - // "click-to-reveal": true - }, - "modules": ["custom/menu","custom/spacer","tray"] - }, - - "custom/menu":{ - "format": "󰅃", - "rotate": 90, - }, - - -"custom/notification": { - "tooltip": false, - "format": "{icon}", - "format-icons": { - "notification": "󰅸", - "none": "󰂜", - "dnd-notification": "󰅸", - "dnd-none": "󱏨", - "inhibited-notification": "󰅸", - "inhibited-none": "󰂜", - "dnd-inhibited-notification": "󰅸", - "dnd-inhibited-none": "󱏨" - }, - "return-type": "json", - "exec-if": "which swaync-client", - "exec": "swaync-client -swb", - "on-click-right": "swaync-client -d -sw", - "on-click": "swaync-client -t -sw", - "escape": true - }, - - "hyprland/window": { -//"format": "{}" // <--- these is the default value -"format": "{class}", - "max-length": 120, - "icon": false, - "icon-size": 13, -}, - - "custom/power": { - "format": "@{}", - "rotate": 0, - "on-click": "ags -t ControlPanel", - "on-click-right": "pkill ags", - "tooltip": true - }, - - "custom/spacer":{ - "format": "|" - }, - - - - - -"hyprland/workspaces": { - "format": "{icon}", - "format-icons": { - "default": "", - "active": "", - //"default": "○", - //"default": "●" - }, -}, - -"wlr/workspaces": { - "persistent-workspaces": { - "3": [], // Always show a workspace with name '3', on all outputs if it does not exists - "4": ["eDP-1"], // Always show a workspace with name '4', on output 'eDP-1' if it does not exists - "5": ["eDP-1", "DP-2"] // Always show a workspace with name '5', on outputs 'eDP-1' and 'DP-2' if it does not exists - } -}, - - -"cava": { - "cava_config": "~/.config/cava/config", - "framerate": 60, - "autosens": 1, - "bars": 14, - "lower_cutoff_freq": 50, - "higher_cutoff_freq": 10000, - "method": "pulse", - "source": "auto", - "stereo": true, - "reverse": false, - "bar_delimiter": 0, - "monstercat": false, - "waves": false, - "noise_reduction": 0.77, - "input_delay": 2, - "format-icons" : ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█" ], - "actions": { - "on-click-right": "mode" - } - }, - - "custom/script": { - "on-click": "~/.config/waybar/volume.sh toggle", - "format": "", - - }, - - "custom/cliphist": { - "format": "{}", - "rotate": 0, - "exec": "echo ; echo 󰅇 clipboard history", - "on-click": "sleep 0.1 && cliphist.sh c", - "on-click-right": "sleep 0.1 && cliphist.sh d", - "on-click-middle": "sleep 0.1 && cliphist.sh w", - "interval" : 86400, // once every day - "tooltip": true - }, - - "custom/wbar": { - "format": "𐌏{}", //   // - "rotate": 0, - "exec": "echo ; echo show app menu", - "on-click": "wofi --show drun", - "on-click-right": "wbarconfgen.sh p", - "on-click-middle": "sleep 0.1 && quickapps.sh kitty firefox spotify code dolphin", - "interval" : 86400, - "tooltip": true - }, - - "custom/theme": { - "format": "{}", - "rotate": 0, - "exec": "echo ; echo 󰟡 pick color", - "on-click": "hyprpicker", - "on-click-right": "themeswitch.sh -p", - "on-click-middle": "sleep 0.1 && themeselect.sh", - "interval" : 86400, // once every day - "tooltip": true - }, - - "custom/wallchange": { - "format": "{}", - "rotate": 0, - "exec": "echo ; echo 󰆊 switch wallpaper", - "on-click": "swww img --transition-type grow --transition-pos 0.071,0.988 --transition-step 255 --transition-fps 60 /home/anik/Downloads/skyway.jpg", - "on-click-right": "swww img --transition-type grow --transition-pos 0.071,0.988 --transition-step 255 --transition-fps 60 /home/anik/Downloads/cloud.png", - "on-click-middle": "swww img --transition-type grow --transition-pos 0.071,0.988 --transition-step 255 --transition-fps 60 /home/anik/Downloads/gradient.jpg", - "on-scroll-up": "swww img --transition-type grow --transition-pos 0.071,0.988 --transition-step 255 --transition-fps 60 /home/anik/Downloads/torvalds.png", - "on-scroll-down": "swww img --transition-type grow --transition-pos 0.071,0.988 --transition-step 255 --transition-fps 60 /home/anik/Downloads/night.png", - "interval" : 86400, // once every day - "tooltip": true - }, - "custom/mouse": - { - "format": "", - "format-alt": "", - - "on-click": "m8mouse -dpi 1 -led 2 -speed 4", - "on-click-right": "m8mouse -dpi 1 -led 4 -speed 4", - "on-click-middle": "m8mouse -dpi 1 -led 7 -speed 4", - "tooltip": true - }, - - "wlr/taskbar": { - "format": "{icon}", - "rotate": 0, - "icon-size": 18, - "icon-theme": "Tela-circle-dracula", - "spacing": 0, - "tooltip-format": "{title}", - "on-click": "activate", - "on-click-middle": "close", - "ignore-list": [ - "Alacritty" - ], - "app_ids-mapping": { - "firefoxdeveloperedition": "firefox-developer-edition" - } - }, - - "custom/spotify": { - "exec": "mediaplayer.py --player spotify", - "format": " {}", - "rotate": 0, - "return-type": "json", - "on-click": "playerctl play-pause --player spotify", - "on-click-right": "playerctl next --player spotify", - "on-click-middle": "playerctl previous --player spotify", - "on-scroll-up": "volumecontrol.sh -p spotify i", - "on-scroll-down": "volumecontrol.sh -p spotify d", - "max-length": 25, - "escape": true, - "tooltip": true - }, - - "idle_inhibitor": { - "format": "{icon}", - "rotate": 0, - "format-icons": { - "activated": "󰥔", - "deactivated": "" - } - }, - - "clock": { - "format": "{:%I:%M %p}", - "rotate": 0, - "on-click": "/usr/local/bin/ags -t ActivityCenter", - "tooltip-format": "{calendar}", - "calendar": { - "mode": "month", - "mode-mon-col": 3, - "on-scroll": 1, - "on-click-right": "mode", - "format": { - "months": "{}", - "weekdays": "{}", - "today": "{}" - } - }, - "actions": { - "on-click-right": "mode", - "on-click-forward": "tz_up", - "on-click-backward": "tz_down", - "on-scroll-up": "shift_up", - "on-scroll-down": "shift_down" - } - }, - - - "battery": { - "states": { - "good": 95, - "warning": 30, - "critical": 20 - }, - "format": "{icon}", - "rotate": 0, - "format-charging": "󱐋", - "format-plugged": "󰂄", - // "format-alt": "<{time} | {capacity}%", - "format-icons": ["󰝦","󰪞","󰪟","󰪠","󰪡","󰪢","󰪣","󰪤","󰪥"], - // "format-icons": ["","","","","","","",""], - //"format-icons": ["󰂎", "󰁺", "󰁻", "󰁼", "󰁽", "󰁾", "󰁿", "󰂀", "󰂁", "󰂂", "󰁹"], - "on-click-right": "pkill waybar & hyprctl dispatch exec waybar", - // "format-icons": [], - - }, - - "backlight": { - "device": "intel_backlight", - "rotate": 0, - "format": "{icon}", - "format-icons": ["󰃞", "󰃝", "󰃟", "󰃠"], - "scroll-step": 1, - "min-length": 2 - }, - - "group/expand-2": { - "orientation": "horizontal", - "drawer": { - "transition-duration": 600, - "children-class": "not-power", - "transition-to-left": true, - "click-to-reveal": true - }, - "modules": ["backlight","backlight/slider","custom/smallspacer","custom/led"] - }, - - "group/expand-3": { - "orientation": "horizontal", - "drawer": { - "transition-duration": 600, - "children-class": "not-power", - "transition-to-left": true, - "click-to-reveal": true - }, - "modules": ["pulseaudio","pulseaudio/slider"] - }, - - "network": { - "tooltip": true, - "format-wifi": "{icon} ", - "format-icons": ["󰤟", "󰤢", "󰤥"], - // "format-wifi": "", - "rotate": 0, - "format-ethernet": "󰈀 ", - "tooltip-format": "Network: {essid}\nSignal strength: {signaldBm}dBm ({signalStrength}%)\nFrequency: {frequency}MHz\nInterface: {ifname}\nIP: {ipaddr}/{cidr}\nGateway: {gwaddr}\nNetmask: {netmask}", - "format-linked": "󰈀 {ifname} (No IP)", - "format-disconnected": " ", - "tooltip-format-disconnected": "Disconnected", - "on-click": "/usr/local/bin/ags -t ControlPanel", - "interval": 2, - }, - - "pulseaudio": { - "format": "{icon}", - "rotate": 0, - "format-muted": "婢", - "tooltip-format": "{icon} {desc} // {volume}%", - "scroll-step": 5, - "format-icons": { - "headphone": "", - "hands-free": "", - "headset": "", - "phone": "", - "portable": "", - "car": "", - "default": ["", "", ""] - } - }, - - "pulseaudio#microphone": { - "format": "{format_source}", - "rotate": 0, - "format-source": "", - "format-source-muted": "", - "on-click": "pavucontrol -t 4", - "on-click-middle": "volumecontrol.sh -i m", - "on-scroll-up": "volumecontrol.sh -i i", - "on-scroll-down": "volumecontrol.sh -i d", - "tooltip-format": "{format_source} {source_desc} // {source_volume}%", - "scroll-step": 5 - }, - - "custom/notifications": { - "tooltip": false, - "format": "{icon} {}", - "rotate": 0, - "format-icons": { - "email-notification": "", - "chat-notification": "󱋊", - "warning-notification": "󱨪", - "error-notification": "󱨪", - "network-notification": "󱂇", - "battery-notification": "󰁺", - "update-notification": "󰚰", - "music-notification": "󰝚", - "volume-notification": "󰕿", - "notification": "", - "none": "" - }, - "return-type": "json", - "exec-if": "which dunstctl", - "exec": "notifications.py", - "on-click": "sleep 0.1 && dunstctl history-pop", - "on-click-middle": "dunstctl history-clear", - "on-click-right": "dunstctl close-all", - "interval": 1, - "tooltip": true, - "escape": true - }, - - "custom/keybindhint": { - "format": " ", - "rotate": 0, - "on-click": "keybinds_hint.sh" - }, - -"custom/expand": { - "on-click":"~/.config/hypr/scripts/expand_toolbar", - "format":"{}", - "exec":"~/.config/hypr/scripts/tools/expand arrow-icon" - }, - -// modules for padding // - - "custom/l_end": { - "format": " ", - "interval" : "once", - "tooltip": false - }, - - "custom/r_end": { - "format": " ", - "interval" : "once", - "tooltip": false - }, - - "custom/sl_end": { - "format": " ", - "interval" : "once", - "tooltip": false - }, - - "custom/sr_end": { - "format": " ", - "interval" : "once", - "tooltip": false - }, - - "custom/rl_end": { - "format": " ", - "interval" : "once", - "tooltip": false - }, - - "custom/rr_end": { - "format": " ", - "interval" : "once", - "tooltip": false - }, - - "custom/padd": { - "format": " ", - "interval" : "once", - "tooltip": false - }, - - -"backlight/slider": { - "min": 5, - "max": 100, - "rotate": 0, - "device": "intel_backlight", - "scroll-step": 1, - }, - - "pulseaudio/slider": { - "min": 5, - "max": 100, - "rotate": 0, - "device": "pulseaudio", - "scroll-step": 1, - }, -}, -{ -// sourced from header module // - - "layer": "top", - "position": "top", - "mod": "dock", - "margin-left": 10, - "margin-right": 10, - "margin-top": 7, - "margin-bottom": 0, - "exclusive": true, - "passthrough": false, - "gtk-layer-shell": true, - "reload_style_on_change": true, - "output": "HDMI-A-2", - -// positions generated based on config.ctl // - - "modules-left": ["hyprland/workspaces"], - "modules-center": ["custom/weather"], - "modules-right": ["network"], - - -// sourced from modules based on config.ctl // - "hyprland/workspaces": { - "format": "{icon}", - "format-icons": { - "default": "", - "active": "", - //"default": "○", - //"default": "●" - } - }, - - "custom/weather": { - "exec": "python3 ~/.config/waybar/weather.py waybar", - "restart-interval": 900, - "return-type": "json" - }, - - "network": { - "tooltip": true, - "format-wifi": "{icon} ", - "format-icons": ["󰤟", "󰤢", "󰤥"], - // "format-wifi": "", - "rotate": 0, - "format-ethernet": "{bandwidthTotalBits} 󰈀 {bandwidthUpBits} 󰶣 {bandwidthDownBits} 󰶡 ", - "tooltip-format": "Network: {essid}\nSignal strength: {signaldBm}dBm ({signalStrength}%)\nFrequency: {frequency}MHz\nInterface: {ifname}\nIP: {ipaddr}/{cidr}\nGateway: {gwaddr}\nNetmask: {netmask}", - "format-linked": "󰈀 {ifname} (No IP)", - "format-disconnected": " ", - "tooltip-format-disconnected": "Disconnected", - "on-click": "/usr/local/bin/ags -t ControlPanel", - "interval": 1, - }, -}] diff --git a/home/hyprland/waybar/configs/cava.sh b/home/hyprland/waybar/configs/cava.sh new file mode 100644 index 0000000..a12dac4 --- /dev/null +++ b/home/hyprland/waybar/configs/cava.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +# Not my own work. Credit to original author + +#----- Optimized bars animation without much CPU usage increase -------- +bar="▁▂▃▄▅▆▇█" +dict="s/;//g" + +# Calculate the length of the bar outside the loop +bar_length=${#bar} + +# Create dictionary to replace char with bar +for ((i = 0; i < bar_length; i++)); do + dict+=";s/$i/${bar:$i:1}/g" +done + +# Create cava config +config_file="/tmp/bar_cava_config" +cat >"$config_file" <` -LATITUDE = 52.264149 -LONGITUDE = 10.526420 - -# waybar colors -GRAY = '#b7c1de' -DARK = '#01012b' -GREEN = '#65dc98' -YELLOW = '#ffe69d' -ORANGE = '#ff6e27' -RED = '#ff124f' -PURPLE = '#7a04eb' -BLUE = '#8386f5' -MOIST = '#e96d5e' - - -### UTILITIES ### - -def print_error(msg: str): - ''' - print an error message with appropriate prefix. - - @param str: the message to print - ''' - print('\x1b[90m[\x1b[31merr\x1b[90m]\x1b[0m', msg) - - -def print_help(): - ''' - print help message, to be used for the `--help` flag and as response to - incorrect usage - ''' - print('usage: \x1b[33m./weather.py [options]\x1b[0m') - print() - print('available subcommands:') - print(' - \x1b[32mgeocoding\x1b[0m : search for city to get its coordinates') - print(' - \x1b[32mcurrent\x1b[0m : print current weather information') - print(' - \x1b[32mforecast[-daily]\x1b[0m : print forecast for the next ~5 days') - print(' - \x1b[32mforecast-detail\x1b[0m : print detailed forecast in 3h intervals') - print(' - \x1b[32mwaybar\x1b[0m : get output for usage with waybar') - - -def make_request(call: str) -> list | dict: - ''' - make a request to an openweathermap api and returns the response as either - a list or a dictionary. the api key is added automatically. quits on error. - - @param call: the api path, e.g. `data/2.5/weather?...` - - @return response of the api request as a list or dict - ''' - try: - req = requests.get(f'https://api.openweathermap.org/{call}&appid={API_KEY}') - return req.json() - except: - print_error(f'failed to make request to `/{call}`') - quit(1) - - -def get_wind_direction(deg: int) -> str: - ''' - turn a wind direction specified by meteorological degrees into a - human-readable form - - @param deg: degrees. expected to be in range [0..360] - - @return human-readable form (e.g. 'NE' for `deg == 45`) - ''' - if deg < 22.5: return 'N' - if deg < 67.5: return 'NE' - if deg < 112.5: return 'E' - if deg < 157.5: return 'SE' - if deg < 202.5: return 'S' - if deg < 247.5: return 'SW' - if deg < 292.5: return 'W' - if deg < 337.5: return 'NW' - else: return 'N' - - -def print_entry(label: str, content: str, indent: int = 0, label_width: int = 8): - ''' - print an 'entry' that consists of a label (printed in gray) and some - content. the labels are automatically filled with whitespace to align - multiple lines properly. - - @param label: the label of the line - @param content: content, printed after the label - @param indent: number of spaces to indent with - @param label_width: width of label - ''' - label_with_whitespace = label.ljust(label_width) - print(f'{" " * indent}\x1b[90m{label_with_whitespace}\x1b[0m {content}') - - -def get_weekday(date: str) -> str: - ''' - get weekday from date - - @param date: date as ISO-8601-formatted string (YYYY-mm-dd) - - @return weekday as lowercase string (e.g. 'monday') - ''' - return datetime.datetime.strptime(date, '%Y-%m-%d').strftime('%A').lower() - - -def colorize(text: str, color: str) -> str: - ''' - wrap `text` with pango markup to colorize it for usage with waybar. - - @param text: the text to colorize - @param color: the color as string ('#rrggbb') - ''' - return f'{text}' - - -def waybar_entry(label: str, content: str, indent: int = 2, label_width: int = 9): - ''' - create an 'entry' for a waybar tooltip that consists of a label (printed in - gray) and some content. the labels are automatically filled with whitespace - to align multiple lines properly. - - @param label: the label of the line - @param content: content, printed after the label - @param indent: number of spaces to indent with - @param label_width: width of label - - @return the entry for use within waybar - ''' - - label_with_whitespace = label.ljust(label_width) - return f'{" " * indent}{colorize(label_with_whitespace, GRAY)} {content}\n' - - - -### GEOCODING ### - -def geocoding(search: str): - ''' - call openweathermap's geocoding api to find the coordinates of cities. can - be used to find the values required for the `LATITUDE` and `LONGITUDE` - constants within this script. the results are printed. - - @param: search term in the format 'city[,state][,country]' - ''' - - res = make_request(f'geo/1.0/direct?q={search}&limit=5') - num_results = len(res) - - if (num_results == 0): - print_error('no results found') - else: - print(f'found {num_results} result{"" if num_results == 1 else "s"}:') - for entry in res: - # obtain data - name = entry['name'] - state = entry['state'] if 'state' in entry.keys() else None - country = entry['country'] - latitude = entry['lat'] - longitude = entry['lon'] - - # print data - print(f' - \x1b[32m{name}\x1b[90m', - f'({f"{state}, " if state else ""}{country})\x1b[0m:', - f'latitude = \x1b[35m{latitude}\x1b[0m,', - f'longitude = \x1b[35m{longitude}\x1b[0m') - - -### CURRENT WEATHER ### - -def get_current_weather_data() -> dict: - ''' - get current weather data from openweathermap api. when the `rain` is not - set (due to there not being any rain), it will be added with a rain amount - of 0 mm over the last hour. - - @return api response - ''' - - res = make_request(f'data/2.5/weather?lat={LATITUDE}&lon={LONGITUDE}&units=metric') - - if 'rain' not in res.keys(): - res['rain'] = { '1h': 0 } - - return res - - -def current_weather(): - ''' - print current weather information - ''' - - data = get_current_weather_data() - - # collect relevant data - weather = data['weather'][0]['description'].lower() - temperature = round(data['main']['temp'], 1) - temperature_felt = round(data['main']['feels_like'], 1) - humidity = data['main']['humidity'] - wind_speed = round(data['wind']['speed'], 1) - wind_direction = get_wind_direction(data['wind']['deg']) - rainfall = data['rain']['1h'] - - # print data - print_entry('weather', f'\x1b[32m{weather}\x1b[0m') - print_entry('temp', f'\x1b[33m{temperature} °C\x1b[90m, feels like \x1b[33m{temperature_felt} °C\x1b[0m') - print_entry('humidity', f'\x1b[31m{humidity} % RH\x1b[0m') - print_entry('wind', f'\x1b[35m{wind_speed} m/s\x1b[90m ({wind_direction})\x1b[0m') - print_entry('rain', f'\x1b[34m{rainfall} mm\x1b[0m') - - - -### FORECAST ### - -def get_forecast_data() -> dict: - ''' - get forecast data for the next ~5 days from the openweathermap api, with - data points separated by 3 hours. the data is grouped by date (the api does - not group the data by default and instead sends it as one sequence). - - @return api response grouped by date - ''' - - res = make_request(f'data/2.5/forecast?lat={LATITUDE}&lon={LONGITUDE}&units=metric') - - days = dict() - for i in res['list']: - day = i['dt_txt'].split(' ')[0] - if day not in days.keys(): - days[day] = list() - if 'rain' not in i.keys(): - i['rain'] = { '3h': 0 } - days[day].append(i) - - return days - - -def get_daily_forecast_data() -> dict[dict]: - ''' - obtain forecast data for the next ~5 days, where values are grouped by day. - since the api only provides the data in 3h intervals, the properties of - different data points are combined in order to provide appropriate data for - each day. - - @return the processed data as a dict with key = date and value = data as - another dict - ''' - - res = get_forecast_data() - - output = dict() - - for day in sorted(res): - data = res[day] - number_of_data_points = len(data) - - # collect relevant from data for each day - temperatures = [i['main']['temp'] for i in data] - weather_descriptions = [i['weather'][0]['description'].lower() for i in data] - humidity = [i['main']['humidity'] for i in data] - rainfall = [i['rain']['3h'] for i in data] - precipitation_prob = [i['pop'] for i in data] - wind_speeds = [i['wind']['speed'] for i in data] - weekday = get_weekday(day) - - # min and max temperature for the day - min_temperature = round(min(temperatures)) - max_temperature = round(max(temperatures)) - - # obtain the average weather by finding the weather description with the highest number of occurances in `weather_descriptions` - weather_count = {i: weather_descriptions.count(i) for i in set(weather_descriptions)} - weather_count_max = max(weather_count.values()) - weather_average = tuple(filter(lambda x: weather_count[x] == weather_count_max, weather_count.keys()))[0] - - # humidiy - humidity_average = round(statistics.mean(humidity)) - - # total rainfall and probability of precipitation. also, estimate the total rainfall if not all data points for a day are available - rainfall_total = round(sum(rainfall), 1) - rainfall_total_estimated = round(rainfall_total / number_of_data_points * 8, 1) - max_precipitation_prob = round(max(precipitation_prob) * 100) - - # average wind - wind_average = round(statistics.mean(wind_speeds), 1) - - output[day] = { - 'number_of_data_points': number_of_data_points, - 'min_temperature': min_temperature, - 'max_temperature': max_temperature, - 'weather_average': weather_average, - 'humidity_average': humidity_average, - 'rainfall_total': rainfall_total, - 'rainfall_total_estimated': rainfall_total_estimated, - 'max_precipitation_prob': max_precipitation_prob, - 'wind_average': wind_average, - 'weekday': weekday, - } - - return output - - -def daily_forecast(): - ''' - print forecast data for the next ~5 days - ''' - - daily_data = get_daily_forecast_data() - - for day in sorted(daily_data): - data = daily_data[day] - - # only display data point if at least half of the data points are available - if data['number_of_data_points'] < 4: - continue - - # print data - print(f'\x1b[1m{day}\x1b[0m ({data["weekday"]}):') - print_entry('weather', f'\x1b[32m{data["weather_average"]}\x1b[0m', indent = 2) - print_entry('temp', f'\x1b[33m{data["max_temperature"]} °C\x1b[0m / \x1b[33m{data["min_temperature"]} °C\x1b[0m', indent = 2) - print_entry('humidity', f'\x1b[31m{data["humidity_average"]} % RH\x1b[0m', indent = 2) - print_entry('wind', f'\x1b[35m{data["wind_average"]} m/s\x1b[0m', indent = 2) - print_entry('rain', f'\x1b[34m{data["rainfall_total_estimated"]} mm\x1b[0m' + - f'\x1b[90m{f""" ({data["max_precipitation_prob"]}%)""" if data["max_precipitation_prob"] > 0 else ""}' + - f'{f" (estimated)" if data["rainfall_total"] != data["rainfall_total_estimated"] else ""}\x1b[0m\n', indent = 2) - - - -### DETAILED FORECAST ### - -def detailed_forecast(): - ''' - print forecast data for the next ~5 days, where values are printed for - every 3h interval provided by the api. some data (e.g. humidity or wind - speeds) are omitted. - ''' - - res = get_forecast_data() - - for day in sorted(res): - weekday = get_weekday(day) - print(f'\x1b[1m{day}\x1b[0m ({weekday})') - - for entry in res[day]: - # collect data - weather = entry['weather'][0]['description'].lower() - temperature = round(entry['main']['temp'], 1) - rainfall = entry['rain']['3h'] - precipitation_prob = round(entry['pop'] * 100) - time = f"{int(entry['dt_txt'].split(' ')[1].split(':')[0]):2}h" - - # print data - output = '' - output += f'\x1b[33m{temperature:4} °C\x1b[90m, ' - output += f'\x1b[32m{weather}\x1b[0m' - if rainfall > 0: - output += f'\x1b[90m: \x1b[34m{rainfall} mm \x1b[90m({precipitation_prob}%)\x1b[0m' - print_entry(time, output, indent = 2, label_width = 3) - - print() - - - -### WAYBAR ### - -def waybar_widget(data: dict) -> str: - ''' - get the widget component of the waybar output. contains the current weather - group and temperature. - - @param current weather data - - @return widget component - ''' - - weather = data['weather'][0]['main'].lower() - temperature = round(data['main']['temp']) - - return f'{colorize(weather, MOIST)} {temperature}°' - - -def waybar_current(data: dict) -> str: - ''' - get the current weather overview for the tooltip of the waybar output. - - @param current weather data - - @return formatted current weather overview - ''' - - # retrieve relevant data - weather = data['weather'][0]['description'].lower() - temperature = round(data['main']['temp'], 1) - temperature_felt = round(data['main']['feels_like'], 1) - humidity = data['main']['humidity'] - wind_speed = round(data['wind']['speed'], 1) - wind_direction = get_wind_direction(data['wind']['deg']) - rainfall = data['rain']['1h'] - - # generate output - output = '' - output += waybar_entry('weather', colorize(weather, YELLOW)) - output += waybar_entry('temp', f'{colorize(f"{temperature} °C", ORANGE)}{colorize(", feels like ", GRAY)}{colorize(f"{temperature_felt} °C", ORANGE)}') - output += waybar_entry('humidity', colorize(f'{humidity} % RH', RED)) - output += waybar_entry('wind', f'{colorize(f"{wind_speed} m/s", PURPLE)} {colorize(f"({wind_direction})", GRAY)}') - output += waybar_entry('rain', colorize(f"{rainfall} mm", BLUE)) - return output - - -def waybar_forecast(data: dict) -> str: - ''' - get the daily forecast for the tooltip of the waybar output. - - @param forecast weather data - - @return formatted daily forecast - ''' - - output = '' - - daily_data = get_daily_forecast_data() - - for day in sorted(daily_data): - data = daily_data[day] - - line_content = colorize(f'{data["max_temperature"]:2}°', ORANGE) + \ - colorize(' / ', GRAY) + \ - colorize(f'{data["min_temperature"]:2}°', ORANGE) + \ - colorize(', ', GRAY) + \ - colorize(data['weather_average'], YELLOW) - - if data['rainfall_total_estimated'] > 0: - line_content += colorize(': ', GRAY) + \ - colorize(f'{data["rainfall_total_estimated"]} mm ', BLUE) + \ - colorize(f'({data["max_precipitation_prob"]}%)', GRAY) - - output += waybar_entry(data['weekday'], line_content) - - return output.rstrip() - - -def waybar(): - ''' - get current and forecast weather data and output it formatted in a way that - allows it to be included as a widget in waybar. only shows weather category - and temperature in the widget, but reveals detailed weather information and - a ~5 day forecast in the tooltip. - ''' - - current_data = get_current_weather_data() - forecast_data = get_forecast_data() - - widget = waybar_widget(current_data) - current = waybar_current(current_data) - forecast = waybar_forecast(forecast_data) - tooltip = colorize('current weather', GREEN) + '\n' + current + '\n' + \ - colorize('forecast', GREEN) + '\n' + forecast - - print(json.dumps({ - 'text': widget, - 'tooltip': tooltip, - })) - - - -### MAIN ### - -def main(): - ''' - main function - ''' - - # no parameters or `--help` - if len(sys.argv) == 1 or sys.argv[1] in ('help', '-h', '--help'): - print_help() - - # >= 1 parameter provided - else: - # geocoding - if sys.argv[1] == 'geocoding': - if len(sys.argv) == 3: - geocoding(sys.argv[2]) - else: - print_error('expected argument ``') - print_help() - # constants not set - elif API_KEY is None or LATITUDE is None or LONGITUDE is None: - print_error('please modify the constants within the script before use.') - # current weather - elif sys.argv[1] == 'current': - current_weather() - # daily forecast - elif sys.argv[1] == 'forecast' or sys.argv[1] == 'forecast-daily': - daily_forecast() - # detailed forecast - elif sys.argv[1] == 'forecast-detail': - detailed_forecast() - # waybar - elif sys.argv[1] == 'waybar': - waybar() - # unknown command - else: - print_error(f'unknown command `{sys.argv[1]}`') - print_help() - -if __name__ == '__main__': - main() - diff --git a/home/hyprland/waybar/default.nix b/home/hyprland/waybar/default.nix index 4e14d6e..f60c6e1 100644 --- a/home/hyprland/waybar/default.nix +++ b/home/hyprland/waybar/default.nix @@ -1,44 +1,11 @@ { pkgs, - config, ... }: { programs.waybar = { enable = true; package = pkgs.waybar; - /* - settings = { - mainBar = { - layer = "top"; - position = "top"; - height = 30; - output = [ - "eDP-1" - "DP-1" - "HDMI-A-1" - "Virtual-1" - ]; - - modules-left = [ "sway/workspaces" "sway/mode" "wlr/taskbar" ]; - modules-center = [ "sway/window" "custom/hello-from-waybar" ]; - modules-right = [ "mpd" "custom/mymodule#with-css-id" "temperature" ]; - - "sway/workspaces" = { - disable-scroll = true; - all-outputs = true; - }; - "custom/hello-from-waybar" = { - format = "hello {}"; - max-length = 40; - interval = "once"; - exec = pkgs.writeShellScript "hello-from-waybar" '' - echo "from within waybar $USER" - ''; - }; - }; - }; - */ }; home.file.".config/waybar" = { diff --git a/home/hyprland/waybar/dual.nix b/home/hyprland/waybar/dual.nix index 5dc8858..a63bfc6 100644 --- a/home/hyprland/waybar/dual.nix +++ b/home/hyprland/waybar/dual.nix @@ -1,188 +1,42 @@ +let + common = import ./common.nix; + hyprland = import ./hyprland.nix; +in { enable = true; settings = { - mainBar = { - layer = "top"; - position = "top"; - height = 30; - output = [ "DP-1" ]; - - modules-left = [ - "hyprland/workspaces" - "mpris" - ]; - - modules-center = [ ]; - - modules-right = [ - "network" - "memory" - "cpu" - "clock" - ]; - - # Widgets - "sway/workspaces" = { - disable-scroll = true; - all-outputs = true; - }; - - mpris = { - format = "{player_icon} {dynamic}"; - format-paused = "{status_icon} {dynamic}"; - max-length = 100; - - player-icons = { - default = "⏸"; - mpv = "🎵"; - }; - - status-icons = { - paused = "▶"; - }; - }; - - "hyprland/workspaces" = { - format = "{icon}"; - format-icons = { - default = ""; - active = ""; - }; - }; - - network = { - tooltip = true; - format-wifi = "{icon} "; - format-icons = [ - "󰤟" - "󰤢" - "󰤥" + mainBar = + { + layer = "top"; + position = "top"; + height = 30; + output = [ "DP-1" ]; + modules-left = [ + "custom/nixicon" + "clock" ]; - rotate = 0; - format-ethernet = "󰈀 "; - tooltip-format = "Network: {essid}\nSignal strength: {signaldBm}dBm ({signalStrength}%)\nFrequency: {frequency}MHz\nInterface: {ifname}\nIP: {ipaddr}/{cidr}\nGateway: {gwaddr}\nNetmask: {netmask}"; - format-linked = "󰈀 {ifname} (No IP)"; - format-disconnected = " "; - tooltip-format-disconnected = "Disconnected"; - on-click = "/usr/local/bin/ags -t ControlPanel"; - interval = 2; - }; - - memory = { - interval = 1; - rotate = 270; - format = "{icon}"; - format-icons = [ - "󰝦" - "󰪞" - "󰪟" - "󰪠" - "󰪡" - "󰪢" - "󰪣" - "󰪤" - "󰪥" + modules-center = [ + "hyprland/workspaces" ]; - max-length = 10; - }; - - cpu = { - interval = 1; - format = "{icon}"; - rotate = 270; - format-icons = [ - "󰝦" - "󰪞" - "󰪟" - "󰪠" - "󰪡" - "󰪢" - "󰪣" - "󰪤" - "󰪥" + modules-right = [ + "group/hardware" ]; - }; + } + // common.widgets + // hyprland.widgets; - clock = { - format = "{:%I:%M %p}"; - rotate = 0; - on-click = "/usr/local/bin/ags -t ActivityCenter"; - tooltip-format = "{calendar}"; - - calendar = { - mode = "month"; - mode-mon-col = 3; - on-scroll = 1; - on-click-right = "mode"; - format = { - months = "{}"; - weekdays = "{}"; - today = "{}"; - }; - }; - - actions = { - on-click-right = "mode"; - on-click-forward = "tz_up"; - on-click-backward = "tz_down"; - on-scroll-up = "shift_up"; - on-scroll-down = "shift_down"; - }; - }; - - }; - - secondBar = { - layer = "top"; - position = "top"; - height = 30; - output = [ - "HDMI-A-2" - ]; - - modules-left = [ - "hyprland/workspaces" - ]; - - modules-center = [ - ]; - - modules-right = [ - "network" - ]; - - "hyprland/workspaces" = { - format = "{icon}"; - format-icons = { - default = ""; - active = ""; - }; - }; - - "custom/weather" = { - exec = "~/.config/waybar/weather.py waybar"; - restart-interval = 900; - return-type = "json"; - }; - - network = { - tooltip = true; - format-wifi = "{icon} "; - format-icons = [ - "󰤟" - "󰤢" - "󰤥" - ]; - rotate = 0; - format-ethernet = "{bandwidthTotalBits} 󰈀 {bandwidthUpBits} 󰶣 {bandwidthDownBits} 󰶡 "; - tooltip-format = "Network: {essid}\nSignal strength: {signaldBm}dBm ({signalStrength}%)\nFrequency: {frequency}MHz\nInterface: {ifname}\nIP: {ipaddr}/{cidr}\nGateway: {gwaddr}\nNetmask: {netmask}"; - format-linked = "󰈀 {ifname} (No IP)"; - format-disconnected = " "; - tooltip-format-disconnected = "Disconnected"; - on-click = "/usr/local/bin/ags -t ControlPanel"; - interval = 2; - }; - }; + secondBar = + { + layer = "top"; + position = "top"; + height = 30; + output = [ "HDMI-A-2" ]; + modules-left = [ "group/media" ]; + modules-center = [ "hyprland/workspaces" ]; + modules-right = [ "custom/weather" ]; + } + // common.widgets + // hyprland.widgets; }; } diff --git a/home/hyprland/waybar/hyprland.nix b/home/hyprland/waybar/hyprland.nix new file mode 100644 index 0000000..59aa729 --- /dev/null +++ b/home/hyprland/waybar/hyprland.nix @@ -0,0 +1,11 @@ +{ + widgets = { + "hyprland/workspaces" = { + format = "{icon}"; + format-icons = { + default = ""; + active = ""; + }; + }; + }; +} diff --git a/home/hyprland/waybar/single.nix b/home/hyprland/waybar/single.nix index d1c27ac..c942a30 100644 --- a/home/hyprland/waybar/single.nix +++ b/home/hyprland/waybar/single.nix @@ -1,150 +1,39 @@ +let + common = import ./common.nix; + hyprland = import ./hyprland.nix; +in { enable = true; settings = { - mainBar = { - layer = "top"; - position = "top"; - height = 30; - output = [ - "eDP-1" - "DP-1" - "HDMI-A-1" - "Virtual-1" - ]; - - modules-left = [ - "hyprland/workspaces" - "mpris" - ]; - - modules-center = [ - "custom/weather" - ]; - - modules-right = [ - "network" - "memory" - "cpu" - "clock" - ]; - - # Widgets - "sway/workspaces" = { - disable-scroll = true; - all-outputs = true; - }; - - mpris = { - format = "{player_icon} {dynamic}"; - format-paused = "{status_icon} {dynamic}"; - max-length = 100; - - player-icons = { - default = "⏸"; - mpv = "🎵"; - }; - - status-icons = { - paused = "▶"; - }; - }; - - "hyprland/workspaces" = { - format = "{icon}"; - format-icons = { - default = ""; - active = ""; - }; - }; - - network = { - tooltip = true; - format-wifi = "{icon} "; - format-icons = [ - "󰤟" - "󰤢" - "󰤥" + mainBar = + { + layer = "top"; + position = "top"; + height = 30; + /* + Waybar should use every display available if not output is specified + output = [ + "eDP-1" + "DP-1" + "HDMI-A-1" + "Virtual-1" + ]; + */ + modules-left = [ + "custom/nixicon" + "clock" + "group/media" ]; - rotate = 0; - format-ethernet = "󰈀 "; - tooltip-format = "Network = {essid}\nSignal strength: {signaldBm}dBm ({signalStrength}%)\nFrequency: {frequency}MHz\nInterface: {ifname}\nIP: {ipaddr}/{cidr}\nGateway: {gwaddr}\nNetmask: {netmask}"; - format-linked = "󰈀 {ifname} (No IP)"; - format-disconnected = " "; - tooltip-format-disconnected = "Disconnected"; - on-click = "/usr/local/bin/ags -t ControlPanel"; - interval = 2; - }; - - memory = { - interval = 1; - rotate = 270; - format = "{icon}"; - format-icons = [ - "󰝦" - "󰪞" - "󰪟" - "󰪠" - "󰪡" - "󰪢" - "󰪣" - "󰪤" - "󰪥" + modules-center = [ + "hyprland/workspaces" ]; - max-length = 10; - }; - - cpu = { - interval = 1; - format = "{icon}"; - rotate = 270; - format-icons = [ - "󰝦" - "󰪞" - "󰪟" - "󰪠" - "󰪡" - "󰪢" - "󰪣" - "󰪤" - "󰪥" + modules-right = [ + "custom/weather" + "group/hardware" ]; - }; - - clock = { - format = "{:%I:%M %p}"; - rotate = 0; - on-click = "/usr/local/bin/ags -t ActivityCenter"; - tooltip-format = "{calendar}"; - - calendar = { - mode = "month"; - mode-mon-col = 3; - on-scroll = 1; - on-click-right = "mode"; - format = { - months = "{}"; - weekdays = "{}"; - today = "{}"; - }; - }; - - actions = { - on-click-right = "mode"; - on-click-forward = "tz_up"; - on-click-backward = "tz_down"; - on-scroll-up = "shift_up"; - on-scroll-down = "shift_down"; - }; - }; - - # Custom Widgets - "custom/weather" = { - exec = "python3 ~/.config/waybar/weather.py waybar"; - restart-interval = 900; - return-type = "json"; - }; - - }; + # Widget configurations + } + // common.widgets + // hyprland.widgets; }; - }