Added: Waybar Conf
This commit is contained in:
parent
2de5052495
commit
f25c210340
@ -11,6 +11,8 @@ in
|
||||
{
|
||||
imports = [
|
||||
./kitty.nix # Terminal
|
||||
../waybar
|
||||
../rofi
|
||||
];
|
||||
|
||||
|
||||
|
607
home/waybar/config/config.jsonc
Normal file
607
home/waybar/config/config.jsonc
Normal file
@ -0,0 +1,607 @@
|
||||
// --// waybar config generated by wbarconfgen.sh //-- //
|
||||
|
||||
[
|
||||
/*{
|
||||
"layer": "top",
|
||||
"position": "left",
|
||||
"mod": "dock",
|
||||
"width": 20,
|
||||
"margin-top": 200,
|
||||
"margin-bottom": 200,
|
||||
"exclusive": true,
|
||||
"passthrough": false,
|
||||
"gtk-layer-shell": true,
|
||||
"reload_style_on_change": true,
|
||||
"output": "HDMI-A-2",
|
||||
|
||||
"modules-left": [],
|
||||
"modules-center": ["wlr/taskbar"],
|
||||
"modules-right": [],
|
||||
|
||||
"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": "<span color='#021c18'></span> <span color='#313436'></span> ",
|
||||
"format-alt": " <span color='#bbc2c7'></span> ",
|
||||
"on-click": "~/mouse.sh",
|
||||
"tooltip": false,
|
||||
},
|
||||
|
||||
"upower": {
|
||||
"icon-size": 20,
|
||||
"format": "",
|
||||
"format-alt": "{}<span color='orange'>[{time}]</span>",
|
||||
"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": "<span color='grey'>{status_icon} {dynamic}</span>",
|
||||
"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": "<span weight='bold' >{class}</span>",
|
||||
"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": "<tt>{calendar}</tt>",
|
||||
"calendar": {
|
||||
"mode": "month",
|
||||
"mode-mon-col": 3,
|
||||
"on-scroll": 1,
|
||||
"on-click-right": "mode",
|
||||
"format": {
|
||||
"months": "<span color='#ffead3'><b>{}</b></span>",
|
||||
"weekdays": "<span color='#ffcc66'><b>{}</b></span>",
|
||||
"today": "<span color='#ff6699'><b>{}</b></span>"
|
||||
}
|
||||
},
|
||||
"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": "<span color='#a6d189'></span>",
|
||||
"format-plugged": "",
|
||||
// "format-alt": "<<span weight='bold' color='#c2864a'>{time} <span weight='bold' color='white'>| <span weight='bold' color='#82d457'>{capacity}%</span></span></span>",
|
||||
"format-icons": ["","","","","","","","",""],
|
||||
// "format-icons": ["","","","","","","",""],
|
||||
//"format-icons": ["", "", "", "", "", "", "", "", "", "", ""],
|
||||
"on-click-right": "pkill waybar & hyprctl dispatch exec waybar",
|
||||
// "format-icons": [<i class='fa-solid fa-wifi-slash'></i>],
|
||||
|
||||
},
|
||||
|
||||
"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": "<i class='fa-solid fa-wifi-slash'></i>",
|
||||
"rotate": 0,
|
||||
"format-ethernet": " ",
|
||||
"tooltip-format": "Network: <big><b>{essid}</b></big>\nSignal strength: <b>{signaldBm}dBm ({signalStrength}%)</b>\nFrequency: <b>{frequency}MHz</b>\nInterface: <b>{ifname}</b>\nIP: <b>{ipaddr}/{cidr}</b>\nGateway: <b>{gwaddr}</b>\nNetmask: <b>{netmask}</b>",
|
||||
"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": "<span foreground='white'><sup></sup></span>",
|
||||
"chat-notification": "<span foreground='white'><sup></sup></span>",
|
||||
"warning-notification": "<span foreground='yellow'><sup></sup></span>",
|
||||
"error-notification": "<span foreground='red'><sup></sup></span>",
|
||||
"network-notification": "<span foreground='white'><sup></sup></span>",
|
||||
"battery-notification": "<span foreground='white'><sup></sup></span>",
|
||||
"update-notification": "<span foreground='white'><sup></sup></span>",
|
||||
"music-notification": "<span foreground='white'><sup></sup></span>",
|
||||
"volume-notification": "<span foreground='white'><sup></sup></span>",
|
||||
"notification": "<span foreground='white'><sup></sup></span>",
|
||||
"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": "<i class='fa-solid fa-wifi-slash'></i>",
|
||||
"rotate": 0,
|
||||
"format-ethernet": "{bandwidthTotalBits} {bandwidthUpBits} {bandwidthDownBits} ",
|
||||
"tooltip-format": "Network: <big><b>{essid}</b></big>\nSignal strength: <b>{signaldBm}dBm ({signalStrength}%)</b>\nFrequency: <b>{frequency}MHz</b>\nInterface: <b>{ifname}</b>\nIP: <b>{ipaddr}/{cidr}</b>\nGateway: <b>{gwaddr}</b>\nNetmask: <b>{netmask}</b>",
|
||||
"format-linked": " {ifname} (No IP)",
|
||||
"format-disconnected": " ",
|
||||
"tooltip-format-disconnected": "Disconnected",
|
||||
"on-click": "/usr/local/bin/ags -t ControlPanel",
|
||||
"interval": 1,
|
||||
},
|
||||
}]
|
397
home/waybar/config/style.css
Normal file
397
home/waybar/config/style.css
Normal file
@ -0,0 +1,397 @@
|
||||
* {
|
||||
font-family: "JetBrains Mono Nerd Font";
|
||||
/*font-family: "JetBrainsMono Nerd Font";*/
|
||||
font-weight: bold;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
#custom-notification {
|
||||
font-family: "JetBrains Mono Nerd Font";
|
||||
font-size: 17px;
|
||||
color: #A1BDCE;
|
||||
margin: 2px 0px 0px 0px;
|
||||
}
|
||||
|
||||
window#waybar {
|
||||
background: #092047;
|
||||
/* border-radius: 15px; */
|
||||
/* border: 2px solid #124323; */
|
||||
/* border: 0px solid #A1BDCE; */
|
||||
border: 3px solid rgba(172, 97, 185, 1);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
tooltip {
|
||||
background: #171717;
|
||||
color: #A1BDCE;
|
||||
font-size: 13px;
|
||||
border-radius: 7px;
|
||||
border: 2px solid #101a24;
|
||||
|
||||
|
||||
}
|
||||
#workspaces{
|
||||
background: rgba(23, 23, 23, 0.0);
|
||||
color: #888789;
|
||||
box-shadow: none;
|
||||
text-shadow: none;
|
||||
border-radius: 9px;
|
||||
transition: 0.2s ease;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
padding-top: 1px;
|
||||
}
|
||||
|
||||
|
||||
#workspaces button {
|
||||
background: rgba(23, 23, 23, 0.0);
|
||||
color: #A1BDCE;
|
||||
box-shadow: none;
|
||||
text-shadow: none;
|
||||
border-radius: 9px;
|
||||
transition: 0.2s ease;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
/* animation: ws_normal 20s ease-in-out 1; */
|
||||
}
|
||||
|
||||
|
||||
|
||||
#workspaces button.active {
|
||||
|
||||
|
||||
/* background-image: url("/home/anik/Documents/bar1.png");*/
|
||||
color: #FF2A6D;
|
||||
transition: all 0.3s ease;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
/* transition: all 0.4s cubic-bezier(.55,0.68,.48,1.682); */
|
||||
}
|
||||
|
||||
#workspaces button:hover {
|
||||
background: none;
|
||||
color: #65DC98;
|
||||
animation: ws_hover 20s ease-in-out 1;
|
||||
transition: all 0.5s cubic-bezier(.55,-0.68,.48,1.682);
|
||||
}
|
||||
|
||||
#taskbar button {
|
||||
box-shadow: none;
|
||||
text-shadow: none;
|
||||
font-size: 4px;
|
||||
padding: 0px;
|
||||
border-radius: 9px;
|
||||
margin-bottom: 3px;
|
||||
margin-left: 0px;
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
margin-right: 0px;
|
||||
color: @wb-color;
|
||||
animation: tb_normal 20s ease-in-out 1;
|
||||
}
|
||||
|
||||
#taskbar button.active {
|
||||
background: @wb-act-bg;
|
||||
color: @wb-act-color;
|
||||
margin-left: 3px;
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
margin-right: 3px;
|
||||
animation: tb_active 20s ease-in-out 1;
|
||||
transition: all 0.4s cubic-bezier(.55,-0.68,.48,1.682);
|
||||
min-height: 9px;
|
||||
}
|
||||
|
||||
#taskbar button:hover {
|
||||
background: @wb-hvr-bg;
|
||||
color: @wb-hvr-color;
|
||||
animation: tb_hover 20s ease-in-out 1;
|
||||
transition: all 0.3s cubic-bezier(.55,-0.68,.48,1.682);
|
||||
}
|
||||
|
||||
#tray menu * {
|
||||
min-height: 16px;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
color: #9488e3;
|
||||
}
|
||||
|
||||
#tray menu separator {
|
||||
min-height: 10px
|
||||
}
|
||||
|
||||
|
||||
#custom-spacer{
|
||||
opacity: 0.0;
|
||||
}
|
||||
#custom-smallspacer{
|
||||
opacity: 0.0;
|
||||
}
|
||||
|
||||
|
||||
#custom-mouse{
|
||||
font-size: 14px;
|
||||
margin-bottom: 6px;
|
||||
background: #161320;
|
||||
}
|
||||
|
||||
|
||||
#custom-power{
|
||||
font-size: 15px;
|
||||
color: #FFFFFF;
|
||||
background: rgba(22, 19, 32, 0.9);
|
||||
margin: 6px 0px 6px 0px;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
#backlight{
|
||||
color: #2096C0;
|
||||
background: rgba(23, 23, 23, 0.0);
|
||||
font-weight: normal;
|
||||
font-size: 19px;
|
||||
margin: 1px 0px 0px 0px;
|
||||
padding-left: 0px;
|
||||
padding-right: 2px;
|
||||
|
||||
}
|
||||
#bluetooth,
|
||||
#custom-cliphist{
|
||||
color: #E6E7E7;
|
||||
background: #161320;
|
||||
opacity: 1;
|
||||
margin: 4px 0px 4px 0px;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
|
||||
}
|
||||
#battery{
|
||||
font-weight: normal;
|
||||
font-size: 22px;
|
||||
color: #a6d189;
|
||||
background: rgba(23, 23, 23, 0.0);
|
||||
opacity: 1;
|
||||
margin: 0px 0px 0px 0px;
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
|
||||
}
|
||||
|
||||
#idle_inhibitor{
|
||||
color: #24966e;
|
||||
background: @bar-bg;
|
||||
opacity: 1;
|
||||
margin: 4px 0px 4px 0px;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
|
||||
}
|
||||
#clock{
|
||||
color: #FDD870;
|
||||
font-size: 15px;
|
||||
font-weight: 900;
|
||||
font-family: "JetBrains Mono Nerd Font";
|
||||
background: rgba(23, 23, 23, 0.0);
|
||||
opacity: 1;
|
||||
margin: 3px 0px 0px 0px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
border: none;
|
||||
|
||||
}
|
||||
#pulseaudio{
|
||||
font-weight: normal;
|
||||
font-size: 18px;
|
||||
color: #0A9CF5;
|
||||
background: rgba(22, 19, 32, 0.0);
|
||||
opacity: 1;
|
||||
margin: 0px 0px 0px 0px;
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
}
|
||||
#cpu{
|
||||
font-weight: normal;
|
||||
font-size: 22px;
|
||||
color: #FF184C;
|
||||
}
|
||||
#custom-led{
|
||||
background: #427287;
|
||||
color: #FFFFFF;
|
||||
margin-top: 7px;
|
||||
margin-bottom: 7px;
|
||||
padding-left: 6px;
|
||||
border-radius: 7px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
#custom-gpuinfo,
|
||||
#custom-keybindhint,
|
||||
#language,
|
||||
#memory{
|
||||
font-weight: normal;
|
||||
font-size: 22px;
|
||||
color: #1AFE49;
|
||||
}
|
||||
#mpris{
|
||||
color: white;
|
||||
animation: repeat;
|
||||
animation-name: blink;
|
||||
animation-duration: 3s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
animation-direction: alternate;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
to {
|
||||
color: #4a4a4a;
|
||||
|
||||
}
|
||||
}
|
||||
#network{
|
||||
color: #FF6E27;
|
||||
font-weight: normal;
|
||||
font-size: 19px;
|
||||
padding-right: 0px;
|
||||
padding-left: 4px
|
||||
}
|
||||
#custom-notifications,
|
||||
#custom-spotify,
|
||||
#taskbar,
|
||||
#custom-theme,
|
||||
#custom-menu{
|
||||
color: #E8EDF0;
|
||||
background: rgba(23, 23, 23, 0.0);
|
||||
margin: 0px 0px 0px 0px;
|
||||
padding-left: 1px;
|
||||
padding-right: 1px;
|
||||
opacity: 0.1
|
||||
}
|
||||
#tray,
|
||||
#custom-updates,
|
||||
#custom-wallchange,
|
||||
#custom-wbar,
|
||||
#window{
|
||||
color: #A1BDCE;
|
||||
font-family: "Martian Mono";
|
||||
}
|
||||
#custom-l_end,
|
||||
#custom-r_end,
|
||||
#custom-sl_end,
|
||||
#custom-sr_end,
|
||||
#custom-rl_end,
|
||||
#cava,
|
||||
#upower#headset,
|
||||
#upower{
|
||||
color: #a6d189;
|
||||
}
|
||||
#mpris{
|
||||
font-size: 15px;
|
||||
font-weight: bold
|
||||
}
|
||||
#custom-rr_end {
|
||||
font-weight: normal;
|
||||
color: #E8EDF0;
|
||||
background: rgba(23, 23, 23, 0.0);
|
||||
opacity: 1;
|
||||
margin: 0px 0px 0px 0px;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
#backlight-slider slider,
|
||||
#pulseaudio-slider slider {
|
||||
background: #A1BDCE;
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
margin-right: 7px;
|
||||
}
|
||||
|
||||
#backlight-slider trough,
|
||||
#pulseaudio-slider trough {
|
||||
margin-top: -3px;
|
||||
min-width: 90px;
|
||||
min-height: 10px;
|
||||
margin-bottom: -4px;
|
||||
border-radius: 8px;
|
||||
background: #343434;
|
||||
}
|
||||
|
||||
#backlight-slider highlight,
|
||||
#pulseaudio-slider highlight {
|
||||
border-radius: 8px;
|
||||
background-color: #2096C0;
|
||||
}
|
||||
|
||||
#battery.charging, #battery.plugged {
|
||||
color: #E8EDF0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
#battery.critical:not(.charging) {
|
||||
color: red;
|
||||
}
|
||||
|
||||
|
||||
#taskbar {
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
#custom-r_end {
|
||||
border-radius: 0px 7px 7px 0px;
|
||||
margin-right: 1px;
|
||||
padding-right: 3px;
|
||||
}
|
||||
|
||||
#custom-l_end {
|
||||
border-radius: 7px 0px 0px 7px;
|
||||
margin-left: 1px;
|
||||
padding-left: 3px;
|
||||
}
|
||||
|
||||
#custom-sr_end {
|
||||
border-radius: 0px;
|
||||
margin-right: 1px;
|
||||
padding-right: 3px;
|
||||
}
|
||||
|
||||
#custom-sl_end {
|
||||
border-radius: 0px;
|
||||
margin-left: 1px;
|
||||
padding-left: 3px;
|
||||
}
|
||||
|
||||
#custom-rr_end {
|
||||
border-radius: 0px 7px 7px 0px;
|
||||
margin-right: 1px;
|
||||
padding-right: 3px;
|
||||
}
|
||||
|
||||
#custom-rl_end {
|
||||
border-radius: 7px 0px 0px 7px;
|
||||
margin-left: 1px;
|
||||
padding-left: 3px;
|
||||
}
|
||||
|
||||
|
||||
/* Style for launchers */
|
||||
|
||||
#custom-expand {
|
||||
min-width: 25px;
|
||||
color: #A1BDCE;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
#group-minimized {
|
||||
border-left: solid;
|
||||
border-left-width: 0.5;
|
||||
}
|
||||
|
||||
#custom-quote {
|
||||
padding-top:0px;
|
||||
color: #999999;
|
||||
font-family: "JetBrains Mono Nerd Font";
|
||||
font-size: 13px;
|
||||
}
|
528
home/waybar/config/weather.py
Executable file
528
home/waybar/config/weather.py
Executable file
@ -0,0 +1,528 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import datetime
|
||||
import json
|
||||
import requests
|
||||
import statistics
|
||||
import sys
|
||||
|
||||
|
||||
|
||||
# TODO
|
||||
# - snowfall data
|
||||
# - weather warnings
|
||||
|
||||
|
||||
|
||||
### CONSTANTS ###
|
||||
|
||||
# api key - get it at https://openweathermap.org/
|
||||
API_KEY = "bfca28ca110b5595380d9aad164d5ee5"
|
||||
|
||||
# latitude and longitude of the city you want to query
|
||||
# can be obtained through `./weather.py geocoding <city[,state,country]>`
|
||||
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 <subcommand> [options]\x1b[0m')
|
||||
print()
|
||||
print('available subcommands:')
|
||||
print(' - \x1b[32mgeocoding\x1b[0m <city[,state][,country]> : 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'<span foreground="{color}">{text}</span>'
|
||||
|
||||
|
||||
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 `<city[,state][,country]>`')
|
||||
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()
|
||||
|
16
home/waybar/default.nix
Normal file
16
home/waybar/default.nix
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
pkgs,
|
||||
config,
|
||||
...
|
||||
}: {
|
||||
programs.waybar = {
|
||||
enable = true;
|
||||
package = pkgs.waybar;
|
||||
};
|
||||
|
||||
home.file.".config/waybar" = {
|
||||
source = ./configs;
|
||||
# copy the scripts directory recursively
|
||||
recursive = true;
|
||||
};
|
||||
}
|
@ -10,7 +10,6 @@
|
||||
|
||||
../../home/hyprland
|
||||
../../home/programs
|
||||
../../home/rofi
|
||||
];
|
||||
|
||||
programs.git = {
|
||||
|
Loading…
Reference in New Issue
Block a user